Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/d/seher/haeuser/abstellraum.c b/d/seher/haeuser/abstellraum.c
new file mode 100644
index 0000000..2f71d75
--- /dev/null
+++ b/d/seher/haeuser/abstellraum.c
@@ -0,0 +1,13 @@
+inherit "/std/room";
+#include "/d/unterwelt/wurzel/wurzel.h" //noch zu aendern...
+
+create() {
+  ::create();
+  SetProp(P_HAUS_ERLAUBT,1);
+  SetProp(P_INT_SHORT,"Der Abstellraum fuer heimatlose Seherhaeuser");
+  SetProp(P_INT_LONG,BS("Hier landen Seherhaeuser, die keinen Platz mehr haben. Wenn "
+  +"Dein Haus hier steht, wende Dich am besten bald an den zustaendigen Magier fuer die "
+  +"Haeuser.\n"));
+  AddDetail("magier","Wurzel oder Wargon kuemmern sich momentan um die Haeuser.\n");
+  AddExit("norden","/gilden/abenteurer");
+}
diff --git a/d/seher/haeuser/access_rights.c b/d/seher/haeuser/access_rights.c
new file mode 100755
index 0000000..1f6097d
--- /dev/null
+++ b/d/seher/haeuser/access_rights.c
@@ -0,0 +1,13 @@
+/* 15.03.2008, Arathorn
+   - Wargon und Wurzel ausgetragen in Abstimmung mit Zook */
+#include "haus.h"
+
+int access_rights(string euid, string file)
+{
+  // return ( euid == "vanion" );
+
+  // Die Maintainer dieses Pakets sind im Define MAINTAINER 
+  // in /d/seher/haeuser/haus.h zentral gepflegt
+  return ( member(MAINTAINER, euid) !=-1);
+}
+
diff --git a/d/seher/haeuser/bausparvertrag.c b/d/seher/haeuser/bausparvertrag.c
new file mode 100644
index 0000000..44d9976
--- /dev/null
+++ b/d/seher/haeuser/bausparvertrag.c
@@ -0,0 +1,155 @@
+/*
+ *  bausparvertrag.c -- Wir geben unsern Sehern ein Zuhause...
+ *
+ *  Grundversion von Jof, weiterbearbeitet von Wargon.
+ *
+ * $Date: 1994/12/17 15:56:11 $
+ * $Revision: 1.4 $
+ * $Log: bausparvertrag.c,v $
+ * Revision 1.4  1994/12/17  15:56:11  Wargon
+ * Etwas mehr Sicherheit gegen Zugriffe von aussen.
+ *
+ * Revision 1.3  1994/10/21  09:52:03  Wargon
+ * zerreissen eingebaut.
+ *
+ * Revision 1.2  1994/10/09  21:00:59  Wargon
+ * Laufzeit etc. ausgebaut, da das ueber den Block abgewickelt
+ * wird.
+ * Funktion Einzahlung() eingebaut (wird von der Bank aufgerufen).
+ *
+ * Revision 1.1  1994/10/09  20:49:28  Wargon
+ * Initial revision
+ *
+ */
+#pragma strong_types,rtt_checks
+
+#include "haus.h"
+#include <properties.h>
+#include <moving.h>
+
+inherit "/std/thing";
+
+protected void create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT,"Ein Bausparvertrag");
+  SetProp(P_NAME,"Bausparvertrag");
+  Set(P_AUTOLOADOBJ, ({0, V_EP}) );
+  Set(P_AUTOLOADOBJ, SECURED, F_MODE); // Sicher ist sicher...
+  SetProp(P_NODROP,1);
+  SetProp(P_NEVERDROP,1);
+  SetProp(P_ARTICLE,1);
+  SetProp(P_WEIGHT, 0);
+  SetProp(P_GENDER,1);
+  AddId( ({"bausparvertrag", "vertrag", "sehe\rvertrag"}) );
+  AddCmd("lies","lesen");
+  AddCmd( ({ "unterschreibe", "unterschreib" }), lambda( ({}),
+    ({#',, ({#'notify_fail, "Hier kannst Du den Vertrag nicht unterschreiben!\n" }),
+	   ({#'return, 0})
+    }) ) );
+  AddCmd( ({ "zerreiss", "zerreisse" }), lambda( ({ 'str }),
+    ({#',, ({#'notify_fail, "Was willst Du zerreissen?\n"}),
+	   ({#'?!, ({#'id, 'str}), ({#'return, 0}) }),
+	   ({#'=, 'blk, ({#'present, "\n block", ({#'this_player}) }) }),
+	   ({#'write, "Du zerreisst Deinen Vertrag.\n"}),
+	   ({#'write_file, PATH+"log/BANK.LOG", ({#'sprintf, "%s - Vertrag: %s zerriss ihn.\n",
+	       ({#'dtime, ({#'time}) }), ({#'getuid, ({#'this_player})})})}),
+	   ({#'?, 'blk, ({#'call_other, 'blk, "remove", 1}) }),
+	   ({#'remove, 1}),
+	   ({#'return, 1})
+    }) ) );
+}
+
+string _query_long()
+{
+  int max;
+
+  max = (Query(P_AUTOLOADOBJ)[1] & V_FAST) ? SUMME_S : SUMME_L;
+  if (Query(P_AUTOLOADOBJ)[1] & V_RAUM)
+    max = max * 4/10;
+  return sprintf(
+      "Dies ist ein Bausparvertrag. Du hast bisher %d (von %d) Punkten eingezahlt.\n",
+      QueryProp(P_AUTOLOADOBJ)[0], max);
+}
+
+#if 0
+int query_prevent_shadow(object ob)
+{
+  HLOG("SHADOW", sprintf("%s, von %O im Vertrag.\n", dtime(time())[5..], ob));
+  return 1;
+}
+#endif
+
+int lesen(string str)
+{
+  if (id(str))
+  {
+    if (Query(P_AUTOLOADOBJ)[1] & V_RAUM)
+      this_player()->More(PATH+"txt/vertrag_raum.txt",1);
+    else
+      this_player()->More(PATH+"txt/vertrag.txt",1);
+    return 1;
+  }
+  return 0;
+}
+
+int Einzahlung()
+{
+  int *al;
+  object env, po;
+
+  if (old_explode(object_name(po=previous_object()),"#")[0] != PATH+"sb_einzahlung") {
+    env = environment();
+    HLOG( (env ? getuid(env) : "BANK.LOG"), sprintf("Vertrag: Einzahlung von aussen! TI = %O, PO = %O\n", this_interactive(), previous_object()));
+  }
+  al = Query(P_AUTOLOADOBJ);
+  return Set(P_AUTOLOADOBJ, ({ al[0]+RATENHOEHE, al[1] }) )[0];
+}
+
+void Sign(int flag)
+{
+  Set(P_AUTOLOADOBJ, ({ Query(P_AUTOLOADOBJ)[0], flag }) );
+}
+
+public int move(mixed dest, int meth)
+{
+  object penv;
+  int ret;
+
+  penv = environment();
+  ret = ::move(dest, meth);
+
+  if (ret == 1 && (penv || (object_name(previous_object()) != PATH+"sb_antrag" &&
+			    object_name(previous_object()) != (stringp(dest) ? dest : object_name(dest)))))
+    HLOG("MOVING", sprintf("Vertrag: von %O -> %O mit %O (%O)\n", environment(), dest, previous_object(), getuid(previous_object())));
+  return ret;
+}
+
+mixed _set_autoloadobj(mixed al)
+{
+  // Loggen, wenn nicht vom Spielerobjekt selber via den
+  // Autoloader-Mechanismus gesetzt.
+  if (previous_object(1)
+      && strstr(load_name(previous_object(1)),"/std/shells") != 0)
+    HLOG("SET_AL",sprintf("Vertrag: TP = %O, PO = %O, ENV = %O\n",
+          this_player(), previous_object(), environment()));
+
+  /* Kompatibilitaet zu alten Vertraegen */
+  if (pointerp(al) && !(al[1] & V_EP)) {
+    al[0] *= 4;
+    al[1] |= V_EP;
+    call_out("notifyChange", 0, this_player());
+  }
+  return Set(P_AUTOLOADOBJ, al);
+}
+
+void notifyChange(object wem)
+{
+  tell_object(wem,
+       "\nNACHRICHT VON DER SEHERBANK:\n"
+      +"Die Vertragsmodalitaeten haben sich geaendert! Bitte lesen Sie den\n"
+      +"Vertrag noch einmal genau durch (insbes. Art. 4)!\n"
+      +"Statt Geld haben Sie jetzt Erfahrungspunkte zu zahlen!\n\n" );
+}
+
diff --git a/d/seher/haeuser/block.c b/d/seher/haeuser/block.c
new file mode 100644
index 0000000..8f011ed
--- /dev/null
+++ b/d/seher/haeuser/block.c
@@ -0,0 +1,324 @@
+// block.c -- Hier sammeln sich die Raten fuer das Seherhaus
+//
+// (c) 1994 Wargon@MorgenGrauen
+//
+// $Id: block.c,v 1.2 2003/11/15 13:56:01 mud Exp $
+//
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+#include <combat.h>
+#include "haus.h"
+
+inherit "std/clothing";
+
+private nosave int shadowing;
+
+create()
+{
+  if (!clonep(this_object())) return;
+
+  ::create();
+
+  SetProp( P_SHORT, "Der MG MASTER-BLOCK" );
+  SetProp( P_NAME, "MASTERBLOCK" );
+  SetProp( P_WEIGHT, 100 );
+  SetProp( P_GENDER, MALE );
+  SetProp( P_NODROP, 1 );
+  SetProp( P_NEVERDROP, 1 );
+  // wenn es keinen TP gibt, muss das Ding per SetProp(P_AUTOLOADOBJ)
+  // konfiguriert werden, nicht hier. Zesstra, 20.10.2007
+  if (objectp(this_player()))
+    Set( P_AUTOLOADOBJ, ({ 0, this_player()->QueryProp(P_AGE) + MAX_TIME, 
+	                   MAX_TIME/2, B_EP }) );
+  Set( P_AUTOLOADOBJ, SECURED, F_MODE );
+  SetProp( P_NOBUY, 1 );
+  SetProp(P_MATERIAL, MAT_MARBLE);
+
+  SetProp(P_WEAR_FUNC, this_object());
+  SetProp(P_REMOVE_FUNC, this_object());
+
+  AddId( ({ "block", "masterblock", "master-block", "MG MASTER-BLOCK", "mg master-block", "\n block" }) );
+
+  AddCmd( "", "remindMe", 1);
+}
+
+int query_prevent_shadow(object ob)
+{
+  HLOG("SHADOW", sprintf("%s, von %O im Block.\n",dtime(time())[5..], ob));
+  return 1;
+}
+
+int
+WearFunc(object me)
+{
+  if (!shadowing && (Query(P_AUTOLOADOBJ)[V_FLAGS] & B_ACTIVE) ) {
+    clone_object("/std/player/shadows/block_shadow");
+    shadowing = 1;
+  }
+  return 1;
+}
+
+int
+RemoveFunc(object me)
+{
+  // manchmal gibts keinen TP und machmal ist TP nicht der Spieler, der den
+  // Block traegt.
+  if (this_player() && this_player() == environment()) {
+    shadowing = 0;
+    this_player()->SeherHatGenug();
+    return 1;
+  }
+  return 0;
+}
+
+static int
+_query_value()
+{
+  return( Query(P_AUTOLOADOBJ)[0] );
+}
+
+static int
+_query_cursed()
+{
+  return 0;
+}
+
+static int
+_set_schwer(int schwer)
+{
+  int *al;
+
+  al = Query(P_AUTOLOADOBJ);
+  if (schwer && !(al[V_FLAGS] & B_FAST)) {
+    al[V_DLINE] -= MAX_TIME/2;
+    al[V_WTIME] /= 2;
+    al[V_FLAGS] |= B_FAST;
+  }
+  else if (!schwer && (al[V_FLAGS] & B_FAST)) {
+    al[V_DLINE] += MAX_TIME/2;
+    al[V_WTIME] *= 2;
+    al[V_FLAGS] &= ~B_FAST;
+  }
+  Set( P_AUTOLOADOBJ, al[0..]);
+  return al[V_FLAGS] & B_FAST;
+}
+
+static int
+_query_schwer()
+{
+  return (Query(P_AUTOLOADOBJ)[V_FLAGS] & B_FAST);
+}
+
+static mixed
+_set_autoloadobj(mixed al)
+{
+  object ti;
+
+  if ((ti = this_interactive()) && old_explode(object_name(ti),"#")[0] != "/secure/login")
+    HLOG("SET_AL",sprintf("Block: TI = %O, ENV = %O\n",this_interactive(), environment()));
+
+  /* Kompatibilitaet... */
+  if (pointerp(al) && !(al[V_FLAGS] & B_EP)) {
+    al[V_MONEY] *= 4;
+    al[V_FLAGS] |= B_EP;
+  }
+  return Set(P_AUTOLOADOBJ, al);
+}
+
+static string
+_query_long()
+{
+  int *al;
+  int laufzeit, einzahlung;
+  int ratenhoehe;
+  string ret;
+
+  al = Query(P_AUTOLOADOBJ);
+  ret = "Dies ist der MG MASTER-BLOCK. Er besteht aus massivem Marmor, um die Sicher-\n"
+       +"heit der eingezahlten Rate zu gewaehrleisten. Mit einem Goldkettchen versehen,\n"
+       +"kann man ihn tragen wie ein Amulett. Dies ist auch noetig, wenn auf den Block\n"
+       +"eingezahlt werden soll.\n";
+
+  ratenhoehe = RATENHOEHE;
+  if (al[V_FLAGS] & B_EXTEND)
+    ratenhoehe += (RATENHOEHE*6)/10;
+  laufzeit = 2*(al[V_DLINE]-this_player()->QueryProp(P_AGE));
+  einzahlung = al[V_MONEY];
+
+  if (laufzeit < 0)
+    ret += "Leider hast Du die Zahlung dieser Rate verpasst!\n";
+  else {
+    if (einzahlung >= ratenhoehe)
+      ret += "Er ist vollstaendig mit der Rate gefuellt.\n";
+    else
+      ret += sprintf( "Du hast bis jetzt %d (von %d) Punkten eingezahlt.\n", einzahlung, ratenhoehe );
+
+    ret += time2string("Diese Rate muss bis in %h %H, %m %M und %s %S abbezahlt sein!\n", laufzeit);
+  }
+  return ret;
+}
+
+static int
+remindMe()
+{
+  int noch, next, *al, ratenhoehe;
+  object vertrag;
+
+  al = Query(P_AUTOLOADOBJ);
+  next = al[V_WTIME];
+  ratenhoehe = RATENHOEHE;
+  if (al[V_FLAGS] & B_EXTEND)
+    ratenhoehe += (RATENHOEHE*6)/10;
+
+  if (al[V_MONEY] < ratenhoehe) {
+    noch = al[V_DLINE]-this_player()->QueryProp(P_AGE);
+    if (noch >= 0) {
+      if (next > noch) {
+	call_out("doRemind", 0, this_player(), time2string( "Die naechste Rate wird in %h %H, %m %M und %s %S faellig!\n", 2*noch));
+	do next -= WARN_TIME; while (next>0 && next>noch);
+	if (next < 0)
+	  next = 0;
+	al[V_WTIME] = next;
+	Set(P_AUTOLOADOBJ, al[0..]);
+      }
+    }
+    else {
+      if (al[V_FLAGS] & B_EXTEND) {
+	string s;
+
+	write( "Du hast die Strafrate nicht bezahlt!\n" );
+	if (vertrag = present( "sehe\rvertrag", this_player())) {
+	  write( "Damit verfaellt auch Dein Bausparvertrag! Und das im wahrsten Sinne des\nWortes...\n" );
+	  vertrag->remove();
+	}
+	s = "Block: "+getuid(this_player())+" hat ueberzogen... ;)\n";
+	HLOG(getuid(this_player()), s);
+	HLOG("BANK.LOG", s);
+	remove(1);
+      }
+      else {
+	write( "Du hast die letzte Ratenzahlung verpasst!\n"
+	      +"Die Ratenhoehe erhoeht sich um die Strafgebuehr.\n" );
+	al[V_FLAGS] |= B_EXTEND;
+	if (al[V_FLAGS] & B_FAST) {
+	  al[V_DLINE] += MAX_TIME/4;
+	  al[V_WTIME] = MAX_TIME/8;
+	}
+	else {
+	  al[V_DLINE] += MAX_TIME/2;
+	  al[V_WTIME] = MAX_TIME/4;
+	}
+	Set( P_AUTOLOADOBJ, al[0..]);
+      }
+    }
+  }
+
+  return 0;
+}
+
+int
+Gutschreiben(int money)
+{
+  int back, ratenhoehe;
+  mixed al;
+
+  if (!QueryProp(P_WORN))  // Zum Gutschreiben muss der Block getragen werden!
+    return money;
+
+  al = Query(P_AUTOLOADOBJ);
+  back = 0;
+
+  ratenhoehe = RATENHOEHE;
+  if (al[V_FLAGS] & B_EXTEND)
+    ratenhoehe += (RATENHOEHE*6)/10;
+
+  if (old_explode(object_name(previous_object()),"#")[0] != "/std/player/shadows/block_shadow") {
+    mixed env;
+
+    env = environment();
+    if (env)
+      env = getuid(env);
+    else
+      env = "BANK.LOG";
+    HLOG(env, sprintf("Block: Gutschrift von aussen (%d EP), TI = %O)!\n", money, this_interactive()));
+  }
+  al[V_MONEY] += money;
+  if (al[V_MONEY] >= ratenhoehe) {  // genug eingezahlt...
+    back = al[V_MONEY]-ratenhoehe;
+    al[V_MONEY] = ratenhoehe;
+    al[V_FLAGS] &= ~B_ACTIVE;
+    money -= back;
+    call_out("blockVoll",0,environment());
+  }
+
+  Set(P_AUTOLOADOBJ, al[0..]);
+
+  return back;
+}
+
+void
+blockVoll(object pl)
+{
+  tell_object(pl, "Der Block fuer diese Rate ist jetzt voll!\n");
+  pl->SeherHatGenug();	// shadow entfernen
+}
+
+void
+doRemind(object pl, string str)
+{
+  tell_object(pl, str);
+}
+
+protected int PreventMove(object dest, object oldenv, int method) {
+  if (previous_object() && previous_object()->QueryBuyFact()) // Im Laden...
+    return ME_CANT_BE_DROPPED;
+  return ::PreventMove(dest, oldenv, method);
+}
+
+protected void NotifyMove(object dest, object oldenv, int method) {
+
+  if (oldenv || (object_name(previous_object()) != PATH+"sb_antrag" &&
+		  object_name(previous_object()) != PATH+"sb_einzahlung" &&
+		  object_name(previous_object()) != object_name(dest)))
+    HLOG("MOVING", sprintf("Block: von %O -> %O mit %O (%O)\n",
+			   environment(), dest,
+			   previous_object(), getuid(previous_object())));
+
+  return ::NotifyMove(dest, oldenv, method);
+}
+
+int
+remove(int silent)
+{
+  if (clonep(this_object()) && environment())
+    environment()->SeherHatGenug();
+
+  return ::remove(silent);
+}
+
+// $Log: block.c,v $
+// Revision 1.2  2003/11/15 13:56:01  mud
+// MAT_MARBLE
+//
+// Revision 1.1.1.1  2000/08/20 20:22:42  mud
+// Ins CVS eingecheckt
+//
+// Revision 1.4  1994/12/17  15:56:58  Wargon
+// Etwas mehr Sicherheit gegen Zugriffe von aussen.
+//
+// Revision 1.3  1994/10/21  09:50:20  Wargon
+// long()->_query_long()
+// Shadow wird nur aktiv, wenn der Block gezueckt ist.
+//
+// Revision 1.2  1994/10/09  21:04:27  Wargon
+// Man kann jetzt einstellen, ob der Block zu einem leichten, aber
+// teureren oder zu einem schweren, aber billigeren Vertrag gehoert.
+// Falls man eine Rate verpasst hat, verfaellt damit auch der Ver-
+// trag.
+// Gutgeschrieben wird nur, wenn der Block gezueckt ist.
+// Gutschreiben() liefert ueberzaehliges Geld zurueck.
+//
+// Revision 1.1  1994/10/07  14:35:34  Wargon
+// Initial revision
+//
diff --git a/d/seher/haeuser/haeuser.txt b/d/seher/haeuser/haeuser.txt
new file mode 100644
index 0000000..1945157
--- /dev/null
+++ b/d/seher/haeuser/haeuser.txt
@@ -0,0 +1,633 @@
+Inhaltsverzeichnis:
+
+1.. Kurzuebersicht der Files
+2.. Der Hausverwalter
+3.. Sonderrechte
+4.. Die Tools
+4a... Der Hausmeister
+4b... liste.c
+4c... haustool.c
+5.. Das Haus
+5a... Aussenansicht    - haus.c
+5b... Die Raeume       - raum.c
+5c... Der Eingangsraum - raum0.c
+5d... Die Truhe        - truhe.c
+6.. Seherbank/Hauserwerb
+7.. Beschreibungen
+7a... Details/ReadDetails/Lang- und Kurzbeschreibung
+7b... Ausgaenge
+7c... Licht
+7d... Befehle
+8.. Rueckmeldungen
+9.. Hilfeseiten
+
+1. Kurzuebersicht der Files
+===========================
+
+/d/seher/haeuser/
+
+  /* Am allerwichtigsten: */
+  haus.h .................... Definitionen, die ueberall benutzt werden
+  access_rights.c ........... Die Leute mit Hauszugriff
+
+  /* Keine Haeuser ohne Verwaltung */
+  hausverwalter.c ........... Die Zentralstelle der Seherhaeuser
+  HAEUSER ................... Infos ueber Hausbesitzer (Wer, wo, wie gross)
+
+  save/
+    haeuser.o ................. Das Savefile des Hausverwalters
+
+  tools/ .................... Sammelplatz fuer Tools zu den Haeusern
+    hausmeister.c ............. Verlegen, Abreissen, .c-Files erstellen
+    haus.apx .................. Fuer .c-File eines Hauses
+    liste.c ................... Zeigt geloeschte und ge-Wiz-te Hausbesitzer
+    haustool.c ................ (obsolete)
+
+  /* Erwerb eines Hauses/Raumes */
+  seherbank.c ............... Das Bankgebaeude
+
+  queue.c ................... Die Schlangen vor den Schaltern
+  queue.h
+
+  sb_antrag.c ............... Schalter zum Beantragen eines Vertrages
+  sb_einzahlung.c ........... Schalter zum Einzahlen einer Rate
+  sb_ausgabe.c .............. Schalter zum Abholen eines Hauses
+
+  bausparvertrag.c .......... Auf dem Vertrag werden die Raten vermerkt
+  kommentar.c ............... Kommentare zum unklaren Vertragstext
+
+  txt/ ...................... Hier stehen die eigentlichen Vertragstexte
+    vertrag.txt ............... a) fuer einen Haus-Vertrag
+    vertrag_raum.txt .......... b) fuer einen Ausbauvertrag
+    kommentar.txt ............. c) erklaerende Bemerkungen zu den Texten
+
+  block.c ................... Der Block verwaltet die laufende Rate
+  /std/player/shadows/block_shadow.c
+
+  traghaus.c ................ Das bekommt man am Ausgabeschalter
+
+  log/ ...................... Logging der Bank, des Blocks, des Vertrages
+
+  /* Die Haeuser selbst */
+  virtual_compiler.c ........ Ordnet die Haeuser passend zu
+  haus.c .................... Die Aussenansicht des Hauses
+  raum.c .................... Allgemeine Funktionen der Raeume
+  raum0.c ................... Der Eingangsraum hat einige Zusatzfeatures
+  modules/ .................. Hilfsmodule:
+    haustuer.c ................ Beschreibung der Haustuer, Oeffnen/Schliessen
+    losa.c .................... Erweitertes Laden und Speichern der Raeume
+    usercmd.c ................. Alles fuer benutzerdefinierte Befehle
+  truhe.c ................... Die Truhe
+
+  save/
+    name.o .................... Savefile der Aussenansicht des Hauses
+    name0.o ................... Savefile von Raum 0
+		   usw.
+    name9.o ................... Savefile von Raum 9
+    nametruhe.o ............... Savefile der Truhe
+
+  special/ .................. Besondere Objekte
+    rj.c ...................... "Romeo und Julia" von Etain
+    rom_jul/ .................. Hier liegen die Seiten der Geschichte
+    seherfaq.c ................ Die Seher-FAQ von Ryne
+    faq/ ...................... Die Seiten der FAQ
+
+  rep/ ...................... Hier landen die 'typo'-Meldungen der Haeuser
+
+  doc/ ...................... Hilfeseiten (nach /doc/scmd kopieren)
+    hausbau ................... Allgemeine Informationen
+    seherhaus ...........
+    instanthaus ........
+
+    aendere .......
+    ausgang ........
+    befehl ..........
+    beschreibe .......
+    erlaube ...........
+    kopiere ............
+    licht ...............
+    loesche ................... Seiten zu den einzelnen Befehlen
+    meldungen ...........
+    notiz ..............
+    schiebe ...........
+    sperre ...........
+    spion ...........
+    uebersicht .....
+    verbiete ......
+    werfe ........
+
+/sys/player/base.h .......... Hier ist "HAUSVERWALTER" definiert
+
+2. Der Hausverwalter
+====================
+
+Dies ist die Zentralstelle der Seherhaeuser. Hier werden wichtige Daten
+der Hausbesitzer verwaltet, naemlich:
+- der Ort, an dem das Haus steht
+- die Zahl der zusaetzlichen Raeume, ueber die das Haus verfuegt
+- die Leute, die im Haus Sonderrechte besitzen
+
+Die Funktionen des Hausverwalters werden von diversen Objekten aus auf-
+gerufen. Im Einzelnen waeren da:
+
+secure() - intern
+  Sicherheitsabfrage fuer einige Funktionen des Hausverwalters. Zugelassen
+  sind EMs und die Leute, die im access_rights.c stehen, ausserdem das
+  Instanthaus (fuer NeuesHaus()) und der Ausgabeschalter (fuer NeuerRaum()).
+
+dump() - intern
+  Bringt die Datei "HAEUSER" auf den neuesten Stand (nachdem ein neues Haus
+  oder ein neuer Raum dazugekommen ist oder ein Haus verlegt bzw. geloescht
+  wurde).
+
+check_exits() - intern
+  Beim Verlegen und Abreissen von Haeusern ist es noetig, dass Ausgaenge in
+  Nachbarhaeuser entfernt werden. Dafuer sorgt diese Funktion.
+
+NeuesHaus() - traghaus.c
+  Ein neues Seherhaus wird geschaffen: es wird im ObjectDaemon angemeldet,
+  die Truhe wird im Eingangsraum plaziert, und die "HAEUSER"-Datei wird auf
+  den neuesten Stand gebracht.
+  Wenn Du zu Testzwecken ein Haus haben willst, kannst Du die Funktion auch
+  von Hand aufrufen.
+
+NeuerRaum() - sb_ausgabe.c
+  Einem Haus wird ein neuer Raum hinzgefuegt.
+  Maximal sind 10 Raeume (raum0-raum9) erlaubt.
+
+_LadeHaus() - virtual_compiler.c
+  Wenn der potentielle Besitzer wirklich ein Haus hat und sich dessen
+  Environment laden laesst, wird das Haus erstellt und zuruekgegeben.
+
+_LadeRaum() - virtual_compiler.c
+  Aehnlich wie _LadeHaus(), nur fuer den Raum mit der gewuenschten Nummer.
+  Beim Eingangsraum wird noch der Ausgang nach draussen gesetzt.
+
+FindeHaus() - hausverwalter.c, sb_antrag.c, hausmeister.c, /std/player/base.c
+  Gibt das Hausobjekt zurueck oder 0, falls der Spieler kein Haus hat.
+  Das Haus wird ggf. geladen.
+
+LoescheHaus() - hausmeister.c
+  Ein Haus wird abgerissen: Es wird im ObjectDaemon abgemeldet, saemtliche
+  Savefiles werden geloescht, ebenso das .rep-File des Hausbesitzers.
+
+  Rueckgabewerte:
+   1 - alles OK, das Haus ist weg
+  -1 - secure() schlug fehl.
+
+VerlegeHaus() - hausmeister.c
+  Ein Haus wird an einen neuen Ort verlegt: Es wird im ObjectDaemon ab- und
+  am neuen Ort wieder angemeldet, und der Ausgang nach draussen wird
+  angepasst.
+
+  Rueckgabewerte:
+     1 - alles Ok, das Haus steht jetzt woanders
+  -111 - secure() schlug fehl.
+    -1 - der angebliche Besitzer hat gar kein Haus
+    -2 - das Haus steht gar nicht da, wo es sein sollte
+    -3 - der Zielraum existiert nicht
+    -4 - im Zielraum kann nicht gebaut werden
+    -5 - es gibt Ausgaenge in benachbarte Seherhaeuser (diese werden beim
+	 Verlegen nicht automatisch geloescht; das muss der Hausbesitzer
+	 selbst machen)
+
+Unbebaubar() - hausverwalter.c, traghaus.c
+  Diese Funktion ermittelt, ob in einem Raum ein Haus gebaut werden kann, und
+  wenn nein, warum nicht.
+
+  Rueckgabewerte:
+    0 - OK, es darf gebaut werden
+    1 - der Zielraum ist entweder ein geclontes Objekt (und damit zumindest
+	nach dem naechsten Reboot auf immer verloren) oder ein Objekt, dass
+	von einem virtual_compiler erzeugt wurde (der ObjectDaemon wird zu
+	einem Zeitpunkt abgefragt, an dem das VC-Objekt noch nicht seinen
+	endgueltigen Namen hat).
+    2 - Im Zielraum ist P_INDOORS gesetzt (wenn in so einem Raum trotzdem
+	gebaut werden soll, weil es zum Beispiel eine grosse Hoehle o.ae.
+	ist, kann man P_INDORRS immer noch temporaer auf 0 setzen).
+    3 - Im Zielraum ist P_HAUS_ERLAUBT nicht gesetzt.
+
+Erlaube() - raum0.c
+  Einem oder mehreren Spielern werden Sonderrechte im Haus eingeraeumt.
+  Zurueckgegeben wird die neue erlaube-Liste.
+
+Verbiete() - raum0.c
+  Einem oder mehreren Spielern werden die Rechte entzogen.
+  Zurueckgegeben wird die neue erlaube-Liste.
+
+HausProp() - haus.c, raum.c, raum0.c, sb_antrag.c, hausmeister.c
+  Mit dieser Funktion koennen die Informationen ueber einen Hausbesitzer
+  abgefragt werden. Moeglich sind:
+  HP_ENV     - der Raum, in dem das Haus steht
+  HP_ROOMS   - die Zahl der zusaetzlichen Raeume, ueber die das Haus verfuegt
+  HP_ALLOWED - Array mit den Leuten, die Sonderrechte geniessen
+
+PCrunch() - raum.c, losa.c, hausmeister.c
+  Um Platz beim Speichern zu sparen, werden Details, ReadDetails und Befehle,
+  die den gleichen Text ergeben, zusammengepackt. Beim Hausmeister hat das
+  den Effekt, dass im generierten Quelltext z.B.
+    AddDetail( ({ "foo", "bar" }), "blafasel\n");
+  steht anstelle von
+    AddDetail( "foo", "blafasel\n");
+    AddDetail( "bar", "blafasel\n");
+
+3. Sonderrechte
+===============
+
+Der Hausbesitzer kann von seinem Eingangsraum aus anderen Leuten Rechte in
+seinem Haus einraeumen. Dazu dient der Befehl "erlaube". Leute mit Sonder-
+rechten koennen:
+- das Haus auf- und abschliessen
+- die Truhe oeffnen (schliessen darf sie jeder)
+- netztot im Haus bleiben
+- Rueckmeldungen aus dem Reportfile ansehen (aber nicht aendern)
+
+Ausserdem muss man Sonderrechte in einem Nachbarhaus haben, wenn man von
+seinem Haus einen Ausgang dorthin legen will.
+
+Das Array mit den Namen der Berechtigten erhaelt man vom Hausverwalter per
+HausProp(<owner>, HP_ALLOWED). In dem Array stehen die User-IDs der Leute,
+allerdings mit grossem Anfangsbuchstaben.
+
+4. Die Tools
+============
+
+4a. Der Hausmeister
+-------------------
+
+Der Hausmeister erlaubt das einfache Abreissen und Verlegen von Haeusern
+sowie das Generieren von LPC-Quelltext aus den Savefiles. Dazu stellt er
+drei Kommandos zur Verfuegung:
+
+verlege <name> nach <ziel>
+  Das Haus des Spielers <name> wird in den Raum <ziel> verlegt. <ziel> ist
+  dabei entweder der Filename des Zielraumes oder "hier", wenn das Haus
+  zu Deinem aktuellen Standort verlegt werden soll.
+  Sollte das Verlegen fehlschlagen (siehe VerlegeHaus() im Hausverwalter)
+  meldet sich der Hausmeister entsprechend.
+
+reisse <name> ab
+  Das Haus des Spielers <name> wird abgerissen.
+  ACHTUNG! Es gibt keine Sicherheitsabfrage!
+
+generiere <name> [<nr>] [soft | ganz]
+  Generiert aus dem Savefile ein LPC-File.
+  Ohne Angabe einer Nummer wird die Aussenansicht generiert, ansonsten der
+  Raum mit der entsprechenden Nummer.
+  Ausser wenn "soft" angegeben wurde, werden die Dateien in das Verzeichnis
+  "rep/" geschrieben, und zwar als "<name>haus.c", "<name>raum0.c" etc.
+  Befindet sich die Truhe in dem generierten Raum, wird auch noch eine Datei
+  "<name>truhe.c" angelegt.
+  Die Dateien sind im wesentlichen fuer Magier gedacht, die ihre Haeuser als
+  LPC-Files unter Eigenregie weiterfuehren wollen/sollen. Ausgaenge sind
+  relativ zu "/players/<name>/seherhaus".
+  Mit dem Parameter "ganz" laesst sich das gesamte Haus auf einen Schlag
+  umwandeln.
+  "soft" stammt noch aus der Zeit, als ich die Typos der Seher selber fixen
+  musste. Das Haus oder der Raum wird dabei in die Datei PATH+"fixed.c"
+  geschrieben.
+
+4b. liste.c
+-----------
+
+Ab und zu sollte man mal nachsehen, welche Seher sich geloescht haben oder
+zu Magiern wurden. Die Haeuser von geloeschten Sehern sollten ebenfalls
+geloescht werden; Magier koennen ihre Haeuser als normale LPC-Objekte
+weiterfuehren (sobald die Level 21 oder 25 haben).
+
+Die Liste zeigt an, welche Leute betroffen sind. Dazu muss sie einfach nur
+geladen werden.
+
+Zwei Ausnahmen gibt es:
+- Krigi hat sich zwar geloescht, er hat aber darum gebeten, dass sein Haus
+  weiter bestehen kann (er ist jetzt Dauergast)
+- Fraggle ist zwar Magier, hat sein Haus aber mal zu Testzwecken bekommen
+
+4c. haustool.c
+--------------
+
+Dieses Teil ist mittlerweile ueberholt; es stammt noch aus der Zeit, als
+'typo'- und aehnliche Meldungen noch in meinem eigenen .rep-File landeten
+und ich den Kram auch abarbeiten musste. Mit dem Tool konnte ich die Mel-
+dungen aus meinem .rep-File ansehen und eine Benachrichtigung an den Haus-
+besitzer generieren.
+
+
+5. Das Haus
+===========
+
+Das Haus selbst zerfaellt, was die Beschreibungen angeht, in drei grosse
+Bereiche: den von aussen sichtbaren Teil, die Raeume im Inneren, und die
+Truhe. Bei den Raeumen hat der Eingangsraum noch ein paar Sonderfunktionen.
+
+5a. Aussenansicht
+-----------------
+
+Dies ist das Objekt, das im ObjectDaemon angemeldet wird ("<name>haus").
+Der Hausbesitzer kann folgende Eigenschaften beschreiben:
+- die Langbeschreibung (P_LONG)
+- eine Zeile, wie die Haustuer aussehen soll (H_DOOR)
+- die Zustaende der Haustuer (offen, geschlossen, abgeschlossen; H_DOORLSTAT)
+
+Ausserdem stehen folgende Kommandos zur Verfuegung:
+- betritt/betrete <haus>
+  Sollte klar sein. Geht nur, wenn das Haus offen ist.
+- klopfe an <haus> an
+  Das Klopfen hoert man im ganze Haus
+- notiz <text>
+  Der Hausbesitzer kann damit eine kurze Notiz an der Haustuer anbringen.
+
+modules/haustuer.c stellt die Befehle zum Oeffnen und Schliessen des Hauses
+zur Verfuegung. Aufgeschlossen werden kann das Haus nur von Leuten mit
+spezieller Genehmigung.
+
+Wenn die Kurzbeschreibung geaendert werden soll, muss das ein Magier machen.
+Offizieller Ansprechpartner dafuer ist der Hausmaintainer (also DU :-)
+Man sollte dabei darauf achten, dass die Beschreibung a) in die Umgebung und
+b) allgemein in eine Fantasywelt passt (das ist leider nicht bei allen
+Haeusern der Fall...)
+Wenn die Kurzbeschreibung geaendert wird, sollten ggf. auch noch die IDs, der
+Name und das Geschlecht angepasst werden.
+Um ein Haus unsichtbar zu machen, muss P_SHORT auf 0 gesetzt werden.
+Wenn noch andere Seherhaeuser im gleichen Raum stehen, und in der Beschrei-
+bung des geaenderten Hauses nichts mehr auf ein "haus" hinweist, kann und
+sollte die ID "haus" entfernt werden.
+Um die Aenderungen zu sichern, muss im Haus Save() aufgerufen werden.
+
+Zwei IDs sollten nie entfernt werden: "sehe\rhaus" und "\n<name>haus"
+
+
+5b. Die Raeume
+--------------
+
+Programmtechnisch befinden sich hier hauptsaechlich Funktionen zum Beschrei-
+ben des Raumes, Hinzufuegen und Entfernen des Lichtes, An- und Abschalten
+des Lichtes und Einsehen des .rep-Files.
+Ausserdem kann man Personen, Gegenstaende oder alles aus dem Haus werfen.
+
+Auch die Raeume haben die ID "sehe\rhaus".
+
+Erwaehnenswerte Funktionen:
+
+normstr()
+  Ersetzt @@ in den Beschreibungen durch ** (damit kein process_string()
+  ausgefuehrt werden kann)
+
+brk()
+  Erzeugt aus einer durch Kommata getrennten Liste ein Array von Strings
+
+befCheck()
+  Faengt verbotene Befehle ab (Kommandos, die Leerzeichen enthalten; nicht
+  gesetzte Ausgaenge; alle Kommandos, die der Raum definiert, ausser
+  "oeffne", "schliess", "schliesse")
+
+owner_check()
+  Darf der Spieler das? Falls ohne Argument aufgerufen, ist die Aktion nur
+  erlaubt, wenn this_player() der Hausbesitzer ist.
+  Mit Argument wird auch die erlaube-Liste geprueft.
+
+arr_out()
+  Gibt ein Array von Strings aus oder zurueck, je nach Anwendung
+
+clean_up()
+  Sorgt dafuer, dass nur leere Raeume weggeraeumt werden
+
+BecomesNetDead()
+  Netztote verlassen das Haus (soweit sie nicht auf der erlaube-Liste stehen)
+
+init()/int_long()
+  Meldungen ueber unsichtbare Magier
+
+lies()
+  ReadDetails werden mit More() ausgegeben
+
+Die Befehle zur Beschreibung darf nur der Hausbesitzer benutzen. Den Befehl
+"uebersicht" koennen auch die Magier, die im access_rights.c stehen, ver-
+wenden.
+Naeheres zu den moeglichen Beschreibungen folgen unten.
+
+
+5c. Der Eingangsraum
+--------------------
+
+Der Eingangsraum stellt zusaetzlich noch folgende Befehle und Moeglichkeiten
+zur Verfuegung:
+
+- Oeffnen, Schliessen und Abschliessen des Hauses von innen
+- Einraeumen und Entziehen von Sonderrechten mit "erlaube" und "verbiete"
+- Benutzen des Gucklochs in der Tuer mit dem Befehl "spion"
+- Mitteilung ueber angesammelte 'typo'-Meldungen seit dem letzten Betreten
+- "uebersicht" zeigt zusaetzlich die Leute auf der erlaube-Liste
+
+
+5d. Die Truhe
+-------------
+
+Die Truhe kann unendlich viele Gegenstaende aufnehmen.
+Aendern lassen sich Geschlecht, Name, Adjektive (aus diesen dreien wird die
+Kurzbeschreibung zusammengesetzt), IDs, Langbeschreibung und Material.
+Die ID "\t\ruhe" ist IMMER gesetzt.
+Die Truhe laesst sich innerhalb des Hauses frei verschieben. In dem Raum,
+in dem sie steht, ist die Property H_CHEST auf 1 gesetzt.
+Typomeldungen der Truhe gehen in das Reportfile des Hausbesitzers.
+
+Problem: Ab und zu geben einige Spezialisten bei der Beschreibung der IDs
+schon die Langbeschreibung ein. Danach koennen sie die Truhe nicht mehr
+ansprechen.
+Loesung: Von Hand eine ID setzen und die Truhe mit Save() speichern.
+
+Ideen: Zustand der Truhe (geoeffnet/geschlossen) beschreibbar machen und
+optional ganz weglassen. Machbar ueber H_DOORLSTAT. In short() sind schon
+die entsprechenden Vorkehrungen getroffen; "beschreibe truhe status" wird
+auch schon abgefragt, aber noch nicht ausgewertet. Ausserdem muesste die
+Kurzbeschreibung dann explizit angegeben werden.
+
+Oeffnen koennen die Truhe nur Leute mit Erlaubnis. Schliessen kann sie
+jeder. Verschieben kann sie nur der Hausbesitzer.
+
+Die Daten der Truhe werden in _set_owner() geladen. Dieser Funktion wiederum
+wird von dem Raum aufgerufen, in dem die Truhe steht (modules/losa::Load()).
+
+
+6. Seherbank/Hauserwerb
+=======================
+
+Die Bank steht in Drachenhort. Wesentliche Bestandteile sind drei Schalter
+mit entsprechenden Warteschlangen.
+
+Der Antragsschalter - sb_antrag.c
+  Hier erhaelt man die Vertraege fuer ein Haus oder einen neuen Raum. Einen
+  Ausbauvertrag bekommt man allerdings erst dann, wenn das Haus schon steht.
+  Vertraege werden nur an Seher ausgehaendigt, die keinen PK haben.
+
+Der Vertrag - bausparvertrag.c
+  Erhaeltlich am Antragsschalter.
+  Der Vertrag muss auch am Antragsschalter unterschrieben werden. Die Unter-
+  schrift wird mit Blut bestaetigt, wodurch ein reduce_hit_point(50) faellig
+  wird. ;)
+  Auf ihm wird jede eingezahlte Rate vermerkt.
+  Will man doch kein Haus, kann man den Vertrag jederzeit zerreissen. Die
+  eingezahlten EP sind dann allerdings weg.
+  Nach der Unterschrift bekommt man den Master-Block ueberreicht.
+
+Der Master-Block - block.c
+  Der Block nimmt die aktuellen Raten auf. Dazu dient der block_shadow
+  (in /std/player/shadows/), der immer dann aktiviert wird, wenn man den
+  Block anzieht (er ist eine AT_MISC-Ruestung, schuetzt also nicht).
+  Der Shadow leitet AddExp()-Aufrufe an dem Block um. Bei mindestens
+  30-40 EP wird alles bis auf den Mindestanteil auf den Block eingezahlt,
+  und der Mindestanteil kommt dem Spieler zugute.
+
+Der Einzahlungsschalter - sb_einzahlung.c
+  Ist die Master-Block voll, kann man ihn hier vorlegen. Die Rate wird dann
+  auf den Vertrag gebucht, und man bekommt einen neuen Block fuer die
+  naechste Rate (soweit man noch nicht fertig ist).
+
+Der Ausgabeschalter - sb_ausgabe.c
+  Hat man alle Raten eingezahlt, kann man hier nach Vorlage des Vertrags die
+  Fruechte seiner Arbeit ernten.
+  Handelte es sich um einen Hausvertrag, bekommt man ein Instanthaus ausge-
+  haendigt.
+  Handelte es sich um einen Ausbauvertrag, wird die Raumzahl des Hauses um
+  eins erhoeht (man bekommt kein Objekt in die Hand; vielmehr kann man in
+  seinem Haus jetzt einen Ausgang in den neuen Raum legen).
+  ALLERDINGS: Um Haus oder Raum auszugeben, muessen in der Zentralbank
+  mindestens 30000 Muenzen vorhanden sein (damit deckt die Seherbank die
+  Materialkosten). Solange nicht genug Geld da ist, gibts auch kein Haus.
+  Der Seher hat zwei Alternativen: a) warten, bis genug Geld da ist, oder
+  b) zum Ende der Welt reisen und das Geld in den Geldspeicher der Zentral-
+  bank werfen.
+
+Eine Rate umfasst 80000 EP.
+Es gibt zwei Vertragsvarianten: sanft und schnell.
+Bei der sanften Variante hat man 4 Stunden Zeit, um die Rate zu sammeln, bei
+der schnellen Variante sind es 2 Stunden.
+Diese Zeiten werden nach Online-Zeit gemessen, nicht nach RL-Zeit. :)
+Fuer ein Haus benoetigt man bei den sanften Variante 30 Raten, bei der
+schnellen Variante 25 Raten.
+Fuer einen Raum sind es 12 Raten bei der sanften und 10 Raten bei der
+schnellen Variante.
+Ueberzieht man die Zeit, erhoeht sich die Ratenhoehe um 60% auf 128000 EP.
+Man hat bei beiden Varianten eine Stunde Zeit, die Rate noch zu erfuellen.
+Schafft man es nicht, verfaellt der Vertrag => alles ist weg.
+Das Zeitlimit gilt nur fuer das Fuellen des Blocks. Wenn er erst man voll
+ist, braucht man nicht innerhab des Limits zur Bank, um den naechsten
+Block zu holen.
+
+7. Beschreibungen
+=================
+
+Folgende Aspekte der Raumbeschreibung lassen sich veraendern:
+
+- Details      "beschreibe detail <det>"                     P_DETAILS
+- ReadDetails  "beschreibe lesbares detail <det>"            P_READ_DETAILS
+- Langbeschr.  "beschreibe haus/raum lang" (auch aussen)     P_(INT_)LONG
+- Kurzbeschr.  "beschreibe haus/raum kurz"                   P_INT_SHORT
+- Ausgaenge    "ausgang <richtung> <nr>" / "sperre richtung" P_EXITS
+- Licht        "licht an/aus"                                P_LIGHT
+- Befehle      "beschreibe befehl <bef>"                     H_COMMANDS
+
+Details, ReadDetails und Befehle lassen sich auch kopieren, allerdings immer
+nur innerhalb der jeweiligen Propertygruppe (es laesst sich also kein Detail
+zu den ReadDetails kopieren).
+
+Ausserdem lassen sich Details, ReadDetails, Befehle und die Langbeschreibung
+aendern. Das laeuft aehnlich wie beim Beschreiben, allerdings wird der
+aktuell vorhandene Text als Vorgabe verwendet.
+
+
+7a. Details/ReadDetails/Lang- und Kurzbeschreibung
+--------------------------------------------------
+
+Hier passiert nicht viel mit dem eingegebenen Text. @@ werden durch **
+ersetzt, damit nicht aus Versehen oder beabsichtigt process_string()
+ausgeloest wird.
+
+Bei der Kurzbeschreibung wird noch geprueft, ob sie laenger als 75 Zeichen
+oder eine Zeile ist (falls ja, wird sie nicht uebernommen).
+
+Moegliche Erweiterungen: Rassenspezifische Details (das muss bisher ueber
+Bfehle gemacht werden), und da AddSmells()/AddSounds() jetzt "offiziell" in
+sind, vielleicht auch die Moeglichkeit, Gerueche und Geraeusche zu
+beschreiben.
+
+7b. Ausgaenge
+-------------
+
+Moegliche Ausgaenge sind die acht Himmelsrichtungen, "oben" und "unten".
+Legt man einen Ausgang an, wird automatisch im Zielraum ein Ausgang in die
+entsprechende Rueckrichtung angelegt.
+Das Anlegen klappt nur, wenn es noch keinen Ausgang in diese Richtung gibt,
+und im Zielraum noch keinen Ausgang in die Rueckrichtung.
+Soll ein Ausgang in ein benachbartes Seherhaus gelegt werden, muss man dort
+darueberhinaus auf der erlaube-Liste stehen.
+Beim Sperren eines Ausgangs wird der entsprechende Ausgang im Zielraum
+automatisch mit entfernt.
+
+Der Grund fuer dieses symmetrische Verhalten liegt darin, dass man auf diese
+Weise keine Spieler in seinem Haus einsperren kann (Ausgang in einen Raum,
+ohne dass es zurueck geht).
+Um die Ausgaenge einander einfach zuordnen zu koennen, sind auch keine selbst
+erfundenen Richtungen moeglich (koennte man allerdings implementieren, indem
+man explizit nach der Rueckrichtung fragt).
+
+
+7c. Licht
+---------
+
+P_LIGHT wird so gesetzt, dass es im Haus mit dem momentanen Lichtlevel gerade
+dunkel oder hell wird. Hat man also nen Gesamtlichtlevel von 20 (durch
+genuegend Lichtkugeln o.ae.) und macht "licht aus", wird P_LIGHT auf -20
+gesetzt.
+
+
+7d. Befehle
+-----------
+
+Um die Auswertung zu vereinfachen, werden die Platzhalter in den Befehls-
+texten in kuerzere Token umgewandelt (Funktion preparse()).
+Die Auswertung erfolgt von modules/usercmd.c aus.
+
+In preparse() werden im wesentlichen die Faelle fuer Namen, Personal- und
+Possessivpronomina in die Zahlen umgesetzt, die den entsprechenden
+Funktionen (name(), QueryPronoun(), QueryPossPronoun()) uebergeben werden.
+Ausgewertet werden sie von usercmd::ucFilter().
+
+Die Auswertung rassenspezifischer Texte muss angepasst werden, wenn neue
+Rassen ins MG kommen. Betroffen davon ist usercmd::ucText().
+Die rassenspezifischen Texte werden durch Platzhalter der Form @RX vonein-
+ander getrennt, wobei X ein Buchstabe fuer die Rasse ist.
+Bei einer neuen Rasse muesste dieser Buchstabe in der regexplode-Zeile am
+Anfang von ucText() in den eckigen Klammern eingefuegt werden, und eine
+entsprechende Abfrage in die switch()-Anweisung rein.
+Und natuerlich sollte die Hilfeseite ("befehl") geupdated werden :)
+
+Bisher sind reine Textausgabebefehle moeglich.
+Einige Seher haben sich weitere Moeglichkeiten gewuenscht, z.B. so etwas
+wie SpecialDetails oder SpecialExits. Dafuer muesste man Zustaende setzen
+und aendern koennen (Property H_VARS oder so?). Allerdings wird dadurch
+die Beschreibung an sich wieder komplizierter.
+
+
+8. Rueckmeldungen
+=================
+
+Die Raeume, die Aussenansicht des Hauses und die Truhe haben eine Funktion
+SmartLog(). Diese Funktion wird von /std/player/base::smart_log() aus auf-
+gerufen und uebernimmt das Loggen der Typo-, Idee- und Fehlermeldungen.
+Die Meldungen werden int "rep/<name>.rep" abgelegt.
+Der Hausbesitzer wird jeweils beim Betreten des Eingangsraumes informiert,
+wie viele Rueckmeldungen sich seit dem letzten Betreten angesammelt haben.
+Ansehen kann er sich die Meldungen mit dem Befehl "meldungen", und mit
+"aendere" oder "beschreibe" kann er die Typos beheben.
+Mit "aendere meldungen" kann er die abgearbeiteten Meldungen aus seinem
+.rep-File entfernen.
+
+
+9. Hilfeseiten
+==============
+
+Die "Arbeitsversionen" der Seiten liegen in doc/, die "offiziellen" Versionen
+in /doc/scmd.
+"beschreibe befehl" hat eine Extraseite ("hilfe befehl"), weil die Seite fuer
+"beschreibe" sonst zu gross waere.
diff --git a/d/seher/haeuser/haus.c b/d/seher/haeuser/haus.c
new file mode 100644
index 0000000..d61471c
--- /dev/null
+++ b/d/seher/haeuser/haus.c
@@ -0,0 +1,361 @@
+//
+//  haus.c  -- Das Rohgeruest eines Seherhauses.
+//
+//  Grundobjekt (c) 1994 Boing@MorgenGrauen
+//  Abschliessen und Rauswerfen von Jof
+//  Fuer Aenderungen ab dem 06.10.94 verantwortlich: Wargon
+//
+// $Date: 1996/02/21 18:09:55 $
+// $Revision: 2.8 $
+/* $Log: haus.c,v $
+ * Revision 2.8  1996/02/21  18:09:55  Wargon
+ * SmartLog() fuer Rueckmeldungen eingebaut.
+ *
+ * Revision 2.7  1995/07/06  11:18:07  Wargon
+ * Langbeschreibung aenderbar.
+ *
+ * Revision 2.6  1995/04/21  10:48:20  Wargon
+ * Bugfix in "beschreibe tuerstatus".
+ *
+ * Revision 2.5  1995/04/21  08:56:55  Wargon
+ * Haustuer ausgelagert.
+ * 'beschreibe tuerzustand' ist dazugekommen.
+ *
+ * Revision 2.4  1995/02/27  20:47:32  Wargon
+ * Diverse Bugs gefixed...
+ *
+ * Revision 2.2  1995/02/15  11:19:17  Wargon
+ * Die Haustuer kann jetzt auch beschrieben werden (H_DOOR).
+ *
+ * Revision 2.1  1995/02/04  14:51:14  Wargon
+ * Statt "Haus" wird weitestgehend name() verwendet.
+ *
+ * Revision 2.0  1995/02/01  20:40:26  Wargon
+ * Das Haus ist jetzt nur noch ein Objekt in einem Raum, nicht
+ * mehr ein Raum im Raum.
+ *
+ */
+#include "haus.h"
+#include <properties.h>
+#include <moving.h>
+#include <wizlevels.h>
+
+inherit "/std/thing";
+inherit HAUSTUER;
+
+private nosave string text;
+private nosave string owner;
+
+void create()
+{
+  if (!clonep(this_object())) return;
+
+  thing::create();
+  haustuer::create();
+
+  SetProp(P_NAME, ({ "Haus", "Hauses", "Haus", "Haus" }));
+  SetProp(P_IDS, ({"haus","sehe\rhaus"}));
+  SetProp(P_GENDER, NEUTER);
+  SetProp(P_NOGET,1);
+
+  Set(P_LONG, SAVE, F_MODE);
+  Set(P_SHORT, SAVE, F_MODE);
+  Set(P_IDS, SAVE, F_MODE);
+  Set(P_GENDER, SAVE, F_MODE);
+  Set(P_NAME, SAVE, F_MODE);
+
+  text = 0;
+
+  AddCmd( ({"betritt", "betrete"}), "betritt" );
+  AddCmd( ({"beschreib", "beschreibe"}), "beschreiben");
+  AddCmd( ({"aender", "aendere"}), "aendern");
+  AddCmd( ({"klopf","klopfe"}), "klopfen");
+  AddCmd("erlaube", "erlauben");
+  AddCmd("verbiete", "verbieten");
+  AddCmd("notiz", "merktext");
+}
+
+varargs void Save(int crunched)
+{
+  save_object(HAUSSAVEPATH+owner);
+}
+
+void Load()
+{
+  restore_object(HAUSSAVEPATH+owner);
+}
+
+void init()
+{
+  // statt im Haus landet man im Hauptraum Raum 0, sollte ansich nicht passieren
+  // da man nomalerweise nicht ins Haus sondern direkt in den Hauptraum gemoved wird
+  if (environment(this_player()) == this_object()) {
+    this_player()->move(RAUMNAME(owner,0), M_GO | M_NO_SHOW | M_SILENT);
+    return;
+  }
+  ::init();
+}
+
+// Extra fuer Boing! ;)
+// Setzt man P_SHORT des Hauses auf 0, so verschwindet es zwar aus
+// der Beschreibung des Raumes, ist aber immer noch ansprechbar!
+string short()
+{
+  string ret;
+
+  ret = ::short();
+  if (previous_object() != environment() && !ret)
+    ret ="";
+
+  return ret;
+}
+
+// Zustand der Haustuer und evtl. Notiz mit einbauen.
+varargs string long(int mode)
+{
+  string ret, door;
+
+  door = haustuer::long(mode);
+
+  ret = QueryProp(P_LONG)+door;
+  if (text) {
+    ret += break_string( "An "+name(WEM,1)+" befindet sich eine Notiz: '"+text+"'.", 75);
+  }
+  return break_string(ret, 75);
+}
+
+// Setze neuen Besitzer fuer dieses Haus
+// setzt Default-Short, -Long und Id fuer neuen Besitzer,
+// leitet Besitzer an Haustuer weiter und laedt den Hauptraum
+varargs string SetOwner(string o)
+{
+  SetProp(P_SHORT, "Das Haus von "+capitalize(o));
+  SetProp(P_LONG, "Das Haus von "+capitalize(o)+", es sieht sehr nach einem Rohbau aus.\n");
+  AddId( ({ "\n"+o+"haus", "haus von "+o }) );
+  haustuer::SetTuerOwner(o);
+  load_object(RAUMNAME(o,0));
+  return owner = o;
+}
+
+// liefert den Name des Haus-Besitzers
+string QueryOwner() { return owner; }
+
+private int tp_ownercheck()
+{
+  if(getuid(this_player()) != owner)
+  {
+    notify_fail( "Das ist nicht Dein Haus!\n" );
+    return 0;
+  }
+  return 1;
+}
+
+// Aktion zum Betreten
+static int betritt(string str)
+{
+  notify_fail("Was moechtest Du betreten?\n");
+
+  // Haus war nicht angesprochen
+  if (!str || !id(str))
+    return 0;
+
+  // Haustuer ist zu
+  if (Query(H_DOORSTAT) & D_CLOSED) {
+    printf("%s von %s ist zu.\n", capitalize(name(WER,1)), capitalize(owner));
+    return 1;
+  }
+  // bewege Spieler in Hauptraum
+  this_player()->move(RAUMNAME(owner,0), M_GO, 0, "betritt "+name(WEN,1), "kommt herein");
+  return 1;
+}
+
+// Aktion zum Setzen/Loeschen von Notizen am Haus
+static int
+merktext(string str)
+{
+  // darf der Spieler das, d.h. ist das sein Haus?
+  if(!tp_ownercheck())
+  {
+    notify_fail( "Du kannst keine Notiz an "+capitalize(name(WEM,1))+" von "+capitalize(owner)+" anbringen!\n" );
+    return 0;
+  }
+
+  str = this_player()->_unparsed_args();
+
+  // kein Text => Notiz loeschen, ansonsten => Text als Notiz setzen
+  if (!str || str == "") {
+    write( "OK, Notiz ist geloescht.\n" );
+    text = 0;
+  }
+  else {
+    write( "OK, Notiz lautet '"+str+"'\n");
+    text = str;
+  }
+
+  return 1;
+}
+
+// Aktion Anklopfen am Haus
+static int
+klopfen(string str)
+{
+  int i;
+  object room;
+
+  notify_fail( "Syntax: klopfe an <haus> an\n" );
+  // parsen von klopfe an <haus> an
+  if (!str || sscanf(str, "an %s an", str) != 1)
+    return 0;
+
+  if (str == "tuer" || str == "haustuer" || id(str)) {
+    // Meldungen an Spieler und Raum
+    printf( "Du klopfst an "+name(WEM,1)+" von "+capitalize(owner)+" an.\n");
+    say( capitalize(this_player()->name(WER))+" klopft an "+name(WEM,1)+" von "+capitalize(owner)+" an.\n" );
+    // in allen geladenen Raeumen Klopfen melden
+    for (i=VERWALTER->HausProp(owner,HP_ROOMS); i>=0; i--) {
+      if (room = find_object(RAUMNAME(owner,i)))
+	tell_room(room, "Du hoerst, wie jemand an der Haustuer anklopft.\n" );
+    }
+    return 1;
+  }
+  return 0;
+}
+
+// Aktion Tuer/Haus beschreiben
+static int
+beschreiben(string str)
+{
+  string *p, *ds;
+
+  // darf der Spieler das, d.h. ist das sein Haus?
+  if (!tp_ownercheck())
+    return 0;
+
+  notify_fail( "Hier draussen funktioniert nur 'beschreibe <haus> lang',\n"
+	      +"'beschreibe haustuer' oder 'beschreibe tuerzustand'!\n");
+
+  // zu wenig Eingabe zum Parsen
+  if (!str || str == "")
+    return 0;
+
+  p = old_explode(str," ");
+
+  if (p[0] == "tuer" || p[0] == "haustuer") {
+    // beschreibe (haus)tuer
+    write( "Beschreibung der Haustuer angeben. Es ist nur eine Zeile moeglich.\n"
+	  +"Als Platzhalter fuer den Zustand der Tuer dient %s. Falls die Tuer\n"
+	  +"nicht in der Langebschreibung des Hauses auftauchen soll, bitte ein-\n"
+	  +"fach nur <RETURN> druecken.\n]" );
+    input_to("doorDesc");
+    return 1;
+  }
+  else if (p[0] == "tuerzustand") {
+    // beschreibe tuerzustand
+    ds = Query(H_DOORLSTAT);
+    write( "Beschreibung des Tuerzustandes angeben. Es ist nur eine Zeile moeg-\n"
+	  +"lich. Die Zustaende muessen durch Kommata getrennt in der Reihen-\n"
+	  +"folge 'offen', 'geschlossen', 'abgeschlossen' angegeben werden.\n"
+	  +"Momentaner Zustand:\n");
+    printf("'%s', '%s', '%s'.\n", ds[0], ds[1], ds[3]);
+    write( "(Abbruch: Einfach <RETURN> druecken!)\n]");
+    input_to("doorLDesc");
+    return 1;
+  }
+  else if (p[<1] == "lang" && id(implode(p[0..<2]," "))) {
+    object home = load_object(RAUMNAME(owner,0));
+    // beschreibe <haus> lang
+    home->beschreiben("haus lang", AUSSEN);
+    return 1;
+  }
+  // Eingabe Syntax nicht passend => notify_fail von oben
+  return 0;
+}
+
+// Aktion Haus Aendern
+static int
+aendern(string str)
+{
+  string *p, *ds;
+
+  // Darf der Spieler das, d.h. ist das sein Haus?
+  if (!tp_ownercheck())
+    return 0;
+
+  notify_fail( "Hier draussen funktioniert nur 'aendere <haus> lang'!\n");
+  // zu wenig Eingabe zum Parsen
+  if (!str || str == "")
+    return 0;
+
+  p = old_explode(str," ");
+
+  if (p[<1] == "lang" && id(implode(p[0..<2]," "))) {
+    object home = load_object(RAUMNAME(owner,0));
+    // aendere <haus> lang
+    home->aendern("haus lang", AUSSEN);
+    return 1;
+  }
+  // Eingabe Syntax nicht passend => notify_fail von oben
+  return 0; 
+}
+
+// input_to zu beschreibe (haus)tuer
+static void doorDesc(string str)
+{
+  if (!str)
+    str = "";
+  if (sizeof(str))
+    str += "\n";
+
+  Set(H_DOOR, str);
+  Save();
+
+  if (str == "")
+    write("OK, Haustuer wurde geloescht.\n");
+  else
+    write("OK, Beschreibung der Haustuer wurde gesetzt.\n");
+}
+
+// input_to zu beschreibe tuerzustand
+static void doorLDesc(string str)
+{
+  string *s;
+
+  if (!str || sizeof(s=old_explode(str, ","))!=3) {
+    write("ABBRUCH!\nNichts geaendert!\n");
+    return;
+  }
+  Set(H_DOORLSTAT, ({ s[0], s[1], 0, s[2] }));
+  printf("OK, Tuerzustaende sehen wie folgt aus:\n'%s', '%s', '%s'.\n",
+	 s[0], s[1], s[2]);
+  Save();
+}
+
+int SmartLog(string ofile, string typ, string msg, string date)
+{
+  object home;
+  string foo, bar;
+  
+  string hn=RAUMNAME(owner,0);
+
+  write_file(REPFILE(owner), sprintf("%s von %s vor dem Haus (%s):\n%s\n",
+				     typ,
+				     capitalize(getuid(this_player())),
+				     date,
+				     break_string(msg,78)));
+
+  if (IS_LEARNER(owner)) {
+    log_file("report/"+owner+".rep",
+	     sprintf("MELDUNG von %s im Seherhaus, vor dem Haus (%s):\n"
+		     +"Bitte zur Kenntnis nehmen! (Mit dem Befehl 'meldungen')  -Wargon\n",
+		     capitalize(getuid(this_player())),
+		     date));
+  }
+
+  home = load_object(hn);
+  home->Set(H_REPORT, home->Query(H_REPORT)+1);
+  home->Save();
+
+  return 1;
+}
+
+
diff --git a/d/seher/haeuser/haus.h b/d/seher/haeuser/haus.h
new file mode 100644
index 0000000..873956e
--- /dev/null
+++ b/d/seher/haeuser/haus.h
@@ -0,0 +1,156 @@
+// haus.h -- Definitionen fuer die Seherhaeuser
+//
+//  Grundobjekt (c) 1994 Boing@MorgenGrauen
+//  Abschliessen und Rauswerfen von Jof
+//  Fuer Aenderungen ab dem 06.10.94 verantwortlich: Wargon
+//
+// $Id: haus.h,v 1.2 2003/05/03 13:09:05 mud Exp $
+//
+#ifndef __HAUS_H__
+#define __HAUS_H__
+
+// #define DEBUG(x)	if (find_player("vanion")) tell_object(find_player("vanion"),x);
+//#define HDEBUG(x) if(find_player("vanion")) \
+//	              tell_object(find_player("vanion"),break_string((x),78,"HDBG: ",1))
+#define HDEBUG(x)
+
+// Maintainer wird u.a. benutzt, um die access_rights zu regeln
+#define MAINTAINER ({"vanion"})
+#define IS_MAINTAINER(x) (member(MAINTAINER, getuid(x))!=-1)
+
+/*** Pfade ***/
+#define HAEUSERPFAD     "/d/seher/haeuser/"
+#define HAUSSAVEPATH	HAEUSERPFAD+"save/"
+#define SPECIALPATH	HAEUSERPFAD+"special/"
+#define VERWALTER	(HAEUSERPFAD+"hausverwalter")
+#define HAUS		HAEUSERPFAD+"haus"
+#define RAUM		HAEUSERPFAD+"raum"
+#define SAVEFILE	HAUSSAVEPATH+"haeuser"
+#define HAUSNAME(x)	(HAEUSERPFAD+x+"haus")
+#define RAUMNAME(x,n)	(HAEUSERPFAD+x+"raum"+to_string(n))
+#define REPFILE(x)	(HAEUSERPFAD+"rep/"+x+".rep")
+#define HLOG(f,x) write_file(HAEUSERPFAD+"log/"+(f), dtime(time())+" - "+(x))
+
+/* PATH bitte nicht mehr benutzen!
+ * Dieser #define existiert nur noch aus Kompatibilitaetsgruenden.
+ */
+#ifndef PATH
+#define PATH		HAEUSERPFAD
+#endif
+
+/*** Module ***/
+#define USERCMD HAEUSERPFAD+"modules/usercmd"
+#define LOSA	HAEUSERPFAD+"modules/losa"
+#define HAUSTUER HAEUSERPFAD+"modules/haustuer"
+
+/*** Bausparvertrag und Ratenblock ***/
+#define SUMME_L 30*RATENHOEHE	  /* Soviel kostet das gute Stueck (langsam) */
+#define SUMME_S 25*RATENHOEHE	  /* Und soviel bei der schnellen Variante */
+#define RATENHOEHE 80000  /* Einzahlbar per Ratenblock */
+#define MAX_TIME 7200	  /* Laufzeit der Raten: 4 Stunden = 7200 heart_beats */
+#define WARN_TIME 450	  /* Abstand zwischen den Warnungen: 15 Minuten */
+
+#define V_MONEY 0   // Das schon eingezahlte Geld
+#define V_DLINE 1   // die Deadline
+#define V_WTIME 2   // Zeit bis zur naechsten Erinnerung
+#define V_FLAGS 3   // Diverse Flags
+
+#define V_SIGNED 1  // Vertrag schon unterschrieben
+#define V_FAST	 2  // Schnelle Variante
+#define V_COMPL  4  // Vertrag erfuellt
+#define V_RAUM	 8  // Ausbauvertrag
+#define V_EP	16  // EP-Vertrag
+
+#define B_FAST	 1  // Block gehoert zur schweren Variante
+#define B_EXTEND 2  // Strafzeit laeuft
+#define B_ACTIVE 4  // Shadow laden
+#define B_SHADOW 8  // Shadow laeuft...
+#define B_EP	16  // EP-Vertrag
+
+/*** Tuerstatus ***/
+#define D_CLOSED 1
+#define D_LOCKED 2
+
+/*** Beschreibungsflags ***/
+#define AUSSEN	1
+#define KURZ	2
+#define LANG	4
+#define DETAIL	8
+#define RDETAIL 16
+#define BEFEHL	32  // Nur aendern
+#define REPORT	64  // Nur aendern
+
+/*** Flags fuer HausProp() ***/
+#define HP_ENV	    0 // Name des Raums, in dem das Haus steht (string)
+#define HP_ROOMS    1 // Anzahl der Raeume, die das Haus besitzt (int)
+#define HP_ALLOWED  2 // Wer hat Sonderbefugnisse im Haus? (string *)
+
+/*** Vermischtes ***/
+#define H_CHEST "h_chest"         // Wenn gesetzt, steht in diesem Raum die Truhe
+#define TRUHE "\t\ruhe"           // Und diese ID hat die Truhe in jedem Fall.
+#define H_COMMANDS "h_commands"   // Enthaelt mapping mit Befehlen.
+#define H_DOOR "h_door"           // Enthaelt die Beschreibung der Tuer.
+#define H_DOORSTAT "h_doorstat"   // Enthaelt den Zustand der Haustuer.
+#define H_DOORLSTAT "h_doorlstat" // Tuerzustand als Stringarray.
+#define H_REPORT "h_report"       // Info ueber typo/bug/idee im Haus
+#define H_SPECIAL "h_special"     // Besonderheiten im Haus
+#define H_FURNITURE "h_furniture" // Rebootfeste Sehermoebel
+
+/*** Typen fuer H_SPECIAL ***/
+#define HS_ITEM 1  // ein bestimmtes Objekt (per AddItem())
+#define HS_EXIT 2  // ein Ausgang (per AddExit())
+
+/*** Einstellungen fuer Furniture ***/
+#define MAX_FURNITURE_PER_ROOM 5
+
+/*** Last but not least... ***/
+#define UP_ARGS(o) ((o)->_unparsed_args(1))
+
+#endif	// __HAUS_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __HAUS_H_PROTOS__
+#define __HAUS_H_PROTOS__
+
+varargs void Save(int crunched);
+void Load();
+varargs string QueryOwner(int withNr);
+varargs string SetOwner(string o, int nr);
+protected void AddExitNoCheck(mixed cmd, mixed dest);
+void RemoveExitNoCheck(mixed cmd);
+
+#endif	/* __HAUS_H_PROTOS__ */
+#endif	/* NEED_PROTOTYPES */
+
+// $Log: haus.h,v $
+// Revision 1.2  2003/05/03 13:09:05  mud
+// PATH in HAEUSERPFAD umbenannt
+//
+// Revision 1.1.1.1  2000/08/20 20:22:42  mud
+// Ins CVS eingecheckt
+//
+// Revision 2.7  1995/06/28  08:58:07  Wargon
+// H_REPORT fuer Spielerrueckmeldungen.
+// Flags BEFEHL und REPORT fuer Aenderungen.
+//
+// Revision 2.6  1995/04/21  08:53:57  Wargon
+// #defines fuer Modularisierung und neue Tuerfeatures.
+//
+// Revision 2.5  1995/02/27  20:54:49  Wargon
+// UP_ARGS angepasst, da Parser jetzt auch fuer Spieler aktiv.
+//
+// Revision 2.3  1995/02/15  11:15:08  Wargon
+// H_DOOR: Enthaelt die Beschreibung der Haustuer.
+//
+// Revision 2.2  1995/02/04  17:18:39  Wargon
+// H_COMMANDS: enthaelt mapping mit selbstdefinierten Hausbefehlen.
+// CHEST in H_CHEST umbenannt.
+//
+// Revision 2.1  1995/02/04  15:06:18  Wargon
+// Definition von CHEST als Property fuer den Standort der Truhe und
+// TRUHE als feste ID der Truhe.
+//
+// Revision 2.0  1995/02/01  20:45:18  Wargon
+// UP_ARGS-Makro, liefert fuer ALLE das Ergebnis von _unparsed_args(1)
+//
diff --git a/d/seher/haeuser/hausverwalter.c b/d/seher/haeuser/hausverwalter.c
new file mode 100644
index 0000000..26643d1
--- /dev/null
+++ b/d/seher/haeuser/hausverwalter.c
@@ -0,0 +1,621 @@
+/*
+ *  hausverwalter.c -- Verwaltung der Seherhaeuser
+ *
+ *  Das Grundobjekt stammt von Boing, Fortfuehrung erfolgte durch Jof.
+ *  Letzte Aenderungen verschuldet durch Wargon ;)
+ *
+ * $Date: 1997/09/09 17:19:29 $
+ * $Revision: 2.3 $
+ * $Log: hausverwalter.c,v $
+ * Revision 2.3  1997/09/09 17:19:29  Wargon
+ * Bugfix beim Verlegen/Loeschen eines Hauses
+ *
+ * Revision 2.2  1996/02/21  18:15:02  Wargon
+ * *** empty log message ***
+ *
+ * Revision 2.0  1994/11/17  13:48:27  Wargon
+ * Modifikationen fuer die Trennung Haus/Raum.
+ *
+ * Revision 1.5  1994/10/24  08:21:55  Wargon
+ * Parameter fuer NeuesHaus geaendert.
+ * Fuer Sicherheitscheck secure() eingebaut.
+ * VerlegeHaus() eingebaut, falls ein Haus mal verlegt werden muss.
+ *
+ * Revision 1.4  1994/10/10  21:50:59  Wargon
+ * NeuesHaus() und LoescheHaus() bedienen nun auch den OBJECTD.
+ * PruefeHaus() wurde damit hinfaellig.
+ *
+ * Revision 1.3  1994/10/09  20:11:48  Wargon
+ * Beschreibung der Haeuser vom Hausverwalter abgekoppelt!
+ * Die megamap enthaelt nur noch Besitzer und Standort des Hauses.
+ * Infolgedessen sind Save() und build() rausgeflogen...
+ *
+ * Revision 1.2  1994/10/07  22:19:48  Wargon
+ * AUFBAU DES MAPPINGS GEAENDERT! Der Filename des Raumes, in dem das
+ * Haus steht, steht jetzt als erster Eintrag im Mapping! Alle anderen
+ * Eintraege sind um 1 weitergewandert.
+ * Beim Laden des Verwalters werden nicht mehr alle Seherhaeuser ge-
+ * laden. Ein Haus wird erst geladen, wenn der Raum, in dem es steht,
+ * geladen wird (PruefeHaus(), siehe auch std/room::create()).
+ *
+ * Revision 1.1  1994/10/07  14:19:36  Wargon
+ * Initial revision
+ *
+ */
+#pragma strict_types
+#include <properties.h>
+#include <wizlevels.h>
+#include <rooms.h>
+#include <moving.h>
+#include "haus.h"
+
+#define H_MAX_ROOMS 9
+
+mapping megamap;
+
+// Haus fuer owner im Raum env erstellen. Wird i.d.R nur vom Instanthaus gemacht.
+void NeuesHaus(string owner, object env);
+
+// Haus von owner loeschen (samt Savefile!). Dieser Vorgang ist unwiderruflich!
+int LoescheHaus(string owner);
+
+// Loescht den letzten hinzugefuegten Raum im Seherhaus von 'owner'.
+void LoescheRaum(string owner);
+
+// Fuegt einen Raum zum Seherhaus von 'owner' hinzu.
+void NeuerRaum(string owner);
+
+// Haus von owner vom Raum 'von' in den Raum 'nach' verschieben.
+int VerlegeHaus(string owner, string von, string nach);
+
+// Kann in ob ein Haus gebaut werden? 0: Ja, sonst Fehler!
+int Unbebaubar(object ob);
+
+// Jemandem im Haus Zusatzrechte einraeumen/entziehen
+string *Erlaube(string owner, string *wer);
+string *Verbiete(string owner, string *wer);
+
+// Eigenschaften aus der megamap abfragen
+mixed HausProp(string owner, int prop);
+
+// Propertymapping deduplizieren
+mixed PCrunch(mapping prop);
+
+// Lade Seherhaus von Besitzer 'owner'
+object _LadeHaus(string owner);
+
+// Lade im Seherhaus von 'owner' den Raum mit der Nummer 'num'
+object _LadeRaum(string owner, int num);
+
+// returnt das Seherhaus von Besitzer 'owner'
+object FindeHaus(string owner);
+
+void create()
+{
+  if (!restore_object(SAVEFILE))
+    megamap = ([ ]);
+  seteuid(getuid(this_object()));
+}
+
+int query_prevent_shadow(object ob)
+{
+  HLOG("SHADOW",sprintf( "%s, von %O im Verwalter.\n",dtime(time())[5..], ob));
+  return 1;
+}
+
+private int
+secure()
+{
+  int ar;
+
+  if (!this_interactive())
+    return 0;
+
+  // das tragbare Instanthaus und die Hausausgabe duerfen:
+  if ((load_name(previous_object()) == PATH+"traghaus") ||
+      (load_name(previous_object()) == PATH+"sb_ausgabe")) {
+    return 1;
+  }
+  
+  catch(ar = (int)(PATH+"access_rights")->access_rights(geteuid(this_interactive()), "haus.h"));
+
+  // Erzmagier und alle mit Schreibrechten auf haus.h duerfen
+  if ( (this_interactive() == this_player()) &&
+      (IS_ARCH(this_interactive()) || ar ) )
+    return 1;
+  return 0;
+}
+
+// ersetzt das HAEUSER logfile mit neuer Statistik
+private void
+dump()
+{
+  string *ind;
+  int i, hnum, rnum = 0;
+
+  // loesche logfile
+  rm(PATH+"HAEUSER");
+
+  // betrachte alle Seherhaeuser
+  ind = m_indices(megamap);
+  if (hnum=sizeof(ind)) {
+    write_file(PATH+"HAEUSER", sprintf("Es gibt %d Seherhaeuser:\n", hnum));
+    ind = sort_array(ind,#'>); //'
+    // alle Haeuser sortiert nach Besitzername durchgehen:
+    for(i = 0; i < hnum; ++i) {
+      // zaehle Raeume
+      ++rnum; // Hauptraum
+      rnum += (megamap[ind[i], HP_ROOMS]); // Nebenraeume
+      // Eine Zeile pro Haus: Besitzername (Raumanzahl) Standort-Pfad
+      write_file(PATH+"HAEUSER",
+                 sprintf( "%-13s (%d) %s\n",
+                          capitalize(ind[i]),
+                          megamap[ind[i],HP_ROOMS],
+                          megamap[ind[i],HP_ENV] ) );
+    }
+    write_file(PATH+"HAEUSER", sprintf("Es gibt insgesamt %d Raeume.\n", rnum));
+  }
+  else
+    write_file(PATH+"HAEUSER", "KEINE HAEUSER!\n");
+}
+
+// Gegenrichtungen
+#define X_EXIT (["oben":"unten", "unten":"oben",\
+		 "westen":"osten", "osten":"westen",\
+		 "sueden":"norden", "norden":"sueden",\
+		 "nordosten":"suedwesten", "suedwesten":"nordosten",\
+		 "nordwesten":"suedosten", "suedosten":"nordwesten" ])
+
+// fuer jeden Raum im Haus [max .. 0] betrachte alle Ausgaenge;
+// zaehle alle Ausgaenge ausser der Haustuer in Raeume,
+// die nicht zu diesem Seherhaus gehoeren
+// (dies sollten Uebergaenge zu anderen Seherhaeusern sein)
+// falls rem != 0 loesche die Gegenrichtung zu diesen Ausgaengen,
+// d.h. kappe alle Uebergaenge aus anderen Seherhaeusern in dieses
+private int
+check_exits(string owner, int max, int rem)
+{
+  int x, nr, bar;
+  string hname, foo;
+  object here, there;
+
+  x = 0;
+  for (nr = max; nr >= 0; --nr) {
+    // betrachte jeden Seherhausraum mit index max .. 0
+    hname = RAUMNAME(owner, nr);
+
+    if (catch(here = load_object(hname);publish)) {
+      printf("error loading %O!\n", hname);
+      continue;
+    }
+    foreach (string dir, string path : (mapping)(here->QueryProp(P_EXITS))) {
+      // betrachte alle Ausgaenge
+      if (dir == "raus") {
+        // Haustuer aus dem Hauptraum darf natuerlich rausfuehren
+        continue;
+      }
+      if ((sscanf(path, PATH+"%sraum%d", foo, bar) != 2) || (foo != owner)) {
+        // Raum in den der Ausgang fuehrt ist nicht in diesem Seherhaus
+        ++x;
+        if (rem) {
+          catch(there = load_object(path);publish);
+          if (there) {
+            // loesche die Gegenrichtung zu dem Ausgang
+            there->RemoveExit(X_EXIT[dir]);
+            there->Save();
+          }
+        }
+      }
+    }
+  }
+  return x;
+}
+
+// Haus fuer owner im Raum env erstellen.
+// Wird i.d.R nur vom Instanthaus gemacht.
+void NeuesHaus(string owner, object env)
+{
+  object h;
+
+  // keine passenden Rechte
+  if (!secure())
+    return;
+
+  // neuen Eintrag im Verwalter-Mapping fuer das Haus erstellen
+  megamap += ([ owner : object_name(env); 0; ({}) ]);
+  // Haus am Bauplatz laden, falls moeglich
+  catch(h = load_object(HAUSNAME(owner));publish);
+  if (!h)
+    return;
+
+  // Haus Speichern und als Raumautoloader eintragen
+  h->Save();
+  OBJECTD->AddObject(h, object_name(env));
+  // Bauplatz auf never clean setzen und Verwalter abspeichern
+  env->SetProp(P_NEVER_CLEAN, 1);
+  save_object(SAVEFILE);
+
+  // Hauptraum des Seherhauses laden
+  h = load_object(RAUMNAME(owner,0));
+  h->SetProp(H_CHEST,1);
+  // Hauptraum speichern
+  h->Save();
+  // Truhe laden
+  h->Load();
+  // Statistik ueber alle Seherhaeuser erneuern
+  dump();
+}
+
+// loescht den letzten hinzufuegten Raum im Seherhaus von 'owner'
+void LoescheRaum(string owner)
+{
+  object raumob;
+  int nr;
+
+  // kein passendes Seherhaus verwaltet oder kein Recht das zu tun
+  if (!member(megamap, owner) || !secure())
+    return;
+
+  nr = megamap[owner, HP_ROOMS];
+  // falls das Haus ueberhaupt zusaetzliche Raeume (neben Hauptraum) hat
+  if (nr > 0 ) {
+    // falls geladen, remove Raum-Objekt
+    raumob = find_object(RAUMNAME(owner,(megamap[owner,HP_ROOMS])));   
+    if (objectp(raumob))
+      raumob->remove(1);
+
+    // loesche Raum aus Verwalter-Mapping durch Anzahl um eins erniedrigen
+    --megamap[owner, HP_ROOMS];
+
+    // savefile muss per Hand geloescht werden:
+    tell_object(this_interactive(),
+                break_string(sprintf("Vergiss nicht, das Savefile zu loeschen, "
+                                     "also: "+HAUSSAVEPATH+"%s%d.o\n",
+                                     owner, nr),
+                             78));
+    // speicher Hausverwaltung ab und erneuer Statistik ueber alle Seherhaeuser
+    save_object(SAVEFILE);
+    dump();
+  }
+}
+
+// Fuegt einen Raum zum Seherhaus von 'owner' hinzu.
+void NeuerRaum(string owner)
+{
+  object raumob;
+
+  // kein passendes Seherhaus verwaltet oder kein Recht das zu tun
+  if (!member(megamap, owner) || !secure())
+    return;
+
+  // ist die Maximalanzahl von Raeumen schon erreicht?
+  if (megamap[owner, HP_ROOMS] < H_MAX_ROOMS)
+  {
+    // erhoehe Raumzaehler in Verwalter-Mapping
+    megamap[owner, HP_ROOMS]++;
+    // lade neuen Raum, falls moeglich
+    catch(raumob = load_object((RAUMNAME(owner,(megamap[owner,
+                                         HP_ROOMS]))));publish);
+    if(objectp(raumob))
+      // speicher neuen Raum
+      raumob->Save();
+
+    // speicher Verwalter-Mapping und erneuer Statistik ueber alle Seherhaeuser
+    save_object(SAVEFILE);
+    dump();
+  }
+}
+
+// Lade Seherhaus von Besitzer 'owner'
+object _LadeHaus(string owner)
+{
+  object haus;
+  string o;
+
+  // es wird kein passendes Seherhaus verwaltet
+  if (!member(megamap, owner))
+    return 0;
+
+  // Haus ist bereits geladen
+  if (haus=find_object(HAUSNAME(owner)))
+    return haus;
+
+  // lade Bauplatz
+  o = megamap[owner];
+  if (catch(load_object(o);publish))
+  {
+    write_file(PATH+"hauserror", o+" konnte nicht geladen werden.\n");
+    return 0;
+  }
+  // Haus ist durch Laden des Bauplatzes nun geladen
+  if (haus = find_object(HAUSNAME(owner)))
+    return haus;
+
+  // clone Standard-Haus, setze Besitzer
+  haus = clone_object(HAUS);
+  haus->move(o, M_NOCHECK);
+  haus->SetOwner(owner, find_object(RAUMNAME(owner,0)));
+  // lade individualisiertes Haus aus dem Savefile
+  haus->Load();
+
+  return haus;
+}
+
+// Lade im Seherhaus von 'owner' den Raum mit der Nummer 'num'
+object _LadeRaum(string owner, int num)
+{
+  object raum;
+
+  // es wird kein passendes Seherhaus verwaltet
+  if (!member(megamap, owner))
+    return 0;
+
+  // Raumnummer nicht zwischen 0 und letzter Raumnummer
+  if (num < 0 || num > megamap[owner,HP_ROOMS])
+    return 0;
+
+  // Raum ist bereits geladen
+  if (raum = find_object(RAUMNAME(owner,num)))
+    return raum;
+
+  // clone passenden Raum (0: Hauptraum, X: Nebenraum X) und setze Besitzer
+  raum = clone_object(num ? (RAUM) : (PATH+"raum0"));
+  raum->SetOwner(owner, num);
+  // lade Moebel, z.B. Seherhaustruhe
+  raum->Load();
+  // Hauptraum bekommt Haustuer-Ausgang zum Bauplatz
+  if (!num)
+    raum->AddExitNoCheck("raus", megamap[owner]);
+
+  return raum;
+}
+
+// returnt das Seherhaus-Objekt von Besitzer 'ow'
+// nur zum Loeschen oder Wegbewegen daher einfacher als _LadeHaus
+object FindeHaus(string ow)
+{
+  // es wird kein passendes Seherhaus verwaltet
+  if (!member(megamap, ow))
+    return 0;
+  return load_object(HAUSNAME(ow));
+}
+
+// Haus von owner loeschen (samt Savefile!). Dieser Vorgang ist unwiderruflich!
+int LoescheHaus(string owner)
+{
+  object haus;
+  int rooms;
+  string tmp;
+
+  // keine passenden Rechte fuers Loeschen
+  if (!secure())
+    return -1;
+
+  // Haus-Objekt finden, als Raumautoloader im Bauplatz austragen und entfernen
+  haus = FindeHaus(owner);
+  if (!haus)
+    return -2;
+  OBJECTD->RemoveObject(haus, object_name(environment(haus)));
+  environment(haus)->RemoveItem(object_name(haus));
+
+  // Raumanzahl merken
+  rooms = megamap[owner,HP_ROOMS];
+
+  // Haus aus Verwalter-Mapping loeschen
+  megamap = m_delete(megamap, owner);
+
+  // Verwalter-Mapping abspeichern
+  save_object(SAVEFILE);
+
+  // Uebergaenge von anderen Seherhaeusern zu diesem entfernen
+  check_exits(owner, rooms, 1);
+
+  // Savefile fuer das Haus entfernen
+  if (file_size(HAUSSAVEPATH+owner+".o")>0)
+    rm(HAUSSAVEPATH+owner+".o");
+
+  // Savefiles fuer die Raeume entfernen
+  do {
+    if (file_size(tmp = sprintf((HAUSSAVEPATH+"%s%d.o"),owner,rooms))>0)
+      rm(tmp);
+  } while (--rooms >= 0);
+
+  // Savefile fuer die Truhe loeschen
+  if (file_size(HAUSSAVEPATH+owner+"truhe.o")>0)
+    rm(HAUSSAVEPATH+owner+"truhe.o");
+
+  // Repfile fuer das Seherhaus loeschen
+  // TODO: Eintraege aus ERRORD loeschen.
+  if (file_size(PATH+"rep/"+owner+".rep") > 0)
+    rm(PATH+"rep/"+owner+".rep");
+
+  // Statistik ueber alle Seherhaeuser erneuern
+  dump();
+  return 1;
+}
+
+// Haus von owner vom Raum 'von' in den Raum 'nach' verschieben.
+int VerlegeHaus(string owner, string von, string nach)
+{
+  object h, ziel;
+
+  // kein Recht das zu tun
+  if (!secure())
+    return -111;
+
+  // zu verlegendes Haus nicht auffindbar
+  if (!(h=FindeHaus(owner)))
+    return -1;
+
+  // aktueller Standort des Hauses ist nicht Startpunkt von
+  if (object_name(environment(h)) != von)
+    return -2;
+
+  // Ziel-Standort nicht ladbar
+  catch(ziel = load_object(nach);publish);
+  if (!ziel)
+    return -3;
+
+  // Am Zielort darf kein Haus gebaut werden
+  if (Unbebaubar(ziel))
+    return -4;
+
+  // Seherhaus ist mit anderem Seherhaus verbunden und kann daher nicht
+  // verschoben werden
+  if (check_exits(owner, megamap[owner,HP_ROOMS], 0))
+    return -5;
+
+  // neuen Standort in Verwalter-Mapping eintragen
+  megamap[owner] = nach;
+  // Raumautoloader am alten Standort austragen und am neuen eintragen
+  OBJECTD->RemoveObject(h, von);
+  OBJECTD->AddObject(h, nach);
+  // Haus bewegen
+  h->move(nach, M_NOCHECK);
+  // Haustuer-Ausgang umtragen und Hauptraum speichern
+  catch(RAUMNAME(owner,0)->AddExitNoCheck("raus", nach);publish);
+  catch(RAUMNAME(owner,0)->Save();publish);
+  // Verwalter-Mapping speichern
+  save_object(SAVEFILE);
+  // Haus als Inmventar des alten Bauplatzes austragen
+  von->RemoveItem(object_name(h));
+  // Statistik ueber alle Seherhaeuser erneuern
+  dump();
+
+  return 1;
+}
+
+// Kann in ob ein Haus gebaut werden? 0: Ja, sonst Fehler!
+int Unbebaubar(object ob)
+{
+  // Raum ist geclont oder hat kein eigenes Filet, z.B. VC
+  if (clonep(ob) || file_size(object_name(ob)+".c")<0)
+    return 1;
+
+  // Innenraum
+  if (ob->QueryProp(P_INDOORS))
+    return 2;
+
+  // Bauplatz-Property nicht gesetzt
+  if (!(ob->QueryProp(P_HAUS_ERLAUBT)))
+    return 3;
+
+  return 0;
+}
+
+// Jemandem im Haus Zusatzrechte einraeumen
+string *Erlaube(string owner, string *wer)
+{
+  string *all;
+
+  // es wird kein passendes Seherhaus verwaltet
+  if (!member(megamap, owner))
+    return 0;
+
+  all = megamap[owner, HP_ALLOWED];
+  // fuege wer zu all dazu ohne doppelte Eintraege zu erzeugen:
+  all += wer-all;
+  // aender Rechteliste in der Verwaltung
+  megamap[owner, HP_ALLOWED] = all;
+  // speicher Verwalter-Mapping
+  save_object(SAVEFILE);
+  // return Liste der aktuellen Rechteinhaber
+  return all;
+}
+
+// Jemandem im Haus Zusatzrechte entziehen
+string *Verbiete(string owner, string *wer)
+{
+  // es wird kein passendes Seherhaus verwaltet
+  if (!member(megamap, owner))
+    return 0;
+
+  // aender Rechteliste in der Verwaltung
+  megamap[owner, HP_ALLOWED] -= wer;
+  // speicher Verwalter-Mapping
+  save_object(SAVEFILE);
+  // return Liste der aktuellen Rechteinhaber
+  return megamap[owner, HP_ALLOWED];
+}
+
+// Abfrage von Property 'prop' im Haus von 'owner'
+// prop gleich HP_ENV, HP_ROOMS oder HP_ALLOWED aus haus.h
+mixed HausProp(string owner, int prop)
+{
+  // es wird kein passendes Seherhaus verwaltet
+  if (!member(megamap, owner))
+    return 0;
+
+  return megamap[owner,prop];
+}
+
+// zerlegt das mapping, fasst indices mit gleichen values zusammen
+// und packt das ganze in ein array -> Deduplizieren
+// Das Ergebnis enthaelt
+// a) fuer jedes Detail ein Array, welches als erstes Element ein Array von
+// Schluesseln und als zweites Element einen String enthaelt.
+// b) fuer jedes Kommando ein Array, welches als erstes Element ein Array von
+// Syntaxen und zwei folgende Elemente mit Strings (Meldungen) enthaelt.
+// Beides ist in einem Format, dass die Elemente unmodifiziert an die
+// entsprechenden Add...()-Aufrufe gegeben werden koennen.
+
+// ([ "key1" : A, "key2" : B, "key3" : A ])
+// => ({ ({ ({ "key1", "key3" }), A )}, ({ ({ "key2" }), B )} })
+
+// ([ "key1" : ([ "key11" : A; B, "key12" : C; B, "key13" : A, B ]),
+//    "key2" : ([ "key21" : A; B, "key22" : C; A ]) ])
+// => ({ ({ ({ "key1 key11", "key1 key13", "key2 key21" }), A, B }),
+//       ({ ({ "key1 key12" }), C, B }),
+//       ({ ({ "key2 key22" }), C, A }) })
+mixed PCrunch(mapping prop)
+{
+  mixed ret = ({});
+  int done = 0;
+  foreach(string key, mixed entry : prop)
+  {
+    if(mappingp(entry))
+    {
+      // mapping breite 2 im mapping fuer H_COMMANDS
+      foreach(string subkey, string val1, string val2 : entry)
+      {
+        done = 0;
+        foreach(mixed retset : ret)
+        {
+          // falls es schon im ergebnis-array etwas mit den selben werten gibt,
+          // fuege den index dort dazu
+          if(sizeof(retset) == 3 && retset[1] == val1 && retset[2] == val2)
+          {
+            retset[0] += ({ key+" "+subkey });
+            done = 1;
+            break;
+          }
+        }
+        // falls es noch nix im ergebnis-array mit den selben werten gab,
+        // fuege einen neuen eintrag hinzu
+        if(!done)
+          ret += ({ ({ ({ key+" "+subkey }), val1, val2 }) });
+      }
+    }
+    else
+    {
+      // einzelne Werte im Mapping fuer P_DETAILS und P_READ_DETAILS
+      done = 0;
+      foreach(mixed retset : ret)
+      {
+        // falls es schon im ergebnis-array etwas mit dem selben wert gibt,
+        // fuege den index dort dazu
+        if(sizeof(retset) == 2 && retset[1] == entry)
+        {
+          retset[0] += ({ key });
+          done = 1;
+          break;
+        }
+      }
+      // falls es noch nix im ergebnis-array mit dem selben wert gab,
+      // fuege einen neuen eintrag hinzu
+      if(!done)
+        ret += ({ ({ ({ key }), entry }) });
+    }
+  }
+  return ret;
+}
diff --git a/d/seher/haeuser/kommentar.c b/d/seher/haeuser/kommentar.c
new file mode 100644
index 0000000..fe79fa4
--- /dev/null
+++ b/d/seher/haeuser/kommentar.c
@@ -0,0 +1,62 @@
+/*
+ * kommentar.c -- DIE Ergaenzung zum Bausparvertrag
+ *
+ * (c) 1994 Beldin@MorgenGrauen
+ *
+ * $Date: 1994/10/13 14:14:06 $
+ * $Revision: 1.2 $
+ * $Log: kommentar.c,v $
+ * Revision 1.2  1994/10/13  14:14:06  Wargon
+ * Ins Seherverzeichnis verlegt, angepasst und "reindented".
+ *
+ * Revision 1.1  1994/10/13  14:10:02  Wargon
+ * Initial revision
+ *
+ */
+
+inherit "std/thing";
+
+#include <properties.h>
+#include <language.h>
+#include "haus.h"
+
+create()
+{
+  ::create();
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Kommentar");
+  SetProp(P_WEIGHT, 500);
+  SetProp(P_VALUE, 0);
+  SetProp(P_NAME,"Kommentar");
+  SetProp(P_LONG,
+     "\nDies ist ein Kommentar. Er besteht aus 100%-recyclefaehigem Altpapier, und\n"
+    +"ist chlorfrei gebleicht. Das Papier scheint duenn zu sein.\n"
+    +"Vielleicht solltest Du ihn schnell lesen!\n\n"
+     );
+
+  AddId( ({"kommentar", "kommentierung"}));
+  AddCmd("lies","lesen");
+}
+
+rem()
+{
+  call_out("remove", 2);
+  tell_object(environment(), "\n\nDer Kommentar loest sich in Wohlgefallen auf.\n\n");
+  return 1;
+}
+
+lesen(str)
+{
+  string komment;
+  komment=PATH+"txt/kommentar.txt";
+  if (!str) return 0;
+  if (id(str)) {
+    this_player()->More(komment,1,#'rem);
+    return 1;
+    }
+  else {
+    notify_fail("Was willst Du lesen???\n");
+    return 0;
+  }
+}
+
diff --git a/d/seher/haeuser/modules/haustuer.c b/d/seher/haeuser/modules/haustuer.c
new file mode 100644
index 0000000..b9d8784
--- /dev/null
+++ b/d/seher/haeuser/modules/haustuer.c
@@ -0,0 +1,215 @@
+//  haustuer.c -- Beschreibung, Oeffnen und Schliessen
+//
+//  (c) 1995 Wargon@MorgenGrauen
+//
+// $Id: haustuer.c,v 1.1.1.1 2000/08/20 20:22:42 mud Exp $
+//
+
+#define NEED_PROTOTYPES
+#include "../haus.h"
+#include <properties.h>
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <thing/commands.h>
+
+static int validHouse = 0;
+static string owner;
+
+void create()
+{
+  Set( H_DOOR, "Die Haustuer ist %s.\n" );
+  Set( H_DOORSTAT, 0);
+  Set( H_DOORLSTAT, ({ "geoeffnet", "geschlossen", 0, "abgeschlossen" }));
+
+  Set( H_DOOR, SAVE, F_MODE);
+  Set( H_DOORSTAT, SAVE, F_MODE);
+  Set( H_DOORLSTAT, SAVE, F_MODE);
+
+  AddCmd( ({"schliess", "schliesse"}), "schliesse");
+  AddCmd("oeffne","oeffne");
+
+  owner = getuid(this_object());
+}
+
+varargs string long(int mode)
+{
+  return sprintf(Query(H_DOOR), Query(H_DOORLSTAT)[Query(H_DOORSTAT)]);
+}
+
+static string SetTuerOwner(string o)
+{
+  validHouse=1;
+  Set( H_DOORSTAT, D_CLOSED | D_LOCKED);
+  return owner=o;
+}
+
+int oeffne(string str)
+{
+  int doorstat;
+
+  notify_fail("Was willst Du oeffnen?\n");
+  doorstat = Query(H_DOORSTAT);
+
+  if (!id(str) && str != "tuer" && str != "haustuer")
+    return 0;
+
+  if (!(doorstat & D_CLOSED)) {
+    notify_fail(capitalize(name(WER,1))+" ist gar nicht geschlossen!\n");
+    return 0;
+  }
+  if (doorstat & D_LOCKED) {
+    notify_fail(capitalize(name(WER,1))+" ist abgeschlossen.\n");
+    return 0;
+  }
+
+  doorstat &= ~D_CLOSED;
+  Set(H_DOORSTAT, doorstat & ~D_CLOSED);
+
+  write("Du oeffnest "+name(WEN,1)+" von "+capitalize(owner)+".\n");
+  tell_room(environment(this_object()),
+	    sprintf("%s oeffnet %s von %s.\n",
+		    this_player()->name(WER),
+		    name(WEN,1),
+		    capitalize(owner) ),
+	    ({this_player()}));
+  if (validHouse) {
+    tell_room(find_object(RAUMNAME(owner,0))||this_object(),
+	      this_player()->name(WER)+" oeffnet "+name(WEN)+".\n",
+	      ({this_player()}));
+    Save();
+  }
+  return 1;
+}
+
+private int close_door()
+{
+  if (Query(H_DOORSTAT) & D_CLOSED) {
+    notify_fail(capitalize(name(WER,1))+" ist doch schon zu!\n");
+    return 0;
+  }
+  Set(H_DOORSTAT, Query(H_DOORSTAT) | D_CLOSED);
+
+  write("Du schliesst "+name(WEN,1)+" von "+capitalize(owner)+".\n");
+  tell_room(environment(this_object()),
+	    sprintf( "%s schliesst %s von %s.\n",
+		     capitalize(this_player()->name(WER)),
+		     name(WEN,1), capitalize(owner)),
+	    ({this_player()}));
+  if (validHouse) {
+    tell_room(find_object(RAUMNAME(owner,0))||this_object(),
+	      capitalize(this_player()->name(WER))+" schliesst "+name(WEN)+".\n",
+	      ({this_player()}));
+    Save();
+  }
+  return 1;
+}
+
+private int lock_door()
+{
+  object tp, home;
+  string nam;
+  int doorstat;
+
+  tp = this_player();
+  nam = capitalize(tp->name(WER));
+
+  if (validHouse) {
+    call_other(RAUMNAME(owner,0), "???");
+    home = find_object(RAUMNAME(owner,0));
+
+    if (!home->allowed_check(this_player())) {
+      notify_fail("Du darfst "+name(WEN,1)+" von "+capitalize(owner)+" aber nicht abschliessen.\n");
+      return 0;
+    }
+  }
+  if ((doorstat = Query(H_DOORSTAT)) & D_LOCKED) {
+    notify_fail("Die Tuer ist abgeschlossen.\n");
+    return 0;
+  }
+  doorstat |= (D_LOCKED|D_CLOSED);
+  write("Du schliesst "+name(WEN)+" ab.\n");
+  Set(H_DOORSTAT, doorstat);
+  tell_room(environment(this_object()),
+	    sprintf( "%s schliesst %s ab.\n",
+		    nam,
+		    (getuid(tp)==owner ?
+		     (tp->QueryPossPronoun(this_object(), WEN)+" "+name(RAW)) :
+		     name(WEN,1) )),
+	    ({tp}));
+  if (validHouse) {
+    tell_room(home, nam+" schliesst "+name(WEN)+" ab.\n",({tp}));
+    Save();
+  }
+  return 1;
+}
+
+private int unlock_door()
+{
+  object tp, home;
+  string nam;
+  int doorstat;
+
+  tp = this_player();
+  nam = capitalize(tp->name(WER));
+
+  if (validHouse) {
+    call_other(RAUMNAME(owner,0), "???");
+    home = find_object(RAUMNAME(owner,0));
+
+    if (!home->allowed_check(this_player())) {
+      notify_fail("Du darfst "+name(WEN,1)+" von "+capitalize(owner)+" aber nicht aufschliessen.\n");
+      return 0;
+    }
+  }
+
+  if (!((doorstat = Query(H_DOORSTAT)) & D_LOCKED)) {
+    notify_fail("Die Tuer ist nicht abgeschlossen.\n");
+    return 0;
+  }
+
+  Set(H_DOORSTAT, doorstat & ~D_LOCKED);
+  write("Du schliesst "+name(WEN)+" auf.\n");
+  tell_room(environment(this_object()),
+	    sprintf("%s schliesst %s auf.\n",
+		     nam,
+		     (getuid(tp)==owner ?
+		      (tp->QueryPossPronoun(this_object(), WEN)+" "+name(RAW)) :
+		      name(WEN,1) )),
+	    ({tp}));
+  if(validHouse) {
+    tell_room(home,nam+" schliesst "+name(WEN)+" auf.\n",({tp}));
+    Save();
+  }
+  return 1;
+}
+
+int schliesse(string str)
+{
+  string was, wen, *s;
+
+  notify_fail("Was willst Du schliessen ?\n");
+  if (!str||str=="")
+    return 0;
+  s = old_explode(str, " ");
+  if ((was = s[<1]) == "auf" || was == "ab")
+    s = s[0..<2];
+  else
+    was = 0;
+
+  wen=implode(s, " ");
+  if (!id(wen) && wen != "tuer" && wen != "haustuer")
+    return 0;
+  if (!was || was=="")
+    return close_door();
+  switch (was)
+  {
+    case "auf": return unlock_door();
+    case "ab":  return lock_door();
+  }
+  return 0;
+}
+
+// $Log: haustuer.c,v $
+// Revision 1.1.1.1  2000/08/20 20:22:42  mud
+// Ins CVS eingecheckt
+//
diff --git a/d/seher/haeuser/modules/losa.c b/d/seher/haeuser/modules/losa.c
new file mode 100644
index 0000000..893ca51
--- /dev/null
+++ b/d/seher/haeuser/modules/losa.c
@@ -0,0 +1,378 @@
+//  losa.c -- Modul fuer Laden und Speichern der Hausdaten
+//
+//  (c) 1995 Wargon@MorgenGrauen
+//      2006 Vanion@MorgenGrauen, fuer die rebootfesten Moebel
+// $Id: losa.c,v 1.1.1.1 2000/08/20 20:22:42 mud Exp $
+//
+#pragma strong_types,rtt_checks
+
+#define NEED_PROTOTYPES
+#include "../haus.h"
+#include <container.h>
+#include <thing/properties.h>
+#include <room/exits.h>
+#include <thing/description.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <wizlevels.h>
+#include <moving.h>
+
+static void start_reload_furniture();
+
+private int csaved;
+
+// Variablen zur Verwaltung der Moebel im Raum.
+mapping furniture;
+mapping broken_furniture;
+
+protected void
+create()
+{
+  Set(H_CHEST, SAVE, F_MODE);
+  Set(H_CHEST, 0);
+
+  Set(H_FURNITURE, SAVE, F_MODE);
+  Set(H_FURNITURE, 0);
+
+//  Set(H_SPECIAL, SAVE|SECURED, F_MODE);
+//  Set(H_SPECIAL, ([:2]), F_VALUE);
+}
+
+/* Scheint nirgendwo benutzt zu werden...
+varargs int AddSpecial(int typ, string key, string extra)
+{
+  if (!this_interactive() || !IS_ARCH(this_interactive()) ||
+      !(PATH+"access_rights")->access_rights(geteuid(this_interactive()),""))
+    return -1;
+
+  if (typ != HS_EXIT && typ != HS_ITEM)
+    return 0;
+
+  Set(H_SPECIAL, Query(H_SPECIAL) + ([ key : typ; extra ]));
+  Save();
+  return 1;
+}
+
+void RemoveSpecial(string key)
+{
+  if (!this_interactive() || !IS_ARCH(this_interactive()) ||
+      !(PATH+"access_rights")->access_rights(geteuid(this_interactive()),""))
+    return;
+
+  Set(H_SPECIAL, m_delete(Query(H_SPECIAL), key));
+  Save();
+}
+*/
+
+void
+reset()
+{
+  if (QueryOwner() &&
+      !sizeof(filter(all_inventory(),#'interactive)) &&
+      !csaved)
+    Save(1);
+}
+
+// crunched komprimiert das Savefile
+varargs void
+Save(int crunched)
+{
+  mixed o1, o2, o3;
+  closure pc;
+  object *obs;
+  int i, found;
+
+  o3 = 0;
+
+  if (!(o1 = Query(P_DETAILS)))
+    Set(P_DETAILS, QueryProp(P_DETAILS), F_VALUE);
+
+  if (!(o2 = Query(P_READ_DETAILS)))
+    Set(P_READ_DETAILS, QueryProp(P_READ_DETAILS), F_VALUE);
+
+  if (csaved = crunched)
+  {
+    pc = symbol_function("PCrunch", VERWALTER);
+    Set(P_DETAILS, funcall(pc, Query(P_DETAILS)), F_VALUE);
+    Set(P_READ_DETAILS, funcall(pc, Query(P_READ_DETAILS)), F_VALUE);
+    o3 = Query(H_COMMANDS, F_VALUE);
+    Set(H_COMMANDS, funcall(pc, o3), F_VALUE);
+  }
+
+  // Autoload-Einrichtung identifizieren und speichern
+  // Code in Anlehnung an dem Autoload-Mechanismus fuer Spieler
+  furniture=([]);
+
+  // Alle Autoloader filtern
+  obs=filter_objects(all_inventory(this_object()), "QueryProp", H_FURNITURE);
+  found = 0;
+
+  // Ueber alle Moebel iteritieren
+  for( i=sizeof(obs)-1;i>=0;i--)
+  {
+    if( clonep(obs[i]))
+    {
+      if ( ++found <= MAX_FURNITURE_PER_ROOM )
+        furniture += ([ object_name(obs[i]):obs[i]->QueryProp(H_FURNITURE) ]);
+    }
+  }
+  if (found > MAX_FURNITURE_PER_ROOM)
+  {
+    tell_object(this_player(), 
+      break_string("Du hast "+found+" Moebelstuecke im Raum stehen. "
+                   "Gespeichert werden nur "+MAX_FURNITURE_PER_ROOM+". "
+                   "Du solltest Dich von einigen Einrichtungsgegenstaenden "
+                   "trennen.",78));
+
+  }
+  HDEBUG("Saving "+ sizeof (furniture) +" (plus "+sizeof(broken_furniture)+
+         " broken) objects in room "+
+         object_name(this_object()) + ".");
+
+  save_object( HAUSSAVEPATH+QueryOwner(1));
+
+  Set(P_DETAILS, o1, F_VALUE);
+  Set(P_READ_DETAILS, o2, F_VALUE);
+  if (o3)
+    Set(H_COMMANDS, o3, F_VALUE);
+}
+
+void
+Load()
+{
+  mixed prop;
+  int i;
+
+  restore_object( HAUSSAVEPATH+QueryOwner(1));
+
+  // Details und Kommandos werden beim Speichern de-dupliziert und in einem
+  // speziellen Format abgespeichert (s. PCrunch() im Hausverwalter). Sie
+  // muessen nach dem Laden durch die entsprechenden Add...()-Aufrufe
+  // wieder eingetragen werden.
+  prop=Query(P_DETAILS, F_VALUE);
+  RemoveDetail(0);
+  if (pointerp(prop))
+  {
+    foreach(<string*|string>* item : prop)
+      AddDetail(item[0], item[1]);
+  }
+  else if (mappingp(prop))
+  {
+    foreach(string key, mixed val : prop)
+      AddDetail(key, val);
+  }
+  else
+    SetProp(P_DETAILS, prop);
+
+  prop = Query(P_READ_DETAILS, F_VALUE);
+  RemoveReadDetail(0);
+  if (pointerp(prop))
+  {
+    foreach(<string*|string>* item : prop)
+      AddDetail(item[0], item[1]);
+  }
+  else if (mappingp(prop))
+  {
+    foreach(string key, mixed val : prop)
+      AddReadDetail(key, val);
+  }
+  else
+    SetProp(P_READ_DETAILS, prop);
+
+  prop = Query(P_EXITS, F_VALUE);
+  RemoveExitNoCheck(0);
+  if (mappingp(prop))
+  {
+    if (widthof(prop) <= 1)
+    {
+      foreach(string key, string dest : prop)
+        AddExitNoCheck(key, dest);
+    }
+    else
+    {
+      foreach(string key, string dest, string msg : prop)
+      {
+        if (stringp(msg))
+          _AddExit(key, dest, msg);
+        else if (stringp(dest) && strstr(dest,"#") != -1)
+          AddExitNoCheck(key, dest);
+        else
+          _AddExit(key, dest, 0);
+      }
+    }
+  }
+
+  prop=Query(H_COMMANDS, F_VALUE);
+  if (pointerp(prop))
+  {
+    Set(H_COMMANDS, ([]), F_VALUE);
+    for (i=sizeof(prop)-1; i>=0; i--)
+      this_object()->AddUserCmd(prop[i][0], 0, prop[i][1], prop[i][2]);
+  }
+
+  if (environment())
+    environment()->SetProp(P_NEVER_CLEAN, 1);
+
+  if (previous_object() && object_name(previous_object())==VERWALTER)
+  {
+    if (Query(H_CHEST))
+      this_object()->AddItem(PATH+"truhe",REFRESH_NONE,
+                             ([ "owner" : QueryOwner() ]));
+/* Das scheint nirgendwo benutzt zu werden und in allen Seherhaeusern leer zu
+ * sein.
+    mapping special = Query(H_SPECIAL, F_VALUE);
+    if (special)
+    {
+      foreach(string key, int type, string extra : special)
+      {
+        switch(type)
+        {
+          case HS_ITEM:
+            AddItem(SPECIALPATH + extra, REFRESH_DESTRUCT);
+            break;
+          case HS_EXIT:
+            AddExitNoCheck(key, extra);
+            break;
+        }
+      }
+    }
+*/
+  }
+
+  // Das Laden der Autoloader wird erst am Ende angestossen. 
+  // Dann ist es nicht schlimm, wenn alle Eval Ticks verbraucht werden.
+  start_reload_furniture();
+}
+
+// Mehrere Save-Anforderungen zusammenfassen.
+static void queued_save()
+{
+        HDEBUG("QS");
+        while (remove_call_out("Save")!=-1);
+        call_out("Save",3);
+}
+
+static int reload_error(string file, mixed data,  string message)
+{
+        HDEBUG(message);
+        broken_furniture+=([file:data]);
+        log_file("seher/haeuser/autoloader_error",
+                 dtime(time())+"\n"+
+                 break_string(object_name(this_object())+" ("+QueryOwner(1)+")",78, " FILE: ",1)+
+                 break_string(message, 78, " MSG:  ",1)+
+                 break_string(sprintf("%O", data),78, " DATA: ",1)+"\n");
+                 
+        return 0; // 0 fuer das filter, damit dieser Eintrag 
+                  // aus furniture geloescht wird.
+}
+
+// Laedt ein einzelnes Moebelstuecks
+static int load_furniture_object( string file, mixed data )
+{
+    object ob;
+    string error;
+    string blueprint;
+    closure pc;
+    // mixed data;
+    
+    // Wenn genug Ticks frei sind, wird versucht, das Objekt zu erzeugen.
+    // Ansonsten ist die Gefahr zu gross, dass ein Erzeugungs-Prozess abbricht.
+    if (get_eval_cost() < 500000) 
+    {
+      // HDEBUG("Suspending Object: "+file+". Only "+to_string(get_eval_cost())+" ticks left.");
+      return 1; // 1 bedeutet, dass dieser Eintrag es im Mapping bleibt.
+    }
+
+    // HDEBUG("Processing Object: "+file+" with Data: "+sprintf("%O",data)+".");
+    
+    // Nummern der Clones sind beim Speichern noetig, um die Identitaeten
+    // der Objekte zu bestimmen (mehrere Objekte vom gleichen Blueprint 
+    // speichern). Hier braucht man sie nicht mehr 
+            
+    blueprint = explode(file,"#")[0];
+    
+    // Data aus dem Mapping holen
+    // data=furniture[file];
+    
+    // Muss ich die Blueprint suchen?
+    ob = find_object(file);
+    
+    // Nein.
+    if (!ob)
+    {
+            // Existiert die BP oder ein VC fuers File?
+        if (file_size(blueprint+".c")<0&&
+           file_size(implode(explode(blueprint,"/")[0..<2],"/")+
+                     "/virtual_compiler.c")<0)
+        {
+           return reload_error(file, data, "Error in file: "+ file +
+                               ". File does not exist.");
+
+        }
+
+        // File gefunden. Versuch, es zu laden.
+        if (error = catch(call_other( blueprint,"???")))
+        {
+           return reload_error(file, data, "Error loading file: "+file+". "+error);
+        }
+    }
+    
+    // Clone erzeugen
+    if ( error = catch(ob = clone_object(blueprint)) )
+    {
+        return reload_error(file, data, "Error cloning object: "+file+". "+error);
+    }
+ 
+    HDEBUG(sprintf("%O",furniture));
+    // Autoload-Daten setzen
+    HDEBUG(object_name(ob)+"->SetProp("+sprintf("%O", data)+")");
+    if (ob)
+        catch(ob->SetProp( H_FURNITURE, data ));
+           
+    // Furniture in das Seherhaus moven
+    if ( error = catch(ob->move( this_object(), M_NOCHECK )) ) {
+        ob->remove();
+        if(ob) destruct(ob);
+        return reload_error(file, data, "Error moving object: "+file+". "+error);
+    }
+
+    // post_create anstossen
+    pc=symbol_function("post_create", ob);
+    if (closurep(pc))
+      call_out(pc, 1);
+    
+    return 0; // 0 bedeutet hier, dieses Objekt nicht noch einmal anstossen.
+}
+
+static void load_furniture()
+{
+        int i;
+        string rv;
+        string current_key;
+        
+        // Abbruchbedingung ist, dass nichts mehr zu laden ist.
+        if (sizeof(furniture)==0) return; 
+        
+        // Anstoßen des naechsten Durchlaufs, falls die Ticks nicht reichen.
+        while (remove_call_out(#'load_furniture) != -1);
+        call_out(#'load_furniture, 1);
+        
+        // Laden aller Moebel anstoßen
+    furniture=filter(furniture, #'load_furniture_object);
+}
+
+// Diese Funktion bereitet das Reloaden der Einrichtung vor
+static void start_reload_furniture()
+{  
+  // Wenn es keine Moebel gibt, ist das Laden beendet.
+  if (!mappingp(furniture)) return;
+  if (broken_furniture==0) broken_furniture=([]);
+  
+  // Falls ein Key von furniture 0 ist, wird dieser geloescht. 
+  m_delete(furniture,0);
+  
+  // Laden des Furniture anstossen
+  load_furniture();  
+}
+
+
diff --git a/d/seher/haeuser/modules/usercmd.c b/d/seher/haeuser/modules/usercmd.c
new file mode 100644
index 0000000..a83b819
--- /dev/null
+++ b/d/seher/haeuser/modules/usercmd.c
@@ -0,0 +1,341 @@
+//  usercmd.c -- Modul fuer benutzerdefinierte Befehle
+//
+//  (c) 1995 Wargon@MorgenGrauen
+//
+// $Id: usercmd.c,v 1.4 2003/11/15 13:48:46 mud Exp $
+//
+
+#define NEED_PROTOTYPES
+#include "../haus.h"
+#include <properties.h>
+#include <wizlevels.h>
+#include <thing/properties.h>
+
+private string ucFilter(string str);
+private string ucText(string str);
+private void splitCmd(string *cmd, string *verb, string *para);
+
+create()
+{
+  Set(H_COMMANDS, SAVE, F_MODE);
+  Set(H_COMMANDS, ([]));
+
+  AddCmd("", "userCommands", 1);
+}
+
+/*
+ * AddUserCmd: eigenes Kommando hinzufuegen
+ *  cmd: String oder Array von Strings. Enthaelt entweder nur die Verben oder
+ *         die kompletten Befehle (Verb + Parameter). Siehe auch pa.
+ *  pa:  Zahl oder Array von Strings. Falls pa eine Zahl ist, so enthaelt cmd
+ *         die kompletten Befehle (samt Parametern). Ansonsten enthaelt pa die
+ *         Parameter fuer das entsprechende Verb in cmd.
+ *         Ist pa = ({ "@NF@"}) , so handelt es sich um ein notify_fail, das an
+ *         die Beschreibung des Verbs (falls vorhanden) angehaengt wird.
+ *  me:  Text fuer den Ausfuehrenden. Der Text muss schon geparsed worden
+ *         sein!
+ *  oth: Text fuer die Umstehenden oder 0. Der Text muss schon geparsed wor-
+ *         den sein!
+ */
+varargs void
+AddUserCmd(mixed cmd, mixed pa, string me, string oth)
+{
+  int v,p;
+  mapping cmds, desc;
+  string *verb, *para, txt;
+
+  cmds = Query(H_COMMANDS);
+  if (stringp(cmd))
+    verb = ({ cmd });
+  else
+    verb = cmd;
+
+  if (intp(pa))
+    splitCmd(verb[0..], &verb, &para);
+  else {
+    if (stringp(pa))
+      para = ({ pa });
+    else if (pointerp(pa))
+      para = pa;
+    for (desc = ([]), p=sizeof(para)-1; p>=0; p--)
+      desc += ([ para[p] : me; oth ]);
+  }
+
+  for (v = sizeof(verb)-1; v>= 0; v--) {
+    if (member(cmds, verb[v])) {
+      if (intp(pa))
+        cmds[verb[v]] += ([para[v] : me; oth ]);
+      else
+        cmds[verb[v]] += desc;
+    }
+    else {
+      if (intp(pa))
+        cmds += ([ verb[v] : ([para[v] : me; oth ]) ]);
+      else
+        cmds += ([ verb[v] : desc ]);
+    }
+  }
+  Set(H_COMMANDS, cmds);
+}
+
+/*
+ * RemUserCmd: Kommando(s) wieder entfernen
+ *  com: String oder Array von Strings. Enthaelt das zu loeschende Kommando
+ *         (Verb oder Verb + Parameter).
+ *  all: Falls != 0, so werden die Verben aus com komplett mit allen Para-
+ *         metern geloescht.
+ */
+varargs void
+RemUserCmd(mixed com, int all)
+{
+  mapping cmd, tmp;
+  string *verb, *para;
+  int v, p;
+
+  cmd = Query(H_COMMANDS);
+  splitCmd(stringp(com) ? ({com}) : com, &verb, &para);
+
+  if (all)
+    for (v=sizeof(verb)-1; v>=0; v--)
+      cmd = m_copy_delete(cmd, verb[v]);
+  else {
+    for (v=sizeof(verb)-1; v>=0; v--) {
+      if (tmp = cmd[verb[v]]) {
+        for (p=sizeof(para)-1; p>=0; p--)
+          tmp = m_copy_delete(tmp, para[p]);
+        cmd[verb[v]] = tmp;
+      }
+    }
+  }
+  Set(H_COMMANDS,cmd);
+}
+
+/*
+ * userCommands: Auswertung der benutzerdefinierten Befehle.
+ */
+static int
+userCommands(string str)
+{
+  mapping ucmd, uparm;
+  string *parts, text;
+  int i;
+
+  ucmd = QueryProp(H_COMMANDS);
+  str = this_player()->_unparsed_args(1)||"";
+  if (uparm = ucmd[query_verb()]) {
+    if (member(uparm, str)) {
+      text = ucText(uparm[str,0]);
+      if (sizeof(old_explode(text, "\n")) >= this_player()->QueryProp(P_SCREENSIZE))
+        this_player()->More(capitalize(text)[0..<2]);
+      else
+        write(capitalize(text));
+
+      if (uparm[str,1])
+        say(ucText(uparm[str,1]));
+
+      if (member(m_indices(QueryProp(P_EXITS)), query_verb()) == -1)
+        return 1;
+    }
+    else {
+      if (str && str != "" && text = uparm["@NF@"])
+        notify_fail(implode(old_explode(text,"@F"),str));
+    }
+  }
+  return 0;
+}
+
+/*** Functions private to this module... ***/
+
+/*
+ * ucFilter: Ersetzen von Name, Personal- und Possessivpronomen.
+ *  Gibt den ersetzten String zurueck.
+ */
+private string
+ucFilter(string str)
+{
+  int p,g;
+
+  switch(str[0..1]) {
+    case "@W": // Name in entsprechendem Fall...
+      return this_player()->name(to_int(str[2..2]));
+    case "@P": // Personalpronomen in entprechendem Fall...
+      return this_player()->QueryPronoun(to_int(str[2..2]));
+    case "@B": // Possesivpronomen in entprechendem Fall...
+      p = to_int(str[4..4]);
+      g = to_int(str[3..3]);
+      return this_player()->QueryPossPronoun(g, to_int(str[2..2]), p);
+  }
+  return str;
+}
+
+/*
+ * ucText: Rassen- und geschlechtsspezifische Teile bearbeiten sowie
+ *  Namen und Pronomina einsetzen.
+ *
+ *  str enthaelt den String, der beim Beschreiben des Befehls
+ *  eingegeben wurde. Bis auf den Default-Text sind alle Teile
+ *  optional. Der String kann folgenden Aufbau haben:
+ *  ------ schnipp ------
+ *  Default-Text, der ausgegeben wird, wenn die folgenden spezielleren
+ *  Texte nicht auftreten oder auf den Ausfuehrenden nicht zutreffen
+ *  @NAME:nameA
+ *  Text, der ausgegeben wird, wenn der Spieler "nameA" das Kommando
+ *  eingegeben hat
+ *  @NAME:nameB
+ *  Und so weiter fuer Spieler "nameB" etc...
+ *  @RA
+ *  Text, der ausgegeben wird, wenn der Ausfuehrende auf der Erlaube-
+ *  Liste des Hauses steht.
+ *  @RE
+ *  Text, der ausgegeben wird, wenn der Ausfuehrende ein Elf
+ *  ist. Ebenso gibt es @RD fuer Dunkelelfen, @RF fuer Felinen,
+ *  @RH fuer Hobbits, @RM fuer Menschen, @RG fuer Goblins  und @RZ 
+ *  fuer Zwerge.
+ *  ------ schnapp ------
+ *
+ *  Der Default-Text muss immer am Anfang stehen. Die anderen Bloecke
+ *  koennen in beliebiger Reihenfolge folgen. Die Reihenfolge, in der
+ *  die Bloecke betrachtet werden, ist dabei folgende:
+ *  - zuerst werden @NAME-Bloecke untersucht
+ *  - dann wird @RA (Erlaube-Liste) getestet
+ *  - danach wird die Rasse ueberprueft (@RD/@RE/@RF/@RH/@RM/@RZ/@RG)
+ *  - zuletzt wird der Default-Text betrachtet
+ *
+ *  Innerhalb jedes der Bloecke kann man noch mit @G zwischen
+ *  maennlichen und weiblichen Vertretern unterscheiden (bei @NAME:
+ *  macht das aber wenig Sinn). Beispiel:
+ *  ------ schnipp ------
+ *  @RZ
+ *  Der Zwerg war maennlich
+ *  @G
+ *  Der Zwerg war weiblich
+ *  ------ schnapp ------
+ *
+ *  Die Funktion gibt den fuer den Ausfuehrenden zutreffenden Text
+ *  zurueck.
+ */
+private string
+ucText(string str)
+{
+  string *parts, *names, *lnames;
+  int i, n;
+
+  // Text nach Namen- und Rassentrennern aufteilen
+  parts = regexplode(str, "(@NAME:[A-Za-z1-9]*\n)|(@R[A-Z]\n)");
+  i = -1;
+
+  if (sizeof(parts) > 1) {
+
+    // Zuerst wird nach Namenstrennern gesucht
+    names = regexp(parts, "@NAME:");
+
+    if (sizeof(names) > 0) {
+      // ein kleiner Umweg, da der Name im Eingabestring nicht
+      // notwendigerweise in Kleinbuchstaben vorliegt.
+      lnames = map(names, #'lower_case);
+      n = member(lnames, "@name:"+getuid(this_player())+"\n");
+
+      if (n >= 0) {
+        i = member(parts, names[n]);
+      }
+    }
+
+    // Kein passender Namenstrenner gefunden: Erlaube-Liste
+    // ueberpruefen
+    if (i<0 && allowed_check(this_player())) {
+      i=member(parts, "@RA\n");
+    }
+
+    // Weder Namenstrenner noch Erlaube-Liste passen: Rasse
+    // ueberpruefen
+    if (i<0) {
+      switch(this_player()->QueryProp(P_RACE)) {
+        case "Zwerg":
+          i=member(parts, "@RZ\n");
+          break;
+        case "Elf":
+          i=member(parts, "@RE\n");
+          break;
+        case "Feline":
+          i=member(parts, "@RF\n");
+          break;
+        case "Hobbit":
+          i = member(parts, "@RH\n");
+          break;
+        case "Dunkelelf":
+          i = member(parts, "@RD\n");
+          break;
+        case "Goblin":
+          i = member(parts, "@RG\n");
+          break;
+        case "Ork": 
+          i = member(parts, "@RO\n");
+          break;
+        default:
+          i=member(parts, "@RM\n");
+          break;
+      }
+    }
+    
+    // Den richtigen Teil des Strings herauspicken
+    if (i>-1)
+      str = parts[i+1];
+    else
+      str = parts[0];
+  }
+  if (sizeof(parts = old_explode(str, "@G\n"))==2)
+    str = parts[(this_player()->QueryProp(P_GENDER) == MALE ? 0 : 1)];
+
+  parts = regexplode(str, "(@W[0-3]|@P[0-3]|@B[0-3][0-2][0-1])");
+  parts = map(parts, #'ucFilter);
+  return implode(parts, "");
+}
+
+/*
+ * splitCmd: Komplettes Kommando in Arrays von Verben und Parametern zerlegen.
+ *  cmd: Array von Strings. Dieses Array enthaelt die aufzuspaltenden Befehle.
+ *  verb: Referenz auf ein Array von Strings fuer die Verben.
+ *  para: Referenz auf ein Array von Strings fuer die Parameter.
+ */
+private void splitCmd(string *cmd, string *verb, string *para)
+{
+  int c, sp;
+
+  for (verb = ({}), para = ({}), c = sizeof(cmd)-1; c>=0; c--) {
+    if ((sp=member(cmd[c], ' ')) >= 0) {
+      verb += ({ cmd[c][0..sp-1] });
+      para += ({ cmd[c][sp+1..] });
+    }
+    else {
+      verb += ({ cmd[c] });
+      para += ({ "" });
+    }
+  }
+}
+
+// $Log: usercmd.c,v $
+// Revision 1.4  2003/11/15 13:48:46  mud
+// @RD als Trenner fuer Dunkelelfen
+//
+// Revision 1.3  2000/12/03 17:15:40  mud
+// ucText: Neuer Trenner @NAME:foo, mit dem man in Seherhausbefehlen
+// Texte fuer bestimmte Spieler vorsehen kann.
+//
+// Revision 1.2  2000/08/20 20:38:40  mud
+// @RF als Trenner fuer Felinen
+//
+// Revision 1.1.1.1  2000/08/20 20:22:42  mud
+// Ins CVS eingecheckt
+//
+// Revision 1.4  1996/04/19  23:10:52  Wargon
+// @RA fuer Leute mit Erlaubnis
+//
+// Revision 1.3  1995/10/31  12:59:52  Wargon
+// @RH fuer Hobbits
+//
+// Revision 1.2  1995/06/22  19:48:31  Wargon
+// Bugfix in userCommands()
+//
+// Revision 1.1  1995/04/21  09:22:50  Wargon
+// Initial revision
+//
diff --git a/d/seher/haeuser/moebel/access_rights.c b/d/seher/haeuser/moebel/access_rights.c
new file mode 100644
index 0000000..255cd58
--- /dev/null
+++ b/d/seher/haeuser/moebel/access_rights.c
@@ -0,0 +1,5 @@
+int access_rights(string user, string file) {
+  if(user == "swift" && file!="autoloadertruhe.c")
+    return 1;  // Swift hat auf die Moebel Zugriff.
+  return 0;
+}
diff --git a/d/seher/haeuser/moebel/autoloadertruhe.c b/d/seher/haeuser/moebel/autoloadertruhe.c
new file mode 100644
index 0000000..e22ce17
--- /dev/null
+++ b/d/seher/haeuser/moebel/autoloadertruhe.c
@@ -0,0 +1,1015 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Aufbewahrungtruhe fuer Autoload-Objekte
+//
+// Magier:              Zesstra
+//--------------------------------------------------------------------------------
+#pragma strong_types,rtt_checks
+
+#include "schrankladen.h"
+inherit LADEN("swift_std_container");
+
+//#include <ansi.h>
+#include <class.h>
+#include <wizlevels.h>
+#include "/d/seher/haeuser/haus.h"
+
+#define IDS     0
+#define NAME   1
+#define ALDATA  2
+
+#define LOG(x,y) log_file(x,sprintf("%s [%s, %O, %O]: %s\n",dtime(time()),\
+      (uuid?uuid:" "),PL,object_name(ME),y))
+#define STORELOG(x) LOG("zesstra/ALTRUHE_STORE.log",x)
+#define PICKLOG(x) LOG("zesstra/ALTRUHE_PICK.log",x)
+
+#define ITEMLOG(x) log_file("zesstra/ALTRUHE_ITEMS.log",\
+    sprintf("%s [%O]: %s\n",dtime(time()),\
+      this_interactive()||PL,x))
+
+#define ERRLOG(x) LOG("zesstra/ALTRUHE.ERR",x)
+
+#undef BS
+#define BS(x) break_string(x,78)
+
+#define VERSION_OBJ "5"
+
+#ifdef MAINTAINER
+#undef MAINTAINER
+#endif
+#define MAINTAINER ({"zesstra"})
+
+// Savefile der Blueprint
+#ifdef SAVEFILE
+#undef SAVEFILE
+#endif
+#define SAVEFILE __FILE__[..<3]
+
+#define DEBUG(x)    if (funcall(symbol_function('find_player),MAINTAINER[0]))\
+          tell_object(funcall(symbol_function('find_player),MAINTAINER[0]),\
+                      "ALTruhe: "+x+"\n")
+
+#define ACCESS (my_secure_level() >= ARCH_LVL)
+
+// Diese 4 sind fuer die Blueprint (Master aller Truhen)
+mapping whitelist=([]); // erlaubte Autoloader, alle anderen nicht erlaubt.
+mapping blacklist=([]); // bereits explizit durch EM+ abgelehnte Autoloader
+mapping vorschlaege=m_allocate(1,2); // vorschlaege der Spieler
+mapping data=([]);  // hier speichert die BP alle Daten der Truhen
+                    // WICHTIG: dieses Mapping wird in /secure/memory
+                    // abgelegt und muss auch beim Neuladen ggf. von dort
+                    // wieder abgeholt werden. Ausserdem teilen sich alle
+                    // Truhen eines Spielers und diese Blueprint die Mappings
+                    // darin, damit Aenderungen sofort in allen Truhen und dem
+                    // Master bekannt sind.
+
+/* die einzelnen Truhen speichern in autoloader. Format: 
+   3 Werte pro Key, Keys sind die Namen der Blueprints der Objekte:
+   ([<blueprint>: <P_IDS>; <ob->name()>; <P_AUTOLOADOBJ> ])
+   */
+nosave mapping autoloader=m_allocate(1,3);
+nosave string uuid; // UUID des Eigentuemers
+nosave object ob_in_bewegung; // uebler Hack eigentlich. :-(
+
+void NotifyInsert(object ob, object oldenv);
+static mapping QueryData();
+static mapping SetData(mixed data);
+protected void save_me();
+protected void check_content();
+
+nomask private int my_secure_level(); //Args.
+
+protected void create() {
+
+  // Ja, es is Absicht, dass das create der BP erst spaeter abgebrochen wird!
+  swift_std_container::create();
+
+  seteuid(getuid(ME));
+
+  // falls dies die BP ist:
+  // 1. das Savefile zu laden.
+  // 2. versuchen, die truhendaten von /secure/memory zu holen, damit nach dem
+  //    Neuladen der Master und die Client immer nach _dieselben_ Mappings
+  //    haben.
+  if (!clonep(ME))
+  {
+    // Savefile restaurieren (auch wenn data im Memory ist, muss das sein,
+    // damit die anderen Variablen wieder eingelesen sind).
+    restore_object(SAVEFILE);
+    // jetzt Daten aus Memory holen, sofern verfuegbar
+    mapping tmp = "/secure/memory"->Load("truhendaten");
+    if (mappingp(tmp))
+    {
+      // Daten aus Savefile durch die vom Memory ersetzen
+      data = tmp;
+    }
+    else
+    {
+      // Keine Daten in Memory. Jetzt in jedem Fall den Pointer auf das Mapping data in
+      // /secure/memory ablegen
+      if ("/secure/memory"->Save("truhendaten",data) != 1)
+      {
+        raise_error("Could not save memory to /secure/memory.");
+      }
+    }
+  }
+  else
+  {
+    // brauchen Clones nicht.
+    set_next_reset(-1);
+    data=0; // brauchen Clones nicht.
+  }
+
+  SetProp(P_SHORT, "Eine kleine, magische Holztruhe");
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_NAME, "Holztruhe");
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_NAME_ADJ,({"klein", "magisch"}));
+  SetProp(P_LONG, BS(
+     "Die kleine Holztruhe ist aus stabilem Eichenholz gefertigt. Eigentlich "
+     "saehe sie recht unscheinbar aus, waeren da nicht die vielen kunstvollen "
+     "Runen an den Seiten und auf dem Deckel.")
+    +"@@cnt_status@@");
+
+  AddId(({"autoloadertruhe", "holztruhe", "truhe"}));
+
+  // den Rest vom Create braucht die BP nicht.
+  if (!clonep(ME)) return;
+
+  SetProp(P_LOG_FILE,"zesstra/ALTRUHE.rep");
+
+  SetProp(P_WEIGHT, 3000);         // Gewicht 5 Kg
+  // die drei hier sind in diesme Fall eigentlich voellig ohne Bedeutung
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);
+  SetProp(P_MAX_OBJECTS, 100);     // sind eh immer 0 echte Objekte drin.
+
+  SetProp(P_VALUE, 0);             // Kein materieller Wert. Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Wird im Laden zerstoert, falls er verkauft wird.
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
+  SetProp(P_MATERIAL, ([MAT_OAK:49, MAT_MISC_MAGIC:50, MAT_JOFIUM: 1]) );
+  SetProp(P_INFO, BS("In diese stabile Truhe kannst Du bestimmte "
+        "Autoload-Objekte hineinlegen und sie lagern, wenn Du sie gerade "
+        "nicht brauchst. (Mit 'deponiere <was>' kannst Du etwas in der "
+        "Truhe zur Aufbewahrung deponieren, mit 'entnehme <was> oder "
+        "'nimm <was> aus truhe' kannst Du einen Gegenstand wieder "
+        "herausnehmen.)"));
+
+  // Prop fuer rebootfeste Moebel
+  // Der endgueltige Wert (UUID des Spielers) wird per SetBesitzer() gesetzt,
+  // sobald die Truhe das erste Mal in ein Seherhaus bewegt wurde.
+  SetProp(H_FURNITURE, 1);
+
+  AD(({"platz","groesse"}), 
+      BS("Die Truhe ist recht klein, aber merkwuerdigerweise "
+        "scheint sie viel mehr an Gegenstaenden aufnehmen zu koennen, als "
+        "es von ihrer Groesse her scheint."));
+  AD(({"gegenstand","gegenstaende"}),
+      BS("Die Truhe scheint zwar vielen Gegenstaenden Platz zu bieten, aber "
+        "dafuer nimmt sie nicht jeden Gegenstand auf."));
+  AD(({"holz","eichenholz"}),
+      BS("Das Eichenholz ist sehr stabil. Es ist ganz glatt geschliffen und "
+        "traegt viele kunstvolle Runen in sich."));
+  AD(({"seiten","seite"}),
+      BS("Die Truhe hat 4 Seiten, die allesamt mit Runen verziert sind."));
+  AD(({"boden"}),
+      BS("Der Boden der Truhe traegt keinerlei Runen."));
+  AD(({"deckel"}),
+      BS("Auch der Deckel der Truhe ist mit Runen verziert."));
+  AD(({"runen","rune"}),
+      BS("Die Runen bedecken alle 4 Seiten und den Deckel der Truhe. Man "
+        "hat sie zweifellos in muehsamer Arbeit aus dem harten Holz "
+        "geschnitzt. Anschliessend wurden sie offenbar mit einem "
+        "Metall gefuellt. Du verstehst zwar ueberhaupt nicht, was "
+        "die Runen bedeuten, aber sie sind bestimmt magisch, denn wann immer "
+        "Du den Deckel oeffnest oder schliesst oder Gegenstaende hineinlegst "
+        "oder herausnimmst, leuchten die Runen hell auf."));
+  AD(({"metall"}),
+      BS("Was das wohl fuer ein Metall sein mag? Zweifellos hat es auch was "
+        "mit Magie zu tun."));
+  AD(({"magie"}),
+      BS("In dieser Truhe scheint viel Magie zu stecken, wenn selbst ein "
+        "Weltuntergang ihr nichts anhaben kann."));
+  AD(({"arbeit","fertigung"}),
+      BS("Die Fertigung dieser Truhe muss sehr aufwendig gewesen sein. "
+        "Kein Wunder, dass die Truhe so teuer ist."));
+  AD(({"wunder"}),
+      BS("Ein Wunder scheint es zu sein."));
+  AD(({"weltuntergang","armageddon"}),
+      BS("Dir schaudert beim Gedanken an den Weltuntergang."));
+  AD(({"gedanken"}),
+      BS("Denk doch lieber an was anderes..."));
+
+  AddCmd("deponier|deponiere&@PRESENT","cmd_deponiere",
+      "Was moechtest Du in der Eichenholztruhe deponieren?");
+  AddCmd("entnimm|entnehme|entnehm","cmd_entnehmen");
+
+  // bei dieser Truhe waere das Erlauben voellig sinnlos. ;-)
+  RemoveCmd(({"serlaube"}));
+
+}
+
+// keine Truhen zerstoeren, die irgendeinen INhalt haben.
+int zertruemmern(string str) {
+  // aus swift_std_container
+  string nf_str;
+  nf_str="Syntax: zertruemmer [Objekt-Id]\n"
+        +"Bsp.:   zertruemmer "+QueryProp(P_IDS)[1]+"\n";
+  notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str);
+  if(!str) return 0;
+  notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"+nf_str);
+  if(present(str)!=TO)   // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+    return 0;
+  if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
+  {
+    write( BS("Nur "+QueryHausbesitzer()+" darf "+name(WEN,1)+" zertruemmern!"));
+    return 1;
+  }
+  // Objekte enthalten? Wenn ja, abbruch.
+  if (sizeof(autoloader)) {
+    tell_object(PL,BS("Du willst gerade zum Schlag ausholen, um "
+          +name(WEN,1)+ " zu zertruemmern, als Dir einfaellt, dass "
+          +QueryPronoun(WER)+ " ja gar nicht leer ist! Nene, wer weiss, ob "
+          "Du das nicht noch brauchen koenntest."));
+    return 1;
+  }
+  // sonst geerbten Kram ausfuehren.
+  return ::zertruemmern(str);
+}
+
+// Zesstra, 1.7.07, fuers Hoerrohr
+string GetOwner() {return "zesstra";}
+
+// Prueft das Objekt auf Eignung fuer die Truhe, speichert seine Daten und
+// zerstoert es anschliessend. NODROP-Objekte koennen nur so in die Truhe
+// gelegt werden, alle anderen koennen auch mit "stecke ... in truhe"
+// deponiert werden, woraufhin ebenfalls PreventInsert() und NotifyInsert()
+// durchlaufen werden.
+protected int cmd_deponiere(string cmd, mixed args) {
+  if (!objectp(PL)) return 0;
+
+  notify_fail(Name(WER,1)+" ist doch geschlossen!\n");
+  if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN) return 0;
+  
+  notify_fail("Was moechtest Du in der Eichenholztruhe deponieren?\n");
+  if (!stringp(cmd) || !sizeof(cmd) 
+      || !pointerp(args) || !sizeof(args)) return 0;
+  object ob=args[0];
+  if (!objectp(ob)) return 0;
+  // wuerde die Truhe das Objekt ueberhaupt aufnehmen? Fehlerausgabe durch
+  // PrevenInsert()
+  if (PreventInsert(ob)) return 1;
+  // Ausziehen... Schade, dass DoUnwear nix vernuenftiges an Rueckgabewert
+  // hat. :-(
+  if (objectp(ob->QueryProp(P_WORN))) {
+    ob->DoUnwear();
+    if (objectp(ob->QueryProp(P_WORN))) {
+      tell_object(PL, BS("Du musst "+ ob->name(WEN,1)+ "zunaechst einmal "
+            "ausziehen!"));
+      return 1;
+    }
+  }
+  // wegstecken
+  if (objectp(ob->QueryProp(P_WIELDED))) {
+    ob->DoUnwield();
+    if (objectp(ob->QueryProp(P_WIELDED))) {
+      tell_object(PL, BS("Du musst "+ ob->name(WEN,1)+ "zunaechst einmal "
+            "wegstecken!"));
+      return 1;
+    }
+  }
+  // NO_CHECK und Silent-Bewegung, Meldungen an den Spieler selber machen.
+  tell_object(PL, BS("Du steckst "+ ob->name(WEN,1) + " in " + name(WEN,1) +
+      "."));
+    
+  tell_room(environment(),BS(PL->Name(WER) + " legt " + ob->name(WEN,0) +
+      " in " + name(WEN,1) + " hinein."),({PL}));
+  ob->move(ME, M_NOCHECK|M_SILENT);
+  return 1;
+}
+
+// alternative zum "nimm bla aus truhe". Spieler wollten was kurzes dafuer
+// haben.
+protected int cmd_entnehmen(string cmd) {
+  int res;
+  mixed noget;
+
+  if (!objectp(PL)) return 0;
+
+  notify_fail(Name(WER,1)+" ist doch geschlossen!\n");
+  if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN) return 0;
+
+  notify_fail(BS("Was moechtest Du aus "+name(WEM,1)+ " entnehmen?\n"));
+  if (!stringp(cmd) || !sizeof(cmd)) return 0;
+
+  object *obs=present_objects(cmd);
+
+  if (!sizeof(obs) || !objectp(obs[0]))
+    return 0;
+
+  // NOGET ist hier bloed. So ist es zwar auch nicht richtig doll... *seufz*
+  // Die hier ist/waere aber nen uebler Hack, erstmal auskommentiert lassen.
+  // also, P_NOGET sichern.
+  /*if (!closurep(noget=ob->Query(P_NOGET,F_QUERY_METHOD))) {
+    noget=ob->Query(P_NOGET,F_VALUE);
+    ob->Set(P_NOGET,0,F_VALUE);
+  }
+  else {
+    ob->Set(P_NOGET,0,F_QUERY_METHOD);
+  }*/
+  // nehmen.
+  res=PL->pick_obj(obs[0]);
+  // P_NOGET zurueckschreiben. (Ja, wenn eine Closure als F_VALUE drinsteht,
+  // landet die jetzt als F_QUERY_METHOD im Objekt.
+  /*if (closurep(noget)) {
+    ob->Set(P_NOGET,noget,F_QUERY_METHOD);
+  }
+  else
+    ob->Set(P_NOGET,noget,F_VALUE);
+*/
+  return(res);
+}
+
+// Hier wird auch das PreventInsert() von der Blueprint gerufen. Das
+// erleichtert es, die Liste an erlaubten Objekten zu aendern, ohne dass man
+// alle Clones ersetzen muss.
+varargs int PreventInsert(object ob) {
+  string oname;
+  // Das Objekt, was die Truhe gerade selber bewegt, wird ignoriert.
+  if (!objectp(ob) || ob_in_bewegung==ob) return 0;
+  
+  oname=BLUE_NAME(ob);
+  
+  // Pruefung in Clonen:
+  if (clonep(ME))
+  {
+    // nur Eigentuemer
+    if (!stringp(uuid) || !sizeof(uuid)) {
+      if (objectp(PL))
+        tell_object(PL,BS(sprintf("%s gehoert Dir nicht, daher kannst Du "
+            "auch keine Gegenstaende in %s legen.",
+            Name(WER,1),QueryPronoun(WEN))));
+      return 1;
+    }
+    if (!objectp(PL) || getuuid(PL)!=uuid) {
+      if (objectp(PL))
+        tell_object(PL,BS(sprintf("Nur %s darf Gegenstaende in %s "
+          "hineinlegen.",capitalize(explode(uuid,"_")[0]),name(WEN,1))));
+      return 1;
+    }
+    // jedes Objekt nur einmal.
+    if (member(autoloader,oname)) {
+      if (objectp(PL))
+        tell_object(PL,BS(Name(WER,1)+ " kann von einem Gegenstand jeweils "
+          "nur ein Exemplar aufnehmen, es ist aber bereits "
+          +ob->name(WER,0) + " in " + QueryPronoun(WEM) + "."));
+      return 1;
+    }
+    // jetzt Erlaubnisliste der BP fragen.
+    if (objectp(blueprint(ME)))
+      return blueprint(ME)->PreventInsert(ob);
+    else
+      return load_object(load_name(ME))->PreventInsert(ob);
+  }
+  // Ende fuer Pruefung fuer Clone.
+
+  // ab hier jetzt die Pruefung durch die BP.
+  // Keine (freigegebener) Autoloader? Hat in diesem Container nix verloren!
+  if( !ob->QueryProp(P_AUTOLOADOBJ) ) {
+    if (objectp(PL))
+      tell_object(PL,BS("In "+name(WEN,1)
+            +" kannst Du nur Autoload-Objekte hineinlegen. "));
+    return 1;
+  }
+  else if (member(blacklist,oname)) {
+    if (objectp(PL))
+      tell_object(PL,BS("In "+name(WEN,1)
+            +" kannst Du nur dafuer erlaubte Autoload-Objekte hineinlegen. "
+            +ob->Name(WER,1) + " wurde explizit als nicht erwuenscht "
+            "befunden."));
+    return 1;
+  }
+  else if (!member(whitelist,oname)) {
+    if (!member(vorschlaege,oname)) {
+      vorschlaege[oname,0]=ob->name(WER,0) || "";
+      vorschlaege[oname,1]=ob->short() || "";
+    }
+    if (objectp(PL))
+      tell_object(PL,BS("In "+name(WEN,1)
+            +" kannst Du nur dafuer erlaubte Autoload-Objekte hineinlegen. "
+            +ob->Name(WER,1) + " ist momentan nicht auf der Liste. Der "
+            "Gegenstand wurde jetzt als Vorschlag gespeichert. Bitte hab "
+            "etwas Geduld, bis sich jemand den Gegenstand angeschaut hat."));
+    // ggf. reset reaktivieren und Maintainer informieren.
+    if (!query_next_reset())
+      set_next_reset(1);
+    return 1;
+  }
+  // getragenes?
+  if (ob->QueryProp(P_WORN)) {
+      ob->DoUnwear(0);
+      if (ob->QueryProp(P_WORN)) { //ARGL. GRUMMEL.
+        if (objectp(PL))
+          tell_object(PL,BS("Willst Du "+ob->name(WEN,1) + " nicht erstmal "
+                "ausziehen?"));
+        return 1;
+      }
+  }
+  // enthaelt es irgendwelche anderen Objekte?
+  if (sizeof(all_inventory(ob))) {
+      if (objectp(PL))
+        tell_object(PL,BS(ob->Name(WER,1) + " ist nicht leer."));
+      return 1;
+  }
+
+  // andere Einschraenkungen, als hier geprueft werden, gibt es nicht.
+  return 0;
+}
+
+// hier ist das Objekt jetzt in der Truhe, d.h. Daten speichern und Objekt
+// destructen. ;)
+void NotifyInsert(object ob, object oldenv) {
+
+  // Das Objekt, was die Truhe gerade selber bewegt, wird ignoriert.
+  if (!objectp(ob) || ob==ob_in_bewegung)
+    return;
+  STORELOG(sprintf("%s deponiert %s [%O], Daten: %O",getuid(PL),ob->name(WEN,0),
+        ob,ob->QueryProp(P_AUTOLOADOBJ)));
+  // noetig sind die IDs, den Namen und die AUTOLOADOBJ-Daten des Objekts.
+  // ;-)
+  autoloader[BLUE_NAME(ob),ALDATA]=ob->QueryProp(P_AUTOLOADOBJ);
+  autoloader[BLUE_NAME(ob),IDS]=ob->QueryProp(P_IDS);
+  // Objekte, die 0 als short liefern, kriegen eine Standard-Short und werden
+  // sichtbar gemacht, da sie sonst nicht wiederzufinden sind. Objekte, die
+  // unsichtbar sein sollen, duerfen nicht erlaubt werden.
+  // TODO eine ID als Short nehmen?
+  autoloader[BLUE_NAME(ob),NAME]=capitalize((ob->short()||"<Unbekannt>.\n")[..<3]);
+  // nach dem Move die realen Objekte destructen.
+  call_out(#'check_content, 0);
+  save_me();
+}
+
+// Objekt wurde entnommen, also aus der Liste der enthaltenen Autoloader
+// loeschen. Ausserdem Objekt konfigurieren. Dies wird erst hier gemacht und
+// nicht in create_object(), damit es so aehnlich wie moeglich zum clonen der
+// Autoloader beim erstellen des Spielerobjektes wird (dort wird erst bewegt,
+// dann konfiguriert).
+void NotifyLeave(object ob, object dest) {
+  string error, oname;
+ 
+  if (!objectp(ob))
+    return;
+  
+  oname=BLUE_NAME(ob);
+  if (!member(autoloader,oname)) {
+    // Das sollte definitiv nicht passieren.
+    ERRLOG(sprintf("Gegenstand (%O) wurde entnommen, der nicht "
+      "gespeichert war!",ob));
+    return;
+  }
+
+  // wenn kein Fehler: Objekt aus Liste loeschen. Sonst wird das Objekt
+  // zerstoert. Damit bleiben die Autoloader-Daten hier erhalten und der
+  // Spieler hat kein disfunktionales Objekt im Inventar.
+  if (error) {
+    ERRLOG(sprintf("Fehler beim Konfigurieren von %O. Fehlermeldung: %O."
+        "Daten: %O",ob,error,autoloader[oname,ALDATA]));
+    ob->remove(1);
+    if (objectp(ob))
+      destruct(ob);
+  }
+  else {
+    PICKLOG(sprintf("Objekt (%O) wurde entnommen.",ob));
+    m_delete(autoloader,oname);
+    // speichern
+    save_me();
+  }
+}
+
+protected void check_content() {
+  // wenn Objekte noch in der Truhe sind, also nicht erfolgreich in
+  // einen Spieler bewegt wurden, werden zerstoert. Datenverlust gibt es
+  // hierbei nicht, weil die Daten der Autoloader nur durch NotifyLeave()
+  // geloescht werden.
+  foreach(object ob: all_inventory()) {
+    ob->remove(1);
+    if (objectp(ob)) destruct(ob);
+  }
+}
+
+// hier nochmal schauen, ob das Objekt auch in den richtigen Spieler bewegt
+// wird... Falls das move dann hier abgebrochen wird, wird das Objekt im
+// naechsten HB von der Truhe zerstoert. (chk_contents())
+varargs int PreventLeave(object ob, mixed dest) {
+  object ob2;
+  
+  //DEBUG(sprintf("PreventLeave(): Ob: %O, Dest: %O (%O)",
+  //        ob,dest,(objectp(dest)?"Objekt":"Non-Object")));
+
+  if (!objectp(ob)) return 0; 
+  // falls string uebergeben, erstmal zug. Spieler finden.
+  if (stringp(dest) && sizeof(dest))
+    dest=find_player(dest);
+
+  // richtiges INteractive? Dann darf das Objekt raus. Sonst nicht. ;-)
+  if (!objectp(dest) || !interactive(dest) || getuuid(dest)!=uuid)
+      return 1;
+
+  // pruefen, ob der Spieler schon ein Objekt dieser Blueprint dabei hat. Wenn
+  // ja, Abbruch, das koennte sonst zuviele Probleme geben.
+  if (objectp(ob2=present_clone(ob,dest))) {
+    tell_object(dest,BS("Du hast bereits "+ob2->name(WEN,0) +
+        " dabei. Zwei Gegenstaende dieser Art kannst du nicht gleichzeitig "
+        "mit Dir herumtragen."));
+    return 1; //nicht rausnehmen.
+  }
+  return 0;
+}
+
+// Objekte ausgeben, die hier angeblich drin sind. ;-) Gibt aber nur die
+// Autoloader aus, keine echten Objekt, die Container sind. Allerdings sollte
+// sowas eh nicht vorkommen. ;-)
+// flags: 1 - return array, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+varargs mixed make_invlist(object viewer, mixed inv, int flags) {
+  int iswiz;
+  mixed objs;
+
+  iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
+  // Mapping benutzen, um multiplen Overhead fuer allokation im foreach() zu
+  // vermeiden.
+  objs=m_allocate(sizeof(autoloader),1);
+  foreach(string oname, string *ids, string sh: autoloader) {
+    if (iswiz && !(flags & 4))
+      objs[oname]=sh + ". ["+oname+"]";
+    else
+      objs[oname]=sh + ".";
+  }
+  if(flags & 1) return(m_values(objs)-({""}));
+  if(!sizeof(autoloader)) return "";
+  return sprintf("%"+(sizeof(objs) > 6 ? "#" : "=")+"-78s",
+                 implode(m_values(objs)-({""}), "\n")) + "\n";
+}
+
+// erzeugt das benannte Objekt und liefert es zurueck. Liefert 0 im Fehlerfall.
+// ausserdem bewegt es das Objekt in die Truhe, damit es ein Env hat und die
+// Truhe via NotifyLeave() mitkriegt, dass es tatsaechlich entnommen wurde.
+private object create_object(string oname) {
+  string error;
+  object ob;
+  mixed noget;
+  if (!member(autoloader,oname)) return 0;
+
+  //Blueprint finden (ja, das ist nicht unbedingt noetig, man koennte auch
+  //direkt clonen)
+  if (error=catch(ob=load_object(oname);publish) ||
+      !objectp(ob)) {
+    ERRLOG(sprintf("Konnte %s nicht laden/finden. Fehler: %O",
+          oname,error));
+    return 0;
+  }
+  // clonen
+  if (error=catch(ob=clone_object(oname);publish) ||
+      !objectp(ob)) {
+    ERRLOG(sprintf("Konnte %s nicht clonen. Fehler: %O",
+        oname,error));
+    return 0;
+  }
+  // konfigurieren
+  error=catch(ob->SetProp(P_AUTOLOADOBJ,autoloader[oname,ALDATA]);publish);
+
+  //Objekt bewegen, dabei Objekt in glob. Var. merken, damit PreventInsert()
+  //und NotifyInsert() es ignorieren. *seufz*
+  ob_in_bewegung=ob;
+  ob->move(ME,M_NOCHECK);
+  ob_in_bewegung=0;
+
+  // jetzt noch nen Callout starten, damit das Objekt zerstoert wird, wenn es
+  // nicht wirklich in einen Spieler bewegt wird.
+  call_out(#'check_content,1);
+
+  return(ob);
+}
+
+// Schauen, ob die truhe ein Objekt mit passender ID enthaelt. Wenn
+// ja, das Objekt erzeugen und zurueckliefern. 
+// Diese Funktion hat eine Reihe von Nachteilen bzw. Defiziten ggue. der
+// normalerweise in Containern definierten Funktion:
+// - Nur das erste Objekt, auf das id passt. Ich hab erstmal keine Lust,
+//   hier mehrere Objekte zu erzeugen. Mal schauen, ob es so geht. Auch waere
+//   es ein Problem, wenn viele Objekt die Evalgrenze bzw. nicht alle in den
+//   Spieler bewegt werden, nachdem sie erzeugt wurden.
+// - keine komplexen Ausdruecke, nur reine IDs.
+// Ich halte den Aufwand fuer nicht gerechtfertigt.
+object *present_objects( string complex_desc ) {
+  object ob;
+  if (!stringp(complex_desc) || !sizeof(complex_desc))
+      return ({});
+  // diese Funktion liefert nur Objete zurueck, wenn der richtige Interactive
+  // versucht, sie zu entnehmen. ;-)
+  if (!objectp(this_interactive()) ||
+      getuuid(this_interactive())!=uuid)
+      return ({});
+
+  // "alles" liefert das erstbeste Objekt.
+  if (complex_desc=="alles" && sizeof(autoloader))
+  {
+    string oname=m_indices(autoloader)[0];
+    ob=create_object(oname);
+    if (objectp(ob)) return ({ob});
+  }
+
+  // ueber alle Eintraege gehen, bis eine ID stimmt, erstes passendes Objekt
+  // erzeugen und in einem Array zurueckliefern.
+  foreach(string oname, string *ids: autoloader) {
+    if (member(ids,complex_desc)==-1) continue;
+    ob=create_object(oname);
+    break; //objekt gefunden, fertig hier
+  }
+  if (objectp(ob)) return ({ob});
+  return ({}); // nix gefunden
+}
+
+
+
+// ******************* Verwaltung *********************************
+
+// registriert die Truhe auf den jeweiligen Eigentuemer.
+protected void SetBesitzer(string unused, string newuuid) {
+  if (!stringp(newuuid) || !sizeof(newuuid)) return;
+  // wenn schon registriert, abbrechen
+  if (stringp(uuid) && sizeof(uuid)) return;
+
+  uuid=newuuid;
+  Set(H_FURNITURE,uuid,F_VALUE); //Setmethode umgehen, sonst Rekursion 
+  // ab jetzt nur noch von der Truhe selber.
+  Set(H_FURNITURE,SECURED,F_MODE_AS);
+  
+  // Daten fuer den Benutzer aus der Blueprint holen (BP liefert KEINE Kopie
+  // und es darf KEINE gemacht werden!):
+  autoloader=(mapping)load_name()->GetData(uuid);
+  
+  // keine Daten gekriegt? -> Fehler loggen
+  if (!mappingp(autoloader))
+  {
+    ERRLOG(sprintf("Keine gueltigen Daten vom Truhenmaster (BP) erhalten. "
+        "initialisiere mit leerem Mapping. :-("));
+    raise_error(sprintf(
+          "Keine gueltigen Daten vom Truhenmaster (BP) fuer UUID %s "
+          "erhalten.\n",uuid));
+  }
+}
+
+// Set-Funktion
+string _set_h_furniture(mixed arg) {
+  if (stringp(arg))
+  {
+    SetBesitzer(0,arg); // bricht ab, wenn bereits registriert.
+  }
+  return(uuid);
+}
+
+// Falls das Speichern in der BP nicht klappte, rufen die Clones diese
+// Funktion. Schreiben der Daten in in Logfile zur Restaurieren per Hand.
+private void EmergencyStore(int res) {
+    ERRLOG(sprintf("EmergencyStore() called. Rueckgabewert des "
+        "Truhenmaster (BP) war: %O. Speichere Daten in Logfile. ",res));
+    write_file(__DIR__+"ALTRUHE.NOTFALLDATEN",
+        sprintf("Daten fuer %O:\n%O\n",uuid,autoloader));
+}
+
+protected void save_me() {
+  int res;
+  // nur BP speichern
+  if (!clonep(ME))
+    save_object(SAVEFILE);
+  else
+  {
+    if (objectp(blueprint(ME)))
+      res=(int)blueprint(ME)->StoreData(uuid,autoloader);
+    else
+      res=(int)load_object(load_name(ME))->StoreData(uuid,autoloader);
+
+    if (res!=1)
+      EmergencyStore(res); // Daten in einem Notfall-Logfile ablegen.
+  }
+}
+
+
+// diese Funktion wird vom Seherhausraum gerufen, sobald ein rebootfestes
+// Moebelstueck erzeugt und konfiguriert wurde.
+void post_create() {
+    if (!clonep()) return;
+
+    // wenn jetzt kein Env: destructen
+    if (!objectp(environment())) {
+        remove(1);
+        return;
+    }
+    //beim Schrankmaster registrieren
+    SCHRANKMASTER->RegisterCnt(ME, QueryProp("cnt_version_std")
+      +":"+QueryProp("cnt_version_obj"), environment()->QueryOwner(),
+       environment());
+}
+
+// diese Funktion wird vom Schrankmaster gerufen, wenn dieser meint, dass
+// dieses Objekt neu erstellt werden sollte.
+int UpdateMe()
+{
+  if (!clonep())
+    return 0;
+  if (!objectp(environment()))
+    return 1;
+  object ob=clone_object(load_name(ME));
+  if (objectp(ob)) {
+    object oldenv=environment();
+    // UUID uebertragen
+    ob->SetProp(H_FURNITURE, uuid);
+    // hierhier bewegen, dabei werden UUID und Daten von der neuen Truhe meist
+    // automatisch geholt.
+    ob->move(oldenv,M_NOCHECK);
+    // jetzt erst post_create() rufen!
+    ob->post_create();
+    // dieses Objekt rausbewegen (damit das Seherhaus es austraegt).
+    move("/room/void",M_NOCHECK);
+    // Seherhausraum speichern (koennte teuer sein!)
+    oldenv->Save(1);
+    // selbstzerstoeren
+    // __INT_MAX__ bedeutet: nicht speichern, die neue Truhe hat die daten
+    // schon aus der BP abgefragt.
+    remove(__INT_MAX__);
+  }
+  return(1);
+}
+
+// bei Selbstzerstoerung speichern bzw. Daten an die Blueprint uebermitteln
+// und beim Schrankmaster abmelden.
+varargs int remove(int silent) {
+  string uid="";
+
+  // Blueprint speichern im Savefile, Clones uebertragen die Daten hier an die
+  // Blueprint. Clones nur, wenn nicht __INT_MAX__ uebergeben wurde.
+  if (silent!=__INT_MAX__ || !clonep())
+    save_me();
+
+  if (clonep()) {
+    // Clone melden sich beim Schrankmaster ab.
+    if (objectp(environment())) {
+        uid=environment()->QueryOwner();
+    }
+    //beim Schrankmaster deregistrieren
+    SCHRANKMASTER->RemoveCnt(ME,uid);
+  }
+  return(::remove(silent));
+}
+
+// ***************** NUR BLUEPRINTS *********************************
+
+// neuen Autoloader zulassen (nur EM+!)
+varargs int AddAutoloader(string path,string nam) {
+  object ob;
+  if (clonep(ME)) return 0;
+  if (!stringp(path) || !sizeof(path)) return -1;
+  if (!ACCESS) return -2;
+  //if (!ARCH_SECURITY) return -2;
+  if (member(whitelist,path)) return -3;
+  if (catch(ob=load_object(path);publish)
+      || !objectp(ob))
+      return -4;
+  // wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
+  if (!stringp(nam) || !sizeof(nam)) {
+      nam=ob->name(WER,0);
+      if (!stringp(nam) || !sizeof(nam)) return -5;
+  }
+  ITEMLOG(sprintf("%s erlaubt: %s (%s)\n",getuid(this_interactive()),
+          path,nam));
+  whitelist+=([path:capitalize(nam)]);
+  if (member(vorschlaege,path))
+    m_delete(vorschlaege,path);
+  save_me();
+  return(1);
+}
+
+// Autoloader aus Erlaubnisliste entfernen (nur EM+!)
+int RemoveAutoloader(string path) {
+  if (clonep(ME)) return 0;
+  if (!stringp(path) || !sizeof(path)) return -1;
+  if (!ACCESS) return -2;
+  //if (!ARCH_SECURITY) return -2;
+  if (!member(whitelist,path)) return -3;
+  ITEMLOG(sprintf("%s widerruft Erlaubnis: %s (%s)\n",getuid(this_interactive()),
+          path,whitelist[path]));
+  whitelist-=([path]);
+  save_me();
+  return(1);
+}
+
+// erlaubte Autoloader abfragen
+mapping QueryAutoloader() {
+  return(copy(whitelist));
+}
+
+// neuen Autoloader in Blacklist eintragen (nur EM+!)
+varargs int AddBlacklist(string path, string nam) {
+  object ob;
+  if (clonep(ME)) return 0;
+  if (!stringp(path) || !sizeof(path)) return -1;
+  if (!ACCESS) return -2;
+  //if (!ARCH_SECURITY) return -2;
+  
+  if (member(blacklist,path)) return -3;
+
+  if (catch(ob=load_object(path);publish)
+      || !objectp(ob))
+      return -4;
+  // wenn Name nicht angegeben und auch nicht aus BP ermittelbar: Abbruch.
+  if (!stringp(nam) || !sizeof(nam)) {
+      nam=ob->name(WER,0);
+      if (!stringp(nam) || !sizeof(nam)) return -5;
+  }
+  // ggf. erlaubten entfernen.
+  if (member(whitelist,path))
+    RemoveAutoloader(path);
+  ITEMLOG(sprintf("%s verbietet: %s (%s)\n",getuid(this_interactive()),
+          path,nam));
+
+  blacklist+=([path:capitalize(nam)]);
+
+  if (member(vorschlaege,path))
+    m_delete(vorschlaege,path);
+
+  save_me();
+  return(1);
+}
+
+// Autoloader aus Blacklist entfernen (nur EM+!)
+int RemoveBlacklist(string path) {
+  if (clonep(ME)) return 0;
+  if (!stringp(path) || !sizeof(path)) return -1;
+  if (!ACCESS) return -2;
+  //if (!ARCH_SECURITY) return -2;
+  if (member(blacklist,path)==-1) return -3;
+  ITEMLOG(sprintf("%s loescht: %s (%s) von Blacklist\n",getuid(this_interactive()),
+          path,blacklist[path]));
+  blacklist-=([path]);
+  save_me();
+  return(1);
+}
+
+// gesperrte Autoloader abfragen
+mapping QueryBlacklist() {
+  return(copy(blacklist));
+}
+
+// vorschlaege abfragen
+varargs mixed QueryVorschlaege(int format) {
+  string res="\n";
+  if (!format) return(copy(vorschlaege));
+
+  foreach(string oname, string nam, string sh: vorschlaege) {
+    res+=sprintf("%.78s:\n   %.37s,%.37s\n",oname,nam,sh); 
+  }
+
+  if (format==2 && objectp(PL))
+    tell_object(PL,res);
+  else
+    return res;
+  return 0;
+}
+
+// Wird diese funktion in der Blueprint gerufen, liefert sie die Daten fuer
+// eine Truhe mit dieser UUID zurueck. Aber nur dann, wenn die auch von einer
+// truhe abgerufen wird und keinem beliebigen anderen Objekt oder wenn ein EM
+// abfragt.
+mapping GetData(string uid) {
+  if (clonep(ME)) return 0;
+  if (extern_call())
+  {
+    if (!objectp(previous_object())) return 0;
+    if (blueprint(previous_object()) != ME
+        && load_name(previous_object()) != __FILE__[..<3]
+        && !ACCESS)
+        return(0);
+  }
+  if (!stringp(uid)) return ([]);
+
+  if (!member(data, uid))
+    data[uid] = m_allocate(1,3);
+
+  // Absichtlich keine Kopie, damit die Truhen (falls jemand mehrere kauft)
+  // sich alle eine Kopie des Mappings teilen.
+  return data[uid];
+}
+
+// Loest die Speicherung der Daten im Savefile aus, denn irgendwelche Daten
+// haben sich in einem Clone geaendert, welche noch auf die Platte muessen.
+// Diese Funktion speicherte frueher Daten aus den Clones wieder im Mapping
+// des Masters (der Blueprint). Dies ist heute nicht mehr so, weil alle Truhen
+// (eines Benutzers) sich data[uid] teilen. Daher wird hier ein Fehler
+// ausgeloest, wenn es von einer Truhe ein Mapping kriegt, welches nicht
+// identisch (!) mit dem data[uid] ist.
+// liefert 0 im Fehlerfall!
+int StoreData(string uid, mapping tmp) {
+
+  if (clonep(ME)) return 0;
+
+  // Aber nur dann, wenn die auch von einer truhe abgerufen wird und keinem
+  // beliebigen anderen Objekt oder wenn ein EM setzt.
+  if (extern_call())
+  {
+    if (!objectp(previous_object()))
+      return 0;
+    if (blueprint(previous_object()) != ME 
+        && load_name(previous_object()) != __FILE__[..<3]
+        && !ACCESS)
+      return 0;
+  }
+  if (!stringp(uid) || !mappingp(tmp))
+    return(0);
+
+  // tmp muss auf _dasselbe_ Mapping zeigen wie data[uid]. Wenn das nicht der
+  // Fall ist, ist was schiefgelaufen. Jedenfalls wird dann hier nen Fehler
+  // ausgeloest.
+  if (tmp != data[uid])
+  {
+//    if(program_time(previous_object()) < 1400525694)
+//      data[uid]=tmp;
+//    else
+      raise_error("StoreData() gerufen und Daten sind nicht identisch "
+          "mit den bereits bekannten.\n");
+  }
+
+  // Absichtlich keine Kopie, damit sich alle Truhen das Mapping teilen.
+  //data[uid]=tmp;
+
+  // Savefile muss natuerlich geschrieben werden, fuer den naechsten Reboot.
+  if (find_call_out(#'save_me)==-1)
+      call_out(#'save_me,10); // Speichervorgaenge ggf. sammeln (upd -ar ...)
+  return(1);
+}
+
+// Maintainer ueber Truhenvorschlaege informieren
+void reset() {
+
+  if (clonep() || !sizeof(vorschlaege)) {
+    // ohne Vorschlaege ist auch kein reset noetig.
+    set_next_reset(-1);
+    return;
+  }
+  set_next_reset(12000); // alle 3-4h reicht.
+  foreach(string uid: MAINTAINER) {
+    object pl=find_player(uid);
+    if (objectp(pl) && query_idle(pl) < 1800)
+      tell_object(pl,BS("Es gibt neue Objektvorschlaege fuer die "
+        "Autoloadertruhe. Bitt schau da doch bei Gelegenheit mal drueber."));
+  }
+}
+
+
+// *************************************************************************
+
+// **************** SONSTIGES **********************************************
+
+// *seufz*
+// secure_level() aus der simul_efun ist hier momentan nicht brauchbar, weil
+// dort auch p/seher/moebel/autoloadertruhe in der Callerkette steht und das
+// ein levle von 0 hat. *seufz*
+nomask private int my_secure_level() {
+  int *level;
+  //kette der Caller durchlaufen, den niedrigsten Level in der Kette
+  //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
+  //von 0.
+  //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
+  //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
+  //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
+  //INteractive geben muss.
+  level=map(caller_stack(1),function int (object caller)
+      {if (objectp(caller))
+        return(query_wiz_level(geteuid(caller)));
+       return(0); // kein Objekt da, 0.
+      } );
+  return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
+}
+
+
+// debugkram
+mixed _query_content() {
+  //deep_copy, damit nicht jemand einfach so an den Daten rumbasteln kann.
+  return(deep_copy(autoloader));
+}
+
+mixed _query_owner() {
+  return(uuid);
+}
+
+int DeleteData(string uid) {
+  if (clonep(ME)) return 0;
+  if (!stringp(uid) || !sizeof(uid)) return -1;
+  if (!ACCESS) return -2;
+  //if (!ARCH_SECURITY) return -2;
+  m_delete(data,uid);
+  save_me();
+  return(1);
+}
+
+mixed testfun() {return "bin da\n";}
+
diff --git a/d/seher/haeuser/moebel/kleiderschrank.c b/d/seher/haeuser/moebel/kleiderschrank.c
new file mode 100644
index 0000000..79209b4
--- /dev/null
+++ b/d/seher/haeuser/moebel/kleiderschrank.c
@@ -0,0 +1,208 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Kleiderschrank
+// Letzte Aenderung:    17.03.2001
+// Magier:              Swift
+//--------------------------------------------------------------------------------
+#pragma strong_types,rtt_checks
+
+#include "schrankladen.h"
+#include <ansi.h>
+inherit LADEN("swift_std_container");
+
+#define VERSION_OBJ "3"
+
+string strall;
+
+protected void create()
+{
+  if (!clonep(TO)) return;
+  swift_std_container::create();
+  SetProp(P_SHORT, "Ein Kleiderschrank");
+  SetProp(P_LONG, BS(
+     "In diesem massiven Holzschrank kannst Du die Ausruestung fuer eine "
+    +"halbe Armee unterbringen. Mehrere Regale sorgen dafuer, dass alles "
+    +"seinen geordneten Platz findet, damit Du es auch schnell "
+    +"wiederfindest.")+"@@cnt_status@@");
+  AddId(({"kleiderschrank", "holzschrank", "schrank"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_NAME, "Kleiderschrank");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_WEIGHT, 5000);         // Gewicht 5 Kg
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 100);     // Mehr sollte nicht sein, lt. Tiamak.
+  SetProp(P_VALUE, 0);             // Kein materieller Wert. Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Wird im Laden zerstoert, falls er verkauft wird.
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
+  SetProp(P_MATERIAL, ({MAT_MISC_WOOD, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuchs mal mit: \"skommandos "+QueryProp(P_IDS)[1]+"\" !\n");
+  SetProp("obj_cmd", "sliste [Objekt-Id] [Kleidungs-/Ruestungstyp|\"alles\"]\n"
+                    +"  Kleidungs-/Ruestungstypen: Helme, Umhaenge, Ruestungen, Hosen, Schuhe,\n"
+                    +"                             Handschuhe, Guertel, Amulette, Ringe, Koecher,\n"
+                    +"                             Schilde, Sonstiges\n");
+  AD(({"regal", "regale"}), BS("Durch die Regale kommt etwas Ordnung in die "
+    +"vielen Sachen, die man in den Schrank stecken kann. Du kannst Dir z.B. "
+    +"eine Liste bestimmter Kleidungs-/Ruestungstypen des Schrankinhaltes "
+    +"mit dem Befehl \"sliste\" anschauen. (Das geht natuerlich nur bei "
+    +"offenem Schrank!"));
+
+  AddCmd(({"sliste"}), "auflistung");
+}
+
+varargs int PreventInsert(object ob)
+{
+  // Nur Ruestung/Kleidung hat in diesem Container was verloren!
+  if ((ob->IsClothing() || ob->QueryProp(P_ARMOUR_TYPE))
+      && !ob->QueryProp(P_WEAPON_TYPE) )
+  {
+    return ::PreventInsert(ob);
+  }
+
+  write( BS("In "+name(WEN,1)+" kannst Du nur Ruestungs-/Kleidungsstuecke legen!"));
+  return 1;
+}
+
+int auflistung2(string was, string nf_str1, string nf_str2, int alles)
+{
+  string obshort, suche, *strs;
+  object *obs;
+  int i;
+  mapping suchobs;
+  switch(was)
+  {
+    case "helme"      : suche=AT_HELMET; break;
+    case "umhaenge"   : suche=AT_CLOAK; break;
+    case "ruestungen" : suche=AT_ARMOUR; break;
+    case "hosen"      : suche=AT_TROUSERS; break;
+    case "schuhe"     : suche=AT_BOOT; break;
+    case "handschuhe" : suche=AT_GLOVE; break;
+    case "guertel"    : suche=AT_BELT; break;
+    case "amulette"   : suche=AT_AMULET; break;
+    case "ringe"      : suche=AT_RING; break;
+    case "koecher"    : suche=AT_QUIVER; break;
+    case "schilde"    : suche=AT_SHIELD; break;
+    case "sonstiges"  : suche=AT_MISC; break;
+    default           : write("Fehler: Ungueltiger Kleidungs-/Ruestungstyp. "
+                             +"Folgende Typen gibt es:\n"+nf_str1+nf_str2);
+                        return 1; break;
+  }
+  obs=all_inventory();
+  suchobs=([]);
+  for(i=0;i<sizeof(obs);i++)
+  {
+    obshort=obs[i]->QueryProp(P_SHORT);
+    if(obshort=="Nichts besonderes") // keine P_SHORT ?
+      obshort=obs[i]->Name(WER);     //   Hoffen wir mal dass das Obj. nen Namen hat.
+    if(obs[i]->QueryProp(P_ARMOUR_TYPE)==suche)    // Gesuchter ArmourType gefunden...
+    {
+      if(suchobs[obshort])                         // P_SHORT des Obj. schon im mapping?
+      {
+        if( obs[i]->QueryProp(P_AMOUNT) )
+          suchobs[obshort]+=obs[i]->QueryProp(P_AMOUNT); //   Dann den Zaehler um Amount hoch!
+        else
+          suchobs[obshort]+=1;                     //   Dann den Zaehler um 1 hoch!
+      }
+      else
+      {
+        if( obs[i]->QueryProp(P_AMOUNT) )
+          suchobs+=([obshort: obs[i]->QueryProp(P_AMOUNT)]);
+        else
+          suchobs+=([obshort: 1]);
+      }
+    }
+  }
+  strs=m_indices(suchobs);
+  if(sizeof(strs))
+  {
+    if(!alles)
+    {
+      strall+=Name(WER,1)+" enthaelt folgende "+CAP(was)+":\n";
+      strall+="------------------------------------------------------------\n";
+    }
+    else
+      strall+=ANSI_BOLD+"=== "+CAP(was)+":\n"+ANSI_NORMAL;
+    for(i=0;i<sizeof(strs);i++)
+    {
+      if(suchobs[strs[i]] > 1)
+        strall+=strs[i]+". ("+suchobs[strs[i]]+")\n";
+      else
+        strall+=strs[i]+".\n";
+    }
+  }
+  else
+    if(!alles)
+      strall+=Name(WER,1)+" enthaelt keine "+CAP(was)+"!\n";
+  return 1;
+}
+
+int auflistung(string str)
+{
+  string *strs, ob_id, was, nf_str1,nf_str2;
+  strall="";
+  nf_str1="   (Helme, Umhaenge, Ruestungen, Hosen, Schuhe, Handschuhe,\n"
+         +"    Guertel, Amulette, Ringe, Koecher, Schilde, Sonstiges)\n";
+  nf_str2="Syntax: sliste [Objekt-Id] [Kleidungs-/Ruestungstyp|\"alles\"]\n"
+         +"Bsp.:   sliste "+QueryProp(P_IDS)[1]+" "+"helme\n"
+         +"        sliste "+QueryProp(P_IDS)[1]+" alles\n";
+  notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str2);
+  if(!str) return 0;
+  if(present(str)==TO)   // Ueberpruefe, ob dieses Objekt gemeint ist!
+  {
+    write("Fehler: Es fehlt ein Kleidungs-/Ruestungstyp. Folgende Typen gibt es:\n"
+         +nf_str1+nf_str2);
+    return 1;
+  }
+  strs=old_explode(str, " ");
+  notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"
+             +nf_str2);
+  if( sizeof(strs) < 2 ) return 0;   // Nur 1 Par. und der ist nicht die Objekt-Id
+  if( sizeof(strs) == 2 )   // Anzahl Par. = 2
+  {
+    ob_id=strs[0];
+    was=strs[1];
+    if( IST_ZAHL(was) )     // Objekt-Id z.B. mit "schrank 2" angesprochen?
+      if(present(ob_id+" "+was)==TO)   // Falls dieses Objekt gemeint ist und kein 3. Par!
+      {
+        write("Fehler: Es fehlt ein Kleidungs-/Ruestungstyp. Folgende Typen gibt es:\n"
+              +nf_str1+nf_str2);
+        return 1;
+      }
+      else
+        return 0;
+  }
+  else if( sizeof(strs) == 3 )
+  {
+    ob_id=strs[0]+" "+strs[1];
+    was=strs[2];
+  }
+  else        // Anzahl erforderlicher Parameter ueberschritten!
+    return 0;
+  if(present(ob_id)!=TO)   // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+    return 0;
+  if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN)
+  {
+    write("Dazu muesste "+name(WER,1)+" geoeffnet sein.\n");
+    return 1;
+  }
+//--- Hier ist Parameterpruefung zu Ende.
+  was=LOWER(was);
+  if(was!="alles")
+    auflistung2(was, nf_str1, nf_str2, 0);
+  else
+  {
+    auflistung2("helme", nf_str1, nf_str2, 1);
+    auflistung2("umhaenge", nf_str1, nf_str2, 1);
+    auflistung2("ruestungen", nf_str1, nf_str2, 1);
+    auflistung2("hosen", nf_str1, nf_str2, 1);
+    auflistung2("schuhe", nf_str1, nf_str2, 1);
+    auflistung2("handschuhe", nf_str1, nf_str2, 1);
+    auflistung2("guertel", nf_str1, nf_str2, 1);
+    auflistung2("amulette", nf_str1, nf_str2, 1);
+    auflistung2("ringe", nf_str1, nf_str2, 1);
+    auflistung2("koecher", nf_str1, nf_str2, 1);
+    auflistung2("schilde", nf_str1, nf_str2, 1);
+    auflistung2("sonstiges", nf_str1, nf_str2, 1);
+  }
+  TP->More(strall);
+  return 1;
+}
diff --git a/d/seher/haeuser/moebel/kommode.c b/d/seher/haeuser/moebel/kommode.c
new file mode 100644
index 0000000..dea2b50
--- /dev/null
+++ b/d/seher/haeuser/moebel/kommode.c
@@ -0,0 +1,40 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Kommode
+// Letzte Aenderung:    17.03.2001
+// Magier:              Swift
+//--------------------------------------------------------------------------------
+#pragma strong_types,rtt_checks
+
+#include "schrankladen.h"
+inherit LADEN("swift_std_container");
+
+#define VERSION_OBJ "3"
+
+protected void create()
+{
+  if (!clonep(TO)) return;
+  swift_std_container::create();
+  SetProp(P_SHORT, "Eine Kommode");
+  SetProp(P_LONG, BS(
+     "Diese robuste Kommode aus Eichenholz passt einfach in jeden Raum. "
+    +"Man kann jede Menge Zeug darin verstauen, und die vielen Schubladen "
+    +"lassen es einfach verschwinden. So ein Moebelstueck gehoert einfach "
+    +"in jedes Haus!")+"@@cnt_status@@");
+  AddId(({"kommode"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_NAME, "Kommode");
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_WEIGHT, 5000);         // Gewicht 5 Kg
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 100);     // Mehr sollte nicht sein, lt. Tiamak.
+  SetProp(P_VALUE, 0);             // Kein materieller Wert. Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Wird im Laden zerstoert, falls er verkauft wird.
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
+  SetProp(P_MATERIAL, ({MAT_OAK, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuchs mal mit: \"skommandos "+QueryProp(P_IDS)[1]+"\" !\n");
+
+  AD(({"schubladen"}), BS("Es sind viele, aber zum Glueck musst Du sie nicht "
+    +"einzeln ansprechen. Du kannst einfach alles in "+name(WEN,1)+" stecken."));
+  AD(({"moebelstueck"}), BS(Name(WER,1)+" ist gemeint."));
+}
diff --git a/d/seher/haeuser/moebel/kuehlschrank.c b/d/seher/haeuser/moebel/kuehlschrank.c
new file mode 100644
index 0000000..7bf1df4
--- /dev/null
+++ b/d/seher/haeuser/moebel/kuehlschrank.c
@@ -0,0 +1,45 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Kuehlschrank
+// Letzte Aenderung:    17.03.2001
+// Magier:              Swift
+//--------------------------------------------------------------------------------
+#pragma strong_types,rtt_checks
+
+#include "schrankladen.h"
+inherit LADEN("swift_std_container");
+
+#define VERSION_OBJ "3"
+
+protected void create()
+{
+  if (!clonep(TO)) return;
+  swift_std_container::create();
+  SetProp(P_SHORT, "Ein Kuehlschrank");
+  SetProp(P_LONG, BS(
+     "Dieser strahlendweisse Kuehlschrank der Firma Brosch ueberzeugt durch "
+    +"ein leises Summen von seiner Funktion, auch wenn Du nicht weisst, woher "
+    +"er den Strom dafuer nimmt. Vermutlich auf magische Weise. Aber Du kennst "
+    +"das ja, mit Magie laesst sich fast alles erklaeren. Gut dass der "
+    +"Kuehlschrank so gross ist, man koennte einen ausgewachsenen Eisbaeren "
+    +"darin unterbringen - aber der duerfte wohl was dagegen haben.")
+    +"@@cnt_status@@");
+  AddId(({"kuehlschrank", "schrank"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_NAME, "Kuehlschrank");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_WEIGHT, 5000);         // Gewicht 5 Kg
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 100);     // Mehr sollte nicht sein, lt. Tiamak.
+  SetProp(P_VALUE, 0);             // Kein materieller Wert. Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Wird im Laden zerstoert, falls er verkauft wird.
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
+  SetProp(P_MATERIAL, ({MAT_MISC_METAL, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuchs mal mit: \"skommandos "+QueryProp(P_IDS)[1]+"\" !\n");
+
+  AD(({"strom"}), BS("Keine Ahnung, wo "+name(WER,1)+" den hernimmt, aber "
+    +"wie gesagt, bestimmt ist Magie im Spiel."));
+
+  AddSounds(({"summen"}), BS("Das Summen des Kuehlschranks ueberzeugt Dich davon, "
+    +"dass er an ist."));
+}
diff --git a/d/seher/haeuser/moebel/lesepult.c b/d/seher/haeuser/moebel/lesepult.c
new file mode 100644
index 0000000..4eb8300
--- /dev/null
+++ b/d/seher/haeuser/moebel/lesepult.c
@@ -0,0 +1,559 @@
+// Einiger Code wurde aus dem Questtagebuch des Tempels uebernommen.
+// Da man das Buch, sobald es im aufgestellten Lesepult des Spielers
+// abgelegt wurde, auch lesen koennen muss, sind die Funktionen und
+// Details auch weiterhin sinnvoll. Kaufen kann es jeder Seher, aber
+// nutzen kann man es nur mit dem von Hels Avatar ueberreichten Buch.
+
+#include <properties.h>
+#include <moving.h>
+#include <language.h>
+#include <wizlevels.h>
+#include "/d/seher/haeuser/moebel/moebel.h"
+#include "/d/seher/haeuser/haus.h"
+#include "/d/anfaenger/ark/knochentempel/files.h"
+#pragma strong_types,rtt_checks
+
+inherit LADEN("swift_std_container");
+
+static string owner;
+string le(string key);
+string in(string key);
+int cmd_eintragen(string str);
+int cmd_lesen(string str);
+int take_page(string str);
+int drop_book(string str);
+int cmd_blaettern(string str);
+int cmd_oeffnen(string str);
+int cmd_schliessen(string str);
+void AddTheOldDetails();
+
+protected void create()
+{
+ if(!clonep(this_object()))
+ {
+  set_next_reset(-1);
+  return;
+ }
+
+ ::create();
+
+owner="Anonymus";
+
+AddDetail("finger",BS(
+"Deine Finger sollten schon vorsichtig sein, wenn sie das Zeder"+
+"nholz beruehren. Einerseits, damit Du Dich nicht verletzt, and"+
+"ererseits, damit Du nichts beschmutzt.",78));
+AddDetail(({"holz","zedernholz","zeder"}),BS(
+"Aus einem einzigen starken Stamm einer Zeder bearbeitet, ist d"+
+"ieses edle Lesepult schon etwas sehr Feines und Dekoratives.",78));
+AddDetail(({"stamm","zedernstamm","holzstamm"}),BS(
+"Der Zedernstamm ist wahrlich kunstvoll veredelt worden, sein k"+
+"raeftiges Braun wurde hervorragend zur Geltung gebracht.",78));
+AddDetail(({"braun","dunkelbraun"}),BS(
+"Der dunkelbraune Stamm glaenzt wunderschoen, wenn Licht auf ih"+
+"n faellt. Ein sehr angenehmes Holz, das fuer das Lesepult verw"+
+"endet wurde.",78));
+AddDetail("geltung",BS(
+"Ja, dieses Holz gefaellt Dir sehr, das ganze Pult ist ein beme"+
+"rkenswertes Kunstwerk.",78));
+AddDetail(({"kunst","werk","kunstwerk"}),BS(
+"Immer wieder faellt Dein bewundernder Blick auf das Lesepult.",78));
+AddDetail("blick",BS(
+"Wachsam und flink wie immer, hat er jedoch ein wenig pausiert,"+
+" indem er muessig auf dem Lesepult ruht.",78));
+AddDetail(({"feines","dekoratives"}),BS(
+"Das teilweise eckige und dann wieder sanft geschwungene, runde"+
+" Lesepult sieht schon ziemlich repraesentativ aus.",78));
+AddDetail("zeder",BS(
+"Von ihr ist nurmehr das Pult geblieben, allerdings findest Du,"+
+" dass es nicht der schlechteste Teil ist.",78));
+AddDetail("teil",BS(
+"Du siehst nur den bearbeiteten Teil der Zeder vor Dir.",78));
+AddDetail("exemplar",BS(
+"Es ist ganz klar zu sehen, dass es sich nur um eine Zeder hand"+
+"elt, aus der dieses Pult entstand.",78));
+AddDetail(({"handwerk","kunst"}),BS(
+"Tatsaechlich ist dieses Lesepult ein sehr schoenes Beispiel ho"+
+"echster handwerklicher Kunst.",78));
+AddDetail("beispiel",BS(
+"Ein weiteres steht hier dummerweise nicht herum.",78));
+
+ SetProp("cnt_version_obj","1");
+ SetProp(P_NAME,"Lesepult");
+ SetProp(P_GENDER,NEUTER);
+ SetProp(P_NEVERDROP,1);
+ SetProp(P_VALUE,10);
+ SetProp(P_WEIGHT,500000);
+ SetProp(P_NOGET,"Das Lesepult ist Dir viel zu schwer.\n");
+ SetProp(P_NOSELL,"Du behaeltst das Lesepult lieber.\n");
+ SetProp(P_MATERIAL,([MAT_CEDAR:100]));
+ SetProp(H_FURNITURE,1);
+ SetProp(P_INFO,BS(
+ "Auf dieses Lesepult kann man ein ganz bestimmtes schwarzes Bu"+
+ "ch legen. Dort liegt es perfekt, um darin zu blaettern und zu"+
+ " lesen. Ausserdem birgt es sicher noch Geheimnisse.",78));
+
+ AddId(({"pult","lesepult","\nlesepult_ark"}));
+
+ AddCmd("eintragen&quest|aufgabe|loesung|erlebnis|abenteuer",
+ #'cmd_eintragen,"Was willst Du hier denn eintragen ?\n");
+ AddCmd(({"lies","lese","les"}),#'cmd_lesen);
+ AddCmd("nimm",#'take_page);
+ AddCmd(({"lege","leg"}),#'drop_book);
+ AddCmd("blaetter|blaettre|blaettere&in&buch|tagebuch|"
+ "tempeltagebuch",#'cmd_blaettern,"Worin willst Du blaettern ?|"
+ "Willst Du im Tagebuch blaettern ?");
+ AddCmd("oeffne&@ID|buch|tagebuch|tempeltagebuch",#'cmd_oeffnen,
+ "Was willst Du oeffnen?\n");
+ AddCmd("schliesse|schliess&@ID|buch|tagebuch|tempeltagebuch",
+ #'cmd_schliessen,"Was willst Du schliessen?\n");
+
+ if (MQTBMASTER->QueryTagebuchAufPult(this_player()))
+ AddTheOldDetails();
+}
+
+void SetOwner(string str)
+{
+ owner=str;
+}
+
+string QueryOwner()
+{
+ return owner;
+}
+
+string _query_short()
+{
+ if ((owner=="Anonymus")||
+      !this_player()||
+      !(MQTBMASTER->QueryTagebuchAufPult(this_player())))
+ return "Ein wunderschoenes Lesepult aus dunkelbraunem Zedernholz";
+ if (getuid(this_player())==owner)
+ return
+ "Ein wunderschoenes Lesepult, auf dem Dein Tagebuch des Tempels ruht";
+ else return
+ "Ein wunderschoenes Lesepult, das ein gueldenes Buch traegt";
+}
+
+string _query_long()
+{
+ if ((owner=="Anonymus")||
+      !this_player()||
+      !(MQTBMASTER->QueryTagebuchAufPult(this_player())))
+ return BS(
+ "Vorsichtig faehrst Du mit den Fingern ueber das dunkelbraune Holz."+
+ " Was Du spuerst, fuehlt sich kuehl und angenehm an, die Zeder, die"+
+ " fuer dieses bestaunenswerte Lesepult herhalten musste, wurde vors"+
+ "ichtig und aeusserst kunstfertig bearbeitet. Ein wirklich schoenes"+
+ " Exemplar holzhandwerklicher Kunst.",78);
+ if (getuid(this_player())==owner)
+ {
+  if (MQTBMASTER->QueryTagebuchAufPult(this_player()))
+  return BS(
+  "Vorsichtig faehrst Du mit den Fingern ueber das dunkelbraune Holz"+
+  ". Was Du spuerst, fuehlt sich kuehl und angenehm an, die Zeder, d"+
+  "ie fuer dieses bestaunenswerte Lesepult herhalten musste, wurde v"+
+  "orsichtig und aeusserst kunstfertig bearbeitet. Ein wirklich scho"+
+  "enes Exemplar holzhandwerklicher Kunst, Du bist froh, dieses Lese"+
+  "pult erstanden zu haben. Auf dem Lesepult siehst Du Dein schwarze"+
+  "s Tagebuch des Knochentempels, in dem Du nun nur noch lesen kanns"+
+  "t, denn Deine Abenteuer dort sind vollstaendig zu Papier gebracht"+
+  ". Allerdings kannst Du auch noch ein wenig weiter drin herumblaet"+
+  "tern, wenn Du magst.",78);
+  return BS(
+  "Vorsichtig faehrst Du mit den Fingern ueber das dunkelbraune Holz"+
+  ". Was Du spuerst, fuehlt sich kuehl und angenehm an, die Zeder, d"+
+  "ie fuer dieses bestaunenswerte Lesepult herhalten musste, wurde v"+
+  "orsichtig und aeusserst kunstfertig bearbeitet. Ein wirklich scho"+
+  "enes Exemplar holzhandwerklicher Kunst, Du bist froh, dieses Lese"+
+  "pult erstanden zu haben. Ein wenig gruebeln laesst Dich die Tatsa"+
+  "che, dass das Lesepult leer ist. Was man dort wohl platzieren kan"+
+  "n ?",78);
+ }
+ return BS(
+ "Vorsichtig faehrst Du mit den Fingern ueber das dunkelbraune Holz."+
+ ". Was Du spuerst, fuehlt sich kuehl und angenehm an, die Zeder, di"+
+ "e fuer dieses bestaunenswerte Lesepult herhalten musste, wurde vor"+
+ "sichtig und aeusserst kunstfertig bearbeitet. Ein wirklich schoene"+
+ "s Exemplar holzhandwerklicher Kunst, Du bist ein wenig neidisch."+
+ ""+(MQTBMASTER->QueryTagebuchAufPult(this_player())?" Auf dem Lesep"+
+ "ult wurde ein schwarzes Buch abgelegt, das sehr wertvoll aussieht.":
+ " Das Lesepult sieht ohne dazugehoeriges Buch ein wenig einsam aus.")+".",78);
+}
+
+void AddTheOldDetails()
+{
+ AddDetail(({"buch","buechlein","tagebuch"}),BS(
+ "Dies ist ein kleines Buechlein aus Gold, auf dem ein Name in "+
+ "schwarzen Lettern steht. Ansonsten ist der Einband schlicht, "+
+ "das Papier schon ausfuehrlich beschrieben und vom vielen Blae"+
+ "ttern hier und da auch schon ein wenig geknickt.",78));
+ AddDetail("blaettern",BS(
+ "Ja, das schoene Buechlein ist von haeufigem Blaettern nicht s"+
+ "choener geworden, aber irgendetwas Magisches hat es schon an "+
+ "sich. Ab und an magst Du immer wieder einmal herumblaettern.",78));
+ AddDetail("magisches",BS(
+ "Es ist nicht so einfach zu ergruenden, indem man einmal genau"+
+ "er hinsieht.",78));
+ AddDetail("leder",BS(
+ "Das Leder ist glatt und vollkommen guelden. Nur die schwarzen"+
+ " Lettern geben einen angenehmen Kontrast ab. Irgendwie seltsa"+
+ "m, denn Du findest, dass Schwarz und Gold eigentlich umgekehr"+
+ "t mehr Sinn machen.",78));
+ AddDetail("sinn",BS(
+ "Vielleicht ist aber auch alles bestens, und nur Dein Verstand"+
+ " will das nicht nachvollziehen.",78));
+ AddDetail("verstand",BS(
+ "Ja, auch Du kannst ihm nicht immer folgen, also wundere Dich "+
+ "nicht, warum dieses Leder hier golden verfaerbt ist und die B"+
+ "uchstaben darauf schwarz.",78));
+ AddDetail(({"lettern","buchstaben"}),BS(
+ "Du kannst sie lesen.",78));
+ AddDetail(({"name","namen"}),BS(
+ "Man kann ihn genauso lesen wie die Buchstaben.",78));
+ AddDetail("kontrast",BS(
+ "Die Buchstaben heben sich wirklich deutlich vom gueldenen Led"+
+ "er des Einbands ab.",78));
+ AddDetail("einband",BS(
+ "Es ist kein aufwaendiger Einband, fuerwahr. Dennoch faszinier"+
+ "t die Pracht, die mit den schwarzen Lettern kontrastiert.",78));
+ AddDetail("pracht",BS(
+ "So muss man den goldenen Einband wohl nennen.",78));
+ AddDetail(({"papier","seiten","buchseiten","seite","buchseite"}),BS(
+ "Das Papier ist von guter Qualitaet, die Buchseiten lassen sic"+
+ "h sicher einfach beschreiben.",78));
+ AddDetail(({"schwarze buchseiten","schwarze buchseite",
+ "schwarze buchseite"}),BS(
+ "Tatsaechlich, beim Blaettern siehst Du einige schwarze Buchse"+
+ "iten, die unbeschrieben sind. Was hat dies zu bedeuten ?",78));
+ AddDetail("qualitaet",BS(
+ "Du siehst, dass das Papier fest und bluetenweiss ist.",78));
+ AddReadDetail(({"buch","buechlein","tagebuch"}),BS(
+ "Das Buch hat ein Inhaltsverzeichnis. Wenn dort etwas steht, k"+
+ "annst Du das oder die Kapitel sicher lesen.",78));
+ AddDetail("kapitel",BS(
+ "Lies nur im Inhaltsverzeichnis nach, vielleicht hat das Buch "+
+ "bereits ein oder mehrere Kapitel.",78));
+ AddReadDetail("kapitel",BS(
+ "Welches Kapitel willst Du denn lesen ? Sie sind sicher durchn"+
+ "ummeriert, wenn man schon etwas im Buch niedergeschrieben hat.",78));
+ AddReadDetail(({"name","namen","buchstaben","lettern"}),#'le);
+ AddReadDetail(({"inhalt","inhaltsverzeichnis"}),#'in);
+ SetProp(P_MATERIAL,([MAT_LEATHER:10,MAT_PAPER:15,MAT_CEDAR:75]));
+}
+
+int drop_book(string str)
+{
+ if (!stringp(str))
+ return 0;
+ if (strstr(lower_case(str),"pult",0)==-1)
+ return 0;
+ notify_fail(BS(
+ "Willst Du etwas auf das Pult legen ? Ein Buch waere passend, "+
+ "aber es sollte schon etwas Besonderes sein.",78));
+ if (strstr(lower_case(str),"buch",0)==-1&&
+     strstr(lower_case(str),"buechlein",0)==-1)
+ return 0;
+ notify_fail(BS(
+ "Das ist nicht Dein Lesepult, Du laesst es besser in Ruhe.",78));
+ if (getuid(this_player())!=owner)
+ return 0;
+ notify_fail(BS(
+ "Auf dem Pult liegt schon ein goldenes Buch.",78));
+ if (MQTBMASTER->QueryTagebuchAufPult(this_player()))
+ return 0;
+ notify_fail(BS(
+ "Entweder hast Du Dich noch nicht als wuerdig erwiesen, indem "+
+ "Du alle Aufgaben im Knochentempel erfuellt hast oder Du hast "+
+ "dem Avatar des Waechters der Nacht, Mhun'dae Hel, noch nicht "+
+ "Deine Aufwartung gemacht und ihm gezeigt, dass Du wuerdig bis"+
+ "t, ein geeignetes Buch zu empfangen. Auf alle Faelle kannst D"+
+ "u hier noch nichts auf das Pult legen.",78));
+ if (!present_clone(OBJ("buechlein_invers"),this_player()))
+ return 0;
+ MQTBMASTER->AddBuchAufPult(this_player());
+ this_player()->ReceiveMsg(BS(
+ "Vorsichtig schlaegst Du Dein gueldenes Tagebuch auf, das Dir "+
+ "Mhun'dae Hel, das Avatar des Waechters der Nacht, ueberreicht"+
+ " hat. Du legst es sachte auf das Pult und merkst, dass es sof"+
+ "ort eine magische Verbindung mit dem Lesepult eingeht. Es ist"+
+ " nun untrennbar mit diesem verbunden.",78),MT_MAGIC,MA_USE);
+ tell_room(environment(),BS(
+ this_player()->Name(WER)+" legt vorsichtig ein gueldenes Buch "+
+ "mit schwarzen Lettern aufgeschlagen auf dem Lesepult ab und b"+
+ "etrachtet dieses dann ehrfuerchtig und zutiefst zufrieden.",78),
+ ({this_player()}));
+ return 1;
+}
+
+int cmd_blaettern(string str)
+{
+ notify_fail(BS(
+ "Auf dem Lesepult liegt doch gar kein Tagebuch.",78));
+ if (!(MQTBMASTER->QueryTagebuchAufPult(find_player(owner))))
+ return 0;
+ notify_fail(BS(
+ "Das ist nicht Dein Tagebuch, Du laesst es lieber in Frieden.",78));
+ if (getuid(this_player())!=owner)
+ return 0;
+ this_player()->ReceiveMsg(BS(
+ "Du blaetterst ein wenig durch Dein Tagebuch und stoesst auf e"+
+ "inige raetselhafte schwarze Seiten. Was mag es damit auf sich"+
+ " haben ? Ob Du sie herausnehmen kannst ?",78),MT_LOOK,MA_LOOK);
+ return 1;
+}
+
+int take_page(string str)
+{
+ if (!stringp(str))
+ return 0;
+ if (present(str,environment()))
+ return 0;
+ notify_fail(BS(
+ "Wo siehst Du denn hier ein Buch ?",78));
+ if (!(MQTBMASTER->QueryTagebuchAufPult(this_player()))&&
+     strstr(lower_case(str),"buch")!=-1)
+ return 0;
+ if ((owner!=getuid(this_player()))&&
+     strstr(lower_case(str),"buch")!=-1)
+ notify_fail(BS(
+ "Dies ist nicht Dein Lesepult oder Dein Buch darauf, deshalb l"+
+ "aesst Du es vorsichtshalber liegen.",78));
+ return 0;
+ if ((owner!=getuid(this_player()))&&
+     strstr(lower_case(str),"seite")!=-1)
+ notify_fail(BS(
+ "Die Eigentuemerin oder der Eigentuemer des Lesepults haette s"+
+ "icher etwas dagegen, wenn Du das Buch zerstoerst, also laesst"+
+ " Du es lieber.",78));
+ return 0;
+ // Heilung 1x alle 6 Stunden.
+ if (this_player()->check_and_update_timed_key(21600,"Ark_Hels_Schwarzes_Buch")==-1)
+ {
+  object o;
+  this_player()->ReceiveMsg(BS(
+  "Du reisst erst vorsichtig eine schwarze Seite und dann eine"+
+  " weitere aus dem goldenen Buch. Dann ueberfaellt Dich eine "+
+  "kleine Schwaeche, die Dich vor weiterem Zerstoeren innehalt"+
+  "en laesst. Du solltest Hels Grosszuegigkeit nicht ueberscha"+
+  "etzen.",78),MT_FEEL,MA_TAKE);
+  tell_room(environment(),BS(
+  this_player()->Name(WER)+" reisst zwei Seiten aus dem Buch, "+
+  "das auf dem Pult liegt. Dann haelt "+this_player()->QueryPronoun(WER)+""+
+  " inne, wahrscheinlich folgt "+this_player()->QueryPronoun(WER)+""+
+  " einer klugen Eingebung.",78));
+  o=clone_object(OBJ("reg_seite"));
+  if (o->move(this_player())!=MOVE_OK)
+  {
+   o->remove(1);
+   this_player()->ReceiveMsg(BS(
+   "Du kannst die erste Seite nicht mehr tragen, sie loest sic"+
+   "h einfach auf.",78),MT_MAGIC,MA_REMOVE);
+  }
+  // Die erste Seite behandelt, hier, falls man auch die zweite
+  // Seite nicht tragen kann oder mit der ersten am Limit ist.
+  o=clone_object(OBJ("reg_seite"));
+  if (o->move(this_player())!=MOVE_OK)
+  {
+   o->remove(1);
+   this_player()->ReceiveMsg(BS(
+   "Du kannst die zweite Seite nicht mehr tragen, sie loest si"+
+   "ch einfach auf.",78),MT_MAGIC,MA_REMOVE);
+  }
+  return 1;
+ }
+ this_player()->ReceiveMsg(BS(
+ "Vergeblich reisst Du mehr oder weniger vorsichtig an einigen"+
+ " schwarzen Seiten im Buch herum, doch sie bleiben stoerrisch"+
+ ". Es wird wohl ein wenig dauern, bis Du das wieder versuchen"+
+ " kannst.",78),MT_MAGIC,MA_USE);
+ tell_room(environment(),BS(
+ this_player()->Name(WER)+" reisst im Buch auf dem Lesepult he"+
+ "rum.",78),({this_player()}));
+ return 1;
+}
+
+int cmd_lesen(string str)
+{
+int k;
+ if(!str) return 0;
+ if (owner=="Anonymus")
+ {
+  write (BS(
+  "Dieses Lesepult scheint niemandem zu gehoeren ... es loest s"+
+  "ich daher einfach auf, als Du darauf etwas lesen willst.",78));
+  remove(1);
+  return 1;
+ }
+ if (getuid(this_player())!=owner)
+ {
+  notify_fail(BS(
+  "Dieses Tagebuch gehoert "+capitalize(owner)+", nicht Dir, Du"+
+  " bist eigentlich nicht so recht an dem interessiert, was man"+
+  " in dem Buch niedergeschrieben hat. Sicher ist es uebertrieb"+
+  "en langatmig formuliert und stimmt nicht genau mit dem ueber"+
+  "ein, was wirklich geschah. Man kennt das ja.",78));
+  return 0;
+ }
+ if (strstr(lower_case(str),"kapitel",0)!=-1&&
+     sscanf(lower_case(str),"kapitel %d",k)!=1)
+ {
+  write (BS(
+  "Willst Du ein Kapitel aus dem Buch lesen ? Dann gib bitte ei"+
+  "ne Ziffer an.",78));
+  return 1;
+ }
+ if (k>0)
+ {
+  MQTBMASTER->LiesQuest(k,this_player());
+  return 1;
+ }
+ return 0;
+}
+
+int cmd_eintragen(string str)
+{
+int i;
+ if (!(MQTBMASTER->QueryTagebuchAufPult(this_player())))
+ {
+  notify_fail(BS(
+  "Du findest hier nichts, worin Du etwas eintragen koenntest.",78));
+  return 0;
+ }
+ if (getuid(this_player())!=owner)
+ {
+  notify_fail(BS(
+  "Dieses Tagebuch gehoert "+capitalize(owner)+", nicht Dir, Du"+
+  " kannst darin nichts eintragen.",78));
+  return 0;
+ }
+ notify_fail(BS(
+ "Deine Missionen im Knochentempel ... besser gesagt, dem Wyrmt"+
+ "empel, sie sind allesamt tapfer von Dir erfuellt worden. Du k"+
+ "annst hier nichts weiter mehr eintragen, sicher aber von der "+
+ "arkanen Macht des Buches profitieren, wenn Du es einmal durch"+
+ "blaetterst.",78));
+ return 0;
+}
+
+string calc_name()
+{
+string *nam;
+string res;
+int i,j;
+ res="";
+ if (owner=="")
+ return "";
+ nam=explode(upperstring(owner),"");
+ if (j=sizeof(nam))
+ {
+  switch(j)
+  {
+   case 3..4:
+    res="          ";
+    break;
+   case 5:
+    res="        ";
+    break;
+   case 7:
+    res="      ";
+    break;
+   case 9:
+    res="    ";
+    break;
+   default:
+    res="   ";
+  }
+  for (i=0;i<j;i++)
+  {
+   res+=nam[i]+" ";
+  }
+ }
+ return res;
+}
+
+string le(string key)
+{
+ return "\n"+
+ "    De Templum Os Hel\n\n"+
+ calc_name()+"\n\n";
+}
+
+string in(string key)
+{
+ if (owner!=getuid(this_player()))
+ return "Dort steht allerdings nichts. Seltsam.\n";
+ return MQTBMASTER->LiesInhalt(find_player(owner));
+}
+
+// Das hier bezeichnet den Urheber des Moebelstuecks.
+// Nicht meine Idee!
+string GetOwner() { return "Ark"; }
+
+int cmd_oeffnen(string str)
+{
+ if (strstr(lower_case(str),"pult")!=-1)
+ {
+  this_player()->ReceiveMsg(
+  "Du kannst das Pult nicht oeffnen, nur etwas zum Lesen darauflegen.",
+  MT_LOOK,MA_FEEL);
+  return 1;
+ }
+ if (strstr(lower_case(str),"buch")!=-1)
+ {
+  if (!(MQTBMASTER->QueryTagebuchAufPult(this_player())))
+  return 0;
+  this_player()->ReceiveMsg(
+  "Das schwarze Buch ist doch schon geoeffnet.",MT_LOOK,MA_FEEL);
+  return 1;
+ }
+ // Alles andere per Default.
+ return 0;
+}
+
+int cmd_schliessen(string str)
+{
+ if (strstr(lower_case(str),"pult")!=-1)
+ {
+  this_player()->ReceiveMsg(
+  "Du kannst das Pult nicht schliessen.",MT_LOOK,MA_FEEL);
+  return 1;
+ }
+ if (strstr(lower_case(str),"buch")!=-1)
+ {
+  if (!(MQTBMASTER->QueryTagebuchAufPult(this_player())))
+  return 0;
+  this_player()->ReceiveMsg(
+  "Das Buch bleibt trotz Deiner Bemuehungen einfach offen.",MT_LOOK,MA_FEEL);
+  return 1;
+ }
+ else return 0;
+}
+
+mixed _query_noget()
+{
+ if (getuid(this_player())!=owner)
+ return BS(
+ "Dies ist offensichtlich nicht Dein Pult, deshalb laesst "+
+ "Du es auch besser liegen.",78);
+ return 0;
+}
+
+string _query_autoloadobj()
+{
+ return QueryOwner();
+}
+
+string _set_autoloadobj(string s)
+{
+ return QueryOwner();
+}
+
+// Beim Kauf setzen wir erstmal nur den Eigentuemer.
+// Ist aus Swifts Container, der diese Funktion im
+// NotifyMove() aufruft.
+protected void SetBesitzer(string uid, string uuid)
+{
+ SetOwner(uid);
+ MQTBMASTER->AddLesepult(find_player(uid)||find_netdead(uid));
+}
diff --git a/d/seher/haeuser/moebel/moebel.h b/d/seher/haeuser/moebel/moebel.h
new file mode 100644
index 0000000..debc474
--- /dev/null
+++ b/d/seher/haeuser/moebel/moebel.h
@@ -0,0 +1,13 @@
+#ifndef _SEHERMOEBEL_H_
+#define _SEHERMOEBEL_H_
+
+#define LADEN(x) "/d/seher/haeuser/moebel/"+x
+#define RAUM_VOR_LADEN "/d/unterwelt/wurzel/dorf/nstr2"
+#define IDEEN_LOG "ideen.log"
+#define SHVERWALTER "/d/seher/haeuser/hausverwalter"
+#define VERANTWORTLICHER_MAGIER "Seleven" 
+// Der Erhaelt Bug-Mails der Spieler!
+#define SCHRANKMASTER load_object(LADEN("schrankmaster"))
+
+#endif // _SEHERMOEBEL_H_
+
diff --git a/d/seher/haeuser/moebel/muellcontainer.c b/d/seher/haeuser/moebel/muellcontainer.c
new file mode 100644
index 0000000..1e95d18
--- /dev/null
+++ b/d/seher/haeuser/moebel/muellcontainer.c
@@ -0,0 +1,92 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Muellcontainer
+// Letzte Aenderung:    17.03.2001
+// Magier:              Swift
+//--------------------------------------------------------------------------------
+
+#include "schrankladen.h"
+inherit LADEN("swift_std_container");
+
+#define VERSION_OBJ ".003"
+
+create()
+{
+  if (!clonep(TO)) return;
+  swift_std_container::create();
+  SetProp(P_SHORT, "Ein Muellcontainer");
+  SetProp(P_LONG, BS(
+     "Ein grosser Container aus Stahl, aus dem es nicht gerade angenehm duftet. "
+    +"Offensichtlich werden dort alle Sachen entsorgt, die Seher nicht gerne in "
+    +"ihrem Haus oder auf der Strasse herumliegen sehen.")
+    +"@@cnt_status@@");
+  AddId(({"muellcontainer", "container"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_NAME, "Muellcontainer");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_WEIGHT, 5000);         // Gewicht 5 Kg
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 100);     // Mehr sollte nicht sein, lt. Tiamak.
+  SetProp(P_VALUE, 0);             // Kein materieller Wert. Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Wird im Laden zerstoert, falls er verkauft wird.
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
+  SetProp(P_MATERIAL, ({MAT_STEEL, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuchs mal mit: \"skommandos "+QueryProp(P_IDS)[1]+"\" !\n");
+
+  AddSmells(({"container", "duft", "gestank"}), BS("Aus "+name(WEM,1)+" stinkt es "
+    +"schon ganz schoen, aber Deine Nase ist ja allerhand gewoehnt."));
+
+  AddCmd(({"wirf", "werf", "werfe"}), "wegwerfen");
+}
+
+int wegwerfen(string str)
+{
+  string was,inwas;
+  notify_fail("Was willst Du wo hinein werfen?\n");
+  if(!str) return 0;
+  if( sscanf(str, "%s in %s", was, inwas) != 2)
+    return 0;
+  if( present(inwas)==TO )   // Dies Objekt ist gemeint!
+  {
+    TP->command_me("stecke "+str);
+    return 1;
+  }
+  return 0;  
+}
+
+varargs int PreventInsert(object ob)
+{
+  if( living(ob) )
+  {
+    write( BS("Lebewesen kannst Du nicht in "+name(WEN,1)+" werfen!"));
+    return 1;
+  }
+  else if( ob->QueryProp(P_VALUE) )
+  {
+    write( BS("Nur Dinge ohne materiellen Wert lassen sich in "
+      +name(WEN,1)+" werfen."));
+    return 1;
+  }
+  else if( ob->QueryProp(P_CURSED) )
+  {
+    write( BS("Verfluchte Dinge kannst Du nicht in "+name(WEN,1)+" werfen!"));
+    return 1;
+  }
+  else
+    return ::PreventInsert(ob);
+}
+
+void reset()
+{
+  int i;
+  object *inv;
+  ::reset();
+  inv=all_inventory(TO);
+  if(inv && sizeof(inv))
+  {
+    for(i=sizeof(inv)-1;i>=0;i--)
+      destruct(inv[i]);
+    tell_room(ETO, BS("Aus "+name(WEM,1)+" ertoent kurz ein Knistern und Knacken, "
+           +"dann ist wieder Ruhe."));
+  }
+}
diff --git a/d/seher/haeuser/moebel/preisliste.txt b/d/seher/haeuser/moebel/preisliste.txt
new file mode 100644
index 0000000..05e7839
--- /dev/null
+++ b/d/seher/haeuser/moebel/preisliste.txt
@@ -0,0 +1,102 @@
+
+=====================================================
+D E R   S C H R A E N K E R   -   P R E I S L I S T E
+=====================================================
+
+Herzlich willkommen im SCHRAENKER. Bei uns finden Sie
+das ideale Mobiliar fuer Ihr Seherhaus. Weg mit der
+Unordnung zuhaus, bringen Sie ihre Ausruestung ueber-
+sichtlich und wohlsortiert in unseren edlen Moebel-
+stuecken unter. Wir hoffen, unsere Auswahl findet
+Ihren Gefallen.
+
+ANMERKUNG: Die Waren, die hier aufgelistet sind, sind NICHT REBOOTFEST !
+           Evtl. werden wir dies zu einem spaeteren Zeitpunkt ins Auge
+           fassen, im Moment ist es nicht so, wir versuchen lieber, die
+           Moebelstuecke zu verbessern, um sie auch an die ungewoehnlichsten
+           Objekte im MorgenGrauen anzupassen.
+
+Art.-Nr.   Name             Beschreibung                                Preis
+-----------------------------------------------------------------------------
+ 1         Kommode          Eine robuste Kommode aus Eichenholz. Sie    3.799
+                            passt in jeden Raum und bietet jede Menge
+                            Platz zum Verstauen von Dingen aller Art.
+
+ 2         Kleiderschrank   Ein massiver Holzschrank, in dem Sie        5.899
+                            Kleider und Ruestungen aller Art unter-
+                            bringen koennen. Der Schrank verfuegt
+                            ueber ein Kommando zur Auflistung von
+                            Ruestungstypen.
+
+ 3         Spind            Ein grosser, einfacher Stahlschrank. Darin  5.299
+                            lassen sich Kleider und Ruestungen aller
+                            Art unterbringen. Der Spind verfuegt ueber
+                            ein Kommando zur Auflistung von Ruestungs-
+                            typen.
+
+ 4         Kuehlschrank     Ein strahlendweisser Kuehlschrank aus dem   3.499
+                            Hause Brosch. Passt in jede Kueche. Darin
+                            bleiben Ihre Speisen und Getraenke frisch!
+
+ 5         Waffenschrank    Ein massiver Holzschrank mit Faechern fuer  5.899
+                            Waffen aller Art. Jede Waffengattung hat
+                            ihren einenen Platz, es gibt ein Kommando
+                            zur Auflistung von Waffentypen.
+
+ 6         Waffentruhe      Eine Stahltruhe mit verschiedenen Faechern  5.399
+                            zur Aufnahme von Waffen aller Art. Die
+                            Truhe verfuegt ueber ein Kommando zur
+                            Auflistung von Waffentypen.
+
+ 7         Tresor           Sichern Sie Ihr Vermoegen gegen Einbruch    4.499
+                            und Diebstahl. Wie? Ganz einfach: Mit
+                            diesem stabilen, feuersicheren Tresor
+                            sind Ihre Wertsachen sicher.
+
+ 8         Zauberkiste      Sind Sie Zauberer? Oder einfach nur Sammler 7.999
+                            hier von Zauberer-Komponenten? Dann haben
+                            wir ein Moebelstueck fuer Sie, das Sie haben
+                            muessen! Aus magischem Birnbaumholz gefertigt.
+
+ 9         Muellcontainer   Der Ersatz fuer den Muellschlucker.           999
+                            Nie mehr brauchen Sie einen Laden aufsuchen,
+                            nur um wertlosen Plunder loszuwerden.
+                            
+10         Schluesselbrett  Sie haben durch Ihre Abenteuer einen Sack     499
+                            voll Schluessel? Dieses Brett schafft
+                            Ordnung.
+
+11         Duschkabine      Endlich auch fuer Ihr Zuhause:              3.499
+                            Eine ultramoderne Waschgelegenheit. Sie 
+                            koennen sogar selbst einstellen, womit Sie 
+                            Sich waschen wollen!
+
+12         mag. Holztruhe   Haben auch Sie Probleme mit einem ueber-   74.999
+                            vollen Inventar? Wir haben jetzt die
+                            Loesung fuer Sie: diese kleine Holztruhe
+                            nimmt ihre ewigen Objekte auf und 
+                            bewahrt sie sicher ueber den naechsten
+                            Weltuntergang hinaus auf!
+ 
+13         Vitrine          Gehoeren Sie auch zu den tapferen Helden,  49.999
+                            die sich bereits Pokale verdient haben? 
+                            Dann sollten Sie sich diese Vitrine 
+                            zulegen, damit Ihre Pokale eine passende
+                            Umgebung erhalten und auch beim Welt-
+                            untergang geschuetzt sind.
+
+14         Lesepult         Das hat man sich redlich verdient! Eine    60.000
+                            schoene Belohnung, wenn man schliesslich
+                            den knoechernen Tempel mal so richtig
+                            aufgeraeumt hat. Und zwar vollstaendig,
+                            so dass selbst der Totengott staunt.
+                            Endlich koennen Sie, wenn er es denn dann
+                            auch zufrieden ist, das Tagebuch hierauf
+                            ablegen und in aller Ruhe blaettern.
+-----------------------------------------------------------------------------
+Anm. der Geschaeftsleitung:
+Wir bemuehen uns, Ihrem Geschmack und Ihren Beduerfnissen gerecht zu werden.
+Falls Sie Verbesserungsvorschlaege haben, oder falls Sie Moebelstuecke fuer Ihr
+Haus in unserem Angebot vermissen, so setzen Sie doch bitte eine entsprechende
+Idee im Laden ab.
+-----------------------------------------------------------------------------
diff --git a/d/seher/haeuser/moebel/schluesselbrett.c b/d/seher/haeuser/moebel/schluesselbrett.c
new file mode 100644
index 0000000..93bace6
--- /dev/null
+++ b/d/seher/haeuser/moebel/schluesselbrett.c
@@ -0,0 +1,679 @@
+//---------------------------------------------------------------------------
+// Name des Objects:    Schluesselbrett
+// Letzte Aenderung:    02.06.2001
+// Original:            Swift
+// Neue Version:        Seleven
+// Übernommen:          23.01.2006
+//---------------------------------------------------------------------------
+/* Changelog
+   * 21.06.2007, Zesstra
+     im inherit abs. Pfad durch LADEN(x) ersetzt.
+   * 01.07.2007, Zesstra
+     GetOwner() definiert.
+   * 11.05.2015, Arathorn
+     weitraeumig ueberarbeitet zur Behebung von Fehlfunktionen
+*/
+#pragma strong_types, rtt_checks
+
+#include "/d/seher/haeuser/moebel/schrankladen.h"
+#include <ansi.h>
+inherit  LADEN("swift_std_container");
+
+#define VERSION_OBJ "3"
+// Zur Indizierung des Mappings <haken>
+#define HOOK_OB    0
+#define HOOK_LABEL 1
+
+private varargs string KeyDescription(int keynum);
+private int IsValidKey(object ob);
+private int GetNextFreeSlot();
+private object GetKeyForLabel(string label);
+private int GetHookNumberForKey(object ob);
+private void ConsolidateInventory();
+
+// Struktur: ([ haken_nr : objekt; beschriftung ])
+private mapping haken=([:2]);
+private int hooklist_long = 0;
+
+protected void create() {
+  if (!clonep(TO)) {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+  
+  foreach(int i: 1..16) {
+    m_add(haken, i);
+  }
+
+  // DEBUG! Sonst funktioniert's ausserhalb von Seherhaeusern nicht.
+  SetProp("test",1);
+  SetProp(P_SHORT, "Ein Schluesselbrett");
+  Set(P_LONG, function string () {
+    string text = BS(
+      "Ein kleines Brett aus Holz, an dem in 2 Reihen je 8 Haken befestigt "
+      "sind, an die man insgesamt 16 Schluessel haengen kann. Um die "
+      "Uebersicht nicht zu verlieren, befindet sich ueber jedem der Haken "
+      "ein kleines Schildchen aus Schiefer, das sich beschriften laesst. "
+      "Auf diese Weise geht der Sinn und Zweck des Schluessels, der an "
+      "diesem Haken ruht, nicht verloren. Praktischerweise haengt an einer "
+      "Schnur ein Stueck Kreide von dem Brett herab, mit dem Du die "
+      "Schildchen beschriften kannst.");
+    text += KeyDescription(0);
+    return text; 
+  }, F_QUERY_METHOD);
+
+  AddId(({"schluesselbrett","brett"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_CNT_STATUS, 0);
+  SetProp(P_NAME, "Schluesselbrett");
+  SetProp(P_GENDER, NEUTER);
+  SetProp(P_WEIGHT, 1000);         // Gewicht 1 Kg
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 16);
+  // Man soll nichts ueber den normalen Mechanismus reinstecken oder 
+  // rausnehmen koennen. Wird von put_and_get.c geprueft.
+  SetProp(P_CONTAINER, 0);
+  SetProp(P_NOINSERT_MSG, BS("An das Schluesselbrett kannst Du nur "
+    "Schluessel haengen.\nSyntax: 'haenge <schluessel> an brett'."));
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+
+    " laesst sich nicht bewegen.\n");
+  SetProp(P_MATERIAL, ({MAT_OAK, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuch's mal mit: \"skommandos schluesselbrett\".\n");
+
+  AD(({"holz"}), BS("Echte, robuste Eiche."));
+
+  RemoveCmd(({"schliesse","schliess","oeffne"}));
+
+  AddCmd(({"unt", "untersuche", "betrachte", "betracht", "betr",
+           "schaue", "schau"}), "unt_haken");
+  AddCmd(({"haeng", "haenge"}), "schluessel_an_haken_haengen");
+  AddCmd(({"nimm"}), "schluessel_von_haken_nehmen");
+  AddCmd("sbeschrifte", "cmd_beschriften");
+  AddCmd("sliste", "cmd_sliste");
+}
+
+/***** KOMMANDOFUNKTIONEN *****/
+
+// Konfiguration der Schluesselliste, entweder "alle" anzeigen oder nur die
+// belegten Haken.
+static int cmd_sliste(string str) {
+  notify_fail("Syntax: 'sliste alle|belegte'.\n");
+  if ( !stringp(str) || !sizeof(str) )
+    return 0;
+
+  if ( str == "alle" ) {
+    if ( !hooklist_long ) {
+      tell_object(TP, BS("Ok, es werden jetzt immer alle Haken "
+        "aufgelistet."));
+      hooklist_long = 1;
+    }
+    else 
+      tell_object(TP, BS("Die Einstellung war schon 'alle'."));
+  }
+  else if ( str == "belegte" ) {
+    if ( hooklist_long ) {
+      tell_object(TP, BS("Ok, es werden jetzt nur die belegten Haken "
+        "aufgelistet."));
+      hooklist_long =0;
+    }
+    else 
+      tell_object(TP, BS("Die Einstellung war schon 'belegte'."));
+  }
+  else 
+    return 0;
+  return 1;
+}
+
+// Haken untersuchen. Ohne Angabe der Nummer werden alle ausgegeben,
+// ansonsten der gewuenschte, sofern es diesen gibt.
+static int unt_haken(string str) {
+  if ( !stringp(str) || !sizeof(str) || strstr(str, "haken") == -1 )
+    return 0;
+  
+  int hakennr;
+  if ( sscanf(str, "haken %d", hakennr) != 1 ) {
+    string det = KeyDescription(hakennr);
+    tell_object(TP, sizeof(det) ? det : "Die Haken sind alle leer.\n");
+    return 1;
+  }
+
+  notify_fail("Welchen Haken moechtest Du untersuchen? Es gibt nur die "
+    "Haken 1 bis 16.\n");
+  if( hakennr<1 || hakennr > 16 )
+    return 0;
+
+//  ausgabe_haken(hakennr);
+  tell_object(TP, KeyDescription(hakennr));
+  return 1;
+}
+
+// Schluessel aufhaengen.
+static int schluessel_an_haken_haengen(string str) {
+  notify_fail("Was willst Du woran haengen?\n");
+  if(!stringp(str) || !sizeof(str))
+    return 0;
+  
+  notify_fail("Das darfst Du nicht.\n");
+  if(!erlaubt())
+    return 0;
+
+  string was, woran;
+  notify_fail("Folgende Syntax versteht Dein Schluesselbrett:\n"
+    "  \"haenge <schluesselname> an haken [nr]\" oder\n"
+    "  \"haenge <schluesselname> an [schluessel]brett\".\n");
+  if ( sscanf(str, "%s an %s", was, woran) != 2 )
+    return 0;
+
+  notify_fail("Was moechtest Du an das Schluesselbrett haengen?\n");
+  if ( !stringp(was) ) 
+    return 0;
+
+  notify_fail("Woran willst Du das denn haengen?\n");
+  if ( strstr(woran, "haken", 0)==-1 && !id(woran) )
+    return 0;
+
+  // Test, ob es Objekte dieses Namens gibt.
+  object *obs = TP->find_obs(was, PUT_GET_NONE);
+  // Aus der Liste der gefundenen Objekte alle rausfiltern, die keine
+  // Schluessel sind. Als Schluessel gelten hier alle Objekte, die die
+  // ID "schluessel" haben oder die mittels QueryDoorKey() einer bestimmten
+  // Tuer zugeordnet sind.
+  obs = filter(obs, #'IsValidKey);
+  notify_fail("Keine(n) Schluessel mit dem Namen gefunden.\n");
+  if ( !sizeof(obs) )
+    return 0;
+
+  int nr = GetNextFreeSlot();
+  notify_fail("Am Schluesselbrett ist kein Haken mehr frei.\n");
+  if ( !nr )
+    return 0;
+
+  // Wenn nur ein Schluessel gefunden wurde, wird genauer geschaut, ob
+  // der Spieler gesagt hat, wo der hinsoll.
+  if ( sizeof(obs) == 1 ) {
+    if ( woran != "haken" || !id(woran) )
+      sscanf(woran, "haken %d", nr);
+  }
+
+  notify_fail("Es gibt nur die Haken 1 bis 16.\n");
+  if ( nr < 1 || nr > 16 )
+    return 0;
+
+  notify_fail("Der Haken ist nicht mehr frei.\n");
+  if ( objectp(haken[nr,HOOK_OB]) )
+    return 0;
+
+  // Alle gefundenen Schluesselobjekte ans Brett haengen.
+  foreach( object key : obs ) {
+    switch (key->move(ME, M_PUT)) {
+      // Die Kapazitaet ist ueber P_MAX_OBJECTS begrenzt. Alle
+      // Schleifendurchlaeufe, die die Kapazitaetsgrenze erreichen, landen
+      // in default und beenden damit die Schleife. Daher muessen sie 
+      // nicht gesondert behandelt werden.
+      case MOVE_OK :
+        // Falls das Brett voll sein sollte, aber faelschlicherweise 
+        // dennoch ein weiterer Schluessel hineinbewegt wurde, ist nr==0,
+        // und es wird ein Fehler ausgeloest, damit nicht hinterher ein
+        // Schluessel an Haken 0 haengt.
+        if ( !nr ) {
+          raise_error("Fehler: Haken 0 darf nicht belegt werden.");
+          break;
+        }
+        tell_object(TP, BS("Du haengst "+obs[0]->name(WEN,1)+" an Haken "+
+          nr+"."));
+        // Nur wenn ein Schluessel erfolgreich drangehaengt wurde, wird er
+        // in die Liste eingetragen. Etwaige Beschriftungen bleiben bestehen,
+        haken[nr, HOOK_OB] = key;
+        // und nur dann wird der naechste Slot probiert. Wenn nur ein 
+        // Schluessel verarbeitet wird, ist das hier unschaedlich, weil es 
+        // nicht nochmal ausgewertet wird.
+        nr = GetNextFreeSlot();
+        // Schluessel wurde verarbeitet und Meldung ausgegeben
+        obs -= ({key});
+        break;
+      case ME_CANT_LEAVE_ENV :
+      case ME_CANT_BE_INSERTED :
+      case ME_CANT_BE_DROPPED :
+        // Schluessel wurde nicht erfolgreich drangehaengt.
+        tell_object(TP, BS(key->Name(WER,1)+" weigert sich, ans "
+          "Schluesselbrett gehaengt zu werden."));
+        // Meldung wurde ausgegeben, nichts weiter zu tun.
+        obs -= ({key});
+        break;
+      // TOO_MANY_OBJECTS wird nicht hier behandelt, sondern weiter unten,
+      // um eine Auflistung der Schluesselnamen ausgeben zu koennen, statt
+      // eine Zeile pro Schluessel. Die anderen Rueckgabewerte von move() 
+      // sind bei dieser Art der Bewegung nicht zu erwarten.
+      default: break;
+    }
+  }
+  // Fuer alle uebrigen war wohl TOO_MANY_OBJECTS der Rueckgabewert, denn
+  // das Schluesselbrett erlaubt nur P_MAX_OBJECTS = 16.
+  if ( sizeof(obs) ) {
+    tell_object(TP, BS("Fuer "+CountUp(obs->name(WEN))+" ist leider kein "
+      "Haken mehr am Schluesselbrett frei."));
+  }
+  return 1;
+}
+
+// Schluessel abnehmen
+static int schluessel_von_haken_nehmen(string str) {
+  if( !stringp(str) || !sizeof(str) ) 
+    return 0;
+
+  string was, woraus;
+  if ( sscanf(str, "%s aus %s", was, woraus) != 2 && 
+       sscanf(str, "%s von %s", was, woraus) != 2 )
+    return 0;
+
+  notify_fail("Das darfst Du nicht!\n");
+  if(!erlaubt()) 
+    return 0;
+ 
+  notify_fail("Woraus willst Du das denn nehmen?\n");
+  if ( !id(woraus) && strstr(woraus, "haken", 0) == -1 ) 
+    return 0;
+
+  notify_fail("Was willst Du denn vom Schluesselbrett nehmen?\n");
+  if( !stringp(was) ) 
+    return 0;
+  
+  notify_fail("Das Schluesselbrett ist doch aber leer.\n");
+  if ( !sizeof(all_inventory(ME)) )
+    return 0;
+
+  int hakennr;
+  object *keys = ({});
+  // Es muessen auch Schluessel von bestimmten Haken abgenommen
+  // werden koennen, nicht nur jeweils der erste mit der angegebenen ID.
+  // Dies hat auch Prio gegenueber der allgemeinen Begriffssuche mit
+  // find_obs().
+  if ( sscanf(woraus, "haken %d", hakennr) == 1 ) {
+    notify_fail("Es gibt nur die Haken 1 bis 16.\n");
+    if ( hakennr < 1 || hakennr > 16 ) 
+      return 0;
+    notify_fail("An diesem Haken haengt aber doch kein Schluessel.\n");
+    if ( !objectp(haken[hakennr,HOOK_OB]) )
+      return 0;
+    
+    keys += ({ haken[hakennr,HOOK_OB] });
+  }
+
+  // Keiner gefunden? Vielleicht ist ja einer mit einer bestimmten 
+  // Beschriftung gemeint?
+  if ( !sizeof(keys) ) {
+    // Schluessel mit dem angegebenen Namen suchen
+    keys = TP->find_obs(was+" in brett in raum");
+    // Hakennummer ermitteln, wenn eine Beschriftung angegeben wurde.
+    string wanted_desc;
+    sscanf(was, "schluessel fuer %s", wanted_desc);
+    object wanted_key = GetKeyForLabel(wanted_desc);
+    if ( objectp(wanted_key) )
+      keys += ({wanted_key});
+  }
+  // Immer noch nix gefunden? Dann muss sich der Spieler was anderes 
+  // einfallen lassen.
+  notify_fail("Am Schluesselbrett haengt kein solcher Schluessel.\n");
+  if ( !sizeof(keys) )
+    return 0;
+
+  // Alle gefundenen Schluessel verarbeiten. Alle, die nicht bewegt werden
+  // koennen, verbleiben in der Liste.
+  foreach( object key : keys ) {
+    int current_hook = GetHookNumberForKey(key);
+    if ( key->move(TP,M_GET) == MOVE_OK ) {
+      keys -= ({ key });
+      tell_object(TP, BS("Du nimmst "+key->name(WEN,1)+" von Haken "+
+        current_hook+" ab."));
+      // Schluessel austragen; bestehende Beschriftungen bleiben erhalten
+      haken[current_hook, HOOK_OB] = 0;
+    }
+  }
+  // Von allen nicht vom Haken genommenen Schluesseln die Namen ermitteln
+  // und auflisten.
+  if ( sizeof(keys) ) {
+    tell_object(TP, BS(capitalize(CountUp(keys->name(WER,1)))+" "+
+      (sizeof(keys)==1?"konnte":"konnten")+" nicht vom Schluesselbrett "
+      "abgenommen werden."));
+  }
+  return 1;
+}
+
+// Verfuegbare Kommandos auflisten.
+varargs int skommandos(string str) {
+  notify_fail( "Fehler: Dieser Befehl benoetigt eine gueltige Objekt-Id als "
+    +"Parameter.\nBeispiel: skommandos [schluessel]brett\n");
+  
+  if( !id(str) ) return 0;
+
+  write(
+    "==================================================================="
+    "==========\n"
+    "Aktuelle Version: "+QueryProp("cnt_version_std")+
+      QueryProp("cnt_version_obj")+"\n"+
+    BS(Name(WER,1)+" kann nur in diesem Seherhaus verwendet werden, "
+    "da "+QueryPronoun(WER)+" speziell dafuer gekauft wurde. "+
+    CAP(QueryPronoun(WER))+" verfuegt ueber folgende Kommandos:")+
+    "----------------------------------------------------------------------"
+    "-------\n"
+    "serlaube [schluessel]brett [spielername|\"hausfreunde\"|\"zweities\"]\n"
+    "  Erlaubt Personen, "+name(WEN,1)+" mitzubenutzen.\n"
+    "  serlaube ohne Angabe eines Arguments listet alle Personen mit Zugriff "
+      "auf\n  "+name(WEN,1)+" auf.\n\n"
+    "verschiebe [schluessel]brett nach [ausgang]\n"
+    "  Damit kannst Du "+name(WEN,1)+" innerhalb Deines Seherhauses "
+    "verschieben.\n\n"
+    "sverstecke [schluessel]brett\n"
+    "  Damit machst Du "+name(WEN,1)+" unsichtbar.\n"
+    "shole [schluessel]brett hervor\n"
+    "  Damit machst Du "+name(WEN,1)+" wieder sichtbar.\n"
+    "zertruemmer [schluessel]brett\n"
+    "  Damit zerstoerst Du das Schluesselbrett.\n"
+    "sbeschrifte haken <nr> mit <text>|nichts\n"
+    "  Eine Beschriftung an einem Haken anbringen oder loeschen.\n"
+    "sliste lang|kurz\n"
+    "  Schluesselanzeige konfigurieren: alle oder nur belegte auflisten.\n"
+    "haenge <schluesselname> an haken [nr]\n"
+    "haenge <schluesselname> an [schluessel]brett\n"
+    "  Damit haengst Du einen Schluessel an das Brett.\n"
+    "nimm schluessel von haken <nr>\n"
+    "nimm <schluesselname> von [schluessel]brett\n"
+    "nimm schluessel fuer <beschriftung> von [schluessel]brett.\n"
+    "  Damit nimmst Du einen Schluessel vom Schluesselbrett.\n"
+    "==================================================================="
+    "==========\n");
+  return 1;
+}
+
+static int erlaubnis_liste()
+{
+  if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
+  {
+    tell_object(TP, BS("Nur "+QueryHausbesitzer()+" darf Berechtigungen "
+      +name(WESSEN,1)+" abfragen!"));
+    return 1;
+  }
+  
+  string *strs;
+  string str = 
+    "==================================================================="
+    "==========\n";
+  if ( hauserlaubnis ) {
+    strs = SHVERWALTER->HausProp(LOWER(QueryHausbesitzer()), 2);
+    str += "Folgende Freunde Deines Hauses duerfen "+name(WEN,1)+
+      " mitbenutzen:\n";
+    if(sizeof(strs)) {
+      str += BS(CountUp(strs));
+    }
+    else {
+      str += BS("Du hast keiner Person Zugriff auf Dein Haus gewaehrt...");
+    }
+  }
+  else {
+    str+= BS("Die Freunde Deines Hauses duerfen "+name(WEN,1)+
+      " nicht mitbenutzen.");
+  }
+
+  str += 
+    "-------------------------------------------------------------------"
+    "----------\n";
+
+  if( zweitieerlaubnis!="" ) {
+    if( zweitieerlaubnis==geteuid(TP) )
+      str += BS("Alle Deine Zweities duerfen "+name(WEN,1)+" mitbenutzen.");
+    else
+      str += BS("Alle Zweities von "+CAP(zweitieerlaubnis)+" duerfen "
+        +name(WEN,1)+" mitbenutzen.");
+    str +=
+      "-------------------------------------------------------------------"
+      "----------\n";
+  }
+  
+  strs=QueryProp("cnt_erlaubnis");
+
+  if(sizeof(strs)) {
+    str += BS("Folgende sonstige Personen duerfen "+name(WEN,1)+
+      " mitbenutzen:");
+    str += CountUp(strs);
+  }
+  else {
+    str += BS("Es gibt keine sonstigen Personen, die "+name(WEN,1)+
+      " mitbenutzen duerfen.");
+  }
+  str +=
+    "==================================================================="
+    "==========\n";
+  
+  return 1;
+}
+
+// Haken beschriften.
+static int cmd_beschriften(string str) {
+  string nfm = "Syntax: sbeschrifte haken <hakennummer> mit "
+    "<text>|nichts\n"
+    "Fuer <text> kannst Du einen beliebigen Text mit bis zu 30 Zeichen "
+    "angeben,\n"
+    "'nichts' loescht die Beschriftung eines Schildes.\n";
+
+  notify_fail("Das darfst Du nicht!\n");
+  if(!erlaubt()) 
+    return 0;
+  
+  string text;
+  int hnr;
+  str = TP->_unparsed_args(0);
+  notify_fail(nfm);
+  if ( !stringp(str) || !sizeof(str) ) 
+    return 0;
+  if ( sscanf(str,"haken %d mit %s", hnr, text)!=2 )
+    return 0;
+
+  notify_fail("Welchen Haken moechtest Du beschriften?\n"+nfm);
+  if(!hnr) 
+    return 0;
+  
+  notify_fail("Es gibt nur die Haken 1 bis 16.\n");
+  if ( hnr < 1 || hnr > 16 ) 
+    return 0;
+
+  notify_fail("Womit moechtest Du den Haken beschriften?\n"+nfm);
+  if( !stringp(text) || !sizeof(text) )
+    return 0;
+
+  notify_fail("Der Text sollte nicht laenger als 30 Zeichen sein.\n");
+  if(sizeof(text) > 30) 
+    return 0;
+
+  if ( lower_case(text) == "nichts" ) {
+    if ( stringp(haken[hnr,HOOK_LABEL]) ) {
+      tell_object(TP, BS("Sorgfaeltig wischst Du die Beschriftung des "
+        "Hakens "+hnr+" weg."));
+      haken[hnr,HOOK_LABEL]=0;
+    }
+    else {
+      tell_object(TP, BS("Du musst Dich geirrt haben. Haken "+hnr+" ist "
+        "gar nicht beschriftet."));
+    }
+  }
+  else if ( lower_case(text) == "schluessel" ) {
+    tell_object(TP, BS(
+      "Du solltest Dir etwas Aussagekraeftigeres einfallen lassen."));
+  }
+  else {
+    tell_object(TP, BS("Du beschriftest das Schildchen ueber Haken "+hnr+
+      " mit "+text+"."));
+    tell_room(environment(), BS(
+      TP->Name(WER)+" schreibt etwas ans Schluesselbrett."), ({TP}));
+    haken[hnr,HOOK_LABEL] = text;
+  }
+  return 1;
+}
+
+/***** HILFSFUNKTIONEN *****/
+
+// Beschreibung eines Hakens erzeugen. Wenn <keynum> angegeben wird, 
+// wird nur der gesuchte Haken angezeigt, egal ob leer oder nicht. 
+// Ansonsten werden die belegten Haken angezeigt, ausser wenn 
+// <hooklist_long> gesetzt ist. Dann werden alle Haken aufgelistet.
+private varargs string KeyDescription(int keynum) {
+  string desc="";
+  mapping keylist=([:2]);
+
+  // Liste derHaken auf den/die gesuchten einschraenken.
+  // Nur ein Haken soll angeschaut werden. Dies hat immer Prioritaet,
+  // d.h. auch bei <sliste alle> wird nur ein Haken gelistet.
+  if ( keynum ) {
+    m_add(keylist, keynum, m_entry(haken, keynum)...);
+  }
+  // Wenn <sliste alle> gesetzt ist, alle ausgeben.
+  else if ( hooklist_long ) {
+    keylist = haken;
+  }
+  // Ansonster nur die belegten.
+  else {
+    keylist = filter(haken, 
+      function int (int num, <string|object>* values) {
+        return objectp(values[0]);
+      });
+  }
+
+  // Ueber das Mapping mit den selektierten Haken laufen und Ausgaben
+  // erzeugen.
+  foreach(int num : 1..16) {
+    // Hier wird der aktuelle Wert uebersprungen, wenn er nicht enthalten 
+    // ist, da die Zuweisung in <key> und <keydesc> auch dann eine 0 ergeben
+    // wuerde, und somit Keys mit leeren Values nicht von nicht existierenden
+    // Keys zu unterscheiden waeren.
+    if ( member(m_indices(keylist), num) == -1 )
+      continue;
+    string keydesc = keylist[num, HOOK_LABEL];
+    object key = keylist[num, HOOK_OB];
+    desc += "Haken " + num + (stringp(keydesc)?" ["+keydesc+"]: " : ": ");
+    desc += (objectp(key) ? key->short() : "leer\n");
+  }
+  return desc;
+}
+
+// Rueckgabewert: 1, wenn es sich um ein Schluesselobjekt handelt, sonst 0.
+// Schluessel werden anhand der ID "schluessel" erkannt oder daran, dass
+// QueryDoorKey() einen Wert zurueckgibt, d.h. dass dieses Objekt eine
+// Standard-Doormaster-Tuer oeffnet.
+private int IsValidKey(object ob) {
+  return (objectp(ob) && (ob->id("schluessel") || ob->QueryDoorKey()!=0));
+}
+
+// Gibt die Nummer des naechsten freien Hakens zurueck oder 0, wenn
+// keiner frei ist. Da Mappings nicht sortiert sind, muss von 1-16 numerisch
+// iteriert werden, damit immer der erste freie Haken gefunden wird und nicht 
+// irgendeiner.
+// Rueckgabewert: Hakennummer von 1-16 oder 0, wenn kein Haken frei ist.
+private int GetNextFreeSlot() {
+  int slot;
+  foreach(int i : 1..16) {
+    if ( !objectp(haken[i,HOOK_OB]) ) {
+      slot = i;
+      break;
+    }
+  }
+  return slot;
+}
+
+// Gibt zu einer Schild-Beschriftung die zugehoerige Hakenposition aus.
+// Es wird die kleingeschriebene Variante gesucht, so dass Spieler zwar
+// Grossschreibung in den Beschriftungen verwenden koennen, diese aber
+// nicht beruecksichtigt wird. Ansonsten koennte es zu Schwierigkeiten mit
+// Dopplungen wie "Vesray" und "vesray" kommen.
+// Rueckgabewert: Hakennummer von 1-16 oder 0, wenn der Text nicht gefunden
+// wurde.
+private object GetKeyForLabel(string label) {
+  if ( label ) 
+    label = lower_case(label);
+  foreach(int i: 1..16) {
+    if ( haken[i, HOOK_LABEL] && label &&
+         lower_case(haken[i,HOOK_LABEL]) == label )
+      return haken[i,HOOK_OB];
+  }
+  return 0;
+}
+
+// Gibt zu dem uebergebenen Objekt die Nummer des Hakens zurueck, an dem
+// es haengt.
+private int GetHookNumberForKey(object ob) {
+  foreach(int num, object key : haken) {
+    if ( key == ob )
+      return num;
+  }
+  return 0;
+}
+
+// Bereinigt Datenstruktur in <haken> und das Inventar des Brettes, indem
+// a) geprueft wird, ob alle in <haken> eingetragenen Objekte sich auch im 
+//    Brett befinden und
+// b) geprueft wird, ob alle im Inventar befindlichen Objekte auch korrekt in 
+//    <haken> eingetragen sind.
+// Ueberzaehlige Objekte werden rausgeworfen.
+private void ConsolidateInventory() {
+  foreach(int i, object ob : haken) {
+    // Objekt existiert, ist aber nicht im Schluesselbrett? Dann austragen.
+    if (objectp(ob) && environment(ob) != ME) {
+      m_delete(haken, i);
+      // Wird auch vom reset() aus gerufen, dann ist TP == 0.
+      if ( objectp(TP) )
+        tell_object(TP, BS("Hoppla! "+ob->Name(WER,1)+" scheint vom "
+          "Schluesselbrett verschwunden zu sein!"));
+    }
+  }
+  object *resterampe = all_inventory(ME) - m_values(haken, HOOK_OB);
+  if (sizeof(resterampe)) {
+    resterampe->move(environment(),M_PUT);
+    tell_room(environment(), BS("Ploetzlich faellt etwas vom "
+      "Schluesselbrett zu Boden, das Du zuvor gar nicht gesehen hattest."));
+  }
+}
+
+string short() {
+  string sh=QueryProp(P_SHORT);
+
+  // Unsichtbar? Dann gibts nichts zu sehen ...
+  if ( QueryProp(P_INVIS) || !sh )
+    return 0;
+  if ( QueryProp("versteckt") == 1 ) 
+    sh = "";
+  else 
+    sh += ".";
+
+  return process_string(sh)+"\n";
+}
+
+// Im Reset vorsichtshalber auch mal aufraeumen.
+void reset() {
+  ConsolidateInventory();
+  return ::reset();
+}
+
+varargs int PreventInsert(object ob) {
+  // Kein Schluessel? Hat in diesem Container nix verloren!
+  if( !IsValidKey(ob )) {
+    return 1;
+  }
+  ConsolidateInventory();
+  return ::PreventInsert(ob);
+}
+
+// Wenn ein Schluessel sich aufloest, muss die Liste abgeglichen werden.
+void NotifyRemove(object ob) {
+  tell_room(environment(ME), BS("Hoppla! "+ob->Name(WER,1)+" hat sich "
+    "gerade in Wohlgefallen aufgeloest."));
+  // Das Objekt <ob> zerstoert sich erst nach dem Aufruf dieser Funktion,
+  // daher per call_out() aufraeumen.
+  call_out(#'ConsolidateInventory, 1);
+}
+
+// Zesstra, 1.7.07, fuers Hoerrohr
+// Darf ich da Arathorn eintragen? ;-)
+string GetOwner() {
+  return "seleven";
+}
+
diff --git a/d/seher/haeuser/moebel/schraenker_gutschein.c b/d/seher/haeuser/moebel/schraenker_gutschein.c
new file mode 100644
index 0000000..9c89204
--- /dev/null
+++ b/d/seher/haeuser/moebel/schraenker_gutschein.c
@@ -0,0 +1,44 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Schraenger-Gutschein
+// Letzte Aenderung:    05.05.2001
+// Magier:              Swift
+//--------------------------------------------------------------------------------
+
+#include "schrankladen.h"
+#include <ansi.h>
+inherit "std/thing";
+
+create()
+{
+  if (!clonep(TO)) return;
+  ::create();
+  SetProp(P_SHORT, "Ein Gutschein");
+  SetProp(P_LONG, "@@longtext@@");
+  AddId(({"gutschein"}));
+  SetProp(P_NAME, "Gutschein");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_WEIGHT, 5);            // Gewicht 5g
+  SetProp(P_VALUE, 0);             // Kein materieller Wert. Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Wird im Laden zerstoert, falls er verkauft wird.
+  SetProp(P_MATERIAL, ({MAT_PAPER}) );
+  SetProp(P_INFO, "Dieser Gutschein ist nur fuer einen Seher mit Eigenheim von Interesse!\n");
+  SetProp(P_AUTOLOADOBJ, 1);
+}
+
+string longtext()
+{
+  string str,aussteller,inhaber,text;
+  int wert;
+  aussteller="Seleven";
+  inhaber="Filli";
+  wert=5000;
+  text="";
+  str="***********************************************************************\n\n"
+     +"  D E R   S C H R A E N K E R\n\n"
+     +"  E I N K A U F S - G U T S C H E I N    .\n\n"
+     +"  Ausgestellt von "+aussteller+" fuer "+inhaber+".\n\n"
+     +"  Wert: "+to_string(wert)+" Muenzen.\n\n"
+     +text
+     +"***********************************************************************\n";
+  return str;
+}
diff --git a/d/seher/haeuser/moebel/schrankladen.c b/d/seher/haeuser/moebel/schrankladen.c
new file mode 100644
index 0000000..bcaf25c
--- /dev/null
+++ b/d/seher/haeuser/moebel/schrankladen.c
@@ -0,0 +1,436 @@
+//----------------------------------------------------------------------------
+// Name des Objects:    SCHRAENKER
+// Letzte Aenderung:    06.04.2006 P_LOG_FILE (Seleven)
+// Magier:              Swift
+//----------------------------------------------------------------------------
+#pragma strong_types,rtt_checks
+inherit "/std/room";
+
+#include <properties.h>
+#include <language.h>
+#include "schrankladen.h"
+
+
+protected void create()
+{
+  ::create();
+  SetProp(P_LOG_FILE,"seleven/schraenker"); // Seleven 06.04.2006
+  SP(P_INT_SHORT, "Im SCHRAENKER");
+  SP(P_INT_LONG, BS(
+     "Du stehst in einem gemuetlichen kleinen Laden. Ein flauschiger "
+    +"Teppichboden daempft Deine Schritte, und der Geruch verschiedener "
+    +"Hoelzer kitzelt angenehm in der Nase. Irgendwoher erklingt eine "
+    +"leise, beruhigende Melodie. An den Waenden haengen Bilder und "
+    +"Zeichnungen verschiedenster Art, sowie eine grosse Preisliste. "
+    +"Wenn Du nicht den Eindruck haettest, dass man hier an Deine Boerse "
+    +"moechte, koenntest Du Dich hier richtig wohl fuehlen.",78));
+
+  SP(P_LIGHT, 1);
+  SP(P_INDOORS, 1);
+
+  AD( ({"zeit"}), BS(
+     "Die aktuelle Zeit: "+ctime(time()),78));
+  AD( ({"ausgang"}), BS(
+     "Nach Osten geht's raus.",78));
+  AD( ({"ausgaenge"}), BS(
+     "So sehr Du auch suchst, mehr als den einen Ausgang findest Du "
+    +"einfach nicht.",78));
+  AD( ({"geldboerse", "boerse"}), BS(
+     "Man redet halt von Boerse, auch wenn Du keine dabeihast. Es geht "
+    +"halt um Dein Geld!",78));
+  AD( ({"geld"}), BS(
+     "Irgendwo wirst Du schon Kohle haben! :)",78));
+  AD( ({"kohle", "zaster", "moneten", "piepen", "maeuse"}), BS(
+     "Kohle, Zaster, Moneten, Piepen, Maeuse... alles dasselbe! :)",78));
+  AD( ({"boden"}), BS(
+     "Ein flauschiger, aber auch sehr strapazierfaehiger Teppich bedeckt "
+    +"den ganzen Boden des Ladens. Billig sieht er nicht aus.",78));
+  AD( ({"teppich", "teppichboden"}), BS(
+     "Es handelt sich um einen echten Vorberg-Teppich. Er wurde "
+    +"von einem Fachmann verlegt und scheint aus einem einzigen grossen "
+    +"Stueck zu sein.",78));
+  AD( ({"stueck"}), BS(
+     "Der Teppich scheint aus einem einzigen grossen Stueck zu sein. So gut "
+    +"kann das eigentlich nur ein Fachmann.",78));
+  AD( ({"fachmann"}), BS("Es ist keiner zu sehen, aber es ist anzunehmen, dass "
+    +"es sich um einen professionellen Teppichleger handelte."));
+  AD( ({"teppichleger"}), "Wie gesagt, keiner zu sehen. Der Teppich liegt ja "
+    +"schon, und der wird auch noch eine Weile halten.\n");
+  AD( ({"weile"}), "Schon noch ein paar Jaehrchen!\n");
+  AD( ({"jaehrchen"}), "Teppiche der Firma Vorberg koennen lange halten.\n");
+  AD( ({"vorberg", "vorbergteppich", "vorberg-teppich"}), BS(
+     "Die Firma Vorberg stellt, im Gegensatz zur Firma Hinterberg, sehr gute "
+    +"Teppiche her, wie jeder weiss.",78));
+  AD( ({"gegensatz"}), "Das weiss wirklich Jeder!\nDu koenntest Ihn fragen, "
+    +"waere er nicht gebanisht. :)\n");
+  AD( ({"ihn"}), BS("Wen IHN ? Meinst Du Jeder? Der ist doch gebanisht. Aus gutem "
+    +"Grund. Wenn irgendwo was wegkommt, dann koennte das Jeder gewesen sein. "
+    +"Das reicht doch, um ihn zu banishen, oder? :)"));
+  AD( ({"hinterberg", "hinterbergteppich", "hinterberg-teppich"}), BS(
+     "Das hier ist ein Teppich der Firma Vorberg. Niemand mit Geschmack wuerde "
+    +"den billigen Abklatsch der Firma Hinterberg kaufen.",78));
+  AD( ({"jeder"}), BS(
+     "Der ist nicht da. Siehst Du ihn etwa?",78));
+  AD( ({"grund", "gruende"}), BS(
+     "Gruende lassen sich immer finden, sei also vorsichtig! :)",78));
+  AD( ({"geschmack"}), BS(
+     "Ueber den laesst sich bekanntlich streiten.",78));
+  AD( ({"teppiche"}), BS(
+     "Du siehst hier einen. Der aber fuellt den ganzen Raum aus.",78));
+  AD( ({"abklatsch"}), BS(
+     "Vorberg-Teppiche sind halt das einzig Wahre!",78));
+  AD( ({"laden"}), BS(
+     "Der Laden ist bekannt unter dem Namen \"SCHRAENKER\". Aber das weisst "
+    +"Du sicher.",78));
+  AD( ({"namen"}), BS(
+     "Der Namen des Ladens ist \"SCHRAENKER\".",78));
+  AD( ({"schraenker"}), BS(
+     "Ja, schau Dich nur um!",78));
+  AD( ({"melodie"}), BS(
+     "Lausche ihr besser.",78));
+  AD( ({"geruch"}), BS(
+     "Sehen kannst Du den nicht. Vielleicht solltest Du Deine Nase benutzen?",78));
+  AD( ({"schritte"}), BS(
+     "Du gehst testweise ein paar Schritte, der Teppich daempft sie tatsaechlich "
+    +"so gut, dass Du nicht das geringste hoeren kannst.",78));
+  AD( ({"schritt"}), BS(
+     "Mach das doch irgendwo, wo es keiner sieht!",78));
+  AD( ({"nase"}), BS(
+     "Du fasst Dir an die Nase. Ein Glueck, sie ist noch da!",78));
+  AD( ({"glueck"}), BS(
+     "Ohne Nase koenntest Du hier gar nichts riechen, stell Dir das nur "
+    +"mal vor!",78));
+  AD( ({"raum", "ort"}), BS(
+     "Du stehst in einem noblen Laden. Mach einfach \"schau\".",78));
+  AD( ({"firma"}), BS(
+     "Du stehst hier in einem Laden. Natuerlich ist das eine Firma, und der "
+    +"Ladeninhaber ist ebenso natuerlich der Firmenbesitzer. Hattest Du denn "
+    +"etwas anderes erwartet?",78));
+  AD( ({"eindruck"}), BS(
+     "Es ist ein Laden. Er sieht nobel aus, Preise wie bei ALDI wird's hier also "
+    +"wohl nicht geben. Und ausserdem ist der erste Eindruck meist der richtige.",78));
+  AD( ({"aldi"}), BS(
+     "NEIN, Du bist hier NICHT bei ALDI !!!",78));
+  AD( ({"preis", "preise", "liste", "preisliste"}), BS(
+     "An der Wand haengt eine grosse Preisliste. Vielleicht solltest Du sie "
+    +"lesen?",78));
+  AD( ({"inhaber", "ladeninhaber", "besitzer", "firmenbesitzer", "verkaeufer"}), BS(
+     "Ist der denn gerade nicht da? Das ist sehr verwunderlich, echt ehrlich.",78));
+  AD( ({"elf", "elfen", "person"}), BS(
+     "Schau Dir halt das Portrait an!",78));
+  AD( ({"darstellung", "darstellungen"}), BS(
+     "Die Bilder zeigen Moebelstuecke, hauptsaechlich Schraenke.",78));
+  AD( ({"reihe"}), BS(
+     "Bei den meisten Bildern handelt es sich um Darstellung von Moebelstuecken. "
+    +"Nur ein einziges Portrait ist dabei, das eine Person zeigt.",78));
+  AD( ({"art"}), BS(
+     "Bei den meisten Bildern handelt es sich um Darstellungen von Moebelstuecken, "
+    +"zumeist von Schraenken. Ein Portrait faellt Dir allerdings auf, das nicht in "
+    +"die Reihe der anderen Bilder passt.",78));
+  AD( ({"bilder"}), BS(
+     "Es gibt mehrere Darstellungen von verschiedenen Moebelstuecken. Zumeist "
+    +"handelt es sich dabei um Schraenke verschiedener Bauweise. Ausserdem ist "
+    +"da noch ein Portrait.",78));
+  AD( ({"bauart", "bauweise", "moebelstueck", "moebelstuecke", "arten"}), BS(
+     "Es gibt Waffenschraenke, Kleiderschraenke, Kuehlschraenke (JA!), Kommoden, "
+    +"sonstige Schraenke (Du kannst ihren Zweck nicht mal erahnen) und einige "
+    +"Truhen, die zwar keine Schraenke im eigentlichen Sinne sind, aber Zeug "
+    +"bringt man darin mit Sicherheit auch unter. Sogar eine Vitrine siehst "
+    +"Du.",78));
+  AD( ({"zweck"}), BS(
+     "Stauraum fuer irgendwelches Zeugs. Das muss es sein. Was fuer einen "
+    +"Zweck koennte es sonst noch geben?",78));
+  AD( ({"vitrine"}), BS("Die Vitrine ist leer, aber das aendert sich sicher, "
+    +"sobald Du eine gekauft hast.",78));
+  AD( ({"kleiderschrank", "kleiderschraenke"}), BS(
+     "Sicher eignet sich solch ein Schrank sehr gut, um darin Ruestungen "
+    +"aufzubewahren.",78));
+  AD( ({"waffenschrank", "waffenschraenke"}), BS(
+     "In einem Waffenschrank kannst Du eine Menge verschiedener Waffen "
+    +"unterbringen, und der Vorteil dabei ist, dass sie dann auch sauber "
+    +"sortiert im Schrank haengen.",78));
+  AD( ({"kommoden"}), BS(
+     "Die kann man nicht nur in einen Flur stellen, sondern auch in jeden "
+    +"anderen Raum!",78));
+  AD( ({"flur"}), BS(
+     "Die meisten Haeuser haben einen.",78));
+  AD( ({"haeuser"}), BS(
+     "HIER ???",78));
+  AD( ({"menge"}), BS(
+     "Mehr als Du brauchst, es sei denn Du bist Sammler.",78));
+  AD( ({"zeugs", "dinge"}), BS(
+     "Dinge halt.",78));
+  AD( ({"waffen"}), BS(
+     "Du kannst sie in einem Waffenschrank unterbringen, wenn Du einen "
+    +"besitzt. Wenn nicht kannst Du ja einen kaufen.",78));
+  AD( ({"waffe"}), BS(
+     "Welche Waffe meinst Du?",78));
+  AD( ({"ruestungen"}), BS(
+     "Du kannst sie in einem Kleiderschrank unterbringen, wenn Du einen "
+    +"besitzt. Wenn nicht koenntest Du Dir einen zulegen.",78));
+  AD( ({"ruestung"}), BS(
+     "Von welcher Ruestung redest Du?",78));
+  AD( ({"vorteil"}), BS(
+     "Jeder ist doch auf seinen persoenlichen Vorteil bedacht.",78));
+  AD( ({"kuehlschrank", "kuehlschraenke"}), BS(
+     "In so einem Kuehlschrank kann man eigentlich alles reinstecken, aber "
+    +"natuerlich bietet er sich besonders fuer Getraenke und Speisen aller "
+    +"Art an. Wie z.B. Schnellhaerter, Broetchen usw.",78));
+  AD( ({"getraenke", "speisen"}), BS(
+     "Das hier ist keine Kneipe. Sowas gibt's hier nicht!",78));
+  AD( ({"kneipe"}), BS(
+     "Das hier ist KEINE Kneipe !!!",78));
+  AD( ({"truhe", "truhen"}), BS(
+     "Es gibt grosse und kleine, aus verschiedenem Material.",78));
+  AD( ({"material"}), BS(
+     "Total verschieden, wirklich.",78));
+  AD( ({"schnellhaerter", "broetchen"}), BS(
+     "Besorg Dir welche, bevor Du sie untersuchen willst!",78));
+  AD( ({"sammler"}), BS(
+     "Bist Du einer?",78));
+  AD( ({"sicherheit"}), BS(
+     "Jo, in so eine Truhe passt viel Zeug rein.",78));
+  AD( ({"zeug"}), BS(
+     "Alles, was Du so mit Dir rumschleppst. Das meiste davon brauchst "
+    +"Du sowieso nie.",78));
+  AD( ({"bild"}), BS(
+     "Es gibt mehrere Darstellungen von verschiedenen Moebelstuecken und ein "
+    +"Portrait. Welches Bild meinst Du?",78));
+  AD( ({"portrait"}), BS(
+     "Es zeigt einen Elf mit strahlend blauen Augen und den typisch spitzen "
+    +"Elfenohren, die frech aus einer beeindruckenden, silbergrauen Loewenmaehne "
+    +"hervorschauen. Der Spitzhut, der ihm auf dem Kopf sitzt, laesst darauf "
+    +"schliessen, dass es sich um einen Zauberer handelt. Unten in der rechten "
+    +"Bildecke ist eine Widmung angebracht.",78));
+  AD( ({"sinn", "sinne"}), BS(
+     "Schraenke und Truhen sehen schon etwas veschieden aus.",78));
+  AD( ({"swift"}), BS(
+     "Siehst Du ihn hier etwa gerade?",78));
+  AD( ({"bildecke"}), BS(
+     "Unten in der rechten Bildecke ist eine Widmung angebracht.",78));
+  AD( ({"augen"}), BS(
+     "Der Elf auf dem Portrait hat strahlend blaue Augen.",78));
+  AD( ({"widmung"}), BS(
+     "Sieht aus als ob man die sogar lesen kann!",78));
+  AD( ({"maehne", "loewenmaehne"}), BS(
+     "Der Elf auf dem Bild hat eine silbergraue Loewenmaehne.",78));
+  AD( ({"zauberer", "zauberhut", "zaubererhut", "spitzhut"}), BS(
+     "Der Zauberhut macht den Elfen auf dem Portrait erst zu einem waschechten "
+    +"Zauberer.",78));
+  AD( ({"ohren", "elfenohren"}), BS(
+     "Die Ohren des Elfen auf dem Portrait sind so spitz, wie man das von "
+    +"einem Elfen erwartet.",78));
+  ARD( ({"widmung"}),
+     "Dank an Geordi fuer diese grossartige Geschaeftsidee.\n"
+    +"Moegen Dir Deine Zauber gelingen und die Spellpunkte niemals ausgehen.\n"
+    +"(Darunter hat jemand unterschrieben, aber das Gekrakel kannst Du nicht "
+    +"identifizieren)\n");
+  AD( ({"unterschrift"}), BS(
+     "Meinst Du das Gekrakel?",78));
+  AD( ({"rotfaerbung", "farbe"}), BS(
+     "Das Holz hat eine leicht roetliche Farbe.",78));
+  AD( ({"ruhe"}), BS(
+     "Aaaahhhhh. Ruhe tut gut. Und dank der leisen Melodie entspannst Du "
+    +"Dich allmaehlich.",78));
+  AD( ({"chaosdaemon"}), BS(
+     "Keiner da. Weder Swift noch ein anderer. Glueck fuer Dich!",78));
+  AD( ({"sachen"}), BS(
+     "Was auch immer.",78));
+  AD( ({"klaue"}), BS(
+     "Wahrscheinlich kann Swift damit andere Sachen besser erledigen als "
+    +"schreiben. Schliesslich ist er ein Chaosdaemon.",78));
+  AD( ({"gekrakel"}), BS(
+     "Du nimmst Dir Zeit und schaust die Unterschrift doch nochmal in aller "
+    +"Ruhe an. Endlich kommt Dir die Erleuchtung. Das soll \"Swift\" heissen. "
+    +"Mann, hat der ne Klaue! (Naja, bei einem Chaosdaemonen wundert das "
+    +"eigentlich nicht)",78));
+  AD( ({"erleuchtung"}), BS(
+     "Sie fand in Deinem Kopf statt. Den muesstet Du schon oeffnen, wenn Du "
+    +"die Erleuchtung physisch untersuchen moechtest. Und das kann sich "
+    +"negativ auf Deine Gesundheit auswirken. Lass es lieber!",78));
+  AD( ({"gesundheit"}), BS(
+     "Mit Kopf bist Du gesund. Das ist gewiss.",78));
+  AD( ({"wand", "waende"}), BS(
+     "Die Waende bestehen aus einem feinen, exotischen Holz mit leichter "
+    +"Rotfaerbung. Sieht sehr edel aus. Mehrere Bilder wurden an den Waenden "
+    +"aufgehaengt und verteilen sich gleichmaessig ueber den Raum.",78));
+  AD( ({"kopf"}), BS(
+     "Ein Glueck, er ist noch dran. Genauso wie bei dem Elfen auf dem "
+    +"Portrait!",78));
+  AD( ({"schrank", "schraenke"}), BS(
+     "Es gibt verschiedene Arten von Schraenken.",78));
+  AD( ({"zeichnung"}), BS(
+     "Es gibt mehrere davon.",78));
+  AD( ({"zeichnungen"}), BS(
+     "Sie zeigen - teilweise skizzenhaft - den Aufbau verschiedener "
+    +"Moebelstuecke - vorzugsweise Schraenke.",78));
+  AD( ({"aufbau", "zusammensetzung"}), BS(
+     "Mit dem Aufbau ist die Zusammensetzung gemeint - und umgekehrt.",78));
+  AD( ({"holz", "hoelzer"}), BS(
+     "Schon hier im Raum wurde mit verschiedensten Hoelzern gearbeitet, "
+    +"ihr edler Duft vermischt sich und bietet einen erhebenden Geruch. "
+    +"Leider sind die meisten Holzarten so exotisch, dass Du beim besten "
+    +"Willen nicht sagen kannst, um was fuer einen Baum es sich da mal "
+    +"gehandelt hat.",78));
+  AD( ({"holzart", "holzarten"}), BS(
+     "Sehr exotische Holzarten. Du bist kein Experte fuer sowas, und "
+    +"vermutlich hast Du auch keine Lust, einer zu werden.",78));
+  AD( ({"experte"}), BS(
+     "Keiner da.",78));
+  AD( ({"lust"}), BS(
+     "Wenn jemand seine Lust untersucht/ueberprueft, bekundet er damit "
+    +"eigentlich schon in Wirklichkeit seine Unlust.",78));
+  AD( ({"unlust"}), BS(
+     "Tja, Unlust kennzeichnet den Faulpelz.",78));
+  AD( ({"faulpelz"}), BS(
+     "Du bist gemeint!",78));
+  AD( ({"wirklichkeit", "realitaet"}), BS(
+     "Die Wirklichkeit ist die Realitaet - und andersrum. Meistens "
+    +"jedenfalls.",78));
+  AD( ({"meistens"}), BS(
+     "Hier schon.",78));
+  AD( ({"hier"}), BS(
+     "Hier halt!",78));
+  AD( ({"baum", "baeume"}), BS(
+     "Hier gibt es keine Baeume. Die wurden bereits alle verarbeitet.",78));
+  AD( ({"duft"}), BS(
+     "Rieche halt mal!",78));
+  AD( ({"wille", "willen"}), BS(
+     "Dein Wille ist stark...",78));
+  AD( ({"decke"}), BS(
+     "Maechtige Balken bilden die Decke. Es koennte sich um Eiche handeln, aber "
+    +"genausogut koennte es irgendeine aehnliche, exotische Holzart sein. "
+    +"Ein herrlicher Kronleuchter haengt von der Mitte herab.",78));
+  AD( ({"eiche", "eichenbalken"}), BS(
+     "Es ist nicht sicher, dass es sich um Eichenbalken handelt, aber Balken "
+    +"sind's auf jeden Fall.",78));
+  AD( ({"fall"}), BS(
+     "Echt ehrlich.",78));
+  AD( ({"balken"}), BS(
+     "Sie bilden die Decke. Ein herrlich anzusehender Kronleuchter haengt "
+    +"daran herab.",78));
+  AD( ({"kronleuchter"}), BS(
+     "Er haengt von der Decke herab und muss ein Vermoegen wert sein. Reines "
+    +"Bergkristall mit einer Unzahl von Kerzen. Was das fuer ein Aufwand sein "
+    +"muss, die immer am Brennen zu halten.",78));
+  AD( ({"vermoegen"}), BS(
+     "Mehr als Du tragen kannst.",78));
+  AD( ({"bergkristall"}), BS(
+     "Nur Bergkristall hat diesen Glanz.",78));
+  AD( ({"brennen"}), BS(
+     "Die Kerzen flackern munter vor sich hin.",78));
+  AD( ({"unzahl"}), BS(
+     "1, 2, ...viele!",78));
+  AD( ({"glanz"}), BS(
+     "Herrlich!",78));
+  AD( ({"mitte"}), BS(
+     "Von der Mitte der Decke haengt ein Kronleuchter herab.",78));
+  AD( ({"kerzen"}), BS(
+     "Dir faellt auf, dass die Kerzen sich gar nicht verbrauchen. Es muss "
+    +"sich um magische Kerzen handeln.",78));
+  AD( ({"aufwand"}), BS(
+     "Entsetzt denkst Du . o O (Das schlaegt sich bestimmt auf den Preis nieder.)",78));
+  AD( ({"ecken"}), BS(
+     "In den Ecken entdeckst Du kleine, edle Lautsprecher, aus denen die "
+    +"Melodie zu kommen scheint.",78));
+  AD( ({"lautsprecher"}), BS(
+     "Du wusstest gar nicht, dass sowas im MorgenGrauen schon erfunden wurde.",78));
+  AD( ({"morgengrauen"}), BS(
+     "Tust Du das nicht schon die ganze Zeit?",78));
+
+  AddSmells(SENSE_DEFAULT, "Es riecht hier angenehm nach verschiedenen "
+                          +"Hoelzern.\n");
+  AddSmells(({"hoelzer", "holz", "holzarten"}), BS("Du kannst nicht sagen, "
+    +"um was fuer Holzarten es sich handelt, aber der Geruch laesst auf viele "
+    +"verschiedene schliessen. Eine Trennung des Geruchs ist Dir nicht moeglich.") );
+  AddSmells(({"geruch", "gerueche"}), BS("Es riecht nach verschiedenen "
+    +"Holzarten.") );
+
+  AddSounds(SENSE_DEFAULT, "Ganz leise hoerst Du eine angenehme, beruhigende "
+                          +"Melodie.\n");
+  AddSounds(({"melodie"}), "Sie scheint von allen Ecken des Ladens zu kommen.\n");
+
+  AddItem( LADEN("verkaeufer"), REFRESH_DESTRUCT);
+
+  AddCmd(({"oeffne"}), "kopf_oeffnen");
+  AddCmd(({"lies", "les", "lese"}), "preisliste_lesen");
+  AddCmd(({"idee"}), "idee_loggen");
+  AddCmd(({"trenn", "trenne"}), "gerueche_trennen");
+  AddCmd(({"werd", "werde"}), "experte_werden");
+  AddCmd(({"zaehl", "zaehle"}), "kerzen_zaehlen");
+
+  AddSpecialExit("osten", "rausgehen");
+}
+
+int kerzen_zaehlen(string str)
+{
+  notify_fail("Was moechtest Du zaehlen?\n");
+  if(!str) return 0;
+  if(str!="kerzen") return 0;
+  write("1..2..viele.\n");
+  return 1;
+}
+
+int rausgehen(string dir)
+{
+  return TP->move(RAUM_VOR_LADEN, M_GO, 0,
+    "verlaesst den Laden", "kommt aus dem Laden");
+}
+
+varargs string GetExits( object viewer )
+{
+  return "Der Ausgang befindet sich im Osten.\n";
+}
+
+int kopf_oeffnen(string str)
+{
+  notify_fail("Was moechtest Du oeffnen?\n");
+  if(!str) return 0;
+  if(str!="kopf" && str!="meinen kopf" && str!="deinen kopf")
+    return 0;
+  write( BS("Neugierig, wie Du bist, greifst Du Dir an den Kopf. Da faellt Dir "
+    +"ein, dass das oeffnen Deines Kopfes evtl. eine Copyright-Verletzung "
+    +"darstellen koennte, Du heisst ja schliesslich nicht Hannibal. Also laesst "
+    +"Du es dann doch bleiben."));
+  return 1;
+}
+
+int preisliste_lesen(string str)
+{
+  notify_fail("Was moechtest Du lesen?\n");
+  if(!str) return 0;
+  if(str!="preisliste" && str!="liste")
+    return 0;
+  TP->More( read_file(LADEN("preisliste.txt")) );
+  return 1;
+}
+
+int idee_loggen(string str)
+{
+  string txt;
+  str=TP->_unparsed_args();
+  txt=BS(str, 78, CAP(geteuid(TP))+"............."[0..12], BS_INDENT_ONCE);
+  write_file( LADEN(IDEEN_LOG), txt+"\n");
+  write("Vielen Dank fuer Deine Idee!\n");
+  return 1;
+}
+
+int gerueche_trennen(string str)
+{
+  notify_fail("Was willst Du trennen?\n");
+  if(!str) return 0;
+  if(str!="geruch" && str!="gerueche")
+    return 0;
+  write("Du versuchst es, aber Du schaffst es einfach nicht.\n");
+  return 1;
+}
+
+int experte_werden(string str)
+{
+  notify_fail("Was willst Du werden?\n");
+  if(!str) return 0;
+  if(str!="experte" && str!="fachmann")
+    return 0;
+  write("Tja, so einfach geht das nicht, aber das haettest Du Dir denken koennen.\n");
+  return 1;
+}
diff --git a/d/seher/haeuser/moebel/schrankladen.h b/d/seher/haeuser/moebel/schrankladen.h
new file mode 100644
index 0000000..1fc76a9
--- /dev/null
+++ b/d/seher/haeuser/moebel/schrankladen.h
@@ -0,0 +1,36 @@
+#include "moebel.h"
+
+#define SCHRANKMASTER2 "/players/swift/workroom"
+
+#include <language.h>
+#include <defines.h>
+#include <combat.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <rooms.h>
+
+#define BS   break_string
+#define ENV(x)  environment(x)
+#define ETO environment(this_object())
+#define ETP environment(this_player())
+#define TP   this_player()
+#define TO   this_object()
+
+#define SP SetProp
+#define QP QueryProp
+#define AD AddDetail
+#define ARD AddReadDetail
+
+#define SUID seteuid(getuid(this_object()))
+
+//#define CAP(x) capitalize(x)
+#define LOWER(x) lower_case(x)
+
+#define CLONEME(x) clone_object(x)->move(ME)
+#define CLONEOB(x) clone_object(x)
+#define CLONE(x,y) clone_object(x)->move(y)
+#define IST_ZAHL(x) (string)to_int(x)==x
+
+#define RIGHT(x,y) (y>sizeof(x)?x:extract(x,sizeof(x)-y,sizeof(x)-1))
+#define LEFT(x,y) (y>sizeof(x)?x:extract(x,0,y-1))
diff --git a/d/seher/haeuser/moebel/schrankmaster.c b/d/seher/haeuser/moebel/schrankmaster.c
new file mode 100644
index 0000000..c602ddc
--- /dev/null
+++ b/d/seher/haeuser/moebel/schrankmaster.c
@@ -0,0 +1,233 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Schrankmaster
+// Letzte Aenderung:    19.03.2001
+// Magier:              wurzel
+//--------------------------------------------------------------------------------
+/* Changelog:
+   * 21.06.2007, Zesstra
+           call_out ohne Verzoegerung beim Updaten durch eines mit 2s Verzoegerung ersetzt.
+*/
+#pragma strong_types,rtt_checks
+
+inherit "/std/room";
+
+#include <properties.h>
+#include <language.h>
+#include "schrankladen.h"
+
+mapping schraenke;
+string *cnts;
+int update_laeuft;
+int reset_laeuft;
+
+protected void create() 
+{
+  ::create();
+  SP(P_INT_SHORT, "Irgendwo und Nirgends");
+  SP(P_INT_LONG,
+     "Dies ist der Master-Raum fuer den SCHRAENKER.\n"
+    +"Hier merkt sich August die Anzahl der verkauften Container.\n"
+    +"Davon sind momentan @@ANZAHL@@ registrierte im Umlauf!\n"
+    +"Folgende Befehle sind verfuegbar:\n"
+    +"  schrankliste.........Listet registrierte, im Spiel befindliche Container.\n"
+    +"  schrankupdate........Fuehrt ein Update aller registrierten Container durch.\n");
+
+  SP(P_LIGHT, 1);
+  SP(P_INDOORS, 1);
+  if( SCHRANKMASTER2->QueryProp("schraenke") )  // Der Sicherungsraum hat Daten?
+    schraenke=SCHRANKMASTER2->QueryProp("schraenke");  // Daten holen...
+  else
+    schraenke=([]);
+  SetProp("schraenke", schraenke );
+
+  update_laeuft=0;
+
+  // unnoetig, SCHRANKMASTER2 ist jetzt schon geladen, s.o.
+  //load_object(SCHRANKMASTER2);
+  set_next_reset(3600); // Reset 1 x pro Std.
+
+  AddCmd(({"schrankliste"}), "Schrankliste");
+  AddCmd(({"schrankupdate"}), "SchrankUpdate");
+}
+
+public int clean_up(int ref) {return 0;}   // verhindert Ausswappen des Raumes
+
+public varargs string GetExits( object viewer ) 
+{
+  return "";
+}
+
+string ANZAHL()
+{
+  return to_string( sizeof(schraenke) );
+}
+
+
+// cnt = Object des Containers
+// ver = Version des Containers (VERSION_STD+VERSION_OBJ von std.cont. + objekt...)
+// seher=Der Besitzer/Kaeufer
+// standort=Raum in dem der Container steht
+void RegisterCnt(mixed cnt, mixed ver, mixed seher, mixed standort)
+{
+  if (objectp(standort)) standort=object_name(standort);
+  if( !objectp(cnt) || !stringp(ver) || !stringp(seher) || !stringp(standort) )
+  {
+    log_file("swift/schraenker/bugs.log", "schrankmaster (RegisterCnt) *"
+      +to_string(cnt)+"*"+to_string(ver)+"*"+to_string(seher)+"*"
+      +to_string(standort)+"*\n");
+    return;
+  }
+  m_delete(schraenke,object_name(cnt));
+  schraenke+=([object_name(cnt): ver; seher; to_string(standort)]);
+  SetProp("schraenke", schraenke );
+  SCHRANKMASTER2->SetProp("schraenke", schraenke );
+}
+
+void RemoveCnt(mixed cnt, mixed seher)
+{
+  if( !objectp(cnt) || !stringp(seher) )
+  {
+    log_file("swift/schraenker/bugs.log", "schrankmaster (RemoveCnt) *"
+      +to_string(cnt)+"*"+to_string(seher)+"*\n");
+    return;
+  }
+  schraenke-=([object_name(cnt)]);
+  SetProp("schraenke", schraenke );
+  SCHRANKMASTER2->SetProp("schraenke", schraenke );
+}
+
+string fn(string was)
+{
+  string str,*strs;
+  strs=old_explode( was, "/" );
+  str=strs[sizeof(strs)-1];
+  strs=old_explode( str, "#" );
+  str=strs[0];
+  return str;
+}
+
+int Schrankliste()
+{
+  int i;
+  string str, *strs;
+  object cnt;
+  if(update_laeuft && !reset_laeuft) // Nicht waehrend eines Updates der Schraenke!
+  {
+    write("Update wird durchgefuehrt. Bitte bis zum Ende warten!\n");
+    return 1;
+  }
+  str="----------------------------------------------------------------------\n";
+  strs=m_indices(schraenke);
+  for(i=0;i<sizeof(strs);i++)
+  {
+    if( cnt=find_object(strs[i]) )
+    {
+      str+=(fn(object_name(cnt))+"....................")[0..20]+" ";
+      str+=schraenke[strs[i], 0]+" ";
+      str+=(schraenke[strs[i], 1]+"....................")[0..20]+" ";
+      str+=fn(schraenke[strs[i], 2])+"\n";
+    }
+    else
+    {
+      str+="Nicht auffindbaren Container aus Liste geloescht!\n";
+      schraenke-=([strs[i]]);
+      SetProp("schraenke", schraenke );
+      SCHRANKMASTER2->SetProp("schraenke", schraenke );
+    }
+  }
+  str+="----------------------------------------------------------------------\n";
+  if( TP && present(TP) ) //wurzel 19.03.01 
+    TP->More(str);
+  return 1;
+}
+
+void Update_naechster_Schrank()
+{
+  int i;
+  object cnt, cnt_neu, *inv;
+  if( sizeof(cnts) )    // Es gibt noch Schraenke zum abarbeiten...
+  {
+    cnt=find_object(cnts[0]); // Schrank auswaehlen + Objekt suchen
+    if(cnt)
+    {
+      // Autoloadertruhe ist etwas spezieller zu updaten... Daher alternativer
+      // Mechanismus: UpdateMe() im Moebelstueck aufrufen. Wenn die Funktion
+      // etwas != 0 liefert, wird angenommen, dass diese Funktion das Update
+      // gemacht hat oder sich in Kuerze darum kuemmert. Sonst wird das alte
+      // Update gemacht.
+      // Das neue Moebelobjekt ist dafuer verantwortlich, sich beim
+      // Schrankmaster zu (de-)registrieren!
+      object oldenv=environment(cnt);
+      if (cnt->UpdateMe())
+      {
+          tell_room(TO, "...Update "+CAP(fn(cnts[0]))
+              +" in " + object_name(oldenv) + "\n" );
+      }
+      else
+      {
+          tell_room(TO, CAP(fn(cnts[0]))
+              +" in " + object_name(oldenv) 
+              + " hat kein UpdateMe()!\n" );
+      }
+    }
+    else
+    {
+      tell_room(TO, "Nicht auffindbaren Container aus Liste geloescht!\n");
+      schraenke-=([cnts[0]]);
+    }
+    cnts-=({cnts[0]});   // Schrank aus Liste nehmen
+    call_out("Update_naechster_Schrank", 2);  // Nur 1 Schrank-Update pro heart_beat!
+  }
+  else  // Alle Schraenke abgearbeitet:
+  {
+    tell_room(TO, "*** Update abgeschlossen.\n");
+    SetProp("schraenke", schraenke );
+    SCHRANKMASTER2->SetProp("schraenke", schraenke );
+    update_laeuft=0;
+  }
+}
+
+int SchrankUpdate()
+{
+  if(update_laeuft)   // Nicht waehrend eines Updates der Schraenke!
+  {
+    write("Update wird durchgefuehrt. Bitte bis zum Ende warten!\n");
+    return 1;
+  }
+  update_laeuft=1;    // Ab sofort laeuft Update!
+  cnts=m_indices(schraenke);
+  tell_room(TO, "*** Update wird gestartet...\n");
+  Update_naechster_Schrank();
+  return 1;
+}
+
+
+void reset()
+{
+  reset_laeuft=1;
+  set_next_reset(3600); // Reset 1 x pro Std.
+  ::reset();
+  // Wirft Schraenke aus der Liste, die aus welchem Grund auch immer nicht mehr existieren
+  Schrankliste();
+  if( !(schraenke==QueryProp("schraenke")==SCHRANKMASTER2->QueryProp("schraenke")) )
+    log_file("swift/schraenker/reset.log", "schrankmaster (reset) "
+      +sizeof(schraenke)+" "
+      +sizeof(QueryProp("schraenke"))+" "
+      +sizeof(SCHRANKMASTER2->QueryProp("schraenke"))+"\n");
+  if( present("Interactive") )
+    tell_room(TO, "*** RESET ***\n");
+  reset_laeuft=0;
+}
+
+/*
+// Veraltete Funktionen, um Fehler abzufangen. Kann nach dem naechsten Reboot
+// geloescht werden !!!
+void AddCnt(mixed cnt, mixed ver, mixed seher, mixed standort)
+{
+  log_file("swift/schraenker/verloren.log", to_string(cnt)+"\n");
+}
+void EditCnt(mixed cnt, mixed ver, mixed seher, mixed standort)
+{
+  log_file("swift/schraenker/verloren.log", to_string(cnt)+"\n");
+}
+*/
diff --git a/d/seher/haeuser/moebel/spind.c b/d/seher/haeuser/moebel/spind.c
new file mode 100644
index 0000000..7473b10
--- /dev/null
+++ b/d/seher/haeuser/moebel/spind.c
@@ -0,0 +1,208 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Spind
+// Letzte Aenderung:    17.03.2001
+// Magier:              Swift
+//--------------------------------------------------------------------------------
+#pragma strong_types,rtt_checks
+
+#include "schrankladen.h"
+#include "ansi.h"
+inherit LADEN("swift_std_container");
+
+#define VERSION_OBJ "3"
+
+string strall;
+
+protected void create()
+{
+  if (!clonep(TO)) return;
+  swift_std_container::create();
+  SetProp(P_SHORT, "Ein Spind");
+  SetProp(P_LONG, BS(
+     "Dieser Stahlschrank ist vor allem fuer spartanische Raeume gedacht. Ebenso "
+    +"wie sein Gegenstueck, der Kleiderschrank, verfuegt er ueber eine Menge "
+    +"Regale, in denen er riesige Ausruestungsmengen aufnehmen kann.")
+    +"@@cnt_status@@");
+  AddId(({"spind", "schrank", "stahlschrank"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_NAME, "Spind");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_WEIGHT, 5000);         // Gewicht 5 Kg
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 100);     // Mehr sollte nicht sein, lt. Tiamak.
+  SetProp(P_VALUE, 0);             // Kein materieller Wert. Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Wird im Laden zerstoert, falls er verkauft wird.
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
+  SetProp(P_MATERIAL, ({MAT_MISC_METAL, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuchs mal mit: \"skommandos "+QueryProp(P_IDS)[1]+"\" !\n");
+  SetProp("obj_cmd", "sliste [Objekt-Id] [Kleidungs-/Ruestungstyp]\n"
+                    +"  Kleidungs-/Ruestungstypen: Helme, Umhaenge, Ruestungen, Hosen, Schuhe,\n"
+                    +"                             Handschuhe, Guertel, Amulette, Ringe, Koecher,\n"
+                    +"                             Schilde, Sonstiges\n");
+  AD(({"regal", "regale"}), BS("Durch die Regale kommt etwas Ordnung in die "
+    +"vielen Sachen, die man in den Schrank stecken kann. Du kannst Dir z.B. "
+    +"eine Liste bestimmter Kleidungs-/Ruestungstypen des Schrankinhaltes "
+    +"mit dem Befehl \"sliste\" anschauen. (Das geht natuerlich nur bei "
+    +"offenem Schrank!"));
+
+  AddCmd(({"sliste"}), "auflistung");
+}
+
+varargs int PreventInsert(object ob)
+{
+  // Nur Ruestung/Kleidung hat in diesem Container was verloren!
+  if ((ob->IsClothing() || ob->QueryProp(P_ARMOUR_TYPE))
+      && !ob->QueryProp(P_WEAPON_TYPE) )
+  {
+    return ::PreventInsert(ob);
+  }
+
+  write( BS("In "+name(WEN,1)+" kannst Du nur Ruestungs-/Kleidungsstuecke legen!"));
+  return 1;
+}
+
+int auflistung2(string was, string nf_str1, string nf_str2, int alles)
+{
+  string obshort, suche, *strs;
+  object *obs;
+  int i;
+  mapping suchobs;
+  switch(was)
+  {
+    case "helme"      : suche=AT_HELMET; break;
+    case "umhaenge"   : suche=AT_CLOAK; break;
+    case "ruestungen" : suche=AT_ARMOUR; break;
+    case "hosen"      : suche=AT_TROUSERS; break;
+    case "schuhe"     : suche=AT_BOOT; break;
+    case "handschuhe" : suche=AT_GLOVE; break;
+    case "guertel"    : suche=AT_BELT; break;
+    case "amulette"   : suche=AT_AMULET; break;
+    case "ringe"      : suche=AT_RING; break;
+    case "koecher"    : suche=AT_QUIVER; break;
+    case "schilde"    : suche=AT_SHIELD; break;
+    case "sonstiges"  : suche=AT_MISC; break;
+    default           : write("Fehler: Ungueltiger Kleidungs-/Ruestungstyp. "
+                             +"Folgende Typen gibt es:\n"+nf_str1+nf_str2);
+                        return 1; break;
+  }
+  obs=all_inventory();
+  suchobs=([]);
+  for(i=0;i<sizeof(obs);i++)
+  {
+    obshort=obs[i]->QueryProp(P_SHORT);
+    if(obshort=="Nichts besonderes") // keine P_SHORT ?
+      obshort=obs[i]->Name(WER);     //   Hoffen wir mal dass das Obj. nen Namen hat.
+    if(obs[i]->QueryProp(P_ARMOUR_TYPE)==suche)    // Gesuchter ArmourType gefunden...
+    {
+      if(suchobs[obshort])                         // P_SHORT des Obj. schon im mapping?
+      {
+        if( obs[i]->QueryProp(P_AMOUNT) )
+          suchobs[obshort]+=obs[i]->QueryProp(P_AMOUNT); //   Dann den Zaehler um Amount hoch!
+        else
+          suchobs[obshort]+=1;                     //   Dann den Zaehler um 1 hoch!
+      }
+      else
+      {
+        if( obs[i]->QueryProp(P_AMOUNT) )
+          suchobs+=([obshort: obs[i]->QueryProp(P_AMOUNT)]);
+        else
+          suchobs+=([obshort: 1]);
+      }
+    }
+  }
+  strs=m_indices(suchobs);
+  if(sizeof(strs))
+  {
+    if(!alles)
+    {
+      strall+=Name(WER,1)+" enthaelt folgende "+CAP(was)+":\n";
+      strall+="------------------------------------------------------------\n";
+    }
+    else
+      strall+=ANSI_BOLD+"=== "+CAP(was)+":\n"+ANSI_NORMAL;
+    for(i=0;i<sizeof(strs);i++)
+    {
+      if(suchobs[strs[i]] > 1)
+        strall+=strs[i]+". ("+suchobs[strs[i]]+")\n";
+      else
+        strall+=strs[i]+".\n";
+    }
+  }
+  else
+    if(!alles)
+      strall+=Name(WER,1)+" enthaelt keine "+CAP(was)+"!\n";
+  return 1;
+}
+
+int auflistung(string str)
+{
+  string *strs, ob_id, was, nf_str1,nf_str2;
+  strall="";
+  nf_str1="   (Helme, Umhaenge, Ruestungen, Hosen, Schuhe, Handschuhe,\n"
+         +"    Guertel, Amulette, Ringe, Koecher, Schilde, Sonstiges)\n";
+  nf_str2="Syntax: sliste [Objekt-Id] [Kleidungs-/Ruestungstyp|\"alles\"]\n"
+         +"Bsp.:   sliste "+QueryProp(P_IDS)[1]+" "+"helme\n"
+         +"        sliste "+QueryProp(P_IDS)[1]+" alles\n";
+  notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str2);
+  if(!str) return 0;
+  if(present(str)==TO)   // Ueberpruefe, ob dieses Objekt gemeint ist!
+  {
+    write("Fehler: Es fehlt ein Kleidungs-/Ruestungstyp. Folgende Typen gibt es:\n"
+         +nf_str1+nf_str2);
+    return 1;
+  }
+  strs=old_explode(str, " ");
+  notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"
+             +nf_str2);
+  if( sizeof(strs) < 2 ) return 0;   // Nur 1 Par. und der ist nicht die Objekt-Id
+  if( sizeof(strs) == 2 )   // Anzahl Par. = 2
+  {
+    ob_id=strs[0];
+    was=strs[1];
+    if( IST_ZAHL(was) )     // Objekt-Id z.B. mit "schrank 2" angesprochen?
+      if(present(ob_id+" "+was)==TO)   // Falls dieses Objekt gemeint ist und kein 3. Par!
+      {
+        write("Fehler: Es fehlt ein Kleidungs-/Ruestungstyp. Folgende Typen gibt es:\n"
+              +nf_str1+nf_str2);
+        return 1;
+      }
+      else
+        return 0;
+  }
+  else if( sizeof(strs) == 3 )
+  {
+    ob_id=strs[0]+" "+strs[1];
+    was=strs[2];
+  }
+  else        // Anzahl erforderlicher Parameter ueberschritten!
+    return 0;
+  if(present(ob_id)!=TO)   // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+    return 0;
+  if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN)
+  {
+    write("Dazu muesste "+name(WER,1)+" geoeffnet sein.\n");
+    return 1;
+  }
+//--- Hier ist Parameterpruefung zu Ende.
+  was=LOWER(was);
+  if(was!="alles")
+    auflistung2(was, nf_str1, nf_str2, 0);
+  else
+  {
+    auflistung2("helme", nf_str1, nf_str2, 1);
+    auflistung2("umhaenge", nf_str1, nf_str2, 1);
+    auflistung2("ruestungen", nf_str1, nf_str2, 1);
+    auflistung2("hosen", nf_str1, nf_str2, 1);
+    auflistung2("schuhe", nf_str1, nf_str2, 1);
+    auflistung2("handschuhe", nf_str1, nf_str2, 1);
+    auflistung2("guertel", nf_str1, nf_str2, 1);
+    auflistung2("amulette", nf_str1, nf_str2, 1);
+    auflistung2("ringe", nf_str1, nf_str2, 1);
+    auflistung2("koecher", nf_str1, nf_str2, 1);
+    auflistung2("schilde", nf_str1, nf_str2, 1);
+    auflistung2("sonstiges", nf_str1, nf_str2, 1);
+  }
+  TP->More(strall);
+  return 1;
+}
diff --git a/d/seher/haeuser/moebel/swift_std_container.c b/d/seher/haeuser/moebel/swift_std_container.c
new file mode 100644
index 0000000..90f4986
--- /dev/null
+++ b/d/seher/haeuser/moebel/swift_std_container.c
@@ -0,0 +1,611 @@
+//----------------------------------------------------------------------------
+// Name des Objects:    Standard_Container
+// Letzte Aenderung:    21.03.2001
+// Magier:              Swift
+// Dieses File wird anstelle von /std/container inherited, so dass dem Objekt
+// erweiterte Moeglichkeiten zur Verfuegung stehen.
+//
+// 07.12.2005 (Seleven): Kurzbeschreibung ausblendbar gemacht
+// 30.06.2007 (Zesstra): create der BP wird nicht mehr abgebrochen. Auf die
+//   Art wird es dem Nutzer dieses Standardobjekt ueberlassen, ob er die BP
+//   initialisieren will oder nicht.
+// 01.07.2007 (Zesstra): Update der Moebel ruft erstmal UpdateMe() in den
+//   Moebeln auf. Wenn das != 0 liefert, ist UpdateMe() fuer das Update
+//   zustaendig und muss das machen.
+//   Ausserdem kleine Aenderung im Registrieren (nimmt auch Obekte als
+//   Standort)
+//   Weiterhin Moebel umgezogen nach /p/seher/moebel/ und in diesem File
+//   GetOwner() definiert, welches "Swift" zurueckgibt. Wer hiervon erbt,
+//   sollte damit also sein eigenes GetOwner() definieren.
+//   glob. Variablen auf 'nosave' geaendert.
+//----------------------------------------------------------------------------
+
+
+// Fuer Testzwecke ist es moeglich, dem entsprechenden Objekt die Property
+// "test" auf 1 zu setzen, damit ist dann so ziemlich alles erlaubt.
+// Man sollte die Property aber unbedingt wieder entfernen, sonst ist es z.B.
+// moeglich, einen Schrank von Seherhaus A nach Seherhaus B oder sogar hinaus
+// zu verschieben !!!
+
+#pragma strong_types,save_types,rtt_checks
+
+#include <container.h>
+inherit "/std/container";
+
+#include "schrankladen.h"
+
+#define VERSION_STD "9"
+
+nosave int hauserlaubnis;
+nosave string zweitieerlaubnis;
+nosave string *erlaubnis;
+
+int erlaubt();
+
+protected void create()
+{
+  //if (!clonep(TO)) return;
+  ::create();
+  SetProp(P_SHORT, "Ein Standard_Container von Swift");
+  SetProp(P_CNT_STATUS,CNT_STATUS_CLOSED);
+  SetProp(P_TRANSPARENT, 0);
+  SetProp(P_LOG_FILE,"seleven/schraenker"); // Seleven 06.04.2006
+  SetProp("cnt_hausfreunde", ({}) );
+  SetProp("cnt_erlaubnis", ({}) );
+  SetProp("cnt_zweitieerlaubnis", "" );
+  SetProp("cnt_version_std", VERSION_STD);
+
+  hauserlaubnis=0;
+  zweitieerlaubnis="";
+  erlaubnis=({});
+
+  set_next_reset(3600); // 1 x pro Std. Reset!
+
+  AddCmd(({"oeffne"}), "oeffnen");
+  AddCmd(({"schliess", "schliesse"}), "schliessen");
+  AddCmd(({"serlaube"}), "erlaubnis");
+  AddCmd(({"skommandos"}), "skommandos");
+  AddCmd(({"verschieb", "verschiebe"}), "verschieben");
+  AddCmd(({"zertruemmer", "zertruemmere"}), "zertruemmern");
+  //Seleven 07.12.2005:
+  AddCmd(({"sverstecke","sversteck"}),"verstecken");
+  AddCmd(({"shole","shol"}),"zeigen");
+}
+
+// Zesstra, 1.7.07, fuers Hoerrohr
+string GetOwner() {return "swift";}
+
+static string QueryHausbesitzer()
+{
+  return CAP(to_string(ETO->QueryOwner()));    // z.B.: Swift
+}
+
+
+
+static string QueryTP()
+{
+  return CAP(geteuid(TP));  // z.B.: Swift
+}
+
+int oeffnen(string str)
+{
+  notify_fail("Was moechtest Du oeffnen?\n");
+  if(!str) return 0;
+  if(present(str)!=TO)  // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+      return 0;
+  if(QueryProp(P_CNT_STATUS)==CNT_STATUS_OPEN)
+  {
+    write(Name(WER,1)+" ist doch schon geoeffnet!\n");
+    return 1;
+  }
+  if( !erlaubt() )
+  {
+    write("Ein magisches Kraftfeld verhindert das Oeffnen "+name(WESSEN,1)+"!\n");
+    return 1;
+  }
+  write("Du oeffnest "+name(WEN)+".\n");
+  say(TP->Name(WER)+" oeffnet "+name(WEN)+".\n", ({TP}) );
+  SetProp(P_CNT_STATUS,CNT_STATUS_OPEN);
+  SetProp(P_TRANSPARENT, 1);
+  return 1;
+}
+
+int schliessen(string str)
+{
+  notify_fail("Was moechtest Du schliessen?\n");
+  if(!str) return 0;
+  if(present(str)!=TO)   // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+    return 0;
+  if(QueryProp(P_CNT_STATUS)==CNT_STATUS_CLOSED)
+  {
+    write(Name(WER,1)+" ist doch schon geschlossen!\n");
+    return 1;
+  }
+  if( !erlaubt() )
+  {
+    write("Ein magisches Kraftfeld verhindert das Schliessen "+name(WESSEN,1)+"!\n");
+    return 1;
+  }
+  write("Du schliesst "+name(WEN)+".\n");
+  say(TP->Name(WER)+" schliesst "+name(WEN)+".\n", ({TP}) );
+  SetProp(P_CNT_STATUS,CNT_STATUS_CLOSED);
+  SetProp(P_TRANSPARENT, 0);
+  return 1;
+}
+
+string cnt_status()
+{
+  return Name(WER,1)+" ist "
+    +(QueryProp(P_CNT_STATUS)?"geschlossen":"geoeffnet")+".\n";
+}
+
+int erlaubt()
+{
+  if( QueryProp("test") ) return 1;       // Zu Testzwecken!
+  if( QueryHausbesitzer()=="0" )          // Schrank steht nicht in einem Seherhaus!
+  {
+    tell_room(ETO, Name(WER,1)+" sollte nicht ausserhalb eines Seherhauses stehen.\n"
+      +"Bitte Mail an "+VERANTWORTLICHER_MAGIER+"!\n");
+    return 0;
+  }
+  if( QueryTP() == QueryHausbesitzer() )  // Der Besitzer des Hauses selbst
+    return 1;
+  if(hauserlaubnis)
+  {
+    if( member(SHVERWALTER->HausProp(LOWER(QueryHausbesitzer()), 2),
+        QueryTP()) != -1 )                // Hausfreunde
+      return 1;
+  }
+  if(zweitieerlaubnis!="")                // Zweities
+  {
+    if( TP->QueryProp(P_SECOND) == zweitieerlaubnis )
+      return 1;
+  }
+  if( sizeof(erlaubnis) )
+  {
+    if( member(erlaubnis, QueryTP()) != -1 ) // Sonstige Personen die berechtigt sind
+      return 1;
+  }
+  return 0;
+}
+
+void cnt_open()
+{
+  SetProp(P_CNT_STATUS,CNT_STATUS_OPEN);
+  SetProp(P_TRANSPARENT, 1);
+}
+
+int erlaubnis_liste()
+{
+  int i;
+  string *strs,str;
+  if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
+  {
+    write( BS("Nur "+QueryHausbesitzer()+" darf Berechtigungen "
+      +name(WESSEN,1)+" abfragen!"));
+    return 1;
+  }
+  write("=============================================================================\n");
+  if(hauserlaubnis)
+  {
+    strs=SHVERWALTER->HausProp(LOWER(QueryHausbesitzer()), 2);
+    write(BS("Folgende Freunde Deines Hauses duerfen "+name(WEN,1)
+      +" oeffnen/schliessen:"));
+    str="";
+    if(sizeof(strs))
+    {
+      for(i=0;i<sizeof(strs);i++)
+      {
+        str+=strs[i];
+        if(i<sizeof(strs)-1)
+          str+=", ";
+      }
+    }
+    else
+      str="Du hast keiner Person Zugriff auf Dein Haus gewaehrt...\n";
+    write(BS(str));
+  }
+  else
+    write(BS("Die Freunde Deines Hauses duerfen "+name(WEN,1)
+      +" nicht oeffnen/schliessen."));
+  write("-----------------------------------------------------------------------------\n");
+  if(zweitieerlaubnis!="")
+  {
+  if( zweitieerlaubnis==geteuid(TP) )
+    write(BS( "Alle Deine Zweities duerfen "+name(WEN,1)+" oeffnen/schliessen."));
+  else
+    write(BS( "Alle Zweities von "+CAP(zweitieerlaubnis)+" duerfen "+name(WEN,1)
+      +" oeffnen/schliessen."));
+  write("-----------------------------------------------------------------------------\n");
+  }
+  strs=QueryProp("cnt_erlaubnis");
+  if(sizeof(strs))
+  {
+    write(BS("Folgende sonstige Personen duerfen "+name(WEN,1)
+      +" oeffnen/schliessen:"));
+    str="";
+    for(i=0;i<sizeof(strs);i++)
+    {
+      str+=strs[i];
+      if(i<sizeof(strs)-1)
+        str+=", ";
+    }
+    write(BS(str));
+  }
+  else
+    write(BS("Es gibt keine sonstigen Personen, die "+name(WEN,1)
+      +" oeffnen/schliessen duerfen."));
+  write("=============================================================================\n");
+  return 1;
+}
+
+int erlaubnis(string str)
+{
+  string *strs,wen,nf_str;
+  nf_str="Syntax: serlaube [Objekt-Id] [Spielername|\"hausfreunde\"|\"zweities\"]\n"
+        +"Bsp.:   serlaube "+QueryProp(P_IDS)[1]+" hausfreunde\n"
+        +"        serlaube "+QueryProp(P_IDS)[1]+" zweities\n"
+        +"        serlaube "+QueryProp(P_IDS)[1]+" geordi\n"
+        +"        serlaube "+QueryProp(P_IDS)[1]+"      (Listet Spieler mit Erlaubnis)\n";
+  notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str);
+  if(!str) return 0;
+  if(present(str)==TO)     // Falls Par==Obj. und kein 2./3. Par.
+  {
+    erlaubnis_liste();           //   Liste der Erlaubten ausgeben!
+    return 1;
+  }
+  notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"+nf_str);
+  strs=old_explode(str, " ");
+  if( sizeof(strs) < 2 )           // Falls nur 1 Par.
+    return 0;             // Anzahl erforderlicher Parameter unterschritten!
+  else if( sizeof(strs) == 2 )
+  {
+    str=strs[0];
+    wen=strs[1];
+    if( IST_ZAHL(wen) )
+      if(present(str+" "+wen)==TO)   // Falls 2 Par. und dies das Obj. ist ohne 3. Par.
+      {
+        erlaubnis_liste();           //   Liste der Erlaubten ausgeben!
+        return 1;
+      }
+      else
+        return 0;
+  }
+  else if( sizeof(strs) == 3 )
+  {
+    str=strs[0]+" "+strs[1];
+    wen=strs[2];
+  }
+  else        // Anzahl erforderlicher Parameter ueberschritten!
+    return 0;
+  if(present(str)!=TO)   // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+    return 0;
+  if( !erlaubt() && !QueryProp("test") )
+  {
+    write( BS("Nur "+QueryHausbesitzer()+" darf Berechtigungen "
+      +name(WESSEN,1)+" aendern!"));
+    return 1;
+  }
+  if(wen=="hausfreunde")
+  {
+    if(hauserlaubnis)
+    {
+      hauserlaubnis=0;
+      SetProp("cnt_hausfreunde", ({}) );
+    }
+    else
+    {
+      hauserlaubnis=1;
+      SetProp("cnt_hausfreunde", SHVERWALTER->HausProp(LOWER(QueryHausbesitzer()), 2) );
+    }
+    write( BS("Die Freunde Deines Seherhauses duerfen "+name(WEN,1)+" jetzt "
+      +(!hauserlaubnis?"nicht mehr ":"")+"oeffnen/schliessen."));
+    return 1;
+  }
+  if(wen=="zweities")
+  {
+    if(zweitieerlaubnis!="")                        // Zweities erlaubt?
+      zweitieerlaubnis="";                          //   dann verbieten!
+    else                                            // ansonsten:
+    {
+      if( TP->QueryProp(P_SECOND) )                 // Selbst ein Zweitie?
+        zweitieerlaubnis=LOWER(TP->QueryProp(P_SECOND)); // Die Zweities vom Erstie erlauben!
+      else                                          // sonst:
+        zweitieerlaubnis=geteuid(TP);               //   Eigene Ersties erlauben!
+    }
+    SetProp("cnt_zweitieerlaubnis", zweitieerlaubnis );
+    if(zweitieerlaubnis!="")
+      write( BS(CAP(zweitieerlaubnis)+"'s Zweities duerfen "+name(WEN,1)+" jetzt "
+        +(zweitieerlaubnis==""?"nicht mehr ":"")+"oeffnen/schliessen."));
+    return 1;
+  }
+  wen=CAP(wen);
+  if( file_size("//save//"+LOWER(wen[0..0])+"//"+LOWER(wen)+".o") != -1 ) // Spieler gibt es auch!
+  {
+    if( member(erlaubnis, wen) != -1 )  // Spieler hat Erlaubnis -> verbieten!
+    {
+      erlaubnis-=({wen});
+      write( BS( wen+" darf "+name(WEN,1)+" jetzt nicht mehr oeffnen/schliessen."));
+    }
+    else
+    {
+      erlaubnis+=({wen});
+      write( BS( wen+" darf "+name(WEN,1)+" jetzt oeffnen/schliessen."));
+    }
+    SetProp("cnt_erlaubnis", erlaubnis);
+    return 1;
+  }
+  else
+    write("Es gibt keinen Spieler namens "+wen+"!\n");
+  return 1;
+}
+
+varargs int skommandos(string str)
+{
+  notify_fail( "Fehler: Dieser Befehl benoetigt eine gueltige Objekt-Id als Parameter.\n"
+    +"Beispiel: skommandos "+QueryProp(P_IDS)[1]+"\n");
+  if(!str) return 0;
+  if(present(str)!=TO ) // Bin ich gemeint?
+    return 0;
+  write("=============================================================================\n");
+  write("Aktuelle Version: "+QueryProp("cnt_version_std")+QueryProp("cnt_version_obj")+"\n");
+  write( BS(Name(WER,1)+" kann nur in diesem Seherhaus verwendet werden, "
+    +"da "+QueryPronoun(WER)+" speziell dafuer gekauft wurde. "+CAP(QueryPronoun(WER))
+    +" verfuegt ueber folgende Kommandos:")
+    +"-----------------------------------------------------------------------------\n"
+    +"oeffne [Objekt-Id]\n"
+    +"  oeffnet "+name(WEN,1)+"\n\n"
+    +"schliesse [Objekt-Id]\n"
+    +"  schliesst "+name(WEN,1)+"\n\n"
+    +"serlaube [Objekt-Id] [Spielername|\"hausfreunde\"|\"zweities\"]\n"
+    +"  Erlaubt Personen, "+name(WEN,1)+" mitzubenutzen.\n"
+    +"  serlaube + Objekt-Id (ohne Spielername/hausfreunde)\n"
+    +"  listet alle Personen mit Zugriff auf "+name(WEN,1)+"\n\n"
+    +"verschiebe [Objekt-Id] nach [Ausgang]\n"
+    +"  Damit kannst Du "+name(WEN,1)+" innerhalb Deines Seherhauses verschieben.\n\n"
+  //Seleven 07.12.2005
+    +"sverstecke [Objekt-Id]\n"
+    +"  Damit machst Du "+name(WEN,1)+" unsichtbar.\n"
+    +"shole [Objekt-Id] hervor\n"
+    +"  Damit machst Du "+name(WEN,1)+" wieder sichtbar.\n"
+  // Bis hier
+    +"zertruemmer [Objekt-Id]\n"
+    +"  Damit zerstoerst Du "+name(WEN,1)+".\n\n");
+
+
+  if( QueryProp("obj_cmd") )
+    write( QueryProp("obj_cmd")+"\n" );
+
+  write("[Objekt-Id] muss eine gueltige Id sein, in diesem Fall z.B. "
+      +QueryProp(P_IDS)[1]+"\n");
+  write("=============================================================================\n");
+
+
+  return 1;
+}
+
+int verschieben(string str)
+{
+  string was, wohin, zielraum,nf_str;
+  nf_str="Syntax: verschiebe [Objekt-Id] nach [Richtung]\n"
+        +"Bsp.:   verschiebe "+QueryProp(P_IDS)[1]+" nach osten\n";
+  notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str);
+  if(!str) return 0;
+  if(sscanf(str,"%s nach %s", was, wohin) != 2)
+    return 0;
+  notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"+nf_str);
+  if(present(was)!=TO)   // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+    return 0;
+  if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
+  {
+    write( BS("Nur "+QueryHausbesitzer()+" darf "+name(WEN,1)+" verschieben!"));
+    return 1;
+  }
+  if( member(m_indices(ETO->QueryProp(P_EXITS)), wohin) != -1 ) // Ausgang vorhanden?
+  {
+    zielraum=ETO->QueryProp(P_EXITS)[wohin];
+    if( strstr(zielraum, LOWER(QueryHausbesitzer())+"raum", 0) != -1  ||
+        QueryProp("test") )  // Gleiches Seherhaus? Oder Test?
+    {
+      TO->move(zielraum, M_PUT);
+      write("Du verschiebst "+name(WEN,1)+" nach "+CAP(wohin)+".\n");
+      TP->move(zielraum, M_GO, "nach "+CAP(wohin), "schiebt "+name(WEN),
+                              "schiebt "+name(WEN)+" herein");
+      if( present("Interactive", SCHRANKMASTER) )
+        tell_room(SCHRANKMASTER, TP->Name(WER)+" verschiebt "+name(WEN)+" nach "
+          +to_string(ETO)+".\n");
+    }
+    else
+      write( BS("Du kannst "+name(WEN,1)+" nicht in einen Raum ausserhalb "
+        +"Deines Hauses schieben!"));
+  }
+  else
+    write( BS("Es gibt keinen Ausgang namens \""+wohin+"\", wohin Du "
+      +name(WEN,1)+" verschieben koenntest.",78,"Fehler: ", BS_INDENT_ONCE));
+  return 1;
+}
+
+int zertruemmern(string str)
+{
+  string nf_str;
+  nf_str="Syntax: zertruemmer [Objekt-Id]\n"
+        +"Bsp.:   zertruemmer "+QueryProp(P_IDS)[1]+"\n";
+  notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str);
+  if(!str) return 0;
+  notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"+nf_str);
+  if(present(str)!=TO)   // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+    return 0;
+  if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
+  {
+    write( BS("Nur "+QueryHausbesitzer()+" darf "+name(WEN,1)+" zertruemmern!"));
+    return 1;
+  }
+  write( BS("Du zertruemmerst "+name(WEN)+" mit wuchtigen Schlaegen, bis "
+    +"nicht mal mehr Kruemel von "+QueryPronoun(WEM)+" uebrig sind."));
+  tell_room(ETO, BS( TP->Name(WER)+" zertruemmert "+name(WEN)+"."), ({TP}) );
+  call_other(SCHRANKMASTER,"???",0);
+  if( present("Interactive", SCHRANKMASTER) )
+    tell_room(SCHRANKMASTER, TP->Name(WER)+" zertruemmert "+name(WEN)+".\n");
+  SCHRANKMASTER->RemoveCnt(TO, geteuid(TP) );
+  remove(1);
+  if(TO) destruct(TO);
+  return 1;
+}
+
+int props2vars()  // Fuer die Update-Funktion des Schrankmasters
+{
+  zweitieerlaubnis=QueryProp("cnt_zweitieerlaubnis")||"";
+  erlaubnis=QueryProp("cnt_erlaubnis")||({});
+  if( sizeof(QueryProp("cnt_hausfreunde")) )
+    hauserlaubnis=1;
+  else
+    hauserlaubnis=0;
+  if( QueryProp(P_CNT_STATUS) == CNT_STATUS_OPEN)
+    SetProp(P_TRANSPARENT, 1);
+  else
+    SetProp(P_TRANSPARENT, 0);
+  return 1;
+}
+
+void reset()
+{
+  set_next_reset(3600); // 1 x pro Std. Reset!
+  ::reset();
+}
+
+public varargs int remove(int silent)
+{
+  // Beim Schrankmaster abmelden, dann zerstoeren.
+  string uid="";
+  if (objectp(environment()))
+  {
+    uid=environment()->QueryOwner();                
+  }
+  SCHRANKMASTER->RemoveCnt(TO, uid );
+  return ::remove(silent);
+}
+
+public int UpdateMe()
+{
+  if (!objectp(environment()))
+    return 0;
+  object cnt_neu=clone_object(load_name(ME));  // Neuen Schrank clonen
+  cnt_neu->move( environment(), M_NOCHECK );  // In selben Raum wie alten schieben...
+  cnt_neu->SetProp("cnt_status", QueryProp("cnt_status") );
+  cnt_neu->SetProp("cnt_hausfreunde", QueryProp("cnt_hausfreunde") );
+  cnt_neu->SetProp("cnt_erlaubnis", QueryProp("cnt_erlaubnis") );
+  cnt_neu->SetProp("cnt_zweitieerlaubnis", QueryProp("cnt_zweitieerlaubnis") );
+  cnt_neu->props2vars();  // Schrank liest gesetzte Props aus in eigene Vars
+  // Inventar bewegen
+  foreach(object inv: all_inventory(this_object()))
+  {
+    if( inv->IsUnit() )                          // Units
+      inv->move(cnt_neu, M_NOCHECK|M_MOVE_ALL);
+    else                                            // Normale Objekte
+      inv->move(cnt_neu, M_NOCHECK);
+  }
+  remove(1);
+  return 1;
+}
+
+// Eingebaut 07.12.2005 von Seleven
+// Die Kurzbeschreibung invis machen:
+static int verstecken (string str)
+{
+   string nf_str;
+   nf_str="Syntax: sverstecke [Objekt-Id]\n"
+         +"Bsp.:   sverstecke "+QueryProp(P_IDS)[1]+"\n";
+   notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str);
+   if(!str || sizeof(str) == 0 || !stringp(str)) return 0;
+
+   notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"+nf_str);
+   if(present(str)!=TO)   // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+    return 0;
+
+  if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
+  {
+    write( BS("Nur "+QueryHausbesitzer()+" darf "+name(WEN,1)+" verstecken!"));
+    return 1;
+  }
+  notify_fail("Du hast "+name(WEN,1)+" doch schon versteckt!\n");
+  if(QueryProp("versteckt") == 1) return 0;
+  write("Du versteckst "+name(WEN,1)+".\n");
+  SetProp("versteckt", 1);
+  return 1;
+}
+
+static int zeigen (string str)
+{
+   string nf_str;
+   nf_str="Syntax: shole [Objekt-Id] hervor\n"
+         +"Bsp.:   shole "+QueryProp(P_IDS)[1]+" hervor\n";
+   notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str);
+   if(!str || sizeof(str) == 0 || !stringp(str)) return 0;
+   if(strstr(str,"hervor") != -1)
+    sscanf(str,"%s hervor",str);
+   notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"+nf_str);
+   if(present(str)!=TO)   // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+    return 0;
+
+   if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
+   {
+     write( BS("Nur "+QueryHausbesitzer()+" darf "+name(WEN,1)+
+               " wieder hervorholen!"));
+     return 1;
+   }
+   notify_fail(Name(WER,1)+" ist doch gar nicht versteckt!\n");
+   if(QueryProp("versteckt") == 0) return 0;
+   write("Du holst "+name(WEN,1)+" aus dem Versteck.\n");
+   SetProp("versteckt", 0);
+   return 1;
+}
+
+
+mixed short()
+{
+  string sh;
+
+  // Unsichtbar? Dann gibts nichts zu sehen ...
+  if (QueryProp(P_INVIS)||!(sh=QueryProp(P_SHORT)))
+  return 0;
+
+  if(QueryProp(P_CNT_STATUS)==CNT_STATUS_OPEN)
+  {sh += " (geoeffnet)";}
+
+  if(QueryProp("versteckt") == 1) {sh = "";}
+  else sh += ".";
+
+  return process_string(sh)+"\n";
+}
+
+// hier nicht genutzt, zum Ueberschreiben gedacht.
+protected void SetBesitzer(string uid, string uuid) { }
+
+// nach dem move schauen, ob man in einem Seherhaus ist. Wenn ja, wird die
+// Funktion SetBesitzer() gerufen und ihr UID und UUID des Eigentuemers
+// uebergeben. Sollten UID und/oder UUID nicht bestimmbar sein, wird die
+// Funktion nicht gerufen.
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+
+  ::NotifyMove(dest, oldenv, method);
+
+  if (dest
+      && strstr(object_name(dest),"/d/seher/haeuser/") == 0)
+  {
+    // (vermutlich) in einem Seherhaus
+    string uid = dest->QueryOwner();
+    // erstmal beim Schrankmaster anmelden.
+    SCHRANKMASTER->RegisterCnt(TO, QueryProp("cnt_version_std")
+        +":"+QueryProp("cnt_version_obj"), uid, dest);
+    // UUID rausfinden
+    mixed ret = (mixed)master()->get_userinfo(uid);
+    if ( pointerp(ret) && sizeof(ret) >= 5 )
+    {
+      // Username + "_" + CreationDate
+      SetBesitzer(uid, ret[0] + "_" + ret[5]);
+    }
+    // else: User unbekannt? -> jedenfalls keine initialisierung moeglich.
+  }
+}
+
diff --git a/d/seher/haeuser/moebel/tresor.c b/d/seher/haeuser/moebel/tresor.c
new file mode 100644
index 0000000..ecad6d7
--- /dev/null
+++ b/d/seher/haeuser/moebel/tresor.c
@@ -0,0 +1,78 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Tresor
+// Letzte Aenderung:    17.03.2001
+// Magier:              Swift
+//--------------------------------------------------------------------------------
+#pragma strong_types,rtt_checks
+
+#include "schrankladen.h"
+inherit LADEN("swift_std_container");
+
+#define VERSION_OBJ "3"
+
+protected void create()
+{
+  if (!clonep(TO)) return;
+  swift_std_container::create();
+  SetProp(P_SHORT, "Ein Tresor");
+  SetProp(P_LONG, BS(
+     "Ein sehr stabiler und feuersicherer Tresor, an dem sich sicher schon so "
+    +"mancher Einbrecher eine Abfuhr geholt hat. Das magische Feld, das den "
+    +"Tresor umgibt, verhindert wohl auch Attacken magischer Art. So wuchtig "
+    +"und wichtig, wie dieser Klotz aussieht, vermutest Du wohl zu Recht, "
+    +"dass sich unsagbare Reichtuemer in seinem Inneren verbergen muessen.")
+    +"@@cnt_status@@");
+  AddId(({"tresor"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_NAME, "Tresor");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_WEIGHT, 5000);         // Gewicht 5 Kg
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 100);     // Mehr sollte nicht sein, lt. Tiamak.
+  SetProp(P_VALUE, 0);             // Kein materieller Wert. Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Wird im Laden zerstoert, falls er verkauft wird.
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
+  SetProp(P_MATERIAL, ({MAT_STEEL, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuchs mal mit: \"skommandos "+QueryProp(P_IDS)[1]+"\" !\n");
+
+  AD(({"feld"}), BS("Ein starkes magisches Feld umgibt "+name(WEN,1)+"."));
+  AD(({"klotz"}), BS(Name(WER,1)+" ist wahrlich ein Klotz, ausgesprochen wuchtig."));
+}
+
+varargs int PreventInsert(object ob)
+{
+  // Zuerst mal: Dinge, deren Wert >= 5000 Muenzen ist, duerfen rein, solange
+  // es keine Ruestungen oder Waffen sind!
+  if( ob->QueryProp(P_VALUE) >= 5000 && !ob->QueryProp(P_ARMOUR_TYPE) &&
+     !ob->QueryProp(P_WEAPON_TYPE) )
+        return ::PreventInsert(ob);
+  if( ob->id("\ngeld") ||                                     // Geld?
+      ob->QueryMaterialGroup(MATGROUP_PRECIOUS_METAL) ||      // Edelmetall?
+      ob->QueryMaterialGroup(MATGROUP_JEWEL) ||               // Edelstein?
+      ob->QueryMaterial(MAT_CRYSTAL) ||                       // Kristall?
+      ob->QueryMaterial(MAT_IVORY) )                          // Elfenbein?
+  {
+    if(!ob->QueryProp(P_WEAPON_TYPE) )    // Auf keinen Fall Waffen!
+    {
+      if( ob->QueryProp(P_ARMOUR_TYPE ) ) // Falls Ruestungsteil...
+      {
+        if( ob->QueryProp(P_ARMOUR_TYPE)==AT_RING ||
+            ob->QueryProp(P_ARMOUR_TYPE)==AT_AMULET ||
+            ob->QueryProp(P_ARMOUR_TYPE)==AT_MISC )
+          return ::PreventInsert(ob);
+        else
+          write(BS("Nur wertvolle Ringe und Amulette kannst Du in "+name(WEN,1)
+            +" stecken, evtl. noch andere wertvolle Sachen, aber auf keinen "
+            +"Fall Ruestungen oder Waffen!"));
+      }
+      else
+        return ::PreventInsert(ob);
+    }
+    else
+      write("Waffen kannst Du nicht in "+name(WEN,1)+" tun!\n");
+  }
+  write( BS("In "+name(WEN,1)+" kannst Du nur Geld und Edelsteine legen, evtl. auch "
+           +"wertvolle Objekte, z.B. aus Edelmetallen, Kristall oder Elfenbein!"));
+  return 1;
+}
diff --git a/d/seher/haeuser/moebel/verkaeufer.c b/d/seher/haeuser/moebel/verkaeufer.c
new file mode 100644
index 0000000..5143ab2
--- /dev/null
+++ b/d/seher/haeuser/moebel/verkaeufer.c
@@ -0,0 +1,311 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Verkaeufer (August vom SCHRAENKER)
+// Letzte Aenderung:    24.08.2006
+// Magier:              Seleven
+//--------------------------------------------------------------------------------
+#pragma strong_types,rtt_checks
+
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <bank.h>
+
+#include "schrankladen.h"
+inherit "/std/npc";
+
+// Wenn im Laden bzw. im Environment von August die Prop "test" auf 1 gesetzt
+// wird, kostet der Kauf nichts. Jeder der ein Seherhaus besitzt, kann kaufen.
+
+// Fuer Prop im Spieler = individuellere Begruessung
+#define LASTVISIT "schrankladen_lastvisit"
+// 10% Rabatt fuer Leute, die intensiv testeten, Ideen fuer den Laden beisteuerten,
+// oder halfen, groessere Bugs zu beheben!
+#define RABATT ({"geordi","foobar","blood","lurchi", "eldaron"})
+
+// kaufe... andere im raum sollten vor der antwort sehen wer august nach was fragt
+
+protected void create()
+{
+  if(!clonep(TO)) return;
+  ::create();
+  SetProp(P_LOG_FILE,"seleven/schraenker"); // Seleven 06.04.2006
+  SP(P_SHORT, "August");
+  SP(P_LONG, BS(
+     "Das ist August, ein kleiner Zwerg und seines Zeichens Seher-Berater. "
+    +"Er versorgt Seher mit Eigenheim mit dem Mobiliar, das man braucht, "
+    +"will man sein Haus uebersichtlich halten und etwas Ordnung in die "
+    +"Sammlungen bringen, die sich in so einem Seherhaus zwangslaeufig "
+    +"anhaeufen. Im Gegensatz zu den meisten Zwergen sieht August nicht nur "
+    +"aus wie frisch geduscht, er steckt auch in einem feinen Anzug. So "
+    +"einen gepflegten Bart wie den von August hast Du vorher noch nie "
+    +"gesehen. Und dann duftet er auch noch nach irgendeinem exotischen "
+    +"Waesserchen. Waere nicht der gierige Blick in seinen Augen, koenntest "
+    +"Du wirklich zweifeln, dass es sich hier wirklich um einen Zwerg "
+    +"handelt.",78,0,1));
+  SP(P_NAME, "August");
+  SP(P_GENDER, MALE);
+  SP(P_ARTICLE, 0);
+  SP(P_RACE, "Zwerg");
+  // Arathorn, 2015-Okt-13, Zeile vor P_XP gezogen, damit auch sicher kein
+  // EK angezeigt wird.
+  create_default_npc(20);
+  SP(P_XP, 0);
+  AddId(({"august", "inhaber", "ladeninhaber", "besitzer",
+          "firmenbesitzer", "verkaeufer", "zwerg", "seher-berater"}));
+
+  SP(P_SIZE, 130);
+  SP(P_NO_ATTACK, BS("Die beruhigende Melodie scheint nicht nur zum "
+    +"Vergnuegen der Kunden zu erklingen. Du bringst es einfach "
+    +"nicht fertig, Deine Hand gegen August zu erheben!"));
+
+  AD(({"anzug"}), BS(Name(WER)+" steckt in einem feinen rotbraunen Anzug, "
+    +"in dem vermutlich auch ein Bettler koeniglich auf Dich wirken wuerde. "
+    +"Offensichtlich ist er nicht der Aermsten einer."));
+  AD(({"bettler"}), BS("Wie ein Bettler wirkt August nicht auf Dich."));
+  AD(({"waesserchen"}), BS("Es riecht koestlich. Schnupper doch mal!"));
+  AD(({"bart"}), BS("Der vermutlich weisse Bart wurde von Kuenstlerhand "
+    +"roetlich eingefaerbt und passt farblich vorzueglich zum Anzug."));
+  AD(({"kuenstlerhand"}), BS("Bestimmt hat August das nicht selbst gemacht."));
+  AD(({"mobiliar"}), BS("Das verkauft er hier."));
+  AD(({"augen"}), BS("Obwohl "+Name(WER)+" perfekte Manieren an den Tag "
+    +"legt, seine Augen verraten ihn. Die Dollarzeichen in seinem Blick "
+    +"sind nicht zu uebersehen."));
+  AD(({"dollarzeichen"}), BS("Eine Redensart. Keine Ahnung was das sein soll."));
+  AD(({"redensart"}), BS("Irgend ein Spruch, den Du irgendwo aufgeschnappt hast."));
+  AD(({"spruch"}), BS("Im Spruecheklopfen bist Du gut, oder?"));
+  AD(({"spruecheklopfen"}), BS("OK, ich geb auf. :)"));
+  AD(({"manieren"}), "Und das bei einem Zwerg!!!\n");
+
+  AddInfo(({"laden", "schraenker"}), "Der Schraenker ist mein Laden, "
+    +"und ich denke es ist ein guter Laden. Das Geschaeft laeuft, seit "
+    +"jeder Ordnung in seinem Seherhaus haben moechte. Und Seherhaeuser "
+    +"gibt es ja mehr als genug! :)", "sagt: ");
+  AddInfo(({"haeuser", "seherhaeuser"}), "Bei mir koennen Seher "
+    +"Mobiliar fuer ihr Seherhaus kaufen. Unsere Qualitaet ist weithin "
+    +"beruehmt. Ausserdem sind wir die einzigen auf dem Markt.", "grinst: ");
+  AddInfo(({"melodie"}), "lauscht vertraeumt.\n");
+  AddInfo(({"geld"}), "Sie haben doch welches?\n", "fragt besorgt: ");
+  // Seleven 01.04.2006: frage nach (schluessel)brett eingefuegt
+  AddInfo(({"mobiliar", "schrank", "schraenke", "truhe", "truhen", "kommode",
+     "kuehlschrank", "waffenschrank", "waffentruhe", "spind", "kleiderschrank","brett","schluesselbrett",
+     "muellcontainer", "zauberkiste", "tresor", "holztruhe","vitrine","pult","lesepult"}),
+     "Lesen Sie doch bitte die Preisliste an der Wand.", "sagt: ");
+  AddInfo(({"liefern", "lieferung"}), "Wir liefern sofort.", "sagt: ");
+  AddInfo(({"bart"}), "Es ist ein schoener Bart, nicht? :)", "fragt: ");
+  AddInfo(({"augen"}), "Was haben Sie an denen auszusetzen?", "fragt: ");
+  AddInfo(({"dusche"}), "Jeden Morgen. Das ist leider Pflicht. Und 364 mal "
+    +"zu oft :(", "sagt: ");
+  AddInfo(({"pflicht", "pflichten"}), "Als Ladeninhaber hat man deren "
+    +"viele!", "sagt: ");
+  AddInfo(({"anzug"}), "Ein echter Armgani!", "verkuendet stolz: ");
+  AddInfo(({"armgani"}), "Du kennst Armgani nicht?", "fragt naseruempfend: ");
+  AddInfo(({"geordi"}), "Von ihm stammt die urspruengliche Geschaeftsidee "
+    +"fuer den SCHRAENKER. Dafuer bekommt er bei uns auch Rabatt!", "erzaehlt: ");
+  AddInfo(({"august"}), "Wollen Sie sich ueber mich lustig machen?", "fragt drohend: ");
+  AddInfo(({"rabatt"}), "Nur Stammkunden, die uns schon brauchbare Ideen "
+    +"geliefert haben, oder die uns beim Aufbau des Geschaefts geholfen haben, "
+    +"gewaehren wir Rabatt.", "sagt: ");
+  AddInfo(({"ideen"}), "Es muessen brauchbare Ideen sein.", "sagt: ");
+  AddInfo(({"stammkunde", "stammkunden"}), "Einige kommen oefter vorbei.", "sagt: ");
+  AddInfo(({"aufbau"}), "Es kommt mir noch so vor, als waers gestern gewesen.", "sagt: ");
+  AddInfo(({"kunde", "kunden"}), "Ich hoffe mal, dass SIE der Kunde sind!?", "sagt: ");
+
+  AddCmd(({"kauf", "kaufe", "bestell", "bestelle"}), "container_kaufen");
+  AddCmd(({"schnupper", "schnuppere"}), "schnuppern");
+}
+
+int schnuppern()
+{
+  write(BS("Ein betoerender Duft umgibt August, und verdeckt jeglichen Geruch, "
+    +"den Du von sonstigen Zwergen gewohnt bist."));
+  return 1;
+}
+
+static string anrede()
+{
+  switch( TP->QueryProp(P_GENDER) )
+  {
+    case MALE   : return "mein Herr"; break;
+    case FEMALE : return "meine Dame"; break;
+  }
+  return "mein H... D... Wasauchimmer";
+}
+
+void begruessung(object pl, string txt)
+{
+  if( objectp(pl) && present(pl) )
+    tell_room(ETO, "\n"+txt);
+}
+
+void init()
+{
+  ::init();
+  if (!interactive(TP) || TP->QueryProp(P_INVIS))
+      return 0; // Keine Meldungen wenn invis oder kein Spieler!
+  if(TP->QueryProp(LASTVISIT) < time()-3600)  // laenger als 1 Std. nicht da gewesen
+  {
+    if( member(RABATT, geteuid(TP)) != -1 )
+      call_out("begruessung", 0, TP, BS("Herzlichst Willkommen in meinem "
+        +"bescheidenen Laden, "+TP->name(WER)+". Es ist jedesmal eine Freude, "
+        +"Sie zu sehen!",78, Name(WER)+" sagt: "));
+    else
+      call_out("begruessung", 0, TP, BS("Willkommen in meinem bescheidenen Laden, "
+        +anrede()+".",78, Name(WER)+" sagt: "));
+  }
+  else  // schonmal innerhalb 1 Std. da gewesen?
+  {
+    if( member(RABATT, geteuid(TP)) != -1 )
+      call_out("begruessung", 0, TP, BS("Hallo, "+TP->name(WER)+". "
+        +"Sie konnten es ja nicht lange ohne mich aushalten, aber Stammkunden "
+        +"sind mir die liebsten Kunden. Womit kann ich Ihnen diesmal "
+        +"dienen?",78, Name(WER)+" sagt: "));
+    else
+      call_out("begruessung", 0, TP, BS("Guten Tag, "+anrede()+". "
+        +"Ich freue mich, dass Sie meinen Laden so bald wieder besuchen kommen! "
+        +"Womit kann ich Ihnen diesmal dienen?",78, Name(WER)+" sagt: "));
+  }
+
+  TP->SetProp(LASTVISIT, time() );
+}
+
+int container_kaufen(string str)
+{
+  string cnt_file;
+  int preis;
+  object zielraum,cnt,ob;
+  tell_room(ETO, TP->Name(WER)+" wendet sich wegen eines Kaufs an "
+    +Name(WEN)+".\n", ({TP}) );
+  if( (query_wiz_level(TP) >= SEER_LVL && query_wiz_level(TP) < LEARNER_LVL) ||
+      ETO->QueryProp("test") ) // TP ist Seher, oder Testmodus.
+  {
+    catch(call_other("/d/seher/haeuser/"+geteuid(TP)+"raum0", "???", 0));
+    if( !zielraum=find_object("/d/seher/haeuser/"+geteuid(TP)+"raum0") )
+    {
+      tell_room(ETO, BS("Tut mir leid, "+anrede()+". Voraussetzung fuer eine "
+        +"Lieferung unserer Artikel ist ein begehbares "
+        +"Seherhaus.",78, Name(WER)+" sagt: "));
+      return 1;
+    }
+    notify_fail( BS("Tut mir leid, "+anrede()+". So einen Artikel fuehren wir nicht. "
+      +"Bitte lesen Sie die Preisliste an der Wand. Dort koennen Sie sich genau "
+      +"darueber informieren, ueber welche Artikel wir zur Zeit "
+      +"verfuegen.",78, Name(WER)+" sagt: "));
+    if(!str) return 0;
+    switch(str)
+    {
+      case "1" :
+      case "kommode" :       cnt_file="kommode";
+                             preis=3799;
+                             break;
+      case "2" :
+      case "kleiderschrank": cnt_file="kleiderschrank";
+                             preis=5899;
+                             break;
+      case "3" :
+      case "spind" :         cnt_file="spind";
+                             preis=5299;
+                             break;
+      case "4" :
+      case "kuehlschrank" :  cnt_file="kuehlschrank";
+                             preis=3499;
+                             break;
+      case "5" :
+      case "waffenschrank" : cnt_file="waffenschrank";
+                             preis=5899;
+                             break;
+      case "6" :
+      case "waffentruhe" :   cnt_file="waffentruhe";
+                             preis=5399;
+                             break;
+      case "7" :
+      case "tresor" :        cnt_file="tresor";
+                             preis=4499;
+                             break;
+      case "8" :
+      case "zauberkiste" :   cnt_file="zauberkiste";
+                             preis=7999;
+                             break;
+      case "9" :
+      case "muellcontainer": cnt_file="muellcontainer";
+                             preis=999;
+                             break;
+      // Seleven 01.04.2006
+      case "10" :
+      case "schluesselbrett": cnt_file="schluesselbrett";
+                             preis=499;
+                             break;
+      case "11" :
+      case "duschkabine": cnt_file="wasch";
+                             preis=3499;
+                             break;
+      case "12":
+      case "holztruhe":
+                             cnt_file="autoloadertruhe";
+                             preis=74999;
+                             break;
+      case "13":
+      case "vitrine":
+                             cnt_file="vitrine";
+                             preis=49999;
+                             break;
+      case "14":
+      case "lesepult":
+                            cnt_file="lesepult";
+                            preis=60000;
+                             break;
+      default:               return 0; break;
+    }
+    if( member(RABATT, geteuid(TP)) != -1 )  // 10% Rabatt fuer bestimmte Leute,
+      preis=preis/100*90;                    // die Ideen fuer den Laden beisteuerten.
+    if( !ETO->QueryProp("test") )
+    {
+      if( !TP->QueryMoney() )
+      {
+        tell_room(ETO, BS("Ohne Geld bekommen Sie bei uns nichts, "+anrede()
+          +". Wir sind kein Wohlfahrtsinstitut!",78, Name(WER)+" sagt: "));
+        return 1;
+      }
+      if( TP->QueryMoney() < preis )
+      {
+        tell_room(ETO, BS( CAP(anrede())+", Sie haben zuwenig Geld dabei. "
+          +"Der Preis fuer dieses Objekt ist "+preis
+          +" Muenzen.",78, Name(WER)+" sagt: "));
+        return 1;
+      }
+      TP->AddMoney(-preis);      // Spieler Geld abziehen
+      ZENTRALBANK->PayIn(preis); // Geld auf die Bank.
+    }
+    cnt=clone_object( LADEN(cnt_file) );
+    cnt->move(zielraum, M_PUT|M_SILENT);
+    tell_room(ETO, TP->Name(WER)+" kauft "+cnt->name(WEN)+".\n", ({TP}) );
+    tell_room(ETO, BS("Es ist ein Vergnuegen, mit Ihnen Geschaefte zu machen, "
+      +anrede()+"! Wenn Sie in Ihr Haus zurueckkehren, werden Sie die Ware bereits "
+      +"vorfinden, so schnell arbeiten wir.",78, Name(WER)+" sagt: "));
+    tell_room(zielraum, BS("Mit Knall und Schwefelduft erscheint ein kleines, "
+      +"verhutzeltes Maennchen. Es traegt einen gruenen Anzug und eine gruene "
+      +"Kappe, auf der in gelbem Schriftzug \"SCHRAENKER\" steht. Das Maennchen "
+      +"stellt "+cnt->name(WEN)+" ab und verschwindet auf dem gleichen Wege, wie "
+      +"es hereingekommen ist."));
+    if( present("Interactive", SCHRANKMASTER) )
+      tell_room(SCHRANKMASTER, TP->Name(WER)+" kauft "+name(WEN)+".\n");
+    SCHRANKMASTER->RegisterCnt(cnt, cnt->QueryProp("cnt_version_std")
+      +cnt->QueryProp("cnt_version_obj"), geteuid(TP),
+       "/d/seher/haeuser/"+geteuid(TP)+"raum0");
+    return 1;
+  }
+  else if(query_wiz_level(TP) >= LEARNER_LVL )  // TP ist Magier.
+  {
+    tell_room(ETO, BS("Tut mir leid, "+anrede()+". An Magier liefern wir nichts. "
+      +"Das ist ein Prinzip des Hauses, und Prinzipien sind dazu da, dass "
+      +"sie eingehalten werden. ",78, Name(WER)+" sagt: "));
+    return 1;
+  }
+  else  // TP ist Spieler, kein Seher oder Magier.
+  {
+    tell_room(ETO, BS("Tut mir leid, "+anrede()+". Wir liefern nur an Seher. "
+      +"Aber da ich Sie als strebsame und erfolgreiche Person ansehe, "
+      +"bin ich sicher, dass wir uns bald wiedersehen!",78, Name(WER)+" sagt: "));
+    return 1;
+  }
+}
diff --git a/d/seher/haeuser/moebel/vitrine.c b/d/seher/haeuser/moebel/vitrine.c
new file mode 100644
index 0000000..ae5bb96
--- /dev/null
+++ b/d/seher/haeuser/moebel/vitrine.c
@@ -0,0 +1,339 @@
+/************************************************************************************
+Vitrine fuer die Pokale
+Autor: Miril
+letzte Aenderung:
+05.09.2011
+************************************************************************************/
+
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include "/d/ebene/miril/fraternitas/fraternitas.h"
+#include "/d/ebene/miril/fraternitas/trophy.h"
+#pragma strong_types,rtt_checks
+#include "/d/seher/haeuser/moebel/moebel.h"
+#include "/d/seher/haeuser/haus.h"
+
+inherit LADEN("swift_std_container");
+
+string besitzer;
+int staub;
+string *trophynamen;
+
+static string my_long();
+static string staub_descr();
+void GetTrophyDetails();
+string *GetAllTrophyNames();
+void NeueDetails();
+
+protected void create()
+{
+  if (!clonep(this_object())){
+    set_next_reset(-1);
+    return;
+  }
+  set_next_reset(86400);
+  ::create();
+  besitzer="";
+  staub=0;
+  SetProp(P_NAME,"Vitrine");
+  SetProp(P_SHORT,"Eine Vitrine");
+  Set(P_LONG,#'my_long,F_QUERY_METHOD);
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_MATERIAL,([MAT_GLASS:50,MAT_EBONY:50]));
+  SetProp(P_VALUE,10000);
+  SetProp(P_WEIGHT,500000);
+  SetProp(P_MAX_OBJECTS,0);
+  SetProp(P_MAX_WEIGHT,0);
+  SetProp(H_FURNITURE,1);
+  SetProp(P_NOGET,"Die Vitrine ist viel zu schwer.\n");
+  SetProp("cnt_version_obj",2);
+  GetTrophyDetails();
+  AddId(({"\nmiril_pokal_vitrine","vitrine"}));
+
+  NeueDetails();
+
+  AddCmd(({"saeubere","saeuber","reinige","reinig","mach","mache"}),
+           "saeuber_cmd");
+
+  AddCmd(({"stelle","stell"}),"stell_cmd");
+  RemoveCmd(({"serlaube"}));
+
+}
+
+void reset(){
+  ++staub;
+  set_next_reset(86400);
+  ::reset();
+  return;
+}
+
+void SetBesitzer(string uid,string uuid){
+  besitzer=uuid;
+  GetTrophyDetails();
+  return;
+}
+
+string QueryBesitzer(){
+  return besitzer;
+}
+
+void NeueDetails(){
+  AddDetail(({"holz","ebenholz"}),BS("Die Vitrine besteht teilweise "
+    "aus Ebenholz. Tueren, Deckel und Einlegeboeden sind aus Glas."));
+  AddDetail(({"tueren","tuer","glastueren","glastuer","deckel"}),BS(
+            "Durch das Glas kannst Du ins Innere der Vitrine gucken."));
+  AddDetail(({"inneres","gegenstaende","gegenstand"}),
+            BS("Dort kann man Pokale hineinstellen, aber nicht wieder "
+            "herausnehmen."));
+  AddDetail(({"glas","scheibe","scheiben"}),
+     BS("Glas ist praktisch, solange es sauber ist: "
+        "Man kann dadurch ins Innere schauen."));
+  AddDetail(({"reihe","platz","einlegeboeden","einlegeboden","boeden",
+              "boden"}),
+           BS("Die vielen Boeden bieten Platz fuer viele Pokale!"));
+  return;
+}
+
+
+static string my_long(){
+  if(staub<21)
+  return BS(
+  "Die schlichte Vitrine aus dunklem Holz eignet sich hervorragend, um "
+  "wertvolle Gegenstaende darin auszustellen. Hinter den beiden Glastueren "
+  "siehst Du eine Reihe von Einlegeboeden, die ebenfalls aus Glas bestehen. "+
+  staub_descr());
+  else return BS(staub_descr());
+}
+
+
+static string staub_descr(){
+  string text;
+  text="";
+  switch(staub){
+    case 0..1:
+      text=
+        "Die Vitrine ist hervorragend gepflegt. Du siehst keinerlei Staub "+
+        "darin, und die Scheiben sind voellig klar.\n";
+        AddDetail(({"staub"}),BS("Keiner zu sehen."));
+    break;
+    case 2..4:
+      text=
+        "Die Vitrine ist gut gepflegt. Es liegt kaum Staub darin, und die "+
+        "Tueren sind klar.\n";
+        AddDetail(({"staub"}),BS("Man sieht nur wenig davon."));
+    break;
+    case 5..7:
+      text=
+        "Die Vitrine ist in einem ordentlichen Zustand, allerdings sammelt "+
+        "sich so langsam Staub auf den Einlegeboeden.\n";
+        AddDetail(({"staub"}),BS("So langsam koennte man die "
+        "Vitrine saeubern!"));
+        AddDetail(({"zustand"}),BS("Noch ist er ordentlich."));
+    break;
+    case 8..10:
+      text=
+        "Die Vitrine wirkt schon etwas staubig. Man erkennt auch, dass "+
+        "der ein oder andere Fingerabdruck die Tueren ziert.";
+        AddDetail(({"staub","fingerabdruck","tueren","glastueren",
+        "deckel","glastuer","tuer","zustand"}),
+        BS("Es lohnt sich jetzt schon, die Vitrine zu saeubern."));
+    break;
+    case 11..15:
+      text=
+        "Du hast den Eindruck, dass die Vitrine langsam einmal gesaeubert "+
+        "werden koennte. Die Einlegeboeden sind staubig und zahlreiche "+
+        "Fingerabdruecke sind auf den Glastueren zu sehen.";
+    break;
+    case 16..20:
+      text=
+        "Die Vitrine macht einen recht ungepflegten Eindruck. Innen ist "+
+        "sie schon sehr verstaubt und auch die Tueren sind schon recht "+
+        "dreckig geworden.";
+        AddDetail(({"staub"}),BS("Nun wird es aber wirklich Zeit, die "
+        "Vitrine zu saeubern. Sonst sieht man bald nichts mehr darin."));
+    break;
+    case 21..25:
+      text=
+        "Die Vitrine ist total zugestaubt. Ueber den Glastueren sind "+
+        "erste Spinnweben zu erkennen und die Scheiben sind schon fast "+
+        "blind von all den Fingerabdruecken unaufmerksamer Gaeste.";
+         RemoveDetail(m_indices(QueryProp(P_DETAILS)));
+         AddDetail(({"spinnweben","dreck","vitrine","glastueren","glastuer",
+         "tueren","tuer","scheibe","scheiben","deckel"}),
+         "Unter dem ganzen Dreck ist leider nichts mehr zu erkennen.\n");
+         AddDetail(({"gaeste","gast","fingerabdruck","fingerabdruecke"}),
+         "Die Gaeste sind sicher schuld an dem Dreck.\n");
+    break;
+    case 26..30:
+      text=
+        "Du kannst die Vitrine unter der dicken Staubschicht kaum noch "+
+        "erkennen und die Glastueren sind blind geworden.";
+         AddDetail(({"staubschicht","schicht"}),
+         BS("Die ist verdammt dick und undurchsichtig."));
+    break;
+    default:
+      text=
+        "Dass sich unter dieser Ansammlung von Spinnweben und Staub noch "+
+        "eine Vitrine mit Glastueren verbergen soll, kannst Du kaum glauben. "+
+        "Jedenfalls kann man durch die voellig blinden Scheiben nichts "+
+        "mehr erkennen.";
+      AddDetail(({"dreck","spinnweben","staub","ansammlung"}),"Pfui Deibel!\n");
+  }
+  return text;
+}
+
+static int saeuber_cmd(string str){
+  string text;
+  if(!str) return 0;
+  if(strstr(str,"vitrine")<0) return 0;
+  switch(staub){
+    case 0:
+      write("Das ist noch nicht noetig!\n");
+      return 1;
+    break;
+    case 1..20:
+      text="Du reinigst die Vitrine gruendlich. Das wurde auch langsam "
+      "Zeit!";
+      if(getuuid(TP)!=besitzer)
+        text+="Du fragst Dich, wieso Du fremde Moebel reinigst.";
+
+      write(BS(text));
+      say(BS(TPN+" reinigt die Vitrine gruendlich. Das wurde auch langsam "
+      "Zeit!\n"),TP);
+      staub=0; //Hier darf noch jemand helfen!
+    break;
+    case 21..30:
+      text="Du beginnst die Vitrine zu reinigen. ";
+      if(getuuid(TP)!=besitzer){
+        text+="Nach einiger Zeit fragst Du Dich, ob Du eigentlich spinnst, "+
+        "denn das ist ueberhaupt nicht Deine Vitrine. Du laesst von dem "+
+        "Vorhaben wieder ab.";
+        say(BS(TPN+" faengt kurz an, die Vitrine zu reinigen, laesst es aber "
+        "schnell wieder sein."),TP);
+      }
+      else{
+        text+="Das dauert eine ganze Weile, aber dann strahlt die Vitrine "
+        "wieder.";
+        say(BS(TPN+" beginnt die Vitrine zu reinigen. Das dauert eine ganze "
+        "Weile, aber dann strahlt sie wieder."),TP);
+        staub=0;
+      }
+      write(BS(text));
+    break;
+    default:
+      if(getuuid(TP)!=besitzer)
+        write("Das dreckige Teil fasst Du nicht an!\n");
+      else{
+        write(BS(
+          "Du schrubbst und schrubbst und es scheint kein Ende zu nehmen. "
+          "Als Du schon langsam aufgeben willst, ist so langsam ein kleiner "
+          "Erfolg zu sehen. Du nimmst Dir vor, die Vitrine in Zukunft "
+          "haeufiger zu reinigen!"));
+        say(BS(
+          TPN+" schrubbt stundenlang an der Vitrine rum, bis sie endlich "
+          "wieder richtig sauber ist."),TP);
+        staub=0;
+      }
+  }
+  if(staub==0){
+    RemoveDetail(m_indices(QueryProp(P_DETAILS)));
+    GetTrophyDetails();
+    NeueDetails();
+   }
+  return 1;
+}
+
+void GetTrophyDetails(){
+  string *trophyliste,*shorts;
+  mapping trophydetails;
+
+  shorts=({});
+
+  trophyliste=MIRIL_TROPHYMASTER->GetPlacedTrophies(besitzer);
+
+  foreach( string tr : trophyliste )
+    shorts+=({ MIRIL_TROPHYMASTER->GetTrophyShort(tr) });
+
+  shorts = sort_array(shorts, #'>);
+  if(!sizeof(shorts))
+    AddDetail(({"pokale","pokal","in vitrine"}),
+                "Bislang ist die Vitrine noch leer.\n");
+  else
+  AddDetail(({"pokale","pokal","in vitrine"}),
+    "Du siehst folgende Pokale in der Vitrine:\n"
+    +implode(shorts,".\n")+".\n");
+
+  foreach( string tr : trophyliste )
+  {
+    AddDetail( MIRIL_TROPHYMASTER->GetTrophyIds(tr),
+               BS(MIRIL_TROPHYMASTER->GetTrophyLong(tr)));
+
+    trophydetails = MIRIL_TROPHYMASTER->GetTrophyDetails(tr);
+    if ( !sizeof(trophydetails) )
+      continue;
+    foreach ( mixed det, string det_desc : trophydetails )
+      AddDetail( det, BS(det_desc) );
+  }
+  return;
+}
+
+string *GetAllTrophyNames(){
+  int i,anzahl;
+  string *trophynamen;
+
+  trophynamen=({});
+
+  foreach(string bla:MIRIL_TROPHYMASTER->GetTrophyListe())
+    trophynamen+=MIRIL_TROPHYMASTER->GetTrophyIds(bla);
+
+  return trophynamen;
+}
+
+static int stell_cmd(string str){
+  string was,identifier;
+  int resultat;
+  object ob;
+  if(!str) return 0;
+  if(strstr(str,"vitrine")<0) return 0;
+  notify_fail("Das ist nicht Deine Vitrine!\n");
+  if(getuuid(TP)!=besitzer)
+    return 0;
+  notify_fail("Die Syntax ist: stelle <gegenstand> in vitrine!\n");
+  if(sscanf(str,"%s in vitrine",was)!=1)
+    return 0;
+
+  notify_fail("Die Vitrine sollte schon offen sein.\n");
+  if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN && !IS_LEARNER(TP))
+    return 0;
+  notify_fail("Du solltest die Vitrine vorher erstmal saeubern!\n");
+  if(staub>20)
+    return 0;
+  notify_fail("Das kannst Du leider nicht in die Vitrine stellen!\n");
+  if(member(GetAllTrophyNames(),lower_case(was))<0
+     && lower_case(was)!="pokal")
+    return 0;
+  notify_fail("Das hast Du nicht!\n");
+  if(!ob=present(was,TP))
+    return 0;
+  notify_fail("Das kannst Du leider nicht in die Vitrine stellen!\n");
+  if(!ob->id("\nmiril_pokal"))
+    return 0;
+
+  identifier=ob->QueryIdentifier();
+  resultat=MIRIL_TROPHYMASTER->PlaceTrophy(getuuid(TP),identifier);
+
+  notify_fail("Hier stimmt etwas nicht, sag bitte einem Erzmagier Bescheid! "
+  "Resultat="+resultat+"\n");
+  if(resultat!=1)
+    return 0;
+  write(BS("Du stellst den Pokal in die Vitrine und bewunderst ihn noch ein "
+  "wenig.\n"));
+  say(TPN+" stellt einen Pokal in die Vitrine.\n",TP);
+  GetTrophyDetails();
+  ob->remove();
+  if(ob)
+    destruct(ob);
+  return 1;
+}
+
diff --git a/d/seher/haeuser/moebel/waffenschrank.c b/d/seher/haeuser/moebel/waffenschrank.c
new file mode 100644
index 0000000..e36abc9
--- /dev/null
+++ b/d/seher/haeuser/moebel/waffenschrank.c
@@ -0,0 +1,220 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Waffenschrank
+// Letzte Aenderung:    22.03.2001
+// Magier:              Swift
+//--------------------------------------------------------------------------------
+/* Changelog
+   * 21.05.2007, Zesstra
+     Kampfstaebe auf Hinweis von tassram ergaenzt.
+*/
+#pragma strong_types,rtt_checks
+
+#include "schrankladen.h"
+#include <ansi.h>
+#include <class.h>
+inherit LADEN("swift_std_container");
+
+#define VERSION_OBJ "6"
+
+string strall;
+
+protected void create()
+{
+  if (!clonep(TO)) return;
+  swift_std_container::create();
+  SetProp(P_SHORT, "Ein Waffenschrank");
+  SetProp(P_LONG, BS(
+     "Mit den Waffen, die in diesen massiven Holzschrank passen, koenntest "
+    +"Du ein ganzes Soeldnerheer ausruesten. Fuer jede Waffengattung gibt "
+    +"es einen eigenen Platz, damit man auch alles gleich findet, sollte "
+    +"man es brauchen.")+"@@cnt_status@@");
+  AddId(({"waffenschrank", "holzschrank", "schrank"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_NAME, "Waffenschrank");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_WEIGHT, 5000);         // Gewicht 5 Kg
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 100);     // Mehr sollte nicht sein, lt. Tiamak.
+  SetProp(P_VALUE, 0);             // Kein materieller Wert. Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Wird im Laden zerstoert, falls er verkauft wird.
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
+  SetProp(P_MATERIAL, ({MAT_MISC_WOOD, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuchs mal mit: \"skommandos "+QueryProp(P_IDS)[1]+"\" !\n");
+  SetProp("obj_cmd", "sliste [Objekt-Id] [Waffentyp|\"alles\"]\n"
+    +"  Waffentypen: Schwerter, Speere, Aexte, Keulen, Kampfstaebe, Messer, Peitschen,\n"
+    +"               Fernwaffen, Munition, Sonstiges, Unzuordbar\n");
+
+  AD(({"platz"}), BS("Der Schrank bietet Platz fuer jede erdenkliche "
+    +"Waffengattung."));
+
+  AddCmd(({"sliste"}), "auflistung");
+}
+
+varargs int PreventInsert(object ob)
+{
+  // Keine Waffe? Hat in diesem Container nix verloren!
+  if( !ob->QueryProp(P_WEAPON_TYPE) && !ob->QueryProp(P_WC) &&
+      !ob->id("\nBumerang") && !ob->is_class_member(CL_AMMUNITION) &&
+      !ob->id("\ngorseilpfeil") )
+  {
+    write( BS("In "+name(WEN,1)+" kannst Du nur Waffen + Munition legen!"));
+    return 1;
+  }
+  else
+    return ::PreventInsert(ob);
+}
+
+int auflistung2(string was, string nf_str1, string nf_str2, int alles)
+{
+  string obshort, suche, *strs;
+  object *obs;
+  int i;
+  mapping suchobs;
+  switch(was)
+  {
+    case "munition"   : suche=WT_AMMU; break;
+    case "aexte"      : suche=WT_AXE; break;
+    case "keulen"     : suche=WT_CLUB; break;
+    case "messer"     : suche=WT_KNIFE; break;
+    case "fernwaffen" : suche=WT_RANGED_WEAPON; break;
+    case "speere"     : suche=WT_SPEAR; break;
+    case "schwerter"  : suche=WT_SWORD; break;
+    case "peitschen"  : suche=WT_WHIP; break;
+    case "sonstiges"  : suche=WT_MISC; break;
+    case "kampfstaebe": suche=WT_STAFF; break;
+    case "unzuordbar" : suche="unzuordbar"; break;
+    default           : write("Fehler: Ungueltiger Waffentyp. "
+                             +"Folgende Typen gibt es:\n"+nf_str1+nf_str2);
+                        return 1; break;
+  }
+  obs=all_inventory();
+  suchobs=([]);
+  for(i=0;i<sizeof(obs);i++)
+  {
+    obshort=to_string(obs[i]->QueryProp(P_SHORT));
+    if(obshort=="Ein Bumerang@@fliegt@@")
+      obshort="Ein Bumerang";
+    else if(obshort=="Nichts besonderes" || obshort=="0") // keine P_SHORT ?
+      obshort=obs[i]->Name(WER);     //   Hoffen wir mal dass das Obj. nen Namen hat.
+    if(obs[i]->QueryProp(P_WEAPON_TYPE)==suche ||  // Gesuchter WeaponType gefunden...
+       (!obs[i]->QueryProp(P_WEAPON_TYPE) && suche=="unzuordbar") )
+    {
+      if(suchobs[obshort])                         // P_SHORT des Obj. schon im mapping?
+      {
+        if( obs[i]->QueryProp(P_AMOUNT) )
+          suchobs[obshort]+=obs[i]->QueryProp(P_AMOUNT); //   Dann den Zaehler um Amount hoch!
+        else
+          suchobs[obshort]+=1;                     //   Dann den Zaehler um 1 hoch!
+      }
+      else
+      {
+        if( obs[i]->QueryProp(P_AMOUNT) )
+          suchobs+=([obshort: obs[i]->QueryProp(P_AMOUNT)]);
+        else
+          suchobs+=([obshort: 1]);
+      }
+    }
+  }
+  strs=m_indices(suchobs);
+  if(sizeof(strs))
+  {
+    if(!alles)
+    {
+      if(suche=="unzuordbar")
+        strall+=Name(WER,1)+" enthaelt folgende nicht zuordbare Waffen:\n";
+      else
+        strall+=Name(WER,1)+" enthaelt folgende "+CAP(was)+":\n";
+      strall+="------------------------------------------------------------\n";
+    }
+    else
+    {
+      if(suche=="unzuordbar")
+        strall+=ANSI_BOLD+"=== Nicht zuordbare Waffen:\n"+ANSI_NORMAL;
+      else
+        strall+=ANSI_BOLD+"=== "+CAP(was)+":\n"+ANSI_NORMAL;
+    }
+    for(i=0;i<sizeof(strs);i++)
+    {
+      if(suchobs[strs[i]] > 1)
+        strall+=strs[i]+". ("+suchobs[strs[i]]+")\n";
+      else
+        strall+=strs[i]+".\n";
+    }
+  }
+  else
+    if(!alles)
+      strall+=Name(WER,1)+" enthaelt keine "+CAP(was)+"!\n";
+  return 1;
+}
+
+int auflistung(string str)
+{
+  string *strs, ob_id, was, nf_str1,nf_str2;
+  strall="";
+  nf_str1="   (Schwerter, Speere, Aexte, Keulen, Kampfstaebe, Messer, Peitschen,\n"
+         +"    Fernwaffen, Munition, Sonstiges, Unzuordbar)\n";
+  nf_str2="Syntax: sliste [Objekt-Id] [Waffentyp]\n"
+         +"Bsp.:   sliste "+QueryProp(P_IDS)[1]+" "+"schwerter\n"
+         +"        sliste "+QueryProp(P_IDS)[1]+" alles\n";
+  notify_fail("Fehler: Ohne Parameter klappt das nicht.\n"+nf_str2);
+  if(!str) return 0;
+  if(present(str)==TO)   // Ueberpruefe, ob dieses Objekt gemeint ist!
+  {
+    write("Fehler: Es fehlt ein Waffentyp. Folgende Typen gibt es:\n"
+         +nf_str1+nf_str2);
+    return 1;
+  }
+  strs=old_explode(str, " ");
+  notify_fail("Fehler: Du musst eine gueltige Objekt-Id angeben!\n"
+             +nf_str2);
+  if( sizeof(strs) < 2 ) return 0;   // Nur 1 Par. und der ist nicht die Objekt-Id
+  if( sizeof(strs) == 2 )   // Anzahl Par. = 2
+  {
+    ob_id=strs[0];
+    was=strs[1];
+    if( IST_ZAHL(was) )     // Objekt-Id z.B. mit "schrank 2" angesprochen?
+      if(present(ob_id+" "+was)==TO)   // Falls dieses Objekt gemeint ist und kein 3. Par!
+      {
+        write("Fehler: Es fehlt ein Waffentyp. Folgende Typen gibt es:\n"
+              +nf_str1+nf_str2);
+        return 1;
+      }
+      else
+        return 0;
+  }
+  else if( sizeof(strs) == 3 )
+  {
+    ob_id=strs[0]+" "+strs[1];
+    was=strs[2];
+  }
+  else        // Anzahl erforderlicher Parameter ueberschritten!
+    return 0;
+  if(present(ob_id)!=TO)   // Ueberpruefe, ob auch dieses Objekt gemeint ist!
+    return 0;
+  if(QueryProp(P_CNT_STATUS)!=CNT_STATUS_OPEN)
+  {
+    write("Dazu muesste "+name(WER,1)+" geoeffnet sein.\n");
+    return 1;
+  }
+//--- Hier ist Parameterpruefung zu Ende.
+  was=LOWER(was);
+  if(was!="alles")
+    auflistung2(was, nf_str1, nf_str2, 0);
+  else
+  {
+    auflistung2("aexte", nf_str1, nf_str2, 1);
+    auflistung2("keulen", nf_str1, nf_str2, 1);
+    auflistung2("kampfstaebe", nf_str1, nf_str2, 1);
+    auflistung2("messer", nf_str1, nf_str2, 1);
+    auflistung2("speere", nf_str1, nf_str2, 1);
+    auflistung2("schwerter", nf_str1, nf_str2, 1);
+    auflistung2("peitschen", nf_str1, nf_str2, 1);
+    auflistung2("fernwaffen", nf_str1, nf_str2, 1);
+    auflistung2("munition", nf_str1, nf_str2, 1);
+    auflistung2("sonstiges", nf_str1, nf_str2, 1);
+    auflistung2("unzuordbar", nf_str1, nf_str2, 1);
+  }
+  TP->More(strall);
+  return 1;
+}
diff --git a/d/seher/haeuser/moebel/waffentruhe.c b/d/seher/haeuser/moebel/waffentruhe.c
new file mode 100644
index 0000000..098aaa7
--- /dev/null
+++ b/d/seher/haeuser/moebel/waffentruhe.c
@@ -0,0 +1,43 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Waffentruhe
+// Letzte Aenderung:    22.03.2001
+// Magier:              Swift
+//--------------------------------------------------------------------------------
+/* Changelog
+   * 21.05.2007, Zesstra
+     Da Waffenschrank und Waffentruhe sich praktisch nicht unterscheiden, bis
+     auf 10 Zeilen im Create, erbt nun die Waffentruhe den Waffenschrank und
+     ueberschreibt einfach die passenden Props.
+*/
+#pragma strong_types,rtt_checks
+
+#include "schrankladen.h"
+#include <ansi.h>
+#include <class.h>
+//inherit LADEN("swift_std_container");
+inherit LADEN("waffenschrank");
+
+#define VERSION_OBJ "7"
+
+protected void create() {
+  if (!clonep(TO)) return;
+  ::create();
+  SetProp(P_SHORT, "Eine Waffentruhe");
+  SetProp(P_LONG, BS(
+     "Diese Stahltruhe ist speziell zur Aufnahme von grossen Waffenmengen "
+     "gefertigt. Verschiedene Faecher fuer die einzelnen Waffentypen erleichtern "
+     "die Suche nach Deinem bevorzugten Waffentyp.")+"@@cnt_status@@");
+  RemoveId(({"waffenschrank", "holzschrank", "schrank"}));
+  AddId(({"waffentruhe", "stahltruhe", "truhe"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_NAME, "Waffentruhe");
+  SetProp(P_GENDER, FEMALE);  
+  SetProp(P_MATERIAL, ({MAT_MISC_METAL, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuchs mal mit: \"skommandos "+QueryProp(P_IDS)[1]+
+    "\" !\n");
+  AD(({"faecher"}), BS("Fuer jeden Waffentyp gibt es ein eigenes Fach, so dass "
+    "Du eine bessere Uebersicht hast."));
+  AD(({"platz"}), BS("Die Waffentruhe bietet Platz fuer jede erdenkliche "
+	"Waffengattung."));
+}
+
diff --git a/d/seher/haeuser/moebel/wasch.c b/d/seher/haeuser/moebel/wasch.c
new file mode 100644
index 0000000..7789350
--- /dev/null
+++ b/d/seher/haeuser/moebel/wasch.c
@@ -0,0 +1,220 @@
+// Name des Objects:    Waschobjekt
+// Letzte Aenderung:    01.04.2005
+// Magier:              Seleven
+// Dieses File gibt dem Spieler die Moeglichkeit, eine Waschgelegenheit in
+// seinem Seherhaus aufzustellen. Das beim Kommando wasch(e), dusch(e) laesst
+// sich die Medung ueberschreiben (Test per raum->QueryProp("h_commands"))
+// 01.07.2007 (Zesstra): GetOwner() definiert.
+//----------------------------------------------------------------------------
+#pragma strong_types,rtt_checks
+
+#include "schrankladen.h"
+inherit LADEN("swift_std_container");
+
+string *wcmd;
+string *objekte =
+  (({"\nstinkobj","\nzesstra_asche","seleve\nscheisse","\npopc",
+     "\nsspbollen","seleve\nstinkobj","\nq2p_gestank","ber\tram_skot",
+     "bambi\nschmutzigehaende"}));
+/* Wurzel(Klo/Imbiss), Zesstra(Vulkan/Illuminatos), Seleven(Klo/Nebelelfen),
+ * Seleven(Popcorn/Nebelelfen), Boing(Schleimschmeisser,SSP), STANDART
+ */
+
+static void wasch_fun();
+
+protected void create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+
+  wcmd = (({"wasche","wasch","dusche","dusch"}));
+  AddId(({"duschkabine","kabine","dusche","waschgelegenheit"}));
+  SetProp(P_NAME,"Duschkabine");
+  
+  SetProp("cnt_version_obj",1);
+
+  SetProp("obj_cmd",
+  "wasch(e), dusch(e)\n"+
+  "   Damit laesst sich das Stinken aus dem Imbissklo beseitigen.\n"+
+  "   Wenn kein Befehl 'wasch(e)' im Raum beschrieben ist, erfolgt eine\n"+
+  "   Standartmeldung, selbiges gilt fuer 'dusch(e)'.\n"+
+  "ssetz(e) waschbefehl\n"+
+  "   Damit kann man die Befehlsliste erweitern, die das Waschen \n"+
+  "   ausloesen\n"+
+  "sloesch(e) waschbefehl\n"+
+  "   Damit kann man einen Befehl aus der Liste loeschen\n"+
+  "waschbefehlslite, wbl"+
+  "   Zeigt eine Liste der Befehle an, die das Waschen ausloesen.\n");
+
+  SetProp(P_SHORT,"Eine Duschkabine");
+  SetProp(P_LONG,BS("Die Duschkabine hat einen Rahmen aus hellem Holz. "
+   "Dazwischen wurden matte Scheiben eingelassen, damit man niemanden beim "
+   "Duschen beobachten kann."));
+  AddDetail("rahmen","Der Rahmen ist aus hellem Holz.\n");
+  AddDetail(({"scheiben","scheibe"}),"Die Scheiben der Duschkabine sind "+
+   "matt und absolut undurchsichtig!\n");
+  AddDetail(({"duschen","beim duschen"}),
+   "Man kann niemanden beim Duschen beobachten.\n");
+
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_WEIGHT, 5000);         // Gewicht 5 Kg
+  SetProp(P_MAX_WEIGHT, 0);        // Soll nur n pseudo-Container
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 0);       // Kein Container :)
+  SetProp(P_VALUE, 0);             // Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Fals doch: zerstoeren!
+  SetProp(P_NOGET,
+    "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
+  SetProp(P_MATERIAL, ({MAT_MISC_WOOD, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuchs mal mit: \"skommandos "+QueryProp(P_IDS)[1]+"\" !\n");
+
+  AddCmd(wcmd,"cmd_waschen");
+  RemoveCmd(({"oeffne","oeffn","schliesse","schliess"}));
+  AddCmd(({"ssetze","ssetz"}),"cmd_setzen");
+  AddCmd(({"sloesche","sloesche"}),"cmd_loeschen");
+  AddCmd(({"wbl","waschbefehlsliste"}),"liste");
+}
+
+// Zesstra, 1.7.07, fuers Hoerrohr
+string GetOwner() {return "seleven";}
+
+varargs int PreventInsert(object ob) {return 1;} //Das ist kein Container :)
+
+static void wasch_fun()
+{
+  int i;
+  object ob;
+  i = sizeof(objekte);
+  i--;
+  while(i > -1)
+  {
+
+    if(!TP) return;
+    ob = present(objekte[i],TP);
+    while(ob)
+    {
+      if(query_once_interactive(ob))
+	  {
+	    tell_object(TP,BS("Du scheinst jemanden in Deinem Inv zu haben!\n"
+	     "Das sollte sich vielleicht ein Magier anschauen."));
+	    i--;
+	    return;
+      }
+      ob->remove(1);
+      if(present(objekte[i],TP));
+       ob = present(objekte[i],TP);
+    }
+    i--;
+
+  }
+}
+
+int cmd_waschen(string str)
+{
+  string verb;
+  verb = query_verb();
+  if(!environment()) return 0;
+  if( !erlaubt() )
+  {
+      write("Du solltest den Besitzer vielleicht um Erlaubnis bitten.\n");
+      return 1;
+  }
+  if(member(environment()->QueryProp("h_commands"),verb) == 0)
+  {
+    write(
+     "Du springst schnell unter die Dusche und waeschst Dich gruendlich.\n");
+    say(this_player()->Name(WER)+" springt schnell unter die Dusche.\n");
+    wasch_fun();
+    return 1;
+  }
+  wasch_fun();
+  return 1;
+}
+
+
+static int cmd_setzen(string str)
+{
+  string bef;
+  notify_fail("Moechtest Du einen Waschbefehl setzen?\n"+
+    "Syntax: ssetz(e) waschbefehl [befehl]\n");
+  if(!str || !stringp(str) || sizeof(str) == 0) return 0;
+  sscanf(str,"waschbefehl %s",bef);
+  if(!bef) return 0;
+  if(bef[sizeof(bef) .. sizeof(bef)-1] == "e")
+   bef = bef[sizeof(bef) .. sizeof(bef)-2];
+  notify_fail("Der Befehl steht schon auf der Liste.\n");
+  if(member(wcmd,bef) != -1) return 0;
+  wcmd += (({bef,bef+"e"}));
+  AddCmd(wcmd,"cmd_waschen");
+  write(BS("Du hast die Befehle "+bef+" und "+bef+"e an die Liste "
+   "angefuegt."));
+  return 1;
+}
+
+static int cmd_loeschen(string str)
+{
+  string bef;
+  notify_fail("Moechtest Du einen Waschbefehl loeschen?\n"+
+    "Syntax: sloesch(e) waschbefehl [befehl]\n");
+  if(!str || !stringp(str) || sizeof(str) == 0) return 0;
+  sscanf(str,"waschbefehl %s",bef);
+  if(!bef) return 0;
+  notify_fail("Der Befehl steht gar nicht auf der Liste.\n");
+  if(member(wcmd,bef) == -1) return 0;
+  wcmd -= (({bef}));
+  RemoveCmd(bef);
+  write(BS("Du hast die Befehle "+bef+" und "+bef+"e von der Liste "
+   "entfernt."));
+  return 1;
+}
+
+static int liste(string str)
+{
+  write("Du hast folgende Befehle auf der Waschliste stehen:\n"+
+   break_string(implode(wcmd,", "),78,3));
+  return 1;
+}
+
+
+
+// oeffnen und schliessen ausbauen.
+varargs int skommandos(string str)
+{
+  notify_fail( "Fehler: Dieser Befehl benoetigt eine gueltige Objekt-Id als Parameter.\n"
+    +"Beispiel: skommandos "+QueryProp(P_IDS)[1]+"\n");
+  if(!str) return 0;
+  if(present(str)!=TO ) // Bin ich gemeint?
+    return 0;
+  write("=============================================================================\n");
+  write("Aktuelle Version: "+QueryProp("cnt_version_std")+QueryProp("cnt_version_obj")+"\n");
+  write( BS(Name(WER,1)+" kann nur in diesem Seherhaus verwendet werden, "
+    +"da "+QueryPronoun(WER)+" speziell dafuer gekauft wurde. "+CAP(QueryPronoun(WER))
+    +" verfuegt ueber folgende Kommandos:")
+    +"-----------------------------------------------------------------------------\n"
+    +"serlaube [Objekt-Id] [Spielername|\"hausfreunde\"|\"zweities\"]\n"
+    +"  Erlaubt Personen, "+name(WEN,1)+" mitzubenutzen.\n"
+    +"  serlaube + Objekt-Id (ohne Spielername/hausfreunde)\n"
+    +"  listet alle Personen mit Zugriff auf "+name(WEN,1)+"\n\n"
+    +"verschiebe [Objekt-Id] nach [Ausgang]\n"
+    +"  Damit kannst Du "+name(WEN,1)+" innerhalb Deines Seherhauses verschieben.\n\n"
+  //Seleven 07.12.2005
+    +"sverstecke [Objekt-Id]\n"
+    +"  Damit machst Du "+name(WEN,1)+" unsichtbar.\n"
+    +"shole [Objekt-Id] hervor\n"
+    +"  Damit machst Du "+name(WEN,1)+" wieder sichtbar.\n"
+  // Bis hier
+    +"zertruemmer [Objekt-Id]\n"
+    +"  Damit zerstoerst Du "+name(WEN,1)+".\n\n");
+
+
+  if( QueryProp("obj_cmd") )
+    write( QueryProp("obj_cmd")+"\n" );
+
+  write("[Objekt-Id] muss eine gueltige Id sein, in diesem Fall z.B. "
+      +QueryProp(P_IDS)[1]+"\n");
+  write("=============================================================================\n");
+
+
+  return 1;
+}
+
diff --git a/d/seher/haeuser/moebel/zauberkiste.c b/d/seher/haeuser/moebel/zauberkiste.c
new file mode 100644
index 0000000..ecf6f74
--- /dev/null
+++ b/d/seher/haeuser/moebel/zauberkiste.c
@@ -0,0 +1,56 @@
+//--------------------------------------------------------------------------------
+// Name des Objects:    Zauberkiste
+// Letzte Aenderung:    10.05.2002
+// Magier:              Vanion
+// Was:                 Die ID fuer die Kompos der Zauberer umgestellt.
+//--------------------------------------------------------------------------------
+#pragma strong_types,rtt_checks
+
+#include "schrankladen.h"
+
+// Fuer die IDs
+#include "/p/zauberer/zauberer.h"
+
+inherit LADEN("swift_std_container");
+
+#define VERSION_OBJ "3"
+
+protected void create()
+{
+  if (!clonep(TO)) return;
+  swift_std_container::create();
+  SetProp(P_SHORT, "Eine Zauberkiste");
+  SetProp(P_LONG, BS(
+     "Eine kleine Kiste aus Birnbaumholz. Da diesem seltenen Holz selbst "
+    +"magische Faehigkeiten nachgesagt werden, ist es wohl das ideale Material, "
+    +"um die teilweise gefaehrlichen Komponenten eines Zauberers darin "
+    +"unterzubringen.")+"@@cnt_status@@");
+  AddId(({"zauberkiste", "kiste"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_NAME, "Zauberkiste");
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_WEIGHT, 5000);         // Gewicht 5 Kg
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 100);     // Mehr sollte nicht sein, lt. Tiamak.
+  SetProp(P_VALUE, 0);             // Kein materieller Wert. Ist eh nicht verkaufbar.
+  SetProp(P_NOBUY, 1);             // Wird im Laden zerstoert, falls er verkauft wird.
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+" haftet wie magisch am Boden.\n");
+  SetProp(P_MATERIAL, ({MAT_PEAR_WOOD, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuchs mal mit: \"skommandos "+QueryProp(P_IDS)[1]+"\" !\n");
+
+  AD(({"holz", "birnbaumholz"}), BS("Fast koenntest Du meinen, dass "+name(WER,1)
+    +" lebt!"));
+}
+
+varargs int PreventInsert(object ob)
+{
+  // Keine Zauberkompo? Hat in diesem Container nix verloren!
+  if( !ob->id(Z_ID_ZUTAT) )
+  {
+    write( BS("In "+name(WEN,1)+" kannst Du nur Zauberer-Komponenten legen!"));
+    return 1;
+  }
+  else
+    return ::PreventInsert(ob);
+}
diff --git a/d/seher/haeuser/queue.c b/d/seher/haeuser/queue.c
new file mode 100644
index 0000000..bf2ca56
--- /dev/null
+++ b/d/seher/haeuser/queue.c
@@ -0,0 +1,433 @@
+/*
+ * queue.c -- Eine Schlange, an der man anstehen kann
+ *
+ * (c) 1994 Wargon@MorgenGrauen
+ *
+ */
+
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include "queue.h"
+
+#define TP this_player()
+
+inherit "std/thing/moving";
+inherit "std/room";
+
+static mapping peopleInQueue;
+static object lastMoved;
+
+/* Spielerkommendos */
+static int anstellen(string str);
+static int draengeln(string str);
+
+/* Spieler in Schlange einfuegen/aus Schlange entfernen */
+static void enqueue(object ob);
+private void leaveQueue(object ob, int silent);
+
+private object *queuedPeople();
+private int queueScan();
+private object adjust(int thresh);
+
+/* werden bei "schlafe ein" bzw. "ende" aufgerufen */
+void BecomesNetDead(object player);
+void PlayerQuit(object player);
+
+create()
+{
+  if (IS_BLUE(this_object())) return;
+
+  ::create();
+
+  SetProp( P_INDOORS, 1 );
+  SetProp( P_LIGHT, 1 );
+  SetProp( P_GENDER, FEMALE );
+
+  SetProp( P_SHORT, "Eine Schlange" );
+  SetProp( P_LONG, "Du siehst eine Schlange.\n" );
+  SetProp( P_INT_SHORT, "In der Schlange" );
+  SetProp( P_INT_LONG, "Du stehst in einer Schlange.\n" );
+  SetProp( P_NAME, "Schlange" );
+  SetProp( P_TRANSPARENT, 0 );
+
+// Netztote werden beim disconnecten eh aus der Schlange entfernt.
+// Hiermit wird die Ausgabe des Hinweises unterdrueckt.
+  SetProp( P_NETDEAD_INFO, 1 );
+
+  AddId( ({ "queue", "schlange" }) );
+
+  peopleInQueue = ([ ]);
+  lastMoved = 0;
+
+// Defaults setzen
+  SetProp( Q_CYCLE_TIME, 15 );
+  SetProp( Q_DEST, "/gilden/abenteurer" );
+  SetProp( Q_LENGTH, 3 );
+  SetProp( Q_SUSPEND, 0 );
+  SetProp( Q_MSGS, ({ "@@wer@@ ist an der Reihe!", "Du bist jetzt an der Reihe!",
+      "Der Naechste ist an der Reihe, und die anderen ruecken auf.",
+      "Die Schlange rueckt weiter. Sogleich stellt sich jemand neu an." }) );
+
+  AddSpecialExit( "raustreten", "raustreten" );
+
+  AddCmd( ({ "anstellen", "stell", "stelle" }), "anstellen" );
+  AddCmd( ({ "draengeln", "draengle", "draengel", "vordraengeln" }), "draengeln" );
+}
+
+int clean_up(int i) { return 0; }
+
+int
+remove()
+{
+  string *wer;
+  mixed raus;
+  int i;
+
+  while( remove_call_out( "rotate" ) != -1 );
+  while( remove_call_out( "funny" ) != -1 );
+
+  if (clonep(this_object())) {
+    if (!(raus = environment()))
+      raus = "/gilden/abenteurer";
+
+    if (i = sizeof(wer = m_indices(peopleInQueue))) {
+      tell_room(this_object(), "Die Schlange loest sich auf.\n" );
+
+      while ((--i)>0)
+  if (peopleInQueue[wer[i],1])
+    peopleInQueue[wer[i],1]->move(raus, M_GO | M_SILENT | M_NO_SHOW);
+    }
+  }
+  return ::remove();
+}
+
+string
+_query_long()
+{
+  string ret, add;
+  mixed *people, ob;
+  closure n;
+
+  people = filter(all_inventory(this_object()), #'interactive/*'*/);
+
+  ret = Query(P_LONG);
+  add = "In der Schlange stehen " + QueryProp(Q_LENGTH) + " Leute.";
+  if (sizeof(people)) {
+    n = lambda( ({ 'ob }), ({#'call_other, 'ob, "Name", WER}) );
+    people = map(people, n);
+    add += " Unter anderem steh";
+    if (sizeof(people) == 1)
+      add += ( "t dort " + people[0] + "." );
+    else {
+      add += ( "en dort " + implode(people[0..<2], ", ") +
+         " und " + people[<1] + "." );
+    }
+  }
+  return ret + break_string(add, 76);
+}
+
+string
+_query_int_long()
+{
+  string ret, add;
+  int act, cur;
+
+  ret = Query(P_INT_LONG);
+  add = "";
+  act = peopleInQueue[getuid(TP), 0];
+  cur = QueryProp(Q_LENGTH);
+
+  if (act == 0)
+    add += ( "Du bist als naechster an der Reihe. Hinter Dir"
+      +" stehen noch " + (cur - 1) +" Leute an." );
+  else {
+    if (act == 1)
+      add += "Es ist noch jemand vor Dir dran.";
+    else
+      add += ( "Es sind noch " + act + " Leute vor Dir dran." );
+
+    if ((cur = cur - act -1) > 0) {
+      if (cur == 1)
+  add += " Es steht noch jemand hinter Dir.";
+      else
+  add += ( " Hinter Dir stehen noch " + cur +" Leute an." );
+    }
+  }
+  return ret + break_string(add, 76);
+}
+
+int _query_qLength()
+{
+  return Query(Q_LENGTH) + sizeof(filter(all_inventory(this_object()), #'interactive/*'*/));
+}
+
+static int
+anstellen(string str)
+{
+  object tp;
+  string *part;
+
+  if (query_verb() != "anstellen") {
+    notify_fail( "stelle was?\n" );
+    if (!str || str == "")
+      return 0;
+
+    part = old_explode(str, " ");
+    if (part[0] != "an" && part[<1] != "an") {
+      notify_fail( "Syntax: stelle an <schlange> an\n" );
+      return 0;
+    }
+    if (!id(implode(part[1..<2], " ")))
+      return 0;
+  }
+  enqueue(TP);
+  return 1;
+}
+
+static int
+raustreten()
+{
+  if (environment(this_object())) {
+    leaveQueue(TP, 0);
+    return 1;
+  }
+  return 0;
+}
+
+static int
+draengeln(string str)
+{
+  int prob;
+  string *people;
+
+  if (environment(TP) != this_object())
+    return 0;
+
+  prob = random(11);
+
+  if (10 == prob) {
+    say( TP->Name(WER) + " will sich vordraengeln. Zur Strafe wird "
+      +TP->QueryPronoun(WER) +" von der auf-\n"
+      +"gebrachten Menge aus der Schlange hinausgeworfen. Du grinst Dir eins.\n" );
+    write( "Du draengelst Dich vor, wirst aber von der aufgebrachten "
+      +"Menge aus der\nSchlange herausgedraengt.\n" );
+    leaveQueue(TP, 0);
+  }
+  else if (prob < 8) {
+    say( TP->Name(WER) + " will sich vordraengeln.\n" );
+    write( "Du willst Dich vordraengeln.\n" );
+    tell_room(this_object(), sprintf("Es kommen Rufe auf: %s\n",
+       ({ "He! Was soll denn das? Unerhoert!",
+    "Also wirklich! Unverschaemtheit sowas!",
+    "...soll'n das? Mistbacke!",
+    "Jau, soweit kommts noch! Ich glaub' es geht los!",
+    "Wieder mal typisch "+TP->QueryProp(P_RACESTRING)[WER]+"!",
+    "Wenn sich das jeder erlauben wuerde...",
+    "Ne, ne, das geht aber nicht! Mal langsam, "+
+       (TP->QueryProp(P_GENDER)==MALE ? "Buerschchen":"Frollein")+"!",
+    "POLIZEI! HILFAEEEH!!! Also die Jugend von heute..." })[prob] ));
+    say( capitalize(TP->QueryPronoun())+" ueberlegt es sich anders.\n" );
+    write( "Du ueberlegst es Dir anders.\n" );
+  }
+  else {
+    say( TP->Name(WER) + " draengelt sich vor.\n" );
+    write( "Du draengelst Dich vor.\n" );
+    prob = peopleInQueue[getuid(TP)];
+    m_delete(peopleInQueue, getuid(TP));
+    for(people = m_indices(peopleInQueue); sizeof(people); people = people[1..] ) {
+      if(peopleInQueue[people[0],0] < prob)
+      peopleInQueue[people[0],0]++;
+    }
+    peopleInQueue += ([ getuid(TP) : 0; TP ]);
+  }
+  return 1;
+}
+
+static void
+enqueue(object ob)
+{
+  int i;
+
+  if (environment(ob) == this_object()) {
+    say(ob->Name(WER) + " stellt sich wieder hinten an.\n", ob );
+    tell_object(ob, "Du stellst Dich wieder hinten an.\n" );
+
+    i = peopleInQueue[getuid(ob),0];
+    m_delete(peopleInQueue, getuid(ob));
+    adjust(i);
+    peopleInQueue += ([ getuid(ob) : QueryProp(Q_LENGTH)-1; ob ]);
+  }
+  else {
+    peopleInQueue += ([ getuid(ob) : QueryProp(Q_LENGTH); ob ]);
+    ob->move(this_object(), M_NOCHECK, 0, "stellt sich an "+name(WEM)+" an",
+      "stellt sich hinten an");
+  }
+  if (find_call_out("rotate") == -1)
+    call_out("rotate", QueryProp(Q_CYCLE_TIME));
+}
+
+private void
+leaveQueue(object ob, int silent)
+{
+  int i;
+
+  if (silent)
+    ob->move(environment(this_object()), M_NOCHECK | M_SILENT);
+  else
+    ob->move(environment(this_object()), M_NOCHECK, 0, "verlaesst die Schlange",
+    "verlaesst "+name(WEN)+" und steht jetzt neben Dir" );
+
+  i = peopleInQueue[getuid(ob), 0];
+
+  m_delete(peopleInQueue, getuid(ob));
+
+  if (sizeof(peopleInQueue))
+    adjust(i);
+  else {
+    while(remove_call_out("rotate") != -1);
+    while(remove_call_out("funny") != -1);
+  }
+}
+
+private int
+queueScan()
+{
+  string *peop;
+  object act;
+  int i, a;
+
+  peop = m_indices(peopleInQueue);
+
+  for (i=sizeof(peop)-1; i>=0; i--) {
+    if (!objectp(act = peopleInQueue[peop[i],1]) ||
+  (environment(act) != this_object())) {
+      a = peopleInQueue[peop[i], 0];
+      m_delete(peopleInQueue, peop[i]);
+      adjust(a);
+    }
+  }
+
+  return sizeof(peopleInQueue);
+}
+
+private object
+adjust(int thresh)
+{
+  string *peop;
+  object ret;
+  int cnt, i;
+
+  ret = 0;
+  peop = m_indices(peopleInQueue);
+
+  for (i=sizeof(peop)-1; i>=0; i--) {
+    cnt = peopleInQueue[peop[i],0];
+    if (cnt > thresh)
+      cnt--;
+    if (cnt < 0)
+      ret = peopleInQueue[peop[i],1];
+    peopleInQueue[peop[i],0] = cnt;
+  }
+  return ret;
+}
+
+rotate()
+{
+  object ich;
+  string oth, *msgs;
+  int rotating, inDest;
+
+  rotating = 1;
+
+  if (QueryProp(Q_SUSPEND)) {
+    if (lastMoved) {
+      rotating = 0;
+      inDest = (object_name(environment(lastMoved)) == QueryProp(Q_DEST));
+      if (!interactive(lastMoved) || !inDest )
+  rotating = 1;
+      if (interactive(lastMoved) && inDest && environment() && query_idle(lastMoved) > 180) {
+  tell_object(lastMoved, "Du wirst rausgeworfen.\n");
+  lastMoved->move(environment(), M_GO, 0, "idlet herein");
+  rotating = 1;
+      }
+    }
+  }
+  if (rotating) {
+    queueScan();
+    msgs = QueryProp(Q_MSGS);
+    if (ich = adjust(-1)) {
+      oth = implode(explode(msgs[QMSG_OTHER], "@@wer@@"), ich->name(WER) );
+      oth = implode(explode(oth, "@@wessen@@"), ich->name(WESSEN) );
+      oth = implode(explode(oth, "@@wem@@"), ich->name(WEM) );
+      oth = implode(explode(oth, "@@wen@@"), ich->name(WEN) );
+      tell_room(this_object(), capitalize(break_string(oth, 78)), ({ ich }) );
+      tell_object(ich, break_string(msgs[QMSG_ME], 78) );
+      tell_room(environment(), break_string(msgs[QMSG_OUTSIDE], 78) );
+      ich->move(QueryProp(Q_DEST), M_GO);
+      lastMoved = ich;
+      m_delete(peopleInQueue, getuid(ich));
+      if (!sizeof(peopleInQueue))
+  return;
+    }
+    else
+      tell_room(this_object(), break_string(msgs[QMSG_DEFAULT], 78) );
+  }
+
+  if (random(100) < 20)
+    call_out("funny", random(QueryProp(Q_CYCLE_TIME)) );
+
+  call_out("rotate", QueryProp(Q_CYCLE_TIME));
+}
+
+funny()
+{
+  string m;
+
+  switch(random(7)) {
+    case 0:
+      m= "Jemand vor Dir laesst tierisch einen fahren. Seltsamerweise wirft man\n"
+  +"aber Dir missbilligende Blicke zu.\n";
+      break;
+    case 1:
+      m= "Von hinten draengelt wer! Wuetend drehst Du Dich um, schaust hoch, schaust\n"
+  +"hoeher, schaust noch hoeher... und schon ist Deine Wut verflogen!\n";
+      break;
+    case 2:
+      m= "Vor Langeweile popelt jemand vor Dir in der Nase. Das quietschende Ge-\n"
+  +"raeusch laesst Dir das Blut in den Adern gefrieren.\n";
+      break;
+    case 3:
+      m= "Ein fliegender Haendler landet neben der Schlange und bietet Kurzwaren\n"
+  +"feil. Da aber niemand etwas kaufen will, hebt er kurz darauf wieder ab\n"
+  +"und fliegt zu einer anderen Schlange, um dort sein Glueck zu versuchen.\n";
+      break;
+    case 4:
+      m= "Vom hinteren Ende der Schlange sickert das Geruecht durch, dass der\n"
+  +"Schalter heute geschlossen ist. Du hoffst instaendig, dass dies nicht\n"
+  +"wahr ist.\n";
+      break;
+    case 5:
+      m= "Fasziniert beobachtest Du, wie eine Spinne zwischen Deinem Vordermann und\n"
+  +"der Wand ein kunstvolles Netz spinnt.\n";
+      break;
+    case 6:
+      m= "Boah, iss dat langweilich...\n";
+      break;
+  }
+  tell_room(this_object(), m);
+}
+
+void
+BecomesNetDead(object player)
+{
+  if(present(player,this_object()))leaveQueue(player, 0);
+}
+
+void
+PlayerQuit(object player)
+{
+  if(present(player,this_player()))leaveQueue(player, 1);
+}
+
diff --git a/d/seher/haeuser/queue.h b/d/seher/haeuser/queue.h
new file mode 100644
index 0000000..3f2283e
--- /dev/null
+++ b/d/seher/haeuser/queue.h
@@ -0,0 +1,34 @@
+#ifndef _QUEUE_H_
+#define _QUEUE_H_
+
+// Zeit, bis der naechste dran ist (Default: 15 Sekunden)
+#define Q_CYCLE_TIME "qCycleTime"
+
+// Zielraum der Schlange (Default: "/gilden/abenteurer")
+#define Q_DEST	     "qDest"
+
+// Laenge der Schlange (Default: 3)
+#define Q_LENGTH     "qLength"
+
+// Falls ungleich Null, wird gewartet, bis der zuletzt bewegte Spieler
+// den Zielraum verlassen hat, bevor es in der Schlange weitergeht
+#define Q_SUSPEND    "qSuspend"
+
+// Meldungen, die beim Aufruecken ausgegeben werden. Vierelementiges Array:
+// 1. Meldung, wenn ein Spieler an der Reihe ist
+// 2. Meldung fuer den Spieler, der an der Reihe ist
+// 3. Meldung, wenn ein Schlangesteher an der Reihe ist
+// 4. Meldung fuer Leute ausserhalb der Schlange
+// In die erste Meldung kann mit @@wer@@, @@wessen@@, @@wem@@ und @@wen@@ der
+// Name des Spielers eingearbeitet werden.
+// (Default: ({ "@@wer@@ ist an der Reihe!", "Du bist jetzt an der Reihe!",
+//    "Der Naechste ist an der Reihe, und die anderen ruecken auf.",
+//    "Die Schlange rueckt weiter. Sogleich stellt sich jemand neu an." }) )
+#define Q_MSGS	     "qMsgs"
+
+#define QMSG_OTHER   0
+#define QMSG_ME      1
+#define QMSG_DEFAULT 2
+#define QMSG_OUTSIDE 3
+
+#endif /* _QUEUE_H_ */
diff --git a/d/seher/haeuser/raum.c b/d/seher/haeuser/raum.c
new file mode 100644
index 0000000..7fd0c55
--- /dev/null
+++ b/d/seher/haeuser/raum.c
@@ -0,0 +1,1840 @@
+//  raum.c  -- Das Rohgeruest eines Seherhauses.
+//
+//  Grundobjekt (c) 1994 Boing@MorgenGrauen
+//  Abschliessen und Rauswerfen von Jof
+//  Fuer Aenderungen ab dem 06.10.94 verantwortlich: Wargon
+//  Ab 03.02.98 Wurzel
+//
+// $Id: raum.c,v 1.5 2003/11/15 14:03:58 mud Exp $
+#pragma strong_types,rtt_checks
+
+#include "haus.h"
+
+inherit "/std/room";
+inherit USERCMD;    // selbstdefinierte Kommandos
+inherit LOSA;       // Load() und Save()
+inherit "/mail/nedit";
+
+#include <wizlevels.h>
+#include <properties.h>
+#include <defines.h>
+#include <moving.h>
+
+static mixed detail;
+static string owner;
+static int flag, csaved;
+static int raumNr;
+static string *ausgaenge = ({ "oben", "norden", "nordosten", "osten", "suedosten",
+                  "unten", "sueden", "suedwesten", "westen", "nordwesten" });
+
+// Prototypes fuer Funktionen aus LOSA
+varargs void Save(int crunched);
+void Load();
+
+// Prototype
+// Falls noetig, mache einen Notausgang, falls nicht loesche vorhandenen
+void make_emergency_exit();
+
+// ersetzt @@ durch **
+private string normstr(string str)
+{
+  return implode(explode(str,"@@"),"**");
+}
+
+// liefert kommagetrennte Teile ohne zusaetzliche Leerzeichen
+// "Bla blubb ,  foo bar,blubber" => ({ "Bla blubb", "foo bar", "blubber" })
+private string *brk(string str)
+{
+  string *t1;
+
+  t1 = explode(str, ",");
+  map(t1, #'trim /*'*/);
+  return t1;
+}
+
+// teste Befehl
+private int befCheck(string bef)
+{
+  // Befehl enthaelt Leerzeichen => return 0
+  if (member(bef, ' ') > -1)
+    return 0;
+
+  // Befehl ist bereits Kommando (ausser Oeffnen/Schliessen)
+  // oder Richtung inclusive "raus" in die kein Ausgang fuehrt
+  // => return 0
+  if (member(m_indices(QueryProp(P_COMMANDS)) -
+                       ({"oeffne","schliess","schliesse"}), bef) > -1
+      || (member(ausgaenge+({"raus"}), bef) > -1
+          && member(m_indices(QueryProp(P_EXITS)), bef) == -1))
+    return 0;
+
+  return 1;
+}
+
+// Test auf Hausbesitzer
+int tp_owner_check()
+{
+  if(getuid(this_player()) != owner)
+  {
+    notify_fail( "Das ist nicht Dein Haus!\n" );
+    return 0;
+  }
+  return 1;
+}
+
+// Test auf Hausbesitzer oder Person mit Erlaubnis
+int allowed_check(object _test)
+{
+   string *ext;
+
+   ext = VERWALTER->HausProp(owner, HP_ALLOWED) + ({ capitalize(owner) });
+   if(member(ext, capitalize(getuid(_test))) < 0)
+   {
+     return 0;
+   }
+   else
+   {
+     return 1;
+   }
+}
+
+// Funktion ersetzt durch tp_owner_check() und allowed_check(object _test)
+// falls allowed == 0 => test ob this_player() Hausbesitzer
+// falls allowed != 0 => test ob allowed/this_player() Hausbesitzer
+//                       oder Person mit Erlaubnis
+deprecated varargs int owner_check(mixed allowed)
+{
+  object test;
+
+  if(objectp(allowed) && query_once_interactive(allowed))
+  {
+    // Parameter ist Spieler
+    test = allowed;
+  }
+  else
+  {
+    // ansonsten TP
+    test = this_player();
+  }
+  
+  if(allowed)
+  {
+    // Test auf Hausbesitzer oder Person mit Erlaubnis
+    if(!allowed_check(test))
+    {
+      notify_fail( "Das darfst Du in diesem Haus nicht!\n" );
+      return 0;
+    }
+  }
+  else if(getuid(test) != owner)
+  {
+    // Test auf Hausbesitzer
+    notify_fail( "Das ist nicht Dein Haus!\n" );
+    return 0;
+  }
+  // Erlaubnis OK
+  return 1;
+}
+
+// Gestaltet Meldung aus Texten in einem Array:
+// array leer               -> melde Text in none
+// array hat ein Element    -> melde Text in one und ersetze %s durch Element
+// array hat viele Elemente -> melde Text in many und danach
+//                             sortierte Aufzaehlung der Elemente
+// flag gesetztes Bit 2: Aufzaehlung nur mit Komma, kein 'und'
+// flag gesetztes Bit 1: Meldung wird als String returnt, sonst per write ausgegeben
+varargs string arr_out(mixed array, string none, string one, string many, int flag)
+{
+  string tmp, lastsep;
+  mixed arr;
+
+  switch(sizeof(array))
+  {
+    case 0:
+      tmp = none;
+      break;
+    case 1:
+      tmp = sprintf(one, array[0]);
+      break;
+    default:
+      tmp = many;
+      arr = sort_array(array, #'> /*'*/);
+      lastsep = (flag & 2) ? ", " : " und ";
+      tmp += CountUp(arr, ", ", lastsep)+".";
+      break;
+  }
+  if (flag & 1)
+    return(break_string(tmp, 75, 0, 1));
+  else
+    write(break_string(tmp, 75, 0, 1));
+
+  return 0;
+}
+
+protected void create()
+{
+  if (!clonep(this_object()) && object_name(this_object())==(RAUM)) {
+      set_next_reset(-1);
+      return;
+  }
+  room::create();
+  usercmd::create();
+  losa::create();
+
+  Set(P_INT_LONG, SAVE, F_MODE_AS);
+  Set(P_INT_SHORT, SAVE, F_MODE_AS);
+  Set(P_EXITS, SAVE, F_MODE_AS);
+  Set(P_DETAILS, SAVE, F_MODE_AS);
+  Set(P_READ_DETAILS, SAVE, F_MODE_AS);
+  Set(P_LIGHT, SAVE, F_MODE_AS);
+
+  SetProp(P_IDS, ({"raum","sehe\rhaus"}));
+  SetProp(P_NAME, "Haus");
+  SetProp(P_GENDER, 0);
+  SetProp(P_TRANSPARENT, 0);
+  SetProp(P_NOGET, 1);
+  SetProp(P_LIGHT, 1);
+  SetProp(P_INDOORS, 1);
+
+  AddCmd( ({ "beschreib", "beschreibe" }), "beschreiben" );
+  AddCmd("uebersicht", "uebersicht");
+  AddCmd("ausgang", "ausgang");
+  AddCmd( ({"loesche", "loesch"}), "loesch");
+  AddCmd( ({"wirf", "werf", "werfe" }), "wirf");
+  AddCmd( ({"sperr", "sperre"}), "sperren");
+  AddCmd( ({"kopier", "kopiere" }), "kopieren");
+  AddCmd("licht", "licht");
+  AddCmd( ({"aendere", "aender" }), "aendern");
+  AddCmd( ({"meldung", "meldungen"}), "report");
+}
+
+void init()
+{
+	object haus;
+  
+  room::init();
+  nedit::init_rescue();
+
+  // Magier betritt fremdes Seherhaus
+  if (query_once_interactive(PL) && IS_LEARNER(PL) && getuid(PL) != owner) {
+    if (PL->QueryProp(P_INVIS))
+      // Magier invis => im Raum melden
+      tell_room(this_object(),
+          capitalize(getuid(PL))+" betritt unsichtbar den Raum.\n",
+          ({PL}));
+
+    if ((haus = load_object(HAUSNAME(owner))) &&
+        (haus->QueryProp(H_DOORSTAT) & D_LOCKED))
+      // Tuer ist zu => Magier einen Hinweis geben
+      tell_object(PL, " ******* ACHTUNG *********************************\n *\n * "+
+        capitalize(owner)+" moechte vielleicht ungestoert bleiben!\n *\n"
+        " ******* ACHTUNG *********************************\n");
+  }
+}
+  
+int PreventInsertLiving(object ob)
+{  
+  return 0;
+}
+
+// Gegenstaende kommen in den Raum
+void NotifyInsert(object ob, object oldenv)
+{
+	object *found;
+
+	if (!objectp(ob)) return;
+	
+	if (ob->QueryProp(H_FURNITURE)!=0)
+	{ 
+    // das was reinkam ist ein autoload Moebelstueck
+    // betrachte alle schon vorhandenen autoload Moebelstuecke
+      found = filter_objects(all_inventory(this_object()),
+		        "QueryProp", H_FURNITURE);
+	  if (sizeof(found)>=MAX_FURNITURE_PER_ROOM)        
+      {
+      // Maximal-Anzahl vorhandener autoload Seherhausmoebel ist schon
+      // erreicht
+	    tell_object(this_player(), break_string(
+	      "Ein Raum kann nur "+MAX_FURNITURE_PER_ROOM+" rebootfeste "
+	      "Moebel speichern. Hier befinden sich schon "+
+	      CountUp(map_objects(found, "name"))+"."));
+	    return 0;
+      }
+      
+      queued_save(); // Speichern vorbereiten (falls mehrere Objekte 
+                     // gedroppt werden, wird so ein overflow verhindert.
+	}
+}
+
+// Gegenstaende verlassen den Raum
+void NotifyLeave(object ob, object dest)
+{
+	if (!ob &&!objectp(ob)) return 0;
+	
+  // rebootfestes Moebelstueck verlaesst den Raum
+	if (ob->QueryProp(H_FURNITURE)!=0)
+	{	
+    // Speichern vorbereiten (falls mehrere Objekte 
+    // gedroppt werden, wird so ein overflow verhindert.
+		queued_save();
+	}
+	return 0;
+}
+
+void
+reset()
+{
+  room::reset();
+  losa::reset();
+  // Wenn ein Notausgang vorhanden ist, checken, ob der noch noetig ist.
+  if (member(m_indices(QueryProp(P_EXITS)),"notausgang")!=-1)
+  {
+      make_emergency_exit();
+  }
+}
+
+int clean_up(int arg)
+{
+  mixed inv;
+
+  if (arg > 1)
+    return 1;
+
+  losa::reset();  // Evtl. gepackt speichern...
+
+  // nichts im Raum oder nur leere Seherhaustruhe => Raum kann weg
+  if ((sizeof(inv=all_inventory()) == 0) ||
+      (sizeof(inv) == 1 && inv[0]->id(TRUHE) &&
+       sizeof(all_inventory(inv[0]))==0))
+    remove(1);
+
+  return 0;
+}
+
+// Haeufungen von gleichen Objekten sind hier egal => nix tun
+protected varargs void remove_multiple(int limit) {}
+
+
+// Spieler wird im Raum Netztot
+void BecomesNetDead(object pl)
+{
+//  Nicht erlaubte Spieler werden rausgeworfen, alle anderen bleiben drin.
+  if(!allowed_check(pl))
+    pl->move(VERWALTER->HausProp(owner, HP_ENV), M_GO, 0, "schlafwandelt heraus", "schlafwandelt herein" );
+}
+
+
+/* Ist dieser Raum ein Ausgang? */
+int is_exit(string path)
+{ 
+  return (sizeof(path) > 4) && (path[<5..] == "raum0");
+}
+
+/*Pruefung, ob es einen Hausausgang gibt*/
+int room_has_exit() {
+
+    string room = object_name(this_object());
+  // sind wir schon in einem Hauptraum mit Ausgang?
+    if (is_exit(room)) return 1;
+
+    mapping r_todo = ([room]);
+    mapping r_done = ([:0]);
+    /* Die Schleife hat maximal 9 * Anzahl der verb. Haeuser Durchlaeufe */
+    while (sizeof(r_todo))
+    {
+      mapping r_exits = ([]);
+
+      // naechsten Raumpfad abarbeiten, d.h. dessen Ausgaenge betrachten
+      room = m_indices(r_todo)[0];
+      // abgearbeiteten Raumpfad von r_todo nach r_done schieben
+      r_done += ([room]);
+      m_delete(r_todo, room);
+
+      // alle Ausgaenge betrachten
+      foreach(string command, string subpath : room->QueryProp(P_EXITS))
+      {
+        // Notausgaenge nicht betrachten, da 'echte' Ausgaenge gesucht sind
+        if(command != "notausgang")
+          // alle anderen Ausgaenge sammeln
+          r_exits += ([ subpath ]);
+      }
+      // nur Raumpfade die noch nicht abgearbeitet sind testen
+      r_exits -= r_done;
+      // ist da ein Hauptraum (der dann Ausgang liefert) dabei?
+      if (sizeof(filter_indices(r_exits, #'is_exit/*'*/)))
+        return 1;
+
+      // neue Raumpfade in die TODO-Liste aufnehmen
+      r_todo += r_exits;
+    }
+    return 0;
+}
+
+// damit man Spieler nicht durch Entfernen von Ausgaengen einsperren kann
+// werden bei Bedarf Notausgaenge in den Hauptraum ergaenzt
+void make_emergency_exit()
+{
+  // Ist der Raum weder Hauptraum mit Ausgang noch fuehrt ein Weg dorthin?
+  // dann Notausgang machen
+  if(!room_has_exit())
+  {
+    // tell_room(this_object(),"DEBUG: Ein Notausgang wurde angelegt.\n");
+    room::AddExit("notausgang",
+      "durch den Notausgang#"+to_string(this_object())[0..<2]+"0");
+  }
+  else
+  {
+    // nicht mehr benoetigten Notausgang entfernen
+    room::RemoveExit("notausgang");
+  }
+}
+
+// ruft direkt das geerbte AddExit auf, ohne ggf. einen Notausgang zu
+// erzeugen. Das wird vom Haus und vom Verwalter benutzt, um Ausgaenge zu
+// erzeugen. Das ist noetig, weil der Notausgangmechanismus noch nicht
+// funktoniert, wenn der Raum gerade geladen wird (hat noch nicht den
+// endgueltigen Namen) und der Check waere ohnehin unnoetig.
+public void AddExitNoCheck(mixed cmd, mixed dest)
+{
+  room::AddExit(cmd, dest);
+}
+
+// Wird benutzt beim Laden von Raeumen, um alle Ausgaenge zu loeschen. Dabei
+// darf _kein_ Notausgang erstellt werden.
+void RemoveExitNoCheck(mixed cmd)
+{
+  room::RemoveExit(cmd);
+}
+
+// beim Ausgang setzten testen ob vorhanderner Notausgang noch benoetigt wird
+void AddExit(mixed cmd, mixed dest)
+{
+  room::AddExit(cmd, dest);
+
+  if (member(m_indices(QueryProp(P_EXITS)),"notausgang")!=-1)
+  {
+    // loescht Notausgang falls nicht mehr benoetigt
+     make_emergency_exit();
+  }
+}
+
+// beim Ausgang loeschen testen ob Notausgang benoetigt wird
+void RemoveExit(mixed cmd)
+{
+  room::RemoveExit(cmd);
+  // setzt Notausgang falls benoetigt
+  make_emergency_exit();
+}
+
+// der Langbeschreibung werden alle unsichtbaren Magier
+// (ausgenommen Seherhausbesitzer) in Klammern angefuegt
+varargs string int_long(mixed viewer, mixed viewpoint, int flags)
+{
+  string ret;
+  object *inv;
+  string *tmp;
+
+  ret = ::int_long(viewer, viewpoint, flags);
+
+  // nur was machen wenn der Betrachter im Raum ist
+  if (!ret || !objectp(viewer) || environment(viewer) != this_object())
+    return ret;
+
+  if(viewpoint == 0)
+    viewpoint = ({ viewer });
+  else if(!pointerp(viewpoint))
+    viewpoint = ({ viewpoint });
+
+  // alle Lebewesen ausser denen aus deren Sicht betrachtet wird
+  inv = filter(all_inventory(this_object()),#'interactive) - viewpoint;
+  foreach(object pl : inv)
+  {
+    // raussuchen wer ausser Seherhausbesitzer unsichtbar ist
+    if(pl && IS_LEARNER(pl) && pl->QueryProp(P_INVIS) && getuid(pl)!=owner)
+    {
+      // Name in Klammer sammeln
+      tmp += ({ "("+capitalize(getuid(pl))+")" });
+    }
+  }
+  return ret + break_string(CountUp(tmp), 78);
+}
+/*
+// TODO: Testen ob das hier nicht das Standard-Verhalten ist
+int lies(string str)
+{
+  string txt;
+
+  notify_fail("Was moechtest Du lesen?\n");
+  if (!str) return 0;
+
+  if (this_player()->CannotSee())
+    return 1;
+  if (txt = QueryProp(P_READ_DETAILS)[str]) {
+    this_player()->More(txt);
+    return 1;
+  }
+  return 0;
+}
+*/
+// Aktion Lebewesen oder Gegenstaende aus dem Haus werfen
+int wirf(string str)
+{
+  string wen, args;
+  object pl, target, *list, tp, to;
+
+  tp = this_player();
+  to = this_object();
+  args = tp->_unparsed_args(1);
+
+  // klappt nur vor Ort und mit der passenden Syntax
+  if ((environment(tp)!=to) ||
+      !args || args == "" ||
+      (sscanf(args,"%s raus",wen) != 1) )
+    return 0;
+  // Raum, in dem das Haus steht, ermitteln
+  target=find_object(VERWALTER->HausProp(owner, HP_ENV));
+  if (!target) {
+    notify_fail("Dieses Haus steht leider gerade in einem "
+                "Fehler im Raum-Zeit-Gefuege.\n");
+    return 0;
+  }
+  // Rauswerfen darf nur der Besitzer
+  if (!tp_owner_check()) {
+    notify_fail("Aber Du kannst doch niemanden aus dem Haus von "+capitalize(owner)+" werfen!\n");
+    return 0;
+  }
+  if (wen=="alle") {
+    // alle Lebewesen ausser tp (== Hausbesitzer)
+    list=filter(all_inventory(to),#'living)-({tp,0});//')
+    if (sizeof(list)==0) {
+      notify_fail("Aber Du bist doch allein in Deinem Haus!\n");
+      return 0;
+    }
+  } else if (wen == "alles") {
+    // alle Gegenstaende ausser Lebewesen und Moebel
+    // (Seherhaustruhe, Autoloader-Moebel oder Seherhaus-Moebel)
+    list = filter(all_inventory(to),
+                  function int(object ob)
+                  {
+                      return objectp(ob) &&
+                             !living(ob) &&
+                             !ob->id(TRUHE) &&
+                          // TODO Test auf nicht Seherhausmoebel von August
+                             strstr(load_name(ob),"/d/seher/haeuser/moebel/")
+                               == 0;
+                  } );
+
+    if (sizeof(list)==0) {
+      notify_fail("Aber hier ist nichts zum wegwerfen!\n");
+      return 0;
+    }
+  } else {
+    pl=present(wen,to);
+    if (!pl) {
+      notify_fail("So jemanden sehe ich hier nicht.\n");
+      return 0;
+    }
+    else if (pl->id(TRUHE) ||
+             // TODO Test auf Seherhausmoebel von August
+              strstr(load_name(pl),"/d/seher/haeuser/moebel/")==0
+             ) {
+      notify_fail("Du kannst "+pl->name(WEN)+" nicht wegwerfen!\n" );
+      return 0;
+    }
+    list=({pl});
+  }
+  string msg_wen = sprintf("%s wirft Dich aus %s Haus.\n",
+                           tp->Name(WER),
+                           tp->QueryPossPronoun(NEUTER,WEM));
+  // fuer alle rauszuwerfenden Opfer Meldungen machen und rausbewegen
+  foreach(object ob : list)
+  {
+    tell_object(ob, msg_wen);
+    tell_room(target,
+        sprintf("%s kommt in hohem Bogen aus dem Haus von %s geflogen.\n",
+                 ob->Name(WER),tp->name(WEM)));
+    ob->move(target,M_SILENT|M_GO);
+    tell_room(to,sprintf("%s wirft %s aus %s Haus.\n",
+                    tp->Name(WER),
+                    ob->name(WEN),tp->QueryPossPronoun(NEUTER,WEM)),
+              ({tp}));
+    printf("Du wirfst %s aus Deinem Haus.\n",ob->name(WEN));
+    // Verfolger abmelden, damit sie nicht gleich zurueckkommen
+    // TODO wenn man einzelne Lebewesen rauswirft kann das ja auch
+    // ein Verfolger von einem anderen Gast sein...
+    tp->RemovePursuer(ob);
+  }
+  return 1;
+}
+
+// Besitzer und Raumnummer fuer diesen Raum setzen
+varargs string SetOwner(string o, int num)
+{
+  // Default Kurz- und Langbeschreibung setzen
+  SetProp(P_INT_SHORT, "Im Haus von "+capitalize(o));
+  SetProp(P_INT_LONG, "Ein total leerer Raum.\n");
+  // Raumnummer und Besitzer merken
+  raumNr = num;
+  return owner = o;
+}
+
+// Liefert den Besitzer
+// falls withNr != 0 mit angehaengter Raumnummer
+varargs string QueryOwner(int withNr)
+{
+  return (withNr ? owner+raumNr : owner);
+}
+
+// Prototype
+static int befEingabe(string *bef);
+
+// Aktion zum Beschreiben des Raumes
+varargs int
+beschreiben(string str, int f)
+{
+  string *parts;
+  int sp, ret;
+
+  // nur der Besitzer darf
+  if(!tp_owner_check())
+    return 0;
+
+  if (!f && (!(str=UP_ARGS(this_player())) || str == "")) {
+    notify_fail("Was willst Du denn beschreiben?\n" );
+    return 0;
+  }
+
+  sp = sizeof(parts = old_explode(str, " "));
+  // je nachdem was beschrieben wird, setze detail und flag
+  // und starte damit Editor, bzw. behandle Befehle extra
+  detail = 0;
+  flag = f;
+
+  switch(parts[0][0..3]) {
+    case "raum":      // Lang- oder Kurzbeschreibung
+    case "haus":
+      if (sp == 1 || parts[1] == "lang")
+    flag |= LANG;
+      else if (parts[1] == "kurz")
+    flag |= KURZ;
+      printf("Bitte %sbeschreibung des %s eingeben.\n", (flag & LANG ? "Lang" : "Kurz"), (flag & AUSSEN ? "Hauses" : "Raumes") );
+      break;
+    case "deta":      // Details
+      if (sp==1) {
+    notify_fail("Welches Detail willst Du denn beschreiben?\n");
+    return 0;
+      }
+      flag |= DETAIL;
+      str = implode(parts[1..]," ");
+      write( "Bitte Beschreibung fuer '"+str+"' eingeben.\n");
+      break;
+    case "lesb":      // lesbare Details
+      notify_fail("Welches lesbare Detail willst Du denn beschreiben?\n");
+      if (sp == 1) return 0;
+      if (parts[1] == "details" || parts[1] == "detail") {
+    if (sp == 2) return 0;
+    str = implode(parts[2..]," ");
+      }
+      else
+    str = implode(parts[1..]," ");
+      flag |= RDETAIL;
+      write( "Bitte Beschreibung fuer '"+str+"' eingeben.\n");
+      break;
+    case "befe":      // Befehle
+      ret = 0;
+      if (sp == 1)
+    notify_fail("Welchen Befehl willst Du denn beschreiben?\n");
+      else
+    ret = befEingabe(brk(implode(parts[1..]," ")));
+      return ret;
+      break;
+    default:
+      notify_fail("Das kannst Du nicht beschreiben! Eine Liste der Dinge, die Du hier\n"
+         +"beschreiben kannst, erhaeltst Du mit 'hilfe beschreibe'.\n" );
+      return 0;
+      break;
+  }
+  detail = brk(str);
+  write( "(Beenden mit . oder **, Abbruch mit ~q)\n" );
+  nedit( "beschreibung" );
+  return 1;
+}
+
+// nedit von beschreibe xxx
+static void beschreibung(string str)
+{
+  if (!str) {
+    write("Nichts geaendert!\n");
+    return;
+  }
+
+  str = normstr(str);
+
+  if (flag & LANG)
+  {
+    // Langbeschreibung
+    if (sizeof(explode(str,"\n")) > 100
+        || sizeof(str) > 7800)
+    {
+      // ueber 100 Zeilen oder ueber 7800 Zeichen
+      write( "Das ist fuer eine Langbeschreibung zu lang!\n"
+             "Sorry, bitte denke Dir eine andere Langbeschreibung aus.\n" );
+      return;
+    }
+    if (flag & AUSSEN) {
+      // Langbeschreibung vom Haus
+      object haus;
+      haus = find_object(HAUSNAME(owner));
+      haus->SetProp(P_LONG, str);
+      haus->Save();
+    }
+    else
+      // Langbeschreibung von diesem Raum
+      SetProp(P_INT_LONG, str);
+  }
+  else if (flag & KURZ) {
+    // Kurzbeschreibung vom Raum
+    if (sizeof(old_explode(str,"\n")) > 1 || sizeof(old_explode(str,".")) > 2 || sizeof(str) > 75) {
+      write( "Das ist fuer eine Kurzbeschreibung zu lang!\nSorry, bitte denke Dir eine andere Kurzbeschreibung aus.\n" );
+      return;
+    }
+    else
+      // Vanion, 27.07.02, Bugfix
+      // Die Zeile buggte, wenn man "." oder "\n" oder "\n." oder sowas
+      // in str hat. (also z.B. bei "beschreibe raum kurz <return> .")
+      // SetProp(P_INT_SHORT, old_explode(old_explode(str,"\n")[0],".")[0]);
+      SetProp(P_INT_SHORT, explode(explode(str,"\n")[0],".")[0]);
+  }
+  else if (flag & DETAIL)
+    // Raum-Detail
+    AddDetail(detail, str);
+  else if (flag & RDETAIL)
+    // lesbares Raum-Detail
+    AddReadDetail(detail, str);
+  else {
+    write( "Huch! Unbekanntes Flag ("+flag+")... Sag mal "
+          + CountUp(MAINTAINER, ", ", " oder ")
+          + " Bescheid...\n");
+    return;
+  }
+
+  write("OK.\n");
+  Save();
+}
+
+// wird in beschreiben(str, int) 'beschreibe befehl' aufgerufen
+static int befEingabe(string *befehle)
+{
+  string* com = ({});
+
+  notify_fail("Kein Befehl zum Beschreiben uebrig... ;)\n");
+  foreach(string bef : befehle)
+  {
+    // schon vorhandener Befehl (ausser oeffnen/schlissen),
+    // Richtung ohne zugehoerigen Ausgang,
+    // oder Befehl enthaelt Leerzeichen 
+    if (!befCheck(bef))
+      write("Der Befehl '"+bef+"' kann nicht beschrieben werden!\n");
+    else
+      com += ({ bef });
+  }
+  if (!sizeof(com))
+    return 0;
+
+  arr_out(com, 0, "Zu beschreibender Befehl: %s",
+          "Zu beschreibende Befehle:\n");
+
+  write( "Bitte Parameter eingeben (evtl. durch Kommata getrennt).\n]");
+  input_to("getBefParam", 0, com);
+  return 1;
+}
+
+// input_to aus befEingabe(string) zu beschreibe befehl ...
+static void getBefParam(string param, string *bef)
+{
+  string txt = "Gib nun bitte den Text ein, der fuer diesen Befehl "
+      "ausgegeben werden soll.\n";
+
+  // Fuehrende und abschliessende Leerzeichen entfernen
+  if (param)
+    param = trim(param);
+
+  if (!param || param == "")
+    // kein Parameter, z.b. bei beschreibe befehl klopfe 
+    detail = ({ bef, "" });
+  else if (param == "@NF@" || param == "@nf@") {
+    // Parameter fuer notify fail zum Ersetzen von Wie bitte?
+    // z.B. bei beschreibe befehl druecke
+    // fuer danach Du kannst hier kein @PARA druecken, nur den Knopf!
+    detail = ({ bef, 1 });
+    txt = "Gib nun bitte den Text ein, der als Fehlermeldung "
+          "ausgegeben werden soll.\n@PARA dient dabei als Platzhalter fuer "
+          "die ungueltige Eingabe.\n";
+  }
+  else
+    // sonstige Parameter
+    // z.B. knopf, klingel bei beschreibe befehl druecke
+    detail = ({ bef }) + brk(lower_case(param));
+
+  printf(txt+"(Beenden mit . oder **, Abbruch mit ~q)\n");
+  nedit("getBefText");
+}
+
+// Prototype
+private string preparse(string str, string *invalid);
+
+// nedit fuer die Eingabe der Texte  (Fehlermeldung/Meldungen) fuer den Befehl
+static void getBefText(string text)
+{
+  string my, *txt, *warn;
+  mixed bef;
+
+  if (!text || text == "") {
+    write("** Abbruch! **\n");
+    detail = 0;
+    return;
+  }
+  // gemerktes Befehls-Array
+  bef = detail[0];
+
+  txt = old_explode(text, "@@\n");
+
+  warn = ({});
+
+  // Meldung an this_player() parsen und in warn falsche Platzhalter sammeln
+  my = preparse(txt[0], &warn);
+  string other = 0;
+  if (sizeof(txt) > 1)
+    // Meldung an andere parsen und in warn falsche Platzhalter sammeln
+    other = preparse(txt[1], &warn);
+
+  AddUserCmd(bef, (stringp(detail[1]) ? detail[1..] : ({ "@NF@" })), my, other);
+  Save();
+  arr_out(warn, "OK.", "WARNUNG! Ungueltiger Platzhalter: %s",
+               "WARNUNG! Ungueltige Platzhalter: ");
+
+  detail = 0;
+}
+
+// check, ob an Position pos in txt ein Buchstabe aus dem array choice steht
+// return 0 falls nicht, prefix + Position des Buchstabens in choice ansonsten
+// check_placeholder(({"R","S","M","N"}), 2, "WESSEN", "X"); -> X1
+string check_placeholder(string* choice, int pos, string txt, string prefix)
+{
+   int idx;
+    
+   if(sizeof(txt) < pos+1 ||
+      ((idx=member(choice, txt[pos..pos])) < 0))
+   {
+     return 0;
+   }
+   else
+   {
+     return prefix+to_string(idx);
+   }
+}
+
+// Dann drueckt @PWER den Knopf -> Dann drueckt @P0 den Knopf
+private string preparse(string str, string *invalid)
+{
+  string *txt;
+
+  txt = explode(str, "@");
+  // fuer jeden Textteil nach einem @
+  // suche Ersatz fuer den Begriff direkt nach dem @
+  // AN: wuerde es nicht theoretisch reichen, hier nur bis i>0
+  // runterzuzaehlen? Das erste Element des Arrays ist immer irrelevant, weil
+  // entweder Leerstring oder kein zu ersetzender Ausdruck.
+  for (int i=sizeof(txt)-1; i>=0; i--) {
+    int warn = 0;
+    string rpl = 0;
+    // falls Teil zu kurz nix ersetzen
+    if (sizeof(txt[i])<3)
+      continue;
+    // anhand der ersten Buchstaben, Ersatz bestimmen
+    // warn signalisiert, ob dies schiefging:
+    switch(txt[i][0..1]) {
+    case "WE":  // Name
+      // WER      -> W0
+      // WES(SEN) -> W1
+      // WEM      -> W2
+      // WEN      -> W3
+      rpl = check_placeholder(({"R","S","M","N"}), 2, txt[i], "W");  
+      warn = !rpl;
+      break;
+    case "PW":  // Personalpronomen
+      // PWER      -> P0
+      // PWES(SEN) -> P1
+      // PWEM      -> P2
+      // PWEN      -> P3
+      rpl = check_placeholder(({"R","S","M","N"}), 3, txt[i], "P");  
+      warn = !rpl;
+      break;
+    case "BN":  // Possessivpronomen
+    case "BM":
+    case "BF":
+      // BNSWER      -> B000  BMSWER      -> B010  BFSWER      -> B020
+      // BNSWES(SEN) -> B100  BMSWES(SEN) -> B110  BFSWES(SEN) -> B120
+      // BNSWEM      -> B200  BMSWEM      -> B210  BFSWEM      -> B220
+      // BNSWEN      -> B300  BMSWEN      -> B310  BFSWEN      -> B320
+      // 
+      // BNPWER      -> B001  BMPWER      -> B011  BFPWER      -> B021
+      // BNPWES(SEN) -> B101  BMPWES(SEN) -> B111  BFPWES(SEN) -> B121
+      // BNPWEM      -> B201  BMPWEM      -> B211  BFPWEM      -> B221
+      rpl = check_placeholder(({"R","S","M","N"}), 5, txt[i], "B");
+      warn = !rpl;
+      if(!warn)
+      {
+        rpl = check_placeholder(({"N","M","F"}), 1, txt[i], rpl);
+        warn = !rpl;
+        if(!warn)
+        {
+          rpl = check_placeholder(({"S","P"}), 2, txt[i], rpl);
+          warn = !rpl;
+        }
+      }
+      break;
+    case "PA":
+      // PARA -> F
+      // kein Ersatz, sondern Textteil hier direkt ersetzen:
+      if(sizeof(txt[i]) > 4)
+        txt[i] = "F"+txt[i][4..];
+      break;
+    default:
+      // kein Ersatz, nix aendern
+      warn = 0;
+      rpl = 0;
+    }
+
+    // falls Ersatz vorhanden, ersetze Pronomen durch ""+rpl und lasse den Rest t2[2] wie ist
+    if (rpl) {
+      string* t2;
+      warn = sizeof(t2 = regexplode(txt[i], "(WER|WESSEN|WEM|WEN)")) < 2;
+      if (!warn) {
+        t2[1] = rpl;
+        t2[0] = "";
+        txt[i] = implode(t2, "");
+      }
+    }
+    // falls es Probleme gab, diese merken
+    if (warn)
+      invalid += ({ "@"+old_explode(txt[i]," ")[0] });
+
+  } // for (i=sizeof(txt)-1; i>=0; i--)
+  // die eventuelle teilweise ersetzetn Teile wieder zusammenfuegen
+  return implode(txt, "@");
+}
+
+static void loesch_alles(string str)
+{
+  if (str == "ja" || str == "Ja") {
+    RemoveDetail(0);
+    RemoveReadDetail(0);
+    //SetProp(P_READ_DETAILS, ([]));
+    SetProp(H_COMMANDS, ([]));
+    write( "OK, alle Details, lesbaren Details und Befehle geloescht!\n" );
+    Save();
+  }
+  else
+    write( "Nichts geloescht!\n" );
+}
+
+static void loesch_etwas(string str, string prop)
+{
+  if (str == "ja" || str == "Ja") {
+    if ( prop == P_DETAILS )
+      RemoveDetail(0);
+    else if ( prop == P_READ_DETAILS )
+      RemoveReadDetail(0);
+    else
+      SetProp(prop, ([]));
+    write("OK.\n");
+    Save();
+  }
+  else
+    write( "Nichts geloescht!\n" );
+}
+
+int loesch(string str)
+{
+  string *s, *t, p, q;
+  int i, ret;
+  mapping com;
+
+  if (!tp_owner_check())
+    return 0;
+
+  if (!(str=UP_ARGS(this_player())) || str == "") {
+    notify_fail("Welches Detail oder welchen Befehl moechtest Du loeschen?\n");
+    return 0;
+  }
+
+  if (str == "alles") {
+    write( "Wirklich alles loeschen (ja/nein)?\n]");
+    input_to("loesch_alles");
+    return 1;
+  }
+
+  if(str=="meldungen") {
+    if(file_size(PATH+"rep/"+owner+".rep")>0) {
+      rm(PATH+"rep/"+owner+".rep");
+      write("Meldungen geloescht.\n");
+    }else{
+      write("Keine Meldungen gefunden.\n");
+    }
+    return 1;
+  }
+
+  s = brk(str);
+  s = ({ (t=old_explode(s[0], " "))[0] })+({ implode(t[1..]," ") })+s[1..];
+  ret = 1;
+  flag = 0;
+
+  switch(s[0]) {
+    case "detail":
+      s = s[1..];
+      flag |= DETAIL;
+      break;
+    case "lesbar":
+      flag |= RDETAIL;
+      s = s[1..];
+      break;
+    case "lesbares":
+    case "lesbare":
+      flag |= RDETAIL;
+      if (s[1][0..5] =="detail") {
+    s = ({ old_explode(s[1]," ")[<1] });
+    if (sizeof(s)>2)
+      s += s[2..];
+      }
+      else
+    s = s[1..];
+      break;
+    case "befehl":
+      s = s[1..];
+      break;
+    case "alle":
+      switch (s[1]) {
+      case "details":
+    q = "Details";
+    p = P_DETAILS;
+    break;
+      case "lesbaren details":
+    q = "lesbaren Details";
+    p = P_READ_DETAILS;
+    break;
+      case "befehle":
+    q = "Befehle";
+    p = H_COMMANDS;
+    break;
+      default:
+    write("Du kannst alle Befehle, alle Details und alle lesbaren Details loeschen!\n");
+    return 1;
+      }
+      printf("Wirklich alle %s loeschen (ja/nein)?\n]", q);
+      input_to("loesch_etwas", 0, p);
+      return 1;
+    default:
+      flag |= (DETAIL|RDETAIL);
+      ret = 0;  // Koennte auch ein Artikel in der Zeitung sein...
+      break;
+  }
+  for (i=sizeof(s)-1; i>=0; i--) {
+    if (!flag) {  // Befehl soll geloescht werden...
+      if (member(com=Query(H_COMMANDS), s[i])) {
+    com = m_copy_delete(com, s[i]);
+    write("Befehl '"+s[i]+"' wurde geloescht.\n");
+      }
+      else if (sizeof(t=old_explode(s[i], " ")) > 1 &&
+           member(com, t[0]) &&
+           member(com[t[0]], p=implode(t[1..], " "))) {
+    com[t[0]] = m_copy_delete(com[t[0]], p);
+    write("Befehl '"+s[i]+"' wurde geloescht.\n");
+      }
+      Set(H_COMMANDS, com);
+    }
+    else {
+      if (flag & DETAIL) {
+    if (!QueryProp(P_DETAILS)[s[i]])
+      notify_fail("Das Detail '"+s[i]+"' gibt es nicht.\n");
+    else {
+      RemoveDetail(s[i]);
+      write("Detail '"+s[i]+"' wurde geloescht.\n");
+      ret = 1;
+    }
+      }
+      if (flag & RDETAIL) {
+    if (!QueryProp(P_READ_DETAILS)[s[i]])
+      notify_fail("Das lesbare Detail '"+s[i]+"' gibt es nicht.\n");
+    else {
+      RemoveReadDetail(s[i]);
+      write("Lesbares Detail '"+s[i]+"' wurde geloescht.\n");
+      ret = 1;
+    }
+      }
+    }
+  }
+  Save();
+  return ret;
+}
+
+int ausgang(string str)
+{
+  int nr, maxNr, hin, zurueck;
+  string hier, da, ext;
+  closure hausProp;
+  mapping known_exits;
+
+  if (!tp_owner_check()) {
+    return 0;
+  }
+
+  hier = da = 0;
+  hausProp = symbol_function("HausProp",VERWALTER);
+
+  if (!(str=UP_ARGS(this_player())) ||
+      (sscanf(str, "%s %d", hier, nr) != 2 &&
+       sscanf(str, "%s %s %d",hier, ext, nr) != 3) ) {
+    notify_fail( "Syntax: ausgang <richtung> [name] <nr>\n" );
+    return 0;
+  }
+
+  if (ext) {
+    if (funcall(hausProp, ext, HP_ENV) != funcall(hausProp, owner, HP_ENV)) {
+      printf("Das Haus von %s steht nicht im gleichen Raum wie Dein Haus!\n",
+             capitalize(ext));
+      return 1;
+    }
+    else
+      da = RAUMNAME(ext, nr);
+
+    // der allowed_check() wird im Eingangsraum des Zielhauses aufgerufen,
+    // da wir von anderen Raumen noch nicht wissen, ob sie ueberhaupt
+    // existieren.
+    if (!(RAUMNAME(ext, 0)->allowed_check(this_player()))) {
+      printf("Du darfst keinen Ausgang von Deinem Haus zu dem von %s legen!\n",
+             capitalize(ext));
+      return 1;
+    }
+  }
+  else {
+    ext = owner;
+    da = RAUMNAME(ext, nr);
+  }
+
+  maxNr = funcall(hausProp, ext, HP_ROOMS);
+
+  if ( (hin = member(ausgaenge, lower_case(hier))) < 0) {
+    arr_out(ausgaenge, 0, 0, "Es sind nur folgende Ausgaenge moeglich:\n" );
+    return 1;
+  }
+  else
+    zurueck = (hin + sizeof(ausgaenge)/2) % sizeof(ausgaenge);
+
+  hier = RAUMNAME(owner, raumNr);
+
+  // Kopie des Ausgaenge-Mappings erzeugen
+  known_exits=deep_copy(QueryProp(P_EXITS));
+  // und den Notausgang entfernen. Somit bleiben nur die zu betrachtenden 
+  // Ausgaenge ueber.
+  known_exits["notausgang"]=0;
+
+  if (nr < 0 || nr > maxNr)
+    printf( "Die Nummer darf sich nur im Bereich zwischen 0 und %d bewegen!\n",
+            maxNr );
+  else if ( ext == owner && nr == raumNr)
+    printf( "Aber dies IST Raum %d!\n", raumNr );
+  else if (member(m_indices(known_exits), ausgaenge[hin]) != -1)
+    write( "Aus diesem Raum fuehrt schon ein Ausgang in diese Richtung!\n" );
+  //else if (member(m_values(QueryProp(P_EXITS)), da) != -1)
+  // Notausgang wird hier zwar geloescht, aber im AddExit
+  // gibt's eh einen neuen, so das noetig ist, V*
+  else if (member(m_values(known_exits), da) != -1)
+    printf( "Es gibt hier schon einen Ausgang zu Raum %d!\n", nr );
+  else if (member(m_indices(da->QueryProp(P_EXITS)), ausgaenge[zurueck]) != -1)
+    printf( "Es fuehrt schon irgendwo ein Ausgang in Richtung '%s'\n"
+            "nach Raum %d!\n", ausgaenge[hin], nr);
+  else {
+    AddExit( ausgaenge[hin], da );
+    Save();
+    da->AddExit(ausgaenge[zurueck], hier);
+    da->Save();
+    printf( "OK, der Ausgang '%s' zum Raum %d wurde eingerichtet.\n",
+            ausgaenge[hin], nr );
+  }
+  return 1;
+}
+
+int
+sperren(string str)
+{
+  mapping ex, cmds;
+  int hin, zurueck;
+
+  if (!tp_owner_check())
+    return 0;
+
+  if (!(str=UP_ARGS(this_player())) || str == "") {
+    notify_fail( "Syntax: sperre <ausgang>\n" );
+    return 0;
+  }
+  str = lower_case(str);
+  ex = QueryProp(P_EXITS);
+
+  if (raumNr == 0 && str == "raus") {
+    write( "Du kannst doch nicht Deine Haustuer loeschen!\n" );
+    return 1;
+  }
+  if (!member(ex,str) || (hin = member(ausgaenge,str)) < 0) {
+    printf( "Es gibt hier keinen Ausgang '%s'!\n", str);
+    return 1;
+  }
+  else
+    zurueck = (hin + sizeof(ausgaenge)/2) % sizeof(ausgaenge);
+
+  ex[str]->RemoveExit(ausgaenge[zurueck]);
+  tell_room(find_object(ex[str]), sprintf("Der Ausgang '%s' verschwindet ploetzlich...\n", ausgaenge[zurueck]));
+  cmds = ex[str]->QueryProp(H_COMMANDS);
+  cmds = m_copy_delete(cmds, ausgaenge[zurueck]);
+  ex[str]->SetProp(H_COMMANDS, cmds);
+  ex[str]->Save();
+  RemoveExit(str);
+  cmds = QueryProp(H_COMMANDS);
+  cmds = m_copy_delete(cmds, str);
+  SetProp(H_COMMANDS, cmds);
+  Save();
+  printf( "OK, der Ausgang '%s' wurde entfernt.\n", str );
+
+  Save();
+
+  return 1;
+}
+
+varargs int
+uebersicht(string dummy, string pre)
+{
+  string *xc, *xd, o, raus, str;
+  mixed *com;
+  mapping tmp;
+  int i,j,k;
+
+  if ( (getuid(this_player()) != owner) &&
+       !(PATH+"access_rights")->access_rights(geteuid(this_player()), "") )
+    return 0;
+
+  i = VERWALTER->HausProp(owner, HP_ROOMS);
+
+  if (i)
+    str = sprintf( "Dein Haus verfuegt ueber %d Raeume.\nDu stehst in Raum %d (%s).\n\n", i+1, raumNr, QueryProp(P_INT_SHORT) );
+  else
+    str = sprintf( "Dein Haus verfuegt ueber einen Raum (%s)\n\n", QueryProp(P_INT_SHORT));
+
+  str += arr_out(m_indices(QueryProp(P_DETAILS)),
+         "Du hast keine Details beschrieben.",
+         "Du hast das Detail '%s' beschrieben.",
+         "Du hast folgende Details beschrieben:\n", 1 );
+
+  str += ("\n" + arr_out(m_indices(QueryProp(P_READ_DETAILS)),
+         "Du hast keine lesbaren Details beschrieben.",
+         "Du hast das lesbare Detail '%s' beschrieben.",
+         "Du hast folgende lesbaren Details beschrieben:\n", 1 ) );
+
+  tmp = Query(H_COMMANDS);
+  xc = sort_array(m_indices(tmp),#'<);
+  if (!sizeof(xc))
+    str += ("\nDu hast keine Befehle beschrieben.\n");
+  else {
+    if (sizeof(xc) == 1 && sizeof(xd=m_indices(tmp[xc[0]])) == 1)
+      str += ("\nDu hast den Befehl '"+
+          xc[0]+((xd[0] == "") ? "" : " "+xd[0])+
+          "' beschrieben.\n");
+    else {
+      str += "\nDu hast folgende Befehle beschrieben:\n";
+
+      for (com = ({}), j=sizeof(xc)-1; j >= 0; j--) {
+    xd = sort_array(m_indices(tmp[xc[j]])-({"@NF@"}),#'>);
+    if ((sizeof(xd) > 1) && (xd[0] == "")) {
+      raus = "* "+xc[j]+", "+xc[j]+" ";
+      xd = xd[1..];
+    }
+    else
+      raus = "* "+xc[j]+" ";
+
+    str += arr_out(xd, "", raus+"%s", raus, 3);
+      }
+    }
+  }
+
+  raus = (member(QueryProp(P_EXITS),"raus") ? "raus: Nach draussen.\n" : 0 );
+  tmp = m_copy_delete(QueryProp(P_EXITS), "raus");
+  m_delete(tmp, "notausgang");
+  xc = m_indices(tmp);
+  xd = m_values(tmp);
+
+  if (!sizeof(xc) && !raus)
+    str += "\nES GIBT KEINE AUSGAENGE!\n";
+  else {
+    str +=  "\nEs gibt folgende Ausgaenge:\n";
+    for (i=sizeof(xc)-1; i>=0; i--)
+      str += sprintf( "%s: Nach Raum %d %s(%s).\n",
+             xc[i],
+             (j=to_int(xd[i][<1..])),
+             (((o=old_explode(xd[i],"/")[<1][0..<6])==owner) ?
+             "" : "von "+capitalize(o)+" "),
+             xd[i]->QueryProp(P_INT_SHORT) );
+  }
+  str += ((raus||"")+(pre||""));
+  this_player()->More(str);
+  return 1;
+}
+
+int kopieren(string str)
+{
+  string was, alt, n, *neu, *par, err;
+  mixed das;
+  mapping com;
+
+  if (!tp_owner_check())
+    return 0;
+
+  notify_fail("'kopiere detail <von> nach <nach>' oder\n"
+         +"'kopiere lesbares detail <von> nach <nach>' oder\n"
+         +"'kopiere befehl <befehl> [<parameter>] nach <befehl> [<parameter>]'!\n");
+
+  if (!(str=UP_ARGS(this_player())) || str == "")
+    return 0;
+
+  neu = old_explode(str, " ");
+  was = neu[0][0..5];
+  if (was == "detail" || was == "befehl")
+    str = implode(neu[1..], " ");
+  else if (was == "lesbar")
+    str = implode(neu[2..], " ");
+  else
+    return 0;
+
+  if (sscanf(str, "%s nach %s", alt, n) != 2)
+    return 0;
+
+  neu = brk(n);
+  switch(was) {
+    case "detail":
+      err = "Detail";
+      if (das = GetDetail(alt)) {
+    AddDetail(neu, das);
+    Save();
+      }
+      break;
+    case "lesbar":
+      err = "lesbares Detail";
+      if (das = QueryProp(P_READ_DETAILS)[alt]) {
+    AddReadDetail(neu, das);
+    Save();
+      }
+      break;
+    case "befehl":
+      err = "Befehl";
+      was = (par=old_explode(alt, " "))[0];
+      if (member(com=QueryProp(H_COMMANDS),was)) {
+    int i;
+    if (sizeof(par) == 1) { // <bef> nach <bef1,bef2,...>
+      das = com[was];
+      for (i=sizeof(neu)-1; i>=0; i--) {
+        if (befCheck(neu[i])) {
+          if (com[neu[i]])
+        com[neu[i]] += das;
+          else
+        com += ([ neu[i] : das ]);
+        }
+        else
+          write("Ungueltiger Befehl: '"+neu[i]+"'.\n");
+      }
+    }
+    else {        // <bef> <parameter> nach <bef1,bef2,...>
+      alt = implode(par[1..]-({""})," ");
+      if (das = com[was][alt]) {
+        for (i=sizeof(neu)-1; i>=0; i--) {
+          if (befCheck(neu[i])) {
+        das = ([ alt : com[was][alt];com[was][alt,1] ]);
+        if (com[neu[i]])
+          com[neu[i]] += das;
+        else
+          com += ([ neu[i] : das ]);
+          }
+          else {
+        par = old_explode(neu[i], " ");
+        n = par[0];
+        if (befCheck(n)) {
+          das = ([ implode(par[1..], " ") : com[was][alt];com[was][alt,1] ]);
+          if (com[n])
+            com[n] += das;
+          else
+            com += ([ n : das ]);
+        }
+        else
+          write("Ungueltiger Befehl: '"+neu[i]+"'.\n");
+          }
+        }
+      }
+    }
+    Save();
+      }
+      break;
+    default:
+      write( "Du kannst nur Details, lesbare Details und Befehle kopieren!\n" );
+      return 1;
+  }
+  if (!das)
+    printf( "Kann %s '%s' nicht finden!\n", err, alt);
+  else
+    write( "OK!\n" );
+
+  return 1;
+}
+
+int licht(string str)
+{
+  int ll, tl;
+
+  if (!allowed_check(this_player()))
+    return 0;
+
+  if (!str || (str != "an" && str != "aus")) {
+    notify_fail("Syntax: 'licht an' oder 'licht aus'\n");
+    return 0;
+  }
+
+  ll = QueryProp(P_LIGHT);
+  tl = PL->QueryProp(P_PLAYER_LIGHT);
+
+  switch(str) {
+  case "an":
+    if (tl > 0)
+      write("Aber es ist doch schon hell!\n");
+    else {
+      SetProp(P_LIGHT, 1);
+      tell_room(this_object(), "Es wird wieder hell.\n");
+    }
+    break;
+  case "aus":
+    if (tl <= 0)
+      write("Aber es ist doch schon dunkel!\n");
+    else {
+      SetProp(P_LIGHT, 0);
+      tell_room(this_object(), "Es wird dunkel.\n");
+    }
+    break;
+  }
+  return 1;
+}
+
+#define CASUS ({ "WER", "WESSEN", "WEM", "WEN" })
+static string rpXchg(string s)
+{
+  int c,p,g;
+
+  switch(s[0..1]) {
+    case "@W":
+      c = to_int(s[2..2]);
+      return ("@"+CASUS[c]);
+    case "@P":
+      c = to_int(s[2..2]);
+      return ("@P"+CASUS[c]);
+    case "@B":
+      c = to_int(s[2..2]);
+      g = to_int(s[3..3]);
+      p = to_int(s[4..4]);
+      return ("@B"+({"N", "M", "F"})[g]+({"S", "P"})[p]+CASUS[c]);
+    case "@F":
+      return "@PARA";
+  }
+  return s;
+}
+
+private string reParse(string s1, string s2)
+{
+  string *p;
+
+  if (s2)
+    s1 = s1+"@@\n"+s2;
+
+  p = regexplode(s1, "(@W[0-3]|@P[0-3]|@B[0-3][0-2][0-1])");
+  p = map(p, #'rpXchg);
+  return implode(p, "");
+}
+
+private string getPreText(string prop, string expr)
+{
+  mixed crunched;
+  int i;
+
+  crunched = VERWALTER->PCrunch(QueryProp(prop));
+  if (!crunched || !pointerp(crunched))
+    return 0;
+
+  if (prop == H_COMMANDS && strstr(expr, " ") < 0)
+    expr = expr+" ";
+
+  for (i=sizeof(crunched)-1; i>=0; i--)
+    if (member(crunched[i][0], expr) >= 0)
+      break;
+
+  if (i<0)
+    return 0;
+
+  detail = crunched[i][0];
+
+  if (prop == H_COMMANDS)
+    return reParse(crunched[i][1], crunched[i][2]);
+  else
+    return crunched[i][1];
+}
+
+varargs int
+aendern(string str, int f)
+{
+  string *parts, pre;
+  int sp, sr, ret;
+
+  if (!tp_owner_check())
+    return 0;
+
+  if (!f && (!(str=UP_ARGS(this_player())) || str == "")) {
+    notify_fail("Was willst Du denn aendern?\n" );
+    return 0;
+  }
+
+  sp = sizeof(parts = old_explode(str, " "));
+  sr = sizeof(brk(str));
+  detail = 0;
+  flag = f;
+
+  switch(parts[0][0..3]) {
+    case "raum":      // Lang- oder Kurzbeschreibung
+    case "haus":
+      if (sp == 1 || parts[1] == "lang")
+    flag |= LANG;
+      else if (parts[1] == "kurz") {
+    write("Nimm dazu doch bitte 'beschreibe'!\n");
+    return 1;
+      }
+      pre = ((flag & AUSSEN) ? (find_object(HAUSNAME(owner)))->QueryProp(P_LONG) : QueryProp(P_INT_LONG));
+      break;
+    case "meld":
+      if (file_size(REPFILE(owner)) > 0)
+    pre = read_file(REPFILE(owner));
+      else {
+    write("Ich finde keine Meldungen aus Deinem Haus!\n");
+    return 1;
+      }
+      flag |= REPORT;
+      break;
+    case "deta":      // Details
+      if (sp==1) {
+    notify_fail("Welches Detail willst Du denn aendern?\n");
+    return 0;
+      }
+      if (sr>1) {
+    notify_fail("Du kannst immer nur ein Detail aendern!\n");
+    return 0;
+      }
+      flag |= DETAIL;
+      pre = getPreText(P_DETAILS, implode(parts[1..], " "));
+      break;
+    case "lesb":      // lesbare Details
+      notify_fail("Welches lesbare Detail willst Du denn aendern?\n");
+      if (sp == 1) return 0;
+      if ((parts[1] == "details" || parts[1] == "detail") && (sp==2))
+    return 0;
+      if (sr>1) {
+    notify_fail("Du kannst immer nur ein lesbares Detail aendern!\n");
+    return 0;
+      }
+      flag |= RDETAIL;
+      pre = getPreText(P_READ_DETAILS, implode(parts[1..], " "));
+      break;
+    case "befe":      // Befehle
+      ret = 0;
+      if (sp == 1) {
+    notify_fail("Welchen Befehl willst Du denn aendern?\n");
+    return 0;
+      }
+      if (sr>1) {
+    notify_fail("Du kannst immer nur einen Befehl aendern!\n");
+    return 0;
+      }
+      flag |= BEFEHL;
+      pre = getPreText(H_COMMANDS, implode(parts[1..], " "));
+      break;
+    default:
+      notify_fail("Das kannst Du nicht aendern! Eine Liste der Dinge, die Du hier aendern\n"
+         +"kannst, erhaeltst Du mit 'hilfe aendere'.\n" );
+      return 0;
+      break;
+  }
+  if (!pre)
+    write("Hm, sowas ist hier noch nicht beschrieben...\n");
+  else {
+    write( "Aendere nun den Text.\n(Beenden mit . oder **, Abbruch mit ~q, Hilfe mit ~h)\n" );
+    nedit( "aenderung", pre );
+  }
+  return 1;
+}
+
+void aenderung(string str)
+{
+  string *warn;
+
+  if (!str) {
+    write("Nichts geaendert!\n");
+    return;
+  }
+
+  if (flag && !(flag & BEFEHL))
+    str = normstr(str);
+
+  warn = ({ });
+
+  if (flag & LANG) {
+    if (flag & AUSSEN) {
+      object haus;
+      haus = find_object(HAUSNAME(owner));
+      haus->SetProp(P_LONG, str);
+      haus->Save();
+    }
+    else
+      SetProp(P_INT_LONG, str);
+  }
+  else if (flag & DETAIL) {
+    if (str == "")
+      RemoveDetail(detail);
+    else
+      AddDetail(detail, str);
+  }
+  else if (flag & RDETAIL) {
+    if (str == "")
+      RemoveReadDetail(detail);
+    else
+      AddReadDetail(detail, str);
+  }
+  else if (flag & BEFEHL) {
+    if (str == "")
+      RemUserCmd(detail);
+    else {
+      string *s;
+
+      s = old_explode(preparse(str, &warn), "@@\n");
+      if (sizeof(s) > 1 && s[1] != "")
+    AddUserCmd(detail, 0, normstr(s[0]), normstr(s[1]));
+      else
+    AddUserCmd(detail, 0, normstr(s[0]), 0);
+    }
+  }
+  else if (flag & REPORT) {
+    rm(REPFILE(owner));
+    if (str != "")
+      write_file(REPFILE(owner), str);
+  }
+  else
+    write( "Huch! Unbekanntes Flag ("+flag+")... Sag mal Wargon Bescheid...\n");
+
+  arr_out(warn, "OK.", "WARNUNG! Ungueltiger Platzhalter: %s",
+               "WARNUNG! Ungueltige Platzhalter: ");
+  Save();
+}
+
+int SmartLog(string ofile, string typ, string msg, string date)
+{
+  object home;
+
+  // speichere Meldung im Rep-Log des Seherhaus-Besitzers
+  write_file(REPFILE(owner), sprintf("%s von %s in Raum %d (%s):\n%s\n",
+                     typ,
+                     capitalize(getuid(this_player())),
+                     raumNr,
+                     date,
+                     break_string(msg,78)));
+
+  if (IS_LEARNER(owner)) {
+    log_file("report/"+owner+".rep",
+         sprintf("MELDUNG von %s im Seherhaus, Raum %d (%s):\n"
+             +"Bitte zur Kenntnis nehmen! (Mit dem Befehl 'meldungen')  -Wargon\n",
+             capitalize(getuid(this_player())),
+             raumNr,
+             date));
+  }
+
+  // erhoehe im Hauptraum den Rep-Zaehler und speichere
+  home = load_object(RAUMNAME(owner,0));
+  home->Set(H_REPORT, home->Query(H_REPORT)+1);
+  home->Save();
+
+  return 1;
+}
+
+static int report(string str)
+{
+  string rep, *lines;
+  int rNum, l, s;
+
+  if (!allowed_check(this_player()))
+    return 0;
+
+  if (file_size(REPFILE(owner)) <= 0) {
+    write( "Keine Meldungen zu finden... Du bist wunschlos gluecklich.\n" );
+    return 1;
+  }
+  rep = read_file(REPFILE(owner));
+
+  if (!rep) {
+    write( "Oha! Die Datei mit den Meldungen ist zu gross! Sag doch bitte mal\n"
+      +"Wargon Bescheid!\n");
+    return 1;
+  }
+
+  if (str) {
+    string d, *new, *tmp, prev;
+    int nr, nextNr, m;
+
+    if (str == "hier")
+      rNum = raumNr;
+    else
+      rNum = to_int(str);
+
+    if (rNum > VERWALTER->HausProp(owner, HP_ROOMS)) {
+      write( "So viele Raeume hast Du gar nicht!\n");
+      return 1;
+    }
+
+    lines = old_explode(rep, "\n");
+    s = sizeof(lines);
+    for (l=0; prev == 0; l++)
+      if (sscanf(lines[l], "%s von %s in Raum %d %s:", d, d, nr, d)==4)
+    prev=lines[l];
+
+    for ( new = ({}), tmp=({}); l<s; l++) {
+      m=sscanf(lines[l], "%s von %s in Raum %d %s:", d, d, nextNr, d);
+      if (m != 4 && nr == rNum)
+    tmp += ({ lines[l] });
+
+      if (m==4) {
+    if (sizeof(tmp)) {
+      new = new + ({ prev }) + tmp;
+      tmp = ({});
+    }
+    nr = nextNr;
+    prev = lines[l];
+      }
+    }
+    if (sizeof(tmp))
+      new = new + ({prev}) + tmp;
+    rep = implode(new, "\n");
+  }
+
+  this_player()->More(rep);
+  return 1;
+}
+
+// $Log: raum.c,v $
+// Revision 1.5  2003/11/15 14:03:58  mud
+// Lichtaenderungen von Zook
+//
+// Revision 1.4  2003/02/17 20:00:00  mud
+// Im Reset wird nun getestet, ob der Raum einen Ausgang in einen Null-Raum
+// hat. Dies wurde notwengig, damit Spieler nicht in Seherhaeuser eingesperrt 
+// werden koennen. Die Funktionen AddExit(), RemoveExit() und Reset starten 
+// gegebenenfalls den Ausgangstest. Einige Funs mussten leicht angepasst 
+// werden.
+// Die Funktionen room_has_exit() und is_exit() wurden von Vardion@MG 
+// entwickelt und zur Verfuegung gestellt. - Vanion
+//
+// Revision 1.3  2001/02/04 21:21:34  mud
+// (brk,getBefParam): Vorkehrungen gegen fuehrende und schliessende
+// Leerzeichen in Befehlsparametern und anderen Listen.
+//
+// Revision 1.2  2001/01/01 18:17:47  mud
+// (ausgang): Wenn ein Ausgang zu einem anderen Seherhaus gelegt wird,
+// wird die Erlaubnis in dessen Eingangsraum abgefragt, und nicht in
+// dem angeforderten Zielraum (der Eingangsraum existiert auf jeden
+// Fall, der Zielraum vielleicht nicht).
+//
+// Revision 1.1.1.1  2000/08/20 20:22:42  mud
+// Ins CVS eingecheckt
+//
+// 04.02.98 Meldungen koennen geloescht werden.
+//
+// Revision 2.16  1997/11/15 19:33:23  Wargon
+// arr_out(), preparse(): kleine Bugfixes
+//
+// Revision 2.15  1997/10/06 15:24:38  Wargon
+// Unsichtbare Magier melden/anzeigen
+// Meldung beim Betreten abgeschlossener Haeuser fuer Magier
+//
+// Revision 2.14  1996/02/21  18:12:47  Wargon
+// SmartLog() rein, dafuer die eigenen Rueckmeldungsbefehle raus
+//
+// Revision 2.13  1995/10/31  12:56:16  Wargon
+// Rueckmeldungen fuer Objekte werden ans Spielerobjekt weitergegeben.
+//
+// Revision 2.12  1995/08/07  18:35:12  Wargon
+// Einige Bugs bei "aendere" behoben.
+//
+// Revision 2.11  1995/06/29  08:57:05  Wargon
+// Hausbesitzer, die schon Magier sind, bekommen bei Rueckmeldungen auch einen
+// Eintrag in ihr /log/report/xyz.rep-File
+// "licht an/aus" ist seit 2.9 drin ;)
+//
+// Revision 2.10  1995/06/28  08:59:57  Wargon
+// Neue Befehle aendere, meldungen fuer den Hausbesitzer.
+// typo, bug/fehler, idee werden dem Haus-.rep-File zugefuehrt.
+// Jetzt koennen die Seher ihre Typos selber fixen! ;^)
+//
+// Revision 2.9  1995/06/20  07:49:15  Wargon
+// *** empty log message ***
+//
+// Revision 2.8  1995/04/21  10:48:39  Wargon
+// Bugfix in beschreiben(), wenn die Hausaussenbeschreibung
+// verlangt wird (war schon seit Ewigkeiten buggy... ;)
+//
+// Revision 2.7  1995/04/21  08:55:32  Wargon
+// Load()/Save() und eigene Kommandos ausgelagert.
+// Kommandos koennen mit notify_fail() versehen werden.
+//
+// Revision 2.6  1995/03/07  13:55:36  Wargon
+// Add/RemUserCmd(), Beschreibungen werden bei reset()/clean_up()
+// gepackt gespeichert.
+// Bei Kommandos nur noch more(), wenn es auch noetig ist.
+//
+// Revision 2.5  1995/02/27  20:48:26  Wargon
+// Kleine Schoenheitsfehler in selbstdefinierten Befehlen beseitigt.
+//
+// Revision 2.4  1995/02/22  21:30:52  Wargon
+// Noch mehr Aenderungen an den Befehlen:
+// - Preparsing der Platzhalter
+// - Platzhalter fuer Possessivpronomen
+// - Meldung fuer Ausfuehrenden wird geMore()t
+// - Rassen- und Geschlechtespezifische Meldungen moeglich
+// - Auch fuer Ausgaenge koennen Befehle definiert werden
+//   (nur fuer existierende; wird der Ausgang gesperrt, wird auch
+//   der Befehl geloescht)
+// Im Zuge des Preparsings hat sich die Befehlauswertung etwas
+// vereinfacht.
+//
+// Revision 2.3  1995/02/20  22:15:44  Wargon
+// READ_DETAILS werden jetzt mit More() ausgegeben.
+// Selbstdefinierte Befehle: mit @PWER, ... koennen die Personalpronomina
+// eingebaut werden; Einbau jetzt auch in die Meldung fuer den Ausloeser
+// moeglich; _unparsed_args() in der Auswertung.
+//
+// Revision 2.2  1995/02/15  11:23:04  Wargon
+// NEU: Selbstdefinierbare Befehle.
+//
+// Revision 2.1  1995/02/04  15:02:36  Wargon
+// Die Truhe wird nun ueber die Property CHEST verwaltet. Der AddItem()-
+// Aufruf wurde deshalb von create() nach Load() verlegt. Geladen wird
+// sie nur, wenn das Load() von Hausverwalter erfolgte.
+// clean_up(), wenn Raum leer ist oder nur eine leere Truhe drin steht.
+//
+// Revision 2.0  1995/02/01  20:36:49  Wargon
+// Entruempelt und Massnahmen fuer _unparse_args() getroffen.
+
diff --git a/d/seher/haeuser/raum0.c b/d/seher/haeuser/raum0.c
new file mode 100644
index 0000000..0282818
--- /dev/null
+++ b/d/seher/haeuser/raum0.c
@@ -0,0 +1,173 @@
+//
+//  raum0.c  -- Der Eingangsraum des Hauses
+//
+//  Grundobjekt (c) 1994 Boing@MorgenGrauen
+//  Abschliessen und Rauswerfen von Jof
+//  Fuer Aenderungen ab dem 06.10.94 verantwortlich: Wargon
+//  Fuer Aenderungen ab dem 01.01.04 verantwortlich: Vanion
+//
+// $Date: 1996/02/21 18:13:41 $
+// $Revision: 2.5 $
+/* $Log: raum0.c,v $
+ * Revision 2.5  1996/02/21  18:13:41  Wargon
+ * *** empty log message ***
+ *
+ * Revision 2.4  1995/06/29  08:54:57  Wargon
+ * Neuer Befehl: "spion" zeigt, wer vor der Tuer steht.
+ *
+ * Revision 2.3  1995/06/28  08:59:25  Wargon
+ * init() meldet dem Hausbesitzer Spielerrueckmeldungen.
+ *
+ * Revision 2.2  1995/02/22  21:30:12  Wargon
+ * "uebersicht" jetzt auch fuer Wargon ueberall moeglich. ;)
+ *
+ * Revision 2.1  1995/02/04  14:31:47  Wargon
+ * AddItem(truhe) rausgenommen. Wird nun in Load() erledigt (siehe raum.c).
+ *
+ * Revision 2.0  1995/02/01  20:38:24  Wargon
+ * Von raum.c abgekoppelt.
+ *
+ */
+/*
+Letzte Aenderung: Vanion, 29. Feb 2004
+                 - "uebersicht erlaubt" zeigt nun nur an, wer erlaubt ist.
+*/
+
+#include "haus.h"
+#include <wizlevels.h>
+#include <properties.h>
+#include <moving.h>
+
+inherit RAUM;
+
+void create()
+{
+  if (!clonep(this_object()) && object_name(this_object())==(PATH+"raum0")) {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+
+  Set(H_REPORT, 0);
+  Set(H_REPORT, SAVE, F_MODE);
+
+  AddCmd( "erlaube", "erlauben" );
+  AddCmd( "verbiete", "verbieten" );
+  AddCmd( "oeffne", "oeffne" );
+  AddCmd( ({ "schliess", "schliesse" }), "schliesse" );
+  AddCmd( "spion", "spion" );
+}
+
+void notifyRep(int r)
+{
+  printf("Seit Deinem letzten Besuch gab es %s Rueckmeldung%s von anderen Spielern\nin Deinem Haus.\n", (r==1) ? "eine" : to_string(r), (r==1) ? "" : "en");
+}
+
+void init()
+{
+  int r;
+
+  ::init();
+
+  if (tp_owner_check() && r=Query(H_REPORT)) {
+    call_out("notifyRep", 0, r);
+    Set(H_REPORT, 0);
+    Save();
+  }
+}
+
+int oeffne(string str)
+{
+  return (HAUSNAME(QueryOwner()))->oeffne(str);
+}
+
+int schliesse(string str)
+{
+  return (HAUSNAME(QueryOwner()))->schliesse(str);
+}
+
+varargs int
+uebersicht(string str, string pre)
+{
+  if ( !(tp_owner_check() || IS_MAINTAINER(this_player())) )
+    return 0;
+
+  pre = "\n"+arr_out(VERWALTER->HausProp(QueryOwner(), HP_ALLOWED),
+	    "Nur Du kannst Dein Haus auf- und abschliessen.\n",
+	    "Du und %s koennen Dein Haus auf- und abschliessen.\n",
+	    "Neben Dir koennen noch folgende Personen Dein Haus auf- und abschliessen:\n", 1 );
+
+  if (str=="erlaubt")
+  {
+     this_player()->More(pre);
+     return 1;
+  }
+  
+  return ::uebersicht(str, pre);
+}
+
+int
+erlauben(string str)
+{
+  string *erlaubt;
+
+  if (!tp_owner_check()) {
+    notify_fail( "Du kannst hier gar nichts erlauben!\n" );
+    return 0;
+  }
+
+  if (!(str=UP_ARGS(this_player())) || str == "") {
+    notify_fail( "Wem willst denn das Auf- und Abschliessen Deines Hauses erlauben?\n" );
+    return 0;
+  }
+
+  erlaubt = VERWALTER->Erlaube(QueryOwner(),
+			       map((regexplode(str, "[, ]")-({","," ",""})), #'capitalize));
+  arr_out(erlaubt, "Nur Du kannst Dein Haus auf- und abschliessen.\n",
+		   "Du und %s koennen Dein Haus auf- und abschliessen.\n",
+		   "Neben Dir koennen noch folgende Personen Dein Haus auf- und abschliessen:\n" );
+  return 1;
+}
+
+int
+verbieten(string str)
+{
+  string *erlaubt;
+
+  if (!tp_owner_check()) {
+    notify_fail( "Du kannst hier gar nichts verbieten!\n" );
+    return 0;
+  }
+
+  if (!(str=UP_ARGS(this_player())) || str == "") {
+    notify_fail( "Wem willst denn das Auf- und Abschliessen Deines Hauses verbieten?\n" );
+    return 0;
+  }
+  erlaubt = VERWALTER->Verbiete(QueryOwner(),
+				map((regexplode(str, "[, ]")-({","," ",""})), #'capitalize));
+  arr_out(erlaubt, "Nur Du kannst Dein Haus auf- und abschliessen.\n",
+		   "Du und %s koennen Dein Haus auf- und abschliessen.\n",
+		   "Neben Dir koennen noch folgende Personen Dein Haus auf- und abschliessen:\n" );
+  return 1;
+}
+
+int
+spion(string str)
+{
+  object env, *inv;
+  string ow;
+
+  ow = QueryOwner();
+  str = VERWALTER->HausProp(ow, HP_ENV);
+  call_other(str, "???");
+  env = find_object(str);
+  inv = all_inventory(env);
+  if (env = present("\n"+ow+"haus", env))
+    inv -= ({ env });
+  if (sizeof(inv))
+    printf( "Vor dem Haus siehst Du:\n%s", make_invlist(this_player(), inv));
+  else
+    printf( "Vor dem Haus ist nichts und niemand zu sehen.\n" );
+
+  return 1;
+}
diff --git a/d/seher/haeuser/sb_antrag.c b/d/seher/haeuser/sb_antrag.c
new file mode 100644
index 0000000..1e6dd20
--- /dev/null
+++ b/d/seher/haeuser/sb_antrag.c
@@ -0,0 +1,175 @@
+#include <properties.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include "haus.h"
+
+inherit "std/room";
+
+create()
+{
+  ::create();
+
+  SetProp( P_INDOORS, 1 );
+  SetProp( P_LIGHT, 1 );
+  SetProp( P_INT_SHORT, "Am Antragsschalter" );
+  SetProp( P_INT_LONG,
+     "Du stehst am Antragsschalter der Bank. Hinter einer Rauchglasscheibe scheint\n"
+    +"sich ein Schalterbeamter zu befinden, Du kannst aber nichts genaueres er-\n"
+    +"kennen.\n"
+    +"Wenn Du Dich traust, kannst Du hier einen Bausparvertrag beantragen und einen\n"
+    +"beantragten Vertrag unterschreiben.\n"
+    +"Die Syntax dazu lautet:\n"
+    +"beantrage vertrag (fuer einen Bausparvertrag)\n"
+    +"beantrage ausbauvertrag (fuer den Ausbau des Hauses)\n"
+    +"unterschreibe vertrag (um den jeweiligen Vertrag zu unterschreiben)\n" );
+
+  AddDetail( ({ "boden" }),
+     "Der tiefschwarze Boden gibt Dir das Gefuehl, im absoluten Nichts zu stehen.\n" );
+  AddDetail( ({ "wand", "waende" }),
+     "Dunkle Schatten tanzen ueber die Waende.\n" );
+  AddDetail( ({ "decke" }),
+     "Sie scheint Dir in dieser unheimlichen Umgebung auf den Kopf fallen zu\n"
+    +"wollen.\n" );
+  AddDetail( ({ "nichts" }),
+     "Du siehst nichts.\n" );
+  AddDetail( ({ "schatten" }),
+     "Die Schatten irritieren Dich sehr.\n" );
+  AddDetail( ({ "schalter" }),
+     "An diesem Schalter kannst Du einen Bausparvertrag beantragen und unter-\n"
+    +"schreiben.\n" );
+  AddDetail( ({ "beamte", "beamter", "beamten", "schalterbeamte", "schalterbeamten", "schalterbeamter" }),
+     "Du kannst ihn nur schemenhaft hinter der Rauchglasscheibe des Schalters er-\n"
+    +"kennen. Die Umrisse scheinen allerdings zu keiner Lebensform zu gehoeren,\n"
+    +"derer Du Dich erinnern koenntest.\n" );
+  AddDetail( ({ "scheibe", "glasscheibe", "rauchglas", "rauchglasscheibe" }),
+     "Ein schemenhafter Beamter sitzt dahinter.\n" );
+  AddDetail( ({ "umrisse" }),
+     "Sie sind aeusserst beunruhigend.\n" );
+
+  AddCmd( ({ "beantrage" }), "beantragen" );
+  AddCmd( ({ "unterschreib", "unterschreibe" }), "unterschreiben" );
+  AddExit( "raus", PATH+"seherbank" );
+}
+
+void
+BecomesNetDead(object pl)
+{
+  pl->move(PATH+"seherbank", M_GO, 0, "wird rausgeworfen", "schlafwandelt herein");
+}
+
+int
+beantragen(string str)
+{
+  object tp, vertrag;
+  int haus;
+
+  tp = this_player();
+
+  if (!str || (haus = member( ({"ausbauvertrag", "vertrag", "bausparvertrag"}), str)) <0) {
+    notify_fail( "Versuch mal 'beantrage vertrag'!\n" );
+    return 0;
+  }
+
+  if (!IS_SEER(tp)) {
+    notify_fail( "Nur Seher koennen einen Bausparvertrag beantragen!\n" );
+    return 0;
+  }
+  if (tp->QueryProp(P_KILLS)) {
+    write("Verbrecher werden hier nicht bedient!\n");
+    return 1;
+  }
+  if (vertrag = present("sehe\rvertrag", tp))
+    write( "Der Beamte schnarrt: Aber Sie haben doch schon einen Vertrag!\n" );
+  else if (haus && (present("sehe\rhaus", tp) || VERWALTER->FindeHaus(getuid(tp)))) {
+    write( "Der Beamte schnarrt: Sie duerfen nur EIN Haus besitzen!\n" );
+    HLOG( "BANK.LOG", "Antrag: "+getuid(tp)+" stellte zweiten Antrag.\n" );
+  }
+  else if (!haus && !VERWALTER->FindeHaus(getuid(tp))) {
+    write( "Der Beamte faucht: Haben sie denn ueberhaupt schon ein Haus aufgestellt?\n"
+	  +"Vorher koennen sie natuerlich keine Erweiterungen beantragen!\n" );
+  }
+  else {
+    if (!haus && VERWALTER->HausProp(getuid(tp),HP_ROOMS) == 9) {
+      write( "Der Beamte droehnt: Ihr Haus hat schon die hoechste Ausbaustufe erreicht!\n");
+      return 1;
+    }
+    (vertrag = clone_object(PATH+"bausparvertrag"))->move(tp, M_NOCHECK);
+
+    write( "Die Schatten verdichten sich zu einem Blatt Papier, welches in Deine Haende\n"
+	  +"geweht wird. Mit droehnender Stimme erklaert der Schalterbeamte:\n"
+	  +"Dies ist Ihr Bausparvertrag. Bitte lesen Sie ihn sich gruendlich durch, be-\n"
+	  +"vor Sie ihn unterschreiben! Wenn Sie ihn erst einmal unterschrieben haben,\n"
+	  +"koennen sie nicht wieder davon zuruecktreten (es sei denn, Sie zerreissen\n"
+	  +"ihn oder ueberziehen die Ratenzahlungen)! Sie sind nicht gezwungen, den Ver-\n"
+	  +"trag sofort zu unterschreiben. Im Hauptraum der Bank befindet sich ein In-\n"
+	  +"formationsstand, an dem Sie Kommentare zu diesem Vertrag kaeuflich erwerben\n"
+	  +"koennen. Wenn Ihnen einige Passagen des Vertrags unverstaendlich sind, wird\n"
+	  +"Ihnen in den Kommentaren sicherlich geholfen.\n"
+	  +"Das Leisten der Unterschrift ist jedoch nur hier an diesem Schalter\n"
+	  +"moeglich!\n" );
+    if (!haus)
+      vertrag->Sign(V_RAUM);
+    HLOG( "BANK.LOG", "Antrag: "+getuid(tp)+" bekam einen "+(haus ? "Haus" : "Raum")+"vertrag.\n" );
+  }
+  return 1;
+}
+
+int
+unterschreiben(string str)
+{
+  object tp, vertrag;
+  int flag;
+
+  tp = this_player();
+
+  notify_fail( "Was willst Du denn unterschreiben?\n" );
+  if (!str || !(vertrag = present(str, tp)) || !vertrag->id("sehe\rvertrag"))
+    return 0;
+
+  if ((flag = vertrag->QueryProp(P_AUTOLOADOBJ)[1]) & V_SIGNED) {
+    write( "Der Schalterbeamte sagt: Aber der Vertrag ist doch schon unterschrieben!\n" );
+    return 1;
+  }
+
+  write( "Der Schalterbeamte sagt: Fuer welche Vertragsvariante haben sie sich\n"
+	+"entschieden? Tippen Sie 'sanft' fuer den sanften Weg zum eigenen Heim\n"
+	+"oder 'schnell' fuer den schnellen Weg.\n] " );
+  input_to("sign", 0, vertrag, flag|V_EP);
+  return 1;
+}
+
+void
+sign(string str, object vertrag, int flag)
+{
+  object block;
+  string s;
+  int *al;
+
+  if (str == "sanft" || str == "schnell") {
+    write( "Der Schalterbeamte gibt Dir einen Fuellfederhalter sowie ein Skalpell.\n"
+	  +"Schwungvoll setzt Du Deine Unterschrift unter den Vertrag. Dann piekst\n"
+	  +"Du Dir mit dem Skalpell in den Finger und bestaetigst die Unterschrift\n"
+	  +"mit einem dunklen Tropfen Deines Blutes.\n"
+	  +"Ploetzlich zucken Blitze um Dich herum auf, und der Schalterbeamte lacht\n"
+	  +"droehnend auf. Du hast das Gefuehl, dass hier etwas nicht ganz mit rechten\n"
+	  +"Dingen vor sich geht. Dann bestaetigt der Beamte die Unterschrift und\n"
+	  +"uebergibt Dir den ersten Block, auf dem Deine Raten vermerkt werden.\n"
+	  +"Der Beamte erklaert: Damit auf den Block auch eingezahlt wird, muss man\n"
+	  +"ihn wie eine Ruestung anziehen!\n" );
+
+    this_player()->reduce_hit_points(50);
+    vertrag->Sign(flag | (str == "sanft" ? V_SIGNED : V_SIGNED | V_FAST));
+    (block = clone_object(PATH+"block"))->move(this_player(), M_NOCHECK);
+    block->SetProp("schwer", V_FAST & vertrag->QueryProp(P_AUTOLOADOBJ)[1]);
+    al = block->QueryProp(P_AUTOLOADOBJ);
+    al[V_FLAGS] |= B_ACTIVE;
+    block->SetProp(P_AUTOLOADOBJ, al);
+    s = "Antrag: "+getuid(this_player())+" unterschrieb den Vertrag ("+str+").\n";
+    HLOG( "BANK.LOG", s );
+    HLOG( getuid(this_player()), s );
+  }
+  else {
+    write( "Der Schalterbeamte sagt: Nur 'sanft' oder 'schnell', bitte!\n" );
+    input_to("sign", 0, vertrag, flag);
+  }
+}
diff --git a/d/seher/haeuser/sb_ausgabe.c b/d/seher/haeuser/sb_ausgabe.c
new file mode 100644
index 0000000..9fed219
--- /dev/null
+++ b/d/seher/haeuser/sb_ausgabe.c
@@ -0,0 +1,115 @@
+#include <properties.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <bank.h>
+#include "haus.h"
+
+inherit "std/room";
+
+create()
+{
+  ::create();
+
+  SetProp(P_INDOORS, 1);
+  SetProp(P_LIGHT, 1);
+  SetProp(P_INT_SHORT, "Am Ausgabeschalter" );
+  SetProp(P_INT_LONG,
+     "Am Ausgabeschalter. Wenn Du alle Raten fuer Deinen Vertrag eingezahlt hast,\n"
+    +"kannst Du nun Dein Haus abholen. Dazu musst Du einfach nur Deinen Vertrag\n"
+    +"vorlegen (mit 'lege vertrag vor').\n"
+    +"Der Schemen am Schalter koennte ein Beamter sein, aber auch etwas viel, viel\n"
+    +"Schlimmeres...\n" );
+
+  AddDetail( ({ "boden" }),
+     "Der tiefschwarze Boden gibt Dir das Gefuehl, im absoluten Nichts zu stehen.\n" );
+  AddDetail( ({ "wand", "waende" }),
+     "Dunkle Schatten tanzen ueber die Waende.\n" );
+  AddDetail( ({ "decke" }),
+     "Sie scheint Dir in dieser unheimlichen Umgebung auf den Kopf fallen zu\n"
+    +"wollen.\n" );
+  AddDetail( ({ "nichts" }),
+     "Du siehst nichts.\n" );
+  AddDetail( ({ "schatten" }),
+     "Die Schatten irritieren Dich sehr.\n" );
+  AddDetail( ({ "schalter", "ausgabeschalter" }),
+     "Du stehst direkt vor dem Ausgabeschalter.\n" );
+  AddDetail( ({ "beamte", "beamter", "beamten", "schalterbeamter", "schalterbeamte", "schalterbeamten", "schemen", "schlimmeres" }),
+     "Der Schalterbeamte schaut Dich mit aufgesetztem Laecheln an.\n" );
+
+  AddCmd( ({ "leg", "lege" }), "legen" );
+  AddExit( "raus", PATH+"seherbank" );
+}
+
+void
+BecomesNetDead(object pl)
+{
+  pl->move(PATH+"seherbank", M_GO, 0, "wird rausgeworfen", "schlafwandelt herein");
+}
+
+int
+legen(string str)
+{
+  object vertrag, tp;
+
+  tp = this_player();
+  vertrag = present("sehe\rvertrag", tp);
+
+  if (!str || str != "vertrag vor")
+    return 0;
+
+  if (!IS_SEER(tp)) {
+    write( "Der Schalterbeamte blickt Dich scharf an. Er sagt: Nur SeherInnen\n"
+	  +"koennen hier ein Haus abholen!\n" );
+    HLOG( "BANK.LOG", "Ausgabe: "+getuid(tp)+" ist kein Seher.\n" );
+  }
+  else if (tp->QueryProp(P_KILLS))
+    write( "Der Schemen faucht: Verbrecher werden hier nicht bedient!\n" );
+  else if (!vertrag) {
+    write( "Der Schemen bemerkt: Sie haben doch gar keinen Vertrag!\n" );
+    HLOG( "BANK.LOG", "Ausgabe: "+getuid(tp)+" hatte keinen Vetrag.\n" );
+  }
+  else if (old_explode(object_name(vertrag),"#")[0] != PATH+"bausparvertrag") {
+    HLOG( "BANK.LOG", "Ausgabe: "+getuid(tp)+" Falscher Vertrag!\n");
+    HLOG( "SCHUMMEL", sprintf("Ausgabe: TP: %O, V: %O\n",
+		      this_player(),
+		      vertrag->QueryProp(P_CLONER)));
+  }
+  else if (!(vertrag->QueryProp(P_AUTOLOADOBJ)[1] & V_COMPL)) {
+    write( "Der Schalterbeamte weist Dich darauf hin, dass Du noch nicht genug\n"
+	  +"auf den Vertrag eingezahlt hast!\n" );
+    HLOG( "BANK.LOG", "Ausgabe: "+getuid(tp)+" hatte zu wenig eingezahlt.\n" );
+  }
+  else if (ZENTRALBANK->_query_current_money() < 30000) {
+    write( "Der Schalterbeamte weist Dich darauf hin, dass die Zentralbank im\n"
+	  +"Moment leider nicht ueber genug Bargeldreserven verfuegt, um ihren\n"
+	  +"Teil der Abmachung zu erfuellen. Er vertroestet Dich auf spaeter.\n" );
+  }
+  else if (vertrag->QueryProp(P_AUTOLOADOBJ)[1] & V_RAUM) {
+    VERWALTER->NeuerRaum(getuid(this_player()));
+    write( "Der Schemen hinter dem Schalter streckt einen langen, schattigen Finger\n"
+	  +"nach Deinem Vertrag aus. Wie von Geisterhand bewegt gleitet der Vertrag\n"
+	  +"hinueber. Der Schemen scheint den Vertrag ausgiebig zu pruefen, dann\n"
+	  +"zerreisst er ihn, wedelt einmal mit seinen schattenhaften Haenden und\n"
+	  +"beginnt mit Grabesstimme zu sprechen:\n"
+	  +"Der neue Raum ist in ihrem Haus installiert! Hinfort mit Ihnen!\n" );
+    HLOG( "BANK.LOG", "Ausgabe: "+getuid(tp)+" bekam einen neuen Raum.\n" );
+    HLOG( getuid(tp), "Ausgabe: "+getuid(tp)+" bekam einen neuen Raum.\n" );
+    "/p/daemon/zentralbank"->WithDraw(30000);
+    vertrag->remove();
+  }
+  else {
+    write( "Der Schemen hinter dem Schalter streckt einen langen, schattigen Finger\n"
+	  +"nach Deinem Vertrag aus. Wie von Geisterhand bewegt gleitet der Vertrag\n"
+	  +"hinueber. Der Schemen scheint den Vertrag ausgiebig zu pruefen, dann\n"
+	  +"zerreisst er ihn, wedelt einmal mit seinen schattenhaften Haenden und\n"
+	  +"vor Dir erscheint ein kleines, tragbares Haus. Zoegernd nimmst Du es in\n"
+	  +"Deine Haende, da erschallt eine grollende Stimme:\n"
+	  +"Das Haus gehoert Ihnen. Beehren sie uns bald wieder! Har, har, har!!!\n" );
+    clone_object(PATH+"traghaus")->move(tp, M_NOCHECK);
+    HLOG( "BANK.LOG", "Ausgabe: "+getuid(tp)+" bekam das Haus.\n" );
+    HLOG( getuid(tp), "Ausgabe: "+getuid(tp)+" bekam das Haus.\n" );
+    "/p/daemon/zentralbank"->WithDraw(30000);
+    vertrag->remove();
+  }
+  return 1;
+}
diff --git a/d/seher/haeuser/sb_einzahlung.c b/d/seher/haeuser/sb_einzahlung.c
new file mode 100644
index 0000000..1f89196
--- /dev/null
+++ b/d/seher/haeuser/sb_einzahlung.c
@@ -0,0 +1,130 @@
+#include <properties.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include "haus.h"
+
+inherit "std/room";
+
+create()
+{
+  ::create();
+
+  SetProp( P_INDOORS, 1 );
+  SetProp( P_LIGHT, 1 );
+  SetProp( P_INT_SHORT, "Am Einzahlungsschalter" );
+  SetProp( P_INT_LONG,
+     "Du stehst hier am Einzahlungsschalter der Bank. Um eine Rate einzuzahlen,\n"
+    +"musst Du einfach nur Deinen MG MASTER-BLOCK vorlegen. Gib dazu einfach nur\n"
+    +"'lege block vor' ein.\n"
+    +"Der Blick eines schemenhaft erkennbaren Beamten scheint Dich selbst durch\n"
+    +"die dicke Rauchglasscheibe hindurch zu durchbohren.\n" );
+
+  AddDetail( ({ "boden" }),
+     "Der tiefschwarze Boden gibt Dir das Gefuehl, im absoluten Nichts zu stehen.\n" );
+  AddDetail( ({ "wand", "waende" }),
+     "Dunkle Schatten tanzen ueber die Waende.\n" );
+  AddDetail( ({ "decke" }),
+     "Sie scheint Dir in dieser unheimlichen Umgebung auf den Kopf fallen zu\n"
+    +"wollen.\n" );
+  AddDetail( ({ "nichts" }),
+     "Du siehst nichts.\n" );
+  AddDetail( ({ "schatten" }),
+     "Die Schatten irritieren Dich sehr.\n" );
+  AddDetail( ({ "scheibe", "rauchglas", "glas", "glasscheibe", "rauchglasscheibe" }),
+     "Hinter ihr scheint sich ein Beamter zu verbergen.\n" );
+  AddDetail( ({ "schalter" }),
+     "An diesem Schalter kannst Du eine Rate fuer Dein Haus einzahlen.\n" );
+  AddDetail( ({ "beamte", "beamter", "beamten", "schalterbeamte", "schalterbeamter", "schalterbeamten" }),
+     "Der Schalterbeamte durchbohrt Dich mit seinen Blicken.\n" );
+
+  AddCmd( ({ "leg", "lege" }), "legen" );
+
+  AddExit( "raus", PATH+"seherbank" );
+}
+
+void
+BecomesNetDead(object pl)
+{
+  pl->move(PATH+"seherbank", M_GO, 0, "wird rausgeworfen", "schlafwandelt herein");
+}
+
+int
+legen(string str)
+{
+  object tp, block, vertrag;
+  int *al, *vl, rate;
+
+  if (!str || str != "block vor")
+    return 0;
+
+  tp = this_player();
+  block = present( "\n block", tp);
+  vertrag = present( "sehe\rvertrag", tp );
+
+  if (!IS_SEER(tp))
+    write( "Der Schalterbeamte weist Dich darauf hin, dass hier nur Seher be-\n"
+	  +"dient werden.\n" );
+  else if (tp->QueryProp(P_KILLS))
+    write( "Ein bohrender Blick verkuendet: Verbrecher werden hier nicht bedient!\n" );
+  else if (!block)
+    write( "Du hast keinen Block, den Du vorlegen koenntest!\n" );
+  else if (!vertrag) {
+    write( "Der Schalterbeamte sagt: Sie haben keinen Bausparvertrag! Wozu wollen\n"
+	  +"Sie dann eine Rate einzahlen? Wie sind Sie ueberhaupt an den Block\n"
+	  +"gekommen?\n" );
+    HLOG( "BANK.LOG", "Einzahlung: "+getuid(tp)+" Block ohne Vertrag!\n" );
+  }
+  else if (old_explode(object_name(block),"#")[0] != PATH+"block" ||
+	   old_explode(object_name(vertrag),"#")[0] != PATH+"bausparvertrag") {
+    write( "Der Schalterbeamte faehrt Dich an: DU WILLST SCHUMMELN???\n" );
+    HLOG( "BANK.LOG", "Einzahlung: "+getuid(tp)+" Falscher Block/Vertrag!\n");
+    HLOG( "SCHUMMEL", sprintf("Einzahlung: TP: %O, B: %O, V: %O\n",
+		      this_player(),
+		      block->QueryProp(P_CLONER),
+		      vertrag->QueryProp(P_CLONER)));
+  }
+  else {
+    al = block->Query(P_AUTOLOADOBJ);
+    rate = RATENHOEHE;
+    if (al[V_FLAGS] & B_EXTEND)
+      rate += (6*RATENHOEHE)/10;
+
+    if (al[V_MONEY] < rate) {
+      write( "Der Schalterbeamte weist Dich darauf hin, dass Du noch nicht genug Erfahrung\n"
+	    +"fuer die Rate gelassen hast.\n" );
+      HLOG( getuid(tp), "Einzahlung: "+getuid(tp)+" hatte nicht genug ("+al[V_MONEY]+"/"+rate+").\n");
+    }
+    else {
+      vl = vertrag->Query(P_AUTOLOADOBJ);
+      rate = (al[V_FLAGS] & B_FAST) ? SUMME_S : SUMME_L;
+      if (vl[1] & V_RAUM)
+	rate = rate * 4 /10;
+      write( "Du zahlst Deine Rate ein.\n" );
+      block->remove();
+      if (vertrag->Einzahlung() >= rate) {
+	  write( "Der Schalterbeamte sagt: Sie haben es geschafft! Sie haben nun genug\n"
+		+"Erfahrung eruebrigt. ");
+	if (vl[1] & V_RAUM)
+	  write( "Am Ausgabeschalter koennen Sie nun den neuen Raum\n"
+		+"eintragen lassen.\n" );
+	else
+	  write( "Am Hausausgabeschalter koennen Sie nun Ihr neues\n"
+		+"Heim in Empfang nehmen.\n" );
+	vertrag->Sign( vl[1] | V_COMPL );
+      }
+      else {
+	write( "Der Schalterbeamte beschwoert Blitz und Donner vom Himmel herab. Ein\n"
+	      +"lauter Knall, und ein neuer Ratenblock erscheint in Deinem Inventar.\n" );
+	(block = clone_object(PATH+"block"))->move(tp, M_NOCHECK);
+	block->SetProp("schwer", vl[1] & V_FAST);
+	al = block->Query(P_AUTOLOADOBJ);
+	al[V_FLAGS] |= B_ACTIVE;
+	block->Set(P_AUTOLOADOBJ, al);
+      }
+      HLOG(getuid(tp), "Einzahlung: "+getuid(tp)+" zahlte ein.\n" );
+      tp->force_save();
+    }
+  }
+  return 1;
+}
+
diff --git a/d/seher/haeuser/sc_schalter.c b/d/seher/haeuser/sc_schalter.c
new file mode 100644
index 0000000..9f29340
--- /dev/null
+++ b/d/seher/haeuser/sc_schalter.c
@@ -0,0 +1,332 @@
+#include <properties.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <moving.h>
+#include <bank.h>
+#include "haus.h"
+#include <money.h>
+
+inherit "std/room";
+
+#define KONTO "seercard_kontostand"
+
+// Prototypes
+public void BecomesNetDead( object pl );
+static int _guthaben();
+static int _einzahlen( string str );
+static int _aufladen( string str );
+static int _kaufen( string str );
+
+
+protected void create()
+{
+    ::create();
+
+    SetProp( P_INDOORS, 1 );
+    SetProp( P_LIGHT, 1 );
+    SetProp( P_LOG_FILE, "report/tiamak.rep" );
+    SetProp( P_INT_SHORT, "Am Geldkartenschalter" );
+    SetProp( P_INT_LONG,
+             break_string( "Das ist also der Geldkartenschalter. Hinter einer "
+                           "kaum durchsichtigen Rauchglasscheibe scheint ein "
+                           "Beamter (oder zumindest irgendein Lebewesen) "
+                           "geduldig zu warten. Neben dem Schalter haengt "
+                           "eine Tafel.", 78 ) );
+
+    AddDetail( ({ "boden" }),
+               "Der tiefschwarze Boden gibt Dir das Gefuehl, im absoluten "
+               "Nichts zu stehen.\n" );
+    AddDetail( "gefuehl", "Kein schoenes Gefuehl.\n" );
+    AddDetail( ({ "schwarz", "schwaerze" }), "Tiefschwarz.\n" );
+    AddDetail( ({ "wand", "waende" }),
+               "Dunkle Schatten tanzen ueber die Waende.\n" );
+    AddDetail( ({ "decke" }),
+               "Sie scheint Dir in dieser unheimlichen Umgebung auf den "
+               "Kopf fallen zu\nwollen.\n" );
+    AddDetail( "umgebung", "Schau Dich doch um.\n" );
+    AddDetail( ({ "kopf" }), "Das ist der Gegenstand auf Deinem Hals.\n" );
+    AddDetail( "gegenstand", "Ich meine Deinen Kopf.\n" );
+    AddDetail( "hals", "Wenn Du Nachhilfe in Anatomie brauchst, such Dir "
+               "jemand anderen.\n" );
+    AddDetail( ({ "jemand" }), "Keiner da.\n" );
+    AddDetail( "nachhilfe", "Dazu musst Du wohl woanders suchen.\n" );
+    AddDetail( "anatomie", "Schlag es in einem Lexikon nach.\n" );
+    AddDetail( "lexikon", "Soetwas hast Du anscheinend noch nie gesehen.\n" );
+    AddDetail( ({ "nichts" }),
+               "Du bist blind!\n" );
+    AddDetail( ({ "schatten" }),
+               "Die Schatten sind nur dazu da, um Dich zu irritieren. "
+               "Und das machen sie gut.\n" );
+    AddDetail( ({ "beamte", "beamter", "beamten", "schalterbeamte",
+                      "schalterbeamten", "schalterbeamter" }),
+               "Du kannst ihn nur schemenhaft hinter der Rauchglasscheibe "
+               "des Schalters er-\nkennen. Die Umrisse scheinen allerdings "
+               "zu keiner Lebensform zu gehoeren,\nderer Du Dich erinnern "
+               "koenntest.\n" );
+    AddDetail( ({ "scheibe", "glasscheibe", "rauchglas", "rauchglasscheibe" }),
+               "Ein schemenhafter Beamter sitzt dahinter.\n" );
+    AddDetail( ({ "umrisse" }),
+               "Sie sind aeusserst beunruhigend.\n" );
+    AddDetail( ({ "lebensform", "schemen", "lebewesen" }),
+               "Du moechtest gar nicht wissen, um was es sich dabei "
+               "handelt.\n" );
+    AddDetail( ({ "schalter", "geldkartenschalter" }),
+               "An diesem Schalter kannst Du die beruehmte"
+               " Seer-Card (TM) erwerben sowie\nDein Konto aufladen.\n" );
+    AddDetail( ({ "konto", "karte", "geldkarte", "card", "seercard",
+                      "seer-card" }), "Wie das geht, steht auf der Tafel.\n" );
+    AddDetail( "tafel", "Du kannst sie lesen.\n" );
+
+    AddSmells( SENSE_DEFAULT, "Es riecht nach altem Staub.\n" );
+    AddSmells( "staub", "Hier lagern sicher viele alte Akten.\n" );
+    AddSmells( "akten", "Die riechen dann irgendwann nach Staub.\n" );
+
+    AddSounds( SENSE_DEFAULT, "Direkt vor dem Schalter ist es erstaunlich "
+               "ruhig.\nGeradezu ehrfurchtsvoll ruhig.\n" );
+    AddSounds( ({ "schalter", "ruhe", "ehrfurcht" }),
+               "Ob das am Schalterbeamten liegt?\n" );
+    AddSounds( ({ "beamter", "beamten", "schalterbeamter", "schalterbeamten" }),
+               "Ist er ueberhaupt noch da?\n" );
+
+    AddReadDetail( ({ "tafel", "preisliste" }),
+                   "Preisliste:\n"
+                   "================================================="
+                   "=====================\n\n"
+                   "Neue Geldkarte .................................."
+                   "...... 10000 Muenzen\n"
+                   "Syntax: kaufe geldkarte\n\n"
+                   "Einzahlungen auf das Geldkarten-Konto ..........."
+                   "...... 10% Gebuehren\n"
+                   "Syntax: zahle <n> muenzen ein\n"
+                   "oder    lade karte auf\n\n" );
+
+    AddCmd( ({ "zahl", "zahle" }), "_einzahlen" );
+    AddCmd( ({ "lad", "lade" }), "_aufladen" );
+    AddCmd( ({ "kaufe", "kauf" }), "_kaufen" );
+    
+    AddExit( "raus", PATH+"seherbank" );
+}
+
+
+public void BecomesNetDead( object pl )
+{
+    pl->move( PATH+"seherbank", M_GO|M_NOCHECK, 0, "wird rausgeworfen",
+              "schlafwandelt herein" );
+}
+
+
+static int _guthaben()
+{
+    mixed wert;
+
+    if ( !(this_player()->Query(KONTO, F_MODE) & SAVE) )
+        this_player()->Set( KONTO, SAVE, F_MODE_AS );
+    
+    wert = this_player()->Query( KONTO, F_VALUE );
+
+    if ( !intp(wert) || wert < 0 || wert > 100000 )
+        return this_player()->Set( KONTO, 0, F_VALUE );
+
+    return wert;
+}
+
+
+static int _einzahlen( string str )
+{
+    int n, guthaben, gebuehr;
+    string dummy;
+    object ob;
+
+    if ( !this_player() || this_player() != this_interactive() )
+        return 0;
+    
+    if ( !str || !stringp(str) || sscanf( str, "%d %s", n, dummy ) != 2 ||
+         dummy != "muenzen ein" )
+        return notify_fail( "Syntax: zahle <n> muenzen ein\n" ), 0;
+
+    if ( !IS_SEER(this_player()) )
+        return notify_fail( break_string("Werd doch erstmal Seher.", 78, "Der "
+                                         "Schalterbeamte grinst ueberheblich: "
+                                         ) ), 0;
+
+    if ( n < 1000 )
+        return notify_fail( break_string("Mit so kleinen Betraegen fangen "
+                                         "wir erst gar nicht an. Du musst "
+                                         "schon mindestens 1000 "
+                                         "Muenzen einzahlen.", 78,
+                                         "Der Schalterbeamte grummelt: ") ), 0;
+
+    guthaben = this_player()->QueryMoney();
+
+    // Man kann sein Konto natuerlich nicht mit dem Geld vom Konto aufladen ;-)
+    if ( ob = present( "\ngeldkarte", this_player() ) )
+        guthaben -= ob->QueryProp( P_AMOUNT );
+
+    if ( guthaben < n )
+        return notify_fail( break_string("Du hast ja gar nicht soviel Geld!",
+                                         78, "Der Schalterbeamte faucht "
+                                         "Dich an: ") ), 0;
+
+    if ( (guthaben = _guthaben()) == 100000 )
+        return notify_fail( break_string( "Deine Seer-Card (TM) ist bereits "
+                                          "maximal aufgeladen. Mehr als "
+                                          "100.000 Muenzen koennen damit aus "
+                                          "technischen Gruenden nicht "
+                                          "verwaltet werden.", 78,
+                                          "Der Schalterbeamte sagt: " ) ), 0;
+
+    gebuehr = to_int(n / 11.0 + 0.5);
+    n -= gebuehr;
+
+    if ( guthaben + n > 100000 )
+        return notify_fail( break_string("Tut mir leid, aber so hohe Betraege "
+                                         "kann die Geldkarte nicht verwalten.\n"
+                                         "Du kannst die Seer-Card (TM) mit "
+                                         "maximal 100.000 Muenzen aufladen. "
+                                         "Ausgehend von Deinem momentanen "
+                                         "Guthaben von " +
+                                         to_string(guthaben) + " Muenzen "
+                                         "wuerde Dich das (inkl. unserer "
+                                         "Verwaltungsgebuehren) " +
+                                         to_string(to_int((100000-guthaben) *
+                                                          1.1 + 0.5)) +
+                                         " Muenzen kosten.\n"
+                                         "Um Dir die Rechnerei zu ersparen, "
+                                         "kannst Du auch einfach \"lade karte "
+                                         "auf\" benutzen, um Deine Geldkarte "
+                                         "maximal aufzuladen.", 78,
+                                         "Der Schalterbeamte sagt: ",
+                                         BS_LEAVE_MY_LFS) ), 0;
+
+    if ( objectp(ob) ){
+        write( "Du zeigst Deine Geldkarte kurz vor.\n" );
+        // notwendig, da sonst das Geld evtl. von der Karte selber abgebucht
+        // wird, falls sie als oberstes im Inv steht
+        this_player()->Set( KONTO, 0, F_VALUE );
+    }
+
+    this_player()->AddMoney( -(n + gebuehr) );
+    ZENTRALBANK->PayIn( gebuehr );
+    this_player()->Set( KONTO, guthaben + n, F_VALUE );
+
+    write( break_string( "Okay, Deine Geldkarte hat nun ein Guthaben von "
+                         "insgesamt " + to_string(_guthaben()) + " Muenzen.",
+                         78, "Der Schalterbeamte sagt: " ) );
+
+    return 1;
+}
+
+
+static int _aufladen( string str )
+{
+    int n, guthaben, gebuehr;
+    string dummy;
+    object ob;
+
+    if ( !this_player() || this_player() != this_interactive() )
+        return 0;
+
+    if ( !stringp(str) || sscanf( str, "%s auf", dummy ) != 1 ||
+         dummy != "karte" && dummy != "geldkarte" && dummy != "card" &&
+         dummy != "seercard" && dummy != "seer-card" )
+        return notify_fail( "Syntax: lade geldkarte auf\n" ), 0;
+
+    if ( !IS_SEER(this_player()) )
+        return notify_fail( break_string("Werd doch erstmal Seher.", 78, "Der "
+                                         "Schalterbeamte grinst ueberheblich: "
+                                         ) ), 0;
+
+    if ( (guthaben = _guthaben()) == 100000 )
+        return notify_fail( break_string( "Deine Seer-Card (TM) ist bereits "
+                                          "maximal aufgeladen. Mehr als "
+                                          "100.000 Muenzen koennen damit aus "
+                                          "technischen Gruenden nicht "
+                                          "verwaltet werden.", 78,
+                                          "Der Schalterbeamte sagt: " ) ), 0;
+
+    n = this_player()->QueryMoney();
+
+    // Man kann sein Konto natuerlich nicht mit dem Geld vom Konto aufladen ;-)
+    if ( ob = present_clone( SEHERKARTE, this_player() ) )
+        n -= ob->QueryProp( P_AMOUNT );
+
+    if ( (gebuehr = to_int( (100000 - guthaben) * 1.1 + 0.5 )) < n )
+        n = gebuehr;
+
+    gebuehr = to_int( n / 11.0 + 0.5 );
+    n -= gebuehr;
+
+    if ( n + gebuehr < 1000 )
+        return notify_fail( break_string("Mit so kleinen Betraegen fangen "
+                                         "wir erst gar nicht an. Du musst "
+                                         "schon mindestens 1000 "
+                                         "Muenzen einzahlen.", 78,
+                                         "Der Schalterbeamte grummelt: ") ), 0;
+
+    if ( objectp(ob) ){
+        write( "Du zeigst Deine Geldkarte kurz vor.\n" );
+        // notwendig, da sonst das Geld evtl. von der Karte selber abgebucht
+        // wird, falls sie als oberstes im Inv steht
+        this_player()->Set( KONTO, 0, F_VALUE );
+    }
+
+    this_player()->AddMoney( -(n + gebuehr) );
+    ZENTRALBANK->PayIn( gebuehr );
+    this_player()->Set( KONTO, guthaben + n, F_VALUE );
+
+    write( break_string( "Du zahlst " + to_string(n+gebuehr) + " Muenzen ein "
+                         "und erhoehst Dein Guthaben damit auf " +
+                         to_string(_guthaben()) + " Muenzen insgesamt.", 78,
+                         "Der Schalterbeamte sagt: " ) );
+    
+    return 1;
+}
+
+
+static int _kaufen( string str )
+{
+    if ( !this_interactive() || this_player() != this_interactive() )
+        return 0;
+
+    if ( str != "geldkarte" && str != "karte" && str != "card" &&
+         str != "seercard" && str != "seer-card" )
+        return notify_fail( "Syntax: kaufe geldkarte\n" ), 0;
+
+    if ( !IS_SEER(this_player()) )
+        return notify_fail( break_string("Werd doch erstmal Seher.", 78, "Der "
+                                         "Schalterbeamte grinst ueberheblich: "
+                                         ) ), 0;
+
+    if ( this_player()->QueryMoney() < 10000 )
+        return notify_fail( break_string("Du hast nicht genug Geld bei!",
+                                         78, "Der Schalterbeamte faucht "
+                                         "Dich an: ") ), 0;
+
+    object ob = clone_object( SEHERKARTE ); 
+    this_player()->AddMoney(-10000);
+    ZENTRALBANK->PayIn(10000);
+    
+    if ( ob->move( this_player(), M_GIVE ) != 1 ){
+        write( "Der Schalterbeamte grummelt: Du kannst die Geldkarte nicht "
+               "mehr tragen.\nDer Schalterbeamte wirft Dir Deine Seer-Card "
+               "(TM) veraechtlich vor die Fuesse.\n" );
+        // trotzdem einmal in's Inv des Spielers bewegen, damit die Karte
+        // auf seinen Namen registriert wird
+        ob->move( this_player(), M_GIVE|M_NOCHECK );
+        // dann auf den Boden legen
+        ob->move( this_object(), M_PUT );
+    }
+    else
+        write( "Der Beamte haendigt Dir eine Seer-Card (TM) aus.\n" );
+
+    write( break_string( "Der Schalterbeamte macht Dich noch darauf "
+      "aufmerksam, dass Deine Seer-Card (TM) immer erst dann als "
+      "Zahlungsmittel in Anspruch genommen wird, wenn Deine in Boersen oder "
+      "in klingender Muenze mitgefuehrte Barschaft aufgebraucht sein "
+      "sollte - egal, was davon sich oben im Inventar befindet.\n"
+      "Was immer das auch heissen soll.", 78, 0, BS_LEAVE_MY_LFS ) );
+
+    this_player()->Set( KONTO, SAVE, F_MODE_AS );
+
+    return 1;
+}
diff --git a/d/seher/haeuser/seherbank.c b/d/seher/haeuser/seherbank.c
new file mode 100644
index 0000000..4e47265
--- /dev/null
+++ b/d/seher/haeuser/seherbank.c
@@ -0,0 +1,175 @@
+#include <moving.h>
+#include <defines.h>
+#include <rooms.h>
+#include <properties.h>
+#include "queue.h"
+#include "haus.h"
+
+inherit "/std/room";
+
+create()
+{
+    object q;
+    
+    ::create();
+    
+    SetProp( P_INDOORS, 1 );
+    SetProp( P_LIGHT, 1 );
+    SetProp( P_INT_SHORT, "In der MorgenGrauen-Bank" );
+    SetProp( P_INT_LONG,
+             break_string( "Dunkle Schatten erfuellen diesen Raum, nur "
+                           "schemenhaft kannst Du einige Schlangen sowie einen "
+                           "Informationsstand erkennen. Du beginnst zu "
+                           "begreifen:\nDu befindest Dich in der "
+                           "MorgenGrauen-Bank, der 'Bank in Ihrem Ruecken'.\n"
+                           "Wenn Du Seher oder Seherin bist, kannst Du hier "
+                           "einen Bausparvertrag fuer ein Seherhaus erwerben, "
+                           "Raten fuer einen laufenden Bausparvertrag "
+                           "einzahlen und ein abbezahltes Haus abholen. "
+                           "Ausserdem kannst Du hier Geldkarten erwerben und "
+                           "Dein Konto auffuellen. Dazu musst Du Dich nur "
+                           "zu den entsprechenden Schaltern begeben. "
+                           "Nach Sueden kannst Du die Bank wieder "
+                           "verlassen.\n", 78, 0, BS_LEAVE_MY_LFS ) );
+    
+    AddDetail( ({ "wand", "waende" }),
+               "An den Waenden treiben die Schatten unheimliche "
+               "Spielchen.\n" );
+    AddDetail( ({ "boden" }),
+               "Der Boden ist schwarz wie die Nacht.\n" );
+    AddDetail( ({ "decke" }),
+               "Schleimige Faeden scheinen von der dunklen Decke "
+               "zu haengen.\n" );
+    AddDetail( ({ "schatten" }),
+               "Die Schatten erfuellen den ganzen Raum und lassen "
+               "Dich nur schemenhafte Details\nerblicken.\n" );
+    AddDetail( ({ "details", "schemen" }),
+               "Ein Informationsstand sowie vier Schlangen von "
+               "Leuten sind als Schemen in\nden Schatten erkennbar.\n" );
+    AddDetail( ({ "schleim", "faeden", "schleimfaeden" }),
+               "Die Schleimfaeden haengen von der Decke und drohen Dir auf "
+               "den Kopf zu\ntropfen. Ein Froesteln schuettelt Dich.\n" );
+    AddDetail( ({ "schalter" }),
+               break_string( "Vor den Schaltern stehen lange Schlangen von "
+                             "Leuten, die auch gerne ein Haus haetten. Es "
+                             "gibt einen Antragsschalter, einen "
+                             "Ratenzahlungsschalter, einen Ausgabeschalter und "
+                             "einen Geldkartenschalter. Vor jedem Schalter "
+                             "steht eine Schlange von mehr oder weniger "
+                             "geduldig wartenden Leuten. Du wirst Dich wohl "
+                             "anstellen muessen...", 78 ) );
+    AddDetail( ({ "stand", "informationsstand" }),
+               "Hier kannst Du fuer den unverschaemten Preis von 10000 "
+               "Muenzen die Kommen-\ntare zum Bausparvertrag erwerben. "
+               "Das geht mit 'kaufe kommentar', ist aber\n"
+               "angesichts des hohen Preises fast nicht anzuraten.\n" );
+    AddDetail( ({ "ausgabeschalter", "hausausgabeschalter", "hausausgabe" }),
+               "Wenn Du alle Raten fuer Dein Haus eingezahlt hast, kannst "
+               "Du es hier ab-\nholen und mitnehmen. Dafuer musst Du Dich "
+               "an die Hausausgabeschlange an-\nstellen.\n" );
+    AddDetail( ({ "ratenschalter", "ratenzahlungsschalter" }),
+               "Hier kannst Du die Raten fuer Dein Seherhaus einzahlen. "
+               "Du wirst Dich\naber erst an die Zahlungsschlange anstellen "
+               "muessen.\n" );
+    AddDetail( ({ "antragsschalter" }),
+               "An diesem Schalter kannst Du einen Bausparvertrag beantragen. "
+               "Stell Dich\ndazu an die Antragsschlange an.\n" );
+    AddDetail( ({ "geldkartenschalter", "kartenschalter" }),
+               break_string( "Am Geldkartenschalter kannst Du die beruehmte "
+                             "Seer-Card (TM) erwerben und Dein Guthabenkonto "
+                             "auffuellen. Allerdings haben das auch noch "
+                             "andere Leute vor - Du wirst Dich also brav "
+                             "hinten an die Geldkartenschlange anstellen "
+                             "muessen.", 78 ) );
+
+    q=AddItem( PATH+"queue", REFRESH_NONE,
+               ([ P_SHORT : "Eine Schlange vor dem Geldkartenschalter",
+                P_LONG : "Die Seer-Card (TM) muss ja wirklich begehrt sein. "
+                "Zumindest stehen mehrere\nLeute an der Schlange zum "
+                "Geldkartenschalter an.\n"
+                "Syntax: 'stell an kartenschlange an'.\n",
+                P_INT_SHORT : "In der Schlange vor dem Geldkartenschalter",
+                P_INT_LONG : "Du steht in der langen Schlange vor dem "
+                "Geldkartenschalter. Wenigstens geht\nes halbwegs flott "
+                "vorwaerts.\n",
+                Q_LENGTH : 7,
+                Q_CYCLE_TIME : 8,
+                Q_SUSPEND : 1,
+                Q_DEST : PATH+"sc_schalter"
+                ]) );
+
+    q->AddId( ({"geldkartenschlange", "kartenschlange"}) );
+
+    q=AddItem( PATH+"queue", REFRESH_NONE,
+               ([ P_SHORT : "Eine Schlange vor dem Hausausgabeschalter",
+                P_LONG  : "In dieser Schlange stehen Leute vor dem "
+                "Hausausgabeschalter an.\n"
+                +"Syntax: 'stell an ausgabeschlange an'.\n",
+                P_INT_SHORT : "In der Schlange vor dem Hausausgabeschalter",
+                P_INT_LONG  : "Du wartest vor dem Hausausgabeschalter.\n",
+                Q_SUSPEND : 1,
+                Q_DEST : PATH+"sb_ausgabe"
+                ]) );
+    
+    q->AddId( ({"hausausgabeschlange", "ausgabeschlange" }) );
+
+    q=AddItem( PATH+"queue", REFRESH_NONE,
+               ([ P_SHORT : "Eine Schlange vor dem Schalter zur Ratenzahlung",
+                P_LONG  : "Wenn Du eine Rate bezahlen willst, solltest Du "
+                "Dich an dieser Schlange\nanstellen.\n"
+                +"Syntax: 'stell an ratenschlange an'.\n",
+                P_INT_SHORT : "In der Schlange vor dem Schalter zur "
+                "Ratenzahlung",
+                P_INT_LONG  : "Du wunderst Dich ein wenig ueber die "
+                "Massen potentieller Ratenzahler.\n",
+                Q_LENGTH : 8,
+                Q_CYCLE_TIME : 10,
+                Q_DEST : PATH+"sb_einzahlung",
+                Q_SUSPEND : 1
+                ]) );
+    
+    q->AddId( ({"ratenschlange", "ratenzahlungsschlange",
+                    "zahlungsschlange" }) );
+
+    q=AddItem( PATH+"queue", REFRESH_NONE,
+               ([ P_SHORT : "Eine Schlange vor dem Antragsschalter",
+                P_LONG  : "Die Leute in dieser Schlange wollen alle ein "
+                "eigenes Heim.\n"
+                +"Syntax: 'stell an antragsschlange an'.\n",
+                P_INT_SHORT : "In der Schlange vor dem Antragsschalter",
+                P_INT_LONG  : "Du wartest inmitten anderer Antragssteller "
+                "darauf, endlich Deinen\nBausparvertrag beantragen zu "
+                "duerfen.\n",
+                Q_LENGTH : 5,
+                Q_CYCLE_TIME : 20,
+                Q_SUSPEND : 1,
+                Q_DEST : PATH+"sb_antrag"
+                ]) );
+    
+    q->AddId( ({"antragsschlange"}) );
+
+    AddExit( "sueden", "/d/ebene/room/dra_str1" );
+
+    AddCmd( ({ "kauf", "kaufe" }), "kaufen" );
+}
+
+static int
+kaufen(string str)
+{
+  object tp;
+
+  if (!str || str != "kommentar") {
+    notify_fail( "Was willst Du kaufen? Hier gibt es nur Kommentare!\n" );
+    return 0;
+  }
+
+  tp = this_player();
+  if (tp->QueryMoney()<10000)
+    write( "Du hast nicht genug Geld dabei!\n" );
+  else {
+    clone_object(PATH+"kommentar")->move(tp, M_NOCHECK);
+    tp->AddMoney(-10000);
+    write( "Du zahlst 10000 Muenzen und erhaeltst dafuer einen Kommentar.\n" );
+  }
+  return 1;
+}
diff --git a/d/seher/haeuser/special/faq/0 b/d/seher/haeuser/special/faq/0
new file mode 100644
index 0000000..1b679e1
--- /dev/null
+++ b/d/seher/haeuser/special/faq/0
@@ -0,0 +1,10 @@
+0. NEUIGKEITEN
+--------------
+
+Hier aktuelle Aenderungen der SeherFAQ 3.02 (Stand Juli 2001):
+
+- Es gibt zwei neue Portale (34 und 36)
+
+- Durch einen Riss in den Dimensionen ist der Zugang zu einer
+  neuen Paralellwelt moeglich
+
diff --git a/d/seher/haeuser/special/faq/1 b/d/seher/haeuser/special/faq/1
new file mode 100644
index 0000000..a2f9c3c
--- /dev/null
+++ b/d/seher/haeuser/special/faq/1
@@ -0,0 +1,54 @@
+1. TRANSPORTMOEGLICHKEITEN
+---------------------------
+
+Als Seher kannst Du nunmehr auf einige neue Fortbewegungsmoeglichkeiten
+zugreifen, um schneller von einem Ort zum anderen zu gelangen:
+
+a) Seherportale
+---------------
+
+Als Seher kannst Du ab sofort magische Portale nutzen, um schneller
+von einem Ort zum anderen zu gelangen. Es gibt im Moment 33 Tore,
+die Du mit dem Befehl teleportiere benutzen kannst. Zur Verfuegung
+stehen Dir dann momentan folgende Moeglichkeiten (wenn du alle Portale hast):
+
+  0. nicht teleportieren                  1. Tamibar, Daribi-Dorf (10 KP)
+  2. Drachenzinnen                        3. Port Vainer Stadttor
+  4. An einem Bach im Gebirge             5. Polargebiet
+  6. Tundra                               7. Die Feenstrasse
+  8. Waldweg                              9. Eine Ruinenstadt
+ 10. Vorplatz vor der Arena              11. Klippenpfad auf Aurora (10 KP)
+ 12. Svolvaer (10 KP)                    13. Im Bergdorf
+ 14. Vorplatz der Traukapelle            15. Lash
+ 16. Dschungel                           17. Ueber den Wolken
+ 18. Am Fluss Tain Wen Tai               19. Ritterdenkmal bei Orkhausen
+ 20. Rodelbahn                           21. Hobbitdorf
+ 22. Ein tiefer Wald (10 KP)             23. Ueber dem Schacht
+ 24. Am Ende der Welt (10 KP)            25. Am Friedhof
+ 26. Unter Shaky Island (10 KP)          27. In der Naehe der Festung
+ 28. Tortugas Leuchtturm (10 KP)         29. Katzmandu
+ 30. In einer Hoehle der Rieseninsel     31. Auf einem kleinen Bergpfad
+ 32. Portalraum                          33. In der Naehe von Umjak
+ 34. Ein Felsen am Bach                  36. Innuit-Siedlung
+ 37. Bei den Werwoelfen                  38. Nordstrand von Krylaios
+ 39. Bei einem schwarzen Felsen          40. Der Abgrund des Zorns
+
+Das Benutzen der Portale setzt allerdings voraus, dass Du diese Portale
+bereits gefunden hast. Du musst die Portale also zunaechst einmal
+suchen.
+Einige Portale auf den Inseln kosten KPs, die meisten jedoch nichts.
+Da danach immer wieder gefragt wird: Natuerlich kann man die Portale nur
+benutzen, wenn man sich gerade bei einem befindet.
+
+
+b) Siebenmeilenstiefel
+----------------------
+
+Diese kann man im Seherladen kaufen. Sie haben die Eigenschaft,
+ihren Traeger immer bis zur naechsten Kreuzung weiterzutragen, was bei
+langen Wegen sinnvoll sein kann, in vielen Gegenden jedoch nicht
+funktioniert, weil in jedem Raum mehr als zwei Ausgaenge vorkommen.
+Ausserdem ist das Ganze extrem gewoehnungsbeduerftig, da man bekannte
+Wege jetzt mit ganz anderen Tastenkombinationen abgeht, als wenn man die
+Wege einfach wie gewohnt 'runterrattert'. Einfach mal austesten,
+lustig ists allemal.
diff --git a/d/seher/haeuser/special/faq/10 b/d/seher/haeuser/special/faq/10
new file mode 100644
index 0000000..92ffb61
--- /dev/null
+++ b/d/seher/haeuser/special/faq/10
@@ -0,0 +1,42 @@
+10. PARALLELDIMENSION
+---------------------
+
+Warscheinlich hast Du schon immer gedacht: 'Eine Welt jenseits des
+MorgenGrauens gibt es genausowenig wie Geheimbuende!'
+Nun, bezueglich der Geheimbuende koennte das zutreffen, und natuerlich
+sollst Du auch keine Welt haben neben dem MorgenGrauen, aber es gibt
+im MUD eine Welt, die nur Seher betreten koennen:
+
+Die Paralleldimension
+
+Die Parallelwelt ist im Prinzip die genau gleiche Welt wie das
+'normale' Morgengrauen, mit dem Unterschied, dass an bestimmten Stellen,
+an denen in der 'Normalwelt' keine NPCs stehen, neue oder eben andere
+Monster stehen. In ganz seltenen Faellen (leider) ist sogar die Raum-
+beschreibung in der Parallelwelt anders als in der normalen Welt.
+
+Beispiele fuer unterschiedliche Raeume in der Parallelwelt sind z.B.:
+
+- Drachenschildkroete (w,n,n,w,w vom Sandtiger)
+- Weghier (Lichtung im Grimmswald, da, wo in der Normalwelt die F&E ist)
+
+Momentan ist die Paralleldimension schon eine relativ grosse Welt, die
+auch staendig waechst. Fuer kleinere Seher ist sie vielleicht zu hart,
+am Anfang koennen sie sich aber z.B. im Paragoblinlager bei Port Vain
+austoben. Fuer die hoeheren Seher sind eher solche Sachen wie der
+Weghier oder die Paragruft vor der Polarwueste interessant.
+
+Betreten kann man als Seher die Parallelwelt am einfachsten und
+sichersten ueber das Portal im obersten Geschoss von Mirils Turm
+(Ebene bei Drachenhort)
+Allerdings transportiert das Portal nur zu 98% Wahrscheinlichkeit, und
+man sollte aufpassen, da dort in der Naehe die Seher-Raeuber von Rochus
+im Gebuesch sitzen, die einen schnell zu sich gezogen haben, dort kommt
+man dann nur noch raus, indem man das Geld abgibt oder kaempft :)
+Ausserdem steht in der Parawelt am Portal 2 (das ist das naechste bei
+Mirils Turm) der sogenannte Huorn, das ist ein Baum, der einem Sachen
+klaut (boese zu Patryn rueberschiel :)) Deshalb sollte man da auch aufpassen.
+
+Geruechten zufolge soll es mittlerweile sogar einen neuen Riss in den
+Dimensionen geben, der Zutritt zu einer vielleicht noch gefaehrlicheren
+Welt ermoeglicht.
diff --git a/d/seher/haeuser/special/faq/11 b/d/seher/haeuser/special/faq/11
new file mode 100644
index 0000000..735fc57
--- /dev/null
+++ b/d/seher/haeuser/special/faq/11
@@ -0,0 +1,13 @@
+11. Para-Portal 8
+-----------------
+
+Viele werden sich jetzt vielleicht fragen: Warum widmet man einem
+einfachen Magischen Portal ein ganzes Unterkapitel, aber es ist meiner
+Ansicht nach notwendig. Das Portal 8 ist das bis jetzt einzige Portal, das
+in der Parawelt anders ist als in der Normalwelt. Es hat andere, zum
+groessten Teil nicht ungefaehrliche, Ziele als das in der Normalwelt.
+Welche Ziele dies nun sind, verrate ich natuerlich nicht, das solltet
+ihr schon selbst herausbekommen (entweder fragt ihr andere Seher, oder
+probiert es !SEHR vorsichtig! aus). Ihr solltet es euch auf jeden Fall mal
+anschauen, denn es hat auch seine guten Seiten...
+
diff --git a/d/seher/haeuser/special/faq/12 b/d/seher/haeuser/special/faq/12
new file mode 100644
index 0000000..a44ff5a
--- /dev/null
+++ b/d/seher/haeuser/special/faq/12
@@ -0,0 +1,13 @@
+12. FINGER-BEFEHL
+----------------
+
+Wenn Du einmal probierst, einen anderen Spieler zu fingern, wirst Du
+feststellen, dass Du nun einige zusaetzliche Informationen
+erhaelst, bei normalen Spielern ihr Alter, und bei Sehern / Magiern
+zusaetzlich die ueber Plan und Projekt eingegebenen Informationen.
+
+So, und wenn Du dann auch obendrein nicht allzu zimperlich bist, und
+gerne Leute Dich fingern laesst, so wirst Du natuerlich auch einen Plan
+und ein Projekt in Deinem 'finger' einsetzen. Dies kannst Du mit Hilfe
+des Planeditors machen, den Du im Seherladen erhaelst.
+
diff --git a/d/seher/haeuser/special/faq/13 b/d/seher/haeuser/special/faq/13
new file mode 100644
index 0000000..6763827
--- /dev/null
+++ b/d/seher/haeuser/special/faq/13
@@ -0,0 +1,17 @@
+13. ECHO & R-EMOTE
+------------------
+
+Solltest Du irgendwann die Quest "Hilf dem Architekten" geloest haben
+(was dringend zu empfehlen ist, denn das ist definitiv eines der besten
+quests hier, IMHO) - dann steht Dir als Seher hinterher der ECHO-Befehl
+zur Verfuegung, mit dessen Hilfe Du in der Lage bist, einen x-Beliebigen
+Text in dem Raum abzusetzen, in dem Du Dich befindest, so dass auch alle
+anderen Spieler diesen Text lesen koennen...
+Nette Spielerei, die leider 50 MP kostet, aber trotzdem immer zu einem
+kleinen Gag gut ist.
+
+Solltest Du die Seherquest 'Die Kristallkugel' geloest haben, so steht
+Dir auch das 'r-emote' zur Verfuegung, das es Dir ermoeglicht, das seit
+der Loesung des Amaryllis-Quests (hast Du doch wohl !!) moegliche 'emote'
+auch auf Spieler ausserhalb des Raumes anzuwenden.
+
diff --git a/d/seher/haeuser/special/faq/14 b/d/seher/haeuser/special/faq/14
new file mode 100644
index 0000000..dbd15b3
--- /dev/null
+++ b/d/seher/haeuser/special/faq/14
@@ -0,0 +1,55 @@
+14. SEHERHAEUSER
+----------------
+
+Interessantes Thema: Die Seherhauser: Sie gibt es nun seit ca. Mitte
+Oktober 94, und folgendes ist dazu anzumerken:
+
+In der Bausparkasse in Drachenhort gibt es die noetigen Informationen zu
+den Hauesern. Das Informationsblatt fuer 10.000 Muenzen sollte man sich
+eben zulegen, auch wenn nur eine Seite Infos drinstehen, und es nach dem
+ersten Lesen weg ist :((.
+
+Grundsaetzlich gibt es zwei Moeglichkeiten, zu einem Haus zu kommen:
+
+a) Der schnelle Vertrag: Kostet 2 Mio EPs, man muss alle 2 online Stunden
+eine Rate von 80.000 EPs einzahlen - insgesamt also 25 Raten. Die Zeit kann
+u.U. nach einem Crash etwas knapp werden, da man dann nicht die noetige
+Zeit hat, um sich selbst erstmal Ausruestung zu besorgen, und ja vor allem
+alle Spieler dabei sind, sich Ausruestung zu beschaffen, so dass nicht
+viel zu holen ist.
+
+b) Der langsame Vertrag: Kostet 2.4 Mio Eps - also 30 Raten, man muss alle
+4 online Stunden seine Rate von 80.000 EPs einzahlen. Sicher besser geeignet
+fuer diejenigen, die oft talken, und nicht die naechste Woche nur mit metzeln
+verbringen wollen - Geschmackssache.... natuerlich kann man bei beiden
+Vertragsarten auch schneller einzahlen, also muss man sich keine Sorge machen,
+dass man beim 4-stunden Vertrag ewig warten muss, bis man einen neuen
+Masterblock kriegt, auf dem die Guthaben gutgeschrieben werden.
+
+Welche Vertragsform man waehlt, ist einem selbst ueberlassen.... aber
+ueberlegs Dir gut, ob Du die Zeit zum talken behalten willst, denn die wird
+beim 2h - Vertrag schnell knapp - nicht zu vergessen die Tatsache, dass bei
+Ueberziehen der Einzahlungszeit fuer die einzelnen Raten zunaechst eine
+saftige Strafe faellig wird (es sei denn, der Masterblock ist voll),
+und dann sogar der ganze Vertrag floeten gehen kann....
+
+Im Seherhaus koennen auch Extra-Raeume eingebaut werden. Diese kosten pro
+Stueck entweder 1017600 EPs (2 Onlinestunden) oder 848000 EPs (1 Onlinestunde)
+Maximal koennen 9 Raeume an das Haus angebaut werden. Sofern in einem Raum
+mehrere Haeuser stehen, besteht auch die Moeglichkeit, die durch den Befehl
+Ausgang miteinander zu verbinden.
+
+In den Seherhaeusern sind seit Februar ' 95 das Beschreiben von
+Befehlen und spezifische Ausgaben moeglich. So kann man rassen- und
+geschlechtsspezifische Ausgaben machen. Ebenso ist es moeglich,
+durch Eingabe von Befehlen bestimmte Ausgaben zu erreichen, z.B.
+'druecke knopf' - ' ein Faden faellt von der Decke' usw. Wie das vor
+sich geht, waere hier zu umfassend, ausserdem hat Wargon eine
+hervorragende Hilfeseite zu den Haeusern erstellt, die sehr
+detailliert erklaert, wie man vorgehen muss.
+
+Desweiteren kann man nun die Truhe in den Seherhaeusern verschieben,
+umbenennen und ihr einen Extralook geben.  Ausserdem kann die Haustuer
+mit speziellen Funktionen in die Aussenbeschreibung des Hauses eingebaut
+oder unsichtbar gemacht werden. Auch hierzu empfehle ich die Lektuere
+der Hilfe- Seite zum Seherhaus.
diff --git a/d/seher/haeuser/special/faq/15 b/d/seher/haeuser/special/faq/15
new file mode 100644
index 0000000..ccbb47c
--- /dev/null
+++ b/d/seher/haeuser/special/faq/15
@@ -0,0 +1,12 @@
+15. NACHWORT
+------------
+
+Erwaehnen will ich auch noch, dass man sich als Seher auch nicht scheuen
+sollte die hoeheren Seher zu fragen, wenn man etwas nicht weiss, die
+meisten werden dann auch antworten, solange der Umgangston in Ordnung
+ist :) Man kann aber nicht nur direkt die Seher fragen, sondern sollte
+auch _immer_ die Seher-Ebene  (-h+) eingeschalten haben, da dort oft
+interessante Dinge diskutiert werden (natuerlich auch viel Stuss :)))
+Auch sollte man regelmaessig die Seher-Rubriken in der MPA lesen, wenn man
+auf dem laufenden bleiben will.
+
diff --git a/d/seher/haeuser/special/faq/16 b/d/seher/haeuser/special/faq/16
new file mode 100644
index 0000000..eed3680
--- /dev/null
+++ b/d/seher/haeuser/special/faq/16
@@ -0,0 +1,14 @@
+16. DANKSAGUNGEN
+----------------
+
+Abschliessend moechte ich noch ein GROSSES DANKESCHOEN an Ryne richten, der
+die Vorfassung dieser FAQ geschrieben hat, und von dem ich auch noch einiges
+uebernommen habe (faul wie ich bin :))
+Und auch an Zephyr noch ein grosses Dankeschoen, da sie diese FAQ fuer mich
+korrekturgelesen hat, und mich auch noch auf einiges hingewiesen hat :)
+Das letzte Dankeschoen geht an Rikus, der sich freundlicherweise die Zeit
+genommen hat, diese FAQ in den Seherladen zu bringen, und mir auch
+ermoeglicht hat, sie immer up to date zu halten :)
+
+
+Sting
diff --git a/d/seher/haeuser/special/faq/2 b/d/seher/haeuser/special/faq/2
new file mode 100644
index 0000000..1228eb2
--- /dev/null
+++ b/d/seher/haeuser/special/faq/2
@@ -0,0 +1,19 @@
+2. SEHERQUEST
+-------------
+
+Eine recht schoene Quest nur fuer Seher hat Troy im Fruehjahr 1995
+geschrieben, allerdings wurde sie erst 4 Monate spaeter
+angeschlossen, nachdem Boing eine Menge Bugs gefixt hatte. Wie auch
+immer:
+Eine lohnende Quest, mit Sicherheit die schoenste, die Troy hier
+geschrieben hat, sie ist auch sehr logisch und die
+Stimmung vor Ort wirklich gut. Also unbedingt spielen, nicht zuletzt,
+weil die Belohnung dafuer wirlich gelungen ist, besser: die Belohnungen!
+Denn a) erhaelt man eine Kristallkugel, mit der man Spieler fuer einen
+Magiepunkt finden kann (siehe auch: Kap. 6: Magische Kugel), und 
+b) erhaelt man das R-Emote, mit dem man sein Emote, das man fuer die
+Amaryllis-Quest erhalten hat, auch auf Spieler ausserhalb des Raumes in
+dem man sich befindet, anwenden kann. 
+
+Viel Spass beim Questen !!!
+
diff --git a/d/seher/haeuser/special/faq/3 b/d/seher/haeuser/special/faq/3
new file mode 100644
index 0000000..f3fc11a
--- /dev/null
+++ b/d/seher/haeuser/special/faq/3
@@ -0,0 +1,23 @@
+3. FLUCHTRICHTUNG
+-----------------
+
+Etwas unscheinbar, aber in meinen Augen ist dies immer noch der
+wertvollste Befehl, den man als Seher neu erhaelt:
+
+fluchtrichtung <Richtung/Befehl>
+
+Ab sofort kannst Du den Befehl 'fluchtrichtung' anwenden.
+Sollte dieser noch nicht aktiviert sein, musst du einmal Ende machen.
+Danach kannst Du selbst bestimmen, in welche Richtung du fliehst, wenn die
+Angst Dich mal ueberkommen sollte (wenn die Vorsicht zuschlaegt)
+Aber Achtung: Der Befehl funktioniert erst bei einem Spielerlevel von 50
+oder hoeher mit 100% Wahrscheinlichkeit, darunter jeweils niedriger.
+Tip: 'fluchtrichtung schlafe ein' funktioniert auch, ebenso wie:
+'fluchtrichtung teile Boing mit Du, ich sterbe gleich'
+Man muss halt die Prioritaeten richtig setzen ;)).
+
+Warum dieser Befehl in meinen Augen besonders wichtig ist? Nun, er
+befreit Spieler mit sehr hoher Wahrscheinlichkeit von dem Risiko, durch
+ein Lag zu sterben, wenn die Flucht im entscheidenden Moment in die falsche
+Richtung zu gehen droht. Man muss sie vorher nur richtig gesetzt haben.
+
diff --git a/d/seher/haeuser/special/faq/4 b/d/seher/haeuser/special/faq/4
new file mode 100644
index 0000000..148796e
--- /dev/null
+++ b/d/seher/haeuser/special/faq/4
@@ -0,0 +1,9 @@
+4. SEHERLEVEL
+-------------
+
+Die Bedeutung der Levels ist insofern hoch, als dass die Erfolgschance
+der Zaubersprueche in den meisten Gilden von dem Level des Spielers abhaengig
+ist. So ist es grundsaetzlich in einigen Gilden moeglich, die maximale
+Erfolgswahrscheinlichkeit eines Zauberspruches erst dann zu erreichen,
+wenn man z.B. Seherlevel 35 erreicht hat.
+
diff --git a/d/seher/haeuser/special/faq/5 b/d/seher/haeuser/special/faq/5
new file mode 100644
index 0000000..9067f0b
--- /dev/null
+++ b/d/seher/haeuser/special/faq/5
@@ -0,0 +1,38 @@
+5. KOMMUNIKATION
+----------------
+
+Als Seher stehen Dir neue Mittel der Kommunikation zur Verfuegung,
+mittels derer Du Nachrichten an andere Spieler senden kannst:
+
+a) Mail
+-------
+
+Ab sofort steht Dir der kostenlose Kurierdienst der MG-Post zur Verfuegung -
+durch Eingabe von 'mail' kannst du von ueberall Post verschicken und
+empfangen. Dies setzt aber voraus, dass Du eine Zeitung bei Dir traegst,
+da das 'mail' eine Zusatzfunktion der MPA ist.
+
+b) MPA
+------
+
+In der Zeitung steht Dir nun die neue Rubrik 'seher.entwicklung' zur
+Verfuegung, in der Du Deine Vorschlaege zur Verbesserung des Sehertums
+einbringen kannst, sowie die Rubrik 'seher.urlaub'.
+
+c) Kanaele
+----------
+
+Zur direkten Kommunikation mit den anderen Sehern steht Dir nun auch ein
+neuer Kanal zur Verfuegung. Er wird mit '-h' angesprochen (nein, nicht mit
+S wie Seher ;)).
+
+d) Fernschreiber
+----------------
+
+Mit dem Fernschreiber aus dem Seherladen kannst Du ganze Nachrichtenbloecke
+an andere Spieler senden. Dies ist haeufig nuetzlich, wenn man laengere
+Texte an einen anderen Spieler senden will, waehrend man z.B. mit diesem
+zusammen eine neue Region oder eine Quest erforscht. Der Einsatz kostet
+15 KP pro Spieler, an den die Nachricht gesendet wird, man kann auch an
+mehrere Spieler gleichzeitig die Meldung ausgeben lassen.
+
diff --git a/d/seher/haeuser/special/faq/6 b/d/seher/haeuser/special/faq/6
new file mode 100644
index 0000000..6620ae2
--- /dev/null
+++ b/d/seher/haeuser/special/faq/6
@@ -0,0 +1,17 @@
+6. MAGISCHE KUGEL
+-----------------
+
+Im Haus der Hexe am Friedhof findest Du eine Kugel, die Du mitnehmen
+kannst - mit ihr kannst du durch den Befehl 'finde <Spielername>'
+jeden eingeloggten Spieler lokalisieren - ein Einsatz kostet jeweils 9
+KP.
+
+Mit der Einfuehrung der Seherquest von Troy erhaelt man als Questbelohnung
+eine - der Name des Quests laesst es vermuten - Kristallkugel, die dieselbe
+Funktion wie die Seherkugel erfuellt. Allerdings kostet diese pro Einsatz
+nur 1 KP. Dafuer erhaelt allerdings der beobachtete Spieler die Meldung
+'Jemand beschwoert Dein Bild herauf.', was auf Dauer etwas aufdringlich
+wird, da der Betroffene ja nicht weiss, wer ihn da sucht.
+Welche Kugel du als Seher benutzt, kommt wohl auf die eigene Entscheidung und
+den jeweiligen Fall an.
+
diff --git a/d/seher/haeuser/special/faq/7 b/d/seher/haeuser/special/faq/7
new file mode 100644
index 0000000..1fdfc6c
--- /dev/null
+++ b/d/seher/haeuser/special/faq/7
@@ -0,0 +1,15 @@
+7. SEHERLADEN
+-------------
+
+Auf Svolvaer findest Du einen Laden, der spezielle Items nur fuer Seher
+anbietet. Interessant ist hier der Bumerang, der Wecker, die Partytroete,
+der Magische Beutel und der kleine graue Kasten.
+
+Fuer das Angebot im Seherladen ist Loco zustendig, der das Angebot
+sicher gerne erweitern wuerde, und daher fuer Ideen immer aufgeschlossen
+ist - daher schreibe ihm ruhig ein Mail, wenn Du eine Idee hast, was
+dort zukuenftig noch angeboten werden koennte.
+
+Uebrigens gibts auch in der Parawelt in Port Vain nen Seher-Laden, den
+muesst ihr allerdings suchen :))
+
diff --git a/d/seher/haeuser/special/faq/8 b/d/seher/haeuser/special/faq/8
new file mode 100644
index 0000000..8d0e537
--- /dev/null
+++ b/d/seher/haeuser/special/faq/8
@@ -0,0 +1,51 @@
+8. DESIGNER-SEHER
+-----------------
+
+Nun, vielleicht ist dieser Titel auch ein wenig uebertrieben. Aber als
+Seher kannst Du (fast) Deine gesamte Erscheinung im MUD selbst gestalten:
+Nun, Designertypen glaenzen durch ihr Aeusseres, also fangen wir mal beim
+Aussehen an: Was die Spieler sehen, wenn sie Dich untersuchen, kannst Du
+mit dem Befehl 'extralook' bestimmen. Die dort eingegebene Beschreibung
+wird der Auflistung dessen, was Du bei Dir traegst, vorangestellt.
+
+Zur Erscheinung gehoert auch das Auftreten, also designen wir mal ein
+bisschen an Deinen Meldungen herum, die erscheinen, wenn Du einen Raum
+betrittst oder verlaesst. Dies kannst Du mit:
+'setmin <TEXT>' bzw. 'setmout <TEXT>' setzen.
+Und wenn Du moechtest, kannst Du mit 'setmmout' bzw. 'setmmin'
+die Meldungen setzen, die erscheinen, wenn Du einen Ort mittels eines
+Teleports betrittst/verlaesst.
+
+Doch auch Designerseher leben unter dem Joch des Armageddon, und sind daher
+manchmal waffen- und ruestungslos. Wer es aber nicht ertragen kann, in diesen
+bitteren Stunden immer die Meldung 'greift mit blossen Haenden an' zu sehen,
+der kann sich mittels des Befehls 'sethands <TEXT>' eine martialischere
+(oder auch zaertlichere, je nach Geschmack ;)) Kampfmeldung fuer seine Haende
+setzen.
+
+Nun, ein richtiger Seher hat natuerlich auch einen interessanteren
+Namen, nicht so was oedes wie 'Wurst, die Seherin'. Und deshalb koennte
+sich Wurst einen eigenen Namen geben, mit dem Befehl 'titel <TEXT>'.
+Dann hiesse sie hinterher beispielsweise 'Wurst ist ne Wurst'.
+Und wem das noch nicht laessig genug ist, der setzt sich mit
+'presay <TEXT>' auch noch einen Namensvorsatz.
+Beispiel: 'Die Oberwurstseherin Wurst ist ne Wurst'.
+Wenngleich das vielleicht doch nicht SOOO cool ist.
+Geschmackssache halt ;).
+
+
+Ach ja, und wenn man dann bei all der Rumdesignerei vergessen haben sollte,
+was man ueberhaupt eingegeben hat, so erhaelt man mit dem Befehl 'review'
+einen Ueberblick ueber die meisten Einstellungen.
+
+Eine Anmerkung noch: Der Toeter hat vor langer Zeit eine kleine Erweiterung
+in den Befehl setmout eingebaut:
+Nunmehr ist es moeglich, den Text 'nach (Richtung)' nicht mehr nur am Ende,
+sondern irgendwo im Text erscheinen zu lassen: dann muss im Text der
+out-message ein # erscheinen, die dann durch 'nach (Richtung)' ersetzt wird.
+Unterdruecken kannst Du die Richtungsangabe allerdings nicht, wenn Du das #r
+weglaesst, wird diese Angabe einfach hinten angehaengt..
+
+Hilfe dazu liefern aber natuerlich auch wiederum die Seherhilfeseiten unter
+'hilfe seher'
+
diff --git a/d/seher/haeuser/special/faq/9 b/d/seher/haeuser/special/faq/9
new file mode 100644
index 0000000..bb5ca35
--- /dev/null
+++ b/d/seher/haeuser/special/faq/9
@@ -0,0 +1,11 @@
+9. FEHLERMELDUNG
+----------------
+
+Ein nicht besonders wichtiger, aber in seinen Auswirkungen haeufig
+angetroffener Befehl: Fehlermeldung. Wenn Dir das ewige
+'Wie bitte ?' langsam zu eintoenig wird, so kannst Du mit
+'Fehlermeldung <TEXT>' einen neuen Text fuer Deine Fehlermeldung setzen -
+uebrigens genauso wie fuer die Uhrmeldung, was allerdings schon als
+Spieler moeglich ist, was jedoch scheinbar nur recht wenige Spieler
+wissen.
+
diff --git a/d/seher/haeuser/special/faq/faq.o b/d/seher/haeuser/special/faq/faq.o
new file mode 100644
index 0000000..a6cd59a
--- /dev/null
+++ b/d/seher/haeuser/special/faq/faq.o
@@ -0,0 +1,3 @@
+#0:0
+properties ([:2])
+synonym (["level":"4","finger":"12","haus":"14","quest":"2","kugel":"6","designer-seher":"8","portal":"11","echo":"13","fehlermeldung":"9","quests":"2","remote":"13","fluchtrichtung":"3","transportmoeglichkeit":"1","test":"20","seherquest":"2","haeuser":"14","r-emote":"13","seherhaus":"14","para-portal":"11","transport":"1","seherhaeuser":"14","seherladen":"7","magische kugel":"6","transportmoeglichkeiten":"1","paralleldimension":"10","danksagung":"16","seherquests":"2","seherlevel":"4","nachwort":"15","danksagungen":"16","designerseher":"8","finger-befehl":"12","kommunikation":"5","paraportal":"11","fingerbefehl":"12",])
diff --git a/d/seher/haeuser/special/faq/inhalt b/d/seher/haeuser/special/faq/inhalt
new file mode 100644
index 0000000..40780bc
--- /dev/null
+++ b/d/seher/haeuser/special/faq/inhalt
@@ -0,0 +1,28 @@
+Inhalt der SeherFAQ 3.02:
+-------------------------
+
+   0. Neuigkeiten
+   1. Transportmoeglichkeiten
+   2. Seherquests
+   3. Fluchtrichtung
+   4. Seherlevel
+   5. Kommunikation
+   6. Magische Kugel
+   7. Seherladen
+   8. Designer-Seher
+   9. Fehlermeldung
+  10. Paralleldimension
+  11. Para-Portal 8
+  12. Finger-Befehl
+  13. Echo, R-Emote
+  14. Seherhaeuser
+  15. Nachwort
+  16. Danksagungen
+
+Die einzelnen Kapitel liest Du einfach mit z.B. lies 12.
+Wenn Du eine Idee hast, was an der FAQ verbessert, geaendert oder
+hinzugefuegt werden sollte, dann waere ich Dir sehr dankbar,
+wenn Du mir eine kurze Mail schicken wuerdest.
+
+Sting
+
diff --git a/d/seher/haeuser/special/faq/seherfaq b/d/seher/haeuser/special/faq/seherfaq
new file mode 100644
index 0000000..1790941
--- /dev/null
+++ b/d/seher/haeuser/special/faq/seherfaq
@@ -0,0 +1 @@
+Mach mal "lies inhalt"
diff --git a/d/seher/haeuser/special/rj.c b/d/seher/haeuser/special/rj.c
new file mode 100644
index 0000000..42f3b98
--- /dev/null
+++ b/d/seher/haeuser/special/rj.c
@@ -0,0 +1,26 @@
+inherit "/std/thing";
+
+#include <properties.h>
+#include "../haus.h"
+
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+
+  SetProp(P_SHORT,0);
+  SetProp(P_NOGET, 1);
+  AddId("r&j");
+  AddCmd("lies", "lies");
+}
+
+static int lies(string str)
+{
+  int nr;
+
+  if (!str || sscanf(str, "seite %d", nr) != 1)
+    return notify_fail("Syntax: lies seite <nr>\n"), 0;
+
+  this_player()->More(SPECIALPATH+"rom_jul/seite"+nr, 1);
+  return 1;
+}
diff --git a/d/seher/haeuser/special/seherfaq.c b/d/seher/haeuser/special/seherfaq.c
new file mode 100644
index 0000000..9ae96e6
--- /dev/null
+++ b/d/seher/haeuser/special/seherfaq.c
@@ -0,0 +1,247 @@
+inherit "std/thing";
+inherit "std/more";
+inherit "/mail/nedit";
+
+#include <properties.h>
+#include <wizlevels.h>
+#include "../haus.h"
+
+#define FAQ(f) (SPECIALPATH+"faq/"+(f))
+#define FAQUSER ({ "ryne", "toss", "sting" })
+#define IS_USER(pl) (member(FAQUSER, getuid(pl)) >= 0)
+
+static string *seite;
+static string f,*s;
+
+mapping synonym = ([]);
+
+create()
+{
+  ::create();
+  SetProp(P_NAME,"SeherFAQ");
+  SetProp(P_GENDER,FEMALE); 
+  SetProp(P_NOGET,1);
+  AddId( ({"faq","seherfaq","FAQ","SeherFAQ" }) );
+  AddCmd("lies","lesen");
+  AddCmd(({"beschreib","beschreibe"}),"beschreiben");
+  AddCmd("seiten","seiten");
+  AddCmd(({"loesch","loesche"}),"loeschen");
+  AddCmd(({"kopier","kopiere"}),"kopieren");
+  restore_object(FAQ("faq"));
+  seite = get_dir(FAQ("*"))-({".","..","faq.o"});
+}
+
+init()
+{
+  thing::init();
+  if (this_player() && IS_USER(this_player()))
+    nedit::init_rescue();
+}
+
+string
+_query_short()
+{
+  if (this_player() && IS_SEER(this_player()))
+    return "Die SeherFAQ";
+  else
+    return 0;
+}
+
+string 
+_query_long()
+{
+  if (this_player() && IS_SEER(this_player()))
+    return "Dies ist Rynes SeherFAQ.\n\
+Wenn Du weitere Fragen oder Anregungen zu dieser FAQ hast,\n\
+sei so nett und schicke Ryne einen kurzen Brief\n\n\
+Zum Inhalt der FAQ kommst Du mit >lies inhalt<\n";
+  else
+    return 0;
+}
+
+int
+id(string str)
+{
+  if (!IS_SEER(this_player()))
+    return 0;
+  else
+    return ::id(str);
+}
+
+int
+seiten(string str)
+{
+  int i,j;
+  string *syn;
+  closure filter_ldfied;
+
+  if (!IS_USER(this_player()))
+    return 0;
+
+  if(!(i = sizeof(seite)))
+    write("Keine Seiten beschrieben!\n");
+  else {
+    filter_ldfied = lambda(({'ind,'page,'map_ldfied/*'*/}),
+		    ({#'==,'page,({#'[/*]'*/,'map_ldfied,'ind})}));
+    for (str = "",i-=1;i>=0;i--) {
+      syn = m_indices(filter_indices(synonym,filter_ldfied,seite[i],synonym));
+      printf("Seite '%s' - ",seite[i]);
+      if (j=sizeof(syn)) {
+	for(j-=1;j>=0;j--)
+	  printf("'%s' ",syn[j]);
+	write("\n");
+      }
+      else
+	write( "(keine Synonyme definiert)\n");
+    }
+  }
+  return 1;
+}
+
+int
+lesen(string str)
+{
+  if (!IS_SEER(this_player()))
+    return 0;
+
+  if (!str || str == "") {
+    notify_fail("Was willst Du denn lesen?\n");
+    return 0;
+  }
+  str = lower_case(this_player()->_unparsed_args());
+  if (member(seite,str)<0) {
+    if (!member(synonym,str)) {
+      notify_fail("Diese Seite gibt es nicht...\n");
+      return 0;
+    }
+    else 
+      str = synonym[str];
+  }
+  this_player()->More(FAQ(str),1);
+  return 1;
+}
+
+int
+beschreiben(string str)
+{
+  string faq,*desc;
+  int i;
+
+  if (!IS_USER(this_player()))
+    return 0;
+
+  str = lower_case(this_player()->_unparsed_args());
+
+  if (!str || str == "") {
+    notify_fail("Was willst Du denn beschreiben?\n");
+    return 0;
+  }
+  if (sscanf(str,"%s %s",faq,str)!=2 || !id(faq)) {
+    notify_fail("Syntax: beschreibe faq <seite>\n");
+    return 0;
+  }
+  str = implode(old_explode(str," ")-({""}), " ");
+  desc = old_explode(implode(old_explode(str,", "),","),",");
+  f = 0;
+  s = 0;
+
+  for (i=sizeof(desc)-1;i>=0;i--) {
+    if (member(seite,desc[i])>=0) {
+      f = desc[i];
+      s = desc-({desc[i]});
+      break;
+    }
+  }
+  if (!f) {
+    f = desc[0];
+    s = desc[1..];
+  }
+  write("Bitte Beschreibung fuer die FAQ-Seite '"+f+"' angeben\n");
+  nedit("faq");
+  return 1;
+}
+
+static void
+faq(string str)
+{
+  int i;
+  string file;
+
+  if (!str || str == 0) {
+    write("** Abbruch! **\nNichts geaendert!\n");
+    return;
+  }
+  
+  if (file_size(file=FAQ(f))>=0)
+    rm(file);
+  write_file(file, str);
+  if (member(seite,f)==-1)
+    seite += ({f});
+
+  for (i=sizeof(s)-1;i>=0;i--) 
+    synonym += ([ s[i] : f ]);
+
+  save_object(FAQ("faq"));
+}
+
+int
+loeschen(string str)
+{
+  string *syn;
+  int i;
+
+  if(!IS_USER(this_player()))
+    return 0;
+
+  str = lower_case(this_player()->_unparsed_args());
+  if (!str || str == "") {
+    notify_fail("Was willst Du denn loeschen?\n");
+    return 0;
+  }
+  if (member(seite,str)>=0) {
+    syn = m_indices(filter_indices(synonym,
+				 lambda(({'ind,'page,'map_ldfied/*'*/}),
+					({#'==,'page,({#'[/*]'*/,'map_ldfied,'ind})}))
+				 ,seite[i],synonym));
+    rm(FAQ(str));
+    seite -= ({str});
+  }
+  else {
+    str = implode(old_explode(str," ")-({""}), " ");
+    syn = old_explode(implode(old_explode(str,", "),","),",");
+  }
+  for (i=sizeof(syn)-1; i>=0; i--)
+    synonym = filter_indices(synonym,
+                             lambda(({'key,'cmp}),
+				    ({#'!=,'key,'cmp/*'*/})),
+			     syn[i]);
+
+  save_object(FAQ("faq"));
+  write("OK\n");
+  return 1;
+}
+
+int
+kopieren(string str)
+{
+  string *part;
+  int i;
+
+  if (!IS_USER(this_player()))
+    return 0;
+
+  str = lower_case(this_player()->_unparsed_args());
+  part = old_explode(str," ")-({""});
+  if(sizeof(part)<4 || !id(part[0]) || part[2] != "nach" || 
+     member(seite,part[1]) == -1) {
+    notify_fail("kopiere faq <seite> nach <alias>\n");
+    return 0;
+  }
+  str = part[1];
+  part=old_explode(implode(old_explode(implode(part[3..]," "),", "),","),",");
+  for(i=sizeof(part)-1;i>=0;i--)
+    synonym += ([ part[i] : str ]);
+  save_object(FAQ("faq"));
+  write("OK.\n");
+  return 1;
+}
diff --git a/d/seher/haeuser/special/seherfaqinvis.c b/d/seher/haeuser/special/seherfaqinvis.c
new file mode 100644
index 0000000..45d3625
--- /dev/null
+++ b/d/seher/haeuser/special/seherfaqinvis.c
@@ -0,0 +1,236 @@
+inherit "std/thing";
+inherit "std/more";
+inherit "/mail/nedit";
+
+#include <properties.h>
+#include <wizlevels.h>
+#include "../haus.h"
+
+#define FAQ(f) (SPECIALPATH+"faq/"+(f))
+#define FAQUSER ({ "ryne", "toss", "sting" })
+#define IS_USER(pl) (member(FAQUSER, getuid(pl)) >= 0)
+
+static string *seite;
+static string f,*s;
+
+mapping synonym = ([]);
+
+void create()
+{
+  ::create();
+  SetProp(P_NAME,"SeherFAQ");
+  SetProp(P_GENDER,FEMALE); 
+  SetProp(P_NOGET,1);
+  AddId( ({"faq","seherfaq","FAQ","SeherFAQ" }) );
+  AddCmd("lies","lesen");
+  AddCmd(({"beschreib","beschreibe"}),"beschreiben");
+  AddCmd("seiten","seiten");
+  AddCmd(({"loesch","loesche"}),"loeschen");
+  AddCmd(({"kopier","kopiere"}),"kopieren");
+  restore_object(FAQ("faq"));
+  seite = get_dir(FAQ("*"))-({".","..","faq.o"});
+}
+
+void init()
+{
+  thing::init();
+  if (this_player() && IS_USER(this_player()))
+    nedit::init_rescue();
+}
+
+string
+_query_short()
+{
+    return 0;
+}
+
+string 
+_query_long()
+{
+    return 0;
+}
+
+int
+id(string str)
+{
+    return 0;
+}
+
+int
+seiten(string str)
+{
+  int i,j;
+  string *syn;
+  closure filter_ldfied;
+
+  if (!IS_USER(this_player()))
+    return 0;
+
+  if(!(i = sizeof(seite)))
+    write("Keine Seiten beschrieben!\n");
+  else {
+    filter_ldfied = lambda(({'ind,'page,'map_ldfied/*'*/}),
+		    ({#'==,'page,({#'[/*]'*/,'map_ldfied,'ind})}));
+    for (str = "",i-=1;i>=0;i--) {
+      syn = m_indices(filter_indices(synonym,filter_ldfied,seite[i],synonym));
+      printf("Seite '%s' - ",seite[i]);
+      if (j=sizeof(syn)) {
+	for(j-=1;j>=0;j--)
+	  printf("'%s' ",syn[j]);
+	write("\n");
+      }
+      else
+	write( "(keine Synonyme definiert)\n");
+    }
+  }
+  return 1;
+}
+
+int
+lesen(string str)
+{
+  if (!IS_USER(this_player()))
+    return 0;
+
+  if (!str || str == "") {
+    notify_fail("Was willst Du denn lesen?\n");
+    return 0;
+  }
+  str = lower_case(this_player()->_unparsed_args());
+  if (member(seite,str)<0) {
+    if (!member(synonym,str)) {
+      notify_fail("Diese Seite gibt es nicht...\n");
+      return 0;
+    }
+    else 
+      str = synonym[str];
+  }
+  this_player()->More(FAQ(str),1);
+  return 1;
+}
+
+int
+beschreiben(string str)
+{
+  string faq,*desc;
+  int i;
+
+  if (!IS_USER(this_player()))
+    return 0;
+
+  if (!str || str == "") {
+    notify_fail("Was willst Du denn beschreiben?\n");
+    return 0;
+  }
+  
+  str = lower_case(this_player()->_unparsed_args());
+
+  if (sscanf(str,"faq %s",str)!=1) {
+    notify_fail("Syntax: beschreibe faq <seite>\n");
+    return 0;
+  }
+  str = implode(old_explode(str," ")-({""}), " ");
+  desc = old_explode(implode(old_explode(str,", "),","),",");
+  f = 0;
+  s = 0;
+
+  for (i=sizeof(desc)-1;i>=0;i--) {
+    if (member(seite,desc[i])>=0) {
+      f = desc[i];
+      s = desc-({desc[i]});
+      break;
+    }
+  }
+  if (!f) {
+    f = desc[0];
+    s = desc[1..];
+  }
+  write("Bitte Beschreibung fuer die FAQ-Seite '"+f+"' angeben\n");
+  nedit("faq");
+  return 1;
+}
+
+static void
+faq(string str)
+{
+  int i;
+  string file;
+
+  if (!str || str == 0) {
+    write("** Abbruch! **\nNichts geaendert!\n");
+    return;
+  }
+  
+  if (file_size(file=FAQ(f))>=0)
+    rm(file);
+  write_file(file, str);
+  if (member(seite,f)==-1)
+    seite += ({f});
+
+  for (i=sizeof(s)-1;i>=0;i--) 
+    synonym += ([ s[i] : f ]);
+
+  save_object(FAQ("faq"));
+}
+
+int
+loeschen(string str)
+{
+  string *syn;
+  int i;
+
+  if(!IS_USER(this_player()))
+    return 0;
+
+  str = lower_case(this_player()->_unparsed_args());
+  if (!str || str == "") {
+    notify_fail("Was willst Du denn loeschen?\n");
+    return 0;
+  }
+  if (member(seite,str)>=0) {
+    syn = m_indices(filter_indices(synonym,
+				 lambda(({'ind,'page,'map_ldfied/*'*/}),
+					({#'==,'page,({#'[/*]'*/,'map_ldfied,'ind})}))
+				 ,seite[i],synonym));
+    rm(FAQ(str));
+    seite -= ({str});
+  }
+  else {
+    str = implode(old_explode(str," ")-({""}), " ");
+    syn = old_explode(implode(old_explode(str,", "),","),",");
+  }
+  for (i=sizeof(syn)-1; i>=0; i--)
+    synonym = filter_indices(synonym,
+                             lambda(({'key,'cmp}),
+				    ({#'!=,'key,'cmp/*'*/})),
+			     syn[i]);
+
+  save_object(FAQ("faq"));
+  write("OK\n");
+  return 1;
+}
+
+int
+kopieren(string str)
+{
+  string *part;
+  int i;
+
+  if (!IS_USER(this_player()))
+    return 0;
+
+  str = lower_case(this_player()->_unparsed_args());
+  part = old_explode(str," ")-({""});
+  if(sizeof(part)<4 || part[0]!="faq" || part[2] != "nach" || 
+     member(seite,part[1]) == -1) {
+    notify_fail("kopiere faq <seite> nach <alias>\n");
+    return 0;
+  }
+  str = part[1];
+  part=old_explode(implode(old_explode(implode(part[3..]," "),", "),","),",");
+  for(i=sizeof(part)-1;i>=0;i--)
+    synonym += ([ part[i] : str ]);
+  save_object(FAQ("faq"));
+  write("OK.\n");
+  return 1;
+}
diff --git a/d/seher/haeuser/special/seherfaqmobil.c b/d/seher/haeuser/special/seherfaqmobil.c
new file mode 100644
index 0000000..250db4b
--- /dev/null
+++ b/d/seher/haeuser/special/seherfaqmobil.c
@@ -0,0 +1,79 @@
+// tragbare Seher-FAQ, gibt es im Seherladen zu kaufen bzw. automatisch zur Seherwerdung
+// Rikus@mg.mud.de
+
+inherit "std/thing";
+
+#include <properties.h>
+#include <wizlevels.h>
+#include "../haus.h"
+
+#define FAQ(f) (SPECIALPATH+"faq/"+(f))
+
+static string *seite;
+
+mapping synonym = ([]);
+
+create()
+{
+//  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT,"Die SeherFAQ");
+  SetProp(P_NAME,"SeherFAQ");
+  SetProp(P_GENDER,FEMALE); 
+  SetProp(P_LONG,"Dies ist die SeherFAQ. Wenn Du weitere Fragen oder "+
+          "Anregungen zu dieser FAQ\nhast, sei so nett und schicke Sting "+
+          "einen kurzen Brief. Zum Inhalt der FAQ\nkommst Du mit "+
+          ">lies inhalt<\n");
+  SetProp(P_WEIGHT,100);
+  SetProp(P_VALUE,1000);
+  AddId(({"faq","seherfaq"}));
+  AddCmd("lies","lesen");
+  restore_object(FAQ("faq"));
+  seite = get_dir(FAQ("*"))-({".","..","faq.o"});
+}
+
+init()
+{
+  object o;
+  ::init();
+  if ((o=environment()) && query_once_interactive(o) && !IS_SEER(o))
+     call_out("wech",0);
+}
+
+wech()
+{
+   write("Die Seher-FAQ lacht Dich aus und loest sich in Wohlgefallen auf.\n");
+   remove();
+}
+
+int lesen(string str)
+{
+  if (!str || str == "") {
+    notify_fail("Was willst Du denn lesen?\n");
+    return 0;
+  }
+  if (environment()!=this_player()) 
+  {
+     notify_fail("Du musst die FAQ schon erst nehmen!\n");
+     return 0;
+  }
+  str = lower_case(this_player()->_unparsed_args());
+  if (member(seite,str)<0) {
+    if (!member(synonym,str)) {
+      notify_fail("Diese Seite gibt es nicht...\n");
+      return 0;
+    }
+    else 
+      str = synonym[str];
+  }
+  this_player()->More(FAQ(str),1);
+  return 1;
+}
+
+string _query_extralook()
+{
+   object o;
+   if ((o=environment()) && (o->QueryProp(P_GENDER)==FEMALE))
+      return "Mit ihrer Seher-FAQ outet sie sich als Wurstseherin.\n";
+   return "Mit seiner Seher-FAQ outet er sich als Wurstseher.\n";
+}
diff --git a/d/seher/haeuser/special/special/faq/0 b/d/seher/haeuser/special/special/faq/0
new file mode 100644
index 0000000..95ec97b
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/0
@@ -0,0 +1,18 @@
+0. NEUIGKEITEN
+Hier aktuelle Aenderungen der SeherFAQ 2.0 (Stand 2.11.1995):
+
+- Zunaechst einen grossen Dank an Cuchullin, der eine detaillierte
+  Beschreibung der Artikel aus dem Seherladen zur FAQ beigetragen hat.
+- Endlich die neue FAQ! Die meisten Informationen waren fast ein Jahr alt,
+  und ich hoffe, alle relevanten Informationen auf dem aktuellen Stand zu
+  haben.
+- Mehrere Seherportale wurden in letzter Zeit neu angehaengt. Welche nunmehr
+  vorhanden sind, kannst du in '1. Transportmoeglichkeiten' nachlesen.
+- Toeter hat das setmout geaendert - nunmehr kann die Richtungsangabe
+  weggelassen oder an eine beliebige Stelle im Text gesetzt werden.
+- Im Seherladen gibt es einige neue Artikel; hierzu '7. Sehrladen'.
+- Die Paralleldimension waechst staendig; einige interessante Monster hieraus
+  sind in '10. Paralleldimension' aufgefuehrt, aber wie so oft, selbst
+  schauen, und man findet was neues ;-).
+
+
diff --git a/d/seher/haeuser/special/special/faq/1 b/d/seher/haeuser/special/special/faq/1
new file mode 100644
index 0000000..c9954c0
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/1
@@ -0,0 +1,47 @@
+1. Transportmoeglichkeiten.
+
+Als Seher kannst Du nunmehr auf einige neue Fortbewegungsmoeglichkeiten
+zugreifen, um schneller von einem Ort zum anderen zu gelangen:
+
+A) Seherportale
+Als Seher kannst Du ab sofort magische Portale nutzen, um schneller
+von einem Ort zum anderen zu gelangen. es gibt im Moment 21 Tore,
+die an folgenden Stellen liegen:
+
+ 1. Tamibar Dschungel              11. Klippenpfad auf Aurora
+ 2. Bei den Drachenzinnen          12. Hauptstrase Suedende auf Svolvaer
+ 3. Port Vainer Stadttor           13. Im Bergdorf
+ 4. An einem Bach im Gebirge       14. Vorplatz Traukapelle
+ 5. Im Polargebiet                 15. Lash
+ 6. Tundra                         16. Im Dschungel
+ 7. Feenstrasse                    17. Auf den Wolken
+ 8. Waldweg                        18. Am Fluss Tain Wen Tai
+ 9. Ruinenstadt                    19. Ritterdenkmal bei Orkhausen
+10. Vorplatz Arena                 20. Rodelbahn
+21. Hobbitdorf
+
+Diese Tore kannst du mit dem Befehl 'teleportiere' benutzen.Dies setzt
+allerdings voraus, dass Du dieses Portal bereits gefunden hast. Du musst die
+Portale also zunaechst einmal suchen ;).Ein Teleportieren zu einem anderen
+Portal kostet 50 MP. Da danach immer wieder gefragt wird: Natuerlich kann man
+die Portale nur benutzen, wenn man sich gerade bei einem befindet ;)).
+
+B) Pegasus
+Du kannst ebenso mit Pegasus durch die Gegend fliegen - das kostet keine
+MP und geht auch recht fixe - Haltestellen: vor dem Elfendorf, im
+Polargebiet, Zwergenstadt, Svolvaer, Port Vain. Einmal mitfliegen
+kostet 222 Muenzen, und man kann als Seher einen normalen Spieler
+zum Mitfliegen einladen. Ich fluche regelmaessig ueber Pegasus, weil
+man es kaum schafft, >besteige Pferd< einzugeben, so schnell hebt
+das Tierchen wieder ab. Deshalb hab ich mir ein ali darauf gelegt, ist
+wohl empfehlenswert.
+
+C) Siebenmeilenstiefel
+Diese kann man im Seher im Seherladen kaufen; sie haben die Eigenschaft,
+ihren Traeger immer bis zur naechsten Kreuzung weiterzutragen, was bei langen
+Wegen sinnvoll sein kann, in vielen Gegenden jedoch nicht funktioniert, weil
+in jedem Raum mehr als zwei Ausgaenge vorkommen. Ausserdem ist das Ganze
+extrem gewoehnungsbeduerftig, da man bekannte Wege jetzt mit ganz anderen
+Tastenkombinationen abgeht, als wenn man die Wege einfach wie gewohnt
+'runterrattert'. Einfach mal austesten, lustig ists allemal.
+
diff --git a/d/seher/haeuser/special/special/faq/10 b/d/seher/haeuser/special/special/faq/10
new file mode 100644
index 0000000..90e96f4
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/10
@@ -0,0 +1,23 @@
+
+10. PARALLELDIMENSION
+
+Warscheinlich hast Du schon immer gedacht: 'Eine Welt jenseits des
+MorgenGrauens gibt es genausowenig wie Geheimbuende!' - Nun, bezueglich der
+Geheimbuende mag das zutreffen, und natuerlich sollst Du auch keine Welt
+haben neben dem MorgenGrauen, aber es gibt im MUD eine Welt, die nur Seher
+betreten koennen: Die Paralleldimension.
+
+Momentan ist die Paralleldimension noch eine relativ kleine Welt, die von den
+meisten Sehern eher per Zufall betreten wird; man erreicht sie naemlich nur,
+wenn beim teleportieren in einem magischen Portal etwas schief laeuft... nun,
+diesen Effekt kann man umkehren, es gibt auch ein starres Portal, das einem
+das bewusste Betreten / Verlassen der Parallelwelt ermoeglicht. Bevor man
+jedoch Hals ueber Kopf in bekannte Sphaeren zurueckkehrt, sollte man sich
+schon ein wenig in der Paralleldimension umschauen, da diese stetig ausgebaut
+wird und mittlerweile einige interessante Monster enthaelt. Diese finden sich
+Hauptsaechlich in der Ebene, wo man einmal den Weghier, den Cu Sith und den
+(den ? (DIE!)) Goblindrachen aufsuchen sollte; aber auch in und um Schrumpfis
+Gruft finden sich einige interessante Monster.
+
+Und: Have fun, schwache Nerven sollte man dort nicht mitbringen :-).
+
diff --git a/d/seher/haeuser/special/special/faq/11 b/d/seher/haeuser/special/special/faq/11
new file mode 100644
index 0000000..792b75a
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/11
@@ -0,0 +1,14 @@
+
+11.FINGER-BEFEHL
+
+Wenn Du einmal probierst, einen anderen Spieler zu fingern, wirst Du
+feststellen, dass Du nun einige zusaetzliche Informationen erhaelst, bei
+normalen Spielern ihr Alter, und bei Sehern / Magiern zusaetzlich die
+ueber Plan und Projekt eingegebenen Informationen.
+
+So, und wenn Du dann auch obendrein nicht allzu zimperlich bist, und gerne
+Leute Dich fingern laesst, so wirst Du natuerlich auch einen Plan und ein
+Projekt in Deinem 'finger' einsetzen. Dies kannst Du mit Hilfe des
+Planeditors machen, den Du im Seherladen erhaelst.
+
+
diff --git a/d/seher/haeuser/special/special/faq/12 b/d/seher/haeuser/special/special/faq/12
new file mode 100644
index 0000000..6593f64
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/12
@@ -0,0 +1,15 @@
+12. ECHO & R-EMOTE
+Solltest Du irgendwann den Archie geloest haben (was dringend zu
+empfehlen ist, denn das ist definitiv eines der besten quests hier neben
+den Pyras und Tarko - IMHO) - dann steht Dir als Seher hinterher der
+ECHO-Befehl zur Verfuegung, mit dessen Hilfe Du in der Lage bist,
+einen x-Beliebigen Text in dem Raum abzusetzen, in dem Du Dich
+befindest, so daDich auch alle anderen Spieler diesen Text lesen koennen...
+Nette Spielerei, die leider 48 MP kostet, aber trotzdem immer zu einem
+kleinen Gag gut ist (gelle, Bloodgore ???).
+
+Solltest Du das Seherquest 'Die Kristallkugel' geloest haben, so steht Dir
+auch das 'r-emote' zur Verfuegung, das es Dir ermoeglicht, das seit der
+Loesung des Amaryllis-Quests (hast Du doch wohl !!) moegliche emote auch auf
+Spieler ausserhalb des Raumes anzuwenden.
+
diff --git a/d/seher/haeuser/special/special/faq/13 b/d/seher/haeuser/special/special/faq/13
new file mode 100644
index 0000000..22866ec
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/13
@@ -0,0 +1,14 @@
+13. MAGISCHER BEUTEL
+In dem Seherladen auf Svolvaer kannst Du einen magischen Beutel kaufen, mit
+dem Du Objekte erzeugen kannst. Grundsaetzlich ist dies zwar nur ein Spass,
+aber es lassen sich damit grundsaetzlich auch Objekte erzeugen, die im Spiel
+verwendet werden koennen. Allerdings halten diese Objekte nur knappe 15
+minuten, bevor sie sich in Luft aufloesen.
+
+Da die Anwendung des Beutels oftmals daran scheitert, dass er ein wenig kom-
+pliziert zu Bedienen und schlecht dokumentiert ist, habe ich eine step-by-
+step Anleitung dazu geschrieben, die allerdings recht ausfuehrlich ist;
+solltest Du die FAQ nur fuer einen kurzen Ueberblick lesen wollen, so kannst
+Du die Anleitung natuerlich auch spaeter lesen. Ansonsten: Die Anleitung
+liest man mit 'lies Anleitung'!! ;-)
+
diff --git a/d/seher/haeuser/special/special/faq/14 b/d/seher/haeuser/special/special/faq/14
new file mode 100644
index 0000000..044850f
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/14
@@ -0,0 +1,61 @@
+14. SEHERHAEUSER
+Interessantes Thema: Die Seherhauser: Sie gibt es nun seit ca. Mitte
+Oktober 94, und folgendes ist dazu anzumerken:
+I. In der Bausparkasse in Drachenhort gibt es die noetigen Informationen
+zu den Hauesern. Das Informationsblatt fuer 10.000 Muenzen sollte man
+sich eben zulegen, auch wenn nur eine Seite Infos drinstehen, und es
+nach dem ersten Lesen weg ist :((.
+Grundsaetzlich gibt es zwei Moeglichkeiten, zu einem Haus zu kommen:
+a). Der schnelle Vertrag: Kostet 2 Mio EPs, man muss alle 2 online Stunden
+eine Rate von 80.000 EPs einzahlen - insgesamt also 25 Raten. Die
+Zeit kann u.U. nach einem Crash etwas knapp werden, da man dann
+nicht die noetige Zeit hat, um sich selbst erstmal Ausruestung zu
+besorgen, und ja vor allem alle Spieler dabei sind, sich Ausruestung zu
+beschaffen, so dass nicht viel zu holen ist.
+b). Der langsame Vertrag: Kostet 2.4 Mio Eps - also 30 Raten,
+man muss alle 4 online Stunden seine Rate von 80.000 EPs
+einzahlen. Sicher besser geeignet fuer diejenigen, die oft talken, und nicht
+die naechste Woche nur mit metzeln verbringen wollen -
+Geschmackssache.... natuerlich kann man bei beiden Vertragsarten auch
+schneller einzahlen, also muss man sich keine Sorge machen, dass man
+beim 4-stunden Vertrag ewig warten muss, bis man einen neuen
+Masterblock kriegt, auf dem die Guthaben gutgeschrieben werden.
+c) Welche Vertragsform man waehlt, ist einem selbst ueberlassen.... aber
+ueberlegs Dir gut, ob Du die Zeit zum talken behalten willst, denn die
+wird beim 2h - Vertrag schnell knapp - nicht zu vergessen die Tatsache,
+dass bei Ueberziehen der Einzahlungszeit fuer die einzelnen Raten
+zunaechst eine saftige Strafe faellig wird, und dann sogar der ganze
+Vertrag floeten gehen kann....
+d). Meine Anmerkung: Wer so richtig heiss auf ein Haus ist, metzelt eine
+nacht durch. Ich hab damals um 0.00 Uhr angefangen, und morgens um 8
+war das Haus fertig - nachts sind weniger Spieler dabei, und man hat
+mehr Opfer, die man ausschlachten kann...... Durchschnittszeit fuer eine
+Rate war laut logfile 23 minuten.... sicherlich ein Argument fuer den 2-
+Stunden- Vertrag.
+II. Seit Mitte Januar koennen in den Haeusern auch extra-Raeume eingebaut
+werden. Diese kosten pro Stueck 200.000 Muenzen, und man kann, wie
+auch beim Hausbau, zwischen einem schnellen und einem sanften Vertrag
+waehlen.
+Maximal koennen 9 Raeume an das Haus angebaut werden. Sofern in einem
+Raum mehrere Haeuser stehen, besteht auch die Moeglichkeit, die durch
+den Befehl Ausgang miteinander zu verbinden.
+III. In den Seherhaeusern sind seit Februar ' 95 das Beschreiben von Befehlen
+und spezifische Ausgaben moeglich. So kann man Rassen und - Geschlechtsspe-
+zifische Ausgaben machen. Ebenso ist es moeglich, durch Eingabe von Befehlen
+bestimmte Ausgaben zu erreichen, z.B. 'druecke knopf' - ' ein Faden faellt
+von der Decke' usw. Wie das vor sich geht, waere hier zu umfassend, ausser-
+dem hat Wargon eine hervorragende Hilfeseite zu den Haeusern erstellt, die
+sehr detailliert erklert, wie man vorgehen muss.
+IV. Desweiteren kann man nun die Truhe in den Seherhaeusern verschieben,
+umbenennen und ihr einen Extralook geben. Ausserdem kann die Haustuer mit
+speziellen Funktionen in die Aussenbeschreibung des Hauses eingebaut oder
+unsichtbar gemacht werden. Auch hierzu empfehle ich die Lektuere der Hilfe-
+Seite zum Seherhaus.
+V. Die Moeglichkeiten, die Im Seherhaus zur Verfuegung stehen, sind hier nur
+rudimentaer darstellbar, insbesondere da sie dank Wargon's toller Arbeit in
+diesem Bereich stetig wachsen. Ich plane in naeherer Zukunft einmal ein
+'Musterhaus' aufzubauen, in dem alle Moeglichkeiten, die die Seherhaeuser
+bieten, dargestellt werden, allerdings wird dieses nicht vor Januar 1996
+fertig werden; allerdings kannst Du eine Menge Hilfen auch auf den
+Hilfeseiten zum Seherhaus erhalten, die sehr ausfuehrlich und gut geschrieben
+sind.
diff --git a/d/seher/haeuser/special/special/faq/15 b/d/seher/haeuser/special/special/faq/15
new file mode 100644
index 0000000..449109c
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/15
@@ -0,0 +1,24 @@
+15. SEHERSPIELPLATZ
+
+Mit dem Seherspielplatz ist nunmehr auch eine Region eroeffnet worden,
+die ausschliesslich von den Sehern gestaltet werden soll. Einige haben
+die Insel am Ostrand der Wueste ja schon gesehen und dort schon einiges
+gebastelt.
+Zwei Sachen erscheinen mir im Zusammenhang mit der Insel
+erwaehnenswert: 1. Die Gestaltung der Insel kann wirklich nur gelingen,
+wenn alle Seher zusammenarbeiten. Dies heisst, dass man sich
+insbesondere bei den gewaehlten Ausgaengen abspricht, um ein
+entstandenes lab nicht durch einen einfachen Ausgang kaputtzumachen.
+2. Sobald die Ausgaenge gelegt sind, und die ersten
+Raumbeschreibungen geschrieben werden, heisst es auch wieder
+aufpassen. Es waere schoen, wenn benachbarte Raeume in gewisser
+Form auch zueinander passen - also schaut Euch um, wenn ihr einen
+Raum beschreibt, was dort in der Nachbarschaft fuer Raeume existieren.
+Wie funktioniert der Automat vor der Insel ?
+Naja, dazu wurden tausend Fragen gestellt - dabei ists ganz einfach:
+a) Man drueckt einen Knopf entsprechend dem item, das man kaufen
+moechte (langbeschreibung, ausgang, detail)
+b) Man wirft die entsprechende Menge von Muenzen ein - die Anzeige
+zeigt, wieviel es kostet, und wieviel noch fehlt.
+c) Man nimmt den erworbenen Gegenstand mit und setzt ihn in dem
+Raum ein, den man sich dafuer ausgesucht hat.
diff --git a/d/seher/haeuser/special/special/faq/16 b/d/seher/haeuser/special/special/faq/16
new file mode 100644
index 0000000..416a1ec
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/16
@@ -0,0 +1,43 @@
+
+Damit bei der Vielzahl von Sehern nicht der Ueberblick verloren
+geht, wo ihre ganzen Haeuser stehen, hier auch ein kleines
+Adressenverzeichnis - voellig unvollstaendig, also schreibt mir mal,
+wenn ihr von einem Haus wisst, das ich ausgelassen habe, oder
+wenn Euer Haus einen anderen Namen als den hier aufgefuehrten hat.
+
+Das Haus von Aargon                auf dem Weg vor dem Zigeunerlager
+Arameas Fachwerkhaus               beim Imker
+Die Blockhuette von Asmodis        westlich vom Fackelladen
+Das Haus von Bambulko              Trampelpfad beim Elfendorf
+Das Haus von Barak                 am Strand der Sonneninsel
+Das Haus von Bernhard              Waldweg oestlich Glockenwald
+Bleelumpes Dreimaster              am Strand noerdlich von Port Vain
+Das Haus von Dion                  auf der Kreuzung vor dem Friedhof
+Hoehle von Eros                    Ostrand des Neandertals
+Das Haus von Grimmborn             auf Svolvaer
+Hardils Baumhaus                   auf dem Baum westlich vom Elfendorf
+Das Haus von Hawk                  Obstwiese beim Ostwall
+Das Haus von Helloween             im SW von Moron
+Das Haus von Iolanthe              beim Bernhardiner noerdlich zum Krater
+Khornes Schrein                    im noerdlichen Lupinental
+Der Steinbruch von Kieselstein     am Strand in Port Vain
+Das Haus von Klinge                beim Drachenbaby auf der HE
+Das Haus von Kotzbrocken           suedlich vom zugefrorenen Weiher
+LaMuertes Felsbrocken                Weg in die Wueste
+Laurins Zwergenhoehle              in Valgessa
+MagicOllis Meditationstempel       Waldweg oestlich Glockenwald
+Minnis Schloesschen                im Glockenwald
+Mystix Wolke                       Eingang Schreckensspitze
+Das Haus von Rafael                bei der Tanne im Hexenwald
+Das Haus von Ray                   auf dem Weg vor dem Zigeunerlager
+Das Haus von Rudiratter            schau mal nebenan nach ;)
+Das Haus von Ryne                  da stehst Du drin !
+Die Strandvilla von Rxtech         am Strand noerdlich von Port Vain
+Das Haus von Slyfox                Kreuzung vor dem Friedhof
+Das Hau von Snooker                Oestlich der Plaza in Moron
+Stahldorns Monolith                beim Brunnen im Hexenwald
+Das Haus des Toeters               zwischen Port Vain und Goblinlager
+Tzuiops Huette                     auf dem Baum westlich vom Elfendorf
+Das Haus von Woelkchen             unter dem Regenbogen beim Bergdorf
+Der Eingang zu einer Hoehle        beim Gletscher (immer nach N- und)
+                                   nicht zufaellig in eine andere Dimension)
diff --git a/d/seher/haeuser/special/special/faq/17 b/d/seher/haeuser/special/special/faq/17
new file mode 100644
index 0000000..2c3fafa
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/17
@@ -0,0 +1 @@
+Du siehst keinen Fehler im Raum-Zeit-Gefuege.
diff --git a/d/seher/haeuser/special/special/faq/18 b/d/seher/haeuser/special/special/faq/18
new file mode 100644
index 0000000..ee8b2a2
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/18
@@ -0,0 +1,239 @@
+
+
+
+Alphabetische Auflistung aller Seher im MorgenGrauen, Stand 18.09.1995
+
+(gesamt 232 Seher, zu diesem Zeitpunkt 215 Magier)
+Aargon           1690  33132039 ( 28)  44 Tage,  7 Stunden und  3 Minuten
+Aceg              905    734710 ( 20)   2 Tage,  5 Stunden und 51 Minuten
+Acrimon          1375   1906217 ( 21)  37 Tage, 19 Stunden und 46 Minuten
+Aell             1285    899016 ( 20)  21 Tage,  7 Stunden und 35 Minuten
+Alemanne         1430   3851409 ( 23)  19 Tage,  0 Stunden und 20 Minuten
+Aline            1410   1065274 ( 21)  24 Tage,  2 Stunden und 20 Minuten
+Alkuin           1615   3471392 ( 23)   4 Tage,  4 Stunden und 15 Minuten
+Almundi          1665  12045567 ( 26)  29 Tage,  9 Stunden und 33 Minuten
+Alxx             1275    210695 ( 20)   8 Tage, 10 Stunden und 58 Minuten
+Amazora           720   7620965 ( 25)  60 Tage, 14 Stunden und 59 Minuten
+Anaid            1375    283907 ( 22)  23 Tage,  2 Stunden und 50 Minuten
+Anakin           2000  91720168 ( 31)  86 Tage, 10 Stunden und 36 Minuten
+Ancalagon        1500   3203323 ( 23)  22 Tage,  5 Stunden und 31 Minuten
+Anno             1250    379677 ( 20)  20 Tage, 21 Stunden und  9 Minuten
+Aquatoll         1230   1016831 ( 20)  12 Tage,  7 Stunden und 44 Minuten
+Aragon           1235   1618754 ( 21)  13 Tage, 19 Stunden und 37 Minuten
+Aramea           1520   5634558 ( 24)  26 Tage,  0 Stunden und 12 Minuten
+Ark              1620   1612954 ( 21)  15 Tage,  3 Stunden und  8 Minuten
+Arty             1940  28051401 ( 28)  30 Tage,  6 Stunden und 37 Minuten
+Arun             1200   1002993 ( 20)   9 Tage, 18 Stunden und 25 Minuten
+Asmodina         1985   3609044 ( 23)  43 Tage, 10 Stunden und 49 Minuten
+Asmodis          1985  26038442 ( 28) 164 Tage,  1 Stunde  und 35 Minuten
+Augra            1790    987087 ( 20)  32 Tage, 17 Stunden und 22 Minuten
+Aurian           1495   1565849 ( 21)   2 Tage, 13 Stunden und 40 Minuten
+Barak            1105   4313368 ( 23)  20 Tage, 22 Stunden und  8 Minuten
+Bellicus         1370   7309868 ( 24)  47 Tage,  2 Stunden und  1 Minute
+Bernhard         2060  30025276 ( 28)  59 Tage, 20 Stunden und 58 Minuten
+Bibo             1875  33001194 ( 28)  65 Tage,  2 Stunden und  8 Minuten
+Bienchen         1305   1979490 ( 21)  10 Tage,  9 Stunden und  9 Minuten
+Big              1690   2937075 ( 22)  11 Tage, 11 Stunden und 50 Minuten
+Bigfoot          1660  12667085 ( 26)  30 Tage, 20 Stunden und 53 Minuten
+Bleelumpe        1265   5256959 ( 24)  20 Tage, 21 Stunden und 14 Minuten
+Bloodgore        1570   6061450 ( 28)  56 Tage, 16 Stunden und 59 Minuten
+Bluemel          1450   1547717 ( 21)  20 Tage, 16 Stunden und 13 Minuten
+Boebbel          1675   1224533 ( 21)  28 Tage,  1 Stunde  und 35 Minuten
+Bottom           1355   1062102 ( 20)  18 Tage, 19 Stunden und 57 Minuten
+Brit             1260   1973712 ( 22)  20 Tage,  6 Stunden und  1 Minute
+Brummel          1850  11779550 ( 26)  25 Tage,  1 Stunde  und 15 Minuten
+Cael             1335   1536548 ( 21)  17 Tage,  7 Stunden und 27 Minuten
+Caradhras        1920 101383337 ( 32) 113 Tage, 23 Stunden und 57 Minuten
+Carefree         1280   1356967 ( 20)   8 Tage, 21 Stunden und  1 Minute
+Castaway         1220   2196508 ( 22)  43 Tage, 22 Stunden und  7 Minuten
+Celebdil         1250  11573503 ( 25)  40 Tage,  0 Stunden und 28 Minuten
+Charly           1430   3793972 ( 22)  16 Tage,  8 Stunden und 51 Minuten
+Chavez           1600    299023 ( 21)  33 Tage, 12 Stunden und  0 Minuten
+Chicola          1450  23157008 ( 27)  67 Tage, 12 Stunden und 17 Minuten
+Coven            1310   1940343 ( 22)  10 Tage, 20 Stunden und 53 Minuten
+Craig            1380   5415105 ( 24)  22 Tage, 20 Stunden und  8 Minuten
+Crolla           2060  15062412 ( 26)  32 Tage, 12 Stunden und 36 Minuten
+Cub              1340   1453817 ( 20)   4 Tage,  4 Stunden und 11 Minuten
+Cuchullin        2060   9366380 ( 25)  41 Tage,  8 Stunden und 26 Minuten
+Daniel           1420   2125614 ( 22)  18 Tage, 23 Stunden und  6 Minuten
+Data             1280   1383631 ( 20)  16 Tage, 11 Stunden und  6 Minuten
+Deimos           1550   1614972 ( 21)   4 Tage, 18 Stunden und 19 Minuten
+Desasta          1560   9822728 ( 25)  24 Tage, 15 Stunden und 27 Minuten
+Desideria        1275   1340228 ( 20)   1 Tag , 12 Stunden und 33 Minuten
+Dhojakt          1625   5612138 ( 24)  42 Tage, 16 Stunden und 57 Minuten
+Digedag          1200    817911 ( 20)  17 Tage, 21 Stunden und  8 Minuten
+Dion             1020   1396400 ( 21)  11 Tage,  8 Stunden und 29 Minuten
+Drakor           1410    973727 ( 20)  13 Tage, 16 Stunden und 25 Minuten
+Durin            1840  27000290 ( 28)  55 Tage,  4 Stunden und 45 Minuten
+Dwight           2030   7337624 ( 24)  42 Tage, 18 Stunden und  4 Minuten
+Earendil         1640   3243728 ( 22)   7 Tage, 14 Stunden und 11 Minuten
+Emil             1420   1189480 ( 20)  15 Tage,  8 Stunden und 26 Minuten
+Engywuck         1755   2485639 ( 23)  83 Tage, 14 Stunden und 51 Minuten
+Eowin            1045   1123115 ( 20)   3 Tage, 21 Stunden und 14 Minuten
+Etain            1285   1184123 ( 21)  23 Tage, 13 Stunden und  3 Minuten
+Eule             1290    534666 ( 20)   9 Tage, 10 Stunden und  3 Minuten
+Fagus            1915   1130690 ( 20)  14 Tage,  6 Stunden und 20 Minuten
+Fahnder          1595   7304188 ( 25)  32 Tage,  7 Stunden und 32 Minuten
+Fantaboy         1415    691662 ( 20)  19 Tage,  1 Stunde  und 13 Minuten
+Fauli            1285   2042386 ( 22)  35 Tage,  1 Stunde  und 30 Minuten
+Fels             1575   4288401 ( 23)  30 Tage, 12 Stunden und 50 Minuten
+Fipps            1325   6187204 ( 24)  27 Tage,  4 Stunden und 46 Minuten
+Flintnail        1415   1392090 ( 21)  29 Tage, 18 Stunden und  1 Minute
+Fones            1385   1695359 ( 21)  11 Tage, 23 Stunden und 43 Minuten
+Fryder           1810  20079125 ( 27)  66 Tage, 19 Stunden und 40 Minuten
+Fyt              1625 103796399 ( 32) 105 Tage,  9 Stunden und 50 Minuten
+Gabi             1985   4912832 ( 24)  14 Tage, 16 Stunden und 55 Minuten
+Gandulf          1435   2363789 ( 22)  15 Tage, 12 Stunden und 39 Minuten
+Granata          1900   8163375 ( 25)  22 Tage, 23 Stunden und 20 Minuten
+Gumpy            1650  12022713 ( 26)  23 Tage, 10 Stunden und 34 Minuten
+Hackfried        2060   7218856 ( 24)  27 Tage,  4 Stunden und 22 Minuten
+Hadra            2010   8863573 ( 25)  41 Tage, 15 Stunden und 31 Minuten
+Har              1200   1434190 ( 21)  39 Tage, 18 Stunden und  7 Minuten
+Hardil           1170   7775164 ( 25)  31 Tage, 23 Stunden und 54 Minuten
+Hasenfuss        1325   7259495 ( 24)  30 Tage,  3 Stunden und 37 Minuten
+Hawk             1230   3618526 ( 23)  31 Tage,  1 Stunde  und 17 Minuten
+Helloween        1805  17742802 ( 27)  29 Tage, 19 Stunden und 32 Minuten
+Hercules         1440   2157741 ( 21)  12 Tage, 16 Stunden und  2 Minuten
+Holden           1380    218539 ( 40)  10 Tage,  3 Stunden und  7 Minuten
+Hoppla           1220   1174934 ( 20)   2 Tage,  7 Stunden und 58 Minuten
+Huor             1245   1770486 ( 21)  13 Tage,  8 Stunden und 43 Minuten
+Hurin            1460   2284648 ( 22)  22 Tage,  5 Stunden und 33 Minuten
+Idur             1965   3376840 ( 23)   5 Tage,  6 Stunden und 50 Minuten
+Ineluki          1555   1969710 ( 21)   1 Tag , 17 Stunden und 26 Minuten
+Inskipp          1585   2982790 ( 22)  12 Tage, 23 Stunden und 17 Minuten
+Iolanthe         1555   5992258 ( 24)  35 Tage,  3 Stunden und 59 Minuten
+Isabeau          2060   3380499 ( 23)   9 Tage,  0 Stunden und 14 Minuten
+Isabelle         1390    547478 ( 20)   3 Tage, 11 Stunden und  5 Minuten
+Isnogood         1775   2881018 ( 22)  27 Tage, 18 Stunden und 27 Minuten
+Issomad          1985  11020515 ( 26)  20 Tage,  5 Stunden und 10 Minuten
+Jaf              1180    429696 ( 20)  71 Tage,  5 Stunden und  4 Minuten
+Janice           2060  13511650 ( 26)  53 Tage, 19 Stunden und 37 Minuten
+Jerrico          1485   4567925 ( 24)  31 Tage,  9 Stunden und 47 Minuten
+Jimdigriz        2015   4229005 ( 23) 100 Tage,  2 Stunden und 26 Minuten
+Jiriki           1570   3858241 ( 23)   3 Tage, 15 Stunden und 41 Minuten
+Jurry            1410   7073544 ( 25)  33 Tage,  8 Stunden und 42 Minuten
+Kafka            1215    769262 ( 21)   9 Tage,  8 Stunden und 55 Minuten
+Kalliope         1385   1539329 ( 20)   1 Tag , 20 Stunden und 24 Minuten
+Kartii           1555   1746919 ( 20)  16 Tage, 13 Stunden und  1 Minute
+Kelder           1945   5109825 ( 23)  46 Tage, 17 Stunden und 44 Minuten
+Kermit           1745   6203256 ( 25)  48 Tage,  3 Stunden und 39 Minuten
+Khidar           1920  50140999 ( 29)  62 Tage, 20 Stunden und 36 Minuten
+Khorne           1640    130376 ( 24)  62 Tage,  8 Stunden und  3 Minuten
+Kieselstein      1220   7925011 ( 26)  16 Tage, 13 Stunden und  3 Minuten
+Kiwi             1495    723804 ( 20)  32 Tage,  6 Stunden und 59 Minuten
+Klausi           1750   6186382 ( 24)  16 Tage, 20 Stunden und  8 Minuten
+Konto            1195   1399690 ( 21)   5 Tage,  0 Stunden und  6 Minuten
+Kotzbrocken      1925  40103319 ( 29)  41 Tage,  8 Stunden und 16 Minuten
+Laurin           1945   4237757 ( 24)  66 Tage, 19 Stunden und 19 Minuten
+Lefty            1660  11580339 ( 26)  27 Tage, 16 Stunden und 20 Minuten
+Leia             1745   7832318 ( 25)  23 Tage, 12 Stunden und 19 Minuten
+Liutberga        1725   2369571 ( 22)   3 Tage,  5 Stunden und 29 Minuten
+Loculus          1485  13363261 ( 26)  34 Tage, 15 Stunden und  6 Minuten
+Lokar            1280   1432495 ( 20)   3 Tage, 13 Stunden und 15 Minuten
+Lug              1685   1723385 ( 21)  13 Tage,  4 Stunden und 12 Minuten
+Magicolli        2060  34924394 ( 28)  42 Tage,  7 Stunden und 10 Minuten
+Magnum           1280   1438794 ( 20)   7 Tage, 18 Stunden und  8 Minuten
+Mahatma          1575   2975612 ( 22)  16 Tage, 12 Stunden und 19 Minuten
+Majere           1215    890832 ( 20)   5 Tage, 19 Stunden und 17 Minuten
+Mars             2060  14613389 ( 26)  46 Tage,  4 Stunden und 59 Minuten
+Maru             1400   1503098 ( 21)   7 Tage, 15 Stunden und 25 Minuten
+Maxin            1365   1120899 ( 20)  17 Tage,  3 Stunden und 54 Minuten
+Mellorn          1290   1066484 ( 20)  18 Tage,  8 Stunden und  9 Minuten
+Meriad           1305   1497000 ( 20)   7 Tage, 17 Stunden und 51 Minuten
+Mesirii          1585   6124525 ( 24)  25 Tage,  3 Stunden und 47 Minuten
+Mieze            1275   2122969 ( 22)  29 Tage,  0 Stunden und 30 Minuten
+Mimo             1195   1127414 ( 20)  15 Tage, 11 Stunden und 25 Minuten
+Minni            1985  13946604 ( 26)  27 Tage,  4 Stunden und 35 Minuten
+Misselli          915   2638912 ( 22)  10 Tage, 23 Stunden und  8 Minuten
+Mith             1285   1202199 ( 21)   6 Tage, 12 Stunden und  4 Minuten
+Mogli            1075   1836066 ( 21)  10 Tage, 13 Stunden und 52 Minuten
+Morgon           1835  10402622 ( 25)  27 Tage,  6 Stunden und 19 Minuten
+Mosh             1420   2479954 ( 22)  16 Tage,  9 Stunden und 21 Minuten
+Mucks            1310   3538464 ( 23)  13 Tage, 22 Stunden und 53 Minuten
+Mumpels          1275   1190902 ( 20)   9 Tage, 10 Stunden und 16 Minuten
+Nature           1490   1632154 ( 21)   6 Tage, 10 Stunden und 49 Minuten
+Nial             1330    573524 ( 20)   8 Tage, 22 Stunden und 38 Minuten
+Nixi             1695   3395231 ( 24)  58 Tage, 10 Stunden und 25 Minuten
+Nixihou          1965   9455954 ( 25)  56 Tage,  8 Stunden und 55 Minuten
+Nok              1595   1322048 ( 20)   1 Tag ,  1 Stunde  und 50 Minuten
+Norweger         1555   6057459 ( 25)  32 Tage, 11 Stunden und 32 Minuten
+Nostradamus      1800  93067790 ( 31)  55 Tage,  5 Stunden und 11 Minuten
+Nullneuner       1360   5220894 ( 24)  13 Tage,  7 Stunden und 38 Minuten
+Orktoeter        1330  20274027 ( 27)  42 Tage, 11 Stunden und 31 Minuten
+Pantoffel        1625   9247568 ( 25)  27 Tage,  5 Stunden und 11 Minuten
+Penelope         1450   1696656 ( 21)  42 Tage, 12 Stunden und 59 Minuten
+Perle            1495   1549393 ( 21)  20 Tage, 20 Stunden und 12 Minuten
+Phobos           1685   1963639 ( 21)   4 Tage, 15 Stunden und 23 Minuten
+Phygos           1420   3423806 ( 23)  31 Tage,  9 Stunden und 27 Minuten
+Piff             1300   3118489 ( 22)   5 Tage, 10 Stunden und 30 Minuten
+Pippin           1415   5490342 ( 24)  19 Tage,  7 Stunden und 37 Minuten
+Plock            1500   3008084 ( 22)  21 Tage, 10 Stunden und 15 Minuten
+Pra              1190   3849602 ( 23)  33 Tage,  1 Stunde  und 49 Minuten
+Priamos          1270   3415166 ( 23)  13 Tage,  1 Stunde  und 56 Minuten
+Puck             1270   1353491 ( 20)   1 Tag ,  0 Stunden und 44 Minuten
+Quengistu        1160   2480429 ( 22)  28 Tage, 19 Stunden und 35 Minuten
+Quillian         1330  13159680 ( 26)  56 Tage,  8 Stunden und 33 Minuten
+Rafael           2015   3556498 ( 23)  49 Tage, 10 Stunden und 13 Minuten
+Raiba            1325   1032368 ( 20)  18 Tage,  3 Stunden und 30 Minuten
+Ratz             1385   1594601 ( 21)  44 Tage, 19 Stunden und 52 Minuten
+Ray              1690  16533169 ( 27)  36 Tage, 18 Stunden und 26 Minuten
+Reinelde         1620  11760476 ( 26)  27 Tage,  2 Stunden und  4 Minuten
+Rexon            1395   3286410 ( 23)  14 Tage, 15 Stunden und  2 Minuten
+Roadrunner       1660   9025017 ( 25)  55 Tage, 17 Stunden und 21 Minuten
+Rosa             1855   4397281 ( 24)   6 Tage, 10 Stunden und 38 Minuten
+Rudiratter       2060  41244745 ( 29)  37 Tage, 19 Stunden und 14 Minuten
+Ryne             2060 193376368 ( 35)  91 Tage, 16 Stunden und 57 Minuten
+Sac              1325   1081674 ( 20)   3 Tage, 16 Stunden und 26 Minuten
+Schelm           1460    461247 ( 22)   8 Tage,  8 Stunden und 20 Minuten
+Schlawiner       1365    534648 ( 21)  29 Tage, 12 Stunden und 48 Minuten
+Scotty           1215   2245107 ( 22)  12 Tage,  3 Stunden und 29 Minuten
+Selbstmord       1260   1281392 ( 20)   2 Tage, 10 Stunden und 15 Minuten
+Semira           1710   8346293 ( 25)  38 Tage, 10 Stunden und 43 Minuten
+Siabat           1340   1342168 ( 21)  15 Tage, 20 Stunden und 21 Minuten
+Siborit          1400   1120931 ( 20)  15 Tage,  3 Stunden und 25 Minuten
+Skipper          1575  33526629 ( 28)  46 Tage,  4 Stunden und 32 Minuten
+Skywalker        1455   2760483 ( 22)  12 Tage,  5 Stunden und 19 Minuten
+Slash            1720   6218999 ( 24)  37 Tage, 12 Stunden und 42 Minuten
+Slyfox           1780  29213700 ( 28)  63 Tage, 22 Stunden und 22 Minuten
+Smaug            1185   2650121 ( 22)  18 Tage, 23 Stunden und 56 Minuten
+Smiley           1375  10534850 ( 25)  22 Tage, 10 Stunden und 45 Minuten
+Snooker          1520  30568048 ( 28)  21 Tage,  1 Stunde  und 59 Minuten
+Snuggle          1525   9048493 ( 25)  31 Tage,  8 Stunden und 50 Minuten
+Sorsha           1440   1386048 ( 20)  20 Tage,  0 Stunden und 56 Minuten
+Speedy           1555  16193978 ( 27)  20 Tage, 18 Stunden und 41 Minuten
+Stahldorn        1735  54765411 ( 29)  60 Tage, 22 Stunden und 41 Minuten
+Steve            1945   6068441 ( 24)  66 Tage,  5 Stunden und 34 Minuten
+Sushi            1775   1608281 ( 21)  17 Tage, 16 Stunden und 17 Minuten
+Symbiont         1320   3504834 ( 23)  13 Tage,  8 Stunden und 54 Minuten
+Sysop            1335   1719336 ( 21)  16 Tage, 11 Stunden und 40 Minuten
+Taz              1515    765173 ( 22)  33 Tage,  3 Stunden und 51 Minuten
+Telka            1550   1357476 ( 20)  14 Tage, 19 Stunden und 14 Minuten
+Terran           1440   1094264 ( 20)  12 Tage,  1 Stunde  und 17 Minuten
+Todesengel       1535   1738662 ( 21)   3 Tage,  7 Stunden und  7 Minuten
+Tommy            1605   7146906 ( 25)  35 Tage, 12 Stunden und 57 Minuten
+Torwall          1330   6124052 ( 24)  28 Tage, 11 Stunden und 46 Minuten
+Toss             1875   7830548 ( 26)  54 Tage,  4 Stunden und 22 Minuten
+Twiggy           1660   9691344 ( 25)  35 Tage, 17 Stunden und 22 Minuten
+Tyrion           1595  19220397 ( 27)  35 Tage, 16 Stunden und 51 Minuten
+Tzuiop           1165  14746734 ( 26)  80 Tage, 18 Stunden und 52 Minuten
+Urza             1485  12589713 ( 26)  29 Tage, 12 Stunden und 56 Minuten
+Vanir            2060   2933143 ( 22)  35 Tage,  4 Stunden und 11 Minuten
+Vanja            1615  16423350 ( 26)  21 Tage,  7 Stunden und  4 Minuten
+Vergon           1305   1071535 ( 20)   1 Tag , 15 Stunden und  4 Minuten
+Verwirrung       1925   4455245 ( 23)  14 Tage, 20 Stunden und 52 Minuten
+Vrumfondel       1255    447438 ( 20)  18 Tage, 16 Stunden und 48 Minuten
+Wahn             2060   3312832 ( 23)  16 Tage,  1 Stunde  und 47 Minuten
+Warlord          1275    630751 ( 21)  30 Tage,  3 Stunden und 54 Minuten
+Willow           1905  28742636 ( 28)  54 Tage, 10 Stunden und 25 Minuten
+Woelkchen        1370   7929828 ( 24)  40 Tage,  3 Stunden und 11 Minuten
+Wolfendart       1415   1575308 ( 21)  11 Tage,  8 Stunden und 46 Minuten
+Xanadu           1525   1784732 ( 21)  37 Tage,  3 Stunden und 28 Minuten
+Xera             1500   1765739 ( 21)  18 Tage,  6 Stunden und 24 Minuten
+Yak              1200   1012278 ( 20)  16 Tage, 17 Stunden und 39 Minuten
+Yasmin           1335   1229340 ( 21)   9 Tage,  2 Stunden und 38 Minuten
+Yeti             1420   6010840 ( 25)  48 Tage, 14 Stunden und 25 Minuten
+Yety             1260   2387612 ( 22)  17 Tage, 23 Stunden und 55 Minuten
+Zephyr           2060  29257249 ( 28)  67 Tage,  4 Stunden und  5 Minuten
+Zorro            2060   5644687 ( 24)  33 Tage, 10 Stunden und 52 Minuten
+Zwirch           1665   9818247 ( 25)  15 Tage, 23 Stunden und 31 Minuten
+
diff --git a/d/seher/haeuser/special/special/faq/2 b/d/seher/haeuser/special/special/faq/2
new file mode 100644
index 0000000..e4b78bd
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/2
@@ -0,0 +1,18 @@
+2. SEHERQUEST
+
+Eine recht schoene quest nur fuer Seher hat Troy im Fruehjahr 1995
+geschrieben, allerdings wurde sie erst 4 Monate spaeter angeschlossen,
+nachdem Boing eine Menge Bugs gefixt hatte. Wie auch immer: Eine lohnende
+quest, mit Sicherheit die schoenste die Troy hier geschrieben hat, und
+ausnahmsweise ist sie sigar logisch und die Stimmung vor Ort nicht schlecht.
+Also unbedingt spielen, nicht zuletzt, weil die Belohnung dafuer wirlich
+gelungen ist, besser: die Belohnungen! Denn a) erhaelt man eine
+Kristallkugel, mit der man Spieler fuer einen Magiepunkt finden kann, und b)
+erhaelt man das R-Emote, mit dem man sein Emote, das man fuer die Amaryllis-
+Quest erhalten hat, auch auf Spieler ausserhalb des Raumes in dem man sich
+befindet, anwenden kann (vgl. hierzu auch Kap. 6: Magische Kugel).
+
+Viel Spass beim Questen !!!
+(aber erst hierbleiben und Faq fertiglesen ;) - und der Ballpark lohnt
+uebrigens auch mal einen kleinen Blick ;))
+
diff --git a/d/seher/haeuser/special/special/faq/3 b/d/seher/haeuser/special/special/faq/3
new file mode 100644
index 0000000..5aea5d2
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/3
@@ -0,0 +1,23 @@
+
+3. FLUCHTRICHTUNG
+
+Etwas unscheinbar, aber in meinen Augen ist dies immer noch der wertvollste
+Befehl, den man als Seher neu erhaelt: Fluchtrichtung.
+
+Ab sofort kannst Du den Befehl 'Fluchtrichtung (Richtung)' anwenden.
+Sollte dieser noch nicht aktiviert sein, musst du einmal Ende machen.
+Danach kannst Du selbst bestimmen, in welche Richtung Du fliehst,
+wenn die Angst Dich mal ueberkommen sollte .....
+Aber Achtung: Der Befehl funktioniert mit einer Warscheinlichkeit von
+80 Prozent; fuer jeden Level ueber 20 erhoeht sich die Warscheinlichkeit
+dann um weitere zwei Prozent...  Tip: Fluchtrichtung >Ende< funktioniert
+auch, ebenso wie: Fluchtrichtung >teile Boing mit Du, ich sterbe gleich :(<
+Man muss halt die Prioritaeten richtig setzen ;)).
+
+Warum dieser Befehl in meinen Augen besonders wichtig ist ? Nun, er befreit
+Spieler mit sehr hoher Warscheinlichkeit von dem Risiko, durch einen Lag zu
+sterben, wenn die Flucht im entscheidenden Moment in die falsche Richtung zu
+gehen droht. Man muss sie vorher nur richtig gesetzt haben. Allerdings, ein
+kleines Risiko bleibt... denn erst ab Level 30 klappt die Fluchtrichtung zu
+100%, also wachbleiben ;).
+
diff --git a/d/seher/haeuser/special/special/faq/4 b/d/seher/haeuser/special/special/faq/4
new file mode 100644
index 0000000..6b24118
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/4
@@ -0,0 +1,34 @@
+4. SEHERLEVEL
+A propos Levels- zwar ist man nun Seher, aber dennoch kann man
+auch weiterhin seine Stufen erhoehen - allerdings mit etwas mehr
+Aufwand als frueher :(( - die Levels  und ihre EP-Anforderungen:
+
+
+Stufe   Erfahrung          Stufe   Erfahrung          Stufe   Erfahrung
+========================================================================
+21       1500000           26      11000000           31      80000000
+22       2250000           27      17000000           32     100000000
+23       3375000           28      25000000           33     125000000
+24       5062500           29      40000000           34     150000000
+25       7600000           30      60000000           35     175000000
+                                                      36     200000000
+
+Die Bedeutung der Levels ist insofern hoch, als dass die Erfolgs-
+chance der Zaubersprueche in den neuen Gilden von dem Level des
+Spielers abhaengig ist. So ist es grundsaetzlich nur moeglich, die maximale
+Erfolgswarscheinlichkeit eines Zauberspruches erst dann zu erreichen, wenn
+man Seherlevel 30 erreicht hat. Berechnet wird das ganze wie folgt:
+Bei Level 20 betraegt die Erfolgswarscheinlichkeit 80 Prozent. Pro Level
+ueber 20 steigt die Warscheinlichkeit dann um weitere 2 Prozent, erreicht
+also bei Level 30 100 Prozent.
+Dies fuehrt im Ergebnis dazu, dass ein Zauberspruch, den man an sich zu 90%
+beherrscht, in Level 25 nur mit einer Warscheinlichkeit von 81 Prozent
+gelingt (90% Beherrschen des Spruches * 90% Warscheinlichkeit aus Level 25).
+Allerdings gilt diese Formel so global nur fuer Standard-Zaubersprueche (ins-
+besondere die in der Abenteurergilde); die Magier der einzelnen Gilden
+koennen durch den Schwierigkeitsgrad eines Zauberspruches bestimmen, ob
+dieser mit hoeherer oder niedrigerer Warscheinlichkeit gelingt, als durch die
+oben angefuehrte Rechnung vorgesehen. In der Karategilde ist es sogar so
+geregelt, dass der Level an sich fuer die Erfolgswarscheinlichkeit eines
+Spruches unerheblich ist.
+
diff --git a/d/seher/haeuser/special/special/faq/5 b/d/seher/haeuser/special/special/faq/5
new file mode 100644
index 0000000..27f31a6
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/5
@@ -0,0 +1,30 @@
+5. Kommunikation
+
+Als Seher stehen Dir neue Mittel der Kommunikation zur Verfuegung, mittels
+derer Du Nachrichten an andere Spieler senden kannst:
+
+I. MAIL
+Ab sofort steht Dir der kostenlose Kurierdienst der MG-Post zur
+Verfuegung - durch Eingabe von 'mail' kannst du von ueberall Post
+verschicken und empfangen. Dies setzt aber voraus, dass Du eine Zeitung
+bei Dir traegst, da das mail eine Zusatzfunktion der MPA ist.
+
+II. MPA
+In der Zeitung steht Dir nun die neue Rubrik 'seher.entwicklung' zur
+Verfuegung, in der Du Deine Vorschlaege zur Verbesserung des
+Sehertums einbringen kannst.
+
+III. Kanaele
+Zur direkten Kommunikation mit den anderen Sehern steht Dir nun auch ein
+neuer Kanal zur Verfuegung. Er wird mit ' -h ' angesprochen
+(nein, nicht mit S wie Seher, dann knallt die Meldung zwar auch ueber
+den Seherkanal, aber ebenfalls ueber Sport und Snoop ;)).
+
+IV. Fernschreiber
+Mit dem Fernschreiber aus dem Seherladen kannst Du ganze Nachrichtenbloecke
+an andere Spieler senden. Dies ist haeufig nuetzlich, wenn man laengere Texte
+an einen anderen Spieler senden will, waehrend man z.B. mit diesem zusammen
+eine neue Region oder ein Quest erforscht. Der Einsatz kostet 15 MP pro
+Spieler, an den die Nachricht gesendet wird, man kann auch an mehrere Spieler
+gleichzeitig die Meldung ausgeben lassen.
+
diff --git a/d/seher/haeuser/special/special/faq/6 b/d/seher/haeuser/special/special/faq/6
new file mode 100644
index 0000000..0eb9dfc
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/6
@@ -0,0 +1,15 @@
+6. MAGISCHE KUGEL
+
+Im Haus der Hexe am Friedhof und in Zwackelmanns Schloss findest
+Du eine Kugel, die Du mitnehmen kannst - mit ihr kannst du durch den Befehl
+'finde (Spielername)' jeden eingeloggten Spieler lokalisieren - ein Einsatz
+kostet jeweils 9 MP.
+
+Mit der Einfuehrung des Seherquests von Troy erhaelt man als Questbelohnung
+eine - der Name des Quests laesst es Vermuten - Kristallkugel, die dieselbe
+Funktion wie die Seherkugel erfuellt. Allerdings kostet diese pro Einsatz nur
+1 MP. Dafuer erhaelt allerdings der beobachtete Spieler die Meldung 'Jemand
+beschwoert Dein Bild herauf.', was auf dauer etwas aufdringlich wird, da der
+Betroffene ja nicht weiss, wer ihn da sucht. Ich persoenlich halte den
+Einsatz der 'normalen' Seherkugel fuer diskreter, insbesondere, da sich ihre
+Kosten ja in einem vertretbaren Rahmen bewegen.
diff --git a/d/seher/haeuser/special/special/faq/7 b/d/seher/haeuser/special/special/faq/7
new file mode 100644
index 0000000..33ed2ca
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/7
@@ -0,0 +1,169 @@
+7. SEHERLADEN
+Auf Svolvaer findest Du einen Laden, der spezielle items nur fuer
+Seher anbietet. Interessant ist hier der Bumerang, dazu noch die
+Partytroete - der Rest ist mehr oder weniger brauchbares Zeugs - abraten
+wuerde ich von dem Kauf der ZT-Wuenschelrute, die zwar relativ
+verlaesslich arbeitet, allerdings pro Einsatz 45 MP kostet, und sich damit
+nur lohnt, wenn man in einem konkreten Raum einen ZT vermutet.
+Ansonsten ist natuerlich der Kauf des von BOING handgenaehten
+Kissens ein MUSS!
+
+Eine kurze Beschreibung saemtlicher Seherladenartikel findet sich hier im
+Anschluss; an dieser Stelle einen herzlichen Dank an Cuchullin, der diese
+Liste erstellt und sich damit sehr viel Muehe gegeben hat.
+
+Fuer das Angebot im Seherladen ist Loco zustendig, der das bisher etwas
+spaerliche Angebot gerne erweitern moechte, und daher fuer Ideen
+immer aufgeschlossen ist - daher schreibe ihm ruhig ein mail, wenn Du
+eine Idee hast, was dort zukuenftig angeboten werden koennte.
+
+Angebotene Artikel im Seherladen (Text von Cuchullin:)
+
+1:    15 Eine Fackel.
+                Eine ganz normale Fackel. Man kann sie anzuenden und
+                wieder loeschen.
+
+ 2:    10 Ein von Boing handgenaehtes Kissen
+                Man kann es anderen sehr gut nachwerfen ;)
+
+ 3:  1800 Ein Wecker (Luxusausgabe).
+                Zeigt die Uhrzeit an, dient als Stoppuhr und als Timer.
+                Autoload.
+
+ 4:    15 Ein Lolli.
+                Ein Lolli, so wie ihn das Partymonster gerne verteilt.
+
+ 5:   240 Ein T-Shirt.
+                Ruestungsgattung Umhang und denkbar schlecht... aber als
+                Seher kann man es mit einem Text versehen und man sieht's
+                im Extralook... Nette Sache in jedem Fall.
+
+ 6:     8 Ein Wuerfel.
+                "Du wuerfelst eine 6." Ein ganz normaler Wuerfel.
+
+ 7:    18 Ein Foermchen.
+                Ein rotes Foermchen in der Form eines Herzens.
+                Mehr allerdings nicht.
+
+ 8:  5400 Ein Bumerang.
+                Eigentlich muss man dazu nicht viel sagen, eine wirklich
+                - fast zu - gute Waffe. Wurde allerdings kuerzlich etwas
+                abgewertet, ist jedoch immer noch wertvolle Hifle im Kampf.
+                Man kann (von den insgesamt vieren pro Reset nur einen Bume-
+                rang kaufen. Dies wurde gegen die Hamsterkaeufe nach einem
+                Shutdown eingefuehrt (Es gibt Seher, die kaempfen mit vier
+                Bumerangs und haben noch drei als Ersatz im Beutel...). Nach
+                einem reboot/crash allerdings erhaelt jeder Seher mindestens
+                einen Bumerang.
+
+ 9:    60 Ein tragbarer Postkartendrucker.
+                Man kann damit sechs Postkarten erzeugen, mit Text versehen
+                und verschicken. Netter Gag.
+
+10:  1800 Die Troete des Partymonsters.
+                Ein Muss fuer Partyfans! Das Utensil zu Partymonsterrufen.
+
+11:  9999 Ein Zauberstab.
+                A la Hilbert. Ist durch den Licht-Zauberspruch relativ
+                unwichtig geworden. Ausserdem ist der gekaufte Stab nicht
+                crashfest.
+
+12:   499 Ein Planeditor.
+                Wer sein Projekt oder seinen Plan editieren will, braucht
+                einen Planeditor.
+
+13:  1200 Ein Paar Siebenmeilenstiefel.
+                Eine Erleichterung fuer diejenigen, die nicht ueber irgend-
+                welche Frontend-Programme verfuegen. Man laeuft einen Weg
+                immer bis zur naechsten Abzweigung.
+
+14:   500 Magischer Beutel.
+                Ein nettes 'Tool', um andere zu aergern. Wer hat nicht
+                schonmal vor einer Schattenruestung gestanden, und sich
+                gefragt, warum er sie nicht nehmen oder anziehen kann.
+                Autoload.
+
+15: 11111 Eine Wuenschelrute.
+                Zum Auffinden von Zaubertraenken. Eignet sich nur bei
+                konkretem Verdacht, da sie nur anzeigt, ob sich ein ZT
+                im Raum befindet.
+
+16:  3000 Ein Testroehrchen (gebrauchsfertig).
+                <hick>! Wie besoffen ist man eigentlich? Wiederverwendbar.
+
+17:  3000 Ein Schiebepuzzle.
+                Wer aergert sich nicht, wenn das Freundschaftsband aus
+                irgendwelchen Gruenden ploetzlich ganz oben im Inventory
+                steht? Hiermit kann das Inv nach Belieben umsortiert werden.
+                Autoload.
+
+18:   500 Ein Dudelsack.
+                Das komfortabelste Musikinstrument MorgenGrauens.
+                Man kann sogar die vorgefertigten Stuecke genau
+                ansprechen...
+
+19:   499 Ein handgearbeiteter Mini-Flammenwerfer (tm).
+                'flame' Ein netter Gag.
+
+20:   199 Eine bunte Spritzpistole.
+                'splash' Siehe Flammenwerfer.
+
+21:   900 Ein Paar feste Schuhe.
+                Das 'rtrete'! Leider werden (noch) keine Adjektive
+                akzeptiert.
+
+22:  4500 Eine Stimmgabel.
+                Ideal fuer die, die Hippe nicht moegen.
+                Ist etwas in der Richtung 'Anti-Hippe-Button'.
+
+23:   298 Ein Strandball.
+                Kann man anderen Leuten zuwerfen. Ist nicht so lustig wie
+                Boings handgenaehtes Kissen, dafuer aber auch nicht so
+                provokant.
+
+24:   798 Eine magische Bierflasche.
+                Fuer Biertrinker ein Muss! Diejenigen, die 'ihr Bier' nicht
+                auf der Liste finden, koennen ja dem entsprechenden Magier
+                ne Mail schreiben. Achja - Syntax: wuensche <sorte>
+
+25:   150 Ein Messer Marke Samthandschuh.
+                Wer mit dieser Waffe gegen einen Schmetterling kaempft, darf
+                getrost ideln, denn sie haelt, was sie verspricht.
+
+26:  3000 Ein Lag-O-Meter.
+                Ist interessant, aber leider nicht autoload. Wer wollte
+                nicht schon immer wissen, wieviel Lag man denn nun wirklich
+                hat...
+
+27:   895 Eine Diddl-Knuddel-Maus.
+                Fuer die, die sich in Moron nicht auskennen oder keine Lust
+                haben, sich an entsprechender Stelle anzustellen.
+
+28:   150 Ein 4Gewinnt-Spiel.
+                SSP leergeraeumt ? Kein Freund eingeloggt ? Ein kleines
+Spiel
+                kann die Langeweile vertreiben.
+
+29:   300 Eine Roentgenbrille.
+                Ermoeglicht tiefe und sehr interessante Blicke unter diverse
+                Schottenroecke. just for fun.
+
+30:  5052 Ein kleiner grauer Kasten.
+                Eines der besten Sehertools. Man kann mit ihm Nachrichten in
+                Form ganzer Textbloecke an andere Spieler senden. Autoload.
+
+31:  3003 Ein Ikonograph.
+                Fuer kleine Schnappschuesse zwischendurch. Haelt das P_Long
+                des betrachteten Objektes fest.
+
+
+(4/95, Cuchullin)
+
+
+Have Fun
+
+Cuchullin ()
+
+
+
+
diff --git a/d/seher/haeuser/special/special/faq/8 b/d/seher/haeuser/special/special/faq/8
new file mode 100644
index 0000000..3eceabc
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/8
@@ -0,0 +1,43 @@
+8. DESIGNER-SEHER
+
+Nun, vielleicht ist dieser Titel auch ein wenig uebertrieben. Aber als Seher
+kannst Du Deine gesamte Erscheinung im MUD selbst gestalten:
+
+Nun, Designertypen glaenzen durch ihr Aeusseres, also fangen wir mal beim
+Aussehen an: Was die Spieler sehen, wenn sie Dich untersuchen, kannst Du mit
+dem Befehl 'extralook' bestimmen. Die dort eingegebene Beschreibung wird der
+Auflistung dessen, was Du bei Dir traegst, vorangestellt.
+
+Zur Erscheinung gehoert auch das Auftreten, also designen wir mal ein bischen
+an Deinen messages herum, die erscheinen, wenn Du einen Raum betrittst oder
+verlaesst. Dies kannst Du mit setmin (text) bzw. setmout (text) setzen. Und
+wenn Du moechtest, kannst Du mit setmmout bzw. setmmin die Meldungen setzen,
+die Erscheinen, wenn Du einen Ort mittels eines teleports verlaesst/
+betrittst.
+
+Doch auch Designerseher leben unter dem Joch des Armageddon, und sind daher
+manchmal Waffen- und Ruestungslos. Wer es aber nicht ertragen kann, in diesen
+bitteren Stunden immer die Meldung 'greifft mit blossen Haenden an' zu sehen,
+der kann sich mittels des Befehls 'sethands (text)' eine martialischere (oder
+auch zaertlichere, je nach Geschmack ;)) Kampfmeldung fuer seine Haende
+setzen.
+
+Nun, son richtiger Schiggi-Miggi Seher hat natuerlich auch einen total
+laessigen Namen, nicht so was oedes wie 'Wurst, die Seherin'. Und deshalb
+koennte sich Wurst einen eigenen Namen geben, mit dem Befehl 'titel (text)'.
+Dann hiesse sie hinterher Beispielsweise 'Wurst iss ne Wurst'. Und wem das
+noch nicht laessig genug ist, der setzt sich mit 'presay (text)' auch noch
+einen Namensvorsatz. Beispiel: 'Mach ne Wurst, iss ne Wurst'. Wenngleich das
+vielleicht doch nicht SO cool ist. Geschmackssache halt ;).
+
+Ach ja, und wenn man dann bei all der Rumdesignerei vergessen haben sollte,
+was man ueberhaupt eingegeben hat, so erhaelt man mit dem Befehl 'review'
+einen Ueberblick ueber die meisten Einstellungen.
+
+Eine Anmerkung noch: Der Toeter (HAIL, HAIL ALL!) hat kuerzlich eine kleine
+Erweiterung in den Befehl setmout eingebaut: Nunmehr ist es moeglich, den
+Text 'nach (Richtung)' nicht mehr nur am Ende, sondern irgendwo im Text
+erscheinen zu lassen; dann muss im Text der out-message ein # erscheinen, die
+dann durch 'nach (Richtung)' ersetzt wird. Unterdruecken kannst Du die
+Richtungsangabe allerdings nicht, wenn Du die # weglaesst, wird diese
+Angabe einfach hinten angehaengt..
diff --git a/d/seher/haeuser/special/special/faq/9 b/d/seher/haeuser/special/special/faq/9
new file mode 100644
index 0000000..24b08d1
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/9
@@ -0,0 +1,9 @@
+
+9. FEHLERMELDUNG
+
+Ein nicht besonders wichtiger, aber in seinen Auswirkungen haeufig
+angetroffener Befehl: Fehlermeldung. Wenn Dir das ewige 'Wie bitte ?' langsam
+zu eintoenig wird, so kannst Du mit 'Fehlermeldung (Text)' einen neuen Text
+fuer Deine Fehlermeldung setzen - uebrigens genauso wie fuer die Uhrmeldung,
+was allerdings schon als Spieler meoglich ist, was jedoch scheinbar nur recht
+wenige Spieler wissen.
diff --git a/d/seher/haeuser/special/special/faq/anleitung b/d/seher/haeuser/special/special/faq/anleitung
new file mode 100644
index 0000000..cba61a7
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/anleitung
@@ -0,0 +1,219 @@
+
+ANLEITUNG MAGISCHER BEUTEL
+
+Stellen wir uns vor, Wurst, die Seherin, wollte ein Blutsaugerschwert
+herstellen, mit dem sie gierige kleine Anfenger aergern will:
+
+/ kommentare stehen immer hinter dem //...
+/ los gehts: 'erschaffe' eingeben, dann ist man im Beuteleditor, der dem
+/ bekannten editor von MPA und mail gleicht.
+/ Wurst gibt zunaechst eine ID fuer das Schwert ein:
+
+Bitte ein Schluesselwort eingeben:
+]id
+]Bitte eine neue ID eingeben:
+]schwert
+
+/ nun moechte Wurst noch eine weitere ID eingeben:
+
+Bitte ein Schluesselwort eingeben, ~h fuer Hilfe.
+]id
+
+/ nun kommen als Ausgabe auch die bisher eingegebenen IDs, in unserem Fall:
+
+Bisherige IDs:
+0 schwert
+Bitte eine neue ID eingeben
+]blutsaugerschwert
+
+/ Wurst haette hier uebrigens keine ID eingeben muessen. haette sie nur id,
+/ und danach enter eingegeben, haette dies dazu gedient, die bisher
+/ eingegebenen IDs anzuschauen.
+/ Weiter gehts mit der Langbeschreibung (das was man sieht, wenn man
+/ untersuche (id) eingibt):
+
+Bitte ein Schluesselwort eingeben:
+]lang
+Bitte die ausfuehrliche Beschreibung angeben. Beenden mit **
+]Dies ist das Schwert eines einst maechtigen Vampirs. Nun ist es
+]jedoch so wertlos geworden, dass sich jeder Seher es mit seinem
+]magischen Beutel klonen kann.
+]Es hat einen goldenen Schaft.
+]**
+
+/ Wurst moechte, dass die Spieler eine Erklaerung dafuer kriegen, dass das
+/ Schwert automatisch gezueckt ist... kein Problem, dafuer beschreibt sie den
+/ Schaft des Schwertes genauer:
+
+Bitte ein Schluesselwort eingeben:
+]detail
+Bitte den Detailnamen eingeben:
+schaft
+]Nun bitte eine Beschreibung fuer: " schaft " eingeben.
+]Dieser Schaft ist von purer Magie. Wer das Schwert besitzt, zueckt es
+]automatisch.
+]**
+Bitte ein Schluesselwort eingeben, ~h fuer Hilfe.
+]
+
+/ Das wars, schon besitzt das Schwert einen Schaft.
+/ Ebenso muss noch eine Kurzbeschreibung angegeben werden:
+
+Bitte ein Schluesselwort eingeben, ~h fuer Hilfe.
+]kurz
+Bitte die Kurzbeschreibung eingeben
+]Ein Blutsaugerschwert (gezueckt)
+
+/ Ueberrascht ? Die Kurzbeschreibung muss genau so eingegeben werden, wie sie
+/ der Spieler in seinem inventory sieht. Damit diese Beschreibung aber nicht
+/ erscheint, wenn das Schwert an das 'Opfer' uebergeben wird, muss ein Name
+/ eingegeben werden:
+
+Bitte ein Schluesselwort eingeben, ~h fuer Hilfe.
+]name
+Bitte den Namen eingeben
+]Blutsaugerschwert
+
+/ Ergo: Wird kein Name speziell eingegeben, wird erscheint, wenn das Objekt
+/ weitergegeben oder fallengelassen wird, die kurzbeschreibung dargestellt.
+/ Um die zu vermeiden, gibt es den Befehl 'name'.
+/ Allerdings wuerde beim Uebergeben des Schwertes jetzt erscheinen: 'Wurst
+/ gibt Dir den Schwert', da noch kein Geschlecht gesetzt wurde, und default-
+/ geschlecht maennlich ist. Daher folgt nun:
+
+Bitte ein Schluesselwort eingeben, ~h fuer Hilfe.
+]art
+Bitte die Kennziffer fuer das Geschlecht eingeben
+]maennlich: 1      weiblich: 2       saechlich: 0
+]0
+
+/ Nun ist mit saechlich auch das richtige Geschlecht gesetzt.
+/ Nun, so ein Schwert soll auch etwas wiegen, also gibt Wurst noch ein
+/ Gewicht mit an:
+Bitte ein Schluesselwort eingeben, ~h fuer Hilfe.
+]gewicht
+Bitte das Gewicht in Gramm eingeben (Minimum 100)
+]5000
+
+/ nun moechte sich Wurst noch vergewissern, dass ihr Plan gut vorbereitet
+/ ist. Daher laesst sie sich mit '~r' nochmal einen Ueberblick ueber das
+/ Schwert geben:
+
+Bitte ein Schluesselwort eingeben:
+]~r
+IDs:
+0 blutsaugerschwert
+1 schwert
+Kurzbeschreibung:
+Ein Blutsaugerschwert (gezueckt)
+Name:
+Blutsaugerschwert
+Gewicht: 5000 Gramm
+Geschlecht: saechlich
+Ausfuehrliche Beschreibung:
+Dies ist das Schwert eines einst maechtigen Vampirs. Nun ist es jedoch
+wertlos geworden, dass sich jeder Seher es mit seinem magischen Beutel
+klonen kann.
+Details:
+0: schaft:
+Dieser Schaft ist von purer Magie. Wer das Schwert besitzt, zueckt es
+automatisch.
+]
+
+/ Wurst ist zufrieden, und moechte nun die 'Aktion BSS fuer alle' beginnen,
+/ und benutzt den Befehl 'start', um das Schwert endgueltig zu erzeugen.
+/ Allerdings achtet sie dabei darauf, nicht im selben Raum wie ihr Opfer zu
+/ stehen, denn die Umstehenden erhalten beim Erzeugen des Objektes die
+/ Meldung:
+
+> Wurst denkt scharf nach und zieht dann etwas aus dem magischen Beutel
+
+/ Alles klar, das Objekt kann erschaffen werden:
+
+]start
+Du hast ein neues Objekt geschaffen. Es ist:
+Blutsaugerschwert
+Bitte ein Schluesselwort eingeben, ~h fuer Hilfe.
+]
+
+/ dummerweise bleibt man nach dem Erschaffen eines Objektes immer im editor
+/ des Beutels drin. Abbrechen muss man also mit '**', was zu einer etwas
+/ verwirrenden Meldung fuehrt:
+
+]Bitte ein Schluesselwort eingeben, ~h fuer Hilfe.
+]**
+Beschreibung ist abgebrochen. Die Werte wurden beibehalten.
+>
+/ Wurst haette auch ohne 'start' einzugeben den Beutel-editor verlassen
+/ koennen; sofern man dies mit '**' (und nicht mit '~q' tut kann man naemlich
+/ mit 'bearbeite' wieder in den Beutel-editor hineingehen, und uebernimmt
+/dabei die alten
+/ Werte.
+
+/ Dass die Werte beibehalten wurden, hat auch eine Auswirkung darauf, dass
+das erschaffene
+/ Objekt abgespeichert werden kann, um es spaeter nochmal zu laden. Dies
+moechte Wurst
+/ natuerlich gerne machen, da sie zu faul ist, den ganzen Kram nochmal zu
+tippen (und weil
+/ Wurst ja DIR alle Funktionen des Beutels zeigen soll;)) - ausserdem ists
+recht einfach - mit
+/ dem Befehl 'erhalte (nr.)' kann
+
+> erhalte 1
+> Das Objekt wurde auf Platz 1 abgespeichert.
+
+/ um sicherzugehen schaut Wurst nochmal mit 'speicherplaetze' nach, ob die
+Speicherung
+/ geklappt hat
+
+>speicherplaetze
+Speicherplatz 0: Nicht belegt.
+Speicherplatz 1: Ein Blutsaugerschwert (gezueckt).
+Speicherplatz 2: Nicht belegt.
+Speicherplatz 3: Nicht belegt.
+(...)
+
+/ offensichtlich hat es funktioniert, Speicherplatz 1 wurde belegt, man sieht
+die Kurzbe-
+/ schreibung des Objektes.
+
+---------
+
+/ Wochen spaeter. Wurst moechte das gespeicherte Schwert laden, um es neu zu
+erzeugen.
+/ dazu benutzt sie den Befehl 'lade (nr.)'
+
+> lade 1
+> Objekt von Speicherplatz Nr.1 wurde geladen.
+
+/ Das reicht allerdings noch nicht. Jetzt sind naemlich nur die Werte im
+Beutel geladen, aber
+/ das Objekt noch nicht erschaffen. Daher folgt zunaechst der Befehl
+bearbeite (nicht
+/ 'erschaffe', weil dieser die evtl. noch vorhandenen Werte im Beutel
+loescht!)
+
+> bearbeite
+Bitte ein Schluesselwort eingeben:
+]
+
+/ an dieser Stelle kann Wurst nun das alte Blutsaugerschwert genauso
+bearbeiten wie vorher -
+/ natuerlich kann sie die vorhandenen Daten uebernehmen, aber auch aendern
+(was durch
+/ einfaches neu eingeben geschieht). Wenn sie das Schwert nicht weiter
+bearbeiten moechte
+/ kann sie nun einfach 'start' eingeben, und den editor verlassen.
+/ So, und zu guter Letzt: Sofern Wurst das veraeppeln von Anfaengern genug
+ist, kann sie den
+/ Speicherplatz fuer das Schwert natuerlich auch freimachen, dies geschieht
+einfach durch den
+/ Befehl 'entferne (nr.)'
+
+So, das wars! Eine Menge Text fuer ein eigentlich brauchbares Sehertool, wenn
+man nur wuesste, wie es funktioniert ;))
+
+
+
+
diff --git a/d/seher/haeuser/special/special/faq/ansichten b/d/seher/haeuser/special/special/faq/ansichten
new file mode 100644
index 0000000..835ca97
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/ansichten
@@ -0,0 +1,4 @@
+Folgende Raeume sind bereits auf der Seherinsel mit langbeschreibungen
+versehen:
+(Koordinaten vert./horiz., wie in der Kurzbeschreibung auf der Insel)
+1,1: Wolpie-Raum :(
diff --git a/d/seher/haeuser/special/special/faq/faq.o b/d/seher/haeuser/special/special/faq/faq.o
new file mode 100644
index 0000000..7fc9efb
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/faq.o
@@ -0,0 +1,3 @@
+#0:0
+properties ([:2])
+synonym (["level":"4","mail":"5","finger":"11","quests":"2","echo":"12","fehlermeldung":"9","seher":"18","remote":"12","setmin":"8","titel":"8","fluchtrichtung":"3","zeitung":"9","haus":"14","sehernamen":"18","kugel":"6","pegasus":"2","portale":"1","quest":"2","looks":"8","namen":"18","beutel":"13","laden":"7","16":"karte","adressenverzeichnis":"16","neuigkeiten":"0","faq":"inhalt","magische kugel":"6","haeuser":"14","adressen":"16","seherladen":"7","roehrchen":"10","kanaele":"9","magische":"6","seherquests":"2","spielplatz":"15","levels":"4","seherquest":"2","paralleldimension":"10","19":"karte","r-emote":"12","seherlevel":"4","seherhaeuser":"14","anmerkung":"15","finger-befehl":"11","seherlevels":"4","uebersichtskarte":"karte","seherportale":"1","designer":"8","seherspielplatz":"15","seherhaus":"14","seherfaq":"inhalt","designer-seher":"8","design":"8","kommunikation":"5","transportmoeglichkeiten":"1",])
diff --git a/d/seher/haeuser/special/special/faq/files b/d/seher/haeuser/special/special/faq/files
new file mode 100644
index 0000000..9113b7b
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/files
@@ -0,0 +1,28 @@
+
+files seherfaq: alle +Path = /../morgeng/seher/faq/faq2-0
+
+
+
+0. Neuigkeiten: news.txt
+
+1. Transportmoeglichkeiten: inhalt.txt
+
+2. Seherquests: quest.txt
+3. Fluchtrichtung: flucht.txt
+4. seherlevel: level.txt
+5: Kommunikation: kommun.txt
+6: magische kugel: kugel.txt
+7: Seherladen: sladen.txt
+8: designer-seher: looks.txt
+9: fehlermeldung: errmes.txt
+10: paralleldimension.txt: para.txt
+11: finger-befehl: finger.txt
+12: echo, r-emote: echo.txt
+13: magischer beutel: beutel.txt
+anleitung magischer beutel: anleit.txt
+14: seherhaeuser: haeuser.txt
+15: Seherspielplatz: splatz.txt
+16: uebersichtskarte spielplatz: smap.txt
+18: alle seher im mg: salle.txt
+19 zusammenfassung & nachwort: zusamm.txt
+files: files.txt
diff --git a/d/seher/haeuser/special/special/faq/inhalt b/d/seher/haeuser/special/special/faq/inhalt
new file mode 100644
index 0000000..9c105de
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/inhalt
@@ -0,0 +1,26 @@
+
+Inhalt der SeherFAQ 2.0:
+
+   0. Neuigkeiten                    10. Paralleldimension
+   1. Transportmoeglichkeiten        11. Finger-Befehl
+   2. Seherquests                    12. Echo, r-emote
+   3. Fluchtrichtung                 13. Magischer Beutel
+   4. Seherlevel                     14. Seherhaeuser
+   5. Kommunikation                  15. Seherspielplatz
+   6. Magische Kugel                 16. Uebersichtskarte Seherspielplatz
+   7. Seherladen                     17.
+   8. Designer-Seher                 18. Alle Seher im MG
+   9. Fehlermeldung                  19. Zusammenfassung & Nachwort
+
+Um die entsprechenden Inhalte zu lesen, gib bitte >lies 2< oder
+>lies seherquests< ein.
+Zu dieser Seite kommst du mit >lies inhalt< zurueck!
+
+Noch eine Anmerkung: Wenn Du eine Idee hast, was an der FAQ verbessert,
+geaendert oder hinzugefuegt werden sollte, dann waere ich Dir sehr dankbar,
+wenn Du mir eine kurze mail schicken wuerdest, oder mit dem Befehl 'idee' in
+diesem Raum absetzen wuerdest !!!
+
+RYNE
+
+
diff --git a/d/seher/haeuser/special/special/faq/karte b/d/seher/haeuser/special/special/faq/karte
new file mode 100644
index 0000000..ef5e5f9
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/karte
@@ -0,0 +1,63 @@
+        SEHERSPIELPLATZ: UEBERSICHTSKARTE
+        (Stand: 20.1.95)
+  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20
+
+  X  X  X  X  X  X  X  X--X--X--X--X--X  X--X  X  X  X--X  X    1
+   \/ \               /   |\           \ |     |  |  |    /
+   /\  \             /    | \           \|     |  |  |   /
+  X  X--X  X  X  X--X  X  X  X  X  X  X  X--X--X--X--X  X--X    2
+  |      \      /      |  |                /          \   /
+  |       \    /       |  |               /            \ /
+  X  X  X  X--X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    3
+   \       |   \  \   /  /  /            |              |
+    \      |    \  \ /  /  /             |              |
+  X  X  X--X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    4
+      \/    \     \/   |  | /           /               |
+      /\     \    /\   |  |/           /                |
+  X  X  X  X  X--X  X  X  X  X--X  X  X  X  X  X  X  X  X  X    5
+     |\/ \/ \        \ |  | /         |                 |
+     |/\ /\  \        \|  |/          |                 |
+  X  X  X  X  X--X--X--X  X  X  X  X  X  X  X  X  X  X  X  X    6
+          / \   /        /  /          \               /|
+         /   \ /        /  /            \             / |
+  X  X  X  X--X--X--X--X--X  X  X  X  X  X  X  X  X  X  X  X    7
+          /         |\  \  \/             \          |
+         /          | \  \ /\              \         |
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X--X--X--X  X  X    8
+        |  |  |  | / \     \
+        |  |  |  |/   \     \
+  X  X  X--X  X--X  X--X--X  X  X  X  X  X  X  X  X  X  X  X    9
+     |\ | /|    /|     |
+     | \|/ |   / |     |
+  X  X  X  X  X  X  X--X--X--X  X  X  X  X  X  X  X  X  X  X    10
+
+
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    11
+
+
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    12
+
+
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    13
+
+
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    14
+
+
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    15
+
+
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    16
+
+
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    17
+
+
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    18
+
+
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    19
+
+
+  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X  X    20
+
diff --git a/d/seher/haeuser/special/special/faq/seherfaq b/d/seher/haeuser/special/special/faq/seherfaq
new file mode 100644
index 0000000..0488959
--- /dev/null
+++ b/d/seher/haeuser/special/special/faq/seherfaq
@@ -0,0 +1,16 @@
+
+
+Hallo!
+Hier also siehst Du die aktuelle Version von Rynes SeherFAQ.
+Wenn Du weitere Fragen oder Anregungen zu dieser FAQ hast,
+se so nett und schicke mir ein kurzes mail.
+Viel Spass beim Lesen !!!
+
+Zur FAQ kommst Du mit >lies Inhalt<
+
+
+
+
+
+
+
diff --git a/d/seher/haeuser/special/special/rj.c b/d/seher/haeuser/special/special/rj.c
new file mode 100644
index 0000000..42f3b98
--- /dev/null
+++ b/d/seher/haeuser/special/special/rj.c
@@ -0,0 +1,26 @@
+inherit "/std/thing";
+
+#include <properties.h>
+#include "../haus.h"
+
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+
+  SetProp(P_SHORT,0);
+  SetProp(P_NOGET, 1);
+  AddId("r&j");
+  AddCmd("lies", "lies");
+}
+
+static int lies(string str)
+{
+  int nr;
+
+  if (!str || sscanf(str, "seite %d", nr) != 1)
+    return notify_fail("Syntax: lies seite <nr>\n"), 0;
+
+  this_player()->More(SPECIALPATH+"rom_jul/seite"+nr, 1);
+  return 1;
+}
diff --git a/d/seher/haeuser/special/special/rom_jul/seite1 b/d/seher/haeuser/special/special/rom_jul/seite1
new file mode 100644
index 0000000..5b870dc
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite1
@@ -0,0 +1,42 @@
+Dieser Text ist von Etains Spielerin (Lissi Paffrath) geschrieben und
+saemtliche Rechte, insbesondere die der Verbreitung und Vermarktung, liegen
+ausschliesslich bei ihr.
+Anmerkungen und konstruktive Kritik bitte per mudmail an Etain oder Snuggle.
+Viel Spass beim Lesen wuenschen Snuggle und Etain.
+
+Und hier noch eine Uebersicht ueber den Inhalt:
+
+Seite 2:  Prolog
+
+Seite 3:  Erster Akt, erste Szene
+Seite 4:  Erster Akt, zweite Szene
+Seite 5:  Erster Akt, dritte Szene
+Seite 6:  Erster Akt, vierte Szene
+Seite 7:  Erster Akt, fuenfte Szene
+
+Seite 8:  Zweiter Akt, Einleitung
+Seite 9:  Zweiter Akt, erste Szene
+Seite 10: Zweiter Akt, zweite Szene
+Seite 11: Zweiter Akt, dritte Szene
+Seite 12: Zweiter Akt, vierte Szene
+Seite 13: Zweiter Akt, fuenfte Szene
+Seite 14: Zweiter Akt, sechste Szene
+
+Seite 15: Dritter Akt, erste Szene
+Seite 16: Dritter Akt, zweite Szene
+Seite 17: Dritter Akt, dritte Szene
+Seite 18: Dritter Akt, vierte Szene
+Seite 19: Dritter Akt, fuenfte Szene
+
+Seite 20: Vierter Akt, erste Szene
+Seite 21: Vierter Akt, zweite Szene
+Seite 22: Vierter Akt, dritte Szene
+Seite 23: Vierter Akt, vierte Szene
+Seite 24: Vierter Akt, fuenfte Szene
+
+Seite 25: Fuenfter Akt, erste Szene
+Seite 26: Fuenfter Akt, zweite Szene
+Seite 27: Fuenfter Akt, dritte Szene
+
+Seite 28: Epilog
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite10 b/d/seher/haeuser/special/special/rom_jul/seite10
new file mode 100644
index 0000000..d54f51d
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite10
@@ -0,0 +1,381 @@
+DER ZWEITE AKT
+	  DIE ZWEITE SZENE
+
+Azzuro,  der  nicht  Gefahr  laufen   wollte,	stellvertretend   fuer	 einen
+Menschenmann von den beiden Frauen zerrissen zu werden, liess sich  auf  einem
+Ast neben Romeos Schulter nieder. Er betrachtete den Jungen mit  einem	Blick,
+der bei Menschen immer erstaunte  Ausrufe  provoziert,	die  zumeist  nur  die
+unertraegliche Arroganz  der  Menschen	kundtun:  _Sieht_  _er_  _nicht_  FAST
+_intelligent_ _aus_?
+
+Romeo hatte keinen Blick fuer Azzuro uebrig, denn  Julia  betrat  den  Balkon.
+Offensichtlich hatte sie noch ein wenig Luft schnappen wollen,	bevor  sie  zu
+Bett ging, denn sie trug nicht mehr als ein Nachthemd,	einen  Schal  und  ein
+paar Tropfen Parfum.
+
+"Sieh sie dir an.", hauchte Romeo atemlos. "Ist sie nicht wunderschoen?"
+
+Azzuro verzichtete auf eine Antwort, denn ihm wurde bewusst,  dass  Romeo  ihn
+nicht nach seiner Meinung gefragt hatte.
+
+"Julia..." Er fluesterte ihren Namen nur. "Sie geht auf wie eine  Sonne,  also
+muss da Osten sein.", meinte Romeo und deutete auf den Balkon.
+
+"Es ist Norden, aber ich verstehe, was du meinst.", brummte Azzuro.
+
+"Wenn sie ihren Schal fallen laesst, muss der Mond untergehen, denn dann  kann
+er sich nicht mehr am Himmel halten."
+
+"Und er kann nissst mehr an sssisss halten.", kicherte Wlad.
+
+"Ich liebe sie und ich wuenschte, sie  wuesste  es.",  murmelte  Romeo.  "Dann
+waeren ihre Augen Sterne am Himmel und ich ein... eine..." Er  blickte  Azzuro
+an. "Wie heisst denn das Tier, das im Dunkeln jagt?"
+
+"Fledermaus.", antwortete Azzuro.
+
+"Genau. Ich waere dann nur zu gerne eine spitzohrige, lederflueglige,  haarige
+und mueckenfressende Fledermaus."
+
+Wlad pfiff schrill und wuetend. "Wenn er jetssst noch ssstinkend sssagt, werde
+isss ihn beissssen!"
+
+"Dann wuerde ich die Muecken fressen, die sie  stechen  wollen,  denn  niemand
+ausser mir darf in ihre Naehe kommen, auch keine Muecke. Ach, warum  sagt  sie
+denn nichts?"
+
+"Vielleicht, weil sie nicht weiss, dass du  hier  bist  um  sie  zu  hoeren?",
+vermutete Azzuro.
+
+"Ja, ich bin auch wirklich zu dreist, mich einfach hier  in  ihrem  Garten  zu
+verstecken und sie anzustarren wie ein Spanner."
+
+Julia stuetzte ihre Ellbogen auf das Gelaender des Balkons und ihr Gesicht  in
+die Haende. Der Schal klaffte auf und enthuellte eine unziemliche Menge  ihres
+Ausschnitts, was Romeo zu einem weiteren tiefen Seufzer veranlasste.
+
+"Ich wollte, ich waere dieser Schal!"
+
+Azzuro, der sich immer noch nicht  entschliessen  konnte,  sein  Urteil  ueber
+Julias Figur zu revidieren, laechelte mitleidig. "Wenn man so etwas mag."
+
+"Ach, dumme Kraehe, was weisst du schon von Liebe!", sagte  Romeo  zornig  und
+duckte sich vor Azzuros scharfem Schnabel. "Wenn  sie  doch  nur  etwas  sagen
+wuerde."
+
+Julia tat ihm den Gefallen. "Ach, wie dumm das doch alles ist."
+
+Vor Freude ueber ihre Stimme begannen Romeos Haende zu zittern und der	Inhalt
+ihrer Worte ging zum einen Ohr hinein und zum anderen hinaus, ohne  den  Umweg
+ueber das Gehirn zu nehmen, wie es bei Verliebten haeufig der Fall  ist.  "Sag
+noch etwas, meine Liebste, dann falle ich bestimmt in Ohnmacht."
+
+Azzuro grinste. "Verkneif dir das  lieber,  denn  der  Laerm  wuerde  bestimmt
+Aufmerksamkeit erregen."
+
+Julia sah nachdenklich zu Ulo hinueber. "Oh, Romeo."
+
+"Ich heisse Ulo, aber das macht nichts."
+
+"Koenntest du einen  Moment  so  tun,  als  seist  du  Romeo?",  fragte  Julia
+hoeflich.
+
+"Sicher, wenn du dich dann besser fuehlst."
+
+"Danke. Ach, Romeo, ich wuenschte, du waerest nicht Romeo."
+
+"Warum das denn?", fragte Ulo verwirrt.
+
+"Wenn du nicht Romeo waerest, dann waerest du auch  kein  Montague,  und  dann
+koennte ich dich lieben, ohne dass mein Vater  einen  Herzanfall  bekommt  und
+meine Mutter ihre Migraene."
+
+Ulo nickte. "Hm, aber wer soll ich denn dann sein?"
+
+"Ist doch egal. Wenn du ein Buergerlicher waerest, dann wuerde ich  aufhoeren,
+Capulet zu sein. Dann koennten wir einfach so  zusammen  sein  und  uns  einen
+Teufel um die Konventionen scheren."
+
+"Und wenn meine Oma groesser gewesen waere, dann haettet ihr sie einen Drachen
+geschimpft.", versetzte Ulo. "Nicht,  dass  sie  das  nicht  gewesen  waere.",
+fuegte sie verschaemt hinzu.
+
+"Was meinst du, soll ich ihr antworten?", fragte Romeo leise.
+
+"Warte noch ein bisschen.", riet Azzuro.
+
+"Verstehst du denn nicht? Die Capulets liegen mit den Montagues in Fehde, aber
+Romeo ist Romeo ist Romeo ist Romeo, auch, wenn er nicht Romeo Montague ist."
+
+"Nein, verstehe ich nicht.", antwortete Ulo.
+
+"Der Name allein ist doch nicht der ganze Mensch. Wenn ich einen  Schweissfuss
+Rose nenne, riecht er deswegen nicht besser.", erklaerte Julia.
+
+Ulo nickte. "_Das_ verstehe ich."
+
+"Ja. Und wenn Romeo nicht Romeo Montague waere,  sondern..."  Sie  ueberlegte.
+"Hans Schmitz, so waere er immer noch der gleiche Mensch und ich  koennte  ihn
+ohne Probleme lieben."
+
+"Hans Schmitz?", fragte Romeo unten leise.
+
+Azzuro grinste nur.
+
+"Also, Romeo, vergiss den Namen, den  brauchst  du  nicht.  Statt  des  Namens
+kannst du mich haben." Julia breitete theatralisch die Arme aus.
+
+Romeo konnte sich nicht laenger zurueckhalten. "Gut, ganz wie du willst.  Wenn
+du mich Hans Schmitz nennen willst, dann bin ich eben Hans Schmitz."
+
+Julia wurde feuerrot. "Spanner!", schimpfte sie.
+
+"Von mir aus auch Hans Spanner." Romeo zuckte die Schultern.
+
+Ulo fiel vor Lachen vom Ast und fing sich wenige Zentimeter  ueber  dem  Boden
+auf.
+
+"Weiss er, was er da sagt?", fluesterte sie Azzuro zu.
+
+"Wahrscheinlich nicht.", gab Azzuro zurueck. "Ist doch  auch  egal,  oder?  So
+weggetreten, wie er im Moment ist, koennte sie ihn Hans Wurst  nennen  und  er
+wuerde es lieben."
+
+Julia beugte sich ueber die Bruestung des Balkons und der Schal  rutschte  ihr
+noch weiter von den Schultern herunter. "Oh,  Moment,  wir  haben  doch  heute
+abend miteinander gesprochen, nicht? Du hast  mich  mit  einem	Weihnachtsbaum
+verglichen." Sie ueberlegte. "Dann bist du Romeo Montague."
+
+Romeo gab sich alle Muehe, den Blick auf  ihr  Gesicht	gerichtet  zu  halten.
+"Nein, denn du hast gerade  erst  beschlossen,  dass  ich  Hans  Spanner  sein
+soll.", erklaerte er im Brustton der Ueberzeugung.
+
+"Wie bist du ueberhaupt hierhergekommen?"
+
+"Ueber die Mauer?" Romeo war sich seiner  Sache  nicht  voellig  sicher.  "Ja,
+doch, ich bin ueber die Mauer geklettert."
+
+Julia kannte die Mauer von ihren eigenen Exkursionen.  "Die  ist  ganz  schoen
+hoch."
+
+"Die Fluegel der Liebe haben mich hinuebergetragen."
+
+"Wo hast du das denn gelesen?"
+
+"Keine Ahnung, aber es klingt huebsch, nicht?"
+
+"Ja, schon.", antwortete Julia. "Wenn meine Leute dich  hier  finden,  reissen
+sie dir den Kopf ab."
+
+"Oder etwas, das  du  mehr  vermissen  wuerdest.",  setzte  Azzuro  mit  einem
+boshaften Grinsen hinzu.
+
+Romeo wurde blass, doch dann besann er sich Julias und der Tatsache,  dass  er
+bei ihr Eindruck machen wollte. "Ach, wird schon nicht so  schlimm  sein."  Er
+zuckte mit den Schultern und  versuchte,  heldenhaft  auszusehen.  "Ich  liebe
+dich, ich will dich, deine Verwandten sind mir egal."
+
+"Du bist ein Holzkopf, wenn du keine Angst vor  ihnen  hast."  Azzuro  zwickte
+Romeo spielerisch in  den  Oberschenkel  und  wich  geschickt  den  aengstlich
+zugreifenden Haenden aus.
+
+"Du bist in grosser Gefahr, wenn du hier bleibst.", meinte auch Julia.
+
+"Ja, aber nicht deine Verwandten sind die Gefahr, sondern meine Augen."  Julia
+erroetete leicht. "Hm, ich muss zugeben, dass es mir keineswegs  recht  waere,
+wenn sie dich hier faenden."
+
+"Waere doch ein romanreifer Tod, nicht? Wenn  du  mich  nicht  lieben  willst,
+koennen sie mich haben, dann ist mir sowieso alles egal."
+
+Ulo lachte. "Mir ist auch alles egal: Ich mache Schluss mit  eurem  Leben  und
+rufe die Verwandten."
+
+"Untersteh dich!" riefen Julia, Romeo, Azzuro und Wlad  wie  aus  einem  Mund,
+beziehungsweise Maul oder Schnabel.
+
+Julia griff beherzt zu und umfasste Ulos Schnabel. "Wirst du ruhig sein?"
+
+"Mmmmm." Ulo schlug mit den Fluegeln.
+
+"Na gut." Julia entfernte ihre Hand, bereit, sofort wieder zuzufassen,  sobald
+Ulo tief Luft holte. "Woher wusstest du ueberhaupt, dass du mich  hier  finden
+wuerdest?", fragte sie.
+
+"Ich bin einfach der Nase nach gelaufen.", antwortete Romeo.
+
+"Nur gut, dass du Julia gefunden hast und  nicht  die  Toilette.",  fluesterte
+Azzuro ihm zu und genoss Romeos  tiefrotes  Gesicht,  das  in  der  Dunkelheit
+regelrecht zu leuchten schien.
+
+Auch Julia schien aehnlich Gedanken zu hegen, sprach sie allerdings nicht aus.
+"Nur gut, dass du nicht sehen kannst, wie rot ich bin."
+
+Ulo beaeugte kritisch Julias Gesicht und suchte die Roete vergeblich.
+
+"Ich bin naemlich ein anstaendiges Maedchen.", fuhr  Julia  fort.  "Eigentlich
+stehe ich nicht nachts auf zugigen Balkonen herum und rede mit Kraehen."
+
+"Das kann ich bestaetigen." Ulo grinste boese, doch bevor  sie  weitersprechen
+konnte, zog Azzuro ihr heftig am Schwanz.
+
+"Ich weiss genau,  dass  ich  eigentlich  ein  bisschen  widerspenstiger  sein
+muesste." Julia zog den  Schal  enger  um  die  Schultern  und  erntete  einen
+enttaeuschten Blick von Romeo. "Aber, wenn ich ehrlich  sein  soll,  habe  ich
+dazu ueberhaupt keine Lust. Das sind bloss Spielchen und ich mag nicht mit dir
+spielen."
+
+"Schade.", warf Romeo ein.
+
+"Nein, nicht schade. Denn dadurch kann ich dir ganz offen sagen, dass ich dich
+auch liebe. Denke ja nicht, ich sei ein Flittchen, weil ich so	ehrlich  bin."
+Julia drohte Romeo scherzhaft mit dem Finger.
+
+"Auch eine Art zu spielen.", murmelte Wlad vor sich hin.
+
+"Wuerde mir niemals  einfallen.",  antwortete  Romeo  und  meinte  es  voellig
+ehrlich.
+
+"Ich muss  zugeben,  dass  du  mich  ziemlich  aus  der  Bahn  geworfen  hast.
+Deinetwegen tue ich Dinge, die mir sonst nicht einfielen."
+
+Romeo erroetete tief und wusste dazu nichts zu sagen.
+
+Mit strengem Gesichtsausdruck fuhr Julia fort: "Wenn du mich wirklich  liebst,
+dann..."
+
+"Dann?", fragte Romeo atemlos.
+
+"Dann sag mir, was du von mir erwartest."  Nun  war  die  Reihe  an  Julia  zu
+erroeten. "Ich will dir den Gefallen tun und mich so verhalten, wie du es  von
+einem anstaendigen Maedchen erwartest."
+
+Romeo schlug sich rasch  alle  unanstaendigen  Gedanken  aus  dem  Kopf.  "Ich
+schwoere dir..."
+
+"Nicht schwoeren!" Julia erinnerte sich nur zu gut an die vielen Schwuere, die
+sie in ihrem Leben getan hatte. Der Schwur, niemals wieder  Nougatpralinen  zu
+essen, von denen  sie  Pickel  bekam,  war  nur  einer	davon  -  und  der  am
+leichtesten gebrochene.
+
+"Ja, aber..." Romeo ueberlegte angestrengt.  "Wenn  ich  dir  nicht  schwoeren
+darf, dass ich dich liebe, was soll ich denn sonst tun? Ich meine..."
+
+Julia unterbrach ihn wieder. "Ich weiss, was du  meinst.  Aber  wenn  du  mich
+wirklich liebst, dann kann es nur einen Schwur geben, den ich akzeptiere."
+
+Romeo schluckte. "Heirat?"
+
+"Zack, schon hat sie dich." Azzuro kicherte. "Geh jetzt,  bevor  es  zu  spaet
+ist! Oder noch besser: Renn!"
+
+Doch Romeo war schon jenseits  aller  guten  Ratschlaege.  "Ja...",  sagte  er
+gedehnt.
+
+"Wunderbar." Julia schenkte ihm ihr strahlendstes Laecheln.  "Dann  kannst  du
+dir aussuchen, wann und wo die Hochzeit stattfinden soll."
+
+"Komm rein, Kindchen, da draussen holst du  dir  den  Tod.",  rief  Polly  von
+drinnen.
+
+Ulo lachte und stiess Wlad an. "Den Tod nicht, eher einen Ehemann."
+
+"Das kann Polly doch nicht wissen.", gab Wlad kichernd zurueck.
+
+"Warte einen Moment, ich will eben Polly beruhigen."  Julia  liess  den  Schal
+fallen und verschwand nach drinnen.
+
+"Umpf.", machte Romeo ueberwaeltigt. "Ich fasse es nicht."
+
+"Ich auch nicht.", sagte Azzuro traurig. "Wie kannst du  dich  nur  so  leicht
+einfangen lassen?"
+
+"Wer hat denn hier wen gefangen?", fragte  Romeo.  "Ich  habe  doch  wohl  die
+wunderbarste, schoenste, kluegste..."
+
+"Ach du lieber Himmel!" Azzuro warf Romeo einen zutiefst entsetzten  Blick  zu
+und verschwand in der Dunkelheit.
+
+Julia tauchte wieder auf dem Balkon auf und legte sich ihren Schal  wieder  um
+die Schultern. "Wenn du es allerdings nicht ernst meinst, dann bitte..."
+
+"Julia!", rief Polly wieder.
+
+"Jaja, ich komme ja schon!" Julia seufzte. "...ich dich, lass mich in Frieden.
+Morgen schicke ich jemanden zu dir, der bei dir den Termin fuer  die  Hochzeit
+abholen soll. Wenn du mir keinen Termin gibst, dann werde ich wohl bis an mein
+Lebensende einsam und allein weinen." Die Vorstellung allein liess eine Traene
+ueber ihre Wange laufen. "Und jetzt schlaf gut."
+
+Romeo sog die feuchte Nachtluft durch die Nase und  bildete  sich  ein,  einen
+Hauch ihres Parfums zu riechen. Bedauernd blickte er zu  Julias  Balkon  hoch,
+der nun abgesehen von der Kraehe auf der Bruestung leer war. "Ach, Julia,  wie
+gerne bin ich bei dir und wie traurig bin ich, weil  ich  jetzt  wieder  gehen
+muss."
+
+"Julia?" Ulo ueberlegte. "Ach so, ich verstehe. Na  gut,  dann  bin  ich  eben
+jetzt Julia."
+
+"Hm?", fragte Julia, die es im Zimmer nicht mehr ausgehalten hatte.
+
+"Ach, nichts." Ulo rueckte zur Seite.
+
+"Bist du noch da, Romeo?", fluesterte Julia. "Wie schade, dass  du  kein  Hund
+bist und ich keine Hundepfeife habe, denn dann koennte ich  dich  rufen,  ohne
+gleich die ganze Nachbarschaft rebellisch zu machen."
+
+Wlad kicherte leise bei der Vorstellung von Romeo als Bernhardiner mit treuen,
+schwermuetigen Augen und einem Faesschen um den Hals,  mit  dessen  Inhalt  er
+sich waehrend der langen Nachtwachen vor Julias Fenster waermte.
+
+"Romeo?" Julia rief nun ein wenig lauter.
+
+"Ich bin schon da." Er sah zu Julias Balkon hoch. "Was soll ich tun?"
+
+"Ihr die Zeitung und die Pantoffeln bringen.", murmelte Ulo und zwinkerte Wlad
+zu.
+
+"Wann kann ich denn meinen Boten zu dir schicken?"
+
+"So gegen neun Uhr?", schlug Romeo vor.
+
+"Gut. Aber ich wollte noch etwas anderes..." Auf ihrer Stirn bildete sich eine
+steile Falte.
+
+"Ich bleibe hier, bis es dir wieder einfaellt." Romeo schien von dem  Gedanken
+sehr angetan zu sein.
+
+Julia kicherte leise. "Dann erinnere ich mich nie mehr daran, weil du mich  so
+voellig verwirrst und ich sowieso an nichts anderes als dich denken kann."
+
+Romeo liess sich Julias Satz noch einmal durch den Kopf gehen und nickte,  als
+er ihn begriffen hatte. "Dann bleiben wir  beide  hier  fuer  immer  und  ewig
+stehen."
+
+"Und verhungern.", warf Ulo voellig unpoetisch ein.
+
+"Dann waerest du wie ein kleiner Hund, weisst du?  Immer,  wenn  du  mal  eben
+aeh..." Julia zoegerte. "...einen Baum markieren willst, zoege ich  an  deiner
+Leine, weil ich dich nicht alleine gehen lassen will."
+
+"Das waere wunderschoen.", seufzte Romeo. "Ich waere gerne dein Hund."
+
+"Ja, der Gedanke ist reizvoll." Julia rief eine Generalmobilmachung  fuer  die
+gesamte Vernunft in ihrem Kopf aus. "Aber wahrscheinlich wuerde  ich  dich  so
+schrecklich  verwoehnen,  dass	du  in	kuerzester  Zeit  wie  eine  Sofarolle
+aussiehst und an Herzverfettung eingehst. Wenn du nicht bald gehst,  sage  ich
+dir gute Nacht, bis die  Sonne	aufgeht,  und  mache  dann  mit  guten	Morgen
+weiter." Sie laechelte, winkte ein letztes Mal und verschwand wieder in  ihrem
+Zimmer.
+
+Romeo sah ihr mit leeren Augen hinterher. "Wenn ich doch bloss ihr Bett waere,
+oder ihre Bettdecke, dann koennte ich  die  ganze  Nacht  bei  ihr  sein."  Er
+seufzte. "Es waere mir schon genug, wenn ich  ein  Traum  sein  duerfte,  dann
+koennte ich die ganze Nacht in ihrem Kopf sein."
+
+Ulo grinste. "In ihren Kopf will er bestimmt nicht hinein."
+
+"Ferkel.", wies Azzuro sie zurecht.
+
+"Nein, Kraehe. Ferkel haben keine Fluegel und sind auch nicht schwarz."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite11 b/d/seher/haeuser/special/special/rom_jul/seite11
new file mode 100644
index 0000000..2b5f62c
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite11
@@ -0,0 +1,131 @@
+DER ZWEITE AKT
+	  DIE DRITTE SZENE
+
+Bruder Lawrence hob seine Fuesse in den Sandalen besonders hoch, denn das Gras
+war nass und kalt vom Tau. Ueber seinen Arm  hatte  er	einen  Korb  gehaengt,
+seine Kutte vorne zusammengefasst und in den  Strick  gesteckt,  der  ihm  als
+Guertel diente. Die Sonne, die vorsichtig ueber die Gartenmauer lugte, brachte
+Bruder Lawrencens Tonsur zum Schimmern und  glitzerte  auf  seinen  taunassen,
+haarigen Beinen, wenn er sich niederbueckte und  Kraeuter  ausrupfte,  die  er
+sorgfaeltig in seinen Korb legte.
+
+"Komm nur her, du huebscher  Borretsch.",  sagte  Bruder  Lawrence  mit  einem
+Laecheln, das die Pflanze nicht erwiderte. "Du kommst heute in meinen Salat."
+
+"Ich koennte mir angenehmeres vorstellen.", murmelte Ulo, die  auf  der  Mauer
+sass und sich die Sonne auf das Gefieder scheinen liess.
+
+Azzuro, dem die durchwachte Nacht zusetzte, schreckte  neben  ihr  aus	seinem
+Nickerchen hoch. "Was meintest du?", fragte er verschlafen.
+
+Ulo antwortete nicht, sondern betrachtete Bruder Lawrence, dessen Bauch es ihm
+sichtlich schwer machte, die Gaensebluemchen zu pfluecken,  die  seinem  Salat
+Farbe geben sollte.
+
+"Hab ich dich.", meinte der  Moench  triumphierend.  "Du  bist  mir  besonders
+erwuenscht, kleines Gaensebluemchen. Denn sagt	nicht  schon  Leonhard	Fuchs:
+_Die_ _Gennssblum_ _ist_ _fuertrefflich_ _gut_ _zu_ _den_  _lamen_  _glidern_.
+Und lahm fuehle ich mich heute morgen ganz bestimmt." Er rieb sich den Ruecken
+und liess seinen Blick ueber den Kraeutergarten  wandern,  bis	er  auf  einem
+Maigloeckchen  haengenblieb.  "Dich  esse  ich  lieber  nicht,  auch  wenn  du
+huebscher bist, als das Gaensebluemchen.", versicherte er der Blume,  die  ihm
+jedoch keine Aufmerksamkeit schenkte. "So schoen du  riechst,  so  huebsch  du
+anzuschauen bist, so boesartig giftig bist du auch." Er seufzte und drohte der
+Blume scherzhaft mit dem Finger. "Jaja, so dicht  liegen  Schoenheit  und  Tod
+manchmal nebeneinander."
+
+"Der gute Bruder ist heute philosophisch aufgelegt.",  informierte  Ulo  ihren
+Gefaehrten.
+
+"Hmpf.", machte Azzuro ohne den Schnabel aus dem Gefieder zu ziehen.
+
+Ulo stiess ihn an und beobachtete mit haemischem Grinsen, wie Azzuro  sich  um
+sein Gleichgewicht bemuehte. "Romeo kommt."
+
+"Lass ihn kommen und mich schlafen.", brummte Azzuro ungehalten.
+
+"Was bist du, ein Mann oder..."
+
+"...eine Kraehe.", vervollstaendigte Azzuro den  Satz  und  beschloss,  lieber
+aufzuwachen, als Ulo weiterhin Gelegenheit zu geben,  ihn  von	der  Mauer  zu
+stossen.
+
+"Guten Morgen, Bruder Lawrence." Romeo winkte dem Moench froehlich zu.
+
+"Du bist aber heute gut  gelaunt,  Romeo.",  wunderte  sich  Bruder  Lawrence.
+"Sonst bekommt man dich doch nicht so frueh aus dem Bett. Junge Leute schlafen
+lang und viel und das ist auch gut so.	Andererseits..."  Er  mass  Romeo  mit
+einem kritischen Blick. "Du  bist  wohl  im  richtigen  Alter  um  eine  Nacht
+durchzumachen."
+
+"Genau. Und ich habe durchgemacht und fuehle mich frischer, als wenn  ich  die
+ganze Nacht geschlafen haette."
+
+Bruder Lawrence, der sich noch gut daran  erinnerte,  wie  es  war,  bevor  er
+Moench wurde, schickte das Grinsen, dass sich auf  seinem  Gesicht  ausbreiten
+wollte mit einem energischen Stirnrunzeln zurueck und besann  sich  auf  seine
+Pflichten als Priester und geistlicher	Berater.  "Du  hast  doch  nicht  etwa
+gesuendigt, mein Sohn?", fragte er streng. "Warst du bei Rosaline?"
+
+"Wer  ist   Rosaline?"   Romeo   rieb   sich   mit   uebertrieben   verwirrtem
+Gesichtsausdruck die Stirn. "Ach so, das war die Frau, die mich so schrecklich
+traurig gemacht hat." Er lachte. "Nein, Bruder Lawrence, diese Frau  habe  ich
+vergessen."
+
+"Siehst du, ich habe dir doch gleich gesagt, dass dies  das  beste  fuer  dich
+sein wuerde." Nun  stahl  sich  trotz  aller  Anstrengungen,  die  der  Moench
+unternahm, doch noch Neugierde in seinen Blick. "Wo hast  du  dich  denn  dann
+rumgetrieben?"
+
+"Ich habe beschlossen, den Anweisungen der Bibel zu folgen und meine Feinde zu
+lieben."
+
+"Das ist an und fuer sich sehr loeblich, aber wenn du mir nicht sagst, was  du
+getan hast, kann ich dir auch nicht sagen, was ich davon halte. Und meinen Rat
+suchst du doch, wenn ich dich richtig verstehe."
+
+"Nein, eigentlich nicht.", antwortete Romeo und  musste  ueber  das  verwirrte
+Gesicht des Moenchs laecheln. "Also, gestern abend habe ich bei  den  Capulets
+eine Feier besucht und dabei Julia kennengelernt."
+
+"Oh.", machte Bruder Lawrence verstaendnislos.
+
+"Sie ist wunderschoen, ich liebe sie, sie liebt mich und alles  was  wir  noch
+brauchen, um endgueltig gluecklich  zu	werden,  ist  ein  Priester,  der  uns
+verheiratet.", sprudelte es nun aus Romeo heraus.
+
+"Aha." Nun verstand Bruder Lawrence, was  sein  Schuetzling  wollte.  "Und  an
+dieser Stelle komme ich ins Spiel." Romeo laechelte seinen vaeterlichen Freund
+so strahlend an, dass dessen Herz dahinschmolz. "Ich muss schon sagen, das ist
+eine ueberraschende Wendung. Erst liebst du  Rosaline  und  jammerst  mir  die
+Ohren voll, dann lernst du eine andere Frau  kennen  und  schon  ist  Rosaline
+Schnee von gestern. Tja, ich sollte mir wohl angewoehnen, die jungen Leute mit
+einem  Koernchen  Salz	zu  geniessen.	Zu  meiner  Zeit  war  man  nicht   so
+sprunghaft."
+
+"Aber du wolltest doch,  dass  ich  Rosaline  nicht  mehr  liebe."  Romeo  war
+verwirrt.
+
+"In Rosaline warst du verknallt, nicht verliebt. Das  ist  ein  entscheidender
+Unterschied."
+
+"Siehst du, jetzt hast du doch deinen Willen. Ich liebe Julia  und  bin  nicht
+verknallt in sie."
+
+"Das hast du von Rosaline auch gesagt."
+
+Romeo ueberlegte, denn dieser Einwand wog schwer. "Ja, aber Rosaline hat  mich
+nicht zurueckgeliebt."
+
+Bruder Lawrence wiegte bedaechtig seinen Kopf hin und her. "Das ist wohl wahr.
+Na, ich werde euch verheiraten, zumindest wird es euren Eltern	helfen,  ihren
+Streit zu begraben."
+
+"Worauf warten wir dann noch?" Romeo lief ein paar  Schritte  auf  die  kleine
+Kapelle zu.
+
+"Immer langsam mit den jungen Pferden." Er wischte sich den Schweiss  von  der
+Stirn und bedachte Romeo mit einem langen Blick. "Nein, junge  Hengste  laufen
+schnell, es sind die alten Wallache, die nicht hinterherkommen.",  korrigierte
+er sich und laechelte.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite12 b/d/seher/haeuser/special/special/rom_jul/seite12
new file mode 100644
index 0000000..a79896f
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite12
@@ -0,0 +1,266 @@
+DER ZWEITE AKT
+	  DIE VIERTE SZENE
+
+Mercutio zog ein unwilliges Gesicht. "Ich frage  mich,  wo  zum  Teufel  Romeo
+steckt. Letzte Nacht war er nicht daheim."
+
+"Jedenfalls nicht bei seinem Vater.", grinste Benevolio.
+
+"Bestimmt  ist  er  draussen  herumgerannt  und  hat  ueber   diese   Rosaline
+gejammert." Mercutio spuckte den Namen regelrecht aus.
+
+"Es kommt noch schlimmer." Benevolio zueckte ein Taschentuch und  putzte  sich
+umstaendlich die Nase. "Es ist ein Brief von Tybalt angekommen."
+
+"Ach  du  dicker..."  Mercutio   wurde   blass.   "Das   ist   bestimmt   eine
+Herausforderung."
+
+Sie wissen,  liebe  Leserin,  lieber  Leser,  dass  in	mythischer  Zeit  eine
+Herausforderung nicht mehr war, als heute eine Einladung zu einem  Tennismatch
+unter  Feinden.  Natuerlich  lief  es  nicht  ganz  so	unblutig  ab  wie  ein
+Tennismatch, dafuer aber hatten die Bestattungsunternehmer mehr zu tun und die
+Gerichte mussten sich  nicht  mit  Beleidigungsklagen  auseinandersetzen.  Ein
+findiger Kopf erfand dann irgendwann die  Rechtschutzversicherung  und	sorgte
+dafuer,  dass  Duelle  verboten  wurden.  Wir  schulden  dem  Erfinder	dieser
+Versicherung  bis  heute  Dank,  denn  ansonsten  waeren  die	Praemien   der
+Sterbekassen und Lebensversicherungen wesentlich hoeher.
+
+Benevolio nickte bedaechtig. "Romeo wird ihm schon  eine  angemessene  Antwort
+darauf geben."
+
+"Solange es keine anmassende ist."  Mercutio  schuettelte  den  Kopf.  "Dieser
+Tybalt macht mir Sorgen."
+
+"Ach was, Tybalt ist ein aufgeblasener Idiot."
+
+"Vielleicht. Aber ausserdem ist er noch ein verdammt guter Fechter."
+
+"Romeo auch."
+
+"Nicht gut genug, fuerchte ich." Mercutio haetter gerne eine  bessere  Meinung
+von den Fechtkuensten  seines  Freundes  gehabt,  hatte  jedoch  Tybalt  schon
+kaempfen sehen.
+
+"Komm, male nicht schwarze Leute an weisse Waende." Benevolio wedelte mit  den
+Haenden, als wolle  er	einen  Mueckenschwarm  verscheuchen.  "Siehst  du?  So
+vertreibe ich deine trueben Gedanken."
+
+"Und holst Romeo damit her.", laechelte Mercutio. "Hallo Romeo.  Na,  Wie  war
+deine Nacht?" Ein luesternes Grinsen breitete sich ueber sein Gesicht aus.
+
+"Interessant.", antwortete Romeo unbestimmt.
+
+"Das kann ich dir ansehen, mein Freund." Benevolio strich die zerzausten Haare
+seines Cousins zurecht.
+
+"Hast uns ja ganz  schoen  reingelegt,  mein  Lieber.",  wies  Mercutio  Romeo
+zurecht.
+
+"Habe ich? Das tut mir leid, aber ich bin im Moment so schrecklich...  aeh..."
+Er ueberlegte und fuegte dann mit einem Grinsen hinzu: "...strapaziert."
+
+Mercutio lachte. "Deine Strapazen moechte ich haben!"
+
+"Dann waerest du genauso muede wie ich."  Romeo  rieb  sich  uebertrieben  die
+Augen.
+
+"Und haette wie du Aehnlichkeit mit der Katze, die den Sahnetopf ausgeschleckt
+hat."
+
+"Nachdem die Koechin das Verbrechen entdeckt hat, kannst du meine Rolle  gerne
+haben.", meinte Romeo grinsend.
+
+"Das ist unfair: Die Sahne bekommst du und ich den Aerger mit der Koechin."
+
+"Besser den Aerger als die Koechin." Romeo schuettelte sich.
+
+"Besser die Sahne als beides."  Mercutio  wiegte  sich  in  den  Hueften.  "So
+gefaellst du mir schon besser, Romeo. Gestern hast du noch ziemlich truebe aus
+der Waesche geschaut." Er grinste breit.  "Und  heute  nacht  in  die  Waesche
+hinein, nehme ich an."
+
+Romeo trat nach Mercutio, gab sich aber keine besondere Muehe, ihn zu treffen.
+"Nichts da, ich bin anstaendig."
+
+"Nicht, dass du es freiwillig waerest."
+
+"Pfft.", machte Romeo und wandte sich ab. Sein Blick blieb auf Polly  haengen,
+die sich ihren Weg ueber den Marktplatz bahnte und dabei an ein Schlachtschiff
+unter vollen Segeln gemahnte. Verschaemt deutete er mit dem Finger auf	Polly.
+"Streicht die Flagge, Leute, der Korsar wird uns entern."
+
+Mercutio verbarg sein Gesicht hinter den  Haenden.  "Bemannt  die  Boote,  sie
+rammt uns!"
+
+Benevolio lachte. "Dort blaest er!"
+
+Azzuro schuettelte sich, als er wieder einmal ein fremdes Zitat hoerte,  blieb
+aber stumm.
+
+"Was soll denn das, Benevolio?" Romeo ergriff die Nase seines Cousins mit zwei
+Fingern, zog sein Gesicht daran nach oben und beschaute sich sowohl die Zaehne
+als auch die Augen. "Bist du sicher, dass du ganz gesund bist?"
+
+"He!" Benevolio gab Romeo einen Stoss. "Lass das, du tust mir weh."
+
+"Ich wollte doch nur nachsehen, ob dir irgendetwas fehlt."
+
+"Da oben fehlt ihm nichts.", belehrte  Mercutio  seinen  Freund.  "Schon  eher
+fehlt ihm unten etwas, aber darueber sprechen anstaendige Maenner ja nicht."
+
+"Anstaendige Maenner benutzen es und schweigen.", meinte Romeo.
+
+"Anstaendige Maenner brauchen es nicht." Benevolio rieb sich die Nase.
+
+"Armer Benevolio, hat man dir das zum Trost  erzaehlt?"  Mercutio  taetschelte
+Benevolios Kopf.
+
+"Sieh an, die Galeone hat noch eine Schaluppe im Schlepptau.", bemerkte Romeo.
+
+"Die Schaluppe ist wohl das Proviantschiff." Benevolio hatte den grossen  Korb
+bemerkt, der ueber Peters Arm hing und mit  Gemuese  vollgestopft  war.  Romeo
+nickte, doch da Polly nun auf Hoerweite herangekommen  war,  zog  er  es  vor,
+nicht mehr zu antworten und sich statt dessen vor Polly zu verneigen.
+
+"Guten Morgen.", gruesste Polly.
+
+"Eher guten Abend.", gab Mercutio zurueck.
+
+"Wieso? Es ist doch noch frueh am Tag."
+
+"Aber Mittag ist viel zu unanstaendig, den uebergeht eine  Dame  besser.  Wenn
+der Zeiger so aufgerichtet ist..." Mercutio liess den Rest des Satzes  in  der
+Luft haengen.
+
+"Peter, mein Faecher.", verlangte Polly und mass Mercutio mit einem Blick, den
+man nicht in Worten beschreiben kann.
+
+Eher ist es moeglich, Mercutios Entsetzen zu beschreiben,  als	er  den  Blick
+auffing und tiefrot wurde. Er erinnerte sich ploetzlich an den Tag, an dem ihn
+seine eigene Kinderfrau mit der Hand in der  Hose  erwischt  hatte.  Um  seine
+Verlegenheit zu ueberspielen, rief er: "Jawohl, Peter, den Faecher  der  Dame,
+der Faecher ist wahrscheinlich ein schoenerer Anblick als das Gesicht."
+
+Polly kniff die Augen zusammen, sagte aber nichts.
+
+"Nehmen  Sie  ihn  nicht  zu  ernst.",  riet  Romeo  und   versuchte,   Pollys
+Aufmerksamkeit von Mercutio abzulenken.
+
+Polly seufzte. "Kann einer von Ihnen mir wohl sagen, wo ich einen jungen  Mann
+namens Romeo finde?"
+
+"Wenn Sie nur seinen Vornamen wissen, wird er vermutlich  ein  Graubart  sein,
+bevor sie ihn  gefunden  haben.",  meinte  Romeo  laechelnd.  "Aber  wenn  Sie
+einstweilen mit mir Vorlieb nehmen wollen?"
+
+"Nicht, dass ich eine Vorliebe fuer Sie haette,  nein,  aber  wenn  Sie  Romeo
+heissen, dann nehme ich Sie."
+
+Mercutio machte "Ha!", und kruemmte sich dann in einem Hustenanfall,  der  ihm
+sofort von Benevolio ein paar kraeftige Schlaege auf den Ruecken eintrug.
+
+"Aeh, ich bringe Mercutio zu dir nach Hause, Romeo, er sollte etwas trinken."
+
+Benevolio gab sich alle erdenkliche Muehe, sein Gesicht ernst zu halten.  "Wir
+treffen uns dann da zum Mittagessen." Er beugte sich  besorgt  ueber  Mercutio
+und schleppte diesen aus Pollys Reichweite.
+
+"Was war denn das fuer ein Flegel?", fragte Polly.
+
+"Wie ich schon sagte, nehmen Sie ihn nicht fuer voll. Was kann  ich  fuer  Sie
+tun?" Romeo hatte selbst Probleme, nicht zu lachen.
+
+"Also wirklich." Polly war immer noch  nicht  bereit,  Mercutios  Benehmen  zu
+vergessen. "Wie kann er sich nur so gegen eine Dame verhalten. Als ob ich eine
+Hergelaufene waere, mit der er machen  kann,  was  er  will."  Ihre  Empoerung
+schlug in Wut gegen Peter um und sie schlug mit ihrem Faecher nach  ihm.  "Und
+du stehst dabei und haelst Maulaffen feil,  waehrend  der  Kerl  mir  zu  nahe
+tritt."
+
+Peter rieb sich die Stelle an seinem Arm, die Polly getroffen hatte. "Ich  hab
+nix gesehen, hab ich nich. Is ja nich, als waer ich nich  direkt  dabei,  wenn
+man Ihnen ueberhaupt zu nah kommen koennt." Sein Blick ruhte einen  Augenblick
+interessiert auf Pollys stattlicher Figur. "Aber der Jung is ihnen nich zu nah
+gekommen, is er nich." Er nickte noch einmal zur Bekraeftigung  und  zwinkerte
+Romeo zu, der sich umstaendlich die Nase putzte und sein Grinsen hinter seinem
+Taschentuch verbarg.
+
+Polly betrachtete Peter nachdenklich, wandte sich dann aber Romeo  zu.	"Also,
+junger Mann. Sie wissen, dass Julia mich schickt. Ich sage Ihnen jetzt	nicht,
+was sie mir gesagt hat, obwohl ich es nicht vergessen habe, denn ich  vergesse
+nie etwas. Auch  die  Beleidigungen  dieses  Flegels  nicht,  der  so  schnell
+verschwunden ist, ohne sich zu entschuldigen. Wenn Sie ein  anstaendiger  Mann
+sind, und das hoffe ich fuer Sie, denn ansonsten bekommen Sie es  mit  mir  zu
+tun, dann rate ich Ihnen, mir zu sagen, wie Ihre Absichten sind.  Glauben  Sie
+mir, ich werde Julia troesten, wenn Sie mit ihr ein  falsches  Spiel  treiben,
+aber Sie werden sich in diesem Fall wuenschen, sie haetten es nie  angefangen.
+Also, was sagen Sie?"
+
+Romeo ueberlegte nicht lange. "Wenn Sie mich so fragen, dann lassen  Sie  mich
+erklaeren, dass..."
+
+Polly  klatschte  in  die  Haende  und	beobachtete  befriedigt,   wie	 Romeo
+zusammenzuckte.
+
+"Gut, das ist sehr gut, da wird mein Laemmchen ausser sich sein vor Glueck."
+
+"Ich glaube, Sie hoeren mir nicht zu."
+
+"Wieso? Ich habe alles gehoert, was ich hoeren muss. Oder haben Sie etwa nicht
+gesagt, dass Sie sich erklaeren? Soweit ich weiss, sagt ein anstaendiger  Mann
+doch, dass er sich erklaert. Wie auch immer, wenn  Sie	sich  erklaeren,  dann
+haben Sie lautere Absichten, also gebe ich mich zufrieden."
+
+"Da, jetzt gibt es keinen Weg zurueck." Ulo grinste boese.
+
+Azzuro schuettelte bedauernd den Kopf und schwieg.
+
+Als Romeo die Bedeutung von Pollys Worten daemmerte, wurde er, soviel soll  zu
+seiner Ehrenrettung gesagt sein, nur  fuer  einen  kurzen  Moment  bleich.  Er
+raeusperte sich. "Na  gut.  Bitten  Sie  Julia,  heute  Nachmittag  zu  Bruder
+Lawrence zu kommen, dort werden wir heiraten." Er grub in seinen  Taschen  und
+foerderte ein paar Muenzen zutage, die er Polly in die Hand drueckte. "Und das
+ist fuer Sie."
+
+"Nein, danke. Ich nehme kein Geld dafuer, dass ich Julia einen Gefallen  tue."
+Azzuro flog eine Runde um Romeos Kopf  und  fluesterte	leise:	"Denk  an  die
+Hochzeitsnacht, das ist doch das Beste am Heiraten."
+
+Romeos Gesicht leuchtete auf. "Na gut, dann nehmen Sie  es  dafuer,  dass  Sie
+waehrend der  Trauung  in  Lawrencens  Garten  stehen  und  eine  Strickleiter
+entgegen   nehmen."   Polly   wurde   puterrot.   "Ah,   ich   verstehe,   die
+Hochzeitsnacht."  Rasch  verbarg  sie  ihr  Gesicht  hinter  dem  Faecher  und
+kicherte. "Einverstanden." Polly blickte zu Peter und runzelte die Stirn. "Hm,
+wer wird die Leiter denn bringen? Ich meine,  wird  derjenige  auch  schweigen
+koennen?"
+
+"Bestimmt, dafuer werde ich sorgen." Romeo laechelte.
+
+"Dann ist es ja gut. Tja, daran habe ich nicht gedacht, als sie als  Kind  auf
+die Nase fiel und mein	Mann  Gott-hab-ihn-selig  sagte..."  Polly  unterbrach
+sich. "Ach ja, da gibt es noch einen jungen Mann, der gerade  zu  Besuch  ist.
+Paris heisst er, vielleicht haben Sie schon von ihm gehoert? Er macht ganz den
+Eindruck, als haette er Lust, sich bei Julia einzunisten und  wenn  er	soweit
+ist auch gleich bei ihrem Vater. Im Geldbeutel, wenn Sie  verstehen,  was  ich
+meine. Was denken Sie, wie niedlich blass Julia wird, wenn  ich  sie  aufziehe
+und ihr sage, dass  Paris  ein	besserer  Mann	ist  als  Sie?"  Polly  genoss
+sichtlich Romeos verwirrten Gesichtsausdruck. "Sie  will  so  gar  nichts  von
+einem anderen Mann hoeren und sieht wirklich allerliebst aus,  wenn  sie  sich
+ueber mich aergert."
+
+"Aeh, ja?"
+
+"Ja, das tut sie. Und mehr noch, Sie wuerden Ihre helle  Freude  daran  haben,
+wenn Sie hoeren koennten, wie sie ihrem Hund von Ihnen erzaehlt. Ganz niedlich
+rollt sie dabei das  R	in  Ihrem  Namen  und  macht  dabei  das  Knurren  des
+Huendchens nach." Romeo zog es vor,  nicht  weiter  ueber  die  Aehnlichkeiten
+zwischen seinem Namen und dem Knurren eines Schosshuendchens nachzudenken  und
+laechelte noch ein Mal mit erzwungener	Freundlichkeit.  "Wie  dem  auch  sei.
+Bitte richten Sie Julia meine Gruesse aus."
+
+"Mehr nicht?"
+
+"Mehr sage ich ihr dann heute Nacht, nachdem wir verheiratet sind."
+
+Wieder wurde Polly rot. "Oh."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite13 b/d/seher/haeuser/special/special/rom_jul/seite13
new file mode 100644
index 0000000..9705992
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite13
@@ -0,0 +1,122 @@
+DER ZWEITE AKT
+	  DIE FUENFTE SZENE
+
+Ulo und Azzuro hatten sich im Garten damit vergnuegt, Julia zu beobachten.
+
+"Ich sitze wie auf Kohlen.", beklagte sich Julia bei Ulo.
+
+"Krah.", machten Ulo und Azzuro wie aus einem Schnabel.
+
+Verwirrt blickte Julia zu ihnen hoch. "Letzte Nacht konntet  ihr  reden."  Sie
+zuckte die Schultern. "Aber letzte Nacht war ja ohnehin nichts so, wie es sein
+sollte. Alles war  ganz..."  Sie  zoegerte.  "...ganz  verzaubert."  Sichtlich
+erfreut ueber ihre poetische Wortwahl rief sie ihren  Hund  und  spielte  eine
+Zeit lang mit ihm. "Polly macht mich noch ganz verrueckt.", klaerte Julia  den
+Hund auf. "Kannst du dir vorstellen, dass sie  fuer  einen  simplen  Botengang
+drei Stunden braucht?"
+
+Statt einer Antwort rollte sich der Hund auf den Ruecken und tat, was Hunde am
+besten koennen: Er hechelte.
+
+Hunde, liebe Leserin, lieber  Leser,  gehoeren	in  eine  Geschichte  wie  der
+unseren einfach dazu. Sie sind das Salz in der Suppe.
+
+Wer  jemals  Liebeskummer  hatte,  weiss  die  Gesellschaft  eines  Hundes  zu
+schaetzen, denn mit ihren seelenvollen braunen Augen haengen  sie  gebannt  an
+den Lippen ihres Frauchens oder Herrchens und scheinen wahrhaftig  jedes  Wort
+zu verstehen. Dies ist selbstverstaendlich ein	Trick,	mit  dem  Hunde  ihren
+Menschen dazu bringen, ihnen einen besonders schoenen Knochen  zu  spendieren.
+Dennoch fallen wir Menschen immer wieder  auf  Hunde  herein,  denn  wir  sind
+leicht zu domestizieren.
+
+Die ganze Tierwelt zollt den Hunden fuer ihren Erfolg  mit  der  Zaehmung  des
+Menschen grossen Respekt. Die einzigen Tiere, die den  Hunden  diese  Leistung
+absprechen,  sind  die	Katzen,  doch  das  liegt  eher  daran,  dass	Katzen
+prinzipiell niemanden respektieren. Julia taetschelte ihrem Hund gehorsam  den
+Bauch. "Wenn Polly bloss schneller waere. Ich halte es kaum noch aus."
+
+Das  Knarzen  und  Quietschen  des  Gartentores  gab  die  Ankunft   wichtiger
+Neuigkeiten bekannt, noch bevor Polly hindurch trat. Julia sprang auf wie  von
+einer Bremse gestochen und rannte auf Polly zu. "Polly, liebe Polly!  Na,  sag
+schon, was hat er  gesagt?  Was  soll  ich  machen?  Hast  du  ihn  ueberhaupt
+getroffen? Red doch!"
+
+Polly schnaufte. "Nun mal langsam. Setz dich lieber erst einmal hin."
+
+"Ich kann mich nicht setzen, bevor du mir nicht gesagt hast, was los ist."
+
+"Und ich kann dir nichts sagen, solange ich noch so  ausser  Atem  bin.  Jetzt
+setz dich hin, du machst mich verrueckt. Wie soll ich denn Luft holen koennen,
+wenn Du so herumspringst?"
+
+Julia betrachtete ihre Amme kritisch. "So sehr kannst du doch gar nicht ausser
+Atem sein, wenn du mir sagen kannst, dass du ausser Atem bist."
+
+"Das verstehst du noch nicht." Polly holte tief Luft und donnerte: "Setz  dich
+hin!" Julia plumpste ins Gras.
+
+"So doch nicht, du machst ja Flecken in dein  Kleid."  Polly  seufzte.  "Kind,
+Kind, wirst du denn nie erwachsen?"
+
+Wortlos stand Julia auf und liess sich auf einer Bank nieder.  "Jetzt  besser?
+Kannst du jetzt endlich reden?"
+
+Polly laechelte. "Ja, jetzt kann ich reden. Da hast du dir ja  einen  schoenen
+Galan ausgesucht, Julia. Also, ich will ihn ja nicht schlechter machen als  er
+ist, aber ich muss schon sagen, er hat ein paar  sehr  unerfreuliche  Freunde.
+Obwohl er einen knackigen ... Geist hat. Knackig im Sinne von frisch, wenn  du
+verstehst, was ich meine. Er ist noch nicht ganz trocken.  Hinter  den	Ohren.
+So, jetzt erzaehl aber, was du heute gefruehstueckt hast."
+
+Ulo grinste breit auf  ihrem  Ast.  "Polly  versteht  es  wirklich,  Julia  zu
+triezen."
+
+Azzuro,  der  fasziniert  auf  Pollys  rotes  Gesicht  blickte	und  auf   den
+Schlaganfall wartete, nickte nur.
+
+Julia, die wusste, was Polly beabsichtigte, seufzte tief. "Das weiss  ich  und
+nichts."
+
+"Nichts? Kind, du faellst mir ja ganz vom Fleisch. Das  ist  aber  nicht  gut,
+wenn Romeo sich an dir blaue Flecken holt."
+
+Julia setzte ein unschuldiges Gesicht auf. "Holt er sich denn blaue Flecken?",
+fragte sie scheinheilig.
+
+Azzuro nickte. "Tut er."
+
+Polly kniff Julia in die Seite. "Also, wenn du es genau wissen willst..."
+
+"Ja?"
+
+"Also, dein junger Freund sagt, so wie es ein anstaendiger Mann sagen  sollte,
+wenn er auf sich haelt und ausserdem noch die Chance seines Lebens  bekommt...
+Wo ist eigentlich deine Mutter?"
+
+"Also, Polly, das ist ja nun sicherlich nicht seine  Antwort,  oder?  'Wo  ist
+deine Mutter'" Julia schuettelte den Kopf.
+
+"Du liebes bisschen, du bist ganz schoen heiss. Auf die  Antwort,  meine  ich.
+Wenn du verstehst, was ich meine. Also, er sagt, ganz und gar..."
+
+Ulo stoehnte mit Julia im Chor.
+
+"Du sollst heute nachmittag in die Kapelle dieses Minoriten  kommen.  Lawrence
+heisst er. Und da bekomme ich einen Ring und du eine Leiter." Sie  schuettelte
+den Kopf. "Nein, umgekehrt: Du bekommst den Ring und ich die Leiter  fuer  die
+Hochzeitsnacht."
+
+Jubelnd huepfte Julia um Polly herum und zog uebermuetig an  Azzuros  Schwanz.
+Ihr Hund, der ihre Freude missverstand, kniff Polly in die Wade  und  handelte
+sich einen Tritt ein.
+
+Auf Pollys verwittertes Gesicht stahl sich ein Laecheln. "Sieht ganz  so  aus,
+Kind, als fielest du gleich auf den Ruecken."
+
+"Erst heute abend.", versetzte Azzuro.
+
+"Hauptsache,  sie  faellt."  Polly  grinste  und  ihre  Wangen  bekamen  einen
+maedchenhaft rosigen Schimmer. "Aeh, nicht herein, meine ich natuerlich."
+
+"Natuerlich.", stimmte Azzuro zu und schaffte es, voellig ernst zu bleiben.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite14 b/d/seher/haeuser/special/special/rom_jul/seite14
new file mode 100644
index 0000000..b26ca1c
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite14
@@ -0,0 +1,93 @@
+DER ZWEITE AKT
+	  DIE SECHSTE SZENE
+
+In jener mythischen Zeit waren heimliche Hochzeiten nicht selten,  sie	hatten
+den Vorteil, dass das Paar nicht  zusammen  lebte.  Natuerlich	ersparte  eine
+heimliche  Hochzeit  auch  die	Kosten	fuer  ein  Fest.  Allerdings  war  die
+Hochzeitsnacht unabdingbarer Bestandteil der Ehe: Ohne Hochzeitsnacht galt die
+Ehe nicht als geschlossen.  Romeo  und	Julia  waren  eine  Ausnahme  von  der
+ueberwiegenden Mehrzahl der heimlichen Hochzeiten: Die	meisten  jungen  Paare
+verlegten die Hochzeitsnacht vor und sparten auf diese Weise sogar die bei der
+Hochzeit uebliche Spende an die Kirche.
+
+Lawrence  stand  neben	der  Tuer  seiner  kleinen  Kapelle  und   betrachtete
+nachdenklich Romeo, der den Huegel hinaufstuermte.
+
+"Er rennt, als seien alle Teufel der Hoelle hinter ihm her.  Dabei  ist  Julia
+noch gar nicht hier." Azzuro reckte gemaechlich die Fluegel.
+
+Lawrence, der wie sein grosses Vorbild, der heilige  Franziskus,  sich	nichts
+dabei dachte, mit Voegeln zu reden, drohte Azzuro mit dem Finger.  "Unberufen!
+Julia ist doch ein nettes Maedchen."
+
+"Das sind sie vor der Hochzeit alle.", stellte Azzuro fest.
+
+"Komm schon, das sagst du jetzt nur, weil deine Frau nicht hier ist." Lawrence
+grinste.
+
+"Natuerlich. Sie ist ja auch schon mit mir verheiratet."
+
+Lawrence bueckte sich und zupfte einen Regenwurm aus dem saftigen  Boden,  den
+er Azzuro reichte. "Wo ist eigentlich deine Frau?"
+
+"Bei Julia. Sie meint, ein Maedchen sollte eine Brautjungfer haben."
+
+Lawrence nickte. "Ein kluger Gedanke."
+
+Azzuro bedachte den Regenwurm mit einem langen Blick. "Ich weiss  nicht.  Was,
+wenn sie den Brautstrauss faengt?"
+
+"Das kann sie ohne Haende nicht, mein schwarzer Freund.",  troestete  Lawrence
+ihn.
+
+"Sie wird dir erhalten bleiben."
+
+"Wahrscheinlich.", stimmte Azzuro truebsinnig zu.
+
+"Nun lach  doch  mal."  Lawrence  stiess  Azzuro  freundschaftlich  an.  "Eine
+Hochzeit ist eine froehliche Angelegenheit."
+
+"Solange sie dauert. Danach kommt das Verheiratet-Sein."
+
+Romeo, der Azzuros letzte Worte gehoert hatte, laechelte  toericht.  "Das  ist
+doch das beste an der ganzen Heiraterei: Danach  kann  sie  mir  niemand  mehr
+wegnehmen."
+
+"Eben, mein Junge, eben.", versetzte Azzuro.
+
+Auch Lawrence meinte, seinem Amt als Seelsorger  gerecht  werden  zu  muessen.
+"Einen gibt es, der dir alles wegnehmen kann.",  meinte  er  und  deutete  zum
+Himmel.
+
+Romeos Blick folgte Lawrencens Finger und gewahrte eine dunkle Wolke. "Ach, so
+ein bisschen Regen wird Julia schon nicht abhalten."
+
+Tatsaechlich kam Julia den Hang herauf, gefolgt von einer keuchenden Ulo. "So,
+hier bin ich."
+
+Romeos Augen strahlten verdaechtig irre, als er sie betrachtete. "Wie  huebsch
+du aussiehst."
+
+Julia laechelte geistlos. "Wer ist denn  der  huebsche  Bursche  hier,  Bruder
+Lawrence?"
+
+Azzuro verdrehte die Augen. "Na, sieh zu, dass du fertig wirst, Lawrence,  ich
+halte es nicht mehr aus!"
+
+Ulo liess sich auf einem Ast nieder und wischte sich mit der Fluegelspitze die
+Traenen ab. "Ach, Hochzeiten sind so ruehrend!"
+
+Azzuro, immer noch niedergeschlagen, nickte. "Ja, mir kommen  auch  jedes  Mal
+die Traenen, wenn ich sehe, dass jemand lebenslaenglich bekommt."
+
+"J-U-L-I-A, ich lieb dich so!", traellerte Romeo.
+
+"Das macht er nur, um mich zu aergern.", grollte Azzuro.
+
+"Ich seh ein Licht...", sang nun auch Julia.
+
+Azzuro, der es hasste, wenn die Menschen den falschen Text aufsagten,  grinste
+niedertraechtig. "... im Hause Frankenstein.", vollendete er  die  Zeile  fuer
+Julia. Ueberlassen wir die Liebenden jetzt eine Weile sich selbst  und	ziehen
+uns diskret zurueck.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite15 b/d/seher/haeuser/special/special/rom_jul/seite15
new file mode 100644
index 0000000..d39fbac
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite15
@@ -0,0 +1,414 @@
+DER DRITTE AKT
+	  DIE ERSTE SZENE
+
+Benevolio wischte sich den Schweiss von der  Stirn  und  sah  ungluecklich  zu
+Mercutio hinueber, der mit hochrotem Gesicht neben ihm hermarschierte. "Es ist
+zu heiss,  um  hier  draussen  herumzulaufen,  Mercutio.  Ausserdem  sind  die
+Capuleten los."
+
+"Angst?", fragte Mercutio.
+
+"Nein, aber es ist zu warm fuer Pruegeleien."
+
+"Huch?" Mercutio legte Benevolio die Hand an die Stirn. "Du  bist  doch  nicht
+etwa krank? Sonst gibt es doch nichts, was dich von einer zuenftigen Pruegelei
+abhalten kann."
+
+Wieder zueckte Benevolio sein Taschentuch. "Doch, Hitze."
+
+"Ach, komm schon, ausgerechnet du willst jetzt ins Haus!"
+
+"Wieso ausgerechnet ich?"
+
+"Na, sonst beguckst du dir dein Messer und sagst: 'Walte Hugo, dass  ich  dich
+nur zum essen brauche.' Und zwar laut und vernehmlich, damit es auch ja  jeder
+Raufbold hoert und weiss, dass du auf Streit aus bist."
+
+"So schlimm bin ich doch gar nicht."
+
+Haette Mercutio ueber unsere Welt Bescheid gewusst,  so  haette  er  Benevolio
+sicherlich als Grossmacht bezeichnet, denn nur Grossmaechte sind in der  Lage,
+Waffen in Sichtweite anderer  Grossmaechte  aufzustellen  und  mit  unbewegtem
+Gesicht zu sagen: "Wir brauchen diese Waffen eigentlich gar nicht, weil wir ja
+friedlich sind, wir wollen sie euch nur zeigen, weil sie so huebsch sind."
+
+"Nein, natuerlich nicht. Erinnerst du dich noch an den Kerl, den  du  verhauen
+hast, weil er an deiner Rose roch?"
+
+"Das musste ich doch! Rose ist ein anstaendiges Maedchen."
+
+"Ich meine nicht die Rose in deinem Bett, sondern die in eurem Vorgarten."
+
+"Hmpf.", machte Benevolio.
+
+In Mercutios Augen tanzten kleine Teufelchen, als er seinen  Blick  ueber  den
+Platz schweifen liess. "Guck mal, da ist Tybalt."
+
+"Uh-oh."
+
+Tybalt trat auf die  beiden  zu  und  grinste  hoehnisch,  als	er  Benevolios
+schweissueberstroemtes Gesicht sah. "Wie sagte meine Grossmutter so  treffend:
+'Schlechtes Fleisch schaeumt.'"
+
+Benevolio wandte sich zum Gehen. "Komm, Mercutio, hier heult ein Wolf den Mond
+an, das ist mir zu laut."
+
+"Mitnichten, Benevolio, wenn der gute Tybalt etwas von uns will, soll er  fuer
+uns tanzen."
+
+"Was fuer eine praechtige Hochzeitsfeier.", kommentierte Azzuro bitter.
+
+"So sind die Menschen." Ulo war nachsichtig und milde gestimmt.
+
+Mercutio fischte in seiner  Hosentasche  nach  einer  Muenze,  die  er	Tybalt
+zuwarf. "Mach Faxen, Alter."
+
+"Faxen kannst du haben!" Nun schaeumte auch Tybalt.
+
+Benevolio hob die Hand. "Beherrscht euch. Mitten auf der Strasse  zu  kaempfen
+ist aeusserst unfein. Da gehoert doch  eine  Wiese  im	Morgengrauen  und  ein
+stilvoller Nebel dazu."
+
+"Ach, Nebel." Mercutio wollte sich so leicht nicht ablenken lassen.  "Ich  hau
+Tybalt auch ohne Nebel meinen Saebel um die Ohren."
+
+"Hier guckt dir jeder zu, Mercutio."
+
+"Na, vielleicht lernen sie ja noch was. Genau wie ich Tybalt lehren werde."
+
+Romeo betrat den Platz mit einem unirdischen Strahlen  auf  dem  Gesicht.  Sie
+wissen schon: Das Strahlen, das man bei Menschen sehen kann,  die  gerade  ein
+besonders exotisches Pilzragout gegessen haben und feststellen, dass es  ihnen
+wider Erwarten gut - allzu gut - bekommt. "Guten  Tag,  ihr  lieben,  goldigen
+Menschen!"
+
+Tybalt bedachte Romeo mit einem  Blick,  der  fuer  gewoehnlich  seltenen  und
+abstossenden Kaefern vorbehalten bleibt. "Lieb?"
+
+Auch Mercutio sah seinen Freund interessiert an, allerdings glich  sein  Blick
+mehr dem eines Biologen. "Lieb. Hat er gesagt. Und goldig."
+
+Azzuro schuettelte sich. "Warte nur ab, bis ihm  klar  wird,  dass  ihn  Julia
+jetzt am Haken hat. Dann ist Schluss mit lustig."
+
+Ulo stiess ihren Gefaehrten an. "Auch wenn du es nicht fuer moeglich  haeltst,
+er ist gluecklich."
+
+"Noch."
+
+Tybalt ueberwand seine Verwirrung als erster und gab sich alle	Muehe,	seiner
+Aufgabe als Schurke dieser Geschichte gerecht zu werden. "Hund, elender!"
+
+Romeo, immer noch mit dem seligen Gesichtsausdruck, laechelte Tybalt an. "Ach,
+Tybalt, du ahnst ja nicht, wie gern ich dich habe."
+
+Ulo zog die Brauen hoch. "Auf einmal?"
+
+"Familienpflichten." Azzuro kannte diese Sitte, die  verhindern  sollte,  dass
+sich  Gefaehrten  eines  Rudels  an   die   Kehle   gehen,   auch   wenn   die
+Rudelangehoerigen noch so widerlich sind. Formulieren wir es anders: Sie haben
+schon als kleines Kind gelernt, dass Sie  Ihre	Tante  auch  dann  nicht  vors
+Schienbein treten duerfen, wenn sie Ihnen noch	so  oft  unerwuenschte	Kuesse
+verpasst. Viele Menschen zahlen teure Sitzungen bei Psychotherapeuten um ihrer
+uebertragenen Tante endlich einmal - ohne mit schaerfsten Sanktionen belegt zu
+werden - sagen zu koennen, dass sie nicht gekuesst werden wollen.
+
+Tybalt, der sich die Gelegenheit nicht entgehen lassen wollte, schenkte  Romeo
+ein zuckersuesses Laecheln. "Du hast wohl das  Ufer  gewechselt?  Willst  mich
+beleidigen, indem du mir unterstellst, ich..." Er  brach  ab  und  streichelte
+erwartungsvoll seinen Degen.
+
+"Aber nein!" Romeo, der offensichtlich Tybalts  Ziel  nicht  erkennen  wollte,
+strahlte weiterhin um die Wette. "Du kennst mich eben nicht,  Tybalt  Capulet,
+sonst wuesstest du, dass ich dich ebenso gern habe wie mich selbst."
+
+Mercutio stoehnte. "Himmel, hilf, er ist unter die  Betbrueder  gegangen!"  Er
+zog blank. "Aber wenn Romeo selbst die Beleidigung nicht  raechen  will,  dann
+lasse ich ihn nicht im Regen stehen. Zieh, Tybalt!"
+
+"Hoert auf." Romeo rang die Haende. "Ihr wisst doch, dass der Fuerst  uns  das
+verboten hat!"
+
+Tybalt schien Mercutio jetzt erst zu bemerken. "Was willst du  denn?",  fragte
+er  in	einem  Tonfall,  den  manche  Menschen	kleinen  Kindern  und	Hunden
+vorbehalten.
+
+"Dein Leben. Oder wenigstens ein ansehnliches Stueck davon."
+
+"Das musst du  dir  erst  holen!"  Tybalts  Degen  schien  ploetzlich  Fluegel
+bekommen zu haben, anders liess sich das Tempo nicht  erklaeren,  mit  dem  er
+seiner Scheide ledig wurde.
+
+Nun sind wir an der Stelle angelangt, an  der  jeder  Autor  die  Grenzen  des
+Papiers verflucht  und	sich  fragt,  ob  er  nicht  doch  lieber  Drehbuecher
+schreiben sollte. Oder die Tintenkleckserei ganz  an  den  Nagel  haengen  und
+vielleicht einer ehrbaren Arbeit nachgehen sollte, die - abgesehen  von  einem
+regelmaessigen Einkommen - noch den Ruf  der  Unstetigkeit  tilgt,  der  jeden
+Schreiber (und jede Schreiberin - natuerlich habe ich das verstanden, Ulo)  in
+die Naehe des Bahnhofsviertels rueckt. Dem Papier fehlt, trotz seiner  bereits
+sprichwoertlichen Geduld, die Faehigkeit, Ereignisse, die sich in  Bruchteilen
+von Sekunden abspielen, in angemessen kurzer Zeit wiederzugeben. Und  das  ist
+keine versteckte Beleidigung meiner LeserInnen.
+
+Stellen Sie sich also vor, sie stuenden in einer  Disko.  Vor  Ihnen  ist  die
+Tanzflaeche, die Sie nur daran erkennen, dass keine Tische darauf stehen.  Ihr
+Magen macht eigenartige  Huepfbewegungen  und  wenn  Sie  sich	konzentrieren,
+koennen  Sie  erkennen,  dass  die  Huepferei  im   Takt   zum	 Wummern   der
+Basslautsprecher erfolgt. Messerscharf folgern Sie, dass die  Musik  (?)  fuer
+die zuckenden Bewegungen Ihrer Innereien verantwortlich ist und nicht etwa das
+Chili con Carne von gestern abend. Im Stroboskoplicht,	dass  die  Tanzflaeche
+beleuchtet, zucken einzelne Glieder durcheinander:  Hier  ein  Arm,  dort  ein
+Bein, ein Stueckchen weiter eine wehende Haarmaehne und dazwischen  vielleicht
+noch ein heller Fleck, der ein Gesicht sein koennte. Die Eindruecke folgen  zu
+schnell aufeinander, als dass Sie die  Glieder	zaehlen  koennten  -  ganz  zu
+schweigen davon, dass Sie aus  Armen,  Beinen,	Haaren	und  Gesichtern  ganze
+Menschen konstruieren koennten. Also nehmen Sie die Bilder  so	hin,  wie  sie
+Ihnen geboten werden. Es bleibt der Gedanke, dass Menschen zu Musik tanzen.
+
+Oder stellen Sie sich einen Film mit den Marx-Brothers vor. Erinnern Sie sich?
+Es kam darin immer wieder zu Szenen, in denen  sich  die  Brueder  gegenseitig
+Torten in die Gesichter warfen oder Ohrfeigen  austeilten.  Schnelle  Schnitte
+anstelle von Schwenks  zeigten	die  Geschwindigkeit,  in  der	das  Geschehen
+passierte.
+
+Nachdem es also keine echte Moeglichkeit gibt, Ihnen das Tempo der  Ereignisse
+der naechsten paar Minuten auf jenem Platz in Verona zu  vermitteln,  erlauben
+Sie mir, das Papier zu zerschneiden und Ihnen die Situation in Schnippseln  zu
+praesentieren. Ganz so, als stuenden Sie daneben und  liessen  Ihre  Augen  im
+Takt  der  Ereignisse  von  einer  Person  zur	naechsten  huepfen,  ohne  die
+Geschehnisse unbedingt miteinander zu verbinden.
+
+Romeos Augen weiten sich vor Schreck. "Nein! Steckt die Dinger weg!"
+
+Benevolio, nicht weniger entsetzt als sein Freund, zueckt  den	Degen  um  die
+Waffen Tybalts und Mercutios herunterzuschlagen.
+
+Tybalt, der die Situation missversteht, fintet in  Richtung  Romeo,  verfuehrt
+damit Benevolio dazu, seinen Degen mit dem seinen zu binden.
+
+Azzuro flattert aufgeregt mit den Fluegeln. "Juch-hu!", kraeht er vergnuegt.
+
+Romeo gibt sich alle Muehe, Mercutio aus dem Gerangel  fortzuziehen,  erreicht
+allerdings nur, dass Mercutio ihm eine saftige Ohrfeige verpasst.
+
+Ulo, die Romeo nicht an seinem Hochzeitstag tot sehen will, fliegt  todesmutig
+einen Sturzangriff auf Tybalt.
+
+Tybalt schlaegt nach dem schwarzen Schemen vor seinem Gesicht und lenkt  damit
+Mercutios Aufmerksamkeit wieder auf sich.
+
+Mercutio schuettelt Romeo ab und tritt Benevolio vors Schienbein,  der	Tybalt
+Ulo ueberlaesst.
+
+Azzuro hat erfolgreich einen Stein in  die  Klauen  genommen  und  laesst  ihn
+Benevolio auf den Kopf fallen.
+
+Benevolio, von dem  Stein  zwar  nicht	hart  getroffen,  wohl	aber  aus  dem
+Gleichgewicht gebracht, laesst Mercutios Arm los.
+
+Mercutio, der sich gerade von  Benevolio  losreissen  wollte,  bekommt	zuviel
+Schwung und rempelt Romeo an.
+
+Romeo taumelt zur Seite.
+
+Tybalt wittert Morgenluft, macht einen schnellen Ausfallschritt  und  versenkt
+seinen Degen in Mercutios Brust.
+
+Als Tybalt klar wurde, was er angerichtet hatte, nahm  er  die	Beine  in  die
+Hand.
+
+Stille.
+
+Stille!
+
+STILLE!
+
+Mercutio  tastete  mit	zitternder  Hand  nach	seiner	Schulter.   "Verdammte
+Scheisse.", sagte er in die donnernde Stille hinein. Eine weisse Taube landete
+weich neben ihm und schaute ihn so vorwurfsvoll an, dass Mercutio seine  Worte
+bedauerte. "Schon gut, ich lasse mir etwas besseres einfallen."
+
+"Damit sollte er sich besser beeilen.", raunte Azzuro Ulo zu.
+
+"Ist aber auch eine Schande, dass den Menschen  so  wenig  Zeit  bleibt,  sich
+wuerdevolle letzte Worte auszudenken." Ulo schniefte leise.
+
+Ein Leuchten huschte ueber Mercutios Gesicht, als ihm eine brauchbare Idee kam.
+
+"Ach, Romeo, waerest du nicht zwischen diesen Ba... Elenden und mich getreten,
+ich  haette  mich  nicht  zurueckhalten  muessen  und  sicherlich   den   Sieg
+davongetragen!", stoehnte er angemessen feierlich.
+
+"Er traegt zu dick auf." Azzuro bekam den mordluesternen Blick, den man  sonst
+bei Kritikern beobachtet.
+
+Romeo, der aussergewoehnlich schnell erkannte, was  von  ihm  erwartet	wurde,
+fiel neben seinem Freund auf die Knie. "Ich habs doch nur gut gemeint!"
+
+Azzuro oeffnete den Schnabel, doch die Taube warf  ihm	einen  Blick  zu,  der
+selbst einen Klatschspalten-Redakteur zum Schweigen gebracht haette.
+
+"...", sagte Mercutio und sein Kopf sank zur Seite.
+
+Romeo schluckte. "Ist er...?"
+
+Ulo liess sich auf seiner Schulter nieder und rieb sanft  den  Schnabel  ueber
+seine Wange.
+
+"Ja."
+
+"Und nur, weil er nicht wollte, dass ich beleidigt werde. Dabei wusste er doch
+gar nicht, warum ich Tybalt in Ruhe liess." Er seufzte. "Ich liebe Julia, aber
+um  ihretwillen  habe  ich   zugelassen,   dass   Mercutio   fuer   mich   die
+Unverschaemtheiten Tybalts bezahlte. Und jetzt hat er dafuer bezahlt. Ich habs
+doch nur gut gemeint." Verzweifelt schuettelte er den Kopf. "Nur gut gemeint."
+
+Die weisse Taube legte den Kopf schief. Da Mercutio nun fuer die Menschen  der
+Mythenwelt tot war, durfte er das Ende der Geschichte  eigentlich  nicht  mehr
+mit ansehen, doch so wie ein Schauspieler den Rest der Vorstellung  beobachten
+und schliesslich mit seinen Kollegen gemeinsam vor dem Vorhang den Applaus des
+Publikums in Empfang nehmen darf, blieb die Taube zurueck  und	gesellte  sich
+Ihnen und mir zu, die wir das Stueck beobachten,  ohne	selbst	eingreifen  zu
+koennen. Die Taube ist fuer die Goetter der Mythenwelt, was die Diskette  fuer
+den Computer  ist:  Ein  Speicher,  der  enthaelt,  was  erst  spaeter	wieder
+gebraucht wird. Obwohl CD-ROM wahrscheinlich ein besseres Wort waere, denn bei
+der naechsten Auffuehrung dieser Geschichte wird Mercutio wieder ganz der Alte
+sein, er wird nichts gelernt haben, keine Veraenderung erfahren.
+
+Benevolio beruehrte Romeo an der Schulter. "Tybalt."
+
+Romeo sprang auf wie von der Tarantel gestochen. "Na warte, der soll bezahlen!
+Er laeuft hier herum,  als  waere  nichts  passiert,  waehrend	Mercutio,  der
+immerhin mit unserem Fuersten verwandt ist..." Er brach ab und zueckte  seinen
+Degen. "Komm her, Tybalt, jetzt sollst du sehen, was  du  davon  hast,  meinen
+Freund umzubringen!" Halten wir Romeo zugute, dass er im  Affekt  handelt  und
+keine boese Absicht hinter seiner  Entscheidung  zur  Lynchjustiz  steckt.  Er
+ficht mit Tybalt und wir wollen uns eine  genaue  Beschreibung	der  einzelnen
+Schritte, Hiebe und Stiche ersparen - nicht nur, weil ich vom  Fechten	nichts
+verstehe, sondern auch, weil ich diejenigen unter Ihnen, die  davon  ebenfalls
+keine Ahnung haben, nicht langweilen moechte. Erzaehlen wir also die Szene  so
+kurz als moeglich: Romeo und Tybalt  fechten  miteinander  und	Tybalts  Taube
+gesellt sich zu Mercutios.
+
+Fragen Sie mich jetzt nicht, warum die Tauben die Koepfe  zusammenstecken,  es
+hat jedenfalls mit unserer Geschichte nichts zu tun. Hoffentlich.
+
+Benevolio sah das  Verhaengnis	in  Gestalt  aufgebrachter  Polizisten	nahen.
+"Mach, dass du wegkommst, Romeo!"
+
+Romeo, der noch versuchte, seinen Degen aus Tybalts Leiche zu ziehen, fluchte.
+"Hilf mir doch bei dem Ding."
+
+Benevolio schuettelte den Kopf. "Los, du hast keine Zeit  zu  verlieren.  Wenn
+die Polizei dich erwischt, hast du es genauso hinter dir wie die beiden hier."
+
+Romeo, der die ganze Tragweite der Geschehnisse jetzt erst begriff, nickte und
+rannte los.
+
+Keine Seuche kann  sich  schneller  ausbreiten	als  die  Nachricht,  dass  es
+irgendwo eine Leiche zu sehen gibt, doch  selbst  die  Geschwindigkeit	dieser
+Nachricht ist nichts gegen das Tempo, mit dem Menschen herbeihasten, die einen
+leibhaftigen Moerder sehen wollen. Nicht anders war es auch jetzt: Die Buerger
+Veronas, sensationsluestern und blutgierig wie alle Menschen, flogen  geradezu
+auf den Platz. Aus dem vielstimmigen Geschrei und dem gierigen Glanz der Augen
+liess sich ohne weiteres  die  Stimmung  der  Menge  in  einen	einzigen  Satz
+kristallisieren: "Wo ist Tybalt, der Moerder?"
+
+Benevolio trat unbehaglich von einem Fuss auf den anderen, wie es die  meisten
+Leute taeten, die sich unversehens mit einem wuetigen Mob konfrontiert	sehen.
+Er deutete auf Tybalts Leiche. "Hier."
+
+Enttaeuschung breitete sich auf den Gesichtern aus. Fuer Benevolio  schien  es
+jetzt unangenehm zu werden - er kannte so  gut	wie  jeder  andere  den  alten
+Spruch, dass der schrecklichste aller Schrecken  ein  Menschenmob  ohne  Opfer
+ist. Zu seinem grossen Glueck kamen jetzt Escalus und die Haeupter der Haeuser
+Capulet und Montague zum Platz, die von  ihrer	Honoratiorenwuerde  nur  wenig
+laenger zurueckgehalten worden waren als die anderen Buerger.
+
+Escalus schob sich durch die Menge wie ein Eisbrecher durch das  Nordmeer  und
+baute sich vor Benevolio auf. "Also?"
+
+Benevolio wagte kaum zu atmen. Jeder auf dem Platz schien den Atem anzuhalten,
+selbst Ulo und Azzuro hatte es einen  Moment  lang  die  Sprache  verschlagen.
+Schliesslich holte Benevolio Luft und dieser Atemzug entwickelte sich zu einem
+kollektiven Seufzen. Er war sich unangenehm der Tatsache bewusst,  dass  aller
+Augen auf ihm ruhten.
+
+"Aeh...", begann er.
+
+"Ja?" Escalusens Geduld glich dem Gummiband im Flugzeug eines kleinen Jungen.
+
+"Also, da war Mercutio. Und dann war da Tybalt. Und das Wetter war  so  heiss.
+Und Tybalt wollte sich pruegeln. Ja. Und jetzt ist Mercutio tot. Und Tybalt."
+
+Frau Capulet,  die  aufgrund  ihrer  Erfahrung	mit  Polly  Benevolios	Saetze
+schneller sortiert hatte, als irgend ein anderer auf dem Platz, schrie auf und
+Rachedurst verzerrte ihre Gesichtszuege  zu  einer  Fratze,  die  selbst  eine
+Medusa haette zu Stein erstarren lassen. "Tybalt! Oh, Tybalt!" Sie  warf  sich
+an Escalusens Brust und trommelte mit den Faeusten auf ihn ein. "Ein  Montague
+hat meinen Verwandten erstochen!" Sie zerraufte sich  hoechst  dramatisch  das
+Haar. "Dafuer muss der Schuldige haengen!"
+
+Escalus  wischte  die  Faeuste	der  Frau  Capulet  beiseite.  "Wer  hat  denn
+angefangen?", fragte er geduldig.
+
+Benevolio schluckte. "Tybalt. Und dann hat Romeo versucht, ihn und Mercutio zu
+trennen. Er hat sogar gesagt, dass Sie es gar nicht  gerne  saehen,  wenn  die
+beiden sich auf offener Strasse pruegelten."
+
+"Auf offener Strasse. Soso."
+
+Benevolio bemerkte, dass diese Aeusserung falsch ausgelegt werden  konnte  und
+rang die Haende. "Tybalt wollte einfach nicht hoeren  und  hat  nach  Mercutio
+gezielt. Naja, Mercutio ist nicht gerade  sanft,  wenn	man  ihn  zu  rasieren
+versucht, und hat sich	natuerlich  gewehrt.  Aber  Tybalt  war  ein  besserer
+Fechter, deshalb wollten die beiden gerade maechtig losfechten, als Romeo  sie
+zu trennen versuchte.  Noch  mal  zu  trennen  versuchte."  Seine  Rede  hatte
+Benevolio ganz atemlos gemacht und er holte tief Luft. "Mercutio stand  hinter
+Romeo und Tybalt vor ihm. Ja, und dann sticht Tybalt unter  Romeos  Arm  durch
+und erwischt Mercutio."
+
+"Und dann?"
+
+"Dann ist Tybalt weggelaufen, aber weil er Romeo nicht stehen  lassen  wollte,
+kam er zurueck und Romeo sah rot. Tja, dann haben sie gefochten und Romeo  hat
+gewonnen. Alles so schnell, dass ich nicht dazwischen gehen konnte."
+
+"Und Romeo?"
+
+"Ist weg."
+
+Escalusens Gesicht hatte jenen Ausdruck, der Erwachsenen eigen	ist,  die  den
+Streit von Kindern aus deren Aussagen nachvollziehen wollen.  Da  die  meisten
+Kinder	die  Geschehnisse,  die  zu  einem  Streit  fuehren,  nicht  unbedingt
+folgerichtig wiedergeben, fuehrt der Versuch meistens  dazu,  dass  Erwachsene
+beide Kinder gleichermassen ermahnen,  Frieden	zu  halten,  gleich,  wer  der
+Schuldige am Streit war. Dies jedoch hatte Escalus bereits versucht und er sah
+sich am Ende seiner Moeglichkeiten.  _Fussball_,  dachte  er,  _ist_  _schoen_
+_und_ _gut_, _aber_ _was_ _soll_ _ich_ _machen_, _wenn_ _sie_ _sich_  _Spikes_
+_unter_ _die_ _Schuhe_ _machen_ _und_ _behaupten_, _sie_ _waeren_ _nur_  _aus_
+_Versehen_  _dem_  _andern_  _auf_  _den_  _Fuss_  _getreten_?	_Was_	_sind_
+_eigentlich_ _Spikes_?
+
+Frau  Capulet,	die  den  nachdenklichen   Gesichtsausdruck   Escalusens   als
+Unsicherheit interpretierte, witterte  Fruehlingsluft.	"Ist  doch  vollkommen
+gleichgueltig, wer angefangen hat! Benevolio ist Romeos Freund,  da  kann  man
+wohl kaum erwarten, dass er die Wahrheit aussagt."
+
+Escalus war sich der Befangenheit seines Zeugen durchaus bewusst, andererseits
+wusste er nur zu genau, dass ein Gehenkter niemals  ausreicht,	den  Blutdurst
+des Mobs zu stillen. Er  hob  demonstrativ  die  Arme.	"Tybalt  hat  Mercutio
+erstochen, bevor er selbst erstochen wurde. Mir scheint, liebe	Frau  Capulet,
+dass Romeo nur dem Rachewunsch der Familie Montague Genuege getan hat."
+
+Herr Montague zauderte nicht, in die gleiche Kerbe  zu	hauen.	"Romeo  haette
+wohl die Arbeit Herrn Escalus ueberlassen sollen, aber er  hat	sicherlich  im
+Affekt gehandelt. War sozusagen nur Henker."
+
+Frau Capulet schnappte nach Luft. "Henker?"
+
+Escalus, dem die Situation zu entgleiten drohte, hob die Hand. "Schluss. Romeo
+hat meine Gerichtshoheit untergraben. Also verbanne ich  ihn.  Fuer  den  Mord
+will ich  weiter  keine  Strafe  verhaengen,  weil  ich  Tybalt  nicht	anders
+behandelt haette."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite16 b/d/seher/haeuser/special/special/rom_jul/seite16
new file mode 100644
index 0000000..471d4ba
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite16
@@ -0,0 +1,212 @@
+DER DRITTE AKT
+	DIE ZWEITE SZENE
+
+Julia sass auf dem Rasen, starrte in einen Zierteich und seufzte tief.
+
+Ulo kniff ein Auge zu. "Was sie wohl hat?", fluesterte sie Azzuro zu.
+
+"Zuviel Sonne?", vermutete Azzuro.
+
+Ein weiterer Seufzer Julias liess die  Beiden  wieder  hinunter  sehen.  Julia
+ergriff einen Stein, wog ihn nachdenklich in der Hand und warf ihn  dann  nach
+dem Spiegelbild der  Sonne  im	Teich.	"Geh  doch  weg.",  forderte  sie  die
+unnachgiebige Sonne auf.
+
+"Aber warum denn bloss?", entfuhr es Ulo lauter als geplant.
+
+Julia blinzelte. "Weil du stoerst. Wenn du untergegangen  bist,  kommt  Romeo.
+Also tu mir einmal einen Gefallen und beeil dich." Sie verlegte sich auf List.
+"Guck mal, wenn du auch nur ein bisschen schneller waerest, dann koenntest  du
+dich viel schneller ausruhen und vielleicht noch eine Weile lesen, oder so."
+
+Azzuro stoehnte. "Als ob die Sonne da einen Einfluss drauf haette. Und welches
+Buch sollte sie wohl lesen? Verbrennt doch sofort." Er  schuettelte  den  Kopf
+ueber soviel Unvernunft.
+
+"Sie weiss es ja nicht besser." Ulo war bereit, Julia die Ignoranz ihrer  Zeit
+nicht anzulasten.
+
+Julia schaute nach Westen. "Ich wuesste zu gerne, was  du  am  liebsten  isst,
+Nacht,	dann  koennte  ich  dich  anlocken  und  du  kaemest  schneller."  Sie
+laechelte. "Und mit dir kommt Romeo."
+
+"Und ob er  kommt."  Azzuro  versuchte  ein  Hecheln  und  wartete  auf  einen
+Rippenstoss von Ulo. Als dieser ausblieb,  blickte  er	verbluefft  auf  seine
+Gefaehrtin.  Ulo  schaute  angestrengt	nach  Westen  und   schien   aehnliche
+Ueberlegungen anzustellen wie Julia.
+
+Ein weiterer Stein wanderte in Julias Hand und ahnte nichts von dem Schicksal,
+das ihn erwartete: Er flog seinem Bruder nach in den Teich. "Da, so werfe  ich
+dich weg, Jungfraeulichkeit."
+
+Azzuro kicherte. "Wenn doch alle Frauen..."
+
+Ulo schnaubte.
+
+"Fuer meinen Romeo..." Julia brach ab und ein gieriger Ausdruck trat  auf  ihr
+Gesicht. "MEIN Romeo, wie das klingt!"
+
+Waehrend Ulo verstaendnisvoll nickte, wand Azzuro sich bei Julias Worten.
+
+Julia schaute hoffnungsvoll zum Himmel, als dieser dunkler  wurde  und	drohte
+dann einer vorwitzigen Wolke mit dem Finger. "Willst mich reinlegen, wie?  Als
+koenntest du eine Nacht sein." Sie zog die Nase  kraus  und  liess  sich  dann
+hintenueber fallen. "Allerdings siehst du fast aus wie mein Romeo."
+
+Azzuro, der keinerlei Aehnlichkeit feststellen konnte, sah Polly, die so rasch
+ihre alten Beine sie tragen wollten, zu Julia gelaufen	kam.  In  ihren  Armen
+trug sie ein Buendel, das sich beim  Naeherkommen  als	eine  aufgerollte  und
+hoffnungslos ineinander verknotete Strickleiter entpuppte.
+
+Julia, die das Schnaufen Pollys hoerte, noch ehe sie  ihre  Amme  sah,	sprang
+auf. "Polly! Hast du die Leiter?"
+
+"Leiter, jajaja, die Leiter hab ich. Sofern man diesen Seilknubbel  noch  eine
+Leiter nennen kann." Polly liess  die  Leiter  fallen  und  wischte  sich  den
+Schweiss von der Stirn.
+
+Julia betrachtete Polly  kritisch.  "Du  siehst  aus,  als  haettest  du  eine
+schlechte Nachricht."
+
+"Du liebe Guete, ja, allerdings eine schlechte Nachricht.  Weil  er  tot  ist.
+Ach, jetzt ist alles vorbei und wir koennen uns gerade so gut  aufhaengen  mit
+dieser unseligen Leiter." Sie rang die Haende. "Weil er naemlich tot  ist  und
+nie wieder kommt."
+
+Julia erbleichte und schluckte. "Was?"
+
+"Ich sag doch, er ist tot."
+
+"Das kann doch nicht wahr sein." Julia schuettelte den Kopf.
+
+"Doch, allerdings ist es  wahr.  Das  haette  ich  Romeo  nun  wirklich  nicht
+zugetraut!"
+
+Die Blaesse in Julias Gesicht wich hektischen Flecken. "Der Teufel  soll  dich
+holen, Polly!", schimpfte sie.
+
+"Was, mich auch noch?" Polly war entsetzt.  Sie  wusste  zwar,  dass  sie  mit
+einiger Sicherheit nicht das Paradies sehen wuerde, doch so  schnell  gedachte
+sie nicht ihren Platz zu raeumen.
+
+"Schlechte Nachrichten  kann  man  nicht  pruegeln,  wohl  aber  den  Boten.",
+kommentierte Ulo.
+
+"Ich wollte, du bekaemest einen einzigen Satz zusammenhaengend heraus, Polly."
+Julias Geduld hatte sich  verabschiedet  und  sich  zu	James  in  die	Kueche
+begeben, wo sie gemeinsam am  Sherry  naschten.  "Also,  da  du  einer  klaren
+Aussage nicht faehig bist: Lebt Romeo? Und wehe, du  sagst  nicht  einfach  Ja
+oder Nein!" Ihr Blick wanderte zu den Seilen und von dort zu Polly.
+
+Polly, die ahnte, was  Julias  Blick  bedeutete,  holte  Luft.	"Ich  hab  ihn
+gesehen, das ganze Hemd voll Blut. Und das ist doch  so  schwer  auszuwaschen.
+Herrjemineh, er sah so traurig aus, blass und voller Blut wie er war." Traurig
+sah sie Julia an und hoffte, ein wenig Mitleid zu erregen.  "Ich  konnte  mich
+nicht mehr auf den Beinen halten."
+
+Julia wurde wieder bleich. "Dann ist wirklich alles aus."
+
+Polly zerdrueckte ein paar Traenen mit ihrem voluminoesen  Taschentuch.  "Ach,
+ich mochte ihn so gern, weil er doch immer hoeflich und freundlich war.  Armer
+Tybalt, jetzt bist du tot und kannst nie mehr nett sein."
+
+Als Julia  daemmerte,  was  Polly  sagte,  atmete  tief  ein  und  fluchte  so
+ausdauernd, dass Azzuro sie staunend anblickte. "Ich ahnte ja nicht, dass  sie
+solche Worte kennt!", murmelte er anerkennend.
+
+Auch Polly war offensichtlich entsetzt. "Kind, wie kannst du nur!"
+
+Julia hingegen war laengst jenseits allen gesitteten Benehmens. "Wenn du jetzt
+nicht augenblicklich sagst, wer tot ist, mein Lieblingsvetter Tybalt oder mein
+Herzliebster Romeo, dann bringe ich dich um!"
+
+Polly zweifelte nicht daran, dass Julia genau das tun wuerde. "Tybalt ist  tot
+und Romeo hat ihn umgebracht. Romeo ist verbannt."
+
+"Sieh an, was eine gezielte Drohung nicht alles erreichen  kann."  Ulo  haette
+Julia applaudiert, wenn ihre Fluegel  das  zugelassen  haetten.  "Sogar  Polly
+bekommt eine klare Aussage zustande."
+
+Gestatten Sie mir, liebe Leserinnen und Leser, an dieser Stelle  einen	Moment
+zu verweilen. Beweist die Geschichte nicht an dieser  Stelle,  dass  auch  die
+Goetter der Mythen einen gewissen Sinn fuer Humor haben? Doch laenger will ich
+Sie nicht aufhalten, schon sehen Ulo und Azzuro mich wuetend an, weil ich  das
+Band angehalten habe und den Anblick einer verwirrten  Polly  ebenso  geniesse
+wie den der innerlich zerrissenen Julia.
+
+"Sie sitzt wirklich in der Patsche, nicht?" Ulo  konnte  sich  ein  haemisches
+Federschuetteln nicht verkneifen.
+
+Julia rannte verzweifelt um den Teich herum. "Was soll  ich  nun  denken?  Wie
+konnte Romeo das nur tun? Hat er zwei Gesichter? Hat er mich angelogen? Wollte
+er nicht seine ganze Familie aufgeben? Hat er mich so hintergangen? Hat er  am
+Ende nur gesagt, er liebe mich, um mich und meine Familie damit zu  kraenken?"
+Sie drehte eine weitere Runde um den Teich.
+
+"Maenner!", fluchte Polly mit Elan. "So sind  sie  doch  alle:  Hinterlistige,
+heuchlerische, halbseidene Hazardeure!" Sie kramte in  ihrer  Handtasche  nach
+einer flachen Flasche. "Da hilft nur noch Medizin."
+
+Azzuro, der den Geruch von Pollys Medizin genuesslich einsog, haette ihr sogar
+die Schmaehung aller Maenner vergeben, wenn  sie  ihm  nur  ein  paar  Tropfen
+abgegeben haette.
+
+"Nichts da!" Julia schlug Polly die Flasche aus der Hand. "Im  Hals  soll  dir
+das Zeug stecken bleiben, weil du Romeo so etwas nachsagst."
+
+Bedauernd schauten Azzuro und Polly  auf  die  Flasche,  die  ihren  kostbaren
+medizinischen Inhalt an ein paar Wuermer  verteilte.  "Du  kannst  doch  nicht
+sagen, dass der Mann, der  deinen  Vetter  umgebracht  hat,  ein  anstaendiger
+ist.", meinte Polly verstaendnislos.
+
+"Immerhin ist er mein Ehemann.", wies Julia ihre Amme zurecht. "Und  wenn  ich
+mich nicht sehr taeusche, wird Romeo schon  einen  Grund  gehabt  haben."  Sie
+ueberlegte und ihr Gesicht hellte sich auf. "Natuerlich, so  wird  es  gewesen
+sein! Notwehr! Tybalt wollte meinen Romeo umbringen und  Romeo	hat  sich  nur
+gewehrt." Befriedigt ueber diese Lesart hob Julia die Flasche wieder  auf  und
+reichte sie Polly, die den Rest der Medizin in sich hineinschuettete.
+
+Julia sah nachdenklich auf Azzuro, der sich mit den  Wuermern  um  die	kleine
+Pfuetze stritt. "Viel schlimmer ist eine  andere  Sache.  Romeo  ist  verbannt
+worden. Jetzt bin ich kaum drei Stunden verheiratet, aber meine Hochzeitsnacht
+bekomme ich nicht."
+
+Ulo konnte Julias Gesichtsausdruck nicht deuten. "So schlimm ist das nun  auch
+wieder nicht."
+
+"Doch." Azzuro wusste wohl,  dass  Julia  weniger  darunter  litt,  dass  ihre
+Eheschliessung jetzt nichtig zu sein drohte. "Sie hat Ovid gelesen."
+
+"Metamorphosen?", fragte Ulo.
+
+"Ars amandi."
+
+"Oh." Ulo war erstaunt.
+
+"Wo sind eigentlich meine Eltern?", fragte Julia.
+
+"Sie halten bei Tybalt die Totenwache. Ich kann  dich  hinbringen,  allerdings
+solltest du dir vorher ein frisches Taschentuch holen."
+
+"Nein, es reicht, wenn  sie  Heulen  und  Zaehneknirschen  demonstrieren,  ich
+brauche  da  nicht  mitzumachen.  Ausserdem  muesste  ich  mich  zur   Haelfte
+verstellen." Julia straffte die Schultern. "Ich gehe auf mein Zimmer und warte
+auf mein Ende."
+
+"Dafuer bist du noch zu jung, Kind. Es wird wohl noch eine Weile dauern."
+
+"Wenn Romeo meine Jungfraeulichkeit nicht bekommt, dann auch kein anderer  als
+Tod, der ohnehin am Ende alles bekommt."
+
+"HM?", fragte eine dumpfe Stimme aus dem Schatten der Gartenmauer.
+
+"Nicht jetzt." Ulo scheuchte Tod davon.
+
+Polly, die Julias Blaesse missverstand und als Suizidabsicht  deutete,	ueber-
+legte angestrengt. "Dann gibt es wohl keine andere Moeglichkeit, als dass ich
+Romeo herbeischaffe. Er hat sich bei Bruder Lawrence versteckt."
+
+"Warum hast du das nicht eher gesagt?" Julia riss sich den  Ring  vom  Finger,
+den sie gerade erst von Romeo bekommen hatte. "Bring ihm den Ring und sag ihm,
+dass er ihn mir wiedergeben soll."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite17 b/d/seher/haeuser/special/special/rom_jul/seite17
new file mode 100644
index 0000000..7dd816e
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite17
@@ -0,0 +1,150 @@
+DER DRITTE AKT
+	DIE DRITTE SZENE
+
+Bruder Lawrence, der nicht weniger neugierig war als andere,  hatte  sich  auf
+dem Marktplatz herumgetrieben, bis er von Romeos Verbannung hoerte. Nun rannte
+er, so schnell ihn seine Beine tragen wollten zu seiner Kapelle  zurueck.  Als
+er sie voellig ausser Atem erreichte, fand er Romeo, der das  Gesicht  in  den
+Haenden vergraben hielt.
+
+"Romeo!" Lawrence bueckte sich stoehnend zu seinem Freund  herunter.  "Hast...
+bist... wirst...?" Er schnaufte.
+
+Romeo sah ihn mit trueben Augen an. "Dreimal ja."
+
+Verwirrt runzelte Lawrence die Stirn.
+
+Ein duennes Laecheln huschte ueber Romeos Gesicht, sah seine gefurchte	Stirn,
+betrachtete die rastlos knetenden Haende und machte sich von dannen. "Ich habe
+Tybalt erstochen, ich bin geflohen  und  ich  werde  mich  aufhaengen  lassen.
+Dreimal ja, also."
+
+"Wer redet denn hier von Aufhaengen?"
+
+"Ich werde  nicht  davonkommen,  weisst  du.  Escalus  hat  ziemlich  deutlich
+gemacht, was er von Randale in den Strassen haelt."
+
+"Doch, doch.", beruhigte Bruder Lawrence ihn "Du kommst schon  davon.  Escalus
+war der Ansicht, dass er Tybalt ohnehin haette haengen muessen, weil er vorher
+Mercutio umgebracht hat. Jetzt verurteilt er dich nur zur Verbannung, weil  du
+ihm vorgegriffen hast."
+
+Romeos Augen weiteten sich. "Verbannung? Das ist ja noch schlimmer!"
+
+"Unsinn. Wenigstens bleibst du am Leben."
+
+"Und was fuer ein wunderbares Leben das ist." Romeo knirschte vor Verbitterung
+mit den Zaehnen. "Ich muss Verona verlassen und Julia bleibt hier."
+
+"Wenn schon? Du lebst, sie lebt: Was willst du mehr?"
+
+"Bei ihr sein."
+
+"Sei nicht undankbar, Junge." So war Romeo schon lange nicht mehr  von  Bruder
+Lawrence bezeichnet worden. "Selbst wenn du Verona verlassen  musst,  bist  du
+immer noch jung genug. Du lebst und kannst in Ruhe abwarten, was  sich	weiter
+ergibt. Vielleicht kannst du eines Tages ja zurueckkommen."
+
+"Eher ist die Erde eine Scheibe."
+
+Bruder Lawrence, der, fest im  Dogma  verankert,  solch  ketzerische  Gedanken
+nicht von seinem Schuetzling hoeren wollte, warf verzweifelt die Haende in die
+Luft. "Das ist sie."
+
+Romeo lachte gackernd. "Natuerlich."
+
+"Romeo, reiss dich zusammen. Es ist noch nicht aller Tage Abend."
+
+"Doch, das ist es. Ohne Julia ist immer Nacht, ohne sie gibt es keine Sonne."
+
+"Ulo, reich mir einen Lappen, Romeo trieft.", fluesterte Azzuro.
+
+"Vor Traenen?"
+
+"Vor Schmalz."
+
+Ulo beachtete ihn nicht weiter, sondern rieb sich den Schnabel an einem Blatt.
+
+Auch Romeo wischte sich die Traenen ab. "Verbannung ist deshalb schlimmer  als
+Haengen, weil ich dann jeden Tag daran	denken	muss,  dass  Julia  hier  ist.
+Selbst ihr Schosshund ist besser dran als ich, weil er	in  ihrer  Naehe  sein
+kann. Das ist ein Baerendienst,  den  Escalus  und  du	mir  erweisen,	Bruder
+Lawrence."
+
+"Ich?" Lawrence war ehrlich entsetzt.
+
+"Du bringst mir die Nachricht, oder?"
+
+Ein Klopfen bewahrte Bruder Lawrence vor den Vorwuerfen, die  Romeo  noch  auf
+den Lippen hatte.
+
+"Geh nach hinten, Romeo, ich will nachsehen, wer da ist."
+
+Romeo hatte sich auf dem Boden zusammengerollt und schluchzte still  vor  sich
+hin.
+
+"Romeo!", zischte Bruder Lawrence. Er wandte  sich  zur  Tuer.  "Einen  Moment
+noch!" Verzweifelt blickte er zwischen der Tuer und Romeo hin  und  her.  "Wer
+ist denn da?"
+
+Polly trat wuetend gegen die Tuer. "Ich bringe eine Nachricht von Julia."
+
+Romeo schaute auf.
+
+Lawrence oeffnete die Tuer und verdeckte mit seiner Leibesfuelle Romeo. Als er
+Polly erkannte, seufzte er. "Was gibt es?"
+
+"Erst muss ich wissen, wo Romeo ist."
+
+Lawrence trat zur Seite. "Sammeln Sie ihn auf.", schlug er vor und deutete auf
+das Haeufchen Elend zu seinen Fuessen.
+
+Polly schuettelte den Kopf. "Sowas Dummes aber auch! Jetzt stecken  Julia  und
+Sie im gleichen Schlamassel. Aber das ist vergossene Milch, jetzt kneifen  Sie
+den A... Reissen Sie sich zusammen, meine ich natuerlich."
+
+Romeo versuchte es und scheiterte knapp.
+
+"So sieht ein braver Romeo aus.", lobte Polly. "Es ist noch nicht  aller  Tage
+Abend, mein Junge."
+
+"Meine Rede.", fuegte Lawrence laechelnd hinzu.
+
+"Was macht Julia?", wollte Romeo wissen.
+
+"Was soll sie schon tun? Sie weint und verflucht Sie und dann weint sie wieder
+und..."
+
+"Ich kann es ihr nicht verdenken. Aber wenn selbst sie mich  verflucht..."  Er
+brach ab und riss sein Messer heraus. "Dann sag ihr, dass ich mit ihrem  Namen
+auf den Lippen sterben will!"
+
+Polly ohrfeigte ihn, das Messer flog in eine Ecke und Romeo in die andere.
+
+Bruder Lawrence, dessen Ueberzeugung ihm das gleiche Argument  verbot,	setzte
+zu einer Predigt an. "Du bist wirklich ein Volltrottel, Romeo. Erst ueberlebst
+du, obwohl Tybalt wahrscheinlich der bessere Fechter ist. Da hast du  Glueck."
+Er schlug Romeo das Wort um die Ohren. "Escalus bestraft dich nicht fuer Mord,
+sondern fuer Selbstjustiz. Da hast du Glueck. Julia trauert um	ihren  Vetter,
+aber Polly ist bestimmt nicht hier, um dir zu sagen, dass sie dich nicht  mehr
+sehen will. Da hast du Glueck."
+
+Polly nickte. "Allerdings. Hier ist dein Ring, Romeo."
+
+"Julia gibt ihn mir zurueck?" Romeo begann laut zu jammern.
+
+"Du sollst ihn ihr noch einmal geben, Trottel."  Lawrence  schlug  die  Haende
+zusammen ueber soviel Unverstand. "Morgen reist du ab und  wartest  in..."  Er
+ueberlegte. "Sagen wir, Mantua, bis Polly und  ich  Gelegenheit  haben,  allen
+Leuten von Julias und deiner Ehe zu erzaehlen. Dann kommst du zurueck und wenn
+ihr nicht sterbt, lebt ihr gluecklich bis zu eurem Ende." Er  wandte  sich  an
+Polly. "Geh zu Julia und sag ihr, dass Romeo kommt.  Er  weiss  es  zwar  noch
+nicht, aber er kommt." Wieder fiel sein Blick auf Romeo. "So, jetzt  hoer  mir
+mal zu, Junge..."
+
+Azzuro stiess Ulo an. "Komm, das Gewitter will ich nicht miterleben."
+
+"Welches Gewitter?", fragte Lawrence scheinheilig.
+
+Die Turtelkraehen machten sich ebenso schnell aus dem Staub wie Polly.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite18 b/d/seher/haeuser/special/special/rom_jul/seite18
new file mode 100644
index 0000000..bbe686f
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite18
@@ -0,0 +1,53 @@
+DER DRITTE AKT
+	DIE VIERTE SZENE
+
+Capulet und Paris sassen sich bei einem Glas  Wein  gegenueber.  "Es  tut  mir
+wirklich leid, dass Julia heute abend nicht mehr zu uns kommt.	Sie  ist  sehr
+aufgebracht." Er wiegte den Kopf.  "Ich  hatte  noch  keine  Gelegenheit,  ihr
+klarzumachen, dass sie dich heiraten wird."
+
+"Er klingt, als sei ihm Tybalts Tod voellig egal.", stellte Ulo fest.
+
+"Er ist ein homo politicus."
+
+"Solche Worte sagst du  nicht  in  meinem  Beisein,  Azzuro!",  verlangte  Ulo
+entruestet.
+
+Azzuro schnappte nach Luft, schwieg aber.
+
+"Unglueckliche Umstaende sind das.", beklagte sich Paris.  "Ich  haette  gerne
+ein bisschen Zeit mit ihr verbracht."
+
+Frau Capulet laechelte. "Keine Sorge, ich werde morgen frueh mit ihr reden und
+Julia wird mir sagen, dass sie sich auf  ihre  Hochzeit  freut."  Ihr  Gesicht
+liess keinen Zweifel daran, dass Julia sich lieber freuen sollte. "Heute abend
+waere es sicherlich ein wenig..." Sie zoegerte. "...unpassend, sie zu stoeren,
+aber morgen sieht alles schon wieder anders aus."
+
+Herr Capulet nickte. "Ja, morgen  sieht  alles  anders  aus.  Ich  denke,  wir
+sollten nur eine kleine Feier ausrichten, um eure Hochzeit  zu	feiern.  Waere
+dir Mittwoch recht, Paris?"
+
+"Heute ist Montag, ist das nicht ein bisschen schnell?", wandte Paris ein.
+
+"Ja, drei Trauertage sollten wir  einhalten,  schliesslich  soll  Tybalt  erst
+beerdigt sein. Also Donnerstag. Und was die Feier angeht, so werden die  Leute
+reden, wenn wir mehr als ein halbes Dutzend Gaeste einladen."
+
+"Wir koennen ja die grosse Feier spaeter ausrichten." Frau Capulet wollte sich
+die Gelegenheit, Stiche in ihrer  Bekanntschaft  auszuteilen,  nicht  entgehen
+lassen.
+
+Herr Capulet sah nach draussen. "Spaeter, ja. Und jetzt ist es so spaet,  dass
+ich fast schon wieder aufstehen muss.  Also:  Donnerstag  sehen  wir  uns  zur
+Hochzeit und Julia ueberlass getrost mir, Paris."
+
+Ulo schniefte. "Als ob Julia eine Kuh waere, die er zum Decken weggibt."
+
+"Ist sie doch.", meinte Azzuro. "In  seinen  Augen,  meine  ich.",  fuegte  er
+schnell hinzu, als er Ulos Blick auffing.
+
+"Und seine Frau macht mit."
+
+Azzuro nickte nur betruebt.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite19 b/d/seher/haeuser/special/special/rom_jul/seite19
new file mode 100644
index 0000000..e856818
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite19
@@ -0,0 +1,252 @@
+DER DRITTE AKT
+	DIE FUENFTE SZENE
+
+Julia kuschelte sich in Romeos Arme und  liess	den  Blick  durch  ihr	Zimmer
+schweifen. Bevor Romeo zu ihr kam, hatte sie eilig ihre Puppen	versteckt  und
+sogar ihre Heiligenbilder von der Wand genommen. Jetzt sah ihr Zimmer eher wie
+das einer Frau aus. Sie fuehlte sich so wohl, dass sie am liebsten  geschnurrt
+haette.
+
+Julia kitzelte Romeos Nase mit einer Straehne ihres  Haares  und  beobachtete,
+wie sie zuckte. Schlaefrig wischte Romeo sich mit der Hand darueber.
+
+Vor dem Fenster zog Ulo gerade den Kopf aus dem Gefieder und stiess Azzuro an.
+"Sie wachen auf."
+
+Azzuro murmelte und versuchte, sich die Augen mit dem Fluegel zuzuhalten.
+
+Romeo schlug die Augen auf  und  linste  aus  dem  Fenster.  "Ich  hoere  eine
+Lerche."
+
+Julia schuettelte den Kopf. "Das  war  bestimmt  eine  Nachtigall.  Guck,  die
+Kraehen da draussen schlafen auch noch, obwohl sie sonst immer mit die	ersten
+sind, die aufwachen."
+
+Zweifelnd blickte Romeo auf und begegnete Ulos Blick.  "Die  Kraehe  da  sieht
+aber ziemlich wach aus. Also war es doch die Lerche."
+
+Ulo plinkerte mit den Augen,  um  auch	die  letzten  Reste  des  Schlafes  zu
+vertreiben.
+
+"Sie hat nur getraeumt. Jetzt sind  ihre  Augen  wieder  zu  und  es  war  die
+Nachtigall."
+
+Azzuro streckte sich und klapperte mit dem Schnabel. "Da."  Romeo  zeigte  mit
+dem Finger. "Jetzt ist die andere auch noch wach." Als er sah, dass Julia  mit
+dem Kinn zitterte, streichelte er ihre Wange. "Schon gut, schon gut.  Wenn  du
+es gerne moechtest, ist es noch Nacht und die beiden da sind Fledermaeuse."
+
+"Wasss issst?", fragte Wlad.
+
+"Nichts.", versetzte Azzuro muerrisch. "Geh schlafen."
+
+Wlad zuckte die Schultern und verschwand unter der Dachrinne.
+
+Julia, die Wlad beobachtet hatte, stoehnte. "Leider geht die Fledermaus gerade
+schlafen. Wahrscheinlich ist es doch  Morgen  und  du  solltest  gehen,  sonst
+erwischen dich noch Escalusens Leute und du wirst aufgehaengt."
+
+"Es kann aber doch nicht Morgen sein. Du hast  gerade  gesagt,  dass  es  noch
+Nacht ist.", noergelte Romeo.
+
+"Und jetzt sage ich, dass es Morgen ist."
+
+Romeo murmelte etwas darueber, dass Frauen nicht wissen, was sie wollen, stand
+aber gehorsam auf und zog sich an.
+
+"Ich moechte dich nicht gehen lassen."
+
+"Ich will ja auch nicht gehen. Aber..." Er brach ab.
+
+"Eben: Aber." Julia grabbelte auf dem Nachttisch nach einem Taschentuch.
+
+"Wenn sie jetzt sagt, dass ihre Herzen immer beieinander sind, hacke  ich  ihr
+die Augen aus.", brummte Azzuro.
+
+Julia laechelte unter Traenen. "Weisst du, unsere Koerper moegen zwar getrennt
+sein, aber..."
+
+Ulo sprang auf Azzuros Ruecken und hielt ihn fest.
+
+"Eben: Aber.", versetzte Romeo.
+
+Polly steckte den Kopf zur Tuer herein. "Es wird gleich  hell.  Ausserdem  ist
+deine Mutter hierher unterwegs, Julia."
+
+Romeo, der schon auf der  Strickleiter	stand,	reckte	sich  Julia  entgegen.
+"Komm, gib mir noch einen Kuss."
+
+Julia grabschte ein hilfloses Holzpferdchen, das sich  nicht  rechtzeitig  mit
+den anderen Spielsachen im Schrank versteckt hatte und	kuesste  es  herzhaft.
+"Hier, das Pferd bekommt ganz viele  Kuesse  mit,  dann  hast  du  welche  auf
+Vorrat." Sie reichte Romeo das Pferdchen.
+
+Romeo laechelte. "Ich komme zurueck, bevor sie alle aufgebraucht sind."
+
+"Wohl hoere ich der Botschaft Wort, allein, mir  fehlt  der  Glaube.",  meinte
+Julia.
+
+"Ich hasse diese falsche Zitiererei.", knurrte Azzuro.
+
+"Noch ist Polen nicht verloren." Romeo zwinkerte Julia aufmunternd zu.
+
+"Trotzdem, etwas ist faul..."
+
+"KRAH!", schrie Azzuro mit Nachdruck.
+
+"Siehst du, jetzt ist Morgen." Romeo winkte. "Ich schreibe dir."
+
+Julia winkte, bis Romeo ueber die Gartenmauer verschwunden war, dann kletterte
+sie von der Fensterbank und wandte sich ihrer Mutter zu.
+
+Frau Capulet, die offenbar gehofft hatte, Julia noch schlaftrunken  und  wenig
+widerspenstig anzutreffen, verzog das Gesicht, als sie ihre Tochter  sah.  "Du
+bist reichlich frueh auf."
+
+"Ich konnte nicht schlafen."
+
+"Haette sie auch gar nicht gewollt." Azzuro kicherte in sich hinein.
+
+"Du trauerst um Tybalt, hm?" Frau Capulet strich Julia  ueber  das  Haar.  "Du
+solltest dich nicht so sehr graemen."
+
+"Ich habe  einen  Menschen  verloren,  der  mir  sehr  viel  bedeutet."  Julia
+schniefte.
+
+"Und ob sie das hat." Ulo wand sich vor unterdruecktem Lachen.
+
+"Du solltest nicht mehr um Tybalt weinen, das macht ihn nicht wieder lebendig.
+Eher weinte ich, weil dieser Romeo noch lebt."
+
+"Es waere gut, wenn ich Romeo gleich jetzt in meinen  Haenden  haette."  Julia
+verbarg ihr Lachen hinter einem umstaendlichen Naseputzen.
+
+"Das laesst sich leider nicht machen. Allerdings werde ich dafuer sorgen, dass
+er auch im Exil nicht lange ueberlebt, glaub mir."
+
+Julia nickte ernsthaft. "Wenn ich nur wuesste, wie ich es anstellen soll, dann
+sorgte ich dafuer, dass Romeo bald eine ewige Ruhe findet."
+
+"Wenn Julia das in die Hand nimmt, kann  Romeo  allerdings  ruhig  schlafen.",
+meinte Azzuro.
+
+"Ihre Mutter merkt aber auch gar nichts, hm?"  Ulo  lehnte  sich  zurueck  und
+genoss Julias spitze Zunge.
+
+Frau Capulet laechelte. "Das ist der rechte Geist, Julia. Wenn  du  so  weiter
+machst, wirst du die Capulets wuerdig vertreten."
+
+"ZERtreten will ich sie." Julia stampfte mit  dem  Fuss  auf.  "Meine  Feinde,
+natuerlich."
+
+"Natuerlich.", stimmte ihre Mutter zu. "Ich habe uebrigens eine gute Nachricht
+fuer dich."
+
+"Gibt es gute Nachrichten, wenn ein geliebter Mensch unerreichbar ist?"
+
+"Jetzt traegt sie zu dick auf." Azzuro schuettelte den Kopf.
+
+"Dein Vater meint, dass das Leben weitergehen muss. Er hat  beschlossen,  dass
+du Paris heiratest. Gleich am Donnerstag."
+
+Julia wurde kreidebleich. "Das ist ja  schon  uebermorgen!  Das  geht  mir  zu
+schnell."
+
+"Unsinn." Frau Capulet runzelte unwillig die Stirn. "Aber warte nur, bis  dein
+Vater das hoert."
+
+Herr Capulet kam ins Zimmer und schuettelte den Kopf. "Heulst  du  denn  immer
+noch, Julia? Hat deine Mutter dir nicht gesagt, dass du allen Grund zur Freude
+hast?"
+
+Julia schluchzte noch lauter.
+
+"Sie will Paris nicht heiraten.", klaerte Frau Capulet  ihren  Mann  auf.  "Es
+geht ihr zu schnell, sagt sie."
+
+In Herrn Capulet erwachte der Jaehzorn, den in seiner Jugend buchstaeblich die
+halbe Stadt gefuerchtet hatte.	"Was?  Paris  ist  ein  guter  Mann  und  eine
+hervorragende Partie. Und du willst nicht?" Sein Geschrei erklomm neue Hoehen.
+"Du wirst den Mann heiraten, den ich dir nenne, hast du mich  verstanden?  Und
+wenn ich dich an den Haaren zur Kirche zerre, du heiratest Paris!"
+
+"Aber, aber." Frau Capulet  beobachtete  besorgt  die  pochende  Ader  an  der
+Schlaefe ihres Mannes und sah sich dem Witwenstand naeher als  ihr  lieb  war.
+"Reg' dich nicht so auf."
+
+Julia erkannte, dass ihr Vater jenseits aller Vernunft war.  "Lass  mich  doch
+erklaeren..."
+
+"Ich soll mich nicht aufregen? Nicht aufregen?"  Er  rang  nach  Luft.  "Meine
+Tochter will, dass ich mein Wort nicht halte, zieht meinen guten Namen in  den
+Schmutz und bringt mich vor der ganzen Stadt in Verruf -  und  ich  soll  mich
+nicht AUFREGEN?"
+
+Polly, die wusste, dass er gleich anfangen wuerde  zu  fluchen,  stellte  sich
+schuetzend vor Julia. "Lasst sie doch reden."
+
+"Reden? Ihr redet mir alle drei schon zuviel. Da ist ein gutaussehender  Mann,
+erfolgreich,  nett,  der  Julia  alles	bieten	kann  und  einen  anstaendigen
+Brautpreis zahlt... Und meiner Tochter faellt nichts  besseres	ein,  als  mir
+vorzuheulen, dass sie ihn nicht will. Was meinst  du  eigentlich,  dass  diese
+Maenner auf den Baeumen wachsen?"
+
+Ulo blickte auf Azzuro und schuettelte den Kopf. "Da hat er leider Recht."
+
+Herr Capulet schuettelte den Kopf wie ein Stier, der dem roten Tuch  hinterher
+schaut. "Ich will dir mal was sagen, Julia: Entweder heiratest du  Paris  oder
+du kannst sehen wo du bleibst." Er drehte sich um und stapfte zur  Tuer.  "Ich
+dachte eigentlich, dass ich von Glueck reden  kann,  wenigstens  ein  Kind  zu
+haben, wenn schon keinen Sohn, aber wenn ich mir dich so ansehe, Julia,  frage
+ich mich, ob ich dich nicht besser gleich nach der Geburt ersaeuft haette."
+
+Julia zuckte zusammen, als die Tuer hinter ihrem Vater ins Schloss schepperte.
+"Mama?"
+
+Frau Capulet seufzte und stiess dann in das Horn ihres Mannes, wie sie es ihre
+ganze Ehe lang getan hatte. "Nein, nicht Mama. Sag, dass du  Paris  heiratest,
+dann kannst du auch wieder Mama sagen."
+
+"Wenn es das ist, was du willst, dann kannst du mich auch gleich neben  Tybalt
+begraben!"
+
+Die Tuer flog ein zweites Mal mit lautem  Knall  zu.  Julia  trat  verzweifelt
+dagegen. "Was soll ich jetzt bloss tun? Polly, hast du keine  Idee?  Ich  kann
+doch nicht Paris heiraten!"
+
+"Und warum nicht?" Polly fand es leichter, sich vom Sturm vorwaerts blasen  zu
+lassen, als sich gegen ihn zu stemmen. "Er ist bestimmt kein schlechter  Mann.
+Dein Vater hat schon Recht: Er sieht besser aus als Romeo, hat einen  besseren
+Ruf, ist nicht mit deiner Familie verfeindet und ausserdem ist	er  hier.  Was
+willst du mehr?"
+
+Julia starrte ihre alte Vertraute unglaeubig an. "Auch du, meine Amme Polly?"
+
+Azzuro stoehnte.
+
+Polly hob hilflos die Haende. "Wir wollen alle nur dein bestes."
+
+"Das bekommt ihr aber nicht.", murmelte Julia.
+
+"Was sagst du?"
+
+"Ich sagte, dass ich zu Bruder Lawrence gehen will. Wenn  ich  heiraten  soll,
+dann muss er mich auf eine wahrhaft christliche Ehe vorbereiten." Julia setzte
+ein falsches Laecheln auf.
+
+Polly traute dem ploetzlichen Frieden nicht ganz, zuckte aber  die  Schultern.
+"Das ist gut. Ich werde deiner Mutter sagen, dass du dich bessern willst." Sie
+ging und sah nicht, wie sich Julia weinend auf ihr Bett warf.
+
+
+Hier stehen sie nun, unsere Akteure.  Julia  weint  sich  die  Augen  aus  dem
+Gesicht, weil sie sich nicht offen  zu	Romeo  bekennen  darf,	Julias	Mutter
+stellt den haeuslichen Frieden	ueber  das  Glueck  ihres  Kindes,  Polly  ist
+wetterwendisch, Romeo verbannt, Paris sieht  der  Rettung  fuer  seinen  allzu
+leeren Geldbeutel entgegen und Bruder Lawrence ist in einer boesen Zwangslage.
+
+Wie gerne, liebe Leserin, lieber Leser, versaehe ich die Geschichte mit  einem
+gluecklichen Ende. Doch die Goetter der Mythenwelt haben  es  anders  bestimmt
+und wuerzen unsere Geschichte mit unendlichen Schwierigkeiten. Es scheint, als
+haetten Romeo und Julia die Goetter veraergert.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite2 b/d/seher/haeuser/special/special/rom_jul/seite2
new file mode 100644
index 0000000..7c4ffd4
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite2
@@ -0,0 +1,86 @@
+PROLOG
+Wir befinden uns in Verona. Stellen Sie sich die Stadt vor. Koennen Sie nicht?
+Gut, fangen wir am Anfang an, so, wie es sich gehoert.
+
+Eigentlich befinden wir uns in einem Zeitalter, dem  man  spaeter  irgendeinen
+gelehrten und eigenartig klingenden Namen geben wird, aber das tut hier nichts
+zur Sache, denn wie es sich fuer eine Geschichte ziemt, ist die  Zeit  ohnehin
+die Gute Alte. Das heisst nicht, dass  es  in  irgendeiner  Weise  besser  und
+schoener waere, als es heute ist, es  bedeutet	lediglich,  dass  sie  unseren
+Traeumen entspricht. Sie ist mythisch und somit jenseits aller Kritik.
+
+Stellen wir uns also Verona vor,  wie  die  Stadt  in  der  guten  alten  Zeit
+ausgesehen haben mag. Die Strassen sind sauber, so sauber, dass man von  ihnen
+essen koennte. Womit ich nicht unterstellen will, dass	Sie  zu  den  Menschen
+gehoeren, die auf solche Gedanken kommen. Im Gegensatz zu aller Ueberlieferung
+und allen historischen Erkenntnissen  sind  die  Gossen  nicht	schmutzig  und
+niemand giesst aus den Fenstern des ersten Stocks eine unaussprechliche,  aber
+im Grossen und Ganzen braune und gelbe Masse.  Und  bitteschoen,  die  Haeuser
+beugen sich nicht wie verhutzelte alte Leute ueber die Strassen und betrachten
+die Menschen mit dem arroganten Gesichtsausdruck, den ein Autor eines  anderen
+Genres ihnen andichten wuerde. Das genaue Gegenteil ist der Fall.  Die	Mauern
+sind weiss, die Fenster sauber geputzt,  die  Gardinen	frisch	gewaschen  und
+bunte Kaesten mit noch bunteren Blumen zieren die Fensterbaenke,  die  genauso
+frisch geputzt sind wie die Strassen. Ja, Sie haben richtig  gehoert:  In  der
+guten alten Zeit putzten anstaendige Menschen  sogar  die  Fensterbaenke.  Sie
+haetten wahrscheinlich auch die Daecher geputzt, wenn  ihre  Arme  lang  genug
+gewesen waeren - fragen Sie die aelteren Menschen in ihrem Dorf, wenn Sie  mir
+nicht glauben.
+
+Womit wir beim Thema Leute sind. Haben Sie  bemerkt,  wie  geschickt  ich  die
+Ueberleitung gemacht habe? Wir befinden uns in	einer  mythischen  Zeit,  also
+koennen Sie sich die Menschen sicherlich vorstellen. Keine Frau, die sich dort
+mit Problemen wie Falten und Orangenhaut herumschluege, kein Mann, dessen Bart
+nicht dicht genug waere oder dessen Muskeln zu unentwickelt. Nein, meine Damen
+und Herren, hier ist Verona. Eigentlich  sollte  ich  VERONA  schreiben.  Oder
+wenigstens _Verona_, wenn Ihnen die  Grossbuchstaben  zu  pompoes  sind.  Jede
+einzelne der Frauen verdiente sich heute  eine	goldene  Nase  als  Model,  so
+perfekt ist ihre Figur, so ebenmaessig ihr Gesicht,  so  dezent  ihr  Make-up.
+Ihre Hautfarbe ist allerdings heute nicht mehr en Vogue, aber das ist  nichts,
+dem nicht ein Nachmittag auf der Sonnenbank abhelfen koennte. Und die Maenner!
+Haben Sie eine Ausgabe von Capital zur Hand? Schauen Sie hinein und suchen Sie
+sich den attraktivsten Mann heraus. Haben Sie? Gut. Nun ziehen Sie ihm	diesen
+grausigen Anzug aus und stecken Sie ihn in eine zweifarbige Strumpfhose.  Dazu
+verpassen Sie ihm noch ein  bauschiges	Rueschenhemd.  Und  streichen  Sie  um
+Gottes Willen dieses schreckliche Handy!
+
+So also sehen die Menschen Veronas aus. Zumindest die, um welche  wir  uns  in
+dieser Geschichte kuemmern wollen. Sie fragen nach den	anderen?  Aber,  liebe
+Leserin, lieber Leser, wer fragt in einer Geschichte, die in  mythischer  Zeit
+spielt,  nach  den  Namenlosen?  Oder  gehoeren  Sie  zu  denen,   die	 einen
+historischen Film mit vielen Statisten nur anschauen,  um  den	unaufmerksamen
+Studenten zu finden, der sich schnell ein paar Mark verdienen  wollte  und  in
+der Aufregung vergass, die Uhr auszuziehen? Wie profan, liebe Leserin,	lieber
+Leser. Aber gut, wenn Sie es gar nicht anders haben wollen,  fuegen  wir  hier
+noch ein paar Statisten ein und geben der Szene noch ein wenig Lokalkolorit.
+
+Sicherlich erkennen Sie die Dame, die  dort  auf  dem  Markt  steht.  Sie  ist
+rundlich, traegt eine weisse Schuerze ueber dem weiten Rock  und  eine	weisse
+Bluse, die huebsch mit ihrem roten Bustier  kontrastiert.  Sie	hat  apfelrote
+Wangen und ein freundliches Laecheln, bei dem weisse Zaehne blitzen. Nein, die
+Dame  ist  nicht  die  Gewinnerin  eines  Zahnpasta-Werbefilms,  sie  ist  die
+Gemueseverkaeuferin. Sie  handelt  unter  anderem  mit	AEpfeln  und  ist  die
+Urheberin des Spruches: Damit Ihr auch morgen noch kraftvoll zubeissen koennt,
+Euer Exzellenz. Der Name dieser Frau ist in den Tiefen der Historie ertrunken,
+sonst fuehrten ihre Erben bis heute einen Kampf um das Urheberrecht  an  ihrem
+Werbeslogan.
+
+Selbstverstaendlich wollen wir ein moeglichst  authentisches  Bild  entwerfen,
+daher fuegen wir nun auch noch die dunkle Gestalt am anderen Ende des  Platzes
+ein. Wie Sie sich schon gedacht haben, handelt es  sich  bei  diesem  Mann  um
+einen Dieb. Er hat nur fuer diesen Absatz seinen Urlaub  auf  der  Ferieninsel
+der Mythen unterbrochen, also zollen Sie ihm gebuehrende Aufmerksamkeit. Sehen
+Sie, er winkt. Winken wir zurueck und lassen ihn seinen wohlverdienten	Urlaub
+fortsetzen.
+
+Moment, was hat der dicke, sonnenverbrannte Mann im Hawaiihemd mit der	Kamera
+da zu suchen? Halten Sie bitte	einen  Augenblick  mit	dem  Lesen  inne,  ich
+schaffe ihn eben dorthin zurueck, wo er herkam.
+
+Danke, Sie koennen jetzt weiterlesen.
+
+Bevor Sie danach fragen, will ich Ihnen  erklaeren,  was  es  mit  den	beiden
+Kraehen auf sich hat,  die  auf  der  Winde  des  huebschen  Brunnens  in  der
+Platzmitte sitzen. Es sind Turtelkraehen. Ihre Namen sind Ulo und  Azzuro  und
+Sie werden die beiden noch besser kennenlernen.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite20 b/d/seher/haeuser/special/special/rom_jul/seite20
new file mode 100644
index 0000000..d51d595
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite20
@@ -0,0 +1,201 @@
+DER VIERTE AKT
+	DIE ERSTE SZENE
+
+Bruder Lawrence betrachtete Paris mit Unwillen. "Donnerstag? Julia?"
+
+Paris nickte. "So will es ihr Vater."
+
+"Das ist sehr kurzfristig." Er wiegte seinen Kopf  hin  und  her.  "Ich  weiss
+nicht, ich weiss nicht."
+
+"Was wissen Sie nicht?" Paris zog die Stirn in Falten.
+
+"Na, so kurz nach dem Tod ihres Vetters... Mir kommt das ein wenig,  wie  soll
+ich sagen, spanisch vor."
+
+"Julias  Vater  ist  entschlossen.  Er  meint,  dass  Tybalts  Tod  sie   mehr
+beunruhigt, als gut fuer sie ist. Ausserdem braucht er wohl einen  neuen  Mann
+in den Reihen seiner Familie."
+
+"Als haetten nicht die Montagues auch einen Verlust,  schliesslich  ist  Romeo
+nicht mehr in Verona."
+
+"Was fuer ein Problem fuer unseren kleinen  Priester,  hm?",  murmelte  Azzuro
+leise.
+
+"Wenn ihm nicht schnell etwas einfaellt, muss er Julia zweimal verheiraten. Ob
+sein Gott das mag?" Ulo fluesterte nur, doch Lawrence hatte  sie  gehoert  und
+drohte ihr mit dem Finger.
+
+"Was sagt Julia selbst dazu?", fragte er Paris.
+
+"Ich habe noch nicht mit ihr gesprochen."
+
+"Nicht, dass sich irgendjemand darum kuemmerte.", raunte Ulo dem Moench zu.
+
+Bruder Lawrence nickte bedaechtig. "Es gefaellt mir  nicht,  ein  Maedchen  zu
+verheiraten, dass ueberhaupt nicht will."
+
+"Ich habe nicht gesagt, dass sie nicht will."
+
+"Das hat mir ein Voeglein ins Ohr gefluestert." Lawrence deutete auf Ulo.
+
+Paris, der einem Franziskaner nahezu alles zutraute, sah Ulo an und versuchte,
+den Ausdruck Voeglein mit der riesigen Kraehe in Einklang zu bringen. "Ich bin
+sicher,  Ihr  Voeglein	hat  der  Nachricht  Gewicht  verliehen.",  meinte  er
+diplomatisch.
+
+Bruder Lawrence rieb sich mit der Hand ueber die  Augen  und  sah  den	Huegel
+hinunter. "Taeusche ich mich, oder kommt da Julia?"
+
+Paris laechelte Julia zu.  "Wie  gut,  dass  du  kommst,  Lieblingsbraut.  Wir
+sprachen gerade von dir."
+
+"Das kann ich mir denken.", antwortete Julia unterkuehlt.
+
+"Lieblingsbraut?" Azzuro kraechzte vergnuegt. "Ob er noch mehr davon hat?"
+
+"Wahrscheinlich nicht, sonst liesse er Julia in Frieden." Ulo war  beeindruckt
+von Julias Haltung.
+
+"Ich haben gerade den Termin fuer die Hochzeit ausgemacht."
+
+"Muss das sein?" Julia verdrehte die Augen.
+
+"Natuerlich muss ich."
+
+"Warum geht er dann nicht hinter die Buesche?", murmelte Ulo.
+
+"Wenn es denn gar nicht anders geht."
+
+Paris, der annahm, Julia wollte sich vor  der  Hochzeit  von  Bruder  Lawrence
+beraten lassen, laechelte freundlich. "Sag einfach Bruder  Lawrence,  dass  du
+mich liebst, dann ist alles in Ordnung."
+
+"Wenn ich alles so einfach in Ordnung bringen koennte, indem  ich  sage,  dass
+ich ihn liebe." Julia seufzte.
+
+"Na, jetzt wird ihre Zunge wieder  spritziger."  Ulo  freute  sich  auf  einen
+Disput mit Paris.
+
+Paris verzog sein Gesicht zu einem duemmlichen Laecheln. "Ich bin sicher, dass
+du mich liebst."
+
+"Eitler Pfau!", schnaubte Ulo veraechtlich.
+
+"Wenn du es sagst, Paris.", meinte Julia.
+
+"Sie uebt schon fuer die Zeit nach der Hochzeit." Ulo  kannte  den  Trick,  so
+lange Ja zu sagen, bis der andere sich eine Sache  anders  ueberlegte  nur  zu
+gut.
+
+"Jetzt tut mir Paris fast leid." Auch Azzuro hatte mit dieser Methode  bereits
+Bekanntschaft geschlossen.
+
+Paris kuesste Julia zuechtig auf die Wange.  "Am  Donnerstag  bekommt  du  den
+grossen Bruder von diesem Kuss." Er zwinkerte ihr  anzueglich  zu  und  drehte
+sich um.
+
+Julia wischte sich die Wange mit einem angewiderten Gesichtsausdruck  ab.  "Es
+sieht ganz danach aus."
+
+Als Paris den  Huegel  hinuntergestakst  war,  drehte  Julia  sich  zu	Bruder
+Lawrence um. "Scheisse."
+
+Ulo, Azzuro und Bruder Lawrence schnappten gleichzeitig nach Luft.
+
+"Das ist aber ein schlechtes Wort, Julia.", tadelte Bruder Lawrence mild.
+
+"Mir faellt kein besseres ein."
+
+"Nanana." Lawrence strich ihr sanft ueber das Haar.
+
+Julia  sah  Bruder  Lawrence  ernst  an.  "Ich  bin  mit  Romeo   rechtmaessig
+verheiratet, oder?"
+
+Der Moench nickte. "Wenn ihr..."  Er  blickte  fragend  zu  Azzuro  hoch,  der
+bestaetigend nickte. "Ja, das seid ihr wohl."
+
+"Dann kann ich auch keinen anderen Mann heiraten. Schon gar  nicht,  wenn  ich
+nicht will."
+
+"Es gibt Praezendenzfaelle, weisst du. Wenn ein Mann  verbannt  ist,  kann  er
+fuer tot erklaert werden."
+
+Julia richtete sich auf eine lange Vorlesung ein, doch als  Lawrence  schwieg,
+hob sie die Haende. "Nur, wenn man einen Antrag stellt.  Und  ich  stelle  den
+bestimmt nicht." Energisch putzte sie sich die Nase. "Ich  werde  Paris  nicht
+heiraten. Er ist haesslich, aufgeblasen und nicht Romeo."
+
+Lawrence nickte. "Drei gute Gruende, aber was koenntest du sonst tun?"
+
+"Ich bring mich um."
+
+"Das lasse ich nicht zu. Gott verbietet es und wenn du  mir  davon  erzaehlst,
+halte ich dich ab."
+
+"Versuchs nur." Julia schaute Bruder Lawrence so durchdringend an, dass dieser
+sich am liebsten in seiner Kutte verkrochen haette wie	eine  Schildkroete  in
+ihrem Panzer.
+
+"Lass mich nachdenken, vielleicht habe ich eine Idee."
+
+"Denk lieber schnell, denn ich halte es nicht mehr aus."
+
+"Wenn du den Mut hast, dich umzubringen, was natuerlich  gegen  Gottes  Gesetz
+waere, dann hast du doch bestimmt den Mut, dich scheinbar umzubringen und  nur
+scheinbar gegen Gottes Gesetz zu handeln?"
+
+"Komplizierter kann er es wohl nicht ausdruecken.", maulte Ulo.
+
+"Legal, illegal..." Julia zoegerte.
+
+"Scheissegal.", half Azzuro aus und  erntete  einen  vernichtenden  Blick  von
+Bruder Lawrence.
+
+Julia nickte. "Richtig. Ich nehme es mit allem auf, Bruder Lawrence.  Wenn  es
+sein muss, dann setze ich mich in einen Ameisenhaufen, spiele Karten mit einer
+Hexe, tanze auf dem Friedhof oder - ja, sogar das -  ich  sage	meinem	Vater,
+dass er ein  herzloser	Tyrann	ist.  Wenn  du	weisst,  wie  ich  aus	diesem
+Schlamassel herauskomme, dann rede."
+
+Bruder	Lawrence  verschwand  in  der  Kapelle	und  kramte  in  einer	Kiste.
+Schliesslich kam er mit einer kleinen Flasche wieder heraus.  "Hier,  das  ist
+die Loesung."
+
+Julia wollte nach dem Flaeschchen greifen, doch Lawrence zog  rasch  die  Hand
+zurueck. "Hoer mir erst zu. Du gehst hiermit nach Hause und tust ganz so,  als
+haettest du es dir  ueberlegt.	Niemand  darf  Verdacht  schoepfen,  also  sei
+froehlich und tu... Na, was immer eine Braut so tut. Am Mittwochabend gehst du
+ins Bett und sorgst dafuer, dass du  in  deinem  Zimmer  lange	genug  alleine
+bist." Er schwenkte die Flasche. "Dieses kleine Schaetzchen hier  wirkt  nicht
+so schnell, dass man die Wirkung nicht	verhindern  koennte,  indem  man  dich
+zum... indem man deinen Magen leert."
+
+Julia nickte traurig. "Also bringe ich mich doch um."
+
+"Nein, nein. Dabei  huelfe  ich  dir  nicht."  Lawrence  laechelte.  "Ganz  im
+Gegenteil. Wenn du es trinkst, wirkst du nur fuer zweiundvierzig  Stunden  wie
+tot. Es laesst deinen Puls so langsam werden, dass niemand  ihn  mehr  fuehlen
+kann, deine Muskeln werden steif, dein Atem setzt  aus	und  deine  Haut  wird
+kalt. Jeder wird denken, du seist tot. Nur, dass du es nicht bist."
+
+Julia nickte. "Was nuetzt das? Irgendwann werde ich wach  und  alles  ist  wie
+vorher."
+
+Bruder Lawrence  breitete  die	Arme  aus,  als  hielte  er  ein  grossartiges
+Geschenk. "Nicht ganz. Man wird dich begraben. Wenn  die  Wirkung  nachlaesst,
+wird Romeo dich  aus  der  Gruft  holen  und  ihr  beide  koennt  nach	Mantua
+verschwinden, ohne dass je ein Mensch davon erfaehrt."
+
+"Romeo? Wie soll er..."
+
+Lawrence unterbrach sie. "Ganz einfach: Ich werde ihm schreiben."
+
+Julia fiel dem Moench um den Hals. "Ich koennte dich kuessen!"
+
+"Lieber nicht, sonst erwuergen ihn seine Geluebde.", meinte Ulo schadenfroh.
+
+"Sieh ihn dir an." Azzuro deutete mit dem Schnabel auf Bruder  Lawrence.  "Das
+tun sie so oder so."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite21 b/d/seher/haeuser/special/special/rom_jul/seite21
new file mode 100644
index 0000000..0b12bf5
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite21
@@ -0,0 +1,57 @@
+DER VIERTE AKT
+	DIE ZWEITE SZENE
+
+Capulet ging mit seiner Frau die Gaesteliste durch. "Was meinst du, sollen wir
+Cousine Adelgund einladen?"
+
+"Bist du wahnsinnig? Wir haben Tante Frieda eingeladen!" Frau  Capulet  liebte
+zwar die kleinen Sticheleien der Gesellschaft, eine Bombe im Haushalt war  ihr
+allerdings zuwider.
+
+"Ja,  wahnsinnig  bin  ich  wahrscheinlich."  Herr  Capulet  seufzte.  "Soviel
+Aufhebens und Julia hat noch nicht einmal ein nettes Wort fuer mich."
+
+Frau Capulet ueberwand sich und legte ihrem Mann die Hand  auf	die  Schulter.
+"Eines Tages wird sie es verstehen."
+
+"Was verstehen?", fragte Julia von der Tuer aus.
+
+"Wieviel Muehe wir uns geben, um dich gut und anstaendig unter  die  Haube  zu
+bringen." Abwesend zupfte Frau Capulet Julias Kleid gerade.
+
+"Doch, ich verstehe schon." Julia schenkte ihren Eltern ein ebenso strahlendes
+wie falsches Laecheln. "Ich verstehe sogar so gut,  dass  ich  Paris  heiraten
+werde, ohne einen Aufstand zu probieren."
+
+Herr Capulet blickte ueberrascht von der Gaesteliste auf. "Bist du  doch  noch
+zur Vernunft gekommen? In dem Fall feiern  wir	die  Hochzeit  gleich  morgen,
+damit du es dir nicht noch einmal anders ueberlegst."
+
+Julia nickte ergeben. "Ganz wie mein Vater wuenscht."
+
+Herr Capulet taetschelte ihr den Kopf. "Das ist Papas  kleines  Maedchen."  Er
+begann zu stammeln. Offensichtlich wusste er nicht mehr, was er sagen  wollte.
+"Jaja, der gute Bruder  Lawrence.  Bis  jetzt  hat  er  noch  jedem  den  Kopf
+zurechtgerueckt. Ich bin ihm dankbar." Er zoegerte und fluechtete sich dann in
+Phrasen. "Ach was, ganz Verona ist ihm zu Dank verpflichtet."
+
+Julia nickte. "Zumindest ich stehe tief in seiner Schuld." Sie dachte  an  die
+kleine Flasche in ihrer Handtasche und laechelte. "Mama, kann ich  morgen  ein
+bisschen Schmuck von dir haben?"
+
+Frau  Capulet  laechelte.  "Such  dir  nur  aus,  was  du  haben   moechtest."
+Nachdenklich blickte sie Julia nach und wandte sich dann an ihren  Mann.  "Ich
+denke nicht, dass wir den Termin auf morgen verschieben  sollten.  Uebermorgen
+waere immer noch ein guter Tag und fuer morgen haben wir bestimmt nicht  genug
+Vorraete im Haus."
+
+"Ach, darum kuemmere ich mich schon." Herr Capulet war fest entschlossen,  die
+milde Stimmung seiner Tochter auszunutzen  und	einen  Skandal	zu  vermeiden.
+"Sorg du dafuer, dass Julia morgen herausgeputzt ist,  ich  putze  dafuer  das
+Haus und erledige den Rest."
+
+"Seit wann bist du unter  die  Hausmaenner  gegangen?",  fragte  Frau  Capulet
+zweifelnd.
+
+Ihr Mann brummte nur und beugte sich wieder ueber die Gaesteliste.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite22 b/d/seher/haeuser/special/special/rom_jul/seite22
new file mode 100644
index 0000000..77219dc
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite22
@@ -0,0 +1,180 @@
+DER VIERTE AKT
+	DIE DRITTE SZENE
+
+Polly legte das Kleid, das Julias Mutter bei ihrer eigenen  Hochzeit  getragen
+hatte, ueber einen Stuhl. "Huebsch ist das. Bestimmt freust du dich,  dass  du
+es morgen anziehen kannst."
+
+Julia hatte die Anprobe wie eine Kleiderpuppe ueber sich  ergehen  lassen  und
+reagierte auch jetzt nicht auf Pollys Worte. Obwohl  es  ihr  schwer  gefallen
+war, hatte sie nicht einmal Einspruch  erhoben,  als  die  Schneiderin	darauf
+bestand, dass Julia zuviel Oberweite hatte und das Kleid  unbedingt  geaendert
+werden musste. Gluecklicherweise war Frau Capulet  eine  praktische  Frau  und
+hatte bei der Anfertigung des Kleides daran gedacht, ausreichend Stoff	uebrig
+zu lassen. Nur Julia hatte Ulos veraechtlichen	Kommentar  gehoert,  dass  der
+Stoff wohl weniger aus Gruenden der Sparsamkeit dort verblieben  war,  sondern
+weil Frau Capulet bei der damaligen Anprobe gemogelt hatte.
+
+Ulo schuettelte auf ihrem Ast vor dem Fenster den Kopf. "Das  arme  Ding.  Wie
+kann man ihr nur zumuten, zu ihrer Hochzeit abgelegte Kleider zu tragen."
+
+"Du hast dir noch nicht einmal ein Band um den  Hals  gebunden,  als  wir  uns
+zusammen getan haben.", murrte Azzuro.
+
+"Warum haette ich auch sollen?", schnappte Ulo. "Du hattest es ja  auch  nicht
+noetig,  mir  einen   Heiratsantrag   zu   machen,   geschweige   denn	 einen
+Verlobungsring zu besorgen."
+
+Azzuro, der sich sehr genau daran erinnerte, einen glitzernden Ring aus  einem
+unbeaufsichtigten Schmuckkaestchen gestohlen zu haben, drehte sich  schmollend
+um. "Ich habe dir immerhin ein schoenes Nest gebaut."
+
+Ulo ignorierte ihn und beobachtete Frau Capulet, die Julias Zimmer betrat.
+
+"Kommt  ihr  zurecht?  Habt  ihr  alles  gefunden?",   fragte   Frau   Capulet
+aufgekratzt.
+
+"Wir sind fertig." Julia gaehnte hinter vorgehaltener Hand. "Ich bin muede und
+wenn ich morgen gut aussehen soll, muss ich noch ein bisschen schlafen."
+
+"Es gibt da noch etwas, worueber wir  reden  sollten.",  begann  Frau  Capulet
+unsicher.
+
+"Keine Sorge, Mama, Polly hat mir  schon  gesagt,  dass  Paris  wahrscheinlich
+schnarcht."
+
+"Das ist nicht das einzige, Julia." Frau Capulet erroetete.
+
+"Hat er Schweissfuesse?"
+
+"N-nein."  Frau  Capulet  wand  sich.  "Du  brauchst  keine  Angst   vor   der
+Hochzeitsnacht zu haben, weisst du." Sie  aehnelte  einem  General,  der  eben
+einen hoffnungslosen Ausfall befohlen hat.
+
+"Natuerlich weiss ich, Mama." Julia genoss es,  ihre  Mutter  so  verlegen  zu
+sehen.
+
+"Paris ist ein freundlicher Mann."
+
+"Und alt genug, dass sie selbst Erfahrungen mit ihm  haben  koennte.",  fuegte
+Azzuro ungnaedig hinzu.
+
+"Ich bin jetzt muede, Mama." Julia zog die  Vorhaenge  vom  Bett  und  winkte.
+"Gute Nacht."
+
+Frau Capulet wollte noch etwas	sagen,	wurde  allerdings  von	Polly  am  Arm
+genommen. "Gehen wir schlafen und lassen Julia ihre Ruhe.", schlug Polly vor.
+
+Julia seufzte laut, als die beiden  gegangen  waren.  "Hoffentlich  nicht  bis
+morgen."
+
+Ulo schniefte laut.
+
+"So schlimm ist es nicht, Kraehe." Julia zog ein Laecheln aus den Tiefen ihrer
+Erinnerung und nagelte es entschlossen auf ihrem Gesicht fest. "Ich bin  froh,
+dass ihr hier seid. Dann muss ich  das	Zeug  wenigstens  nicht  ganz  alleine
+trinken."
+
+"Aehm, es macht dir aber hoffentlich nichts aus, wenn wir nicht  mittrinken?",
+fragte Ulo.
+
+"Aber du koenntest es ruhig mit Wein verduennen und mir  einen  Schluck  davon
+abgeben. Von dem Wein, natuerlich." Azzuro rieb sich beilaeufig mit der Kralle
+ueber den Schnabel.
+
+Julia hoerte nicht zu, sondern betrachtete die Flasche. "Was  meint  ihr,  hat
+Bruder Lawrence mich reingelegt?"
+
+"Warum sollte er?", fragte Ulo.
+
+"Wie staende er wohl da, wenn ich allen erzaehle, dass er mich  binnen  zweier
+Tage gleich zwei Mal verheiratet hat?"
+
+"Ach was." Ulo kniff ein Auge zu.  "Wer  sollte  dir  wohl  glauben,  dass  du
+ausgerechnet mit Romeo Montague verheiratet bist? Die Leute glauben viel, aber
+das ginge im Leben nicht in ihre Koepfe."
+
+Julia nickte. "Mag sein. Also scheidet das aus:  Bruder  Lawrence  hat  keinen
+Grund mich zu hintergehen. Bosheit ist es sicherlich nicht,  schliesslich  ist
+er ein Priester und heiliger Mann."
+
+"Heiliger Mann!", schnaubte Azzuro. "Hast du schon mal was  von  den  Borghias
+gehoert?"
+
+"Sie sind glaube ich weitlaeufig mit mir verwandt.  Allerdings  habe  ich  sie
+noch nie getroffen, sie leben in einer anderen Stadt." Sie sagte  es  so,  wie
+wir heute jemanden darueber  aufklaeren,  dass	wir  Verwandte	in  Australien
+haben.
+
+"Jedenfalls...Aua!" Azzuro verstummte und blickte Ulo verletzt an.
+
+"Wisst ihr, das Schlimmste waere, wenn es nicht wirkt. Ich  meine,  was,  wenn
+ich morgen aufwache und heiraten muss?" Sie sah sich  suchend  im  Zimmer  um,
+erblickte ein scharfes Handarbeitsmesser und wog es nachdenklich in der  Hand.
+"Na, ehe ich Paris heirate..."
+
+"Das gaebe eine ziemliche Schweinerei." Ulo  schuettelte  es  allein  bei  dem
+Gedanken daran.
+
+"Besser als Paris heiraten.", versetzte Julia scharf.
+
+"Schon gut, schon gut." Ulo musste ihr wider Willen zustimmen.
+
+"Wenn nur mein Vater nicht  in  letzter  Minute  noch  den  Termin  vorgezogen
+haette."
+
+"Was soll daran so schlimm sein?", fragte Azzuro. "Du trinkst das Zeug einfach
+einen Tag frueher."
+
+"Und wenn Romeo den Brief nicht rechtzeitig bekommt? Bruder Lawrence  hat  ihn
+losgeschickt, aber man weiss ja, wie langsam Klosterbrueder voran kommen, wenn
+sie auf Reisen sind."
+
+"Allzu viele Weinstuben gibt es auf dem Weg nach Mantua nicht.", beruhigte Ulo
+sie.
+
+"Trotzdem. Mich graust es bei dem Gedanken an diese staubige Gruft.  Luftdicht
+ist gut, wenn man den Gestank drinnen halten will, aber wenn ich darin bin..."
+
+"Du wirst es ueberleben." Azzuro versuchte, seiner Stimme  einen  beruhigenden
+Klang zu geben.
+
+"Vielleicht waere das gar nicht so gut. Ich habe schreckliche Angst davor,  da
+drinnen aufzuwachen. Am Ende werde ich wahnsinnig."  Ihr  Laecheln  verschwand
+und machte einem bitteren Ausdruck Platz. "Das waere schlimmer als sterben."
+
+Ulo versuchte, sie zu troesten. "Du wuerdest es nicht merken."
+
+Julia nickte. "Vermutlich." Sie zog die Nase kraus und dachte nach. "Sagt mal,
+stimmt es, dass die Toten in der Gruft..." Sie zoegerte. "Ihr  wisst  schon...
+Es heisst, das es einige Tage dauert, bis sich der Tote  mit  seinem  ...Platz
+abgefunden hat."
+
+"Woher sollen wir das wissen? Wir waren noch nicht  tot."  Azzuro  zuckte  die
+Schultern. "Es gibt da so etwas wie Gase, die noch  rumoren,  aber  vielleicht
+ist das auch nur ein Geruecht."
+
+Julia dachte mit Grausen an  Tybalt.  "Ob  ich  Geister  sehen  werde?  Tybalt
+vielleicht, der Romeo sucht und ihn nicht findet? Wenn er dann mit mir Vorlieb
+nimmt?" Sie schuettelte sich.
+
+"Du wirst schlafen." Ulo liess sich auf Julias Schulter nieder und strich  ihr
+sanft mit dem Kopfgefieder ueber die Wange.
+
+"Das kitzelt." Julia kicherte, dann wurde sie wieder ernst. "Ich habe so  viel
+darueber gehoert, was nachts auf Friedhoefen vorgeht. Manchmal hoert  man  die
+Toten entsetzlich schreien, heisst es."
+
+"Das sind bloss Katzen." Azzuro hatte die riesigen Tiere gesehen, die auf  der
+Suche nach Ratten ueber die Graeber huschten  und  die	Liebespaare  stoerten,
+fuer die der Friedhof der einzige wirklich ungestoerte Ort war.
+
+"Wenn du es sagst.", meinte Julia  zweifelnd.  "Naja,  wahrscheinlich  hat  es
+keinen Sinn, es noch laenger hinauszuschieben." Sie hob  die  Phiole.  "Prost,
+ihr beiden. Falls ich es selbst nicht kann, gruesst Romeo von mir."
+
+Ulo huepfte zur Seite, als Julia niedersank. "Jetzt  hat  sie  zumindest  ihre
+Ruhe."
+
+"Sie haette es wirklich in Wein nehmen koennen.", murrte Azzuro.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite23 b/d/seher/haeuser/special/special/rom_jul/seite23
new file mode 100644
index 0000000..e37dbce
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite23
@@ -0,0 +1,134 @@
+DER VIERTE AKT
+	DIE VIERTE SZENE
+
+Frau Capulet schleuderte ihre Blicke ueber das	Schlachtfeld,  das  aus  ihrem
+Haus geworden war, fand Polly und bannte sie mitten im	Schritt.  "Hier,  nimm
+die Schluessel, in der Kueche werden noch Kraeuter gebraucht."
+
+Polly nahm  die  Insignien  hausfraulicher  Macht  andaechtig  entgegen.  "Der
+Baecker moechte auch Datteln und Quitten haben."
+
+"Dann gib sie ihm." Frau Capulet sah ihren Mann die Treppe herunter kommen und
+stoehnte. "Er hat mir grade noch gefehlt."
+
+Herr Capulet nahm mit Schwung  die  Kurve  um  einen  Diener  herum,  der  ein
+Bierfass in Richtung Speisesaal rollte, duckte sich im	letzten  Moment  unter
+einem Tablett mit Glaesern durch und wich um  Haaresbreite  einem  Tisch  aus,
+der, vollbeladen mit Obst, unschuldig inmitten des Raumes stand. "Was hat denn
+der Tisch hier zu suchen? Sonst ist der doch  auch  nicht  da.",  wunderte  er
+sich.
+
+"Natuerlich nicht." Frau Capulet fasste sich mit uebertriebener Geste  an  die
+Stirn. "Aber nachdem wir so wenig Zeit haben, die Hochzeit vorzubereiten, kann
+schon einmal ein Tisch im Weg stehen."
+
+"Schon gut, schon gut." Herr Capulet gewahrte einen Kuechenjungen, der sich in
+eine Ecke drueckte und versuchte, moeglichst viel von der Plaenkelei  zwischen
+ihm und seiner Frau zu hoeren. "Du da!"
+
+"Ich?" Der Kuechenjunge blickte sich um.
+
+"Ja, du. Sieh zu, dass du auf die Fuesse kommt, wir haben nicht den ganzen Tag
+Zeit." Herr Capulet wedelte mit den Haenden und ohrfeigte  einen  Diener,  der
+sorgsam einen Fuss vor den anderen setzte und mit  zusammengekniffenen	Lippen
+auf die Suppenterrine in seinen Haenden starrte.
+
+Pollys Gesicht verzog sich zu einem Inbild  des  Zorns,  als  der  Diener  die
+Terrine  fallen  liess	und  eine  gute  Handvoll  anderer  Bediensteter   mit
+bruehheisser Suppe ueberschuettete. Sie stemmte die  Arme  in  die  Seite  und
+wartete geduldig, bis sich der Aufruhr gelegt hatte.  "Herr  Capulet?"  Pollys
+Stimme war freundlich.
+
+"Ja?"
+
+Polly atmete tief durch und man sah ihre Lippen die Zahlen von eins  bis  zehn
+formen.  Schliesslich  kniff  sie  die	Augen  zusammen.  "Sie  wollten  nicht
+zufaellig gerade gehen?", saeuselte Polly.
+
+"Kielholen, kielholen!" Ulo jauchzte.
+
+"Nein, eigentlich nicht." Herr Capulet war sichtlich verwirrt.
+
+"Ein Mann wie Sie hat sicherlich keine rechte Freude  daran,  Frauenarbeit  zu
+tun, oder?"
+
+"Gibs ihm!" Azzuro vergass ganz und gar, kraehisch zu sprechen.
+
+"Habe ich Keks gesagt, dass du Kruemel dich meldest?", fragte Polly scharf und
+erwuergte Azzuro mit einem Blick.
+
+"Kruemel?" Azzuro war entruestet.
+
+"Irgendjemand  muss  sich  ja  darum  kuemmern,  dass  alles   glatt   geht.",
+verteidigte sich Herr Capulet, der die Kraehen gar nicht beachtete.
+
+"Ja-a.", machte Polly. "Wir."
+
+"Wenn ich euch Weibern hier das Regiment ueberlasse, geht  alles  drunter  und
+drueber."
+
+Polly betrachtete betont die Scherben der Terrine und schwieg.
+
+"Sie hat Kruemel gesagt!", empoerte sich Azzuro in die eisige Stille hinein.
+
+"Ueberhaupt war ich zu meiner  Zeit  durchaus  in  der  Lage,  mich  um  einen
+Haushalt zu kuemmern." Herr Capulet hob unbehaglich die Arme.
+
+"Richtig, zu Ihrer Zeit. Und morgen jammern Sie ueber ihr Zipperlein, weil Sie
+die Nacht durchgemacht haben."
+
+"Unsinn."  Herr  Capulet  runzelte  die  Stirn.  "Fuerchtest  du,  es  koennte
+auffallen, wieviel besser ich das kann als du?"
+
+"Das Jammern?" Ulo kicherte.
+
+Alle Blicke senkten sich wieder auf die Scherben.
+
+"Liebling, das war die Terrine aus dem  Service,  das  meine  Mutter  uns  zur
+Hochzeit geschenkt hat.", mischte sich Frau Capulet ein.
+
+"Hoechste Zeit, dass wir ein  neues  anschaffen."  Herr  Capulet  wandte  sich
+brummend zu zwei Dienern um, die ein  Schwein  auf  einem  Spiess  zur	Kueche
+trugen. "Was ist das?", schnauzte er.
+
+Der vordere Diener wandte sich um und betrachtete das  Tier  auf  dem  Spiess.
+"Ein Schwein, wenn mich nicht alles taeuscht."
+
+"Ein Schwein, pah!" Polly schnaubte und schob sich an den  Dienern  vorbei  in
+die Kueche.
+
+"Wen hat sie damit wohl gemeint?", fragte Ulo leise.
+
+"Kruemel, pah!" Azzuro schlug mit den Fluegeln.
+
+"Nein, mit dem Schwein."
+
+"Wen interessiert das? Sie hat mich Kruemel genannt!"
+
+Herrn Capulets Zeigefinger machte einen Vorstoss auf eine Magd.  "Was  ist  in
+dem Korb?"
+
+"Keine Ahnung, ist fuer die Kueche."
+
+"Dann bring es hin."
+
+"Ich bin dabei.", versetzte die Magd.
+
+"Gut, gut." Herr Capulet ignorierte seine Frau, die  sich  mit  verschraenkten
+Armen gegen die Wand lehnte.
+
+"Da kommt Paris." Ulo wandte den Kopf  und  betrachtete  den  bunten  Zug  von
+Musikanten und Akrobaten, die Paris begleiteten.
+
+Auch Herr Capulet hatte die Musik gehoert. "Du lieber Himmel, da ist ja  schon
+Paris!" Er drehte sich um seine Achse. "Polly!"
+
+Polly streckte den Kopf durch die Kuechentuer. "Was ist denn?"
+
+"Paris ist los - nein, da. Los, los, wecke Julia. Bring sie  her!  Nein,  zieh
+sie erst an, bring sie danach." Herr Capulet breitete die Arme aus und  drehte
+sich um seine Achse.
+
+Polly fing den Blick Frau Capulets und tippte sich bedaechtig mit einem Finger
+an die Stirn, dann verschwand sie die Treppe hinauf.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite24 b/d/seher/haeuser/special/special/rom_jul/seite24
new file mode 100644
index 0000000..502ad02
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite24
@@ -0,0 +1,217 @@
+DER VIERTE AKT
+	DIE FUENFTE SZENE
+
+Polly hielt sich nicht damit auf, an Julias Tuer zu klopfen, sondern  stuermte
+gleich ins Zimmer. "Julia, Mauseschwaenzchen, Zeit zum  Aufstehen.",  floetete
+sie.
+
+"Wie lange sie wohl braucht?" Azzuro blickte betont  zu  Julias  Bett,  dessen
+Vorhaenge nur einen Spalt weit offen standen.
+
+"Warten wir's ab." Ulo legte bedaechtig eine Feder wieder an  ihren  richtigen
+Platz.
+
+"Julia, troedel nicht, dein Braeutigam ist da." Polly hielt einen Moment  inne
+und kicherte. "Du tankst wohl jetzt schon Schlaf? Recht hast  du,  Paris  wird
+sicherlich fuer die naechste Zeit andere Plaene fuer die  Naechte  haben.  So,
+jetzt aber raus mit dir, du Schlafmuetze! Du willst  doch  nicht,  dass  Paris
+dich im Bett findet, oder? Dann kommt ihr beide verspaetet zur Hochzeit -  und
+zu frueh zur Hochzeitsnacht." Sie kicherte wieder.
+
+"Da!" Azzuro wackelte aufgeregt auf seinem  Ast  hin  und  her.  "Jetzt  merkt
+sie's."
+
+Polly zog die Vorhaenge zurueck. "Was denn, immer  noch  angezogen?  Warst  du
+gestern so muede? Schaemen solltest  du  dich,	das  schoene  Kleid  ist  ganz
+verdrueckt. Und ich muss es wieder buegeln." Sie ruettelte an Julias Schulter.
+"Julia!" Pollys Stirn krauste sich. "Julia?"
+
+"Achtung, jetzt kommt's. Was gaebe  ich  jetzt  fuer  ein  Paar  Haende!"  Ulo
+stoehnte, als Polly markerschuetternd schrie.
+
+Wlad streckte den Kopf unter der Regenrinne hervor. "Wer ssschreit denn mitten
+in der Nacht?"
+
+"Schlaf weiter." Azzuro wandte  den  Blick  nicht  von  der  Szene  in  Julias
+Schlafzimmer.
+
+Brummelnd zog Wlad sich zurueck.
+
+"Was ist denn das fuer ein Laerm hier?"  Frau  Capulets  modisch  weisse  Haut
+roetete sich vor Aerger.
+
+"Oh Jammer, oh Weh!" Polly zerraufte sich das Haar. "Julia!"
+
+"Hat sie wieder Floehe im Kopf?", fragte Herr Capulet ungnaedig,  als  er  ins
+Zimmer kam.
+
+"Nein, aber bald Wuermer. Sie ist tot!" Polly zog das letzte  Wort  mit  einem
+Schluchzen so lang, dass man es kaum verstehen konnte.
+
+"Tot." Frau Capulet wurde wieder bleich und plumpste auf den Boden.
+
+Herr Capulet schluckte schwer. "Tot?"
+
+"Verschieden, zu ihren Ahnen gegangen, hat den Loeffel abgegeben, den  letzten
+Tanz getanzt, in den ewigen Jagdgruenden..." Polly holte Luft. "Tot."
+
+Nun daemmerte auch Herrn Capulet die Bedeutung ihrer Worte. Hilflos blickte er
+sich im Raum um, fand schliesslich eine  Waschschuessel  und  schleuderte  sie
+gegen die Wand. "Das ist nicht wahr!"
+
+Frau Capulet, die den Inhalt der Waschschuessel ins  Gesicht  bekommen	hatte,
+oeffnete ein Auge und griff sich an die Brust. "Meine Julia? Julia!"
+
+"Krokodilstraenen." Ulo reckte veraechtlich den Schnabel in die Luft.
+
+"Du denkst auch immer das Schlimmste." Azzuro konnte sich ein Schniefen  nicht
+verkneifen.
+
+"Ich kenne die Menschen.", versetzte Ulo.
+
+"Wie konnte das  nur  passieren?"  Polly  umfasste  ihre  kleine  Flasche  mit
+Lebenselixir, die sie aus ihren grossen Rocktaschen gezogen hatte.
+
+"Hey, davon kann sie mir etwas abgeben." Azzuro beugte sich nach vorne.
+
+"Pass nur auf, dass du nicht vom Ast faellst,  Gierschlund."  Ulo  zupfte  ihn
+missbilligend  am  Schwanz.   "Da,   jetzt   kommt   Paris.   Da   kannst   du
+Krokodilstraenen sehen!"
+
+"Ach was. Er sagt doch, dass er sie liebt."
+
+"Richtig, er sagt es. Das heisst aber nicht, dass er es wirklich tut."
+
+"Wenn er sie nicht liebt, warum heiratet er  sie  dann?  Sie  hat  noch  nicht
+einmal Eier gelegt."
+
+"Menschen  legen  keine  Eier,  sie  werden  schwanger.",  klaerte  Ulo  ihren
+Gefaehrten auf. "Paris will Julias Stellung und ihr Erbe. An ihr selbst hat er
+kein Interesse. Aber jetzt muss er  trauern,  sonst  redet  die  Nachbarschaft
+ueber ihn."
+
+Bruder Lawrence erfasste beim ersten Blick ins Zimmer, dass Julia die von  ihm
+verabreichte Medizin getrunken hatte. Er  verkniff  sich  ein  Laecheln.  "Wie
+steht es? Ist unsere huebsche Braut fertig? Es ist Zeit fuer sie,  zur	Kirche
+zu gehen."
+
+Herr Capulet rieb sich ueber das Gesicht. "Tja, hingehen wird  sie  wohl,  nur
+heim kommt sie nicht mehr."
+
+Paris, der das Schlafzimmer einer Dame nicht unaufgefordert  betreten  wollte,
+war an der Tuer stehen geblieben und ahnte  noch  nicht,  dass	er  auf  seine
+Hochzeit verzichten musste. Nun lachte er. "Doch, aber in mein Heim."
+
+Herr Capulet schuettelte den Kopf.  "Nein,  heute  Nacht  hat  Julia  den  Tod
+geheiratet, und der hat sie dann gleich defloriert, meine kleine Blume."
+
+"Umpf." Azzuro wand sich. "Defloriert - wie unpassend."
+
+Paris machte einen taumelnden Schritt in den Raum hinein und  schuettelte  die
+Faust gegen die Decke. "Oh, Tod, wie konntest du mir das antun? Wie sehr  habe
+ich mich darauf gefreut, meine Braut zu kuessen, und jetzt nimmst du  sie  mir
+weg."
+
+"ICH HABE DOCH GAR NICHTS DAMIT ZU TUN."
+
+Ulo wandte sich zu der dunklen Gestalt um, die neben ihr auf dem Ast sass  und
+mit den knochigen Beinen baumelte. "Das weiss er aber nicht."
+
+"Du bist erst spaeter dran, warum trinkst du nicht noch einen  Kaffee  in  der
+Garderobe?", schlug Azzuro vor.
+
+"WO SOLL ICH  DEN  DENN  HINTUN?"  Tod  versank  in  die  Schatten  unter  den
+Blaettern.
+
+Frau Capulet betupfte ihre Augen mit einem  Spitzentuechlein.  "Was  fuer  ein
+schrecklicher  Tag.  Da  habe  ich  ein  Kind,	ein   einziges,   wunderbares,
+freundliches, schoenes, froehliches Kind, das einzige Kind, an dem ich	Freude
+habe, und kaum kann ich sie verheiraten stirbt sie."
+
+Wie ein verstaerktes Echo trompetete Polly laut in ihr Taschentuch.  "Wirklich
+ein schrecklicher, widerlicher, stinkender, ekelhafter Tag.  Die  arme	kleine
+Julia, statt auf den Ruecken zu fallen und die Fuesse zur Decke  zu  strecken,
+faellt sie aufs Kreuz und bleibt liegen."
+
+Paris, der so  rasch  kein  Taschentuch  hatte	finden	koennen,  nahm	Julias
+Brautschleier und vergrub sein Gesicht	darin.	"Noch  nicht  verheiratet  und
+schon von meiner Braut geschieden, ich fuehle mich betrogen."  Er  warf  Herrn
+Capulet einen Blick zu, den dieser sogleich verstand.
+
+"Wem soll ich jetzt mein Vermoegen vererben? Warum  konnte  sie  nicht  zuerst
+heiraten und dann sterben?" Herr Capulet machte  sich  nicht  die  Muehe,  ein
+Taschentuch zu suchen, sondern schneuzte sich in seinen Aermel.
+
+"Wahrscheinlich hattest du Recht mit den Krokodilstraenen.", meinte Azzuro.
+
+"Ich habe immer Recht." Ulo stiess ihren Gefaehrten mit dem Fluegel an.
+
+Bruder Lawrence war zum Fenster getreten. "Wann hat sie's getrunken?",  raunte
+er den Kraehen zu.
+
+"Ungefaehr Mitternacht.", antwortete Ulo.
+
+Lawrence rechnete. "Zehn Stunden, bleiben  noch  zweiunddreissig.  Gut.  Zeit,
+dass sie beerdigt wird, sonst wacht sie uns noch waehrend der  Beerdigung  auf
+und das waere peinlich." Er wandte sich um und setzte die Miene  auf,  die  er
+sonst fuer Predigten bereithielt. "So zu jammern ist  wirklich  nicht  noetig,
+liebe Brueder und Schwestern. Ihr wisst doch, dass Julia jetzt im  Himmel  ist
+und mit den Englein Harfe spielt."
+
+Ulo verdrehte die Augen, schwieg aber.
+
+"Liebe Schwestern und Brueder, ihr solltet euch freuen, dass  Julia  jetzt  im
+Himmel ist. Wie eigensuechtig von euch, hier zu weinen. Goennt ihr Julia  denn
+nicht, dass sie jetzt im Paradies ist?" Er machte eine Pause. Als Frau Capulet
+Luft holte, um seine Frage zu beantworten, fuhr Lawrence schnell fort. "Eltern
+wuenschen sich doch, dass ihr Kind es gut hat. Und das hat sie es  jetzt.  Sie
+ist beim lieben Gott und darf jetzt fuer ihn singen. Statt zu jammern, solltet
+ihr euch freuen und feiern. Nur dass ihr jetzt nicht Julias Hochzeit mit einem
+irdischen Braeutigam feiert, sondern ihre Hochzeit mit einem himmlischen.  Und
+der ist allemal besser als  ein  irdischer."  Er  zog  Polly  das  Taschentuch
+herunter und enthuellte die kleine Flasche, die sie im Schutz des Tuches immer
+wieder an ihre Lippen gehoben hatte. "Also, Schluss jetzt  mit  dem  Gejammer,
+zieht Julia huebsch an und bringt sie in die Kirche, damit sie beerdigt werden
+kann."
+
+Herr Capulet seufzte. "Jaja, das Leben geht weiter."
+
+"Richtig." Bruder Lawrence nickte ihm zu.
+
+"Dann werden wir also statt weisser Tuecher schwarze aufhaengen  und  traurige
+Lieder	statt  der  froehlichen  spielen.  Mit	dem  Fest  wollen  wir	Julias
+gedenken." Herr Capulet sah keinen Sinn darin, das  gute  Essen  verkommen  zu
+lassen.
+
+"Ganz genau. Und jetzt troedelt nicht  laenger,  Gott  will,  dass  ihr  Julia
+schnell beerdigt, damit nichts sie  mehr  von  den  Englein  ablenkt."  Bruder
+Lawrence scheuchte alle hinaus.
+
+Polly sah sich im Festsaal um. "Tja, da werden wir wohl noch  ein  paar  Dinge
+aendern muessen." Ihr Blick fiel auf die Musiker. "Ihr koennt dann gehen,  wir
+brauchen euch nicht."
+
+Ein alter Gaertner, der die  Topfrosen	hinaus	und  dafuer  Buchsbaum	herein
+brachte, schuettelte den Kopf. "Nein, geht noch nicht. Spielt  mir  doch  'Die
+Lorelei'."
+
+"Warum ausgerechnet das?", wollte der erste Geiger wissen.
+
+"Weil ich es mich auf andere Gedanken bringt. Ich hoere  in  meinem  Kopf  die
+ganze Zeit den Trauermarsch." Der alte Mann kratzte sich am Kopf und dachte an
+die Margaritenbaeumchen im Gewaechshaus. Julia hatte sie besonders gern gehabt
+und er beschloss, sie hier herein zu stellen und sich  nicht  um  das  Gezeter
+Herrn Capulets	zu  kuemmern,  der  Margariten	unpassend  finden  wuerde.  Er
+schneuzte sich verstohlen und nahm eine weitere Topfrose hoch.
+
+Der Geiger nickte. "Kannst du haben, aber was bekommen wir?"
+
+"Wie waere es mit einer Rose?" Der Gaertner spuerte Zorn  aufsteigen.  "Koennt
+ihr einem alten Mann nicht einfach einen Gefallen tun?"
+
+"Davon werden wir nicht satt, Mann."
+
+Der Bratschist hob beschwichtigend seinen Bogen. "Komm,  lass  gut  sein.  Wir
+spielen die Lorelei waehrend wir  auf  die  Trauergaeste  warten.  Waere  doch
+gelacht, wenn wir nicht ein paar Happen abbekaemen."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite25 b/d/seher/haeuser/special/special/rom_jul/seite25
new file mode 100644
index 0000000..ce3a080
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite25
@@ -0,0 +1,117 @@
+DER FUENFTE AKT
+	DIE ERSTE SZENE
+
+"Das war eine bescheuerte Idee.", beschwerte  sich  Azzuro.  "Wir  haetten  in
+Verona bleiben sollen."
+
+"Und diesen aufgeblasenen Affen zusehen, wie sie Trauer heucheln?" Ulo tauchte
+ihren Schnabel tief in eine Pfuetze und schmatzte.
+
+"Warum nicht? Schliesslich gibt es nachher ein Festmahl."
+
+"Du bist verfressen."
+
+"Ja." Azzuro konnte dies nicht abstreiten. "Aber nach Mantua ist es  weit  und
+ich bin muede."
+
+"Ich moechte Romeo sehen, wenn er zwei Nachrichten gleichzeitig  bekommt.  Was
+wird er wohl glauben, dass Julia tot ist oder dass sie schlaeft  und  auf  ihn
+wartet? Und was, wenn Bruder Lawrence den Brief vergessen hat?"
+
+"Keine Ahnung. Sagen kannst  du  es  ihm  jedenfalls  nicht,  sonst  sind  wir
+draussen." Er schuettelte sich bei dem Gedanken. "Sieh  mal,  da  geht  Bruder
+John."
+
+"Wer ist das nun wieder?"
+
+"Lawrence hat ihm den Brief fuer Romeo gegeben. Gut, dann  koennen  wir  jetzt
+umdrehen."
+
+"Ich wuerde mich lieber davon ueberzeugen, dass Romeo den Brief bekommt."
+
+"Sei nicht albern." Azzuro streckte die Fluegel und hob nach  Verona  ab,  die
+seufzende Ulo dicht hinter sich.
+
+Kehren wir nicht mit den Turtelkraehen nach Verona zurueck,  ich  bin  sicher,
+dass Sie, liebe  Leserin,  lieber  Leser,  sich  Erspriesslicheres  vorstellen
+koennen, als bei Julias Beerdigung zuzusehen. Zumal weder Sie noch  ich  etwas
+von dem Festschmaus hinterher bekommen werden.
+
+Romeo wischte sich den	Schlaf	aus  den  Augen,  als  sein  Diener  Balthasar
+eintrat. "Guten Morgen. Meine Guete, habe ich geschlafen."
+
+"Gut, hoffe ich?" Balthasar war nicht danach zumute, seine Nachrichten schnell
+loszuwerden.
+
+"Ich hatte einen verrueckten Traum. Ich lag tot in einem Grab und Julia kam zu
+mir. Hast du schon einmal so etwas Dummes gehoert?" Er gaehnte und schwang die
+Beine ueber die Bettkante. "Na, ich brauche nur  an  Julia  zu  denken,  schon
+fuehle ich mich praechtig. Was gibt es Neues aus Verona? Wie  geht  es	Julia?
+Und was macht meine Familie?"
+
+Balthasar goss Wasser in die Waschschuessel. "Ich habe zwei Nachrichten,  eine
+gute und eine schlechte."
+
+"Raetsel am fruehen Morgen!", stoehnte Romeo. "Kannst du damit  nicht  warten,
+bis ich wacher bin?"
+
+"Julia geht es so gut, dass es schon wieder  schlecht  ist."  Balthasar  legte
+langsam ein Handtuch neben die Schuessel.
+
+"Komm, spuck's aus." Romeo tauchte das Gesicht ins Wasser.
+
+"Es geht Julia gut, denn sie ist im  Himmel.  Das  ist  die  gute  Nachricht."
+Balthasar sprach schnell.
+
+"Ich verstehe." Romeo verzog keine  Miene.  "Besorg'  mir  Papier,  meins  ist
+gestern ausgegangen."
+
+Balthasar betrachtete Romeo kritisch. "Du siehst nicht gut aus. Viel zu  blass
+um die Nase fuer meinen Geschmack."
+
+Romeo, fest entschlossen, sich nichts anmerken zu lassen, grinste  breit.  "Du
+sollst mich ja auch nicht heiraten. Los schon, ich brauche das Papier."
+
+Balthasar  ging,  doch	nicht  ohne  Romeo  noch  einen  misstrauischen  Blick
+zuzuwerfen.
+
+Romeo zog seine Hose an und stopfte das Hemd energisch	hinein.  "Heute  abend
+bin ich bei Julia, komme was wolle.", murmelte er.
+
+Die Strassen Mantuas waren ueberfuellt und Romeo wurde	staendig  angerempelt,
+waehrend er sich einen Weg durch die Menge bahnte. Schliesslich  erreichte  er
+sein Ziel: Eine Apotheke, deren Geschaefte schlecht  genug  gingen,  dass  ihr
+Besitzer die Gesetze missachten und  ihm  Gift	verkaufen  wuerde.  Aergerlich
+betrachtete er das Geschlossen-Schild an der Tuer  und	schlug	dann  mit  der
+Faust gegen den Rahmen. "Hallo?"
+
+Der Apotheker oeffnete. "Ich habe heute keinen Notdienst."
+
+"Doch, haben Sie." Romeo hielt dem Mann ein kleines Vermoegen unter die Nase.
+
+"Kommen Sie rein." Der  Apotheker  zog  ihn  am  Aermel  hinter  sich  in  den
+Verkaufsraum und schlug die Tuer zu. "Was wollen Sie?"
+
+"Gift. Ein gutes, schnelles Gift."
+
+Der Mann schuettelte den Kopf. "Ich bin doch nicht wahnsinnig. Wenn ich  Ihnen
+Gift verkaufe, verliere ich meine Zulassung."
+
+"Zulassung hin, Geld her." Romeo schwenkte die Muenzen. "Ich werde  Sie  nicht
+verraten."
+
+Die Finger des Apothekers zuckten. "Ich kann nicht."
+
+"Was? Widerstehen?" Romeo laechelte. "Koennte ich auch nicht, wenn  ich  sehe,
+wie schlecht ausgestattet Sie sind. Fuer das Geld koennen Sie eine Menge Ware
+kaufen."
+
+Der Apotheker schloss umstaendlich einen Schrank auf.  "Da,  nehmen  Sie.  Und
+dann verschwinden Sie."
+
+Romeo nickte. "Ich verschwinde auf immer, keine Sorge.  Und  wenn  jemand  Sie
+fragt, haben Sie mir kein Gift verkauft, sondern Medizin."
+
+"Worauf Sie sich verlassen koennen." Der Apotheker blickte auf die Muenzen  in
+seinen Haenden, denn Scheine waren noch nicht erfunden worden.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite26 b/d/seher/haeuser/special/special/rom_jul/seite26
new file mode 100644
index 0000000..091d181
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite26
@@ -0,0 +1,51 @@
+DER FUENFTE AKT
+	DIE ZWEITE SZENE
+
+Wahrscheinlich fragen Sie sich jetzt, wo Bruder John  abgeblieben  ist,  nicht
+wahr? Sehen wir also nach, wo er sich herumtreibt.
+
+Bruder John marschierte  schnaufend  den  Berg	hinauf.  Als  er  Lawrence  im
+Kraeutergarten sah, winkte er. "Lawrence, Bruder Lawrence!"
+
+Lawrence richtete sich stoehnend auf. "Ah, da bist du ja." Er  reichte  seinem
+Ordensbruder laechelnd einen  Krug  Wein.  "Hier  trink.  Hast  du  den  Brief
+abgeliefert?"
+
+John nahm einen tiefen Zug und schuettelte den Kopf. "Nein,  so  leid  es  mir
+tut." Er griff in seine Kutte und foerderte den zerknitterten  und  nach  Wein
+riechenden Brief zutage. "Hier ist er wieder."
+
+"Hoelle und Teufel!" Lawrence ergriff den Kragen der Kutte seines  Mitbruders.
+"Weisst du, wie wichtig dieser Brief ist?"
+
+"Ich konnte nichts dafuer!", wimmerte John. "Ich war schon halb in Mantua, als
+ich durch ein Dorf kam..."
+
+"Und du konntest dem Wein da nicht widerstehen?  Nicht  ein  einziges  Mal  in
+deinem Leben?" Lawrence wedelte anklagend mit dem Brief unter Johns  Nase  hin
+und her. "Meinst du, ich rieche nicht, was du mit dem Brief angestellt hast?"
+
+"In dem Dorf war das Wasser verseucht, wir konnten nur Wein trinken,  waehrend
+wir die Kranken versorgten."
+
+"Ja, natuerlich. Und die Erde  ist  eine  Kugel."  Bruder  Lawrence  schnaubte
+veraechtlich. "Himmel, was soll ich jetzt nur tun?"
+
+"Vielleicht einen neuen Brief schreiben?", wagte John vorzuschlagen.
+
+"Ja, aber erst muss ich Julia aus dem Grab holen."
+
+"Julia?" Bruder John war entsetzt. "Das  ist  Grabschaendung!  Das  darfst  du
+nicht tun."
+
+Lawrence schuettelte den Kopf. "Julia lebt. Und genau das stand  auch  in  dem
+Brief." Mit einem Stoehnen wischte er sich den Schweiss ab. "Und sie  wird  in
+ziemlich genau drei Stunden wieder wach werden."
+
+"Aber davon hast du  mir  nichts  gesagt."  Bruder  Johns  Stimme  war  voller
+Vorwurf.
+
+"Natuerlich nicht, sonst weiss es ja binnen  einer  Stunde  die  ganze  Stadt.
+Also, besorg mir ein Brecheisen.  Ich  hole  Julia  aus  der  Gruft  und  dann
+schreibe ich einen neuen Brief an Romeo."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite27 b/d/seher/haeuser/special/special/rom_jul/seite27
new file mode 100644
index 0000000..03521e6
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite27
@@ -0,0 +1,391 @@
+DER FUENFTE AKT
+	DIE FUENFTE SZENE
+
+Azzuro	haemmerte  mit	dem  Schnabel  gegen  die  Regenrinne	ueber	Julias
+Schlafzimmer. "Wlad, aufwachen!"
+
+Wlads weinerliche Stimme ertoente von drinnen. "Meine Augen  sssind  ssstumpf,
+mein Radar issst muede und meine Sssaehne sssind  taub...  Wer	ssstoert  eine
+arme alte Fledermausss mitten in der Nacht?"
+
+"Hoer schon auf, wir sind's: Ulo und Azzuro!", rief Azzuro.
+
+Die Fledermaus streckte den Kopf heraus. "Ach ssso, und  ich  dachte  ssschon,
+esss waere die Polizzzei. Wasss gibt esss denn?"
+
+"Kommst du mit zum Friedhof?", fragte Ulo.
+
+"Picknick?"
+
+Azzuro ruelpste verhalten. "Nein, ich bekomme keinen Bissen mehr runter."
+
+"Ich hoffe, du hassst mir noch eine Fliege uebrig gelasssen?"
+
+"Keine einzige..."
+
+"Wasss?", unterbrach ihn Wlad empoert.
+
+"Ich habe keine einzige Fliege gegessen. Wir haben uns bei  Julias  Begraebnis
+schadlos gehalten."
+
+"Warum habt ihr misss nissst geweckt? Dasss haette  isss  gerne  gesehen.  Und
+aussserdem: Wasss sssoll isss dann jetssst noch auf dem Friedhof?"
+
+"Showdown!" Azzuro machte eine dramatische Gebaerde.
+
+"Wiessso?"
+
+"Romeo hat den Brief nicht bekommen, in dem steht, dass Julia noch  lebt.  Und
+Paris will sich heute abend sehen lassen, wie er Julia zuenftig bejammert."
+
+"Klingt interesssant." Wlad grinste. "Alssso, fliegen wir."
+
+Sie erreichten den Friedhof zusammen mit  Paris  und  seinem  schwatzhaftesten
+Pagen.
+
+Paris nahm dem Jungen eine Flasche Parfum ab. "Du kannst da drueben  auf  mich
+warten. Wenn jemand kommt, pfeif nach mir."
+
+"Es ist dunkel, wir sind auf dem Friedhof und ich  habe  Angst.",  wandte  der
+Page ein.
+
+"Natuerlich ist es dunkel, schliesslich ist es Nacht." Paris  schuettelte  den
+Kopf. "Wenn du Gespenster siehst, kannst du ja pfeifen."
+
+Murmelnd zog der Junge sich zurueck und hockte sich ein Stueck entfernt  unter
+eine Eibe.
+
+Wlad kicherte leise. "Passst mal auf." Sie liess  sich  von  dem  Ast  fallen,
+unter dem sie gehangen hatte und drehte eine  halsbrecherische	Runde  um  den
+Kopf des Pagen. Der  Junge  spitzte  die  Lippen  und  versuchte  zu  pfeifen,
+erreichte aber nur ein kaum hoerbares Zischen. Lachend zog  Wlad  sich	wieder
+auf ihren Ast zurueck.
+
+"Du bist gemein." Auch Ulo grinste.
+
+"Ja." Wlad strich sich mit dem Fluegel ueber die Ohren.
+
+"Guckt euch Paris an." Azzuro deutete mit dem Schnabel. "Ich haette eigentlich
+erwartet, dass er Zuschauer mitbringt."
+
+"Hat er doch." Ulo wies auf den Pagen. "Der Kleine ist nicht  nur  fuer  seine
+Feigheit bekannt, er kann auch nichts fuer sich behalten."
+
+"Oh." Azzuro nickte.
+
+Paris drueckte sich ein Tuechlein ans Gesicht und vergoss ein paar Traenen.
+
+"Sssind dasss Ssswiebeln, die isss da riessse?", fragte Wlad.
+
+"Und Ammoniak." Ulo rieb sich den Schnabel mit der Klaue. "Das treibt einem ja
+die Traenen in die Augen."
+
+"Darum hat er es ja." Azzuro kniff die Augen zusammen. "Passt auf, jetzt kommt
+ein Gedicht."
+
+Ulo und Wlad stoehnten.
+
+"Oh, Julia, schoenste Blume du, mit Traenen  decke  ich  dein  Grab..."  Paris
+zoegerte. "...mit Traenen deck' dein Grab ich zu. Blumen streu  ich  auf  dein
+Haupt, weil boeser Tod dich hat geraubt."
+
+"Aua.", kommentierte Ulo.
+
+"Reim disss, oder isss beisss disss.", stimmte Wlad ihr zu.  Grinsend  deutete
+sie auf den Pagen. "Er versssucht'sss immer noch." Sie spitzte die Lippen  und
+pfiff schrill. "Sssso geht dasss."
+
+Der Junge schaute sie aengstlich an. Paris, der festen Meinung, sein Page habe
+gepfiffen, blickte sich um und nahm einen Fackelschein wahr. Rasch steckte  er
+das Parfumflaeschchen ein und duckte sich neben einen steinernen Engel.
+
+"Ah, da issst Romeo." Wlad reckte sich so weit vor,  dass  sie  fast  vom  Ast
+fiel.
+
+Romeo nahm einen Brief aus seiner  Jackentasche  und  reichte  ihn  Balthasar.
+"Hier, den gibst du gleich morgen frueh meinem Vater."
+
+Balthasar fuehlte sich alles andere als wohl in  seiner  Haut.	"Was  hast  du
+vor?"
+
+"Ich werde mir Julias Ehering holen, denn schliesslich steht mir der als ihrem
+Mann zu, und ihr die letzte Ehre erweisen. Was sonst, meinst du?"  Romeo  nahm
+Balthasar das Stemmeisen ab. "Du musst hier verschwinden, ich will nicht, dass
+du meinetwegen in Schwierigkeiten kommst."
+
+"Ach, mit Schwierigkeiten werde ich fertig." Balthasar zuckte  die  Schultern.
+"Aber dich auf dem dunklen Friedhof allein zu lassen, das behagt mir nicht."
+
+"Vernuenftig, der Mann.", meinte Azzuro.
+
+Romeo legte seinem Diener die Hand auf die Schulter. "Mach dir um  mich  keine
+Sorgen. Verschwinde hier, und wenn ich dich erwische, dass du dich hier in der
+Naehe aufhaelst, reisse ich dir die Ohren ab, verstanden?"
+
+Balthasar nickte. "Ja,  ich  habe  verstanden.  Trotzdem:  Romeo,  mach  keine
+Dummheiten."
+
+"Ich war nie in meinem Leben vernuenftiger. Mach dich vom Acker." Er sah,  wie
+Balthasar zusammenzuckte. "Gottesacker, wollte ich sagen."
+
+Balthasar nickte ergeben, machte auf dem Absatz kehrt und blickte  auch  nicht
+zurueck, als er hoerte, wie Romeo das Grab aufbrach.
+
+Paris zitterte hinter dem Rock des  Engels  vor  Wut,  als  er	die  hallenden
+Schlaege hoerte, mit  denen  Romeo  das  Grab  oeffnete.  "Du  Grabschaender!"
+Wuetend sprang er nach vorne und zog seinen Degen.
+
+Romeo wandte sich langsam um, das Brecheisen in der  Rechten.  "Hoer  zu,  ich
+habe nicht vor, das Grab zu schaenden. Wenn du nur fuer fuenf Pfennig Verstand
+hast, machst du, dass du wegkommst."
+
+"Reicht es dir nicht, dass Julia vor lauter Kummer ueber deinen Mord an  ihrem
+Vetters Tybalt Selbstmord begangen hat? Musst du jetzt	auch  noch  ihre  Ruhe
+stoeren?" Die  Degenspitze  reckte  sich  begehrlich  Romeos  Kehle  entgegen.
+"Escalus hat dich verbannt fuer dieses Verbrechen, jetzt kommst du zurueck, um
+noch mehr Schandtaten zu begehen?" Paris Gesicht leuchtete weiss vor  Zorn  im
+schwachen Sternenlicht. "Diesmal kommst du nicht davon, das schwoere ich  dir.
+Ich nehme dich fest und morgen wird Escalus dich zum Tod verurteilen."
+
+"Ich bin laengst  zum  Tod  verurteilt,  Mann."  Romeo  verzog  bedauernd  das
+Gesicht. "Ich bin nur hier, weil ich sterben will,  verstehst  du  das  nicht?
+Also tu mir und dir  den  Gefallen  und  zwing	mich  nicht,  dich  auch  noch
+umzubringen." Er hob das Brecheisen.
+
+Paris antwortete nicht, sondern hieb wild nach Romeo.
+
+Der Page hoerte den Laerm und richtete sich abrupt auf, dann drehte er sich um
+und rannte lauthals nach der Wache schreiend den Huegel hinunter.
+
+"Als Fechter kann er noch etwas lernen." Azzuro schuettelte den Kopf.
+
+"Elegant ist er nicht, das stimmt."  Ulo  kraechzte  leise,  als  Paris  einen
+Schlag mir dem Brecheisen nur mit knapper Not abfing. Der naechste Schlag  kam
+schneller, als Paris es erwartet hatte. "Und lernen wird er auch nichts mehr."
+
+"Sssie haetten sssisss ruhig ein bisssen mehr anstrengen koennen.", beschwerte
+sich Wlad.
+
+Paris stoehnte, als Romeo sich ueber ihn beugte. "Begrab mich  neben  Julia.",
+fluesterte er.
+
+Romeo nickte. "Versprochen." Als Paris starb, sah er  genauer  hin.  Er  wurde
+blass, als er Paris erkannte. "Verdammt."
+
+"Julia sollte ihn heiraten.", fluesterte Azzuro leise in Romeos Ohr.
+
+Romeo stampfte mit dem Fuss auf. "Verdammt, verdammt, verdammt.  Wieso  musste
+ich ausgerechnet ihn erwischen?"
+
+"He, Romeo, ich muss dir etwas sagen." Ulo huepfte  auf  einen  Vorsprung  der
+Gruft.
+
+Romeo blickte auf. "Tu's nicht."
+
+"Doch." Aufgeregt trippelte Ulo hin und her. "Julia ist..."
+
+"Tot und beerdigt, ich weiss.", unterbrach Romeo sie. "Und ich bin auch  schon
+tot. So gut wie." Er seufzte und hob Paris hoch. "Und  Paris  wird  von  einem
+Toten beerdigt. Was fuer eine Ironie des Schicksals." Er lachte bitter.
+
+"Nein, Julia ist..."
+
+"...unten.", beendete Azzuro den Satz fuer Ulo und warf  ihr  einen  drohenden
+Blick zu. Kurz entschlossen landete Wlad auf  Ulos  Kopf  und  hielt  ihr  den
+Schnabel mit den Fluegeln zu.
+
+Verwirrt sah Romeo zu Ulo hoch, die  sich  heftig  gegen  Wlad	wehrte.  "Eine
+Fledermaus hockt auf einer Kraehe. Das ist wohl ein Vorzeichen." Er schulterte
+Paris Leichnam und ging vorsichtig die Stufen der Gruft hinunter.
+
+Unten sah Romeo sich schaudernd um und legte dann Paris neben Tybalt  auf  den
+Stein. "So, jetzt hast du einen Platz, der  dir  angemessen  ist.  Denn direkt
+neben Julia liege ich - und niemand anders." Er beugte sich ueber  Julia.  "Du
+hast ja noch ganz rote Lippen." Er laechelte und kuesste sie. "Schade, dass es
+kein Maerchen ist, sonst wachtest du  jetzt  auf."  Nachdenklich  wog  er  die
+Phiole mit dem Gift in der Hand. "Wo du bist, will ich auch hingehen,  Julia."
+Er trank und sein Laecheln vertiefte sich, bevor  er  leblos  in  Julias  Arme
+sank.
+
+Draussen war es Ulo endlich gelungen, Wlad herunterzuschubsen. "Du..., du..."
+
+"Ja?", fragte Wlad freundlich.
+
+"Deinetwegen ist..."
+
+"Still, da kommt Bruder Lawrence.", unterbrach Azzuro den Streit.
+
+Lawrence beugte sich nachdenklich ueber das Blut und das Brecheisen am	Boden.
+"Was ist denn hier passiert?"
+
+"Romeo war hier.", klaerte Azzuro ihn auf.
+
+"War?" Bruder Lawrence sah ihn entsetzt an. "Ich hatte schon  den  ganzen  Tag
+ein ungutes Gefuehl." Er seufzte. "Wenigstens muss ich jetzt die  Gruft  nicht
+mehr aufbrechen." Behutsam stieg er hinunter.
+
+Mit Grausen liess Lawrence den Blick ueber die Leichen schweifen, dann blickte
+er zu Azzuro auf. "Wie lange?", fragte er leise.
+
+"Ungefaehr eine halbe Stunde."
+
+Bruder Lawrence fluchte so herzhaft, dass Wlad das Maul  offen	stehen	blieb.
+"Fuer einen Priessster hat er einen interessssanten Wortsssatsss.", meinte sie
+beifaellig.
+
+"Dass du das ja nicht petzt." Bruder Lawrence drohte ihr mit dem Finger.
+
+"Ich?" Wlad versuchte, unschuldig auszusehen.
+
+Julia stoehnte und klappte muehsam ein Auge auf. "Wo...?"  Sie  oeffnete  auch
+das andere Auge. "Oh, ja. Eine Gruft. Meine Gruft." Als  sie  sich  aufsetzte,
+fiel ihr Blick auf  Lawrence,  der  sie  mit  schreckgeweiteten  Augen	ansah.
+"Bruder Lawrence. Gut. Wo ist Romeo?"
+
+Wortlos deutete Lawrence auf die Leiche. Er raeusperte sich, als  Julia  einen
+Schrei ausstiess. "Julia, wir sollten hier verschwinden. Ich  bringe  dich  in
+ein Kloster."
+
+"Kloster? Ich will bei Romeo sein!"
+
+"Sei vernuenftig, das geht doch nicht. Er ist tot."
+
+"Dann will ich das auch sein." Sie betrachtete die Phiole in  Romeos  Fingern.
+"Leer. Du Geizhals, wie soll ich jetzt zu dir kommen?"
+
+Romeo reagierte nicht auf ihre Vorwuerfe, doch eine der drei  Tauben  auf  der
+Wolke ueber ihnen sah sehr betruebt aus.
+
+"Julia..." Bruder Lawrence fasste sie an der Schulter.
+
+"Nein." Julia zog Romeos Dolch  aus  der  Scheide,  winkte  den  Kraehen,  der
+Fledermaus und Bruder Lawrence zu und erstach sich.
+
+"Sie hat gewunken." Ulo schluckte.
+
+"Warte, bis wir sie hinter der Buehne sehen. Das gibt Aerger.", meinte Azzuro.
+
+"KANN ICH JETZT?"
+
+"Bedien disss, sssie warten ssson."
+
+"DANKE."
+
+Bruder Lawrence hoerte einen Wachmann und blickte sich wild um. "Ich muss  weg
+hier, das ist mir unheimlich." Er raffte seine Kutte zusammen und kletterte so
+geschwind aus der Gruft, dass Ulo anerkennend pfiff.
+
+Der  Wachhauptmann  kam  zusammen  mit	Paris  Pagen  herab.  "Was  fuer  eine
+Scheisse." Er seufzte und ging zu seinen Kollegen nach oben. "Durchkaemmt  den
+Friedhof. Unten liegen drei Leichen, alle noch	warm.  Der  Taeter  kann  noch
+nicht weit sein." Er deutete auf einen Gefreiten. "Du gehst und  holst  Fuerst
+Escalus, die Capulets und die Montagues."
+
+"Wer sind die Leichen?", wollte sein Sergeant wissen.
+
+"Graf Paris, Romeo Montague und - haltet euch fest - Julia Capulet."
+
+"Julia?" Der Sergeant runzelte die Stirn. "Wie kann sie noch  warm  sein?  Ich
+denke, sie ist schon seit zwei Tagen tot?"
+
+"Nein, ist sie nicht.", kraechzte Ulo.
+
+"Tja.", meinte der Hauptmann. "Denkt  was  ihr  wollt,  aber  die  Kraehe  hat
+Recht."
+
+Escalus, der sich zwar	die  Amtsrobe  ueber  die  Schultern  geworfen	hatte,
+allerdings in der Aufregung vergessen hatte, sich auch Schuhe anzuziehen, sah
+sich verwirrt um. "Womit hat die Kraehe Recht?"
+
+"Hier in der Gruft liegen drei frische Leichen: Romeo, Julia und  Paris."  Der
+Hauptmann nahm Haltung an.
+
+Frau Capulet, die sich gerade einen Weg durch die  versammelte	Menge  bahnte,
+schrie auf. "Meine Julia stirbt zweimal?"
+
+Auch  Escalus  konnte  es  kaum  glauben.  "Also  von  Anfang.  Wer  hat   wen
+erschlagen?"
+
+Bruder Lawrence, der zwischen zwei Wachmaennern herangeschleppt wurde wie  ein
+Sack Waesche, zupfte sich verlegen an der Nase. "Ich sollte  wohl  etwas  dazu
+sagen."
+
+"Vermutlich." Fuerst Escalus nickte dem Moench zu. "Wenn Sie es koennen."
+
+Bruder Lawrence seufzte. "Ich glaube, ich bin an allem schuld. Also: Romeo und
+Julia haben am Dienstag heimlich geheiratet."
+
+Sowohl die Familie Capulet als auch Herr Montague stoehnten auf  und  blickten
+unglaeubig in die Runde.
+
+Herr Montague sah Fuerst Escalus  aus  grossen,  traurigen  Augen  an.	"Nicht
+genug, dass meine Frau heute gestorben ist, weil sie es nicht ertragen konnte,
+dass Romeo verbannt ist, stirbt jetzt auch noch mein Sohn."
+
+Romeo, der neben Julia auf  der  weichen  Wolke  sass  und  die  Taubengestalt
+abgelegt hatte, um abwesend Ulos Gefieder zu kraulen,  lachte  auf.  "Das  ist
+starker Tobak fuer sie."
+
+Julia legte ihre Hand auf seinen Arm. "Das mit deiner Mutter tut mir leid."
+
+Lawrence nickte den Eltern zu. "Es stimmt. Und am Montag hat Romeo auch Tybalt
+erstochen. Deshalb musste er noch vor Sonnenaufgang am Dienstagmorgen aus  der
+Stadt  verschwinden.  Ich  nehme  doch	stark  an,  dass  die	Ehe   trotzdem
+rechtsgueltig ist. Polly weiss das sicher besser als ich."
+
+Julia kicherte und wurde rot.
+
+"Weil Herr Capulet dann am Dienstag Julia mit Paris verlobt hat, kam Julia  zu
+mir und wollte, dass ich ihr helfe." Bruder  Lawrence  zuckte  die  Schultern.
+"Ich wusste nichts besseres, als ihr ein Schlafmittel zu geben,  dass  sie  am
+Mittwochabend nehmen sollte, damit alle  glauben,  sie	habe  sich  vergiftet.
+Gleichzeitig habe ich einen Mitbruder mit  einem  Brief  zu  Romeo  geschickt.
+Leider hat Herr Capulet dann Dienstag beschlossen, dass die Hochzeit  zwischen
+Julia und Paris bereits  am  Mittwoch  stattfinden  soll,  weshalb  Julia  das
+Schlafmittel  schon  am  Dienstag  nahm.  Eigentlich  haette  die  Frist  noch
+ausgereicht, um Romeo zu warnen, dass Julia lebt, aber mein Bruder hat mir den
+Brief zurueckgegeben." Lawrence seufzte. "Heute abend kam ich her,  damit  ich
+Julia aus der Gruft holen konnte und fand Romeo und Paris bereits tot vor."
+
+Escalus nickte bedaechtig. "Gut. Oder vielmehr, nicht gut. Wer kann mir sagen,
+wie Paris hier hin kam?"
+
+Paris Page trat vor. "Ich habe ihn begleitet, weil er hier Blumen  auf  Julias
+Grab streuen wollte."
+
+"Dabei hat Romeo ihn unterbrochen.",  fuegte  Balthasar  hinzu.  "Als  ich  am
+Donnerstag hier war, um fuer Romeo Neuigkeiten zu  erfahren,  hoerte  ich  von
+Julias Tod. Ich ritt also nach Mantua zurueck und Romeo, nehme ich an,	kaufte
+sich da ein Gift. Dann kamen wir hierher, wobei  Romeo	mich  glauben  machte,
+dass er nur Julia die letzte Ehre erweisen wollte. Er schickte mich weg,  aber
+ich sah noch, dass er und Paris aneinandergerieten. Einen Brief  hat  er  noch
+geschrieben." Er reichte Escalus den Umschlag.
+
+"Bist der bessere Fechter." Paris klopfte Romeo auf die Schulter.
+
+"Wir haben eine Ewigkeit Zeit, deinen Stil zu verbessern.", antwortete Romeo.
+
+Escalus las das Schreiben und nickte wieder. "Das ist ein  Abschiedsbrief,  in
+dem Romeo selbst schreibt, dass er mit Julia verheiratet war."
+
+Herr Montague streckte impulsiv seine Hand nach Herrn Capulet aus.  "Verdammte
+Scheisse, wie?"
+
+"Ja, das  auch."  Misstrauisch  blickte  Herr  Capulet  auf  die  Hand  seines
+Erzfeindes,  entdeckte	kein  Messer  und  schloss  ihn  dann  in  die	 Arme.
+"Versoehnen wir uns?"
+
+Noch ehe Herr Montague antworten konnte, hob Escalus die  Hand.  "Und  ob  ihr
+euch versoehnt. Ansonsten stecke ich euch beide in einen Sack und schlage  mit
+einem Knueppel drauf."
+
+"Traefe nicht die Verkehrten.", stimmte ihm Herr Montague zu.
+
+"Das haette Escalus lieber gleich tun sollen.", maulte Ulo.
+
+"Hauptsache, es ist endlich Ruhe." Azzuro schmiegte sich an  Julia  und  liess
+sich von dieser kraulen.
+
+"Jawohl." Julia laechelte. "Frieden auf Erden und das Himmelreich  fuer  Romeo
+und mich."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite28 b/d/seher/haeuser/special/special/rom_jul/seite28
new file mode 100644
index 0000000..20aeeae
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite28
@@ -0,0 +1,16 @@
+EPILOG
+
+Hier verlassen wir nun Verona, das sich wie jede andere anstaendige Stadt  nun
+nur noch mit einem grossen Buchstaben schreibt, bis wir  uns  wiedersehen,  um
+die Geschichte noch einmal zu erleben.
+
+Ich hoffe, Sie, liebe Leserin, lieber Leser, nehmen  den  Turtelkraehen  ihren
+Vorwitz nicht uebel, denn die Goetter der Mythenwelt haben beschlossen,  ueber
+ihre Verfehlungen hinwegzusehen. Also wollen wir nicht kleinlicher sein.
+
+Es bleibt noch hinzuzufuegen, dass sie, nun, da sie gestorben sind, bis zu dem
+Tag im Ferienparadies der Mythen ihre  Flitterwochen  verleben,  bis  sie  das
+naechste Mal gebraucht werden.
+
+Auf Wiedersehen.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite3 b/d/seher/haeuser/special/special/rom_jul/seite3
new file mode 100644
index 0000000..2239446
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite3
@@ -0,0 +1,1023 @@
+DER ERSTE AKT
+	  DIE ERSTE SZENE
+An dieser Stelle sollte eigentlich ein Chor auftreten, denn so gehoert es sich
+fuer eine Tragoedie, doch im Zuge der  allgemeinen  Sparmassnahmen  wurde  der
+Chor gestrichen und Ulo und  Azzuro  bekamen  zusaetzlich  zu  ihrer  normalen
+Aufgabe die Pflicht aufgebuerdet, Sie, verehrte LeserInnen, ueber die  jeweils
+aktuelle Lage auf dem Laufenden zu  halten,  fuer  den	Fall,  dass  Sie  eine
+Zusammenfassung oder einen Kommentar  wuenschen.  Auch	wenn  Sie  dies  nicht
+wollen, werden die beiden Sie informieren, denn dafuer werden sie schliesslich
+bezahlt.
+
+Ulo holte tief Luft und setzte sich in Positur. "Two households, both alike in
+dignity, in fair Verona, where we lay our scene..."
+
+"Ach, sei ruhig." knurrte Azzuro.
+
+"Was denn, was denn? Sonst stehst du doch auf die Klassiker?"  Ulo  mochte  es
+gar nicht, mitten in einem Zitat unterbrochen zu werden.
+
+"Denk doch mal an die Leser!"
+
+"Und Leserinnen."
+
+"Ja, die auch. Sie wollen doch nicht ein englisches Zitat lesen."
+
+"Woher weisst du das?"
+
+Bitte stellen Sie sich vor, liebe Leserin, lieber Leser, dass Sie  im  Theater
+sitzen und sich Ulo nun an Sie wendet. "Sagen Sie mir, was Sie hoeren  wollen.
+Jaja, auch Sie, dahinten in der vierzigsten Reihe Parkett, ganz links."
+
+Nun, ich gehe davon aus, dass Sie, auch der Herr ganz links in der vierzigsten
+Reihe Parkett, mit Shakespeare vertraut sind.  Daher  erspare  ich  ihnen  das
+Zitat und fasse den Inhalt kurz zusammen. Gerade stoesst mich  Azzuro  an  und
+verweist auf die Gewerkschaft. Sie entschuldigen mich  bitte,  ich  ueberlasse
+die Zusammenfassung unseren beiden Turtelkraehen.
+
+"Du oder ich?" Ulo ist weiblichen Geschlechts und immer  bereit,  Azzuro  eine
+unangenehme Aufgabe zu ueberlassen.
+
+"Egal."
+
+"Dann du." Meistens hat Ulo Erfolg mit ihrer Taktik.
+
+"OK. Wir finden uns in Verona wieder, Zeit  irgendwann.  Zwei  Familien  leben
+hier."
+
+"Es sind bestimmt noch mehr."
+
+"Das ist doch nicht wichtig, oder?  Es  geht  nur  um  die  Capulets  und  die
+Montagues."
+
+"Wirklich?"
+
+"Nein, nicht wirklich, aber die beiden sind die  wichtigsten,  denn  sie  sind
+schuld daran, dass sich die ganze Stadt in  zwei  feindliche  Lager  gespalten
+hat."
+
+"Das ist doch Unsinn. Schuld daran ist niemand anderer als der Autor."
+
+"Ulo!"
+
+"Schon gut."
+
+Azzuro strich mit dem Schnabel	ueber  das  Gefieder.  "Aus  irgendeinem  lang
+vergessenem Grunde liegen die beiden Familien in  Fehde  miteinander  und  die
+jungen Leute einer jeden Generation finden immer wieder die Moeglichkeit,  die
+Fehde nicht in Vergessenheit geraten zu lassen.  Im  Laufe  der  verschiedenen
+Hochzeiten, Geburten und Taufen haben sich alle Bewohner Veronas in irgendeine
+Beziehung zueinander gesetzt und da die Capulets und die Montagues die	beiden
+aeltesten Familien sind, haben alle Buerger auch eine Beziehung zu  der  einen
+oder anderen Familie. Sei es, dass Onkel Adalbert der Vater der Frau war,  die
+der junge Montague vor zwanzig Jahren ins Unglueck gestuerzt hat, weil er  ihr
+die Rosen nicht gegeben hat, die doch der Bruder  des  Cousins	seines	besten
+Freundes ihm ausdruecklich fuer diese Frau uebergeben hatte."
+
+"Du schweifst ab.", tadelte Ulo.
+
+"Aber wie soll ich es denn sonst erklaeren?"
+
+"Ist doch einfach: Verona ist ein Dorf."
+
+"Oh."
+
+Triumphierend blickte Ulo auf ihren Gefaehrten. "Und ein Dorf ist ein Dorf ist
+ein Dorf ist ein Dorf."
+
+"Also gut. Verona ist ein Dorf, das in zwei Teile  gespalten  ist.  Jeder  hat
+einen Groll gegen jeden anderen."
+
+"Und jedE hat einen Groll gegen jedE andere."  Ulo  hatte  die  bemerkenswerte
+Faehigkeit, Grossbuchstaben auszusprechen.
+
+"Ja." Azzuro seufzte. "Wie auch immer. Die beiden Familien  haben  jede  einen
+Sproessling. Romeo Montague ist ein huebscher Bengel von achtzehn Jahren,  der
+eines Tages das Geld und die Fehde von seinem Vater  erben  wird.  Julia,  die
+einzige Tochter des Hauses Capulet, wird einmal bildhuebsch, wenn man sie noch
+ein paar Jahre auf die Weide schickt."
+
+"Wie kannst du das sagen?"
+
+"He, sie ist erst dreizehn!"
+
+"Alt genug, um demnaechst verheiratet zu werden. Und  bereits  jetzt  schoener
+als Helena."
+
+"Was hat Helena damit zu tun?" Azzuro duckte sich sicherheitshalber.
+
+"Helena von Troja."
+
+"Ach so, die. Jaja, meinetwegen ist sie schoener als Helena."
+
+"An wen dachtest du denn?", verlangte Ulo zu wissen.
+
+_Sie_ _hat_ _schon_ _wieder_ _diesen_ _Tonfall_,  dachte  Azzuro.  "Niemanden,
+selbstverstaendlich. Ausser dir sehe ich doch  keine  Frau  an,  Liebste,  das
+weisst du doch."
+
+"Ist auch besser so."
+
+Zwei Maenner betraten den Platz und Azzuro deutete mit dem  Schnabel  auf  die
+beiden.
+
+"Da. Samson und Gregory."
+
+"Was ist mit ihnen?"
+
+"Gleich werden sie mit ihren Saebeln rasseln und..."
+
+"Scht!"
+
+Verlegen blickte Azzuro auf die Frau, die sich gerade ueber den Brunnen beugte
+und Wasser schoepfte. "Ups."
+
+"Du weisst, dass _sie_ uns nicht hoeren duerfen."
+
+"Ja.", antwortete Azzuro kleinlaut. "Ich weiss."
+
+Hier unterbreche ich noch einmal  den  Lauf  der  Handlung,  um  Ihnen,  liebe
+Leserin, lieber Leser, zu erklaeren, was Turtelkraehen nun eigentlich sind.
+
+Turtelkraehen, wie der Name vermuten laesst, treten immer paarweise  auf.  Ein
+Maennchen, in unserem Fall  Azzuro,  und  ein  Weibchen,  Ulo,	wie  Sie  sich
+bestimmt schon gedacht haben. Was Sie nicht  wissen  koennen,  ist,  dass  die
+Turtelkraehen ein Geschenk der Goetter sind, und  somit  keineswegs  das,  was
+sich ein normaler Mensch wuenschen wuerde. Sie gehoeren zu  jeder  Geschichte,
+in der Liebende auftauchen. Fragen Sie mich nicht, warum, denn das  haben  die
+Goetter vergessen zu erwaehnen. Die Aufgabe  der  Turtelkraehen  ist  es,  die
+Geschichte zu verfolgen und den Goettern der Sagenwelt Bericht	zu  erstatten.
+Auch dies ist eine Funktion, die von den Goettern unerklaert blieb,  doch  ich
+wage die Vermutung, dass Goetter zwar allwissend sind, aber dennoch  nur  zwei
+Augen haben, die noch dazu nicht unabhaengig voneinander funktionieren.  Oder,
+um es anders auszudruecken, die Goetter sind wie Kaufhausdetektive, haben ihre
+Augen immer nur auf einem Bengel in zerschlissenen Jeans und haben  daher  die
+Turtelkraehen als eine Art Videoueberwachung installiert. Geschichten sind wie
+das Endlosband einer telefonischen Warteschleife. Kennen Sie das?  Sie	warten
+darauf,  endlich  eine	Verbindung  zu	bekommen  und  hoeren  sich   schlecht
+interpretierte Musik in noch schlechterer Qualitaet an, die immer  wieder  von
+einer mehr oder minder freundlichen  Stimme  mit  _Bitte_  _warten_,  _please_
+_hold_	_the_  _line_  unterbrochen  wird.  Die  Unterbrechung	ist  noch  das
+angenehmste am ganzen Band. Wann immer diese Pause kommt, die  andeutet,  dass
+das Band nun ein Ende erreicht hat, steigt Ihre  Spannung  und	Sie  erwarten,
+gleich eine vielleicht nicht ganz  so  freundliche,  dafuer  aber  menschliche
+Stimme zu hoeren, doch in Wahrheit spult sich das Band	nur  zurueck,  um  dem
+Anrufer die Langeweile zu verkuerzen. Falls Sie sich fragen,  warum  das  Band
+dann endlos ist, werden Sie weiter ratlos bleiben muessen, denn darauf	wissen
+selbst die Goetter keine Antwort. Bekanntermassen hatten die  Goetter  ohnehin
+nicht viel damit zu tun, die Warteschleife zu  erfinden,  dieses  zweifelhafte
+Verdienst ist das der Hoelle. Damit soll Ihnen auch nur nahe gebracht  werden,
+dass eine Geschichte endlos weitergeht, auch, wenn sie	ihr  scheinbares  Ende
+erreicht hat. Sobald der eine  Mensch  die  Warteschleife  verlaesst  und  der
+naechste vom Besetztzeichen der  Langeweile  zur  Kurzweil  der  Warteschleife
+vorrueckt, beginnt die Geschichte von vorne.
+
+So erklaert sich, dass Ulo und Azzuro reichlich Zeit  hatten,  die  Geschichte
+kennenzulernen. Waehrend sie fuer die Akteure unseres  Dramas  jedes  Mal  neu
+ist, kennen die Turtelkraehen sie auswendig. Warum sie dann trotzdem weiterhin
+den Fortgang beobachten muessen, bleibt unklar, obwohl es vermutlich  mit  der
+Gewerkschaft zusammenhaengt.
+
+Ulo und Azzuro sitzen auf dem Brunnen und betrachten die Tragoedie,  die  sich
+vor  ihren  Augen  abspult,  wie  ein  altes  Ehepaar  die  fuenfundzwanzigste
+Wiederholung von Vom Winde Verweht anschaut: Jeder (und jede) weiss, wann  der
+(oder die) andere Taschentuecher benoetigt und wann er (oder  sie)  dem  (oder
+der) anderen die Augen zuhalten muss. Leider haben die Turtelkraehen auch eine
+andere Eigenschaft des alten Ehepaares: Wann immer  eine  besonders  gelungene
+Szene kommt, weisen sie einander im Voraus darauf hin und verderben sich damit
+jede Spannung.
+
+Bitte verzeihen Sie die umstaendliche Klammersetzung, aber Ulo besteht darauf.
+Sie, liebe Leserin, lieber Leser, koennen sich mit Sicherheit vorstellen,  was
+geschaehe, wenn ein Kinobesucher Scarlett verriete, wie widerlich  Rhett  sich
+ihr gegenueber verhalten wird. Sie koennen es, nicht wahr? Und	daher  ist  es
+Turtelkraehen strikt verboten, sich  ueber  Geschehnisse,  die	der  werdenden
+Mutter Zukunft noch nicht  einmal  Wehen  verursachen,	zu  unterhalten.  Ganz
+undenkbar, wenn sie gar das Geschlecht	des  Kindes  verrieten,  dass  Zukunft
+gebaeren wird!
+
+Kehren wir also zurueck zu unserer Geschichte und hoffen, dass Ulos  Ermahnung
+Azzuro von weiteren Verfehlungen abhalten wird.
+
+Wir erinnern uns, dass gerade Samson und Gregory auf den Platz	kamen.	Samson
+pfiff einer jungen Frau hinterher und  handelte  sich  einen  Rippenstoss  von
+seinem Freund ein.
+
+"Lass die Maedels in Ruhe, Sam.", verlangte Gregory.
+
+"Ach, komm schon, sie ist wirklich niedlich."
+
+Gregory schaute ueber die Schulter und konnte dem nur zustimmen. "Hey,  wonach
+mir jetzt wirklich der Sinn stuende, das waere der eine oder andere Capulet."
+
+Samson grinste. "Am Spiess?"
+
+"Waere doch was, nicht?"
+
+"Und dann haeuten wir ihre Frauen?"
+
+"Faellt dir nichts besseres ein?", fragte Gregory.
+
+"Ich wuesste nichts besseres, als eine Frau mit meinem Spiess zu haeuten."
+
+"Oh, _die_ Haut meinst du. Du solltest Maedchen sagen."
+
+Ulo schuettelte sich und liess etwas fallen, was hier auf mit  Ruecksicht  auf
+jugendliche Leser (entschuldige  Ulo)  und  Leserinnen	nicht  naeher  benannt
+werden soll.
+
+"Die Kraehe hat mir auf den Kopf ge..." Samson holte ein nicht allzu  sauberes
+Taschentuch heraus und wischte sich ueber die beginnende Glatze.
+
+"Das wird dich lehren, du Mistkerl!", kraechzte Ulo.
+
+"Und sie  lacht  auch  noch."  Samson  fluchte.  Er  hatte  Ulos  Worte  nicht
+verstanden, da diese klug genug gewesen war, Kraehisch zu sprechen,  doch  der
+Tonfall war unverkennbar.
+
+"Du spinnst, mein Freund. Seit wann lachen Kraehen? Steck den Saebel weg."
+
+"Ich bring das Vieh um!" Samson war keineswegs der Mann, der  seine  Emotionen
+kontrollieren konnte oder wollte.
+
+"Dann lass das Ding halt draussen, aber da drueben findest du  ein  groesseres
+Ziel." Samson grinste breit und entbloesste ein Gebiss, das einen Zahnarzt ein
+Jahr lang ernaehrt haette. "Capulets! Lasst sie mir!"
+
+"Wo  mag  er  das  nur  aufgeschnappt  haben?   Im   Original   steht   nichts
+dergleichen.", bemerkte Azzuro verdriesslich.
+
+"Halt! Wenn du so einfach losrennst, ist das nicht gut. Vergiss nie, dass  wir
+das Gesetz auf unserer Seite haben wollen." Gregory hielt seinen Kameraden  am
+Aermel zurueck.
+
+"Wen?"
+
+"Das Gesetz. Wenn du losstuermst und  sie  einfach  angreifst,  sind  wir  die
+Boesen und bekommen Schwierigkeiten. Also lass mich machen, ich sorge  dafuer,
+dass wir in Notwehr handeln."
+
+Samson	begriff  nicht,  was  Gregory  mit  Gesetzen  wollte,  aber  das  Wort
+Schwierigkeiten  weckte  in  ihm  jene	Vorsicht,  die	allen  untergeordneten
+Soldaten dieser und aller anderen  Welten  gemein  ist.  Schwierigkeiten,  das
+hatte etwas mit wuetenden Vorgesetzten, Latrinenputzen und Wochenenddienst  zu
+tun, davon hielt man sich besser fern.
+
+"Zeig ihm den Stinkefinger.", schlug Samson vor.
+
+"Aber nicht doch, dann haetten  wir  das  Gesetz  nicht  auf  unserer  Seite."
+Gregory schuettelte  vehement  den  Kopf  und  streckte  nichtsdestotrotz  den
+Mittelfinger in unmissverstaendlicher Geste vor.
+
+"Hey, aber du zeigst ihm den Finger? Ich denke, das duerfen wir nicht?" Samson
+verstand seinen Kameraden nicht.
+
+"Wie? Zeigst du den Stinkefinger?", verlangte auch Abraham zu wissen.
+
+"Jawoll, ich zeige den Stinkefinger.", grinste  Gregory  und  betrachtete  die
+Hand Samsons, der ihm alles nachahmte.
+
+"Zeigst du ihn am Ende _mir_?" Abraham freute sich sichtlich auf die Antwort.
+
+Samson fluesterte: "Ist das Gesetz auf unserer Seite, wenn ich ja sage?"
+
+"Nein, bloss nicht." Gregory stupste seinen Freund in die  Seite.  "Lass  mich
+machen."
+
+"Also, ich zeige den Stinkefinger, aber wie kommst du nur darauf, dass ich ihn
+dir zeigen koennte? Mein armer Finger! Das taete ich ihm niemals an!"
+
+"Wie war das? Sucht ihr Streit?"
+
+"Welch eine Unterstellung!"
+
+"Schade." Abraham grinste. "Ich sehe eine gute  Gelegenheit,  euch  beiden  zu
+zeigen, dass Capulet alle Male besser ist als Montague."
+
+"Pah." Gregory schnaubte.
+
+"Bloedsinn,  die  spinnen,  die  Capulets.",  fuegte   Samson   aus   tiefster
+Ueberzeugung hinzu.
+
+"Schon wieder!", empoerte sich Azzuro.
+
+"Oh doch, das ist es."
+
+"Ist es nicht."
+
+"Doch."
+
+"Nein."
+
+...
+
+Liebe Leserin, lieber Leser, setzen Sie in Gedanken  dieses  Auftrumpfen  noch
+ein wenig fort. In Anbetracht  der  Tatsache,  dass  wir  es  mit  erwachsenen
+Maennern zu tun haben, wollen wir uns nicht etwa Kinder vorstellen,  die  sich
+ueber die Groesse der  Autos  ihrer  Vaeter  streiten,	sondern  zum  Beispiel
+Fussballfans, die sich nicht darueber einig werden koennen, welcher Verein  es
+eher verdient hat, den Pokal zu gewinnen.  Wie	jeder  gestandene  Fussballfan
+Ihnen bestaetigen kann, haengt der Gewinn des Pokals ja in  keiner  Weise  von
+den Faehigkeiten der Spieler oder des Trainers ab, sondern ausschliesslich von
+der Intensitaet des Glaubens der begeisterten Zuschauer. Fussball,  so	wissen
+wir, war zu jener Zeit noch nicht erfunden, doch wo  immer  zwei  Mannschaften
+sind, finden sich auch Fans. Den Rest zu  erklaeren  ueberlasse  ich  weiseren
+Menschen.
+
+Eines wollen wir aber nicht vergessen: Wo immer sich zwei Trupps von Hooligans
+gegenueberstehen, ist der Fanbetreuer nicht  weit  und	versucht,  Frieden  zu
+stiften. Gerade  erinnerte  Samson  Gregory  an  seinen  Lieblingsziegelstein.
+Verzeihung, seinen besten Saebelstreich, als Benevolio	das  nahende  Unglueck
+sah.
+
+"Hoert ihr auf!" Benevolio zog seinen eigenen Saebel und  schlaegt  damit  die
+der Kombattanten herunter. "Ihr habt sie wohl nicht mehr alle. Frieden  jetzt,
+sonst hol ich die Polizei."
+
+"Hol die Bullen,  mein  Junge,  aber  steck  den  Zahnstocher  weg.",  knurrte
+Gregory.
+
+Benevolio ruempfte die Nase ueber die ungepflegte Ausdrucksweise.  "Lern  erst
+mal vernuenftig zu reden, dann hoere ich dir vielleicht  auch  zu.  Und  jetzt
+friedlich hier!"
+
+Ulo streckte den Kopf vor. "Ist er nicht ein Schaetzchen, unser Benevolio?"
+
+"Ein wahrer Held.", meinte  Azzuro  hoehnisch.  "Nicht,  dass  er  viel  damit
+erreichte."
+
+"Wieso?"
+
+"Guck mal da rueber. Tybalt kommt."
+
+"Der Raufbold der Capulets? Na, dann haben wir ja gleich den  schoensten  Tanz
+hier."
+
+Ulo reckte den Kopf.
+
+Tybalt fragte nicht lange, sondern zog seinen Saebel blank. "Ja, was haben wir
+denn hier? Benevolio Montague, schau an. Und er hat  auch  noch  zwei  Freunde
+dabei. Das macht drei gegen zwei, wenn ich mich nicht  taeusche.  Schaemt  ihr
+euch nicht, oder habt ihr etwa Angst?"
+
+"Schau an, er kann rechnen." Ulo lachte kraechzend.
+
+"Von Muffensausen kann keine Rede sein.", meinte Samson.
+
+Benevolio schenkte Tybalt einen veraechtlichen Blick. "Nein, keine Angst, mein
+Lieber, nur der Wunsch, Frieden zu stiften."
+
+"Indem  ihr  unsere  Leute  _umbringt_?  Nicht  gerade  die  feine  Art,  Herr
+Benevolio. Ich werde Ihnen schon zeigen, wie  friedlich  wir  sind,  wenn  wir
+angegriffen werden."
+
+Sie kennen alle die schoenen Schlaegereien, die zum Western gehoeren, wie  der
+rechte Schuh zum linken? Man pruegelt sich und achtet nicht weiter darauf, wen
+man gerade trifft, schliesslich ist jeder, der sich in die Naehe der  Keilerei
+wagt entweder beteiligt oder selber schuld. Harmlose Passanten gibt es	nicht,
+wer seine Zaehne behalten will, hat nur eine Moeglichkeit, dies zu  erreichen:
+Er muss tot spielen und sich der Gefahr aussetzen, harte Tritte  gegen	weiche
+Teile zu kassieren oder er muss schneller zuschlagen als alle anderen.	Binnen
+kuerzester Frist ist eine Schlaegerei im Gange, die ihresgleichen sucht.
+
+Wie immer in solchen Faellen platzt die Polizei mitten	hinein,  und  da  ihre
+Angehoerigen ebenfalls Menschen sind, koennen sie nicht umhin, bei  der  einen
+Partei ein wenig mehr Frieden zu stiften als bei der anderen. Halten  wir  der
+Polizei, insofern man fuer Individuen einen solch  generalisierenden  Ausdruck
+verwenden kann, zugute, dass sie bei all ihrer Menschlichkeit und  Fehlbarkeit
+nichtsdestoweniger auf Ausgeglichenheit achtet, so dass fuer  jeden  Anhaenger
+der Capulets auch einer der Montagues eingestellt wird. Oder, um es ein  wenig
+polemischer auszudruecken, keine der beiden Seiten bekommt weniger Polizei als
+die andere.
+
+Herr Capulet, der gerade seine Gattin auf einem Spaziergang begleitete,  hatte
+seine helle Freude, als er die wunderschoene Pruegelei sah. "Das  gibt  es  ja
+nicht! Ca-Pu-Let, Ca- Pu-Let!"
+
+"Idiot. Und guck nur, er hat einen Morgenrock  an.",  kommentierte  Ulo.  "Der
+Kerl ist wirklich senil."
+
+"Lass ihn doch. Der alte Knochen kann nur noch bruellen."  Azzuro  zuckte  die
+Schultern.
+
+"Sollte mich wundern, wenn er es dabei beliesse." Ulo kannte ihre Pappenheimer.
+
+"Verdammt, wir verlieren! Mein Schwert!"
+
+Frau Capulet, jeder Zoll eine Dame  und  sich  voellig	im  Klaren  ueber  den
+Zustand ihres Gatten, laechelte ein wenig. "Du meinst  gewiss  deine  Kruecke,
+mein Schatz." "Ich meine mein Schwert."
+
+"Sag ich doch.", erwiderte seine Gattin malizioes.
+
+Herr Capulet, der ahnte, was ihn daheim erwartete, wenn er seiner Gattin jetzt
+widerspraeche,	zuckte	nur  die  Schultern  und  begnuegte  sich  damit,  die
+Capuletanhaenger anzufeuern und uebersah in seinem Eifer ganz  und  gar,  dass
+inzwischen auch Herr Montague mit seiner Frau das Schlachtfeld betreten hatte.
+Herrn Montague war die Gestalt seines Lieblingsfeindes	allerdings  keineswegs
+entgangen. "Du hinterhaeltiger Sack! Wozu hast du deine Leute angestiftet!" Er
+stuerzte mit erhobener Faust auf Capulet zu. "Du, du..."
+
+"Du Capulet.", soufflierte Azzuro und handelte sich einen zornigen  Blick  von
+Ulo ein. Dankbar nahm Montague die Hilfestellung an. "Du _Capulet_,  dir  zeig
+ich, was ein echter Montague ist!"
+
+"Jetzt wird es erst lustig.", freute sich Ulo  und  setzte  sich  in  Positur.
+"Bestimmt bekommen wir gleich eine bilderbuchreife Herzattacke zu sehen."
+
+"Nichts da." Azzuro deutete mit dem Schnabel auf die Polizisten.  "Da,  siehst
+du? Sie haben es geschafft, die beiden Seiten zu trennen."
+
+Ulo seufzte. "Schade."
+
+Die Polizisten hatten die beiden feindlichen  Seiten  saeuberlich  aussortiert
+und an den gegenueberliegenden Seiten des Platzes Aufstellung  nehmen  lassen.
+Zu  jener  Zeit  hielt	man  sich  nicht  lange  damit	auf,  die  Personalien
+festzustellen, die Raufbolde  abzufuehren,  auszunuechtern  und  nach  einigen
+Wochen oder Monaten vor Gericht zu stellen, man sammelte  die  Ueberreste  der
+Pruegelei ein und fuehrte sie dem Richter einfach  vor,  der  lange  nicht  so
+ueberlastet war, wie es die Gerichte in unserer leidgeplagten  Zeit  sind.  So
+geschah es auch jetzt. Das Gericht, in	Gestalt  des  Buergermeisters  Escalus
+betrat den Gerichtshof, will  sagen,  den  Marktplatz,	und  nahm  am  Brunnen
+Aufstellung.
+
+"Was ist hier los?", verlangte Escalus zu wissen.
+
+"Sie haben sich gepruegelt.", bemerkte Ulo leise.
+
+"Danke, soviel kann ich  selber  aus  den  blutigen  Nasen  ablesen."  Escalus
+ignorierte die fragenden Blicke, denn er wusste ja nicht, dass er als Einziger
+Ulos Worte gehoert hatte. "Mir reicht es jetzt, Herrschaften.  Geht  es  schon
+wieder um die alte Fehde?" Montague und Capulet, beides Maenner, die fuer ihre
+Angestellten und Anhaenger einstanden, nickten betreten.
+
+"Ich habe die Nase voll. Statt euch  wie  anstaendige  Menschen  zu  benehmen,
+pruegelt ihr euch auf offener Strasse, als gaebe es  keine  zivilisierte  Art,
+die Angelegenheit zu regeln."
+
+"Hoert, hoert." Azzuro grinste.
+
+"Warum erfindest du nicht den Fussball?", fragte Ulo so leise, dass wieder nur
+Escalus sie hoeren konnte.
+
+Wenn die Goetter der Mythenwelt Ulo jetzt gehoert  haetten,  waere  sicherlich
+ein Blitz vom Himmel gekommen und haette  sie  erschlagen.  Doch  die  Goetter
+waren anderweitig beschaeftigt, so kam es, dass Ulo ungeschoren  davonkam  und
+statt des strafenden Blitzes einer der Eingebung einschlug. Er traf auch nicht
+Ulo, in deren Kopf schon genug mehr  oder  minder  dumme  Ideen  herumspukten,
+sondern Escalus, der tief Luft holte und die grandiose	Idee  in  seinem  Hirn
+speicherte, wo sie bis zu ihrer  Reife	liegen	sollte.  _Eines_  _Tages_,  so
+dachte sich Escalus, _wird_ _die_ _Welt_ _friedlicher_ _sein_. _Dann_ _tragen_
+_wir_ _unseren_ _Streit_ _nicht_ _mehr_ _mit_ _Waffengewalt_ _aus_,  _sondern_
+_spielen_  _statt_  _dessen_  _Fussball_.  _Das_  _wird_   _eine_   _bessere_,
+_weisere_  _Welt_  _sein_,  _ganz_  _bestimmt_.  _Dann_,  _eines_   _schoenen_
+_Tages_,  _werden_  _zwei_  _verfeindete_  _Lager_  _ihre_  _besten_   _Leute_
+_auswaehlen_ _und_ _sie_ _in_ _fairem_ _Wettkampf_ _ihren_ _Streit_ _beilegen_
+_lassen_. _Ja_, _das_ _wird_ _wirklich_ _eine_ _bessere_ _Welt_ _sein_.  _Ich_
+_muss_ _mir_ _nur_  _noch_  _ueberlegen_,  _wie_  _man_  _Fussball_  _spielen_
+_soll_.
+
+Halten wir Escalus zugute, dass er noch nichts von Fans wissen konnte.
+
+"Das ist jetzt das dritte Mal, meine  Herren.  Dreimal  hat  euer  verfluchter
+Haendel schon die ganze Stadt in Aufruhr gestuerzt."
+
+Er haette hinzufuegen koennen,	dass  bei  jeder  dieser  Auseinandersetzungen
+Steuerzahler starben oder fuer laengere Zeit arbeitsunfaehig wurden,  so  dass
+man ihm persoenlichen Schaden zufuegte, aber  ein  solcher  Mann  war  Escalus
+nicht. Statt dessen dachte er nur an die Menschen,  die  durch	diesen	dummen
+Streit keine Ruhe fanden und nie wussten, ob  sie  am  naechsten  Morgen  ihre
+Stadt noch so vorfinden wuerden, wie sie  am  Abend,  als  sie	sich  schlafen
+legten, noch gewesen war. Wer konnte sagen, ob nicht am naechsten Tag alles in
+Schutt und Asche lag,  was  noch  am  Vortag  als  fuer  die  Ewigkeit	gebaut
+ausgesehen hatte? Wer wusste, ob und  wann  der  Streit  zwischen  den	beiden
+Familien eskalierte? Wer konnte  guten	Gewissens  _Bis_  _morgen_  zu	seinen
+Freunden sagen, wenn er nicht wusste, ob er nicht beim Ueberqueren des Marktes
+alle Gedanken an ein Morgen gegen die unendliche Gleichgueltigkeit  des  Todes
+eintauschte? Haben Sie, liebe Leserin, lieber Leser,  jemals  eine  Dorfkirmes
+besucht? Wenn nicht, empfehle ich Ihnen, sich  ein  solches  Spektakel	einmal
+anzusehen. Vielleicht gehen Sie am Samstagabend hin, wenn die  jungen  Maenner
+des Oberdorfes sich zum Bier an einem der drei Bierstaende  treffen,  waehrend
+die des Unterdorfes am zweiten stehen  und  die  Erwachsenen,  Distinguierten,
+Weiseren, in der Mitte zwischen den beiden Bierstaenden bei einem Glas	sitzen
+und ueber Politik  sprechen,  waehrend	sie  darauf  warten,  dass  der  Spass
+anfaengt. Betrachten Sie die Szene und stellen Sie sich vor,  dass  statt  der
+Faeuste Messer und Degen im Einsatz sind, dann koennen Sie sich in  Escalusens
+Lage versetzen, der nichts anderes will, als endlich Frieden in seiner Stadt -
+und allen Menschen ein Wohlgefallen.
+
+Escalus war ein praktischer Mann, auch wenn ihm die Idee von einem Spiel, dass
+die Geschicke der Menschheit entschiede,  ausnehmend  gut  gefiel.  Moralische
+Skrupel plagten ihn weniger als die Probleme, die sich aus  einer  Fortsetzung
+des Streits ergaben. "Geht mir aus den Augen und wieder an eure Arbeit, Leute.
+Und  benehmt  euch  wie  Menschen,  statt  euch  wie  Tiere   gegenseitig   zu
+zerfleischen." Er  schielte  ein  wenig,  um  beide  Seiten  des  Marktplatzes
+gleichzeitig scharf ins Auge zu fassen. "Damit das eine klar  ist:  Ich  dulde
+nicht, dass ihr euch weiterhin daneben benehmt. Wenn ich einen	erwische,  der
+sich mit einem anderen schlaegt, schlage ich  mit  der	Faust  dazwischen  und
+lasse ihn auf der Stelle enthaupten. Verstanden?"  Escalus  wartete  vergebens
+auf  das  verlegene  Fuessescharren,  das  eine  solche   Drohung   eigentlich
+hervorrufen sollte. Langsam, ein wenig widerstrebend machten sich die  Buerger
+wieder auf den Weg zu ihren Haeusern und ihrer Arbeit. Capulet, immer noch  in
+seinem gaenzlich unpassenden Morgenmantel, folgte Escalus zu dessen Buero,  um
+dort zu einer Geldstrafe verdonnert zu werden, denn schliesslich war  bei  dem
+Streit ein Sachschaden	entstanden,  den  Escalus  ersetzt  sehen  wollte.  Es
+interessierte Escalus nicht im geringsten,  wer  den  Streit  heute  vom  Zaun
+gebrochen hatte, solange er sowohl von Capulet	als  auch  von	Montague  eine
+finanzielle Entschaedigung erhielt. Darin unterschied sich Escalus  wenig  von
+den heutigen Kommunen, die  sich  auch	nicht  besonders  darum  kuemmern,  ob
+Nachbars Hund auf die Strasse lief und damit ursaechlich verantwortlich dafuer
+war, dass Herr Mueller oder Frau Schmitz gegen	die  Ampel  fuhr  -  zumindest
+solange nicht, wie die Ampel bezahlt wird.
+
+Lassen wir also Escalus seinen Schadenersatz  kassieren  und  folgen  Ulo  und
+Azzuro, denen es auf dem Marktplatz zu	langweilig  wurde,  bis  zu  Montagues
+Haus, wo sich Benevolio Herrn Montague gegenueber rechtfertigen muss.
+
+"Ehrlich, Onkel, ich hatte ueberhaupt nichts mit der ganzen  Angelegenheit  zu
+tun."
+
+"Das sagst du jetzt.", regte sich Herr Montague auf.
+
+Frau Montague hatte als kluge Hausfrau nichts dafuer uebrig, ueber  vergossene
+Milch zu klagen. "Wo steckt eigentlich Romeo?"
+
+"Lass Romeo jetzt, Liebste. Also, mein lieber Neffe, was war los?"
+
+"Also, da waren dieser Capulet, der  sich  mit  Samson  und  Gregory  angelegt
+hatte. Offenbar wollte er sich pruegeln, als ich dazu kam."
+
+"Eine Gelegenheit, die du dir nicht entgehen lassen  konntest,  nicht  wahr?",
+fragte Herr Montague scharf.
+
+"Keineswegs, Onkel, ich habe nur versucht, die drei zu trennen. Tja, und  dann
+kam Tybalt Capulet dazu."
+
+"Nur gut, dass Romeo nicht  dabei  war,  dieser  Tybalt  haette  ihn  am  Ende
+verletzt.", liess sich Frau Capulet vernehmen.
+
+"Die stellt sich vielleicht wegen des Bengels an.", knurrte Ulo von einem Ast,
+der, als haetten die Goetter ihn extra dort wachsen lassen, gerade so weit  an
+das Fenster heranreichte, dass	die  beiden  Turtelkraehen  das  Gespraech  im
+Innern des Hauses mitverfolgen konnten.
+
+"Sie gibt nicht auf.", meinte Azzuro. "Was hat sie denn mit dem Jungen?"
+
+"Ach, wahrscheinlich ist er wieder hinter irgendeiner Frau her und heult  sich
+deswegen die Augen aus." Ulo hielt nicht viel von Romeo.
+
+"Er ist halt jung." Azzuro hatte  fuer  das  Verhalten  Romeos  deutlich  mehr
+Verstaendnis.
+
+"Tybalt missverstand die Situation und hoerte mir auch nicht zu,  als  ich  es
+ihm erklaeren wollte."
+
+"Sieht ihm aehnlich." Herr Montague kannte Tybalts schlechten Ruf.
+
+"Naja.",  machte  Benevolio  gedehnt.  "Ich  musste  mich   doch   gegen   ihn
+verteidigen."
+
+Erinnern Sie sich, liebe Leserin, lieber  Leser,  wie  Gregory	sich  eingangs
+ueber Notwehr aeusserte? Er hatte offenbar Erfolg mit seiner Taktik.  Nur  mit
+Buergermeister	Escalus  hat  Gregory  nicht  gerechnet.  Wenn	Herr  Montague
+wuesste, wie die Notwehr zustande kam, zoege er die Geldstrafe, die er nun  zu
+zahlen hat, sicherlich von Gregorys Sold ab.
+
+"Wo ist denn nur Romeo?" Frau Montague war offensichtlich  nicht  bereit,  die
+Sache auf sich beruhen zu lassen.
+
+"Er hat sich wieder eingeschlossen.", antwortete Benevolio.
+
+"Was heisst das?", verlangte Frau  Montague  mit  der  Verve  einer  besorgten
+Mutter zu wissen.
+
+"Weiss sie es am Ende wirklich nicht?", ueberlegte Ulo laut.
+
+"Wahrscheinlich nicht. Muetter nehmen ja immer nur das beste von ihren Kindern
+an." Azzuro zuckte mit den Schultern.
+
+"Es heisst, dass er sich in seinem Zimmer  eingeschlossen  und  die  Vorhaenge
+zugezogen hat.", antwortete Benevolio.
+
+"Er  hat  die  Vorhaenge  zugezogen?"  Frau  Montague  mochte  an  ein   solch
+unsittliches Verhalten bei ihrem  Sohn	nicht  glauben.  Wie  Ihnen  jedermann
+bestaetigen kann, sind am hellichten Tage zugezogene Vorhaenge ein  deutliches
+Anzeichen  liederlichen  Lebenswandels,  den  Frau  Montague  ihrem  Sohn  nie
+gestattet haette. Zumindest, so koennen Sie es in jedem Dorf lernen, zieht man
+morgens die Vorhaenge auf und wahrt damit den Schein.
+
+"Sage ich doch, liebe  Tante.  Ausserdem  hat  er  einen  riesigen  Vorrat  an
+Taschentuechern."
+
+"Was will er denn damit?", wollte nun auch Herr Montague wissen.
+
+"Ist doch klar.", murmelte Azzuro.
+
+"Komm, das muss ich mir ansehen." Ulo grinste haemisch.
+
+Sie flogen um das Haus herum, denn  im	Gegensatz  zum	Wohnzimmer  lagen  die
+Schlafzimmer auf der Rueckseite des Hauses, zum Garten hinaus.
+
+"Hoer dir das an!" Ulo hatte ihre helle Freude an Romeos lautem  Gejammer  und
+Gestoehne.
+
+"Was hat er denn, der arme  Kleine."  Auch  Azzuro  hatte  kein  Mitleid  fuer
+Menschen, die es so sehr uebertrieben.
+
+"Er hat Liebeskummer."
+
+"Och, da _tut_ er mir aber leid."
+
+"Ach, Rosaline!", erscholl es von drinnen, gefolgt von einem tiefen Seufzer.
+
+"Rosaline heisst sie also.", bemerkte Ulo ueberfluessigerweise.
+
+"Nicht Julia?"
+
+"Noch nicht, das kommt erst noch."
+
+Romeo, der den Kopf aus dem Fenster gesteckt hatte,  warf  einen  verbluefften
+Blick auf die  Kraehen.  "Oh  nein,  meine  einzige  und  wahre  Liebe  heisst
+Rosaline."
+
+Azzuro ueberlegte, wie er seinen Fehler wieder wett machen konnte. Er  schaute
+schnell zu Ulo, doch auch diese schien zu betroffen zu sein, um sein  Vergehen
+gegen die  elementarsten  Regeln  des  Turtelkraehendaseins  den  Goettern  zu
+petzen.
+
+"Ganz bestimmt ist sie das.", versicherte Azzuro schnell.  "Sie  ist  ja  auch
+wunderschoen."
+
+Er trippelte verlegen von einem Fuss auf den anderen.
+
+"Ja, das ist sie. Aber wer ist Julia?"
+
+"Das braucht dich doch nicht zu interessieren, schliesslich ist Rosaline deine
+einzige Liebe."
+
+"Hm." Romeo ueberlegte einen Moment, dann brach er wieder in Traenen aus. "Ja,
+das ist sie. Und nie hat ein Mann eine so herzlose Frau geliebt."
+
+"Sie ist gemein zu dir?", fragte Azzuro.
+
+"Gemein ist gar kein Ausdruck. Ach, wenn ich nur wuesste, was ich tun muss, um
+sie zu ueberreden."
+
+Benevolio  bollerte  gegen  Romeos  Tuer  und  verlangte  von  seinem	Cousin
+eingelassen zu werden, was Ulo und Azzuro zum Anlass nahmen, sich  schleunigst
+zu verdruecken.
+
+"Du bist wohl wahnsinnig geworden!", schnauzte Ulo ihren Gefaehrten an.
+
+"Wer ahnt denn, dass der dumme Kerl ausgerechnet  in  diesem  Moment  aus  dem
+Fenster guckt." verteidigte sich Azzuro.
+
+"Hoffentlich geht das gut."
+
+"Ach, da mach dir mal keine Sorgen. So  wie  der  Junge  gebaut  ist,  hat  er
+Rosaline und Julia morgen vergessen und rennt einer dritten hinterher."
+
+"Wie kommst du denn darauf?"
+
+"Ist doch klar. Er ist achtzehn, aus guter Familie und reich. Wie sollte er da
+nicht ausreichend Maedchen finden, denen er unter die  Roecke  schauen	kann?"
+Azzuro blickte auf seinen eigenen reichen Erfahrungsschatz zurueck.
+
+"Du denkst auch immer das schlechteste."
+
+"Aber nein. So sind die jungen Burschen nun mal. Fuer ihn ist es einfach chic,
+Liebeskummer zu haben."
+
+"Gut, dass du das hinter dir hast."  Ulos  Stimme  liess  vermuten,  dass  sie
+diesen Satz als Mahnung verstanden wissen wollte.
+
+"Hoefische Minne hat mir noch nie gelegen.", verteidigte sich Azzuro.
+
+"Ja, ich weiss." Ulo seufzte und gab sich fuer einige wenige Augenblicke ihren
+Traeumen hin.
+
+"Hoer auf damit, du weisst ja gar nicht, wovon du da traeumst." Azzuro  kannte
+seine Gefaehrtin.
+
+"Ach, weiss ich nicht?"
+
+"Nein. Wenn du gewollt haettest, dass ich dich minne, dann  haettest  du  mich
+niemals erhoeren duerfen."
+
+"Hm, haette ich besser getan, ja."
+
+"Versteh doch. Ich liebe dich  ja,  aber  ich  minne  dich  nicht.  Zur  Minne
+gehoert, dass man die angebliche Geliebte nie bekommen kann."
+
+"Wie kommst du denn darauf?"
+
+"Na, Minne hat nicht viel mit  erfuellter  Liebe  zu  tun,  sondern  mehr  mit
+Aus-Der-Ferne- Schmachten."
+
+"Oh?" Ulos Stimme bekam eine deutliche Spitze. "Du vermisst mich  also  nicht,
+wenn ich nicht bei dir bin?"
+
+"Du bist doch nie von mir weg.", verteidigte  sich  Azzuro.  "Und  ueberhaupt,
+lenk nicht vom Thema ab."
+
+"Wer lenkt denn  hier  ab?  Wahrscheinlich  vergnuegst  du  dich  ohnehin  mit
+irgendeinem Spatzenflittchen, wenn ich mal nicht in der Naehe bin."
+
+Azzuro, der genau  wusste,  welche  Richtung  die  Diskussion  nehmen  wuerde,
+betrachtete betont interessiert die Vorgaenge innerhalb Romeos Zimmer und  zog
+es vor, nicht zu antworten.
+
+Benevolio hatte es inzwischen geschafft, Romeo dazu zu ueberreden, die Tuer zu
+oeffnen und ihn einzulassen. "Guten  Morgen,  Romeo.",  begruesste  er  seinen
+Vetter froehlich.
+
+"Ist es denn Morgen?", fragte Romeo duester.
+
+"Und was fuer einer. Los, mach die Vorhaenge auf und schau dir den wunderbaren
+Sonnenschein an. Hoer nur, wie die Voegel singen." Benevolio riss die schweren
+Vorhaenge zur Seite und erblickte die beiden Kraehen. "Naja, vielleicht singen
+sie nicht ganz so schoen, aber die Sonne scheint auf jeden Fall."
+
+"Es ist schrecklich, aber ich bin so traurig, dass ich gar nicht  sehen  will,
+wie die Sonne scheint, denn das sieht so aus, als wolle sie mich verspotten."
+
+"Keineswegs  will  sie  das.  Sie  hat  nichts  anderes  im  Sinn,  als   dich
+aufzuheitern."
+
+"Ist er irre?", fragte Ulo leise.
+
+"Aber nein. An  ihm  ist  bloss  eine  Krankenschwester  verloren  gegangen.",
+bemerkte Azzuro.
+
+"Was macht dich denn so traurig, Romeo? Willst du  es  mir  nicht  erzaehlen?"
+fragte Benevolio.
+
+"Er ist wirklich ein Schaetzchen." Benevolio  hatte  unzweifelhaft  Ulos  Herz
+fuer sich gewonnen.
+
+"Ein  Psychiater  koennte  er  auch  sein."  Azzuro  konnte  dem  freundlichen
+Benevolio nicht ganz so viel abgewinnen.
+
+"Mir ist  langweilig,  weil  ich  das  nicht  habe,  was  mir  die  Langeweile
+vertreibt.", antwortete Romeo.
+
+"Er will eine Modelleisenbahn?", vermutete Azzuro.
+
+Benevolio hatte offenbar ebenfalls  keine  Vorstellung	von  dem,  was	seinem
+Freund die Langeweile vertreiben koennte. "Was liebst du denn so sehr?"
+
+"Nicht was, sondern wen."
+
+"Liebe ist doch etwas schoenes, warum macht sie dich bloss so traurig?"
+
+"Weil ich Liebe gebe und  keine  bekomme.  Ich  fuehle  mich  wie  eine  leere
+Flasche. Das, was einmal in mir war, wollten alle haben, aber  jetzt,  da  ich
+nur noch eine leere Flasche bin, gibt  es  niemanden  mehr,  der  mich	wieder
+fuellen wuerde."
+
+"Eine Flasche ist er allerdings.", bemerkte Ulo.
+
+"Nein, ist er nicht.", wies Benevolio sie streng zurecht. "Moment,  seit  wann
+koennen Kraehen reden?"
+
+"Seit Maenner Flaschen sind.", versetzte Ulo schnippisch.
+
+"Lass doch die bloeden Kraehen, Benevolio." Romeo mochte es  gar  nicht,  wenn
+man sich nicht so leicht von ihm ablenken liess.
+
+Azzuro stiess Ulo mit einem gekonnten Schubs vom Ast und trieb	sie  zu  einem
+Baum in sicherer Entfernung von Romeos Zimmer. "Halt  bloss  deinen  vorlauten
+Schnabel!"
+
+"Er hat uns bloed genannt."
+
+"Ja."
+
+"Und das aergert mich."
+
+"Aha?"
+
+"Weil ich nicht bloed bin."
+
+"Du darfst in _ihrem_ Beisein trotzdem keine  Kommentare  machen,  das  weisst
+du."
+
+"Pah."
+
+"Weisst du es, oder nicht?"
+
+"Ja, ich weiss es. Aber er ist wirklich eine Flasche."
+
+"Das habe ich nicht bestritten. Wenn du jetzt deinen vorlauten Schnabel halten
+kannst, koennen wir wieder zurueck und uns den Rest ansehen."
+
+Ulo antwortete nicht, sondern flog wieder zu ihrem Ast und spaehte  durch  das
+Fenster auf die zuckenden Schultern Benevolios.
+
+"Lachst du mich aus?", fragte Romeo.
+
+"Mitnichten." Benevolios Stimme klang so ernst, dass  selbst  in  Ulo  Zweifel
+erwachten.
+
+"Eigentlich weine ich."
+
+"Warum denn das?"
+
+"Weil du so arm dran bist, lieber Romeo."
+
+"Hoer auf, bitte. Wenn du jetzt auch noch weinst, fuehle  ich  mich  nur  noch
+elender. Wenn das ueberhaupt geht."
+
+"Jaja, so geht das mit der Liebe." Benevolio grinste duemmlich.
+
+"Ja, genau so. Liebe, das ist ein Meer."
+
+"Ein Meer? Wie kommst du denn darauf?"
+
+Azzuro liess sich neben Ulo nieder.
+
+"Achtung, Romeo wird poetisch.", warnte Ulo ihren Gefaehrten.
+
+"Ein Meer, weil Traenen salzig sind und  reichlich  fliessen,  wenn  Liebe  im
+Spiel ist."
+
+"Aha. Ja, ich glaube, ich weiss, was du meinst." Benevolio war prosaischer als
+sein Freund.
+
+"Ich glaube, ich mache  einen  Spaziergang."  Romeo  machte  keine  Anstalten,
+aufzustehen, sondern seufzte nur tief.
+
+"Immer langsam, ich weiss doch noch nicht alles, das  waere  gemein,  wenn  du
+mich jetzt so stehen liessest."
+
+"Ich bin sowieso nicht hier. Der echte Romeo ist woanders."
+
+"Und wo?"
+
+"Du willst wohl wissen, wie sie heisst?"
+
+"Gut gefolgert, lieber Watson."
+
+"Ich werde noch wahnsinnig!", stoehnte Azzuro.
+
+"Was ist denn los?" Ulo warf ihm einen verwirrten Blick zu.
+
+"Dauernd zitieren sie aus fremden Quellen, das macht mich irre."
+
+"Keine Sorge, du wirst dich nicht anders fuehlen als vorher." Ulo grinste.
+
+"Also soll ich dir vorstoehnen, wie  sie  heisst?",  fragte  Romeo  mit  einem
+Grinsen.
+
+"Es reicht, wenn du es mir einfach sagst.", antwortete Benevolio.
+
+"Klar, ich mache jetzt voellig einfach mein Testament."
+
+"Nana, so weit ist es noch nicht. Du bist ja noch  gruen  hinter  den  Ohren."
+Benevolio lachte.
+
+"Pfft.", machte Romeo. "So gruen bin ich nicht mehr, dass ich nicht weiss, wie
+man liebt."
+
+"Das wiederum habe ich nicht bezweifelt. Nun sag endlich, wie heisst sie?"
+
+"Sie ist eine Frau."
+
+"Hm,  dachte  ich  mir.  Zumindest  hatte  ich  keinen  Grund,  etwas  anderes
+anzunehmen."
+
+Romeo streckte seinem Vetter die Zunge heraus. "Und schoen ist sie auch."
+
+"Es war mir ebenfalls klar, dass du kein Kostveraechter bist."
+
+"Nein, ich verhungere lieber. Sie will mich nicht."
+
+"Klug und geschickt ist sie also auch noch?"
+
+"So geschickt, dass Cupidos Pfeil sie nicht trifft. Immer duckt sie sich weg."
+
+Wir  wollen  hier  anmerken,  dass   auch   Cupido   zu   jeder   anstaendigen
+Liebesgeschichte gehoert. Auch zu den unanstaendigen, aber von denen reden wir
+hier lieber nicht, denn es mag ja sein, dass unter  Ihnen,  liebe  Leserinnen,
+liebe Leser, Minderjaehrige sind. Kennen Sie Cupido? Das ist der kleine, dicke
+und nackte Bengel, den Sie auf fast jedem barocken  Bild  betrachten  koennen.
+Damals hatte man eine Vorliebe fuer das Kerlchen, weil ein  nackter  Saeugling
+mit einem  Bogen  einfach  lustig  aussieht.  Auf  diese  Weise  gaben	unsere
+Vorfahren dem im Grunde ernsten Ding Liebe noch eine amuesante Wendung.  Bitte
+verwechseln Sie unseren kleinen Freund nicht mit den  Engeln,  die  sind  zwar
+auch nackt, klein  und	dick,  haben  aber  keinen  Bogen  in  den  pummeligen
+Faeustchen.
+
+Romeo stellt sich Cupido noch nicht ganz so dick vor, dafuer sind seine  Augen
+verbunden. Zu  Romeos  Zeiten,	in  jener  mythischen  und  so	ganz  und  gar
+unwirklichen Zeit, sah Cupido noch nicht, wen er mit seinen Pfeilen traf.  Man
+koennte denken, dass dies zu allerlei Verwirrung gefuehrt  haben  muss,  etwa,
+wenn sein  Pfeil  eigentlich  dem  Schaefer  galt,  der  unaufmerksame	Bengel
+allerdings eines der Schafe traf, doch scheint dies so selten der Fall gewesen
+zu sein, dass uns von solchen  Geschehnissen  nichts  ueberliefert  wurde.  So
+muessen wir annehmen, dass damit lediglich das Sprichwort  illustriert	werden
+soll, das sagt: Wo die Liebe hinfaellt, da waechst kein Gras mehr.
+
+"Schlimmer noch." Jetzt stoehnte Romeo wirklich. "Sie ist  eine  wahre  Diana,
+was das Ausweichen angeht."
+
+Sie erinnern sich sicher an Diana, liebe Leserin, lieber Leser.  Sie  war  die
+Goettin der Jagd und Jungfrau von Beruf. Nichts und niemand  konnte  sie  dazu
+bringen, sich mit einem Mann, gleich ob  Gott  oder  Mensch,  einzulassen.  So
+schlimm trieb sie es, dass ein	Mann  zur  Strafe,  weil  er  ihr  beim  Baden
+zugesehen hatte, in einen Hirsch verwandelt  und  von  seinen  eigenen	Hunden
+gefressen wurde. Diese Anekdote sagt ebensoviel ueber  die  Intelligenz  eines
+Hundes wie ueber die  des  Mannes  aus,  denn  beide  vergassen  ihre  gesamte
+Erziehung. Die Hunde, als sie den Hirsch rochen, der Mann,  als  er  die  Frau
+sah.
+
+"Eine Diana, wie? Dann hat sie wohl lebenslange Keuschheit gelobt?"
+
+Romeo gab ein Geraeusch von sich, dass dem des	gestellten  Hirsches  aehnlich
+sein mochte. "Was soll ich  nur  machen?  Sie  ist  wunderschoen  und  es  ist
+geradezu ein Frevel, dass sie ins Kloster will, denn damit verzichtet sie auch
+darauf, ihre Schoenheit an die Nachwelt zu ueberliefern."
+
+"Noch ist nicht aller Tage Abend. Vielleicht bekommt sie ja trotzdem Kinder."
+
+"Trotz der Keuschheit? Das geht nicht."
+
+"Stimmt.", grinste Ulo. "Das gute Maedchen ist wohl lesbisch, wie?"
+
+Azzuro zuckte die Schultern. "Wer weiss das schon?"
+
+"Hm, wenn ich nur unter Frauen sein wollte, ginge ich auch ins Kloster."
+
+Azzuro lachte. "Spielt es eine Rolle? Ich meine, wenn sie ihn nicht will, dann
+kann er sich auf den Kopf stellen und mit den Fuessen Hurra schreien, er  wird
+nicht erhoert."
+
+"Grausame Welt.", seufzte Romeo von drinnen. "Ich kann einfach  nicht  an  sie
+herankommen. Sie ist schoen, sie ist klug und ihre  Weste  ist	so  weiss  wie
+Schnee. Nichts, aber auch gar  nichts  gibt  mir  eine	Moeglichkeit,  sie  zu
+erpr..." Er unterbrach sich schnell.
+
+"Wollte er eben erpressen sagen?" fragte Ulo.
+
+"Ich nehme es an. Warum?"
+
+"Maenner!"
+
+"Du meinst Menschen." korrigierte Azzuro sie.
+
+"Ich meine Maenner. Wenn ich noch ein einziges Mal hoere, dass ein  Mann  eine
+Frau rein und keusch nennt, weiss ich, was ich davon zu halten habe."
+
+"So typisch ist Romeo nun auch wieder nicht."
+
+Ulo schob den Einwand mit einer ungeduldigen Bewegung ihres Fluegels beiseite.
+"Ich werde nie wieder einem Mann weiter trauen, als ich ihn spucken kann."
+
+"Wir koennen nicht spucken, wir sind Voegel."
+
+"Eben."
+
+Azzuro schuettelte den Kopf ueber soviel  Unvernunft.  "Du  brauchst  ja  auch
+keinem Mann zu trauen, du hast ja mich."
+
+"Um so schlimmer."
+
+"He!", machte Azzuro.
+
+Benevolio betrachtete seinen Freund besorgt. "Lass dir einen Tip geben, Romeo.
+Vergiss sie."
+
+"Vergessen? Sie? Genauso gut koennte ich das Atmen vergessen."
+
+"Kann er doch gar nicht.", meinte Azzuro.
+
+"Das will er damit ja sagen.", informierte ihn Ulo.
+
+"Ich sag dir, wie du sie vergessen kannst." Benevolio sah sich genoetigt,  ein
+wenig lauter zu sprechen, um das Geschrei der beiden Kraehen vor  dem  Fenster
+zu uebertoenen. "Schau dich einfach ein wenig um. Andere  Muetter  haben  auch
+schoene Toechter."
+
+"Ach, damit machst du es ja nur noch schlimmer. Wenn ich andere  Frauen  sehe,
+vergleiche ich sie immer mit ihr und dann haben  die  anderen  verloren.  Lass
+mich lieber allein, ich will in meinem Schmerz ertrinken."
+
+"Er koennte ihn ersaeufen.", schlug Ulo vor.
+
+"In Schnaps?", fragte Azzuro.
+
+"Worin sonst? Meinst du, er versuchte, ihn mit einem Stein zusammen  in  einen
+Sack zu stecken und in den Fluss zu werfen?"
+
+Benevolio kannte das Leid der Jugend und grinste verschaemt.  "Warte  ab,  ich
+lasse mir was einfallen."
+
+Kaum hatte Benevolio den Raum verlassen, als Romeo sich auf das Bett warf  und
+zum Steinerweichen schluchzte.
+
+"So ein Jammerlappen.", bemerkte Ulo.
+
+"Nun hoer schon auf, er ist noch jung und er ist verliebt."
+
+"Schon gut, das ist der Freibrief fuer jede nur denkbare Bloedheit."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite4 b/d/seher/haeuser/special/special/rom_jul/seite4
new file mode 100644
index 0000000..3041d58
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite4
@@ -0,0 +1,258 @@
+DER ERSTE AKT
+	  DIE ZWEITE SZENE
+
+Ulo und Azzuro beschlossen, Romeo nicht weiter dabei zuzusehen,  wie  er  sich
+seinem Liebeskummer hingab, sondern lieber zum Haus Capulets  zu  fliegen.  Im
+Wohnzimmer der Villa Capulets fanden sie den inzwischen angezogenen Hausherren
+mit seinem Freund Paris.
+
+"Noch so ein Stutzer.", bemerkte Azzuro  und  deutete  mit  dem  Schnabel  auf
+Paris.
+
+"Was hast  du  denn  gegen  ihn?",  fragte  Ulo.  "Ich  finde,  er  hat  einen
+knackigen..."
+
+"Ulo!"
+
+"...Ausdruck im Gesicht.", schloss Ulo lahm.
+
+"Seit wann gefallen dir  denn  Menschen?"  Azzuro  verstand  seine  Gefaehrtin
+nicht.
+
+"Es ist gut, dass ich so alt bin.", raesonierte Capulet drinnen.
+
+"Pfft.", machte Azzuro. "Das nennt man dann aus der Not eine Tugend machen."
+
+Ulo grinste nur zur Antwort.
+
+"Wenn ich juenger waere, fiele es mir wahrscheinlich schwerer, endlich Frieden
+mit Montague zu halten." Capulet seufzte.
+
+Ulo lachte kraechzend. "Kannst du dir vorstellen, wie es  aussaehe,  wenn  die
+beiden alten Maenner aufeinander losgingen?"
+
+Azzuro	lachte	mit.  "Nur  zu  gut.  Wahrscheinlich  wuerden  sie  sich   mit
+Pillendoeschen bewerfen."
+
+"Oder ihre Krankengeschichte aufschreiben."
+
+Azzuro schuettelte sich. "Also _das_ waere dann wirklich zuviel."
+
+"Es ist schon eine Schande, dass  der  alte  Streit  immer  noch  existiert.",
+meinte Paris.
+
+"So  geht  es  nun  einmal.  Irgendwann  machen  sich  solche   Streitigkeiten
+selbstaendig.", antwortete Capulet.
+
+"Das erste vernuenftige Wort, dass der Alte sagt." Haette Ulo Haende  anstelle
+der Fluegel gehabt, so haette sie wohl Beifall geklatscht.
+
+Paris nickte bedaechtig. "Damit hast du wahrscheinlich Recht,  aber  lass  uns
+von etwas angenehmeren reden. Was haeltst du von dem Gedanken, dass ich  Julia
+heirate?"
+
+Azzuro warf einen kritischen Blick auf	Paris.	"Abstand  haelt  er,  wenn  er
+schlau ist."
+
+"Im Grund habe ich nichts dagegen, aber sie  ist  meine  einzige  Tochter  und
+ausserdem noch zu jung. Sie weiss ja noch nicht einmal, was  es  bedeutet,  zu
+heiraten. Warte, bis sie sechzehn ist, dann hast du meinen Segen."
+
+"Hm, ob sie ueberhaupt schon weiss, wie das mit den Bienchen und den Bluemchen
+ist?", fragte sich Ulo.
+
+"Sie wird es schon herausfinden. Immerhin laesst der Alte  ihr  ja  noch  zwei
+Jahre."
+
+"Ich kenne Frauen, die juenger waren", wandte  Paris  ein  "und  trotzdem  mit
+einem Stall voller Kinder gluecklich wurden."
+
+"Sieh dir nur meine Frau an. Du hast nicht viel von ihr, wenn du sie  so  jung
+heiratest, glaub mir." Capulet zuckte mit den Schultern.
+
+"Dieser..." Ulo holte tief Luft.
+
+"Beherrsch dich." Azzuro zupfte sie an den Schwanzfedern.
+
+Paris nickte. "Wenn du meinst, werde ich mich natuerlich danach richten."
+
+"Ich koennte ihnen...", grollte Ulo.
+
+Azzuro ahnte, was jetzt kam. "Tus nicht."
+
+Ulo seufzte tief und verfluchte einmal mehr die Goetter, die sie zwangen, eine
+stumme Beobachterin zu sein.
+
+"Vertrau mir, ich habe schon manche Frau gesehen.", meinte Capulet.
+
+"Das glaub ich dem alten Bock.", grinste Azzuro.
+
+"Fang du nicht auch noch an, dich wie ein Macho zu benehmen.", fuhr Ulo  ihren
+Gefaehrten an.
+
+"Heute abend gebe ich ein Fest, Paris, da kannst du  Julia  kennenlernen."  Er
+zwinkerte seinem Freund zu. "Schau dich um  und  such  dir  unter  den  jungen
+Frauen einen angenehmen Zeitvertreib fuer die naechsten beiden Jahre. Und  was
+Julia angeht, so umwirb sie ein bisschen,  damit  sie  in  zwei  Jahren  nicht
+gleich in Traenen ausbricht, dann kannst du sie haben."
+
+"Wenn das deine Entscheidung ist, werde ich mich natuerlich daran halten."
+
+Capulet wandte sich um und schuettelte heftig eine kleine Glocke, die auf  dem
+Tisch stand. Ulo haette sich am liebsten die Ohren zugehalten, denn die Glocke
+war offensichtlich gebrochen und bewies, dass Capulet am falschen Ende sparte.
+Als der Diener das Zimmer betrat, drueckte Capulet ihm eine Liste in die Hand.
+"Geh und suche all die Leute auf, die auf dieser Liste  stehen,  richte  ihnen
+aus, dass sie herzlich eingeladen sind."
+
+Der Diener warf einen zweifelnden Blick  auf  die  Liste  in  seinen  Haenden.
+"Alle?"
+
+"Was dachtest du denn?" Capulet schuettelte den Kopf und hakte sich bei  Paris
+unter. "Komm mit, wir werfen einen Blick auf die Rosen."
+
+"Alle..." Der Diener, den wir, obwohl  er  keine  weitere  Bedeutung  hat  und
+eigentlich zu den Namenlosen gehoert, James nennen wollen, starrte  die  Liste
+an, als koenne er sie mit seinen Blicken  in  Brand  setzen.  "Alle  soll  ich
+einladen.", brummte er. "Als ob ich lesen koennte."
+
+Ulo zwinkerte belustigt. "Na, so ein  Pech.  Da  wird  Paris  ja  heute  abend
+alleine sein."
+
+"Warten wirs ab.", antwortete Azzuro.
+
+"Jetzt steh ich hier, ich armer Tor." James seufzte.
+
+"Ich hasse es, wenn sie die Zitate vertauschen." Azzuro schuettelte sich. "Wie
+soll man denn da den Ueberblick behalten."
+
+Die Kraehen folgten James, als er mit der Liste in der Hand das Haus verliess,
+um jemanden zu finden, der ihm sagte, wen er einladen  sollte.	Wie  so  viele
+Menschen war auch Herr Capulet jemand, der Dingen, die er  schwarz  auf  weiss
+vor sich sah, unbedingtes Vertrauen schenkte. Wer jedoch je  erlebt  hat,  wie
+Geruechte  wirken,  weiss,  dass  das  gesprochene  und  erinnerte  Wort   dem
+niedergeschriebenen immer ueberlegen ist. Geschichte, so wissen  wir,  besteht
+nur zu einem verschwindend kleinen Teil in Dokumenten. Seien Sie ehrlich,  wem
+glaubten Sie denn eher: einem verstaubten und  halbverschimmelten  Brief,  der
+besagt,  dass  Ihr  Grossvater	gar  nicht  Ihr  Grossvater  ist,   oder   den
+Erzaehlungen ihrer Grossmutter, die einen wunderbaren Kuchen  backt,  um  ihre
+Behauptung zu untermauern?
+
+"Wenn James sich eine Menge Aerger ersparen will, braucht er nur die Liste als
+Klopapier zu benutzen.", meinte Ulo.
+
+James blickte verwirrt auf. "Was meinst du damit?"
+
+"Krah.", antwortete Azzuro an Ulos statt.
+
+"Ich hab genau gehoert, dass du geredet hast."
+
+"Krahrah.", machte nun auch Ulo.
+
+"Jajaja, Krahrahrah." James trat missmutig gegen einen Stein und  fluchte,  da
+der Stein den ungleichen Kampf gewann. "Ich habe es gehoert, habe ich."
+
+"Nichts hast du gehoert.", meinte Ulo. "Krah.", fuegte sie  nach  einer  Pause
+hinzu.
+
+"Ach, leck mich. Erst die dumme Liste und dann  auch  noch  eine  Kraehe,  die
+redet."
+
+"Nicht weniger dumm als die Liste.", kommentierte Azzuro so  leise,  dass  nur
+Ulo ihn verstand.
+
+"He, wenn es diese Liste nicht gaebe, wuerde Romeo..."
+
+"Halt den Schnabel!", fuhr Azzuro seine Gefaehrtin an.
+
+"Wer ist Romeo?", verlangte James zu wissen, der immer sehr  genau  hinhoerte,
+wenn ein Gespraech nicht fuer ihn bestimmt war.
+
+Zu Ulos und Azzuros Glueck rief die  Koechin  nach  James  und	gab  ihm  eine
+fluessige Staerkung mit auf den Weg. So schnell, wie der Schnaps seinen Weg in
+Jamesens Magen fand, vergass dieser die beiden	Kraehen  und  machte  sich  an
+seinen Auftrag. Auch Sie und ich haben Glueck, liebe  Leserin,	lieber	Leser,
+denn der erste Mensch, dem  James  begegnete,  war  Benevolio,	der  Romeo  im
+wahrsten Sinne des Wortes hinter sich herschleppte. Stellen Sie sich nur  vor,
+es sei jemand anders gewesen, dem James ueber den Weg lief. Vielleicht jemand,
+den Herr Capulet einzuladen vergessen hatte, dann verliefe  unsere  Geschichte
+von hier ab voellig anders, als Sie sie hoeren	wollen	und  ich  muesste  mir
+einfallen lassen, wie ich dies den Goettern der Mythenwelt erklaere.
+
+"Und der Gewerkschaft.", sagt Ulo amuesiert.
+
+_Und_ _der_ _Gewerkschaft_, _ja_. _Sei_ _gewarnt_, _Ulo_.  _Wenn_  _du_  _mir_
+_noch_ _einmal_ _dazwischenquasselst_, _streiche_  _ich_  _dich_  _aus_  _der_
+_Geschichte_!
+
+"Das glaubst du doch selbst nicht. Wenn du das machst, trete ich in  Streik.",
+knurrt Azzuro und schwenkt ein rotes Faehnchen.
+
+_Schon_ _gut_. _Versucht_ _wenigstens_, _euch_ _anstaendig_ _zu_ _benehmen_.
+
+Benevolio grinste seinen Cousin an.  "Hast  du  dich  schon  einmal  im  Kreis
+gedreht, bis dir schwindlig wurde?"
+
+"Aeh..." Romeo verstand nicht, worauf sein Freund hinaus wollte.
+
+"Wenn man sich dreht, bis einem schwindlig wird, kann man sich  helfen,  indem
+man sich in die andere Richtung dreht."
+
+"Das weiss ich."
+
+"Genauso ist es auch mit dir. Wenn du Rosaline  nicht  bekommen  kannst,  dann
+schau dir eine andere an und schon fuehlst du dich besser."
+
+"Das waere ja den Teufel mit Beelzebub austreiben.", kommentierte Azzuro.
+
+"Wenn's denn hilft." Romeo zuckte die Schultern.
+
+"Sie koennen nicht zufaellig lesen?", fragte James. "Guten Tag, uebrigens."
+
+"Kommt darauf an.", brummte Romeo schlechtgelaunt.
+
+"Ach, schon gut, dann suche ich mir eben jemanden, der es kann."
+
+"Ich kann lesen." Nun hatte die Neugierde Romeo gepackt.
+
+Romeo nahm Herrn Capulets Liste entgegen und entzifferte  unter  Stoehnen  und
+Fluchen die Handschrift. Als er fertig war, gab er die Liste zurueck.
+
+"Danke, aber das brauche ich jetzt nicht mehr, schliesslich habe ich mir alles
+gemerkt."
+
+James war sichtlich stolz auf sein Gedaechtnis.
+
+"Wohin sollen denn all die Leute kommen?", wollte Romeo wissen.
+
+"Herr Capulet gibt heute ein Fest. Wissen Sie, kommen Sie doch einfach vorbei,
+wenn sie nicht gerade den Montagues die Stange halten."
+
+Benevolio grinste. "Das ist die Gelegenheit."
+
+"Du sprichst heute in Raetseln."
+
+"Hast du denn gar nicht darauf geachtet, was du vorgelesen hast?"
+
+"Ich war mit der Handschrift beschaeftigt."
+
+"Heute abend sind sowohl Rosaline als auch die schoensten Frauen  Veronas  bei
+Capulet zu Gast."
+
+"Ja und? Sollen wir jetzt ein eigenes Fest geben?"
+
+"Nein. Wir gehen da hin und dann kannst du mir sagen, ob Rosaline wirklich die
+Schoenste ist. Ich bin gespannt, was du sagst, wenn wir erst da sind."
+
+"Sie ist die Allerschoenste."
+
+"Sicher." Benevolio taetschelte Romeos Schulter. "Aber bis jetzt hast  du  sie
+noch nicht gesehen, wenn alle anderen Schoenheiten dabei waren."
+
+"Hoert sich an wie eine Miss-Wahl." meinte Azzuro.
+
+Romeo ueberlegte eine Weile, dann nickte er. "Ich komme mit, aber nicht,  weil
+ich mir die anderen Frauen ansehen will, sondern bloss um Rosaline zu sehen."
+
+Benevolio seufzte. "Warten wir ab."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite5 b/d/seher/haeuser/special/special/rom_jul/seite5
new file mode 100644
index 0000000..26f28b3
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite5
@@ -0,0 +1,213 @@
+DER ERSTE AKT
+	  DIE DRITTE SZENE
+
+Im Boudoir Frau Capulets beobachteten Ulo und Azzuro zwei reichlich  ungleiche
+Frauen. Die eine war Frau Capulet, zierlich und wenn schon nicht huebsch, dann
+doch wenigstens geschickt geschminkt. Die andere war Polly, Julias  Amme,  die
+dem Klischee ihres Berufes gerecht wurde, denn sie war frueh alt geworden  und
+weniger geschickt im Umgang  mit  Schminke  als  Frau  Capulet.  Rundlich  und
+inzwischen fast zahnlos, mit wirrem, fast ergrautem Haar und Falten,  die  man
+Alpen nennen wuerde, wenn sie nur ein wenig groesser waeren, wirbelte  sie  so
+schnell ihre krampfadrigen Beine sie tragen wollten durch das Zimmer.
+
+"Ich hab ihr gesagt, dass sie herkommen soll. Jawohl, das habe  ich.  Und  ich
+vergesse nie etwas.", murmelte sie vor sich hin.
+
+"Sie ist jedenfalls nicht hier.", kommentierte Frau Capulet.
+
+Polly holte asthmatisch Luft und rief: "Julia! Wo steckst du, Schaetzchen?"
+
+"Ich bin ja schon da.", meinte Julia von der Tuer her.
+
+"Oh. Sie muss aber noch ziemlich lange auf die Weide." Azzuro schuettelte  den
+Kopf.
+
+"Ist ja noch ein rechter Backfisch."
+
+"Ich geb' dir gleich Backfisch auf den Schnabel.", drohte Ulo.
+
+"Sieh sie dir doch an. Wenn sie einen Pfirsichkern verschluckt, sieht sie aus,
+als sei sie im fuenften Monat!"
+
+"Das waechst sich noch aus."
+
+"Bestimmt. Aber bis dahin holt man sich blaue Flecken."
+
+Ulo drehte sich empoert um und antwortete nicht mehr.
+
+"Was ist denn, Mama?", wollte Julia wissen.
+
+"Wie alt ist Julia jetzt, Polly?", fragte Frau Capulet.
+
+"Die Schminke hat ihr das  Gehirn  verkleistert."  Ulo  kicherte.  "Sie  weiss
+schon nicht mehr, wie alt ihre Tochter ist."
+
+"Ich vergesse nie etwas und ich sage Ihnen, Julia ist noch  nicht  vierzehn.",
+antwortete Polly. "Am ersten August  ist  sie  geboren,  da  wette  ich  meine
+restlichen Zaehne drauf."
+
+"Erster August, ja. Koennte hinkommen." Frau Capulet  hatte  sichtlich  Muehe,
+sich an die Geburt Julias zu erinnern.
+
+"Jawohl, da bin ich sicher, und ich vergesse nie etwas. Ich weiss sogar  noch,
+wie sie an ihrem dritten Geburtstag hinfiel und  sich  eine  Beule  holte,  so
+gross wie meine dicke Zehe. Und damals, das weiss ich auch noch, hat mein Mann
+Gott-hab-ihn-selig sie in den Arm genommen, weil sie  so  schrecklich  geweint
+hat. Und er sagte, dass sie jetzt noch jung genug sei, um auf das  Gesicht  zu
+fallen, aber wenn sie aelter wuerde, werde sie	gewitzter  sein  und  auf  den
+Ruecken fallen. Jawohl, das weiss ich noch,  als  waere  es  gestern  gewesen.
+Sehen Sie, was ich fuer ein gutes Gedaechtnis habe."
+
+"Ja, Sie haben ein gutes Gedaechtnis." Frau Capulet schaute abwesend  aus  dem
+Fenster und versuchte vergeblich, Pollys Nebensaetze zu sortieren.
+
+"Jajaja. Und mein Mann sagte noch, wenn sie gewitzt sei,  werde  sie  auf  den
+Ruecken fallen und nicht auf das Gesicht. Und das Lustigste  daran  war,  dass
+meine kleine Julia gleich aufhoerte, zu weinen und nur noch 'Ja.' sagte.  Ganz
+so, als habe sie das alles  schon  verstanden.	Ich  musste  ganz  schrecklich
+lachen, als mein Mann sagte, dass sie auf den Ruecken fallen wuerde, wenn  sie
+aelter waere."
+
+Mutter und Tochter stoehnten wie aus  einem  Munde  und  sahen	sich  dann  in
+seltenem Einverstaendnis grinsend an.
+
+"Du warst das huebscheste Kind, das  ich  je  gesehen  habe,  Julia.  Und  ich
+vergesse nie etwas. Mein groesster Wunsch  waere,  dich  noch  verheiratet  zu
+sehen."
+
+"Das sollte sie besser nicht wuenschen.", meinte Azzuro.
+
+"Warum denn nicht?" wollte Ulo wissen.
+
+"Weil die Goetter sie sterben lassen koennten, wenn sie ihren Wunsch  hat.  So
+geht das doch in Mythen."
+
+"Ums Heiraten geht es gerade.", sagte Frau Capulet.
+
+Sowohl im Zimmer als auch auf  dem  Ast  der  beiden  Turtelkraehen  wurde  es
+schlagartig so still, dass man die beruehmte Stecknadel haette	fallen	hoeren
+koennen.
+
+"Das waere ja noch schoener.", meinte Julia.
+
+"Nicht wahr?", fragte Polly, die sie absichtlich missverstand. "Wenn ich nicht
+wuesste, wer dich gestillt hat, als du noch klein warst, und ich vergesse  nie
+etwas, dann sagte ich, dass du die Klugheit nicht nur mit Loeffeln  gefressen,
+sondern schon mit der Milch getrunken haettest."
+
+"Ich denke noch nicht einmal darueber  nach."  Julia  hatte  sichtlich  Muehe,
+nicht auf die Brueste ihrer Amme zu starren.
+
+"Wenn ich mich nicht verrechne, dann war ich schon deine Mutter, als ich  noch
+nicht so alt war wie du."
+
+"Im Leben nicht.", meinte Azzuro.
+
+"Mit dem Rechnen steht sie auf Kriegsfuss, vergiss das  nicht.  Immerhin  kann
+sie ja noch nicht einmal sagen, wie  alt  Julia  ist."  Ulo  betrachtete  Frau
+Capulet eingehend. "Aber so ungefaehr koennte es hinkommen. Sie  ist  bestimmt
+nicht halb so alt wie ihr Mann."
+
+"Wahrscheinlich warst du schon reifer, Mama."
+
+"Wenn sie nicht merkt, dass Julia sie auf den  Arm  nimmt,  ist  sie  bestimmt
+schon um einiges reifer." Azzuro zupfte bedaechtig eine Feder zurecht.
+
+"Um wen geht es ueberhaupt?", wollte Julia wissen.
+
+"Paris hat sich in dich verliebt.", antwortete ihre Mutter.
+
+"Oh." Julia schien den Gedanken zu verarbeiten.
+
+Polly brach in helles Entzuecken aus. "Ein richtiger Mann,  Julia.  Stell  dir
+das nur vor! Ausserdem findest du bestimmt keinen Besseren, er ist  weich  wie
+Wachs."
+
+"Seltsame Prioritaeten.", murmelte Azzuro.
+
+"Sie hat schon recht. Das ist es, worauf  es  bei  einem  Mann  ankommt."  Ulo
+grinste niedertraechtig.
+
+"Ach?"
+
+"Wuesstest du etwas anderes?" Ulo  zerzauste  das  Gefieder  ihres  Gefaehrten
+wieder.
+
+"Denk darueber nach, Julia. Er ist wirklich eine gute  Partie."  Frau  Capulet
+wandte sich ihrem Spiegel zu.
+
+"Ganz richtig, eine wunderbare Partie.", stimmte Polly aus vollem Herzen zu.
+
+Frau Capulet betrachtete im Spiegel das zweifelnde Gesicht ihrer Tochter.  "Du
+wirst ihn heute abend kennenlernen. Schau  ihn	dir  an,  dann	wirst  du  mir
+zustimmen, dass er wirklich ein netter Mann ist."
+
+"Er hatte ja Zeit genug, einer zu werden.", versetzte Julia.
+
+"Es fehlt ihm nur noch eines zu seinem Glueck. Die richtige Frau."
+
+"Und das soll ich sein?"
+
+"Er ist ein Buch, das noch keinen Einband hat."
+
+"Zumindest ist er so verknittert." Julia zog ein Gesicht.
+
+"Und wenn du ihn heiratest, dann kann man ihn nach dem  Einband  beurteilen.",
+sprang Polly hilfreich ein.
+
+"Ich meine mich zu erinnern, dass du mich gelehrt haettest, genau das nicht zu
+tun. Natuerlich ist mein Gedaechtnis nicht so gut wie das deine, Polly."
+
+"Sieh es doch mal so: Es  gibt  bestimmt  haesslichere,  aeltere  und  aermere
+Maenner als ihn.", sprang Polly hilfreich ein.
+
+Ulo schuettelte sich angewidert. "Das klingt ja fast wie eine Drohung."
+
+"Ein Mann wie er wird  deine  Position  nicht  verschlechtern.",  fuegte  Frau
+Capulet hinzu.
+
+"Nicht verschlechtern!" Polly war entsetzt. "Frauen wachsen an ihren Maennern,
+also verbessert sich Julias Position sogar."
+
+"Bestimmt werde ich beizeiten einiges ueber Krankenpflege lernen."
+
+"Du tust gerade so, als stuende er schon mit einem Bein im Grab." Frau Capulet
+schuettelte unwillig den Kopf. "So alt ist er wirklich noch nicht."
+
+"Wenn du darauf  bestehst,  werde  ich  mir  wohl  seine  Annaeherungsversuche
+gefallen lassen muessen, Mama."
+
+"Das ist doch eine Farce!", empoerte sich Ulo.
+
+"Wieso?", verlangte Azzuro zu wissen. "Immerhin hat sie ihre Tochter gefragt."
+
+"Hat sie? Ich finde, das klang eher wie ein Befehl. Und Polly  sollte  zu  ihr
+halten, statt dessen laesst sie Julia im Stich."
+
+"Nein, das tue ich nicht." Polly war  zum  Fenster  getreten  und  hatte  Ulos
+letzte Worte gehoert.
+
+"Doch."
+
+"Nein. Julia muss so oder so heiraten, da ist ein Mann  wie  Paris  nicht  die
+schlechteste aller Moeglichkeiten."
+
+"Und wenn sie ihn gar nicht will?"
+
+"Sie wird ihn schon wollen. Wie mein Mann damals sagte, sie wird  schlau  sein
+und auf den Ruecken fallen."
+
+Ulo schnaubte wuetend.
+
+_Ulo_! _Das_ _ist_ _die_ _letzte_ _Warnung_!
+
+"Polly, wenn du sie jetzt mit diesem Paris verkuppelst, bekommt  sie  nie  den
+Mann, der ihr bestimmt ist.", sagte Ulo.
+
+Azzuro wirft einen misstrauischen Blick auf den Radiergummi, der Ulo  aus  der
+Geschichte entfernen soll und knurrt nur drohend: "Streik gefaellig?"
+
+"Das ist nicht wahr.", verteidigte sich Polly.
+
+"Du wirst es sehen."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite6 b/d/seher/haeuser/special/special/rom_jul/seite6
new file mode 100644
index 0000000..a0972db
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite6
@@ -0,0 +1,105 @@
+DER ERSTE AKT
+	  DIE VIERTE SZENE
+
+"Ich mag nicht.", murrte Romeo und versuchte, Benevolios Hand  abzuschuetteln,
+die ihn langsam aber stetig auf die Villa der Capulets zuzog.
+
+"Ach komm schon, stell dich nicht so an." Mercutio stupste  seinen  Freund  in
+die Seite. "Wir werden tanzen  und  uns  darueber  amuesieren,  wie  der  alte
+Capulet sich vollaufen laesst."
+
+"Wir fliegen sowieso raus, noch bevor wir drinnen sind."
+
+"Das geht nicht.", kommentierte Benevolio, der sich  fest  vorgenommen  hatte,
+Romeo aufzuheitern. "Es wird bestimmt lustig."
+
+"Klar. So lustig, wie mein Traum letzte Nacht."
+
+"Hey, Traeume sind Schaeume.", meinte Mercutio.
+
+"Was hast du denn getraeumt?" Benevolio nahm seinen Freund ernster.
+
+"Bestimmt von einer Frau.", sagte Mercutio lachend. "Davon traeumen  doch  die
+meisten Leute."
+
+"Nein, eine Frau war es nicht."
+
+Mercutio rueckte betont einen Schritt von Romeo ab. "Aeh, gibt  es  da  etwas,
+was du uns sagen solltest? Nicht, dass ich dir zu nahe	treten	wollte,  aber,
+aeh..." Er grinste breit.
+
+"Romeo, hoer mit dem Unsinn auf. Jeder traeumt einmal. Du  hast  bestimmt  nur
+etwas Unrechtes gegessen."
+
+"Ich habe gar nicht gegessen."
+
+"Na, siehst du, was sage ich?"
+
+Benevolio warf einen verwirrten Blick auf Mercutio, beschloss dann  aber,  das
+Thema nicht weiter zu  verfolgen  und  zupfte  statt  dessen  Romeos  Krawatte
+zurecht. "So, jetzt siehst du gut aus."
+
+"Ich habe wirklich keine Lust zu tanzen." Romeos Gesicht hatte einen Ausdruck,
+den jede Mutter sofort erkannt und mit einem strengen Blick geahndet haette.
+
+"Dann geh wenigstens mit hinein  und  tu  so,  als  ob  du  dich  amuesierst."
+Benevolio rueckte auch seine eigene Krawatte gerade. "Der  Appetit  kommt  mit
+dem Essen."
+
+"Siehste, sag ich doch." Azzuro imitierte ueberzeugend Mercutios Stimme.
+
+"Hm, Mercutio, wir sprechen inzwischen ueber etwas anderes." Romeo konnte sich
+ein Grinsen nicht verkneifen.
+
+"Ich?" Mercutio deutete auf sich selbst. "Ich habe nichts gesagt."
+
+"Wisst ihr, ich habe getraeumt, dass ich heute  meinem  Verhaengnis  begegne."
+Romeo  schaffte  es  erstaunlich  schnell,   seine   melancholische   Stimmung
+wiederzubeleben.
+
+"Das Verhaengnis kenne ich. Es faengt mit R an und hoert  mit  osaline  auf.",
+meinte Benevolio. "Warte es erst einmal ab, vielleicht gefaellt  sie  dir  gar
+nicht mehr, wenn du sie erst einmal betrunken siehst."
+
+"Die edle, durchgeistigte  und  wunderschoene  Rosaline  und  sich  betrinken?
+Benevolio, wie kannst du so etwas unterstellen!" Mercutio legte  die  Arme  um
+seine Freunde.
+
+"Wahrscheinlich wird es Romeo viel mehr stoeren, wenn er sie mit  den  anderen
+gackernd in der Ecke stehen sieht."
+
+"Sie gackert nicht.", brummte Romeo veraergert.
+
+"Oh,  ich  vergass."  Mercutio  zwinkerte  Benevolio  zu.  "Sie  kichert   nur
+maedchenhaft hinter der vorgehaltenen Hand."
+
+"Ach, Romeo, was sind Sie aber auch fuer ein Schlimmer.", quiekte Benevolio im
+Falsett.
+
+"Hoert auf, ich komme ja schon mit, aber tanzen werde ich nicht."
+
+"Das werden wir ja sehen. Vielleicht frage ich Rosaline, ob sie mit mir tanzt,
+dann wette ich, dass du nichts eiligeres zu tun hast,  als  mich  abzuloesen."
+Mercutio nahm Benevolios Hand. "Ach  bitte,  schoenste  Rosaline,  wollen  Sie
+nicht mit mir tanzen?"
+
+"Oh, nur zu gerne, liebster Mercutio." Benevolio machte  einen  Knicks.  "Aber
+treten Sie mir nicht so auf die Fuesse wie Romeo!"
+
+Romeo ergriff Benevolios Krawatte und zog kraeftig daran. "Ich  bin  ihr  noch
+nie auf die Fuesse getreten."
+
+"Weil du noch nie mit ihr getanzt hast.", grinste Benevolio.
+
+"Ausserdem, wenn du ihr noch nie auf die Fuesse getreten  bist,  verstehe  ich
+nicht, warum sie dich nicht lieben will.", fuegte Mercutio hinzu.
+
+Romeo verzog schmollend das Gesicht. "Das weiss ich auch nicht."
+
+"Schluss jetzt, wir gehen hinein und du wirst dich schon amuesieren, Romeo."
+
+"Das klingt wie eine Drohung, Benevolio.", lachte  Mercutio.  "Amuesier  dich,
+Romeo, bis dir das Lachen im Halse stecken bleibt."
+
+"Das tut es bestimmt.", meinte Romeo duester.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite7 b/d/seher/haeuser/special/special/rom_jul/seite7
new file mode 100644
index 0000000..9ffa64b
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite7
@@ -0,0 +1,420 @@
+DER ERSTE AKT
+	  DIE FUENFTE SZENE
+
+Der Abend kam und mit ihm die Gaeste, die James  dank  Romeos  Hilfe  gefunden
+hatte. Sie wissen, wie es bei solchen Festen zugeht: Die  Menschen,  die  sich
+dort treffen, sind einander haeufig spinnefeind und  kommen  nur  deshalb  zur
+Party, weil sie jemand anderem Bosheiten sagen wollen, die  nichtsdestoweniger
+freundlich klingen. Niemand geht gerne zu einer  solchen  Veranstaltung,  doch
+man kann sich nicht  erlauben,	ihr  fern  zu  bleiben,  weil  man  sonst  die
+Geruechte verpasst, die bis zur naechsten Party den Gespraechsstoff  fuer  die
+ganze gute Gesellschaft bilden werden.
+
+James wirbelte mit einem Staubtuch in der  Hand  ein  letztes  Mal  durch  das
+Wohnzimmer der Capulets, aus dem man wohlweislich alles Zerbrechliche entfernt
+hatte. "Wo steckt Potpan?"
+
+"Was weiss denn ich?", fragte sein Kollege.
+
+"Da stehen ja noch die Stuehle! Muss ich  denn  alles  selbst  machen?"  James
+schnappte sich einen Kuechenjungen bei den Haaren und schubste ihn in Richtung
+der unschuldig dastehenden Stuehle. "Los, bring sie raus."
+
+"Jawoll, Jungchen, hol ihn raus." Jamesens Kollege hielt sich den Bauch.
+
+"Keine Zeit jetzt fuer Witze.", grollte James. "Sieh zu, dass ein  Stueck  von
+dem Marzipan fuer mich uebrigbleibt."
+
+"Mach ich.", antwortete der andere und verschwand mit einem  Tablett  auf  dem
+Arm in Richtung Kueche.
+
+James stapelte die Stuehle aufeinander. "Potpan!"
+
+"Ich bin ja schon da. Komm mal wieder runter." Potpan leckte  sich  die  Reste
+der Schokoladensosse von den Fingern.
+
+"Wo hast du eigentlich nicht die Finger drin?", fragte James. "Los,  los,  wir
+haben nicht ewig Zeit. Ist die Buehne fertig?"
+
+"Ist sie."
+
+"Du hast auch die Ruhe weg."
+
+"Besser ruhig bleiben als einen Herzinfarkt bekommen.", grinste  Potpan.  "Hm,
+ist das Marzipan?"
+
+"Es ist _mein_ Marzipan." James stopfte sich das Stueck in den Mund. "Bo eiben
+ie enn?"
+
+"Kein Grund, sich zu ueberschlagen, im Moment stehen sie auf der Terrasse  und
+rauchen. Zeit genug, hier fertig zu werden."
+
+James schluckte den letzten Bissen hinunter und rieb sich den Bauch. "Das  war
+gut. Also, weiter jetzt, den letzten beissen die Hunde."
+
+"Den letzten beisst James.", grinste Potpan.
+
+Die Musiker, die gerade mit dem  Stimmen  ihrer  Instrumente  fertig  geworden
+waren, spielten den River Kwai Marsch und begleiteten damit James Abgang.
+
+"Jetzt zitieren sie nicht nur aus voellig anderen Quellen, jetzt  spielen  sie
+auch noch die falsche Musik." Azzuro schuettelte sich verzweifelt.
+
+"Goenn ihnen den Spass.", meinte Ulo. "Nachher muessen  sie  eh  nur  Schlager
+spielen."
+
+Azzuro deutete auf die Tuer. "Da kommen sie. Aber wo steckt denn Romeo?"
+
+"Der kommt bestimmt gleich." Ulo zuckte die Schultern. "Er wird das Rendezvous
+mit seinem Schicksal kaum verpassen wollen."
+
+"Klang aber vorhin ganz so. Na, wir werden sehen. Julia ist jedenfalls da."
+
+"In diesem weissen Kleid sieht sie ziemlich schlank aus." Ulo schuettelte  den
+Kopf. "Sie haette besser etwas farbiges angezogen."
+
+"Schlank?"  Azzuro  schaute  hinueber.  "So  nett  kann  auch  nur  eine  Frau
+untertreiben."
+
+Frau Capulet, deren blondes Haar  ueber  das  dunkelblaue  Kleid  zu  fliessen
+schien, beugte sich zu ihrer Cousine herueber. "Meine Liebste,  was  fuer  ein
+wunderschoenes Kleid du da traegst!" Ihre Augen funkelten.  "Ich  habe  diesen
+Stil letztes Jahr schon an unserer Putzfrau bewundert, du musst mir  unbedingt
+sagen, wo du es hast machen lassen."
+
+Helena laechelte und zeigte dabei zu viele  Zaehne.  "Aber  nur  wenn  du  mir
+sagst, welcher Friseur dein Haar zurichtet."
+
+Ulo bog sich auf ihrem Ast vor unterdruecktem Lachen. "Hast  du  das  gehoert,
+Azzuro?" Sie prustete los. "Zurichtet..."
+
+"Hm?" Azzuros Aufmerksamkeit galt den juengeren unter den weiblichen Gaesten.
+
+"Diese  Stichelei  ist  doch  das  schoenste  an  so  einem  Fest."  Ulo   war
+offensichtlich in ihrem Element.
+
+Herr Capulet breitete theatralisch die Arme aus. "Gegessen  haben  wir,  jetzt
+wollen wir  den  Damen	Gelegenheit  geben,  die  Kalorien  abzuarbeiten."  Er
+verneigte sich uebertrieben vor Frau Capulet. "Meine Liebe?"
+
+Das Gesicht, das Frau Capulet zog, liess keinen Zweifel daran,	dass  bei  den
+Qualitaeten ihres Gatten als Taenzer die Betonung auf  der  ersten  Silbe  von
+Qualitaet lag. "Vielleicht  sollten  wir  heute  die  jungen  Leute  den  Tanz
+eroeffnen lassen, mein Lieber." Sie winkte Julia und  Paris  heran.  "Seid  so
+lieb, Kinder."
+
+"Ganz recht, Liebste, wir werden allmaehlich zu  alt  dafuer.",  stimmte  Herr
+Capulet zu und erntete dafuer einen giftigen Blick von	seiner	Frau,  den  er
+jedoch tunlichst uebersah. "Was, Hermann, ist schon eine ganze Zeit her,  dass
+wir nur zum Spass getanzt haben, nicht?"
+
+"Ja doch." Sein Cousin Hermann liess  sich  schwer  neben  ihn  auf  die  Bank
+fallen. "Das ist bestimmt schon zwanzig Jahre her."
+
+"Ach, so lang kann das doch nicht sein. Schliesslich haben wir  bei  Lucentios
+Hochzeit noch maechtig die Maedchen gewirbelt.", lachte Capulet.
+
+"Nein, da bin ich ganz sicher. Schliesslich war  Lucentios  Hochzeit  nur  ein
+halbes	Jahr  nach  Klein-Markus  Taufe,  und  der  wird   demnaechst	selber
+Schwiegervater."
+
+"Das kann doch nicht sein. Kinder, Kinder, wie die Zeit vergeht." Herr Capulet
+taetschelte die Hand seiner Frau und laechelte duemmlich, als ihm aufging, was
+er gerade gesagt hatte. "Aber damals gab es dich  ja  noch  gar  nicht,  meine
+Liebe."
+
+"Pfft.", machte Frau Capulet und wandte sich  wieder  ihrer  Cousine  zu,  die
+einen Moment lang die Beherrschung verlor und ueber das ganze Gesicht grinste.
+
+"Schau an, Hermann, wir bekommen sogar  unerwarteten  Besuch."  Capulet  erhob
+sich und ging auf Romeo und seine Freunde zu. "Willkommen, meine Herren,  aber
+nur, wenn sie auch wissen wie man tanzt."
+
+Mercutio verbeugte sich andeutungsweise. "Ja, das wissen wir."
+
+"Dann fuehlen Sie sich ganz wie daheim."
+
+Romeos Augen schweiften durch den Saal und suchten Rosaline, bis sie an  Julia
+haengenblieben. Einen Augenblick lang hielt die Welt den Atem an. Die  Musiker
+verstummten, die Taenzer standen still,  die  aelteren	Maenner  liessen  ihre
+Weinglaeser sinken, den Damen blieben fuer einen Moment die Bosheiten im Halse
+stecken und  selbst  James  vergass  fuer  den	Hauch  eines  Augenblicks  das
+Schnapsglas in seiner Hand. Eine Gasse schien sich durch die Menschen hindurch
+aufzutun, in der die metaphorischen Blitze, die zwischen den Augen Romeos  und
+Julias hin und herschossen keinen Schaden anrichten konnten.
+
+Dann fiel James das Glas aus der Hand und der Bann war gebrochen.
+
+Romeo ergriff den Aermel eines Kellners. "Wer ist sie?"
+
+"Die obligatorische Frage.", murmelte Azzuro und versuchte, die Faszination zu
+ueberspielen, die auch ihn erfasst hatte.
+
+"Keine Ahnung, ich bin nur zur Aushilfe hier.", antwortete der Kellner.
+
+"Die obligatorische Antwort." Ulo schwelgte in  der  Romantik  der  Situation.
+"Halt dich fest, Azzuro, jetzt legt er los."
+
+Azzuro stoehnte. "Ich geh mir derweil eine Fliege fangen."
+
+Auch Mercutio war Romeos verzueckter Gesichtsausdruck nicht entgangen. "Wo ist
+denn Rosaline?"
+
+"Wer ist Rosaline?", fragte Romeo. Er blickte zu Ulo hinueber,  die  sich  auf
+ihrem Ast vorlehnte. "Sie ist wie eine Taube, die zwischen Kraehen fliegt, so,
+wie sie da drueben zwischen den anderen Frauen	steht.	So  schlank  wie  eine
+junge Birke."
+
+Azzuro kam mit einer Motte im Schnabel zurueck und reichte  sie  Ulo.  "Birke?
+Das sind die mageren Dinger, die bei jedem Sturm Schwierigkeiten haben, stehen
+zu bleiben, nicht?"
+
+"Du bist widerlich, Azzuro." Ulo verspeiste die  Motte,  ohne  den  Blick  von
+Romeo zu lassen.
+
+"Ich habe noch nie eine Frau wie sie gesehen.  Und  ich  Trottel  dachte,  ich
+liebte Rosaline!"
+
+"Trottel ist das richtige Wort.", murmelte Azzuro.
+
+"Sag ich doch die ganze Zeit.", versetzte Ulo.
+
+Tybalt drehte den Kopf und erkannte Romeo. "Was denn, wie denn? Das  ist  doch
+Romeo Montague, da drueben?", fragte er Herrn Capulet.
+
+"Ja und?" Herr Capulet verstand seinen Neffen nicht.
+
+"Er ist bestimmt nur hergekommen, um uns das Fest zu verderben." Tybalt loeste
+seinen Degen in der Scheide.
+
+"Wie kommst du denn darauf?"
+
+"Er ist ein Montague, oder? Kannst du dir etwas anderes vorstellen,  als  dass
+er keine Gelegenheit auslaesst, uns zu aergern?"
+
+"Das ihr jungen Leute auch immer nur das Schlechte in anderen sehen muesst. Er
+ist jung, also wird er hergekommen sein, um zu tanzen. Gib also Ruhe."
+
+"Lieber Onkel, dem werde ich's zeigen.", knurrte Tybalt.
+
+"Wenn du darauf bestehst, dann besiege ihn auf der Tanzflaeche."
+
+"Mit einem Degen in der Hand.", freute sich Tybalt.
+
+"Nein, mit einem Maedchen im Arm." Capulet  dachte  an  seine  Geldboerse  und
+daran, dass er sich eine neuerliche Geldstrafe nicht leisten konnte. "Immerhin
+ist er ein Gast und im Gegensatz zu dir ist er	mir  heute  abend  noch  nicht
+unangenehm aufgefallen."
+
+"Aber..." Tybalt konnte nicht aus seiner Haut.
+
+"Benimm dich, verstehst du? Ja, Livia, da hast du ohne Zweifel recht." Er warf
+Tybalt einen letzten Blick zu und kam  dann  seinen  Pflichten	als  Gastgeber
+nach.
+
+"Oh-oh.", machte Ulo.
+
+"Er ist sauer.", stimmte Azzuro ihr zu.
+
+"Sauer? Er ist daemlich." Tybalt bezog Azzuros Kommentar auf seinen Onkel.
+
+"Krah.", antwortete Ulo.
+
+"Weisst du was, Kraehe?" Tybalts Augen glitzerten.
+
+"Krah?"
+
+"Ich werde Romeo zeigen, was ich von ihm halte. Vielleicht nicht  jetzt,  denn
+mein tueddeliger Onkel will mich ja nicht lassen, aber bald. Sehr bald."
+
+Azzuro sah Tybalt nach. "Das also war der Auftritt des Boesewichts."
+
+"Vergiss ihn. Guck lieber da rueber. Sind  sie  nicht  niedlich?"  Ulos  Augen
+hatten einen  Ausdruck,  den  Azzuro  von  Frauen  kannte,  die  einander  mit
+Kinderwagen im Park begegneten und ihre Kinder verglichen.
+
+Romeo hatte inzwischen den Brei in seinen Beinen  besiegt  und	es  geschafft,
+sich durch das Gedraenge der Gaeste zu Julia vorzuarbeiten. Nun stand  er  vor
+ihr und schien einen ernsthaften Versuch zu unternehmen,  in  ihren  Augen  zu
+ertrinken.
+
+"Er sieht laecherlich aus." Azzuro kannte  kein  Mitgefuehl  fuer  den  frisch
+Verliebten. "Schnappt mit offenem  Mund  nach  Luft  wie  ein  Fisch  auf  dem
+Trockenen."
+
+Nach einer Weile sah Romeo auf seine Hand  hinab  und  eine  Eingebung	zuckte
+durch sein Gesicht wie ein Blitz ueber einen dunklen Himmel. "Darf ich dir die
+Hand geben?"
+
+Azzuro stoehnte.
+
+"Warum?" Julia hatte Romane gelesen.
+
+"Ich moechte dir guten Tag sagen."
+
+"Ah. Ja, du darfst mir die Hand geben." Julias Seufzer verschmolz mit Azzuros,
+der sich auf einer Zimmerpflanze niedergelassen hatte.
+
+"Wenn ich dich anfasse...", fluesterte Azzuro. "Nein,  warte.  Wenn  ich  dich
+beruehre, fuehle ich mich wie ein kleiner Junge vor einem Weihnachtsbaum."
+
+Romeo wiederholte Azzuros Worte.
+
+"Weihnachtsbaum? Habe ich denn pieksige Nadeln? Oder bin ich zu aufgeputzt?"
+
+"Aeh..", machte Romeo und warf einen verzweifelten Blick auf Azzuro,  der  nur
+haemisch grinste und den angehenden  Liebhaber	seinem	Schicksal  ueberliess.
+Romeo holte tief Luft  und  ging  im  Geiste  alle  mit  einem	Weihnachtsbaum
+assoziierten Gedanken durch, doch abgesehen von den Nadeln  und  dem  Schmuck,
+die Julia bereits erwaehnt hatte, wollte ihm nur der Duft  einfallen.  Moment,
+dachte er, der Duft! "Ich wollte sagen, du duftest wie ein Weihnachtsbaum. So,
+als sei ein ganzer Wald in einem einzigen Zimmer." Romeo fuehlte die Alpen von
+seinem Herzen fallen.
+
+Julia holte tief Luft, besann sich dann aber ihrer guten Manieren und statt zu
+bruellen, senkte  sie  die  Stimme  zu	einem  unheilverkuendenden  Fluestern.
+"Willst du wirklich sagen, dass ich nach _HO-Frisch_  rieche?"
+
+"Was meint sie mit HO-Frisch?", fragte Azzuro verwirrt.
+
+"Haimliches-Oertchen-Frisch.", antwortete Ulo,  die  Romeos  Leid  nicht  mehr
+ertragen konnte und ihm leise ins Ohr fluesterte: "Ich stehe vor dir  wie  ein
+Kind und bewundere deine Schoenheit. Wo du bist, werden Traeume wahr."
+
+Romeos Lippen bewegten sich, als er die Worte wiederholte und an der richtigen
+Betonung ueberlegte. Schliesslich fasste er sich ein Herz und sprach sie  laut
+aus. Julia, die schon halb entschlossen gewesen war, den jungen Mann, der sich
+so toelpelhaft benahm stehenzulassen, schmolz dahin. "Wirklich?"
+
+"Oh, ja.", versicherte ihr Romeo. "Und suess wie ein Marzipanapfel."  Nun,  da
+er einen Hinweis bekommen hatte, kam  seine  Zunge  mit  dem  Aussprechen  der
+Komplimente kaum noch nach. "Dein Haar ist golden wie das  Kleid  des  Engels,
+den wir daheim immer auf die Spitze setzen. Deine Haende sind genauso zierlich
+wie seine. Weisst du, er ist aus Wachs, und leider ist er einmal  zu  nahe  an
+eine Kerze geraten und dabei sind die Haende ein  bisschen  geschmolzen.  Aber
+man kann immer noch sehen, dass es Haende sind. Dein Kleid  glitzert  wie  die
+Staniolsterne, die wir immer werfen, damit sie nicht so  hingesetzt  aussehen.
+Deine Augen schimmern wie die Kerzen und sowieso habe ich  das	Gefuehl,  dass
+sie mich gleich verbrennen werden." Auf den letzten Satz war  Romeo  besonders
+stolz, denn er hatte einmal in einem Roman seiner Mutter  gestoebert  und  war
+ueber die Worte _brennende_ _Liebe_ gestolpert.
+
+Julia ueberlegte kurz, ob sie auf die geschmolzenen Haende des Engels  genauer
+eingehen sollte, doch dann schob sie den Gedanken  beiseite.  "Wenn  ich  eine
+Kerze waere, wuerde ich schmelzen.", stellte sie fest.
+
+"Darf ich dich schmelzen?", fragte Romeo, der nun keck wurde.
+
+"Wenn du die Flamme  bist,  die  mich  schmilzt,  gehst  du  aus,  sobald  ich
+geschmolzen bin."
+
+Romeo dachte einen Moment nach. "Oh, das ist nicht schlimm, denn wenn ich  das
+Wachs nicht mehr habe, nuetzt mir auch der Docht nichts."
+
+Julia warf einen zweifelnden Blick auf Romeos Mitte. "Docht?"
+
+"Aeh..." Romeo zoegerte. "Naja, ich meine, wenn du nicht  in  der  Welt  bist,
+dann kann ich sowieso nie mehr brennen."
+
+"Ach so."
+
+Polly, die mit Argusaugen ueber die Tugend der	beiden	jungen	Leute  gewacht
+hatte, waehlte diesen Augenblick, um zu Julia zu treten. "Deine Mutter moechte
+dich sprechen, Schatz."
+
+Julia drehte das Gesicht zur Seite und zog eine Schnute, dann zuckte  sie  mit
+den  Schultern	und  ging  zu  ihrer  Mutter,  die  am	Ausgang   die	Gaeste
+verabschiedete.
+
+"Wer ist denn ihre Mutter?", fragte Romeo.
+
+"Das wissen Sie nicht?" Polly war ehrlich  erstaunt.  "Ihre  Mutter  ist  Frau
+Capulet, und wenn es  stimmt,  dass  Toechter  die  juengeren  Ausgaben  ihrer
+Muetter sind, dann ist sie eine tugendhafte und kluge Frau."
+
+"Hm.", machte Romeo.
+
+"Naja, vielleicht hat sie auch  ein  bisschen  von  meinem  guten  Gedaechtnis
+mitbekommen. Und ausserdem bekommt sie ihres Vaters Moneten."
+
+"Was kann ein Mann sich mehr wuenschen.", meinte Azzuro leise.
+
+"Prima.", seufzte Romeo bitter. "Dann kann ich sie also gleich vergessen. Aber
+das will ich nicht, und wenn sie tausendmal  die  Tochter  des	alten  Capulet
+waere."
+
+Benevolio nahm seinen Cousin beim Arm. "Komm, Romeo, wir  wollen  gehen.  Sieh
+nur, wir sind schon fast die letzten, gleich beissen uns die Hunde."
+
+"Ja, so kommt es mir vor.", murmelte Romeo und folgte Benevolio zur Tuer.
+
+Herr Capulet laechelte breit, als er seine Gaeste sah. "Aber meine Herren, Sie
+wollen doch nicht schon gehen?" Sein Gesicht bekam einen erstaunten  Ausdruck,
+als ihm Tybalt ins Ohr fluesterte. "Tatsaechlich?"  Er  rieb  sich  ueber  die
+Glatze. "Na, dann muessen Sie wohl gehen, es war mir ein Vergnuegen, Sie  hier
+begruessen zu duerfen."
+
+Tybalt schenkte Romeo ein Laecheln, das an einen Tiger gemahnte. "Gute  Nacht,
+Romeo."
+
+Frau Capulet, die weitere Zusammenstoesse vermeiden  wollte,  hakte  sich  bei
+ihrem Mann ein. "Komm, Lieber, wir wollen zu Bett  gehen,  ich  bekomme  meine
+Migraene."
+
+Julia verstaute den Trick zum spaeteren Gebrauch in ihrem Gedaechtnis und  zog
+an Pollys Aermel. "Wer ist der da?" Sie zeigte verstohlen mit dem Finger.
+
+"Das ist Tiberio junior. Der einzige Sohn und Erbe des alten  Tiberio."  Polly
+laechelte.
+
+"Und der?"
+
+"Petruchio, aber der ist nichts besonderes."
+
+Wieder deutete Julia mit dem Finger. "Und der da? Der den ganzen  Abend  nicht
+ein Mal getanzt hat?"
+
+Polly warf ihr einen misstrauischen Blick zu. "Warum willst du das denn  alles
+wissen?"
+
+"Och, ich muss doch die Leute kennen, oder?" Julia schaute ihre Amme  mit  dem
+Blick an, der, wie sie wusste, am  besten  geeignet  war,  jeden  Verdacht  zu
+zerstreuen.
+
+"Ja. Aber wer der Junge ist, weiss ich nicht."
+
+"Dann tu mir die Liebe und frag ihn nach  seinem  Namen,  ja?"  Julia  seufzte
+tief. "Wenn er schon verheiratet ist, werde ich ins Kloster gehen." Undeutlich
+erinnerte sie sich an einen Roman, in dem der Held, nach langen Jahren endlich
+Witwer, in einem Kloster seine einzige Geliebte trifft und diese ihre Geluebde
+sofort vergisst.
+
+"Nein, dann wirst du Paris heiraten.", meinte Polly bestimmt.  "Er  ist  Romeo
+Montague."
+
+"Das gibt es doch nicht!" In Julias Augen machte sich Schrecken breit. "Er ist
+der einzige Mann, den ich lieben kann, und ausgerechnet  ihn  darf  ich  nicht
+lieben,  weil  sonst  mein  Vater  einen  Herzanfall  bekommt."  Eine  weitere
+Erinnerung schwamm in ihrem Hirn nach oben wie eine Wasserleiche:  Eine  junge
+Frau, die heiratet und ungluecklich ist, bis ihr Geliebter  sie  aus  der  Ehe
+entfuehrt, damit sie in einem fernen Land gluecklich wuerden. Sie seufzte noch
+einmal. "Wenn das so ist, werde  ich  wohl  fuer  immer  ungluecklich  sein.",
+meinte sie und gefiel sich bereits jetzt in ihrer Rolle als Isolde.
+
+"Was soll denn der Unsinn, Kind?", fragte Polly unwillig.
+
+"Ach, schon gut, das verstehst du sowieso nicht.", antwortete Julia mit  einem
+tragischen Laecheln.
+
+Die Stimme Frau Capulets rief nach ihrer Tochter und Polly schob  Julia  sanft
+aber bestimmt ins Haus. "Komm, Zeit, ins Bett zu gehen. Ausserdem  sind  jetzt
+alle weg und es gibt nichts mehr zu sehen."
+
+"Stimmt." Azzuro gaehnte. "Hoechste Zeit fuer einen gemuetlichen Ast, Liebes."
+
+"Es kommt noch was, geschlafen wird spaeter."
+
+"Oh, nein, nicht heute.", stoehnte Azzuro.
+
+"_Das_ meine ich nicht."
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite8 b/d/seher/haeuser/special/special/rom_jul/seite8
new file mode 100644
index 0000000..e07f4df
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite8
@@ -0,0 +1,72 @@
+DER ZWEITE AKT
+
+An dieser Stelle sollte eigentlich wieder ein Chor auftreten, doch  inzwischen
+haben Sie gewiss nichts mehr gegen die Kuerzungen im Etat und nehmen  mit  Ulo
+und Azzuro vorlieb. Wenn ich ehrlich sein soll, mir sind  die  beiden  richtig
+ans Herz gewachsen, obwohl ein Chor wahrscheinlich fuegsamer waere.
+
+"N'Abend, Kollege." Azzuro nickte einer Fledermaus zu,  die  im  Schimmer  der
+Lampen im Haus nach Insekten jagte.
+
+"Ssseid ssspaet dran.", kommentierte die Fledermaus. "Wollt ihr nissst  lieber
+ssslafen gehen?"
+
+"Koennen wir noch nicht." Ulo gaehnte hinter dem vorgehaltenen Fluegel.
+
+"Wasss issst denn noch?" Bedauernd blickte die Fledermaus hinter  einer  Motte
+her, die Azzuro ihr weggeschnappt hatte.  "Wenn  ihr  ssso  ssspaet  noch  auf
+ssseid, dann lassst wenigssstensss die Sssnaebel von  meinem  Fruehssstueck.",
+schalt sie.
+
+"Ich habe Hunger.", entschuldigte sich Azzuro.
+
+"Isss auch. Aber jetssst ersssaehlt, wasss geht denn vor?"
+
+"Wir sehen jemandem zu, der gerade die Begierde gegen die  Liebe  eingetauscht
+hat."
+
+"Nein, wie romantisss!"  Wie  alle  Fledermaeuse  war  auch  diese  hier  sehr
+gefuehlsbetont.
+
+"Isss liebe sssolssse Gesssisssten."
+
+"Ja, ich auch.", freute sich Ulo.
+
+"Ersssaehl!"
+
+"Romeo hat festgestellt, dass Liebe und Begehren nicht das gleiche ist."
+
+"Dasss haette isss ihm vorher sssagen koennnen."
+
+Ulo nickte weise. "Ich weiss. Wahrscheinlich haette es  ihm  jede  Frau  sagen
+koennen."
+
+Sie  warf  Azzuro  einen  ironischen  Seitenblick  zu.	"Aber   Maenner   sind
+Sklaven..."
+
+"... weniger anssstaendiger Absssisssten.", vollendete die Fledermaus den Satz
+verschaemt.
+
+"Genau. Na, und jetzt steckt er in  einer  boesen  Klemme."  Ulo  machte  eine
+Spannungspause.
+
+"Sssag ssson!"
+
+"Er hat sich ausgerechnet in eine Frau einer  verfeindeten  Familie  verliebt,
+und zu allem Ueberdruss liebt sie ihn auch."
+
+Traenen traten in die kurzsichtigen Augen der  Fledermaus.  "Wie  sssrecklisss
+sssoen! Wie heissst esss: Die Liebe findet einen Weg."
+
+"Wir werden sehen, ob er es schafft, den Wurm vom Haken  zu  holen  ohne  sich
+aufzuspiessen." Azzuro fand seine Freude daran, die Frauen mit wenig  schoenen
+Metaphern zu schockieren. "Er kann ihr nicht mal ein Staendchen bringen,  denn
+wenn man ihn hier im Garten erwischt..." Azzuro machte eine  eindeutige  Geste
+und schaute die Fledermaus erwartungsvoll an, die seinen  Blick  bemerkte  und
+sich mit weiblichem Grossmut doch noch zu einem entsetzten Keuchen entschloss.
+
+"Aber dasss issst ja..."
+
+"Still!"  Ulo  hatte  Romeo  jenseits  der  Mauer  entdeckt,  die  den  Garten
+umschloss.
+
diff --git a/d/seher/haeuser/special/special/rom_jul/seite9 b/d/seher/haeuser/special/special/rom_jul/seite9
new file mode 100644
index 0000000..c6c1f41
--- /dev/null
+++ b/d/seher/haeuser/special/special/rom_jul/seite9
@@ -0,0 +1,130 @@
+DER ZWEITE AKT
+	  DIE ERSTE SZENE
+
+Romeo stellte sich auf die Zehenspitzen und spaehte ueber die Mauer hinweg. Er
+wunderte sich nur einen Moment lang darueber, dass auf ein und	demselben  Ast
+eine Fledermaus kopfueber  hing  und  direkt  ueber  ihr  zwei	Kraehen.  _In_
+_dieser_ _Nacht_, so dachte Romeo mit einem Schulterzucken, _kann_  _wirklich_
+_alles_ _passieren_. _Ausserdem_: _Da_ _drin_  _irgendwo_  _ist_  _Julia_.  Er
+zwinkerte den Kraehen zu.
+
+"Wisst ihr, ich gehe heute nacht nicht  nach  Hause.  Schliesslich  ist  _sie_
+hier, da kann ich einfach nicht ins Bett gehen, als sei nichts passiert."
+
+Mercutio  und  Benevolio  tauchten  mit  dem  Laerm,  den   Betrunkene	 immer
+veranstalten, an der Strassenecke auf. Romeo glitt mit einer Behendigkeit, die
+ihn selbst erstaunte, in den Schatten eines Eingangs zurueck  und  betrachtete
+seine Freunde mit einem Grinsen.
+
+"Romeo, hey, alder Schunge, wo schteckscht du denn?", rief Benevolio.
+
+"Er ischt schlauer geweschen alsch  wir  und  hat  schisch  schon  insch  Bedd
+gelescht..."
+
+Mercutio sah ganz danach aus, als habe er fuer nichts so gute  Verwendung  wie
+fuer ein Bett.
+
+"Seinen Schaedel moechte ich  morgen  frueh  nicht  haben.",  bemerkte  Azzuro
+mitfuehlend.
+
+"Ob er wohl genaussso sssmeckt wie eine Fliege,  die  ausss  einem  Weinglasss
+gesssluerrft hat?", ueberlegte die Fledermaus.
+
+"Du willst doch nicht etwa  den  Ruf  der  Fledermaeuse  ruinieren  und  einen
+Menschen anknabbern?", fragte Ulo mit einem angeekelten Gesichtsausdruck.
+
+"Nein, esss war nur wissssensssafltissse Neugier."
+
+"Hm." Ulo schenkte  der  Fledermaus  einen  langen,  forschenden  Blick.  "Sag
+mal,wie heisst du eigentlich?"
+
+"Wlad."  Die  Fledermaus  grinste  und   leckte   sich   ueber   ihre   zarte,
+hufeisenfoermige Nase.
+
+"Wir sind Ulo und Azzuro." Ulo rueckte ein kleines Stueck von Wlad ab.
+
+"Er holt schisch den Tod, wenn  er  hier  drauschen  rumschpringt."  Benevolio
+machte sich ernste Sorgen um seinen Cousin. "Ruf  ihn,  scheine  Mu...  Mutter
+reischt mir den Kopf ab, wenn isch ohne ihn ankomme."
+
+"Isch  weisch  wasch  bescheresch."  Auch  Mercutio  kannte   Frau   Montagues
+Einstellung zu Sauftouren. "Isch habe mal in einem Buch geleschen..."
+
+"Nein!" Benevolio lachte.
+
+"Doch. Pasch auf, isch werde ihn beschwoeren."
+
+"Und wasch scholl er tun?"
+
+"Herkommen, Schnapschnasche, wasch denn schonscht?" Mercutio holte Luft.
+
+"Romeo!  Isch  beschwoere  disch  bei  deiner   Verliebtheit,   erscheine   du
+Scheuftscher."
+
+"Er ischt doch gar kein Schaeufer."  Benevolio  erinnerte  sich  dunkel,  dass
+Romeo den ganzen Abend an einem einzigen Glas Wein  genippt  hatte  und  schon
+nicht mehr bei ihnen gewesen war, als sie die Kneipe gefunden hatten.
+
+"Scheuftscher!" Mercutio verdeutlichte seine Worte, indem er tief seufzte. "Du
+bischt bloed und beschoffen."
+
+"Schtimmt." Benevolio grinste.
+
+"Romeo! Schag wasch. Esch reischt, wenn du 'Liebe' schagscht, wasch  anderesch
+bekommscht du eh nischt rausch." Mercutio  kicherte.  "Du  kannscht  auch  ein
+bi... bisch..."  Ein  konzentrierter  Ausdruck  trat  auf  Mercuitos  Gesicht.
+"...biss-chen  jammern,  dann   erkennen   wir   disch   auch.   Alscho,   bei
+Roschalinschens   langen   Beinen,   bei    ihrer    Schtupschnasche,	 ihren
+Schommerschproschen und ihrem Buschen: Romeo, erscheine!"  Benevolio  ruelpste
+ungeniert. "Wenn er disch jetscht gehoert hat, bekommscht du Pruegel."
+
+"Quatsch. Viel schlimmer waere esch doch, wenn isch ihn  riefe  und  ihn  dann
+schtehen liesche. Scho wasch machen nur gemeine Frauen." Sein  Gesicht  verzog
+sich in einer trunkenen Parodie der Trauer. "Aber  leider  schind  scho  viele
+Frauen gemein." Eine Weile hielten sich die beiden Maenner in  den  Armen  und
+gedachten traenenreich frueherer Enttaeuschungen.
+
+Nach einer Weile putzte sich Benevolio umstaendlich die Nase, die jetzt  nicht
+mehr nur vom Alkohol geroetet war. "Weischt du wasch, lasch unsch nach Hausche
+gehen, Romeo will beschtimmt den Mond anheulen, da schtoeren  wir  schowiescho
+nur." Mercutio schniefte, dann, in seinen Stimmungswechseln  so  schnell,  wie
+nur Betrunkene es sein koennen, lachte er. "Wahrscheinlisch schitscht er unter
+einem Baum und wuenscht schisch, er mueschte  nischt  allesch  schelber  tun."
+Seine eindeutige Geste liess keinen Zweifel am Inhalt seiner  Worte.  "Alscho,
+fuer misch reischt heute nacht der Betrieb in meinem  Bett."  Er  beugte  sich
+nieder und betastete den Boden. "Nee, dasch ischt mir schu hart,  lasch  unsch
+nach Hausche gehen und Romeo..." Er ueberlegte einen  Moment,  dann  rief  er:
+"Schlaf gut, Romeo. Und wenn du heut nacht  klempnerscht,  dann  verleg'  fuer
+mich ein Rohr mit."
+
+"Was fuer ein ordinaerer..." Ulo suchte in ihrem Gedaechtnis nach Ausdruecken,
+die  gleichzeitig  damenhaft  und  ihrer  Stimmung  angemessen	 waren,   fand
+allerdings keine.
+
+"Recht hassst du." Auch Wlad war empoert. "Ssso  wasss  sssollten  wir  Frauen
+unsss nissst gefallen lassssen."
+
+"Ist Vlad nicht ein Maennername?", wollte Azzuro wissen.
+
+"Mit V ssson, aber isss sssreibe misss mit W."
+
+"Oh.", machte Ulo und rueckte verlegen wieder naeher an Wlad heran.
+
+"Issst Romeo auch ssso ein Ssswein?"
+
+"Keine Ahnung." Ulo zuckte die Schultern. "Er ist ein Mann, wahrscheinlich ist
+er also um keinen Deut besser."
+
+"Was willst du damit sagen?", fragte Azzuro saeuerlich.
+
+"Ach, nichts. Still, Romeo kommt wieder."
+
+Romeo, der aus dem Schatten trat,  war	ebenso	wenig  begeistert  von	seinen
+Freunden wie Ulo und Wlad. "Der hat gut reden, so wie er gebaut  ist,  hat  er
+die Haende immer frei.", murmelte er. Sein Blick fiel auf den Balkon im ersten
+Stock und den Baum davor.
+
+"Er ueberlegt, ob er da rauf kommt.", fluesterte Ulo.
+
+"Alssso issst er doch ein Ssswein.", stellte Wlad ebenso leise fest.
+
diff --git a/d/seher/haeuser/special/special/seherfaq.c b/d/seher/haeuser/special/special/seherfaq.c
new file mode 100644
index 0000000..e881696
--- /dev/null
+++ b/d/seher/haeuser/special/special/seherfaq.c
@@ -0,0 +1,247 @@
+inherit "std/thing";
+inherit "std/more";
+inherit "/mail/nedit";
+
+#include <properties.h>
+#include <wizlevels.h>
+#include "../haus.h"
+
+#define FAQ(f) (SPECIALPATH+"faq/"+(f))
+#define FAQUSER ({ "ryne", "toss" })
+#define IS_USER(pl) (member(FAQUSER, getuid(pl)) >= 0)
+
+static string *seite;
+static string f,*s;
+
+mapping synonym = ([]);
+
+create()
+{
+  ::create();
+  SetProp(P_NAME,"SeherFAQ");
+  SetProp(P_GENDER,FEMALE); 
+  SetProp(P_NOGET,1);
+  AddId( ({"faq","seherfaq","FAQ","SeherFAQ" }) );
+  AddCmd("lies","lesen");
+  AddCmd(({"beschreib","beschreibe"}),"beschreiben");
+  AddCmd("seiten","seiten");
+  AddCmd(({"loesch","loesche"}),"loeschen");
+  AddCmd(({"kopier","kopiere"}),"kopieren");
+  restore_object(FAQ("faq"));
+  seite = get_dir(FAQ("*"))-({".","..","faq.o"});
+}
+
+init()
+{
+  thing::init();
+  if (this_player() && IS_USER(this_player()))
+    nedit::init_rescue();
+}
+
+string
+_query_short()
+{
+  if (this_player() && IS_SEER(this_player()))
+    return "Die SeherFAQ";
+  else
+    return 0;
+}
+
+string 
+_query_long()
+{
+  if (this_player() && IS_SEER(this_player()))
+    return "Dies ist Rynes SeherFAQ.\n\
+Wenn Du weitere Fragen oder Anregungen zu dieser FAQ hast,\n\
+sei so nett und schicke Ryne einen kurzen Brief\n\n\
+Zum Inhalt der FAQ kommst Du mit >lies inhalt<\n";
+  else
+    return 0;
+}
+
+int
+id(string str)
+{
+  if (!IS_SEER(this_player()))
+    return 0;
+  else
+    return ::id(str);
+}
+
+int
+seiten(string str)
+{
+  int i,j;
+  string *syn;
+  closure filter_ldfied;
+
+  if (!IS_USER(this_player()))
+    return 0;
+
+  if(!(i = sizeof(seite)))
+    write("Keine Seiten beschrieben!\n");
+  else {
+    filter_ldfied = lambda(({'ind,'page,'map_ldfied/*'*/}),
+		    ({#'==,'page,({#'[/*]'*/,'map_ldfied,'ind})}));
+    for (str = "",i-=1;i>=0;i--) {
+      syn = m_indices(filter_indices(synonym,filter_ldfied,seite[i],synonym));
+      printf("Seite '%s' - ",seite[i]);
+      if (j=sizeof(syn)) {
+	for(j-=1;j>=0;j--)
+	  printf("'%s' ",syn[j]);
+	write("\n");
+      }
+      else
+	write( "(keine Synonyme definiert)\n");
+    }
+  }
+  return 1;
+}
+
+int
+lesen(string str)
+{
+  if (!IS_SEER(this_player()))
+    return 0;
+
+  if (!str || str == "") {
+    notify_fail("Was willst Du denn lesen?\n");
+    return 0;
+  }
+  str = lower_case(this_player()->_unparsed_args());
+  if (member(seite,str)<0) {
+    if (!member(synonym,str)) {
+      notify_fail("Diese Seite gibt es nicht...\n");
+      return 0;
+    }
+    else 
+      str = synonym[str];
+  }
+  this_player()->More(FAQ(str),1);
+  return 1;
+}
+
+int
+beschreiben(string str)
+{
+  string faq,*desc;
+  int i;
+
+  if (!IS_USER(this_player()))
+    return 0;
+
+  str = lower_case(this_player()->_unparsed_args());
+
+  if (!str || str == "") {
+    notify_fail("Was willst Du denn beschreiben?\n");
+    return 0;
+  }
+  if (sscanf(str,"%s %s",faq,str)!=2 || !id(faq)) {
+    notify_fail("Syntax: beschreibe faq <seite>\n");
+    return 0;
+  }
+  str = implode(old_explode(str," ")-({""}), " ");
+  desc = old_explode(implode(old_explode(str,", "),","),",");
+  f = 0;
+  s = 0;
+
+  for (i=sizeof(desc)-1;i>=0;i--) {
+    if (member(seite,desc[i])>=0) {
+      f = desc[i];
+      s = desc-({desc[i]});
+      break;
+    }
+  }
+  if (!f) {
+    f = desc[0];
+    s = desc[1..];
+  }
+  write("Bitte Beschreibung fuer die FAQ-Seite '"+f+"' angeben\n");
+  nedit("faq");
+  return 1;
+}
+
+static void
+faq(string str)
+{
+  int i;
+  string file;
+
+  if (!str || str == 0) {
+    write("** Abbruch! **\nNichts geaendert!\n");
+    return;
+  }
+  
+  if (file_size(file=FAQ(f))>=0)
+    rm(file);
+  write_file(file, str);
+  if (member(seite,f)==-1)
+    seite += ({f});
+
+  for (i=sizeof(s)-1;i>=0;i--) 
+    synonym += ([ s[i] : f ]);
+
+  save_object(FAQ("faq"));
+}
+
+int
+loeschen(string str)
+{
+  string *syn;
+  int i;
+
+  if(!IS_USER(this_player()))
+    return 0;
+
+  str = lower_case(this_player()->_unparsed_args());
+  if (!str || str == "") {
+    notify_fail("Was willst Du denn loeschen?\n");
+    return 0;
+  }
+  if (member(seite,str)>=0) {
+    syn = m_indices(filter_indices(synonym,
+				 lambda(({'ind,'page,'map_ldfied/*'*/}),
+					({#'==,'page,({#'[/*]'*/,'map_ldfied,'ind})}))
+				 ,seite[i],synonym));
+    rm(FAQ(str));
+    seite -= ({str});
+  }
+  else {
+    str = implode(old_explode(str," ")-({""}), " ");
+    syn = old_explode(implode(old_explode(str,", "),","),",");
+  }
+  for (i=sizeof(syn)-1; i>=0; i--)
+    synonym = filter_indices(synonym,
+                             lambda(({'key,'cmp}),
+				    ({#'!=,'key,'cmp/*'*/})),
+			     syn[i]);
+
+  save_object(FAQ("faq"));
+  write("OK\n");
+  return 1;
+}
+
+int
+kopieren(string str)
+{
+  string *part;
+  int i;
+
+  if (!IS_USER(this_player()))
+    return 0;
+
+  str = lower_case(this_player()->_unparsed_args());
+  part = old_explode(str," ")-({""});
+  if(sizeof(part)<4 || !id(part[0]) || part[2] != "nach" || 
+     member(seite,part[1]) == -1) {
+    notify_fail("kopiere faq <seite> nach <alias>\n");
+    return 0;
+  }
+  str = part[1];
+  part=old_explode(implode(old_explode(implode(part[3..]," "),", "),","),",");
+  for(i=sizeof(part)-1;i>=0;i--)
+    synonym += ([ part[i] : str ]);
+  save_object(FAQ("faq"));
+  write("OK.\n");
+  return 1;
+}
diff --git a/d/seher/haeuser/tools/generator.c b/d/seher/haeuser/tools/generator.c
new file mode 100644
index 0000000..3254c2b
--- /dev/null
+++ b/d/seher/haeuser/tools/generator.c
@@ -0,0 +1,740 @@
+#include "../haus.h"
+
+inherit "/p/service/wargon/sm/statemachine";
+
+#include "/p/service/wargon/sm/statemachine.h"
+#include <properties.h>
+
+#define STATE_IDLE           0
+#define STATE_HOUSE          1
+#define STATE_ROOM           2
+#define STATE_CHEST          3
+#define STATE_DETAIL         4
+#define STATE_RDETAIL        5
+#define STATE_COMMAND        6
+#define STATE_FINALIZE       7
+#define STATE_EXITS          8
+
+#define SIG_INITIALIZE       0
+#define SIG_HOUSE            1
+#define SIG_HOUSE_DONE       2
+#define SIG_ROOM             3
+#define SIG_DO_CHEST         4
+#define SIG_DO_DETAIL        5
+#define SIG_DETAIL_DONE      6
+#define SIG_RDETAIL_DONE     7
+#define SIG_COMMAND_DONE     8
+#define SIG_DO_EXITS         9
+#define SIG_ROOM_DONE       10
+
+private nosave string file;
+private nosave int    final;
+private nosave object raum;
+private nosave int    nr;
+private nosave object hausmeister;
+private nosave mixed  hmargs;
+
+string dump(object env, int final, mixed args);
+
+private string special(string str);
+private void removeOutputFile();
+private void dumpHouse();
+private void dumpRoomHeader();
+private void dumpChest();
+private void dumpDetails();
+private void dumpReadDetails();
+private void dumpCommands();
+private void doFinalize();
+private void dumpExits();
+private void dumpRoomTrailer();
+private void doReturn();
+private void sProp(string file, string pName, string pStr);
+static void mProp(string file, string pName, mixed map_ldfied, int pos, int signal);
+static void cProp(string file, string pName, mixed cmd, int pos, int signal);
+private void prettyString(string file, string *str, int indent);
+private void prettyArray(string file, string *arr, int sep);
+private void headerRoom();
+private void trailerRoom();
+private void headerHouse();
+private void trailerHouse();
+
+create()
+{
+  ::create();
+  seteuid(getuid());
+  
+  AddState(STATE_IDLE, 0, 0);
+  AddState(STATE_HOUSE,    #'dumpHouse, 0);
+  AddState(STATE_ROOM,     #'dumpRoomHeader, 0);
+  AddState(STATE_CHEST,    #'dumpChest, 0);
+  AddState(STATE_DETAIL,   #'dumpDetails, 0);
+  AddState(STATE_RDETAIL,  #'dumpReadDetails, 0);
+  AddState(STATE_COMMAND,  #'dumpCommands, 0);
+  AddState(STATE_FINALIZE, #'doFinalize, 0);
+  AddState(STATE_EXITS,    #'dumpExits, 0);
+
+  AddTransition(SM_INVALID, SIG_INITIALIZE, STATE_IDLE, 0);
+
+  // Zustandsuebergaenge zum Schreiben des Hauses
+  AddTransition(STATE_IDLE, SIG_HOUSE, STATE_HOUSE, #'removeOutputFile);
+  AddTransition(STATE_HOUSE, SIG_HOUSE_DONE, STATE_IDLE, #'doReturn);
+
+  // Zustandsuebergaenge zum Schreiben eines Raums
+  AddTransition(STATE_IDLE,     SIG_ROOM,      STATE_ROOM, #'removeOutputFile);
+  AddTransition(STATE_ROOM,     SIG_DO_CHEST,  STATE_CHEST);
+  AddTransition(STATE_ROOM,     SIG_DO_DETAIL, STATE_DETAIL);
+  AddTransition(STATE_CHEST,    SIG_DO_DETAIL, STATE_DETAIL);
+  AddTransition(STATE_DETAIL,   SIG_DETAIL_DONE,  STATE_RDETAIL);
+  AddTransition(STATE_RDETAIL,  SIG_RDETAIL_DONE, STATE_COMMAND);
+  AddTransition(STATE_COMMAND,  SIG_COMMAND_DONE, STATE_FINALIZE);
+  AddTransition(STATE_FINALIZE, SIG_DO_EXITS,  STATE_EXITS);
+  AddTransition(STATE_FINALIZE, SIG_ROOM_DONE, STATE_IDLE, #'doReturn);
+  AddTransition(STATE_EXITS,    SIG_ROOM_DONE, STATE_IDLE, #'dumpRoomTrailer);
+  
+  StateTransition(SIG_INITIALIZE);
+}
+
+
+// Erzeuge normalen LPC-Quellcode aus einem Haus oder Raum.
+//
+// Parameter:
+//   env: Das Haus oder der Raum, aus dem Quellcode erzeugt werden soll
+//   fin: Falls gleich 0, so wird Quellcode fuer Typofixes o.ae.
+//        erzeugt; das erzeugte File heisst immer 'fixed.c'.
+//        Andernfalls wird Quellcode fuer ein eigenstaendiges Objekt
+//        erzeugt. Dieses kann z.B. zum Anschluss als "Magierhaus"
+//        verwendet werden. Die erzeugten Files heissen
+//        'rep/<name>raum<nr>.c' bzw. 'rep/<name>haus.c'. Befindet sich
+//        in dem Raum die Truhe, wird zusaetzlich die Datei
+//        'rep/<name>truhe.c' geschrieben.
+//
+// Rueckgabewert:
+//   Der Dateiname, in den die Daten geschrieben wurden.
+
+string
+dump(object env, int fin, mixed args)
+{
+  string out;
+  int num;
+
+  hausmeister = previous_object();
+  hmargs = args;
+  
+  if (QueryState() != STATE_IDLE)
+  {
+    write("Der Generator arbeitet noch...\nBitte warten!\n");
+    return "<keine Datei generiert>\n";
+  }
+  
+  // Falls ein Raum generiert wird, erhaelt <nr> die Raumnummer.
+  // Bei der Aussenansicht des Hauses wird <nr> auf -1 gesetzt.
+  if (sscanf(object_name(env), "%sraum%d", out, num) < 2)
+  {
+    num = -1;
+  }
+  
+  if (fin)
+  {
+    out = PATH+"rep/"+env->QueryOwner();
+
+    if (num >= 0)
+    {
+      out += "raum"+num+".c";
+    }
+    else
+    {
+      out += "haus.c";
+    }
+  }
+  else
+  {
+    out = PATH+"fixed.c";
+  }
+
+  // Informationen fuer die Statemachine setzen
+  file = out;
+  final = fin;
+  raum = env;
+  nr = num;
+
+  if (num >= 0)
+  {
+    StateTransition(SIG_ROOM);
+  }
+  else
+  {
+    StateTransition(SIG_HOUSE);
+  }
+  return out;
+}
+
+// Sonderzeichen \, " und TAB korrekt umwandeln
+private string special(string s)
+{
+  s = implode(explode(s, "\\"), "\\\\");
+  s = implode(explode(s, "\t"), "\\t");
+  s = implode(explode(s, "\""), "\\\"");
+
+  return s;
+}
+
+/*** Funktionen zum Generieren einer Datei aus den Properties des Hauses ***/
+
+#define EXIT_TMPL "\"/players/%s/seherhaus/%sraum%d\""
+
+private void
+removeOutputFile()
+{
+  catch(rm(file));
+}
+
+private void
+dumpHouse()
+{
+  headerHouse();
+  
+  sProp(file, "P_SHORT", raum->QueryProp(P_SHORT));
+  write_file(file, "\n");
+  sProp(file, "P_LONG", raum->QueryProp(P_LONG));
+  write_file(file, "\n");
+
+  if (final)
+  {
+    mixed n;
+
+    write_file(file, "  SetProp(P_NOGET, 1);\n");
+    write_file(file,
+               "  SetProp(P_GENDER, "+
+               ({"NEUTER","MALE","FEMALE"})[raum->QueryProp(P_GENDER)]+
+               " );\n  SetProp(P_NAME, ");
+    n = raum->QueryProp(P_NAME);
+
+    if (stringp(n))
+    {
+      write_file(file, "\""+n+"\"");
+    }
+    else
+    {
+      prettyArray(file, n, 0);
+    }
+    write_file(file, " );\n\n  AddId( ");
+    prettyArray(file,
+                raum->QueryProp(P_IDS)-({ "sehe\rhaus",
+                                          "\n"+raum->QueryOwner()+"haus" }),
+                0);
+    write_file(file, " );\n");
+  }
+  trailerHouse();
+
+  StateTransition(SIG_HOUSE_DONE);
+}
+
+private void
+dumpRoomHeader()
+{
+  headerRoom();
+  
+  sProp(file, "P_INT_SHORT", raum->QueryProp(P_INT_SHORT));
+  write_file(file, "\n");
+  sProp(file, "P_INT_LONG", raum->QueryProp(P_INT_LONG));
+  write_file(file, "\n");
+
+  if (final && raum->QueryProp(H_CHEST))
+  {
+    StateTransition(SIG_DO_CHEST);
+  }
+  else
+  {
+    StateTransition(SIG_DO_DETAIL);
+  }
+}
+
+private void
+dumpChest()
+{
+  mixed n;
+  string cfile;
+  object chest;
+
+  cfile = PATH+"rep/"+raum->QueryOwner()+"truhe.c";
+  chest = present(TRUHE, raum);
+  
+  catch(rm(cfile));
+
+  write_file(cfile,
+             "#include <properties.h>\n#include <moving.h>\n\n"
+             "inherit \"/d/wueste/durian/behaelter\";\n\n"
+             "void create()\n{\n  if(!clonep(this_object())) return;\n\n"
+             "  ::create();\n\n" );
+  sProp(cfile, "P_SHORT", chest->QueryProp(P_SHORT));
+  sProp(cfile, "P_LONG",  chest->QueryProp(P_LONG));
+
+  write_file(cfile, "  SetProp(P_NOGET, 1);\n");
+  write_file(cfile,
+             "  SetProp(P_GENDER, "+
+             ({"NEUTER","MALE","FEMALE"})[chest->QueryProp(P_GENDER)]+
+             " );\n  SetProp(P_NAME, ");
+
+  n = chest->QueryProp(P_NAME);
+  if (stringp(n))
+  {
+    write_file(cfile, "\""+n+"\"");
+  }
+  else
+  {
+    prettyArray(file, n, 0);
+  }
+  write_file(cfile, " );\n\n  AddId( ");
+  prettyArray(cfile, chest->QueryProp(P_IDS)-({ TRUHE }), 0);
+  write_file(cfile, " );\n");
+
+  if ((n=chest->QueryProp(P_ADJECTIVES)) && sizeof(n))
+  {
+    write_file(cfile, "\n  AddAdjective( ");
+    prettyArray(cfile, n, 0);
+    write_file(cfile, " );\n");
+  }
+  if ((n=chest->QueryProp(P_NAME_ADJ)) && sizeof(n))
+  {
+    write_file(cfile, "\n  SetProp(P_NAME_ADJ, ");
+    prettyArray(cfile, n, 0);
+    write_file(cfile, " );\n");
+  }
+  write_file(cfile, "}\n");
+
+  StateTransition(SIG_DO_DETAIL);
+}
+
+private void
+dumpDetails()
+{
+  mapping prop;
+  mixed det;
+  
+  prop = raum->QueryProp(P_DETAILS);
+
+  if (sizeof(prop))
+  {
+    det = VERWALTER->PCrunch(prop);
+    // next state transition happens when do_mProp is done
+    mProp(file, "AddDetail", det, sizeof(det)-1, SIG_DETAIL_DONE);
+  }
+  else
+  {
+    StateTransition(SIG_DETAIL_DONE);
+  }
+}
+
+private void
+dumpReadDetails()
+{
+  mapping prop;
+  mixed rdet;
+  
+  prop = raum->QueryProp(P_READ_DETAILS);
+
+  if (sizeof(prop))
+  {
+    rdet = VERWALTER->PCrunch(prop);
+    // next state transition happens when do_mProp is done
+    mProp(file, "AddReadDetail", rdet, sizeof(rdet)-1, SIG_RDETAIL_DONE);
+  }
+  else
+  {
+    StateTransition(SIG_RDETAIL_DONE);
+  }
+}
+
+private void
+dumpCommands()
+{
+  mapping comm;
+  mixed cmd;
+  
+  comm = raum->QueryProp(H_COMMANDS);
+
+  if (sizeof(comm))
+  {
+    cmd = VERWALTER->PCrunch(comm);
+    // next state transition happens when do_cProp is done
+    cProp(file, "AddUserCmd", cmd, sizeof(cmd)-1, SIG_COMMAND_DONE);
+  }
+  else
+  {
+    StateTransition(SIG_COMMAND_DONE);
+  }
+}
+
+private void
+doFinalize()
+{
+  if (final)
+  {
+    StateTransition(SIG_DO_EXITS);
+  }
+  else
+  {
+    StateTransition(SIG_ROOM_DONE);
+  }
+}
+
+private void
+dumpExits()
+{
+  string *k, o;
+  mapping prop;
+  int i, num;
+
+  prop = raum->QueryProp(P_EXITS);
+  k = m_indices(prop);
+  if (member(k, "raus") >= 0)
+  {
+    k -= ({ "raus" });
+    write_file(file, sprintf("  AddExit( \"raus\", \"%s\");\n", prop["raus"]));
+  }
+  
+  for (i=sizeof(k)-1; i>=0; i--)
+  {
+    if (sscanf(prop[k[i]], PATH+"%sraum%d", o, num) != 2)
+    {
+      printf("Komischer Exit (%O)\n%s -> %s\n", raum, k[i], prop[k[i]]);
+    }
+    else
+    {
+      if (o == raum->QueryOwner())
+      {
+        o = sprintf(EXIT_TMPL, o, o, num);
+        write_file(file, sprintf("  AddExit( \"%s\", %s);\n", k[i], o));
+      }
+      else
+      {
+        write_file(file,
+                   sprintf("  AddExit( \"%s\", \"%s\");\n", k[i], prop[k[i]]));
+        printf("Exit von %O nach %s!\n", raum, prop[k[i]]);
+      }
+    }
+  }
+  StateTransition(SIG_ROOM_DONE);
+}
+
+private void
+dumpRoomTrailer()
+{
+  trailerRoom();
+  doReturn();
+}
+
+private void
+doReturn()
+{
+  if (hausmeister != 0)
+  {
+    apply(#'call_other, hausmeister, "GenerationDone", hmargs);
+  }
+  destruct(this_object());
+}
+
+private void
+sProp(string f, string pName, string pStr)
+{
+  string *str;
+
+  write_file(f, "  SetProp( "+pName+",");
+
+  if (!pStr)
+  {
+    write_file(f, "0 );\n");
+    return;
+  }
+
+  pStr = special(pStr);
+
+  if (sizeof(str=old_explode(pStr,"\n")) > 1)
+  {
+    prettyString(f, str, 4);
+  }
+  else
+  {
+    if (sizeof(str)==0)
+    {
+      str = ({""});
+    }
+    write_file(f, "\n     \""+str[0]);
+
+    if (pStr[<1] == '\n')
+    {
+      write_file(f, "\\n\"");
+    }
+    else
+    {
+      write_file(f, "\"");
+    }
+  }
+  write_file(f, " );\n");
+}
+
+static void
+mProp(string file, string pName, mixed cmd, int pos, int signal)
+{
+  int i;
+  string *eq, t1;
+  
+  for (i = pos; (i >= 0) && (get_eval_cost() > 10000); --i)
+  {
+    write_file(file, "  "+pName+"(\n    ");
+    eq = cmd[i][0];
+    t1 = cmd[i][1];
+
+    prettyArray(file, eq, 0);
+    write_file(file, ", ");
+
+    prettyString(file, old_explode(special(t1), "\n"), 6);
+    write_file(file, " );\n");
+  }
+
+  // Falls wir die eval_cost ausgereizt haben, aber noch nicht
+  // alle Einträge bearbeitet wurden, wird jetzt die naechste
+  // Runde gestartet
+  if (i >= 0)
+  {
+    call_out("mProp", 1, file, pName, cmd, i, signal);
+  }
+  else
+  {
+    // Ansonsten wechseln wir jetzt in den naechsten Zustand
+    write_file(file, "\n");
+    StateTransition(signal);
+  }
+}
+
+static void
+cProp(string file, string pName, mixed cmd, int pos, int signal)
+{
+  string t1, t2;
+  mixed  eq;
+  int i;
+
+  for (i = pos; (i >= 0) && (get_eval_cost() > 10000); --i)
+  {
+    write_file(file, "  "+pName+"(\n    ");
+    eq = cmd[i][0];
+    t1 = cmd[i][1];
+    t2 = cmd[i][2];
+
+    prettyArray(file, eq, 1);
+    write_file(file, ", 0, ");
+
+    prettyString(file, old_explode(special(t1), "\n"), 4);
+    write_file(file, ", ");
+    
+    if (t2)
+    {
+      prettyString(file, old_explode(special(t2), "\n"), 4);
+    }
+    else
+    {
+      write_file(file, "0");
+    }
+    
+    write_file(file, " );\n");
+  }
+
+  // Falls wir die eval_cost ausgereizt haben, aber noch nicht
+  // alle Einträge bearbeitet wurden, wird jetzt die naechste
+  // Runde gestartet
+  if (i >= 0)
+  {
+    call_out("cProp", 1, file, pName, cmd, i, signal);
+  }
+  else
+  {
+    // Ansonsten wechseln wir jetzt in den naechsten Zustand
+    write_file(file, "\n");
+    StateTransition(signal);
+  }
+}
+
+private void
+prettyString(string f, string *str, int indent)
+{
+  string ind;
+  int i;
+
+  ind = extract("\n          ",0,indent);
+
+  if (!sizeof(str))
+  {
+    write_file(f, ind+" \"\\n\"");
+    return;
+  }
+
+  write_file(f, ind+" \""+str[0]+"\\n\"");
+
+  for (i=1; i<sizeof(str); i++)
+  {
+    write_file(f, ind+"+\""+str[i]+"\\n\"");
+  }
+}
+
+private void
+prettyArray(string f, string *arr, int sep)
+{
+  int i,j;
+  string res, t1, t2;
+
+  write_file(f, "({");
+
+  if (sizeof(arr))
+  {
+    t1 = ("\""+arr[0]+"\"");
+    res = " "+t1;
+    t2 = "";
+
+    for (i=1, j=sizeof(arr); i<j; i++)
+    {
+      t2 = "\""+arr[i]+"\"";
+      if (!sep)
+      {
+	if ((sizeof(t1) + sizeof(t2)) > 69)
+        {
+	  res += (",\n       "+t2);
+	  t1 = t2;
+	  t2 = "";
+	}
+	else {
+	  t1 += (", "+t2);
+	  res += (", "+t2);
+	}
+      }
+      else {
+	res += (",\n       "+t2);
+      }
+    }
+  }
+  write_file(f, res + " })" );
+}
+
+private void
+headerRoom()
+{
+  if (final)
+  {
+    write_file(file,
+               "#include <properties.h>\n\n"
+               "inherit \"std/room\";\n"
+               "inherit \""+PATH+"modules/usercmd\";\n\n"
+               "create()\n"
+               "{\n"
+               "  room::create();\n"
+               "  usercmd::create();\n\n"
+               "  SetProp(P_LIGHT, 1);\n"
+               "  SetProp(P_INDOORS, 1);\n\n");
+  }
+  else
+  {
+    write_file(file,
+               "#include \"haus.h\"\n"
+               "#include <properties.h>\n\n"
+               "inherit RAUM;\n"
+               "inherit \"/std/thing/moving\";\n\n"
+               "create()\n"
+               "{\n"
+               "  if (!clonep(this_object())) return;\n"
+               "  ::create();\n\n"
+               "  SetOwner(\""+raum->QueryOwner()+"\", "+nr+");\n"
+               "  Load();\n\n"
+               "  SetProp(P_DETAILS, ([]));\n"
+               "  SetProp(P_READ_DETAILS, ([]));\n\n");
+  }
+}
+
+private void
+trailerRoom()
+{
+  if (final)
+  {
+    write_file(file, "}\n");
+  }
+  else
+  {
+    write_file(file,
+               "  Save(1);\n\n"
+               "  { object raum;\n"
+               "    if (raum = find_object(RAUMNAME("
+               "\""+raum->QueryOwner()+"\", "+nr+")))\n"
+               "      raum->Load();\n"
+               "  }\n\n"
+               "  call_out(\"remove\",0);\n"
+               "}\n");
+  }
+}
+
+private void
+headerHouse()
+{
+  if (final)
+  {
+    write_file(file,
+               "#include <properties.h>\n"
+               "#include <moving.h>\n"
+               "#include \""+PATH+"haus.h\"\n\n"
+               "inherit \"std/thing\";\n"
+               "inherit HAUSTUER;\n\n"
+               "create()\n"
+               "{\n"
+               "  thing::create();\n"
+               "  haustuer::create();\n\n");
+  }
+  else
+  {
+    write_file(file,
+               "#include \"haus.h\"\n"
+               "#include <properties.h>\n\n"
+               "inherit HAUS;\n"
+               "inherit \"/std/thing/moving\";\n\n"
+               "create()\n"
+               "{\n"
+               "  if (!clonep(this_object())) return;\n"
+               "  ::create();\n\n"
+               "  SetOwner(\""+raum->QueryOwner()+"\"\n"
+               "  Load();\n\n");
+  }
+}
+
+private void
+trailerHouse()
+{
+  if (final)
+  {
+    write_file(file, read_file(PATH+"tools/haus.apx"));
+    write_file(file, "  this_player()->move(");
+    write_file(file, sprintf(EXIT_TMPL,
+                             raum->QueryOwner(), raum->QueryOwner(), 0));
+    write_file(file,
+               ",\n\t\t\tM_GO, 0, \"betritt \"+name(WEN,1), \"kommt herein\");\n"
+               "  return 1;\n}\n");
+    if (!raum->QueryProp(P_SHORT))
+    {
+      write_file(file,
+                 "\nstring short()\n"
+                 "{\n"
+                 "  string ret;\n\n"
+                 "  ret = ::short();\n"
+                 "  if (previous_object() != environment() && !ret)\n"
+                 "    ret =\"\";\n\n"
+                 "  return ret;\n"
+                 "}\n");
+    }
+  }
+  else
+  {
+    write_file(file,
+               "  Save(1);\n\n"
+               "  { object raum;\n"
+               "    if (raum = find_object(HAUSNAME("
+               "\""+raum->QueryOwner()+"\")))\n"
+               "      raum->Load();\n"
+               "  }\n\n"
+               "  call_out(\"remove\",0);\n}\n");
+  }
+}
diff --git a/d/seher/haeuser/tools/haus.apx b/d/seher/haeuser/tools/haus.apx
new file mode 100644
index 0000000..74e2877
--- /dev/null
+++ b/d/seher/haeuser/tools/haus.apx
@@ -0,0 +1,25 @@
+
+  AddCmd( ({ "betritt", "betrete" }), "betritt");
+}
+
+string long()
+{
+  string door;
+
+  door = haustuer::long();
+
+  return QueryProp(P_LONG)+door;
+}
+
+static int betritt(string str)
+{
+  notify_fail("Was moechtest Du betreten?\n");
+
+  if (!str || !id(str))
+    return 0;
+
+  if (Query(H_DOORSTAT) & D_CLOSED) {
+    printf("%s von %s ist zu.\n", capitalize(name(WER,1)), capitalize(owner));
+    return 1;
+  }
+
diff --git a/d/seher/haeuser/tools/hausmeister.c b/d/seher/haeuser/tools/hausmeister.c
new file mode 100644
index 0000000..b39de0e
--- /dev/null
+++ b/d/seher/haeuser/tools/hausmeister.c
@@ -0,0 +1,247 @@
+#include "../haus.h"
+
+inherit "std/thing";
+inherit "/p/service/wargon/sm/statemachine";
+
+#include "/p/service/wargon/sm/statemachine.h"
+#include <properties.h>
+#include <moving.h>
+
+#define STATE_IDLE       0
+#define STATE_GENERATING 1
+
+#define SIG_INIT      0
+#define SIG_GENERATE  1
+#define SIG_DONE      2
+
+private string dump(int haus, int final);
+private void enterGenerating(string ow, object env, int nr, int fin, int all);
+private void doGenerating(string ow, object env, int nr, int fin, int all);
+
+create()
+{
+  if (!clonep(this_object())) return;
+  thing::create();
+  statemachine::create();
+  
+  SetProp( P_NAME, "Hausmeister");
+  SetProp( P_SHORT, "Ein Hausmeister" );
+  SetProp( P_LONG,
+     "Dies ist der Hausmeister aller Seherhaeuser. Er hilft Wurzel bei der\n"
+    +"Verwaltung der Haeuser.\n"
+    +"Der Hausmeister hat einen blauen Kittel an und eine Abrissbirne gezueckt.\n" );
+  SetProp( P_GENDER,MALE);
+  SetProp( P_NOGET, 1);
+
+  AddId( "hausmeister" );
+
+  AddState(STATE_IDLE, 0, 0);
+  AddState(STATE_GENERATING, #'doGenerating, 0);
+
+  AddTransition(SM_INVALID, SIG_INIT, STATE_IDLE);
+  AddTransition(STATE_IDLE, SIG_GENERATE, STATE_GENERATING, #'enterGenerating);
+  AddTransition(STATE_GENERATING, SIG_GENERATE, STATE_GENERATING, #'enterGenerating);
+  AddTransition(STATE_GENERATING, SIG_DONE, STATE_IDLE);
+
+  StateTransition(SIG_INIT);
+  
+  AddCmd( "generiere", "generieren" );
+  AddCmd( ({ "reiss", "reisse" }), "abreissen" );
+  AddCmd( "verlege", "verlegen" );
+}
+
+static int
+bewege(mixed dest, int flags, string msg1, string msg2)
+{
+  int ret;
+
+  tell_room(environment(), capitalize(name(WER))+" "+msg1+".\n");
+  if ((ret = move(dest, flags)) == 1)
+    tell_room(environment(), capitalize(name(WER))+" "+msg2+".\n");
+
+  return ret;
+}
+
+static int generieren(string str)
+{
+  object env;
+  int nr, fin, all;
+  string ow, *parm;
+
+  env = environment(this_player());
+
+  notify_fail( "Syntax: generiere <name> [<nr>] [soft | ganz]\n" );
+
+  if (!str || str == "")
+    return 0;
+
+  parm = old_explode(str, " ");
+  fin = 1;
+
+  switch (sizeof(parm)) {
+    case 3:
+      if (parm[2] == "soft")
+	fin = 0;
+    case 2:
+      if (parm[1] == "soft")
+	fin = 0;
+      else if (parm[1] == "ganz") {
+	ow = parm[0];
+	nr = (VERWALTER)->HausProp(ow, HP_ROOMS);
+	str = ow+"raum"+nr;
+	all = 1;
+	break;
+      }
+      else {
+	nr = to_int(parm[1]);
+	str = parm[0]+"raum"+parm[1];
+	ow = parm[0];
+	break;
+      }
+    case 1:
+      ow = parm[0];
+      nr = -1;
+      str = ow+"haus";
+      break;
+    default:
+      return 0;
+  }
+  if (file_size(HAUSSAVEPATH+ow+".o")<0) {
+    write( "Es gibt kein '"+str+"'!\n");
+    return 1;
+  }
+  StateTransition(SIG_GENERATE, ({ow, env, nr, fin, all }) );
+  return 1;
+}
+
+static void enterGenerating(string ow, object env, int nr, int fin, int all)
+{
+  string str;
+  
+  if (nr >= 0)
+    str = ow+"raum"+nr;
+  else
+    str = ow+"haus";
+
+  bewege(PATH+str, M_NOCHECK, "geht zur Arbeit", "kommt an");
+  tell_room(environment(),
+            Name(WER)+" zueckt einen Block, sieht sich alles genau an und macht\n"
+            +"sich hin und wieder Notizen. Dann nickt er zufrieden und steckt seinen\n"
+            "Block wieder weg.\n" );
+}
+
+static void doGenerating(string ow, object env, int nr, int fin, int all)
+{
+  object gen;
+  string str, out;
+
+  gen = clone_object(PATH+"tools/generator");
+  out = gen->dump(environment(), fin, ({ ow, env, nr, fin, all }));
+  
+  if (nr >= 0)
+    str = ow+"raum"+nr;
+  else
+    str = ow+"haus";
+
+  bewege(env, M_NOCHECK, "geht zu seinem Auftraggeber zurueck", "kommt an");
+  write( "Der Hausmeister sagt: Ich habe '"+str+"' in die Datei\n"
+	+"Der Hausmeister sagt: '"+out+"' geschrieben, Chef!\n");
+}
+
+void
+GenerationDone(string ow, object env, int nr, int fin, int all)
+{
+  if (all)
+  {
+    if (nr > 0)
+    {
+      call_out("StateTransition", 1,
+               SIG_GENERATE, ({ ow, env, nr-1, fin, 1 }) );
+    }
+    else
+    {
+      call_out("StateTransition", 1,
+               SIG_GENERATE, ({ ow, env, -1, fin, 0 }) );
+    }
+  }
+  else
+  {
+    StateTransition(SIG_DONE, ({ ow, env, -1, fin, 0 }) );
+  }
+}
+
+static int abreissen(string str)
+{
+  object haus, env;
+  string ich, msg;
+
+  if (!str || str == "" || sscanf(str, "%s ab",str) != 1) {
+    notify_fail( "Syntax: reisse <name> ab\n" );
+    return 0;
+  }
+  str = lower_case(str);
+  if (!(haus = VERWALTER->FindeHaus(str))) {
+    write( capitalize(str)+" hat kein Haus!\n" );
+    return 1;
+  }
+  ich = capitalize(name(WER));
+  env = environment();
+  bewege(environment(haus), M_NOCHECK, "geht zur Arbeit", "kommt an");
+  msg = haus->Name(WER);
+
+  if (VERWALTER->LoescheHaus(str) == -1) {
+    tell_room(environment(), ich+" versucht vergeblich, ein Haus abzureissen.\n" );
+    msg = "Der Abrissversuch ist fehlgeschlagen, Chef!\n";
+  }
+  else {
+    tell_room(environment(), ich+" betaetigt eine Sirene.\n"+msg+" wird von "+name(WEM)+" eingeebnet.\n");
+    msg = msg+" ist abgerissen, Chef!\n";
+    if (haus) haus->remove();
+  }
+  bewege(env, M_NOCHECK, "geht zu seinem Auftraggeber zurueck", "kommt an");
+  write( "Der Hausmeister sagt: "+msg);
+  return 1;
+}
+
+static int verlegen(string str)
+{
+  string name, von, nach;
+  object haus;
+  int ret;
+
+  if (!str || sscanf(str, "%s nach %s", name, nach) != 2) {
+    notify_fail( "Syntax: verlege <name> nach <ziel>\n" );
+    return 0;
+  }
+  if (nach == "hier")
+    nach = object_name(environment(this_player()));
+
+  name = lower_case(name);
+
+  if (!(haus = VERWALTER->FindeHaus(name))) {
+    write( "Der Hausmeister sagt: "+capitalize(name)+" hat kein Haus!\n" );
+    return 1;
+  }
+  von = object_name(environment(haus));
+  ret = VERWALTER->VerlegeHaus(name, von, nach);
+  write( "Der Hausmeister sagt: " );
+  switch(ret) {
+    case -111:
+      write( "Das hast Du nicht zu bestimmen!\n" );
+      break;
+    case -3:
+      write( "Der Zielraum laesst sich nicht laden! Verlegen abgebrochen!\n" );
+      break;
+    case -4:
+      write( "Im Zielraum kann nicht gebaut werden!\n" );
+      break;
+    case -5:
+      write( "Im Haus von "+capitalize(name)+" gibt es noch Ausgaenge in andere Haeuser!\n");
+      break;
+    case 1:
+      write( "OK, Chef, ich habe das Haus verlegt!\n" );
+      break;
+  }
+  return 1;
+}
+
diff --git a/d/seher/haeuser/tools/hausmeister2.c b/d/seher/haeuser/tools/hausmeister2.c
new file mode 100644
index 0000000..41c5450
--- /dev/null
+++ b/d/seher/haeuser/tools/hausmeister2.c
@@ -0,0 +1,534 @@
+#include "../haus.h"
+#include <properties.h>
+#include <moving.h>
+
+inherit "std/thing";
+
+static void do_gen(string ow, object env, int nr, int fin, int all);
+private string special(string str);
+private string dump(int haus, int final);
+private void header(string f, int final, object haus, int nr);
+private void trailer(string f, int final, object haus, int nr);
+private void sProp(string f, string pName, string pStr);
+private void mProp(string f, string pName, mapping pMap);
+private void cProp(string f, string pName, mapping cMap);
+private void prettyString(string f, string *str, int indent);
+private void prettyArray(string f, string *arr, int sep);
+
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+
+  SetProp( P_NAME, "Hausmeister");
+  SetProp( P_SHORT, "Ein Hausmeister" );
+  SetProp( P_LONG,
+     "Dies ist der Hausmeister aller Seherhaeuser. Er hilft Wurzel bei der\n"
+    +"Verwaltung der Haeuser.\n"
+    +"Der Hausmeister hat einen blauen Kittel an und eine Abrissbirne gezueckt.\n" );
+  SetProp( P_GENDER,MALE);
+  SetProp( P_NOGET, 1);
+
+  AddId( "hausmeister" );
+
+  AddCmd( "generiere", "generieren" );
+  AddCmd( ({ "reiss", "reisse" }), "abreissen" );
+  AddCmd( "verlege", "verlegen" );
+}
+
+static int
+bewege(mixed dest, int flags, string msg1, string msg2)
+{
+  int ret;
+
+  tell_room(environment(), capitalize(name(WER))+" "+msg1+".\n");
+  if ((ret = move(dest, flags)) == 1)
+    tell_room(environment(), capitalize(name(WER))+" "+msg2+".\n");
+
+  return ret;
+}
+
+static int generieren(string str)
+{
+  object env;
+  int nr, fin, all;
+  string ow, *parm;
+
+  env = environment(this_player());
+
+  notify_fail( "Syntax: generiere <name> [<nr>] [soft | ganz]\n" );
+
+  if (!str || str == "")
+    return 0;
+
+  parm = old_explode(str, " ");
+  fin = 1;
+
+  switch (sizeof(parm)) {
+    case 3:
+      if (parm[2] == "soft")
+	fin = 0;
+    case 2:
+      if (parm[1] == "soft")
+	fin = 0;
+      else if (parm[1] == "ganz") {
+	ow = parm[0];
+	nr = (VERWALTER)->HausProp(ow, HP_ROOMS);
+	str = ow+"raum"+nr;
+	all = 1;
+	break;
+      }
+      else {
+	nr = to_int(parm[1]);
+	str = parm[0]+"raum"+parm[1];
+	ow = parm[0];
+	break;
+      }
+    case 1:
+      ow = parm[0];
+      nr = -1;
+      str = ow+"haus";
+      break;
+    default:
+      return 0;
+  }
+  if (file_size(HAUSSAVEPATH+ow+".o")<0) {
+    if (nr >= 0 && file_size(HAUSSAVEPATH+ow+nr+".o")<0) {
+      write( "Es gibt kein '"+str+"'!\n");
+      return 1;
+    }
+  }
+  do_gen(ow, env, nr, fin, all);
+  return 1;
+}
+
+static void do_gen(string ow, object env, int nr, int fin, int all)
+{
+  string str, out;
+
+  if (nr >= 0)
+    str = ow+"raum"+nr;
+  else
+    str = ow+"haus";
+
+  bewege(PATH+str, M_NOCHECK, "geht zur Arbeit", "kommt an");
+  tell_room(environment(), Name(WER)+" zueckt einen Block, sieht sich alles genau an und macht\n"
+    +"sich hin und wieder Notizen. Dann nickt er zufrieden und steckt seinen\nBlock wieder weg.\n" );
+  out = dump(nr, fin);
+  bewege(env, M_NOCHECK, "geht zu seinem Auftraggeber zurueck", "kommt an");
+  write( "Der Hausmeister sagt: Ich habe '"+str+"' in die Datei\n"
+	+"Der Hausmeister sagt: '"+out+"' geschrieben, Chef!\n");
+
+  if (all) {
+    if (nr > 0)
+      call_out("do_gen", 1, ow, env, nr-1, fin, all);
+    else
+      call_out("do_gen", 1, ow, env, -1, fin);
+  }
+}
+
+static int abreissen(string str)
+{
+  object haus, env;
+  string ich, msg;
+
+  if (!str || str == "" || sscanf(str, "%s ab",str) != 1) {
+    notify_fail( "Syntax: reisse <name> ab\n" );
+    return 0;
+  }
+  str = lower_case(str);
+  if (!(haus = VERWALTER->FindeHaus(str))) {
+    write( capitalize(str)+" hat kein Haus!\n" );
+    return 1;
+  }
+  ich = capitalize(name(WER));
+  env = environment();
+  bewege(environment(haus), M_NOCHECK, "geht zur Arbeit", "kommt an");
+  msg = haus->Name(WER);
+
+  if (VERWALTER->LoescheHaus(str) == -1) {
+    tell_room(environment(), ich+" versucht vergeblich, ein Haus abzureissen.\n" );
+    msg = "Der Abrissversuch ist fehlgeschlagen, Chef!\n";
+  }
+  else {
+    tell_room(environment(), ich+" betaetigt eine Sirene.\n"+msg+" wird von "+name(WEM)+" eingeebnet.\n");
+    msg = msg+" ist abgerissen, Chef!\n";
+    if (haus) haus->remove();
+  }
+  bewege(env, M_NOCHECK, "geht zu seinem Auftraggeber zurueck", "kommt an");
+  write( "Der Hausmeister sagt: "+msg);
+  return 1;
+}
+
+static int verlegen(string str)
+{
+  string name, von, nach;
+  object haus;
+  int ret;
+
+  if (!str || sscanf(str, "%s nach %s", name, nach) != 2) {
+    notify_fail( "Syntax: verlege <name> nach <ziel>\n" );
+    return 0;
+  }
+  if (nach == "hier")
+    nach = object_name(environment(this_player()));
+
+  name = lower_case(name);
+
+  if (!(haus = VERWALTER->FindeHaus(name))) {
+    write( "Der Hausmeister sagt: "+capitalize(name)+" hat kein Haus!\n" );
+    return 1;
+  }
+  von = object_name(environment(haus));
+  ret = VERWALTER->VerlegeHaus(name, von, nach);
+  write( "Der Hausmeister sagt: " );
+  switch(ret) {
+    case -111:
+      write( "Das hast Du nicht zu bestimmen!\n" );
+      break;
+    case -3:
+      write( "Der Zielraum laesst sich nicht laden! Verlegen abgebrochen!\n" );
+      break;
+    case -4:
+      write( "Im Zielraum kann nicht gebaut werden!\n" );
+      break;
+    case -5:
+      write( "Im Haus von "+capitalize(name)+" gibt es noch Ausgaenge in andere Haeuser!\n");
+      break;
+    case 1:
+      write( "OK, Chef, ich habe das Haus verlegt!\n" );
+      break;
+  }
+  return 1;
+}
+
+/*** Funktionen zum Generieren einer Datei aus den Properties des Hauses ***/
+
+#define EXIT_TMPL "\"/players/%s/seherhaus/%sraum%d\""
+
+private void dump_chest(string out, object chest)
+{
+  mixed n;
+
+  catch(rm(out));
+
+  write_file(out, "#include <properties.h>\n#include <moving.h>\n\n"
+		  "inherit \"/d/wueste/durian/behaelter\";\n\n"
+		  "void create()\n{\n  if(!clonep(this_object())) return;\n\n"
+		  "  ::create();\n\n" );
+  sProp(out, "P_SHORT", chest->QueryProp(P_SHORT));
+  sProp(out, "P_LONG",  chest->QueryProp(P_LONG));
+
+  write_file(out, "  SetProp(P_NOGET, 1);\n");
+  write_file(out, "  SetProp(P_GENDER, "+
+		    ({"NEUTER","MALE","FEMALE"})[chest->QueryProp(P_GENDER)]+
+		    " );\n  SetProp(P_NAME, ");
+
+  n = chest->QueryProp(P_NAME);
+  if (stringp(n))
+    write_file(out, "\""+n+"\"");
+  else
+    prettyArray(out, n, 0);
+  write_file(out, " );\n\n  AddId( ");
+  prettyArray(out, chest->QueryProp(P_IDS)-({ TRUHE }), 0);
+  write_file(out, " );\n");
+
+  if ((n=chest->QueryProp(P_ADJECTIVES)) && sizeof(n)) {
+    write_file(out, "\n  AddAdjective( ");
+    prettyArray(out, n, 0);
+    write_file(out, " );\n");
+  }
+  if ((n=chest->QueryProp(P_NAME_ADJ)) && sizeof(n)) {
+    write_file(out, "\n  SetProp(P_NAME_ADJ, ");
+    prettyArray(out, n, 0);
+    write_file(out, " );\n");
+  }
+  write_file(out, "}\n");
+}
+
+private string dump(int nr, int final)
+{
+  string out;
+  object env;
+  mapping prop;
+
+  env = environment(this_object());
+
+  if (final && env->QueryProp(H_CHEST))
+    dump_chest(PATH+"rep/"+env->QueryOwner()+"truhe.c", present(TRUHE, env));
+
+  if (final) {
+    out = PATH+"rep/"+env->QueryOwner();
+    if (nr < 0)
+      out += "haus.c";
+    else
+      out += "raum"+nr+".c";
+  }
+  else
+    out = PATH+"fixed.c";
+
+  catch(rm(out));
+
+  header(out, final, env, nr);
+  if (nr<0) {
+    sProp(out, "P_SHORT", env->QueryProp(P_SHORT));
+    write_file(out, "\n");
+    sProp(out, "P_LONG", env->QueryProp(P_LONG));
+    write_file(out, "\n");
+
+    if (final) {
+      mixed n;
+
+      write_file(out, "  SetProp(P_NOGET, 1);\n");
+      write_file(out, "  SetProp(P_GENDER, "+
+		      ({"NEUTER","MALE","FEMALE"})[env->QueryProp(P_GENDER)]+
+		      " );\n  SetProp(P_NAME, ");
+      n = env->QueryProp(P_NAME);
+      if (stringp(n))
+	write_file(out, "\""+n+"\"");
+      else
+	prettyArray(out, n, 0);
+      write_file(out, " );\n\n  AddId( ");
+      prettyArray(out, env->QueryProp(P_IDS)-({ "sehe\rhaus", "\n"+env->QueryOwner()+"haus" }), 0);
+      write_file(out, " );\n");
+    }
+  }
+  else {
+    sProp(out, "P_INT_SHORT", env->QueryProp(P_INT_SHORT));
+    write_file(out, "\n");
+    sProp(out, "P_INT_LONG", env->QueryProp(P_INT_LONG));
+    write_file(out, "\n");
+    if (sizeof(prop = env->QueryProp(P_DETAILS))) {
+      mProp(out, "AddDetail", prop);
+      write_file(out, "\n");
+    }
+    if (sizeof(prop = env->QueryProp(P_READ_DETAILS))) {
+      mProp(out, "AddReadDetail", prop);
+      write_file(out, "\n");
+    }
+    if (sizeof(prop = env->QueryProp(H_COMMANDS)))
+      cProp(out, "AddUserCmd", prop);
+
+    write_file(out, "\n");
+    if (final) {
+      string *k, o;
+      int i, num;
+
+      prop = env->QueryProp(P_EXITS);
+      k = m_indices(prop);
+      if (member(k, "raus") >= 0) {
+	k -= ({ "raus" });
+	write_file(out, sprintf("  AddExit( \"raus\", \"%s\");\n", prop["raus"]));
+      }
+      for (i=sizeof(k)-1; i>=0; i--) {
+	if (sscanf(prop[k[i]], PATH+"%sraum%d", o, num) != 2)
+	  printf("Komischer Exit (%O)\n%s -> %s\n", env, k[i], prop[k[i]]);
+	else {
+	  if (o == env->QueryOwner()) {
+	    o = sprintf(EXIT_TMPL, o, o, num);
+	    write_file(out, sprintf("  AddExit( \"%s\", %s);\n", k[i], o));
+	  }
+	  else {
+	    write_file(out, sprintf("  AddExit( \"%s\", \"%s\");\n", k[i], prop[k[i]]));
+	    printf("Exit von %O nach %s!\n", env, prop[k[i]]);
+	  }
+	}
+      }
+    }
+  }
+  trailer(out, final, env, nr);
+
+  return out;
+}
+
+private void header(string f, int final, object haus, int nr)
+{
+  if (final) {
+    write_file(f, "#include <properties.h>" );
+    if (nr >= 0) {
+      write_file(f, "\n\ninherit \"std/room\";\ninherit \""+PATH+"modules/usercmd\";");
+      write_file(f, "\n\ncreate()\n{\n  room::create();\n  usercmd::create();\n\n");
+      write_file(f, "  SetProp(P_LIGHT, 1);\n  SetProp(P_INDOORS, 1);\n\n");
+    }
+    else {
+      write_file(f, "\n#include <moving.h>\n#include \""+PATH+"haus.h\"\n\n" );
+      write_file(f, "inherit \"std/thing\";\ninherit HAUSTUER;\n\n" );
+      write_file(f, "create()\n{\n  thing::create();\n  haustuer::create();\n\n");
+    }
+  }
+  else {
+    write_file(f, "#include \"haus.h\"\n#include <properties.h>\n\ninherit ");
+    write_file(f, ((nr < 0) ? "HAUS" : "RAUM" ) );
+    write_file(f, ";\ninherit \"/std/thing/moving\";\n\n");
+    write_file(f, "create()\n{\n  if (!clonep(this_object())) return;\n  ::create();\n\n");
+    write_file(f, "  SetOwner(\""+haus->QueryOwner()+"\"");
+    if (nr >= 0)
+      write_file(f, ", "+nr);
+    write_file(f, ");\n  Load();\n\n");
+    if (nr >= 0)
+      write_file(f, "  SetProp(P_DETAILS, ([]));\n  SetProp(P_READ_DETAILS, ([]));\n\n");
+  }
+}
+
+private void trailer(string f, int final, object haus, int nr)
+{
+  if (final) {
+    if (nr >= 0)
+      write_file(f, "}\n");
+    else {
+      write_file(f, read_file(PATH+"tools/haus.apx"));
+      write_file(f, "  this_player()->move(");
+      write_file(f, sprintf(EXIT_TMPL, haus->QueryOwner(), haus->QueryOwner(), 0));
+      write_file(f, ",\n\t\t\tM_GO, 0, \"betritt \"+name(WEN,1), \"kommt herein\");\n"
+		    "  return 1;\n}\n");
+      if (!haus->QueryProp(P_SHORT))
+	write_file(f, "\nstring short()\n{\n  string ret;\n\n"
+		      "  ret = ::short();\n"
+		      "  if (previous_object() != environment() && !ret)\n"
+		      "    ret =\"\";\n\n  return ret;\n}\n");
+    }
+  }
+  else {
+    write_file(f, "  Save(1);\n\n");
+    write_file(f, "  { object raum;\n    if (raum = find_object(");
+    if (nr >= 0)
+      write_file(f, "RAUMNAME(\""+haus->QueryOwner()+"\", "+nr+")))\n");
+    else
+      write_file(f, "HAUSNAME(\""+haus->QueryOwner()+"\")))\n");
+    write_file(f, "      raum->Load();\n  }\n\n  call_out(\"remove\",0);\n}\n");
+  }
+}
+
+private string special(string s)
+{
+  s = implode(explode(s, "\\"), "\\\\");
+  s = implode(explode(s, "\t"), "\\t");
+  s = implode(explode(s, "\""), "\\\"");
+
+  return s;
+}
+
+private void sProp(string f, string pName, string pStr)
+{
+  string *str;
+  write_file(f, "  SetProp( "+pName+",");
+  if (!pStr) {
+    write_file(f, "0 );\n");
+    return;
+  }
+  pStr = special(pStr);
+  if (sizeof(str=old_explode(pStr,"\n")) > 1)
+    prettyString(f, str, 4);
+  else {
+    if (sizeof(str)==0)
+      str = ({""});
+    write_file(f, "\n     \""+str[0]);
+    if (pStr[<1] == '\n')
+      write_file(f, "\\n\"");
+    else
+      write_file(f, "\"");
+  }
+  write_file(f, " );\n");
+}
+
+private void mProp(string f, string pName, mapping pMap)
+{
+  string *eq, t1;
+  mixed cmd;
+  int i,j;
+
+  if (sizeof(pMap) == 0)
+    return;
+
+  cmd = VERWALTER->PCrunch(pMap);
+
+  for (i=sizeof(cmd)-1; i>=0; i--) {
+    write_file(f, "  "+pName+"(\n    ");
+    eq = cmd[i][0];
+    t1 = cmd[i][1];
+
+    prettyArray(f, eq, 0);
+    write_file(f, ", ");
+
+    prettyString(f, old_explode(special(t1), "\n"), 6);
+    write_file(f, " );\n");
+  }
+}
+
+private void cProp(string f, string pName, mapping cMap)
+{
+  string t1, t2;
+  mixed cmd, eq;
+  int i,j;
+
+  cmd = VERWALTER->PCrunch(cMap);
+
+  for( i=sizeof(cmd)-1; i>=0; i--) {
+    write_file(f, "  "+pName+"(\n    ");
+    eq = cmd[i][0];
+    t1 = cmd[i][1];
+    t2 = cmd[i][2];
+
+    prettyArray(f, eq, 1);
+    write_file(f, ", 0, ");
+
+    prettyString(f, old_explode(special(t1), "\n"), 4);
+    write_file(f, ", ");
+    if (t2)
+      prettyString(f, old_explode(special(t2), "\n"), 4);
+    else
+      write_file(f, "0");
+    write_file(f, " );\n");
+  }
+}
+
+private void prettyString(string f, string *str, int indent)
+{
+  string ind;
+  int i;
+
+  ind = extract("\n          ",0,indent);
+
+  if (!sizeof(str)) {
+    write_file(f, ind+" \"\\n\"");
+    return;
+  }
+  write_file(f, ind+" \""+str[0]+"\\n\"");
+  for (i=1; i<sizeof(str); i++) {
+    write_file(f, ind+"+\""+str[i]+"\\n\"");
+  }
+}
+
+private void prettyArray(string f, string *arr, int sep)
+{
+  int i,j;
+  string res, t1, t2;
+
+  write_file(f, "({");
+
+  if (sizeof(arr)) {
+    t1 = ("\""+arr[0]+"\"");
+    res = " "+t1;
+    t2 = "";
+
+    for (i=1, j=sizeof(arr); i<j; i++) {
+      t2 = "\""+arr[i]+"\"";
+      if (!sep) {
+	if ((sizeof(t1)+sizeof(t2))>69) {
+	  res += (",\n       "+t2);
+	  t1 = t2;
+	  t2 = "";
+	}
+	else {
+	  t1 += (", "+t2);
+	  res += (", "+t2);
+	}
+      }
+      else {
+	res += (",\n       "+t2);
+      }
+    }
+  }
+  write_file(f, res + " })" );
+}
+
diff --git a/d/seher/haeuser/tools/haustool.c b/d/seher/haeuser/tools/haustool.c
new file mode 100644
index 0000000..50f6e1e
--- /dev/null
+++ b/d/seher/haeuser/tools/haustool.c
@@ -0,0 +1,182 @@
+#include "../haus.h"
+#include <properties.h>
+#include <moving.h>
+#include <wizlevels.h>
+
+inherit "std/secure_thing";
+
+private int secure()
+{
+  return (this_interactive() && (IS_ARCH(this_interactive()) ||
+				 getuid(this_interactive()) == "wurzel"));
+}
+
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+
+  SetProp(P_SHORT, "Ein Adressverzeichnis" );
+  SetProp(P_LONG,
+     "Mit diesem Adressverzeichnis koennen die richtigen Leute Ordnung in den\n"
+    +"Wust der Seherhausangelegenheiten bringen.\n"
+    +"Die richtigen Leute?\n"
+    +"Ja: Man muss schon Erzmagier sein oder Wurzel heissen! ;)\n"
+    +"Du kannst es uebrigens lesen!\n" );
+  SetProp(P_NAME, "Adressverzeichnis");
+  SetProp(P_READ_MSG,
+     "Es stehen folgende Befehle zur Verfuegung:\n"
+    +"checked <name> [+/-] <nr>\n"
+    +"rep\n"
+    +"mkmail <name>\n"
+    );
+  SetProp(P_GENDER, NEUTER);
+  SetProp(P_WEIGHT, 100);
+  SetProp(P_AUTOLOADOBJ, 1);
+  SetProp(P_NEVERDROP, 1);
+  SetProp(P_NODROP, 1);
+
+  AddId( ({ "verzeichnis", "adressverzeichnis", "haustool" }) );
+
+  AddCmd("checked", "checked");
+  AddCmd("rep", "report");
+  AddCmd("mkmail", "mkmail");
+}
+
+#define CHECKED PATH+"log/CHECKED"
+static int
+checked(string str)
+{
+  mapping drin;
+  string name, *lines, *ind, pm;
+  int nr, i;
+  closure hp;
+
+  if (!secure())
+    return 0;
+
+  notify_fail("Syntax: checked <name> [+/-] <nr>\n" );
+  if (!str || str == "")
+    return 0;
+
+  if (file_size(CHECKED) > 0)
+    lines = explode(read_file(CHECKED), "\n") - ({""});
+  else
+    lines = ({});
+
+  for (drin = ([]), i=sizeof(lines)-1; i>=0; i--) {
+    if (sscanf(lines[i], "%s%t%d", name, nr) == 2)
+      drin += ([ name : nr-1 ]);
+  }
+  lines = ({ });
+  pm = "";
+  hp = symbol_function("HausProp", VERWALTER);
+
+  if (sscanf(str, "%s %d", name, nr)==2 || sscanf(str, "%s %s %d", name, pm, nr) == 3) {
+    if (!funcall(hp, name, HP_ENV)) {
+      write(capitalize(name)+" hat gar kein Haus!\n");
+      return 1;
+    }
+    if (nr < 0) {
+      write("Zum Abziehen nimm bitte 'checked <name> - <nr>'!\n");
+      return 1;
+    }
+    if (member(drin, name))
+      i = drin[name];
+    switch(pm) {
+      case "+":
+	if (i+nr > funcall(hp, name, HP_ROOMS)) {
+	  printf("%d Raeume? Wie soll das gehen?\n", i+nr);
+	  return 1;
+	}
+	drin[name] += nr;
+	break;
+      case "-":
+	if (i-nr < 0) {
+	  write("Soviele Raeume kannst Du "+capitalize(name)+" gar nicht abziehen...\n");
+	  return 1;
+	}
+	drin[name] -= nr;
+	break;
+      default:
+	if (nr > funcall(hp, name, HP_ROOMS)) {
+	  printf("%s nat keine %d Raeume!\n",capitalize(name),nr);
+	  return 1;
+	}
+	drin[name] = nr;
+    }
+    rm(CHECKED);
+    for (i=sizeof(ind=sort_array(m_indices(drin),#'<))-1; i>=0; i--)
+      write_file(CHECKED, sprintf("%s %d\n",ind[i], 1+drin[ind[i]]));
+    return 1;
+  }
+  else
+    return 0;
+}
+
+static int
+report()
+{
+  string *rep, *head, name;
+  int i, lp, nr;
+
+  if (file_size("/log/report/wurzel.rep")<=0) {
+    write("Kein wurzel.rep (Jippieh! ;)\n");
+    return 1;
+  }
+
+  rep = old_explode(read_file("/log/report/wurzel.rep"),"\n");
+  lp = sizeof(PATH)-1;
+
+  for (i=sizeof(rep)-2; i>=0; i-=2) {
+    head = old_explode(rep[i], " ");
+    if (head[4][0..lp] == PATH) {
+      name = old_explode(head[4],"/")[<1];
+      nr = to_int(name[<1..<1]);
+      name = name[0..<6];
+      head[2] = capitalize(head[2]);
+      write_file(PATH+"rep/"+name,
+		 sprintf("%s in Raum %d:\n%s\n",
+			 implode(head[0..2]," "),nr,rep[i+1]));
+    }
+    else
+      write_file(PATH+"rep/wurzel.rep",sprintf("%s\n%s\n", rep[i], rep[i+1]));
+  }
+  return 1;
+}
+
+static int
+mkmail(string str)
+{
+  string f,fm;
+
+  if (!str || str == "") {
+    notify_fail( "Syntax: mkmail <name>\n");
+    return 0;
+  }
+
+  if (!(f=(VERWALTER)->HausProp(str, HP_ENV))) {
+    printf("%s hat kein Haus!\n", str);
+    return 1;
+  }
+  fm = PATH+str+".mail";
+
+  write_file(fm, "\
+  Huhu!\n\
+\n\
+Da Du Magier bist, brauchst Du auch kein Seherhaus mehr. Damit Deine\n\
+Beschreibungen aber nicht verloren sind, findest Du die Files Deines\n\
+Hauses in /players/"+str+"/seherhaus/.\n\
+\n\
+Soll Dein Haus weiterhin an der jetzigen Stelle stehen bleiben\n\
+("+f+"),\n\
+so klaere das bitte mit dem dort zustaendigen Magier sowie den zustaen-\n\
+digen Regionsmagiern (weil es sich jetzt um den Anschluss von Files an\n\
+ein Gebiet handelt).\n\
+\n\
+!! Das Haus wird auf jeden Fall nach Ende der Haussperre abgerissen !!\n\
+\n\
+  Wargon.\n");
+
+  return 1;
+}
diff --git a/d/seher/haeuser/tools/liste.c b/d/seher/haeuser/tools/liste.c
new file mode 100755
index 0000000..12b39a2
--- /dev/null
+++ b/d/seher/haeuser/tools/liste.c
@@ -0,0 +1,25 @@
+#include "../haus.h"
+#include <wizlevels.h>
+
+mapping megamap;
+
+create() {
+  seteuid(getuid(this_object()));
+  restore_object(SAVEFILE);
+  do_dump();
+}
+
+do_dump()
+{
+  string *ow, typ;
+  int i, r, r2;
+  ow = m_indices(megamap);
+  ow = sort_array(ow,#'<);
+  i = sizeof(ow)-1;
+  for(r=0, r2=0; i>=0; i--) {
+    r = megamap[ow[i],HP_ROOMS];
+    if (IS_LEARNER(ow[i])) printf("%13s %d MAGIER(%d)\n", ow[i],r,query_wiz_level(ow[i]));
+    else if (!IS_SEER(ow[i])) printf("%13s %d KEIN SEHER MEHR\n", ow[i], r);
+  }
+  destruct(this_object());
+}
diff --git a/d/seher/haeuser/traghaus.c b/d/seher/haeuser/traghaus.c
new file mode 100644
index 0000000..f79fb36
--- /dev/null
+++ b/d/seher/haeuser/traghaus.c
@@ -0,0 +1,187 @@
+/*
+ * traghaus.c -- Wie suess! ;)
+ *
+ * (c) 1994 Boing@MorgenGrauen
+ * Be- und ueberarbeitet von Jof und Wargon.
+ *
+ * HINWEIS: Um einen Bauplatz zu kennzeichnen, muss dort die
+ *  Propertie "haus_erlaubt" auf 1 gesetzt werden!
+ *
+ * 17.11.2003 Feigling - Bugfix: Fehlenden Regionsmagier abgefangen
+ *
+ * $Date: 1994/10/24 08:25:18 $
+ * $Revision: 1.5 $
+ * $Log: traghaus.c,v $
+ * Revision 1.5  1994/10/24  08:25:18  Wargon
+ * Macht jetzt Gebrauch von VERWALTER->Unbebaubar() und dem geaenderten
+ * VERWALTER->NeuesHaus().
+ *
+ * Revision 1.4  1994/10/19  10:03:29  Wargon
+ * Hinweis eingebaut (s.o.)
+ * Beschraenkung auf ein Seherhaus pro Raum rausgenommen.
+ *
+ * Revision 1.3  1994/10/09  20:17:03  Wargon
+ * In blas() haus->Save() eingefuegt.
+ *
+ * Revision 1.2  1994/10/07  22:13:17  Wargon
+ * Etwas aufgeraeumt.
+ * Text beim Giessen geaendert.
+ * Falls in dem Raum schon ein Seherhaus steht, kann kein weiteres mehr
+ * aufgestellt werden.
+ *
+ * Revision 1.1  1994/10/07  14:40:54  Wargon
+ * Initial revision
+ *
+ */
+#include <properties.h>
+#include <moving.h>
+#include "haus.h"
+
+inherit "std/thing";
+
+static string owner;
+
+void create()
+{
+   if (!clonep(this_object())) return;
+   ::create();
+   owner = getuid(this_player());
+   SetProp(P_SHORT, "Ein tragbares Instanthaus");
+   SetProp(P_LONG, "Dies ist Dein Haus. Du kannst es abstellen und giessen, dann wird daraus ein\n\
+richtiges Seherhaus, in das Du sofort einziehen kannst. Es ist allerdings\n\
+noetig, dass der Magier, der den Raum erschaffen hat, in dem Du es abstellen\n\
+moechtest, dem Bau eines Seherhauses in diesem Raum zustimmt.\n");
+   SetProp(P_NAME, "Haus");
+   SetProp(P_NEVERDROP, 1);
+   SetProp(P_GENDER, 0);
+   AddId(({"haus", "sehe\rhaus"}));
+   SetProp(P_VALUE, 0);
+   SetProp(P_WEIGHT, 0);
+   AddCmd(({"gies", "giess", "giesse"}), "blas");
+}
+
+string SetOwner(string o)
+{
+   return owner = o;
+}
+
+string QueryOwner()
+{
+  return owner;
+}
+
+string get_mag(mixed dest)
+{
+  mapping reg;
+  string *mag;
+  string ret;
+  int i, j;
+
+  catch(reg = "/obj/mliste"->QueryProp("Magierliste"));
+
+  if (mappingp(reg) && member(reg, getuid(dest)))
+    mag = reg[getuid(dest)];
+  else
+    return capitalize(getuid(dest));
+
+
+  if (!sizeof(mag)) 
+    return capitalize(getuid(dest)); //Hinzugefuegt Feigling
+  
+  if ((i=sizeof(mag)) == 1)
+    return capitalize(mag[0]);
+  else {
+    ret = capitalize(mag[0]);
+    j = 1;
+    while (j < i-1) {
+      ret += ", "+capitalize(mag[j]);
+      j++;
+    }
+    ret += " und "+capitalize(mag[<1]);
+  }
+  return ret;
+}
+
+int move(mixed dest, int method)
+{
+  int nix;
+
+  if ((method & M_GIVE) == M_GIVE)
+  {
+    write("Dein sauer verdientes Seherhaus weggeben? NIEMALS!\n");
+    return 0;
+  }
+  if ((method & M_PUT) == M_PUT)
+  {
+    if ((nix = VERWALTER->Unbebaubar(dest)))
+    {
+      write( ({ "Hier ist das Bauen von Haeusern untersagt!\n",
+		"Du kannst das Haus nicht in geschlossenen Raeumen abstellen.\n",
+		"Dieser Platz ist nicht als Bauplatz gekennzeichnet, Du solltest mal\nmit dem verantwortlichen Magier reden. Die Zustaendigkeit fuer diesen Ort\nliegt bei "+get_mag(dest)+".\n"
+	     })[nix-1]);
+      return 0;
+    }
+    return ::move(dest, method);
+  }
+  if ((method & M_GET) == M_GET)
+  {
+    if (getuid(this_player())!=owner)
+    {
+      write("Dies ist nicht Dein Haus.\n");
+      return 0;
+    }
+  }
+  return ::move(dest, method);
+}
+
+int blas(string str)
+{
+  object haus;
+
+  notify_fail("Was moechtest Du giessen?\n");
+  if (!str || str!="haus")
+    return 0;
+  if (living(environment()))
+  {
+    write("Du solltest das Haus vielleicht erst mal abstellen.\n");
+    return 1;
+  }
+  if (getuid(this_player())!=owner)
+  {
+    write("Machst Du immer fremde Haeuser nass?\n");
+    return 1;
+  }
+  if (!environment()->QueryProp(P_HAUS_ERLAUBT))
+  {
+    write("Dieser Platz ist nicht als Bauplatz gekennzeichnet, Du solltest mal\n\
+mit dem entsprechenden Magier reden.\nFrag mal "+capitalize(getuid(environment()))+".\n");
+    return 0;
+  }
+  write( "Voller Vorfreude greifst Du nach Deiner Giesskanne und beginnst, Dein mueh-\n\
+sam erspartes Instanthaus zu begiessen. Moment... Giesskanne? Reichlich ver-\n\
+dutzt schaust Du auf die kleine gruene Kanne in Deinen Haenden. Wo mag sie\n\
+nur hergekommen sein? Doch kaum erreichen die ersten Tropfen das Instanthaus,\n\
+wird Deine Aufmerksamkeit auch schon wieder von der Kanne abgelenkt:\n\
+Das Trockenhaus beginnt zu schaeumen und sich ueber den Boden auszubreiten.\n\
+In dem Schaum entstehen und vergehen Waende, Decken und Fenster, als ob sie\n\
+sich noch nicht recht entscheiden koennten, wie sich sich zu einem Ganzen zu-\n\
+sammenfuegen sollen.\n\
+Dann erhebt sich dort, wo Du das Trockenhaus abgestellt hattest, eine grosse,\n\
+schaumige Blase! Sie wird groesser und groesser, bis sie Dich schliesslich\n\
+streift. Dabei platzt sie mit einem leisen *PLOPP* - und es bleibt ein be-\n\
+zugsfertiges Seherhaus zurueck!\n\
+Du wirfst noch einmal einen verstohlenen Blick auf die Giesskanne, musst aber\n\
+feststellen, dass sie verschwunden ist und auch verschwunden bleibt.\n" );
+
+  say(capitalize((string)(this_player()->name()))+" verwandelt mit einem Tropfen Wasser "+
+      this_player()->QueryPossPronoun(this_object(), WEN) + " Haus.\n");
+
+  VERWALTER->NeuesHaus(owner, environment(this_object()));
+  remove();
+  return 1;
+}
+
+static string _query_autoloadobj()
+{
+  return owner;
+}
diff --git a/d/seher/haeuser/truhe.c b/d/seher/haeuser/truhe.c
new file mode 100644
index 0000000..3eab8ad
--- /dev/null
+++ b/d/seher/haeuser/truhe.c
@@ -0,0 +1,365 @@
+#include "haus.h"
+#include <properties.h>
+#include <moving.h>
+#include <wizlevels.h>
+
+inherit "/d/wueste/durian/behaelter";
+inherit "/p/service/wargon/mat_ed";
+inherit "/mail/nedit";
+
+static string owner;
+
+void create()
+{
+  if (!clonep(this_object())) return;
+
+  ::create();
+
+  Set(P_NAME, SAVE, F_MODE_AS);
+  Set(P_SHORT, SAVE, F_MODE_AS);
+  Set(P_LONG, SAVE, F_MODE_AS);
+  Set(P_GENDER, SAVE, F_MODE_AS);
+  Set(P_IDS, SAVE, F_MODE_AS);
+  Set(P_INVIS, SAVE, F_MODE_AS);
+  Set(P_NAME_ADJ, SAVE, F_MODE_AS);
+  Set(P_MATERIAL, SAVE, F_MODE_AS);
+  Set(H_DOORLSTAT, SAVE, F_MODE_AS);
+
+  SetProp( P_NAME, "Truhe" );
+  SetProp( P_SHORT, "Eine Truhe" );
+  SetProp( P_LONG,
+     "Eine stabile Holztruhe mit einem Sicherheitsschloss. Nur ausgewaehlte\n"
+    +"Personen duerfen die Truhe oeffnen und Sachen in ihr lagern oder aus\n"
+    +"ihr nehmen.\n" );
+  SetProp( P_GENDER, FEMALE );
+  SetProp( P_NOGET, 1 );
+  SetProp( P_IDS, ({ "truhe","holztruhe", TRUHE}) );
+  SetProp( P_MATERIAL, ([ MAT_MISC_WOOD : 100 ]) );
+  SetProp( H_DOORLSTAT, ({ "geschlossen", "geoeffnet"}) );
+
+  AddCmd( ({ "schieb","schiebe"}), "schieben" );
+  AddCmd( ({ "beschreib", "beschreibe" }), "beschreiben" );
+  AddCmd( ({ "aender", "aendere" }), "aendern" );
+}
+
+mixed
+_set_owner(string ow)
+{
+  string *na, *a;
+  int i;
+
+  if (ow && stringp(ow))
+    restore_object(HAUSSAVEPATH+ow+"truhe");
+
+  if ((na = QueryProp(P_NAME_ADJ)) && (i=sizeof(na))) {
+    for (a = ({ }), i-=1; i>=0; i--)
+      a += ({ DeclAdj(na[i], WER)[0..<2], DeclAdj(na[i], WEM)[0..<2], DeclAdj(na[i],WEN)[0..<2] });
+
+    a = m_indices(mkmapping(a));
+    if (sizeof(a))
+      AddAdjective(a);
+  }
+
+  SetZu();
+  return owner=ow;
+}
+
+string
+short()
+{
+  int open;
+  string sh, *stat;
+
+  open = (QueryProp(P_CNT_STATUS) == CNT_STATUS_OPEN);
+  sh = QueryProp(P_SHORT);
+  stat = QueryProp(H_DOORLSTAT);
+  if (!stat)
+    stat = ({ "", ""});
+  else if (strstr(sh, "%s") == -1)
+    sh += " (%s)";
+  if(QueryProp(P_INVIS)) return "";
+  return sprintf(sh, stat[offen])+".\n";
+}
+
+oeffne(string str)
+{
+  object env;
+
+  notify_fail("WAS moechtest Du oeffnen?\n");
+
+  // bin ich gemeint?
+  if (!str || present(str, environment()) != this_object())
+    return 0;
+
+  if ((env=environment()) && env->id("sehe\rhaus")) {
+    if (!env->allowed_check(this_player())) {
+      write("Du darfst "+name(WEN)+" nicht oeffnen!\n" );
+      return 1;
+    }
+  }
+  return ::oeffne(str);
+}
+
+int MayAddWeight(int w)
+{
+  return 0;
+}
+
+static int
+schieben(string richtung)
+{
+  string *aus;
+  mapping exits;
+
+  if (!environment() || (!environment()->id("sehe\rhaus")) ||
+      !environment()->tp_owner_check())
+    return 0;
+
+  notify_fail( "Syntax: schiebe <truhe> nach <richtung>\n");
+
+  if (!richtung || richtung == "")
+    return 0;
+
+  aus = old_explode(richtung, " ");
+  richtung = aus[<1];
+  if (sizeof(aus) < 3 || !id(implode(aus[0..<3]," ")))
+    return 0;
+
+  if (member(aus = m_indices(exits = environment()->QueryProp(P_EXITS)),richtung)==-1)
+    write( "Du kannst "+name(WEN,1)+" nicht nach '"+richtung+"' schieben!\n");
+  else if (!exits[richtung]->tp_owner_check())
+    write( "Du kannst "+name(WEN,1)+" nicht aus Deinem Haus hinausschieben!\n");
+  else {
+    write( "Du schiebst "+name(WEN,1)+" in einen anderen Raum. Dann kehrst\n"
+	  +"Du wieder zurueck.\n");
+    say(capitalize(this_player()->name(WER))+" schiebt "+name(WEN)+" in einen anderen\n"
+	+"Raum, kehrt aber selber wieder.\n");
+    environment()->Set(H_CHEST,0);
+    environment()->Save();
+    move(exits[richtung], M_NOCHECK);
+    environment()->Set(H_CHEST,1);
+    environment()->Save();
+  }
+  return 1;
+}
+
+private string *brk(string str)
+{
+  int i;
+  string *t1, *t2;
+
+  t1 = regexplode(str, ", *");
+  for (t2 = ({}), i=sizeof(t1)-1; i>=0; i-=2)
+    t2 = ({t1[i]}) + t2;
+
+  return t2;
+}
+
+static void getShort();
+
+static int beschreiben(string str)
+{
+  string *x;
+
+  if (!str || getuid(this_player()) != owner)
+    return 0;
+
+  x = old_explode(str, " ");
+
+  if (x[<1] == "status" && !id(implode(x[0..<2]," ")))
+    return 0;
+  else if (!id(str) && (str != "truhe"))
+    return 0;
+
+  write( "Beschreibung "+name(WESSEN,1)+"\n"
+	+"Wenn die Vorgaben nicht geaendert werden sollen, einfach weiter\n"
+	+"mit <RETURN>.\n\n");
+  getShort();
+  return 1;
+}
+
+static void getShort()
+{
+  write("Geschlecht (m, w, s): ["
+	+({ "s","m","w" })[QueryProp(P_GENDER)]+"]\n]" );
+  input_to("gotGender");
+}
+
+static void gotGender(string str)
+{
+  int gen;
+  mixed name;
+
+  if (!str || str == "")
+    write("Geschlecht nicht geaendert.\n");
+  else if ((gen = member(({'s','m','w'}),str[0])) == -1) {
+    write( "Bitte nur 'm', 'w' oder 's' eingeben!\nGeschlecht:\n]");
+    input_to("gotGender");
+    return;
+  }
+  else {
+    SetProp(P_GENDER, gen);
+    write( "Das Geschlecht ist jetzt "+QueryGenderString()+".\n");
+  }
+
+  name = QueryProp(P_NAME);
+  write("Nun musst Du den Namen eingeben. Sollte der Name unregelmaessig dekliniert\n"
+       +"werden, musst Du den Namen in allen vier Faellen in der Reihenfolge Nomi-\n"
+       +"nativ, Genitiv, Dativ, Akkusativ und durch Kommata getrennt eingeben.\n"
+       +"Name: ["+(pointerp(name) ? implode(name, ", ") : name)+ "]\n]");
+  input_to("gotName");
+}
+
+static void gotName(string str)
+{
+  string *n;
+
+  if (!str || member(({ "", ".", "**", "~q"}), str) >= 0)
+    write("Name wurde nicht geaendert.\n");
+  else {
+    n = brk(str);
+
+    if (sizeof(n) == 1)
+      SetProp(P_NAME, n[0]);
+    else {
+      if (sizeof(n) > 4)
+	n = n[0..3];
+      else
+	while (sizeof(n) != 4)
+	  n = n + ({ n[0] });
+      SetProp(P_NAME,n);
+    }
+    printf("Der Name ist jetzt '%s'\n", implode(n, ", "));
+  }
+  write("Du kannst jetzt noch Namensadjektive angeben. Hier darfst Du nur den Wort-\n"
+       +"stamm angeben, da die Adjektive automatisch dekliniert werden (also fuer\n"
+       +"eine 'alte Truhe' muesstest Du 'alt' angeben).\n"
+       +"Du kannst auch mehrere Adjektive, durch Kommata getrennt, angeben.\n"
+       +"Um alle Namensadjektive zu loeschen, gib bitte 'nix' ein.\n"
+       +"Namensadjektive: ["+implode(QueryProp(P_NAME_ADJ),", ")+"]\n");
+  input_to("gotAdj");
+}
+
+static void gotAdj(string str)
+{
+  string *n;
+
+  if (!str || member(({ "", ".", "**", "~q"}), str) >= 0)
+    write("Adjektive nicht geaendert.\n");
+  else {
+    n = brk(str);
+
+    if (member(n, "nix") == -1) {
+      SetProp(P_NAME_ADJ, n);
+      printf("Namensadjektive lauten %s.\n", implode(n,", "));
+    }
+    else {
+      SetProp(P_NAME_ADJ, ({}) );
+      write("Namensadjektive geloescht.\n");
+    }
+  }
+  SetProp(P_SHORT, capitalize(name(WER)));
+  write( "Die Kurzbeschreibung lautet damit:\n"+short());
+  write( "Falls das Ergebnis nicht Deinen Vorstellungen entspricht, gelangst Du\n"
+	+"mit 'n' wieder in die Geschlechtsabfrage, ansonsten geht es in der\n"
+	+"Beschreibunssequenz weiter.\n"
+	+"Stimmt die Kurzbeschreibung so? (j/n)\n]");
+  input_to("gotKurz");
+}
+
+static void gotKurz(string str)
+{
+  string art, nam, *p;
+  int gen;
+
+  if (str && sizeof(str) && str[0] == 'n') {
+    write( "Neues Spiel, neues Glueck... ;)\n");
+    getShort();
+    return;
+  }
+  write("\nIDs (durch Kommata getrennt):\n["+implode(QueryProp(P_IDS)-({TRUHE}), ",")+"]\n]");
+  input_to("gotIDs");
+}
+
+static void gotIDs(string str)
+{
+  if (!str || member( ({ "", ".", "**", "~q" }), str) >= 0 )
+    write("IDs nicht geaendert.\n");
+  else {
+    str = implode(old_explode(lower_case(str)," ")-({""})," ");
+    str = implode(old_explode(str,", "),",");
+    SetProp(P_IDS, old_explode(str, ",")+({TRUHE}));
+    write("OK, IDs lauten '"+str+"'\n");
+  }
+  write("\nBitte Langbeschreibung eingeben (Abbruch mit ~q, Ende mit . oder **):\n");
+  nedit("gotLang");
+}
+
+static void gotMat(mapping mat);
+
+static void gotLang(string str)
+{
+  if (!str || str == "")
+    write("Die Langbeschreibung wurde nicht veraendert.\n");
+  else
+    SetProp(P_LONG,implode(old_explode(str, "@@"),"**"));
+
+  write("\nBitte das Material "+name(WESSEN,1)+" bestimmen:\n"
+	"(<RETURN> falls nichts geaendert werden soll)\n\n");
+
+  EditMaterial(QueryProp(P_MATERIAL), #'gotMat, 0, ({MATGROUP_GAS,MATGROUP_EXPLOSIVE}));
+}
+
+static void gotMat(mapping mat)
+{
+  if (mappingp(mat)) {
+    SetProp(P_MATERIAL, mat);
+    write("OK.\n");
+  }
+  else
+    write("Das Material wurde nicht veraendert.\n");
+
+  save_object(HAUSSAVEPATH+owner+"truhe");
+}
+
+static int aendern(string str)
+{
+  if (getuid(this_player())!=owner || !id(str))
+    return 0;
+
+  write("Bitte Langbeschreibung aendern.\n(Abbruch mit ~q, Ende mit . oder **, Hilfe mit ~h)\n");
+  nedit("gotLang", QueryProp(P_LONG));
+  return 1;
+}
+
+void Save()
+{
+  save_object(HAUSSAVEPATH+owner+"truhe");
+}
+
+int SmartLog(string ofile, string typ, string msg, string date)
+{
+  object home;
+  string foo, bar;
+
+  write_file(REPFILE(owner), sprintf("%s von %s an der Truhe (%s):\n%s\n",
+				     typ,
+				     capitalize(getuid(this_player())),
+				     date,
+				     break_string(msg,78)));
+
+  if (IS_LEARNER(owner)) {
+    log_file("report/"+owner+".rep",
+	     sprintf("MELDUNG von %s im Seherhaus, an der Truhe (%s):\n"
+		     +"Bitte zur Kenntnis nehmen! (Mit dem Befehl 'meldungen')  -Wargon\n",
+		     capitalize(getuid(this_player())),
+		     date));
+  }
+
+  home = load_object(RAUMNAME(owner,0));
+  home->Set(H_REPORT, home->Query(H_REPORT)+1);
+  home->Save();
+
+  return 1;
+}
+
diff --git a/d/seher/haeuser/txt/kommentar.txt b/d/seher/haeuser/txt/kommentar.txt
new file mode 100644
index 0000000..53a4e09
--- /dev/null
+++ b/d/seher/haeuser/txt/kommentar.txt
@@ -0,0 +1,68 @@
+		    Jof'sche Kurz-Kommentare
+
+	    Kommentar zum Bausparvertrag fuer Seher
+
+bearbeitet von:
+
+Aufsichtsratsvorschwitzender Dr. fac. ju. Beldin
+Oberjurisdiktionsschrat Dr. hcl. Olim
+
+Zu Art. 1  (Gegenstand des Vertrages):	Du zahlst Erfahrung ein
+und bekommst am Ende (vielleicht) Dein Haus dafuer.  Die Erfah-
+rungspunkte sind auf jeden Fall erstmal weg.
+
+Zu Art. 2 (Parteien): Es gibt zwei Parteien, Dich und die Bank.
+Die Bank ist immer gleichberechtigter als Du.
+
+Zu Art. 3 (Wohnstaette): Ein Haus.
+
+Zu Art. 4 (Finanzierung):
+
+ Abs.1 (Kapitalvolumen)  Insgesamt  kostet das Haus 2544000  EP
+ (Option A) bzw. 2120000 EP (Option B).
+
+ Abs.2 (Ratenzahlung/Laufzeit)	Du musst  immer 80000  pro Rate
+ zahlen. Dabei hast Du zwei Moeglichkeiten:
+
+ A: 4  Stunden	Zeit  fuer jede  Rate. Zeit  auch  fuer  andere
+ Aktivitaeten  im Mud  ausser zu Metzeln.  Als Strafe darfst Du
+ dafuer insgesamt etwas mehr loehnen als bei Option B.
+ B: 2 Stunden  Zeit  fuer  jede  Rate.	Brutal,  aber  Du  bist
+ schneller  fertig mit	dem Kram. Waehl, unterschreib,	und die
+ Zeit laeuft bis alles eingezahlt ist.
+
+ Abs.3 (Zinsertrag)  6% Zinsen d.h.  Du musst nicht den  ganzen
+ Betrag  (2544000 bzw. 2120000	EP),  sondern  nur  einen  Teil
+ (2400000 bzw. 2000000 EP) zahlen.
+
+ Abs.4 (Einzahlung)  Die Erfahrung,  die die Bank interessiert,
+ musst Du  beim Metzeln  und Questen aufbringen.  Worauf Du den
+ Schwerpunkt legst, bleibt Dir ganz selbst ueberlassen. Es wird
+ waehrend des Kampfes automatisch  ein Teil der Dir zustehenden
+ Erfahrung auf den  BLOCK umgebucht.  Diesen musst Du dazu aber
+ erst einmal anziehen. Vor dem Kampf.  Du kannst natuerlich nur
+ soviel Erfahrung im Block speichern, wie Deine Rate zulaesst.
+
+ Abs.5 (Saeumniss)  Es gibt zwei  Moeglichkeiten  bei der  Bank
+ mehr Erfahrung zu lassen:
+ 1. Du	ueberziehst  eine  Ratenfrist  ->  Eine  Vertragsstrafe
+ i.H.v. 48000 EP (60%) ist  innerhalb von 2 bzw. 1 Onlinestunde
+ beizubringen.
+ 2. Du	ueberziehst  auch  die	Strafe -> Deine gesamte  bisher
+ eingezahlte Erfahrung ist VERLOREN.
+
+Zu Art. 5 (Unterschrift):  Du musst den Vertrag  unterschreiben
+(SYNTAX: unterschreibe vertrag).
+
+Zu Art. 6 (Erfuellung des Vertrages):  Wenn Du alles eingezahlt
+hast bekommst Du Dein Haus.
+
+Zu Art. 7 (Ansprueche  des Kunden): Du	hast keine  Ansprueche.
+Genau das steht auch in den AGB.
+
+Zu Anhang A: Du  kannst  das  Haus  in	einem  gewissen  Rahmen
+veraendern.
+
+Zu Anhang B: Mumpitz!  Keine  Garantie	fuer  gar  nichts  (Die
+GfgWbmbH ist eine 100% Tochter der MorgenGrauen-Bank)!
+
diff --git a/d/seher/haeuser/txt/vertrag.txt b/d/seher/haeuser/txt/vertrag.txt
new file mode 100644
index 0000000..13bcd07
--- /dev/null
+++ b/d/seher/haeuser/txt/vertrag.txt
@@ -0,0 +1,139 @@
+                   BAUSPARVERTRAG fuer SEHER
+         MorgenGrauen-Bank, 'Die Bank in Ihrem Ruecken'
+
+Art. 1 (Gegenstand  des Vertrages):  Gegenstand dieses  Vertrages
+ist die  verzinsliche Ansparung von  Erfahrungswerten zum  Zwecke
+der   Errichtung   einer   geeigneten   Wohnstaette   fuer    den
+unterzeichnenden  Seher.  Eine  vorzeitige  Auszahlung (d.h.  vor
+Ablauf  des Vertrages) ist nicht vorgesehen. Zur Wohnstaette s.u.
+Art.  3, zur   Laufzeit  s.u. Art.  4 Abs.2,  zur Erfuellung  des
+Vertrages s.u. Art. 6.
+
+Art.   2   (Parteien):   Parteien  dieses   Vertrages  sind   die
+MorgenGrauen-Bank auf der einen Seite (im folgenden Bank genannt)
+und Sie,  der unterzeichnende  Seher auf  der  anderen Seite  (im
+folgenden Seher genannt). Gemaess unserer Unternehmensphilosophie
+sind Bank und Seher bei diesem Rechtsgeschaeft  gleichberechtigte
+Partner.
+
+Art.  3  (Wohnstaette):  Als  geeignete  Wohnstaette i.S.  dieses
+Vertrages  gilt  das  MorgenGrauen-Seher-Einheitshaus   (MIN-Norm
+15647), welches  jedoch beschraenkt   modifizierbar  ist. Zu  den
+moeglichen  Modifikationen  s.u. Anhang  A. Zum  Volumen und  dem
+Zinsertrag des Bausparvertrages s.u. Art. 4 Abs.1 und  Abs.3.
+
+Art.  4 (Finanzierung):  Abs.1 (Kapitalvolumen)  Das Volumen  des
+Vertrages ist variabel. Es belaeuft sich bei  Option A (s. Abs.2)
+auf  2544000,  und bei Option B  (s. Abs.2)  auf 2120000  Morgen-
+Grauen-Erfahrungspunkte  (EP).  In diesen  Betrag  eingeschlossen
+sind  sowohl  Ihre zu  leistenden Zahlungen  als auch der erwirt-
+schaftete Zinsertrag.
+
+  Abs.2  (Ratenzahlung/Laufzeit)  Die  Bank  bietet  Ihnen   zwei
+  unterschiedliche Moeglichkeiten der Finanzierung:
+  Option A: DER SANFTE WEG ZUM EIGENHEIM    Bei einer  Ratenhoehe
+  von 80000 EP  innerhalb von jeweils 4 Online-Stunden und  einer
+  Ratenanzahl von 30  betraegt die  Gesamtlaufzeit  des Vertrages
+  120 Online-Stunden.
+  Option B: DER SCHNELLE WEG ZUM EIGENHEIM  Bei einer  Ratenhoehe
+  von 80000 EP  innerhalb von jeweils 2 Online-Stunden und  einer
+  Ratenanzahl  von 25 betraegt die  Gesamtlaufzeit des  Vertrages
+  50 Online-Stunden.
+  Die Laufzeit  beginnt mit dem Inkrafttreten  des Vertrages  und
+  endet mit der Erstellung des gewuenschten Hauses. Bei Saeumniss
+  des Sehers sind die Bestimmungen  des Art. 4 Abs.5  anzuwenden.
+  Der  Vertrag wird  mit der Unterschrift  des jeweiligen  Sehers
+  wirksam. Zu den Bestimmungen  ueber die  Unterschrift wird  auf
+  Art. 5 verwiesen.
+
+  Abs.3 (Zinsertrag) Der Zinssatz belaeuft  sich  auf 6,00%.  Die
+  Zinsen werden mit Einzahlung des letzten Erfahrungspunktes  be-
+  rechnet. Hieraus ergibt sich ein Zinsertrag von genau 144000 EP
+  bei Option A und 120000 bei Option B.
+
+  Abs.4  (Einzahlung)  Die  Bank  bietet  Ihnen  eine bisher  nie
+  dagewesene  finanzielle Flexibilitaet mit dem MG  MASTER-BLOCK.
+  Auch bei unvorhergesehenen und spontanen Raubzuegen koennen Sie
+  sich jederzeit Erfahrungspunkte gutschreiben lassen. Sie tragen
+  nur  Ihren  Block wie ein  uebliches Amulett und  waehrend  des
+  Kampfes oder auch  beim Loesen einer Quest  wird ihnen ein Teil
+  der  Erfahrungspunkte  gutgeschrieben  (bis  zur  Ratenhoechst-
+  grenze). Bareinzahlung der Raten ist nicht moeglich.
+
+  Abs.5 (Saeumniss) Saeumniss i.S. dieses Vertrages besteht immer
+  dann, wenn  der Unterzeichnende den in  Art.3  Abs.2  genannten
+  Mindestratenzeitraum  ueberschreitet. Dabei wird zunaechst eine
+  Vertragsstrafe in Hoehe von 60% der  Rate faellig. Wird  darauf
+  die Strafe nicht  innerhalb von 2 Online-Stunden (bei Option A)
+  oder  1 Online-Stunde  (bei Option B)  waehrend der  natuerlich
+  weiterlaufenden,  normalen  Ratenzahlungsfrist  beglichen, gilt
+  der  Vertrag ex  nunc  als hinfaellig. Bei  Hinfaelligkeit  des
+  Vertrags  wird  das  bisher eingezahlte  Kapital von  der  Bank
+  einbehalten.
+
+Art. 5  (Unterschrift): Als  gueltige Unterschrift  wird nur  ein
+individuelles  Zeichen  auf  dem  dafuer  vorgesehenen  Platz  am
+unteren,  rechten  Rand  der  letzten   Seite  dieses   Vertrages
+zugelassen (MIN-Norm 32454). Zur Sicherung der Authentizitaet des
+Dokuments ist die Unterschrift weiterhin mit Herzblut zu leisten.
+Als Schreibutensil werden sowohl eine Feder als auch ein Skalpell
+von der  Bank zur Verfuegung gestellt. Die Unterschrift kann  nur
+in Anwesenheit  eines autorisierten  Angestellten unseres  Hauses
+geleistet werden.
+
+Art. 6 (Erfuellung des Vertrages): Bei  Erfuellung des  Vertrages
+lt. den o.a. Art. wird dem Seher  das Haus  an der  Ausgabestelle
+schluesselfertig ausgeliefert. Der Erhalt ist zu quittieren.
+
+Art. 7 (Ansprueche  des Kunden)  Fuer etwaige  Schaeden am  Leser
+dieses  Vertrages  uebernimmt  die  Bank  keine  Haftung, da  sie
+ausschliesslich   auf   fehlerhaftes   Lesen   oder    Missbrauch
+zurueckzufuehren sind. Ansonsten garantiert die  Bank, dass  sich
+der Vertrag im wesentlichen  (und   nicht etwa  komplett) an  die
+derzeitig   gueltigen   Regeln   der   deutschen       Grammatik,
+Orthographie, und Stilistik  (und nicht etwa  an die  Regeln  des
+gesunden  Menschenverstandes)  haelt.  Bezueglich aller  unklaren
+Begriffe in den o.a. Art., welche sich  auf bankinterne  Ablaeufe
+beziehen, haben Sie das Recht, die in der Bank  aushaengenden AGB
+zu  lesen.  Unsere  freundlichen  geschulten  Mitarbeiter  werden
+sicherlich gerne auf die  wenigen noch  offenen Fragen  eingehen.
+In jedem Fall gilt das  Recht einer  von uns  im Schadensfall  zu
+benennenden Region.
+
+Wir danken Ihnen fuer das entgegengebrachte Vertrauen
+
+i.A.
+Aufsichtsratsvorschwitzender Dr. fac. ju. Beldin
+Oberjurisdiktionsschrat Dr. hcl. Olim
+
+
+
+                                   ------------------------------
+                                   (Unterschrift von ***********)
+
+
+Anhang A (Auszug aus  den Beschluessen  der MG-Normenkonferenz)
+"Modifikation:"
+[...]
+Das   Haus   wird  in  einheitlicher  Groesse   und   Ausstattung
+angeliefert  (MIN-Norm  15647).  Moderne   Werkstoffe  wie   z.B.
+drachenodemgebrannte  Nabla-Schlamm-Ziegel  gewaehrleisten sowohl
+kostenguenstige  Produktion,  als   auch  eine  fast  unbegrenzte
+Haltbarkeit.  Alle   Bauteile   sind   numeriert,  um   moegliche
+Modifikationen zu erleichtern. [...]
+Dem  Besitzer ist es  erlaubt sowohl  an der Fassade wie auch  im
+Inneren gewisse, individuelle Merkmale einzubringen.
+
+Anhang B (Lieferbestimmung der  Gesellschaft fuer gemeinnuetzigen
+          Wohnungsbau m.b.H.)
+[...]
+IV. Garantie
+Die GfgWbmbH uebernimmt keine Garantie fuer das uneingeschraenkte
+Funktionieren  des  Morgengrauen-Seher-Einheitshauses   (MIN-Norm
+15647) bei der Auslieferung. Fehler im, sowie am  Haus sind  nach
+dem derzeitigen Stand  der Bautechnik  unvermeidlich und  fuehren
+nicht   zu   Ersatzanspruechen.   Fuer   etwaige   Schaeden    am
+MorgenGrauen-Seher-Einheitshaus  (MIN-Norm  15647)  nach   dessen
+Auslieferung  uebernimmt  die  GfgWbmbH  keine  Haftung,  da  sie
+ausschliesslich   auf  fehlerhaftes   Bewohnen  oder   Missbrauch
+zurueckzufuehren  sind.
diff --git a/d/seher/haeuser/txt/vertrag_raum.txt b/d/seher/haeuser/txt/vertrag_raum.txt
new file mode 100644
index 0000000..5393f38
--- /dev/null
+++ b/d/seher/haeuser/txt/vertrag_raum.txt
@@ -0,0 +1,142 @@
+                   BAUSPARVERTRAG fuer SEHER
+                      -- AUSBAUVERTRAG --
+         MorgenGrauen-Bank, 'Die Bank in Ihrem Ruecken'
+
+Art. 1 (Gegenstand  des Vertrages):  Gegenstand dieses  Vertrages
+ist die  verzinsliche Ansparung von  Erfahrungswerten zum  Zwecke
+des  Ausbaues  der Wohnstaette des unterzeichnenden  Sehers. Eine
+vorzeitige  Auszahlung (d.h.  vor Ablauf des Vertrages) ist nicht
+vorgesehen. Zum Ausbau der Wohnstaette s.u.Art.  3, zur  Laufzeit
+s.u. Art. 4 Abs.2, zur Erfuellung des Vertrages s.u. Art. 6.
+
+Art.   2   (Parteien):   Parteien  dieses   Vertrages  sind   die
+MorgenGrauen-Bank auf der einen Seite (im folgenden Bank genannt)
+und Sie,  der unterzeichnende  Seher auf  der  anderen Seite  (im
+folgenden Seher genannt). Gemaess unserer Unternehmensphilosophie
+sind Bank und Seher bei diesem Rechtsgeschaeft  gleichberechtigte
+Partner.
+
+Art.  3 (Ausbau der Wohnstaette): Als Ausbau der Wohnstaette i.S.
+dieses   Vertrages   gilt   der   MorgenGrauen-Seher-Einheitsraum
+(MIN-Norm 15648), welcher jedoch beschraenkt  modifizierbar  ist.
+Zu den moeglichen Modifikationen s.u. Anhang  A. Zum  Volumen und
+dem Zinsertrag des Bausparvertrages s.u. Art. 4 Abs.1 und  Abs.3.
+
+Art.  4 (Finanzierung):  Abs.1 (Kapitalvolumen)  Das Volumen  des
+Vertrages ist variabel. Es belaeuft sich bei  Option A (s. Abs.2)
+auf 1017600, und bei Option B (s.Abs.2) auf 848000  MorgenGrauen-
+Erfahrungspunkte  (EP).  In  diesen  Betrag  eingeschlossen  sind
+sowohl Ihre zu  leistenden Zahlungen als auch der erwirtschaftete
+Zinsertrag.
+
+  Abs.2  (Ratenzahlung/Laufzeit)  Die  Bank  bietet  Ihnen   zwei
+  unterschiedliche Moeglichkeiten der Finanzierung:
+  Option A: DER SANFTE WEG ZUM AUSBAU       Bei einer  Ratenhoehe
+  von 80000 EP  innerhalb von jeweils 4 Online-Stunden und  einer
+  Ratenanzahl von 12  betraegt die  Gesamtlaufzeit  des Vertrages
+  48 Online-Stunden.
+  Option B: DER SCHNELLE WEG ZUM AUSBAU     Bei einer  Ratenhoehe
+  von 80000 EP  innerhalb von jeweils 2 Online-Stunden und  einer
+  Ratenanzahl  von 10 betraegt die  Gesamtlaufzeit des  Vertrages
+  20 Online-Stunden.
+  Die Laufzeit  beginnt mit dem Inkrafttreten  des Vertrages  und
+  endet mit dem  Einbau des Ausbaus.  Bei Saumnis des Sehers sind
+  sind die Bestimmungen  des Art. 4 Abs.5  anzuwenden.
+  Der  Vertrag wird  mit der Unterschrift  des jeweiligen  Sehers
+  wirksam. Zu den Bestimmungen  ueber die  Unterschrift wird  auf
+  Art. 5 verwiesen.
+
+  Abs.3 (Zinsertrag) Der Zinssatz belaeuft  sich  auf 6,00%.  Die
+  Zinsen werden mit Einzahlung des letzten Erfahrungspunktes  be-
+  rechnet. Hieraus ergibt sich ein Zinsertrag von genau  57600 EP
+  bei Option A und 48000 bei Option B.
+
+  Abs.4  (Einzahlung)  Die  Bank  bietet  Ihnen  eine bisher  nie
+  dagewesene  finanzielle Flexibilitaet mit dem MG  MASTER-BLOCK.
+  Auch bei unvorhergesehenen und spontanen Raubzuegen koennen Sie
+  sich jederzeit Erfahrungspunkte gutschreiben lassen. Sie tragen
+  nur  Ihren  Block wie ein  uebliches Amulett und  waehrend  des
+  Kampfes oder auch  beim Loesen einer Quest  wird ihnen ein Teil
+  der  Erfahrungspunkte  gutgeschrieben  (bis  zur  Ratenhoechst-
+  grenze). Bareinzahlung der Raten ist nicht moeglich.
+
+  Abs.5 (Saeumniss) Saeumniss i.S. dieses Vertrages besteht immer
+  dann, wenn  der Unterzeichnende den in  Art.3  Abs.2  genannten
+  Mindestratenzeitraum  ueberschreitet. Dabei wird zunaechst eine
+  Vertragsstrafe in Hoehe von 60% der  Rate faellig. Wird  darauf
+  die Strafe nicht  innerhalb von 2 Online-Stunden (bei Option A)
+  oder  1 Online-Stunde  (bei Option B)  waehrend der  natuerlich
+  weiterlaufenden,  normalen  Ratenzahlungsfrist  beglichen, gilt
+  der  Vertrag ex  nunc  als hinfaellig. Bei  Hinfaelligkeit  des
+  Vertrags  wird  das  bisher eingezahlte  Kapital von  der  Bank
+  einbehalten.
+
+Art. 5  (Unterschrift): Als  gueltige Unterschrift  wird nur  ein
+individuelles  Zeichen  auf  dem  dafuer  vorgesehenen  Platz  am
+unteren,  rechten  Rand  der  letzten   Seite  dieses   Vertrages
+zugelassen (MIN-Norm 32454). Zur Sicherung der Authentizitaet des
+Dokuments ist die Unterschrift weiterhin mit Herzblut zu leisten.
+Als Schreibutensil werden sowohl eine Feder als auch ein Skalpell
+von der  Bank zur Verfuegung gestellt. Die Unterschrift kann  nur
+in Anwesenheit  eines autorisierten  Angestellten unseres  Hauses
+geleistet werden.
+
+Art. 6 (Erfuellung des Vertrages): Bei  Erfuellung des  Vertrages
+lt.  den o.a. Art. wird bei  Vorlage des  Vertrages an der dafuer
+eingerichteten  Stelle der Ausbau der  Heimstatt des Sehers durch
+unseren   Ausbauservice   nach   MIN-Norm   20815   unverzueglich
+vorgenommen.   Dem  Seher  entstehen  hierdurch   keine  weiteren
+Verpflichtungen.
+
+Art. 7 (Ansprueche  des Kunden)  Fuer etwaige  Schaeden am  Leser
+dieses  Vertrages  uebernimmt  die  Bank  keine  Haftung, da  sie
+ausschliesslich   auf   fehlerhaftes   Lesen   oder    Missbrauch
+zurueckzufuehren sind. Ansonsten garantiert die  Bank, dass  sich
+der Vertrag im wesentlichen  (und   nicht etwa  komplett) an  die
+derzeitig   gueltigen   Regeln   der   deutschen       Grammatik,
+Orthographie, und Stilistik  (und nicht etwa  an die  Regeln  des
+gesunden  Menschenverstandes)  haelt.  Bezueglich aller  unklaren
+Begriffe in den o.a. Art., welche sich  auf bankinterne  Ablaeufe
+beziehen, haben Sie das Recht, die in der Bank  aushaengenden AGB
+zu  lesen.  Unsere  freundlichen  geschulten  Mitarbeiter  werden
+sicherlich gerne auf die  wenigen noch  offenen Fragen  eingehen.
+In jedem Fall gilt das  Recht einer  von uns  im Schadensfall  zu
+benennenden Region.
+
+Wir danken Ihnen fuer das entgegengebrachte Vertrauen
+
+i.A.
+Aufsichtsratsvorschwitzender Dr. fac. ju. Beldin
+Oberjurisdiktionsschrat Dr. hcl. Olim
+
+
+
+                                   ------------------------------
+                                   (Unterschrift von ***********)
+
+
+Anhang A (Auszug aus  den Beschluessen  der MG-Normenkonferenz)
+"Modifikation:"
+[...]
+Der  Ausbau  wird  in  einheitlicher  Groesse   und   Ausstattung
+angeliefert  (MIN-Norm  15648).   Der  Einbau  des   Ausbaus  ist
+jedoch   nur    und   ausschliesslich    gestattet    in    einem
+MorgenGrauen-Seher-Einheitshaus (MIN-Norm 15648).   Um   etwaigem
+Missbrauch vorzubeugen,  hat der  Einbau des  Ausbaus  nur  durch
+geschultes Fachpersonal zu erfolgen. [...]
+Dem  Besitzer ist es  erlaubt im  Inneren  gewisse,  individuelle
+Merkmale einzubringen.
+
+Anhang B (Lieferbestimmung der  Gesellschaft fuer gemeinnuetzigen
+          Wohnungsbau m.b.H.)
+[...]
+IV. Garantie
+Die GfgWbmbH uebernimmt keine Garantie fuer das uneingeschraenkte
+Funktionieren  des  Morgengrauen-Seher-Einheitsraumes   (MIN-Norm
+15648) bei der Auslieferung. Fehler im, sowie am  Raum sind  nach
+dem derzeitigen Stand  der Bautechnik  unvermeidlich und  fuehren
+nicht   zu   Ersatzanspruechen.   Fuer   etwaige   Schaeden    am
+MorgenGrauen-Seher-Einheitsraum  (MIN-Norm  15648)  nach   dessen
+Einbau   uebernimmt   die   GfgWbmbH   keine   Haftung,   da  sie
+ausschliesslich   auf  fehlerhaftes   Bewohnen  oder   Missbrauch
+zurueckzufuehren  sind.
diff --git a/d/seher/haeuser/virtual_compiler.c b/d/seher/haeuser/virtual_compiler.c
new file mode 100644
index 0000000..d5d1ab8
--- /dev/null
+++ b/d/seher/haeuser/virtual_compiler.c
@@ -0,0 +1,164 @@
+/** Letzte Aenderung:
+// 01.04.2008 - Objekte des VC werden jetzt im memory gespeichert
+//            - Kleinere Aenderungen im Code.
+// 01.01.2007 - Grundlegende Ueberarbeitung, Vanion
+//            - Verwaltung der Objekte in einer Liste
+//              Dadurch kann ls die VC-Objekte darstellen
+//            - Genauere Ueberpruefung der Dateinamen auf 
+//              Korrektheit. Falls ein illegaler Zugriff
+//              stattfindet, wirds geloggt.
+//
+*/
+
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+#pragma pedantic
+#pragma warn_deprecated
+
+#include "haus.h"
+#include <thing/properties.h>
+#include <wizlevels.h>
+
+#define MEMORY "/secure/memory"
+
+private nosave mapping objects;
+private nosave int show_clone_list;
+
+/**
+ Initialisierung
+*/
+void create()
+{
+  seteuid(getuid());
+
+  // Zeiger auf Cloneliste holen
+  if (call_other(MEMORY,"HaveRights")) {
+    // Objektpointer laden
+    objects = (mixed) call_other(MEMORY,"Load","objects");
+
+    // Hats geklappt?
+    if (!mappingp(objects)) 
+    // vermutlich noch nicht initialisiert.
+    // Zeiger erzeugen
+    objects = ([]); 
+    call_other(MEMORY,"Save","objects",objects); // Und in den Memory schreiben
+  } else {
+    // Wenn ich keinen Zeiger habe, muss ich halt einen lokalen nehmen.
+
+    objects = ([]);
+  }
+
+  show_clone_list = 0; 
+}
+
+/**
+ Die Funktion wird vom GameDriver aufgerufen. Wenn die Namen-
+ konventionen passen, erzeugt compile_object ein passendes Objekt
+ und gibt es zurueck
+ \param name Name des zu ladenden Objekts
+ \return Das geladene und initialisierte Objekt
+*/
+object compile_object(string name)
+{
+  object ob;
+
+  if (name[<6..<3] == "haus" )
+  {
+    ob = (object) (VERWALTER)->_LadeHaus(name[0..<7]);
+  }
+  else if (name[<7..<4] == "raum" )
+  {
+    ob = (object) (VERWALTER)->_LadeRaum(name[0..<8], to_int(name[<3..<3]));
+  } 
+  else 
+  {
+    log_file("seher/haeuser/vc_load_error", 
+      dtime(time())+"\n Falscher Objektname: "+name+"\n"+
+  	         " PO:  "+object_name(previous_object(1))+",\n"+
+  	         " PPO: "+object_name(previous_object(2))+",\n"+
+  	         " TI:  "+object_name(this_interactive())+".\n\n");
+  	return 0;
+  }
+  
+  // Objekt speichern (fuer ls)
+  if (objectp(ob)) objects[name] = ob;
+
+  return ob;
+}
+
+/**
+  Soll die Liste der Objekte zum VC per QueryObjects ausgegeben werden oder nicht?
+*/
+  
+int ToggleCloneList()
+{
+  show_clone_list ^= 1;
+  tell_object(this_player(), "Die Clone List wird nun "+
+        (show_clone_list?"":"nicht mehr ")+"angezeigt.\n");
+}
+
+/**
+ return all cloned virtual objects, wird zum Beispiel fuer "ls" benutzt.
+*/
+mixed QueryObjects()
+{
+  if (show_clone_list)
+    return m_values(objects)-({0});
+  else 
+    return ({});
+}
+
+/**
+  Nur EM und Maintainer duerfen die sicheren Funktionen nutzen
+*/
+int secure()
+{
+	if (ARCH_SECURITY || IS_MAINTAINER(this_interactive())) 
+	  return 1;
+	else  
+	  return 0;
+}
+
+/*
+/**
+ Die Funktion versucht, die Clones dieses VCs aus der Objekt List 
+ zu regenerieren
+*/
+/*
+int regenerate_clone_list()
+{
+	if (sizeof(objects)==0)
+	HDEBUG("Tbd: regenerate_clone_list im vc");
+	
+	return 1;
+}
+*/
+
+/**
+  Die Funktion zerstoert alle bereits geclonten Seherhausobjekte
+  Das gibt wirklich nur irre wenige Momente, wo das nicht schlecht ist.
+*/
+int desctruct_all_my_clones()
+{
+  mixed ob;
+
+  if (!secure()) return 0;
+
+  if(!mappingp(objects)) return 0;
+  
+  for(ob = QueryObjects(); sizeof(ob); ob = ob[1..])
+  {
+    if(objectp(ob[0]))
+    {
+      ob[0]->remove();
+      if(objectp(ob[0])) destruct(ob[0]);
+    }
+  }
+   
+  return 1;
+}
+
diff --git a/doc/.idx b/doc/.idx
new file mode 100644
index 0000000..ceb6245
--- /dev/null
+++ b/doc/.idx
@@ -0,0 +1,1218 @@
+.	
+Grundlagen	Grundlagen
+Objekte	Grundlagen/Objekte
+Effizienz	Grundlagen/Effizienz
+Effizienz.0	Grundlagen/Effizienz.0
+efun	efun
+transpose_array	efun/transpose_array
+find_player	efun/find_player
+write_file	efun/write_file
+write_bytes	efun/write_bytes
+write	efun/write
+wizlist_info	efun/wizlist_info
+users	efun/users
+wizlist	efun/wizlist
+walk_mapping	efun/walk_mapping
+unique_array	efun/unique_array
+unshadow	efun/unshadow
+unbound_lambda	efun/unbound_lambda
+typeof	efun/typeof
+traceprefix	efun/traceprefix
+transfer	efun/transfer
+to_string	efun/to_string
+trace	efun/trace
+to_int	efun/to_int
+this_player	efun/this_player
+to_float	efun/to_float
+to_array	efun/to_array
+time	efun/time
+throw	efun/throw
+this_interactive	efun/this_interactive
+tan	efun/tan
+this_object	efun/this_object
+tell_object	efun/tell_object
+tell_room	efun/tell_room
+test_bit	efun/test_bit
+symbolp	efun/symbolp
+tail	efun/tail
+shutdown	efun/shutdown
+sin	efun/sin
+symbol_function	efun/symbol_function
+swap	efun/swap
+strstr	efun/strstr
+strlen	efun/strlen
+stringp	efun/stringp
+sscanf	efun/sscanf
+sprintf	efun/sprintf
+sqrt	efun/sqrt
+sort_array	efun/sort_array
+snoop	efun/snoop
+slice_array	efun/slice_array
+sizeof	efun/sizeof
+seteuid	efun/seteuid
+shadow	efun/shadow
+shout	efun/shout
+set_extra_wizinfo_size	efun/set_extra_wizinfo_size
+set_this_player	efun/set_this_player
+set_this_object	efun/set_this_object
+sizeof	efun/sizeof
+say	efun/say
+set_modify_command	efun/set_modify_command
+set_prompt	efun/set_prompt
+rm	efun/rm
+set_light	efun/set_light
+set_living_name	efun/set_living_name
+set_heart_beat	efun/set_heart_beat
+set_is_wizard	efun/set_is_wizard
+set_extra_wizinfo	efun/set_extra_wizinfo
+save_object	efun/save_object
+set_bit	efun/set_bit
+rename	efun/rename
+set_auto_include_string	efun/set_auto_include_string
+send_imp	efun/send_imp
+rmdir	efun/rmdir
+rusage	efun/rusage
+restore_object	efun/restore_object
+query_once_interactive	efun/query_once_interactive
+replace_program	efun/replace_program
+rename_object	efun/rename_object
+remove_call_out	efun/remove_call_out
+remove_action	efun/remove_action
+remove_alist	efun/remove_alist
+referencep	efun/referencep
+remove_interactive	efun/remove_interactive
+read_file	efun/read_file
+regexplode	efun/regexplode
+regexp	efun/regexp
+read_bytes	efun/read_bytes
+raise_error	efun/raise_error
+random	efun/random
+query_verb	efun/query_verb
+quote	efun/quote
+query_load_average	efun/query_load_average
+query_snoop	efun/query_snoop
+query_mud_port	efun/query_mud_port
+query_input_pending	efun/query_input_pending
+query_ip_number	efun/query_ip_number
+query_ip_name	efun/query_ip_name
+query_imp_port	efun/query_imp_port
+query_editing	efun/query_editing
+query_idle	efun/query_idle
+interactive	efun/interactive
+log	efun/log
+query_actions	efun/query_actions
+program_time	efun/program_time
+printf	efun/printf
+query_host_name	efun/query_host_name
+process_string	efun/process_string
+previous_object	efun/previous_object
+pointerp	efun/pointerp
+people	efun/people
+present	efun/present
+parse_command	efun/parse_command
+parse_command.1	efun/parse_command.1
+order_alist	efun/order_alist
+objectp	efun/objectp
+negate	efun/negate
+notify_fail	efun/notify_fail
+next_inventory	efun/next_inventory
+mkdir	efun/mkdir
+move_object	efun/move_object
+member_array	efun/member_array
+mkmapping	efun/mkmapping
+mappingp	efun/mappingp
+m_contains	efun/m_contains
+member	efun/member
+map	efun/map
+m_values	efun/m_values
+map_indices	efun/map_indices
+map_objects	efun/map_objects
+m_indices	efun/m_indices
+m_delete	efun/m_delete
+log_file	efun/log_file
+lower_case	efun/lower_case
+living	efun/living
+lambda	efun/lambda
+intp	efun/intp
+function_exists	efun/function_exists
+intersect_alist	efun/intersect_alist
+insert_alist.orig	efun/insert_alist.orig
+insert_alist.rej	efun/insert_alist.rej
+inherit_list	efun/inherit_list
+input_to	efun/input_to
+implode	efun/implode
+insert_alist	efun/insert_alist
+heart_beat_info	efun/heart_beat_info
+get_type_info	efun/get_type_info
+geteuid	efun/geteuid
+getuid	efun/getuid
+get_error_file	efun/get_error_file
+get_extra_wizinfo	efun/get_extra_wizinfo
+get_eval_cost	efun/get_eval_cost
+get_dir	efun/get_dir
+garbage_collection	efun/garbage_collection
+funcall	efun/funcall
+find_object	efun/find_object
+first_inventory	efun/first_inventory
+functionlist	efun/functionlist
+floatp	efun/floatp
+enable_commands	efun/enable_commands
+filter_objects	efun/filter_objects
+find_living	efun/find_living
+find_call_out	efun/find_call_out
+filter	efun/filter
+filter_indices	efun/filter_indices
+file_size	efun/file_size
+object_name	efun/object_name
+file_time	efun/file_time
+export_uid	efun/export_uid
+extract	efun/extract
+explode	efun/explode
+exclude_array	efun/exclude_array
+exp	efun/exp
+exec	efun/exec
+exclude_alist	efun/exclude_alist
+ed	efun/ed
+environment	efun/environment
+debug_info	efun/debug_info
+efun	efun/efun
+cos	efun/cos
+destruct	efun/destruct
+crypt	efun/crypt
+disable_commands	efun/disable_commands
+caller_stack_depth	efun/caller_stack_depth
+deep_inventory	efun/deep_inventory
+ctime	efun/ctime
+create_wizard	efun/create_wizard
+creator	efun/creator
+copy	efun/copy
+command	efun/command
+clone_object	efun/clone_object
+closurep	efun/closurep
+catch	efun/catch
+clear_bit	efun/clear_bit
+cindent	efun/cindent
+cat	efun/cat
+call_resolved	efun/call_resolved
+call_out_info	efun/call_out_info
+capitalize	efun/capitalize
+call_other	efun/call_other
+call_out	efun/call_out
+allocate_mapping	efun/allocate_mapping
+break_string	efun/break_string
+assoc	efun/assoc
+bind_lambda	efun/bind_lambda
+atan	efun/atan
+apply	efun/apply
+break_point	efun/break_point
+allocate	efun/allocate
+add_worth	efun/add_worth
+asin	efun/asin
+all_inventory	efun/all_inventory
+add_xverb	efun/add_xverb
+send_erq	efun/send_erq
+add_verb	efun/add_verb
+acos	efun/acos
+add_action	efun/add_action
+attach_erq_demon	efun/attach_erq_demon
+binary_message	efun/binary_message
+debug_message	efun/debug_message
+set_environment	efun/set_environment
+extern_call	efun/extern_call
+last_instructions	efun/last_instructions
+query_shadowing	efun/query_shadowing
+set_buffer_size	efun/set_buffer_size
+set_connection_charset	efun/set_connection_charset
+set_driver_hook	efun/set_driver_hook
+symbol_variable	efun/symbol_variable
+to_object	efun/to_object
+deep_present	efun/deep_present
+efun::m_delete	efun/efun::m_delete
+efun::explode	efun/efun::explode
+all_environment	efun/all_environment
+dtime	efun/dtime
+find_livings	efun/find_livings
+find_netdead	efun/find_netdead
+is_clone	efun/is_clone
+lowerchar	efun/lowerchar
+lowerstring	efun/lowerstring
+match_living	efun/match_living
+new_explode	efun/new_explode
+old_explode	efun/old_explode
+query_wiz_grp	efun/query_wiz_grp
+query_wiz_level	efun/query_wiz_level
+set_object_heart_beat	efun/set_object_heart_beat
+update_actions	efun/update_actions
+upperstring	efun/upperstring
+uptime	efun/uptime
+time2string	efun/time2string
+regreplace	efun/regreplace
+dump_netdead	efun/dump_netdead
+std	std
+room.doku	std/room.doku
+weapon	std/weapon
+transport	std/transport
+room	std/room
+thing	std/thing
+armour	std/armour
+unit	std/unit
+tuer	std/tuer
+ruestung	std/ruestung
+sequencer	std/sequencer
+schluessel	std/schluessel
+living	std/living
+door	std/door
+corpse	std/corpse
+container	std/container
+vererbungsbaeume	std/vererbungsbaeume
+virtual_compiler	std/virtual_compiler
+MG	MG
+gilden-doku	MG/gilden-doku
+waffen_werte	MG/waffen_werte
+zaubertraenke	MG/zaubertraenke
+obj_geruest	MG/obj_geruest
+netz	MG/netz
+put_and_get	MG/put_and_get
+autoload	MG/autoload
+GOOD_STYLE	MG/GOOD_STYLE
+benennung	MG/benennung
+banish	MG/banish
+angriff	MG/angriff
+muster_raum.c	MG/muster_raum.c
+skills.doc	MG/skills.doc
+put_and_get.c	MG/put_and_get.c
+quests.doc	MG/quests.doc
+todessequenz	MG/todessequenz
+sensitive	MG/sensitive
+concepts	concepts
+erq	concepts/erq
+hooks	concepts/hooks
+properties.new	concepts/properties.new
+terminals	concepts/terminals
+uids	concepts/uids
+simul_efun	concepts/simul_efun
+oop	concepts/oop
+properties	concepts/properties
+memory	concepts/memory
+news	concepts/news
+objects	concepts/objects
+native	concepts/native
+mail	concepts/mail
+lpc	concepts/lpc
+files	concepts/files
+concepts	concepts/concepts
+imp	concepts/imp
+negotiation	concepts/negotiation
+lfun	lfun
+QueryDamage	lfun/QueryDamage
+SetHitFunc	lfun/SetHitFunc
+HitFunc	lfun/HitFunc
+create_default_npc	lfun/create_default_npc
+SetDefendFunc	lfun/SetDefendFunc
+DefendFunc	lfun/DefendFunc
+QueryDefend	lfun/QueryDefend
+Defend	lfun/Defend
+Attack	lfun/Attack
+AddCmd	lfun/AddCmd
+heart_beat	lfun/heart_beat
+name	lfun/name
+reset	lfun/reset
+remove	lfun/remove
+muster	lfun/muster
+long	lfun/long
+query_real_name	lfun/query_real_name
+reduce_hit_point	lfun/reduce_hit_point
+move	lfun/move
+id	lfun/id
+modify_command	lfun/modify_command
+lfun	lfun/lfun
+logon	lfun/logon
+init	lfun/init
+do_damage	lfun/do_damage
+exit	lfun/exit
+heal_self	lfun/heal_self
+extra_look	lfun/extra_look
+clean_up	lfun/clean_up
+create	lfun/create
+DiscoverDoor	lfun/DiscoverDoor
+catch_tell	lfun/catch_tell
+catch_msg	lfun/catch_msg
+Teleport	lfun/Teleport
+__INIT	lfun/__INIT
+SetProp	lfun/SetProp
+ShowDoors	lfun/ShowDoors
+Kill	lfun/Kill
+NewDoor	lfun/NewDoor
+QueryPossPronoun	lfun/QueryPossPronoun
+CheckResistance	lfun/CheckResistance
+DoorIsKnown	lfun/DoorIsKnown
+AddPursuer	lfun/AddPursuer
+AddRoomCmd	lfun/AddRoomCmd
+AddInfo	lfun/AddInfo
+RemoveRursuer	lfun/RemoveRursuer
+PreventFollow	lfun/PreventFollow
+Message	lfun/Message
+InFight	lfun/InFight
+AddAmount	lfun/AddAmount
+access_rights	lfun/access_rights
+trigger_sensitive_attack	lfun/trigger_sensitive_attack
+QueryDoorStatus	lfun/QueryDoorStatus
+QueryAllDoors	lfun/QueryAllDoors
+SetDoorStatus	lfun/SetDoorStatus
+QueryDoorKey	lfun/QueryDoorKey
+AddAdjective	lfun/AddAdjective
+AddClass	lfun/AddClass
+AddDetail	lfun/AddDetail
+AddDrink	lfun/AddDrink
+AddExit	lfun/AddExit
+AddFood	lfun/AddFood
+AddFuel	lfun/AddFuel
+AddFun	lfun/AddFun
+AddId	lfun/AddId
+AddItem	lfun/AddItem
+AddMsg	lfun/AddMsg
+AddPluralId	lfun/AddPluralId
+AddReadDetail	lfun/AddReadDetail
+AddRoomMessage	lfun/AddRoomMessage
+AddRoute	lfun/AddRoute
+AddSingularId	lfun/AddSingularId
+AddSpecialDetail	lfun/AddSpecialDetail
+DeclAdj	lfun/DeclAdj
+AddSpecialExit	lfun/AddSpecialExit
+DoUnwear	lfun/DoUnwear
+DoUnwield	lfun/DoUnwield
+DoWear	lfun/DoWear
+Enter	lfun/Enter
+GetDetail	lfun/GetDetail
+GetExits	lfun/GetExits
+Halt	lfun/Halt
+Identify	lfun/Identify
+IsUnit	lfun/IsUnit
+Leave	lfun/Leave
+MayAddWeight	lfun/MayAddWeight
+PreventInsert	lfun/PreventInsert
+Query	lfun/Query
+QueryArrived	lfun/QueryArrived
+QueryArticle	lfun/QueryArticle
+QueryBuyFact	lfun/QueryBuyFact
+QueryCoinsPerUnits	lfun/QueryCoinsPerUnits
+QueryDu	lfun/QueryDu
+QueryFlaw	lfun/QueryFlaw
+QueryGenderString	lfun/QueryGenderString
+QueryGramsPerUnits	lfun/QueryGramsPerUnits
+QueryName	lfun/QueryName
+QueryPassengers	lfun/QueryPassengers
+QueryPosition	lfun/QueryPosition
+QueryPronoun	lfun/QueryPronoun
+QueryProp	lfun/QueryProp
+QueryProperties	lfun/QueryProperties
+RemoveAdjective	lfun/RemoveAdjective
+RemoveId	lfun/RemoveId
+RemoveClass	lfun/RemoveClass
+RemoveCmd	lfun/RemoveCmd
+RemoveDetail	lfun/RemoveDetail
+RemoveExit	lfun/RemoveExit
+RemoveFunc	lfun/RemoveFunc
+RemoveReadDetail	lfun/RemoveReadDetail
+RemoveRoute	lfun/RemoveRoute
+RemoveSpecialDetail	lfun/RemoveSpecialDetail
+RemoveSpecialExit	lfun/RemoveSpecialExit
+Set	lfun/Set
+SetBuyFact	lfun/SetBuyFact
+SetCoinsPerUnits	lfun/SetCoinsPerUnits
+SetGramsPerUnits	lfun/SetGramsPerUnits
+SetProperties	lfun/SetProperties
+SetStorageRoom	lfun/SetStorageRoom
+ShowPropList	lfun/ShowPropList
+Start	lfun/Start
+SuggestArticle	lfun/SuggestArticle
+TakeFlaw	lfun/TakeFlaw
+UnwieldFunc	lfun/UnwieldFunc
+WearFunc	lfun/WearFunc
+WieldFunc	lfun/WieldFunc
+do_unwear	lfun/do_unwear
+do_wear	lfun/do_wear
+int_long	lfun/int_long
+int_short	lfun/int_short
+is_class_member	lfun/is_class_member
+locate_objects	lfun/locate_objects
+make_invlist	lfun/make_invlist
+paramove	lfun/paramove
+present_objects	lfun/present_objects
+query_weight_contents	lfun/query_weight_contents
+short	lfun/short
+trigger_sensitive_inv	lfun/trigger_sensitive_inv
+wield_me	lfun/wield_me
+AddSpell	lfun/AddSpell
+RemoveItem	lfun/RemoveItem
+NotifyGiveQuest	lfun/NotifyGiveQuest
+Damage	lfun/Damage
+SetChats	lfun/SetChats
+SetAttackChats	lfun/SetAttackChats
+RemovePluralId	lfun/RemovePluralId
+RemoveSingularId	lfun/RemoveSingularId
+QueryMaterial	lfun/QueryMaterial
+MaterialList	lfun/MaterialList
+QueryMaterialGroup	lfun/QueryMaterialGroup
+misc	misc
+called_by_player	misc/called_by_player
+simul_efuns	misc/simul_efuns
+CLEAN_UP	misc/CLEAN_UP
+mem	misc/mem
+beispiele	beispiele
+bspruest1.c	beispiele/bspruest1.c
+bspmon2.c	beispiele/bspmon2.c
+raum1.c	beispiele/raum1.c
+wolke.c	beispiele/wolke.c
+bspwaffe1.c	beispiele/bspwaffe1.c
+bspmon1.c	beispiele/bspmon1.c
+LPC	LPC
+alists	LPC/alists
+arrays	LPC/arrays
+block	LPC/block
+closures	LPC/closures
+closures-example	LPC/closures-example
+do-while	LPC/do-while
+ed0	LPC/ed0
+ed1	LPC/ed1
+ed2	LPC/ed2
+ed3	LPC/ed3
+ed4	LPC/ed4
+ed5	LPC/ed5
+ed6	LPC/ed6
+efuns	LPC/efuns
+for	LPC/for
+functions	LPC/functions
+inherit	LPC/inherit
+inheritance	LPC/inheritance
+initialisation	LPC/initialisation
+lfuns	LPC/lfuns
+lpc	LPC/lpc
+mappings	LPC/mappings
+operators	LPC/operators
+pragma	LPC/pragma
+preprocessor	LPC/preprocessor
+references	LPC/references
+switch	LPC/switch
+types	LPC/types
+while	LPC/while
+master	master
+valid_query_snoop	master/valid_query_snoop
+valid_snoop	master/valid_snoop
+valid_write	master/valid_write
+valid_read	master/valid_read
+valid_seteuid	master/valid_seteuid
+slow_shut_down	master/slow_shut_down
+valid_exec	master/valid_exec
+retrieve_ed_setup	master/retrieve_ed_setup
+save_ed_setup	master/save_ed_setup
+runtime_error	master/runtime_error
+remove_player	master/remove_player
+query_player_level	master/query_player_level
+receive_imp	master/receive_imp
+quota_demon	master/quota_demon
+preload	master/preload
+privilege_violation	master/privilege_violation
+reactivate_destructed_master	master/reactivate_destructed_master
+query_allow_shadow	master/query_allow_shadow
+heart_beat_error	master/heart_beat_error
+object_name	master/object_name
+prepare_destruct	master/prepare_destruct
+parse_command_all_word	master/parse_command_all_word
+parse_command_prepos_list	master/parse_command_prepos_list
+make_path_absolute	master/make_path_absolute
+master	master/master
+get_root_uid	master/get_root_uid
+log_error	master/log_error
+external_master_reload	master/external_master_reload
+inaugurate_master	master/inaugurate_master
+get_wiz_name	master/get_wiz_name
+get_simul_efun	master/get_simul_efun
+get_bb_uid	master/get_bb_uid
+flag	master/flag
+epilog	master/epilog
+get_ed_buffer_save_object_name	master/get_ed_buffer_save_object_name
+connect	master/connect
+dangling_lfun_closure	master/dangling_lfun_closure
+define_include_dirs	master/define_include_dirs
+disconnect	master/disconnect
+creator_file	master/creator_file
+compile_object	master/compile_object
+master-3.2	master/master-3.2
+Grep-Privilege	master/Grep-Privilege
+Grep-Apply-Master	master/Grep-Apply-Master
+Grep-CheckValidPath-Master	master/Grep-CheckValidPath-Master
+get_master_uid	master/get_master_uid
+initialisation	master/initialisation
+master-3.2.1	master/master-3.2.1
+notify_shutdown	master/notify_shutdown
+stale_erq	master/stale_erq
+ed	ed
+ed4	ed/ed4
+ed1	ed/ed1
+ed0	ed/ed0
+ed3	ed/ed3
+ed2	ed/ed2
+driver	driver
+commandline	driver/commandline
+driver	driver/driver
+debugmalloc	driver/debugmalloc
+dumpallobj	driver/dumpallobj
+malloc	driver/malloc
+opcdump	driver/opcdump
+predefined	driver/predefined
+showsmallnewmalloced	driver/showsmallnewmalloced
+status	driver/status
+KURS	KURS
+LPC-KURS2	KURS/LPC-KURS2
+chapter5	KURS/LPC-KURS2/chapter5
+chapter4	KURS/LPC-KURS2/chapter4
+chapter3	KURS/LPC-KURS2/chapter3
+chapter7	KURS/LPC-KURS2/chapter7
+chapter6	KURS/LPC-KURS2/chapter6
+chapter2	KURS/LPC-KURS2/chapter2
+Copyright	KURS/LPC-KURS2/Copyright
+chapter1	KURS/LPC-KURS2/chapter1
+Contents	KURS/LPC-KURS2/Contents
+LPC-KURS	KURS/LPC-KURS
+Introduction	KURS/LPC-KURS/Introduction
+chapter4	KURS/LPC-KURS/chapter4
+chapter2	KURS/LPC-KURS/chapter2
+chapter8	KURS/LPC-KURS/chapter8
+chapter7	KURS/LPC-KURS/chapter7
+chapter6	KURS/LPC-KURS/chapter6
+chapter5	KURS/LPC-KURS/chapter5
+chapter3	KURS/LPC-KURS/chapter3
+chapter1	KURS/LPC-KURS/chapter1
+Contents	KURS/LPC-KURS/Contents
+objekte	KURS/objekte
+raum1	KURS/raum1
+einleitung	KURS/einleitung
+RULES	KURS/RULES
+RULES.WIZ	KURS/RULES.WIZ
+COMMENTS	KURS/COMMENTS
+GUIDE.old	KURS/GUIDE.old
+3.0	3.0
+mudlib_outline	3.0/mudlib_outline
+problems	3.0/problems
+lfun_efun.man	3.0/lfun_efun.man
+lfun_efun.me	3.0/lfun_efun.me
+auto_destruct	3.0/auto_destruct
+LPmud.doc.me	3.0/LPmud.doc.me
+LPmud.doc.man	3.0/LPmud.doc.man
+3.0.info	3.0/3.0.info
+@README	@README
+about-efun+lfun-docs	about-efun+lfun-docs
+closures	closures
+func_spec	func_spec
+help	help
+abenteuer	help/abenteuer
+abenteurer	help/abenteurer
+attribute	help/attribute
+bewegung	help/bewegung
+bierschuettler	help/bierschuettler
+buecher	help/buecher
+chaos	help/chaos
+editor	help/editor
+erzmagier	help/erzmagier
+faq	help/faq
+forscherpunkte	help/forscherpunkte
+gilden	help/gilden
+kaempfer	help/kaempfer
+karate	help/karate
+klerus	help/klerus
+konzept	help/konzept
+magier	help/magier
+post	help/post
+regionen	help/regionen
+scripte	help/scripte
+seher	help/seher
+stufen	help/stufen
+stufenpunkte	help/stufenpunkte
+syntax	help/syntax
+zauberei	help/zauberei
+zauberer	help/zauberer
+zaubertraenke	help/zaubertraenke
+verein	help/verein
+props	props
+P_COMBATCMDS	props/P_COMBATCMDS
+P_INVIS	props/P_INVIS
+P_COMBATCMDS.old	props/P_COMBATCMDS.old
+P_HIDE_EXITS	props/P_HIDE_EXITS
+P_INVIS.old	props/P_INVIS.old
+P_HIDE_EXITS.old	props/P_HIDE_EXITS.old
+P_AC	props/P_AC
+P_WORN	props/P_WORN
+P_ARMOUR_TYPE	props/P_ARMOUR_TYPE
+P_DEFEND_FUNC	props/P_DEFEND_FUNC
+P_WEAR_FUNC	props/P_WEAR_FUNC
+P_REMOVE_FUNC	props/P_REMOVE_FUNC
+P_NO_GLOBAL_ATTACK	props/P_NO_GLOBAL_ATTACK
+P_DAMAGED	props/P_DAMAGED
+P_EFFECTIVE_WC	props/P_EFFECTIVE_WC
+P_EFFECTIVE_AC	props/P_EFFECTIVE_AC
+P_MAX_WEIGHT	props/P_MAX_WEIGHT
+P_CONTENTS	props/P_CONTENTS
+P_CNT_STATUS	props/P_CNT_STATUS
+P_TRANSPARENT	props/P_TRANSPARENT
+P_DOOR_INFOS	props/P_DOOR_INFOS
+P_WATER	props/P_WATER
+P_FISH	props/P_FISH
+P_LIQUID	props/P_LIQUID
+P_LONG_EMPTY	props/P_LONG_EMPTY
+P_LONG_FULL	props/P_LONG_FULL
+P_GUARD	props/P_GUARD
+P_EVAL_OFFSETS	props/P_EVAL_OFFSETS
+P_EVAL_FACTORS	props/P_EVAL_FACTORS
+P_INPC_LAST_PLAYER_CONTACT	props/P_INPC_LAST_PLAYER_CONTACT
+P_INPC_LAST_ENVIRONMENT	props/P_INPC_LAST_ENVIRONMENT
+P_INPC_WALK_MODE	props/P_INPC_WALK_MODE
+P_INPC_WALK_DELAYS	props/P_INPC_WALK_DELAYS
+P_INPC_WALK_FLAGS	props/P_INPC_WALK_FLAGS
+P_INPC_WALK_AREA	props/P_INPC_WALK_AREA
+P_INPC_WALK_ROUTE	props/P_INPC_WALK_ROUTE
+P_INPC_HOME	props/P_INPC_HOME
+P_ARTICLE	props/P_ARTICLE
+P_GENDER	props/P_GENDER
+P_ATTRIBUTES	props/P_ATTRIBUTES
+P_ATTRIBUTES_OFFSETS	props/P_ATTRIBUTES_OFFSETS
+P_X_ATTR_MOD	props/P_X_ATTR_MOD
+P_ATTRIBUTES_MODIFIER	props/P_ATTRIBUTES_MODIFIER
+P_ABILITIES	props/P_ABILITIES
+P_RESISTANCE	props/P_RESISTANCE
+P_RESISTANCE_STRENGHTS	props/P_RESISTANCE_STRENGHTS
+P_VULNERABILITY	props/P_VULNERABILITY
+P_TOTAL_AC	props/P_TOTAL_AC
+P_HANDS	props/P_HANDS
+P_MAX_HANDS	props/P_MAX_HANDS
+P_USED_HANDS	props/P_USED_HANDS
+P_ATTACK_BUSY	props/P_ATTACK_BUSY
+P_PREFERED_ENEMY	props/P_PREFERED_ENEMY
+P_RESISTANCE_STRENGTHS	props/P_RESISTANCE_STRENGTHS
+P_AGE	props/P_AGE
+P_ALIGN	props/P_ALIGN
+P_DEADS	props/P_DEADS
+P_GHOST	props/P_GHOST
+P_FROG	props/P_FROG
+P_FOOD	props/P_FOOD
+P_MAX_FOOD	props/P_MAX_FOOD
+P_DRINK	props/P_DRINK
+P_MAX_DRINK	props/P_MAX_DRINK
+P_ALCOHOL	props/P_ALCOHOL
+P_MAX_ALCOHOL	props/P_MAX_ALCOHOL
+P_HP	props/P_HP
+P_MAX_HP	props/P_MAX_HP
+P_SP	props/P_SP
+P_MAX_SP	props/P_MAX_SP
+P_XP	props/P_XP
+P_VALID_GUILDS	props/P_VALID_GUILDS
+P_GUILD_SKILLS	props/P_GUILD_SKILLS
+P_GUILD_RESTRICTIONS	props/P_GUILD_RESTRICTIONS
+P_GUILD_DEFAULT_SPELLBOOK	props/P_GUILD_DEFAULT_SPELLBOOK
+P_GUILD_MALE_TITLES	props/P_GUILD_MALE_TITLES
+P_GUILD_FEMALE_TITLES	props/P_GUILD_FEMALE_TITLES
+P_GUILD_LEVELS	props/P_GUILD_LEVELS
+P_SB_SPELLS	props/P_SB_SPELLS
+P_GLOBAL_SKILLPROPS	props/P_GLOBAL_SKILLPROPS
+P_GUILD_LEVEL	props/P_GUILD_LEVEL
+P_GUILD_TITLE	props/P_GUILD_TITLE
+P_GUILD_RATING	props/P_GUILD_RATING
+P_NEWSKILLS	props/P_NEWSKILLS
+P_NEXT_SPELL_TIME	props/P_NEXT_SPELL_TIME
+P_TMP_ATTACK_HOOK	props/P_TMP_ATTACK_HOOK
+P_TMP_ATTACK_MOD	props/P_TMP_ATTACK_MOD
+P_LEP	props/P_LEP
+P_TMP_DEFEND_HOOK	props/P_TMP_DEFEND_HOOK
+P_TMP_DIE_HOOK	props/P_TMP_DIE_HOOK
+P_DEFENDERS	props/P_DEFENDERS
+P_SKILL_ATTRIBUTES	props/P_SKILL_ATTRIBUTES
+P_IGNORE	props/P_IGNORE
+P_LAST_COMMAND_ENV	props/P_LAST_COMMAND_ENV
+P_HISTMIN	props/P_HISTMIN
+P_SHOW_ALIAS_PROCESSING	props/P_SHOW_ALIAS_PROCESSING
+P_DEFAULT_NOTIFY_FAIL	props/P_DEFAULT_NOTIFY_FAIL
+P_NETDEAD_INFO	props/P_NETDEAD_INFO
+P_IP_NAME	props/P_IP_NAME
+P_AUTH_INFO	props/P_AUTH_INFO
+P_LAST_KILLER	props/P_LAST_KILLER
+P_ACTUAL_NOTIFY_FAIL	props/P_ACTUAL_NOTIFY_FAIL
+P_LEP_MALUS	props/P_LEP_MALUS
+P_LAST_LOGIN	props/P_LAST_LOGIN
+P_LAST_LOGOUT	props/P_LAST_LOGOUT
+P_SHOW_EXITS	props/P_SHOW_EXITS
+P_WANTS_TO_LEARN	props/P_WANTS_TO_LEARN
+P_AUTOLOADOBJ	props/P_AUTOLOADOBJ
+P_TTY	props/P_TTY
+P_AUTOLOAD	props/P_AUTOLOAD
+P_MAILADDR	props/P_MAILADDR
+P_HOMEPAGE	props/P_HOMEPAGE
+P_FOLLOW_SILENT	props/P_FOLLOW_SILENT
+P_SECOND	props/P_SECOND
+P_TESTPLAYER	props/P_TESTPLAYER
+P_START_HOME	props/P_START_HOME
+P_CMSG	props/P_CMSG
+P_DMSG	props/P_DMSG
+P_CLONE_MSG	props/P_CLONE_MSG
+P_DESTRUCT_MSG	props/P_DESTRUCT_MSG
+P_CARRIED_VALUE	props/P_CARRIED_VALUE
+P_PROMPT	props/P_PROMPT
+P_SCREENSIZE	props/P_SCREENSIZE
+P_FLAGS	props/P_FLAGS
+P_CAN_FLAGS	props/P_CAN_FLAGS
+P_READ_NEWS	props/P_READ_NEWS
+P_NEEDED_QP	props/P_NEEDED_QP
+P_INTERMUD	props/P_INTERMUD
+P_BUFFER	props/P_BUFFER
+P_DEAF	props/P_DEAF
+P_PERM_STRING	props/P_PERM_STRING
+P_EXTRA_LOOK	props/P_EXTRA_LOOK
+P_PRESAY	props/P_PRESAY
+P_TITLE	props/P_TITLE
+P_AVERAGE_SIZE	props/P_AVERAGE_SIZE
+P_REFERENCE_OBJECT	props/P_REFERENCE_OBJECT
+P_MSGIN	props/P_MSGIN
+P_MSGOUT	props/P_MSGOUT
+P_MMSGIN	props/P_MMSGIN
+P_MMSGOUT	props/P_MMSGOUT
+P_POTIONROOMS	props/P_POTIONROOMS
+P_TRANK_FINDEN	props/P_TRANK_FINDEN
+P_VISITED_POTIONROOMS	props/P_VISITED_POTIONROOMS
+P_BONUS_POTIONS	props/P_BONUS_POTIONS
+P_QUESTS	props/P_QUESTS
+P_QP	props/P_QP
+P_SKILLS	props/P_SKILLS
+P_BLIND	props/P_BLIND
+P_BRIEF	props/P_BRIEF
+P_ORIG_NAME	props/P_ORIG_NAME
+P_KILLER	props/P_KILLER
+P_MURDER_MSG	props/P_MURDER_MSG
+P_KILL_MSG	props/P_KILL_MSG
+P_KILL_NAME	props/P_KILL_NAME
+P_CORPSE	props/P_CORPSE
+P_ENEMY_DEATH_SEQUENCE	props/P_ENEMY_DEATH_SEQUENCE
+P_LIGHT	props/P_LIGHT
+P_CLONER	props/P_CLONER
+P_TOTAL_LIGHT	props/P_TOTAL_LIGHT
+P_LAST_CONTENT_CHANGE	props/P_LAST_CONTENT_CHANGE
+P_VALUE	props/P_VALUE
+P_FORCE_DEMONST	props/P_FORCE_DEMONST
+P_INFO	props/P_INFO
+P_READ_MSG	props/P_READ_MSG
+P_FW_ALWAYS_READABLE	props/P_FW_ALWAYS_READABLE
+P_NOBUY	props/P_NOBUY
+P_NEVERDROP	props/P_NEVERDROP
+P_MAGIC	props/P_MAGIC
+P_WEIGHT_PERCENT	props/P_WEIGHT_PERCENT
+P_DETAILS	props/P_DETAILS
+P_SPECIAL_DETAILS	props/P_SPECIAL_DETAILS
+P_READ_DETAILS	props/P_READ_DETAILS
+P_EXITS	props/P_EXITS
+P_SPECIAL_EXITS	props/P_SPECIAL_EXITS
+P_DOORS	props/P_DOORS
+P_DOORS2	props/P_DOORS2
+P_ITEMS	props/P_ITEMS
+P_NO_TPORT	props/P_NO_TPORT
+P_TPORT_COST_IN	props/P_TPORT_COST_IN
+P_TPORT_COST_OUT	props/P_TPORT_COST_OUT
+P_INDOORS	props/P_INDOORS
+P_NOMAGIC	props/P_NOMAGIC
+P_ORAKEL	props/P_ORAKEL
+P_RACE	props/P_RACE
+P_TOTAL_WC	props/P_TOTAL_WC
+P_ZAP_MSG	props/P_ZAP_MSG
+P_AWAY	props/P_AWAY
+P_WEAPON	props/P_WEAPON
+P_ARMOURS	props/P_ARMOURS
+P_NPC	props/P_NPC
+P_WIMPY	props/P_WIMPY
+P_WIMPY_DIRECTION	props/P_WIMPY_DIRECTION
+P_HEAL	props/P_HEAL
+P_HB	props/P_HB
+P_POISON	props/P_POISON
+P_MAX_POISON	props/P_MAX_POISON
+P_DISABLE_ATTACK	props/P_DISABLE_ATTACK
+P_DIE_MSG	props/P_DIE_MSG
+P_KILLS	props/P_KILLS
+P_CALLED_FROM_IP	props/P_CALLED_FROM_IP
+P_DESCRIPTION	props/P_DESCRIPTION
+P_GUILD	props/P_GUILD
+P_LEVEL	props/P_LEVEL
+P_CAP_NAME	props/P_CAP_NAME
+P_EARMUFFS	props/P_EARMUFFS
+P_MARRIED	props/P_MARRIED
+P_AMOUNT	props/P_AMOUNT
+P_VALUE_PER_UNIT	props/P_VALUE_PER_UNIT
+P_WEIGHT_PER_UNIT	props/P_WEIGHT_PER_UNIT
+P_FUEL	props/P_FUEL
+P_LIGHTDESC	props/P_LIGHTDESC
+P_DO_DESTRUCT	props/P_DO_DESTRUCT
+P_LIGHTED	props/P_LIGHTED
+P_CHATS	props/P_CHATS
+P_CHAT_CHANCE	props/P_CHAT_CHANCE
+P_ACHATS	props/P_ACHATS
+P_ACHAT_CHANCE	props/P_ACHAT_CHANCE
+P_BODY	props/P_BODY
+P_AGGRESSIVE	props/P_AGGRESSIVE
+P_NOCORPSE	props/P_NOCORPSE
+P_REJECT	props/P_REJECT
+P_RACE_DESCRIPTION	props/P_RACE_DESCRIPTION
+P_RACESTRING	props/P_RACESTRING
+P_CONTAINER	props/P_CONTAINER
+P_FW_UNDERSTAND	props/P_FW_UNDERSTAND
+P_TRAY	props/P_TRAY
+P_DEFAULT_INFO	props/P_DEFAULT_INFO
+P_LOG_INFO	props/P_LOG_INFO
+P_PURSUERS	props/P_PURSUERS
+P_HP_HOOKS	props/P_HP_HOOKS
+P_GIVEN_AMOUNT	props/P_GIVEN_AMOUNT
+P_GIVEN_OBJECT	props/P_GIVEN_OBJECT
+P_CURSED	props/P_CURSED
+P_KEEP_ON_SELL	props/P_KEEP_ON_SELL
+P_SPELLS	props/P_SPELLS
+P_SPELLRATE	props/P_SPELLRATE
+P_INFORMME	props/P_INFORMME
+P_WAITFOR	props/P_WAITFOR
+P_LOCALCMDS	props/P_LOCALCMDS
+P_CLOCKMSG	props/P_CLOCKMSG
+P_PARA	props/P_PARA
+P_SIZE	props/P_SIZE
+P_INT_SHORT	props/P_INT_SHORT
+P_INT_LONG	props/P_INT_LONG
+P_ROOM_MSG	props/P_ROOM_MSG
+P_FUNC_MSG	props/P_FUNC_MSG
+P_MSG_PROB	props/P_MSG_PROB
+P_HAUS_ERLAUBT	props/P_HAUS_ERLAUBT
+P_SENSITIVE_INVENTORY	props/P_SENSITIVE_INVENTORY
+P_SENSITIVE_INVENTORY_TRIGGER	props/P_SENSITIVE_INVENTORY_TRIGGER
+P_SENSITIVE_ATTACK	props/P_SENSITIVE_ATTACK
+P_CURRENTDIR	props/P_CURRENTDIR
+P_SHORT_CWD	props/P_SHORT_CWD
+P_SNOOPFLAGS	props/P_SNOOPFLAGS
+P_COMMANDS	props/P_COMMANDS
+P_NAME	props/P_NAME
+P_NAME_ADJ	props/P_NAME_ADJ
+P_SHORT	props/P_SHORT
+P_LONG	props/P_LONG
+P_IDS	props/P_IDS
+P_ADJECTIVES	props/P_ADJECTIVES
+P_SHOW_INV	props/P_SHOW_INV
+P_CLASS	props/P_CLASS
+P_NODROP	props/P_NODROP
+P_NOGET	props/P_NOGET
+P_SENSITIVE	props/P_SENSITIVE
+P_UID	props/P_UID
+P_EUID	props/P_EUID
+P_WEIGHT	props/P_WEIGHT
+P_TOTAL_WEIGHT	props/P_TOTAL_WEIGHT
+P_ENTERMSG	props/P_ENTERMSG
+P_LEAVEMSG	props/P_LEAVEMSG
+P_LEAVEFAIL	props/P_LEAVEFAIL
+P_ENTERFAIL	props/P_ENTERFAIL
+P_ARRIVEMSG	props/P_ARRIVEMSG
+P_DEPARTMSG	props/P_DEPARTMSG
+P_ENTERCMDS	props/P_ENTERCMDS
+P_LEAVECMDS	props/P_LEAVECMDS
+P_MAX_PASSENGERS	props/P_MAX_PASSENGERS
+P_STD_OBJECT	props/P_STD_OBJECT
+P_COMPILER_PATH	props/P_COMPILER_PATH
+P_NR_HANDS	props/P_NR_HANDS
+P_WC	props/P_WC
+P_WEAPON_TYPE	props/P_WEAPON_TYPE
+P_DAM_TYPE	props/P_DAM_TYPE
+P_WIELDED	props/P_WIELDED
+P_HIT_FUNC	props/P_HIT_FUNC
+P_WIELD_FUNC	props/P_WIELD_FUNC
+P_UNWIELD_FUNC	props/P_UNWIELD_FUNC
+P_HIDE_EXITS.res	props/P_HIDE_EXITS.res
+P_FRIEND	props/P_FRIEND
+P_DAM_DESC	props/P_DAM_DESC
+P_QUALITY	props/P_QUALITY
+P_ALCOHOL_DELAY	props/P_ALCOHOL_DELAY
+P_DRINK_DELAY	props/P_DRINK_DELAY
+P_FOOD_DELAY	props/P_FOOD_DELAY
+P_HP_DELAY	props/P_HP_DELAY
+P_SP_DELAY	props/P_SP_DELAY
+P_POISON_DELAY	props/P_POISON_DELAY
+P_MATERIAL	props/P_MATERIAL
+obj	obj
+doormaster	obj/doormaster
+REGELN	REGELN
+README	REGELN/README
+zweities	REGELN/zweities
+Scripte	REGELN/Scripte
+balance	REGELN/balance
+npcs	REGELN/npcs
+waffen	REGELN/waffen
+ruestungen	REGELN/ruestungen
+properties.h	properties.h
+g.abenteurer	g.abenteurer
+ausweichen	g.abenteurer/ausweichen
+feuerball	g.abenteurer/feuerball
+identifiziere	g.abenteurer/identifiziere
+kampfschrei	g.abenteurer/kampfschrei
+licht	g.abenteurer/licht
+pfeil	g.abenteurer/pfeil
+schaetz	g.abenteurer/schaetz
+schnell	g.abenteurer/schnell
+g.bierschuettler	g.bierschuettler
+abstufungen	g.bierschuettler/abstufungen
+alkoholgift	g.bierschuettler/alkoholgift
+beliefere	g.bierschuettler/beliefere
+beobachte	g.bierschuettler/beobachte
+beruhige	g.bierschuettler/beruhige
+bierschuerze	g.bierschuettler/bierschuerze
+blubber	g.bierschuettler/blubber
+erdbeben	g.bierschuettler/erdbeben
+fliesse	g.bierschuettler/fliesse
+floesse	g.bierschuettler/floesse
+freibier	g.bierschuettler/freibier
+haarwuchs	g.bierschuettler/haarwuchs
+hitzeschlag	g.bierschuettler/hitzeschlag
+licht	g.bierschuettler/licht
+massiere	g.bierschuettler/massiere
+nebel	g.bierschuettler/nebel
+nuechtern	g.bierschuettler/nuechtern
+party	g.bierschuettler/party
+rkaufe	g.bierschuettler/rkaufe
+sand	g.bierschuettler/sand
+schimmer	g.bierschuettler/schimmer
+schuettele	g.bierschuettler/schuettele
+schuettelstarre	g.bierschuettler/schuettelstarre
+zaubersprueche	g.bierschuettler/zaubersprueche
+g.karate	g.karate
+abwehr	g.karate/abwehr
+angriff	g.karate/angriff
+konzentration	g.karate/konzentration
+g.klerus	g.klerus
+beistand	g.klerus/beistand
+bete	g.klerus/bete
+blitz	g.klerus/blitz
+donner	g.klerus/donner
+elementarschild	g.klerus/elementarschild
+elementarsphaere	g.klerus/elementarsphaere
+entfluche	g.klerus/entfluche
+entfrosche	g.klerus/entfrosche
+entgifte	g.klerus/entgifte
+heile	g.klerus/heile
+heiligenschein	g.klerus/heiligenschein
+heiltrank	g.klerus/heiltrank
+identifiziere	g.klerus/identifiziere
+kuriere	g.klerus/kuriere
+laeutere	g.klerus/laeutere
+lebenskraft	g.klerus/lebenskraft
+leuchten	g.klerus/leuchten
+messerkreis	g.klerus/messerkreis
+regeneriere	g.klerus/regeneriere
+schaetz	g.klerus/schaetz
+schutzhand	g.klerus/schutzhand
+segne	g.klerus/segne
+sonnenschutz	g.klerus/sonnenschutz
+spaltung	g.klerus/spaltung
+traue	g.klerus/traue
+weihe	g.klerus/weihe
+wunder	g.klerus/wunder
+g.chaos	g.chaos
+g.zauberer	g.zauberer
+old.applied	old.applied
+applied	old.applied/applied
+remove	old.applied/remove
+logon	old.applied/logon
+modify_command	old.applied/modify_command
+query_level	old.applied/query_level
+catch_tell	old.applied/catch_tell
+clean_up	old.applied/clean_up
+create	old.applied/create
+heart_beat	old.applied/heart_beat
+init	old.applied/init
+reset	old.applied/reset
+__INIT	old.applied/__INIT
+parse_command_id_list	old.applied/parse_command_id_list
+parse_command_adjectiv_id_list	old.applied/parse_command_adjectiv_id_list
+parse_command_plural_id_list	old.applied/parse_command_plural_id_list
+catch_msg	old.applied/catch_msg
+add_weight	old.applied/add_weight
+query_real_name	old.applied/query_real_name
+exit	old.applied/exit
+query_weight	old.applied/query_weight
+drop	old.applied/drop
+get	old.applied/get
+id.old	old.applied/id.old
+can_put_and_get	old.applied/can_put_and_get
+prevent_insert	old.applied/prevent_insert
+hilfe.magier	hilfe.magier
+hilfe.seher	hilfe.seher
+hilfe.spieler	hilfe.spieler
+mcmd	mcmd
+addmaster	mcmd/addmaster
+at	mcmd/at
+banish	mcmd/banish
+cat	mcmd/cat
+cd	mcmd/cd
+clone	mcmd/clone
+cp	mcmd/cp
+destruct	mcmd/destruct
+do	mcmd/do
+echoall	mcmd/echoall
+echoto	mcmd/echoto
+ed	mcmd/ed
+exec	mcmd/exec
+frieden	mcmd/frieden
+goto	mcmd/goto
+grep	mcmd/grep
+head	mcmd/head
+heile	mcmd/heile
+home	mcmd/home
+in	mcmd/in
+invis	mcmd/invis
+load	mcmd/load
+localcmd	mcmd/localcmd
+ls	mcmd/ls
+man	mcmd/man
+mbanish	mcmd/mbanish
+mecho	mcmd/mecho
+mkdir	mcmd/mkdir
+more	mcmd/more
+mschau	mcmd/mschau
+mv	mcmd/mv
+oropax	mcmd/oropax
+people	mcmd/people
+ping	mcmd/ping
+prompt	mcmd/prompt
+protect	mcmd/protect
+pwd	mcmd/pwd
+pwho	mcmd/pwho
+removemaster	mcmd/removemaster
+rm	mcmd/rm
+rmdir	mcmd/rmdir
+sallow	mcmd/sallow
+set	mcmd/set
+setcmsg	mcmd/setcmsg
+setdmsg	mcmd/setdmsg
+showpresay	mcmd/showpresay
+shutdown	mcmd/shutdown
+snoop	mcmd/snoop
+tail	mcmd/tail
+traenke	mcmd/traenke
+trans	mcmd/trans
+udpq	mcmd/udpq
+upd	mcmd/upd
+update	mcmd/update
+verfolge	mcmd/verfolge
+vis	mcmd/vis
+zap	mcmd/zap
+zwinge	mcmd/zwinge
+pcmd	pcmd
+adverb	pcmd/adverb
+alias	pcmd/alias
+antworte	pcmd/antworte
+ausgaenge	pcmd/ausgaenge
+behalte	pcmd/behalte
+ebenen	pcmd/ebenen
+email	pcmd/email
+emote	pcmd/emote
+ende	pcmd/ende
+entgifte	pcmd/entgifte
+ersetzungsanzeige	pcmd/ersetzungsanzeige
+erwarte	pcmd/erwarte
+erwidere	pcmd/erwidere
+fehler	pcmd/fehler
+finger	pcmd/finger
+fluestere	pcmd/fluestere
+frage	pcmd/frage
+gespraech	pcmd/gespraech
+gib	pcmd/gib
+hilfe	pcmd/hilfe
+history	pcmd/history
+hole	pcmd/hole
+idee	pcmd/idee
+ignoriere	pcmd/ignoriere
+info	pcmd/info
+inform	pcmd/inform
+inventur	pcmd/inventur
+kkwer	pcmd/kkwer
+klettere	pcmd/klettere
+kobold	pcmd/kobold
+kurz	pcmd/kurz
+kwer	pcmd/kwer
+lang	pcmd/lang
+mrufe	pcmd/mrufe
+muds	pcmd/muds
+nimm	pcmd/nimm
+passwort	pcmd/passwort
+rknuddle	pcmd/rknuddle
+rufe	pcmd/rufe
+rwinke	pcmd/rwinke
+sage	pcmd/sage
+schau	pcmd/schau
+schlafe	pcmd/schlafe
+selbstloeschung	pcmd/selbstloeschung
+speichern	pcmd/speichern
+spielpause	pcmd/spielpause
+stecke	pcmd/stecke
+stop	pcmd/stop
+stty	pcmd/stty
+teile	pcmd/teile
+toete	pcmd/toete
+trage	pcmd/trage
+typo	pcmd/typo
+uhrmeldung	pcmd/uhrmeldung
+ultrakurz	pcmd/ultrakurz
+unalias	pcmd/unalias
+url	pcmd/url
+verben	pcmd/verben
+vorsicht	pcmd/vorsicht
+weg	pcmd/weg
+wer	pcmd/wer
+wirf	pcmd/wirf
+zeilen	pcmd/zeilen
+zeit	pcmd/zeit
+ziehe	pcmd/ziehe
+zuecke	pcmd/zuecke
+entgifte.deaktiviert	pcmd/entgifte.deaktiviert
+README	README
+scmd	scmd
+aendere	scmd/aendere
+ausgang	scmd/ausgang
+befehl	scmd/befehl
+beschreibe	scmd/beschreibe
+echo	scmd/echo
+erlaube	scmd/erlaube
+extralook	scmd/extralook
+fehlermeldung	scmd/fehlermeldung
+fluchtrichtung	scmd/fluchtrichtung
+hausbau	scmd/hausbau
+instanthaus	scmd/instanthaus
+kopiere	scmd/kopiere
+licht	scmd/licht
+loesche	scmd/loesche
+meldungen	scmd/meldungen
+notiz	scmd/notiz
+presay	scmd/presay
+remote	scmd/remote
+review	scmd/review
+schiebe	scmd/schiebe
+seherhaus	scmd/seherhaus
+sethands	scmd/sethands
+setmin	scmd/setmin
+setmmin	scmd/setmmin
+setmmout	scmd/setmmout
+setmout	scmd/setmout
+sperre	scmd/sperre
+spion	scmd/spion
+titel	scmd/titel
+uebersicht	scmd/uebersicht
+verbiete	scmd/verbiete
+werfe	scmd/werfe
+wiz	wiz
+balance	wiz/balance
+forscherpunkte	wiz/forscherpunkte
+testspieler	wiz/testspieler
+zweitspieler	wiz/zweitspieler
+lupe	wiz/lupe
+knete	wiz/knete
+material	wiz/material
+materialliste	wiz/materialliste
+regionsmagier	wiz/regionsmagier
+materialgruppen	wiz/materialgruppen
+materialerkennung	wiz/materialerkennung
+materialdb	wiz/materialdb
diff --git a/doc/.propdesc b/doc/.propdesc
new file mode 100644
index 0000000..6360535
--- /dev/null
+++ b/doc/.propdesc
@@ -0,0 +1,1033 @@
+P_LIGHT
+// Greift auf den Lichtlevel zu. Handle with care !!!
+
+P_NAME
+// In dieser Property wird der Name des Objektes gespeichert. Er
+// sollte nur aus einem Wort bestehen. Der Name dient dazu, das
+// Objekt in einem Satz zu erwaehnen und wird auch dekliniert.
+
+P_NAME_ADJ
+// In dieser Property kann ein Adjektiv abgelegt werden, dass dann
+// bei der Ausgabe mittels name() mitbenutzt wird.
+// (In der Regel nicht noetig.)
+
+P_SHORT
+// In dieser Property wird die Kurzbeschreibung des Objektes
+// gespeichert, die beim Umschauen im Raum oder bei der Ausgabe
+// des Inventars ausgegeben wird. Fuer unsichtbare Objekte
+// sollte sie 0 sein.
+
+P_LONG
+// Unter dieser Property wird die Beschreibung gespeichert, die
+// bei der Untersuchung des Objektes ausgegeben wird.
+
+P_WEIGHT
+// Das Gewicht eines Objetes in Gramm.
+
+P_VALUE
+// Wert des Objektes in Goldmuenzen. Diesen Wert erhaelt man beim
+// Verkauf. Kaufen kostet ein Vielfaches hiervon.
+
+P_IDS
+// Hier werden die Bezeichnungen abgespeichert, unter denen sich das
+// Objekt angesprochen fuehlt.
+// Sollte nur mittels AddId( id ); gesetzt werden.
+
+P_ADJECTIVES
+// Hier werden Adjektive gespeichert, unter denen sich das Objekt
+// angesprochen fuehlt. So sind Kombinationen der Synonyme mit
+// mehreren Adjektiven moeglich. Ggf sollte auch der deklinierte
+// Fall des Adjektives eingegeben werden.
+// Sollte nur mittels AddAdjective( adjective ); gesetzt werden.
+
+P_INFO
+// Geheime Information, die ggf. ueber einen Zauberspruch
+// von Spielern ermittelt werden kann.
+
+P_READ_MSG
+// Hier koennen Informationen gespeichert werden, die beim Lesen
+// des Objektes ausgegeben werden.
+
+P_UID
+// Simulation des Zugriffs auf die uid.
+
+P_EUID
+// Simulation des Zugriffs auf die euid.
+
+P_AUTOLOADOBJ
+// Mit dieser Property werden Autoloadobjekte verwaltet.
+// Der Inhalt der Property sind die permanenten Eigenschaften des
+// Objektes, die der Spieler uebers ausloggen hinweg beibehaelt.
+// Beim Einloggen werden sie automatisch neu gesetzt. (ACHTUNG:
+// Die Property muss intern selbst verwaltet werden.)
+// Autoloadobjekte werden beim Ausloggen nicht fallengelassen!
+
+P_NOGET
+// Diese Property enthaelt eine Meldung, die ausgegeben wird, wenn
+// jemand versucht, dieses Objekt zu nehmen. Wird die Prop. auf einen
+// nicht-String-Wert gesetzt, so wird eine Defaultmeldung ausgegeben.
+
+P_NODROP
+// Diese Property enthaelt eine Meldung, die ausgegeben wird, wenn
+// jemand versucht, das Objekt fallen zu lassen. Wird die Prop.  auf einen
+// nicht-String-Wert gesetzt, so wird eine Defaultmeldung ausgegeben.
+
+P_NOBUY
+// Wenn diese Property gesetzt ist, wird das Objekt nach einem
+// Verkauf im Laden zerstoert, damit es nicht wieder von einem Spieler
+// gekauft werden kann.
+
+P_NEVERDROP
+// Objekte mit dieser Property werden beim Tod des Spielers nicht
+// in den Leichnam gelegt.
+// P_NODROP wird automatisch mitgesetzt.
+
+P_NR_HANDS
+// Anzahl der Haende, die man zur Benuztung des Objektes benoetigt.
+
+P_MAGIC
+// Dieses Objekt ist magisch.
+
+P_MAX_WEIGHT
+// Maximales Gewicht in Gramm, das in dem Container verstaut werden
+// kann.
+
+P_TOTAL_WEIGHT
+// Gewicht incl. Inhalt in Gramm. P_WEIGHT_PERCENT wird beruecksichtigt.
+
+P_TRANSPARENT
+// ist != 0, wenn hinein oder hinausgeschaut werden kann.
+
+P_CNT_STATUS
+// Status des Containers (offen, geschlossen, abgeschlossen)
+// siehe auch /sys/container.h
+
+P_WEIGHT_PERCENT
+// Diese Property gibt an, wieviel Prozent des Gewichts des Inhaltes
+// "nach aussen" wiedergegeben werden.
+
+P_INT_SHORT
+// Kurzbeschreibung, wenn man sich im Inneren des Containers
+// befindet.
+
+P_INT_LONG
+// Beschreibung, die man bekommt, wenn man sich in dem Container
+// umschaut.
+
+P_DETAILS
+// Diese Property enthaelt ein mapping, in der Objekte im Raum
+// definiert werden und Beschreibungen, die ausgegeben werden,
+// wenn man sich diese Details anschaut.
+
+P_SPECIAL_DETAILS
+// Mapping von Details, die beim Anschauen eine Funktion starten.
+
+P_READ_DETAILS
+// Details, die durch Lesen ermittelt werden koennen.
+
+P_ROOM_MSG
+// Liste mit Meldungen, die zufaellig im Raum ausgegeben werden.
+
+P_FUNC_MSG
+// Liste mit Funktionen, die zufaellig im Raum aufgerufen werden.
+
+P_MSG_PROB
+// Numerischer Wert fuer Wahrscheinlichkeit, mit der die Meldungen
+// und/oder die Funktionen ausgegeben/aufgerufen werden.
+
+P_EXITS
+// Mapping aller unmittelbar sichtbaren Ausgaenge mit zugehoerigen
+// Nachbarraeumen. Sollte nur mittels AddExit() benutzt werden.
+
+P_SPECIAL_EXITS
+// Dito, aber anstatt des Nachbarraums wird eine Funktion (im Raum)
+// angegebem, die bei Eingabe der Richtung ausgefuehrt wird.
+
+P_DOORS
+// *** OBSOLET! ***
+// Siehe P_DOOR_INFOS
+
+P_COMMANDS
+// Mapping von Kommandos, die im Objekt definiert sind.
+// Sollte nur mittels AddCmd() benutzt werden.
+
+P_ITEMS
+// Definition von Gegenstaenden, die in dem Raum liegen sollen.
+// Erklaerung in einem Extrafile.
+
+P_NO_TPORT
+// Kann folgende Werte annnehmen (definiert in moving.h):
+// NO_TPORT_IN	= Man kann nicht in den Raum hinein teleportieren.
+// NO_TPORT_OUT = Man kann nicht aus dem Raum hinaus teleportieren.
+// NO_TPORT	= Weder noch.
+
+P_INDOORS
+// Gesetzt, wenn von dem Raum aus der Himmel nicht sichtbar ist.
+// Dinge wie Wetter oder Mondaufgaenge werden in solchen Raeumen
+// nicht gezeigt.
+// Ausserdem
+
+P_NOMAGIC
+// Angabe in Prozent, mit welcher Wahrscheinlichkeit in einem
+// Raum nicht gezaubert werden kann. Bei NPC's zeigt es die
+// Resistenz gegen Magie an.
+// (Noch nicht implementiert)
+
+P_ORAKEL
+// Wenn diese Property gesetzt ist, kann der Wanderer in diesen
+// Raum hinein.
+
+P_ALIGN
+// Numerischer Wert fuer Gut- oder Boesheit des Wesens.
+
+P_RACE
+// String mit der Rasse des Wesens.
+
+P_GENDER
+// Grammatikalisches Geschlecht des Objektes:
+// (Definiert in language.h) MALE, FEMALE oder NEUTER
+
+P_ATTRIBUTES
+// Mapping mit den Attributen des Wesens.
+
+P_FOOD
+// Numerischer Wert fuer Saettigungsgrad des Wesens.
+
+P_DRINK
+// Numerischer Wert fuer Saettigung des Wesens mit Getraenken.
+
+P_ALCOHOL
+// Num. Wert fuer Besoffenheit.
+
+P_MAX_FOOD
+// Numerischer Wert fuer die maximale Saettigung des Wesens.
+
+P_MAX_DRINK
+// Numerischer Wert fuer die maximale 'Wassermenge' im Wesen.
+
+P_MAX_ALCOHOL
+// Numerischer Wert fuer die Alkoholvertraeglichkeit des Wesens.
+
+P_HP
+// Anzahl der Lebenspunkte des Wesens.
+
+P_SP
+// Anzahl der Magiepunkte des Wesens.
+
+P_MAX_HP
+// Maximale Anzahl der Lebenspunkte.
+
+P_MAX_SP
+// Maximale Anzahl der Magiepunkte.
+
+P_XP
+// Anzahl der Erfahrungspunkte.
+
+P_TOTAL_AC
+// Numerischer Wert der Abwehrstaerke des Wesens.
+
+P_TOTAL_WC
+// Numerischer Wert der Angriffsstaerke des Wesens.
+
+P_MSGIN
+// String mit der Meldung, die beim Verlassen eines Raumes mit M_GO
+// an die uebrigen Anwesenden ausgegeben wird.
+
+P_MSGOUT
+// String mit der Meldung, die beim Betreten eines Raumes mit M_GO
+// an die uebrigen Anwesenden ausgegeben wird.
+
+P_MMSGIN
+// String mit der Meldung, die beim Verlassen eines Raumes mit M_TPORT
+// an die uebrigen Anwesenden ausgegeben wird.
+
+P_MMSGOUT
+// String mit der Meldung, die beim Betreten eines Raumes mit M_TPORT
+// an die uebrigen Anwesenden ausgegeben wird.
+
+P_TITLE
+// Titel des Spielers. Erscheint hinter dem Namen in Kurz/Langbeschreibung.
+
+P_PRESAY
+// Presay des Spielers. Erscheint vor dem Namen in Kurz/Langbeschreibung.
+// Erscheint auch in name(), also in sag, ruf, teile mit usw.
+
+P_ZAP_MSG
+// Dreielementiges Array:
+// 1.) Meldung, die der Magier beim Zappen bekommt.
+// 2.) Meldung, die die Spieler im Raum beim Zappen bekommen.
+// 3.) Meldung, die das Opfer beim Zappen bekommt.
+// Mit @@wer@@, @@wessen@@, ... kann der Name des Opfers und mit
+// @@ich@@ der Name des Magiers in die Meldung eingewebt werden.
+
+P_TRANK_FINDEN
+// Wenn die Property auf 1 steht kann immer ein Zaubertrank gefunden
+// werden, auch wenn er nicht in der Liste des Spielers steht.
+
+P_AWAY
+// String der ausgegeben wird, wenn man weg ist und eine Mitteilung bekommt.
+
+P_IGNORE
+// Array der Spieler, deren Mitteilungen ignoriert werden.
+
+P_WEAPON
+// Momentan gezueckte Waffe.
+
+P_COMBATCMDS
+// Fuer den Kampf gebrauchbare Befehle spezieller Objekte (damit auch
+// Monster sie automatisch richtig anwenden koennen)
+// Der Inhalt von P_COMBATCMDS ist ein Mapping, der Key ist das Kommando,
+// um den Gegenstand zu benutzen (also z.B. "wirf flammenkugel"), und der
+// Value ein weiteres Mapping mit Zusatzinfos (definiert in /sys/combat.h).
+// Folgende Keys sind definiert:
+// - C_MIN, C_AVG, C_MAX:
+//   minimaler, mittlerer und maximaler Schaden, den das
+//   Objekt macht. Alle Angaben in LEBENSPUNKTEN, d.h. Defend-Einheiten/10.
+//   Bei einem Aufruf wie 'enemy->Defend(200+random(200), ...)' ist dann
+//   C_MIN=20, C_AVG=30, C_MAX=40.
+// - C_DTYPES:
+//   Array mit dem Schadenstyp oder den Schadenstypen. Beim Eisstab
+//   wuerde der Eintrag dann 'C_DTYPES:({DT_COLD})' lauten.
+// - C_HEAL:
+//   Sollte das Kampfobjekt ueber die Moeglichkeit verfuegen, den Anwender
+//   irgendwie zu heilen, so wird hier die Heilung in LP/MP eingetragen.
+//   Das funktioniert auch bei Objekten, die nur heilen, also sonst
+//   nichts mit Kampf zu tun haben.
+//   Im Lupinental z.B. gibt es Pfirsiche, die beim Essen 5LP heilen. Da
+//   kann man dann 'SetProp(P_COMBATCMDS, (["iss pfirsich":([C_HEAL:5])]))'
+//   eintragen.
+// Es sind auch mehrere Kommandos moeglich, z.B. bei Objekten, die sowohl
+// heilen als auch Kampfwirkung haben.
+
+P_ARMOURS
+// Liste der getragenen Schutzbekleidungen.
+
+P_HANDS
+// 3-elem. Array
+// 1. Elem.: String mit der Meldung, wenn ohne Waffen angegriffen wird.
+// 2. Elem.: Weaponclass, wenn ohne Waffen angegriffen wird.
+// 3. Elem.: Angriffs-typ, default ist DT_BLUDGEON
+
+P_RESISTANCE
+// Array mit Angriffsarten, gegen die das Lebewesen teilweise
+// resistent ist.
+
+P_VULNERABILITY
+// Array mit Angriffsarten, gegen die das Lebewesen empfindlich
+// ist.
+
+P_RESISTANCE_STRENGTHS
+// Mapping mit Schadensfaktoren minus 1.0 fuer jeden Schadenstyp
+// -0.5 entspricht also Resistance (Faktor 0.5)
+// 1.0 entspricht also Vulnerability (Faktor 2.0)
+
+P_MAX_HANDS
+// Anzahl der Haende, die ein Wesen hat.
+
+P_USED_HANDS
+// Anzahl der Haende in Benutztung
+
+P_ABILITIES
+// *** OBSOLET! ***
+// Siehe P_NEWSKILLS.
+
+P_ENEMY_DAMAGE
+// Gibt eine Kopie des Mappings zurueck, in dem vermerkt wird, wer
+// diesem Lebewesen welchen Schaden zugefuegt hat.
+
+P_NPC
+// Gesetzt bei Monstern.
+
+P_WIMPY
+// Numerischer Wert. Das Wesen flieht, wenn die Lebenspunkte
+// unter diesen Wert sinken.
+
+P_BRIEF
+// Ist gesetzt, wenn der Spieler nur die Kurzbeschreibung sehen will.
+
+P_HEAL
+// Numerischer Wert, der beim Verzehr dieses Objektes zu den
+// Lebenspunkten hinzugezaehlt wird. (kann auch negativ sein)
+// Der Wert sollte zwischen +4 und -4 liegen und bei leichten
+// Monstern 0 sein.
+
+P_DISABLE_ATTACK
+// Das Lebewesen kann nicht angreifen.
+
+P_DIE_MSG
+// String mit der Meldung, die ausgegeben wird, wenn das Wesen stirbt.
+// ist die Property nicht gesetzt, so nehme " faellt tot zu Boden.\n".
+
+P_KILLS
+// Anzahl der Spieler, die dieser Spieler schon getoetet hat.
+// Unerlaubte Manipulation ist ein SCHWERES VERGEHEN gegen
+// die Mudreglen.
+
+P_MAILADDR
+// EMailadresse des Spielers.
+
+P_CALLED_FROM_IP
+// Letzte IP-Adr, von der aus sich der Spieler eingeloggt hat.
+
+P_CURRENTDIR
+// Momentanes Verzeichnis in dem der Spieler ist. (nur fuer
+// Magier von Belang)
+
+P_AUTOLOAD
+// Mapping mit der Menge der Autoloadobjekte und den zugeh.
+// Properties.
+
+P_FROG
+// Gesetzt, wenn der Spieler ein Frosch ist.
+
+P_INVIS
+// Die Property P_INVIS dient dazu, Objekte (insbesondere Magier) als
+// unsichtbar zu kennzeichnen. Man sollte drei Arten von unsichtbaren
+// Objekten unterscheiden:
+// - Gegenstaende
+//   Gegenstaende macht man unsichtbar, indem man in ihnen die Property
+//   P_SHORT auf 0 setzt; will man ein Objekt unsichtbar machen, ohne
+//   seine Kurzbeschreibung zu aendern, kann man aber auch P_INVIS auf
+//   einen Wert ungleich 0 setzen.
+// - NPCs
+//   NPCs macht man ebenfalls unsichtbar, indem man in ihnen die Property
+//   P_SHORT auf 0 setzt. GGf. kann man auch noch die Property P_INVIS auf
+//   1 setzen.
+//   Der Unterschied: Bei gesetztem P_INVIS wird als Name 'Jemand' ver-
+//   wendet, ansonsten der normale Name des NPCs.
+// - Spieler / Magier
+//   Spieler und Magier macht man unsichtbar, indem man ihnen die Property
+//   P_INVIS auf einen Wert <>0 setzt.
+//   Spieler duerfen nicht unsichtbar gemacht werden!			 !
+//   Wird ein Magier unsichtbar gemacht, muss man ihm die Property	 !
+//   P_INVIS auf den Wert setzen, den die Property P_AGE zu diesem	 !
+//   Zeitpunkt hat (keine F_QUERY_METHOD !).				 !
+//   Setzt man die Property auf den Wert 1, so erhaelt ein Spieler,
+//   wenn er den entsp. Magier fingert, die Ausgabe: Alter: 00:00:02,
+//   was genauso verraeterisch ist, wie ein Alter, dass bei einem
+//   scheinbar nicht eingeloggten Magier immer weiter hochgezaehlt
+//   wird.
+
+P_GHOST
+// Gesetzt, wenn der Spieler tot ist.
+
+P_EXTRA_LOOK
+// String, der einen zusaetzlichen Text in der long()-Beschreibung
+// eines Spielers erzeugt.
+
+P_GUILD
+// Gilde, der der Spieler angehoert.
+// Der Name der Gilde ist hier als String definiert. Bei Spielern
+// ist der Wert dieser Property niemals 0 (Defaultmaessig gehoert
+// ein Spieler der Abenteurergilde an).
+
+P_LEVEL
+// Spieler-Level (!= Magierlevel)
+
+P_QUESTS
+// Liste der geloesten Quests.
+
+P_CAP_NAME
+// Name des Spielers, der dekliniert und ausgegen wird.
+// NOT YET IMPLEMENTED.
+
+P_TTY
+// Name der Terminalemulation, die der Spieler nutzt.
+// NOT YET IMPLEMENTED.
+
+P_SHOW_EXITS
+// Gesetzt, wenn der Spieler die offensichtlichen Ausgaenge
+// immer automatisch sehen will.
+
+P_CAN_EMOTE
+// Gesetzt, wenn der Spieler 'emoten' kann.
+
+P_EARMUFFS
+// Shouts von Magiern mit Level < earmuffs werden abgeblockt
+// (Nur fuer Magier)
+
+P_WANTS_TO_LEARN
+// Gesetzt, wenn der Magier die Filenamen sehen will.
+// (Nur fuer Magier). Wird diese Property auf 0 gesetzt, gehen auch
+// einige andere Dinge nicht mehr - verfolge zB. Eigentlich sollten
+// dann auch die Magierbefehle wie "goto" usw unterbunden werden -
+// das kommt vielleicht noch.
+
+P_TESTPLAYER
+// Gesetzt, wenn der Spieler nicht in der Bestenliste auftauchen soll.
+
+P_AGE
+// Alter des Spielers in Heart-Beats (1 HB == 2 Sekunden)
+
+P_BLIND
+// TRUE, wenn der Spieler nichts sehen kann.
+
+P_MARRIED
+// Enthaelt einen String mit der uid des Partners
+// (sofern vorhanden)
+
+P_WC
+// Numerischer Wert fuer die Staerke der Waffe.
+
+P_DAM_TYPE
+// String mit der Art der Verletzung.
+
+P_WEAPON_TYPE
+// Art der Waffe
+
+P_WIELDED
+// Flag ob die Waffe gezueckt ist.
+
+P_AC
+// Numerischer Wert fuer die Abwehrstaerke der Ruestung.
+
+P_ARMOUR_TYPE
+// String fuer Art der Ruestung; welcher Koerperteil wird
+// geschuetzt?
+
+P_WORN
+// Flag, ob die Ruestung im Moment getragen wird. Falls ja,
+// enthaelt die Property das Traegerobjekt.
+
+P_AMOUNT
+// Anzahl der Objekte, fuer die das Objekt steht.
+
+P_VALUE_PER_UNIT
+// Wert in Goldstuecken pro Untereinheit.
+
+P_WEIGHT_PER_UNIT
+// Gewicht in Gramm pro Untereinheit.
+
+P_FUEL
+// Numerischer Wert fuer die Zeitdauer, die die Lichtquelle noch
+// leuchten kann.
+
+P_LIGHTDESC
+// String mit der Bezeichnung des Leuchtvorgangs, den die Licht-
+// quelle ausfuehrt (leuchtend, brennend, gluehend ...)
+
+P_DO_DESTRUCT
+// Flag, ob sich die Lichtquelle am Ende der Leuchtzeit selbst
+// zerstoert. (Oder sogar mit String fuer die Meldung, die bei
+// der Zerstoerung angegeben wird?)
+
+P_LIGHTED
+// Flag, ob die Lichtquelle in Betrieb ist.
+
+P_CHATS
+// Alist mit Strings, die das Monster zufaellig ausgibt.
+
+P_CHAT_CHANCE
+// Wahrscheinlichkeit, mit der die Chats ausgegeben werden.
+
+P_ACHATS
+// Chats, die das Monster im Kampf ausgibt.
+
+P_ACHAT_CHANCE
+// Wahrscheinlichkeit fuer die Attack-Chat-Ausgabe.
+
+P_BODY
+// Numerischer Wert fuer die Abwehrstaerke des blanken Koerpers
+// des Wesens.
+
+P_HB
+// Diese Property wird gesetzt, wenn das Monster immer einen
+// heart_beat haben soll. (VORSICHT, nur wenn es WIRKLICH sein muss!)
+
+P_AGGRESSIVE
+// Gesetzt, wenn das Wesen von sich aus Angriffe startet.
+
+P_NOCORPSE
+// Gesetzt, wenn im Todesfall kein Leichnam erzeugt werden soll.
+
+P_REJECT
+// Ein Array aus 2 Elementen, das bestimmt, das der npc mit Dingen
+// tuen soll, die ihm gegeben werden.
+// Default, der npc behaelt die Dinge einfach.
+// Wenn gesetzt:
+// 1.Element: Art der Handlung. (aus <moving.h>)
+//	REJECT_GIVE: Der NPC gibt das Objekt zurueck.
+//	REJECT_DROP: Der NPC laesst das Objekt fallen.
+//	REJECT_KEEP: Der NPC behaelt das Objekt doch.
+// 2.Arrayelement: Meldung, mit der der NPC die Handlung kommentiert.
+
+P_ENTERMSG
+// Array mit zwei Meldungen, eine fuer den Raum, den der Spieler
+// verlaesst, und eine fuer den Transporter, in den er geht.
+
+P_LEAVEMSG
+// Array mit zwei Meldungen, eine fuer den Transporter, den er verlaesst,
+// und eine fuer den Raum in den er kommt.
+
+P_LEAVEFAIL
+// Meldung an ein Wesen, wenn es ausserhalb der Anlegezeiten den Transporter
+// verlassen will. Ist die Prop. ein Array, so wird das erste Element als
+// meldung an das Wesen, das zweite als Meldung an die Mitspieler im
+// Transporter geschickt.
+
+P_ENTERFAIL
+// Meldung an ein Wesen, wenn den vollen Transporter betreten will.
+// Ist die Prop. ein Array, so wird das erste Element als
+// meldung an das Wesen, das zweite als Meldung an die Mitspieler im
+// Raum geschickt.
+
+P_ARRIVEMSG
+// Meldung, wenn der Transporter anlegt.
+
+P_DEPARTMSG
+// Meldung, mit der ein Transporter ablegt.
+
+P_ENTERCMDS
+// Befehlsliste, die zum Betreten des Transporters fuehrt.
+
+P_LEAVECMDS
+// Befehlsliste, die zum Verlassen des Transporters fuehrt.
+
+P_MAX_PASSENGERS
+// Numerischer Wert fuer die maximale Anzahl von Wesen in dem Transporter.
+// 0 bedeutet unbeschaenkte Spielerzahl.
+
+P_ARTICLE
+// Gibt an, ob in der Beschreibung ein Artikel ausgegeben werden soll
+// oder nicht.
+
+P_LAST_COMMAND_ENV
+// Der Raum, in dem das letzte Kommando eingegeben wurde.
+
+P_NETDEAD_INFO
+// Hier kann ein Raum Informationen in einem Spieler zwischenspeichern, wenn
+// dieser in den Netztotenraum gemoved wird.
+
+P_FOLLOW_SILENT
+// Wenn diese Property 1 ist, wird der MOVE vom verfolge Silent ausge-
+// fuehrt.
+
+P_START_HOME
+// Raum, in dem der Spieler nach dem Einloggen landen soll
+
+P_CARRIED_VALUE
+// Entschaedigung, die der Spieler beim Einloggen erhaelt.
+
+P_PROMPT
+// Das Prompt (Nur fuer Magier).
+
+P_CANECHO
+// Zeigt an, ob der Seher "echo" benutzen kann oder nicht.
+
+P_POTIONROOMS
+// Alist mit den Nummern der Raeume, in denen der Spieler noch Zauber-
+// traenke finden kann.
+
+P_QP
+// Anzahl der Questpunkte, die ein Spieler hat.
+
+P_ENEMY_DEATH_SEQUENCE
+// Wenn man jemanden toetet, wird dieses in seine Todessequenz eingefuegt.
+
+P_CLONER
+// Enthaelt einen String mit dem Namen desjenigen, der das Objekt gecloned hat.
+
+P_RACE_DESCRIPTION
+// Beschreibung der Vor/Nachteile einer Rasse.
+
+P_DOOR_INFOS
+// Hat was mit den Tueren zu tun -> Muss Rochus mal dokumentieren
+
+P_ATTRIBUTES_OFFSETS
+// Mapping mit Offsets, die zu den Attributen addiert werden (koennen auch
+// negativ sein) - zB Rassenboni.
+
+P_DEADS
+// Anzahl der Tode des Spielers seit Einfuehrung dieser Property (irgendwann
+// im Dezember 94)
+
+P_LAST_LOGIN
+// Zeitpunkt des letzen Logins
+
+P_LAST_LOGOUT
+// Zeitpunkt des letzen Logouts
+
+P_SECOND
+// Spieler ist Zweitiea
+
+P_SCREENSIZE
+// Bildschirmgroesse in Zeilen (fuer More)
+
+P_FLAGS
+// Flags des Spielers
+
+P_CAN_FLAGS
+// Flags, ob Spieler echoen/emoten kann
+
+P_NEEDED_QP
+// APs, die man fuer den Seherstatus braucht
+
+P_INTERMUD
+// Obsolet?
+
+P_BUFFER
+// Enthaelt Kobold-Nachrichten
+
+P_CLASS
+// Enthaelt die Klasse (definiert in class.h), zu der ein Objekt gehoert.
+// Sollte nur mit AddClass() gesetzt werden.
+// Das setzen ist nur noetig, wenn die Klasse nicht schon aus den Ids
+// oder (bei Lebewesen) der Rasse ersichtlich ist.
+
+P_CONTENTS
+// *** OBSOLET! ***
+
+P_KILLER
+// ???
+
+P_MURDER_MSG
+// Welche Meldung soll auf dem Moerder-Kanal erscheinen?
+
+P_KILL_MSG
+// Meldung auf dem Todeskanal, wenn jemand von uns geotetet wird
+
+P_CORPSE
+// Welchen Corpse hinterlassen wir?
+
+P_WIMPY_DIRECTION
+// Fluchtrichtung
+
+P_POISON
+// Wie stark wir vergiftet sind (0-11)
+
+P_RACE_DESCRIPTION
+// Rassenbeschreibung, die man bei der Wahl der Rasse abrufen kann
+
+P_DEFAULT_INFO
+// NPC-Info-System: Default-Antwort auf dumme Fragen
+
+P_PURSUERS
+// Enthaelt Verfolger - nicht von Hand manipulieren!
+
+P_HP_HOOKS
+// Welche Objekte sollen bei HP-Aenderungen benachrichtigt werden?
+
+P_CURSED
+// Verfluchte Waffen/Ruestungen kann man nicht ausziehen/wegstecken
+
+P_SPELLS
+// NPC-Spells
+
+P_SPELLRATE
+// NPC-Spellrate (in %)
+
+P_INFORMME
+// Informieren ueber Spieler, die ins Mud kommen/aus dem Mud gehen?
+
+P_WAITFOR
+// Die Erwarte-Liste
+
+P_HAUS_ERLAUBT
+// Hier darf gebaut werden
+
+P_SHORT_CWD
+// .readme bei cd ausgeben oder nicht
+
+P_PARA
+// Nummer der Parallelwelt in der ein Spieler ist.
+
+P_LOG_INFO
+// Wenn diese Property gesetzt ist wird jede Frage, die ein
+// Monster nicht beantworten kann, im Report-File des
+// zustaendigen Magiers geloggt.
+
+P_COMBATCMDS
+// Befehle, die man mit Spezialwaffen ausfuehren kann und genauere
+// Informationen hierzu.
+
+P_DEAF
+// Der Spieler ist taub. Falls hier ein String steht, wird dieser bei
+// "teile ... mit" an den Mitteilenden ausgegeben, ansonsten kommt nur
+// "Soundso ist leider gerade taub.\n"
+
+P_ATTACK_BUSY
+// Kann der Spieler noch Spezialwaffen (zB Flammenkugel) einsetzen?
+
+P_PREFERED_ENEMY
+// Array: 1. Element: Wahrscheinlichkeit, dass einer der bevorzugten
+// Feinde (restliche Elemente) genommen wird.
+
+P_HISTMIN
+// Minimale Laenge, die eine Zeile haben muss, um in die History zu kommen
+
+P_SHOW_ALIAS_PROCESSING
+// Arbeit des Parsers beobachten (debugging)
+
+P_DEFAULT_NOTIFY_FAIL
+// Welche Fehlermeldung kommt, wenn kein Objekt ein notify_fail macht?
+
+P_IP_NAME
+// Rechnername des Interactives
+
+P_AUTH_INFO
+// Username des Spielers, wenn bei ihm ein AUTHD laeuft
+
+P_LAST_KILLER
+// Letzter Moerdes des Wesens
+
+P_CLONE_MSG
+// Meldung, die beim Clonen eines Obj ausgegegen wird (nur Magier)
+
+P_CMSG
+// *** OBSOLET! *** Siehe P_CLONE_MSG
+
+P_DESTRUCT_MSG
+// Meldung, die beim Destructen Obj ausgegegen wird (nur Magier)
+
+P_DMSG
+// *** OBSOLET! *** Siehe P_DESTRUCT_MSG
+
+P_READ_NEWS
+// Welche Artikel bereits gelesen wurde (frueher: in der MPA)
+
+P_LAST_CONTENT_CHANGE
+// Wann wurde zum letzten Mal was ins Obj gestopft oder rausgenommen?
+// Wichtig fuer den Weight-Cache
+
+P_MAX_POISON
+// Maximale Vergiftung
+
+P_DESCRIPTION
+// Beschreibung des Spielers
+
+P_KEEP_ON_SELL
+// Bei "verkaufe alles" wird das Objekt behalten.
+
+P_DESCRIPTION
+// Beschreibung des Spielers
+
+P_KEEP_ON_SELL
+// Objekt soll bei verkaufe alles behalten werden
+
+P_CLOCKMSG
+// Die Meldung wird zur vollen Stunde ausgegeben
+
+P_SIZE
+// Groesse des Lebewesens (in cm)
+
+P_AVERAGE_SIZE
+// Durchschnittliche Groesse eines Wesens dieser Rasse (derzeit nur Player)
+
+P_PERM_STRING
+// Fuer Sprachflueche. In dem Objekt, das in P_PERM_STRING vermerkt ist,
+// wird bei Sprachbefehlen permutate_string(str) aufgerufen und der
+// zurueckgegebene String statt dessen ausgegeben.
+
+P_RACESTRING
+// Gibt eine dem Geschlecht angepasste Beschreibung der Rasse zurueck
+// ("Zwerg" oder "Zwergin" etc.)
+
+P_WATER
+// Gewaessertyp. Erlaetuerungen in fishing.h
+
+P_FISH
+// Fischdichte. Erlaeuterungen in fishing.h
+
+P_SCREENSIZE
+// Zahl der Zeilen (wie mit dem "zeilen"-Befehl eingestellt).
+// Nur im Spielerobjekt sinnvoll ;)
+
+P_SKILLS
+// *** OBSOLET! ***
+// Siehe P_NEWSKILLS.
+
+P_VALID_GUILDS
+// Enthaelt die zugelassenen Gilden und ist nur fuer den Gildenmaster
+// von Bedeutung.
+
+P_GUILD_SKILLS
+// Gildenproperty
+// Enthaelt ein Mapping mit allen Spruechen und Faehigkeiten der Gilde.
+// Wird mit AddSkill()/AddSpell() modifiziert.
+
+P_GUILD_RESTRICTIONS
+// Gildenproperty
+// Enthaelt ein Mapping mit den Eintrittbeschraenkungen einer Gilde.
+
+P_GUILD_DEFAULT_SPELLBOOK
+// Gildenproperty
+// Der Name des Spellbooks, das von der Gilde standardmaessig verwendet
+// wird (kann bei Spells mit SI_SPELLBOOK ueberschrieben werden).
+
+P_GUILD_MALE_TITLES
+// Gildenproperty
+// Ein Mapping mit den Stufenbezeichnungen fuer maennliche Gildenmitglieder.
+// Als Key dient der Gildenlevel.
+
+P_GUILD_FEMALE_TITLES
+// Gildenproperty
+// Ein Mapping mit den Stufenbezeichnungen fuer weibliche Gildenmitglieder.
+// Als Key dient der Gildenlevel.
+
+P_GUILD_LEVELS
+// Gildenproperty
+// Ein Mapping mit den Stufenbeschraenkungen fuer die Gildenlevel.
+// Als Key dient der jeweilige Gildenlevel.
+
+P_SB_SPELLS
+// Spellbookproperty
+// Hier sind saemtliche Spells des Spellbooks vermerkt (wird mit AddSpell()
+// veraendert).
+
+P_GLOBAL_SKILLPROPS
+// Gilden- und Spellbookproperty
+// Eigenschaften, die ALLE Spells eines Spellbooks bzw. ALLE Skills und
+// Spells einer Gilde haben sollen.
+
+P_GUILD_LEVEL
+// Gildenlevel des Spielers (nicht unbedingt identisch mit dem Spielerlevel)
+
+P_GUILD_TITLE
+// Gildentitel, der dem Gildenlevel des Spielers entspricht. Wird nur
+// angezeigt, wenn P_TITLE=0 ist.
+
+P_GUILD_RATING
+// Einstufung des Spielers in der Gilde (zwischen 0 und 10000).
+// Wie sich die Einstufung zusammensetzt, ist Sache der jeweiligen Gilde.
+
+P_NEWSKILLS
+// Hier sind saemtliche Skills und Spells vermerkt, die der Spieler kennt.
+
+P_NEXT_SPELL_TIME
+// Wann kann das naechste Mal gezaubert werden?
+
+P_HIT_FUNC
+// Enthaelt das Objekt, das eine HitFunc() definiert
+// (fuer Waffen)
+
+P_WIELD_FUNC
+// Enthaelt das Objekt, das eine WieldFunc() definiert
+// (fuer Waffen)
+
+P_UNWIELD_FUNC
+// Enthaelt das Objekt, das eine UnwieldFunc() definiert
+// (fuer Waffen)
+
+P_DEFEND_FUNC
+// Enthaelt das Objekt, das eine DefendFunc() definiert
+// (fuer Ruestungen)
+
+P_WEAR_FUNC
+// Enthaelt das Objekt, das eine WearFunc() definiert
+// (fuer Ruestungen)
+
+P_REMOVE_FUNC
+// Enthaelt das Objekt, das eine RemoveFunc() definiert
+// (fuer Ruestungen)
+
+P_LEP
+// Levelpunkte eines Spielers
+// NICHT VON HAND SETZEN!!!
+
+P_LEP_MALUS
+// Malus auf die Levelpunkte
+// NICHT VON HAND SETZEN!!!
+
+P_ORIG_NAME
+// In einer Leiche der Name des Gestorbenen. (name(RAW))
+
+P_NO_GLOBAL_ATTACK
+// Falls diese Propertie in einem NPC gesetzt ist, wird dieser bei einem
+// "toete alle" nicht mit angegriffen
+// (das ist zB. bei BegleitNPCs ganz nuetzlich)
+
+P_DAMAGED
+// Grad der Beschaedigung einer Waffe/Ruestung.
+// Die Propertie sollte so gesetzt sein, dass P_DAMAGED + aktuelles P_WC/AC
+// die urspruengliche P_WC/AC ergibt
+
+P_EFFECTIVE_WC
+// Die durchschnittliche Wirkung einer Waffe, basierend auf P_WC und
+// einer ggf. vorhandenen HitFunc().
+
+P_EFFECTIVE_AC
+// Die durchschnittliche Wirkung einer Ruestung, basierend auf P_AC
+// und einer ggf. vorhandenen DefendFunc().
+
+P_HOMEPAGE
+// Die Homepage eines Spielers (mit dem Befehl 'url' zu setzen).
+
+P_TPORT_COST_IN
+// In einem Raum mit Sehertor: Kostenanteil, um sich in den Raum zu
+// teleportieren
+
+P_TPORT_COST_OUT
+// In einem Raum mit Sehertor: Kostenanteil, sich aus dem Raum heraus
+// zu teleportieren
+
+P_HIDE_EXITS
+// Ist diese Property in einem Raum auf einen Wert ungleich 0, gesetzt,
+// werden einem Spieler fuer diesen Raum keine Ausgaenge angezeigt.
+
+P_TMP_ATTACK_HOOK
+// Mindestens 3-elementiges Array ({zeitpunkt, objekt, funktion, ...}).
+// Die Funktion wird im 'objekt' mit dem Feind des Lebewesens als Parameter
+// zu Beginn von Attack() (des Lebewesens) aufgerufen, wenn der 'zeitpunkt'
+// noch nicht ueberschritten ist. Wenn die Funktion 0 zurueckgibt, wird
+// Attack() abgebrochen. Ueber optionale Arrayelemente koennen der Funktion
+// weitere Parameter uebergeben werden.
+
+P_TMP_DEFEND_HOOK
+// Mindestens 3-elementiges Array ({zeitpunkt, objekt, funktion, ...}).
+// Die Funktion wird im 'objekt' mit den gleichen Parametern wie Defend()
+// zu Beginn von Defend() (des Lebewesens) aufgerufen, wenn der 'zeitpunkt'
+// noch nicht ueberschritten ist. Wenn die Funktion 0 zurueckgibt, wird
+// Defend() abgebrochen, ansonsten wird als Rueckgabe ein 3-elementiges
+// Array ({schaden, schadenstypen, spell}) erwartet, die anstelle der
+// Defend() uebergebenen Werte verwendet werden. Ueber optionale Array-
+// elemente koennen der Funktion weitere Parameter uebergeben werden.
+
+P_TMP_DIE_HOOK
+// Mindestens 3-elementiges Array ({zeitpunkt, objekt, funktion, ...}).
+// Die Funktion wird im 'objekt' mit dem gleichen Parameter wie die()
+// zu Beginn von die() (des Lebewesens) aufgerufen, wenn der 'zeitpunkt'
+// noch nicht ueberschritten ist. Wenn die Funktion 1 zurueckgibt, wird
+// die() abgebrochen. Ueber optionale Arrayelemente koennen der Funktion
+// weitere Parameter uebergeben werden.
+
+P_DAM_DESC
+// Beschreibung beschaedigter Waffen/Ruestungen.
+
+P_LAST_DAMTYPES
+// Schadenstypen, mit denen das letzte do_damage() von Defend() aus
+// ausgeloest wurde (kann von Leichen abgefragt werden).
+// Entweder ein Array mit den Schadenstypen oder 0.
+
+P_TEAM
+// Teamobjekt, falls Spieler in einem Team ist.
+
+P_TEAM_NEWMEMBER
+// Spieler moechte ins Team von diesem Objekt aufgenommen werden.
+
+P_TEAM_ATTACK_CMD
+// Angriffsbefehl des Spielers, nicht setzbar.
+
+P_TEAM_AUTOFOLLOW
+// Folgewunsch des Spielers, nicht setzbar.
+
+P_TEAM_WANTED_ROW
+// Gewuenschte Reihe des Spielers (von 1 bis MAX_TEAMROWS)
+
+P_TEAM_WIMPY_ROW
+// Fluchtreihe des Spielers (von 1 bis MAX_TEAMROWS)
+// Wenn die Fluchtreihe <=1 ist ist die Flucht in eine hintere Reihe
+// deaktiviert.
+
+P_TEAM_LEADER
+// Teamobjekt, falls Spieler Anfuehrer eines Teams ist.
+
+P_TEAM_ASSOC_MEMBERS
+// Array mit den zugeordneten NPCs des Spielers bzw.
+// der Spieler, dem dieser NPC zugeordnet ist.
+// Zugeordnete NPCs sind automatisch im Team des Spielers.
+// Der Zugriff auf diese Property sollte ueber AssocMember()
+// bzw. DeAssocMember() erfolgen.
+
+P_TEAM_COLORS
+// Grenzwerte fuer farbige Anzeige im Teaminfo.
+// Array mit 4 Werten ({ lp_rot, lp_gelb, sp_rot, sp_gelb })
diff --git a/doc/.readme b/doc/.readme
new file mode 100644
index 0000000..19d3abf
--- /dev/null
+++ b/doc/.readme
@@ -0,0 +1,25 @@
+Dokumentation von Befehlen und Themen innerhalb des Spiels:
+ * help/  - allgemeine Themen, die ALLE betreffen
+ * pcmd/  - die Befehle, ueber die man schon als einfacher Spieler verfuegt
+ * scmd/  - Seherbefehle und Hilfen zum Hausbau
+ * mcmd/  - hier sind die Magierbefehle beschrieben
+ * wiz/   - allgemeine Themen fuer Magier
+ * g.XYZ/ - Hilfeseiten, die zur Gilde XYZ gehoeren
+
+Uebersicht ueber Objekte und Funktionen MudLib
+ * efun/   - Funktionen, die der GameDriver zur Verfuegung stellt
+ * lfun/   - Funktionen, die die Objekte der MudLib zur Verfuegung stellen
+ * master/ - Funktionen, die das Masterobject zur Verfuegung stellt
+
+ * std/    - Die Objekte des Basis-MudLib
+ * obj/    - Einige hilfreiche Zusatzobjekte
+
+ * props/  - Die Properties
+
+ Allgemeines
+ * beispiele/ - einige hilfreiche Beispiele fuer den Start ins Magierleben
+
+Bei Aenderungen bitte Gloinson Bescheid geben oder eine Mail an
+Doku@MorgenGrauen schicken. Eine genauere Uebersicht ueber das
+Hilfsverzeichnis findest Du in der Datei /doc/README.
+
diff --git a/doc/.synonym b/doc/.synonym
new file mode 100644
index 0000000..32d3e7d
--- /dev/null
+++ b/doc/.synonym
@@ -0,0 +1 @@
+lpc LPC
diff --git a/doc/KURS/COMMENTS b/doc/KURS/COMMENTS
new file mode 100644
index 0000000..4ef9ca9
--- /dev/null
+++ b/doc/KURS/COMMENTS
@@ -0,0 +1,75 @@
+	BEMERKUNGEN ZUM THEMA SPIELVERDERBER
+	------------------------------------
+
+	Eine konsequente Verwendung der Worte Moerder, Verbrecher,
+	o.ae. verkennt den wahren Charakter:
+	Es sind einfach nur Spielverderber, und sollten auch 
+	dementsprechend behandelt werden.
+
+	Im Moment wird noch nicht gespielt.
+	Ergo gibt es noch keine Spielverderber.
+
+	Wenn es denn welche gibt, wie geht man mit ihnen um ?
+	-	Man spielt nicht mehr mit Ihnen.
+	-	Man nimmt ihnen den Spass, sodass sie von selber
+		aufhoeren mitzuspielen, bzw. anderen das Spiel
+		zu verderben.
+
+	Vorschlaege(!) fuer Massnahmen:
+	
+	-	Belegen mit Fluechen.
+
+	(zB Erkaeltungen, oder zeitweiliger Verlust der Faehigkeit,
+	Waffen zu tragen, Schlechtes Gewissen, dass man immer wieder
+	daran erinnert wird, was man boeses getan hat. etc.)
+	Wer hat weitere Ideen ??
+
+	Vorteile:
+
+		+	Die Bestrafung erfolgt innerhalb des Spieles.
+		+	Durch die Dauer kann die Strafe fein dosiert
+			werden.
+
+	Nachteil: 
+
+		+	Magier sind auf diese Weise schwer zu
+			bestrafen.
+
+
+	-	Geld oder XP wegnehmen.
+
+	-	Herabstufung. Nur im NOTFALL
+
+	-	Aussschluss vom Spiel. Nur im AEUSSERSTEN NOTFALL
+
+
+	Wer ein Spielverderber ist, entscheidet im allgemeinen
+	die Gemeinschaft der Spielenden. Deshalb darf es keine
+	fatalen Eingriffe in das Spiel durch die Entscheidung 
+	einzelner Spieler geben!
+
+	BEMERKUNGEN ZUM THEMA FLEISS VON MAGIERN
+	----------------------------------------
+
+	Wenn ein Magier selten im Spiel ist, heisst das nicht,
+	dass er sich, wenn er einmal da ist, um so staerker
+	bemerkbar machen muss.
+
+
+	BEMERKUNGEN ZU GENAUEREN REGELN
+	-------------------------------
+
+	Man sollte sich nicht an irgendwelchen Zahlen (wiz_levels),
+	Verordnungen, Geschaeftsordnugnen etc. festhalten, sondern sich
+	immer vor Augen halten, dass es sich im ein Spiel handelt.
+	Es haben sich Leute zusammengefunden, um miteinander Spass zu
+	haben. Dieses Spiel ist keine so komplexe Gesellschaft wie die
+	reale; deshalb sollte es auch keiner so komplexen Regeln
+	beduerfen.
+
+	Olpp, Sheriff des MorgenGrauens
+	Rumata, MudAdm des MorgenGrauens
+
+	P.S.:
+		Kommentare und weitere Vorschlaege sind herzlich
+		willkommen.
diff --git a/doc/KURS/LPC-KURS/Contents b/doc/KURS/LPC-KURS/Contents
new file mode 100644
index 0000000..0dd4efb
--- /dev/null
+++ b/doc/KURS/LPC-KURS/Contents
@@ -0,0 +1,13 @@
+                             LPC Basics
+                      Written by Descartes of Borg
+                            23 april 1993
+
+Introduction
+Chapter 1: Introduction to the Coding Environment
+Chapter 2: The LPC Program
+Chapter 3: LPC Data Types
+Chapter 4: Functions
+Chapter 5: The Basics of Inheritance
+Chapter 6: Variable Handling
+Chapter 7: Flow Control
+Chapter 8: The Data Type Object
diff --git a/doc/KURS/LPC-KURS/Introduction b/doc/KURS/LPC-KURS/Introduction
new file mode 100644
index 0000000..2e3c7ff
--- /dev/null
+++ b/doc/KURS/LPC-KURS/Introduction
@@ -0,0 +1,106 @@
+                             LPC Basics
+                     Written by Descartes of Borg
+                            23 april 1993
+ 
+ 
+                            INTRODUCTION
+               This manual, how to use it, and its terms
+ 
+I have seen a lot of requests lately on USENET for LPC manuals.  In addition,
+the immortals on my mud have been telling how good the building documentation
+of Nightmare is, but that there was just no adequate explanation of the
+LPC programming language.  So I decided to try my hand at writing a manual.
+Some things you should keep in mind.
+LPC is a very easy programming language to learn, and it has real
+value in that place most of us know as the real world.  I began playing
+muds in 1991, and in the space of a month created an unimpressive area
+and musician's guild on the original Bates College MUD called Orlith.
+After that, I moved to Los Angeles for a year and had no contact with
+mudding or computers.  In June of 1992, I was back on the internet and
+a wizard of Igor.  In September of 1992 I began coding the Nightmare
+mudlib for our use, and then later decided to distribute it due to there
+not being any mudlibs for MudOS at the time that a person could just throw
+up a running mud with (now, that of course is not the case :)).
+So, I have been doing serious coding for less than a year.  As a
+Philosophy major in a world of Computer Science majors, I just want to
+make clear that it is not at all required that you have ever done anything
+with your computer than log into a mud in order for you to really come
+to understand LPC coding.  This manual makes the following assumptions:
+Someone has taught you basic UNIX commands like ls, cd, mkdir, mv, rm, etc.
+You know how to enter your mud's editor and write a file.  No other
+assumptions are made.  If you know C, you are handicapped in that LPC
+looks a lot like C, but it is not C.  Your preconceptions about
+modular programming development will be a hinderence you will have to
+overcome.  If you have never heard of the C programming language (like
+me in May of 1991), then you are only missing an understanding of the
+simple constructs of C like the flow of program execution and logical
+operators and such.  So a C guru has no real advantage over you, since
+what they know from C which is applicable to LPC is easy to pick up.
+The stuff they know about C which makes them a guru is irrelevant to
+LPC.
+ 
+The chapters of this manual are meant to be read in order.  Starting with
+the introduction, going sequentially through the chapter numbers as
+ordered in the contents file.  Each chapter begins with a paragraph or
+two explaining what you should have come to understand by that point
+in your studies.  After those introductory paragraphs, the chapter then
+begins to discuss its subject matter in nauseating detail.  At the end
+of the chapter is a briefly worded summary of what you should understand
+from that chapter if I have been successful.  Following that may or may
+not be some sidenotes relevant to the subject at hand, but not necessary
+to its understanding.
+ 
+If at any time you get to a chapter intro, and you have read the preceeding
+chapters thoroughly and you do not understand what it says you should
+understand by that point, please mail me!  Clearly, I have failed at that
+point and I need to know where it is I have gone wrong so I can revise
+it properly.  Similarly, if you do not understand what the chapter summary
+says you should, please mail me.  If your mumud is on the MudOS intermud
+system, mail descartes@nightmare.  Otherwise mail borg@hebron.connected.com.
+ 
+Some basic terms this manual uses:
+driver-
+This is the C program which is the game.  It accepts incoming sockets
+(links to other computers), interprets LPC code defined by the mudlib,
+keeps mud objects in memory, makes periodic attempts to clean unused
+mud objects from memory, makes periodic calls to objects, and so on.
+ 
+mudlib-
+LPC code which defines the world in which you are in.  The driver of itself
+is not a game.  It is just a program which allows the creation of a
+multi-user environment.  In some sense, the driver is like an LPC
+compiler, and the mudlib is like a compiler's library (a very loose
+analogy).  The mudlib defines basic objects which will likely be used
+over and over again by people creating in the mud world.  Examples of
+such objects are /std/room (or /room/room), /std/user.c (or /obj/player.c),
+and so on.
+ 
+area or castle:
+Specific creator coded objects which often use a feature of LPC called
+inheritance to make use of the properties of basic mudlib objects and
+turn them into specific objects to be used by players in the game
+ 
+object:
+a room, a weapon, a monster, a player, a bag, etc.  More importantly,
+every individual file with a .c extension is an object.  Objects are
+used in different ways.  Objects like /std/living.c are inherited by
+objects like monster.c and user.c.  Others are cloned, which means a
+duplicate of that code is loaded into memory.  And still others are
+simply loaded into memory to be referenced by other objects.
+ 
+native and compat:
+these two terms refer to two popular flavours of drivers.  Native mode
+mudlibs make use of on the design of LPMud driver 3.0 and later.  You may
+have a 3.0 driver however, but have a 2.4.5 style mudlib.  This is what
+is meant by compat mode.  Mudlibs which are native mode are any for
+MudOS, CD, and LPMud mudlibs that
+are listed as native.  Compat mudlibs are any LPMud mudlib before 3.0 and
+those which are 3.* compat mudlibs.  I believe Amylaar's is compat.
+[ Not true, Amylaar supports native and compat mudlibs, MorgenGrauen
+  is native - Boing ]
+
+Good Luck!
+George Reese
+(Descartes of Borg)
+12 july 1993
+borg@hebron.connected.com
diff --git a/doc/KURS/LPC-KURS/chapter1 b/doc/KURS/LPC-KURS/chapter1
new file mode 100644
index 0000000..6f233d0
--- /dev/null
+++ b/doc/KURS/LPC-KURS/chapter1
@@ -0,0 +1,90 @@
+                           LPC Basics
+                  Written by Descartes of Borg
+                  first edition: 23 april 1993
+                  second edition: 25 may 1993
+
+CHAPTER 1: Introduction to the Coding Environment
+
+1.1 UNIX file structure
+LPMuds use basic UNIX commands and its file structure.  If you know
+UNIX commands already, then note (with a few exceptions) options are
+not available to the commands.  Like DOS, UNIX is heirarchical.  The
+root directory of which all directories are sub-directories is called
+root(/).  And from those sub-directories you may have further
+sub-directories.  A directory may be referred to in two different ways:
+1) by its full name, or absolute name, or 2) by its relative name.
+Absolute name refers to the directory's full path starting from / winding
+down the directory tree until you name the directory in question.  For
+example:
+
+    /players/descartes/obj/monster
+
+refers to the directory monster which is a sub-directory of obj which
+is a sub-directory of descartes which is a sub-directory of players
+which is a sudirectory of /.
+
+The relative name refers to the name relative to another directory.
+The above example is called monster relative to /players/descartes/obj,
+but it is also called obj/monster relative to /players/descartes,
+descartes/obj/monster relative to /players, and finally
+players/descartes/obj/monster relative to /.  You can tell the
+difference between absolute names and relative names because absolute
+names always start with /.  In order to know exactly which directory
+is being named by a relative name, you naturally must know what
+directory it is relative to.
+
+A directory contains sub-directories and files.  LPMuds only use text files
+inside the mudlib.  Like directories, files have both absolute and
+relative names.  The most basic relative name is often referred to as the file
+name, with the rest of the absolute name being referred to as the path.  So,
+for the file: /players/descartes/castle.c, castle.c is the file name, and
+/players/descartes is the path.
+
+On some muds, a file with a file name beginning with a . (like .plan) is
+not visible when you list files with the regular file listing command.
+
+1.2 UNIX Commands
+Along with the UNIX file structure, LPMuds use many UNIX commands.  Typical
+UNIX commands on most muds are:
+pwd, cd, ls, rm, mv, cp, mkdir, rmdir, more, head, cat, ed
+If you have never before seen UNIX commands, you probably are thinking this
+is all nonsense.  Well, it is, but you got to use them.  Before getting
+into what they mean though, first a discussion of current directory.
+If you know DOS, then you know what a current working directory is.
+At any given point, you are considered to be "in" some directory.  This
+means that any relative file or directory names you give in UNIX commands
+are relative to that directory.  For example, if my current directory is
+/players/descartes and I type "ed castle.c" (ed is the command to edit),
+then it assumes I mean the file /players/descartes/castle.c
+
+pwd: shows you your current working directory
+cd: changes your current working directory.  You may give either relative
+    or absolute path names.  With no arguments, it changes to your home
+    directory.
+ls: lists all files in the directory named.  If no directory is named,
+    it lists the files of the current working directory
+rm: deletes the file named
+mv: renames the file named
+cp: copies the file named
+mkdir: makes a new directory
+rmdir: deletes a directory.  All files must have been first removed.
+more: pages the file named so that the file appears on your screen one
+    page at a time.
+cat: shows the whole file to you at once
+head: shows you the first several lines of a file
+tail: shows you the last several lines of a file
+ed: allows you to edit a file using the mud editor
+
+1.3 Chapter Summary
+UNIX uses a heirarchical file structure with the root of the tree being
+named /.  Other directories branch off from that root directory and
+in turn have their own sub-directories.  All directories may contain
+directories and files.  Directories and files are referred to either
+by their absolute name, which always begins with /, or by their relative
+name which gives the file's name relative to a particular directory.
+In order to get around in the UNIX files structure, you have the
+typical UNIX commands for listing files, your current directory, etc.
+On your mud, all of the above commands should have detailed help commands
+to help you explore exactly what they do.  In addition, there should
+be a very detailed file on your mud's editor.  If you are unfamiliar
+with ed, you should go over this convoluted file.
diff --git a/doc/KURS/LPC-KURS/chapter2 b/doc/KURS/LPC-KURS/chapter2
new file mode 100644
index 0000000..f9ad566
--- /dev/null
+++ b/doc/KURS/LPC-KURS/chapter2
@@ -0,0 +1,195 @@
+                           LPC Basics
+                  Written by Descartes of Borg
+                  first edition: 23 april 1993
+                  second edition: 16 june 1993
+
+CHAPTER 2: The LPC Program
+
+2.1 About programs
+The title of this chapter of the textbook is actually poorly named, since
+one does not write programs in LPC.  An LPC coder instead writes *objects*.
+What is the difference?  Well, for our purposes now, the difference is
+in the way the file is executed.  When you "run" a program, execution
+begins at a definite place in the program.  In other words, there
+is a place in all programs that is noted as the beginning where program
+execution starts.  In addition, programs have definite end points,
+so that when execution reaches that point, the execution of the program
+terminates.  So, in short, execution of a program runs from a definite
+beginning point through to a definite end point.  This is not so with
+LPC objects.
+
+With muds, LPC objects are simply distinct parts of the C program which
+is running the game (the driver).  In other words, execution of the mud
+program begins and ends in the driver.  But the driver in fact does
+very little in the way of creating the world you know when you play
+a mud.  Instead, the driver relies heavily on the code created in LPC,
+executing lines of the objects in the mud as needed.  LPC objects thus
+have no place that is necessarily the beginning point, nor do they
+have a definite ending point.
+
+Like other programming languages, an LPC "program" may be made up of
+one or more files.  For an LPC object to get executed, it simple
+needs to be loaded into the driver's memory.  The driver will call lines
+from the object as it needs according to a structure which will be
+defined throughout this textbook.  The important thing you need to
+understand at this point is that there is no "beginning" to an LPC
+object in terms of execution, and there is no "end".
+
+2.2 Driver-mudlib interaction
+As I have mentioned earlier, the driver is the C program that runs on
+the host machine.  It connects you into the game and processes LPC code.
+Note that this is one theory of mud programming, and not necessarily
+better than others.  It could be that the entire game is written in C.
+Such a game would be much faster, but it would be less flexible in
+that wizards could not add things to the game while it was running. This
+is the theory behind DikuMUDs.  Instead, LPMUDs run on the theory that
+the driver should in no define the nature of the game, that the nature
+of the game is to be decided by the individuals involved, and that
+you should be able to add to the game *as it is being played*.  This
+is why LPMUDs make use of the LPC programming language.  It allows
+you to define the nature of the game in LPC for the driver to read and
+execute as needed.  It is also a much simpler language to understand
+than C, thus making the process of world creation open to a greater
+number of people.
+
+Once you have written a file in LPC (assuming it is corrent LPC ), it justs
+sits there on the host machine's hard drive until something in the game
+makes reference to it.  When something in the game finally does make
+reference to the object, a copy of the file is loaded into memory and
+a special *function* of that object is called in order to initialize
+the values of the variables in the object.  Now, do not be concerned
+if that last sentence went right over your head, since someone brand
+new to programming would not know what the hell a function or a variable
+is.  The important thing to understand right now is that a copy of the
+object file is taken by the driver from the machine's hard drive and
+stored into memory (since it is a copy, multiple versions of that
+object may exist).  You will later understand what a function is, what
+a variable is, and exactly how it is something in the game made reference
+to your object.
+
+2.3 Loading an object into memory
+Although there is no particular place in an object code that must exist
+in order for the driver to begin executing it, there is a place for which
+the driver will search in order to initialize the object.  On compat 
+drivers, it is the function called reset().  On native muds it is the
+function called create(). [ MorgenGrauen is native - Boing ]
+
+LPC objects are made up of variables (values which can change) and
+functions which are used to manipulate those variables.  Functions
+manipulate variables through the use of LPC grammatical structures,
+which include calling other functions, using externally defined
+functions (efuns), and basic LPC expressions and flow control 
+mechanisms.
+
+Does that sound convoluted?  First lets start with a variable.  A
+variable might be something like: level.  It can "vary" from sitation
+to situation in value, and different things use the value of the player's
+level to make different things happen.  For instance, if you are a
+level 19 player, the value of the variable level will be 19.  Now
+if your mud is on the old LPMud 2.4.5 system where levels 1-19 are
+players and 20+ are wizards, things can ask for your level value to
+see if you can perform wizard type actions.  Basically, each object
+in LPC is a pile of variables with values which change over time.
+Things happen to these objects based on what values its variables
+hold.  Often, then things that happen cause the variables to change.
+
+So, whenever an object in LPC is referenced by another object currently
+in memory, the driver searches to see what places for values the
+object has (but they have no values yet).  Once that is done, the driver
+calls a function in the object called reset() or create() (depending
+on your driver) which will set up the starting values for the object's
+variables.  It is thus through *calls* to *functions* that variable
+values get manipulated.
+
+But create() or reset() is NOT the starting place of LPC code, although
+it is where most LPC code execution does begin.  The fact is, those
+functions need not exist.  If your object does just fine with its
+starting values all being NULL pointers (meaning, for our purposes
+here, 0), then you do not need a create() or reset() function.  Thus
+the first bit of execution of the object's code may begin somewhere
+completely different.
+
+Now we get to what this chapter is all about.  The question: What
+consists a complete LPC object?  Well, an LPC object is simply
+one or more functions grouped together manipulating 0 or more
+variables.  The order in which functions are placed in an object
+relative to one another is irrelevant.  In other words:
+
+-----
+void init() { add_action("smile", "smile"); }
+
+void create() { return; }
+
+int smile(string str) { return 0; }
+-----
+
+is exactly the same as:
+
+-----
+void create() { return; }
+
+int smile(string str) { return 0; }
+
+void init() { add_action("smile", "smile"); }
+_____
+
+Also important to note, the object containing only:
+
+-----
+void nonsense() {}
+-----
+
+is a valid, but trivial object, although it probably would not interact
+properly with other objects on your mud since such an object has no
+weight, is invisible, etc..
+
+2.4 Chapter summary
+LPC code has no beginning point or ending point, since LPC code is used
+to create objects to be used by the driver program rather than create
+individual programs.  LPC objects consist of one or more functions whose
+order in the code is irrelevant, as well as of zero or more variables whose
+values are manipulated inside those functions.  LPC objects simply sit
+on the host machine's hard driver until referenced by another object in
+the game (in other words, they do not really exist).  Once the object
+is referenced, it is loaded into the machine's memory with empty
+values for the variables.  The function reset() in compat muds or
+create() in native muds is called in that object if it exists to allow
+the variables to take on initial values.  Other functions in the object
+are used by the driver and other objects in the game to allow interaction
+among objects and the manipulation of the LPC variables.
+
+A note on reset() and create():
+create() is only used by muds in native mode (see the textbook Introduction
+for more information on native mode vs. compat mode).  It is only used
+to initialize newly referenced objects.
+
+reset() is used by both muds in compat mode and native mode.  In compat
+mode, reset() performs two functions.  First, it is used to initialize
+newly referenced objects.  In addition, however, compat mode muds use
+reset() to "reset" the object.  In other words, return it to its initial
+state of affairs.  This allows monsters to regenerate in a room and doors
+to start back in the shut position, etc..  Native mode muds use reset()
+to perform the second function (as its name implies).
+
+So there are two important things which happen in LP style muds which
+cause the driver to make calls to functions in objects.  The first is
+the creation of the object.  At this time, the driver calls a function
+to initalize the values in the object.  For compat mode muds, this
+is performed by the function named reset() (with an argument of 0,
+more on this later though).  For muds running in native mode, this is
+performed by the function create().
+
+The second is the returning of the room to some base state of affairs.
+This base set of affairs may or may not be different from the initial
+state of affairs, and certainly you would not want to take up time
+doing redundant things (like resetting variables that never change).
+Compat mode muds nevertheless use the same function that was used to
+create the object to reset it, that being reset().  Native mode muds,
+who use create() to create the room, instead use reset() to reset it.
+All is not lost in compat mode though, as there is a way to tell the
+difference between creation and resetting.  For reset purposes, the
+driver passes either 1 or the reset number as an argument to reset()
+in compat mode.  Now this is meaningless to you now, but just keep in
+mind that you can in fact tell the difference in compat mode.  Also
+keep in mind that the argment in the creation use of reset is 0 and
+the argument in the reset use is a nonzero number.
diff --git a/doc/KURS/LPC-KURS/chapter3 b/doc/KURS/LPC-KURS/chapter3
new file mode 100644
index 0000000..f7e89a4
--- /dev/null
+++ b/doc/KURS/LPC-KURS/chapter3
@@ -0,0 +1,185 @@
+                           LPC Basics
+                  Written by Descartes of Borg
+                  first edition: 23 april 1993
+                  second edition: 17 june 1993
+
+CHAPTER 3: LPC Data Types
+
+3.1 What you should know by now
+LPC object are made up of zero or more variables manipulated by one or
+more functions.  The order in which these functions appear in code is
+irrelevant.  The driver uses the LPC code you write by loading copies of
+it into memory whenever it is first referenced and additional copies
+through cloning.  When each object is loaded into memory, all the variables
+initially point to no value.  The reset() function in compat muds, and
+create() in native muds are used to give initial values to variables in
+objects.  The function for creation is called immediately after the object
+is loaded into memory.  However, if you are reading this textbook with no
+prior programming experience, you may not know what a function is or how
+it gets called.  And even if you have programming experience, you may
+be wondering how the process of functions calling each other gets started
+in newly created objects.  Before any of these questions get answered,
+however, you need to know more about what it is the functions are
+manipulating.  You therefore should thouroughly come to know the concept
+behind LPC data types.  Certainly the most boring subject in this manual,
+yet it is the most crucial, as 90% of all errors (excepting misplaced
+{} and ()) involve the improper usage of LPC data types.  So bear through
+this important chapter, because it is my feeling that understanding this
+chapter alone can help you find coding much, much easier.
+
+3.2 Communicating with the computer
+You possibly already know that computers cannot understand the letters
+and numbers used by humans.  Instead, the "language" spoken by computers
+consists of an "alphabet" of 0's and 1's.  Certainly you know computers
+do not understand natural human languages.  But in fact, they do not
+understand the computer languages we write for them either.  Computer
+languages like BASIC, C, C++, Pascal, etc. are all intermediate
+languages.  They allow you to structure your thoughts more coherently
+for translation into the 0's and 1's of the computer's languages.
+
+There are two methods in which translation is done: compilation and
+interpretation.  These simply are differences betweem when the 
+programming language is translated into computer language.  With
+compiled languages, the programmer writes the code then uses a program
+called a compiler to translate the program into the computer's
+language.  This translation occurs before the program is run.  With
+interpreted languages however, the process of translation occurs as
+the program is being run.  Since the translation of the program is
+occurring during the time of the program's running in interpreted
+languages, interpreted languages make much slower programs than
+compiled languages.
+
+The bottom line is, no matter what language you are writing in, at
+some point this has to be changed into 0's and 1's which can be
+understood by the computer.  But the variables which you store in
+memory are not simply 0's and 1's.  So you have to have a way in
+your programming languages of telling the computer whether or not
+the 0's and 1's should be treated as decimal numbers or characters or
+strings or anything else.  You do this through the use of data types.
+
+For example, say you have a variable which you call 'x' and you give
+it the decimal whole number value 65.  In LPC you would do this through
+the statement:
+
+-----
+x = 65;
+-----
+
+You can later do things like:
+
+_____
+write(x+"\n");        /* \n is symbolically represents a carriage return */
+y = x + 5;
+-----
+
+The first line allows you to send 65 and a carriage return to someone's screen.
+The second line lets you set the value of y to 70.
+The problem for the computer is that it does not know what '65' means when
+you tell it x = 65;.  What you think of 65, it might think of as:
+00000000000000000000000001000001
+But, also, to the computer, the letter 'A' is represented as:
+00000000000000000000000001000001
+So, whenever you instruct the computer write(x+"\n");, it must have some
+way of knowing that you want to see '65' and not 'A'.
+
+The computer can tell the difference between '65' and 'A' through the use
+of data types.  A data types simply says what type of data is being stored
+by the memory location pointed to by a given variable.  Thus, each LPC
+variable has a variable type which guides conversions.  In the example
+given above, you would have had the following line somewhere in the
+code *before* the lines shown above:
+
+-----
+int x;
+-----
+
+This one line tells the driver that whatever value x points to, it will
+be used as the data type "int", which is short for integer, or whole
+number.  So you have a basic introduction into the reason why data types
+exist.  They exist so the driver can make sense of the 0's and 1's that
+the computer is storing in memory.
+
+3.3 The data types of LPC
+All LPMud drivers have the following data types:
+
+void, status, int, string, object, int *, string *, object *, mixed *
+
+Many drivers, but not all have the following important data types which
+are important to discuss:
+
+float, mapping, float *, mapping *
+
+And there are a few drivers with the following rarely used data types
+which are not important to discuss:
+
+function, enum, struct, char
+
+3.4 Simple data types
+This introductory textbook will deal with the data types void, status,
+int, float, string, object, mand mixed.  You can find out about the
+more complex data types like mappings and arrays in the intermediate
+textbook.  This chapter deals with the two simplest data types (from the
+point of view of the LPC coder), int and string.
+
+An int is any whole number.  Thus 1, 42, -17, 0, -10000023 are all type int.
+A string is one or more alphanumeric characters.  Thus "a", "we are borg",
+"42", "This is a string" are all strings.  Note that strings are always
+enclosed in "" to allow the driver to distinguish between the int 42 and
+the string "42" as well as to distinguish between variable names (like x)
+and strings by the same names (like "x").
+
+When you use a variable in code, you must first let the driver know
+what type of data to which that variable points.  This process is
+called *declaration*.  You do this at the beginning of the function
+or at the beginning of the object code (outside of functions before all
+functions which use it).  This is done by placing the name of the data type
+before the name of the variable like in the following example:
+
+-----
+void add_two_and_two() {
+    int x;
+    int y;
+
+    x = 2;
+    y = x + x;
+}
+-----
+
+Now, this is a complete function.  The name of the function is 
+add_two_and_two().  The function begins with the declaration of an
+int variable named x followed by the declaration of an in variable
+named y.  So now, at this point, the driver now has two variables which
+point to NULL values, and it expects what ever values end up there to be
+of type int.
+
+A note about the data types void and status:
+Void is a trivial data type which points to nothing.  It is not used
+with respect to variables, but instead with respect to functions.  You
+will come to understand this better later.  For now, you need only
+understand that it points to no value.  
+
+The data type status is a boolean data type.  That is, it can only have
+1 or 0 as a value.  This is often referred to as being true or false.
+
+3.5 Chapter summary
+For variables, the driver needs to know how the 0's and 1's the computer
+stores in memory get converted into the forms in which you intend them
+to be used.  The simplest LPC data types are void, status, int, and string.
+You do not user variables of type void, but the data type does come
+into play with respect to functions.  In addition to being used for
+translation from one form to the next, data types are used in determining
+what rules the driver uses for such operations as +, -, etc.  For example,
+in the expression 5+5, the driver knows to add the values of 5 and 5
+together to make 10.  With strings however, the rules for int addition
+make no sense.  So instead, with "a"+"b", it appends "b" to the string "a"
+so that the final string is "ab".  Errors can thus result if you mistakenly
+try to add "5"+5.  Since int addition makes no sense with strings, the
+driver will convert the second 5 to "5" and use string addition.  The final
+result would be "55".  If you were looking for 10, you would therefore
+have ended up with erroneous code.  Keep in mind, however, that in most
+instances, the driver will not do something so useful as coming up with
+"55".  It comes up with "55" cause it has a rule for adding a string
+to an int, namely to treat the int as a string.  In most cases, if you
+use a data type for which an operation or function is not defined
+(like if you tried to divide "this is" by "nonsense", "this is"/"nonsense"),
+the driver will barf and report an error to you.
diff --git a/doc/KURS/LPC-KURS/chapter4 b/doc/KURS/LPC-KURS/chapter4
new file mode 100644
index 0000000..4f44cdc
--- /dev/null
+++ b/doc/KURS/LPC-KURS/chapter4
@@ -0,0 +1,225 @@
+                           LPC Basics
+                  Written by Descartes of Borg
+                  first edition: 23 april 1993
+                  second edition: 22 june 1993
+
+CHAPTER 4: Functions
+
+4.1 Review
+By this point, you should be aware that LPC objects consist of functions
+which manipulate variables.  The functions manipulate variables when they
+are executed, and they get executed through *calls* to those functions.
+The order in which the functions are placed in a file does not matter.
+Inside a function, the variables get manipulated.  They are stored in
+computer memory and used by the computer as 0's and 1's which
+get translated to and from useable output and input through a device
+called data typing.  String data types tell the driver that the
+data should appear to you and come from you in the form of alphanumeric
+characters.  Variables of type int are represented to you as whole
+number values.  Type status is represented to you as either 1 or 0.
+And finally type void has no value to you or the machine, and is not
+really used with variable data types.
+
+4.2 What is a function?
+Like math functions, LPC functions take input and return output.
+Languages like Pascal distinguish between the concept of proceedure abd
+the concept of function.  LPC does not, however, it is useful to
+understand this distinction.  What Pascal calls a proceedure, LPC
+calls a function of type void.  In other words, a proceedure, or function
+of type void returns no output.  What Pascal calls a function differs
+in that it does return output.  In LPC, the most trivial, correct
+function is:
+
+-----
+void do_nothing() { }
+-----
+
+This function accepts no input, performs no instructions, and returns no
+value.
+
+There are three parts to every properly written LPC function:
+1) The declaration
+2) The definition
+3) The call
+
+Like with variables, functions must be declared.  This will allow the
+driver to know 1) what type of data the function is returning as output,
+and 2) how many input(s) and of what type those input(s) are.  The
+more common word for input is parameters.
+A function declaration therefore consists of:
+type name(parameter1, parameter2, ..., parameterN);
+The declaration of a function called drink_water() which accepts a string as
+input and an int as output would thus look like this:
+
+-----
+int drink_water(string str);
+-----
+
+where str is the name of the input as it will be used inside the function.
+
+The function definition is the code which describes what the function actually
+does with the input sent to it.  
+The call is any place in other functions which invokes the execution of the
+function in question.  For two functions write_vals() and add(), you thus
+might have the following bit of code:
+
+-----
+/* First, function declarations.  They usually appear at the beginning
+   of object code. 
+*/
+void write_vals();
+int add(int x, int y);
+
+/* Next, the definition of the function write_vals().  We assume that
+   this function is going to be called from outside the object
+*/
+void write_vals() {
+    int x;
+
+    /*N Now we assign x the value of the output of add() through a call */
+    x = add(2, 2);
+    write(x+"\n");
+}
+
+/* Finally, the definition of add() */
+int add(int x, int y) {
+    return (x + y);
+}
+-----
+
+Remember, it does not matter which function definition appears first in the
+code.  This is because functions are not executed consecutively.  Instead,
+functions are executed as called.  The only requirement is that the
+declaration of a function appear before its definition and before the
+definition of any function which makes a call to it.
+
+4.3 Efuns
+Perhaps you have heard people refer to efuns.  They are externally defined
+functions.  Namely, they are defined by the mud driver.  If you have
+played around at all with coding in LPC, you have probably found some
+expressions you were told to use like this_player(), write(), say(),
+this_object(), etc. look a lot like functions.  That is because they are
+efuns.  The value of efuns is that they are much faster than LPC functions,
+since they already exist in the binary form the computer understands.
+
+In the function write_vals() above, two functions calls were made.  The first was to
+the functions add(), which you declared and defined.  The second call, however,
+was to a function called write(), and efun.  The driver has already declared
+and defined this function for you.  You needs only to make calls to it.
+
+Efuns are created to hanldle common, every day function calls, to handle
+input/output to the internet sockets, and other matters difficult to be
+dealt with in LPC.  They are written in C in the game driver and compiled
+along with the driver before the mud comes up, making them much faster
+in execution.  But for your purposes, efun calls are just like calls
+made to your functions.  Still, it is important to know two things of any
+efun: 1) what return type does it have, and 2) what parameters of what
+types does it take.
+
+Information on efuns such as input parameters and return types is often
+found in a directory called /doc/efun on your mud.  I cannot
+detail efuns here, because efuns vary from driver to driver.  However,
+you can often access this information using the commands "man" or "help"
+depending on your mudlib.  For instance, the command "man write" would
+give you information on the write efun.  But if all else fails,
+"more /doc/efun/write" should work.
+
+By looking it up, you will find write is declared as follows:
+
+-----
+void write(string);
+-----
+
+This tells you an appropriate call to write expects no return value and
+passes a single parameter of type string.
+
+4.4 Defining your own functions
+Although ordering your functions within the file does not matter, ordering
+the code which defines a function is most important.  Once a function
+has been called, function code is executed in the order it appears
+in the function definition.  In write_vals() above, the instruction:
+    
+-----
+x = add(2, 2);
+-----
+
+Must come before the write() efun call if you want to see the appropriate
+value of x used in write().  
+
+With respect to values returned by function, this is done through the "return"
+instruction followed by a value of the same data type as the function.  In
+add() above, the instruction is "return (x+y);", where the value of (x+y)
+is the value returned to write_vals() and assigned to x.  On a more
+general level, "return" halts the execution of a function and returns
+code execution to the function which called that function.  In addition,
+it returns to the calling function the value of any expression that follows.
+To stop the execution of a function of type void out of order, use
+"return"; without any value following.  Once again, remember, the data
+type of the value of any expression returned using "return" MUST be the
+same as the data type of the function itself.
+
+4.5 Chapter Summary
+The files which define LPC objects are made of of functions.  Functions, in
+turn, are made up of three parts:
+    1) The declaration
+    2) The definition
+    3) The call
+Function declarations generally appear at the top of the file before any
+defintions, although the requirement is that the declaration must appear
+before the function definition and before the definition of any function
+which calls it.
+Function definitions may appear in the file in any order so long as they
+come after their declaration.  In addition, you may not define one function
+inside another function.
+Function calls appear inside the definition of other functions where you
+want the code to begin execution of your function.  They may also appear
+within the definition of the function itself, but this is not recommended
+for new coders, as it can easily lead to infinite loops.
+
+The function definition consists of the following in this order:
+    1) function return type
+    2) function name
+    3) opening ( followed by a parameter list and a closing )
+    4) an opening { instructing the driver that execution begins here
+    5) declarations of any variables to be used only in that function
+    6) instructions, expressions, and calls to other functions as needed
+    7) a closing } stating that the function code ends here and, if no
+       "return" instruction has been given at this point (type void functions
+       only), execution returns to the calling function as if a r"return"
+       instruction was given
+
+The trivial function would thus be:
+
+-----
+void do_nothing() {}
+-----
+
+since this function does not accept any input, perform any instructions, or
+return any output.
+
+Any function which is not of type void MUST return a value of a data type
+matching the function's data type.
+
+Each driver has a set of functions already defined for you called efuns
+These you need neither need to declare nor define since it has already
+been done for you.  Furthermore, execution of these functions is faster
+than the execution of your functions since efuns are in the driver.
+In addition, each mudlib has special functions like efuns in that they
+are already defined and declared for you, but different in that they
+are defined in the mudlib and in LPC.  They are called simul_efuns, or
+simulated efuns.  You can find out all about each of these as they are
+listed in the /doc/efun directory on most muds.  In addition many
+muds have a command called "man" or a "help" command which allows you
+simply to call up the info files on them.
+
+Note on style:
+Some drivers may not require you to declare your functions, and some
+may not require you to specify the return type of the function in its
+definition.  Regardless of this fact, you should never omit this information
+for the following reasons:
+    1) It is easier for other people (and you at later dates) to read your
+       code and understand what is meant.  This is particularly useful
+       for debugging, where a large portion of errors (outside of misplaced
+       parentheses and brackets) involve problems with data types (Ever
+       gotten "Bad arg 1 to foo() line 32"?).
+    2) It is simply considered good coding form.
diff --git a/doc/KURS/LPC-KURS/chapter5 b/doc/KURS/LPC-KURS/chapter5
new file mode 100644
index 0000000..29d2a0c
--- /dev/null
+++ b/doc/KURS/LPC-KURS/chapter5
@@ -0,0 +1,161 @@
+                           LPC Basics
+                  Written by Descartes of Borg
+                  first edition: 23 april 1993
+                  second edition: 01 july 1993
+
+CHAPTER 5: The Basics of Inheritance
+
+5.1 Review
+You should now understand the basic workings of functions.  You should be
+able to declare and call one.  In addition, you should be able to recognize
+function definitions, although, if this is your first experience with LPC,
+it is unlikely that you will as yet be able to define your own functions.
+There functions form the basic building blocks of LPC objects.  Code
+in them is executed when another function makes a call to them.  In making
+a call, input is passed from the calling function into the execution of
+the called one.  The called function then executes and returns a value
+of a certain data type to the calling function.  Functions which return
+no value are of type void.
+
+After examining your workroom code, it might look something like this
+(depending on the mudlib):
+
+-----
+inherit "/std/room";
+
+void create() {
+    ::create();
+    set_property("light", 2);
+    set_property("indoors", 1);
+    set("short", "Descartes' Workroom");
+    set("long", "This is where Descartes works.\nIt is a cube.\n");
+    set_exits( ({ "/d/standard/square" }), ({ "square" }) );
+}
+-----
+
+If you understand the entire textbook to this point, you should recognize
+of the code the following:
+    1) create() is the definition of a function (hey! he did not declare it)
+    2) It makes calls to set_property(), set(), and set_exits(), none
+       of which are declared or defined in the code.
+    3) There is a line at the top that is no variable or function declaration
+       nor is it a function definition!
+
+This chapter will seek to answer the questions that should be in your head
+at this point:
+    1) Why is there no declaration of create()?
+    2) Where are the functions set_property(), set(), and set_exits() declared
+       and defined?
+    3) What the hell is that line at the top of the file?
+
+5.2 Object oriented programming
+Inheritance is one of the properties which define true object oriented
+programming (OOP).  It allows you to create generic code which can be used
+in many different ways by many different programs.  What a mudlib does is
+create these generalized files (objects) which you use to make very specific
+objects.
+
+If you had to write the code necessary for you to define the workroom above,
+you would have to write about 1000 lines of code to get all the functionality
+of the room above.  Clearly that is a waste of disk space.  In addition,
+such code does not interact well with players and other rooms since every
+creator is making up his or her own functions to perform the functionality
+of a room.  Thus, what you might use to write out the room's long description,
+query_long(), another wizard might be calling long().  This is the primary
+reason mudlibs are not compatible, since they use different protocols for
+object interaction.
+
+OOP overcomes these problems.  In the above workroom, you inherit the
+functions already defined in a file called "/std/room.c".  It has all
+the functions which are commonly needed by all rooms defined in it.  When
+you get to make a specific room, you are taking the general functionality
+of that room file and making a unique room by adding your own function,
+create().
+
+5.3 How inheritance works
+As you might have guessed by now, the line:
+
+-----
+inherit "/std/room";
+-----
+
+has you inherit the functionality of the room "/std/room.c".  By inheriting
+the functionality, it means that you can use the functions which have
+been declared and defined in the file "/std/room.c"  In the Nightmare Mudlib,
+"/std/room.c" has, among other functions, set_property(), set(), and
+set_exits() declared and defined.  In your function create(), you are
+making calls to those functions in order to set values you want your
+room to start with.  These values make your room different from others, yet
+able to interact well with other objects in memory.
+
+In actual practice, each mudlib is different, and thus requires you to use
+a different set of standard functions, often to do the same thing.  It is
+therefore beyond the scope of this textbook even to describe what
+functions exist and what they do.  If your mudlib is well documented,
+however, then (probably in /doc/build) you will have tutorials on how
+to use the inheritable files to create such objects.  These tutorials
+should tell you what functions exist, what input they take, the data
+type of their output, and what they do.
+
+5.4 Chapter summary
+This is far from a complete explanation of the complex subject of inheritance.
+The idea here is for you to be able to understand how to use inheritance in
+creating your objects.  A full discussion will follow in a later textbook.
+Right now you should know the following:
+    1) Each mudlib has a library of generic objects with their own general
+       functions used by creators through inheritance to make coding objects
+       easier and to make interaction between objects smoother.
+    2) The functions in the inheritable files of a mudlib vary from mudlib
+       to mudlib.  There should exist documentation on your mud on how to
+       use each inheritable file.  If you are unaware what functions are
+       available, then there is simply no way for you to use them.  Always
+       pay special attention to the data types of the input and the data
+       types of ay output.
+    3) You inherit the functionality of another object through the line:
+
+-----
+inherit "filename";
+-----
+       
+       where filename is the name of the file of the object to be inherited.
+       This line goes at the beginning of your code.
+
+Note:
+You may see the syntax ::create() or ::init() or ::reset() in places.
+You do not need fully to understand at this point the full nuances of this,
+but you should have a clue as to what it is. The "::" operator is a way
+to call a function specifically in an inherited object (called the scope
+resolution operator).  For instance, most muds' room.c has a function
+called create().  When you inherit room.c and configure it, you are doing
+what is called overriding the create() function in room.c.  This means
+that whenever ANYTHING calls create(), it will call *your* version and not
+the one in room.c.  However, there may be important stuff in the room.c
+version of create().  The :: operator allows you to call the create() in
+room.c instead of your create().
+An example:
+
+-----
+#1
+
+inherit "/std/room";
+
+void create() { create(); }
+-----
+
+-----
+#2
+
+inherit "/std/room";
+
+void create() { ::create(); }
+-----
+
+Example 1 is a horror.  When loaded, the driver calls create(), and then
+create() calls create(), which calls create(), which calls create()...
+In other words, all create() does is keep calling itself until the driver
+detects a too deep recursion and exits.
+
+Example 2 is basically just a waste of RAM, as it is no different from room.c
+functionally.  With it, the driver calls its create(), which in turn calls
+::create(), the create() in room.c.  Otherwise it is functionally
+exactly the same as room.c.
diff --git a/doc/KURS/LPC-KURS/chapter6 b/doc/KURS/LPC-KURS/chapter6
new file mode 100644
index 0000000..6a64215
--- /dev/null
+++ b/doc/KURS/LPC-KURS/chapter6
@@ -0,0 +1,333 @@
+                           LPC Basics
+                  Written by Descartes of Borg
+                  first edition: 23 april 1993
+                  second edition: july 5 1993
+
+CHAPTER 6: Variable Handling
+
+6.1 Review
+By now you should be able to code some simple objects using your muds standard
+object library.  Inheritance allows you to use functions defined in those
+objects without having to go and define yourself.  In addition,
+you should know how to declare your own functions.  This
+chapter will teach you about the basic elements of LPC which will allow you to
+define your own functions using the manipulation of variables.
+
+6.2 Values and objects
+Basically, what makes objects on the mud different are two things:
+1) Some have different functions
+2) All have different values
+
+Now, all player objects have the same functions.  They are therefore
+differentiated by the values they hold.  For instance, the player
+named "Forlock" is different from "Descartes" *at least* in that they
+have different values for the variable true_name, those being
+"descartes" and "forlock".
+
+Therefore, changes in the game involve changes in the values of the objects
+in the game.  Functions are used to name specific process for manipulating
+values.  For instance, the create() function is the function whose
+process is specifically to initialize the values of an object.
+Within a function, it is specifically things called instructions which are
+responsible for the direct manipulation of variables.
+
+6.3 Local and global variables
+Like variables in most programming language, LPC variables may be declared
+as variables "local" to a specific function, or "globally" available
+to all functions.  Local variables are declared inside the function which
+will use them.  No other function knows about their existence, since
+the values are only stored in memory while that function is being executed.
+A global variable is available to any function which comes after its
+declaration in the object code.  Since global variables take up RAM for
+the entire existence of the object, you should use them only when
+you need a value stored for the entire existence of the object.
+Have a look at the following 2 bits of code:
+
+-----
+int x;
+
+int query_x() { return x; }
+
+void set_x(int y) { x = y; }
+-----
+
+-----
+void set_x(int y) {
+    int x;
+
+    x = y;
+    write("x is set to x"+x+" and will now be forgotten.\n");
+}
+-----
+
+In the first example, x is declared outside of any functions, and therefore
+will be available to any function declared after it.  In that example,
+x is a global variable.
+In the second example, x is declared inside the function set_x().  It
+only exists while the function set_x() is being executed.  Afterwards,
+it ceases to exist.  In that example, x is a local variable.
+
+6.4 Manipulating the values of variables
+Instructions to the driver are used to manipulate the values of variables.
+An example of an instruction would be:
+
+-----
+x = 5;
+-----
+
+The above instruction is self-explanatory.  It assigns to the variable
+x the value 5.  However, there are some important concepts in involved
+in that instruction which are involved in instructions in general.
+The first involves the concept of an expression.  An expression is
+any series of symbols which have a value.  In the above instruction,
+the variable x is assigned the value of the expression 5.  Constant
+values are the simplest forms in which expressions can be put.  A constant
+is a value that never changes like the int 5 or the string "hello".
+The last concept is the concept of an operator.  In the above example,
+the assignment operator = is used.
+
+There are however many more operators in LPC, and expressions can get
+quite complex.  If we go up one level of complexity, we get:
+
+-----
+y = 5;
+x = y +2;
+-----
+
+The first instruction uses the assignment operator to assign the value
+of the constant expression 5 to the variable y.  The second one
+uses the assignment operator to assign to x the value of the expression
+(y+2) which uses the addition operator to come up with a value which
+is the sum of the value of y and the value of the constant expression 2.
+Sound like a lot of hot air?
+
+In another manner of speaking, operators can be used to form complex
+expressions.  In the above example, there are two expressions in the
+one instruction x = y + 2;:
+    1) the expression y+2
+    2) the expression x = y + 2
+As stated before, all expressions have a value.  The expression
+y+2 has the value of the sum of y and 2 (here, 7);
+The expression x = y + 2 *also* has the value of 7.
+So operators have to important tasks:
+    1) They *may* act upon input like a function
+    2) They evaluate as having a value themselves.
+Now, not all operators do what 1 does.  The = operators does act upon
+the value of 7 on its right by assigning that value to x.  The operator
++ however does nothing.  They both, however, have their own values.
+
+6.5 Complex expressions
+As you may have noticed above, the expression x = 5 *itself* has a value
+of 5.  In fact, since LPC operators themselves have value as expressions,
+they cal allow you to write some really convoluted looking nonsense like:
+    i = ( (x=sizeof(tmp=users())) ? --x : sizeof(tmp=children("/std/monster"))-1)
+which says basically:
+    assing to tmp the array returned by the efun users(), then assign to x
+    the value equal to the number of elements to that array.  If the value
+    of the expression assigning the value to x is true (not 0), then assign
+    x by 1 and assign the value of x-1 to i.  If x is false though,
+    then set tmp to the array returned by the efun children(), and then
+    assign to i the value of the number of members in the array tmp -1.
+Would you ever use the above statement? I doubt it.  However you might
+see or use expressions similar to it, since the ability to consolidate
+so much information into one single line helps to speed up the execution of
+your code.  A more often used version of this property of LPC operators
+would be something like:
+    x = sizeof(tmp = users());
+    while(i--) write((string)tmp[i]->query_name()+"\n");
+instead of writing something like:
+    tmp = users();
+    x = sizeof(tmp);
+    for(i=0; i<x; i++) write((string)tmp[i]->query_name()+"\n");
+Things like for(), while(), arrays and such will be explained later.
+But the first bit of code is more concise and it executed faster.
+
+NOTE: A detailed description of all basic LPC operators follows the chapter
+summary.
+
+
+6.6 Chapter Summary
+You now know how to declare variables and understand the difference between
+declaring and using them globally or locally.  Once you become familiar
+with your driver's efuns, you can display those values in many different
+ways.  In addition, through the LPC operators, you know how to change
+and evaluate the values contained in variables.  This is useful of course
+in that it allows you to do something like count how many apples have
+been picked from a tree, so that once all apples have been picked, no
+players can pick more.  Unfortunately, you do not know how to have
+code executed in anything other than a linera fashion.  In other words,
+hold off on that apple until the next chapter, cause you do not know
+how to check if the apples picked is equal to the number of apples in the
+tree.  You also do not know about the special function init() where you
+give new commands to players.  But you are almost ready to code a nice,
+fairly complex area.
+
+6.7 LPC operators
+This section contains a detailed listing of the simpler LPC operators,
+including what they do to the values they use (if anything) and the value
+that they have.
+
+The operators described here are:
+=    +    -    *    /    %    +=    -=    *=    /=    %=
+--    ++    ==    !=    >    <    >=    <=    !    &&    ||
+->    ? :
+
+Those operators are all described in a rather dry manner below, but it is best
+to at least look at each one, since some may not behave *exactly* as
+you think.  But it should make a rather good reference guide.
+
+= assignment operator:
+    example: x = 5;
+    value: the value of the variable on the *left* after its function is done
+    explanation: It takes the value of any expression on the *right* and
+      assigns it to the variable on the *left*.  Note that you must use
+      a single variable on the left, as you cannot assign values to 
+      constants or complex expressions.
+
++ addition operator:
+    example: x + 7
+    value: The sum of the value on the left and the value on the right
+    exaplanation: It takes the value of the expression on the right and
+      adds it to the value of the expression on the left. For values
+      of type int, this means the numerical sum.  For strings,
+      it means that the value on the right is stuck onto the value on
+      the left ("ab" is the value of "a"+"b").  This operator does not
+      modify any of the original values (i.e. the variable x from
+      above retains its old value).
+
+- subtraction operator:
+    example: x - 7
+    value: the value of the expression on the left reduced by the right
+    explanation:  Same characteristics as addition, except it subtracts.
+      With strings: "a" is the value of "ab" - "b"
+
+* multiplication operator:
+    example: x*7
+    value and explanation: same as with adding and subtracting except
+      this one performs the math of multiplication
+
+/ division operator:
+    example: x/7
+    value and explanation: see above
+
++= additive assignment operator:
+    example: x += 5
+    value: the same as x + 5
+    exaplanation: It takes the value of the variable on the left
+      and the value of the expression on the right, adds them together
+      and assigns the sum to the variable on the left.
+      example: if x = 2... x += 5 assigns the value
+        7 to the variable x.  The whole expression
+        has the value of 7.
+
+-= subtraction assignment operator
+    example: x-=7
+    value: the value of the left value reduced by the right value
+    examplanation: The same as += except for subtraction.
+
+*= multiplicative assignment operator
+    example: x *= 7
+    value: the value of the left value multiplied by the right
+    explanation: Similar to -= and += except for addition.
+
+/= division assignment operator
+    example: x /= 7
+    value: the value of the variable on the left divided by the right value
+    explanation: similar to above, except with division
+
+++ post/pre-increment operators
+    examples: i++ or ++i
+    values: 
+      i++ has the value of i
+      ++i has the value of i+1
+    explanation: ++ changes the value of i by increasing it by 1.
+      However, the value of the expression depends on where you
+      place the ++.  ++i is the pre-increment operator.  This means
+      that it performs the increment *before* giving a value.
+      i++ is the post-ncrement operator.  It evalutes before incrementing
+      i.  What is the point?  Well, it does not much matter to you at
+      this point, but you should recognize what it means.
+
+-- post/pre-decrement operators
+    examples: i-- or --i
+    values:
+      i-- the value of i
+      --i the value of i reduced by 1
+    explanation: like ++ except for subtraction
+
+== equality operator
+    example: x == 5
+    value: true or false (not 0 or 0)
+    explanation: it does nothing to either value, but
+      it returns true if the 2 values are the same.
+      It returns false if they are not equal.
+
+!= inequality operator
+    example: x != 5
+    value: true or false
+    explanation returns true if the left expression is not equal to the right
+      expression.  It returns fals if they are equal
+
+> greater than operator
+    example: x > 5
+    value: true or false
+    explanation: true only if x has a value greater than 5
+      false if the value is equal or less
+
+< less than operator
+>= greater than or equal to operator
+<= less than or equal to operator
+    examples: x < y    x >= y    x <= y
+    values: true or false
+    explanation: similar as to > except
+      < true if left is less than right
+      >= true if left is greater than *or equal to* right
+      <= true if the left is less than *or equal to* the right
+
+&& logical and operator
+|| logical or operator
+    examples: x && y      x || y
+    values: true or false
+    explanation: If the right value and left value are non-zero, && is true.
+      If either are false, then && is false.
+      For ||, only one of the values must be true for it to evaluate
+      as true.  It is only false if both values indeed
+      are false
+
+! negation operator
+    example: !x
+    value: true or false
+    explanation: If x is true, then !x is false
+      If x is false, !x is true.
+
+A pair of more complicated ones that are here just for the sake of being
+here.  Do not worry if they utterly confuse you.
+
+-> the call other operator
+    example: this_player()->query_name()
+    value: The value returned by the function being called
+    explanation:  It calls the function which is on the right in the object
+      on the left side of the operator.  The left expression *must* be
+      an object, and the right expression *must* be the name of a function.
+      If not such function exists in the object, it will return 0 (or
+      more correctly, undefined).
+
+? : conditional operator
+    example: x ? y : z
+    values: in the above example, if x is try, the value is y
+      if x is false, the value of the expression is z
+    explanation: If the leftmost value is true, it will give the expression as
+      a whole the value of the middle expression.  Else, it will give the
+      expression as a whole the value of the rightmost expression.
+
+A note on equality:  A very nasty error people make that is VERY difficult
+to debug is the error of placing = where you mean ==.  Since
+operators return values, they both make sense when being evaluated.
+In other words, no error occurs.  But they have very different values.  For example:
+  if(x == 5)    if(x = 5)
+The value of x == 5 is true if the value of x is 5, false othewise.
+The value of x = 5 is 5 (and therefore always true).
+The if statement is looking for the expression in () to be either true or false,
+so if you had = and meant ==, you would end up with an expression that is
+always true.  And you would pull your hair out trying to figure out
+why things were not happening like they should :)
diff --git a/doc/KURS/LPC-KURS/chapter7 b/doc/KURS/LPC-KURS/chapter7
new file mode 100644
index 0000000..39f311b
--- /dev/null
+++ b/doc/KURS/LPC-KURS/chapter7
@@ -0,0 +1,432 @@
+                           LPC Basics
+                  Written by Descartes of Borg
+                  first edition: 23 april 1993
+                  second edition: 10 july 1993
+
+CHAPTER 7: Flow Control
+
+7.1 Review of variables
+Variables may be manipulated by assigning or changing values with the
+expressions =, +=, -=, ++, --.  Those expressions may be combined with
+the expressions -, +, *, /, %.  However, so far, you have only been
+shown how to use a function to do these in a linear way.  For example:
+ 
+int hello(int x) {
+    x--;
+    write("Hello, x is "+x+".\n");
+    return x;
+}
+ 
+is a function you should know how to write and understand.  But what
+if you wanted to write the value of x only if x = 1?  Or what if
+you wanted it to keep writing x over and over until x = 1 before
+returning?  LPC uses flow control in exactly the same way as C and C++.
+
+7.2 The LPC flow control statements
+LPC uses the following expressions:
+ 
+if(expression) instruction;
+ 
+if(expression) instruction;
+else instruction;
+ 
+if(expression) instruction;
+else if(expression) instruction;
+else instruction;
+ 
+while(expression) instruction;
+ 
+do { instruction; } while(expression);
+ 
+switch(expression) {
+    case (expression): instruction; break;
+    default: instruction;
+}
+ 
+Before we discuss these, first something on what is meant by expression and
+instruction.  An expression is anything with a value like a variable,
+a comparison (like x>5, where if x is 6 or more, the value is 1, else the
+value is 0), or an assignment(like x += 2).  An instruction can be any
+single line of lpc code like a function call, a value assignment or
+modification, etc.
+ 
+You should know also the operators &&, ||, ==, !=, and !.  These are the
+logical operators.  They return a nonzero value when true, and 0 when false.
+Make note of the values of the following expressions:
+ 
+(1 && 1) value: 1   (1 and 1)
+(1 && 0) value: 0   (1 and 0)
+(1 || 0) value: 1   (1 or 0)
+(1 == 1) value: 1   (1 is equal to 1)
+(1 != 1) value: 0   (1 is not equal to 1)
+(!1) value: 0       (not 1)
+(!0) value: 1       (not 0)
+ 
+In expressions using &&, if the value of the first item being compared
+is 0, the second is never tested even.  When using ||, if the first is
+true (1), then the second is not tested.
+ 
+7.3 if()
+The first expression to look at that alters flow control is if().  Take
+a look at the following example:
+ 
+1 void reset() {
+2     int x;
+3
+4     ::reset();
+5     x = random(10);
+6     if(x > 50) set_search_func("floorboards", "search_floor");
+7 }
+ 
+The line numbers are for reference only.
+In line 2, of course we declare a variable of type int called x.  Line 3
+is aethetic whitespace to clearly show where the declarations end and the
+function code begins.  The variable x is only available to the function
+reset().
+Line 4 makes a call to the room.c version of reset().
+Line 5 uses the driver efun random() to return a random number between
+0 and the parameter minus 1.  So here we are looking for a number between
+0 and 99.
+In line 6, we test the value of the expression (x>50) to see if it is true
+or false.  If it is true, then it makes a call to the room.c function
+set_search_func().  If it is false, the call to set_search_func() is never
+executed.
+In line 7, the function returns driver control to the calling function
+(the driver itself in this case) without returning any value.
+ 
+If you had wanted to execute multiple instructions instead of just the one,
+you would have done it in the following manner:
+ 
+if(x>50) {
+    set_search_func("floorboards", "search_floor");
+    if(!present("beggar", this_object())) make_beggar();
+}
+ 
+Notice the {} encapsulate the instructions to be executed if the test
+expression is true.  In the example, again we call the room.c function
+which sets a function (search_floor()) that you will later define yourself
+to be called when the player types "search floorboards" (NOTE: This is
+highly mudlib dependent.  Nightmare mudlibs have this function call.
+Others may have something similar, while others may not have this feature
+under any name).  Next, there is another if() expression that tests the
+truth of the expression (!present("beggar",this_object())).  The ! in the
+test expression changes the truth of the expression which follows it.  In
+this case, it changes the truth of the efun present(), which will return
+the object that is a beggar if it is in the room (this_object()), or it
+will return 0 if there is no beggar in the room.  So if there is a beggar
+still living in the room, (present("beggar", this_object())) will have
+a value equal to the beggar object (data type object), otherwise it will
+be 0.  The ! will change a 0 to a 1, or any nonzero value (like the
+beggar object) to a 0.  Therefore, the expression
+(!present("beggar", this_object())) is true if there is no beggar in the
+room, and false if there is.  So, if there is no beggar in the room,
+then it calls the function you define in your room code that makes a
+new beggar and puts it in the room. (If there is a beggar in the room,
+we do not want to add yet another one :))
+ 
+Of course, if()'s often comes with ands or buts :).  In LPC, the formal
+reading of the if() statement is:
+ 
+if(expression) { set of intructions }
+else if(expression) { set of instructions }
+else { set of instructions }
+ 
+This means:
+ 
+If expression is true, then do these instructions.
+Otherise, if this second expression is true, do this second set.
+And if none of those were true, then do this last set.
+ 
+You can have if() alone:
+ 
+if(x>5) write("Foo,\n");
+ 
+with an else if():
+ 
+if(x > 5) write("X is greater than 5.\n");
+else if(x >2) write("X is less than 6, but greater than 2.\n");
+ 
+with an else:
+ 
+if(x>5) write("X is greater than 5.\n");
+else write("X is less than 6.\n");
+ 
+or the whole lot of them as listed above.  You can have any number of
+else if()'s in the expression, but you must have one and only one
+if() and at most one else.  Of course, as with the beggar example,
+you may nest if() statements inside if() instructions. (For example,
+    if(x>5) {
+        if(x==7) write("Lucky number!\n");
+        else write("Roll again.\n");
+    }
+    else write("You lose.\n");
+ 
+7.4 The statements: while() and do {} while()
+Prototype:
+while(expression) { set of instructions }
+do { set of instructions } while(expression);
+ 
+These allow you to create a set of instructions which continue to
+execute so long as some expression is true.  Suppose you wanted to
+set a variable equal to a player's level and keep subtracting random
+amounts of either money or hp from a player until that variable equals
+0 (so that player's of higher levels would lose more).  You might do it
+this way:
+ 
+1    int x;
+2
+3    x = (int)this_player()->query_level();  /* this has yet to be explained */
+4    while(x > 0) {
+5        if(random(2)) this_player()->add_money("silver", -random(50));
+6        else this_player()->add_hp(-(random(10));
+7        x--;
+8    }
+ 
+The expression this_player()->query_level() calIn line 4, we start a loop that executes so long as x is greater than 0.
+    Another way we could have done this line would be:
+        while(x) {
+    The problem with that would be if we later made a change to the funtion
+y anywhere between 0 and 49 coins.
+In line 6, if instead it returns 0, we call the add_hp() function in the
+    player which reduces the player's hit points anywhere between 0 and 9 hp.
+In line 7, we reduce x by 1.
+At line 8, the execution comes to the end of the while() instructions and
+    goes back up to line 4 to see if x is still greater than 0.  This
+    loop will keep executing until x is finally less than 1.
+ 
+You might, however, want to test an expression *after* you execute some
+instructions.  For instance, in the above, if you wanted to execute
+the instructions at least once for everyone, even if their level is
+below the test level:
+ 
+    int x;
+ 
+    x = (int)this_player()->query_level();
+    do {
+        if(random(2)) this_player()->add_money("silver", -random(50));
+        else this_player()->add_hp(-random(10));
+        x--;
+    } while(x > 0);
+ 
+This is a rather bizarre example, being as few muds have level 0 players.
+And even still, you could have done it using the original loop with
+a different test.  Nevertheless, it is intended to show how a do{} while()
+works.  As you see, instead of initiating the test at the beginning of the
+loop (which would immediately exclude some values of x), it tests after
+the loop has been executed.  This assures that the instructions of the loop
+get executed at least one time, no matter what x is.
+ 
+7.5 for() loops
+Prototype:
+for(initialize values ; test expression ; instruction) { instructions }
+ 
+initialize values:
+This allows you to set starting values of variables which will be used
+in the loop.  This part is optional.
+ 
+test expression:
+Same as the expression in if() and while().  The loop is executed
+as long as this expression (or expressions) is true. You must have a
+test expression.
+ 
+instruction:
+An expression (or expressions) which is to be executed at the end of each
+loop.  This is optional.
+ 
+Note:
+for(;expression;) {}
+IS EXACTLY THE SAME AS
+while(expression) {}
+ 
+Example:
+ 
+1    int x;
+2
+3    for(x= (int)this_player()->query_level(); x>0; x--) {
+4        if(random(2)) this_player()->add_money("silver", -random(50));
+5        else this_player()->add_hp(-random(10));
+6    }
+ 
+This for() loop behaves EXACTLY like the while() example.
+Additionally, if you wanted to initialize 2 variables:
+ 
+for(x=0, y=random(20); x<y; x++) { write(x+"\n"); }
+ 
+Here, we initialize 2 variables, x and y, and we separate them by a
+comma.  You can do the same with any of the 3 parts of the for()
+expression.
+ 
+7.6 The statement: switch()
+Prototype:
+switch(expression) {
+    case constant: instructions
+    case constant: instructions
+    ...
+    case constant: instructions
+    default: instructions
+}
+ 
+This is functionally much like if() expressions, and much nicer to the
+CPU, however most rarely used because it looks so damn complicated.
+But it is not.
+ 
+First off, the expression is not a test.  The cases are tests.  A English
+sounding way to read:
+ 
+1    int x;
+2
+3    x = random(5);
+4    switch(x) {
+5        case 1: write("X is 1.\n");
+6        case 2: x++;
+7        default: x--;
+8    }
+9    write(x+"\n");
+ 
+is:
+ 
+set variable x to a random number between 0 and 4.
+In case 1 of variable x write its value add 1 to it and subtract 1.
+In case 2 of variable x, add 1 to its value and then subtract 1.
+In other cases subtract 1.
+Write the value of x.
+ 
+switch(x) basically tells the driver that the variable x is the value
+we are trying to match to a case.
+Once the driver finds a case which matches, that case *and all following
+cases* will be acted upon.  You may break out of the switch statement
+as well as any other flow control statement with a break instruction in
+order only to execute a single case.  But that will be explained later.
+The default statement is one that will be executed for any value of
+x so long as the switch() flow has not been broken.  You may use any
+data type in a switch statement:
+ 
+string name;
+ 
+name = (string)this_player()->query_name();
+switch(name) {
+    case "descartes": write("You borg.\n");
+    case "flamme":
+    case "forlock":
+    case "shadowwolf": write("You are a Nightmare head arch.\n");
+    default: write("You exist.\n");
+}
+ 
+For me, I would see:
+You borg.
+You are a Nightmare head arch.
+You exist.
+ 
+Flamme, Forlock, or Shadowwolf would see:
+You are a Nightmare head arch.
+You exist.
+ 
+Everyone else would see:
+You exist.
+ 
+7.7 Altering the flow of functions and flow control statements
+The following instructions:
+return    continue    break
+ 
+alter the natural flow of things as described above.
+First of all,
+return
+no matter where it occurs in a function, will cease the execution of that
+function and return control to the function which called the one the
+return statement is in.  If the function is NOT of type void, then a
+value must follow the return statement, and that value must be of a
+type matching the function.  An absolute value function would look
+like this:
+ 
+int absolute_value(int x) {
+    if(x>-1) return x;
+    else return -x;
+}
+ 
+In the second line, the function ceases execution and returns to the calling
+function because the desired value has been found if x is a positive
+number.
+ 
+continue is most often used in for() and while statements.  It serves
+to stop the execution of the current loop and send the execution back
+to the beginning of the loop.  For instance, say you wanted to avoid
+division by 0:
+ 
+x= 4;
+while( x > -5) {
+    x--
+    if(!x) continue;
+    write((100/x)+"\n");
+}
+write("Done.\n")
+ 
+You would see the following output:
+33
+50
+100
+-100
+-50
+-33
+-25
+Done.
+To avoid an error, it checks in each loop to make sure x is not 0.
+If x is zero, then it starts back with the test expression without
+finishing its current loop.
+ 
+In a for() expression
+ for(x=3; x>-5; x--) {
+    if(!x) continue;
+    write((100/x)+"\n");
+ }
+ write("Done.\n");
+It works much the same way.  Note this gives exactly the same output
+as before. At x=1, it tests to see if x is zero, it is not, so it
+writes 100/x, then goes back to the top, subtracts one from x, checks to
+see if it is zero again, and it is zero, so it goes back to the top
+and subtracts 1 again.
+ 
+break
+This one ceases the function of a flow control statement.  No matter
+where you are in the statement, the control of the program will go
+to the end of the loop.  So, if in the above examples, we had
+used break instead of continue, the output would have looked like this:
+ 
+33
+50
+100
+Done.
+ 
+continue is most often used with the for() and while() statements.
+break however is mostly used with switch()
+ 
+switch(name) {
+    case "descartes": write("You are borg.\n"); break;
+    case "flamme": write("You are flamme.\n"); break;
+    case "forlock": write("You are forlock.\n"); break;
+    case "shadowwolf": write("You are shadowwolf.\n"); break;
+    default: write("You will be assimilated.\n");
+}
+ 
+This functions just like:
+ 
+if(name == "descartes") write("You are borg.\n");
+else if(name == "flamme") write("You are flamme.\n");
+else if(name == "forlock") write("You are forlock.\n");
+else if(name == "shadowwolf") write("You are shadowwolf.\n");
+else write("You will be assimilated.\n");
+ 
+except the switch statement is much better on the CPU.
+If any of these are placed in nested statements, then they alter the
+flow of the most immediate statement.
+ 
+7.8 Chapter summary
+This chapter covered one hell of a lot, but it was stuff that needed to
+be seen all at once.  You should now completely understand if() for()
+while() do{} while() and switch(), as well as how to alter their flow
+using return, continue, and break.  Effeciency says if it can be done in
+a natural way using switch() instead of a lot of if() else if()'s, then
+by all means do it.  You were also introduced to the idea of calling
+functions in other objects.  That however, is a topic to be detailed later.
+You now should be completely at ease writing simple rooms (if you have
+read your mudlib's room building document), simple monsters, and
+other sorts of simple objects.
diff --git a/doc/KURS/LPC-KURS/chapter8 b/doc/KURS/LPC-KURS/chapter8
new file mode 100644
index 0000000..a794713
--- /dev/null
+++ b/doc/KURS/LPC-KURS/chapter8
@@ -0,0 +1,195 @@
+                           LPC Basics
+                  Written by Descartes of Borg
+                  first edition: 23 april 1993
+                  second edition: 12 july 1993
+
+CHAPTER 8: The data type "object"
+
+8.1 Review
+You should now be able to do anything so long as you stick to calling
+functions within your own object.  You should also know, that at the
+bare minimum you can get the create() (or reset()) function in your object
+called to start just by loading it into memory, and that your reset()
+function will be called every now and then so that you may write the
+code necessary to refresh your room.  Note that neither of these
+functions MUST be in your object.  The driver checks to see if the
+function exists in your object first.  If it does not, then it does not
+bother.  You are also acquainted with the data types void, int, and string.
+ 
+7.2 Objects as data types
+In this chapter you will be acquainted with a more complex data type,
+object.  An object variable points to a real object loaded into the
+driver's memory.  You declare it in the same manner as other data types:
+    object ob;
+It differs in that you cannot use +, -, +=, -=, *, or / (what would it
+mean to divide a monster by another monster?).  And since efuns like
+say() and write() only want strings or ints, you cannot write() or
+say() them (again, what would it mean to say a monster?).
+But you can use them with some other of the most important efuns on any
+LPMud.
+ 
+8.3 The efun: this_object()
+This is an efun which returns an object in which the function being executed
+exists.  In other words, in a file, this_object() refers to the object your
+file is in whether the file gets cloned itself or inherted by another file.
+It is often useful when you are writing a file which is getting inherited
+by another file.  Say you are writing your own living.c which gets
+inherited by user.c and monster.c, but never used alone.  You want to log
+the function set_level() it is a player's level being set (but you do not
+care if it is a monster.
+You might do this:
+ 
+void set_level(int x) {
+    if(this_object()->is_player()) log_file("levels", "foo\n");
+    level = x;
+}
+ 
+Since is_player() is not defined in living.c or anything it inherits,
+just saying if(is_player()) will result in an error since the driver
+does not find that function in your file or anything it inherits.
+this_object() allows you to access functions which may or may not be
+present in any final products because your file is inherited by others
+without resulting in an error.
+ 
+8.4 Calling functions in other objects
+This of course introduces us to the most important characteristic of
+the object data type.  It allows us to access functions in other objects.
+In previous examples you have been able to find out about a player's level,
+reduce the money they have, and how much hp they have.
+Calls to functions in other objects may be done in two ways:
+ 
+object->function(parameters)
+call_other(object, "function", parameters);
+ 
+example:
+this_player()->add_money("silver", -5);
+call_other(this_player(), "add_money", "silver", -5);
+ 
+In some (very loose sense), the game is just a chain reaction of function
+calls initiated by player commands.  When a player initiates a chain of
+function calls, that player is the object which is returned by
+the efun this_player().  So, since this_player() can change depending
+on who initiated the sequence of events, you want to be very careful
+as to where you place calls to functions in this_player().  The most common
+place you do this is through the last important lfun (we have mentioned
+create() and reset()) init().
+ 
+8.5 The lfun: init()
+Any time a living thing encounters an object (enters a new room, or enters
+the same room as a certain other object), init() is called in all of
+the objects the living being newly encounters.  It is at this point
+that you can add commands the player can issue in order to act.
+Here is a sample init() function in a flower.
+ 
+void init() {
+    ::init();
+    add_action("smell_flower", "smell");
+}
+ 
+Ito smell_flower().  So you should have smell_flower() look like this:
+ 
+1 int smell_flower(string str);        /* action functions are type int */
+2
+3 int smell_flower(string str) {
+4    if(str != "flower") return 0;     /* it is not the flower being smelled */
+5    write("You sniff the flower.\n");
+6    say((string)this_player()->query_cap_name()+" smells the flower.\n");
+7    this_player()->add_hp(random(5));
+8    return 1;
+9 }
+ 
+In line 1, we have our function declared.
+In line 3, smell_flower() begins.  str becomes whatever comes after the
+    players command (not including the first white space).
+In line 4, it checks to see if the player had typed "smell flower".  If
+    the player had typed "smell cheese", then str would be "cheese".  If
+    it is not in fact "flower" which is being smelled, then 0 is returned,
+    letting the driver know that this was not the function which should
+    have been called.  If in fact the player had a piece of cheese as well
+    which had a smell command to it, the driver would then call the function
+    for smelling in that object.  The driver will keep calling all functions
+    tied to smell commands until one of them returns 1.  If they all return
+    0, then the player sees "What?"
+In line 5, the efun write() is called.  write() prints the string which
+    is passed to it to this_player().  So whoever typed the command here
+    sees "You sniff the flower."
+In line 6, the efun say() is called.  say() prints the string which is
+    doing the sniffing, we have to call the query_cap_name() function
+    in this_player().  That way if the player is invis, it will say
+    "Someone" (or something like that), and it will also be properly
+    capitalized.
+In line 7, we call the add_hp() function in the this_player() object,
+    since we want to do a little healing for the sniff (Note: do not
+    code this object on your mud, whoever balances your mud will shoot you).
+In line 8, we return control of the game to the driver, returning 1 to
+    let it know that this was in fact the right function to call.
+ 
+8.6 Adding objects to your rooms
+And now, using the data type object, you can add monsters to your rooms:
+ 
+void create() {
+    ::create();
+    set_property("light", 3);
+    set("short", "Krasna Square");
+    set("long", "Welcome to the Central Square of the town of Praxis.\n");
+    set_exits( ({ "d/standard/hall" }), ({ "east" }) );
+}
+ 
+void reset() {
+    object ob;
+ 
+    ::reset();
+    if(present("guard")) return;     /* Do not want to add a guard if */
+    ob = new("/std/monster");        /* one is already here           */
+    ob->set_name("guard");
+    ob->set("id", ({ "guard", "town guard" }) );
+    ob->set("short", "Town guard");
+    ob->set("long", "He guards Praxis from nothingness.\n");
+    ob->set_gender("male");
+    ob->set_race("human");
+    ob->set_level(10);
+    ob->set_alignment(200);
+    ob->set_humanoid();
+    ob->set_hp(150);
+    ob->set_wielding_limbs( ({ "right hand", "left hand" }) );
+    ob->move(this_object());
+}
+ 
+Now, this will be wildly different on most muds.  Some, as noted before,
+in that object so you have a uniquely configured monster object.  The
+last act in native muds is to call move() in the monster object to move
+it to this room (this_object()).  In compat muds, you call the efun
+move_object() which takes two parameters, the object to be moved, and the
+object into which it is being moved.
+ 
+8.7 Chapter summary
+At this point, you now have enough knowledge to code some really nice
+stuff.  Of course, as I have been stressing all along, you really need
+to read the documents on building for your mud, as they detail which
+functions exist in which types of objects for you to call.  No matter
+what your knowledge of the mudlib is, you have enough know-how to
+give a player extra things to do like sniffing flowers or glue or whatever.
+At this point you should get busy coding stuff.  But the moment things
+even look to become tedious, that means it is time for you to move to
+the next level and do more.  Right now code yourself a small area.
+Make extensive use of the special functions coded in your mud's
+room.c (search the docs for obscure ones no one else seems to use).
+Add lots o' neat actions.  Create weapons which have magic powers which
+gradually fade away.  All of this you should be able to do now.  Once
+this becomes routine for you, it will be time to move on to intermediate
+stuff.  Note that few people actually get to the intermediate stuff.
+If you have played at all, you notice there are few areas on the mud
+which do what I just told you you should be able to do.  It is not
+because it is hard, but because there is a lot of arrogance out there
+on the part of people who have gotten beyond this point, and very little
+communicating of that knowledge.  The trick is to push yourself and
+think of something you want to do that is impossible.  If you ask someone
+in the know how to do X, and they say that is impossible, find out
+youself how to code it by experimenting.
+
+George Reese
+Descartes of Borg
+12 july 1993
+borg@hebron.connected.com
+Descartes@Nightmare (intermud)
+Descartes@Igor (not intermud)
diff --git a/doc/KURS/LPC-KURS2/Contents b/doc/KURS/LPC-KURS2/Contents
new file mode 100644
index 0000000..ff58a8a
--- /dev/null
+++ b/doc/KURS/LPC-KURS2/Contents
@@ -0,0 +1,15 @@
+Intermediate LPC
+Descartes of Borg 
+November 1993
+
+                                   Contents
+
+1: Introduction
+2: The LPMud Driver
+3: Complex Data Types
+4: The LPC Pre-Compiler
+5: Advanced String Handling
+6: Intermediate Inheritance
+7: Debugging
+
+Copyright (c) George Reese 1993
diff --git a/doc/KURS/LPC-KURS2/Copyright b/doc/KURS/LPC-KURS2/Copyright
new file mode 100644
index 0000000..29a2620
--- /dev/null
+++ b/doc/KURS/LPC-KURS2/Copyright
@@ -0,0 +1,62 @@
+                      Intermediate LPC first edition
+
+Copyright (c) 1993 George Reese
+All rights to this text are retained by the author.
+
+
+Permission is granted to distrubute and display the contents of this
+document in full so long as the following conditions are met:
+1) No payment may be received for the redistribution or display of this text,
+except to cover the costs for distribution media and and shipping and/or
+transmission charges.
+2) The textbook must be distributed or displayed in its entirety in its original
+form.  Changes may only be made to private, individual copies, except as
+outlined below.
+
+Acceptable changes are defined as the following:
+1) Format changes, such as changing from WordPerfect to Word 
+2) Medium changes, such as from electronic copy to paper
+3) Content changes are only acceptable under the following circumstances:
+	a) In electronic media: none of the original text may be ommitted. 
+        You may add comments
+	as you feel necessary, so long as comments are enclosed in <* *>
+        and are accompanied by
+	the game name or real name of the author of the comments
+	b) In hard copy: none of the original text may be omitted, but it may
+        be struck out so long
+	as the content of the original text is visible.  Comments may be made
+        in any form so long 
+	as they are made in handwriting and they are signed by the author. 
+        Comments which are typed or printed must be made in accordance with the
+        format for electronic media.
+
+Practically speaking, this is what I mean:
+First, I wrote this mostly for mud admins to put onto their muds for learning
+coders to read as they are learning to build realms.  I did not do this for
+someone else to make a buck.  So if you charge money for redistributing it
+or allowing someone else to see it, you are in violation of this copyright. 
+Unless you are simply charging for what it cost you to print up a copy or
+what the diskettes and postage cost to mail it.
+Second, I wrote this textbook, and I should receive credit/blame for what I
+say, and others should receive credit/blame for what they say.  For example,
+if I said something completely wrong, and you simply corrected it, I would
+be getting credit for something I did not do.  Yet, if you comment according
+to the outline above, you will be properly credited for your comments. 
+More important to me, however, is the practical effect of having hundreds of
+copies of this textbook everywhere.  If you change something I had right
+without noting it as a comment, I will be blamed for spreading
+misinformation.  This problem is only compunded if the text is redistributed. 
+So I prefer my words to remain my own.  But, when I make mistakes, or if
+something I say does not fit your driver/mudlib, please comment it so people
+will know.  In addition, having the comments side-by-side allows people to
+see other ideas, like how another driver might handle something.
+
+
+I want to please note again, you may display this on your mud (like in /doc). 
+You do not need to
+mail me for permission.  I would not mind email, since it is nice to know
+people are using it, but that is not required.  Also, if you really feel I have
+done such a wonderful job that you should pay money to use this, then give
+$5 to your local ASPCA (or international equivalent).
+
+See the file titled Contents for a full list of textbook chapters.
diff --git a/doc/KURS/LPC-KURS2/chapter1 b/doc/KURS/LPC-KURS2/chapter1
new file mode 100644
index 0000000..6c459cd
--- /dev/null
+++ b/doc/KURS/LPC-KURS2/chapter1
@@ -0,0 +1,143 @@
+Intermediate LPC
+Descartes of Borg
+Novermber 1993
+
+                         Chapter 1: Introduction
+
+1.1 LPC Basics
+Anyone reading this textbook should either have read the textbook LPC
+Basics or be familiar enough with mud realm coding such that not only are
+they capable of building rooms and other such objects involved in area
+coding, but they also have a good idea of what is going on when the code
+they write is executing.  If you do not feel you are at this point, then go
+back and read LPC Basics before continuing.  If you do so, you will find
+that what you read here will be much more meaningful to you.
+
+1.2 Goals of This Textbook
+The introductory textbook was meant to take people new to LPC from
+knowing nothing to being able to code a nice realm on any LPMud.  There
+is naturally much more to LPC and to LPMud building, however, than
+building rooms, armours, monsters, and weapons.  As you get into more
+complicated concepts like guilds, or desire to do more involved things with
+your realm, you will find the concepts detailed in LPC Basics to be lacking
+in support for these projects.  Intermediate LPC is designed to take you
+beyond the simple realm building process into a full knowledge of LPC for
+functioning as a realm builder on an LPMud.  The task of mudlib building
+itself is left to a later text.  After reading this textbook and working through
+it by experimenting with actual code, the reader should be able to code game
+objects to fit any design or idea they have in mind, so long as I have been
+successful.
+
+1.3 An Overview
+What more is there?  Well many of you are quite aware that LPC supports
+mappings and arrays and have been asking me why those were not detailed
+in LPC Basics.  I felt that those concepts were beyond the scope of what I
+was trying to do with that textbook and were more fitting to this textbook. 
+But new tools are all fine and dandy, what matters, however, is what you
+can do with those tools.  The goal of LPC Basics was to get you to building
+quality LPMud realms.  Mappings and arrays are not necessary to do that. 
+The goal of this book is to allow you to code any idea you might want to
+code in your area.  That ability requires the knowledge of mappings and
+arrays.
+
+Any idea you want to code in an LPMud is possible.  LPC is a language
+which is amazingly well suited to this task.  All that prevents you from
+coding your ideas is your knowledge of LPC or an inadequate mudlib or
+your mudÕs theme or administrative policies.  This textbook cannot make
+the mudlib you are working with any better, and it cannot change the mud
+theme or the mudÕs administrative policies.  Never once think that LPC is
+incapable of doing what you want to do.  If your idea is prevented by
+administrative policies or themes, then it is simply not an idea for your
+current mud.  If the mudlib is inadequate, talk to the people in charge of
+your mudlib about what can be done at the mudlib level to facilitate it.  You
+would be surprised by what is actually in the mudlib you did not know
+about.  More important, after reading this textbook, you should be able to
+read all of the mudlib code in your mudÕs mudlib and understand what is
+going on at each line in the mudlib code.  You may not as yet be able to
+reproduce that code on your own, but at least you can understand what is
+going on at the mudlib level.
+
+This textbook starts out with a discussion about what the LPMud driver is
+doing.  One nice thing about this textbook, in general it is completely driver
+and mudlib independent (excepting for the Dworkin Game Driver).  The
+chapter on the game driver does not get into actual implementation, but
+instead deals with what all game drivers basically do in order to run the
+mud.
+
+Next I discuss those magic topics everyone wants to know more about,
+arrays and mappings.  Mappings may be simultaneously the easiest and
+most difficult data type to understand.  Since they are sort of complex arrays
+in a loose sense, you really need to understand arrays before discussing
+them.  All the same, once you understand them, they are much easier than
+arrays to use in real situations.  At any rate, spend most of your time
+working with that chapter, because it is probably the most difficult, yet most
+useful chapter in the book.
+
+After that follows a brief chapter on the LPC pre-compiler, a tool you can
+use for sorting out how your code will look before it gets sent to the
+compiler.  Despite my horrid intro to it here, this chapter is perhaps the
+easiest chapter in the textbook.  I put it after the mappings and arrays
+chapter for exactly that reason.
+
+Strings are re-introduced next, going into more detail with how you can do
+such things as advanced command handling by breaking up strings.  Once
+you understand arrays fairly well, this chapter should be really simple.
+
+The next chapter is the second most important in the book.  It may be the
+most important if you ever intend to go beyond the intermediate stage and
+dive into mudlib coding.  That chapter involves the complex ideas behind
+LPC inheritance.  Since the goal of this textbook is not to teach mudlib
+programming, the chapter is not a detailed discussion on object oriented
+programming.  Understanding this chapter, however, will give you some
+good insights into what is involved with object oriented programming, as
+well as allow you to build more complex objects by overriding functions
+and defining your own base classes.
+
+Finally, the textbook ends with a simple discussion of code debugging. 
+This is not an essential chapter, but instead it is meant as more of an
+auxiliary supplement to what the knowledge you have accumulated so far.
+
+1.4 Not Appearing in This Textbook
+Perhaps what might appear to some as the most glaring omission of this
+textbook is largely a political omission, shadows.  Never have I ever
+encountered an example of where a shadow was either the best or most
+effecient manner of doing anything.  It does not follow from that, however,
+that there are no uses for shadows.  My reasoning for omitting shadows
+from this textbook is that the learner is best served by learning the concepts
+in this textbook first and having spent time with them before dealing with
+the subject of shadows.  In that way, I feel the person learning LPC will be
+better capable of judging the merits of using a shadow down the road.  I
+will discuss shadows in a future textbook.
+
+If you are someone who uses shadows some or a lot, please do not take the
+above paragraph as a personal attack.  There may be some perfectly valid
+uses for shadows somewhere which I have yet to encounter.  Nevertheless,
+they are not the ideal way to accomplish any given task, and therefore they
+are not considered for the purposes of this textbook an intermediate coding
+tool.
+
+I have also omitted discussions of security and object oriented
+programming.  Both are quite obviously mudlib issues.  Many people,
+however, might take exception with my leaving out a discussion of object
+oriented programming.  I chose to leave that for a later text, since most area
+builders code for the creativity, not for the computer science theory.  In both
+the intermediate and beginner textbooks, I have chosen only to discuss
+theory where it is directly applicable to practical LPC programming.  For
+people who are starting out green in LPC and want to code the next great
+mudlib, perhaps theory would be more useful.  But for the purposes of this
+book, a discussion of object oriented programming is simply a snoozer.  I
+do plan to get heavy into theory with the next textbook.
+
+1.5 Summary
+LPC is not difficult to learn.  It is a language which, although pathetic
+compared to any other language for performing most computer language
+tasks, is incredibly powerful and unequalled for the tasks of building an
+area in MUD type games.  For the beginner, it allows you to easily jump in
+and code useful objects without even knowing what you are doing.  For the
+intermediate person, it allows you to turn any idea you have into textual
+virtual reality.  And for the advanced person, itÕs object oriented features
+can allow you to build one of the most popular games on the internet.  What
+you can do is simply limited by how much you know.  And learning more
+does not require a computer science degree.
+
+Copyright (c) George Reese 1993
diff --git a/doc/KURS/LPC-KURS2/chapter2 b/doc/KURS/LPC-KURS2/chapter2
new file mode 100644
index 0000000..51a4c3c
--- /dev/null
+++ b/doc/KURS/LPC-KURS2/chapter2
@@ -0,0 +1,223 @@
+Intermediate LPC
+Descartes of Borg
+Novermber 1993
+
+                         Chapter 2: The LPMud Driver
+
+2.1 Review of Basic Driver/Mudlib Interaction
+In the LPC Basics textbook, you learned a lot about the way the mudlib
+works, specifically in relation to objects you code in order to build your
+realm.  Not much was discussed about the interaction between the
+mudlib and the driver.  You should know, however, that the driver
+does the following:
+1) When an object is first loaded into memory, the driver will call
+create() in native muds and reset() in compat muds.  A creator
+uses create() or reset() to give initial values to the object.
+2) At an interval setup by the game administrator, the driver calls the
+function reset().  This allows the object to regenerate monsters and
+such.  Notice that in a compat mud, the same function is used to set up
+initial values as is used to reset the room.
+3) Any time a living object comes in contact with an object of any sort,
+the driver calls init() in the newly encountered object.  This allows
+newly encountered objects to give living objects commands to execute
+through the add_action() efun, as well as perform other actions which
+should happen whenever a living thing encounters a given object.
+4) The driver defines a set of functions known as efuns which are
+available to all objects in the game.  Examples of commonly used efuns
+are: this_player(), this_object(), write(), say(), etc.
+
+2.2 The Driver Cycle
+The driver is a C program which runs the game.  Its basic functions are
+to accept connections from the outside world so people can login,
+interpret the LPC code which defines LPC objects and how they
+function in the game, and accept user input and call the appropriate LPC
+functions which match the event.  In its most simplest essence, it is an
+unending loop.
+
+Once the game has booted up and is properly functioning (the boot up
+process will be discussed in a future, advanced LPC textbook), the
+driver enters a loop which does not terminate until the shutdown() efun
+is legally called or a bug causes the driver program to crash.  First off,
+the driver handles any new incoming connections and passes control of
+the connection to a login object.  After that, the driver puts together a
+table of commands which have been entered by users since the last cycle
+of the driver.  After the command table is assembled, all messages
+scheduled to be sent to the connection from the last driver cycle are sent
+out to the user.  At this point, the driver goes through the table of
+commands to be executed and executes each set of commands each
+object has stored there.  The driver ends its cycle by calling the function
+heart_beat() in every object with a heart_beat() set and finally
+performing all pending call outs.  This chapter will not deal with the
+handling of connections, but instead will focus on how the driver
+handles user commands and heartbeats and call outs.
+
+2.3 User Commands
+As noted in section 1.2, the driver stores a list of commands for each
+user to be executed each cycle.  The commands list has the name of the
+living object performing the command, the object which gave the living
+object that command, and the function which is to be executed in order
+to perform the command.  The driver refers to the object which typed in
+the command as the command giver.  It is the command giver which
+gets returned as this_player() in most cases.
+
+The driver starts at the top of the list of living objects with pending
+commands, and successively performs each command it typed by calling
+the function associated with the command and passing any arguments
+the command giver gave as arguments to the function.  As the driver
+starts with the commands issued by a new living object, the command
+giver variable is changed to be equal to the new living object, so that
+during the sequence of functions initiated by that command, the efun
+this_player() returns the object which issued the command.
+
+Let's look at the command buffer for an example player.  Since the
+execution of his last command, Bozo has typed "north" and "tell
+descartes when is the next reboot".  The command "north" is associated
+with the function "Do_Move()" in the room Bozo is in (the command
+"north" is automatically setup by the set_exits() efun in that room).  The
+command "tell" is not specifically listed as a command for the player,
+however, in the player object there is a function called "cmd_hook()"
+which is associated with the command "", which matches any possible
+user input.
+
+Once the driver gets down to Bozo, the command giver variable is set to
+the object which is Bozo.  Then, seeing Bozo typed "north" and the
+function "north" is associated with, the driver calls Bozo's_Room-
+>Do_Move(0).  An argument of 0 is passed to the function since Bozo
+only typed the command "north" with no arguments.  The room
+naturally calls some functions it needs, all the while such that the efun
+this_player() returns the object which is Bozo.  Eventually, the room
+object will call move_player() in Bozo, which in turn calls the
+move_object() efun.  This efun is responsible for changing an object's
+environment.
+
+When the environment of an object changes, the commands available to
+it from objects in its previous environment as well as from its previous
+environment are removed from the object.  Once that is done, the driver
+calls the efun init() in the new environment as well as in each object in
+the new environment.  During each of these calls to init(), the object
+Bozo is still the command giver.  Thus all add_action() efuns from this
+move will apply to Bozo.  Once all those calls are done, control passes
+back from the move_object() efun to the move_player() lfun in Bozo. 
+move_player() returns control back to Do_Move() in the old room,
+which returns 1 to signify to the driver that the command action was
+successful.  If the Do_Move() function had returned 0 for some reason,
+the driver would have written "What?" (or whatever your driver's
+default bad command message is) to Bozo.
+
+Once the first command returns 1, the driver proceeds on to Bozo's
+second command, following much the same structure.  Note that with
+"tell descartes when is the next reboot", the driver passes "descartes
+when is the next reboot" to the function associated with tell.  That
+function in turn has to decide what to do with that argument.  After that
+command returns either 1 or 0, the driver then proceeds on to the next
+living object with commands pending, and so on until all living objects
+with pending commands have had their commands performed.
+
+2.4 The Efuns set_heart_beat() and call_out()
+Once all commands are performed for objects with commands pending,
+the driver then proceeds to call the heart_beat() function in all objects
+listed with the driver as having heartbeats.  Whenever an object calls the
+efun set_heart_beat() with a non-zero argument (depending on your
+driver, what non-zero number may be important, but in most cases you
+call it with the int 1).  The efun set_heart_beat() adds the object which
+calls set_heart_beat() to the list of objects with heartbeats.  If you call it
+with an argument of 0, then it removes the object from the list of objects
+with heartbeats.
+
+The most common use for heartbeats in the mudlib is to heal players and
+monsters and perform combat.  Once the driver has finished dealing with
+the command list, it goes through the heartbeat list calling heart_beat() in
+each object in the list.  So for a player, for example, the driver will call
+heart_beat() in the player which will:
+1) age the player
+2) heal the player according to a heal rate
+3) check to see if there are any hunted, hunting, or attacking objects
+around
+4) perform an attack if step 3 returns true.
+5) any other things which need to happen automatically roughly every
+second
+
+Note that the more objects which have heartbeats, the more processing
+which has to happen every cycle the mud is up.  Objects with heartbeats
+are thus known as the major hog of CPU time on muds.  
+
+The call_out() efun is used to perform timed function calls which do not
+need to happen as often as heartbeats, or which just happen once.  Call
+outs let you specify the function in an object you want called.  The
+general formula for call outs is:
+call_out(func, time, args);
+The third argument specifying arguments is optional.  The first argument
+is a string representing the name of the function to be called.  The second
+argument is how many seconds should pass before the function gets
+called.
+
+Practically speaking, when an object calls call_out(), it is added to a list
+of objects with pending call outs with the amount of time of the call out
+and the name of the function to be called.  Each cycle of the driver, the
+time is counted down until it becomes time for the function to be called. 
+When the time comes, the driver removes the object from the list of
+objects with pending call outs and performs the call to the call out
+function, passing any special args originally specified by the call out
+function.
+
+If you want a to remove a pending call before it occurs, you need to use
+the remove_call_out() efun, passing the name of the function being
+called out.  The driver will remove the next pending call out to that
+function.  This means you may have some ambiguity if more than one
+call out is pending for the same function.
+
+In order to make a call out cyclical, you must reissue the call_out() efun
+in the function you called out, since the driver automatically removes the
+function from the call out table when a call out is performed.  Example:
+
+void foo() { call_out("hello", 10); }
+
+void hello() { call_out("hello", 10); }
+
+will set up hello() to be called every 10 seconds after foo() is first called. 
+There are several things to be careful about here.  First, you must watch
+to make sure you do not structure your call outs to be recursive in any
+unintended fashion.  Second, compare what a set_heart_beat() does
+when compared directly to what call_out() does.
+
+set_heart_beat():
+a) Adds this_object() to a table listing objects with heartbeats.
+b) The function heart_beat() in this_object() gets called every single
+driver cycle.
+
+call_out():
+a) Adds this_object(), the name of a function in this_object(), a time
+delay, and a set of arguments to a table listing functions with pending
+call outs.  
+b) The function named is called only once, and that call comes after the
+specified delay.
+
+As you can see, there is a much greater memory overhead associated
+with call outs for part (a), yet that there is a much greater CPU overhead
+associated with heartbeats as shown in part (b), assuming that the delay
+for the call out is greater than a single driver cycle. 
+
+Clearly, you do not want to be issuing 1 second call outs, for then you
+get the worst of both worlds.  Similarly, you do not want to be having
+heart beats in objects that can perform the same functions with call outs
+of a greater duration than 1 second.  I personally have heard much talk
+about at what point you should use a call out over a heartbeat.  What I
+have mostly heard is that for single calls or for cycles of a duration
+greater than 10 seconds, it is best to use a call out.  For repetitive calls of
+durations less than 10 seconds, you are better off using heartbeats.  I do
+not know if this is true, but I do not think following this can do any
+harm.
+
+2.5 Summary
+Basic to a more in depth understanding of LPC is and understanding of
+the way in which the driver interacts with the mudlib.  You should now
+understand the order in which the driver performs functions, as well as a
+more detailed knowledge of the efuns this_player(), add_action(), and
+move_object() and the lfun init().  In addition to this building upon
+knowledge you got from the LPC Basics textbook, this chapter has
+introduced call outs and heartbeats and the manner in which the driver
+handles them.  You should now have a basic understanding of call outs
+and heartbeats such that you can experiment with them in your realm
+code.
+
+Copyright (c) George Reese 1993
diff --git a/doc/KURS/LPC-KURS2/chapter3 b/doc/KURS/LPC-KURS2/chapter3
new file mode 100644
index 0000000..f986737
--- /dev/null
+++ b/doc/KURS/LPC-KURS2/chapter3
@@ -0,0 +1,481 @@
+Intermediate LPC
+Descartes of Borg
+November 1993
+
+                        Chapter 3: Complex Data Types
+
+3.1 Simple Data Types
+In the textbook LPC Basics, you learned about the common, basic LPC
+data types: int, string, object, void.  Most important you learned that
+many operations and functions behave differently based on the data type
+of the variables upon which they are operating.  Some operators and
+functions will even give errors if you use them with the wrong data
+types.  For example, "a"+"b" is handled much differently than 1+1. 
+When you ass "a"+"b", you are adding "b" onto the end of "a" to get
+"ab".  On the other hand, when you add 1+1, you do not get 11, you get
+2 as you would expect.
+
+I refer to these data types as simple data types, because they atomic in
+that they cannot be broken down into smaller component data types. 
+The object data type is a sort of exception, but you really cannot refer
+individually to the components which make it up, so I refer to it as a
+simple data type.
+
+This chapter introduces the concept of the complex data type, a data type
+which is made up of units of simple data types.  LPC has two common
+complex data types, both kinds of arrays.  First, there is the traditional
+array which stores values in consecutive elements accessed by a number
+representing which element they are stored in.  Second is an associative
+array called a mapping.  A mapping associates to values together to
+allow a more natural access to data.
+
+3.2 The Values NULL and 0
+Before getting fully into arrays, there first should be a full understanding
+of the concept of NULL versus the concept of 0.  In LPC, a null value is
+represented by the integer 0.  Although the integer 0 and NULL are often
+freely interchangeable, this interchangeability often leads to some great
+confusion when you get into the realm of complex data types.  You may
+have even encountered such confusion while using strings.
+
+0 represents a value which for integers means the value you add to
+another value yet still retain the value added.  This for any addition
+operation on any data type, the ZERO value for that data type is the value
+that you can add to any other value and get the original value.  Thus:   A
+plus ZERO equals A where A is some value of a given data type and
+ZERO is the ZERO value for that data type.  This is not any sort of
+official mathematical definition.  There exists one, but I am not a
+mathematician, so I have no idea what the term is.  Thus for integers, 0
+is the ZERO value since 1 + 0 equals 1.
+
+NULL, on the other hand, is the absence of any value or meaning.  The
+LPC driver will interpret NULL as an integer 0 if it can make sense of it
+in that context.  In any context besides integer addition, A plus NULL
+causes an error.  NULL causes an error because adding valueless fields
+in other data types to those data types makes no sense.
+
+Looking at this from another point of view, we can get the ZERO value
+for strings by knowing what added to "a" will give us "a" as a result. 
+The answer is not 0, but instead "".  With integers, interchanging NULL
+and 0 was acceptable since 0 represents no value with respect to the
+integer data type.  This interchangeability is not true for other data types,
+since their ZERO values do not represent no value.  Namely, ""
+represents a string of no length and is very different from 0.  
+
+When you first declare any variable of any type, it has no value.  Any
+data type except integers therefore must be initialized somehow before
+you perform any operation on it.  Generally, initialization is done in the
+create() function for global variables, or at the top of the local function
+for local variables by assigning them some value, often the ZERO value
+for that data type.  For example, in the following code I want to build a
+string with random words:
+
+string build_nonsense() {
+    string str;
+    int i;
+
+    str = ""; /* Here str is initialized to the string
+ZERO value */
+    for(i=0; i<6; i++) {
+        switch(random(3)+1) {
+            case 1: str += "bing"; break;
+            case 2: str += "borg"; break;
+            case 3: str += "foo"; break;
+        }
+        if(i==5) str += ".\n";
+        else str += " ";
+    }
+    return capitalize(str);
+}
+
+If we had not initialized the variable str, an error would have resulted
+from trying to add a string to a NULL value.  Instead, this code first
+initializes str to the ZERO value for strings, "".  After that, it enters a
+loop which makes 6 cycles, each time randomly adding one of three
+possible words to the string.  For all words except the last, an additional
+blank character is added.  For the last word, a period and a return
+character are added.  The function then exits the loop, capitalizes the
+nonsense string, then exits.
+
+3.3 Arrays in LPC
+An array is a powerful complex data type of LPC which allows you to
+access multiple values through a single variable.  For instance,
+Nightmare has an indefinite number of currencies in which players may
+do business.  Only five of those currencies, however, can be considered
+hard currencies.  A hard currency for the sake of this example is a
+currency which is readily exchangeable for any other hard currency,
+whereas a soft currency may only be bought, but not sold.  In the bank,
+there is a list of hard currencies to allow bank keepers to know which
+currencies are in fact hard currencies.  With simple data types, we would
+have to perform the following nasty operation for every exchange
+transaction:
+
+int exchange(string str) {
+    string from, to;
+    int amt;
+
+    if(!str) return 0;
+    if(sscanf(str, "%d %s for %s", amt, from, to) != 3)
+      return 0;
+    if(from != "platinum" && from != "gold" && from !=
+      "silver" &&
+      from != "electrum" && from != "copper") {
+        notify_fail("We do not buy soft currencies!\n");
+        return 0;
+    }
+    ...
+}
+
+With five hard currencies, we have a rather simple example.  After all it
+took only two lines of code to represent the if statement which filtered
+out bad currencies.  But what if you had to check against all the names
+which cannot be used to make characters in the game?  There might be
+100 of those; would you want to write a 100 part if statement?
+What if you wanted to add a currency to the list of hard currencies?  That
+means you would have to change every check in the game for hard
+currencies to add one more part to the if clauses.  Arrays allow you
+simple access to groups of related data so that you do not have to deal
+with each individual value every time you want to perform a group
+operation.
+
+As a constant, an array might look like this:
+    ({ "platinum", "gold", "silver", "electrum", "copper" })
+which is an array of type string.  Individual data values in arrays are
+called elements, or sometimes members.  In code, just as constant
+strings are represented by surrounding them with "", constant arrays are
+represented by being surrounded by ({ }), with individual elements of
+the array being separated by a ,.
+
+You may have arrays of any LPC data type, simple or complex.  Arrays
+made up of mixes of values are called arrays of mixed type.  In most
+LPC drivers, you declare an array using a throw-back to C language
+syntax for arrays.  This syntax is often confusing for LPC coders
+because the syntax has a meaning in C that simply does not translate into
+LPC.  Nevertheless, if we wanted an array of type string, we would
+declare it in the following manner:
+
+string *arr;
+
+In other words, the data type of the elements it will contain followed by
+a space and an asterisk.  Remember, however, that this newly declared
+string array has a NULL value in it at the time of declaration.
+
+3.4 Using Arrays
+You now should understand how to declare and recognize an array in
+code.  In order to understand how they work in code, let's review the
+bank code, this time using arrays:
+
+string *hard_currencies;
+
+int exchange(string str) {
+    string from, to;
+    int amt;
+
+    if(!str) return 0;
+    if(sscanf(str, "%d %s for %s", amt, from, to) != 3)
+return 0;
+    if(member_array(from, hard_currencies) == -1) {
+        notify_fail("We do not buy soft currencies!\n");
+        return 0;
+    }
+    ...
+}
+
+This code assumes hard_currencies is a global variable and is initialized
+in create() as:
+    hard_currencies = ({ "platinum", "gold", "electrum", "silver",
+   "copper" });
+Ideally, you would have hard currencies as a #define in a header file for
+all objects to use, but #define is a topic for a later chapter.
+
+Once you know what the member_array() efun does, this method
+certainly is much easier to read as well as is much more efficient and
+easier to code.  In fact, you can probably guess what the
+member_array() efun does:  It tells you if a given value is a member of
+the array in question.  Specifically here, we want to know if the currency
+the player is trying to sell is an element in the hard_curencies array. 
+What might be confusing to you is, not only does member_array() tell us
+if the value is an element in the array, but it in fact tells us which element
+of the array the value is.
+
+How does it tell you which element?  It is easier to understand arrays if
+you think of the array variable as holding a number.  In the value above,
+for the sake of argument, we will say that hard_currencies holds the
+value 179000.  This value tells the driver where to look for the array
+hard_currencies represents.  Thus, hard_currencies points to a place
+where the array values may be found.  When someone is talking about
+the first element of the array, they want the element located at 179000. 
+When the object needs the value of the second element of the array, it
+looks at 179000 + one value, then 179000 plus two values for the third,
+and so on.  We can therefore access individual elements of an array by
+their index, which is the number of values beyond the starting point of
+the array we need to look to find the value.  For the array
+hard_currencies array:
+"platinum" has an index of 0.
+"gold" has an index of 1.
+"electrum" has an index of 2.
+"silver" has an index of 3.
+"copper" has an index of 4.
+
+The efun member_array() thus returns the index of the element being
+tested if it is in the array, or -1 if it is not in the array.  In order to
+reference an individual element in an array, you use its index number in
+the following manner:
+array_name[index_no]
+Example:
+hard_currencies[3]
+where hard_currencies[3] would refer to "silver".
+
+So, you now should now several ways in which arrays appear either as
+a whole or as individual elements.  As a whole, you refer to an array
+variable by its name and an array constant by enclosing the array in ({ })
+and separating elements by ,.  Individually, you refer to array variables
+by the array name followed by the element's index number enclosed in
+[], and to array constants in the same way you would refer to simple data
+types of the same type as the constant.  Examples:
+
+Whole arrays:
+variable:  arr
+constant: ({ "platinum", "gold", "electrum", "silver", "copper" })
+
+Individual members of arrays:
+variable: arr[2]
+constant: "electrum"
+
+You can use these means of reference to do all the things you are used to
+doing with other data types.  You can assign values, use the values in
+operations, pass the values as parameters to functions, and use the
+values as return types.  It is important to remember that when you are
+treating an element alone as an individual, the individual element is not
+itself an array (unless you are dealing with an array of arrays).  In the
+example above, the individual elements are strings.  So that:
+    str = arr[3] + " and " + arr[1];
+will create str to equal "silver and gold".  Although this seems simple
+enough, many people new to arrays start to run into trouble when trying
+to add elements to an array.  When you are treating an array as a whole
+and you wish to add a new element to it, you must do it by adding
+another array.
+
+Note the following example:
+string str1, str2;
+string *arr;
+
+str1 = "hi";
+str2 = "bye";
+/* str1 + str2 equals "hibye" */
+arr = ({ str1 }) + ({ str2 });
+/* arr is equal to ({ str1, str2 }) */
+Before going any further, I have to note that this example gives an
+extremely horrible way of building an array.  You should set it: arr = ({
+str1, str2 }).  The point of the example, however, is that you must add
+like types together.  If you try adding an element to an array as the data
+type it is, you will get an error.  Instead you have to treat it as an array of
+a single element.
+
+3.5 Mappings
+One of the major advances made in LPMuds since they were created is
+the mapping data type.  People alternately refer to them as associative
+arrays.  Practically speaking, a mapping allows you freedom from the
+association of a numerical index to a value which arrays require. 
+Instead, mappings allow you to associate values with indices which
+actually have meaning to you, much like a relational database.
+
+In an array of 5 elements, you access those values solely by their integer
+indices which cover the range 0 to 4.  Imagine going back to the example
+of money again.  Players have money of different amounts and different
+types.  In the player object, you need a way to store the types of money
+that exist as well as relate them to the amount of that currency type the
+player has.  The best way to do this with arrays would have been to
+store an array of strings representing money types and an array of
+integers representing values in the player object.  This would result in
+CPU-eating ugly code like this:
+
+int query_money(string type) {
+    int i;
+
+    i = member_array(type, currencies);
+    if(i>-1 && i < sizeof(amounts))  /* sizeof efun
+returns # of elements */
+        return amounts[i];
+    else return 0;
+}
+
+And that is a simple query function.  Look at an add function:
+
+void add_money(string type, int amt) {
+    string *tmp1;
+    int * tmp2;
+    int i, x, j, maxj;
+    
+    i = member_array(type, currencies);
+    if(i >= sizeof(amounts)) /*  corrupt data, we are in
+      a bad way */
+        return;
+    else if(i== -1) {
+        currencies += ({ type });
+        amounts += ({ amt });
+        return;
+    }
+    else {
+        amounts[i] += amt;
+        if(amounts[i] < 1) {
+            tmp1 = allocate(sizeof(currencies)-1);
+            tmp2 = allocate(sizeof(amounts)-1);
+            for(j=0, x =0, maxj=sizeof(tmp1); j < maxj;
+              j++) {
+                if(j==i) x = 1;
+                tmp1[j] = currencies[j+x];
+                tmp2[j] = amounts[j+x];
+            }
+            currencies = tmp1;
+            amounts = tmp2;
+        }
+    }
+}
+
+That is really some nasty code to perform the rather simple concept of
+adding some money.  First, we figure out if the player has any of that
+kind of money, and if so, which element of the currencies array it is. 
+After that, we have to check to see that the integrity of the currency data
+has been maintained.  If the index of the type in the currencies array is
+greater than the highest index of the amounts array, then we have a
+problem since the indices are our only way of relating the two arrays. 
+Once we know our data is in tact, if the currency type is not currently
+held by the player, we simply tack on the type as a new element to the
+currencies array and the amount as a new element to the amounts array. 
+Finally, if it is a currency the player currently has, we just add the
+amount to the corresponding index in the amounts array.  If the money
+gets below 1, meaning having no money of that type, we want to clear
+the currency out of memory.
+
+Subtracting an element from an array is no simple matter.  Take, for
+example, the result of the following:
+
+string *arr;
+
+arr = ({ "a", "b", "a" });
+arr -= ({ arr[2] });
+
+What do you think the final value of arr is? Well, it is:
+    ({ "b", "a" })
+Subtracting arr[2] from the original array does not remove the third
+element from the array.  Instead, it subtracts the value of the third
+element of the array from the array.  And array subtraction removes the
+first instance of the value from the array.  Since we do not want to be
+<* NOTE Highlander@MorgenGrauen 11.2.94:
+	WRONG in MorgenGrauen (at least). The result is actually ({ "b" }). Array
+	subtraction removes ALL instances of the subtracted value from the array.
+	This holds true for all Amylaar-driver LPMuds.
+*>
+forced on counting on the elements of the array as being unique, we are
+forced to go through some somersaults to remove the correct element
+from both arrays in order to maintain the correspondence of the indices
+in the two arrays.
+
+Mappings provide a better way.  They allow you to directly associate the
+money type with its value.  Some people think of mappings as arrays
+where you are not restricted to integers as indices.  Truth is, mappings
+are an entirely different concept in storing aggregate information.  Arrays
+force you to choose an index which is meaningful to the machine for
+locating the appropriate data.  The indices tell the machine how many
+elements beyond the first value the value you desire can be found.  With
+mappings, you choose indices which are meaningful to you without
+worrying about how that machine locates and stores it.
+
+You may recognize mappings in the following forms:
+
+constant values:
+whole: ([ index:value, index:value ]) Ex: ([ "gold":10, "silver":20 ])
+element:  10
+
+variable values:
+whole:    map   (where map is the name of a mapping variable)
+element: map["gold"]
+
+So now my monetary functions would look like:
+
+int query_money(string type) { return money[type]; }
+
+void add_money(string type, int amt) {
+    if(!money[type]) money[type] = amt;
+    else money[type] += amt;
+    if(money[type] < 1)
+      map_delete(money, type);          /* this is for
+          MudOS */
+            ...OR... 
+            money = m_delete(money, type)  /* for some
+          LPMud 3.* varieties */
+            ... OR... 
+         m_delete(money, type);    /* for other LPMud 3.*
+          varieties */
+}
+
+Please notice first that the efuns for clearing a mapping element from the
+mapping vary from driver to driver.  Check with your driver's
+documentation for the exact name an syntax of the relevant efun.
+
+As you can see immediately, you do not need to check the integrity of
+your data since the values which interest you are inextricably bound to
+one another in the mapping.  Secondly, getting rid of useless values is a
+simple efun call rather than a tricky, CPU-eating loop.  Finally, the
+query function is made up solely of a return instruction. 
+
+You must declare and initialize any mapping before using it. 
+Declarations look like:
+mapping map;
+Whereas common initializations look like:
+map = ([]);
+map = m_allocate(10)   ...OR...   map = m_allocate(10);
+map = ([ "gold": 20, "silver": 15 ]);
+
+As with other data types, there are rules defining how they work in
+common operations like addition and subtraction:
+    ([ "gold":20, "silver":30 ]) + ([ "electrum":5 ]) 
+gives:
+    (["gold":20, "silver":30, "electrum":5])   
+Although my demonstration shows a continuity of order, there is in fact
+no guarantee of the order in which elements of mappings will stored. 
+Equivalence tests among mappings are therefore not a good thing.
+
+3.6 Summary
+Mappings and arrays can be built as complex as you need them to be. 
+You can have an array of mappings of arrays.  Such a thing would be
+declared like this:
+
+mapping *map_of_arrs;
+which might look like:
+({ ([ ind1: ({ valA1, valA2}), ind2: ({valB1, valB2}) ]), ([ indX:
+({valX1,valX2}) ]) })
+
+Mappings may use any data type as an index, including objects. 
+Mapping indices are often referred to as keys as well, a term from
+databases.  Always keep in mind that with any non-integer data type,
+you must first initialize a variable before making use of it in common
+operations such as addition and subtraction.  In spite of the ease and
+dynamics added to LPC coding by mappings and arrays, errors caused
+by failing to initialize their values can be the most maddening experience
+for people new to these data types.  I would venture that a very high
+percentage of all errors people experimenting with mappings and arrays
+for the first time encounter are one of three error messages:
+	Indexing on illegal type.
+	Illegal index.
+	Bad argument 1 to (+ += - -=) /* insert your favourite operator */
+Error messages 1 and 3 are darn near almost always caused by a failure
+to initialize the array or mapping in question.  Error message 2 is caused
+generally when you are trying to use an index in an initialized array
+which does not exist.  Also, for arrays, often people new to arrays will
+get error message 3 because they try to add a single element to an array
+by adding the initial array to the single element value instead of adding
+an array of the single element to the initial array.  Remember, add only
+arrays to arrays.
+
+At this point, you should feel comfortable enough with mappings and
+arrays to play with them.  Expect to encounter the above error messages
+a lot when first playing with these.  The key to success with mappings is
+in debugging all of these errors and seeing exactly what causes wholes
+in your programming which allow you to try to work with uninitialized
+mappings and arrays.  Finally, go back through the basic room code and
+look at things like the set_exits() (or the equivalent on your mudlib)
+function.  Chances are it makes use of mappings.  In some instances, it
+will use arrays as well for compatibility with mudlib.n.
+
+Copyright (c) George Reese 1993
diff --git a/doc/KURS/LPC-KURS2/chapter4 b/doc/KURS/LPC-KURS2/chapter4
new file mode 100644
index 0000000..c3e9905
--- /dev/null
+++ b/doc/KURS/LPC-KURS2/chapter4
@@ -0,0 +1,195 @@
+Intermediate LPC
+Descartes of Borg
+November 1993
+
+                       Chapter 4: The LPC Pre-Compiler
+
+4.1 Review
+The previous chapter was quite heavy, so now I will slow down a bit so
+you can digest and play with mappings and arrays by taking on the
+rather simple topic of the LPC pre-compiler.  By this point, however,
+you should well understand how the driver interacts with the mudlib and
+be able to code objects which use call outs and heart beats.  In addition,
+you should be coding simple objects which use mappings and arrays,
+noting how these data types perform in objects.  It is also a good idea to
+start looking in detail at the actual mudlib code that makes up your mud. 
+See if you understand everything which is going on in your mudlibs
+room and monster codes.  For things you do not understand, ask the
+people on your mud designated to answer creator coding questions.
+
+Pre-compiler is actually a bit of a misnomer since LPC code is never
+truly compiled.  Although this is changing with prototypes of newer
+LPC drivers, LPC drivers interpret the LPC code written by creators
+rather than compile it into binary format.  Nevertheless, the LPC pre-
+compiler functions still perform much like pre-compilers for compiled
+languages in that pre-compiler directives are interpreted before the driver
+even starts to look at object code. 
+
+4.2 Pre-compiler Directives
+If you do not know what a pre-compiler is, you really do not need to
+worry.  With respect to LPC, it is basically a process which happens
+before the driver begins to interpret LPC code which allows you to
+perform actions upon the entire code found in your file.  Since the code
+is not yet interpreted, the pre-compiler process is involved before the file
+exists as an object and before any LPC functions or instructions are ever
+examined.  The pre-compiler is thus working at the file level, meaning
+that it does not deal with any code in inherited files.
+
+The pre-compiler searches a file sent to it for pre-compiler directives. 
+These are little instructions in the file meant only for the pre-compiler
+and are not really part of the LPC language.  A pre-compiler directive is
+any line in a file beginning with a pound (#) sign.  Pre-compiler
+directives are generally used to construct what the final code of a file will
+look at.  The most common pre-compiler directives are:
+
+#define
+#undefine
+#include
+#ifdef
+#ifndef
+#if
+#elseif
+#else
+#endif
+#pragma
+
+Most realm coders on muds use exclusively the directives #define and
+#include.  The other directives you may see often and should understand
+what they mean even if you never use them.
+
+The first pair of directives are:
+#define
+#undefine
+
+The #define directive sets up a set of characters which will be replaced
+any where they exist in the code at precompiler time with their definition. 
+For example, take:
+
+#define OB_USER "/std/user"
+
+This directive has the pre-compiler search the entire file for instances of
+OB_USER.  Everywhere it sees OB_USER, it replaces with "/std/user". 
+<* NOTE Highlander@MorgenGrauen 11.2.94:
+	WRONG. OB_USER will _not_ be replaced if within "" in which case it is
+	treated as a normal string. So it is possible to write the text OB_USER.
+*>
+Note that it does not make OB_USER a variable in the code.  The LPC
+interpreter never sees the OB_USER label.  As stated above, the pre-
+compiler is a process which takes place before code interpretation.  So
+what you wrote as:
+
+#define OB_USER "/std/user"
+
+void create() {
+    if(!file_exists(OB_USER+".c")) write("Merde! No user file!");
+    else write("Good! User file still exists!");
+}
+
+would arrive at the LPC interpreter as:
+
+void create() {
+    if(!file_exists("/std/user"+".c")) write("Merde! No user file!");
+    else write("Good! User file still exists!");
+}
+
+<* NOTE Highlander@MorgenGrauen 11.2.94
+	But: write("Text is OB_USER foo bar\n");
+	simply writes "Text is OB_USER foo bar". Confer previous note.
+*>
+
+Simply put, #define just literally replaces the defined label with whatever
+follows it.  You may also use #define in a special instance where no
+value follows.  This is called a binary definition.  For example:
+
+#define __NIGHTMARE
+
+exists in the config file for the Nightmare Mudlib.  This allows for pre-
+compiler tests which will be described later in the chapter.
+
+The other pre-compiler directive you are likely to use often is #include. 
+As the name implies, #include includes the contents of another file right
+into the file being pre-compiled at the point in the file where the directive
+is placed.  Files made for inclusion into other files are often called header
+files.  They sometimes contain things like #define directives used by
+multiple files and function declarations for the file.  The traditional file
+extension to header files is .h.
+
+Include directives follow one of 2 syntax's:
+
+#include <filename>
+#include "filename"
+
+If you give the absolute name of the file, then which syntax you use is
+irrelevant.  How you enclose the file name determines how the pre-
+compiler searches for the header files.  The pre-compiler first searches in
+system include directories for files enclosed in <>.  For files enclosed in
+"", the pre-compiler begins its search in the same directory as the file
+going through the pre-compiler.  Either way, the pre-compiler will
+search the system include directories and the directory of the file for the
+header file before giving up.  The syntax simply determines the order.
+<* NOTE Highlander@MorgenGrauen 11.2.94
+	When using standard-headerfiles one should choose <>. "" is appropriate
+	when dealing with selfdefined headerfiles.
+*>
+
+The simplest pre-compiler directive is the #pragma directive.  It is
+doubtful you will ever use this one.  Basically, you follow the directive
+with some keyword which is meaningful to your driver.  The only
+keyword I have ever seen is strict_types, which simply lets the driver
+know you want this file interpreted with strict data typing.  I doubt you
+will ever need to use this, and you may never even see it.  I just included
+it in the list in the event you do see it so you do not think it is doing
+anything truly meaningful.
+
+The final group of pre-compiler directives are the conditional pre-
+compiler directives.  They allow you to pre-compile the file one way
+given the truth value of an expression, otherwise pre-compile the file
+another way.  This is mostly useful for making code portable among
+mudlibs, since putting the m_delete() efun in code on a MudOS mud
+would normally cause an error, for example.  So you might write the
+following:
+
+#ifdef  MUDOS
+    map_delete(map, key);
+#else
+    map = m_delete(map, key);
+#endif
+
+which after being passed through the pre-compiler will appear to the
+interpreter as:
+
+    map_delete(map, key);   
+
+on a MudOS mud, and:
+  
+    map = m_delete(map, key);
+
+on other muds.  The interpreter never sees the function call that would
+cause it to spam out in error. 
+
+Notice that my example made use of a binary definition as described
+above.  Binary definitions allow you to pass certain code to the
+interpreter based on what driver or mudlib you are using, among other
+conditions.
+
+4.3 Summary
+The pre-compiler is a useful LPC tool for maintaining modularity among
+your programs.  When you have values that might be subject to change,
+but are used widely throughout your files, you might stick all of those
+values in a header file as #define statements so that any need to make a
+future change will cause you to need to change just the #define directive. 
+A very good example of where this would be useful would be a header
+file called money.h  which includes the directive:
+#define HARD_CURRENCIES ({ "gold", "platinum", "silver",
+"electrum", "copper" })
+so that if ever you wanted to add a new hard currency, you only need
+change this directive in order to update all files needing to know what the
+hard currencies are.
+
+The LPC pre-compiler also allows you to write code which can be
+ported without change among different mudlibs and drivers.  Finally,
+you should be aware that the pre-compiler only accepts lines ending in
+carriage returns.  If you want a multiple line pre-compiler directive, you
+need to end each incomplete line with a backslash(\).
+
+Copyright (c) George Reese 1993
diff --git a/doc/KURS/LPC-KURS2/chapter5 b/doc/KURS/LPC-KURS2/chapter5
new file mode 100644
index 0000000..f45b6fa
--- /dev/null
+++ b/doc/KURS/LPC-KURS2/chapter5
@@ -0,0 +1,186 @@
+Intermediate LPC
+Descartes of Borg
+November 1993
+
+                     Chapter 5: Advanced String Handling
+
+5.1 What a String Is
+The LPC Basics textbook taught strings as simple data types.  LPC
+generally deals with strings in such a matter.  The underlying driver
+program, however, is written in C, which has no string data type.  The
+driver in fact sees strings as a complex data type made up of an array of
+characters, a simple C data type.  LPC, on the other hand does not
+recognize a character data type (there may actually be a driver or two out
+there which do recognize the character as a data type, but in general not). 
+The net effect is that there are some array-like things you can do with
+strings that you cannot do with other LPC data types.
+
+The first efun regarding strings you should learn is the strlen() efun. 
+This efun returns the length in characters of an LPC string, and is thus
+the string equivalent to sizeof() for arrays.  Just from the behaviour of
+this efun, you can see that the driver treats a string as if it were made up
+of smaller elements.  In this chapter, you will learn how to deal with
+strings on a more basic level, as characters and sub strings.
+
+5.2 Strings as Character Arrays
+You can do nearly anything with strings that you can do with arrays,
+except assign values on a character basis.  At the most basic, you can
+actually refer to character constants by enclosing them in '' (single
+quotes).  'a' and "a" are therefore very different things in LPC.  'a'
+represents a character which cannot be used in assignment statements or
+any other operations except comparison evaluations.  "a" on the other
+hand is a string made up of a single character.  You can add and subtract
+other strings to it and assign it as a value to a variable.
+
+With string variables, you can access the individual characters to run
+comparisons against character constants using exactly the same syntax
+that is used with arrays.  In other words, the statement:
+    if(str[2] == 'a')
+is a valid LPC statement comparing the second character in the str string
+to the character 'a'.  You have to be very careful that you are not
+comparing elements of arrays to characters, nor are you comparing
+characters of strings to strings.
+
+LPC also allows you to access several characters together using LPC's
+range operator ..:
+    if(str[0..1] == "ab")
+In other words, you can look for the string which is formed by the
+characters 0 through 1 in the string str.  As with arrays, you must be
+careful when using indexing or range operators so that you do not try to
+reference an index number larger than the last index.  Doing so will
+result in an error.
+
+Now you can see a couple of similarities between strings and arrays:
+1) You may index on both to access the values of individual elements.
+	a) The individual elements of strings are characters
+	b) The individual elements of arrays match the data type of the
+array.
+2) You may operate on a range of values
+	a) Ex: "abcdef"[1..3] is the string "bcd"
+	b) Ex: ({ 1, 2, 3, 4, 5 })[1..3] is the int array ({ 2, 3, 4 })
+<* NOTE Highlander@MorgenGrauen
+	Also possible in MorgenGrauen (in common: Amylaar-driver LPMuds):
+	"abcdef"[2..]    -> "cdef" and
+	"abcdef"[1..<2]  -> "bcde"  (< means start counting from the end and with 1)
+*>
+
+And of course, you should always keep in mind the fundamental
+difference: a string is not made up of a more fundamental LPC data type. 
+In other words, you may not act on the individual characters by
+assigning them values.
+
+5.3 The Efun sscanf()
+You cannot do any decent string handling in LPC without using
+sscanf().  Without it, you are left trying to play with the full strings
+passed by command statements to the command functions.  In other
+words, you could not handle a command like: "give sword to leo", since
+you would have no way of separating "sword to leo" into its constituent
+parts.  Commands such as these therefore use this efun in order to use
+commands with multiple arguments or to make commands more
+"English-like".
+
+Most people find the manual entries for sscanf() to be rather difficult
+reading.  The function does not lend itself well to the format used by
+manual entries.  As I said above, the function is used to take a string and
+break it into usable parts.  Technically it is supposed to take a string and
+scan it into one or more variables of varying types.  Take the example
+above:
+
+int give(string str) {
+    string what, whom;
+
+    if(!str) return notify_fail("Give what to whom?\n");
+    if(sscanf(str, "%s to %s", what, whom) != 2) 
+      return notify_fail("Give what to whom?\n");
+    ... rest of give code ...
+}
+
+The efun sscanf() takes three or more arguments.  The first argument is
+the string you want scanned.  The second argument is called a control
+string.  The control string is a model which demonstrates in what form
+the original string is written, and how it should be divided up.  The rest
+of the arguments are variables to which you will assign values based
+upon the control string.
+
+The control string is made up of three different types of elements: 1)
+constants, 2) variable arguments to be scanned, and 3) variable
+arguments to be discarded.  You must have as many of the variable
+arguments in sscanf() as you have elements of type 2 in your control
+string.  In the above example, the control string was "%s to %s", which
+is a three element control string made up of one constant part (" to "),
+and two variable arguments to be scanned ("%s").  There were no
+variables to be discarded.
+
+The control string basically indicates that the function should find the
+string " to " in the string str.  Whatever comes before that constant will
+be placed into the first variable argument as a string.  The same thing
+will happen to whatever comes after the constant.
+
+Variable elements are noted by a "%" sign followed by a code for
+decoding them.  If the variable element is to be discarded, the "%" sign
+is followed by the "*" as well as the code for decoding the variable. 
+Common codes for variable element decoding are "s" for strings and "d"
+for integers.  In addition, your mudlib may support other conversion
+codes, such as "f" for float.  So in the two examples above, the "%s" in
+the control string indicates that whatever lies in the original string in the
+corresponding place will be scanned into a new variable as a string.
+
+A simple exercise.  How would you turn the string "145" into an
+integer?
+
+Answer:
+int x;
+sscanf("145", "%d", x);
+
+After the sscanf() function, x will equal the integer 145.
+
+Whenever you scan a string against a control string, the function
+searches the original string for the first instance of the first constant in
+the original string.  For example, if your string is "magic attack 100" and
+you have the following:
+int improve(string str) {
+    string skill;
+    int x;
+
+    if(sscanf(str, "%s %d", skill, x) != 2) return 0;
+    ...
+}
+you would find that you have come up with the wrong return value for
+sscanf() (more on the return values later).  The control string, "%s %d",
+is made up of to variables to be scanned and one constant.  The constant
+is " ".  So the function searches the original string for the first instance
+of " ", placing whatever comes before the " " into skill, and trying to
+place whatever comes after the " " into x.  This separates "magic attack
+100" into the components "magic" and "attack 100".  The function,
+however, cannot make heads or tales of "attack 100" as an integer, so it
+returns 1, meaning that 1 variable value was successfully scanned
+("magic" into skill).
+
+Perhaps you guessed from the above examples, but the efun sscanf()
+returns an int, which is the number of variables into which values from
+the original string were successfully scanned.  Some examples with
+return values for you to examine:
+
+sscanf("swo  rd descartes", "%s to %s", str1, str2)           return: 0
+sscanf("swo  rd descartes", "%s %s", str1, str2)              return: 2
+sscanf("200 gold to descartes", "%d %s to %s", x, str1, str2) return: 3
+sscanf("200 gold to descartes", "%d %*s to %s", x, str1)      return: 2
+where x is an int and str1 and str2 are string
+
+5.4 Summary
+LPC strings can be thought of as arrays of characters, yet always
+keeping in mind that LPC does not have the character data type (with
+most, but not all drivers).  Since the character is not a true LPC data
+type, you cannot act upon individual characters in an LPC string in the
+same manner you would act upon different data types.  Noticing the
+intimate relationship between strings and arrays nevertheless makes it
+easier to understand such concepts as the range operator and indexing on
+strings.
+
+There are efuns other than sscanf() which involve advanced string
+handling, however, they are not needed nearly as often.  You should
+check on your mud for man or help files on the efuns: explode(),
+implode(), replace_string(), sprintf().  All of these are very valuable
+tools, especially if you intend to do coding at the mudlib level.
+
+Copyright (c) George Reese 1993
diff --git a/doc/KURS/LPC-KURS2/chapter6 b/doc/KURS/LPC-KURS2/chapter6
new file mode 100644
index 0000000..98b3552
--- /dev/null
+++ b/doc/KURS/LPC-KURS2/chapter6
@@ -0,0 +1,276 @@
+Intermediate LPC
+Descartes of Borg
+November 1993
+
+                       Chapter 6: Intermediate Inheritance
+
+6.1 Basics of Inheritance
+In the textbook LPC Basics, you learned how it is the mudlib maintains
+consistency amoung mud objects through inheritance.  Inheritance
+allows the mud administrators to code the basic functions and such that
+all mudlib objects, or all mudlib objects of a certain type must have so
+that you can concentrate on creating the functions which make these
+objects different.  When you build a room, or a weapon, or a monster,
+you are taking a set of functions already written for you and inheriting
+them into your object.  In this way, all objects on the mud can count on
+other objects to behave in a certain manner.  For instance, player objects
+can rely on the fact that all room objects will have a function in them
+called query_long() which describes the room.  Inheritance thus keeps
+you from having to worry about what the function query_long() should
+look like.
+
+Naturally, this textbook tries to go beyond this fundamental knowledge
+of inheritance to give the coder a better undertstanding of how
+inheritance works in LPC programming.  Without getting into detail that
+the advanced domain coder/beginner mudlib coder simply does not yet
+need, this chapter will try to explain exactly what happens when you
+inherit an object.
+
+6.2 Cloning and Inheritance
+Whenever a file is referenced for the first time as an object (as opposed
+to reading the contents of the file), the game tries to load the file into
+memory and create an object.  If the object is successfully loaded into
+memory, it becomes as master copy.  Master copies of objects may be
+cloned but not used as actual game objects.  The master copy is used to
+support any clone objects in the game.
+
+The master copy is the source of one of the controversies of mud LPC
+coding, that is whether to clone or inherit.  With rooms, there is no
+question of what you wish to do, since there should only be one instance
+of each room object in the game.  So you generally use inheritance in
+creating rooms.  Many mud administrators, including myself, however
+encourage creators to clone the standard monster object and configure it
+from inside room objects instead of keeping monsters in separate files
+which inherit the standard monster object.
+
+As I stated above, each time a file is referenced to create an object, a
+master copy is loaded into memory.  When you do something like:
+void reset() {
+    object ob;
+    ob = new("/std/monster");
+      /* clone_object("/std/monster") some places */
+    ob->set_name("foo monster");
+    ...  rest of monster config code followed by moving
+it to the room ...
+}
+the driver searches to see if their is a master object called "/std/monster". 
+If not, it creates one.  If it does exist, or after it has been created, the
+driver then creates a clone object called "/std/monster#<number>".  If
+this is the first time "/std/monster" is being referenced, in effect, two
+objects are being created: the master object and the cloned instance.
+
+On the other hand, let's say you did all your configuring in the create()
+of a special monster file which inherits "/std/monster".  Instead of
+cloning the standard monster object from your room, you clone your
+monster file.  If the standard monster has not been loaded, it gets loaded
+since your monster inherits it.  In addition, a master copy of your file
+gets loaded into memory.  Finally, a clone of your monster is created
+and moved into the room, for a total of three objects added to the game. 
+Note that you cannot make use of the master copy easily to get around
+this.  If, for example, you were to do:
+    "/wizards/descartes/my_monster"->move(this_object());
+instead of
+    new("/wizards/descartes/my_monster")->move(this_object());
+you would not be able to modify the file "my_monster.c" and update it,
+since the update command destroys the current master version of an
+object.  On some mudlibs it also loads the new version into memory. 
+Imagine the look on a player's face when their monster disappears in
+mid-combat cause you updated the file!
+
+Cloning is therefore a useful too when you plan on doing just that-
+cloning.  If you are doing nothing special to a monster which cannot be
+done through a few call others, then you will save the mud from getting
+loaded with useless master copies.  Inheritance, however, is useful if
+you plan to add functionality to an object (write your own functions) or
+if you have a single configuration that gets used over and over again
+(you have an army of orc guards all the same, so you write a special orc
+file and clone it).
+
+6.3 Inside Inheritance
+When objects A and B inherit object C, all three objects have their own
+set of data sharing one set of function definitions from object C.  In
+addition, A and B will have separate functions definitions which were
+entered separately into their code.  For the sake of example throughout
+the rest of the chapter, we will use the following code.  Do not be
+disturbed if, at this point, some of the code makes no sense:
+
+OBJECT C
+private string name, cap_name, short, long;
+private int setup;
+
+void set_name(string str)
+nomask string query_name();
+private int query_setup();
+static void unsetup();
+void set_short(string str);
+string query_short();
+void set_long(string str);
+string query_long();
+
+
+void set_name(string str) { 
+    if(!query_setup()) {
+        name = str;
+    setup = 1;
+}
+
+nomask string query_name() { return name; }
+
+private query_setup() { return setup; }
+
+static void unsetup() { setup = 0; }
+
+string query_cap_name() {
+    return (name ? capitalize(name) : ""); }
+}
+
+void set_short(string str) { short = str; }
+
+string query_short() { return short; }
+
+void set_long(string str) { long = str; }
+
+string query_long() { return str; }
+
+void create() { seteuid(getuid()); }
+
+OBJECT B
+inherit "/std/objectc";
+
+private int wc;
+
+void set_wc(int wc);
+int query_wc();
+int wieldweapon(string str);
+
+void create() { ::create(); }
+
+void init() {
+    if(environment(this_object()) == this_player())
+      add_action("wieldweapon", "wield");
+}
+
+void set_wc(int x) { wc = x; }
+
+int query_wc() { return wc; }
+
+int wieldweapon(string str) {
+    ... code for wielding the weapon ...
+}
+
+OBJECT A
+inherit "/std/objectc";
+
+int ghost;
+
+void create() { ::create(); }
+
+void change_name(string str) {
+    if(!((int)this_object()->is_player())) unsetup();
+    set_name(str);
+}
+
+string query_cap_name() {
+    if(ghost) return "A ghost";
+    else return ::query_cap_name();
+}
+
+As you can see, object C is inherited both by object A and object B. 
+Object C is a representation of a much oversimplified base object, with B
+being an equally oversimplified weapon and A being an equally
+simplified living object.  Only one copy of each function is retained in
+memory, even though we have here three objects using the functions. 
+There are of course, three instances of the variables from Object C in
+memory, with one instance of the variables of Object A and Object B in
+memory.  Each object thus gets its own data.  
+
+6.4 Function and Variable Labels
+Notice that many of the functions above are proceeded with labels which
+have not yet appeared in either this text or the beginner text, the labels
+static, private, and nomask.  These labels define special priveledges
+which an object may have to its data and member functions.  Functions
+you have used up to this point have the default label public.  This is
+default to such a degree, some drivers do not support the labeling.
+
+A public variable is available to any object down the inheritance tree
+from the object in which the variable is declared.  Public variables in
+object C may be accessed by both objects A and B.  Similarly, public
+functions may be called by any object down the inheritance tree from the
+object in which they are declared. 
+
+The opposite of public is of course private.  A private variable or
+function may only be referenced from inside the object which declares it. 
+If object A or B tried to make any reference to any of the variables in
+object C, an error would result, since the variables are said to be out of
+scope, or not available to inheriting classes due to their private labels. 
+Functions, however, provide a unique challenge which variables do not. 
+External objects in LPC have the ability to call functions in other objects
+through call others.  The private label does not protect against call
+others.
+
+To protect against call others, functions use the label static.  A function
+which is static may only be called from inside the complete object or
+from the game driver.  By complete object, I mean object A can call
+static functions in the object C it inherits.  The static only protects against
+external call others.  In addition, this_object()->foo() is considered an
+internal call as far as the static label goes.
+
+Since variables cannot be referenced externally, there is no need for an
+equivalent label for them.  Somewhere along the line, someone decided
+to muddy up the waters and use the static label with variables to have a
+completely separate meaning.  What is even more maddening is that this
+label has nothing to do with what it means in the C programming
+language.  A static variable is simply a variable that does not get saved to
+file through the efun save_object() and does not get restored through
+restore_object().  Go figure.
+
+In general, it is good practice to have private variables with public
+functions, using query_*() functions to access the values of inherited
+variables, and set_*(), add_*(), and other such functions to change
+those values.  In realm coding this is not something one really has to
+worry a lot about.  As a matter of fact, in realm coding you do not have
+to know much of anything which is in this chapter.  To be come a really
+good realm coder, however, you have to be able to read the mudlib
+code.  And mudlib code is full of these labels.  So you should work
+around with these labels until you can read code and understand why it
+is written that way and what it means to objects which inherit the code.
+
+The final label is nomask, and it deals with a property of inheritance
+which allows you to rewrite functions which have already been defined. 
+For example, you can see above that object A rewrote the function
+query_cap_name().  A rewrite of  function is called overriding the
+function.  The most common override of a function would be in a case
+like this, where a condition peculiar to our object (object A) needs to
+happen on a call ot the function under certain circumstances.  Putting test
+code into object C just so object A can be a ghost is plain silly.  So
+instead, we override query_cap_name() in object A, testing to see if the
+object is a ghost.  If so, we change what happens when another object
+queries for the cap name.  If it is not a ghost, then we want the regular
+object behaviour to happen.  We therefore use the scope resolution
+operator (::) to call the inherited version of the query_cap_name()
+function and return its value.
+
+A nomask function is one which cannot be overridden either through
+inheritance or through shadowing.  Shadowing is a sort of backwards
+inheritance which will be detailed in the advanced LPC textbook.  In the
+example above, neither object A nor object B (nor any other object for
+that matter) can override query_name().  Since we want to use
+query_name() as a unique identifier of objects, we don't want people
+faking us through shadowing or inheritance.  The function therefore gets
+the nomask label.
+
+6.5 Summary
+Through inheritance, a coder may make user of functions defined in
+other objects in order to reduce the tedium of producing masses of
+similar objects and to increase the consistency of object behaviour across
+mudlib objects.  LPC inheritance allows objects maximum priveledges in
+defining how their data can be accessed by external objects as well as
+objects inheriting them.  This data security is maintained through the
+keywords, nomask, private, and static.
+
+In addition, a coder is able to change the functionality of non-protected
+functions by overriding them.  Even in the process of overriding a
+function, however, an object may access the original function through
+the scope resolution operator.
+
+Copyright (c) George Reese 1993
diff --git a/doc/KURS/LPC-KURS2/chapter7 b/doc/KURS/LPC-KURS2/chapter7
new file mode 100644
index 0000000..aa8cc0e
--- /dev/null
+++ b/doc/KURS/LPC-KURS2/chapter7
@@ -0,0 +1,298 @@
+Intermediate LPC
+Descartes of Borg
+November 1993
+
+                            Chapter 7: Debugging
+
+7.1 Types of Errors
+By now, you have likely run into errors here, there, and everywhere.  In
+general, there are three sorts of errors you might see: compile time
+errors, run time errors, and malfunctioning code.  On most muds you
+will find a personal file where your compile time errors are logged.  For
+the most part, this file can be found either in your home directory as the
+file named "log" or ".log", or somewhere in the directory "/log" as a file
+with your name..  In addition, muds tend to keep a log of run time errors
+which occur while the mud is up.  Again, this is generally found in
+"/log".  On MudOS muds it is called "debug.log".  On other muds it may
+be called something different like "lpmud.log".  Ask your administrators
+where compile time and run time errors are each logged if you do not
+already know.
+
+Compile time errors are errors which occur when the driver tries to load
+an object into memory.  If, when the driver is trying to load an object
+into memory, it encounters things which it simply does not understand
+with respect to what you wrote, it will fail to load it into memory and log
+why it could not load the object into your personal error log.  The most
+common compile time errors are typos, missing or extra (), {}. [], or "",
+and failure to declare properly functions and variables used by the
+object.
+
+Run time errors occur when something wrong happens to an object in
+memory while it is executing a statement.  For example, the driver
+cannot tell whether the statement "x/y" will be valid in all circumstances. 
+In fact, it is a valid LPC expression.  Yet, if the value of y is 0, then a
+run time error will occur since you cannot divide by 0.  When the driver
+runs across an error during the execution of a function, it aborts
+execution of the function and logs an error to the game's run time error
+log.  It will also show the error to this_player(), if defined, if the player
+is a creator, or it will show "What?" to players.  Most common causes
+for run time errors are bad values and trying to perform operations with
+data types for which those operations are not defined.
+
+The most insideous type of error, however, is plain malfunctioning
+code.  These errors do not log, since the driver never really realizes that
+anything is wrong.  In short, this error happens when you think the code
+says one thing, but in fact it says another thing.  People too often
+encounter this bug and automatically insist that it must be a mudlib or
+driver bug.  Everyone makes all types of errors though, and more often
+than not when code is not functioning the way you should, it will be
+because you misread it.  
+
+7.2 Debugging Compile Time Errors
+Compile time errors are certainly the most common and simplest bugs to
+debug.  New coders often get frustrated by them due to the obscure
+nature of some error messages.  Nevertheless, once a person becomes
+used to the error messages generated by their driver, debugging compile
+time errors becomes utterly routine.
+
+In your error log, the driver will tell you the type of error and on which
+line it finally noticed there was an error.  Note that this is not on which
+line the actual error necessarily exists.  The most common compile time
+error, besides the typo, is the missing or superfluous parentheses,
+brackets, braces, or quotes.  Yet this error is the one that most baffles
+new coders, since the driver will not notice the missing or extra piece
+until well after the original.  Take for example the following code:
+
+1 int test(string str) {
+2    int x;
+3    for(x =0; x<10; x++)
+4        write(x+"\n");
+5    }
+6    write("Done.\n");
+7 }
+
+Depending on what you intended, the actual error here is either at line 3
+(meaning you are missing a {) or at line 5 (meaing you have an extra }). 
+Nevertheless, the driver will report that it found an error when it gets to
+line 6.  The actual driver message may vary from driver to driver, but no
+matter which driver, you will see an error on line 6, since the } in line 5
+is interpreted as ending the function test().  At line 6, the driver sees that
+you have a write() sitting outside any function definition, and thus
+reports an error.  Generally, the driver will also go on to report that it
+found an error at line 7 in the form of an extra }.
+
+The secret to debugging these is coding style.  Having closing } match
+up vertically with the clauses they close out helps you see where you are
+missing them when you are debugging code.  Similarly, when using
+multiple sets of parentheses, space out different groups like this:
+    if( (x=sizeof(who=users()) > ( (y+z)/(a-b) + (-(random(7))) ) ) 
+As you can see, the parentheses for the for() statement, are spaced out
+from the rest of the statement.  In addition, individual sub-groups are
+spaced so they can easily be sorted out in the event of an error.
+
+Once you have a coding style which aids in picking these out, you learn
+which error messages tend to indicate this sort of error.  When
+debugging this sort of error, you then view a section of code before and
+after the line in question.  In most all cases, you will catch the bug right
+off.
+
+Another common compile time error is where the driver reports an
+unknown identifier.  Generally, typos and failure to declare variables
+causes this sort of error.  Fortunately, the error log will almost always
+tell you exactly where the error is.  So when debugging it, enter the
+editor and find the line in question.  If the problem is with a variable and
+is not a typo, make sure you declared it properly.  On the other hand, if
+it is a typo, simply fix it!
+
+One thing to beware of, however, is that this error will sometimes be
+reported in conjunction with a missing parentheses, brackets, or braces
+type error.  In these situations, your problem with an unknown identifier
+is often bogus.  The driver misreads the way the {} or whatever are
+setup, and thus gets variable declarations confused.  Therefore make
+sure all other compile time errors are corrected before bothering with
+these types of errors.
+
+In the same class with the above error, is the general syntax error.  The
+driver generates this error when it simply fails to understand what you
+said.  Again, this is often caused by typos, but can also be caused by not
+properly understanding the syntax of a certain feature like writing a for()
+statement: for(x=0, x<10, x++).  If you get an error like this which is
+not a syntax error, try reviewing the syntax of the statement in which the
+error is occurring.
+
+7.3 Debugging Run Time Errors
+Run time errors are much more complex than their compile time
+counterparts.  Fortunately these errors do get logged, though many
+creators do not realise or they do not know where to look.  The error log
+for run time errors are also generally much more detailed than compile
+time errors, meaning that you can trace the history of the execution train
+from where it started to where it went wrong.  You therefore can setup
+debugging traps using precompiler statements much easier using these
+logs.  Run time errors, however, tend to result from using more
+complex codign techniques than beginners tend to use, which means you
+are left with errors which are generally more complicated than simple
+compile time errors.
+
+Run time errors almost always result from misusing LPC data types. 
+Most commonly, trying to do call others using object variables which are
+NULL, indexing on mapping, array, or string variables which are
+NULL, or passing bad arguments to functions.  We will look at a real
+run time error log from Nightmare:
+
+Bad argument 1 to explode()
+program: bin/system/_grep.c, object: bin/system/_grep
+line 32
+'       cmd_hook' in '        std/living.c' ('      
+std/user#4002')line 83
+'       cmd_grep' in '  bin/system/_grep.c' ('   
+bin/system/_grep')line 32     
+Bad argument 2 to message()
+program: adm/obj/simul_efun.c, object: adm/obj/simul_efun
+line 34
+'       cmd_hook' in '        std/living.c' ('      
+std/user#4957')line 83
+'       cmd_look' in '  bin/mortal/_look.c' ('   
+bin/mortal/_look')line 23
+' examine_object' in '  bin/mortal/_look.c' ('   
+bin/mortal/_look')line 78
+'          write' in 'adm/obj/simul_efun.c' (' 
+adm/obj/simul_efun')line 34
+Bad argument 1 to call_other()
+program: bin/system/_clone.c, object: bin/system/_clone
+line 25
+'       cmd_hook' in '        std/living.c' ('      
+std/user#3734')line 83
+'      cmd_clone' in ' bin/system/_clone.c' ('  
+bin/system/_clone')line 25
+Illegal index
+program: std/monster.c, object:
+wizards/zaknaifen/spy#7205 line 76
+'     heart_beat' in '       std/monster.c'
+('wizards/zaknaifen/spy#7205')line
+76                                                                                  
+
+All of the errors, except the last one, involve passing a bad argument to a
+function.  The first bug, involves passing a bad first arument to the efun
+explode().  This efun expects a string as its first argment.  In debugging
+these kinds of errors, we would therefore go to line 32 in
+/bin/system/_grep.c and check to see what the data type of the first
+argument being passed in fact is.  In this particular case, the value being
+passed should be a string.
+
+If for some reason I has actually passed something else, I would be done
+debugging at that point and fix it simply by making sure that I was
+passing a string.  This situation is more complex.  I now need to trace
+the actual values contained by the variable being passed to explode, so
+that I can see what it is the explode() efun sees that it is being passed.
+
+The line is question is this:
+ borg[files[i]] = regexp(explode(read_file(files[i]), "\n"), exp);
+where files is an array for strings, i is an integer, and borg is a mapping. 
+So clearly we need to find out what the value of read_file(files[i]) is. 
+Well, this efun returns a string unless the file in question does not exist,
+the object in question does not have read access to the file in question, or
+the file in question is an empty file, in which cases the function will
+return NULL.  Clearly, our problem is that one of these events must
+have happened.  In order to see which, we need to look at files[i].
+
+Examining the code, the files array gets its value through the get_dir()
+efun.  This returns all the files in a directory if the object has read access
+to the directory.  Therefore the problem is neither lack of access or non-
+existent files.  The file which caused this error then must have been an
+empty file.  And, in fact, that is exactly what caused this error.  To
+debug that, we would pass files through the filter() efun and make
+sure that only files with a file size greater than 0 were allowed into the
+array.
+
+The key to debugging a run time error is therefore knowing exactly what
+the values of all variables in question are at the exact moment where the
+bug created.  When reading your run time log, be careful to separate the
+object from the file in which the bug occurred.  For example, the
+indexing error above came about in the object /wizards/zaknaifen/spy,
+but the error occured while running a function in /std/monster.c, which
+the object inherited.
+
+7.4 Malfunctioning Code
+The nastiest problem to deal with is when your code does not behave the
+way you intended it to behave.  The object loads fine, and it produces no
+run time errors, but things simply do not happen the way they should. 
+Since the driver does not see a problem with this type of code, no logs
+are produced.  You therefore need to go through the code line by line
+and figure out what is happening.
+
+Step 1: Locate the last line of code you knew successfully executed
+Step 2: Locate the first line of code where you know things are going
+wrong
+Step 3: Examine the flow of the code from the known successful point to
+the first known unsuccessful point.
+
+More often than not, these problems occurr when you are using if()
+statements and not accounting for all possibilities.  For example:
+
+int cmd(string tmp) {
+    if(stringp(tmp)) return do_a()
+    else if(intp(tmp)) return do_b()
+    return 1;
+}
+
+In this code, we find that it compiles and runs fine.  Problem is nothing
+happens when it is executed.  We know for sure that the cmd() function
+is getting executed, so we can start there.  We also know that a value of
+1 is in fact being returned, since we do not see "What?" when we enter
+the command.  Immediately, we can see that for some reason the
+variable tmp has a value other than string or int.  As it turns out, we
+issued the command without parameters, so tmp was NULL and failed
+all tests.
+
+The above example is rather simplistic, bordering on silly. 
+Nevertheless, it gives you an idea of how to examine the flow of the
+code when debugging malfunctioning code.  Other tools are available as
+well to help in debugging code.  The most important tool is the use of
+the precompiler to debug code.  With the code above, we have a clause
+checking for integers being passed to cmd().  When we type "cmd 10",
+we are expecting do_b() to execute.  We need to see what the value of
+tmp is before we get into the loop:
+
+#define DEBUG
+int cmd(string tmp) {
+#ifdef DEBUG
+    write(tmp);
+#endif
+    if(stringp(tmp)) return do_a();
+    else if(intp(tmp)) return do_b();
+    else return 1;
+}
+
+We find out immediately upon issuing the command, that tmp has a
+value of "10".  Looking back at the code, we slap ourselves silly,
+forgetting that we have to change command arguments to integers using
+sscanf() before evaluating them as integers.
+
+7.5 Summary
+The key to debugging any LPC problem is always being aware of what
+the values of your variables are at any given step in your code.  LPC
+execution reduces on the simplest level to changes in variable values, so
+bad values are what causes bad things to happen once code has been
+loaded into memory.  If you get errors about bad arguments to
+functions, more likely than not you are passing a NULL value to a
+function for that argument.  This happens most often with objects, since
+people will do one of the following:
+    1) use a value that was set to an object that has since destructed
+    2) use the return value of this_player() when there is no this_player()
+    3) use the return value of this_object() just after this_object() was
+    destructed
+
+In addition, people will often run into errors involving illegal indexing or
+indexing on illegal types.  Most often, this is because the mapping or
+array in question was not initialized, and therefore cannot be indexed. 
+The key is to know exactly what the full value of the array or mapping
+should be at the point in question.  In addition, watch for using index
+numbers larger than the size of given arrays
+
+Finally, make use of the precompiler to temporarly throw out code, or
+introduce code which will show you the values of variables.  The
+precompiler makes it easy to get rid of debugging code quickly once you
+are done.  You can simply remove the DEBUG define when you are
+done.
+
+Copyright (c) George Reese 1993
diff --git a/doc/KURS/RULES b/doc/KURS/RULES
new file mode 100644
index 0000000..ac88cfc
--- /dev/null
+++ b/doc/KURS/RULES
@@ -0,0 +1,60 @@
+		DIE ZWEI GEBOTE IN MORGENGRAUEN
+		===============================
+
+	Was sollen Gebote?
+	------------------
+
+	Wie in jedem Spiel so haben sich auch in unserem MUD alle
+	Mitspieler (Magier eingeschlossen) an Spielregeln zu halten.
+	Allerdings machen die Spiele am meisten Spass, die die
+	wenigsten Regeln besitzen. 
+
+	Um zu verhindern, dass jetzt schon angefangen wird, dass
+	unser MUD in feste Regeln gedraengt wird, habe ich mich 
+	entschlossen, selber dem Spiel ein Regelwerk zu geben.
+	Aber ich werde dafuer sorgen, dass sie kurz bleiben.
+	
+	1.Gebot )	DU SOLLST NETT SEIN.
+
+	Das MUD ist ein Spiel; und Spiele machen nur dann Spass,
+	wenn man sich nicht gegenseitig versucht in die Pfanne zu
+	hauen; somit gilt diese Regel fuer Spieler wie fuer Magier.
+
+	2.Gebot )	DU SOLLST DIE MUDLIB EHREN UND VERMEHREN.
+
+	Das MUD steht und faellt mit der Guete seiner Mudlib; und da
+	unser Hauptziel ein schoenes MUD ist, ist es die vorrangige
+	Aufgabe aller Magier, fuer den Aufbau, die Wartung und die
+	Erhaltung der Mudlib zu sorgen.
+
+	Zum Thema Vermehrung:
+	---------------------
+
+	Es gibt in einem MUD natuerlich auch noch andere Aufgaben:
+	-	Regionskoordination
+		(Regionsmagier,Erzmagier des Verkehrs)
+	-	Wahrung des Gleichgewichtes der Kraefte
+		(Waffen,Ruestungen,Geld,XP,Monster,u.s.w.)
+	-	Schutz des MUDs vor Spielverderbern.
+		(Sheriff)
+
+	Diese Aufgaben sind aber keine Lebensaufgaben; sie sind
+	Aufgaben, die sich den Geboten unterzuordnen haben.
+	Insbesondere sollen Magier, die nichts zur Vermehrung der
+	Mudlib beitragen, nicht ueber andere urteilen.
+
+	Zum Thema Ehrung:
+	-----------------
+
+	Hiermit meine ich den Aufruf an die Magier, keine Objekte
+	zu schreiben, die das Gleichtgewicht der Kraefte verletzen.
+
+	Und ich meine den Aufruf an die Magier, dafuer zu sorgen,
+	dass es in diesem MUD Herausforderungen gibt, die (zumindest
+	teilweise) nicht umgangen werden koennen.
+	Natuerlich ist damit auch gemeint, dass Spielern nur in den
+	Faellen geholfen wird, wo Fehler oder Luecken in der Mudlib
+	auftauchen.
+
+	Olpp, Sheriff des MorgenGrauens
+	Rumata, MudAdm des MorgenGrauens
diff --git a/doc/KURS/RULES.WIZ b/doc/KURS/RULES.WIZ
new file mode 100644
index 0000000..349f0b6
--- /dev/null
+++ b/doc/KURS/RULES.WIZ
@@ -0,0 +1,46 @@
+These are the rules for a wizard.
+(Wird noch ueberarbeitet, am besten Olpp oder Rumata fragen, was
+wirklich Sache ist. Aber eine grobe Richtung weisen diese "alten"
+Regeln schon :-)
+
+1. You may not help other players, even if they lost points due to
+   lost connection. They have to go to the post office and ask Lars,
+   or use bug.
+
+2. Never attack a player or kill him. If a player behaves badly, file
+   the complaint to Lars.
+
+3. Do not make 'deadly' trapps. A player must never die because he didn't
+   know what happens in the next room. If some room is very dangerous,
+   make some hint (like the giant footprints beside the giant lair).
+
+4. Never initialize the destination of an object outside your castle. If you
+   make a monster that can walk out of the castle, be sure that it is a
+   very nice monster.
+
+5. Never generate messages that looks like something it isn't (good and precise
+   rule :-). That is, don't try to fool the player that someone says something
+   when it isn't true etc.
+
+6. If you have a "test" character, make sure is isn't seen in the top score
+   list. He must no be wizard either.
+
+7. Try to avoid making devices, situations which makes the players loose
+   experience if they are adventuring or examining objects. Players should be
+   rewarded for adventuring and discovering things. If they get killed...
+   Well, they will then loose some experience, but this is a special case!
+
+8. The game is supposed to be in the "long distant past", and thus no
+   modern things should exist. If you want some kind of airplane, use a
+   flying horse in stead etc.
+
+9. If you make available some kind of restaurant that sells food that
+   heals, then the healing must cost on average 4 gp/hp (or more). The amount
+   of healing must also be limited per reset. Generating healing items for
+   free is only allowed if they heal at most 20 points, and are destructed.
+   Not more than one such item may be generated per room and reset.
+
+10.Teleporting items should be VERY restricted, and very rare. They must
+   not allow teleportations to anywhere, but only to one or more predefined
+   room. Never to a specific player. It must cost at least 50 spell points
+   to use each time.
diff --git a/doc/KURS/einleitung b/doc/KURS/einleitung
new file mode 100644
index 0000000..1483155
--- /dev/null
+++ b/doc/KURS/einleitung
@@ -0,0 +1,45 @@
+	Einfuehrung in die Progrmmierung der Morgengrauen-Mudlib
+	von Don Rumata, MudAdm des MorgenGrauens
+	========================================================
+
+	EINLEITUNG:
+	-----------
+
+	Herzlich willkommen als Magier im MorgenGrauen. Du bist
+	gerade Magier geworden. Dieser Kurs soll Dir helfen,
+	in diesem MUD alles Moegliche zu programmieren. 
+
+
+	WICHTIGE INFORMATIONEN FUER DEN ANFANG:
+	---------------------------------------
+
+	Ein Magier hat sich gewissen Regeln zu unterwerfen, wie man
+	sich bein einem Spiel auch in die Spielregeln zu halten hat.
+	Diese Spielregeln stehen in RULES (das sind die wichtigen)
+	und in RULES.WIZ (das sind die technischen Hinweise).
+
+	Wenn Du Files editieren willst, gibt es mehrere Moeglich-
+	keiten:
+
+	1.) Mit dem Befehl "ed <filename>":
+	    Eine Einleitung fuer ed kannst Du in /doc/ed/* finden.
+
+	2.) Fileuebertragung per ftp
+	3.) Fileuebertragung per mtp
+	    frage am besten einen der erfahrenen Magier.
+
+
+	KURSUEBERBLICK:
+	---------------
+
+	I	Programmierung von Raeumen
+	     1.	Ein einfacher Raum
+	 
+	II	Programmierung von Objekten
+	
+	III	Programmierung von Monstern
+
+
+	Achja: Ich bitte flehentlich um Korrekturlesen und
+	Verbesserungs/Ergaenzungsvorschlaege. Und noch mehr
+	um Mitarbeit. (Ich hab schon jetzt wunde Finger :-)
diff --git a/doc/KURS/objekte b/doc/KURS/objekte
new file mode 100644
index 0000000..fbb11d3
--- /dev/null
+++ b/doc/KURS/objekte
@@ -0,0 +1,29 @@
+	OBJEKTE
+	
+	Als erstes musst Du wissen, dass jedes Objekt in diesem
+	Mud durch ein File repraesentiert wird. Dieses File
+	wird auch die "Blueprint" genannt. Du kannst neue Objekte
+	auf 2 Arten erschaffen.
+
+	1.) "clone"n von Blueprints. Das bedeutet, dass Du ein
+	    Objekt erschaffst, das die Eigenschaften hat, die in
+	    der Blueprint programmiert worden sind. Von einer
+	    Blueprint koennen mehrere "Clones" existieren.
+	    Typische Beispiele fuer "Clones" sind Waffen,
+	    Monster, Geld oder Spieler.
+
+	2.) "load"en von Blueprints. Das bedeutet, dass Du
+	    diese Blueprint zu Leben erweckst. In gewisser Weise
+	    benehmen sich diese Objekte genauso wie die "Clones",
+	    allerdings koennen von einmal geladenen Blueprints
+	    keine "Clones" mehr erschaffen werden.
+	    Typische geladene Objekte sind Raeme.
+
+	KNIGGE FUER MAGIER
+
+	Dadurch, dass Du beliebige Files laden oder clonen kannst,
+	bist Du in der Lage, jedem Spieler das gewuenschte Objekt
+	zu beschaffen. Aber das ist natuerlich nicht der Sinn der
+	Sache. Als Magier bist Du verpflichtet, den Spielern nur
+	noch dann zu helfen, wenn sie aufgrund von Programmier-
+	fehlern in Schwierigkeiten geraten.
diff --git a/doc/KURS/raum1 b/doc/KURS/raum1
new file mode 100644
index 0000000..b623c35
--- /dev/null
+++ b/doc/KURS/raum1
@@ -0,0 +1,137 @@
+
+	PROGRAMMIERUNG VON RAEUMEN - TEIL 1
+	von Don Rumata, MudAdm des MorgenGrauens
+	=============================================================
+
+	Ein Raum ist sehr einfach zu programmieren. Man fuellt
+	ihn mit den Informationen, die man in ihm haben will.
+
+	Am besten erklaert sich alles an einem Beispiel. Zu den
+	Funktionen sind in /doc/efuns und /doc/lfuns weitere
+	Informationen erhaeltlich.
+
+	Nachfolgend kannst Du einen (zugegebeneermassen ueber-
+	ausfuehrlich) dokumentierten Beispielraum finden.
+	In /doc/beispiele/raum1.c kannst Du den selben Raum
+	noch einmal (ohne Kommentare) finden. Den Raum
+	kannst Du uebrigens auch betreten.
+
+/* beispielraum.c von Rumata */
+
+inherit "std/room";
+	// Alle Raeume muessen diese Zeile enthalten, da in dem
+	// Standardraum wichtige Funktionen definiert werden, die
+	// innerhalb des Spieles benoetigt werden.
+
+#include <properties.h>
+	// In diesem File sind die Namen der Properties definiert.
+	// Fuer naehre Informationen kannst Du in
+	// /doc/concepts/properties und in /doc/properties.h
+	// nachschauen, da es sehr viele verschiedene Properties
+	// gibt.
+
+#include <language.h>
+	// Diese Module enthalten alle Definitionen im Zusammenhang
+	// mit Sprache. Siehe /doc/language.h
+
+create()
+// Diese Funktion wird automatisch aufgerufen, wenn der Raum geladen
+// wird. Das passiert beim Kommando "load /doc/beispiel/bspraum1" oder
+// wenn ein Spieler den Raum als erster betritt.
+{
+	::create();
+	// Diese Funktion darf NIEMALS fehlen! Sie sorgt dafuer, dass
+	// der Raum auch die allgemeine Initialisierung durchfuehrt,
+	// da diese hier die der "Mutter-Klasse" std/room ueberlaedt.
+
+	SetProp( P_INT_SHORT, "Ein kleines Schreibzimmer" );
+	SetProp( P_INT_LONG,
+		"Du stehst in einem kleinen Schreibzimmer.\n"
+	+	"Es gehoerte wohl irgendwann einmal einem Magier, aber nun\n"
+	+	"ist dieser Raum verwaist und rottet vor sich hin.\n"
+	+	"Ein grosser Schreibtisch in der Mitte des Raumes scheint\n"
+	+	"noch einigermassen gut erhalten zu sein. Durch die Tuer\n"
+	+	"im Norden faellt etwas Licht hinein.\n"
+	);
+	SetProp( P_LIGHT, 1 );
+	SetProp( P_INDOORS, 1 );
+	// Diese Properties MUESSEN in JEDEM Raum gesetzt werden.
+	// P_INT_SHORT	= Beschreibung fuer Spieler im "kurz"-Modus.
+	// P_INT_LONG	= Beschreibung fuer Spieler im "lang"-Modus.
+	// P_LIGHT	= 1 fuer Hell, 0 fuer Dunkel
+	// P_INDOORS	= 1, wenn man den Himmel nicht sehen kann.
+
+	AddDetail( ({ "schreibtisch", "tisch" }),
+		"Auf dem Tisch liegt eine dicke Staubschicht.\n"
+	+	"Eine Schublade findest Du ebenfalls.\n"
+	);
+	AddDetail( ({ "staub", "staubschicht", "schicht" }),
+		"Du malst gelangweilt einige Kreise in den Staub.\n"
+	);
+	AddDetail( "schublade",
+		"So sehr Du Dich anstrengst; Du kannst sie nicht bewegen.\n"
+	);
+	AddDetail( "tuer" ,
+		"Sie steht seit Jahren offen und ist in dieser Lage\n"
+	+	"hoffnungslos festgerostet.\n"
+	);
+	// Details sind Dinge, die man sich in einem Raum anschauen
+	// aber nicht nehmen kann. Je mehr Details ein Raum hat, destso
+	// schoener ist er. Schaut sich ein Spieler ein Detail an,
+	// so bekommt der die angegebene Beschreibung.
+
+	AddRoomCmd( ({ "schliesse", "oeffne", "bewege",
+		"schliess", "beweg" }), "beweg_etwas" );
+	// RoomCommands sind Befehle, die man in diesem ausfuehren kann.
+	// Es wird die angegebene Funktion ausgefuehrt.
+	// WICHTIG: Als Verben sollten immer die IMPERATIVE benutzt
+	//	werden! Also nicht "lese", sondern "lies".
+	
+	AddExit( "norden", "players/rumata/workroom" );
+	// Definition eines Ausgangs. Das zeite Argument bestimmt den
+	// Raum, den ein Spieler erreicht, wenn er in die angegebene
+	// Richtung geht.
+
+}
+
+beweg_etwas( str )
+// Wenn ein Spieler ein Kommando ausfuehrt, so wird alles, was hinter
+// dem Verb steht als String uebergeben, es landet also hier in "str".
+// WICHTIG: Wenn der Befehl abgearbeitet wird, MUSS er eine 1 zurueck-
+//          geben. Wenn der Befehl nichts Sinnvolles tuen konnte, eine 0.
+{
+	notify_fail( "Was willst Du denn bewegen ?" );
+	// Meldung, die der Spieler bekommt, wenn ausser dem Raum auch
+	// kein anderes Objekt den Befehl verarbeiten konnte.
+	if( str == "tuer" )
+	{
+		write( "Die Tuer ist hoffnungslos festgerostet.\n" );
+		// Write gibt eine Meldung an den Spieler (und nur an ihn)
+		// aus.
+		return 1;
+		// Das Kommando bewirkt zwar aus Sicht des Spielers
+		// nichts, aber es konnte abgearbeitet werden.
+		// Also gib eine 1 zurueck.
+	}
+	if ( str == "lade" || str == "schublade" )
+	{
+		write( "Die Schublade klemmt einfach nur.\n" );
+		return 1;
+	}
+	if ( query_verb() == "bewege" &&
+		( str == "tisch" || str == "schreibtisch" ) )
+	{
+		write(
+			"Der Tisch scheint am Boden festgeschraubt zu sein.\n"
+		);
+		say(
+			this_player()->name(WER,2)+" zerrt am Schreibtisch.\n"
+		);
+		// say gibt eine Meldung an alle Spieler im selben
+		// Raum wie der Spieler (aber nicht an ihn selbst) aus.
+		return 1;
+	}
+	return 0;
+	// Der Raum konnte nichts mit dem Kommando anfangen, also gib
+	// eine 0 zurueck.
+}
diff --git a/doc/LPC/.synonym b/doc/LPC/.synonym
new file mode 100644
index 0000000..300916b
--- /dev/null
+++ b/doc/LPC/.synonym
@@ -0,0 +1,26 @@
+array arrays
+vererbung inheritance
+erben inheritance
+integer integers
+mapping mappings
+modulo operators
+operatoren operators
+increment operators
+decrement operators
+string strings
+case switch
+private modifiers
+public modifiers
+static modifiers
+nomask modifiers
+protected modifiers
+nosave modifiers
+virtual modifiers
+struct structs
+strong_types pragma
+strict_types pragma
+save_types pragma
+pedantic pragma
+range_check pragma
+rttc pragma
+rtt_checks pragma
diff --git a/doc/LPC/alists b/doc/LPC/alists
new file mode 100644
index 0000000..5550de7
--- /dev/null
+++ b/doc/LPC/alists
@@ -0,0 +1,53 @@
+CONCEPT
+        alists
+
+LAST UPDATE
+        2 Mar 92 21:10:21 GMT
+
+AUTHOR
+        From: amylaar@mcshh.hanse.de (Joern Rennecke)
+        Subject: general documentation on alists
+
+DESCRIPTION
+        Alists provide a fast and convenient way to access data
+        associatively.
+
+        Alists are implemented as arrays of arrays, the first being
+        the array holding the keys, the others arrays holding
+        associated data. An empty alist is an array of empty arrays.
+
+        Note that the the dimensions of the arrays are used the other
+        way than in lisp to allow for faster searching.
+
+        Keys have to be of type integer, string or object. Types can
+        be mixed.
+
+        The search functions return an undefined value when another
+        list is given in place of a presorted key list.
+
+        A list with non-numeric keys retrieved by restore_object() has
+        to be readjusted by using order_alist(), especially after
+        reboot.
+
+        Deleting an entry can safely be done with exclude_array as
+        long as all associated data lists are treated like the key
+        array; index finding for such purposes can be done with assoc.
+
+        Typical applications: holding administrary information about
+        wizards, list of visitors in a pub, list of customers having
+        some sort of credit, information remembered about items etc.
+
+NOTE
+        The main use of alists, storing data associatively, is now
+        better performed by mappings. Alists are needed for more
+        extreme situations only.
+
+        Alists are available only if the driver is compiled with
+        alist support. In that case, __ALISTS__ is defined.
+
+HISTORY
+        LDMud 3.3 made alists an optional efun.
+
+SEE ALSO
+        mappings(LPC), order_alist(E), insert_alist(E), assoc(E),
+        transpose_array(E)
diff --git a/doc/LPC/arrays b/doc/LPC/arrays
new file mode 100644
index 0000000..883dddb
--- /dev/null
+++ b/doc/LPC/arrays
@@ -0,0 +1,116 @@
+CONCEPT
+        arrays
+
+DESCRIPTION
+        There is support for arrays. The arrays can't be declared, but
+        should be allocated dynamically with the function 'allocate()'
+        (see efun/allocate).
+
+        Arrays are stored by reference, so all assignments of whole
+        arrays will just copy the address. The array will be
+        deallocated when no variable points to it any longer.
+
+        When a variable points to an array, items can be accessed with
+        indexing: 'arr[3]' as an example. The name of the array being
+        indexed can be any expression, even a function call:
+        'func()[2]'. It can also be another array, if this array has
+        pointers to arrays:
+
+        arr = allocate(2);
+        arr[0] = allocate(3);
+        arr[1] = allocate(3);
+
+        Now 'arr[1][2]' is a valid value.
+
+        The 'sizeof()' function (in true C a compiler-directive, not a
+        function) will give the number of elements in an array (see
+        efun/sizeof).
+
+NOTE
+        Nowadays it is most of the time preferable to use an array
+        constructor, a list surrounded by '({' and '})',
+        e.g. ({ 1, "xx", 2 }) will construct a new array with size 3,
+        initialized with 1, "xx" and 2 respectively.
+
+
+OPERATIONS
+
+        INDEXING
+        There are several very useful operations defined on arrays.
+        The most used is the indexing:
+                a=({ 0,1,2,3 });
+                return a[2];      // this will return 2
+        You also can count from the end of the array. Use <1 to specify
+        the last element in the array:
+                a=({ 0,1,2,3 });
+                return a[<3];     // this will return 1
+        With indexing you can also create sub-arrays:
+                a=({ 0,1,2,3,4,5,6,7 });
+                return a[3..5];   // this will return ({ 3,4,5 })
+                return a[2..<2];  // this will return ({ 2,3,4,5,6 })
+                return a[<5..<3]; // this will return ({ 3,4,5 })
+                return a[<6..5];  // this will return ({ 2,3,4,5 })
+                return a[3..3];   // this will return ({ 3 })
+                return a[3..2];   // this will return ({ })
+                return a[3..0];   // this will return ({ })
+                return a[5..100]; // this will return ({ 5,6,7 })
+                [x..] is interpreted as [x..<1]
+
+        ADDING
+        You can add two arrays. The result is one array with the elements
+        of both the former arrays:
+                a=({ 0,1 });
+                b=({ "a","b" });
+                return a+b;       // this will return ({ 0,1,"a","b" })
+                return b+a;       // this will return ({ "a","b",0,1 })
+
+        SUBTRACTING
+        You can erase all elements of one array that occur in another
+        array:
+                a=({ 0,1,2,3,4,5,6,7 });
+                b=({ 7,2,5,8,1,9 });
+                return a-b;       // this will return ({ 0,3,4,6 })
+                return b-a;       // this will return ({ 8,9 })
+
+        INTERJUNCTION
+        Use the &-operator to create the interjunction of two arrays:
+                a=({ 5,2,8,1,9,4 })
+                b=({ 1,6,7,3,4,5 })
+                return a&b;       // this will return ({ 1,4,5 })
+
+        ASSIGNING
+        Assigning can also be done to sub-arrays and is thus very powerful:
+                a=({ 0,1,2,3,4,5,6,7 });
+                a[<4..<3]=({ 8,9 });
+                return a;         // this will return ({ 0,1,2,3,8,9,6,7 })
+
+                a=({ 0,1,2,3,4,5,6,7 });
+                a[2..5]=({ });
+                return a;         // this will return ({ 0,1,6,7 })
+
+                a=({ 0,1,2,3,4 });
+                a[3..2]=({ 8,9 });
+                return a;         // this will return ({ 0,1,2,8,9,3,4 })
+
+                a=({ 0,1,2,3,4 });
+                a[3..0]=({ 8,9 });
+                return a;         // this will return ({ 0,1,2,8,9,1,2,3,4 })
+                                  // this is quite funny but true ;-)
+                                  // WARNING: If done unintentionally and
+                                  // within a loop, you can quickly cause
+                                  // the game to run out of memory!
+
+        GENERAL
+        Of course for any of the operators explained above you can use
+        the combined form of assigning and operating; that means the
+        operators +=, -= and &= work.
+
+
+TIPS
+        If you want to make sure that no element is more than once in an
+        array you can use the following:
+                a = m_indices(mkmapping(a));
+        This creates a mapping out of the array and recreates the array
+        at once. The elements in the array can be shuffled by this
+        procedure.
+
diff --git a/doc/LPC/block b/doc/LPC/block
new file mode 100644
index 0000000..29eb382
--- /dev/null
+++ b/doc/LPC/block
@@ -0,0 +1,48 @@
+NAME
+        block
+
+DESCRIPTION
+        A block is a special statment, that begins with '{', contains
+        a list of statements, and ends with '}'.
+
+        The block may define local variables. If for a variable no
+        initialisation is given, the variable is initialised to 0 every
+        time the block is entered. Otherwise, the initialisation
+        expression is evaluated and its result assigned to the variable
+        everytime the block is entered.
+
+        Example definitions are:
+
+          int i;
+          int j = 3;
+          int k = 3 * j, l;
+
+          Here, i and l are both initialised to 0; j is initialised
+          to 3, and k is initialised to 9 (3 * j).
+
+        Local variables defined in a block are visible only until the
+        end of the block. Definitions in an inner block hide definitions in
+        outer blocks.
+
+HISTORY
+        Up to 3.2.7 local variables were visible (from their point of
+        definition) in the whole function. That is, code like
+
+            do {
+                int res;
+
+                res = ...
+            } while (res == 5);
+            write(res);
+
+        was perfectly legal. It is no longer, as 'res' ceases to exist
+        with the closing '}' of the while().
+
+        Up to 3.5.0 you could get this old behaviour back with the 
+        #pragma no_local_scopes and switch it off again with 
+        #pragma local_scopes.
+
+        Since 3.5.0 it is not possible to disable the local scope behaviour.
+
+        Up to 3.2.8, local variables could not be initialised in their
+        definition.
diff --git a/doc/LPC/closure_guide b/doc/LPC/closure_guide
new file mode 100644
index 0000000..438ba46
--- /dev/null
+++ b/doc/LPC/closure_guide
@@ -0,0 +1,730 @@
+Closure Guide for LPC
+
+Table of Contents
+
+        1 Indroduction, Overview and Efun-Closures
+
+        2 Lfun-, Inline and Lambda-Closures
+        2.1 Lfun-Closures
+        2.2 Inline-Closures
+        2.3 Lambda-Closures
+        2.3.1 Advantages of Lambda-Closures
+        2.3.2 Free Variables in Lambda-Closure Constructs
+        2.3.3 Special Efun-Closures and Operator-Closures for Lambdas
+        2.4 Closures with Strange Names
+        2.5 Operator-Closures
+        2.6 Variable-Closures
+
+        3 Examples
+        3.1 Lfun-Closure
+        3.2 Lambda-Closure
+
+1 Introduction, Overview and Efun-Closures
+
+  A closure is a pointer to a function. That means that it is data like an
+  int or a string are. It may be assigned to a variable or given to anoth-
+  er function as argument.
+
+  To create a closure that points to an efun like write() you can write
+  the name of the efun prepended with "hash-tick": #'. #'write is a clo-
+  sure that points to the efun write().
+
+  I very often put parentheses around such a closure-notation because
+  otherwise my editor gets confused by the hashmark: (#'write). This is
+  especially of interest within lambda-closures (see below).
+
+  A closure can be evaluated (which means that the function it points to
+  is called) using the efuns funcall() or apply(), which also allow to
+  give arguments to the function. Example:
+
+    funcall(#'write,"hello");
+
+  This will result in the same as write("hello"); alone. The string
+  "hello" is given as first (and only) argument to the function the clo-
+  sure #'write points to.
+
+  The return value of the function the closure points to is returned by
+  the efun funcall() or apply(). (Since write() always returns 0 the re-
+  turn value of the example above will be 0.)
+
+  What are closures good for? With closures you can make much more univer-
+  sally usable functions. A good example is the function filter().
+  It gets an array and a closure as arguments. Then it calls the function
+  the closure points to for each element of the array:
+
+    filter(({ "bla","foo","bar" }),#'write);
+
+  This will call write("bla"), write("foo") and write("bar") in any order;
+  the order is undefined.
+  (In the current implementation the given closure is evaluated for all
+  elements from the first to the last, so the output will be "blafoobar".)
+
+  Furthermore the efun filter() examines the return value of each
+  call of the function the closure points to (the return value of the
+  write()s). If the value is true (not 0) then this element is put into
+  another array which filter() builds up. If the return value is
+  false (== 0) then this element is _not_ put into this array. When all
+  calls are done the slowly built up array is returned. Thus,
+  filter() filters from the given array all elements that the given
+  closure evaluates "true" for and returns an array of those. (The array
+  given to filter() itself is _not_ changed!)
+
+  A more sensical example for filterwould be this:
+
+    x = filter(users(),#'query_is_wizard);
+
+  users() is an efun that gets no arguments and returns an array of all
+  logged in players (wizards and players). query_is_wizard() is a
+  simul_efun that gets an object as first (and only) argument and returns
+  true (1) if this object is a wizard and 0 otherwise.
+
+  So, for each element of the array returned by users() the function
+  query_is_wizard() is called and only those for which 1 was returned are
+  collected into the result and then put into the variable x.
+
+  We now have all logged in wizards stored as array in the variable x.
+
+  Another example: We want to filter out all numbers that are greater than
+  42 from the array a of integers:
+
+    x = filter(({ 10,50,30,70 }),#'>,42);
+
+  (x will now be ({ 50,70 }).)
+
+  Here two things are new: first: we create a closure that points to an
+  operator; second: we use the possibility to give extra arguments to
+  filter().
+
+  Like all efuns the usual operators can be pointed to with a closure by
+  prepending #' to them. funcall(#'>,4,5) is exactly the same as (4>5).
+
+  The extra arguments given as third to last argument (as many as you
+  like) to filter() are given as second to last argument to the
+  function pointed to by the closure each time it is called.
+
+  Thus we now call (({ 10,50,30,70 })[0]>42), (({ 10,50,30,70 })[1]>42) ...
+  (which is (10>42), (50>42) ...) and return an array of all elements this
+  returns true for and store it into x.
+
+  If you want to create a closure to an efun of which you have the name
+  stored in a string you can create such an efun-closure with the efun
+  symbol_function():
+
+    symbol_function("write") // this will return #'write
+
+    funcall(symbol_function("write"),"foobar");  // == write("foobar");
+
+  This function does not very often occur in normal code but it is very
+  useful for tool-programming (eg the robe uses symbol_function() to allow
+  you call any efun you give).
+
+2 Lfun- and Lambda-Closures
+
+  Very often the possibilities closures to efuns offer are not sufficient
+  for the purpose one has. In nearly all cases three possibilities exist in
+  such cases: use an lfun- or inline-closure, or a lambda-closure.
+
+2.1 Lfun-Closures
+
+    The first possibility is rather easy: like with the efun-closures you
+    can create a pointer to a function in the same object you are by using
+    the #' to prepend it to a function name of a function declared above.
+    Example:
+
+      status foo(int x) {
+        return ((x*2) > 42);
+      }
+
+      int *bar() {
+        return filter(({ 10,50,30,70 }),#'foo);
+      }
+
+    Thus, #'foo is used like there was an efun of this name and doing the
+    job that is done in foo().
+
+2.2 Inline Closure
+
+    Inline closures are a variant of lfun closures, the difference being
+    that the function text is written right where the closure is used,
+    enclosed in a pair of '(:' and ':)'. The compiler will then take care
+    of creating a proper lfun and lfun-closure. The arguments passed to
+    such an inline closure are accessible by position: $1 would be the
+    first argument, $2 the second, and so on. With this, the
+    above example would read:
+
+      int * bar() {
+        return filter(({ 10,50,30,70 }), (: ($1 * 2) > 42 :));
+      }
+
+    or alternatively:
+
+      int * bar() {
+        return filter(({ 10,50,30,70 }), (: return ($1 * 2) > 42; :));
+      }
+
+    The difference between the two versions is that in the first form the text
+    of the inline closure must be an expression only, whereas in the second
+    form any legal statement is allowed. The compiler distinguishes the two
+    forms by the last character before the ':)': if it's a ';' or '}', the
+    compiler treats the closure as statement(s), otherwise as expression.
+
+    Inline closures may also nested, so that the following (not very useful)
+    example is legal, too:
+
+      return filter( ({ 10, 50, 30, 70 })
+                         , (: string *s;
+                              s = map(users(), (: $1->query_name() :));
+                              return s[random(sizeof(s))] + ($1 * 2);
+                            :));
+
+    The notation of inline closures is modelled after the MudOS functionals,
+    but there are a few important differences in behaviour.
+
+
+2.3 Lambda-Closures
+
+    Lambda-Closures take the idea of 'define it where you use it' one step
+    further. On first glance they may look like inline closures with an uglier
+    notation, but they offer a few increased possibilities. But first things
+    first.
+
+    The efun lambda() creates a function temporarily and returns a closure
+    pointing to this function. lambda() therefor gets two arrays as
+    arguments, the first is a list of all arguments the function shall expect
+    and the second array is the code of the function (in a more or less
+    complicated form; at least not in C- or LPC-syntax). The closure #'foo
+    from the example above could be notated as lambda-closure:
+
+      lambda(({ 'x }),({ (#'>),
+                         ({ (#'*),'x,2 }),
+                         42
+                      }))
+
+    Now, the first argument is ({ 'x }), an array of all arguments the
+    function shall expect: 1 argument (called 'x) is expected. Notice the
+    strange notation for this argument with one single leading tick. Like
+    The hash-tick to denote closures the leading tick is used to denote
+    things called "symbols". They do not differ much from strings and if
+    you do not want to have a deeper look into closures you can leave it
+    this way.
+
+    The second argument is an array. The first element of such an array
+    must be an efun- or an lfun-closure, the further elements are the
+    arguments for the function this closure points to. If such an argu-
+    ment is an array, it is treated alike; the first element must be a
+    closure and the remaining elements are arguments (which of course
+    also might be arrays ...).
+
+    This leads to a problem: sometimes you want to give an array as an
+    argument to a function. But arrays in an array given to lambda() are
+    interpreted as code-arrays. To allow you to give an array as an argu-
+    ment within an array given to lambda(), you can use the function
+    quote() to make your array to a quoted array (a quoted array is for
+    an array what a symbol is for a string):
+
+      lambda(0,({ (#'sizeof),
+                  quote(({ 10,50,30,70 }))
+               }))
+
+    For array constants, you can also use a single quote to the same
+    effect:
+
+      lambda(0,({ (#'sizeof),
+                  '({ 10,50,30,70 })
+               }))
+
+    This lambda-closure points to a function that will return 4 (it will
+    call sizeof() for the array ({ 10,50,30,70 })). Another thing: if
+    we want to create a function that expects no arguments, we can give
+    an empty array as first argument to lambda() but we can give 0 as
+    well to attain this. This is just an abbreviation.
+
+    Lambda-closure constructs can become quite large and hard to read. The
+    larger they become the harder the code is to read and you should avoid
+    extreme cases. Very often the possibility to use an lfun or an inline
+    instead of a large lambda shortens the code dramatically. Example:
+
+      status foo(object o) {
+        return environment(o)->query_level()>WL_APPRENTICE;
+      }
+
+      x=filter(a,#'foo);
+
+    does the same as
+
+      x=filter(a,lambda(({ 'o }),
+                              ({ (#'>),
+                                 ({ (#'call_other),
+                                    ({ (#'environment),'o }),
+                                    "query_level"
+                                 }),
+                                 WL_APPRENTICE
+                              })));
+
+    (Note that the syntax with the arrow "->" for call_other()s cannot be
+    used, #'-> does not exist. You have to use #'call_other for this and
+    give the name of the lfun to be called as a string.)
+
+    This example also demonstrates the two disadvantages of lambda closures.
+    First, they are very difficult to read, even for a simple example like
+    this. Second, the lambda closure is re-created everytime the
+    filter() is executed, even though the created code is always the
+    same.
+
+    'Why use lambdas at all then?' you may ask now. Well, read on.
+
+
+2.3.1 Advantages of Lambda Closures
+
+    The advantages of lambdas stem from the fact that they are created
+    at runtime from normal arrays.
+
+    This means that the behaviour of a lambda can be made dependant on data
+    available only at runtime. For example:
+
+      closure c;
+      c = lambda(0, ({#'-, ({ #'time }), time() }) );
+
+    Whenever you now call this closure ('funcall(c)') it will return the
+    elapsed time since the closure was created.
+
+    The second advantage of lambdas is that the arrays from which they
+    are compiled can be constructed at runtime. Imagine a customizable prompt
+    which can be configured to display the time, the environment, or both:
+
+      mixed code;
+
+      code = ({ "> " });
+      if (user_wants_time)
+        code = ({ #'+, ({ #'ctime }), code });
+      if (user_wants_environment)
+        code = ({ #'+, ({#'to_string, ({#'environment, ({#'this_player }) }) })
+                     , code });
+      set_prompt(lambda(0, code));
+
+
+2.3.2 Free Variables in Lambda-Closure Constructs
+
+      You can use local variables in lambda constructs without declaring
+      them, just use them. The only limitation is that you at first have
+      to assign something to them. Give them as symbols like you do with
+      the arguments. This feature does not make much sense without the use
+      of complexer flow controlling features described below.
+
+      The closure #'= is used to assign a value to something (like the
+      LPC-operator = is).
+
+2.3.3 Special Efun-Closures and Operator-Closures for Lambdas
+
+      There are some special closures that are supposed to be used only
+      within a lambda construct. With them you can create nearly all code
+      you can with regular LPC-code like loops and conditions.
+
+      #'? acts like the "if" statement in LPC. The first argument is the
+          condition, the second is the code to be executed if the condition
+          returns true. The following arguments can also be such couples of
+          code-arrays that state a condition and a possible result. If at
+          the end there is a single argument, it is used as the else-case
+          if no condition returned true.
+
+          lambda(({ 'x }),({ (#'?),             // if
+                             ({ (#'>),'x,5 }),  //    (x > 5)
+                             ({ (#'*),'x,2 }),  //       result is x * 2;
+                             ({ (#'<),'x,-5 }), // else if (x < -5)
+                             ({ (#'/),'x,2 }),  //    result is x/2;
+                             'x                 // else result is x;
+                          }))
+
+      #'?! is like the #'? but it negates all conditions after evaluation
+           and thus is like an ifnot in LPC (if there were one).
+
+      #', (which looks a bit strange) is the equivalent of the comma-operator
+          in LPC and says: evaluate all arguments and return the value of
+          the last. It is used to do several things inside a lambda-closure.
+
+          lambda(({ 'x }),({ (#',),  // two commas necessary!
+                                     // one for the closure and one as
+                                     // delimiter in the array
+                             ({ (#'write),"hello world!" }),
+                             ({ (#'say),"Foobar." })
+                          }))
+
+      #'while acts like the LPC statement "while" and repeats executing one
+              code-array while another returns true.
+              #'while expects two or more arguments: the condition as first
+              argument, then the result the whole expression shall have after
+              the condition turns false (this is in many cases of no interest)
+              and as third to last argument the body of the loop.
+
+              lambda(0,({ (#',),            // several things to do ...
+                          ({ (#'=),'i,0 }),    // i is a local variable of this
+                                               // lambda-closure and is
+                                               // initialized with 0 now.
+                          ({ (#'while),
+                             ({ (#'<),'i,10 }),   // condition: i < 10
+                             42,                  // result is not interesting,
+                                                  // but we must give one
+                             ({ (#'write),'i }),  // give out i
+                             ({ (#'+=),'i,1 })    // increase i
+                          })
+                       }))
+
+               The function this closure points to will give out the
+               numbers from 0 to 9 and then return 42.
+
+      #'do is like the do-while statement in LPC and is very much like the
+           #'while. The difference is that #'while tests the condition al-
+           ready before the body is evaluated for the first time, this means
+           that the body might not be evaluated even once. #'do evaluates
+           the body first and then the condition, thus the body is evaluated
+           at least one time.
+           Furthermore, the arguments for #'do are changed in order. #'do
+           expects as first to last but two the body of the loop, then the
+           condition (as last-but-one'th argument) and the result value as
+           last argument. So #'do must have at least two arguments: the
+           condition and the result.
+
+           lambda(0,({ (#',),          // several things to do ...
+                       ({ (#'=),'i,0 }),  // i is a local variable of this
+                                          // lambda-closure and is initialized
+                                          // with 0 now.
+                       ({ (#'do),
+                          ({ (#'write),'i }),  // give out i
+                          ({ (#'+=),'i,1 })    // increase i
+                          ({ (#'<),'i,10 }),   // condition: i < 10
+                          42                   // result is not interesting
+                       })
+                    }))
+
+      NOTE: There is no #'for in LPC, you should use #'while for this.
+
+      #'foreach is like the foreach() statement in LPC. It evaluates one or
+           more bodies repeatedly for every value in a giving string, array
+           or mapping. The result of the closure is 0.
+
+           #'foreach expects two or more arguments:
+            - a single variable symbol, or an array with several variable
+              symbols
+            - the value to iterate over
+            - zero or more bodes to evaluate in each iteration.
+
+           The single values retrieved from the given value are assigned
+           one after another to the variable(s), then the bodies are executed
+           for each assignment.
+
+           lambda(0, ({#'foreach, 'o, ({#'users})
+                                    , ({#'call_other, 'o, "die" })
+                     }));
+
+           lambda(0, ({#'foreach, ({'k, 'v}), ({ ...mapping...})
+                                    , ({#'printf, "%O:%O\n", 'k, 'v })
+                     }));
+
+      #'return gets one argument and acts like the "return" statement in LPC
+               in the function that is created by lambda(). It aborts the
+               execution of this function and returns the argument.
+
+               lambda(0,({ (#'while),// loop
+                           1,        // condition is 1 ==> endles loop
+                           42,       // return value (which will never be used)
+                           ({ (#'write),"grin" })
+                           ({ (#'?!),               // ifnot
+                              ({ (#'random),10 }),  //       (random(10))
+                              ({ (#'return),100 })  //   return 100;
+                           })
+                        }))
+
+                This function will enter an endles loop that will in each
+                turn give out "grin" and if random(10) returns 0 (which will
+                of course happen very soon) it will leave the function with
+                "return 100". The value 42 that is given as result of the
+                loop would be returned if the condition would evaluate to 0
+                which cannot be. (1 is never 0 ;-)
+
+      #'break is used like the "break" statement in LPC and aborts the exe-
+              cution of loops and switches.
+              It must not appear outside a loop or switch of the lambda
+              closure itself, it cannot abort the execution of the function
+              the closure points to!
+
+                lambda(0,({ (#'?),
+                            ({ (#'random),2 }),
+                            ({ (#'break) }), // this will cause the error
+                                             // "Unimplemented operator break
+                                             // for lambda()"
+                            "random was false!"
+                         }));
+
+              You can use ({ #'return,0 }) instead of ({ #'break }) in such
+              cases.
+
+      #'continue is used like the "continue" statement in LPC and jumps to
+                 the end of the current loop and continues with the loop
+                 condition.
+
+      #'default may be used within a #'switch-construct but be careful!
+                To call symbol_function("default") (which is done usually
+                by tools that allow closure-creation) might crash the
+                driver! So please do only use it within your LPC-files.
+                (NOTE: This driver bug is fixed somewhere below 3.2.1@131.)
+
+      #'.. may be used within a #'switch-construct but is not implemented
+           yet (3.2.1@131). But #'[..] works well instead of it.
+
+      #'switch is used to create closures which behave very much like the
+               switch-construct in LPC. To understand the following you
+               should already know the syntax and possibilities of the
+               latter one (which is mightier than the C-version of switch).
+
+               I will confront some LPC versions and the corresponding clo-
+               sure versions below.
+
+               LPC:                  Closure:
+                 switch (x) {          lambda(0,({ (#'switch), x,
+                 case 5:                           ({ 5 }),
+                   return "five";                  ({ (#'return),"five" }),
+                                                   (#',),
+                 case 6..9:                        ({ 6, (#'[..]), 9 }),
+                   return "six to nine";           ({ (#'return),
+                                                      "six to nine" }),
+                                                   (#',),
+                 case 1:                           ({ 1 }),
+                   write("one");                   ({ (#'write),"one" }),
+                   // fall through                 (#',),
+                 case 2:                           ({ 2,
+                 case 10:                             10 }),
+                   return "two or ten";            ({ (#'return),
+                                                      "two or ten" }),
+                                                   (#',),
+                 case 3..4:                        ({ 3, (#'[..]), 4 }),
+                   write("three to four");         ({ (#'write),
+                                                      "three to four" }),
+                   break;  // leave switch         (#'break),
+                 default:                          ({ (#'default) }),
+                   write("something else");        ({ (#'write),
+                                                      "something else" }),
+                   break;                          (#'break)
+                 }                              }))
+
+      #'&& evaluates the arguments from the first on and stops if one evalu-
+           ates to 0 and returns 0. If none evaluates to 0 it returns the
+           result of the last argument.
+
+      #'|| evaluates the arguments from the first on and stops if one evalu-
+           ates to true (not 0) and returns it. If all evaluate to 0 it
+           returns 0.
+
+      #'catch executes the closure given as argument, but catches any
+           runtime error (see catch(E)). Optionally the symbols 'nolog,
+           'publish and 'reserve may be given as additional arguments to
+           modify the behaviour of the catch.
+
+      #'sscanf acts similar to how a funcall would, but passes the third
+           and following arguments as lvalues, that is, values which can
+           be assigned to.
+
+      #'= and the #'<op>= variants are also special because the first
+           argument has to be an lvalue.
+
+2.4 Closures with Strange Names
+
+    #'negate is the unary minus that returns -x for the argument x.
+
+             map(({ 1,2,3 }),#'negate)
+
+             This returns ({ -1,-2,-3 }).
+
+    #'[ is used for the things that in LPC are done with
+             the []-operator (it indexes an array or a mapping).
+
+             lambda(0,({ #'[,quote(({ 10,50,30,70 })),2 })) ==> 30
+             lambda(0,({ #'[,([ "x":10;50, "y":30;70 ]),"x",1 })) ==> 50
+
+    #'[< is the same as #'[ but counts the elements from the
+               end (like indexing with [] and the "<").
+
+    #'[..] returns a subarray of the argument from the one
+             given index to the other given index, both counted from the
+             beginning of the array.
+
+    #'[..<]
+    #'[<..]
+    #'[<..<] same as above, but the indexes are counted from the end,
+
+                lambda(0,({ #'[..<],
+                            quote(({ 0,1,2,3,4,5,6,7 })),2,3
+                         }))
+
+                This will return ({ 2,3,4,5 }).
+    #'[..
+    #'[<.. same as above, but only the first index is given, the
+               subarray will go till the end of the original (like with
+               [x..]).
+
+    #'({ is used to create arrays (as with ({ }) in LPC). All arguments
+             become the elements of the array.
+
+             lambda(0,({ #'({,
+                         ({ (#'random),10 }),
+                         ({ (#'random),50 }),
+                         ({ (#'random),30 }),
+                         ({ (#'random),70 })
+                      }))
+
+             This returns ({ random(10),random(50),random(30),random(70) }).
+
+    #'([ is used to create mappings out of single entries (with seve-
+               ral values) like the ([ ]) in LPC. Very unusual is the fact
+               that this closure gets arrays as argument that are not eval-
+               uated, although they are not quoted.
+
+               lambda(0,({ #'([,
+                           ({ "x",1,2,3 }),
+                           ({ "y",4,5,6 })
+                        }));
+
+               This returns ([ "x": 1;2;3,
+                               "y": 4;5;6 ]).
+
+               However, the elements of the arrays are evaluated as lambda
+               expressions, so if you want to create a mapping from values
+               evaluated at call time, write them as lambda closures:
+
+               lambda(0, ({ #'([, ({ 1, ({ #'ctime }) }) }) )
+
+               will return ([ 1: <result of ctime() at call time ]).
+
+               Arrays can be put into the mapping by quoting:
+
+               lambda(0, ({ #'([, ({ 1, '({ 2 }) }) }) )
+
+               will return ([ 1: ({ 2 }) ])
+
+
+    #'[,] is nearly the same as #'[. The only difference
+                     shows up if you want to index a mapping with a width
+                     greater than 1 (with more than just one value per
+                     key) directly with funcall(). Example:
+                     funcall(#'[,([ 0:1;2, 3:4;5 ]),0,1)
+                     This will not work. Use #'[,] and it will
+                     work. If you want to use it in a lambda closure you
+                     do not have to use #'[,] and #'[ will
+                     do fine. On the other hand, #'[,] cannot
+                     work with arrays, so in nearly all cases use #'[
+                     and just in the described special case, use
+                     #'[,].
+                     This is a strange thing and I deem it a bug, so it
+                     might change in the future.
+
+2.5 Operator-Closures
+
+    Most of the closures that are used for things which are done by opera-
+    tors are in fact not operator-closures but efun-closures. But there are
+    a few which do not have the state of efun-closures but are called
+    "operator-closures". #'return is an example, a complete list of them is
+    given below.
+
+    These closures cannot be called directly using funcall() or apply() (or
+    other efuns like filter()), but must appear only in lambda-con-
+    structs.
+
+      funcall(#'return,4);  // does not work! This will raise an
+                            // Uncallable-closure error.
+      funcall(lambda(0,     // this is a correct example
+                     ({ (#'return),4 })
+                    ));
+
+    All operator-closures:
+    #'&&
+    #'||
+    #',
+    #'?
+    #'?!
+    #'=
+    #'<op>=
+    #'++
+    #'--
+    #'break
+    #'catch
+    #'continue
+    #'default
+    #'do
+    #'foreach
+    #'return
+    #'sscanf
+    #'switch
+    #'while
+    #'({
+    #'([
+
+    #'.. is very likely to be an operator closure too, but since it is
+    not implemented yet, I cannot say for sure.
+
+2.6 Variable-Closures
+
+    All object-global variables might be "closured" by prepending a #' to
+    them to allow access and/or manipulation of them. So if your object has
+    a global variable x you can use #'x within a closure.
+
+    Normally you will treat those expressions like lfun-closures: put them
+    into an array to get the value:
+
+    object.c:
+              int x;
+              int foo() {
+                return lambda(0,({ (#'write),({ (#'x) }) }));
+              }
+
+    Anybody who now calls object->foo() will get a closure which will, when
+    evaluated, write the actual value of object's global variable x.
+
+    Variable closures do not accept arguments.
+
+3 Examples
+
+  In this section I will give and explain some examples coming out of
+  praxis. If the explanation seems to be in some cases too detailed this
+  can be explained by the trial to allow the reader to read the examples
+  section first ;-)
+
+3.1 Lfun-Closure
+
+    An item with a complex long-description like a watch that shall always
+    show the actual time will usually base upon the complex/item-class and
+    give an lfun-closure as argument to the set_long()-method.
+
+    watch.c:
+             inherit "complex/item";
+
+             string my_long() {
+               return ("The watch is small and has a strange otherworldly"
+                       " aura about it.\n"
+                       "The current time is: "+ctime()+".\n");
+             }
+
+             void create() {
+               set_short("a little watch");
+               set_id(({ "watch","little watch" }));
+               set_long(#'my_long);  // the lfun-closure to the lfun my_long()
+             }
+
+3.2 Lambda-Closure
+
+    The example from 3.1 can also be written using a lambda-closure.
+
+    watch.c:
+             inherit "complex/item";
+
+             void create() {
+               set_short("a little watch");
+               set_id(({ "watch","little watch" }));
+               set_long(lambda(0,({ (#'+),
+                                    "The watch is small and has a strange"
+                                    " otherworldly aura about it.\n"
+                                    "The current time is: ",
+                                    ({ (#'+),
+                                       ({ (#'ctime) }),
+                                       ".\n"
+                                    })
+                                 })));
+             }
diff --git a/doc/LPC/closures b/doc/LPC/closures
new file mode 100644
index 0000000..ba5d804
--- /dev/null
+++ b/doc/LPC/closures
@@ -0,0 +1,331 @@
+CONCEPT
+        closures
+
+NOTE
+        This is the official man page concerning closures. If you find
+        it hard to read maybe the Closure Guide is an easier introduction
+        for you: See closure_guide(LPC) and closures-example(LPC).
+
+DESCRIPTION
+        Closures provide a means of creating code dynamically and
+        passing pieces of code as parameters, storing them in
+        variables. One might think of them as a very advanced form of
+        process_string(). However, this falls short of what you can
+        actually do with them.
+
+        The simplest kind of closures are efuns, lfuns or operators.
+        For example, #'this_player is an example of a closure. You can
+        assign it to a variable as in
+
+                closure f;
+                object p;
+                f = #'this_player;
+
+        and later use either the funcall() or apply() efun to evaluate
+        it. Like
+
+                p = funcall(f);
+
+        or
+
+                p = apply(f);
+
+        In both cases there p will afterwards hold the value of
+        this_player().  Of course, this is only a rather simple
+        application.
+
+        Inline closures are a variant of lfun closures, the difference
+        being that the function text is written right where the
+        closure is used.  Since they are pretty powerful by
+        themselves, inline closures have their own manpage.
+
+        More useful instances of closures can be created
+        using the lambda() efun. It is much like the lambda function
+        in LISP. For example, you can do the following:
+
+                f = lambda( ({ 'x }), ({ #'environment, 'x }) );
+
+        This will create a lambda closure and assign it to f. The
+        first argument to lambda is an array describing the arguments
+        (symbols) passed to the closure upon evaluation by funcall()
+        or apply(). You can now evaluate f, for example by means of
+        funcall(f,this_object()). This will result in the following
+        steps:
+
+                1. The value of this_object() will be bound to symbol x.
+                2. environment(x) evaluates to environment(this_object())
+                   and is returned as the result of the funcall().
+
+        One might wonder why there are two functions, funcall() and
+        apply(), to perform the seemingly same job, namely evaluating
+        a closure. Of course there is a subtle difference. If the last
+        argument to apply() is an array, then each of its elements
+        gets expanded to an additional paramater. The obvious use
+        would be #'call_other as in:
+
+                mixed eval(object ob,string func,mixed *args) {
+                  return apply(#'call_other,ob,func,args);
+                }
+
+        This will result in calling
+        ob->func(args[0],args[1],...,args[sizeof(args)-1]).  Using
+        funcall() instead of apply() would have given us
+        ob->func(args).
+
+        Of course, besides efuns there are closures for operators,
+        like #'+, '-, #'<, #'&&, etc.
+
+        Well, so far closures have been pretty much limited despite
+        their obvious flexibility. This changes now with the
+        introduction of conditional and loop operators. For example,
+        try:
+
+                closure max;
+                max = lambda( ({ 'x, 'y }),
+                              ({ #'? ,({ #'>, 'x, 'y }), 'x, 'y }) );
+                return funcall(max,7,3);
+
+        The above example will return 7. What happened? Of course #'?
+        is the conditional operator and its 'syntax' is as follows:
+
+                ({ #'?, cond1, val1, cond2, val2, ..., condn, valn,
+                  valdefault });
+
+        It evaluates cond1, cond2, ..., condn successively until it
+        gets a nonzero result and then returns the corresponding
+        value. If there is no condition evaluating to a nonzero
+        result, valdefault gets returned. If valdefault is omitted, 0
+        gets returned. #'?! works just like #'?, except that the !
+        operator is applied to conditions before testing. Therefore,
+        while #'? is somewhat like an if statement, #'?! resembles an
+        if_not statement if there were one.
+
+        There are also loops:
+
+                ({ #'do, loopbody1, ..., loopbodyN, loopcond, loopresult })
+
+        will evaluate the loopbodies until loopcond evaluates to 0 and
+        then return the value of loopresult. Symbols may be used as
+        variables, of course.
+
+                ({ #'while, loopcond, loopresult, loopbody1, ..., loopbodyN })
+
+        works similar but evaluates loopcond before the loopbodies.
+
+        The foreach() loop also exists:
+
+                ({ #'foreach, 'var, expr, loopbody1, ..., loopbodyN })
+                ({ #'foreach, ({ 'var1, ..., 'varN}) , expr
+                                        , loopbody1, ..., loopbodyN })
+
+
+        Now on to a couple of tricky things:
+
+        a) How do I write down an array within a lambda closure to
+           avoid interpretation as a subclosure?
+
+           ({ #'member, ({ "abc", "xyz" }), 'x }) will obviously
+           result in an error as soon as lambda() tries to interpret
+           "abc" as a closure operator. The solution is to quote the
+           array, as in: ({ #'member, '({ "abc", "xyz" }), 'x }).
+           Applying lambda() to this will not result in an error.
+           Instead, the quote will be stripped from the array and the
+           result regarded as a normal array literal. The same can be
+           achieved by using the efun quote(), e.g.:
+
+           ({ #'member, quote( ({ "abc", "xyz" }), 'x ) })
+
+        b) Isn't it a security risk to pass, say, a closure to the
+           master object which then evaluates it with all the
+           permissions it got?
+
+           Luckily, no. Each closure gets upon compilation bound to
+           the object defining it. That means that executing it first
+           sets this_object() to the object that defined it and then
+           evaluates the closure. This also allows us to call lfuns
+           which might otherwise be undefined in the calling object.
+
+           There is however, a variant of lambda(), called
+           unbound_lambda(), which works similar but does not allow
+           the use of lfuns and does not bind the closure to the
+           defining object. The drawback is that trying to evaluate it
+           by apply() or funcall() will result in an error. The
+           closure first needs to be bound by calling bind_lambda().
+           bind_lambda() normally takes one argument and transforms an
+           unbound closure into a closure bound to the object
+           executing the bind_lambda().
+
+           Privileged objects, like the master and the simul_efun
+           object (or those authorized by the privilege_violation()
+           function in the master) may also give an object as the
+           second argument to bind_lambda(). This will bind the
+           closure to that object. A sample application is:
+
+           dump_object(ob)
+           // will dump the variables of ob to /dump.o
+           {
+             closure save;
+             save = unbound_lambda( ({ }),
+                                    ({ #'save_object, "/open/dump" }) );
+             bind_lambda(save,ob);
+             funcall(save);
+           }
+
+           bind_lambda() can also be used with efun closures.
+
+        c) It might be an interesting application to create closures
+           dynamically as an alternative to writing LPC code to a file
+           and then loading it.  However, how do I avoid doing exactly
+           that if I need symbols like 'x or 'y?
+
+           To do that one uses the quote() efun. It takes a string as
+           its argument and transforms it into a symbol. For example,
+           writing quote("x") is exactly the same as writing 'x.
+
+        d) How do I test if a variable holds a closure?
+
+           Use the closurep() efun which works like all the other type
+           testing efuns. For symbols there is also symbolp()
+           available.
+
+        e) That means, I can do:
+           if (closurep(f)) return funcall(f); else return f; ?
+
+           Yes, but in the case of funcall() it is unnecessary. If
+           funcall() gets only one argument and it is not a closure it
+           will be returned unchanged. So return funcall(f); would
+           suffice.
+
+        f) I want to use a function in some object as a closure. How do I do
+           that?
+
+           There are several ways. If the function resides in
+           this_object(), just use #'func_name. If not, or if you want
+           to create the function dnynamically, use the efun
+           symbol_function(). It takes a string as it first and an
+           object as its second argument and returns a closure which
+           upon evaluation calls the given function in the given
+           object (and faster than call_other(), too, if done from
+           inside a loop, since function search will be done only when
+           calling symbol_function().
+
+        g) Can I create efun closures dynamically, too?
+
+           Yes, just use symbol_function() with a single argument.
+           Most useful for marker objects and the like. But
+           theoretically a security risk if not used properly and from
+           inside a security relevant object.  Take care, however,
+           that, if there is a simul_efun with the same name, it will
+           be preferred as in the case of #'function. Use the efun::
+           modifier to get the efun if you need it.
+
+        h) Are there other uses of closures except using them to store
+           code?
+
+           Lots. For example, you can use them within almost all of
+           the efuns where you give a function as an argument, like
+           filter(), sort_array() or walk_mapping().
+           sort_array(array,#'>) does indeed what is expected. Another
+           application is set_prompt(), where a closure can output
+           your own prompt based on the current time and other stuff
+           which changes all the time.
+
+        Finally, there are some special efun/operator closures:
+
+        #'[ : indexes an array.
+        #'[< : does the same, but starting at the end.
+        #'[..] : gets an array and two numbers
+                and returns a sub-array.
+        #'[..<] : same as above but the second index is
+                interpreted as counted from the left end.
+        #'[<..]  and
+        #'[<..<] : should be clear now.
+        #'[.. : takes only one index and returns the sub-
+                array from this index to the end.
+        #'[<.. : same as above but the index is interpreted
+                as counted from the left end.
+        #'({ : puts all arguments into an array.
+        #'([ : gets an unquoted (!) array which must include
+                at least one element as argument and returns a mapping of
+                the width of the given array's size with one entry that
+                contains the first element as key and the other elements
+                as values to the key.
+
+        #'negate is for unary minus.
+        #', may be followed by any number of closures,
+        e.g.: ({ (#',),
+                 ({#'= 'h, 'a, }), ({#'=, 'a, 'b }), ({#'=, 'b, 'h }) })
+        will swap 'a and 'b when compiled and executed.
+
+
+        Procedural elements:
+        ====================
+
+        definition of terms:
+          <block>  : zero or more values to be evaluated.
+          <test>   : one value to be evaluated as branch or loop condition.
+          <result> : one value to be evaluated at the end of the
+                     execution of the form; the value is returned.
+          <lvalue> : local variable/parameter, global variable, or an
+                     indexed lvalue.
+        useded EBNF operators:
+        { }     iteration
+        [ ]     option
+
+        forms:
+          ({#', <body> <result>})
+          ({#'? { <test> <result> } [ <result> ] })
+          ({#'?! { <test> <result> } [ <result> ] })
+          ({#'&& { test } })
+          ({#'|| { test } })
+          ({#'while <test> <result> <body>})    loop while test
+                                                evaluates non-zero.
+          ({#'do <body> <test> <result>})       loop till test
+                                                evaluates zero.
+          ({#'= { <lvalue> <value> } })         assignment
+                                                other assignment
+                                                operators work, too.
+
+        lisp similars:
+          #',           progn
+          #'?           cond
+          #'&&          and
+          #'||          or
+          #'while       do      /* but lisp has more syntactic candy here */
+          #'=           setq
+
+        A parameter / local variable 'foo' is referenced as 'foo , a
+        global variable as ({#'foo}) . In lvalue positions
+        (assignment), you need not enclose global variable closures in
+        arrays.
+
+        Call by reference parameters are given with ({#'&, <lvalue>})
+
+        Some special efuns:
+        #'[             indexing
+        #'[<            indexing from the end
+        #'negate        unary -
+
+        Unbound lambda closures
+        =======================
+
+        These closures are not bound to any object. They are created
+        with the efun unbound_lambda() . They cannot contain
+        references to global variables, and all lfun closures are
+        inserted as is, since there is no native object for this
+        closure.  You can bind and rebind unbound lambda closures to
+        an object with efun bind_lambda() You need to bind it before
+        it can be called. Ordinary objects can obly bind to
+        themselves, binding to other objects causes a privilege
+        violation().  The point is that previous_object for calls done
+        from inside the closure will reflect the object doing
+        bind_lambda(), and all object / uid based security will also
+        refer to this object.
+
+
+AUTHOR
+        MacBeth, Amylaar, Hyp
+
+SEE ALSO
+        closures-abstract(LPC), closures-example(LPC), closure_guide(LPC)
+        inline-closures(LPC)
diff --git a/doc/LPC/closures-abstract b/doc/LPC/closures-abstract
new file mode 100644
index 0000000..6b1d932
--- /dev/null
+++ b/doc/LPC/closures-abstract
@@ -0,0 +1,82 @@
+Procedural elements:
+====================
+
+definition of terms:
+  <block>     : zero or more values to be evaluated.
+  <test>      : one value to be evaluated as branch or loop condition.
+  <result>    : one value to be evaluated at the end of the execution of
+                the form; the value is returned.
+  <lvalue>    : local variable/parameter, global variable, or an indexed
+                lvalue.
+  <expression>: one value to be evaluated.
+  <integer>   : an integer constant
+  <string>    : a string constant, or 0.
+used EBNF operators:
+{ }        iteration
+[ ]        option
+|        alternative
+
+forms:
+  ({#', <body> <result>})
+  ({#'? { <test> <result> } [ <result> ] })
+  ({#'?! { <test> <result> } [ <result> ] })
+  ({#'&& { test } })
+  ({#'|| { test } })
+  ({#'while <test> <result> <body>...})        loop while test evaluates non-zero.
+  ({#'do <body> <test> <result>})        loop till test evaluates zero.
+  ({#'foreach <var> <expr> <body>...})        loop over all values of <expr>.
+  ({#'foreach ({ <var>...<var> }) <expr> <body>...})
+  ({#'= { <lvalue> <value> } })                assignment
+                			other assignment operators work too.
+  case_label: <integer> | <string> | #'default
+  generalized_case_label: case_label | <integer> #'.. <integer>
+  case_label_list: case_label | ({ { generalized_case_label } })
+  case_delimiter: #', | #'break
+  ({#'switch <expression> { case_label_list <result> case_delimiter } })
+        Evaluate expression, then evaluate the result form labeled with
+        the value equal to the value evaluated from expression.
+        If no matching label exists, the value of #'switch is 0.
+  ({#'catch, <body> [, 'nolog ] [, 'publish ], [, 'reserve, <expr>  })
+        Evaluates the <body> and catches any runtime error. If the symbol
+        'nolog is also given, a caught error is not logged. If the
+        symbol 'publish is also given, master::runtime_error() is
+        called for the caught error. If the symbol 'reserve with a
+        following expression is given, this value is used as the
+        computing reserve.
+
+
+lisp similars:
+  #',                progn
+  #'?                cond
+  #'&&                and
+  #'||                or
+  #'while        do         /* but lisp has more syntactic candy here */
+  #'=                setq
+
+A parameter / local variable 'foo' is referenced as 'foo , a global
+variable as ({#'foo}) . In lvalue positions (assignment), you need not
+enclose global variable closures in arrays.
+
+Call by reference parameters are given with ({#'&, <lvalue>})
+
+Some special efuns:
+#'[                indexing
+#'[<                indexing from the end
+#'negate        unary -
+
+Unbound lambda closures
+=======================
+
+These closures are not bound to any object. They are created with the efun
+unbound_lambda() . They cannot contain references to global variables, and
+all lfun closures are inserted as is, since there is no native object for
+this closure.
+You can bind and rebind unbound lambda closures to an object with efun
+bind_lambda() You need to bind it before it can be called. Ordinary objects
+can obly bind to themselves, binding to other objects causes a privilege
+violation().
+The point is that previous_object for calls done from inside the closure
+will reflect the object doing bind_lambda(), and all object / uid based
+security will also refer to this object.
+
+
diff --git a/doc/LPC/closures-example b/doc/LPC/closures-example
new file mode 100644
index 0000000..f50fd35
--- /dev/null
+++ b/doc/LPC/closures-example
@@ -0,0 +1,202 @@
+CONCEPT
+        closures example
+
+DESCRIPTION
+        This document contains small examples of the usage of
+        (lambda-)closures. For technical details see the closures(LPC)
+        doc. For hints when to use which type of closure, see the end
+        of this doc.
+
+
+        Many Muds use 'details' to add more flavour. 'Details' are
+        items which can be looked at, but are not implemented as own
+        objects, but instead simulated by the environment.
+        Lets assume that the function
+
+          AddDetail(string keyword, string|closure desc)
+
+        adds the detail 'keyword' to the room, which, when look at,
+        returns the string 'desc' resp. the result of the execution of
+        closure 'desc' as the detail description to the player.
+
+        Now imagine that one wants to equip a room with magic runes,
+        which read as 'Hello <playername>!\n" when looked at.
+        Obviously
+
+          AddDetail("runes", sprintf( "Hello %s!\n"
+                                    , this_player()->QueryName()));
+
+        is not sufficient, as the 'this_player()' is executed to early
+        and just once: for the player loading the room.
+
+        The solution is to use closures. First, the solution using
+        lfun-closures:
+
+          private string _detail_runes () {
+            return sprintf("Hello %s!\n", this_player()->QueryName());
+          }
+            ...
+          AddDetail("runes", #'_detail_runes);
+
+        or with an inline closure:
+
+          AddDetail("runes"
+                   , (: sprintf("Hello %s!\n", this_player()->QueryName()) :)
+                   );
+
+
+        Simple? Here is the same code, this time as lambda-closure:
+
+          AddDetail( "runes"
+                   , lambda(0
+                     , ({#'sprintf, "Hello %s!\n"
+                                  , ({#'call_other, ({#'this_player})
+                                                  , "QueryName" })
+                       })
+                   ));
+
+        Why the extra ({ }) around '#'this_player'? #'this_player
+        alone is just a symbol, symbolizing the efun this_player(),
+        but call_other() needs an object as first argument. Therefore,
+        the #'this_player has to be interpreted as function to
+        evaluate, which is enforced by enclosing it in ({ }). The same
+        reason also dictates the enclosing of the whole #'call_other
+        expression into ({ }).
+        Note also the missing #'return: it is not needed. The result
+        of a lambda-closure is the last value computed.
+
+
+        Another example: Task is to reduce the HP of every living in a
+        room by 10, unless the result would be negative.
+        Selecting all livings in a room is simply
+
+          filter(all_inventory(room), #'living)
+
+        The tricky part is to reduce the HP. Again, first the
+        lfun-closure solution:
+
+          private _reduce_hp (object liv) {
+            int hp;
+            hp = liv->QueryHP();
+            if (hp > 10)
+              liv->SetHP(hp-10);
+          }
+            ...
+
+          map( filter(all_inventory(room), #'living)
+                   , #'_reduce_hp)
+
+        or as an inline closure:
+
+          map( filter(all_inventory(room), #'living)
+                   , (: int hp;
+                        hp = liv->QueryHP();
+                        if (hp > 10)
+                          liv->SetHP(hp - 10);
+                      :) );
+
+        Both filter() and map() pass the actual array item
+        being filtered/mapped as first argument to the closure.
+
+        Now, the lambda-closure solution:
+
+          map( filter(all_inventory(room), #'living)
+          , lambda( ({ 'liv })
+            , ({'#, , ({#'=, 'hp, ({#'call_other, 'liv, "QueryHP" }) })
+                    , ({#'?, ({#'>, 'hp, 10 })
+                           , ({#'call_other, 'liv, "SetHP"
+                                           , ({#'-, 'hp, 10 })
+                             })
+                      })
+              })
+            ) // of lambda()
+          );
+
+        It is worthy to point out how local variables like 'hp' are
+        declared in a lambda-closure: not at all. They are just used
+        by writing their symbol 'hp . Same applies to the closures
+        parameter 'liv .
+        The lambda-closure solution is not recommended for three
+        reasons: it is complicated, does not use the powers of
+        lambda(), and the lambda() is recompiled every time this
+        statement is executed!
+
+
+        So far, lambda-closures seem to be just complicated, and in
+        fact: they are. Their powers lie elsewhere.
+
+        Imagine a computation, like for skill resolution, which
+        involves two object properties multiplied with factors and
+        then added.
+        The straightforward solution would be a function like:
+
+          int Compute (object obj, string stat1, int factor1
+                                 , string stat2, int factor2)
+          {
+            return   call_other(obj, "Query"+stat1) * factor1
+                   + call_other(obj, "Query"+stat2) * factor2;
+          }
+
+        Each call to Compute() involves several operations (computing
+        the function names and resolving the call_other()s) which in
+        fact need to be done just once. Using lambda-closures, one can
+        construct and compile a piece of code which behaves like a
+        Compute() tailored for a specific stat/factor combination:
+
+          closure ConstructCompute (object obj, string stat1, int factor1
+                                              , string stat2, int factor2)
+          {
+            mixed code;
+
+            // Construct the first multiplication.
+            // The symbol_function() creates a symbol for the
+            // lfun 'Query<stat1>', speeding up later calls.
+            // Note again the extra ({ }) around the created symbol.
+
+            code = ({#'*, ({ symbol_function("Query"+stat1, obj) })
+                        , factor1 });
+
+            // Construct the second multiplication, and the addition
+            // of both terms.
+
+            code = ({#'+, code
+                        , ({#'*, ({ symbol_function("Query"+stat2, obj) })
+                               , factor2 })
+                   });
+
+            // Compile the code and return the generated closure.
+            return lambda(0, code);
+          }
+
+        Once the closure is compiled,
+
+          str_dex_fun = ConstructCompute(obj, "Str", 10, "Dex", 90);
+
+        it can be used with a simple 'funcall(str_dex_fun)'.
+
+
+DESCRIPTION -- When to use which closure?
+        First, a closure is only then useful if it needn't to live any
+        longer than the object defining it. Reason: when the defining
+        object gets destructed, the closure will vanish, too.
+
+        Efun-, lfun- and inline closures should be used where useful, as they
+        mostly do the job and are easy to read. The disadvantage of lfun- and
+        inline closures is that they make a replace_program() impossible
+        - but since such objects tend to not being replaceable at all, this is
+        no real loss.
+
+        Lambda closures are needed if the actions of the closure are
+        heavily depending on some data available only at runtime, like
+        the actual inventory of a certain player.
+        If you use lfun-closures and find yourself shoving around
+        runtime data in arguments or (gasp!) global variables, it is
+        time to think about using a lambda-closure, compiling the
+        value hard into it.
+        The disadvantages of lambda closures are clear: they are damn
+        hard to read, and each lambda() statement requires extra time to
+        compile the closure.
+
+
+SEE ALSO
+        closures(LPC), closure_guide(LPC), closures-abstract(LPC)
diff --git a/doc/LPC/closures2 b/doc/LPC/closures2
new file mode 100644
index 0000000..fc52894
--- /dev/null
+++ b/doc/LPC/closures2
@@ -0,0 +1,369 @@
+Closures provide a means of creating code dynamically and passing pieces
+of code as parameters, storing them in variables. One might think of them
+as a very advanced form of process_string(). However, this falls short of
+what you can actually do with them.
+
+The simplest kind of closures are efuns, lfuns or operators. For
+example, #'this_player is an example of a closure. You can assign it
+to a variable as in
+
+	closure f;
+	object p;
+	f = #'this_player;
+
+and later use either the funcall() or apply() efun to evaluate it. Like
+
+	p = funcall(f);
+
+or
+	p = apply(f);
+
+In both cases there p will afterwards hold the value of this_player().
+Of course, this is only a rather simple application. More useful
+instances of closures can be created using the lambda() efun. It is much
+like the lambda function in LISP. For example, you can do the following:
+
+	f = lambda( ({ 'x }), ({ #'environment, 'x }) );
+
+This will create a lambda closure and assign it to f. The first argument
+to lambda is an array describing the arguments (symbols) passed to the
+closure upon evaluation by funcall() or apply(). You can now evaluate f,
+for example by means of funcall(f,this_object()). This will result in
+the following steps:
+
+	1. The value of this_object() will be bound to symbol x.
+	2. environment(x) evaluates to environment(this_object())
+	   and is returned as the result of the funcall().
+
+One might wonder why there are two functions, funcall() and apply(), to
+perform the seemingly same job, namely evaluating a closure. Of course
+there is a subtle difference. If the last argument to apply() is an array,
+then each of its elements gets expanded to an additional paramater. The
+obvious use would be #'call_other as in:
+
+	mixed eval(object ob,string func,mixed *args)
+	{
+	  return apply(#'call_other,ob,func,args);
+	}
+
+This will result in calling ob->func(args[0],args[1],...,args[sizeof(args)-1]).
+Using funcall() instead of apply() would have given us ob->func(args).
+
+Of course, besides efuns there are closures for operators, like #'+,
+#'-, #'<, #'&&, etc.
+
+Well, so far closures have been pretty much limited despite their
+obvious flexibility. This changes now with the introduction of
+conditional and loop operators. For example, try:
+
+	closure max;
+	max = lambda( ({ 'x, 'y }), ({ #'? ,({ #'>, 'x, 'y}), 'x, 'y }) });
+	return funcall(max,7,3);
+
+The above example will return 7. What happened? Of course #'? is the
+conditional operator and its 'syntax' is as follows:
+
+	({ #'?, cond1, val1, cond2, val2, ..., condn, valn, valdefault });
+
+It evaluates cond1, cond2, ..., condn successively until it gets a
+nonzero result and then returns the corresponding value. If there is no
+condition evaluating to a nonzero result, valdefault gets returned. If
+valdefault is omitted, 0 gets returned. #'?! works just like #'?, except
+that the ! operator is applied to conditions before testing. Therefore,
+while #'? is somewhat like an if statement, #'?! resembles an if_not
+statement if there were one.
+
+	There are also loops:
+	({ #'do, loopbody, loopcond, loopresult })
+	will evaluate loopbody until loopcond evaluates to 0 and
+	then return the value of loopresult. Symbols my be used
+	as variables, of course.
+
+	({ #'while, loopcond, loopresult, loopbody })
+	works similar but evaluates loopcond before loopbody.
+
+There are, however, some questions open:
+
+a) How do I write down an array within a lambda closure to avoid
+   interpretation as a subclosure?
+   ({ #'member_array, 'x, ({ "abc", "xyz }) }) will obviously result in
+   an error as soon as lambda() tries to interpret "abc" as a closure
+   operator. The solution is to quote the array, as in:
+   ({ #'member_array, 'x, '({ "abc", "xyz }) }). Applying lambda() to
+   this will not result in an error. Instead, the quote will be stripped
+   from the array and the result regarded as a normal array literal. The
+   same can be achieved by using the efun quote(), e.g.:
+   ({ #'member_array, 'x, quote( ({ "abc", "xyz }) ) })
+b) Isn't it a security risk to pass, say, a closure to the master object
+   which then evaluates it with all the permissions it got?
+   Luckily, no. Each closure gets upon compilation bound to the object
+   defining it. That means that executing it first sets this_object()
+   to the object that defined it and then evaluates the closure. This
+   also allows us to call lfuns which might otherwise be undefined in
+   the calling object.
+   There is however, a variant of lambda(), called unbound_lambda(),
+   which works similar but does not allow the use of lfuns and does not
+   bind the closure to the defining object. The drawback is that trying
+   to evaluate it by apply() or funcall() will result in an error. The
+   closure first needs to be bound by calling bind_lambda().
+   bind_lambda() normally takes one argument and transforms an unbound
+   closure into a closure bound to the object executing the
+   bind_lambda().
+   Privileged objects, like the master and the simul_efun object (or
+   those authorized by the privilege_violation() function in the master)
+   may also give an object as the second argument to bind_lambda(). This
+   will bind the closure to that object. A sample application is:
+
+   dump_object(ob)
+   // will dump the variables of ob to /dump.o
+   {
+     closure save;
+     save = unbound_lambda( ({ }), ({ #'save_object, "/open/dump" }) );
+     bind_lambda(save,ob);
+     funcall(save);
+   }
+
+   bind_lambda() can also be used with efun closures.
+
+c) It might be an interesting application to create closures dynamically
+   as an alternative to writing LPC code to a file and then loading it.
+   However, how do I avoid doing exactly that if I need symbols like 'x
+   or 'y?
+   To do that one uses the quote() efun. It takes a string as its
+   argument and transforms it into a symbol. For example, writing
+   quote("x") is exactly the same as writing 'x.
+
+d) How do I test if a variable holds a closure?
+   Use the closurep() efun which works like all the other type testing
+   efuns. For symbols there is also symbolp() available.
+
+e) That means, I can do:
+   if (closurep(f)) return funcall(f); else return f; ?
+   Yes, but in the case of funcall() it is unnecessary. If funcall()
+   gets only one argument and it is not a closure it will be returned
+   unchanged. So return funcall(f); would suffice.
+
+f) I want to use a function in some object as a closure. How do I do
+   that?
+   There are several ways. If the function resides in this_object(),
+   just use #'func_name. If not, or if you want to create the function
+   dnynamically, use the efun symbol_function(). It takes a string as
+   it first and an object as its second argument and returns a closure
+   which upon evaluation calls the given function in the given object
+   (and faster than call_other(), too, if done from inside a loop,
+   since function search will be done only when calling symbol_function().
+
+g) Can I create efun closures dynamically, too?
+   Yes, just use symbol_function() with a single argument. Most useful
+   for marker objects and the like. But theoretically a security risk
+   if not used properly and from inside a security relevant object.
+   Take care, however, that, if there is a simul_efun with the same
+   name, it will be preferred as in the case of #'function. Use the
+   efun:: modifier to get the efun if you need it.
+
+h) Are there other uses of closures except using them to store code?
+   Lots. For example, you can use them within almost all of the
+   efuns where you give a function as an argument, like filter(),
+   sort_array() or walk_mapping(). sort_array(array,#'>) does indeed
+   what is expected. Another application is set_prompt(), where a
+   closure can output your own prompt based on the current time and other
+   stuff which changes all the time.
+
+Finally, there are some special efun/operator closures:
+
+#'[ indexes an array.
+#'[< does the same, but starting at the end.
+#'negate is for unary minus.
+#', may be followed by any number of closures,
+e.g.: ({ #', ({#'= 'h, 'a, }), ({#'=, 'a, 'b }), ({#'=, 'b, 'h }) })
+will swap 'a and 'b when compiled and executed.
+
+------------
+An example from Amylaar:
+
+#I have tested the replace_program() functionality with one example, which I
+#include below. The room is commonly known as /room/orc_valley.c .
+#A prerequisite to make this work is to have valued properties in room.c .
+#The property C_EXTRA_RESET, if defined, is evaluated at reset time in
+#the reset() of the used room.c .
+#Moreover, you need a function to query an unprocessed property to use
+#orc_valley.c from fortress.c (That is, don't do an automatic funcall there.)
+#If you can't supply such a function, you have to set the property "get_orc"
+#at the end of orc_valley.c 's extra_reset() with:
+#    add_prop("get_orc", lambda(0, get_orc) );
+#which will set the property to a function that returns the function that
+#is in the variable get_orc at the time you do the add_prop() call.
+#
+#Back to fortress.c : Assume you have the function successfully queried and
+#stored in the variable get_orc. Now you can compute your extra_reset()
+#function by:
+#    get_orc = lambda( 0, ({#'funcall, get_orc, 8, 40}) );
+#which creates the usual 8 orcs with an a_chat chance of 40.
+#
+#Here comes the orc_valley.c source:
+#
+#    ----------- cut here ------- cut here -------- cut here ------------
+#
+##include "room.h"
+##include "/sys/stdproperties.h"
+##undef EXTRA_RESET
+##define EXTRA_RESET extra_reset();
+#
+#extra_reset() {
+#    closure get_orc;
+#
+#    replace_program("room/room"); /* Must come first. */
+#    get_orc = lambda( ({'num_orcs, 'chat_chance}),
+#       ({#'?!, ({#'present, "orc", ({#'previous_object}) }),
+#           ({#'do,
+#               ({#'=, 'orc, ({#'clone_object, "obj/monster"}) }),
+#               ({#'=, 'i, 9}),
+#               ({#'do,
+#                   ({#'call_other, 'orc, "set_level",
+#                       ({#'+, ({#'random, 2}), 1}) }),
+#                   ({#'call_other, 'orc,
+#                       ({#'[,
+#                           quote(({"set_aggressive", "set_ac", "set_short",
+#                              "set_al", "set_ep", "set_hp", "set_race",
+#                              "set_alias", "set_name"})), ({#'-=, 'i, 1}) }),
+#                       ({#'[,
+#                           quote(({1, 0, "An orc", -60, 1014, 30, "orc",
+#                              "dirty crap", "orc"})), 'i}) }),
+#               'i, 0}),
+#               ({#'call_other, 'orc, "load_a_chat", 'chat_chance,
+#                   quote(({ "Orc says: Kill 'em!\n",
+#                       "Orc says: Bloody humans!\n",
+#                       "Orc says: Stop 'em!\n",
+#                       "Orc says: Get 'em!\n",
+#                       "Orc says: Let's rip out his guts!\n",
+#                       "Orc says: Kill 'em before they run away!\n",
+#                       "Orc says: What is that human doing here!\n",
+#                   })) }),
+#               ({#'=, 'n, ({#'*, ({#'random, 3}), 5}) }),
+#               ({#'=, 'weapon, ({#'clone_object, "obj/weapon"}) }),
+#               ({#'=, 'i, 5}),
+#               ({#'do,
+#                   ({#'call_other, 'weapon,
+#                       ({#'[,
+#                           quote(({ "set_alt_name", "set_weight", "set_value",
+#                               "set_class", "set_name"})), ({#'-=, 'i, 1}) }),
+#                       ({#'[,
+#                           quote(({ "knife", 1, 8,  5, "knife",
+#                               "knife", 1, 15, 7, "curved knife",
+#                               "axe",   2, 25, 9, "hand axe",  })),
+#                           ({#'+, 'n, 'i}) }) }),
+#               'i, 0}),
+#               ({#'transfer, 'weapon, 'orc}),
+#               ({#'command,
+#                   ({#'+, "wield ",
+#                       ({#'call_other, 'weapon, "query_name"}) }), 'orc}),
+#               ({#'move_object, 'orc, ({#'previous_object}) }),
+#           ({#'-=, 'num_orcs, 1}), 0})
+#       })
+#    );
+#    add_prop("get_orc", get_orc);
+#    get_orc = lambda( 0, ({#'funcall, get_orc, 2, 50}) );
+#    add_prop(C_EXTRA_RESET, get_orc);
+#    funcall(get_orc);
+#}
+#
+#TWO_EXIT("room/slope", "east",
+#        "room/fortress", "north",
+#        "The orc valley",
+#        "You are in the orc valley. This place is inhabited by orcs.\n" +
+#        "There is a fortress to the north, with lot of signs of orcs.\n", 1)
+#
+#    ----------- cut here ------- cut here -------- cut here ------------
+#
+Procedural elements:
+====================
+
+definition of terms:
+  <block>  : zero or more values to be evaluated.
+  <test>   : one value to be evaluated as branch or loop condition.
+  <result> : one value to be evaluated at the end of the execution of the
+             form; the value is returned.
+  <lvalue> : local variable/parameter, global variable, or an indexed lvalue.
+useded EBNF operators:
+{ }     iteration
+[ ]     option
+
+forms:
+  ({#', <body> <result>})
+  ({#'? { <test> <result> } [ <result> ] })
+  ({#'?! { <test> <result> } [ <result> ] })
+  ({#'&& { test } })
+  ({#'|| { test } })
+  ({#'while <test> <result> <body>})    loop while test evaluates non-zero.
+  ({#'do <body> <test> <result>})       loop till test evaluates zero.
+  ({#'= { <lvalue> <value> } })         assignment
+                                        other assignment operators work too.
+
+lisp similars:
+  #',           progn
+  #'?           cond
+  #'&&          and
+  #'||          or
+  #'while       do      /* but lisp has more syntactic candy here */
+  #'=           setq
+
+A parameter / local variable 'foo' is referenced as 'foo , a global
+variable as ({#'foo}) . In lvalue positions (assignment), you need not
+enclose global variable closures in arrays.
+
+Call by reference parameters are given with ({#'&, <lvalue>})
+
+Some special efuns:
+#'[             indexing
+#'[<            indexing from the end
+#'negate        unary -
+
+Unbound lambda closures
+=======================
+
+These closures are not bound to any object. They are created with the efun
+unbound_lambda() . They cannot contain references to global variables, and
+all lfun closures are inserted as is, since there is no native object for
+this closure.
+You can bind and rebind unbound lambda closures to an object with efun
+bind_lambda() You need to bind it before it can be called. Ordinary objects
+can obly bind to themselves, binding to other objects causes a privilege
+violation().
+The point is that previous_object for calls done from inside the closure
+will reflect the object doing bind_lambda(), and all object / uid based
+security will also refer to this object.
+
+
+The following is mostly vapourware.
+Well, another application would be that some things in the driver can be,
+sort of, microprogrammed.
+The master object could set some hooks in inaugurate_master(), like creating
+the code for move_object(), using a primitive low_move_object() or
+__move_object() or such. All calls of init(), exit(), etc. can thus be
+controlled on mudlib level.
+The driver would do an implicit bind_lambda() to the victim when the closure
+is used.
+
+e.g.
+({#'?, ({#'=, 'ob, ({#'first_inventory, 'destination}) }),
+    ({#'do,
+        ({#'call_other, 'ob, "init"}),
+    ({#'=, 'ob, ({#'next_inventory, 'ob}) }), 0 })
+})
+
+or
+
+({#'filter_objects, ({#'all_inventory, 'destination}), "init"})
+/* Won't show init failures due to move/destruct */
+
+is equivalent to
+
+if (ob = first_inventory(destination) ) {
+    do {
+        ob->init();
+    } while(ob = next_inventory(ob) );
+}
+
+and it's speed is mainly determined by the call_other. Thus, it shouldn't be
+noticably slower than the current C code in move_object().
+
diff --git a/doc/LPC/comments b/doc/LPC/comments
new file mode 100644
index 0000000..a3b0fd0
--- /dev/null
+++ b/doc/LPC/comments
@@ -0,0 +1,44 @@
+NAME
+        comments
+
+SYNTAX
+        /* block comment text */
+        // line comment text <end of line>
+
+
+DESCRIPTION
+        Comments are used to stored arbitrary text in the LPC program
+        source. It is a good idea if some if this text explains the
+        deeper intentions behind the actual LPC statements.
+
+        There are block comments and line comments.
+
+        Block comments start with a '/*' and end with a '*/'. They cannot
+        be nested, so
+
+          /* this /* is */ illegal */
+
+        will treat '/* this /* is */' as the comment.
+
+        Line comments start with '//' and continue until the unescaped(!)
+        end of the line (as in the new C standard).
+
+        It is not possible to next block and line comments within
+        each other. Meaning: '//' within /* ... */ has no special meaning,
+        neither does '/*' or '*/' have after a //.
+
+EXAMPLES
+        /* Simple block comment */
+
+        /* Block comments can
+           span several lines */
+
+        // Simple line comment
+
+        // Line comments can \
+           span several lines, too!
+
+        //#define LONG_MACRO The unique behaviour \
+           or line comments regarding the end of line \
+           can be used for example to comment out a \
+           large macro with just to keystrokes.
diff --git a/doc/LPC/do-while b/doc/LPC/do-while
new file mode 100644
index 0000000..8f69b2a
--- /dev/null
+++ b/doc/LPC/do-while
@@ -0,0 +1,15 @@
+NAME
+        do-while
+
+SYNTAX
+        do { statement } while(expr);
+
+DESCRIPTION
+        Execute 'statment' until 'expr' evaulates to 0.
+
+        A 'break' in the 'statement' will terminate the loop. A
+        'continue' will continue the execution from the beginning of
+        the loop.
+
+SEE ALSO
+        for(LPC), foreach(LPC), while(LPC), if(LPC), switch(LPC)
diff --git a/doc/LPC/ed0 b/doc/LPC/ed0
new file mode 100644
index 0000000..0edac7f
--- /dev/null
+++ b/doc/LPC/ed0
@@ -0,0 +1,39 @@
+NAME
+        ed0
+
+DESCRIPTION
+        When in 'ed', the prompt is ':'.
+
+        Ed has two modes, command mode and insert mode. The insert
+        mode has no prompt. You exit the insert mode by typing a
+        single '.' on a line.
+
+        All commands have the following syntax:
+
+        X,Ycmd
+
+        or
+
+        Xcmd
+
+        For example:
+
+        1,10p
+        Will print line 1 to 10.
+        1,5d
+        Will delete line 1 to 5.
+        8p
+        Will print line 8.
+        A '.' is the "current line". The current line is the last line
+        referenced. If you want to print last line + 10 more:
+        .,.+10p
+
+NOTE
+        These manpages seem a bit antique, though still correct. For a
+        better detailed help, invoke ed and use the ``h'' command.
+        Also you could look into the man page for ed(1) on you nearest
+        Unix box. And for a bit of fun you can try the good old
+        quiz(6), invoke as ``quiz function ed-command''.
+
+SEE ALSO
+        ed1(LPC), ed(E)
diff --git a/doc/LPC/ed1 b/doc/LPC/ed1
new file mode 100644
index 0000000..25f3ac2
--- /dev/null
+++ b/doc/LPC/ed1
@@ -0,0 +1,32 @@
+NAME
+        ed1
+
+DESCRIPTION
+        Commands that use a line range:
+        If no line is given, then curent line is printed.
+
+        p        Print line.
+        d        Delete line.
+        l        Print line with control characters.
+        r file        Read in a file after the line specified.
+        s        Substitute patterns. See special documentation.
+        z        Print 10 lines.
+        a        Start insert mode after specified line. Exit with
+                '.'<return>.
+        i        Start insert mode before specified line. Exit with
+                '.'<return>.
+
+        Commands used without line specification:
+
+        q        Quit. Won't work if file is changed.
+        Q        Quit and discard all changes if not saved.
+        w        Write the file out.
+        w file        Write the file out with name 'file'.
+        e file        Edit a file.
+        !cmd        Give a game command. For example "say Wait, I am busy".
+
+        As line numbers '.' is current line, and '$' is last line of
+        file. Thus '1,$p' will always print all of the file.
+
+SEE ALSO
+        ed2(LPC)
diff --git a/doc/LPC/ed2 b/doc/LPC/ed2
new file mode 100644
index 0000000..85d520e
--- /dev/null
+++ b/doc/LPC/ed2
@@ -0,0 +1,25 @@
+NAME
+        ed2
+
+DESCRIPTION
+        Substitutions are very advanced.
+
+        First a simple example:
+
+        s/apa/bepa/
+        This will substitue the 'apa' in current line to 'bepa'.
+        If an 'p' is appended, you will also immediately see the result.
+
+        1,$s/apa/bepa/
+        Same, but all lines in file. Only first occurence on every line.
+
+        Any character can used instead of '/':
+        s!apa!bepa!g
+        The 'g' specifies that all occurences of apa on this line are
+        changed to bepa.
+
+        The pattern that are supposed to be replaced, can be a regular
+        expression. See ed3 about that.
+
+SEE ALSO
+        ed3(LPC)
diff --git a/doc/LPC/ed3 b/doc/LPC/ed3
new file mode 100644
index 0000000..380c008
--- /dev/null
+++ b/doc/LPC/ed3
@@ -0,0 +1,30 @@
+NAME
+        ed3
+
+DESCRIPTION
+        Searching is done with:
+        /hello/
+        Find first line in of after current line.
+        Just // will repeat the search.
+
+        The patterns are regular expressions, where some characters
+        have a special meaning:
+        .        Match any character.
+        x*        Match any numbers of x (0 or more).
+        [abc]        Match 'a', 'b' or 'c'.
+        [0-9]        Match any digit 0 - 9.
+        [a-z]        Match any lowercase letter.
+        \x        Match 'x' where 'x' can be any character except '('
+                and ')'.
+
+EXAMPLE
+        s/ab.d/ABCD/
+        Substitute any string 'abXd' against 'ABCD' where X can be any
+        character.
+
+NOTE
+        This only half the truth, there is lots more magic in the
+        regexps.
+
+SEE ALSO
+        regexp(E), ed4(LPC)
diff --git a/doc/LPC/ed4 b/doc/LPC/ed4
new file mode 100644
index 0000000..2d5303c
--- /dev/null
+++ b/doc/LPC/ed4
@@ -0,0 +1,12 @@
+NAME
+        ed4
+
+DESCRIPTION
+        How to copy from a standard file.
+
+        Enter ed. Then do 'r /room/vill_green.c'. Now you have
+        something in the buffer. Change it into what you want it to
+        be. Then 'w /players/peter/hall.c'. Or 'w hall.c'.
+
+SEE ALSO
+        ed5(LPC)
diff --git a/doc/LPC/ed5 b/doc/LPC/ed5
new file mode 100644
index 0000000..9156ff3
--- /dev/null
+++ b/doc/LPC/ed5
@@ -0,0 +1,47 @@
+NAME
+        ed5
+
+DESCRIPTION
+        = : prints current line
+        a : append lines
+        c : change, that is, delate, than insert
+        d : delete line(s)
+        E <filename> :  discard current buffer and edit the file named
+                        <filename>
+        e <filename> : like e, but refuse if file has been changed
+        f : print current filename
+        f <filename> : set filename
+        i : insert line(s)
+        j : with no or one argument: join line with following line
+        j : with two arguments : join line range given
+        k<letter> : set mark <letter> to current line. <letter> must
+                be in the range [a-z] . The mark can be used
+                thereinafter as a line address, with a leading slash.
+                (e.g. ka to set mark a, /ap to print marked line)
+        l : print line with control characters
+        <start>,<end>m<dest> : move block (lines from start to end)
+                                behind line # dest
+        <line>m<dest>        : move single line
+        m<dest>                : move current line
+        M : remove Ctrl-M (CR) characters.
+        p : print line
+        P : print line
+        Q : quit, discarding the buffer
+        q : the same, but refuse if file has been changed since last
+            write
+        r : read in file. If no adress is given, at the last insert
+            position, if also nothing was inserted, at the end of the
+            buffer
+        s : substitute
+        <start>,<end>t<dest>        : copy block ( lines from start to end )
+                                  behind line position dest
+        <line>t<dest>                : copy single line
+        t<dest>                        : copy current line
+        w : write file
+        W : write file
+        x : write file if buffer has been changed since last change,
+            then quit
+        z : show approx. a screenful of lines
+
+SEE ALSO
+        ed6(LPC)
diff --git a/doc/LPC/ed6 b/doc/LPC/ed6
new file mode 100644
index 0000000..8b80a70
--- /dev/null
+++ b/doc/LPC/ed6
@@ -0,0 +1,32 @@
+NAME
+        ed6
+
+DESCRIPTION
+        This is the list of extended ed commands that Xio unearthed
+        somewhere, thanks!
+
+        a) never use 1,$p to print out an editfile, because you will
+          be thrown out 'cause of too much text transfereed to you.
+
+        b) $: jump to end of file.
+
+        c) ?anything? and ?? : search from bottom to up. (like '/'
+           from beginning to end of file. (also with substitutions,
+           try out..)
+
+        d) ( g/xxx/p search global xxx and print corresponding lines,
+           /xxx/s/new/old/p : search xxx, substitute new to old in this
+           line and print out. (try this concatenations with other
+           commands)
+
+        e) 1,nmx ( see ed5 ) , but also: 1,ntx : don't move the lines,
+           but make a copy of them.
+
+        f) x,y w name : save lines x to y to file name (if you don't
+           know the line numbers : '=' current line number)
+
+        g) s/$/text/p : append text to the end of current LINE and
+           print line
+
+        h) s/^/text/p : insert text at beginning og current LINE and
+           print line
diff --git a/doc/LPC/efuns b/doc/LPC/efuns
new file mode 100644
index 0000000..ca5b224
--- /dev/null
+++ b/doc/LPC/efuns
@@ -0,0 +1,16 @@
+CONCEPT
+        efuns
+
+DESCRIPTION
+        Efuns are "system calls" in the LPC driver, the C program
+        which compiles and executes the LPC programs of the mudlib.
+        These are hardcoded functions which perform basic tasks which
+        would be ineffective or impossible to be implemented in the
+        mudlib.
+
+        There are efuns for accessing the underlying filesystem, for
+        creating, moving and destructing objects, for writing
+        messages to users, for manipulation of LPC data types.
+
+SEE ALSO
+        efun(E), lfuns(LPC), applied(A), master(M)
diff --git a/doc/LPC/escape b/doc/LPC/escape
new file mode 100644
index 0000000..8518749
--- /dev/null
+++ b/doc/LPC/escape
@@ -0,0 +1,81 @@
+CONCEPT
+        character escape codes
+
+DESCRIPTION
+	Character escape codes are used to represent some common
+	special characters that would be awkward or impossible to
+	enter in the source program directly. The escape characters
+	come in two varieties: 'character escapes', which can be
+	used to represent some particular formatting and special
+	characters, and 'numeric escapes', which allow a character to
+	be specified by its numeric encoding.
+
+	Escapes begin always with a backslash '\'. If the following
+	characters could not be treated as a valid escape the backslash
+	is merely ignored.
+
+	The following character escapes are available in LPC (the code
+	may differ from platform to platform):
+
+	  \a    Code 007    Bell
+          \b    Code 008    Backspace
+          \e    Code 027    Escape
+	  \f    Code 012    Formfeed
+	  \n    Code 010    Newline
+	  \r    Code 013    Carriage-Return
+	  \t    Code 009    Tabulator
+	  \\    Code 092    Backslash itself
+	  \'    Code 039    Single quote
+	  \"    Code 034    Double quote
+
+	The single quote may appear without preceding backslash in character
+	constants, and the double quote may appear without a backslash in
+	string constants.
+
+	The numeric escapes could be used to express a character directly
+	by its code in binary, octal, decimal or hexadecimal notation.
+	
+	  \0b   Beginning of binary notation
+	  \0o   Beginning of octal notation
+	  \0x   Beginning of hexadecimal notation
+	  \x    Beginning of hexadecimal notation
+
+	A backslash followed by a digit ([0-9]) which does not map to one
+	of the above starts an escape in decimal notation.
+
+	A numeric escape terminates when N digits have been used up or
+	when the first character that is not a valid digit in that
+	notation is encountered. N is 2 for hexadeximals, 3 for
+	decimals and octals and 8 for binarys.
+
+	If the specified code is greater than 255 a warning is issued and
+	the value modulo 256 is used.
+
+EXAMPLES
+	Put a newline at the end of user output
+	  "You enter.\n"
+
+	Alert the user
+	  "Beeep.\a Wake up\n"
+
+	Put a double quote in a string
+	  "You say \"hello\"\n"
+
+	Write the line from above
+	  "\"You say \\\"hello\\\"\\n\""
+
+	Put a single quote in a string
+	  "You say 'hello'\n"
+
+	Some forms to write "abcde"
+	  "abcde"
+	  "ab\99de"              (with c's code being 99)
+	  "ab\099de"
+	  "ab\x63de"             (99 = 0x63)
+	  "ab\0x63de"
+
+	The following string consists of two characters
+	  "\0111"                (\011 and 1)
+
+	The following string consists of three characters
+	  "\0o090"               (\000 and 9 and 0)
diff --git a/doc/LPC/for b/doc/LPC/for
new file mode 100644
index 0000000..e7a988d
--- /dev/null
+++ b/doc/LPC/for
@@ -0,0 +1,36 @@
+NAME
+        for
+
+SYNTAX
+        for(init; expr2; expr3) statement;
+
+DESCRIPTION
+        Execute <init> once. Then, while <expr2> returns a non-zero
+        value, execute <statement>. Every time <statement> has been
+        executed, or a 'continue' statement has been executed, execute
+        <expr3> before next loop.
+
+        <init> is usually a series of one or more expressions (remember
+        that assignments are expressions, too), separated by commas.
+        Additionally it is also allowed to define new local variables
+        here and assign them an initial value. The scope of such variables
+        is the whole for statement.
+
+        Examples for legal <init> expressions are:
+
+           for (i = 0; ...
+           for (i = 0, j = 0; ...
+           for (i = 0, int j = i; ...
+           for (int j = 4; ...
+
+        Illegal <init> expressions are:
+
+           for (int i; ...      : no value assigned
+           for (int i += 4; ... : only plain assignments allowed
+
+        A 'break' in the 'statement' will terminate the loop. A
+        'continue' will continue the execution from the beginning of
+        the loop.
+
+SEE ALSO
+    foreach(LPC), if(LPC), do-while(LPC), while(LPC), switch(LPC)
diff --git a/doc/LPC/foreach b/doc/LPC/foreach
new file mode 100644
index 0000000..7e6060b
--- /dev/null
+++ b/doc/LPC/foreach
@@ -0,0 +1,132 @@
+NAME
+        foreach
+
+SYNTAX
+        foreach (<var> : <expr>) <statement>;
+        foreach (<var>, <var2>, ... ,<varN> : <expr>) <statement>;
+
+        foreach (<var> : <expr1> .. <expr2>) <statement>;
+        foreach (<var>, <var2>, ... ,<varN> : <expr1>..<expr2> ) <statement>;
+
+        /* MudOS compatibility only - not for new code: */
+        foreach (<var> in <expr>) <statement>;
+        foreach (<var>, <var2>, ... ,<varN> in <expr>) <statement>;
+
+DESCRIPTION
+        The instruction evaluates its range specification - either a
+        simple <expr> which can yield an array, a struct, a string, a
+        mapping or an integer, or an integer range <expr1> through
+        <expr2> - and executes <statement> once for each value in the
+        range. The respective value is assigned to <var> right before
+        <statement> is executed.
+
+        A 'break' in the <statement> will terminate the loop. A
+        'continue' will continue the execution from the beginning of
+        the loop.
+
+        Every <var> specification can declare a new local variable, whose
+        scope is the whole foreach() statement.
+
+
+        The normal form (one <expr>):
+
+            <expr> is evaluated and has to yield an array, a struct, a
+            string or a mapping (or reference to the former), or an
+            integer.
+
+            If <expr> is a array, struct, or string, the values of
+            <expr> (in case of the string, the integer values of the
+            characters) are then assigned one by one in order of
+            occurence to <var>, and <statement> is executed for every
+            assignment.
+
+            If <expr> is a mapping, the keys are assigned one by one
+            to <var>, and the values for each key are assigned in
+            order to <var2>..<varN>.  If there are more values than
+            variable, the extraneous values are ignored. Due to the
+            nature of mappings, a specific order of the keys can not
+            be guaranteed.
+
+            If <expr> evaluates to a reference to an array, mapping, or
+            string, the loop will assign references to the values into
+            the variables. This allows the loop body to change the contents
+            of the original data.
+
+            If <expr> evalutes to an integer, the loop will count up <var>
+            from 0 to <expr>-1, basically implementing a count loop.
+
+            If there are more variables than necessary, the unneeded ones
+            are not changed.
+
+
+        The ranged form (<expr1> .. <expr2>):
+
+            <expr1> and <expr2> are evaluated and must yield integers.
+            The loop will count up <var> from <expr1> to <expr2>, basically
+            implementing a counted loop.
+
+            If <expr1> is less than <expr2>, the loop will terminate at once.
+
+            If there are more than variable, the unneeded ones are not
+            changed.
+
+
+
+        WHAT HAPPENS IF <expr> IS CHANGED IN THE LOOP?
+
+        If <expr> yields an array or struct:
+         - assignments to single elements or to array ranges effect
+           the values assigned to the variable:
+             a = ({1, 2, 3})
+             foreach(x : a) { a[1..2] = ({4, 5}); write(x+" "); }
+           will write ("1 4 5 ").
+         - operations which implicitely copy the array or struct (this
+           includes range assignments which change the size) don't
+           have an effect on the loop.
+
+        If <expr> yields a mapping, the loop will run over the indices
+        the mapping had at the begin of the loop. Deleted indices are silently
+        skipped, new indices ignored, but changes of the data of existing
+        indices are acknowledged.
+
+        If <expr> yields a string, the value used at the start of the loop
+        remains.
+
+
+WARNING
+        The additional syntax forms using "in" as keyword are meant
+        to make re-engineering of MudOS objects easier. Do not use them
+        for newly written code, as they may not be available in future.
+
+
+EXAMPLES
+        // Call quit() in all interactive users
+        foreach(o : users()) o->quit();
+        foreach(object o : users()) o->quit();
+
+        // Print the contents of a mapping <m>
+        foreach(key, value : m) printf("%O:%O\n", key, value);
+        foreach(mixed key, mixed value : m) printf("%O:%O\n", key, value);
+
+        // Don't change the content of a string: s remains "FOOBAR".
+        s = "FOOBAR";
+        foreach(i : s) i += 32;
+
+        // Do change the content of a string: s will become "foobar".
+        s = "FOOBAR";
+        foreach(i : &s) i += 32;
+
+        // Count from 0 to 5
+        foreach(i : 6) printf("%d\n", i);
+
+        // Count from 1 to 6
+        foreach(i : 1 .. 6) printf("%d\n", i);
+
+HISTORY
+        LDMud 3.3.44 introduced the use of references, the loop over
+          an integer expression, and the loop over an integer range.
+        LDMud 3.3.266 added support for structs.
+
+
+SEE ALSO
+        for(LPC)
diff --git a/doc/LPC/functions b/doc/LPC/functions
new file mode 100644
index 0000000..50c39a0
--- /dev/null
+++ b/doc/LPC/functions
@@ -0,0 +1,174 @@
+CONCEPT
+        functions
+
+DESCRIPTION
+        Functions are named blocks of code which are be called with
+        a number of argument values, and which return a result value
+        to the caller.
+
+        Functions are defined in an object and are also known as
+        "local funs" or short "lfuns".
+
+
+        DEFINING A FUNCTION
+
+        A function definition takes the form
+
+          <modifiers> <type> name ( <arguments> ) {
+             statements...
+          }
+
+        The parts in detail:
+          - <modifiers> can be any one of "static", "private", "public"
+              and "protected" (see modifiers(LPC)), optionally combined
+              with "varargs" (see varargs(LPC)) and/or "nomask".
+              If not specified, the function behaves as if it was
+              specified as "public", but this visibility can be restricted
+              in derived object through non-public inheritance.
+          - <type> is the type of the result returned by the function.
+              If specified as "void", the function is compiled to return
+              the value 0 under all circumstances. If not specified, the
+              type is assumed to be "mixed", furthermore typechecking is
+              disabled for this function.
+          - name is the name of the function, e.g. "short", or "Nice_Try",
+              under which it is made known.
+          - <arguments> is a list of variable definitions in the
+              normal '<type> <name>' style, separated by comma.
+              Examples: () : no argument taken
+                        (int a) : takes on integer argument
+                        (mixed a, object *b): takes two arguments, one
+                           arbitrary type, one array of objects.
+          - { statements... } defines the code for this function. This
+              is a normal block (see block(LPC)) and as such can define
+              its own local variables.
+
+
+        DECLARING A FUNCTION
+
+        A function declaration makes the name and type of a function known
+        to the compiler with the assertion that the code for this function
+        will be provided "elsewhere".
+
+        The form is:
+
+          <modifiers> <type> name ( <arguments> );
+
+        Typical uses are:
+          - to declare in advance functions which are called before they
+            can be defined; for example if the create() function of an object
+            calls other functions which are defined after the create().
+          - to declare functions which will be provided by an inheriting
+            object.
+
+        Calling a declared but undefined function results in a runtime error.
+
+
+        CALLING A FUNCTION
+
+        Functions in other objects are called with the call_other() efun,
+        which can be shortened to '->':
+
+           ob->fun(a, b, c)
+           call_other(ob, "fun", a, b, c)
+
+        Note: See the entry H_DEFAULT_METHOD in hooks(C) for a modification
+        of this behaviour.
+
+        Functions in the same object are called just by writing their name,
+        followed by the arguments in parenthesis:
+
+           short()
+           compute(a)
+           do_that(a, "foo")
+
+        Array function arguments can be 'flattened' with the '...' operator.
+        For example:
+
+           mixed * m = ({ "foo", "bar" });
+           fun(m..., 1);
+
+        will be executed as:
+
+           fun("foo", "bar", 1);
+
+
+        If the number of values passed to the function does not match the
+        number of expected arguments (and if type checking is not enabled), the
+        driver will perform the necessary adaption at call time: excess
+        values are ignored, missing values are substituted by the number 0.
+        The values passed to the called function are massaged by the driver
+        to match the argument list
+
+
+        FUNCTIONS AND INHERITANCE
+
+        A "public" or "protected" (== "static") function defined in one
+        object is also visible in all inheriting objects. The exception from
+        this rule is when an inheriting child redefines ("overloads") the
+        inherited function with its own. When compiling with type checking,
+        the argument list of the redefined function has to match the
+        original one.
+
+        When a function is called, the driver looks for the function first
+        in the object called, and if not found there, then in the inherited
+        objects.
+
+        To explicitely call an inherited function (useful when a redefining
+        functions wants to use the original one), the "::" operator is used:
+
+            ::create()
+            ::compute(a)
+
+        The named function is searched only in the inherited objects, and
+        the first found is used.
+
+
+        If the function is inherited from several objects and a specific
+        one is to be called, the "::" can be extended to contain the
+        partial or full name of the inherited object:
+
+            inherit "/obj/cooker";
+            inherit "/obj/container";
+
+            tainer::create()
+            container::create()
+            "tainer"::create()
+            "container"::create()
+            "obj/container"::create()
+            "/obj/container"::create()
+
+        all call the create() in the container inherit. Note that the
+        name given to the :: operator is matched against the ends of
+        the inherited names.
+
+
+        One special form of this call is
+
+            efun::find_object()
+
+        which bypasses any redefinition of an efun (here find_object())
+        and directly calls the efun itself. This is only possible for
+        efun-redefinitions which do not use the "nomask" modifier.
+
+
+        Additionally, a call to a function inherited from several objects
+        can be instructed to call _all_ inherited functions through the
+        use of the wildcards "*" (match any number of arbitrary characters)
+        and "?" (match one arbitrary character):
+
+            inherit "/obj/cooker";
+            inherit "/obj/container";
+
+            "*"::create()
+            "co*"::create()
+            "*er"::create()
+
+        all call both inherited create()s. The function called this way
+        must not take arguments, and the single results from all calls are
+        combined into one array used as final result. If there is no such
+        function inherited at all, the statement will just return
+        an empty array.
+
+SEE ALSO
+        types(LPC), modifiers(LPC), varargs(LPC), references(LPC),
+        call_other(E), simul_efun(C), call_out(E)
diff --git a/doc/LPC/if b/doc/LPC/if
new file mode 100644
index 0000000..33bbff2
--- /dev/null
+++ b/doc/LPC/if
@@ -0,0 +1,39 @@
+NAME
+        if
+
+SYNTAX
+        if (expr1) statement1;
+        else if (expr2) statement2;
+        ...
+        else if (exprN) statementN;
+        else statementX;
+
+DESCRIPTION
+        The if() statement implements the conditional execution of statements.
+        The expressions 'expr1' .. 'exprN' are evaluate in the order they
+        appear until one of the expressions returns non-0 ('true'). At that
+        point, the statement associated with the expression is executed, and
+        the program continues after the if() statement. If none of the
+        expressions evaluate to 'true', the statementX in the 'else'-branch
+        is executed.
+
+        Both the 'else if' branches and the 'else' branch are optional, and
+        there can be any number of 'else if' branches - but there must be one
+        'if' branch, and the branches must be in the order given above.
+
+        Any 'else' or 'else if' always relates to the immediately preceeding
+        'if' resp. 'else if' conditional. This means that
+
+          if (a)
+          if (b) do_b;
+          else do_c;
+
+        is interpreted as
+
+          if (a) {
+            if (b) do_b;
+            else   do_c;
+          }
+
+SEE ALSO
+        for(LPC), foreach(LPC), do-while(LPC), while(LPC), switch(LPC)
diff --git a/doc/LPC/inherit b/doc/LPC/inherit
new file mode 100644
index 0000000..0e4c0f2
--- /dev/null
+++ b/doc/LPC/inherit
@@ -0,0 +1,9 @@
+NAME
+        inherit
+
+DESCRIPTION
+        The concept of inheritance and the inherit statement are
+        explained in detail in the man page for ``inheritance''.
+
+SEE ALSO
+        inheritance(C)
diff --git a/doc/LPC/inheritance b/doc/LPC/inheritance
new file mode 100644
index 0000000..23fb804
--- /dev/null
+++ b/doc/LPC/inheritance
@@ -0,0 +1,181 @@
+KONZEPT
+     inheritance
+
+BESCHREIBUNG
+     Mittels Vererbung kann man das Verhalten und/oder die implementierten
+     Methoden eines Objektes in ein neues Objekt hineinerben.
+
+     1. Wozu ist Vererbung gut
+     1.1. Erben von Implementationen: Strukturieren von Bibliotheken
+     Mit Vererbung der Implementation koennen aufeinander aufbauende und gut
+     wart-/nutzbare Strukturen geschaffen werden:
+     
+     /std/thing/description beinhaltet
+     - wie ein Objekt aussieht/welche Details es gibt
+     - wie man das Objekt finden/identifizieren kann
+     /std/thing/moving beinhaltet
+     - wie man ein Objekt bewegen kann
+     /std/thing erbt
+     - /std/thing/description und
+     - /std/thing/moving
+     - damit den Code aus beiden Objekten (und noch andere)
+
+     Sowohl thing/description als auch thing/moving sind nicht als
+     eigenstaendige Objekte sinnvoll nutzbar, sind abstrakt.
+
+     1.2. Erben von Schnittstellen und Verhalten      
+     Durch das Erben von /std/thing werden Schnittstelle und auch
+     Verhalten von /std/thing geerbt:
+     
+     -- keks.c --
+     inherit "/std/thing";
+     --
+     
+     Dadurch koennen jetzt andere Objekte, wie NPCs oder Spieler mit dem
+     Keks-Objekt so interagieren, als wenn es ein /std/thing waere.
+
+     Morgengrauen stellt eine grosse Bibliothek von miteinander sinnvoll
+     agierenden Objekten unter /std zur Verfuegung, Die dort verfuegbaren
+     Objekte sind groesstenteils selbsterklaerend, wie /std/npc,
+     /std/armour oder /std/weapon.
+
+     Das Keks-Objekt muss erweitert werden, wenn es sich vom normalen
+     Ding unterscheiden soll. Typischerweise geschieht das durch
+     Ueberschreiben der Initialisierungmethode namens "create". 
+       
+     2. Ueberschreiben/Erweitern von Verhalten/Methoden
+     2.1. Ueberschreiben
+     Um das Verhalten von geerbten Methoden zu erweitern, muss diese
+     ueberschrieben werden:
+     
+     -- keks.c --
+     ...
+     void create() {
+       SetProp(P_NAME, "Keks");
+       SetProp(P_GENDER, MALE);
+       AddId(({"keks"}));
+     }
+     --
+     
+     Allerdings wuerde jetzt der normale Code von "create" in /std/thing
+     nicht mehr ausgefuehrt werden. Mit dem 'scope'-Operator :: wird
+     innerhalb einer ueberschriebenen Methode auf deren bisherige
+     Implementation zugegriffen:
+
+     -- keks.c --
+     ...
+     void create() {
+       ::create();
+       SetProp(P_NAME, "Keks");
+       SetProp(P_GENDER, MALE);
+       AddId(({"keks"}));
+     }
+     --
+
+     Auf geerbte globale Variablen kann normal zugegriffen werden, wenn 
+     diese nicht vor direktem Zugriff durch erbende Objekte geschuetzt
+     wurden. Also in ihrer Sichtbarkeit veraendert wurde.
+     
+     Ueberschreiben von Methoden in den davon erbenden Objekten kann durch
+     das Schluesselwort 'nomask' verboten werden.
+
+     2.2. Sichtbarkeit von Methoden und Variablen
+     Es ist moeglich, Methoden und Variablen mit einem Modifikator fuer
+     ihre Sichtbarkeit zu versehen, der auch bei der Vererbung zum
+     Tragen kommt:
+     - 'public' Methoden sind von aussen/innen in Eltern/Kind zugreifbar
+     - 'protected' Methoden sind nur von innen in Eltern/Kind zugreifbar
+     - 'private' Methoden sind nur von innen in Eltern zugreifbar
+                 (also nur im definierenden Objekt selbst)   
+
+     2.3 Laufzeit-Polymorphie (vielgestaltes Verhalten)
+     Methoden werden in LPC immer erst zum Zeitpunkt ihres Aufrufs
+     gebunden, also nicht schon beim Laden. Beispielsweise koennen
+     wir unseren Keks essbar machen:
+
+     -- keks.c --
+     ...
+     void create() {
+       ...
+       AddCmd("iss&@ID", "action_essen", "Was willst du essen?");
+     }
+     
+     int action_essen() {
+       if(this_player()->eat_food(1, 0, 
+                                 "Du bekommst "+QueryPronoun(WEN)+
+				 " nicht mehr hineingestopft.\n")>0) {
+         write("Du isst "+name(WEN,1)+".\n");
+         say(this_player()->Name(WER)+" isst "+name(WEN)+".\n");
+         remove();
+       }
+       return 1;
+     }
+     --
+     
+     und jetzt in einem davon erbenden Kruemelkeks diesen beim Entfernen
+     im "remove" kruemeln lassen:
+     
+     -- kruemelkeks.c --
+     inherit "/doc/beispiele/inherit/keks.c";
+     ...
+     varargs int remove(int silent) {
+       if(!silent && living(environment()))
+         tell_object(environment(), Name(WER,1)+
+	                            " kruemelt ein letztes Mal.\n");
+       return ::remove(silent);
+     }
+     --
+     
+     Trotzdem wir "action_essen" nicht modifiziert haben, wird von dieser
+     Methode in einem Kruemelkeks immer automatisch die aktuelle und
+     kruemelende "remove" aufgerufen.
+
+     3. Multiple Inheritance
+     In LPC ist multiple Vererbung moeglich. Das heisst, man kann von 
+     mehreren Objekten erben. Das kann zu Konflikten fuehren, zum Beispiel 
+     wenn eine Methode in zwei Objekten von denen geerbt wurde vorkommt. 
+     
+     Diese Konflikte sollte man dann "per Hand" loesen in dem man diese 
+     spezielle Methode ueberschreibt und mittels des scope-Operators die 
+     gewuenschte(n) geerbt(en) Methode(n) aufruft:
+
+     inherit "/std/weapon";
+     inherit "/std/lightsource";
+
+     void create() {
+       weapon::create();
+       lightsource::create();
+       ...
+     }
+
+     void init() {
+       weapon::init();
+       lightsource::init();
+       // oder sehr generell und unkontrolliert:
+       // "*"::init();
+     }
+
+     Schwerwiegende Konflikte koennen auftreten, wenn eine gleichnamige
+     Variable aus verschiedenen Objekten geerbt wird. Die Variablen
+     existieren im letztlich erbenden Objekt zweimal und die verschiedenen
+     geerbten Methoden greifen auf ihre jeweilige Variante zu.
+
+     Beispiel ist die sog. "Masche" oder der "Diamond of Death": 
+
+                        A    (Ursprungsobjekt mit globaler Variable x)   
+                       / \
+                      B   C  (B, C erben von A und manipulieren beide x)
+                       \ /
+                        D    (D erbt von B und A)
+
+     Mit dem Schluesselwort 'virtual' kann die Doppelung beim Erben
+     in B und C unterbunden werden.
+
+SIEHE AUCH:
+     inherit
+     private, protected, public, nomask
+     virtual
+     objekte, oop
+     /doc/beispiele/inherit
+
+2.Feb 2008 Gloinson
diff --git a/doc/LPC/initialisation b/doc/LPC/initialisation
new file mode 100644
index 0000000..61f3c2b
--- /dev/null
+++ b/doc/LPC/initialisation
@@ -0,0 +1,93 @@
+CONCEPT
+
+        VARIABLE INITIALIZATION
+
+DESCRIPTION
+        Global variables, like their local counterparts, can be defined
+        with an initial value:
+
+            int * a = ({ 3, 4 });
+
+        The initialization value can be any legal LPC expression,
+        including function calls. The code for the initializations is
+        collected in a compiler-generated function __INIT() which is
+        called even before the create-hook is applied on the object.
+
+        During initialization, blueprints and clones are treated
+        slightly differently:
+
+        Blueprint variables are always all initialized using __INIT().
+
+        For clones the programmer can select whether the clone's
+        variables should also be initialized with __INIT(), or if they
+        should be assigned from the current blueprint values
+        ('shared with the blueprint'). The latter method is useful for
+        example if blueprints and clones shall share arrays or
+        mappings.
+
+        The selection is performed with the two pragmas
+        'init_variables' and 'share_variables'. The status of this
+        pragma at the point of the first variable definition counts,
+        and is applied to all variables in the program.
+
+        The clone initialization method is evaluated per-program, i.e.
+        if an inherited program defines 'share_variables' and the child
+        program doesn't, only the inherited variables are initialized
+        from the blueprint, and all others from __INIT().
+
+        The default setting for the pragma is configured into the
+        driver, but can also be chosen when starting the driver.
+        
+EXAMPLE
+        For the object
+
+          ----------
+          inherit "a";
+          int a = 4;
+          int b;
+          ----------
+
+        the compiler executes the equivalent of these __INIT() functions:
+
+        #pragma share_variables:
+
+            unknown __INIT()
+            {
+                "a"::__INIT();
+                if (clonep(this_object()))
+                {
+                    a = (blueprint of this_object())->a;
+                }
+                else
+                {
+                    a = 4;
+                }
+                return 1;
+            }
+
+
+        #pragma init_variables:
+
+            unknown __INIT()
+            {
+                "a"::__INIT();
+                a = 4;
+                return 1;
+            }
+
+
+        In either case the variable 'b' (in fact all variables) are
+        set to '0' as part of the loading/cloning process before the
+        driver performs the specific initialisation.
+
+WARNING
+        Do not call __INIT() yourself, overload, or use it directly in
+        any other way. The implementation of the variable
+        initialization may change at any time.
+
+HISTORY
+        Before LDMud 3.3.378, the choice between sharing and initializing
+          variables was a fixed configuration choice of the driver.
+
+SEE ALSO
+        pragma(LPC), create(H), invocation(D)
diff --git a/doc/LPC/inline-closures b/doc/LPC/inline-closures
new file mode 100644
index 0000000..3a2ecdd
--- /dev/null
+++ b/doc/LPC/inline-closures
@@ -0,0 +1,131 @@
+CONCEPT
+        inline closures
+
+SYNTAX
+        function <returntype> ( <arguments> ) : <context> { <code> }
+
+        (: <statements> ; <expr>, ... , <expr> :)
+
+
+DESCRIPTION
+        Inline closures are a way to program closures which are
+        compiled at the time an object is loaded, but can access
+        values from their enclosing function at runtime.
+
+        Example:
+
+          closure factory (int arg) {
+            return function int (int val) { return val * arg; };
+          }
+
+          closure f1 = factory(2);
+          closure f2 = factory(3);
+          funcall(f1, 3) -> will yield 6.
+          funcall(f2, 3) -> will yield 9.
+
+        The closure here 'inherits' the current value of the local
+        variable 'arg' at the time the closure is created. These
+        values are called the "context" of the closures - they are
+        stored in a special set of variables in the closure.
+
+        One specific feature of the closure context is that it can be
+        changed from within the closure, and that these changes remain
+        permanent:
+
+          closure factory (int arg) {
+            return function int (int val) { return val * arg++; };
+          }
+
+          closure f = factory(2);
+          funcall(f, 3) -> will yield 6.
+          funcall(f, 3) -> will now yield 9!
+
+        But changes of the closure context will not reflect on the
+        local variable it was copied from and vice versa.
+
+        In addition to the implicite context inherited from the
+        defining function, additional context variables can be defined
+        in the closure:
+
+          closure factory (int arg) {
+            return function int (int val) : int x = 2 * arg
+                                          { return val * x; };
+          }
+
+          closure f = factory(2);
+          funcall(f, 3) -> will yield 12.
+
+        It is possible to define multiple context variables with and
+        without initialisation:
+
+          closure factory (int arg) {
+            return function int (int val) : int y, x = 2 * arg;
+                                            int z
+                                          { return val * x; };
+          }
+
+        These explicite context variables are useful when the closures
+        needs to keep a state, or to improve performance:
+
+          mapping m = ...;
+          closure slow (int arg) {
+            return function mixed () { return m[arg]; }
+          }
+          closure fast (int arg) {
+            return function mixed () : mixed val = m[arg] { return val; }
+          }
+
+        In the above example, the fast() function executes the lookup
+        m[arg] only once when the inline closure is created; the
+        slow() function on the other hand returns a closures which
+        looks up m[arg] every time it is called. A second effect is
+        that the results of the slow closure change when m changes;
+        the result of the fast closure is always the same.
+
+
+        In the definition of an inline closure, some elements are
+        optional:
+
+          <returntype>     defaults to 'mixed'
+          ( <arguments> )  defaults to '(mixed $1 ...  mixed $9)'
+          : <context>      no default
+
+
+        The special (: :) form is meant for simple expressions (and
+        MudOS compatibility). The form
+
+          (: <statements> ; <expr>, ..., <expr> :)
+
+        is the shorthand notation for
+
+          function { <statements>; return <expr>, ..., <expr>; }
+
+        For example the two statements
+
+          sort_array(arr, function { return $1 < $2; } )
+          sort_array(arr, (: $1 < $2 :) )
+
+        do the same. The example also demonstrates that both the <statements>
+        and the <expr> part in this form are optional.
+
+
+
+NOTES
+        The macro __LPC_INLINE_CLOSURES__ is defined when the
+        inline closures as described here are available. If not
+        defined, the driver implements a more restricted version
+        ('(: :)' syntax only, no context variables) for backwards
+        compatibility.
+
+        Inline closures are not to be confused with inline functions
+        known from other languages.
+
+HISTORY
+        LDMud 3.2.7 implemented the older, restricted form of inline
+        closures.
+        LDMud 3.3.271 implemented the full form of inline closures.
+        LDMud 3.3.275 re-allowed statements in the (: :) form.
+
+SEE ALSO
+        closures-abstract(LPC), closures-example(LPC), closure_guide(LPC)
+        closures(LPC)
diff --git a/doc/LPC/integers b/doc/LPC/integers
new file mode 100644
index 0000000..edbd848
--- /dev/null
+++ b/doc/LPC/integers
@@ -0,0 +1,40 @@
+NAME
+        integers
+
+DESCRIPTION
+        int is the fastest data type in LPC.
+
+        An integer contains a whole number between
+        -2147483648 and 2147483647.
+
+        Operations with integers:
+          +  plus               &   AND    << SHIFT right
+          -  minus              |   OR     >> SHIFT left
+          *  multiply           ^   XOR
+          /  divide (whole)     ~   NOT    =  Assign value
+          %  divide (modulo)    +=  add    -= subtract        
+
+        The result of a boolean expression is also an integer:
+          0 (false)
+          1 (true)
+
+        Boolean operators are:
+          !  (not)
+          == (equal)
+          || (or)
+          && (and)
+
+FUNCTIONS
+        int to_int(string)
+        int to_int(float)
+        int sscanf(string str, string fmt, mixed var1, mixed var2, ...)
+        void printf(string format, ...)
+
+REMARKS
+        Single characters in strings are also integers.
+
+SEE ALSO
+        arrays, mappings, operators, strings, types
+
+LAST CHANGED
+        Wednesday, 7th May, 2003, by Amaryllis
diff --git a/doc/LPC/lfuns b/doc/LPC/lfuns
new file mode 100644
index 0000000..a9c559d
--- /dev/null
+++ b/doc/LPC/lfuns
@@ -0,0 +1,31 @@
+CONCEPT
+        lfuns
+
+DESCRIPTION
+        A lfun is a LPC function within an object which is public and can
+        be called by other objects. In OO terms, lfuns are "methods"
+        which you can send "messages" to.
+
+        Calling lfuns is done by using the efun call_other(), which
+        takes as arguments the object in which the lfun is to be called,
+        the name of the lfun to be called in the object, and additional
+        and optional arguments.
+
+        An example looks like this:
+
+        call_other(drink, "QueryShort");
+
+        This call may also be written as
+
+        drink->QueryShort();
+
+        This means call_other(object, "function", args...) can also be
+        written as object->function(args...). The second form is
+        preferred as it is easier to read.
+
+        Some lfuns have a special meaning for the LPC driver, because
+        they are applied by the interpreter instead from an LPC object.
+        To distinguish those, they are called ``applied lfuns''.
+
+SEE ALSO
+        efuns(LPC), efun(E), applied(A), master(M), call_other(E)
diff --git a/doc/LPC/lpc b/doc/LPC/lpc
new file mode 100644
index 0000000..c03df0f
--- /dev/null
+++ b/doc/LPC/lpc
@@ -0,0 +1,9 @@
+NAME
+        lpc
+
+DESCRIPTION
+        This directory contains man pages about various aspects of the
+        LPC language as it is provided by Amylaars parser/driver.
+
+SEE ALSO
+        concepts(C), driver(D), efun(E), applied(A), master(M)
diff --git a/doc/LPC/mappings b/doc/LPC/mappings
new file mode 100644
index 0000000..381e3c8
--- /dev/null
+++ b/doc/LPC/mappings
@@ -0,0 +1,474 @@
+CONCEPT
+        mappings
+
+LAST UPDATE
+        Mon, 15 Mar 1999
+
+DESCRIPTION
+
+  A step-by-step introduction to mappings:
+  ----------------------------------------
+
+  1. What is a mapping?
+
+    A mapping is a datatype which allows to store data associated to a key.
+  In other languages  they are also  known  as 'dictionaries' or  'alists'.
+  There are also alists in LPC but they are not a separate datatype but are
+  implemented on  top of arrays.  Alists are  the predecessors of mappings.
+  The keys and the values  can be of  any type.  But most common  datatypes
+  for keys are strings, integers and objects.  Others like arrays, mappings
+  or closures aren't a good choice because comparision between i.e.  arrays
+  often returns false  even if they equal  in content.  This is because the
+  driver compares i.e. two arrays by their internal pointers and not by
+  their content. The reason for this is simple: speed.
+
+    Mappings  are allways  treated  as references    when passing  them  to
+  functions. This means when you pass a mapping  to another object and this
+  object modifies the mapping the modification will  take place in a global
+  scope - visible to all objects holding this mapping in a variable.
+
+
+  2. What are mappings good for?
+
+    The term 'dictionary'  probably describes the  use  of a mapping  best.
+  Opposed  to arrays mappings don't have  a specific  order. They provide a
+  mechanism to   create  a set  of associations  between  values.  Such  an
+  association consists of a unique  key and data  that is identified by the
+  key. Think of  a dictionary  where you have  a  word and a definition  of
+  it. You use the word to lookup its definition.
+
+    Mappings can be used i.e.  to hold  aliases for commands. The key would
+  then be the  name of  the alias and  the  data the command(s) behind   an
+  alias.  Or they can be used for  the exits of a  room.  The keys would be
+  the directions where one can go  to and the associated  data would be the
+  file names of the  rooms.  But mappings can  also be used  as a kind of a
+  sparse array.   A  sparse array is  an  array where most of  the elements
+  aren't used  (occupied by 0).  I.e.  if  you want to  store values at the
+  position 0, 13  and 37642 of an  array you would  have to create an array
+  with a size of at least 37643.  This  costs a lot  of memory so a mapping
+  would be  more useful because you would  then use the  numbers 0,  13 and
+  37642 as a key and not as an index to a position  (actually the keys of a
+  mapping are sometimes  called indices  but this  is just  because the way
+  data is accessed in a mapping is similar to  arrays: by the [] operator).
+  This also allows to  query all occupied   positions of a sparse array  by
+  querying for all  the keys of  the mapping opposed  to an array where you
+  have to iterate over all elements.
+
+
+  3. How do I create a mapping?
+
+    There are several ways to do so. The most convenient is the following:
+
+      mapping map;
+      map = ([ key0: value00; ...; value0n,
+               ... : ...    ; ...; ...    ,
+               keyn: valuen0; ...; valuenn ]);
+
+    As you can see, a key may  have more than  one value assigned.  But the
+  amount of values per key must always be equal.  It  is  even  possible to
+  have mappings without any values!
+    Another  method is  to use the   efun mkmapping().  This  efun gets two
+  arguments with the first beeing an array of keys and the following beeing
+  arrays of values:
+
+      mapping map;
+      map = mkmapping (({ key0   , ..., keyn    }),
+                       ({ value00, ..., value0n }),
+                       ({ ...    , ..., ...     }),
+                       ({ valuen0, ..., valuenn }));
+
+    If the efun only gets one argument, then this argument will be taken as
+  an array of keys and a mapping  without values will  be returned.
+
+    An empty mapping can be created by using the above described methods by
+  simply ommitting the keys and values:
+
+      mapping map;
+      map = ([]);
+  or:
+      map = mkmapping(({}), ({}));
+
+    Or  by  using the efun   m_allocate().  This efun gets  as  first
+  argument the  amount  of keys which will  be  added soon and  an optional
+  second argument specifying the width of the mapping:
+
+      map = m_allocate(n, width);
+
+    The value <n>  may be a bit  confusing  since mappings shrink and  grow
+  dynamically. This value just tells the driver how 'long' this  mapping is
+  going to be  so proper memory  allocations  will be  performed to  reduce
+  the overhead of memory reallocation.  I.e.  if you want to read in a file
+  and store the  read data in  a mapping  you probably  know  the amount of
+  keys.  So you allocate  a mapping with this  efun and tell the driver how
+  much memory should  be allocated  by specifing  a proper <n>  value.
+  Thus causing  a    speedup when adding  the  read   data to the   mapping
+  afterwards.    The <width> just specifies how   many  values per key this
+  mapping is   going to have. If  no  width is given, 1  will  be  taken as
+  default.
+
+  An empty mapping created with '([])' will always have a width of 1. To
+  create empty mappings with other widths, write it as
+
+      map = ([:width ]);
+
+  <width> can be any expression returning an integer value (including
+  function calls), and in fact this notation is just a fancy way of
+  writing
+
+      map = m_allocate(0, width);
+
+
+
+  4. How can I modify the data of a mapping?
+
+    Adding a  new key is similiar to   modifying the associated  data of an
+  existing key:
+
+      map += ([ key: value0; ...; valuen ]);
+
+    Or in case only a single value should be modified:
+
+      map[key, n] = valuen;
+
+    If  <n> is out of  range or if <key> doesn't  exists and <n> is greater
+  than 0 an "Illegal index" error will be reported. If <n> is equal to 0 or
+  the mapping only has a single value per key one can abbreviate it with:
+
+      map[key] = value;
+
+    If there is no <key> (and <n> is equal to 0 or  not specified at all) a
+  new one will be added automatically.
+
+    Deletion   of a key    is  done with    the  -=  operator or  the  efun
+  m_delete(). A mapping can only be substracted by one without any values:
+
+      map -= ([ key ]);
+  or:
+      map -= ([ key0, ..., keyn ]);
+
+  The efun takes a mapping as first and a key as second argument:
+
+      m_delete(map, key);
+
+    The  efun   m_delete() returns  the mapping   but because  mappings are
+  handled as references there is no need of an assignment like:
+
+      map = m_delete(map, key);
+
+
+  5. How can I access the data stored in a mapping?
+
+    This can be done by:
+
+      valuen = map[key, n];
+
+    Or in case of a mapping with just one value per key:
+
+      value0 = map[key];
+
+    If there is no  <key> in the mapping  and <n> is  0 or not specified at
+  all (which is the same) a 0 will be returned or if <n>  is greater than 0
+  an "Illegal index" error will be reported.
+
+
+  6. How can I test for the existance of a key?
+
+    A  return value of 0 is  sufficient for most applications but sometimes
+  the ambiguity  between an existing value of  0 and  a nonexisting key can
+  lead   to  a  problem.  Therefore   one  can use   the  efun member()  or
+  mapping_contains() to check if there actually is a key in the mapping:
+
+      if (member(map, key)) {
+        ...
+      }
+  or:
+      if (mapping_contains(&value0, ..., &valuen, map, key)) {
+        ...
+      }
+
+    This also shows how  one can retrieve all values   associated to a  key
+  from a mapping in a single step. The '&' is  the reference operator which
+  is neccesary to let the efun store the values in the variables.
+
+    In case   of  mappings   with   no  values,   the  efun   member()  and
+  mapping_contains() are equal in their behaviour  and their way of calling
+  because mapping_contains() won't get any reference variables to store the
+  values in (obviously, because there aren't any).
+
+     Also normally member() is known to return the postion of an element in
+  a list (i.e.  a  character in a  string or data   in an array) and if  an
+  element couldn't be  found -1 is returned.   But in the case  of mappings
+  there are no such things as order and postion. So member() only returns 0
+  or 1.
+
+
+  7. How can I copy a mapping?
+
+    A  mapping can  be  copied   with  the  +  operator   or by the    efun
+  copy_mapping():
+
+      newmap = ([]) + map;
+  or:
+      newmap = copy_mapping(map);
+
+    A mapping should only be copied when it is neccesary to get an own copy
+  of it that  must not be  shared by other objects.
+
+
+  8. How can I get all keys of a mapping?
+
+    The  efun m_indices() gets a mapping  as argument  and returns an array
+  holding all keys defined in this mapping:
+
+      keys = m_indices(map);
+
+
+  9. How can I get all the values of a mapping?
+
+    The efun m_values() gets  a mapping as  argument  and returns  an array
+  holding all the first (second, ...) values of it.
+
+      values0 = m_values(map);     returns the first values
+      values0 = m_values(map, 0);  dito
+      values1 = m_values(map, 1);  returns the second values
+        etc
+
+
+  10. How can I determine the size of a mapping?
+
+    Because a mapping is a kind of rectangle it has two sizes: a length and
+  a width.  There are three different efuns  to query these values. The first
+  two are the  efuns sizeof(), which returns the  amount of key-value
+  associations (the length of  a mapping), and widthof(), which returns the
+  number of values per key (the width). The third is the efun get_type_info().
+  get_type_info() is meant  to be a function  to identify a datatype.   Its
+  return value is an  array of two  numerical values.  The first  specifies
+  the datatype   of the argument and   the second is a   datatype dependend
+  value. In the case of a mapping the first value  is T_MAPPING (which is a
+  value defined in  <lpctypes.h>) and the  second the amount of values  per
+  key (a.k.a.  columns or the width  of the mapping  - actually it would be
+  correct to say that the width of a mapping is the  amount of columns plus
+  one for the keys but this is uncommon).
+
+
+  11. What is the best method to iterate over a mapping?
+
+    First of all the main purpose of a mapping is not meant to  be a set of
+  data to iterate over. Afterall the keys in a mapping have no specific but
+  a random order (at least on the LPC side).  But  still it is possible and
+  sometimes even neccesary to do so.
+
+    If all key-value associations  should be processed  then one should use
+  walk_mapping().  If all keys of a mapping should be processed to create a
+  new mapping being a subset of the given one, then filter_mapping() should
+  be  used.  If all  keys  are going to  be  processed and  to create a new
+  mapping with the  same set of keys as  the given mapping, then one  would
+  use map_mapping().  But in the case of an  iteration that should/can stop
+  even if not all data is processed it is probably wise to iterate over the
+  mapping by first querying for the keys and then to iterate over them with
+  a for() or a while() loop and querying the values by 'hand'.
+
+    The efun walk_mapping() gets  a mapping as  first argument and the name
+  of a function  as second one. All the  following arguments are treated as
+  extras which  will  be  passed to the   function specified  with the  2nd
+  argument. Instead of a string for the name of a function a closure can be
+  used, too. Nothing will be returned:
+
+      ...
+      walk_mapping(map, "func", xarg0, ..., xargn);
+      ...
+
+      void func(mixed key, mixed value0, ..., mixed valuen,
+                mixed xarg0, ..., mixed xargn) {
+        ...
+      }
+
+    func() will be called for all key-value associations  and gets as first
+  argument the key.  The next arguments are the  values behind the key  and
+  are passed as references.  The  rest  of the  passed arguments are  those
+  specified as extras. Because the values are passed as references (opposed
+  to  copies) it is possible  to modify them  from  inside func() by simply
+  assigning new value to the variables <value0>, ..., <valuen>.
+
+    The efun filter_mapping() calls  a function for  each key in  a mapping
+  and creates a new mapping  which only contains key-value associations for
+  which the called function returned true (not  equal 0 that is). The first
+  argument is the mapping to iterate over and the second is a function name
+  given as a string or a closure:
+
+      ...
+      submap = filter_mapping(map, "func", xarg0, ..., xargn);
+      ...
+
+      int func(mixed key, mixed xarg0, ..., mixed xargn) {
+        ...
+      }
+
+    func() gets  as first argument the key  and the others are those passed
+  as extras to filter_mapping().
+
+    The efun map_mapping() gets a mapping as first argument and a string as
+  a function name (or again a closure) as  second argument.  Any additional
+  arguments are again used  as extras that will  be passed to the iteration
+  function. This efun returns a new mapping with the same keys as the given
+  one.  The values  returned by the function  that is invoked  for each key
+  will be used as the associated data behind each key of the new mapping:
+
+      ...
+      newmap = map_mapping(map, "func", xarg0, ..., xargn);
+      ...
+
+      mixed func(mixed key, mixed xarg0, ..., mixed xargn) {
+        ...
+      }
+
+    func() gets  as first argument the key  and the others are those passed
+  as extras to map_mapping().
+
+    Because a function can only return  a single value  (even when it is an
+  array) it restricts the use  of map_mapping() to  only allow creation  of
+  mappings with a single value per key.
+
+
+  12. Is it possible to join/intersect/cut mappings with another?
+
+    Joining mappings is only possible, if  they have the same width (amount
+  of values per key). One can use the + and += operator:
+
+      map = map1 + map2 + ... + mapn;
+      map += map1 + map2 + ... + mapn;
+
+    Intersection     of   two   mappings is    only      possible by  using
+  filter_mapping(). There is  no efun or operator  which features this. The
+  'easiest' way may be the following function:
+
+      mapping intersect_mapping(mapping map1, mapping map2) {
+        closure cl;
+
+        cl = lambda(({ 'key }), ({ #'member, map2, 'key }));
+        return filter_mapping(map1, cl, map2);
+      }
+
+    This function returns a  new mapping which   consists of all  key-value
+  associations   of  <map1>  for which  an  equal  key  could   be found in
+  <map2>. This function uses  a closure which returns 0  or 1  depending on
+  wether a key from <map1> is contained in <map2> or not.
+
+    Cutting out  all key-value associations of a   mapping for which  a key
+  could be  found in another mapping  can  be done  by using  the  - and -=
+  operator:
+
+      mapping cut_mapping(mapping map1, mapping map2) {
+        return map1 - mkmapping(m_indices(map2));
+      }
+
+    Because a maping can  only be substracted by one  without any values we
+  first have to create such by using m_indices() and mkmapping().
+
+
+  13. What are those mappings without any values (besides keys) good for?
+
+    Because the way how the driver  searches for a  key in a mapping is
+  rather fast, those mappings can be used as a  set of elements with a fast
+  method for testing if an element is  contained in the set. This technique
+  is called hashing (further  explanation   would lead  too far)  which  is
+  faster  than searching for  values  in array  (which  is done in a linear
+  fashion).
+
+    Another (maybe  more pratical) use  of these  mappings  are to create a
+  array of unique values out of an array with several equal values:
+
+      uniques = m_indices(mkmapping(array));
+
+    mkmapping() uses  <array> to  create  a mapping without any  values but
+  just keys. And  because a mapping can only  have unique keys all multiple
+  values in <array> are taken as one.  The call of m_indices() then returns
+  an  array  of  these  unique keys.  Actually we  only  make  use of those
+  mappings temporarily.
+
+
+  14. How can I convert an alist into a  mapping and vice versa?
+
+    There are no special efuns which handle such conversions. But it can be
+  done by the following functions:
+
+      mapping alist_to_mapping(mixed *alist) {
+        return apply(#'mkmapping, alist);
+      }
+
+    The efun apply() takes a closure and an array of values and passes each
+  element of the  array as an  argument  to the  closure. Because  an alist
+  consists of an array of arrays with the first beeing the list of keys and
+  the others the values associated to each key passing them as arguments to
+  the efun closure #'mkmapping via apply() causes the creation of a mapping
+  out of an alist.
+
+      mixed *mapping_to_alist(mapping map) {
+        mixed *alist;
+        symbol *vars;
+        string var;
+        closure cl;
+        int width;
+
+        width = get_type_info(map)[1];
+        alist = allocate(width + 1);
+        vars  = allocate(width + 2);
+        for (var = "a"; width; var[0]++, width--) {
+          alist[width] = ({});
+          vars[width]  = quote(var);
+        }
+        alist[0] = ({});
+        vars[0]  = 'key;
+        vars[<1] = 'alist;
+        cl = lambda(vars, ({ #'=, 'alist, ({ #'insert_alist }) + vars }));
+        walk_mapping(map, cl, &alist);
+        return alist;
+      }
+
+    This function is  a bit more  complicated  than the other  and detailed
+  description would lead   too far of   the topic.  This  function has  one
+  restriction:  it can only turn a  mappings with up to  26  values per key
+  into an  alist.    But  this  should  be   sufficient for probably    all
+  applications which use mappings.
+
+  And Hyps further comment on this:
+        The function mapping_to_alist() is also not that
+        clever because insert_alist() allways creates a new
+        alist.  A second (optional) argument to m_values() to
+        specify the value column would be better. Besides
+        this, the conversion of a mapping into an alist could
+        be done by to_array().
+
+  15. Dirty Mappings
+
+  'Dirty mappings' are nothing the LPC programmer directly is involved
+  with, however, as it relates to the way mappings are implemented
+  internally by the gamedriver. However, as this term shows up in
+  various driver statistics, it is explained here.
+
+  There are two fundamental approaches to implement mappings:
+
+    1. Store all data entries in an array-like structure, in sorted order. 
+    2. Store all data in a hashtable, each entry allocaed separately.
+
+  Method 1 is very space efficient, as it doesn't need much overhead
+  per entry; however, insertions and deletions of entries are
+  relatively slow as all other entries need to be moved.     
+  Method 2 is very fast as nothing needs to be moved in memory,
+  however it has a large overhead.
+
+  The gamedriver uses a hybrid method: at the basis is a mapping
+  implementation based on arrays. However the driver uses a hash table
+  in addition to handle all the ongoing insertions and deletions.
+  Every once in a while, the contents of the hash table are sorted
+  into the base array, reasoning that any entry surviving for longer
+  time in the hash table is worth keeping in a more space-efficient
+  manner. 'Dirty' mappings are such mappings with both an array and a
+  hash part, 'clean' mappings are those with just an array part.
+
+HISTORY
+        The ([:width ]) notation was added in LDMud 3.2.9/3.3.208 .
+
+SEE ALSO
+        alists(LPC), closures(LPC), structs(LPC), mkmapping(E),
+        walk_mapping(E)
diff --git a/doc/LPC/modifiers b/doc/LPC/modifiers
new file mode 100644
index 0000000..a30904e
--- /dev/null
+++ b/doc/LPC/modifiers
@@ -0,0 +1,182 @@
+CONCEPT
+        modifiers
+
+DESCRIPTION
+        A modifier changes the syntactic and/or semantic behaviour of
+        an object-global variable or a function in an object.
+        The existing modifiers are described below.
+        To use a modifier just prepend it to the declaration. If several
+        modifiers are to be used their order does not matter:
+
+        private int bar;                         // example for a variable
+        protected nomask int foo() { return 3; } // example for a function
+
+        For functions:
+        ~~~~~~~~~~~~~~
+        private   -- such functions can only be called with an internal
+                     call from within this file. Not even inheriting
+                     objects can call these functions. You can nevertheless
+                     build an lfun-closure with #' out of a private function
+                     (but you cannot save and restore it).
+        protected -- such functions can be called from within the object,
+                     or from inheriting objects; but in neither case
+                     with call_other(). It is possible to create #' closures
+                     or use symbol_function() from within the object.
+                     Its use is preferred over the older "static".
+        static    -- such functions can be called from within the object
+                     in either way (internal call or with call_other()).
+                     Inheriting objects can call such functions.
+                     But it is not possible to call static functions from
+                     other objects via call_other().
+                     The use of 'static' in new code is not recommended.
+                     Note that an add_action() is treated like a call
+                     from within the object except the player who got the
+                     add_action() was forced (thus it is a simple way to
+                     secure an add_action() against forces, although this
+                     method has the severe disadvantages of raising an error
+                     at the force so better use the security system).
+                     Also efuns like call_out() or input_to() can call
+                     these functions if given as a string.
+        public    -- this is the default type. Such functions can be called
+                     from within the file as well as from inheriting objects
+                     and other objects via call_other().
+                     To declare a function public only results in the
+                     impossibility to change the accessibility at the
+                     inherit statement (see below). No error will occur,
+                     only the type will not be modified by the inherit
+                     statement.
+        nomask    -- such functions cannot be overridden by inheriting
+                     objects. If you have the fun foo() defined in your
+                     object and inherit an object which also has declared
+                     a function foo() and this nomask, you will get an
+                     compile error if you try to load your object.
+                     Furthermore a shadow will fail if it tries to shadow
+                     a nomask declared function.
+        varargs   -- this changes the syntax of the function in a way that
+                     not all of the arguments in the declaration must be
+                     given at the call. This is often very usefull if some
+                     of the arguments shall be omitable (the omitted
+                     arguments are set to 0 if the function is called with
+                     fewer arguments than specified).
+                     This is mainly within the object really necessary;
+                     call_other()s usually (that is if they do not have a
+                     certain pragma ('man pragma')) do not need the called
+                     function to be declared varargs to omit any arguments,
+                     but it is good style to use this modifier to document
+                     the code by this.
+        deprecated - Whenever this function is called, a warning is issued.
+                     Usually this is done at compile-time. Exceptions are
+                     call_others and symbol_function() which warn at run-time.
+
+        For object-global variables:
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        private   -- such variables can only be accessed from within the
+                     same object. Not even inheriting objects can access
+                     private variables.
+                     It is a good style to declare all internal variables
+                     private to prevent inheriting objects from accessing
+                     the variables directly without using functions.
+        nosave    -- such variables are neither stored with save_object()
+                     nor restored with restore_object(). This can be very
+                     useful if you want a room to use save_object() and
+                     restore_object() to save your own defined variables
+                     but not the hundreds of variables inherited from a
+                     room-class (e.g. /complex/room). You then use the modifier
+                     at the inherit statement (see below).
+                     Note that nosave and private do not overlap in any
+                     way. They are absolutely independant.
+        static    -- the old name for 'nosave'. Its use is deprecated.
+        public    -- declares the variable public. It cannot be declared
+                     private or static by inheriting. No error will occur,
+                     only the type will not be modified by the inherit
+                     statement.
+        deprecated - Whenever this variable is used, a warning is issue.
+                     Usually this is done at compile-time, but
+                     symbol_variable() warns at run-time.
+
+        It is no good style to let inheriting objects have access to
+        internal variables so declare them as private and offer functions
+        to query and change the variables if possible.
+
+        It is also possible to redeclare all variables and/or functions
+        of an inherited object for the own object at the inheriting
+        statement:
+
+        private functions nosave variables inherit "complex/room";
+        public variables inherit "complex/room";
+        private functions inherit "complex/room";
+
+        To redeclare a function or a variable declared public in the
+        inherited object to be private or protected is not possible.
+
+        There also exists a modifier explicitly for the inherit statement:
+
+        virtual   -- inherits the given object virtually. This only makes
+                     sense in a complex inherit tree.
+                     If an object is inherited normally (not virtually)
+                     twice somewhere in the inherit tree the intern
+                     variables exist twice. If inherited virtually they
+                     exist only once.
+                     Example:
+                     A inherits B and C.
+                     B inherits D.
+                     C inherits D.
+                     If the inheritance of D is virtual in B and C
+                     D's variables exist only once in A. If A changes
+                     D's variables via functions of B this also changes
+                     the variables of D as known by C.
+
+                     virtual:               non-virtual:
+                        A                        A
+                       / \                      / \
+                      B   C                    B   C
+                       \ /                     |   |
+                        D                      D   D
+
+
+        To simplify the adoption of existing code, LPC allows to specify
+        a default visibility for functions and variables, using a syntax
+        similar to the inherit syntax:
+
+          default private;
+
+            All variables and functions are by default private.
+
+          default private variables public functions;
+
+            All variables are by default private, but functions are public.
+
+        Only the modifiers 'private', 'public' and 'protected' (and 'static'
+        for functions only) are allowed here.
+
+        The default visibility thus set affects only variables/functions with
+        no explicite visibility:
+
+          default private;
+
+          int private_var;
+          public int public_var;
+
+        The definition is valid from the point of the 'default' statement
+        until the end of the file, or until the next 'default' statement:
+
+          default private;
+
+          int private_var;
+
+          default public;
+
+          int public_var;
+
+        Note that this default visibility does not affect inherits.
+
+
+HISTORY
+        The modifier 'static' for variables was renamed to 'nosave'
+        with LDMud 3.2.8. 'static' is still recognized as an alias.
+
+        The default visibility was added in LDMud 3.2.9 as experimental
+        feature.
+
+SEE ALSO
+            closures(LPC), inheritance(LPC), functions(LPC), types(LPC)
diff --git a/doc/LPC/operators b/doc/LPC/operators
new file mode 100644
index 0000000..18406a3
--- /dev/null
+++ b/doc/LPC/operators
@@ -0,0 +1,213 @@
+NAME
+        operators
+
+DESCRIPTION
+
+        These are the operators availailable in LPC. They are listed
+        in the order of precedence (low priority first):
+
+
+        expr1 , expr2        Evaluate 'expr1' and then 'expr2'. The
+                        returned value is the result of 'expr2'. The
+                        returned value of 'expr1' is thrown away.
+
+        var = expr        Evaluate 'expr', and assign the value to
+                        'var'. The new value of 'var' is the result.
+
+        var += expr        Assign the value of 'expr' + 'var' to 'var'.
+                        This is mostly equivalent to "var = var + expr".
+
+        var -= expr        Similar to '+=' above.
+        var &= expr
+        var |= expr
+        var ^= expr
+        var <<= expr
+        var >>= expr
+        var >>>= expr
+        var *= expr
+        var %= expr
+        var /= expr
+        var &&= expr
+        var ||= expr
+
+        expr1 ? expr2 : expr3
+                        Evaluates 'expr1' and branches according to
+                        its truth value. If it is true, the 'expr2' is
+                        evaluated and returned as result, else
+                        'expr3'.
+
+        expr1 || expr2        The result is true if 'expr1' or 'expr2' is
+                        true. 'expr2' is not evaluated if 'expr1' was
+                        true.
+
+        expr1 && expr2        The result is true i 'expr1' and 'expr2' is
+                        true. 'expr2' is not evaluated if 'expr1' was
+                        false.
+
+        expr1 | expr2        The result is the bitwise or of 'expr1' and
+                        'expr2'.
+                        For arrays, the union set is computed: all elements
+                        from <expr1> plus all those from <expr2> which
+                        are not in <expr1>.
+
+        expr1 ^ expr2        The result is the bitwise xor of 'expr1' and
+                        'expr2'.
+                        For arrays, the symmetric difference is computed:
+                        all elements from <expr1> which are not in <expr2>,
+                        plus all those from <expr2> which are not in <expr1>.
+
+        expr1 & expr2        The result is the bitwise and of 'expr1' and
+                        'expr2'.
+
+                        For arrays and strings, the intersection set
+                        (all elements resp. characters from expr1 which
+                        which are also in the expr2) is computed.
+                        Note: "aab" & "a" -> "aa"
+                         but  ({ 'a','a','b' }) & ({ 'a' }) -> ({ 'a' })
+                        Eventually the array behaviour will be changed
+                        to match the string behaviour.
+
+                        Intersecting an array with a mapping is equivalent
+                        to intersecting the array with the indices of the
+                        mapping: array & mapping = array & m_indices(mapping)
+
+                        Mappings can be intersected with another mapping
+                        or an array. The resulting mapping holds all
+                        those entries from the first mapping, which are
+                        also mentioned in the second mapping (as index)
+                        resp. in the array.
+
+        expr1 == expr2        Compare values. Valid for strings, numbers,
+                              objects and closures.
+
+        expr1 != expr1        Compare values. Valid for strings, numbers,
+                              objects and closures.
+
+        expr1 > expr2        Valid for strings and numbers.
+
+        expr1 >= expr2        Valid for strings and numbers.
+
+        expr1 < expr2        Valid for strings and numbers.
+
+        expr1 <= expr2        Valid for strings and numbers.
+
+        expr1 << expr2        Shift 'expr1' left by 'expr2' bits; the sign
+                              bit is not preserved.
+
+        expr1 >> expr2        Shift 'expr1' right by 'expr2' bits.
+                        This shift preserves the sign of 'expr1'.
+
+        expr1 >>> expr2        Shift 'expr1' right by 'expr2' bits.
+                        This shift does not preserve the sign of 'expr1',
+                        instead it shifts in 0 bits.
+
+        expr1 + expr2        Add 'expr1' and 'expr2'. If numbers, then
+                        arithmetic addition is used. If one of the
+                        expressions are a string, then that string is
+                        concatenated with the other value.
+                        If the expressions are arrays, the result is
+                        the right array appended to the left.
+                        If the expressions are mappings of equal width,
+                        the result is merger of the two mappings. If one
+                        key exists in both mappings, the element from the
+                        right mapping appears in the result. If the two
+                        mappings are of different width, the result is
+                        <expr1> if non-empty, and <expr2> otherwise.
+
+        expr1 - expr2        Subtract 'expr2' from 'expr1'. Valid for
+                        numbers, strings, arrays, mappings.
+                        For arrays and strings, all occurences of the
+                        elements resp. characters in 'expr2' are removed
+                        from 'expr1', and the result is returned.
+                        For mapping, all occurances of elemens in 'expr1'
+                        which have a matching key in 'expr2' are removed, and
+                        the result is returned.
+
+        expr1 * expr2        Multiply 'expr1' with 'expr2'.
+                        If strings or arrays are multiplied with a number
+                        (zero or positive), the result is a repetition of the
+                        original string or array.
+
+        expr1 % expr2        The modulo operator of numeric arguments.
+
+        expr1 / expr2        Integer division.
+
+        ++ var                Increment the value of variable 'var', and
+                        return the new value.
+
+        -- var                Decrement the value of variable 'var', and
+                        return the new value.
+
+        - var                Compute the negative value of 'var'.
+
+        ! var                Compute the logical 'not' of an integer.
+
+        ~ var                The boolean 'not' of an integer.
+
+        ( type ) var    Return the value of <var> converted to <type>.
+                        <type> can be 'string', 'int', 'object', 'float'
+                        or 'int*'. <var> must be of a specific type
+                        for a conversion to take place; if <var> is 'mixed'
+                        or unknown, the cast is purely declarative.
+                        Also, if the declared type of <var> is that of <type>,
+                        the value is not changed.
+
+                        NB. The literal number 0 is of unknown type, as
+                        it doubles as 'not initialized' for strings, objects,
+                        and arrays.
+
+                        The operator acts like the efuns
+                        to_string(), to_int(), to_object(), to_float()
+                        and to_array(). It is advisable to use the
+                        efuns directly instead of the cast.
+
+        ({ type }) var  <var> is now assumed to have the type <type>.
+                        This is purely declarative, the actual value
+                        of <var> is not changed.
+
+        var ++                Increment the value of variable 'var', and
+                        return the old value.
+
+        var --                Decrement the value of variable 'var', and
+                        return the old value.
+
+        expr1[expr2]        The array or mapping given by 'expr1' is
+                        indexed by 'expr2'.
+
+        expr1[expr2..expr3] Extracts a
+                        piece from an array or string.
+                        expr2 or expr3 may be omitted, default is the begin
+                        or end of expr1.
+                        Negative numbers for expr2 or expr3
+                        mean ``count from before the beginning'', i.e.
+                        foo[-2..-1] is an empty array or string.
+                        foo[<2..<1] gives the 2nd and last element of
+                        the array resp. chars of the string.
+
+        expr1->name(...) The symbolic form of call_other(). 'expr1'
+                        gives either an object or a string which is
+                        used as the file_name of an object, and calls
+                        the function 'name' in this object.
+
+        ident::name(...)
+                        Call the inherited function 'name' with the
+                        given parameters in the parent 'ident'.
+                        'ident' may be given as string containing the
+                        full pathname, or as identifier containing the
+                        pure basename.
+                        If 'ident' is omitted, the last inherited
+                        function of this 'name' is called.
+
+        ({ })                Array constructor.
+        ([ ])                Mapping constructor.
+
+NOTE
+        The closure operators are not described here.
+
+HISTORY
+        LDMud 3.2.9 added '>>>', '>>>=', '&&=' and '||='.
+        LDMud 3.2.10 extended '&' to mappings.
+        LDMud 3.3 extended '|' and '^' to arrays.
+
+SEE ALSO
+        arrays(LPC), alists(LPC), mappings(LPC), closures(LPC)
diff --git a/doc/LPC/pragma b/doc/LPC/pragma
new file mode 100644
index 0000000..d26f326
--- /dev/null
+++ b/doc/LPC/pragma
@@ -0,0 +1,116 @@
+NAME
+        pragma
+
+DESCRIPTION
+        The preprocessor directive #pragma can be used to select
+        several compilation options. Multiple options can be selected
+        in one #pragma directive by separating them with commas.
+
+        no_clone: The blueprint object can't be cloned.
+        no_inherit: The program can't be inherited.
+        no_shadow: The program can't be shadowed (similar to declaring
+                all functions as 'nomask').
+
+        init_variables: Clone variables are initialized by __INIT().
+        share_variables: Clone variables are initialized from the
+                blueprint.
+
+        weak_types: no type checking (this is the default).
+        strict_types: all functions must be declared with argument
+                prototypes, and the return values of call_other() must
+                be casted.
+        strong_types: all functions must be declared with complete
+                types of return value and parameters.
+        save_types: the declaration data is kept after compilation and
+                checked at runtime. This is important for type-safe
+                inheritance.
+
+        rtt_checks: runtime checks during execution of this program will be
+                enabled. The interpreter will check for correct datatypes of
+                arguments on function calls. (Later it will include checks
+                upon assignments.)
+                Don't confuse this with strong/strict_types, they only
+                check at compile time.
+                strong_types/strict_types is seriously recommended.
+                This pragma implicitly enables save_types as well.
+        no_rtt_checks: disable runtime type checks for this program (default).
+
+        pedantic: Certain warnings are treated as errors:
+                - failure to pass enough arguments to simul efuns
+        sloppy: Turns off pedantic (the default).
+
+        range_check: Use of questionable ranges (ranges of negative sizes,
+                or with bounds beyond the array's size) cause a runtime
+                warning.
+        no_range_check: Turns off range_check (the default).
+
+        warn_deprecated: Use of deprecated efuns or indexing operations
+                causes the compiler to issue a warning (the default).
+        no_warn_deprecated: Turns off warn_deprecated.
+
+        warn_empty_casts: A cast of a value to its own type generates
+                a warning (the default). Exception are casts to type
+                'mixed'.
+        no_warn_empty_casts: Turns off warn_empty_casts.
+
+        warn_missing_return: Warn if a value-returning function is missing
+                a return statement (the default). If possible, the driver
+                will try to detect this at compile time; otherwise a runtime
+                warning will be generated when the function is executed.
+                The check applies only to functions with a declared return
+                type other than 'void'.
+        no_warn_missing_return: Turn off warn_missing_return.
+
+        warn_function_inconsistent: If an inherited function is
+                overloaded with inconsistent return types or arguments,
+                a warning is generated; or if pragma_pedantic is in effect,
+                an error. By default this is active.
+        no_warn_function_inconsistent: An inherited function can
+                be overloaded with inconsistent return types or arguments,
+                as long as pragma_pedantic is not in effect.
+
+                This pragma is meant to easen the adaption of legacy
+                mudlib code - in general one should fix the warnings,
+                not turn them off.
+
+        When an object is compiled with type testing (#pragma
+        strict_types), all types are saved of the arguments for that
+        function during compilation.  If the #pragma save_types is
+        specified, then the types are saved even after compilation, to
+        be used when the object is inherited.
+
+        The following two pragmas are available if the driver was
+        compiled with DEBUG and TRACE_CODE options:
+
+        set_code_window: Sets an offset to the current program writing
+                position. Use this BEFORE a piece of code where you
+                want to check to what bytecodes it is compiled.
+        show_code_window: shows some bytes starting at or near the
+                last point set_code_window was called.
+
+EXAMPLES
+        #pragma strict_types
+        #pragma no_clone, no_inherit
+
+HISTORY
+        LDMud 3.2.7 added local_scopes, no_local_scopes, no_clone
+        and no_inherit.
+        LDMud 3.2.8 added weak_types, pedantic and sloppy.
+        LDMud 3.2.9 allowed to specify multiple pragmas in one directive.
+        LDMud 3.2.9 added (no_)warn_deprecated.
+        LDMud 3.2.10 added (no_)warn_empty_casts.
+        Starting with LDMud 3.2.10, #pragma xxx_types in an included file are
+          no longer valid only until the end of the file, but remain active
+          when processing returns to the including file.
+        LDMud 3.2.11 added (no_)warn_function_inconsistent.
+        LDMud 3.3.378 added init_variables, share_variables.
+        LDMud 3.3.357 added (no_)warn_missing_return.
+        LDMud 3.3.646 added (no_)range_check.
+        LDMud 3.5.0 removed combine_strings and no_combine_strings.
+        LDMud 3.5.0 removed local_scopes and no_local_scopes.
+        LDMud 3.5.0 removed verbose_errors (making its behaviour mandatory).
+        LDMud 3.5.0 enabled warn_deprecated by default.
+
+SEE ALSO
+        inheritance(LPC), initialisation(LPC), objects(C),
+        operators(LPC)
diff --git a/doc/LPC/preprocessor b/doc/LPC/preprocessor
new file mode 100644
index 0000000..61593f0
--- /dev/null
+++ b/doc/LPC/preprocessor
@@ -0,0 +1,23 @@
+NAME
+        preprocessor
+
+DESCRIPTION
+        The LPC driver understands the following preprocessor
+        directives:
+
+        #include, #define, #if, #ifdef, #ifndef, #else, #elif,
+        #endif, #undef
+                same as in ANSI C
+
+        #line <num>
+                line numbers start at <num> with the next line
+
+        #echo
+                the rest of the line is printed to the error output
+                (stderr), thus can be captured into a log file
+
+        #pragma
+                see the separate man page
+
+SEE ALSO
+        pragma(LPC), predefined(D)
diff --git a/doc/LPC/references b/doc/LPC/references
new file mode 100644
index 0000000..c1d60f0
--- /dev/null
+++ b/doc/LPC/references
@@ -0,0 +1,49 @@
+CONCEPT
+        references
+
+DESCRIPTION
+        Call by reference can be used to have a function that passes
+        more than one value to the caller, without using arrays that
+        have to be unpacked thereinafter.
+        There is nothing special to declare in the calling function,
+        you simply do an assignment to a parameter of the function.
+        The caller has to pass references explicitely; this is done by
+        prefixing an lvalue with '&' .
+        To pass a reference to an element of an array, you have to
+        enclose the indexed lvalue in round brackets.
+
+EXAMPLE
+
+        void assign(mixed destination, mixed source) {
+            destination = source;
+        }
+
+        void extract_number(int destination, string source) {
+            sscanf(source, "%d", destination);
+        }
+
+        void test() {
+            int i;
+            float f;
+            mixed *a;
+
+            extract_number(&i, "42 palantirs");
+            assign(&f, 3.141592653589793);
+            assign(&a, ({ i, f }));
+            assign(&(a[<0..<1]), ({1,2,3,"sink","x","y","x"}));
+            assign(&(a[5][0]), 'w');
+            assign(&(a[5][<1]), 'g');
+            printf("%O", a));
+        }
+
+        ({ /* sizeof() == 9 */
+          42,
+          3.14159,
+          1,
+          2,
+          3,
+          "wing",
+          "x",
+          "y",
+          "x"
+        })
diff --git a/doc/LPC/strings b/doc/LPC/strings
new file mode 100644
index 0000000..15820ef
--- /dev/null
+++ b/doc/LPC/strings
@@ -0,0 +1,65 @@
+NAME
+        strings
+
+DESCRIPTION
+        A string is a data type in LPC.
+
+        It consists of several ASCII characters (letters), numbers
+        and special characters. ASCII characters may contain of
+        values between -128 and 127 (e.g. 'A' (65) or '5' (53)), but
+        0 is not allowed (because strings are 'null terminated',
+        which means that marks the end of a string).
+
+        Notations for strings:
+        "abc" + "def" "ghi" + 5 + "jkl" + to_string( ({'m','n'}) )
+        == "abcdefghi5jklmn"
+
+        Special characters must be escaped with a leading '\':
+          \n   (new line)
+          \t   (tabulator)
+          \b   (backspace)
+          ...
+
+        The single characters can be accessed like an array:
+          string s;
+          int i,j;
+
+          s="test";
+
+          s[0]       (ASCII Code (value) of the first character)     ='t'
+          s[<1]      (last character)                                ='t'
+          s[0..0]    (string composed of the first char)             ="t"
+          s[1..<2]   (string from second to character before last)   ="es"
+          s[2..]     (string from third to last character)           ="st"
+          ...
+
+        String manipulations:
+          s[0]=s[0]-32    ("test" -> "Test")
+          s[1..<2]="o"    ("test" -> "tot")
+          ...
+
+FUNCTIONS
+        int strlen(string str)
+        int member(string s, int elem)
+        int strstr (string str, string str2, int pos)
+        int to_int(string)
+        mixed *to_array(string)
+        string to_string(mixed)
+        string upperstring(string str)
+        string lowerstring(string str)
+        string lower_case(string str)
+        string capitalize(string str)
+        string break_string(string str, int width, int space, int leave_lfs)
+        string sprintf(string fmt, ...)
+        int sscanf(string str, string fmt, mixed var1, mixed var2, ...)
+        string *new_explode(string str, string del)
+        string *explode(string str, string del);
+        string implode(mixed *arr, string del)
+        string *regexplode (string text, string pattern)
+        string *regexp(string *list, string pattern)
+
+SEE ALSO
+        arrays, integers, mappings, operators, types
+
+LAST CHANGED
+        12. Mar 2004 Gloinson
diff --git a/doc/LPC/structs b/doc/LPC/structs
new file mode 100644
index 0000000..217b0a6
--- /dev/null
+++ b/doc/LPC/structs
@@ -0,0 +1,227 @@
+CONCEPT
+        structs
+
+INTRODUCTION
+        structs are, next to arrays and mappings, a way to group a
+        collection of value together.
+
+        A struct holds a fixed number of values, called 'members', and
+        allows to access them by their given name. The name is resolved
+        when the LPC code is compiled, making struct member access as fast
+        as array member access.
+
+        structs are passed by reference.
+
+
+DEFINITION
+        A new struct type has to be defined at the top level of an
+        object. For example
+
+            struct Foo {
+              int        one, *two;
+              struct Bar three;
+            };
+
+        defines the new struct 'Foo' with three members: integer 'one',
+        integer array 'two', and struct Bar 'three'
+
+        It is possible to 'inherit' structs from each other. Given above
+        definition of struct Foo, the following definition
+
+            struct Quux (Foo) {
+              int four;
+            };
+
+        is equivalent to the definition
+
+            struct Quux {
+              int        one, *two;
+              struct Bar three;
+              int four;
+            };
+
+
+        The usual visibility modifiers apply, e.g.
+
+            protected struct Bang {...};
+
+
+        struct definitions are promoted through inheritance like functions,
+        with the difference that all structs live in the same flat namespace.
+        This means: a struct defined in a program is visible in _all_
+        inherited programs, regardless of how deep the inheritance is
+        nested.  This also means that in one program there must not be
+        two structs, inherited or not, with the same name.
+
+
+        To declare a struct without defining it, write:
+
+            struct Quux;
+
+        This notation is useful if you have two structs referencing
+        each other:
+
+            struct Quux;
+
+            struct Bar {
+              struct Quux quux;
+            };
+            struct Quux {
+              struct Bar bar;
+            };
+
+
+USAGE
+        To use a struct, its definition must be visible - either because it
+        is defined in the object compiled, or it has been inherited.
+        (Note: #include'ing structs does not what you think it does: in
+        LPC it constructs a new struct type whereever it is included).
+
+
+        A variable to hold a struct is defined like this:
+
+            struct Foo var;
+
+        and similar for function arguments:
+
+            void fun (struct Foo arg)
+
+
+        Just writing 'struct Foo var' however does not _create_ a struct,
+        it just creates a variable capable of holding one. To assign a value
+        to the variable upon creation, assign it with a struct value, either
+        from another variable or from a literal struct:
+
+            struct Foo var = (<Foo>);
+
+
+        Literal structs are written using (<>) as delimiters:
+
+            (<Foo>)
+                creates an empty instance of struct Foo
+
+            (<Foo> 1, ({ 2 }), bar)
+                creates an instance of struct Foo, and assigns 1 to member
+                'one', ({ 2 }) to member 'two', and the content of variable
+                bar to member 'three'.
+
+            (<Foo> two: ({ 2 }) )
+               creates an instance of struct Foo which is all empty except
+               for member 'two' which is assigned the value ({ 2 }).
+
+        It is not possible to use both named and unnamed initializers
+        in the same literal.
+
+
+        A struct member is accessed using the -> operator:
+
+            struct Foo var = ...;
+
+            var->one = 1;
+
+
+        It is possible to compute struct lookups at runtime:
+
+            struct Foo bar = ...;
+            string member = "one";
+
+            bar->(member) = 1; // sets bar->one to 1
+            bar->(0) = 1;      // sets bar->one to 1
+
+
+        When using struct values held in variables/expressions of type
+        'mixed', the 'mixed' value should to be casted to the struct
+        value. The cast can be omitted if the looked-up member exists
+        in only one struct (and its children) known to the compiler:
+
+            struct Foo { int one; };
+            struct Bar { int two; };
+            struct Baz { int two; };
+            mixed var;
+
+                        var->one  // looks up Foo->one
+            (struct Foo)var->one  // looks up Foo->one
+                        var->two  // ERROR: ambiguous lookup
+            (struct Bar)var->one  // looks up Bar->one
+
+
+USAGE IN CLOSURES
+        The #'(< operator can be used in lambda closures to create a
+        struct; the type of the struct is given by the 'template'
+        struct passed as first argument. The content of the template
+        struct is irrelevant, so an empty struct suffices. For
+        example, to create an instance of struct Foo:
+        
+            ({ #'(<, (<Foo>), 1, ({ 2 }), (<Bar>) })
+
+        The order of the member values is the order in which they
+        appear in the struct definition.
+
+        To access a struct member in a lambda closure, use the #'->
+        operator with the name of the member as double-quoted symbol
+        or literal string:
+
+            ({ #'->, struct-expression, ''one })
+            ({ #'->, struct-expression, "one" })
+
+
+MISCELLANEOUS
+        Internally structs can be identified by the ID string
+        returned from get_type_info(). This string contains the name
+        of the struct, the name of the program its type was defined in,
+        and the ID number of the program. However, do not rely on
+        a particular format of this string!
+
+        Support for structs is signaled by the macro __LPC_STRUCTS__.
+
+        Since structs are tied to the program they are defined in,
+        re-compiling a program creates new struct types which are
+        in principle incompatible to the old ones. However, the LPC
+        compiler checks if the newly compiled structs have the same
+        structure as their older counterparts of the same name
+        (and defining program). If the structures conform, the existing
+        older struct types are used instead of the new ones. This way
+        an accidental of for example /std/types.c doesn't break
+        the whole mud.
+
+
+EXAMPLES
+        Suppose we have two objects: a monster, and a monster
+        coordinate tracker, and we want to use a struct to store the
+        coordinate:
+
+          -- monster_coordinate.c --
+            struct Coordinate { int x; int y; };
+
+          -- monster_tracker.c --
+            inherit "monster_coordinate";
+
+            void track (struct Coordinate coord) { ... }
+
+          -- monster.c --
+            inherit "monster_coordinate";
+
+            int move (..) {
+              ...
+              "monster_tracker"->track( (<Coordinate> my_x, my_y) );
+            }
+
+        Note that using '#include "monster_coordinate.c"' instead of inherit
+        won't work. While the objects would compile, the first call to
+        track() would cause a runtime error of the type 
+
+          Illegal type to struct->(): struct Coordinate (/monster.c #234),
+                                      expected struct Coordinate
+                                      (/monster_tracker.c #552)
+
+
+HISTORY
+        structs were fully implemented first in LDMud 3.3.246.
+        The implementation was revised in LDMud 3.3.344.
+        The reactivation of unchanged structs in object updates was
+        implemented in LDMud 3.3.417.
+
+
+SEE ALSO
+        mappings(LPC), get_type_info(E), structp(E), to_mapping(E),
+        to_struct(E), struct_info(E), baseof(E)
diff --git a/doc/LPC/switch b/doc/LPC/switch
new file mode 100644
index 0000000..9bca596
--- /dev/null
+++ b/doc/LPC/switch
@@ -0,0 +1,93 @@
+NAME
+        switch
+
+SYNTAX
+        switch (expr) block
+
+DESCRIPTION
+        Branch to the case label in statement that matches expr.
+        If no matching case label is found (by value or by type),
+        branch to the default label in statement.
+
+        A case label has the form
+
+                case expr_n :
+
+        where expr_n must be constant, or the form
+
+                case expr_n1 .. expr_n2 :
+
+        where expr_n1 and expr_n2 must be numeric constants and
+        expr_n1 < expr_n2.
+
+        Either all case labels have to be strings or all have to be
+        numeric. Only 0 is special: it is allowed in a switch
+        statement where all other labels are strings.
+
+        A default label has the form
+
+                default :
+
+        The default label defaults to the end of statement if not
+        given explicitly.
+
+        Whenever a 'break' statement is executed inside 'statement' a
+        branch to the end of the switch statement is performed.
+
+EXAMPLE
+        Typical usage:
+
+            switch(random(100)) {
+              case 0 .. 22 : write("Nothing happens"); break;
+              case 23 .. 27 :
+                write("You are surrounded by a golden glow");
+                this_player()->heal_self(random(3));
+                break;
+              case 28 .. 32 :
+                write("The water was poisoned!\n");
+                this_player()->add_exp(this_player()->hit_player(random(4)));
+                break;
+              case 33 : write("You hear a voice whispering: "+random_hint());
+              /* fall through */
+              case 34 :
+                write("While you didn't pay attention, a water demon "
+                      "snatches a coin out of your purse!\n");
+                this_player()->add_money(-1);
+                break;
+              default : write "You hear some strange noises\n"; break;
+              case 42 : return;
+              case 99 : write("It tastes good.\n";
+            }
+
+NOTE
+        In C, the grammar for switch() is
+
+            switch (expr) statement
+
+        allowing constructs like
+
+            switch (expr)
+              while (expr2)
+              {
+              case 1: ...
+              case 2: ...
+              }
+
+        In LPC a switch has to be followed by a block that contains the
+        case labels directly. In contrast to C the group of statements
+        following a case label have their own lexical scope so that
+        variable declarations may not cross case labels.
+
+HISTORY
+        LDMud 3.2.10 constrained the grammar to require a block for the
+          switch() body, not just a statement. This differs from the C
+          syntax, but was necessary as the compiler didn't handle
+          the statement case correctly.
+        LDMud 3.3 allowed to pass values of the wrong type to switch(), the
+          driver would in that case use the default case. Before, values of
+          the wrong type caused a runtime error.
+        LDMud 3.3.718 disallowed case labels in inner blocks and variable
+          declarations that cross case labels.
+
+SEE ALSO
+        for(LPC), foreach(LPC), do-while(LPC), if(LPC), while(LPC)
diff --git a/doc/LPC/types b/doc/LPC/types
new file mode 100644
index 0000000..903a1be
--- /dev/null
+++ b/doc/LPC/types
@@ -0,0 +1,136 @@
+CONCEPT
+    types
+
+DESCRIPTION
+
+    Variables can have the following types:
+
+    o int       An integer. Normally full 32 bits signed, yielding a
+                range of at least -2,147,483,648 to 2,147,483,647. The
+                exact available range is given by the predefined
+                macros __INT_MIN__ and __INT_MAX__.
+
+                Integer values can be specified in decimal, in
+                sedecimal when preceeded by '0x' (e.g. 0x11), binary
+                when preceeded by '0b' (e.g. 0b00010001), octal when
+                preceeded by '0o' (e.g. 0o21) and as character
+                yielding the charset value for the character as the number
+                to use (e.g. '0' yields 48 on ASCII machines).
+
+                Character values are enclosed in single-quotes ('),
+                with the sequence ''' returning the single-quote
+                itself. Instead of the literal character an
+                escape-sequence can be written between the
+                single-quotes:
+                  \N   : the character code N in decimal
+                  \0xN : the character code N in sedecimal
+                  \xN  : the character code N in sedecimal
+                  \0oN : the character code N in octal
+                  \0bN : the character code N in binary
+                  \a   : BEL (0x07)
+                  \b   : Backspace (0x08)
+                  \t   : Tab (0x09)
+                  \e   : Escape (0x1b)
+                  \n   : Newline (0x0a)
+                  \f   : Formfeed (0x0c)
+                  \r   : Carriage Return (0x0d)
+                  \<other character>: the given character
+
+    o status    OUTDATED - status was planned to be an optimized
+                boolean format, but this was never actually
+                implemented. status does work; however, since it
+                is only an alias for type 'int', just use int.
+
+    o string    Strings in lpc are true strings, not arrays of characters
+                as in C (and not pointers to strings). Strings are
+                mutable -- that is, the contents of a string can be
+                modified as needed.
+
+                The text of a string is written between double-quotes
+                ("). A string can written over several lines when the
+                lineends are escaped (like a macro), however a better
+                solution is to write one string per line and let the
+                gamedriver concatenate them.
+
+                String text typically consists of literal characters,
+                but escape-sequences can be used instead of
+                characters:
+                  \<CR>     : Carriage Return (0x0d)
+                  \<CR><LF> : ignored
+                  \<LF>     : ignored
+                  \<LF><CR> : ignored
+
+                  \N   : the character code N in decimal
+                  \0xN : the character code N in sedecimal
+                  \xN  : the character code N in sedecimal
+                  \0oN : the character code N in octal
+                  \0bN : the character code N in binary
+                  \a   : BEL (0x07)
+                  \b   : Backspace (0x08)
+                  \t   : Tab (0x09)
+                  \e   : Escape (0x1b)
+                  \n   : Newline (0x0a)
+                  \f   : Formfeed (0x0c)
+                  \r   : Carriage Return (0x0d)
+                  \"   : The double quote (")
+                  \<other character>: the given character
+
+                Adjacent string literals are automatically
+                concatenated by the driver when the LPC program is
+                compiled. String literals joined with '+' are
+                concatenated by the LPC compiler as well.
+
+    o object    Pointer to an object. Objects are always passed by
+                reference.
+
+    o array     Pointer to a vector of values, which could also
+                be an alist. Arrays take the form ({ n1, n2, n3 })
+                and may contain any type or a mix of types. Arrays
+                are always passed by reference. Note that the size
+                of arrays in LPC, unlike most programming languages,
+                CAN be changed at run-time.
+
+    o mapping   An 'associative array' consisting of values indexed by
+                keys. The indices can be any kind of datatype.
+                Mappings take the form ([ key1: value1, key2: value2 ]).
+                By default, mappings are passed by reference.
+
+    o closure   References to executable code, both to local
+                functions, efuns and to functions compiled at
+                run-time ("lambda closures").
+
+    o symbol    Identifier names, which in essence are quoted strings.
+                They are used to compute lambda closures, e.g. instead
+                of ({..., 'ident, ... }) you can write declare a
+                'symbol' variable foo, compute a value for it, and then
+                create the closure as ({ ..., foo, ... })
+
+    o float     A floating point number in the absolute range
+                __FLOAT_MIN__ to __FLOAT_MAX__ (typically 1e-38 to 1e+38).
+                Floating point numbers are signified by a '.'
+                appearing, e.g. '1' is integer 1, but '1.' is
+                floating-point 1 .
+
+    o mixed     A variable allowed to take a value of any type (int,
+                string, object, array, mapping, float or closure).
+
+    o struct    A collection of values. See structs(LPC).
+
+    o union     A range of types, either of which the variable
+                can contain at runtime. See unions(LPC).
+
+    All uninitialized variables have the value 0.
+
+    The type of a variable is really only for documentation. Unless
+    you define #pragma strong_types or rtt_checks, variables can
+    actually be of any type and has no effect at all on the program.
+    However, it's extremely bad style to declare one type but use
+    another, so please try to avoid this.
+
+    A pointer to a destructed object will always have the value 0.
+
+
+SEE ALSO
+    alists(LPC), arrays(LPC), mappings(LPC), closures(LPC), structs(LPC),
+    unions(LPC), typeof(E), get_type_info(E), inheritance(LPC),
+    pragma(LPC), modifiers(LPC), escape(LPC)
diff --git a/doc/LPC/unions b/doc/LPC/unions
new file mode 100644
index 0000000..65a841d
--- /dev/null
+++ b/doc/LPC/unions
@@ -0,0 +1,30 @@
+CONCEPT
+        unions
+
+DESCRIPTION
+        Unions types are a declaration at compile time that a variable,
+        function parameter or function return value can be one of
+        several types.
+
+        Except for type checks using #pragma rtt_checks they have no
+        impact at runtime. There is no runtime union type, the concrete
+        value type is one of the possibilities of the union type.
+
+        Union types have no type names for themselves, they are declared
+        anonymously with the declaration of the variable, function
+        parameter or return type:
+
+            int|string var;
+            int|float fun(object|closure f);
+
+        When using union types as array member types they must be
+        enclosed with < >:
+
+            <int|string>* arr;    /* An array of ints and strings. */
+            int*|string*  arr;    /* Either an array of ints or
+                                     an array of strings.          */
+
+            /* There must be a whitespace between two consecutive <
+               to be not confused with the << operator:            */
+            < <int|string>*|object >* arr;
+
diff --git a/doc/LPC/varargs b/doc/LPC/varargs
new file mode 100644
index 0000000..ef21837
--- /dev/null
+++ b/doc/LPC/varargs
@@ -0,0 +1,75 @@
+CONCEPT
+    varargs
+
+DESCRIPTION
+    A function uses "varargs", short for "variable arguments", if
+    it intentionally may be called with less or more arguments
+    than formally specified.
+
+    The proper order to define a function call is:
+
+        [ modifier ] [ varargs ] [ return type ] function( args...)
+
+     Any other order will result in an error.
+
+
+    Given a function definition like this:
+
+        void fun (string arg1, int arg2, int arg3)
+
+    fun() has to be called with exactly three parameters: one
+    string and two integers.
+
+
+    If the function is defined as
+
+        varargs void fun (string arg1, int arg2, int arg3)
+
+    it is possible to call the function with just arg1, or arg1
+    and arg2. The remaining unspecified arguments (arg2 and arg3,
+    resp. arg3) are in these cases assumed to be 0.
+
+
+    To pass more arguments than specified, the functions last
+    parameter must be defined as following:
+
+        void fun (string arg1, int arg2, varargs int * arg3)
+
+    This allows fun() to be called with two or more arguments.
+    The arguments, except those assigned to the other parameters,
+    in this case arg1 and arg2, and collected into an array which
+    is then passed as arg3. For example
+
+        fun("foo", 1)       -> arg3 == ({ })
+        fun("foo", 1, 2)    -> arg3 == ({ 2 })
+        fun("foo", 1, 2, 3) -> arg3 == ({ 2, 3 })
+
+    The type of the varargs argument has to be an array of the
+    expected type (int*, object*, string*, ...); in this example,
+    only integers are allowed. To accept arguments of any kind,
+    define the parameter as 'varargs mixed' or 'varargs mixed*'.
+
+    To 'flatten' the received argument array in your own function
+    calls, use the efun apply(); e.g.:
+
+        apply(#'call_out, "bar", 1, arg3)
+
+    or the 'flatten arguments' operator:
+
+       call_out("bar", 1, arg3...)
+
+    The two varargs variants can of course be combined:
+
+        varargs void fun (string arg1, int arg2, varargs int* arg3)
+
+    defines a function which may be called with any number of
+    arguments.
+
+
+HISTORY
+    The possibility to pass more arguments than formally specified
+    was introduced in 3.2.1@132. Before, the excess arguments were
+    silently ignored.
+
+SEE ALSO
+    pragma(LPC), apply(E), modifiers(LPC)
diff --git a/doc/LPC/while b/doc/LPC/while
new file mode 100644
index 0000000..adc4708
--- /dev/null
+++ b/doc/LPC/while
@@ -0,0 +1,15 @@
+NAME
+        while
+
+SYNTAX
+        while (expr) statement;
+
+DESCRIPTION
+        While 'expr' evaluates to non 0, execute statement.
+
+        A 'break' in the 'statement' will terminate the loop. A
+        'continue' will continue the execution from the beginning of
+        the loop.
+
+SEE ALSO
+        for(LPC), foreach(LPC), do-while(LPC), if(LPC), switch(LPC)
diff --git a/doc/README b/doc/README
new file mode 100644
index 0000000..5121f9a
--- /dev/null
+++ b/doc/README
@@ -0,0 +1,54 @@
+In diesem Verzeichnis befindet sich alles, was das MorgenGrauen an
+Dokumentation zu bieten hat. Obwohl hier schon so einiges zu finden ist,
+ist die Dokumentation noch lange nicht komplett.
+Zum Teil werden die Verzeichnisse noch umstrukturiert. Was genau wo zu
+finden ist, steht unten sowie in den .readme-Dateien der jeweiligen
+Verzeichnisse.
+
+Die Hilfeseiten werden parallel im ASCII- und im HTML-Format gefuehrt.
+Die HTML-Seiten sind unter http://mg.mud.de/help/ zu finden.
+
+Bei Aenderungen an den Seiten achtet bitte auf Korrektheit. Im Zweifelsfall
+schreibt eine Mail an einen aktiven H oder EM.
+
+Die Unterverzeichnisse und Dateien hier haben folgende Bedeutungen:
+
+* Dokumentation von Befehlen und Themen innerhalb des Spiels
+  (mehr oder weniger komplett ueberarbeitet)
+   o help/   - allgemeine Themen, die ALLE betreffen
+   o pcmd/   - die Befehle, die alle Spieler beherrschen
+   o scmd/   - Seherbefehle und Hilfen zum Hausbau
+   o mcmd/   - hier sind die Magierbefehle beschrieben
+   o wiz/    - allgemeine Themen fuer Magier
+   o g.*/    - Hilfeseiten, die zur Gilde XYZ gehoeren
+   o hilfe.* - Uebersichtsseiten mit den entsprechenden Befehlen
+   o balance/- Uebersicht ueber balancerelevante Themen und Regeln
+
+* Uebersicht ueber Objekte und Funktionen MudLib (nie vollstaendig)
+   o efun/   - Funktionen, die der GameDriver zur Verfuegung stellt
+   o lfun/   - Funktionen, die die Objekte der MudLib zur Verfuegung stellen
+   o master/ - Funktionen, die das Masterobjekt zur Verfuegung stellt
+
+   o std/    - Die Objekte des Basis-MudLib
+   o obj/    - Einige hilfreiche Zusatzobjekte
+
+   o props/  - Die Properties
+   o props/properties.h
+	     - Kurzuebersicht der Properties (Einzelseiten sind aktueller)
+
+* Sonstiges
+   o 3.0/         - einige SEHR alte Hilfeseiten ueber LPmuds im Allgemeinen
+   o beispiele/   - Beispielobjekte und -raeume, muss dringend gefuellt werden
+   o concepts/    - Grundkonzepte von LPC
+   o driver/      - Befehle und Kommandozeile des GameDrivers
+   o ed/          - ein paar Hilfeseiten fuer den 'ed'itor
+   o Grundlagen/  - ein paar grundlegende Informationen; landet vielleicht
+                    mal im Kurs (s.u.)
+   o KURS/        - Hier soll mal ein MG-spezifischer LPC-Kurs hin. Der Kurs
+                    in den Unterverzeichnissen LPC-KURS/ und LPC-KURS2/ ist
+                    a) in englisch und b) nicht unbedingt fuer das
+                    Programmieren im MG geeignet. Fuers Lernen von LPC sollte
+                    er aber ausreichen.
+   o LPC/         - Datentypen und Sprachelemente von LPC
+   o misc/        - alles, was sonst nirgendwo passt
+
diff --git a/doc/about-efun+lfun-docs b/doc/about-efun+lfun-docs
new file mode 100644
index 0000000..473e384
--- /dev/null
+++ b/doc/about-efun+lfun-docs
@@ -0,0 +1,72 @@
+NAME
+	about-efun+lfun-docs
+
+AUTHOR
+	These man pages were collected from the original 2.4.5 docs,
+	from the installed docs of NightFall, TubMud and MorgenGrauen.
+
+	They were collected corrected and completed Pepel@NightFall,
+	with assistance and support by the LPC wizards of NightFall,
+	TubMud and MorgenGrauen, namely Hyp, Macbeth and Mateese. Also
+	to mention are Bumblebee, Boing, Deepthought, Demos, Hate and
+	Jof.
+
+DESCRIPTION
+	The man pages generally adhere to the following format, which
+	was inspired by the Unix man pages.
+
+	<manpage> ::= <section>+
+
+	There are one or more sections in a man page.
+
+	<section> ::= <section-name><newline><section-text><newline>
+
+	The section names are always at the beginning of a line, and
+	in all capital letters (debatable uglyness).
+
+	<section-name> ::=   'NAME' | 'CONCEPT' | 'SYNOPSIS'
+			   | 'LAST UPDATED'
+			   | 'DESCRIPTION' | 'AUTHOR' | 'BUGS'
+			   | 'CAVEATS' | 'WARNING' | 'NOTE' | 'NOTES'
+			   | 'EXAMPLE' | 'EXAMPLES'
+			   | 'SEE ALSO'
+
+	If a NAME section occurs, it should give the name of the man
+	page.
+	If a SYNOPSIS section occurs, the name of the man page canb be
+	derived from the function name, that appears in the line after
+	SYNOPSIS, which looks as follows:
+
+	<returntype><space>['*']<function-name>'('<parameter-prototypes>
+
+	SEE ALSO is followed by one or more lines that contain
+	crossreferences of the form
+
+	<crossreferences> ::=
+		<crossref>[','<white-space><crossref>]*<white-space><EOF>
+	<crossref> ::= <man-page-name>'('<chapter-abbrev>')'
+
+	The names of the referenced man pages are followed in brackets
+	by an abbreveiation for the chaper (i.e. directory) the man
+	page is in. The abbrevs are
+	E for efun/	the ``system calls'' of LPC
+	L for lfun/	member functions that the driver applies to objects
+	C for concepts/	general concepts of LPMUD and LPC
+	LPC for LPC/	about parts of the LPC language
+	M for master/	lfuns that are applied to the master object only
+	D for driver/	some info about internal operation of the driver
+
+	If you want to process these man pages by some converter
+	program, you can use these to map the chapter names to file
+	names or whatever.
+
+BUGS
+	There are no man pages supported by Amylaar himself.
+	I hope one day I can convince him to at least put these docs
+	into the driver distribution.
+
+	Meanwhile, please report any corrections / suggestions /
+	completions to <pepel@ibr.cs.tu-bs.de>
+
+SEE ALSO
+	efun(E), lfun(L), concepts(C), lpc(LPC), master(M), driver(D)
diff --git a/doc/applied/__INIT b/doc/applied/__INIT
new file mode 100644
index 0000000..dc56f6c
--- /dev/null
+++ b/doc/applied/__INIT
@@ -0,0 +1,12 @@
+SYNOPSIS
+        __INIT
+
+DESCRIPTION
+        This function is constructed automagically by the parser at
+        compiler, if the parser was compiled with #define
+        INITIALISATION__INIT. This function is not intended to be
+        defined by the lpc objects, and never to be called from lpc
+        objects. This man page is here just for completeness.
+
+SEE ALSO
+        initialisation(LPC)
diff --git a/doc/applied/add_weight b/doc/applied/add_weight
new file mode 100644
index 0000000..5f8912f
--- /dev/null
+++ b/doc/applied/add_weight
@@ -0,0 +1,24 @@
+DEPRECATED
+SYNOPSIS
+        int add_weight(int w)
+
+DESCRIPTION
+        In compat mode, this function is used by the efun transfer().
+
+        An object that can contain other objects and is not a room
+        must define this function. It is called with the extra weight
+        of the object that is moved into it. If this is ok, then it
+        has to increment the local weight count, and return true.
+        Otherwise, return false, and the new object can not be entered
+        into this object.
+
+        The function is also called with the negative weight in the
+        object that the moving leaves.
+
+        Note that no set_weight() is required by the parser.
+
+HISTORY
+        Deprecated in LDMud 3.3 as transfer() has been deprecated.
+
+SEE ALSO
+        transfer(E), query_weight(A), prevent_insert(A)
diff --git a/doc/applied/applied b/doc/applied/applied
new file mode 100644
index 0000000..b7f63b9
--- /dev/null
+++ b/doc/applied/applied
@@ -0,0 +1,12 @@
+NAME
+        applied
+
+DESCRIPTION
+        This directory contains descriptions for the lfuns used by
+        Amylaar's version of the LPC parser.
+
+        These are functions that are applied by the parser to the LPC
+        objects on various occasions.
+
+SEE ALSO
+        efun(E), master(M), concepts(C), lpc(LPC), driver(D)
diff --git a/doc/applied/can_put_and_get b/doc/applied/can_put_and_get
new file mode 100644
index 0000000..5479832
--- /dev/null
+++ b/doc/applied/can_put_and_get
@@ -0,0 +1,20 @@
+DEPRECATED
+SYNOPSIS
+        int can_put_and_get()
+
+DESCRIPTION
+        In compat mode, this function is used by the efun transfer().
+
+        Define this function in objects that are neither living nor
+        rooms if you want to make it possible to put something into
+        current object.
+
+        Return true if ok, otherwise 0. That means that default is
+        that it is not possible to put something into an object.
+
+HISTORY
+        Deprecated in LDMud 3.3 as transfer() has been deprecated.
+
+SEE ALSO
+        transfer(E), prevent_insert(A)
+
diff --git a/doc/applied/catch_msg b/doc/applied/catch_msg
new file mode 100644
index 0000000..d244af4
--- /dev/null
+++ b/doc/applied/catch_msg
@@ -0,0 +1,19 @@
+SYNOPSIS
+        void catch_msg(mixed *|struct|mapping|object msg, object obj)
+
+DESCRIPTION
+        When say(), tell_room() or tell_object() are used with an
+        non-string as message, the value will be passed to catch_msg()
+        in all living objects that can hear it, instead of writing to
+        the user resp.  sending to catch_tell(). This can be used to
+        implement communication protocols between livings. The second
+        argument denotes the object that has sent the message.
+
+HISTORY
+        LDMud 3.2.11 added tell_object() to the efuns calling this
+        lfun for symmetry reasons.
+        LDMud 3.3.686 added the use of a mapping/struct/object as message
+        value.
+
+SEE ALSO
+        say(E), tell_room(E), catch_tell(A)
diff --git a/doc/applied/catch_tell b/doc/applied/catch_tell
new file mode 100644
index 0000000..942a81a
--- /dev/null
+++ b/doc/applied/catch_tell
@@ -0,0 +1,22 @@
+SYNOPSIS
+        void catch_tell(string)
+
+DESCRIPTION
+        When a message is sent to an non-interactive object, via say(),
+        tell_object, tell_room() or write(), it will get to the function
+        catch_tell(string). The idea is to enable communications between
+        NPCs and from a user to an NPC.
+
+        Messages sent to an interactive object are also passed to that
+        object's catch_tell() lfun, _if it has one_. If the receiver
+        (or one of its shadows) doesn't have that lfun, the text is sent
+        to the socket directly. Only messages sent by an interactive
+        object to itself inside a catch_tell always written to the socket
+        immediately.
+
+        This allows to filter and process text before it is written
+        to a player.
+
+SEE ALSO
+        enable_commands(E), say(E), tell_object(E), tell_room(E),
+        write(E), snoop(E), catch_msg(A)
diff --git a/doc/applied/clean_up b/doc/applied/clean_up
new file mode 100644
index 0000000..47dfa73
--- /dev/null
+++ b/doc/applied/clean_up
@@ -0,0 +1,51 @@
+SYNOPSIS
+        int <lfun> (int refcount)
+        int <closure>(int ref, object ob)
+
+DESCRIPTION
+        The hook H_CLEAN_UP defines a lfun or a closure used to clean
+        up an object. In older drivers this was hardwired to the lfun
+        clean_up().
+
+        The function is applied by the driver when an object hasn't been
+        used for a long time, to give it a possibility to
+        self-destruct. The refcount <ref> passed as argument will be 0 for
+        clone objects, 1 for a simple loaded object, and greater when
+        the object is cloned or inherited by some existing object.  It
+        is recommended not to self_destruct the object when the
+        reference count is greater than one.
+
+        By convention, a refcount < 0 is used if some other object
+        asks the called object to clean_up.
+
+        If the function is a closure, the second argument <ob> is the
+        object to clean up.
+
+        If the hook specifies a non-existing lfun, or if the call
+        returns 0, no further attempt to clean up this object will be done.
+
+        Returning a non-zero value is only recommended when the reason
+        why the object can't self-destruct is likely to vanish without
+        the object being touched, that is, when no local function is
+        called in it, (and in compat mode also when the object is not
+        being moved around).
+
+        A typical mud configuration defines the time to wait for
+        clean_up() so long that you can assert reset() has been
+        called since the object has been touched last time.
+
+EXAMPLES
+        A clone of /std/drink defines clean_up() to self-destruct if
+        it is empty, not carried a living being and not touched for
+        a long time.
+
+        A room that inherits /std/room defines clean_up() to
+        self-destruct if it is neither inherited nor used as a
+        blueprint, is empty and was not entered for a long time.
+
+HISTORY
+        Before 3.2.1, the function was hardwired to the lfun clean_up().
+
+SEE ALSO
+        reset(A), heart_beat(A), call_out(E), destruct(E), remove(A),
+        hooks(C)
diff --git a/doc/applied/create b/doc/applied/create
new file mode 100644
index 0000000..1056e13
--- /dev/null
+++ b/doc/applied/create
@@ -0,0 +1,29 @@
+SYNOPSIS
+        void create()
+
+DESCRIPTION
+        The H_CREATE_SUPER/_OB/_CLONE hooks define the function or closure
+        to be called when the driver creates a new object. In older
+        drivers this was hardwired to the lfun create(), and a lot of
+        hook implementations still follow this tradition.
+
+        This function will be called only once on creation of the
+        object (this is when the object will be loaded or cloned).
+        Inside this function all major initialization can be done. The
+        current user and the previous object are defined but the
+        object has no environment.
+
+EXAMPLE
+        object cloner;
+        void create() {
+           cloner=this_player();
+        }
+
+        Initialize the global variable to hold the one who
+        created/cloned the object.
+
+        For 3.2.1, the mudlib may be programmed to call other lfuns
+        than create() on an objects creation.
+
+SEE ALSO
+        reset(A), init(A), __INIT(A), initialisation(LPC), hooks(C), native(C)
diff --git a/doc/applied/drop b/doc/applied/drop
new file mode 100644
index 0000000..14f2bb1
--- /dev/null
+++ b/doc/applied/drop
@@ -0,0 +1,25 @@
+DEPRECATED
+SYNOPSIS
+        int drop(void)
+        int drop(int silently)
+
+DESCRIPTION
+        In compat mode this lfun is used by the efun transfer().
+
+        It is called to check if an object wants to be moved out of
+        the inventory of a living object. drop() should return 1 to
+        prevent dropping. This is the opposite of the get() function.
+        That is because if drop() is not defined, it will always be
+        possible to drop an object.
+
+        If the object self-destructs when drop() is called, be sure to
+        return 1, as the destructed item surely not can be dropped.
+
+        Most compat mode LPC libraries to define one argument for
+        drop. If silently is true, no messages should be written.
+
+HISTORY
+        Deprecated in LDMud 3.3 as transfer() has been deprecated.
+
+SEE ALSO
+        transfer(E)
diff --git a/doc/applied/exit b/doc/applied/exit
new file mode 100644
index 0000000..575fdd3
--- /dev/null
+++ b/doc/applied/exit
@@ -0,0 +1,21 @@
+SYNOPSIS
+        void exit(object ob)
+
+DESCRIPTION
+        This function was used in compat mode drivers in context
+        with moving objects. Nowadays objects are moved by means
+        of the H_MOVE_OBJECT0/1 hooks, and use of this lfun is
+        up to the mudlib.
+
+        This function was called in the old environment everytime a
+        living object ob leaves it.
+
+        The function this_player() will return a random value, don't
+        use it at this point.
+
+        WARNING: Using this function is EXTREMELY dangerous. A single
+        bug, and you are forever (i.e. until the next reboot occurs)
+        caught in the room.
+
+SEE ALSO
+        init(A)
diff --git a/doc/applied/get b/doc/applied/get
new file mode 100644
index 0000000..655a1b0
--- /dev/null
+++ b/doc/applied/get
@@ -0,0 +1,18 @@
+DEPRECATED
+SYNOPSIS
+        int get()
+
+DESRIPTION
+        In compat mode, this function is used by the efun transfer().
+
+        If an object wants control over when it is possible to pick it
+        up, i.e. moved into a living object, then it must define
+        get(), and return 1 if ok to pick up.
+
+        id() has been called before this to identify the object.
+
+HISTORY
+        Deprecated in LDMud 3.3 as transfer() has been deprecated.
+
+SEE ALSO
+        transfer(E), drop(A)
diff --git a/doc/applied/heart_beat b/doc/applied/heart_beat
new file mode 100644
index 0000000..9876b03
--- /dev/null
+++ b/doc/applied/heart_beat
@@ -0,0 +1,47 @@
+SYNOPSIS
+        void heart_beat()
+
+DESCRIPTION
+        This function will be called automatically every
+        __HEART_BEAT_INTERVAL__ seconds. The driver will call the function
+        directly in the object, even if the function is being shadowed.
+
+        The start and stop of heart beat is controlled by the efun
+        set_heart_beat(). Be careful not to have objects with heart
+        beat running all the time, as it uses a lot of driver
+        resources. If there is an error in the heart beat routine, the
+        heart beat will be turned off for this object and the
+        master function heart_beat_error() is called. If the call
+        to the master function returns a non-zero value, the heart beat
+        will be turned back on again.
+
+        The function this_player() will return this object, but only if
+        it is living. Otherwise, this_player() will return 0.
+
+        The function will be called only if there is at least one interactive
+        user in the game.
+
+        Note that error messages will be given to the current user
+        which will be the object itself or nobody.
+
+EXAMPLE
+        object owner;
+        void create() {
+           ...
+           owner=this_player();
+           set_heart_beat(1);
+        }
+        void heart_beat() {
+           tell_object(owner, "Your heart goes: BUM BUM\n");
+        }
+
+        We have to use tell_object() because write goes to the current
+        user and this can only be the object itself or nobody.
+
+HISTORY
+        LDMud 3.3.687 made the interval time configurable at driver compile
+        time (used to be 2 seconds fixed).
+
+SEE ALSO
+        set_heart_beat(E), call_out(E), enable_commands(E),
+        heart_beat_error(M)
diff --git a/doc/applied/id b/doc/applied/id
new file mode 100644
index 0000000..bdb1fb2
--- /dev/null
+++ b/doc/applied/id
@@ -0,0 +1,17 @@
+SYNOPSIS
+        varargs int id(string str, int lvl);
+
+DESCRIPTION
+        Let the object identify itself. If str matches an id of the
+        current object then return a none zero value.
+
+        This lfun is applied for the efun present().
+
+EXAMPLE
+        int id(string str) {
+           return "sword" == str || "sword of fire" == str;
+        }
+
+SEE ALSO
+        present(E)
+        id(L)
diff --git a/doc/applied/init b/doc/applied/init
new file mode 100644
index 0000000..17675da
--- /dev/null
+++ b/doc/applied/init
@@ -0,0 +1,70 @@
+SYNOPSIS
+        void init()
+
+DESCRIPTION
+        The H_MOVE_OBJECT0/1 implement the details of moving objects.
+        In older drivers, init() was called to handle the adding of
+        actions, and a lot of hook implementations still follow this
+        tradition.
+
+        The main purpose of this function is to publish the commands
+        an object implements to other, living objects. Traditionally,
+        whenever a living object enters the vicinity of another
+        object, init() is called in the latter and this_player() will
+        point to the former object. This happens mutually should both
+        objects happen to be living.
+
+        Or more formally:
+
+            If the object O that moves is marked as living then first
+            call init() of the destination object D with this_player()
+            set to O.
+
+            Then apply the two following rules for each object C
+            inside D:
+
+                    If C is marked as living then call O->init() with
+                    this_player() set to C.
+
+                    If O is marked as living then call C->init() with
+                    this_player() set to O.
+
+            Finally, if D is marked as living then call O->init(),
+            with this_player() set to D.
+
+        Starting with 3.2.1, the actual move handling became part of the
+        object library, so a given installation may implement any other scheme
+        of calling init().
+
+        One caveat: commands defined in the player object for the player
+        himself should not be defined in init(), as these commands would be
+        added to _other_ players whenever they happen to be nearby. Instead
+        use a separate function ("add_player_commands()" or so) which
+        is called during the creation of the player.
+
+EXAMPLE
+        (This example assumes a traditional implementation of the
+         movement handling)
+
+        Lets say we have a object structure of living (l1 and l2) and
+        non living objects (n1 and n2) as the following:
+
+        l1
+           n1
+           l2
+           n2
+
+        If we now move another living object l3 into l1, the call
+        suequence of the init() functions looks like this:
+
+        l1->init()  first init() of the destination will be called
+        n1->init()  now iterate throw the inventory of the destination
+        l3->init()
+        l2->init()
+        n2->init()
+        l3->init()  and finally call init() of the object that has
+                    been moved
+
+SEE ALSO
+        add_action(E), set_environment(E), environment(E), move_object(E),
+        hooks(C)
diff --git a/doc/applied/logon b/doc/applied/logon
new file mode 100644
index 0000000..97f0373
--- /dev/null
+++ b/doc/applied/logon
@@ -0,0 +1,28 @@
+SYNOPSIS
+        int logon (void)
+        int logon (int flag)
+
+DESCRIPTION
+        When the driver created a new connection (either by accepting
+        it or by creating it with net_connect()) and bound it to an
+        object, it then calls logon() in that object.
+
+        The method should return 0 on failure, and everything else on
+        success.
+
+        If the driver attempted to create a connection in the
+        background and failed, it will call logon(-1) in the intended
+        object to inform the mudlib about the failure.
+
+        If the master attempted a secure connection in connect(E) and
+        did not set an explicit TLS callback, the call to logon() won't
+        happen until the TLS handshake is complete. If the master set
+        a TLS callback, that will be executed in place of logon().
+
+HISTORY
+        LDMud 3.2.10 added the extended meaning for net_connect().
+        LDMud 3.2.13/3.3.713 streamlined the handling of secure connections
+        during logon.
+
+SEE ALSO
+        connect(M), net_connect(E), exec(E), tls_init_connection(E)
diff --git a/doc/applied/modify_command b/doc/applied/modify_command
new file mode 100644
index 0000000..4a4b2ef
--- /dev/null
+++ b/doc/applied/modify_command
@@ -0,0 +1,27 @@
+SYNOPSIS
+        int|string <name>(string cmd)
+
+DESCRIPTION
+        After set_modify_command(mob) was called for an interactive
+        object iob, all commands for that user will be passed to
+        mob-><name>(), and the return will then be checked for
+        actions. The actual name of the lfun to call is specified
+        by the H_MODIFY_COMMAND_FNAME hook - traditionally it's
+        'modify_command'.
+
+        If the result is a string, it is the new command to execute
+        instead of the given one. Note that it is not possible to
+        make several commands from one this way!
+        If the result is a non-zero number, the given command is to
+        be ignored. In case of the closure/lfun setting this may
+        mean that the closure/lfun already executed it.
+        If the result is 0, the originally given command is to be
+        used.
+
+HISTORY
+        In 3.2.1@109 the name of the lfun to call must be specified
+        using the H_MODIFY_COMMAND_FNAME driver hook.
+
+
+SEE ALSO
+        set_modify_command(E), hooks(C)
diff --git a/doc/applied/parse_command_adjectiv_id_list b/doc/applied/parse_command_adjectiv_id_list
new file mode 100644
index 0000000..7745de9
--- /dev/null
+++ b/doc/applied/parse_command_adjectiv_id_list
@@ -0,0 +1,15 @@
+SYNOPSIS
+        string *parse_command_adjectiv_id_list(void)
+
+DESCRIPTION
+        Return all adjectives associated with this object.
+
+        Used by parse_command().
+
+EXAMPLE
+        string * parse_command_adjectiv_id_list() {
+            return ({ "iffish" });
+        }
+
+SEE ALSO
+        parse_command(E)
diff --git a/doc/applied/parse_command_id_list b/doc/applied/parse_command_id_list
new file mode 100644
index 0000000..4c8fc73
--- /dev/null
+++ b/doc/applied/parse_command_id_list
@@ -0,0 +1,10 @@
+SYNOPSIS
+        string *parse_command_id_list(void)
+
+DESCRIPTION
+        Return the normal singular names of this object.
+
+        Used by parse_command().
+
+SEE ALSO
+        parse_command(E), parse_command_pural_id_list(A)
diff --git a/doc/applied/parse_command_plural_id_list b/doc/applied/parse_command_plural_id_list
new file mode 100644
index 0000000..ee8d564
--- /dev/null
+++ b/doc/applied/parse_command_plural_id_list
@@ -0,0 +1,13 @@
+SYNOPSIS
+        string *parse_command_plural_id_list(void)
+
+DESCRIPTION
+        Return the plural names of this object.
+
+        If this function doesn't exist, the parser tries to pluralize
+        the names returned by parse_command_id_list().
+
+        Used by parse_command().
+
+SEE ALSO
+        parse_command(E), parse_command_id_list(A)
diff --git a/doc/applied/prevent_insert b/doc/applied/prevent_insert
new file mode 100644
index 0000000..a154ad9
--- /dev/null
+++ b/doc/applied/prevent_insert
@@ -0,0 +1,19 @@
+DEPRECATED
+SYNOPSIS
+        int prevent_insert()
+
+DESCRIPTION
+        In compat mode, this function is used by the efun transfer().
+
+        Define this function in objects that are neither living nor
+        rooms if you want to prevent to put something into current
+        object.
+
+        Return true if ok, otherwise 0. That means that default is
+        that it is not possible to put something into an object.
+
+HISTORY
+        Deprecated in LDMud 3.3 as transfer() has been deprecated.
+
+SEE ALSO
+        transfer(E), can_put_and_get(A)
diff --git a/doc/applied/query_weight b/doc/applied/query_weight
new file mode 100644
index 0000000..7d30b85
--- /dev/null
+++ b/doc/applied/query_weight
@@ -0,0 +1,13 @@
+DEPRECATED
+SYNOPSIS
+        int query_weight(void)
+
+DESCRIPTION
+        In compat mode this lfun is used by the efun transfer().
+        Not that no set_weight() is required by the parser.
+
+HISTORY
+        Deprecated in LDMud 3.3 as transfer() has been deprecated.
+
+SEE ALSO
+        transfer(E), add_weight(A)
diff --git a/doc/applied/remove b/doc/applied/remove
new file mode 100644
index 0000000..bb732b2
--- /dev/null
+++ b/doc/applied/remove
@@ -0,0 +1,21 @@
+SYNOPSIS
+        int remove(void)
+
+DESCRIPTION
+        remove() does some housekeeping to ensure consistency and then
+        destructs the current object.
+
+        This lfun is not applied by the parser, but by other objects
+        to tell the current object to self-destruct. remove() should
+        be supplied by the base classes of the library.  Return 1 if
+        actually self-destructed, 0 otherwise.
+
+        An alternative way to ensure the housekeeping on destruction
+        is through the use of the master apply prepare_destruct().
+
+NOTE
+        Your actual mudlib may name this lfun differently, "remove()" is
+        just the traditional name.
+
+SEE ALSO
+        destruct(E), prepare_destruct(M)
diff --git a/doc/applied/reset b/doc/applied/reset
new file mode 100644
index 0000000..db08bec
--- /dev/null
+++ b/doc/applied/reset
@@ -0,0 +1,37 @@
+SYNOPSIS
+        void reset(int arg)        /* compat mode */
+        void reset(void)           /* native mode */
+
+DESCRIPTION
+        The H_RESET hook defines the function or closure to be called
+        when the driver needs to reset an object. In older drivers
+        this was hardwired to the lfun reset(), and a lot of hook
+        implementations still follow this tradition.
+
+        In compat mode, reset() was called with arg 0 after the object
+        was compiled, and with arg != 0 every once in a while.        In
+        native mode, create() is called after compiling, and reset()
+        is called without arguments every once in a while.
+
+        So, if the argument is zero, the parser is running in compat
+        mode, and reset() may call your create() code. If create() is
+        called, you're on the new version and reset() is not called
+        for object creation. You may call reset() from create() and
+        vice versa, but make sure they do not call each other
+        recursive on either type of driver.
+
+        reset() will be called only in objects that have been used
+        since the last call to reset(), i.e. a function in them was
+        called (other than reset() or clean_up()), or it was moved
+        around.
+
+        This function can be used to reset the state of the object or
+        do some checks or what ever. The game wouldn't be fun if no
+        challenges remained.
+
+        For 3.2.1, the mudlib can be programmed to call an other lfun
+        than reset() to reset an object.
+
+SEE ALSO
+        clean_up(A), heart_beat(A), call_out(E), create(A), __INIT(A),
+        reset(H), hooks(C), initialisation(M), native(C)
diff --git a/doc/balance/artillerie b/doc/balance/artillerie
new file mode 100644
index 0000000..914278d
--- /dev/null
+++ b/doc/balance/artillerie
@@ -0,0 +1,63 @@
+Regeln fuer Artillerie-Objekte
+==============================
+
+1. Definition von Artillerie
+----------------------------
+
+Unter dem Begriff "Artillerie" fasst man alle Objekte zusammen, die
+zusaetzlich zu den vorhandenen Gildenfaehigkeiten durch ein vom
+Spieler initiiertes Kommando direkt Schaden an einem oder mehreren
+Gegnern verursachen.
+
+Waffen und Ruestungen, welche per Kommando Schaden verursachen (z. B.
+Eisstab, Ring vom Schlammdrachen), fallen unter diese
+Definition. Waffen und Ruestungen, die "von sich aus" (ueblicherweise
+per Hit-/DefendFunc) Schaden verursachen (z. B. Todesdrachenpanzer,
+Goblinring), fallen nicht unter diese Definition, sind aber natuerlich
+nach wie vor genehmigungspflichtig.
+
+2. Basisanforderungen
+---------------------
+ 
+Solche Artillerie muss folgenden Anforderungen genuegen:
+
+ a. Artillerie _muss_ P_ATTACK_BUSY beachten (setzen/abfragen).
+ b. Artillerie _muss_ bei Verwendung Konzentrationspunkte 
+    verbrauchen.
+ c. Artillerie darf nicht (bzw. nur begrenzt) gehortet werden
+    koennen.
+ d. Artillerie darf bei Paralyse nicht angewendet werden koennen
+    (P_DISABLE_ATTACK).
+ e. Artillerie muss durch Setzen der Property P_IS_ARTILLERY 
+    gekennzeichnet sein. 
+ f. Artillerie, die Munition benutzt oder selbst Munition ist (wie z.B.
+    Wurfsterne), muss das Unitobjekt und den dortigen Mechanismus zum
+    Zerfall von Objekten nutzen. Dabei sind P_UNIT_DECAY_INTERVAL und
+    P_UNIT_DECAY_QUOTA so zu setzen, dass die Munition eine Halbwertszeit
+    zwischen 5 und 10  Tagen hat, der ihrer Verfuegbarkeit angemessen
+    ist. Dies laesst sich durch geeignetes Einstellen des
+    Prozentsatzes und/oder der Resetzeit erreichen.
+
+    Beispiele:
+    Setzt man p=1% Zerfall pro Reset an, dann muss der Reset
+    fuer eine Halbwertszeit von 5 Tagen dann 
+    t=ln(0.5)/ln(0,99)*5*24*3600 s dauern, das sind 6224 s.
+
+    Moechte man lieber den normalen Reset, der im Mittel 45 min, d.h. 160
+    Resets in 5 Tagen betraegt, so erhaelt man folgenden Prozentsatz:
+    p = 1-0,5^(1/160) d.h. ca. 0,43%.
+ g. Artillerie ist immer genehmigungspflichtig.
+
+Details werden fallweise entschieden.
+
+
+3. Inkraftreten
+---------------
+
+Diese Regeln treten am 5. August 2003 in Kraft.
+Revision am 5. April 2008
+Revision am 7. April 2011
+Revision am 24. August 2011
+
+--------------------------------------------------------------------------
+Letzte Aenderung: Mi, 24.8. 2011 von Humni
diff --git a/doc/balance/attribute b/doc/balance/attribute
new file mode 100644
index 0000000..c1b7f9b
--- /dev/null
+++ b/doc/balance/attribute
@@ -0,0 +1,45 @@
+
+ ATTRIBUTVERAENDERUNGEN
+
+    Positive Veraenderungen an Spielerattributen sind auf jeden Fall von
+    der Balance zu genehmigen. Dabei gelten folgende Regeln:
+
+   - Soll eine Attributveraenderung durch das Tragen/Zuecken von Ruestungen
+    oder Waffen ausgeloest werden, geschieht dies ueber das Setzen der
+    Property P_M_ATTR_MOD. Auch negative Veraenderungen sind von der Balance
+    zu genehmigen, da diese Property mit P_X_ATTR_MOD interagiert.
+   - Soll schon der Besitz eines Objektes die Attributveraenderung
+    ausloesen, muss P_X_ATTR_MOD gesetzt werden. Auch negative 
+    Veraenderungen sind von der Balance zu genehmigen, da diese Property 
+    mit P_M_ATTR_MOD interagiert.
+   - Soll eine Attributveraenderung ohne Beruecksichtigung der Properties
+    P_M_ATTR_MOD und P_X_ATTR_MOD verändert werden, geschieht das ueber die
+    Property P_TIMED_ATTR_MOD. 
+
+
+    Attributveraenderungen sind mit der Balance abzuklaeren.
+
+    Als Regeln sieht die Balance vor:
+    - Bei Objekten mit einem einzelnen Bonus darf +10 nicht ueberschritten 
+    werden.
+    - Bei Objekten mit einem einzigen Malus darf -4 nicht unterschritten
+    werden.
+    - Bei Objekten mit mehreren positiven oder negativen Veraenderungen
+    darf die Summe +4 bzw. -4 nicht ueber/unterschreiten.
+    - Es duerfen nicht mehr als 2 Attribute in einem Objekt geaendert
+    werden. Die Differenz darf 6 nicht ueberschreiten.
+    - Ja hoeher die Attributsveraenderung, um so schlechter sollte das
+    Objekt sonst sein (keine AC 50 mit Ausdauer +10 oder auch -4).
+    - Alle Objekte, die Attribute veraendern, muessen Ruestungs- oder
+    Waffenslots belegen. Ausnahme sind Krankheiten, die der Spieler
+    nicht steuern kann.
+
+ SIEHE AUCH
+
+     balance, ruestungen, waffen, fernwaffen, uniques, npcs,
+     resistenzen, kampfobjekte, grenzwerte
+
+
+------------------------------------------------------------------------------
+Last modified: Thu 2011-04-07 by Humni
+
diff --git a/doc/balance/attributsveraenderungen b/doc/balance/attributsveraenderungen
new file mode 100644
index 0000000..ddd6bf0
--- /dev/null
+++ b/doc/balance/attributsveraenderungen
@@ -0,0 +1,47 @@
+
+ ATTRIBUTVERAENDERUNGEN
+
+    Positive Veraenderungen an Spielerattributen sind auf jeden Fall von
+    der Balance zu genehmigen. Dabei gelten folgende Regeln:
+
+   - Soll eine Attributveraenderung durch das Tragen/Zuecken von Ruestungen
+    oder Waffen ausgeloest werden, geschieht dies ueber das Setzen der
+    Property P_M_ATTR_MOD. Auch negative Veraenderungen sind von der Balance
+    zu genehmigen, da diese Property mit P_X_ATTR_MOD interagiert.
+   - Soll schon der Besitz eines Objektes die Attributveraenderung
+    ausloesen, muss P_X_ATTR_MOD gesetzt werden. Auch negative 
+    Veraenderungen sind von der Balance zu genehmigen, da diese Property 
+    mit P_M_ATTR_MOD interagiert.
+   - Soll eine Attributveraenderung ohne Beruecksichtigung der Properties
+    P_M_ATTR_MOD und P_X_ATTR_MOD verändert werden, geschieht das ueber die
+    Property P_TIMED_ATTR_MOD. 
+
+
+    Attributveraenderungen sind mit der Balance abzuklaeren.
+
+    Als Regeln sieht die Balance vor:
+    - Bei Objekten mit einem einzelnen Bonus darf +10 nicht ueberschritten 
+    werden.
+    - Bei Objekten mit einem einzigen Malus darf -4 nicht unterschritten
+    werden.
+    - Bei Objekten mit mehreren positiven oder negativen Veraenderungen
+    darf die Summe +4 bzw. -4 nicht ueber/unterschreiten.
+    - Es duerfen nicht mehr als 2 Attribute in einem Objekt geaendert
+    werden. Die Differenz darf 6 nicht ueberschreiten.
+    - Ja hoeher die Attributsveraenderung, um so schlechter sollte das
+    Objekt sonst sein (keine AC 50 mit Ausdauer +10 oder auch -4).
+    - Alle Objekte, die Attribute veraendern, muessen Ruestungs- oder
+    Waffenslots belegen. Ausnahme sind Krankheiten, die der Spieler
+    nicht steuern kann und Aehnliches. Damit sind auch Stellen verboten,
+    an der Spieler derartige Krankheiten gezielt bekommen und loswerden
+    kann. Im Zweifel entscheidet die Balance.
+
+ SIEHE AUCH
+
+     balance, ruestungen, waffen, fernwaffen, uniques, npcs,
+     resistenzen, kampfobjekte, grenzwerte
+
+
+------------------------------------------------------------------------------
+Last modified: Thu 2011-06-10 by Humni
+
diff --git a/doc/balance/balance b/doc/balance/balance
new file mode 100644
index 0000000..658d8c5
--- /dev/null
+++ b/doc/balance/balance
@@ -0,0 +1,73 @@
+
+Kurzer Abriss unseres Aufgabenbereiches:
+---------------------------------------
+
+1) Was wird von uns begutachtet?
+
+   Alle Ruestungen und Waffen, die ueber eine DefendFunc 
+   verfuegen, die fuer Spieler von Nutzen ist.
+
+   Alle Ruestungen und Waffen, die ueber eine HitFunc
+   verfuegen, die fuer Spieler von Nutzen ist.
+
+   Alle Ruestungen und Waffen, deren AC/WC die Genehmigungs-
+   grenze ueberschreitet. Auch solche, bei denen dies erst
+   in einer Hit- oder DefendFunc der Fall ist.
+
+   Alle Ruestungen und Waffen, die einen genehmigungs-
+   pflichtigen Schadenstyp enthalten.
+   
+   Alle Objekte, die in irgendeiner Weise das Kampfgeschehen
+   beeinflussen.
+
+2) Was entscheiden wir fuer Gilden?
+
+   Nur Kaempfer-Boni in Waffen und Ruestungen (Waffenschlag, Finte). 
+   Alle anderen Gilden, z.B. Unterstuetzungen fuer Zauberer-Sprueche, 
+   Chaoskomponenten unterliegen dem zustaendigen GildenMagier und 
+   der GildenBalance.
+
+   Selbst ueber Einschraenkungen fuer Gilden haben wir nicht zu 
+   entscheiden. Wenn ein Magier sein Objekt nur fuer Chaoten nutz-
+   bar machen moechte, macht er das eben. Oder wenn er Tanjian aus-
+   schliessen moechte ebenfalls. Das nennt sich dann Programmier-
+   freiheit.
+
+3) Was ist mit Objekten, die keine Waffe/Ruestung sind?
+
+   Hier unterliegen alle Objekte unserer Begutachtung, die irgend-
+   wie eine kampfrelevante Bedeutung haben. Dazu zaehlt:
+
+   - Schaden verursachen koennen (Saeurekugeln)
+   - Paralysetools (graue Steine)
+   - Begleit-Npc (Einhorn, Feuerelementar)
+   - Informationen ueber eingehende Schadenstypen geben
+   - Waffen/Ruestungen manipulieren koennen (Geisterhauch, Blutstropfen)
+   - Informationen ueber Npc, Raeume, Reset-Zeiten usw. ausgeben
+   - Werte manipulieren (Attribute oder Geschwindigkeit erhoehen)
+
+   Ein Sonderfall stellen hier Objekte dar, die vom Typ AT_MISC oder
+   WT_MISC sind. Fuer diese gilt:
+
+   - Sie duerfen keinerlei positive Veraenderungen im Spieler vor-
+     nehmen
+   - Sie duerfen keinerlei kampfrelevante Bedeutung haben (im Sinne 
+     von Spell-/Skill-/Gegner-Manipulation, Attributsveraenderungen, 
+     Heilungen, Informationen u.a.) oder sonstwie einen Kampf be-
+     einflussen
+   - Sie duerfen ueber keine Hit- oder DefendFunc verfuegen. Die AC 
+     bzw. WC solcher Objekte ist immer 0
+
+4) Worauf ist besonders zu achten?
+
+   Die Grenzwerte bei Waffen und Ruestungen sollen den fuer diesen 
+   Typ geltenden Wert nur im seltensten Fall ueberschreiten. Dies
+   gilt auch fuer Werte einer Defend- oder HitFunc.
+
+   Boni aus einer Defend- oder HitFunc sind IMMER per random() zu
+   geben!
+
+   Besonders gute Objekte haben ueber Restriktionen zu verfuegen.
+
+   Besonders gute Objekte sollten auch Nachteile haben.
+
diff --git a/doc/balance/begleitnpcs b/doc/balance/begleitnpcs
new file mode 100644
index 0000000..d8554a5
--- /dev/null
+++ b/doc/balance/begleitnpcs
@@ -0,0 +1,22 @@
+BEGLEITNPCS
+-----------
+
+Manche NPCs unterstuetzen den Spieler im Kampf und laufen mit ihm mit.
+Als Regeln fuer diese NPCs hat die Balance folgende Regeln aufgestellt:
+
+a. NPCs _muessen_ sich im Spieler registrieren.
+b. Man darf nur einen Kampfhelfer zur gleichen Zeit beschwoeren, ausnahmen
+koennen statische NPCs nach Absprache sein.
+c. Kein Kampfhelfer darf eine staerkere Resistenz als -0.5 haben
+d. Ist ein Kampfhelfer in einer bestimmten Kathegorie (Angriff, verteidigung
+...) deutlich besser als Durchschnittliche Gilden-NPCs, muss er entsprechende
+Nachteile mitbringen. Denkbar waeren z.B.: Kurze Haltbarkeit, teuer in der
+Anschaffung, teuer im Unterhalt, kann nicht betankt/geheilt/bewegt werden, hat
+nur wenig LP, grosse Anfaelligkeiten usw.
+e. Im Normalfall sollten Begleit-NPCs nicht in Teams eintreten. Eine Ausnahme
+koennte z.B. ein Bogenschuetze, der sich in die zweite Reihe stellt oder etwas
+in der Art sein. 
+
+Zum Registrieren der NPCs siehe RegisterHelperNPC und UnregisterHelperNPC.
+
+2011-02-22, Humni
diff --git a/doc/balance/fernwaffen b/doc/balance/fernwaffen
new file mode 100644
index 0000000..e070984
--- /dev/null
+++ b/doc/balance/fernwaffen
@@ -0,0 +1,94 @@
+Mit Fernwaffen sind uebrigens ausnahmslos Waffen gemeint, die Munition 
+benoetigen, nicht etwa Speere oder dergleichen. Alle Fernwaffen sind 
+ausnahmslos genehmigungspflichtig, das gilt auch fuer jegliche Munition. 
+     
+Properties in Fernwaffen und ihre Bedeutung:
+
+     P_WC
+        Vorsicht mit dieser Property. Bei Fernwaffen gibt sie _nur_ den
+        Schaden bei Zweckentfremdung der Waffe als Knueppel an. 
+        Dementsprechend ist dieser Wert extrem niedrig zu halten.
+        Standardwert ist hier 30, der auch nur in Ausnahmefaellen 
+        ueberschritten werden sollte. Kaum eine Armbrust oder ein Bogen 
+        taugt nunmal als grossartige Nahkampfwaffe.
+
+     P_QUALITY
+        Wird nur beim Nahkampf mit der Waffe beachtet. Standardmaessig auf
+        100 gesetzt, da Boegen und Armbrueste leicht beschaedigt werden,
+        wenn man damit auf jemanden einpruegelt.
+
+     P_HIT_FUNC 
+        HitFunc fuer den _Nahkampf_. Hat beim Schuss mit der Waffe keinen
+        Einfluss. 
+                
+     P_NR_HANDS
+        Boegen und Armbrueste sind in jedem Fall zweihaendig. Einhaendige
+        Fernwaffen sind aber denkbar (Schlingen zum Schleudern kleiner 
+        Steine z.B.)
+        
+     P_SHOOTING_WC
+        Die Basis-Waffenklasse der Fernwaffe. Zu ihr wird die Waffenklasse
+        der Munition addiert, um den endgueltigen Angriffswert beim Schuss
+        zu berechnen.
+     
+     P_RANGE 
+        Reichweite der Waffe in Metern. Wichtig, wenn aus Containern 
+        (Wachtuermen, Schiffen, etc.) nach aussen geschossen wird.
+        Damit das funktioniert, muss dieser Wert hoeher sein als der
+        im Container definierte (steht dort in der P_SHOOTING_AREA).
+    
+     P_STRETCH_TIME
+        Anzahl der Runden, die zum Laden/Spannen der Waffe benoetigt
+        werden. 1 ist hier der Standardwert, das bedeutet, es kann jede
+        Runde geschossen werden.
+     
+     P_AMMUNITION
+        Benoetigter Munitionstyp. Hier ist eine der moeglichen Konstanten
+        (MUN_*) einzusetzen (z.B. MUN_ARROW fuer Boegen).
+     
+     P_NOGET
+        Hat bei Fernwaffen eine zusaetzliche Bedeutung. Wenn gesetzt, muss
+        die Waffen nicht gezueckt werden, um sie abfeuern zu koennen. Das
+        ist z.B. fuer Katapulte gedacht, die im Raum stehen.
+        
+     Fuer die Munition gibt es kein Standardobjekt. Wichtig ist nur, dass 
+     die entsprechenden Properties gesetzt sind. Normalerweise sollte die 
+     Munition natuerlich eine Unit sein, aber auch Einzelobjekte (ein
+     besonderer Pfeil oder ein grosser Stein fuer ein Katapult) sind 
+     moeglich.
+
+Properties fuer die Munition sind:
+     
+     P_SHOOTING_WC
+        Die Waffenklasse der Munition. Wird zur WC der Waffe addiert.
+        
+     P_DAM_TYPE
+        Schadenstyp der Munition. Sollte normalerweise DT_PIERCE fuer 
+        Pfeile aller Art und DT_BLUDGEON fuer stumpfe Munition wie Steine
+        sein. Magische Schadensarten sind aber natuerlich moeglich.
+        
+     P_HIT_FUNC
+        HitFunc, die beim Schuss mit der Munition benutzt wird.
+        
+     Ausserdem muss in der Munition mittels AddId() der entsprechende
+     Munitionstyp gesetzt werden, z.B. MUN_ARROW.
+
+Genehmigungsgrenzen:
+-------------------
+     Alle Waffen dieser Art sind grundsaetzlich genehmigungspflichtig.
+     Folgende Werte sollten allerdings Obergrenzen darstellen, die
+     im Normalfall nicht zu Ueberschreiten sind:
+     
+     Waffe kann jede Runde abgefeuert werden:  P_SHOOTING_WC 100
+     Waffe braucht eine Ladezeit            :  P_SHOOTING_WC 130
+     
+     Die Obergrenze fuer Munition liegt bei :  P_SHOOTING_WC 60.
+
+     Vorsichtig mit P_SHOOTING_AREA in Raeumen/Containern. 
+
+     Bisher ist dieses Schiessen von Raum zu Raum weitestgehend ungetestet,
+     und es ist nicht klar, welche Probleme das verursachen kann. Wenn 
+     eventuelle Ziele keine Moeglichkeit haben, sich zu wehren oder 
+     wegzulaufen, ist schnell jegliche Balance dahin. Die Regionsmagier
+     haben bei Abnahme von Gebieten darauf zu achten, dass diese Property
+     nur in wenigen, gut begruendeten Raeumen gesetzt wird.
diff --git a/doc/balance/grenzwerte b/doc/balance/grenzwerte
new file mode 100644
index 0000000..f2fc1a6
--- /dev/null
+++ b/doc/balance/grenzwerte
@@ -0,0 +1,58 @@
+ GRENZWERTE
+
+    Waffen:
+   
+      Einhaendige Waffen  :    ab P_WC >= 140
+      Zweihaendige Waffen :    ab P_WC >= 175
+
+      Fuer Waffen die einen nichtphysikalischen Schadenstyp enthalten:
+
+      Einhaendige Waffen  :    ab P_WC >= 120
+      Zweihaendige Waffen :    ab P_WC >= 150
+
+      Genehmigungspflichtig sind auch generell Waffen, die als Schadens-
+      typ DT_TERROR, DT_SOUND, DT_AIR oder DT_SQUEEZE enthalten, unabhaengig
+      von der WC.
+
+      Der Schadenstyp DT_EXPLOSION wird nicht mehr genehmigt.
+
+      Alle Waffen mit HitFunc oder entsprechender Funktion muessen genehmigt
+      werden (Es sei denn diese wirken nur bei !interactive() oder erhoehen 
+      den Schaden nicht).
+
+    Ruestungen:
+
+      +--------------+--------+----------------------------+
+      | Ruestungstyp | MAX_AC | Genehmigungsgrenze (inkl.) |
+      +--------------+--------+----------------------------+
+      | AT_AMULET    |    2   |             --             |
+      | AT_ARMOUR    |   50   |             38             |
+      | AT_BELT      |    2   |             --             |
+      | AT_BOOT      |    6   |              5             |
+      | AT_CLOAK     |   10   |              8             |
+      | AT_GLOVE     |    5   |              5             |
+      | AT_HELMET    |   15   |             13             |
+      | AT_QUIVER    |    0   |             --             |
+      | AT_RING      |    2   |             --             |
+      | AT_SHIELD    |   40   |             28             |
+      | AT_TROUSERS  |   15   |             13             |
+      | AT_MISC      |    0   |             --             |
+      +--------------+--------+----------------------------+
+
+    Ruestungen mit DefendFunc o.ae. muessen generell genehmigt werden 
+    (siehe Waffen).
+
+    Ruestungen und Waffen vom Typ AT_MISC oder WT_MISC duerfen keiner-
+    lei positive Veraenderungen im Spieler hervorrufen, keine Stats
+    veraendern oder sonstwie kampfrelevante Bedeutung haben. Die WC/AC
+    solcher Objekte ist immer 0 und wird auch nicht anders genehmigt.
+
+ SIEHE AUCH
+
+     balance, ruestungen, fernwaffen, uniques, npcs, schadenstypen
+     attributsveraenderungen, resistenzen, kampfobjekte
+
+
+------------------------------------------------------------------------------
+ LETZTE AeNDERUNG:
+    Don, 03.01.02, 12:00:00 von Tilly
diff --git a/doc/balance/kaempferproperties b/doc/balance/kaempferproperties
new file mode 100644
index 0000000..a7ed271
--- /dev/null
+++ b/doc/balance/kaempferproperties
@@ -0,0 +1,9 @@
+Properties in Waffen fuer Trves und ihre Hoechstwerte:
+-----------------------------------------------------
+
+K_THROWING_WEAPON         Waffenwurf            ==> Max:  50
+K_BRAWLING_WC             Waffenschlag          ==> Max:  30
+K_WEAPON_SHATTER          Waffenbruch           ==> Max:  50
+K_DISTRACTING_WEAPON      Finte / Waffentrick   ==> Max:  50
+K_CRITICAL_HIT            Todesstoss            ==> Max: 100
+
diff --git a/doc/balance/kaempferwaffen b/doc/balance/kaempferwaffen
new file mode 100644
index 0000000..0666316
--- /dev/null
+++ b/doc/balance/kaempferwaffen
@@ -0,0 +1,143 @@
+
+      Spezial-Waffen fuer die Kaempfergilde
+      -------------------------------------
+                                   
+Fuer die Waffen bzw. Ruestungen muss /p/kaempfer/kampf.h includet werden.
+  
+  
+
+Zuerst einmal was ueber den Weapon-Type. Zum hundertesten male der Hinweis,
+dass es nicht nur Schwerter gibt! Obwohl die Kaempfer jede beliebige
+Waffe benutzen koennen, gibt es doch, je nach Rasse, sehr grosse Unter-
+schiede. Nur um ein Beispiel zu nenne:
+Ein Hobbit, der alle Werte auf 100% hat, hat mit dem Knochenschaelmesser
+(WC 145) einen hoeheren Angriffswert als mit einer Lanze (WC 200)!!!
+
+
+1) Parierwaffen
+
+  Parierwaffen eigenen sich vor allem zum Parieren, wie z.B. der Sai.
+  Da man mit solchen Waffen oft auch die gegnerische Waffe festhalten
+  kann, gilt der Bonus auch fuer den Block. Parierwaffen haben meistens
+  eine nicht so hohe WC.
+  Es wird die Property P_EFFECTIVE_AC gesetzt.
+  Normale Waffen, die zusaetzlich zum Parieren geeignet sind:
+    P_EFFECTIVE_AC: 1 - 15
+  Reine Parierwaffen mit niedriger WC:
+    P_EFFECTIVE_AC: 20 - 30
+  
+2) Wurfwaffen
+
+  Wurfwaffen sind speciell ausbalanziert, besitzen meist keinen richtigen
+  Griff etc. Aus diesem Grunde sollte ihre WC nicht zu hoch sein.
+  Es wird K_THROWING_WEAPON gesetzt. Der Wert sollte zwischen 1-50 
+  liegen, wobei wirklich nur sehr gute Wurfwaffen mit niedrigerer
+  WC Werte zwischen 35 - 50 haben sollten.
+  
+3) Waffen mit ungewoehnichen Angriffsmoeglichkeiten
+
+  Man kann an Waffen auch an ungewoehnlicher Stelle zusaetzliche
+  Angriffsmoeglichkeiten anbauen. So koennte z.B. die Parierstange
+  bei einem Schwert aus Klingen bestehen, oder das Ende des Stiels
+  bei einer Axt in einem Dorn enden. Beim Waffenschlag wird mit
+  diesen Extrawaffen gearbeitet. Diese Besonderheiten (Klinge am 
+  Pommel des Griffs, etc) sollten natuerlich auch in P_LONG
+  erwaehnt werden, schliesslich sind sie ja mit blossem Auge
+  zu erkennen.
+  Property: K_BRAWLING_WC mit Werten zwischen 1 - 30
+  Weiterhin besteht die Moeglichkeit dieser zusaetzlichen Angriffs-
+  moeglichkeit einen eigenen Schadenstyp zu geben. Dazu setzt man
+  die Property K_BRAWLING_DT (analog zu P_DAM_TYPE).
+
+4) Waffen, die andere Waffen zerstoeren sollen
+  
+  Als Kaempfer kann man versuchen, die Waffe des Gegners zu beschaedigen.
+  Dafuer eignen sich natuerlich besonders schwere Waffen, vor allem
+  z.B. Haemmer. Man koennte natuerlich Waffen konstruieren, die besonders
+  dafuer geeignet sind. Dafuer wird K_WEAPON_SHATTER gesetzt.
+  Der Wert sollte zwischen 1 - 50 liegen.
+  
+5) Waffen, die besonders gut fuer den KO-Schlag sind
+  
+  Der Kaempfer kann versuchen einen Gegner zu betaeuben. Dies geschied
+  in der Regel durch einen Schlag auf den Kopf. Je hoeher das Gewicht
+  der Waffe, desto besser. Es gibt natuerlich Waffen, die dafuer besonders
+  geeignet sind (z.B Todstschlaeger). Dann wird K_KO als Property gesetzt.
+  Der Wert sollte zwischen 1 - 50 liegen. 
+ 
+6) Waffen, die den Gegner besonders gut ablenken koennen
+  
+  Es gibt natuerlich Waffen, die den Gegner leicht ablenken koennen.
+  Dies ist fuer Finten und den Waffentrick sehr nuetzlich. Eine solche
+  Waffe ist z.B. der singende Speer, der den Gegner (und den Traeger)
+  fuerchterlich nervt. Gesetzt wird K_DISTRACTING_WEAPON, wobei
+  der Wert zwischen 1 - 50 liegen sollte.
+ 
+7) Waffen, die beim Todesstoss besonders effektiv sind
+
+  Man kann sich Waffen vorstellen, die speziell fuer den Todesstoss
+  geeignet sind. Vor allem magische Waffen, die z.B. die Seele aus dem
+  geschwaechten Koerper des Gegners reissen, sind vorstellbar. Oder
+  Waffen mit besonders langen, spitzen und duennen Klingen, die sich
+  tief in den Gegner bohren. Es wird K_CRITICAL_HIT gesetzt, der
+  Wert liegt zwischen 1 und 100, wobei 100 eine Verdopplung des 
+  Schadens ergibt!!! Also wirklich sehr vorsichtig verwenden!!!!!
+
+
+8) Schilde fuer den Schildstoss
+
+  Ist ein Schild extra fuer den Schildstoss angefertigt (hat es z.B. einen
+  Dorn montiert, mit dem man gefaehrlich den Gegner rammen kann) so
+  kann im Schild P_EFFECTIVE_WC gesetzt werden. Ausserdem ist es moeglich
+  einen bestimmten Schadenstyp mit P_DAM_TYPE anzugeben (Feuerschild).
+                                      
+9) Special-Ruestungen
+
+  Folgende Ruestungstypen koennen fuer die Kaempfer modifiziert werden:
+  AT_TROUSERS; AT_HELMET; AT_ARMOUR; AT_BOOT. Dabei sind drei verschiedene
+  Sachen moeglich. 
+  a) P_EFFECTIVE_AC wird gesetzt, wenn die Ruestung durch besondere
+     DefendFunc's einen hoeheren Schutz als die AC macht
+  b) Die Ruestung soll auch als Waffe zu verwenden sein (Stiefel beim
+     Kampftritt, Hose beim Kniestoss, Helm beim Kopfstoss und Ruestung
+     beim Ellbogenschlag). z.B. waeren Dornen an den Stiefeln denkbar.
+     Dadurch werden die Stiefel ja nicht als Schutz besser, aber als
+     Waffe. Deshalb P_EFFECTIVE_WC setzen. Den Wert nicht hoeher als
+     doppelte AC.
+     [Boing:]
+     Wenn man der Ruestung wirklich einen Attackebonus geben moechte,
+     dann muss P_EFFECTIVE_WC hoeher sein, als P_AC der Ruestung!
+     Da im Falle, wenn P_EFFECTIVE_WC nicht gesetzt ist, P_AC als
+     Angriffswert genommen wird, waere das ansonsten eine Schwaechung!
+     Das kann man natuerlich gezielt einsetzen, wenn man eine Ruestung
+     machen will, die sehr gut schuetzt, aber nur geringe Kaempferboni
+     aufweist. 
+     Beispiel: 
+        Ruestung mit ac 40 und keiner eff_wc: angriffswert ist 40
+        Ruestung mit ac 40 und eff_wc 20:     angriffswert ist 20
+        Ruestung mit ac 40 und eff_wec 60:    angriffswert ist 60
+  c) Die Ruestung macht einen besonderen Schadenstyp. z.B. Flammenstiefel,
+     die beim Kampftritt DT_FIRE und DT_BLUDGEON machen, oder Hosen
+     mit Spikes an den Knien -> DT_PIERCE. Es sollte immer ein
+     physikalischer Schaden vorhanden sein. Zusaetzlich waere jeder
+     magische Schaden denkbar, der Phantasie sind keine Grenzen gesetzt.
+     Natuerlich sollten solche Specialsachen auch in P_LONG der Ruestung
+     auftauchen. Die Kaempfer haben aber die Moeglichkeit Ruestungen
+     einzuschaetzen und sowas zu entdecken.
+     
+
+
+  Bitte alle fuer die Kaempfer geaenderten Waffen und Ruestungen
+  bei mir absegnen lassen, ich moechte den Ueberblick nicht verlieren :))
+  Ausserdem sollten die Werte nicht gleich zu hoch gewaehlt werden.
+  Die Maximalwerte sollten so gut wie nie vergeben werden. 
+  Vorsicht!!! Werden die Werte ueber die Grenzen erhoeht, wird ein 
+  Kaempfer mit so einer Waffe viel zu stark. Bis jetzt hab ich noch keine
+  Sicherheitsfunktion eingebaut, was sich aber aendern wird, wenn 
+  es jemand uebertreibt :))
+  [Boing:] Leider wurde es uebertrieben, also ist jetzt eine Sicherheits-
+  funktion drin.
+
+
+Ciao Zardoz
+     
diff --git a/doc/balance/kampfobjekte b/doc/balance/kampfobjekte
new file mode 100644
index 0000000..e31793d
--- /dev/null
+++ b/doc/balance/kampfobjekte
@@ -0,0 +1,31 @@
+    Hierzu zaehlen alle Sachen, die irgendeinen Einfluss auf den Kampf nehmen
+    und weder `/std/weapon.c' noch `/std/armour.c' sind. Also die sogenannte
+    Artillerie, Eisstab und artverwandtes, Wurfsterne, Parasteine, Bumi und
+    unterstuetzende NPC.
+
+    Prinzipiell sind alle diese Sachen genehmigungspflichtig.
+    
+    Auto-Selbstfahrer, also Sachen, die ohne Eingriff des Spielers agieren,
+    sind unerwuenscht und sollten vermieden werden. Sorry fuer die
+    Laggeplagten...
+
+    Kampfobjekte sollten P_ATTACK_BUSY setzen und auch vor der Anwendung
+    abfragen, damit sie nicht unbegrenzt hintereinander genutzt werden
+    koennen. Magische Sachen sollten entweder aufgeladen werden muessen oder
+    ihre magische Energie verlieren, damit sie nicht unendlich haltbar sind.
+    Ausserdem hat Magie normalerweise auch unerwuenschte Nebenwirkungen. Die
+    Spieler sollen die moeglichen Vorteile gegen Nachteile oder Nebenwirkungen
+    des Gegenstands abwaegen. Auch bei diesen Objekten *muss* P_INFO gesetzt
+    werden, um eine Begruendung fuer die besondere Wirkung und z.B. einen
+    Hinweis auf die Benutzung zu liefern.
+    
+    Sie sollten keinesfalls kaeuflich erworben werden koennen.
+
+    Objekte, die Auskunft ueber ein- und/oder ausgehende Schaeden geben, 
+    sind genehmigungspflichtig. Solche Objekte sollten nach Moeglichkeit
+    eine Waffe oder Kleidungsstueck - dies jedoch nicht AT_MISC - sein.
+    Dass solche Objekte auch ueber Luecken in der Angabe der Schaeden und
+    Nachteile verfuegen sollten, ist eigentlich klar. 
+    Solche Nachteile liessen sich ueber eine Mindest-Schadenshoehe oder
+    random realisieren. Informationen ueber mehrere Schadenstypen durch 
+    ein und das selbe Objekt sind nicht erwuenscht.
diff --git a/doc/balance/modifyskillattribute b/doc/balance/modifyskillattribute
new file mode 100644
index 0000000..e072791
--- /dev/null
+++ b/doc/balance/modifyskillattribute
@@ -0,0 +1,100 @@
+ModifySkillAttribute()
+
+FUNKTION:
+    public int ModifySkillAttribute(string atrname, mixed value, 
+                                    int duration)
+
+DEFINIERT IN:
+    /std/living/skill_attributes.c
+
+ARGUMENTE:
+    <atrname>   string
+                Name des zu veraendernden Attributes
+                (Definiert in /sys/living/skill_attributes.h)
+
+    <value>     int oder closure
+                Wert des Modifikators
+                oder
+                eine Closure, welche bei Abfrage des betreffenden SAs
+                abgefragt um den Modifikator zu bestimmen.
+
+    <duration>  int
+                Dauer in Sekunden
+
+BESCHREIBUNG:
+    Aendert temporaer, d.h. fuer eine bestimmte Zeit, ein Skill-Attribut eines
+    Lebewesen, indem ein Modifikator hinzugefuegt wird.
+    
+    Der Standardwert eines SA wird von P_SKILL_ATTRIBUTE_OFFSETS festgelegt
+    oder ist 100, wenn besagte Property leer ist.
+    Alle Modifikatoren (negativ wie positiv) werden addiert und bilden
+    zusammen mit dem Standardwert eine Gesamtsumme.
+    Bei allen SAs ausser SA_QUALITY wird diese Gesamtsumme noch mit
+    SA_QUALITY (welches sich damit auf alle anderen Skill-Attribute auswirkt)
+    multipliziert und das Ergebnis stellt den Endwert des SA dar.
+    (Beispiel s.u.)
+
+    Der Wert eines Modifikators muss zwischen -1000 und 1000 liegen. Der
+    Gesamtwert eines SA kann 10 nicht unter- und 1000 nicht ueberschreiten.
+
+    Falle <value> eine Closure ist, wird diese Closure jedesmal ausgefuehrt,
+    wenn das entsprechende SA abgefragt wird. Der Rueckgabewert dieser Closure
+    stellt dann den Wert des Modifikators dar. Auch dieser muss zwischen -1000
+    und 1000 liegen. Gibt die Closure keinen int zurueck, wird der Modifikator
+    geloescht.
+
+    Gueltige Skill-Attribute sind momentan:
+    * SA_QUALITY:  Allgemeine Qualitaet
+    * SA_DAMAGE:   Schaden, den das Lebewesen macht
+    * SA_SPEED:    Geschwindigkeit des Lebewesens
+    * SA_DURATION: Spell-/Skilldauer
+    * SA_ENEMY_SAVE: identisch zu SA_SPELL_PENETRATION (OBSOLET!)
+    * SA_SPELL_PENETRATION: Chance des _Casters_, einen Spell durch ein
+                            P_NOMAGIC durchzukriegen.
+    * SA_RANGE:     Reichweite des Lebewesens (eher unbenutzt)
+    * SA_EXTENSION:  "Ausdehnung" (unbenutzt)
+
+RUECKGABEWERT:
+    SA_MOD_OK              wenn der Modifikator gesetzt wurde
+    SA_TOO_MANY_MODS       wenn die max. Anzahl an Mods schon erreicht ist.
+    SA_MOD_TOO_SMALL       wenn der Mod zu klein ist
+    SA_MOD_TOO_BIG         wenn der Mod zu gross ist
+    SA_MOD_INVALID_ATTR    wenn das gewuenschte SA gar nicht existiert
+    SA_MOD_INVALID_OBJECT  wenn das setzende Objekt ungueltig ist
+    Wenn man nur wissen will, ob die Operation erfolgreich war, empfiehlt es
+    sich, auf == SA_MOD_OK zu pruefen.
+
+BEMERKUNGEN:
+    Nachdem ein Objekt, welches Modifikatoren setzte, zerstoert wurde, werden
+    die Modifikatoren spaetestens ungueltig, sobald in dem manipulierten
+    Lebewesen erneut ModifySkillAttribute() gerufen wird! Bei Closures ist der
+    Mod sofort weg.
+
+BEISPIELE:
+    // sei PL ein Spieler, den mein NPC schwaechen will:
+    PL->ModifySkillAttribute(SA_QUALITY, -75, 13);
+    // Fuer 13s wird SA_QUALITY um 75 reduziert. Dies wirkt sich auf alle
+    // anderen SAs aus! (s. drittes Beispiel)
+
+    // sei PL ein Lebewesen, welchem ich fuer 11s 2 Schlaege pro Kampfrunde
+    // zusaetzlich geben moechte:
+    PL->ModifySkillAttribute(SA_SPEED, 200, 11);
+    // wenn keine weiteres Modifikatoren wirken, hat PL jetzt 3 Schlaege pro
+    // Kampfrunde (Basiswert 100 + 200 == 300 => 3).
+
+    Angenommen, ein Lebewesen hat einen Basiswert von 130 auf SA_SPEED und 100
+    auf SA_QUALITY (P_SKILL_ATTRIBUTE_OFFSETS) und nun 3 Modifikatoren
+    gesetzt: SA_SPEED +100, SA_SPEED -30 und SA_QUALITY von -10:
+    Zunaechst wird SA_QUALITY bestimmt: 100 - 10 = 90  => 0.9
+    Anschliessend wird SA_SPEED bestimmt: 130 + 100 - 30 = 200 => 2
+    Nun wird SA_SPEED noch mit SA_QUALITY multipliziert: 2 * 0.9 = 1.8
+    Das Lebewesen hat nun also im Endeffekt 1.8 Schlaege pro Kampfrunde.
+
+    
+SIEHE AUCH:
+    P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS,
+    QuerySkillAttribute(), QuerySkillAttributeModifier(),
+    RemoveSkillAttributeModifier()
+
+-----------------------------------------------------------------------------
+07.08.2008, Zesstra
diff --git a/doc/balance/multiobjekte b/doc/balance/multiobjekte
new file mode 100644
index 0000000..46086a5
--- /dev/null
+++ b/doc/balance/multiobjekte
@@ -0,0 +1,47 @@
+MULTIOBJEKTE:
+
+	a. Was sind Multiobjekte?
+
+	Multiobjekte sind Multiwaffen oder Multiruestungen.
+
+	b. Multiwaffen
+
+	Multiwaffen sind Waffen, die mehrere Funktionen haben koennen. Das 
+	klassische Beispiel sind Waffen, die mehrere Schadensarten verursachen
+	koennen, und diese z.B. auf Kommando wechseln koennen. Oder auch die
+	Waffengattung.
+
+	Ebenfalls als Multiwaffen werden Waffen behandelt, bei denen im 
+	geringen Umkreis um eine Fundstelle viele aehnliche Waffen auffindbar
+	sind, die sich z.B. nur in der Gattung unterscheiden (ein 
+	Quetschschwert, eine Quetschkeule, ein Quetschstab, ein Quetschspeer..
+	oder : Ein Feuerschwert, ein Eisschwert, ein Saeureschwert, ein
+	Blitzschwert, ein Wasserschwert...)
+
+	c. Multiruestungen
+
+	Multiruestungen sind analog Ruestugnen, die gegen bestimmte Schaeden
+	schuetzen und entweder selbst in Schadensart wandelbar sind, oder wo
+	es viele gleichartige in einem geringen Umkreis gibt. (Blitzhelm,
+	Blitzhose, Blitzpanzer, Blitzring... oder Feuerring, Wasserring,
+	Blitzring, Eisring...). Analoges gilt fuer die Schadensart der
+	Ruestung fuer die Kaempfergilde.
+
+	d. Regel
+
+	Multiobjekte duerfen in einem geringen Umkreis nur in maximal 4 
+	Varianten vorkommen oder von/fuer Spieler herstellbar sein.
+
+	Diese Regelung ist schwammig, aber sie soll verhindern, dass es ein
+	universelles Ausruestungsgebiet gibt.
+
+	Was ein geringer Umkreis ist, ob Waffen oder Ruestungen als
+	Multiobjekte gelten, ob die Tatbestaende also fuer den konkreten
+	Programmierungssachverhalt gelten, entscheidet die Balance nach
+	Auslegung und evtl. Regelfortbildung dem Sinn bzw. der Historie
+	dieser Regel nach.
+
+	Als Ansprechpartner fuer diese Regel steht die Balance zur
+	Verfuegung.
+
+Letzte Aenderung: Humni, 2011-01-15
diff --git a/doc/balance/parierwaffen b/doc/balance/parierwaffen
new file mode 100644
index 0000000..d7e38b4
--- /dev/null
+++ b/doc/balance/parierwaffen
@@ -0,0 +1,7 @@
+Aktuelle Grenzwerte:
+====================
+
+  Parier-Typ   Genehmigungsgrenze
+  ------------ --------------------
+  PARRY_ONLY   Wie Ruestungen vom Typ AT_SHIELD
+  PARRY_TOO    Generelle Genehmigungspflicht
diff --git a/doc/balance/properties b/doc/balance/properties
new file mode 100644
index 0000000..e849ab6
--- /dev/null
+++ b/doc/balance/properties
@@ -0,0 +1,194 @@
+P_AC                  : ArmourClass, Wert der den Schutz einer Ruestung 
+                        (P_ARMOURS,P_ARMOUR_TYPE) angibt
+
+P_ATTACK_BUSY         : Anzahl der moeglichen Aktionen eines Lebewesens pro
+                        Kampfrunde (1 HeartBeat). Bei Spielern ist dies 1, 
+                        bei Sehern max. 5. Wegen des hoeheren Wertes bei
+                        Sehern ist darauf zu achten, das hier ADDIERT wird
+                        und nicht einfach nur auf 1 gesetzt.
+                        Siehe auch: bman kampfobjekte
+
+P_ATTRIBUTES          : Attribute eines Spielers, koennen durch Objekte,
+                        Flueche und/oder Krankheiten manipuliert werden.
+                        Siehe auch: bman attribute
+
+P_ATTRIBUTES_MODIFIER : Hier werden Attribut-Modifier gespeichert, die 
+                        ueber einen laengeren Zeitraum wirken sollen.
+                        Bekannteste Beispiele sind wohl der Todes-Malus
+                        und der Frosch-Malus. Normale Aenderungen sollten
+                        per P_X_ATTR_MOD oder P_M_ATTR_MOD realisiert 
+                        werden
+                        Siehe auch: bman attribute
+
+P_COMBATCMDS          : Beinhaltet Befehle, Schadenshoehe und -arten in 
+                        Kampfobjekten, damit diese auch von NPC benutzt
+                        werden koennen.
+                        Siehe auch: bman kampfobjekte
+
+P_CURSED              : Objekte in denen hier ein String oder Int-Wert ge-
+                        setzt ist, koennen nicht abgelegt werden. Kleriker
+                        haben die Faehigkeit, zu entfluchen
+
+P_DAMAGED             : Grad der Beschaedigung einer Waffe oder Ruestung.
+                        Wird meistens per waffe->Damage(x) realisiert. Kann
+                        bei div. Schmieden wieder auf 0 gesetzt werden, Trves
+                        koennen es durch Waffenschaerfen sogar selbst
+
+P_DAM_TYPE            : Schadenstyp, den eine Ruestung oder Waffe macht
+                        Siehe auch: bman schadenstypen
+
+P_DEFEND_FUNC         : Gesetzt wenn eine Ruestung eine DefendFunc definiert
+                        hat. Werte einer DefendFunc sind IMMER per random
+                        zu geben, Ausnahme: negativer Rueckgabewert. Auch
+                        hier ist darauf zu achten, das P_WC + Rueckgabewert
+                        nicht ueber den Grenzwert kommen
+                        Siehe auch: bman grenzwerte, bman ruestungen
+
+P_DISABLE_ATTACK      : Gesetzt wenn das Lebewesen ausnahmsweise mal nicht
+                        angreifen kann. Es wird die Anzahl der Kampfrunden
+                        (also HeartBeats) gesetzt, in denen die Paralyse
+                        wirkt. Neue Paralyse-Tools sind nicht gern gesehen
+                        und haben wenig Chancen auf Genehmigung
+
+P_EFFECTIVE_AC        : Die effektive ArmourClass einer Ruestung. Ist in
+                        einer Ruestung noch eine P_DEFEND_FUNC definiert,
+                        so betraegt ist P_EFFECTIVE_AC = P_AC + 2 * Mittelwert
+                        des dortigen Rueckgabewertes
+
+P_EFFECTIVE_WC        : Die effektive WeaponClass einer Waffe oder Ruestung.
+                        Ist in einer Waffe eine P_HIT_FUNC definiert, so be-
+                        rechnet sich die P_EFFECTIVE aus der Summe von P_WC
+                        und dem doppelten Mittelwert des dortigen Rueckgabe-
+			wertes
+
+P_HANDS               : Enthaelt mehrere Eintraege, die Meldung, Staerke
+                        eines Angriffes mit blossen Haenden und den oder
+                        die dabei verursachten Schadenstypen beinhalten
+
+P_HIT_FUNC            : Gesetzt, wenn eine Waffe oder Ruestung eine HitFunc
+                        definiert hat. Die Werte muessen IMMER per random
+                        gegeben werden. Auch hier sind einzig negative Rueck-
+                        gabewerte die Ausnahme. Darauf zu achten ist, das 
+                        P_AC + Rueckgabewert nicht den Grenzwert ueber-
+                        schreiten
+                        Siehe auch: bman grenzwerte
+
+P_HP                  : Aktuelle Lebenspunkte (HealthPoints) eines Lebe-
+                        wesens
+
+P_HP_DELAY            : Numerischer Wert, der angibt, wieviele HeartBeats
+                        notwendig sind, damit sich die P_HP um 1 regenieren
+
+P_HP_HOOKS            : Was fuer Objekte sollen bei einer Aenderung der P_HP
+                        benachrichtigt werden? Bekanntestes Beispiel ist 
+                        hier sicher der Teddy aus der Nibelungenquest
+
+P_LEP                 : LEvelPoints, absolute Stufenpunkte eines Spielers.
+                        Ist manchmal sinnvoller, P_LEP abzufragen anstatt
+                        P_LEVEL
+
+P_M_ATTR_MOD          : Mapping mit Attributen die veraendert werden, wenn
+                        das Lebewesen diese RUESTUNG oder WAFFE traegt oder
+                        zueckt. Jedes Attribut kann nur durch einen Modifier
+                        beeinflusst werden. Ist also ein Attribut blockiert,
+                        kann die Ruestung/Waffe nicht angezogen/gezueckt
+                        werden. Fuer Krankheiten usw. gibt es P_X_ATTR_MOD
+                        Siehe auch: bman attribute
+
+P_M_HEALTH_MOD        : Mapping mit dem die max. P_HP und max. P_SP eines
+                        Lebewesens manipuliert werden koennen, wenn es diese
+                        RUESTUNG oder WAFFE traegt oder zueckt. Es erfolgt
+                        keinerlei Blockade. Fuer Krankheiten usw. gibt es 
+                        P_X_HEALTH_MOD
+
+P_NOMAGIC             : Wert zwischen 0 (default) und 100, der den Grad an-
+                        gibt, mit welcher Wahrscheinlichkeit Magie keine
+                        Wirkung erzielt. Kann in Raeumen, Npc und Objekten
+                        gesetzt werden. Von Spieler nicht gern gesehen :)
+
+P_NR_HANDS            : Wieviel freie Haende sind notwendig, um diese Waffe
+                        oder diesen Schild zu zuecken?
+ 
+P_PARRY_WEAPON        : In Waffen gesetzt wenn es sich um eine Parierwaffe
+                        handelt
+                        Siehe auch: bman parierwaffen
+
+P_PURSUERS            : Beinhaltet alle Objekte/Npc die den Spieler momentan
+                        verfolgen
+
+P_QUALITY             : Beliebiger Wert in Waffen. Setzt man ihn auf n, so wird
+                        die Waffe bei jedem n-ten Schlag um 1 Punkt be-
+                        beschaedigt (P_DAMAGED also um 1 erhoeht) und dieser 
+                        von der P_AC abgezogen, der Schaden laesst sich in
+                        einer Schmiede reparieren.
+                       
+P_REMOVE_FUNC         : Funktion die vor dem Ausziehen einer Ruestung auf-
+                        gerufen wird
+                        Siehe auch: bman ruestungen
+
+P_RESISTANCE          : Was fuer Resistenzen sind gesetzt? Enthaelt ein 
+                        Array der Schadenstypen, gegen die das Lebewesen
+                        resistent sein soll. P_RESISTANCE_STRENGHTS ist hier
+                        viel genauer und sollte deswegen verwendet werden
+
+P_RESISTANCE_MODIFIER : Hier steht die Summe aller Modifikatoren, die 
+                        Resistenzen und Anfaelligkeiten betreffen. Diese
+                        Propertie sollte NIE direkt manipuliert werden
+
+P_RESISTANCE_STRENGHTS: Huebscher Typo bei Realisierung von Anfaelligkeiten
+                        und Resistenzen eines Lebenwesens :-)
+
+P_RESISTANCE_STRENGTHS: Was fuer Resistenzen oder Anfaelligkeiten hat das
+                        Lebewesen? Gesetzt wird ein Mapping mit den
+                        Schadenstypen und ein entsprechender numerischer 
+                        Wert. Dieser Wert geht von -1 .. 0 .. 1, ist er
+                        negativ, so ist das Lebewesen resistent, ist er 
+                        positiv, so ist das Lebewesen anfaellig
+
+P_RESTRICTIONS        : Wird vor dem Zuecken oder Anziehen einer Waffe oder
+                        Ruestung geprueft. Hier lassen sich diverse Werte
+                        abfragen, die das verhindern
+                        Siehe auch: bman restriktionen
+
+P_SP                  : SpellPoints, aktuelle Konzentrationspunkte eines
+                        Lebewesens
+
+P_SP_DELAY            : Numerischer Wert, der angibt, wieviele HeartBeats
+                        notwendig sind, damit sich die P_SP um 1 regenieren
+
+P_UNWIELD_FUNC        : Funktion, die vor dem Wegstecken einer Waffe auf-
+                        gerufen wird
+
+P_VULNERABILITY       : Empfindlichkeiten eines Npc/Spielers, sollte ueber
+                        P_RESISTANCE_STRENGTHS realisiert werden
+                         
+P_WC                  : WeaponClass, Wert der die Guete einer Waffe angibt.
+                        Absoluter Oberwert ist 200
+                        Siehe auch: bman grenzwerte
+
+P_WEAPON_TYPE         : Um was fuer eine Waffe handelt es sich? Keule, Speer
+                        oder Axt? Oder was anderes? 
+                        Siehe auch: bman waffen
+
+P_WEAR_FUNC           : Funktion die vor dem Anziehen einer Ruestung auf-
+                        gerufen wird. Sie kann das Anziehen verhindern, hier
+                        ist oftmals ein Eintrag in P_RESTRICTIONS sinnvoller
+                        Siehe auch: bman restriktionen
+
+P_WIELD_FUNC          : Funktion, die vor dem Zuecken einer Waffe aufgerufen
+                        wird. Sie kann das Zuecken verhindern. Oftmals ist
+                        da ein Eintrag per P_RESTRICTIONS vorteilhafter
+                        Siehe auch: bman restriktionen
+
+P_X_ATTR_MOD          : Mapping mit Attributen die veraendert werden, wenn
+                        das Lebewesen dieses OBJEKT bei sich hat. Ist fuer
+                        Krankheiten u.ae. gedacht. Bekanntes Beispiel sind
+                        die Flueche, die man sich auf Akhar Nth'tar weg-
+                        holen kann
+                        Siehe auch: bman attribute
+
+P_X_HEALTH_MOD        : Mapping mit dem die max. P_HP und max. P_SP eines
+                        Lebewesens veraendert werden, das dieses OBJEKT bei
+                        sich traegt. Ohne nachgesehen zu haben sag ich mal,
+                        das es so z.B. beim Rattenblut gemacht wird
+                        Siehe auch: bman kampfobjekte
diff --git a/doc/balance/resistenzen b/doc/balance/resistenzen
new file mode 100644
index 0000000..f2b92a9
--- /dev/null
+++ b/doc/balance/resistenzen
@@ -0,0 +1,59 @@
+
+ RESISTANCE MODIFIER:
+    Soll ein Objekt in einem Spieler ResistanceModifier setzen, gelten
+    folgende Regeln:
+
+    a) Es muss sich um eine Ruestung handeln (in ganz seltenen Faellen
+       werden auch Waffen mit dieser Moeglichkeit erlaubt). Bei Ruestungen
+       muss _nicht_ AddResistanceModifier() benutzt werden, sondern es 
+       genuegt, P_RESISTANCE_STRENGTH in der Ruestung zu setzen.  Dabei 
+       werden _anders_ als bei AddResistanceModifer() Prozentwerte der 
+       maximal erlaubten Resistenz fuer den entsprechenden Ruestungstyp 
+       angegeben. Diese werden beim Tragen der Ruestung automatisch
+       in tatsaechliche Resistenzen des Traegers umgerechnet.
+       (Beispiel siehe "man P_RESISTANCE_STRENGTHS").
+
+    b) Das Objekt muss natuerlich genehmigt werden.
+
+    c) Die Summe aller Resistenzen darf max. -0.15 betragen. Ausserdem
+       muessen zu jedem Schadenstyp gleichzeitig Empfindlichkeiten in
+       mindestens gleicher Staerke bei einem anderen Schadenstyp gesetzt
+       werden oder die Resistenzen muessen mit anderen Nachteilen (z.B.
+       Aufloesung des Objekte bei Angriff mit einem bestimmten Schadenstyp)
+       ausgeglichen werden.
+       
+       Bei Ruestungen darf maximal eine relative Resistenz von 
+       (logischerweise) 100% eingetragen werden. 100% in der Property
+       P_RESISTANCE_STRENGTH einer Ruestung bedeuten folgende Resistenzen
+       im Traeger:
+       
+       AT_ARMOR,
+       AT_SHIELD : -0.15
+       
+       AT_CLOAK,
+       AT_RING,
+       AT_AMULET : -0.10
+       
+       AT_MISC   : nicht gestattet!
+
+       andere    : -0.05
+       
+    d) Zur leichteren 'Identifizierbarkeit' sollten die ResistanceModifier
+       sollten die gesetzten Modifier auch in dem setzenden Objekt als
+       P_RESISTANCE_STRENGTHS gesetzt werden. Bei Ruestungen MUSS dies
+       geschehen (siehe a) ).
+
+    e) Es duerfen keine mechanischen Resistenzen gesetzt werden. Schutz
+       vor mechanischen Angriffen wird durch die Ruestungen sowieso 
+       schon gewaehrt (im Gegensatz zu Schutz vor nichtmechanischen). Und
+       NEIN, da gibt es keine Ausnahmen!
+
+ SIEHE AUCH
+
+     balance, ruestungen, waffen, fernwaffen, uniques, npcs,
+     attributsveraenderungen, kampfobjekte, grenzwerte
+
+
+------------------------------------------------------------------------------
+ LETZTE AeNDERUNG:
+    2003-08-29, Zook.
diff --git a/doc/balance/restriktionen b/doc/balance/restriktionen
new file mode 100644
index 0000000..838bb7d
--- /dev/null
+++ b/doc/balance/restriktionen
@@ -0,0 +1,30 @@
+Es ist moeglich, Waffen und Ruestungen zu beschraenken, so dass sie
+nur von Spielern benutzt werden koennen, die gewisse Kriterien erfuellen.
+Diese Pruefung wird allerdings _nur_ beim Zuecken oder Anziehen geprueft.
+Sollte sich die entsprechende Eigenschaft des Spielers waehrend der
+Benutzung aendern, kann er oder sie das entsprechende Teil weiter
+nutzen, bis es ausgezogen/weggesteckt wird.
+
+Um solche Restriktionen zu implementieren, muss ein Eintrag in die
+Property P_RESTRICTIONS erfolgen. Was kann dort alles eingetragen
+werden?
+
+- Mindestwerte fuer alle Attribute (Geschicklichkeit etc.)
+- Mindestwerte fuer - Questpunkte
+                    - Spielerlevel
+                    - Erfahrungspunkte
+- Spielerrasse oder eine Liste von ein- bzw. ausgeschlossenen Rassen
+- Gildenlevel (macht nur wenig Sinn)
+- Seherstatus
+- Maximalwerte fuer Alkohol, Essen, Trinken
+- Gesinnung (nach oben und unten)
+- Koerpergroesse (nach oben und unten)
+- Anzahl der freien Haende
+
+Die Bedingungen sind beliebig zu kombinieren, man kann also eine Waffe
+programmieren, die nur von ueber 2 Meter grossen satanischen Hobbits
+gezueckt werden kann, die einen leeren Magen haben. (Vermutlich eine
+riesige Fleischgabel...)
+Fuer gute Waffen und Ruestungen sind allerdings vermutlich in erster Linie
+die Restriktionen Spielerlevel, Seherstatus und vielleicht noch Attribute
+interessant. Vielleicht auch noch Erfahrungspunkte.
diff --git a/doc/balance/ruestungen b/doc/balance/ruestungen
new file mode 100644
index 0000000..a77a735
--- /dev/null
+++ b/doc/balance/ruestungen
@@ -0,0 +1,128 @@
+Allgemeines und generelles ueber Ruestungen:
+-------------------------------------------
+
+Properties:
+
+        P_WEIGHT
+           Bitte realistisch halten. Ringe mit weit ueber 100 Gramm sind
+           Schwachsinn, ebenso Hosen mit Gewichten unter 200 usw.
+           Orientierungen an mittelalten Ruestungen; bei neueren treten zum
+           Teil laecherliche Werte auf, die bei Gelegenheit angepasst werden
+           muessen.
+
+        P_VALUE
+           Wert der Ruestungen sollte auch nicht zu gross sein und sich ein
+           wenig an der AC orientieren. Ebenfalls: RMs, schaut genau hin.
+
+        P_ARMOUR_TYPE
+           Bitte sinnvoll benutzen! Kleider als Hosen definieren oder
+           aehnliches ist unsinnig und sollte nicht genehmigt werden. Sind mal
+           wieder die RMs fuer zustaendig. Bitte achtet drauf!
+
+        Folgende Ruestungstypen sind moeglich:
+
+            AT_ARMOUR
+               Alles was irgendwie den Torso schuetzt. Also vorn und hinten,
+               wie ein Pullover, ein Kettenhemd, ein Panzer etc.
+            AT_CLOAK
+               Umhaenge, auch im weiteren Sinn wie Decken und Schleier. Im
+               Prinzip das, was normalerweise nur den Ruecken schuetzt, bei
+               Bedarf aber auch vorn herum gewickelt werden koennte.
+            AT_HELMET
+               Kopfbedeckungen jeder Art, vom normalen Hut ueber Kronen bis
+               hin zu Stirnbaendern (wenn sie schuetzen sollen)
+            AT_TROUSERS
+               Hosen. Alles was zum ueberwiegenden Teil dazu gedacht ist, die
+               Beine und/oder den Popo zu schuetzen, also auch Schuerzen oder
+               Leggins. Natuerlich gehoeren auch Badehosen oder Lendenschuerze
+               hierher. AC dann selbstverstaendlich gering.
+            AT_BOOT
+               Schuhe, Stiefel und Fussbekleidungen jeder Art.
+            AT_GLOVE
+               Handschuhe oder alles, was man so zum Schutz der oberen
+               Extremitaeten benutzt, wie Armschoner, Glacehandschuhe,
+               Faeustlinge, Boxhandschuhe etc.
+            AT_QUIVER
+               Koecher und aehnliches, in denen man Munition fuer
+               Schusswaffen unterbringen kann. Schuetzen tut sowas
+               natuerlich nicht.
+            AT_SHIELD
+               Alles, was man so anstelle der eigenen Arme in einen
+               gegnerischen Schlag halten kann und das nicht offiziell als
+               Waffe deklariert ist. Es sollte nicht grade aus Papier
+               bestehen, und das Gewicht ist mit entscheidend fuer die Guete
+               des Schildes. Sie werden sehr wenig im MG genutzt, was
+               eigentlich seltsam ist. Schliesslich erreicht ein guter Schild
+               die Guete von Helm, Hose und Handschuhen zusammen oder 3/4 der
+               Qualitaet einer Ruestung. Andererseits: es gibt auch nicht sehr
+               viele Schilde. Da herrscht Bedarf!
+            AT_RING
+               Ringe. Prinzipiell sollte gelten, das Ringe praktisch keine     
+	             Schutzwirkung haben (also AC 1 oder max. 2). Nur, und dafuer
+	             sind die Dinger da, wenn sie magisch sind koennen sie
+	             zusaetzliche Funktionen haben.
+            AT_AMULET
+               Im Prinzip dasselbe wie bei AT_RING, nur werden die Dinger
+               meist an Kordeln um den Hals oder als Broschen an den Klamotten
+               getragen, koennten also je nach Groesse tatsaechlich mehr
+               Schutz bieten als ein Ring. Aber auch hier gilt: AC>2 schreit
+               nach Erklaerung und sollte magischen Dingern vorbehalten
+               bleiben.
+            AT_BELT
+               Guertel aller Art (z.B. Waffenguertel oder Magisterguertel
+               der Zauberer). Schutzwirkung hat sowas natuerlich kaum.
+            AT_MISC
+               Alles, was man sonst noch so anziehen kann, was aber eher als
+               Zierrat gedacht ist. AC immer 0. Bitte *keine*
+               Kleidungsstuecke, die in eine der anderen Kategorien passen,
+               als AT_MISC definieren. Dann lieber die AC sehr tief. Wenn es
+               eh als Gag gedacht ist koennen die Spieler sich auch bei Bedarf
+               umziehen.
+
+        P_EFFECTIVE_AC
+           Falls eine DefendFunc vorhanden ist und die regulaere AC
+           veraendert, sollte sie gesetzt werden. Es wird der
+           Durchschnittswert der AC incl. der DefendFunc gesetzt. Dient der
+           Kaempfergilde zur Einschaetzung, wird auch von Gilden evtl. zur
+           Berechnung von Zusatzschutz oder weiteren Effekten verwendet.
+
+        P_DAM_TYPE
+           Auch Ruestungen koennen einen Damage-Typ haben. Nutzen tut das
+           bisher nur die Kaempfergilde, aber schaden tut's keinem. Default
+           ist DT_BLUDGEON.
+
+        P_NOBUY
+           Alle Ruestungen ab 2/3 der maximal fuer den jeweiligen Ruestungstyp
+           zulaessigen AC werden beim Verkauf im Laden einbehalten. Dennoch
+           sollten besondere Ruestungen P_NOBUY gesetzt haben. Insbesondere
+           waere das fuer alles mit DefendFuncs zu wuenschen, aber auch
+           Sachen, die als Gag gedacht sind. Dafuer koennen sich die Spieler
+           ruhig etwas recken.
+
+        P_DAMAGED
+            Diese Property (sie gibt den Grad der Beschaedigung einer Waffe
+            oder Ruestung an) sollte man _nicht_ per Hand setzen. Stattdessen
+            ruft man in der Ruestung Damage(int) auf. Ein positiver Parameter
+            bedeutet eine Beschaedigung, ein negativer Reparatur um diesen
+            Wert. In der Funktion werden alle notwendigen Bedingungen
+            geprueft (maximale Ruestungsklasse etc).
+
+
+Spezialruestungen/Ruestungen mit Sonderfunktion:
+
+       Alle Ruestungen, die ueber eine DefendFunc oder aehnliches verfuegen
+       sind genehmigungspflichtig.
+
+       Prinzipiell sollten die geltenden Grenzwerte nicht ueberschritten 
+       werden; Ausnahmen koennen unter Umstaenden genehmigt
+       werden. Immer ist der Return-Wert per random() zurueckzuliefern, da der
+       Wert ohne weiteres random() aufaddiert wird.
+
+       Saemtliche Sonderfunktionen, wie Heilungen, Sonderattacken auf Gegner,
+       Waffenbeschaedigungen etc. muessen genehmigt werden.
+
+       Die Properties, mit denen Handschuhe 'handfrei' bzw 'fingerfrei' gemacht
+       werden, duerfen nur vergeben werden, wenn die Handschuhe den Schaden der Hand
+       nicht veraendern und die Beschreibung der Handschuhe dazu passt.
+
+------------------- LETZTE AENDERUNG: Humni, 2011-10-13
diff --git a/doc/balance/schadenstypen b/doc/balance/schadenstypen
new file mode 100644
index 0000000..41896f2
--- /dev/null
+++ b/doc/balance/schadenstypen
@@ -0,0 +1,48 @@
+Schadenstypen:
+
+Es werden zwei Arten von Schadenstypen unterschieden:
+
+a) physikalische Schadenstypen
+
+   DT_BLUDGEON      Schlagschaden, z.B. Keulen
+   DT_EXPLOSION     Schaden durch eine Explosion
+   DT_PIERCE        Stechschaden, z.B. Lanzen
+   DT_RIP           Reissender Schade, z.B. Krallen
+   DT_SLASH         Schnittschaden, z.B. Schwerter
+   DT_SQUEEZE       Quetschschaden, z.B. Tentakel
+   DT_WHIP          Peitschenschaden, z.B. Peitschen
+
+   Alle physikalischen Schadenstypen stehen auch in dem Mapping
+   PHYSICAL_DAMAGE_TYPES.
+
+b) magische Schadenstypen
+
+   DT_ACID          Saeureschaden
+   DT_AIR           Luft(mangel)schaden
+   DT_COLD          Kaelteschaden
+   DT_FIRE          Feuerschaden
+   DT_HOLY          Heiliger Schaden
+   DT_LIGHTNING     Blitzschaden
+   DT_MAGIC         Genereller magischer Schaden
+   DT_POISON        Giftschaden
+   DT_SOUND         Laermschaden
+   DT_TERROR        Angstschaden
+   DT_UNHOLY        Unheiliger/daemonischer Schaden
+   DT_WATER         Wasserschaden
+
+   Alle magischen Schadenstypen stehen auch in dem Mapping
+   MAGICAL_DAMAGE_TYPES.
+
+Waffen duerfen maximal 50% homogenen magischen Schaden machen. 
+Die Summe der magischen Schaeden darf maximal 66% betragen. 
+Es _muss_ immer mindestens ein physikalischer Schaden gesetzt 
+sein. Der Anteil an physikalischem Schaden betraegt demzufolge
+immer mindestens 34%. Solche Waffen sind generell genehmigungs-
+pflichtig.
+
+Genehmigungspflichtig sind auch generell Waffen, die als Schadens-
+typ DT_TERROR, DT_SOUND, DT_AIR oder DT_SQUEEZE enthalten.
+
+Der Schadenstyp DT_EXPLOSION wird nicht mehr genehmigt.
+
+
diff --git a/doc/balance/techniken b/doc/balance/techniken
new file mode 100644
index 0000000..9c10eae
--- /dev/null
+++ b/doc/balance/techniken
@@ -0,0 +1,108 @@
+****************************************************************************
+* Dieser Text ist veraltet. Die Techniken werden von der Mudlib nicht      *
+* ausgewertet und momentan auch von keiner Gilde (Ausnahme: die Tanjian    *
+* zeigt sie an, tut aber nix damit.                                        *
+****************************************************************************
+----------------------------------------------------------------------------
+     P_TECHNIQUE: Techniken, mit denen Waffen eingesetzt werden koennen
+----------------------------------------------------------------------------
+
+TQ_THRASH - "Schlagtechnik"
+
+    Die vom Verstaendnis her einfachste Technik: Man nehme sich einen
+    Gegenstand,  der gross genug ist und halbwegs  brauchbare  Dichte 
+    besitzt - also nicht gerade federleicht  fuer seine Groesse ist - 
+    und haue drauf.  Egal ob Henkersaxt,  Stock,  Stuhl,  Bierflasche 
+    (ganz), Knueppel, Keule oder Bratpfanne,  das und aehnliches sind 
+    brauchbare  'Waffen',  um mit dieser Technik Erfolg zu haben.  Es 
+    ist eine schlagende Bewegung,  also das gewollte schwunghafte Zu-
+    sammenfuehren zweier Koerper, fuer gewoehnlich Waffe und Gegner.
+
+
+TQ_THRUST - "Stosstechnik"
+
+    Auch diese Technik erfreut sich zumindest in Kneipenschlaegereien
+    grosser Beliebtheit: Das Stossen.  Mit einem vorzugsweise spitzen
+    Gegenstand wie Speer, Dolch, Bierflasche (zerbrochen) oder Helle-
+    barde  wird dem Gegner ein Loch in den Leib  gestossen,  im Falle 
+    eines   stumpfen Gegenstandes wie z.B.  einem Kampfstock oder dem 
+    eigenen  Zeigefinger oder Knoechel zielt man auf Vitalpunkte oder
+    Weichteile und erzielt auch damit beeindruckende Ergebnisse.  Das
+    Gewicht der Waffe ist  weniger von Bedeutung,  da  der Akteur bei 
+    einem korrekt ausgefuehrtem seine eigene Masse mit einsetzt.
+
+
+TQ_STROKE - "Streichtechnik"
+
+    Ein etwas eigenwillig anmutender Begriff,  aber bei genauerer Be-
+    trachtung hat  diese Technik tatsaechlich etwas mit Streicheln zu
+    tun:  Die wichtigste Komponente der Bewegung fuehrt mehr oder we-
+    niger am  Gegner _entlang_ und nicht auf ihn zu.  Man stelle sich 
+    ein Schwert vor, das geschmeidig durch die Kehle eines Orks glei-
+    tet  und eine Menge Blut  freisetzt oder ein Pergamentblatt,  das
+    man aus der Hand gezogen bekommt  (und ebenfalls eine Menge  Blut
+    freisetzt).  In den allermeisten Faellen ist also eine moeglichst
+    scharfe Schneide im Spiel und, auch wenn schmerzhafte Erfahrungen
+    mit  Pargamentkanten gelegentlich gegenteiliges  vermuten lassen, 
+    je schaerfer die Schneide, desto weniger Druck in Richtung feind-
+    lichen  Zielkoerper  muss fuer  einen  "durchschneidenden Erfolg" 
+    ausgeuebt werden.
+
+
+TQ_WHIP - "Peitschtechnik"
+
+    Unter der Peitschtechnik wird nun alles zusammengefasst,  was den
+    Umgang mit beweglichen Elementen an der Waffe betrifft.  Egal, ob
+    Peitsche a la George Lucas, neunschwaenzige Katze, Manriki-Gusari
+    (eine halbmeter lange Kette mit Gewichten an den Enden), ein- bis
+    dreikoepfigem  beketteten Morgenstern oder gar Dreschflegel,  sie 
+    sind zumindestteilweise sehr unterschiedlich konzipiert (so fehlt
+    der  Peitsche am aeusseren Ende das Gewicht,  so dass jenes durch
+    eine gegenlaeufige Bewegung beschleunigt werden muss), haben aber
+    eines gemeinsam: Der Schaden wird durch ein ueber die normale Be-
+    wegung des Kaempfers  hinaus beschleunigtes Teil der Waffe verur-
+    sacht. Durch jene Beweglichkeit der  Waffe wird ein sehr  grosses 
+    Mass an Koordinationsvermoegen vom Kaempfer abverlangt.
+
+----------------------------------------------------------------------------
+
+Vermischtes
+
+    - Waffen koennen  sich fuer mehrere  Techniken eignen.  Mit einem
+      Katana, dem Schwert der Samurai, beispielsweise kann man in die
+      ungeschuetzten Partien des Gegners stechen,  dem Gegner mit der 
+      Streichtechnik tiefe Schnitte in die Unterseiten der Arme  (ein
+      durch typische Samurairuestungen schlecht geschuetzter Bereich)
+      zufuegen, und mit der beruehmten halben Koerperdrehung dem in-
+      zwischen taumelndem Kontrahenten den Kopf abschlagen. 
+
+    - Waffen koennen sich, obwohl von der gleichen Gattung, fuer ver-
+      schiedene Techniken eignen.  Ein spitzer Dolch mag eine stumpfe
+      Klinge haben  und nur fuer ausschliesslich stossende  Praktiken
+      zu gebrauchen zu sein, waehrend ein abgerundetes Kuechenmesser,
+      ebenfalls vom Typ "Zyn", fuer kantiges Zuschlagen und eventuell
+      auch fuer schnittige Streichtechniken herhalten kann.
+
+    - Je nach Zweck und Koennen wird die beste zu verwendende Technik 
+      variieren:  Wenn es um arme Grashalme geht,  wird die Sense mit 
+      schneidenden Bewegungen gefuehrt  (schoen in der  Milka-Werbung 
+      zu sehen,  fuer die, die es partout nicht glauben wollen),  bei 
+      einer Bauernrevolte mag das gute Stueck jedoch auch mit anderen 
+      Techniken effektiv (und vielleicht sogar effektiver) eingesetzt 
+      werden, wer weiss, wer war schon dabei...?  Selbst bei normalen
+      Waffen wie Schwertern  wuerde ein Ungeuebter  selten versuchen,  
+      mit Streichtechniken Schaden zu verursachen, denn das Zustechen 
+      ist schlicht einfacher.
+
+    - Es wird immer  besondere Waffen geben.  So  sind beispielsweise
+      das Zweililien und  die Hellebarde  beide dem Typ "Kzrok" (fuer
+      nicht-Trves: Stangenwaffen) zuzuordnen, die Techniken  weichen,
+      bedingt  durch die Vielseitigkeit  der Waffen,  jedoch von  der
+      Norm ab: Das Zweililien, an jedem  Ende eine lange und  scharfe
+      Klinge,  kann  ebenso  vielseitig  gehandhabt  werden  wie  ein 
+      Schwert,  waehrend die Hellebarde (eine Mischung aus  Lanze und
+      Axt) durch ihre unausgewogene Gewichtsverteilung  und dem damit
+      verbundenen Potenzial an Schwung schlichtweg DIE Waffe der Wahl 
+      fuer feige Zwerge ist:  Schwingen wie eine grosse  Axt und  den 
+      Gegner auf Distanz halten koennen.  Gut,  dass es keine  feigen 
+      Zwerge gibt ;o)
diff --git a/doc/balance/waffen b/doc/balance/waffen
new file mode 100644
index 0000000..44fa6e3
--- /dev/null
+++ b/doc/balance/waffen
@@ -0,0 +1,112 @@
+Allgemeines und generelles zu Waffen:
+------------------------------------
+
+   P_WC
+        Die WC sollte einigermassen "realistisch" gewaehlt werden. Die
+        Verantwortung hierfuer obliegt den jeweiligen RMs. Sie sollte
+        zwischen ca. 35 und 200 liegen. Auf jeden Fall sind die aktuellen
+        Genehmigungsgrenzen zu beachten. Sollte die WC diese Grenzen
+        ueberschreiten, unterliegt die Waffe ausser der Genehmigung durch
+        den RM der Beurteilung durch das Waffengremium.
+
+   P_NR_HANDS
+        Waffen ueber einer effektiven WC von 150 muessen zweihaendig sein.
+        Ausnahmen koennen unter Umstaenden genehmigt werden, zum Beispiel
+        wenn die Waffe schwer erreichbar oder zahlenmaessig begrenzt, eine
+        Questbelohnung etc. ist. Alle einhaendigen Waffen ueber WC 140 sind
+        in jedem Fall genehmigen zu lassen.
+
+        Messer muessen generell einhaendig sein!
+
+   P_WEIGHT 
+        Bitte realistisch halten. Damit ist *nicht* das RL-Gewicht
+        gemeint, sondern man sollte am besten vergleichbare Waffen des
+        MG als Massstab nutzen. Da hier z.T. gravierende Diskrepanzen 
+        bestehen, evtl. mal mehrere vergleichen. Die Verantwortung 
+        hierfuer obliegt den RMs.
+
+        Waffen mit einem Gewicht von ueber 4000 Gramm sollten normalerweise
+        auch zweihaendig sein oder muessen der Balance vorgelegt werden.
+	
+   P_VALUE
+      Wie bei P_WEIGHT sind die RMs gefragt: Augenmass zaehlt. Werte
+      ueber 10000 oder so sind zwar nett, aber sinnlos und unrealistisch.
+
+   P_DAM_TYPE
+      Jede Waffe, die aus physikalischem Material besteht (also faktisch
+      alles mit Hardware) *muss* einen physikalischen Schadenstyp haben.
+
+   P_WEAPON_TYPE
+      WT_SWORD, WT_AXE,  WT_CLUB, WT_SPEAR, WT_KNIFE, WT_AMMU, WT_MAGIC,
+      WT_WHIP, WT_STAFF, WT_MISC, WT_RANGED_WEAPON 
+
+   P_DAMAGED
+      Diese Property (sie gibt den Grad der Beschaedigung einer Waffe
+      oder Ruestung an) sollte man _nicht_ per Hand setzen. Stattdessen
+      ruft man in der Waffe Damage(int) auf. Ein positiver Parameter
+      bedeutet eine Beschaedigung, ein negativer Reparatur um diesen
+      Wert. In der Funktion werden alle notwendigen Bedingungen
+      geprueft (minimale/maximale Waffenklasse etc).
+
+   P_EFFECTIVE_WC
+      Hier kann die evtl durch eine HitFunc veraenderte WC angegeben
+      werden. Der Durchschnittswert soll da stehen. Die Kaempfergilde
+      kann das in gewissen Grenzen abschaetzen und es wird auch fuer
+      die Guete von Zusatzangriffen von Gilden genutzt.
+
+   P_EFFECTIVE_AC
+      Fuer Paradewaffen setzen. Werte unbedingt mit Boing absprechen!
+
+   P_NOBUY
+      Waffen ab WC 150 werden beim Verkauf im Laden zurueckbehalten. Es
+      sollte aber auch fuer Waffen, die HitFuncs enthalten, P_NOBUY
+      gesetzt werden.
+
+Spezialwaffen/Waffen mit Sonderfunktion
+
+   ** ALLE Waffen mit HitFunc oder entsprechender Fkt. muessen   **
+   ** genehmigt werden!                                          **
+
+   Solche Waffen muessen ueber ein P_INFO verfuegen und irgendwo in ihrer
+   Beschreibung oder im P_INFO mindestens eine Andeutung ueber die
+   Herkunft oder den Grund ihrer besonderen Faehigkeit haben.
+
+   Auch Spezialwaffen sollten nach Moeglichkeit nicht ueber eine
+   EFFECTIVE_WC von 200 hinausgehen. Der Return-Wert der HitFunc, sofern
+   er von 0 abweicht, MUSS per random() zurueckgegeben werden, da er ohne
+   weiteres random() auf die WC aufaddiert wird.
+
+   Zu beachten ist, dass man bei Waffen, die nur fuer NPCs mehr Schaden 
+   machen sollen, nicht (nur) ueber P_RACE prueft, ob der Benutzer ein 
+   solcher ist. Es ist denkbar, dass Spieler ihre Rasse temporaer ver-
+   aendern koennen. Waffen die nur dann besser zuschlagen, wenn es
+   keinem Spieler zuguten kommen kann (!interactive, nicht zueckbar 
+   durch Hilfs-NPC etc.), muessen nicht genehnigt werden.
+
+   Waffen, die Monster vergiften (also nicht nur P_DAM_TYPE DT_POISON
+   haben) sind zwar nicht grundsaetzlich verboten, aber doch unerwuenscht.
+   Sie sind auf jeden Fall genehmigungspflichtig. Ausserdem sollte die
+   Wahrscheinlichkeit einer Vergiftung pro Kampfrunde max. bei 1% liegen.
+
+Parierwaffen
+       
+   Parierwaffen sind Waffen, die die Verteidigung unterstuetzen.  
+   Eine Waffe wird als Parierwaffe verwendet, wenn die Property
+   P_PARRY gesetzt ist. Folgende Werte sind moeglich:
+       
+   PARRY_NOT     Die ist KEINE Parierwaffe (=default).
+       
+   PARRY_ONLY    Dies ist eine reine Parierwaffe.
+
+   PARRY_TOO     Diese Waffe wird sowohl als Parierwaffe als auch
+                 als Angriffswaffe benutzt
+       
+   Die Schutzwirkung der Parierwaffe wird wie bei Ruestungen ueber die
+   Property P_AC gesetzt. Ueber die Property P_DEFEND_FUNC kann wie
+   bei Ruestungen eine DefendFunc gesetzt werden.
+       
+   Waffen mit PARRY_ONLY unterliegen den gleichen P_AC-Grenzwerten
+   wie Ruestungen vom Typ AT_SHIELD. Waffen mit PARRY_TOO oder einer
+   DefendFunc muessen generell genehmigt werden.
+
+---------- LETZTE AENDERUNG: Humni, 2011-10-12
diff --git a/doc/beispiele/AddCmd/Einfuehrung.AddCmd.txt b/doc/beispiele/AddCmd/Einfuehrung.AddCmd.txt
new file mode 100644
index 0000000..eadf6f9
--- /dev/null
+++ b/doc/beispiele/AddCmd/Einfuehrung.AddCmd.txt
@@ -0,0 +1,174 @@
+Hallo MitmagierInnen *g*,
+
+Ok, dann gehts mal ein bischen zur Sache:
+
+/std/thing/commands.c wurde so erweitert, dass man auch komplexe Syntax
+direkt im AddCmd vorparsen kann.
+
+Dazu ein kurzes Beispiel:
+-------------------------
+
+In einem Objekt (ein Busch) findet sich folgende Syntaxdefinition.
+
+ AddCmd("pfluecke|ernte&fruechte|beeren&@ID","cmd_pfluecken",
+        "Was willst Du denn @verben?|Wo willst Du denn die Beeren @verben?");
+
+Der erste Parameter definiert die Syntax. Das Zeichen & trennt verschiedene
+zu parsende Items:
+
+1. pfluecke|ernte
+2. fruechte|beeren
+3. @ID
+
+Unter 1. finden sich die Verben - das sind die gleichen, die man in der
+alten Version auch belegen konnte. Die Regel reagiert also auf "pfluecke"
+oder auf "ernte".
+
+Alle weiteren Regeln muessen erfuellt werden, damit der zweite Parameter
+(die Funktion) aufgerufen wird. @ID steht dabei fuer die ID des Objektes,
+in dem das AddCmd steht. Hier also der Busch.
+
+Dazu einige Beispiele:
+
+a) > pfluecke beeren
+ -> erfuellt 1 und 2. Aber nicht 3. => Fehlermeldung.
+
+b) > ernte von busch
+ -> erfuellt 1 und 3, jedoch 2 nicht. => Fehlermeldung.
+
+c) > pfluecke fruechte von busch
+ -> erfuellt 1, 2 und 3 -> busch->cmd_pfluecken() wird aufgerufen. Das "von"
+    ist Fuellwerk. Davon kann beliebig viel im String sein. Sie werden 
+    ignoriert.
+    Es sind also auch unsinnige Syntaxe richtig, solange der String die 
+    Items erufellt.
+
+Im Fall c) wird das Verhalten des Busches durch die Funktion "cmd_pfluecken"
+definiert, wie auch in der alten Version von AddCmd.
+
+Was passiert aber in den Faellen a) und b)? Dafuer ist der dritte
+Parameter da:
+  "Was willst Du denn @VERBen?|Wo willst Du denn die Beeren @VERBen?");
+
+Hier sind zwei Fehlermeldungen, durch | getrennt, definiert:
+
+x) Was willst Du denn @VERBen?
+y) Wo willst Du denn die Beeren @VERBen?
+
+Erst wird geprueft, ob 2. erfuellt ist. Falls nicht, gibt es Fehlermeldung x)
+Dann wird auf 3. geprueft. Ist das nicht erfuellt (aber 2. schon) gibt es
+Fehlermeldung y).
+
+Im Fall a sind 1 und 2 erfuellt. Demnach wird y) ausgegeben. @VERB wird
+dabei durch das benutzte Verb ersetzt (das abschliessende en, bzw e wird
+abgeschnitten):
+ Wo willst Du denn die Beeren pfluecken?
+
+Im Fall b ist 1 erufellt, 2 aber schon nicht. Daher gibt es Fehlermeldung
+x). Auf 3. wird nicht mehr geprueft.
+ Was willst Du denn ernten?
+
+Wie schon erwaehnt erfuellt c) alle Bedingungen und es gibt keine Ausgabe -
+Statt dessen kann man das Pfluecken in der Funktion abhandeln.
+
+In den Faellen 1 und 2 wird die Fehlermeldung ueber notify_fail()
+abgehandelt. Ergo wird hier anderen Befehlen von anderen Objekten noch
+eine Chance gegeben. Gleiches gilt fuer die Funktion cmd_pfluecken(), wenn
+deren Rueckgabewert 0 ist. Auch hier gibt es die Analogie zum alten AddCmd.
+
+Gibt cmd_pfluecken() einen Wert ungleich 0 zurueck, wird die Suche nach
+der richtigen Syntax danach abgebrochen, so dass auch kein anderes Objekt
+mehr gefragt wird (wie gehabt).
+
+Das ist auch fuer Forscherpunkte wichtig. FP werden generell nur bei einem
+Rueckgabewert ungleich 0 vergeben.
+
+Eine kleine Erweiterung der Beispielsyntax:
+-------------------------------------------
+
+ AddCmd("pfluecke|ernte&fruechte|beeren&@ID@\impossible",0,
+  "Was willst Du denn @VERBen?|Wo willst Du denn die Beeren @VERBen?|"
+  "Du pflueckst ein paar Beeren und isst sie auf.^@WER isst ein paar
+  Beeren.");
+
+Eine neue Regel:
+
+4. \impossible
+
+Und eine neue Fehlermeldung:
+z) Du pflueckst ein paar Beeren und isst sie auf.^@WER isst ein paar Beeren.
+
+Zur Regel:
+
+Die Regel ist nicht erfuellbar, da Spieler das \i nicht erzeugen
+koennen. Zwangslaeufig gibt es auch keine Funktion, die aufgerufen werden
+koennte.
+
+Dafuer gibt es eine Fehlermeldung, die ein neues Element enthaelt. Das
+^. Wird diese Meldung ausgegeben ist das genau wie ein return 1 in der
+Funktion. Die Syntax wird als richtig anerkannt. (Es kann hierfuer also
+auch FP geben).
+
+Der Text vor dem ^ geht an den Spieler, alles dahinter geht an die Spieler
+im Raum. Steht hinter dem ^ nichts, gibt es keine Raummeldung.
+
+Auf diese Weise kann man leicht ein Raumkommando erstellen, dass nur
+Textausgaben macht, aber keine Funktionalitaet enthaelt. Es ist keine
+eigenstaendige Funktion mit irgendwelchem eigenen Parsing mehr noetig.
+
+Das @WER wird durch this_player()->Name() ersetzt, so dass man auch einen
+Persoenlichen bezug in den Meldungen herstellen kann.
+
+Abwaertskompatibel
+------------------
+
+Die Erweiterungen von AddCmd erlauben ein einfacheres Erstellen von
+Standard-Kommandos, ohne dass man sich mit dem Parsen in Funktionen
+beschaeftigen muss.
+
+Die Aenderung ist vollstaendig abwaertskompatibel. Wer sich also sagt,
+das das alles viel zu kompliziert sei, kann weiterhin:
+
+ AddCmd(({"pfluecke","ernte"}),"cmd_pfluecken");
+
+benutzen. Das Verhalten ist haargenau das selbe wie bisher. Dementsprechend
+ist auch kaum Aufwand beim Einpflegen dieser Aenderung notwendig.
+
+Zur Beachtung
+-------------
+
+Ueberall, wo direkt auf P_COMMANDS zugegriffen wird, kann es prinzipiell
+zu Problemen kommen, da sich die Struktur des Mappings und der Zugriff
+darauf geaendert hat.
+
+Im Folgeartikel findet sich eine Liste mit allen Objekten, die das machen. In
+den oeffentlichen Verzeichnissen werde ich mich mit den Magiern in Verbindung
+setzen oder selbst kontrollieren, ob Aenderungen notwendig sind.
+
+Objekte in den Players-Verzeichnissen werde ich nur in Ausnahmefaellen
+anpassen. Zur Erinnerung: Angeschlossenes gehoert nicht nach /players/.
+
+Der Code ist auf Kompatibilitaet mit dem alten System ausgelegt, dennoch
+gibt es ein paar Aenderungen. Zum Beispiel ist P_COMMANDS keine echte
+Property mehr sondern eine lokale Variable. Demzufolge wird P_COMMANDS
+bei "xprop" nicht mehr angezeigt. Die Schnittstelle wird durch _set_
+und _query_funktionen realisiert. Die Mappings werden _immer_ als Kopien
+rausgegeben, so dass man damit nicht versehentlich Unsinn machen kann.
+
+Schlusswort
+-----------
+
+Dieser Artikel stellt nur eine Einleitung in die Aenderung und deren
+Moeglichkeiten dar und ist keinesfalls vollstaendig. Wer mehr darueber wissen
+will, ist eingeladen, sich die Manpage zu "AddCmd" und zu den Beispielen
+"AddCmd_bsp" anzuschauen.
+
+Die Seiten sind aus dem Regenbogen "geliehen" und werden in den kommenden
+Tagen noch auf die Beduerfnisse des Morgengrauen angepasst.
+
+Mein Dank geht an Gloinson, der die Idee hatte, die Anpassungen vorzunehmen
+und Migration des Codes ins MorgenGrauen uebernommen hat.
+
+Falls nun noch Fragen sind, nur zu! :)
+
+V*,
diff --git a/doc/beispiele/AddCmd/busch.c b/doc/beispiele/AddCmd/busch.c
new file mode 100644
index 0000000..da3c5b3
--- /dev/null
+++ b/doc/beispiele/AddCmd/busch.c
@@ -0,0 +1,58 @@
+inherit "/std/thing";
+#include <properties.h>
+#include <moving.h>
+#include <language.h>
+
+#pragma strict_types,save_types,rtt_checks
+
+protected void create()
+{
+   ::create();
+
+   SetProp(P_GENDER, NEUTER);
+   SetProp(P_NAME, "Gebuesch");
+   SetProp(P_SHORT,"Ein Gebuesch");
+   SetProp(P_LONG,"Du stehst vor einem gaanz normalen Gebuesch. Es traegt "
+                  "Fruechte. Und eventuell\nkann man darin etwas finden.\n");
+   SetProp(P_NOGET,"Es ist festgewachsen, eventuell kann man es ausgraben?\n");
+
+   SetProp(P_WEIGHT,5000);
+   SetProp(P_SIZE,100);
+
+   AddId(({"busch","gebuesch"}));
+
+   AddDetail(({"fruechte","frucht"}),
+     "Es scheinen Beeren zu sein. Vielleicht kannst Du sie pfluecken?\n");
+   AddDetail("beeren",
+     "Pflueck sie doch, vielleicht bringt das ja was.\n");
+
+   // Nur wenn die Syntax erfuellt ist, wird cmd_pfluecken() aufgerufen.
+   // "pfluecke beeren von busch" etc.
+   AddCmd("pfluecke|ernte&fruechte|beeren&@ID","cmd_pfluecken",
+          "Was willst Du denn @verben?|Wo willst Du denn die Beeren @verben?");
+
+   // suchen gibt nur eine Meldung aus. Dafuer braucht man kuenftig keine
+   // Funktionen mehr.
+   AddCmd("such|suche|durchsuch|durchsuche&@ID&\nimpossible",0, 
+          "Wo willst Du @VERBen?|Du durchsuchst das Gebuesch, findest aber nichts.^"
+          "@WER1 durchsucht ein Gebuesch, findet aber nichts.");
+
+   // Graben geht eh nicht. Daher nur Fehlermeldungen.
+   // Das ^ sagt, dass hier ein return1 zurueckgegeben wird. Es gibt aber keine 
+   // Raummeldung.
+   AddCmd("grab|grabe&@ID&aus@\nimpossible",0,
+          "Was willst Du graben?|Du willst das Gebuesch ausgraben?|"
+          "Die Wurzeln scheinen tief zu rechen. Das wird nichts.^");
+}
+
+int cmd_pfluecken(string arg, mixed *param)
+{
+    object obj;
+    obj=clone_object(__DIR__"obst");
+    write("Verwundert pflueckst Du "+(obj->name())+" vom Busch. Komisch.\n");
+
+    // Das hier ist ein Beispiel fuer AddCmd, daher mach ich mir nich 
+    // die Muehe das richtig zu machen. Is eh nur fuer Magier!
+    obj->move(this_player(),M_GET|M_NOCHECK);
+    return 1;
+}
diff --git a/doc/beispiele/AddCmd/obst.c b/doc/beispiele/AddCmd/obst.c
new file mode 100644
index 0000000..8efb471
--- /dev/null
+++ b/doc/beispiele/AddCmd/obst.c
@@ -0,0 +1,232 @@
+/* Hinweis zu diesem Beispielobjekt: Lebensmittel lassen sich besser von
+ * /std/food ableiten, das bringt viele Funktionalitaeten mit, die man sonst
+ * muehsam selber basteln muss.
+ */
+
+inherit "/std/thing";
+
+#include <moving.h>
+#include <properties.h>
+#include <wizlevels.h>
+
+#define SAETTIGUNG 2
+#define HEILUNG 20
+#define EAT_DELAY 1200 // 20 Minuten
+
+#define BS78(x) break_string(x,78)
+
+protected void create()
+{
+  ::create();
+
+  // Gemeinsamer Teil des create
+  AddId("obst");
+  SetProp( P_VALUE, 60+random(10) );
+
+  // AddCmd( ({"iss","esse"}), "act_essen");  
+  // new style AddCmd
+  AddCmd( "iss|esse&@ID","act_essen","Was willst Du denn essen?");
+
+  // Nach ca. 10 Minuten resetet das Obst, im Reset wird es destructet, 
+  // so spare ich call_outs
+  set_next_reset(600);
+
+  // Name, ID, Gender, Desc und Gewicht sind Abhaengig von der Obstart
+  switch (random(11)){
+  default:
+    SetProp( P_GENDER, MALE );
+    SetProp( P_NAME, "Apfel" );
+    AddId( "apfel" );
+    SetProp( P_SHORT, "Ein Apfel" );
+    SetProp( P_LONG, BS78(
+      "Ein saftiger Apfel. Dir laeuft das Wasser im Munde zusammen."
+    ));
+    SetProp( P_WEIGHT, 80 );
+    break;
+  case 1:
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Birne" );
+    AddId( "birne" );
+    SetProp( P_SHORT, "Eine Birne" );
+    SetProp( P_LONG, BS78(
+      "Eine koestliche, reife Birne. Die ist sicher lecker."
+    ));
+    SetProp( P_WEIGHT, 90 );
+    break;
+  case 2:
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Banane" );
+    AddId( "banane" );
+    SetProp( P_SHORT, "Eine Banane" );
+    SetProp( P_LONG, BS78(
+      "Die Banane sieht wirklich lecker aus, am besten gleich essen."
+    ));
+    SetProp( P_WEIGHT, 110 );
+    break;  
+  case 3:
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Kiwi" );
+    AddId( "kiwi" );
+    SetProp( P_SHORT, "Eine Kiwi" );
+    SetProp( P_LONG, BS78(
+      "Klein und gruen, aber mit leckeren Innenleben. Yam yam."
+    ));
+    SetProp( P_WEIGHT, 50 );
+    break;  
+  case 4:
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Erdbeere" );
+    AddId( "erdbeere" );
+    SetProp( P_SHORT, "Eine Erdbeere" );
+    SetProp( P_LONG, BS78(
+      "Eine ueberdurchschnittlich grosse Erdbeere. Sie "
+      "ist sicher suess und saftig."
+    ));
+    SetProp( P_WEIGHT, 20 );
+    break;  
+  case 5:
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Kirsche" );
+    AddId( "kirsche" );
+    SetProp( P_SHORT, "Eine Kirsche" );
+    SetProp( P_LONG, BS78(
+      "Zwar nur klein ist diese Kirsche, aber so dunkel wie sie "
+      "aussieht, ist sie sicher total suess und lecker."
+    ));
+    SetProp( P_WEIGHT, 15 );
+    break;  
+  case 6:
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Orange" );
+    AddId( "orange" );
+    SetProp( P_SHORT, "Eine Orange" );
+    SetProp( P_LONG, BS78(
+      "Eine grosse, gelbe Orange. Eine von diesen suessen, die sich so "
+      "leicht schaelen lassen und keine Zicken machen, so wie Saftorangen."
+    ));
+    SetProp( P_WEIGHT, 90 );
+    break;  
+  case 7:
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Mandarine" );
+    AddId( "mandarine" );
+    SetProp( P_SHORT, "Eine Mandarine" );
+    SetProp( P_LONG, BS78(
+      "Suess und saftig und gesund. So muss das Essen im Paradies "
+      "gewesen sein."
+    ));
+    SetProp( P_WEIGHT, 60 );
+    break;  
+  case 8:
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Zitrone" );
+    AddId( "zitrone" );
+    SetProp( P_SHORT, "Eine Zitrone" );
+    SetProp( P_LONG, BS78(
+      "Sauer, sauer und nochmals sauer. Aber ultralecker. Na, sabberst Du "
+      "schon auf die Tastatur?"
+    ));
+    SetProp( P_WEIGHT, 60 );
+    break;  
+  case 9:
+    SetProp( P_GENDER, MALE );
+    SetProp( P_NAME, "Granatapfel" );
+    AddId( "granatapfel" );
+    SetProp( P_SHORT, "Ein Granatapfel" );
+    SetProp( P_LONG, BS78(
+      "Er ist zwar etwas schwieriger, den zu essen, aber der Aufwand "
+      "lohnt sich."
+    ));
+    SetProp( P_WEIGHT, 90 );
+    break;  
+  case 10:
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Pflaume" );
+    AddId( "pflaume" );
+    SetProp( P_SHORT, "Eine Pflaume" );
+    SetProp( P_LONG, BS78(
+      "Eine grosse, blaue Pflaume. Lass sie Dir doch schmecken."
+    ));
+    SetProp( P_WEIGHT, 20 );
+    break;
+  }
+}
+
+void reset()
+{
+  object env = environment(this_object());
+
+  if (env) {
+    if (query_once_interactive(env)){
+      // Mein Env is ein User
+      tell_object(env, BS78(
+        capitalize(this_object()->name(WER))+" verschwindet mit einem "+
+        "\"PLOPP\" aus deinem Inventar. Da scheint Magie im Spiel zu sein."
+      ));
+    } else if (sizeof(all_inventory(env)&users())){
+      // In meinem Environment sind User (Es ist ein Raum)
+      tell_room(env, BS78(
+        capitalize(this_object()->name(WER))+" verschwindet mit einem "+
+        "\"PLOPP\". Da scheint Magie im Spiel zu sein."
+      ));
+    }
+  }
+
+  // Objekt zerstoeren
+  remove(1);
+}
+
+static int act_essen(string args)
+{
+    // Nur ein Demo-Objekt
+    if (!IS_LEARNING(this_player()))
+    {
+      write("Schwups, nun ist @@name@@ verschwunden. War wohl eine Illusion.");
+      remove(1);
+    }
+
+    // Der Syntaxtest ist unnoetig, der ist schon im new style
+    // AddCmd() eingebaut.
+
+    // Wenn das Objekt ein Environment hat (hoff ich doch)
+    // und das Environment nicht this_player() ist, 
+    // hat der Spieler das Obst nich im Inv - das geht doch nicht ...
+    if (environment(this_object()) && (environment(this_object()) != this_player()) )
+    {
+      write(BS78(
+        "Du solltest "+ this_object()->name(WER,1) + " erstmal nehmen."
+      ));
+      return 1;
+    }
+
+    // DEBUG ("Satt?");
+    if (this_player()->eat_food(SAETTIGUNG))
+    {
+      // DEBUG("Noe!");
+      if (this_player()->check_and_update_timed_key(EAT_DELAY,"vanion_zach_obst")==-1)
+      {
+        write(BS78(
+          "Du isst genuesslich "+this_object()->name(WEN,0)+
+          ". Du fuehlst Dich gestaerkt."
+        ));
+        say(BS78(
+          this_player()->name()+" isst mit vollem Genuss "+this_object()->name(WEN,0)+". "
+          "Dir laeuft das Wasser im Munde zusammen."
+        ));
+        // Heilen und aufessen :)
+        this_player()->heal_self(HEILUNG);
+      }
+      else { // if (check_and_update_timed_key())
+        // Wenn es noch zu frueh ist, wieder welches zu essen
+        // Keine Heilung, aber Saettigung
+        write(BS78(
+          "Du solltest "+this_object()->name(WEN,1)+" lieber jemandem geben, "
+          "den Du magst, statt alles selbst zu futtern, findest Du nicht? "
+          "Deine Gier hat Dir jetzt nur einen vollen Bauch und ein schlechtes "
+          "Gewissen bescheert."
+        ));
+      } // end if (check_and_update_timed_key())
+      if (!remove()) destruct(this_object());
+    } // end of eat_food
+    return 1;
+}
diff --git a/doc/beispiele/Praxis-Kurs/README b/doc/beispiele/Praxis-Kurs/README
new file mode 100644
index 0000000..99a8dd0
--- /dev/null
+++ b/doc/beispiele/Praxis-Kurs/README
@@ -0,0 +1,16 @@
+In diesem Verzeichnis befindet sich ein kleiner "Praxis LPC-Kurs".
+Im Prinzip sind es verschiedene gut dokumentierte LPC-Objekte die
+aufeinander aufbauen und in denen alles notwendige erklaert wird.
+Wer also schon mal irgendwo programmiert hat und sich nur an die
+LPC-Umgebung gewoehnen muss, fuer den ist dieser Mini-Kurs sicher
+genau das richtige. Fuer jemand der noch nie programmiert hat ist
+dieser Kurs alleine aber sicher nicht geeignet um programmieren zu
+lernen. Die Files sollten in der Alphabetischer Reihenfolge und da
+die Files aufeinander aufbauen, auch vollstaendig durchgegangen
+werden.
+Bei dem Verzeichnis zauberwald handelt es sich um ein kleines und
+dadurch ueberschaubares Gebiet von mir, das nicht eine Ansammlung
+von AddDetails darstellt, sondern auch komplexere Funktionen wie
+Virtual Compiler oder Heilstellen enthaelt.
+
+                                             Padreic
diff --git a/doc/beispiele/Praxis-Kurs/hello_world.c b/doc/beispiele/Praxis-Kurs/hello_world.c
new file mode 100644
index 0000000..8109e06
--- /dev/null
+++ b/doc/beispiele/Praxis-Kurs/hello_world.c
@@ -0,0 +1,37 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+// Auch wenn dieses Objekt noch keinerlei tieferen Sinn hat, so ist es doch
+// bereits ein vollstaendiges Objekt (bereits eine einzige Funktion reicht
+//  fuer ein LPC-Object aus).
+
+// Die 3 Funktionen in diesem Objekt haben darueber hinaus noch eine
+// ganz besondere Bedeutung. Jede dieser 3 Funktionen wird vom Game-Driver
+// im laufenden Betrieb bei besonderen Anlaessen aufgerufen.
+
+protected void create()
+// Diese Funktion wird aufgerufen sobald dieses Objekt geclont wird.
+{
+   // gibt an den Spieler der dieses Objekt clont eine Meldung aus.
+   tell_object(this_player(), "Hello World!\n");
+}
+
+void init()
+// Diese Funktion wird aufgerufen, sobald ein Spieler oder ein NPC in
+// Reichweite dieses Objektes kommt: Also entweder wenn ein Lebewesen in
+// das Objekt bewegt wird oder umgekehrt oder wenn ein Lebewesen in die
+// Umgebung eines Objektes kommt oder umgekehrt.
+{
+   // gibt wieder eine Meldung an den Spieler aus...
+   tell_object(this_player(), ("Hello again :).\n");
+}
+
+void reset()
+// Diese Funktion wird im beruemten Reset aufgerufen, den Du ja sicher auch
+// schon als Spieler kennengelernt hast (Also alle 30-60 Minuten).
+// Ausnahme: Wenn das Objekt laengere Zeit mit keinem anderen Objekt
+// kommuniziert hat, wird diese Funktion vorlaeufig nicht mehr aufgerufen!
+{
+  // Hier kann man nun auch irgendetwas machen. Ein write("..") haette
+  // hier jedoch keinen Effekt, da kein Spieler diese Funktion ausgeloest
+  // hat und daher nicht bekannt ist, an wen die Meldung denn gehen soll...
+}
diff --git a/doc/beispiele/Praxis-Kurs/juwelen.c b/doc/beispiele/Praxis-Kurs/juwelen.c
new file mode 100644
index 0000000..c1070a6
--- /dev/null
+++ b/doc/beispiele/Praxis-Kurs/juwelen.c
@@ -0,0 +1,66 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+// Dieses ist ein erstes Objekt das so oder aehnlich tatsaechlich im
+// MorgenGrauen vorkommen koennte.
+
+// Jedes Objekt im MorgenGrauen braucht bereits eine solche Vielzahl von
+// Standardfunktionen, dass es ziemlich unmoeglich ist, diese alle in jedem
+// Objekt zu definieren. Daher "erbt" man diese Funktionen von einem
+// Standardobjekt.
+// "Erben" bedeutet hierbei, das man auf alle Funktionen, die bereits im
+// Standardobjekt definiert sind, zugreifen kann.
+//
+// Die folgende Zeile erklaert, dass man das Objekt an der Position
+//  /std/thing.c "erben" moechte.
+inherit "/std/thing";
+
+
+// Die Funktion folgender Zeile kann man sich recht einfach wie folgt
+// vorstellen: Wenn der Game-Driver dieses Objekt hier compiliert, dann
+// tut er so, als wuerde die Datei /sys/properties.h genau an dieser Stelle
+// hier im Code stehen.
+//
+// Die Datei properties.h beinhaltet einfach nur haufenweise Defines fuer
+// die haufenweise Properties.
+//
+// Anmerkung: Alle Defines in dieser Datei starten uebrigens mit einem 'P_'.
+// Am Besten solltest Du einfach mal kurz in /doc/properties.h reinsehn
+// und ein bisschen rumlesen
+//  (keine Angst, die muss man nicht alle kennen :).
+#include <properties.h>
+
+protected void create()
+{
+  // Die Funktion create() wurde naemlich bereits in /std/thing.c
+  // beschrieben, kann aber nicht mehr ueber create() aufgerufen werden, da
+  // dann ja wieder diese Funktion hier aufgerufen wuerde
+  // (wodurch wir dann eine Endlos-Rekursion haetten).
+  // Kurzschreibweise waere ::create(); wenn man nur von einem Objekt erbt.
+  thing::create(); // oder auch einfach ::create();
+
+  // Auch diese Funktion wurde aus /std/thing geerbt.
+  // SetProp() ist eine der wichtigsten Funktionen ueberhaupt, wenn es darum
+  // geht, ein Objekt zu beschreiben. Mit dieser Funktion kannst Du naemlich
+  // zahlreiche Eigenschaften (engl. properties) des Objektes festlegen.
+  SetProp(P_SHORT, "Einige wertvolle Juwelen");      // die Kurzbeschreibung
+  SetProp(P_LONG, "Die Juwelen glitzern verlockend.\n"); // Langbeschreibung
+  SetProp(P_NAME, "Juwelen"); // der eigentliche Name
+  SetProp(P_NAME_ADJ, "wertvoll"); // das Adjektiv vor dem Namen
+  SetProp(P_GENDER, FEMALE); // Geschlecht (kann auch MALE oder NEUTER sein)
+  SetProp(P_WEIGHT, 5000); // dieses Objekt wiegt 5kg
+  SetProp(P_VALUE, 10000); // das Objekt ist insgesamt 10000 Muenzen Wert
+  
+  // Mit dieser Funktion kann man eine id angeben, ueber die das Objekt
+  // ansprechbar ist.
+  // Anmerkung: Auch diese Funktion ist wieder von std/thing geerbt, im
+  //   folgenden werde ich dies aber nicht mehr bei jeder Funktion extra
+  //   angeben. Wenn nicht anders angegeben, handelt es sich ab jetzt
+  //   naemlich _immer_ um ererbte Funktionen.
+  AddId("juwelen");
+  
+  // In diesem Fall benoetigt man noch ein weiteres Adjektiv, da man das
+  // Objekt ja auch mit wertvolle Juwelen ansprechen koennen moechte.
+  // z.B.: unt wertvolle juwelen
+  //       gib wertvolle juwelen an padreic
+  AddAdjective("wertvolle");
+}
diff --git a/doc/beispiele/Praxis-Kurs/los.c b/doc/beispiele/Praxis-Kurs/los.c
new file mode 100644
index 0000000..f3b41e8
--- /dev/null
+++ b/doc/beispiele/Praxis-Kurs/los.c
@@ -0,0 +1,118 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include <properties.h> // wieder unsere allgemeinen Propertys
+#include <moving.h>     // einige Defines zum Bewegen von Objekten (s.u.)
+
+inherit "/std/thing";
+
+protected void create()
+{
+  ::create();
+   // Objekt ueber seine Properties beschreiben...
+   SetProp(P_SHORT, "Ein Rubbellos");
+   SetProp(P_LONG,
+    "Du kannst dieses Los aufrubbeln um zu sehen, ob Du einen grossen "
+    "Schatz\ngewonnen hast.\n");
+   SetProp(P_NAME, "Rubbellos");
+   SetProp(P_ARTICLE, 1); // ist eigentlich bereits Defaultwert
+   SetProp(P_GENDER, NEUTER);
+   SetProp(P_VALUE, 1000);
+   SetProp(P_WEIGHT, 500);
+   SetProp(P_INFO, "Deine Gewinnchancen bei diesen Losen sind nicht "
+                   "sonderlich gut.\n");
+   SetProp(P_MAGIC, 0); // (einfach nur, um mal alle Properties zu benutzen)
+   AddId("los"); // noch eine id angeben...
+
+   // Mit Hilfe dieser neuen wichtigen Funktion ist es moeglich, Befehle zu
+   // definieren, ueber die man irgendetwas ausloesen koennen soll:
+   // Und zwar wird sobald ein Spieler den Befehl rubble oder rubbel
+   // benutzt, die Funktion _rubbeln() hier im Objekt aufgerufen. Da diese
+   // Funktion bisher ja noch nicht definiert ist, muss diese natuerlich
+   // auch noch programmiert werden.
+   AddCmd(({"rubble", "rubbel"}), "_rubbeln");
+}
+
+// Diese Funktion sieht gleich um einiges komplexer aus als die Funktionen
+// create(), init() und reset(), die bisher benutzt wurden.
+// Das static am Anfang erklaert nur, dass diese Funktion nicht von aussen,
+// sondern nur von innerhalb des Objektes aufgerufen werden kann.
+// Das "int" anstelle des "void" bedeutet, dass die Funktionen ein Ergebnis
+// zurueckliefert. Da diese Funktion in Verbindung mit AddCmd() benutzt
+// wird, hat dieser Rueckgabewert auch eine besondere Bedeutung:
+//   Eine 1 bedeutet, dass der Befehl fertig abgearbeitet wurde.
+//   Eine 0 bedeutet, das Objekt konnte mit dem Befehl nichts anfangen, der
+//    Gamedriver muss also noch in weiteren Objekten anfragen, die der
+//    Spieler bei sich traegt.
+// Der String 'str', den die Funktion als Parameter uebergeben bekommt,
+// enthaelt das, was der Spieler ausser dem eigentlichen Verb eingegeben
+// hat (Woerter wie 'und', 'ein', 'der', 'die', 'das' ... werden hierbei
+//  zuvor herausgefiltert)
+// Bsp.:   rubbel los      ->  _rubbeln("los")
+//         rubbel katze    ->  _rubbeln("katze")
+//         rubbel los auf  ->  _rubbeln("los auf")
+public int _rubbeln(string str)
+{
+   // Die Funktion notify_fail() ist wieder eine Funktion des Gamedrivers:
+   // Und zwar ist es moeglich hier eine Meldung anzugeben, die anstelle
+   // eines "Wie bitte?" kommt, falls kein Objekt den Befehl richtig
+   // abgearbeitet hat.
+   notify_fail("Syntax: rubbel los auf\n"); // eigentlich efun::notify_fail()
+
+   // Wenn der uebergebene String ungleich "los auf" ist, dann fuehlt sich
+   // das Objekt nicht angesprochen, kann das Verb also nicht komplett
+   // abarbeiten und gibt deshalb eine 0 zurueck.
+   if (str!="los auf") return 0;
+
+   // Auch die Funktion random() ist wieder eine Funktion des Gamedriver:
+   // Und zwar liefert sie eine ganzzahlige Zufallszahl zwischen 0 und n-1
+   //  (wobei n die uebergebene Zahl ist). In diesem Fall also zwischen
+   // 0 und 99.
+   if (random(100)<92) { // sollte diese Zahl < 92 sein, dann tue Folgendes:
+     write("Du rubbelst das Los auf. Leider war es jedoch nur eine Niete.\n"
+          +"Veraergert laesst Du das Los deshalb fallen.\n");
+   }
+   else { // sollte die Zahl nicht < 92 gewesen sein, dann tue Folgendes:
+     object ob; // bereitstellen einer Hilfsvariablen
+
+     write("Du rubbelst das Los auf und strahlst vor Freude. Es war der "
+          +"absolute Hauptgewinn.\nSofort kommt ein Bote herein und "
+          +"ueberreicht Dir Deinen Gewinn.\n");
+
+     // Mit clone_object() kann man ein beliebiges Objekt Clonen, indem man
+     // einfach den entsprechenden Filenamen als Parameter uebergibt.
+     ob=clone_object("/doc/beispiele/Praxis-Kurs/juwelen");
+
+     // Nun kommen gleich 3 neue Dinge auf einmal...
+     // Mit -> wird eine Funktion in einem anderen Objekt aufgerufen
+     //  (alternativ auch: call_other(ob,move,this_player(), M_GET); )
+     // Die Funktion, die hierbei aufgerufen werden soll, heisst move().
+     // Diese Funktion ist in allen Objekten definiert, die /std/thing erben.
+     // Somit koennen wir also fest davon ausgehen, dass sie auch in unseren
+     // Juwelen vorhanden ist.
+     // this_player() ist eine sehr wichtige Gamedriver-Funktion, da sie
+     // uns das Lebewesen zurueckliefert, das gerade aktiv durch einen Befehl
+     // den aktuellen Funktionsaufruf gestartet hat.
+     // M_GET ist lediglich ein Define, das in moving.h definiert ist.
+     // Naeheres hierzu findet man auch in <man move>.
+     ob->move(this_player(), M_GET);
+     // ACHTUNG: um dieses Beispiel simpel zu halten, wird hier darauf
+     // verzichtet, zu pruefen, ob diese Bewegung ueberhaupt funktioniert
+     // hat. Normalerweise muss man in diesem Fall (misslungene Bewegung)
+     // eine Fehlerbehandlung durchfuehren (z.B. ob wieder entfernen,
+     // Meldung an den Spieler ausgeben).
+   }
+
+   // Die Funktion say() gibt an alle Spieler im Raum des aktiven Spielers
+   // eine Nachricht aus, aber nicht an den betroffenen Spieler selbst!
+   // Die Funktion Name(), die hier im Spieler aufgerufen wird, wertet
+   // dessen Propertie P_NAME aus und dekliniert den Namen im angegebenen
+   // Fall.
+   say(this_player()->Name(WER)+" rubbelt ein Los auf.\n");
+
+   // Und nun soll sich das Objekt selbst entfernen (zerstoeren).
+   remove();
+
+   // Da der Befehl ja komplett abgearbeitet wurde, geben wir eine 1
+   // zurueck.
+   return 1;
+}
diff --git a/doc/beispiele/Praxis-Kurs/milch.c b/doc/beispiele/Praxis-Kurs/milch.c
new file mode 100644
index 0000000..66b6173
--- /dev/null
+++ b/doc/beispiele/Praxis-Kurs/milch.c
@@ -0,0 +1,91 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+// Anmerkung: Heutzutage gibt es fuer Lebensmittel (bzw. alle Dinge, die
+// man essen oder trinken kann) ein Standardobjekt namens /std/food,
+// welches viele Funktionalitaet mitbringt, die man ansonsten selber
+// muehsam basteln muss. Die Verwendung ist dringend empfohlen.
+
+#include <properties.h> // wieder unsere allgemeinen Properties
+
+inherit "/std/thing";
+
+// globale Variable, die angibt, wie voll die Flasche ist. Es sind 5
+// Portionen enthalten.
+int menge = 5;
+
+protected void create()
+{
+   ::create();
+   // Objekt ueber seine Properties beschreiben...
+   // P_SHORT und P_LONG werden direkt als Funktion implementiert (s.u.)
+   SetProp(P_NAME, "Milchflasche");
+   SetProp(P_GENDER, FEMALE);
+   SetProp(P_VALUE, 80);
+   SetProp(P_WEIGHT, 1000);
+   AddId(({"flasche", "milchflasche"}));
+   AddCmd(({"trink", "trinke"}), "_trinken");
+}
+
+// Anstelle von P_SHORT Kurzbeschreibung ueber Funktion short().
+// Dies ist nicht immer moeglich, da nicht zwangslaeufig zu jeder Propertie
+// eine gleichnamige Funktion existiert.
+// Es ist allerdings noch sog. Querymethoden (siehe dazu <man QueryProp> und
+// <man Query> bei Bedarf).
+public string short()
+{
+   string str;
+   switch(menge) {
+     case 1: str="Eine fast leere Milchflasche.\n"; break;
+     case 2: str="Eine bald leere Milchflasche.\n"; break;
+     case 3: str="Eine halbvolle Milchflasche.\n";  break;
+     case 4: str="Eine fast volle Milchflasche.\n"; break;
+     case 5: str="Eine volle Milchflasche.\n";      break;
+     default: str="Eine leere Milchflasche.\n";
+   }
+   return str;
+}
+
+public string long()
+{
+   if (menge>1)
+      return "Eine Flasche mit leckerer Vollmilch.\n";
+   else
+      return "Die Milchflasche ist bald leider schon alle, Du solltest "
+             "Dich vielleicht mal\nnach Nachschub umsehn.\n";
+}
+
+public int _trinken(string str)
+{
+   notify_fail("Syntax: trinke aus milchflasche\n");
+
+   // Falls die ersten vier Zeichen in dem uebergebenen String ungleich
+   // "aus " sind, dann fuehlt sich das Objekt nicht angesprochen, kann das
+   // Verb also nicht komplett abarbeiten und gibt deshalb eine 0 zurueck.
+   if (str[0..3]!="aus ") return 0;
+
+   // Ich benutze nun alles ab dem vierten Zeichen und pruefe, ob sich das
+   // Objekt davon angesprochen fuehlt. Hierzu dient die Funktion id().
+   // Grosser Vorteil: Uebersichtlich und spaeteres Ergaenzen von Ids bzw.
+   // Adjektiven geht sehr einfach und erspart lange if()-Abfragen.
+   if ( !id(str[4..]) ) return 0;
+
+   if (menge<=0) {
+     write("Die Milchflasche ist leider schon leer :(.\n");
+   }
+   else {
+     // drink_soft() ist eine Funktion, die in allen Lebewesen im
+     // Morgengrauen definiert ist und ueber die man sowohl Abfragen als
+     // auch hochsetzen von P_DRINK vornehmen sollte.
+     if (this_player()->drink_soft(10)) {
+
+       // heal_self() ist auch in allen Lebewesen definiert und heilt bei
+       // dem Lebewesen Lebens- und Konzentrationspunkte.
+       this_player()->heal_self(10);
+       write("Genuesslich trinkst Du einen Schluck Milch.\n");
+       say(this_player()->Name(WER)+" trinkt einen Schluck Vollmilch,\n");
+       menge--;
+     }
+     else write("Soviel kannst Du leider nicht mehr trinken.\n");
+   }
+   return 1;
+}
diff --git a/doc/beispiele/Praxis-Kurs/npc.c b/doc/beispiele/Praxis-Kurs/npc.c
new file mode 100644
index 0000000..39a54fd
--- /dev/null
+++ b/doc/beispiele/Praxis-Kurs/npc.c
@@ -0,0 +1,115 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include <properties.h>
+#include <attributes.h>
+#include <combat.h>
+#include <guard.h>
+#include <moving.h>
+#include <new_skills.h>
+
+// Ein NPC wird nicht von /std/thing direkt geerbt, sondern von /std/npc.
+// Da /std/npc aber seinerseits wieder von /std/thing erbt, sind im NPC auch
+// saemtliche Funktionen definiert, die bereits in /std/thing enthalten sind.
+inherit "/std/npc";
+
+void create()
+{
+   ::create();
+   // create_default_npc() setzt schon mal einige wichtige Properties wie
+   // P_ATTRIBUTES automatisch...
+   create_default_npc(20, 300);
+
+   // Keine Angst: Es muessen keineswegs immer alle diese Properties benutzt
+   // werden, auch wenn ich sie jetzt einmal alle angebe, um eine Uebersicht
+   // zu geben, was man alles ueber Properties steuern kann...
+   SetProp(P_SHORT, "Ein fieser Ork");
+   SetProp(P_LONG, "Der Ork schaut Dich grimmig an.\n");
+   SetProp(P_NAME, "Ork");
+   SetProp(P_NAME_ADJ, "fies");
+   SetProp(P_GENDER, MALE);
+   SetProp(P_MAGIC, 0);
+   SetProp(P_MATERIAL, MAT_MISC_LIVING);
+
+   // nun komen einige wirklich neue Properties
+   SetProp(P_INFO, "Der Ork ist ein ganz gemeiner... :)\n");
+   SetProp(P_ALIGN, -500); // Alignment des Gegners
+   SetProp(P_RACE, "Ork"); // Rasse des NPCs
+   SetProp(P_AGGRESSIVE, 1); // greift aggressiv jeden an
+   SetProp(P_ATTRIBUTES, ([A_INT:5, A_DEX:10, A_STR:25, A_CON:10]));
+   SetProp(P_SIZE, 134); // Groesse in cm
+   SetProp(P_MAX_HP, 300); // max. Lebenspunkte
+   SetProp(P_HP, 300); // Lebenspunkte
+   SetProp(P_MAX_SP, 200); // max. Magiepunkte
+   SetProp(P_SP, 200); // Magiepunkte
+   SetProp(P_BODY, 80); // Grundruestungsschutz des Koerpers
+   SetProp(P_MAX_HANDS, 2); // kaempft mit zwei Haenden...
+   SetProp(P_HANDS, ({" mit blossen Haenden", 150}));
+   SetProp(P_NOMAGIC, 20); // 20% Resistenz gegen Spells
+   SetProp(P_GUARD, 20); // Der NPC laesst sich ggf. weglocken
+   SetProp(P_NO_GLOBAL_ATTACK, 0); // wird von 'toete alles' erfasst
+   SetProp(P_NO_ATTACK, 0);  // kann man den NPC ueberhaupt angreifen?
+   SetProp(P_FRIEND, 0); // soll der NPC in Spells als Freund erfasst
+                         // werden?
+   SetProp(P_RESISTANCE, DT_FIRE);  // Resistenzen
+   SetProp(P_VULNERABILITY, DT_HOLY);  // Empfindlichkeiten
+   SetProp(P_FOOD, 0);
+   SetProp(P_MAX_FOOD, 100);
+   SetProp(P_DRINK, 0);
+   SetProp(P_MAX_DRINK, 100);
+   SetProp(P_ALCOHOL, 0);
+   SetProp(P_MAX_ALCOHOL, 100);
+   SetProp(P_XP, QueryProp(P_MAX_HP)*QueryProp(P_HANDS)[0]*5); // Erfahrung
+   SetProp(P_MURDER_MSG, "Ich komme doch eh wieder!\n"); // Moerdermeldung
+   SetProp(P_KILL_NAME, "Ein fieser Testork"); // noetig falls != P_NAME
+   SetProp(P_KILL_MSG, "Das kommt davon wenn man sich mit mir anlegt.\n");
+   SetProp(P_DIE_MSG, "Der Ork schreit ein letztes mal laut auf.\n");
+   SetProp(P_NOCORPSE, 0); // soll der NPC eine Leiche hinterlassen?
+   SetProp(P_HEAL, -10); // wieviel LP bekommt man beim Leichen essen...
+
+   // der Ork ist Mitglied der Abenteurergilde...
+   SetProp(P_GUILD, "abenteurer"); // in die Abenteuergilde mit ihm...
+   SetProp(P_GUILD_LEVEL, 20);
+   ModifySkill("feuerball",([SI_SKILLABILITY:10000]),1,"abenteurer");
+
+   // nun noch eine andere Art zu zaubern...
+   SetProp(P_SPELLRATE, 10);
+   AddSpell(100, 400,
+     "Der Ork tritt Dich einmal feste zwischen die Beine.\n",
+     "Der Ork tritt @WEN einmal feste zwischen die Beine.\n", DT_BLUDGEON);
+
+   // was soll der NPC mit ihm gegebenen Gegenstaenden machen...
+   SetProp(P_REJECT, ({REJECT_DROP, "Damit kann ich nichts anfangen.\n"}));
+
+   // und natuerlich auch noch eine id fuer den ork
+   AddId("ork");
+
+   // Der NPC hat immer einige Juwelen bei sich.
+   AddItem("/doc/beispiele/Praxis-Kurs/juwelen");
+}
+
+// Diese Funktion wird aufgerufen, wenn man einen Gegenstand bekommt...
+public void give_notify(object ob)
+{
+   if (ob->id("milchflasche")) // Milch annehmen und einen Spruch aufsagen
+     write("Der Ork sagt: Ohh.... Danke fuer die leckere Milch.\n");
+   else ::give_notify(ob); // Gegenstand fallen lassen
+}
+
+// Diese Funktion wird aufgerufen, wenn uns jemand im Kampf Schaden zufuegen
+// moechte...
+public varargs int Defend(int dam, mixed dam_type, mixed spell, object enemy)
+{
+   if (!random(4))
+     write("Der Ork weicht Deinem Angriff geschickt aus.\n");
+   else
+     return ::Defend(dam, dam_type, spell, enemy);
+}
+
+public void Attack(object enemy)
+{
+  // das Kommando 'Feuerball' eingeben. Da der NPC Mitglied der
+  // Abenteurergilde ist (s.o.), spricht er also den Spell, falls er genug
+  // Magiepunkt dafuer hat.
+  if (!random(3)) command_me("feuerball");
+  ::Attack(enemy);
+}
diff --git a/doc/beispiele/fernwaffen/kurzbogen.c b/doc/beispiele/fernwaffen/kurzbogen.c
new file mode 100644
index 0000000..93623e5
--- /dev/null
+++ b/doc/beispiele/fernwaffen/kurzbogen.c
@@ -0,0 +1,32 @@
+inherit "/std/ranged_weapon";
+
+#include "./path.h"
+#include <properties.h>
+
+void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+
+  SetProp(P_SHORT, "Ein Kurzbogen");
+  SetProp(P_INFO,
+    "Die Syntax lautet: schiesse <geschoss> auf <ziel>\n");
+  SetProp(P_NAME, "Kurzbogen");
+  SetProp(P_LONG, break_string(
+    "Mit diesem Kurzbogen kann man sicher nicht weit schiessen."));
+  SetProp(P_MATERIAL, MAT_MISC_WOOD);
+
+  AddId("kurzbogen");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_WEIGHT, 800);
+  SetProp(P_VALUE, 300);
+
+  SetProp(P_WC, 10);
+  SetProp(P_DAM_TYPE, DT_BLUDGEON);
+
+  SetProp(P_SHOOTING_WC, 60);
+  SetProp(P_NR_HANDS, 2);
+  SetProp(P_WEAPON_TYPE, WT_RANGED_WEAPON);
+  SetProp(P_AMMUNITION, MUN_ARROW);
+  SetProp(P_STRETCH_TIME, 1);
+  SetProp(P_RANGE, 10);
+}
\ No newline at end of file
diff --git a/doc/beispiele/fernwaffen/langbogen.c b/doc/beispiele/fernwaffen/langbogen.c
new file mode 100644
index 0000000..7956dd3
--- /dev/null
+++ b/doc/beispiele/fernwaffen/langbogen.c
@@ -0,0 +1,32 @@
+inherit "/std/ranged_weapon";
+
+#include "./path.h"
+#include <properties.h>
+
+void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+
+  SetProp(P_SHORT, "Ein Langbogen");
+  SetProp(P_INFO,
+    "Die Syntax lautet: schiesse <geschoss> auf <ziel>\n");
+  SetProp(P_NAME, "Langbogen");
+  SetProp(P_LONG, break_string(
+    "Mit diesem Langbogen kann man bestimmt hervorragend weit schiessen."));
+  SetProp(P_MATERIAL, MAT_MISC_WOOD);
+
+  AddId("langbogen");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_WEIGHT, 800);
+  SetProp(P_VALUE, 300);
+
+  SetProp(P_WC, 30);
+  SetProp(P_DAM_TYPE, DT_BLUDGEON);
+
+  SetProp(P_SHOOTING_WC, 120);
+  SetProp(P_NR_HANDS, 2);
+  SetProp(P_WEAPON_TYPE, WT_RANGED_WEAPON);
+  SetProp(P_AMMUNITION, MUN_ARROW);
+  SetProp(P_STRETCH_TIME, 2);
+  SetProp(P_RANGE, 30);
+}
\ No newline at end of file
diff --git a/doc/beispiele/fernwaffen/npc.c b/doc/beispiele/fernwaffen/npc.c
new file mode 100644
index 0000000..3544d44
--- /dev/null
+++ b/doc/beispiele/fernwaffen/npc.c
@@ -0,0 +1,29 @@
+inherit "/std/npc";
+
+#include "./path.h"
+#include <properties.h>
+
+void create() {
+  ::create();
+  AddId(({"npc", "gegner", "ziel"}));
+  SetProp(P_NAME, "Ziel");
+  SetProp(P_GENDER, NEUTER);
+  SetProp(P_SHORT, "Ein Ziel");
+
+  SetProp(P_HP, 1000);
+  SetProp(P_BODY, 100);
+}
+
+varargs int Defend(int dam, string* dam_type, mixed spell, object enemy) {
+  int predam = dam;
+  int result = ::Defend(dam, &dam_type, &spell, enemy);
+  
+  string str = break_string(sprintf(
+    "Die Zielscheibe sagt: %d Schaden rein und fuer %d getroffen.",
+    predam, result), 78);
+
+  tell_room(load_object(__PATH__(0)"zielraum"), str);
+  tell_room(load_object(__PATH__(0)"schussraum"), str);
+
+  return result;
+}
\ No newline at end of file
diff --git a/doc/beispiele/fernwaffen/path.h b/doc/beispiele/fernwaffen/path.h
new file mode 100644
index 0000000..d284140
--- /dev/null
+++ b/doc/beispiele/fernwaffen/path.h
@@ -0,0 +1,3 @@
+#pragma rtt_checks, save_types
+#pragma pedantic,  range_check
+#pragma warn_empty_casts, warn_missing_return, warn_function_inconsistent
\ No newline at end of file
diff --git a/doc/beispiele/fernwaffen/pfeile.c b/doc/beispiele/fernwaffen/pfeile.c
new file mode 100644
index 0000000..f4b11a3
--- /dev/null
+++ b/doc/beispiele/fernwaffen/pfeile.c
@@ -0,0 +1,41 @@
+inherit "/std/unit";
+
+#include "./path.h"
+#include <properties.h>
+#include <class.h>
+
+void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+
+  SetProp(P_NAME, ({"Pfeil", "Pfeile"}) );
+  SetProp(P_LONG, break_string(
+    "Ein paar einfache Holzpfeile.", 78));
+  SetProp(P_GENDER, MALE);
+  SetProp(P_AMOUNT, 1);
+  SetProp(P_SHOOTING_WC, 40);
+  SetProp(P_DAM_TYPE, ({DT_PIERCE}));
+  SetProp(P_WEAPON_TYPE, WT_AMMU);
+  SetProp(P_MATERIAL, MAT_MISC_WOOD);
+
+  SetGramsPerUnits(120,1);
+  SetCoinsPerUnits(25,1);
+
+  AddId(MUN_ARROW);
+  AddSingularId("pfeil");
+  AddPluralId("pfeile");
+  AddClass(CL_AMMUNITION);
+
+  SetProp(P_HIT_FUNC, this_object());
+}
+
+int HitFunc(object enemy) {
+  if(!random(5)) {
+    tell_object(environment(), break_string(
+      "Der Pfeil schnurrt vom Bogen und dabei splittert das Holz "
+      "etwas. Gratiswiderhaken, das ist ja toll!", 78));
+    return 20;
+  }
+
+  return 0;
+}
\ No newline at end of file
diff --git a/doc/beispiele/fernwaffen/schussraum.c b/doc/beispiele/fernwaffen/schussraum.c
new file mode 100644
index 0000000..79179c9
--- /dev/null
+++ b/doc/beispiele/fernwaffen/schussraum.c
@@ -0,0 +1,34 @@
+inherit "/std/room";
+
+#include "./path.h"
+#include <properties.h>
+
+void create() {
+  ::create();
+
+  SetProp(P_LIGHT, 1);
+  SetProp(P_INT_SHORT, "Auf einem Baum");
+  SetProp(P_INT_LONG, break_string(
+    "Du hockst auf einem Baum und kannst auf die Lichtung unter Dir sehen. "
+    "'schau runter' hilft sicherlich, Ziele auszumachen."));
+
+  AddDetail("runter", function string { 
+                        return __PATH__(0)"zielraum"->int_long(this_player());
+                      });
+
+  AddItem(__PATH__(0)"langbogen", REFRESH_REMOVE);
+  AddItem(__PATH__(0)"kurzbogen", REFRESH_REMOVE);
+  AddItem(__PATH__(0)"pfeile", REFRESH_REMOVE, ([P_AMOUNT: 20]));
+
+  AddExit("zielraum", __PATH__(0)"zielraum");
+  load_object(__PATH__(0)"zielraum");
+
+  SetProp(P_TARGET_AREA, __PATH__(0)"zielraum"); // anderer Raum beschiessbar
+  SetProp(P_SHOOTING_AREA, 15);                  // 15 Entfernung
+}
+
+// nur wegen des P_NEVER_CLEAN im Zielraum und nur hier in doc relevant
+public varargs int remove(int silent) {
+  __PATH__(0)"zielraum"->remove();
+  return ::remove();
+}
diff --git a/doc/beispiele/fernwaffen/zielraum.c b/doc/beispiele/fernwaffen/zielraum.c
new file mode 100644
index 0000000..6177032
--- /dev/null
+++ b/doc/beispiele/fernwaffen/zielraum.c
@@ -0,0 +1,19 @@
+inherit "/std/room";
+
+#include "./path.h"
+#include <properties.h>
+
+void create() {
+  ::create();
+
+  SetProp(P_LIGHT, 1);
+  SetProp(P_INT_SHORT, "Auf einer Lichtung");
+  SetProp(P_INT_LONG, break_string(
+    "Auf dieser Lichtung steht ein Baum, der verdaechtig viele Aeste hat, "
+    "von denen man auf die Lichtung gut sehen kann."));
+  SetProp(P_NEVER_CLEAN, 1); // damit der Raum auch da bleibt
+
+  AddItem(__PATH__(0)"npc", REFRESH_REMOVE);
+
+  AddExit("schussraum", __PATH__(0)"schussraum");
+}
diff --git a/doc/beispiele/food/banane.c b/doc/beispiele/food/banane.c
new file mode 100644
index 0000000..8882f4f
--- /dev/null
+++ b/doc/beispiele/food/banane.c
@@ -0,0 +1,114 @@
+/*
+Beispiel fuer eine Banane, die in einem Haps weg ist,
+aber eine Schale hat, die uebrig bleibt und genau wie die
+komplette Banane vergammelt und vergeht.
+*/
+
+#include <food.h>
+
+inherit "/std/food";
+
+string _query_long() {
+  string m = "Du haeltst ";
+  if (is_not_empty()) {
+    m += "eine Banane in der Hand.";
+    if (!is_bad())
+      m += " Sie ist schoen gelb und ohne braune Flecken.";
+    else
+      m += " Sie ist total braun und schon ganz matschig.";
+
+  } else {
+    m += "eine Bananenschale in der Hand.";
+    if (is_bad())
+      m += " Sie ist ganz verschimmelt.";
+  }
+  return break_string(m);
+}
+
+void create() {
+  if (!clonep(this_object())) {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+
+  SetProp(P_SHORT,"Eine schoene reife Banane");
+  SetProp(P_NAME, "Banane");
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_VALUE,20); // ohne Schale
+  SetProp(P_WEIGHT,50); // ohne Schale
+  // SetProp(P_POTION,1); // ist eh Standard
+  SetProp(P_CONSUME_MSG,
+    "@WER2 schaelt eine Banane und isst sie genuesslich.");
+  SetProp(P_EATER_MSG,
+    "Du schaelst eine Banane und isst sie genuesslich.");
+  SetProp(P_BAD_MSG,
+    "Die Banane wird ganz matschig und schlecht.");
+  SetProp(P_REMOVE_MSG,
+    "Die Banane ist so verschimmelt, dass Du sie entsorgst.");
+  SetProp(P_EMPTY_MSG,
+    "Aber das ist doch nur eine BananenSCHALE, die willst Du nicht essen!");
+
+  SetProp(P_MATERIAL,([MAT_MISC_PLANT:100]));
+
+  SetProp(P_EMPTY_PROPS, ([
+    P_SHORT:"Eine Bananenschale",
+    P_NAME:"Bananenschale",
+    P_EMPTY_IDS:({"schale","bananenschale","banane"}),
+    P_VALUE:5, // Wert der Bananenschale
+    P_WEIGHT:5  // Gewicht der Bananenschale
+  ]));
+
+  //SetProp(P_RESET_LIFETIME,1); // ist eh Standard
+  SetProp(P_DESTROY_BAD,300); // verdorbene Bananen verschwinden nach 5 Min
+
+  SetProp(P_FOOD,50); // Fuellgrad der Banane
+  SetProp(P_HP,5);
+  SetProp(P_SP,5);
+
+  AddId(({"banane"}));
+
+}
+
+// Der Behaelter vergammelt auch
+public int make_destroy() {
+  if (!::make_destroy()) {
+    return remove(1);
+  }
+  return 1;
+}
+
+public int make_empty() {
+  SetProp(P_BAD_MSG,
+    "Die Bananenschale wird ganz schimmlig.");
+  SetProp(P_REMOVE_MSG,
+    "Die Bananenschale zersetzt sich vollstaendig.");
+  return ::make_empty();
+}
+
+// parent methode immer aufrufen und pruefen!!
+void make_bad() {
+  if (!::make_bad()) {
+    if (is_not_empty()) {
+      SetProp(P_SHORT,"Eine matschige Banane");
+      AddAdjective(({"matschige","matschigen"}));
+      SetProp(P_EATER_MSG,"Du lutschst die matschige Banane aus ihrer"
+        "Schale. Baeh, die schmeckt ueberhaupt nicht mehr.");
+      SetProp(P_CONSUME_MSG,
+        "@WER2 lutscht eine matschige Banane aus ihrer Schale.");
+      // Die Schale ist dann natuerlich auch vergammelt
+      SetProp(P_EMPTY_PROPS, ([
+        P_SHORT:"Eine verschimmelte Bananenschale",
+        P_NAME:"Bananenschale",
+        P_EMPTY_IDS:({"schale","bananenschale","banane"}),
+        P_EMPTY_ADJ:({"verschimmelte","verschimmelten"}),
+        P_VALUE:5, // Wert der Bananenschale
+        P_WEIGHT:5  // Gewicht der Bananenschale
+      ]));
+    } else {
+      message(P_BAD_MSG);
+      SetProp(P_SHORT,"Eine verschimmelte Bananenschale");
+      AddAdjective(({"verschimmelte","verschimmelten"}));
+    }
+  }
+}
diff --git a/doc/beispiele/food/wasserflasche.c b/doc/beispiele/food/wasserflasche.c
new file mode 100644
index 0000000..3c62c13
--- /dev/null
+++ b/doc/beispiele/food/wasserflasche.c
@@ -0,0 +1,92 @@
+/*
+Beispiel fuer ein tragbares Getraenk in einer Flasche.
+*/
+
+#include <food.h>
+
+inherit "/std/food";
+
+string _query_long() {
+  string m = "Du siehst eine Glasflasche,";
+  if (is_not_empty()) {
+    m += " in der sich Wasser befindet.";
+    if (!is_bad())
+      m += " Es ist glasklar und sieht irgendwie verlockend aus.";
+    else
+      m += " Es ist truebe und sieht nicht gerade verlockend aus.";
+
+    m += " Die Flasche ist ";
+    switch (to_int((QueryProp(P_PORTIONS)-1)/2.5)) {
+      case 0:
+        m += "fast leer.";
+        break;
+      case 1:
+        m += "halb voll.";
+        break;
+      case 2:
+        m += "fast voll.";
+        break;
+      case 3:
+        m += "voll.";
+        break;
+      default:
+        m += "nicht leer.";
+    }
+  } else {
+    m += " in der frueher mal Wasser war.";
+  }
+  return break_string(m);
+}
+
+void create() {
+  if (!clonep(this_object())) {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+
+  SetProp(P_SHORT,"Eine Flasche mit Wasser");
+  SetProp(P_NAME, "Wasserflasche");
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_VALUE,5); // pro Portion (ohne Flasche)
+  SetProp(P_WEIGHT,50); // pro Portion (ohne Flasche)
+  SetProp(P_PORTIONS,10);
+  SetProp(P_DESTROY_BAD,0); // verdorbenes Wasser wird nicht zerstoert
+  SetProp(P_CONSUME_MSG,
+    "@WER2 trinkt aus einer Flasche. Wasser laeuft ueber @WENQPPNS2 Kinn.");
+  SetProp(P_EATER_MSG,
+    "Du trinkst Wasser aus der Flasche. Etwas laeuft Dir ueber das Kinn.");
+  SetProp(P_BAD_MSG,
+    "Das Wasser in der Flasche wird truebe.");
+
+  SetProp(P_MATERIAL,([MAT_GLASS:15, MAT_WATER:85]));
+
+  SetProp(P_EMPTY_PROPS, ([
+    P_SHORT:"Eine leere Flasche",
+    P_NAME:"Flasche",
+    P_EMPTY_IDS:({"flasche"}),
+    P_EMPTY_ADJ:({"leere","leeren"}),
+    P_MATERIAL:([MAT_GLASS:100]),
+    P_VALUE:5, // Wert der Flasche ohne Wasser
+    P_WEIGHT:20  // Gewicht der Flasche ohne Wasser
+  ]));
+
+  SetProp(P_RESET_LIFETIME,3); // 3 Resets, ehe das Wasser truebe wird
+  SetProp(P_DRINK,50); // Fuellgrad pro Schluck
+  SetProp(P_DISTRIBUTION,10); // Punkte Heilung pro Heartbeat
+  SetProp(P_HP,30); // pro Schluck
+  SetProp(P_SP,30); // pro Schluck
+
+  AddId(({"flasche","wasserflasche","wasser"}));
+  AddAdjective(({"volle","vollen"}));
+
+}
+
+// parent methode immer aufrufen und pruefen!!
+void make_bad() {
+  if (!::make_bad() && is_not_empty()) {
+    // die Trinkmeldung aendern wir mal
+    SetProp(P_EATER_MSG,"Du trinkst truebes Wasser aus der Flasche. "
+      "Das war wohl nicht so gut. Dir wird ganz uebel!");
+  }
+}
\ No newline at end of file
diff --git a/doc/beispiele/kekse/keks.c b/doc/beispiele/kekse/keks.c
new file mode 100644
index 0000000..f4a2ab5
--- /dev/null
+++ b/doc/beispiele/kekse/keks.c
@@ -0,0 +1,28 @@
+/* Einfaches Beispiel zur Demonstration von Vererbung.
+   ACHTUNG: Dieses Beispiel stellt _kein_ sinnvolles Lebensmittelobjekt dar.
+            Hierfuer sollte /std/food verwendet werden, siehe auch
+            /doc/beispiele/food/
+*/
+
+#include <properties.h>
+#include <moving.h>
+inherit "/std/thing";
+
+protected void create() {
+  ::create();
+  SetProp(P_NAME, "Keks");
+  SetProp(P_GENDER, MALE);
+  AddId(({"keks"}));
+  AddCmd("iss&@ID", "action_essen", "Was willst du essen?");
+}
+
+public int action_essen(string cmd) {
+  if(this_player()->eat_food(1, 0, 
+                            "Du bekommst "+QueryPronoun(WEN)+
+                            " nicht mehr hineingestopft.\n")>0) {
+    write("Du isst "+name(WEN,1)+".\n");
+    say(this_player()->Name(WER)+" isst "+name(WEN)+".\n");
+    remove(1);
+  }
+  return 1;
+}
diff --git a/doc/beispiele/kekse/kruemelkeks.c b/doc/beispiele/kekse/kruemelkeks.c
new file mode 100644
index 0000000..2fee85a
--- /dev/null
+++ b/doc/beispiele/kekse/kruemelkeks.c
@@ -0,0 +1,15 @@
+/* Einfaches Beispiel zur Demonstration von Vererbung.
+   ACHTUNG: Dieses Beispiel stellt _kein_ sinnvolles Lebensmittelobjekt dar.
+            Hierfuer sollte /std/food verwendet werden, siehe auch
+            /doc/beispiele/food/
+*/
+
+#include <properties.h>
+inherit __DIR__"keks";
+
+varargs int remove(int silent) {
+  if(!silent && living(environment()))
+    tell_object(environment(), Name(WER,1)+
+                               " kruemelt ein letztes Mal.\n");
+  return ::remove(silent);
+}
diff --git a/doc/beispiele/master/access_rights.c b/doc/beispiele/master/access_rights.c
new file mode 100644
index 0000000..367e909
--- /dev/null
+++ b/doc/beispiele/master/access_rights.c
@@ -0,0 +1,13 @@
+// Wenn ein Verzeichnis fuer ein Objekt nicht schreibbar ist, kann der
+// Besitzer des Verzeichnisses eine Datei access_rights anlegen, die
+// dieses Recht vergibt.
+// Da DOC hier eigentlich nicht schreiben darf, war das hier noetig.
+// Ein Magier wird das fuer die eigenen Verz. idR nicht brauchen.
+
+int access_rights( string user, string pfad ) {
+  if( user=="DOC" && pfad=="opferstocklog" ) {
+  	// DOC darf opferstocklog schreiben
+	return 1;
+  }
+  return 0;
+}
diff --git a/doc/beispiele/master/opferstock.c b/doc/beispiele/master/opferstock.c
new file mode 100644
index 0000000..048a461
--- /dev/null
+++ b/doc/beispiele/master/opferstock.c
@@ -0,0 +1,142 @@
+#include <defines.h>
+#include <properties.h>
+#include <moving.h>
+//
+// By Rumata@MorgenGrauen 3/99
+//
+// Beispieldatei fuer die Benutzung von Mastern und Klienten.
+//
+// Ich gehe hier nicht auf die "normalen" Funktionen ein.
+
+inherit "/std/thing";
+
+#define OS_MASTER "/doc/beispiele/master/opferstockmaster"
+
+void create()
+{
+	if(IS_BLUE(ME)) return;
+	::create();
+	SetProp( P_NAME, "Opferstock" );
+	SetProp( P_GENDER, MALE );
+	SetProp( P_VALUE, 1000 + random(2000) );
+	AddId( ({"stock","inschrift","opferstock"}) );
+	SetProp( P_SHORT, "In einer Ecke steht ein Opferstock" );
+	SetProp( P_LONG,
+		"Der Opferstock besteht aus solidem Holz. Vorne auf dem Kasten ist eine\n"
+	+	"Inschrift zu sehen, die Du lesen kannst.\n"
+	+	"@@contents@@"
+	);
+	SetProp( P_READ_MSG,
+		">>>>>>>>>>>> Fuer den Aufbau eines Orkwaisenhauses <<<<<<<<<<<<\n"
+	+	"In den letzten Jahren wurden immer wieder unschuldige Orkkinder\n"
+	+	"durch brutale Abenteurer ihrer Eltern beraubt. Bitte unter-\n"
+	+	"stuetzen Sie mit einer kleinen Spende den Aufbau eines Waisen-\n"
+	+	"hauses fuer diese bemitleidenswerten Kreaturen.\n"
+	);
+	AddDetail( "holz", "Solide und so gut wie unzerbrechlich.\n" );
+	SetProp( P_NOGET,
+		"Der Opferstock ist nicht ohne Grund am Boden festgenagelt.\n" );
+	AddCmd( "spende","spende" );
+	AddCmd( ({"stecke","steck"}), "stecken" );
+	AddCmd( "oeffne","oeffne" );
+	AddCmd( ({"brich","breche"}),"breche" );
+}
+
+contents()
+{
+	switch(QueryProp(P_VALUE))
+	{
+	case 0:
+		return "Er ist leer.\n";
+	case 1:
+		return "Er enthaelt:\nEine Muenze.\n";
+	default:
+		return "Er enthaelt:\n"+QueryProp(P_VALUE)+" Muenzen.\n";
+	}
+}
+
+stecken( str )
+{
+	string was, worein;
+	if( !str || sscanf(str,"%s in %s",was,worein)!=2 || !id(worein) ) return 0;
+	return spende( str );
+}
+
+spende( str )
+{
+	int anz, newAl;
+	string arg;
+	
+	notify_fail( "Wieviele Muenzen willst Du denn spenden?\n" );
+	if( !str || str=="" )
+		return 0;
+	if( sscanf(str,"%d %s",anz,arg)== 2 )
+		str = arg;
+	else
+	{
+		if( str=="eine muenze" || str=="ein goldstueck" )
+		{
+			anz = 1;
+			str = "muenze";
+		}
+		else
+			return 0;
+	}
+	if( anz<=0 || 
+		member(({"muenze","goldstueck","muenzen","goldstuecke"}), str)==-1
+	)
+		return 0;
+	if( anz>PL->QueryMoney() )
+	{
+		write( "So viel Geld hast Du nicht.\n" );
+		return 1;
+	}
+	PL->AddMoney(-anz);
+	SetProp(P_VALUE,QueryProp(P_VALUE)+anz);
+
+	// Hier wird der Master aufgerufen, der das Alignment der Spieler
+	// dann aendert.
+	if( OS_MASTER->addAlignment( PL, anz/3 ) > 0 ) {
+		// Eigentlich könnte man hier auch die Meldung ausgeben, aber
+		// der Spieler soll den Unterschied zwischen Alignment geaendert
+		// unt Alignment nicht geaendert sehen koennen.
+	}
+	write( "Du hast wahrhaft das Gefuehl, etwas Gutes getan zu haben.\n" );
+
+	say( capitalize(PL->name(WER))+" spendet "+anz
+		+ ((anz==1)?" Muenze.\n":" Muenzen.\n") );
+	return 1;
+}
+
+oeffne( str )
+{
+	int newAl;
+	
+	notify_fail( "WAS willst Du oeffnen?\n" );
+	if( !id(str) )
+		return 0;
+	OS_MASTER->addAlignment( PL, -30 );
+	write( "Allein schon der Gedanke....\n" );
+	return 1;
+}
+
+breche( str )
+{
+	string arg;
+	notify_fail( "WAS willst Du aufbrechen?\n" );
+	if( !str )
+		return 0;
+	if( sscanf(str,"%s auf",arg)==1 )
+		str = arg;
+	if( !id(str) )
+		return 0;
+	write( "Dein lautes Getoese ruft einen Teufel herbei, der Dich gleich\n"
+	+	"mit in die Hoelle nimmt.\n"
+	);
+	say( capitalize(PL->name(WER))+" versucht, den Opferstock aufzubrechen.\n"
+	+ "Gleich erscheint ein Teufel, um "+PL->QueryPronoun(WEN)
+	+	" in die Hoelle zu reissen.\n" );
+	PL->move("/d/unterwelt/raeume/qualenraum",M_GO,"zur Hoelle","faehrt");
+	OS_MASTER->addAlignment( PL, -200 );
+	return 1;
+}
diff --git a/doc/beispiele/master/opferstockmaster.c b/doc/beispiele/master/opferstockmaster.c
new file mode 100644
index 0000000..33a081f
--- /dev/null
+++ b/doc/beispiele/master/opferstockmaster.c
@@ -0,0 +1,120 @@
+/*
+ * Beispieldatei fuer einen einfachen Master, der Spielerdaten auch
+ * ueber reboots, resets und updates hinweg speichert, und gleichzeitg
+ * dafuer sorgt, dass die Datenmengen nicht immer groesser werden.
+ *
+ * By: Rumata@MorgenGrauen 3/99
+ *
+ */
+
+// Von diesem Objekt gibt es keine Clones, sondern nur die Blueprint.
+// Das Konstrukt if( clonep(ME) ) destruct(this_object()); ist dadurch
+// obsolet.
+#pragma no_clone
+
+#include <properties.h>
+#include <defines.h>
+
+// Ort, an dem die Daten gespeichert werden koennen. Die Endung .o
+// wird vom System angehaengt.
+#define SAVEFILE "/doc/beispiele/master/opferstocklog"
+
+// Dieses ist der Klient, der diesen Master benutzt. Dieser Wert wird
+// in diesem Programm zwar nicht benutzt, steht hier aber, damit man
+// weiss, wofuer dieser Master gut ist.
+#define CLIENT "/doc/beispiele/master/opferstock"
+
+// Es braucht kein Objekt inheritet werden, da wir keinerlei Spiel-
+// Funktionitaet brauchen. Der Master kann nicht genommen, bewegt oder
+// sonstwie innherlab des Muds benutzt werden. Insbesondere sollen
+// im savefile zum Master keine Properties oder so auftauchen.
+// inhert "/std/thing";
+
+// Um diese Daten geht es.
+// Das Mapping speichert zu jedem Spieler, wann das letzte Mal durch einen
+// der Klienten das Alignment geaendert wurde. Alte Daten werden bei
+// Gelegenheit geloescht.
+mapping data;
+
+void purge();
+
+void create() {
+
+  // Damit Schreibzugriff auf Savefile moeglich.
+  seteuid(getuid());
+
+  if( restore_object( SAVEFILE ) ) {
+    purge();
+  } else {
+    data = ([]);
+    save_object( SAVEFILE ); // Damit Savefile und Daten immer synchron sind.
+  }
+}
+
+// Diese Funktion testet einen einzelnen Eintrag, ob er veraltet ist.
+// (ist nicht Jahr 2038-fest :-)
+int notExpired( string name, int date ) {
+  return time() - date < 86400;
+}
+
+// Das Mapping untersuchen, ob Eintraege vorhanden sind, die nicht
+// mehr benoetigt werden.
+// (In diesem Fall sind das Eintraege, die aelter als einen Tag sind.)
+// Es reicht uns, diese Funktion einmal pro reboot auszufuehren. Bei
+// anderen Anwendungen koennte das natuerlich haeufiger noetig sein.
+void purge() {
+  data = filter_indices( data, #'notExpired );
+  save_object( SAVEFILE );
+}
+
+// Diese Funktion ist die eingetliche Funktion, die "gemastert" werden
+// soll, also für mehrere Opferstoecke gemeinsam benutzt wird.
+// Der Opferstock uebergibt das Spielerobjekt und die gewuenschte
+// Alignmentaenderung, als Ergebnis wird 1 geliefert, wenn eine Aenderung
+// vorgenommen wurde (0 sonst) und das Alignment des Spielers entsprechend
+// gesetzt.
+int addAlignment( object player, int align ) {
+  int newAlign;
+  string name;
+
+  /*
+  // Falls man verhindern will, dass nur der Klient auf die Daten zugreift,
+  // kann man hier noch Abfragen einbauen, typischerweise sieht das dann so
+  // aus:
+  if( object_name(previous_object())[0..xxx] != CLIENT ) return -1;
+  // oder
+  if( geteuid(previous_object()) != geteuid() ) return -1;
+  // etc. etc.
+  */
+
+  name = geteuid(player);
+
+  // Nur eine Aenderung pro Tag.
+  // Wir benutzen hier, dass data[name] == 0 ist, falls data den Namen nicht
+  // enthaelt!
+  if( notExpired( name, data[name] ) ) return 0;
+
+  // Daten setzen und speichern.
+  data[name] = time();
+  save_object( SAVEFILE );
+
+  // Maximale Aenderung: 200
+  if( align < -200 ) align = -200;
+  if( align > 200 ) align = 200;
+
+  newAlign = player->QueryProp( P_ALIGN ) + align;
+
+  // Kappung bei +-1000
+  if( newAlign < -1000 ) newAlign = -1000;
+  if( newAlign > 1000 ) newAlign = 1000;
+ 
+  player->SetProp( P_ALIGN, newAlign );
+  return 1;
+}
+
+// Schlussbemerkung:
+//
+// Gewitzte Programmierer koennten den Klient und den Master in einer
+// Datei zusammen ablegen. Die Blueprint wird als Master, die Clones werden
+// als Klienten benutzt. Ich habe das hier bewusst anders gemacht und empfehle
+// das auch als Vorbild, weil so der Code wesentlich besser zu verstehen ist.
diff --git a/doc/beispiele/misc/bspmon1.c b/doc/beispiele/misc/bspmon1.c
new file mode 100644
index 0000000..8ba85fc
--- /dev/null
+++ b/doc/beispiele/misc/bspmon1.c
@@ -0,0 +1,78 @@
+/*
+** Ein ganz normales Standard-monster ohne Besonderheiten
+** (von Boing)
+*/
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+
+// den Standard-NPC der Mudlib erben.
+inherit "/std/npc";
+
+#include <properties.h>
+#include <language.h>
+
+protected void create()
+{
+  ::create();    /* Nicht vergessen, ohne das geht nichts */
+
+/* Die Kurzbeschreibung wird zum Beispiel angezeigt wenn man in einen Raum
+   mit dem Monster reinlaeuft */
+  SetProp(P_SHORT, "Beispielmonster");
+
+/* Beim Anschauen des Monsters wird die long-description angezeigt */
+  SetProp(P_LONG, "Ein Beispielmonster ohne Besonderheiten.\n");
+
+/* Ein Name muss sein, da sonst z.B. im Todesfall 'Leiche von 0' daliegt */
+  SetProp(P_NAME, "Monster");
+
+/* Das Geschlecht des Monsters. Als Geschlechter sind */
+/* die in <language.h> definierten Symbole NEUTER, MALE   */
+/* und FEMALE zulaessig.                                  */
+  SetProp(P_GENDER, NEUTER);
+
+/* Ein Monster braucht mindestens eine ID, unter der man es ansprechen kann */
+/* Es kann aber auch mehrere verschiedene ID's haben.                       */
+  AddId("monster");
+  AddId("beispielmonster");
+
+/* Zuerst sollte man dem Monster einen Grundlevel geben. */
+/* Die Spellpunkte und die Lebenpunkte sowie die Attribute werden dann */
+/* automatisch angepasst. */
+  create_default_npc( 10 );
+
+/* Nun machen wir es etwas widerstandsfaehiger, indem wir P_BODY setzen.  */
+/* Nie P_TOTAL_AC oder P_AC setzen, P_TOTAL_AC wird automatisch berechnet */
+/* und P_AC ist nur fuer Ruestungen da.                                   */
+  SetProp(P_BODY, 55);
+
+/* Das Monster schlaegt mit blossen Haenden zu, also wird P_HANDS gesetzt.   */
+/* Auch hier weder P_TOTAL_WC noch P_WC setzen.                              */
+  SetProp(P_HANDS, ({" mit seinen Haenden", 55}));
+/*                  ^ dieses Leerzeichen ist wichtig                         */
+/* Beim Kampf erscheint nun: 'Das Monster greift Dich mit seinen Haenden an' */
+/* 55 entspricht der Waffenklasse                                            */
+
+/* Gesinnung des Monsters, 0 ist neutral, negativ boese und positiv gut */
+  SetProp(P_ALIGN, 100);  /* Einigermassen nett, aber nichts besonderes */
+
+/* Die Rasse des Monsters */
+  SetProp(P_RACE, "Irgendnerasse");
+
+/* Erfahrungspunkte des Monsters, beim Tod erhaelt der 'Killer' ein   */
+/* hundertstel dieses Wertes. Schon im Kampf erhaelt man bei jedem    */
+/* Schlag weapon_class*schaden/10 punkte (weapon_class hier 55), also */
+/* insgesamt weapon_class*hit_points/10.                              */
+  SetProp(P_XP, 10000);
+
+/* Die Groesse des Monsters in cm. Dies wird bei einigen Kampfbefehlen */
+/* ausgewertet, sowie bei einigen Identifikationsspruechen von Gilden  */
+  SetProp(P_SIZE,180);
+
+/* Weitere Werte: P_(MAX)_FOOD, P_(MAX)_DRINK, P_(MAX)_ALCOHOL,       */
+/* P_MSGIN, P_MSGOUT, P_MMSGIN, P_MMSGOUT, P_MAX_HANDS, P_USED_HANDS  */
+
+}
diff --git a/doc/beispiele/misc/bspmon2.c b/doc/beispiele/misc/bspmon2.c
new file mode 100644
index 0000000..95f3909
--- /dev/null
+++ b/doc/beispiele/misc/bspmon2.c
@@ -0,0 +1,199 @@
+/*
+** Ein Beispielmonster mit div Extras (von Boing, aktualisiert von Wim+Zesstra)
+*/
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+
+inherit "/std/npc";
+
+#include <properties.h>
+#include <language.h>
+#include <combat.h>    // fuer die damage types
+#include <moving.h>    // fuer REJECT_KEEP   
+#include <class.h>     // fuer AddClass 
+#include <new_skills.h> // fuer SP_* bei AddSpell
+
+protected void create()
+{
+  ::create();       /* WICHTIG!!! */
+
+/* Standard-Knofiguration (Erlaeuterungen siehe bspmon1.c): */
+  SetProp(P_SHORT, "Ein Zauberer");
+  SetProp(P_LONG, "Dieser Zauberer zaubert wie wild und schwingt dabei "
+    "seinen langen Bart.\n");
+  SetProp(P_NAME, "Zauberer");
+  SetProp(P_GENDER, MALE);
+  AddId("zauberer");
+  create_default_npc(18);
+  SetProp(P_ALIGN, -700);   /* Brrr, ist der boese .... */
+  SetProp(P_BODY, 125);
+  SetProp(P_HANDS, ({" mit seinem langen Bart", 185}) );
+  SetProp(P_SIZE,180);
+  SetProp(P_MAX_HANDS, 2);  /* Anzahl der Haende (default ist 2) */
+  // set_living_name() setzt einen Namen, mit der der Zauberer z.B. mit einem
+  // 'finde' gefunden werden kann. Fuer die meisten NPC ist dies nicht noetig.
+  // Speziell sollte man keine generischen Bezeichnungen hier anmelden, wenn
+  // schon, dann individuelle Namen.
+  /* set_living_name("zauberer"); */
+
+/* Mit AddClass() und P_RACE wird festgelegt, in welche Gruppe von Lebe-  */
+/* wesen der NPC gehoert, welche mit is_class_member(mixed str) abgefragt */
+/* werden kann. Im Minimalfall ist der NPC von der Klasse, die bei P_RACE */
+/* eingetragen ist. Mit AddClass() koennen aber noch weitere Eigen-       */
+/* schaften hinzugefuegt werden.                                          */
+
+  SetProp(P_RACE,"Superduperzauberer");
+  AddClass( ({ CL_HUMAN, CL_MAMMAL }) );
+
+/* Mit P_RESISTANCE und P_VULNERABILITY werden fixe Werte (50%) fuer      */
+/* Resistenzen respektive Anfaelligkeiten gesetzt                         */
+/* Die Liste der moeglichen Schadensarten ist in /sys/combat.h definiert. */  
+/* z.B.  SetProp(P_RESISTANCE, ({ DT_MAGIC }));                           */
+/*       SetProp(P_VULNERABILITY, ({ DT_COLD }))                          */
+/*                                                                        */
+/* Mit P_RESISTANCE_STRENGTHS koennen Resistenzen und Anfaelligkeit       */
+/* konfiguriert werden. Diese Property enthaelt ein Mapping von           */
+/* von Schadensarten enthaelt. Negative Faktoren bis maximal -1.0         */ 
+/* (=Immunitaet) geben Resistenzen, positive Werte Anfaelligkeiten an.    */
+
+  SetProp(P_RESISTANCE_STRENGTHS, ([ DT_MAGIC: -0.5, DT_COLD: 2.0]) );					      
+
+/* Durch diesen Befehl wird eine Waffe geclont, sofern sie nicht im Raum  */
+/* herumliegt (in dem Fall wird sie vom NPC aufgehoben), und gezueckt     */ 
+/* (definiert in /std/npc/items.c resp. /sys/npc.h)                       */
+
+  AddItem("/doc/beispiele/misc/bspwaffe1", CLONE_WIELD);
+/* Und noch eine Ruestung clonen und anziehen.                            */  
+  AddItem("/doc/beispiele/misc/bspruest1", CLONE_WEAR);
+
+/* Jetzt wird gezaubert ....     */
+
+/* SetProp(P_SPELLRATE,) legt die Wahrscheinlichkeit fest, mit der ein   */
+/* Zauberspruch durchgefuehrt wird, hier 40%                            */
+  SetProp(P_SPELLRATE,40);
+
+/* Mit AddSpell() wird ein Zauberspruch hinzugefuegt. Das erste Argument  */
+/* ist so etwas wie die relative Wahrscheinlichkeit des Spruchs im        */
+/* Vergleich zu den anderen, hier beim ersten spruch 1 beim zweiten 2,    */
+/* das heisst in 20% der Faelle wird der erste Zauberspruch genommen und  */
+/* in 40% der zweite und in 60% der dritte.                               */
+/* Das zweite Argument ist der maximale Schaden, entsprechend der WC      */
+/* einer Waffe oder P_HANDS beim waffenlosen Kampf.                       */
+/* Die zwei weiteren Argumente sind die Meldungen die der angegriffene    */
+/* Spieler selbst und eventuelle Zuschauer erhalten. Hierbei stehen @WER, */
+/* @WEN, @WEM und @WESSEN fuer den Namen des Opfers im entsprechenden     */
+/* Fall.                                                                  */
+/* Das 5. Argument ist optional und gibt den Schadenstyp an, fehlt er     */
+/* wird DT_MAGIC angenommen.                                              */  
+/* Im 6. Arugument kann eine Funktion stehen, die bei dem Spruch ausge-   */
+/* fuehrt werden soll,                                                    */
+/* Das siebte uebergibt die Spell Parameter, hier ist wichtig, dass bei   */
+/* physikalischen Schaeden auch SP_PHYSICAL_ATTACK gesetzt wird, da sonst */
+/* einige Gilden den "Zauberspruch" abwehren koennen (siehe SpellDefend)  */
+
+  AddSpell(20,60, 
+	   "  Der Zauberer wirft einen Feuerball nach Dir.\n",
+	   "  Der Zauberer wirft einen Feuerball nach @WEM.\n",
+	   ({ DT_FIRE }) );
+  AddSpell(40,95,
+	   "  Der Zauberer beschwoert einen Sturm ueber deinem Kopf hervor.\n",
+	   "  Der Zauberer beschwoert einen Sturm ueber @WESSEN Kopf hervor.\n",
+	   ({ DT_AIR, DT_LIGHTNING }) );  
+  AddSpell(60,50,
+           "  Der Zauberer tritt Dich hinterlistig.\n",
+	   "  Der Zauberer tritt nach @WEM.\n",
+	   ({ DT_BLUDGEON }), 0,
+	   ([SP_SHOW_DAMAGE:1, SP_PHYSICAL_ATTACK:1]) );  	   
+
+
+/* Reden kann der Zauberer auch von alleine, hier kommen die chats:     */
+
+/* Das erste Argument ist die Wahrscheinlichkeit, mit der das Monster
+ * einen Spruch bringt (weniger ist mehr!). Das zweite Argument ist die Liste
+ * der Sprueche (oder Aktionen).
+ */
+
+  SetChats(2, ({
+  "Der Zauberer laeuft im Kreis herum.\n",
+  "Der Zauberer stolpert ueber seinen Bart.\n",
+  "Der Zauberer sagt: Heh Du! Was machst Du da?\n",
+  "Der Zauberer murmelt ein paar seltsame Worte in seinen Bart.\n",
+  "Der Zauberer bricht in unkontrolliertes Gelaechter aus.\n",
+  "Der Zauberer sagt: Hast mir mal ne Mark, ey?\n",
+  "Der Zauberer sagt: Wenn ich diesen Olpp erwische ...\n",
+  "Der Zauberer zaubert ein Kaninchen aus seinem Hut.\n"
+  }) );
+
+/* Das selbe fuer Sprueche die waehrend eines Kampfes kommen sollen    */
+
+  SetAttackChats(20, ({
+  "Der Zauberer macht: Buh!\n",
+  "Der Zauberer wirft mit weissen Maeusen nach Dir.\n",
+  "Der Zauberer sagt: Das war ein grosser Fehler!\n",
+  "Der Zauberer beisst Dich in den Arm.\n",
+  "Der Zauberer zaubert gruene Punkte auf deine Wange.\n",
+  "Der Zauberer verwandelt sich in eine Kroete.\n",
+  "Der Zauberer verschwindet und taucht hinter Dir wieder auf.\n"
+  }) );
+
+/* Wenn er stirbt soll eine besondere Meldung erscheinen. */
+  SetProp( P_DIE_MSG, " loest sich in Luft auf.\n" );
+/* Dann soll natuerlich auch kein Leichnam rumliegen */
+  SetProp( P_NOCORPSE, 1 );
+ 
+/* Nun wollen wir den Zauberer noch auf ein paar Fragen antworten lassen    */
+/* AddInfo(DEFAULT_INFO,) setzt die Antwort auf alle Fragen, wo der         */
+/* Schluessel nicht bekannt ist. Der Name des Zauberers wird immer vor den  */
+/* String gesetzt. (SetProp(P_DEFAULT_INFO, ist obsolet und sollte nicht    */
+/* mehr angewendet werden.)                                                 */ 
+
+  AddInfo(DEFAULT_INFO,"sagt: Keine Ahnung, von was Du redest.\n",0,
+                       "sagt zu @WEM: Keine Ahnung, von was Du redest.\n");
+
+/* Die bekannten Schluessel werden mit AddInfo dazugefuegt, das erste       */
+/* Argument ist das Wort (oder die Liste von Woertern), zu der der Zauberer */
+/* befragt werden kann, das zweite Argument ist die entsprechende Antwort.  */
+/* ist das dritte Argument gesetzt, so wird die Antwort automatisch umge-   */
+/* brochen und bekommt den Text des Arguments vor jede Zeile gestellt.      */
+/* Das vierte, ebenfalls optionale, Argument wird sofern gesetzt an die     */
+/* umstehenden Personen ausgegeben, dadurch laesst sich ein Fluestern oder  */
+/* eine spielerabhaengige Antwort realisieren.                              */
+
+  AddInfo("kaninchen",
+  "sagt: Die hol ich immer aus meinem Hut.\n");
+  AddInfo("zauberei",
+  "sagt: Ich bin ein Meister der Zauberei.\n");
+  AddInfo(({"maus", "maeuse"}),
+  "sagt: Maeuse sind meine Lieblingstiere.\n"); 
+  
+  AddInfo( ({"tier","tiere"}), 
+          "Oh, hauptsaechlich beschaeftige ich mich mit Kaninchen und Maeusen.",
+          "fluestert Dir zu: ",
+          "fluestert mit @WEM.\n");
+
+/* Normalerweile nehmen Monster alles was man ihnen gibt, ohne einen Ton */
+/* zu sagen. Will man dort ein anderes Verhalten, so tut man das so:     */
+/* Moeglich sind auch REJECT_GIVE (= zurueckgeben) REJECT_DROP (=fallen  */
+/* lassen. */
+  SetProp( P_REJECT, ({ REJECT_KEEP, "Der Zauberer sagt: Dankeschoen.\n" }) );
+
+/* Der Zauberer kann auch zusaetzliche Verben haben, die nur er selber
+ * benutzen kann, kein anderes Living. Diese koennen mit add_action() im
+ * create eines NPC angemeldet werden (und idR NUR in dieser Funktion und NUR
+ * in Lebewesen, nicht in sonstigen Objekten! */
+  add_action("nasebohren", "bohreinnase");
+
+/* Verben, die Spieler in Gegenwart des Zauberers koennen sollen, werden     */
+/* mit AddCmd angemeldet. */
+}
+
+int nasebohren( string str ) // str ist das, was hinter dem verb eingegeben wurde.
+{
+   say( "Der Zauberer bohrt mit dem Stab in seiner Nase.\n" );
+   return 1; // Verb war erfolgreich.
+}
+
diff --git a/doc/beispiele/misc/bspraum1.c b/doc/beispiele/misc/bspraum1.c
new file mode 100644
index 0000000..3483095
--- /dev/null
+++ b/doc/beispiele/misc/bspraum1.c
@@ -0,0 +1,78 @@
+/* /doc/beispiele/misc/bspraum1.c von Rumata */
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+
+inherit "/std/room";
+
+#include <properties.h>
+#include <language.h>
+
+protected void create()
+{
+    ::create();
+
+    SetProp( P_INT_SHORT, "Ein kleines Schreibzimmer" );
+    SetProp( P_INT_LONG,
+        "Du stehst in einem kleinen Schreibzimmer.\n"
+         "Es gehoerte wohl irgendwann einmal einem Magier, aber nun\n"
+         "ist dieser Raum verwaist und rottet vor sich hin.\n"
+         "Ein grosser Schreibtisch in der Mitte des Raumes scheint\n"
+         "noch einigermassen gut erhalten zu sein. Durch die Tuer\n"
+         "im Norden faellt etwas Licht hinein.\n"
+    );
+    SetProp( P_LIGHT, 1 );
+    SetProp( P_INDOORS, 1 );
+
+    AddDetail( ({ "schreibtisch", "tisch" }),
+        "Auf dem Tisch liegt eine dicke Staubschicht.\n"
+        "Eine Schublade findest Du ebenfalls.\n"
+    );
+    AddDetail( ({ "staub", "staubschicht", "schicht" }),
+        "Du malst gelangweilt einige Kreise in den Staub.\n"
+    );
+    AddDetail( "schublade",
+        "So sehr Du Dich anstrengst; Du kannst sie nicht bewegen.\n"
+    );
+    AddDetail( "tuer" ,
+        "Sie steht seit Jahren offen und ist in dieser Lage\n"
+        "hoffnungslos festgerostet.\n"
+    );
+
+    AddCmd( ({ "schliesse", "oeffne", "bewege", "schliess", "beweg" }), 
+            "beweg_etwas" );
+    AddExit( "norden", "players/rumata/workroom" );
+}
+
+public int beweg_etwas( string str )
+{
+    notify_fail( "Was willst Du denn bewegen ?" );
+    if( str == "tuer" )
+    {
+        write( "Die Tuer ist hoffnungslos festgerostet.\n" );
+        return 1;
+    }
+    if ( str == "lade" || str == "schublade" )
+    {
+        write( "Die Schublade klemmt einfach nur.\n" );
+        return 1;
+    }
+    if ( query_verb() == "bewege" &&
+        ( str == "tisch" || str == "schreibtisch" ) )
+    {
+        tell_object(this_player(),
+            "Der Tisch scheint am Boden festgeschraubt zu sein.\n"
+        );
+        tell_room(this_object(),
+            this_player()->name(WER,2)+" zerrt am Schreibtisch.\n"
+        );
+        return 1;
+    }
+ // offenbar passte nix, Kommando nicht erfolgreich abgearbeitet. Ggf. weitere
+ // versuchen und/oder die notify_fail-Meldung ausgeben.
+    return 0;
+}
+
diff --git a/doc/beispiele/misc/bspruest1.c b/doc/beispiele/misc/bspruest1.c
new file mode 100644
index 0000000..10e3b0e
--- /dev/null
+++ b/doc/beispiele/misc/bspruest1.c
@@ -0,0 +1,38 @@
+/*
+** Eine Beispielruestung
+** (von Boing)
+*/
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+
+inherit "/std/armour";
+
+#include <properties.h>
+#include <combat.h>
+#include <language.h>
+
+protected void create()
+{
+  ::create();   /* WICHTIG!!! */
+
+/* Standardkonfiguration, genaueres siehe /doc/beispiele/bspwaffe1.c    */
+  SetProp(P_SHORT, "Ein grauer Umhang");
+  SetProp(P_LONG, "Es ist ein langer grauer Umhang aus einem feinen Material.\n");
+  SetProp(P_NAME, "Umhang");
+  SetProp(P_GENDER, MALE);
+  AddId("umhang");
+  SetProp(P_WEIGHT, 250);
+  SetProp(P_VALUE, 200);
+
+/* Zur Waffe fehlt nicht mehr viel, nur noch die Ruestungsklasse (AC)    */
+/* setzen und einen Ruestungstyp setzen. Die Typen sind in sys/combat.h  */
+/* definiert. Richtlinien zu Ruestungsklassen der verschiedenen Typen    */
+/* stehen momentan in /players/boing/waffen.                             */
+
+  SetProp(P_ARMOUR_TYPE, AT_CLOAK);
+  SetProp(P_AC, 8);
+}
diff --git a/doc/beispiele/misc/bsptransporter1.c b/doc/beispiele/misc/bsptransporter1.c
new file mode 100644
index 0000000..3e26020
--- /dev/null
+++ b/doc/beispiele/misc/bsptransporter1.c
@@ -0,0 +1,132 @@
+inherit "std/transport";
+
+/* wolke.c Ein Beispiel zur Programmierung eines Transporters */
+/* von Rumata (mit kleinen Bugfixes von Wargon ;) */
+
+#include <properties.h>
+#include <language.h>
+
+create()
+{
+  ::create();
+  SetProp( P_NAME, "Woelkchen" );
+  AddId( ({ "wolke", "woelkchen" }) );
+  SetProp( P_GENDER, NEUTER );
+  SetProp( P_INT_SHORT, "Auf einer kleinen Wolke" );
+  SetProp( P_INT_LONG,
+	  "Du sitzt auf einer kleinen Wolke, die zwischen Jofs und Rumatas\n"
+	  +	"Arbeitszimmer hin- und herpendelt. Wenn sie niedrig fliegt, kannst\n"
+	  +	"Du bestimmt von ihr heruntersteigen.\n"
+	  );
+  SetProp( P_SHORT, "Eine kleine Wolke" );
+  SetProp( P_LONG,
+	  "Diese suesse kleine Cumuluswolke schwebt zwischen Jofs und Rumatas\n"
+	  +	"Arbeitszimmer hin und her. Vielleicht kannst Du auf sie draufsteigen.\n"
+	  );
+  SetProp(P_LIGHT, 1 );
+  SetProp(P_TRANSPARENT,1);
+  // Man soll auf(in) die Wolke und von ihr herunter schauen koennen.
+    
+  /*** Meldungen, die Transporter geben koennen ***/
+  
+  SetProp( P_DEPARTMSG, ({
+    "Die Wolke steigt in die Luft.\n", 
+    "Die Wolke fliegt davon.\n"
+  }) );
+  // Die erste Meldung ist fuer Leute auf der Wolke.
+  // Die zweite fuer Leute in dem Raum, in dem die Wolke ankommt.
+      
+  SetProp( P_ARRIVEMSG, ({
+    "Die Wolke naehert sich dem Boden von @@QueryArrived@@.\n",
+    "Eine kleine Wolke schwebt herab.\n"
+  }) );
+  // Die erste Meldung ist fuer Leute auf der Wolke.
+  // Die zweite fuer Leute in dem Raum, aus dem die Wolke abreist.
+    
+  SetProp( P_ENTERMSG, ({
+    "steigt auf die Wolke",
+    "kommt auf die Wolke"
+  }) );
+  // Die erste Meldung ist fuer den Raum, aus dem der Spieler kommt.
+  // (=Raum). Die zweite Meldung ist fuer Spieler in dem Raum, in
+  // den der Spieler geht (=Transporter).
+	
+  SetProp( P_LEAVEMSG, ({
+    "steigt von der Wolke",
+    "steigt von der Wolke"
+  }) );
+  // Die erste Meldung ist fuer den Raum, aus dem der Spieler kommt.
+  // (=Transporter). Die zweite Meldung ist fuer Spieler in dem Raum,
+  // in den der Spieler geht (=Raum).
+      
+  SetProp( P_LEAVEFAIL, "Die Wolke schwebt viel zu hoch" );
+  // Meldung, die kommt, wenn ein Spieler den Transporter verlassen
+  // will, aber dieser an keinem Raum angelegt hat.
+  // Ist der Parameter ein String (so wie hier), so bekommt nur
+  // der betreffende Spieler die Meldung, wenn sie ein Array aus
+  // 2 Elementen ist, bekommt der Spieler die erste und alle
+  // anderen Spieler im Transporter die zweite (mit dem Namen
+  // des Spielers davor).
+  // Moeglich waere also auch:
+  /*
+     SetProp( P_LEAVEFAIL, ({
+     "Die Wolke schwebt viel zu hoch zum Herunterspringen",
+     "faellt fast von der Wolke"
+     }) );
+  */
+
+  SetProp( P_ENTERFAIL, "Es passen nur 5 Passagiere auf die Wolke.\n" );
+  // Diese Meldung bekommt der Spieler, wenn er versucht, einen
+  // vollen Transporter zu betreten. Hier ist nur eine Meldung
+  // (an den Spieler) vorgesehen.
+
+  SetProp( P_MAX_PASSENGERS, 5 );
+  // Maximale Anzahl der Spieler, die sich im Transporter befinden
+  // koennen. Fuer Gegenstaende gibt es keine Beschraenkung.
+
+  /* Verben */
+  AddCmd( ({ "steig", "steige", "spring", "springe" }), "steige" );
+
+  /* Kurs */
+  AddRoute( "players/rumata/workroom", 30, 20, "Rumatas Arbeitszimmer" );
+  // Hier wird der Raum /players/rumata/workroom als Anlegeplatz
+  // in den Kurs eingefuegt. Der Transporter verweilt 30sek in diesem
+  // Raum und faeht dann 20sek lang, bis er den naechten Punkt
+  // erreicht. Danach kann man noch den Code angeben, der bei
+  // QueryArrived zurueckgegeben wird, setzen. Wird kein Code
+  // gesetzt, wo wird dort ein Leerstring zurueckgegeben. 0 wird
+  // dann zurueckgegeben, wenn der Transporter an keinem Raum angelegt
+  // hat.
+
+  AddMsg( "Die Wolke treibt nun im Wind Richtung Jofs Arbeitszimmer.\n", 30 );
+  // Hier wird eine Meldung ausgegeben, und der Transporter reist 30sek lang
+  // weiter.
+
+  AddMsg( "Die Wolke beginnt zu sinken.\n", 10 );
+  AddRoute( "players/jof/workroom", 30, 20, "Jofs Arbeitszimmer" );
+  AddMsg( "Die Wolke treibt nun im Wind Richtung Rumatas Arbietszimmer.\n", 30 );
+  AddMsg( "Die Wolke beginnt zu sinken.\n", 10 );
+  // Nach dem Letzten Haltepunkt wird der Kurs wieder von vorn
+  // befahren.
+ 	
+  Start();
+  // Lasse den Transporter am ersten dieser Haltepunkte starten.
+}
+
+steige(str)
+{
+  string arg, arg2;
+  
+  if (sscanf( str, "auf %s", arg ) > 0 && id(old_explode(arg," ")[0]))
+    return Enter();
+  // Wenn sicher ist, dass der Spieler die Wolke BEsteigen will,
+  // kann man mit return Enter() alle weiteren Tests durchfuehren.
+
+  if (sscanf( str, "von %s", arg ) > 0 && id(old_explode(arg, " ")[0]))
+    return Leave();
+  // Das selbe gilt fuer das ABsteigen und Leave().
+  // Verben sollten nach Enter() oder Leave() keine weiteren Befehle
+  // enthalten.
+}
+
+
diff --git a/doc/beispiele/misc/bspwaffe1.c b/doc/beispiele/misc/bspwaffe1.c
new file mode 100644
index 0000000..259ae2a
--- /dev/null
+++ b/doc/beispiele/misc/bspwaffe1.c
@@ -0,0 +1,56 @@
+/*
+** Eine Beispielwaffe
+** (von Boing)
+*/
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+
+inherit "/std/weapon";
+
+#include <properties.h>     /* Definition der Properties */
+#include <combat.h>         /* Definition der kampfspezifischen Konstanten */
+#include <language.h>       /* Definition von MALE, FEMALE, WER, ... */
+
+protected void create()
+{
+  ::create();   /* WICHTIG!!! */
+
+/* Kurzbeschreibung fuer Darstellung in inventories */
+  SetProp(P_SHORT, "Ein Knueppel");
+
+/* Beschreibung des Knueppels */
+  SetProp(P_LONG, 
+  "Dieser Knueppel ist ziemlich normal, er ist aus festem Holz gefertigt und\n"+
+	  "man kann ihn als Waffe benutzen.\n");
+
+/* Name und Geschlecht sind wichtig, jedes Objekt braucht das */
+  SetProp(P_NAME, "Knueppel");
+  SetProp(P_GENDER, MALE);
+
+/* Jedes Objekt braucht eine oder mehrere Id's */
+  AddId("knueppel");
+
+/* Wert und Gewicht */
+  SetProp(P_VALUE, 300);
+  SetProp(P_WEIGHT, 1250);   /* in Gramm */
+
+/* Nun die wirklich Waffenspezifischen Dinge:                   */
+/* Waffen- und Schadensarten sind in /sys/combat.h definiert    */
+  SetProp(P_WEAPON_TYPE, WT_CLUB);
+  SetProp(P_DAM_TYPE, DT_BLUDGEON);
+
+/* Die Waffenklasse muss im Bereich zwischen 1 und 200 liegen   */
+  SetProp(P_WC, 125);
+  
+/* Anzahl der benoetigten Haende, um die Waffe zu zuecken.      */
+/* Waffen mit einer WC groesser 150, sollten auf jeden Fall     */
+/* Zweihaender sein, bei Sonderfaellen bitte an den Erzmagier   */
+/* fuer Waffen wenden. Wenn nichts gesetzt wird, ist die Waffe  */
+/* ein Zweihaender.                                             */
+  SetProp(P_NR_HANDS, 1);
+}
+
diff --git a/doc/beispiele/misc/krankheit.c b/doc/beispiele/misc/krankheit.c
new file mode 100644
index 0000000..c5816a3
--- /dev/null
+++ b/doc/beispiele/misc/krankheit.c
@@ -0,0 +1,66 @@
+/*  Paracelsus: Eine Krankheit
+**
+**      Beispiel fuer die Verwendung von P_X_ATTR_MOD und P_X_HEALTH_MOD
+**
+**      Diese Krankheit setzt alle Attribute herab und erniedrigt
+**      zusaetzlich P_MAX_HP und P_MAX_SP um jew. 20 (zusaetzlich zu den
+**      Auswirkungen der Attributsenkungen).
+**      Gestartet wird die Krankheit, indem sie einfach in das Opfer
+**      bewegt wird.
+**      Man beachte, dass im remove() die P_X_*-Properties ein leeres
+**      Mapping zugewiesen bekommen und keine 0. Nur so werden die
+**      Krankheitsfolgen rueckgaengig gemacht.
+*/
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+
+inherit "/std/thing";
+
+#include <properties.h>
+#include <moving.h>
+#include <language.h>
+#include <class.h>
+
+protected void create()
+{
+    ::create();
+
+    // ausnahmsweise darf dieses Objekt mal kein P_SHORT+P_LONG haben...
+    SetProp(P_SHORT,0);
+    SetProp(P_LONG, 0);
+    SetProp(P_INVIS, 1); // unsichtbar machen.
+    SetProp(P_NAME,"Krankheit");
+    SetProp(P_GENDER,FEMALE);
+    // wichtig, sonst wiegt die Krankheit 1kg (Standardgewicht fuer thing) 
+    SetProp(P_WEIGHT,0);
+    SetProp(P_VALUE,0);
+    SetProp(P_MATERIAL,MAT_MISC_MAGIC);
+    SetProp(P_NODROP,1); // Nicht wegwerfbar.
+
+// ----> Dieser Abschnitt sorgt fuer fiese Statabzuege
+
+    SetProp(P_X_HEALTH_MOD,
+    ([
+        P_HP : -20,   // Max. Lebenspunkte um 20 runter
+        P_SP : -20    // Max. Konzentrationspunkte um 20 runter
+    ]) );
+    SetProp(P_X_ATTR_MOD,
+    ([
+        A_CON : -1,   // Ausdauer um 1 runter, reduziert auch max. LP!
+        A_DEX : -1,   // Geschicklichkeit um 1 runter
+        A_INT : -2,   // Intelligenz um 2 runter, reduziert auch max. KP!
+        A_STR : -4,   // Staerke um 4 runter
+    ]) );
+
+// <----
+
+    AddId( ({"PARA\nSICK\nEXAMPLE"}) );
+
+    AddClass(CL_DISEASE); // Damit Kleriker helfen koennen.
+    SetProp(P_LEVEL,15);  // Aber nicht ganz so einfach zu entfernen
+}
+
diff --git a/doc/beispiele/misc/lebensring.c b/doc/beispiele/misc/lebensring.c
new file mode 100644
index 0000000..f82ea5e
--- /dev/null
+++ b/doc/beispiele/misc/lebensring.c
@@ -0,0 +1,56 @@
+/* Paracelsus: Lebensring
+**
+**    Ein Beispiel fuer die Anwendung von P_M_HEALTH_MOD
+**
+**    Zieht ein Spieler diesen Ring an, so erhoehen sich seine maximalen
+**    Lebenspunkte dabei um 10, waehrend seine max. Konzentrationspunkte
+**    um 5 erniedrigt werden.
+*/
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+
+inherit "/std/armour";
+
+#include <properties.h>
+#include <armour.h>
+#include <language.h>
+#include <combat.h>
+
+protected void create()
+{
+    ::create();
+
+    SetProp(P_SHORT,"Ein Lebensring");
+    SetProp(P_LONG,
+        "Ein kleiner Ring aus einem seltsamen, gruenen Material.\n");
+    SetProp(P_INFO,
+        "Dieser Ring unterstuetzt die Gesundheit.\n"+
+        "Dabei schwaecht er leider den Geist.\n");
+    SetProp(P_NAME,"Lebensring");
+    SetProp(P_GENDER,MALE);
+    SetProp(P_WEIGHT,80);
+    SetProp(P_VALUE,2000+random(501));
+    SetProp(P_ARMOUR_TYPE,AT_RING);
+    SetProp(P_AC,0); // keine Schutzwirkung im Kampf
+    SetProp(P_MATERIAL,MAT_MISC_MAGIC);
+
+// ---->
+
+    SetProp(P_M_HEALTH_MOD,
+    ([
+        P_HP : 10,      // Max. LP um 10 erhoehen
+        P_SP : -5       // Max. KP um 5 verringern
+    ]) );
+
+// <----
+
+    AddId( ({"ring","lebensring"}) );
+
+    AddDetail( "material",
+        "Es ist gruen.\n");
+}
+
diff --git a/doc/beispiele/misc/seherhaus.c b/doc/beispiele/misc/seherhaus.c
new file mode 100644
index 0000000..ea127e4
--- /dev/null
+++ b/doc/beispiele/misc/seherhaus.c
@@ -0,0 +1,136 @@
+/*
+Kurze Uebersicht der Termini:
+
+P_HAUS_ERLAUBT     Propertie, die das Bauen eines Hauses im Raum er-
+                   laubt oder verbietet. Default auf 0
+                   Syntax: xcall $h->SetProp(P_HAUS_ERLAUBT,x)
+                   
+Save()             Muss nach Aenderungen am Haus aufgerufen werden.
+                   Syntax: xcall /d/seher/haeuser/tillyhaus->Save()
+
+Okay, also ein Seher moechte, dass sein Haus 1) unsichtbar ist, 2) mit
+einem anderen Kommando betreten werden soll und 3) nicht auf die id 
+<haus> hoert sondern auf eine andere:
+
+Zu 1): Ganz einfach, man tippe 
+
+       'xcall /d/seher/haeuser/tillyhaus->SetProp(P_SHORT,0)' und
+       'xcall /d/seher/haeuser/tillyhaus->Save()'
+
+Zu 2) und 3): Das ist nicht so einfach, sondern muss ueber den Raum, in 
+              dem das Haus steht, gemacht werden. Deswegen folgend nun 
+              ein Beispiel zu Funktionen in so einem Raum.
+
+
+Folgende Dinge stehen natuerlich schon im Raum, sonst liesse er sich
+nicht laden :-)              
+*/
+
+#pragma strong_types, save_types, rtt_checks
+
+inherit "/std/room";
+
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+
+/*
+   Fuer properties und sonstige Definitionen zum Seherhaus.
+*/
+#include "/d/seher/haeuser/haus.h"
+
+/*
+   Aus Gruenden der Uebersichtlichkeit ziehen viele Magier vor, den Hauskrams
+   in eine separate Funktion zusammenzufassen.
+*/
+private void haus_create();
+
+/*
+Es folgt nun das normale create() eines Raumes
+*/
+void create(){
+  ::create();
+  /* ... div. Konfiguration des Raum ... */
+
+  /*
+  Das extra-create() muss natuerlich aufgerufen werden
+  */  
+  haus_create();
+}      
+
+/*
+Hier kommen wir nun zu der Fkt, die alles am Seherhaus verwaltet
+*/
+private void haus_create()
+{
+  object ob = find_object(HAUSNAME("tilly")); // findet Haus von Tilly
+
+  /*
+  Wenn das Haus im Raum steht. Hat den Vorteil, dass bei einem Verlegen
+  des Hauses, Cmd's usw nicht mehr verfuegbar sind -> Keine evtl Bugs :)
+  */
+  if(objectp(ob))
+  {
+    /*
+    Der Spieler wollte es ja nicht anders
+    */
+    ob->RemoveCmd(({"betret","betritt","betrete"}));
+    ob->RemoveId("haus");
+    ob->RemoveId("haus von tilly");
+    ob->AddId("steinhaufen");
+    
+    AddDetail(({"haufen","steinhaufen"}),"Da ist doch ein kleiner Spalt? "
+     +"Vielleicht kannst Du auf den Steinhaufen klettern?\n");
+
+    AddCmd(({"kletter","klettere"}),"kletter_cmd");
+  }
+}
+
+public int kletter_cmd(string str)
+{
+  object ob = find_object(HAUSNAME("tilly"));
+
+  _notify_fail("Wo moechtest Du hinklettern?\n");
+
+  if(!str) return 0;
+
+  if(str=="auf haufen" || str=="auf steinhaufen")
+  {
+    if(objectp(ob))
+    {
+      /*
+      Das ist die Meldung, die ein Spieler bekommt, wenn das Seherhaus
+      abgeschlossen ist und er versucht, es zu 'betreten'.
+      */
+      notify_fail("Du kletterst auf den Haufen, doch der Spalt ist zu "
+       "schmal fuer Dich.\n");
+       
+      if(!(ob->QueryProp(H_DOORSTAT) & D_CLOSED))
+      {
+        tell_object(this_player(),
+            "Du kletterst auf den Steinhaufen, rutschst den Spalt runter "
+             "und findest Dich urploetzlich in einer schummrigen Hoehle.\n");
+        tell_room(this_object(),this_player()->name()+" klettert auf einen "
+          "Steinhaufen und ist ploetzlich verschwunden.",({this_player()}));
+        tell_room(RAUMNAME("tilly",0),this_player()->name()+
+          " faellt fluchend herein.\n");
+        this_player()->move(RAUMNAME("tilly",0),M_GO|M_SILENT,0);
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+  
+/*
+Natuerlich gibt es noch viel mehr Moeglichkeiten rund um Seherhaeuser, 
+doch sollte dies erstmal reichen. Zu beachten ist bei solcher Vorgehens-
+weise, dass ein 'schliesse|oeffne haus' evtl zu Fehlermeldungen fuehrt.
+Dem Spieler sei nahegelegt, es doch mit 'oeffne haustuer' zu versuchen.
+
+HAUSNAME und RAUMNAME (u.a.) sind Defines aus dem haus.h. Man sollte sich
+dieses File anschauen und die dortigen Defines uebernehmen. Dann bugt auch 
+nichts, falls mal etwas an den Haeusern umgestellt wird.
+
+Tilly, 20. Mai 2001, 00:10:24h
+*/
diff --git a/doc/beispiele/misc/statkrallen.c b/doc/beispiele/misc/statkrallen.c
new file mode 100644
index 0000000..a0ae4c3
--- /dev/null
+++ b/doc/beispiele/misc/statkrallen.c
@@ -0,0 +1,61 @@
+/* Paracelsus: Statkrallen
+**
+**     Beispiel fuer die Verwendung von P_M_ATTR_MOD
+**
+**     Zieht ein Spieler diese Krallen an, so erhoeht sich seine Staerke
+**     um 2. Gleichzeitig wird das Erhoehen seiner Geschicklichkeit durch
+**     andere Ruestungen/Waffen blockiert. 
+**     Die Krallen koennen nur angezogen werden, wenn weder A_STR noch
+**     A_DEX durch eine andere Ruestung/Waffe blockiert wird.
+*/
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+
+#include <properties.h>
+#include <language.h>
+#include <combat.h>
+
+inherit "/std/armour";
+
+protected void create()
+{
+    ::create();
+
+    SetProp(P_SHORT,"Statkrallen");
+    SetProp(P_LONG,
+        "Diese krallenbewehrten Handschuhe schimmern blaeulich.\n");
+    SetProp(P_NAME,"Statkrallen");
+    SetProp(P_INFO,"Die Krallen machen staerker.\n");
+    SetProp(P_GENDER,FEMALE);
+    SetProp(P_NOBUY,1); // wird bei Verkauf im laden zerstoert.
+    SetProp(P_WEIGHT,800);
+    SetProp(P_VALUE,5000+random(2000));
+    SetProp(P_ARMOUR_TYPE,AT_GLOVE);
+    SetProp(P_AC,2);
+    SetProp(P_EFFECTIVE_WC,15);
+    SetProp(P_MATERIAL,
+    ([
+        MAT_LEATHER    : 60,
+        MAT_MISC_METAL : 40
+    ]) );
+
+// ---->
+
+    SetProp(P_M_ATTR_MOD,
+    ([
+        A_STR : 2,  // Staerke um 2 erhoehen
+        A_DEX : 0   // Geschicklichkeit blockieren
+    ]) );
+
+// <----
+
+    AddId( ({"handschuhe","krallen"}) );
+
+    AddDetail( "schimmer",
+        "Ein blaeulicher Schimmer liegt auf den Krallen.\n");
+}
+
diff --git a/doc/beispiele/precompiler/konstanten.c b/doc/beispiele/precompiler/konstanten.c
new file mode 100644
index 0000000..687d1a3
--- /dev/null
+++ b/doc/beispiele/precompiler/konstanten.c
@@ -0,0 +1,50 @@
+// Mit dem Praecompiler lassen sich symbolische Konstanten definieren. Dies
+// z.B. sinnvoll, wenn eine Zahl eine bestimmte Bedeutung hat. Z.b. hat die
+// Zahl 42 an sich keine fixe Bedeutung, hier ist die jedoch die Antwort!
+#define ANTWORT 42
+// Schreibt man nun irgendwo im Code ANTWORT, ersetzt der Praecompiler jedes
+// Vorkommen von ANTWORT durch 42.
+
+// auch komplexere Konstanten sind moeglich, z.B. ein Argument eines Defines:
+#define ROOM(x) ("/d/ebene/zesstra/advent/room/"+x)
+// Dies dient meist, um sich Schreibarbeit zu sparen. Formuliert man nach
+// diesem #define ein ROOM("weg1"), erzeugt der Praecompiler hieraus:
+// ("/d/ebene/zesstra/advent/room/weg1")
+
+// Weiteres Beispiel:
+#define BS(x) break_string(x, 78)
+// erzeugt also aus BS("langer text") ein break_string("langer text",78)
+
+// Es gehen auch mehrere Argumente:
+#define BS(x,y) break_string(x, 78, y)
+// BS("langer text", 78, "Zesstra sagt: ")
+//   -> break_string("langer text", 78, "Zesstra sagt: ")
+
+// Solche Defines lassen sich schachteln:
+#define HOME(x) ("/d/ebene/zesstra/advent/"+x)
+#define ROOM(x) (HOME("room/"+x))
+// Ein ROOM("weg1") wird nun durch (HOME("room/"+"weg1")) ersetzt
+// und dieses wiederum durch ein 
+// (("/d/ebene/zesstra/advent/"+"room/"+"weg1")), was letztendlich ein
+// (("/d/ebene/zesstra/advent/room/weg1")) ist.
+
+// Letzteres wird haeufig benutzt bei der Erstellung von Gebieten um sich bei
+// Umzuegen Arbeit zu sparen: zieht das Gebiet um (z.B. von /players/zesstra/
+// nach /d/ebene/zesstra/advent/, reicht es aus, HOME zu aendern und man muss
+// nicht 150mal den kompletten Pfad im Code aendern.
+
+// moechte man ein Define ueber mehrere Zeilen definieren, muss der
+// Zeilenumbruch mit einem \ escaped  werden:
+#define LOG(x) log_file("bla",\
+    "Dies ist ein laengerer Text.\n")
+// WICHTIG: Hinter dem \ darf KEIN Leerzeichen, Tab o.ae. stehen!
+
+// Tips:
+// * Damit man Defines schnell als solche erkennt, sollten alle Defines
+//   grossgeschrieben werden: z.B. PL vs. pl
+// * von komplexen Defines wird abgeraten, weil sie schnell Uebersicht 
+//   und Effizienz ruinieren koennen
+// * einige haeufig benutzte Defines finden sich in <defines.h>
+// * der Driver definiert bestimmte Defines automatisch. Eine Liste
+//   findet sich in /doc/driver/predefined
+
diff --git a/doc/beispiele/rotaugenvampire/.readme b/doc/beispiele/rotaugenvampire/.readme
new file mode 100644
index 0000000..a2ceeb6
--- /dev/null
+++ b/doc/beispiele/rotaugenvampire/.readme
@@ -0,0 +1,3 @@
+Drei Beispielmonster, die wichtige Basiseigenschaften von NPCs sowie
+die Vererbung demonstrieren. Angelehnt an bspmon1.c von Boing.
+(Zesstra)
diff --git a/doc/beispiele/rotaugenvampire/rotaugenvampir.c b/doc/beispiele/rotaugenvampire/rotaugenvampir.c
new file mode 100644
index 0000000..ca464d5
--- /dev/null
+++ b/doc/beispiele/rotaugenvampire/rotaugenvampir.c
@@ -0,0 +1,135 @@
+/*
+   Ein Beispiel-NPC (von Zesstra, angelehnt an Boings bspmon1.c)
+*/
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+// Einige Warnungen werden als Fehler betrachtet (empfohlen)
+#pragma pedantic, range_check
+// Schaltet bestimmte Warnungen ein (empfohlen)
+#pragma warn_deprecated, warn_empty_casts, warn_missing_return
+#pragma warn_function_inconsistent
+
+// Erbt den Standard-NPC / das Standard-Monster
+inherit "/std/npc";
+
+// Inkludiert Definitionen fuer Properties und einige Konstanten
+#include <properties.h>
+#include <language.h>
+#include <class.h> // Konstanten fuer AddClass()
+
+// create() wird beim Erzeugen des Objekt vom Driver gerufen und initialisiert
+// diesen NPC. Es bekommt kein Argument, gibt keinen Wert zurueck (Typ: void)
+// und kann nicht von ausserhalb des Objektes gerufen werden (protected).
+protected void create()
+{
+  // Nicht vergessen, ohne das geht nichts. Fuehrt das create() des geerbten
+  // /std/npc aus und initialisiert den Standard-NPC.
+  ::create();
+
+/* Die Kurzbeschreibung wird zum Beispiel angezeigt wenn man in einen Raum
+   mit dem Monster reinlaeuft */
+  SetProp(P_SHORT, "Rotaugenvampir");
+
+/* Beim Anschauen des Monsters wird die long-description angezeigt,
+ * Zeilenumbruch nach Bedarf nach 78 Zeichen. */
+  SetProp(P_LONG, break_string(
+    "Ein finster aussehender Rotaugenvampir.",78));
+
+/* Ein Name muss sein, da sonst z.B. im Todesfall 'Leiche von 0' daliegt */
+  SetProp(P_NAME, "Rotaugenvampir");
+  SetProp(P_NAME_ADJ,"ausgemergelt");
+  
+/* Das Geschlecht des Monsters. Als Geschlechter sind */
+/* die in <language.h> definierten Symbole NEUTER, MALE   */
+/* und FEMALE zulaessig.                                  */
+  SetProp(P_GENDER, MALE);
+
+/* Ein Monster braucht mindestens eine ID, unter der man es ansprechen kann */
+/* Es kann aber auch mehrere verschiedene ID's haben.                       */
+  AddId( ({"vampir","rotaugenvampir"}) );
+
+/* Zuerst sollte man dem Monster einen Grundlevel geben. */
+/* Setzt Standardwerte fuer P_LEVEL, P_MAX_HP (Lebenspunkte), P_MAX_SP
+ * (Magiepunkte), P_HANDS, P_BODY, P_XP und die Attribute A_STR, A_INT, A_DEX
+ * und A_CON. Alle koennen spaeter noch geaendert werden.
+ */
+  create_default_npc( 25 );
+
+/* Nun machen wir es etwas widerstandsfaehiger, indem wir P_BODY setzen.  */
+/* Nie P_TOTAL_AC oder P_AC setzen, P_TOTAL_AC wird automatisch berechnet */
+/* und P_AC ist nur fuer Ruestungen da.                                   */
+  SetProp(P_BODY, 55);
+
+/* Das Monster schlaegt mit blossen Haenden zu, also wird P_HANDS gesetzt.   */
+/* Auch hier weder P_TOTAL_WC noch P_WC setzen.                              */
+  SetProp(P_HANDS, ({" mit seinen Haenden", 135}));
+/*                    ^ dieses Leerzeichen ist wichtig                       */
+/* Beim Kampf erscheint nun: 'Das Monster greift Dich mit seinen Haenden an' */
+/* 135 entspricht der Waffenklasse                                            */
+
+/* Gesinnung des Monsters, 0 ist neutral, negativ boese und positiv gut */
+  SetProp(P_ALIGN, -100);  /* etwas boese, aber nichts besonderes */
+
+/* Die Rasse des Monsters */
+  SetProp(P_RACE, "Vampir");
+
+/* Erfahrungspunkte des Monsters, beim Tod erhaelt der 'Killer' ein   */
+/* hundertstel dieses Wertes. Schon im Kampf erhaelt man bei jedem    */
+/* Schlag weapon_class*schaden/10 punkte (weapon_class hier 55), also */
+/* insgesamt weapon_class*hit_points/10.                              */
+/* Ab 200000 gibt der NPC einen Stufenpunkt, ab 600000 2 Stufenpunkte */
+  SetProp(P_XP, 130000);
+  
+/* Die Groesse des Monsters in cm. Dies wird bei einigen Kampfbefehlen */
+/* ausgewertet, sowie bei einigen Identifikationsspruechen von Gilden.
+ * Irgendwas zwischen 170 und 190 cm.  */
+  SetProp(P_SIZE, 170 + random(21));
+
+/* Ein Gewicht (in Gramm) sollte der NPC haben, irgendwas zwischen 40 und 48
+ * kg.
+ */
+  SetProp(P_WEIGHT, 40000 + random(8000));
+
+/* Weitere Werte: P_(MAX)_FOOD, P_(MAX)_DRINK, P_(MAX)_ALCOHOL,       */
+/* P_MSGIN, P_MSGOUT, P_MMSGIN, P_MMSGOUT, P_MAX_HANDS                */
+
+/* Spezielle Infos (fuer den Identifiziere-Spruch) */
+  SetProp(P_INFO, break_string(
+    "Der Rotaugenvampir laesst sich vielleicht mit Knoblauch vertreiben.",
+    78));
+
+/* NPC koennen einer Klasse angehoeren und Waffen koennten z.B. gegen
+ * bestimmte Klassen/Rassen besonders (in)effektiv sein.
+ */
+  AddClass(({CL_VAMPIRE,CL_UNDEAD}));
+
+/* Resistenzen (negativ) und Anfaelligkeiten (positiv) */
+  SetProp(P_RESISTANCE_STRENGTHS, ([
+        DT_BLUDGEON:0.2,DT_RIP:0.2,
+        DT_MAGIC: -0.2, DT_COLD: -0.1, DT_TERROR: -0.8 ]) );
+  
+// Wer hat den Spieler gekillt und was sagt man dann?
+  SetProp(P_KILL_NAME,"Ein Rotaugenvampir");
+  SetProp(P_KILL_MSG,({"Grossartig %s! Ein weiteres Opfer fuer Zesstra!", 
+                       MSG_SAY}));
+
+  // beschwerde auf -moerder
+  SetProp(P_MURDER_MSG,"Du steckst doch mit Jof im Bunde, %s!");
+  SetProp(P_DIE_MSG," bricht stoehnend zusammen.\n");
+
+  // Auch NPC sollten Details haben:
+  AddDetail( ({"augen","augenlider","lider"}), break_string(
+    "Der Rotaugenvampir schaut Dich mit weit aufgerissenen, rot "
+    "schimmernden Augen wild an.",78));
+  // ...
+
+  // riecht der nach was? -> AddSmells()
+  
+  // Kann man den nach was fragen? -> AddInfo().
+
+}
+
diff --git a/doc/beispiele/rotaugenvampire/rotaugenvampirmagier.c b/doc/beispiele/rotaugenvampire/rotaugenvampirmagier.c
new file mode 100644
index 0000000..a6c5aaf
--- /dev/null
+++ b/doc/beispiele/rotaugenvampire/rotaugenvampirmagier.c
@@ -0,0 +1,77 @@
+/*
+   Ein magisch begabter Beispiel-NPC (von Zesstra, angelehnt an Boings
+   bspmon1.c)
+*/
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+// Einige Warnungen werden als Fehler betrachtet (empfohlen)
+#pragma pedantic, range_check
+// Schaltet bestimmte Warnungen ein (empfohlen)
+#pragma warn_deprecated, warn_empty_casts, warn_missing_return
+#pragma warn_function_inconsistent
+
+// Dieser NPC soll ein Rotaugenvampir sein, der ein paar Spells kann. Und nen
+// Rotaugenvampir gibt es schon. Also einfach den normalen Rotaugenvampir
+// aus diesem Verzeichnis erben.
+inherit __DIR__+"rotaugenvampir";
+
+// Inkludiert Definitionen fuer Properties
+#include <properties.h>
+
+protected void create()
+{
+  // Nicht vergessen, ohne das geht nichts. Fuehrt das create() des geerbten
+  // Rotaugenvampir aus und konfiguriert ihn.
+  ::create();
+
+/* Die Kurzbeschreibung wird zum Beispiel angezeigt wenn man in einen Raum
+   mit dem Monster reinlaeuft */
+  SetProp(P_SHORT, "Rotaugenvampirmagier");
+
+/* Beim Anschauen des Monsters wird die long-description angezeigt,
+ * Zeilenumbruch nach Bedarf nach 78 Zeichen. */
+  SetProp(P_LONG, break_string(
+    "Ein finster aussehender Rotaugenvampir in einer Kutte.",78));
+
+/* Ein Name muss sein, da sonst z.B. im Todesfall 'Leiche von 0' daliegt */
+  SetProp(P_NAME, "Rotaugenvampirmagier");
+  SetProp(P_NAME_ADJ,"listenreich");
+
+  // fast alle Eigenschaften uebernehmen wir einfach unveraendert unveraendert
+  // vom normalen Rotaugenvampir. Die Hands nicht, der Magier haut nicht so
+  // gut zu...
+  SetProp(P_HANDS, ({" mit seinen Haenden", 85}));
+
+  // ok, spielt wenig Rolle in diesem Fall, aber damits stilecht ist, kriegt
+  // der hier jetzt etwas mehr an Magiepunkten (spellpoints)
+  SetProp(P_MAX_SP, 350);
+
+  // und noch ein paar Spells
+  // Anmerkung: diese Spells haben nichts mit irgendwelchen Gildenspells zu
+  // tun und - ja, tatsaechlich - verbrauchen keine Magiepunkte...
+
+  // wahrscheinlichkeit in % pro runde, einen Spell zu casten:
+  SetProp(P_SPELLRATE,30);
+  // Ein Spell, der 100 Schadenspunkte macht und in 60% der Faelle ausgewaehlt
+  // wird. Erste Meldung geht an den Spieler, die zweite an den Raum.
+  // Schadenstyp ist Schlagschaden.
+  AddSpell(60,100,
+    break_string(Name(WER) + " macht eine Handbewegung vor Deinem Gesicht. "
+      "So abgelenkt stolperst Du in faellst hart zu Boden.",78),
+    break_string(Name(WER) + " macht eine Handbewegung vor @WESSEN Gesicht. "
+      "@WER stolpert und faellt hart auf den Boden.",78),
+    ({DT_BLUDGEON}) );
+  // Ein Spell, der 800 Schadenspunkte macht und in 40% der Faelle ausgewaehlt
+  // wird. Schadenstyp: Feuer.
+  AddSpell(40,800,
+    break_string(Name(WER) + " schaut Dich mit brennenden Augen "
+      "durchdringend an. Du glaubst, innerlich zu verbrennen.",78),
+    break_string(Name(WER) + " schaut @WEN mit gluehenden Augen an. "
+      "@WER kruemmt sich vor Schmerzen.",78),
+    ({DT_FIRE}) );
+}
+
diff --git a/doc/beispiele/rotaugenvampire/rotaugenvampirmagierin.c b/doc/beispiele/rotaugenvampire/rotaugenvampirmagierin.c
new file mode 100644
index 0000000..b506e1e
--- /dev/null
+++ b/doc/beispiele/rotaugenvampire/rotaugenvampirmagierin.c
@@ -0,0 +1,48 @@
+/*
+   Ein magisch begabter Beispiel-NPC (von Zesstra, angelehnt an Boings
+   bspmon1.c)
+   In diesem Fall soll eine Magierin sein, die aber die gleichen Stats wie der
+   maennliche Kollege hat.
+*/
+
+// Diese Pragmas sorgen dafuer, dass der Driver darauf achtet, dass bei
+// Funktionsargumenten, -Rueckgabewerten und teilweise bei Zuweisung von
+// Werten an Variablen der richtige Datentyp verwendet wird (z.b. kein string
+// anstelle eines int verwendet wird). Sollte in keinem Objekt fehlen.
+#pragma strong_types, save_types, rtt_checks
+// Einige Warnungen werden als Fehler betrachtet (empfohlen)
+#pragma pedantic, range_check
+// Schaltet bestimmte Warnungen ein (empfohlen)
+#pragma warn_deprecated, warn_empty_casts, warn_missing_return
+#pragma warn_function_inconsistent
+
+// Dieser NPC soll eine Rotaugenvampirmagierin sein. Und nen
+// Rotaugenvampirmagier gibt es schon. Also einfach den normalen
+// Rotaugenvampirmagier aus diesem Verzeichnis erben.
+inherit __DIR__+"rotaugenvampirmagier";
+
+// Inkludiert Definitionen fuer Properties
+#include <properties.h>
+#include <language.h> // fuer FEMALE-Define
+
+protected void create()
+{
+  // Nicht vergessen, ohne das geht nichts. Fuehrt das create() des geerbten
+  // Rotaugenvampirmagiers aus und konfiguriert ihn.
+  ::create();
+
+  // Geschlecht aendern.
+  SetProp(P_GENDER, FEMALE);
+
+  // Kurz- und Langbeschreibung aendern.
+  SetProp(P_SHORT, "Rotaugenvampirmagierin");
+  SetProp(P_LONG, break_string(
+    "Eine finster aussehende Rotaugenvampirmagierin in einer Kutte.",78));
+
+  // Name anpassen
+  SetProp(P_NAME, "Rotaugenvampirmagierin");
+
+  // Meldung anpassen.
+  SetProp(P_HANDS, ({" mit ihren Haenden", 85}));
+}
+
diff --git a/doc/beispiele/shadow/aa.c b/doc/beispiele/shadow/aa.c
new file mode 100644
index 0000000..179d7e3
--- /dev/null
+++ b/doc/beispiele/shadow/aa.c
@@ -0,0 +1,8 @@
+     void fun() {
+         printf("%O [a] fun()\n", this_object());
+     }
+
+     void fun3() {
+         printf("%O [a] fun3()\n", this_object());
+     }
+
diff --git a/doc/beispiele/shadow/bb.c b/doc/beispiele/shadow/bb.c
new file mode 100644
index 0000000..9c77090
--- /dev/null
+++ b/doc/beispiele/shadow/bb.c
@@ -0,0 +1,13 @@
+     int fun() {
+         printf("%O [b] fun()\n", this_object());
+         find_object("/doc/beispiele/shadow/aa")->fun();
+     }
+
+     void fun2() {
+         printf("%O [b] fun2()\n", this_object());
+         find_object("/doc/beispiele/shadow/aa")->fun3();
+         this_object()->fun3();
+     }
+
+     void do_shadow(object target) { shadow(target, 1); }
+
diff --git a/doc/beispiele/shadow/cc.c b/doc/beispiele/shadow/cc.c
new file mode 100644
index 0000000..8aa60ed
--- /dev/null
+++ b/doc/beispiele/shadow/cc.c
@@ -0,0 +1,11 @@
+     int fun() {
+         printf("%O [c] fun()\n", this_object());
+         find_object("/doc/beispiele/shadow/aa")->fun();
+     }
+
+     void fun3() {
+         printf("%O [c] fun3()\n", this_object());
+     }
+
+     void do_shadow(object target) { shadow(target, 1); }
+
diff --git a/doc/beispiele/shadow/loadme.c b/doc/beispiele/shadow/loadme.c
new file mode 100644
index 0000000..9d6e4d5
--- /dev/null
+++ b/doc/beispiele/shadow/loadme.c
@@ -0,0 +1,24 @@
+void create() {
+     object a, b, c;
+
+     if (find_object("/doc/beispiele/shadow/aa"))
+       destruct(find_object("/doc/beispiele/shadow/aa"));
+     a = load_object("/doc/beispiele/shadow/aa");
+     if (find_object("/doc/beispiele/shadow/bb"))
+       destruct(find_object("/doc/beispiele/shadow/bb"));
+     b = load_object("/doc/beispiele/shadow/bb");
+     if (find_object("/doc/beispiele/shadow/cc"))
+       destruct(find_object("/doc/beispiele/shadow/cc"));
+     c = load_object("/doc/beispiele/shadow/cc");
+
+     b->do_shadow(a);
+     c->do_shadow(a);
+     printf("--- a->fun() ---\n");
+     a->fun();
+     printf("--- b->fun() ---\n");
+     b->fun();
+     printf("--- c->fun() ---\n");
+     c->fun();
+     printf("--- b->fun2() ---\n");
+     b->fun2();
+}
diff --git a/doc/beispiele/ssp/README b/doc/beispiele/ssp/README
new file mode 100644
index 0000000..bd1cad1
--- /dev/null
+++ b/doc/beispiele/ssp/README
@@ -0,0 +1,3 @@
+Diese Dateien sind nur als Beispiele zu verwenden und nicht in einem
+anderen Mud als dem MorgenGrauen fuer Spieler zugaenglich zu machen.
+  Boing
diff --git a/doc/beispiele/ssp/files.h b/doc/beispiele/ssp/files.h
new file mode 100644
index 0000000..8c5951a
--- /dev/null
+++ b/doc/beispiele/ssp/files.h
@@ -0,0 +1,13 @@
+#define SM(x) ("/doc/beispiele/ssp/"+x)
+#define L1(x) (SM("l1/"+x))
+#define L2(x) (SM("l2/"+x))
+#define L3(x) (SM("l3/"+x))
+#define L4(x) (SM("l4/"+x))
+#define L5(x) (SM("l5/"+x))
+#define L6(x) (SM("l6/"+x))
+#define MON(x) (SM("mon/"+x))
+#define OBJ(x) (SM("obj/"+x))
+
+#define LOG_OBJ "/players/boing/obj/logobj"
+#define PFLANZE "/doc/beispiele/ssp/mon/pflanze"
+#define MMONST "/doc/beispiele/ssp/mon/metallmonster"
diff --git a/doc/beispiele/ssp/l1/m1x1.c b/doc/beispiele/ssp/l1/m1x1.c
new file mode 100644
index 0000000..f954372
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m1x1.c
@@ -0,0 +1,30 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include "../files.h"
+
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Der Raum ist gefuellt\n"+
+  "mit grau-blauem Rauch, der von dem grossen Drachen stammt, der ihn be-\n"+
+  "wohnt. Es ist sehr warm hier drin und an den Waenden und an der Decke\n"+
+  "kondensiert der Dampf zu kleinen Tropfen. Der einzige Ausgang liegt im\n"+
+  "Osten.\n");
+  AddDetail("raum", "Ein Drache wohnt hier normalerweise.\n");
+  AddDetail(({"drache", "drachen"}), "Er scheint gerade nicht da zu sein.\n");
+  AddDetail("rauch", "Der Rauch zieht zum Ausgang im Osten hinaus.\n");
+  AddDetail(({"wand", "waende", "boden", "decke", "metall", "stahl"}), "Alles hier ist aus reinem Stahl.\n");
+  AddDetail("dampf", "Er kondensiert an den Waenden.\n");
+  AddDetail(({"wasser", "kondenswasser"}), "Es tropft zu Boden.\n");
+  AddDetail("tropfen", "Sie tropfen auf den Boden.\n");
+  AddDetail("ausgang", "Er liegt im Osten.\n");
+  AddItem(MON("stahldrache"), REFRESH_REMOVE);
+  AddExit("osten",L1("m2x1"));
+}
diff --git a/doc/beispiele/ssp/l1/m1x2.c b/doc/beispiele/ssp/l1/m1x2.c
new file mode 100644
index 0000000..c91355a
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m1x2.c
@@ -0,0 +1,27 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include "../files.h"
+
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. In dieser gemuetlichen\n"+
+  "Ecke (natuerlich nur fuer Metall liebende Lebewesen) hat sich wohl eine\n"+
+  "der merkwuerdigen Titanwalzen eingenistet, auf jeden Fall ist der Boden\n"+
+  "dermassen plattgewalzt, wie es nur diese Art schaffen kann. Der Gang\n"+
+  "selbst fuehrt hier nach Sueden und Osten.\n");
+  AddDetail("ecke", "Richtig nett hier ...\n");
+  AddDetail(({"wand", "waende", "decke", "metall"}), "Alles hier ist aus Metall.\n");
+  AddDetail(({"walze", "titanwalze"}), "Die Titanwalze scheint gerade nicht daheim zu sein.\n");
+  AddDetail("boden", "Der Boden ist absolut platt.\n");
+  AddDetail("gang", "Ein aus Metall gearbeiteter Gang.\n");
+  AddExit("sueden",L1("m1x3"));
+  AddExit("osten",L1("m2x2"));
+  AddItem(MON("titanwalze"), REFRESH_REMOVE);
+}
diff --git a/doc/beispiele/ssp/l1/m1x3.c b/doc/beispiele/ssp/l1/m1x3.c
new file mode 100644
index 0000000..6093029
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m1x3.c
@@ -0,0 +1,24 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Eine lange Metallroehre\n"+
+  "fuehrt weiter von Sueden nach Norden und scheint kein Ende zu nehmen. Deine\n"+
+  "Schritte hallen blechern durch die Gaenge und informieren deren Bewohner\n"+
+  "ueber Deine Anwesenheit.\n");
+  AddDetail(({"metallroehre", "roehre", "gang", "gaenge"}), "Du befindest Dich in einer langen Metallroehre.\n");
+  AddDetail("metall", "Das Metall sieht sehr stabil aus.\n");
+  AddDetail(({"boden", "decke", "wand", "waende"}), "Alles ist aus Metall.\n");
+  AddDetail("bewohner", "Hier scheinen keine zu sein.\n");
+  AddExit("norden",L1("m1x2"));
+  AddExit("sueden",L1("m1x4"));
+}
diff --git a/doc/beispiele/ssp/l1/m1x4.c b/doc/beispiele/ssp/l1/m1x4.c
new file mode 100644
index 0000000..a279aad
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m1x4.c
@@ -0,0 +1,24 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Die Metallroehre, in der\n"+
+  "Du Dich befindest fuehrt von Sueden nach Norden. Auch hier klingt der Boden\n"+
+  "unter Deinen Fuessen recht hohl, es ist wohl wirklich etwas Besonderes\n"+
+  "darunter. Allerdings sind keinerlei Anzeichen fuer einen Zugang zu sehen.\n");
+  AddDetail(({"gang", "roehre", "metallroehre"}), "Der Gang fuehrt nach Norden und Sueden.\n");
+  AddDetail("metall", "Hartes, glaenzendes Metall.\n");
+  AddDetail(({"wand", "waende", "boden", "decke"}), "Alles ist aus Metall.\n");
+  AddDetail("zugang", "Es ist keiner zu sehen.\n");
+  AddExit("norden",L1("m1x3"));
+  AddExit("sueden",L1("m1x5"));
+}
diff --git a/doc/beispiele/ssp/l1/m1x5.c b/doc/beispiele/ssp/l1/m1x5.c
new file mode 100644
index 0000000..694fb6a
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m1x5.c
@@ -0,0 +1,31 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include <moving.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Der voellig mit Metall\n"+
+  "verkleidete Gang macht hier eine Biegung von Osten nach Norden, wo er\n"+
+  "in die schwarze Dunkelheit fuehrt. Deine Schritte hallen hier leicht nach,\n"+
+  "es scheint sich wohl ein Hohlraum unter dem Boden zu befinden.\n");
+  AddDetail("metall", "Es scheint Eisen oder Stahl zu sein.\n");
+  AddDetail(({"eisen", "stahl"}), "Du weisst nicht genau, ob es Eisen oder Stahl ist.\n");
+  AddDetail(({"gang", "roehre"}), "Der Gang macht einen Knick.\n");
+  AddDetail("dunkelheit", "Sie verschlingt alles.\n");
+  AddDetail(({"wand", "waende", "boden", "decke"}), "Alles ist aus Metall.\n");
+  AddDetail("hohlraum", "Du kannst keine Hinweise auf einen Zugang finden.\n");
+  AddDetail("zugang", "Du siehst keinen.\n");
+  AddDetail(({"knick", "biegung"}), "So nennt sich sowas halt ... ich kann nix dafuer!\n");
+  AddItem(MON("kampfblech"), REFRESH_REMOVE);
+  
+  AddExit("norden", L1("m1x4"));
+  AddExit("osten",L1("m2x5"));
+}
diff --git a/doc/beispiele/ssp/l1/m2x1.c b/doc/beispiele/ssp/l1/m2x1.c
new file mode 100644
index 0000000..d4e7ae7
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m2x1.c
@@ -0,0 +1,27 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Dieser Gang hier ist aus\n"+
+  "reinstem Stahl gefertigt, der in Deinem Licht matt funkelt. Aus einer\n"+
+  "Kammer im Westen dringen Rauchschwaden hervor und ein Schnauben ist zu\n"+
+  "hoeren. Du kannst es allerdings auch vorziehen, nach Sueden dem Unheil\n"+
+  "zu entgehen.\n");
+  AddDetail(({"gang", "gaenge", "roehre", "roehren"}), "Der Gang macht eine Biegung von Sueden nach Westen.\n");
+  AddDetail(({"boden", "decke", "wand", "waende", "metall", "stahl"}), "Alles hier ist aus hartem Stahl.\n");
+  AddDetail("kammer", "Im Westen liegt eine Kammer.\n");
+  AddDetail(({"rauchschwaden", "rauch", "schwaden"}), "Der Rauch zieht durch ein paar Ritzen in der Decke ab.\n");
+  AddDetail(({"ritze", "ritzen"}), "Die Ritzen haben nichts Bemerkenswertes an sich.\n");
+  AddDetail("schnauben", "Von wem wohl das Schnauben kommt?\n");
+  AddExit("westen",L1("m1x1"));
+  AddExit("sueden",L1("m2x2"));
+}
diff --git a/doc/beispiele/ssp/l1/m2x2.c b/doc/beispiele/ssp/l1/m2x2.c
new file mode 100644
index 0000000..8cefccd
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m2x2.c
@@ -0,0 +1,26 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Hier verzweigt sich der\n"+
+  "Gang in verschiedene Metallroehren, die nach Norden, Sueden und Westen\n"+
+  "fuehren. Ausserdem ist in der Decke ein kleines Loch, durch das eine\n"+
+  "Leiter nach oben fuehrt.\n");
+  AddDetail(({"gang", "gaenge", "metallroehre", "metallroehren", "roehre", "roehren"}), "Gaenge fuehren in die verschiedensten Richtungen.\n");
+  AddDetail(({"wand", "waende", "boden", "metall"}), "Hier ist einfach alles aus Metall.\n");
+  AddDetail("leiter", "Die Leiter fuehrt durch ein Loch in der Decke nach oben.\n");
+  AddDetail(({"loch", "decke"}), "In der Decke ist ein Loch.\n");
+  AddExit("sueden",L1("m2x3"));
+  AddExit("norden",L1("m2x1"));
+  AddExit("westen",L1("m1x2"));
+  AddExit("oben", L1("oben"));
+}
diff --git a/doc/beispiele/ssp/l1/m2x3.c b/doc/beispiele/ssp/l1/m2x3.c
new file mode 100644
index 0000000..f291ac2
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m2x3.c
@@ -0,0 +1,27 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include "../files.h"
+
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Der Gang, in dem Du\n"+
+  "Dich befindest fuehrt schnurgerade von Norden nach Sueden. Die Waende\n"+
+  "reflektieren das Licht und Deine Schritte erzeugen einen metallischen\n"+
+  "Klang auf dem Boden.\n"); 
+  AddDetail(({"boden", "wand", "waende", "decke", "metall"}), "Alles ist aus Metall.\n");
+  AddDetail("gang", "Er fuehrt nach Norden und Sueden.\n");
+  AddDetail("licht", "Es kommt irgendwoher.\n");
+  AddDetail("schritte", "Wenn Du laeufst, machst Du Schritte, das wusstest Du aber, oder?\n");
+  AddDetail("klang", "Der Klang ist nicht sonderlich interessant, aber zweifellos vorhanden.\n");
+  AddExit("norden",L1("m2x2"));
+  AddExit("sueden",L1("m2x4"));
+  AddItem(MON("dosenoeffner"), REFRESH_REMOVE);
+}
diff --git a/doc/beispiele/ssp/l1/m2x4.c b/doc/beispiele/ssp/l1/m2x4.c
new file mode 100644
index 0000000..674b44e
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m2x4.c
@@ -0,0 +1,22 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Der voellig mit Metall\n"+
+  "ausgeschlagene Gang macht hier einen scharfen Knick von Norden nach Osten\n"+
+  "und verschwindet dort in der Dunkelheit.\n");
+  AddDetail(({"wand", "waende", "boden", "decke", "metall"}), "Alles hier ist aus Metall.\n");
+  AddDetail("gang", "Der Gang fuehrt nach Norden und Osten.\n");
+  AddDetail("dunkelheit", "Du kannst hingehen und schauen, ob sie dann noch da ist.\n");
+  AddExit("norden",L1("m2x3"));
+  AddExit("osten",L1("m3x4"));
+}
diff --git a/doc/beispiele/ssp/l1/m2x5.c b/doc/beispiele/ssp/l1/m2x5.c
new file mode 100644
index 0000000..95b0c3d
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m2x5.c
@@ -0,0 +1,26 @@
+inherit "std/room";
+
+#include <rooms.h>
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Die Waende des Ganges,\n"+
+  "der von Osten nach Westen (oder andersrum) verlaeuft, sind mit glaenzendem\n"+
+  "Metall ausgekleidet. Irgendwie kommt Dir diese Umgebung ziemlich seltsam\n"+
+  "vor, aber keine Sorge, es kommt noch viel schlimmer!\n");
+  AddDetail(({"gang", "roehre"}), "Der Gang verlaeuft von Osten nach Westen.\n");
+  AddDetail(({"wand", "waende", "decke", "boden"}), "Alles ist aus glaenzendem Metall.\n");
+  AddDetail("metall", "Das Metall ist an einer Stelle leicht eingedellt.\n");
+  AddDetail(({"delle", "stelle", "beule"}), "Da wollte wohl jemand mit dem Kopf durch die Wand.\n");
+  AddExit("osten",L1("m3x5"));
+  AddExit("westen",L1("m1x5"));
+  AddItem(MON("dosenoeffner"), REFRESH_REMOVE);
+  AddItem(MON("dosenoeffner"), REFRESH_REMOVE);
+}
diff --git a/doc/beispiele/ssp/l1/m3x1.c b/doc/beispiele/ssp/l1/m3x1.c
new file mode 100644
index 0000000..30022f4
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m3x1.c
@@ -0,0 +1,31 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Steinebene");
+  SetProp(P_INT_LONG,
+  "Du bist am Eingang zum Hoehlenlabyrinth der Schreckensspitze. Etwas Licht\n"+
+  "dringt vom Ausgang im Norden herein, doch im Sueden lauert die pechschwarze\n"+
+  "Dunkelheit.\n");
+  AddDetail("eingang", "Es ist der Eingang zu mehr, als Du denkst.\n");
+  AddDetail("boden", "Der Boden besteht aus grauem Fels.\n");
+  AddDetail("fels", "Der Fels ist nicht besonders interessant, nur grau.\n");
+  AddDetail(({"wand", "waende"}), "Die Waende sind von Orkblut beschmutzt.\n");
+  AddDetail(({"orkblut", "blut"}), "Orks werden hier wohl zum Spass gemetzelt.\n");
+  AddDetail("decke", "Ueber Deinem Kopf, das wird Dir hier noch oefters passieren.\n");
+  AddDetail(({"hoehlenlabyrinth", "labyrinth", "hoehlen"}), "Diese Labyrinth erstreckt sich ueber etliche Ebenen, viel Spass beim\nErkunden.\n");
+  AddDetail("licht", "Das letzte Tageslicht.\n");
+  AddDetail("ausgang", "Der Ausgang aus der Dunkelheit.\n");
+  AddDetail("dunkelheit", "Das wird Dir dort unten noch oefters begegnen.\n");
+  AddItem(MON("ork"), REFRESH_REMOVE);
+  AddItem(MON("ork"), REFRESH_REMOVE);
+  AddExit("norden", "/d/gebirge/room/spitze4");
+  AddExit("sueden",L1("m3x2"));
+}
diff --git a/doc/beispiele/ssp/l1/m3x2.c b/doc/beispiele/ssp/l1/m3x2.c
new file mode 100644
index 0000000..d913527
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m3x2.c
@@ -0,0 +1,29 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Steinebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Ein schmaler Gang, der\n"+
+  "von Norden nach Sueden fuehrt, ist hier durch den harten Fels gehauen.\n"+
+  "Der Gang ist nur grob behauen, ein Zeichen dafuer, dass hier nicht Zwerge\n"+
+  "am Werk waren, sondern boese Wesen, wahrscheinlich Orks.\n");
+  AddDetail("fels", "Der Fels ist hart und nur grob behauen.\n");
+  AddDetail(({"gang", "gaenge"}), "Der Gang fuehrt von Norden nach Sueden.\n");
+  AddDetail("boden", "Auf dem Boden siehst Du zahlreiche Ueberreste von Abenteurern, die nicht\nvorsichtig genug waren.\n");
+  AddDetail(({"rest", "ueberrest", "reste", "ueberreste", "abenteurer"}),
+  "Uhoh ... blutige Sache ...\n");
+  AddDetail("blut", "Weia ...\n");
+  AddDetail(({"wand", "waende"}), "Die Waende sind aus Fels und nicht sonderlich interessant.\n");
+  AddDetail("decke", "Wenn Du ueberlegst, was da alles ueber Deinem Kopf schwebt ... ohoh!\n");
+  AddDetail(({"ork", "orks"}), "Davon hat es sicher einige in der Naehe.\n");
+  AddDetail(({"zwerg", "zwerge"}), "Zwerge haben hiermit nichts zu tun!\n");
+  AddExit("sueden",L1("m3x3"));
+  AddExit("norden",L1("m3x1"));
+}
diff --git a/doc/beispiele/ssp/l1/m3x3.c b/doc/beispiele/ssp/l1/m3x3.c
new file mode 100644
index 0000000..f84fce5
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m3x3.c
@@ -0,0 +1,27 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Steinebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Der schmale Gang macht\n"+
+  "hier eine Biegung von Norden nach Osten, wo er jeweils in die Dunkelheit\n"+
+  "fuehrt.\n");
+  AddDetail("gang", "Der Gang macht eine Biegung.\n");
+  AddDetail("dunkelheit", "Schwarze Dunkelheit.\n");
+  AddDetail("boden", "Auf dem Boden ist nichts Besonderes zu sehen.\n");
+  AddDetail("decke", "Die Decke ist total langweilig.\n");
+  AddDetail(({"wand", "waende"}), "An der Wand haengt eine Hand.\n");
+  AddDetail("hand", "Ach nee, hat sich nur gereimt.\n");
+  AddDetail("biegung", "Sie fuehrt nach Norden und Osten.\n");
+  AddExit("norden",L1("m3x2"));
+  AddExit("osten",L1("m4x3"));
+  AddItem(MON("ork"), REFRESH_REMOVE);
+}
diff --git a/doc/beispiele/ssp/l1/m3x4.c b/doc/beispiele/ssp/l1/m3x4.c
new file mode 100644
index 0000000..549492a
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m3x4.c
@@ -0,0 +1,39 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include <moving.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Metallbeschlagene Waende\n"+
+  "umringen Dich im Norden, Osten und Sueden waehrend ein roehrenartiger Gang\n"+
+  "nach Westen fuehrt. Ausserdem befindet sich im Boden ein Loch, durch das\n"+
+  "eine Leiter nach unten fuehrt.\n");
+  AddDetail("loch", "Was einen da unten wohl erwarten mag?\n");
+  AddDetail("leiter", "Die Leiter fuehrt nach unten.\n");
+  AddDetail("boden", "Im Boden ist ein Loch.\n");
+  AddDetail(({"wand", "waende", "decke", "metall"}), "Alles ist aus Metall.\n");
+  AddDetail(({"roehre", "gang"}), "Der Gang fuehrt nach Westen.\n");
+  AddItem(MON("titanwalze"), REFRESH_REMOVE);
+  AddExit("westen",L1("m2x4"));
+  AddSpecialExit("unten", "unten");
+}
+
+unten()
+{
+  if (present("titanwalze"))
+  {
+    write("Die Titanwalze laesst Dich nicht vorbei.\n");
+    return 1;
+  }
+  this_player()->move(L2("m3x4"), M_GO, "nach unten");
+  this_player()->SetProp("boing:drom_marker", 1);
+  return 1;
+}
diff --git a/doc/beispiele/ssp/l1/m3x5.c b/doc/beispiele/ssp/l1/m3x5.c
new file mode 100644
index 0000000..69c58fa
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m3x5.c
@@ -0,0 +1,26 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Der roehrenartige Gang\n"+
+  "fuehrt von Osten nach Westen durch die Dunkelheit, nur hier glaenzt das\n"+
+  "Metall in Deinem Licht.\n");
+  AddDetail(({"gang", "roehre"}), "Wer wohl diesen Gang angelegt hat?\n");
+  AddDetail("dunkelheit", "Du kannst sie nicht erreichen, solange Du Dein Licht an laesst.\n");
+  AddDetail("metall", "Alles hier ist aus blankem Metall.\n");
+  AddDetail("boden", "Ziemlich uninteressant.\n");
+  AddDetail(({"wand", "waende"}), "Hey! Da ist eine Inschrift!\n");
+  AddDetail("inschrift", "Das ist etwas, was man lesen kann.\n");
+  AddReadDetail("inschrift", "Du liest: Boing war hier.\n");
+  AddDetail("decke", "Ist sie nicht huebsch?\n");
+  AddExit("osten",L1("m4x5"));
+  AddExit("westen",L1("m2x5"));
+}
diff --git a/doc/beispiele/ssp/l1/m4x1.c b/doc/beispiele/ssp/l1/m4x1.c
new file mode 100644
index 0000000..a64351e
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m4x1.c
@@ -0,0 +1,38 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INT_SHORT, "Steinebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Du hast die Hoehle eines\n"+
+  "schrecklichen Untiers erreicht. Der Gestank ist wirklich grauenerregend\n"+
+  "und der Anblick rumliegender Knochen und halb verwester Koerperteile von\n"+
+  "armen Opfern tut sein Uebriges, um Dir den Appetit zu verderben. Der\n"+
+  "einzige Ausgang aus diesem Loch befindet sich im Osten.\n");
+  AddDetail(({"hoehle", "loch"}), "Ein uebles Loch, in das Du hier geraten bist.\n");
+  AddDetail("gestank", "Dir wird beinahe schlecht.\n");
+  AddDetail(({"teile", "koerperteile"}), "Buah, das ist ja widerlich.\n");
+  AddDetail("ausgang", "Der Ausgang ist im Osten.\n");
+  AddSpecialDetail("knochen", "knochen");
+  AddDetail(({"wand", "waende"}), "Die Waende sind langweilig und grau.\n");
+  AddDetail("decke", "Die Decke ist uninteressant.\n");
+  AddDetail("boden", "Die verwesenden Koerperteile auf dem Boden sind kein schoener Anblick.\n");
+  AddExit("osten",L1("m5x1"));
+  AddItem(MON("trollmops"), REFRESH_REMOVE);
+}
+
+knochen()
+{
+   if (present("trollmops"))
+      return "Der Trollmops laesst nicht zu, dass Du sein Abendessen durchwuehlst.\n";
+
+   if (this_player()->FindPotion("Du wuehlst in den Knochen herum und findest einen Zaubertrank.\n"))
+      return "";
+   return "Du wuehlst in den Knochen herum, findest aber nichts.\n";
+}
diff --git a/doc/beispiele/ssp/l1/m4x2.c b/doc/beispiele/ssp/l1/m4x2.c
new file mode 100644
index 0000000..1456ef2
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m4x2.c
@@ -0,0 +1,41 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Steinebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Das hier scheint wohl\n"+
+  "der Aufenthaltsraum von ein paar stinkenden Trollen zu sein. Eine Menge\n"+
+  "alter Knochen liegen auf dem Boden rum und in der Ecke stinkt ein grosser\n"+
+  "Haufen Trollkot vor sich hin. Alles in allem kein gemuetlicher Ort,\n"+
+  "vielleicht solltest Du ihn nach Sueden verlassen.\n");
+  AddDetail("knochen", "Die Knochen wurden von Trollen abgenagt.\n");
+  AddDetail("boden", "Auf dem Boden liegen Knochen.\n");
+  AddDetail("decke", "Die Decke hier ist nicht so grauenhaft interessant, ich wette es wird\ninteressanter, wenn Du ein paar Ebenen tiefer vordringst.\n");
+  AddDetail(({"ebene", "ebenen"}), "Tja, wenn Du wuesstest wieviele Hohlraeume sich unter Dir befinden, dann\nwuerdest Du hier nicht so ruhig stehen.\n");
+  AddDetail(({"hohlraum", "hohlraeume"}), "Erkunde sie! Sofort!\n");
+  AddDetail(({"wand", "waende"}), "Sie sind verschmiert und ekelhaft.\n");
+  AddDetail("troll", "Keiner da! Aetschbaetsch!\n");
+  AddDetail("ecke", "Wuerg!\n");
+  AddDetail(({"trollkot", "kot", "haufen"}), "Igitt! Das stinkt ja erbaermlich.\n");
+  AddExit("sueden",L1("m4x3"));
+  AddItem(MON("troll"), REFRESH_REMOVE);
+  AddItem(MON("troll"), REFRESH_REMOVE);
+  AddCmd("erkunde", "erkunden");
+}
+
+erkunden(str)
+{ 
+   notify_fail("Was moechtest Du erkunden?\n");
+   if (!str || (str!="hohlraum" && str !="hohlraeume"))
+      return 0;
+   write("Also ein bisschen mehr musst Du da schon fuer tun.\n");
+   return 1;
+}
diff --git a/doc/beispiele/ssp/l1/m4x3.c b/doc/beispiele/ssp/l1/m4x3.c
new file mode 100644
index 0000000..37472a9
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m4x3.c
@@ -0,0 +1,31 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Steinebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Der durch den harten\n"+
+  "Fels gehauene Gang verzweigt sich hier nach Norden, Osten und Westen.\n"+
+  "Du stellst fest, dass vom Norden her ein ziemlich unangenehmer Geruch\n"+
+  "kommt.\n");
+  AddDetail("fels", "Der Fels wurde von Orks oder Trollen bearbeitet, und das ziemlich schlecht.\n");
+  AddDetail(({"gang", "gaenge"}), "Der Gang verzweigt sich hier.\n");
+  AddDetail("geruch", "Man koennte es auch Gestank nennen.\n");
+  AddDetail("gestank", "Wenn der Himmel hier sichtbar waere, wuerde es zu ihm stinken.\n");
+  AddDetail("boden", "Auf dem Boden findest Du nichts von Bedeutung.\n");
+  AddDetail("decke", "Grauer Fels.\n");
+  AddDetail(({"wand", "waende"}), "An der Wand siehst Du eine Inschrift.\n");
+  AddDetail("inschrift", "Du kannst sie lesen.\n");
+  AddReadDetail("inschrift", "Du liest: Nicht alles, was man nicht sieht, ist auch wichtig.\n");
+  AddDetail(({"orks", "trolle"}), "Die laufen hier irgendwo rum ... oder liegen tot in der Ecke.\n");
+  AddDetail("ecke", "In dieser Ecke sind jedenfalls keine.\n");
+  AddExit("norden",L1("m4x2"));
+  AddExit("osten",L1("m5x3"));
+  AddExit("westen",L1("m3x3"));
+}
diff --git a/doc/beispiele/ssp/l1/m4x4.c b/doc/beispiele/ssp/l1/m4x4.c
new file mode 100644
index 0000000..be0485f
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m4x4.c
@@ -0,0 +1,28 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Dies ist zweifellos die\n"+
+  "Heimat eines der beruechtigten Eisenfresser, die Waende sehen auch schon\n"+
+  "ganz zernagt aus. Der einzige Ausgang fuehrt nach Sueden.\n");
+  AddDetail(({"wand", "waende"}), "Die Waende sind aus reinem Eisen und wurden von einem Eisenfresser schon\nziemlich stark misshandelt.\n");
+  AddDetail("boden", "Der Boden ist aus Metall und funkelt im Licht. Solltest Du natuerlich Nacht-\nsicht haben, dann funkelt es nur in Deiner Einbildung.\n");
+  AddDetail("decke", "Die Decke hat eine gewaltige Last zu tragen.\n");
+  AddDetail("last", "Frag mich nicht wieviele Tonnen Gestein sich ueber Dir befinden.\n");
+  AddDetail("gestein", "Sowas gibt es im Gebirge, wenn es auch hier nicht so offensichtlich ist.\n");
+  AddDetail("gebirge", "Ach hoer doch auf ...\n");
+  AddDetail("eisenfresser", "Der hat sich wohl zu Tode gefressen.\n");
+  AddDetail(({"metall", "eisen"}), "Es glaenzt matt.\n");
+  AddDetail("ausgang", "Er liegt im Sueden.\n");
+  AddItem(MON("eisenfresser"), REFRESH_REMOVE);
+  AddExit("sueden",L1("m4x5"));
+}
diff --git a/doc/beispiele/ssp/l1/m4x5.c b/doc/beispiele/ssp/l1/m4x5.c
new file mode 100644
index 0000000..d64774b
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m4x5.c
@@ -0,0 +1,26 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Die metallischen Roehren\n"+
+  "verzweigen sich hier nach Norden, Osten und Westen. Im Norden scheint etwas\n"+
+  "Unangenehmes zu lauern, merkwuerdige Geraeusche dringen von dort hierher.\n");
+  AddDetail(({"wand", "waende", "roehre", "roehren", "gaenge", "gang"}), "Die Gaenge sind voellig mit Metall ausgekleidet.\n");
+  AddDetail("metall", "Das Metall glaenzt silbern.\n");
+  AddDetail("unangenehmes", "Wer weiss, was sich dort verbirgt ...\n");
+  AddDetail("boden", "Du bist froh, dass es hier einen Boden gibt, sonst wuerdest Du tief fallen.\n");
+  AddDetail("decke", "Die Decke ist metallisch und glaenzt.\n");
+  AddDetail(({"geraeusch", "geraeusche"}), "Es quietscht.\n");
+  AddExit("norden",L1("m4x4"));
+  AddExit("osten",L1("m5x5"));
+  AddExit("westen",L1("m3x5"));
+}
diff --git a/doc/beispiele/ssp/l1/m5x1.c b/doc/beispiele/ssp/l1/m5x1.c
new file mode 100644
index 0000000..5865a91
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m5x1.c
@@ -0,0 +1,26 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Steinebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Der grob behauene Gang\n"+
+  "macht hier eine Biegung von Sueden nach Westen, wo er in einem schwarzen\n"+
+  "Loch verschwindet, aus dem ein fuerchterlicher Gestank dringt.\n"); 
+  AddDetail("gang", "Der Gang fuehrt in die Dunkelheit.\n");
+  AddDetail("dunkelheit", "Schwarze, lichtlose Dunkelheit.\n");
+  AddDetail("loch", "Hier lauert irgend etwas Schreckliches.\n");
+  AddDetail("gestank", "Der Gestank ist wirklich grauenhaft.\n");
+  AddDetail("boden", "Hoppla, hier liegen Fleischfetzen rum, im Westen scheint wohl was zu wueten.\n");
+  AddDetail(({"fleischfetzen", "fetzen", "fleisch"}), "Ist ja eklig!\n");
+  AddDetail(({"wand", "waende"}), "Die Waende sind verschmiert.\n");
+  AddDetail("decke", "Gaehn.\n");
+  AddExit("westen",L1("m4x1"));
+  AddExit("sueden",L1("m5x2"));
+}
diff --git a/doc/beispiele/ssp/l1/m5x2.c b/doc/beispiele/ssp/l1/m5x2.c
new file mode 100644
index 0000000..75f6a5f
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m5x2.c
@@ -0,0 +1,26 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Steinebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Ein mit roher Gewalt in\n"+
+  "den Fels genagter Gang fuehrt von Sueden nach Norden. Du spuerst, dass\n"+
+  "hinter jeder Ecke Orks oder anderes uebles Gezuecht lauern koennen.\n");
+  AddDetail(({"gang", "gaenge"}), "Der Gang fuehrt von Sueden nach Norden.\n");
+  AddDetail("fels", "Es ist ein harter Fels, der von boesen Wesen durchbohrt wurde.\n");
+  AddDetail(({"ork", "orks", "gezuecht"}), "Momentan siehst Du keine Orks, aber das kann sich ja schnell aendern.\n");
+  AddDetail("boden", "Blanker Fels.\n");
+  AddDetail("fels", "Das wird allmaehlich langweilig.\n");
+  AddDetail("decke", "An der Decke haengt eine bunte Girlande.\n");
+  AddDetail("girlande", "Wer die wohl da aufgehaengt hat?\n");
+  AddDetail(({"wand", "waende"}), "Da gibt es nichts interessantes zu sehen.\n");
+  AddExit("norden",L1("m5x1"));
+  AddExit("sueden",L1("m5x3"));
+}
diff --git a/doc/beispiele/ssp/l1/m5x3.c b/doc/beispiele/ssp/l1/m5x3.c
new file mode 100644
index 0000000..b7d4ce7
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m5x3.c
@@ -0,0 +1,32 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <moving.h>
+#include <rooms.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Steinebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Dunkle Gaenge fuehren\n"+
+  "nach Norden, Sueden und Westen. Der suedliche Gang ist etwas merkwuerdig,\n"+
+  "denn sein Eingang ist mit glaenzendem Metall seltsam beschlagen.\n");
+  AddDetail(({"felsboden", "boden"}), "Der blanke Felsboden geht im Sueden in Metall ueber.\n");
+  AddDetail(({"gang", "gaenge"}), "Es sind Gaenge der Orks.\n");
+  AddDetail("eingang", "Im Sueden glaenzen die Waende, die mit Metall beschlagen sind.\n");
+  AddDetail(({"wand", "waende"}), "Die Waende sind da ... wie immer.\n");
+  AddDetail("decke", "Die Decke ist nicht interessant.\n");
+  AddDetail("fels", "Grauer Fels.\n");
+  AddDetail("metall", "Es scheint Eisen zu sein.\n");
+  AddDetail("eisen", "Ja, zweifellos, es ist Eisen.\n");
+  AddExit("westen",L1("m4x3"));
+  AddExit("norden",L1("m5x2"));
+  AddExit("sueden",L1("m5x4"));
+
+  AddItem(MON("orkchef"), REFRESH_REMOVE);
+}
+
diff --git a/doc/beispiele/ssp/l1/m5x4.c b/doc/beispiele/ssp/l1/m5x4.c
new file mode 100644
index 0000000..b95d15e
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m5x4.c
@@ -0,0 +1,25 @@
+inherit "std/room";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Der Gang, der hier von\n"+
+  "Norden nach Sueden fuehrt, aehnelt mehr einer Roehre, denn alles ist voll-\n"+
+  "staendig aus einem glaenzenden Metall. Du fragst Dich, wer wohl diese\n"+
+  "Gaenge angelegt hat.\n");
+  AddDetail(({"gang", "gaenge", "roehre", "rohr"}), "Die Gaenge sind voellig mit Metall ausgekleidet.\n");
+  AddDetail("metall", "Das Metall glaenzt silbern.\n");
+  AddDetail(({"wand", "waende"}), "Die Waende glaenzen silbern.\n");
+  AddDetail("boden", "Sei froh, dass er da ist.\n");
+  AddDetail("decke", "Die Decke ist wirklich ziemlich langweilig.\n");
+  AddExit("norden",L1("m5x3"));
+  AddExit("sueden",L1("m5x5"));
+}
diff --git a/doc/beispiele/ssp/l1/m5x5.c b/doc/beispiele/ssp/l1/m5x5.c
new file mode 100644
index 0000000..4f99ba1
--- /dev/null
+++ b/doc/beispiele/ssp/l1/m5x5.c
@@ -0,0 +1,27 @@
+inherit "std/room";
+
+#include <properties.h>
+#include <rooms.h>
+#include "../files.h"
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Metallebene");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Eigentlich koennte man\n"+
+  "es auch fast Roehrenlabyrinth nennen, denn Du befindest Dich in einer\n"+
+  "solchen, die hier eine Biegung von Norden nach Westen macht. Die Waende\n"+
+  "sind aus einem glaenzenden Metall gefertigt, was Dir ziemlich merkwuerdig\n"+
+  "vorkommt.\n");
+  AddDetail(({"wand", "waende", "roehre", "roehren", "gaenge", "gang"}), "Die Gaenge sind voellig mit Metall ausgekleidet.\n");
+  AddDetail("metall", "Das Metall glaenzt silbern.\n");
+  AddDetail("boden", "Der Boden ist aus Metall.\n");
+  AddDetail("decke", "Selbst die Decke glaenzt metallisch, Du fragst Dich, wer das angelegt hat.\n");
+  AddDetail("biegung", "Die Biegung fuehrt von Norden nach Westen.\n");
+  AddItem(MON("kampfblech"), REFRESH_REMOVE);
+  AddExit("norden",L1("m5x4"));
+  AddExit("westen",L1("m4x5"));
+}
diff --git a/doc/beispiele/ssp/l1/oben.c b/doc/beispiele/ssp/l1/oben.c
new file mode 100644
index 0000000..f3bc039
--- /dev/null
+++ b/doc/beispiele/ssp/l1/oben.c
@@ -0,0 +1,27 @@
+inherit "std/room";
+ 
+#include <properties.h>
+#include <rooms.h>
+#include "../files.h"
+ 
+create()
+{
+  replace_program("std/room");
+  ::create();
+  SetProp(P_LIGHT, 0);
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_SHORT, "Beim Knochenschaeler");
+  SetProp(P_INT_LONG,
+  "Du bist im Hoehlenlabyrinth der Schreckensspitze. Dieser kleine Raum ist\n"+
+  "die Heimat eines fuerchterlichen Ungeheuers, die Heimat des schrecklichen\n"+
+  "Knochenschaelers. Im Boden des Raums ist ein kleines Loch, durch das eine\n"+
+  "Leiter nach unten fuehrt.\n");
+  AddDetail("raum", "Der Raum ist ziemlich klein.\n");
+  AddDetail(({"wand", "decke", "waende", "metall"}), "Alles ist aus Metall.\n");
+  AddDetail(({"boden", "loch"}), "Im Boden ist ein Loch, durch das eine Leiter nach unten fuehrt.\n");
+  AddDetail("leiter", "Die Leiter fuehrt nach unten.\n");
+  AddDetail("ungeheuer", "Der Knochenschaeler ist einfach fuerchterlich.\n");
+  AddDetail("knochenschaeler", "Zum Glueck ist er gerade nicht daheim.\n");
+  AddItem(MON("knochenschaeler"), REFRESH_REMOVE);
+  AddExit("unten", L1("m2x2"));
+}
diff --git a/doc/beispiele/ssp/mon/dosenoeffner.c b/doc/beispiele/ssp/mon/dosenoeffner.c
new file mode 100644
index 0000000..46d627c
--- /dev/null
+++ b/doc/beispiele/ssp/mon/dosenoeffner.c
@@ -0,0 +1,24 @@
+#include <properties.h>
+#include "../files.h"
+
+inherit MMONST;
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   AddId(({"dosenoeffner", "oeffner"}));
+   AddAdjective(({"mobiler", "mobilen"}));
+   SetProp(P_SHORT, "Ein mobiler Dosenoeffner");
+   SetProp(P_LONG, "Der Dosenoeffner moechte Dir sicherlich den Kopf mit seinen scharfen\nSchneideraedern oeffnen.\n");
+   SetProp(P_NAME, "Dosenoeffner");
+   SetProp(P_GENDER, 1);
+   SetProp(P_LEVEL, 12);
+   SetProp(P_MAX_HP, 120);
+   SetProp(P_ALIGN, -123);
+   SetProp(P_BODY, 80);
+   SetProp(P_HANDS, ({" mit scharfen Schneideraedern", 50}));
+   SetProp(P_XP, 30000);
+   SetProp(P_SIZE, random(20)+40);
+   set_living_name("dosenoeffner");
+}
diff --git a/doc/beispiele/ssp/mon/eisenfresser.c b/doc/beispiele/ssp/mon/eisenfresser.c
new file mode 100644
index 0000000..ba7b041
--- /dev/null
+++ b/doc/beispiele/ssp/mon/eisenfresser.c
@@ -0,0 +1,28 @@
+#include <properties.h>
+#include "../files.h"
+
+inherit MMONST;
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   AddId(({"eisenfresser", "fresser"}));
+   SetProp(P_SHORT, "Ein Eisenfresser");
+   SetProp(P_LONG,
+   "Eisenfresser ernaehren sich ausschliesslich von Eisen, allerdings sind\n"+
+   "sie so kurzsichtig, dass sie erstmal in alles reinbeissen, was sie\n"+
+   "irgendwie wahrnehmen.\n");
+   SetProp(P_NAME, "Eisenfresser");
+   SetProp(P_GENDER, 1);
+   SetProp(P_LEVEL, 14);
+   SetProp(P_MAX_HP, 150);
+   SetProp(P_ALIGN, -215);
+   SetProp(P_BODY, 75);
+   SetProp(P_HANDS, ({" mit eisenbeschlagenen Zaehnen", 55}));
+   SetProp(P_XP, 42000);
+   SetProp(P_AGGRESSIVE, 1);
+   SetProp(P_SIZE, random(200)+50);   /* Naja, kurzsichtig halt... ;-) */
+   set_living_name("eisenfresser");
+   AddItem( OBJ("eisenklumpen") );
+}
diff --git a/doc/beispiele/ssp/mon/kampfblech.c b/doc/beispiele/ssp/mon/kampfblech.c
new file mode 100644
index 0000000..e218d01
--- /dev/null
+++ b/doc/beispiele/ssp/mon/kampfblech.c
@@ -0,0 +1,36 @@
+#include <properties.h>
+#include <combat.h>
+#include "../files.h"
+
+inherit "std/npc";
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Ein Kampfblech");
+   SetProp(P_LONG, 
+   "Das Kampfblech sieht irgendwie aus wie ein normales Backblech, nur\n"+
+   "viel gefaehrlicher!\n");
+   SetProp(P_NAME, "Kampfblech");
+   SetProp(P_GENDER, 0);
+   AddId(({"blech", "kampfblech","\nkampfblech"}));
+   SetProp(P_LEVEL, 8);
+   SetProp(P_MAX_HP, 100);
+   SetProp(P_ALIGN, -33);
+   SetProp(P_BODY, 70);
+   SetProp(P_HANDS, ({" mit eisenharten Schlaegen", 60}));
+   SetProp(P_XP, 30000);
+   SetProp(P_SIZE, 40);
+   SetProp(P_RACE, "Metallmonster");
+   SetProp(P_VULNERABILITY, ({ DT_WATER }));
+   SetProp(P_NOCORPSE, 1);
+   SetProp(P_DIE_MSG, " faellt laut scheppernd um.\n");
+   set_living_name("kampfblech");
+}
+
+die()
+{
+   clone_object(OBJ("kampfblech"))->move(environment());
+   ::die();
+}
diff --git a/doc/beispiele/ssp/mon/knochenschaeler.c b/doc/beispiele/ssp/mon/knochenschaeler.c
new file mode 100644
index 0000000..84c1e3b
--- /dev/null
+++ b/doc/beispiele/ssp/mon/knochenschaeler.c
@@ -0,0 +1,43 @@
+#include <properties.h>
+#include <combat.h>
+#include "../files.h"
+
+inherit MMONST;
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Der Knochenschaeler");
+   SetProp(P_LONG,
+   "Der Knochenschaeler geht um, geht um\n"+
+   "Der Knochenschaeler geht um, geht um\n"+
+   "Sein Messer so lang, wie tausend grosse Zaehne\n"+
+   "Wie tausend grosse Zaehne, sein Messer so lang\n"+
+   "Er schneidet Dich in Stuecke, portionsgerecht und fein\n"+
+   "Er schneidet Dich in Stuecke, und keiner tuts ihm gleich\n"+
+   "Er schaelt die Knochen, er quaelt Deinen Geist\n"+
+   "Er schaelt die Knochen, er quaelt Deinen Geist\n"+
+   "Habet acht, habet acht! Seiet vorsichtig, habet Acht!\n"+
+   "Schau Dich um, schau Dich um\n"+
+   "Schau Dich um, Du wirst der naechste sein\n");
+   SetProp(P_NAME, "Knochenschaeler");
+   SetProp(P_GENDER, 1);
+   AddId(({"schaeler", "knochenschaeler"}));
+   SetProp(P_LEVEL, 18);
+   SetProp(P_MAX_HP, 300);
+   SetProp(P_ALIGN, -789);
+   SetProp(P_HANDS, ({" mit seinem langen Messer", 145}) );
+   SetProp(P_BODY, 110);
+   SetProp(P_XP, 220000);
+   SetProp(P_AGGRESSIVE, 1);
+   SetProp(P_SIZE, 220);
+   set_living_name("knochenschaeler");
+   clone_object(OBJ("schaelmesser"))->move(this_object());
+   command("zuecke messer");
+}
+
+QueryArticle(c, d, f)
+{
+   return ::QueryArticle(c, 1, f);
+}
diff --git a/doc/beispiele/ssp/mon/metallmonster.c b/doc/beispiele/ssp/mon/metallmonster.c
new file mode 100644
index 0000000..5482186
--- /dev/null
+++ b/doc/beispiele/ssp/mon/metallmonster.c
@@ -0,0 +1,28 @@
+inherit "std/npc";
+
+#include <properties.h>
+#include <combat.h>
+#include "../files.h"
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_NOCORPSE, 1);
+   SetProp(P_RACE, "Metallmonster");
+   SetProp(P_DIE_MSG, " zerfaellt in seine Bestandteile.\n");
+   SetProp(P_VULNERABILITY, ({ DT_WATER }));
+}
+
+die()
+{
+   create_blech();
+   ::die();
+}
+
+create_blech()
+{
+   object b;
+   b = clone_object(OBJ("blech"));
+   b->move(environment(this_object()));
+}
diff --git a/doc/beispiele/ssp/mon/ork.c b/doc/beispiele/ssp/mon/ork.c
new file mode 100644
index 0000000..975e596
--- /dev/null
+++ b/doc/beispiele/ssp/mon/ork.c
@@ -0,0 +1,26 @@
+#include <properties.h>
+#include "../files.h"
+
+inherit "std/npc";
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Ein grimmiger Ork");
+   SetProp(P_LONG, "Der Ork schaut Dich unfreundlich an und wedelt mit seinem Krummschwert.\n");
+   SetProp(P_NAME, "Ork");
+   SetProp(P_GENDER, 1);
+   AddId("ork");
+   SetProp(P_RACE, "Ork");
+   SetProp(P_LEVEL, 10);
+   SetProp(P_MAX_HP, 130);
+   SetProp(P_ALIGN, -250);
+   SetProp(P_XP, 90000);
+   SetProp(P_BODY, 50);
+   SetProp(P_SIZE, random(20)+110);
+   set_living_name("ork");
+   seteuid(getuid(this_object()));
+   AddItem(OBJ("krummschwert"),CLONE_WIELD);
+   AddItem(OBJ("orkhose"),CLONE_WEAR);
+}
diff --git a/doc/beispiele/ssp/mon/orkchef.c b/doc/beispiele/ssp/mon/orkchef.c
new file mode 100644
index 0000000..2d6e6cd
--- /dev/null
+++ b/doc/beispiele/ssp/mon/orkchef.c
@@ -0,0 +1,29 @@
+inherit "std/npc";
+
+#include <properties.h>
+#include "../files.h"
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Ein Anfuehrer der Orks");
+   SetProp(P_LONG, "Dies ist ein fetter, stinkender Ork-Haeuptling, der Dir alles \nandere als freundlich gesonnen ist.\n");
+   SetProp(P_NAME, "Ork");
+   SetProp(P_GENDER, 1);
+   AddId(({"ork", "anfuehrer", "haeuptling"}));
+   SetProp(P_RACE, "Ork");
+   SetProp(P_LEVEL, 16);
+   SetProp(P_MAX_HP, 220);
+   SetProp(P_ALIGN, -280);
+   SetProp(P_XP, 160000);
+   SetProp(P_BODY, 60);
+   SetProp(P_SIZE, random(20)+130);
+   set_living_name("orkchef");
+   seteuid(getuid(this_object()));
+   AddItem(OBJ("saebel"),CLONE_WIELD);
+   AddItem(OBJ("orkhose"),CLONE_WEAR);
+   AddItem(OBJ("orkhelm"),CLONE_WEAR);
+}
+
+      
diff --git a/doc/beispiele/ssp/mon/stahldrache.c b/doc/beispiele/ssp/mon/stahldrache.c
new file mode 100644
index 0000000..b4efcac
--- /dev/null
+++ b/doc/beispiele/ssp/mon/stahldrache.c
@@ -0,0 +1,39 @@
+#include <properties.h>
+#include <combat.h>
+#include "../files.h"
+
+inherit MMONST;
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Ein Stahldrache");
+   SetProp(P_LONG,
+   "Der Stahldrache ist ein ernstzunehmender Gegner, gepanzert mit Platten\n"+
+   "aus reinem Stahl ist es schwer, ihm irgendwie Schaden zuzufuegen.\n");
+   SetProp(P_NAME, ({"Stahldrache", "Stahldrachen", "Stahldrachen", "Stahldrachen"}));
+   SetProp(P_GENDER, 1);
+   AddId(({"drache", "stahldrache", "drachen", "stahldrachen"}));
+   SetProp(P_RACE, "Drache");
+   SetProp(P_LEVEL, 20);
+   SetProp(P_MAX_HP, 400);
+   SetProp(P_ALIGN, -250);
+   SetProp(P_HANDS, ({" mit staehlernen Klauen", 150}));
+   SetProp(P_BODY, 130);
+   SetProp(P_XP, 300000);
+   SetProp(P_SIZE, random(50)+250);
+   SetChats(40, ({
+      "Der Stahldrache schnaubt laut.\n",
+      "Der Stahldrache schaut Dich mit staehlernem Blick an.\n",
+      "Der Stahldrache waelzt sich herum.\n",
+      "Der Stahldrache spuckt etwas stahlblaues Feuer.\n",
+   }) );
+   AddSpell(1, 115, "Der Stahldrache spuckt stahlblaues Feuer auf Dich.\n",
+  	            "Der Stahldrache spuckt stahlblaues Feuer auf @WEN.\n",
+	    DT_FIRE);
+   SetProp(P_SPELLRATE,20);
+   clone_object(OBJ("gummistiefel"))->move(this_object());
+   clone_object(OBJ("stahlschwert"))->move(this_object());
+   set_living_name("stahldrache");
+}
diff --git a/doc/beispiele/ssp/mon/titanwalze.c b/doc/beispiele/ssp/mon/titanwalze.c
new file mode 100644
index 0000000..23113e6
--- /dev/null
+++ b/doc/beispiele/ssp/mon/titanwalze.c
@@ -0,0 +1,30 @@
+#include <properties.h>                    
+#include <combat.h>
+#include "../files.h"
+
+inherit MMONST;
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Eine Titanwalze");
+   SetProp(P_LONG, "Pass nur auf, dass diese Titanwalze Dir nicht ueber die Zehen rollt.\nBei diesem Gewicht koennte das auesserst unangenehme Folgen haben.\n");
+   SetProp(P_NAME, "Titanwalze");
+   SetProp(P_GENDER, 2);
+   AddId(({"walze", "titanwalze"}));
+   SetProp(P_LEVEL, 14);
+   SetProp(P_MAX_HP, 170);
+   SetProp(P_ALIGN, 120);
+   SetProp(P_XP, 51000);
+   SetProp(P_BODY, 90);
+   SetProp(P_HANDS, ({" mit wuchtigen Schlaegen", 60}));
+   SetProp(P_SIZE, 210);
+   SetProp(P_DIE_MSG, " zerfaellt in ihre Bestandteile.\n");
+   SetAttackChats(30, ({
+   "Die Titanwalze versucht Dich platt zu walzen.\n",
+   "Die Titanwalze macht: Rumpel, polter ...\n",
+   "Die Titanwalze roehrt.\n"}));
+   set_living_name("titanwalze");
+   AddItem(OBJ("titanring")); 
+}
diff --git a/doc/beispiele/ssp/mon/troll.c b/doc/beispiele/ssp/mon/troll.c
new file mode 100644
index 0000000..8eb1043
--- /dev/null
+++ b/doc/beispiele/ssp/mon/troll.c
@@ -0,0 +1,28 @@
+#include <properties.h>
+#include "../files.h"
+
+inherit "std/npc";
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Ein Troll");
+   SetProp(P_LONG, "Ein Troll wie aus dem Bilderbuch: Gross, haesslich und dumm.\n");
+   SetProp(P_NAME, "Troll");
+   SetProp(P_GENDER, 1);
+   SetProp(P_RACE, "Troll");
+   AddId("troll");
+   SetProp(P_LEVEL, 15);
+   SetProp(P_MAX_HP, 200);
+   SetProp(P_ALIGN, -417);
+   SetProp(P_XP, 120000);
+   SetProp(P_BODY, 35);
+   SetProp(P_SIZE, random(40)+199);
+   set_living_name("troll");
+   seteuid(getuid(this_object()));
+   AddItem(OBJ("trolldolch"),CLONE_WIELD);
+   AddItem(OBJ("steinring"),CLONE_WEAR);
+   AddItem(OBJ("lkhemd"),CLONE_WEAR);
+}
+      
diff --git a/doc/beispiele/ssp/mon/trollmops.c b/doc/beispiele/ssp/mon/trollmops.c
new file mode 100644
index 0000000..5830796
--- /dev/null
+++ b/doc/beispiele/ssp/mon/trollmops.c
@@ -0,0 +1,31 @@
+#include <properties.h>
+#include "../files.h"
+
+inherit "std/npc";
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Ein Trollmops");
+   SetProp(P_LONG, 
+   "Dies ist einer der sagenumwobenen, blutruenstigen Trollmoepse. Hervor-\n"+
+   "gegangen aus der misslungenen Kreuzung zwischen einem Troll und einem\n"+
+   "Mops, terrorisieren diese grausamen Bestien jetzt die Welt. Flieh, so-\n"+
+   "lange Du noch kannst.\n");
+   SetProp(P_NAME, "Trollmops");
+   SetProp(P_GENDER, 1);
+   AddId("trollmops");
+   AddId("troll");
+   AddId("mops");
+   SetProp(P_RACE, "Troll");
+   SetProp(P_LEVEL, 20);
+   SetProp(P_MAX_HP, 350);
+   SetProp(P_ALIGN, -735 - random(100));
+   SetProp(P_HANDS, ({" mit blutverschmierten Krallen", 200 +random(10)}));
+   SetProp(P_XP, 350000);
+   SetProp(P_BODY, 140 + random(40));
+   SetProp(P_SIZE, random(20)+105);  /* Da sind die Groessen der Eltern drin */
+   set_living_name("trollmops");
+   SetProp(P_AGGRESSIVE, 1);
+}
diff --git a/doc/beispiele/ssp/obj/blech.c b/doc/beispiele/ssp/obj/blech.c
new file mode 100644
index 0000000..5c5c2ec
--- /dev/null
+++ b/doc/beispiele/ssp/obj/blech.c
@@ -0,0 +1,16 @@
+#include <properties.h>
+
+inherit "/std/thing";
+
+create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein altes Blech");
+  SetProp(P_LONG, "Das sind die Ueberreste eines Metallmonsters.\n");
+  SetProp(P_NAME, "Blech");
+  SetProp(P_GENDER, 0);
+  AddId("blech");
+  AddAdjective(({"alt", "altes"}));
+  SetProp(P_WEIGHT, 1100);
+  SetProp(P_VALUE, random(QueryProp(P_MAX_HP))+100);
+}
diff --git a/doc/beispiele/ssp/obj/eisenklumpen.c b/doc/beispiele/ssp/obj/eisenklumpen.c
new file mode 100644
index 0000000..bac122b
--- /dev/null
+++ b/doc/beispiele/ssp/obj/eisenklumpen.c
@@ -0,0 +1,16 @@
+#include <properties.h>
+
+inherit "/std/thing";
+
+create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein Klumpen Eisen");
+  SetProp(P_LONG, "Sieht aus, als haette da schon jemand dran rumgenagt. Derjenige muss\naber sehr stabile Zaehne gehabt haben.\n");
+  SetProp(P_NAME, "Klumpen");
+  SetProp(P_GENDER, 1);
+  AddId(({"klumpen", "eisen", "klumpen eisen"}));
+  SetProp(P_MATERIAL, MAT_IRON);
+  SetProp(P_WEIGHT, 2438);
+  SetProp(P_VALUE, random(750));
+}
diff --git a/doc/beispiele/ssp/obj/gummistiefel.c b/doc/beispiele/ssp/obj/gummistiefel.c
new file mode 100644
index 0000000..ea4728e
--- /dev/null
+++ b/doc/beispiele/ssp/obj/gummistiefel.c
@@ -0,0 +1,37 @@
+#include <properties.h>
+#include <language.h>
+#include <combat.h>
+#include "../files.h"
+ 
+inherit "std/armour";
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Ein Paar Gummistiefel");
+   SetProp(P_LONG, "Damit bekommt man sicher keine nassen Fuesse.\n");
+   SetProp(P_NAME, "Paar Gummistiefel");
+   SetProp(P_GENDER, 0);
+   AddId(({"stiefel", "gummistiefel", "paar", "paar stiefel"}));
+   SetProp(P_ARMOUR_TYPE, AT_BOOT);
+   SetProp(P_AC, 0);
+   SetProp(P_WEIGHT, 700);
+   SetProp(P_VALUE, 120);
+   SetProp(P_REMOVE_FUNC, this_object());
+}
+
+RemoveFunc()
+{
+   object env;
+   if ((env=environment(QueryProp(P_WORN)))==find_object(L2("m2x1")) ||
+       env==find_object(L2("m1x1")))
+   {
+      write("Das waere keine so tolle Idee hier die Gummistiefel auszuziehen.\n");
+      return 0;
+   }
+   return 1;
+}
+       
+
+
diff --git a/doc/beispiele/ssp/obj/kampfblech.c b/doc/beispiele/ssp/obj/kampfblech.c
new file mode 100644
index 0000000..63c953a
--- /dev/null
+++ b/doc/beispiele/ssp/obj/kampfblech.c
@@ -0,0 +1,22 @@
+#include <properties.h>
+#include <combat.h>
+inherit "std/armour";
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein totes Kampfblech");
+  SetProp(P_LONG, "Sieht aus wie ein normales Kampfblech, nur tot. Ausserdem ueberrascht Dich der merkwuerdige Griff.\n");
+  SetProp(P_NAME, "Kampfblech");
+  SetProp(P_NAME_ADJ, "tot");
+  SetProp(P_GENDER, 0);
+  SetProp(P_ARMOUR_TYPE, AT_SHIELD);
+  SetProp(P_AC, 12);
+  SetProp(P_WEIGHT, 850);
+  SetProp(P_VALUE, 420);
+  SetProp(P_MATERIAL, MAT_MISC_METAL);
+  SetProp(P_INFO, "Ziemlicher Schwachsinn dieses Objekt, oder nicht?\n");
+  AddId(({"schild", "blech", "kampfblech", "totes kampfblech"}));
+  AddDetail("griff", "Mit Hilfe des Griffs kannst Du das tote Kampfblech als Schild benutzen.\n");
+}
diff --git a/doc/beispiele/ssp/obj/krummschwert.c b/doc/beispiele/ssp/obj/krummschwert.c
new file mode 100644
index 0000000..659ff02
--- /dev/null
+++ b/doc/beispiele/ssp/obj/krummschwert.c
@@ -0,0 +1,22 @@
+#include <properties.h>
+#include <combat.h>
+inherit "std/weapon";
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein Krummschwert");
+  SetProp(P_LONG, "Das Krummschwert sieht aus, als haette es einmal einem Ork gehoert.\n");
+  SetProp(P_NAME, "Krummschwert");
+  SetProp(P_GENDER, 0);
+  SetProp(P_WC, 130 + random(15));
+  SetProp(P_VALUE, 1200 + random(200));
+  SetProp(P_WEIGHT, 1100 + random(100));
+  SetProp(P_WEAPON_TYPE, WT_SWORD);
+  SetProp(P_DAM_TYPE, DT_SLASH);
+  SetProp(P_NR_HANDS, 1);
+  SetProp(P_MATERIAL, MAT_MISC_METAL);
+  AddId("schwert");
+  AddId("krummschwert");
+}
diff --git a/doc/beispiele/ssp/obj/lkhemd.c b/doc/beispiele/ssp/obj/lkhemd.c
new file mode 100644
index 0000000..5c381bc
--- /dev/null
+++ b/doc/beispiele/ssp/obj/lkhemd.c
@@ -0,0 +1,19 @@
+#include <properties.h>
+#include <combat.h>
+inherit "std/armour";
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein leichtes Kettenhemd");
+  SetProp(P_LONG, "Ein nicht sonderlich gut gearbeitetes Kettenhemd. Anziehen kann man\nes aber trotzdem.\n");
+  SetProp(P_NAME, "Kettenhemd");
+  SetProp(P_GENDER, 0);
+  SetProp(P_ARMOUR_TYPE, AT_ARMOUR);
+  SetProp(P_AC, 18);
+  SetProp(P_WEIGHT, 1700);
+  SetProp(P_VALUE, 1200 +random(100));
+  SetProp(P_MATERIAL, MAT_MISC_METAL);
+  AddId(({"kettenhemd", "hemd"}));
+}
diff --git a/doc/beispiele/ssp/obj/orkhelm.c b/doc/beispiele/ssp/obj/orkhelm.c
new file mode 100644
index 0000000..36d6a8b
--- /dev/null
+++ b/doc/beispiele/ssp/obj/orkhelm.c
@@ -0,0 +1,20 @@
+inherit "std/armour";
+ 
+#include <properties.h>                    
+#include <combat.h>
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein Orkhelm");
+  SetProp(P_LONG, "Ein stabiler Helm, der schon ausgiebig von einem stinkenden Ork\ngetragen wurde.\n");
+  SetProp(P_NAME, "Orkhelm");
+  SetProp(P_GENDER, 1);
+  SetProp(P_ARMOUR_TYPE, AT_HELMET);
+  SetProp(P_AC, 7);
+  SetProp(P_WEIGHT, 450);
+  SetProp(P_VALUE, 350);
+  SetProp(P_MATERIAL, ([MAT_MISC_METAL:95, MAT_SLIME:5]));
+  AddId(({"helm", "orkhelm"}));
+}
diff --git a/doc/beispiele/ssp/obj/orkhose.c b/doc/beispiele/ssp/obj/orkhose.c
new file mode 100644
index 0000000..8ba0056
--- /dev/null
+++ b/doc/beispiele/ssp/obj/orkhose.c
@@ -0,0 +1,19 @@
+#include <properties.h>
+#include <combat.h>
+inherit "std/armour";
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Eine Orkhose");
+  SetProp(P_LONG, "Die Beinbekleidung von Orks.\n");
+  SetProp(P_NAME, "Hose");
+  SetProp(P_GENDER, 2);
+  SetProp(P_ARMOUR_TYPE, AT_TROUSERS);
+  SetProp(P_AC, 7);
+  SetProp(P_WEIGHT, 555);
+  SetProp(P_VALUE, 480 + random(40));
+  SetProp(P_MATERIAL, ([MAT_CLOTH:99, MAT_SHIT:1]));
+  AddId(({"hose", "hosen", "orkhose", "orkhosen"}));
+}
diff --git a/doc/beispiele/ssp/obj/saebel.c b/doc/beispiele/ssp/obj/saebel.c
new file mode 100644
index 0000000..3aec958
--- /dev/null
+++ b/doc/beispiele/ssp/obj/saebel.c
@@ -0,0 +1,23 @@
+inherit "std/weapon";
+
+#include <properties.h>                    
+#include <combat.h>
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein schartiger Saebel");
+  SetProp(P_LONG, "Der Saebel ist schartig, koennte aber trotzdem recht brauchbar sein.\n");
+  SetProp(P_NAME, "Saebel"); 
+  SetProp(P_GENDER, 1);
+  SetProp(P_WC, 144);
+  SetProp(P_VALUE, 1525);
+  SetProp(P_WEIGHT, 1264);
+  SetProp(P_WEAPON_TYPE, WT_SWORD);
+  SetProp(P_DAM_TYPE, DT_SLASH);
+  SetProp(P_NR_HANDS, 1);
+  SetProp(P_MATERIAL, MAT_MISC_METAL);
+  AddId("saebel");
+  AddAdjective(({"schartig", "schartiger", "schartigen"}));
+}
diff --git a/doc/beispiele/ssp/obj/schaelmesser.c b/doc/beispiele/ssp/obj/schaelmesser.c
new file mode 100644
index 0000000..2b0b686
--- /dev/null
+++ b/doc/beispiele/ssp/obj/schaelmesser.c
@@ -0,0 +1,25 @@
+inherit "std/weapon";
+
+#include <combat.h>
+#include <properties.h>
+
+create()
+{
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Ein Knochenschaelmesser");
+   SetProp(P_LONG, 
+	   "Ein Messer so lang, wie tausend grosse Zaehne\n"+
+	   "Wie tausend grosse Zaehne, ein Messer so lang\n"+
+	   "So lang, wie tausend grosse, grosse Zaehne\n");
+   SetProp(P_NAME, "Knochenschaelmesser");
+   SetProp(P_GENDER, 0);
+   AddId(({"knochenschaelmesser", "schaelmesser", "messer"}));
+   SetProp(P_WC, 145);
+   SetProp(P_NR_HANDS, 1),
+   SetProp(P_WEAPON_TYPE, WT_KNIFE);
+   SetProp(P_DAM_TYPE, DT_SLASH);
+   SetProp(P_VALUE, 1610);
+   SetProp(P_WEIGHT, 1150);
+   SetProp(P_MATERIAL, MAT_MISC_METAL);
+}
diff --git a/doc/beispiele/ssp/obj/stahlschwert.c b/doc/beispiele/ssp/obj/stahlschwert.c
new file mode 100644
index 0000000..4446898
--- /dev/null
+++ b/doc/beispiele/ssp/obj/stahlschwert.c
@@ -0,0 +1,21 @@
+#include <properties.h>
+#include <combat.h>
+inherit "std/weapon";
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein Stahlschwert");
+  SetProp(P_LONG, "Dieses Schwert hat eine Klinge aus reinem Stahl, mit der man seine Gegner\nwohl ziemlich gut niedermachen kann. Das Schwert ist recht schwer und\nlaesst sich nur mit zwei Haenden fuehren.\n");
+  SetProp(P_NAME, "Stahlschwert");
+  SetProp(P_GENDER, 0);
+  SetProp(P_WC, 175);
+  SetProp(P_VALUE, 1750 + random(100));
+  SetProp(P_WEIGHT, 2926);
+  SetProp(P_WEAPON_TYPE, WT_SWORD);
+  SetProp(P_DAM_TYPE, DT_SLASH);
+  SetProp(P_NR_HANDS, 2);
+  SetProp(P_MATERIAL, MAT_STEEL);
+  AddId(({"schwert", "stahlschwert", "\nsspstahlschwert"}));
+}
diff --git a/doc/beispiele/ssp/obj/steinring.c b/doc/beispiele/ssp/obj/steinring.c
new file mode 100644
index 0000000..5da09a7
--- /dev/null
+++ b/doc/beispiele/ssp/obj/steinring.c
@@ -0,0 +1,19 @@
+#include <properties.h>
+#include <combat.h>
+inherit "std/armour";
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein Steinring");
+  SetProp(P_LONG, "Ein kleiner Ring, voellig aus einem grauen Stein.\n");
+  SetProp(P_NAME, "Steinring");
+  SetProp(P_GENDER, 1);
+  SetProp(P_ARMOUR_TYPE, AT_RING);
+  SetProp(P_AC, 2);
+  SetProp(P_WEIGHT, 270);
+  SetProp(P_VALUE, 300 +random(150));
+  SetProp(P_MATERIAL, MAT_MISC_STONE);
+  AddId(({"ring", "steinring"}));
+}
diff --git a/doc/beispiele/ssp/obj/titanring.c b/doc/beispiele/ssp/obj/titanring.c
new file mode 100644
index 0000000..389de66
--- /dev/null
+++ b/doc/beispiele/ssp/obj/titanring.c
@@ -0,0 +1,18 @@
+inherit "std/armour";
+
+#include <properties.h>
+#include <combat.h>
+
+create()
+{
+  ::create();
+  SetProp(P_SHORT, "Ein Titanring");
+  SetProp(P_LONG, "Ein Ring aus Titan.\n");
+  SetProp(P_NAME, "Titanring");
+  SetProp(P_GENDER, 1);
+  AddId(({"titanring", "ring"}));
+  SetProp(P_ARMOUR_TYPE, AT_RING);
+  SetProp(P_AC, 2);
+  SetProp(P_WEIGHT, 280);
+  SetProp(P_VALUE, 520);
+}
diff --git a/doc/beispiele/ssp/obj/trolldolch.c b/doc/beispiele/ssp/obj/trolldolch.c
new file mode 100644
index 0000000..fd06092
--- /dev/null
+++ b/doc/beispiele/ssp/obj/trolldolch.c
@@ -0,0 +1,21 @@
+#include <properties.h>
+#include <combat.h>
+inherit "std/weapon";
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein Trolldolch");
+  SetProp(P_LONG, "Ein langer, scharfer Dolch, dem Gestank nach muss er einem Troll\ngehoert haben.\n");
+  SetProp(P_NAME, "Trolldolch");
+  SetProp(P_GENDER, 1);
+  SetProp(P_WC, 119);
+  SetProp(P_VALUE, 1000 +random(400));
+  SetProp(P_WEIGHT, 500 +random(50));
+  SetProp(P_WEAPON_TYPE, WT_KNIFE);
+  SetProp(P_DAM_TYPE, DT_PIERCE);
+  SetProp(P_NR_HANDS, 1);
+  SetProp(P_MATERIAL, MAT_MISC_METAL);
+  AddId(({"dolch", "trolldolch"}));
+}
diff --git a/doc/beispiele/testobjekte/.readme b/doc/beispiele/testobjekte/.readme
new file mode 100644
index 0000000..56ff41f
--- /dev/null
+++ b/doc/beispiele/testobjekte/.readme
@@ -0,0 +1,13 @@
+
+Hier liegen Test-Objekte zu man-pages. Unterschieden wird momentan nur nach:
+- xxx_testobj -> das kann man laden und ggf clonen
+- xxx_testraum -> den kann man betreten
+
+Der Praefix deutet an, fuer welche Features (also man-pages) der Testcode geschrieben wurde. Die man-page selbst nennt diesen Dateinamen hier, insofern:
+* bitte mit Bedacht veraendern - pruefen, welche Funktionalitaet
+  beispielhaft gezeigt werden soll und das nicht loeschen
+* bitte Dateinamen nur veraendern, wenn ihr euch sicher seid, _alle_
+  darauf verweisenden man-pages ebenfalls geaendert zu haben
+
+Verantwortlich: Gloinson
+
diff --git a/doc/beispiele/testobjekte/attack_busy_sensitive_testobj.c b/doc/beispiele/testobjekte/attack_busy_sensitive_testobj.c
new file mode 100644
index 0000000..4225d88
--- /dev/null
+++ b/doc/beispiele/testobjekte/attack_busy_sensitive_testobj.c
@@ -0,0 +1,105 @@
+inherit "/std/thing";

+#include <properties.h>

+#include <new_skills.h>

+#include <sensitive.h>

+

+private action_puste(string str);

+private int counter;

+

+void create() {

+  if (!clonep(this_object())) return;

+  ::create();

+  counter = 2+random(4);

+  

+  SetProp(P_NAME, "Pusteblume");

+  SetProp(P_SHORT, "Eine kleine Pustblume");

+  SetProp(P_LONG, break_string(

+    "Eine abgebluehte Pflanze, die jetzt wie ein kleiner, weisser Ball "

+	"aussieht. Die fiedrigen Samen fliegen bestimmt prima.", 78));

+  AddDetail("samen", "Er sieht sehr fein und leicht aus.\n");

+  SetProp(P_GENDER,FEMALE);

+  SetProp(P_MATERIAL, MAT_MISC_PLANT);

+  SetProp(P_NOBUY, 1);

+  SetProp(P_VALUE, random(10));

+

+  SetProp(P_INFO, "Starker Wind taete ihr nicht gut.\n");

+

+  AddId(({"blume", "pusteblume", "loewenzahn"}));

+  SetProp(P_COMBATCMDS,(["puste loewenzahn":

+                         ([C_HEAL: 5])]));

+

+  SetProp(P_SENSITIVE,({({SENSITIVE_ATTACK, DT_AIR, 20}),

+                        ({SENSITIVE_INVENTORY, DT_AIR, 20})}));

+ 

+  AddCmd("puste&@ID", #'action_puste, "Puste wen oder was (an)?");

+}

+

+private action_puste(string str) {

+  if(environment()!=this_player()) {

+    notify_fail("Dazu solltest du "+name(WEN,1)+" haben.\n");

+	return 0;

+  }

+  if (this_player()->QueryProp(P_ATTACK_BUSY)) {

+    write("Du hast dafuer momentan einfach nicht mehr die Puste.\n");

+    return 1;

+  }

+  this_player()->SetProp(P_ATTACK_BUSY, 1);

+

+  if(counter<0) {

+    write(break_string("Du pustest sinnlos auf "+name(WEN, 2)+".", 78));

+	say(break_string(this_player()->Name(WER)+

+	  " pustet wie daemlich gegen "+name(WEN, 0)+".", 78));

+	return 1;

+  } else {

+    write(break_string(

+      "Du pustest "+name(WEN, 2)+" vorsichtig an, einige Samen "

+      "loesen sich und fliegen taumelnd in deinem Atem davon."+

+	  (counter<0?" Es bleibt nur noch ein nutzloser Strunk.":""), 78));

+    say(break_string(

+      this_player()->Name(WER)+" pustet sachte gegen "+name(WEN, 0)+" und "

+	  "du schaust verzueckt den davonfliegenden Samen nach.", 78));

+  }

+	

+  object who = this_player()->QueryEnemy();

+  

+  if(objectp(who)) {

+    if(!interactive(this_player())) {

+      who->ModifySkillAttribute(SA_SPEED, 80+random(10), 6);

+	  this_player()->heal_self(5);

+	} else

+	  who->ModifySkillAttribute(SA_SPEED, 90+random(10), 4);

+  }

+

+  counter--;

+  if(counter<0) {

+    call_out(#'remove, 10+random(60));

+	AddId("strunk");

+	SetProp(P_NAME, "Strunk");

+	SetProp(P_SHORT, "Der Strunk einer Pusteblume");

+	SetProp(P_LONG, "Ein haesslicher, leerer Strunk.\n");

+	SetProp(P_GENDER, MALE);

+  }

+  return 1;

+}

+

+private void notify_env_destroy() {

+  object ob = environment();

+  while(ob && !living(ob)) ob = environment(ob);

+  if(objectp(ob))

+    tell_object(ob, "Der Wind zerblaest "+name(WEN, 2)+".\n");

+  remove(1);

+}

+

+varargs void trigger_sensitive_attack() {

+  notify_env_destroy();

+}

+

+varargs void trigger_sensitive_inv() {

+  notify_env_destroy();

+}

+

+varargs int remove(int silent) {

+  if(!silent && living(environment()))

+    tell_object(environment(), "Du wirfst "+name(WEN, 2)+" weg.\n");

+  return ::remove(silent);

+}
\ No newline at end of file
diff --git a/doc/beispiele/testobjekte/command_me_testraum.c b/doc/beispiele/testobjekte/command_me_testraum.c
new file mode 100644
index 0000000..29086ce
--- /dev/null
+++ b/doc/beispiele/testobjekte/command_me_testraum.c
@@ -0,0 +1,33 @@
+#include <properties.h>
+inherit "/std/room";
+
+void create() {
+  ::create();
+  SetProp(P_LONG, "AddCmd-Testraum, Kommandos "
+                  "\"kriech\" und \"schleiche&heran|herum\".");
+  AddCmd("schleiche&heran|herum", "action_schleichen");
+  AddExit("gilde", "/gilden/abenteurer");
+}
+
+void init() {
+  ::init();
+  add_action("action_kriechen", "kriech", 1);
+}
+
+static action_schleichen(string str) {
+  string tmp = this_player()->QueryProp(P_RACE);
+  if(tmp[<1]=='e') tmp=tmp[0..<2];
+  write(break_string("Du versuchst leise zu schleichen, dabei passiert "
+    "dir aber ein allzu "+
+	(tmp=="Mensch"?"menschliches":lower_case(tmp)+"isches")+
+	" Missgeschick. Verflucht!", 78));
+  this_player()->command_me("\\furze");
+  return 1;
+}
+
+static int action_kriechen(string str) {
+  write(break_string("Deine Knie tun zu sehr weh dafuer.", 78));
+  tell_room(this_object(), break_string(this_player()->Name(WER)+
+    " knackt mit den Knien.", 78));
+  return 1;
+}
\ No newline at end of file
diff --git a/doc/beispiele/testobjekte/modifyskillspell_test.c b/doc/beispiele/testobjekte/modifyskillspell_test.c
new file mode 100644
index 0000000..3c58342
--- /dev/null
+++ b/doc/beispiele/testobjekte/modifyskillspell_test.c
@@ -0,0 +1,103 @@
+// ModifySkill-Beispiel fuer einem Spell. Erlaeuterung auf man-Page
+// und im Objekt selbst
+inherit "/std/thing";
+#include <new_skills.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <break_string.h>
+
+static int skillset(string str);
+static int skillunset(string str);
+
+void create() {
+  if(!clonep(this_object())) {
+    set_next_reset(-1);
+    return;
+  }
+
+  ::create();
+  set_next_reset(7200);
+
+  SetProp(P_NAME, "Skillbeispiel");
+  SetProp(P_SHORT, "Eine Skillbeispiel");
+  SetProp(P_LONG, break_string(
+    "Mit \"skillset\" kann man sich den Skill (eigentlich Spell) "
+    "\"fnrx\" setzen.\n"+
+    "Durch \"skillunset\" wird der Skill geloescht.\n"
+    "Das zaehlt deshalb als Spell, weil er kleingeschrieben und "
+    "damit direkt vom Spieler (also dir) als Kommando via UseSpell() "
+    "ausfuehrbar ist.", 78, 0, BS_LEAVE_MY_LFS));
+  SetProp(P_GENDER, NEUTER);
+  AddId(({"beispiel", "skillbeispiel", "spellbeispiel",
+          "skill", "spell"}));
+  
+  AddCmd("skillset", #'skillset);
+  AddCmd("skillunset", #'skillunset);
+}
+
+// Testfunktion, weil im Code dazu aufgefordert wird, das Objekt auch
+// mal zu wegzulegen. Spieler sollten nicht an sich herumfummeln.
+private static int _checkLearner(object pl) {
+  if(!IS_LEARNER(this_player())) {
+    notify_fail("Du bist kein Magier, deshalb geht das nicht.\n");
+    call_out(#'remove, 1);
+    return 0;
+  }
+  return 1;
+}
+
+static int skillset(string str) {
+  if(!_checkLearner(this_player()))
+    return 0;
+
+  if(this_player()->QuerySkill("fnrx")) {
+    notify_fail("Du kannst den Skill schon.\n");
+    return 0;
+  }
+
+  this_player()->ModifySkill("fnrx",
+    ([SI_CLOSURE: function int (object caster, string skill, mapping sinf) {
+            caster->LearnSkill("fnrx", 1);
+            tell_object(caster, "Peng! Dein Skillwert steigt auf "+
+                                caster->QuerySkillAbility("fnrx")+".\n");
+                    return 1;
+                  },
+      SI_SKILLABILITY: 8432]), 100, "ANY");
+  tell_object(this_player(), break_string(
+    "Der Skill ist gesetzt. Tipp doch ein paar Mal \"fnrx\" und schau den "
+    "Code unter "+load_name(this_object())+" an, damit du siehst, dass der "
+    "Skill jetzt nur aus einem Eintrag in dir und einer ueber UseSpell() "
+    "gerufenen Funktion in diesem Objekt besteht. Der Skill ist kein "
+    "Kommando in einem anderen Objekt.\n\n"
+    "Wenn du dieses Objekt weglegst, funktioniert also alles weiter. "
+    "Zerstoerst du aber das Objekt (oder dich mit \"ende\", funktioniert "
+    "die Skillfunktion nicht mehr.\n"
+    "Schau dir auch xeval this_player()->QuerySkill(\"fnrx\") an.", 78, 0,
+    BS_LEAVE_MY_LFS));
+  return 1;
+}
+
+// Here be dragons!
+// Bitte benutzt die folgende Art von Manipulation der Skills nur dann,
+// wenn ihr genau wisst, was ihr tut. In anderen Worten: NICHT MACHEN!
+// Und wenn, dann NUR AM EIGENEN MAGIEROBJEKT, NICHT AN SPIELERN!
+static int skillunset(string str) {
+  if(!_checkLearner(this_player()))
+    return 0;
+
+  // per Query() das Mapping an der _query_*-Fun vorbei direkt holen
+  mapping skills = this_player()->Query(P_NEWSKILLS);
+  // ... und manipulieren (wirkt sich wegen Referenz direkt aus)
+  if(mappingp(skills) && mappingp(skills["ANY"]) &&
+     member(skills["ANY"], "fnrx")) {
+    efun::m_delete(skills["ANY"], "fnrx");
+    tell_object(this_player(), "Erledigt.\n");
+  } else
+    tell_object(this_player(), "Nichts zu erledigen.\n");
+  
+  return 1;
+}
+
+void reset() {
+  remove();
+}
diff --git a/doc/beispiele/testobjekte/netdead_info_testraum.c b/doc/beispiele/testobjekte/netdead_info_testraum.c
new file mode 100644
index 0000000..b2d8a71
--- /dev/null
+++ b/doc/beispiele/testobjekte/netdead_info_testraum.c
@@ -0,0 +1,69 @@
+// Beispielraum fuer P_NETDEAD_INFO-Funktionalitaet
+#include <properties.h>
+#include <break_string.h>
+inherit "/std/room";
+     
+string create_destiny(mixed val);
+int create_room(string dir);
+
+void create() {
+  ::create();
+
+  if (clonep(this_object())) {
+    // setze Informationen, die in einem Netztoten gespeichert werden sollen
+    Set(P_NETDEAD_INFO, random(5));
+    SetProp(P_INT_LONG, break_string(
+      "Wenn du hier einschlaefst, wird der Raum nach 30s zerstoert. Beim "
+      "Aufwachen wirst du an die Blueprint dieses Raums die Info "+
+      QueryProp(P_NETDEAD_INFO)+" uebergeben. Diese wird aus dieser Info "
+      "einen Raumpfad ermitteln, in den du bewegt wirst.", 78));
+  } else {
+    // Blueprint: hier kann man zu einem Cloneraum gehen
+    AddExit("cloneraum", #'create_room);
+    SetProp(P_INT_LONG, break_string(
+      "Zum Testen einfach den Ausgang 'cloneraum' benutzen. Es wird dann "
+      "ein Raum geclont, in den man bewegt wird. Wenn man dort einschlaeft, "
+      "wird der geclonte Raum nach circa 30s zerstoert.\n"
+      "Beim Aufwachen werden die im Spieler gespeicherten Informationen des "
+      "geclonten (und nunmehr zerstoerten) Raumes benutzt, um einen "
+      "alternativen Aufwachraum (Klerusgilde, Karategilde, Wald oder Port "
+      "Vain) zu bestimmen.\n"
+      "Andernfalls wuerde der Spieler in der Abenteurergilde erwachen.", 78,
+      0, BS_LEAVE_MY_LFS));
+  }
+
+  // Set-Method, um die Informationen aus P_NETDEAD_INFO beim Aufwachen
+  // in der Blueprint auswerten zu koennen
+  Set(P_NETDEAD_INFO, #'create_destiny, F_SET_METHOD);
+  SetProp(P_LIGHT, 1);
+}
+     
+// Raum entfernen, normalerweise so KEINE GUTE IDEE!
+void BecomesNetDead(object pl) {
+  call_out(#'remove, 30);
+}
+
+// erzeuge einen Cloneraum und bewege den Spieler dahin
+int create_room(string dir) {
+  object dest = clone_object(object_name(this_object()));
+  this_player()->move(dest, M_NOCHECK);
+  return 1;
+}
+     
+// Set-Method fuer P_NETDEAD_INFO: gibt Pfad zurueck
+// benutze die Informationen aus dem jetzt aufwachenden Netztoten, um einen
+// alternativen Aufwachraum zu ermitteln, da der Einschlafraum zerstoert ist
+string create_destiny(mixed val) {
+  if (intp(val)) {
+    switch (val) {
+      case 0:
+        return "/d/ebene/room/PortVain/po_haf1";
+      case 1:
+        return "/gilden/klerus";
+      case 2:
+        return "/gilden/karate";
+      default:
+    }
+    return "/d/ebene/room/waldweg4";
+  }
+}
diff --git a/doc/beispiele/virtual/.readme b/doc/beispiele/virtual/.readme
new file mode 100644
index 0000000..7cbded2
--- /dev/null
+++ b/doc/beispiele/virtual/.readme
@@ -0,0 +1,2 @@
+Ein einfaches Beispiel fuer per virtual compiler erzeugte Objekte von Zesstra
+und ein leicht komplexeres von Hate.
diff --git a/doc/beispiele/virtual/hate/v_room.c b/doc/beispiele/virtual/hate/v_room.c
new file mode 100644
index 0000000..59c28fd
--- /dev/null
+++ b/doc/beispiele/virtual/hate/v_room.c
@@ -0,0 +1,12 @@
+inherit "/std/room";
+
+#include <properties.h>
+#include <defines.h>
+
+create()
+{
+        if(IS_BLUE(this_object())) return;
+        ::create();
+        previous_object()->CustomizeObject();
+        call_out(symbol_function("QueryObject", find_object(OBJECTD)), 2);
+}
diff --git a/doc/beispiele/virtual/hate/vr_compiler.c b/doc/beispiele/virtual/hate/vr_compiler.c
new file mode 100644
index 0000000..1a50d0b
--- /dev/null
+++ b/doc/beispiele/virtual/hate/vr_compiler.c
@@ -0,0 +1,85 @@
+// MorgenGrauen MUDlib
+//
+// VR_COMPILER.C -- virtual room compiler
+//
+// $Date: 2002/08/28 09:57:18 $
+// $Revision: 1.3 $
+/* $Log: vr_compiler.c,v $
+ * Revision 1.3  2002/08/28 09:57:18  Rikus
+ * auf strong_types umgestellt
+ *
+ * Revision 1.2  1994/07/27 14:48:18  Jof
+ * suid
+ *
+ * Revision 1.1  1994/02/01  19:47:57  Hate
+ * Initial revision
+ *
+*/
+
+// principle:
+// 	- inherit this object into your own 'virtual_compiler.c'
+//  - customize Validate() and CustomizeObject() for you own sake
+//  
+//  * Validate() checks if a room filename given as argument (without path)
+//    is valid and returns this filename with stripped '.c'!!
+//  * CustomizeObject() uses the previous_object()->Function() strategy to
+//    customize the standard object (for example to set a description)
+//
+// Properties: P_VALID_NAME, P_MIN_X, P_MAX_X, P_MIN_Y, P_MAX_Y
+
+#pragma strong_types
+
+inherit "/std/virtual/v_compiler";
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <defines.h>
+#include <v_compiler.h>
+#include "/obj/virtual/vr_compiler.h"
+#include <sys_debug.h>
+
+void create()
+{
+  ::create();
+  SetProp(P_VALID_NAME, "raum");
+}
+
+string Validate(string file)
+{
+  int x,y;
+  string path, name;
+
+  file = ::Validate(file);
+	if((sscanf(file, "%s[%d,%d]", name, x, y) == 3) &&
+			name == QueryProp(P_VALID_NAME) &&
+		  (x >= QueryProp(P_MIN_X) && x <= QueryProp(P_MAX_X)) &&
+		  (y >= QueryProp(P_MIN_Y) && y <= QueryProp(P_MAX_Y))
+		)
+      return file;
+}
+
+mixed CustomizeObject()
+{
+	string path, file, name;
+	int x,y;
+
+	if(!(file = ::CustomizeObject())) return 0;
+
+	path = QueryProp(P_COMPILER_PATH);
+	sscanf(file, "%s[%d,%d]", name, x, y);
+	name = QueryProp(P_VALID_NAME);
+	if(y < QueryProp(P_MAX_Y))
+	    previous_object()->AddExit("norden", 
+		path+"/"+name+"["+(x  )+","+(y+1)+"]");
+	if(y > QueryProp(P_MIN_Y))
+	    previous_object()->AddExit("sueden", 
+		path+"/"+name+"["+(x  )+","+(y-1)+"]");
+	if(x < QueryProp(P_MAX_X))
+	    previous_object()->AddExit("osten" , 
+		path+"/"+name+"["+(x+1)+","+(y  )+"]");
+	if(x > QueryProp(P_MIN_X))
+	    previous_object()->AddExit("westen", 
+		path+"/"+name+"["+(x-1)+","+(y  )+"]");
+}
+
diff --git a/doc/beispiele/virtual/hate/vr_compiler.h b/doc/beispiele/virtual/hate/vr_compiler.h
new file mode 100644
index 0000000..673fc54
--- /dev/null
+++ b/doc/beispiele/virtual/hate/vr_compiler.h
@@ -0,0 +1,33 @@
+// MorgenGrauen MUDlib
+//
+// V_COMPILER.H -- a general virtual compiler object
+//
+// $Date: 1994/02/01 19:47:57 $
+// $Revision: 1.1 $
+/* $Log: vr_compiler.h,v $
+ * Revision 1.1  1994/02/01  19:47:57  Hate
+ * Initial revision
+ *
+*/
+
+#include <v_compiler.h>
+
+#ifndef __VR_COMPILER_H__
+#define __VR_COMPILER_H__
+
+#define P_VALID_NAME	"valid_name"
+#define P_MIN_X				"min_x"
+#define P_MAX_X				"max_x"
+#define P_MIN_Y				"min_y"
+#define P_MAX_Y				"max_y"
+
+#endif // __V_COMPILER_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __VR_COMPILER_H_PROTO__
+#define __VR_COMPILER_H_PROTO__
+
+#endif // __VR_COMPILER_H_PROTO__
+
+#endif //NEED_PROTOTYPES
diff --git a/doc/beispiele/virtual/zesstra/std_arena.c b/doc/beispiele/virtual/zesstra/std_arena.c
new file mode 100644
index 0000000..de2e5a2
--- /dev/null
+++ b/doc/beispiele/virtual/zesstra/std_arena.c
@@ -0,0 +1,20 @@
+inherit "/std/room";
+
+#pragma strong_types,save_types,rtt_checks
+#include <properties.h>
+
+protected void create()
+{
+    if(!clonep(TO))
+        return;
+    ::create();
+
+    SetProp(P_INT_LONG, break_string(
+          "Du stehst hier in einer voellig leeren Arena."
+          "Gegen wen solltest Du hier denn gleich kaempfen...?",78));
+    SetProp(P_INT_SHORT, "In einer Arena");
+    SetProp(P_LIGHT, 1);
+    SetProp(P_INDOORS,0);
+
+    AddExit("raus", "/gilden/abenteurer");
+}
diff --git a/doc/beispiele/virtual/zesstra/virtual_compiler.c b/doc/beispiele/virtual/zesstra/virtual_compiler.c
new file mode 100644
index 0000000..36753df
--- /dev/null
+++ b/doc/beispiele/virtual/zesstra/virtual_compiler.c
@@ -0,0 +1,32 @@
+inherit "/std/virtual/v_compiler.c";
+
+#pragma strong_types,rtt_checks,save_types
+
+#include <thing/properties.h>
+
+#define NEED_PROTOTYPES
+#include <v_compiler.h>
+#undef NEED_PROTOTYPES
+
+protected void create() {
+    ::create();
+
+    // jeder Spieler kriegt eine "Kopie" von std_arena als Raum.
+    SetProp(P_STD_OBJECT, __DIR__"std_arena");
+    SetProp(P_COMPILER_PATH, __DIR__);
+}
+
+public string Validate(string file)
+{
+    string raum, spieler;
+    //.c abschneiden
+    file=::Validate(file);
+    // wenn das gewuenscht file dem Schema "arena|spielername" folgt, ist es
+    // zulaessig.
+    if(sscanf(file,"%s|%s",raum,spieler)==2 && raum=="arena")
+       return file;
+
+    // nicht zulaessig.
+    return 0;
+}
+
diff --git a/doc/beispiele/zauberwald/files.h b/doc/beispiele/zauberwald/files.h
new file mode 100644
index 0000000..8525d78
--- /dev/null
+++ b/doc/beispiele/zauberwald/files.h
@@ -0,0 +1,25 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#define HOME(x) "/doc/beispiele/"+x
+#define WALD(x) HOME("zauberwald/"+x)
+#define ROOM(x) WALD("room/"+x)
+#define NPC(x)  WALD("npc/"+x)
+#define OBJ(x)  WALD("obj/"+x)
+
+#include <defines.h>
+#include <properties.h>
+#include "/p/service/padreic/mnpc/mnpc.h"
+
+#define BS(x) break_string(x, 78)
+#define PO    previous_object()
+#define DEBUG(x) tell_object(find_player("padreic"), x);
+
+#define SAECKCHEN "/d/wald/padreic/obj/saeckchen.c"
+
+
+#define ZAUBERWALD "\npadreiczauberwald" /* gesetzt wenn man im wald ist */
+#define AGGRESSIVE_TIME 900              /* zeit bis zur befriedung */
+#define ZAUBERWALDNPC WALDID("npc")
+#define AUSGANG     "\npadreicausgang"   /* die letzte Bewegung */
+#define WALDID(x)   ZAUBERWALD+x
+#define EXTRA_LONG   "\npadreiczusatzlong" /* zur ergaenzung zur VC-long */
diff --git a/doc/beispiele/zauberwald/npc/arina.c b/doc/beispiele/zauberwald/npc/arina.c
new file mode 100644
index 0000000..9e08e25
--- /dev/null
+++ b/doc/beispiele/zauberwald/npc/arina.c
@@ -0,0 +1,101 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+#include <combat.h>
+#include <new_skills.h>
+#include "/p/zauberer/zauberer.h"
+
+inherit NPC("stdnpc");
+
+void create()
+{
+   ::create();
+   SetProp(P_SHORT, "Arina die Waldfee");
+   SetProp(P_LONG,
+      "Vor Dir steht Arina die weise Waldfee. Sie ist die Lehrerin des Waldes und\n"
+     +"unterrichtet die Pixies. In ihrer Hand haelt sie einen kleinen Zeigestock mit\n"
+     +"dem sie alles moegliche erklaert und Buchstaben in den Sand zeichnet. Auch\n"
+     +"Arina ist, genauso wie ihre Schwesten, bildhuebsch, doch ihr gestrenger Blick\n"
+     +"floesst jedem in ihrer Naehe grossen Respekt ein.\n");
+   SetProp(P_NAME, "Arina");
+   SetProp(P_ARTICLE, 0);
+   SetProp(P_GENDER, FEMALE);
+   SetProp(P_LEVEL, 80);
+   SetProp(P_ATTRIBUTES, (["int":80,"con":40,"str":16,"dex":50]) );
+   SetProp(P_DISABLE_ATTACK, -10000); // is nich :)
+   SetProp(P_BODY, 80);
+   SetProp(P_MAX_SP, 2000);
+   SetProp(P_SP, 2000);
+   SetProp(P_MAX_HP, 700);
+   SetProp(P_HP, 700);
+   SetProp(P_ALIGN, 400);
+   SetProp(P_RACE, "Fee");
+   SetProp(P_SIZE, 160+random(16));
+   SetProp(P_ALCOHOL, 0);
+   SetProp(P_CORPSE, OBJ("feenleiche"));
+   SetProp(P_MAX_ALCOHOL, 0); // kein rauschen :)
+   AddAdjective("wunderschoen");
+   AddId(({"arina", "fee", "waldfee", WALDID("fee")}));
+   SetProp(P_HANDS, ({" mit ihren zarten Haenden", 150, DT_BLUDGEON}) );
+   SetProp(P_SKILL_ATTRIBUTE_OFFSETS, ([SA_SPELL_PENETRATION: 200]));
+   SetProp(P_XP, 800*200*5*2);
+   SetProp(P_GUILD, "zauberer");
+   SetProp(P_Z_NO_MATERIAL, 1);
+   SetProp(P_DEFAULT_INFO, "schweigt und laechelt Dich einfach nur freundlich an.\n");
+   ModifySkill("verletze",
+      ([SI_SKILLABILITY:9000, SI_SPELLCOST:0,
+        SI_SPELLFATIGUE:0, SI_NO_CONSEQUENCES:10000,
+        SI_SKILLRESTR_USE:([P_GUILD_LEVEL:0,SR_FREE_HANDS:0]),
+        SI_ARMOUR_RESTR:0]),0,"zauberer");
+   SetProp(P_GUILD, 0);
+   SetProp(P_RESISTANCE_STRENGTHS,
+      ([DT_POISON: 0.15,
+        DT_MAGIC: -0.3 ]) );
+   AddItem(OBJ("stock"), CLONE_WIELD);
+   AddItem(OBJ("kleid"), CLONE_WEAR);
+   if (file_size(SAECKCHEN)>0) AddItem(SAECKCHEN, CLONE_NEW);
+}
+
+void Attack(object enemy)
+{
+   int normal_speed;
+   normal_speed=(enemy->QueryProp(P_SKILL_ATTRIBUTE_OFFSETS)||([]))[SA_SPEED]||100;
+   if (enemy && enemy->QuerySkillAttribute(SA_SPEED) > normal_speed) {
+      write("Die Fee macht eine beruhigende Handbewegung und ploetzlich fuehlst Du Dich\n"
+           +"ruhig und Du merkst wie Du allmaehlich wieder langsamer wirst...\n");
+      enemy->ModifySkillAttribute(SA_SPEED, -20, 180);
+   }
+   if (enemy) ::Attack(enemy);
+   SetProp(P_GUILD, "zauberer");
+   command("verletze mit "+({"feuer", "magie", "eis", "wasser", "wind"})[random(5)] );
+   SetProp(P_GUILD, 0);
+}
+
+int Defend(int dam, mixed dts, mixed spell, object enemy)
+{
+   int normal_speed;
+   normal_speed=(enemy->QueryProp(P_SKILL_ATTRIBUTE_OFFSETS)||([]))[SA_SPEED]||100;
+   if (enemy && enemy->QuerySkillAttribute(SA_SPEED) > normal_speed) {
+      write(Name(WER)+" macht eine beruhigende Handbewegung und ploetzlich fuehlst Du Dich\n"
+           +"ruhig und Du merkst wie Du allmaehlich wieder langsamer wirst...\n");
+      enemy->ModifySkillAttribute(SA_SPEED, -20, 180);
+   }
+
+   if ((!spell || (mappingp(spell) && spell[SP_PHYSICAL_ATTACK])) && !random(3)) {
+      tell_room(environment(), Name(WER)+" weicht schnell einen Schritt zurueck und weicht Deinem Angriff aus.\n");
+      dam=0;
+   }
+   return (int)::Defend(dam, dts, spell, enemy);
+}
+
+void NotifyPlayerDeath(object who, object killer, object lost_exp)
+{
+  if (!who || killer!=ME) return; // uninteressant
+  log_file("padreic/kill", ctime(time())+" "+capitalize(getuid(who))+" getoetet von /zauberwald/arina\n");
+}
+
+void die()
+{
+  log_file("padreic/kill", ctime(time())+" Arina wurde von "+get_killer()+" getoetet.\n");
+  ::die();
+}
diff --git a/doc/beispiele/zauberwald/npc/laufeiche.c b/doc/beispiele/zauberwald/npc/laufeiche.c
new file mode 100644
index 0000000..0649276
--- /dev/null
+++ b/doc/beispiele/zauberwald/npc/laufeiche.c
@@ -0,0 +1,124 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+#include <combat.h>
+#include <moving.h>
+#include <new_skills.h>
+
+inherit NPC("stdnpc");
+inherit MNPC_MOVING;
+
+void create()
+{
+   if (!clonep(ME)) return;
+   stdnpc::create();
+   moving::mnpc_create();
+   SetProp(P_NAME_ADJ, "alt");
+   SetProp(P_NAME, "Eiche");
+   SetProp(P_SHORT, "Eine alte Eiche");
+   SetProp(P_MSGOUT, "wandert");
+   SetProp(P_LONG,
+     "Vor Dir steht eine sehr sehr alte Eiche. Doch diese Eiche ist keine\n"
+    +"gewoehnliche Eiche, wie Du sie bereits an vielen anderen Stellen gesehn hast.\n"
+    +"Diese Eiche lebt! Du kannst deutlich einen Mund, eine Nase und sogar zwei\n"
+    +"Ohren erkennen. Sie ist eigentlich sehr friedlich gesonnen, kann aber auch\n"
+    +"sehr sehr boese werden, wenn es jemand wagt die Ruhe des Waldes zu stoeren.\n");
+   SetProp(P_RACE, "eiche");
+   SetProp(P_LEVEL, 50);
+   SetProp(P_ATTRIBUTES, (["int":50,"con":50,"str":70,"dex":15]) );
+   SetProp(P_GENDER, FEMALE);
+   SetProp(P_MAX_HP, 500);
+   SetProp(P_HP, 500);
+   SetProp(P_ALIGN, 500);
+   SetProp(P_BODY, 100);
+   SetProp(P_SIZE, 650+random(151));
+   AddAdjective("alt");
+   AddId(({"eiche", "eichen", WALDID("eiche") }));
+   SetProp(P_HANDS, ({" mit einem ihrer langen Aeste",400, DT_WHIP}) );
+   SetProp(P_SKILL_ATTRIBUTE_OFFSETS, ([SA_SPEED:180]));
+   SetProp(P_NOCORPSE, 1);
+   SetProp(P_XP, 500*400*5);
+   SetProp(P_DEFAULT_INFO, "bleibt absolut regungslos und reagiert ueberhaupt nicht.\n");
+   SetProp(P_RESISTANCE_STRENGTHS,
+    ([DT_SLASH:     0.1,
+      DT_MAGIC:    -1.0,
+      DT_BLUDGEON: -0.5,
+      DT_POISON:    0.25,
+      DT_HOLY:     -1.0,
+      DT_RIP:       0.25,
+      DT_FIRE:      0.25,
+      DT_PIERCE:   -0.5,
+      DT_WHIP:     -1.0 ]) );
+   SetProp(MNPC_AREA, ({ ROOM("weg2"), ROOM("lichtung") }) );
+   SetProp(MNPC_RANDOM, 10);
+   SetProp(MNPC_DELAY, 8);
+   SetProp(MNPC_WALK_TIME, 600);
+   SetProp(MNPC_FLAGS, MNPC_WALK|MNPC_NO_WALK_IN_FIGHT|MNPC_ONLY_EXITS);
+}
+
+static string _query_info()
+{
+   if (!PL || PL->QueryProp(ZAUBERWALD)<=time())
+      return "Die alte Eiche ist Dir sehr friedlich gesonnen. Es ist jedoch auch sicher\n"
+            +"klug dieses nicht zu aendern, da sie mit ihren Aesten wohl auch sehr kraeftig\n"
+            +"zuschlagen kann.\n";
+   return "Du hast den Zorn der alten Eiche auf Dich gezogen, das war ganz und gar nicht\n"
+         +"klug von Dir. Jetzt lebe auch mit den Konsequenzen....\n";
+}
+
+int remove()
+// wenn eine Eiche getoetet wird, dann verlangsamt das den reset der Eiche
+// die auf weg2 blockt. In der Regel steht ja auch so schon immer eine der
+// wanderden Eichen dort. Dies soll verhindern, das gerade eine Eiche resetet
+// wenn man eine tod hat.
+{
+   call_other(ROOM("weg2"), "delay_reset");
+   catch(call_other(QueryProp(MNPC_HOME), "delay_reset", 900+random(3600)));
+   return (int)::remove();
+}
+
+// wenn man den MNPC mit einem anderen stdnpc kombiniert muessen einige
+// funktionen per Hand ueberschrieben werden....
+
+void reset()
+{
+   stdnpc::reset();
+   moving::mnpc_reset();
+}
+
+void init()
+{
+   stdnpc::init();
+   moving::mnpc_init();
+}
+
+varargs int move(mixed dest, int meth, string dir, string out, string in)
+{
+   int i;
+
+   // extra fuer den Zauberwald eingebaut
+   if (environment() && object_name(environment())==ROOM("weg2")) {
+      // wenn es die einzige Eiche ist, dann bleibt sie hier stehn,
+      // bewacht den Weg und bewegt sich nicht mehr hier weg!
+      object *inv;
+
+      inv = all_inventory(environment()) - ({ ME });
+      for (i=sizeof(inv)-1; i>=0; i--)
+         if (BLUE_NAME(inv[i])==NPC("laufeiche")) break;
+      if (i<0) return ME_CANT_LEAVE_ENV;
+   }
+   //////////////////////////////////////
+   i=(int)stdnpc::move(dest, meth, dir, out, in);
+   if (i!=1) return i;
+   moving::mnpc_move();
+   return 1;
+}
+
+int PreventFollow(object dest)
+{  return moving::mnpc_PreventFollow(dest);  }
+
+void NotifyPlayerDeath(object who, object killer, object lost_exp)
+{
+  if (!who || killer!=ME) return; // uninteressant
+  log_file("padreic/kill", ctime(time())+" "+capitalize(getuid(who))+" getoetet von /zauberwald/laufeiche\n");
+}
diff --git a/doc/beispiele/zauberwald/npc/pixie.c b/doc/beispiele/zauberwald/npc/pixie.c
new file mode 100644
index 0000000..7ed953d
--- /dev/null
+++ b/doc/beispiele/zauberwald/npc/pixie.c
@@ -0,0 +1,150 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+#include <moving.h>
+#include <combat.h>
+
+inherit NPC("stdnpc");
+
+void create()
+{
+   ::create();
+   SetProp(P_SHORT, "Ein kleiner Pixie");
+   SetProp(P_SIZE, 55+random(11));
+   SetProp(P_LONG, BS(
+     "Der Pixie ist etwa "+QueryProp(P_SIZE)+"cm gross und sieht vermutlich einem "
+    +"kleinen pumeligen Menschenkind am aehnlichsten. Wie alle Pixies scheint er "
+    +"sehr verspielt zu sein und nichts als Unfug im Kopf zu haben, Du solltest "
+    +"Dich also vor ihm ihn acht nehmen. Auch wenn er aussieht wie ein Kind, sich "
+    +"benimmt wie ein Kind, so kann er Dir mit seiner Magie sicher uebel zu spieln."));
+   SetProp(P_INFO, /* kleine Warnung fuer die Kaempfer :) */
+     "Man sollte sich immer vor der Magie der Pixies in acht nehmen, sie ist nicht\n"
+    +"unbedingt gefaehrlich, aber es koennen die unerwartesten Dinge geschehn. So\n"
+    +"ist durchaus von Leuten bekannt die einen Kampf als kleine Ratte beendeten,\n"
+    +"oder deren Schwert im Kampf ploetzlich zu Scheisse zerfloss...\n");
+   SetProp(P_NAME_ADJ, "klein");
+   SetProp(P_NAME, "Pixie");
+   SetProp(P_GENDER, MALE);
+   SetProp(P_RACE, "pixie");
+   SetProp(P_ATTRIBUTES, (["int":30,"con":20,"str":15,"dex":30]) );
+   SetProp(P_LEVEL, 20);
+   SetProp(P_MAX_HP, 600);
+   SetProp(P_HP, 600);
+   SetProp(P_MAX_SP, 800);
+   SetProp(P_SP, 800);
+   SetProp(P_ALIGN, 250);
+   SetProp(P_HANDS, ({" mit seinen kleinen Haenden", 150, DT_BLUDGEON}) );
+   SetProp(P_XP, 600*150*5);
+   AddAdjective("klein");
+   AddId("pixie");
+}
+
+// teleporter Ziele im Gebiet. Der Pixie will ja einfach nur ein bisschen
+// Aergern und nicht helfen (ein Pixiekampf ist kein Ausgang :)
+#define DEST ({"lichtungno", "lichtungso", "lichtungn", "lichtungs", \
+               "lichtungnw", "lichtungsw", "lichtungo", "lichtungw", \
+               "weg2", "stein"})
+
+void Attack(object enemy)
+{
+   object ob, weapon;
+   
+   ob=SelectEnemy();
+   if (ob) switch(random(7*8)) { // jede 8te Runde ein Zauberspruch...
+      case 0: // in Frosch verwandeln...
+        if (!ob->QueryProp(P_FROG)) {
+          tell_object(ob, "Der Pixie dreht Dir eine lange Nase und verwandelt Dich in einen Frosch.\n");
+          say(BS("Der Pixie dreht "+ob->name(WEM)+" eine lange Nase und verwandelt "
+                +ob->QueryPronoun(WEN)+" in einen Frosch,"), ob);
+          ob->SetProp(P_FROG, 1);
+          return;
+        }
+        break;
+      case 8..10: // einfach nur nerviger Teleport innerhalb des Gebiets :)
+        tell_object(ob,
+          "Der Pixie schliesst kurz seine Augen und eh Du Dich versiehst, verschwimmt\n"
+         +"alles um Dich herum...\n");
+        say(BS("Der Pixie schliesst kurz seine Augen und ploetzlich loest sich "
+              +ob->name(WER)+" in Luft auf."), ob);
+        ob->move(ROOM(DEST[random(sizeof(DEST))]), M_TPORT|M_NOCHECK|M_SILENT);
+        return;
+      case 16: // Geschlecht aendern... :)
+        tell_object(ob,
+          "Der Pixie grinst breit bis ueber beide Ohren und schaut Dich an, Du weisst\n"
+         +"gar nicht wie Dir geschieht, aber irgendetwas aendert sich an Deinem Koerper.\n");
+        say(BS("Der Pixie grinst "+ob->name(WEN)+" breit an. Ploetzlich "
+         +(ob->QueryProp(P_GENDER)==FEMALE
+         ? "verschwinden ihre Brueste und es waechst ihr ploetzlich ein Bart."
+         : "verschwindet sein Bart und ihm wachsen zwei neue Brueste.")), ob);
+        if (ob->QueryProp(P_GENDER)==FEMALE)
+          ob->SetProp(P_GENDER, MALE);
+        else ob->SetProp(P_GENDER, FEMALE);
+        return;
+      case 24:
+        if (objectp(weapon=ob->QueryProp(P_WEAPON))) {
+           weapon->DoUnwield(1);
+           // verfluchte Waffen nicht betreffen :))
+           if (!objectp(weapon->QueryProp(P_WIELDED))) {
+              string str1, str2;
+              str1=str2=(weapon)->name(WER);
+              if (str1[0..2]=="ein") {
+                str1="D"+str1;
+                str2="s"+str2;
+              }
+              tell_object(ob, BS(
+               "Der Pixie starrt Dir in die Augen und schnippst einmal kurz mit seinen "
+              +"Fingern. Ploetzlich zerrint "+str1+" in Deinen Haenden zu einem Haufen "
+              +"Scheisse."));
+              say(BS("Der Pixie starrt "+weapon->name(WEM)+" in die Augen und "
+                    +"schnippst einmal kurz mit den Fingern. Ploetzlich "
+                    +"zerrint "+weapon->name(WEM)+" "+str2
+                    +" in einen Haufen Scheisse."), ob);
+              weapon->remove();
+              if (weapon) destruct(weapon);
+              return;
+           }
+        }
+        break;
+      case 32:
+        if (!ob->QueryProp(P_BLIND)) {
+          tell_object(ob, BS(
+             "Der Pixie haelt sich die Hand vor die Augen und grinst breit. Ploetzlich "
+            +"merkst Du, wie alles ganz dunkel um Dich rum wird..."));
+          say(BS("Der Pixie haelt sich die Hand vor die Augen und grinst breit. "
+                +ob->name(WER)
+                +" schaut daraufhin ziemlich irritiert in die Gegend."), ob);
+          ob->SetProp(P_BLIND, 1);
+          return;
+        }
+        break;
+      case 40:
+        if (ob->QueryProp(P_POISON)<3 && ob->QueryProp(P_MAX_POISON)>=3) {
+          tell_object(ob, BS(
+             "Der Pixie streicht sich mit der Hand zufrieden ueber seinen Bauch und schaut "
+            +"Dich dabei an. Ploetzlich wird Dir richtig uebel..."));
+          say(BS("Der Pixie streicht sich mit seiner Hand zufrieden ueber seinen Bauch und\n"
+                +"schaut "+ob->name(WEM)+" dabei in die Augen. Ploetzlich wird "+ob->name(WEM)
+                +" ganz uebel."), ob);
+          ob->SetProp(P_POISON, 3);
+          return;
+        }
+        break;
+      case 48:
+        if (!ob->QueryProp(P_DEAF)) {
+          tell_object(ob, "Der Pixie haelt sich die Ohren zu und grinst breit.\n");
+          say(BS("Der Pixie haelt sich die Ohren zu und grinst dabei "
+                +ob->name(WEN)+" an."), ob);
+          ob->SetProp(P_DEAF, 1);
+          return;
+        }
+        break;
+      default:
+   }
+   if (enemy && present(enemy, environment())) ::Attack(enemy);
+}
+
+void NotifyPlayerDeath(object who, object killer, object lost_exp)
+{
+  if (!who || killer!=ME) return; // uninteressant
+  log_file("padreic/kill", ctime(time())+" "+capitalize(getuid(who))+" getoetet von /zauberwald/pixie\n");
+}
diff --git a/doc/beispiele/zauberwald/npc/riese.c b/doc/beispiele/zauberwald/npc/riese.c
new file mode 100644
index 0000000..a68a3e2
--- /dev/null
+++ b/doc/beispiele/zauberwald/npc/riese.c
@@ -0,0 +1,92 @@
+// (c) 2003 by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+#include <properties.h>
+
+inherit NPC("stdnpc");
+
+void create()
+{
+   ::create();
+   create_default_npc(30, 2500);
+   SetProp(P_ARTICLE, 0);
+   SetProp(P_SHORT, "Grimmbart Felsenschieber");
+   SetProp(P_LONG,
+     "Vor Dir sitzt der Riese Grimmbart Felsenschieber. Eigentlich scheint er\n"
+    +"ein sehr gutmuetiger und freundlicher Riese zu sein. Aber irgendwie wirkt\n"
+    +"er derzeit sehr traurig.\n");
+
+   SetProp(P_NAME, "Grimmbart Felsenschieber");
+   SetProp(P_GENDER, MALE);
+   SetProp(P_SIZE, 297);
+   SetProp(P_ALIGN, 200);
+   SetProp(P_RESISTANCE_STRENGTHS,
+     ([DT_HOLY:-1.0,
+       DT_UNHOLY:0.5,
+       DT_MAGIC:0.25,
+       DT_POISON:-0.25,
+       DT_WHIP:-0.6,
+       DT_BLUDGEON:-0.3,
+       DT_PIERCE:-0.1,
+       DT_SLASH:-0.1
+     ]));
+
+   AddInfo(({"felsen", "fels"}),
+     "Ich liebe es meine Muskeln anzuspannen und Felsen zu schieben.",
+     "antwortet: ");
+   AddInfo("schieben",
+     "Ich habe doch keine Felsen mehr die ich schieben koennte.");
+   AddInfo(({"trauer", "traurigkeit"}),
+     "Frueher da habe ich den ganzen Tag hier meine Felsen hin- und "
+    +"hergeschoben, aber nun hat Ulinia mir verboten hier im Wald mit "
+    +"den Felsen zu spielen und ich musste sie alle wegtragen :o(",
+     "jammert: ");
+   AddInfo("tag", "Ja so ein Tag ist ganz schoen lang ohne Felsen",
+     "sagt: ");
+   AddInfo("ulinia",
+      "Eine richtig bloede Fee und haette sie ihre doofen Zauberkraefte nicht,"
+     +"koennte sie uns hier auch nicht alle so rumkommandieren *mopper*.",
+     "sagt: ");
+   AddInfo("grund",
+      "Nur weil mir einmal ausversehen ein klitzekleiner Fels in den Tuempel "
+     +"gefallen ist, musste ich all meine schoenen Felsen aus dem Wald bringen.",
+     "sagt: ");
+   AddInfo("tuempel",
+      "Bloeder Tuempel, wer braucht den schon!", "antwortet gereizt: ");
+   AddInfo("zauberkraefte",
+      "Ich freu mich schon richtig auf den Tag, an dem Ulinias Zauberkraefte "
+     +"nachlassen!",
+      "sagt: ");
+   AddInfo(({"wald", "zauberwald"}),
+      "Wenn es hier wieder ganz viele schoene Felsen gaebe, waer er wieder das "
+     +"reinste Paradies.", "antwortet: ");
+   AddInfo("paradies",
+      "Ja frueher war der Wald hier einmal das reinste Paradies.", "antwortet: ");
+
+   SetChats(3,
+     ({Name(WER)+" sagt: Ich bin so traurig.\n",
+       Name(WER)+" schluchzt: meine schoenen Felsen.. wuaaahhhhh\n",
+       Name(WER)+" sagt: nie wieder werde ich Felsen schieben koennen.\n",
+       Name(WER)+" sagt: F E L S E N !\n",
+       Name(WER)+" jammert: Die Welt ist so furchtbar ungerecht!\n",
+       Name(WER)+" fragt: Wie soll ich so bloss weiterleben?\n"
+     }));
+   
+   AddId("riese");
+   AddId("grimmbart");
+   AddId("felsenschieber");
+}
+
+void NotifyPlayerDeath(object who, object killer, object lost_exp)
+{
+  if (!who || killer!=ME) return; // uninteressant
+  log_file("padreic/kill", ctime(time())+" "+capitalize(getuid(who))
+    +"getoetet von /zauberwald/riese\n");
+}
+
+void die()
+{
+  log_file("padreic/kill", ctime(time())+" Grimmbart wurde von  "+get_killer()
+    +" getoetet.\n");
+  ::die();
+}
diff --git a/doc/beispiele/zauberwald/npc/stdnpc.c b/doc/beispiele/zauberwald/npc/stdnpc.c
new file mode 100644
index 0000000..a2b3d2f
--- /dev/null
+++ b/doc/beispiele/zauberwald/npc/stdnpc.c
@@ -0,0 +1,39 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+
+inherit "/std/npc";
+inherit "/p/service/mupfel/getkill";
+
+void create()
+{
+   ::create();
+   SetProp(ZAUBERWALDNPC, 1);
+   SetProp(P_AGGRESSIVE, ([P_RACE:(["Dunkelelf":1, 0:0.0]) ]));
+}
+
+int AutoAttack(object ob)
+{
+   if (ob && ob->QueryProp(ZAUBERWALD)>time()) return 1;
+   return ::AutoAttack(ob);
+}
+
+int InsertEnemy(object ob)
+{
+   int ret, is_enemy;
+   is_enemy=IsEnemy(ob);
+   ret=(int)::InsertEnemy(ob);
+   if (!is_enemy) { // alle anderen Zauberwald-NPCs im Raum verstaendigen
+      filter_objects(
+        filter_objects(all_inventory(environment())-users(), "QueryProp", ZAUBERWALDNPC),
+        "InsertEnemy", ob
+      );
+   }
+   return ret;
+}
+
+int Defend(int dam, mixed dts, mixed spell, object enemy)
+{
+   if (enemy) enemy->SetProp(ZAUBERWALD, time()+AGGRESSIVE_TIME);
+   return (int)::Defend(dam, dts, spell, enemy);
+}
diff --git a/doc/beispiele/zauberwald/npc/titina.c b/doc/beispiele/zauberwald/npc/titina.c
new file mode 100644
index 0000000..adce062
--- /dev/null
+++ b/doc/beispiele/zauberwald/npc/titina.c
@@ -0,0 +1,114 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+#include <combat.h>
+#include <new_skills.h>
+#include "/p/zauberer/zauberer.h"
+
+inherit NPC("stdnpc");
+
+void create()
+{
+   ::create();
+   SetProp(P_SHORT, "Titina die Waldfee");
+   SetProp(P_LONG,
+      BS("Vor Dir steht Titina die Waldfee. Ihr langes goldenes Haar weht "
+     +"leicht im Wind und das einzige was sie bedeckt, ist ein leichtes Blaetterkleid "
+     +"das ihre weiblichen Rundungen ausserordentlich gut zur Geltung bringt. "
+     +"Ihre schoenen blauen Augen, tun ein uebriges um Dich ganz wahnsinnig zu machen."));
+   SetProp(P_NAME, "Titina");
+   SetProp(P_ARTICLE, 0);
+   SetProp(P_GENDER, FEMALE);
+   SetProp(P_LEVEL, 80);
+   SetProp(P_ATTRIBUTES, (["int":60,"con":40,"str":16,"dex":50]) );
+   SetProp(P_DISABLE_ATTACK, -10000); // is nich :)
+   SetProp(P_BODY, 80);
+   SetProp(P_MAX_SP, 2000);
+   SetProp(P_SP, 2000);
+   SetProp(P_MAX_HP, 600);
+   SetProp(P_HP, 600);
+   SetProp(P_ALIGN, 400);
+   SetProp(P_RACE, "Fee");
+   SetProp(P_SIZE, 160+random(16));
+   SetProp(P_ALCOHOL, 0);
+   SetProp(P_CORPSE, OBJ("feenleiche"));
+   SetProp(P_MAX_ALCOHOL, 0); // kein rauschen :)
+   AddAdjective("wunderschoen");
+   AddId(({"titina", "fee", "waldfee", WALDID("fee")}));
+   SetProp(P_HANDS, ({" mit ihren zarten Haenden", 150, DT_BLUDGEON}) );
+   SetProp(P_SKILL_ATTRIBUTE_OFFSETS, ([SA_SPELL_PENETRATION: 100]));
+   SetProp(P_XP, 800*200*5*2);
+   SetProp(P_GUILD, "zauberer");
+   SetProp(P_Z_NO_MATERIAL, 1);
+   SetProp(P_DEFAULT_INFO, "schweigt und laechelt Dich einfach nur freundlich an.\n");
+   ModifySkill("verletze",
+      ([SI_SKILLABILITY:8000, SI_SPELLCOST:0,
+        SI_SPELLFATIGUE:0, SI_NO_CONSEQUENCES:10000,
+        SI_SKILLRESTR_USE:([P_GUILD_LEVEL:0,SR_FREE_HANDS:0]),
+        SI_ARMOUR_RESTR:0]),0,"zauberer");
+   SetProp(P_GUILD, 0);
+   SetProp(P_RESISTANCE_STRENGTHS,
+      ([DT_UNHOLY: 0.25,
+        DT_AIR:    0.3,
+        DT_POISON: 0.15]) ); // sonst selten in benutzung :)
+
+   AddItem(OBJ("kamm"), CLONE_WIELD);
+   AddItem(OBJ("kleid"), CLONE_WEAR);
+   if (file_size(SAECKCHEN)>0) AddItem(SAECKCHEN, CLONE_NEW);
+}
+
+static string _query_long()
+{
+   if (!PL || PL->QueryProp(P_GENDER)==FEMALE)
+      return Query(P_LONG)
+        +"Ihr ueberhebliches laecheln bringt Dich jedoch total zur Weissglut, so eine\n"
+        +"Zicke hier den ganzen Tag rumzusitzen und sich nur die Haare zu kaemmen.\n";
+   return Query(P_LONG)
+     +"Mit ihrem charmanten laecheln, zieht sie Dich sofort in ihren Bann und Du\n"
+     +"wuerdest ihr jeden Wunsch erfuellen, so sie denn einen aeussern wuerde.\n";
+}
+
+void Attack(object enemy)
+{
+   int normal_speed;
+   normal_speed=(enemy->QueryProp(P_SKILL_ATTRIBUTE_OFFSETS)||([]))[SA_SPEED]||100;
+   if (enemy && enemy->QuerySkillAttribute(SA_SPEED) > normal_speed) {
+      write("Die Fee macht eine beruhigende Handbewegung und ploetzlich fuehlst Du Dich\n"
+           +"ruhig und Du merkst wie Du allmaehlich wieder langsamer wirst...\n");
+      enemy->ModifySkillAttribute(SA_SPEED, -20, 180);
+   }
+   if (enemy) ::Attack(enemy);
+   SetProp(P_GUILD, "zauberer");
+   command("verletze mit "+({"feuer", "magie", "eis", "wasser", "gift",
+                             "wind", "saeure", "laerm"})[random(8)] );
+   SetProp(P_GUILD, 0);
+}
+
+int Defend(int dam, mixed dts, mixed spell, object enemy)
+{
+   int normal_speed;
+   normal_speed=(enemy->QueryProp(P_SKILL_ATTRIBUTE_OFFSETS)||([]))[SA_SPEED]||100;
+   if (enemy && enemy->QuerySkillAttribute(SA_SPEED) > normal_speed) {
+      write(Name(WER)+" macht eine beruhigende Handbewegung und ploetzlich fuehlst Du Dich\n"
+           +"ruhig und Du merkst wie Du allmaehlich wieder langsamer wirst...\n");
+      enemy->ModifySkillAttribute(SA_SPEED, -20, 180);
+   }
+
+   if ((!spell || (mappingp(spell) && spell[SP_PHYSICAL_ATTACK])) && !random(4)) {
+      tell_room(environment(), Name(WER)+" weicht schnell einen Schritt zurueck und weicht Deinem Angriff aus.\n");
+      dam=0;
+   }
+   return (int)::Defend(dam, dts, spell, enemy);
+}
+
+void NotifyPlayerDeath(object who, object killer, object lost_exp)
+{
+  if (!who || killer!=ME) return; // uninteressant
+  log_file("padreic/kill", ctime(time())+" "+capitalize(getuid(who))+" getoetet von /zauberwald/titina\n");
+}
+
+void die()
+{
+  log_file("padreic/kill", ctime(time())+" Titina wurde von "+get_killer()+" getoetet.\n");
+  ::die();
+}
diff --git a/doc/beispiele/zauberwald/npc/ulinia.c b/doc/beispiele/zauberwald/npc/ulinia.c
new file mode 100644
index 0000000..7ea596e
--- /dev/null
+++ b/doc/beispiele/zauberwald/npc/ulinia.c
@@ -0,0 +1,160 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+#include <combat.h>
+#include <moving.h>
+#include <new_skills.h>
+#include "/p/zauberer/zauberer.h"
+
+inherit NPC("stdnpc");
+
+string info_tarnzauber()
+{
+   object ob;
+   if (present("\ntarnzauber", PL))
+      return "sagt: Ich habe Dich doch bereist verzaubert.\n";
+   ob=clone_object(OBJ("tarnzauber"));
+   ob->move(PL, M_NOCHECK);
+   ob->Initialize(PL);
+   return "sagt: Nun gut, so sei es...\n"
+         +"macht eine weite Handbewegung und Du fuehlst wie Du Dich verwandelst.\n"
+         +"sagt: Viel Glueck!";
+}
+
+void create()
+{
+   ::create();
+   SetProp(P_SHORT, "Ulinia die Waldfee");
+   SetProp(P_LONG,
+      BS("Ueber dem Teich schwebt Ulinia die Waldfee. Ihr langes goldenes Haar weht "
+     +"leicht im Wind und das einzige was sie bedeckt, ist ein leichtes Blaetterkleid "
+     +"das ihre weiblichen Rundungen ausserordentlich gut zur Geltung bringt. "
+     +"In der Hand haelt sie einen kleinen Zauberstab, mit dem sie hoch ueber allem thront."));
+   SetProp(P_NAME, "Ulinia");
+   SetProp(P_ARTICLE, 0);
+   SetProp(P_GENDER, FEMALE);
+   SetProp(P_LEVEL, 80);
+   SetProp(P_ATTRIBUTES, (["int":80,"con":40,"str":16,"dex":50]) );
+   SetProp(P_DISABLE_ATTACK, -10000); // is nich :)
+   SetProp(P_BODY, 80);
+   SetProp(P_MAX_SP, 2000);
+   SetProp(P_SP, 2000);
+   SetProp(P_MAX_HP, 800);
+   SetProp(P_HP, 800);
+   SetProp(P_ALIGN, 400);
+   SetProp(P_RACE, "Fee");
+   SetProp(P_SIZE, 160+random(16));
+   SetProp(P_ALCOHOL, 0);
+   SetProp(P_CORPSE, OBJ("feenleiche"));
+   SetProp(P_MAX_ALCOHOL, 0); // kein rauschen :)
+   AddAdjective("wunderschoen");
+   AddId(({"fee", "waldfee", "ulinia", WALDID("fee")}));
+   SetProp(P_HANDS, ({" mit ihren zarten Haenden", 150, DT_BLUDGEON}) );
+   SetProp(P_SKILL_ATTRIBUTE_OFFSETS, ([SA_SPELL_PENETRATION: 250]));
+   SetProp(P_XP, 800*200*5*2);
+   SetProp(P_GUILD, "zauberer");
+   SetProp(P_Z_NO_MATERIAL, 1);
+   SetProp(P_DEFAULT_INFO, "schweigt und laechelt Dich einfach nur freundlich an.\n");
+   AddInfo(({"quest", "hilfe", "aufgabe", "dunkelelfenamulett", "delfenamulett"}),
+     "Ohh in der Tat haette ich da eine Aufgabe fuer Dich, aber ich muss Dich "
+    +"warnen, das sie nicht ganz ungefaehrlich ist.\n"
+    +"Ein befreundeter Kobold berichtete mir von einem lange verschollenen Buch, "
+    +"dass in der Bibliothek der Dunkelelfen wieder aufgetaucht ist. In diesem "
+    +"Buch ist unter anderem von einem alten Amulett die Rede, mit dessen Hilfe "
+    +"die Dunkelelfen ihre alte Macht wiedererlangen und erneut Furcht und "
+    +"Schrecken ueber das gesamte Morgengrauen bringen koennten. "
+    +"Wenn Du die Gefahr nicht scheust, waere es ausserordentlich nett wenn Du "
+    +"Dich nach Zzerenzzen begeben wuerdest und dort nach dem Amulett suchst.\n"
+    +"Solltest Du das Amulett tatsaechlich finden, so ist es wichtig das es "
+    +"auf keinen Fall in die Hand der Dunkelelfen geraet und zerstoert wird.",
+    "antwortet: ");
+   AddInfo("gefahr",
+     "Nun Du wirst Dich mitten unter die Dunkelelfen begeben muessen um "
+    +"zu ihrer Bibliothek gelangen zu koennen. In Zzerenzzen wirst Du "
+    +"hierbei voellig auf Dich allein gestellt sein.\n"
+    +"Alles was ich hierbei fuer Dich tun kann, ist einen Tarnzauber zu "
+    +"sprechen der Dich aeusserlich in einen Dunkelelfen verwandelt. "
+    +"Aber vorsicht, spaetestens wenn Du Dich in einen Kampf verwickelst, "
+    +"wird sich der Zauber loesen.", "sagt: ");
+   AddInfo("tarnzauber", #'info_tarnzauber, "");
+   AddInfo(({"furcht", "schrecken", "macht"}),
+     "Ja frueher waren die Dunkelelfen sehr sehr maechtig und alle "
+    +"friedliebenden Geschoepfe dieser Welt sollten hoffen das sie "
+    +"nie wieder ihre alte Macht erlangen werden.", "antwortet: ");  
+   AddInfo("amulett",
+     "Nun leider weiss ich auch nichts genaues ueber das Amulett. Ich bin "
+    +"nicht mal sicher ob es existiert oder je existiert hat. Aber sollte "
+    +"es noch existieren, muss es auf jeden Fall zerstoert werden!", "sagt: ");
+   AddInfo("kobold",
+     "Ich habe versprochen ihn nicht zu verraten und so kann ich Dir nichts "
+    +"weiter ueber ihn sagen.", "antwortet: ");
+   AddInfo(({"buch", "bibliothek"}),
+     "Ich denke am besten begibst Du Dich in die Hoehle des Loewen und versuchst "
+    +"in der Bibliothek der Dunkelelfen selbst ein Blick in dies Buch zu werfen.\n"
+    +"Wenn ich das noch recht in Erinnerung habe, handelt es sich hierbei um "
+    +"das Buch eines Dunkelelfen names Teyrion.", "sagt: ");
+   AddInfo("loewen", "Das ist doch bloss eine Redewendung.", "sagt: ");
+   AddInfo("redewendung", "Na das sagt man halt so...", "sagt: ");
+   AddInfo(({"hoehle", "zzerenzzen"}),
+     "Der Eingang nach Zzerenzzen liegt in der naehe von Wilhelmsburg gut hinter "
+    +"einem Wasserfall versteckt.", "sagt: ");
+   ModifySkill("verletze",
+      ([SI_SKILLABILITY:10000, SI_SPELLCOST:0,
+        SI_SPELLFATIGUE:0, SI_NO_CONSEQUENCES:10000,
+        SI_SKILLRESTR_USE:([P_GUILD_LEVEL:0,SR_FREE_HANDS:0]),
+        SI_ARMOUR_RESTR:0]),0,"zauberer");
+   SetProp(P_GUILD, 0);
+   SetProp(P_RESISTANCE_STRENGTHS,
+      ([DT_UNHOLY: 0.25,
+        DT_POISON: 0.15, 
+        DT_MAGIC: -0.3]) );
+
+   AddItem(OBJ("zauberstab"), CLONE_WIELD);
+   AddItem(OBJ("kleid"), CLONE_WEAR);
+   if (file_size(SAECKCHEN)>0) AddItem(SAECKCHEN, CLONE_NEW);
+}
+
+void Attack(object enemy)
+{
+   int normal_speed;
+   normal_speed=(enemy->QueryProp(P_SKILL_ATTRIBUTE_OFFSETS)||([]))[SA_SPEED]||100;
+   if (enemy && enemy->QuerySkillAttribute(SA_SPEED) > normal_speed) {
+      write("Die Fee macht eine beruhigende Handbewegung und ploetzlich fuehlst Du Dich\n"
+           +"ruhig und Du merkst wie Du allmaehlich wieder langsamer wirst...\n");
+      enemy->ModifySkillAttribute(SA_SPEED, -20, 180);
+   }
+   if (enemy) ::Attack(enemy);
+   SetProp(P_GUILD, "zauberer");
+   command("verletze mit "+({"feuer", "magie", "eis", "wasser", "gift",
+                             "wind", "saeure", "laerm"})[random(8)] );
+   SetProp(P_GUILD, 0);
+}
+
+int Defend(int dam, mixed dts, mixed spell, object enemy)
+{
+   int normal_speed;
+   normal_speed=(enemy->QueryProp(P_SKILL_ATTRIBUTE_OFFSETS)||([]))[SA_SPEED]||100;
+   if (enemy && enemy->QuerySkillAttribute(SA_SPEED) > normal_speed) {
+      write(Name(WER)+" macht eine beruhigende Handbewegung und ploetzlich fuehlst Du Dich\n"
+           +"ruhig und Du merkst wie Du allmaehlich wieder langsamer wirst...\n");
+      enemy->ModifySkillAttribute(SA_SPEED, -20, 180);
+   }
+
+   if ((!spell || (mappingp(spell) && spell[SP_PHYSICAL_ATTACK])) && !random(3)) {
+      tell_room(environment(), Name(WER)+" weicht schnell einen Schritt zurueck und weicht Deinem Angriff aus.\n");
+      dam=0;
+   }
+   return (int)::Defend(dam, dts, spell, enemy);
+}
+
+void NotifyPlayerDeath(object who, object killer, object lost_exp)
+{
+  if (!who || killer!=ME) return; // uninteressant
+  log_file("padreic/kill", ctime(time())+" "+capitalize(getuid(who))+" getoetet von /zauberwald/ulinia\n");
+}
+
+void die()
+{
+  log_file("padreic/kill", ctime(time())+" Ulinia wurde von "+get_killer()+" getoetet.\n");
+  ::die();
+}
diff --git a/doc/beispiele/zauberwald/npc/waechter.c b/doc/beispiele/zauberwald/npc/waechter.c
new file mode 100644
index 0000000..55c1a10
--- /dev/null
+++ b/doc/beispiele/zauberwald/npc/waechter.c
@@ -0,0 +1,87 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+#include <combat.h>
+#include <new_skills.h>
+
+inherit NPC("stdnpc");
+
+void create()
+{
+   ::create();
+   SetProp(P_NAME, "Waechter des Waldes");
+   SetProp(P_SHORT, "Der Waechter des Waldes");
+   SetProp(P_LONG,
+     "Vor Dir steht eine sehr sehr alte Eiche. Doch diese Eiche ist keine\n"
+    +"gewoehnliche Eiche, wie Du sie bereits an vielen anderen Stellen gesehn hast.\n"
+    +"Diese Eiche lebt! Du kannst deutlich einen Mund, eine Nase und sogar zwei\n"
+    +"Ohren erkennen. Sie ist eigentlich sehr friedlich gesonnen, kann aber auch\n"
+    +"sehr sehr boese werden, wenn es jemand wagt die Ruhe des Waldes zu stoeren.\n");
+   SetProp(P_RACE, "eiche");
+   SetProp(P_AGGRESSIVE, 0);
+   SetProp(P_LEVEL, 100);
+   SetProp(P_ATTRIBUTES, (["int":80,"con":100,"str":100,"dex":20]) );
+   SetProp(P_GENDER, MALE);
+   SetProp(P_MAX_HP, 1000);
+   SetProp(P_HP, 1000);
+   SetProp(P_ALIGN, 500);
+   SetProp(P_BODY, 100);
+   SetProp(P_SIZE, 1500);
+   SetProp(P_DISABLE_ATTACK, -10000); // is nich :)
+   AddId(({"eiche", "waechter","waechter des waldes", WALDID("waechtereiche") }));
+   SetProp(P_HANDS, ({" mit einem seiner langen Aeste",550,DT_WHIP}) );
+   SetProp(P_SKILL_ATTRIBUTE_OFFSETS, ([SA_SPEED:210]));
+   SetProp(P_NOCORPSE, 1);
+   SetProp(P_XP, 1000*550*5);
+   SetProp(P_DEFAULT_INFO, "bleibt absolut regungslos und reagiert ueberhaupt nicht.\n");
+   SetProp(P_RESISTANCE_STRENGTHS,
+    ([DT_SLASH:     0.1,
+      DT_MAGIC:    -1.0,
+      DT_BLUDGEON: -0.5,
+      DT_POISON:    0.25,
+      DT_HOLY:     -1.0,
+      DT_RIP:       0.25,
+      DT_FIRE:      0.25,
+      DT_PIERCE:   -0.5,
+      DT_WHIP:     -1.0 ]) );
+   AddCmd(({"osten", "westen"}), "cmd_blocken");
+}
+
+static string _query_info()
+{
+   if (!PL || PL->QueryProp(ZAUBERWALD)<=time())
+      return "Die alte Eiche ist Dir sehr friedlich gesonnen. Es ist jedoch auch sicher\n"
+            +"klug dieses nicht zu aendern, da sie mit ihren Aesten wohl auch sehr kraeftig\n"
+            +"zuschlagen kann.\n";
+   return "Du hast den Zorn der alten Waechtereiche auf Dich gezogen, das war ganz und\n"
+         +"gar nicht klug von Dir. Jetzt lebe auch mit den Konsequenzen....\n";
+}
+
+static int cmd_blocken()
+{
+   if (PL->QueryProp(P_RACE)=="Dunkelelf" &&
+       PL->QueryProp(AUSGANG)==query_verb()) {
+      write(BS(Name(WER, 1)+" laesst Dich nicht vorbei.")
+           +break_string("Du solltest hier besser verschwinden, "
+                        +"so Typen wie Du sind hier nicht gern gesehn!",
+                        78, Name(WER, 1)+" sagt: "));
+      return 1;
+   }
+   if (PL && PL->QueryProp(ZAUBERWALD) &&
+       PL->QueryProp(AUSGANG)==query_verb()) {
+      write(BS(Name(WER, 1)+" steht Dir da leider im Weg und laesst Dich nicht vorbei."));
+      return 1;
+   }
+}
+
+int remove()
+{
+   call_other(ROOM("weg1"), "delay_reset");
+   return (int)::remove();
+}
+
+void NotifyPlayerDeath(object who, object killer, object lost_exp)
+{
+  if (!who || killer!=ME) return; // uninteressant
+  log_file("padreic/kill", ctime(time())+" "+capitalize(getuid(who))+" getoetet von /zauberwald/waechter\n");
+}
diff --git a/doc/beispiele/zauberwald/obj/feenleiche.c b/doc/beispiele/zauberwald/obj/feenleiche.c
new file mode 100644
index 0000000..0e7e034
--- /dev/null
+++ b/doc/beispiele/zauberwald/obj/feenleiche.c
@@ -0,0 +1,24 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+#include <moving.h>
+
+inherit "std/corpse";
+
+int mampf( string str )
+{
+   object ob;
+   if (QueryDecay()<3) return (int)::mampf(str);
+   notify_fail("Was moechtest Du essen?\n");
+   if (!str || !id(str)) return 0;
+   if (!PL->eat_food(8)) return 1; // fehlermeldung gibt eat_food aus
+   if (!objectp(ob=present(WALDID("leichenmp"), PL))) {
+      ob=clone_object(OBJ("leichenmp"));
+      ob->move(PL, M_NOCHECK|M_GET);
+   }
+   ob->new_corpse();
+   PL->restore_spell_points(50+random(100));
+   write("Du merkst wie die noch im Koerper verbliebene Energie in Dich ueberfliesst.\n");
+   remove();
+   return 1;
+}
diff --git a/doc/beispiele/zauberwald/obj/hahnenfuss.c b/doc/beispiele/zauberwald/obj/hahnenfuss.c
new file mode 100644
index 0000000..8992e46
--- /dev/null
+++ b/doc/beispiele/zauberwald/obj/hahnenfuss.c
@@ -0,0 +1,32 @@
+#include <items/kraeuter/kraeuterliste.h>
+#include <items/kraeuter/kraeuter.h>
+#include "../files.h"
+
+inherit STDPLANT;
+
+void create()
+{
+   ::create();
+   customizeMe(WASSER_HAHNENFUSS_WEISS);
+}
+
+/*varargs int move(mixed dest, int method)
+{
+   // die Einwohner des Zauberwalds sehen es nicht gerne, wenn jemand ihre
+   // magischen Pflanzen pflueckt...
+   int res;
+   res=(int)::move(dest, method);
+   if (res!=1) return res;
+   if (environment()) 
+     environment()->SetProp(ZAUBERWALD, time()+AGGRESSIVE_TIME);
+   return 1;
+}*/
+
+protected void NotifyMove(object dest, object oldenv, int method) {
+  if ( !oldenv && objectp(dest) && query_once_interactive(dest) &&
+       strstr(object_name(environment(dest)),"zauberwald")>-1) {
+    dest->SetProp(ZAUBERWALD, time()+AGGRESSIVE_TIME);
+  }
+  return ::NotifyMove(dest, oldenv, method);
+}
+
diff --git a/doc/beispiele/zauberwald/obj/kamm.c b/doc/beispiele/zauberwald/obj/kamm.c
new file mode 100644
index 0000000..b26d6f8
--- /dev/null
+++ b/doc/beispiele/zauberwald/obj/kamm.c
@@ -0,0 +1,39 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+inherit "std/weapon";
+
+#include "../files.h"
+#include <combat.h>
+
+void create()
+{
+   ::create();
+   SetProp(P_SHORT, "Ein langer Kamm");
+   SetProp(P_NAME, "Kamm");
+   SetProp(P_GENDER, MALE);
+   SetProp(P_LONG,
+     "Dieser schoene Kamm gehoerte einmal einer Waldfee die damit immer ihr schoenes\n"
+    +"langes Haar kaemmte...\n");
+   AddAdjective("kamm");
+   AddId("kamm");
+   SetProp(P_WEAPON_TYPE, WT_CLUB);
+   SetProp(P_DAM_TYPE, DT_PIERCE);
+   SetProp(P_NR_HANDS, 1);
+   SetProp(P_WC, 100);
+   SetProp(P_VALUE, 100);
+   SetProp(P_NOBUY, 1);
+   SetProp(P_MATERIAL, ({ MAT_MISC_MAGIC, MAT_HORN }) );
+   AddCmd("kaemme", "cmd_kaemmen");
+}
+
+static int cmd_kaemmen(string str)
+{
+   notify_fail("Was moechtest Du denn kaemmen?\n");
+   if (str!="haare") return 0;
+   write("Eitel wie Du bist, versuchst Du Dir mit dem Kamm die Haare zu kaemmen, doch\n"
+        +"irgendwie ist er ein bisschen zu gross fuer Deine Haare.\n");
+   say(BS("Eitel wie "+PL->QueryPronoun()+" ist, kaemmt sich "+PL->name(WER)
+         +" die Haare. Doch irgendwie ist der Kamm ein bisschen zu gross fuer "
+         +PL->QueryPossPronoun(NEUTER, WEN, 1)+" kurzen Haare."));
+   return 1;
+}
diff --git a/doc/beispiele/zauberwald/obj/kleid.c b/doc/beispiele/zauberwald/obj/kleid.c
new file mode 100644
index 0000000..f52e157
--- /dev/null
+++ b/doc/beispiele/zauberwald/obj/kleid.c
@@ -0,0 +1,38 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+inherit "std/armour";
+
+#include "../files.h"
+#include <combat.h>
+
+void create()
+{
+   ::create();
+   SetProp(P_NAME, "Blaetterkleid");
+   SetProp(P_SHORT, "Ein Blaetterkleid");
+   SetProp(P_GENDER, NEUTER);
+   SetProp(P_LONG,
+     "Das Blaetterkleid stammt von einer Waldfee und war genau auf ihre Rundungen\n"
+    +"und ihre Groesse abgepasst. Du hast leider nicht die geringste Chance, dieses\n"
+    +"Kleid jemals tragen zu koennen.\n");
+   AddId(({"kleid", "blaetterkleid"}));
+   SetProp(P_MATERIAL, MAT_MISC_WOOD);
+   SetProp(P_ARMOUR_TYPE, AT_ARMOUR);
+   SetProp(P_VALUE, 500);
+   SetProp(P_NOBUY, 1);
+   SetProp(P_AC, 10);
+   SetProp(P_WEAR_FUNC, ME);
+}
+
+int WearFunc(object me, int silent)
+{
+   if (!PL || query_once_interactive(PL) || getuid(PL)!=getuid() ||
+       PL->QueryProp(P_RACE)!="Fee") {
+      if (!silent)
+        write("Dieses Kleid war genau auf die Rundungen und die Groesse der Waldfee\n"
+             +"angepasst von der es stammt. Du hast leider nicht die geringste Chance,\n"
+             +"dieses Kleid jemals tragen zu koennen.\n");
+      return 0;
+   }
+   return 1;
+}
diff --git a/doc/beispiele/zauberwald/obj/leichenmp.c b/doc/beispiele/zauberwald/obj/leichenmp.c
new file mode 100644
index 0000000..aea7c10
--- /dev/null
+++ b/doc/beispiele/zauberwald/obj/leichenmp.c
@@ -0,0 +1,81 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+/* Changelog:
+   * 09.11.06 Zesstra
+     object_info()[11] durch query_next_reset() ersetzt.
+     */
+#include "../files.h"
+
+inherit "/std/thing";
+
+static int leichen;
+
+void create()
+{
+    leichen=0;
+    if (!clonep(ME))
+       set_next_reset(-1);
+    else {
+       ::create();
+       SetProp(P_INVIS, 1);
+       SetProp(P_NODROP, 1);
+       SetProp(P_NEVERDROP, 1);
+       AddId(WALDID("leichenmp"));
+       set_next_reset(3600*3); // erster reset nach 3h
+    }
+}
+
+int remove()
+{
+    closure clo;
+    if (environment()) {
+      clo=environment()->Query(P_MAX_SP, F_QUERY_METHOD);
+      if (clo && to_object(clo)==ME)
+          environment()->Set(P_MAX_SP, 0, F_QUERY_METHOD);
+    }
+    destruct(ME);
+    return 1;
+}
+
+int calculate_mp()
+{
+    int mp;
+    switch(leichen) {
+       case 1:  mp = 5;  break;
+       case 2:  mp = 9;  break;
+       case 3:  mp = 12; break;
+       case 4:  mp = 14; break;
+       case 5:  mp = 15; break;
+       default: mp = 0;
+    }
+    SetProp(P_X_HEALTH_MOD, ([P_SP:mp]) );
+}
+
+void new_corpse()
+{
+    closure clo;
+    clo=PL->Query(P_MAX_SP, F_QUERY_METHOD);
+    if (clo && to_object(clo)!=ME) {
+       call_out("remove", 0);
+       return;
+    }
+    if (leichen<5) {
+       leichen++;
+       set_next_reset(3600+3);
+    }
+    else set_next_reset( (query_next_reset(ME)-time())+3600 );
+    calculate_mp();
+}
+
+void reset()
+{
+   leichen--;
+   if (environment()) {
+      tell_object(environment(), "Du spuerst wie Deine mentalen Kraefte schwaecher werden...\n");
+      if (environment()->QueryProp(P_SP) > environment()->QueryProp(P_MAX_SP))
+          environment()->SetProp(P_SP, environment()->QueryProp(P_MAX_SP));
+   }
+   calculate_mp();
+   if (leichen<=0)
+      remove();
+   else set_next_reset(3600);
+}
diff --git a/doc/beispiele/zauberwald/obj/stock.c b/doc/beispiele/zauberwald/obj/stock.c
new file mode 100644
index 0000000..f79d5e4
--- /dev/null
+++ b/doc/beispiele/zauberwald/obj/stock.c
@@ -0,0 +1,26 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+inherit "std/weapon";
+
+#include "../files.h"
+#include <combat.h>
+
+void create()
+{
+   ::create();
+   SetProp(P_SHORT, "Ein kleiner Zeigestock");
+   SetProp(P_NAME, "Zeigestock");
+   SetProp(P_GENDER, MALE);
+   SetProp(P_LONG,
+     "Ein kleiner Stock, mit dem man gut Dinge zeigen oder in den Sand malen kann.\n");
+   AddAdjective("klein");
+   AddId(({"stock", "zeigestock"}));
+   SetProp(P_WEAPON_TYPE, WT_STAFF);
+   SetProp(P_SIZE, 120);
+   SetProp(P_DAM_TYPE, DT_BLUDGEON);
+   SetProp(P_NR_HANDS, 1);
+   SetProp(P_WC, 120);
+   SetProp(P_VALUE, 300);
+   SetProp(P_NOBUY, 1);
+   SetProp(P_MATERIAL, MAT_MISC_WOOD);
+}
diff --git a/doc/beispiele/zauberwald/obj/tarnzauber.c b/doc/beispiele/zauberwald/obj/tarnzauber.c
new file mode 100644
index 0000000..c226494
--- /dev/null
+++ b/doc/beispiele/zauberwald/obj/tarnzauber.c
@@ -0,0 +1,78 @@
+// (c) 2003 by Padreic (Padreic@mg.mud.de)
+// Das Objekt realisiert einen Tarnzauber, aehnlich
+// dem Tarnhelm. Jedoch ohne Shadow und nur auf die
+// Rasse beschraenkt.
+
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+#include <defines.h>
+
+inherit "/std/thing";
+
+static string race;
+
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, 0);
+  SetProp(P_LONG, "Tarnzauber");
+  SetProp(P_NAME, "Tarnzauber");
+  SetProp(P_INVIS, 1);
+  SetProp(P_GENDER, MALE);
+  SetProp(P_VALUE,  0);
+  SetProp(P_NOGET, 1);
+  SetProp(P_WEIGHT, 0);
+  Set(P_DEFEND_FUNC, ME);
+  AddId("\ntarnzauber");
+}
+
+// die NPCs im Zauberwald durchschauen den Zauber... :o)
+string _query_race()
+{
+   if (previous_object(1) && getuid(previous_object(1))==getuid())
+      return previous_object()->_query_race()||race;
+   return "Dunkelelf";
+}
+
+void Initialize(object pl)
+{
+   object *armours;
+   pl->Set(P_RACE, #'_query_race, F_QUERY_METHOD);
+   armours=(pl->QueryProp(P_ARMOURS));
+   if (member(armours, ME)>=0) return 0;
+   pl->SetProp(P_ARMOURS, armours+({ ME }));
+   race=pl->Query(P_RACE);
+}
+
+int QueryDefend(string *dtyp, mixed spell, object enemy)
+{
+  mixed am;
+  // noch ein paar paranoide Sicherheitsabfragen... :o)
+  if (!previous_object() ||
+      !pointerp(am=previous_object()->QueryProp(P_ARMOURS)) ||
+      member(am, ME)==-1) return 0;
+  tell_object(previous_object(), "Dein Tarnzauber loest sich auf.\n");
+  remove();
+}
+
+int DefendFunc(string *dtyp, mixed spell, object enemy)
+{
+   return QueryDefend(dtyp, spell, enemy);
+}
+
+varargs int move(mixed dest, int method)
+// beim Tod soll sich der Zauber entfernen und auch nirgends rumfliegen
+{
+   int res;
+   res=(int)::move(dest, method);
+   if (!living(environment())) remove();
+   return res;
+}
+
+varargs int remove(int silent)
+{
+  if (living(environment())) environment()->Set(P_RACE,0,F_QUERY_METHOD);
+  return (int)::remove(silent);
+}
diff --git a/doc/beispiele/zauberwald/obj/zauberstab.c b/doc/beispiele/zauberwald/obj/zauberstab.c
new file mode 100644
index 0000000..1e01fa6
--- /dev/null
+++ b/doc/beispiele/zauberwald/obj/zauberstab.c
@@ -0,0 +1,36 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+inherit "std/weapon";
+
+#include "../files.h"
+#include <combat.h>
+
+void create()
+{
+   ::create();
+   SetProp(P_SHORT, "Ein Zauberstab");
+   SetProp(P_NAME, "Zauberstab");
+   SetProp(P_GENDER, MALE);
+   SetProp(P_LONG,
+     "Dies war einmal der Zauberstab einer Waldfee aus dem Zauberwald. In dem Stab\n"
+    +"liegt sicherlich noch eine grosse Magie verborgen, doch nutzen kannst Du sie\n"
+    +"nicht.\n");
+   AddId(({"stab", "zauberstab"}));
+   SetProp(P_WEAPON_TYPE, WT_CLUB);
+   SetProp(P_DAM_TYPE, ({ DT_MAGIC, DT_BLUDGEON }) );
+   SetProp(P_NR_HANDS, 1);
+   SetProp(P_WC, 140);
+   SetProp(P_VALUE, 800);
+   SetProp(P_NOBUY, 1);
+   SetProp(P_HIT_FUNC, ME);
+   SetProp(P_MATERIAL, ({ MAT_MISC_MAGIC, MAT_MISC_WOOD }) );
+}
+
+int HitFunc(object enemy)
+{
+    object ob;
+    ob=QueryProp(P_WIELDED);
+    if (ob && !query_once_interactive(ob) && getuid(ob)==getuid() &&
+        ob->QueryProp(P_RACE)=="Fee") return 100+random(300);
+    return 0;
+}
diff --git a/doc/beispiele/zauberwald/room/eingang.c b/doc/beispiele/zauberwald/room/eingang.c
new file mode 100644
index 0000000..058a05e
--- /dev/null
+++ b/doc/beispiele/zauberwald/room/eingang.c
@@ -0,0 +1,48 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+
+inherit ROOM("stdroom");
+
+void create()
+{
+   ::create();
+   SetProp(P_INDOORS, 0);
+   SetProp(P_LIGHT, 1);
+   SetProp(P_INT_SHORT, "Auf einem kleinen Waldweg");
+   SetProp(P_INT_LONG,
+     "Du stehst auf einem kleinen Waldweg der nach Westen hin tiefer in den Wald\n"
+    +"hinein fuehrt. Nach Osten kannst Du jedoch auch wieder zurueck zur Kreuzung\n"
+    +"gelangen. Alles ist hier recht duester, denn das Zweigdach der Baeume laesst\n"
+    +"kaum noch Sonnenlicht hindurch.\n");
+   AddDetail("wald", QueryProp(P_INT_LONG));
+   AddDetail(({"boden", "weg", "erde", "waldweg"}),
+     "Der Weg wurde nicht richtig angelegt und es scheint auch nicht so, als wuerde\n"
+    +"ihn jemand pflegen. Dennoch wachsen keinerlei Pflanzen auf dem Weg und Du\n"
+    +"gehst auf der blossen Erde.\n");
+   AddDetail(({"wegrand", "wegesrand", "gestruepp"}),
+     "Am Wegesrand wachsen einige alte Baeume und andere Pflanzen die zusammen ein\n"
+    +"undurchdringliches Gestruepp ergeben. Dir bleibt also nichts weiter uebrig, als\n"
+    +"nur den Weg nach Westen oder nach Osten zu folgen.\n");
+   AddDetail(({"baeume", "alter"}),
+     "Das Alter der Baeume laesst sich nicht genau bestimmen, sie scheinen jedoch\n"
+    +"sehr sehr alt zu sein. In einem der Baeume siehst Du ein kleines Baumhaus.\n");
+   AddDetail(({"pflanzen", "farne", "straeucher"}),
+     "Verschiedene Straeucher, Farne und zahlreiche Baeume zieren den Wegesrand.\n"
+    +"Du entdeckst dabei aber nichts weiter auffaelliges.\n");
+   AddDetail(({"osten", "kreuzung"}),
+     "Wenn Du dem Weg nach Osten hin folgst, kannst Du zurueck zur Kreuzung gelangen.\n");
+   AddDetail(({"zweige", "zweigdach", "sonnenlicht", "licht", "dach", "westen"}),
+     "Das Zweigdach der Baeume laesst hier so wenig Sonnenlicht hindurch, das Du ein\n"
+    +"Stueck weiter im Westen kaum noch ohne eigene Lichtquelle weiterkommen kannst.\n"
+    +"Du solltest Dich also besser nicht ohne eigene Lichtquelle tiefer in den Wald\n"
+    +"hinein wagen.\n");
+   AddDetail(({"fackel", "lampe", "licht", "lichtquelle", "lichtspruch"}),
+     "Wenn Du beabsichtigst weiter nach Westen zu gehn, solltest Du eine Fackel,\n"
+    +"eine Lampe oder sonst irgendeine Lichtquelle mitnehemn. Zumindest ist es dort\n"
+    +"sehr dunkel.\n");
+   AddDetail(({"wolke", "wolken", "sonne", "himmel"}),
+     "Der Himmel ist durch das dichte Dach der Zweige, nicht mehr zu sehn.\n");
+   AddExit("osten", "/d/unterwelt/raeume/wald4");
+   AddExit("westen", ROOM("weg1"));
+}
diff --git a/doc/beispiele/zauberwald/room/huette.c b/doc/beispiele/zauberwald/room/huette.c
new file mode 100644
index 0000000..e19a272
--- /dev/null
+++ b/doc/beispiele/zauberwald/room/huette.c
@@ -0,0 +1,129 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+// Ein Raum zum schoenen einbinden eines Seherhauses in den Wald
+
+#include "../files.h"
+#include <moving.h>
+
+inherit ROOM("stdroom");
+
+void create()
+{
+   ::create();
+   SetProp(P_INDOORS, 1);
+   SetProp(P_LIGHT, 1);
+   SetProp(P_INT_SHORT, "Mitten im Wald");
+   SetProp(P_INT_LONG,
+     "Du stehst nun inmitten des Zauberwalds genau vor einer riesigen Buche unter\n"
+    +"der jemand eine kleine Huette gebaut hat. Ringsumher stehen weitere Buesche\n"
+    +"und Baeume und bilden eine dichte Vegetation, doch im Vergleich zu dieser\n"
+    +"riesigen Buche wirkt alles andere wirklich mickrig. Durch das dichte\n"
+    +"Blaetterdach dringt nur wenig Licht und so wundert es auch nicht das der\n"
+    +"Boden hier relativ feucht ist und die Pflanzen so gut gedeihen koennen. Nach\n"
+    +"Norden fuehrt als einziger Ausgang ein kleiner Trampelpfad zur Lichtung.\n");
+   AddDetail(({"natur", "wald", "zauberwald"}), QueryProp(P_INT_LONG));
+   AddDetail(({"buesche", "baeume", "vegetation", "pflanzen", "farne"}),
+     "Rings um die Huette herscht eine wilde Vegetation. Es wachsen verschiedenste\n"
+    +"Farne und Buesche und eine vielfalt verschiedenster Baeume. Alles zusammen\n"
+    +"bildet eine fast undurchdringliche Vegetation.\n");
+   AddDetail(({"licht", "blaetterdach"}),
+     "Durch das relativ dichte Blaetterdach der hohen Baeume, faellt eigentlich nur\n"
+    +"sehr wenig Licht durch und so ist es hier doch schon relativ duester, auch\n"
+    +"wenn man noch nicht unbedingt eine eigene Lichtquelle braucht um etwas sehen\n"
+    +"zu koennen.\n");
+   AddDetail("lichtquelle",
+     "Eine eigene Lichtquelle ist hier nicht unbedingt erforderlich, da dann doch\n"
+    +"noch genuegend Licht durch das Blaetterdach durchdringt.\n");
+   AddDetail("himmel",
+     "Es scheint Dir als wuerde die Sonne gerade scheinen, auch wen das dichte\n"
+    +"Blaetterdach die meisten Sonnenstrahlen nicht hindurch laesst.\n");
+   AddDetail("boden",
+     "Der Boden ist hier eigentlich, mit Ausnahme des Trampelpfads natuerlich,\n"
+    +"ziemlich zugewachsen und von zahlreichen Pflanzen ueberwuchert.\n");
+   AddDetail("erde",
+     "Die Erde ist hier ueberall ein wenig lehmig, aber wirklich besonderes kannst\n"
+    +"Du an der Erde nicht entdecken.\n");
+   AddDetail(({"trampelpfad", "ausgang", "pfad"}),
+     "Der Trampelpfad scheint doch relativ oft benutzt zu werden, denn die Erde ist\n"
+    +"fast voellig bloss gelegt und die wenigen Pflanzen die dann doch versuchen ihn\n"
+    +"zu bewachsen sind fast alle niedergetrampelt.\n");
+   AddDetail(({"lichtung", "norden"}),
+     "Vielleicht solltest Du einfach dem Trampelpfad nach Norden folgen und Dir die\n"
+    +"Lichtung einfach aus der Naehe anschauen.\n");
+   AddDetail(({"buche", "riesige buche"}),
+     "Eichen sollst Du weichen, Buchen sollst Du suchen!\n");
+   AddDetail(({"eiche", "eichen"}),
+     "Du siehst zwar alles moegliche an Baeumen hier, aber Eichen befinden sich zum\n"
+    +"Glueck nicht dadrunter.\n");
+   AddDetail(({"glueck", "frage"}),
+     "Ob es soetwas wie Glueck ueberhaupt gibt, ist wohl eher eine philosophische\n"
+    +"Frage und etwas wo man stundenlang drueber diskutieren koennte. Fuer den\n"
+    +"Moment beschliesst Du jedenfalls lieber die schoene Natur zu geniessen und\n"
+    +"dem zwitschern der Voegel zu lauschen, als Dich mit soetwas zu beschaeftigen.\n");
+   AddDetail(({"voegel", "zipfel", "baumzipfel", "zipfel der baeume"}),
+     "Oben in den Zipfeln der Baeume kannst Du wage einige Voegel erkennen, doch um\n"
+    +"diese naeher spezifizieren zu koennen, sind diese dann doch zu weit weg.\n");
+   AddDetail("moment",
+     "Geniesse den Moment und lebe Dein Leben!\n");
+   AddDetail("ruhe",
+     "Die Ruhe wird hier und da lediglich vom zwitschern einiger Voegel unterbrochen.\n");
+   AddSpecialDetail(({"huette"}), "det_huette");
+   AddSounds(SENSE_DEFAULT,
+     "Es ist hier wirklich herrlich ruhig und nur hier und da hoerst Du das\n"
+    +"zwitschern einiger Voegel. Natur pur.\n");
+   AddSounds(({"voegel", "zwitschern", "voegel zwitschern"}),
+     "Du lauschst dem zwitschern der Voegel und geniesst die himmlische Ruhe.\n");
+   AddCmd(({"such", "suche"}), "cmd_suchen");
+   AddCmd(({"geniess", "geniesse"}), "cmd_geniessen");
+   AddCmd(({"leb", "lebe"}), "cmd_leben");
+   AddCmd(({"kletter", "klettere"}), "cmd_klettern");
+   AddExit("norden", ROOM("lichtungs"));
+}
+
+static string det_huette()
+// Langbeschreibung nicht fest setzen, damit der Spieler sie selbst
+// aendern kann.
+{
+   object ob;
+   if (ob=present("\nmorgeneshaus", ME))
+      return ob->long();
+}
+
+static int cmd_suchen(string str)
+{
+   notify_fail("WAS moechtest Du suchen?\n");
+   if (str!="buchen" && str!="buche") return 0;
+   write("Du brauchst nicht lange suchen und schon entdeckt Du eine riesige Buche vor\n"
+        +"Dir, aber eigentlich waere es wohl auch eher erstaunlich, wenn Du diese\n"
+        +"riesige Buche direkt vor Deiner Nase uebersehen wuerdest :).\n");
+   return 1;
+}
+
+static int cmd_geniessen(string str)
+{
+   notify_fail("WAS moechtest Du geniessen?\n");
+   if (str!="moment" && str!="den moment" && str!="ruhe") return 0;
+   write("Du haeltst einen Moment inne und geniesst die himmlische Ruhe, die nur durch\n"
+        +"das zwitschern einiger Voegel durchbrochen aber nicht gestoert wird.\n");
+   return 1;
+}
+
+static int cmd_leben(string str)
+{
+   notify_fail("WAS moechtest Du leben?\n");
+   if (str!="leben" && str!="mein leben" && str!="dein leben") return 0;
+   write("Nun, wenn die Welt so einfach waere das Du mit einem simplen Kommando Dein\n"
+        +"ganzes Leben steuern koenntest, dann waer die Welt doch wirklich langweilig,\n"
+        +"oder? Ich will doch stark hoffen, das es sooo weit noch nicht bei Dir ist :).\n");
+   return 1;
+}
+
+static int cmd_klettern(string str)
+{
+   notify_fail("WOHIN oder WORAUF moechtest Du klettern?\n");
+   if (member(({"auf baum", "auf baumzipfel", "baum hoch",
+                "buche hoch", "auf buche"}), str)==-1) return 0;
+   write("Du versuchst vergeblich die riesige Buche hochzuklettern, doch die unteren\n"
+        +"Aeste haengen leider bereits viel zu hoch, um diese erklimmen zu koennen.\n");
+   return 1;
+}
diff --git a/doc/beispiele/zauberwald/room/schule.c b/doc/beispiele/zauberwald/room/schule.c
new file mode 100644
index 0000000..a97acc4
--- /dev/null
+++ b/doc/beispiele/zauberwald/room/schule.c
@@ -0,0 +1,78 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+
+inherit ROOM("stdroom");
+
+void create()
+{
+   ::create();
+   SetProp(P_INDOORS, 0);
+   SetProp(P_LIGHT, 1);
+   AddSpecialDetail(({"pixieschule", "schule", "sandplatz", "waldschule"}), "_query_int_long");
+   AddDetail(({"spuren", "kampfplatz", "sand"}),
+     "Die Spuren im Sand deuten auf einen laengeren Kampf hin, der hier getobt\n"
+    +"haben muss. Wer auch immer ihn verloren hat, er hat sich wacker gewehrt.\n");
+   AddDetail("wald",
+     "Was meinst Du eigentlich, wo Du gerad die ganze Zeit rumlaeufst? Mach einfach\n"
+    +"die Augen auf und schau Dir alles in Ruhe an.\n");
+   AddDetail("ruhe", "In der Ruhe liegt die Kraft...\n");
+   AddDetail(({"augen", "details", "kampfzauber", "kopf", "freund"}),
+     "Nu uebertreib mal nich, nicht jedes Substantiv verbirgt wirklich ein\n"
+    +"sinnvolles Detail.\n");
+   AddDetail("kraft",
+     "Wow! Deine Kraft ist ploetzlich auf 32 angestiegen...\n"
+    +"Das hast Du jetzt aber nicht wirklich geglaubt, oder?\n"
+    +"Wieso untersuchst Du solche Details eigentlich?\n");
+   AddSpecialDetail("zeit", "det_zeit");
+   AddDetail(({"pixies", "bloedsinn", "unfug", "zauberwesen", "streich"}), 
+     "Pixies sind kleine Zauberwesen, die nichts als Unfug im Kopf haben. Wie\n"
+    +"niemand sonst nutzen sie ihre magischen Faehigkeiten nicht um jemanden zu\n"
+    +"heilen oder fuer Kampfzauber. Nein sie nutzen sie um jemanden zu Aergern\n"
+    +"oder ihm einen Streich zu spielen. Einen Pixie zum Freund zu haben, kann\n"
+    +"sicher sehr unterhaltsam sein, auf die Dauer aber wohl auch ziemlich\n"
+    +"anstrengend.\n");
+   AddSpecialDetail("boden", "det_boden");
+   AddDetail("halbkreis",
+     "Einige Pixies sitzen im Halbkreis um Arina herum und hoeren gespannt zu, wie\n"
+    +"sie ihnen einen spannende Geschichte erzaehlt.\n");
+   AddDetail(({"geschichten", "sprache"}),
+     "Wenn Arina doch bloss in einer Sprache sprechen wuerde, Die Du verstehst...\n"
+    +"Ihre Geschichten hoeren sich jedenfalls ausserordentlich spannend an. *seufz*\n");
+   AddDetail("himmel", "Der Himmel ist klar und nahezu wolkenfrei.\n");
+   AddDetail(({"osten", "ausgang", "lichtung", "weg", "waldweg"}),
+     "Der einzige Ausgang von hier, ist der kleine Waldweg im Osten der zurueck\n"
+    +"zur grossen Lichtung fuehrt.\n");
+   SetProp(P_INT_SHORT, "Die Pixieschule");
+   AddExit("osten", ROOM("lichtungnw"));
+   AddItem(NPC("pixie"), REFRESH_MOVE_HOME);
+   AddItem(NPC("pixie"), REFRESH_MOVE_HOME);
+   AddItem(NPC("pixie"), REFRESH_MOVE_HOME);
+   AddItem(NPC("arina"), REFRESH_REMOVE, 1);
+}
+
+static string det_boden()
+{
+   if (present(WALDID("fee"), ME))
+      return GetDetail("halbkreis");
+   return GetDetail("sand");
+}
+
+static string det_zeit()
+{  return "Wir haben genau "+dtime(time())[<8..]+".\n"; }
+
+static string _query_int_long()
+{
+   if (present(WALDID("fee"), ME))
+      return "Du befindest Dich in der Waldschule der Pixies. Einige Pixies sitzen im\n"
+            +"Halbkreis um die Waldfee Arina rum und hoeren gespannt ihren Geschichten zu.\n"
+            +"Gelegentlich erhebt sich Arina um mit einem Stock etwas in den Sand zu\n"
+            +"zeichnen, oder um einen der Pixies zu ermahnen weil er wieder irgendeinen\n"
+            +"verrueckten Bloedsinn gemacht hat. Da sich ringsherum der dichte Wald\n"
+            +"anschliesst, ist der einzige Ausgang, der Weg zurueck nach Osten auf die\n"
+            +"grosse Lichtung.\n";
+   return "Du stehst auf einem kleinen Sandplatz mitten im Wald. Hier und da siehst Du\n"
+         +"im Sand noch deutlich die Spuren eines Kampfes. Umgeben wird dieser Platz\n"
+         +"von einem dichten Wald, so dass der einzige Ausgang der Weg zurueck nach\n"
+         +"Osten zur grossen Lichtung ist.\n";
+}
diff --git a/doc/beispiele/zauberwald/room/stdlichtung.c b/doc/beispiele/zauberwald/room/stdlichtung.c
new file mode 100644
index 0000000..341ee6e
--- /dev/null
+++ b/doc/beispiele/zauberwald/room/stdlichtung.c
@@ -0,0 +1,157 @@
+// (c) by Thomas Winheller (Padreic@mg.mud.de)
+
+#include "../files.h"
+#include <moving.h>
+
+inherit ROOM("stdroom");
+inherit "/std/room/kraeuter";
+
+void maybe_replace_program()
+// dieser Standardraum, darf _nicht_ replaced werden...
+{ }
+
+int tuempel_ex(string exit, mapping map_ldfied)
+{   return map_ldfied[exit]==ROOM("tuempel");  }
+
+static int plant;
+
+void create()
+{
+   int i;
+   string tuempel, dir;
+   mixed tmp;
+
+   if (!clonep(ME)) return;
+   ::create();
+   plant=0;
+   SetProp(P_INDOORS, 0);
+   SetProp(P_LIGHT, 1);
+   SetProp(P_INT_SHORT, "Am Rande der Zauberlichtung");
+   SetProp(EXTRA_LONG, "");
+   if (PO) dir=(PO->CustomizeObject());
+   if (!dir) return;
+   dir=(["o":"ost", "w":"west", "n":"nord", "s":"sued",
+         "no":"nordost", "nw":"nordwest",
+         "so":"suedost", "sw":"suedwest"])[dir[8..]];
+   tuempel=filter(m_indices(QueryProp(P_EXITS)||([])), "tuempel_ex", ME, QueryProp(P_EXITS)||([]))[0][0..<3];
+   dir=capitalize(dir); tuempel=capitalize(tuempel);
+   SetProp(P_INT_LONG, BS(
+     "Du stehst am "+dir+"rand der grossen Lichtung inmitten des Zauberwalds. "
+    +tuempel+"lich von hier, befindet sich genau in der Mitte der "
+    +"Lichtung ein kleiner Tuempel in dessen Wasser sich das Sonnenlicht in "
+    +"allen Farben des Regenbogens spiegelt. Hier und da stehen um den Teich "
+    +"herum vereinzelt einige Eichen die sich irgendwie zu bewegen "
+    +"scheinen und ab und zu siehst Du wie sich einige kleine Pixies aus "
+    +"dem Wald heraus ans Wasser trauen. Die Eichen jedoch, halten immer "
+    +"einen deutlichen Abstand zum Wasser."+QueryProp(EXTRA_LONG)));
+   AddDetail(({"raum", lower_case(dir)+"rand", "zauberwald"}), QueryProp(P_INT_LONG));
+   AddDetail(({"pixies", "zauberwesen", "unfug"}),
+     "Pixies sind kleine verspielte Zauberwesen, die hier im Zauberwald wohnen. Um\n"
+    +"einem von ihnen zu begegnen wirst Du hier im Wald sicher nich lange suchen\n" 
+    +"muessen, doch vorsicht, sie haben nichts als Unfug im Kopf...\n");
+   AddDetail(({"farben", "regenbogen", "computer"}),
+     "Noch nie einen Regenbogen gesehn? Hmm.. dann sitzt Du wirklich eindeutig\n"
+    +"zuviel vor Deinem Computer...\n");
+   AddDetail("abstand",
+     "Wieso sie diesen Abstand halten kannst Du nicht nachvollziehn, aber mehr als\n"
+    +"ein paar Meter gehen sie nie an den alten Weiher dran.\n");
+   AddDetail(({"sonne", "sonnenstrahlen", "sonnenlicht", "himmel", "herz"}),
+     "Zahlreiche warme Sonnenstrahlen erwaermen Dein Herz und Du bist richtig\n"
+    +"gluecklich. Solch ein Idyllisches Plaetzchen willst Du am liebsten nie\n"
+    +"wieder verlassen. Fehlt eigentlich nur noch ein Partner, mit dem man\n"
+    +"hier gemeinsam in der Sonne liegen und das rauschen des Wassers geniessen\n"
+    +"kann.\n");
+   AddDetail("partner", "Den musst Du Dir schon selbst mitbringen...\n");
+   AddDetail(({"wald", "zauberwald"}),
+     "Du befindest Dich quasi mittem ihn ihm. Rund herum um die Lichtung schliesst\n"
+    +"sich ein dichter Wald an.\n");
+   AddDetail(({"eiche", "eichen"}),
+     "Hier befindet sich gerade keine, so dass Du sie nich naeher ansehn kannst.\n");
+   AddDetail(({"lichtung", "rand", "plaetzchen"}), QueryProp(P_INT_LONG));
+   AddDetail(({"wasser", "tuempel", "teich", lower_case(tuempel)+"en", "mitte"}),
+     "Wenn Du Dir den Tuempel in der Mitte der Lichtung etwas naeher ansehen\n"
+    +"moechtest, solltest Du vielleicht einfach noch ein bisschen naeher rangehn.\n");
+   AddDetail(({"waldweg", "weg"}),
+     "Ueber den Waldweg kannst Du den Wald wieder verlassen wenn Du moechtest.\n");
+   AddDetail(({"boden"}),
+     "Der Boden ist hier nirgends nackt sichtbar, sondern ist mit einem schoenen\n"
+    +"gruenen Rasenteppich bedeckt. Nicht zu hoch und nicht zu tief, offensichtlich\n"
+    +"bedarf diese Grasart keinerlei Pflege oder aber irgendjemand pflegt diesen\n"
+    +"Rasen hier seeehhhhrrr gruendlich.\n");
+   AddDetail(({"grasart", "rasen", "art", "gras", "rasenteppich", "halme"}),
+     "Du schaust Dir den Rasen noch einmal gruendlich an und streichst mit Deiner\n"
+    +"Hand durch die Halme. Was auch immer das fuer eine Art ist, solch einen Rasen\n"
+    +"hast Du noch nicht gesehn.\n");
+   AddDetail("hand",
+     "Sei lieber vorsichtig, sonst ist die Hand schneller ab als Du denkst...\n");
+   AddDetail("pflege",
+     "Ob jemand diesen Rasen pflegt, bzw. _wer_ kannst Du nicht entdecken...\n");
+   AddSounds(({"rauschen", "wasser"}), "Leise hoerst Du das rauschen des Wassers....\n");
+}
+
+#define VERB (["iss": "essen", "esse": "essen", "pflueck": "pfluecken", "pfluecke": "pfluecken"])
+
+static int cmd_pilze(string str)
+{
+   notify_fail("Was moechtest Du "+VERB[query_verb()]+"?\n");
+   if (str!="pilz" && str!="pilze") return 0;
+   if (plant>time()) {
+      write("Du solltest die Pilze erstmal in Ruhe wieder etwas nachwachsen lassen.\n");
+      return 1;
+   }
+   if (PL->QueryProp(ZAUBERWALD)<=time()) {
+      write("Du bist nur Gast hier und die Bewohner des Zauberwalds, sehen es nicht gerne\n"
+           +"wenn man hier einfach so die Pilze pflueckt.\n");
+      return 1;
+   }
+   plant=time()+300;
+   write("Du pflueckst Dir einige Pilze und isst sie. Anschliessend geht es Dir\n"
+        +"bedeutend besser.\n");
+   PL->reduce_hit_points( negate(100+random(100)) );
+   return 1;
+}
+
+static int cmd_farne(string str)
+{
+   int food;
+   notify_fail("Was moechtest Du "+VERB[query_verb()]+"?\n");
+   if (str!="farn" && str!="farne") return 0;
+   if (PL->QueryProp(ZAUBERWALD)<=time()) {
+      write("Du bist nur Gast hier und die Bewohner des Zauberwalds, sehen es nicht gerne\n"
+           +"wenn man hier einfach so den Farn abreisst.\n");
+      return 1;
+   }
+   // das tanken am Wasser ist umsonst, es heilt dabei wie ein
+   // mittleres Kneipenessen das genau 1 Muenze kostet. Geheilt werden nur HP
+   // Ein Missbrauch ist im Wald ausgeschlossen, bzw. man muesste den
+   // gesamten Wald leermetzeln und kann dann hier tanken bis zum naechsten
+   // reset. Bei Kosten von einer Muenze pro Heilung ist es sehr fraglich
+   // ob sich das lohnt.... :)
+   food=(PL->QueryProp(P_MAX_FOOD)) - (PL->QueryProp(P_FOOD));
+   if (food>10) food=10; // nie mehr als fuer 10 food tanken...
+   if (food <= 0 || !PL->eat_food(food, 1)) {
+      write("Du bist so voll, Du kannst leider wirklich nichts mehr essen...\n");
+      return 1;
+   }
+   write("Du pflueckst ein wenig von dem Farn und isst ihn. Sogleich spuerst Du, wie es\n"
+        +"Dir allmaehlich wieder besser geht...\n");
+   if (PL->eat_food(food)) { // food gutschreiben erfolgreich?
+      PL->buffer_hp(food*6, 8);
+      PL->SetProp(ZAUBERWALD, time()+AGGRESSIVE_TIME);
+   }
+   return 1;
+}
+
+static int cmd_sueden()
+{
+   if (PL->QueryProp(ZAUBERWALD)>time()) {
+      write("Du versuchst in den Wald zu fluechten, doch die Farne und Buesche bilden\n"
+           +"eine pflanzliche Barriere und lassen Dich nicht hindurch.\n");
+      return 1;
+   }
+   write("Du machst einen Schritt hin nach Sueden und sofort biegen sich auf magische\n"
+        +"Weise die Farne und Buesche zur Seite und geben einen Trampelpfad nach Sueden\n"
+        +"frei.\n");
+   PL->move(ROOM("huette"), M_GO, "durch die Buesche nach Sueden", "zwaengt sich");
+   return 1;
+}
diff --git a/doc/beispiele/zauberwald/room/stdroom.c b/doc/beispiele/zauberwald/room/stdroom.c
new file mode 100644
index 0000000..35297e6
--- /dev/null
+++ b/doc/beispiele/zauberwald/room/stdroom.c
@@ -0,0 +1,52 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+inherit "std/room";
+
+#include "../files.h"
+#include <moving.h>
+
+static string sounds();
+
+void create()
+{
+   ::create();
+   SetProp(P_NO_TPORT, NO_TPORT);
+   AddSmells(SENSE_DEFAULT,
+     "Der Duft vom Harz der Baeume und einiger sonstiger Pflanzen liegt in der Luft.\n"
+    +"Man merkt sofort, Du stehst mittem im Wald.\n");
+   AddSounds(SENSE_DEFAULT, #'sounds);
+   AddSounds(({"voegel", "voegeln", "zwitschern"}),
+     "Das herrliche zwitschern der Voegel, ist das einzige das die Ruhe im Wald\n"
+    +"durchbricht.\n");
+}
+
+static string sounds()
+{
+   if (sizeof(filter_objects(all_inventory(), "InFight")))
+      return "Der Laerm des Kampfes stoert die Ruhe des Waldes und uebertoent alles.\n";
+   if (sizeof(filter(all_inventory(), #'query_once_interactive))>1)
+      return "Es ist unheimlich still hier im Wald und nur das zwitschern einiger Voegel\n"
+            +"ist zu hoeren.\n";
+   return "Du hast das Gefuehl, als waerst Du ganz allein hier im Wald. Alles ist ruhig\n"
+         +"und nur hier und da vernimmst Du das zwitschern einiger Voegel.\n";
+}
+
+varargs void delay_reset(int time)
+// NPCs koennen boing resets verhindern
+{
+   if (time)
+      set_next_reset(time);
+   else set_next_reset(3600);
+}
+
+int _normalfunction()
+{
+  mixed z;
+  int i;
+  z=this_player()->QueryProp(AUSGANG);
+  this_player()->SetProp(AUSGANG, query_verb());
+  i=(int)::_normalfunction();
+  if (!i) this_player()->SetProp(AUSGANG, z);
+  return i;
+}
+              
\ No newline at end of file
diff --git a/doc/beispiele/zauberwald/room/stein.c b/doc/beispiele/zauberwald/room/stein.c
new file mode 100644
index 0000000..cd74b07
--- /dev/null
+++ b/doc/beispiele/zauberwald/room/stein.c
@@ -0,0 +1,112 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+
+inherit ROOM("stdroom");
+
+void create()
+{
+   ::create();
+   SetProp(P_INDOORS, 0);
+   SetProp(P_LIGHT, 1);
+   SetProp(P_INT_SHORT, "Bei einem Stein");
+   AddSpecialDetail(({"ort", "wald", "platz"}), "_query_int_long");
+   AddDetail("stein",
+     "Der Stein ist, durch die Sonnenstrahlen ordentlich aufgeheizt, angenehm warm\n"
+    +"und bietet eigentlich den idealen Platz um es sich gut gehn zu lassen und in\n"
+    +"der Sonne zu liegen.\n");
+   AddDetail(({"sonne", "himmel", "sonnenstrahlen", "strahlen"}),
+     "Der blaue Himmel ist nahezu wolkenfrei und so strahlt die Sonne in voller\n"
+    +"Pracht oben am Himmel und ihre Strahlen prasseln auf Deine Haut.\n");
+   AddDetail(({"boden", "weg", "waldweg", "erde"}),
+     "Ein ganz normaler Waldweg halt, ein bisschen sandig, einige kleine Aestchen\n"
+    +"ganz normal halt.\n");
+   AddSpecialDetail("zeit", "det_zeit");
+   AddDetail(({"aestchen", "stoeckchen"}),
+     "Ganz kleine heruntergefallene Stoeckchen, durch reges drueber laufen\n"
+    +"im Laufe der Zeit kleingemalen und vom Wetter aufgeloest...\n");
+   AddDetail("wetter",
+     "Auch wenn hier heute die Sonne scheint, gibt es natuerlich auch hier\n"
+    +"gelegentlich verregnete Tage.\n");
+   AddDetail("tage", "Naja... allzuviele sind es jedoch nicht.\n");
+   AddDetail(({"nordosten", "lichtung", "ausgang", "weiher"}),
+     "Im Nordosten kannst Du immer noch die grosse Lichtung mit den wandernden Eichen\n"
+    +"und dem verwunschenen Weiher erkennen.\n");
+   AddDetail(({"eichen", "wandernde eichen"}),
+     "Im Nordosten auf der Lichtung hast Du vorhin wandernde Eichen gesehn,\n"
+    +"vielleicht solltest Du einfach nochmal zurueck gehn und sie Dir naeher\n"
+    +"anschaun.\n");
+   AddDetail(({"wolke", "wolken", "schaefchenwolke"}),
+     "Der Himmel ist eigentlich wolkenfrei nur hier und da zieht mal eine kleine\n"
+    +"Schaefchenwolke vorbei.\n");
+   AddExit("nordosten", ROOM("lichtungsw"));
+   AddItem(NPC("titina"), REFRESH_REMOVE, 1);
+   AddCmd(({"setz", "setze", "leg", "lege", "kletter", "klettere"}), "cmd_setzen");
+   AddCmd(({"steh", "stehe"}), "cmd_aufstehn");
+}
+
+#define STEIN WALDID("stein")
+
+void init()
+{
+  if (PL) PL->SetProp(STEIN, 0);
+  ::init();
+}
+
+static int cmd_aufstehn(string str)
+{
+    if (!PL->QueryProp(STEIN)) return 0;
+    if (str!="auf" && str!="von stein auf") {
+       notify_fail("Von was moechtest Du aufstehn?\n");
+       return 0;
+    }
+    PL->SetProp(STEIN, 0);
+    write("Du kletterst also vom Stein wieder runter und stehst auf.\n");
+    return 1;
+}
+
+#define VERB(x) (["setz": "setzt", "leg": "legst", "kletter": "kletterst"])[x]
+
+static int cmd_setzen(string str)
+{
+   string verb;
+   verb=query_verb();
+   if (verb && verb[<1]=='e') verb=verb[0..<2];
+   if (str!="auf stein" && str!="stein" && str!="von stein") {
+      notify_fail(BS("Wohin moechtest Du "
+                 +(verb!="kletter" ? "Dich " : "")+verb+"en?"));
+      return 0;
+   }
+   if (PL->QueryProp(STEIN)) {
+      if (verb!="kletter" || str=="auf stein") {
+         write("Du sitzt doch bereits auf dem Stein!\n");
+         return 1;
+      }
+      PL->SetProp(STEIN, 0);
+      write("Du kletterst also vom Stein wieder runter und stehst auf.\n");
+      return 1;
+   }
+
+   write(BS("Entspannt "+VERB(verb)+" Du "+(verb!="kletter" ? "Dich " : "")
+        +"auf den Stein und geniesst die Sonne."));
+   PL->SetProp(STEIN, time());
+   return 1;
+}
+
+static string det_zeit()
+{  return "Wir haben genau "+dtime(time())[<8..]+".\n"; }
+
+static string _query_int_long()
+{
+   if (present(WALDID("fee"), ME))
+      return
+        "Hier mitten im Wald steht ein grosser Stein, auf dem Titina die wunderschoene\n"
+       +"Waldfee sitzt und sich in aller Ruhe ihr langes goldenes Haar kaemmt. Die\n"
+       +"Sonne strahlt genau auf den Stein und es ist wohl wirklich der gemuetlichste\n"
+       +"Ort auf Erden. Links und rechts vom Weg schliesst der dichte Wald an und so\n"
+       +"ist der einzige Ausgang Richtung Nordosten zurueck zur Lichtung.\n";
+   return "Hier mitten im Wald steht ein grosser Stein, der von der Sonne ordentlich\n"
+         +"aufgeheizt wird und ihn zu einem traumhaften Ort fuer alle Sonnenliebhaber\n"
+         +"macht. Links und rechts vom Weg schliesst direkt der dichte Wald an und so\n"
+         +"ist der einzige Ausgang Richtung Nordosten zurueck zur Lichtung.\n";
+}
diff --git a/doc/beispiele/zauberwald/room/tuempel.c b/doc/beispiele/zauberwald/room/tuempel.c
new file mode 100644
index 0000000..6f22d86
--- /dev/null
+++ b/doc/beispiele/zauberwald/room/tuempel.c
@@ -0,0 +1,179 @@
+// (c) by Thomas Winheller (Padreic@mg.mud.de)
+
+#include "../files.h"
+#include <moving.h>
+
+inherit ROOM("stdroom");
+inherit "/std/room/kraeuter";
+
+void create()
+{
+   ::create();
+   SetProp(P_LIGHT, 2);
+   SetProp(P_INDOORS, 0);
+   SetProp(P_INT_SHORT, "An einem verwunschenden Weiher");
+   AddSpecialDetail(({"ort", "weiher"}), "_query_int_long");
+   AddDetail(({"seerosen", "farben", "farbenvielfalt"}),
+     "Auf dem Wasser schwimmen zahlreiche Seerosen in allen Farben des Regenbogens\n"
+    +"eine solche Farbenvielfalt wie hier, hast Du bisher noch nirgendwo sonst\n"
+    +"gesehn.\n");
+   AddDetail("regenbogen", "Derzeit steht keiner am Himmel.\n");
+   AddDetail(({"libelle", "libellen", "atmosphaere", "summen"}),
+     "Hier und da schwirren ueber dem Weiher einige Libellen herum. Ihr Summen\n"
+    +"verbreitet eine richtig idyllische Atmosphaere an diesem Ort.\n");
+   AddDetail(({"pixies", "zauberwesen"}),
+     "Pixies sind kleine verspielte Zauberwesen, die hier im Zauberwald wohnen. Um\n"
+    +"einem von ihnen zu begegnen wirst Du hier im Wald sicher nich lange suchen\n"
+    +"muessen, doch vorsicht, sie haben nichts als Unfug im Kopf...\n");
+   AddDetail(({"wald", "zauberwald", "punkt"}),
+     "Der verwunschene Weiher ist mehr oder weniger der zentrale Punkt hier im\n"
+    +"Zauberwald. Er liegt mitten auf einer grossen Lichtung umgeben vom dichten\n"
+    +"Wald der Pixies\n");
+   AddDetail(({"lichtung", "mitte"}),
+     "Der Weiher befindet sich genau in der Mitte der Lichtung, sieh Dich halt\n"
+    +"einfach mal um.\n");
+   AddDetail(({"boden", "ufer"}),
+     "Das Ufer des Weihers besteht aus zahlreichen Steinen die bis in den Weiher\n"
+    +"hineinreichen. Auf und zwischen diesen Steinen sind jedoch ueberall zahlreiche\n"
+    +"kleine und mittlere Pflanzen gewachsen.\n");
+   AddDetail(({"stein", "steine"}), "Ein Stein ist ein Stein ist ein Stein ist ein Stein ist ein Stein.\n");
+   AddDetail("pflanzen",
+     "Die verschiedensten Pflanzen wachsen zwischen den Steinen empor und begruenen\n"
+    +"das Ufer. An einer Stelle kannst Du sogar weissen Wasser-Hahnenfuss wachsen\n"
+    +"sehen.\n");
+   AddDetail(({"wasser", "schimmer", "schimmern"}),
+     "Das Wasser ist irgendwie recht trueb und es geht ein sehr seltsames Schimmern\n"
+    +"von ihm aus. Irgendetwas besonderes hat es mit diesem Wasser auf sich, das\n"
+    +"spuerst Du genau. Du weisst jedoch nicht, ob es etwas gutes oder etwas boeses\n"
+    +"ist.\n");
+   AddDetail(({"himmel", "luft", "sonne", "waerme", "strahlen", "sonnenstrahlen"}),
+     "Die Sonne steht hoch am Himmel und fuellt diesen Ort mit einer herrlichen\n"
+    +"Waerme und ihre Strahlen reflektieren im Wasser in allen erdenklichen Farben.\n");
+   AddExit("norden", ROOM("lichtungn"));
+   AddExit("nordosten", ROOM("lichtungno"));
+   AddExit("nordwesten", ROOM("lichtungnw"));
+   AddExit("osten", ROOM("lichtungo"));
+   AddExit("westen", ROOM("lichtungw"));
+   AddExit("sueden", ROOM("lichtungs"));
+   AddExit("suedwesten", ROOM("lichtungsw"));
+   AddExit("suedosten", ROOM("lichtungso"));
+   AddItem(NPC("ulinia"), REFRESH_REMOVE, 1);
+   AddCmd(({"bad", "bade", "schwimm", "schwimme", "tauch", "tauche"}), "cmd_schwimmen");
+   AddCmd(({"trink", "trinke"}), "cmd_trinken");
+   AddCmd(({"pflueck", "pfluecke"}), "cmd_pfluecken");
+   // Detail per Hand hinzufuegen, da GetPlant anstelle von AddPlant
+   // verwendet wird.
+   AddPlantDetail(OBJ("hahnenfuss"));
+}
+
+static int cmd_trinken(string str)
+{
+   int drink;
+   object ob;
+   if (member(({"wasser", "wasser aus teich", "wasser aus tuempel",
+                "wasser aus see", "aus teich", "aus tuempel", "aus wasser",
+                "aus see"}), str)==-1) {
+      notify_fail("Was moechtest Du trinken?\n");
+      return 0;
+   }
+   if ((ob=present(WALDID("fee"), ME)) && !ob->IsEnemy(PL)) {
+      write(BS("Die Waldfee scheint Deinen Entschluss ein wenig von dem "
+         +"Wasser zu kosten, bemerkt zu haben und straft Dich mit einem "
+         +"mahnenden Blick ab. Du ueberlegst es Dir also noch einmal und "
+         +"laesst von Deinem Vorhaben ab."));
+      return 1;
+   }
+   // das tanken am Wasser ist umsonst, es heilt dabei wie ein
+   // Kneipengetraenk das genau 1 Muenze kostet. Geheilt werden nur SP
+   // Ein Missbrauch ist im Wald ausgeschlossen, bzw. man muesste den
+   // gesamten Wald leermetzeln und kann dann ueben bis zum naechsten
+   // reset. Bei Kosten von einer Muenze pro Heilung ist es sehr fraglich
+   // ob sich das lohnt.... :)
+   drink=(PL->QueryProp(P_MAX_DRINK)) - (PL->QueryProp(P_DRINK));
+   if (drink>10) drink=10; // nie mehr als fuer 10 soak tanken...
+   if (drink <= 0 || !PL->drink_soft(drink, 1)) {
+      write("Du bist so voll, Du kannst leider wirklich nichts mehr trinken...\n");
+      return 1;
+   }
+   write("Du gehst an den Rand des Weihers und nimmst einen kraeftigen Schluck von dem\n"
+        +"Wasser. Sogleich spuerst Du, wie Deine mentalen Kraefte langsam gestaerkt\n"
+        +"werden.\n");
+   if (PL->drink_soft(drink)) { // soak gutschreiben erfolgreich?
+      PL->buffer_sp(drink*6, 5);
+      PL->SetProp(ZAUBERWALD, time()+AGGRESSIVE_TIME);
+   }
+   return 1;
+}
+
+static int cmd_schwimmen(string str)
+{
+   string verb;
+
+   // besondere Eigenschaft der schluesselwoerter ausnutzen... :)
+   verb=(query_verb()||"");
+   if (verb[<1]!='e') verb+="e";
+   if (member(({"in teich", "in weiher", "in tuempel", "in see"}), str)==-1) {
+      notify_fail("Worin willst Du "+verb+"n?\n");
+      return 0;
+   }
+   str=capitalize(str[3..]); // "in " abschneiden...
+   if (present(WALDID("fee"), ME)) {
+      write(BS("Die Waldfee scheint Deinen Entschluss im "+str+" "+verb
+         +"n zu gehn irgendwie bemerkt zu haben und straft Dich mit einem "
+         +"mahnenden Blick ab. Deine Lust zum "+capitalize(verb)
+         +"n sinkt sogleich gegen null, denn Du verspuerst keine grosse "
+         +"Lust Dich mit der Waldfee anzulegen."));
+      return 1;
+   }
+   write(BS("Mutig gehst Du einen Schritt ins Wasser, da ueberkommt Dich auch "
+           +"schon ein seltsames kribbeln am ganzen Koerper. In windeseile "
+           +"hast Du den "+str+" auch schon wieder verlassen."));
+   return 1;
+}
+
+static int cmd_pfluecken(string str)
+{
+   object ob;
+   notify_fail("WAS moechtest Du pfluecken?\n");
+   if (member(({"hahnenfuss",
+                "wasser-hahnenfuss",
+                "weisser hahnenfuss",
+                "weisser wasser-hahnenfuss"}), str)<0) return 0;
+   if ((ob=present(WALDID("fee"), ME)) && !ob->IsEnemy(PL)) {
+      write(BS("Die Waldfee scheint Dein Vorhaben bemerkt zu haben und "
+         +"straft Dich mit einem mahnenden Blick ab. Du ueberlegst es Dir "
+         +"wieder anders und laesst von Deinem Vorhaben ab, solange die "
+         +"Fee hier wacht."));
+      return 1;
+   }
+   ob=GetPlant(OBJ("hahnenfuss"));
+   if (objectp(ob)) {
+      if (ob->move(PL, M_GET)==1)
+          write(BS("Vorsichtig pflueckst Du "+ob->name(WEN, 1)
+                  +" und nimmst "+ob->QueryPronoun(WEN)+" an Dich."));
+      else write(BS("Vorsichtig pflueckst Du "+ob->name(WEN, 1)+" kannst "
+                  +ob->QueryPronoun(WEN)+" aber nicht nehmen."));
+   }
+   else if (!ob)
+      write(BS("Der Hahnenfuss ist noch nicht wieder weit genug "
+              +"nachgewachsen um ihn pfluecken zu koennen."));
+   return 1;
+}
+
+static string _query_int_long()
+{
+   if (present(WALDID("fee"), ME))
+      return
+        "Du stehst nun an einem kleinen verwunschenen Weiher, inmitten des Zauberwalds.\n"
+       +"Auf dem Wasser schwimmen zahlreiche Seerosen in den verschiedensten Farben\n"
+       +"und hier und dort schwirrt eine Libelle in der Luft. Mitten ueber dem Weiher\n"
+       +"schwebt eine wunderschoene Waldfee in der Luft und scheint den Weiher zu\n"
+       +"bewachen. Irgendetwas geheimnisvolles scheint es mit dem Weiher auf sich\n"
+       +"zu haben, nur was?\n";
+   return
+     "Du stehst nun an einem kleinen verwunschenen Weiher, inmitten des Zauberwalds.\n"
+    +"Auf dem Wasser schwimmen zahlreiche Seerosen in den verschiedensten Farben und\n"
+    +"hier und dort schwirrt eine Libelle in der Luft. Alles scheint hier sehr\n"
+    +"friedlich zu sein, fast ein wenig zu friedlich. Irgendetwas geheimnisvolles\n"
+    +"scheint es mit diesem Weiher auf sich zu haben, nur was?\n";
+}
diff --git a/doc/beispiele/zauberwald/room/virtual_compiler.c b/doc/beispiele/zauberwald/room/virtual_compiler.c
new file mode 100644
index 0000000..1cc4d5c
--- /dev/null
+++ b/doc/beispiele/zauberwald/room/virtual_compiler.c
@@ -0,0 +1,133 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include <properties.h>
+#include <v_compiler.h>
+#include "../files.h"
+#include "/p/service/padreic/kraeuter/plant.h"
+
+inherit "std/virtual/v_compiler";
+
+create()
+{
+  ::create();
+  SetProp(P_STD_OBJECT, ROOM("stdlichtung"));
+  SetProp(P_COMPILER_PATH, ROOM(""));
+}
+
+string Validate(string file)
+{
+  file=::Validate(file);
+  switch(file) { 
+     case "lichtungso":
+     case "lichtungo":
+     case "lichtungno":
+     case "lichtungn":
+     case "lichtungs":
+     case "lichtungnw":
+     case "lichtungw":
+     case "lichtungsw":  return file;
+     default:
+  }
+}
+         
+mixed CustomizeObject()
+{
+  string file;
+  file=Validate(::CustomizeObject());
+  if (!random(3)) PO->AddItem(NPC("laufeiche"), REFRESH_DESTRUCT, ([MNPC_HOME: ROOM(file)]) );
+  switch(file) {
+     case "lichtungno":
+       PO->AddExit("westen", ROOM("lichtungn"));
+       PO->AddExit("sueden", ROOM("lichtungo"));
+       PO->AddExit("suedwesten", ROOM("tuempel"));
+       PO->AddExit("suedosten", ROOM("weg2"));
+       PO->SetProp(EXTRA_LONG,
+         " Im Suedosten befindet sich der Waldweg, ueber den Du den Wald wieder "
+        +"verlassen kannst.");
+       return file;
+
+     case "lichtungo":
+       PO->AddExit("norden", ROOM("lichtungno"));
+       PO->AddExit("sueden", ROOM("lichtungso"));
+       PO->AddExit("westen", ROOM("tuempel"));
+       PO->AddExit("osten", ROOM("weg2"));
+       PO->SetProp(EXTRA_LONG,
+         " Im Osten befindet sich der Waldweg, ueber den Du den Wald wieder "
+        +"verlassen kannst.");
+       return file;
+
+     case "lichtungso":
+       PO->AddExit("westen", ROOM("lichtungs"));
+       PO->AddExit("norden", ROOM("lichtungo"));
+       PO->AddExit("nordwesten", ROOM("tuempel"));
+       PO->AddExit("nordosten", ROOM("weg2"));
+       PO->SetProp(EXTRA_LONG,
+         " Im Nordosten befindet sich der Waldweg, ueber den Du den Wald wieder "
+        +"verlassen kannst.");
+       PO->AddItem(NPC("riese"), REFRESH_DESTRUCT);
+       return file;
+
+     case "lichtungs":
+       PO->AddExit("westen", ROOM("lichtungsw"));
+       PO->AddExit("osten", ROOM("lichtungso"));
+       PO->AddExit("norden", ROOM("tuempel"));
+       PO->SetProp(EXTRA_LONG,
+         " In der Wiese siehst Du an einer Stelle etwas Klee wachsen.");
+       PO->AddPlant(BITTERKLEE);
+       // "klee" _nach_ dem AddPlant einfuegen, damit die restlichen Details
+       // wie Bitterklee trotzdem noch eingefuegt werden
+       PO->RemoveDetail(({"klee"}));
+       PO->AddDetail(({"stelle", "klee"}),
+         "Bei der Stelle scheint es sich unzweifelhaft um Bitterklee zu handeln.\n");
+       PO->AddCmd("sueden", "cmd_sueden");
+       return file;
+
+     case "lichtungn":
+       PO->AddExit("westen", ROOM("lichtungnw"));
+       PO->AddExit("osten", ROOM("lichtungno"));
+       PO->AddExit("sueden", ROOM("tuempel"));
+       PO->SetProp(EXTRA_LONG, " Am Rand der Wiese entdeckt Du einige kleine Pilze im Gras.");
+       PO->AddCmd(({"esse", "iss", "pflueck", "pfluecke"}), "cmd_pilze");
+       PO->AddDetail(({"pilz", "pilze", "schimmer"}),
+         "Die Pilze haben einen aeusserst seltsamen metallischem Schimmer und\n"
+        +"verbreiten einen sehr angenehmen suesslichen Duft im Raum.\n");
+       PO->AddSmells(({"pilz", "pilze"}),
+         "Die Pilze verbreiten einen sehr suesslichen Duft.\n");
+       PO->AddSmells("duft", "Der Duft ist eigentlich sehr befreiend und angenehm.\n");
+       return file;
+
+     case "lichtungnw":
+       PO->AddExit("westen", ROOM("schule"));
+       PO->AddExit("osten", ROOM("lichtungn"));
+       PO->AddExit("sueden", ROOM("lichtungw"));
+       PO->AddExit("suedosten", ROOM("tuempel"));
+       PO->SetProp(EXTRA_LONG,
+         " Nach Westen kannst Du ein Stueck in den Wald hinein gehn.");
+       return file;
+
+     case "lichtungw":
+       PO->AddExit("norden", ROOM("lichtungnw"));
+       PO->AddExit("sueden", ROOM("lichtungsw"));
+       PO->AddExit("osten", ROOM("tuempel"));
+       PO->SetProp(EXTRA_LONG, " Am Waldrand stehen einige blaeuliche Farne.");
+       PO->AddCmd(({"esse", "iss", "pflueck", "pfluecke"}), "cmd_farne");
+       PO->AddDetail(({"farn", "farne"}),
+         "Die blaeulichen Farne sind wirklich sehr merkwuerdig. soetwas hast Du bisher\n"
+        +"noch nirgendwo gesehn. Irgendetwas besonderes hat es sicherlich damit auf sich.\n");
+       return file;
+
+     case "lichtungsw":
+       PO->AddExit("osten", ROOM("lichtungs"));
+       PO->AddExit("norden", ROOM("lichtungw"));
+       PO->AddExit("nordosten", ROOM("tuempel"));
+       PO->AddExit("suedwesten", ROOM("stein"));
+       PO->SetProp(EXTRA_LONG,
+         " Nach Suedwesten kannst Du ein Stueck in den Wald hinein gehn.");
+       return file;
+
+     default: return 0;
+  }
+  return file;
+}
+
+int NoParaObjects() { return 1; }
diff --git a/doc/beispiele/zauberwald/room/weg1.c b/doc/beispiele/zauberwald/room/weg1.c
new file mode 100644
index 0000000..169b73f
--- /dev/null
+++ b/doc/beispiele/zauberwald/room/weg1.c
@@ -0,0 +1,75 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+
+inherit ROOM("stdroom");
+
+void create()
+{
+   ::create();
+   SetProp(P_INDOORS, 0);
+   SetProp(P_LIGHT, 0);
+   SetProp(P_INT_SHORT, "Auf einem Waldweg im Zauberwald");
+   AddDetail(({"blaetterdach", "dach", "baeume"}),
+     "Die Baeume rechts und links des Weges bilden ueber dem Weg ein so dichtes\n"
+    +"Blaetterdach, das so gut wie ueberhaupt kein Sonnenlicht mehr auf den Weg\n"
+    +"scheint.\n");
+   AddSpecialDetail("szene", "_query_int_long");
+   AddSpecialDetail(({"weg", "weges", "waldweg", "boden"}), "det_weg");
+   AddDetail(({"licht", "sonnenlicht", "sonnenstrahlen"}),
+     "Durch das dichte Blaetterdach schaffen es die Sonnenstrahlen nur noch sehr\n"
+    +"vereinzelt bis auf den Weg durchzukommen. Dadurch ist es hier unten relativ\n"
+    +"Feucht, kalt und dunkel.\n");
+   AddDetail(({"himmel", "sonne", "wolke", "wolken"}),
+     "Das Blaetterdach ist hier so dicht, das Du den Himmel nicht mal mehr erahnen\n"
+    +"kannst. Du weisst nicht mal, ob es regnet oder ob die Sonne scheint.\n");
+   AddDetail("feucht",
+     "Bloss weil feucht grossgeschrieben ist es noch lange kein Substantiv, wenn\n"
+    +"ueberhaupt heisst es dann _Feuchtigkeit_ :).\n");
+   AddDetail("substantiv", "Nicht jedes Substantiv ist ein sinnvolles Detail :)\n");
+   AddDetail("feuchtigkeit", "Ein wenig feucht, wie man es halt aus jedem Wald so kennt.\n");
+   AddDetail("kaelte",
+     "Durch das wenige Sonnenlicht, das bis nach hier unten durchdringt, ist es\n"
+    +"hier verhaeltnismaessig frisch.\n");
+   AddDetail("dunkelheit",
+     "Wenn es hell ist, ist es nicht mehr dunkel und wenn es nicht hell ist, kannst\n"
+    +"Du nicht viel sehn. Wie also willst Du die Dunkelheit untersuchen :)?\n");
+   AddDetail("_feuchtigkeit_", "Meine Guete, nimm doch nich immer alles gleich _so_ woertlich.\n");
+   AddDetail("guete", "Sei lieber vorsichtig, soo guetig bin ich nu auch nicht :).\n");
+   AddDetail(({"lichtquelle", "nachtsicht"}),
+     "Hier ist es eigentlich verdammt dunkel und ohne Nachtsicht oder eine eigene\n"
+    +"Lichtquelle, koenntest Du hier _nichts_ sehn.\n");
+   AddDetail(({"ausgang", "osten"}),
+     "Wenn Du dem Weg Richtung Osten folgst, dann kommst Du ziemlich bald wieder\n"
+    +"Richtung Ausgang.\n");
+   AddDetail(({"stueck", "wald", "westen"}),
+     "Naja.. Du stehst zwar schon im Wald drin, aber nach Westen hin, gehst es noch\n"
+    +"ein Stueck tiefer hinein.\n");
+   AddExit("osten", ROOM("eingang"));
+   AddExit("westen", ROOM("weg2"));
+   AddItem(NPC("waechter"), REFRESH_REMOVE, 1);
+}
+
+static string det_weg()
+{
+   if (present(WALDID("waechtereiche"), ME))
+      return "Mitten auf dem Weg steht der Waechter des Waldes und kontrolliert sehr genau,\n"
+            +"wer in den Wald hinein darf und wer nicht. Eigentlich ist er jedoch den\n"
+            +"meisten immer sehr friedlich gesonnen.\n";
+   return "Der Weg fuehrt hier nach Westen noch ein Stueck tiefer in den Wald hinein,\n"
+         +"nach Osten kannst Du jedoch wieder in Richtung Ausgang des Zauberwalds gehn.\n";
+}
+
+static string _query_int_long()
+{
+   if (present(WALDID("waechtereiche"), ME))
+      return "Hier ist es jetzt richtig duester und ohne eigene Lichtquelle koenntest Du\n"
+            +"nichts mehr sehn, da so gut wie ueberhaupt kein Licht mehr durch das\n"
+            +"Blaetterdach auf den Weg scheint. Mitten in dieser eigentlich lebens-\n"
+            +"unfreundlichen Szene, steht eine grosse alte Eiche mitten auf dem Weg.\n";
+   return "Hier ist es jetzt richtig duester und ohne eigene Lichtquelle koenntest Du\n"
+         +"hier nichts mehr sehn, da so gut wie ueberhaupt kein Licht mehr durch das\n"
+         +"Blaetterdach auf den Weg scheint. Du kannst noch ein Stueck tiefer in den\n"
+         +"Wald hinein und nach Westen gehn, oder aber zurueck Richtung Kreuzung nach\n"
+         +"Osten.\n";
+}
diff --git a/doc/beispiele/zauberwald/room/weg2.c b/doc/beispiele/zauberwald/room/weg2.c
new file mode 100644
index 0000000..0d06169
--- /dev/null
+++ b/doc/beispiele/zauberwald/room/weg2.c
@@ -0,0 +1,93 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#include "../files.h"
+
+inherit ROOM("stdroom");
+
+void create()
+{
+   ::create();
+   SetProp(P_INDOORS, 0);
+   SetProp(P_LIGHT, 1);
+   SetProp(P_INT_SHORT, "Am Rande einer Lichtung im Zauberwald");
+   AddSpecialDetail(({"wald", "zauberwald"}), "_query_int_long");
+   AddDetail(({"westen", "lichtung", "plaetzchen", "idyllisches plaetzchen"}),
+     "Im Westen befindet sich eine grosse Lichtung mit einem kleinen Tuempel, am\n"
+    +"Rande der Lichtung stehen vereinzelt noch einige Baeume aber zum Tuempel hin,\n"
+    +"wird es dann mehr und mehr nur noch Gras das den Boden bedeckt.\n");
+   AddDetail(({"rasen", "gras"}),
+     "Am besten betrittst Du die Lichtung einfach und schaust Dir den Rasen\n"
+    +"naeher an.\n");
+   AddDetail("tuempel",
+     "Wenn Du Dir den Tuempel naeher ansehen moechtest, dann solltest Du schon\n"
+    +"ein Stueck naeher rangehen.\n");
+   AddDetail(({"stueck", "raum"}),
+     "Na wenigstens einen Raum weiter solltest Du schon gehn.\n");
+   AddDetail("schatten",
+     "Hier befindest Du Dich noch im Schatten der Baeume, die Lichtung im Westen\n"
+    +"liegt jedoch komplett in der waermenden Sonne.\n");
+   AddSpecialDetail("sonne", "det_sonne");
+   AddDetail(({"wegesrand", "baeume", "wegrand", "rand"}),
+     "Am Wegesrand stehen einige Baeume die ein schoenes Blaetterdach ueber den Weg\n"
+    +"spannen und den Weg in ihren Schatten legen.\n");
+   AddDetail(({"himmel", "blaetterdach", "dach", "licht", "sonnenstrahlen", "wolken", "wolke"}),
+     "Das Blaetterdach ist ziemlich dicht, so dass Du nicht soo viel sehen kannst,\n"
+    +"aber einige Sonnenstrahlen durchbrechen das Dach und erreichen den Weg.\n");
+   AddDetail(({"weg", "boden", "erde", "ausgang", "osten", "richtung"}),
+     "Im Westen fuehrt der Weg zu einer grossen Lichtung, waehrend er nach Osten hin\n"
+    +"Richtung Ausgang des Zauberwalds fuehrt.\n");
+   AddDetail("voegel", "Du kannst sie deutlich hoeren, bekommst aber keinen zu sehn.\n");
+   AddExit("osten", ROOM("weg1"));
+   AddExit("suedwesten", ROOM("lichtungso"));
+   AddExit("westen", ROOM("lichtungo"));
+   AddExit("nordwesten", ROOM("lichtungno"));
+   AddCmd("osten", "cmd_osten");
+   AddItem(NPC("laufeiche"), REFRESH_DESTRUCT);
+}
+
+static string det_sonne()
+{
+   if (PL) PL->SetProp(P_BLIND, 1);
+   return "Du schaust fasziniert in die Sonne und untersucht sie naeher, doch das war\n"
+         +"wohl ein grosser Fehler, denn ploetzlich wird es ganz ganz dunkel um Dich rum.\n";
+}
+
+void init()
+{
+   // wenn man von westen kommt und keine blockende Eiche hier ist, dann
+   // so betrachten als waere man aus dem osten gekommen
+   if (query_once_interactive(PL) && !present(WALDID("eiche")))
+      PL->SetProp(AUSGANG, "westen");
+   ::init();
+}
+
+static int cmd_osten()
+// wenn gerade eine Eiche hierhin gelaufen ist, kommen feindliche
+// Spieler nicht mehr vorbei :)
+{
+   if (PL && PL->QueryProp(AUSGANG)!="westen" &&
+       PL->QueryProp(ZAUBERWALD)>time() && present(WALDID("eiche"), ME)) {
+      if (present(WALDID("eiche 2"), ME))
+         write("Die Eichen stehen Dir dabei leider im Weg und lassen Dich nicht vorbei.\n");
+      else write("Die Eiche steht Dir dabei leider im Weg und laesst Dich nicht vorbei.\n");
+      return 1;
+   }
+}
+
+static string _query_int_long()
+{
+   if (!PL || PL->QueryProp(ZAUBERWALD)<=time())
+     return
+      "Du stehst nun mitten im Zauberwald. Im Westen erstreckt sich vor Dir eine\n"
+     +"grosse Lichtung, auf die Du muehelos gelangen kannst. Nach Osten hin fuehrt\n"
+     +"ein schmaler Weg zurueck zum Ausgang des Waldes. Alles scheint hier sehr\n"
+     +"friedlich zu sein, die Voegel zwitschern und das Blaetterdach ist hier auch\n"
+     +"nicht mehr so dicht wie im Osten, so dass einige Sonnenstrahlen den Weg\n"
+     +"erreichen. Es zieht Dich foermlich nach Westen auf die Lichtung, mitten auf\n"
+     +"dieses sonnige idyllische Plaetzchen.\n";
+   return "Nichts wie raus hier. Es war ganz und gar keine gute Idee hier im Wald Deiner\n"
+         +"Aggressivitaet freien Lauf zu lassen. Du kannst von Glueck haben, wenn Du mit\n"
+         +"einem blauen Auge davon kommst und den Wald noch lebend verlassen kannst.\n"
+         +"Nach Westen gelangst Du auf die grosse Lichtung, ratsamer waere jedoch wohl\n"
+         +"eher der Waldweg im Osten, ueber den Du den Zauberwald wieder verlassen kannst.\n";
+}
diff --git a/doc/concepts/.synonym b/doc/concepts/.synonym
new file mode 100644
index 0000000..06bb40a
--- /dev/null
+++ b/doc/concepts/.synonym
@@ -0,0 +1,19 @@
+effizient effizienz
+optimieren effizienz
+optimierung effizienz
+optimal effizienz
+performanz effizienz
+speicher memory
+swap memory
+swapping memory
+objekt objects
+objekte objects
+objektorientiert oop
+objektorientierung oop
+property properties
+props properties
+terminal terminals
+stil goodstyle
+style goodstyle
+guterstil goodstyle
+kosten ticks
diff --git a/doc/concepts/concepts b/doc/concepts/concepts
new file mode 100644
index 0000000..54bfae0
--- /dev/null
+++ b/doc/concepts/concepts
@@ -0,0 +1,9 @@
+NAME
+        concepts
+
+DESCRIPTION
+        This directory contains man pages about basic concepts of the
+        LPC language as it is provided by Amylaars parser/interpreter.
+
+SEE ALSO
+        driver(D), efun(E), applied(A), master(M), lpc(LPC)
diff --git a/doc/concepts/effizienz b/doc/concepts/effizienz
new file mode 100644
index 0000000..3f99c48
--- /dev/null
+++ b/doc/concepts/effizienz
@@ -0,0 +1,222 @@
+Effizienz
+ BESCHREIBUNG:
+     Effizienz in der Programmierung ist leider nicht ganz so einfach zu
+     beschreiben, da es viel mit der zugrundeliegenden Verarbeitung der
+     Programme zu tun hat. Es geht ganz gut am Beispiel.
+
+     Generell haben Lesbarkeit und Wartbarkeit von Code Vorrang vor dessen
+     Effizienz, gerade weil die wirklich arbeitslastigen Methoden in der Lib
+     stecken. Ausserdem ist es im Allgemeinen nicht empfehlenswert, (viel)
+     Aufwand in die Optimierung von Code zu stecken, solange nicht klar ist,
+     dass dies ueberhaupt notwendig ist.
+     Les-/Wartbarkeit und effizienter Stil schliessen sich aber nicht aus und
+     einige (einfache) Grundregeln lassen sich einfach einhalten.
+
+     Fuer diejenigen unter euch, die gerade erst mit LPC zusammenstossen
+     gibt es (*) an den besonders wichtigen Stellen. Auf Dauer solltet ihr
+     aber mal alle Eintraege ueberfliegen. Den ersten koennen alle hier
+     beherzigen:
+
+     LPC wird beim Laden nicht optimiert:
+      Das was ihr schreibt, wird auch so ausgefuehrt, es werden keine
+      Schleifen optimiert, keine unnoetigen Zuweisungen entfernt, nichts
+      wird veraendert:
+      - ueberlegt euch also euren Code gut, wenn er an kritischen Stellen
+        steht oder sehr viel Rechenzeit kostet (zB geschachtelte Schleifen)
+      - testet einfach mal Varianten und fragt auf -lpc nach Optimierung!
+
+     call_out und heart_beat erzeugen konstante Last:
+      Jeder call_out() steht in einer Liste, die im selben Takt wie der
+      heart_beat() durchsucht wird. Beides kostet Zeit. Beide Methoden
+      verhindern zudem das Ausswappen des entsprechenden Objektes. Deshalb
+      schalten sich Raummeldungen (AddRoomMessage funktioniert ueber
+      call_out()) und der heart_beat() von /std/npc nach dem Verlassen des
+      Raumes durch den letzten Spieler selbst aus.
+ *    - bitte achtet darauf, unnoetige call_out/heart_beat zu vermeiden.
+        (Insbesondere sich bewegende NPCs sollten sich auch irgendwann
+         wieder abschalten - es gibt einen funktionierenden MNPC mit diesen
+         Eigenschaften unter /p/service/padreic/mnpc.)
+      - fuer regelmaessige Aufrufe in einem Objekt, wo der genaue Zeitpunkt
+        nicht auf einige Sekunden ankommt,  bietet sich auch reset() mit
+        set_next_reset() an
+      - statt call_out()-Ketten in einem Raum laufen zu lassen, kann man
+        sich auch die letzte Aktivierung merken und bei einem init()
+        wieder ein entsprechend langes call_out() starten
+
+     Speicher und das Drumherum:
+      Die Speichersituation ist nicht mehr verzweifelt. Das heisst aber
+      nicht, dass damit geschlampt werden kann. Gleichzeitig ist die
+      Reservierung von Speicher und die Garbage Collection, das Einsammeln
+      freigegebenen Speichers bei Freigaben von Variablen (wie bei x+y,
+      x=0 (x,y==array/mapping)) immer kostspielig. Folgend ein paar
+      Tipps dazu:
+      Groesse:
+      - wenn moeglich, globale Variablen nach Nutzung freigeben - ggf.
+        #defines benutzen: Vorsicht jedoch bei Mapping/Array (siehe unten)
+      - globale oder in Properties abgespeicherte Mappings/Arrays/
+        Strings klein halten und nur dynamisch erweitern
+      - programmiert man an vielen Stellen gleichen Code, dann ist es
+        sinnvoll, diesen in eine eigene Datei/Klasse zu giessen und von
+        dieser zu erben - das spart Speicher und laesst sich besser warten
+      - replace_program bitte nur benutzen, wenn man weiss, was es bewirkt,
+        /std/room verwendet es bereits automagisch
+ *    - Objekte in Raeumen und NPCs sollten per AddItem() addiert werden,
+        da die generelle Aufraeumfunktion /std/room::clean_up() dann weiss
+        ob der Raum entfernt werden kann
+      - es sollte keine ewigen Objektquellen geben
+      - Blueprints:
+        - Soll es immer nur ein Objekt von etwas geben, stellt die Blueprint
+          per AddItem(...,...,1) dort hin.
+          Achtung: Blueprints neu zu laden, ist teuer im Vergleich zum clonen.
+                   Gerade bei NPCs (die beim Tod zerstoert werden), sollte
+                   man das im Hinterkopf behalten.
+        - Die BP von geclonten Objekten muss nicht immer initialisiert werden,
+          speziell bei komplexen Objekten kann es sich lohnen, die
+          Initialisierung der BP im create abzubrechen. (Denn meistens ist nur
+          ihr Programm interessant)
+          protected void create() {
+            if(!clonep(this_object())) {
+              set_next_reset(-1);   // falls die Clones im reset() was
+              return;               // machen
+            }
+            ::create(); ...
+          }
+      
+     Kosten:
+ *    - es lohnt, lokale Mappings oder Arrays mit bekannter Groesse via
+        allocate() oder m_allocate() vor Belegung in voller benoetiger
+        Groesse zu reservieren:
+        statt:
+          int *x = ({}); foreach(int i: 10) x+=({i});
+        lieber:
+          int *x = allocate(10); foreach(int i: 10) x[i] = i;
+ *    - wiederholtes Ausschneiden (slice) aus Arrays vermeiden, dabei wird
+        staendig Speicher neu alloziiert und benutzter Speicher freigegeben:
+        statt:
+          int *x; ...; while(sizeof(x)) { x[0]...; x=[1..x]; }
+        lieber:
+          int *x; ...; i=sizeof(x); while(j<i) { x[j]...; j++; }
+ *    - direkte Mapping/Array ({}), ([]) in Methoden (zB ueber #define)
+        sparen zwar globalen Platz, kosten aber Konstruktionszeit bei jedem
+        Aufruf dieser Methoden - fuer haeufig gerufene Methoden sollten
+        grosse Datenstrukturen einmal global konstruiert werden
+        statt:    #define GROSSES_MAPPING ([....])
+                  void haeufige_fun() { ... GROSSES_MAPPING ... }
+        lieber:   mapping GROSSES_MAPPING = ([....]);
+                  void haeufige_fun() { ... GROSSES_MAPPING ... }
+ *    - diverse efuns sind genauso schnell zugreifbar wie Variablen,
+        muessen also nur zugewiesen werden, wenn sich der Wert aendern kann:
+        this_player(), this_interactive(), environment(), previous_object(),
+        this_object().
+ *    - statt all_inventory() einer Variablen zuzuweisen und darueber
+        zu iterieren, kann man oft mit first_inventory() und next_inventory()
+        ein Inventory durchgehen
+
+     Methoden:
+      Die Methoden eines Objektes werden in einer Liste gespeichert, die
+      beim Aufruf einer Methode ueber call_other() (oder o->fun())
+      durchgesehen wird. Das hat folgende Konsequenzen:
+ *    - jede oeffentliche Methode wird bei call_other() durchsucht und
+        das kostet Zeit, wenn eine Methode also nicht oeffentlich sein
+        muss, dann schreibt auch ein "protected" davor, wenn sie in den
+        erbenden Klassen nicht sichtbar sein muss: "private"
+      - nutzt ihr eine fremde Methode mehrfach (zB QueryProp), dann ist es
+        an sehr kritischen Stellen sinnvoll, diese einmal zu suchen und an
+        eine Lfun-Closure zu binden, weitere Aufrufe sind schneller:
+         closure cl;
+         cl=symbol_function("QueryProp",this_player());
+         funcall(cl, P_LEVEL); funcall(cl, P_SIZE); ...
+      Nebenbei bemerkt:
+      - es gibt in LPC kein sog. fruehes Binden, "this_object()->function();"
+        ist fast immer unnoetig und fast immer nur ein Zeichen fuer Faulheit die
+        richtigen Prototypen zu inkludieren/formulieren.
+
+     Lambdas:
+      Lambda-Closures sind nicht nur schwer zu lesen, sondern oft auch langsamer
+      als andere Closures. Speziell wird bei jedem Auftreten von lambda() die
+      Lambda neu erzeugt.
+      Nehmt euch die Zeit aus einer Lambda-Closure eine Lfun-Closure zu
+      machen oder sie zumindest an eine globale Closure-Variable zu binden,
+      damits sie schnell ausgefuehrt werden kann. #define bietet sich hier
+      nicht an.
+      statt:  filter(users(),
+                       lambda(({'x}), ({#'call_other,'x,
+                                    "QueryProp",P_SECOND})));
+      lieber: private static int _isasec(object o) {
+                return o->QueryProp(P_SECOND);
+              }
+              ...
+              filter(users(), #'_isasec);
+      oder:   closure cl;
+              cl=lambda(({'x}), ... );
+              ...
+              filter(users(), cl);
+      oder:
+      Bessere Alternative zu Lambdas sind uebrigens inline-closures (man
+      inline-closures), die deutlich schneller und einfacher zu lesen sind.
+              filter(users(), function mixed (pl)
+                              {
+                                pl->QueryProp(P_SECOND);
+                              }
+                    );
+
+
+     Simul-efun und die Last der Vergangenheit:
+      Es gibt einige Simul-Efuns, die anstelle einer aehnlichen Efun verwendet
+      werden, aber langsamer sind. Beispiel: die sefun m_copy_delete() macht
+      fast das gleiche wie m_delete(), erzeugt aber vorher immer eine Kopie.
+      Wenn man diese nicht braucht, sollte man m_delete() den Vorzug geben.
+
+
+     Generelle Bemerkungen:
+ ***  - LAG entsteht vor allem dann, wenn zu viele Dinge auf einmal
+        identifiziert, bewegt, geladen, gecloned oder kopiert werden
+        sollen (in nur einem Kommando, in einem reset(), ...)
+        - zerlegt solche Aufgaben mit call_out/heart_beat in Haeppchen
+        - lasst es einen Erzmagier durchsehen
+ *    - Variablen sind immer auf 0 initialisiert,
+        allocate()-Arrays sind mit 0 oder Wunschwert initialisiert.
+      - gleicher Code sollte aus Schleifen sollten entfernt werden,
+        zB bei Iteration ueber ein Array gehoert das sizeof() vor die
+        Schleife, nicht in den Test
+ *    - beim Identifizieren eindeutiger Objekte ist present_clone()
+        wesentlich billiger als ein present() + geschuetzten IDs
+ *    - aus Arrays koennen mittels "-" viele identische Werte auf einmal
+        entfernt werden, es ist also sinnvoll bei Loeschoperationen
+        zu loeschende Werte auf einen bestimmten Wert zu setzen und diesen
+        dann mittels array-=({wert}) zu entfernen.
+        Wir entfernen alle getoeteten NPC, d.h. alle geloeschten Objekte
+        aus einer Liste: meinelistemitnpcs-=({0})
+      - efuns sind oft schneller als eigene Konstrukte, gerade was
+        Arrays betrifft. Pauschalisiert kann das nicht werden, man muss
+        auch immer die noetige Reservierung von Speicher mitbetrachten!
+        Zusammen mit einer Referenz sind sort_array(), filter(), map() etc.
+        dennoch oft euer Freund:
+        statt:   t=allocate(0);
+                 for (i=sizeof(a1); i--; )
+                   if (member(a2,a1[i])>=0) t+=({a1[i]});
+        lieber:  private static mixed _is_member(mixed x, a) {
+                   if (member(a,x)>=0) return 1;
+                   else return 0;
+                 }
+                 ...
+                 t=filter(a1, #'_is_member, &a2);
+        oder hier noch besser:
+                   t=a1&a2;
+      - x&y ist bei zwei grossen Arrays manchmal die schlechtere Wahl:
+        statt:   t=all_inventory(TO)&users();       // zwei Arrays
+        lieber:  t=filter(all_inventory(TO),        // ein Array!
+                          #'query_once_interactive);
+        Eventuell lohnt es sich hier, gleich mit first_inventory() und
+        next_inventory() ueber den Raum zu iterieren und auf allen
+        query_once_interactive() die gewuenschten Operationen vorzunehmen.
+      - foreach() ist oft gegenueber for() die bessere Alternative (etwas
+        schneller, einfacher formuliert)
+      - weitere schnelle efuns:
+        query_verb(), interactive(), query_once_interactive(), living(),
+        stringp(), intp(), closurep(), objectp(), ...
+
+ SIEHE AUCH:
+     memory, objekte, mudrechner, goodstyle, ticks
+
+ 6. Sep 2012 Gloinson
diff --git a/doc/concepts/erq b/doc/concepts/erq
new file mode 100644
index 0000000..378d59d
--- /dev/null
+++ b/doc/concepts/erq
@@ -0,0 +1,578 @@
+CONCEPT
+        erq - External Request Demon
+
+DESCRIPTION
+        Up to version 3.2.1@61, LPMud utilized two external programs
+        in an ad-hoc manner to solve problems: the 'hname' program to
+        resolve IP addresses into meaningful hostnames, and the
+        'indent' program to properly indent LPC files.
+        In version 3.2.1@61 both functions were united in a
+        generalized 'erq' process, to which additional functions may
+        be attached. Unfortunately it was never documented by Amylaar,
+        so the information presented here had to be reverse engineered
+        from the sources - better take it with a grain of salt.
+
+        The erq feature is available if the driver is compiled with
+        ERQ_DEMON defined (in config.h).
+
+        When the driver starts up, it tries to fork off the program
+        'BINDIR/erq --forked <other args>' (with BINDIR defined in
+        the Makefile). If this succeeds, the erq may talk with
+        the driver through stdin and stdout (piped through AF_UNIX
+        sockets). The erq has to signal its successfull start by
+        writing the character '1' back to the driver.
+
+        The erq has to understand these commandline arguments:
+
+          --forked: explained above
+          --execdir <dir>: The directory where the callable executables
+                    can be found. If not specified, ERQ_DIR is used.
+                    <dir> must not end in a '/' and should be absolute.
+
+        At runtime, the erq may be changed/removed from within the
+        mudlib using the efun attach_erq_demon(). This efun is given
+        an interactive object as argument, and takes the connection
+        away(!) from this object and stores it as the erq connection
+        to use (an old erq connection is closed first). The object
+        (which now no longer is interactive) is then no longer needed,
+        but may continue to exist.
+        The erq attached this way of course has to use the sockets it
+        opened to communicate with the driver.
+
+        Most of the communication between erq and driver is going to
+        be initiated by the driver (the erq has to look up the
+        hostnames for given IP addresses), but using the efun
+        send_erq() the mudlib may talk with the erq as well.
+
+        The communication between driver and erq is done using
+        messages of specified structures and constants (defined in
+        util/erq.h resp. sys/erq.h). The 'int32's are signed integers
+        of four byte length, and are sent with the MSByte first.
+        Every message must be sent atomically!
+
+        The head of the messages is always the same:
+
+          struct erq_msghead {
+            int32  msglen;  /* Total size of message in bytes */
+            int32  handle;  /* Identification number */
+          }
+
+        The 'handle' number is set by the driver (do not make
+        assumptions about its value) and is used to associated the erq
+        responses with the pending requests. This way the erq is free
+        to respond in an order different to those of the incoming
+        requests.
+
+        The messages send to the erq follow this symbolic format:
+
+          struct to_erq_msg {
+            int32  msglen;
+            int32  handle;
+            char   request;
+            char   data[0];
+          }
+
+        The 'request' denotes which service is requested from the erq,
+        the size and content of 'data' depends on the requested
+        service.
+
+        The answer message from the erq to the driver (if there is one
+        at all) may have two forms:
+
+          struct from_erq_msg {
+            int32  msglen;
+            int32  handle;
+            char   data[0];
+          }
+
+          struct from_erq_keep_msg {
+            int32        msglen;
+            const int32  keep = ERQ_KEEP_HANDLE;
+            int32        handle;
+            char         data[0];
+          }
+
+        The replied data from the erq is stored in 'data', which size
+        and content depends on the request answered. The answer is
+        identified by 'header.handle'. Normally, one request results
+        in just one response sent by the erq using struct from_erq_msg,
+        so the handle is recycled after this response.
+        Shall the erq send several responses (or break one response
+        into several parts), the struct from_erq_keep_msg has to be
+        used for all but the last response - this message with its
+        included special handle keeps the real handle alive.
+
+
+        Mudlib generated erq-calls specify the 'request' and the
+        'data' to be sent, and receive the 'data' replied. When
+        dealing with spawned programs, the first byte of the returned
+        'data' determines the content type of the received message.
+        The actual 'data' which the lpc programs get to see is sent
+        and retrieved as arrays of byte integers (integers in the
+        range of 0..255).
+
+
+        The actual interface between erq demon and driver is limited
+        to the general message formats and the hostname lookup
+        mechanism. The driver is meant to withstand erq demon failures
+        at least in a garbage-in garbage-out fashion. You could add
+        new requests to the erq demon, or write your own from scratch,
+        without changing the driver.
+
+
+        Currently five services are predefined in the supplied
+        erq-demon (util/erq.c in the driver source archive): looking
+        up a hostname, execution, forking or spawning an external
+        program, authentification of a connection, and handling of
+        external UDP/TCP connections. As mentioned above, only the
+        hostname-lookup is a true must.
+
+        For a program to be executable for erq, it must be placed in
+        or below ERQ_DIR (defined in config.h). On most unix systems,
+        it is possible to use a symlink instead of the whole program
+        if you want a standard binary. You could even symlink entire
+        directories like /usr/sbin, but chances are you make a big
+        security hole this way :-)
+
+
+        Hostname lookup:
+
+          request  : ERQ_RLOOKUP
+          data sent: struct in_addr.s_addr addr // the address to resolve
+          data recv: struct in_addr.s_addr addr // the resolved address
+                     char[]                name // the hostname (if any)
+
+          If the sent address can't be resolved, just the address is
+          to be returned. The string need not be 0-terminated.
+
+
+        Hostname lookup:
+
+          request  : ERQ_LOOKUP
+          data sent: char[]                name // the name to resolve
+          data recv: struct in_addr.s_addr addr // the resolved address
+
+          If the sent address can't be resolved, no data is returned (the
+          driver will get a message with just the header).
+
+
+        Hostname lookup - IPv6:
+
+          request  : ERQ_RLOOKUPV6
+          data sent: char[] addr  // the address to resolve
+          data recv: char[] data  // the resolved name
+
+          If the address could be resolved, the returned data is a string,
+          with exactly one space, in the form "<addr> <name>". <addr> is
+          the address passed to the erq, <name> is the hostname of the
+          address or, if there is no reverse-IPv6 entry for <addr>, the
+          IPv6 address which may or may not be different from <addr>.
+
+          If the address can not be resolved, the returned data is
+          an error message without a space (currently, just "invalid-format"
+          and "out-of-memory" are returned).
+
+
+        Execute/Fork program:
+
+          request  : ERQ_EXECUTE/ERQ_FORK
+          data sent: char[] command  // the command to execute
+          data recv: char   status = CHILD_FREE
+                     char   rc       // the success/error code
+                     char   info     // additional information
+
+          The erq executes the sent command using the execv().
+          The erq does the processing of the command line arguments
+          (which must not contain '\') and checks the validity of the
+          command (it must not start with '/' nor contain '..'), which
+          is interpreted relative to ERQ_DIR.
+          The external program is executed from a fork()ed instance of
+          the erq, however, with ERQ_EXECUTE the erq waits until the
+          external program finished before replying its response, with
+          ERQ_FORK the response is immediately sent back.
+
+          Possible return codes are:
+            ERQ_OK         : Operation succeeded.
+            ERQ_E_ARGLENGTH: Too long command.
+            ERQ_E_ARGFORMAT: Illegal argument given (contains '\');
+            ERQ_E_ARGNUMBER: Too much arguments (>= 96).
+            ERQ_E_ILLEGAL  : Command from outside ERQ_DIR requested.
+            ERQ_E_PATHLEN  : Commandpath too long.
+            ERQ_E_FORKFAIL : Command could not be forked;
+                             info holds the errno value.
+
+          ERQ_EXECUTE features some more return codes:
+            ERQ_OK         : Operation succeeded, <info> holds the exit status.
+            ERQ_SIGNALED   : Command terminated the signal <info>.
+            ERQ_E_NOTFOUND : No process found to wait() for.
+            ERQ_E_UNKNOWN  : Unknown exit condition from wait().
+
+
+        Spawn program:
+
+          request  : ERQ_SPAWN
+          data sent: char[] command  // the command to execute
+          data recv: Spawn failed:
+                     char   rc       // the error code (see ERQ_FORK)
+                     char   info     // additional information
+          data recv: Spawn succeeded:
+                     char   rc = ERQ_OK
+                     char[] ticket        // the spawn ticket.
+
+          The erq executes the sent command as if given an ERQ_FORK
+          command, but returns additional information about the
+          started process to allow further communication.
+          In contrast to ERQ_FORK, ERQ_SPAWNED processes may be
+          controlled via ERQ_KILL, receive data from the mud via
+          ERQ_SEND on their stdin, and output from their stdout/stderr
+          is sent back to the mud.
+          The spawned process is identified by its <ticket> (don't
+          make any assumptions about its length or content), the transaction
+          itself by <handle>.
+
+
+        Send data to spawned program:
+
+          request  : ERQ_SEND
+          data sent: char[]  ticket // the addressed process ticket.
+                     char[]  text   // the text to send.
+          data recv: char    rc     // the success/error code.
+                     int32   info   // opt: additional info.
+
+          The <text> is sent to the stdin of the spawned process
+          identified by <ticket>.
+
+          Possible return codes are:
+            ERQ_OK          : Operation succeeded, no <info> is replied.
+            ERQ_E_TICKET    : The given ticket is invalid, no <info> replied.
+            ERQ_E_INCOMPLETE: Only <info> chars of the text have been
+                              sent.
+                              If a callback is specified, the erq will send
+                              a ERQ_OK message once all data has been sent
+                              (this may never happen).
+            ERQ_E_WOULDBLOCK: Error E_WOULDBLOCK (also stored in <info>)
+                              happened while sending the text.
+            ERQ_E_PIPE      : Error E_PIPE (also stored in <info>)
+                              happened while sending the text.
+            ERQ_E_UNKNOWN   : The error with code <info> happened
+                              while sending the data.
+
+         Amylaar-erq doesn't try to re-send the remaining data after
+         a ERQ_E_INCOMPLETE, so there will never be an ERQ_OK.
+
+
+        Send a signal to a spawned program:
+
+          request  : ERQ_KILL
+          data sent: char[]  ticket // the addressed process ticket
+                     int32   signal // the signal to send
+          data recv: char    rc     // the success/error code
+
+          The <signal> is sent to the spawned process identified by <ticket>.
+
+          Possible return codes are:
+            ERQ_OK          : Operation succeeded, no <info> is replied.
+            ERQ_E_TICKET    : The given ticket is invalid, no <info> replied.
+            ERQ_E_ILLEGAL   : The given signal is illegal.
+
+
+        Data replies from spawned programs:
+
+          data recv: char   out_or_err  // type of text output
+                     char[] text        // text output by child process
+
+          The child process controlled by the erq did output <text>
+          on stdout (<out_or_err> == ERQ_STDOUT) resp. on stderr
+          (<out_or_err> == ERQ_STDERR).
+
+
+        Exit notifications from spawned programs:
+
+          data recv: char   rc          // the exit code
+                     char   info        // additional information.
+
+          The child process controlled by the erq did terminate.
+          Possible exit codes are:
+            ERQ_EXITED    : Process exited with status <info>.
+            ERQ_SIGNALED  : Process terminated by signal <info>.
+            ERQ_E_UNKNOWN : Process terminated for unknown reason.
+
+
+        Authentificate connection (see rfc 931):
+
+          request  : ERQ_AUTH
+          data sent: struct sockaddr_in remote // the address to check
+                     int32              port   // the mud port
+             or
+          data sent: int32  remote_ip    // remote ip to check
+                     int16  remote_port  // remote port to check
+                     int16  local_port   // the mud port
+
+          data recv: char[]             reply  // the data received by authd
+
+          The erq attempts to connect the authd on the remote system
+          and to verify the connection between the remote port and the
+          mud port. The latter will normally be the port number of the
+          socket on besides of the gamedriver, retrieveable by
+          query_ip_number().
+
+          The answer from the authd (one line of text) if there is any
+          is returned as result.
+
+          The second form of the ERQ_AUTH command is recognized by
+          the xerq as alternative.
+
+
+        Open an UPD port:
+
+          request  : ERQ_OPEN_UDP
+          data sent: char[2] port   // the port number to open (network order)
+          data recv: Open failed:
+                     char    rc     // the success/error code.
+                     char    info   // opt: additional info.
+          data recv: Open succeeded:
+                     char   rc = ERQ_OK
+                     char[] ticket  // the connection ticket.
+
+          The erq opens an UDP-port on the host machine with the given
+          port number.
+          Possible exit codes are:
+            ERQ_OK          : Operation succeeded.
+            ERQ_E_ARGLENGTH : The port number given does not consist
+                              of two bytes.
+            ERQ_E_NSLOTS    : The max number of child processes (given
+                              in <info>) is exhausted.
+            ERQ_E_UNKNOWN   : Error <info> occured in one of the system
+                              calls done to open the port.
+
+          Once the port is open, it is treated as if is just another
+          spawned program.
+
+
+        Send data over an UDP port:
+
+          request  : ERQ_SEND
+          data sent: char[]                ticket // the addressed port's ticket.
+                     struct in_addr.s_addr addr   // address of receiver.
+                     struct addr.sin_port  port   // port of receiver.
+                     char[]                text   // the text to send.
+          data recv: char   rc     // the success/error code.
+                     int32  info   // opt: additional info.
+
+          The <text> is sent from our port <ticket> to the network
+          address <addr>, port <port>.
+
+          Possible return codes are:
+            ERQ_OK          : Operation succeeded, no <info> is replied.
+            ERQ_E_TICKET    : The given ticket is invalid, no <info> replied.
+            ERQ_E_INCOMPLETE: Only <info> chars of the text have been
+                              sent. The erq will send a ERQ_OK message
+                              once all data has been sent.
+            ERQ_E_WOULDBLOCK: Error E_WOULDBLOCK (also stored in <info>)
+                              happened while sending the text.
+            ERQ_E_PIPE      : Error E_PIPE (also stored in <info>)
+                              happened while sending the text.
+            ERQ_E_UNKNOWN   : The error with code <info> happened
+                              while sending the data.
+
+
+        Close an UDP port:
+
+          request  : ERQ_KILL
+          data sent: char[]  ticket // the addressed port's ticket
+                     int32   signal // the signal to send (ignored)
+          data recv: char    rc = ERQ_OK
+
+          The port <ticket> is closed. The <signal> must be sent, but
+          its value is ignored.
+
+
+        Data received over an UDP connection:
+
+          data recv: char                  out_or_err = ERQ_STDOUT
+                     struct in_addr.s_addr addr    // ip-address of sender
+                     struct addr.sin_port  port    // port of sender
+                     char[]                text    // data received
+
+          The UPD port controlled by the erq did receive <text> over
+          the network from the sender at <addr>, reply port number <port>.
+
+
+        Open an TCP to listen for connections:
+
+          request  : ERQ_LISTEN
+          data sent: struct addr.sin_port port   // the port number to open
+          data recv: Open failed:
+                     char    rc     // the success/error code.
+                     char    info   // opt: additional info.
+          data recv: Open succeeded:
+                     char   rc = ERQ_OK
+                     char[] ticket  // the connection ticket.
+
+          The erq opens an TCP-port on the host machine with the given
+          port number to listen for connections.
+          Possible exit codes are:
+            ERQ_OK          : Operation succeeded.
+            ERQ_E_ARGLENGTH : The port number given does not consist
+                              of two bytes.
+            ERQ_E_NSLOTS    : The max number of child processes (given
+                              in <info>) is exhausted.
+            ERQ_E_UNKNOWN   : Error <info> occured in one of the system
+                              calls done to open the port.
+
+          Once the port is open, it is treated as if is just another
+          spawned program.
+
+
+        Open an TCP port:
+
+          request  : ERQ_OPEN_TCP
+          data sent: struct in_addr.s_addr ip   // the ip to address
+                     struct addr.sin_port  port // the port to address
+          data recv: Open failed:
+                     char    rc     // the success/error code.
+                     char    info   // opt: additional info.
+          data recv: Open succeeded:
+                     char   rc = ERQ_OK
+                     char[] ticket  // the connection ticket.
+
+          The erq opens an TCP-port on the host machine and tries to connect
+          it to the address <ip>:<port>.
+          Possible exit codes are:
+            ERQ_OK          : Operation succeeded.
+            ERQ_E_ARGLENGTH : The port number given does not consist
+                              of two bytes.
+            ERQ_E_NSLOTS    : The max number of child processes (given
+                              in <info>) is exhausted.
+            ERQ_E_UNKNOWN   : Error <info> occured in one of the system
+                              calls done to open the port.
+
+          Once the port is open, it is treated as if is just another
+          spawned program.
+
+
+        Send data over a TCP connection:
+
+          request  : ERQ_SEND
+          data sent: char[]  ticket // the addressed process ticket.
+                     char[]  text   // the text to send.
+          data recv: char    rc     // the success/error code.
+                     int32   info   // opt: additional info.
+
+          The <text> is sent to the stdin of the spawned process
+          identified by <ticket>.
+
+          Possible return codes are:
+            ERQ_OK          : Operation succeeded, no <info> is replied.
+            ERQ_E_TICKET    : The given ticket is invalid, no <info> replied.
+            ERQ_E_INCOMPLETE: Only <info> chars of the text have been
+                              sent. The erq will send a ERQ_OK message
+                              once all data has been sent.
+            ERQ_E_WOULDBLOCK: Error E_WOULDBLOCK (also stored in <info>)
+                              happened while sending the text.
+            ERQ_E_PIPE      : Error E_PIPE (also stored in <info>)
+                              happened while sending the text.
+            ERQ_E_UNKNOWN   : The error with code <info> happened
+                              while sending the data.
+
+
+        Data ready to read on TCP connection:
+
+          data recv: char    out_or_err = ERQ_OK
+                     char[]  ticket  // ticket of this connection
+
+          There is data available to read on the specified TCP connection.
+
+
+        Data received over a TCP connection:
+
+          data recv: char    out_or_err = ERQ_STDOUT
+                     char[]  text    // data received
+
+          The TCP port controlled by the erq did receive <text>.
+
+
+        TCP connection closes on error:
+
+          data recv: char    out_or_err = ERQ_E_UNKNOWN
+                     char    errno   // errno from socket operation
+
+          The TCP connection caused an error <errno> and has been closed.
+
+
+        TCP connection closed:
+
+          data recv: char    out_or_err = ERQ_EXITED
+
+          The TCP connection closed regularily (End Of File).
+
+
+        Connection pending on TCP socket:
+
+          data recv: char    out_or_err = ERQ_STDOUT
+
+          The TCP 'listen' port controlled by the erq received
+          a connection request.
+
+
+        Accept a pending connections:
+
+          request  : ERQ_ACCEPT
+          data sent: char[]  ticket // the ticket of this socket
+          data recv: Accept failed:
+                     char    rc     // the success/error code.
+                     char    info   // opt: additional info.
+          data recv: Accept succeeded:
+                     char    rc = ERQ_OK
+                     struct in_addr.s_addr ip    // remote side's ip
+                     struct addr.sin_port  port  // remote side's port
+                     char[]                ticket  // the new ticket.
+
+          The erq accepts a new connection on an accept-TCP-port, creates
+          an child and ticket for it and returns its ticket together with
+          the remote's side <ip>:<port> number (in network byte order).
+          Possible exit codes are:
+            ERQ_OK          : Operation succeeded.
+            ERQ_E_ARGLENGTH : The port number given does not consist
+                              of two bytes.
+            ERQ_E_NSLOTS    : The max number of child processes (given
+                              in <info>) is exhausted.
+            ERQ_E_TICKET    : the ticket didn't match
+            ERQ_E_UNKNOWN   : Error <info> occured in one of the system
+                              calls done to open the port.
+
+          Once the port is open, it is treated as if is just another
+          spawned program.
+
+
+EXAMPLE
+        Assume you have a script 'welcome-mail' to send a welcome mail
+        to a new player. Put this script into the directory for the callable
+        executables, then you can use it like this:
+
+        void erq_response(mixed * data)
+        {
+            write_file( "WELCOMELOG"
+                      , sprintf("rc %d, info %d\n", data[0], data[1]));
+        }
+
+        void send_mail(string player_name, string player_email)
+        {
+            send_erq( ERQ_EXECUTE
+                    , "welcome-mail '"+player_name+"' '"+player_email+"'"
+                    , #'erq_response);
+        }
+
+
+HISTORY
+        The erq was introduced with 3.2.1@61.
+        ERQ_AUTH was introduced with 3.2.1@81.
+        ERQ_SEND, ERQ_SPAWN, ERQ_KILL were introduced with 3.2.1@82.
+        ERQ_OPEN_UDP, ERQ_OPEN_TCP, ERQ_LIST were introduced with 3.2.1@98.
+        ERQ_RLOOKUPV6 was introduced in 3.2.8.
+        LDMud 3.2.9 added the '--execdir' argument to erq, and the ERQ_OK
+          after ERQ_E_INCOMPLETE protocol.
+
+SEE ALSO
+        attach_erq_demon(E), send_erq(E), stale_erq(M), rfc 931
+        query_ip_number(E)
diff --git a/doc/concepts/files b/doc/concepts/files
new file mode 100644
index 0000000..56e6e64
--- /dev/null
+++ b/doc/concepts/files
@@ -0,0 +1,16 @@
+CONCEPT
+        files
+
+DESCRIPTION
+        As a wizard, you are working with files. Each file represents
+        the bulding plan for one or more objects (except text or doc files
+        of course).
+
+        The mudlib has a root, and when working with filenames, you
+        can always specify a full pathname from the root by starting
+        with a '/' (slash) at the beginning of the file name.
+
+        (oops, truncated - why when where did it happen?)
+
+SEE ALSO
+        objects(C), create(A), reset(A)
diff --git a/doc/concepts/goodstyle b/doc/concepts/goodstyle
new file mode 100644
index 0000000..15ea04b
--- /dev/null
+++ b/doc/concepts/goodstyle
@@ -0,0 +1,74 @@
+Guter Stil
+ BESCHREIBUNG:
+     Guten Stil kann man lernen. Zumindest in der Programmierung. Guter Stil
+     bedeutet vor allem: Schreib es so, das andere es lesen und verstehen
+     koennen. (Ansonsten werde /secure/-Erzmagier, die muessen aufgrund
+     eingebauter Paranoia selbstverschluesselnd schreiben.)
+
+     Lernen kann man auch am Beispiel, unter /d/gebirge/room/,
+     /d/gebirge/obj/, /d/gebirge/mon/, /doc/beispiele/ ist sauberer Code
+     zu finden.
+
+     Tipps zum Aussehen:
+     - Programmzeilen nicht laenger als 80 Zeichen schreiben, denn 80 Zeichen
+       breite Fenster sind immer noch die Regel
+       - Code kann auf der naechsten Zeile weiterfuehren:
+         filter(all_inventory(environment(TP)),
+		      #'einetollesortierfunktion, &extravariablen);
+       - Strings koennen (ohne Addition mit +) unterbrochen werden:
+         "Jemand "<EOL>    "jammert" == "Jemand jammert"
+         "Jemand \<EOL>jammert"      == "Jemand jammert"
+
+     - Bloecke (mit {} umrandeter Code) einruecken, damit man den
+       geplanten Programmfluss gut erkennen kann
+       - 2 bis 4 Zeichen, nicht gleich ein ganzes Tab (siehe erste Regel)
+       - die { und } ruhig in einzelen Zeilen schreiben
+
+     - Variablen in dem Block deklarieren, in dem sie gebraucht werden,
+       dadurch sind sie schneller zu finden
+
+     - #define nicht uebertreiben, wenn komplexe Funktionen damit gebaut
+       sind, uebersieht der Leser den Code oft nicht mehr
+       - #define sollten in #includeten Headerdateien stehen
+       - wenn es eine oft benutzte Funktion ist, schreib sie als Lfun
+       - ist es vielleicht schon in /sys/*.h oder /secure/*.h definiert?
+
+     Tipps zum Code:
+     - objektorientiert programmieren
+       - das was andere nicht von aussen sehen oder aufrufen muessen, mit
+         "protected" oder "private" bei Vererbung verstecken
+
+     - return mitten im Code wenn moeglich vermeiden, da der Programmfluss
+       damit aufgesplittert wird - ein einziger Funktionsausgang ist
+       uebersichtlicher
+       - Ausnahme hiervon kann aber sein, (die meisten) Ausschlussbedingungen
+         fuer irgendwas am Anfang einer Funktion abzupruefen und die Funktion
+         dann auch sofort zu verlassen.
+      
+     - korrekte Typen bei Variablen und Typen verwenden, damit der Leser
+       erkennt welches Ding was ist
+       - #pragma strong_types oder gar #pragma strict_types hilft
+       - Auch Typpruefungen zur Laufzeit (#pragma rtt_checks) verwenden
+       - bei Objekten, die geerbt werden, immer auch  #pragma save_types
+
+     Tipps zu Dateien:
+     - unterteilt eure Gegenden am besten in verschiedene Verzeichnisse,
+       dann findet man sich schnell zurecht:
+       - NPCs, Objekte, Raeume, Master (ggf. Waffen, Ruestungen wenn zu viel)
+
+     - Pfade sollten in einer zentralen #include Datei stehen, welche dann
+       relativ #included werden kann. Damit erleichtert man spaeteren Magiern
+       eventuell noetige Verzeichnisaenderungen.
+       statt:	AddItem("/d/ebene/<magier>/sumpf/npc/schleimi", ...);
+       lieber:
+         #include "../local.h"
+           [enthaelt ein
+            #define SUMPFNPC(x) ("/d/ebene/<magier>/sumpf/npc/" x)]
+		     ...
+		     AddItem(SUMPFNPC("schleimi"), ...);
+
+ SIEHE AUCH:
+     inheritance, effizienz, pragma, oop
+
+05.06.2014, Zesstra
+
diff --git a/doc/concepts/hooks b/doc/concepts/hooks
new file mode 100644
index 0000000..108da08
--- /dev/null
+++ b/doc/concepts/hooks
@@ -0,0 +1,144 @@
+CONCEPT
+        driver hooks
+
+DESCRIPTION
+        To allow a greater flexibility of the muds, the gamedrivers
+        since 3.2.1 moved several once hardcoded 'underground'
+        activities from the driver into the mudlib. This includes for
+        example the differences between compat and native mode.
+
+        The hooks are set with the privileged efun set_driver_hook().
+        Some of the hooks are mandatory, some not. Most hooks accept
+        unbound lambda closures as values, some also lfun closures or
+        even strings.
+        The hooks are identified by an ordinal number, for which
+        symbolic names are defined in /sys/driverhooks.h.
+
+        H_MOVE_OBJECT0
+        H_MOVE_OBJECT1
+          Mandatory hooks to implement the efun void move_object().
+
+
+        H_LOAD_UIDS
+        H_CLONE_UIDS
+          Mandatory hooks to determine the uid and euid of loaded or cloned
+          objects.
+
+
+        H_CREATE_SUPER
+        H_CREATE_OB
+        H_CREATE_CLONE
+          Optional hooks to initialize an object after creation.
+
+          H_CREATE_SUPER is called for blueprints implicitely loaded
+          by inheritance, H_CREATE_OB for explicitely loaded
+          blueprints/objects, and H_CREATE_CLONE for cloned objects.
+
+
+        H_RESET
+          Optional hook to reset an object.
+
+
+        H_CLEAN_UP
+          Optional hook to clean up an object.
+
+
+        H_DEFAULT_METHOD
+          Optional hook to provide default implementation for unresolved
+          calls.
+
+
+        H_DEFAULT_PROMPT
+          Optional hook for the command prompt. If this hook is not used,
+          the driver will use "> " as the command prompt.
+
+
+        H_PRINT_PROMPT
+          Optional hook to print the current command prompt. If this hook is
+          not set, the driver will just print the prompt to the user.
+
+
+        H_COMMAND
+          Optional hook to parse and execute commands. If this hook is used,
+          it bypasses the normal command parsing done by the driver (including
+          the MODIFY_COMMAND and NOTIFY_FAIL hooks).
+
+
+        H_MODIFY_COMMAND
+          Optional hook to modify commands (both entered or given by a
+          call to command()) before the parser sees them (this includes
+          special commands like 'status').
+
+
+        H_MODIFY_COMMAND_FNAME
+          Mandatory hook specifying the name of the 'modify_command'
+          lfun to call for newly entered commands as result of a
+          set_modify_command().
+
+
+        H_NOTIFY_FAIL
+          Mandatory hook to issue the default message if an entered
+          command couldn't be parsed and no notify_fail() command is
+          in effect.
+
+
+        H_SEND_NOTIFY_FAIL
+          Optional hook to send the notify fail message, regardless
+          of how it was determined, to the player. If the hook is not
+          set, the message is delivered using tell_object() internally.
+
+
+        H_NO_IPC_SLOT
+          Optional hook specifying the message given to logins
+          rejected due to space limitations (MAX_PLAYER).
+
+
+        H_INCLUDE_DIRS
+          Semi-mandatory hook specifying the directories where <>-type
+          include files are searched (this includes ""-includes not
+          found as specified).
+
+
+        H_AUTO_INCLUDE
+          Optional hook specifying a string to be included before
+          the source of every compiled LPC object.
+
+
+        H_TELNET_NEG
+          Optional hook to specifiy how to perform a single telnet
+          negotiation.  If not set, most telnet options are rejected (read:
+          only a very minimal negotiation takes place).
+
+
+        H_NOECHO
+          Optional hook to specifiy how to perform the telnet actions
+          to switch the echo mode (used for e.g. password input_to()s).
+          If not set, a default handling is performed.
+
+          IMPORTANT: If this hook is used, the control of all telnet
+          negotiation is transferred to the mudlib (you must combine it
+          with H_TELNET_NEG to conform to the telnet protocol).
+
+
+        H_ERQ_STOP
+          Optional hook to notify the mudlib about the termination of
+          the erq demon.
+
+
+HISTORY
+        The hooks concept was introduced in 3.2.1
+        H_MOVE_OBJECT0/1 were introduced in 3.2.1@1
+        H_CLEAN_UP was introduced in 3.2.1@34
+        H_MODIFY_COMMAND was introduced in 3.2.1@51.
+        H_MODIFY_COMMAND_FNAME was 'hooked' in 3.2.1@109.
+        H_NOTIFY_FAILE and H_NO_IPC_SLOT were introduced in 3.2.1@55.
+        H_INCLUDE_DIRS was introduced in 3.2.1@57.
+        H_TELNET_NEG was introduced in 3.2.1@60.
+        H_NOECHO and H_ERQ_STOP were introduced in 3.2.1@85.
+        H_COMMAND was introduced in 3.2.7.
+        H_SEND_NOTIFY_FAIL and H_AUTO_INCLUDE were introduced in 3.2.9.
+        H_DEFAULT_METHOD was introduced in 3.3.113.
+        H_DEFAULT_PROMPT and H_PRINT_PROMPT were introduced in 3.3.163.
+
+SEE ALSO
+        native(C), set_driver_hook(E), all in (H)
diff --git a/doc/concepts/hsregexp b/doc/concepts/hsregexp
new file mode 100644
index 0000000..04266be
--- /dev/null
+++ b/doc/concepts/hsregexp
@@ -0,0 +1,99 @@
+SYNOPSIS
+        Henry Spencer Regular Expressions
+
+
+DESCRIPTION
+        This document describes the regular expressions supported by the
+        implementation by Henry Spencer (the traditional package for
+        LPMud).
+
+
+OPTIONS
+        The following bitflag options modify the behaviour of the
+        regular expressions - both interpretation and actual matching.
+
+        The efuns may understand additional options.
+
+          RE_EXCOMPATIBLE
+
+        If this bit is set, the pattern is interpreted as the UNIX ed
+        editor would do it: () match literally, and the \( \) group
+        expressions.
+
+
+REGULAR EXPRESSION DETAILS
+        A regular expression is a pattern that is matched against  a
+        subject string from left to right. Most characters stand for
+        themselves in a pattern, and match the corresponding charac-
+        ters in the subject. As a trivial example, the pattern
+
+          The quick brown fox
+
+        matches a portion of a subject string that is  identical  to
+        itself.  The  power  of  regular  expressions comes from the
+        ability to include alternatives and repetitions in the  pat-
+        tern.  These  are encoded in the pattern by the use of meta-
+        characters, which do not stand for  themselves  but  instead
+        are interpreted in some special way.
+
+        There are two different sets of meta-characters: those  that
+        are  recognized anywhere in the pattern except within square
+        brackets, and those that are recognized in square  brackets.
+        Outside square brackets, the meta-characters are as follows:
+
+          .       Match any character.
+
+          ^       Match begin of line.
+
+          $       Match end of line.
+
+          \<      Match begin of word.
+
+          \>      Match end of word.
+
+          \B      not at edge of a word (supposed to be like the emacs
+                  compatibility one in gnu egrep)
+
+          x|y     Match regexp x or regexp y.
+
+          ()      Match enclosed regexp like a 'simple' one (unless
+                  RE_EXCOMPATIBLE is set).
+
+          x*      Match any number (0 or more) of regexp x.
+
+          x+      Match any number (1 or more) of regexp x.
+
+          [..]    Match one of the characters enclosed.
+
+          [^ ..]  Match none of the characters enclosed. The .. are to
+                  replaced by single characters or character ranges:
+
+          [abc]   matches a, b or c.
+
+          [ab0-9] matches a, b or any digit.
+
+          [^a-z]  does not match any lowercase character.
+
+          \c      match character c even if it's one of the special
+                  characters.
+
+
+NOTES
+        The \< and \> metacharacters from Henry Spencers package
+        are not available in PCRE, but can be emulate with \b,
+        as required, also in conjunction with \W or \w.
+
+        In LDMud, backtracks are limited by the EVAL_COST runtime
+        limit, to avoid freezing the driver with a match
+        like regexp(({"=XX==================="}), "X(.+)+X").
+
+
+AUTHOR
+        Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
+        Henry Spencer, University of Torronto (henry@utzoo.edu)
+        Joern Rennecke
+        Ian Phillipps
+
+
+SEE ALSO
+        regexp(C), pcre(C)
diff --git a/doc/concepts/imp b/doc/concepts/imp
new file mode 100644
index 0000000..27d9ab9
--- /dev/null
+++ b/doc/concepts/imp
@@ -0,0 +1,63 @@
+CONCEPT
+        imp
+
+LAST UPDATED
+        Deepthought, 10-Nov-92
+        Pepel,             18-Nov-93
+
+DESRIPTION
+        This document describes IMP, the intermud message protocol,
+        also known as Intermud-1.
+
+        Imp messages are exchanged between muds using UDP
+        (unreliable datagram protocol) packets. Each mud provides
+        a connection endpoint which is given by the ip host address
+        and the UDP port number. Muds may then send messages to
+        this port by using the efun send_udp(). The applied function
+        receive_udp will be called by the driver in the master
+        object if a imp message arrives at the mud's UDP port.
+
+        Imp message packets have the following format:
+
+        password@objectname@functionname[[@argument]...]
+
+        <password> is the connection password to verify incoming
+        imp packets. It is encoded using crypt(E) and compared to
+        the stored password. Each mud participating in the imp
+        network has a secret password which is encoded by the
+        admin and distributed to remote muds with which the mud
+        should have direct connection. Encryted passwords may also
+        propagated to other muds over already secure channels.
+
+        <objectname> is a logical name which is not to be confused
+        with mudlib object filenames. It is used by receive_msg in
+        the master object to route the message to another object by
+        associating the logical object name with a real mudlib file
+        name. A good idea would be to reserve a special directory
+        for imp objects, e.g. /secure/net/<objectname>.
+
+        <functionname> is the function which is called by the master
+        object in the object described by <objectname>.
+
+        <argument> are additional arguments which are handed to the
+        function <functionname>. The exact definition of functions
+        and arguments are left to the imp applications.
+
+AUTHOR
+        originally Deepthought
+
+NOTE
+        The above is only particularly correct nowadays. Recently a
+        package name ``inetd'' was published, that is based on the IMP
+        mechanism in the driver (send_udp() and receive_udp()), but
+        it uses a different message format. That package seems to
+        enjoy much publicity and is installed in a number of muds. For
+        details look into the inetd description.
+
+        An other method of inter mud connection is the Mudlink
+        package, which uses a normal user connection that is connected
+        to a special user object, and an auxiliary process that does
+        the connection to other muds.
+
+SEE ALSO
+        send_udp(E), receive_udp(M), intermud(C)
diff --git a/doc/concepts/inheritance b/doc/concepts/inheritance
new file mode 100644
index 0000000..46bf338
--- /dev/null
+++ b/doc/concepts/inheritance
@@ -0,0 +1,177 @@
+CONCEPT
+        Inheritance
+
+DESCRIPTION
+        Have you noticed how many objects in the system have the same
+        functionality in common? Let's look at rooms for instance, they
+        all have the ability to host people and provide commands. It's
+        not that every room is programmed with the same basic functions
+        again and again, rather it will use a model room and then make
+        some special changes to it. That doesn't work by copying the
+        file.. Ouch! Don't replicate code! But by putting a tiny inherit
+        declaration
+        
+                inherit "<model-class>";
+                
+        at the beginning of your new file. This must come before any local
+        ariables or functions. Once inherited your class will behave just
+        like the model class, because all the public methods are available
+        to the outside world. Now it is in your hands to change such an
+        inherited behaviour. You have the following tools to do so:
+
+        * Access to variables
+
+        It is one of the best design decisions in LPC that variables
+        are not accessible from outside, but you can use inherited
+        variables just as if they were your own. Modifiers apply however.
+
+        * Method overloading
+
+                int method_that_also_exists_in_the_model() {
+                        <your new code>
+                }
+
+        You can simply rewrite a method that is also defined in the model
+        class, and thus change how it behaves. Contrary to other languages
+        in LPC method overloading only matches the name of the method, so
+        even by changing the amount and type of parameters you will mask
+        out the original version of the method. You can even apply other
+        modifiers to it as the original.
+
+        * Calling inherited methods
+
+                int method_that_also_exists_in_the_model() {
+                        <your new code>
+                        return ::method_that_also_exists_in_the_model();
+                }
+
+        You can add to the behaviour of a method by redefining it,
+        then calling it from within your new version. You can actually
+        call inherited methods from anywhere in your code. The double
+        colon tells the compiler you are looking for the inherited
+        variant.
+
+EXAMPLE
+
+        Let's imagine very simple food in a file called "/the/food.c":
+
+                // unless "modified" variables are accessible by inheritors
+                int vitamins = 10;
+
+                // please overload this function with your own description
+                public short() { return "something edible"; }
+
+                // let's do some standard action for food
+                public consume() {
+                        this_player() -> nourish(vitamins);
+                        destruct(this_object());
+                }
+
+        And now someone else decides to do some italian cooking in a
+        file called "/the/fusilli.c"
+
+                inherit "/the/food";
+
+                // we have our own variables.
+                int gone_cold = 0;
+
+                // and we simply redefine the short() function to replace it
+                public short() {
+                    // description changes depending on gone_cold
+                    return "a "+( gone_cold ? "stinking" : "steaming" )
+                            +" plate of fusilli";
+                }
+
+                // we have a new function to make food go cold
+                private deteriorate() {
+                    gone_cold = 1;
+                    write("The fusilli have gone cold.\n");
+                }
+
+                // assume this gets called at creation
+                private create() {
+                    // we can access the variable we inherited from food.c
+                    vitamins = 44;           // tomato has plenty of vitamins
+
+                    // go cold in 5 minutes
+                    call_out( #'deteriorate, 5 * 60 );
+                }
+
+                // we can overload the function even with new parameters
+                public consume(how) {
+                    // fetch the name of the person, or use "Someone"
+                    string name = this_player() -> name() || "Someone";
+
+                    if (!gone_cold) {
+                        write("You enjoy a delicious plate of fusilli.\n");
+                        say(name +" guzzles a plate of hot fusilli.\n");
+                    }
+                    else if (how == "quickly") {
+                        write("You eat the fusilli so quickly you "
+                              "hardly notice they have gone cold.\n");
+                        say(name +" wolfs down a plate of cold fusilli.\n");
+                    }
+                    else {
+                        write("You eye the plate and wonder if you "
+                              "really feel like eating cold fusilli.\n");
+                        return; // don't eat
+                    }
+
+                    // and here comes the most important part:
+                    // we execute consume() from food.c, so we
+                    // actually inherit its behaviour.
+                    ::consume();
+                }
+
+ADVANCED USAGE
+
+        * Doing multiple inheritance
+
+        While the Java(TM) language has so-called interfaces as a kludge,
+        LPC doesn't need them as it supports real multiple inheritance.
+        A very powerful feature, it lets you combine the behaviour of
+        several classes into a new one. Simply put several lines of
+        inherit declarations underneath each other. If you have name
+        collisions in the namespace of inherited methods, you will have
+        to address them explicitely with a "the/file"::method(args) syntax.
+
+        * Wildcarded multiple inheritance
+
+        LDMUD 3.2.1@117 introduces an advanced voodoo syntax which allows
+        you to call several methods in model classes at once, but for some
+        technical reasons it cannot pass any arguments. This works by
+        writing a glob type match ('*' and '?' wildcards) into the string
+        in front of the double colon, as in "*"::create(). I wouldn't
+        recommend you to use this, it's better to be clearly conscious of
+        what you inherit and do. But if you're desperate, there you go.
+
+ADVANCED EXAMPLE
+
+          inherit "foo";
+          inherit "bar";
+          inherit "baz";
+          inherit "ball";
+
+          reset() {
+                "ba?"::reset();
+                // calls bar::reset() and baz::reset()
+
+                "ba*"::reset();
+                // calls bar::reset(), baz::reset() and ball::reset()
+
+                "*"::reset();
+                // calls every inherited reset() function.
+
+                "ball"::rejoice("Listen to italectro today!");
+                // only explicit filename of model class allows
+                // passing arguments to the inherited method
+          }
+
+AUTHOR
+        symlynX of PSYC and Nemesis, with a little help from Someone
+
+SEE ALSO
+        functions(LPC), initialisation(LPC), modifiers(LPC), pragma(LPC),
+        overloading(C)
+        function_exists(efun), functionlist(efun), inherit_list(efun),
+        symbol_variable(efun), variable_exists(efun), variable_list(efun).
diff --git a/doc/concepts/intermud b/doc/concepts/intermud
new file mode 100644
index 0000000..abe2b4f
--- /dev/null
+++ b/doc/concepts/intermud
@@ -0,0 +1,224 @@
+CONCEPT
+        intermud
+
+DESCRIPTION
+        There are several intermud protocols which define how (players on)
+        different muds can communicate with each other. The protocols are
+        in general not muddriver or mudlib dependant, though the number of
+        implementations is limited.
+
+        This text is about the rather old widely spread 'Zebedee Intermud',
+        which is also called 'Intermud 2' altough it differs quite a lot
+        from the real Intermud 2 protocol.
+
+        Full information on the newer Intermud 3 could be found on the
+        web at http://www.imaginary.com/protocols/intermud3.html so there
+        is no discussion here - the following is just about Zebedee Intermud
+        (aka Intermud 2).
+
+        Zebedee Intermud communication is handled by the /secure/inetd
+        object, originally written by Nostradamus for Zebedee with some
+        extensions that are discussed in inetd(C). How the data is
+        actually sent across the network is described in intermud.basic(C).
+
+SERVICES
+        Note that the fields "NAME" and "UDP_PORT" should be present in
+        every message. Very common are the fields "ID" (used whenever an
+        reply is expected) and "SND" (the sender: he should receive the
+        reply). These fields will not be mentioned in the list below.
+
+        Request types are listed on the leftmost row (e.g. "REQ=channel"),
+        associated header are listed indented.
+
+        "channel"
+            The channel-request is used for sending a message on any
+            channel. The "CMD" field is optional and may be omitted for
+            normal messages. Note that you should not send an history or
+            list request to _all_ known muds!
+
+            "CHANNEL"
+                The channel on which a message is send (the standard
+                channels are "intermud", "intercode", "interadm", "d-chat",
+                "d-code" and "d-adm"; on the d-channels German is spoken)
+
+            "DATA"
+                The message to be send (not used with history/list request)
+
+            "CMD"
+                The body of this header may be:
+                ""        for normal intermud messages,
+                "emote"   if the message is an emote/gemote,
+                "history" for an history request: the last 20 lines of
+                          this channel will be shown.
+                "list"    to list all remote users listening to this channel
+
+            "EMOTE"     (optional)
+                The body is 1 if the message is an emote.
+                The body is 2 if the message is a gemote.
+
+        "finger"
+            Retreive information about a player or creator on a remote mud.
+
+            "DATA"
+                The player of whom information is requested
+
+        "locate"
+            Check whether a certain player is logged on at a remote mud.
+            This request is usually send to all known muds at the same time.
+
+            "user"
+                Name of the person who requests the information.
+                This is used by the sending mud only and has to be included
+                in the reply.
+
+            "vbs"
+                The verbose option has only two pre-defined values:
+                1 Even report when the result was negative
+                2 Don't do timeouts, but keep waiting
+                This is used by the sending mud only and has to be included
+                in the reply.
+
+            "fnd"
+                The found option is only used in the reply and it's value
+                is either 1 (success) or 0 (failure). The absence of a
+                found parameter indicates failure as well.
+
+            "DATA"
+                The player to find.
+
+        "man"
+            Retreive a manual page from a remote mud. Many muds don't
+            support this feature...
+
+            "DATA"
+                The name of the requested manual page
+
+        "mail"
+            An extension to the standard protocol, by Alvin@Sushi. This is
+            used to send mails from one mud to another.
+
+            "udpm_status"
+                This field should only be used in the reply and indicates
+                how mail is handled. Currently there are four pre-defined
+                values for the status field:
+                0 time out
+                1 delivered ok
+                2 unknown player
+                3 in spool (will be delivered later)
+
+            "udpm_writer"
+                Name of the person who wrote this mail
+
+            "udpm_spool_name"
+                Should be returned as sent, this value is used to remove
+                the mail from the spool directory after it has been
+                delivered (or refused)
+
+            "udpm_subject"
+                Subject of the mail message
+
+            "DATA"
+                The body of the mail (the actual message)
+
+        "ping"
+            A ping request has only the standard fields, the reply is
+            usually a short string like " is alive."
+
+        "query"
+            Get standard information about another mud. This is the only
+            command of which the reply may not include a load of rubbish,
+            but should only hold the requested information, so that it can
+            be parsed by the server.
+
+            "DATA"
+                The following queries are pretty much standard:
+                "commands" List all commands that are supported by the inetd
+                "email"    The email-address of the mud administrator(s)
+                "hosts"    A listing of all hosts in a special format [t.b.d.]
+                "inetd"    The version number of the inetd used
+                "list"     The list of all items which can be queried
+                "info"     A short human-readable string with practically
+                           "query" information
+                "mud_port" The portnumber that players connect to on login
+                "time"     The local time for this mud
+                "users"    A list of the people that are active in this mud
+                "version"  The version of the mud-driver (and library)
+                "www"      The URL of the mud's web page (e.g.
+                           http://mud.stack.nl/)
+
+        "reply"
+            This request method is used for _all_ replies.
+
+            "DATA"
+                A human-readable string, containing the reply to a given query
+
+            "RCPNT"
+                The same name as in the "SND" field or the query; Usually
+                this is the name of the player who initiated the query
+
+            "QUERY"
+                This field is only used in a response to a "query" request
+                and should be equal to the "DATA" field of that request
+
+            "vbs"
+                This field is only used in a response to a "locate" request
+                and should be equal to the "vbs" field of that request
+
+            "user"
+                This field is only used in a response to a "locate" request
+                and should be equal to the "user" field of that request
+
+            "fnd"
+                This field is only used in a response to a "locate" request
+                and should be 1 if the player was located and 0 otherwise
+
+        "tell"
+            Say something to a player on another mud.
+
+            "RCPNT"
+                Name of the player to whom you are talking
+
+            "DATA"
+                Whatever you wish to say to this person
+
+            Optional emote-tos are handles are also handled as tells, so
+            muds without emote-to support display them as reasonable readable
+            tell message.
+
+            "RCPNT"
+                Name of the player to whom you are talking
+
+            "METHOD"
+                The body of this header may be:
+                "emote"   if the message is an emote
+                "gemote"  if the message is a genitiv emote
+
+            "DATA"
+                The text to be emoted prepended with "*" and appended
+                with "* ". If you display the emote you have to cut the
+                stars off. Muds that do not process emote-tos display the
+                emote as tell message with the stars as indication of
+                the message's emote meaning.
+
+        "who"
+            List the people that are active on a remote mud. The anwer
+            usually contains some active information about the players,
+            like titles, levels or age.
+
+            "DATA"
+                Not supported by many muds. Introduced August 1997.
+                Additional switch(es) (blanc separated) that change the
+                appearence of the resulting list. The switches normally
+                resemble the switches used inside of that mud for the 'who'
+                command.  Typical values include:
+                "short" "s" "-short" "-s" "kurz":
+                    Return a concise listing.
+                "alpha" "a" "alphabetisch" "-alpha" "-a"
+                    Sort the players alphabetically.
+
+AUTHOR
+        Information taken from Outerspaces documentation to be found
+        on http://mud.stack.nl/intermud/
+
+SEE ALSO
+        inetd(C), intermud.basic(C), imp(C)
diff --git a/doc/concepts/intermud.basic b/doc/concepts/intermud.basic
new file mode 100644
index 0000000..901967f
--- /dev/null
+++ b/doc/concepts/intermud.basic
@@ -0,0 +1,151 @@
+CONCEPT
+        intermud.basic
+
+DESCRIPTION
+        Here is how intermud data is sent across the internet - specific
+        for Zebedee Intermud (aka Intermud 2).
+
+ADVANCED PROTOCOL
+        This file was originally written as a brief outline of the intermud
+        protocol for use by developers interested in incorperating similar,
+        compatible intermud protocols into their own mud systems. It is
+        included here as it provides a much more detailed description of the
+        intermud protocol than that provided by the original PROTOCOL file,
+        and hence may be of use to LpMud developers. 
+
+PACKET PROTOCOL / FORMAT
+        All information is transferred as a string via a UDP port (each mud
+        has 1 send and 1 receive port). This kindof transfer is inherently
+        unreliable, but it's fast and doesn't use up file descriptors.
+        The format of the strings (packets) is as follows: 
+
+           header1:body1|headerN:bodyN|DATA:body-data
+
+        In other words, a header name, followed by a : and then the data
+        associated with this header. Each header/body pair is separated by
+        the | character. This means that headers and their body cannot
+        contain the | character. You should check for this in outgoing
+        packets to aviod decoding errors at the recieving end. The exception
+        to this is the DATA field. If it is present, it is ALWAYS positioned
+        at the end of the packet. Once a DATA header is found, everything
+        following it is interpreted as the body of the DATA field. This
+        means it can contain special characters without error and it is
+        used to carry the main body or data of all packets. 
+
+        By convention, predefined system fields will use capital letters for
+        field headers and custom headers used by specific applications will
+        use lowercase names to avoid clashes. The defined system fields are
+        generally refered to by a set of macros which are defined in a
+        common header file for clarity. 
+
+        There is one exception to this header format; If the data is too
+        large to be transmitted in one single packet, it will be split into
+        packets of convenient size, each with a special unique packet header
+        to enable them to be reassembled at the receiving end. These
+        headers are of the format: 
+
+          PKT:mudname:packet-id:packet-number/total-packets|rest-of-packet
+
+        In this case, the mudname and packet-id combine to form a unique id
+        for the packet. The packet-number and total-packets information is
+        used to determine when all buffered packets have been received. The
+        rest-of-packet part is not parsed, but is stored while the receiver
+        awaits the other parts of the packet. When/if all parts have been
+        received they are concatenated and decoded as a normal packet. 
+
+PACKET ENCODING / DECODING
+        Only 2 generic data types are fully suported within the inetd code
+        itself (namely strings and integers), though others can easily be
+        used by converting them to one of the supported data types before
+        transfer and converting back again in receipt. The LpMud "object"
+        data type is converted to a string automatically by the inetd on
+        encoding, but no such conversion is carried out on decoding. 
+
+        On encoding integers are simply converted to a corresponding string.
+        Strings are left untouched as long as there is no ambiguity as to
+        wether they should be decoded as a string or an integer. In this
+        case of ambiguity, the string is prepended with a $ character. If
+        the first character of a string is the $ character, it is escaped
+        by prepending another $ character. On decoding, any string with a $
+        as its first character will have it removed and will then be treated
+        as a string. Any remaining strings that can be converted to an
+        integer and then back to a string with no loss of information are
+        considered to be integers. Any remaining strings are treated as
+        such and are left unaltered. 
+
+DEFINED SYSTEM HEADERS
+        "RCPNT" (RECIPIENT)
+            The body of this field should contiain the recipient the message
+            is to be sent to if applicable. 
+        "REQ" (REQUEST)
+            The name of the intermud request that is being made of the
+            receiving mud. Standard requests that should be supported by
+            all systems are "ping" (PING), "query" (QUERY), and "reply"
+            (REPLY). The PING request is used to determine wether or not a
+            mud is active. The QUERY request is used to query a remote mud
+            for information about itself (look at the udp/query module for
+            details of what information can be requested). The REPLY request
+            is special in that it is the request name used for all replies
+            made to by mud B to an initial request made by a mud A. It is
+            mud A's responsibility to keep track of the original request
+            type so that the reply can be handled appropriately. 
+        "SND" (SENDER)
+            The name of the person or object which sent the request or to
+            whom replies should be directed. This is essential if a reply
+            is expected. 
+        "DATA" (DATA)
+            This field should contain the main body of any packet. It is
+            the only field that can contain special delimiting characters
+            without error. 
+
+        The following headers are used internally by the inetd and should
+        not be used by external objects: 
+        "HST" (HOST)
+            The IP address of the host from which a request was received.
+            This is set by the receiving mud and is not contained in
+            outgoing packets. 
+        "ID" (ID)
+            The packet id. This field is simply an integer which is set by
+            the sending inetd. The number is incremented each time a packet
+            is sent (zero is never used). This field is only needed if a
+            reply is expected. REPLY packets _must_ include the original
+            request id. This is _not_ done by the inetd. 
+        "NAME" (NAME)
+            The name of the local mud. Used for security checking and to
+            update host list information. 
+        "PKT" (PACKET)
+            A special header reserved for packets which have been split.
+            See PACKET PROTOCOL / FORMAT. 
+        "UDP" (UDP_PORT)
+            The UDP port the local mud is receiving on. Used for security
+            checking and updating host list information. 
+        "SYS" (SYSTEM)
+            Contains special system flags. The only system flag used at
+            present is TIME_OUT. This is included in packets returned due
+            to an expected reply timing out to differentiate it from an
+            actual reply. 
+
+UDP REQUESTS / MODULES
+        The following are standard request types that must be supported
+        by all systems: 
+        "ping" (PING)
+            This module should return a REPLY packet that contains the
+            original requests ID in it's ID field and the SENDER in it's
+            RECIPIENT field. It should also include an appropriate string
+            in the DATA field, eg. "Mud-Name is alive.\n" 
+        "query" (QUERY)
+            This module expects the type of query requested to appear in the
+            recieved DATA field. It should return a REPLY packet containing
+            the original ID in the ID field, the SENDER in it's RECIPIENT
+            field, and the query type in a QUERY field. The DATA field should
+            contain the information requested. 
+
+        For details of how other intermud requests operate, look at the
+        relevant module code. 
+
+AUTHOR
+        Information taken from Outerspaces documentation to be found 
+        on http://mud.stack.nl/intermud/
+
+SEE ALSO
+        inetd(C), intermud(C)
diff --git a/doc/concepts/lpc b/doc/concepts/lpc
new file mode 100644
index 0000000..1e5911e
--- /dev/null
+++ b/doc/concepts/lpc
@@ -0,0 +1,64 @@
+* What is LPC?
+
+LPC is the language in which LPmud objects are written.
+LPC stands for Lars Pensj| C.  As one might surmise from the name,
+LPC is based on the syntax of C.  LPC provides the C while loop, for loop,
+if statement, switch statement, a variant of sscanf, and integer data type,
+(LPC also provides other data types not in C such as the object and the
+mapping).  LPC uses C's syntax for defining and calling functions and for
+declaring variables.  Note that LPC's version of the string datatype is
+much different from that provided by C.  See the LPC tutorial on syntax
+and language constructs for more information.
+
+Here are some differences between LPC and C:
+
+There is no need for a function named "main" in LPC objects (although there
+is one called "create").
+
+The efuns (or system calls) provided by the gamedriver are different than
+those typically found in the C library (libc.a).
+
+There is no malloc().  However, there is an allocate(int value) efun that
+lets space be allocated for arrays.  Note that the argument to 'allocate'
+is not in units of bytes, but rather in units of elements.
+
+Memory is never explicitly deallocated.  The gamedriver keeps track of
+how many times a given piece of data has been referenced.  When the
+reference count goes to zero (when no object has a copy of that variable),
+then the space used by the variable is reclaimed (garbage collected).
+
+The string data type in LPC is closer to that provided by BASIC than that
+provided by C.  Strings are not declared as arrays of characters but rather
+as a basic intrinsic type.  Strings may be concatenated using the '+' operator.
+
+For example, the LPC statements:
+
+string ack;
+
+ack = foo + bar;
+
+are equivalent to the C statements:
+
+char *ack;
+
+ack = (char *)malloc(strlen(foo) + 1);
+strcpy(ack,foo);
+ack = (char *)realloc(strlen(ack) + strlen(bar) + 1);
+strcat(ack,bar);
+
+Note: ack[i] may not appear as an lvalue (i.e. ack[i] = 'a'; will not
+work as expected).
+
+LPC is an interpreted language (however it is compiled into an internal
+compact tokenized form before being interpreted).
+
+LPC has no structures or unions.  In fact, the -> operator is used to
+indicate a call to another object.  The mapping datatype can serve
+as an effective substitute for structures in some situations.
+
+sscanf does not work in the same way as in C.  arguments to sscanf need not
+be pointers (since LPC does not have the explicit pointer data type).  Also,
+sscanf(arg,"%s %s",str1,str2) does not operate as the C programmer would
+expect.  In C, the first word of arg would be copied into str1 and the
+second word of arg into str2.  In LPC, the first word is copied into str1
+and the _remainder_ of arg is copied into str2.
diff --git a/doc/concepts/mail b/doc/concepts/mail
new file mode 100644
index 0000000..c08508c
--- /dev/null
+++ b/doc/concepts/mail
@@ -0,0 +1,73 @@
+CONCEPT:
+	mail
+
+
+DESCRIPTION:
+	This document describes the mail system used in Nightfall.
+	The idea is to make a central mail handling object which
+	accepts and distributes mail between users. Mail is stored in
+	the /mail directory. save_object is used to save mail
+	information in mail files in this directory. Only the mail
+	demon object and the owner of the mail file can access it.
+
+	A number of mail readers will probably available which access
+	the mail files. A typical mail user agent has commands to
+	read mail contained in the user's mail file, to reply to
+	messages, to forward, delete, save them. A folder structure
+	can be implemented. Outgoing mail is given to the mail demon
+	object by the user agent for distribution. The mailreader
+	should implement multiple recipients - carbon copy, cc and
+	blind carbon copy, bcc. Carbon copy means alternate recipients
+	to which the message should be sent. Blind carbon copy is the
+	same, but the recipients won't be listed in the received
+	message.
+
+	Save file format (sort of formal notation):
+
+	mixed *folders = ({
+	   ({ string name1; string name2; ... string nameN; })
+	   ({ mixed *msgs1; mixed *msgs2; ... mixed *msgsN; })
+	})
+
+	The array variable <folders> contains a number of folder
+	structures containing the actual messages. There are special
+	folders which are reserved: mail, newmail. New mail will
+	be delivered into the newmail folder. This is the only hard
+	coded requirement (the mail demon will simply deposit new
+	mail there). The folder name 'mail' should be used for read
+	mail. Other folders can be dynamically created by the user
+	agent.
+
+	Each msgs field is an array of messages:
+
+	mixed *msgs = ({ mixed *message1; ... mixed *messageM })
+
+	A message is represented as an array with the following fields:
+
+	mixed *message = ({
+	   string from;
+	   string sender;
+	   string recipient;
+	   string *cc;
+	   string *bcc;
+	   string subject;
+	   string date;
+	   string id;
+	   string body;
+	})
+
+	The mailer demon (/secure/mailer, or /obj/mailer) provides
+	the following functions:
+
+	DeliverMail(mixed *message)
+	  Hand a mail message over to the mailer demon. The mailer
+	  demon extracts recipients from the recipient, cc and bcc
+	  fields and removes the bcc information. It then deposits
+	  the message to the mail files of all recipients. A valid
+	  message is shown above.
+
+	int FingerMail(string user)
+	  Gives the number of unread messages a user has.
+
+
+SEE ALSO:
diff --git a/doc/concepts/mccp b/doc/concepts/mccp
new file mode 100644
index 0000000..f3e31fb
--- /dev/null
+++ b/doc/concepts/mccp
@@ -0,0 +1,100 @@
+CONCEPT
+        mccp - The Mud Client Compression Protocol
+
+DESCRIPTION
+        Informations and code taken from the MCCP Homepage
+        http://www.randomly.org/projects/MCCP/
+
+        MCCP is implemented as a Telnet option [RFC854, RFC855]. The server
+        and client negotiate the use of MCCP as they would any other telnet
+        option. Once agreement has been reached on the use of the option,
+        option subnegotiation is used to determine acceptable compression
+        methods to use, and to indicate the start of a compressed data stream. 
+
+        If the driver is compiled with MCCP Support there is a
+        define __MCCP__.
+
+        The driver currently supports both versions of mccp. If your mud
+        has a H_NOECHO hook you have to find out if the client supports
+        mccp. Without this hook you still have to start neogotiation.
+
+        All sub-negotiation is done by the efuns start_mccp_compress() and
+        end_mccp_compress() whether you have this hook or not.
+
+        Notice: when the client uses compressions all binary_message calls
+                are executed with flag=3. This is because writing to the
+                socket would disturb zlib stream.
+        
+        mccp-efuns:
+
+          start_mccp_compress(int telopt) (only needed with H_NOECHO)
+          end_mccp_compress(int telopt)   (only needed with H_NOECHO)
+          query_mccp(object player)
+          query_mccp_stats(object player)
+
+       Initiating MCCP without H_NOECHO hook:
+
+          if(!query_mccp()){
+            binary_message(({ IAC, WILL, TELOPT_COMPRESS2 }),1)
+            binary_message(({ IAC, WILL, TELOPT_COMPRESS }),1)
+          }
+
+          the driver will parse the clients answers and start compression.
+          (The connection might already be compressed, because although the
+           documentation says clients should not negotiate from themselfes,
+           zmud e.g. does.)
+
+          You can start and stop compression manually by efuns
+          when you are sure client supports compression :)
+
+
+       Initiating MCCP compression with H_NOECHO hook:
+
+          If your mudlib uses the H_NOECHO driver-hook you decided to do
+          all the negotiation by yourself:
+
+          Server Commands
+          IAC WILL COMPRESS indicates the sender supports version 1 of the
+                            protocol, and is willing to compress data it sends. 
+
+          IAC WILL COMPRESS2 indicates the sender supports version 2, and is
+                             willing to compress data it sends. 
+
+          IAC WONT COMPRESS indicates the sender refuses to compress data using
+                            version 1. 
+
+          IAC WONT COMPRESS2 indicates the sender refuses to compress data
+                             using version 2. 
+
+          Client Commands
+          IAC DO COMPRESS indicates the sender supports version 1 of the
+                          protocol, and is willing to decompress data received. 
+
+          IAC DO COMPRESS2 indicates the sender supports version 2 or above,
+                           and is willing to decompress data received. 
+
+          IAC DONT COMPRESS indicates the sender refuses to support version 1.
+                            If compression was previously negotiated and is
+                            currently being used, the server should terminate
+                            compression. 
+
+          IAC DONT COMPRESS2 indicates the sender refuses to support version 2.
+                             If compression was previously negotiated and is
+                             currently being used, the server should terminate
+                             compression
+
+          After you found out whether the client supports mccp or not you can
+          start compression with start_mccp_compress(TELOPT_COMPRESS2) or
+          start_mccp_compress(TELOPT_COMPRESS). ( you could start it without
+          checking but some players would protest :) )
+
+AUTHOR
+        Bastian Hoyer (dafire@ff.mud.de) (some text taken from project page)
+
+HISTORY
+        Added in LDMud 3.3.447, backported to LDMud 3.2.10.
+
+SEE ALSO
+        start_mccp_compress(E), end_mccp_compress(E), query_mccp(E),
+        query_mccp_stats(object player)
+
diff --git a/doc/concepts/memory b/doc/concepts/memory
new file mode 100644
index 0000000..9524eb3
--- /dev/null
+++ b/doc/concepts/memory
@@ -0,0 +1,56 @@
+CONCEPT
+        memory
+        swapping
+
+DESCRIPTION
+
+        TODO: This is out of date. Also document the relation with reset
+
+        (Collected from the Changelogs of the driver source)
+
+        The swapping algorithm has been changed. A test is done for
+        every object, comparing to a time stamp. If the object hasn't
+        been touched for a while, it could be subject for swapping.
+        Here comes the new thing: the function 'clean_up()' will be
+        called in the object. If the object still remains, the old
+        swapping algorithm will continue. That means that objects that
+        would never be subject to swapping (cloned objects) now have a
+        chance to self-destruct. It also means that rooms that
+        contains no important data can self-destruct. Self-destruction
+        saves more memory than swapping, as swapping only frees the
+        program code, while self-destruction also frees the internal
+        object representation.
+
+        The call of clean_up() has been modified. There is a constant
+        in config.h that defines how long time until clean_up is
+        called in an object. This call is independent of reset() and
+        swapping. It is recommended that the swapping time is
+        something short, like 10 minutes to 30 minutes, while the time
+        to clean_up is longer.
+
+        Fixed several bugs in the swap/reset/clean_up logic.
+        Recommended values are that the swap time is short (less than
+        30 minutes), and that reset time is medium (aprox 60 minutes),
+        and that time to clean_up is long (greater than 1.5h hours).
+        Any feedback of how to best tune these values are welcome. The
+        call of reset will be done once, and not yet again until the
+        object has been touched. This enables reset'ed objects to stay
+        swapped out. If you have a mudlib that has no ojbects that
+        defines 'clean_up', then you may better define this time as 0,
+        which means never call clean_up (and thus never swap the
+        object in needlessly). A well implemented usage of clean_up is
+        better than the swap algorithm, as even cloned objects can be
+        cleaned up and a self destruction is more efficient than
+        swapping (memory wise).
+
+        Changed mechanism of calling clean_up() slightly. Only objects
+        that defines the function will be called. And, only clean_up()
+        that returns non-zero will be called again. This will minimize
+        calls of clean_up(), while still cost very litte to maintain.
+
+        clean_up() now gets a flag as argument, which will be non-zero
+        if the the program of this object is used for inheritance by
+        other objects.
+
+SEE ALSO
+        clean_up(A), slow_shut_down(M), quota_demon(M), malloc(D)
diff --git a/doc/concepts/mysql b/doc/concepts/mysql
new file mode 100644
index 0000000..4fb6dee
--- /dev/null
+++ b/doc/concepts/mysql
@@ -0,0 +1,205 @@
+CONCEPT
+            mysql - mySQL support
+
+DESCRIPTION
+        On hosts with the mySQL package installed, the driver can be
+        configured to interface with the mySQL database. If that is done,
+        the driver defines the macro __MYSQL__ for LPC programs and
+        activates a number of efuns.
+
+        -- Configuration --
+
+        Create a dedicated user in the mySQL database for the driver.
+        Enter this username and password in the file pkg-mysql.c, function
+        mysql_real_connect(), and compile the driver (the username and
+        password are built into the driver for security reasons).
+        If you chose to not create either a username and/or a password,
+        leave the corresponding entry at 0.
+
+        Use mysqladmin to create any databases you want to provide - the
+        names are later used in the efun db_connect() to connect to
+        the databases.
+
+
+        -- Usage --
+
+        The idea behind SQL-support is that you can swap large amounts of
+        data into a database where it can be accessed very easily.
+        As mySQL "limits" the number of connections to 100 and as every
+        connection to the mySQL-server takes time, you should use
+        database serverobjects in your MUD which constantly keep the
+        connection to the mySQL-server.
+
+        To connect to your mySQL-server, use the efun db_connect(). It
+        takes only one argument which is the name of the database (which
+        must exist).  The return-value of db_connect() is an integer
+        representing the unique handle to the database with which you will
+        identify your connection later.
+
+        To send or retrieve data from this connection, use db_exec(). The
+        first parameter for all efuns dealing with an open connection is
+        always the handle and so is the first argument the handle and the
+        second one the command you want to issue. The return-value is
+        either 0 if there was an error in your command (this can have
+        various reasons), otherwise your handle is returned again. A typical
+        SQL-statement to retrieve data would be like this:
+
+                select aliases.command from aliases where (name = 'mario' AND
+                  alias regexp 'l.*')
+
+        As you know, mySQL accepts either " or ' to classify strings for
+        parameters.  Most likely, you will pass variables and don't know
+        whether they contain one or more of these key-chars (or even other
+        chars that need to be converted). mySQL provides a function for
+        converting just any string into an acceptable argument and this is
+        implemented in db_conv_string().
+
+        So the above example with variables looks like this:
+
+                select aliases.command from aliases where (name ='"+
+                  db_conv_string(name)+"' AND alias regexp '"+
+                  db_conv_string(mask)+"')
+
+        I left out the db_exec()-stuff, more complete examples will follow.
+
+        After you initiated a statement that should return rows from the
+        database, use db_fetch() to retrieve the data. db_fetch() returns
+        the data row by row and not all at once. You need to call it until
+        it returns 0. THIS IS IMPORTANT! If stop calling db_fetch() before
+        it reaches the end of data, serious inconsistencies can happen.
+
+        If you used a DELETE- or UPDATE-statement, you cannot call db_fetch(),
+        but you might be interested in the number of deleted/changed rows
+        which can be queried with db_affected_rows().
+
+        After all operations are done in the database, you should use
+        db_close() to close the connection again. If you are using a
+        database-server-concept, place it in the remove()-function.
+
+        The SQL-efuns have some built-in optimization-features to speed up
+        often used connections. To get a list of all open connections to the
+        mySQL-server, use db_handles() which returns an array of integers
+        with all open handles.
+
+
+        -- Security --
+
+        Most SQL efuns (unless execute by the master or the simul-efun object)
+        trigger a privilege_violation ("mysql", "<efun_name>"). If a more
+        finegrained control is desired, overload the individual efuns with a
+        nomask simul-efun.
+
+        The unprivileged efuns are:
+
+          db_conv_string()
+
+
+EXAMPLE
+        A simple server to store aliases could be implemented like this:
+
+        /*
+        **  CREATION:
+        **
+        **  create table aliases (
+        **      name varchar(15) not NULL,
+        **      alias varchar(20) not NULL,
+        **      command varchar(255) not NULL,
+        **      primary key (name, alias));
+        */
+
+        #define DATABASE "mud"
+
+        private int handle;
+
+        public void create()
+        {
+            handle = db_connect(DATABASE);
+        }
+
+        public int remove()
+        {
+            if ( handle )
+                db_close(handle);
+            destruct(ME);
+            return !ME;
+        }
+
+        public int AddAlias(string alias, string command, object ob)
+        {
+            if ( !handle )
+                handle = db_connect(DATABASE);
+            if ( !db_exec(handle,
+                          "insert into aliases (name, alias, command) values "
+                          "('" + getuid(ob) + "','" + db_conv_string(alias)
+                               + "','"+
+                          db_conv_string(command) + "')") )
+                return -1;
+            return 1;
+        }
+
+        public int RemoveAlias(string alias, object ob)
+        {
+            int res;
+
+            if ( !handle )
+                handle = db_connect(DATABASE);
+            res = db_exec(handle,
+                          "delete from aliases where (name = '"+
+                          getuid(ob) + "' AND alias = '"
+                                     + db_conv_string(alias)+
+                          "')");
+            if ( !res )
+                return 0;
+            res = db_affected_rows(handle);
+            return (res > 0)?1:-1;
+        }
+
+        public mixed *QueryAliases(string mask, object ob)
+        {
+            mixed *result;
+            string *tmp;
+
+            if ( !handle )
+                handle = db_connect(DATABASE);
+            if ( !db_exec(handle,
+                          "select aliases.alias, aliases.command from aliases where "
+                          "(name = '" + getuid(ob)+
+                          "' AND alias regexp '" + db_conv_string(mask) + "')") )
+                return ({ });
+            result = ({ });
+            while ( sizeof(tmp = db_fetch(handle)) )
+                result += ({ tmp });
+            return result;
+        }
+
+        public string QueryAlias(string alias, object ob)
+        {
+            mixed *result;
+            string *tmp;
+
+            if ( !handle )
+                handle = db_connect(DATABASE);
+            if ( !db_exec(handle,
+                          "select aliases.command from aliases where "
+                          "(name = '" + getuid(ob)+
+                          "' AND alias = '" + db_conv_string(alias) + "')") )
+                return 0;
+            result = ({ });
+            while ( sizeof(tmp = db_fetch(handle)) )
+                result += tmp;
+            return sizeof(result)?result[0]:0;
+        }
+
+
+AUTHOR
+        Mark Daniel Reidel and others.
+
+HISTORY
+        mySQL support was added as a package in 3.2.8 and became and
+        integral driver part in 3.2.9.
+        LDMud 3.2.11 added a privilege_violation() call for each efun.
+
+SEE ALSO
+        pgsql(C), db_affected_rows(E), db_conv_string(E), db_close(E),
+        db_connect(E), db_exec(E), db_fetch(E), db_handles(E),
+        db_insert_id(E), db_coldefs(E), db_error(E), privilege_violation(A)
diff --git a/doc/concepts/native b/doc/concepts/native
new file mode 100644
index 0000000..13cf008
--- /dev/null
+++ b/doc/concepts/native
@@ -0,0 +1,170 @@
+CONCEPT
+        driver modes / native driver mode
+
+DESCRIPTION
+        During the evolution of LPMud there has been a hiatus as the
+        old driver became too restricting for the demands of modern
+        muds: it did a lot of things the mudlib could do better or
+        completely different. Removing these things from the driver
+        weren't a problem, but to keep compatible with the existing
+        mudlibs (namely the well-known 2.4.5 lib), it was possible to
+        undo these changes. First by setting a runtime option, then
+        by compiling the driver either in 'compat' or in 'native' mode.
+
+        Starting with 3.2.1, the distinction between compat and native
+        mode is more and more transferred into the mudlib, with the
+        future goal of having a modeless driver.
+
+        Starting with 3.2.7, native mode no longer exists as such,
+        only 'plain' (quasi a superset of 'native' and 'compat')
+        and 'compat' mode, and since 3.2.9 the mode selection can be
+        made via commandline option.
+
+        The main mode of the driver is determined at compile time
+        by preprocessor symbols to be defined/undefined in config.h:
+
+          COMPAT_MODE: when defined, the compat mode specifics are activated
+                       by default.
+
+        Additional modifications can be achieved by the specification
+        of commandline arguments (most of them have a default setting
+        entry in config.h as well):
+
+          strict-euids: when active, euid usage is enforced.
+          compat:       when active, the compat mode is used.
+
+        Following is the description of the changes (de) activated by
+        these defines. A shorthand notation is used: 'compat' means
+        'if compat mode is active' and '!compat' means 'if
+        compat mode is not active', etc.
+
+
+        Predefined Preprocessor Symbols
+          If compat, the symbols COMPAT_FLAG and __COMPAT_MODE__ are
+          defined for all LPC programs.
+          If strict-euids, the symbol __STRICT_EUIDS__ is defined
+          for all LPC programs.
+          For compatibility reasons, the symbol __EUIDS__ is defined
+          for all LPC programs all the time.
+
+
+        Preloading Of Objects
+          The driver has the possibility to preload objects before the
+          game is actually opened to the world. This is done by
+          calling master->epilog(), which has to return 0 or an array.
+          If its an array, its elements (as long as they are strings)
+          are given one by one as argument to master->preload() which
+          may now preload the objects (or do anything else).
+
+
+        Initialisation Of Objects
+          It is task of the mudlib (through the driver hooks) to call
+          the initialisation lfuns in newly created objects. The
+          following table shows the traditional calls:
+
+                  mode        : init call : reset call
+           --------------------------------------------
+            !compat & !native :  create() :  reset(1)
+            !compat &  native :  create() :  reset()
+             compat & !native :  reset(0) :  reset(1)
+             compat &  native :  reset(0) :  reset(1)
+
+          If INITIALIZATION_BY___INIT was defined, the lfun __INIT()
+          is called first on creation to initialize the objects
+          variables.
+
+
+        Movement Of Objects
+          The efun move_object() is implemented in the mudlib through
+          driver hooks and the set_environment() efun.
+          move_object() itself exists just for convenience and
+          compatibility.
+
+          In original native mode, move_object() could applied only to
+          this_object() as the object to move, and it called the lfun
+          exit() in the old environment if in compat mode. As a side
+          effect, the lfun exit() may not be target of add_action()s
+          in compat mode.
+
+          In compat mode, objects may be moved using the transfer()
+          efun. It does make assumptions about the design of the
+          mudlib, though, as it calls the lfuns query_weight(),
+          can_put_and_get(), get(), prevent_insert() and add_weight().
+
+
+        Efuns In General
+          creator(), transfer()
+            These exist only in compat mode (creator() is
+            identical with getuid()).
+
+          object_name(),function_exists()
+            In !compat mode, the returned filenames start with a
+            leading '/', in compat mode they don't.
+
+          parse_command()
+            This command exists in two versions: the old is used with
+            compat, the new with !compat. However,
+            SUPPLY_PARSE_COMMAND must be defined in config.h in both
+            cases (this efun is not very useful at all).
+
+          process_string()
+            If this_object() doesn't exist, it defaults to this_player()
+            and receives the backbone uid (returned by master->get_bb_uid())
+            as euid. If strict-euids, this uid must not be 0.
+
+        Userids and Effective Userids
+          This is probably the most important difference between the
+          modes.
+
+          LPMud always had userids (uids) attributing the objects,
+          though they were called 'creator names' in compat mode.
+          Internally, the compat mode uses the 'creator names' as
+          (e)uid.
+
+          With the introduction of native/plain mode, additionally
+          'effective userids' (euids) were introduced to improve
+          security handling (which was only a partial success).
+          The hardcoded handling of euids and uids was quite complex
+          and too mudlib-insensitive, so most of it got moved from the
+          driver into the mudlib with 3.2.1.
+
+          In strict-euids mode, only objects with a non-zero euid may load
+          or create new objects.
+
+          --- In Detail ---
+
+            Userids of the Master
+              The masters (e)uid is determined by a call to
+              master->get_master_uid().
+              In strict-euids mode, the result has to be a string,
+                otherwise the driver won't start up at all. If the result is
+                valid it is set as the masters uid and euid.
+              In !strict-euids mode, the result may be any value: 0 or a
+                string are treated as the uid to set, a non-zero integer
+                leads to the use of the uid set in the default 'global'
+                wizlist entry, and any other value defaults to 0.
+                The euid is either set to the returned string (if any),
+                or to 0.
+              The masters uid is determined only on startup this way,
+              at runtime the uids of a reloaded master determined as
+              for every object by a call to the appropriate driver
+              hooks.
+
+            Userids of New Objects
+              To determine the (e)uids for a new object (loaded or
+              inherited, or cloned), the appropriate driver hook is
+              evaluated (H_LOAD_UIDS, H_CLONE_UIDS) and the result set
+              as (e)uid. The result may be a single value, in which case the
+              euid is set to 0, or an array ({ uid, euid }).
+              In strict-euids mode, both uid and euid must be 0 or a string,
+                any other value causes the load/clone to fail.
+              In !strict-euids mode, the uid (however returned) may also be
+                a non-zero integer to use the uid of the global
+                wizlist entry as uid. The euid is then
+                set to either 0 or the second entry of the returned
+                array if it's a string.
+
+          --- ---
+
+SEE ALSO
+        hooks(C), uids(C), move_object(E), initialisation(LPC)
diff --git a/doc/concepts/negotiation b/doc/concepts/negotiation
new file mode 100644
index 0000000..ed2318f
--- /dev/null
+++ b/doc/concepts/negotiation
@@ -0,0 +1,316 @@
+CONCEPT
+        Telnet Negotiations
+
+DESCRIPTION
+        The telnet protocol is used to control textbased connections
+        between a client (the 'telnet' program or a mud client) and a
+        server (the game driver). Most of the options offered by the
+        protocol are optional and need to be negotiated between the
+        client and the server.  Consequently, and due to their
+        specialized nature, mud clients don't have to support the full
+        telnet option feature set.
+
+        For the server to find out if a client supports the telnet
+        protocol at all, one good approach is to a simple, commonly
+        used telnet command to the client. If the client reacts
+        conform to the protocol (or sends telnet commands itself), the
+        mud can continue to negotiate further options. If the client
+        does not react, the mud can safely refrain from further
+        negotiations.
+
+        The following list is a more or less comprehensive overview of
+        the telnet related RFCs (available for example on
+        http://www.faqs.org/rfcs):
+
+	 RFC Titel                                              rel. Code
+
+	 495 TELNET Protocol Specification
+	 513 Comments on the new TELNET specifications
+	 559 Comments on the new TELNET Protocol and its Implem
+	 595 Some Thoughts in Defense of the TELNET Go-Ahead
+	 596 Second Thoughts on Telnet Go-Ahead
+	 652 Telnet Output Carriage-Return Disposition Option   NAOCRD     10
+	 653 Telnet Output Horizontal Tabstops Option           NAOHTS     11
+	 654 Telnet Output Horizontal Tab Disposition Option    NAOHTD     12
+	 655 Telnet Output Formfeed Disposition Option          NAOFFD     13
+	 656 Telnet Output Vertical Tabstops Option             NAOVTS     14
+	 657 Telnet Output Vertical Tab Disposition Option      NAOVTD     15
+	 658 Telnet Output Linefeed Disposition                 NAOLFD     16
+	 698 Telnet Extended Ascii Option                       X-ASCII    17
+	 727 Telnet Logout Option                               LOGOUT     18
+	 728 A Minor Pitfall in the Telnet Protocol
+	 735 Revised TELNET Byte Macro Option                   BM         19
+	 749 Telnet SUPDUP-OUTPUT Option                        SUPDUP     22
+	 764 Telnet Protocol Specification
+	 779 Telnet SEND-LOCATION Option                        SENDLOC    23
+	 818 The Remote User Telnet Service
+	 854 Telnet Protocol Specification
+	 855 Telnet Option Specifications
+	 856 Telnet Binary Transmission                         BINARY      0
+	 857 Telnet Echo Option                                 ECHO        1
+	 858 Telnet Suppress Go Ahead Option                    SGA         3
+	 859 Telnet Status Option                               STATUS      5
+	 860 Telnet Timing Mark Option                          TM          6
+	 861 Telnet Extended Options - List Option              EXOPL     255
+	 884 Telnet Terminal Type Option                        TTYPE      24
+	 885 Telnet End of Record Option                        EOR        25
+	 930 Telnet Terminal Type Option                        TTYPE      24
+	 933 Output Marking Telnet Option                       OUTMRK     27
+	 946 Telnet Terminal Location Number Option             TTYLOC     28
+	1043 Telnet Data Entry Terminal Option DODIIS Implement DET        20
+	1053 Telnet X.3 PAD Option                              X.3-PAD    30
+	1073 Telnet Window Size Option                          NAWS       31
+	1079 Telnet Terminal Speed Option                       TSPEED     32
+	1080 Telnet Remote Flow Control Option                  FLOWCTRL   33
+	1091 Telnet Terminal-Type Option                        TTYPE      24
+	1096 Telnet X Display Location Option                   XDISPLOC   35
+	1116 Telnet Linemode Option                             LINEMODE   34
+	1143 The Q Method of Implementing TELNET Option Negotia
+	1184 Telnet Linemode Option                             LINEMODE   34
+	1372 Telnet Remote Flow Control Option                  FLOWCTRL   33
+	1408 Telnet Environment Option                          ENVIRON    36
+	1571 Telnet Environment Option Interoperability Issues
+	1572 Telnet Environment Option                          NEWENV     39
+	2066 Telnet Charset Option                              CHARSET    42
+	2217 Telnet Com Port Control Option                     COMPORT    44
+	2877 5250 Telnet Enhancements
+
+        All negotiations start with the special character IAC which is
+        defined in /usr/include/arpa/telnet.h (or in
+        src/driver/telnet.h for 3.2(.1)) and has the decimal value of
+        255. Negotiations are based on different telnetoptions (their
+        values are defined in telnet.h too). Before a negotiation can
+        start the client and the server have to agree that they
+        support the option.
+        This works in the following way:
+
+        If a client wants to send something to the server it has to
+        send 'IAC WILL option' (For terminaltype negotation this would
+        be the 3 bytes 255,251,24; again, check telnet.h) to confirm
+        that it is able to do that. If the server is supporting that
+        option and wants to receive something it sends 'IAC DO option'
+        (255,253,option)
+
+        If one side is receiving an 'IAC WILL option' and has not yet
+        sent with DO or DONT it has to respond with either 'IAC DO
+        option' if it will support this negotiation or 'IAC DONT
+        option' if it won't.
+
+        If one side is receiving an 'IAC DO option' and has not yet
+        sent a WILL or WONT it has to reply with either 'IAC WILL
+        option' if it supports the option or 'IAC WONT option' if not.
+
+        A small example: Lets assume we want to negotiating
+        terminaltype. (TELOPT_TTYPE with value 24). client is the
+        telnet executable on the playerside, the server is the
+        gamedriver.
+
+                client                        server
+            IAC WILL TTYPE
+                                    IAC DO TTYPE
+
+        Or:
+                                    IAC DO TTYPE
+            IAC WILL TTYPE
+
+        After this we are ready to transfer the terminaltype from the
+        client to the server as explained below.
+
+        Now we are ready to start the real negotiations. I explain the
+        3 options I have currently implemented.
+
+        First TerminalType aka TTYPE aka 24 aka TELOPT_TTYPE assuming
+        the client and the server have exchanged WILL/DO.
+
+        The server is now free to send 'IAC SB TELOPT_TTYPE
+        TELQUAL_SEND IAC SE' which will be replied with 'IAC SB
+        TELOPT_TTYPE TELQUAL_IS terminaltype IAC SE' where
+        terminaltype is a non-zero terminated string (it's terminated
+        by the IAC) (For values look up telnet.h) AND switch the
+        client's terminalemulation to 'terminaltype'. terminaltype is
+        case-insensitive. terminal-type may be UNKNOWN. The server may
+        repeat the SEND request and the client will respond with the
+        next preferred terminaltype. If this is the same as the
+        previous received, it marks the end of the list of
+        terminaltypes. The next SEND request will start the
+        terminaltypes from the beginning.
+
+        Example: (we have exchanged WILL/DO already)
+                  client                                server
+                                        IAC SB TTYPE SEND IAC SE
+        IAC SB TTYPE IS VT200 IAC SE
+                                        IAC SB TTYPE SEND IAC SE
+        IAC SB TTYPE IS VT100 IAC SE
+                                        IAC SB TTYPE SEND IAC SE
+        IAC SB TTYPE IS VT52 IAC SE
+                                        IAC SB TTYPE SEND IAC SE
+        IAC SB TTYPE IS VT52 IAC SE
+        /* this marks that we have all terminaltypes. We decide to use the
+         * vt200 mode so we have to skip to VT200
+         */
+                                        IAC SB TTYPE SEND IAC SE
+        IAC SB TTYPE IS VT200 IAC SE
+
+
+        Next important option is NAWS (31) or WindowSizeNegotiation.
+
+        This one is a bit easier than terminaltype. After having
+        received a IAC DO NAWS from the server, the client will reply
+        with IAC WILL NAWS and immediately after that send IAC SB NAWS
+        columns_high columns_low lines_high lines_low IAC SE where
+        xx_low refers to the lowbyte of xx and xx_high refers to the
+        highbyte of xx. This will be automagically resent at every
+        windowresize (when the client gets a SIGWINCH for example) or
+        at your request with 'IAC SB NAWS SEND IAC SE'.
+
+        Example: (WILL/DO exchanged)
+                client                                server
+        IAC SB NAWS 0 80 0 24 IAC SE         /* the standard vt100 windowsize */
+                                        /* no reply */
+
+        And, a bit less important but most complex, the LINEMODE (34)
+        option. It was implemented it due to the fact, that
+        some weird DOS telnets would not work otherwise. Implemented
+        are only the absolute basic feature, which is the actual
+        switching the telnet to linemode. After exchanging WILL/DO the
+        server sends a modechange request to the client using IAC SB
+        LINEMODE LM_MODE MODE_EDIT IAC SE, which should turn on local
+        commandline-editing for the client. If a client supports
+        LINEMODE it HAS to support this modechange. The client will
+        reply with IAC SB LINEMODE LM_MODE MODE_EDIT|MODE_ACK IAC SE
+        (x|y is bitwise or). Thats it for linemode. (You will perhaps
+        receive other IAC SB LINEMODEs with other LM_xxx ... you may
+        ignore them. (At least IRIX 5.x sends IAC SB LINEMODE LM_SLC
+        .... IAC SE which declares the local characterset.)).
+
+        Example: (WILL/DO negotiated)
+
+                client                                        server
+                                                IAC SB LINEMODE LM_MODE
+                                                       MODE_EDIT IAC SE
+        IAC SB LINEMODE LM_MODE
+          MODE_EDIT|MODE_ACK IAC SE
+
+        Note: The option is much more funnier as it looks here, it for
+          example supports a mixed mode between linemode and
+          charactermode... flushing the input at certain characters (at
+          ESC or TAB for shell-like commandline completition). We suggest
+          reading RFC 1184.
+
+        You might be interested in TELOPT_XDISPLAYLOC and TELOPT_ENVIRON too.
+
+        Now, how to implement this using LDMud?
+
+        0. Patch src/driver/comm1.c, function init_telopts() to include
+            telopts_do[TELOPT_XXX] = reply_h_telnet_neg;
+            telopts_dont[TELOPT_XXX] = reply_h_telnet_neg;
+            telopts_will[TELOPT_XXX] = reply_h_telnet_neg;
+            telopts_wont[TELOPT_XXX] = reply_h_telnet_neg;
+           for every telnet negotiation you want to use.
+           Do not overwrite the TELOPT_ECHO and TELOPT_SGA hooks.
+
+           Alternatively, set the driver hook H_NOECHO in master.c:
+           this diverts _all_ telnet data into the mudlib.
+
+        1. Add a new driver hook to master.c just below the others.
+                set_driver_hook(H_TELNET_NEG,"telnet_neg"),
+        2. Make a telnet.h for your mudlib... just change the arrays in
+                src/driver/telnet.h.
+        3. define a function
+
+                void telnet_neg(int cmd, int option, int * optargs)
+
+           in your interactive objects (login.c , shells, player.c or
+           whereever). And note, in ALL objects, through which a
+           player is handed through (in TAPPMud these are login.c and
+           player.c). [Ok, master.c is interactive for a very short
+           time too, but it won't accept input, will it?]
+           'cmd' will be TELCMD_xxxx (see telnet.h), 'option' one of
+           TELOPT_xxxx and 'optargs' will be an array of ints (bytes in
+           fact) when 'cmd' is SB.
+           Parse 'cmd'/'option' and reply with appropiate answers
+           using binary_message() (appropiate meaning sending the
+           right DO/DONT/WILL/WONT if not sent before and using the SB
+           return values).
+        3.1. Sent IAC DO TTYPE IAC DO NAWS IAC DO LINEMODE at the
+           first time you can do it (before cat()ing /WELCOME perhaps).
+        3.2. Note all sent and received WILL/WONT/DO/DONT options for
+           conforming to the standard, avoiding endless loops and for
+           easy debugging :)
+        3.3. Pass those recevied/sent data and other data when the
+           interactive object is changed (from login.c to player.c or
+           at other bodychanges). Clear the data when the player goes
+           linkdead or quits. You won't need to save this data.
+        3.4. Lower_case() terminaltypes... ;)
+        3.5. Use reasonable defaultvalues if the client does not
+           support one of the options. (columns 80,lines 24 if not
+           NAWS, unknown or vt100 for no terminaltype)
+
+        The WILL/WONT/DO/DONT data is best saved in a mapping looking
+        like this:
+          ([ "received": ([ option1: DO_DONT_OR_0;WILL_WONT_OR_0, ... ])
+           , "sent"    : ([ option1: DO_DONT_OR_0;WILL_WONT_OR_0, ... ])
+          ])
+
+        (Ok, it can be done better. But not without confusing *me*
+        more.)
+
+        Before sending anything check
+          TN["sent"][option,0_if_do_dont_or_1_if_will_wont]
+        so you don't enter endless loops, save network traffic and the
+        like.
+
+        The windowsize is best saved in the players environment
+        variables so that he can modify them later on. (Or in two
+        integers in the player object...). Use for these values is
+        clear I think.
+
+        The terminaltypes received using above mentioned method are
+        best stored in an array. The actual set terminaltype is best
+        stored in an environment variable where the player can modify
+        it. Upon modifying it the IAC SB TTYPE SEND IAC SE cycle
+        should be started to match the emulation to the entered new
+        terminaltype. You then may use data retrieved from
+        /etc/termcap (man 5 termcap) or /usr/lib/terminfo/*/* (SysVID,
+        man 5 terminfo) to implement terminalcontrol codes dependend
+        on the terminaltype. /etc/termcap may prove to be the easiest
+        way tough /usr/lib/terminfo/*/* is the newer (and better) SysV
+        way of doing it.
+
+        [Anyone got a description of the internal terminfo format for
+        me? -Marcus]
+
+        LINEMODE replies may be left alone if only using the mode
+        change to MODE_EDIT
+
+        Some statistics about what clients support telnet negotiations:
+
+        Tinyfugue and some other mudclients usually do not support
+        negotiations.
+        Except for TF, which supports the Telnet End-Of-Record option
+        as marker for the end of the prompt. So if you send IAC EOR
+        after every prompt, it will print the prompt always in the
+        input window. (Do not forget to negotiate that. First IAC WILL
+        TELOPT_EOR/wait for IAC DO TELOPT_EOR). Newer versions of
+        TF will support NAWS and there will be a patch for TTYPE
+        negotiation available soon.
+
+        All telnets able to do negotiations I've encountered support
+        the TTYPE option.
+        HP9.x,Irix5.x,Linux,EP/IX,CUTELNET/NCSATELNET (Novell) and
+        perhaps more support NAWS.
+        At least Irix5.x,Linux,CU/NCSATELNET support LINEMODE.
+        SUN does not support NAWS and LINEMODE neither in SunOS 4.1.3
+        nor in Solaris 2.3.
+
+        For getting RFCs you can for example use
+        ftp://ftp.uni-erlangen.de/pub/doc/rfc/
+
+
+BUGS
+        Not all aspects of the options are mentioned to keep this doc
+        at a reasonable size. Refer to the RFCs to get more confused.
+
+CREDITS
+        Provided by Marcus@TAPPMud (Marcus Meissner,
+        <msmeissn@cip.informatik.uni-erlangen.de>).
diff --git a/doc/concepts/news b/doc/concepts/news
new file mode 100644
index 0000000..cacefb4
--- /dev/null
+++ b/doc/concepts/news
@@ -0,0 +1,88 @@
+CONCEPT:
+	news
+
+
+DESCRIPTION:
+	This document describes the news system used in Nightfall.
+	News is intended to provide a general system for bulletin
+	boards and similar objects. It is similar to the Usenet
+	news system. Articles are stored in a central area, /news.
+	Articles (Messages) are stored as files within this
+	directory. Only the news demon object is allowed to write
+	and read in the /news directory. Interfaceing to the
+	news demon is done via interface functions in the news
+	demon.
+	
+	Typically news are read and written by a bulletin
+	board object or by a newsreader. Player buletin boards
+	should of course be limited to specific news groups.
+	A newsreader might be intelligent in that it autmatically
+	shows new messages. Groups may be moderated, then only the
+	moderator can write there. There are also flags whether a
+	board is a wiz_only board and who may remove messages.
+
+	Security is in several levels. It may be 0, then every
+	effective userid might read and write and delete articles,
+	although the news demon will still look for the match of
+	the sender field with certain group requirements. This level
+	of security is to make it possible to create groups
+	accessible by bulletin boards which have no euid.
+
+	Security level 1 means that euid check is done, for reading
+	and writing. This still allows for bulltin boards, but those
+	must have root euid and thus the ability to seteuid to the
+	euid of the object using the bulletin board (normally the
+	player). This feature requires native gamedriver mode.
+
+	Saved news file format (formal notation):
+
+	int security;	/* wheter euid check is done, 0 or 1 */
+	mixed *accesslist = ({
+	   mixed *readaccess;    /* who can read messages */
+	   mixed *writeaccess;   /* who can write messages */
+	   mixed *deleteaccess;  /* who can delete messages */
+	   mixed *controlaccess; /* who can control the accesslist */
+	})
+	mixed *messages = ({
+	   mixed *msg1; mixed *msg2; ... mixed *msgN;
+	})
+
+	An access entry can be be one of the following:
+	- an effective userid (string)
+	- one of the keywords "wizard", "all", "author"
+	- a wizard level (integer) which is required minimal
+	- an array of one of the plain types above (Alternative).
+
+	A message looks as follows:
+
+	mixed *message = ({
+	   string author;       /* who wrote the message */
+	   string subject;      /* the subject */
+	   string *groups;      /* news groups where this was posted */
+	   int date;            /* date of message written */
+	   string messageid;    /* a unique string */
+	   string *referenceid; /* list of references */
+	   int expire;          /* when message expires */
+	   string body;         /* the contents of the message */
+	})
+
+	The news demon (/secure/news, or /obj/news) provides the
+	following functions:
+
+	int success = PostMessage(mixed *message)
+	  Tells the demon to insert the message in the news database.
+	  Depending on security level, euid or message.author is
+	  checked if this is allowed.
+
+	int success = DeleteMessage(string *groups, string messageid)
+	  Remove a message from the database.
+
+	mixed *messagelist = ReadMessageHeaders(string group)
+	  Get all message headers (complete info, but without
+	  body) of newsgroup <group>.
+
+	mixed *message = ReadMessage(string *groups, string messageid)
+	  Retrieve a message from the database.
+
+
+SEE ALSO:
diff --git a/doc/concepts/objects b/doc/concepts/objects
new file mode 100644
index 0000000..b9c7eb9
--- /dev/null
+++ b/doc/concepts/objects
@@ -0,0 +1,25 @@
+CONCEPT
+        objects
+
+LAST UPDATED
+        never
+
+DESCRIPTION
+        An object consists of a collection of functions (also called
+        'methods') and data (variables) on which the functions operate.
+        The only way to manipulate the data contained in an object is
+        via one of the functions defined by the object.
+
+        Every single thing in a mud is an object.  Rooms are objects.
+        Weapons are objects.  Even your character is an object (a special
+        kind of object called "interactive" but still an object in most
+        every respect).  Each object (except possibly virtual objects) in
+        the mud is associated with some file written in LPC (in the mud's
+        directory structure) that describes how the object is to interact
+        with the gamedriver and the rest of the objects in the mud.
+
+AUTHOR
+        Someone
+
+SEE ALSO
+        files(C), inheritance(C), create(A), reset(A)
diff --git a/doc/concepts/oop b/doc/concepts/oop
new file mode 100644
index 0000000..5146a7e
--- /dev/null
+++ b/doc/concepts/oop
@@ -0,0 +1,76 @@
+OOP
+ BESCHREIBUNG:
+     OOP steht fuer "Object-Orientierte Programmierung":
+
+     Wenn du weisst, wie man in einer prozeduralen Sprache programmiert
+     (C, PASCAL, BASIC), dann hast du bereits viele der Faehigkeiten, die
+     noetig sind um effektiv in LPC zu Programmieren. Die Hauptfaehigkeit,
+     die du brauchen wirst, ist die Begabung deine Ideen in eine Reihe von
+     Schritten zu unterteilen, so dass der Computer diese fuer dich
+     ausfuehren kann.
+
+     LPC ist aber auch eine (imperative, strukturierte) objektorientierte
+     Sprache. OOP haelt dazu an, sich zuerst um die Daten zu kuemmern und
+     dann um die Methoden mit denen diese Daten manipuliert werden:
+     Ein Spieler ist zuerst einmal ein Haufen von Attributen und Punkten
+     und seinem Namen, wie auf diese eingewirkt werden kann wird danach
+     geklaert. Ein Spielerobjekt kommuniziert und kooperiert mit vielen
+     anderen Objekten hier.
+
+     Im Folgenden sind einige der Kriterien objektorientierter Programmierung
+     aufgefuehrt:
+
+     Klassen:
+	Eine Klasse beschreibt das Verhalten einer Gruppe gleichartiger
+	Objekte. Beispielsweise kann man Lebewesen als eine Klasse ansehen,
+	weil man alle Objekte in Lebewesen und nicht-Lebewesen einteilen
+	kann. Hat man einen konkretes Monster und einen konkreten Spieler in
+	einem Raum, dann sind dies Objekte (Instanzen). Man kann von einer
+	Menge gleichartiger Objekte sagen, dass sie zu ein- und derselben
+	Klasse gehoeren. 
+
+     Abstraktion:
+	Jedes Objekt im System verkoerpert als abstraktes Modell einen
+	"Arbeiter", der Auftraege erledigen kann, seinen Zustand berichten
+	und aendern kann und mit den anderen Objekten im System kommunizieren
+	kann, ohne offen zu legen, wie diese seine Faehigkeiten implementiert
+	sind.
+	[So kann man einem Objekt den Auftrag Defend(1000, DT_FIRE, ...)
+	 geben. In MG werden lebende Objekte diesen Auftrag durch Aenderung
+	 ihres Zustandes - LP-Abzug oder Magiereaktion - erfuellen.]
+
+     Kapselung
+	Auch das "Verbergen von Information" genannt, sorgt K. dafuer, dass
+	Objekte den internen Zustand anderer Objekte nicht in unerwarteter
+	Weise aendern koennen; nur den eigenen Methoden eines Objektes soll
+	es erlaubt sein, auf den internen Zustand direkt zuzugreifen. Alle
+	Sorten von Objekten praesentieren nach aussen Schnittstellen (man
+	properties), die darueber bestimmen, wie andere Objekte mit ihnen
+	wechselwirken koennen.
+	[Siehe vor allem man properties]
+	
+     Polymorphie
+	Zeiger zu Objekten koennen es mit sich bringen, dass bei der Auswahl
+	eines konkreten Objektes seine Klasse (sein Typ) nicht offensichtlich
+	ist. Trotzdem werden Nachrichten an so selektierte Objekte korrekt
+	der tatsaechlichen Klasse zugeordnet. Wenn diese Zuordnung erst zur
+	Laufzeit aufgeloest wird, dann wird dieses Verhalten Polymorphismus
+	(auch: spaete Bindung oder dynamische Bindung) genannt.
+	[In LPC ist dynamische Bindung der Standard. Ueberschreibt man also
+	 in einem NPC die(), dann wird auf jeden Fall die neue Methode
+	 aufgerufen, wenn der NPC aus do_damage() heraus in sich die() ruft.]
+
+     Vererbung
+	Organisiert und erleichtert Polymorphie, indem neue Objekte definiert
+	und erzeugt werden koennen, die Spezialisierungen schon existierender
+	Objekte sind. Solche neuen Objekte koennen das vorhandene Verhalten
+	uebernehmen und erweitern, ohne dass dieses Urverhalten neu
+	implementiert werden muss. Typischerweise wird das dadurch erreicht,
+	dass Objekte zu Klassen und zu Hierarchien von Klassen gruppiert
+	werden, in denen sich die Gemeinsamkeiten im Verhalten ausdruecken.
+	[Siehe man vererbung]
+
+ SIEHE AUCH:
+     objekte, inheritance, goodstyle
+
+ 22. Maerz 2004 Gloinson
diff --git a/doc/concepts/overloading b/doc/concepts/overloading
new file mode 100644
index 0000000..06209e9
--- /dev/null
+++ b/doc/concepts/overloading
@@ -0,0 +1,48 @@
+CONCEPT
+        overloading
+
+DESCRIPTION
+        This concept is strongly connected with the concept of inheritance.
+        A function is called 'overloaded' if it is defined more than once
+        in an object. This can happen if the object inherits other objects
+        which have defined a function with the same name.
+        Usually the overloading is wanted and intended by the inheriting
+        object to change the behaviour of the function it overloads.
+        To call the overloaded functions from the overloading object the
+        ::-operator is used.
+        From outside the object only one of the functions can be called
+        via call_other() or the like; this will be the topmost of all
+        overloaded functions.
+
+        Normally an overloading function is declared the same way as the
+        overloaded function, this means it has the same number and types
+        of arguments. If an object wants to change the behaviour of the
+        function in a way that it can get more arguments than the original
+        function, it has to use the modifier 'varargs' or a compiler error
+        will be raised.
+
+EXAMPLE
+        File /players/alfe/a.c:
+
+            foo() { write("A"); }
+
+        File /players/alfe/b.c:
+
+            foo() { write("B"); }
+
+        File /players/alfe/c.c:
+
+            inherit "players/alfe/a";
+            inherit "players/alfe/b";
+
+            foo() {
+              a::foo();
+	      b::foo();
+	      write("C");
+	    }
+
+	To call "players/alfe/c"->foo() will now result in the output of
+	ABC.
+
+SEE ALSO
+        modifiers(LPC), inheritance(C), functions(LPC)
diff --git a/doc/concepts/pcre b/doc/concepts/pcre
new file mode 100644
index 0000000..f863ffd
--- /dev/null
+++ b/doc/concepts/pcre
@@ -0,0 +1,1422 @@
+SYNOPSIS
+        PCRE - Perl-compatible regular expressions
+
+
+DESCRIPTION
+        This document describes the regular expressions supported by the
+        PCRE package. When the package is compiled into the driver, the
+        macro __PCRE__ is defined.
+
+        Most of this manpage is lifted directly from the original PCRE
+        manpage (dated January 2003).
+
+        The PCRE library is a set of functions that implement  regular
+        expression  pattern  matching using the same syntax and semantics
+        as Perl  5,  with  just  a  few  differences  (see below).  The
+        current  implementation  corresponds  to  Perl 5.005, with some
+        additional features  from  later  versions.  This  includes  some
+        experimental,  incomplete  support for UTF-8 encoded strings.
+        Details of exactly what is  and  what is not supported are given
+        below.
+
+
+PCRE REGULAR EXPRESSION DETAILS
+
+       The  syntax  and semantics of the regular expressions supported by PCRE
+       are described below. Regular expressions are also described in the Perl
+       documentation  and in a number of other books, some of which have copi-
+       ous examples. Jeffrey Friedl's "Mastering  Regular  Expressions",  pub-
+       lished  by  O'Reilly, covers them in great detail. The description here
+       is intended as reference documentation.
+
+       The basic operation of PCRE is on strings of bytes. However,  there  is
+       also  support for UTF-8 character strings. To use this support you must
+       build PCRE to include UTF-8 support, and then call pcre_compile()  with
+       the  PCRE_UTF8  option.  How  this affects the pattern matching is men-
+       tioned in several places below. There is also a summary of  UTF-8  fea-
+       tures in the section on UTF-8 support in the main pcre page.
+
+       A  regular  expression  is  a pattern that is matched against a subject
+       string from left to right. Most characters stand for  themselves  in  a
+       pattern,  and  match  the corresponding characters in the subject. As a
+       trivial example, the pattern
+
+         The quick brown fox
+
+       matches a portion of a subject string that is identical to itself.  The
+       power of regular expressions comes from the ability to include alterna-
+       tives and repetitions in the pattern. These are encoded in the  pattern
+       by  the  use  of meta-characters, which do not stand for themselves but
+       instead are interpreted in some special way.
+
+       There are two different sets of meta-characters: those that are  recog-
+       nized  anywhere in the pattern except within square brackets, and those
+       that are recognized in square brackets. Outside  square  brackets,  the
+       meta-characters are as follows:
+
+         \      general escape character with several uses
+         ^      assert start of string (or line, in multiline mode)
+         $      assert end of string (or line, in multiline mode)
+         .      match any character except newline (by default)
+         [      start character class definition
+         |      start of alternative branch
+         (      start subpattern
+         )      end subpattern
+         ?      extends the meaning of (
+                also 0 or 1 quantifier
+                also quantifier minimizer
+         *      0 or more quantifier
+         +      1 or more quantifier
+                also "possessive quantifier"
+         {      start min/max quantifier
+
+       Part  of  a  pattern  that is in square brackets is called a "character
+       class". In a character class the only meta-characters are:
+
+         \      general escape character
+         ^      negate the class, but only if the first character
+         -      indicates character range
+         [      POSIX character class (only if followed by POSIX
+                  syntax)
+         ]      terminates the character class
+
+       The following sections describe the use of each of the meta-characters.
+
+
+BACKSLASH
+
+       The backslash character has several uses. Firstly, if it is followed by
+       a non-alphameric character, it takes  away  any  special  meaning  that
+       character  may  have.  This  use  of  backslash  as an escape character
+       applies both inside and outside character classes.
+
+       For example, if you want to match a * character, you write  \*  in  the
+       pattern.   This  escaping  action  applies whether or not the following
+       character would otherwise be interpreted as a meta-character, so it  is
+       always  safe to precede a non-alphameric with backslash to specify that
+       it stands for itself. In particular, if you want to match a  backslash,
+       you write \\.
+
+       If  a  pattern is compiled with the PCRE_EXTENDED option, whitespace in
+       the pattern (other than in a character class) and characters between  a
+       # outside a character class and the next newline character are ignored.
+       An escaping backslash can be used to include a whitespace or #  charac-
+       ter as part of the pattern.
+
+       If  you  want  to remove the special meaning from a sequence of charac-
+       ters, you can do so by putting them between \Q and \E. This is  differ-
+       ent  from  Perl  in  that  $  and  @ are handled as literals in \Q...\E
+       sequences in PCRE, whereas in Perl, $ and @ cause  variable  interpola-
+       tion. Note the following examples:
+
+         Pattern            PCRE matches   Perl matches
+
+         \Qabc$xyz\E        abc$xyz        abc followed by the
+                                             contents of $xyz
+         \Qabc\$xyz\E       abc\$xyz       abc\$xyz
+         \Qabc\E\$\Qxyz\E   abc$xyz        abc$xyz
+
+       The  \Q...\E  sequence  is recognized both inside and outside character
+       classes.
+
+       A second use of backslash provides a way of encoding non-printing char-
+       acters  in patterns in a visible manner. There is no restriction on the
+       appearance of non-printing characters, apart from the binary zero  that
+       terminates  a  pattern,  but  when  a pattern is being prepared by text
+       editing, it is usually easier  to  use  one  of  the  following  escape
+       sequences than the binary character it represents:
+
+         \a        alarm, that is, the BEL character (hex 07)
+         \cx       "control-x", where x is any character
+         \e        escape (hex 1B)
+         \f        formfeed (hex 0C)
+         \n        newline (hex 0A)
+         \r        carriage return (hex 0D)
+         \t        tab (hex 09)
+         \ddd      character with octal code ddd, or backreference
+         \xhh      character with hex code hh
+         \x{hhh..} character with hex code hhh... (UTF-8 mode only)
+
+       The  precise  effect of \cx is as follows: if x is a lower case letter,
+       it is converted to upper case. Then bit 6 of the character (hex 40)  is
+       inverted.   Thus  \cz becomes hex 1A, but \c{ becomes hex 3B, while \c;
+       becomes hex 7B.
+
+       After \x, from zero to two hexadecimal digits are read (letters can  be
+       in  upper or lower case). In UTF-8 mode, any number of hexadecimal dig-
+       its may appear between \x{ and }, but the value of the  character  code
+       must  be  less  than  2**31  (that is, the maximum hexadecimal value is
+       7FFFFFFF). If characters other than hexadecimal digits  appear  between
+       \x{  and }, or if there is no terminating }, this form of escape is not
+       recognized. Instead, the initial \x will be interpreted as a basic hex-
+       adecimal escape, with no following digits, giving a byte whose value is
+       zero.
+
+       Characters whose value is less than 256 can be defined by either of the
+       two  syntaxes for \x when PCRE is in UTF-8 mode. There is no difference
+       in the way they are handled. For example, \xdc is exactly the  same  as
+       \x{dc}.
+
+       After  \0  up  to  two further octal digits are read. In both cases, if
+       there are fewer than two digits, just those that are present are  used.
+       Thus  the sequence \0\x\07 specifies two binary zeros followed by a BEL
+       character (code value 7). Make sure you supply  two  digits  after  the
+       initial zero if the character that follows is itself an octal digit.
+
+       The handling of a backslash followed by a digit other than 0 is compli-
+       cated.  Outside a character class, PCRE reads it and any following dig-
+       its  as  a  decimal  number. If the number is less than 10, or if there
+       have been at least that many previous capturing left parentheses in the
+       expression,  the  entire  sequence  is  taken  as  a  back reference. A
+       description of how this works is given later, following the  discussion
+       of parenthesized subpatterns.
+
+       Inside  a  character  class, or if the decimal number is greater than 9
+       and there have not been that many capturing subpatterns, PCRE  re-reads
+       up  to three octal digits following the backslash, and generates a sin-
+       gle byte from the least significant 8 bits of the value. Any subsequent
+       digits stand for themselves.  For example:
+
+         \040   is another way of writing a space
+         \40    is the same, provided there are fewer than 40
+                   previous capturing subpatterns
+         \7     is always a back reference
+         \11    might be a back reference, or another way of
+                   writing a tab
+         \011   is always a tab
+         \0113  is a tab followed by the character "3"
+         \113   might be a back reference, otherwise the
+                   character with octal code 113
+         \377   might be a back reference, otherwise
+                   the byte consisting entirely of 1 bits
+         \81    is either a back reference, or a binary zero
+                   followed by the two characters "8" and "1"
+
+       Note  that  octal  values of 100 or greater must not be introduced by a
+       leading zero, because no more than three octal digits are ever read.
+
+       All the sequences that define a single byte value  or  a  single  UTF-8
+       character (in UTF-8 mode) can be used both inside and outside character
+       classes. In addition, inside a character  class,  the  sequence  \b  is
+       interpreted  as  the  backspace character (hex 08). Outside a character
+       class it has a different meaning (see below).
+
+       The third use of backslash is for specifying generic character types:
+
+         \d     any decimal digit
+         \D     any character that is not a decimal digit
+         \s     any whitespace character
+         \S     any character that is not a whitespace character
+         \w     any "word" character
+         \W     any "non-word" character
+
+       Each pair of escape sequences partitions the complete set of characters
+       into  two disjoint sets. Any given character matches one, and only one,
+       of each pair.
+
+       In UTF-8 mode, characters with values greater than 255 never match  \d,
+       \s, or \w, and always match \D, \S, and \W.
+
+       For  compatibility  with Perl, \s does not match the VT character (code
+       11).  This makes it different from the the POSIX "space" class. The  \s
+       characters are HT (9), LF (10), FF (12), CR (13), and space (32).
+
+       A  "word" character is any letter or digit or the underscore character,
+       that is, any character which can be part of a Perl "word". The  defini-
+       tion  of  letters  and digits is controlled by PCRE's character tables,
+       and may vary if locale- specific matching is taking place (see  "Locale
+       support"  in  the  pcreapi  page).  For  example,  in the "fr" (French)
+       locale, some character codes greater than 128  are  used  for  accented
+       letters, and these are matched by \w.
+
+       These character type sequences can appear both inside and outside char-
+       acter classes. They each match one character of the  appropriate  type.
+       If  the current matching point is at the end of the subject string, all
+       of them fail, since there is no character to match.
+
+       The fourth use of backslash is for certain simple assertions. An asser-
+       tion  specifies a condition that has to be met at a particular point in
+       a match, without consuming any characters from the subject string.  The
+       use  of subpatterns for more complicated assertions is described below.
+       The backslashed assertions are
+
+         \b     matches at a word boundary
+         \B     matches when not at a word boundary
+         \A     matches at start of subject
+         \Z     matches at end of subject or before newline at end
+         \z     matches at end of subject
+         \G     matches at first matching position in subject
+
+       These assertions may not appear in character classes (but note that  \b
+       has a different meaning, namely the backspace character, inside a char-
+       acter class).
+
+       A word boundary is a position in the subject string where  the  current
+       character  and  the previous character do not both match \w or \W (i.e.
+       one matches \w and the other matches \W), or the start or  end  of  the
+       string if the first or last character matches \w, respectively.
+
+       The  \A,  \Z,  and \z assertions differ from the traditional circumflex
+       and dollar (described below) in that they only ever match at  the  very
+       start  and  end  of the subject string, whatever options are set. Thus,
+       they are independent of multiline mode.
+
+       They are not affected by the PCRE_NOTBOL or PCRE_NOTEOL options. If the
+       startoffset argument of pcre_exec() is non-zero, indicating that match-
+       ing is to start at a point other than the beginning of the subject,  \A
+       can  never  match.  The difference between \Z and \z is that \Z matches
+       before a newline that is the last character of the string as well as at
+       the end of the string, whereas \z matches only at the end.
+
+       The  \G assertion is true only when the current matching position is at
+       the start point of the match, as specified by the startoffset  argument
+       of  pcre_exec().  It  differs  from \A when the value of startoffset is
+       non-zero. By calling pcre_exec() multiple times with appropriate  argu-
+       ments, you can mimic Perl's /g option, and it is in this kind of imple-
+       mentation where \G can be useful.
+
+       Note, however, that PCRE's interpretation of \G, as the  start  of  the
+       current match, is subtly different from Perl's, which defines it as the
+       end of the previous match. In Perl, these can  be  different  when  the
+       previously  matched  string was empty. Because PCRE does just one match
+       at a time, it cannot reproduce this behaviour.
+
+       If all the alternatives of a pattern begin with \G, the  expression  is
+       anchored to the starting match position, and the "anchored" flag is set
+       in the compiled regular expression.
+
+
+CIRCUMFLEX AND DOLLAR
+
+       Outside a character class, in the default matching mode, the circumflex
+       character  is  an  assertion which is true only if the current matching
+       point is at the start of the subject string. If the  startoffset  argu-
+       ment  of  pcre_exec()  is  non-zero,  circumflex can never match if the
+       PCRE_MULTILINE option is unset. Inside a  character  class,  circumflex
+       has an entirely different meaning (see below).
+
+       Circumflex  need  not be the first character of the pattern if a number
+       of alternatives are involved, but it should be the first thing in  each
+       alternative  in  which  it appears if the pattern is ever to match that
+       branch. If all possible alternatives start with a circumflex, that  is,
+       if  the  pattern  is constrained to match only at the start of the sub-
+       ject, it is said to be an "anchored" pattern.  (There  are  also  other
+       constructs that can cause a pattern to be anchored.)
+
+       A  dollar  character  is an assertion which is true only if the current
+       matching point is at the end of  the  subject  string,  or  immediately
+       before a newline character that is the last character in the string (by
+       default). Dollar need not be the last character of  the  pattern  if  a
+       number  of alternatives are involved, but it should be the last item in
+       any branch in which it appears.  Dollar has no  special  meaning  in  a
+       character class.
+
+       The  meaning  of  dollar  can be changed so that it matches only at the
+       very end of the string, by setting the  PCRE_DOLLAR_ENDONLY  option  at
+       compile time. This does not affect the \Z assertion.
+
+       The meanings of the circumflex and dollar characters are changed if the
+       PCRE_MULTILINE option is set. When this is the case, they match immedi-
+       ately  after  and  immediately  before  an  internal newline character,
+       respectively, in addition to matching at the start and end of the  sub-
+       ject  string.  For  example,  the  pattern  /^abc$/ matches the subject
+       string "def\nabc" in multiline mode, but not  otherwise.  Consequently,
+       patterns  that  are  anchored  in single line mode because all branches
+       start with ^ are not anchored in multiline mode, and a match  for  cir-
+       cumflex  is  possible  when  the startoffset argument of pcre_exec() is
+       non-zero. The PCRE_DOLLAR_ENDONLY option is ignored  if  PCRE_MULTILINE
+       is set.
+
+       Note  that  the sequences \A, \Z, and \z can be used to match the start
+       and end of the subject in both modes, and if all branches of a  pattern
+       start  with  \A it is always anchored, whether PCRE_MULTILINE is set or
+       not.
+
+
+FULL STOP (PERIOD, DOT)
+
+       Outside a character class, a dot in the pattern matches any one charac-
+       ter  in  the  subject,  including a non-printing character, but not (by
+       default) newline.  In UTF-8 mode, a dot matches  any  UTF-8  character,
+       which  might  be  more than one byte long, except (by default) for new-
+       line. If the PCRE_DOTALL option is set, dots match  newlines  as  well.
+       The  handling of dot is entirely independent of the handling of circum-
+       flex and dollar, the only relationship being  that  they  both  involve
+       newline characters. Dot has no special meaning in a character class.
+
+
+MATCHING A SINGLE BYTE
+
+       Outside a character class, the escape sequence \C matches any one byte,
+       both in and out of UTF-8 mode. Unlike a dot, it always matches  a  new-
+       line.  The  feature  is  provided  in Perl in order to match individual
+       bytes in UTF-8 mode.  Because it breaks up UTF-8 characters into  indi-
+       vidual  bytes,  what  remains  in  the  string may be a malformed UTF-8
+       string. For this reason it is best avoided.
+
+       PCRE does not allow \C to appear in lookbehind assertions (see  below),
+       because in UTF-8 mode it makes it impossible to calculate the length of
+       the lookbehind.
+
+
+SQUARE BRACKETS
+
+       An opening square bracket introduces a character class, terminated by a
+       closing square bracket. A closing square bracket on its own is not spe-
+       cial. If a closing square bracket is required as a member of the class,
+       it  should  be  the first data character in the class (after an initial
+       circumflex, if present) or escaped with a backslash.
+
+       A character class matches a single character in the subject.  In  UTF-8
+       mode,  the character may occupy more than one byte. A matched character
+       must be in the set of characters defined by the class, unless the first
+       character  in  the  class definition is a circumflex, in which case the
+       subject character must not be in the set defined by  the  class.  If  a
+       circumflex  is actually required as a member of the class, ensure it is
+       not the first character, or escape it with a backslash.
+
+       For example, the character class [aeiou] matches any lower case  vowel,
+       while  [^aeiou]  matches  any character that is not a lower case vowel.
+       Note that a circumflex is just a convenient notation for specifying the
+       characters which are in the class by enumerating those that are not. It
+       is not an assertion: it still consumes a  character  from  the  subject
+       string, and fails if the current pointer is at the end of the string.
+
+       In  UTF-8 mode, characters with values greater than 255 can be included
+       in a class as a literal string of bytes, or by using the  \x{  escaping
+       mechanism.
+
+       When  caseless  matching  is set, any letters in a class represent both
+       their upper case and lower case versions, so for  example,  a  caseless
+       [aeiou]  matches  "A"  as well as "a", and a caseless [^aeiou] does not
+       match "A", whereas a caseful version would. PCRE does not  support  the
+       concept of case for characters with values greater than 255.
+
+       The  newline character is never treated in any special way in character
+       classes, whatever the setting  of  the  PCRE_DOTALL  or  PCRE_MULTILINE
+       options is. A class such as [^a] will always match a newline.
+
+       The  minus (hyphen) character can be used to specify a range of charac-
+       ters in a character  class.  For  example,  [d-m]  matches  any  letter
+       between  d  and  m,  inclusive.  If  a minus character is required in a
+       class, it must be escaped with a backslash  or  appear  in  a  position
+       where  it cannot be interpreted as indicating a range, typically as the
+       first or last character in the class.
+
+       It is not possible to have the literal character "]" as the end charac-
+       ter  of a range. A pattern such as [W-]46] is interpreted as a class of
+       two characters ("W" and "-") followed by a literal string "46]", so  it
+       would  match  "W46]"  or  "-46]". However, if the "]" is escaped with a
+       backslash it is interpreted as the end of range, so [W-\]46] is  inter-
+       preted  as  a  single class containing a range followed by two separate
+       characters. The octal or hexadecimal representation of "]" can also  be
+       used to end a range.
+
+       Ranges  operate in the collating sequence of character values. They can
+       also  be  used  for  characters  specified  numerically,  for   example
+       [\000-\037].  In UTF-8 mode, ranges can include characters whose values
+       are greater than 255, for example [\x{100}-\x{2ff}].
+
+       If a range that includes letters is used when caseless matching is set,
+       it matches the letters in either case. For example, [W-c] is equivalent
+       to [][\^_`wxyzabc], matched caselessly, and if character tables for the
+       "fr"  locale  are  in use, [\xc8-\xcb] matches accented E characters in
+       both cases.
+
+       The character types \d, \D, \s, \S, \w, and \W may  also  appear  in  a
+       character  class,  and add the characters that they match to the class.
+       For example, [\dABCDEF] matches any hexadecimal digit. A circumflex can
+       conveniently  be  used with the upper case character types to specify a
+       more restricted set of characters than the matching  lower  case  type.
+       For  example,  the  class  [^\W_]  matches any letter or digit, but not
+       underscore.
+
+       All non-alphameric characters other than \, -, ^ (at the start) and the
+       terminating ] are non-special in character classes, but it does no harm
+       if they are escaped.
+
+
+POSIX CHARACTER CLASSES
+
+       Perl supports the POSIX notation  for  character  classes,  which  uses
+       names  enclosed by [: and :] within the enclosing square brackets. PCRE
+       also supports this notation. For example,
+
+         [01[:alpha:]%]
+
+       matches "0", "1", any alphabetic character, or "%". The supported class
+       names are
+
+         alnum    letters and digits
+         alpha    letters
+         ascii    character codes 0 - 127
+         blank    space or tab only
+         cntrl    control characters
+         digit    decimal digits (same as \d)
+         graph    printing characters, excluding space
+         lower    lower case letters
+         print    printing characters, including space
+         punct    printing characters, excluding letters and digits
+         space    white space (not quite the same as \s)
+         upper    upper case letters
+         word     "word" characters (same as \w)
+         xdigit   hexadecimal digits
+
+       The  "space" characters are HT (9), LF (10), VT (11), FF (12), CR (13),
+       and space (32). Notice that this list includes the VT  character  (code
+       11). This makes "space" different to \s, which does not include VT (for
+       Perl compatibility).
+
+       The name "word" is a Perl extension, and "blank"  is  a  GNU  extension
+       from  Perl  5.8. Another Perl extension is negation, which is indicated
+       by a ^ character after the colon. For example,
+
+         [12[:^digit:]]
+
+       matches "1", "2", or any non-digit. PCRE (and Perl) also recognize  the
+       POSIX syntax [.ch.] and [=ch=] where "ch" is a "collating element", but
+       these are not supported, and an error is given if they are encountered.
+
+       In UTF-8 mode, characters with values greater than 255 do not match any
+       of the POSIX character classes.
+
+
+VERTICAL BAR
+
+       Vertical bar characters are used to separate alternative patterns.  For
+       example, the pattern
+
+         gilbert|sullivan
+
+       matches  either "gilbert" or "sullivan". Any number of alternatives may
+       appear, and an empty  alternative  is  permitted  (matching  the  empty
+       string).   The  matching  process  tries each alternative in turn, from
+       left to right, and the first one that succeeds is used. If the alterna-
+       tives  are within a subpattern (defined below), "succeeds" means match-
+       ing the rest of the main pattern as well as the alternative in the sub-
+       pattern.
+
+
+INTERNAL OPTION SETTING
+
+       The  settings  of  the  PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL, and
+       PCRE_EXTENDED options can be changed  from  within  the  pattern  by  a
+       sequence  of  Perl  option  letters  enclosed between "(?" and ")". The
+       option letters are
+
+         i  for PCRE_CASELESS
+         m  for PCRE_MULTILINE
+         s  for PCRE_DOTALL
+         x  for PCRE_EXTENDED
+
+       For example, (?im) sets caseless, multiline matching. It is also possi-
+       ble to unset these options by preceding the letter with a hyphen, and a
+       combined setting and unsetting such as (?im-sx), which sets  PCRE_CASE-
+       LESS  and PCRE_MULTILINE while unsetting PCRE_DOTALL and PCRE_EXTENDED,
+       is also permitted. If a  letter  appears  both  before  and  after  the
+       hyphen, the option is unset.
+
+       When  an option change occurs at top level (that is, not inside subpat-
+       tern parentheses), the change applies to the remainder of  the  pattern
+       that follows.  If the change is placed right at the start of a pattern,
+       PCRE extracts it into the global options (and it will therefore show up
+       in data extracted by the pcre_fullinfo() function).
+
+       An option change within a subpattern affects only that part of the cur-
+       rent pattern that follows it, so
+
+         (a(?i)b)c
+
+       matches abc and aBc and no other strings (assuming PCRE_CASELESS is not
+       used).   By  this means, options can be made to have different settings
+       in different parts of the pattern. Any changes made in one  alternative
+       do  carry  on  into subsequent branches within the same subpattern. For
+       example,
+
+         (a(?i)b|c)
+
+       matches "ab", "aB", "c", and "C", even though  when  matching  "C"  the
+       first  branch  is  abandoned before the option setting. This is because
+       the effects of option settings happen at compile time. There  would  be
+       some very weird behaviour otherwise.
+
+       The  PCRE-specific  options PCRE_UNGREEDY and PCRE_EXTRA can be changed
+       in the same way as the Perl-compatible options by using the  characters
+       U  and X respectively. The (?X) flag setting is special in that it must
+       always occur earlier in the pattern than any of the additional features
+       it turns on, even when it is at top level. It is best put at the start.
+
+
+SUBPATTERNS
+
+       Subpatterns are delimited by parentheses (round brackets), which can be
+       nested.  Marking part of a pattern as a subpattern does two things:
+
+       1. It localizes a set of alternatives. For example, the pattern
+
+         cat(aract|erpillar|)
+
+       matches  one  of the words "cat", "cataract", or "caterpillar". Without
+       the parentheses, it would match "cataract",  "erpillar"  or  the  empty
+       string.
+
+       2.  It  sets  up  the  subpattern as a capturing subpattern (as defined
+       above).  When the whole pattern matches, that portion  of  the  subject
+       string that matched the subpattern is passed back to the caller via the
+       ovector argument of pcre_exec(). Opening parentheses are  counted  from
+       left  to right (starting from 1) to obtain the numbers of the capturing
+       subpatterns.
+
+       For example, if the string "the red king" is matched against  the  pat-
+       tern
+
+         the ((red|white) (king|queen))
+
+       the captured substrings are "red king", "red", and "king", and are num-
+       bered 1, 2, and 3, respectively.
+
+       The fact that plain parentheses fulfil  two  functions  is  not  always
+       helpful.   There are often times when a grouping subpattern is required
+       without a capturing requirement. If an opening parenthesis is  followed
+       by  a question mark and a colon, the subpattern does not do any captur-
+       ing, and is not counted when computing the  number  of  any  subsequent
+       capturing  subpatterns. For example, if the string "the white queen" is
+       matched against the pattern
+
+         the ((?:red|white) (king|queen))
+
+       the captured substrings are "white queen" and "queen", and are numbered
+       1  and 2. The maximum number of capturing subpatterns is 65535, and the
+       maximum depth of nesting of all subpatterns, both  capturing  and  non-
+       capturing, is 200.
+
+       As  a  convenient shorthand, if any option settings are required at the
+       start of a non-capturing subpattern,  the  option  letters  may  appear
+       between the "?" and the ":". Thus the two patterns
+
+         (?i:saturday|sunday)
+         (?:(?i)saturday|sunday)
+
+       match exactly the same set of strings. Because alternative branches are
+       tried from left to right, and options are not reset until  the  end  of
+       the  subpattern is reached, an option setting in one branch does affect
+       subsequent branches, so the above patterns match "SUNDAY"  as  well  as
+       "Saturday".
+
+
+NAMED SUBPATTERNS
+
+       Identifying  capturing  parentheses  by number is simple, but it can be
+       very hard to keep track of the numbers in complicated  regular  expres-
+       sions.  Furthermore,  if  an  expression  is  modified, the numbers may
+       change. To help with the difficulty, PCRE supports the naming  of  sub-
+       patterns,  something  that  Perl  does  not  provide. The Python syntax
+       (?P<name>...) is used. Names consist  of  alphanumeric  characters  and
+       underscores, and must be unique within a pattern.
+
+       Named  capturing  parentheses  are  still  allocated numbers as well as
+       names. The PCRE API provides function calls for extracting the name-to-
+       number  translation  table from a compiled pattern. For further details
+       see the pcreapi documentation.
+
+
+REPETITION
+
+       Repetition is specified by quantifiers, which can  follow  any  of  the
+       following items:
+
+         a literal data character
+         the . metacharacter
+         the \C escape sequence
+         escapes such as \d that match single characters
+         a character class
+         a back reference (see next section)
+         a parenthesized subpattern (unless it is an assertion)
+
+       The  general repetition quantifier specifies a minimum and maximum num-
+       ber of permitted matches, by giving the two numbers in  curly  brackets
+       (braces),  separated  by  a comma. The numbers must be less than 65536,
+       and the first must be less than or equal to the second. For example:
+
+         z{2,4}
+
+       matches "zz", "zzz", or "zzzz". A closing brace on its  own  is  not  a
+       special  character.  If  the second number is omitted, but the comma is
+       present, there is no upper limit; if the second number  and  the  comma
+       are  both omitted, the quantifier specifies an exact number of required
+       matches. Thus
+
+         [aeiou]{3,}
+
+       matches at least 3 successive vowels, but may match many more, while
+
+         \d{8}
+
+       matches exactly 8 digits. An opening curly bracket that  appears  in  a
+       position  where a quantifier is not allowed, or one that does not match
+       the syntax of a quantifier, is taken as a literal character. For  exam-
+       ple, {,6} is not a quantifier, but a literal string of four characters.
+
+       In UTF-8 mode, quantifiers apply to UTF-8  characters  rather  than  to
+       individual bytes. Thus, for example, \x{100}{2} matches two UTF-8 char-
+       acters, each of which is represented by a two-byte sequence.
+
+       The quantifier {0} is permitted, causing the expression to behave as if
+       the previous item and the quantifier were not present.
+
+       For  convenience  (and  historical compatibility) the three most common
+       quantifiers have single-character abbreviations:
+
+         *    is equivalent to {0,}
+         +    is equivalent to {1,}
+         ?    is equivalent to {0,1}
+
+       It is possible to construct infinite loops by  following  a  subpattern
+       that can match no characters with a quantifier that has no upper limit,
+       for example:
+
+         (a?)*
+
+       Earlier versions of Perl and PCRE used to give an error at compile time
+       for  such  patterns. However, because there are cases where this can be
+       useful, such patterns are now accepted, but if any  repetition  of  the
+       subpattern  does in fact match no characters, the loop is forcibly bro-
+       ken.
+
+       By default, the quantifiers are "greedy", that is, they match  as  much
+       as  possible  (up  to  the  maximum number of permitted times), without
+       causing the rest of the pattern to fail. The classic example  of  where
+       this gives problems is in trying to match comments in C programs. These
+       appear between the sequences /* and */ and within the  sequence,  indi-
+       vidual * and / characters may appear. An attempt to match C comments by
+       applying the pattern
+
+         /\*.*\*/
+
+       to the string
+
+         /* first command */  not comment  /* second comment */
+
+       fails, because it matches the entire string owing to the greediness  of
+       the .*  item.
+
+       However,  if  a quantifier is followed by a question mark, it ceases to
+       be greedy, and instead matches the minimum number of times possible, so
+       the pattern
+
+         /\*.*?\*/
+
+       does  the  right  thing with the C comments. The meaning of the various
+       quantifiers is not otherwise changed,  just  the  preferred  number  of
+       matches.   Do  not  confuse this use of question mark with its use as a
+       quantifier in its own right. Because it has two uses, it can  sometimes
+       appear doubled, as in
+
+         \d??\d
+
+       which matches one digit by preference, but can match two if that is the
+       only way the rest of the pattern matches.
+
+       If the PCRE_UNGREEDY option is set (an option which is not available in
+       Perl),  the  quantifiers are not greedy by default, but individual ones
+       can be made greedy by following them with a  question  mark.  In  other
+       words, it inverts the default behaviour.
+
+       When  a  parenthesized  subpattern  is quantified with a minimum repeat
+       count that is greater than 1 or with a limited maximum, more  store  is
+       required  for  the  compiled  pattern, in proportion to the size of the
+       minimum or maximum.
+
+       If a pattern starts with .* or .{0,} and the PCRE_DOTALL option (equiv-
+       alent  to Perl's /s) is set, thus allowing the . to match newlines, the
+       pattern is implicitly anchored, because whatever follows will be  tried
+       against  every character position in the subject string, so there is no
+       point in retrying the overall match at any position  after  the  first.
+       PCRE normally treats such a pattern as though it were preceded by \A.
+
+       In  cases  where  it  is known that the subject string contains no new-
+       lines, it is worth setting PCRE_DOTALL in order to  obtain  this  opti-
+       mization, or alternatively using ^ to indicate anchoring explicitly.
+
+       However,  there is one situation where the optimization cannot be used.
+       When .*  is inside capturing parentheses that  are  the  subject  of  a
+       backreference  elsewhere in the pattern, a match at the start may fail,
+       and a later one succeed. Consider, for example:
+
+         (.*)abc\1
+
+       If the subject is "xyz123abc123" the match point is the fourth  charac-
+       ter. For this reason, such a pattern is not implicitly anchored.
+
+       When a capturing subpattern is repeated, the value captured is the sub-
+       string that matched the final iteration. For example, after
+
+         (tweedle[dume]{3}\s*)+
+
+       has matched "tweedledum tweedledee" the value of the captured substring
+       is  "tweedledee".  However,  if there are nested capturing subpatterns,
+       the corresponding captured values may have been set in previous  itera-
+       tions. For example, after
+
+         /(a|(b))+/
+
+       matches "aba" the value of the second captured substring is "b".
+
+
+ATOMIC GROUPING AND POSSESSIVE QUANTIFIERS
+
+       With both maximizing and minimizing repetition, failure of what follows
+       normally causes the repeated item to be re-evaluated to see if  a  dif-
+       ferent number of repeats allows the rest of the pattern to match. Some-
+       times it is useful to prevent this, either to change the nature of  the
+       match,  or  to  cause it fail earlier than it otherwise might, when the
+       author of the pattern knows there is no point in carrying on.
+
+       Consider, for example, the pattern \d+foo when applied to  the  subject
+       line
+
+         123456bar
+
+       After matching all 6 digits and then failing to match "foo", the normal
+       action of the matcher is to try again with only 5 digits  matching  the
+       \d+  item,  and  then  with  4,  and  so on, before ultimately failing.
+       "Atomic grouping" (a term taken from Jeffrey  Friedl's  book)  provides
+       the  means for specifying that once a subpattern has matched, it is not
+       to be re-evaluated in this way.
+
+       If we use atomic grouping for the previous example, the  matcher  would
+       give up immediately on failing to match "foo" the first time. The nota-
+       tion is a kind of special parenthesis, starting with  (?>  as  in  this
+       example:
+
+         (?>\d+)foo
+
+       This  kind  of  parenthesis "locks up" the  part of the pattern it con-
+       tains once it has matched, and a failure further into  the  pattern  is
+       prevented  from  backtracking into it. Backtracking past it to previous
+       items, however, works as normal.
+
+       An alternative description is that a subpattern of  this  type  matches
+       the  string  of  characters  that an identical standalone pattern would
+       match, if anchored at the current point in the subject string.
+
+       Atomic grouping subpatterns are not capturing subpatterns. Simple cases
+       such as the above example can be thought of as a maximizing repeat that
+       must swallow everything it can. So, while both \d+ and  \d+?  are  pre-
+       pared  to  adjust  the number of digits they match in order to make the
+       rest of the pattern match, (?>\d+) can only match an entire sequence of
+       digits.
+
+       Atomic  groups in general can of course contain arbitrarily complicated
+       subpatterns, and can be nested. However, when  the  subpattern  for  an
+       atomic group is just a single repeated item, as in the example above, a
+       simpler notation, called a "possessive quantifier" can  be  used.  This
+       consists  of  an  additional  + character following a quantifier. Using
+       this notation, the previous example can be rewritten as
+
+         \d++bar
+
+       Possessive  quantifiers  are  always  greedy;  the   setting   of   the
+       PCRE_UNGREEDY option is ignored. They are a convenient notation for the
+       simpler forms of atomic group. However, there is no difference  in  the
+       meaning  or  processing  of  a possessive quantifier and the equivalent
+       atomic group.
+
+       The possessive quantifier syntax is an extension to the Perl syntax. It
+       originates in Sun's Java package.
+
+       When  a  pattern  contains an unlimited repeat inside a subpattern that
+       can itself be repeated an unlimited number of  times,  the  use  of  an
+       atomic  group  is  the  only way to avoid some failing matches taking a
+       very long time indeed. The pattern
+
+         (\D+|<\d+>)*[!?]
+
+       matches an unlimited number of substrings that either consist  of  non-
+       digits,  or  digits  enclosed in <>, followed by either ! or ?. When it
+       matches, it runs quickly. However, if it is applied to
+
+         aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+       it takes a long time before reporting  failure.  This  is  because  the
+       string  can  be  divided  between  the two repeats in a large number of
+       ways, and all have to be tried. (The example used [!?]  rather  than  a
+       single  character  at the end, because both PCRE and Perl have an opti-
+       mization that allows for fast failure when a single character is  used.
+       They  remember  the last single character that is required for a match,
+       and fail early if it is not present in the string.)  If the pattern  is
+       changed to
+
+         ((?>\D+)|<\d+>)*[!?]
+
+       sequences  of non-digits cannot be broken, and failure happens quickly.
+
+
+BACK REFERENCES
+
+       Outside a character class, a backslash followed by a digit greater than
+       0 (and possibly further digits) is a back reference to a capturing sub-
+       pattern earlier (that is, to its left) in the pattern,  provided  there
+       have been that many previous capturing left parentheses.
+
+       However, if the decimal number following the backslash is less than 10,
+       it is always taken as a back reference, and causes  an  error  only  if
+       there  are  not that many capturing left parentheses in the entire pat-
+       tern. In other words, the parentheses that are referenced need  not  be
+       to  the left of the reference for numbers less than 10. See the section
+       entitled "Backslash" above for further details of the handling of  dig-
+       its following a backslash.
+
+       A  back  reference matches whatever actually matched the capturing sub-
+       pattern in the current subject string, rather  than  anything  matching
+       the subpattern itself (see "Subpatterns as subroutines" below for a way
+       of doing that). So the pattern
+
+         (sens|respons)e and \1ibility
+
+       matches "sense and sensibility" and "response and responsibility",  but
+       not  "sense and responsibility". If caseful matching is in force at the
+       time of the back reference, the case of letters is relevant. For  exam-
+       ple,
+
+         ((?i)rah)\s+\1
+
+       matches  "rah  rah"  and  "RAH RAH", but not "RAH rah", even though the
+       original capturing subpattern is matched caselessly.
+
+       Back references to named subpatterns use the Python  syntax  (?P=name).
+       We could rewrite the above example as follows:
+
+         (?<p1>(?i)rah)\s+(?P=p1)
+
+       There  may be more than one back reference to the same subpattern. If a
+       subpattern has not actually been used in a particular match,  any  back
+       references to it always fail. For example, the pattern
+
+         (a|(bc))\2
+
+       always  fails if it starts to match "a" rather than "bc". Because there
+       may be many capturing parentheses in a pattern,  all  digits  following
+       the  backslash  are taken as part of a potential back reference number.
+       If the pattern continues with a digit character, some delimiter must be
+       used  to  terminate  the back reference. If the PCRE_EXTENDED option is
+       set, this can be whitespace.  Otherwise an empty comment can be used.
+
+       A back reference that occurs inside the parentheses to which it  refers
+       fails  when  the subpattern is first used, so, for example, (a\1) never
+       matches.  However, such references can be useful inside  repeated  sub-
+       patterns. For example, the pattern
+
+         (a|b\1)+
+
+       matches any number of "a"s and also "aba", "ababbaa" etc. At each iter-
+       ation of the subpattern,  the  back  reference  matches  the  character
+       string  corresponding  to  the previous iteration. In order for this to
+       work, the pattern must be such that the first iteration does  not  need
+       to  match the back reference. This can be done using alternation, as in
+       the example above, or by a quantifier with a minimum of zero.
+
+
+ASSERTIONS
+
+       An assertion is a test on the characters  following  or  preceding  the
+       current  matching  point that does not actually consume any characters.
+       The simple assertions coded as \b, \B, \A, \G, \Z,  \z,  ^  and  $  are
+       described above.  More complicated assertions are coded as subpatterns.
+       There are two kinds: those that look ahead of the current  position  in
+       the subject string, and those that look behind it.
+
+       An  assertion  subpattern  is matched in the normal way, except that it
+       does not cause the current matching position to be  changed.  Lookahead
+       assertions  start with (?= for positive assertions and (?! for negative
+       assertions. For example,
+
+         \w+(?=;)
+
+       matches a word followed by a semicolon, but does not include the  semi-
+       colon in the match, and
+
+         foo(?!bar)
+
+       matches  any  occurrence  of  "foo" that is not followed by "bar". Note
+       that the apparently similar pattern
+
+         (?!foo)bar
+
+       does not find an occurrence of "bar"  that  is  preceded  by  something
+       other  than "foo"; it finds any occurrence of "bar" whatsoever, because
+       the assertion (?!foo) is always true when the next three characters are
+       "bar". A lookbehind assertion is needed to achieve this effect.
+
+       If you want to force a matching failure at some point in a pattern, the
+       most convenient way to do it is  with  (?!)  because  an  empty  string
+       always  matches, so an assertion that requires there not to be an empty
+       string must always fail.
+
+       Lookbehind assertions start with (?<= for positive assertions and  (?<!
+       for negative assertions. For example,
+
+         (?<!foo)bar
+
+       does  find  an  occurrence  of "bar" that is not preceded by "foo". The
+       contents of a lookbehind assertion are restricted  such  that  all  the
+       strings it matches must have a fixed length. However, if there are sev-
+       eral alternatives, they do not all have to have the same fixed  length.
+       Thus
+
+         (?<=bullock|donkey)
+
+       is permitted, but
+
+         (?<!dogs?|cats?)
+
+       causes  an  error at compile time. Branches that match different length
+       strings are permitted only at the top level of a lookbehind  assertion.
+       This  is  an  extension  compared  with  Perl (at least for 5.8), which
+       requires all branches to match the same length of string. An  assertion
+       such as
+
+         (?<=ab(c|de))
+
+       is  not  permitted,  because  its single top-level branch can match two
+       different lengths, but it is acceptable if rewritten to  use  two  top-
+       level branches:
+
+         (?<=abc|abde)
+
+       The  implementation  of lookbehind assertions is, for each alternative,
+       to temporarily move the current position back by the  fixed  width  and
+       then try to match. If there are insufficient characters before the cur-
+       rent position, the match is deemed to fail.
+
+       PCRE does not allow the \C escape (which matches a single byte in UTF-8
+       mode)  to appear in lookbehind assertions, because it makes it impossi-
+       ble to calculate the length of the lookbehind.
+
+       Atomic groups can be used in conjunction with lookbehind assertions  to
+       specify efficient matching at the end of the subject string. Consider a
+       simple pattern such as
+
+         abcd$
+
+       when applied to a long string that does  not  match.  Because  matching
+       proceeds from left to right, PCRE will look for each "a" in the subject
+       and then see if what follows matches the rest of the  pattern.  If  the
+       pattern is specified as
+
+         ^.*abcd$
+
+       the  initial .* matches the entire string at first, but when this fails
+       (because there is no following "a"), it backtracks to match all but the
+       last  character,  then all but the last two characters, and so on. Once
+       again the search for "a" covers the entire string, from right to  left,
+       so we are no better off. However, if the pattern is written as
+
+         ^(?>.*)(?<=abcd)
+
+       or, equivalently,
+
+         ^.*+(?<=abcd)
+
+       there  can  be  no  backtracking for the .* item; it can match only the
+       entire string. The subsequent lookbehind assertion does a  single  test
+       on  the last four characters. If it fails, the match fails immediately.
+       For long strings, this approach makes a significant difference  to  the
+       processing time.
+
+       Several assertions (of any sort) may occur in succession. For example,
+
+         (?<=\d{3})(?<!999)foo
+
+       matches  "foo" preceded by three digits that are not "999". Notice that
+       each of the assertions is applied independently at the  same  point  in
+       the  subject  string.  First  there  is a check that the previous three
+       characters are all digits, and then there is  a  check  that  the  same
+       three characters are not "999".  This pattern does not match "foo" pre-
+       ceded by six characters, the first of which are  digits  and  the  last
+       three  of  which  are not "999". For example, it doesn't match "123abc-
+       foo". A pattern to do that is
+
+         (?<=\d{3}...)(?<!999)foo
+
+       This time the first assertion looks at the  preceding  six  characters,
+       checking that the first three are digits, and then the second assertion
+       checks that the preceding three characters are not "999".
+
+       Assertions can be nested in any combination. For example,
+
+         (?<=(?<!foo)bar)baz
+
+       matches an occurrence of "baz" that is preceded by "bar" which in  turn
+       is not preceded by "foo", while
+
+         (?<=\d{3}(?!999)...)foo
+
+       is another pattern which matches "foo" preceded by three digits and any
+       three characters that are not "999".
+
+       Assertion subpatterns are not capturing subpatterns,  and  may  not  be
+       repeated,  because  it  makes no sense to assert the same thing several
+       times. If any kind of assertion contains capturing  subpatterns  within
+       it,  these are counted for the purposes of numbering the capturing sub-
+       patterns in the whole pattern.  However, substring capturing is carried
+       out  only  for  positive assertions, because it does not make sense for
+       negative assertions.
+
+
+CONDITIONAL SUBPATTERNS
+
+       It is possible to cause the matching process to obey a subpattern  con-
+       ditionally  or to choose between two alternative subpatterns, depending
+       on the  result  of  an  assertion,  or  whether  a  previous  capturing
+       subpattern  matched  or not. The two possible forms of conditional sub-
+       pattern are
+
+         (?(condition)yes-pattern)
+         (?(condition)yes-pattern|no-pattern)
+
+       If the condition is satisfied, the yes-pattern is used;  otherwise  the
+       no-pattern  (if  present)  is used. If there are more than two alterna-
+       tives in the subpattern, a compile-time error occurs.
+
+       There are three kinds of condition. If the text between the parentheses
+       consists  of  a  sequence  of digits, the condition is satisfied if the
+       capturing subpattern of that number has previously matched. The  number
+       must  be  greater than zero. Consider the following pattern, which con-
+       tains non-significant white space to make it more readable (assume  the
+       PCRE_EXTENDED  option)  and  to  divide it into three parts for ease of
+       discussion:
+
+         ( \( )?    [^()]+    (?(1) \) )
+
+       The first part matches an optional opening  parenthesis,  and  if  that
+       character is present, sets it as the first captured substring. The sec-
+       ond part matches one or more characters that are not  parentheses.  The
+       third part is a conditional subpattern that tests whether the first set
+       of parentheses matched or not. If they did, that is, if subject started
+       with an opening parenthesis, the condition is true, and so the yes-pat-
+       tern is executed and a  closing  parenthesis  is  required.  Otherwise,
+       since  no-pattern  is  not  present, the subpattern matches nothing. In
+       other words,  this  pattern  matches  a  sequence  of  non-parentheses,
+       optionally enclosed in parentheses.
+
+       If the condition is the string (R), it is satisfied if a recursive call
+       to the pattern or subpattern has been made. At "top level", the  condi-
+       tion  is  false.   This  is  a  PCRE  extension. Recursive patterns are
+       described in the next section.
+
+       If the condition is not a sequence of digits or  (R),  it  must  be  an
+       assertion.   This may be a positive or negative lookahead or lookbehind
+       assertion. Consider  this  pattern,  again  containing  non-significant
+       white space, and with the two alternatives on the second line:
+
+         (?(?=[^a-z]*[a-z])
+         \d{2}-[a-z]{3}-\d{2}  |  \d{2}-\d{2}-\d{2} )
+
+       The  condition  is  a  positive  lookahead  assertion  that  matches an
+       optional sequence of non-letters followed by a letter. In other  words,
+       it  tests  for the presence of at least one letter in the subject. If a
+       letter is found, the subject is matched against the first  alternative;
+       otherwise  it  is  matched  against  the  second.  This pattern matches
+       strings in one of the two forms dd-aaa-dd or dd-dd-dd,  where  aaa  are
+       letters and dd are digits.
+
+
+COMMENTS
+
+       The sequence (?# marks the start of a comment which continues up to the
+       next closing parenthesis. Nested parentheses  are  not  permitted.  The
+       characters  that make up a comment play no part in the pattern matching
+       at all.
+
+       If the PCRE_EXTENDED option is set, an unescaped # character outside  a
+       character class introduces a comment that continues up to the next new-
+       line character in the pattern.
+
+
+RECURSIVE PATTERNS
+
+       Consider the problem of matching a string in parentheses, allowing  for
+       unlimited  nested  parentheses.  Without the use of recursion, the best
+       that can be done is to use a pattern that  matches  up  to  some  fixed
+       depth  of  nesting.  It  is not possible to handle an arbitrary nesting
+       depth. Perl has provided an experimental facility that  allows  regular
+       expressions to recurse (amongst other things). It does this by interpo-
+       lating Perl code in the expression at run time, and the code can  refer
+       to the expression itself. A Perl pattern to solve the parentheses prob-
+       lem can be created like this:
+
+         $re = qr{\( (?: (?>[^()]+) | (?p{$re}) )* \)}x;
+
+       The (?p{...}) item interpolates Perl code at run time, and in this case
+       refers  recursively to the pattern in which it appears. Obviously, PCRE
+       cannot support the interpolation of Perl  code.  Instead,  it  supports
+       some  special  syntax for recursion of the entire pattern, and also for
+       individual subpattern recursion.
+
+       The special item that consists of (? followed by a number greater  than
+       zero and a closing parenthesis is a recursive call of the subpattern of
+       the given number, provided that it occurs inside that  subpattern.  (If
+       not,  it  is  a  "subroutine" call, which is described in the next sec-
+       tion.) The special item (?R) is a recursive call of the entire  regular
+       expression.
+
+       For  example,  this  PCRE pattern solves the nested parentheses problem
+       (assume the  PCRE_EXTENDED  option  is  set  so  that  white  space  is
+       ignored):
+
+         \( ( (?>[^()]+) | (?R) )* \)
+
+       First  it matches an opening parenthesis. Then it matches any number of
+       substrings which can either be a  sequence  of  non-parentheses,  or  a
+       recursive  match  of  the pattern itself (that is a correctly parenthe-
+       sized substring).  Finally there is a closing parenthesis.
+
+       If this were part of a larger pattern, you would not  want  to  recurse
+       the entire pattern, so instead you could use this:
+
+         ( \( ( (?>[^()]+) | (?1) )* \) )
+
+       We  have  put the pattern into parentheses, and caused the recursion to
+       refer to them instead of the whole pattern. In a larger pattern,  keep-
+       ing  track  of parenthesis numbers can be tricky. It may be more conve-
+       nient to use named parentheses instead. For this, PCRE uses  (?P>name),
+       which  is  an  extension  to the Python syntax that PCRE uses for named
+       parentheses (Perl does not provide named parentheses). We could rewrite
+       the above example as follows:
+
+         (?P<pn> \( ( (?>[^()]+) | (?P>pn) )* \) )
+
+       This  particular example pattern contains nested unlimited repeats, and
+       so the use of atomic grouping for matching strings  of  non-parentheses
+       is  important  when  applying the pattern to strings that do not match.
+       For example, when this pattern is applied to
+
+         (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()
+
+       it yields "no match" quickly. However, if atomic grouping is not  used,
+       the  match  runs  for a very long time indeed because there are so many
+       different ways the + and * repeats can carve up the  subject,  and  all
+       have to be tested before failure can be reported.
+
+       At the end of a match, the values set for any capturing subpatterns are
+       those from the outermost level of the recursion at which the subpattern
+       value  is  set.   If  you want to obtain intermediate values, a callout
+       function can be used (see below and the pcrecallout documentation).  If
+       the pattern above is matched against
+
+         (ab(cd)ef)
+
+       the  value  for  the  capturing  parentheses is "ef", which is the last
+       value taken on at the top level. If additional parentheses  are  added,
+       giving
+
+         \( ( ( (?>[^()]+) | (?R) )* ) \)
+            ^                        ^
+            ^                        ^
+
+       the  string  they  capture is "ab(cd)ef", the contents of the top level
+       parentheses. If there are more than 15 capturing parentheses in a  pat-
+       tern, PCRE has to obtain extra memory to store data during a recursion,
+       which it does by using pcre_malloc, freeing  it  via  pcre_free  after-
+       wards.  If  no  memory  can  be  obtained,  the  match  fails  with the
+       PCRE_ERROR_NOMEMORY error.
+
+       Do not confuse the (?R) item with the condition (R),  which  tests  for
+       recursion.   Consider  this pattern, which matches text in angle brack-
+       ets, allowing for arbitrary nesting. Only digits are allowed in  nested
+       brackets  (that is, when recursing), whereas any characters are permit-
+       ted at the outer level.
+
+         < (?: (?(R) \d++  | [^<>]*+) | (?R)) * >
+
+       In this pattern, (?(R) is the start of a conditional  subpattern,  with
+       two  different  alternatives for the recursive and non-recursive cases.
+       The (?R) item is the actual recursive call.
+
+
+SUBPATTERNS AS SUBROUTINES
+
+       If the syntax for a recursive subpattern reference (either by number or
+       by  name)  is used outside the parentheses to which it refers, it oper-
+       ates like a subroutine in a programming language.  An  earlier  example
+       pointed out that the pattern
+
+         (sens|respons)e and \1ibility
+
+       matches  "sense and sensibility" and "response and responsibility", but
+       not "sense and responsibility". If instead the pattern
+
+         (sens|respons)e and (?1)ibility
+
+       is used, it does match "sense and responsibility" as well as the  other
+       two  strings.  Such  references must, however, follow the subpattern to
+       which they refer.
+
+
+CALLOUTS
+
+       Perl has a feature whereby using the sequence (?{...}) causes arbitrary
+       Perl  code to be obeyed in the middle of matching a regular expression.
+       This makes it possible, amongst other things, to extract different sub-
+       strings that match the same pair of parentheses when there is a repeti-
+       tion.
+
+       PCRE provides a similar feature, but of course it cannot obey arbitrary
+       Perl code. The feature is called "callout". The caller of PCRE provides
+       an external function by putting its entry point in the global  variable
+       pcre_callout.   By default, this variable contains NULL, which disables
+       all calling out.
+
+       Within a regular expression, (?C) indicates the  points  at  which  the
+       external  function  is  to be called. If you want to identify different
+       callout points, you can put a number less than 256 after the letter  C.
+       The  default  value is zero.  For example, this pattern has two callout
+       points:
+
+         (?C1)abc(?C2)def
+
+       During matching, when PCRE reaches a callout point (and pcre_callout is
+       set),  the  external function is called. It is provided with the number
+       of the callout, and, optionally, one item of data  originally  supplied
+       by  the  caller of pcre_exec(). The callout function may cause matching
+       to backtrack, or to fail altogether.  A  complete  description  of  the
+       interface  to the callout function is given in the pcrecallout documen-
+       tation.
+
+
+DIFFERENCES FROM PERL
+       This section escribes the differences in the ways that PCRE and Perl
+       handle regular expressions. The differences  described  here  are  with
+       respect to Perl 5.8.
+
+       1.  PCRE does not have full UTF-8 support. Details of what it does have
+       are given in the section on UTF-8 support in the main pcre page.
+
+       2. PCRE does not allow repeat quantifiers on lookahead assertions. Perl
+       permits  them,  but they do not mean what you might think. For example,
+       (?!a){3} does not assert that the next three characters are not "a". It
+       just asserts that the next character is not "a" three times.
+
+       3.  Capturing  subpatterns  that occur inside negative lookahead asser-
+       tions are counted, but their entries in the offsets  vector  are  never
+       set.  Perl sets its numerical variables from any such patterns that are
+       matched before the assertion fails to match something (thereby succeed-
+       ing),  but  only  if the negative lookahead assertion contains just one
+       branch.
+
+       4. Though binary zero characters are supported in the  subject  string,
+       they are not allowed in a pattern string because it is passed as a nor-
+       mal C string, terminated by zero. The escape sequence "\0" can be  used
+       in the pattern to represent a binary zero.
+
+       5.  The  following Perl escape sequences are not supported: \l, \u, \L,
+       \U, \P, \p, \N, and \X. In fact these are implemented by Perl's general
+       string-handling and are not part of its pattern matching engine. If any
+       of these are encountered by PCRE, an error is generated.
+
+       6. PCRE does support the \Q...\E escape for quoting substrings. Charac-
+       ters  in  between  are  treated as literals. This is slightly different
+       from Perl in that $ and @ are  also  handled  as  literals  inside  the
+       quotes.  In Perl, they cause variable interpolation (but of course PCRE
+       does not have variables). Note the following examples:
+
+           Pattern            PCRE matches      Perl matches
+
+           \Qabc$xyz\E        abc$xyz           abc followed by the
+                                                  contents of $xyz
+           \Qabc\$xyz\E       abc\$xyz          abc\$xyz
+           \Qabc\E\$\Qxyz\E   abc$xyz           abc$xyz
+
+       The \Q...\E sequence is recognized both inside  and  outside  character
+       classes.
+
+       7. Fairly obviously, PCRE does not support the (?{code}) and (?p{code})
+       constructions. However, there is some experimental support  for  recur-
+       sive  patterns  using the non-Perl items (?R), (?number) and (?P>name).
+       Also, the PCRE "callout" feature allows  an  external  function  to  be
+       called during pattern matching.
+
+       8.  There  are some differences that are concerned with the settings of
+       captured strings when part of  a  pattern  is  repeated.  For  example,
+       matching  "aba"  against  the  pattern  /^(a(b)?)+$/  in Perl leaves $2
+       unset, but in PCRE it is set to "b".
+
+       9. PCRE  provides  some  extensions  to  the  Perl  regular  expression
+       facilities:
+
+       (a)  Although  lookbehind  assertions  must match fixed length strings,
+       each alternative branch of a lookbehind assertion can match a different
+       length of string. Perl requires them all to have the same length.
+
+       (b)  If PCRE_DOLLAR_ENDONLY is set and PCRE_MULTILINE is not set, the $
+       meta-character matches only at the very end of the string.
+
+       (c) If PCRE_EXTRA is set, a backslash followed by a letter with no spe-
+       cial meaning is faulted.
+
+       (d)  If  PCRE_UNGREEDY is set, the greediness of the repetition quanti-
+       fiers is inverted, that is, by default they are not greedy, but if fol-
+       lowed by a question mark they are.
+
+       (e)  PCRE_ANCHORED  can  be used to force a pattern to be tried only at
+       the first matching position in the subject string.
+
+       (f) The PCRE_NOTBOL, PCRE_NOTEOL, PCRE_NOTEMPTY, and  PCRE_NO_AUTO_CAP-
+       TURE options for pcre_exec() have no Perl equivalents.
+
+       (g)  The (?R), (?number), and (?P>name) constructs allows for recursive
+       pattern matching (Perl can do  this  using  the  (?p{code})  construct,
+       which PCRE cannot support.)
+
+       (h)  PCRE supports named capturing substrings, using the Python syntax.
+
+       (i) PCRE supports the possessive quantifier  "++"  syntax,  taken  from
+       Sun's Java package.
+
+       (j) The (R) condition, for testing recursion, is a PCRE extension.
+
+       (k) The callout facility is PCRE-specific.
+
+
+
+NOTES
+        The \< and \> metacharacters from Henry Spencers package
+        are not available in PCRE, but can be emulate with \b,
+        as required, also in conjunction with \W or \w.
+
+        In LDMud, backtracks are limited by the EVAL_COST runtime
+        limit, to avoid freezing the driver with a match
+        like regexp(({"=XX==================="}), "X(.+)+X").
+
+        LDMud doesn't support PCRE callouts.
+
+
+LIMITATIONS
+        There are some size limitations in PCRE but it is hoped that
+        they will never in practice be relevant.  The maximum length
+        of a compiled pattern is 65539 (sic) bytes.  All  values  in
+        repeating  quantifiers  must be less than 65536.  There max-
+        imum number of capturing subpatterns is 65535.  There is  no
+        limit  to  the  number of non-capturing subpatterns, but the
+        maximum depth of nesting of all kinds of parenthesized  sub-
+        pattern,  including  capturing  subpatterns, assertions, and
+        other types of subpattern, is 200.
+
+        The maximum length of a subject string is the largest  posi-
+        tive number that an integer variable can hold. However, PCRE
+        uses recursion to handle subpatterns and indefinite  repeti-
+        tion.  This  means  that the available stack space may limit
+        the size of a subject string that can be processed  by  cer-
+        tain patterns.
+
+
+AUTHOR
+        Philip Hazel <ph10@cam.ac.uk>
+        University Computing Service,
+        New Museums Site,
+        Cambridge CB2 3QG, England.
+        Phone: +44 1223 334714
+
+SEE ALSO
+        regexp(C), hsregexp(C)
diff --git a/doc/concepts/pgsql b/doc/concepts/pgsql
new file mode 100644
index 0000000..12f2cc4
--- /dev/null
+++ b/doc/concepts/pgsql
@@ -0,0 +1,88 @@
+CONCEPT
+        pgsql - PostgreSQL support
+
+DESCRIPTION
+        On hosts with the PostgreSQL package installed, the driver can be
+        configured to interface with the PostgreSQL database. If that is done,
+        the driver defines the macro __PGSQL__ for LPC programs and
+        activates a number of related efuns.
+
+        -- Usage --
+
+        The interface to the PostgreSQL database is implemented
+        through the concept of a controlling object: when opening a
+        database connection, the LPC code has to provide a callback
+        function. The object this function is bound to is the
+        controlling object: all queries to the database will be issued
+        by this object, and the responses will be sent to the callback
+        function.
+
+        The interface is also asynchronous: the pg_query() efun just
+        queues the query with the database connection, and returns
+        immediately. When the database has finished working the query,
+        the callback function is called with the results.
+
+        The callback function can be defined by name or by closure,
+        and can be defined with extra parameters:
+
+
+          #include <pgsql.h>
+
+          void <callback>(int type, mixed ret, int id [, mixed extra...])
+
+            <type> is the type of the call, <id> identifies the query
+            for which this call is executed:
+
+              PGRES_TUPLES_OK: <ret> is the result from a query.
+                               It is either a mapping (field name as
+                               key, indexing <n> values for n returned
+                               tuples), or an array of arrays (one per
+                               row).
+
+              PGRES_COMMAND_OK: <ret> is a string which contains the
+                                server response (e.g. on INSERT or DELETE) 
+
+              PGRES_BAD_RESPONSE,
+              PGRES_NONFATAL_ERROR,
+              PGRES_FATAL_ERROR: ret is the error-string
+
+
+          void <callback>(int type, mixed ret [, mixed extra...])
+
+            <type> is the type of the call, which is not related a
+            specific query:
+
+              PGCONN_SUCCESS: The database-connection was established,
+                              <ret> is a dummy string.
+              PGCONN_FAILED:  The database-connection failed, <ret> is
+                              the error message.
+                The first message to the callback after a call to
+                pg_connect() is always one of these two.
+
+              PGRES_NOTICE: <ret> is a informational text.
+
+              PGCONN_ABORTED: If the connection to the backend fails
+                              we try to re-establish (reset) it. If the
+                              reset fails, the connection is closed and
+                              this value is returned. Consider the
+                              connection gone and don't try to close or 
+                              otherwise operate further on it.
+                              <ret> is a dummy string.
+
+        -- Security --
+
+        All SQL efuns (unless execute by the master or the simul-efun object)
+        trigger a privilege_violation ("pgsql", "<efun_name>"). If a more
+        finegrained control is desired, overload the individual efuns with a
+        nomask simul-efun.
+
+AUTHOR
+        Florian Heinz and others.
+
+HISTORY
+        Added as package in LDMud 3.3.445.
+        LDMud 3.3.640 added a privilege_violation() call for each efun.
+
+SEE ALSO
+        mysql(C), pg_connect(E), pg_conv_string(E), pg_query(E), pg_pending(E),
+        pg_close(E), privilege_violation(M)
diff --git a/doc/concepts/properties b/doc/concepts/properties
new file mode 100644
index 0000000..1ab418a
--- /dev/null
+++ b/doc/concepts/properties
@@ -0,0 +1,109 @@
+Properties
+ BESCHREIBUNG:
+     Im Gegensatz zu Variablen innerhalb eines Objektes, kann man Properties
+     von aussen veraendern, ohne eine besondere Funktion geschrieben zu haben.
+
+     1. Das zugrundeliegende Prinzip
+     ===============================
+      Das grundlegende Konzept der MUDlib ist, dass wichtige, objektbezogene
+      Informationen in den sogenannnten Properties gespeichert werden (engl.
+      property -- Eigenschaft, Eigentum).
+
+      Diese Informationen koennen einfache Werte, wie z.B. Zahlen, Zeichen oder
+      Objekte, aber auch kompliziertere Strukturen sein.
+      Jedes Objekt kann beliebig viele solcher Properties besitzen und deren
+      Namensgebung ist nicht nur auf die von der MUDlib bereitgestellten
+      Standardproperties begrenzt. Das heisst, das fuer eigene Anwendungen die
+      Menge der Properties fuer ein Objekt beliebig erweitert werden kann.
+      Damit sind auch schon die beiden Hauptmerkmale einer Property ange-
+      sprochen:
+
+	a) ein Name oder Kennung und
+	b) ein Wert, der durch den Namen repraesentiert wird.
+
+      Das reine Verwalten einer Property mit Namen und Wert ist aber nicht sehr
+      sinnvoll und so gehoeren zu jeder Property noch zwei weitere wichtige
+      Dinge. Zu jeder Property wurden jeweils zwei Operationen eingefuehrt,
+      welche den uebergebenen Wert vor der Speicherung oder Abfrage bearbeiten.
+
+      Zusammenfassend laesst sich das Konzept der Property in folgendem Schema
+      darstellen:
+
+               +-------------------------------------------+
+               | Property                                  |
+               +-------------------------------------------+
+               | privater Datenbereich (Property Werte)    |
+               +-------------------------------------------+
+               | Direktzugriff auf den Datenbereich        |
+               +-------------------------------------+     |
+               |     ^       Methoden       v        | ^ v |
+               |   Setzen       |        Abfragen    |     |
+               +-------------------------------------+-----+
+                     ^                      |
+	             |                      V
+                  SetProp()             QueryProp()
+
+      Aus dem Schema laesst sich Folgendes erkennen:
+      - beim Setzen und Abfragen wird der Wert einer Methode uebergeben, die
+	den Wert zurueckgibt oder ggf. die Aenderungen vornimmt
+      - ein direkter Zugriff auf den Wert der ist ebenfalls moeglich, sollte
+	aber nicht der Normalfall sein, da die Methoden Nebeneffekte erzeugen
+        - in bestimmten Faellen kann man den aeusserlich aendernden Zugriff
+	  vollkommen unterbinden (NOSETMETHOD, PROTECT)
+	  (VORSICHT bei mappings/arrays, diese werden bei QueryProp()
+	   als Referenz zurueckgegeben, sind also so aenderbar)
+
+     2. Implementation
+     =================
+
+      Die Klasse /std/thing/properties.c stellt folgende Funktionen fuer die
+      Behandlung von Properties bereit:
+
+      Normaler Zugriff:	mixed SetProp(<name>, <wert>)
+			- setzt den Wert von <name> auf <wert>
+			mixed QueryProp(<name>)
+			- gibt den Wert von <name> zurueck
+ 
+      Direkter Zugriff:	mixed Set(<name>, <wert>, <interpretation>)
+			- setzt fuer <name> einen <wert>:
+			  - den normalen Wert
+			    <interpretation>: F_VALUE (==0)
+			  - eine Methode
+			    <wert>: closure
+			    <interpretation>: F_SET_METHOD, F_QUERY_METHOD
+			  - ein Flag
+			    <wert>: SAVE, SECURED, PROTECTED, NOSETMETHOD
+			    <interpretation>: F_MODE, F_MODE_AS, F_MODE_AD
+			mixed Query(<name>, <interpretation>)
+			- fragt fuer <name> einen <wert> ab
+			  - F_SET_METHOD, F_QUERY_METHOD: die Closure/0
+			  - F_MODE: das (veroderte!) Flag
+      Global:		void SetProperties(<mapping>)
+			- setzt das Mapping komplett, beachtet >= PROTECTED
+			mapping QueryProperties()
+			- fragte das komplette Mapping als Kopie ab
+
+     3. Besonderheiten/Eingebaute Properties:
+
+      Existiert zu einer Property eine Funktion mit dem selben Namen und einem
+      "_set_" bzw "_query_" davor, so wird nicht auf die das Property-Mapping
+      zugegriffen, sondern es werden die Argumente an diese Funktion uebergeben
+      und der Rueckgabewert dieser Funktion zurueckgegeben.
+      Vorteil:
+      - so kann man Daten, die schnell verfuegbar sein muessen,	(bei denen
+        also Effizienz gegen SetProp/QueryProp spricht) trotzdem nach aussen
+	einheitlich zugreifbar machen
+      Nachteil:
+      - nicht wirklich sauber
+      - Speichern muss man selbst vornehmen
+      - Set/Query gehen wie auch bei Methoden an _set_*/_query_* vorbei
+      - dieses Verhalten sollte der Mudlib vorbehalten bleiben, fuer eigene
+        Prueffunktionen (wird etwas gesetzt/abgefragt) bzw. Aenderungen
+	sollte man Methoden (F_SET_METHOD/F_QUERY_METHOD) benutzen
+
+ SIEHE AUCH:
+     SetProp(L), QueryProp(L), Set(L), Query(L), SetProperties(L),
+     QueryProperties(L)
+     objekte, effizienz, closures
+
+ 21. Maerz 2004 Gloinson
diff --git a/doc/concepts/regexp b/doc/concepts/regexp
new file mode 100644
index 0000000..9a9d70e
--- /dev/null
+++ b/doc/concepts/regexp
@@ -0,0 +1,121 @@
+SYNOPSIS
+        Regular Expressions
+
+
+DESCRIPTION
+        LDMud supports both the traditional regular expressions as
+        implemented by Henry Spencer ("HS" or "traditional"), and the
+        Perl-compatible regular expressions by Philip Hazel ("PCRE").
+        Both packages can be used concurrently, with the selection
+        being made through extra option flags argument to the efuns.
+        One of the two packages can be selected at compile time, by
+        commandline argument, and by driver hook to be the default
+        package.
+
+        The packages differ in the expressivity of their expressions
+        (PCRE offering more options that Henry Spencer's package),
+        though they both implement the common subset outlined below.
+
+        All regular expression efuns take an additional options
+        parameter, which is a an number composed of bitflags, and is
+        used to modify the exact behaviour of the expression
+        evaluation. In addition, certain efuns may accept additional
+        specific options.
+
+        For details, refer to the detailed manpages: hsregexp(C) for
+        the Henry Spencer package, pcre(C) for the PCRE package.
+
+
+REGULAR EXPRESSION DETAILS
+        A regular expression is a pattern that is matched against  a
+        subject string from left to right. Most characters stand for
+        themselves in a pattern, and match the corresponding charac-
+        ters in the subject. As a trivial example, the pattern
+
+          The quick brown fox
+
+        matches a portion of a subject string that is  identical  to
+        itself.  The  power  of  regular  expressions comes from the
+        ability to include alternatives and repetitions in the  pat-
+        tern.  These  are encoded in the pattern by the use of meta-
+        characters, which do not stand for  themselves  but  instead
+        are interpreted in some special way.
+
+        The following metacharacters are 'universal' in that both regexp
+        packages understand them in the same way:
+
+          .       Match any character.
+
+          ^       Match begin of line.
+
+          $       Match end of line.
+
+          x|y     Match regexp x or regexp y.
+
+          ()      Match enclosed regexp like a 'simple' one.
+
+          x*      Match any number (0 or more) of regexp x.
+
+          x+      Match any number (1 or more) of regexp x.
+
+          [..]    Match one of the characters enclosed.
+
+          [^ ..]  Match none of the characters enclosed. The .. are to
+                  replaced by single characters or character ranges:
+
+          [abc]   matches a, b or c.
+
+          [ab0-9] matches a, b or any digit.
+
+          [^a-z]  does not match any lowercase character.
+
+          \B      not a word boundary
+
+          \c      match character c even if it's one of the special
+                  characters.
+
+        The following metacharacters or metacharacter combinations implement
+        similar functions in the two regexp packages;
+
+          \b      PCRE: word boundary, also used inconjunction with
+                        \w (any "word" character) and \W (any "non-word"
+                        character).
+
+          \<      HS: Match begin of word.
+          \>      HS: Match end of word.
+
+
+OPTIONS
+        The package is selected with these option flags:
+
+          RE_PCRE
+          RE_TRADITIONAL
+
+        These flags are also used for the H_REGEXP_PACKAGE driver
+        hook.
+
+
+        Traditional regular expressions understand one option:
+
+          RE_EXCOMPATIBLE
+
+
+        PCRE understands these options:
+
+          RE_ANCHORED
+          RE_CASELESS
+          RE_DOLLAR_ENDONLY
+          RE_DOTALL
+          RE_EXTENDED
+          RE_MULTILINE
+          RE_UNGREEDY
+          RE_NOTBOL
+          RE_NOTEOL
+          RE_NOTEMPTY
+
+HISTORY
+        LDMud 3.3.596 implemented the concurrent use of both packages.
+
+SEE ALSO
+        hsregexp(C), pcre(C), regexp_package(H), regexp(E), regexplode(E),
+        regmatch(E), regreplace(E), regexp_package(E), invocation(D)
diff --git a/doc/concepts/rtfm b/doc/concepts/rtfm
new file mode 100644
index 0000000..0c566fe
--- /dev/null
+++ b/doc/concepts/rtfm
@@ -0,0 +1,33 @@
+CONCEPT
+        rtfm - read the fucking manual
+
+UPDATE
+        Mateese, 15-Jun-93, 03:15 MET
+
+SYNOPSIS
+        rtfm
+
+        OPTIONS None, you have to read the manual for an answer.
+
+DESCRIPTION
+        Used when lazy people ask stupid  questions.  Normaly  cried
+        out in vain.
+
+FILES
+        /dev/null
+
+ENVIRONMENT
+        Any.
+
+CREDITS
+        Bert Nase, who else?
+
+SEE ALSO
+        man(H)
+
+DIAGNOSTICS
+        Is an diagnostic. Since you are reading this you are getting
+        the idea.
+
+BUGS
+        Ha!
diff --git a/doc/concepts/secure b/doc/concepts/secure
new file mode 100644
index 0000000..a294a5e
--- /dev/null
+++ b/doc/concepts/secure
@@ -0,0 +1,52 @@
+THEMA:
+	secure-Verzeichnisse
+
+FUNKTION:
+	Magier haben die Moeglichkeit in ihren Gilden oder Regions-
+	verzeichnissen /secure Verzeichnisse anzulegen, in denen Daten dann
+	vor Lesezugriffen anderer Magier geschuetzt sind. Leserechte in diesen
+	Verzeichnissen haben grundsaetzlich nur diejenigen, die dort auch
+	Schreibrechte haben. Diese Verzeichnisse sind fuer (Quest-)Raetsel oder
+	schwierige NPCs mit vielen Stufenpunkten gedacht. Diese Verzeichnisse
+	sind ausdruecklich _nicht_ dazu gedacht, dort ganze Gebiete oder Quests
+	abzulegen, da bei Problemen andere Magier nur noch sehr schwer helfen
+	koennen. Aus diesem Grund sind die Regionsmagier gehalten darauf zu
+	achten, dass diese Verzeichnisse nur mit Bedacht verwendet werden.
+	Sollte das ganze ausarten und uebertrieben werden, so wird der Schutz
+	der secure Verzeichnisse in den betroffenen Gebieten/Regionen wieder
+	aufgehoben!
+
+HINWEIS:
+	Es ist _nicht_ moeglich ganze Verzeichnisbaeume in ein secure/
+	Verzeichnis abzulegen. Anders formuliert:
+  Unterverzeichnisse von secure/ geniessen _keinen_ besonderen Schutz.
+
+BEISPIEL:
+
+	o richtiger Einsatz in einem fiktiven standard include file...
+
+	  #define HOME(x)    "/d/region/magiername/meingebiet/"+x
+	  #define NPC(x)     HOME("npc/"+x)
+     -> /d/region/magiername/meingebiet/npc/
+	  #define OBJ(x)     HOME("obj/"+x)
+     -> /d/region/magiername/meingebiet/obj/
+	  #define ROOM(x)    HOME("room/"+x)
+     -> /d/region/magiername/meingebiet/room/
+	  #define SECURE(x)  HOME("secure/"+x)
+     -> /d/region/magiername/meingebiet/secure/
+
+
+	o falscher (wirkungsloser) Einsatz mit einem Verzeichnisbaum:
+
+	  #define HOME(x)    "/d/region/magiername/meingebiet/secure/"+x
+	  #define NPC(x)     HOME("npc/"+x)
+     -> /d/region/magiername/meingebiet/secure/npc/
+	  #define OBJ(x)     HOME("obj/"+x)
+     -> /d/region/magiername/meingebiet/secure/obj/
+	  #define ROOM(x)    HOME("room/"+x)
+     -> /d/region/magiername/meingebiet/secure/room/
+
+
+LETZTE AeNDERUNG:
+  04.09.2011 Zesstra
+
diff --git a/doc/concepts/simul_efun b/doc/concepts/simul_efun
new file mode 100644
index 0000000..8c4a2a0
--- /dev/null
+++ b/doc/concepts/simul_efun
@@ -0,0 +1,14 @@
+CONCEPT
+        simul_efun
+
+DESCRIPTION
+        The simul_efun object is automagically sort-of inherited by
+        every object. That functions that are defined in it can be
+        accessed just like efuns or inherited functions by every
+        object (except the master object). To get access to efuns that
+        are overloaded by the simul_efun object, you can use the
+        efun::function() to bypass the simul_efun (unless the
+        simul_efun object has defined the function as ``nomask'').
+
+SEE ALSO
+        get_simul_efun(M), inheritance(LPC), operators(LPC)
diff --git a/doc/concepts/terminals b/doc/concepts/terminals
new file mode 100644
index 0000000..3dd3171
--- /dev/null
+++ b/doc/concepts/terminals
@@ -0,0 +1,18 @@
+terminals
+ BESCHREIBUNG:
+     Ein Spieler kann sein Terminal mit "stty" in verschiedene Modi
+     schalten. Mit ANSI-Escape-Sequenzen kann man dementsprechend dann
+     diesem Spieler farbige, unterstrichene, blinkende Texte auf den
+     Bildschirm zaubern, wenn dieser es unterstuetzt.
+
+     Die derzeit unterstuetzten Terminals sind:
+	dumb:   bitte keine Escapesequenzen nutzen ...
+	vt100:  versteht reverse, bold, blinking
+	ansi:   versteht vt100 und Farben
+
+     Die nutzbaren Farbcodes sind unter /sys/ansi.h definiert.
+
+ SIEHE AUCH:
+     P_TTY, stty
+
+ 21. Maerz 2004 Gloinson
diff --git a/doc/concepts/ticks b/doc/concepts/ticks
new file mode 100644
index 0000000..a30f18f
--- /dev/null
+++ b/doc/concepts/ticks
@@ -0,0 +1,66 @@
+* Was sind Ticks?
+Ein Tick ist eine Masseinheit, die man frueher eingefuehrt hat, um zu
+verhindern, dass ein Objekt/Magier den Driver beliebig lang beschaeftigen
+kann und niemand anders mehr zum Zuge kommt.
+
+* Sind es also Zeiteinheiten? Oder Rechenoperationen?
+Am ehesten entspricht ein Tick der Rechenoperation. Besser gesagt: jeder
+LPC-Operator, jedes LPC-Schluesselwort und jede efun, die man ruft, verringert
+die zur Verfuegung stehenden Ticks um mindestens 1: Sowas wie +, -, if(),
+else, switch(), etc.
+Hierbei stehen die Ticks, die ein Stueck Code braucht und die Zeit, die dessen
+Ausfuehrung braucht, in keinem konstanten Verhaeltnis. Ein Stueck Code
+braucht immer die gleiche definierte Menge an Ticks, aber kann dafuer
+unterschiedliche Zeiten benoetigen (z.b. wenn man erst was einswappen muss).
+Ebenso brauchen 2 Stueck Code, die die gleiche Menge Ticks brauchen, oft
+unterschiedlich lang.
+
+* Wie messe ich Ticks?
+Die Funktion get_eval_cost() liefert einem zurueck, wieviele Ticks man in
+diesem Ausfuehrungsthread man noch verbraten darf, bis die Ausfuehrung
+abgebrochen wird. Will man wissen, wieviel etwas kostet, ruft man vorher
+get_eval_cost(), merkt sich das und vergleicht mit dem Ergebnis von
+get_eval_cost() danach.
+
+* Wieviel ticks verbraucht diese und diese Operation/Funktion?
+Jede elementare Operation verbraucht erstmal einen Tick. Aber: es gibt Efuns
+und Operatoren, die sehr grosse Datenmengen manipulieren (koennen). Damit man
+mit diesen nicht megabyteweise Daten fuer einen Tick manipulieren kann, gibt
+es in LDMud die sog. dynamischen Evalcosts, bei denen je nach Umfang der
+manipulierten Daten Ticks abgezogen werden. Bsp: str1 + str2 kostet umso mehr,
+je groesser str1 und str2 sind.
+Wenn man es genau wissen will, sollte man per get_eval_cost() messen, dies ist
+natuerlich fuer die dynamischen Evalcosts schwieriger. Ggf. fragt den EM fuer
+Driver/Mudlib, ob er das weiss.
+
+* Wieviel stehen dem Driver pro Heartbeat zur Verfuegung? 
+Das ist so global nicht zu beantworten. Aber: pro Ausfuehrungsthread stehen
+uns momentan 1500000 Ticks zur Verfuegung. So ein Ausfuehrungsthread startet
+z.B., wenn der Driver in einem Objekt heart_beat() ruft, auch reset(),
+clean_up() oder wenn der Driver ein Spielerkommando auswertet. Callouts sind
+ein Spezialfall, hierbei teilen sich die "gleichzeitig" (im gleichen
+Backend-Zyklus des Drivers) und unter der gleichen UID ausgefuehrten Callouts
+letztendlich die Ticks.
+
+* Was passiert, wenn es doch mal nicht reicht?
+In dem Fall gibt es den beruehmt-beruechigten 'too long evaluation'-Fehler
+(TLE) und die Ausfuehrung wird an der Stelle abgebrochen, wo die Anzahl an
+verfuegbaren Ticks auf 0 faellt.
+
+* Wie finde ich heraus, wieviel Laufzeit eine bestimmte Operation benoetigt?
+Hierbei helfen einem die verbrauchten Ticks nicht weiter. Um die Laufzeit
+eines Stuecks Code zu bestimmen, misst man vorher die aktuelle Zeit mit
+genuegend grosser Genauigkeit, fuehrt seinen Code, misst erneut die Zeit und
+bildet die Differenz. Die genaueste Moeglichkeit der Zeitmessung im Mud stellt
+die efun utime() dar, welche die Zeit in Mikrosekunden ermitteln kann.
+Beispiel:
+  int *zeit1 = utime();
+  // code ausfuehren
+  int *zeit2 = utime();
+  int usec = (zeit2[0] - zeit1[0]) * 1000000 - zeit1[1] + zeit2[1];
+
+SIEHE AUCH:
+  effizienz, memory, goodstyle
+
+04.09.2008, Zesstra
+
diff --git a/doc/concepts/tls b/doc/concepts/tls
new file mode 100644
index 0000000..25946a2
--- /dev/null
+++ b/doc/concepts/tls
@@ -0,0 +1,73 @@
+PRELIMINARY
+CONCEPT
+        tls (transport layer security)
+
+DESCRIPTION
+	TLS stands for Transport Layer Security which is the successor
+	of the well known SSL (Secure Socket Layer). Both techniques 
+	provide a way to authenticate and encrypt the data send through
+	a network connection.
+	By enabling TLS during compilation of the driver you can provide
+	a secure channel into the mud to your players.
+	In difference to other solutions as "sslwrap" or "stunnel" the
+	driver integrated approach has the advantage that the mud sees 
+	the real IP of the player, not the IP of the local mud host.
+
+USAGE
+	To use TLS configure your driver with --enable-tls option.
+	After starting your driver you have five new efuns
+	(tls_init_connection(), tls_deinit_connection(), tls_error(),
+	 tls_query_connection_info(), tls_query_connection_state()).
+
+	You can switch on TLS by calling tls_init_connection().
+	This can happen in three ways:
+
+	1) in telnet_neg()
+
+            Advantage of this method is that you can offer TLS on a normal 
+            mud port. If you have a limited number of ports this can 
+            become important. The TLS connection will be started by 
+            the client with help of telnet option STARTTLS. Currently
+            there are no mudclients that support this method.
+            
+            You will have to implement the telnet option STARTTLS (46) for
+            this method. The draft for this can be found here:
+            http://www.ietf.org/proceedings/99mar/I-D/draft-ietf-tn3270e-telnet-tls-01.txt
+            Call tls_init_connection() to initiate the TLS handshake.
+
+
+	2) in master_ob->connect()
+
+            Advantage of this method is that your users can connect with
+            any program that supports TLS/SSL. Examples are telnet-ssl,
+            sslwrap or stunnel. Disadvantage is that you have to spend
+            a dedicated port for this.
+            
+            You have to call tls_init_connection() as first command
+            after the player connected (normally in master_ob->connect())
+
+        3) in an interactive object using a callback.
+
+            This method is similar to method (1), but not limited to
+            telnet: it is useful for implementing protocols thta use
+            STARTTLS like SMTP or IMAP. tls_init_connection() can be
+            called at any time by the interactive object.
+
+            You must not write to the connection after calling this
+            efun until the callback is executed (the prompt will
+            be supressed automatically during this time).
+
+        To test your code, you can use the openssl binary.
+        `openssl s_client -connect host:port' should display your certificate
+        and anything you write after the callback is executed. If you
+        encounter the error message `SSL3_GET_RECORD: wrong version number'
+        you're probably writing to the connection while you should not.
+
+BUG
+        This manpage might be not quite up to date with the implementation.
+
+HISTORY
+        Introduced in LDMud 3.3.474 and following, backported to 3.2.11.
+
+SEE ALSO
+        tls_* efuns
diff --git a/doc/concepts/uids b/doc/concepts/uids
new file mode 100644
index 0000000..724a7af
--- /dev/null
+++ b/doc/concepts/uids
@@ -0,0 +1,96 @@
+CONCEPT
+        uids (userids)
+
+DESCRIPTION
+        Every object in the mud is attributed with a user-id 'uid': a string
+        which associates the object with a certain 'user' (aka 'wizard' or
+        'creator', though it is not limited to that). The uid can be 0, which
+        internally is the default-uid.
+
+        The uid serves a dual purpose: on the on hand it is used to gather
+        statistics about the various groups of objects (in the famous
+        'wizlist'), on the other hand the uid can come in handy in the
+        implementation of security systems.
+
+        The uid of an object is assigned at its creation through the
+        driver hooks H_LOAD_UIDS for loaded objects, and H_CLONE_UIDS
+        for cloned objects, and can't be changed afterwards.
+
+        The uid of an object can be queried with the efun getuid() (resp.
+        creator() in compat-mode).
+
+
+        Every object also has a second string attribute, the 'effective
+        userid' or 'euid', which also may be 0. This value was intended to
+        implement a security system based on difference between theoretical
+        and effective permissions. Since the effectiveness of this system is
+        doubtful, the driver enforces such a use only as an option.
+
+        As uids, euids are assigned at an objects creation through
+        the two aformentioned driverhooks. They can be queried with
+        the efun geteuid() and changed with the efun seteuid(). Calls
+        to the latter are verified by the master lfun valid_seteuid().
+
+        Additionally objects can impose their uid onto an other objects
+        euid with the efun export_uid().
+
+
+        If the driver is run in 'strict euids' mode, euids are taken
+        more seriously than being just another attribute:
+          - all objects must have a non-0 uid.
+          - objects with a 0 euid can't load or clone other objects.
+          - the backbone uid as returned by master::get_bb_uid() must
+            not be 0.
+
+
+        Userids are assigned at the time of the creation of an object
+        by calling the driverhooks H_LOAD_UIDS and H_CLONE_UIDS:
+
+            mixed <load_uids closure> (string objectname)
+            mixed <clone_uids closure>(object blueprint, string objectname)
+
+        When an object is newly loaded, the H_LOAD_UIDS hook is
+        called with the object name as argument.
+        When an object is cloned, the H_CLONE_UIDS hook is called
+        with the blueprint object as first and the clone's designated
+        name as second argument.
+        In both cases the new object already exists, but has 0 uids.
+
+        For the result, the following possibilities exist (<num> is
+        a non-zero number, <no-string> is anything but a string):
+
+             "<uid>"                    -> uid = "<uid>", euid = "<uid>"
+             ({ "<uid>", "<euid>" })    -> uid = "<uid>", euid = "<euid>"
+             ({ "<uid>", <no-string> }) -> uid = "<uid>", euid = 0
+
+        If strict-euids is not active, the following results are
+        possible, too:
+
+             <num>                      -> uid = 'default', euid = 0
+             ({ <num>, "<euid>" })      -> uid = 'default', euid = "<euid>"
+             ({ <num>, <no-string> })   -> uid = 'default', euid = 0
+
+
+
+        Slightly different rules apply to the (e)uid of the master.
+        The masters (e)uid is determined by a call to
+        master->get_master_uid():
+
+             "<uid"> -> uid = "<uid>", euid = "<uid>"
+
+        In non-strict-euids mode, more results are possible:
+
+             0       -> uid = 0, euid = 0
+             <num>   -> uid = 'default', euid = 0
+
+        If your uids are in general based on filenames, it is wise to return a
+        value here which can not be legally generated from any filename.  OSB
+        for example uses 'ze/us'.
+
+        The masters uid is determined only on startup this way, at runtime the
+        uids of a reloaded master determined as for every object by a call to
+        the appropriate driver hooks.
+
+SEE ALSO
+        native(C), get_root_uid(M), valid_seteuid(M),
+        objects(C), clone_object(E), geteuid(E), getuid(E), seteuid(E)
diff --git a/doc/driver/codestyle b/doc/driver/codestyle
new file mode 100644
index 0000000..78a05b6
--- /dev/null
+++ b/doc/driver/codestyle
@@ -0,0 +1,247 @@
+    The LPMud gamedriver is by nature the result of the cooperative work
+    of multiple programmers, often separated by large oceans and years
+    of time. In order to keep the appearance of the driver source consistent
+    (and with that maintainable), the following guidelines should be followed
+    for all code contributions.
+
+    For a quick start in how good driver source should look like, take
+    a look at comm.{c,h}, object.{c,h} and mapping.{c.h}.
+
+    The guidelines have a strong emphasis on code layout and commenting,
+    stemming from the fact that proper layout and comments gave the
+    incentive for LDMud in the first place. Right now, 50% of all lines
+    are comments, and that has been proven to be a Good Thing.
+
+    Any reader of the "real programmers don't write comments"-school
+    of thought is asked to take his or her incompetence elsewhere.
+
+
+Language
+--------
+    The language is ISO Standard C (also known as 'C89' or 'ANSI C').
+    Common compiler extensions or features from the new C99 standard are
+    permitted if their addition is transparent for other C89 compilers.
+    For example: the 'inline' keyword permitted through the use of the
+    INLINE macro; so are the Metrowerks-pragmas and GNU-attributes. Not
+    permitted are GNU's local functions.
+
+    System/Platform specifics are to be detected by the configure script
+    and provided with transparent implementations in port.{c,h} (or for
+    module-specific dependencies in the respective modules).
+
+    Adherence to the Standard has the following implications:
+
+     - All functions must be fully prototyped.
+     - Standard types like size_t, ssize_t or ptrdiff_t are to be used
+       whereever possible.
+     - Unixisms like
+         {
+           a = malloc(20);
+           b = malloc(20);
+           c = b-a;
+         }
+       are not legal and shouldn't be used.
+     - Don't make assumptions about the size of the base types (e.g.
+       a char might have more than 8 bits). If such an assumption
+       is unavoidable, comment it clearly and if possible add a test
+       to raise a compile or runtime error if the assumption is not met.
+
+
+Style
+-----
+    All modules (.c-files) have to have their interface in an accompaning
+    .h-file. The header file must be guarded against repeated inclusion
+    with the normal
+        #ifndef HEADERNAME_H
+        #define HEADERNAME_H 1
+          ...
+        #endif /* HEADERNAME_H */
+    construct. To use a module, its headerfile must be included - no random
+    'extern' declarations.
+
+    Every module must include "driver.h" which in turn includes the
+    portability headers and provides common defines.
+
+    Use the driver-provided types and macros like Bool or p_int.
+
+    Code should be written defensively and readable. This is not the IOCCC.
+
+    No magic numbers - use #defines to give them names.
+
+    Add sanity checks where useful. If the checks are costly, enclose
+    them in a #ifdef DEBUG...#endif bracket.
+
+    Comment questionable code or opportunities for possible extensions with a
+    'TODO: ...' comment. For multiline comments, use 'TODO::' on the second
+    and following lines (this makes reading a grep easier).
+
+    Comment temporary debug code with a 'DEBUG:' comment. Similar, debug
+    output should always begin with 'DEBUG:'.
+
+    Variable identifiers should start with a lowercase letter, function
+    identifiers may start with upper or lowercase, constant identifiers
+    should start with an uppercase letter. Macro identifiers should be
+    all UPPERCASE, other identifiers may be under_scored or SkudlyCaps.
+    Hungarian notation is accepted only in a very light form: pFoo for
+    pointers, ppFoo for pointer to pointers, iFoo for integer types,
+    sFoo for string pointers, aFoo for complex types - you get the
+    idea. But no alpzsFoo and friends.
+
+    f_xxx() function names are reserved for efun implementations.
+    typedef'd typenames should end in _t (e.g. 'mapping_t'), struct
+    names should end in _s (e.g. 'struct instrs_s').
+
+    Indentation is 4 spaces per level. No tab characters anywhere!
+
+    The indentation style is a variant of the 'Allman style':
+
+        if (foo)
+        {
+            ...body...
+        } /* if (foo) */
+
+    Note the comment at the closing brace!
+
+    One line bodies may be written as
+
+        if (foo) body;
+
+    or
+
+        if (foo)
+            body;
+
+    _if_ it improves the readability.
+
+    Similar, the typical layout of a function is:
+
+        static int
+        function_name ( arg1 , arg2)
+
+        /* Twiddle <arg1> with <arg2> and return the result.
+         */
+
+        {
+            ....
+        } /* function_name() */
+
+    If an expression (argument list, ...) extends over several, the first
+    literal element on a line should be an operator or syntactical marker:
+
+        if (condition1
+         && (   condition2
+             || condition3)
+           )
+
+        printf( "..."
+              , arg1, arg2, arg3
+              );
+
+    Be generous with whitespace - both horizontal and vertical.
+
+    [ The reasoning behind this style is to use the language elements
+      to create strong visual structures for the eyes to follow. By doing so,
+      the structure of the program becomes obvious without much
+      conscious thought.
+    ]
+
+
+Commenting
+----------
+    The comments also follow the idea of giving strong visual clues of
+    how the program is structured.
+
+    Horizontal lines should be created as
+
+        /*------...-------*/
+        /*======...=======*/
+        /* - - -... - - - */
+
+    The '---' line is the normal separator between the components of a
+    source file (includes, variable declarations, macro declarations,
+    the separate functions). The '===' line can be used to separate
+    larger sections of a source file (e.g. the lowlevel routines from
+    the efun implementations). '- -' lines, which usally span less than
+    the whole line, can be used to subdivide large functions (though then
+    it's often better to split the function into several small ones).
+
+    A '***' line is reserved for the end of every source file.
+
+    A '/* --- Subsection --- */' is also good to separate subsections.
+
+    Vertical lines are to be constructed as
+
+      /*
+       *
+       */
+
+    No box comments.
+
+    Every function must have a head comment explaining the meaning
+    of the arguments, what the function does, and the possible results.
+    For efun implementations, this comment should be identical to the
+    man page.
+
+    Within a function, every variable should be commented as
+
+       int foo;  /* short comment */
+       int bar;
+         /* long comment which doesn't fit on one line.
+          */
+
+    The major steps in a function should be preceeded by a comment
+    explaining them. Also, wherever a critical design decision has
+    been made, a comment should line out the whats and whys:
+
+        /* Duplicate the stored arguments for the function call.
+         * (It's tempting to use the stored arguments directly
+         *  in the last pass; however it's not guaranteed that
+         *  the last pass actually comes this far.)
+         */
+
+
+    A typical file layout, commentwise, looks like this:
+
+        /*------------------------------------------------------
+         * Gamedriver Bouncing module.
+         *
+         * <reserved for future copyright notice>
+         *------------------------------------------------------
+         * 'Big picture' description of the module and its
+         * relation to the other gamedriver parts.
+         *
+         * Tricky design discussions also belong in here.
+         *------------------------------------------------------
+         */
+
+        #include "driver.h"
+        #include "typedefs.h"
+
+        #include <stdlib.h>
+
+        #include "bounce.h"
+        #include "backend.h"
+
+        /*--------------------------------------------------------*/
+
+        /* --- User information --- */
+        interactive_t *all_bouncers[MAX_PLAYERS];
+
+        /* ---  Statistics --- */
+
+        p_int number_of_calls;
+
+        /*--------------------------------------------------------*/
+        void
+        add_bouncer (interactive_t *bouncer)
+
+        /* This function adds <bouncer> to the all_bouncers[].
+         */
+        {
+            int i;  /* search index */
+
+            ....
+        } /* add_bouncer() */
+
+        /**********************************************************/
+
diff --git a/doc/driver/commandline b/doc/driver/commandline
new file mode 100644
index 0000000..6e197e9
--- /dev/null
+++ b/doc/driver/commandline
@@ -0,0 +1,60 @@
+NAME
+	commandline
+
+DESCRIPTION
+	The driver understands several command line options and
+	arguments.
+
+	-f<string>: When the master object is loaded and fully
+		    operational after startup, the function flag()
+		    with argument string is applied to the master
+		    object for each occurence of -f in the command line.
+
+	-o	  : run in compat mode (thus sometimes also called
+		    -o mode), instead of native mode. Note: the flag
+		    is obsolete by now, the driver must be compiled
+		    with the appropriate definitions in the config.h
+		    instead.
+
+	-c	  : trace compilations.
+
+	-Dsymbol  : Globally pre-#define symbol for preprocessor.
+
+	-D	  : without symbol, this logs specific trace messages
+		    to /log/D_TRACE (if the driver was compiled for it)
+
+	-e	  : The number of occurences of the -e flag is
+		    passed as argument to the epilog() function
+		    in the master at startup time.
+
+	-N	  : Don't start the erq demon.
+
+	-M<master>: provide a different name for the master object.
+
+	-m<dir>	  : provide a different mudlib directory.
+
+	-ru<size> : set the size of the reserved user memory.
+	-rm<size> : set the size of the reserved master memory.
+	-rs<size> : set the size of the reserved system memory.
+
+	-E<cost>  : set the maximum allowed evaluation cost.
+
+	--max_malloced : set maximum size of mallocable memory chunks.
+	--max_small_malloced : set max. size of mallocable memory chunks.
+
+	-u<port> : set the UDP port number for IMP communication.
+
+	-t	 : disable heart_beat and reset.
+
+	-d	 : run with debug.
+
+	-s<time> : Set the time between swapout attempts. -1 means
+		   don't swap.
+
+	-y	 : If the driver has been compiled with YYDEBUG,
+		   this will enable debug output for the LPC compiler.
+
+	<number> : the TCP port number to use for user connections.
+
+SEE ALSO
+	flag(M), epilog(M), native(C), imp(C)
diff --git a/doc/driver/copyright-issue b/doc/driver/copyright-issue
new file mode 100644
index 0000000..9f086a5
--- /dev/null
+++ b/doc/driver/copyright-issue
@@ -0,0 +1,252 @@
+The question of the Copyright and Terms of Use for the driver is an old
+one. Here is what I could salvage from the amylaar-users mailing list.
+Important is Jacob's mail I received in 1999 which essentially casts
+the current copyright terms in stone.
+
+  -- Lars (not /Lars)
+
+-------------------------------------------------------------------------------
+Date: Sat, 13 Nov 1993 03:11:29 +0100 (MET)
+From: amylaar@meolyon.hanse.de (Joern Rennecke)
+Subject: LPmud Copyright
+To: busey@ux1.cso.uiuc.edu (busey andrew), lars@cd.chalmers.se,
+        croes@swi.psy.uva.nl, gusar@uniwa.uwa.OZ.AU, duening@ibr.cs.tu-bs.de,
+        jacob@cd.chalmers.se, r_behren@informatik.uni-kl.de,
+        mud@alijku05.edvz.uni-linz.ac.at, alcaman@cs.tu-berlin.de
+
+Motivation of this letter:
+There seems to be a potential for muds that run on dedicated machines
+that charge fees from player to make the mud economically feasible.
+The Copyright file says that LPmud can freely used iff it is not for
+monetary gain. Now the debate what constitutes monetary gain and if
+an individual license is an license to break the copyright,
+is an interpretation of the license in Copyright or gives rights
+independent the restrictions in Copyright has become a normal flame
+war in the rec.games.mud.* groups. That is to say, one of the worst
+thinkable.
+
+To allow muds to charge fees to cover costs, without going through
+such debates every time, I suggest to amend the Copyright file
+with terms under witch such a mud is considered to comply to the
+'no monetary gain clause' .
+
+Explanation of the recipient list and some individual messages:
+
+Busey Andrew: wants to set up a mud that charges fees to cover costs.
+  If the below rules won't make it into the Copyright, you can regard this
+  as a license - of course only for the code written by me.
+Lars Pensj|: original author.
+  Please forward this letter to other authors that have contributed to 3.1.2
+  who have a say in the copyright.
+Felix A. Croes: wrote the non-corrupting indentation code for ed.
+Sean A Reith: wrote Mud-sprintf() .
+Lars Duening: wrote the Amiga port.
+Reimer Behrends: wrote mergesort based sort_array() .
+Herp: wrote get_object_actions() .
+Jacob Hallen: is one of the people involved with the CD driver; the email
+ address was in the news recently...
+  Please forward this letter to the person holding the copyright for the
+  UDP connectivity(unless it's yourself :-) .
+Alexander Weidt:
+  Please try to forward this letter to my brother...
+
+I hope to finally get terms which all autors can agree on that can be included
+into the Copyright file. I suggest group replies, so that we can get some
+kind of discussion going (unless there is immediate approval from all
+authors :-) . When you have objections, please try to point out what is
+wrong with these terms. Even better would it be if you had a solution
+to the problem.
+
+        Joern Rennecke (Amylaar)
+
+Proposed Terms:
+1. A LPmud may charge fees from players to cover running and machine costs.
+2. Running costs in these terms are the cost for the network connection,
+   electric power to operate the host machine, wear of backup media,
+   repair costs for the host machine, and cost for a bank account.
+   For the costs of a bank account to be considered runnung costs,
+   they must not habe been considered according to 8. , and the
+   institute has to be choosen with at least usual consideration on
+   terms and costs of the account and that there must be no affiliaton
+   with the institute.
+3. Money to cover running costs for a maximum of 18 month may be accumulated
+   in advance from fees to smoothe fluctation and to ensure stability of
+   the mud. The spare money has to be kept separate from personal money
+   and should be invested in trustee investment if liquidity allows.
+   If the mud is permanently put down, this money has to be refounded to the
+   playeres.
+4. Machine costs are costs for buying, installation and upgrade of the host
+   machine. The costs have to appear on a bona fide purchase / service
+   contract with a person/institution that is not affiliated with the
+   person who sets up the mud.
+   When the host machine is put out of use, or parts of it are removed for
+   other than technical reasons, are parts are nor re-inserted after the
+   technical resons for removal and not re-inserting have become void,
+   the current value of the machine that has put out of use/the removed
+   parts is to be subtracted from the machine costs.
+   If thus more money has been paid for machine costs than there are
+   currently, the surplus amount has to be refounded to the mud players.
+5. The machine cost share in the fee may not be more than 1/2400th
+   of the machine costs per month. If the mud has less than 100 players,
+   it may be up to machine costs / 24 / number of players, but not more than
+   1/120th of the machine costs per month.
+6. When money has to be payed back to the mud players, only those that
+   have payed at least once a fee within the last 24 month are to be
+   considered. For these players, the money is distributed in the ratio
+   of the all fee shares ever payed to cover machine costs.
+7. All players pay equal fees.
+8. Banking costs that have to be paid by the mud administration and are
+   immediately connected to incoming money transactions can be subtracted
+   from the transferred amount before counting it as payment of fees,
+   provided that the institute was choosen with at least usual
+   consideration on terms and costs of the account, and that there is
+   no affiliaton with the institute.
+9. The amount of voluntary donations is unlimited. A donation is not
+   considered voluntary if it is connected with special features or
+   favours in the mud other than an hounarary mentioning of the donor,
+   or if the donor is made to believe that such a connection exists.
+   Reasonable measures have to be taken that there is no
+   misunderstanding on this point.
+
+Comments:
+3. You may not use the money of the mud to bridge personal inliquidity.
+   Don't gamble with other persons money, e.g. investing it in junk bonds.
+5. Fees should not be arbitrarily raised so that players can be driven
+   out. I considered a fixed minimal distributen of the costs to be
+   the best means to codify this.
+   Absolute figures are bound to become void by inflation.
+6. The 24 month period is introduced to allow to erease records of
+   clients after two years, and to keep overhead affordable.
+7. We don't want favourites to get a free lift, and others grudgingly
+   paying extra. If you think somebody needs free access, find someone
+   who pays for it, or make a found payed from voluntary donations.
+
+-------------------------------------------------------------------------------
+Date: Fri, 19 Nov 1993 17:10:44 +0100 (MET)
+From: Lars Pensj| <lars@cd.chalmers.se>
+Subject: Re: LPmud Copyright
+To: amylaar@meolyon.hanse.de (Joern Rennecke)
+Cc: busey@ux1.cso.uiuc.edu, lars@cd.chalmers.se, croes@swi.psy.uva.nl,
+        gusar@uniwa.uwa.OZ.AU, duening@ibr.cs.tu-bs.de, jacob@cd.chalmers.se,
+        r_behren@informatik.uni-kl.de, mud@alijku05.edvz.uni-linz.ac.at,
+        alcaman@cs.tu-berlin.de
+
+I agree that fix of the copyright is needed. I would prefer to use the
+Gnu Copyleft, as I don't care any longer if anyone makes money from it. The
+important thing is that it is free, which means noone will be able to make
+much money anyway.
+
+Any thoughts about it ?
+
+/Lars
+
+-------------------------------------------------------------------------------
+Date: Fri, 19 Nov 1993 20:14:10 +0100 (MET)
+From: Jacob Hallen <jacob@cd.chalmers.se>
+Subject: Re: LPmud Copyright
+To: amylaar@meolyon.hanse.de (Joern Rennecke)
+Cc: busey@ux1.cso.uiuc.edu, lars@cd.chalmers.se, croes@swi.psy.uva.nl,
+        gusar@uniwa.uwa.OZ.AU, duening@ibr.cs.tu-bs.de, jacob@cd.chalmers.se,
+        r_behren@informatik.uni-kl.de, mud@alijku05.edvz.uni-linz.ac.at,
+        alcaman@cs.tu-berlin.de
+
+> Jacob Hallen: is one of the people involved with the CD driver; the email
+>  address was in the news recently...
+>   Please forward this letter to the person holding the copyright for the
+>   UDP connectivity(unless it's yourself :-) .
+
+I represent everyone involved in the CD driver. The UDP stuff is to be
+considered public domain. All other parts are covered by the non-profit clause.
+Code origination from me, Johan Andersson (Commander), Ronny Wikh (Mrpr),
+Lennart Augustsson (Marvin) is covered by it. We have no intention of
+allowing people to charge money for the usage of our driver, or borrowed
+pieces thereof.
+We consider the acceptance of volontary donations as fair practice, and we
+can accept the charging for the use of extra equipment needed to allow
+people to access the mud, as long as there is a reasonable way to access the
+mud without being charged. (Providing modem access at a cost while allowing
+free access over the internet is an example of such a setup.)
+
+My personal view is that an elaborate setup of terms like the one in the
+original letter is unreasonable. It is designed for a very specific set of
+circumstances. It is impossible to check and it is very bureaucratic.
+It does not have my support.
+
+Jacob Hallen
+
+-------------------------------------------------------------------------------
+Date: Sat, 20 Nov 1993 23:35:12 +0100 (MET)
+From: Multi User Dungeon <mud@alijku05.edvz.uni-linz.ac.at>
+Subject: Re: LPmud Copyright
+To: lars@cd.chalmers.se (Lars Pensj|)
+Cc: busey@ux1.cso.uiuc.edu, croes@swi.psy.uva.nl, gusar@uniwa.uwa.OZ.AU,
+        duening@ibr.cs.tu-bs.de, jacob@cd.chalmers.se,
+        r_behren@informatik.uni-kl.de, alcaman@cs.tu-berlin.de
+
+Lars> important thing is that it is free, which means noone will be able to make
+Lars> much money anyway.
+
+You are speaking about the GD,correct ? Normally, many a site uses an unmodified
+GD based upon which is a more or less heavily or not heavily Mudlib. Based upon
+this Mudlib is the work of the `wizards' ... Sorry for repeating known stuff.
+
+This makes most Muds differ from each other. So, the fact that the GD itself
+is free, doesn't imply that you won't make money.
+
+Another point to argue: maintainig a Mud takes time .. a LOT of time. Usually,
+doing so is not fun at all. I experienced that the more players you have, the
+less fun it is fore the adminstrators. You spend a lot of time coding,
+searching and fixing bugs ... and I think, this can be regarded as a
+service for players (... and players really can be a pain sometimes ...)
+Would it be legal to charge money for that ?
+
+Another thought: Internet Muds. They run on internet, usually on computers
+owned by a school or university, some with, some without the knowledge of
+the site adminstrators. Would it be legal to charge money when you run
+a Mud on equipment not owned by yourself ? And, even if you own the computer,
+do you pay for the internet link ? If not, I fear you must not charge money
+for a Mud without speaking with the network adminstrator since you are using
+the network components (router/bridges, even cables :-> ...) for free.
+
+How difficult is charging money in European Muds ? Not that I plan
+to do so for HM (it's closed for players currently anyway), but isn't there a
+big difference according to the "accounting mechanism" (ugh, bad english :-)
+that is used in the States ? I heard that it is much more easy to make
+financial transactions within the States. So, I suspect the "Mud money charging"
+discussion arrives from the US :-)
+
+Greetings, Herp (mud@mud.uni-linz.ac.at)
+
+------------------------------------------------------------------------------
+From jacob@cd.chalmers.se Sun Oct 24 17:02:53 1999
+Newsgroups: rec.games.mud.admin
+Subject: Re: Newsgroups
+From: jacob@cd.chalmers.se (Jacob Hallen)
+Date: 24 Oct 1999 16:02:53 GMT
+
+In article <380f817d.35916528@news.earthlink.net>,
+Lars Duening <lars@bearnip.com> wrote:
+>On Thu, 21 Oct 1999 12:11:36 -0700, Ilya
+><ilya@spam.free.gamecommandos.com> wrote:
+>
+>>Hey Lars, what hope is there, if any, of getting
+>>something going in the LP world, using LDmud or
+>>whatever,  that can be used commercially?
+>
+>Unfortunately only slim hope: the parts written by the Genesis folks
+>are definitely non-commercial; but rewriting is difficult because
+>nobody remembers _which_ parts are concerned (and mailed requests
+>haven't been answered). And I haven't asked the other contributors
+>yet, either.
+
+I have a pretty good idea of who wrote what parts of the early version 3
+gamedrivers. All in all there have been about 30 people involved. Unless
+you recode the entire gamedriver from scratch, or build from assembled pieces
+with known copyright restrictions, there is no way you can come up with
+something that does not infringe on the rights of someone who will not
+allow commecial use.
+
+Jacob Hallén
+
+------------------------------------------------------------------------------
+
diff --git a/doc/driver/debugmalloc b/doc/driver/debugmalloc
new file mode 100644
index 0000000..8f78bae
--- /dev/null
+++ b/doc/driver/debugmalloc
@@ -0,0 +1,17 @@
+NAME
+        debugmalloc
+
+DESCRIPTION
+        This command is hardcoded into the driver.
+
+        Toggles the debug mode for the memory managment.
+        If the O_IS_WIZARD flag is used in the mudlib (i.e. if
+        set_is_wizard() was called), this command is allowed only for
+        users that have this flag set.
+
+HISTORY
+        Deactivated in 3.2.7 by default, it was effectless before anyway.
+
+SEE ALSO
+        malloc(D), status(D), memory(C), objects(C), debug_info(E),
+        set_is_wizard(E)
diff --git a/doc/driver/driver b/doc/driver/driver
new file mode 100644
index 0000000..40430e8
--- /dev/null
+++ b/doc/driver/driver
@@ -0,0 +1,15 @@
+NAME
+        driver
+
+DESCRIPTION
+        This directory contains descriptions of miscellaneous
+        internals of Amylaar's version of the LPC parser/interpreter,
+        that might be useful to know.
+
+        One thing described here are the special hardcoded commands
+        of the interpreter for querying the status of the memory
+        management.
+
+SEE ALSO
+        efun(E), applied(A), concepts(C), master(M), lpc(LPC),
+        malloc(D), status(D)
diff --git a/doc/driver/dumpallobj b/doc/driver/dumpallobj
new file mode 100644
index 0000000..24b884a
--- /dev/null
+++ b/doc/driver/dumpallobj
@@ -0,0 +1,49 @@
+NAME
+        dumpallobj
+
+DESCRIPTION
+        Write a list of all loaded or cloned objects into the file
+        OBJ_DUMP, and a list of all destructed objects into the
+        file DEST_OBJ_DUMP. Both files are located in the root directory of
+        the mudlib.
+
+        Warning: these files can be very large, and if the driver is low
+        on memory there is a certain crash probability.
+
+        If the O_IS_WIZARD flag is used in the mudlib (i.e. if
+        set_is_wizard() was called), this command is allowed only for
+        users that have this flag set.
+
+        For every live object, a line is written into the file with the
+        following information in the given order:
+          - object name
+          - size in memory, shared data counted only once
+          - size in memory if data wouldn't be shared
+          - number of references
+          - 'HB' if the object has a heartbeat, nothing if not.
+          - the name of the environment, or '--' if the object has no
+            environment
+          - in parentheses the number of execution ticks spent in this
+            object
+          - the swap status:
+             nothing if not swapped,
+             'PROG SWAPPED' if only the program is swapped
+             'VAR SWAPPED' if only the variabes are swapped
+             'SWAPPED' if both program and variables are swapped
+          - the time the object was created
+
+        For every destructed object, a line is written into the file with the
+        following information in the given order:
+          - object name
+          - number of references
+          - 'NEW' if the object was destructed in this executiong
+            thread, nothing if it is older already.
+
+
+HISTORY
+        LDMud 3.2.9 added the DEST_OBJ_DUMP.
+        LDMud 3.2.10 added the object creation time to OBJ_DUMP.
+
+SEE ALSO
+        malloc(D), status(D), memory(C), objects(C), debug_info(E),
+        set_is_wizard(E)
diff --git a/doc/driver/invocation b/doc/driver/invocation
new file mode 100644
index 0000000..5760b46
--- /dev/null
+++ b/doc/driver/invocation
@@ -0,0 +1,335 @@
+NAME
+    driver/invocation
+
+PURPOSE
+    Description of the invocation of the gamedriver, especially of the command
+    arguments. This document describes the commandline version of the driver
+    only; non-commandline versions are platform specific and described in
+    the related documentation.
+
+DESCRIPTION
+    The driver is invoked from the commandline as other normal programs.
+    Neither the current directory nor the directory the executable is in need
+    to be in any special relation the directory of the mudlib. Once the driver
+    is running, it emits two streams of outputs:
+
+      - driver-related messages on stderr; this unfortunately includes
+        LPC compiler diagnostics
+      - LPC runtime-related messages in the logfile <mudlib>/<host>.parse.log
+        (the name can be changed).
+
+    It is possible to start the driver without any commandline arguments as
+    suitable defaults are specified at compile time. The invocation syntax
+    is:
+
+        driver [options] [<portnumber>]...
+
+    <portnumber> the number of the port the driver shall use to accept
+    connections. The maximum number of ports is determined by MAXNUMPORTS
+    in the source file config.h.
+
+    The options modify the behaviour of the gamedriver. Some of them are only
+    available if a certain compile-time option was enabled (typically in
+    the source file config.h). The following options are recognized:
+
+      -P|--inherit <fd-number>
+        Inherit filedescriptor <fd-number> from the parent process
+        as socket to listen for connections.
+        Only available if compiled with MAXNUMPORTS.
+
+      -u|--udp <portnumber>
+        Specify the <portnumber> for the UDP port, overriding the compiled-in
+        default.
+        Only available if compiled with CATCH_UDP_PORT.
+
+      -D|--define <macro>[=<text>]
+        Add <macro> (optionally to be expanded to <text>) to the list of
+        predefined macros known by the LPC compiler.
+
+      -E|--eval-cost <ticks>
+        Set the number of <ticks> available for one evaluation thread.
+        If 0, execution is unlimited.
+
+      -M|--master <filename>
+        Use <filename> for the master object.
+
+      -m|--mudlib <pathname>
+        Use <pathname> as the top directory of the mudlib.
+
+      --debug-file <filename>
+        Log all debug output in <filename> instead of
+        <mudlib>/<host>.debug.log .
+
+      --hostname <name>
+        Use <name> as hostname instead of what the system says.
+
+      --hostaddr <addr>
+        Use <addr> as address of this machine, instead of what the
+        system says.  In particular this address will be used to open
+        the driver ports.
+
+      --no-compat
+      --compat
+        Select the mode (plain or compat) of the driver.
+        This choice does not affect the default name of the master object.
+
+      -d|--debug
+        Generate debug output; repeat the argument for even more output:
+          >= 1: log resets, clean ups, swaps, reception of urgend data,
+                   telnet negotiation states.
+                check_a_lot_of_refcounts() on startup when swapping of
+                   variables is disabled.
+          >= 2: log all add_message()s, name lookup failures, new players.
+          >= 3: progress of garbage collection
+          >= 4: even more junk from garbage collection
+
+      -c|--list-compiles
+        List the name of every compiled file on stderr.
+
+      -e|--no-preload
+        Pass a non-zero argument (the number of occurences of this option)
+        to master->preload(), which usually inhibits all preloads of castles
+        and other objects.
+
+      --erq <filename>
+      --erq "<filename> <erq args>"
+        Use <filename> instead of 'erq' as the basename of the ERQ executable.
+        If the name starts with a '/', it is take to be an absolute pathname,
+        otherwise it is interpreted relative to <bindir>. If not specified,
+        'erq' is used as the executable name.
+
+        By enclosing the argument value in quotes, it is possible to pass
+        arguments (e.g. --execdir) to the erq. These arguments however must
+        not contain embedded spaces.
+
+      -N|--no-erq
+        Don't start the erq demon (if it would be started at all).
+
+      --alarm-time <seconds>
+        Set the granularity of call_out() and heartbeat timing. Minimum
+        value is 1.
+
+      --heart-interval <seconds>
+        Set the interval between two heartbeats. Minimum value is 1.
+
+      --sync-heart
+        All heartbeats occur at the same time (modulo granularity).
+      
+      --async-heart
+        Heartbeats occur when they are due (modulo granularity).
+
+      -t|--no-heart
+        Disable heartbeats and call_outs.
+
+      -f|--funcall <word>
+        The lfun master->flag() is called with <word> as argument before the
+        gamedriver accepts netword connections.
+
+      --regexp pcre | traditional
+        Select the default regexp package.
+
+      --max-array <size>
+        The maximum number of elements an array can hold.
+        Set to 0, arrays of any size are allowed.
+
+      --max-mapping <size>
+        The maximum number of elements a mapping can hold.
+        Set to 0, mappings of any size are allowed.
+
+      --max-mapping-keys <size>
+        The maximum number of entries a mapping can hold.
+        Set to 0, mappings of any size are allowed.
+
+      --max-callouts <size>
+        The maximum number of callouts at one time.
+        Set to 0, any number is allowed.
+
+      --max-bytes <size>
+        The maximum number of bytes one read_bytes()/write_bytes() call
+        can handle.
+        Set to 0, reads and writes of any size are allowed.
+
+      --max-file <size>
+        The maximum number of bytes one read_file()/write_file() call
+        can handle.
+        Set to 0, reads and writes of any size are allowed.
+
+      --max-thread-pending <size>\n"
+        The maximum number of bytes to be kept pending by the socket write
+        thread.
+        Set to 0, an unlimited amount of data can be kept pending.
+
+        This option is ignored if pthreads are not used.
+
+      --cleanup-time <time>
+        The idle time in seconds for an object before the driver tries to
+        clean it up. It should be substantially longer than the reset time.
+        A time <= 0 disables the cleanup mechanism.
+
+      --reset-time <time>
+        The time in seconds before an object is reset. A time <= 0 disables
+        the reset mechanism.
+
+      -s <time>  | --swap-time <time>
+      -s v<time> | --swap-variables <time>
+        Time in seconds before an object (or its variables) are swapped out.
+        A time less or equal 0 disables swapping.
+
+      -s f<name> | --swap-file <name>
+        Swap into file <name> instead of <mudlib>/LP_SWAP.<host> .
+
+      -s c       | --swap-compact
+        Reuse free space in the swap file immediately.
+        Giving this option results in smaller, but also more fragmented
+        swapfiles, and the swap performance may degrade.
+
+      --max-malloc <size>
+        Restrict total memory allocations to <size> bytes.
+        A <size> of 0 or 'unlimited' removes any restriction.\n"
+
+      --min-malloc <size>
+      --min-small-malloc <size>
+        Determine the sizes for the explicite initial large resp. small chunk
+        allocation. A size of 0 disables the explicite initial allocations.
+
+      -r u<size> | --reserve-user <size>
+      -r m<size> | --reserve-master <size>
+      -r s<size> | --reserve-system <size>
+        Reserve <size> amount of memory for user/master/system allocations to
+        be held until main memory runs out.
+
+      --filename-spaces
+      --no-filename-spaces
+        Allow/disallow the use of spaces in filenames.
+
+      --strict-euids
+      --no-strict-euids
+        Enable/disable the enforced use of euids.
+
+      --share-variables
+      --init-variables
+        Select how clones initialize their variables:
+          - by sharing the current values of their blueprint
+          - by initializing them afresh (using __INIT()).
+
+      --pidfile <filename>\n"
+        Write the pid of the driver process into <filename>.\n"
+
+      --tls-key <pathname>
+          Use <pathname> as the x509 keyfile, default is 'key.pem'.
+          If relative, <pathname> is interpreted relative to <mudlib>.
+
+      --tls-cert <pathname>
+         Use <pathname> as the x509 certfile, default is 'cert.pem'.
+         If relative, <pathname> is interpreted relative to <mudlib>.
+
+      --tls-trustfile <pathname>
+        Use <pathname> as the filename holding your trusted PEM certificates.
+        If relative, <pathname> is interpreted relative to <mudlib>.
+
+      --tls-trustdirectory <pathname>
+        Use <pathname> as the directory where your trusted
+        PEM certificates reside, default is '/etc/ssl/certs'.
+        If relative, <pathname> is interpreted relative to <mudlib>.
+
+      --wizlist-file <filename>
+      --no-wizlist-file
+        Read and save the wizlist in the named file (always interpreted
+        relative the mudlib); resp. don't read or save the wizlist.
+
+      --gcollect-outfd <filename>|<num>
+        Garbage collector output (like a log of all reclaimed memory blocks)
+        is sent to <filename> (or inherited fd <num>) instead of stderr.
+        Only available if compiled with MALLOC_smalloc.
+
+      --y|--yydebug
+        Enable debugging of the LPC compiler.
+        Only available if compiled with YYDEBUG.
+
+      --random-seed <num>
+        Seed value for the random number generator. If not given, the
+        driver chooses a seed value on its own.
+        This option is for debugging.
+
+      --check-state <lvl>
+        Perform a regular simplistic check of the virtual machine according
+        to <lvl>:
+          = 0: no check
+          = 1: once per backend loop
+          = 2: at various points in the backend loop
+        Only available if compiled with DEBUG.
+
+      --check-refcounts
+        Every backend cycle, all refcounts in the system are checked.
+        SLOW! Only available if compiled with DEBUG.
+
+      --gobble-descriptors <num>
+        <num> (more) filedescriptors are used up. You'll know when you need it.
+        Only available if compiled with DEBUG.
+
+      --check-strings
+        Every backend cycle, all shared strings in the system are checked.
+        SLOW! Only available if compiled with DEBUG and CHECK_STRINGS.
+
+      -V|--version
+        Print the version of the driver and exit.
+
+      --options
+        Print the version and compilation options of the driver and exit.
+
+      -h|-?|--help
+        Display a command help and exit.
+
+      --longhelp
+        Display a long command help and exit.
+
+      --args <filename>
+        The driver reads and parses the given file and treats its contents
+        as if given on the commandline right where the --args option
+        occured. The file itself can again contain --args options.
+
+
+DESCRIPTION -- Argument Parser
+    The parser analyses the commandline arguments given with the driver
+    invocation and distinguishes 'options', which start with a '-', from
+    proper arguments. Options are further distinguished by their name and
+    may take an additional value. In general, options and arguments can be
+    givein in any order.
+
+    Options are recognized in two forms. In the short form the option must
+    be given as a single '-' followed by a single letter. In the long form,
+    options start with '--' followed by a string of arbitrary length. The
+    short options are case sensitive, the long options aren't.
+    Most options can be specified in both the short and long form, but that
+    is not mandatory. Examples: '-r' and '--recursive'.
+
+    If an option takes a value, it must follow the option immediately after
+    a separating space or '='. Additionally, the value for a short option
+    may follow the option without separator. Examples are: '-fMakefile',
+    '-f Makefile', '--file=Makefile' and '--file Makefile'.
+
+    Short options may be collated into one argument, e.g. '-rtl', but
+    of these only the last may take a value.
+
+    The option '--' marks the end of options. All following command arguments
+    are considered proper arguments even if they start with a '-' or '--'.
+
+    The arguments are usually taken from the commandline; but the parser
+    is also able to read them from a textfiles, which can be nested. The
+    content of the textfiles is broken down into words delimited by whitespace,
+    which are then treated as given on the commandline at the place where
+    the instruction to read the textfile stood.
+
+    The file parser recognizes simple double-quoted strings, which must be
+    contained on a single line. Additionally, the '#' character given by
+    itself is a comment marker - everthing after the '#' until the end
+    of the current line is ignored.
+
+HISTORY
+    LDMud 3.2.9 added the --max-thread-pending, --hostname,
+      --hostaddr, --args and --random-seed options.
+    LDMud 3.2.10 added the --filename-spaces options.
+    LDMud 3.3.378 added --share-variables, --init-variables.
+    LDMud 3.3.475/3.2.11 added --tls-key, --tls-cert.
+    LDMud 3.3.672/3.2.11 added --tls-trustfile, --tls-trustdirectory.
+    LDMud 3.3.677 added --max-mapping-keys.
diff --git a/doc/driver/malloc b/doc/driver/malloc
new file mode 100644
index 0000000..7fb2722
--- /dev/null
+++ b/doc/driver/malloc
@@ -0,0 +1,12 @@
+NAME
+        malloc
+
+DESCRIPTION
+        This command is hardcoded into the driver's input parser.
+        It shows the statistics of the memory management module.
+
+HISTORY
+        Since 3.2.7, 'status malloc' has the same effect.
+
+SEE ALSO
+        status(D), memory(C), debug_info(E)
diff --git a/doc/driver/opcdump b/doc/driver/opcdump
new file mode 100644
index 0000000..b75ea68
--- /dev/null
+++ b/doc/driver/opcdump
@@ -0,0 +1,13 @@
+NAME
+        opcdump
+
+DESCRIPTION
+        If the driver was compiled to do opcode profiling, this command
+        will save the collected profiling information into the file /OPC_DUMP.
+        If the O_IS_WIZARD flag is used in the mudlib (i.e. if
+        set_is_wizard() was called), this command is allowed only for
+        users that have this flag set.
+
+SEE ALSO
+        malloc(D), status(D), memory(C), objects(C), debug_info(E),
+        set_is_wizard(E)
diff --git a/doc/driver/predefined b/doc/driver/predefined
new file mode 100644
index 0000000..17a7eb0
--- /dev/null
+++ b/doc/driver/predefined
@@ -0,0 +1,130 @@
+NAME
+    predefined - predefined #defines by the parser
+
+DESCRIPTION
+    Several preprocessor macros are pre#defined by the parser,
+    to provide information about parser version, compile time
+    options and parser invocation options:
+
+      LPC3            : always defined.
+      __LDMUD__       : always defined.
+      __EUIDS__       : always (for compatibility).
+      COMPAT_FLAG     : defined if the driver runs in compat mode.
+      __COMPAT_MODE__ : ditto
+      __STRICT_EUIDS__: defined if strict euid usage is enforced.
+      __FILENAME_SPACES__: defined if filenames may contain spaces.
+
+      __MASTER_OBJECT__ : the name of the master object (in compat mode
+                          without leading '/').
+      __FILE__          : the name of the compiled file (in compat mode
+                          without leading '/').
+      __LINE__          : the current line number.
+      __FUNCTION__      : the current function name.
+      __DIR__           : the directory path of the compiled file (in
+                          compat mode without leading '/').
+      __PATH__(n)       : the directory path of the compiled file without
+                          the <n> trailing elements (in compat mode without
+                          leading '/').
+      __VERSION__       : the version string of the driver.
+      __VERSION_MAJOR__ : the major version number of the driver.
+      __VERSION_MINOR__ : the minor version number of the driver.
+      __VERSION_MICRO__ : the micro version number of the driver.
+      __VERSION_PATCH__ : the patchlevel of the driver; a 0 here means
+                          'no patchlevel'.
+      __VERSION_COMMITID__ : the commit ID of the source of the driver 
+                             (attention: it might be <unknown>, if the driver
+                              was not compiled from a git repository)
+      __VERSION_LOCAL__ : the (optional) LOCAL_LEVEL, the user has defined.
+
+
+      __DOMAIN_NAME__    : the domain the host is part of.
+      __HOST_IP_NUMBER__ : the hosts IP number (as a string).
+      __HOST_NAME__      : the full hostname.
+      __MAX_RECURSION__  : the max count of nested function calls
+                           (this is config.h:MAX_USER_TRACE).
+      __MAX_EVAL_COST__  : the max evaluation cost.
+      __RESET_TIME__     : default interval time between object resets.
+      __CLEANUP_TIME__   : default interval time between object cleanups.
+      __ALARM_TIME__     : the configured timing granularity.
+      __HEART_BEAT_INTERVAL__: the configured heartbeat time.
+      __SYNCHRONOUS_HEART_BEAT__: defined if synchronous heartbeats are
+                           enabled.
+      __MAX_COMMAND_LENGTH__: the maximum length a command can have.
+      __EFUN_DEFINED__(name) : if the efun 'name' exists, this
+                               macro evaluates to " 1 ", else to " 0 ".
+      __DRIVER_LOG__     : the name of the default debug.log file (within
+                           the mudlib); undefined if a different name
+                           has been specified on the commandline.
+      __WIZLIST__        : the name of the (mudlib) file from where the
+                           driver read the initial WIZLIST information.
+                           It is undefined if the driver was configured
+                           to not read the information.
+      __MAX_MALLOC__     : the internal upper limit for total memory
+                           usage.
+      __INT_MAX__        : the largest integer number
+      __INT_MIN__        : the smallest integer number
+      __FLOAT_MAX__      : the largest (positive) float number
+      __FLOAT_MIN__      : the smallest (positive) float number
+
+      __LPC_NOSAVE__     : always defined
+      __LPC_STRUCTS__    : defined when struct support is enabled.
+                           Once structs are fully supported, this macro
+                           will always be defined.
+      __LPC_INLINE_CLOSURES__: defined when the 'real' inline closures
+                           are enabled.
+      __LPC_ARRAY_CALLS__: call_other()s on arrays of objects enabled.
+      __BOOT_TIME__      : the time() the driver was started.
+
+    If the ERQ is supported, the following macros are defined:
+
+      __ERQ_MAX_SEND__  : the max size of the send buffer
+      __ERQ_MAX_REPLY__ : the max size of the reply buffer
+
+    The following macros are defined if their associated package
+    has been compiled into the driver:
+
+      __IDNA__ :      support for IDNA
+      __IPV6__ :      support for IP v.6
+      __MYSQL__ :     support for mySQL
+      __PGSQL__ :     support for PostgreSQL
+      __SQLITE__ :    support for SQLite 3.
+      __XML_DOM__ :   support for XML parsing.
+      __JSON__ :      support for JSON parsing/serializing.
+      __MCCP__:       support for MCCP http://www.randomly.org/projects/MCCP
+      __ALISTS__:     support for alists
+      __PCRE__:       support for PCRE
+      __TLS__:        support for TLS (internal)
+      __GNUTLS__:     if __TLS__: TLS support provided by GnuTLS.
+      __OPENSSL__:    if __TLS__: TLS support provided by OpenSSL.
+      __GCRYPT__:     cryptographic routines provided by libgcrypt.
+      __DEPRECATED__: support for obsolete and deprecated efuns.
+
+
+HISTORY
+    3.2.1 added __DOMAIN_NAME__, __HOST_IP_NUMBER__, __HOST_NAME__,
+        __MAX_RECURSION__, __EFUN_DEFINED__().
+    3.2.5 added __COMPAT_MODE__, __NATIVE_MODE__, __EUIDS__,
+        __ERQ_MAX_SEND__ and __ERQ_MAX_REPLY__.
+    3.2.6 added __MAX_EVAL_COST__.
+    3.2.7 added __STRICT_EUIDS__ and made __EUIDS__ standard.
+    3.2.8 added __IPV6__, __LPC_NOSAVE__, __DIR__, __PATH__().
+    3.2.9 added __LDMUD__, __MYSQL__, __DEPRECATED__, __VERSION_MAJOR__,
+         __VERSION_MINOR__, __VERSION_MICRO__, __VERSION_PATCH__,
+         __INT_MAX__, __INT_MIN__, __FLOAT_MIN__, __FLOAT_MAX__,
+        __CATCH_EVAL_COST__, __MASTER_EVAL_COST__, __RESET_TIME__,
+        __CLEANUP_TIME__, __DRIVER_LOG__, and __WIZLIST__.
+    3.2.10 added __MAX_MALLOC__, __MSDOS_FS__, __LPC_ARRAY_CALLS__
+        and __FILENAME_SPACES__.
+    3.3 made __LPC_NOSAVE__ always defined and added __ALISTS__,
+        __MCCP__, __LPC_STRUCTS__, __LPC_INLINE_CLOSURES__, __PGSQL__,
+        __PTHREADS__, __TLS__, __BOOT_TIME__, __ALARM_TIME__,
+        __HEART_BEAT_INTERVAL__, __SYNCHRONOUS_HEART_BEAT__, and __PCRE__.
+    3.3.713 added __IDNA__, __SQLITE__.
+    3.3.714 added __OPENSSL__, __GNUTLS__.
+    3.3.718 added __XML_DOM__.
+    3.3.719 removed __PTHREADS__, AMIGA, MSDOS_FS, __BEOS__  
+		    and added __GCRYPT__.
+    3.3.721 added __FUNCTION__.
+
+SEE ALSO
+    pragma(LPC), preprocessor(LPC)
diff --git a/doc/driver/showsmallnewmalloced b/doc/driver/showsmallnewmalloced
new file mode 100644
index 0000000..17e2252
--- /dev/null
+++ b/doc/driver/showsmallnewmalloced
@@ -0,0 +1,17 @@
+NAME
+        showsmallnewmalloced
+
+DESCRIPTION
+        This command is hardcoded into the driver.
+
+        Shows a list of recently allocated small memory blocks.
+        If the O_IS_WIZARD flag is used in the mudlib (i.e. if
+        set_is_wizard() was called), this command is allowed only for
+        users that have this flag set.
+
+HISTORY
+        Deactivated in 3.2.7 by default.
+
+SEE ALSO
+        malloc(D), status(D), memory(C), objects(C), debug_info(E),
+        set_is_wizard(E)
diff --git a/doc/driver/status b/doc/driver/status
new file mode 100644
index 0000000..79759dd
--- /dev/null
+++ b/doc/driver/status
@@ -0,0 +1,18 @@
+NAME
+        status
+        status tables
+        status swap
+        status malloc
+
+DESCRIPTION
+        This command is hardcoded into the drivers input routine.
+        It displays information about the run status of the system.
+        If the O_IS_WIZARD flag is used in the mudlib (i.e. if
+        set_is_wizard() was called), this command is allowed only for
+        users that have this flag set.
+
+HISTORY
+        3.2.7 added the 'status malloc' variant.
+
+SEE ALSO
+        malloc(D), memory(C), objects(C), debug_info(E), set_is_wizard(E)
diff --git a/doc/efun/.default b/doc/efun/.default
new file mode 100644
index 0000000..2560e19
--- /dev/null
+++ b/doc/efun/.default
@@ -0,0 +1,14 @@
+FUNKTION:
+
+ARGUMENTE:
+
+BESCHREIBUNG:
+
+RUECKGABEWERT:
+
+BEMERKUNGEN:
+
+BEISPIELE:
+
+SIEHE AUCH:
+
diff --git a/doc/efun/.synonym b/doc/efun/.synonym
new file mode 100644
index 0000000..f024a1b
--- /dev/null
+++ b/doc/efun/.synonym
@@ -0,0 +1,3 @@
+round ceil
+trunc ceil
+efun::m_delete efun__m_delete
diff --git "a/doc/efun/\133\135" "b/doc/efun/\133\135"
new file mode 100644
index 0000000..a59696a
--- /dev/null
+++ "b/doc/efun/\133\135"
@@ -0,0 +1,66 @@
+SYNOPSIS
+        mixed  arr[index];
+        int    str[index];
+
+        *mixed arr[from .. to];
+        string str[from .. to];
+
+BESCHREIBUNG
+        Liefert ein Element aus einem Array oder String (erste Form), oder
+        eine Teilmenge (zweite Form).
+
+        Die Indizes <index>, <from> und <to> sind durchnummeriert von 0 bis
+        strlen(<str>)-1 bzw. sizeof(<arr>)-1. Wenn ein <index> als '<wert'
+        geschrieben wird, wird der Wert vom Ende des Stings / Arrays her
+        gezaehlt. Dabei wird nummeriert von 1 bis strlen(<str>) bzw.
+        sizeof(<arr>). Wird <from> weggelassen, beginnt die Teilmenge mit
+        dem ersten Element. Wird <to> weggelassen, endet die Teilmenge mit
+        dem letzten Element.
+
+        In der ersten Form muss <index> innerhalb der Grenzen des Strings /
+        Arrays sein, sonst wird ein Laufzeitfehler (RTE) verursacht. In der
+        zweiten Form werden die Indizes an die Groesse des Strings / Arrays
+        angepasst. Wenn <from> groesser ist als <to> oder beide ausserhalb
+        der Groesse des Strings / Arrays liegen, wird ein leerer String ""
+        bzw. ein leeres Array ({}) zurueck geliefert.
+
+        Die Notation als Closure ist entsprechend:
+
+            [index]      -> ({'#[,      arr, index })
+            [<index]     -> ({'#[<,     arr, index })
+            [from..to]   -> ({'#[..],   arr, from, to })
+            [<from..to]  -> ({'#[<..],  arr, from, to })
+            [from..<to]  -> ({'#[..<],  arr, from, to })
+            [<from..<to] -> ({'#[<..<], arr, from, to })
+
+BEISPIELE
+        foo = ({ 1, 2, 3, 4 });         str = "test";
+
+        foo[1]     -> 1                 str[1] -> 'e' == 101
+        foo[1..2]  -> ({ 2, 3 })        str[1..2]  -> "es"
+        foo[2..1]  -> ({ })             str[2..1]  -> ""
+        foo[0..<2] -> ({ 1, 2 })        str[0..<2]  -> "tes"
+
+        foo[..<2]  -> ({ 1, 2 })        str[..<2]  -> "tes"
+        foo[<3..]  -> ({ 2, 3, 4 })     str[<3..]  -> "est"
+
+        foo[1] = 5                      -> foo == ({ 1, 5, 3, 4 })
+        foo[1..2] = ({ 5, 6, 7 })       -> foo == ({ 1, 5, 6, 7, 4 })
+        foo[1..2] = ({ })               -> foo == ({ 1, 4 })
+
+        str[1] = 'a'                    -> str == "tast"
+        str[1..2] = "bar"               -> str == "tbart"
+        str[1..2] = ""                  -> str == "tt"
+
+AENDERUNGEN
+        slice_array() ist die alte Form der []-Operatoren fuer Arrays,
+        extract() ist die alte Form der []-Operatoren fuer Strings.
+        BEIDE VARIANTEN SIND VERALTET, WERDEN NICHT MEHR UNTERSTUETZT UND
+        SOLLTEN DESHALB NICHT MEHR VERWENDET WERDEN.
+
+        Die Syntax fuer 'rueckwaerts zaehlen vom letzten Element' hat sich von
+        Version 3.1.J zu 3.1.K geaendert von '-1' zu '<1'. Auch ist seit dann
+        foo[0..-1] ein leeres Array bzw. ein leerer String.
+
+SIEHE AUCH
+        member(E), sizeof(E), slice_array(E)
diff --git a/doc/efun/abs b/doc/efun/abs
new file mode 100644
index 0000000..586beea
--- /dev/null
+++ b/doc/efun/abs
@@ -0,0 +1,20 @@
+SYNOPSIS
+        int   abs (int arg)
+        float abs (float arg)
+
+BESCHREIBUNG
+        Liefert den Betrag des Argumentes <arg>.
+
+BEISPIELE
+        Funktion      Rueckgabewert
+        -------------------------------------------------------------------
+        abs(-18    )  18
+        abs( 11    )  11
+        abs( -1.974)   1.974
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.6.
+
+SIEHE AUCH
+        sin(E), asin(E), cos(E), acos(E), tan(E), atan(E), log(E), exp(E),
+        sqrt(E), floor(E), ceil(E), pow(E), sgn(E)
diff --git a/doc/efun/acos b/doc/efun/acos
new file mode 100644
index 0000000..2487958
--- /dev/null
+++ b/doc/efun/acos
@@ -0,0 +1,8 @@
+SYNOPSIS
+        float acos(float)
+
+BESCHREIBUNG
+        Liefert der Arkuskosinus des Argumentes.
+
+SIEHE AUCH
+        sin(E), asin(E), cos(E), tan(E), atan(E), atan2(E)
diff --git a/doc/efun/add_action b/doc/efun/add_action
new file mode 100644
index 0000000..8b0f778
--- /dev/null
+++ b/doc/efun/add_action
@@ -0,0 +1,149 @@
+add_action(E)
+FUNKTION:
+     void add_action(string fun, string cmd)
+     void add_action(string fun, string cmd, int flag)
+
+ARGUMENTE:
+     fun
+	gerufene Methode
+     cmd
+	ausloesendes Verb
+     flag
+	Flag fuer unscharfe Interpretation
+
+BESCHREIBUNG:
+     Generell: Das Verhalten dieser efun wird durch AddCmd aus
+               /std/thing/commands.c komfortabler widergespiegelt.
+               Darauf sollte man zurueckgreifen.
+
+     Legt fuer "cmd" (ein Kommandoverb) eine im entsprechenden Objekt
+     zu rufende Methode fest.
+
+     Diese Methode bekommt die Argumente als String uebergeben und
+     muss 0 (fuer einen Fehler) oder 1 (fuer Erfolg) zurueckgeben.
+
+     Der Parser durchsucht bei einer Spielereingabe die Liste der
+     eingetragenen Kommandos nach passenden Kommandos und ruft die
+     zugehoerigen Methoden in den Objekten bis eine der Methoden
+     1 zurueckgibt oder die Liste durchlaufen wurde. In dem Fall
+     kommt die Fehlermeldung (notify_fail()) zum Einsatz.
+
+     Mit Werten != 0 fuer "flag" legt man eine unscharfe Auswertung
+     fest, im Beispiel: Die Methode "action_fun" bei einem
+      add_action("action_fun", "hops", 1);
+     wird sowohl beim Kommando "hops" als auch bei Kommandos wie
+     "hopse", "hopseblub", ... gerufen werden.
+     Dieses Flag sollte vorsichtig verwendet werden.
+
+     Es gibt drei moegliche Verhaltensweise, die per "flag" eingeschaltet
+     werden koennen:
+     Wenn das Argument <flag> AA_SHORT (d.h. 1) ist, koennen Argumente von
+     cmd ohne einen Leerschlag an cmd angehaengt sein. An die Funktion fun
+     werden alle Zeichen nach dem ersten Leerschlag als Argument
+     uebergeben.
+
+     Wenn <flag> AA_NOSPACE (d.h. 2) ist, koennen die Argumente wiederum
+     ohne Leerschlag ans Verb angehaengt sein. Im Unterschied zu AA_SHORT
+     werden alle Zeichen nach dem Verb als Argument an die Funktion
+     uebergeben. Die Zeichen, welche nicht durch einen Leerschlag vom
+     Verb getrennt sind, werden ZUSAETZLICH von query_verb() zurueck
+     gegeben.
+
+     Wenn <flag> AA_IMM_ARGS (3) ist, werden alle Zeichen nach dem Verb
+     als Argument uebergeben, nicht aber von query_verb() beruecksichtigt.
+
+
+BEMERKUNGEN:
+     (1) add_action() sollte immer innerhalb von init() benutzt werden
+     (2) das definierende Objekt muss im inventory des Spielers oder
+         environment() des kommandogebenden Lebewesens sein
+     (3) im init() spaeter eingetragene Kommandos oder spaeter hinzu-
+         kommende Objekte werden bei der Kommandoauswertung VOR den
+         alten beruecksichtigt
+         (Daher bewegt sich das Xtool der Magier regelmaessing neu in
+          das Inventory, um immer "erstes" Objekt zu sein.)
+
+BEISPIELE:
+     // ein Kommando in einem Schirm
+     void init() {
+      ::init();
+      add_action("action_oeffne", "oeffne");
+     }
+
+     int action_oeffne(string str) {
+      if(stringp(str) && id(str))	// Argument da und bin ich gemeint?
+       write("Du oeffnest den Schirm.\n");
+       say(break_string(this_player()->Name(WER)+" oeffnet einen Schirm.",78));
+       return 1;
+      }
+      notify_fail("Was willst Du oeffnen?\n");
+      return 0;
+     }
+
+     // frueher beliebt um Spieler lahmzulegen, da es _alle_ Kommandos 
+     // triggert -> siehe heute jedoch dafuer eher P_DISABLE_COMMANDS
+     // Achtung: siehe Implikation von (3)
+     add_action("action_nothing", "",1 );
+     ...
+     int action_nothing(string str) {
+      write("Dir sind die Haende gebunden.\n");
+      return 1;
+     }
+
+     Beispiele fuer die Verwendung des Argumentes "flag":
+      add_action("disfunc", "disconnect", AA_NOSPACE);
+
+        Die Funktion disfunc() wird aufgerufen, wenn der Spieler "disconnect"
+        oder eine Abkuerzung davon (zum Beispiel "di" oder "discon") eingibt.
+        Laengere Worte (zum Beispiel "disconnecting") werden NICHT erkannt.
+        Um rauszufinden, was der Spieler tatsaechlich eingegeben hat, muss
+        query_verb() aufgerufen werden.
+
+      add_action("fun", "cmd");
+      add_action("fun", "scmd", AA_SHORT);
+      add_action("fun", "ncmd", AA_NOSPACE);
+      add_action("fun", "icmd", AA_IMM_ARGS);
+
+        Die folgende Tabelle zeigt, was die oben aufgefuehrten Kommandos bzw.
+        <flag> fuer Werte an query_verb() und als Argumente an die Funktion
+        fun uebergeben:
+        |-------------------|--------------|----------------------------|
+        | Eingabe           | query_verb() | Argument der Funktion fun  |
+        |-------------------|--------------|----------------------------|
+        | "cmd"             | "cmd"        | 0                          |
+        | "cmd bla fasel"   | "cmd"        | "bla fasel"                |
+        |-------------------|--------------|----------------------------|
+        | "scmd"            | "scmd"       | 0                          |
+        | "scmd bla"        | "scmd"       | "bla"                      |
+        | "scmdbla"         | "scmdbla"    | 0                          |
+        | "scmd bla fasel"  | "scmd"       | "bla fasel"                |
+        | "scmdbla fasel"   | "scmdbla"    | "fasel"                    |
+        |-------------------|--------------|----------------------------|
+        | "ncmd"            | "ncmd"       | 0                          |
+        | "ncmd bla"        | "ncmd"       | " bla"                     |
+        | "ncmdbla"         | "ncmdbla"    | "bla"                      |
+        | "ncmd bla fasel"  | "ncmd"       | " bla fasel"               |
+        | "ncmdbla fasel"   | "ncmdbla"    | "bla fasel"                |
+        |-------------------|--------------|----------------------------|
+        | "icmd"            | "icmd"       | 0                          |
+        | "icmd bla"        | "icmd"       | " bla"                     |
+        | "icmdbla"         | "icmd"       | "bla"                      |
+        | "icmd bla fasel"  | "icmd"       | " bla fasel"               |
+        | "icmdbla fasel"   | "icmd"       | "bla fasel"                |
+        |-------------------|--------------|----------------------------|
+
+
+GESCHICHTE
+        Das Argument <flag> < 0 wird seit 3.2@127 unterstuetzt, aber erst ab
+        LDMud 3.2.8 richtig implementiert. LDMud 3.2.9 fuehrte das AA_IMM_ARGS
+        Flag ein. Ab LDMud 3.5 kann man Closures als Funktionen uebergeben.
+
+SIEHE AUCH:
+			remove_action(E), init(E), enable_commands(E)
+     Fehlermeldung:	notify_fail(E), _notify_fail(E)
+     Argumentstring:	query_verb(E), _unparsed_args(L)
+     obsolet:		add_verb(E), add_xverb(E)
+     alternativ:	AddAction(L), AddCmd(L)
+                        P_DISABLE_COMMANDS
+
+24. Maerz 2004 Gloinson@MG
diff --git a/doc/efun/all_environment b/doc/efun/all_environment
new file mode 100644
index 0000000..993f493
--- /dev/null
+++ b/doc/efun/all_environment
@@ -0,0 +1,33 @@
+SYNOPSIS
+        object *all_environment();
+        object *all_environment(object ob);
+
+ARGUMENTE:
+        ob - das Objekt, dessen environment()s gewuenscht werden
+
+BESCHREIBUNG
+        Gibt ein Array mit allen Umgebungen des Objekts <ob> zurueck.
+        Wenn <ob> nicht angegeben wird, wird standardmaessig this_object()
+        verwendet.
+
+        Wenn <o> keine Umgebung hat oder zerstoert wurde, wird 0 zurueck
+        gegeben.
+
+BEMERKUNGEN:
+        Das zurueckgegebene Array ist so angelegt, dass die innerste Umgebung
+        am Anfang des Arrays steht, und die aeusserste Umgebung am Ende.
+
+BEISPIEL
+        (Die gesuchte Fackel befindet sich in einem Behaelter, den Wargon bei
+        sich traegt. Wargon steht in seinem Workroom.)
+
+        ob = all_environment(find_object("/obj/fackel#32"));
+        => ob = ({[+wueste/durian/behaelter#31],[/magier:wargon],[~/workroom]})
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.6. Ein Vorschlag von TubMud.
+
+SIEHE AUCH
+        environment(E), all_inventory(E)
+
+7.Feb 2007 Gloinson
\ No newline at end of file
diff --git a/doc/efun/all_inventory b/doc/efun/all_inventory
new file mode 100644
index 0000000..ad6eea1
--- /dev/null
+++ b/doc/efun/all_inventory
@@ -0,0 +1,17 @@
+all_inventory
+FUNKTION:
+     object *all_inventory()
+     object *all_inventory(object ob)
+
+BESCHREIBUNG:
+     Liefert ein Feld (Array) mit allen Objekten, die sich im Objekt ob
+     befinden (bzw. in this_object(), sofern kein Argument angegeben
+     wurde).
+     Gerade bei sehr vollen Objekten bietet sich eher first_inventory(),
+     next_inventory() an.
+
+SIEHE AUCH:
+     first_inventory(E), next_inventory(E), deep_inventory(E),
+     all_environment(E), environment(E)
+
+22. Maerz 2004 Gloinson
diff --git a/doc/efun/allocate b/doc/efun/allocate
new file mode 100644
index 0000000..c591c5e
--- /dev/null
+++ b/doc/efun/allocate
@@ -0,0 +1,38 @@
+SYNOPSIS
+        *mixed allocate(int size);
+        *mixed allocate(int size, mixed init_value);
+
+        *mixed allocate(*int sizes);
+        *mixed allocate(*int sizes, mixed init_value);
+
+BESCHREIBUNG
+        Alloziert ein Array von <size> Elementen. Die Anzahl Elemente muss
+        groesser sein als 0, darf aber das Systemmaximum (normalerweise 1000)
+        nicht uebersteigen. Wird <init_value> angegeben, wird allen Elementen
+        dieser als Anfangswert zugewiesen. Wenn <init_value> ein Mapping oder
+        ein Array ist, wird fuer jedes Element eine einfache Kopie erstellt.
+        Wird <init_value> nicht angegeben, sind alle Elemente 0.
+
+        In der zweiten Form (mit einem Feld von <sizes> anstelle nur einer
+        <size>) erzeugt allocate() ein mehrdimensionales Array, ein Array aus
+        Arrays.
+
+        Heute wird allocate() kaum mehr benoetigt, weil Arrays mit dem
+        +-Operator addiert werden koennen und mit dem ({})-Operator
+        initialisiert. Der einzige Nutzen der Funktion ist, grosse leere
+        oder initialisierte Arrays zu erzeugen.
+
+BEISPIEL
+        string *buffer;
+        buffer = allocate(50);
+        buffer = allocate(50, "");
+
+        buffer = allocate( ({ 2, 3 }) )
+          --> ({ ({ 0, 0, 0 }), ({ 0, 0, 0 }) })
+
+AENDERUNGEN
+        LDMud 3.2.9 fuehrte den Anfangswert <init_value> und die
+            Initialisierung von mehrdimensionalen Arrays ein.
+
+SIEHE AUCH
+        sizeof(E)
diff --git a/doc/efun/and_bits b/doc/efun/and_bits
new file mode 100644
index 0000000..f066d61
--- /dev/null
+++ b/doc/efun/and_bits
@@ -0,0 +1,23 @@
+SYNOPSIS
+        string and_bits(string str1, string str2);
+
+BESCHREIBUNG
+        <str1> und <str2> seien beides Bitstrings. Das Resultat von and_bits()
+        ist ein Bitstring mit dem binaeren Und von <str1> und <str2>, das
+        heisst ein String, in dem ein Bit nur gesetzt ist, wenn das
+        entsprechende Bit in beiden Strings <str1> und <str2> gesetzt ist.
+
+BEISPIEL
+        string s1, s2, s3;
+
+        s1 = set_bit("", 3); s1 = set_bit(s1, 15);  -> s1 is "( ("
+        s2 = set_bit("", 3); s2 = set_bit(s2, 4);   -> s2 is "8"
+
+        s3 = and_bits(s1, s2);
+
+        --> s3 ist jetzt "8", d.h. ein Bitstring, in dem nur das 3. Bit
+            gesetzt ist.
+
+SIEHE AUCH
+        clear_bit(E), set_bit(E), test_bit(E), next_bit(E), last_bit(E),
+        count_bits(E), or_bits(E), xor_bits(E), invert_bits(E), copy_bits(E)
diff --git a/doc/efun/apply b/doc/efun/apply
new file mode 100644
index 0000000..aaa30a5
--- /dev/null
+++ b/doc/efun/apply
@@ -0,0 +1,35 @@
+SYNOPSIS
+        mixed apply(closure cl, mixed arg, ...);
+
+BESCHREIBUNG
+        Wertet die Closure <cl> aus. Wenn <cl> keine Closure ist, wird <cl>
+        unveraendert zurueck geliefert und alle Argumente <arg> werden
+        ignoriert.
+
+        Es gibt einen kleinen Unterschied zu funcall(), das ja im Wesentlichen
+        das gleiche tut (naemlich, eine Closure auswerten): wenn das letzte
+        Argument von apply() ein Array ist, wird jedes Element dieses Arrays
+        zu einem separaten zusaetzlichen Parameter der Closure umgewandelt.
+
+        Eine moegliche Anwendung waere:
+            mixed eval(object ob,string func,mixed *args)
+            {
+                return apply(#'call_other,ob,func,args);
+            }
+
+        Das fuehrt zu folgenden Aufrufen:
+            ob->func(args[0],args[1],...,args[sizeof(args)-1])
+
+        Waere stattdessen funcall() aufgerufen worden, so haette das ergeben:
+            ob->func(args)
+
+        Eine wichtige Anwendung von apply() ist das Auswerten des
+        Array-Arguments in "varargs" Funktionen.
+
+AENDERUNGEN
+        Eingefuehrt in 3.2@70.
+        LDMud 3.2.8 fuehrte ein, dass das erste Argument zurueck gegeben wird,
+            wenn es sich nicht um eine Closure handelt.
+
+SIEHE AUCH
+        funcall(E), closures(LPC), varargs(LPC)
diff --git a/doc/efun/asin b/doc/efun/asin
new file mode 100644
index 0000000..44c6d4d
--- /dev/null
+++ b/doc/efun/asin
@@ -0,0 +1,8 @@
+SYNOPSIS
+        float asin(float)
+
+BESCHREIBUNG
+        Liefert der Arkussinus des Argumentes.
+
+SIEHE AUCH
+        sin(E), cos(E), acos(E), tan(E), atan(E), atan2(E)
diff --git a/doc/efun/atan b/doc/efun/atan
new file mode 100644
index 0000000..105b748
--- /dev/null
+++ b/doc/efun/atan
@@ -0,0 +1,11 @@
+SYNOPSIS
+        float atan(int|float)
+
+BESCHREIBUNG
+        Liefert den Arkustangens des Argumentes.
+
+AENDERUNGEN
+        LDMud 3.2.9: Ganzzahlen (Integers) als Argument hinzugefuegt.
+
+SIEHE AUCH
+        atan2(E), sin(E), cos(E), tan(E), asin(E), acos(E)
diff --git a/doc/efun/atan2 b/doc/efun/atan2
new file mode 100644
index 0000000..e4cd78a
--- /dev/null
+++ b/doc/efun/atan2
@@ -0,0 +1,16 @@
+SYNOPSIS
+        float atan2 (int|float y, int|float x)
+
+BESCHREIBUNG
+        Liefert den Winkel der Polarkoordinaten des Punktes (x, y) im
+        Bereich (-Pi, Pi].
+
+        Man beachte die Vertauschung der Koordinaten x und y in der
+        Parameter-Liste, die die Abfolge in der Steigungs-Winkel-Umrechnung
+        atan(y / x) widerspiegelt.
+
+AENDERUNGEN
+        LDMud 3.2.9: Ganzzahlen (Integers) als Argumente hinzugefuegt.
+
+SIEHE AUCH
+        sin(E), cos(E), tan(E), asin(E), acos(E), atan(E)
diff --git a/doc/efun/attach_erq_demon b/doc/efun/attach_erq_demon
new file mode 100644
index 0000000..032b13b
--- /dev/null
+++ b/doc/efun/attach_erq_demon
@@ -0,0 +1,37 @@
+SYNOPSIS
+        int attach_erq_demon(object ob, int do_close)
+        int attach_erq_demon(string obname, int do_close)
+
+DESCRIPTION
+        This privileged efun is to set/change the connection of the
+        driver to the external erq demon, thus in effect changing the
+        demons.
+
+        The connection of the given interactive 'ob'ject is taken away(!)
+        from it and stored as the erq-connection. The object itself is
+        then no longer needed, but may stay alive - it is just another
+        non-interactive object then.
+
+        In the second form, the string will be combined as suffix to
+        the filename ERQFILE<obname>, which is then the binary to be
+        forked off as new erq demon. The communication with this erq
+        will take place over unix domain sockets. ERQFILE defaults to
+    BINDIR/erq, where BINDIR is the configuration value for the
+    executable directory.
+
+        If there is alreay an erq demon connected to the driver, the
+        function will fail unless 'do_close' (default 0) is specified
+        as 1 (or any other odd integer): then the old connection will
+        be closed before attaching the new.
+        The efun returns 1 on success, else 0.
+
+EXAMPLE
+        To restart the (default) erq, write in
+        master.c::stale_erq(closure c):
+          attach_erq_demon("", 0);
+
+HISTORY
+        Introduced in 3.2.1@61.
+
+SEE ALSO
+        send_erq(E), erq(C)
diff --git a/doc/efun/baseof b/doc/efun/baseof
new file mode 100644
index 0000000..d64cc20
--- /dev/null
+++ b/doc/efun/baseof
@@ -0,0 +1,16 @@
+SYNOPSIS
+        int baseof (struct b, struct s)
+
+DESCRIPTION
+        Test if the type of struct <b> is a base of struct <s> (the
+        values of <b> and <s> are irrelevant). Results are:
+          0: <b> is not a base of <s>, nor is <b> of equal type as <s>
+               (though <s> might be a base of <b>).
+          1: <b> is a true base of <s>
+          2: <b> and <s> are the same struct type
+
+HISTORY
+        Introducted in LDMud 3.3.344.
+
+SEE ALSO
+        structp(E), structs(LPC)
diff --git a/doc/efun/binary_message b/doc/efun/binary_message
new file mode 100644
index 0000000..08bc321
--- /dev/null
+++ b/doc/efun/binary_message
@@ -0,0 +1,36 @@
+GESCHUETZT
+SYNOPSIS
+        int binary_message(int *|string messages, int flags);
+
+BESCHREIBUNG
+        Liest den Output aus und sendet diesen direkt mit write() OHNE IAC
+        QUOTING. Die Nachricht kann Nullen enthalten, wenn sie als
+        int * angegeben sind. Die Nachricht wird an this_object() ausgegeben,
+        aber nur, wenn dieses interaktiv ist.
+
+        Der Rueckgabewert ist die Anzahl tatsaechlich gedruckter Zeichen. Eine
+        allfaellige "allowed charset" Einstellung wird uebergangen.
+
+        <flags> werden bitweise interpretiert und koennen ueber das binaere
+        Oder verbunden werden.
+
+        Bit 0 (Wert 1): wenn gesetzt, wird add_message() anstelle von
+            write() verwendet. So muss der Output nicht zuerst ausgelesen
+            werden, allerdings erfolgt die Ausgabe nicht sofort. Auch kann
+            dann die Anzahl effektiv uebertragener Zeichen nicht bestimmt
+            werden - der Rueckgabewert ist nicht definiert.
+
+        Bit 1 (Wert 2): Der Puffer wird ausgelesen, _nachdem_ die Nachricht
+            angefuegt wurde. Ist nur in Verbindung mit Bit 0 sinnvoll.
+
+        Die Idee hinter den Flags ist, dass das Senden von Kommandocodes
+        zum Beispiel fuer Farben an den vorhandenen Filtern fuer erlaubte
+        Zeichen vorbeigeschleust werden muss, jedoch nicht wichtig genug
+        ist, um die Verschwendung von Bandbreite mittels einer
+        synchronen Uebertragung zu rechtfertigen.
+
+AENDERUNGEN
+        Eingefuehrt in 3.2.1@40.
+
+SIEHE AUCH
+        set_connection_charset(E)
diff --git a/doc/efun/bind_lambda b/doc/efun/bind_lambda
new file mode 100644
index 0000000..6075a33
--- /dev/null
+++ b/doc/efun/bind_lambda
@@ -0,0 +1,16 @@
+SYNOPSIS
+        closure bind_lambda(closure, object ob)
+
+DESCRIPTION
+        Binds an unbound lambda closure to an object and return it.
+        The efun can also be used to rebind an efun-, simul-efun
+        or operator closure to a different object.
+
+        If the optional argument ob is not this_object(), the privilege
+        violation ("bind_lambda", this_object(), ob) occurs.
+
+HISTORY
+        Introduced in 3.2@82.
+
+SEE ALSO
+        lambda(E), unbound_lambda(E), apply(E), funcall(E), closures(LPC)
diff --git a/doc/efun/blueprint b/doc/efun/blueprint
new file mode 100644
index 0000000..1c77497
--- /dev/null
+++ b/doc/efun/blueprint
@@ -0,0 +1,23 @@
+VORLAEUFIG
+SYNOPSIS
+        object blueprint()
+        object blueprint(string|object ob);
+
+BESCHREIBUNG
+        Die Efun liefert den Blueprint fuer das angegeben Objekt <ob> oder
+        fuer this_object(), wenn nicht angegeben.
+
+        Wenn der Blueprint zerstoert wurde, liefert die Funktion 0. Fuer
+        Objekte mit replace_program() liefert die Funktion den Blueprint des
+        ersetzenden Programs.
+
+BEISPIEL
+        blueprint("/obj/ding");                 -> liefert /obj/ding
+        blueprint(find_object("/obj/ding"));    -> liefert /obj/ding
+        blueprint(clone_object("/obj/ding"));   -> liefert /obj/ding
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9.
+
+SIEHE AUCH
+        clones(E), clone_object(E)
diff --git a/doc/efun/break_point b/doc/efun/break_point
new file mode 100644
index 0000000..584453e
--- /dev/null
+++ b/doc/efun/break_point
@@ -0,0 +1,11 @@
+OPTIONAL
+SYNOPSIS
+        void break_point()
+
+DESCRIPTION
+        This function is for system internal use and should never be called by
+        user objects. It is supposed to check the stack integrity and aborts
+        the driver when it detects corruption.
+
+SEE ALSO
+        shutdown(E), swap(E)
diff --git a/doc/efun/call_direct b/doc/efun/call_direct
new file mode 100644
index 0000000..94ab028
--- /dev/null
+++ b/doc/efun/call_direct
@@ -0,0 +1,75 @@
+SYNOPSIS
+        unknown call_direct (object ob, string fun, mixed arg, ...)
+        unknown call_direct (object *ob, string fun, mixed arg, ...)
+
+DESCRIPTION
+        Call a member function <fun> in another object <ob> with an
+        the argument(s) <arg...>. Result is the value returned from
+        the called function (or 0 for non-existing or void functions).
+
+        This efun is a twin to call_other(), with the difference
+        being that call_direct() never calls a default method.
+
+        Optionally the driver can be configured to accept an array of
+        objects as <ob>: the function is called with the same
+        arguments in all the given objects.  The single results are
+        collected in an array and yield the final result.  Array
+        elements can be objects or the names of existing objects;
+        destructed objects and 0s will yield a '0' as result, but
+        don't cause an error.
+
+        If the array-calling mode is available, the macro
+        __LPC_ARRAY_CALLS__ is defined.
+
+        The object(s) can be given directly or via a string (i.e. its
+        object_name). If it is given by a string and the object does not
+        exist yet, it will be loaded.
+
+        ob->fun(args) and "ob_name"->fun(args) is equivalent to
+        call_other(ob, "fun", args). Nowadays the ob_name string can
+        also be a variable.
+
+        ob->fun(args) and ob->"fun"(args) are equivalent to
+        call_other(ob, "fun", args). ob->(fun)(args) are equivalent
+        to call_other(ob, fun, args) where fun is a runtime expression
+        returning the function name.
+
+        If ob::fun does not define a publicly accessible function, the
+        call_other() will return 0, which is indistinguishable from
+        a function returning 0.
+
+        "publicly accessible" means "public" when calling other objects,
+        and "public" or "static" when calling this_object(). "private"
+        and "protected" function can never be called with call_other().
+
+        The return type of call_other() is 'any' be default. However,
+        if your LPC code uses #pragma strict_types, the return type is
+        'unknown', and the result of call_other() must be casted to
+        the appropriate type before you can use it for anything.
+
+EXAMPLES
+        // All the following statements call the lfun QueryProp()
+        // in the current player with the argument P_SHORT.
+        string str, fun;
+
+        str = (string)call_direct(this_player(), "QueryProp", P_SHORT);
+        fun = "QueryProp";
+        str = (string)call_direct(this_player(), fun, P_SHORT);
+
+        You have to do explicit type casting because of the unknown
+        return type, if you have set #pragma strict_types.
+
+        // This statement calls the lfun short() in all interactive users
+        // and stores the collected results in a variable.
+        string * s;
+
+        s = (string *)call_direct(users(), "short");
+
+
+HISTORY
+        Introduced in LDMud 3.3.113 with the H_DEFAULT_METHOD hook.
+        LDMud 3.2.10 made the call on arrays of objects configurable.
+
+SEE ALSO
+        call_other(E), call_direct_resolved(E), create(A), pragma(LPC),
+        extern_call(E), function_exists(E), functions(LPC)
diff --git a/doc/efun/call_direct_resolved b/doc/efun/call_direct_resolved
new file mode 100644
index 0000000..e227934
--- /dev/null
+++ b/doc/efun/call_direct_resolved
@@ -0,0 +1,25 @@
+SYNOPSIS
+        int call_direct_resolved(mixed result, object ob, string func, ...)
+
+DESCRIPTION
+        Similar to call_direct(). If ob->func() is defined and publicly
+        accessible, any of the optional extra arguments are passed to
+        ob->func(...). The result of that function call is stored in
+        result, which must be passed by reference.
+
+        This efun is a twin to call_resolved(), with the difference
+        being that call_direct_resolved() never calls a default method.
+
+        The efun returns 1 if the function could be called.
+        If ob::fun does not define a publicly accessible function, the
+        efun will return 0.
+
+        ob can also be an object_name. If a string is passed for ob, and
+        no object with that name does exist, an error occurs.
+
+HISTORY
+        Introduced in LDMud 3.3.113 with the H_DEFAULT_METHOD hook.
+
+SEE ALSO
+        call_direct(E), call_resolved(E), function_exists(E),
+        find_object(E)
diff --git a/doc/efun/call_other b/doc/efun/call_other
new file mode 100644
index 0000000..b487f4d
--- /dev/null
+++ b/doc/efun/call_other
@@ -0,0 +1,94 @@
+SYNOPSIS
+        unknown call_other(object ob, string fun, mixed arg, ...);
+        unknown call_other(object *ob, string fun, mixed arg, ...);
+
+        ob->fun(mixed arg, ...);
+        ob->"fun"(mixed arg, ...);
+        ob->(fun)(mixed arg, ...);
+
+BESCHREIBUNG
+        Ruft die in einem anderen Objekt <ob> die Funktion <fun> mit den
+        Argumenten <arg...> auf und gibt den Wert zurueck, der von der
+        Funktion <fun> geliefert wird (oder 0 fuer nicht existierende oder
+        als void deklarierte  Funktionen).
+
+        Optional kann der Driver so konfigueriert werden, dass auch ein Array
+        von Objekten *<ob> akzeptiert wird. Die Funktion <fun> wird dann fuer
+        jedes Objekt <ob> im Array mit den Argumenten <arg...> aufgerufen.
+        Die einzelnen Resultate werden in einem Array zusammen gefasst und
+        dieses Array dann als Endresultat von call_other() zurueck gegeben.
+        Die Elemente von *<ob> koennen Objekte oder Namen von Objekten sein.
+        Zerstoerte Objekte und 0 als Element geben eine 0 zurueck, fuehren
+        aber nicht zu einem Fehler.
+        call_other() auf Arrays von Objekten ist im MG aktiviert, welches
+        durch das Define __LPC_ARRAY_CALLS__ angezeigt wird.
+
+        Das Objekt (bzw. die Objekte) kann direkt oder ueber einen String
+        (d.h. den Objektnamen) angegeben werden. Wenn ein String angegeben
+        wird und das Objekt noch nicht existiert, wird es geladen.
+
+        ob->fun(args) und "ob_name"->fun(args) sind aequivalent zu
+        call_other(ob, "fun", args). Heutzutage kann "ob_name" auch eine
+        Variable sein. ob->(fun)(args) ist aequivalent zu
+        call_other(ob, fun, args), wobei <fun> ein Runtime Ausdruck ist,
+        der den Funktionsnamen liefert.
+
+        Wenn das Objekt <ob> keine oeffentliche Funktion mit dem Namen <fun>
+        enthaelt, gibt call_other() den Wert 0 zurueck. Dies ist nicht
+        unterscheidbar von einer Funktion <fun>, die 0 zurueck liefert.
+        Oeffentlich bedeutet "public", wenn andere Objekte aufgerufen
+        werden und "public" oder "static", wenn der Aufruf an this_object()
+        ergeht. Funktionen, die "private" oder "protected" definiert sind,
+        koennen niemals von call_other() aufgerufen werden.
+
+        Der Rueckgabewert von call_other() ist standardmaessig 'any'. Falls
+        aber #pragma strict_types gesetzt ist, ist der Rueckgabewert
+        'unknown', und das Resultat des call_other() muss zuerst auf einen
+        zutreffenden Variablentyp gecastet werden, bevor man es fuer etwas
+        verwenden kann.
+
+BEISPIELE
+        Die nachfolgenden Beispiele rufen alle die Funktion "QueryProp" auf
+        mit dem Argument P_SHORT.
+
+            string str, fun;
+            str = (string)call_other(this_player(), "QueryProp", P_SHORT);
+            fun = "QueryProp";
+            str = (string)call_other(this_player(), fun, P_SHORT);
+
+            str = (string)this_player()->QueryProp(P_SHORT);
+            str = (string)this_player()->"QueryProp"(P_SHORT);
+            fun = "QueryProp";
+            str = (string)this_player()->(fun)(P_SHORT);
+
+        Solange #pragma strict_types gesetzt ist, muss man das Resultat von
+        call_other() explizit auf einen passenden Typ casten, weil
+        call_other() unknown liefert.
+
+        Das folgende Statement ruft die lfun short() in allen aktiven
+        Benutzern auf und speichert das gesammelte Resultat in einer
+        Variablen:
+
+            string * s;
+            s = (string *)users()->short();
+
+        Objekte laden (obsolet):
+            Compat: call_other("/users/luser/thing", "???", 0);
+
+        Das sieht etwas merkwuerdig aus, wurde aber oft verwendet, um einfach
+        ein Objekt zu laden. Dazu wurde die (nicht existierende) Funktion
+        "???" im Objekt aufgerufen. Gluecklicherweise gibt es heute zu
+        diesem Zweck die Efun load_object(), also bitte nicht mehr verwenden.
+
+AENDERUNGEN
+        In LDMud 3.2.8 wurden die folgenden Verbesserungen eingefuehrt:
+          - die Formen x->"y"() und x->(y)() werden erkannt;
+          - die Form x->y() kollidiert nicht mehr mit einer lokalen Variablen,
+            die auch "y" heisst.
+          - eine simul_efun call_other() erwischt auch Aufrufe der Form ->().
+          - call_other kann auch auf Arrays von Objekten angewandt werden.
+        LDMud 3.2.10 machte den Aufruf von Objektarrays konfigurierbar.
+
+SIEHE AUCH
+        function_exists(E), call_resolved(E), create(A), pragma(LPC),
+        extern_call(E), functions(LPC)
diff --git a/doc/efun/call_out b/doc/efun/call_out
new file mode 100644
index 0000000..dba28b3
--- /dev/null
+++ b/doc/efun/call_out
@@ -0,0 +1,54 @@
+SYNOPSIS
+        void call_out(string fun, int delay, mixed arg, ...)
+        void call_out(closure cl, int delay, mixed arg, ...)
+
+DESCRIPTION
+        Set up a call to function fun in the current object, or to
+        closure cl. The call will take place in delay seconds, with the
+        remaining argument list provided. delay can be 0, but be careful!
+
+        call_out() saves and restores the current user. It is now
+        possible to use say() or write() which rely on a current
+        user to be something useful.
+
+        call_out() can only call functions which are publicly accessible,
+        i.e. "public" and "static" functions. "private" and "protected"
+        functions can't be called.
+
+        The execution of the call_out()s implies a simple (not
+        exhaustive) measure against rabbits: the evaluation costs of
+        those call_outs() executing at the same time are summed up on
+        a per-UID base. If the summed-up costs exceed the given maximum,
+        a 'too long evaluation' error will occur and any remaining
+        call_outs() of this user scheduled for the same time are
+        discarded.
+
+        Callouts are executed every 2s, therefore your delay may be bigger
+        than the you specified. In fact, a Callout with delay==0 is
+        executed within 2s from the function call to call_out(). 
+
+WARNING
+        Please never use call_out(...,0) in recursive Callouts and be at least
+        very careful in other cases. That Callout would be executed directly
+        after your current function and you really get 0 delay.
+
+EXAMPLE
+        call_out("RefreshMe", 10);
+
+        This will call the function RefreshMe() in 10 seconds without
+        any arguments. The function RefreshMe() can then call out
+        itself again which will result in a loop (not in a recursion)
+        which can be used to check or set up things in the object in
+        intervals. Be aware that callouts are stored in a linear
+        list, and so are somewhat expensive for the driver.
+
+        And YES: self-replicating call_out()s, where each call_out()
+        creates two or more other call_out()s in a loop (so called
+        'rabbits') slow the mud down very fast, and are even able
+        to crash it. No need to try it yourself.
+
+SEE ALSO
+        remove_call_out(E), call_out_info(E), find_call_out(E),
+        this_player(E), reset(A), heart_beat(A)
+
+05.11.06 Zesstra
diff --git a/doc/efun/call_out_info b/doc/efun/call_out_info
new file mode 100644
index 0000000..61d5ff5
--- /dev/null
+++ b/doc/efun/call_out_info
@@ -0,0 +1,19 @@
+SYNOPSIS
+        mixed *call_out_info();
+
+BESCHREIBUNG
+        Liefert Informationen ueber alle anhaengigen call_out()s. Das Resultat
+        ist ein Array, bei dem jedes Element wiederum aus einem Array besteht,
+        das einen call_out() beschreibt. Jedes dieser Unter-Arrays enthaelt 3
+        oder mehr Elemente:
+
+        1. Das Objekt, in dem die Funktion oder die Closure aufgerufen wird.
+        2. Die Funktion oder Closure.
+        3. Der verbleibende Delay bis zum Aufruf.
+        4ff. Die (optionalen) Argumente.
+
+        call_out()s fuer zerstoerte Objekte werden nicht in der Liste
+        aufgefuehrt.
+
+SIEHE AUCH
+        call_out(E), remove_call_out(E), find_call_out(E)
diff --git a/doc/efun/call_resolved b/doc/efun/call_resolved
new file mode 100644
index 0000000..0103766
--- /dev/null
+++ b/doc/efun/call_resolved
@@ -0,0 +1,18 @@
+SYNOPSIS
+        int call_resolved(mixed result, object ob, string func, mixed arg,...);
+
+BESCHREIBUNG
+        Die Funktion ist aehnlich zu call_other(). Wenn obj->func() definiert
+        und oeffentlich ist, werden alle Argumente <arg> an obj->func()
+        uebergeben. Das Resultat dieses Funktionsaufrufes wird in <result>
+        gespeichert und muss deshalb als Referenz uebergeben werden.
+
+        Wenn <ob> zerstoert wurde oder keine oeffentlich zugaengliche Funktion
+        <func> definiert, liefert call_resolved() 0 fuer Fehler, 1 bei Erfolg.
+
+        <ob> kann auch ein object_name() sein. Wenn <ob> ein String ist und
+        das Objekt mit diesem Namen nicht gefunden oder geladen werden kann,
+        tritt ein Fehler auf.
+
+SIEHE AUCH
+        call_other(E), function_exists(E), find_object(E)
diff --git a/doc/efun/caller_stack b/doc/efun/caller_stack
new file mode 100644
index 0000000..d1b5777
--- /dev/null
+++ b/doc/efun/caller_stack
@@ -0,0 +1,33 @@
+SYNOPSIS
+        *object caller_stack();
+        *object caller_stack(int add_interactive);
+
+BESCHREIBUNG
+        Liefert ein Array der previous_object(), die einen call_other() auf
+        this_object() verursacht haben. Dabei entspricht previous_object(i)
+        caller_stack()[i].
+
+        Wenn die Funktion mit <add_interactive> (als wahr) aufgerufen wird,
+        wird this_interactive() dem Array hinzugefuegt, oder 0, wenn kein
+        this_interactive() existiert.
+
+BEISPIEL
+        Das interaktive Objekt A gibt ein Kommando ein, das im Objekt B eine
+        Funktion aufruft, die auf das Objekt C verweist, welches wiederum
+        eine Funktion im Objekt D aufruft.
+
+        Wenn D nun caller_stack() aufruft, ergibt dies: ({C,B}).
+        Fuer caller_stack(1) ergibt die Funktion: ({C,B,A}).
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.6, vorgeschlagen von TubMud.
+
+ANMERKUNG
+        Aufrufe von "alien lfun closures" (vergleiche symbol_function())
+        erzeugen zwei Eintraege im Stack, wenn das gebundene Objekt sich vom
+        Objekt der Closure unterscheidet: der erste Eintrag steht fuer das
+        gebundene Objekt, der zweite fuer das Closure-Objekt.
+
+SIEHE AUCH
+        caller_stack_depth(E), previous_object(E), this_interactive(E),
+        call_other(E)
diff --git a/doc/efun/caller_stack_depth b/doc/efun/caller_stack_depth
new file mode 100644
index 0000000..230827e
--- /dev/null
+++ b/doc/efun/caller_stack_depth
@@ -0,0 +1,9 @@
+SYNOPSIS
+        int caller_stack_depth(void)
+
+DESCRIPTION
+        Returns the number of previous objects on the stack. This
+        can be used for security checks.
+
+SEE ALSO
+        caller_stack(E), previous_object(E), call_other(E), call_resolved(E)
diff --git a/doc/efun/capitalize b/doc/efun/capitalize
new file mode 100644
index 0000000..bbb82b7
--- /dev/null
+++ b/doc/efun/capitalize
@@ -0,0 +1,12 @@
+SYNOPSIS
+        string capitalize(string str)
+
+DESCRIPTION
+        Convert the first character in str to upper case, and return
+        the new string.
+
+EXAMPLES
+        capitalize("heya!") -> "Heya!"
+
+SEE ALSO
+        lower_case(E), upper_case(E)
diff --git a/doc/efun/cat b/doc/efun/cat
new file mode 100644
index 0000000..665fe4d
--- /dev/null
+++ b/doc/efun/cat
@@ -0,0 +1,29 @@
+SYNOPSIS
+        int cat(string path, int start, int num)
+
+DESCRIPTION
+        List the file found at path.
+
+        In most installations it is not legal to have '..' or spaces
+        in the path. This commands is normally connected to the "cat"
+        command that wizards have. It is also used by the "help"
+        command. The optional arguments start and num are start line
+        number and number of lines. If they are not given the whole
+        file is printed from the beginning.
+
+        The total number of lines will not exceed a system limit, which
+        normally is 50 lines.
+
+        cat() returns the number of lines read and printed if success,
+        0 if no such file or no lines to read (if start or len is < 0,
+        or if the file has less than start lines).
+
+EXAMPLE
+        cat("/doc/efun/cat", 5, 9);
+
+        This will print out the file "/doc/efun/cat" begining at line
+        5 and ending with line 13.
+
+SEE ALSO
+        get_dir(E), file_size(E), read_file(E), read_bytes(E),
+        valid_read(M)
diff --git a/doc/efun/catch b/doc/efun/catch
new file mode 100644
index 0000000..c436b7d
--- /dev/null
+++ b/doc/efun/catch
@@ -0,0 +1,64 @@
+SYNOPSIS
+        mixed catch(expr, expr, ...);
+        mixed catch(expr, expr, ...; modifiers);
+
+BESCHREIBUNG
+        Wertet die Ausdruecke <expr> aus. Wenn kein Fehler auftritt, wird 0
+        zurueck geliefert. Wenn ein Fehler auftritt, wird die Ausfuehrung an
+        diesem Punkt abgebrochen und ein String mit der Fehlermeldung wird
+        zurueck gegeben.
+
+        Systemeigene Fehlermeldungen beginnen mit einem "*", benutzerdefinierte
+        Fehlermeldungen aus throw() und raise_error() (sofern von 0
+        verschieden), werden unveraendert zurueck geliefert.
+
+        Wenn zum Zeitpunkt, zu dem catch() aufgerufen wird, weniger als
+        __CATCH_EVAL_COST__ Rechenticks uebrig sind, wird ein Laufzeitfehler
+        RTE innerhalb von catch() erzeugt (und somit wie jeder andere
+        Fehler abgefangen) und es werden keine Ausdruecke <expr> aus catch()
+        ausgewertet. Der Modifikator 'reserve' kann verwendet werden,
+        einen anderen Wert fuer die Reserve anzugeben.
+
+        Das Verhalten von catch() kann durch <modifiers> veraendert werden:
+
+            'nolog':    Normalerweise wird der Fehler im Fehlerlog
+                        gespeichert, um die Fehlersuche zu erleichtern. Wird
+                        'nolog' gesetzt, wird dieser Log-Eintrag unterdrueckt.
+            'publish':  Normalerweise wird master::runtime_error() fuer einen
+                        Fehler innerhalb eines catch() nicht aufgerufen. Mit
+                        diesem <modifier> wird runtime_error() trotzdem
+                        aufgerufen.
+            'reserve <expr>': <expr> muss eine ganzen Zahl groesser 0
+                        ergeben, welche dann als Rechenreserve anstelle
+                        von __CATCH_EVAL_COST__ verwendet wird. Das Minimum
+                        ist 2 * __MASTER_EVAL_COST__ .
+
+        catch() an sich ist nicht besonders laufzeit-intensiv: es braucht
+        nur etwas mehr Zeit als ein Intra-Objekt Funktionsaufruf.
+        
+        throw() ist ebenfalls nicht sehr teuer, da lediglich einige
+        interne Strukturen aufgeraeumt werden muessen.
+
+        Echte Laufzeitfehlers (ob nun mit oder ohne catch()) auf
+        der anderen Seite ist sehr zeitintensiv.
+
+        catch ist nicht im eigentlichen Sinne eine Efun, sondern eine Compiler
+        Anweisung.
+
+BEISPIEL
+        object obj;
+        string err;
+        if(err = catch(obj = clone_object("/foo/bar/baz")))
+            write("Kann das Objekt nicht clonen. Grund: "+err+"\n");
+
+AENDERUNGEN
+        LDMud 3.2.9 fuehrte den 'nolog' catch() als experimentelles Feature
+            ein.
+        LDMud 3.2.10 implementierte 'nolog' als offizielle Form und fuehrte
+            zudem 'publish' ein.
+        LDMud 3.3.559 verlegte den Test auf verbleibende Rechenticks in die
+            vom catch() umschlossenen Ausfuehrung.
+        LDMud 3.3.560 fuegte den Modifikator 'reserve' ein.
+
+SIEHE AUCH
+        throw(E), raise_error(E), predefined(D), runtime_error(M)
diff --git a/doc/efun/ceil b/doc/efun/ceil
new file mode 100644
index 0000000..dc44244
--- /dev/null
+++ b/doc/efun/ceil
@@ -0,0 +1,19 @@
+SYNOPSIS
+        float ceil(int|float arg);
+
+BESCHREIBUNG
+        Rundet <arg> zur naechsten ganzen Zahl auf und liefert das Resultat
+        zurueck. Wenn <arg> ein Integer ist, wird das Resultat in float
+        konvertiert.
+
+BEISPIEL
+        ceil(4.5);      ergibt:  5.0
+        ceil(-4.5);     ergibt: -4.0
+        ceil(5);        ergibt:  5.0
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.7.
+        LDMud 3.2.9 fuehrte neu Integer als moegliche Argumente ein.
+
+SIEHE AUCH
+        abs(E), floor(E)
diff --git a/doc/efun/clear_bit b/doc/efun/clear_bit
new file mode 100644
index 0000000..93f15ec
--- /dev/null
+++ b/doc/efun/clear_bit
@@ -0,0 +1,35 @@
+SYNOPSIS
+        string clear_bit(string str, int n);
+
+BESCHREIBUNG
+        Gibt einen neuen String zurueck, in dem das n-te Bit im String <str>
+        nicht gesetzt ist. Dabei wird <str> selbst nicht veraendert.
+
+        Jedes Zeichen enthaelt sechs Bits. So kann in jedem Zeichen eine Zahl
+        zwischen 0 und 63 (2^6=64) gespeichert werden. Das erste Zeichen ist
+        der Leerschlag " " mit Wert 0. Das erste Zeichen im String ist jenes
+        mit den niedrigsten Bits (0 bis 5).
+
+BEISPIELE
+        string s;
+        s = clear_bit("_", 5);
+
+        Weil "_" den hoechsten moeglichen Wert enthaelt (63), enthaelt die
+        Variable s nun das Zeichen "?", das dem Wert 31 entspricht (63-2^5=31).
+
+        string s;
+        s = clear_bit("?<",3);
+        s = clear_bit(s, 8);
+
+        s enthaelt nun den String "78". "?" entspricht dem Wert 31 und "<" dem
+        Wert 28. "?<" entspricht also dem Wert 31+28<<6=31+1792=1823, was in
+        Binaerschreibweise (hoechstes Bit rechts) 11111000111 ergibt. Werden
+        aus dieser Zahl die Bits 3 und 8 (die Nummerierung beginnt mit dem
+        0. Bit) ergibt dann: 11101000011. Die ersten 6 Bits 010111 sind in
+        Dezimalschreibweise 23. Die zweiten 6 Bits (0)11000 ergeben 24 in
+        Dezimalschreibweise. Nun entspricht der Wert 23 dem Zeichen "7" und
+        der Wert 24 dem Zeichen "8". Der String s enthaelt also "78".
+
+SIEHE AUCH
+        set_bit(E), next_bit(E), last_bit(E), test_bit(E), count_bits(E),
+        and_bits(E), or_bits(E), xor_bits(E), invert_bits(E), copy_bits(E)
diff --git a/doc/efun/clone_object b/doc/efun/clone_object
new file mode 100644
index 0000000..f622bef
--- /dev/null
+++ b/doc/efun/clone_object
@@ -0,0 +1,23 @@
+SYNOPSIS:
+        object clone_object(string name)
+
+DESCRIPTION:
+        Clone a new object from definition name, and give it a new unique
+	name. Return the new object.
+        
+        The original, called blue print, used for cloning, should not be
+	used in the game, only be used for cloning. The cloned objects
+	contain only the data but the blue print also the function code.
+	The blue print is the one without a unique number at the end of
+	the object's object_name(). The clone_object() function never
+	returns a blue print.
+        
+        Note that the pathname must be complete, which means there are no
+        relative paths allowed.
+
+EXAMPLE:
+        object torch;
+        torch = clone_object("/obj/torch");
+
+SEE ALSO:
+        destruct(E), move_object(E), uids(C)
diff --git a/doc/efun/clonep b/doc/efun/clonep
new file mode 100644
index 0000000..afff71a
--- /dev/null
+++ b/doc/efun/clonep
@@ -0,0 +1,28 @@
+SYNOPSIS
+        int clonep ()
+        int clonep (object obj)
+        int clonep (string obj)
+        int clonep (mixed  arg)
+
+BESCHREIBUNG
+        Liefert 1, wenn das angegebene Objekt ein Klon ist, ansonsten 0.
+        Das Objekt kann dabei auch durch seinen Objekt-Namen angegeben werden.
+        Wird kein Argument uebergeben, so wird this_object() getestet.
+        Liefert 0, wenn das Argument von einem anderen Typ.
+        Ein Objekt, dessen Programm mittels replace_program() ersetzt wurde,
+        zaehlt nicht als Klon.
+
+BEISPIEL
+        object o;
+        o = clone_object("/obj/ding");
+        write(clonep(o));                          --> schreibt "1"
+        write(clonep("/obj/ding"))                 --> schreibt "0"
+
+        (Im COMPAT_MODE "obj/ding" als Dateinahmen benutzen)
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.6, geaendert in 3.2.7, so dass Objekte mit
+        ersetzten Programmen nicht mehr als Klone zaehlen.
+
+SIEHE AUCH
+        load_name(E), clone_object(E), clones(E)
diff --git a/doc/efun/clones b/doc/efun/clones
new file mode 100644
index 0000000..23e20d2
--- /dev/null
+++ b/doc/efun/clones
@@ -0,0 +1,38 @@
+SYNOPSIS
+        *object clones();
+        *object clones(int what);
+        *object clones(string|object obj [, int what]);
+
+BESCHREIBUNG
+        Diese Efun liefert ein Array mit allen Clones eines bestimmten
+        Blueprints. Dabei unterliegt das Array den normalen Systemlimiten.
+
+        Wenn <obj> angegeben ist, werden alle Clones des Blueprints von <obj>
+        (oder von <obj> selbst, falls <obj> ein Blueprint ist) ausgegeben,
+        sonst die Clone des aktuellen Objekts bzw. die Clone des Blueprints
+        des aktuellen Objekts. Wenn <obj> als String angegeben ist, muss es
+        der Name eines existierenden Objekts sein.
+
+        <what> waehlt aus, wie Clone von aelteren Versionen des Blueprints
+        zu behandeln sind:
+            == 0: liefert nur die Clone des aktuellen Blueprints (Standard)
+            == 1: liefert nur die Clone der alten Blueprint-Version
+            == 2: liefert alle Clones aller Blueprint-Versionen
+
+        Wenn der Treiber mit DYNAMIC_COSTS kompiliert wurde, sind die Kosten
+        fuer diese Funktion proportional zur Anzahl Objekte im Spiel.
+
+BEISPIEL
+        object o, p;
+        o = clone_object("/std/thing"); /* oder "std/thing" im COMPAT_MODE */
+        destruct(find_object("/std/thing"));
+        p = clone_object("/std/thing");
+
+        clones("/std/thing")    --> ergibt ({ p })
+        clones("/std/thing", 0) --> ergibt ({ p })
+        clones("/std/thing", 1) --> ergibt ({ o })
+        clones("/std/thing", 2) --> ergibt ({ o, p })
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.8.
+        LDMud 3.2.9 fuehrte die dynamischen Kosten ein.
diff --git a/doc/efun/closurep b/doc/efun/closurep
new file mode 100644
index 0000000..25a2829
--- /dev/null
+++ b/doc/efun/closurep
@@ -0,0 +1,11 @@
+SYNOPSIS
+        int closurep(mixed arg)
+
+BESCHREIBUNG
+        Liefert 1, wenn das Argument eine Closure ist, ansonsten 0.
+
+AENDERUNGEN
+        Eingefuehrt in 3.2@70.
+
+SIEHE AUCH
+        intp(E), referencep(E), symbolp(E)
diff --git a/doc/efun/command b/doc/efun/command
new file mode 100644
index 0000000..09d6c0b
--- /dev/null
+++ b/doc/efun/command
@@ -0,0 +1,50 @@
+command()
+FUNKTION:
+     int command(string str)
+     int command(string str, object ob)
+
+BESCHREIBUNG:
+     Fuehrt str wie ein Kommando welches direkt vom Nutzer abgegeben wurde
+     aus. Alle Effekte des Kommandos wirken sich auf this_object() oder,
+     falls angegeben, auf das Objekt <obj> aus.
+
+     Der Rueckgabewert ist >=1 fuer Erfolg und 0 fuer Misserfolg.
+     Rueckgabewert ist im Erfolgsfall die Hoehe der EvalCost in Ticks.
+
+     Wenn command() auf ein anderes Objekt angewendet wird, koennen auf
+     diesem Wege keine "static" deklarierten Funktionen aufgerufen werden,
+     um etwas Schutz vor unerlaubten Aufrufen zu geben.
+
+     Kommandi werden gestapelt, das heisst, nach der Ausfuehrung von <str>
+     werden die alten Werte fuer this_player(), query_verb() etc. wieder
+     hergestellt (ein Kommando kann dazu fuehren, dass ein Kommando
+     ausgefuehrt wird).
+
+BEMERKUNGEN:
+     Die meisten in Lebewesen definierten Kommandofunktionen sind vor
+     aeusserem Aufrufen durch "static" oAe geschuetzt. Zum Ausfuehren dieser
+     Kommandos muss command_me(L) eingesetzt werden, um diesen Schutz zu
+     umgehen.
+
+BEISPIELE:
+     Siehe command_me(L) fuer einen Vergleich.
+	 
+     // #1 Ein NPC nimmt und zuendet eine herumliegende /std/lightsource an
+     object f = present("\nlichtquelle", environment());
+     if(f && command("nimm lichtquelle"))
+       if(command("zuende lichtquelle an"))
+         tell_room(environment(), Name(WER)+" freut sich.\n");
+       else
+         tell_room(environment(), Name(WER)+" schaut bedroeppelt.\n");
+     
+     // #2 Ein NPC traegt seine Sachen
+     clone_object("/ruestung/sommerkleid")->move(this_object(), 2);
+     command("trage kleid")
+     // aequivalent und besser ist hier:
+     AddItem("/ruestung/sommerkleid", REFRESH_REMOVE|CLONE_WEAR);
+
+SIEHE AUCH
+     command_stack(E), notify_fail(E), enable_commands(E), get_eval_cost(E)
+     command_me(L)
+
+6 Sep 2012 Gloinson
diff --git a/doc/efun/command_stack b/doc/efun/command_stack
new file mode 100644
index 0000000..d3a4e46
--- /dev/null
+++ b/doc/efun/command_stack
@@ -0,0 +1,34 @@
+GESCHUETZT
+SYNOPSIS
+        #include <sys/commands.h>
+
+        *mixed command_stack();
+
+BESCHREIBUNG
+        Liefert ein Array, das den Kommando Stack beschreibt. Das Array
+        umfasst command_stack_depth() Eintraege, der erste davon beschreibt
+        das Top-Level Kommando, der letze Eintrag das aktuelle Kommando.
+
+        Jeder Eintrag ist wiederum ein Array mit folgenden Eintraegen:
+
+            string [CMD_VERB]:      das Verb dieses Kommandos
+            string [CMD_TEXT]:      der volle Text des Kommandos
+            object [CMD_ORIGIN]:    der urspruengliche Kommandogeber
+            object [CMD_PLAYER]:    der momentane Kommandogeber
+            mixed  [CMD_FAIL]:      der Inhalt von notify_fail() (oder 0)
+            mixed  [CMD_FAILOBJ]:   das Objekt, welches notify_fail() gesetzt
+                                    hat
+
+        CMD_ORIGIN und CMD_PLAYER sind fuer gewohenlich das gleiche Objekt.
+        Es gibt nur einen Unterschied, wenn der modify_command Hook den
+        Kommandogeber mit set_this_player() aendert.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.7.
+        LDMud 3.2.8 fuegte den CMD_FAILOBJ Eintrag hinzu.
+
+ANMERKUNG
+        Jeder der Eintraege im Array kann 0 sein.
+
+SIEHE AUCH
+        command(E), command_stack_depth(E), notify_fail(E)
diff --git a/doc/efun/command_stack_depth b/doc/efun/command_stack_depth
new file mode 100644
index 0000000..37205b5
--- /dev/null
+++ b/doc/efun/command_stack_depth
@@ -0,0 +1,13 @@
+GESCHUETZT
+SYNOPSIS
+        int command_stack_depth();
+
+BESCHREIBUNG
+        Liefert die Anzahl der verschachtelten Kommandi, also die Tiefe des
+        Command Stacks.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.7.
+
+SIEHE AUCH
+        command(E), command_stack(E)
diff --git a/doc/efun/configure_driver b/doc/efun/configure_driver
new file mode 100644
index 0000000..fa2407b
--- /dev/null
+++ b/doc/efun/configure_driver
@@ -0,0 +1,133 @@
+SYNOPSIS
+        #include <configuration.h>
+
+        void configure_driver(int what, mixed data)
+
+DESCRIPTION
+        This efun configures runtime adjustable bahviour of the driver.
+
+        Sets the option <what> to the value <data>.
+
+        This function always causes the privilege_violation
+        ("configure_driver", this_object(), what, data).
+
+        <what> == DC_MEMORY_LIMIT
+           Set new soft and hard memory limits for the driver.
+           <data> is expected to be an array with two elements, which have to
+           be integers giving the amount of memory in bytes.
+           ({<soft memory limit>, <hard memory limit>})
+
+        <what> == DC_ENABLE_HEART_BEATS
+           Globally enable the calling of Heartbeats, if <data> is 1,
+           globally disable them if <data> is 0.
+           If called during heartbeat processing, the change comes into effect
+           at the next backend cycle.
+
+        <what> == DC_LONG_EXEC_TIME
+           Set the time considered as (too) long for top-level executions. If
+           an execution exceeds this time, a stack trace will be written to
+           the debug log. The execution will continue at that point.
+           <data> is an integer and measured in microseconds.
+           A time of 0 disables the detection of long executions.
+
+        <what> == DC_DATA_CLEAN_TIME
+           Sets the average time between clean-ups of an objects data
+           structures. This is not to be confused with the standard cleanup
+           time which determines when H_CLEAN_UP is called. A long time for
+           data cleanup may lead to larger memory consumption and prevents
+           destructed objects being cleaned up. A too short time may lead to
+           high loads and lag on the machine. The actual time delay will be a
+           time between 0.9*DC_DATA_CLEAN_TIME and 1.1*DC_DATA_CLEAN_TIME.
+           Default at driver startup are 3600s.
+           <data> is an integer and measured in seconds.
+
+        <what> == DC_TLS_CERTIFICATE
+           Sets the current certificate used for new TLS sessions.
+           It can be one of the certificates in the key directory
+           (command line option --tls-keydirectory) or the main
+           certificate (given with --tls-certfile).
+           Default is the main certificate or else the first
+           certificate found in the directory. The chosen certificate at the
+           time of the tls_init_connection() call is used for that connection.
+           <data> is a string containing the SHA1 fingerprint
+           of the certificate with hexadecimal numbers,
+           it may contain colons or whitespaces (for example
+           "5A:FE:CA:57:1E:50:5E:1E:C7:ED:BA:11:AD:50:10:75:0F:7A:1E:50").
+           When loading certificates their fingerprints are printed
+           on stdout and into the logfile.
+
+        <what> == DC_TLS_DHE_PARAMETER
+           Sets new parameters for the Diffie-Hellman keyexchange for new TLS
+           sessions. The paramters must be given as a PEM encoded string
+           (e.g. the output of 'openssl dhparam -5 2048').
+           If <data> is 0, the built-in defaults will be restored.
+           If importing the new parameters fails (e.g. due to an incorrect
+           format), the driver tries to keep the old parameters 
+
+        <what> == DC_TLS_CIPHERLIST
+           Sets a new list of ciphers (OpenSSL) or priorities (GnuTLS) to use.
+           For the correct format, please refer to the help of 'openssl
+           ciphers' or documentation of priority strings in GnuTLS.
+           With GnuTLS a syntax error in the list causes an error.
+           With OpenSSL an error is only raised of none of the given ciphers
+           could be selected.
+           By default, the preferred ciphers of the driver take precedence
+           This can be changed in the priority strings for GnuTLS, but
+           currently not for OpenSSL.
+
+        <what> == DC_EXTRA_WIZINFO_SIZE
+           Indicate that the wizlist should contain an array of the given size
+           with extra info for each wizard. A negative value indicates
+           a non-array value.
+
+           The value is only used to allocate a proper empty 'extra' value
+           for newly created wizlist entries.
+
+        <what> == DC_DEFAULT_RUNTIME_LIMITS
+           Sets the default runtime limits, that will be used for each thread.
+           They will be in effect as the initial limits with the next thread.
+           The limits must be given as an array with the following entries:
+
+             int[LIMIT_EVAL]:         the max number of eval costs
+             int[LIMIT_ARRAY]:        the max number of array entries
+             int[LIMIT_MAPPING_SIZE]: the max number of mapping values
+             int[LIMIT_MAPPING_KEYS]: the max number of mapping entries
+             int[LIMIT_BYTE]:         the max number of bytes handled with
+                                      one read_bytes()/write_bytes() call.
+             int[LIMIT_FILE]:         the max number of bytes handled with
+                                      one read_file()/write_file() call.
+             int[LIMIT_CALLOUTS]:     the number of callouts at one time.
+             int[LIMIT_COST]:         how to account the current cost.
+             int[LIMIT_MEMROY]:       the max. number of bytes which can be 
+                                        _additionally_ allocated/used
+                                        _per top-level execution thread_.
+
+          The limit settings recognize three special values:
+
+            LIMIT_UNLIMITED:  the limit is deactivated
+            LIMIT_KEEP:       the former setting is kept
+            LIMIT_DEFAULT:    the 'global' default setting is used.
+
+          For LIMIT_COST, the special values have these meaning:
+            LIMIT_UNLIMITED:  at maximum 1 tick is accounted
+            LIMIT_KEEP:        LIMIT_COST is set to 0
+            LIMIT_DEFAULT:     LIMIT_COST is set to -100
+
+        <what> == DC_SWAP_COMPACT_MODE
+           Sets free swap space shall be reused immediately to keep
+           the swap file as small as possible.
+           (Same as the --swap-compact command line switch.)
+
+HISTORY
+        Introduced in LDMud 3.3.719.
+        DC_ENABLE_HEART_BEATS was added in 3.5.0.
+        DC_LONG_EXEC_TIME was added in 3.5.0.
+        DC_DATA_CLEAN_TIME was added in 3.5.0.
+        DC_EXTRA_WIZINFO_SIZE was added in 3.5.0.
+        DC_TLS_CERTIFICATE was added in 3.5.0.
+        DC_TLS_DHE_PARAMETER was added in 3.5.0.
+        DC_TLS_CIPHERLIST was added in 3.5.0.
+        DC_SWAP_COMPACT_MODE was added in 3.5.0.
+
+SEE ALSO
+        configure_interactive(E)
diff --git a/doc/efun/configure_interactive b/doc/efun/configure_interactive
new file mode 100644
index 0000000..b7d145d
--- /dev/null
+++ b/doc/efun/configure_interactive
@@ -0,0 +1,139 @@
+SYNOPSIS
+        #include <configuration.h>
+
+        void configure_interactive(object ob, int what, mixed data)
+
+DESCRIPTION
+        Sets the option <what> to the value <data> on the interactive <ob>
+        or the default for all interactives if <ob> is 0.
+
+        If the first argument <ob> is not this_object(), the privilege
+        violation ("configure_interactive", this_object(), ob, what, data)
+        occurs.
+
+        As <what>, the following arguments are accepted:
+
+        <what> == IC_MAX_WRITE_BUFFER_SIZE
+          Sets the maximum amount of data to be held pending for writing
+          per player to <data> bytes. A value of -1 means unlimited,
+          0 deactivates the write buffer.
+
+        <what> == IC_SOCKET_BUFFER_SIZE
+          Changes the socket buffer size to the given size in bytes.
+          Not every operating system might provide this option to
+          change the buffer size.
+
+          The buffer size is used for sending, when the remote side isn't
+          getting the data fast enough. When the socket buffer is full,
+          the driver will buffer in its internal write buffer (see
+          IC_MAX_WRITE_BUFFER_SIZE). When that gets full, too, then
+          messages are discarded.
+
+        <what> == IC_COMBINE_CHARSET_AS_STRING
+          Set the set of characters which can be combined into a single
+          string when already received en-bloc in charmode from the
+          interactive user <ob>. Non-combinable characters and single
+          received characters are returned as separate strings as usual.
+
+          The newline '\n' and the NUL character '\0' are always
+          non-combinable.
+
+          The given string should contain all combinable characters.
+          If given as the number 0, the default combine charset is
+          re-established.
+
+        <what> == IC_COMBINE_CHARSET_AS_ARRAY
+          Set the set of characters which can be combined into a single
+          string, just like IC_COMBINE_CHARSET_AS_STRING.
+
+          The given array shall contain an array of up to 32 integers
+          that are interpreted as 8-bit-values. Each character is encoded
+          as one bit (ASCII characters 0-7 in the first integer, and so on).
+          So a character <n> is treated as combinable if
+
+              array[n/8] & (1 << n%8)
+
+          If the array contains less elements, the missing elements will
+          be regarded as 0 (non-combinable characters).
+
+        <what> == IC_CONNECTION_CHARSET_AS_STRING
+          Set the set of characters which can be output to the interactive
+          user <ob>. All other characters are discarded. (This does not
+          apply to binary_message()).
+
+          The given string should contain all allowed characters.
+          If given as the number 0, the default charset is re-established.
+
+        <what> == IC_CONNECTION_CHARSET_AS_ARRAY
+          Set the set of characters which can be output to the interactive
+          user <ob>, just like IC_CONNECTION_CHARSET_AS_STRING.
+
+          The given array shall contain an array of up to 32 integers
+          that are interpreted as 8-bit-values. Each character is encoded
+          as one bit (ASCII characters 0-7 in the first integer, and so on).
+          So a character <n> is allowed to be output if
+
+              array[n/8] & (1 << n%8)
+
+          If the array contains less elements, the missing elements will
+          be regarded as 0 (not allowed, ie. to be discarded).
+
+        <what> == IC_QUOTE_IAC
+          Sets whether the character 255 (telnet IAC) shall be quoted
+          by prepending another IAC character, so it will not be interpreted
+          by the telnet protocol. Enable with 1, disable with 0. By default
+          it is enabled and does only apply if character 255 is allowed to
+          be output (ie. it is part of the connection charset).
+
+        <what> == IC_TELNET_ENABLED
+          Enables (1) or disables (0) the telnet machine for the interactive
+          user <ob>. When deactivated the driver won't handle telnet
+          negotiations (eg. H_TELNET_NEG won't be called), they will be
+          part of the user input. Also INPUT_NOECHO won't be effective
+          as the driver won't send any telnet negotiations itself.
+
+        <what> == IC_MCCP
+          Starts oder ends MCCP compression of the driver -> client traffic.
+          <data> must be the MCCP version (either TELOPT_COMPRESS or
+          TELOPT_COMPRESS2 from <telnet.h>). When the telnet machine
+          is disabled, any value other then zero will do, and the compression
+          starts without a telnet preamble.
+
+          Available only if the driver is compiled with MCCP enabled;
+          __MCCP__ is defined in that case.
+
+        <what> == IC_PROMPT
+          Sets the prompt for the interactive user <ob> to <data>. The
+          prompt can either be a string or a closure that will be called
+          each time the prompt is shown.
+
+        <what> == IC_MAX_COMMANDS
+          Sets the max number of commands the interactive user <ob> is
+          allowed to execute per second to <data>. A negative value means
+          'unlimited' and is the setting for newly created connections.
+
+          A 'command' in this context means every received data packet
+          which causes a LPC call - actions and calls to input_to()
+          alike.
+
+        <what> == IC_MODIFY_COMMAND
+          Sets an object that will act as a modifier for each command.
+          All commands for the interactive user <ob> will be passed to
+          data->modify_command() before actually being executed.
+          <data> must be given as an object.
+
+          When an object is set, the H_MODIFY_COMMAND hook wont be
+          called anymore. 0 as argument will stop the command modification
+          and reinstall the use of that driver hook.
+
+          This mechanism is intended to expand aliases on quicktypers
+          or the like. The name of the lfun called can be changed
+          from modify_command() to something else using the
+          H_MODIFY_COMMAND_FNAME hook.
+
+
+HISTORY
+        Introduced in LDMud 3.3.719.
+
+SEE ALSO
+        configure_driver(E)
diff --git a/doc/efun/configure_object b/doc/efun/configure_object
new file mode 100644
index 0000000..9a54789
--- /dev/null
+++ b/doc/efun/configure_object
@@ -0,0 +1,41 @@
+SYNOPSIS
+        #include <configuration.h>
+
+        void configure_object(object ob, int what, mixed data)
+
+DESCRIPTION
+        Sets the option <what> to the value <data> on the object <ob>
+        or the default for all interactives if <ob> is 0.
+
+        If the first argument <ob> is not this_object(), the privilege
+        violation ("configure_object", this_object(), ob, what, data)
+        occurs.
+
+        As <what>, the following arguments are accepted:
+
+        <what> == OC_COMMANDS_ENABLED
+          Sets whether <ob> can use commands normally accessible to
+          users (1) or not (0). This also marks the object as "living".
+
+        <what> == OC_HEART_BEAT
+          Enables (1) or disables (0) the heart beat for <ob>. The
+          driver will apply the lfun heart_beat() to the <ob> every
+          __HEARTBEAT_INTERVAL__ seconds, if it is enabled.
+          A shadow over the heart_beat() lfun will be ignored.
+
+          If the heart beat is not needed for the moment, then do disable
+          it. This will reduce system overhead.
+
+          Note that heart_beat()s are called only if there are enabled
+          via configuer_driver(DC_ENABLE_HEART_BEATS), which is the
+          default.
+
+
+        The current values for these options can be queried using
+        object_info().
+
+HISTORY
+        Introduced in LDMud 3.5.0.
+
+SEE ALSO
+        object_info(E), configure_interactive(E), configure_driver(E)
diff --git a/doc/efun/convert_charset b/doc/efun/convert_charset
new file mode 100644
index 0000000..567ce65
--- /dev/null
+++ b/doc/efun/convert_charset
@@ -0,0 +1,94 @@
+SYNOPSIS
+        string convert_charset(string str, string from_cs, string to_cs)
+
+BESCHREIBUNG
+        Der String <str> wird von Zeichensatz <from_cs> in Zeichensatz
+        <to_cs> umgewandelt und das Ergebnis zurueckgegeben.
+
+        Die Zeichensaetze werden bei Namen angegeben (sowohl Klein-
+        als auch Grossschrift ist zulaessig).
+
+        Die Funktion ist nur verfuegbar auf Systemen mit installierter
+        libiconv.
+
+        Erlaubte Zeichensaetze sind:
+
+           Der derzeitige Systemzeichensatz:
+               "" (der Leerstring)
+
+           European 
+               ASCII, ISO-8859-{1,2,3,4,5,7,9,10,13,14,15,16}, KOI8-R, KOI8-U,
+               KOI8-RU, CP{1250,1251,1252,1253,1254,1257}, CP{850,866},
+               Mac{Roman,CentralEurope,Iceland,Croatian,Romania},
+               Mac{Cyrillic,Ukraine,Greek,Turkish}, Macintosh
+
+           Semitic 
+               ISO-8859-{6,8}, CP{1255,1256}, CP862, Mac{Hebrew,Arabic}
+
+           Japanese
+               EUC-JP,   SHIFT_JIS,    CP932,    ISO-2022-JP,    ISO-2022-JP-2,
+               ISO-2022-JP-1
+
+           Chinese
+               EUC-CN,  HZ,  GBK,  GB18030,  EUC-TW,  BIG5,  CP950, BIG5-HKSCS,
+               ISO-2022-CN, ISO-2022-CN-EXT
+
+           Korean
+               EUC-KR, CP949, ISO-2022-KR, JOHAB
+
+           Armenian
+               ARMSCII-8
+
+           Georgian
+               Georgian-Academy, Georgian-PS
+
+           Tajik
+               KOI8-T
+
+           Thai
+               TIS-620, CP874, MacThai
+
+           Laotian
+               MuleLao-1, CP1133
+
+           Vietnamese
+              VISCII, TCVN, CP1258
+
+           Platform specifics
+              HP-ROMAN8, NEXTSTEP
+
+           Full Unicode
+              UTF-8
+              UCS-2, UCS-2BE, UCS-2LE
+              UCS-4, UCS-4BE, UCS-4LE
+              UTF-16, UTF-16BE, UTF-16LE
+              UTF-32, UTF-32BE, UTF-32LE
+              UTF-7
+              C99, JAVA
+
+        Auf einigen System sind auch die folgenden Zeichensaetze verfuegbar:
+
+            European 
+                CP{437,737,775,852,853,855,857,858,860,861,863,865,869,1125}
+
+            Semitic 
+                CP864
+
+            Japanese
+                EUC-JISX0213, Shift_JISX0213, ISO-2022-JP-3
+
+            Turkmen
+                TDS565
+
+            Platform specifics
+                RISCOS-LATIN1
+
+
+BEISPIEL
+        convert_charset("Hi!", "ascii", "utf-8")
+
+GESCHICHTE
+        Introduced in LDMud 3.3.531 .
+
+SEE ALSO
+
diff --git a/doc/efun/copy b/doc/efun/copy
new file mode 100644
index 0000000..39f3752
--- /dev/null
+++ b/doc/efun/copy
@@ -0,0 +1,23 @@
+SYNOPSIS
+     mixed copy(mixed arg);
+
+BESCHREIBUNG
+     Erzeugt eine flache Kopie von <arg> und liefert diese zurueck. Fuer
+     Arrays und Mappings heisst das, dass neue Arrays bzw. Mappings erzeugt
+     werden, die Kopien der Elemente des Originals enthalten. Eingebettete
+     Arrays und Mappings werden jedoch als Referenz uebergeben!
+
+     Fuer andere Werte von <arg> bewirkt diese Funktion nichts.
+
+BEISPIEL
+     mixed *a, *b;
+     a = ({ 1, ({ 21, 22 }) });
+     b = copy(a);
+     a[0] = -1; a[1][0] = -21;
+         --> a ist nun ({ -1, ({ -21, 22 }) })
+             b ist nun ({  1, ({ -21, 22 }) })
+
+SIEHE AUCH
+     deep_copy(E)
+
+10.Apr.2007 Gloinson
\ No newline at end of file
diff --git a/doc/efun/copy_bits b/doc/efun/copy_bits
new file mode 100644
index 0000000..ec68d51
--- /dev/null
+++ b/doc/efun/copy_bits
@@ -0,0 +1,41 @@
+SYNOPSIS
+        string copy_bits(string src, string dest [, int srcstart
+            [, int deststart [, int copylen]]]);
+
+BESCHREIBUNG
+        Kopiert den Bitbereich [<srcstart> .. <srcstart> + <copylen>] aus dem
+        Bitstring <src> in den Bitstring <dest> beginnend an der Position
+        <deststart>. Die alten Werte von <dest> werden dabei ueberschrieben.
+
+        Der resultierende String wird zurueck geliefert, die beiden
+        Originalstrings bleiben unbeeinflusst.
+
+        Wird <srcstart> nicht angegeben, wird <src> von Anfang an kopiert.
+        Ist <srcstart> negativ, wird vom letzten Bit her gezaehlt (d.h. -1
+        bezeichnet das letzte Bit).
+
+        Wird <deststart> nicht angegeben, wird <dest> von Anfang an kopiert.
+        Ist <deststart> negativ, wird vom letzten Bit her gezaehlt (d.h. -1
+        bezeichnet das letzte Bit).
+
+        Wird <copylen> nicht angegeben wird, so wird der gesamte Bitstring
+        <src> kopiert. Das Resultat besteht dann aus dem Bitstring <dest>
+        bis zur Position <deststart>, gefolgt von <src> ab der Position
+        <srcstart>.
+
+        Wenn <copylen> negativ ist, werden abs(<copylen>) _vor_ <srcstart> in
+        das Resultat kopiert.
+
+BEISPIELE
+        copy_bits(src, dest, 10)       === src[10..]
+        copy_bits(src, dest, 10, 5)    === dest[0..4] + src[10..]
+        copy_bits(src, dest, 10, 5, 3) === dest[0..4] + src[10..12] + dest[8..]
+
+        (Die Notation src[] / dest[] dient nur der Illustration!)
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9
+
+SIEHE AUCH
+        clear_bit(E), set_bit(E), test_bit(E), next_bit(E), last_bit(E),
+        count_bits(E), or_bits(E), xor_bits(E), invert_bits(E), and_bits(E)
diff --git a/doc/efun/copy_file b/doc/efun/copy_file
new file mode 100644
index 0000000..6e091ed
--- /dev/null
+++ b/doc/efun/copy_file
@@ -0,0 +1,19 @@
+SYNOPSIS
+        int copy_file(string from, string to)
+
+DESCRIPTION
+        The efun copy_file() will copy the file <from> to the new name <to>.
+        If <to> exists and is a directory, then <from> will be placed in that
+        directory and keep its original name.
+        
+        You must have read permission for <from> and write permission for
+        the target file to copy the file.
+        
+        On successful completion copy_file() will return 0. If any
+        occurs, 1 is returned, or a runtime is generated.
+
+EXAMPLE
+        copy_file("/players/wizard/obj.c", "/players/wizard/newobj.c");
+
+SEE ALSO
+        mkdir(E), rmdir(E), rm(E), rename(E)
diff --git a/doc/efun/cos b/doc/efun/cos
new file mode 100644
index 0000000..85f3e92
--- /dev/null
+++ b/doc/efun/cos
@@ -0,0 +1,11 @@
+SYNOPSIS
+        float cos(int|float)
+
+BESCHREIBUNG
+        Liefert den Kosinus des Argumentes.
+
+AENDERUNGEN
+        LDMud 3.2.9: Ganzzahlen (Integers) als Argument hinzugefuegt.
+
+SIEHE AUCH
+        sin(E), asin(E), acos(E), tan(E), atan(E), atan2(E)
diff --git a/doc/efun/count_bits b/doc/efun/count_bits
new file mode 100644
index 0000000..1b725d0
--- /dev/null
+++ b/doc/efun/count_bits
@@ -0,0 +1,20 @@
+SYNOPSIS
+     int count_bits(string str);
+
+BESCHREIBUNG
+     Diese Funktion zaehlt die Anzahl gesetzer Bits im Bitstring <str> und
+     liefert die Anzahl als Resultat zurueck.
+
+BEMERKUNGEN
+     Arbeitet eigentlich nur sinnvoll auf Bitstrings :)
+
+BEISPIEL
+     string s;
+     s = set_bit("", 3); s = set_bit(s, 15);
+     count_bits(s) --> liefert 2
+
+SIEHE AUCH
+     clear_bit(E), set_bit(E), test_bit(E), next_bit(E), last_bit(E),
+     or_bits(E), xor_bits(E), invert_bits(E), copy_bits(E), bitstrings (C)
+
+10.Apr.2007 Gloinson
diff --git a/doc/efun/crypt b/doc/efun/crypt
new file mode 100644
index 0000000..932089d
--- /dev/null
+++ b/doc/efun/crypt
@@ -0,0 +1,16 @@
+SYNOPSIS
+     string crypt(string str, int seed);
+     string crypt(string str, string seed);
+
+BESCHREIBUNG
+     Verschluesselt den String <str> mit dem Schluessel <seed>. <seed> kann
+     entweder ein Integer sein oder zwei Zeichen aus dem String <seed>.
+     Wenn <seed> 0 ist, wird ein zufaelliger Schluessel erzeugt.
+
+     Das Resultat enthaelt den Schluessel als die ersten beiden Zeichen.
+
+     Fuer Passwortabfragen, die ohne Echo eingegeben werden koennen sollen,
+     bietet input_to() ein spezielles Flag.
+
+SIEHE AUCH
+     md5_crypt(E), md5(E), sha1(E), hash(E), hmac(E)
\ No newline at end of file
diff --git a/doc/efun/ctime b/doc/efun/ctime
new file mode 100644
index 0000000..8db61f8
--- /dev/null
+++ b/doc/efun/ctime
@@ -0,0 +1,22 @@
+SYNOPSIS
+     string ctime(int clock);
+     string ctime(int* uclock);
+
+BESCHREIBUNG
+     Interpretiert das Argument <clock> als Anzahl Sekunden seit dem
+     01.JAN.1970, 00:00 Uhr und konvertiert dieses in einen ansehnlichen
+     String, der Datum und Zeit enthaelt. Wenn <clock> nicht angegeben
+     wird, wird time() verwendet.
+
+     Die zweite Form entspricht der ersten, ausser dass das Argument ein
+     Array mit zwei Integer Elementen ist. Das erste Element int[0] gibt
+     die Anzahl Sekunden seit dem 01.JAN.1970 an, das zweite Element
+     int[1] die Anzahl Millisekunden innerhalb dieser Sekunde.
+
+BEISPIEL
+     write(ctime()+"\n");
+
+     Dies gibt etwas aus wie "Sun Oct 26 19:28:30 2003".
+
+SIEHE AUCH
+     dtime(E), gmtime(E), localtime(E), strftime(E), time(E), utime(E)
diff --git a/doc/efun/db_affected_rows b/doc/efun/db_affected_rows
new file mode 100644
index 0000000..1ea2783
--- /dev/null
+++ b/doc/efun/db_affected_rows
@@ -0,0 +1,24 @@
+OPTIONAL
+SYNOPSIS
+        int db_affected_rows(int handle)
+
+BESCHREIBUNG
+        Resultat ist die Anzaehl der Zeilen die vom letzten SQL-Befehl
+        beeinflusst wurden, welches zum SQL-server via <handle> gesendet
+        wurde. Diese Funktion ist nuetzlich lediglich fuer DELETE-
+        und UPDATE-Befehle.
+
+        Die Funktion ist nur verfuegbar wenn der Driver mit
+        mySQL-Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __MYSQL__ definiert.
+
+        Die Efun ist privilegiert als ("mysql", "db_affected_rows").
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.9.
+        LDMud 3.2.11 machte die Efun privilegiert.
+
+SIEHE AUCH
+        db_conv_string(E), db_close(E), db_coldefs(E), db_connect(E),
+        db_exec(E), db_error(E), db_fetch(E), db_insert_id(E), db_handles(E),
+        mysql(C), privilege_violation(M)
diff --git a/doc/efun/db_close b/doc/efun/db_close
new file mode 100644
index 0000000..208176c
--- /dev/null
+++ b/doc/efun/db_close
@@ -0,0 +1,22 @@
+OPTIONAL
+SYNOPSIS
+        int db_close(int handle)
+
+BESCHREIBUNG
+        Schliesse die Verbindung <handle> zum SQL-Server. Resultat ist
+        <handle> bei Erfolg.
+
+        Die Funktion ist nur verfuegbar wenn der Driver mit
+        mySQL-Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __MYSQL__ definiert.
+
+        Die Efun ist privilegiert als ("mysql", "db_close").
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.9.
+        LDMud 3.2.11 machte die Efun privilegiert.
+
+SIEHE AUCH
+        db_affected_rows(E), db_conv_string(E), db_coldefs(E), db_connect(E),
+        db_exec(E), db_error(E), db_fetch(E), db_handles(E), db_insert_id(E),
+        mysql(C, privilege_violation(M))
diff --git a/doc/efun/db_coldefs b/doc/efun/db_coldefs
new file mode 100644
index 0000000..c135a11
--- /dev/null
+++ b/doc/efun/db_coldefs
@@ -0,0 +1,23 @@
+OPTIONAL
+SYNOPSIS
+        string * db_coldefs (int handle)
+
+BESCHREIBUNG
+        Resultat ist ein Array mit den Spaltennamen der Tabelle des
+        letzten QUERY-Befehls. Gab die Datenbank keine Tabelle zurueck, dann
+        gibt die Efun 0 als Ergebnis.
+
+        Die Funktion ist nur verfuegbar wenn der Driver mit
+        mySQL-Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __MYSQL__ definiert.
+
+        Die Efun ist privilegiert als ("mysql", "db_coldefs").
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.9.
+        LDMud 3.2.11 machte die Efun privilegiert.
+
+SIEHE AUCH
+        db_affected_rows(E), db_conv_string(E), db_close(E), db_connect(E),
+        db_exec(E), db_error(E), db_fetch(E), db_handles(E), db_insert_id(E),
+        mysql(C), privilege_violation(M)
diff --git a/doc/efun/db_connect b/doc/efun/db_connect
new file mode 100644
index 0000000..abe20c9
--- /dev/null
+++ b/doc/efun/db_connect
@@ -0,0 +1,32 @@
+OPTIONAL
+SYNOPSIS
+        int db_connect(string database)
+        int db_connect(string database, string user)
+        int db_connect(string database, string user, string password)
+
+BESCHREIBUNG
+        Stelle eine Verbindung zur SQL-Datenbank <database> des
+        lokalen SQL-Servers her. Das Ergebnis ist die Handle-Nummer fuer diese
+        Verbindung und muss fuer alle Anfragen zu dieser Datenbank verwendet
+        werden.
+
+        Existiert die Datenbank nicht, oder kann der lokale SQL-Server nicht
+        gefunden werden, wird ein Laufzeitfehler erzeugt.
+
+        Wenn angegeben, wird die Verbindung fuer <user> mit <password>
+        erzeugt.
+
+        Die Funktion ist nur verfuegbar wenn der Driver mit
+        mySQL-Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __MYSQL__ definiert.
+
+        Die Efun ist privilegiert als ("mysql", "db_connect").
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.9.
+        LDMud 3.2.11 machte die Efun privilegiert.
+
+SIEHE AUCH
+        db_affected_rows(E), db_conv_string(E), db_close(E), db_coldefs(E),
+        db_exec(E), db_error(E), db_fetch(E), db_handles(E),
+        db_insert_id(E), mysql(C), privilege_violation(M)
diff --git a/doc/efun/db_conv_string b/doc/efun/db_conv_string
new file mode 100644
index 0000000..b630d6f
--- /dev/null
+++ b/doc/efun/db_conv_string
@@ -0,0 +1,20 @@
+OPTIONAL
+SYNOPSIS
+        string db_conv_string(string str)
+
+BESCHREIBUNG
+        Wandele den String <str> in einen String um, der von der Datenbank
+        korrekt interpretiert werden kann; z.B. werden all Apostrophe
+        durch \' ersetzt.
+
+        Die Funktion ist nur verfuegbar wenn der Driver mit
+        mySQL-Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __MYSQL__ definiert.
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.9.
+
+SIEHE AUCH
+        db_affected_rows(E), db_close(E), db_coldefs(E), db_connect(E),
+        db_exec(E), db_error(E), db_fetch(E), db_handles(E), db_insert_id(E),
+        mysql(C)
diff --git a/doc/efun/db_error b/doc/efun/db_error
new file mode 100644
index 0000000..cb6d291
--- /dev/null
+++ b/doc/efun/db_error
@@ -0,0 +1,23 @@
+OPTIONAL
+SYNOPSIS
+        string db_error(int handle)
+
+BESCHREIBUNG
+        Result ist ein String, der den Fehler der letzten
+        Datenbanktransaktion beschreibt.  War die letzte Transaktion
+        erfolgreich, ist das Ergebnis 0.
+
+        Die Funktion ist nur verfuegbar wenn der Driver mit
+        mySQL-Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __MYSQL__ definiert.
+
+        Die Efun ist privilegiert als ("mysql", "db_error").
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.9.
+        LDMud 3.2.11 machte die Efun privilegiert.
+
+SIEHE AUCH
+        db_affected_rows(E), db_conv_string(E), db_close(E), db_coldefs(E),
+        db_connect(E), db_exec(E), db_fetch(E), db_handles(E), db_insert_id(E),
+        mysql(C), privilege_violation(M)
diff --git a/doc/efun/db_exec b/doc/efun/db_exec
new file mode 100644
index 0000000..274c983
--- /dev/null
+++ b/doc/efun/db_exec
@@ -0,0 +1,22 @@
+OPTIONAL
+SYNOPSIS
+        int db_exec(int handle, string statement)
+
+BESCHREIBUNG
+        Fuehre den SQL-Befehl <statement> fuer die Verbindung <handle> aus.
+        Resultat ist das Handle bei Erfolg, oder 0 bei einem Fehler.
+
+        Die Funktion ist nur verfuegbar wenn der Driver mit
+        mySQL-Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __MYSQL__ definiert.
+
+        Die Efun ist privilegiert als ("mysql", "db_exec").
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.9.
+        LDMud 3.2.11 machte die Efun privilegiert.
+
+SIEHE AUCH
+        db_affected_rows(E), db_conv_string(E), db_close(E), db_coldefs(E),
+        db_connect(E), db_error(E), db_fetch(E), db_handles(E),
+        db_insert_id(E), mysql(C), privilege_violation(M)
diff --git a/doc/efun/db_fetch b/doc/efun/db_fetch
new file mode 100644
index 0000000..b143e8a
--- /dev/null
+++ b/doc/efun/db_fetch
@@ -0,0 +1,23 @@
+OPTIONAL
+SYNOPSIS
+        mixed db_fetch(int handle)
+
+BESCHREIBUNG
+        Hole _eine_ Resultatzeile der letzten SQL-Aktion fuer Verbindung
+        <handle>. Sind keine weiteren Zeilen verfuegbar, wird 0
+        zurueckgegeben.
+
+        Die Funktion ist nur verfuegbar wenn der Driver mit
+        mySQL-Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __MYSQL__ definiert.
+
+        Die Efun ist privilegiert als ("mysql", "db_fetch").
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.9.
+        LDMud 3.2.11 machte die Efun privilegiert.
+
+SIEHE AUCH
+        db_affected_rows(E), db_conv_string(E), db_close(E), db_coldefs(E),
+        db_connect(E), db_error(E), db_exec(E), db_handles(E), db_insert_id(E),
+        mysql(C), privilege_violation(M)
diff --git a/doc/efun/db_handles b/doc/efun/db_handles
new file mode 100644
index 0000000..1a304b5
--- /dev/null
+++ b/doc/efun/db_handles
@@ -0,0 +1,24 @@
+OPTIONAL
+SYNOPSIS
+        int *db_handles()
+
+BESCHREIBUNG
+        Result ist ein Array mit allen aktiven Verbindungshandles zum
+        SQL-Server. Das Array ist nach der Zeit der letzten Aktion sortiert:
+        das zuletzt genutzte Handle kommt an erster Stelle. Sind keine
+        Verbindungen aktiv, ist das Array leer.
+
+        Die Funktion ist nur verfuegbar wenn der Driver mit
+        mySQL-Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __MYSQL__ definiert.
+
+        Die Efun ist privilegiert als ("mysql", "db_handles").
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.9.
+        LDMud 3.2.11 machte die Efun privilegiert.
+
+SIEHE AUCH
+        db_affected_rows(E), db_conv_string(E), db_close(E), db_coldefs(E),
+        db_connect(E), db_error(E), db_exec(E), db_fetch(E), db_insert_id(E),
+        mysql(C), privilege_violation(M)
diff --git a/doc/efun/db_insert_id b/doc/efun/db_insert_id
new file mode 100644
index 0000000..25e2a17
--- /dev/null
+++ b/doc/efun/db_insert_id
@@ -0,0 +1,23 @@
+OPTIONAL
+SYNOPSIS
+        int db_insert_id (int handle)
+
+BESCHREIBUNG
+        Nach dem Einfuegen einer Zeile in eine Tabelle mit einem
+        AUTO_INCREMENT-Feld, diese Efun kann dazu benutzt werden den neuen
+        Wert dieses Feldes zurueckzugeben.
+
+        Die Funktion ist nur verfuegbar wenn der Driver mit
+        mySQL-Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __MYSQL__ definiert.
+
+        Die Efun ist privilegiert als ("mysql", "db_insert_id").
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.9.
+        LDMud 3.2.11 machte die Efun privilegiert.
+
+SIEHE AUCH
+        db_affected_rows(E), db_conv_string(E), db_close(E), db_coldefs(E),
+        db_connect(E), db_error(E), db_exec(E), db_fetch(E), db_handles(E),
+        mysql(C), privilege_violation(M)
diff --git a/doc/efun/debug_message b/doc/efun/debug_message
new file mode 100644
index 0000000..07e75e6
--- /dev/null
+++ b/doc/efun/debug_message
@@ -0,0 +1,36 @@
+SYNOPSIS
+        #include <sys/debug_message.h>
+
+        void debug_message(string text);
+        void debug_message(string text, int flags);
+
+BESCHREIBUNG
+        Gibt <text> an die Ausgaenge stdout und stderr sowie an die Datei
+        <host>.debug.log, oder an eine beliebige Kombination dieser drei.
+
+        Das Argument <flag> bezeichnet durch eine Kombination von Bitflags das
+        Ziel und die Art, in der das Resultat geschrieben wird.
+
+        Die Ziel-Flags sind: DMSG_STDOUT, DMSG_STDERR und DMS_LOGFILE. Wird
+        zusaetzlich das Flag DMSG_STAMP gesetzt, erhaelt jeder Eintrag einen
+        Zeitstempel (Timestamp) im Format 'YYYY.MM.DD HH:MM:SS '.
+
+        Wenn <flags> 0 ist, weggelassen wird oder kein Ziel-Flag enthaelt,
+        wird <text> standardmaessig an stdout und ins Logfile ausgegeben.
+
+BEISPIEL
+        debug_message("Dieser Text geht an stdout und ins Logfile.\n");
+        debug_message("Dies geht an stderr.\n", DMSG_STDERR);
+        debug_message("Dies geht an stdout und stderr.\n", DMSG_STDOUT
+            | DMSG_STDERR);
+        debug_message("Dies geht an stdout und ins Logfile, mit Timestamp.\n",
+            DMSG_STAMP);
+        debug_message("Die geht an stdout, mit vorangestelltem Timestamp.\n",
+            DMSG_STDOUT | DMSG_STAMP);
+
+AENDERUNGEN
+        Eingefuehrt in 3.2.1@34.
+        LDMud 3.2.9 fuehrte das Argument <flags> ein.
+
+SIEHE AUCH
+        last_instructions(E)
diff --git a/doc/efun/deep_copy b/doc/efun/deep_copy
new file mode 100644
index 0000000..1cef9a8
--- /dev/null
+++ b/doc/efun/deep_copy
@@ -0,0 +1,27 @@
+SYNOPSIS
+        mixed deep_copy(mixed arg);
+
+BESCHREIBUNG
+        Erzeugt eine echte Kopie von <arg> und liefert diese zurueck. Fuer
+        Arrays und Mappings bedeutet dies, dass ein neues Array oder Mapping
+        erzeugt wird, das exakte Kopien der Eintraege des Originals enthaelt.
+        Eingebettete Arrays und Mappings werden ebenso echt kopiert.
+
+        Fuer andere Typen als Mappings und Arrays bewirkt diese Funktion
+        nichts.
+
+        Wenn im Driver DYNAMIC_COST definiert ist, zaehlt jedes eingebettete
+        Mapping oder Array zu den Evaluationskosten sowohl in der Groesse als
+        auch in der Einbettungstiefe.
+
+BEISPIEL
+        mixed *a, *b;
+        a = ({ 1, ({ 21, 22 }) });
+        b = deep_copy(a);
+        a[0] = -1; a[1][0] = -21;
+         --> a ist jetzt   ({ -1, ({ -21, 22 }) })
+             b bleibt      ({  1, ({  21, 22 }) })
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.6.
+        LDMud 3.2.9 fuegte die dynamischen Kosten zur Efun hinzu.
diff --git a/doc/efun/deep_inventory b/doc/efun/deep_inventory
new file mode 100644
index 0000000..51f4c66
--- /dev/null
+++ b/doc/efun/deep_inventory
@@ -0,0 +1,36 @@
+SYNOPSIS
+        *object deep_inventory();
+        *object deep_inventory(object ob);
+        object *deep_inventory(object ob, int depth)
+
+BESCHREIBUNG
+        Gibt ein Array der Objekte zurueck, die in <obj> enthalten sind.
+        Wenn <obj> nicht angegeben wird, wird standardmaessig this_object()
+        verwendet. Ebenso werden alle Objekte angegeben, die sich im Inventory
+        der in <obj> enthaltenen Objekte befinden. Die Suche verlaeuft
+        rekursiv absteigend.
+
+        Ist <depth> angegeben und ungleich 0, ist das Resultat wie folgt
+        gefiltert:
+          <depth> > 0: Nur die Objekte in den ersten <depth> Ebenen
+                       werden zurueckgegeben.
+          <depth> < 0: Nur die Objekte in der -<depth>ten Ebene werden
+                       zurueckgegeben.
+
+BEISPIEL
+        ob
+        +- ob1
+        +- ob2
+        |   +- ob21
+        |  ob3
+        |   +- ob31
+        +- ob4
+
+
+        deep_inventory(ob) => ({ob1, ob2, ob3, ob4, ob21, ob31})
+        deep_inventory(ob, 1) => ({ob1, ob2, ob3, ob4})
+        deep_inventory(ob, 2) => ({ob1, ob2, ob3, ob4, ob21, ob31})
+        deep_inventory(ob, -2) => ({ob21, ob31})
+
+SIEHE AUCH
+        first_inventory(E), next_inventory(E), all_inventory(E)
diff --git a/doc/efun/destruct b/doc/efun/destruct
new file mode 100644
index 0000000..f64de56
--- /dev/null
+++ b/doc/efun/destruct
@@ -0,0 +1,43 @@
+SYNOPSIS
+        void destruct(object ob)
+
+BESCHREIBUNG
+        Zerstoert und entfernt das Objekt ob. Ist ob == 0 (ob wurde schon
+        zerstoert), so passiert nichts. Nach dem Aufruf von destruct()
+        existieren keine globalen Variablen mehr, sondern nur noch lokale
+        Variablen und Argumente.
+
+        Wenn sich ein Objekt selbst zerstoert, wird die Ausfuehrung nicht
+        sofort abgebrochen. Wird die Efun this_object() aufgerufen, so
+        liefert diese 0 zurueck.
+
+        Die meisten Mudlibs moegen es nicht, wenn andere Objekte mittels
+        destruct() ins Daten-Nirwana geschickt werden. Stattdessen erwarten
+        sie den Aufruf einer speziellen Lfun im zu zerstoerenden Objekt,
+        welche normalerweise remove() heisst. Damit kann das zu
+        zerstoerende Objekt noch abschliessende Arbeiten im Zusammenhang
+        mit seinem Ableben erledigen, wie z. B. die Anpassung des Gewichtes
+        seiner Umgebung etc. pp.
+
+        Der Interpreter zerstoert das Objekt nicht sofort, sondern er
+        markiert es als zerstoert, entfernt es von der Liste, die alle
+        Objekte enthaelt, und fuegt es zu der Liste zu zerstoerenden
+        Objekte hinzu. Die tatsaechliche Zerstoerung geschieht erst dann,
+        wenn keine Referenzen mehr auf das Objekt existieren. Deshalb ist
+        es moeglich, dass ein Objekt noch lange nach seiner Zerstoerung
+        Speicher belegt, obwohl es von ausserhalb nicht mehr gefunden wird.
+
+BEISPIEL
+        ob->remove();
+        if(ob)        // es existiert noch, vielleicht ist die Lfun
+                      // remove() nicht definiert?
+            destruct(ob);
+
+        Dies ist die Standard-Methode, ein Objekt zu zerstoeren, ihm aber
+        vorher noch die Chance zu geben, es selber zu tun.
+
+AENDERUNGEN
+        LDMud 3.2.7: 0 (zerstoerte Objekt) als Argument annehmen.
+
+SIEHE AUCH
+        clone_object(E), remove(A)
diff --git a/doc/efun/disable_commands b/doc/efun/disable_commands
new file mode 100644
index 0000000..0b6757e
--- /dev/null
+++ b/doc/efun/disable_commands
@@ -0,0 +1,10 @@
+SYNOPSIS
+        void disable_commands();
+
+BESCHREIBUNG
+        Verbietet dem Objekt, Kommandi zu verwenden, die normalerweise Usern
+        zugaenglich sind. Diese Funktion ist das Gegenteil von
+        enable_commands().
+
+SIEHE AUCH
+        enable_commands(E)
diff --git a/doc/efun/driver_info b/doc/efun/driver_info
new file mode 100644
index 0000000..f1eaa66
--- /dev/null
+++ b/doc/efun/driver_info
@@ -0,0 +1,685 @@
+SYNOPSIS
+        #include <driver_info.h>
+
+        mixed driver_info(int what)
+
+DESCRIPTION
+        Returns some internal information about the driver.
+        The argument <what> determines which information is returned.
+
+        It can be either a configuration option as given to
+        configure_driver() or one of the following options:
+
+
+
+        Driver Environment:
+
+        <what> == DI_BOOT_TIME:
+          The time() when the driver was started.
+
+
+
+        LPC Runtime status:
+
+        <what> == DI_CURRENT_RUNTIME_LIMITS:
+          Return an array with the current runtime limits.
+          The entries in the returned array are:
+
+            int[LIMIT_EVAL]:         the max number of eval costs
+            int[LIMIT_ARRAY]:        the max number of array entries
+            int[LIMIT_MAPPING_SIZE]: the max number of mapping values
+            int[LIMIT_MAPPING_KEYS]: the max number of mapping entries
+               (LIMIT_MAPPING is an alias for LIMIT_MAPPING_KEYS)
+            int[LIMIT_BYTE]:         the max number of bytes handled with
+                                     one read_bytes()/write_bytes() call.
+            int[LIMIT_FILE]:         the max number of bytes handled with
+                                     one read_file()/write_file() call.
+            int[LIMIT_CALLOUTS]:     the number of callouts at one time.
+            int[LIMIT_COST]:         how to account the current cost.
+            int[LIMIT_MEMROY]:       the max. number of bytes which can be 
+                                     _additionally_ allocated/used 
+                                     _per top-level execution thread_
+
+          For all limits except LIMIT_COST a limit of '0' aka LIMIT_UNLIMITED
+          means 'no limit'.
+
+          The value for LIMIT_COST has these meanings:
+
+            value > 0: the execution will cost minimum(<value>, actual cost) .
+            value = 0: if the current LIMIT_EVAL is larger than the calling
+                       LIMIT_EVAL, the evaluation will cost only 10; otherwise
+                       the full cost will be accounted.
+            value < 0: (-value)% of the current evaluation cost will be
+                       accounted; -100 obviously means 'full cost'.
+
+        <what> == DI_EVAL_NUMBER:
+          Return the current evaluation number.
+          The number is incremented for each top-level call. Top-level
+          calls are initiated by the driver, usually in reaction to an
+          external event:
+              - commands (added by add_action)
+              - heart_beat, reset, clean_up
+              - calls from call_out or input_to
+              - master applies triggered by external events
+              - calls of driver hooks in reaction to external events
+              - send_erq callbacks
+              - logon in interactives
+
+          The number can be used to detect cases where the same code is
+          executed twice in the same top level evaluation (say, heart_beat),
+          and also for time stamps for ordering some events.
+
+          Please note that the counter may overflow, especially on 32 bit
+          systems. As a result, it can also be negative.
+
+
+
+        Network configuration:
+
+        <what> == DI_MUD_PORTS:
+          Returns an array with all open ports, which the driver
+          listens for player connections on.
+
+        <what> == DI_UDP_PORT:
+          Returns the port number of the UDP socket.
+
+
+
+        Memory management:
+
+        <what> == DI_MEMORY_RESERVE_USER:
+          Current size of the user memory reserve.
+          The user memory reserve is allocated at startup and is used
+          when the driver runs out of memory.
+
+        <what> == DI_MEMORY_RESERVE_MASTER:
+          Current size of the master memory reserve.
+          The master memory reserve is allocated at startup and is used
+          when the driver runs out of memory while executing master
+          code.
+
+        <what> == DI_MEMORY_RESERVE_SYSTEM:
+          Current size of the system memory reserve.
+          The system memory reserve is allocated at startup and is used
+          when the driver runs out of memory while executing internal
+          operations.
+
+
+
+        Traces:
+
+        <what> == DI_TRACE_CURRENT:
+          Returns the current stack trace in array form.
+
+          If the array has more than one entries, the first entry is 0 or
+          the name of the object with the heartbeat which started the
+          current thread; all following entries describe the call stack
+          starting with the topmost function called.
+
+          All call entries are arrays themselves with the following elements:
+
+               int[TRACE_TYPE]: The type of the call frame:
+                   TRACE_TYPE_SYMBOL (0): a function symbol (shouldn't happen).
+                   TRACE_TYPE_SEFUN  (1): a simul-efun.
+                   TRACE_TYPE_EFUN   (2): an efun closure.
+                   TRACE_TYPE_LAMBDA (3): a lambda closure.
+                   TRACE_TYPE_LFUN   (4): a normal lfun.
+
+               mixed[TRACE_NAME]: The 'name' of the called frame:
+                   _TYPE_EFUN:   either the name of the efun, or the code of
+                                 the instruction for operator closures
+                   _TYPE_LAMBDA: the numeric lambda identifier.
+                   _TYPE_LFUN:   the name of the lfun.
+
+               string[TRACE_PROGRAM]: The (file)name of the program holding
+                                      the code.
+
+               string[TRACE_OBJECT]:  The name of the object for which the code
+                                      was executed.
+               int[TRACE_LOC]:
+                   _TYPE_LAMBDA: current program offset from the start of the
+                                 closure code.
+                   _TYPE_LFUN:   the line number.
+
+        <what> == DI_TRACE_CURRENT_DEPTH:
+          Return the current number of frames on the control stack
+          (recursion depth).
+
+        <what> == DI_TRACE_CURRENT_AS_STRING:
+          Returns the current stack trace as a string.
+
+        <what> == DI_TRACE_LAST_ERROR:
+          Returns the stack trace of the last error in array form
+          (same format as DI_TRACE_CURRENT). Stack traces of errors
+          before the last GC might not be available anymore.
+
+        <what> == DI_TRACE_LAST_ERROR_AS_STRING:
+          Returns the stack trace of the last error as a string.
+
+        <what> == DI_TRACE_LAST_UNCAUGHT_ERROR:
+          Returns the stack trace of the last uncaught error in array form
+          (same format as DI_TRACE_CURRENT). Stack traces of errors
+          before the last GC might not be available anymore.
+
+        <what> == DI_TRACE_LAST_UNCAUGHT_ERROR_AS_STRING:
+          Returns the stack trace of the last uncaught error as a string.
+
+
+
+        LPC Runtime statistics:
+
+        <what> == DI_NUM_FUNCTION_NAME_CALLS:
+          Number of function calls by name (like call_other).
+
+        <what> == DI_NUM_FUNCTION_NAME_CALL_HITS:
+          Function calls by name are cached (to accelerate
+          lookup of the corresponding program code).
+          This returns the number of cache hits.
+
+        <what> == DI_NUM_FUNCTION_NAME_CALL_MISSES:
+          The number of function call cache misses.
+
+        <what> == DI_NUM_OBJECTS_LAST_PROCESSED:
+          Number of listed objects processed in the last backend cycle.
+
+        <what> == DI_NUM_HEARTBEAT_TOTAL_CYCLES:
+          Total number of heart_beats cycles so far.
+
+        <what> == DI_NUM_HEARTBEAT_ACTIVE_CYCLES:
+          Number of active heart_beat cycles executed so far
+          (ie. cycles in which at least one heart_beat() function
+          was called).
+
+        <what> == DI_NUM_HEARTBEATS_LAST_PROCESSED:
+          Number of heart_beats calls in the last backend cycle
+
+        <what> == DI_NUM_STRING_TABLE_STRINGS_ADDED:
+          Number of distinct strings added to the string table so far.
+
+        <what> == DI_NUM_STRING_TABLE_STRINGS_REMOVED:
+          Number of distinct strings removed from the string table so far.
+
+        <what> == DI_NUM_STRING_TABLE_LOOKUPS_BY_VALUE:
+          Number of string searches by value.
+
+        <what> == DI_NUM_STRING_TABLE_LOOKUPS_BY_INDEX:
+          Number of string searches by address.
+
+        <what> == DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_VALUE:
+          Number of lookup steps needed for string searches by value.
+
+        <what> == DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_INDEX:
+          Number of lookup steps needed for string searches by address.
+
+        <what> == DI_NUM_STRING_TABLE_HITS_BY_VALUE:
+          Number of successful lookups of strings by value.
+
+        <what> == DI_NUM_STRING_TABLE_HITS_BY_INDEX:
+          Number of successful lookups of strings by address.
+
+        <what> == DI_NUM_STRING_TABLE_COLLISIONS:
+          Number of distinct strings added to an existing hash chain so far.
+
+        <what> == DI_NUM_REGEX_LOOKUPS:
+          Number of requests for new regexps.
+
+        <what> == DI_NUM_REGEX_LOOKUP_HITS:
+          Number of requested regexps found in the table.
+
+        <what> == DI_NUM_REGEX_LOOKUP_MISSES:
+          Number of requested regexps not found in the table.
+
+        <what> == DI_NUM_REGEX_LOOKUP_COLLISIONS:
+          Number of requested new regexps which collided with a cached one.
+
+
+
+        Network statistics:
+
+        <what> == DI_NUM_MESSAGES_OUT:
+          Number of messages sent to a player.
+
+        <what> == DI_NUM_PACKETS_OUT:
+          Number of packets sent to a player.
+
+        <what> == DI_NUM_PACKETS_IN:
+          Number of packets received from a player.
+
+        <what> == DI_SIZE_PACKETS_OUT:
+          Number of bytes sent to a player.
+
+        <what> == DI_SIZE_PACKETS_IN:
+          Number of bytes received from a player.
+
+
+
+        Load:
+
+        <what> == DI_LOAD_AVERAGE_COMMANDS:
+          A float value that shows the number of executed player commands
+          per second.
+
+        <what> == DI_LOAD_AVERAGE_LINES:
+          A float value that shows the number of compiled code lines
+          per second.
+
+        <what> == DI_LOAD_AVERAGE_PROCESSED_OBJECTS:
+          A float value that shows the average number of objects processed
+          each backend cycle.
+
+        <what> == DI_LOAD_AVERAGE_PROCESSED_OBJECTS_RELATIVE:
+          Average number of objects processed each cycle, expressed
+          as percentage (0..1.0) of the number of present objects.
+
+        <what> == DI_LOAD_AVERAGE_PROCESSED_HEARTBEATS_RELATIVE:
+          Average number of heart_beats called each cycle, expressed
+          as fraction (0..1.0) of the number of active heartbeats.
+
+
+
+        Memory use statistics:
+
+        <what> == DI_NUM_ACTIONS:
+          Number of allocated actions.
+
+        <what> == DI_NUM_CALLOUTS:
+          Number of pending call_outs.
+
+        <what> == DI_NUM_HEARTBEATS:
+          Number of objects with a heartbeat.
+
+        <what> == DI_NUM_SHADOWS:
+          Number of allocated shadows.
+
+        <what> == DI_NUM_OBJECTS:
+          Number of objects.
+
+        <what> == DI_NUM_OBJECTS_SWAPPED:
+          Number of objects that are swapped-out.
+
+        <what> == DI_NUM_OBJECTS_IN_LIST:
+          Number of objects in the object list
+          (i.e. not destructed objects).
+
+        <what> == DI_NUM_OBJECTS_IN_TABLE:
+          Number of objects in the object table.
+
+        <what> == DI_NUM_OBJECTS_DESTRUCTED:
+          Number of destructed, but still referenced objects.
+          (Not counting DI_NUM_OBJECTS_NEWLY_DESTRUCTED).
+
+        <what> == DI_NUM_OBJECTS_NEWLY_DESTRUCTED:
+          Number of newly destructed objects (ie. objects destructed
+          in this execution thread, that will really be destroyed in
+          the next backend cycle).
+
+        <what> == DI_NUM_OBJECT_TABLE_SLOTS:
+          Number of hash slots provided by the object table.
+
+        <what> == DI_NUM_PROGS:
+          Size occupied by the object table.
+
+        <what> == DI_NUM_PROGS_SWAPPED:
+          Number of swapped-out program blocks
+
+        <what> == DI_NUM_PROGS_UNSWAPPED:
+          Number of programs that were swapped-out, are now swapped-in,
+          but still have allocated space in the swap file.
+
+        <what> == DI_NUM_ARRAYS:
+          Number of currently existing arrays.
+
+        <what> == DI_NUM_MAPPINGS:
+          Number of currently existing mappings.
+
+        <what> == DI_NUM_MAPPINGS_CLEAN:
+          Number of clean mappings (mappings without a hash part).
+
+        <what> == DI_NUM_MAPPINGS_HASH:
+          Number of hash mappings.
+
+        <what> == DI_NUM_MAPPINGS_HYBRID:
+          Number of hybrid mappings (mappings that have a
+          condensed and hash part).
+
+        <what> == DI_NUM_STRUCTS:
+          Number of currently existing structs.
+
+        <what> == DI_NUM_STRUCT_TYPES:
+          Number of currently existing struct types.
+
+        <what> == DI_NUM_VIRTUAL_STRINGS:
+          Number of currently existing virtual strings
+          (identical strings are counted separately).
+
+        <what> == DI_NUM_STRINGS:
+          Number of real strings (identical strings
+          are counted once).
+
+        <what> == DI_NUM_STRINGS_TABLED:
+          Number of directly tabled strings.
+
+        <what> == DI_NUM_STRINGS_UNTABLED:
+          Number of untabled strings.
+
+        <what> == DI_NUM_STRING_TABLE_SLOTS:
+          Number of hash slots in the string table.
+
+        <what> == DI_NUM_STRING_TABLE_SLOTS_USED:
+          Number of hash chains in the string table.
+
+        <what> == DI_NUM_REGEX:
+          Number of cached regular expressions.
+
+        <what> == DI_NUM_REGEX_TABLE_SLOTS:
+          Number of slots in the regexp cache table.
+
+        <what> == DI_SIZE_ACTIONS:
+          Total size of allocated actions.
+
+        <what> == DI_SIZE_CALLOUTS:
+          Total size of pending call_outs.
+
+        <what> == DI_SIZE_HEARTBEATS:
+          Total size of the heart_beat list.
+
+        <what> == DI_SIZE_SHADOWS:
+          Total size of allocated shadows.
+
+        <what> == DI_SIZE_OBJECTS:
+          Total size of objects (not counting the size of the values
+          of their variables).
+
+        <what> == DI_SIZE_OBJECTS_SWAPPED:
+          Total size of swapped-out variable blocks.
+
+        <what> == DI_SIZE_OBJECT_TABLE:
+          Size occupied by the object table.
+
+        <what> == DI_SIZE_PROGS:
+          Total size of all programs.
+
+        <what> == DI_SIZE_PROGS_SWAPPED:
+          Total size of swapped-out program blocks
+
+        <what> == DI_SIZE_PROGS_UNSWAPPED:
+          Total size of unswapped program blocks
+
+        <what> == DI_SIZE_ARRAYS:
+          Total size of all arrays (not counting additional sizes
+          of array element values).
+
+        <what> == DI_SIZE_MAPPINGS:
+          Total size of all mapping (not counting additional sizes
+          of contained values).
+
+        <what> == DI_SIZE_STRUCTS:
+          Total size of all structs (not counting additional sizes
+          of contained values).
+
+        <what> == DI_SIZE_STRUCT_TYPES:
+          Total size of all struct type definitions.
+
+        <what> == DI_SIZE_STRINGS:
+          Total size of all strings.
+
+        <what> == DI_SIZE_STRINGS_TABLED:
+          Total size of all tabled strings.
+
+        <what> == DI_SIZE_STRINGS_UNTABLED:
+          Total size of all untabled strings.
+
+        <what> == DI_SIZE_STRING_TABLE:
+          Size of the string table structure itself.
+
+        <what> == DI_SIZE_STRING_OVERHEAD:
+          Size of the overhead per string (compared to a raw string).
+
+        <what> == DI_SIZE_REGEX:
+          Total size of all cached regular expressions.
+
+        <what> == DI_SIZE_BUFFER_FILE:
+          The size of the memory buffer for file operations.
+
+        <what> == DI_SIZE_BUFFER_SWAP:
+          The size of the memory buffer for the swap file.
+
+
+
+        Memory swapper statistics:
+
+        <what> == DI_NUM_SWAP_BLOCKS:
+          Number of blocks in the swap file.
+
+        <what> == DI_NUM_SWAP_BLOCKS_FREE:
+          Number of free blocks in the swap file.
+
+        <what> == DI_NUM_SWAP_BLOCKS_REUSE_LOOKUPS:
+          Number of searches for blocks to reuse in the swap file.
+
+        <what> == DI_NUM_SWAP_BLOCKS_REUSE_LOOKUP_STEPS:
+          Total number of lookup steps in searches for blocks
+          to reuse in the swap file.
+
+        <what> == DI_NUM_SWAP_BLOCKS_FREE_LOOKUPS:
+          Number of searches for blocks to free in the swap file.
+
+        <what> == DI_NUM_SWAP_BLOCKS_FREE_LOOKUP_STEPS:
+          Total number of lookup steps in searches for blocks
+          to free in the swap file.
+
+        <what> == DI_SIZE_SWAP_BLOCKS:
+          Size of the swap file.
+
+        <what> == DI_SIZE_SWAP_BLOCKS_FREE:
+          Size of free blocks in the swap file.
+
+        <what> == DI_SIZE_SWAP_BLOCKS_REUSED:
+          Total reused space in the swap file.
+
+        <what> == DI_SWAP_RECYCLE_PHASE:
+          True if the swapper is currently recycling free block.
+
+
+
+        Memory allocator statistics:
+
+        <what> == DI_MEMORY_ALLOCATOR_NAME:
+          The name of the allocator: "sysmalloc", "smalloc", "slaballoc
+
+        <what> == DI_NUM_SYS_ALLOCATED_BLOCKS:
+          Number of memory blocks requested from the operating system.
+
+        <what> == DI_NUM_LARGE_BLOCKS_ALLOCATED:
+          Number of large allocated blocks.
+          (With smalloc: The large allocated blocks include
+          the small chunk blocks.)
+
+        <what> == DI_NUM_LARGE_BLOCKS_FREE:
+          Number of large free blocks.
+
+        <what> == DI_NUM_LARGE_BLOCKS_WASTE:
+          Number of unusable large memory fragments.
+
+        <what> == DI_NUM_SMALL_BLOCKS_ALLOCATED:
+          Number of small allocated blocks.
+
+        <what> == DI_NUM_SMALL_BLOCKS_FREE:
+          Number of small free blocks.
+
+        <what> == DI_NUM_SMALL_BLOCKS_WASTE:
+          Number of unusably small memory fragments.
+
+        <what> == DI_NUM_SMALL_BLOCK_CHUNKS:
+          Number of small chunk/slab blocks.
+          (That are large blocks that are used as a
+          base for small blocks.)
+
+        <what> == DI_NUM_UNMANAGED_BLOCKS:
+          Number of unmanaged (non-GC-able) allocations.
+
+        <what> == DI_NUM_FREE_BLOCKS_AVL_NODES:
+          Number of AVL nodes used to manage the large free
+          blocks. This value might go away again.
+
+        <what> == DI_SIZE_SYS_ALLOCATED_BLOCKS:
+          Total size of memory requested from the operating system.
+
+        <what> == DI_SIZE_LARGE_BLOCKS_ALLOCATED:
+          Total size of large allocated blocks.
+
+        <what> == DI_SIZE_LARGE_BLOCKS_FREE:
+          Total size of large free blocks.
+
+        <what> == DI_SIZE_LARGE_BLOCKS_WASTE:
+          Total size of unusable large memory fragments.
+
+        <what> == DI_SIZE_LARGE_BLOCK_OVERHEAD:
+          The overhead of every large block allocation.
+
+        <what> == DI_SIZE_SMALL_BLOCKS_ALLOCATED:
+          Total size of small allocated blocks.
+
+        <what> == DI_SIZE_SMALL_BLOCKS_FREE:
+          Total size of small free blocks.
+
+        <what> == DI_SIZE_SMALL_BLOCKS_WASTE:
+          Total size of unusably small memory fragments.
+
+        <what> == DI_SIZE_SMALL_BLOCK_OVERHEAD:
+          The overhead of every small block allocation.
+
+        <what> == DI_SIZE_SMALL_BLOCK_CHUNKS:
+          Total size of small chunk/slab blocks.
+
+        <what> == DI_SIZE_UNMANAGED_BLOCKS:
+          Total size of unmanaged (non-GC-able) allocations.
+
+        <what> == DI_SIZE_MEMORY_USED:
+          The amount of memory currently allocated from the allocator.
+
+        <what> == DI_SIZE_MEMORY_UNUSED:
+          The amount of memory allocated from the system, but
+          not used by the driver.
+
+        <what> == DI_SIZE_MEMORY_OVERHEAD:
+          Amount of memory used for the management of the memory.
+
+        <what> == DI_NUM_INCREMENT_SIZE_CALLS:
+          Number of requests to increase the size of a memory block.
+
+        <what> == DI_NUM_INCREMENT_SIZE_CALL_SUCCESSES:
+          Number of successful requests to increase the
+          size of a memory block.
+
+        <what> == DI_SIZE_INCREMENT_SIZE_CALL_DIFFS:
+          Total size of additionally allocated memory by
+          increasing already allocated memory blocks.
+
+        <what> == DI_NUM_REPLACEMENT_MALLOC_CALLS:
+          Number of allocations done through the
+          clib functions (if supported by the allocator).
+
+        <what> == DI_SIZE_REPLACEMENT_MALLOC_CALLS:
+          Total size of allocations done through the
+          clib functions (if supported by the allocator).
+
+        <what> == DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_FULL:
+          Total number of requests to defragment all small memory chunks.
+
+        <what> == DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_TARGETED:
+          Total number of requests to defragment small memory chunks
+          for a desired size.
+
+        <what> == DI_NUM_MEMORY_DEFRAGMENTATION_CALL_TARGET_HITS:
+          Total number of successful requests to defragment small
+          memory chunks for a desired size.
+
+        <what> == DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_INSPECTED:
+          Number of blocks inspected during defragmentations.
+
+        <what> == DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_MERGED:
+          Number of blocks merged during defragmentations.
+
+        <what> == DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_RESULTING:
+          Number of defragmented blocks (ie. merge results).
+
+        <what> == DI_MEMORY_EXTENDED_STATISTICS:
+          If the driver was compiled with extended memory statistics,
+          they are returned in this entry; if the driver was compiled
+          without the statistics, 0 is returned.
+
+          The array contains NUM+2 entries, where NUM is the number
+          of distinct small block sizes. Entry [NUM] describes the
+          statistics of oversized small blocks (smalloc) resp. for
+          all slabs (slaballoc), entry [NUM+1] summarizes all large
+          blocks. Each entry is an array of these fields:
+
+               int DIM_ES_MAX_ALLOC:
+                       Max number of allocated blocks of this size.
+
+               int DIM_ES_CUR_ALLOC:
+                       Current number of allocated blocks of this size.
+
+               int DIM_ES_MAX_FREE:
+                       Max number of allocated blocks of this size.
+
+               int DIM_ES_CUR_FREE:
+                       Current number of allocated blocks of this size.
+
+               float DIM_ES_AVG_XALLOC:
+                       Number of explicit allocation requests per
+                       second.
+
+               float DIM_ES_AVG_XFREE:
+                       Number of explicit deallocation requests per
+                       second.
+
+               int DIM_ES_FULL_SLABS:
+                       Number of fully used slabs (slaballoc only).
+
+               int DIM_ES_FREE_SLABS:
+                       Number of fully free slabs (slaballoc only).
+
+               int DIM_ES_TOTAL_SLABS:
+                       Total number of slabs: partially used, fully used
+                       and fully free (slaballoc only).
+
+           The allocation/deallocation-per-second statistics do
+           not cover internal shuffling of the freelists.
+
+           The slab statistics (entry [NUM], slaballoc only) shows
+           in the AVG statistics the frequence with which slabs were
+           allocated from resp. returned to the large memory pool.
+
+
+
+        Status texts:
+
+        <what> == DI_STATUS_TEXT_MEMORY:
+          A printable string containing information about
+          the memory usage.
+
+        <what> == DI_STATUS_TEXT_TABLES:
+          A printable string containing information about
+          the LPC runtime.
+
+        <what> == DI_STATUS_TEXT_SWAP:
+          A printable string containing information about
+          the swap system.
+
+        <what> == DI_STATUS_TEXT_MALLOC:
+          A printable string containing information about
+          memory allocations.
+
+        <what> == DI_STATUS_TEXT_MALLOC_EXTENDED:
+          A printable strings with extended memory statistics
+          (if the driver was compiled with them).
+
+
+HISTORY
+        Introduced in LDMud 3.5.0.
+
+SEE ALSO
+        configure_driver(E), object_info(E), interactive_info(E),
+        dump_driver_info(E)
diff --git a/doc/efun/dump_driver_info b/doc/efun/dump_driver_info
new file mode 100644
index 0000000..111f8ad
--- /dev/null
+++ b/doc/efun/dump_driver_info
@@ -0,0 +1,75 @@
+SYNOPSIS
+        #include <driver_info.h>
+
+        int dump_driver_info(int what)
+        int dump_driver_info(int what, string filename)
+
+DESCRIPTION
+        Dumps information specificied by <what> into a file
+        specified by <filename>. If <filename> is omitted,
+        a default file name is used. The function calls
+        master->valid_write() to check that it can write
+        the files. The file in question is always written anew
+
+        On success the efun returns 1, or 0 if an error occured.
+
+        <what> == DDI_OBJECTS:
+          Dumps information about all live objects.
+          Default filename is '/OBJ_DUMP',
+          valid_write() will read 'objdump' for the function.
+
+          For every object, a line is written into the file with the
+          following information in the given order:
+            - object name
+            - size in memory, shared data counted only once
+            - size in memory if data wouldn't be shared
+            - number of references
+            - 'HB' if the object has a heartbeat, nothing if not.
+            - the name of the environment, or '--' if the object
+              has no environment
+            - in parentheses the number of execution ticks spent
+              in this object
+            - the swap status:
+               nothing if not swapped,
+               'PROG SWAPPED' if only the program is swapped
+               'VAR SWAPPED' if only the variabes are swapped
+               'SWAPPED' if both program and variables are swapped
+            - the time the object was created
+
+        <what> == DDI_OBJECTS_DESTRUCTED:
+          Dumps information about all destructed objects. 
+          Default filename is '/DEST_OBJ_DUMP',
+          valid_write() will read 'objdump' for the function.
+
+          For every object, a line is written into the file with the
+          following information in the given order:
+            - object name
+            - number of references
+            - 'NEW' if the object was destructed in this execution
+              thread, nothing if it is older already.
+
+        <what> == DDI_OPCODES:
+          Dumps usage information about the opcodes.
+          Default filename is '/OPC_DUMP',
+          valid_write() will read 'opcdump' for the function.
+
+        <what> == DDI_MEMORY:
+          Dumps a list of all allocated memory blocks (if the allocator
+          supports this).
+          Default filename is '/MEMORY_DUMP',
+          valid_write() will read 'memdump' for the function,
+          and the new data will be appended to the end of the file.
+
+          If the allocator doesn't support memory dumps, this call will
+          always return 0, and nothing will be written.
+
+          This works best if the allocator is compiled with
+          MALLOC_TRACE and/or MALLOC_LPC_TRACE.
+
+          NOTE: Make sure that this option can't be abused!
+
+HISTORY
+        Introduced in LDMud 3.5.0.
+
+SEE ALSO
+        driver_info(E), object_info(E), interactive_info(E)
diff --git a/doc/efun/ed b/doc/efun/ed
new file mode 100644
index 0000000..8a8dc33
--- /dev/null
+++ b/doc/efun/ed
@@ -0,0 +1,19 @@
+SYNOPSIS
+        void ed()
+        void ed(string file)
+        void ed(string file, string func)
+
+DESCRIPTION
+        Calling without arguments will start the editor ed with the
+        name of the error file, that was returned by
+        master->valid_read(0, geteuid(this_player()), "ed_start",
+        this_player()), usually something like ~/.err. If that file is
+        empty, ed will immediatly exit again.
+        Calling ed() with argument file will start the editor on the
+        file. If the optional argument func is given, this function
+        will be called after exiting the editor.
+
+        The editor ed is almost ed(1) compatible.
+
+SEE ALSO
+        enable_commands(E), query_editing(E), ed0(LPC)
diff --git a/doc/efun/efun b/doc/efun/efun
new file mode 100644
index 0000000..88cd3f5
--- /dev/null
+++ b/doc/efun/efun
@@ -0,0 +1,45 @@
+NAME
+        efun
+
+BESCHREIBUNG
+        Dieses Directory enthaelt die Beschreibungen fuer die Efuns in
+        LDMud 3.3 .
+
+        Diese Funktionen werden vom Driver zur Verfuegung gestellt und koennen
+        von jedem LPC-Objekt verwendet werden.
+
+        Spezielle Arten dieser Efuns sind:
+
+         - 'Optional'
+
+           Es steht dem Mud-Betreiber frei, diese Efuns zu deaktivieren.
+
+             assoc()
+             break_point()
+             db_*()
+             insert_alist()
+             intersect_alist()
+             parse_command()
+             process_string()
+             rusage()
+             set_is_wizard()
+             set_light()
+             transfer()
+
+         - 'Vorlaeufig'
+
+           Das Verhalten dieser Efuns kann sich in spaeteren Driverversionene
+           aendern.
+
+
+         - 'Obsolet' or 'Veraltet'
+
+             make_shared_string()
+
+           Diese Efuns sollten nicht weiter verwendet werden. Sie stehen
+           lediglich aus Kompatibilitaetsgruenden zur Verfuegung, und
+           werden in einer spaeteren Drivervariante entfernt werden.
+
+
+SIEHE AUCH
+        efuns(LPC), applied(A), master(M), lpc(LPC), concepts(C), driver(D)
diff --git a/doc/efun/efun__m_delete b/doc/efun/efun__m_delete
new file mode 100644
index 0000000..32b9e02
--- /dev/null
+++ b/doc/efun/efun__m_delete
@@ -0,0 +1,24 @@
+SYNOPSIS:
+	mapping m_delete(mapping map, mixed key)
+
+DESCRIPTION:
+	Remove the entry with index 'key' from mapping 'map', and
+	return the changed mapping. If the mapping does not have an
+	entry with index 'key', the first argument is returned.
+
+        If you don't want to modify the mapping map, checkout the
+        simul_efun m_delete().
+
+EXAMPLE: 
+
+        mapping m1, m2;
+
+        m1 = ([ "a":1, "b":2, "c":3 ]);
+
+        m2 = efun::m_delete(m1, "b");
+           => m1 = ([ "a":1, "c":3 ])
+	      m2 = ([ "a":1, "c":3 ])
+
+SEE ALSO:
+	mappingp(E), mkmapping(E), m_indices(E), m_values(E),
+	sizeof(E)
diff --git a/doc/efun/environment b/doc/efun/environment
new file mode 100644
index 0000000..ea10b3d
--- /dev/null
+++ b/doc/efun/environment
@@ -0,0 +1,69 @@
+SYNOPSIS
+        object environment();
+        object environment(object obj);
+        object environment(string obj);
+
+ARGUMENTE:
+     obj
+          Objekt oder Pfad-String eines Objektes dessen Umgebungsobjekt
+          gesucht ist
+
+BESCHREIBUNG:
+     Es wird das Objekt bestimmt, in dem sich obj aufhaelt.
+     Wird obj nicht angegeben, so wird this_object() als Objekt angenommen.
+
+     Zerstoerte Objekte haben kein Environment.
+
+BEMERKUNGEN:
+     - Blueprints, wie zum Beispiel Raeume haben oft kein environment(). Man
+       sollte daher ueberpruefen, ob ein environment() existiert, wenn man
+       darin oder darauf eine Funktion aufrufen will.
+
+BEISPIELE:
+     // In der Variable "raum" steht der Raum, in dem sich der Spieler derzeit
+     // aufhaelt - das kann auch 0 sein!
+     raum = environment(this_player());
+
+     // Dieses Objekt hat noch kein environment, da es eben erst geclont
+     // wurde. Ergo steht in env eine 0.
+     obj = clone_object("/std/thing");
+     env = environment(obj);
+
+     // alle Methoden die auf Environment arbeiten, sollten das vorher
+     // pruefen - insbesondere tell_room()
+     if(this_player() && environment(this_player()) &&
+        objectp(o=present("schild",environment(this_player()))) {
+
+      write("Du klebst Deine Plakette auf "+o->name(WEN)+".\n");
+      tell_room(environment(this_player()), break_string(
+		this_player()->Name(WER)+" pappt einen Aufkleber auf "+
+		o->name(WEN)+".",78),
+		({this_player()}));
+     }
+
+     // wenn Dinge sehr offensichtlich in Leuten kaputtgehen wird es
+     // komplexer (man kann das natuerlich noch weiter schachteln oder
+     // ueber all_environment() versuchen zu loesen
+     if(environment()) {
+      object ee;
+      ee=environment(environment());
+      if(living(environment())) {
+       tell_object(environment(),Name(WER)+" zerfaellt.\n");
+       if(ee)
+        tell_room(ee,
+	 environment()->Name(WESSEN)+" "+name(RAW)+" zerfaellt.\n",
+	 ({environment()}));
+      } else if(ee && living(ee))
+       if(environment()->QueryProp(P_TRANSPARENT))
+        tell_object(ee, Name(WER)+" zerfaellt in Deine"+
+	 (environment()->QueryProp(P_PLURAL)?"n":
+	  (environment()->QueryProp(P_GENDER)==FEMALE?"r":"m"))+
+	 environment()->name(RAW)+".\n");
+      } else tell_room(environment(),Name(WER)+" zerfaellt.\n");
+     }
+
+SIEHE AUCH:
+        first_inventory(E), next_inventory(E), all_inventory(E)
+        all_environment(E)
+
+20. Sep. 2002 Gloinson@MG
\ No newline at end of file
diff --git a/doc/efun/exec b/doc/efun/exec
new file mode 100644
index 0000000..c86d3e0
--- /dev/null
+++ b/doc/efun/exec
@@ -0,0 +1,39 @@
+SYNOPSIS
+        int exec(object new, object old)
+
+DESCRIPTION
+        exec() switches the connection from the interactive object old
+        to the object new. If the new object is also interactive, it's
+        connection will be transferred to the old object, thus
+        exchaning the two connections between the object. If the new
+        object is not interactive, the old will not be interactive
+        anymore after the exec call succeeded.
+
+        The result is 1 on success, and 0 on failure.
+
+        exec() is used to load different "user objects" or to reconnect
+        link dead users.
+
+        To provide security mechanisms, the interpreter calls
+        master->valid_exec(current_program, new, old), which must
+        return anything other than 0 to allow this exec() invocation.
+
+        After the exec(), if arg 2 was this_player(), this_player()
+        becomes arg 1, else vice versa. Ditto for this_interactive().
+
+        Take care when writing a simul-efun around exec(): the
+        'current_program' passed to the valid_exec() function will be
+        that of the simul-efun object. To get around this, use
+        bind_lambda() to bind #'exec to the real object and funcall()
+        the resulting closure.
+
+EXAMPLE
+        ob = clone_object("std/player");
+        exec(ob, this_object());
+        destruct(this_object());
+
+HISTORY
+        LDMud 3.2.9 added the switchover of this_interactive().
+
+SEE ALSO
+        connect(M), disconnect(M), logon(A), interactive(E)
diff --git a/doc/efun/execute_command b/doc/efun/execute_command
new file mode 100644
index 0000000..0784c7e
--- /dev/null
+++ b/doc/efun/execute_command
@@ -0,0 +1,21 @@
+VORLAEUFIG, GESCHUETZT
+SYNOPSIS
+        int execute_command(string command, object origin, object player);
+
+BESCHREIBUNG
+        Diese Funktion bietet Low-Level Zugriff auf den Kommandoparser:
+        <command> wird in Verb und Rest aufgespaltet und es werden die
+        entsprechenden add_actions in <origin> aufgerufen. Fuer die
+        Ausfuehrung der Funktion(en) wird this_player() auf <player> gesetzt.
+        execute_command() setzt auch passende Werte fuer query_command()
+        und query_verb(), die dem angegebenen <command> entsprechen.
+
+        execute_command() beruecksichtigt weder den H_MODIFY_COMMAND noch den
+        H_NOTIFY_FAIL Hook. Zwar kann notify_fail() verwendet werden, die
+        Verarbeitung muss jedoch durch das aufrufende Objekt erfolgen.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.7
+
+SIEHE AUCH
+        hooks(C), command(E), notify_fail(E), command_stack(E)
diff --git a/doc/efun/exp b/doc/efun/exp
new file mode 100644
index 0000000..c5746dc
--- /dev/null
+++ b/doc/efun/exp
@@ -0,0 +1,8 @@
+SYNOPSIS
+        float exp(int|float zahl);
+
+BESCHREIBUNG
+        Die Exponentialfunktion. Liefert e^zahl.
+
+SIEHE AUCH
+        log(E), pow(E)
diff --git a/doc/efun/expand_define b/doc/efun/expand_define
new file mode 100644
index 0000000..c6ff189
--- /dev/null
+++ b/doc/efun/expand_define
@@ -0,0 +1,24 @@
+SYNOPSIS
+        string expand_define (string name)
+        string expand_define (string name, string arg, ...)
+
+DESCRIPTION
+        Expands the macro <name> with the argument(s) <arg>... (default is
+        one empty string "").
+        Result is the expanded macro, or 0 if there is no macro with
+        that name.
+
+        This efun is applicable only while an object is compiled,
+        therefore its usage is restricted to a few functions like the
+        H_INCLUDE_DIRS driver hook, or the masters runtime_error()
+        function.
+
+EXAMPLE
+        While compiling 'foo.c':
+          expand_define("__FILE__") --> "foo.c"
+
+HISTORY
+        Introduced in 3.2.1@93.
+
+SEE ALSO
+        hooks(C), runtime_error(M)
diff --git a/doc/efun/explode b/doc/efun/explode
new file mode 100644
index 0000000..096eaa1
--- /dev/null
+++ b/doc/efun/explode
@@ -0,0 +1,28 @@
+SYNOPSIS
+        string *explode(string str, string del)
+
+BESCHREIBUNG
+        Liefert ein Feld (Array) mit Zeichenketten (Strings), indem alle
+        Vorkommen von del (delimiter = Trenner) aus str herausgeschnitten
+        werden und so str in mehrere Zeichenketten zerlegt wird.
+
+        implode(explode(str, del), del) == str ist immer wahr.
+
+BEISPIELE
+        Funktion                    Rueckgabewert
+        -------------------------------------------------------------------
+        explode(" ab cd ef ", " ")  ({ "", "ab", "cd", "ef", "" })
+        explode("abc", "abc")       ({ "", "" })
+        explode("", "")             ({})
+        explode("abc", "xyz")       ({ "abc" })
+        explode("abc", "")          ({ "a", "b", "c" })
+
+AENDERUNGEN
+        Zeitpunkt der Aenderung unbekannt.
+        explode(" ab cd ef ", " ") lieferte frueher ({ "ab", "cd", "ef" })
+        anstatt ({ "", "ab", "cd", "ef", "" }), d. h., leere Zeichenketten
+        wurden ignoriert. Das neue Verhalten ist schoener, da nun
+        implode(explode(str, del), del) == str immer wahr ist.
+
+SIEHE AUCH
+        sscanf(E), implode(E), regexplode(E)
diff --git a/doc/efun/export_uid b/doc/efun/export_uid
new file mode 100644
index 0000000..5b1f6a3
--- /dev/null
+++ b/doc/efun/export_uid
@@ -0,0 +1,15 @@
+SYNOPSIS
+        void export_uid(object ob)
+
+DESCRIPTION
+        Set the uid of object ob to the current object's effective uid.
+        It is only possible when object ob has an effective uid of 0.
+
+        This efun is not available in Morgengrauen.
+
+HISTORY
+        Since 3.2.1@47, this efun is availabe only when using euids.
+        Since 3.2.7, this efun is always available.
+
+SEE ALSO
+        seteuid(E), getuid(E), geteuid(E), uids(C), native(C)
diff --git a/doc/efun/extern_call b/doc/efun/extern_call
new file mode 100644
index 0000000..03dca5f
--- /dev/null
+++ b/doc/efun/extern_call
@@ -0,0 +1,12 @@
+SYNOPSIS
+        int extern_call();
+
+BESCHREIBUNG
+        Liefert 0, wenn die momentane ausgefuehrte Funktion durch eine lokale
+        Funktion aufgerufen wurde, einen Wert ungleich 0 fuer Aufrufe durch
+        call_other(), den Treiber, Closures etc. Im Moment liefert die
+        Funktion in all diesen Faellen 1 zurueck, in Zukunft koennte
+        allerdings eine Unterscheidung der Aufrufe moeglich werden.
+
+SIEHE AUCH
+        call_other(E), previous_object(E)
\ No newline at end of file
diff --git a/doc/efun/file_size b/doc/efun/file_size
new file mode 100644
index 0000000..d49dd53
--- /dev/null
+++ b/doc/efun/file_size
@@ -0,0 +1,35 @@
+FUNKTION:
+        int file_size(string file)
+
+BESCHREIBUNG:
+        Liefert die Groesse des Files in Bytes.
+
+RUECKGABEWERT:
+        Liefert die Dateigroesse in Bytes.
+
+        Davon abweichend kann auch folgendes als Rueckgabewert vor-
+        kommen:
+
+        FSIZE_NOFILE    Das File ist entweder nicht vorhanden oder das
+         (-1)           abfragende Objekt besitzt keine Leserechte dafuer.
+        FSIZE_DIR       Es handelt sich nicht um ein File sondern um
+         (-2)           ein Verzeichnis.
+
+BEISPIELE:
+        Ein Spieler soll seinen Plan abfragen koennen:
+
+        #include <sys/files.h>
+        if(file_size("/p/service/loco/plans/"+
+                     getuid(this_player())+".plan") <= FSIZE_NOFILE)
+        {
+          write("Du hast keinen eigenen Plan festgelegt.\n");
+          return 1;
+        }
+ 
+        this_player()->More(read_file("/p/service/loco/plans/"+
+                            getuid(this_player())+".plan");
+         
+SIEHE AUCH:
+        file_time(S), write_file(E), cat(E), get_dir(E), ls()
+
+03.08.2007, Zesstra
diff --git a/doc/efun/filter b/doc/efun/filter
new file mode 100644
index 0000000..61bc1e8
--- /dev/null
+++ b/doc/efun/filter
@@ -0,0 +1,189 @@
+filter(E)
+
+FUNKTION:
+     mixed * filter (mixed *arg, string fun, string|object ob
+                                              , mixed extra...)
+     mixed * filter (mixed *arg, closure cl, mixed extra...)
+     mixed * filter (mixed *arg, mapping map, mixed extra...)
+
+     string  filter (string arg, string fun, string|object ob
+                                        , mixed extra...)
+     string  filter (string arg, closure cl, mixed extra...)
+     string  filter (string arg, mapping map, mixed extra...)
+
+     mapping filter (mapping arg, string func, string|object ob
+                                             , mixed extra...)
+     mapping filter (mapping arg, closure cl, mixed extra...)
+
+PARAMETER:
+     arr     - zu filterndes Array/Mapping/String
+     fun/cl  - zu rufende Methode/Closure
+     map     - filterndes Mapping
+     ob      - Objekt/Dateiname, an dem Methode gerufen werden soll
+     extra   - weitere Parameter fuer Methode/Closure
+
+BESCHREIBUNG:        
+     Ruft fuer jedes Element des Arrays oder Mappings <arg> die Funktion
+     <ob>-><func>() bzw. die Closure <cl> auf und liefert jene Elemente,
+     fuer die die Funktion / Closure TRUE ergeben hat. Die <extra>
+     Argumente werden als zusaetzliche Parameter an die Funktion
+     uebergeben und duerfen keine Referenzen von Array- oder Mapping-
+     Elementen sein (wie &(i[1]) ).
+
+     Wird <ob> nicht angegeben oder ist es weder ein String noch ein
+     Objekt, wird standardmaessig this_object() verwendet.
+
+     Ist <arg> ein Array, wird <fun> mit jedem Element des Arrays als
+     ersten Parameter aufgerufen, gefolgt von den <extra> Argumenten.
+     Wenn das Resultat der Funktion TRUE ergibt, wird das Element in das
+     Ergebnis der filter() Operation mit einbezogen.
+
+     Wird filter() mit einem Mapping <map> anstelle der Funktion <func>
+     aufgerufen, wird jedes Element im Array <arg>, das ein Key von <map>
+     ist, ins Ergebnis mit einbezogen.
+
+     Wenn <arg> ein Mapping ist, wird die Funktion <func> mit jedem Key
+     als erstem und (falls vorhanden) den Werten dieses Keys als restliche
+     Parameter, gefolgt von den <extra> Argumenten, aufgerufen. Wenn die
+     Funktion TRUE ergibt, wird das betreffende Element des Mappings ins
+     Ergebnis aufgenommen.
+
+     Abhaengig von der Groesse des Mappings <arg> erfolgt der Aufruf der
+     Funktion auf drei unterschiedliche Arten:
+
+     widthof(arg) == 0:  ob->func(key, 0, extra,...);
+     widthof(arg) == 1:  ob->func(key, arg[key], extra, ...);
+     widthof(arg) >1:    ob->fund(key, ({arg[key,0]...arg[key, n-1]}),
+                                  extra, ...);
+
+     Der Vorteil dieser Vorgehensweise ist, dass beide Typen von
+     multidimensionalen Mappings (Mappings mit mehreren Werte pro Key und
+     Mappings aus Arrays) gleich verarbeitet werden koennen.
+
+     Ist <arg> ein String, werden der Filterfunktion die einzelnen Zeichen des
+     Strings uebergeben und nur jene Zeichen im zurueckgegebenen String
+     aufgenommen, fuer die die Filterfunkton != 0 zurueckgibt.
+
+AENDERUNGEN
+     Eingefuehrt in LDMud 3.2.6. Die Funktion loest filter_array() ab.
+
+RUeCKGABEWERT:
+     Gefiltertes Array mit allen die Filterbedingung erfuellenden Elementen.
+
+BEMERKUNGEN:
+     (1) Achtung, die Elemente in 'arr' werden nicht tief kopiert, sind sie
+     also selbst Arrays oder Mappings, so fuehrt eine spaetere Aenderung im
+     Rueckgabe-Arrays zur Aenderung im Ursprungsarray:
+
+     int *i, *j;
+     i=({({1,2,3}),({4,5,6})});
+     j=filter(i, #'sizeof);     // filtert leere Arrays heraus
+     j[0][0]=8;
+
+     fuehrt zu: i==j==({({8,2,3}),({4,5,6})});
+
+     (2) Das Kopieren in das Rueckgabemapping erfolgt fuer jedes Element nach
+     Ausfuehrung der Filtermethode. Aenderungen der Werte im Array in dieser
+     Methode (globale Variable/Uebergabe als Referenz an filter)
+     schlagen sich also im Rueckgabearray nieder.
+
+     (3) Fuer Arrays wirkt filter() wie filter_array(), fuer Mappings stellt
+     filter() eine Verallgemeinerung von filter_indices() dar.
+
+BEISPIEL:
+     ### Filtere alle Lebewesen in einem Raum in ein Array ###
+     filter(all_inventory(this_object()),#'living);
+
+
+     ### Filtere alle tauben Spieler im Raum in ein Array ###
+     static int filter_isdeaf(object who) {
+       return (interactive(who) && who->QueryProp(P_DEAF));
+     }
+     
+     filter(all_inventory(this_object()), #'filter_isdeaf);
+
+
+     ### Filtern von Idlern (>=1 Sekunde idle) ###
+     // Folgend identische Resultate, aber andere Ansaetze:
+
+     #1: nutzt die Efun query_idle() als Lfun-Closure (ideal hier)
+         idle_usr = filter(users(), #'query_idle );
+
+     #2: mit Filtermethode
+         int check_if_idle(object user) {
+           return query_idle(user);
+         }
+         
+         #2a: filtert mittels der Lfun im selben Objekt die Idler in das
+              Rueckgabearray
+              idle_usr = filter(users(), "check_if_idle");
+              idle_usr = filter(users(), "check_if_idle", this_object());
+
+         #2b: ruft die Lfun check_if_idle() als Lfun-Closure (Funktions-
+              pointer)
+              idle_usr = filter(users(), #'check_if_idle );
+
+     #3: nutzt eine Lambda-Closure (langsamer, nicht fuer alle leserlich)
+         idle_usr = filter(users(), lambda(({'u}), ({#'query_idle, 'u})));
+
+     #4: erreicht dasselbe wie #3 mit besser lesbarer Inline-Closure
+         idle_usr = filter(users(), function int (object user) {
+                      return query_idle(user);
+                    } );
+
+     ### Filtern von Idlern (>=20 Sekunden idle) mit Extraparameter ###
+     // Folgend identische Resultate, aber andere Ansaetze:
+
+     #1: die Efun koennen wir nicht mehr direkt nutzen, weil sie
+         diesen Parameter nicht unterstuetzt
+        // idle_usr = filter(users(), #'query_idle );
+
+     #2: mit separater Filtermethode ... mit neuem Parameter
+         int check_if_idle(object user, int length) {
+           return query_idle(user)>length;
+         }
+      
+         #2a: filtert mittels der Lfun im selben Objekt die Idler in das
+              Rueckgabearray ... mit drittem Parameter!
+              idle_usr = filter(users(), "check_if_idle", this_object(), 20);
+
+         #2b: ruft die Lfun check_if_idle() als Lfun-Closure (Funktions-
+              pointer)
+              idle_usr = filter(users(), #'check_if_idle, 20);
+
+     #3: nutzt eine Lambda-Closure (langsamer, nicht fuer alle leserlich)
+         idle_usr = filter(users(), 
+                      lambda(({'u, 'l}), ({#'>,({#'query_idle, 'u}),'l})), 
+                      20);
+
+     #4: erreicht dasselbe wie #3 mit einer deutlich besser lesbaren
+         Inline-Closure (gegenueber Lambdas zu empfehlende Variante):
+         idle_usr = filter(users(), function int (object user, int length) {
+                      return (query_idle(user) > length); 
+                      }, 20);
+
+
+AeQUIVALENZCODE (nicht empfohlen, nur zum Verstaendnis!):
+     int i;
+     mixed *ret; mixed *input;
+
+     ret=allocate(0);
+     i=sizeof(input);
+     while(i--)
+       if(ob->fun(input[i] [, extra1, extra2, ...]))
+       // if(funcall(cl, input[i] [, extra1, extra2, ...]))
+       // if(member(map, input[i]))
+         ret+=({input[i]});
+
+SIEHE AUCH:
+     Arrays:        map(E)
+     Objektarrays:  filter_objects(E), map_objects(E)
+     Mappings:      filter_indices(E), map_indices(E)
+                    walk_mapping(E), member(E)
+
+     Sonstiges:     sort_array(E), unique_array()
+                    transpose_array(E)
+
+----------------------------------------------------------------------------
+09.04.2008, Zesstra
+
diff --git a/doc/efun/filter.eng b/doc/efun/filter.eng
new file mode 100644
index 0000000..5d0be29
--- /dev/null
+++ b/doc/efun/filter.eng
@@ -0,0 +1,49 @@
+filter(E)
+SYNOPSIS
+     mixed *filter(mixed *arr, string fun, string|object ob
+                         [, mixed extra, ...])
+     mixed *filter(mixed *arr, closure cl [, mixed extra, ...])
+     mixed *filter(mixed *arr, mapping map [, mixed extra, ...])
+
+DESCRIPTION
+        Returns an array holding the items of arr filtered through
+        ob->fun(element, extra, ...), the closure cl, or the mapping map.
+        The function 'fun' in 'ob' resp. the closure 'cl' is called
+        for each element in arr with that element as parameter. The
+        extra and following parameters are in each call if given.
+        The mapping 'map' is likewise indexed by each element.
+        If ob->fun(arr[index], extra) returns != 0 resp.
+        map[arr[index]] exists, the element is included in the
+        returned array.
+
+        If arr is not an array, an error occurs.
+
+        The extra argument(s) are optional and must not be protected
+        references like &(i[0]).
+        If <ob> is omitted, or neither a string nor an object, it
+        defaults to this_object().
+
+        Since 3.2.1@36, the second arg can also be a mapping. Then
+        only the elements of the array which belong to the map (as
+        keys) will be returned (i.e. map[arr[index]] != 0).
+
+EXAMPLE
+        int check_if_idle(object user) { return query_idle(user); }
+
+        ...
+
+        object *idle_users;
+
+        idle_users = filter(users(), "check_if_idle");
+        /* equivalent but smaller and faster */
+        idle_users = filter(users(), #'query_idle );
+
+        Now idle_users contains all users that have been idle for more
+        than 1 second.
+
+SEE ALSO:
+        Similar filter:	filter(E), filter_object(E), filter_indices(E)
+        Mapping:	map(E), map_objects(E), map_indices(E)
+        Related:	sort_aray(E), unique_array(E)
+
+29.10.2006 Zesstra
diff --git a/doc/efun/filter_indices b/doc/efun/filter_indices
new file mode 100644
index 0000000..54fdadf
--- /dev/null
+++ b/doc/efun/filter_indices
@@ -0,0 +1,115 @@
+filter_indices(E)
+
+FUNKTION:
+     mapping filter_indices(mapping map, string fun, string|object ob
+                            [, mixed extra, ...])
+     mapping filter_indices(mapping map, closure cl [, mixed extra, ...])
+
+PARAMETER:
+     map	- zu filterndes Mapping
+     fun/cl	- zu rufende Methode/Closure
+     ob		- Objekt/Dateiname, an dem Methode gerufen werden soll
+     extra	- weitere Parameter fuer Methode/Closure
+
+BESCHREIBUNG:
+     Filtert die Elemente (jeweils Schluessel) aus 'map' durch die
+     Methode 'fun' oder die Closure 'cl' in ein neues Mapping.
+     Fuer jedes Element aus 'map' wird 'fun' oder 'cl' mit dem Schluessel als
+     erstem Parameter [und folgend den optionalen Extra-Parametern] gerufen.
+
+     Wenn der Aufruf
+      	ob->fun(element [, extra1, extra2, ...]) bzw.
+       	funcall(cl, element [, extra1, extra2, ...])
+     als Rueckgabewert !=0 zurueckgibt dann wird der Schluessel+Werte in das
+     neue Array aufgenommen, sonst nicht.
+
+     Wenn auf die Werte zugegriffen werden muss, kann das Mapping 'map'
+     zusaetzlich als 'extra'-Parameter uebergeben werden. Alternativ kann man
+     walk_mapping() benutzen und das Rueckgabemapping selbst erstellen.
+
+
+     Verwendung von Methoden:
+	Wenn bei der Angabe von 'fun' kein Objekt 'ob' in Form eines Strings
+	oder Objekts angegeben wird, wird this_object() angenommen.
+
+     Verwendung von Closures:
+	Es koennen sowohl Lfun-Closures als auch Lambda-Closures verwendet
+	werden. Lfun-Closures koennen, wenn im selben Objekt vorhanden auch
+	'private' oder/und 'static' deklariert sein, muessen aber zu dem
+	Zeitpunkt der Verwendung bekannt sein (Funktionsprototypen benutzen).
+
+RUeCKGABEWERT:
+     Gefiltertes Mapping mit Filterbedingung erfuellenden Elementen.
+
+BEMERKUNGEN:
+     (1) Achtung, die Elemente in 'map' werden nicht tief kopiert, sind sie
+     also selbst Arrays oder Mappings, so fuehrt eine spaetere Aenderung im
+     Rueckgabe-Mapping zur Aenderung im Ursprungsmapping:
+
+     mapping m,n;
+     m=([1:({1,2}),0:({2,3,4})]);
+     n=filter_indicdes(m, #'!);		// filtert alle Keys !=0 heraus
+     n[0][0]=8;
+
+     fuehrt zu: n=([0:({8,3,4})])
+  		m=([0:({8,3,4}),1:({1,2})])
+
+     (2) Das Kopieren in das Rueckgabemapping erfolgt fuer jedes Element nach
+     Ausfuehrung der Filtermethode. Aenderungen der Werte im Mapping in dieser
+     Methode (globale Variable/Uebergabe als Referenz an filter_indices)
+     schlagen sich also im Rueckgabemapping nieder.
+
+BEISPIELE:
+     ### erfundene Liste mit Spielern saeubern ... ###
+     mapping x=([ [/human:liafar]:  20,
+		  [/dwarf:mesirii]: 50,
+		  [/elf:zarniya]:   40,
+		  [/feline:turbo]:  30]);
+
+     int is_badguy(object who) {
+      if(who->InFight()) return 1;
+      return 0;
+     }
+
+     mapping result=filter_indices(x, #'is_badguy);
+     // 'result' enthaelt nur noch kaempfende Spieler
+
+     ### erfundene Liste ueber ihre Werte saeubern ###
+     int is_badguy(object who, mapping m) {
+      if(m[x]<30) return 1;
+      return 0;
+     }
+
+     mapping result=filter_indices(x, #'is_badguy, &x); // Referenz
+     // 'result' enthaelt nur Spieler mit Werten >= 30
+
+AeQUIVALENZCODE (nicht empfohlen, nur zum Verstaendnis!):
+     int i, width;
+     mapping ret; mapping input;
+     mixed *index;
+
+     width=get_type_info(input)[1];
+     ret=m_allocate(0, width);
+     index=m_indices(input);
+     i=sizeof(index);
+     while(i--)
+       if(ob->fun(index[i] [, extra1, extra2, ...]))
+       // if(funcall(cl, index[i] [, extra1, extra2, ...]))
+       {
+         int j;
+         j=width;
+
+         while(j--)
+           ret[index[i],j]=input[index[i],j];
+       }
+
+SIEHE AUCH:
+     Arrays:		filter(E), map(E)
+     Objektarrays:	filter_objects(E), map_objects(E)
+     Mappings:		map_indices(E)
+
+     Sonstiges:		walk_mapping(E), m_contains(E)
+			member()
+			m_indices(E), m_values(E)
+
+29.10.2006 Zesstra
diff --git a/doc/efun/filter_objects b/doc/efun/filter_objects
new file mode 100644
index 0000000..9fb6922
--- /dev/null
+++ b/doc/efun/filter_objects
@@ -0,0 +1,59 @@
+filter_objects(E)
+
+FUNKTION:
+     object *filter_objects(mixed *arr, string fun, [, mixed extra, ...])
+
+PARAMETER:
+     arr	- zu filterndes Array von Objekten/Objektpfaden
+     fun	- an Objekten zu rufende Methode
+     extra	- weitere Parameter fuer Methode
+
+BESCHREIBUNG:
+     Filtert die Elemente aus 'arr' durch den Aufruf der Methode 'fun' an
+     jedem der Elemente von 'arr'. 0-Eintraege werden ignoriert.
+
+     Wenn der Aufruf
+	arr[n]->fun([extra1, extra2, ...])
+     als Rueckgabewert !=0 zurueckgibt dann wird das Element in das neue
+     Array aufgenommen, sonst nicht.
+
+RUeCKGABEWERT:
+     Gefiltertes Array mit Filterbedingung erfuellenden Objekten.
+
+BEMERKUNGEN:
+     Werden Pfade angegeben, so wird versucht ein Objekt zu laden, falls
+     dieses nicht existiert.
+
+BEISPIEL:
+     // gibt alle tauben Personen wieder
+     deaf=filter_objects(users(), "QueryProp", P_DEAF);
+
+     // gibt alle blinden Personen wieder
+     blind=filter_objects(users(), "QueryProp", P_BLIND);
+
+     // filtert alle Objekte auf eine bestimmte ID (-> present)
+     idmatch=filter_objects(arr, "id", "irgendwasID");
+
+     // gibt alle Autoloader wieder
+     al=filter_objects(all_inventory(this_player()),
+                      "QueryProp", P_AUTOLOADOBJ);
+
+AeQUIVALENZCODE (nicht empfohlen, nur zum Verstaendnis!):
+     int i;
+     object *ret; mixed *input;
+
+     ret=allocate(0);
+     i=sizeof(input);
+     while(i--)
+       if(input[i]->fun([extra1, extra2, ...]))
+         ret+=({input[i]});
+
+SIEHE AUCH:
+     Arrays:		filter(E), map(E)
+     Objektarrays:	map_objects(E)
+     Mappings:		filter_indices(E), map_indices(E)
+
+     Sonstiges:		sort_array(E), unique_array()
+			alist, transpose_array(E)
+
+20.Jan 2005 Gloinson
diff --git a/doc/efun/find_call_out b/doc/efun/find_call_out
new file mode 100644
index 0000000..0e94337
--- /dev/null
+++ b/doc/efun/find_call_out
@@ -0,0 +1,41 @@
+FUNKTION:
+        int find_call_out(string func)
+        int find_call_out(closure cl)
+
+BESCHREIBUNG:
+        Findet den ersten call_out() auf die Funktion 'func' im aktuellen Objekt
+        (bzw. auf die Closure 'cl') der ausgefuehrt werden soll.
+        Zurueck gegeben wird die verbleibende Zeit bis zum Aufruf in Sekunden.
+        Wenn kein call_out() gefunden wird, wird -1 zurueck gegeben.
+
+BEISPIELE:
+        // Findet sich kein call_out auf die Funktion 'func', so kann er
+        // gestartet werden. (Wichtig falls der call_out nicht mehrfach
+        // aufgerufen werden soll).
+ 
+        if(find_call_out("func")==-1)
+          call_out("func",5);
+
+        // Alle call_out auf Funktion 'func' werden gefunden und gestoppt.
+        // (Beispielsweise weil ein Spieler den Raum verlaesst oder sich
+        // disconnectet).
+
+        while(find_call_out("func") > -1)
+          remove_call_out("func");
+
+        // Die Suche nach call_out()s auf Closures funktioniert nur, wenn der
+        // genaue Wert der Closure gesucht wird.
+
+        // Das funktioniert:
+            closure cl = symbol_function("main", obj);
+            call_out(cl, 2);
+            find_call_out(cl);
+
+        // Das funktioniert nicht:
+            call_out(symbol_function("main", obj), 2);
+            find_call_out(symbol_function("main", obj));
+
+SIEHE AUCH:
+        call_out(E), remove_call_out(E), call_out_info(E)
+
+4.Aug 2007 Gloinson
diff --git a/doc/efun/find_input_to b/doc/efun/find_input_to
new file mode 100644
index 0000000..cb299c0
--- /dev/null
+++ b/doc/efun/find_input_to
@@ -0,0 +1,26 @@
+SYNOPSIS
+        int find_input_to (object player, string fun);
+        int find_input_to (object player, closure fun);
+        int find_input_to (object player, object fun);
+        int find_input_to (object player, object ob, string fun);
+
+BESCHREIBUNG
+        Findet den zuletzt fuer <player> gestarteten input_to(), abhaengig vom
+        Argument <fun>:
+          - <fun> ist ein String: der Funktionsname des input_to() muss passen
+          - <fun> ist ein Objekt: das Objekt, an welches die Funktion des
+            input_to() gebunden ist, muss passen
+          - <fun> ist eine Closure: die input_to() Closure muss passen
+          - <ob> und <fun> sind angegeben: sowohl Objekt als auch
+            Funktionsname muessen passen.
+
+        Die Funktion liefert -1, wenn kein input_to() gefunden wurde, sonst
+        die Position im input_to() Stack (wobei 0 den als letztes
+        hinzugefuegten input_to() bezeichnet).
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9
+
+SIEHE AUCH
+        input_to(E), input_to_info(E), remove_input_to(E),
+        query_input_pending(E)
diff --git a/doc/efun/find_object b/doc/efun/find_object
new file mode 100644
index 0000000..2a4f0d0
--- /dev/null
+++ b/doc/efun/find_object
@@ -0,0 +1,23 @@
+SYNOPSIS:
+        object find_object(string str)
+
+DESCRIPTION:
+        Find an object with the object_name str. If the object isn't loaded,
+	it will not be found.
+
+EXAMPLE:
+        object obj;
+        obj = find_object("std/thing");
+        obj = find_object("std/thing.c");
+        obj = find_object("/std/thing");
+        obj = find_object("/std/thing.c");
+        
+        All four statements are equal.
+
+	obj = find_object("/std/thing#42");
+
+	returns the clone whose object_name is "std/thing#42", if
+	it exists.
+
+SEE ALSO:
+        find_living(E), find_player(E), object_name(E)
diff --git a/doc/efun/first_inventory b/doc/efun/first_inventory
new file mode 100644
index 0000000..99be7fd
--- /dev/null
+++ b/doc/efun/first_inventory
@@ -0,0 +1,34 @@
+SYNOPSIS
+        object first_inventory();
+        object first_inventory(string ob);
+        object first_inventory(object ob);
+
+BESCHREIBUNG
+        Liefert das erste Objekt im Inventory von <obj>, wobei <obj> entweder
+        ein Objekt oder der Name eines Objekts ist. Wenn <obj> nicht angegeben
+        wird, wird standardmaessig this_object() verwendet.
+
+BEISPIELE
+        Diese Efun verwendet man am haeufigsten im folgenden Kontext:
+
+            for(obj=first_inventory(container);obj;obj=next_inventory(obj))
+            {
+                <irgendwelcher Code>
+            }
+
+        ACHTUNG: Wenn das Objekt <obj> innerhalb von <irgendwelcher Code>
+        bewegt wird, liefert next_inventory() ein Objekt aus dem neuen
+        Inventory von <obj>. Auch sollte next_inventory() nicht fuer
+        zerstoerte Objekte <obj> aufgerufen werden. Fuer den Fall, dass
+        <obj> bewegt und/oder zerstoert wird, ist folgende Loesung
+        vorzuziehen:
+
+            for(obj=first_inventory(container);obj;)
+            {
+                next=next_inventory(obj);
+                <irgendwelcher Code mit Moves oder Removes>
+                obj=next;
+            }
+
+SIEHE AUCH
+        next_inventory(E), all_inventory(E), environment(E), deep_inventory(E)
diff --git a/doc/efun/floatp b/doc/efun/floatp
new file mode 100644
index 0000000..109cace
--- /dev/null
+++ b/doc/efun/floatp
@@ -0,0 +1,10 @@
+SYNOPSIS
+        int floatp(mixed arg)
+
+BESCHREIBUNG
+        Liefert 1, wenn das Argument eine Fliesskommazahl (float) ist,
+        ansonsten 0.
+
+SIEHE AUCH
+        intp(E), mappingp(E), stringp(E), closurep(E), objectp(E),
+        referencep(E), pointerp(E), symbolp(E), clonep(E)
diff --git a/doc/efun/floor b/doc/efun/floor
new file mode 100644
index 0000000..e7b85e6
--- /dev/null
+++ b/doc/efun/floor
@@ -0,0 +1,19 @@
+SYNOPSIS
+        float floor(float arg);
+
+BESCHREIBUNG
+        Rundet das Argument <arg> ab auf die naechste ganze Zahl und gibt
+        diesen Wert zurueck. Wenn <arg> ein Integer ist, wird das Resultat
+        trotzdem als Float zurueckgegeben.
+
+BEISPIELE
+        floor(4.5)  --> liefert 4.0
+        floor(-4.5) --> liefert -5.0
+        floor(5)    --> liefert 5.0
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.7.
+        LDMud 3.2.9 erlaubt auch Integer als Argumente.
+
+SIEHE AUCH
+        abs(E), ceil(E)
diff --git a/doc/efun/funcall b/doc/efun/funcall
new file mode 100644
index 0000000..6bb9833
--- /dev/null
+++ b/doc/efun/funcall
@@ -0,0 +1,10 @@
+SYNOPSIS
+        mixed funcall(closure cl, mixed arg, ...);
+
+BESCHREIBUNG
+        Wertet die Closure 'cl' aus. Die Argumente 'args' werden als Argumente
+        an die Closure uebergeben. Wenn 'cl' keine Closure ist, wird 'cl'
+        zurueck gegeben.
+
+SIEHE AUCH
+        apply(E), quote(E)
diff --git a/doc/efun/function_exists b/doc/efun/function_exists
new file mode 100644
index 0000000..43f2da2
--- /dev/null
+++ b/doc/efun/function_exists
@@ -0,0 +1,14 @@
+SYNOPSIS
+        string function_exists(string str, object ob)
+
+DESCRIPTION
+        Return the file name of the object that defines the function
+        str in object ob. The returned value can be other than
+        object_name(ob) if the function is defined in an inherited
+        object. In !compat mode, the returned name always begins with a
+        '/' (absolute path). 0 is returned if the function was not
+        defined, or was defined as static (protected function on the
+        other hand are found).
+
+SEE ALSO
+        call_other(E), call_resolved(E), functionlist(E)
diff --git a/doc/efun/functionlist b/doc/efun/functionlist
new file mode 100644
index 0000000..46f2cba
--- /dev/null
+++ b/doc/efun/functionlist
@@ -0,0 +1,55 @@
+GESCHUETZT
+SYNOPSIS
+        #include <sys/functionlist.h>
+        #include <sys/lpctypes.h>
+
+        mixed *functionlist(object ob, int flags = RETURN_FUNCTION_NAME)
+
+BESCHREIBUNG
+        Liefert ein Array mit Informationen zu den Lfuns von <ob>. Fuer jede
+        Funktion werden 1 bis 4 Werte (abhaengig von <flags>) in das Ergebnis
+        eingetragen, und zwar in folgender Reihenfolge:
+          - der Name der Funktion
+          - die Flags der Funktion (vergleiche weiter unten)
+          - den Rueckgabetyp (aufgelistet in mudlib/sys/lpctypes.h)
+          - die Anzahl Argumente, die die Funktion akzeptiert.
+
+        <ob> kann als echtes Objekt oder als Objektname uebergeben werden. Im
+        zweiten Fall versucht die Efun nicht, das Objekt vorher zu laden.
+
+        <flags> bestimmt sowohl, welche Informationen im Ergebnis gesammelt
+        werden, als auch, fuer welche Funktionen die Pruefung durchgefuehrt
+        wird. Der Wert von <flags> wird durch binaere Veroderung folgender
+        Konstanten aus mudlib/sys/functionlist.h festgelegt:
+
+        Festlegen der gesammelten Information:
+            RETURN_FUNCTION_NAME        liefert den Funktionsnamen
+            RETURN_FUNCTION_FLAGS       liefert die Flas der Funktion
+            RETURN_FUNCTION_TYPE        liefert den Rueckgabetyp der Funktion
+            RETURN_FUNCTION_NUMARG      liefert die Anzahl moeglicher
+                                        Argumente.
+
+            RETURN_FUNCTION_ARGTYPE     ist definiert, aber nicht
+                                        implementiert.
+
+        Festlegen der geprueften Funktionen:
+            NAME_INHERITED      geerbte Funktionen nicht beachten
+            TYPE_MOD_STATIC     static deklarierte Funktion nicht beachten
+            TYPE_MOD_PRIVATE    private deklarierte Funktionen nicht beachten
+            TYPE_MOD_PROTECTED  protected deklarierte Funktionen nicht
+                                beachten
+            NAME_HIDDEN         nur beachten, wenn sichtbar durch Vererbung
+
+        <flags> besteht aus der binaeren Veroderung der oben genannten Flags,
+        zusammen mit den folgenden:
+            TYPE_MOD_VARARGS    die Funktion ist varargs deklariert
+            NAME_UNDEFINED      die Funktion ist noch nicht definiert, aber
+                                referenziert
+            NAME_CROSS_DEFINED  die Funktion ist definiert, um in einem
+                                anderen Programm verwendet zu werden
+            TYPE_MOD_NOMASK     die Funktion ist nomask deklariert
+            TYPE_MOD_PUBLIC     die Funktion ist public deklariert
+
+SIEHE AUCH
+        inherit_list(E), function_exists(E), variable_list(E),
+        call_resolved(E)
diff --git a/doc/efun/garbage_collection b/doc/efun/garbage_collection
new file mode 100644
index 0000000..5c6a738
--- /dev/null
+++ b/doc/efun/garbage_collection
@@ -0,0 +1,22 @@
+GESCHUETZT
+SYNOPSIS
+        void garbage_collection();
+        void garbage_collection(string filename);
+
+BESCHREIBUNG
+        Befiehlt dem Treiber, nach Ende der aktuellen Ausfuehrung eine
+        Garbage Collection zu beginnen. Je nachdem, welcher Memory Allocator
+        verwendet wird, ist die Garbage Collection mehr oder weniger
+        gruendlich.
+
+        Wird der smalloc Memory Allocator verwendet, erzeugt GC einen Output
+        in einem Logfile. Der Standardname fuer das Logfile wird beim
+        Programmstart festgelegt, kann aber zur Laufzeit veraendert werden,
+        wenn das Argument <filename> angegeben ist. Der Log-Output wird in
+        diesem Fall an das bezeichnete Logfile angefuegt.
+
+        Fuer andere Memory Allocators erzeugt garbage_collection() keinen
+        Output. Ein allfaelliges Argument <filename> wird ignoriert.
+
+SIEHE AUCH
+        rusage(E), valid_write(M)
diff --git a/doc/efun/get_dir b/doc/efun/get_dir
new file mode 100644
index 0000000..b14288a
--- /dev/null
+++ b/doc/efun/get_dir
@@ -0,0 +1,113 @@
+SYNOPSIS
+        #include <files.h>
+
+        mixed *get_dir(string str)
+        mixed *get_dir(string str, int mask)
+
+BESCHREIBUNG
+        Benoetigt einen Pfad als erstes Argument und liefert ein Feld
+        (Array) mit Dateinamen bzw. Eigenschaften der gefundenen Dateien im
+        angegebenen Verzeichnis.
+
+        Liefert 0 zurueck, wenn es das Verzeichnis, in dem gesucht werden
+        soll, nicht gibt.
+
+        Der Dateinamen-Teil des Pfades darf "*" und "?" als Platzhalter
+        enthalten: jeder "*" steht fuer eine beliebige Anzahl Zeichen (oder
+        sich selber), "?" fuer ein beliebiges Zeichen. Entsprechend liefert
+        get_dir("/pfad/*") ein alphabetisch sortiertes Feld aller Dateien
+        im Verzeichnis "/pfad" oder ({ "/pfad/*" }), wenn es diese Datei
+        geben sollte.
+
+        Gibt man den Pfad eines Verzeichnisses mit abschliessendem "/" oder
+        "/." an (z. B. get_dir("/pfad/.")), so erhaelt man den Inhalt des
+        Verzeichnisses. Um Informationen ueber das Verzeichnis selber zu
+        erhalten, muss man den Pfad des Verzeichnisses angeben.
+
+        Das optionale zweite Argument ist eine Bitmaske, mit der man
+        angeben kann, welche Informationen man ueber die angegebenen
+        Dateien erhalten moechte.
+
+        GETDIR_EMPTY    (0x00)  get_dir() liefert ein leeres Feld (nicht
+                                wirklich sinnvoll)
+        GETDIR_NAMES    (0x01)  liefert die alphabetisch sortierten
+                                Dateinamen.
+        GETDIR_SIZES    (0x02)  liefert die unsortierten Dateigroessen
+                                (file_size()), Verzeichnisse haben die
+                                Dateigroesse FSIZE_DIR (-2).
+        GETDIR_DATES    (0x04)  liefert die unsortierten Zeiten der jeweils
+                                letzten Aenderung in Sekunden seit dem
+                                01.01.1970.
+        GETDIR_ACCESS   (0x40)  liefert die unsortierten Zeiten des jeweils
+                                letzten Zugriffes in Sekunden seit dem
+                                01.01.1970.
+        GETDIR_MODES    (0x80)  liefert die Filemode-Maske
+
+        GETDIR_ALL      (0xDF)  Liefert all Werte zurueck.
+
+        GETDIR_PATH     (0x10)  Dateinamen werden als volle Pfade geliefert.
+                                (ein ev. fehlendes GETDIR_NAMES wird als
+                                vorhanden angenommen).
+        GETDIR_UNSORTED (0x20)  Das Ergebnis wird nicht sortiert.
+
+        Wichtig: Man muss GETDIR_NAMES|GETDIR_UNSORTED verwenden, wenn man
+        die Eintraege in der selben Reihenfolge wie bei GETDIR_SIZES und
+        GETDIR_DATES haben moechte.
+
+        Die Eintraege in der Bitmaske koennen miteinander kombiniert
+        werden.
+
+BEISPIELE
+        Funktion                         Rueckgabewert
+        -------------------------------------------------------------------
+        get_dir("/obj/.")                Alle Dateien, die im Verzeichnis
+                                         /obj enthalten sind.
+        get_dir("/obj/")                 Wie get_dir("/obj/").
+
+        get_dir("/obj/fackel.c")        ({ "fackel.c" }), sofern
+                                         /obj/fackel.c existiert (als
+                                         Datei oder Verzeichnis), ansonsten
+                                         ({}), sofern /obj ein Verzeichnis
+                                         ist, ansonsten 0.
+
+        get_dir("/obj/*")                ({ "*" }), sofern * existiert.
+                                         Ansonsten und normalerweise ein
+                                         alphabetisch sortiertes Feld mit
+                                         den Namen aller Dateien und
+                                         Verzeichnisse in /obj, sofern /obj
+                                         ein Verzeichnis ist, ansonsten 0.
+
+        get_dir("/obj/fackel.c", GETDIR_SIZES)
+                                         ({ <Groesse von /obj/fackel.c> }),
+                                         sofern /obj/fackel.c existiert.
+        get_dir("/obj/.", GETDIR_NAMES)  Wie get_dir("/obj/.").
+        get_dir("/obj/.", GETDIR_SIZES)  Ein unsortiertes Feld mit den
+                                         Groessen der Dateien in /obj.
+        get_dir("/obj/.", GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES)
+            oder kuerzer
+        get_dir("/obj/.", GETDIR_ALL)    Ein eindimensionales und nach
+                                         Namen sortiertes Feld, das fuer
+                                         jede Datei in /obj den Namen, die
+                                         Groesse und den Zeitpunkt der
+                                         letzten Aenderung enthaelt, z.B.
+                                         ({
+                                             "axt.c"    ,  927, 994539583,
+                                             "fackel.c", 1283, 998153903,
+                                             }).
+
+        get_dir("/obj/fackel.c", GETDIR_NAMES|GETDIR_PATH)
+                                         ({ "/obj/fackel.c" }), sofern
+                                         vorhanden.
+        get_dir("/obj/fackel.c", GETDIR_PATH)  Kurzform dafuer.
+
+        transpose_array(({ get_dir(str, GETDIR_NAMES|GETDIR_UNSORTED)
+                         , get_dir(str, GETDIR_SIZES)
+                         , get_dir(str, GETDIR_DATES) }));
+        Liefert ein unsortiertes Feld mit Feldern, von denen ein jedes
+        Name, Groesse und Zeit einer Datei enthaelt, z. B.
+        ({
+            ({ "fackel.c", 1283, 998153903 }),
+            ({ "axt.c"    ,  927, 994539583 }),
+            }).
+SIEHE AUCH
+        cat(E), mkdir(E), rmdir(E), file_size(E)
diff --git a/doc/efun/get_error_file b/doc/efun/get_error_file
new file mode 100644
index 0000000..797e523
--- /dev/null
+++ b/doc/efun/get_error_file
@@ -0,0 +1,21 @@
+SYNOPSIS
+        mixed * get_error_file(string name, int set_forget_flag)
+
+DESCRIPTION
+        Return information about the last error which occured for
+        <name> (where <name> is a valid name from the wiz list).
+
+        Result is an array of four elements: the filename of the
+        program where the error occured, the linenumber in the
+        program, the error message (runtime error messages usually
+        start with a '*'), and a numerical flag (the 'forget flag') if
+        the error information has been queried already.
+
+        If there is no error stored for the given <name>, 0 is
+        returned.
+
+        If <set_forget_flag> is non-zero, the 'forget' flag is set
+        for the error message after it has been returned.
+
+SEE ALSO
+        ed(E), valid_read(M)
diff --git a/doc/efun/get_eval_cost b/doc/efun/get_eval_cost
new file mode 100644
index 0000000..50bc012
--- /dev/null
+++ b/doc/efun/get_eval_cost
@@ -0,0 +1,33 @@
+FUNKTION:
+        int get_eval_cost(void)
+
+BESCHREIBUNG:
+        Liefert die noch verbleibenden Eval Kosten, die das momentane
+        Kommando noch verbrauchen darf.
+        Der Maximalwert betraegt zur Zeit 1.500.000 Ticks (Stand: 2007). 
+
+        Sollten die Kosten bei der Ausfuehrung irgendwo groesser werden,
+        wird ein Fehler der Art "too long eval" erzeugt. Diese Funktion
+        dient dazu, solche Fehler genau zu lokalisieren bzw. 
+        herauszufinden, an welchen Stellen im Code wieviel Rechenzeit 
+        verbraucht wird.
+        
+BEISPIELE
+        void foo()
+        {
+          int prev, used, i;
+          
+          prev=get_eval_cost(); // Merken, was bis hierhin verbraucht wurde 
+          for (i=0;i<=1000;i++) // Dann kommt der zu testende Code, zB eine
+          {                     // Schleife
+            ...
+          }
+          used=prev-get_eval_cost(); // Berechnung der Differenz
+          printf("Die Schleife verbrauchte %d Ticks.\n", used);
+        }
+
+SIEHE AUCH:
+        caller_stack_depth(E), rusage(E), command(E), query_limits(E)
+        Konstante: __MAX_EVAL_COST__
+
+4.Aug 2007 Gloinson
diff --git a/doc/efun/get_extra_wizinfo b/doc/efun/get_extra_wizinfo
new file mode 100644
index 0000000..c356fba
--- /dev/null
+++ b/doc/efun/get_extra_wizinfo
@@ -0,0 +1,19 @@
+SYNOPSIS
+        mixed get_extra_wizinfo (object wiz)
+        mixed get_extra_wizinfo (string wiz)
+        mixed get_extra_wizinfo (int    wiz)
+
+DESCRIPTION
+        Returns the 'extra' information that was set for the given
+        wizard <wiz> in the wizlist.
+
+        If <wiz> is an object, the entry of its creator (uid) is used.
+        If <wiz> is a string (a creator aka uid), it names the entry
+        to use.
+        If <wiz> is the number 0, the data is get from the default wizlist
+        entry.
+
+        The function causes a privilege violation.
+
+SEE ALSO
+        wizlist_info(E), set_extra_wizinfo(E)
diff --git a/doc/efun/get_type_info b/doc/efun/get_type_info
new file mode 100644
index 0000000..81b1f8b
--- /dev/null
+++ b/doc/efun/get_type_info
@@ -0,0 +1,57 @@
+SYNOPSIS
+        mixed get_type_info(mixed arg, int flag);
+
+BESCHREIBUNG
+        Liefert Informationen uber den Typ von <arg>, wie von <flag>
+        angegeben.
+
+        Wenn <flag> keine Zahl ist, liefert get_type_info() ein Array, dessen
+        erstes Element ein Integer ist, der den Datentyp bezeichnet, wie in
+        <lpctypes.h> definiert. Der zweite Eintrag kann zusaetzliche
+        Informationen zu <arg> enthalten.
+
+        Ist <flag> 0, wird nur das erste Element (d.h. der Datentyp) geliefert.
+        Ist <flag> 1, wird nur das zweite Element geliefert.
+
+        Wenn <arg> eine Closure enthaelt, so kann get_type_info() das Objekt
+        der Closure liefern, wenn fuer <flag> 2 gesetzt ist. (Fuer 'alien
+        lfun closures' ist das das Objekt, in dem die Closure definiert ist,
+        nicht das Objekt, an das die Closure gebunden ist.)
+
+        Wenn <arg> eine LFun/Context-Closure enthaelt, so kann get_type_info()
+        den Namen des definierenden Programmes liefern, wenn fuer <flag> 3
+        gesetzt ist. Fur andere Closures wird 0 zurueckgegeben.
+
+        Wenn <arg> eine LFun/Context-Closure enthaelt, so kann get_type_info()
+        den Namen der Funktion liefern, wenn fuer <flag> 4
+        gesetzt ist. Fur andere Closures wird 0 zurueckgegeben.
+
+        Fuer jeden anderen Wert fuer <flag> liefert die Funktion -1.
+
+        Die zusaetzlichen Informationen (also der zweite Eintrag des Arrays)
+        beinhalten:
+          - fuer Mappings deren Breite, also die Anzahl Datenwerte pro Key.
+          - fuer Symbole und gequotete Arrays die Anzahl Quotes.
+          - fuer Closures den (internen) Typ der Closure.
+          - fuer gemeinsam verwendete Strings 0, ungleich 0 fuer andere Strings
+          - fuer structs der eindeutige Identifizierungsstring
+          - -1 fuer alle anderen Datentypen
+
+BUGS
+        Diese Funktion unterliegt haeufigen Veraenderungen im Zug der
+        Treiberentwicklung.
+
+AENDERUNGEN
+        Eingefuehrt in 3.2@127
+        Flagwert 2 eingefuehrt in 3.2.1@84
+        Zusatzinformationen zu Strings eingefuehrt in 3.2.7
+        Bis und mit 3.2.7 lieferte get_type_info(closure, 2) keine Objekte
+            von Lamda Closures und gebundenen Lambda Closures.
+        Bis und mit 3.2.7 lieferte get_type_info(closure, 2) keine Objekte von
+            Efun-, Simul-Efun- oder Operator-Closures.
+        LDMud 3.3.276 fuegte die zusaetzliche Information fuer structs hinzu.
+        LDMud 3.3.548 fuegte Flagwert '3' hinzu.
+        LDMud 3.3.708 fuegte Flagwert '4' hinzu.
+
+SIEHE AUCH
+        debug_info(E), typeof(E), to_object(E)
diff --git a/doc/efun/geteuid b/doc/efun/geteuid
new file mode 100644
index 0000000..75cc3d1
--- /dev/null
+++ b/doc/efun/geteuid
@@ -0,0 +1,12 @@
+SYNOPSIS
+        string geteuid(object ob);
+
+BESCHREIBUNG
+        Liefert die effektive User-ID des Objekts <obj> (normalerweise ein
+        Magier oder eine Domain). Objekte, die durch <obj> geclonet werden,
+        erhalten diese User-ID. Die effektive User-ID wird auch fuer
+        Zugriffschecks verwendet. Wenn <obj> nicht angegeben wird, wird
+        standardmaessig this_object() verwendet.
+
+SIEHE AUCH
+        seteuid(E), getuid(E), getuuid(E), export_uid(E)
\ No newline at end of file
diff --git a/doc/efun/getuid b/doc/efun/getuid
new file mode 100644
index 0000000..bf38fe8
--- /dev/null
+++ b/doc/efun/getuid
@@ -0,0 +1,17 @@
+SYNOPSIS
+        string getuid(object ob);
+
+BESCHREIBUNG
+        User-IDs werden im Compat Modus nicht verwendet.
+
+        Die Funktion liefert die UID des Objekts, also den Namen des Magiers
+        oder der Domain oder Gruppe, die fuer das Objekt verantwortlich ist.
+        Der Name entspricht dem Namen, der in der Wizlist verwendet wird.
+        Wird <ob> nicht angegeben, wird standardmaessig this_object()
+        genommen.
+
+AENDERUNGEN
+        Seit 3.2.1@47 ist diese Efun ein Alias fuer creator()
+
+SIEHE AUCH
+        seteuid(E), geteuid(E), export_uid(E), creator(E), getuuid(E)
diff --git a/doc/efun/gmtime b/doc/efun/gmtime
new file mode 100644
index 0000000..ab12eff
--- /dev/null
+++ b/doc/efun/gmtime
@@ -0,0 +1,38 @@
+SYNOPSIS
+        #include <sys/time.h>
+
+        int *gmtime(int clock);
+        int *gmtime(int *uclock);
+
+BESCHREIBUNG
+        Interpretiert <clock> als Anzahl Sekunden seit dem 01. Januar 1970,
+        00:00:00 Uhr und gibt die Zeit in UTC in einer schoenen Struktur
+        aus. Wird <clock> nicht angegeben, wird stattdessen time() verwendet.
+
+        Alternativ kann auch ein Array mit zwei Elementen angegeben werden,
+        wie es von uclock() geliefert wird: das erste Element wird
+        interpretiert wie <clock>, das zweite Element bezeichnet die
+        Mikrosekunden in der aktuellen Sekunde. Dieses zweite Element wird
+        ignoriert.
+
+        Das Resultat von gmtime() ist ein Array, das folgende Werte beinhaltet:
+            int TM_SEC   (0) : Sekunden (0..59)
+            int TM_MIN   (1) : Minuten (0..59)
+            int TM_HOUR  (2) : Stunden (0..23)
+            int TM_MDAY  (3) : Tag im Monat (1..31)
+            int TM_MON   (4) : Monat im Jahr (0..11)
+            int TM_YEAR  (5) : Jahr (z.B.  2001)
+            int TM_WDAY  (6) : Wochentag (Sunday = 0)
+            int TM_YDAY  (7) : Tag im Jahr (0..365)
+            int TM_ISDST (8) : TRUE: Daylight saving time
+
+BEISPIEL
+        printf("Heute ist %s\n", ({ "Sonntag", "Montag", "Dienstag",
+            "Mittwoch",        "Donnerstag", "Freitag", "Samstag"})
+            [gmtime()[TM_WDAY]]);
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9.
+
+SIEHE AUCH
+    ctime(E), localtime(E), time(E), utime(E)
diff --git a/doc/efun/hash b/doc/efun/hash
new file mode 100644
index 0000000..e3457c1
--- /dev/null
+++ b/doc/efun/hash
@@ -0,0 +1,36 @@
+SYNOPSIS
+        #include <sys/tls.h>
+
+        string hash(int method, string arg [, int iterations ] )
+        string hash(int method, int *  arg [, int iterations ] )
+
+BESCHREIBUNG
+        Berechne den Hash <method> vom Argument <arg>. Der Hash wird
+        mit <iterations> Wiederholungen berechnet, wird der Wert weggelassen,
+        wird eine Wiederholung verwendet.
+
+        <method> ist eine der TLS_HASH_-Konstanten in tls.h; nicht jede
+        beschriebene Methode ist in einem gegebenen Driver vorhanden:
+
+          TLS_HASH_SHA1      (1)
+          TLS_HASH_SHA224    (2)
+          TLS_HASH_SHA256    (3)
+          TLS_HASH_SHA384    (4)
+          TLS_HASH_SHA512    (5)
+          TLS_HASH_MD5       (6)
+          TLS_HASH_RIPEMD160 (7)
+
+        Diese Funktion ist nur verfuegbar wenn der Driver mit OpenSSL
+        compiliert wurde.
+
+BEISPIELE
+        string s;
+
+        s = hash(TLS_HASH_SHA1, "Hello", 2);
+        s = hash(TLS_HASH_SHA1, ({ 'H', 'e', 'l', 'l', 'o' }) )
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.3.714
+
+SIEHE AUCH
+        crypt(E), md5(E), md5_crypt(E), sha1(E), hmac(E)
diff --git a/doc/efun/heart_beat_info b/doc/efun/heart_beat_info
new file mode 100644
index 0000000..dee8d52
--- /dev/null
+++ b/doc/efun/heart_beat_info
@@ -0,0 +1,9 @@
+SYNOPSIS
+        object *heart_beat_info()
+
+BESCHREIBUNG
+        Resultat ist ein Array mit allen Objekten, die einen aktiven
+        heart_beat() haben.
+
+SIEHE AUCH
+        set_heart_beat(E), heart_beat(A), call_out_info(E)
diff --git a/doc/efun/hmac b/doc/efun/hmac
new file mode 100644
index 0000000..d41d6d5
--- /dev/null
+++ b/doc/efun/hmac
@@ -0,0 +1,35 @@
+SYNOPSIS
+        #include <sys/tls.h>
+
+        string hmac(int method, string key, string arg )
+        string hmac(int method, string key, int *  arg )
+
+BESCHREIBUNG
+        Berechnet den Hashed Message Authenication Code fuer <arg>
+        nach Methode <method> und fuer das Password <key>.
+
+        <method> ist eine der TLS_HASH_-Konstanten in tls.h; nicht jede
+        beschriebene Methode ist in einem gegebenen Driver vorhanden:
+
+          TLS_HASH_SHA1      (1)
+          TLS_HASH_SHA224    (2)
+          TLS_HASH_SHA256    (3)
+          TLS_HASH_SHA384    (4)
+          TLS_HASH_SHA512    (5)
+          TLS_HASH_MD5       (6)
+          TLS_HASH_RIPEMD160 (7)
+
+        Diese Funktion ist nur verfuegbar wenn der Driver mit OpenSSL
+        compiliert wurde.
+
+BEISPIELE
+        string s;
+
+        s = hmac(TLS_HASH_SHA1, "secret", "Hello");
+        s = hmac(TLS_HASH_SHA1, "secret", ({ 'H', 'e', 'l', 'l', 'o' }) )
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.3.714
+
+SIEHE AUCH
+        crypt(E), md5(E), md5_crypt(E), sha1(E), hmac(E)
diff --git a/doc/efun/idna_stringprep b/doc/efun/idna_stringprep
new file mode 100644
index 0000000..7001140
--- /dev/null
+++ b/doc/efun/idna_stringprep
@@ -0,0 +1,22 @@
+OPTIONAL
+SYNOPSIS
+        #include <sys/idn.h>
+
+        string idna_stringprep (string str, int profile, int flags = 0)
+
+BESCHREIBUNG
+        Prepariere den UTF-8 String <str> passend zum Profil <profile>
+        (siehe auch libidn stringprep(3)).
+
+        <profile> und <flags> sind definiert in idn.h .
+
+        Sollte ein Fehler auftreten, wird eine Exception geworfen.
+
+        Diese Efun is nur verfuegbar auf Systemen mit libidn
+        installiert - in diesem Fall ist das Makro __IDNA__ definiert.
+
+GESCHICHTE
+        Introduced in LDMud 3.3.713.
+
+SIEHE AUCH
+        convert_charset(E), idna_to_ascii(E), idna_to_unicode(E)
diff --git a/doc/efun/idna_to_ascii b/doc/efun/idna_to_ascii
new file mode 100644
index 0000000..63c66b9
--- /dev/null
+++ b/doc/efun/idna_to_ascii
@@ -0,0 +1,20 @@
+OPTIONAL
+SYNOPSIS
+        #include <sys/idn.h>
+
+        string idna_to_ascii (string name)
+
+BESCHREIBUNG
+        Wandle den String <name> von UTF-8 to IDNA Darstellung (8z
+        punycode).
+
+        Sollte ein Fehler auftreten, wird eine Exception geworfen.
+
+        Diese Efun is nur verfuegbar auf Systemen mit libidn
+        installiert - in diesem Fall ist das Makro __IDNA__ definiert.
+
+GESCHICHTE
+        Introduced in LDMud 3.3.713.
+
+SIEHE AUCH
+        convert_charset(E), idna_to_unicode(E), idna_stringprep(E)
diff --git a/doc/efun/idna_to_unicode b/doc/efun/idna_to_unicode
new file mode 100644
index 0000000..183dce8
--- /dev/null
+++ b/doc/efun/idna_to_unicode
@@ -0,0 +1,20 @@
+OPTIONAL
+SYNOPSIS
+        #include <sys/idn.h>
+
+        string idna_to_unicode (string name)
+
+BESCHREIBUNG
+        Wandle den String <name> von IDNA Darstellung (8z punycode)
+        nach UTF-8.
+
+        Sollte ein Fehler auftreten, wird eine Exception geworfen.
+
+        Diese Efun is nur verfuegbar auf Systemen mit libidn
+        installiert - in diesem Fall ist das Makro __IDNA__ definiert.
+
+GESCHICHTE
+        Introduced in LDMud 3.3.713.
+
+SIEHE AUCH
+        convert_charset(E), idna_to_ascii(E), idna_stringprep(E)
diff --git a/doc/efun/implode b/doc/efun/implode
new file mode 100644
index 0000000..85a723b
--- /dev/null
+++ b/doc/efun/implode
@@ -0,0 +1,25 @@
+SYNOPSIS
+        string implode(mixed *arr, string del)
+
+BESCHREIBUNG
+	Setzt alle Zeichenketten (Strings) aus dem Feld (Array) arr zu
+        einer Zeichenkette zusammen und fuegt dabei die Zeichenkette del
+        zwischen je zwei Elementen ein. Elemente aus dem Feld arr, welche
+        keine Zeichenketten sind, werden ignoriert.
+
+BEISPIELE
+	Funktion                                        Rueckgabewert
+        -------------------------------------------------------------------
+	implode(({ "foo", "bar", "" }), "*")            "foo*bar*"
+	implode(({ "a", 2, this_object(), "c" }), "b")  "abc"
+
+	Kann zusammen mit explode() als Funktion zum Ersetzen von
+        Zeichenketten verwendet werden:
+        implode(explode("a short text", " "), "_")      "a_short_text"
+
+	Heutzutage kann man stattdessen auch
+	regreplace("a short text", " ", "_", 1)
+	verwenden.
+
+SIEHE AUCH
+        explode(E)
diff --git a/doc/efun/include_list b/doc/efun/include_list
new file mode 100644
index 0000000..cc44fa9
--- /dev/null
+++ b/doc/efun/include_list
@@ -0,0 +1,80 @@
+SYNOPSIS
+        #include <sys/include_list.h>
+
+        string *include_list();
+        string *include_list(object ob);
+        string *include_list(object ob, int flags);
+
+BESCHREIBUNG
+        Diese Funktion liefert Informationen ueber alle Dateien, die bei der
+        Kompilierung von <ob> in dieses Objekt eingebunden wurden, inklusive
+        den Programmnamen von <ob>. Wird <ob> nicht angegeben, wird
+        standardmaessig das aktuelle Objekt verwendet.
+
+        Im resultierenden Array besteht die Information zu einem Includefile
+        aus drei Elementen:
+          - string [i+0]: der Name des Includefiles, wie er im Programm
+                          auftaucht, inklusive den Trennzeichen wie " " oder
+                          < >.
+          - string [i+1]: der absolute Dateipfad des Includefiles.
+          - int    [i+2]: die Tiefe der Inklusion (gewoehnlich 1, fuer
+                          verschachtelte Includes auch groesser als 1)
+
+        Der erste Eintrag im Resultat ist der Name des Programmes selbst in
+        [i+0], die anderen beiden Elemente [i+1] und [i+2] sind 0.
+
+        <flag> bezeichnet die Struktur des Ergebnisses:
+          - <flag> = INCLIST_FLAT (0, Standard):
+            Das Resultat ist ein flaches Array. Der erste Eintrag bezeichnet
+            <ob> selbst, die restlichen Eintraege bezeichnen alle Includefiles
+            in der Reihenfolge, in der sie auftreten.
+          - <flag> = INCLIST_TREE (1):
+            Das Resultat ist ein Array, dessen erster Eintrag das Objekt <ob>
+            selbst bezeichnet. Alle folgenden Eintraege bezeichnen die
+            Includefiles von <ob>. Wenn ein Includefile von <ob> selbst keine
+            Includefiles hat, wird seine Information direkt im Array
+            gespeichert. Fuer verschachtelte Includefiles wird ein Untervektor
+            erzeugt und dann in diesem Untervektor abgespeichert (wiederum
+            in [i+0], [i+1] und [i+2] sind 0). Diese Untervektoren haben
+            die gleiche Struktur wie das Resultatarray.
+
+        Wenn ein Objekt, inklusive <ob>, einem replace_programm() unterworfen
+        war, spiegeln die gelieferten Dateinamen das effektiv aktive Programm
+        wider.
+
+        Die Includepfade, die geliefert werden, beginnen immer mit '/'
+        (absolute Pfade), auch wenn der Treiber im COMPAT Modus laeuft.
+        Trotzdem beginnt der tatsaechliche Dateiname nicht mit einem '/',
+        wenn der Treiber im COMPAT Modus laeuft.
+
+BEISPIEL
+        Dieser Code erzeugt (mit /sys als Includeverzeichnis des Systems):
+
+            a.c:  #include "b.h"
+                  #include <c.h>
+            b.h:  #include "d.h"
+            c.h:  #define BAR
+            d.h:  #define FOO
+
+        Die Efun liefert drei Resultate:
+
+            include_list(a, INCLIST_FLAT)
+            -> ({ "a.c", 0, 0
+                , "\"b.h\"", "/.../b.h", 1
+                , "\"d.h\"", "/.../d.h", 2
+                , "<c.h>",   "/sys/c.h", 1
+               })
+
+             include_list(a, INCLIST_TREE)
+             -> ({ "a.c", 0, 0
+                 , ({ "\"b.h\"", "/.../b.h", 1
+                    , "\"d.h\"", "/.../d.h", 2
+                   }), 0, 0
+                 , "<c.h>",   "/sys/c.h", 1
+                 })
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9, 3.3.128
+
+SIEHE AUCH
+        debug_info(E), inherit_list(E)
diff --git a/doc/efun/inherit_list b/doc/efun/inherit_list
new file mode 100644
index 0000000..97c738e
--- /dev/null
+++ b/doc/efun/inherit_list
@@ -0,0 +1,60 @@
+SYNOPSIS
+        #include <sys/inherit_list.h>
+
+        string *inherit_list();
+        string *inherit_list(object ob);
+        string *inherit_list(object ob, int flags);
+
+BESCHREIBUNG
+        Liefert die Namen von allen Dateien, die von <ob> geerbt werden,
+        inklusive <ob>s eigener Dateiname. Wird <ob> nicht angegeben, wird
+        standarndmaessig das aktuelle Objekt verwendet.
+
+        Der Wert von <flags> bestimmt die Struktur des Rueckgabewertes:
+          - <flag> = INHLIST_FLAT (0, default):
+            inherit_list() liefert ein Array von Dateinamen, beginnend mit dem
+            Namen von <ob> selbst, gefolgt von den Namen aller geerbten
+            Objekten.
+          - <flag> = INHLIST_TREE (1):
+            inherit_list() liefert ein Array von Dateinamen, beginnend mit dem
+            Namen von <ob> selbst. Wenn ein geerbte File selbst keine Files
+            erbt, wird sein Name direkt in das Ergebnis eingetragen. Wenn ein
+            geerbtes File selbst Files erbt, wird ein Untervektor erzeugt, in
+            dem die Inherits eingetragen werden. Der Untervektor hat die
+            gleiche Struktur wie der Haupvektor.
+          - <flag> = INHLIST_TAG_VIRTUAL (2):
+            Alle Namen im Ergebnisvektor haben ein leeres Tag "  " (zwei
+            Leerschlaege) fuer normale Inherits und "v " fuer virtuelle
+            Inherits als Praefix vorangestellt.
+
+        Alle Flags koennen mit einem binaeren Oder | verbunden werden, wobei
+        INHLIST_FLAT und INHLIST_TREE sich gegenseitig ausschliessen.
+
+        Wenn ein Objekt, inklusive <ob>, einem replace_programm() unterworfen
+        war, spiegeln die gelieferten Dateinamen das effektiv aktive Programm
+        wider.
+
+        Die Inheritpfade, die geliefert werden, beginnen immer mit '/'
+        (absolute Pfade), auch wenn der Treiber im COMPAT Modus laeuft.
+
+BEISPIEL
+        Gegeben folgende Vererbungsstruktur:
+
+            / c - d
+          a
+            \ b
+
+        Wobei d virtuell geerbt wird, ergeben sich folgende Resultate:
+
+            inherit_list(a) -> ({ "a", "c", "b", "d" })
+            inherit_list(c) -> ({ "c", "d" })
+            inherit_list(a, 1) -> ({ "a", ({ "c", "d" }), "b" })
+            inherit_list(a, 3) -> ({ "  a", ({ " c", "v d" }), "  b" })
+
+AENDERUNGEN
+        Vor 3.2.8, begannen die gelieferten Namen niemals mit einem '/'.
+        LDMud 3.2.9 fuehrte die Baumstruktur (_TREE) und Tags fuer virtuelle
+            Inherits ("v ") ein.
+
+SIEHE AUCH
+        debug_info(E), include_list(E)
diff --git a/doc/efun/input_to b/doc/efun/input_to
new file mode 100644
index 0000000..11cbd97
--- /dev/null
+++ b/doc/efun/input_to
@@ -0,0 +1,146 @@
+SYNOPSIS
+        #include <sys/input_to.h>
+
+        void input_to(string|closure fun);
+        void input_to(string|closure fun, int flag, ...);
+        void input_to(string|closure fun, int flag, string|closure prompt, ...);
+
+BESCHREIBUNG
+        Die naechste Zeile, die der Spieler eintippt, wird als Argument an die
+        Funktion <fun> uebergeben. Ausnahme: wenn die naechste Zeile mit einem
+        '!' beginnt, wird sie als Kommando ausgewertet bzw. an das letzte
+        input_to() uebergeben, das das INPUT_IGNORE_BANG Flag gesetzt hat.
+        Die Funktion <fun> kann "static" deklariert sein, nicht aber "private"
+        (sonst wird sie nicht gefunden).
+
+        Der Aufruf von <fun> erfolgt nicht sofort, sondern erst, wenn der
+        Spieler die Entertaste drueckt.
+
+        Wenn input_to() mehr als einmal pro Funktion aufgerufen wird,
+        wird normalerweise nur das erste input_to() beruecksichtigt.
+        Diese Verhalten kann durch die Angabe von INPUT_APPEND
+        modifiziert werden: in diesem Fall wird das input_to() an die
+        Liste der bereits anhaengigen input_tos angehaengt (siehe
+        BEISPIELE).
+
+        Wird andererseits waehrend einem laufenden input_to() (mittels "!" am
+        Zeilenanfang) eine neue Funktion aufgerufen, die wiederum ein
+        input_to() enthaelt, wird das urspruengliche input_to() so lange
+        unterbrochen, bis das neue input_to() abgearbeitet wurde,
+        anschliessend wird es wieder aktiv.
+
+        Das optionale <flag> kann ein binaeres Oder (|) der folgenden
+        Werte sein:
+
+        INPUT_NOECHO (1):
+            Die vom Spieler eingegebene Zeile erzeugt kein Echo und wird auch
+            nicht erkannt, wenn der Spieler beobachtet wird.
+
+            Dieser Modus kann nur geaendert werden, wenn telnet enabled ist.
+
+        INPUT_CHARMODE (2):
+            Die Verbindung zum User wechselt von Zeilen- auf Zeichenmodus. So
+            wird nur ein einzelnes Zeichen (!) vom Spieler empfangen.
+
+            Ist telnet disabled, wird lediglich die Interpretation der
+            einkommenden Daten durch den Driver umgeschaltet - das
+            Clientprogramm des Spieler verbleibt im gerade aktiven Modus.
+
+            Nachdem die Funktion <fun> ausgefuehrt wurde, wechselt die
+            Verbindung zurueck in Zeilenmodus, ausser ein nachfolgendes
+            input_to( , 2) wurde gestartet.
+
+            Zeilenumbrueche werden je nach Client unterschiedlich empfangen,
+            entweder als "", als "\r" gefolgt von "" oder als "\r" (letzteres
+            kommt vor allem bei Windows Clients vor).
+
+            Das Frontend des Spielers kann dauernd im Zeilenmodus bleiben.
+            Auch wenn input_to() nur ein einzelnes Zeichen fordert, muss der
+            Spieler unter Umstaenden das Zeichen und einen Zeilenumbruch
+            druecken (und senden). Normalerweise erhaelt <fun> dann den
+            gesamten Input auf einmal.
+
+            Will man laenger im Zeichenmodus bleiben, kann der Overhead
+            reduziert werden, indem set_combine_charset() verwendet wird. So
+            koennen Zeichensequenzen als ein String anstelle von
+            Zeichen-fuer-Zeichen empfangen werden. In einem screenorientierten
+            Editor gilt dies fuer die meisten druckbaren Zeichen.
+
+        INPUT_PROMPT (4):
+            Das Argument nach dem <flag> wird als Prompt fuer die Eingabe
+            verwendet. Wenn dieses Argument nicht angegeben (und damit kein
+            Propmt definiert) ist, wird kein Prompt ausgegeben.
+
+        INPUT_NO_TELNET (8):
+            Modifiziert das INPUT_CHARMODE Argument: der Driver aendert
+            seine Behandlung von eingehenden Daten entsprechend dem _CHARMODE
+            Argument, sendet aber keine Telnetkommandos zur Anpassung
+            des Verhaltens des Clientprogrammes.
+
+        INPUT_APPEND (16):
+            Das input_to() wird an die Liste der bereits anhaengigen
+            input_tos angehaengt.
+
+        INPUT_IGNORE_BANG (128):
+            Eingaben, die mit ! beginnen, werden NICHT als Kommandi geparset,
+            sondern auch als Argument an die Funkion <fun> uebergeben. Die
+            Verwendung dieses Flags ist eingeschraenkt.
+
+        Alle nachfolgenden Argumente werden als zweites bzw. drittes usw.
+        Argument an <fun> uebergeben.
+
+BEISPIEL
+        void func()
+        {
+            ...
+            input_to("enter_name", INPUT_PROMPT, "Wie lautet dein Name?:");
+            /* Frueher erledigte man dies mit:
+             *   write("Wie lautet dein Name?:");
+             *   input_to("enter_name");
+             */
+             ...
+         }
+
+         enter_name(string str)
+         {
+            write("Heisst du wirklich '"+str+"'?? *kicher*\n");
+            ...
+        }
+
+        Bei der input_to() Anweisung fuehrt der Driver die Funktion
+        func() aus, erwartet aber gleichzeitig Input vom Spieler. Wenn
+        dieser etwas eingegeben UND DIE ENTERTASTE GEDRUECKT HAT, arbeitet
+        der Driver die Funktion enter_name() mit dem eingegebenen String
+        als Argument ab.
+
+
+        void func() {
+          ..
+          input_to("enter_firstname");
+          input_to("enter_lastname, INPUT_APPEND);
+          ...
+        }
+
+        Diese Sequenze erzeugt zwei input_tos: Eingaben gehen zuerst
+        an enter_firstname(); und wenn diese Funktion fertig ist
+        (einschliesslich etwaiger eigener non-INPUT_APPEND input_tos), geht
+        die naechste Eingabe an enter_lastname().
+
+AENDERUNGEN
+        Die Bedeutung von <flag> wurde in 3.2.1@93 erweitert.
+        Die Limitierung fuer das "stapeln" von input_to()s aus !-Kommandi
+            wurde in LDMud 3.2.8 implementiert.
+        Seit LDMud 3.2.8 kann <fun> in Form einer Closure angegeben werden.
+        LDMud 3.2.9 fuehrte das Flag INPUT_PROMPT samt zugehoerigem Argument
+            ein.
+        LDMud 3.2.11/3.3.593 fuehrte das INPUT_NO_TELNET Flag ein.
+        LDMud 3.2.11/3.3.637 fuehrte das INPUT_APPEND Flag ein.
+
+BUGS
+        Im Zeichenmodus sollten Zeilenumbrueche eigentlich als "\n" zurueck
+        gegeben werden. Dies allerdings kann existierenden Code zerstoeren.
+
+SIEHE AUCH
+        call_other(E), sscanf(E), privilege_violation(M),
+        set_combine_charset(E), query_input_pending(E), find_input_to(E),
+        input_to_info(E), remove_input_to(E), enable_telnet(E)
diff --git a/doc/efun/input_to_info b/doc/efun/input_to_info
new file mode 100644
index 0000000..8dac3d3
--- /dev/null
+++ b/doc/efun/input_to_info
@@ -0,0 +1,21 @@
+SYNOPSIS
+        *mixed input_to_info(object player);
+
+BESCHREIBUNG
+        Liefert ein Array aller fuer <player> haengigen input_to()s. Der
+        erste Eintrag im Array ist der aelteste haengige input_to()
+        (derjenige, welcher als erstes gestartet wurde), der letzte
+        Eintrag ist der neuste input_to().
+
+        Jeder Eintrag im Array ist wiederum ein Array von 2 oder mehr
+        Eintraegen, das folgende Elemente enthaelt:
+            0:   Das Objekt (nur, wenn die Funktion ein string ist)
+            1:   Die Funktion (String oder Closure)
+            2ff: Die Argumente
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9.
+
+SIEHE AUCH
+        input_to(E), find_input_to(E), remove_input_to(E),
+        query_input_pending(E)
diff --git a/doc/efun/insert_alist b/doc/efun/insert_alist
new file mode 100644
index 0000000..01aca4e
--- /dev/null
+++ b/doc/efun/insert_alist
@@ -0,0 +1,28 @@
+OPTIONAL, VERALTET
+SYNOPSIS
+        mixed *insert_alist(mixed key, mixed data, ... , mixed *alist);
+        int    insert_alist(mixed key, mixed *keys);
+
+BESCHREIBUNG
+     1. Form: Einfuegen in eine Alist.
+        Der <key> und alle foglenden <data> Argumente werden in die Alist
+        eingefuegt. Wenn bereits ein Eintrag fuer <key> existiert, werden
+        nur die <data> Eintraege ersetzt. Natuerlich muss die Anzahl <data>
+        Argumente der Anzahl der Datenarrays in der Alist entsprechen.
+        Das Resultat dieser Operation ist die neue Alist.
+
+     2. Form: Einfuegen eines Keys
+        Der <key> wird in ein (geordnetes) Array von <keys> eingeordnet,
+        sodass nachfolgendes assoc()s schnell suchen koennen. Das Resultat
+        ist der Index, unter dem <key> eingefuegt oder bereits gefunden
+        wurde.
+
+ANMERKUNG
+        Wird mit String-Keys gearbeitet, kann der Index nach dem naechsten
+        Aufruf von insert_alist() nicht mehr gueltig sein.
+
+        Komplexitaet: O(lg(n) + a*n) fuer n gleich der Anzahl der Keys und
+        eine sehr kleine Konstante s (fuer Blockverschiebungen).
+
+SIEHE AUCH
+        alists(LPC), assoc(E), order_alist(E)
diff --git a/doc/efun/interactive b/doc/efun/interactive
new file mode 100644
index 0000000..c326f8a
--- /dev/null
+++ b/doc/efun/interactive
@@ -0,0 +1,10 @@
+SYNOPSIS
+        int interactive(object obj);
+
+BESCHREIBUNG
+        Liefert 1 zurueck, wenn <obj> ein interaktiver User (ein Spieler)
+        ist. Wird <obj> weggelassen, dann wird this_object() verwendet.
+
+SIEHE AUCH
+        query_once_interactive(E), query_ip_number(E), query_ip_name(E),
+        query_idle(E)
diff --git a/doc/efun/interactive_info b/doc/efun/interactive_info
new file mode 100644
index 0000000..f7c2877
--- /dev/null
+++ b/doc/efun/interactive_info
@@ -0,0 +1,106 @@
+SYNOPSIS
+        #include <interactive_info.h>
+
+        mixed interactive_info(object ob, int what)
+
+DESCRIPTION
+        Returns some internal information about the interactive user <ob>.
+        The argument <what> determines which information is returned.
+
+        It can be either a configuration option as given to
+        configure_interactive() or one of the following options:
+
+
+
+        Connection Information:
+
+        <what> == II_IP_NAME:
+          The hostname of <ob>. The hostname will asynchronously
+          looked up by the ERQ daemon and might therefore not be
+          available at the time of the first connection.
+          If no name is available the address will be returned.
+
+        <what> == II_IP_NUMBER:
+          The IP address of <ob> given as a string.
+
+        <what> == II_IP_PORT:
+          The client port number of <ob>.
+
+        <what> == II_IP_ADDRESS:
+          The full socket address structure given as an array of bytes.
+
+          For IPv4 (sockaddr_in):
+              array[0.. 1]: sin_family
+              array[2.. 3]: sin_port
+              array[4.. 7]: sin_addr
+              array[8..15]: undefined.
+
+          For IPv6 (sockaddr_in6):
+              array[ 0.. 1]: sin6_family
+              array[ 2.. 3]: sin6_port
+              array[ 4.. 7]: sin6_flowinfo
+              array[ 8..23]: sin6_addr
+              array[24..27]: sin6_scope_id
+
+        <what> == II_MUD_PORT:
+          The server port number that <ob> connected to.
+
+
+
+        Telnet Related Information:
+
+        <what> == II_MCCP_STATS:
+          Statistics about the current compression of <ob> given
+          as an array ({ uncompressed bytes, compressed bytes }).
+
+          If the connection is not compressed, 0 is returned.
+
+          Available only if the driver is compiled with MCCP enabled;
+          __MCCP__ is defined in that case.
+
+
+
+        Input Handling:
+
+        <what> == II_INPUT_PENDING:
+          If <ob> has an input_to() pending, the object that has called
+          the input_to() is returned, else 0.
+
+        <what> == II_EDITING:
+          If <ob> is currently editing with ed() and ed() was called with
+          an exit function, then the object that has called ed()
+          will be returned, 0 otherwise.
+
+        <what> == II_IDLE:
+          The number of seconds that the interactive object <ob> has been
+          idle.
+
+
+
+        Output Handling:
+
+        <what> == II_SNOOP_NEXT:
+          Returns the user who is currently snooping <ob>.
+          The calling object must be privileged by the master object
+          via valid_query_snoop().
+
+        <what> == II_SNOOP_PREV:
+          Returns the victim who is currently snooped by <ob>.
+          The calling object must be privileged by the master object
+          via valid_query_snoop().
+
+        <what> == II_SNOOP_ALL:
+          Returns all objects who are currently snooping <ob>.
+          Only one object can snoop <ob> directly, but that user might
+          be snooped, too, and so building a chain that is returned
+          as an array.
+
+          The calling object must be privileged by the master object
+          via valid_query_snoop().
+
+
+HISTORY
+        Introduced in LDMud 3.5.0.
+
+SEE ALSO
+        configure_interactive(E), object_info(E), driver_info(E)
diff --git a/doc/efun/intp b/doc/efun/intp
new file mode 100644
index 0000000..6a6a0f2
--- /dev/null
+++ b/doc/efun/intp
@@ -0,0 +1,10 @@
+SYNOPSIS
+        int intp(mixed arg)
+
+BESCHREIBUNG
+        Liefert 1, wenn das Argument eine Ganzzahl (Integer) ist, ansonsten
+        0.
+
+SIEHE AUCH
+        closurep(E), floatp(E), mappingp(E), objectp(E), pointerp(E),
+        referencep(E), stringp(E), symbolp(E), clonep(E)
diff --git a/doc/efun/invert_bits b/doc/efun/invert_bits
new file mode 100644
index 0000000..27bbe6d
--- /dev/null
+++ b/doc/efun/invert_bits
@@ -0,0 +1,17 @@
+SYNOPSIS
+        string invert_bits(string str);
+
+BESCHREIBUNG
+        Invertiert den Status aller Bits im Bitstring <str> und liefert den
+        neuen String zurueck. Dabei bleibt die Laenge von <str>, also die
+        gesamte Anzahl Bits, die gleiche.
+
+BEISPIEL
+        string s;
+        s = set_bit("", 3); s = set_bit(s, 4); s = set_bit(s, 15);
+        --> s ist nun  "8 ("
+        invert_bits(s) --> liefert "G_W"
+
+SIEHE AUCH
+        set_bit(E), clear_bit(E), test_bit(E), last_bit(E), and_bits(E),
+        or_bits(E), xor_bits(E), count_bits(E), copy_bits(E)
diff --git a/doc/efun/json_parse b/doc/efun/json_parse
new file mode 100644
index 0000000..a9f4c31
--- /dev/null
+++ b/doc/efun/json_parse
@@ -0,0 +1,45 @@
+OPTIONAL
+EXPERIMENTAL
+SYNOPSIS
+
+        mixed json_parse(string jsonstring)
+
+DESCRIPTION
+        This efun parses the JSON object encoded as string in <jsonstr> into a
+        suitable LPC type.
+        
+        Handles the following JSON types:
+        <null>        -> int (0)
+        <boolean>     -> int (0 or 1)
+        <int | int64> -> int
+        <double>      -> float
+        <string>      -> string
+        <object>      -> mapping
+        <array>       -> arrays
+        All other JSON types cause a runtime error.
+
+        The JSON object can nest other JSON objects.
+        
+        The function is available only if the driver is compiled with Iksemel
+        support. In that case, __JSON__ is defined. 
+ 
+LIMITATIONS
+        64 bit wide integers can only be parsed losslessly on hosts with
+        a 64 bit wide LPC int and json-c library newer than 0.90.
+
+BUGS
+        __FLOAT_MIN__ is not serialized/parsed losslessly.
+
+EXAMPLES
+        json_parse("42")              -> 42
+        json_parse("42.0")            -> 42.0
+        json_parse("\"hello world\\n\"")   -> "hello world\n"
+        json_parse("[ 1, 2, 3, 4, 5, 6 ]") -> ({1,2,3,4,5,6})
+        json_parse("{ \"test 2\": 42.000000, \"test 1\": 42 }")
+                                      -> ([ "test 1": 42, "test 2": 42.0 ])
+        
+HISTORY
+        Added in LDMud 3.5.0
+
+SEE ALSO
+        json_serialize(E)
diff --git a/doc/efun/json_serialize b/doc/efun/json_serialize
new file mode 100644
index 0000000..fd68deb
--- /dev/null
+++ b/doc/efun/json_serialize
@@ -0,0 +1,45 @@
+OPTIONAL
+EXPERIMENTAL
+SYNOPSIS
+
+        string json_serialize(mixed <data>)
+
+DESCRIPTION
+        This efun creates a JSON object from the given LPC variable and
+        returns the object encoded as a LPC string. For container types like 
+        arrays, mappings and structs, this will be done recursively.
+        
+        Only the following LPC types are serialized. All other LPC types cause
+        a  runtime error.
+        <int>        -> JSON int
+        <float>      -> JSON double
+        <string>     -> JSON string
+        <mapping>    -> JSON objects
+        <array>      -> JSON arrays
+        <struct>     -> JSON objects
+        
+        The function is available only if the driver is compiled with Iksemel
+        support. In that case, __JSON__ is defined. 
+
+LIMITATIONS 
+        Only mappings with a width of 1 value per key and only string keys
+        can be serialized.
+        64 bit wide integers can only be serialized losslessly on hosts with
+        a 64 bit wide LPC int and json-c library newer than 0.90.
+
+BUGS
+        __FLOAT_MIN__ is not serialized/parsed losslessly.
+
+EXAMPLES
+        json_serialize(42)              -> "42"
+        json_serialize(42.0)            -> "42.0"
+        json_serialize("hello world\n") -> "\"hello world\\n\""
+        json_serialize(({1,2,3,4,5,6})) -> "[ 1, 2, 3, 4, 5, 6 ]"
+        json_serialize(([ "test 1": 42, "test 2": 42.0 ]))
+                                -> "{ \"test 2\": 42.000000, \"test 1\": 42 }"
+        
+HISTORY
+        Added in LDMud 3.5.0
+
+SEE ALSO
+        json_parse(E)
diff --git a/doc/efun/lambda b/doc/efun/lambda
new file mode 100644
index 0000000..b156798
--- /dev/null
+++ b/doc/efun/lambda
@@ -0,0 +1,28 @@
+SYNOPSIS
+        closure lambda(mixed *arr, mixed);
+
+BESCHREIBUNG
+        Erzeugt eine Lambda Closure, entsprechend den Lamda Closures in LISP.
+        Die Closure ist an das Objekt gebunden, das sie erzeugt hat, und kann
+        deshalb Verweise auf globale Variablen enthalten.
+
+        Das erste Argument ist ein Array, das die Argumente (Symbole)
+        beschreibt, die der Closure bei ihrer Auswertung durch funcall()
+        oder apply() uebergeben werden.
+
+        Von der Verwendung wird aus Lesbarkeits- und Wartungsgruenden dringend
+        abgeraten.
+
+BEISPIEL
+        // Lambdas werden gern eingesetzt, um komplexere Filter zu schreiben
+        // Allerdings kann jede Lambda dabei auch durch eine Inline-Closure
+        // oder eine LFun-Closure ersetzt werden.
+        filter(users(), 
+          lambda(({'x}),
+                 ({#'==,
+                   ({#'call_other,'x,"QueryProp",P_SECOND}),"gloinson"
+                })));
+
+SIEHE AUCH
+        Verwandt: unbound_lambda(E), apply(E), funcall(E), bind_lambda(E)
+        Generell: closures-abstract(LPC), closures(LPC)
diff --git a/doc/efun/last_bit b/doc/efun/last_bit
new file mode 100644
index 0000000..d0d2471
--- /dev/null
+++ b/doc/efun/last_bit
@@ -0,0 +1,19 @@
+SYNOPSIS
+        int last_bit(string str);
+
+BESCHREIBUNG
+        Liefert die Nummer des letzten gesetzten Bits im Bitstring <str>.
+
+        Jedes Zeichen enthaelt 6 Bits. Also kann in jedem Zeichen ein Wert von
+        0 bis 63 gespeichert werden (2^6=64). Das erste Zeichen ist der
+        Leerschlag " " mit dem Wert 0. Das erste Zeichen im String ist jenes
+        mit den niedrigsten Bits (0-5).
+
+BEISPIEL
+        string s;
+        s = set_bit("", 4); s = set_bit(s, 2);
+        last_bit(s) --> liefert 4
+
+SIEHE AUCH
+        set_bit(E), clear_bit(E), next_bit(E), test_bit(E), count_bits(E),
+        and_bits(E), or_bits(E), xor_bits(E), invert_bits(E), copy_bits(E)
diff --git a/doc/efun/last_instructions b/doc/efun/last_instructions
new file mode 100644
index 0000000..ca447d9
--- /dev/null
+++ b/doc/efun/last_instructions
@@ -0,0 +1,28 @@
+GESCHUETZT
+SYNOPSIS
+        string *last_instructions(int lenght, int verbose);
+
+BESCHREIBUNG
+        Liefert ein Array mit der "Laenge" der zuletzt ausgefuehrten
+        Anweisungen. Wenn <verbose> ungleich 0 ist (standardmaessig so),
+        werden auch Infos zur Zeilennummer angezeigt. Jeder String hat
+        folgende Form:
+
+            Opcode-Adresse: Opcode Operand Mnemonic (Stapeltiefe) Zeilennummer
+
+        Die Information zur Stapeltiefe besteht aus zwei Zahlen <rel>:<abs>,
+        wobei <rel> der relative Stapelverbrauch der Funktion ist, <abs> der
+        absolute Stapelverbrauch.
+
+        Die Information zur Zeilennummer wird angefuegt, wenn das Flag gesetzt
+        ist und eine neue Zeile im Quellcode erreicht wird. Ebenso erzeugen
+        Aufrufe zwischen Objekten einen Eintrag im Resultatarray (allerdings
+        nur, wenn das verbose-Flag gesetzt ist). Dieser Eintrag hat die Form:
+
+            Objektname Programmname Zeilennummer.
+
+        Es gibt ein vorkonfiguriertes oberes Limit, wie viele Instruktionen
+        zurueckverfolgt werden koennen.
+
+SIEHE AUCH
+        debug_message(E)
diff --git a/doc/efun/limited b/doc/efun/limited
new file mode 100644
index 0000000..c967812
--- /dev/null
+++ b/doc/efun/limited
@@ -0,0 +1,64 @@
+VORLAEUFIG, GESCHUETZT
+SYNOPSIS
+        #include <sys/rtlimits.h>
+
+        mixed limited(closure fun);
+        mixed limited(closure fun, int tag, int value, ...);
+        mixed limited(closure fun, int *limits [, mixed *args]);
+
+BESCHREIBUNG
+        limited() ruft die Funktion <fun> mit den bezeichneten Argumenten
+        <args> auf und fuehrt sie mit den gegebenen Laufzeitlimiten aus.
+
+        Beim Ende der Funktion <fun> werden die momentan aktiven
+        Laufzeitlimiten wiederhergestellt. limited() liefert den
+        Rueckgabewert der Closure <fun> zurueck.
+
+        Die Laufzeitlimiten koennen in zwei Formen angegeben werden: als
+        Array (wie es von query_limits() geliefert wird) oder als Liste von
+        Werten mit Tags. Wird limited() ohne Angabe von Limits aufgerufen,
+        gelten die Laufzeitlimiten als 'unlimitiert'.
+
+        Die Limiteneinstellung kennt drei spezielle Werte aus <rtlimits.h>:
+            LIMIT_UNLIMITED:    es gibt kein Limit
+            LIMIT_KEEP:         das zuletzt gesetzte Limit wird beibehalten
+            LIMIT_DEFAULT:      die 'globalen' Limiten werden verwendet
+
+        Fuer LIMIT_COST, die Spezialwerte haben diese Bedeutung:
+            LIMIT_UNLIMITED: die Ausfuehrung kosten lediglich einen Tick
+            LIMIT_KEEP:      LIMIT_COST wird auf 0 gesetzt
+            LIMIT_DEFAULT:   LIMIT_COST wird auf -100 gesetzt
+
+        limited() erzeugt eine Schutzverletzung ("limited", current_object,
+        fun, limits-array).
+
+BEMERKUNGEN:
+        Diese Funktion kann bei uns mudlibweit genutzt werden. Allerdings wird
+        nur die _Reduktion_ von LIMIT_EVAL zugelassen, alle anderen Limits
+        duerfen _nicht_ veraendert werden. Hierbei ist zu beachten, dass das
+        Limit fuer LIMIT_EVAL um min. 1000 Ticks unter den noch verfuegbaren
+        Ticks liegen muss (get_eval_cost()).
+        Fuer LIMIT_COST sind nur 0 und -100 zugelassen.
+
+BEISPIELE
+        limited(#'function)
+        --> fuehrt die Funktion ohne Limiten aus.
+
+        limited(#'function, ({ 200000 }), "foo")
+        --> fuehrt die Funktion mit einem Eval-Kosten Limit von 200000 Ticks
+            aus. Die Funktion wird als 'function("foo")' aufgerufen.
+
+        limited(lambda(0, ({#'function, "foo"})), LIMIT_EVAL, 200000)
+        --> fuehrt die Funktion mit einem Eval-Kosten Limit von 200000 Ticks
+            aus. Die Funktion wird als 'function("foo")' aufgerufen.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.7.
+        LDMud 3.3.563 fuehrte LIMIT_COST ein.
+
+SIEHE AUCH
+        query_limits(E), set_limits(E)
+        get_eval_cost(E)
+
+16.05.2007, Zesstra
+
diff --git a/doc/efun/living b/doc/efun/living
new file mode 100644
index 0000000..6f0a886
--- /dev/null
+++ b/doc/efun/living
@@ -0,0 +1,13 @@
+SYNOPSIS
+        int living(object ob);
+
+BESCHREIBUNG
+        Liefert 1 zurueck, wenn <ob> ein lebendiges Objekt (living) ist.
+        <ob> ist living, wenn enable_commands() aus <obj> aufgerufen wurde.
+        <ob> kann auch 0 sein.
+
+BEISPIEL
+        living(this_player());  -> Dies liefert (hoffentlich) 1 zuerueck.
+
+SIEHE AUCH
+        enable_commands(E)
diff --git a/doc/efun/load_name b/doc/efun/load_name
new file mode 100644
index 0000000..f26c1ea
--- /dev/null
+++ b/doc/efun/load_name
@@ -0,0 +1,47 @@
+SYNOPSIS
+        string load_name()
+        string load_name(object|string obj)
+
+BESCHREIBUNG
+        Die Funktion liefert den Namen, mit dem <obj> geladen wurde. <obj>
+        kann direkt als Objekt oder als String mit seinem Namen angegeben
+        werden.
+
+        Wenn <obj> ein Clon ist, liefert die Funktion den Namen des Blueprints.
+        Wenn <obj> ein Blueprint ist, liefert die Funktion den Namen des Files,
+        aus dem der Blueprint kompiliert wurde.
+
+        Wenn <obj> ueber seinen Namen angegeben wurde, aber nicht / nicht mehr
+        existiert, generiert die Funktion den Namen, wie er sein muesste und
+        gibt diesen zurueck. Wenn der angegebene Name ungueltig ist, liefert
+        die Funktion 0.
+
+        Als Spezialfall liefert die Funktion 0, wenn <ob> 0 ist.
+
+        Fuer virtuelle Objekte liefert load_name() den originalen Ladenamen
+        des Objekts, welches der virtuelle Compiler erzeugte.
+
+        Wird <obj> nicht angegeben, wird der Name fuer das momentan gueltige
+        Objekt angegeben.
+
+        Im Gegensatz zum object_name() kann der load_name() nicht durch
+        rename_object() oder einen VC veraendert werden. Ist ein <obj> jedoch
+        einem replace_program() unterworfen, spiegelt der load_name() nicht
+        mehr das effektive Verhalten des Objekts wider.
+
+BEISPIELE
+        object o;
+        o = clone_object("/std/thing");
+        write(load_name(o));  --> liefert "/std/thing" in !Compat Modus
+                                  und "std/thing"  im Compat Modus
+        write(load_name("/std/thing"));  --> gleich wie oben
+        write(load_name("/std/thing#4n5")); --> liefert 0
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.6.
+        Strings als Argumente sind moeglich seit 3.2.8.
+        0 ist zulaessig seit 3.2.9.
+
+SIEHE AUCH
+        clone_object(E), clonep(E), object_name(E), load_object(E),
+        replace_program(E), program_name(E), present_clone(E)
diff --git a/doc/efun/load_object b/doc/efun/load_object
new file mode 100644
index 0000000..847d0b8
--- /dev/null
+++ b/doc/efun/load_object
@@ -0,0 +1,23 @@
+SYNOPSIS
+        object load_object(string name)
+
+DESCRIPTION
+        Load the object from the file <name> and return it. If the
+        object already exists, just return it.
+
+        This efun can be used only to load blueprints - for clones, use
+        the efun clone_object().
+
+        If strict euids are enforced, the cloning object must have
+        a non-zero euid.
+
+EXAMPLE
+        // Update and reload the standard player object
+        destruct(find_object("/std/player"));
+        load_object("/std/player");
+
+HISTORY
+        Introduced in LDMud 3.2.6.
+
+SEE ALSO
+        clone_object(E)
diff --git a/doc/efun/localtime b/doc/efun/localtime
new file mode 100644
index 0000000..a522dc4
--- /dev/null
+++ b/doc/efun/localtime
@@ -0,0 +1,39 @@
+SYNOPSIS
+        #include <sys/time.h>
+
+        int *localtime(int clock);
+        int *localtime(int *uclock);
+
+BESCHREIBUNG
+        Interpretiert das Argument <clock> als Anzahl Sekunden seit dem
+        01. Januar 1970, 00:00:00, und gibt die Zeit in Lokalzeit in einer
+        sauberen Struktur zurueck. Wird <clock> nicht angegeben, wird
+        standardmaessig time() verwendet.
+
+        Alternativ kann auch ein Array von zwei Zahlen als Argument angegeben
+        werden. Das erste Element wird interpretiert wie <clock>, das zweite
+        Argument enthaelt die vergangenen Mikrosekunden in dieser Sekunde und
+        wird ignoriert.
+
+        Das Resultat ist ein Array mit folgenden Elementen:
+
+            int TM_SEC    (0):  Sekunde in der Minute (0..59)
+            int TM_MIN    (1):  Minute in der Stunde (0..59)
+            int TM_HOUR   (2):  Stunde des Tages (0..23)
+            int TM_MDAY   (3):  Tag im Monat (1..31)
+            int TM_MON    (4):  Monat des Jahres (0..11)
+            int TM_YEAR   (5):  Jahr (z.B. 2001)
+            int TM_WDAY   (6):  Wochentag (0..6, Sonntag = 0)
+            int TM_YDAY   (7):  Tag im Jahr (0..365)
+            inz TM_ISDST  (8):  TRUE: Daylight Saving Time
+
+BEISPIEL
+        printf("Today is %s\n", ({ "Sonntag", "Montag", "Dienstag",
+            "Mittwoch", "Donnerstag", "Freitag", "Samstag"})
+            [localtime()[TM_WDAY]]);
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9
+
+SIEHE AUCH
+    ctime(E), gmtime(E), time(E), utime(E)
diff --git a/doc/efun/log b/doc/efun/log
new file mode 100644
index 0000000..1b2abff
--- /dev/null
+++ b/doc/efun/log
@@ -0,0 +1,8 @@
+SYNOPSIS
+        float log(int|float arg);
+
+BESCHREIBUNG
+        Liefert den natuerlichen Logarithmus von <arg>.
+
+SIEHE AUCH
+        exp(E), pow(E)
diff --git a/doc/efun/lower_case b/doc/efun/lower_case
new file mode 100644
index 0000000..9701e20
--- /dev/null
+++ b/doc/efun/lower_case
@@ -0,0 +1,12 @@
+SYNOPSIS
+        string lower_case(string str);
+
+BESCHREIBUNG
+        Konvertiert alle Zeichen in <str> in Kleinbuchstaben und liefert den
+        neuen String.
+
+BEISPIEL
+        lower_case("Hallo WeLT!") -> "hallo welt!"
+
+SIEHE AUCH
+        capitalize(E), upper_case(E)
diff --git a/doc/efun/m_add b/doc/efun/m_add
new file mode 100644
index 0000000..624f4b9
--- /dev/null
+++ b/doc/efun/m_add
@@ -0,0 +1,29 @@
+SYNOPSIS
+        mapping m_add(mapping map, mixed key, [mixed data, ...]);
+
+BESCHREIBUNG
+        Fuegt einen neuen Eintrag mit Index <key> zum Mapping <map> hinzu
+        oder ersetzt diesen. Das veraenderte Mapping wird als Ergebnis
+        zurueck geliefert. Werte, die nicht zugeordnet werden koennen, werden
+        als 0 interpretiert, irrelevante Argumente werden ignoriert.
+
+BEISPIELE
+        mapping m;
+        m = ([ "foo" ]);
+        m_add(m, "bar", 1) --> ([ "foo", "bar" ])
+
+        m = ([ "foo":1 ]);
+        m_add(m, "bar", 1) --> ([ "foo":1, "bar":1 ])
+
+        m = ([ "foo":1;2 ]);
+        m_add(m, "bar", 1) --> ([ "foo":1;2, "bar":1;0 ])
+
+        apply(#'m_add, m, "baz", ({ 4, 5 }))
+        --> ([ "foo":1;2, "bar":1;0, "baz":4;5 ])
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9.
+
+SIEHE AUCH
+        mappingp(E), mkmapping(E), m_delete(E), m_entry(E), m_indices(E),
+        m_values(E), sizeof(E), widthof(E)
diff --git a/doc/efun/m_allocate b/doc/efun/m_allocate
new file mode 100644
index 0000000..255f3b9
--- /dev/null
+++ b/doc/efun/m_allocate
@@ -0,0 +1,46 @@
+SYNOPSIS
+        mapping m_allocate(int size)
+        mapping m_allocate(int size, int width)
+
+BESCHREIBUNG
+        Die Funktion reserviert Speicher fuer ein Mapping. <size> ist die
+        Anzahl Eintraege (d.h. die Anzahl Keys), <width> ist die Anzahl
+        Dateneintraege pro Key. Wird <width> nicht angegeben, werden Keys
+        mit einem Datenelement erzeugt.
+
+        Die Funktion ist nur sinnvoll, wenn man ein Mapping erzeugt, dessen
+        ungefaehre Groesse von vornherein bekannt ist, um so den Overhead
+        von wiederholten Speicherallokation zu minimieren. Wenn nicht alle
+        allozierten Datenelemente mit Daten bestueckt werden, werden die
+        Ueberbleibsel einige Zeit spaeter bei Gelegenheit wieder freigegeben
+        (s. Bemerkungen).
+        m_allocate() ist auch nuetzlich, wenn ein Mapping bestimmter Breite
+        erzeugt werden soll, ohne bereits die Daten zu den Keys bereit zu
+        stellen.
+
+        Wenn bloss ein leeres Mapping bestimmter Breite erzeugt werden soll,
+        so kann folgende Notation verwendet werden:
+
+        ([ ]) : erzeugt ein leeres Mapping mit Breite 1.
+        ([:<width>]) : erzeugt ein leeres Mapping der Breite <width>,
+            wobei <width> eine beliebige Anweisung sein kann, die eine
+            Integerzahl zurueck liefert. Tatsaechlich wird diese Notation
+            als 'm_allocate(0, <width>)' kompiliert.
+
+BEISPIELE
+        m_allocate(3,7) -> erzeugt ein Mapping mit 7 Werten pro Key und Platz
+           fuer 3 Eintraege.
+       ([:2*3]) -> entspricht m_allocate(0,6).
+
+BEMERKUNGEN
+        Ungenutzer Speicher des allozierten Mappins wird waehrend des sog.
+        Kompaktierens des Mappings freigegeben. Dies passiert waehrend eines
+        "data cleanups" oder einer "garbage collection". Die Zeit zwischen
+        "data cleanups" ist mit configure_driver() konfigurierbar.
+
+GESCHICHTE
+        Umbenannt von allocate_mapping() in LDMud 3.2.6.
+        Die ([:<width>]) Notation wurde in 3.2.9 eingefuehrt.
+
+SIEHE AUCH
+        mappings(LPC), walk_mapping(E), get_type_info(E), m_reallocate(E)
diff --git a/doc/efun/m_contains b/doc/efun/m_contains
new file mode 100644
index 0000000..e941ff3
--- /dev/null
+++ b/doc/efun/m_contains
@@ -0,0 +1,18 @@
+SYNOPSIS
+        int m_contains(mixed &data1, ... , &dataN, mapping map, mixed key);
+
+BESCHREIBUNG
+        Wenn <map> den Key <key> enthaelt, werden die entsprechenden Werte
+        den Datenargumenten von <key> zugeordnet, welche per Referenz
+        uebergeben werden muessen. m_contains liefert in diesem Fall 1 zurueck.
+        Wenn <key> nicht in <map> enthalten ist, liefert die Funktion 0
+        zurueck und die Datenargumente bleiben unveraendert.
+
+        Man kann diese Funktion auch fuer 0-Wert Mappings verwenden, wobei
+        sie den gleichen Effekt wie member(E) hat.
+
+AENDERUNGEN
+        Umbenannt von 'mapping_contains' in LDMud 3.2.6.
+
+SIEHE AUCH
+        m_entry(E), mappings(LPC), member(E)
diff --git a/doc/efun/m_delete b/doc/efun/m_delete
new file mode 100644
index 0000000..bb9d453
--- /dev/null
+++ b/doc/efun/m_delete
@@ -0,0 +1,11 @@
+SYNOPSIS
+        mapping m_delete(mapping map, mixed key)
+
+BESCHREIBUNG
+        Loescht den Eintrag mit dem Index <key> aus dem Mapping <map> und
+        liefert das veraenderte Mapping zurueck. Wenn <map> keinen Eintag
+        mit dem Index <key> enthaelt, wird nichts veraendert.
+
+SIEHE AUCH
+        mappingp(E), mkmapping(E), m_add(E), m_indices(E), m_values(E),
+        sizeof(E), widthof(E)
diff --git a/doc/efun/m_entry b/doc/efun/m_entry
new file mode 100644
index 0000000..20ea2af
--- /dev/null
+++ b/doc/efun/m_entry
@@ -0,0 +1,24 @@
+SYNOPSIS
+        *mixed m_entry(mapping map, mixed key);
+
+BESCHREIBUNG
+        Durchsucht das Mapping <map> nach dem Eintrag mit Index <key> und
+        liefert alle Werte von <key> in einem Array zurueck.
+
+        Wenn <map> keinen Eintrag mit Index <key> enthaelt, liefert
+        m_entry() 0.
+
+BEISPIEL
+        mapping m = ([1:"bla":-1, 2:"fasel":-2 ])
+        m_entry(m, 0) -> 0
+        m_entry(m, 1) -> ({"bla", -1})
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.10.
+
+ANMEKRUNG
+        Mit der Efun m_add() koennen alle Werte eines Eintrages auf einmal
+        addiert werden.
+
+SIEHE AUCH
+        m_add(E), m_contains(E), mappings(LPC), member(E)
diff --git a/doc/efun/m_indices b/doc/efun/m_indices
new file mode 100644
index 0000000..4d84c4e
--- /dev/null
+++ b/doc/efun/m_indices
@@ -0,0 +1,9 @@
+SYNOPSIS:
+	mixed *m_indices(mapping map)
+
+DESCRIPTION:
+	Return an array containing the indices of mapping 'map'.
+
+SEE ALSO:
+	mappingp(E), mkmapping(E), m_values(E), m_delete(E),
+	sizeof(E), widthof(E)
diff --git a/doc/efun/m_reallocate b/doc/efun/m_reallocate
new file mode 100644
index 0000000..627bc97
--- /dev/null
+++ b/doc/efun/m_reallocate
@@ -0,0 +1,23 @@
+SYNOPSIS
+        mapping m_reallocate(mapping m, int width);
+
+BESCHREIBUNG
+        Erzeugt ein neues Mapping mit <width> Werten pro Key und fuellt das
+        Mapping mit den Werten aus <m>. Wenn <m> weniger als <width> Werte
+        pro Key hat, werden im neuen Mapping die restlichen Werte auf 0
+        gesetzt. Wenn <m> mehr als <width> Werte pro Key hat, werden die
+        ueberzaehligen Werte ignoriert.
+
+        Das urspruengliche Mapping <m> wird nicht veraendert.
+
+BEISPIEL
+        mapping m = ([ "foo":1;2;3, "bar":4;5;6 ])
+
+        m_reallocate(m, 1) --> liefert ([ "foo":1,       "bar:4 ])
+        m_reallocate(m, 4) --> liefert ([ "foo":1;2;3;0, "bar:4;5;6;0 ])
+
+AENDERUNGEN
+    Eingefuehrt in LDMud 3.2.6, auf Vorschlag von TubMud.
+
+SIEHE AUCH
+        m_allocate(E), m_values(E), widthof(E)
diff --git a/doc/efun/m_values b/doc/efun/m_values
new file mode 100644
index 0000000..4b94154
--- /dev/null
+++ b/doc/efun/m_values
@@ -0,0 +1,14 @@
+SYNOPSIS:
+        mixed *m_values(mapping map)
+        mixed *m_values(mapping map, int index)
+
+DESCRIPTION:
+       If index is 0 or not given, return an array with the first values of 
+       mapping 'map'.
+       For values of index >0, return an array with the corresponding higher
+       values of mapping 'map', i.e. m_values(map,2) returns the third 
+       values.
+
+SEE ALSO:
+       mappingp(E), mkmapping(E), m_indices(E), m_delete(E),
+       sizeof(E), widthof(E), mappings(LPC)
diff --git a/doc/efun/make_shared_string b/doc/efun/make_shared_string
new file mode 100644
index 0000000..3bb6732
--- /dev/null
+++ b/doc/efun/make_shared_string
@@ -0,0 +1,24 @@
+VERALTET
+SYNOPSIS
+        string make_shared_string (string str);
+
+BESCHREIBUNG
+        Fuegt <str> in die Tabelle der gemeinsam verwendeten String des Spiels
+        ein.
+
+        Wenn ein String von mehreren Variablen / Objekten verwendet wird,
+        spart dies Speicher. Keys von Alists und Mappings sind immer gemeinsam
+        verwendete Strings.
+
+        In LDMud 3.3 ist diese Funktion nicht laenger nuetzlich: Strings
+        werden sowieso so weit wie moeglich gemeinsam verwendet, und der
+        Driver wandelt untablierte Strings nach einiger Zeit automatisch in
+        tablierte Strings um um
+
+BUGS
+        Ein besseres Stringhandling im Driver sollte diese Efun ueberfluessig
+        machen.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.6, auf Vorschlag von TubMud.
+        Veraltet seit LDMud 3.3 .
diff --git a/doc/efun/map b/doc/efun/map
new file mode 100644
index 0000000..808f065
--- /dev/null
+++ b/doc/efun/map
@@ -0,0 +1,93 @@
+SYNOPSIS
+        mixed * map(mixed *arg, string func, string|object ob, mixed extra...)
+        mixed * map(mixed *arg, closure cl, mixed extra...)
+        mixed * map(mixed *arg, mapping m [, int idx])
+
+        mixed * map(struct arg, string func, string|object ob, mixed extra...)
+        mixed * map(struct arg, closure cl, mixed extra...)
+        mixed * map(struct arg, mapping m [, int idx])
+
+        mapping map(mapping arg, string func, string|object ob, mixed extra...)
+        mapping map(mapping arg, closure cl, mixed extra...)
+
+        string map(string arg, string func, string|object ob, mixed extra...)
+        string map(string arg, closure cl, mixed extra...)
+        string map(string arg, mapping m [, int idx])
+
+BESCHREIBUNG
+        Ruft die Funktion <ob>-><func>() bzw. die Closure <cl> fuer jedes
+        Element des Strings, Arrays, Mappings oder der Struktur <arg> auf
+        und liefert ein Resultat, das aus den verschiedenen Rueckgabewerten
+        erstellt wurde.
+
+        Wurde <ob> nicht angegeben, oder ist es weder ein String noch ein
+        Objekt, wird stattdessen this_object() verwendet.
+
+        Ist <arg> ein Array, ein String oder eine Struktur, wird die Funktion
+        mit jedem Element des Arrays als erstem Parameter aufgerufen, gefolgt
+        von den <extra> Argumenten. Das Resultat der Efun ist ein Array, das
+        die Rueckgabewerte der Funktionsaufrufe enthaelt. Man koennte die
+        Operation map() deshalb umschreiben als:
+
+            foreach(index) result[index] = ob->func(arg[index], extra...)
+
+        Ist <arg> ein Array, ein String oder eine Struktur, und wurde statt
+        einer Funktion ein Mapping angegeben, so liefert map() ein Array mit
+        den Werten, die im Mapping an Stelle der urspruenglichen Werte stehen,
+        bzw. mit den Originalwerten, wenn fuer sie kein Mappingeintrag
+        existiert. Ist <idx> angegeben, so wird die entsprechende Spalte des
+        Mappings verwendet. Mit anderen Worten:
+
+            foreach(index)
+                if (arg[index] ist ein Key in <arg>)
+                    result[index] = map[arg[index]] oder map[arg[index]]
+                else
+                    result[index] = arg[index]
+
+        Ist <arg> ein Mapping, wird die Funktion fuer jeden Key des Mappings
+        als erstem Parameter und den Datenwerten (sofern vorhanden) als
+        zusaetzliche Argumente aufgerufen, gefolgt von den <extra> Argumenten.
+        Die <extra> Argumente duerfen keine geschuetzten Referenzen enthalten
+        (wie z.B. &(i[0])). Das Ergebnis der Efun ist ein Mapping, das die
+        Resultate der Funktionsaufrufe als Zuordnung zum jeweiligen Key
+        enthaelt.
+
+        Abhaengig von der Breite des Mappings <arg>, kann die Operation
+        umschrieben werden als:
+
+            foreach (key in arg)
+                switch (widthof(arg))
+                case 0:
+                    result[key] = ob->func(key, 0, extra...)
+                case 1:
+                    result[key] = ob->func(key, arg[key], extra...)
+                else  :
+                    result[key] = ob->func( key
+                                        , ({ arg[key,0] ...arg[key,width-1] })
+                                        , extra...)
+
+        Der Vorteil dieses Ansatzes ist, dass beide Arten von mehrdimensionalen
+        Mappings (Mappings mit mehreren Werten pro Key und Mappings von Arrays)
+        gleich behandelt werden koennen.
+
+BEISPIELE
+        arr = ({ 1, 2, 3, 4 });
+        m = ([ 1:-1, 3:-3 ]);
+
+        map(arr, #'%, 2)  --> liefert ({ 1, 0, 1, 0 })
+        map(arr, m)       --> liefert ([ -1, 2, -3, 4 })
+
+ANMERKUNGEN
+        map() auf Arrays angewandt verhaelt sich wie map_array(), auf Mappings
+        angewandt hingegen verhaelt es sich wie eine Verallgemeinerung von
+        map_indices().
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.2.6, loest map_array() ab.
+        LDMud 3.2.8 fuehrt neu die Moeglichkeit ein, ein Array durch ein
+        Mapping zu mappen.
+        LDMud 3.3.439 fuehrt map() fuer Strings ein.
+        LDMud 3.3.719 fuehrt den <idx>-Parameter fuer mappen mit Mappings ein.
+
+SIEHE AUCH
+        filter(E), filter_indices(E), map_indices(E), map_objects(E)
diff --git a/doc/efun/map_indices b/doc/efun/map_indices
new file mode 100644
index 0000000..de3b15f
--- /dev/null
+++ b/doc/efun/map_indices
@@ -0,0 +1,75 @@
+map_indices(E)
+
+FUNKTION:
+     mapping map_indices(mapping m, string fun, object ob [, mixed extra])
+     mapping map_indices(mapping m, closure cl [, mixed extra])
+
+PARAMETER:
+     arr	- zu mappendes Array
+     fun/cl	- zu rufende Methode/Closure
+     ob		- Objekt/Dateiname, an dem Methode gerufen werden soll
+     extra	- weitere Parameter fuer Methode/Closure
+
+BESCHREIBUNG:
+     Mapped die Elemente (jeweils Schluessel) aus 'map' durch die Methode
+     'fun' oder die Closure 'cl' in ein neues Mapping.
+     Fuer jedes Element aus 'm' wird 'fun' oder 'cl' mit dem Schluessel als
+     erstem Parameter [und folgend den optionalen Extra-Parametern] gerufen.
+
+     Der Rueckgabewert der Methode/Closure wird in fuer den Schluessel als
+     Datenwert in das neue Mapping eingetragen.
+
+     ACHTUNG: Alle anderen Daten bei Mapping mit Breite>1 verfallen!
+
+     Verwendung von Methoden:
+	Wenn bei der Angabe von 'fun' kein Objekt 'ob' in Form eines Strings
+	oder Objekts angegeben wird, wird this_object() angenommen.
+
+     Verwendung von Closures:
+	Es koennen sowohl Lfun-Closures als auch Lambda-Closures verwendet
+	werden. Lfun-Closures koennen, wenn im selben Objekt vorhanden auch
+	'private' oder/und 'static' deklariert sein, muessen aber zu dem
+	Zeitpunkt der Verwendung bekannt sein (Funktionsprototypen benutzen).
+
+RUeCKGABEWERT:
+     Mapping mit Schluessel:Rueckgabewerten der Methode/Closure.
+
+BEISPIELE:
+     // ersetze in einem Mapping die Datenwerte durch das Doppelte,
+     // nutze dabei die Datenwerte des Altmappings durch Uebergabe als
+     // extra-Parameter
+
+     // Anmerkung: Das geht mit walk_mapping() eleganter!
+
+     int do_double(string key, mapping m, int mult) {
+      return m[key]*mult;
+     }
+
+     mapping x, y;
+     x=(["a":2, "b":3]);
+     y=map_indices((["a":2, "b":3]), #'do_double, &x, 3);
+
+     y == (["a":6,"b":9])
+
+AeQUIVALENZCODE (nicht empfohlen, nur zum Verstaendnis!):
+     int i;
+     mapping ret; mapping input;
+     mixed *index;
+
+     ret=m_allocate(0, 1);
+     index=m_indices(input);
+     i=sizeof(index);
+     while(i--)
+       ret[index[i]]=ob->fun(index[i] [, extra1, extra2, ...]))
+       // ret[index[i]]=funcall(cl, index[i] [, extra1, extra2, ...]);
+
+SIEHE AUCH:
+     Arrays:		filter(E), map(E)
+     Objektarrays:	filter_objects(E), map_objects(E)
+     Mappings:		filter_indices(E)
+
+     Sonstiges:		walk_mapping(E), m_contains(E)
+			member()
+			m_indices(E), m_values(E)
+
+29.10.2006 Zesstra
diff --git a/doc/efun/map_objects b/doc/efun/map_objects
new file mode 100644
index 0000000..4543a2e
--- /dev/null
+++ b/doc/efun/map_objects
@@ -0,0 +1,51 @@
+map_objects(E)
+
+FUNKTION:
+     object *map_objects(object *arr, string fun [, mixed extra])
+
+PARAMETER:
+     arr	- zu mappendes Array von Objekten/Objektpfaden
+     fun	- an Objekten zu rufende Methode
+     extra	- weitere Parameter fuer Methode
+
+BESCHREIBUNG:
+     Mapped die Elemente aus 'arr' durch den Aufruf der Methode 'fun' an
+     jedem der Elemente von 'arr' in ein neues Array.
+     0-Eintraege werden ignoriert.
+
+     Der Rueckgabewert von
+	arr[n]->fun([extra1, extra2, ...])
+     wird an der Indexposition des Elementes in das neue Array eingetragen.
+
+RUeCKGABEWERT:
+     Array mit Resultaten der Funktionsaufrufe am jeweiligen Objekt.
+
+BEMERKUNGEN:
+     Werden Pfade angegeben, so wird versucht ein Objekt zu laden, falls
+     dieses nicht existiert.
+
+BEISPIEL:
+     // ersetze alle Objekte durch ihre Namen
+     arr=map_objects(inputarr, "name");
+
+     // ersetze alle Objekte durch ihre Namen im Genitiv
+     arr=map_objects(inputarr, "name", WESSEN);
+
+AeQUIVALENZCODE (nicht empfohlen, nur zum Verstaendnis!):
+     int i;
+     object *ret; mixed *input;
+
+     i=sizeof(input);
+     ret=allocate(i);
+     while(i--)
+       ret[i]=input[i]->fun([extra1, extra2, ...]);
+
+SIEHE AUCH:
+     Arrays:		filter(E), map(E)
+     Objektarrays:	filter_objects(E)
+     Mappings:		filter_indices(E), map_indices(E)
+
+     Sonstiges:		sort_array(E), unique_array()
+			alist, transpose_array(E)
+
+20.Jan 2005 Gloinson
diff --git a/doc/efun/mappingp b/doc/efun/mappingp
new file mode 100644
index 0000000..fef2ac7
--- /dev/null
+++ b/doc/efun/mappingp
@@ -0,0 +1,9 @@
+SYNOPSIS:
+	int mappingp(mixed arg)
+
+DESCRIPTION:
+	Return 1 if the argument is a mapping, or 0 if it is not. 
+
+SEE ALSO:
+	intp(E), stringp(E), objectp(E), pointerp(E), mkmapping(E),
+	m_indices(E), m_values(E), m_delete(E), sizeof(E)
diff --git a/doc/efun/master b/doc/efun/master
new file mode 100644
index 0000000..cdf55b6
--- /dev/null
+++ b/doc/efun/master
@@ -0,0 +1,17 @@
+SYNOPSIS
+        object master();
+        object master(int dont_load);
+
+BESCHREIBUNG
+        Die Funktion liefert das Masterobjekt.
+
+        Wenn <dont_load> nicht wahr ist, stellt master() zuerst sicher, dass
+        das Masterobjekt auf existiert. Wenn <dont_load> wahr ist, liefert
+        master() nur das Masterobjekt oder 0, falls das aktuelle Masterobjekt
+        zerstoert wurde.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.10
+
+SIEHE AUCH
+        master(M)
diff --git a/doc/efun/match_command b/doc/efun/match_command
new file mode 100644
index 0000000..cd495d7
--- /dev/null
+++ b/doc/efun/match_command
@@ -0,0 +1,27 @@
+SYNOPSIS
+        #include <sys/commands.h>
+
+        mixed * match_command (string command, object origin)
+
+DESCRIPTION
+        Take the command <command>, parse it, and return an array of all
+        matching actions added to <origin> (read: <origin> is the object
+        'issuing' the command).
+
+        Each entry in the result array is itself an array of:
+
+          string [CMDM_VERB]:   The matched verb.
+          string [CMDM_ARG]:    The argument string remaining, or 0 if none.
+          object [CMDM_OBJECT]: The object defining the action.
+          string [CMDM_FUN]:    The name of the function to call in
+                                CMDM_OBJECT, which may be static.
+
+        The efun is useful for both debugging, and for implementing your
+        own H_COMMAND handling.
+
+HISTORY
+        Introduced in LDMud 3.3.259.
+
+SEE ALSO
+        hooks(C), execute_command(E), command(E), notify_fail(E),
+        command_stack(E)
diff --git a/doc/efun/max b/doc/efun/max
new file mode 100644
index 0000000..189f4a5
--- /dev/null
+++ b/doc/efun/max
@@ -0,0 +1,23 @@
+SYNOPSIS
+        string max(string arg, ...);
+        string max(string *arg);
+
+        int|float max(int|float arg, ...);
+        int|float max(int|float *arg);
+
+BESCHREIBUNG
+        Die Funktion liefert den groessten Wert aller <arg> und liefert ihn
+        zurueck. Wird max() nur mit einem Array aufgerufen wird (das nicht
+        leer sein darf), liefert die Funktion das groesste Element aus <arg>.
+
+BEISPIEL
+        max(1)                     - liefert 1
+        max(1, 1.1)                - liefert 1.1
+        max("foo", "bar")          - liefert "foo"
+        max( ({ "foo", "bar" }) )  - liefert "foo"
+
+AENDERUNGEN
+        Eingefuehrt in LDMued 3.2.9.
+
+SIEHE AUCH
+        min(E)
diff --git a/doc/efun/md5_crypt b/doc/efun/md5_crypt
new file mode 100644
index 0000000..d91ba18
--- /dev/null
+++ b/doc/efun/md5_crypt
@@ -0,0 +1,20 @@
+SYNOPSIS
+        string md5_crypt(string str, int seed)
+        string md5_crypt(string str, string seed)
+
+BESCHREIBUNG
+        Verschluesselt den String <str> mit dem Schluessel <seed>.
+        <seed> kann entweder ein Integer sein oder zwei Zeichen aus
+        dem String <seed>.  Wenn <seed> 0 ist, wird ein zufaelliger
+        Schluessel erzeugt.
+
+        Das Resultat enthaelt den Schluessel als die ersten beiden Zeichen.
+
+        Die Efun verwendet den MD5-Algorithmus, und das Resultat ist
+        kompatible mit der Passwordverschluesselung des Apache Webservers.
+
+        Fuer Passwortabfragen, die ohne Echo eingegeben werden koennen sollen,
+        bietet input_to() ein spezielles Flag.
+
+SIEHE AUCH
+        crypt(E), md5(E), sha1(E), input_to(E)
diff --git a/doc/efun/member b/doc/efun/member
new file mode 100644
index 0000000..9f0e66a
--- /dev/null
+++ b/doc/efun/member
@@ -0,0 +1,24 @@
+SYNOPSIS
+        int member(mixed *array, mixed elem [, int start]);
+        int member(string s, int elem [, int start]);
+        int member(mapping map, mixed key);
+
+BESCHREIBUNG
+        Fuer Arrays und String liefert member() den Index des ersten
+        Auftretens von <elem> in <arg>. Ist <elem> nicht in <arg> enthalten,
+        wird -1 zurueck gegeben.
+
+        Ist <start> als Zahl >= 0 gegeben, beginnt die Suche ab der
+        angegebenen Position. Eine Startposition groesser als die
+        Laenge des Strings/Arrays liefert stets das Resultat -1.
+
+        Fuer Mapping prueft member(), ob <key> in <map> enthalten ist und
+        liefert 1 zurueck falls ja, 0 sonst.
+
+BEISPIELE
+        member( ({ "abc", "defg" }), "defg" ) = 1
+        member( ({ "abc", "defg" }), "x" ) = -1
+        member( "abcdefg", 100 ) = member( "abcdefg", 'd' ) = 3
+
+SIEHE AUCH
+        rmember(E), mappings(LPC)
\ No newline at end of file
diff --git a/doc/efun/min b/doc/efun/min
new file mode 100644
index 0000000..c11f9b5
--- /dev/null
+++ b/doc/efun/min
@@ -0,0 +1,22 @@
+SYNOPSIS
+        string min(string arg, ... );
+        string min(string *arg_array);
+        int|float min(int|float arg, ...);
+        int|flaot min(int|float *arg_array);
+
+BESCHREIBUNG
+        Liefert das kleinste der Argumente <arg>. Wenn min() fuer ein
+        einzelnes (nicht leeres) Array aufgerufen wird, liefert min() das
+        kleinste Element im Array.
+
+BEISPIEL
+        min(1)                      -> liefert 1
+        min(1, -1.1)                -> liefert -1.1
+        min("bla", "fasel")         -> liefert "bla"
+        min( ({ "bla", "fasel" }) ) -> liefert "bla"
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9.
+
+SIEHE AUCH
+        max(E)
diff --git a/doc/efun/mkdir b/doc/efun/mkdir
new file mode 100644
index 0000000..8c7a683
--- /dev/null
+++ b/doc/efun/mkdir
@@ -0,0 +1,9 @@
+SYNOPSIS
+        int mkdir(string path);
+
+BESCHREIBUNG
+        Erstellt ein Verzeichnis <path> und liefert 1, wenn das Verzeichnis
+        erstellt werden konnte, sonst 0.
+
+SIEHE AUCH
+        rmdir(E), rm(E), get_dir(E), file_size(E)
diff --git a/doc/efun/mkmapping b/doc/efun/mkmapping
new file mode 100644
index 0000000..75cf827
--- /dev/null
+++ b/doc/efun/mkmapping
@@ -0,0 +1,37 @@
+SYNOPSIS
+        mapping mkmapping(mixed *arr1, mixed *arr2, mixed *arr3, ...);
+        mapping mkmapping(struct st);
+
+BESCHREIBUNG
+        Liefert ein Mapping mit Keys aus <arr1> und Datenelementen aus
+        <arr2>, <arr3>.... Dem Key <arr1[0]> werden die Datenelemente
+        <arr2[0]>, <arr3[0]>... zugeordnet. Wenn die Datenarrays
+        ungleich gross sind, enthaelt das Mapping nur so viele Eintraege,
+        wie im kleinsten Datenarray enthalten sind.
+
+        Die zweite Form konvertiert die angegebene struct <st> in ein Mapping.
+        Hierbei werden die Namen des jeweiligen Elementes in der struct als
+        als Schluessel.
+
+        Gewoehnlich werden Mappings erweitert, indem einfach neue Elemente
+        eingefuegt werden. Diese Funktion ist nuetzlich, wenn der
+        ungefaehr benoetigte Speicherplatz bereits vorher bekannt ist,
+        um so den Overhead bei der Speicherallokation zu minimieren.
+        Allenfalls zu viel allozierter Speicher wird freigegeben, sobald
+        die Funktion, die die mkmapping() Anweisung enthaelt, beendet ist.
+
+BESIPIEL
+        mkmapping( ({ 1, 2 }), ({ 10, 11 }), ({ 20, 21, 22}));
+          liefert ([ 1:10;20, 2:11;21 ])
+
+        struct s { int a, b, c; };
+        mkmapping( (<s> 1, ({ 2, 3 }), 3 )
+          liefert ([ "a":1, "b":({2,3}), "c":3 ])
+
+AeNDERUNGEN
+        LDMud 3.3.433 ermoeglichte die Konversion von structs.
+
+VERGLEICHE
+        mappings(LPC), mappingp(E), m_indices(E), m_values(E),
+        m_add(E), m_delete(E), sizeof(E), widthof(E), unmkmapping(E),
+        to_struct(E)
diff --git a/doc/efun/mktime b/doc/efun/mktime
new file mode 100644
index 0000000..b71227b
--- /dev/null
+++ b/doc/efun/mktime
@@ -0,0 +1,38 @@
+SYNOPSIS
+        #include <sys/time.h>
+
+        int mktime(int *ts);
+
+BESCHREIBUNG
+        Ist das Argument <ts> ein Array mit 9 Elementen (int), entsprechend
+        des Rueckgabewertes von local_time()/gm_time(), liefert die Funktion
+        die Anzahl Sekunden seit dem 01. Januar 1970, 00:00:00 zurueck.
+        Dies ist von Nutzen, wenn man ein Datum/Uhrzeit hat, diese aber als
+        Ganzzahl-Wert speichern will oder eine Zeitdifferenz zwischen zwei
+        Daten ausrechnen will.
+
+        Das Array muss dabei so aufgebaut sein:
+            int TM_SEC    (0):  Sekunde in der Minute (0..59)
+            int TM_MIN    (1):  Minute in der Stunde (0..59)
+            int TM_HOUR   (2):  Stunde des Tages (0..23)
+            int TM_MDAY   (3):  Tag im Monat (1..31)
+            int TM_MON    (4):  Monat des Jahres (0..11)
+            int TM_YEAR   (5):  Jahr (z.B. 2001)
+            int TM_WDAY   (6):  Wochentag (0..6, Sonntag = 0)
+            int TM_YDAY   (7):  Tag im Jahr (0..365)
+            inz TM_ISDST  (8):  TRUE: Daylight Saving Time
+
+      TM_YDAY und TM_WDAY werden ignoriert und koennen beliebige Zahlen
+      enthalten.
+
+BEISPIEL
+      Man hat ein Datum/Uhrzeit (z.B. Benutzereingabe), welches als
+      Unix-Zeitstmepel gespeichert werden soll:
+      // "Mit, 24. Okt 2007, 10:48:00" entspricht folgendem Zeitstempel:
+      int unixzeit = mktime( ({0, 48, 09, 24, 09, 2007, 0, 01, 0}) );
+      
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.3.71x
+
+SIEHE AUCH
+    ctime(E), gmtime(E), local_time(E), time(E), utime(E)
diff --git a/doc/efun/modify_command b/doc/efun/modify_command
new file mode 100644
index 0000000..eea6070
--- /dev/null
+++ b/doc/efun/modify_command
@@ -0,0 +1,21 @@
+SYNOPSIS
+	int|string modify_command(string cmd)
+
+DESCRIPTION
+	After set_modify_command(mob) was called for an interactive
+	object iob, all commands for that user will be passed to
+	mob->modify_command(), and the return will then be checked for
+	actions.
+
+	If the result is a string, it is the new command to execute
+	instead of the given one. Note that it is not possible to
+	make several commands from one this way!
+	If the result is a non-zero number, the given command is to
+	be ignored. In case of the closure/lfun setting this may
+	mean that the closure/lfun already executed it.
+	If the result is 0, the originally given command is to be
+	used.
+
+
+SEE ALSO
+	set_modify_command(E), hooks(C)
diff --git a/doc/efun/move_object b/doc/efun/move_object
new file mode 100644
index 0000000..48b23e9
--- /dev/null
+++ b/doc/efun/move_object
@@ -0,0 +1,18 @@
+SYNOPSIS:
+        void move_object(mixed item, mixed dest)
+
+        item->move(object dest, string methods)
+
+DESCRIPTION:
+	The item, which can be a object_name or an object, is moved into
+	it's new environment dest, which can also be object_name or an
+	object.  In native mode, the only object that can be moved
+	with move_object() is the calling object itself. This function
+	is only used to implement the lfun move().
+        
+        Use the lfun move() instead by inheriting standard objects.
+	move() must be called in the object to be moved. This gives
+	the moved object full control over its movement.
+
+SEE ALSO:
+        move(L), remove(L), init(L), transfer(E), native(C)
diff --git a/doc/efun/negate b/doc/efun/negate
new file mode 100644
index 0000000..b1f0bdf
--- /dev/null
+++ b/doc/efun/negate
@@ -0,0 +1,6 @@
+SYNOPSIS
+        int negate(int arg);
+        float negate(float arg);
+
+BESCHREIBUNG
+        Liefert den negativen Wert von <arg>.
diff --git a/doc/efun/net_connect b/doc/efun/net_connect
new file mode 100644
index 0000000..f3aae2f
--- /dev/null
+++ b/doc/efun/net_connect
@@ -0,0 +1,30 @@
+SYNOPSIS
+        int net_connect(string host, int port)
+
+BESCHREIBUNG
+        Oeffne eine nicht-blockierende TCP Netzverbindung zu
+        <host>:<port> .  Bei Erfolg wird die Verbindung zum
+        aufrufenden Objekt gebunden und die lfun logon() wird in dem
+        Objekt aufgerufen.
+
+        Resultat ist 0 bei Erfolg, und eine Unix ERRNO bei Misserfolg.
+
+        Ist der Driver fuer IPv6 konfiguriert, wird <host> erst als
+        IPv6-Name interpretiert, und wenn das fehlschlaegt, als
+        IPv4-Name.
+
+        Wenn die Verbindung nicht sofort erzeugt werden kann, gibt die
+        Funktion 'Erfolg' zurueck, und der Driver vollendet die
+        Funktion im Hintergrund. Sollte die Verbindungsaufbau im
+        Hintergrund fehlschlagen, wird logon(-1) im aktuellen Objekt
+        aufgerufen.
+
+        Die Funktion erzeugt eine privilege violation ("net_connect",
+        host, port).
+
+        WARNUNG: Ist <host> ein Name und keine IP, fuehrt die Funktion
+        einen DNS-Aufruf durch, der den Driver fuer einige Zeit
+        blockieren kann.
+
+SIEHE AUCH
+        logon(A)
diff --git a/doc/efun/next_bit b/doc/efun/next_bit
new file mode 100644
index 0000000..31fb90a
--- /dev/null
+++ b/doc/efun/next_bit
@@ -0,0 +1,32 @@
+SYNOPSIS
+        int next_bit(string str, int start);
+        int next_bit(string str, int start, int find_cleared);
+
+BESCHREIBUNG
+        Liefert den Zahlenwert des naechsten Bits im Bitstring <bit> nach
+        der Position <start>. Gewoehnlich ist dies das naechste gesetzte
+        Bit, aber wenn <find_cleared> angegeben und nicht 0 ist, wird
+        die Postion des naechsten geloeschten Bits zurueck gegeben.
+
+        Dabei ist das Finden von geloeschten Bits nach dem letzten gesetzten
+        Bit auf die tatsaechliche Laenge von <str> beschraenkt.
+
+        Jedes Zeichen enthaelt 6 Bits. In jedem Zeichen kann deshalb eine
+        Zahl von 0 bis 63 gespeichert werde (2^6=64). Das erste Zeichen
+        ist der Leerschlag " " mit dem Wert 0. Das erste Zeichen im String
+        ist jenes mit den niedrigsten Bits (0-5).
+
+BEISPIEL
+        string s;
+        int p;
+
+        s = set_bit("", 4); s = set_bit(s, 2);
+
+        for (p = -1; -1 != (p = next_bit(s, p); )
+            write(p+"\n");
+
+        --> das gibt 2 und 4 aus.
+
+SIEHE AUCH
+        set_bit(E), clear_bit(E), test_bit(E), last_bit(E), count_bits(E),
+        and_bits(E), or_bits(E), xor_bits(E), invert_bits(E), copy_bits(E)
diff --git a/doc/efun/next_inventory b/doc/efun/next_inventory
new file mode 100644
index 0000000..61370a0
--- /dev/null
+++ b/doc/efun/next_inventory
@@ -0,0 +1,17 @@
+SYNOPSIS
+        object next_inventory();
+        object next_inventory(object ob);
+
+BESCHREIBUNG
+        Liefert das naechste Objekt aus dem Inventar von <ob>. Wird <ob>
+        nicht angegeben, wird this_object() verwendet.
+
+        Diese Efun wird meistens zusammen mit first_inventory() verwendet
+        um ueber Raum- und Containerinhalte zu iterieren.
+        Fuer ein Beispiel siehe dort.
+
+SIEHE AUCH
+        first_inventory(E), all_inventory(E), environment(E),
+        deep_inventory(E)
+
+7.Aug 2007 Gloinson
\ No newline at end of file
diff --git a/doc/efun/object_info b/doc/efun/object_info
new file mode 100644
index 0000000..4a773fc
--- /dev/null
+++ b/doc/efun/object_info
@@ -0,0 +1,202 @@
+SYNOPSIS
+        #include <object_info.h>
+
+        mixed object_info(object ob, int what)
+
+DESCRIPTION
+        Returns some internal information about object <ob>. The
+        Argument <what> determines which information is returned.
+
+        It can be either a configuration option as given to
+        configure_object() or one of the following options:
+
+
+
+        Object Flags:
+
+        <what> == OI_ONCE_INTERACTIVE:
+           1 if <ob> was once (or still is) interactive, 0 else.
+
+        <what> == OI_RESET_STATE:
+           1 if <ob> is (still) reset, 0 else.
+
+        <what> == OI_WILL_CLEAN_UP:
+           1 if <ob>'s clean_up() will be called, 0 else.
+
+        <what> == OI_LAMBDA_REFERENCED:
+           1 if <ob> has lambdas (and there replace_program()
+           is not allowed anymore), 0 else.
+
+        <what> == OI_REPLACED:
+           1 if the program for <ob> was replaced, 0 else.
+
+
+
+        Program Flags:
+
+        <what> == OI_NO_INHERIT:
+           1 if the program can't be inherited.
+
+        <what> == OI_NO_CLONE:
+           1 if the program/blueprint can't be cloned.
+
+        <what> == OI_NO_SHADOW:
+           1 if the program's functions can't be shadowed.
+
+        <what> == OI_SHARE_VARIABLES:
+           1 if clones of this program share their initial
+           variable values with the blueprint.
+
+
+
+        Swapping Information:
+
+        <what> == OI_SWAPPED:
+           1 if <ob> is swapped, 0 else.
+
+        <what> == OI_PROG_SWAPPED:
+           1 if <ob>'s program is swapped, 0 else.
+
+        <what> == OI_VAR_SWAPPED:
+           1 if <ob>'s variables are swapped, 0 else.
+
+        <what> == OI_SWAP_NUM:
+           The swap number for <ob>s program, or -1 if not swapped.
+
+
+
+        Time Information:
+
+        <what> == OI_NEXT_RESET_TIME:
+            Time of the next reset.
+
+        <what> == OI_NEXT_CLEANUP_TIME:
+            Time of the next data cleanup.
+
+        <what> == OI_LAST_REF_TIME:
+            Time of the last call to <ob>.
+
+
+
+        Object List:
+
+        <what> == OI_OBJECT_NEXT:
+           The next object in the global object list.
+
+        <what> == OI_OBJECT_PREV:
+           The previous object in the global object list.
+
+        <what> == OI_OBJECT_POS:
+           The position of <ob> in the global object list,
+           counting from 0 up. This can be expensive to compute.
+
+
+
+        Shadows:
+
+        <what> == OI_SHADOW_NEXT:
+           The next object in the shadow list, i.e. the object
+           that is shadowing <ob>, or 0 if <ob> is not shadowed.
+
+        <what> == OI_SHADOW_PREV:
+           The previous object in the shadow list, i.e. the object
+           that <ob> is currently shadowing, or 0 if <ob> is not a shadow.
+
+        <what> == OI_SHADOW_ALL:
+           Returns an array of all objects that are currently
+           shadowing <ob>, or an empty array if <ob> is not shadowed.
+
+
+
+        Object Statistics:
+
+        <what> == OI_OBJECT_REFS:
+           The number of references to <ob>.
+
+        <what> == OI_TICKS:
+           The accumulated evaluation cost spend in <ob> modulo 1000000000.
+
+        <what> == OI_GIGATICKS:
+           The accumulated evaluation cost spend in <ob> divided by 1000000000.
+
+        <what> == OI_DATA_SIZE:
+           The total size of the values held in the object's variables,
+           scaled down according to the extend of data sharing.
+
+        <what> == OI_DATA_SIZE_TOTAL:
+           The unmodified total size of the values held in the
+           object's variables
+
+
+
+        Program Statistics:
+
+        <what> == OI_PROG_REFS:
+           The number of references to <ob>'s program.
+
+        <what> == OI_NUM_FUNCTIONS:
+           The number of functions in the program.
+
+        <what> == OI_NUM_VARIABLES:
+           The number of variables in the program.
+
+        <what> == OI_NUM_STRINGS:
+           The number of strings in the program.
+
+        <what> == OI_NUM_INHERITED:
+           The number of explicitely inherited programs.
+
+        <what> == OI_NUM_INCLUDED:
+           The number of included files in the program.
+
+        <what> == OI_SIZE_FUNCTIONS:
+           The size needed for the function structures.
+           Note that this does not include size of the function code.
+
+        <what> == OI_SIZE_VARIABLES:
+           The size needed for the variable structures.
+           Note that this does not include size of the variable data,
+           See OI_DATA_SIZE/OI_DATA_SIZE_TOTAL for that.
+
+        <what> == OI_SIZE_STRINGS:
+           The size needed for the string pointers.
+
+        <what> == OI_SIZE_STRINGS_DATA:
+           The size needed for the string values,
+           scaled down according to the extend of data sharing.
+
+        <what> == OI_SIZE_STRINGS_DATA_TOTAL:
+           The unmodified size needed for the string values.
+
+        <what> == OI_SIZE_INHERITED:
+           The size needed for the inherit structures.
+
+        <what> == OI_SIZE_INCLUDED:
+           The size needed for the include structures.
+
+        <what> == OI_PROG_SIZE:
+           The size of the program structure.
+
+        <what> == OI_PROG_SIZE_TOTAL:
+           The total size of the program.
+
+
+HISTORY
+        Introduced in LDMud 3.2.6.
+        Changes in LDMud 3.2.7:
+          - new basic result OIB_REPLACED.
+          - basic result OIB_IS_WIZARD is always 0 if set_is_wizard()
+              is not available.
+          - basic result OIB_APPROVED is gone.
+        LDMud 3.2.8 added OIM_DATA_SIZE to the result of OINFO_MEMORY.
+        LDMud 3.2.9 added the index mechanism, OIM_NUM_INCLUDES,
+          OIM_NO_INHERIT, OIM_NO_SHADOW, OIM_NO_CLONE, OIM_SIZE_STRINGS_DATA,
+          OIM_SIZE_STRINGS_TOTAL, and OIM_DATA_SIZE_TOTAL to the result
+          of OINFO_MEMORY.
+        LDMud 3.3.378 added the OIM_SHARE_VARIABLES to the result
+          of OINFO_MEMORY.
+        LDMud 3.3.654 added the OIB_NEXT_CLEANUP to the result of OINFO_BASIC.
+        LDMud 3.5.0 redesigned the whole efun.
+
+SEE ALSO
+        configure_object(E), interactive_info(E), driver_info(E)
diff --git a/doc/efun/object_name b/doc/efun/object_name
new file mode 100644
index 0000000..6ba82ed
--- /dev/null
+++ b/doc/efun/object_name
@@ -0,0 +1,26 @@
+SYNOPSIS:
+    string object_name()
+    string object_name(object ob)
+
+DESCRIPTION:
+    Get the file name of an object or if no argument is given of the current
+    object. If the object is a cloned object, then it will not have a
+    corresponding file name, but rather a new name based on the original
+    file name.
+        
+    The returned name always begins with '/' (absolute path),
+  	except when the parser runs in COMPAT (-o) mode.
+
+EXAMPLES:
+    find_object(object_name(ob)) == ob    
+    This is guaranteed to be true for all objects ob that are not
+   	destructed.
+        
+    sizeof(explode(object_name(ob), "#")) == 1  
+    This is always true if ob is a blue print.
+        
+SEE ALSO:
+        find_object(E)
+
+29.10.2006 Zesstra
+
diff --git a/doc/efun/object_time b/doc/efun/object_time
new file mode 100644
index 0000000..db4d91c
--- /dev/null
+++ b/doc/efun/object_time
@@ -0,0 +1,10 @@
+SYNOPSIS
+        int object_time();
+        int object_time(object ob);
+
+BESCHREIBUNG
+        Liefert die Zeit, zu der das Objekt <ob> erstellt wurde. Wird <obj>
+        nicht angegeben, wird standardmaessig this_object() verwendet.
+
+SIEHE AUCH
+        program_time(E), program_name(E)
diff --git a/doc/efun/objectp b/doc/efun/objectp
new file mode 100644
index 0000000..2b0758c
--- /dev/null
+++ b/doc/efun/objectp
@@ -0,0 +1,9 @@
+SYNOPSIS
+        int objectp(mixed arg);
+
+BESCHREIBUNG
+        Liefert 1, wenn <arg> ein Objekt ist.
+
+SIEHE AUCH
+        clonep(E), intp(E), stringp(E), pointerp(E), symbolp(E),
+        referencep(E), symbolp(E)
diff --git a/doc/efun/or_bits b/doc/efun/or_bits
new file mode 100644
index 0000000..25406e4
--- /dev/null
+++ b/doc/efun/or_bits
@@ -0,0 +1,27 @@
+SYNOPSIS
+        string or_bits(string str1, string str2);
+
+BESCHREIBUNG
+        <str1> und <str2> sind beides Bitstrings. Das Resultat von or_bits()
+        ist ein Bitstring, der das binaere Oder von <str1> und <str2>
+        enthaelt, d.h. ein String, in dem ein Bit gesetzt ist, wenn es
+        in <str1> oder <str2> oder in beiden gesetzt ist.
+
+        Jedes Zeichen enthaelt 6 Bits. In jedem Zeichen kann deshalb eine
+        Zahl von 0 bis 63 gespeichert werde (2^6=64). Das erste Zeichen
+        ist der Leerschlag " " mit dem Wert 0. Das erste Zeichen im String
+        ist jenes mit den niedrigsten Bits (0-5).
+
+BEISPIEL
+        string s1, s2, s3;
+
+        s1 = set_bit("", 3); s1 = set_bit(s1, 15);  -> s1 is "( ("
+        s2 = set_bit("", 3); s2 = set_bit(s2, 4);   -> s2 is "8"
+
+        s3 = or_bits(s1, s2);
+
+        -> s3 is now "8 (", ie. a bitstring with bits 3, 4 and 15 set.
+
+SIEHE AUCH
+        clear_bit(E), set_bit(E), test_bit(E), next_bit(E), last_bit(E),
+        count_bits(E), and_bits(E), xor_bits(E), invert_bits(E), copy_bits(E)
diff --git a/doc/efun/parse_command b/doc/efun/parse_command
new file mode 100644
index 0000000..83b7731
--- /dev/null
+++ b/doc/efun/parse_command
@@ -0,0 +1,132 @@
+OPTIONAL
+SYNOPSIS
+        int parse_command (string cmd, object  env, string fmt, mixed &var, ...)
+        int parse_command (string cmd, object* arr, string fmt, mixed &var, ...)
+
+DESCRIPTION
+        parse_command() is basically a spiffed up sscanf operating
+        on word basis and targeted at recognizing object descriptions from
+        command strings.
+
+        The efun takes the command string <cmd> and the object(s) <env>/<arr>
+        and tries to match it against the format string <fmt>. Successfully
+        matched elements are assigned to the variables <var>.... The result
+        from the efun is 1 if the command could be fully matched, and 0
+        otherwise.
+
+        If the objects are given as a single object <env>, the efun matches
+        against the given object and all objects contained therein. Otherwise,
+        if the objects are given as an array <arr> of objects, the efun
+        matches only against the given objects.
+
+        The format string <fmt> consists of words, syntactic markers, and
+        %-directives for the values to parse and return in the variables.
+        A typical example is " 'get' / 'take' %i " or
+        " 'spray' / 'paint' [paint] %i ". The elements in detail are:
+
+           'word': obligatory text
+           [word]: optional text
+           /     : Alternative marker
+           %o    : Single item, object
+           %s    : Any text
+           %w    : Any word
+           %p    : One of a list of prepositions.
+                   If the variable associated with %p is used to pass
+                   a list of words to the efun, the matching will take
+                   only against this list.
+           %l    : non-compat: Living objects
+                   compat: a single living object
+           %i    : Any objects
+           %d    : Number >= 0, or when given textual: 0-99.
+
+        A <word> in this context is any sequence of characters not containing
+        a space. 'living objects' are searched by calls to the (simul)efuns
+        find_player() and find_living(): both functions have to accept a name
+        as argument and return the object for this name, or 0 if there
+        is none.
+
+        The results assigned to the variables by the %-directives are:
+
+           %o : returns an object
+           %s : returns a string of words
+           %w : returns a string of one word
+           %p : if passed empty: a string
+                if passed as array of words: var[0] is the matched word
+           %i : returns an array with the following content:
+                  [0]: int: the count/number recognized in the object spec
+                            > 0: a count (e.g. 'three', '4')
+                            < 0: an ordinal (e.g. 'second', 'third')
+                            = 0: 'all' or a generic plural such as 'apples'
+                  [1..]: object: all(!) objects matching the item description.
+                                 In the <env> form this may be the whole
+                                 recursive inventory of the <env> object.
+                It is up to the caller to interpret the recognized numeral
+                and to apply it on the list of matched objects.
+           %l : non-compat: as %i, except that only living objects are
+                            returned.
+                compat: as %o, except that only a living object is returned.
+
+        %i (and non-compat-%l) match descriptions like 'three red roses',
+        'all nasty bugs' or 'second blue sword'.
+
+        Note: Patterns of type: "%s %w %i" might not work as one would expect.
+        %w will always succeed so the arg corresponding to %s will always be
+        empty.
+
+        To make the efun useful it must have a certain support from the
+        mudlib: it calls a set of functions in objects to get the
+        information it needs to parse a string.
+
+          1. string *parse_command_id_list()
+              Normal singular names of the object.
+
+          2. string *parse_command_plural_id_list() - optional
+              Plural forms of the names returned by 1.
+              If this function doesn't exist, the parser tries to pluralize
+              the names returned by 1.
+
+          3. string *parse_command_adjectiv_id_list() -  optional
+              All adjectives associated with this object.
+
+        All names and adjectives may consist of several words separated
+        by spaces.
+
+        These functions should exist in all objects and are therefore best
+        put into a mandatory inherit file (e.g. /std/object.c).
+
+        In addition the master object may offer the same functions to provide
+        reasonable defaults (like 'thing' as generic singular name):
+
+             string *parse_command_id_list()
+               - Would normally return: ({ "one", "thing" })
+
+             string *parse_command_plural_id_list()
+               - Would normally return: ({ "ones", "things", "them" })
+
+             string *parse_command_adjectiv_id_list()
+               - Would normally return ({ "iffish" })
+
+        Two additional functions in the master object provide the default
+        list of prepositions (needed for %p) and the single 'all' word:
+
+             string *parse_command_prepos_list()
+               - Would normally return: ({ "in", "on", "under", "behind",
+                 "beside" })
+
+             string parse_command_all_word()
+               - Would normally return: "all"
+
+
+        int parse_command(string, object|object*, string, destargs...)
+
+
+EXAMPLE
+        object *items;
+        parse_command( "take apple",environment(this_player())
+                     , " 'get' / 'take' %i ",items);
+
+HISTORY
+        LDMud 3.3.258 removed the compat-mode parse_command().
+
+SEE ALSO
+        sscanf(E)
diff --git a/doc/efun/people b/doc/efun/people
new file mode 100644
index 0000000..e04c3bf
--- /dev/null
+++ b/doc/efun/people
@@ -0,0 +1,8 @@
+void people()
+
+A function that will list all interactive players, and some info about them. 
+This function is normally
+
+connected to the people command, that wizards have.
+
+THIS FUNCTION IS OBSOLETE. LOOK AT users() INSTEAD.
diff --git a/doc/efun/pg_close b/doc/efun/pg_close
new file mode 100644
index 0000000..af4f8f0
--- /dev/null
+++ b/doc/efun/pg_close
@@ -0,0 +1,19 @@
+OPTIONAL
+SYNOPSIS
+        void pg_close()
+
+DESCRIPTION
+        Close the database connection for the current object, if there is one.
+
+        The function is available only if the driver is compiled with
+        PostgreSQL support. In that case, __PGSQL__ is defined.
+
+        The efun triggers a privilege violation ("pgsql", "pg_close").
+
+HISTORY
+        Added in 3.3.445.
+        LDMud 3.3.640 added the privilege violation.
+
+SEE ALSO
+        pgsql(C), pg_connect(E), pg_conv_string(E), pg_query(E), pg_pending(E),
+        privilege_violation(M)
diff --git a/doc/efun/pg_connect b/doc/efun/pg_connect
new file mode 100644
index 0000000..a9d4d58
--- /dev/null
+++ b/doc/efun/pg_connect
@@ -0,0 +1,39 @@
+OPTIONAL
+SYNOPSIS
+        int pg_connect (string conn, string fun)
+        int pg_connect (string conn, string fun, string|object obj, mixed extra, ...)
+        int pg_connect (string conn, closure cl, mixed extra, ...)
+
+DESCRIPTION
+        Open a database connection as directed by <conn>, and assign the
+        callback function <fun>/<cl> with the optional <extra> parameters
+        to it.
+
+        The object holding the callback function becomes the controlling
+        object; obiously it is an error to assign more than one connection
+        to the same controlling object.
+
+        The <conn> string is in the format accepted by Postgres'
+        PQconnectStart() API functions. Pass an empty string to use the
+        default options, or a string holding the '<key>=<value>' options
+        separated by whitespace.
+        
+        The most useful options are:
+          dbname:   The database name
+          user:     The user name to connect as.
+          password: Password to be used.
+
+        Return 0 on success, and -1 on failure.
+
+        The function is available only if the driver is compiled with
+        PostgreSQL support. In that case, __PGSQL__ is defined.
+
+        The efun triggers a privilege violation ("pgsql", "pg_connect").
+
+HISTORY
+        Added in 3.3.445.
+        LDMud 3.3.640 added the privilege violation.
+
+SEE ALSO
+        pgsql(C), pg_query(E), pg_pending(E), pg_conv_string(E), pg_close(E),
+        privilege_violation(M)
diff --git a/doc/efun/pg_conv_string b/doc/efun/pg_conv_string
new file mode 100644
index 0000000..d849d06
--- /dev/null
+++ b/doc/efun/pg_conv_string
@@ -0,0 +1,20 @@
+OPTIONAL
+SYNOPSIS
+        string pg_conv_string(string str)
+
+DESCRIPTION
+        Convert the string <str> into a string that is correctly interpretated
+        for usage as a string in pg_query(), e.g. ' is replaced with \' and so
+        on.
+
+        The function is available only if the driver is compiled with
+        PostgreSQL support. In that case, __PGSQL__ is defined.
+
+        The efun triggers a privilege violation ("pgsql", "pg_connect").
+
+HISTORY
+        Added in 3.3.708.
+
+SEE ALSO
+        pgsql(C), pg_query(E), pg_pending(E), pg_conv_string(E), pg_close(E),
+        privilege_violation(M)
diff --git a/doc/efun/pg_pending b/doc/efun/pg_pending
new file mode 100644
index 0000000..46a6dcc
--- /dev/null
+++ b/doc/efun/pg_pending
@@ -0,0 +1,22 @@
+OPTIONAL
+SYNOPSIS
+        int pg_pending ()
+        int pg_pending (object obj)
+
+DESCRIPTION
+        Return the number of pending queries for the connection on the given
+        object <obj> (default is the current object). The object has no
+        database connection, return -1.
+
+        The function is available only if the driver is compiled with
+        PostgreSQL support. In that case, __PGSQL__ is defined.
+
+        The efun triggers a privilege violation ("pgsql", "pg_pending").
+
+HISTORY
+        Added in 3.3.445.
+        LDMud 3.3.640 added the privilege violation.
+
+SEE ALSO
+        pgsql(C), pg_connect(E), pg_conv_string(E), pg_query(E), pg_close(E),
+        privilege_violation(M)
diff --git a/doc/efun/pg_query b/doc/efun/pg_query
new file mode 100644
index 0000000..d95f2cc
--- /dev/null
+++ b/doc/efun/pg_query
@@ -0,0 +1,28 @@
+OPTIONAL
+SYNOPSIS
+        #include <pgsql.h>
+
+        int pg_query (string query)
+        int pg_query (string query, int flags)
+
+DESCRIPTION
+        Queue a new query <query> to the database connection on the current
+        object. Return the unique id of the query. The query result itself
+        will be passed as argument to the callback function.
+
+        <flags> can be one of these values:
+          PG_RESULT_ARRAY: Pass the query result as array.
+          PG_RESULT_MAP:   Pass the query result as mapping.
+
+        The function is available only if the driver is compiled with
+        PostgreSQL support. In that case, __PGSQL__ is defined.
+
+        The efun triggers a privilege violation ("pgsql", "pg_query").
+
+HISTORY
+        Added in 3.3.445.
+        LDMud 3.3.640 added the privilege violation.
+
+SEE ALSO
+        pgsql(C), pg_connect(E), pg_conv_string(E), pg_pending(E), pg_close(E),
+        privilege_violation(M)
diff --git a/doc/efun/pointerp b/doc/efun/pointerp
new file mode 100644
index 0000000..a6f2c2c
--- /dev/null
+++ b/doc/efun/pointerp
@@ -0,0 +1,9 @@
+SYNOPSIS
+        int pointerp(mixed arg)
+
+BESCHREIBUNG
+        Liefert 1, wenn das Argument ein Feld (Array) ist, ansonsten 0.
+
+SIEHE AUCH
+        closurep(E), floatp(E), mappingp(E), objectp(E), intp(E),
+        referencep(E), stringp(E), symbolp(E), clonep(E)
diff --git a/doc/efun/pow b/doc/efun/pow
new file mode 100644
index 0000000..f0ff586
--- /dev/null
+++ b/doc/efun/pow
@@ -0,0 +1,16 @@
+SYNOPSIS
+        float pow(int|flaot base, int|flaot exp);
+
+BESCHREIBUNG
+        Die Potenzfunktion. Sie liefert das Resultat von "<base> hoch <exp>".
+
+BEISPIEL
+        pow(-2, 3) -> liefert -8.0
+        pow(8, 1.0/3.0) -> liefert 2.0
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.7.
+        LDMud 3.2.9. erlaubte neu Integer als Argumente.
+
+SIEHE AUCH
+        exp(E), log(E)
diff --git a/doc/efun/present b/doc/efun/present
new file mode 100644
index 0000000..7b7d8d3
--- /dev/null
+++ b/doc/efun/present
@@ -0,0 +1,84 @@
+SYNOPSIS
+        object present(string str)
+        object present(string str, int n)
+        object present(string str, object env)
+        object present(string str, int n, object env)
+
+        object present(object ob)
+        object present(object ob, object env)
+
+BESCHREIBUNG
+        Wenn ein Objekt mit der Id <str> sich im Inventar oder in der Umgebung
+        von this_object() befindet, wird es zurueck geliefert.
+        
+        Ist das Argument <n> gegeben, wird das n-te Objekt mit Namen <id>
+        zurueck geliefert. Das heisst, der Driver fuehrt fuer alle Objekte
+        im Inventar und in der Umgebung von this_object() die Funktion
+        id(str) aus, bis ein Treffer erzielt wird (wenn ueberhaupt).
+
+        Ist <n> nicht gegeben, aber <str> hat die Form "<id> <n>" , wird
+        ebenfalls das n-te Objekt mit Namen <id> zurueckgeliefert.
+
+        Es ist: id(str) { return str == <name>; }
+
+        <str> kann auch ein Objekt (anstatt einem object_name()) sein, was den
+        Test schneller und einfacher macht.
+
+        Das Argument <env> ist optional. <env> gibt an, wo nach <str> gesucht
+        wird. Nur das Inventory von <env> wird durchsucht, nicht jedoch dessen
+        Umgebung. Oftmals wird fuer <env> this_player() gesetzt.
+
+
+ANMERKUNG
+        Diese efun kann u.U. _sehr_ teuer sein! Bitte schaut euch auf jeden
+        Fall auch die efun present_clone() an und schaut, ob die evtl. das
+        kann, was ihr machen wollt. present_clone() ist erheblich billiger.
+
+        Wenn die efun sowohl in this_object() als auch dessen Umgebung
+        sucht, werden, soweit es die Numerierung betrifft, die beiden
+        Umgebungen zusammenhaengend betrachtet.
+
+BEISPIELE
+        present("chest");
+          --> findet das erste 'chest' Objekt
+
+        present("chest 2");
+          --> findet das zweite 'chest' Objekt
+
+        present("chest 2", 1);
+          --> findet das erste 'chest 2' Objekt
+
+        Wenn sich eine "chest" im this_object() befindet, und zwei
+        ausserhalb:
+          present("chest", 1) -> findet die chest im Objekt
+          present("chest", 2) -> findet die erste chest ausserhalb
+          present("chest", 3) -> findet die zweite chest ausserhalb
+        
+        Wenn ein Objekt die Forum "<id> <n>" in Verbindung mit einem selbst
+        ueber add_action() gesetzten Verb unterstuetzen soll (damit z. B.
+        "oeffne Kiste 3" funktioniert), kann das folgendermassen geloest
+        werden:
+
+            void init() { add_action("oeffne_kiste", "oeffne"); }
+
+            int oeffne_kiste(string str)
+            {
+                if(present(str) != this_object() )
+                {
+                    return 0; /* nicht diese Kiste */
+                    ...
+                }
+            }
+
+HISTORY
+        LDMud 3.2.11/3.3.610 fuehrte die (str, n)-Form ein.
+        LDMud 3.3.713 aenderte die Numerierung wenn sowohl Inventory
+          als auch Umgebung durchsucht werden. In der vorherigen
+          Implementierung wurde eine Numerierung auf beiden Umgebungen
+          einzeln angewandt, was zur Folge hatte, dass niedere Objekte
+          in der aeusseren Umgebung nicht gefunden werden koennten, da
+          sie von den Objekten in Inneren verdeckt wurden.
+
+SIEHE AUCH
+        move_object(E), environment(E), this_object(E), present_clone(E),
+        id(A), init(A)
diff --git a/doc/efun/present_clone b/doc/efun/present_clone
new file mode 100644
index 0000000..cd64488
--- /dev/null
+++ b/doc/efun/present_clone
@@ -0,0 +1,41 @@
+VORLAEUFIG
+SYNOPSIS
+        object present_clone(string str [, object env] );
+        object present_clone(object obj [, object env] );
+
+BESCHREIBUNG
+        Diese Efun durchsucht das Inventar von <env> nach einem Objekt mit
+        einem bestimmten Blueprint . Wird <env> nicht angegeben, wird in
+        this_object() gesucht. Der Blueprint kann entweder mit seinem Namen
+        <str> angegeben werden, oder als Blueprint des Objekts <obj>. Gesucht
+        wird in beiden Faellen aufgrund von load_name().
+        Wird kein passendes Objekt gefunden, liefert die Efun 0 zurueck.
+
+        Fuer Driver im Plain Modus beginnt der Name in jedem Fall mit '/', im
+        Compat Modus nicht.
+
+BEISPIELE
+        Angenommen, das Objekt <env> enthalte die Objekte /items/money#8,
+        /std/weapon#9, /std/weapon#12 und /obj/key in der angegeben
+        Reihenfolge.
+
+        +--------------------------------------------------+---------------+
+        | Funktion                                         | Liefert       |
+        +--------------------------------------------------+---------------+
+        | present_clone("/items/money", env)               | /items/money#8|
+        | present_clone("/std/weapon#12", env)             | /std/weapon#9 |
+        | present_clone(find_object("/items/money#1"), env)| /items/money#8|
+        | present_clone("/obj/key#18", env)                | /obj/key      |
+        +--------------------------------------------------+---------------+
+
+        Fuer Driver im Compat Modus liefert die Funktion keine '/' am Anfang.
+
+AENDERUNGEN
+        Eingefuehrt in 3.2.7.
+
+ANMERKUNG
+        Im Unterschied zu present() sucht present_clone() niemals in der
+        Umgebung von <env>.
+
+SIEHE AUCH
+        load_name(E), present(E)
diff --git a/doc/efun/previous_object b/doc/efun/previous_object
new file mode 100644
index 0000000..6a712ad
--- /dev/null
+++ b/doc/efun/previous_object
@@ -0,0 +1,50 @@
+SYNOPSIS
+        object previous_object();
+        object previous_object(int i);
+
+BESCHREIBUNG
+        Liefert einen Pointer auf das letzte Objekt, das einen Aufruf (mittels
+        call_other(), funcall() etc.) auf das aktuelle Objekt this_object()
+        gemacht hat. Wenn dieses aufrufende Objekt inzwischen zerstoert wurde,
+        liefert previous_object() 0.
+        Bei einem Aufruf einer eigenen Funktion durch ein Objekt bleibt das
+        bisherige previous_object() unveraendert.
+
+        Wird das Argument <i> angegeben, so verfolgt previous_object() den
+        Aufruf <i> Stufen zurueck. Zum Beispiel liefert previous_object(1) das
+        aufrufende Objekt des aufrufenden Objekts. Fuer <i> muss gelten:
+        0 <= i < call_stack_depth(). Ein Wert <i> < 0 liefert das erste
+        aufrufende Object zurueck.
+
+        Es gibt einen wichtigen Spezialfall: in Funktionen, die vom Gamedriver
+        auf Grund eines externen Ereignises aufgerufen wurden (zum Beispiel
+        Kommandi, die mit add_action() definiert wurden), liefert
+        previous_object() this_object(), previous_object(0) hingegen 0.
+
+BEISPIEL
+        int sicherheitscheck()
+        {
+            object prev;
+            if(!(prev=previous_object()));
+            else if(getuid(prev)!=getuid(this_object()));
+            else if(geteuid(prev)!=geteuid(this_object()));
+            else return 1;
+            return 0;
+        }
+        void sensible_funktion()
+        {
+            if(!sicherheitscheck())
+            return;
+            ...
+        }
+
+        Diese Funktion zeigt, wie man ueberpruefen kann, ob der letzte Aufruf
+        einer Funktion im aktuellen Objekt sicher war, oder ob die
+        Verarbeitung abgebrochen werden sollte.
+
+FEHLER
+        Werte von <i> < 0 werden wie <i> == 0 behandelt - dies ist historisch.
+
+SIEHE AUCH
+        call_other(E), this_object(E), this_player(E)
+        caller_stack(E), caller_stack_depth(E), extern_call(E)
\ No newline at end of file
diff --git a/doc/efun/printf b/doc/efun/printf
new file mode 100644
index 0000000..d58233b
--- /dev/null
+++ b/doc/efun/printf
@@ -0,0 +1,9 @@
+SYNOPSIS
+        void printf(string format, ...);
+
+BESCHREIBUNG
+        Eine Mischung aus sprintf() und write(). Gibt void zurueck und den
+        String an den Benutzer aus.
+
+SIEHE AUCH
+        sprintf(E), write(E), terminal_colour(E)
diff --git a/doc/efun/program_name b/doc/efun/program_name
new file mode 100644
index 0000000..44162d5
--- /dev/null
+++ b/doc/efun/program_name
@@ -0,0 +1,38 @@
+SYNOPSIS
+        string program_name()
+        string program_name(object obj)
+
+BESCHREIBUNG
+        Liefert den Name des Programms, aus dem <obj> kompiliert wurde.
+        Wenn <obj> nicht angegeben wird, wird standardmaessig this_object()
+        verwendet.
+
+        Der Name ist fuer Clones der Name des Files, aus dem der Blueprint
+        kompliert wurde. Der Name wechselt, wenn ein Objekt sein Programm
+        durch replace_program() aendert.
+
+        Fuer den Spezialfall, dass <obj> als 0 uebergeben wird, liefert
+        program_name() 0 zurueck.
+
+        Der Name endet immer mit '.c'. Er beginnt mit einem '/', wenn der
+        Driver sich nicht im Compat Modus befindet.
+
+BEISPIEL
+        object o;
+        o = clone_object("/std/dings");
+        write(program_name(o));
+
+        liefert:
+        --> "/std/dings.c", wenn der Driver nicht im Compat Modus laeuft.
+        --> "std/dings.c", wenn der Driver im Compat Modus laeuft.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.6.
+        Seit 3.2.9 ist das Argument 0 erlaubt.
+
+ANMERKUNG
+        Die Efun swapt zum Programm, wenn dieses geswapt ist.
+
+SIEHE AUCH
+        clone_object(E), clonep(E), load_name(E), load_object(E),
+        object_name(E), replace_program(E)
diff --git a/doc/efun/program_time b/doc/efun/program_time
new file mode 100644
index 0000000..62a6297
--- /dev/null
+++ b/doc/efun/program_time
@@ -0,0 +1,17 @@
+SYNOPSIS
+        int program_time()
+        int program_time(object ob)
+
+DESCRIPTION
+        Returns the creation (compilation) time of the object's
+        program. In other words, this is the object_time() of
+        the blueprint.
+
+        Default is this_object(), if no arg is given.
+
+	
+        CAVEAT: If the objects program is swapped out, this efun
+          swaps it back in.
+
+SEE ALSO
+        object_time(E), program_name(E)
diff --git a/doc/efun/query_actions b/doc/efun/query_actions
new file mode 100644
index 0000000..90acaba
--- /dev/null
+++ b/doc/efun/query_actions
@@ -0,0 +1,37 @@
+SYNOPSIS
+        #include <sys/commands.h>
+
+        mixed *query_actions(object|string ob, mixed mask_or_verb);
+
+BESCHREIBUNG
+        Das erste Argument von query_actions() ist entweder ein Objekt
+        oder der Dateiname eines Objekts. Das zweite Argument muss
+        entweder eine Bitmaske (ein Integer) oder ein String sein.
+
+        Ist das zweite Argument ein String, liefert query_actions() ein
+        Array mit Informationen ueber das Verb oder 0 zurueck, wenn
+        das lebendige Objekt <ob> das Verb nicht verwenden kann.
+
+        Wenn das zweite Argument ein Integer ist, liefert query_actions()
+        ein flaches Array mir den Informationen entsprechend der Bitmaske
+        zu allen Verben von <ob>.
+
+            QA_VERB         ( 1):   das Verb,
+            QA_TYPE         ( 2):   der Typ,
+            QA_SHORT_VERB   ( 4):   das short_verb,
+            QA_OBJECT       ( 8):   das Objekt,
+            QA_FUNCTION     (16):   die Funktion.
+
+        Der Typ ist ein Wert wie in <sent.h> (bzw. /sys/sent.h) definiert,
+        der vom Parser Quellcode geliefert wird.
+
+            SENT_PLAIN       durch add_action(fun, cmd) hinzugefuegt
+            SENT_SHORT_VERB  durch add_action(fun, cmd, 1) hinzugefuegt
+            SENT_NO_SPACE    durch add_action(fun); add_xverb(cmd);
+            SENT_NO_VERB     nur eine Funktion mit add_action(fun), ohne Verb
+            SENT_MARKER      intern, ist nicht im Array enthalten
+            negativer Wert   das Verb muss nur in den ersten -<wert> Zeichen
+                             uebereinstimmen.
+
+SIEHE AUCH
+        add_action(E), init(A)
diff --git a/doc/efun/query_command b/doc/efun/query_command
new file mode 100644
index 0000000..bbabdbd
--- /dev/null
+++ b/doc/efun/query_command
@@ -0,0 +1,34 @@
+SYNOPSIS
+        string query_command();
+
+BESCHREIBUNG
+        Liefert den Text des aktuellen Kommandos oder 0, wenn keines
+        ausgefuehrt wird.
+
+        Der Text entspricht dem, was der Parser "sieht", also nachdem
+        modify_command() ausgefuehrt und nachfolgende Leerzeichen
+        abgeschnitten wurden.
+
+        query_command() liefert 0, wenn es von einer Funktion gestartet wurde,
+        die wiederum von einem call_out() oder dem heart_beat() aufgerufen
+        wurde. Auch Kommandi, die beim Userlogin aufgerufen werden, liefern 0.
+
+BEISPIEL
+        void init()
+        {
+            ...
+            add_action("sing","sing");
+            ...
+        }
+
+        int sing(string str)
+        {
+            write("Dein Kommando war:"+query_command()+"\n");
+            return 1;
+        }
+
+        Jedesmal, wenn jemand "sing blafasel" eingibt, liefert das Programm
+        "Dein Kommando war: sing blafasel".
+
+SIEHE AUCH
+        add_action(E), query_verb(E)
diff --git a/doc/efun/query_notify_fail b/doc/efun/query_notify_fail
new file mode 100644
index 0000000..3d98423
--- /dev/null
+++ b/doc/efun/query_notify_fail
@@ -0,0 +1,21 @@
+SYNOPSIS
+        mixed query_notify_fail();
+        mixed query_notify_fail(int flag);
+
+BESCHREIBUNG
+        Wenn <flag> nicht angegeben oder 0 ist, liefert die Funktion den String
+        oder die Closure, die zuletzt als Fehlermeldung fuer ein Kommando
+        gesetzt wurde (mit notify_fail()).
+
+        Wurde keine Meldung gesetzt, wird 0 geliefert.
+
+        Ist <flag> != 0 wird das Objekt zurueckgeliefert, was das letzte
+        Notify-Fail gesetzt hat.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.7.
+        LDMud 3.2.8 fuegte den Paramter <flag> hinzu.
+
+SIEHE AUCH
+        add_action(E), query_verb(E), query_command(E), notify_fail(E),
+        hooks(C)
diff --git a/doc/efun/query_verb b/doc/efun/query_verb
new file mode 100644
index 0000000..f216591
--- /dev/null
+++ b/doc/efun/query_verb
@@ -0,0 +1,53 @@
+SYNOPSIS
+        string query_verb();
+        string query_verb(int flag);
+
+BESCHREIBUNG
+        Liefert das Verb des aktuellen Kommandos oder 0, wenn kein Kommando
+        bearbeitet wird.
+
+        Wenn <flag> nicht angegeben oder 0 ist, wird das Verb wie vom User
+        eingegeben geliefert (das ist das erste Wort der Inputzeile des
+        Spielers, bis zum (aber ohne) den ersten Leerschlag / Zeilenumbruch).
+        Wenn <flag> nicht 0 ist, wird das Verb entsprechend der add_action()
+        zurueck gegeben.
+
+        Innerhalb einer add_action()-Funktion, die von mehreren Kommandos
+        aufgerufen wird kann man so zwischen diesen Kommandos unterscheiden.
+
+BEMERKUNGEN
+        Die fruehere Einschraenkung, dass bei geschachtelten Kommandos
+        query-verb() nach dem inneren Kommando 0 zurueck gibt ist jetzt
+        entfallen.
+
+BEISPIEL
+        void init()
+        {
+            ...
+            add_action("sing","singe");
+            add_action("sing","jodel", 1);
+            ...
+        }
+
+        int sing(string str)
+        {
+            write("Das Kommando war:"+query_verb()+(str ? str : "")+"\n");
+            write("Das Verb war:"+query_verb(1)+(str ? str : "")+"\n");
+        }
+
+        Das Kommando "sing blafasel" liefert:
+            Das Kommando war: sing
+            Das Verb war: sing
+
+        Das Kommando "jodel blafasel" liefert:
+            Das Kommando war: jodel
+            Das Verb war: jodel
+
+        Das Kommando "jodele blafasel" liefert:
+            Das Kommando war: jodele
+            Das Verb war: jodel
+
+SIEHE AUCH
+        query_command(E), add_action(E), AddCmd(L), AddAction(L)
+
+7.Aug 2007 Gloinson
\ No newline at end of file
diff --git a/doc/efun/quote b/doc/efun/quote
new file mode 100644
index 0000000..ce01fd7
--- /dev/null
+++ b/doc/efun/quote
@@ -0,0 +1,13 @@
+SYNOPSIS
+        mixed quote(mixed arg);
+
+BESCHREIBUNG
+        Konvertiert ein Array zu einem quoted Array und Strings zu Symbolen.
+        Symbole und quoted Arrays werden erneut gequoted.
+
+BEISPIEL
+        quote("foo") -> 'foo
+        quote(({1,2,3})) -> '({1,2,3})
+
+SIEHE AUCH
+        sympolp(E), unquote(E)
diff --git a/doc/efun/raise_error b/doc/efun/raise_error
new file mode 100644
index 0000000..40ac234
--- /dev/null
+++ b/doc/efun/raise_error
@@ -0,0 +1,23 @@
+SYNOPSIS
+        void raise_error(string arg);
+
+BESCHREIBUNG
+        Bricht die Ausfuehrung des laufenden Programms ab. Wenn das Programm
+        durch catch() aufgerufen wurde, liefert dieses catch() <arg> als
+        Fehlercode, sonst wird <arg> als Fehlermeldung ausgegeben.
+
+        raise_error() gleicht in der Funktion throw(), aber waehrend throw()
+        aus catch() heraus aufgerufen werden soll, kann raise_error() von
+        ueberall her aufgerufen werden.
+
+        Da raise_error() sich wie andere 'echte' Laufzeitfehler verhaelt,
+        einschliesslich der Erzeugung eines Stack Backtraces, ist diese
+        Funktion sehr zeitintensiv.
+
+BEMERKUNGEN
+        Der String sollte umgebrochen oder wenigstens mit einem \n terminiert
+	werden, da die Ausgabe direkt an den interaktiven Magier erfolgen
+	kann bzw. auf dem Debug-Kanal das letzte Zeichen entfernt wird.
+
+SIEHE AUCH
+        catch(E), throw(E)
diff --git a/doc/efun/random b/doc/efun/random
new file mode 100644
index 0000000..a98542b
--- /dev/null
+++ b/doc/efun/random
@@ -0,0 +1,42 @@
+FUNKTION:
+        int random(int n)
+
+ARGUMENTE:
+        n: Zahlen-Bereich aus dem gewaehlt werden soll.
+
+BESCHREIBUNG:
+        Gibt eine zufaellige Zahl im Bereich von 0..(n-1) zurueck.
+
+BEMERKUNGEN:
+        Der Nachteil der Zufaelligkeit (ueber grosse Zahlen) ist, dass wenn
+        viele Zahlen in einem kleinen Bereich in kurzer Zeit generiert
+        werden, einige Zahlen sehr viel haeufiger auftreten als andere.
+
+        Es sei nochmal darauf hingewiesen, dass der Bereich immer bei 0 
+        und NICHT bei 1 anfaengt. Ein random(10) geht also nicht von 
+        1..10 sondern von 0..9!
+
+BEISPIELE:
+        // Einfache Abfrage z.B. aus der HitFunc einer Waffe:
+
+        if(random(101) >= 70) return random(11);
+        else                  return 0;
+
+        // Spieler soll in einen zufaellig ausgewaehlten Raum gemovt
+        // werden: 
+
+        string *dest = ({ "raum1","raum2","raum3","raum4","raum5" });
+        this_player()->move(dest[random(sizeof(dest))],M_GO|M_NOCHECK);
+
+        // Es soll eine zufaellige Meldung ausgegeben werden:
+
+        tell_object(this_player(),
+         ({ "Es knackt.\n", "Dir ist kalt.\n", "Du schwitzt.\n",
+            "Du bekommst Angst.\n", "Hinter Dir knackt es im Gebuesch.\n",
+            "Ein kuehler Wind kommt auf.\n" })[random(6)]);
+
+        Wie man sieht, gibt es fuer random() viele schoene Einsatz-
+        moeglichkeiten. Wobei letzteres Beispiel ueber AddRoomMessage
+        (fuer Raeume) viel einfacher umzusetzen ist.
+
+7.Aug 2007 Gloinson
diff --git a/doc/efun/read_bytes b/doc/efun/read_bytes
new file mode 100644
index 0000000..95cabbd
--- /dev/null
+++ b/doc/efun/read_bytes
@@ -0,0 +1,19 @@
+SYNOPSIS
+        string read_bytes(string file, int start, int anzahl)
+
+BESCHREIBUNG
+        Liest eine bestimmte Anzahl Bytes aus dem File <file>. Wenn <start>
+        nicht angegeben oder 0 ist, wird das File von Beginn weg gelesen,
+        sonst vom Byte mit der Nummer <start>. Wenn <start> negativ ist,
+        werden die Bytes vom Ende des Files her gezaehlt. <anzahl> ist die
+        Anzahl Bytes, die gelesen werden sollen. Werte von 0 oder negative
+        Werte sind zwar moeglich, aber wenig sinnvoll.
+
+        Wenn <start> ausserhalb der Groesse des Files liegt, liefert
+        read_byte() anstelle eines Strings 0 zurueck.
+
+        Die max. Anzahl einzulesender Bytes pro Aufruf dieser Funktion
+        betraegt LIMIT_BYTE (s. query_limits()).
+
+SIEHE AUCH
+        read_file(E), write_bytes(E), write_file(E)
diff --git a/doc/efun/read_file b/doc/efun/read_file
new file mode 100644
index 0000000..7f1ccfc
--- /dev/null
+++ b/doc/efun/read_file
@@ -0,0 +1,19 @@
+SYNOPSIS
+        string read_file(string file, int start, int anzahl)
+
+BESCHREIBUNG
+        Liest Zeilen aus einem File <file>. Wenn <start> angegeben ist und
+        nicht 0, wird von Beginn der Zeile <start> an gelesen; ist <start> 0
+        oder nicht angegeben, wird vom Beginn des Files gelesen.
+
+        Wenn <anzahl> nicht angegeben oder 0 ist, wird das gesamte File
+        gelesen, sonst nur <anzahl> Zeilen.
+
+        Wenn <start> ausserhalb der Groesse des Files liegt, liefert
+        read_file() anstelle eines Strings 0 zurueck.
+
+        Die max. Anzahl einzulesender Bytes (nicht Zeilen!) pro Aufruf dieser
+        Funktion betraegt LIMIT_FILE (s. query_limits()).
+
+SIEHE AUCH
+        read_bytes(E), write_file(E)
diff --git a/doc/efun/referencep b/doc/efun/referencep
new file mode 100644
index 0000000..5c2c0df
--- /dev/null
+++ b/doc/efun/referencep
@@ -0,0 +1,13 @@
+SYNOPSIS
+        int referencep(mixed arg)
+
+BESCHREIBUNG
+        Liefert 1, wenn das Argument an die aktuelle Funktion per Referenz
+        uebergeben wurde, ansonsten 0.
+
+        Man beachte, dass das Argument als Referenz an referencep()
+        uebergeben werden muss, z. B. referencep(&x).
+
+SIEHE AUCH
+        closurep(E), floatp(E), mappingp(E), objectp(E), intp(E),
+        pointerp(E), stringp(E), symbolp(E), clonep(E), references(LPC)
diff --git a/doc/efun/regexp b/doc/efun/regexp
new file mode 100644
index 0000000..354e6a6
--- /dev/null
+++ b/doc/efun/regexp
@@ -0,0 +1,41 @@
+SYNOPSIS
+        #include <regexp.h>
+
+        string *regexp(string *list, string pattern)
+        string *regexp(string *list, string pattern, int opt)
+
+DESCRIPTION
+        Match the pattern <pattern> (interpreted according to <opt> if
+        given) against all strings in list, and return a new array with all
+        strings that matched.
+
+        If there is an error in the regular expression, a runtime
+        error will be raised.
+
+EXAMPLE
+        string strs;
+        string pattern;
+        
+        if (regexp_package() == RE_PCRE)
+            pattern = "\\<help\\>.*\\<me\\>";
+        else
+            pattern = "\\bhelp\\b.*\\bme\\b";
+
+        if (strs = regexp( ({"please, help me Sir John."}),
+                         , pattern
+                         ))
+        {
+           if (sizeof(strs)
+              write("It matches.\n");
+        }
+
+        The regular expression will test the given string (which is
+        packed into an array) if there is something like "help ... me"
+        inside of it.
+
+HISTORY
+        LDMud 3.3 added the optional <opt> argument.
+
+SEE ALSO
+        regexplode(E), regmatch(E), regreplace(E), regexp_package(E), sscanf(E),
+        regexp(C)
diff --git a/doc/efun/regexp_package b/doc/efun/regexp_package
new file mode 100644
index 0000000..8b8d29f
--- /dev/null
+++ b/doc/efun/regexp_package
@@ -0,0 +1,48 @@
+SYNOPSIS
+        #include <regexp.h>
+
+        int regexp_package()
+
+DESCRIPTION
+        Return which regexp package is used by default:
+
+          RE_TRADITIONAL: traditional regexps
+          RE_PCRE:        PCRE
+
+        As the package can be selected at runtime through the
+        REGEXP_PACKAGE driver hook, there is no good way to determine
+        the package at LPC compile time.
+        Match the pattern <pattern> (interpreted according to <opt> if
+        given) against all strings in list, and return a new array with all
+        strings that matched.
+
+        If there is an error in the regular expression, a runtime
+        error will be raised.
+
+EXAMPLE
+        string strs;
+        string pattern;
+        
+        if (regexp_package() == RE_PCRE)
+            pattern = "\\<help\\>.*\\<me\\>";
+        else
+            pattern = "\\bhelp\\b.*\\bme\\b";
+
+        if (strs = regexp( ({"please, help me Sir John."}),
+                         , pattern
+                         ))
+        {
+           if (sizeof(strs)
+              write("It matches.\n");
+        }
+
+        The regular expression will test the given string (which is
+        packed into an array) if there is something like "help ... me"
+        inside of it.
+
+HISTORY
+        Introduced in LDMud 3.3.634.
+
+SEE ALSO
+        regexp(E), regexplode(E), regmatch(E), regreplace(E), sscanf(E),
+        regexp(C), regexp_package(H)
diff --git a/doc/efun/regexplode b/doc/efun/regexplode
new file mode 100644
index 0000000..595e9df
--- /dev/null
+++ b/doc/efun/regexplode
@@ -0,0 +1,28 @@
+SYNOPSIS
+        #include <regexp.h>
+
+        string *regexplode (string text, string pattern)
+        string *regexplode (string text, string pattern, int opt)
+
+DESCRIPTION
+        This function is similar to explode but accepts a regular
+        expression <pattern> as delimiter (interpreted according to <opt>
+        if given).
+
+        If flag RE_OMIT_DELIM is not set in <opt>, then every second element
+        in the result vector will be the text that matched the delimiter.
+        If the flag is set, then the result vector will contain only
+        the text between the delimiters.
+
+EXAMPLES
+        regexplode("abcdef", "cde")                -> ({ "ab", "cde", "f" })
+        regexplode("abcdef", "cde", RE_OMIT_DELIM) -> ({ "ab", "f" })
+
+HISTORY
+        Introduced in 3.2@61
+        LDMud 3.3 added the optional <opt> argument and the RE_OMIT_DELIM
+          flag.
+
+SEE ALSO
+        explode(E), regexp(E), regmatch(E), regreplace(E),
+        regexp_package(E), regexp(C)
diff --git a/doc/efun/regmatch b/doc/efun/regmatch
new file mode 100644
index 0000000..dbb73f5
--- /dev/null
+++ b/doc/efun/regmatch
@@ -0,0 +1,46 @@
+SYNOPSIS
+        #include <regexp.h>
+
+        string  regmatch (string text, string pattern)
+        string  regmatch (string text, string pattern, int opt)
+        string  regmatch (string text, string pattern, int opt, int start)
+        string *regmatch (string text, string pattern, int opt)
+        string *regmatch (string text, string pattern, int opt, int start)
+
+DESCRIPTION
+        Match the string <txt> against <pattern> (interpreted according
+        to <opt> if given). If <start> is given, it is the start
+        position for the match and must be in the range [0..strlen(text)].
+
+        If there is no match, the result is 0. If there is a match, the exact
+        result is determined by the flag RE_MATCH_SUBS:
+
+        If the flag RE_MATCH_SUBS is not set, the result is the matched
+        expression.
+
+        If the flag RE_MATCH_SUBS is set, the result is an array of the
+        matched string(s) of the first match. Entry [0] is the full string
+        matching the <pattern>, following entries are the string segments
+        matching parenthesized subexpressions in <pattern>. If a particular
+        subexpression didn't have a match, the corresponding array entry will
+        be 0.
+
+        The last entry in the array will be the new start index in case you
+        want to repeat the match on the remaining parts of the string. This
+        new index is usually equal the length of the match, but at least one
+        higher than the original start index.
+
+EXAMPLE
+        regmatch("abcdefcdf", "cd")    -> "cd"
+        regmatch("abcdefcdf", "cd(e)") -> "cde"
+
+        regmatch("abcdefcdf", "cd", RE_MATCH_SUBS)    -> ({ "cd" })
+        regmatch("abcdefcdf", "cd(e)", RE_MATCH_SUBS) -> ({ "cde", "e" })
+
+HISTORY
+        Introduced in LDMud 3.3.198.
+        Modified in 3.3.214 to return 0 for non-matches, and to take and
+        return a start position.
+
+SEE ALSO
+        regexplode(E), regreplace(E), regexp(E), regexp_package(E), regexp(C)
diff --git a/doc/efun/regreplace b/doc/efun/regreplace
new file mode 100644
index 0000000..d459bb2
--- /dev/null
+++ b/doc/efun/regreplace
@@ -0,0 +1,57 @@
+SYNOPSIS
+     string regreplace(string txt, string pattern,
+                       string|closure replacepattern, int flags)
+
+BESCHREIBUNG
+     Die Funktion durchsucht den String txt nach einem Vorkommen
+     des regulaeren Ausdrucks pattern, und ersetzt ihn durch
+     den String replacepattern. (replacepattern kann auch eine Closure
+     sein. Sie bekommt als argument den passenden Substring und muss
+     den Ersatzstring zurueckliefern.)
+
+     Im replacestring kann man via '&' auf den zum Suchausdruck
+     passenden Teilstring im Original zugreifen. Mit \n (wobei n
+     eine Ganzzahl ist) kann man auf den n-ten Unterausdruck
+     des regulaeren Ausdrucks zugreifen. Um "&" oder "\\1" als
+     Ersetzung zu erreichen, muss "\\&" bzw "\\\\1" verwendet werden.
+
+     Beim Flag bestimmt ein gesetztes Bit 0, dass der Ausdruck so oft
+     wie vorhanden ersetzt wird, sonst nur das erste Auftreten.
+     Bit 1 bestimmt, ob der regulaere Ausdruck 'ed' oder 'sed' kompatibel
+     ist (Bit geloescht) oder 'ex' kompatibel (gesetzt).
+
+     Die Funktion wirkt wie s/pattern/replacepattern/flags in
+     Editoren wie vi oder sed. Sie ist besonders gut geeignet um
+     variable Strings zu ersetzen, im Gegensatz zu regexplode, wo
+     man den String nur nach einem regulaeren Ausdruck zerlegen kann.
+
+BEISPIELE
+     // ersetzt @@wer@@ durch den Namen des Objektes
+     regreplace(str,"@@wer@@",(string)obj->name(WER,2),1)
+
+     // ersetze alle doppelten Leerzeichen durch ein einzelnes
+     regreplace(str, "  *", " ", 1);
+
+     // Sucht nach 'teilt Dir mit: ' und schliesst den nachfolgenden
+     // Text mit <underline> und </underline> ein; bei jedem Auftreten.
+
+     msgin = regreplace(msgin, "teilt Dir mit: (.*)",
+                               "teilt Dir mit: <underline>\\1</underline>",
+                               1);
+
+     // Ersetzt die <underline> html-Tags durch die vt100
+     // Escape-Sequenzen fuer Unterstreichung
+     txt = regreplace(txt, "<underline>", "<ESC>[5m", 1);
+
+     // Ersetzt das Wort HOUSE in Kleinbuchstaben.
+     txt = regreplace(txt, "HOUSE", #'lower_case, 1);
+
+BUGS:
+     I have written that efun. It doesn't have BUGS. By definition.
+     Well, in fact it crashed as I first tried it on running TAPPMud.
+     *sigh*
+     Marcus@TAPPMud
+
+SIEHE AUCH
+     Verwandt: regexp(E), regexplode(E), sscanf(E)
+     Aehnlich: replace_personal(L)
diff --git a/doc/efun/remove_action b/doc/efun/remove_action
new file mode 100644
index 0000000..b939a51
--- /dev/null
+++ b/doc/efun/remove_action
@@ -0,0 +1,21 @@
+SYNOPSIS
+        int remove_action(int|string verb);
+        int remove_action(int|string verb, object obj);
+
+BESCHREIBUNG
+        Wenn <verb> ein String ist: entferne das erste im Objekt <obj> durch
+        add_action() definierte Kommando <verb>. Wenn <obj> nicht
+        angegeben wird, wird standardmaessig this_player() verwendet.
+        Gib 1 zurueck, wenn ein Kommando gefunden und entfernt wurde,
+        sonst 0.
+
+        Wenn <verb> ein Integer ist: wenn verb nicht 0 ist, entferne alle
+        durch das Objekt <obj> definierten Kommandi. Wenn <obj> nicht
+        angegeben wird, wird standardmaessig this_player() verwendet. Gibt
+        die Anzahl der entfernten Kommandi zurueck.
+
+SIEHE AUCH
+     Verwandt:      add_action(E), init(E)
+     Alternativ:	AddAction(L), AddCmd(L)
+
+7.Aug 2007 Gloinson
diff --git a/doc/efun/remove_call_out b/doc/efun/remove_call_out
new file mode 100644
index 0000000..d5a22db
--- /dev/null
+++ b/doc/efun/remove_call_out
@@ -0,0 +1,32 @@
+SYNOPSIS
+        int remove_call_out(string fun);
+        int remove_call_out(closure fun);
+
+BESCHREIBUNG:
+        Entfernt den naechsten laufenden call_out() auf die Funktion <fun>
+        im aktuellen Objekt bzw. den naechsten laufenden call_out() auf die
+        Closure <fun>. Die verbleibende Zeit wird zurueckgeliefert.
+
+        Wenn es keine laufenden call_out()s auf gibt, wird -1 zurueck
+        geliefert.
+
+BEISPIEL
+        Um alle call_out()s auf MeineFunktion() zu entfernen:
+
+        while(remove_call_out("MeineFunktion") != -1);      /* wiederhole */
+
+BUGS
+        Das Entfernen von call_out()s auf Closures funktioniert nur, wenn der
+        exakt gleiche Wert fuer die Closure verwendet wird.
+
+        Das funktioniert:
+            closure cl = symbol_function("main", obj);
+            call_out(cl, 2);
+            remove_call_out(cl);
+
+        Das funktioniert nicht:
+            call_out(symbol_function("main", obj), 2);
+            remove_call_out(symbol_function("main", obj));
+
+SIEHE AUCH
+        call_out(E), call_out_info(E), find_call_out(E)
diff --git a/doc/efun/remove_input_to b/doc/efun/remove_input_to
new file mode 100644
index 0000000..7cd9393
--- /dev/null
+++ b/doc/efun/remove_input_to
@@ -0,0 +1,38 @@
+SYNOPSIS
+        int remove_input_to (object player);
+        int remove_input_to (object player, string fun);
+        int remove_input_to (object player, closure fun);
+        int remove_input_to (object player, object fun);
+        int remove_input_to (object player, object obj, string fun);
+
+BESCHREIBUNG
+        Entfernt ein haengiges input_to() aus dem interaktiven Playerobjekt.
+        Wenn <fun> nicht angegeben ist, wird der zuletzt aufgerufen input_to()
+        entfernt.
+
+        Wurde <fun> angegeben, entfernt remove_input_to das neueste auf <fun>
+        aufgerufene input_to. Je nach der Definition von <fun> wird nach
+        unterschiedlichen Kriterien gesucht:
+          - <fun> ist ein String: gesucht wird nach dem Funktionsnamen
+          - <fun> ist ein Objekt: gesucht wird nach dem Objekt, in dem
+            input_to() aufgerufen wurde
+          - <fun> ist eine Closure: gesucht wird nach der Closure, die
+            input_to() enthaelt
+          - <obj> und <fun> wurden angegeben: es wird nach Objekt und
+            Funktionsname gesucht. Beide muessen uebereinstimmen.
+
+        remove_input_to() liefert 1 bei Erfolg, sonst 0 (es wurde kein
+        input_to() gefunden, das Objekt ist nicht interaktiv oder es gibt
+        kein haengiges input_to() auf dem Objekt).
+
+BEISPIELE
+        Entfernt alle haengigen input_to()s des aktuellen Spielers, falls
+        vorhanden:
+            while(remove_input_to(this_interactive()));
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9.
+
+SIEHE AUCH
+        input_to(E), find_input_to(E), input_to_info(E),
+        query_input_pending(E)
diff --git a/doc/efun/remove_interactive b/doc/efun/remove_interactive
new file mode 100644
index 0000000..0d6d200
--- /dev/null
+++ b/doc/efun/remove_interactive
@@ -0,0 +1,13 @@
+SYNOPSIS
+        void remove_interactive(object ob)
+
+DESCRIPTION
+        Close the connection to the interactive object ob.
+
+        For the time of the LPC execution, the object is only marked
+        internally as 'about to be closed', meaning that while all
+        output will be redirected to stdout, the actual network connection
+        will continue to exist until the LPC execution ends.
+
+SEE ALSO
+        connect(M), logon(A), disconnect(M)
diff --git a/doc/efun/rename b/doc/efun/rename
new file mode 100644
index 0000000..2f74492
--- /dev/null
+++ b/doc/efun/rename
@@ -0,0 +1,25 @@
+SYNOPSIS
+        int rename(string from, string to);
+
+BESCHREIBUNG
+        Die Efun rename() verschiebt <from> nach <to>. Wenn <from> ein File
+        ist, kann <to> entweder ein andere File oder ein Verzeichnis sein.
+        Wenn <from> ein Verzeichnis ist, muss <to> auch ein Verzeichnis sein.
+        Wenn in diesem Fall <to> existiert und ein Verzeichnis ist, wird
+        <from> in <to> verschoben und behaelt seinen Namen.
+
+        <from> umzubenennen erfordert Schreibrechte auf <from>.
+
+        Unterverzeichnisse (Verzeichnisse in Verzeichnissen) koennen nur auf
+        Maschinen umbenannt werden, die unter System V laufen, d.h. es ist
+        nicht moeglich, diese in ein anderes Verzeichnis zu verschieben. Das
+        Verschieben von Verzeichnissen von einem Filesystem zum andreren ist
+        unter keinem System moeglich.
+
+        Bei Erfolg liefert rename() 0, bei Fehlschlag einen Wert ungleich 0.
+
+BEISPIEL
+        rename("/players/wizard/obj.c", "/players/wizard/newobj.c");
+
+SIEHE AUCH
+        copy_file(E), mkdir(E), rmdir(E), rm(E)
diff --git a/doc/efun/rename_object b/doc/efun/rename_object
new file mode 100644
index 0000000..2d31674
--- /dev/null
+++ b/doc/efun/rename_object
@@ -0,0 +1,10 @@
+SYNOPSIS:
+	void rename_object (object ob, string new_name);
+
+DESCRIPTION:
+	Give the current object a new object_name. Causes a privilege
+	violation. The new name must not contain a # character, except
+	at the end, to avoid confusion with clone numbers.
+
+SEE ALSO:
+	creator(E), object_name(E)
diff --git a/doc/efun/replace_program b/doc/efun/replace_program
new file mode 100644
index 0000000..4812d4e
--- /dev/null
+++ b/doc/efun/replace_program
@@ -0,0 +1,52 @@
+SYNOPSIS
+        void replace_program();
+        void replace_program(string program);
+
+BESCHREIBUNG
+        Ersetzt ein Programm mit dem geerbten Programm (inherit) <program>.
+        Das Argument <program> kann weggelassen werden, wenn nur ein Programm
+        geerbt wird. In diesem Fall waehlt der Treiber automatisch dieses eine
+        geerbte Programm.
+
+        Diese Efun ist nuetzlich, wenn es um Leistung und Speicherverbrauch
+        des Treibers geht. Ein Programm, welches keine zusaetzlichen Variablen
+        oder Funktionen braucht (ausser waehrend der Erstellung), kann
+        replace_program() aufrufen, um die Trefferquote des Treibers auf den
+        Funktionen-Caches zu erhoehen. Dies verringert die Anzahl Programme
+        im System.
+
+        Raeume sind ein gutes Beispiel fuer die Anwendung dieser Funktion, da
+        viele Raeume nur aus einem Inherit und einer Konfigurationsfunktion
+        bestehen. Jedes Objekt kann replace_program() aufrufen, verliert dabei
+        jedoch alle Variablen und Funktionen, die nicht im geerbten Programm
+        definiert sind.
+
+        Wenn replace_program() angewendet wird, werden Shadows vom Objekt
+        entfernt, in dem replace_program() stattfindet. Dies ist so seit
+        3.2@166.
+
+        Es ist nicht moeglich, replace_program() aufzurufen, wenn es an das
+        Objekt gebundene (Lambda-) Closures gibt. Hingegen ist es moeglich,
+        zuerst das Programm des Objekts zu ersetzen und dann Lambdaclosures
+        daran zu binden.
+
+        Das Ersetzen des Programms findet erst statt, wenn das Objekt
+        vollstaendig abgearbeitet wurde, nicht schon beim Aufruf der Funktion
+        replace_program(). Das kann dazu fuehren, dass Closures auf inzwischen
+        verschwundene Lfuns des Objekts referenzieren. Das stellt allerdings
+        kein Problem dar, solange diese Referenzen nicht ausgefuehrt werden.
+
+BEMERKUNGEN
+        Raeume benutzen replace_program automatisch. Es ist nicht sinnvoll
+        oder notwendig, replace_program selbst ohne Konsultaton mit einem
+        Erzmagier einzusetzen!
+
+BUGS
+        Wenn ein ersetzendes Programm virtuell geerbte Variablen enthaelt,
+        muss dieses Programm als erstes geerbt werden. Ohne diese
+        Einschraenkung ueberleben die falschen Variablen den
+        Ersetzungsprozess.
+
+AENDERUNGEN
+        LDMud 3.2.9 liess zu, dass das Argument <program> weggelassen wird,
+        wenn nur ein Inherit existiert.
diff --git a/doc/efun/restore_object b/doc/efun/restore_object
new file mode 100644
index 0000000..fa991d6
--- /dev/null
+++ b/doc/efun/restore_object
@@ -0,0 +1,45 @@
+SYNOPSIS
+        int restore_object(string name);
+        int restore_object(string str);     (VORLAEUFIG)
+
+BESCHREIBUNG
+        Laedt Werte von Variablen fuer das aktuelle Objekt aus der Datei
+        <name> oder direkt aus dem String <str>.
+
+        Um direkt aus einem String Variablen laden zu koennen, muss dieser
+        mit der typischen Zeile "#x:y" beginnen. Strings, die von der Efun
+        save_object() erzeugt wurden, beginnen so.
+
+        Wenn Variablen aus einer Datei geladen werden, kann <name> mit .c
+        enden. Diese Dateiendung wird vom Treiber entfernt. Das Masterobjekt
+        fuegt dem Dateinamen ein .o hinzu. Die Gueltigkeit des Dateinamens
+        wird mit der Funktion check_valid_path() automatisch ueberprueft.
+
+        Die Funktion gibt 1 zurueck, wenn die Variablen erfolgreich geladen
+        wurden und 0, wenn es nichts zu laden gab.
+
+        Variablen mit dem Typenvermerk nosave werden nicht geladen, zum
+        Beispiel nosave int xxx;
+
+        Closures aus Lfuns, Variablne und simul_efuns werden nur geladen,
+        wenn sie gefunden werden. Falls nicht, werden sie mit dem Wert '0'
+        geladen.
+
+        Wenn Vererbung verwendet wird, kann es vorkommen, dass mehrere
+        Variablen mit dem gleichen Namen an unterschiedlichen Orten vorkommen
+        und deshalb in der Speicherdatei mehrmals auftreten. Beim Laden der
+        Variablen werden sie in Reihenfolge ihres Auftretens im Vererbungsbaum
+        geladen. Ein geeignetes Vorgehen ist die Verwendung von Verbose und
+        einzigartigen Namen bei nicht-statischen Variablen. So wird auch das
+        manuelle Lesen oder Editieren des Savefiles einfacher.
+
+AENDERUNGEN
+        Das direkte Laden aus einem String wurde in LDMud 3.2.8 eingefuehrt,
+            wird aber moeglicherweise in Zukunft in eine separate Efun
+            ausgelagert.
+        LDMud 3.2.9 ergaenzte die Funktion um die Moeglichkeit,
+            NonLambda-Closures, Symbole und gequotete Arrays zu laden,
+            indem ein neues Format fuer das Savefile verwendet wird.
+
+SIEHE AUCH
+        save_object(E), restore_value(E), valid_read(M)
diff --git a/doc/efun/restore_value b/doc/efun/restore_value
new file mode 100644
index 0000000..fa44b18
--- /dev/null
+++ b/doc/efun/restore_value
@@ -0,0 +1,17 @@
+SYNOPSIS
+        mixed restore_value(string str);
+
+BESCHREIBUNG
+        Wandelt die String-Entsprechung <str> eines Wertes zurueck in den Wert
+        selbst und gibt diesen Wert zurueck. <str> ist ein String, wie er von
+        save_value() erzeugt wird. Die Spezifikation des Speicherformates
+        '#x:y' ist optional.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.8.
+        Mit LDMud 3.2.9 kam das Laden von Non-Lambda Closures, Symbolen und
+        gequoteten Arrays hinzu, wozu ein neues Format fuer die Speicherdatei
+        verwendet wird.
+
+SIEHE AUCH
+        save_value(E), restore_object(E), save_object(E)
diff --git a/doc/efun/reverse b/doc/efun/reverse
new file mode 100644
index 0000000..90c7890
--- /dev/null
+++ b/doc/efun/reverse
@@ -0,0 +1,31 @@
+SYNOPSIS
+        inti    reverse (int arg)
+        string  reverse (string arg)
+        mixed * reverse (mixed * arg)
+        mixed * reverse (mixed * & arg)
+
+DESCRIPTION
+        Kehrt die Reihenfolge des Inhaltes von Array oder String <arg>
+        um und liefert den neuen Wert als Resultat.
+        Ist <arg> eine Zahl, wird die Reihenfolge der Bits in <arg> umgekehrt.
+
+        Wenn in der Referenz-Variante verwendet, wird das Argumentarray selber
+        invertiert und auch zurueckgegeben.
+
+EXAMPLES
+        reverse (0x306a) - return 0x560c0000
+
+        reverse ("test") - return "tset"
+
+        mixed * a = ({ 1, 2 });
+        reverse(a)  - returns ({ 2, 1 }), a ist unveraendert.
+        reverse(&a) - returns ({ 2, 1 }), a ist nun ({ 2, 1 })
+
+BUGS
+        Referenz-Teilarrays wie reverse(&(a[1..2])) sind nicht unterstuetzt.
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.3.529.
+        LDMud 3.3.532 fuegte die Bitumkehr von Zahlen ein.
+
+SEE ALSO
diff --git a/doc/efun/rm b/doc/efun/rm
new file mode 100644
index 0000000..d1b59e3
--- /dev/null
+++ b/doc/efun/rm
@@ -0,0 +1,9 @@
+SYNOPSIS
+        int rm(string file);
+
+BESCHREIBUNG
+        Loescht die Datei <file>. Gibt 0 zurueck, wenn etwas nicht geklappt
+        hat, 1 bei Erfolg.
+
+SIEHE AUCH
+        mkdir(E), rmdir(E), rename(E)
diff --git a/doc/efun/rmdir b/doc/efun/rmdir
new file mode 100644
index 0000000..19af329
--- /dev/null
+++ b/doc/efun/rmdir
@@ -0,0 +1,8 @@
+SYNOPSIS
+        int rmdir(string dir);
+
+BESCHREIBUNG
+        Loescht das Verzeichnis <dir>. Liefert 1 bei Erfolg, 0 bei Misserfolg.
+
+SIEHE AUCH
+        mkdir(E), rm(E), rename(E)
diff --git a/doc/efun/rmember b/doc/efun/rmember
new file mode 100644
index 0000000..dc1962c
--- /dev/null
+++ b/doc/efun/rmember
@@ -0,0 +1,20 @@
+SYNOPSIS
+        int rmember(mixed *array, mixed elem [, int start]);
+        int rmember(string str, int elem [, int start]);
+
+BESCHREIBUNG
+        Liefert fuer Arrays und Strings den Index des letzten Auftretens des
+        Arguments <elem> im Array <*array>  bzw. im String <str>. Wenn <elem>
+        nicht gefunden wird, liefert die Funktion -1 zurueck.
+
+        Ist <start> als Zahl >= 0 gegeben, beginnt die Suche ab der
+        angegebenen Position. Eine Startposition groesser als die
+        Laenge des Strings/Arrays liefert stets das Resultat -1.
+
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.10.
+        LDMud 3.3.556 fuegte den <start>-Parameter hinzu.
+
+SIEHE AUCH
+        member(E)
diff --git a/doc/efun/rusage b/doc/efun/rusage
new file mode 100644
index 0000000..8dc2523
--- /dev/null
+++ b/doc/efun/rusage
@@ -0,0 +1,18 @@
+OPTIONAL
+SYNOPSIS
+        int *rusage();
+
+BESCHREIBUNG
+        Liefert ein Array mit Informationen ueber den momentanen
+        Systemzustand; Benutzungsstatistiken, wie sie vom Unix-Kommando
+        getrusage(2) generiert werden, namentlich:
+            utime, stime, maxrss, rus.ru_ixrss, rus.ru_idrss, rus.ru_isrss,
+            rus.ru_minflt, rus.ru_majflt, rus.ru_nswap, rus.ru_inblock,
+            rus.ru_oublock, rus.ru_msgsnd, rus.ru_msgrcv, rus.ru_nsignals,
+            rus.ru_nvcsw, rus.ru_nivcsw
+
+ANMERKUNG
+    Diese Funktion ist optional.
+
+SIEHE AUCH
+        sys/resource.h(Unix)
diff --git a/doc/efun/save_object b/doc/efun/save_object
new file mode 100644
index 0000000..681946b
--- /dev/null
+++ b/doc/efun/save_object
@@ -0,0 +1,54 @@
+SYNOPSIS
+        int save_object(string name [, int format]);
+        string save_object([int format]);
+
+BESCHREIBUNG
+        Codiert die speicherbaren (s.u.) Variablen des aktuellen Objekts in
+        einen String.
+
+        In der ersten Form wir der String in die Datei <name> geschrieben. Eine
+        Endung ".c" in <name> wird entfernt, dafuer kann eine Endung ".o"
+        durch das Masterobjekt angefuegt werden, waehrend der Ueberpruefung
+        durch valid_read(). Die Efun save_object() liefert 0, wenn die
+        Speicherdatei erfolgreich erstellt wurde, sonst eine Zahl ungleich 0,
+        wenn ein nicht schwerwiegendee Fehler aufgetreten ist (die Datei
+        konnte nicht geschrieben werden oder das aktuelle Objekt wurde
+        inzwischen zerstoert).
+
+        In der zweiten Form wird der String direkt zurueck gegeben. Wenn das
+        Objekt zerstoert wurde, wird 0 zurueck gegeben. In beiden Faellen kann
+        durch das optionale Argument <format> das Format der Speicherdatei
+        angegeben werden:
+
+            -1: das normale Format des Treibers (Standard)
+             0: das Originalformat nach Amylaar's LPMud und LDMud <=3.2.8
+             1: LDMud >= 3.2.9: Non-Lambda Closures, Symbole und gequotete
+                Arrays koennen gespeichert werden
+
+        Eine Variable wird als 'speicherbar' betrachtet, wenn sie nicht als
+        'nosave' oder 'static' deklariert ist.
+
+        Bitte beachten, dass diese efun im MG durch eine Sefun ueberschrieben
+        wird und deren Manpage auch lesen!
+
+BEMERKUNGEN:
+        Damit ein Objekt in Verzeichnisse der Region/des Magiers schreiben
+        kann, muss es entsprechende Rechte, also eine gueltige EUID
+        besitzen. Im create() sollte daher ein:
+
+        seteuid(getuid(this_object()));
+
+        stehen.
+
+AENDERUNGEN
+        Seit LDMud 3.2.8 liefert save_object() einen fixen Wert bei Erfolg.
+        Das direkte Abspeichern in einen String wurde in LDMud 3.2.8
+            eingefuehrt, wird aber in Zukunft eventuell in eine separate Efun
+            umgezogen.
+        LDMud 3.2.9 ergaenzte die Funktion um das Speichern von Non-Lambda
+            Closures, Symbolen und gequoteten Arrays. Dazu wurde ein neues
+            Format fuer die Speicherdatei eingefuehrt.
+        LDMud 3.2.10 fuehrte das Argument <format> ein.
+
+SIEHE AUCH
+        restore_object(E), save_value(E), save_object (Sefun)
diff --git a/doc/efun/save_value b/doc/efun/save_value
new file mode 100644
index 0000000..d9e1486
--- /dev/null
+++ b/doc/efun/save_value
@@ -0,0 +1,31 @@
+SYNOPSIS
+        string save_value(mixed wert [, int format]);
+
+BESCHREIBUNG
+        Schreibt <wert> in einen String, der sich mit restore_value()
+        auswerten laesst. Der String wird zurueck gegeben.
+
+        Das optionale Argument <format> bestimmt das Format des Strings:
+
+            -1: das normale Format des Treibers (Standard)
+             0: das Originalformat nach Amylaar's LPMud und LDMud <=3.2.8
+             1: LDMud >= 3.2.9: Non-Lambda Closures, Symbole und gequotete
+                Arrays koennen gespeichert werden
+
+        Der erzeugte String besteht aus zwei Zeilen, die jeweils mit einem
+        Zeilenumbruch enden. Die erste Zeile beschreibt das Format, in dem der
+        Wert gespeichert wird, in der '#x:y'-Schreibweise. Die zweite Zeile
+        stellt den eigentlichen Wert da.
+
+        Das Format zum Schreiben des Wertes in den String entspricht dem von
+        save_object() und restore_object() verwendeten Format.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.8.
+        LDMud 3.2.9. ergaenzte die Funktion um die Moeglichkeit, Non-Lambda
+            Closures, Symbole und gequotete Arrays zu speichern. Dazu wurde
+            ein neues Format fuer den String eingefuehrt.
+        LDMud 3.2.10 fuehrte das Argument <format> ein.
+
+SIEHE AUCH
+        restore_value(E), restore_object(E), save_object(E)
diff --git a/doc/efun/say b/doc/efun/say
new file mode 100644
index 0000000..6f0958f
--- /dev/null
+++ b/doc/efun/say
@@ -0,0 +1,98 @@
+SYNOPSIS
+        void say(string str);
+        void say(string str, object exclude);
+        void say(string str, object *excludes);
+
+        void say(mixed *|mapping|struct|object msg);
+        void say(mixed *|mapping|struct|object msg, object exclude);
+        void say(mixed *|mapping|struct|object msg, object *excludes);
+
+BESCHREIBUNG
+        Es bestehen zwei Hauptanwendungen fuer say():
+
+        Wenn das erste Argument ein String <str> ist, wird er an alle
+        lebendigen Objekte im aktuellen Raum gesendet, ausser zum Objekt,
+        das die Funktion aufgerufen hat.
+
+        Ist die Nachricht ein String, wird der Text an interaktive Objekte
+        direkt ausgegeben, fuer andere Objekte wird die lfun catch_tell()
+        in diesen aufgerufen.
+        Falls ein Lewebesen die Funktion catch_tell() definiert (-> shadow),
+        so wird der Text hier ausgewertet und nicht an den User ausgegeben.
+
+        Wenn das zweite Argument, die Nachricht, kein String ist, wird in
+        allen Lebewesen, die den Text erhalten, catch_msg() anstatt
+        catch_tell() aufgerufen.
+
+        Mit dem Array <*exclude> kann man verhindern, dass die Nachricht an
+        die darin enthaltenen Objekte gesendet wird. Der Sender muss nicht
+        noch einmal aufgefuehrt werden.
+        Das ist sinnvoll, wenn zB eine Spielergruppe Ausloeser einer Meldung
+        ist und diese selbst nicht erhalten soll.
+
+        Das aufrufende Objekt wird nach folgenden Regeln bestimmt:
+          - Wenn say() aus einem lebendigen Objekt aufgerufen wird, gilt
+            dieses als das aufrufende Objekt.
+          - Wenn say() aus einem nicht-lebendigen Objekt als Resultat einer
+            Handlung eines Benutzers aufgerufen wird (das heisst,
+            this_player() ist gueltig), gilt this_player() als aufrufendes
+            Objekt.
+          - In allen anderen Faellen (Aufruf aus reset zB) gilt das Objekt,
+            das say() aufgerufen hat, als aufrufendes Objekt.
+
+BEMERKUNGEN
+        - fuer laengere Meldungen sollte break_string() verwendet werden
+        - wird in einem catch_msg() der Wert von <msg> veraendert, erhalten
+          alle nachfolgenden Objekte das veraenderte <msg> (Referenz!)
+
+        - say(<str>) ist verhaltensgleich zu
+          tell_room(environment(), <str>, ({this_player()||this_object()}))
+
+BEISPIELE
+        // Folgende Aufrufe sind gleich, wenn sie durch ein Spielerkommando
+        // in einem nicht lebendigen Objekt aufgeloest werden:
+
+            say("Hi!\n");
+            say("Hi!\n", this_player());
+
+        // Das folgende Beispiel zeigt, wie say() zusammen mit catch_tell()
+        // funktioniert. Das zweite Objekt darf nicht lebendig sein, damit
+        // write() an den aktuellen Benutzer geht.
+
+        Objekt 1 (living):
+            void catch_tell(string str) {
+                write("Empfangen: "+str+"\n");
+            }
+
+        Objekt 2 (nicht living):
+            void func() {
+                ...
+                say("HiHo!\n");
+                ...
+            }
+
+        // Ein etwas komplexeres Beispiel zeigt das Zusammenspiel von say()
+        // und catch_msg(). Auch hier wird ein nicht-lebendiges Objekt
+        // verwendet, das die Nachricht ausgibt, sodass das 'wer' in
+        // catch_msg() auf den aktuellen Benutzer zeigt.
+
+        Object 1 (living):
+            void catch_msg(mixed *arr, object who) {
+                foreach(mixed element: arr)
+                    tell_object(who, sprintf("%O\n", element));
+            }
+
+        Object 2 (nicht living):
+            void func() {
+                ...
+                say( ({ "Hello", "there!" }) );
+                ...
+            }
+
+SIEHE AUCH
+        Aehnlich:   tell_room(E), write(E), shout(E), tell_object(E),
+                    printf(E)
+        Verwandt:   catch_tell(E), catch_msg(A)
+        Sonstiges:  object_name(E)
+
+7.Aug 2007 Gloinson
\ No newline at end of file
diff --git a/doc/efun/send_erq b/doc/efun/send_erq
new file mode 100644
index 0000000..d3521e1
--- /dev/null
+++ b/doc/efun/send_erq
@@ -0,0 +1,24 @@
+GESCHUETZT
+SYNOPSIS
+        int send_erq(int request, string|int *data, closure callback);
+
+BESCHREIBUNG
+        Eine Anfrage vom Typ <request> (standardmaessig 0) wird mit Inhalt
+        <data> word an den ERQ gesandt. Wenn <callback> angegeben ist,
+        wird diese Closure aufgerufen, wenn eine Antwort vom ERQ eintrifft
+        (ein Status Code), vorausgesetzt die Antwort enthaelt ausreichend
+        Daten, um damit zu arbeiten:
+
+            void <closure>(int *response_data, int len);
+
+        <data> kann entweder ein String oder ein Array von Integers sein,
+        wobei im zweiten Fall die Zahlen als Zeichen interpretiert werden.
+        Die unterschiedlichen Anfragen sind in /sys/erq.h definiert.
+
+        Die Funktion liefert 0 zurueck, wenn das Senden fehlgeschlagen ist,
+        etwas anderes sost.
+
+        Die Funktion verursacht eine Schutzverletzung "erq".
+
+SIEHE AUCH
+        attach_erq_demon(E), erq(C)
diff --git a/doc/efun/send_udp b/doc/efun/send_udp
new file mode 100644
index 0000000..8ad0c33
--- /dev/null
+++ b/doc/efun/send_udp
@@ -0,0 +1,12 @@
+SYNOPSIS:
+	int send_udp(string host, int port, string message)
+
+DESCRIPTION:
+	Sends The message in an UDP packet to the given host and port
+	number. Causes a privilege violation.
+	Returns 1 on success, 0 on failure.
+
+SEE ALSO:
+	receive_udp(M)
+
+29.10.2006 Zesstra
diff --git a/doc/efun/set_auto_include_string b/doc/efun/set_auto_include_string
new file mode 100644
index 0000000..247e50d
--- /dev/null
+++ b/doc/efun/set_auto_include_string
@@ -0,0 +1,19 @@
+DEPRECATED
+SYNOPSIS:
+	void set_auto_include_string(string arg)
+
+DESCRIPTION:
+
+	The arg will be automatically included into every compiled LPC
+	object. This is useful to enforce global definitions, e.g.
+	``#pragma combine_strings'' or ``#pragma strict_types''.  The
+	calling objectneeds to be privileged by the master object.
+
+NOTE:
+  Not available in LDMud 3.3.x
+  Please use the driver hook H_AUTO_INCLUDE in LDMud > 3.2.9
+
+SEE ALSO:
+	privilege_violation(M), pragma(LPC)
+  
+29.10.2006 Zesstra
diff --git a/doc/efun/set_bit b/doc/efun/set_bit
new file mode 100644
index 0000000..847c6b5
--- /dev/null
+++ b/doc/efun/set_bit
@@ -0,0 +1,30 @@
+SYNOPSIS
+        string set_bit(string str, int n);
+
+BESCHREIBUNG
+        Liefert einen neuen String, bei dem das Bit <n> in <str> gesetzt
+        ist. Dabei wird der urspruengliche String <str> nicht veraendert.
+
+        Jedes Zeichen enthaelt 6 Bits. In jedem Zeichen kann deshalb eine
+        Zahl von 0 bis 63 gespeichert werde (2^6=64). Das erste Zeichen
+        ist der Leerschlag " " mit dem Wert 0. Das erste Zeichen im String
+        ist jenes mit den niedrigsten Bits (0-5).
+
+        Der neue String wird automatisch verlaengert, falls noetig.
+
+BEISPIEL
+        string s;
+        s=set_bit("?",5);
+
+        Weil "?" einen Wert von 31 hat, ist das 6. Bit nicht gesetzt. Wird
+        es gesetzt, so ergibt sich "_". Der String s enthaelt nun also "_".
+
+        string s;
+        s=set_bit("78",3);
+        s=set_bit(s,8);
+
+        s enthaelt nun "?<".
+
+SIEHE AUCH
+        clear_bit(E), last_bit(E), next_bit(E), test_bit(E), count_bits(E),
+        and_bits(E), or_bits(E), xor_bits(E), invert_bits(E), copy_bits(E)
diff --git a/doc/efun/set_driver_hook b/doc/efun/set_driver_hook
new file mode 100644
index 0000000..5489120
--- /dev/null
+++ b/doc/efun/set_driver_hook
@@ -0,0 +1,98 @@
+SYNOPSIS
+        void set_driver_hook(int what, closure arg)
+        void set_driver_hook(int what, string arg)
+        void set_driver_hook(int what, string * arg)
+
+DESCRIPTION
+        This privileged efun sets the driver hook 'what' (values are
+        defined in /sys/driver_hook.h) to 'arg'.
+        The exact meanings and types of 'arg' depend of the hook set.
+        To remove a hook, set 'arg' to 0.
+
+        These hooks exist:
+
+        H_MOVE_OBJECT0
+        H_MOVE_OBJECT1
+          arg: unbound lambda
+          Mandatory hooks implementing the move_object() efun.
+
+        H_LOAD_UIDS
+        H_CLONE_UIDS
+          arg: unbound lambda or lfun closure
+          Mandatory hooks to determine the (e)uid of new objects.
+
+        H_CREATE_SUPER
+        H_CREATE_OB
+        H_CREATE_CLONE
+        H_RESET
+        H_CLEAN_UP
+          arg: lambda closure (H_CLEAN_UP also accepts a lfun
+            closure), function name.
+          Optional hooks for creation/reset/clean up-actions.
+
+        H_DEFAULT_METHOD
+          arg: lambda closure, lfun closure, function name.
+          Optional hook for default method implementation.
+
+        H_DEFAULT_PROMPT
+          arg: lambda closure, lfun closure, prompt string.
+          Optional hook for the default command prompt.
+
+        H_PRINT_PROMPT
+          arg: lambda closure, lfun closure, function name.
+          Optional hook to print the command prompt.
+
+        H_MODIFY_COMMAND
+          arg: lambda closure, lfun closure, function name, mapping
+          Optional hook for modifying player commands before the
+          parser sees them.
+
+        H_NOTIFY_FAIL
+          arg: lambda closure, lfun closure, string.
+          Mandatory hook to generate the default message if an entered
+          command couldn't be parsed and no notify_fail() command is
+          in effect.
+
+        H_SEND_NOTIFY_FAIL
+          arg: lambda closure, lfun closure, string.
+          Optional hook to deliver the notify fail message from a failed
+          command.
+
+        H_NO_IPC_SLOT
+          arg: string.
+          Optional hook specifying the 'sorry' messages if logins are
+          rejected due to fullness of the mud.
+
+        H_INCLUDE_DIRS
+          arg: lambda closure, lfun closure, string array.
+          Semi-mandatory hook specifying the directories where <>-type
+          include files are searched.
+
+        H_AUTO_INCLUDE
+          arg: lambda closure, lfun closure, string
+          Optional hook to specify a string to be included before the
+          source of every compiled LPC object.
+
+        H_TELNET_NEG
+          arg: lambda closure, lfun closure, string.
+          Optional hook to specifiy how to perform a single telnet
+          negotiation.
+
+        H_NOECHO
+          arg: lambda closure, lfun closure, string.
+          Optional hook to specifiy how to perform the telnet actions
+          to switch the echo mode (used for e.g. password input_to()s).
+
+        H_ERQ_STOP
+          arg: lambda closure, lfun closure.
+          Optional hook to notify the mudlib about the termination of
+          the erq demon.
+
+        See hooks(C) for a detailed discussion.
+
+HISTORY
+        Introduced in 3.2.1@1 as efun309(), renamed to
+        set_driver_hook() in 3.2.1@13
+
+SEE ALSO
+        hooks(C)
diff --git a/doc/efun/set_environment b/doc/efun/set_environment
new file mode 100644
index 0000000..fe3a969
--- /dev/null
+++ b/doc/efun/set_environment
@@ -0,0 +1,20 @@
+SYNOPSIS
+	void set_environment(object item, object env)
+
+DESCRIPTION
+	The item is moved into its new environment env, which may be 0.
+	This efun is to be used in the move_object() hook, as it does
+	nothing else than moving the item - no calls to init() or such.
+
+	Don't use it in your own objects!
+
+HISTORY
+  Introduced in 3.2.1@1 as 'efun308()', renamed to 'set_environment()'
+                in 3.2.6 and LP "03.02.1@150".
+
+SEE ALSO
+	remove(A), init(A), move_object(E), transfer(E), hooks(C),
+	native(C)
+
+29.10.2006 Zesstra
+
diff --git a/doc/efun/set_extra_wizinfo b/doc/efun/set_extra_wizinfo
new file mode 100644
index 0000000..f0095c9
--- /dev/null
+++ b/doc/efun/set_extra_wizinfo
@@ -0,0 +1,22 @@
+SYNOPSIS
+        void set_extra_wizinfo (object wiz, mixed extra)
+        void set_extra_wizinfo (string wiz, mixed extra)
+        void set_extra_wizinfo (int    wiz, mixed extra)
+
+DESCRIPTION
+        Set the value <extra> as the 'extra' information for the wizlist
+        entry of <wiz>.
+
+        If <wiz> is an object, the entry of its creator (uid) is used.
+        If <wiz> is a string (a creator aka uid), it names the entry
+        to use.
+        If <wiz> is the number 0, the data is set in the default wizlist
+        entry. It can be used to store data for the lifetime of this
+        driver run, like the time of the last reboot.
+
+        The <extra> argument may be any value.
+
+        The function causes a privilege violation.
+
+SEE ALSO
+        get_extra_wizinfo(E), set_extra_wizinfo_size(E), wizlist_info(E)
diff --git a/doc/efun/set_next_reset b/doc/efun/set_next_reset
new file mode 100644
index 0000000..c4cd3b7
--- /dev/null
+++ b/doc/efun/set_next_reset
@@ -0,0 +1,55 @@
+FUNKTION
+     int set_next_reset(int delay)
+
+ARGUMENTE
+     delay - minimale Zeit bis zum naechsten Reset des Objektes
+
+BESCHREIBUNG
+     Mit dieser efun ist es moeglich Einfluss auf das Resetverhalten des
+     Objektes zu nehmen:
+
+     Das Objekt bekommt einen reset()-Aufruf fruehestens in <delay> Sekunden.
+     Bei Angabe eines Wertes <0 wird der reset() für das Objekt abgeschaltet,
+     was fuer Blueprints gelegentlich sinnvoll ist.
+
+     Intern wird in gleichbleibenden Abstaenden (derzeit: in der Regel 2s,
+     kann sich aber auch verzoegern, wenn der Driver viel zu hat, z.B. auf 
+     4s) geprueft ob die Zeit zum zum naechsten reset() abgelaufen ist. 
+     Sollte dies der Fall sein, wird die Funktion reset() im Objekt 
+     aufgerufen.
+
+     Die Funktion gibt die verbleibende Zeit bis zum naechsten Reset
+     zurueck, bevor <delay> gesetzt wurde. Der Wert kann auch negativ
+     sein, wenn der Reset ueberfaellig war.
+
+     Achtung: die tatsaechliche Zeit, wann der Reset im Objekt durchgefuehrt
+     wird, haengt auch davon ab, ob das Objekt nach Ablauf von <delay>
+     verwendet wird.
+
+BEISPIELE
+     // ein Objekt mit verkuerzter reset()-Zeit
+     void create() {
+       ...
+       set_next_reset(15*60);	// ~ 15 Minuten
+       ...
+     }
+
+     void reset() {
+       set_next_reset(900);	// die muss im reset() immer wieder
+       ::reset();           // neu gesetzt werden
+     }
+
+     // ein Objekt, dessen Blueprint keinen reset() bekommen soll
+     void create() {
+       if(!clonep(this_object())) {
+         set_next_reset(-1);
+         return;
+       }
+       ::create();
+       ...
+     }
+
+SIEHE AUCH
+     call_out(E), object_info(E), reset(L), query_next_reset(E)
+
+7.Aug 2007 Gloinson
diff --git a/doc/efun/set_this_object b/doc/efun/set_this_object
new file mode 100644
index 0000000..2afda56
--- /dev/null
+++ b/doc/efun/set_this_object
@@ -0,0 +1,60 @@
+GESCHUETZT
+SYNOPSIS
+        void set_this_object(object objekt-an-stelle-von-originalobjekt);
+
+BESCHREIBUNG
+        Dies ist eine geschuetzte Funktion, die nur vom Master-Objekt und im
+        Simul-Efun-Objekt genutzt werden darf. Bei sonstiger Benutzung
+        erzeugt diese Funktion einen Fehler.
+
+        Die Funktion aendert das Resultat von this_object() in der laufenden
+        Funktion, ebenso das Resultat von previous_object() in Funktionen in
+        anderen Objekten, die einen call_other() Aufruf machen.
+
+        Der Effekt von set_this_object() bleibt bestehen, bis ein externer
+        Funktionsaufruf abgeschlossen ist oder bis zu einem erneuten
+        set_this_object(). Waehrend der Ausfuehrung von Code im Master-Objekt
+        oder im Simul-Efun-Objekt ist set_this_object() garantiert, auch wenn
+        this_object() durch set_this_object() veraendert wird. Die gilt
+        nicht fuer Funktionen, die aus anderen Programmen inheritet werden.
+
+        Diese Funktion darf nur mit hoechster Sorgfalt verwendet werden, um
+        Inkonsistenzen zu vermeiden. Nach einem Aufruf von set_this_object()
+        koennen sich gewisse LPC-Konstrukte merkwuerdig verhalten oder gar
+        das System zum Absturz bringen. Insbesondere die Verwendung von
+        globalen Variablen oder der Aufruf von lokalen Funktionen (ausser
+        durch call_other()) ist unzulaessig und wird aktiv verhindert.
+
+        Erlaubt sind call_other(), map(), der Zugriff auf lokale Variablen
+        (die auch Pointer auf globale Arrays enthalten duerfen), einfache
+        Arithmetik und der Zuweisungs-Operator.
+
+BUGS
+        Es ist momentan nicht moeglich, das originale gueltige Objekt wieder
+        herzustellen. Anstelle von:
+
+            object ich = this_object();
+            set_this_object(dings);
+            <irgendwelcher Code>
+            set_this_object(ich);
+            <mehr Code>
+
+        muss das ueber einen Umweg geloest werden:
+
+            private void tuwas(object dings)
+            {
+                set_this_object(dings);
+                <irgendwelcher code>
+            }
+
+            funcall(#'tuwas, dings);
+            <mehr Code>
+
+        Manche Leute bezeichnen das als Feature.
+
+AENDERUNGEN
+        LDMud 3.2.10 verhindert aktiv die Referenz auf globale Variablen und
+            Funktionsaufrufe nach Adresse, waehren set_this_object() gilt.
+
+SIEHE AUCH
+        this_object(E), set_this_player(E)
diff --git a/doc/efun/set_this_player b/doc/efun/set_this_player
new file mode 100644
index 0000000..085c529
--- /dev/null
+++ b/doc/efun/set_this_player
@@ -0,0 +1,31 @@
+GESCHUETZT
+SYNOPSIS
+        void set_this_player(object ob);
+
+BESCHREIBUNG
+        Aendert den momentanen Kommandogeber zu <ob>. Dabei kann <ob> auch
+        0 sein, wenn der aktuelle Kommandogeber 'deaktiviert' werden soll.
+
+        Diese Funktion ist nicht geschuetzt und sollte deshalb von einer
+        simul_efun ueberlagert werden, die die Efun entweder komplett
+        abschaltet, oder mindestens gewisse Sicherheitschecks durchfuehrt.
+        Es ist sonst einfach, die Sicherheit eines Muds mit Hilfe dieser
+        Efun zu untergraben.
+
+        Die Efun ist nur in 3.2.1 verfuegbar. Eine moegliche Simulation fuer
+        3.2 koennte etwa so aussehen:
+
+            void set_this_player(object ob)
+            {
+                /* Insert your privilege checks here */
+                if (living(ob))
+                    funcall(bind_lambda(#'enable_commands, ob));
+            }
+            (suggested by Mark Lewis (Nostradamus@Zebedee))
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.1.
+        LDMud 3.2.6 fuehrte die 0 als moeglichen Parameter ein.
+
+SIEHE AUCH
+        set_this_object(E), this_player(E)
diff --git a/doc/efun/seteuid b/doc/efun/seteuid
new file mode 100644
index 0000000..5c2793a
--- /dev/null
+++ b/doc/efun/seteuid
@@ -0,0 +1,17 @@
+SYNOPSIS
+        int seteuid(string str);
+
+BESCHREIBUNG
+        Setzt die effektive UID auf <str>. Das aufrufende Objekt muss dazu
+        vom Masterobjekt berechtigt sein. In den meisten Installationen
+        kann die effektive UID jederzeit auf die momentane UID des Objekts,
+        auf die UID des Erschaffers des Objekts oder auf 0 gesetzt werden.
+
+        Nur wenn dieser Wert 0 ist, kann die UID des Objekts durch
+        export_uid() veraendert werden.
+
+        Unter strikten eUID Regeln koennen Objekte mit UID 0 keine anderen
+        Objekte laden oder clonen.
+
+SIEHE AUCH
+        export_uid(E), getuid(E), getuuid(E), geteuid(E), native(C)
diff --git a/doc/efun/sgn b/doc/efun/sgn
new file mode 100644
index 0000000..2331c0d
--- /dev/null
+++ b/doc/efun/sgn
@@ -0,0 +1,17 @@
+SYNOPSIS
+        int sgn (int|float arg)
+
+BESCHREIBUNG
+        Liefert das Vorzeichen des Argumentes.
+
+        arg  sgn(arg)
+        --------------
+        > 0   1
+          0   0
+        < 0  -1
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9.
+
+SIEHE AUCH
+        abs(E)
diff --git a/doc/efun/shutdown b/doc/efun/shutdown
new file mode 100644
index 0000000..46ef0af
--- /dev/null
+++ b/doc/efun/shutdown
@@ -0,0 +1,18 @@
+SYNOPSIS
+        void shutdown();
+        void shutdown(int exit_code);
+
+BESCHREIBUNG
+        Faehrt das Mud herunter. Diese Funktion darf nie verwendet werden!
+        Wenn das Mud heruntergefahren werden muss, so hat dies ueber den
+        Shutdownbefehl zu erfolgen.
+
+        Ist ein <exit_code> Argument gegeben, wird sein Wert als der
+        Unix-Resultatwert verwendet; andernfalls wird 0 verwendet.
+
+        Man fragt sich nun vielleicht, wozu es dann diese Funktion gibt,
+        wenn man sie nicht verwenden darf. Sorry, das darf hier nicht
+        bekannt gegeben werden. Streng geheim.
+
+SIEHE AUCH
+        break_point(E), swap(E)
diff --git a/doc/efun/sin b/doc/efun/sin
new file mode 100644
index 0000000..6eab7ec
--- /dev/null
+++ b/doc/efun/sin
@@ -0,0 +1,8 @@
+SYNOPSIS
+        float sin(int|float)
+
+BESCHREIBUNG
+        Liefert den Sinus des Argumentes.
+
+SIEHE AUCH
+        asin(E), cos(E), acos(E), tan(E), atan(E), atan2(E)
diff --git a/doc/efun/sizeof b/doc/efun/sizeof
new file mode 100644
index 0000000..b5023b4
--- /dev/null
+++ b/doc/efun/sizeof
@@ -0,0 +1,16 @@
+SYNOPSIS
+        int sizeof(mixed * val);
+        int sizeof(string  val);
+        int sizeof(mapping val);
+
+BESCHREIBUNG
+        Liefert die Anzahl Elemente in einem Array <val>, die Anzahl
+        Zeichen in einem String <val> oder die Anzahl Keys in einem Mapping
+        <val>.
+
+        Als Spezialfall kann <val> auch 0 sein. In diesem Fall liefert die
+        Funktion 0 zurueck.
+
+SIEHE AUCH
+        allocate(E), pointerp(E), mappingp(E), m_allocate(E),
+        widthof(E)
diff --git a/doc/efun/sl_close b/doc/efun/sl_close
new file mode 100644
index 0000000..753948d
--- /dev/null
+++ b/doc/efun/sl_close
@@ -0,0 +1,17 @@
+OPTIONAL
+SYNOPSIS
+        void sl_close()
+
+BESCHREIBUNG
+        Schliesst die SQLite-Datenbank, welche vom aktuellen Objekt
+        geoeffnet wurde.
+
+        Diese Funktion ist nur verfuegbar, wenn der Driver mit SQLite-
+        Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __SQLITE__ definiert.
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.3.713.
+
+SIEHE AUCH
+        sl_open(E), sl_exec(E), sl_insert_id(E)
diff --git a/doc/efun/sl_exec b/doc/efun/sl_exec
new file mode 100644
index 0000000..e8c4a8f
--- /dev/null
+++ b/doc/efun/sl_exec
@@ -0,0 +1,25 @@
+OPTIONAL
+SYNOPSIS
+        mixed* sl_exec(string statement, ...)
+
+BESCHREIBUNG
+        Fuehrt den SQL-Befehl <statement> in der aktuell geoeffneten
+        SQLite-Datenbank aus. Dieser SQL-Befehl kann Wildcards wie '?'
+        nd '?nnn', wobei 'nnn' eine Zahl ist, enthalten. Diese Wildcards
+        koennen als weitere Parameter an sl_exec uebergeben werden.
+        Mit '?nnn' kann direkt die Nummer eines bestimmten Parameters
+        angegeben werden, der erste Parameter hat die Nummer 1.
+
+        Falls der SQL-Befehl Daten zurueckliefert, liefert sl_exec ein
+        Array aus den einzelnen Zeilen (welche wieder Arrays der einzelnen
+        Felder sind) zurueck.
+
+        Diese Funktion ist nur verfuegbar, wenn der Driver mit SQLite-
+        Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __SQLITE__ definiert.
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.3.713.
+
+SIEHE AUCH
+        sl_open(E), sl_insert_id(E), sl_close(E)
diff --git a/doc/efun/sl_insert_id b/doc/efun/sl_insert_id
new file mode 100644
index 0000000..a425cef
--- /dev/null
+++ b/doc/efun/sl_insert_id
@@ -0,0 +1,18 @@
+OPTIONAL
+SYNOPSIS
+        int sl_insert_id()
+
+BESCHREIBUNG
+        Nachdem eine Zeile in eine Tabelle mit einer AUTO_INCREMENT-Spalte
+        eingefuegt wurde, kann man mit dieser Funktion den (neuen) Wert
+        dieses AUTO_INCREMENT-Feldes der eingefuegten Zeile abfragen.
+
+        Diese Funktion ist nur verfuegbar, wenn der Driver mit SQLite-
+        Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __SQLITE__ definiert.
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.3.713.
+
+SIEHE AUCH
+        sl_open(E), sl_exec(E), sl_close(E)
diff --git a/doc/efun/sl_open b/doc/efun/sl_open
new file mode 100644
index 0000000..b54685d
--- /dev/null
+++ b/doc/efun/sl_open
@@ -0,0 +1,20 @@
+OPTIONAL
+SYNOPSIS
+        int sl_open(string filename)
+
+BESCHREIBUNG
+        Oeffnet die Datei <filename> als SQLite-Datenbank. Falls
+        sie noch nicht existiert, wird sie erstellt. Es ist nur
+        eine geoeffnete Datenbank pro Objekt erlaubt. Im Erfolgsfalle
+        liefert diese Funktion 1 zurueck, anderenfalls wird
+        normalerweise ein Fehler ausgeloest.
+
+        Diese Funktion ist nur verfuegbar, wenn der Driver mit SQLite-
+        Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __SQLITE__ definiert.
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.3.713.
+
+SIEHE AUCH
+        sl_exec(E), sl_insert_id(E), sl_close(E)
diff --git a/doc/efun/snoop b/doc/efun/snoop
new file mode 100644
index 0000000..484b202
--- /dev/null
+++ b/doc/efun/snoop
@@ -0,0 +1,22 @@
+GESCHUETZT
+SYNOPSIS
+        int snoop(object snooper);
+        int snoop(object snooper, object snoopee);
+
+BESCHREIBUNG
+        Beginnt die Beobachtung des Objekts <snoopee> durch <snooper>. Wenn
+        <snoopee> nicht angegeben wird, werden alle Beobachtungen von
+        <snooper> beendet.
+
+        Die Funktion liefert 1 bei Erfolg, -1 wenn eine Schleife entstehen
+        wuerde und 0 fuer alle anderen Fehler.
+
+        Die Beobachtung wird mit dem Master-Objekt auf Gueltigkeit geprueft.
+        Es wird auch ein Fehler verursacht, wenn eine Beobachtung zu einer
+        Rekursion fuehren wuerde.
+
+ANMERKUNG
+        Diese Funktion ist geschuetzt.
+
+SIEHE AUCH
+        query_snoop(E)
diff --git a/doc/efun/sort_array b/doc/efun/sort_array
new file mode 100644
index 0000000..b4a65ca
--- /dev/null
+++ b/doc/efun/sort_array
@@ -0,0 +1,111 @@
+sort_array(E)
+
+FUNKTION:
+     mixed *sort_array(mixed *arr, mixed func [, mixed ob])
+
+PARAMETER:
+     arr  - zu sortierendes Array
+     func - zu rufende Methode; kann als String (Funktionsname) oder 
+            Closure uebergeben werden
+     ob   - Objekt, an dem Methode gerufen werden soll; kann als String
+            (Filename) oder Objektpointer uebergeben werden
+
+BESCHREIBUNG
+     Erzeugt eine (flache) Kopie des Arrays 'arr' und sortiert diese mit der
+     Sortierfunktion 'func'. Die sortierte Kopie wird dann zurueckgegeben.
+
+     Die Elemente des zu sortierenden Arrays werden paarweise an die
+     Sortierfunktion als Argumente uebergeben.
+     Die Sortierfunktion sollte eine Zahl
+     - >0 zurueckgeben, wenn die Elemente in der falschen
+       Reihenfolge sind
+     - <=0 zurueckgeben, wenn die Elemente in der richtigen
+       Reihenfolge sind.
+
+     Verwendung von Methoden:
+       Wenn bei der Angabe von 'func' kein Objekt 'ob' in Form eines Strings
+       oder Objekts angegeben wird, wird this_object() angenommen.
+
+     Verwendung von Closures:
+       Es koennen Lfun-, Lambda- und Inline-Closures verwendet werden. 
+       Lfun-Closures koennen, wenn im selben Objekt vorhanden, auch
+       'private' oder/und 'static' deklariert sein, muessen aber zu dem
+       Zeitpunkt der Verwendung bekannt sein (Funktionsprototypen benutzen).
+       Von der Verwendung von Lambda-Closures wird ausdruecklich abgeraten.
+
+BEMERKUNGEN:
+     (1) sort_array() unterstuetzt keinen extra-Parameter
+
+     (2) Achtung, die Elemente in 'arr' werden nicht tief kopiert, sind sie
+     also selbst Arrays oder Mappings, so fuehrt eine Aenderung im Rueckgabe-
+     Array zur Aenderung im Ursprungsarray:
+
+     int *i, *j;
+     i=({({1}),({2,3}),({4,5,6})});
+     j=sort_array(i,        // sortiert der Groesse nach absteigend
+         function int ( mixed x, mixed y ) {
+           return sizeof(x) < sizeof(y); }
+         );
+     
+     Zu diesem Zeitpunkt ist  i == ({ ({1}),({2,3}),({4,5,6}) })
+                              j == ({ ({4,5,6}),({2,3}),({1}) })
+     
+     Fuehrt man jetzt die Zuweisung j[0][0]=8; aus
+
+     resultieren folgende Arrays: i == ({ ({1}),({2,3}),({8,5,6}) })
+                                  j == ({ ({8,5,6}),({2,3}),({1}) })
+
+BEISPIELE:
+     ### Sortieren von Zahlen in aufsteigender Reihenfolge ###
+     
+     int *arr = ({ 3, 8, 1, 3 })
+
+     // Folgend identische Resultate, aber andere Ansaetze:
+
+     #1: nutzt die 'Efun' > als Lfun-Closure (ideal hier):
+         sort_array(arr, #'>);
+
+     #2: mit Sortierfunktion im selben Objekt:
+         int is_greater (int a, int b) {
+           return a > b;
+         }
+         
+         #2a: sortiert mittels der Lfun im selben Objekt die Elemente in das
+              Rueckgabearray
+              sort_array(arr, "is_greater", this_object())
+              sort_array(arr, "is_greater")
+
+         #2b: nutzt die Lfun is_greater() als Lfun-Closure (Funktionspointer)
+              sort_array(arr, #'is_greater)
+
+     #3: nutzt eine Lambda-Closure (langsamer, nicht fuer alle leserlich)
+         sort_array(arr, lambda(({'a, 'b}), ({#'>, 'a, 'b})))
+
+     #4: analog zu 3, mit Inline-Closure
+         sort_array(arr, function int (int a, int b) {
+           return a > b; } );
+
+     Resultat in allen Faellen: ({1,3,3,8})
+
+     ### Sortieren von geschachtelten Arrays ###
+
+     arr = ({ ({ "foo", 3 }), ({ "quux", 1 }), ... })
+
+     // Vorgehen identisch, allerdings muss die Sortierfunktion
+     // angepasst werden (siehe auch das Beispiel unter Verwendung von
+     // Inline-Closures oben unter "Bemerkungen"):
+     
+     int is_greater (mixed *a, mixed *b) {
+       return a[1] > b[1];
+     }
+
+SIEHE AUCH:
+     Arrays:        filter(E), map(E)
+     Objektarrays:  filter_objects(E), map_objects(E)
+     Mappings:      filter_indices(E), map_indices(E)
+
+     Sonstiges:     unique_array()
+                    alist, transpose_array(E)
+
+----------------------------------------------------------------------------
+Last modified: Mon Feb 18 23:09 2008 by Arathorn
diff --git a/doc/efun/sprintf b/doc/efun/sprintf
new file mode 100644
index 0000000..3cb6970
--- /dev/null
+++ b/doc/efun/sprintf
@@ -0,0 +1,147 @@
+SYNOPSIS

+        string sprintf(string fmt, ...)

+

+BESCHREIBUNG

+        Mit dieser Funktion kann man auf einfache Weise aus dem Inhalt

+        von Variablen einen String bauen; und dies effektiver als

+        mit der ueblichen "Du hast "+anzahl+" Punkt(e)"-Methode.

+

+        Die Funktion bekommt als erstes Argument einen Formatstring fmt,

+        der Informationen darueber enthaelt, wie die weiteren beliebigen

+        Argumente in den Ergebnisstring eingebaut werden sollen.

+        Die meisten Zeichen gelangen vom Formatstring unveraendert in

+        den Ausgabestring. Die Regeln zum Einbau eines Arguments werden

+        immer mit '%' eingeleitet. Moechte man ein '%' in die Ausgabe

+        bringen, so muss man im Formatstring "%%" verwenden.

+

+        Ein einfaches Beispiel ist erg=sprintf("%s %d", str, i);

+        '%' leitet einen Argumentformatstring (AFS) ein. Das 's' schliesst

+        ihn ab und besagt, dass ein String eingebaut werden soll. Das

+        folgende Leerzeichen wird unveraendert uebernommen. '%' leitet

+        wieder einen neuen Formatstring ein, wobei 'd' eine Ganzzahl

+        bezeichnet (eine Variable von Typ int).

+        Dies ist ein allerdings nur ein sehr einfaches Beispiel.

+

+        Jeder Argumentformatstring kennzeichnet also, auf welche Art

+        ein Argument in das Ergebnis eingebaut werden soll. Der erste

+        AFS ist fuer das zweite Argument der Funktion, der zweite AFS

+        fuer das dritte Argument u.s.w. (das erste Argument der Funktion

+        ist ja der Formatstring selbst).

+

+        Jeder AFS beginnt mit einem '%' und endet mit einem der

+        folgenden Zeichen (Argumenttyp-Kennzeichner):

+        Zeichen    Argumenttyp    Bemerkung

+        's'        string

+        'c'        integer        als ASCII-Zeichen

+        'd' 'i'    integer        Dezimalschreibweise

+        'o'        integer        Oktalschreibweise

+        'b' 'B'    integer        Binaerschreibweise

+        'x' 'X'    integer        Hexadezimalschreibweise

+        'e' 'E'    float          Exponentialschreibweise

+        'f' 'F'    float          Gleitkommadarstellung

+        'g' 'G'    float          Gleitkommadarstellung

+        'O'        mixed          Gibt fuer Debugging alles irgendwie

+                                  lesbar aus, auch Arrays und Mappings

+        'Q'        mixed          Wie 'O', gibt jedoch Sonderzeichen in

+                                  Strings in der LPC-Notation aus

+        

+        Zwischen dem '%' und dem Argumenttyp-Kennzeichner kann man

+        noch mehrere Modifikatoren setzen, die das Verhalten

+        beeinflussen.

+        Hier eine Uebersicht. n steht hier fuer eine Ganzzahl, also

+        zum Beispiel "12".

+        Modifikator  Bedeutung

+        n            Minimale Stringlaenge, die fuer dieses Argument

+                     verwendet werden soll. Fehlende Zeichen werden mit

+                     einem Fuellzeichen aufgefuellt. Beginnt n mit einer

+                     '0' (etwa "08") so ist das Fuellzeichen '0' sonst

+                     ist es per Default ' '. (sogenannte 'Feldbreite')

+        .n           Bei Ganzzahlen die Maxanzahl der Stellen, bei Gleit-

+                     kommazahlen die Maximalzahl der Nachkommastellen.

+                     Bei (einfachen) Strings die Maximallaenge.

+        :n           Ist dasselbe wie n.n - setzt also beide Werte auf

+                     dieselbe Zahl.

+        'X'          Als Fuellzeichen wird X genutzt. X koennen dabei

+                     auch mehrere Zeichen sein, etwa fuehrt '-=' zu

+                     Fuellungen der Art "-=-=-=-=". Um mit Hochkommas

+                     zu fuellen ist '\\'' anzugeben. Rueckwaerts-

+                     schraegstrich entsprechend mit '\\\\'.

+        <Space>      Vor positive Zahlen wird ein Leerzeichen gefuegt.

+        +            Vor positive Zahlen wird ein '+' gefuegt.

+        -            Der Wert wird linksbuendig in das fuer dieses Argument

+                     vorgesehene Feld eingefuegt (Standard ist rechts-

+                     buendig). Bei Strings wird meistens diese Ausrichtung

+                     die sinnvollste sein.

+        |            Der Wert wird zentriert in das Feld eingefuegt.

+                     (Siehe Modifikator n, Feldbreite)

+        $            Blocksatz. Benoetigt eine Feldbreite, funktioniert nur

+                     bei Strings (auch im Spaltenmodus).

+        =            Spaltenmodus (siehe unten).

+        #            Fuer Strings: Tabellenmodus (siehe unten).

+                     Fuer '%O'/'%Q': kompakte Ausgabe.

+        @            Arraymodus (siehe unten).

+        *            Ein Stern kann immer dort eingesetzt werden, wo

+                     hier weiter oben ein n fuer eine Ganzzahl steht.

+                     Der Wert der Zahl muss dann als weiterer Parameter

+                     an die Funktion uebergeben werden.

+

+BEISPIELE

+        Mit den bis jetzt erwaehnten Moeglichkeiten kann man zB machen:

+

+        sprintf("%d (dec) == %o (octal) == %x (hex)", 20, 20, 20);

+        => "20 (dec) == 24 (octal) == 14 (hex)"

+

+        sprintf("Du drehst den Knopf um %.3f Umdrehungen", 12.3456);

+        => "Du drehst den Knopf um 12.345 Umdrehungen"

+

+        sprintf("Du liest %|'*':9s", "Fiona");

+        => "Du liest **Fiona**"

+

+        sprintf("Auf dem Zettelstueck steht: %-.*s...", 7, "Hallo Du da");

+        => "Auf dem Zettelstueck steht: Hallo D...

+

+ERWEITERTE MODI

+        Mit dem Modifikatoren = # und @ stehen maechtige Werkzeuge zur

+        Verfuegung. Mit ein wenig Ueberlegung kann man sich oft viele

+        Zeilen Code ersparen.

+        Arraymodus (@):

+          sprintf("%@s", arr_of_string);

+          Der Argumentformatstring (allerdings ohne das @) wird sooft

+          hintereinandergereiht, wieviele Elemente das Array hat.

+          Jeder AFS wird dann fuer ein Element des Arrays benutzt.

+          sprintf("%@s", ({"aaa","bbb"})) ist somit dasselbe wie

+          sprintf("%s%s", "aaa", "bbb"). Allerdings passt es sich

+          immer an die Elementzahl der uebergebenden Arrays an.

+          Dies ist nuetzlich um Ergebnisse von map() oder aehnlich

+          auszugeben.

+          sprintf("%@s", map_objects(all_inventory(), "short"));

+          Der Argumenttyp-Kennzeichner muss hierbei immer dem Typen

+          eines Elementes des Arrays entsprechen.

+        Spaltenmodus (=):

+          Diese Funktion bricht Text um. Die Feldbreite muss angegeben

+          werden. Wird neben der Feldbreite auch eine maximale String-

+          laenge angegeben, so wird die letztere fuer die Breite des

+          Umbrechens verwendet, die Feldbreite wird mit Fuellzeichen

+          aufgefuellt.

+          sprintf("%=-20s", str); bricht den String str 'wordwrap'end

+          auf 20 Zeichen Laenge um. sprintf("%=-*s", len, str);

+          ist schon eine einfache break_string() Variante.

+        Tabellenmodus (#):

+          Diese Funktion gibt Strings tabellenartig aus. Die Teilstrings

+          muessen mit \n getrennt als ein String als Argument uebergeben

+          werden. Die Feldbreite muss angegeben werden und bezeichnet

+          die (maximale) Gesamtbreite der Tabelle.

+          Die Anzahl der Spalten der Tabelle wird moeglichst optimal

+          bestimmt, und ist fuer alle Spalten gleich. Wird ein

+          Wert als 'Praezision' angegeben, so ist dies die Anzahl von

+          Spalten, die verwendet werden soll.

+          sprintf("%#30.4s", str) erzeugt eine Tabelle, die maximal

+          30 Zeichen breit ist und 4 Spalten enthaelt.

+          sprintf("%#30s", str) legt die Spaltenzahl dynamisch anhand

+          der Einzelstringlaengen fest, so dass der laengste String

+          noch genau in die Tabelle passt.

+          Wenn string* worte die in die Tabelle einzubettenden Worte

+          enthaelt, so muss str=implode(worte,"\n") sein.

+

+SIEHE AUCH:

+        printf(E)

diff --git a/doc/efun/sqrt b/doc/efun/sqrt
new file mode 100644
index 0000000..44c4805
--- /dev/null
+++ b/doc/efun/sqrt
@@ -0,0 +1,9 @@
+SYNOPSIS
+        float sqrt(int value);
+        float sqrt(floag value);
+
+BESCHREIBUNG
+        Liefert die Quadratwurzel von <value>.
+
+SIEHE AUCH
+        log(E), pow(E)
\ No newline at end of file
diff --git a/doc/efun/sscanf b/doc/efun/sscanf
new file mode 100644
index 0000000..a616bf2
--- /dev/null
+++ b/doc/efun/sscanf
@@ -0,0 +1,115 @@
+FUNKTION:
+ int sscanf(string str,string fmt,mixed var1,mixed var2,...)
+
+ARGUMENTE:
+      str
+        - String, der nach einem Muster zu durchsuchen ist
+        - darf nicht NULL sein
+      fmt
+        - Format-String, nach dessen Muster untersucht wird
+      var1,var2,...
+        - Argumente die mit %d oder %s korrespondieren
+
+RUeCKGABEWERT:
+     Anzahl der gefundenen Argumente.
+
+BESCHREIBUNG:
+     Wertet einen String <str> unter Beruecksichtigung des Formats <fmt>
+     aus. <fmt> kann Strings beinhalten, die durch %d und %s getrennt
+     werden. Jedes %d und %s entspricht einer der Variablen <var1>, <var2>,
+     etc. in die gespeichert werden soll.
+
+     Die Operatoren im Format-String <fmt> haben eines der folgenden
+     Formate:
+
+         %[+][!|~][<size>[.<minmatch>]]<type>
+
+        <type> kann folgendes sein:
+            d: steht fuer eine Zahl
+            D: steht fuer eine Zahl
+            U:
+            s: steht fuer eine Zeichenkette
+            %: steht fuer das %-Zeichen
+            t: steht fuer Whitespaces (also Leerschlaege und Tabulatoren),
+               speichert diese aber nicht.
+
+     <size> ist die erwartete Feldgroesse, <minmatch> die verlangte
+     minimale Laenge fuer einen Treffer (Standardwerte sind 0 fuer
+     Strings und 1 fuer Nummern). Sowohl <size> als auch <minmatch> kann
+     entweder numerisch oder mit '*' angegeben werden - im zweiten Fall
+     wird die gueltige Variable in der Liste der Argumente benutzt.
+
+     Wird + angegeben, muessen die Zeichen nach dem Feld ebenfalls
+     matchen. Ist dies nicht der Fall, wird auch dieses Feld als
+     Misserfolg in der Rueckgabe betrachtet (auch wenn der Wert
+     bereits an die zugehoerige Variable zugewiesen wurde).
+
+     Wird ! angegeben, wird zwar die Suche durchgefuehrt, aber Treffer
+     werden weder gespeichert noch gezaehlt. Mit ~ als Argument wird
+     zwar die Suche durchgefuehrt und die Treffer gezaehlt, das Resultat
+     wird aber nicht gespeichert.
+
+     Wenn ein %s nicht am Ende von <fmt> steht, wird nur ein Treffer
+     registriert, wenn auch der nachfolgende String bzw. das
+     nachfolgende Format gefunden wird. Weiter unten gibt es dazu
+     ein Beispiel.
+     Bei einem %d allerdings muss dieses Verhalten mit einem '+'
+     erzwungen werden.
+
+     Der Unterschied zwischen %d und %D %U ist, dass letzteres ein
+     unmittelbar vorausgehendes %s so bald als moeglich abbricht,
+     waehrend ersteres zuerst versucht, einen moeglichst grossen Treffer
+     fuer %s zu erzielen. Trotzdem ueberspringt %D/%U keine Whitespaces,
+     dazu muss %.0t%D gesetzt werden.
+
+     Die Funktion sscanf() ist insofern ein Spezialfall, als dass
+     Argumente automatisch nach Referenz uebergeben werden.
+
+BEISPIELE:
+     string who, what;
+         if (sscanf("wirf frisbee zu rex",
+                    "wirf %s zu %s", what, who) != 2)
+             write("Usage: Wirf <what> zu <who>\n");
+         else
+             write("Du wirfst einen "+what+" zu "+who+".\n");
+
+     sscanf("ab", "%s%s", who, what)
+     ==> liefert 2, who = "", what = "ab"
+
+     sscanf("ab", "%s %s", who, what)
+     ==> liefert 0, who = 0, what = 0
+
+     sscanf("ab ", "%s %s", who, what)
+     ==> liefert 2, who = "ab", what = ""
+
+
+     // Achtung bei %d
+     sscanf("12 ","%d xyz", num1);
+     ==> liefert 1, num1 = 12
+
+     sscanf("12 ","%s xyz", num1);
+     ==> liefert 0, num1 = 0
+
+     // mit '+' wird das Parsen des Restformats erzwungen:
+     sscanf("12 ","%+d xyz", num1);
+     ==> liefert 0, num1 = 12
+
+
+     // Weiteres Beispiel zu %d:
+     sscanf("get 12 coins","get %d rubys",num)
+     ==> ergibt 1, weil 'rubys' ignoriert wird
+
+     // Beispiel Format-Ignore 2
+     sscanf("get 12 coins","get %+d rubys",num);
+     ==> ergibt 0, da mit dem '+' das Parsen des Restformats erzwungen wird
+     ==> ergibt 1 bei 'get 12 rubys'
+
+     // Beispiel Format-Ignore 3 [alte Variante]
+     sscanf("get 12 coins","get %d rubys%s", num, dummy)
+     ==> ergibt 1
+     ==> ergibt 2 bei "get 12 rubys"
+
+SIEHE AUCH:
+        explode(E), regexp(E)
+
+8.Aug 2007 Gloinson
diff --git a/doc/efun/strftime b/doc/efun/strftime
new file mode 100644
index 0000000..c167afb
--- /dev/null
+++ b/doc/efun/strftime
@@ -0,0 +1,112 @@
+SYNOPSIS
+     string strftime()
+     string strftime(string fmt)
+     string strftime(int clock)
+     string strftime(string fmt, int clock)
+     string strftime(string fmt, int clock, int localized)
+
+BESCHREIBUNG
+     Gibt, aehnliche wie ctime(), eine Zeit als formatierten String zurueck.
+     Hierbei kann ein String mit div. Platzhaltern vom Benutzer angegeben
+     werden (s.u.). Wird kein String angegeben, wird "%c" als Formatstring
+     benutzt.
+
+     Das Argument <clock> wird als Anzahl Sekunden seit dem 01.01.1970, 00:00
+     Uhr interpretiert. Wenn <clock> nicht angegeben wird, wird time()
+     verwendet.
+
+     Das Argument <localized> gibt an, ob die Ausgabe englisch (das sog.
+     klassische "C" locale) oder in der jeweiligen Landessprache (z.B.
+     deutsch) erfolgen soll. Hierbei haengt die Sprache allerdings von den auf
+     dem Mudrechner gesetzten Umgebungsvariablen LC_TIME oder LC_ALL ab, sie
+     kann nicht selber gewaehlt werden. Wird kein <localized> angegeben, wird
+     1 verwendet, was einer Ausgabe in Landessprache entspricht.
+     0: Ausgabe im klassischen "C" locale (english)
+     1: Ausgabe in Landessprache des Mudrechners.
+
+BEMERKUNGEN:
+     Der zurueckgebene Ergebnisstring ist max. 511 Zeichen lang.
+
+PLATZHALTER:
+     Diese Funktion versteht alle Platzhalter, die die Funktion strftime() aus
+     der C-Standardbibliothek versteht. Momentan sind dies:
+       %a     Der abgekuerzte Wochentag abhaengig von der momentanen Locale.
+       %A     Der gesamte Wochentag abhaengig von der momentanen Locale.
+       %b     Der abgekuerzte Monatsname abhaengig von der momentanen Locale.
+       %B     Der volle Monatsname abhaengig von der momentanen Locale.
+       %c     Das bevorzugte Datums- und Uhrzeit-Repraesentation laut Einstel-
+              lungen der momentanen Locale.
+       %C     Das Jahrhundert als zweistellige Zahl.
+       %d     Der Tag im Monat als Dezimalzahl (01 - 31).
+       %D     Aequivalent  zu %m/%d/%y.  (US-amerikanisches Format.  In anderen
+              Laendern ist %d/%m/%y durchaus ueblich . In internationalem Kon- 
+              text ist dieses Format daher mehrdeutig und sollte nicht verwen-
+              det werden.)
+       %e     Wie %d, der Tag im Monat als Dezimalzahl, aber eine fuehrende
+              Null ist durch ein Leerzeichen ersetzt.
+       %E     Modifikator: Alternatives Format benutzen, s.u.
+       %g     Wie  %G,  aber ohne das Jahrhundert, also mit zweistelligem Jahr
+              (00-99).
+       %G     Das Jahr laut ISO 8601 mit dem Jahrhundert als Dezimalzahl.  Das
+              vierstellige Jahr, das zu ISO-Wochennummer (siehe %V) passt.  Es
+              hat dasselbe Format und denselben Wert wie %y,  nur  dass,  wenn
+              die  ISO-Wochennummer  zum  vorhergehenden  oder  naechsten  Jahr
+              gehoert, dieses Jahr stattdessen benutzt wird.
+       %h     Aequivalent zu %b.
+       %H     Die Stunde im 24h-Format als Ganzzahl (00 - 23).
+       %I     Die Stunde im 12h-Format als Ganzzahl (01 - 12).
+       %j     Der Tag im Jahr als Ganzzahl (001 - 366).
+       %k     Die Stunde im 24h-Format als Ganzzahl (0 - 23); einzelne Ziffern
+              haben ein vorangestelltes Leerzeichen. (Siehe %H.)
+       %l     Die Stunde im 12h-Format als Ganzzahl (0 - 12); einzelne Ziffern
+              haben ein vorangestelltes Leerzeichen. (Siehe %I.)
+       %m     Der Monat als Ganzzahl (01 - 12).
+       %M     Die Minute als Ganzzahl (00 - 59).
+       %n     Ein Zeilenvorschub.
+       %p     Entweder 'AM' oder 'PM', je nach der uebergebenen  Uhrzeit,  oder
+              die  zugehoerigen Zeichenketten in der momentanen Locale.  Mittag
+              erhaelt 'PM', Mitternacht 'AM'.
+       %P     Wie %p, aber in Kleinbuchstaben.
+       %r     Zeit in AM/PM-Notation; in der POSIX-Locale ist das Aequivalent
+              zu '%I:%M:%S %p'.
+       %R     Zeit in 24h-Notation (%H:%M). (SU) Fuer eine Version mit Sekunden
+              siehe %T.
+       %s     Die Zahl der Sekunden seit  der  Epoche,  also  seit  1970-01-01
+              00:00:00 UTC.
+       %S     Die Sekunde als Ganzzahl (00 - 61).
+       %t     Ein Tabulatorzeichen.
+       %T     Zeit in 24h-Notation (%H:%M:%S).
+       %u     Der Tag der Woche als Zahl von 1 bis 7, mit Montag als 1.  Siehe
+              auch %w.
+       %U     Die Wochennummer des aktuellen Jahres als Ganzzahl  von  00  bis
+              53,  beginnend  mit dem ersten Sonntag als erster Tag der ersten
+              Woche.  Siehe auch %V und %W.
+       %V     Die Wochennummer nach ISO 8601:1988 als Dezimalzahl von  01  bis
+              53,  wobei Woche 1 die erste Woche ist, die wenigstens 4 Tage im
+              laufenden Jahr hat, mit Montag als dem  ersten  Tag  der  Woche.
+              Siehe auch %U und %W.
+       %w     Der  Tag  der  Woche  als  Zahl  von 0 bis 6, mit Sonntag als 0.
+              Siehe auch %u.
+       %W     Die Wochennummer des aktuellen Jahres als Ganzzahl  von  00  bis
+              53,  beginnend  mit  dem ersten Montag als erster Tag der ersten
+              Woche.
+       %x     Die bevorzugte Datums-Repraesentation ohne die Zeit in der momen-
+              tanen Locale.
+       %X     Die  bevorzugte  Uhrzeit-Repraesentation  ohne  das  Datum in der
+              momentanen Locale.
+       %y     Das Jahr als Ganzzahl ohne das Jahrhundert (00 - 99).
+       %Y     Das Jahr als Ganzzahl mit dem Jahrhundert.
+       %z     Die  Zeitzone  als  Stundendifferenz  zu  GMT. Benoetigt, um
+              RFC822-konforme  Datumsangaben  zu  erhalten  (mit '%a, %d %b %Y
+              %H:%M:%S %z').
+       %Z     Die Zeitzone oder der Name oder die Abkuerzung.
+       %+     Datum und Zeit im Format von date(1).
+       %%     Das Zeichen '%'.
+     
+BEISPIEL
+     write(strftime("Heute ist %A, der %d. %B %Y.\n"))
+     ergibt z.B.
+     "Heute ist Montag, der 24. September 2007.\n"
+
+SIEHE AUCH
+     ctime(E), gmtime(E), localtime(E), mktime(E), time(E), utime(E)
diff --git a/doc/efun/strftime.en b/doc/efun/strftime.en
new file mode 100644
index 0000000..ca0aa65
--- /dev/null
+++ b/doc/efun/strftime.en
@@ -0,0 +1,122 @@
+SYNOPSIS
+     string strftime()
+     string strftime(string fmt)
+     string strftime(int clock)
+     string strftime(string fmt, int clock)
+     string strftime(string fmt, int clock, int localized)
+
+BESCHREIBUNG
+     Gibt, aehnliche wie ctime(), eine Zeit als formatierten String zurueck.
+     Hierbei kann ein String mit div. Platzhaltern vom Benutzer angegeben
+     werden (s.u.). Wird kein String angegeben, wird "%c" als Formatstring
+     benutzt.
+
+     Das Argument <clock> wird als Anzahl Sekunden seit dem 01.01.1970, 00:00
+     Uhr interpretiert. Wenn <clock> nicht angegeben wird, wird time()
+     verwendet.
+
+     Das Argument <localized> gibt an, ob die Ausgabe englisch (das sog.
+     klassische "C" locale) oder in der jeweiligen Landessprache (z.B.
+     deutsch) erfolgen soll. Hierbei haengt die Sprache allerdings von den auf
+     dem Mudrechner gesetzten Umgebungsvariablen LC_TIME oder LC_ALL ab, sie
+     kann nicht selber gewaehlt werden. Wird kein <localized> angegeben, wird
+     1 verwendet, was einer Ausgabe in Landessprache entspricht.
+
+BEMERKUNGEN:
+     Der zurueckgebene Ergebnisstring ist max. 511 Zeichen lang.
+     Im MG erfolgt momentan immer eine englische Ausgabe.
+
+PLATZHALTER:
+     Diese Funktion versteht alle Platzhalter, die die Funktion strftime() aus
+     der C-Standardbibliothek versteht. Momentan sind dies:
+     
+     %a
+        is replaced by the locale's abbreviated weekday name. 
+     %A
+        is replaced by the locale's full weekday name. 
+     %b
+        is replaced by the locale's abbreviated month name. 
+     %B
+        is replaced by the locale's full month name. 
+     %c
+        is replaced by the locale's appropriate date and time representation. 
+     %C
+        is replaced by the century number (the year divided by 100 and 
+        truncated to an integer) as a decimal number [00-99]. 
+     %d
+        is replaced by the day of the month as a decimal number [01,31].
+     %D
+        same as %m/%d/%y. 
+     %e
+        is replaced by the day of the month as a decimal number [1,31]; a 
+        single digit is preceded by a space. 
+     %h
+        same as %b. 
+     %H
+        is replaced by the hour (24-hour clock) as a decimal number 
+        [00,23]. 
+     %I
+        is replaced by the hour (12-hour clock) as a decimal number 
+        [01,12]. 
+     %j
+        is replaced by the day of the year as a decimal number 
+        [001,366]. 
+     %m
+        is replaced by the month as a decimal number [01,12]. 
+     %M
+        is replaced by the minute as a decimal number [00,59]. 
+     %n
+        is replaced by a newline character. 
+     %p
+        is replaced by the locale's equivalent of either a.m. or p.m. 
+     %r
+        is replaced by the time in a.m. and p.m. notation; in the POSIX 
+        locale this is equivalent to %I:%M:%S %p. 
+     %R
+        is replaced by the time in 24 hour notation (%H:%M). 
+     %S
+        is replaced by the second as a decimal number [00,61]. 
+     %t
+        is replaced by a tab character. 
+     %T
+        is replaced by the time (%H:%M:%S). 
+     %u
+        is replaced by the weekday as a decimal number [1,7], with 1 
+        representing Monday. 
+     %U
+        is replaced by the week number of the year (Sunday as the first day 
+        of the week) as a decimal number [00,53]. 
+     %V
+        is replaced by the week number of the year (Monday as the first day 
+        of the week) as a decimal number [01,53]. If the week containing 1 
+        January has four or more days in the new year, then it is considered 
+        week 1. Otherwise, it is the last week of the previous year, and the 
+        next week is week 1. 
+     %w
+        is replaced by the weekday as a decimal number [0,6], with 0 
+        representing Sunday. 
+     %W
+        is replaced by the week number of the year (Monday as the first day 
+        of the week) as a decimal number [00,53]. All days in a new year 
+        preceding the first Monday are considered to be in week 0. 
+     %x
+        is replaced by the locale's appropriate date representation. 
+     %X
+        is replaced by the locale's appropriate time representation. 
+     %y
+        is replaced by the year without century as a decimal number [00,99]. 
+     %Y
+        is replaced by the year with century as a decimal number. 
+     %Z
+        is replaced by the timezone name or abbreviation, or by no bytes if 
+        no timezone information exists. 
+     %%
+        is replaced by %.
+
+BEISPIEL
+     write(strftime("Heute ist %A, der %d. %B %Y.\n"))
+     ergibt z.B.
+     "Heute ist Montag, der 24. September 2007.\n"
+
+SIEHE AUCH
+     gmtime(E), localtime(E), mktime(), time(E), utime(E)
diff --git a/doc/efun/stringp b/doc/efun/stringp
new file mode 100644
index 0000000..97d82b0
--- /dev/null
+++ b/doc/efun/stringp
@@ -0,0 +1,10 @@
+SYNOPSIS
+        int stringp(mixed arg)
+
+BESCHREIBUNG
+        Liefert 1, wenn das Argument eine Zeichenkette (String) ist,
+        ansonsten 0.
+
+SIEHE AUCH
+        closurep(E), floatp(E), mappingp(E), objectp(E), intp(E),
+        referencep(E), pointerp(E), symbolp(E), clonep(E)
diff --git a/doc/efun/strlen b/doc/efun/strlen
new file mode 100644
index 0000000..e081a7a
--- /dev/null
+++ b/doc/efun/strlen
@@ -0,0 +1,11 @@
+SYNOPSIS
+        int strlen(string str)
+
+BESCHREIBUNG
+        Liefert die Laenge eines Strings.
+        Diese efun ist VERALTET und ersetzt durch sizeof().
+        Bitte in neuem Code nicht mehr benutzen und in altem
+        Code sukzessive ersetzen.
+
+SIEHE AUCH
+        sizeof(E), extract(E)
diff --git a/doc/efun/strrstr b/doc/efun/strrstr
new file mode 100644
index 0000000..bc039b6
--- /dev/null
+++ b/doc/efun/strrstr
@@ -0,0 +1,25 @@
+SYNOPSIS
+        int strrstr(string str, string muster);
+        int strrstr(string str, string muster, int pos);
+
+BESCHREIBUNG:
+        Liefert den Index des ersten Auftretens von <muster> im String <str>,
+        ausgehend von der Position <pos> her rueckwaerts gesucht. Wird <pos>
+        nicht angegeben, wird als Standard -1 gesetzt, was dem Ende von <str>
+        entspricht. Mit anderen Worten: die Funktion liefert den Index des
+        letzten Auftretens von <muster> vor <pos>.
+
+        Der Index, der zurueck gegeben wird, ist relativ zum Beginn des
+        Strings <str>.
+
+        Wenn <muster> nicht in <str> gefunden wird, wird -1 zurueck gegeben.
+
+        Wenn <pos> negativ ist, bezeichnet <pos> den Index vom Ende des
+        Strings aus gezaehlt. Dabei wird die Suche aber dennoch rueckwaerts
+        im String <str> durchgefuehrt.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.10
+
+SIEHE AUCH
+        strstr(E), strlen(E), sscanf(E), sprintf(E), explode(E)
diff --git a/doc/efun/strstr b/doc/efun/strstr
new file mode 100644
index 0000000..ea5cb83
--- /dev/null
+++ b/doc/efun/strstr
@@ -0,0 +1,17 @@
+SYNOPSIS
+        int strstr(string str, string muster);
+        int strstr(string str, string muster, int pos);
+
+BESCHREIBUNG
+        Liefert den Index des ersten Auftretens von <muster> in <str>,
+        ausgehend von der Position <pos>, wobei in <str> vorwaerts gesucht
+        wird. Wird <pos> nicht angegeben, wird als Standardwert 0 gesetzt,
+        also vom Beginn von <str> her gesucht.
+
+        Wenn <muster> nicht gefunden wird, wird -1 zurueck geliefert.
+
+        Wenn <pos> negativ ist, bezeichnet <pos> die Startposition der Suche
+        relativ zum Ende von <str>, es wird aber weiterhin vorwaerts gesucht.
+
+SIEHE AUCH
+        strrstr(E), strlen(E), sscanf(E), sprintf(E), explode(E)
diff --git a/doc/efun/struct_info b/doc/efun/struct_info
new file mode 100644
index 0000000..4d4cd0b
--- /dev/null
+++ b/doc/efun/struct_info
@@ -0,0 +1,46 @@
+SYNOPSIS
+        #include <struct_info.h>
+        #include <lpctypes.h>
+
+        mixed * struct_info (struct st, int what)
+
+DESCRIPTION
+        Return information about the structure of struct <st> in an array.
+        If <st> has a base struct, <what> determines how the information
+        is returned:
+
+        <what> == SINFO_FLAT:
+            All members of <st>, including those inherited from the base
+            struct, are returned on the top level of the result.
+            The base struct is signified by just its name.
+
+        <what> == SINFO_NESTED:
+            Only the members defined in <st> itself are returned on
+            the top level of the result. The information for the base
+            struct is a array by itself, as it would be returned
+            by a call to struct_info() for a base struct instance.
+
+        The elements in the resulting array are:
+
+          string [SI_NAME]:        the name of the struct
+          string [SI_PROG_NAME]:   the name of program defining the struct
+          string [SI_PROG_ID]:     the id of the program defining the struct
+          mixed  [SI_BASE]:        0, or the base struct information
+          mixed* [SI_MEMBER+0]:    the first member information
+          mixed* [SI_MEMBER+n]:    the last member information
+
+        The member information entries are arrays themselves with
+        these elements:
+
+          string [SIM_NAME]:  name of the member
+          int    [SIM_TYPE]:  the type of the member (compile-time value)
+          string [SIM_EXTRA]: 0, or if the member is a struct, the
+                              struct name
+
+HISTORY
+        Introduced in LDMud 3.3.344.
+        LDMud 3.3.417 introduced SI_PROG_NAME and SI_PROG_ID in exchange
+          for SI_UNIQUE_NAME.
+
+SEE ALSO
+        structs(LPC)
diff --git a/doc/efun/structp b/doc/efun/structp
new file mode 100644
index 0000000..0b1668d
--- /dev/null
+++ b/doc/efun/structp
@@ -0,0 +1,13 @@
+SYNOPSIS
+        int structp(mixed arg)
+
+DESCRIPTION
+        Return 1 if arg is a struct.
+
+HISTORY
+        Introducted in LDMud 3.3.273.
+
+SEE ALSO
+        baseof(E), closurep(E), floatp(E), mappingp(E), objectp(E),
+        intp(E), referencep(E), pointerp(E), stringp(E), symbolp(E),
+        clonep(E)
diff --git a/doc/efun/swap b/doc/efun/swap
new file mode 100644
index 0000000..fd242e4
--- /dev/null
+++ b/doc/efun/swap
@@ -0,0 +1,7 @@
+GESCHUETZT
+SYNOPSIS
+        void swap(object obj);
+
+BESCHREIBUNG
+        Lagert ein Objekt aus. Diese Efun ist nur fuer systeminternes
+        Debugging und kann einen Absturz verursachen.
diff --git a/doc/efun/symbol_function b/doc/efun/symbol_function
new file mode 100644
index 0000000..dbb53d0
--- /dev/null
+++ b/doc/efun/symbol_function
@@ -0,0 +1,36 @@
+SYNOPSIS
+        closure symbol_function(symbol arg);
+        closrue symbol_function(string arg);
+        closure symbol_function(string arg, object|string obj);
+
+BESCHREIBUNG
+        Erzeugt eine Lfun-, Efun- oder Operator-Closure aus <arg>, wobei
+        <arg> entweder ein string oder ein symbol sein muss. Fuer
+        Lfun-Closures gibt <obj> an, zu welchem Objekt die Lfun gehoert,
+        entweder angegeben durch das Objekt selbst (bzw. einen pointer
+        darauf) oder durch den Objektnamen als String. Wenn ein String
+        angegeben wird, wird das Objekt beim Aufruf geladen.
+
+        Wenn die Closure fuer eine Lfun in einem anderen als dem momentanen
+        Objekt erzeugt wird, ergibt dies eine "alien lfun closure". Solche
+        Closures sind an das Objekt gebunden, das symbol_function()
+        aufgerufen hat (dieses Objekt wird von to_object() geliefert),
+        obwohl der eigentliche Code in einem anderen Objekt steht (das mit
+        get_type_info() gefunden werden kann).
+
+        Als "private" deklarierte Funktionen koennen auf diese Weise nie
+        zu einer Closure gewandelt werden, "static" und "protected"
+        deklarierte Lfuns nur dann, wenn <obj> das gueltige Objekt
+        (d.h. this_object()) ist.
+        Expord.h. tiert man die Closures, koennen sie unabhaengig vom Modifier der
+        Ursprungsfunktionen von jedem gerufen werden.
+        umgehen).
+
+BEISPIELE
+        symbol_function("efun::users");
+            --> ergibt: #'users
+        symbol_function("QueryProp", other_obj);
+            --> ergibt: other_obj->QueryProp()
+
+SIEHE AUCH
+        lambda(E), quote(E)
diff --git a/doc/efun/symbol_variable b/doc/efun/symbol_variable
new file mode 100644
index 0000000..b3a66d4
--- /dev/null
+++ b/doc/efun/symbol_variable
@@ -0,0 +1,29 @@
+SYNOPSIS
+        closure symbol_variable(string arg);
+        closure symbol_variable(symbol arg);
+        closure symbol_variable(int arg);
+
+BESCHREIBUNG
+        Erzeugt eine Identifier (Lfun) Closure aus der globalen Variablen
+        <arg> des gueltigen Objekts. Die Variable kann angegeben werden
+        als Symbol, mit ihrem Namen oder durch die ordinale Nummer in der
+        Variablentabelle des Objekts.
+
+        Wenn keine solche Variable existiert oder sie von aussen nicht
+        sichtbar ist, wird 0 zurueck geliefert.
+
+        Wenn <arg> ein Integer ist und sich auf eine geerbte Variable
+        bezieht, die im geerbten Objekt "private" deklariert ist (d.h.
+        versteckt), fuehrt dies zu einer Schutzverletzung.
+
+AENDERUNGEN
+        Eingefuehrt in 3.2.1@8
+
+BEISPIELE
+        int base;
+        int var;
+        symbol_variable("var");         ergibt: #'<this_object>->var
+        symbol_variable(0);             ergibt: #'<this_object>->base
+
+SIEHE AUCH
+        lambda(E), quote(E), symbol_function(E)
diff --git a/doc/efun/symbolp b/doc/efun/symbolp
new file mode 100644
index 0000000..92c64b7
--- /dev/null
+++ b/doc/efun/symbolp
@@ -0,0 +1,14 @@
+SYNOPSIS
+        int symbolp(mixed arg)
+
+BESCHREIBUNG
+        Liefert 1, wenn das Argument ein Symbol ist, ansonsten 0.
+
+BEISPIEL
+        symbolp('foo) liefert 1.
+
+AENDERUNGEN
+        Eingefuehrt in 3.2@70.
+
+SIEHE AUCH
+        intp(E), quote(E)
diff --git a/doc/efun/tail b/doc/efun/tail
new file mode 100644
index 0000000..8c50b35
--- /dev/null
+++ b/doc/efun/tail
@@ -0,0 +1,9 @@
+SYNOPSIS
+        void tail(string file);
+
+BESCHREIBUNG
+        Listet das Ende eines Files. Es gibt kein Zeilenlimit, es werden aber
+        maximal 1000 Bytes ausgegeben.
+
+SIEHE AUCH
+        cat(E), ed(E)
diff --git a/doc/efun/tan b/doc/efun/tan
new file mode 100644
index 0000000..f99a085
--- /dev/null
+++ b/doc/efun/tan
@@ -0,0 +1,11 @@
+SYNOPSIS
+        float tan(int|float)
+
+BESCHREIBUNG
+        Liefert den Tangens des Argumentes.
+
+AENDERUNGEN
+        LDMud 3.2.9: Ganzzahlen (Integers) als Argument hinzugefuegt.
+
+SIEHE AUCH
+        sin(E), asin(E), cos(E), acos(E), atan(E), atan2(E)
diff --git a/doc/efun/tell_object b/doc/efun/tell_object
new file mode 100644
index 0000000..e2cfd9c
--- /dev/null
+++ b/doc/efun/tell_object
@@ -0,0 +1,47 @@
+SYNOPSIS
+        void tell_object(object|string obj, string str);
+        void tell_object(object|string obj, mixed *|mapping|struct|object msg);
+
+BESCHREIBUNG
+        Sendet einen Nachricht an das Objekt <obj> (das auch durch seinen
+        Objektnamen angegeben werden kann).
+
+        Ist die Nachricht ein String, wird der Text an interaktive Objekte
+        direkt ausgegeben, fuer andere Objekte wird die lfun catch_tell()
+        in diesen aufgerufen.
+
+        Ist die Nachricht ein anderer Typ, wird die lfun catch_msg() im
+        Empfaenger aufgerufen.
+
+BEMERKUNGEN
+        - wird in einem catch_msg() der Wert von <msg> veraendert, erhalten
+          alle nachfolgenden Objekte das veraenderte <msg> (Referenz!)
+
+BEISPIELE
+        // Dies gibt ein einfaches "Hi!" an den Spieler Thomas aus:
+
+            object wer;
+            wer = find_player("thomas");
+            tell_object(wer, "Hi!\n");
+
+        // Ein Beispiel mit zwei Objekten, das zeigt, wie das Zusammenspiel
+        // von catch_tell() und tell_object() ablaueft. Objekt1 ist ein
+        // Lebewesen mit Namen "Dummymonster", Objekt2 verteilt die Meldung:
+
+        Objekt1:
+            void catch_tell(string str) {
+                write("Erhaltener Text: "+str+"\n");
+            }
+
+        Objekt2:
+            void fun() {
+                object wer;
+                wer = find_living("dummymonster");
+                tell_object(wer, "Folge mir, Sterblicher!\n");
+                ...
+            }
+
+SIEHE AUCH
+        Aehnlich:   write(E), shout(E), say(E), tell_room(E), printf(E)
+        Verwandt:   catch_tell(E), catch_msg(A)
+        Sonstiges:  object_name(E)
\ No newline at end of file
diff --git a/doc/efun/tell_room b/doc/efun/tell_room
new file mode 100644
index 0000000..77f302c
--- /dev/null
+++ b/doc/efun/tell_room
@@ -0,0 +1,79 @@
+FUNKTION:
+        void tell_room(string|object obj, string str);
+        void tell_room(string|object obj, string str, object *exclude);
+
+        void tell_room(string|object obj, mixed *|mapping|struct|object msg)
+        void tell_room(string|object obj, mixed *|mapping|struct|object  msg
+                                        , object *exclude);
+
+BESCHREIBUNG:
+        Gibt einen Text <str> an den Raum <obj> aus. <obj> kann auch der
+        Objektname des Raumes als String sein.
+        Wenn das Raumobjekt mit seinem Namen angegeben ist, sucht der Driver
+        das Objekt unter diesem Namen und laedt es, falls notwendig.
+
+        Ist die Nachricht ein String, wird der Text an interaktive Objekte
+        direkt ausgegeben, fuer andere Objekte wird die lfun catch_tell()
+        in diesen aufgerufen.
+        Falls ein Lewebesen die Funktion catch_tell() definiert (-> shadow),
+        so wird der Text hier ausgewertet und nicht an den User ausgegeben.
+
+        Wenn das zweite Argument, die Nachricht, kein String ist, wird in
+        allen Lebewesen, die den Text erhalten, catch_msg() anstatt
+        catch_tell() aufgerufen.
+
+        Mit dem Array <*exclude> kann man verhindern, dass die Nachricht an
+        die darin enthaltenen Objekte gesendet wird.
+        Das ist sinnvoll, wenn zB ein Spieler Ausloeser einer Meldung ist
+        und diese selbst nicht erhalten soll.
+
+BEMERKUNGEN:
+        - der Eintrag von mehreren Anwesenden in *exclude ist moeglich
+        - wird in einem catch_msg() der Wert von <msg> veraendert, erhalten
+          alle nachfolgenden Objekte das veraenderte <msg> (Referenz!)
+
+        - say("str") ist verhaltensgleich zu
+          tell_room(environment(), "str", ({this_player()||this_object()}))
+
+BEISPIELE:
+        // Dies ist ein einfaches Beispiel fuer eine Meldung an alle An-
+        // wesenden im Raum.
+
+        tell_room(this_object(),"Ein leichter Wind kommt auf.\n");
+
+        // Diese Meldung wird im Raum /d/ebene/ark/raum.c ausgegeben, dieser
+        // Raum muss nicht derjenige sein, in dem das tell_room() ausgefuehrt
+        // wird.
+
+        tell_room("/d/ebene/ark/raum","Ein leichter Wind kommt auf.\n");
+
+
+        // Diese Meldung wird an alle Anwesenden im Raum AUSSER this_player()
+        // (der diese Meldung ausgeloest hat) ausgegeben. Der muss eine ge-
+        // sonderte Meldung ueber sein Stolpern per write() oder
+        // tell_object() bekommen.
+        tell_room(this_object(),
+                  break_string(this_player()->Name()+" stolpert.", 78),
+                  ({ this_player() }));
+        tell_object(this_player(), "Du stolperst.\n");
+
+        // Ein Beispiel mit zwei Objekten, das zeigt, wie das Zusammenspiel
+        // von catch_tell() und tell_room() ablaueft. Objekt1 ist ein
+        // Lebewesen mit Namen "Dummymonster", Objekt2 verteilt die Meldung:
+
+        Objekt1 (ein Lebewesen, steht im Env von this_player()):
+            void catch_tell(string str) {
+                write("Empfangen: "+str+"\n");
+            }
+
+        Objekt2:
+            void fun() {
+                tell_room(environment(this_player()), "Hallo Welt!\n");
+            }
+
+SIEHE AUCH
+        Aehnlich:   tell_object(E), write(E), shout(E), say(E), printf(E)
+        Verwandt:   catch_tell(E), catch_msg(A)
+        Sonstiges:  object_name(E)
+
+7.Aug 2007 Gloinson
diff --git a/doc/efun/terminal_colour b/doc/efun/terminal_colour
new file mode 100644
index 0000000..6cd1bce
--- /dev/null
+++ b/doc/efun/terminal_colour
@@ -0,0 +1,127 @@
+SYNOPSIS
+        varargs string terminal_colour(string str,
+                                       null | mapping | closure map,
+                                       int wrap, int indent);
+BESCHREIBUNG
+        Ist <map> ein Wert ungleich 0, ersetzt diese Efun alle Farb-
+        Definitionen der Form "%^KEY%^" (siehe unten fuer Details) im
+        String <str> und ersetzt sie durch die entsprechenden Werte aus dem
+        unter <map> angegebenen Farbschluessel.
+
+        Ist <map> ein Mapping, muessen die Eintraege das Format
+        "KEY" : "wert" haben; Eintraege, die keine Strings enthalten,
+        werden ignoriert. Einzige Ausnahme dazu: enthaelt <map> einen
+        Eintrag der Form 0:wert, wird dieser fuer alle Farbdefinitionen
+        verwendet, die keinem anderen Schluessel zugeordnet werden koennen.
+        <wert> kann in diesem Fall ein String oder eine Closure sein. Handelt
+        es sich um eine Closure, erhaelt diese den <KEY> als Argument und
+        muss einen String zurueck liefern, der <KEY> ersetzt.
+
+        Ist <map> eine Closure, wird diese mit den Farbdefinitionen <KEY>
+        als Argument aufgerufen und muss einen String zurueck liefern, der
+        die <KEY>s ersetzt.
+
+        Die speziellen Schluessel "%^%^" und "%%^^" werden immer durch das
+        Literal "%^" ersetzt.
+
+        Die Parameter <wrap> und <indent> sind optional. Ist nur <wrap>
+        angegeben, wird <str> in der Spalte <wrap> umgebrochen. Ist
+        zusaetzlich <indent> angegeben, werden alle umgebrochenen Zeilen
+        um <indent> Spalten eingerueckt.
+
+        Der Zeilenumbruch ignoriert die Laenge der Farbmakros und ihrer
+        Inhalte. Er bricht <str> anhand der Laenge der uebrigen Zeichen
+        um, ist also farb-neutral.
+
+        Ist <map> als 0 angegeben, fuehrt die Efun kein Suchen und Ersetzen
+        von Farbdefinitionen durch. Die Funktionalitaet von Zeilenumbruch
+        und Einrueckung bleiben erhalten, wenn gewuenscht. Auf diese Weise
+        dupliziert terminal_colour() die Funktion von sprintf("%-=s") und
+        wirkt als einfache Zeilenumbruch Funktion.
+
+
+        ERKENNEN VON FARBDEFINITIONEN
+
+        Wie bereits erwaehnt, werden die speziellen Schluessel "%^%^" und
+        "%%^^" durch das Literal "%^" ersetzt und spielen im Weiteren
+        keine Rolle.
+
+        Fuer den Eingabestring wird das folgende Format vorausgesetzt:
+
+            text { '%^' colorkey '%^' text } [ '%^' colorkey ]
+
+        Oder in Worten: die Efun trennt den String bei jedem '%^', das
+        sie antrifft und behandelt anschliessend jeden zweiten Teilstring
+        als Farbschluessel.
+
+        Merke: dieses Verhalten unterscheidet sich von der Behandlung des
+        Eingabestrings unter MudOS. Dort lautet die Syntax:
+
+            key_oder_text { '%^' key_oder_text }
+
+        Oder in Worten: die MudOS Efun trennt den String bei jedem '%^'
+        und versucht dann jeden Teilstring als Farbschluessel zu behandeln.
+        Dieses Verhalten laesst sich auch unter LPC erreichen:
+
+          string mudos_terminal_colour(string str, mapping ext, int w, int i)
+          {
+            return terminal_colour("%^"+implode(explode(str, "%^")-({""})
+                                               ,"%^%^")
+                                  , ext, w, i);
+          }
+
+
+BEISPIELE
+        mapping trans;
+        string str;
+
+        trans = ([ "GREEN" : "ansi-green", "RED" : "", "BLUE" : 1 ]);
+
+        str = terminal_colour( "%^GREEN%^ and %^RED%^ and %^BLUE%^", trans );
+
+        Dies fuehrt zu str == "ansi-green and  and BLUE".
+
+        "%^GREEN^%" wird ersetzt durch "ansi-green", weil <trans> das so
+        definiert,
+        "%^RED%^" wird aus <str> entfernt, weil es mit "" ersetzt wird, und
+        "%^BLUE%^" wird um die "%^" verkuert, weil der Eintrag zu BLUE in
+        <trans> keinen gueltigen Wert enthaelt (d.h. kein String ist). Das
+        selbe wuerde passieren, wenn <str> "%^DEFINE%^" enthalten wuerde,
+        zu dem es keinen Eintrag in <trans> gibt.
+
+        Merke: um direkt benachbarte Schluessel zu ersetzen, soll die
+        Efun wie folgt verwendet werden:
+
+            str = terminal_colour( "%^GREEN%^%^RED%^", trans );
+
+        Eine Eingabe der Form
+
+            str = terminal_colour( "%^GREEN%^RED%^", trans );
+
+        fuehrt zum logischen, aber vielleicht unerwarteten Ergebnis
+        "ansi-greenRED".
+
+
+        Einige Worte zum Zeilenumbruch:
+
+        Ein String, der ohne Einrueckung umgebrochen wird (<indent> ist 0),
+        sieht so aus:
+
+            "dies ist die erste Zeile\nund dies ist die zweite Zeile"
+
+        Ein String, der mit <indent> 3 umgebrochen wird, sieht so aus:
+
+            "dies ist die erste Zeile\n   und dies ist die zweite Zeile"
+
+AENDERUNGEN
+        Die Idee fuer diese Efun und die erste Implementierung stammen
+        aus MudOS; die Strategie fuer das Erkennen von Schluesseln
+        (eingeschlossen die pure Zeilenumbruch Funktion) wurde in
+        LDMud 3.2.8 geglaettet.
+        LDMud 3.2.9 fuegte die Verwendung von Closures zur Definition
+        von Farbschluesseln hinzu. Es erklaerte zudem offiziell das
+        Verhalten betreffen "%%^^" aus Gruenden besserer Kompatibilitaet
+        mit MudOS.
+
+SIEHE AUCH
+        sprintf(E)
diff --git a/doc/efun/test_bit b/doc/efun/test_bit
new file mode 100644
index 0000000..af82c95
--- /dev/null
+++ b/doc/efun/test_bit
@@ -0,0 +1,21 @@
+SYNOPSIS
+        int test_bit(string str, int n);
+
+BESCHREIBUNG
+        Gibt 0 oder 1 des <n>-ten Bits im String <str> zurueck.
+
+        Jedes Zeichen besteht aus 6 Bits. Jedem Zeichen ist also ein Wert
+        zwischen 0 und 63 zugeordnet (weil 2^6=64). Das erste Zeichen ist der
+        Leerschlag " " mit dem Wert 0 (keines der Bits ist gesetzt). Das
+        erste Zeichen im String ist dasjenige mit den niedrigsten Bits (0-5).
+
+BEISPIELE
+        test_bit("_", 5);   Liefert 1, weil "_" das 63. Zeichen ist und
+                            deshalb das 5. Bit gesetzt hat.
+
+        test_bit(" ", 3);   Liefert 0, weil " " das 0. Zeichen ist und deshalb
+                            kein Bit gesetzt hat.
+
+SIEHE AUCH
+        set_bit(E), clear_bit(E), last_bit(E), next_bit(E), count_bits(E),
+        and_bits(E), or_bits(E), xor_bits(E), invert_bits(E), copy_bits(E)
diff --git a/doc/efun/this_interactive b/doc/efun/this_interactive
new file mode 100644
index 0000000..92938bb
--- /dev/null
+++ b/doc/efun/this_interactive
@@ -0,0 +1,9 @@
+SYNOPSIS
+        object this_interactive();
+
+BESCHREIBUNG
+        Die Funktion gibt das momentane interaktive Objekt zurueck, falls
+        vorhanden, also dasjenige welches "die Entertaste gedrueckt hat".
+
+SIEHE AUCH
+        this_player(E), previous_object(E), interactive(E), living(E)
diff --git a/doc/efun/this_object b/doc/efun/this_object
new file mode 100644
index 0000000..5b60276
--- /dev/null
+++ b/doc/efun/this_object
@@ -0,0 +1,10 @@
+SYNOPSIS:
+        object this_object(void)
+
+DESCRIPTION:
+        Return the object pointer for this object. This is not to be
+        confused with the internal name of an object, which is used by
+        the id() function.
+
+SEE ALSO:
+        this_player(E), previous_object(E), object_name(E), find_object(E)
diff --git a/doc/efun/this_player b/doc/efun/this_player
new file mode 100644
index 0000000..5ab6420
--- /dev/null
+++ b/doc/efun/this_player
@@ -0,0 +1,18 @@
+SYNOPSIS
+        object this_player();
+
+BESCHREIBUNG
+        Liefert den momentanen Kommandogeber. Das kann ein interaktiver
+        Benutzer oder ein lebendiges Objekt sein, zum Beispiel ein NPC.
+
+        Wenn die Funktion von innerhalb des heart_beat() eines nicht
+        lebendigen Objekts aufgerufen wird, wird 0 zurueck gegeben.
+
+BEISPIEL
+        if(this_player() != this_interactive())
+        {
+            write("Hey, jemand zwingt uns, Kommandos auszufuehren!\n");
+        }
+
+SIEHE AUCH
+        this_object(E), previous_object(E), interactive(E), living(E)
diff --git a/doc/efun/throw b/doc/efun/throw
new file mode 100644
index 0000000..2a21cde
--- /dev/null
+++ b/doc/efun/throw
@@ -0,0 +1,16 @@
+SYNOPSIS
+        void throw(mixed arg);
+
+BESCHREIBUNG
+        Bricht die Programmverarbeitung ab. Wenn die Verarbeitung mit catch()
+        gestartet wurde, gibt dieses catch() <arg> als Fehlermeldung aus.
+
+        Der Aufruf von throw() ohne vorheriges catch() ist sinnlos und
+        erzeugt einen "throw without catch" Fehler.
+
+BEISPIEL
+        catch(throw("Verarbeitung abgebrochen!"));
+        Das macht nichts als "Verarbeitung abgebrochen!" auszugeben.
+
+SIEHE AUCH
+        catch(E), raise_error(E)
diff --git a/doc/efun/time b/doc/efun/time
new file mode 100644
index 0000000..f7016a4
--- /dev/null
+++ b/doc/efun/time
@@ -0,0 +1,21 @@
+SYNOPSIS
+        int time();
+
+BESCHREIBUNG
+        Liefert die Anzahl Sekunden, die seit dem 01. Januar 1970,
+        00:00:00 GMT verstrichen sind.
+
+        Die Zeitangabe basiert auf der Systemzeit des Hosts, der Driver
+        stellt jedoch sicher, dass das Resultat von time() monoton ansteigt
+        (also immer nur zu hoeheren Werten wechselt).
+
+        Das Resultat von time() veraendert sich nicht waehrend dem Abarbeiten
+        eines Kommandos.
+
+BEISPIEL
+        Um das aktuelle Datum und die aktuelle Zeit anzuzeigen:
+
+            write(ctime(time())+"\n");
+
+SIEHE AUCH
+        ctime(E), gmtime(E), localtime(E), utime(E)
diff --git a/doc/efun/tls_available b/doc/efun/tls_available
new file mode 100644
index 0000000..804a03d
--- /dev/null
+++ b/doc/efun/tls_available
@@ -0,0 +1,19 @@
+PRELIMINARY
+SYNOPSIS
+        int tls_available()
+
+DESCRIPTION
+        If the global TLS initialisation could not been set up, 
+        tls_is_available() returns 0, otherwise 1.
+        It is not very useful calling any other tls_*-efun if this one 
+        returns 0, since there is no TLS-encryption available.
+        Most likely the global initialisation fails due to missing or 
+        unreadable key resp. certificate-file.
+
+HISTORY
+        Introduced in LDMud 3.3.474 and following, backported to 3.2.11.
+
+SEE ALSO
+        tls_init_connection(E), tls_deinit_connection(E), 
+        tls_query_connection_state(E), tls_query_connection_info(E),
+        tls_check_certificate(E), tls_refresh_certs(E)
diff --git a/doc/efun/tls_check_certificate b/doc/efun/tls_check_certificate
new file mode 100644
index 0000000..f8afc8e
--- /dev/null
+++ b/doc/efun/tls_check_certificate
@@ -0,0 +1,49 @@
+PRELIMINARY
+SYNOPSIS
+        mixed *tls_check_certificate(object obj);
+        mixed *tls_check_certificate(object obj, int extra);
+
+DESCRIPTION
+        tls_check_certificate() checks the certificate of the secured
+        connection bound to <obj> (default is the current object).  If
+        <obj> is not interactive, or if TLS is not available, an error
+        is thrown.
+
+        If <obj> doesn't have a secure connection up and running, an
+        error is thrown.
+        Otherwise, the result is an array with these values:
+
+          int [0]      : Result code of SSL_get_verify_result (see man 1 verify
+                         subsection DIAGNOSTICS for possible values)
+          array [1]    : array with 3*n entries of extra x509 data.
+                         structure is:
+                            3*i    : numerical form of object name,
+                                     e.g. "2.5.4.3"
+                            3*i + 1: long or short name if available,
+                                     e.g. "commonName"
+                            3*i + 2: value
+          array [2]    : if extra is set:
+                            array with 3*n entries of x509 extension data
+                            data structure is:
+                            3*i    : numerical form of extension name
+                            3*i + 1: long or short name of extension
+                                     name if available
+                            3*i + 2: array of strings with the data
+                                     structure of [1]
+
+        Note: a x509 certificate can have more than one object with
+        the same name
+
+BUGS
+        Not supported when using GnuTLS.
+
+HISTORY
+        Introduced in LDMud 3.3.672/3.2.11.
+        LDMud 3.3.711/3.2.12 modified the behaviour to return the
+        low-level API result value, and to throw an error if the connection
+        is not secure.
+
+SEE ALSO
+        tls_init_connection(E), tls_deinit_connection(E), tls_error(E),
+        tls_query_connection_state(E), tls_query_connection_info(E),
+        tls_available(E), tls_refresh_certs(E), mudlib/psyc-tls.c
diff --git a/doc/efun/tls_deinit_connection b/doc/efun/tls_deinit_connection
new file mode 100644
index 0000000..7883801
--- /dev/null
+++ b/doc/efun/tls_deinit_connection
@@ -0,0 +1,20 @@
+PRELIMINARY
+SYNOPSIS
+        void tls_deinit_connection(object ob)
+
+DESCRIPTION
+        tls_deinit_connection() shuts down a TLS connection to
+        the interactive object <ob> (or this_object() if <ob> is not
+        given) but the connection is not closed.
+
+        Under normal circumstances there is no need to use this efun: most
+        clients operate in either secure or unsecure mode, but don't allow
+        switching connection security on the fly.
+
+HISTORY
+        Introduced in LDMud 3.3.474 and following, backported to 3.2.11.
+
+SEE ALSO
+        tls_init_connection(E), tls_error(E), tls_query_connection_state(E),
+        tls_query_connection_info(E), tls_available(E),
+        tls_check_certificate(E), tls_refresh_certs(E)
diff --git a/doc/efun/tls_error b/doc/efun/tls_error
new file mode 100644
index 0000000..2b5c788
--- /dev/null
+++ b/doc/efun/tls_error
@@ -0,0 +1,15 @@
+PRELIMINARY
+SYNOPSIS
+        string tls_error(int errorno)
+
+DESCRIPTION
+        tls_error() returns a string describing the error behind the
+        error number errorno.
+
+HISTORY
+        Introduced in LDMud 3.3.474 and following, backported to 3.2.11.
+
+SEE ALSO
+        tls_init_connection(E), tls_deinit_connection(E), 
+        tls_query_connection_state(E), tls_query_connection_info(E),
+        tls_available(E), tls_check_certificate(E), tls_refresh_certs(E)
diff --git a/doc/efun/tls_init_connection b/doc/efun/tls_init_connection
new file mode 100644
index 0000000..e33afae
--- /dev/null
+++ b/doc/efun/tls_init_connection
@@ -0,0 +1,50 @@
+PRELIMINARY
+SYNOPSIS
+        int tls_init_connection(object ob)
+        int tls_init_connection(object ob, string fun, string|object fob, mixed extra...)
+        int tls_init_connection(object ob, closure fun, mixed extra...)
+
+DESCRIPTION
+        tls_init_connection() tries to start a TLS secured connection to 
+        the interactive object <ob> (or this_object() if <ob> is not given).
+
+        Result:
+          errorcode < 0: unsuccessful, use tls_error() to get an useful
+                         description of the error
+             number > 0: the secure connection is still being set up in the
+                          background
+            number == 0: the secure connection is active.
+        
+        OpenSSL only:
+
+            If the callback <fun>/<fun>:<fob> is specified, it will be called
+            once the fate of the secure connection has been determined. The
+            first argument will be the return code from the handshake
+            (errorcode < 0 on failure, or 0 on success), followed by the
+            interactive object <ob> and any <extra> arguments.
+
+        If the TLS setup fails, it is not necessary to call
+        tls_deinit_connection().
+
+        IMPORTANT: During the TLS handshake nothing else must be sent
+        to the client! For the most cases (TLS-capable clients logging in)
+        this means that the TLS handshake is the first and only thing the
+        client gets to see while the handshake is in progress.
+
+        The driver automatically suppresses the printing of the prompt
+        while the TLS handshake is in progress.
+
+        If tls_init_connection() is called in the master::connect() function,
+        the driver will either call the set callback in place of logon(), or
+        if not callback has been set, delay the call of logon() until the
+        state of the connection is clear.
+
+HISTORY
+        Introduced in LDMud 3.3.474 and following, backported to 3.2.11.
+        LDMud 3.2.13/3.3.713 streamlined the handling of secure connections
+        during logon.
+
+SEE ALSO
+        tls_deinit_connection(E), tls_error(E), tls_query_connection_state(E),
+        tls_query_connection_info(E), tls_available(E),
+        tls_check_certificate(E), tls_refresh_certs(E), connect(M), logon(A)
diff --git a/doc/efun/tls_query_connection_info b/doc/efun/tls_query_connection_info
new file mode 100644
index 0000000..32aeef7
--- /dev/null
+++ b/doc/efun/tls_query_connection_info
@@ -0,0 +1,37 @@
+PRELIMINARY
+SYNOPSIS
+        #include <sys/ tls.h>
+        int *tls_query_connection_info (object ob)
+
+DESCRIPTION
+        If <ob> does not have a TLS connection or if the connection
+        is still being set-up, or if <ob> is not interactive, the efun
+        returns 0.
+
+        If <ob> has a TLS connection, tls_query_connection_info()
+        returns an array that contains some parameters of <ob>'s
+        connection:
+
+          int|string [TLS_CIPHER]: the cipher used
+          int        [TLS_COMP]:   the compression used
+          int        [TLS_KX]:     the key-exchange used
+          int        [TLS_MAC]:    the digest algorithm used
+          int|string [TLS_PROT]:   the protocol used
+
+        To translate these numbers into strings, <tls.h> offers a
+        number of macros:
+
+          TLS_xxx_TABLE: a literal array of strings describing the
+            value in question.
+          TLS_xxx_NAME(x): a macro translating the numeric result
+            value into a string.
+
+          xxx: CIPHER, COMP, KX, MAC, PROT
+
+HISTORY
+        Introduced in LDMud 3.3.474 and following, backported to 3.2.11.
+
+SEE ALSO
+	tls_init_connection(E), tls_deinit_connection(E), tls_error(E),
+	tls_query_connection_state(E), tls_available(E),
+        tls_check_certificate(E), tls_refresh_certs(E)
diff --git a/doc/efun/tls_query_connection_state b/doc/efun/tls_query_connection_state
new file mode 100644
index 0000000..2624f20
--- /dev/null
+++ b/doc/efun/tls_query_connection_state
@@ -0,0 +1,16 @@
+PRELIMINARY
+SYNOPSIS
+        int tls_query_connection_state(object ob)
+
+DESCRIPTION
+        tls_query_connection_state() returns a positive number if <ob>'s
+        connection is TLS secured, 0 if it's unsecured, and a negative number
+        if the TLS connection setup is still being set-up.
+
+HISTORY
+        Introduced in LDMud 3.3.474 and following, backported to 3.2.11.
+
+SEE ALSO
+	tls_init_connection(E), tls_deinit_connection(E), tls_error(E), 
+	tls_query_connection_info(E), tls_available(E),
+        tls_check_certificate(E), tls_refresh_certs(E)
diff --git a/doc/efun/tls_refresh_certs b/doc/efun/tls_refresh_certs
new file mode 100644
index 0000000..c8364c5
--- /dev/null
+++ b/doc/efun/tls_refresh_certs
@@ -0,0 +1,17 @@
+PRELIMINARY
+SYNOPSIS
+        void tls_refresh_certs()
+
+DESCRIPTION
+        Reload the certificates and certificate revocation information.
+
+BUGS
+        Not supported when using GnuTLS.
+
+HISTORY
+        Introduced in LDMud 3.3.714/3.2.15.
+
+SEE ALSO
+        tls_init_connection(E), tls_deinit_connection(E), tls_error(E),
+        tls_query_connection_state(E), tls_query_connection_info(E),
+        tls_available(E), tls_check_certificate(E), mudlib/psyc-tls.c
diff --git a/doc/efun/to_array b/doc/efun/to_array
new file mode 100644
index 0000000..21b1a93
--- /dev/null
+++ b/doc/efun/to_array
@@ -0,0 +1,30 @@
+SYNOPSIS
+        mixed *to_array(string arg);
+        mixed *to_array(symbol arg);
+        mixed *to_array(quotedarray arr);
+        mixed *to_array(mixed *arg);
+        mixed *to_array(struct);
+
+        (int*)<value>
+
+BESCHREIBUNG
+        Strings und Symbole werden umgewandelt in ein Integer-Array, das aus
+        den Zeichen von <arg> besteht.
+
+        Gequotete Arrays werden "entquotet", und Arrays bleiben, wie sie sind.
+
+        Structs werden in normale Arrays umgewandelt.
+
+BEISPIELE:
+        to_array("12") liefert ({33,34}).
+
+BUGS
+        Die Cast-Schreibweise funktioniert nur, wenn der genaue Wert von
+        <value> zum Zeitpunkt der Kompilierung bekannt ist. Dies wird
+        nicht geaendert werden, da die Funktionsform verwendet werden kann.
+
+HISTORY
+        LDMud 3.3.250 added structs to the accepted data types.
+
+SIEHE AUCH
+        to_int(E), to_string(E), to_struct(E)
diff --git a/doc/efun/to_float b/doc/efun/to_float
new file mode 100644
index 0000000..7b989ce
--- /dev/null
+++ b/doc/efun/to_float
@@ -0,0 +1,19 @@
+SYNOPSIS
+        float to_float(int arg);
+        float to_float(string arg);
+        float to_flaot(float arg);
+
+        (float)<value>
+
+BESCHREIBUNG
+        Integers werden zu Floats erweitert, Strings werden in Floats
+        konvertiert bis zum ersten Zeichen, das nicht mehr zum Float
+        gehoert. Floats werden direkt zurueck gegeben.
+
+BUGS
+        Die Cast-Schreibweise funktioniert nur, wenn der genaue Wert von
+        <value> zum Zeitpunkt der Kompilierung bekannt ist. Dies wird nicht
+        geaendert werden, da die Funktionsform verwendet werden kann.
+
+SIEHE AUCH
+        to_string(E), to_int(E), sscanf(E)
diff --git a/doc/efun/to_int b/doc/efun/to_int
new file mode 100644
index 0000000..6c648e4
--- /dev/null
+++ b/doc/efun/to_int
@@ -0,0 +1,32 @@
+SYNOPSIS
+        int to_int(string arg);
+        int to_int(float arg);
+        int to_int(int arg);
+        int to_int(closure arg);
+
+        (int)<value>
+
+BESCHREIBUNG
+        Bei Floats werden die Nachkommastellen abgeschnitten, Strings mit
+        Ziffern am Anfang werden bis zum ersten Nicht-Ziffern-Zeichen in
+        Integers umgewandelt. Lfun-Closures werden in ihren Funktionsindex
+        konvertiert, Variablen-Closures in ihren Variablenindex. Integers
+        werden unveraendert zurueck gegeben.
+
+        Bezueglich Floats ist es wichtig, Rundungseffekte zu beachten:
+        to_int(3.1*10.0) ergibt nicht 31, sondern 30, weil intern das
+        Resultat der Multiplikation 30.999999 ergibt.
+     
+        Diese Funktion unterstuetzt die Basisprefixe '0x', '0o' und '0b'.
+
+BUGS
+        Die Cast-Schreibweise funktioniert nur, wenn der genaue Wert von
+        <value> zum Zeitpunkt der Kompilierung bekannt ist. Dies wird
+        nicht geaendert werden, da die Funktionsform verwendet werden kann.
+
+AENDERUNGEN
+        Eingefuehrt in 3.2.1@2
+        LDMud 3.2.11 fuehrte die Basisprefixe ein.
+
+SIEHE AUCH
+        to_string(E), sscanf(E)
diff --git a/doc/efun/to_object b/doc/efun/to_object
new file mode 100644
index 0000000..bb577f0
--- /dev/null
+++ b/doc/efun/to_object
@@ -0,0 +1,15 @@
+SYNOPSIS
+	object to_object(string arg)
+	object to_object(closure arg)
+	object to_object(object arg)
+
+DESCRIPTION
+	The argument is converted into an object, if possible.
+	For strings, the object with a matching object_name() is
+	returned, or 0 if there is none, as find_object() does.
+	For (bound!) closures, the object holding the closure is
+	returned.
+	Objects and the number 0 return themselves.
+
+SEE ALSO
+	find_object(E), to_array(E), to_int(E), to_string(E)
diff --git a/doc/efun/to_string b/doc/efun/to_string
new file mode 100644
index 0000000..1d23ef6
--- /dev/null
+++ b/doc/efun/to_string
@@ -0,0 +1,29 @@
+SYNOPSIS
+        string to_string(mixed arg)
+
+BESCHREIBUNG
+        <arg> wird in einen String umgewandelt. Das klappt mit Werten vom Typ
+        int, float, object, array, struct, symbol, string oder closure.
+
+        Closures werden in einen passenden Namen umgewandelt (vorwiegend fuer
+        Debugging-Zwecke geeignet).
+
+ANMERKUNGEN
+        Arrays werden als "explodete" Strings betrachtet, also Arrays von
+        Zeichencodes. Sie werden bis zur ersten 0 oder bis zum ersten
+        nicht-numerischen Eintrag "implodet", je nachdem, was zuerst eintritt.
+
+        Das bedeutet, dass to_string( ({ 49, 50 }) ); "12" liefert, und nicht
+        "({ 49, 50 })"
+
+FEHLER
+        Die Cast-Schreibweise funktioniert nur, wenn der genaue Wert von
+        <value> zum Zeitpunkt der Kompilierung bekannt ist. Dies wird nicht
+        geaendert werden, da die Funktionsform verwendet werden kann.
+
+GESCHICHTE
+        LDMud 3.2.8 laesst Lambdaclosures als gueltige Datentypen zu.
+        LDMud 3.3.250 laesst structs als gueltige Datentypen zu.
+
+SIEHE AUCH
+        to_array(E), to_int(E), to_object(E), to_struct(E), sprintf(E)
diff --git a/doc/efun/to_struct b/doc/efun/to_struct
new file mode 100644
index 0000000..e5b0bfa
--- /dev/null
+++ b/doc/efun/to_struct
@@ -0,0 +1,47 @@
+SYNOPSIS
+        mixed to_struct(mixed *|mapping data)
+        mixed to_struct(mixed *|mapping data, struct template)
+        mixed to_struct(struct data)
+        mixed to_struct(struct data, struct template)
+
+DESCRIPTION
+        The given array, mapping or struct <data> is returned as a struct.
+        If a <template> struct is given, the returned struct is of the same
+        type. Without a template, an anonymous struct is returned in case of
+        arrays and mappings and in case of structs <data> is returned
+        unchanged.
+
+        If <data> is an array, its elements are assigned in order to the
+        resulting struct. For an anonymous struct, all elements of <data>
+        are assigned, for a templated struct only as many as fit into
+        the struct.
+
+        If <data> is a mapping and no template is given, the resulting
+        anonymous struct contains all elements from <data> with a string
+        key; the key name is used as struct member name.
+
+        If <data> is a mapping and a template is given, the struct
+        member names are used as keys for lookups in <data>; the found
+        data is assigned to the struct members.
+
+        If <data> is a struct and a template is given, a struct of the type
+        of template is created and all members from <data> are copied to the
+        new struct, which exist in both structs. This conversion is only
+        allowed between a struct and one of its base structs or a base struct
+        and one of its children. Otherwise an error is raised.
+
+        Neither <data> nor <template> will be changed in this process - the
+        result is a new struct value. The actual value of <template> does not
+        matter, only its type.
+
+        Since the returned struct can't be known at compile time, the
+        efun is declared to return 'mixed'.
+
+HISTORY
+        Introduced in LDMud 3.3.250 .
+        LDMud 3.3.344 added the template argument.
+        LDMud 3.3.433 added the conversion from mappings.
+        LDMud 3.3.720 added the conversion of structs into another struct.
+
+SEE ALSO
+        to_array(E), to_string(E), mkmapping(E), structs(LPC)
diff --git a/doc/efun/trace b/doc/efun/trace
new file mode 100644
index 0000000..36edc79
--- /dev/null
+++ b/doc/efun/trace
@@ -0,0 +1,54 @@
+GESCHUETZT
+SYNOPSIS
+        #include <sys/trace.h>
+
+        int trace(int traceflags);
+
+BESCHREIBUNG
+        Setzt die Trace Flags und liefert die alten Trace Flags zurueck.
+        Wenn Tracing eingeschaltet ist, wird waehrend der Ausfuehrung eine
+        Menge Informationen ausgegeben. Zu viel Output kann die Verbindung
+        lahm legen oder sogar den ganzen Treiber zum Absturz bringen.
+
+        Tracing erfolgt auf einer Pro-Verbindung-Basis: jeder interaktive (!)
+        User kann sein eigenes Tracelevel und -praefix festlegen. Jeder
+        erhaelt nur den Traceoutput fuer den Code, der waehrend der
+        Auswertung eines vom User eingegeben Kommandos ausgefuehrt wird.
+
+        Die Trace-Bits (aus <trace.h>) sind:
+
+            TRACE_NOTHING       (  0): Beendet das Tracing
+            TRACE_CALL          (  1): Tracet alle Aufrufe von Lfuns
+            TRACE_CALL_OTHER    (  2): Tracet alle call_other() Aufrufe
+            TRACE_RETURN        (  4): Tracet Resultate von Funktionen
+            TRACE_ARGS          (  8): Gibt Argumente und Resultate von
+                                       Funktionen aus
+            TRACE_EXEC          ( 16): Tracet alle ausgefuehrten Anweisungen
+            TRACE_HEART_BEAT    ( 32): Tracet den Heartbeat Code
+            TRACE_APPLY         ( 64): Tracet Treiber-Applies
+            TRACE_OBJNAME       (128): Gibt den Namen des Objektes aus
+
+        TRACE_EXEC und TRACE_HEART_BEAT sollten nicht verwendet werden, weil
+        sie massiven Output verursachen. TRACE_OBJNAME sollte nicht verwendet
+        werden, wenn bekannt ist, welches Objekt getracet wird.
+
+        Die Master-Lfun valid_trace() wird mit ("trace", traceflags)
+        aufgerufen, um die Erlaubnis fuer die Nutzung von trace() zu erhalten.
+
+BEISPIEL
+        object obj;
+        string prefix;
+        obj=find_player("thomas");
+        prefix=objec_name(obj);
+        prefix=prefix[1..strlen(prefix)-1];  /* entfernt den Praefix "/" */
+        traceprefix(prefix);
+        /* Von hier an wird nur Code im Objekt std/player#69 getracet */
+        trace(TRACE_CALL|TRACE_CALL_OTHER|TRACE_RETURN|TRACE_ARGS);
+        ...
+        trace(TRACE_NOTHING);
+
+AENDERUNGEN
+        LDMud 3.2.9 uebergibt auch <traceflags> an valid_trace()
+
+SIEHE AUCH
+        traceprefix(E)
diff --git a/doc/efun/traceprefix b/doc/efun/traceprefix
new file mode 100644
index 0000000..0f46f45
--- /dev/null
+++ b/doc/efun/traceprefix
@@ -0,0 +1,25 @@
+SYNOPSIS:
+        string traceprefix(string prefix)
+        string traceprefix(int dummy)
+
+DESCRIPTION:
+        If called with a string, only objects matching this prefix will
+        be traced. The string must not contain a leading "/" because
+        the object names are stored internally without it. If called
+        with a number, the traceprefix will be ignored and all objects
+        will be traced. Returns the last traceprefix or 0 if there
+        wasn't any.
+
+EXAMPLE:
+        object obj;
+        string prefix;
+        obj = find_player("deepthought");
+        prefix = object_name(obj);
+        prefix = prefix[1..strlen(prefix)-1]; /* cut off the leading "/" */
+        traceprefix(prefix);
+        trace(1|2|4|8);
+        ...
+        trace(0);
+        
+SEE ALSO:
+        trace(E)
diff --git a/doc/efun/transfer b/doc/efun/transfer
new file mode 100644
index 0000000..b413f02
--- /dev/null
+++ b/doc/efun/transfer
@@ -0,0 +1,25 @@
+VERALTET
+SYNOPSIS
+        int transfer(object item, object dest)
+
+BESCHREIBUNG
+        Diese Funktion existiert lediglich aus Kompatibilitaetsgrunden, und
+        dann auch nur, wennn der Driver mit USE_DEPRECATED kompiliert wird.
+
+        Das Object <item> wird in Objekt <dest> bewegt. Verschiedene Tests
+        werden durchgefuehrt, und das Resultat beschreibt den (Miss)Erfolg:
+
+        0: Erfolg.
+        1: Zu schwer fuer <dest>
+        2: Kann nicht fallen gelassen werden.
+        3: Kann nicht aus seinem  Behaelter genommen werden.
+        4: <item> kann in keinen Behaelter gesteckt werden.
+        5: <dest> akzeptiert <item> nicht.
+        6: <item> kann nicht aufgenommen werden.
+
+        Die Funktion ruft die lfuns add_weight(), drop(), get(),
+        prevent_insert(), add_weight(), und can_put_and_get() nach Bedarf..
+
+SIEHE AUCH
+         move_object(E), drop(A), get(A), prevent_insert(A),
+         can_put_and_get(A), add_weight(A)
diff --git a/doc/efun/transpose_array b/doc/efun/transpose_array
new file mode 100644
index 0000000..68e2a78
--- /dev/null
+++ b/doc/efun/transpose_array
@@ -0,0 +1,22 @@
+SYNOPSIS:
+	mixed *transpose_array (mixed *arr);
+
+DESCRIPTION:
+	transpose_array ( ({ ({1,2,3}), ({a,b,c}) }) )
+			== ({ ({1,a}), ({2,b)}, ({3,c}) }) 
+
+	transpose_array() applied to an alist results in an array of
+	({ key, data }) pairs, useful if you want to use sort_array()
+	or filter() on the alist.
+
+EXAMPLE:
+	sort_array(transpose_array( ({ m_indices(map), m_values(map) }) ),
+		   lambda( ({ 'a, 'b }),
+			   ({ #'<, ({ #'[, 'a, 0 }),
+				   ({ #'[, 'b, 0}) }) ) )
+
+	The given mapping 'map' is returned as an array of 
+	({ key,	data })  pairs, sorted by the keys.
+
+SEE ALSO:
+	alists(LPC), sort_array(E)
diff --git a/doc/efun/trim b/doc/efun/trim
new file mode 100644
index 0000000..4525a82
--- /dev/null
+++ b/doc/efun/trim
@@ -0,0 +1,33 @@
+SYNOPSIS
+        #include <sys/strings.h>
+
+        string trim(string str);
+        string trim(string str, int where);
+        string trim(string str, int where, string char);
+
+BESCHREIBUNG
+        Entfernt alle vorausgehenden und abschliessenden Zeichen <char> in
+        einem String <str> und gibt den neuen String zurueck.
+
+        <char> kann entweder ein oder mehrere Zeichen sein. Wird <char> nicht
+        angegeben, wird standardmaessig der Leerschlag " \t" genutzt.
+
+        Mit <where> kann angegeben werden, wo Zeichen entfernt werden:
+
+            TRIM_LEFT   (1):        entfernt alle vorausgehenden
+                                    Zeichen <char>
+            TRIM_RIGHT  (2):        entfernt alle abschliessenden
+                                    Zeichen <char>
+            TRIM_BOTH   (3 oder 0): entfernt sowohl vorausgehende als auch
+                                    abschliessende Zeichen <char>
+
+BEISPIEL
+        trim("    1234    ");                       ergibt: "1234"
+        trim("    1234    ", TRIM_RIGHT);           ergibt: "    1234"
+        trim("    1234    ", TRIM_BOTH, " 1");      ergibt: "234"
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.7
+
+SIEHE AUCH
+        regreplace(E)
diff --git a/doc/efun/typeof b/doc/efun/typeof
new file mode 100644
index 0000000..151bb96
--- /dev/null
+++ b/doc/efun/typeof
@@ -0,0 +1,13 @@
+SYNOPSIS
+        int typeof(mixed arg);
+
+BESCHREIBUNG
+        Gibt einen Code fuer den Typ des Arguments <arg>. Die Typen sind
+        definiert in <sys/lpctypes.h>
+
+AENDERUNGEN
+        Eingefuehrt in 3.2@63
+
+SIEHE AUCH
+        get_type_info(E), intp(E), objectp(E), floatp(E), pointerp(E),
+        closurep(E), symbolp(E), stringp(E), mappingp(E)
diff --git a/doc/efun/unbound_lambda b/doc/efun/unbound_lambda
new file mode 100644
index 0000000..804b1b5
--- /dev/null
+++ b/doc/efun/unbound_lambda
@@ -0,0 +1,27 @@
+SYNOPSIS
+        closure unbound_lambda(mixed *arg, mixed code);
+
+BESCHREIBUNG
+        Erzeugt eine Lambda-Closure, die nicht an ein Objekt gebunden ist,
+        entsprechend einer Lambda-Funktion in LISP.
+
+        Die Closure kann keine Referenz zu globalen Variablen enthalten.
+        Lfun-Closures werden unveraendert in die Closure eingebunden, da es
+        kein Ursprungsobjekt fuer diese Closure gibt.
+
+        Bevor die Closure aufgerufen werden kann, muss sie an ein Objekt
+        gebunden werden. Normale Objekte koennen Closures nur an sich selbst
+        binden, das Binden an andere Objekte erzeugt eine Schutzverletzung.
+
+        Der Punkt ist, dass previous_object() fuer Aufrufe innerhalb der
+        Closure auf das Objekt zeigt, das bind_lambda() aufgerufen hat, und
+        alle objekt- oder uid-basierten Sicherheitschecks beziehen sich
+        auf jenes Objekt.
+
+        Das erste Argument <*arg> ist ein Array, das die Argumente (Symbole)
+        enthaelt, die an die Closure uebergeben werden, wenn diese mit
+        funcall() oder apply() ausgewertet wird. Das zweite Argument <code>
+        enthaelt den Code der Closure.
+
+SIEHE AUCH
+        closures(LPC), lambda(E), apply(E), funcall(E), bind_lambda(E)
diff --git a/doc/efun/unique_array b/doc/efun/unique_array
new file mode 100644
index 0000000..390c56e
--- /dev/null
+++ b/doc/efun/unique_array
@@ -0,0 +1,47 @@
+SYNOPSIS
+        mixed unique_array(object *obj, string|closure fun)
+        mixed unique_array(object *obj, string|closure fun, mixed skip)
+        mixed unique_array(object *obj, string|closure fun, mixed extra...
+                                      , mixed skip)
+
+BESCHREIBUNG
+        Gruppiert alle Objekte aus <*obj>, fuer die die Funktion <fun>
+        den gleichen Wert liefert. Wenn <*obj> etwas anderes als Objekte
+        enthaelt, werden diese ignoriert.
+        
+        Ist die Funktion mit Namen angegeben, wird sie in jedem Objekt
+        in <*obj> einmal aufgerufen. Wurden <extra> Argumente
+        gegeben, werden diese an die Funktion bei jedem Aufruf als
+        Parameter uebergeben.
+
+        Ist die Funktion als Closure angegeben, wird sie fuer jedes Objekt
+        in <*obj> einmal aufgerufen, wobei das Objekt als erstes Argument
+        uebergeben wird, gefolgt von etwaiigen <extra> Argumenten.
+
+        Wird ein Argument <skip> angegeben (bei Verwendung von <extra>
+        Argumenten muss dies geschehen), und entspricht <skip> dem
+        Resultat von <separator> fuer ein Element aus <*obj>, so wird
+        dieses Element nicht in das Resultat von unique_array()
+        uebernommen.
+        
+        Das Resultat von unique_array() hat die Form:
+
+            ({ ({same1:1, same1:2, ... same1:n}),
+               ({same2:1, same2:2, ... same2:n}),
+               ({samem:1, samem:2, ... samem:n}) })
+
+BEISPIEL
+        Um ein Array von Arrays zu erhalten, das alle Benutzer, nach Level
+        gruppiert, enthaelt:
+
+            mixed *arr;
+            arr=unique_array(users(), "_query_level", -1);
+
+        Goetter haben einen Level von -1. Sie werden nicht in arr aufgenommen,
+        weil <skip> == -1.
+
+SIEHE AUCH
+      Arrays:       filter(E), map(E)
+      Objektarrays: filter_objects(E), map_objects(E)
+      Mappings:     filter(E), map(E), filter_indices(E), map_indices(E)
+
diff --git a/doc/efun/unmkmapping b/doc/efun/unmkmapping
new file mode 100644
index 0000000..3beeccb
--- /dev/null
+++ b/doc/efun/unmkmapping
@@ -0,0 +1,27 @@
+SYNOPSIS
+        *mixed unmkmapping(mapping map);
+
+BESCHREIBUNG
+        Wandelt das Mapping <map> in ein Array von Arrays aus, das alle Keys
+        und Werte von <map> enthaelt und gibt dieses Array zurueck.
+
+        Das Resultat von unmkmapping() hat die Form ({ keys[], data0[],
+        data1[] ... }), wobei keys[] ein Array aller Keys ist, data0[] ein
+        Array mit allen Werten aus der ersten Spalte, data1[] ein Array mit
+        allen Werten aus der zweiten Spalte etc. Das heisst, dass die
+        Werte von key[x] in data0[x], data1[x] usw. gespeichert sind.
+
+        unmkmapping() ist die Umkehrfunktion von mkmapping(), sodass gilt:
+
+            apply(#'mkmapping, unmkmapping(m)) == m
+
+BEISPIEL
+        mapping m = ([ 1:10;20, 2:11;21 ]);
+        unmkmapping(m) ergibt: ({ ({1, 2}) , ({10, 11}) , ({20, 21}) })
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.6.
+
+SIEHE AUCH
+        mappings(LPC), mappingp(E), m_indices(E), m_values(E), m_delete(E),
+        sizeof(E), widthof(E).
diff --git a/doc/efun/unquote b/doc/efun/unquote
new file mode 100644
index 0000000..8000060
--- /dev/null
+++ b/doc/efun/unquote
@@ -0,0 +1,17 @@
+SYNOPSIS
+        quoted_array unquote(quoted_array arr);
+        string|symbol unquote(symbol sym);
+
+BESCHREIBUNG
+        Entfernt ein Quote von einem gequoteten Array oder Symbol. Wenn das
+        letzte Quote von einem Symbol entfernt wird, entsteht ein String.
+
+BEISPIELE:
+        unquote('foo);              ergibt: "foo"
+        unquote( '({1,2,3}) );      ergibt: ({1,2,3})
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9.
+
+SIEHE AUCH
+        quote(E), symbolp(E)
diff --git a/doc/efun/unshadow b/doc/efun/unshadow
new file mode 100644
index 0000000..688d5ff
--- /dev/null
+++ b/doc/efun/unshadow
@@ -0,0 +1,21 @@
+FUNKTION
+     void unshadow()
+
+BESCHREIBUNG
+     Das aufrufende Objekt wird als Shadow von allen anderen Objekten
+     entfernt, denen es uebergeworfen war. Wenn dem aufrufenden Objekt
+     selbst ein Shadow uebergeworfen war, wird dieser entfernt.
+     Man sollte diese (s)efun rufen, bevor man den Schatten zerstoert.
+
+BEISPIELE
+     // B beschattet A
+     void b::stop_shadowing() {
+       unshadow();
+     }
+
+SIEHE AUCH
+     Generell:	     shadow(E)
+     Rechte:	     query_allow_shadow(M), query_prevent_shadow(L)
+     Informationen:  query_shadowing(E)
+
+20.08.2009, Zesstra
diff --git a/doc/efun/upper_case b/doc/efun/upper_case
new file mode 100644
index 0000000..ac1ffa5
--- /dev/null
+++ b/doc/efun/upper_case
@@ -0,0 +1,12 @@
+SYNOPSIS
+        string upper_case(string str);
+
+BESCHREIBUNG
+        Wandelt alle Zeichen in <str> in Grossbuchstaben um und gibt das
+        Resultat zurueck.
+
+BEISPIEL
+        upper_case("Heya!")     ergibt: "HEYA!"
+
+SIEHE AUCH
+        capitalize(E), lower_case(E)
diff --git a/doc/efun/users b/doc/efun/users
new file mode 100644
index 0000000..48b4ae3
--- /dev/null
+++ b/doc/efun/users
@@ -0,0 +1,5 @@
+SYNOPSIS
+        *object users();
+
+BESCHREIBUNG
+        Liefert ein Array, das alle interaktiven Benutzer enthaelt.
diff --git a/doc/efun/utime b/doc/efun/utime
new file mode 100644
index 0000000..96218af
--- /dev/null
+++ b/doc/efun/utime
@@ -0,0 +1,22 @@
+SYNOPSIS
+        *int utime()
+
+BESCHREIBUNG
+        Liefert ein Array der Zeit, die seit dem 01. Januar 1970,
+        00:00:00 GMT vergangen ist, mit Genauigkeit in Mikrosekunden
+        (0.000001 Sekunden).
+
+        Zurueck gegeben wird ein Array der Form:
+            int[0]: Anzahl Sekunden seit Beginn der Zeitrechnung
+            int[1]: Anzahl Mikrosekunden innerhalb der aktuellen Sekunde
+
+BEISPIEL
+        write(ctime(utime())+"\n");
+
+        Gibt das aktuelle Datum und Zeit zurueck.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9.
+
+SIEHE AUCH
+        ctime(E), gmtime(E), localtime(E), time(E)
diff --git a/doc/efun/variable_exists b/doc/efun/variable_exists
new file mode 100644
index 0000000..1bbd843
--- /dev/null
+++ b/doc/efun/variable_exists
@@ -0,0 +1,29 @@
+SYNOPSIS
+        #include <functionlist.h>
+
+        string variable_exists(string str [, int flags]);
+        string variable_exists(string str, object obj [, int flags]);
+
+BESCHREIBUNG
+        Sucht eine Varialbe <str> in this_object() oder (falls angegeben)
+        im Objekt <obj>.
+
+        Das Resultat ist der Name des Programms, in dem die Variable definiert
+        ist. Das kann entweder object_name(obj) sein oder der Name eines
+        geerbten Programms. Wenn sich der Treiber nicht im Compat-Modus
+        befindet, beginnt der zurueck gelieferte Name immer mit '/'.
+
+        Wird <flags> NAME_HIDDEN gesetzt, so liefert variable_exists() auch
+        Informationen ueber Variablen vom Typ "static" und "protected" in
+        anderen Objekten. Es ist nicht moeglich, Informationen ueber "private"
+        deklarierte Variablen zu erhalten.
+
+        Wird die Variable nicht gefunden (weil sie nicht existiert oder weil
+        sie fuer das aufrufende Objekt nicht sichtbar sind), wird 0 zurueck
+        geliefert.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.10.
+
+SIEHE AUCH
+        function_exists(E), variable_list(E)
diff --git a/doc/efun/variable_list b/doc/efun/variable_list
new file mode 100644
index 0000000..673b377
--- /dev/null
+++ b/doc/efun/variable_list
@@ -0,0 +1,60 @@
+GESCHUETZT
+SYNOPSIS
+        #include <sys/functionlist.h>
+        #include <sys/lpctypes.h>
+
+        *mixed variable_list(object obj, int flags = RETURN_FUNCTION_NAME);
+
+BESCHREIBUNG
+        Liefert ein Array mit Informationen ueber die Variablen von <obj>.
+        Fuer jede Variable werden 1 bis 4 Werte in diesem Array gespeichert,
+        abhaengig von <flags>. Die Resultate werden in dieser Reihenfolge
+        im Array abgespeichert:
+          - der Name der Variablen
+          - die Flags der Variablen (siehe weiter unten)
+          - der Rueckgabetyp (gemaess mudlib/sys/lpctypes.h)
+          - der Wert der Variablen
+
+        <obj> kann als Objekt oder als Dateinamen uebergeben werden. Im
+        zweiten Fall versucht variable_list() nicht, das Objekt vor der
+        Verarbeitung zu laden.
+
+        Wenn <obj> nicht das aufrufende Objekt ist und der Wert der Variablen
+        abgefragt wird, erzeugt dies eine Schutzverletzung ("variable_list",
+        <obj>).
+
+        Mit <flags> wird festgelegt, welche Informationen ueber welche
+        Variablen abgefragt werden. Folgende Flags aus <sys/functionlist.h>
+        koennen mit binaerem Oder kombiniert werden:
+
+        Auswahl der gesammelten Information:
+            RETURN_FUNCTION_NAME    liefert den Namen der Variablen
+            RETURN_FUNCTION_FLAGS   liefert die Flags der Variablen (s. unten)
+            RETURN_FUNCTION_TYPE    liefert den Rueckgabetyp
+            RETURN_VARIABLE_VALUE   liefert den Wert der Variablen
+
+        Auswahl der Variablen, die ausgewertet werden:
+            NAME_INHERITED        schliesst geerbte Variablen aus
+            TYPE_MOD_STATIC       schliesst "static" deklarierte Variablen aus
+            TYPE_MOD_NOSAVE       schliesst "nosave" deklarierte Variablen aus
+            TYPE_MOD_PRIVATE      schliesst "private" deklarierte Variablen aus
+            TYPE_MOD_PROTECTED    schliesst "protected" deklarierte Variablen
+                                  aus
+            NAME_HIDDEN           enthaelt Variablen, die geerbt wurden.
+
+        Die Flags der Variablen koennen die Auswahl-Flags enthalten und
+        zusaeztlich folgende Werte:
+            TYPE_MOD_VIRTUAL      die Variable wurde virtuell geerbt
+            TYPE_MOD_NO_MASGK     die Variable ist "nomask" deklariert
+            TYPE_MOD_PUBLIC       die Variable ist "public" deklariert
+
+        All diese Flags sind in mudlib/sys/functionlist.h definiert und
+        sollten an einen allgemein zugaenglichen Platz kopiert werden.
+        Die Rueckgabewerte sind in mudlib/sys/lpctypes.h definiert, die
+        auch in die Mudlib kopiert werden sollten.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.10
+
+SIEHE AUCH
+        inherit_list(E), functionlist(E), variable_exists(E)
diff --git a/doc/efun/walk_mapping b/doc/efun/walk_mapping
new file mode 100644
index 0000000..7396fcf
--- /dev/null
+++ b/doc/efun/walk_mapping
@@ -0,0 +1,88 @@
+walk_mapping(E)
+
+FUNKTION:
+        void walk_mapping(mapping m, string fun [, mixed extra, ...])
+        void walk_mapping(mapping m, string fun, string|object ob
+                                                , mixed extra,...)
+        void walk_mapping(mapping m, closure cl, mixed extra,...)
+
+PARAMETER:
+     m		- durchzugehendes Mapping
+     fun/cl	- zu rufende Methode/Closure
+     ob		- Objekt/Dateiname, an dem Methode gerufen werden soll
+     extra	- weitere Parameter fuer Methode/Closure
+
+BESCHREIBUNG:
+     Ruft die Methode fuer jeden Eintrag im Mapping:
+      ob->func(key, value1, ..., valueN, extra,...)
+     beziehungsweise fuehrt die Closure fuer jeden dieser Eintraege aus:
+      funcall(cl, key, value1, ..., valueN, extra,...)
+
+     Die Schluessel werden als Wert uebergeben und die dazugehoerigen Werte
+     per Referenz, diese koennen somit also in der Funktion/Closure geaendert
+     werden.
+
+
+     Verwendung von Methoden:
+	Wenn bei der Angabe von 'fun' kein Objekt 'ob' in Form eines Strings
+	oder Objekts angegeben wird, wird this_object() angenommen.
+
+     Verwendung von Closures:
+	Es koennen sowohl Lfun-Closures als auch Lambda-Closures verwendet
+	werden. Lfun-Closures koennen, wenn im selben Objekt vorhanden auch
+	'private' oder/und 'static' deklariert sein, muessen aber zu dem
+	Zeitpunkt der Verwendung bekannt sein (Funktionsprototypen benutzen).
+
+BEISPIELE:
+     // Liste mit Spielern durchgehen ...
+     mapping x=([ [/human:liafar]:  20,
+		  [/dwarf:mesirii]: 50,
+		  [/elf:zarniya]:   40,
+		  [/feline:turbo]:  30]);
+
+     // ... und Werte aendern:
+     void add_val(object key, int val, int add) {
+       if(key->InFight())
+         val+=add;
+       else
+         val-=add;
+     }
+
+     // verschiedene Aufrufarten, identisches Resultat:
+     walk_mapping(x, "add_val", 0, 10);
+     walk_mapping(x, "add_val", this_object(), 10
+     walk_mapping(x, "add_val", "/players/jof/addierobjektmitmethode", 10);
+
+     walk_mapping(x, #'add_val, 10);
+
+
+AeQUIVALENZCODE (nicht empfohlen, nur zum Verstaendnis!):
+     // so richtig aequivalent wird es nur mit einer Closure hier
+     int i, width;
+     mapping input;
+     mixed *index, *param;
+
+     width=get_type_info(input)[1];
+     param=allocate(width);
+     index=m_indices(input);
+     i=sizeof(index);
+     while(i--) {
+       j=width;
+       while(j-- > 1)
+         param[j]=input[index[i],j];
+       j[0]=index[i];
+
+       apply(cl, param);
+       // fun(index[i], input[index[i],0], input[index[i],1], ...);
+      }
+
+SIEHE AUCH:
+     Arrays:		filter(E), map(E)
+     Objektarrays:	filter_objects(E), map_objects(E)
+     Mappings:		filter_indices(E), map_indices(E)
+
+     Sonstiges:		m_contains(E)
+			member()
+			m_indices(E), m_values(E)
+
+20.Jan 2005 Gloinson
diff --git a/doc/efun/widthof b/doc/efun/widthof
new file mode 100644
index 0000000..e3ac599
--- /dev/null
+++ b/doc/efun/widthof
@@ -0,0 +1,16 @@
+SYNOPSIS
+        int widthof(mapping map);
+
+BESCHREIBUNG
+        Liefert die Anzahl Values pro Key im Mapping <map>. Wenn <map> 0 ist,
+        ist das Resultat 0.
+
+BEISPIEL
+        mapping map = (["foo" : 1;2]);
+        widthof(map)    ergibt 2.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.6
+
+SIEHE AUCH
+        sizeof(E), mkmapping(E), m_reallocate(E), m_values(E), unmkmapping(E)
diff --git a/doc/efun/wizlist_info b/doc/efun/wizlist_info
new file mode 100644
index 0000000..674ec9a
--- /dev/null
+++ b/doc/efun/wizlist_info
@@ -0,0 +1,38 @@
+SYNOPSIS
+        #include <wizlist.h>
+
+        mixed * wizlist_info()
+
+DESCRIPTION
+        Returns an array with the interesting entries of the wizlist.
+        Needs to be privileged by the master object.
+
+        The result is an array with one entry for every wizard (uid).
+        Every entry is an array itself:
+
+          string w[WL_NAME]        = Name of the wizard.
+          int    w[WL_COMMANDS]    = Weighted number of commands execute by
+                                     objects of this wizard.
+          int    w[WL_COST],
+          int    w[WL_GIGACOST]       = Weighted sum of eval_costs.
+          int    w[WL_TOTAL_COST],
+          int    w[WL_TOTAL_GIGACOST] = Total sum of eval_costs.
+          int    w[WL_HEART_BEATS]   = Weighted count of heart_beats.
+          int    w[WL_CALL_OUT]      = Reserved for call_out() (unused yet).
+          int    w[WL_ARRAY_TOTAL]   = Total size of arrays in elements.
+          int    w[WL_MAPPING_TOTAL] = Total size of mappings in elements.
+          int    w[WL_STRUCT_TOTAL]  = Total size of structs in elements.
+          mixed  w[WL_EXTRA]         = Extra wizlist-info if set.
+
+        The 'weighted' entries decay every hour by 10%.
+
+HISTORY
+        LDMud 3.2.10 split the old WL_EVAL_COST into WL_COST and WL_GIGACOST
+          to accomodate for longer uptimes, and introduced
+          WL_TOTAL_COST/WL_TOTAL_GIGACOST.
+        LDMud 3.3.174 added WL_MAPPING_TOTAL.
+        LDMud 3.3.? added WL_STRUCT_TOTAL.
+
+SEE ALSO
+        privilege_violation(M), set_extra_wizinfo_size(E)
+        get_extra_wizinfo(E), set_extra_wizinfo(E)
diff --git a/doc/efun/write b/doc/efun/write
new file mode 100644
index 0000000..a3041b0
--- /dev/null
+++ b/doc/efun/write
@@ -0,0 +1,37 @@
+SYNOPSIS:
+        void write(mixed msg)
+
+DESCRIPTION:
+        Write out something to the current player. What exactly will
+        be printed in the end depends of the type of msg.
+        Please mind: if there is no current player (this_player()), the
+        function will output directly to the driver debug log. Please check
+        before!
+        
+        If it is a string or a number then just prints it out.
+        
+        If it is an object then the object will be printed in the
+        form: "OBJ("+object_name((object)mix)+")"
+        
+        If it is an array just "<ARRAY>" will be printed.
+        
+        If the write() function is invoked by a command of an living
+        but not interactive object and the given argument is a string
+        then the lfun catch_tell() of the living will be invoked with
+        the message as argument.
+
+EXAMPLES:
+        write("Hello world!\n");
+        
+        Just print out a string.
+        
+        write(this_player());
+        
+        This will print out something like "OBJ(std/player#1234)".
+        
+        write( ({ "blub" }) );
+        
+        Will print out "<ARRAY>".
+        
+SEE ALSO:
+        say(E), tell_object(E), tell_room(E), shout(E), catch_tell(L)
diff --git a/doc/efun/write_bytes b/doc/efun/write_bytes
new file mode 100644
index 0000000..bb75484
--- /dev/null
+++ b/doc/efun/write_bytes
@@ -0,0 +1,11 @@
+SYNOPSIS
+        int write_bytes(string file, int start, string str);
+
+BESCHREIBUNG
+        Schreibt den String <str> ins File <file> und ueberschreibt dabei die
+        alten Bytes ab Position <start>. Wenn <start> eine negative Zahl ist,
+        werden die Zeichen vom Ende von <file> an gezaehlt. write_bytes()
+        liefert 1 bei Erfolg, 0 bei Misserfolg.
+
+SIEHE AUCH
+        save_object(E), write_file(E)
diff --git a/doc/efun/write_file b/doc/efun/write_file
new file mode 100644
index 0000000..e79fb6b
--- /dev/null
+++ b/doc/efun/write_file
@@ -0,0 +1,14 @@
+SYNOPSIS
+        int write_file(string file, string str);
+        int write_file(string file, string str, int flags);
+
+BESCHREIBUNG
+        Haengt den String <str> an die Datei <file> an. Liefert 1 bei Erfolg,
+        0 bei Misserfolg.
+
+        If <flags> = 1, dann wird die Datei vor dem eigentlichen
+        Schreiben geloescht; das 'anhaengen' wird so effektiv ein
+        'ueberschreiben'. Defaultwert fuer <flags> ist 0.
+
+SIEHE AUCH
+        file_size(E), cat(E), write_bytes(E), read_file(E), rm(E)
diff --git a/doc/efun/xml_generate b/doc/efun/xml_generate
new file mode 100644
index 0000000..86506e0
--- /dev/null
+++ b/doc/efun/xml_generate
@@ -0,0 +1,77 @@
+OPTIONAL
+EXPERIMENTAL
+SYNOPSIS
+        #include <xml.h>
+
+        string xml_generate(mixed *xml)
+
+BESCHREIBUNG
+        Wandelt das uebergebene <xml>-Array in einen XML-konformen String um,
+        sofern moeglich. Der <xml>-Parameter muss folgende Struktur haben:
+
+        Er muss Tag-Arrays mit folgenden drei Elementen enthalten:
+
+            string XML_TAG_NAME
+                Der Name des XML-Tags.
+
+            mapping XML_TAG_ATTRIBUTES
+                Alle Attribute dieses XML-Tags als ein Mapping mit dem
+                jeweiligen Attributnamen als Schluessel und den Attributwert
+                als der dazugehoerige String.
+                
+                Falls ein XML-Tag keine Attribute enthaelt, so ist dieses
+                Element 0.
+
+            mixed * XML_TAG_CONTENTS
+                Der Inhalt des XML-Tags als ein Array. Dieses Array kann
+                entweder Strings (reine Zeichendaten) oder Arrays
+                als weitere Tags enthalten, welche wiederum diese
+                drei Elemente besitzen.
+                
+                Falls das XML-Tag nichts enthaelt, so ist dieses Element 0.
+
+        Falls der uebergebe Parameter nicht diese Struktur besitzt, so wird
+        eine Fehlermeldung ausgegeben. Ansonsten ist ein gueltiger XML-String
+        das Resultat.
+
+        Diese Funktion ist nur verfuegbar, wenn der Driver mit Iksemel-
+        Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __XML_DOM__ definiert.
+
+BEISPIEL
+        xml_generate(({ "abc", 0, 0 })) -> "<abc/>"
+        xml_generate(({ "abc", ([ "xyz" : "cde" ]), 0 })) -> "<abc xyz="cde"/>"
+
+        mixed* xml = ({ "buch"
+                      , ([ "sprache" : "deutsch" ])
+                      , ({ ({ "titel"
+                            , 0
+                            , ({ "Dies ist der Titel" })
+                           })
+                         , ({ "kapitel"
+                            , 0
+                            , ({ "Dies ist ein Kapitel" })
+                           })
+                         , ({ "kapitel"
+                            , 0
+                            , ({ "Das soll "
+                               , ({ "b"
+                                  , 0 
+                                  , ({ "fettgedruckt" })
+                                 })
+                               , " sein."
+                              })
+                           })
+                        })
+                     })
+
+        xml_generate(xml)
+            -> "<buch sprache="deutsch"><titel>Dies ist der Titel</titel>"
+               "<kapitel>Dies ist ein Kapitel</kapitel><kapitel>Das soll "
+               "<b>fettgedruckt</b> sein.</kapitel></buch>"
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.3.718.
+
+SIEHE AUCH
+        xml_parse(E)
diff --git a/doc/efun/xml_parse b/doc/efun/xml_parse
new file mode 100644
index 0000000..4649c9a
--- /dev/null
+++ b/doc/efun/xml_parse
@@ -0,0 +1,78 @@
+OPTIONAL
+EXPERIMENTAL
+SYNOPSIS
+        #include <xml.h>
+
+        mixed* xml_parse(string xml)
+
+BESCHREIBUNG
+        Parst den angegebenen String <xml> als XML. Der String darf nur ein
+        einziges Root-Tag enthalten, weitere Root-Tags werden ignoriert.
+
+        Falls der String XML-konform war, so wird ein Array mit drei Elementen
+        zurueckgegeben, dessen Elemente folgendermassen definiert sind:
+
+            string XML_TAG_NAME
+                Der Name des XML-Tags.
+
+            mapping XML_TAG_ATTRIBUTES
+                Alle Attribute dieses XML-Tags als ein Mapping mit dem
+                jeweiligen Attributnamen als Schluessel und den Attributwert
+                als der dazugehoerige String.
+                
+                Falls ein XML-Tag keine Attribute enthaelt, so ist dieses
+                Element 0.
+
+            mixed * XML_TAG_CONTENTS
+                Der Inhalt des XML-Tags als ein Array. Dieses Array kann
+                entweder Strings (reine Zeichendaten) oder Arrays
+                als weitere Tags enthalten, welche wiederum diese
+                drei Elemente besitzen.
+                
+                Falls das XML-Tag nichts enthaelt, so ist dieses Element 0.
+
+        Falls der XML-String nicht wohlgeformt ist oder falls nicht genug Speicher
+        zur Verfuegung steht, wird eine Fehlermeldung ausgegeben.
+
+        Diese Funktion ist nur verfuegbar, wenn der Driver mit Iksemel-
+        Unterstuetzung compiliert wurde. In diesem Fall ist das Makro
+        __XML_DOM__ definiert.
+
+BEISPIEL
+        xml_parse("<abc/>")           -> ({ "abc", 0, 0 })
+        xml_parse("<abc xyz="cde"/>") -> ({ "abc", ([ "xyz" : "cde" ]), 0 })
+
+        xml_parse("<buch sprache="deutsch">" + 
+                  "    <titel>Dies ist der Titel</titel>" + 
+                  "    <kapitel>Dies ist ein Kapitel</kapitel>" + 
+                  "    <kapitel>Das soll <b>fettgedruckt</b> sein.</kapitel>" +
+                  "</buch>")
+
+            -> ({ "buch"
+                , ([ "sprache" : "deutsch" ])
+                , ({ ({ "titel"
+                      , 0
+                      , ({ "Dies ist der Titel" })
+                     })
+                   , ({ "kapitel"
+                      , 0
+                      , ({ "Dies ist ein Kapitel" })
+                     })
+                   , ({ "kapitel"
+                      , 0
+                      , ({ "Dies soll "
+                         , ({ "b"
+                            , 0 
+                            , ({ "fettgedruckt" })
+                           })
+                         , "sein."
+                        })
+                     })
+                  })
+               })
+
+GESCHICHTE
+        Eingefuehrt in LDMud 3.3.718.
+
+SIEHE AUCH
+        xml_generate(E)
diff --git a/doc/efun/xor_bits b/doc/efun/xor_bits
new file mode 100644
index 0000000..6f9eab9
--- /dev/null
+++ b/doc/efun/xor_bits
@@ -0,0 +1,19 @@
+SYNOPSIS
+        string xor_bits(string str1, string str2);
+
+BESCHREIBUNG
+        <str1> und <str2> sind beiden Bitstrings. Das Resultat von xor_bits()
+        ist ein Bitstring mit dem binaeren XOR von <str1> und <str2>, also
+        ein String, in dem ein Bit nur gesetzt ist, wenn es entweder in <str1>
+        oder in <str2> vorkommt, nicht aber in beiden.
+
+BEISPIELE
+        string s1, s2, s3;
+        s1 = set_bit("", 3); s1 = set_bit(s1, 15);  -> s1 ist "( ("
+        s2 = set_bit("", 3); s2 = set_bit(s2, 4);   -> s2 ist "8"
+        s3 = xor_bits(s1, s2);
+        -> s3 ist "0 (", es sind also das 4. und das 15. Bit gesetzt.
+
+SIEHE AUCH
+        clear_bit(E), set_bit(E), test_bit(E), next_bit(E), last_bit(E)
+        count_bits(E), and_bits(E), or_bits(E), invert_bits(E), copy_bits(E)
diff --git a/doc/events/EVT_GUILD_ADVANCE b/doc/events/EVT_GUILD_ADVANCE
new file mode 100644
index 0000000..f421f43
--- /dev/null
+++ b/doc/events/EVT_GUILD_ADVANCE
@@ -0,0 +1,36 @@
+EVENT: 
+   EVT_GUILD_ADVANCE
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /std/gilden_ob.c
+
+BESCHREIBUNG:
+   Dieser Event wird immer ausgeloest, wenn sich der Gildenlevel eines
+   Spielers erhoeht oder auch, wenn nur der Gildenlevel eines Spielers von
+   einer Gilde geaendert wird (weil sie keine Gildenlevel kennt).
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_OBJECT:         (object) Spielerobjekt
+      E_PLNAME:         (string) UID/Spielername
+      E_ENVIRONMENT:    (object) Environment des Spielers
+      E_GUILDNAME:      (string) Gildenname
+      P_GUILD_LEVEL     (string) s. Property, neuer Gildenlevel
+      P_GUILD_TITLE:    (string) s. Property, neuer Gildentitel
+      P_SUBGUILD_TITLE: (string) s. Property, neuer Subguild-Title
+    ])
+
+BEMERKUNGEN:
+   Wenn Gilden keine Gildenlevel kenne, sondern andere Formen des Gildenstatus
+   vergeben und der Gildentitel allein nicht ausreicht,  muss hier ueber eine
+   Ergaenzung der Daten nachgedacht werden.
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+
+-----------------------------------------------------------------------------
+16.08.2007, Zesstra
+
diff --git a/doc/events/EVT_GUILD_CHANGE b/doc/events/EVT_GUILD_CHANGE
new file mode 100644
index 0000000..4acb906
--- /dev/null
+++ b/doc/events/EVT_GUILD_CHANGE
@@ -0,0 +1,31 @@
+EVENT: 
+   EVT_GUILD_CHANGE
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /secure/gildenmaster.c
+
+BESCHREIBUNG:
+   Dieser Event wird immer ausgeloest, wenn ein Spieler eine Gilde wechselt.
+   Da Spieler bei einem Gildenaustritt in ihrer Standardgilde landen, ist
+   jeder Aus- und Eintritt letztendlich ein Wechsel.
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_OBJECT:         (object) Spielerobjekt, was die Gilde wechselte,
+      E_PLNAME:         (string) UID/Playername des Spielers,
+      E_ENVIRONMENT:    (object) Environment des Spielers,
+      E_GUILDNAME:      (string) neue Gilde,
+      E_LAST_GUILDNAME: (string) alte Gilde,
+    ])
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+
+-----------------------------------------------------------------------------
+16.08.2007, Zesstra
+
diff --git a/doc/events/EVT_LIB_ADVANCE b/doc/events/EVT_LIB_ADVANCE
new file mode 100644
index 0000000..9dd8fd1
--- /dev/null
+++ b/doc/events/EVT_LIB_ADVANCE
@@ -0,0 +1,30 @@
+EVENT: 
+   EVT_LIB_ADVANCE
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /std/gilde.c
+
+BESCHREIBUNG:
+   Dieses Event wird ausgeloest, wenn sich der Spielerlevel eines Spielers
+   aendert. (Momentan erhoeht er sich nur.)
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_OBJECT:         (object) Spielerobjekt
+      E_PLNAME:         (string) UID/Spielername
+      E_ENVIRONMENT:    (object) Environment des Spielers
+      E_GUILDNAME:      (string) Name der Gilde des Spielers
+      P_LEVEL:          (int) s. Prop, neuer Spielerlevel
+    ])
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+
+-----------------------------------------------------------------------------
+16.08.2007, Zesstra
+
diff --git a/doc/events/EVT_LIB_CLOCK b/doc/events/EVT_LIB_CLOCK
new file mode 100644
index 0000000..22ff451
--- /dev/null
+++ b/doc/events/EVT_LIB_CLOCK
@@ -0,0 +1,32 @@
+EVENT: 
+   EVT_LIB_CLOCK
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /obj/uhr.c
+
+BESCHREIBUNG:
+   Alle Viertelstunde wird dieser Event gerufen. Wenn man Ereignisse jeweils
+   zur vollen Stunde, xx:15, xx:30 und xx:45 durchfuehren will, bietet es sich
+   an, diesem Event zu lauschen.
+
+EVENT-DATEN:
+   Die Daten sind ein Mapping mit den Arrayindizes von localtime() als Keys
+   und den dazu gehoerigen Daten als Values.
+
+BEMERKUNGEN:
+   Will man ermitteln, in welcher Viertelstunde man sich gerade befindet,
+   bietet sich ((minuten/15) % 4) an. Das ergibt immer eine Zahl zwischen 0
+   und 3. 0-> volle Stunde, 1-> viertel nach, 2-> halb, 3-> viertel vor voll.
+   Hierbei wird nach Benachrichtigen der Objekt um ueblicherweise 0-2s
+   verzoegert, wenn es als wirklich exakt sein muss, laesst sich dieser Event
+   dann doch nicht verwenden.
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+
+-----------------------------------------------------------------------------
+28.11.2012, Zesstra
+
diff --git a/doc/events/EVT_LIB_DATECHANGE b/doc/events/EVT_LIB_DATECHANGE
new file mode 100644
index 0000000..3638b49
--- /dev/null
+++ b/doc/events/EVT_LIB_DATECHANGE
@@ -0,0 +1,24 @@
+EVENT: 
+   EVT_LIB_DATECHANGE
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /obj/uhr.c
+
+BESCHREIBUNG:
+   Dieser Event tritt auf, wenn sich das Datum aendert. Also immer um 00:00.
+
+EVENT-DATEN:
+   Die Daten sind ein Mapping mit den Arrayindizes von localtime() als Keys
+   und den dazu gehoerigen Daten als Values.
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+   EVT_LIB_CLOCK
+-----------------------------------------------------------------------------
+28.11.2012, Zesstra
+
diff --git a/doc/events/EVT_LIB_LOGIN b/doc/events/EVT_LIB_LOGIN
new file mode 100644
index 0000000..22cba32
--- /dev/null
+++ b/doc/events/EVT_LIB_LOGIN
@@ -0,0 +1,32 @@
+EVENT: 
+   EVT_LIB_LOGIN
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /std/player/base.c
+
+BESCHREIBUNG:
+   Ein Spieler hat sich gerade eingeloggt.
+   Bitte beachten: Objekt im Inventar und im Environment eines Spieler sollten
+   weiterhin BecomesNetAlive() benutzen! Dieser Event ist nur fuer Objekte
+   gedacht, die das Einloggen mitkriegen wollen, aber nicht in der Naehe des
+   Spieler sind.
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_OBJECT:      (object) Objekt des eingeloggten Spielers,
+      E_PLNAME:      (string) UID/Name des eingeloggten Spielers,
+      E_ENVIRONMENT: (object) Environment nach dem Einloggen,
+    ])
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+   EVT_LIB_LOGOUT
+
+-----------------------------------------------------------------------------
+16.08.2007, Zesstra
+
diff --git a/doc/events/EVT_LIB_LOGOUT b/doc/events/EVT_LIB_LOGOUT
new file mode 100644
index 0000000..b3b3afa
--- /dev/null
+++ b/doc/events/EVT_LIB_LOGOUT
@@ -0,0 +1,32 @@
+EVENT: 
+   EVT_LIB_LOGOUT
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /std/player/base.c
+
+BESCHREIBUNG:
+   Ein Spieler hat sich gerade ausgeloggt.
+   Bitte beachten: Objekt im Inventar und im Environment eines Spieler sollten
+   weiterhin BecomesNetDead() benutzen! Dieser Event ist nur fuer Objekte
+   gedacht, die das Ausloggen mitkriegen wollen, aber nicht in der Naehe des
+   Spieler sind.
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_OBJECT:      (object) Objekt des ausgeloggten Spielers,
+      E_PLNAME:      (string) UID/Name des ausgeloggten Spielers,
+      E_ENVIRONMENT: (object) letztes Environment vor dem Ausloggen,
+    ])
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+   EVT_LIB_LOGIN
+
+-----------------------------------------------------------------------------
+16.08.2007, Zesstra
+
diff --git a/doc/events/EVT_LIB_MINIQUEST_SOLVED b/doc/events/EVT_LIB_MINIQUEST_SOLVED
new file mode 100644
index 0000000..932f02d
--- /dev/null
+++ b/doc/events/EVT_LIB_MINIQUEST_SOLVED
@@ -0,0 +1,29 @@
+EVENT: 
+   EVT_LIB_MINIQUEST_SOLVED
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /secure/questmaster.c
+
+BESCHREIBUNG:
+   Dieser Event wird vom Questmaster ausgeloest, wenn ein Spieler eine
+   Miniquest geloest hat.
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_OBJECT:        (object) Questgeber-Objekt,
+      E_OBNAME:        (string) Objektname des Questgebers
+      E_PLNAME:        (string) Spielername,
+      E_MINIQUESTNAME: (string) Titel der Miniquest
+    ])
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+
+-----------------------------------------------------------------------------
+2014-Feb-03, Arathorn
+
diff --git a/doc/events/EVT_LIB_NEW_ERROR b/doc/events/EVT_LIB_NEW_ERROR
new file mode 100644
index 0000000..9c04648
--- /dev/null
+++ b/doc/events/EVT_LIB_NEW_ERROR
@@ -0,0 +1,31 @@
+EVENT: 
+   EVT_LIB_NEW_ERROR
+
+DEFINIERT IN:
+   /sys/events.h
+   /secure/errord.h (Konstanten fuer Eventdaten sind dort definiert)
+
+GETRIGGERT VON:
+   /secure/errord.c
+
+BESCHREIBUNG:
+   Ein neuer Fehlereintrag wurde im ErrorD eingetragen. Hierbei muss es kein
+   echter Fehler sein, sondern kann auch eine Warnung oder eine Idee eines
+   Spielers sein (alle Fehlertypen T_* aus /secure/errord.h sind moeglich).
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ F_TYPE:     (int) Typ des Fehlereintrags
+      F_HASHKEY:  (string) ID des Fehlereintrages
+      F_UID:      (string) UID des Objektes, in welchen der Fehler auftrat
+    ])
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd,
+   fehlerteufel
+
+-----------------------------------------------------------------------------
+07.09.2011, Zesstra
+
diff --git a/doc/events/EVT_LIB_NPC_DEATH b/doc/events/EVT_LIB_NPC_DEATH
new file mode 100644
index 0000000..6b1fb00
--- /dev/null
+++ b/doc/events/EVT_LIB_NPC_DEATH
@@ -0,0 +1,69 @@
+EVENT: 
+   EVT_LIB_NPC_DEATH(x)
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /std/living/life.c
+
+BESCHREIBUNG:
+   Dieser Event wird immer dann von einem NPC ausgeloest, wenn dieser gerade
+   getoetet wurde (um genau zu sein: im die()).
+   Dieser Event macht vermutlich die meisten Die-Hooks einfach unnoetig, da es
+   bei vielen nur darum geht, zu erfahren, ob der NPC tot ist, wer ihn
+   getoetet hat und ob derjenige genug Schaden gemacht hat. Dies laesst sich
+   aus den Daten dieses Event ebenfalls ermitteln, ohne den Aufwand eines
+   Hooks zu betreiben.
+
+   Bitte beachtet, dass in diesem Fall das ebenfalls gelieferte triggernde
+   Objekt (der NPC) bereits zerstoert ist, wenn ihr den Event empfangt,
+   'trigob' also 0 ist! Ihr muesst also den Eintrag E_OBNAME im Datenmapping 
+   des Events nutzen, um herauszufinden, welcher NPC getoetet wurde.
+
+PARAMETER:
+   Der an das Event-Define zu uebergebende Parameter "x" muss ein String 
+   sein.
+   Uebergibt man den Leerstring "", registriert man sich fuer das globale
+   Event EVT_LIB_NPC_DEATH(""), ueber das der Tod saemtlicher NPCs 
+   weitergemeldet wird.
+   Uebergibt man den load_name() des Zielobjekts als Parameter, so werden
+   nur die Tode von NPCs gemeldet, die sich von der Blueprint des
+   Zielobjekts ableiten, also auch aller Clones.
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_OBNAME:        (string)   Objektname des getoeteten NPCs,
+      E_ENVIRONMENT:   (object)   Environment des Opfer beim Tod,
+      E_TIME:          (int)      Zeitstempel des Todes,
+      P_NAME:          (string,string*)  P_NAME,
+      P_KILLER:        (object)   der Killer, s. Property,
+      P_ENEMY_DAMAGE:  (mapping)  s. Manpage P_ENEMY_DAMAGE,
+      P_LAST_DAMAGE:   (int)      s. Property,
+      P_LAST_DAMTYPES: (string *) s. Property,
+      E_EXTERNAL_DEATH:(int)      Flag, ob die() von aussen gerufen,
+      E_POISON_DEATH:  (int)      Flag, ob der Tod durch Gift ausgeloest,
+      E_CORPSE:        (object)   Leiche, sofern es eine gibt
+      P_XP:            (int)      P_XP,
+      P_ATTRIBUTES:    (int*)     P_ATTRIBUTES,
+      P_MAX_HP:        (int)      P_MAX_HP,
+      P_HANDS:         (mixed)    P_HANDS,
+      P_ALIGN:         (int)      P_ALIGN)
+      P_RACE:          (string)   P_RACE,
+      P_CLASS:         (string*)  P_CLASS,
+    ])
+
+BEMERKUNGEN:
+   Bei der Registrierung fuer die Todes-Events von einzelnen NPCs kann es 
+   im Fall von VC-generierten NPCs zu unerwarteten Effekten kommen, da hier
+   load_name() fuer jedes Objekt den Namen des VC-Standardobjekts 
+   zurueckliefert.
+   Die Registrierung fuer das Todes-Event eines einzelnen Clones ist nicht
+   moeglich.
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+
+-----------------------------------------------------------------------------
+21.08.2007, Zesstra
+
diff --git a/doc/events/EVT_LIB_PLAYER_ATTR_CHANGE b/doc/events/EVT_LIB_PLAYER_ATTR_CHANGE
new file mode 100644
index 0000000..8ad2fd4
--- /dev/null
+++ b/doc/events/EVT_LIB_PLAYER_ATTR_CHANGE
@@ -0,0 +1,24 @@
+EVENT: 
+   EVT_LIB_PLAYER_ATTR_CHANGE
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /std/living/life.c
+
+BESCHREIBUNG:
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ 
+    ])
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+
+-----------------------------------------------------------------------------
+16.08.2007, Zesstra
+
diff --git a/doc/events/EVT_LIB_PLAYER_CREATION b/doc/events/EVT_LIB_PLAYER_CREATION
new file mode 100644
index 0000000..abff825
--- /dev/null
+++ b/doc/events/EVT_LIB_PLAYER_CREATION
@@ -0,0 +1,31 @@
+EVENT: 
+   EVT_LIB_PLAYER_CREATION
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /std/player/base.c
+
+BESCHREIBUNG:
+   Dieser Event wird vom Spielerobjekt ausgeloest, wenn sich ein Spieler
+   erstmalig ins MorgenGrauen eingeloggt hat.
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_PLNAME:       (string) UID/Name des erstellten Spielers,
+      E_OBJECT:       (object) Objekt des Spielers,
+    ])
+
+BEMERKUNGEN:
+   Da zu diesem Zeitpunkt das Spielerobjekt noch nicht vollstaendig
+   konfiguriert ist, werden nur Objekt und Spielername in den Daten 
+   uebermittelt.
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+   EVT_LIB_PLAYER_DELETION
+
+-----------------------------------------------------------------------------
+16.08.2007, Zesstra
+
diff --git a/doc/events/EVT_LIB_PLAYER_DEATH b/doc/events/EVT_LIB_PLAYER_DEATH
new file mode 100644
index 0000000..45e5598
--- /dev/null
+++ b/doc/events/EVT_LIB_PLAYER_DEATH
@@ -0,0 +1,36 @@
+EVENT: 
+   EVT_LIB_PLAYER_DEATH
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /std/living/life.c
+
+BESCHREIBUNG:
+   Dieser Event wird immer dann von einem Spielerobjekt ausgeloest, wenn 
+   der Spieler  gerade getoetet wurde (um genau zu sein: im die()).
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_OBJECT:        (object) Objekt des getoeteten Spielers,
+      E_PLNAME:        (string) UID/Playername des getoeteten Spielers, 
+      E_ENVIRONMENT:   (object) Environment des Opfer beim Tod,
+      E_TIME:          (int) Zeitstempel des Todes,
+      P_LAST_KILLER:   (object) Der Killer, s. Property
+      P_LAST_DAMAGE:   (int) s. Property,
+      P_LAST_DAMTYPES: (string *) s. Property,
+      P_LAST_DEATH_PROPS: (mixed) s. Property
+      E_EXTERNAL_DEATH:(int) Flag, ob die() von aussen gerufen,
+      E_POISON_DEATH:  (int) Flag, ob der Tod durch Gift ausgeloest,
+      E_CORPSE:        (object) Leiche, sofern es eine gibt
+    ])
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+
+-----------------------------------------------------------------------------
+21.08.2007, Zesstra
+
diff --git a/doc/events/EVT_LIB_PLAYER_DELETION b/doc/events/EVT_LIB_PLAYER_DELETION
new file mode 100644
index 0000000..7d63856
--- /dev/null
+++ b/doc/events/EVT_LIB_PLAYER_DELETION
@@ -0,0 +1,32 @@
+EVENT: 
+   EVT_LIB_PLAYER_DELETION
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /std/player/base.c
+
+BESCHREIBUNG:
+   Dieser Event wird vom Spielerobjekt ausgeloest, wenn sich ein Spieler
+   mittels "selbstloeschung" loescht. Der Event wird umittelbar vor der
+   Zerstoerung des Spielerobjekts ausgeloest.
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_PLNAME:       (string) UID/Name des geloeschten Spielers,
+      E_ENVIRONMENT:  (object) letztes Environment des Spielers,
+      E_GUILDNAME:    (string) Name der letzten Gilde des Spielers,,
+    ])
+
+BEMERKUNGEN:
+   Wenn Spieler vom Loeschscript erfasst oder vom Sheriff oder einem EM
+   geloescht werden, wird dieser Event natuerlich nicht ausgeloest.
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+   EVT_LIB_PLAYER_CREATION
+
+-----------------------------------------------------------------------------
+16.08.2007, Zesstra
+
diff --git a/doc/events/EVT_LIB_QUEST_SOLVED b/doc/events/EVT_LIB_QUEST_SOLVED
new file mode 100644
index 0000000..6abbfc9
--- /dev/null
+++ b/doc/events/EVT_LIB_QUEST_SOLVED
@@ -0,0 +1,30 @@
+EVENT: 
+   EVT_LIB_QUEST_SOLVED
+
+DEFINIERT IN:
+   /sys/events.h
+
+GETRIGGERT VON:
+   /std/player/quests.c
+
+BESCHREIBUNG:
+   Dieser Event wird vom Spielerobjekt ausgeloest, wenn der Spieler eine Quest
+   geloest hat.
+
+EVENT-DATEN:
+   Die Daten werden als Mapping uebergeben:
+   ([ E_OBJECT:         (object) Spielerobjekt,
+      E_PLNAME:         (string) Spielername,
+      E_ENVIRONMENT:    (object) Environment des Spielers,
+      E_QUESTNAME:      (string) Name der Quest,
+      E_QP_GRANTED:     (int)    vergebene Abenteuerpunkte,
+    ])
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+   events, RegisterEvent(), UnregisterEvent(), TriggerEvent(), eventd
+
+-----------------------------------------------------------------------------
+2014-Feb-03, Arathorn
+
diff --git a/doc/events/eventd b/doc/events/eventd
new file mode 100644
index 0000000..790e35f
--- /dev/null
+++ b/doc/events/eventd
@@ -0,0 +1,29 @@
+Event-Dispatcher: /p/daemon/eventd.c
+
+BENUTZUNG:
+   #include <events.h>
+   EVENTD->fun(bla);
+
+BESCHREIBUNG:
+   Der Event-Dispatcher merkt sich Event und Daten, die ihr ueber
+   TriggerEvent() an ihn meldet und informiert mit kurzer Zeitverzoegerung
+   alle fuer den jeweiligen Event-Typ angemeldeten Objekte.
+
+FUNKTIONEN:
+   - RegisterEvent(): anmelden fuer einen Event
+   - UnregisterEvent(): von einem Event abmelden
+   - TriggerEvent(): einen Event melden
+   - CheckEventID(): wieviele Lauscher gibt es fuer den Event?
+
+BEMERKUNGEN:
+   Wenn Bedarf fuer einen spezialisierten Event-Dispatcher besteht, der
+   Sonderfunktionen hat oder auch nicht-oeffentliche Events anbietet, besteht
+   die Moeglichkeit, den normalen eventd zu erben. Bitte sprecht in dem Fall
+   aber bitte vorher Zesstra an.
+
+SIEHE AUCH:
+   events, 
+   RegisterEvent(), UnregisterEvent(), TriggerEvent(), CheckEventID()
+
+18.08.2007, Zesstra
+
diff --git a/doc/events/events b/doc/events/events
new file mode 100644
index 0000000..fa0dfa8
--- /dev/null
+++ b/doc/events/events
@@ -0,0 +1,120 @@
+Was sind Events?
+================
+Events im MorgenGrauen sind Ereignisse, die von beliebigen Objekten ausgeloest
+werden koennen. Es gibt eine Reihe von vordefinierten Ereignissen, die die
+Basis-Lib bereitstellt (s.u.). Zusaetzlich zu diesen koennen Magier beliebige
+weitere Events erstellen bzw. benutzen.
+
+Wenn ein Objekt dem zentralen Event-Dispatcher einen bestimmten Event meldet,
+merkt dieser sich dies und die Daten, die ihm vom ausloesenden Objekt
+uebergeben wurde. Mit kurzer Verzoegerung (0-2s) informiert der
+Event-Dispatcher dann alle Objekte, die sich fuer diesen Event interessieren,
+d.h. dafuer angemeldet sind. Hierzu ruft er eine bestimmte Funktion in den
+angemeldeten Objekten auf.
+Hierbei ist zu beachten, dass die informierten Objekte das Ereignis in keinem
+Fall mehr beeinflussen koennen, da es bereits abgeschlossen wurde. Es ist
+lediglich eine Information ueber das Ereignis. Wenn ein Objekt Einfluss nehmen
+will (z.B. auf einen Tod), ist dafuer ein Hook zu verwenden.
+
+
+Welche Events definiert die Mudlib?
+==================================
+Diese Events haben in /sys/events.h vordefinierte Event-IDs und werden von der
+Basis-Mudlib mit den entsprechenden Daten ausgeloest (s. jew. Event):
+- EVT_LIB_LOGIN: Login eines Spielers
+- EVT_LIB_LOGOUT: Logout eines Spielers
+- EVT_LIB_PLAYER_DEATH: Ein Spieler ist gestorben
+- EVT_LIB_PLAYER_DELETION: Ein Spieler hat sich geloescht
+- EVT_LIB_PLAYER_CREATION: Ein Spieler wurde neu angelegt.
+- EVT_LIB_NPC_DEATH(): Ein NPC ist gestorben (Parameter s. Event-Manpage)
+- EVT_LIB_ADVANCE: Ein Spieler hat seine Spielerstufe erhoeht
+- EVT_LIB_PLAYER_ATTR_CHANGE: Die Attribute eines Spielers wurden geaendert
+- EVT_LIB_CLOCK: Die volle Stunde ist erreicht
+- EVT_LIB_DATECHANGE: Ein neuer Tag ist angebrochen. ;-)
+- EVT_LIB_QUEST_SOLVED: Ein Spieler hat eine Quest geloest
+- EVT_LIB_MINIQUEST_SOLVED: Ein Spieler hat eine Miniquest geloest
+
+Gilden muessen definieren (ggf. via /std/gilden_ob.c):
+- EVT_GUILD_CHANGE: Spieler hat seine Gilde gewechselt.
+- EVT_GUILD_ADVANCE: Ein Spieler hat seine Gildenstufe erhoeht
+
+
+Namenskonvention fuer Event-IDs:
+===============================
+- Die IDs aller Standardevents beginnen mit "evt_lib_". 
+  Dementsprechend sind alle Strings, die damit anfangen, fuer ALLE ausser 
+  der Mudlib TABU! D.h.
+  - diese Events werden NIEMALS von Objekten ausserhalb der Basislib
+    ausgeloest!
+  - es duerfen keine Events registriert werden, die mit dieser Zeichenfolge
+    anfangen, ausser mit den symbolischen Namen in /sys/events.h
+- Gilden-Events beginnen mit "evt_guild_", sofern es ein Event ist, den jede
+  Gilde definiert (s. /sys/events.h).
+  Die Events einzelner Gilden sollten mit "evt_gildenname_" beginnen.
+- Die IDs einzelner Magier sollten am besten zwecks Eindeutigkeit mit
+  "magiername_" beginnen.
+
+
+Was ist als lauschendes Objekt zu beachten?
+==========================================
+Das lauschende Objekt muss zunaechst /sys/events.h inkludieren und sich
+mittels Aufrufs von RegisterEvent() als Lauscher registrieren.
+Weiterhin muss es eine Funktion oeffentlich definieren (Name beliebig), die
+der EVENTD aufrufen kann. Diese Funktion bekommt 3 Argumente uebergeben:
+Event-ID, event-ausloesendes Objekt und die Event-Daten:
+   public void listen_event(string eid, object trigob, mixed data);
+'data' ist ein Datentyp, der vom Eventtyp abhaengt. Oft handelt es sich
+um ein Mapping. Fuer eine genaue Beschreibung schaut bitte die in die Manpage
+des konkreten Events.
+
+Bitte beachten: Die Daten in 'data' im Falle eines Arrays/Mappings niemals
+aendern. Aus Effizienzgruenden wird 'data' nicht kopiert, bevor es an die
+einzelnen Objekte uebergeben wird. Wenn ihr also Muell reinschreibt, werden
+nach euch informierte Lauscher euren Schrott empfangen. Sollte hier
+'Missbrauch' auffallen, wird das entsprechend geahndet!
+
+Und zum Schluss: Bitte missbraucht die Events nicht und meldet euch (speziell 
+fuer haeufige Events) nur an, wenn ihr mit den Informationen was sinnvolles 
+macht (also z.B. nicht nur, um ein Log der getoeteten NPCs im Spiel zu 
+erstellen. ;-)).
+
+
+Was ist als ausloesendes Objekt zu beachten?
+===========================================
+Eigentlich gar nicht viel. Ihr muesst lediglich /sys/events.h inkludieren und
+bei Auftreten des Events die Funktion TriggerEvent() im EVENTD aufrufen und
+ihr die Event-ID und die Event-Daten uebergeben.
+Bitte uebergebt als Event-Daten ggf. Kopien von Mappings/Arrays, da lauschende
+Objekte diese Daten veraendern koennen.
+
+
+Beispiele:
+========
+1. Ein Objekt moechte ueber Spielertode informiert werden:
+     EVENTD->RegisterEvent(EVT_LIB_PLAYER_DEATH, "spieler_ist_tot", 
+                           this_object());
+   Ab jetzt wird im Objekt jedes Mal, wenn ein Spieler stirbt, die Funktion
+   "spieler_ist_tot" aufgerufen.
+
+2. Ein Objekt moechte nicht mehr ueber Spielertode informiert werden:
+     EVENTD->UnregisterEvent(EVT_LIB_PLAYER_DEATH, this_object());
+
+3. Ein Objekt will informiert werden, wenn der Event
+   "boing_zwergenkoenig_angriff" ausgeloest wird:
+     EVENTD->RegisterEvent("boing_zwergenkoenig_angriff","alarm",
+                           this_object());
+
+4. Der Zwergenkoenig (oder einer seiner Waechter) wird angegriffen:
+   EVENTD->TriggerEvent("boing_zwergenkoenig_angriff", daten);
+   Anschliessend wird jedes Objekt, das es wissen will (s. 3.) vom EVENTD
+   informiert und kriegt dabei 'daten' uebergeben. Dies kann ein beliebiger
+   Datentyp sein (z.b. ein Mapping). In diesem Fall enthaelt 'daten'
+   zweckmaessigerweise den Angreifer.
+
+
+Siehe auch:
+==========
+  eventd, TriggerEvent(), RegisterEvent(), UnregisterEvent()
+
+18.08.2007, Zesstra
+
diff --git a/doc/help/.readme b/doc/help/.readme
new file mode 100644
index 0000000..0b2a57f
--- /dev/null
+++ b/doc/help/.readme
@@ -0,0 +1,3 @@
+Hier befinden sich Hilfen zu allgemeinen Themen, die sowohl fuer Spieler
+als auch fuer Magier von Interesse sind.
+
diff --git a/doc/help/.synonym b/doc/help/.synonym
new file mode 100644
index 0000000..893c568
--- /dev/null
+++ b/doc/help/.synonym
@@ -0,0 +1,138 @@
+4gewinnt spiele
+abalone spiele
+abenteuerpunkte abenteuer
+abkuerzung abk
+abkuerzungen abk
+akuefi abk
+alkohol essen_trinken
+anbetung gott
+anfaenger einfuehrung
+armageddon reboot
+attribut attribute
+ausdauer attribute
+bezugsobjekt bezugspunkt
+boing gott
+buch buecher
+ciceronen cicerones
+clients client
+cpu mudrechner
+dart spiele
+doppelkopf spiele
+eks erstkillstufenpunkte
+erstkills erstkillstufenpunkte
+erstie zweitspieler
+erstspieler zweitspieler
+essen essen_trinken
+facebook sozialmedien
+feier parties
+fete parties
+fluch gift
+flueche gift
+ftp mudrechner
+fussball spiele
+geschick attribute
+geschicklichkeit attribute
+gesundheit lebenspunkte
+getraenke essen_trinken
+getraenk essen_trinken
+gifte gift
+gilde gilden
+gladiopolis spiele
+gmcp GMCP
+goetter gott
+google sozialmedien
+go spiele
+heilen heilung
+herzschlag leben
+hp leben
+intelligenz attribute
+jof gott
+kaempfen kampf
+kneipe heilung
+kneipen heilung
+konzentrationspunkte zauberei
+konzentration zauberei
+kraft attribute
+krankheiten gift
+krankheit gift
+lars tod
+lebenspunkte leben
+loeschen loeschskript
+loeschung loeschskript
+lp leben
+maexchen spiele
+magiepunkte zauberei
+magie zauberei
+metzeln kampf
+minesweeper spiele
+miniquests stufenpunkte
+miniquest stufenpunkte
+mpa zeitung
+nahrung essen_trinken
+netlag lag
+netzlag lag
+netz mudrechner
+orakel zaubertraenke
+party parties
+portale sehertore
+portal sehertore
+qp abenteuer
+quest abenteuer
+questen abenteuer
+questliste abenteuerliste
+questpunkte abenteuer
+quests abenteuer
+rage spiele
+rechner mudrechner
+regel regeln
+region regionen
+regionsmagier regionen
+rodeln spiele
+rumata gott
+saufen essen_trinken
+schach spiele
+scripte regeln
+script regeln
+scripts regeln
+seherportale sehertore
+seherportal sehertore
+sehertor sehertore
+skat spiele
+skripte regeln
+skript regeln
+skripts regeln
+speicher mudrechner
+speise essen_trinken
+speisen essen_trinken
+spielerloeschen loeschskript
+spielerloeschung loeschskript
+spielertestie spielertesties
+staerke attribute
+tablut spiele
+tanke heilung
+tanken heilung
+team teamkampf
+testie spielertesties
+testies spielertesties
+testspieler spielertesties
+tf client
+todesfolgen tod
+todesmalus tod
+tot tod
+trinken essen_trinken
+ts3 teamspeak
+ts teamspeak
+twitter sozialmedien
+umlaute syntax
+verbindung client
+vergiftung gift
+waffenskills waffenfertigkeiten
+waffenskill waffenfertigkeiten
+waffen waffenfertigkeiten
+waffe waffenfertigkeiten
+zaubertrank zaubertraenke
+zauber zauberei
+zook gott
+zt zaubertraenke
+zweities zweitspieler
+zweitie zweitspieler
diff --git a/doc/help/GMCP b/doc/help/GMCP
new file mode 100644
index 0000000..600ad88
--- /dev/null
+++ b/doc/help/GMCP
@@ -0,0 +1,356 @@
+General Mud Communication Protocol im MorgenGrauen
+==================================================
+
+0. Einleitung
+   GMCP wird benutzt, um Daten zwischen MUD und Client "out of band" zu
+   uebertragen. "Out of band" bedeutet hier, dass der Datenaustausch hinter
+   den Kulissen passiert statt in der normalen textuellen Spielausgabe,
+   welcher vom Client mittels mehr oder weniger komplizierter Trigger
+   ausgewertet werden muss.
+
+   Vorteile:
+   * keine komplexen Trigger (Regexps) mehr.
+   * Daten werden in einem definierten Format uebertragen, welches der Client
+     fuer euch interpretiert.
+   * keine Gagtrigger mehr noetig, um empfangenen Text auszublenden
+   * keine stoerenden Ausgaben, falls man diese Gagtrigger nicht hat.
+   * Daten koennen auch uebertragen werden in Situationen, in denen eine
+     normale Textausgabe nicht so gut geht, z.B. in Editoren.
+   * Daten, die die Clientscripte nicht verarbeiten, stoeren nicht den
+     Textfluss.
+   * Der Client kann konfigurieren, welche Informationen er bekommen will.
+   * Im MG ist neben Dingen wie LP und KP die Uebertragung div. anderer Dinge
+     geplant, vor allem andere Informationen  ueber den Char oder den Raum
+     (z.B. eindeutige Raum-IDs (in den meisten Faellen)).
+
+
+1. Unterstuetzte Module
+
+1.1. Core (aus IRE (Iron Realms Entertainment) MUDs)
+  Das Core Modul ist immer aktiv. Es verwaltet die grundsaetzlichen
+  Einstellungen von GMCP. Die MG-Variante ist fast identisch zur IRE-Variante,
+  hat allerdings zusaetzliche das "Debug"-Kommando.
+
+  Vom Client gesendet:
+    - Core.Hello
+        Muss die erste Nachricht sein, welche der Client nach Aktivierung von
+        GMCP sendet.
+        Die Daten sind ein Objekt mit den Schluesseln "client" und "version",
+        welche entsprechend den Clientnamen und seine Versionskennung
+        enthalten.
+      Core.Hello { "client": "Nexus", "version": "3.1.90" }
+    - Core.Supports.Set
+        Informiert das MUD ueber die Pakete/Module, die der Client
+        unterstuetzt, d.h. die der Client empfangen kann. Es ist immer noch
+        Entscheidung des Muds, welche dieser Module ihre Daten senden (z.B.
+        wird MG einem "MG.char" den Vorzug geben vor einem generischen "Char",
+        falls vom Client beide unterstuetzt werden.
+        Wenn bereits vorher ein Core.Supports.* Kommando empfangen wurde, wird
+        die Liste der unterstuetzten Module durch die neue ersetzt.
+        Daten sind ein Array von Strings, welche jeweils den Modulnamen und
+        die Versionsnummer angeben (mit einem Leerzeichen getrennt).
+        Die Versionsnummer muss eine positive Ganzzahl sein.
+        Die meisten Clients werden Set nur einmal am Anfang senden und
+        brauchen kein Add/Remove.
+      Core.Supports.Set [ "MG.char 1", "MG.room 1" ]
+    - Core.Supports.Add
+        aehnlich zu Set, aber es fuegt die angegeben Module an die in der
+        Vergangenheit uebermittelten an.
+        Wenn noch keine Liste gesendet wurde, ist das Ergebnis wie bei Set.
+        Wenn die Liste Module enthaelt, die bereits aktiv sind, wird die neue
+        Versionsnummer Prioritaet vor der alten haben, selbst wenn die kleiner
+        ist.
+        Die Daten sind identisch zu Set.
+    - Core.Supports.Remove
+        Entfernt die angebenen Module aus der Liste der unterstuetzen Module.
+        Die Daten sind identisch zu Set. Die Modulversionen sind NICHT
+        optional (anders als bei IRE). Allerdings werden auch Module anderer
+        Version abgeschaltet. (Insofern koennte man - zur Zeit - einfach immer 1
+        als Versionsnummer senden.)
+    - Core.KeepAlive
+        Vom MG ignoriert, weil Verbindungen nicht automatisch getrennt werden.
+    - Core.Ping
+        Veranlasst das MG mit einem Core.Ping zu antworten.
+        Die Daten ist eine Ganzzahl, welche die durchschnittliche Pingzeit
+        aus verherigen Pings enthaelt, falls verfuegbar.
+        (Anmerkung: IRE macht keine Angabe ueber die Einheit dieser Zahl. MG
+         geht von Millisekunden aus.)
+      Core.Ping 120
+    - Core.Debug
+        Kann gesendet werden, um die Ausgabe von menschenlesbaren
+        Debugmeldungen vom GMCP zu aktivieren.
+        Die Daten sind eine Ganzzahl. Je groesser, desto mehr Debugmeldungen
+        werden ausgegeben.
+        0 schaltet die Debugmeldungen aus.
+        1 schaltet nur die Ausgabe von GMCP-Fehlern an.
+        100 schaltet alles an.
+      Core.Debug 1
+  Vom Server gesendet:
+    - Core.Ping
+        Als Antwort eines vom Client empfangenen Core.Ping gesendet.
+        Keine Daten.
+    - Core.Goodbye [NOT IMPLEMENTED YET!]
+        Gesendet vom MUD unmittelbar vor Verbindungstrennung.
+        Die Daten sind eine Nachricht (string), welche dem User angezeigt
+        werden kann und koennen den Grund fuer den Disconnect erklaeren.
+      Core.Goodbye "Goodbye, adventurer"
+
+1.2. MG.char 1 (angelehnt an Aardwolf, aber modifiziert)
+  Die Uebertragung der Kommandos in diesem Modul wird aktiviert, wenn
+  "MG.char" aktiv ist. Es gibt keine Submodule, die getrennt aktiviert werden
+  muessten. Die jeweiligen Kommandos werden gesendet, wenn sich eins der
+  gesendeten Daten aendert.
+  Alle Kommandos werden einmal bei Modulaktivierung gesendet (auch wenn ein
+  Modul durch ein Core.Supports.Set (erneut) aktiviert wird).
+  Ist dieses Modul vom Client unterstuetzt, werden keine Daten aus den
+  Modulen "Char" und "char" gesendet.
+  (Spieler aelter als 12.05.2013 muessen einmalig den textuellen Report mit
+   "report ein" und "report vorsicht" einschalten, damit die Uebertragung
+   via GMCP freigeschaltet wird. Der Report kann danach wieder
+   ausgeschaltet werden.)
+
+1.2.2. MG.char.base
+  Vom Server gesendet:
+    - MG.char.base
+      Einige grundlegende Eigenschaften des Chars, die sich selten aendern.
+      Ein "wizlevel" von 1 zeigt an, dass der Char ein Seher ist.
+      Der "title" kann am Anfang ein Backspace "\b" gefolgt von "," oder "'"
+      enthalten.
+      MG.char.base { "name": "Jof", "guild": "Abenteurer",
+                     "race": "Elf", "presay": "Mudgott",
+                     "title": "idlet.", "wizlevel": 101 }
+
+1.2.3. MG.char.vitals
+  Vom Server gesendet:
+    - MG.char.vitals
+      Aktuelle Lebenspunkte, Konzentrationspunkte und Vergiftungzustand.
+      Im Falle von Gift: je groesser, desto staerker die Vergiftung.
+      MG.char.vitals { "hp": 210, "sp": 90, "poison": 3 }
+    - MG.char.maxvitals
+      Aktuelle Maximalwerte fuer LP, KP und Vergiftungszustand. Werden in
+      einer separaten Gruppe uebertragen, weil sie sich viel seltener aendern.
+      MG.char.maxvitals { "max_hp": 210, "max_sp": 226, "max_poison": 10 }
+
+1.2.4. MG.char.attributes
+  Vom Server gesendet:
+    - MG.char.attributes
+      Aktuelle Attribute des Chars.
+      Die Daten sind ein Objekt Name-Wert-Paaren von Attributen.
+      MG.char.attributes { "int": 12, "con": 10, "str": 8, "dex": 12 }
+
+1.2.5. MG.char.infoVars
+  Vom Server gesendet:
+    - MG.char.infoVars
+      Diese Nachricht gibt den "technischen" Namen aus MG.char.info einen
+      Namen, der dem Spieler stattdessen angezeigt werden sollte.
+      Diese Nachricht wird nur bei Modulaktivierung gesendet.
+      Die Daten sind ein Objekt von Name-Beschreibung-Paaren.
+      MG.char.infoVars { "level": "Stufe", "guildlevel": "Gildenstufe" }
+
+1.2.6. MG.char.info
+  Vom Server gesendet:
+    - MG.char.info
+      Uebertraegt (ausgewaehlte) Daten aus dem Kommando <info>.
+      Die Daten sind ein Objekt von Name-Wert-Paaren.
+      (Moegliche weitere Angaben in der Zukunft: Questpunkte, Stufenpunkte
+       (Genauigkeit 20% einer Stufe), ...)
+      MG.char.info { "level": 210, "guildlevel": 9 }
+
+1.2.7. MG.char.wimpy
+  Vom Server gesendet:
+    - MG.char.wimpy
+      Aktuelle Vorsicht und ggf. Fluchtrichtung des Char.
+      Die Daten sind ein Objekt Name-Wert-Paare von Vorsicht+Fluchtrichtung.
+      Ist keine Fluchtrichtung gesetzt, wird 0 uebertragen.
+      Fuer die Uebermittlung der Vorsicht muss der Char die Quest "Der Schrat
+      kann nicht einschlafen" geloest haben und fuer die Fluchtrichtung
+      Seher sein.
+      MG.char.wimpy { "wimpy": 50, "wimpy_dir": "norden" }
+
+
+1.3. char 1 (Aardwolf)
+  Dieses Modul ist so aehnlich zum "char" Modul von Aardwolf wie es geht, d.h.
+  es benutzt die gleichen Schluesselnamen. Es ist aber reduziert im Umfang der
+  Daten, d.h. es uebertraegt weniger Daten als Aardwolf es tut.
+  Wann immer moeglich, wird empfohlen, das Modul MG.char stattdessen zu
+  verwenden.
+  Wenn MG.char aktiv ist, werden von diesem Modul keine Daten uebertragen.
+  ES WIRD DAVON ABGERATEN, DIESES MODUL ZU VERWENDEN.
+  Wann immer moeglich, solltet ihr es in euren Clients abschalten!
+
+1.3.1. char.base
+  Vom Server gesendet:
+    - char.base
+      Einige grundlegende Eigenschaften des Chars, die sich selten aendern.
+      char.base { "name": "Jof", "race": "Elf",}
+
+1.3.2. char.vitals
+  Vom Server gesendet:
+    - char.vitals
+      Aktuelle Lebens- und Konzentrationspunkte.
+      char.vitals { "hp": 210, "mana": 90}
+
+1.3.3. char.stats
+  Vom Server gesendet:
+    - char.stats
+      Aktuelle Attribute des Spielers.
+      Die Daten sind ein Objekt Name-Wert-Paaren von Attributen.
+      char.stats { "int": 12, "con": 10, "str": 8, "dex": 12 }
+
+1.3.4. char.status
+  Vom Server gesendet:
+    - char.status
+      Aktueller Level des Chars. (Dies ist die einzige Angabe aus dem Aardwolf
+      char.status, welche das MG machen kann.)
+      Die Daten sind ein Objekt Name-Wert-Paaren von Attributen.
+      char.status { "level": 210 }
+
+
+1.4. Char 1 (IRE)
+  Dieses Modul ist so aehnlich zum "Char" Modul von IRE wie es geht, d.h.
+  es benutzt die gleichen Schluesselnamen. Es ist aber reduziert im Umfang der
+  Daten, d.h. es uebertraegt weniger Daten als IRE es tut.
+  Wann immer moeglich, wird empfohlen, das Modul MG.char stattdessen zu
+  verwenden.
+  Wenn MG.char aktiv ist, werden von diesem Modul keine Daten uebertragen.
+  ES WIRD DAVON ABGERATEN, DIESES MODUL ZU VERWENDEN.
+  Wann immer moeglich, solltet ihr es in euren Clients abschalten!
+
+1.4.1. Char.Vitals
+  Vom Server gesendet:
+    - Char.Vitals
+      Aktuelle und maximale Lebens- und Konzentrationspunkte.
+      Char.Vitals { "hp": 210, "mp": 90, "maxhp": 210, "maxmp": 226 }
+
+1.4.2. Char.Status
+  Vom Server gesendet:
+    - Char.Status
+      Aktueller Level und Gilde des Chars. 
+      Die Daten sind ein Objekt Name-Wert-Paaren von Attributen.
+      Char.Status { "level": 210, "guild": "Zauberer" }
+
+1.4.3. Char.StatusVars
+  Vom Server gesendet:
+    - Char.StatusVars
+      Diese Nachricht gibt den "technischen" Namen aus Char.Status einen
+      Namen, der dem Spieler stattdessen angezeigt werden sollte.
+      Dieses Kommando wird nur bei Modulaktivierung gesendet.
+      Die Daten sind ein Objekt von Name-Beschreibung-Paaren.
+      Char.StatusVars { "level": "Spielerstufe", "guild": "Gilde" }
+
+
+1.5. comm.channel 1 (Aardwolf)
+  Dieses Modul ist genauso wie es Aardwolf definiert. Allerdings werden
+  natuerlich aus dem MG andere Ebenen uebertragen. ;-)
+  Vom Server gesendet:
+    - comm.channel
+      Diese Nachricht wird immer dann gesendet, wenn jemand was auf einer
+      eingeschalteten Ebene sagt. (Nicht bei Abruf der History!)
+      Zur Zeit ist es nicht moeglich, Ebenen per GMCP ein- oder auszuschalten.
+      Wenn dieses GMCP-Modul eingeschaltet ist, wird die Ausgabe der
+      Ebenenmeldung in der normalen Spielausgabe des MG unterdrueckt.
+      Achtung: Zur Zeit erfolgt (noch) keine Beruecksichtung eurer
+      "ignoriere"-Einstellungen bei den via GMCP uebertragenen
+      Ebenenmeldungen.
+      comm.channel { "chan": "allgemein",
+          "msg": "[Allgemein:Maharet] Guten Morgen, Grauen!", 
+          "player": "Maharet" }
+
+
+1.5. MG.team 1 (eigene Variante, inspiriert von Aardwolf)
+     Kommt spaeter. ;-)
+
+
+1.6. MG.room 1 (eigene Variante, inspiriert von Aardwolf)
+  MG.room beinhaltet Informationen ueber den aktuellen Raum des Spielers
+
+  Vom Server gesendet:
+  - MG.room.info
+    Diese Nachrichtig wird immer nach Betreten eines neuen Raumes gesendet
+    und enthaelt folgende Informationen:
+    * id: Eine eindeutige ID des Raumes (technisch: ein MD5-Hash)
+        Diese Angabe fehlt in Labyrinthraeumen (nicht gewollt) und in einigen
+        speziellen Raeumen (technische Gruende, z.B. manche 'virtual
+        compiler'). In diesem Fall ist die ID "" (leerer String).
+    * short: Die Kurzbeschreibung des Raumes
+    * domain: Die Region, in der der Raum liegt
+        Manche Raeume liegen nicht in den Regionen. In dem Fall fehlt diese
+        Angabe.
+    * exits: eine Liste von sichtbaren Ausgaengen (d.h. unsichtbare Ausgaenge
+        werden nicht angezeigt).
+    Hinweis: es ist moeglich, dass der Spieler in einen Raum bewegt wird,
+    der zwar technisch ein anderer Raum ist, aber kein anderer Ort. In
+    diesem Fall unterbleibt diese Nachricht.
+    room.info { "id": "d41d8cd98f00b204e9800998ecf8427e",
+                "short": "Die beruehmte Abenteurergilde",
+                "domain": "Ebene",
+                exits: [ "norden", "oben" ] }
+
+
+2. Vergleich mit GMCP in Aardwolf und IRE Muds
+   Im Morgengrauen wird GMCP letztendlich nur als Spezifikation des Transport
+   Layers betrachtet, da bei den Modulen jedes Mud seine eigene Definition der
+   uebertragenen Daten und ihrer Bedeutung vornimmt.
+
+   Das Modul "Char" ist aus den IRE-Muds, welches nur existiert, weil einige
+   Clients es standardmaessig schonmal einschalten. Es sendet aber nur einen
+   Minimalsatz an Daten.
+   Das Modul "char" ist aus Aardwolf, welches nur existiert, weil einige
+   Clients es standardmaessig schonmal einschalten. Es sendet aber nur einen
+   Minimalsatz an Daten.
+
+   Dem Modul MG.char sollte immer der Vorzug vor "Char" oder "char" gegeben
+   werden, da dort deutlich mehr Daten uebertragen werden.
+
+   Das Modul "Room" aus IRE wird nicht unterstuetzt, weil seine Daten vom MG
+   groesstenteils nicht in der geforderten Form bereitgestellt werden koennen
+   (zumindest nicht ohne ungerechtfertigten Aufwand).
+
+3. Transport Specification
+   Einstweilen am besten auf http://www.gammon.com.au/gmcp nachlesen.
+
+   Kommt hier vielleicht spaeter noch, einstweilen am besten im Netz
+   nachgucken.
+
+4. Implementationsdetails
+  Im MG ist Gross-/Kleinschreibung des Modulnamens wichtig, im Gegensatz zum
+  IRE. Meines Wissens (bitte korrigiert mich ggf.), machte die
+  Transportspezifikation keine Aussage in diesem Punkt, sondern IRE hat dies
+  entschieden.
+  Jetzt ist es so, dass Aardwolf teils Module mit dem gleichen Namen, aber
+  anderer Funktion wie IRE gebaut hat. Alle Doku von Aardwolf schreibt aber
+  immer von kleingeschriebenen Modulnamen. Insofern ist das jetzt ein Versuch,
+  die Module von IRE/Aardwolf doch irgndendwie auseinander zu halten.
+
+
+5. Client-Kram
+5.1. Tips und Hinweiss
+  Manche Clients zeigen die empfangenen GMCP-Daten standardmaessig auf dem
+  Bildschirm an.
+  Normalerweise sortieren sie die Daten  aber in von Scripten nutzbare
+  Tabellen ein, so dass es entfaellt, irgendeinen Text zu parsen.
+  In der Regel ist es dann auch so, dass der Client Moeglichkeiten anbietet,
+  auf den Empfang von GMCP-Daten reagieren zu koennen (z.B. nen Script
+  aufrufen zu lassen).
+
+5.2. Clients mit GMCP-MG-Support
+
+5.3. Clients mit GMCP-Support (Transport Spec)
+  * Mudlet (http://forums.mudlet.org/viewtopic.php?f=7&t=1547)
+    Im Netz sind div. weitere Quellen und Tutorial zu finden.
+  * Tintin++ (http://tintin.sourceforge.net/board/viewtopic.php?p=4651)
+  * TF 5 (http://mikeride.chaosnet.org/abelinc/scripts/telopt.html)
+  * CMUD (http://forums.zuggsoft.com/forums/viewtopic.php?p=158455#158455)
+  * Mushclient (http://www.aardwolf.com/wiki/index.php/Clients/MushclientGMCP)
+
+
+6. Angedachte Module fuer die Zukunft
+Ob und wann etwas aus diesem Bereich kommt, ist voellig offen. Haengt auch vom
+Interesse der Spielerschaft ab.
+
+6.3. Char.Items (IRE)
+  Eigentlich eher weniger geplant...
+
+LETZTE AeNDERUNG:
+09.01.2016, Zesstra
+
diff --git a/doc/help/abenteuer b/doc/help/abenteuer
new file mode 100644
index 0000000..3029db4
--- /dev/null
+++ b/doc/help/abenteuer
@@ -0,0 +1,41 @@
+
+Abenteuer
+=========
+
+    Um im MorgenGrauen zu etwas zu kommen, muss man Abenteuer (in
+    englischsprachigen MUDs als 'Quests' bekannt) loesen. Fuer bestandene
+    Abenteuer bekommt man Abenteuerpunkte. Nur wer genug Abenteuerpunkte
+    bekommen hat, kann Magier oder Seher werden. Je komplizierter ein
+    Abenteuer zu loesen ist, desto mehr Abenteuerpunkte bekommt man beim
+    Bestehen.
+
+    Einige Abenteuer sind nicht nur schwer, sondern auch gefaehrlich. Diese
+    sollten nur von Spielern auf einer hohen Stufe in Angriff genommen werden.
+
+    Eine Liste aller vorhandenen Abenteuer kann man in den Gilden mit dem
+    Befehl 'liste' bekommen. Dort werden auch die Punkte fuer das Abenteuer
+    angezeigt. Die Abenteuer sind in der Liste in etwa nach der
+    Gefaehrlichkeit sortiert. Die Stufe, die dort angegeben ist, bezieht sich
+    nicht allein auf die Spielerstufe, sondern kann auch als Hinweis auf die
+    durchschnittlichen Attribute verstanden werden, die man zum Loesen des
+    Abenteuers aufweisen sollte.
+
+    In der Hochebene ist ein alter Wanderer zu finden, der einem sagen kann,
+    worin die Aufgabe bei den Abenteuern besteht. Er sagt (wird einmal sagen),
+    ob das Abenteuer nicht zu gefaehrlich fuer den Spieler ist.
+
+ TECHNISCHER HINWEIS:
+    Wenn man ein Abenteuer durchspielt, sollte man nicht einschlafen (seine
+    Verbindung zum MorgenGrauen kappen), da nicht garantiert werden kann, dass
+    das Abenteuer auch nach dem Wiedererwachen noch weitergespielt werden
+    kann.
+
+    Manchmal ist ein Gegenstand/Quest-NPC gerade nicht da, zB weil ein
+    Mitspieler die Quest geloest oder den NPC getoetet hat. Dann musst du
+    bis zum naechsten Reset warten.
+
+ SIEHE AUCH:
+    abenteuerliste, stufen, stufenpunkte, schlafe, kampf, reset
+
+1.11.2012, Zesstra
+
diff --git a/doc/help/abenteuerliste b/doc/help/abenteuerliste
new file mode 100644
index 0000000..7ac2ffe
--- /dev/null
+++ b/doc/help/abenteuerliste
@@ -0,0 +1,34 @@
+Abenteuerliste
+==============
+
+ Die Liste der spielbaren Abenteuer haengt in jeder Gilde im MorgenGrauen aus.
+ Sie ist mit dem Kommando "liste" abrufbar.
+
+ Die in der Liste verwendeten Abkuerzungen werden im Folgenden erlaeutert:
+ 
+ AP       = Fuer die Quest vergebene Abenteuerpunkte
+ Klasse   = Subjektive Einstufung innerhalb der Gruppe bzgl. Arbeitsaufwand,
+            Dauer oder Gefahren
+ Attribut = Keines    -> Keine besonderen Charakteristika
+            Fleissig  -> Vornehmlich Sammeln, Auffinden von Items oder von
+                         Informationen
+            Heroisch  -> Heldentat, die Mut, Kampfbereitschaft und eine
+                         gewisse Bereitschaft zum Forschen braucht
+            Episch    -> Grosse Anforderungen an Ausdauer und Mut, grosser
+                         Aufwand fuer das Erforschen der Loesung, Kaempfe
+                         mittlerer Haerte
+            Legendaer -> Barden preisen die Einsatzbereitschaft der Helden,
+                         recht grosse Gefahr fuer Leib und Leben, mitunter
+                         lange Questzeit
+ Stufe    = Vor dem / steht die empfohlene Stufe fuer die Quest. Diese ist 
+            keine Voraussetzung, ausser bei Seherquests, die teilweise den
+            Status voraussetzen.
+            Nach dem / stehen die Durchschnittsstufen der loesenden Spieler.
+ Autor    = Magier, der die Quest geschrieben hat
+
+
+ SIEHE AUCH:
+    abenteuer, stufen, stufenpunkte, schlafe, kampf, reset
+
+26.05.2016, Zesstra
+
diff --git a/doc/help/abenteurer b/doc/help/abenteurer
new file mode 100644
index 0000000..e223861
--- /dev/null
+++ b/doc/help/abenteurer
@@ -0,0 +1,35 @@
+
+Die Abenteurergilde
+-------------------
+
+    Die meisten neuen Spieler sind automatisch Mitglied der Abenteurergilde.
+    Die Gilde beherbergt alle Spieler, die keiner anderen Gilde angehoeren,
+    sei es durch Geburt oder freiwilligen Wechsel.
+
+    Das Gildenhaus befindet sich in der Stadt Port Vain. Dort ist eigentlich
+    immer etwas los.
+
+    Die Abenteurergilde stellt ein paar Zaubersprueche zur Verfuegung, ist
+    aber nicht unbedingt die staerkste Gilde. Sie ist eher dafuer gedacht,
+    dass man sich als Abenteurer in die Welt des MorgenGrauen einspielen kann
+    und sich einen Ueberblick verschafft, bevor man sich fuer eine Karriere in
+    einer der anderen Gilden entschliesst.
+
+    Es gibt jedoch auch verwegene Spieler, die es als Abenteurer zu grossem
+    Ruhm gebracht haben.
+
+ SIEHE AUCH:
+    gilden,
+    steinschlag (5P/S1), identifiziere (10P/S3), licht (5P/S6),
+    schaetz (3P/S8), pfeil (10P/S10), ausweichen (10P/S13), 
+    kampfschrei (30P/S16), feuerball (20P/S18), schnell (100P/S21), 
+    saeurestrahl (25P/S28).
+
+    Fuer die Werte in Klammern gilt: P=Magiekosten; S=Stufe zum Erlernen.
+
+    *Ist man Mitglied einer anderen Gilde, so muss man fuer die Hilfen zu den
+    Spruechen `hilfe gilde abenteurer <name>' benutzen!*
+
+ LETZTE AeNDERUNG:
+    03.11.2012, Zesstra
+
diff --git a/doc/help/abentoira b/doc/help/abentoira
new file mode 100644
index 0000000..dbbac86
--- /dev/null
+++ b/doc/help/abentoira
@@ -0,0 +1,35 @@
+Die Abentoiragilde
+-------------------
+
+    Als kleiner, frisch erschaffener Goblin ist man automatisch Mitglied der
+    Abentoiragilde. Diese ist die Goblin-Zweigstelle der bekannten
+    Abenteurergilde.
+
+    Das Haupt-Gildenhaus befindet sich in der Stadt Port Vain. Nur dort sind
+    Gildenein- und -austritt moeglich, auch kann man nur dort neue
+    Zaubersprueche lernen. Seine Stufe kann man in jedem Gildenhaus erhoehen,
+    jedoch werden einem Goblin nur in der Zweigstelle im Goblindorf beim
+    Aufstieg seine besonderen Abentoira-Titel verliehen!
+
+    Die Abenteurergilde stellt ein paar Zaubersprueche zur Verfuegung, ist
+    aber nicht unbedingt die staerkste Gilde. Sie ist eher dafuer gedacht,
+    dass man sich als Abenteurer in die Welt des MorgenGrauen einspielen kann
+    und sich einen Ueberblick verschafft, bevor man sich fuer eine Karriere
+    in einer der anderen Gilden entschliesst.
+
+    Es gibt jedoch auch verwegene Spieler, die es als Abenteurer zu grossem
+    Ruhm gebracht haben.
+
+ SIEHE AUCH:
+    gilden,
+    licht (5P/S4), identifiziere (10P/S8), schaetz (3P/S10), pfeil (10P/S11),
+    ausweichen (10P/S12), kampfschrei (30P/S13), feuerball (20P/S15),
+    schnell (100P/S17), saeurestrahl (25P/S30).
+
+    Fuer die Werte in Klammern gilt: P=Magiekosten; S=Stufe zum Erlernen.
+
+    *Ist man Mitglied einer anderen Gilde, so muss man fuer die Hilfen zu den
+    Spruechen `hilfe gilde abenteurer <name>' benutzen!*
+
+ LETZTE AeNDERUNG:
+    1.Jan 2008 Kessa
diff --git a/doc/help/abk b/doc/help/abk
new file mode 100644
index 0000000..ea9c938
--- /dev/null
+++ b/doc/help/abk
@@ -0,0 +1,72 @@
+Haeufig benutzte Abkuerzungen
+-----------------------------
+
+     11       Elf
+     112      Feuerwehr
+     AdT      Auge des Teufels (Quest)
+     AFR      Anti-Feuer-Ring
+     AP       Abenteuerpunkte
+     AFAIK    As far as i know (soweit ich weiss)
+     AKUEFI   Abkuerzungsfimmel
+     Aur      Aurora
+     AU       Ausdauer
+     BSS      Blutsauger-Schwert
+     C4       Zephyr
+     CK       Chaoskneipe
+     D11      Dunkelelf
+     DD       Daemonendimension
+     DE(LF)   Dunkelelf
+     DG       Drachengott
+     EE       Elementarebenen
+     EK       Erstkill
+     EKSP     Erstkill-Stufenpunkte
+     EP       Erfahrungspunkt(e)
+     Famu     Feuer-Amulett
+     FE       Feuerebene
+     Flapa    Flammenpanzer
+     F&E      Forschungs- und Entwicklungsabteilung
+     FI       Feuerinsel
+     FidKG    Frauen in die Klerikergilde (c) Lug
+     FiRZG    Fehler im Raum-Zeit-Gefuege
+     FK       Friedhofskneipe
+     FL       Fluchtrichtung
+     FLW      Famous last words
+     FP       Forscherpunkte
+     FR       Fluchtrichtung
+     GE       Geschicklichkeit
+     GL       Gildenlevel
+     GQ       Gildenquest
+     HE       Hochebene
+     IN       Intelligenz
+     IMHO     In my humble opinion (meiner bescheidenen Meinung nach)
+     IIRC     If I remember correctly (wenn ich mich recht erinnere)
+     KL       Komplettloesung
+     KP       Konzentrationspunkte
+     KR       Kraft
+     LP       Lebenspunkte
+     LOL      Laughing out Loud (laut lachen)
+     MG       MorgenGrauen :-)
+     MQ       MiniQuest
+     NNADW    Nichts Neues auf der Welt
+     nFE      Neue Feuerebene
+     PA*      Boese Magier: Paracelsus, Padreic, [Patryn]
+     PK       Playerkill
+     PV       Port Vain
+     PARA     Parallelwelt
+     PW       Parallelwelt
+     RL       Real Life
+     ROTFL    Rolling on the Floor, laughing (sich kringeln vor Lachen)
+     SL       Spielerlevel
+     SMS      Schwertmeisterschwert (manchmal auch SpeerMeisterSpeer)
+     SP       Stufenpunkte
+     SPMSP    Speermeisterspeer
+     SSP      Schreckensspitze
+     Stupse   Stufenpunkte
+     tab      team angriffsbefehl
+     TI       Toeter's Insel (Akhar Nth'tar)
+     UvSdER   Ungeheuer vom See des ewigen Regens
+     VL       Virtual Life oder Verlorenes Land
+     VS       Vollstrecker oder Vorsicht
+     WUW      Wasserunwesen
+     WWI      Werwolfinsel
+     WWW      Wald der Wonnewichte
\ No newline at end of file
diff --git a/doc/help/altgilden b/doc/help/altgilden
new file mode 100644
index 0000000..677ac92
--- /dev/null
+++ b/doc/help/altgilden
@@ -0,0 +1,29 @@
+
+Ehemals geplante Gilden
+=======================
+
+   Folgende Gilden waren einmal angedacht, mit einer Fertigstellung durch die
+   damaligen Magier ist aber nicht mehr zu rechnen.
+
+   Diese Liste mag als Inspiration dienen, wenn jemand eine Gilde bauen 
+   moechte, aber nicht weiss, welche. Ob es noch Teile davon gibt, ist 
+   von Gilde zu Gilde voellig verschieden.
+
+     * Hexen (Twingi)
+     * Eiskrieger (Tilly)
+     * Piraten (Astar)
+     * Druiden (Bernard)
+     * Assassinen (Catweazle)
+     * Schwerttaenzer (Dancer)
+     * Ninja (Morgoth)
+     * Elementare (Morgoth)
+     * Harlekine (Puma)
+     * Todesanbeter (Vrai)
+     * Thaumaturgen (Yoru, Paracelsus)
+     * Idioten (Muadib, Boing)
+     * Paladine (Nonne)
+
+ SIEHE AUCH:
+
+ LETZTE AeNDERUNG: 
+    Fre, 1.11.2013, von Humni 
diff --git a/doc/help/attribute b/doc/help/attribute
new file mode 100644
index 0000000..7c83ec1
--- /dev/null
+++ b/doc/help/attribute
@@ -0,0 +1,35 @@
+
+Die Attribute
+=============
+
+    In MorgenGrauen gibt es fuer Spieler 4 Attribute: *Intelligenz*, *Kraft*,
+    *Geschicklichkeit* und *Ausdauer*. Je hoeher der Wert dieser Attribute
+    ist, desto besser ist es natuerlich fuer den Spieler. Die Attribute wirken
+    sich z.B. auf folgende Bereiche aus:
+
+     Intelligenz
+        Konzentration, Lernfaehigkeiten
+     Kraft
+        Kampfkraft, Tragen von Gegenstaenden
+     Geschicklichkeit
+        Benutzen von Waffen, Schutz vor Schaden im Kampf
+     Ausdauer
+        Gesundheit (Lebenspunkte)
+
+    Erhoeht werden die Attribute durch Zaubertraenke, die ueberall im Spiel
+    versteckt sind. Die Zaubertraenke sind teilweise recht leicht zu finden,
+    teilweise aber auch sehr schwer. Das Orakel von Tingan gibt allerdings zu
+    jedem Zaubertrank einen Hinweis.
+
+    Die einzelnen Rassen haben einen rassenspezifischen Bonus bei den
+    Attributen, der immer bestehenbleibt.
+
+    Wenn man nach einem Tod wieder ins Leben zurueckkehrt, sind die Attribute
+    um ein gewisses Mass herabgesetzt. Sie regenerieren sich aber im Laufe der
+    Zeit von selbst wieder.
+
+ SIEHE AUCH:
+    info, zaubertraenke, lebenspunkte
+
+ LETZTE AeNDERUNG: 17.01.2009, Zesstra
+
diff --git a/doc/help/balance b/doc/help/balance
new file mode 100644
index 0000000..9832dc8
--- /dev/null
+++ b/doc/help/balance
@@ -0,0 +1,58 @@
+Balance im MorgenGrauen
+=======================
+
+Ein Thema ueber das immer wieder gerne gestritten wird, ist die Balance
+im MorgenGrauen. Auf der einen Seite steht der (auf den ersten Blick 
+verstaendliche) Wunsch der Spieler, moeglichst maechtig zu sein, auf
+der anderen Seite steht der Wunsch der Magierseite, den Spielern noch
+Herausforderungen stellen zu koennen. Wenn Spieler nur noch zappend
+durch die Gegend rennen, dann wird das auch schnell langweilig ...
+
+
+Es gibt vier 'Institutionen' der Balance:
+
+1) Gilden-Balance
+
+Hierfuer zustaendig ist ein Erzmagier (momentan Humni), 
+der versucht, die Gilden in einem einigermassen vernuenftigen Rahmen
+zu halten. Hierbei beruecksichtigt werden neben den Faehigkeiten auch
+der Aufwand und das allgemeine Gilden-Feeling.
+
+2) Objekt-Balance
+
+Fuer die Objekt-Balance ist eine Gruppe von Spielern und Magiern zustaendig
+(das sogenannte Balance-Team). Behandelt werden praktisch alle Dinge, die
+den Kampf irgendwie beeinflussen (Waffen, Ruestungen, magische Objekte, etc.)
+Momentan setzt sich das Balance-Team aus folgenden Personen zusammen:
+
+  McDee, Tassram, Talanis, Croft, Xutelius, Soynada, Zook 
+  und Humni (als verantwortlicher Erzmagier).
+
+** out of order: 3) NPC-Balance
+**
+** Die NPC-Balance soll ein Ansprechpartner fuer Spieler sein, die der Ansicht
+** sind, dass irgendein Monster (NPC - non player character) zu stark, unfair
+** oder sonst irgendwie daneben ist. 
+**
+** Eine Institution "NPC-Balance" gibt es bis auf weiteres nicht mehr.
+** Zustaendig fuer die Eintragung von Erstkill-Stupsen ist Arathorn. Fuer
+** Beschwerden ueber Monster, die zu stark oder zu unfair sind, stehen
+** die Regionsmagier oder Zook zur Verfuegung.
+
+4) Heilungs-Balance
+
+Die Heilungs-Balance deckt alles ab, was irgendwie mit Heilung zu tun
+hat. Konkret sind das Kneipen, Heiler (auch Gift, Krankheit, etc.), feste
+Heilstellen, tragbare Heilung und Sachen die Saettigung oder Alkohol
+runtersetzen. Momentan zustaendig fuer dieses Gebiet ist Humni.
+
+
+Alle Institutionen arbeiten besser, wenn sie sowohl von Spielern als
+auch Magiern unterstuetzt werden. Speziell die NPC-Balance kommt in der
+Regel NUR zum Einsatz, wenn ein Spieler einen Misstand anspricht. Also
+sprecht die entsprechenden Leute an, wenn ihr irgendwo ein Problem seht!
+
+------------------------------------------------------------------------------
+ LETZTE AENDERUNG:
+    13-01-2011, Zesstra
+    
diff --git a/doc/help/bewegung b/doc/help/bewegung
new file mode 100644
index 0000000..43e68a6
--- /dev/null
+++ b/doc/help/bewegung
@@ -0,0 +1,24 @@
+
+Bewegen im MorgenGrauen
+=======================
+
+ KOMMANDOS:
+    s(ueden), w(esten), n(orden), o(sten), ob(en), u(nten), n(ord)w(esten),
+    ...
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Mit diesen Kommandos kann man den Raum in eine bestimmte Richtung
+    verlassen. Jede der Himmelsrichtungen kann durch einen (oder zwei)
+    Buchstaben abgekuerzt werden.
+
+    ABER: Manchmal muss man einen Weg finden, der nicht in eine bestimmte
+    Himmelsrichtung fuehrt.
+
+ SIEHE AUCH:
+untersuche, kurz, ausgaenge, klettere, reise
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/help/bezugspunkt b/doc/help/bezugspunkt
new file mode 100644
index 0000000..01ed421
--- /dev/null
+++ b/doc/help/bezugspunkt
@@ -0,0 +1,54 @@
+
+Der Bezugspunkt
+-------------------
+
+    Der Bezugspunkt oder das Bezugsobjekt ist bei den Befehlen "schau",
+    "untersuche", "lies", "taste", "lausche" und "rieche" wichtig.
+
+    Diese Kommandos merken sich den jeweils zuletzt erfolgreich untersuchten
+    Gegenstand (bzw. den Gegenstand, an dem das zuletzt erfolgreich
+    untersuchte Detail klebt) als Bezugspunkt fuer das naechste Kommando.
+    Hierbei benutzen alle genannten Befehle den gleichen Bezugspunkt.
+
+ BEISPIELE:
+    Beispiel 1:
+    > "unt schrank"
+    -> gibt es einen Schrank, wird der beschrieben _und_ er wird der neue
+       Bezugspunkt.
+    > "unt tuer"
+    -> Jetzt wird "tuer" am Bezugsobjekt "schrank" gesucht. Existiert das
+       Detail, bleibt der Schrank auch weiterhin der Bezugspunkt. Existiert
+       es nicht, wird ein Detail "tuer" oder ein Objekt "tuer" im Raum und
+       dann in euch gesucht. Gibt es da sowas, wird es beschrieben und das
+       neue Bezugsobjekt. Wenn alles fehlschlaegt, gibt es eine
+       Fehlermeldung und der Bezugspunkt geloescht. Beim naechsten "unt" wird
+        zuerst im Raum und dann in euch gesucht.
+    (Es wuerde auch gehen: "unt tuer an schrank".)
+
+    Beispiel 2:
+    > "unt paket"
+    -> Das Paket wird beschrieben und der neue Bezugspunkt.
+    > "unt seil"
+    -> Das Seil im paket wird beschrieben (sofern sie existiert) und der
+       neue Bezugspunkt.
+    > "unt wolle"
+    -> Das Detail "wolle" am Seil wird beschrieben. Das Seil bleibt der
+       Bezugspunkt.
+    (In diesem Beispiel koennte man die Wolle auch direkter untersuchen,
+     indem man "unt wolle an seil in paket in mir" benutzt.)
+
+    Beispiel 3:
+    > "unt rucksack"
+    -> Der Rucksack wird untersucht und der neue Bezugspunkt.
+    > "lies schild"
+    -> Hat der Rucksack ein lesbares Detail "schild", wird es ausgegeben. Der
+       Rucksack bleibt der Bezugspunkt. Wenn nicht, wird ein lesbares Detail
+       oder ein lesbarer Gegenstand "schild" im Raum und in euch gesucht.
+    (Es wuerde auch gehen: "lies schild an rucksack")
+ 
+SIEHE AUCH:
+    schau, untersuche, lies, taste, lausche, rieche
+
+ LETZTE AeNDERUNG:
+    18.01.2013, Zesstra
+
diff --git a/doc/help/bierschuettler b/doc/help/bierschuettler
new file mode 100644
index 0000000..4e6ba58
--- /dev/null
+++ b/doc/help/bierschuettler
@@ -0,0 +1,48 @@
+
+Die Bierschuettlergilde
+-----------------------
+
+    Vor einiger Zeit gruendeten die Shakies auf Shaky Island die Gilde der
+    Bierschuettler, damit diese Kunst des Bierschuettelns nicht ausstirbt.
+    Bruno wurde von allen Shakies zum Gildenshaky gewaehlt, weil er sich mit
+    diesem Thema bestens auskennt. Nun koennen alle Spieler bei ihm
+    einundzwanzig Zaubersprueche lernen. Da gibt es zum Beispiel das Erdbeben
+    oder den Hitzeschlag, die im Kampf eingesetzt werden koennen. Natuerlich
+    kann ein echter Bierschuettler seinen Feind auch alkoholisieren und sich
+    selbst auch nuechterner machen.
+
+    Da Shakies sehr behaart sind, ist Bruno in der Lage dem Gildenmitglied den
+    Trick mit der Haarruestung zu zeigen, die nicht nur schuetzt, sondern noch
+    ein kleines Special hat. Die Shakies sind eine frohe Rasse, die natuerlich
+    auch ueber die Zaubersprueche Party und Freibier verfuegen. Fuer jeden
+    Gildenlevelaufstieg ist Bruno bereit, dem Mitglied einen neuen Spruch
+    beizubringen. Die Gildenlevel sind wie in der Abenteurergilde an die
+    Spielerlevel gekoppelt, so dass auch Anfaenger schnell aufsteigen koennen.
+
+    Trotzdem ist die Gilde der Bierschuettler fuer Seher nicht uninteressant.
+    Die Gildenlevel reichen vom 'nuechternen Anfaenger' bis hin zum
+    'Bierschuettelmeister'. Uebrigens gibt es keinerlei Auflagen wenn man den
+    Bierschuettlern beitreten moechte.
+
+    Bruno gibt Bierschuettlern ab Level zwei noch ein kleines Geschenk mit auf
+    den Weg. Es handelt sich dabei um eine Bierschuerze, die dem Mitglied
+    einige Vorteile bietet. Man bekommt dann zum Beispiel in einigen Kneipen
+    einen Rabatt von bis zu 25% eingeraeumt.
+
+    Weitere Informationen, zum Beispiel ueber die einzelnen Sprueche, kann man
+    direkt bei Bruno einholen. Ausserdem steht es jedem Spieler frei, auf dem
+    Kanal [Bierschuettler:] zu lauschen, den man mit dem Befehl
+    -Bierschuettler+ einschalten kann. Hier gibt Bruno auch bekannt, wenn die
+    Bierschuettler neue Mitglieder begruessen duerfen.
+
+ SIEHE AUCH:
+    gilden, attribute, ebenen
+
+ FUeR GILDENMITGLIEDER:
+    zaubersprueche, bierschuerze, abstufungen
+
+    *Ist man Mitglied einer anderen Gilde, so muss man fuer die Hilfen zu den
+    Spruechen `hilfe gilde bierschuettler <name>' benutzen!*
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/help/buecher b/doc/help/buecher
new file mode 100644
index 0000000..d4d4218
--- /dev/null
+++ b/doc/help/buecher
@@ -0,0 +1,26 @@
+
+Buecher
+=======
+
+    Im MorgenGrauen gibt es viele Buecher, die man mit 'lies buch' lesen kann.
+    Bei standardisierten Buechern kann man die folgenden Kommandos benutzen:
+
+     oeffne buch
+        Zum Oeffnen des Buches; die meisten Buecher sind anfaenglich
+        geschlossen.
+
+     schliesse buch
+        Zum Schliessen des Buches; wenn man das Buch ausgelesen hat, schliesst
+        man es automatisch.
+
+     lies buch
+        Wenn das Buch geschlossen ist, zeigt dieses Kommando die Titelseite;
+        ist es offen, zeigt es die Seite, auf der es aufgeschlagen ist, und
+        blaettert um. Wird das Ende des Buches erreicht, wird es geschlossen.
+
+     blaettere zu seite <n>
+        Mit diesem Kommando kann man im Buch vorwaerts- und rueckwaerts
+        blaettern, natuerlich nur, wenn das Buch geoeffnet ist.
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/help/chaos b/doc/help/chaos
new file mode 100644
index 0000000..35aae8d
--- /dev/null
+++ b/doc/help/chaos
@@ -0,0 +1,46 @@
+
+Die Gilde der Krieger des Chaos
+-------------------------------
+
+    Man kann in die Gilde der Krieger des Chaos bei Kruuolq, dem obersten
+    Meister des Chaos, eintreten. Um aufgenommen zu werden muss man mindestens
+    Spielerlevel 9 (Kundschafter/Kundschafterin) sein.
+
+    Insgesamt gibt es 11 Gildenstufen, vom kleinen Chaoten bis zum Meister des
+    Chaos. Fuer jede Stufenerhoehung muss man einen Kampf auf Leben und Tod in
+    der gildeneigenen Arena austragen, man muss Sprueche und Daemonen, der
+    jeweiligen Stufe entsprechend gut, beherrschen und ab Gildenlevel 9 kommen
+    noch besondere Anforderungen hinzu.
+
+    Je nach Stufe kann man unterschiedliche Faehigkeiten erlernen. Diese
+    teilen sich grob in zwei Kategorien:
+    1) Zaubersprueche
+    2) Beschwoeren von Daemonen.
+    Mit Hilfe der Zaubersprueche kann man z.B. Raeume verdunkeln oder einen
+    Chaosball werfen, mit Hilfe der Daemonen kann man Sachen identifizieren
+    oder, mit steigender Stufe, Daemonen beschwoeren die den Spieler im Kampf
+    unterstuetzen. Da es nicht ganz so einfach ist Daemonen zu beschwoeren
+    kann es dabei passieren, dass der falsche Daemon erscheint. Um dies zu
+    verhindern sollte man sich im Gildenladen 'Nuetzliche Zutaten fuer boese
+    Zaubersprueche' mit allerlei Utensilien ausstatten, die einen bei den
+    Beschwoerungen unterstuetzen. Genaueres ueber die Zaubersprueche und
+    Daemonen erfaehrst Du bei Kruuolq.
+
+    Ueben kann man Sprueche und Beschwoerungen in dem nur fuer
+    Gildenmitglieder zugaenglichen Ritualraum bei Wuqour. Er hilft Anfaengern
+    sich besser in der Welt des Chaos zurecht zu finden.
+
+    Es gibt zwei Voraussetzungen um seine chaotischen Faehigkeiten voll zu
+    entfalten. Man muss ausreichend 'boese' sein um die Sprueche anzuwenden
+    und man muss einen Daemonen beschwoeren, der in die eigene Haut eindringt,
+    sie verstaerkt und die Attribute des Chaoten gegebenenfalls mit seinen
+    eigenen auf- oder abwertet. Dieser Daemon ermoeglicht erst die Anwendung
+    aller Sprueche.
+
+    Natuerlich haben die Chaoten auch eine eigene Ebene [Chaos:].
+
+ SIEHE AUCH:
+    attribute, ebenen, gilden
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/help/cicerones b/doc/help/cicerones
new file mode 100644
index 0000000..ba488cb
--- /dev/null
+++ b/doc/help/cicerones
@@ -0,0 +1,95 @@
+CICERONES 
+---------
+
+<bildung>
+
+Der Begriff stammt aus dem italienischen und ist eine Bezeichnung fuer einen
+Fremdenfuehrer oder Aehnliches.
+
+</bildung>
+
+Cicerones sind Spieler, die sich bereiterklaert haben, fuer unsere kleinen
+Spieler da zu sein. Sie sind die Ansprechpartner fuer die Neulinge im MUD.
+
+Jeder Spieler sollte bereit sein, Neulingen unter die Arme zu greifen. Die
+Cicerones haben aber abgesehen davon, dass sie den Titel ,,Cicerone'' fuehren
+duerfen, keine besonderen Vorrechte.
+
+Um Cicerone zu werden kann man den Befehl ,,cicerone'' verwenden. Naehres 
+dazu siehe auf der dazugehoerigen Hilfeseite.
+
+AUFGABEN DER CICERONES
+----------------------
+
+Die Cicerones sollen kleinen Spielern auf Anfrage beistehen. Sie sollen ihnen
+Fragen beantworten (natuerlich keine Quest-KLs vorkauen) und ihnen die ersten
+Schritte und Befehle im MG erklaeren. Auch Konzepte ("Gilden" oder 
+"Zaubertraenke") sollen erlaeutert werden.
+
+Cicerones sollen aber definitiv nicht dem kleinen Spieler das Spielen 
+abnehmen. Klar darf man bei der Anfaengerquest danebenstehen und bei 
+offensichtlichen Problemen Tips geben. Aber immer mit dem Hintergrund, dass
+der Spieler etwas dabei lernt.
+
+Beispiel: (diese Quest ist fiktiv)
+
+Humni sagt: Wie geht das hier jetzt?
+Muadib sagt: Ist doch ganz klar. Du musst den Stein nehmen, dann gibst Du ein
+Muadib sagt: ,wirf stein auf berg'. Dann kommt ein Schatten runter, dem gibst
+Muadib sagt: Du das Amulett und sagst dann das Zauberwort ,,Bloerderkrampf.''
+Humni sagt: Oh toll! Danke!
+
+_FALSCH_.
+
+Humni sagt: Wie geht das hier jetzt?
+Muadib sagt: Na, schau Dich mal genau um. Du kannst auch gewisse Dinge 
+Muadib sagt: untersuchen, die es quasi ueberall gibt. Es gibt quasi ueberall
+Muadib sagt: einen Boden.
+Humni sagt: Aha.
+Humni sagt: Boah, da liegt ja ein Stein?
+Muadib sagt: Ja, richtig. Was kannst Du damit machen?
+Humni gruebelt.
+Humni sagt: Aufessen?
+Muadib sagt: Nein. Das ist ein Stein.
+Humni sagt: Ach so.
+Humni sagt: Ich kann ihn ja mal aufheben.
+Muadib sagt: Genau.
+Humni nimmt den Stein.
+Muadib sagt: So, dann schau Dich mal weiter um. Was ist hier noch?
+Humni sagt: Ein Berg.
+Muadib sagt: Hast Du Dir den mal genauer angeguckt?
+Humni sagt: Da steht, ich kann den mit Steinen bewerfen, der mag das.
+.
+.
+
+_RICHTIG_
+
+Ja, der zweite Fall ist nerviger, er kostet mehr Zeit und der Spieler hat 
+weniger Erfolgserlebnisse als im ersten Fall.
+
+Halt.
+
+Stimmt nicht.
+
+Klar, im ersten Fall hat der Spieler nach 2 Minuten seine erste Quest 
+geloest - toll, oder? Nur was ist mit der naechsten? Wenn bei der naechsten
+Quest kein Cicerone danebensteht hat der Spieler verloren. Er hat naemlich
+nichts selbst rausgekriegt und nichts gelernt.
+
+Im zweiten Fall hat der Spieler ganz viel gelernt: Er hat gelernt, dass man
+eigentlich ueberall den Boden angucken kann, dass man sich die Dinge angucken
+muss und dass man mit den Gegenstaenden logische Sachen machen kann und dass
+sehr, sehr oft in den Details die Anwendungsmoeglichkeiten versteckt sind.
+
+DAS ist die Aufgabe eines Ciceronen, und die kostet Zeit.
+
+Darum sollte man sich gut ueberlegen, ob man diese Zeit gerade jetzt im 
+Moment aufbringen kann und will. Wenn nicht, sollte man es besser sein 
+lassen.
+
+SIEHE AUCH
+----------
+
+einfuehrung, cicerone
+
+Letzte Aenderung: 2005-01-04, von Humni
diff --git a/doc/help/client b/doc/help/client
new file mode 100644
index 0000000..2c136a4
--- /dev/null
+++ b/doc/help/client
@@ -0,0 +1,19 @@
+Clients
+=======
+
+    Man kann in Morgengrauen ueber ein einfaches 'telnet'-Programm spielen,
+    aber diese trennen meist Ein- und Ausgabe nicht und machen damit den
+    Lesefluss meist unangenehm.
+    
+    Es gibt verschiedene Programme, ueber die das Unterscheiden der Ein- und
+    Ausgabe, ein Einfaerben/Auslassen von Texten (wie zB den Kanaelen oder
+    Kampfmeldungen) und noch vieles mehr moeglich ist.
+    
+    Verlinkt findet ihr auf unserer Webseite: http://mg.mud.de einige der
+    fuer am besten befundenen: http://mg.mud.de/newweb/download/index.shtml
+
+ SIEHE AUCH:
+    www
+
+ LETZTE AeNDERUNG:
+    20. Juli 2007 Gloinson
diff --git a/doc/help/crash b/doc/help/crash
new file mode 100644
index 0000000..cbc2563
--- /dev/null
+++ b/doc/help/crash
@@ -0,0 +1,20 @@
+Crashs und ihre Folgen
+-----------------------
+
+    Vorsichtig ausgedrueckt ist ein 'Crash' ein unbeabsichtigter Neustart 
+    des Spieles (siehe: hilfe reboot), jedoch kann es hierbei zu 
+    unvorhersehbaren Nebenwirkungen kommen. Solltest Du also der Meinung
+    sein, dass nach einem 'Crash' irgendetwas mit Dir nicht stimmen
+    kann, wende Dich an einen Magier Deines Vertrauens.
+    Die Nebenwirkungen eines 'Crashs' sind jedoch relativ selten.
+
+    Die Gruende fuer einen 'Crash' sind vielfaeltig. Doch wenn man es
+    auf den Punkt bringen moechte, wird ein 'Crash' schlicht und 
+    einfach durch schwere Programmierfehler ausgeloest, an denen sich das 
+    MorgenGrauen verschluckt. 
+    
+ SIEHE AUCH:
+    reboot, magier
+
+ LETZTE AeNDERUNG:
+    Sat, 14.10.2000, 10:16:25 von Bierchen.
diff --git a/doc/help/dunkelelfen b/doc/help/dunkelelfen
new file mode 100644
index 0000000..ad78184
--- /dev/null
+++ b/doc/help/dunkelelfen
@@ -0,0 +1,51 @@
+
+Die Dunkelelfengilde
+-----------------------
+
+      Das Volk der Dunkelelfen lebt in einer grossen Hoehlenstadt gut
+      versteckt hinter einem Wasserfall. Ueber kaum ein anderes Volk gibt es
+      soviele Vorurteile wie ueber die Dunkelelfen und so werden sie von
+      allen misstrauisch beaeugt oder sogar bekaempft. In diesem Kampf,
+      insbesondere gegen die Elfen, sind sie voellig auf sich allein gestellt
+      und so hat sich eine mehr oder minder autarke Gesellschaft entwickelt.
+      Die Dunkelelfen haben eine eigene Kultur und eine eigene Goettin der
+      sie huldigen. Wie auch die Elfen verfuegen sie ueber ausserordenlich
+      grosse magische Faehigkeiten, auch wenn sie sich mehr auf die schwarze
+      Seite der Magie spezialisiert haben.
+
+      Die Gilde besteht aus vier Teilen, einem allgemeinen Teil mit Skills
+      wie identifiziere, schaetz, feuerlanze usw. die jeder in der Gilde
+      erlernen muss. Darueber hinaus gibt es drei verschiedene Zweige der
+      Gilde, von denen jeder Dunkelelf in zwei Zweige eintreten und sich am
+      Ende dann in einem dieser zwei Zweige spezialisieren kann. Diese drei
+      Zweige sind Kampf, Magie und Klerus. Insgesamt gibt es also 6 moegliche
+      Zweigkombinationen, zwischen denen die Dunkelelfen frei waehlen koennen.
+
+      Der Kampfzweig widmet sich in erster Linie dem Umgang mit dem
+      Runenschwert. Es gibt einen Schwertskill, einen Skill zum Schmieden von
+      Runenschwertern, Verzauberungen des Schwerts, sowie einige allgemeine
+      Kampffaehigkeiten wie Finte, Parade, Ausweichen, Ausfall.
+
+      Der Klerikale Zweig widmet sich der Huldigung von Seyt-Hamakha und der
+      dunklen Seite der Macht. Die Faehigkeiten der Kleriker sind z.B.
+      verfluchen, vergiften, das Beschwoeren von Zombies, jedoch keine
+      Heilzauber oder soetwas.
+
+      Im Magiezweig geht es dann um Zaubersprueche im engeren Sinne.
+      Feuerlanzen, Schmerzen, Schutzzauber und aehnliches.
+
+      Jeder der drei Zweige hat einen eigenen Vorsitzenden. Nyela die
+      Waffenschmiedin steht dem Kampfzweig vor. Teo'na'katl der Hohepriester,
+      dem Klerikalen Zweig und Nenais'u die Zauberin dem Magiezweig.
+      Ausserdem ist Nenais'u als Vorsitzende des Magiezweigs traditionell
+      auch das Gildenoberhaupt. Zusaetzlich zu den drei Vorsitzenden gibt es
+      drei weitere wichtige NPCs. Der Ruestungsschmied Baiy'Sagar, der Wirt
+      und eine Kraeuterkundige im Labor der Delfengilde.
+
+
+ SIEHE AUCH:
+    gilden, attribute, ebenen, gildenstufen, abstufungen
+
+
+ LETZTE AeNDERUNG:
+    Mon, 28. Jul 2003, 23:27:04 von Padreic
diff --git a/doc/help/editor b/doc/help/editor
new file mode 100644
index 0000000..1ebcfb4
--- /dev/null
+++ b/doc/help/editor
@@ -0,0 +1,129 @@
+
+Der Editor
+==========
+
+    Der Editor wird von der Zeitung, der Post und in den Seherhaeusern
+    verwendet. Es besteht keine Trennung zwischen Eingabe- und Kommandomodus;
+    Befehle werden mit einer Tilde (~) als erstem Zeichen in der Zeile
+    eingeleitet (Ausnahmen: ., ** und !<cmd>).
+
+    Der Editor versteht folgende Befehle:
+
+     Allgemeine Kommandos
+
+         . oder **
+            Der eingegebene Text wird uebernommen, der Editor beendet.
+
+         !<cmd>
+            Das Kommando <cmd> wird ausgefuehrt, als wenn Du gerade nicht
+            schreiben wuerdest.
+
+         ~q
+            Abbruch. Schon eingegebener Text ist verloren; in der Post/MPA
+            wird kein Artikel erzeugt.
+
+         ~h
+            Eine Hilfeseite wird angezeigt (quasi eine Kurzform dieser Seite)
+
+         ~!
+            Der Editor wird voruebergehend verlassen.
+            Man kann mit dem Befehl ~r weiterschreiben (das ~r ist als
+            normales Kommando ausserhalb des Editors einzugeben!)
+
+         ~r
+            Der gesamte bisher geschriebene Text wird angezeigt.
+         ~R
+            Wie ~r, es werden jedoch noch Zeilennummern mit ausgegeben.
+
+         ~z
+            Der Textausschnitt um den Cursor herum wird angezeigt.
+         ~Z
+            wie ~z, es werden jedoch noch Zeilennummern mit ausgegeben.
+
+         ~s
+            Es werden Statusinformationen angezeigt.
+
+         ~b
+            Es wird zwischen Flattersatz (default) und Blocksatz umgeschaltet
+
+        Nur fuer Magier:
+
+         ~i <filename>
+            Die Datei <filename> wird an der momentanen Cursorposition in den
+            Text eingefuegt.
+            Aus Kompatibilitaetsgruenden kann man auch ~r <filename> nehmen.
+
+     Zeilenorientierte Kommandos
+
+         ~d
+            Die letzte Zeile wird geloescht (Text-, nicht Eingabezeile).
+
+         ~v
+            Es wird zwischen dem Einfuegemodus (default) und dem
+            Ueberschreibmodus umgeschaltet.
+
+         ~s !s1!s2!
+            Ersetzt das erste Vorkommnis des Strings s1 durch den String s2 in
+            der aktuellen Zeile.
+            Statt durch ! koennen die Strings auch durch beliebige andere
+            Zeichen getrennt werden, die weder in s1 noch in s2 vorkommen.
+            Mit der Form ~s !s1!s2!p wird die geaenderte Zeile direkt
+            angezeigt.
+
+         ~f
+            Formatiert die aktuelle Zeile neu.
+
+     Cursorkommandos
+
+         ~cu
+            Der Cursor wird um eine Zeile nach oben bewegt.
+         ~cd
+            Der Cursor wird um eine Zeile nach unten bewegt.
+
+         ~ct
+            Der Cursor wird an den Anfang des Textes bewegt.
+         ~cb
+            Der Cursor wird an das Ende des Textes bewegt.
+
+         ~cs
+            Der Cursor wird an den Anfang des markierten Blocks bewegt.
+         ~ce
+            Der Cursor wird an das Ende des markierten Blocks bewegt.
+
+         ~c<nr>
+            Der Cursor wird in die Zeile <nr> bewegt.
+         ~c+<nr>
+            Der Cursor wird um <nr> Zeilen nach oben bewegt.
+         ~c-<nr>
+            Der Cursor wird um <nr> Zeilen nach unten bewegt.
+
+     Blockorientierte Kommandos
+
+         ~bs
+            Der Blockanfang wird auf die aktuelle Zeile gesetzt.
+         ~bs<nr>
+            Der Blockanfang wird auf die Zeile <nr> gesetzt.
+
+         ~be
+            Das Blockende wird auf die aktuelle Zeile gesetzt.
+         ~be<nr>
+            Das Blockende wird auf die Zeile <nr> gesetzt.
+
+         ~F
+            Der markierte Block wird neu formatiert.
+
+         ~D
+            Der markierte Block wird geloescht.
+
+         ~m
+            Der markierte Block wird an die aktuelle Cursorposition
+            verschoben.
+
+    Alles andere gilt als Text. Ueberlange Zeilen werden auf eine maximale
+    Laenge von 78 Zeichen umgebrochen.
+
+    Nach ~!, oder wenn man beim Schreiben netztot wird, kann man mit ~r wieder
+    in den Editor einsteigen.
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/help/einfuehrung b/doc/help/einfuehrung
new file mode 100644
index 0000000..bc8e8dc
--- /dev/null
+++ b/doc/help/einfuehrung
@@ -0,0 +1,58 @@
+Eine kurze Einfuehrung
+======================
+
+Hallo und willkommen im MorgenGrauen!
+
+Du befindest Dich jetzt in einer virtuellen Welt, in der Du Deinen Charakter
+(so bezeichnet man Deine Spielfigur) mittels verbaler Befehle dazu bringen
+kannst, die verschiedensten Bewegungen und Aktionen auszufuehren. Du kannst
+umhergehen, Dich umsehen, kaempfen, Dinge aufheben und benutzen und vieles
+mehr! Dazu solltest Du Dich mit den grundlegenden Befehlen vertraut machen,
+die dazu notwendig sind. Folgende Hilfeseiten seien Dir an's Herz gelegt:
+
+- "hilfe bewegung"
+- "hilfe syntax"
+
+Du kannst Dich mit den anderen Spielern auf verschiedene Weise
+verstaendigen. Wichtig ist zum einen das direkte Mitteilen, das funktioniert
+so: 
+
+"teile <spielername> mit <nachricht>"
+
+also zum Beispiel
+
+"teile zook mit hallo mudgott!"
+
+Das andere sind die sogenannten "Ebenen", auf denen Du allen Spielern (die
+dort grade mithoeren) etwas mitteilen kannst. Am Anfang befindest Du Dich
+auf den Ebene "anfaenger" und "abenteuer". Wenn Du zum Beispiel die anderen
+Spieler etwas fragen moechtest, tipp zum Beispiel
+
+-anfaenger Hallo ihr da draussen! Wie komme ich zur Abenteurergilde?
+
+und alle, die auf der Anfaengerebene mithoeren, werden diese Nachricht
+bekommen. Fuer weitere Informationen zu den Ebenen lies einfach mal
+die Hilfeseite dazu ("hilfe ebenen").
+
+Eines der Hauptziele dieses Spieles ist, seine Spielfigur weiterzuentwickeln,
+also die Werte zu verbessern. Informationen dazu findest Du auf den
+Hilfeseiten "stufenpunkte", "abenteuer" und "attribute".
+
+Es sei noch darauf hingewiesen, dass es natuerlich auch gewisse Regeln
+gibt, an die sich die Spieler halten muessen ("hilfe regeln").
+Lies Dir diese auf jeden Fall in Ruhe vollstaendig durch!
+
+Es gibt auch gewisse Spieler, die sich extra bereiterklaert haben, Dir als
+Anfaenger zur Seite zu stehen, die Ciceronen. Wenn Du wissen willst, wer 
+davon online ist, kannst Du den Befehl
+
+"kwer cicerone"
+
+verwenden. Die sollten dann anwesend sein und auch Zeit fuer Dich und Deine
+Fragen haben.
+
+ SIEHE AUCH:
+     abenteuer, bewegung, forscherpunkte, regeln, stufen, syntax, ciceronen
+ 
+ LETZTE AeNDERUNG:
+    Sun, 24.06.2001, 22:30:00 von Nachtwind
diff --git a/doc/help/erstkillstufenpunkte b/doc/help/erstkillstufenpunkte
new file mode 100644
index 0000000..0176881
--- /dev/null
+++ b/doc/help/erstkillstufenpunkte
@@ -0,0 +1,43 @@
+Erstkill-Stufenpunkte (EK-Stupse)
+=================================
+
+    Zusaetzlich zu der allgemeinen Erlaeuterung, was EK-Stufenpunkte sind
+    und wofuer diese gutgeschrieben werden, die in der Hilfeseite zu den
+    Stufenpunkten nachzulesen ist, sei hier ein technisches Detail zu 
+    Erstkill-Stufenpunkten in Zusammenhang mit Kerbholz und Duesterwald-
+    Plakette (beides Questbelohnungen) naeher erlaeutert:
+      
+    Wenn ein Monster, fuer das EK-Stupse gutgeschrieben werden, zum
+    allerersten Mal ueberhaupt getoetet wird, d.h. in der Regel nach
+    dem Neuanschluss eines Gebietes, ist dieser Kill noch nicht sofort
+    auf der Plakette erkennbar, und auch noch nicht in die Gesamt-
+    Stufenpunkte des Spielers eingerechnet. Das Kerbholz wird jedoch
+    anzeigen, dass der Spieler den Kill schon hat.
+    
+    Aus technischen Gruenden ist es unumgaenglich, solche "frischen" EKs
+    zunaechst nur temporaer zu registrieren und von einem Erzmagier auf
+    Gueltigkeit pruefen zu lassen. So lange der zustaendige EM (wer das 
+    jeweils  ist, laesst sich auf der Hilfeseite "erzmagier" nachlesen)
+    den EK nicht bestaetigt hat, gibt es voruebergehend also eine
+    Diskrepanz zwischen Kerbholz, Stufe und Plakette. Sobald der
+    EK jedoch bestaetigt wurde, verschwindet diese.
+
+    Weiterhin ist zu beachten, dass eventuell vergebene Sonder-
+    Stufenpunkte fuer besonders harte Monster (z.B. Drachengott)
+    erst *nach* einer Bestaetigung des EKs durch den zustaendigen
+    EM eingetragen werden koennen, so dass es nachtraeglich noch zu
+    einer Erhoehung der Stufenpunkte des betreffenden Spielers kommen kann.
+
+    WICHTIG:
+    Diese Hinweise gelten nur fuer solche Spieler, die Monster getoetet 
+    haben, welche noch nie zuvor umgehauen wurden. Fuer alle, die den Kill 
+    erst spaeter erreichen, aendert sich nichts: Die Punkte werden sofort in 
+    voller Hoehe gutgeschrieben, und die Plakette zaehlt den EK auch sofort 
+    mit.
+
+  SIEHE AUCH:
+    stufen, stufenpunkte, erzmagier
+
+  LETZTE AeNDERUNG:
+    16. August 2013, von Arathorn
+
diff --git a/doc/help/erzmagier b/doc/help/erzmagier
new file mode 100644
index 0000000..8963000
--- /dev/null
+++ b/doc/help/erzmagier
@@ -0,0 +1,66 @@
+Erzmagier
+=========
+
+ERZMAGIER:
+  Der Einflussbereich der Erzmagier beschraenkt sich nicht nur auf Bereiche
+  innerhalb des Spiels, sondern auch auf Bereiche ausserhalb. Sie koennen
+  Aenderungen an der Basis-MudLib vornehmen und damit die Rahmenbedingungen
+  des Spiels aendern oder ergaenzen. Ausserdem sind sie die richtigen
+  Ansprechpartner, wenn man sein Passwort vergessen hat, oder wenn durch
+  einen Absturz des Rechners das Savefile eines Spielers kaputt gegangen
+  ist.
+
+  Die MudGoetter Jof, Rumata und Zook sind dabei die hoechsten Instanzen;
+  Jof macht sich allerdings in letzter Zeit etwas rar.
+
+Die Aufgabenbereiche der einzelnen Erzmagier sind wie folgt festgelegt:
+
+      Amaryllis
+        Amaryllis ist fuer die Administration und das Hosting unseres
+        Servers zustaendig und hat ausserdem das Amt des Sheriffs inne.
+      Arathorn
+        Arathorn kuemmert sich um die Vergabe der Erstkill-Stupse (dazu
+        begutachtet er die NPCs). Darueberhinaus kuemmert er sich 
+        um die Weiterentwicklung der Mudlib.
+      Ark
+        Ark testet neue Abenteuer sowie sogenannte MiniQuests bevor sie
+        auf Spieler losgelassen werden und traegt diese mit 
+        angemessener Punktzahl ein.
+        Er kuemmert sich um die Pflege der bestehenden Rassen sowie die
+        Ausarbeitung weiterer Rassen. Dazu gehoeren sowohl die Rassenshells
+        als auch die Seele.
+      Humni
+        Humni ist fuer die Gilden-Balance zustaendig und leitet das
+        Balance-Team. Des Weiteren ist er fuer die Heilungs-Balance
+        zustaendig.
+      Jof
+        Jof ist als MudGott fuer mehr oder weniger alles zustaendig.
+      Rumata
+        Rumata ist MudGoettin und damit auch Maedchen fuer alles.
+        Und delegiert dann ;-)
+      Miril
+        Miril verwaltet die Forscherpunkte und Zaubertraenke.
+      N.N. 
+        Er/Sie ist weiterhin fuer die Pflege des Stufensystems verantwortlich 
+        und kuemmert sich um die Pflege der Mudlib.
+      Zesstra
+        Zesstra kuemmert sich um unseren Driver und die Mudlib.
+      Zook
+        Zook ist einer der MudGoetter und damit Maedchen fuer 
+        alles. Er ernennt Regionsmagier und beruft diese gegebenenfalls
+        ab. Jungmagier koennen sich mit ihren Gesellenstuecken gerne bei
+        ihm melden.
+
+Noch im Kreis der Erzmagier, aber schon ihrer Aufgaben entbunden 
+sind: 
+
+      Hate
+        Hate war fuer die Administration unseres Mud-Rechners zustaendig.
+
+SIEHE AUCH:
+        ebenen, regionen, goetter
+        (nur Magier): balance
+
+LETZTE AeNDERUNG:
+        2011-09-25 von Zook.
+
diff --git a/doc/help/essen_trinken b/doc/help/essen_trinken
new file mode 100644
index 0000000..887c4ed
--- /dev/null
+++ b/doc/help/essen_trinken
@@ -0,0 +1,30 @@
+
+Essen und Trinken
+=================
+
+     Eines voraus: Essen und Trinken sind in Morgengrauen nicht lebens-
+     notwendig. Niemand wird an Hunger oder Durst sterben und auch der
+     Gang auf die Toilette ist hoechst optional.
+
+     Hauptsaechlich in Kneipen kann man hier essen, trinken oder sich
+     besaufen. Es gibt auch tragbare Getraenke und Speisen.
+     Dabei haengen die Grenzen des Fassungsvermoegens fuer Nahrung,
+     Fluessigkeit und Alkohol vor allem von der Rasse ab, aber auch manche
+     Gilden koennen durch Magie oder Uebung fuer mehr Kapazitaet sorgen.
+     
+     Mit der Zeit entleert man sich dann wieder von alleine. Man kann diesem
+     Vorgang auch etwas auf die Spruenge helfen, jedoch nicht unbegrenzt
+     oft in kurzer Zeit. Hierbei sollte bedacht werden, dass nur auf Klo
+     gehen kann, wer auch vorher eine bestimmte Menge an Nahrung zu sich
+     genommen hat.
+
+     Speisen und Getraenke heilen in bestimmtem Mass. Dabei koennen sie
+     Lebens- oder Konzentrationspunkte auch unabhaengig voneinander
+     staerken. Ein jeder suche sich die Kneipe seiner Wahl.
+
+ SIEHE AUCH:
+     heilung, leben
+
+ LETZTE AeNDERUNG:
+     14.Maerz 2004 Gloinson
+
diff --git a/doc/help/faq b/doc/help/faq
new file mode 100644
index 0000000..e9c89f6
--- /dev/null
+++ b/doc/help/faq
@@ -0,0 +1,28 @@
+
+Die FAQ
+=======
+
+    Du hast Interesse an den FAQ? Gut so, eine kluge Entscheidung.
+
+    Zur Zeit findest Du die neueste Ausgabe der FAQ auf dem FTP Server des
+    Muds im Verzeichnis `/open/Texte/FAQ/'. Dieses Verzeichnis ist auch fuer
+    Spieler zugaenglich. Im einzelnen solltest Du folgendermassen vorgehen:
+
+    > ftp mg.mud.de
+    [Als Login-Namen 'anonymous' und als Passwort Deine EMail-Adresse angeben]
+    [Magier koennen auch Namen und Passwort ihres Charakters nehmen]
+    > cd /open/Texte/FAQ
+    > get mg-faq.v202
+
+    Eine andere Moeglichkeit ist, eine E-Mail an mud@mg.mud.de zu
+    schicken und als Subject `FAQ' anzugeben.
+
+    Hast Du das geschafft, bist Du im Besitz eines umfangreichen Werkes ueber
+    sehr viele Fragen, die im Zusammenhang mit dem Spiel auftauchen koennen.
+    Sollte Deiner Meinung nach etwas in den FAQ fehlen, so schreib einfach
+    eine Mail an Pilgrim oder Viper. Pilgrim ist als Autor des Werkes fuer
+    Anregungen sehr dankbar.
+
+LETZTE AeNDERUNG:
+    Sam, 29. Sep 2001, 11:35:01 von Zook.
+
diff --git a/doc/help/forscherpunkte b/doc/help/forscherpunkte
new file mode 100644
index 0000000..d12ddd9
--- /dev/null
+++ b/doc/help/forscherpunkte
@@ -0,0 +1,54 @@
+
+Forscherpunkte
+==============
+
+    Die Forscherpunkte geben Deinen aktuellen Grad der Kenntnis des
+    MorgenGrauens wieder. Sie werden allerdings nicht als nackte Zahl
+    praesentiert, sondern in Form einer Einschaetzung gemessen an der Kenntnis
+    Deiner Mitspieler sowie am gesamten MorgenGrauen.
+
+    Die Kenntnis waechst im Laufe der Zeit, wenn Du Dich einigermassen
+    sorgfaeltig umsiehst und mit NPCs unterhaeltst. Wer einfach nur stumpf das
+    absolut Noetigste eingibt, um ein Abenteuer zu loesen, darf sich also
+    nicht wundern, wenn er sich im MorgenGrauen trotzdem so gut wie gar nicht
+    auskennt.
+
+    Die Forscherpunkte machen einen grossen Teil der Stufenpunkte aus.
+
+    Wenn man die erste Huerde (`Du kennst Dich im MorgenGrauen so gut wie gar
+    nicht aus.') ueberwunden hat, sind folgende relativen und absoluten
+    Einstufungen moeglich:
+
+    Verglichen mit Deinen Mitspielern, kennst Du Dich im MorgenGrauen
+
+     * kaum aus.
+     * aeusserst schlecht aus.
+     * sehr schlecht aus.
+     * schlecht aus.
+     * unterdurchschnittlich aus.
+     * durchschnittlich gut aus.
+     * besser als der Durchschnitt aus.
+     * recht gut aus.
+     * ziemlich gut aus.
+     * gut aus.
+     * sehr gut aus.
+     * ausserordentlich gut aus.
+     * unheimlich gut aus.
+     * einfach hervorragend aus.
+
+    Absolut gesehen
+
+     * kennst Du nur wenig vom MorgenGrauen.
+     * solltest Du Dich vielleicht noch genauer umsehen.
+     * bist Du durchaus schon herumgekommen.
+     * hast Du schon einiges gesehen.
+     * bist Du schon weit herumgekommen.
+     * koenntest Du eigentlich einen Reisefuehrer herausbringen.
+     * hast Du schon sehr viel gesehen.
+     * besitzt Du eine hervorragende Ortskenntnis.
+
+ SIEHE AUCH:
+    stufenpunkte, info
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/help/gesellenstueck b/doc/help/gesellenstueck
new file mode 100644
index 0000000..ae980bb
--- /dev/null
+++ b/doc/help/gesellenstueck
@@ -0,0 +1,76 @@
+
+GESELLENSTUECK
+==============
+
+      Im MorgenGrauen musst Du als Jungmagier erst ein Gesellenstueck
+      abliefern, bevor Du von den zustaendigen Erzmagiern zum Vollmagier
+      befoerdert wirst. Bis dahin bist Du noch Lehrling und hast nur ein-
+      geschraenkte Leserechte.
+
+      Diese Einschraenkung der Leserechte soll Dich keinesfalls bei Deiner
+      Arbeit behindern - wenn Du Fragen zur Programmierung hast, die durch
+      die Beispiele im Verzeichnis '/doc' nicht beantwortet werden, so
+      kannst Du Dich jederzeit an Deinen Sponsor wenden, um weitere
+      Beispiel-Dateien zu bekommen.
+
+      Bei der Gestaltung Deines Gesellenstueckes kannst Du Deiner Phantasie
+      vollkommen freien Lauf lassen, Es gibt fuer das Gesellenstueck im MG 
+      keine festen Regeln wie "mindestens 20 Raeume und fuenf NPCs". Dein 
+      Gesellenstueck sollte jedoch etwas neues sein und keine Ueberarbeitung
+      von bestehendem Code, weil es LPC-Anfaengern erfahrungsgemaess
+      schwerer faellt, sich in fremden Code einzulesen. Ausserdem soll
+      ja klar erkennbar sein, dass das Neue tatsaechlich komplett von Dir
+      selbst stammt.
+
+      Ein kleiner Punkt sei als Denkanstoss erwaehnt: Wie waere es, wenn 
+      Du etwas fuer Anfaenger schreibst und nicht das x-te Super-Duper 
+      Seher-Team Metzelgebiet? Versuche ein Gebiet zu schreiben, an dem 
+      viele Spieler ihre Freude haben koennen und nicht nur die sehr hohen 
+      Seher. 
+
+      Genauso gut kannst Du eine neue Quest programmieren oder (gerade auch
+      in Teamarbeit) eine neue Gilde. Letzteres ist allerdings nicht fuer
+      LPC-Anfaenger zu empfehlen. Es muss nichtmal unbedingt ein "Standard-
+      Gesellenstueck" sein. 
+
+      Die Erzmagier sind auch anderen hier nicht aufgefuehrten Ideen immer 
+      aufgeschlossen gegenueber - sprich sie einfach einmal an und erzaehle,
+      was Du so an Vorstellungen davon hast, was Du im MorgenGrauen 
+      programmieren moechtest.        
+ 
+      Bevor Du Dich an das Programmieren begibst, solltest Du das Konzept
+      fuer Dein Gesellenstueck mit einem der zustaendigen Erzmagier
+      abklaeren, um sicherzugehen, dass es als Gesellenstueck ausreicht.
+      
+      Um Dein Gesellenstueck bewerten zu lassen, wendest Du Dich bitte an
+      einen der in der Hilfe als zustaendig angegebenen Erzmagier.
+      Du kannst auch gerne danach fragen, was von den anderen Jungmagiern 
+      bisher so an Gesellenstuecken abgegeben wurde, um Dir einen kleinen 
+      Ueberblick zu verschaffen, was ungefaehr von Dir erwartet wird.
+
+      Feste Grenzwerte vorzugeben, ab wann ein Gesellenstueck ausreichend 
+      ist, kann nicht im Sinne dieser Regelung sein. Daher gibt es an dieser
+      Stelle auch nur einige wenige grobe Richtlinien:
+
+      - ein Gesellenstueck sollte eine Bereicherung fuer das MorgenGrauen
+        sein - also kein lieblos hingeklatschtes Gebiet, bei dem man sofort
+        merkt, dass es nur zur Befoerderung zum Vollmagier taugen sollte.
+
+      - man sollte dem Gesellenstueck ansehen koennen, dass einiges an
+        Arbeit dahinter steckt, und dass es nicht "mal eben" an einem
+        Wochenende zusammengestrickt wurde.
+
+      - mittlerweile gibt es im MorgenGrauen gewisse Qualitaets-Standards,
+        was neue Gebiete angeht. Dazu gehoert u.a., dass die Raeume nicht
+        alle gleich aussehen (Copy&Paste laesst gruessen), gewisse
+        Standard-Details ueberall beschrieben sind etc. Dieser Standard gilt
+        selbstverstaendlich auch fuer Gesellenstuecke.
+
+      Bei weitergehenden Fragen wende Dich einfach an einen der in der Hilfe 
+      als Ansprechpartner angegebenen Erzmagier.
+
+ SIEHE AUCH:
+    erzmagier, regionsmagier, magier
+
+ LETZTE AeNDERUNG:
+    Mon, 28. AUG 2000, 22:30:00 von Wim.
diff --git a/doc/help/gift b/doc/help/gift
new file mode 100644
index 0000000..3699eae
--- /dev/null
+++ b/doc/help/gift
@@ -0,0 +1,28 @@
+Gift/Krankheit/Flueche
+======================
+
+    Es gibt im Morgengrauen verschiedene Gifte, Krankheiten und Flueche,
+    die einem das Leben schwerer machen.
+
+    Krankheiten und Gifte beeinflussen fast immer "nur" Lebens- und
+    Konzentrationspunkte. Flueche haben ganz andere oder weitergehende
+    Wirkungen.
+
+    Dabei nimmt man Gifte meist selbst (und oft freiwillig) auf,
+    Krankheiten und Flueche jedoch verpasst einem irgendwer oder
+    irgendwas ohne einen zu fragen.
+
+    Die generelle Vergiftung wird einem durch Meldungen und die Anzeige
+    von "info" bekannt gemacht. Die Wirkung ist, das man nicht mehr
+    ohne Tanken heilt bis hin zum Lebenspunktverlust. Einige Heiler und
+    Gilden koennen die Vergiftung lindern und aufheben.
+
+    Der einfache Fluch wirkt auf Waffen und Ruestungen. Diese sind dann
+    nicht mehr wegsteckbar bzw. ausziehbar. Auch hier koennen Heiler und
+    Gilden helfen.
+
+ SIEHE AUCH:
+    sterben
+
+ LETZTE AeNDERUNG:
+    18. Maerz 2004 Gloinson
diff --git a/doc/help/gilden b/doc/help/gilden
new file mode 100644
index 0000000..947f7a3
--- /dev/null
+++ b/doc/help/gilden
@@ -0,0 +1,55 @@
+Die Gilden
+==========
+
+    Es gibt im Morgengrauen mehrere Gilden, in denen man einige besondere
+    Faehigkeiten erwerben kann. Man kann jedoch nur in einer Gilde
+    gleichzeitig sein und hat auch immer nur die Faehigkeiten der Gilde, in
+    der man momentan ist. Wer mit seiner Gilde nicht zufrieden ist, kann
+    natuerlich auch jederzeit wieder austreten, hat dann, falls er erneut
+    eintritt, jedoch nur noch einen Teil der frueher erworbenen Faehigkeiten.
+
+    Jeder ist beim Start schon in einer Gilde: Menschen, Hobbits und
+    Zwerge in der Abenteurergilde, Felinen bei den Katzenkriegern, Elfen in 
+    der Gilde der Wipfellaeufer, Orks in der Gilde der Urukhai und 
+    Dunkelelfen in der Dunkelelfengilde. 
+
+    Die Faehigkeiten seiner Anfangsgilde verliert man nicht, wenn man in 
+    einer anderen Gilde war und dort ausgetreten ist.
+
+    Dunkelelfen sind bei der Auswahl stark eingeschraenkt, da sie in den
+    meisten Gilden nicht akzeptiert werden und auch nur ihre Gilde ihnen
+    den lebensnotwendigen Schutz bietet, jedoch koennen sie sich auf
+    mehrere Zweige spezialisieren.
+
+    Momentan gibt es folgende Gilden:
+
+     * Abenteurergilde, in Port Vain, von Boing
+     * Wipfellaeufer, im Elfendorf, von Humni
+     * Gilde der Karatekaempfer, in der Stadt Xian im Fernen Westen, von
+       Rochus
+     * Krieger des Chaos, in der Eiswueste, von Zardoz
+     * Akademie der geistigen Kraft zu Taramis, von Silvana
+     * Der Heilige Orden, in Djelibeby, von Wargon
+     * Bierschuettlergilde, auf Shaky Island, von Shakedbeer
+     * Kaempfergilde, auf Akhar Nth'tar, von Zardoz, Toeter und Bendeigid
+     * Die Katzenkrieger, in der Dschungelstadt Katzmandu, von Paracelsus
+     * Die Tanjian, im Eistannenwald, von Paracelsus
+     * Dunkelelfen, in der Hoehlenstadt Zzerenzzen, von Padreic
+     * Urukhai, von Morgoth und Bugfix
+     * Werwoelfe, von Humni
+
+    Folgende Gilden sind derzeit noch in Entwicklung, der Zeitpunkt ihrer
+    Fertigstellung ist aber noch ungewiss:
+
+     * Magusgilde, von Ark
+ 
+ SIEHE AUCH:
+    abenteurer, bierschuettler, chaos, kaempfer, karate, katzenkrieger,
+    klerus, zauberer, tanjian, dunkelelfen, waffenfertigkeiten,
+    gildenmagier, wipfellaeufer, altgilden
+ 
+ LETZTE AENDERUNG: 
+    2011-11-13 von Humni
+
+
+----------------------------------------------------------------------
diff --git a/doc/help/gildenmagier b/doc/help/gildenmagier
new file mode 100644
index 0000000..cad3f7a
--- /dev/null
+++ b/doc/help/gildenmagier
@@ -0,0 +1,37 @@
+
+Die Gildenmagier des MorgenGrauens
+==================================
+
+Jede Gilde wird von ein oder mehreren Magiern betreut, die die Gilde warten
+und erweitern. Sie sind die Ansprechpartner, wenn es Probleme innerhalb
+einer dieser Gilden gibt.
+
+Hier also alle Gilden des Morgengrauens mit ihren Gildenmagiern:
+(MA - Mitarbeiter)
+
+(Einige der Gildenmagier sind derzeit ein wenig inaktiver als sonst.
+Diese sind mit einem ,,i'' gekennzeichnet. In diesem Fall gibt es derzeit
+niemanden, der die Gilde weiterentwickelt. Fuer dringende Fragen kannst Du
+Dich dann an den Erzmagier fuer Gilden, derzeit Humni, wenden.)
+
+  Abenteurergilde               - Boing
+  Gilde der Karatekaempfer      - Humni
+  Krieger des Chaos             - Ennox (i), Mupfel (i), Bugfix
+  Akademie der geistigen Kraft  - Chagall
+  Der Heilige Orden             - Wargon (i), Gloinson
+  Bierschuettlergilde           - Bielefeld (i), Shakedbeer(i), Raschaua (i)
+  Kaempfergilde                 - Gabylon
+  Die Katzenkrieger             - Bambi
+  Die Tanjian                   - Chagall, Nibel (i)
+  Dunkelelfen                   - Amaryllis(*), Padreic (i)
+  Uruk-Hai                      - Bugfix, Rhovan, Morgoth (i)
+  Wipfellaeufer                 - Humni
+  Werwoelfe                     - Humni
+
+(*) Amaryllis hat fuer die Gilde derzeit wenig Zeit.
+(i) Dieser Magier ist zur Zeit inaktiv.
+
+SIEHE AUCH:
+     Gilden, Magier, Regionen, Regionsmagier, Erzmagier
+
+Fre, 05. Juni 2015, 22:52:00 von Boing
diff --git a/doc/help/gott b/doc/help/gott
new file mode 100644
index 0000000..25c88d5
--- /dev/null
+++ b/doc/help/gott
@@ -0,0 +1,26 @@
+
+Goetter im Morgengrauen
+=======================
+
+ BESCHREIBUNG:
+     Es gibt einige, wenige Goetter im Morgengrauen. Wie bei allen Goettern
+     gilt es in der Anbetung des Jof, Rumata und Zook erstens jeweils den
+     richtigen Ansprechpartner und zweitens die richtige Balance zwischen
+     Schleimerei und Gottesbelaestigung zu finden. Kleine Opfergaben wie
+     Bierreichungen auf Parties sind natuerlich gern gesehen.
+
+     Unsere Goetter zogen ein schweres Los - wo die Erzmagier Arbeit
+     erledigen, tragen die Goetter die schwere Last der Verantwortung. Sie
+     muessen dafuer sorgen, dass das rechte Mass an Neid, Missgunst, Gier und
+     Verstimmung zwischen Spielern, Sehern, Magiern, Erzmagiern und sonstigen
+     Spielteilnehmern immer gewahrt bleibt.
+
+ BEMERKUNGEN:
+     Es gibt einen zurueckgetretenen Gott, Boing. Dieser wird immer noch
+     von einigen Unglaeubigen in blutigen Ritualen in der SSP verehrt.
+
+ SIEHE AUCH:
+        erzmagier, willkuer, parties
+
+ LETZTE AeNDERUNG:
+    22. November 2004 Muadib
diff --git a/doc/help/hardcore b/doc/help/hardcore
new file mode 100644
index 0000000..0ac2b98
--- /dev/null
+++ b/doc/help/hardcore
@@ -0,0 +1,29 @@
+Der Hardcoremodus
+======================
+
+    Entscheidet sich der Spieler fuer nur ein einziges Leben, so 
+    spielt er im Hardcoremodus.
+
+    Dies bedeutet, dass der Spieler nach seinem ersten Tod nicht mehr
+    aktiv am Spiel teilnehmen kann. Lediglich kommunikative Mittel wie
+    das Rufen oder die Ebenen stehen ihm noch zur Verfuegung.
+
+    Dieser Modus sollte nur von erfahrenen Spielern gewaehlt werden.
+
+    Die Hardcorespieler unterliegen den Regeln wie ganz normale 
+    Spieler.
+
+    Wenn ein Spieler wuenscht, im Hardcoremodus zu spielen, kann er/sie vor
+    Erreichen der zweiten Stufe (d.h. bevor man das erste Mal erhoehen kann)
+    und vor dem ersten Tod einen Erzmagier ansprechen, welcher den HC-Modus
+    aktiviert. Dies ist endgueltig und wird nicht rueckgaengig gemacht.
+
+    Eine Warnung: Ein gestorbener Spieler wird unter gar keinen Umstaenden
+    wieder in das Leben zurueckgeholt.
+
+ SIEHE AUCH:
+    rufe, ebenen, tod, regeln 
+
+ LETZTE AeNDERUNG:
+    22.8.2011, Zesstra
+
diff --git a/doc/help/heilung b/doc/help/heilung
new file mode 100644
index 0000000..f29f8eb
--- /dev/null
+++ b/doc/help/heilung
@@ -0,0 +1,40 @@
+
+Die Heilung
+===========
+
+     Im Kampf oder durch Gift verliert man mehr Lebenspunkte als einem
+     lieb sein kann. Zauberei verbraucht zusaetzlich Konzentration.
+
+     Es gibt mehrere Wege um sich zu heilen:
+
+      Mit Geduld: Im Herzschlag heilt ein Spieler - so er nicht vergiftet
+                  ist - langsam sich selbst. Das ist immer auch von der
+                  Umgebung und der Rasse des Spielers abhaengig.
+
+      In Kneipen: Man kann sich durch Verzehr von Speisen oder Getraenken
+                  rascher selbst heilen. Dabei wird einem diese Heilung
+                  innerhalb des Herzschlages langsam zuteil.
+                  Wichtig ist hierbei vor allem, dass man nur soviel
+                  Heilungspotential mit einem Mal essen oder trinken kann,
+                  wie man maximale Lebenspunkte/Konzentrationspunkte hat.
+                  Besitzt man also maximal 90 Lebenspunkte, so kann man in
+                  einem Moment nur fuer 90 Lebenspunkte essen oder trinken,
+                  alle darueberhinausgehende Heilung der Speise geht
+                  verloren.
+                  Bei aktuellen 20 LP waere es also sinnvoll, zwei Getraenke
+                  a 35 Lebenspunkte zu sich zu nehmen. Vor einem Kampf kann
+                  man sich dadurch uebrigens einen zusaetzlichen Puffer
+                  verschaffen, indem man 2 Getraenke a 35 und 1 Getraenk a
+                  20 Lebenspunkte zu sich nimmt. Die 20 LP werden einem dann
+                  bei eventuellem LP-Verlust im Kampf wieder zugefuehrt.
+
+      Mit Magie:  Viele Orte oder auch Gildenangehoerige bestimmter Gilden
+                  besitzen spezielle Magie, die einem ebenfalls zu Heilung
+                  verhelfen kann. Diese Heilung ist entweder sofortig oder
+                  unterliegt den gleichen Regeln wie die Heilung in Kneipen.
+
+ SIEHE AUCH:
+     tod, leben, essen, kampf, report
+
+ LETZTE AeNDERUNG:
+     14. Maerz 2004 Gloinson
diff --git a/doc/help/irc b/doc/help/irc
new file mode 100644
index 0000000..61e8a71
--- /dev/null
+++ b/doc/help/irc
@@ -0,0 +1,23 @@
+IRC

+===

+

+     Der IRC-Kanal des Morgengrauens (-#mg) ist von aussen wie folgt

+     zu erreichen:

+     

+     Server:	irc.freenet.de

+		irc.fu-berlin.de

+		- siehe auch http://irc.fu-berlin.de/ircmap.html

+     Kanal:	mg.de

+

+     Befehle:   Um aus dem IRC zu sehen, welche Spieler den IRC-

+                Kanal des MG nutzen:

+                /msg [MG] who #mg.de

+

+                Um vom MG heraus die Liste der ICR-User zu sehen:

+                -#MG @who

+							

+ SIEHE AUCH:

+     kanaele, www

+

+ LETZTE AeNDERUNG:

+    30. Maerz 2006 Gloinson

diff --git a/doc/help/kaempfer b/doc/help/kaempfer
new file mode 100644
index 0000000..1e3a2f0
--- /dev/null
+++ b/doc/help/kaempfer
@@ -0,0 +1,79 @@
+
+Die Kaempfergilde
+-----------------
+
+    Die Kaempfergilde im MorgenGrauen ist dem Bund der Trves angeschlossen.
+    Sie hat ihren Hauptsitz im Koru-Tschakar-Struv, welches im Nordosten
+    der Insel "Akhar Nth'Tar" gelegen ist.
+
+    Man erlernt dort den Umgang mit den verschiedenen Waffentypen, wobei die
+    maximale Faehigkeit abhaengig von der Rasse ist. Es gibt:
+
+     * Messer und Dolche,
+     * Schwerter,
+     * Kampfstaebe,
+     * Aexte und Beile,
+     * stumpfe Hiebwaffen und
+     * Speere bzw. Stangenwaffen.
+
+    Weiterhin gibt es noch den allgemeinen Umgang mit Zweihandwaffen.
+
+    Es gibt verschiedene Kampftaktiken bzw. Techniken, die man im Kampf je
+    nach Gegner waehlen kann:
+
+     * Allgemeine Taktik (Verhaeltnis von Offensive zu Defensive),
+     * die Raserei und
+     * die Technik der Schildkroete, Drache oder Schlange
+
+    Besondere Aktionen im Kampf sind z.B.:
+
+     * das Schnelle Kaempfen,
+     * das Unterlaufen der Waffe des Gegners,
+     * die Finte und
+     * der Kampfwille.
+
+    Es gibt eine Reihe von zusaetzlichen Angriffen, die man machen kann,
+    wie z.B.:
+
+     * Kampftritt,
+     * Kniestoss und
+     * Todesstoss.
+
+    Ausserdem kann man einen Waffenbruch und einen KO-Schlag anbringen.
+
+    Es gibt natuerlich auch defensive Kampfmittel:
+    Die Parade und die Schildparade. Recht anspruchsvoll ist das Ausweichen
+    von Zauberspruechen.
+
+    Techniken, die im Gruppenkampf nuetzlich sind:
+
+     * Rueckendeckung,
+     * das Blockieren der Waffe des Gegners und
+     * das Beschimpfen des Gegners.
+
+    Zusaetzlich gibt es noch:
+
+     * das Waffenschaerfen,
+     * das Einschaetzen von Monstern, Waffen und Ruestungen,
+     * das Identifizieren von Dingen,
+     * den Kampfsprung,
+     * das Saufen und
+     * das Tragen von schweren Lasten.
+
+ Insgesamt gibt es 41 verschiedene Faehigkeiten zu erlernen.
+ Darueberhinaus gilt es, auf seinem Weg durch die verschiedenen Gildenlevel
+ (zur Zeit bis GL 24), immer einige der 38 Aufgaben, die das Struv fuer
+ einen bereithaelt, zu erledigen.
+
+ Wer sich noch mehr Informationen verschaffen moechte, schaut einfach mal
+ auf http://www.trves.de vorbei.
+
+
+ SIEHE AUCH:
+    attribute, ebenen, gilden
+
+    *Ist man Mitglied einer anderen Gilde, so muss man fuer die Hilfen zu den
+    Spruechen `hilfe gilde kaempfer <name>' benutzen!*
+
+ LETZTE AeNDERUNG:
+    Thu, 17.01.2008, 14:00:00 von Gabylon
diff --git a/doc/help/kampf b/doc/help/kampf
new file mode 100644
index 0000000..991e545
--- /dev/null
+++ b/doc/help/kampf
@@ -0,0 +1,32 @@
+
+Kaempfen
+========
+
+     Kaempfen, auch allgemein als Metzeln bekannt ist eine der Lieblings-
+     sportarten diverser Gilden und aktiver Spieler. Dabei sucht man sich
+     einen moeglichst interessant ausgestatteten NPC, bringt ihn um und
+     benutzt dessen ehemaliges Hab und Gut um einen weiteren NPC zu toeten.
+     Das ist beliebig iterierbar.
+
+     Ein Kampf ist in Kampfrunden aufgeteilt, die durch den Herzschlag
+     bestimmt sind. Benutzt man eine Waffe oder hat Haende frei, so greift
+     man mit diesen einen Gegner an, dessen Haut und Ruestung schuetzt
+     davor. Dann greifen der oder die Gegner an.
+
+     In jeder Kampfrunde kann man zusaetzlich mit Zauberei oder Objekten
+     Schaden verursachen oder den Kampf beeinflussen. Wie oft das moeglich
+     ist, haengt von Seherstatus, (Seher)level und eigener Geschwindigkeit
+     ab.
+
+     Gerade schwierigere Monster koennen nur im Team besiegt werden. In
+     der Parallelwelt, die nur fuer Seher erreichbar ist, sind die Monster
+     manchmal auch noch etwas staerker oder hinterhaeltiger.
+
+ SIEHE AUCH:
+     Verwandt: toete, stop, report, team
+     Anderes:  heilung, leben, tod 
+               zauberei, waffenfertigkeiten
+               balance, abenteuer
+
+ LETZTE AeNDERUNG:
+    14. Maerz 2004 Gloinson
diff --git a/doc/help/karate b/doc/help/karate
new file mode 100644
index 0000000..5b1216b
--- /dev/null
+++ b/doc/help/karate
@@ -0,0 +1,72 @@
+
+Die Gilde der Karatekaempfer
+----------------------------
+
+    Man kann in der Gilde der Karatekaempfer bei Sensei Funakoshi Karate
+    lernen. Wenn man in der Gilde eingetreten ist, sollte man ihn nach den
+    Anforderungen fragen - er sagt einem dann, welche Karatetechniken man
+    lernen und ueben muss, um jeweils den naechsten Grad zu erreichen.
+
+    Es wird jedoch einige Zeit dauern, bis man die verschiedenen Techniken gut
+    genug kann. Man kann in der Gilde jede Technik einzeln ueben, bei den
+    Abwehrtechniken ist es jedoch ratsam, diese mit `uebe abwehr' gegen den
+    Sensei zu ueben - dabei muss man auf jeden Angriff mit einer Abwehrtechnik
+    reagieren. Natuerlich muss es die richtige Abwehr sein - einen Age-Zuki
+    (Fauststoss nach oben, im Kopfbereich) kann man nun einmal nicht mit einem
+    Gedan-Barai (Abwehr nach unten, gegen Schlaege unterhalb der Guertellinie)
+    abwehren. Anfangs wird man auch noch nicht jeden Angriff abwehren koennen.
+    Beim Training sollte man uebrigens nicht zuviel Krempel mit sich
+    herumschleppen - immerhin muss man sich bei Karate viel bewegen.
+
+    Im Kampf wendet man die erlernten Techniken uebrigens automatisch an. Auch
+    dadurch lernt man einiges hinzu. Schwere Ruestungen schraenken allerdings
+    die Beweglichkeit stark ein - man sollte sich also auf Gi und Guertel
+    beschraenken.
+
+    Es werden beim Kampf uebrigens nur die Ruestungen im getroffenen
+    Koerperbereich beim Gegner beruecksichtigt - wenn jemand in einem Bereich
+    schlecht geschuetzt ist, sollte man sich mit `angriff mit xxx, yyy und
+    zzz' auf die Angriffstechniken im richtigen Zielbereich beschraenken. Wer
+    einen hohen Grad hat, waehlt uebrigens die richtige Technik automatisch
+    etwas geschickter...
+
+    Es ist auch wichtig, wie gross der Gegner ist - ein Elf sollte nicht
+    versuchen, einen Zwerg mit einem Age-Zuki anzugreifen, weil er dann
+    Loecher in die Luft ueber ihm schlaegt.
+
+    Von den Angriffstechniken sind Tritte am effektivsten, danach kommen
+    Handkantenschlaege, dann Ellbogenschlaege und Knietritte, dann
+    Faustschlaege und zuletzt Fauststoesse.
+
+    Man kann mit zwei Haenden im Prinzip besser verteidigen als mit einer -
+    jedoch ist eine darauf folgende Handtechnik immer etwas schlechter. Auch
+    zwei Fusstechniken kurz hintereinander sind nicht empfehlenswert.
+
+    Die Erfolgswahrscheinlichkeit einer Technik haengt nicht nur vom Koennen
+    ab, sondern auch von der geistigen Konzentration, also den Magiepunkten.
+    Wenn diese nachlassen, sinkt die Wahrscheinlichkeit, bei schwierigen
+    Techniken frueher und bei leichten spaeter. Je hoeher jedoch der Grad ist,
+    desto langsamer sinkt die Wahrscheinlichkeit dann.
+
+    Die maximale Wirkung eines misslungenen Schlages ist schlechter als die
+    maximale Wirkung eines gelungenen Schlages - bei einfachen Schlaegen halb
+    so gut, bei schwierigen noch weniger. Je hoeher der Grad ist, desto hoeher
+    ist die garantierte Mindestwirkung eines gelungenen Schlages. Bei hohen
+    Graden kann es auch geschehen, dass ein Schlag besonders gut gelingt - bei
+    voller Konzentration wird die Wirkung dadurch fast verdoppelt. Handschuhe
+    bzw. Schuhe daempfen natuerlich einen Schlag bzw. Tritt.
+
+    Die Verteidigung ist auch noch von der Art des Angriffs abhaengig - ein
+    Karateka kann Angriffe mit stumpfen und spitzen Waffen gut abwehren,
+    Schwerter und aehnliche Waffen auch noch recht gut, aber etwas schlechter.
+    Gegen Magie, Feuer und Aehnliches sieht ein Karateka anfangs recht alt
+    aus, weil er dies nicht abwehren kann - wenn er einen hohen Grad hat
+    gelingt, es ihm aber oefter, einem solchen Angriff auszuweichen, jedoch
+    nur, wenn er nicht zuviel Gewicht dafuer bewegen muss.
+
+ SIEHE AUCH:
+    abwehr, angriff, konzentration, nemoku-owaru,
+    attribute, ebenen, gilden, daempfung
+
+    *Ist man Mitglied einer anderen Gilde, so muss man fuer die Hilfen zu den
+    Kommandos `hilfe gilde karate <name>' benutzen!*
diff --git a/doc/help/katzenkrieger b/doc/help/katzenkrieger
new file mode 100644
index 0000000..a70d42b
--- /dev/null
+++ b/doc/help/katzenkrieger
@@ -0,0 +1,13 @@
+KATZENKRIEGER:
+
+        Als die Dschungelstadt  Katzmandu ihre Tore fuer  die Welt oeffnete,
+        lernte die Welt nicht nur die Felinen kennen, sondern auch die Gilde
+        der  Katzenkrieger.  Die Mitglieder  dieser  Gilde  verfuegen  ueber
+        verschiedene Zauber und  Faehigkeiten,  deren Ursprung im taeglichen
+        Kampf um das Ueberleben im Dschungel zu suchen ist.
+
+        Naehere  Informationen  zu  dieser  Gilde findest  Du in der grossen
+        Bibliothek von Katzmandu.
+
+SIEHE AUCH:
+	attribute, gilden, kanaele
diff --git a/doc/help/klerus b/doc/help/klerus
new file mode 100644
index 0000000..0fb8693
--- /dev/null
+++ b/doc/help/klerus
@@ -0,0 +1,72 @@
+
+Die Klerikergilde
+-----------------
+
+    Obwohl der Stammtempel des Heiligen Ordens sich auf der Insel am Ende der
+    Welt befindet, spielt sich der fuer die Spieler relevante Teil im Tempel
+    in der Stadt Djelibeby ab. Die Begruendung kann man im 'Buch der
+    Grundsaetze' nachlesen.
+
+    Praktisch bedeutet dies, dass so gut wie alle Aktionen, wie der Ein- oder
+    Austritt, das Erlernen von Spruechen (bei den Klerikern Anrufungen
+    genannt) und das Erhoehen der Stufe nur im Tempel in Djelibeby verfuegbar
+    sind. Nur fuer die Priesterweihe und den Aufstieg zum/zur Erleuchteten
+    muss man den Stammtempel aufsuchen. Der Besuch des Stammtempels lohnt sich
+    jedoch auch, wenn man sich genauer ueber die Ordensgeschichte informieren
+    will, da dort die wichtigsten Schriftstuecke zu finden sind.
+
+ INFORMATIONEN:
+    Um sich ueber den Orden zu informieren, sollte man im Tempel folgende
+    Buecher lesen:
+
+     Das Buch der Anrufungen
+        hier stehen Informationen zu den einzelnen Anrufungen
+
+     Das Buch der Grundsaetze
+     Das Buch des zweiten Exodus
+        ein paar historische Informationen
+
+     Das Buch der Hierarchie
+        die einzelnen Ordensstufen mit Anforderungen und Faehigkeiten
+
+ ZUM EINTRITT:
+    Fuer den allerersten Eintritt in den Orden benoetigt man einen
+    Fuersprecher. Dieser muss selbst Mitglied des Heiligen Ordens und in der
+    Ordenshierarchie schon zum Wanderprediger aufgestiegen sein. Wenn man (aus
+    welchen Gruenden auch immer) aus dem Heiligen Orden austritt und dann doch
+    wieder eintreten will, ist ein Fuersprecher nicht mehr noetig.
+
+    Weitere Eintrittsbedingung ist, dass man einen heiligen Charakter
+    aufweist.
+
+ ZU DEN ANRUFUNGSKOSTEN:
+    Bei den Kosten, die auf den Hilfeseiten angegeben sind, handelt es sich
+    nur um Grundwerte. Diese Werte variieren abhaengig vom Charakter und der
+    Glaubensstaerke. Ein frecher, boeser oder gar satanischer Charakter zieht
+    hoehere Kosten nach sich, waehrend es bei nettem, gutem oder heiligem
+    Charakter zu einer Kostensenkung kommt.
+
+    Ausserdem werden bei der Weihe zum Priester die Kosten der Anrufungen der
+    Gottheit, der man sich geweiht hat, generell um 20% reduziert, waehrend
+    die Kosten fuer die Anrufungen der anderen Goetter um 20% steigen.
+
+    Die genauen Kosten zu gegebenem Charakter und Glaubensstaerke kann man
+    immer im Buch der Anrufungen nachschlagen.
+
+ SONSTIGES:
+    In den Tempeln steht den Ordensmitgliedern der Befehl `tempeltransport'
+    zur Verfuegung, der den schnellen Wechsel zwischen beiden Tempeln erlaubt.
+
+ SIEHE AUCH:
+    gilden,
+    bete, entfrosche, identifiziere, lebenskraft, sonnenschutz, donner,
+    entfluche, kuriere, laeutere, blitz, heile, schaetz, segne,
+    elementarsphaere, entgifte, leuchten, traue, weihe, elementarschild,
+    heiligenschein, messerkreis, regeneriere, spaltung, beistand, heiltrank,
+    schutzhand, wunder
+
+    *Nicht-Kleriker muessen fuer die Hilfen zu den Anrufungen `hilfe gilde
+    klerus <name>' benutzen!*
+
+ LETZTE AeNDERUNG:
+    Fri, 05.09.1997, 08:47:08 von Wargon
diff --git a/doc/help/konzept b/doc/help/konzept
new file mode 100644
index 0000000..5b84130
--- /dev/null
+++ b/doc/help/konzept
@@ -0,0 +1,58 @@
+
+Die Idee hinter diesem Spiel
+============================
+
+    Dies sind die (etwas technisch gehaltenen) Grundkonzepte des
+    MorgenGrauens, bzw. von LPMuds an sich:
+
+     1. Ein Magier kann das Spiel erweitern.
+
+     2. Spielerweiterungen koennen am laufenden Spiel vorgenommen werden.
+
+     3. Alles im Spiel besteht aus Objekten. Raeume, Spieler, Monster und
+        Schaetze sind alles Objekte.
+
+     4. Alle Objekte werden in einem C-Dialekt (LPC) geschrieben. Die
+        Objekte werden bei Bedarf geladen und halbinterpretiert.
+
+     5. Es gibt keinen Parser. Alle Kommandos werden von den Objekten
+        definiert. Ein Messer definiert zum Beispiel das Kommando `schneide'
+        und eine Lederjacke das Kommando `trage'.
+
+        Ein Objekt definiert ein Kommando, indem es sie mit einer im Objekt
+        implementierten Funktion verknuepft. Wenn der Spieler das Kommando
+        eingibt, so wird die zugehoerige Funktion im Objekt aufgerufen. Wenn
+        der Spieler `trage jacke' eingibt, so wird `jacke' als Argument an
+        diese Funktion uebergeben. Wenn der Spieler `trage schild' eingibt, so
+        erkennt die Funktion, dass `jacke' != `schild' ist, und gibt den Wert
+        fuer Misserfolg (0) zurueck. Dann wird ein anderes `trage'-Kommando
+        ausprobiert, bis ein Passendes gefunden wird.
+
+        Bewegt der Spieler die Jacke aus seinem Inventory oder seinem
+        Environment - sprich seinem Einflussbereich so werden alle Kommandos,
+        die zur Jacke gehoeren geloescht.
+
+     6. Raeume sind Objekte, die Kommandos wie `osten' oder `kletter' und
+        aehnliches definieren. Wenn der Spieler `osten' eingibt, so wird die
+        zugehoerige Funktion etwas mit dem Spieler tun.
+
+     7. Ein Objekt kann eine Funktion namens heart_beat() definieren. Diese
+        Funktion wird alle zwei Sekunden aufgerufen. Diese Funktion kann fuer
+        selbststaendig agierende Monster, verloeschende Fackeln oder
+        verzoegerte Fallen eingesetzt werden.
+
+        Der Phantasie der Magier sind keine Grenzen gesetzt.
+        (Der Effizienz dieser Funktion schon!)
+
+     8. Das komplizierteste Objekt ist das Spielerobjekt. Es definiert
+        Kommandos wie `nimm', `laechel', `schau' oder `toete'.
+
+     9. Wenn ein Spieler Magier wird, so wird er einer Region zugeteilt.
+        Innerhalb dieser Region darf er dann das Spiel erweitern. Er kann
+        Schloesser bauen, oder eine Hoehle ...
+
+    10. Fuer Magier, die Objekte schreiben, stehen ein ed-kompatibler Editor
+        und ein UNIX-aehnliches Filesystem zur Verfuegung.
+
+ LETZTE AeNDERUNG:
+    14. Maerz 2004 Gloinson
diff --git a/doc/help/lag b/doc/help/lag
new file mode 100644
index 0000000..227a5a1
--- /dev/null
+++ b/doc/help/lag
@@ -0,0 +1,41 @@
+Das LAG
+=======
+
+    Das LAG ist das groesste und fieseste Monster des Morgengrauen. Selbst
+    Magier und Goetter sind machtlos. Es tritt auf den Plan, wenn ein Magier
+    mal wieder Unsinn fabriziert, ein Seher das eigene Haus bis zum
+    Dachspanten vollgemuellt oder ein sehr beliebter Mitspieler ein
+    "mail freunde" abgesetzt hat.
+
+    Ganz technisch betrachtet ist der Rechner ueberlastet und kann den
+    Mud-Prozess nicht mit gewohnter Geschwindigkeit ausfuehren. Dadurch
+    haengt dann natuerlich alles - der Vorteil jedoch ist, dass man im
+    Normalfall an Lag nicht stirbt, da auch alle Monster unter Zeitmangel
+    leiden.
+    Dahingegen ist das kleine Monster Netzlag hinterhaeltiger. Meist reicht
+    es aber, den Nachbarn vom Netzkabel herunterzuschubsen um es zu
+    vertreiben.
+
+
+    Es gibt einige Spielzeuge, mit denen man sich das Lag in Zahlen
+    anzeigen lassen kann. Am beliebtesten ist jedoch der Kanal <MasteR>:
+
+    [<MasteR>:Gloinson] l
+    [<MasteR>:<MasteR>] 7.80 cmds/s, 127.97 comp lines/s
+    [<MasteR>:<MasteR>] Lag: 0.5%/60, 0.8%/15, 1.2/5, 0.0%/1
+
+    Dabei bedeuten die Zahlen, um wieviel Prozent im Vergleich zur normalen
+    Zeit das MUD in den letzten 60/15/5/1 Minuten im Durchschnitt langsamer
+    war.
+    
+    Hierbei ist zu beachten, dass selbst der 1-Minutenwert nur alle 10s
+    berechnet wird. Tritt also gerade jetzt ein Lag auf und ihr fragt sofort
+    das Lag ab, ist es wahrscheinlich, dass es noch nicht im 1-Minutenwert
+    enthalten ist. Die anderen Werte werden entsprechend seltener neu
+    bestimmt.
+
+ SIEHE AUCH:
+    mudrechner, sterben, ebenen
+
+ LETZTE AeNDERUNG:
+    04.09.2010, Gloinson
diff --git a/doc/help/leben b/doc/help/leben
new file mode 100644
index 0000000..883b399
--- /dev/null
+++ b/doc/help/leben
@@ -0,0 +1,23 @@
+
+Leben und Lebenspunkte
+======================
+
+     Ein jeder geniesst sein (virtuelles) Leben so gut er kann, oft wird
+     gemetzelt, manchmal gequestet und noch haeufiger geidelt.
+
+     Das Leben wird bestimmt durch Lebenspunkte und Konzentrationspunkte,
+     wobei erstere ueber den Tod entscheiden und zweitere einen Einfluss
+     auf die Zauberei haben. Sinken die Lebenspunkte durch Kampf oder Gift
+     auf unter 0, stirbt man (fast immer). Die Anzahl der Lebenspunkte
+     haengt vom Attribut Ausdauer ab, kann aber auch durch besondere
+     Zaubereien oder Ausruestung erhoeht werden.
+
+     Jeder Spieler hat einen Herzschlag. Diesen spuert er im Kampf oder
+     nach dem Kampf, denn dieser bestimmt Kampfrunden (und die darin
+     moeglichen Aktionen) und die Heilung.
+
+ SIEHE AUCH:
+     tod, heilung, kampf, report
+
+ LETZTE AeNDERUNG:
+     4. Nov 2011 Gloinson
diff --git a/doc/help/leitfaden b/doc/help/leitfaden
new file mode 100644
index 0000000..e0eb5d2
--- /dev/null
+++ b/doc/help/leitfaden
@@ -0,0 +1,228 @@
+Ein kleiner Leitfaden zum Spiel im MorgenGrauen
+===================================================
+
+  Das MorgenGrauen ist ein Spiel. Wie bei jedem Spiel muessen, um den
+  Spielspass moeglichst fuer alle zu erhalten, ein paar wenige Regeln
+  beachtet werden. Diese Regeln gelten sowohl fuer Spieler als auch
+  fuer Magier.
+
+                                *
+
+    Die wichtigste Regel betrifft das Angreifen und Toeten anderer
+    Spieler.
+
+    ES IST STRIKT UNTERSAGT ANDERE SPIELER ANZUGREIFEN ODER ZU TOETEN.
+
+    Auch das absichtliche indirekte Toeten von Spielern durch
+    Gildenfaehigkeiten, spezielle Objekte oder aehnliches faellt unter
+    diese Regel.
+
+    Sollte man einen Spieler umgebracht haben, treten sofort
+    Beschraenkungen in Kraft, die nur durch einen Erzmagier wieder
+    aufgehoben werden koennen.
+
+    Will man sich dennoch mit anderen Spielern messen, so existieren
+    extra dafuer eingerichtete Gebiete, wie zum Beispiel die Arena in
+    der Wueste, in denen man sich gegenseitig bekaempfen kann.
+
+                                *
+
+    Hat man mit seinem Charakter eine Zeit lang gespielt, so entsteht
+    oft der Wunsch, einmal eine andere Rasse, Gilde oder einfach einen
+    anderen Rollenspielcharakter zu spielen.
+    Seinen bisherigen Charakter kann oder moechte man dazu allerdings
+    nicht benutzen.
+    Daher kann jeder Spieler weitere Charaktere anlegen.
+
+    Diese muessen dann jedoch als sogenannter Zweitspieler des ersten
+    Charakters markiert werden.
+    Diese Markierung kann man zum Beispiel beim Schmied in der
+    Zwergenstadt oder beim Dorfschreiber im Elfendorf erhalten.
+    Diese Markierung kann man auch vor anderen Spielern verbergen.
+
+    Man kann beliebig viele seiner Charaktere gleichzeitig einloggen,
+    doch ist hierbei darauf zu achten, dass nur zwei dieser Charaktere
+    aktiv am Spielgeschehen, das ist alles was ueber reine
+    Kommunikation hinausgeht, teilnehmen.
+    Die anderen Charaktere muessen sich auf die Kommunikation
+    beschraenken.
+
+    Weitere Einschraenkungen existieren fuer Magier.
+
+    Ein Magier darf nicht gleichzeitig mit seinen Spielercharakteren
+    eingeloggt sein, diese manipulieren oder fuer seine Zweitspieler
+    fremden Code analysieren.
+
+    Der Magier-Zweitspieler sollte darauf achten, anderen Spielern
+    nicht durch seinen eventuellen Wissensvorteil bei Questen zu
+    helfen.
+
+    Ein Magier-Zweitspieler sollte weiterhin darauf achten, andere
+    Spieler nicht in ihrem Spiel zu behindern. Dies koennte zum
+    Beispiel durch Blockieren von Quests oder bestimmter limitierter
+    Gildenraenge geschehen.
+    Den Programmierern von Quests oder Gilden steht es natuerlich frei,
+    hier eine eigene Vorsorge zu treffen.
+
+                                *
+
+    Wer im MorgenGrauen spielt, der ist des oefteren auf die Hilfe
+    Anderer angewiesen. Eine Form der Hilfe ist es, einen Charakter
+    auszuleihen oder ausgeliehen zu bekommen.
+    Um sprachlich begruendete Missverstaendnisse im weiteren zu
+    vermeiden, hier kurz folgende Nomenklatur:
+
+      Ein Spieler, der einem anderen Spieler einen Charakter zur
+      Verfuegung stellt, wird als Verleiher bezeichnet.
+
+      Ein Spieler, der einen Charakter eines anderen Spielers nutzt,
+      wird als Nutzer bezeichnet.
+
+    Das Ausleihen von Charakteren ist mit gewissen Randbedingungen
+    erlaubt.
+
+    Ein ausgeliehener Charakter wird als aktiver Zweitspieler des
+    Nutzers betrachtet und ist somit den Regelungen fuer Zweitspieler
+    unterworfen.
+
+    Besonders zu beachten ist, dass der ausgeliehene Charakter nicht
+    mit dem Nutzer, oder einem Zweitspieler des Nutzers, in ein Team
+    darf.
+
+    Sowohl der Nutzer des ausgeliehenen Spielers als auch Verleiher,
+    koennen fuer die Handlungen des ausgeliehen Charakters
+    verantwortlich gemacht werden.
+
+                                *
+
+    Wer mit einem Charakter Aufgaben geloest hat oder zu bestimmten
+    Vermutungen ueber Forscherpunkte gelangt ist, der notiert dies oft,
+    um bei spaeteren Charakteren auf diese Informationen zugreifen zu
+    koennen.
+
+    Solche Forscherpunktlisten oder Komplettloesungen sind zum
+    Eigengebrauch, das heisst fuer die eigenen Zweitspieler erlaubt.
+
+    Die Weitergabe dieser Informationen oder das Nutzen von nicht
+    selbst erstellten Komplettloesungen oder Forscherpunktlisten sind
+    anderen Spielern gegenueber im hoechsten Masse unfair und daher
+    untersagt.
+
+                                *
+
+    Um das Spielen von Muds zu vereinfachen und komfortabler zu
+    gestalten, gibt es eine Reihe sogenannter Mud-Clients.
+
+    Diese Clienten bieten oftmals die Moeglichkeit, mehrere Kommandos
+    im Mud auf eine einzige Aktion hin auszufuehren oder auf Ausgaben
+    mit solchen Kommandoketten zu reagieren.
+
+    Man spricht hier von Scripten oder Triggern.
+
+    Zusammen mit einer internen Programmiersprache der Clienten lassen
+    sich vielfach bestimmte Aktionen im Mud automatisieren.
+
+    Die Verwendung solcher Scripte ist mit einigen Auflagen jedem
+    Spieler freigestellt.
+
+    Sogenannte Untersuchescripte, die selbstaendig eine oder mehrere
+    Detailtiefen von Raeumen, Monstern oder anderen Gegenstaenden
+    abfragen, sind nicht erlaubt.
+
+    Jeder Charakter sollte individuell gesteuert werden, daher sind
+    Scripte, die mehrere Charaktere auf einmal steuern untersagt.
+
+    Scripte, welche einen Charakter im Sinne einer Komplettloesung
+    durch eine Quest fuehren oder eine Forscherpunktliste abarbeiten
+    sind nicht erlaubt.
+
+    Grundsaetzlich duerfen andere Spieler durch Scripte nicht in ihrem
+    Spiel behindert oder belaestigt werden.
+
+    Alle anderen Scripte sind erlaubt, soweit der Spieler innerhalb
+    einer Minute im Rahmen seiner Moeglichkeiten ansprechbar bleibt.
+
+    Als unerlaubtes Skript zaehlt auch der Kampf mit einem Monster,
+    wenn der Spieler dabei laengere Zeit keine Tastatureingaben
+    vornimmt und dabei nicht ansprechbar ist(totidlen). Dies gilt vor
+    allem wenn der Tod des Monsters nicht absehbar ist.
+
+                                *
+
+    Das MorgenGrauen baut auf die Arbeit vieler freiwilliger
+    Programmierer auf. Wie bei jedem Programm kann es dabei zu Fehlern,
+    auch Bugs genannt, kommen.
+
+    Jeder Spieler sollte ein Interesse daran haben, dass diese Fehler
+    beseitigt werden.
+
+    Dies kann man durch eine Meldung des Fehlers an den jeweiligen
+    Magier oder falls dieser nicht erreichbar ist, durch eine Meldung
+    an den entsprechenden Regionsmagier erreichen. Auch der Befehl
+    "fehler" kann hier nuetzliche Dienste leisten.
+
+    Wer diese Magier sind, kann jedem Spieler bei Bedarf durch einen
+    anwesenden Magier oder oft auch anderen Spieler mitgeteilt werden.
+
+    Dieses Vorgehen sollte auch fuer Fehler gelten, die dem Spieler,
+    der diese Fehler entdeckt, zum Vorteil gereichen.
+
+    Sollte sich ein Spieler nicht sicher sein, ob es sich bei einem
+    bestimmten Verhalten von Gegenstaenden, Monstern oder Raeumen um
+    einen Fehler handelt oder um Absicht des Programmieres, so kann er
+    den zustaendigen Magier problemlos danach fragen und somit bei der
+    Beseitigung von Fehlern aktiv helfen.
+
+    Das Verschweigen und Ausnutzen von Fehlern ist anderen Spielern
+    gegenueber unfair und ist daher zu unterlassen.
+
+                                *
+
+    Grundsaetzlich sollte der Spielspass fuer moeglichst alle Spieler
+    erhalten bleiben. Dazu gehoert auch ein gewisses Mass an Fairness
+    der Spieler untereinander.
+
+    Es ist nicht der Sinn der Regeln, das Spielen im MorgenGrauen durch
+    Ueberreglement zu behindern oder reizlos zu gestalten.
+
+    Daher ist es jedoch auch moeglich, dass im Spiel Situationen
+    auftreten, die den Spielspass anderer Spieler und die allgemeine
+    Fairness beeintraechtigen, ohne dass diese Situation durch diesen
+    Leitfaden abgedeckt ist.
+
+    Hier ist jeder Spieler und auch Magier gefragt, seinen gesunden
+    Menschenverstand zu befragen und zu ueberlegen, wie er eine solche
+    Situation vermeiden oder entspannen kann, auch wenn man damit auch
+    einmal auf einen Vorteil verzichten muss.
+
+    Es ist also durchaus nicht alles erlaubt, was nicht explizit
+    verboten ist.
+
+                                *
+
+    Fuer Magier gelten natuerlich zusaetzlich die Regeln des
+    Magiervertrages.
+
+                                *
+
+    Sollte ein Spieler oder Magier gegen Regeln verstossen haben, so
+    kann dies vom Sheriff, seinen Deputies oder den Erzmagiern geahndet
+    werden.
+    Strafen koennen sich von einer einfachen Verwarnung bis hin zur
+    Loeschung von Charakteren erstrecken.
+
+    Die Strafen werden in einem gesunden Verhaeltnis zum Fehlverhalten
+    des Spielers stehen.
+
+                                *
+
+    Wer die Funktion des Sheriffs oder eines Deputies wahrnimmt, kann
+    man mit dem Kommando 'hilfe sheriff' erfragen.
+
+
+ SIEHE AUCH:
+    zweitiemarkierung, fehler, sheriff
+
+ LETZTE AeNDERUNG:
+    Wed, 30.08.2000, 11:35:00 von Muadib
+    
diff --git a/doc/help/loeschskript b/doc/help/loeschskript
new file mode 100644
index 0000000..c54b4be
--- /dev/null
+++ b/doc/help/loeschskript
@@ -0,0 +1,18 @@
+Kriterieren fuer das Loeschskript

+=================================

+

+    Das Script zum Loeschen alter Charaktere laeuft jeweils am 20. eines

+    Monats.

+    Charaktere werden geloescht, wenn sie ALLE folgenden Bedingungen

+    erfuellen:

+

+    * 90 Tage nicht eingeloggt

+    * weniger als 30 Abenteuerpunkte

+    * weniger als 30000 Erfahrungspunkte

+    * Stufe kleiner 10

+

+ SIEHE AUCH:

+    selbstloeschung, spielpause

+

+ LETZTE AeNDERUNG:

+    09.03.2013 Zesstra

diff --git a/doc/help/magier b/doc/help/magier
new file mode 100644
index 0000000..e439dc8
--- /dev/null
+++ b/doc/help/magier
@@ -0,0 +1,130 @@
+
+MAGIER
+======
+
+    In MorgenGrauen erweitern Magier das MUD. Ausserdem haben sie viele
+    Moeglichkeiten, die Spieler nicht haben.
+
+     Wer kann Magier werden?
+        Magier kann jeder werden, der die Anforderungen zur Seher-Werdung
+        erfuellt; siehe dazu die Hilfeseite 'seher'.
+        Soll ein Charakter Magier werden, der selber kein Seher ist, aber der
+        Erstspieler dieses Spielers ist Seher, ist dies mit Genehmigung der
+        EMs moeglich. Hierzu muss den EMs (in Absprache mit dem zukuenftigen
+        Sponsor) ein GS-Konzept vorgestellt werden (in diesem Fall bitte 
+        auch Abschnitt 'Magierwerdung mit einem nicht-Seher' unten beachten).
+
+     Muss ich Magier werden?
+        Wer keine Lust hat, Magier zu werden und lieber noch spielen moechte,
+        kann mit diesen Voraussetzungen Seher werden. Als Seher hat man auch
+        ein paar zusaetzliche Faehigkeiten.
+
+     Wie werde ich Magier?
+        Wenn Du die Voraussetzungen erfuellst, um Magier zu werden, musst Du
+        einen Magier finden, der Dich unterstuetzt. Dieser Magier soll den
+        neuen Magier in seine Moeglichkeiten einweisen und fuer Fragen offen
+        sein. D.h. es sollte am besten ein Magier sein, den Du schon kennst
+        und von dem Du Hilfe erwarten kannst.
+
+        Wenn Du so einen Magier gefunden hast, fragst Du ihn, ob er Dich zu
+        einem neuen Magier ausbildet. Von dem Magier bekommst Du dann einen
+        Vertrag, den Du sorgfaeltig lesen solltest. Wenn Du dann immer noch
+        Magier werden willst, unterschreibst Du den Vertrag. Danach gehst Du
+        mit dem Vertrag zu Merlin (erstmal suchen) und der Magier, von dem
+        Du den Vertrag hast, bittet Merlin, Dich zum Magier zu machen.
+
+        Danach solltest Du von dem Magier eine Einfuehrung erhalten,
+        desweiteren wird Dir Merlin ein Mail mit nuetzlichen Informationen
+        schicken.
+
+     Und was dann?
+        Du beginnst Dein Magierdasein als Lehrling (Lv 15). Du hast noch kein
+        eigenes Verzeichnis, in dem Du programmieren kannst, und Du kannst
+        auch nicht in allen Verzeichnissen lesen. Du kannst Dich allerdings
+        schon einmal mit den Einschraenkungen des Magierlebens (siehe Vertrag)
+        sowie mit der MudLib vertraut machen. Dazu hast Du Leserechte in den
+        Verzeichnissen `/doc', `/std', `/secure' und `/sys'.
+
+        Wenn Du zu diesem Zeitpunkt erkennst, dass der Magierstatus wohl doch
+        nichts fuer Dich ist, kannst Du Dich immer noch an einen Erzmagier
+        wenden, damit er Dich wieder zum Spieler macht. Hiermit solltest Du
+        jedoch nicht zulangen warten, da hierzu eine aeltere Kopie Deines
+        ehemaligen Spielers noetig ist.
+
+        Wenn Du allerdings merkst, dass Du gerne etwas programmieren moech-
+        test, dann melde Dich (mit mail erzmagier) unter Angabe eines kurzen
+        Abrisses, wie Du Deinen weiteren Werdegang als Magier vorstellst bei
+        den Erzmagiern, damit sie Dich auf die naechste Magierstufe (Lv 20)
+        befoerdern. Die Bearbeitugnszeit dieser Mail liegt bei ein bis zwei
+        Wochen. Auf dieser Stufe bekommst Du ein eigenes Verzeichnis samt
+        Workroom, und Du kannst Deine ersten eigenen Programmierversuche
+        starten.
+
+        Wenn Du auch nach Deinen ersten mehr oder weniger erfolgreichen
+        Programmierversuchen noch immer nicht die Flinte ins Korn geworfen
+        hast, dann solltest Du Dir einmal Gedanken darueber machen, was Du
+        im MorgenGrauen genau programmieren moechtest. Der zustaendige
+        Regionsmagier wird Dich bei der Ausarbeitung Deines Konzeptes gerne
+        beraten. Bevor Du anfaengst zu programmieren, solltest Du mit
+        Deinem genauen Konzept noch einmal bei einem zustaendigen
+        Erzmagier nachfragen, ob es als Gesellenstueck ausreicht.
+
+        Sobald Dein Regionsmagier und Du der Meinung seid, dass Deine
+        Sachen fertig sind, koennt Ihr Euch damit an einen der fuer
+        die Abnahme von Gesellenstuecken zustaendigen Erzmagier (siehe
+        Hilfe Erzmagier) wenden. Dieser wird sich Deine Arbeit ansehen und
+        dann entscheiden, ob sie fuer ein Gesellenstueck im MorgenGrauen
+        ausreichend ist. Beachte bitte, dass vor einer solchen Genehmigung
+        durch die zustaendigen Erzmagier von Dir noch keine Objekte
+        unter eigenem Namen angeschlossen werden koennen/duerfen.
+
+        Sofern Du die Regeln fuer Gesellenstuecke erfuellt hast, wird Dich
+        der zustaendige Erzmagier zu einem Vollmagier machen. Erst dann
+        kannst Du auch in anderen Teilen der MudLib einen Blick in den
+        Quellcode anderer Magier werfen. Ausserdem bekommst Du ein eigenes
+        Verzeichnis in der Region, in der Du mitarbeiten moechtest. Dort
+        kannst Du Deine Sachen ausgiebig testen und spaeter dann auch von
+        Deinem Regionsmagier anschliessen lassen.
+
+
+Magierwerdung mit einem nicht-Seher
+-----------------------------------
+
+      * Die Berufung eines nicht-Seher-Charakters zum Magier ist moeglich,
+        sofern der betreffende RL-Spieler bereits einen Seher hat.
+      * Die Berufung eines solchen Charakters zum Magier ist nur in Absprache
+        mit den EMs moeglich. Bei konkreten Anfragen an EMs muss eine erste 
+        Idee angegeben werden, was man sich als GS vorstellt.
+      * Ein vernuenftiges, d.h. zustimmungsfaehiges Konzept muss _vor_
+        Magierwerdung den EMs vorgstellt werden.. An dieses werden die sonst
+        ueblichen Anforderungen bzgl. Umfang und Komplexitaet gestellt. Das
+        Konzept wird von EMs genehmigt, bevor die Berufung zum Magier
+        erfolgt.
+      * Nach Sponsorn des Charakters entfaellt die sonst uebliche Wartezeit
+        von 1-2 Wochen (weil die EMs ja vorher zugestimmt haben) und der
+        Char kann direkt auf Level 20 befoerdert werden.
+      * Das GS muss innerhalb von 6 Monaten nach Magierwerdung fertiggestellt 
+        werden.
+        Die Fertigstellung ist definiert als Einreichung an den RM zur 
+        Abnahme.
+        Dies darf natuerlich keine pro-forma Einreichung von halbfertigem 
+        Zeugs sein.
+      * Eine Verlaengerung dieser Frist kann durch einen von den EMs be-
+        nannten EM (idR EM Gesellenstuecke) genehmigt werden, sofern ihm
+        dies sinnvoll erscheint ("begruendete Faelle", fast fertig, etc.).
+        In Zweifelsfaellen wird die Verlaengerung von den EMs diskutiert.
+      * Muss das GS nach Abnahme durch den RM und/oder den EM Gesellenstuecke
+        nachgebessert werden, sollte dieses innerhalb von 2 Monaten geschehen.
+        Verlaengerungen: s.o.
+      * Bei Nicht-Einhaltung der Frist erfolgt die Loeschung des Charakters 
+        oder Dauerspielpausen auf Magierlevel 15.
+      * Der Spieler unterliegt auch nach Loeschung dem Magiervertrag insofern,
+        als dass er ueber etwaige als Magier gewonnene Interna von nicht-
+        oeffentlichem Code Stillschweigen zu wahren hat.
+
+ SIEHE AUCH:
+    stufen, magierstufen, abenteuer, konzept, gesellenstueck
+    seher, regionen, gildenmagier, erzmagier, goetter
+
+ LETZTE AeNDERUNG: 2015-05-30 von Zook
+
diff --git a/doc/help/magierstufen b/doc/help/magierstufen
new file mode 100644
index 0000000..e36c8b3
--- /dev/null
+++ b/doc/help/magierstufen
@@ -0,0 +1,68 @@
+magierstufen
+============
+
+    Es gibt verschiedene Magierstufen, die bestimmte Rechte und Pflichten mit
+    sich bringen. Ueblicherweise sind die entsprechenden Magier oft auch ein
+    paar Level hoeher (so ist ein Gott ueblicherweise Level 111), dies sind
+    die Grenzen, ab wo die Bezeichnung bzw. die Rechte im beschriebenen 
+    Umfang gueltig sind:
+
+    Level 100	---	Gott
+		Ernennt Erzmagier, klaert schwerwiegende Probleme, traegt die
+		Last der Verantwortung.
+		Volle Schreibrechte.
+
+    Level 60	---	Erzmagier
+		Sind fuer verschiedene praktische Aufgabengebiete wie
+		(Gildenbalance), Mudlib oder Gesellenstuecke verantwortlich.
+		Volle Schreibrechte.
+
+    Level 50	---	Weise
+		Bearbeiten ein spezielles Thema.
+		Volle Schreibrechte bis auf die Mudlib.
+
+    Level 40	---	Regionsmagier
+		Sind fuer eine oder mehrere Regionen zustaendig und haben
+		dort volle Schreibrechte. Ernennt Regionsmitarbeiter.
+
+    Level 30	---	Hilfsmagier
+		Kuemmern sich um Dokumentation oder andere Spezialaufgaben,
+		haben dafuer im Dokumentationsbereich volle Schreibrechte.
+
+    Level 26    ---     Regionsmitarbeiter
+                Wie Level 25, darf zusaetzlich selbst Neumagier berufen.
+		Wird vom Regionsmagier verliehen, fuer Magier, denen diese
+		Verantwortung zugetraut werden kann.
+
+    Level 25	---	Regionsmitarbeiter
+		Arbeitet in einer (oder mehreren) Regionen mit und hat dort
+		auf sein Verzeichnis volle Schreibrechte.
+
+    Level 21	---	Vollmagier
+		Meist ein Uebergangszustand, wenn das Gesellenstueck
+		akzeptiert aber in keiner Region angeschlossen ist.
+		Schreibrechte im eigenen Verzeichnis.
+		Leserechte in allen Gebieten und der Mudlib, mit gewissen
+		Ausnahmen.
+
+    Level 20	---	Lehrling
+		Schreibt sein Gesellenstueck (oder sollte es tun).
+		Schreibrechte im eigenen Verzeichnis.
+		Keine Leserechte in Regionen, Magier- oder Gilden-
+		verzeichnissen.
+
+    Level 15	---	Lehrling/Neumagier
+		Auf diesem Level kann man in der Dokumentation und in der
+		Mudlib stoebern und sich mit dem Magierdasein vertraut
+		machen. Diesen Level erhaelt man direkt nach der
+		Magierwerdung.
+		Bei Annahme des Konzeptes fuer das Gesellenstueck erfolgt
+		Befoerderung auf Level 20.
+		Keine Schreibrechte.
+
+ SIEHE AUCH:
+    magier, stufen, vertrag
+    seher, regionen, gildenmagier, erzmagier, goetter
+
+ LETZTE AeNDERUNG:
+	25.01.2006 von Humni
diff --git a/doc/help/merlin b/doc/help/merlin
new file mode 100644
index 0000000..48ebe78
--- /dev/null
+++ b/doc/help/merlin
@@ -0,0 +1,13 @@
+
+Merlin
+======
+
+Merlin ist der Uebervater aller Magier. Er ist auch zustaendig,
+falls ein Spieler Magier oder Seher werden moechte. Sprich ihn
+einfach einmal darauf an.
+
+Daneben ist er auch reichlich tuettelig und vergisst manchmal,
+wo er sich befindet....
+
+
+LETZTE AeNDERUNG: 2006-02-21 von Zook.
\ No newline at end of file
diff --git a/doc/help/mudrechner b/doc/help/mudrechner
new file mode 100644
index 0000000..7a3100e
--- /dev/null
+++ b/doc/help/mudrechner
@@ -0,0 +1,31 @@
+Der Mudrechner
+==============
+
+     Das Mud laeuft seit 21. Feb 2010 auf:
+     - Dell 2950 mit 2 Netzteilen
+     - 2 * XEON 5420 2.5GHz, 8GB RAM, 70GB (RAID1) OS, 500 GB (RAID1) Daten
+     und ist ueber
+      - 1GB Ethernet (extern 1GB)
+     an das Internet angeschlossen.
+
+     Unter der Adresse mg.mud.de sind ebenfalls:
+      stunnel:  - zur Verschluesselung der Verbindung mit dem MUD
+      FTP:      - anonym
+                  - zum Hochladen von Partybildern
+                  - zum Zugriff auf Software (inklusive aktueller Mudlib)
+                - mit Magiername/Passwort
+                  - zum Zugriff auf das eigene Verzeichnis im MUD
+      git:      - mit hinterlegtem Schluessel (fuer Magier)
+                  - zum Zugriff auf saemtliche freien/eigenen git-Repositories
+      WWW:      - die Webseiten des MG
+     zu erreichen.
+
+ SIEHE AUCH:
+   lag, irc, stunnel, teamspeak, client, www
+
+ HISTORY:
+      Rechner bis 21. Feb 2010, (Stand 08. April 2004):
+      - Xeon CPU 2.40GHz 1024MB RAM, 36 + 70 GB Plattenplatz (SCSI)
+
+ LETZTE AeNDERUNG:
+     8. Nov 2012 Gloinson
diff --git a/doc/help/parties b/doc/help/parties
new file mode 100644
index 0000000..4bd8719
--- /dev/null
+++ b/doc/help/parties
@@ -0,0 +1,28 @@
+
+Parties des Morgengrauen
+========================
+
+ KOMMANDOS:
+     anmelden
+
+ BESCHREIBUNG:
+     Mit diesen Kommando meldet man sich an einem Partybrett zu einer der
+     regelmaessig stattfindenden Parties der Morgengrauen Mudder an. Dort
+     darf man dann dem Protzen welterfahrener Spieler, den Aufschneiden
+     schon lange nicht mehr spielender Seher, den vagen Andeutungen der
+     Magier und sonstigen Prahlereien bei gepflegtem Alkoholkonsum und immer
+     unter der Aufsicht eines Erzmagiers fuer Sittlichkeit lauschen.
+     Wirklich wichtige Details koennen von dir thematisiert werden.
+
+     Ort, Zeit und Kosten der demnaechst stattfindenden Party kann man in
+     der MPA, rubrik "party" oder im Web unter
+       https://mg.mud.de/realworld/party.shtml
+     erfahren.
+
+     erfahren.
+
+ SIEHE AUCH:
+     verein, magier
+
+ LETZTE AeNDERUNG:
+     22. Maerz 2004 Gloinson
diff --git a/doc/help/post b/doc/help/post
new file mode 100644
index 0000000..df10b56
--- /dev/null
+++ b/doc/help/post
@@ -0,0 +1,182 @@
+Das Postsystem
+==============
+
+    Im Mud gibt es auch die Moeglichkeit, anderen Spielern Post zu senden.
+    Anders als bei der Bundespost sind aber weder Briefe noch Postkarten im
+    Mud freimachungspflichtig.
+
+    Man geht einfach in eine der Postfilialen und tippt `post' oder `mail', um
+    in das Postmenue zu gelangen. Das Postmenue gibt auf `h' (oder `hilfe')
+    eine kurze Hilfsseite mit einer Befehlsuebersicht.
+
+    Will man nach 'post' oder 'mail' nicht die im Folder enthaltenen Mails
+    aufgelistet bekommen, so nutze man stattdessen 'post -silent' oder
+    'mail -silent'.
+
+ Inhaltsverzeichnis dieser Hilfsseite:
+
+    1. Mail schreiben
+    2. Mail nach und von ausserhalb des Muds
+    3. Mailaliase
+    4. Mail lesen, beantworten, loeschen und weitersenden - die anderen
+       Menuepunkte im Postmenue
+    5. Ordner (folders)
+    6. Nachsendeauftraege (.forward)
+    7. Paketpost
+    8. Absender ignorieren
+
+ 1. Mail schreiben:
+    Grundsaetzlich gibt es zwei Moeglichkeiten: Entweder innerhalb des
+    Postmenues `m <name>', oder aber man ruft direkt mit `mail <name>' das
+    Schreiben auf und landet dann nach Beendigung im Menue.
+
+    Man wird zunaechst nach der Ueberschrift gefragt und gibt dann den Text
+    ein. Die Eingabe verwendet einen einfachen Editor mit einigen
+    rudimentaeren Editorfunktionen, eine eigene Hilfsseite ist mit `~h'
+    abzufragen.
+    Ueberlange Zeilen werden auf 78 Zeichen pro Zeile umgebrochen, man kann
+    also getrost auch lange Texte am Stueck schreiben.
+
+    Beendet wird die Eingabe mit `.' oder `**' (als eigene Zeile eingeben) und
+    wird dann nach CC's gefragt. CC bedeutet `carbon copy', also Durchschlag.
+    Man kann jetzt einen oder mehrere durch Komma oder Leerzeichen getrennte
+    Namen angeben, an die der Brief zusaetzlich zum eigentlichen Empfaenger
+    gesendet wird.
+
+    Mail an mehrere Personen:
+    Man kann gleich beim Aufruf mehrere Namen durch Komma oder Leerzeichen
+    getrennt angeben, oder die anderen als cc. Auch eine Kombination ist
+    erlaubt. Gibt man mehr als einen Namen als Empfaenger an, so gilt der
+    erste als Empfaenger, die restlichen bekommen cc's.
+
+    Blind Carbon Copies (BCC):
+    Setzt man vor einen Namen ein `-', so wird der Brief auch an diese Adresse
+    gesendet, aber die Adresse steht nicht im Verteiler.
+    *Achtung:* Der eigentliche Empfaenger, also die erste angegebene Adresse,
+    darf keine BCC sein.
+
+ 2. Mail nach und von ausserhalb
+    
+    a) Mail in andere Muds ((Inter-)Mudmail)
+      Es ist moeglich, Mails in eine Reihe von anderen Muds zu schicken und
+      welche von dort zu empfangen.
+      Zum Empfangen muss man nix tun.
+      Zum Versenden gibt man den Charnamen des Empfaenger gefolgt von einem @
+      und dem Mudnamen des Empfaengers an, z.B. 'bla@unitopia'.
+
+    b) Mail in den Rest des Internets
+      
+      Achtung: Politische Vorgaben und das Anwachsen von Spam haben zur
+               Abschaltung dieser Funktionen gefuehrt.
+
+ 3. Mailaliase:
+    Mailaliase koennen wie Namen verwendet werden. Es gibt eine Anzahl
+    vordefinierter Aliase, eine Liste der Aliase bekommt man im Postmenue mit
+    `a'.
+    Magier haben die Moeglichkeit, sich eine Datei `.mailrc' in ihr
+    Verzeichnis zu legen, die eigene Mailaliase enthaelt.
+
+    Es gibt einige vordefinierte Mailaliase, die fuer alle gelten:
+
+     * `me' sendet an diejenige Adresse, die man selber als Emailadresse
+       angegeben hat. Das ist nuetzlich, wenn man Post an seinen eigenen
+       account weiterschicken will.
+     * `freunde' - das sind alle Deine Freunde, wie sie auf Deinem
+       Freundschaftsband stehen (falls Du eins hast).
+     * Rundschreiben an alle Mitarbeiter einer Region: Einfach `d.xyz' als
+       Empfaenger angeben, wobei `xyz' natuerlich der Name der Region ist.
+     * einige weitere Aliase wie `erzmagier', `polizei', `goetter', ... (siehe
+       Liste mit `a') erleichtern es, den richtigen Ansprechpartner zu finden.
+     * Einige Aliase sind eigentlich Nachsendeauftraege und werden bald
+       verschwinden.
+
+ 4. Mail lesen und die anderen Menuepunkte im Postmenue
+
+     Mail lesen:
+        Einfach die Nummer des Briefes tippen, die man lesen will. Um den
+        aktuellen Brief (das ist der, auf den der Pfeil zeigt) zu lesen, `.'
+        tippen, fuer den naechsten `+' (praktisch zum Durchblaettern), `-'
+        fuer den vorhergehenden.
+
+     Mail loeschen:
+        Alle Briefe werden gespeichert, bis man sie loescht. Als Argument
+        Nummer angeben, ansonsten wird der Brief geloescht, auf den der Pfeil
+        in der Liste zeigt.
+
+        Listenargumente der Form [nr] [nr-nr] ... sind zulaessig.
+
+        Beispiel: `d 18 12-14 2' loescht die Briefe Nummer 2,12,13,14,18.
+
+     Mail beantworten, Gruppenantwort:
+        Dasselbe wie `schreiben', nur dass der Empfaenger automatisch der
+        Absender des beantworteten Briefes ist, und, ausser man gibt eine
+        andere an, die Ueberschrift `Re: <Originalueberschrift>' lautet.
+
+        Bei "Gruppenantwort" wird die Antwort ausserdem an alle Empfaenger von
+        CC's des Originalbriefes verschickt.
+
+        Ist eine `Reply-to:'-Zeile im Text enthalten, wird der Brief an die
+        hier angegebene Adresse geschickt.
+
+        *Achtung:* Auch in diesem Fall werden Mailaliase und ggf.
+        Nachsendeauftraege ausgewertet!
+
+     Mail weitersenden (forward):
+        Der erhaltene Brief wird kopiert und an den neuen Empfaenger
+        weitergeschickt. Bei `F' bzw. `Weiter' kann man ausserdem noch
+        zusaetzlich eigenen Text anhaengen.
+
+        Nur fuer `f' (also ohne zusaetzlichen Text) gilt: Wird eine Liste als
+        Argument angegeben, so werden alle Briefe zu einem langen Brief
+        zusammengehaengt und dieser weitergesendet.
+
+ 5. Zum Thema Ordner:
+    Man kann Briefe, die man noch nicht loeschen will, in verschiedene Ordner
+    (folders) sortieren. Solche Ordner muss man zunaechst anlegen, dann kann
+    man die Briefe von einem zum anderen verschieben. Angezeigt werden
+    natuerlich immer nur die Briefe des aktuellen Ordners. Um andere zu lesen,
+    muss man zunaechst den Ordner wechseln.
+
+    Beim Einstieg ins Postmenue schlaegt man zunaechst den Ordner `newmail'
+    auf, in den alle ankommenden Briefe auch abgespeichert werden.
+
+    Alle Befehle, die Ordnernamen als Argumente verlangen, akzeptieren auch
+    `-' fuer den in der Liste vorhergehenden Ordner, `+' fuer den
+    nachfolgenden und jede eindeutige Abkuerzung.
+
+ 6. Nachsendeauftraege
+    Will man seine Post lieber an einen anderen Charakter ausgeliefert
+    bekommen, so geht das auch. Hierzu muss man aber als Emailadresse den
+    anderen Char in dieser Form angeben: 'neuer_empfaenger@morgengrauen' 
+    (siehe auch Befehl 'email').
+
+    Um einen solchen Nachsendeauftrag zu stellen, geht man zu irgendeinem
+    Postamt mit Paketschalter. Genaueres erzaehlt der Postbeamte auf
+    Nachfrage.
+
+    Man sollte sich dieses aber gut ueberlegen: hat ein Charakter keine
+    gueltige eMail-Adresse eingetragen, ist das Zuruecksetzen des Passwortes
+    erheblich komplizierter oder sogar unmoeglich.
+
+    Es ist NICHT moeglich, eine eMail-Adresse ausserhalb des Morgengrauens
+    anzugeben.
+
+ 7. Paketpost
+    Im MorgenGrauen gibt es eine Paketpost. Informationen erteilt der
+    Postbeamte an jedem Paketschalter.
+
+ 8. Absender ignorieren
+    Sollte man von einem bestimmten Absender ueberhaupt keine Post empfangen
+    wollen, so kann dies durch "ignoriere name.mail" erreicht werden, wobei
+    "name" natuerlich durch den konkreten Spielernamen zu ersetzen ist.
+
+------------------------------------------------------------------------------
+
+(c) 1993-1996 Morgengrauen Postverwaltung
+Abteilung Technik, Loco (Abteilungsleiter)
+Anregungen, Fehlermeldungen und Fragen bitte direkt an Loco,
+Flames direkt nach /dev/null.
+
+LETZTE AeNDERUNG:
+   28.10.2013, Zesstra
+
diff --git a/doc/help/rassen b/doc/help/rassen
new file mode 100644
index 0000000..45e4ef5
--- /dev/null
+++ b/doc/help/rassen
@@ -0,0 +1,102 @@
+DIE RASSEN DES MORGENGRAUEN
+---------------------------
+
+Wenn Du jetzt nicht gerade ein Gast bist, kannst Du Deine Rasse ohnehin nicht
+mehr veraendern. Der folgende Ueberblick ist vielleicht trotzdem von Nutzen.
+
+Er beinhaltet genau die Hinweise, die auch bei der Charaktergenerierung
+angezeigt werden.
+
+MENSCHEN
+--------
+
+Die Staerke des Menschen ist seine Vielseitigkeit.
+Der Mensch kann zwar nichts besonders gut - dafuer aber eigentlich alles.
+
+DUNKELELFEN
+-----------
+
+Das Volk der Dunkelelfen lebt in einer grossen Hoehlenstadt gut versteckt
+hinter einem Wasserfall. Ueber kaum ein anderes Volk gibt es soviele
+Vorurteile wie ueber die Dunkelelfen, und so werden sie von allen misstrauisch
+beaeugt oder sogar bekaempft. In diesem Kampf, insbesondere gegen die Elfen,
+sind sie voellig auf sich allein gestellt, und so hat sich eine mehr oder
+minder autarke Gesellschaft entwickelt. Die Dunkelelfen haben eine eigene
+Kultur und eine eigene Goettin, der sie huldigen. Wie auch die Elfen
+verfuegen sie ueber ausserordenlich grosse magische Faehigkeiten, auch wenn
+sie sich mehr auf die schwarze Seite der Magie spezialisiert haben.
+
+ZWERGE
+------
+
+Zwerge sind kleine aber kraeftige Gebirgsbewohner, nicht sehr gespraechig,
+leicht erzuernt, aber eine schlagkraeftige Unterstuetzung fuer ihre Freunde.
+Ihr Mut und ihre Standfestigkeit ist weit und breit beruehmt, auch ihr
+Geschick im Umgang mit Zwergenwaffen verleiht ihnen zusaetzliche Kraft.
+Leider sind Zwerge nicht allzu schlau, sie verlassen sich lieber auf
+ihre Kraft als auf ihr Gehirn.
+
+GOBLINS
+-------
+Goblins sind winzige, gruenhaeutige Wesen, sogar noch kleiner als Hobbits.
+An ihren zu dick geratenen Koepfchen befinden sich lange, selten reglose,
+Ohren und eine grosse, krumme Nase. Ihre kleine Statur sollte jedoch nicht
+taeuschen, denn ihre fehlende Kraft machen sie mit Geschwindigkeit,
+Praezision und nicht zuletzt ihrer unbestrittenen Ruchlosigkeit alleweil
+wett.
+Obwohl fuer sie Pluendern, lautes Herumbruellen und die gemeinsten Streiche
+spielen zum Alltag gehoeren, wuerde sie niemand als boesartig bezeichnen.
+Denn Goblins sind vieles, aber sicherlich nicht die intelligentesten
+Kreaturen. Durch ihren zaehen Willen und die dicke, lederne Haut sind sie
+aussergewoehnlich widerstandsfaehig, und, sofern funkelnde Beute winkt, fuer
+jedes Abenteuer zu haben.
+
+ELFEN
+-----
+
+Als Elfen bezeichnet man in der Regel jene hageren Hinterwaeldler, deren
+demonstratives Naturgehabe in der Regel nur durch ihre Liebe zu kitschigen
+Gedichten und ausschweifendem Geschlechtsleben in den Schatten gestellt wird.
+Einen Elf kann man im allgemeinen nicht nur an aeusseren Missbildungen
+(spitze Ohren, spindelduerre Gestalt, blonde Haare) erkennen, sondern auch
+an seiner aufdringlichen Art, ueber jeden und alles hemmungslos ins Gruene
+loszuphilosophieren.
+
+FELINEN
+-------
+
+Felinen sind aufrecht gehende Katzenwesen.
+Ihre Heimat ist der Dschungel. Kaum jemand duerfte sich dort besser
+zurechtfinden als sie. Bedingt durch diese Umgebung haben sie im Laufe der
+Zeit eine Vorliebe fuer elegante Hoelzer und funkelnde Edelsteine entwickelt.
+Sie sind zwar nicht so 'raffgierig' wie Zwerge, aber dennoch sollte man besser
+nicht versuchen, einem Felinen einen Edelstein wegzunehmen. Sie benutzen die
+Edelsteine sehr gerne, um sich damit zu schmuecken. Felinen betreiben sogar
+einen regelrechten Koerperkult, insbesondere wenn es darum geht, das Fell oder
+die Krallen zu faerben. Edelsteine kommen da als Accessoires gerade recht.
+Auch im Kampf gegen einen Felinen sollte man sehr vorsichtig sein, da Felinen
+ihre geringe Ausdauer durch eine hohe Geschwindigkeit sowie Geschick und
+Intelligenz wettmachen. Auch die Spitzen Krallen sind da nicht zu verachten
+und so mancher Gegner musste schon als Ersatz fuer einen Kratzbaum herhalten.
+
+HOBBITS
+-------
+
+Hobbits sind kleine Wesen, die am ehesten den Menschen aehneln.
+Sie zeichnen sich trotz Ihrer Groesse durch ihren Mut und Standfestigkeit aus.
+Obwohl sie viel lieber zuhause vorm warmen Kamin sitzen, sind sie immer
+fuer ein Abenteuer zu haben.
+
+ORKS
+----
+
+Orks sind humanoide Wesen, allerdings etwas kleiner als Menschen. Von Natur
+aus sind sie ausgesprochen haesslich anzusehen mit ihrere dunklen Haut, die
+fast ins zwergenartig-dreckige geht. Aufgrund ihrer charakterlichen Neigung
+zu Gewaltbereitschaft werden sie gerne als unzivilisiert angesehen, haben
+sich jedoch als unerschrockene, risikobereite Kaempfer einen Namen gemacht.
+
+Letzte Aenderung: 20.05.2015, Amaryllis
+
+
+
diff --git a/doc/help/reboot b/doc/help/reboot
new file mode 100644
index 0000000..e6f183b
--- /dev/null
+++ b/doc/help/reboot
@@ -0,0 +1,57 @@
+Reboots und ihre Folgen
+-----------------------
+    
+    Bei einem 'Reboot' wird das MorgenGrauen heruntergefahren, d.h.
+    das aktuelle Spiel wird beendet und, wenn alles gut geht, ein neues 
+    Spiel gestartet. Dies ist manchmal noetig, wenn groessere Aenderungen 
+    am MorgenGrauen vorgenommen wurden.
+    
+    Dabei gehen in der Regel alle Objekte (Waffen, Ruestungen etc.), die 
+    im Umlauf waren, verloren. Nach einem 'Reboot' stehst Du also erstmal 
+    ziemlich nackt in der Gegend herum :)
+    
+    Eine Ausnahme bilden jedoch sogenannte 'Autoload-Objekte'. Oft sind
+    dies Objekte, die man als Belohnung fuer bestandene Abenteuer erhalten
+    hat, Gildenobjekte, Muenzen und einiges mehr. Sie ueberleben selbst
+    einen 'Reboot' und bleiben Dir erhalten. Was Du an 'Autoload-Objekten'
+    bei Dir traegst, kannst Du mit dem Befehl 'i +a' erfahren (Siehe hierzu:
+    hilfe inv).
+    
+    Fuer Objekte, die Du durch einen 'Reboot' verloren hast, bekommst
+    Du eine finanzielle Entschaedigung.
+ 
+    Ein 'Reboot' hat jedoch keine Auswirkungen auf Deine Gildenfaehigkeiten,
+    die Hoehe Deiner Erfahrungs- oder Abenteuerpunkte u.ae., er fuegt Dir
+    lediglich einen unwesentlichen materiellen Schaden zu.
+
+Armageddon
+----------
+    Wird ein Reboot noetig, ist das meist keine ploetzliche Angelegenheit,
+    sondern von langer Hand vorbereitet. Daher kann der Reboot auch einige
+    Tage zuvor angekuendigt werden, so dass Spieler sich auf ihn einstellen
+    koennen. Diese Aufgabe uebernimmt Armageddon als Herold des Weltunter-
+    gangs. Dieser ruft regelmaessig aus, wie lange es noch bis zum Reboot 
+    ist und sorgt auch sonst fuer eine passende, unheimliche, also dem Welt-
+    untergang angemessene Stimmung.
+
+    Wenn Du nicht durch Armageddons Meldungen belaestigt werden moechtest, 
+    kannst Du dies auf zweierlei Arten tun:
+    
+    Entweder durch 'ignoriere armageddon'. Du wirst ab sofort nur noch das 
+    absolute Minimum an Meldungen von Armageddon bekommen. Weltuntergangs-
+    stimmung gibt es dann aber auch nicht mehr.
+
+    Oder durch 'teile armageddon mit ruhe'. Damit bringst Du diesen einen
+    Armageddon zum schweigen. Wird dieser aber abgebrochen, oder kommt es
+    doch mal zum Reboot, ist ein neuer Armageddon redseelig wie gehabt. 
+
+    Auf diese Weise verpasst Du keinen Armageddon, wirst aber auch nicht 
+    durch staendiges gescrolle genervt. Falls Du doch wieder genervt werden
+    moechtest, reicht die Wiederholung des oben genannten tm.
+
+    
+ SIEHE AUCH:
+    inv, ignoriere, crash, erzmagier
+
+ LETZTE AeNDERUNG:
+    Mon Apr 05 21:30:00 2004 von Vanion.
diff --git a/doc/help/regeln b/doc/help/regeln
new file mode 100644
index 0000000..5a5136d
--- /dev/null
+++ b/doc/help/regeln
@@ -0,0 +1,232 @@
+Ein kleiner Leitfaden zum Spiel im MorgenGrauen
+===================================================
+
+  Das MorgenGrauen ist ein Spiel. Wie bei jedem Spiel muessen, um den
+  Spielspass moeglichst fuer alle zu erhalten, ein paar wenige Regeln
+  beachtet werden. Diese Regeln gelten sowohl fuer Spieler als auch
+  fuer Magier.
+
+                                *
+
+    Die wichtigste Regel betrifft das Angreifen und Toeten anderer
+    Spieler.
+
+    ES IST STRIKT UNTERSAGT ANDERE SPIELER ANZUGREIFEN ODER ZU TOETEN.
+
+    Auch das absichtliche indirekte Toeten von Spielern durch
+    Gildenfaehigkeiten, spezielle Objekte oder aehnliches faellt unter
+    diese Regel.
+
+    Sollte man einen Spieler umgebracht haben, treten sofort
+    Beschraenkungen in Kraft, die nur durch einen Erzmagier wieder
+    aufgehoben werden koennen.
+
+    Will man sich dennoch mit anderen Spielern messen, so existieren
+    extra dafuer eingerichtete Gebiete, wie zum Beispiel die Arena in
+    der Wueste, in denen man sich gegenseitig bekaempfen kann.
+
+                                *
+
+    Hat man mit seinem Charakter eine Zeit lang gespielt, so entsteht
+    oft der Wunsch, einmal eine andere Rasse, Gilde oder einfach einen
+    anderen Rollenspielcharakter zu spielen.
+    Seinen bisherigen Charakter kann oder moechte man dazu allerdings
+    nicht benutzen.
+    Daher kann jeder Spieler weitere Charaktere anlegen.
+
+    Diese muessen dann jedoch als sogenannter Zweitspieler des ersten
+    Charakters markiert werden.
+    Diese Markierung kann man zum Beispiel beim Schmied in der
+    Zwergenstadt oder beim Dorfschreiber im Elfendorf erhalten.
+    Diese Markierung kann man auch vor anderen Spielern verbergen.
+
+    Man kann beliebig viele seiner Charaktere gleichzeitig einloggen,
+    doch ist hierbei darauf zu achten, dass nur zwei dieser Charaktere
+    aktiv am Spielgeschehen, das ist alles was ueber reine
+    Kommunikation hinausgeht, teilnehmen.
+    Die anderen Charaktere muessen sich auf die Kommunikation
+    beschraenken.
+
+    Weitere Einschraenkungen existieren fuer Magier.
+
+    Ein Magier darf nicht gleichzeitig mit seinen Spielercharakteren
+    eingeloggt sein, diese manipulieren oder fuer seine Zweitspieler
+    fremden Code analysieren.
+
+    Dazu gibt es nur ein Ausnahme: Zum Zwecke der Zweitiemarkierung
+    darf der Magier sich gleichzeitig mit dem Spielercharakter
+    einloggen.
+
+    Der Magier-Zweitspieler sollte darauf achten, anderen Spielern
+    nicht durch seinen eventuellen Wissensvorteil bei Questen zu
+    helfen.
+
+    Ein Magier-Zweitspieler sollte weiterhin darauf achten, andere
+    Spieler nicht in ihrem Spiel zu behindern. Dies koennte zum
+    Beispiel durch Blockieren von Quests oder bestimmter limitierter
+    Gildenraenge geschehen.
+    Den Programmierern von Quests oder Gilden steht es natuerlich frei,
+    hier eine eigene Vorsorge zu treffen.
+
+                                *
+
+    Wer im MorgenGrauen spielt, der ist des oefteren auf die Hilfe
+    Anderer angewiesen. Eine Form der Hilfe ist es, einen Charakter
+    auszuleihen oder ausgeliehen zu bekommen.
+    Um sprachlich begruendete Missverstaendnisse im weiteren zu
+    vermeiden, hier kurz folgende Nomenklatur:
+
+      Ein Spieler, der einem anderen Spieler einen Charakter zur
+      Verfuegung stellt, wird als Verleiher bezeichnet.
+
+      Ein Spieler, der einen Charakter eines anderen Spielers nutzt,
+      wird als Nutzer bezeichnet.
+
+    Das Ausleihen von Charakteren ist mit gewissen Randbedingungen
+    erlaubt.
+
+    Ein ausgeliehener Charakter wird als aktiver Zweitspieler des
+    Nutzers betrachtet und ist somit den Regelungen fuer Zweitspieler
+    unterworfen.
+
+    Besonders zu beachten ist, dass der ausgeliehene Charakter nicht
+    mit dem Nutzer, oder einem Zweitspieler des Nutzers, in ein Team
+    darf.
+
+    Sowohl der Nutzer des ausgeliehenen Spielers als auch Verleiher,
+    koennen fuer die Handlungen des ausgeliehen Charakters
+    verantwortlich gemacht werden.
+
+                                *
+
+    Wer mit einem Charakter Aufgaben geloest hat oder zu bestimmten
+    Vermutungen ueber Forscherpunkte gelangt ist, der notiert dies oft,
+    um bei spaeteren Charakteren auf diese Informationen zugreifen zu
+    koennen.
+
+    Solche Forscherpunktlisten oder Komplettloesungen sind zum
+    Eigengebrauch, das heisst fuer die eigenen Zweitspieler erlaubt.
+
+    Die Weitergabe dieser Informationen oder das Nutzen von nicht
+    selbst erstellten Komplettloesungen oder Forscherpunktlisten sind
+    anderen Spielern gegenueber im hoechsten Masse unfair und daher
+    untersagt.
+
+                                *
+
+    Um das Spielen von Muds zu vereinfachen und komfortabler zu
+    gestalten, gibt es eine Reihe sogenannter Mud-Clients.
+
+    Diese Clienten bieten oftmals die Moeglichkeit, mehrere Kommandos
+    im Mud auf eine einzige Aktion hin auszufuehren oder auf Ausgaben
+    mit solchen Kommandoketten zu reagieren.
+
+    Man spricht hier von Scripten oder Triggern.
+
+    Zusammen mit einer internen Programmiersprache der Clienten lassen
+    sich vielfach bestimmte Aktionen im Mud automatisieren.
+
+    Die Verwendung solcher Scripte ist mit einigen Auflagen jedem
+    Spieler freigestellt.
+
+    Sogenannte Untersuchescripte, die selbstaendig eine oder mehrere
+    Detailtiefen von Raeumen, Monstern oder anderen Gegenstaenden
+    abfragen, sind nicht erlaubt.
+
+    Jeder Charakter sollte individuell gesteuert werden, daher sind
+    Scripte, die mehrere Charaktere auf einmal steuern untersagt.
+
+    Scripte, welche einen Charakter im Sinne einer Komplettloesung
+    durch eine Quest fuehren oder eine Forscherpunktliste abarbeiten
+    sind nicht erlaubt.
+
+    Grundsaetzlich duerfen andere Spieler durch Scripte nicht in ihrem
+    Spiel behindert oder belaestigt werden.
+
+    Alle anderen Scripte sind erlaubt, soweit der Spieler innerhalb
+    einer Minute im Rahmen seiner Moeglichkeiten ansprechbar bleibt.
+
+    Als unerlaubtes Skript zaehlt auch der Kampf mit einem Monster,
+    wenn der Spieler dabei laengere Zeit keine Tastatureingaben
+    vornimmt und dabei nicht ansprechbar ist(totidlen). Dies gilt vor
+    allem wenn der Tod des Monsters nicht absehbar ist.
+
+                                *
+
+    Das MorgenGrauen baut auf die Arbeit vieler freiwilliger
+    Programmierer auf. Wie bei jedem Programm kann es dabei zu Fehlern,
+    auch Bugs genannt, kommen.
+
+    Jeder Spieler sollte ein Interesse daran haben, dass diese Fehler
+    beseitigt werden.
+
+    Dies kann man durch eine Meldung des Fehlers an den jeweiligen
+    Magier oder falls dieser nicht erreichbar ist, durch eine Meldung
+    an den entsprechenden Regionsmagier erreichen. Auch der Befehl
+    "fehler" kann hier nuetzliche Dienste leisten.
+
+    Wer diese Magier sind, kann jedem Spieler bei Bedarf durch einen
+    anwesenden Magier oder oft auch anderen Spieler mitgeteilt werden.
+
+    Dieses Vorgehen sollte auch fuer Fehler gelten, die dem Spieler,
+    der diese Fehler entdeckt, zum Vorteil gereichen.
+
+    Sollte sich ein Spieler nicht sicher sein, ob es sich bei einem
+    bestimmten Verhalten von Gegenstaenden, Monstern oder Raeumen um
+    einen Fehler handelt oder um Absicht des Programmieres, so kann er
+    den zustaendigen Magier problemlos danach fragen und somit bei der
+    Beseitigung von Fehlern aktiv helfen.
+
+    Das Verschweigen und Ausnutzen von Fehlern ist anderen Spielern
+    gegenueber unfair und ist daher zu unterlassen.
+
+                                *
+
+    Grundsaetzlich sollte der Spielspass fuer moeglichst alle Spieler
+    erhalten bleiben. Dazu gehoert auch ein gewisses Mass an Fairness
+    der Spieler untereinander.
+
+    Es ist nicht der Sinn der Regeln, das Spielen im MorgenGrauen durch
+    Ueberreglement zu behindern oder reizlos zu gestalten.
+
+    Daher ist es jedoch auch moeglich, dass im Spiel Situationen
+    auftreten, die den Spielspass anderer Spieler und die allgemeine
+    Fairness beeintraechtigen, ohne dass diese Situation durch diesen
+    Leitfaden abgedeckt ist.
+
+    Hier ist jeder Spieler und auch Magier gefragt, seinen gesunden
+    Menschenverstand zu befragen und zu ueberlegen, wie er eine solche
+    Situation vermeiden oder entspannen kann, auch wenn man damit auch
+    einmal auf einen Vorteil verzichten muss.
+
+    Es ist also durchaus nicht alles erlaubt, was nicht explizit
+    verboten ist.
+
+                                *
+
+    Fuer Magier gelten natuerlich zusaetzlich die Regeln des
+    Magiervertrages.
+
+                                *
+
+    Sollte ein Spieler oder Magier gegen Regeln verstossen haben, so
+    kann dies vom Sheriff, seinen Deputies oder den Erzmagiern geahndet
+    werden.
+    Strafen koennen sich von einer einfachen Verwarnung bis hin zur
+    Loeschung von Charakteren erstrecken.
+
+    Die Strafen werden in einem gesunden Verhaeltnis zum Fehlverhalten
+    des Spielers stehen.
+
+                                *
+
+    Wer die Funktion des Sheriffs oder eines Deputies wahrnimmt, kann
+    man mit dem Kommando 'hilfe sheriff' erfragen.
+
+
+ SIEHE AUCH:
+    zweitiemarkierung, fehler, sheriff
+
+ LETZTE AeNDERUNG:
+    Fri, 08.06.2001, 11:35:00 von Nonne
+    
diff --git a/doc/help/regionen b/doc/help/regionen
new file mode 100644
index 0000000..a278350
--- /dev/null
+++ b/doc/help/regionen
@@ -0,0 +1,37 @@
+Die Regionen im MorgenGrauen
+============================
+
+Fuer so manchen Spieler ist es vielleicht von Interesse zu erfahren, welcher
+Magier fuer welche Region zustaendig ist. Das ist zum einen hilfreich, wenn
+man ein Seherhaus bauen will, zum anderen aber auch dann, wenn man sich mit
+der Absicht traegt selbst mal Magier zu werden und einen Ansprechpartner
+sucht.
+
+Hier also alle Regionen des Morgengrauens mit ihren Regionsmagiern:
+
+   Anfaenger     Ark
+   Dschungel     Ark
+   Ebene         Arathorn
+   Fernwest      Vanion
+   Gebirge       Bambi
+   Inseln        Miril
+   Polar         Humni, Caldra
+   Schattenwelt  Zook
+   Seher         Zook
+   Unterwelt     Zesstra (vertretungsweise in Abwesenheit eines RM)
+   VLand         Bugfix, Esme, nur noch spiritus rector: Morgoth
+   Wald          Notstrom 
+   Wueste        Notstrom, Ennox
+
+Wenn Du den Regionsmagiern einer Region eine Mail schreiben moechtest,
+so verwende am besten die Mail-Aliase "rm_<regionname>".
+
+Falls der/die RMs einer Region ueber laengere Zeit nicht erreichbar sind:
+* bitte ersatzweise einen Erzmagier ansprechen
+* bei akuten Problemen mit "mrufe" und "mail erzmagier" alle ansprechen
+
+SIEHE AUCH:
+	Gildenmagier
+
+----------------------------------------------------------------------------
+Last modified: 2015-12-18 von Zook.
diff --git a/doc/help/reset b/doc/help/reset
new file mode 100644
index 0000000..d3451a9
--- /dev/null
+++ b/doc/help/reset
@@ -0,0 +1,39 @@
+
+Reset
+=====
+
+    Resets sind ein Ereignis fuer alle Objekte (also Wesen, Raeume, Waffen
+    und Ruestungen).
+    Dabei werden diese (oft) auf einen bestimmten Zustand zurueckgesetzt
+    oder es werden Aktionen, wie zB Heilung durch Amulette durchgefuehrt.
+
+    Speziell bei Raeumen werden im Reset:
+      1. Objekte des Raumes neuerzeugt:
+         * sowohl herumliegende Objekte, welche entfernt wurden
+         * als auch getoetete NPCs, die jetzt wiederbelebt werden
+      2. Tueren geschlossen/geoeffnet (je nach Ursprungszustand).
+      3. gleiche, herumliegende Objekte im Raum aufgeraeumt.
+         (danach gibt es maximal 3 gleiche Objekte, das findet nur bei
+          Spielerabwesenheit statt)
+
+    Speziell 1. ist interessant, weil in einem Reset mit einem Mal wieder
+    dieses fiese Blockademonster auftaucht oder endlich der heissersehnte
+    Questgegenstand erlangbar ist.
+
+    Normalerweise ist die Zeit zwischen zwei Resets 30+random(30) Minuten.
+    Allerdings veraendern Magier diese Werte gern mal.
+
+ BEMERKUNGEN:
+    Beruehmt-beruechtigt ist der sogenannte Boing-Reset:
+    Aus technischen Gruenden fand frueher ein Reset in einem das erste Mal
+    seit langer Zeit betretenen (und damit geladenen) Raum sehr frueh nach
+    diesem Betreten statt.
+    Insbesondere in der SSP (also Boings Gebiet) fand man sich dadurch
+    ploetzlich erneut in der Gesellschaft eines doch gerade eben hoeflich
+    hinfortkomplimentierten Blockademonsters wieder.
+
+ SIEHE AUCH:
+    abk, reboot, abenteuer
+
+21.10.2014, Zesstra
+
diff --git a/doc/help/seher b/doc/help/seher
new file mode 100644
index 0000000..69f147c
--- /dev/null
+++ b/doc/help/seher
@@ -0,0 +1,62 @@
+
+Seher
+=====
+
+    Seher im MorgenGrauen sind Spieler, die durch das Erfuellen 
+    bestimmter Anforderungen zeigen, dass sie sich im MorgenGrauen
+    gut auskennen und schon eine Reihe von Herausforderungen 
+    gemeistert haben. Die Seher-Anforderungen muss man auch erfuellen,
+    um Magier werden zu koennen.
+
+    Mit dem Status des Sehers sind einige Vorrechte verbunden, die dem
+    'kleinen' Spieler nicht zur Verfuegung stehen:
+
+     * Seher koennen einige Aspekte ihrer Charakterbeschreibung wie z.B. den
+       Titel, das Presay oder die Bewegungsmeldungen aendern.
+     * Seher koennen sich ein Haus bauen und weitgehend frei einrichten. Das
+       Haus dient danach auch als Startpunkt nach ende oder einem Reboot.
+     * In einigen Gilden stehen die hoechsten Raenge, Zaubersprueche oder
+       Kampftechniken nur Sehern offen.
+     * Weitere Bonbons stellt die Fraternitas dono Archmagorum 
+       zur Verfuegung. 
+
+    Die Bedingungen fuer den Seherstatus sind wie folgt festgelegt:
+
+    Insgesamt benoetigt man 3800 Stufenpunkte und muss die 
+    Spielerstufe 42 erreicht haben, um Seher zu werden. Fuer die 
+    Stufenpunkte muss man in einigen Bereichen gewisse Mindestpunkt-
+    zahlen erreichen:
+
+     * Man muss mindestens 1610 Abenteuerpunkte erzielt haben.
+     * Man muss mindestens 100 Forscherpunkte gesammelt haben, dem
+       entsprechen 600 Stufenpunkten. 
+     * Man muss mindestens 40 Zaubertraenke gefunden haben, dem
+       entsprechen 200 Stufenpunkte.
+     * Man muss eine Reihe von wuerdigen Gegnern erlegt haben, so 
+       dass man aus diesen "Erstkills" 150 Stufenpunkte erzielt.
+
+    Die verbleibenden Stufenpunkte koennen auf jede beliebiege
+    Art gesammelt werden (so auch die Stufenpunkte, die fuer die 
+    Gildenfaehigkeiten, fuer MiniQuests etc. vergeben werden). 
+
+    ** WICHTIGER HINWEIS: 
+    ** Die Seheranforderungen werden jaehrlich von den Erzmagiern
+    ** evaluiert und ggf. nach oben oder unten angepasst. Der Termin
+    ** fuer solche etwaig willkuerliche Anpassungen ist jedes Jahr
+    ** der 15. September. 
+
+    Wenn man alle Anforderungen erfuellt hat, kann man sich von
+    Merlin befoerdern lassen. 
+
+    Ist man einmal Seher, und bekommt ploetzlich doch Lust, sich als Magier zu
+    betaetigen, so kann man ohne weiteres Magier werden (wenn man einen
+    Sponsor findet).
+
+SIEHE AUCH:
+    allg. Hilfe: merlin, stufen, stufenpunkte, abenteuer, forscherpunkte, 
+                 magier
+    Seherhilfe: hausbau, extralook, fehlermeldung, fluchtrichtung, presay, 
+                review, sethands, setmin, setmout, setmmin, setmmout, titel
+                (nur ab Seherstatus lesbar)
+
+LETZTE AeNDERUNG: 2016-06-05 von Arathorn
diff --git a/doc/help/sehertore b/doc/help/sehertore
new file mode 100644
index 0000000..586e505
--- /dev/null
+++ b/doc/help/sehertore
@@ -0,0 +1,30 @@
+Sehertore
+=========
+
+    Sehertore sind ueber das gesamte MorgenGrauen verteilt und nur fuer
+    Seher zu erkennen und zu nutzen.
+
+    Mithilfe dieser Portale ist es moeglich, grosse Distanzen im MG
+    durch einen Teleport von einem Tor zu einem anderen zurueckzulegen.
+    Liegt das Ziel auf einer anderen Insel als das Starttor, sind zum
+    Teleportieren Magiepunkte notwendig.
+
+    Die frueher moegliche "Fehlteleportation" ist inzwischen zum 
+    Glueck der Seher abgeschafft worden. Wobei in der Parallelwelt auch
+    diesbezueglich manchmal andere Regeln gelten.
+
+    Bevor jedoch Sehertore benutzt werden koennen, muessen sie entdeckt
+    werden. Dies geschieht oftmals durch blosses Betreten eines Raumes,
+    in dem ein Sehertor aufgestellt ist; es ist jedoch auch denkbar, 
+    dass naehere Untersuchungen oder gar Aktionen notwendig werden, be-
+    vor sich ein Sehertor zu erkennen gibt.
+
+    Einmal gefundene Sehertore bleiben bekannt.
+
+    Seit einiger Zeit erlaubt die Fraternitas Domo Archmagorum auch
+    Spielern das Erlernen von Portalen. Dies ist jedoch eingeschraenkt.
+    Dazu sollte man sich in der Fraternitas einfach einmal ein wenig 
+    umschauen.
+
+ LETZTE AeNDERUNG:
+	2013-01-27 von Humni
diff --git a/doc/help/sheriff b/doc/help/sheriff
new file mode 100644
index 0000000..dd6c16e
--- /dev/null
+++ b/doc/help/sheriff
@@ -0,0 +1,18 @@
+Der Sheriff uebernimmt die Auslegung der Regeln und Ahndung von Regel-
+verstoessen. Er stellt insofern also eine Art Schiedsrichter da. 
+
+Derzeit ist Amaryllis dafuer zustaendig.
+
+Es steht den Sheriffs frei, weitere Deputys zu ernennen die sie bei
+der Arbeit unterstuetzen, in jedem Fall ist ihren Anweisungen und
+denen ihrer Deputys Folge zu leisten.
+
+Meldet Euch bei Fragen oder Problemen bitte direkt bei ihnen oder
+schickt ihnen eine Mail, hilfreich ist hier auch das Mailalias
+"polizei".
+
+SIEHE AUCH:
+   leitfaden
+
+LETZTE AeNDERUNG:
+   04.02.2008 von Zesstra.
diff --git a/doc/help/sozialmedien b/doc/help/sozialmedien
new file mode 100644
index 0000000..5986947
--- /dev/null
+++ b/doc/help/sozialmedien
@@ -0,0 +1,14 @@
+Diverse MG-Gruppen ausserhalb des MUDs
+======================================
+
+Morgengrauen besitzt momentan:
+
+ * einen Twitter-Account: https://twitter.com/morgengrauen
+ * eine Facebook-Gruppe: https://www.facebook.com/groups/120370845579
+ * eine G+-Community:
+   https://plus.google.com/communities/101092454975782252702
+
+SIEHE AUCH:
+    teamspeak
+
+1. Jun 2016, Gloinson
diff --git a/doc/help/spiele b/doc/help/spiele
new file mode 100644
index 0000000..1cd6d3e
--- /dev/null
+++ b/doc/help/spiele
@@ -0,0 +1,34 @@
+
+Spiele
+======
+
+     Als waere das Morgengrauen nicht Spiel genug, haben sich doch einige
+     magisch wankende Gestalten daran gemacht noch ganz andere Spiele zu
+     basteln, bei denen man sich vom Metzel- und Questalltag erholen kann.
+
+     Wen es also reizt, der kann folgende Spiele zusammen mit anderen
+     Spielern suchen und versuchen:
+
+     17&4		Wald, Elfendorf, Casino
+     4 Gewinnt		Ebene, Port Vain
+     Abalone		Ebene, Port Vain, Owen Braddon
+     Dart		Vland, Orkhausen, Kneipe
+     Doppelkopf		Inseln, Shaky Island
+     Fussball		Ebene, Feuerbergstadion
+     Go			Fernwest, Xian
+     Maexchen		Ebene, Port Vain, Abenteurerkneipe/Owen Braddon
+     Minesweeper	Dschungel, Ohort, Hinterzimmer
+     Rage		Wald, Drakonien, Huette
+     Rodeln		Polar, Tundra
+     Schach		Gebirge, Hochebene
+     Skat		Gebirge, Lupinental, Kneipe
+     Tablut		Gebirge, Bergdorf, Kneipe
+     Take It Easy	Dschungel, Steppe, Huette
+     Monopoly           Feuerinsel, Vulkan d. Parawelt (GladioPolis)
+
+ SIEHE AUCH:
+     parties, sterben
+
+ LETZTE AeNDERUNG:
+     22. Maerz Gloinson
+
diff --git a/doc/help/spielertesties b/doc/help/spielertesties
new file mode 100644
index 0000000..d0204bf
--- /dev/null
+++ b/doc/help/spielertesties
@@ -0,0 +1,85 @@
+Spielertesties
+==============
+
+Nach Absprache mit einem Erzmagier ist es erlaubt, dass ein Spieler
+unangeschlossene Gebiete, Quests oder auch Gilden testet. 
+
+Fuer das Testen von Gebieten und Questen gibt es zwei Moeglichkeiten:
+  
+Der Magier leitet den Test hauptverantwortlich:
+Diese Regelung ist vor allem fuer das Testen kleinerer Quests und Gebiete gedacht.
+Hier gelten folgende Regeln:
+ Der Spieler, der den Test durchfuehrt, bekommt einen Testspieler des Magiers 
+ sowie eine angemessene Ausruestung. Bei der Auswahl eines Testspielers
+ ist es auch moeglich Testspieler anderer Magier mit deren Einverstaendnis
+ zu verwenden.
+ Der leitende Magier informiert einen Erzmagier ueber den Testspieler.
+ Der Testspieler darf waehrend der Testzeit das Testgebiet nicht verlassen und 
+ auch keine Objekte des zu testenden Gebietes an andere weiterreichen. 
+ Der Testspieler wird waehrend der ganzen Testzeit vom Magier ueberwacht. 
+ Dies geschieht sowohl zur Ueberwachung dieser Regeln, als auch,  um dem 
+ programmierenden Magier weitere Ideen zur Verfuegung zu stellen.
+ Dieser Test kann nach der Fertigstellng und Tests des Gebietes durch den 
+ verantwortlichen Magier stattfinden.
+ 
+Ein Erzmagier leitet den Test hauptverantwortlich:
+Diese Regelung ist vor allem fuer das Testen groesserer Quests und Gebiete 
+gedacht.
+Hier gelten folgende Regeln:
+ Der Spieler, der den Test durchfuehrt, bekommt durch den Erzmagier einen 
+ Testspieler sowie eine angemessene Ausruestung gestellt. Bei der Auswahl 
+ eines Testspielers ist es auch moeglich Testspieler anderer Magier mit 
+ deren Einverstaendnis zu verwenden. Dieser Testspieler darf waehrend der 
+ Testzeit das Testgebiet nicht verlassen und auch keine Objekte des zu 
+ testenden Gebietes an andere weiterreichen. 
+ Der Testspieler wird waehrend der ganzen Testzeit vom Magier und Erzmagier 
+ ueberwacht. Dies geschieht sowohl zur Ueberwachung dieser Regeln, als auch,  
+ um dem programmierenden Magier weitere Ideen zur Verfuegung zu stellen.
+ Der Erstcharakter des Spielers (auf Wunsch auch ein Zweitie) erhaelt nach
+ Abschluss des Tests die gefundenen Forscherpunkte und Erstkillstupse 
+ gutgeschrieben, aber auch die erlittenen Schaeden (insbesondere Tode) - 
+ selbstverstaendlich, soweit sie nicht durch Programmier-fehler bedingt waren.
+Dieser Test findet NACH dem Test des Gebietes durch den zustaendigen 
+Regionsmagier und (bei Questen) durch den Quest-Erzmagier, sowie NACH 
+Eintrag der Forscherpunkte statt.
+
+Beide Testvarianten ersetzen auf keinen Fall die Tests durch Magier.  
+
+Fuer das Testen von Gilden gelten andere Regeln, da sich diese zusammen mit
+anderen Spielern in gleichen Gebieten aufhalten koennen:
+
+1) Gildentesties sind immer echte, zu diesem Zweck erschaffene Testspieler.
+   P_TESTPLAYER wird dabei auf den Namen der Gilde gesetzt.
+
+2) Jeder einem Spieler zur Verfuegung gestellte Gildentestie muss 
+   mittels P_SECOND mit dem Namen des Erstspielers markiert sein, der
+   ihn fuehrt. Jeder Testspieler sollte eindeutig zuzuordnen sein.
+  
+3) Die Testies unterliegen ALLEN Regeln des Testie-Daseins. Das heisst
+   unter anderem, dass sie NICHT fuer andere Spieler Ausruestung beschaffen
+   oder Wege freimetzeln duerfen! Verstoesse hiergegen koennen unter
+   Umstaenden zur Loeschung der beguenstigten Spielercharaktere und
+   des Testcharakters fuehren.
+
+4) Die Testies werden staendig ueberwacht. Logfiles koennen unter Umstaenden
+   von den Gildenmagiern eingesehen werden.
+
+5) Jeder Testie muss vom EM Gilden (z.Z. Humni) genehmigt werden!
+
+6) 5 Testies pro Gilde sollten genuegen. Wenn ein Teil der Testies nichts
+   tut, kann man den Testie auch weitergeben lassen.
+
+SIEHE AUCH:
+        Erzmagier, Gildenmagier, Regionsmagier, Tod
+
+----------------------------------------------------------------------------
+Last modified: Fri Oct 18 16:24:00 2002 by Muadib
+
+
+Fuer Magier.
+Magier koennen natuerlich ohne Absprache Testspieler einer bestimmten Gilde
+haben, sofern diese normalen Spielern zugaenglich ist. Sofern eine Gilde das
+Anlegen von Testspielern unterstuetzt, gibt es entsprechende Hilfeseiten unter
+<gildenname>.testspieler - zum Beispiel katzenkrieger.testspieler
+
+Zusatz: Tue Jan  7 23:22:44 2003, durch Bambi.
\ No newline at end of file
diff --git a/doc/help/sterben b/doc/help/sterben
new file mode 100644
index 0000000..08ab270
--- /dev/null
+++ b/doc/help/sterben
@@ -0,0 +1,37 @@
+ Sterben ist in diesem Mud recht leicht.
+ Einige recht sichere Moeglichkeiten sind:
+ - In die Steinbeisserhoehle, s,s,o,n,n,
+ - In den Schacht der F&E (Archie), 15 mal "unten" tippen
+ - Bei den Raeubern "vorsicht 0", "toete alle"
+ - In Yantros Haus der Farben im schwarzen Raum: beschwoere Tod
+ - In der Daemonenkneipe (Zardoz' Burg): kaufe liqour manor
+ - Pandora: Beim Skispringen die Skier ausziehen
+ - Bei der Hydra solange Koepfe abschlagen und NICHT ausbrennen,
+   bis ca. 50 Stueck da sind, DANN den Koerper angreifen.
+ - Auf Tamibar: toete haiavaha (Noch sicherer: Man sage Bowan's
+   Spruch aus "Traumpark" auf...)
+ - In Orkhausens Katakomben: nur ein wenig kaempfen und idlen
+ - Im Tal der Lupinen am zerstoerten Schrein: bete
+ - Als Seher: "fluchtrichtung egalwohin", "toete weghier"
+ - In Fekesh's Laden "kaufe tamahorny", 3x "schlage tamahorny"
+ - In der Sauna "nimm handtuch", "toete masseur"
+ - Als Elf: Dunkelelfen toeten, "nimm runenschwert", "zuecke runenschwert"
+ - Als Zauberer: In der Archie-Kneipe einen Grog und eine Cola 40 trinken,
+   anschliessend "verletze architekt"
+ - Als Karateka: Steintroll angreifen
+
+ Generelle Tipps:
+ - "vorsicht 0" ist bei vereinzelten Gegnern angebracht; wenn aggressive
+   Monster in den Nebenraeumen stehen, ist hohe vorsicht sinnvoller.
+ - "toete alle" bei vielen starken Gegnern fuehrt recht sicher zum Tod
+ - ohne Ruestung stirbt man leichter
+ - Als Seher: "fluchtrichtung -B Ich will sterben!"
+ - Key fragen, der kann brauchbare Tips geben
+ - Monster von Rochus oder Patryn angreifen 
+
+ SIEHE AUCH:
+    toete, vorsicht, fluchtrichtung, kampf
+
+ LETZTE AeNDERUNG:
+    12.12.2011, Zesstra
+
diff --git a/doc/help/stufen b/doc/help/stufen
new file mode 100644
index 0000000..1c4732d
--- /dev/null
+++ b/doc/help/stufen
@@ -0,0 +1,53 @@
+
+stufen
+======
+
+    Seine (Spieler-)Stufe kann man in jeder Gilde erhoehen. Die einzige
+    Voraussetzung dabei ist es, genuegend Stufenpunkte zu haben.
+
+    Ab Stufe 9 gilt:
+    Um auf Stufe <x> aufsteigen zu koennen, benoetigt man <x-4>*100 dieser
+    Stufenpunkte.
+    Bis Stufe 9 kosten die Stufen weniger als 100 Stufenpunkte (s. Tabelle).
+
+    Die einzige Ausnahme ist der Aufstieg vom Spieler zum Seher oder Magier:
+     Um Seher zu werden gibt es zusaetzliche Bedingungen, wie bestimmte
+     Anzahlen von Abenteuerpunkten, Forscherpunkten und Erstkills. Die
+     aktuelle Liste ist unter der Hilfeseite "seher" zu finden.
+     Magier kann man nach Erreichen des Seherstands auf Anfrage bei einem
+     hoeheren Magier werden.
+
+    Bis zur Stufe 19 sehen die Stufenbezeichnungen fuer Abenteurer wie folgt
+    aus:
+
+      Stufe  St.pkte        Maennlich               Weiblich          
+       19     1500      der angehende Seher     die angehende Seherin     
+       18     1400      der Held                die Heldin                
+       17     1300      der Eroberer            die Erobererin            
+       16     1200      der Entdecker           die Entdeckerin           
+       15     1100      der Feuerbaendiger      die Feuerbaendigerin      
+       14     1000      der Schatzsucher        die Schatzsucherin        
+       13      900      der Draufgaenger        die Draufgaengerin        
+       12      800      der Weltenbummler       die Weltenbummlerin       
+       11      700      der Abenteurer          die Abenteurerin          
+       10      600      der Reisende            die Reisende              
+        9      500      der Kundschafter        die Kundschafterin        
+        8      420      der Jaeger              die Jaegerin              
+        7      340      der Wegekundige         die Wegekundige           
+        6      280      der Faehrtensucher      die Faehrtensucherin      
+        5      220      der Waldlaeufer         die Waldlaeuferin         
+        4      180      der Wandergeselle       die Wandergesellin        
+        3      140      der Streuner            die Streunerin            
+        2      120      der Landstreicher       die Landstreicherin       
+        1      100      der hoffnungsvolle      die hoffnungsvolle        
+
+    Seherstufen tragen keinen Titel, da jeder Seher seinen Titel selbst frei
+    waehlen darf.
+
+ SIEHE AUCH:
+    seher, magier, magierstufen
+    forscherpunkte, stufenpunkte
+    abenteuer
+
+ LETZTE AeNDERUNG:
+    04.05.2013, Zesstra
diff --git a/doc/help/stufenpunkte b/doc/help/stufenpunkte
new file mode 100644
index 0000000..b16f470
--- /dev/null
+++ b/doc/help/stufenpunkte
@@ -0,0 +1,56 @@
+
+Stufenpunkte
+============
+
+    Die Stufenpunkte bilden die Grundlage des neuen Stufenkonzeptes im
+    MorgenGrauen. Anstatt wie frueher nur durch Erfahrungspunkte und in
+    gewissem Masse Abenteuerpunkte wird die Moeglichkeit zum Aufstieg nun auch
+    noch von einigen anderen Faktoren bestimmt, die insgesamt eine
+    ausgewogenere Einschaetzung des Charakters ermoeglichen sollen:
+
+    Den wichtigsten Anteil bilden nun die Abenteuerpunkte. Sie gehen im
+    Verhaeltnis 1:1 in die Stufenpunkte ein.
+
+    Der zweitgroesste Anteil wird von den Forscherpunkten gestellt. Diese
+    Punkte werden einem zwar nicht direkt als Zahl bekannt gegeben; mit dem
+    Befehl `info' (auch als `score' oder `punkte' bekannt) kann man jedoch
+    eine Einschaetzung der Mud-Kenntnis abfragen.
+
+    Den naechsten Anteil liefern gefundene Zaubertraenke und ein weiteres
+    Level die Heiltraenke (die letzten vier ZTs).
+
+    Die Gilde, in der man sich befindet, liefert einen vierten Anteil. Hier
+    gehen in der Regel die Kenntnisse der einzelnen Sprueche und der
+    Faehigkeiten ein, die man in der Gilde erlernen kann (das ist aber jeder
+    Gilde selbst ueberlassen). Gilden, aus denen man ausgetreten ist, werden
+    dabei nicht beruecksichtigt! Manche Gilden vergeben sehr viele Stufen-
+    punkte schon bis Level 20. Dann verliert man beim Wechsel einige Stufen,
+    die man in der neuen Gilde aber mit der Zeit wieder erlangt.
+
+    Schliesslich gibt es noch Stufenpunkte fuer das Toeten von Monstern;
+    allerdings muss das Monster dafuer eine bestimmte minimale Menge von
+    Erfahrungspunkten geben. Diese Punkte werden nur fuer das erstmalige
+    Toeten des Monsters vergeben. Es nuetzt also nichts, den selben Ork 27 mal
+    zu toeten; wenn er ueberhaupt Stufenpunkte abwirft, dann nur beim ersten
+    Mal.
+
+    (Ein erweiterter Hinweis zum Thema Erstkill-Stufenpunkte, insbesondere
+     in Zusammenhang mit Kerbholz und  Duesterwald-Plakette, ist in der
+     Hilfeseite "erstkillstufenpunkte" (oder "eks") zu finden.)
+
+    Auch "MiniQuests" koennen Stufenpunkte vergeben. (Miniquests sind kleine
+    Aufgaben, die keine Abenteuerpunkte geben).
+
+    Und zum guten Schluss kommen auch die Erfahrungspunkte noch zu Ehren, wenn
+    auch nicht zu besonders grossen... ;-)
+    Fuer x Erfahrungspunkte erhaelt man x^0.32 Stufenpunkte, so dass im
+    unteren Erfahrungspunktebereich recht schnell Stufenpunkte erzielt
+    werden, nach oben hin jedoch immer weniger Stufenpunkte pro erzieltem
+    Erfahrungspunkt vergeben werden.
+
+ SIEHE AUCH:
+    forscherpunkte, info, stufen, erstkillstufenpunkte, erzmagier
+
+ LETZTE AeNDERUNG:
+    Wed, 09.05.01, 15:00:00 von Rikus
+ 
diff --git a/doc/help/stunnel b/doc/help/stunnel
new file mode 100644
index 0000000..88b46de
--- /dev/null
+++ b/doc/help/stunnel
@@ -0,0 +1,73 @@
+Verschluesselung der MUD-Kommunikation mit stunnel
+==================================================
+
+    Mit 'stunnel' kann man seine saemtliche Kommunikation mit dem MUD
+    verschluesseln. Dazu startet man lokal bei sich stunnel und verbindet
+    sich dann per telnet/tf/... mit diesem Client.
+    In den folgenden Beispielen wird dafuer jeweils localhost:4711 benutzt.
+
+    Stunnel gibt es fuer diverse OS unter: http://www.stunnel.org/
+    
+    Start von stunnel unter Linux uAe:
+    * bis zur Version 3.* mit dem Kommando
+      stunnel -c -d 4711 -r mg.mud.de:4712
+    * ab Version 4.* mit Konfigurationsdatei folgenden Inhalts, gefolgt
+      von Start via "stunnel <konfigurationsdatei>"
+    --
+      foreground = yes
+      client = yes
+
+      [mg]
+      connect = mg.mud.de:4712
+      accept = localhost:4711
+    --
+
+    Start von stunnel unter Windows:
+    * Installieren/Starten, dann unter Configuration die Datei stunnel.conf
+      editieren und bei 'Service definitions' folgende Zeilen hinzufuegen:
+    --
+      client = yes
+      [mg]
+      connect = mg.mud.de:4712
+      accept = 127.0.0.1:4711
+    --
+
+ BEMERKUNGEN:
+    Der Server unterstuetzt Kompression, zB durch die Konfigurationszeile:
+      compression = zlib
+    ausserhalb des Profils.
+    
+    Auszugsweise momentan moegliche Chiffren sind, nach Staerke aufsteigend:
+      DES-CBC-SHA           SSLv3 Kx=RSA    Au=RSA  Enc=DES(56)   Mac=SHA1
+      RC4-SHA               SSLv3 Kx=RSA    Au=RSA  Enc=RC4(128)  Mac=SHA1
+      RC4-MD5               SSLv3 Kx=RSA    Au=RSA  Enc=RC4(128)  Mac=MD5 
+      CAMELLIA128-SHA       SSLv3 Kx=RSA    Au=RSA  Enc=Camellia(128) Mac=SHA1
+      DHE-RSA-AES128-SHA    SSLv3 Kx=DH     Au=RSA  Enc=AES(128)  Mac=SHA1
+      DES-CBC3-SHA          SSLv3 Kx=RSA    Au=RSA  Enc=3DES(168) Mac=SHA1
+      EDH-RSA-DES-CBC3-SHA  SSLv3 Kx=DH     Au=RSA  Enc=3DES(168) Mac=SHA1
+
+      AES128-SHA            SSLv3 Kx=RSA    Au=RSA  Enc=AES(128)  Mac=SHA1
+      AES128-SHA256         TLSv1.2 Kx=RSA  Au=RSA  Enc=AES(128)  Mac=SHA256
+      AES128-GCM-SHA256     TLSv1.2 Kx=RSA  Au=RSA  Enc=AESGCM(128) Mac=AEAD
+      AES256-SHA            SSLv3 Kx=RSA    Au=RSA  Enc=AES(256)  Mac=SHA1
+      AES256-SHA256         TLSv1.2 Kx=RSA  Au=RSA  Enc=AES(256)  Mac=SHA256
+      AES256-GCM-SHA384     TLSv1.2 Kx=RSA  Au=RSA  Enc=AESGCM(256) Mac=AEAD
+
+      DHE-RSA-CAMELLIA256-SHA SSLv3 Kx=DH   Au=RSA  Enc=Camellia(256) Mac=SHA1
+      DHE-RSA-AES256-SHA    SSLv3 Kx=DH     Au=RSA  Enc=AES(256)  Mac=SHA1
+      DHE-RSA-AES256-SHA256 TLSv1.2 Kx=DH   Au=RSA  Enc=AES(256)  Mac=SHA256
+      DHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=DH Au=RSA  Enc=AESGCM(256) Mac=AEAD
+    
+    Einstellbar ist das bei speziellen Wuenschen (Vorschriften
+    zur Verschluesselungsstaerke im Ausland zB) mit:
+      ciphers = RC4-SHA
+    innerhalb des Profils. Fuer einige muss allerdings
+      fips = no
+    gesetzt werden, da sie als nicht mehr sicher genug gelten.
+ 
+ SIEHE AUCH:
+    teamspeak, mudrechner
+    Details zu Zertifikaten: http://mg.mud.de/download/stunnel.shtml
+
+ LETZTE AeNDERUNG:
+3. Aug 2015 Gloinson
diff --git a/doc/help/syntax b/doc/help/syntax
new file mode 100644
index 0000000..2315c37
--- /dev/null
+++ b/doc/help/syntax
@@ -0,0 +1,60 @@
+
+Die Syntax der Eingaben im MorgenGrauen
+=======================================
+
+    Eine Eingabe sollte im allgemeinen folgendermassen aussehen:
+
+    > <verb> [<objekt(e)>] [<adverb> [und <adverb>]]
+
+    Gross- und Kleinschreibung sowie Artikel koennen in den meisten Faellen
+    benutzt werden. Das Verb MUSS allerdings IMMER klein geschrieben werden!
+    Folgende Ausdruecke sollten also aequivalent sein:
+
+    > steck das Schwert in den Beutel
+    > steck schwert in beutel
+
+    Umlaute und andere Sonderzeichen (Zeichen mit ASCII-Codes >= 127) sind
+    verboten und werden durch `?' ersetzt.
+
+    Wenn nicht genau klar ist, welches Objekt gemeint ist, kann man das
+    gewuenschte Objekt mit einer angehaengten Nummer identifizieren:
+
+    > steck schwert 2 in beutel
+    > steck das 2. Schwert in den Beutel
+
+    Folgendes geht allerdings nicht:
+
+    > steck das zweite Schwert in den Beutel
+
+    Solltest Du irgendwo im Spiel auf eine Stelle stossen, wo keine
+    grammatikalisch richtige Eingabe erwartet wird, gib bitte einem Magier
+    Bescheid.
+
+    Wenn Du bei der Verwendung eines Adverbs in Zusammenhang mit einem Verb
+    Probleme hast, so vergewissere Dich, ob das Verb ueberhaupt die Verwendung
+    von Adverbien unterstuetzt.
+
+    Bei Verben, die die Verwendung von Adverbien unterstuetzen, kannst Du auch
+    einen Text nach Wahl benutzen und zwar folgendermassen:
+
+    > <verb> /<text_nach_wahl>
+
+    Viel Spass beim Ausprobieren.
+
+ BEISPIELE:
+
+    > nimm buch
+    > stecke buch in tasche
+    > ziehe schild an
+    > nimm schwert aus leichnam 2
+
+    > grinse
+    > grinse jamm und jofi (jamm und jofi muessen natuerlich
+                            in der "adverb"-Liste stehen.)
+    > grinse /was-neues-was-auch-nicht-in-der-"adverb"-Liste-steht
+
+ SIEHE AUCH:
+    verben, adverb, idee
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/help/tanjian b/doc/help/tanjian
new file mode 100644
index 0000000..3642bd0
--- /dev/null
+++ b/doc/help/tanjian
@@ -0,0 +1,33 @@
+
+Die Gilde der Tanjian
+---------------------
+
+          #          Die Gilde der Tanjian befindet sich im Wald der 
+          ###        Eistannen, suedoestlich eines dunklen Waldes, der
+          ##         angeblich boese Drakonier beherbergt. 
+          #    ##     
+   ################  Die Tanjian betrachten sich als Wahrer der Neutralitaet,
+    ##   ##     ##   daher wenden sie sich sowohl gegen heilige als auch
+         #      ##   gegen satanische Lebewesen.
+        ##     ##     
+       ##      ##    Der Tanjian lernt den Umgang mit verschiedensten Fern-
+      ##       ##    und Nahkampfwaffen, gleichzeitig strebt er danach, die
+    ##     #  ##     Kraefte des Universums in Form von maechtigen Spruechen
+  ###       ####     fuer sich zu nutzen.
+ #           #         
+                     Naeheres zu der Gilde erzaehlt einem gerne der
+ Lehrmeister der Gilde, Siamil, oder der Schulleiter Orthosius. 
+ 
+ Um sich der Tanjiangilde anzuschliessen sollte Deine Stufe mindestens der
+ 5. entsprechen, all Deine Attribute 5 oder mehr betragen, Dein Charakter
+ weder satanisch noch heilig sein und Du sollst ein Alter von wenigstens 
+ einem Tag aufweisen. Zusaetzlich benoetigst Du 50 Abenteuerpunkte.
+
+ Wer einmal Mitglied der Chaos- oder Klerikergilde war, kann der Tanjiangilde
+ nicht mehr beitreten!
+                         
+ SIEHE AUCH:
+    gilden
+
+ LETZTE AeNDERUNG:
+    Mon, 17. Mai 2004, 17:10:00 von Nibel
diff --git a/doc/help/teamkampf b/doc/help/teamkampf
new file mode 100644
index 0000000..1af65c3
--- /dev/null
+++ b/doc/help/teamkampf
@@ -0,0 +1,155 @@
+Teamkampf
+=========
+
+Gruenden eines Teams und Aufnahme neuer Mitglieder
+--------------------------------------------------
+team folge            Ein Spieler kann mit <team folge name> beantragen,
+                      dass <name> ihn in sein Team aufnimmt.
+
+team aufnahme         Wenn ein Spieler die Aufnahme ins Team beantragt hat,
+                      kann der Teamleiter ihn mit <team aufnahme name>
+                      aufnehmen. Wenn man noch nicht in einem Team ist,
+                      so wird hiermit eins gegruendet.
+                      Von der Aufnahme ausgeschlossen sind Gaeste und
+                      Geister sowie Zweitspieler von Teammitgliedern.
+
+team verlasse         Mit diesem Befehl verlaesst man das Team.
+
+Organisation innerhalb des Teams
+--------------------------------
+team leiter           Der Teamleiter seine Aufgabe an ein anderes Mitglied
+                      abgeben. Schlaeft der Teamleiter ein oder ist laenger
+                      als 10 Minuten inaktiv, koennen auch die anderen
+                      Mitglieder einen neuen Teamleiter ernennen.
+
+team entlasse         Der Teamleiter kann auch Mitglieder entlassen.
+
+Angriffskoordination
+--------------------
+team angriffsbefehl   Ein Teammitglied kann einen Befehl angeben, der
+                      ausgeloest wird, sobald der Teamleiter das Signal
+                      zum Angriff gibt.
+                      Beispiel: <team angriffsbefehl norden> - wenn der
+                      Teamleiter das Signal gibt geht man nach Norden
+                      zum Monster.
+
+team angriff          Der Teamleiter loest den Angriffsbefehl bei allen
+                      Teammitgliedern aus, die im gleichen Raum stehen
+                      und einen Angriffsbefehl eingestellt haben.
+
+Organisation des Teamkampfes
+----------------------------
+                      Teammitglieder haben die Moeglichkeit, ihre Position
+                      innerhalb der Kampfaufstellung zu bestimmen. So koennen
+                      die Kaempfer vorne kaempfen und den Kleriker decken
+                      der sie staendig heilt :-)
+
+team kampfreihe       Jedes Teammitglied kann seinen Positionswunsch mit
+                      <team kampfreihe nr> angeben, wobei <nr> im Bereich
+                      von 1 bis 5 sein muss. Dabei wird jedoch immer die vom
+                      Teamleiter gewuenschte Formation eingehalten.
+                      Durch die Teammitglieder in den vorderen Reihen
+                      werden die in den hinteren Reihen geschuetzt.
+                      Beispielsweise reichen drei Mitglieder in Reihe 1 aus,
+                      zwei in Reihe 2 und eines in Reihe 3 zu schuetzen.
+                      Der Anteil der Schlaege die ein Mitglied abbekommt
+                      entspricht der Anzahl der ungeschuetzen Mitglieder
+                      seiner Reihe (in der ersten Reihe plus 1).
+                      Nur die erste Reihe kann selber im Nahkampf zuschlagen.
+
+team fluchtreihe      Ein Teammitglied kann eine Reihe angeben, in die er
+                      bei der Flucht wechseln moechte statt abzuhauen.
+                      So kann man sich - ausreichende Deckung vorausgesetzt -
+                      in Ruhe von dem mitgebrachten Kleriker heilen lassen.
+                      Wird die Flucht ausgeloest, so wird die Fluchtreihe zur
+                      gewuenschten Kampfreihe. Mit
+                      <team fluchtreihe 0> wird die Fluchreihe deaktiviert.
+                      Falls die Flucht in eine hintere Reihe nicht gelingt,
+                      wird die normale Flucht ausgeloest.
+
+team formation        Der Teamleiter kann bestimmen, innerhalb welcher
+                      Grenzen die Formation eingehalten werden soll.
+                      Dies geschieht mit <team formation min1-max1
+                      min2-max2 min3-max3 min4-max4 min5-max5>. Die
+                      Beschraenkungen fuer nicht angegebene Reihen
+                      werden nicht geaendert. Wenn statt Minimum und
+                      Maximum nur eine Zahl angegeben wird, gilt diese
+                      fuer beide Werte. Die Grenzen werden automatisch so
+                      angepasst, dass sie erfuellbar sind, maxmial 6
+                      Mitglieder in jeder Reihe stehen und mindestens eines
+                      in Reihe 1.
+                      Beispiel: "team formation 3-4 2 1"
+
+team uebersicht       Zeigt die eigenen und gegnerischen Reihen an.
+
+Weitere nuetzliche Teambefehle
+------------------------------
+team autofolge        Teammitglieder, die mit <team autofolge ein> den
+                      Folgemodus aktiviert haben, fuehren automatisch
+                      jeden Befehl des Teamleiters (nach kurzer Verzoegerung)
+                      aus, der einen Ortswechsel bewirkt, vorausgesetzt sie
+                      stehen dort wo der Leiter direkt vor seinem Ortswechsel
+                      war.
+                      Der Teamleiter muss den Folgemodus auch aktiviert haben,
+                      damit die anderen ihm folgen.
+                      Wenn der Teamleiter versucht zu fliehen, wird der
+                      Folgemodus fuer ihn deaktiviert.
+
+team info             Gibt Informationen ueber die Teammitglieder.
+                      Dabei steht in den Spalten:
+                      *      - Kennzeichnung des Teamleiters
+                      Name   - der Name
+                      Gilde  - die Gilde
+                      LV     - Stufe
+                      GLV    - Gildenstufe
+                      LP/MLP - Lebenspunkte
+                      KP/MKP - Konzentrations/Magiepunkte
+                      Vors.  - Vorsicht
+                      GR     - Gewuenschte Reihe
+                      AR     - Aktuelle Reihe (bezogen aufs ganze Team)
+                      TR     - Tatsaechliche Reihe (bezogen auf Anwesende)
+                      FR     - Fluchtreihe
+                      A      - Angriffsbefehl eingestellt
+                      V      - Verfolgemodus aktiviert
+                      Der Teamleiter kann durch die Angabe "lang" eine
+                      zweizeilige Ausgabe bekommen, bei der auch
+                      Angriffsbefehl und Fluchtrichtung gezeigt werden.
+                      Wenn man "sortiert" angibt, wird die Ausgabe nach
+                      (TR,AR,GR,Name) sortiert, bei "alphabetisch" erfolgt
+                      die Sortierung nach dem Namen, wobei Spieler jedoch
+                      vor anderen Teammitgliedern eingeordnet werden.
+                      Ansonsten wird nach AR geordnet.
+
+team farben           Damit laesst sich aendern, ab wann die eigenen
+                      Lebenspunkte bzw. Konzentrationspunkte gelb oder
+                      gar rot (bzw. fett) angezeigt werden.
+
+team orte             Zeigt an, wo sich die Teammitglieder befinden.
+                      Normalerweise werden nur Spieler angezeigt, mit
+                      "alle" jedoch auch andere Teammitglieder.
+
+team rufe             Mitteilung an alle Teammitglieder.
+
+team hist             History der Teammitteilungen. Die Anzahl der
+                      anzuzeigenden Zeilen (bis 100) kann angegeben werden.
+
+team name             Man kann seinem Team auch einen Namen geben.
+
+team hilfe            Gibt eine kurze Befehlsuebersicht.
+
+team liste            Zeigt an, welche Teams es schon gibt.
+
+Punkteverteilung
+----------------
+Die Erfahrungspunkte fuers Toeten eines Monsters werden zur Haelfte
+unter den anwesenden Teammitgliedern und zur Haelfte anteilmaessig
+verteilt. Dies gilt nur fuer das Team des Killers, fuer alle anderen
+ist die Verteilung wie bisher anteilmaessig.
+
+Die Erstkillpunkte werden an ein Teammitglied vergeben, das diese noch
+nicht hat. Dabei werden Teammitglieder in der ersten Reihe bevorzugt.
+
+Abk.
+----
+Statt "team" sind auch "gruppe" und die Abkuerzung "g" moeglich.
+
diff --git a/doc/help/teamspeak b/doc/help/teamspeak
new file mode 100644
index 0000000..dfd1433
--- /dev/null
+++ b/doc/help/teamspeak
@@ -0,0 +1,64 @@
+
+Das Morgengrauen-Teamspeak
+==========================
+
+Teamspeak - was ist das ueberhaupt?
+
+    Teamspeak ist ein Voice-Chat-System, zu dem man sich mit einem geeigneten
+    Client verbinden kann, um sich mit anderen Leuten zu unterhalten. Das
+    Ganze funktioniert im Grunde aehnlich wie Skype, hat aber gegenueber
+    diesem den Vorteil, dass die Kommunikation ueber Server laeuft, die
+    nicht unter der Kontrolle von Fremdfirmen stehen, so dass auch nichts
+    mitgeloggt wird, was dort gesprochen wird.
+
+
+Wofuer braucht man das?
+    
+    Im Teamspeak kann man sich treffen, um gemeinsam Mehrspielerquests zu
+    loesen, groessere Metzelaktionen zu koordinieren oder einfach nur
+    zu quatschen.
+
+    Fuer Magier gibt es separate Channel, in denen man sich zur Diskussion
+    von Programmierthemen und -problemen treffen kann.
+
+
+Bin ich schon drin?
+
+    Wenn Du Lust bekommen hast, unser TS mal auszuprobieren, benoetigst Du
+    folgendes:
+
+    - eine Soundkarte im PC
+    - Lautsprecher und ein Mikrofon, besser jedoch ein Headset
+    - den Teamspeak3-Client von http://www.teamspeak.com
+    - die Zugangsdaten:
+
+      Servername:     mgts.mud.de (sollte ohne Portangabe funktionieren)
+      Serverpasswort: der Name unseres bekannten Urvaters aller Magier
+      Server-IP (falls benoetigt): 85.214.250.188:4711
+
+
+    Im Teamspeak-Client kannst Du diese Daten im Menue unter "Favoriten -
+    Favoriten verwalten" eintragen, indem Du einen neuen Favoriten anlegst
+    und unter "Adresse" den Servernamen eintraegst. Deinen Spielernamen
+    traegst Du noch unter "Nickname" ein, und unter "Server Passwort" 
+    jenes, das Du (hoffentlich) aus dem Hinweis oben ableiten konntest.
+
+    Wenn Du moechtest, kannst Du das MG-TS noch als Standard-Server
+    einstellen, indem Du in der Favoritenverwaltung unten links auf "Mehr"
+    klickst und dann ein Haekchen bei "Beim Start verbinden" setzt. Dann
+    wirst Du bei jedem Starten des TS3-Clients direkt mit dem MG-TS 
+    verbunden.
+
+  
+Noch einige Hinweise:
+
+    Der Server wird derzeit von Olli zur Verfuegung gestellt, um die
+    Moeglichkeit zum Ausprobieren anzubieten, benehmt Euch also, wenn Ihr
+    da drauf seid. :-)
+
+    Die Rechteverwaltung von TS3 ist ausgesprochen komplex geraten, so dass
+    wir um Geduld bitten moechten, wenn irgendetwas nicht auf Anhieb
+    funktioniert.
+
+31.10.2012  Arathorn
+
diff --git a/doc/help/tod b/doc/help/tod
new file mode 100644
index 0000000..b5e2059
--- /dev/null
+++ b/doc/help/tod
@@ -0,0 +1,37 @@
+
+Der Tod und seine Folgen
+========================
+
+    Im MorgenGrauen gibt es viele verschiedene Moeglichkeiten, zu ster-
+    ben. Der haeufigste Grund ist jedoch das Sinken der Lebenspunkte
+    auf unter Null.
+
+    Den Tod bemerkt man an einer mehr oder weniger originellen Todes-
+    sequenz, die automatisch vor einem ablaeuft; in diese kann nicht
+    eingegriffen werden. Ausserdem verliert man als Spieler 33% seiner
+    Erfahrungspunkte. Je mehr Erfahrung man gesammelt hat, desto mehr
+    Punkte verliert man ergo. Damit der Verlust nicht ins Bodenlose 
+    steigen kann, geniessen Seher den Vorteil, mit jeder neuen Stufe
+    weniger Erfahrungspunkte im Todesfall zu verlieren.
+
+    Ein weiterer Effekt des Sterbens ist natuerlich der Verlust des 
+    Koerpers. Aus der Todessequenz kommt man als Geist wieder zurueck
+    auf die Welt. (Tip: "rassle /freundlich" ;-) Um wieder in den Ge-
+    nuss von koerperlichen Freu(n)den zu kommen, muss man einen heiligen
+    Ort aufsuchen und das weltliche Kommando "bete" eingeben.
+    [Chaoten beschwoeren alternativ einen unheiligen Daemonen.]
+
+    Nach dem Tod verliert man zeitlich begrenzt Attribute und buesst
+    Gildenfaehigkeiten ein. Nichtsdestotrotz kann man mit diesem Malus
+    an anderer Stelle im MG weiterspielen. Wer das ueberpruefen moechte,
+    sollte mit "hilfe sterben" eine schnelle legale Moeglichkeit finden,
+    Lars einen Besuch abzustatten oder Rochus eine Mail schicken.
+
+    Lars ist uebrigens der Gehilfe des Todes, und Rochus die linke und
+    die rechte Hand des Todes. :-)
+   
+ SIEHE AUCH:
+    sterben, leben, kampf
+
+ LETZTE AeNDERUNG:
+    14. Maerz 2004 Gloinson
diff --git a/doc/help/topliste b/doc/help/topliste
new file mode 100644
index 0000000..1d986c1
--- /dev/null
+++ b/doc/help/topliste
@@ -0,0 +1,21 @@
+
+ TOPLISTEN:
+ ==========
+
+    In (fast) jeder Kneipe im Morgengrauen findet man eine Topliste.
+    Auf dieser Topliste stehen die 100 Spieler, die die meisten
+    Stufenpunkte haben.
+    Wer - aus welchen Gruenden auch immer - darauf verzichten moechte,
+    in dieser Topliste aufzutauchen (wenn er/sie denn gut genug dafuer
+    ist), kann sich selbst mit dem Kommando
+
+        topliste nein
+
+    fuer die Topliste sperren. Mit dem Kommando
+
+        topliste ja
+
+    wird dies wieder rueckgaengig gemacht.
+
+ LETZTE AeNDERUNG:
+    Son, 30.8.1998, 22:10:00 von Paracelsus
diff --git a/doc/help/urukhai b/doc/help/urukhai
new file mode 100644
index 0000000..1965538
--- /dev/null
+++ b/doc/help/urukhai
@@ -0,0 +1,28 @@
+
+Die Gilde der Urukhai
+---------------------
+
+In Orkhausen im Verlorenen Land gibt es die Gilde der Urukhai. Dies ist die
+Standardgilde der Orks.
+
+Allerdings koennen auch andere Spieler Urukhai werden. Nur Felinen werden
+nicht zugelassen, da die Katzenwesen einfach fehl am Platze waeren. Weitere
+Voraussetzungen, um ein Urukhai zu werden, gibt es nicht, allerdings sollte
+man die richtige Einstellung mitbringen und es gerne blutig moegen.
+
+Hragznor ist der Gildenmeister der Urukhai. Bei ihm kann man der Gilde 
+beitreten und die verschiedenen Sprueche lernen, er weiss auch vieles 
+ueber die Gilde.
+
+Orks koennen von Natur aus gut mit bestimmten Waffen kaempfen. Auch dazu
+weiss Hragznor mehr.
+
+SIEHE AUCH:
+beisse, nachtsicht, identifiziere, schaetz, ruelpse, wirbelwind,
+spucke, hammerfaust, steinhaut, riesenpranke, leichenfledder,
+furcht, trollstaerke
+
+ALS NICHT-URUKHAI MUSS MAN DAFUER FOLGENDE SYNTAX NUTZEN:
+hilfe gilde urukhai <zaubername>
+z.B. hilfe gilde urukhai nachtsicht
+
diff --git a/doc/help/verein b/doc/help/verein
new file mode 100644
index 0000000..a745d66
--- /dev/null
+++ b/doc/help/verein
@@ -0,0 +1,73 @@
+#===================================================================#

+#   DU BIST NOCH NICHT MITGLIED? DANN LIES BESONDERS SORGFAELTIG!   #

+#===================================================================#

+

+Informationen zum Foerderverein Projekt MorgenGrauen e. V.:

+- WWW-Seite: http://mg.mud.de/Verein 

+- MPA unter der Rubrik "morgengrauen.verein"

+

+Zweck des Vereins:

+Foerderung des MUDs MorgenGrauen

+- finanzielle Mittel durch Spenden

+- Betreuung des vereinseigenen Rechners, auf dem das MUD laeuft

+- Kurse und Vortraege fuer Programmierer und Spieler

+  (Natuerlich keine Questloesungen)

+Diese Ziele sind satzungsbedingt. 

+

+Satzung:

+- im Vereinsbuero im Ordner

+- auf den WWW-Seiten des Vereins (auch zum ausdrucken)

+

+Wie werde ich Mitglied?

+- Antrag

+- moeglichst auch Einzugsermaechtigung

+(Siehe WWW-Seite)

+

+Was mach ich als Mitglied?

+- zu den Versammlungen kommen

+- mithelfen, die Zwecke des Vereins zu erfuellen

+- Sponsoren fuer den MG e. V. suchen und dem Vorstand mitteilen

+  (bitte keine Alleingaenge)

+- Kursthemen vorschlagen

+- selbst einen Vortrag halten

+

+Wer ist eigentlich der Vorstand?

+Der Vorstand besteht aus folgenden vier Personen:

+

+1. Vorsitzender:   Xutelius
+                   Henning Ullrich
+                   Himmelpfortener Weg 52
+                   59823 Arnsberg
+
+2. Vorsitzender:   Gloinson
+                   Christian Becker
+                   Abt-Thomas-Strasse 40
+                   88682 Salem
+
+Kassenwart:        Vanion
+                   Christian Fiedler
+                   Athenstr. 26
+                   26384 Wilhelmshaven
+
+Schriftfuehrer:    Guard
+                   Rene Waechter
+                   Karl-Kunger-Str. 66                 offizielle
+                   12435 Berlin                        Vereinsadresse!!
+
+Unsere Kontoverbindung:
+
+IBAN DE09 2802 0050 9204 1417 00
+SWIFT-BIC OLBODEH2XXX
+Kontonummer: 9204141700
+Oldenburgische Landesbank AG
+BLZ: 280 200 50
+
+Bei Fragen ueber den Verein koennt Ihr Euch direkt an eine dieser vier

+Personen wenden. Ihr erreicht alle Vorstandsmitglieder auch per E-Mail

+unter der Adresse 

+                        vorstand@mg.mud.de.

+

+#===================================================================#

+#              WAS ZOEGERST DU NOCH, WERDE MITGLIED!                #

+#===================================================================#

+

diff --git a/doc/help/waffenfertigkeiten b/doc/help/waffenfertigkeiten
new file mode 100644
index 0000000..e6a5285
--- /dev/null
+++ b/doc/help/waffenfertigkeiten
@@ -0,0 +1,33 @@
+Allgemeine Waffenfertigkeiten:
+==============================
+
+Bestimmte Gilden erlernen Waffenfertigkeiten. Dies sind bisher die
+Mitglieder folgender Gilden:
+
+ - Abenteurer
+ - Bierschuettler
+ - Chaos
+ - Katzenkrieger
+ - Wipfellaeufer
+
+Die Spieler dieser Gilde koennen das Kaempfen mit den Waffengattungen 
+Keulen, Staebe, Aexte, Speere, Messer, Schwerter und Peitschen durch 
+Benutzung dieser Waffen erlernen. Je besser man mit den Waffen umgehen
+kann, desto mehr Schaden kann man bei seinem Gegner verursachen.
+
+Neben der Faehigkeit im Umgang mit den Waffen spielt auch die Waffe
+eine Rolle, sowie die eigene Kraft und Geschicklichkeit. Dabei sollte
+man beruecksichtigen, dass die Waffen unterschiedliche Anforderungen 
+an das Geschick stellen. So ist die Benutzung einer Peitsche sehr
+komplex, zum Kampf mit einem Messer muss man sich ebenso recht
+geschickt anstellen, dicht gefolgt vom Kampf mit Speeren. Recht 
+ausgeglichen zwischen Geschicklichkeit und Kraft ist der Kampf mit einem
+Schwert. Die Benutzung von Aexten erfordert noch einiges Geschick, waehrend
+man bei Keulen am ehesten mit purer Kraft bedient ist.
+
+Wer wissen moechte, wie gut er den Umgang mit den Waffen bereits
+beherrscht, kann dazu einen Kaempfer (mindestens im Range eines
+Unteroffiziers) befragen. Der sollte dies beurteilen koennen. 
+
+
+Letzte Aenderung: 2012-08-26 von Arathorn.
diff --git a/doc/help/werwoelfe b/doc/help/werwoelfe
new file mode 100644
index 0000000..9a04f3a
--- /dev/null
+++ b/doc/help/werwoelfe
@@ -0,0 +1,13 @@
+-----------------------
+Die Gilde der Werwoelfe
+-----------------------
+
+Man kann auf der Werwolfinsel Mitglied der Gilde der Werwoelfe werden.
+Dazu muss man Mondheuler finden und bei ihm in die Gilde eintreten.
+
+Was die Werwoelfe koennen und welche Faehigkeiten sie haben kann man 
+dann von diversen Gildenmitgliedern erfragen.
+
+Eine Einfuehrung kann man bekommen, wenn man Mondheuler nach Hilfe fragt.
+
+Letzte Aenderung: 1.11.2013, Humni
diff --git a/doc/help/willkuer b/doc/help/willkuer
new file mode 100644
index 0000000..e78e038
--- /dev/null
+++ b/doc/help/willkuer
@@ -0,0 +1,9 @@
+Willkuer
+========
+        Willkuer ist subjektiv.
+
+ SIEHE AUCH:
+        goetter, erzmagier
+
+ LETZTE AeNDERUNG:
+     morgen, Gloinson i.A. Vanion
diff --git a/doc/help/wipfellaeufer b/doc/help/wipfellaeufer
new file mode 100644
index 0000000..3d662f5
--- /dev/null
+++ b/doc/help/wipfellaeufer
@@ -0,0 +1,71 @@
+GILDE DER WIPFELLAEUFER
+-----------------------
+
+Ueber dem heiligen Hain der Elfen befindet sich die Gilde der Wipfellaeufer.
+Illdereth, der Gildenvorsteher, bringt jungen Elfen und allen, die sonst der
+Gilde beitreten moechten, alles bei, was die Wipfellaeufer wissen muessen.
+
+EINSTUFUNG DER GILDE
+--------------------
+
+Die Wipfellaeufer sind eine explizite Anfaengergilde und als Anfaengergilde
+der Elfen ausgelegt. Allerdings werden sie im Laufe der Stufen auch durchaus
+in vielen Bereichen noch besser, so dass ein paar ihrer Faehigkeiten auch 
+fuer groessere Spieler interessant sein koennen.
+
+PRINZIPIEN
+----------
+
+Wipfellaeufer sind Kreaturen des Waldes, in der Regel Elfen, fuer die diese
+Gilde auch die Anfangsgilde ist. Sie verstehen sich auf das Bogenschiessen, 
+was vor allem dann sehr gut nutzbar ist, wenn sich jemand schuetzend im Kampf
+vor sie stellt.
+
+Ferner beherrschen sie ein paar Angriffszauber und kennen natuerlich die
+elementaren Dinge, die fuer das Ueberleben im Wald noetig sind.
+
+SONSTIGES
+---------
+
+Die wichtigsten Informationen ueber die Wipfellaeufer kennt Illdereth, 
+ansonsten kann man andere Mitspieler auf der Wipfellaeufer-Ebene befragen.
+
+Die Wipfellaeufer-Ebene kann man mit -wipfel+ betreten. (Mehr dazu unter 
+"hilfe ebenen").
+
+BESCHRAENKUNGEN
+---------------
+
+Zwerge koennen keine Wipfellaeufer werden. Die zwergischen Prinzipien des
+Stollenbaus wuerden auch nicht dazu passen, und die meisten Zwerge haben auch
+gar kein Interesse daran.
+
+Ferner koennen Dunkelelfen aufgrund des alten Hasses zwischen den Voelkern
+auch keine Wipfellaeufer werden.
+
+ZAUBERSPRUECHE
+--------------
+
+gluehwuermchen
+identifiziere
+schaetz
+regenguss
+baumrinde
+hagelschlag
+pfeilschnell
+blitz
+
+Gildenmagier
+------------
+
+Die Gilde wurde von Humni geschrieben, der derzeit auch dafuer zustaendig ist.
+
+Siehe auch: 
+-----------
+gilden, (die einzelnen Zaubersprueche), bogenschiessen
+
+* WENN MAN EINER ANDEREN GILDE ANGEHOERT, MUSS MAN FUER DIE HILFE
+,,hilfe gilde wipfellaeufer <thema>'' EINGEBEN! *
+
+Letzte Aenderung: Humni, 2011-10-27
+
diff --git a/doc/help/www b/doc/help/www
new file mode 100644
index 0000000..7d9638a
--- /dev/null
+++ b/doc/help/www
@@ -0,0 +1,19 @@
+Die Webseite(n)
+===============
+
+     Auf folgenden Webseiten finden sich Informationen ueber unser
+     MUD und dazugehoerige Projekte:
+
+     Offizielle Seite: http://mg.mud.de
+     * ermoeglicht Zugriff auf MPA/Archive
+     * enthaelt einen Web-Client und Links auf Clients
+     * enthaelt Informationen zum Ausrichten und Bilder von Parties
+     * enthaelt die Vereins-Seite: http://mg.mud.de/verein
+
+     Die Wiki-Seite: http://http://wiki.morgengrauen.info/
+     * auch von Nichtmagiern editierbare Projektseiten
+
+SIEHE AUCH:
+     mudrechner, client, teamspeak, irc
+
+26. Aug 2013 Gloinson
diff --git a/doc/help/zauberei b/doc/help/zauberei
new file mode 100644
index 0000000..11cd502
--- /dev/null
+++ b/doc/help/zauberei
@@ -0,0 +1,37 @@
+
+Zauberei im MorgenGrauen
+========================
+
+    Die meisten Gilden stellen ihren Mitgliedern mehr oder weniger nuetzliche
+    Zaubersprueche zur Verfuegung. Genaueres zu den einzelnen Spruechen
+    findest Du auf den jeweiligen Hilfeseiten.
+
+    Allgemein laesst sich sagen, dass die Anwendung von Magie im wesentlichen
+    an die Intelligenz des Charakters gekoppelt ist.
+
+    So verbrauchen die Sprueche in der Regel mehr oder weniger viele
+    Magiepunkte. Deren Anzahl wiederum haengt direkt von der Intelligenz ab,
+    kann aber auch durch andere Magie erhoeht werden.
+
+    Lernt man einen Spruch neu dazu, so wird ihm ein Anfangswert fuer die
+    Erfolgswahrscheinlichkeit zugeordnet. Auch dieser Anfangswert haengt bei
+    den meisten Gilden von der Intelligenz ab (hier koennen aber auch noch
+    weitere Faktoren wirksam werden).
+
+    Die Erfolgswahrscheinlichkeit eines Spruches verbessert sich, wenn der
+    Spruch fehlschlaegt (`Du lernst aus Deinem Fehler.'). Hier muss man
+    allerdings aufpassen: Einige Sprueche haben eine Beschraenkung der maximal
+    erreichbaren Wahrscheinlichkeit. Die Schranke wandert mit zunehmender
+    Spielerstufe weiter nach oben. Hat man den fuer seine Stufe maximalen Wert
+    erreicht, so kann es bei einigen Gilden immer noch zu der Lernmeldung
+    kommen, obwohl man eigentlich nichts dazulernt!
+
+    Die Verbesserung der Wahrscheinlichkeit im Falle eines Fehlschlags haengt
+    uebrigens wiederum in den meisten Faellen von der Intelligenz ab.
+
+ SIEHE AUCH:
+    attribute, gilden, heilung
+
+ LETZTE AeNDERUNG:
+    18. Maerz 2004 Gloinson
+
diff --git a/doc/help/zauberer b/doc/help/zauberer
new file mode 100644
index 0000000..846a375
--- /dev/null
+++ b/doc/help/zauberer
@@ -0,0 +1,36 @@
+
+Die Gilde der Zauberer
+----------------------
+
+    Die Gilde der Zauberer befindet sich in Taramis und ist im Moment nur
+    ueber ein magisches Tor zu erreichen. Um der Gilde beitreten zu koennen,
+    muss man den zweiten Level erreicht haben und 50 Abenteuerpunkte
+    aufweisen. Insgesamt gibt es zehn Stufen. Davon sind die ersten vier
+    Anfaengern vorbehalten. Vor dem Erreichen der fuenften Stufe muss man die
+    Lehrlingspruefung ablegen. Nach Bestehen der Pruefung bekommt man seinen
+    Zauberstab, der eine grosse Hilfe fuer den Zauberer ist.
+
+    Danach kann man bis zur Stufe Acht aufsteigen. Vor dem Erreichen der Stufe
+    Neun muss die Magisterpruefung abgelegt werden. Im Gegensatz zur
+    Lehrlingspruefung ist die Magisterpruefung aeusserst schwer. Danach steigt
+    man zum Magister auf und kann einen Magisterposten in einem der Zweige
+    beantragen. Aus spieltechnischen und balancetechnischen Gruenden ist der
+    Rang eines Erzmagisters momentan nicht von Spielern zu erreichen.
+
+    Die Zaubersprueche, die der Zauberer lernen kann, unterteilen sich in
+    sechs Zweige: Angriff, Beherrschung, Hellsicht, Illusion, Schutz und
+    Verwandlung.
+    Zu Beginn lernt man aus jedem Zweig einen Spruch. Nach der
+    Lehrlingspruefung kann man auf jeder Stufe (5-8) je einen Spruch aus einem
+    Zweig lernen. Als Magister kann man sich fuer einen Zweig entscheiden und
+    dort zwei Magistersprueche lernen. 
+
+
+ SIEHE AUCH:
+    attribute, ebenen, gilden
+
+    Angehoerige fremder Gilden koennen sich in der Bibliothek der Zauberer
+    ueber die einzelnen Sprueche und Zweige informieren.
+
+ LETZTE AeNDERUNG:
+    Die, 03.10.2006, 20:32:00 von Chagall
diff --git a/doc/help/zaubertraenke b/doc/help/zaubertraenke
new file mode 100644
index 0000000..2c65860
--- /dev/null
+++ b/doc/help/zaubertraenke
@@ -0,0 +1,64 @@
+
+Zaubertraenke
+=============
+
+ ZAUBERTRAeNKE:
+    Im ganzen MorgenGrauen sind Zaubertraenke (ZT) versteckt, welche die
+    Attribute erhoehen. Da das Spiel aber inzwischen so gross geworden ist,
+    dass man sie ohne Hilfe praktisch nicht mehr finden kann, gibt es das
+    Orakel von Tingan. Dort bekommt man in Abhaengigkeit von den
+    Stufenpunkten konkrete Hinweise, wo man einen Zaubertrank finden kann.
+    Natuerlich bekommen Anfaenger zuerst die leichtesten Verstecke verraten,
+    der Schwierigkeitsgrad steigert sich dann im Spielverlauf. So sollte es
+    immer moeglich sein, mit den Tipps auch etwas anzufangen (siehe DETAILS).
+
+    Konkret sieht das so aus:
+
+     * 8 Tipps kann man sich direkt bei Spielbeginn umsonst holen
+     * 26 Tipps gibt es fuer jeweils 25 Stufenpunkte
+     * 26 Tipps gibt es fuer jeweils 40 Stufenpunkte
+     * 10 Tipps gibt es fuer jeweils 70 Stufenpunkte
+     * 10 Tipps gibt es fuer jeweils 100 Stufenpunkte
+
+    Natuerlich muss man keine Stufenpunkte bezahlen, man muss sie nur haben.
+
+    ACHTUNG: Man kann die Zaubertraenke erst finden, wenn man den
+    entsprechenden Hinweis vom Orakel bekommen hat.
+
+    Wo ist das Orakel?
+    Das Orakel kann man finden, wenn man sich den westlichen Teil der
+    Hochebene etwas genauer anschaut. Wie man die Hochebene findet? Das kann
+    jeder Spieler mit ein bisschen Spielerfahrung erklaeren, also fragt ruhig
+    ein bisschen herum.
+
+    Wie sehen die Hinweise aus?
+    Natuerlich orakelmaessig verschluesselt, aber nicht *zu* undurchsichtig.
+    Man sollte in der Regel schon etwas damit anfangen koennen, das heisst
+    aber nicht, dass man ueberhaupt nicht mehr suchen oder nachdenken muss.
+    Damit man sich nicht alles aufschreiben muss, merkt sich der Spieler die
+    momentan aktuellen Sprueche automatisch, man kann sie mit dem Befehl
+    `zaubertraenke' abrufen. Wenn der Zaubertrank gefunden wurde, verschwindet
+    auch der entsprechende Hinweis aus der Liste.
+    Fuer einige Zaubertraenke gibt es mehrere Sprueche, die in der Liste
+    zufaellig ausgewaehlt und angezeigt werden.
+
+ DETAILS:
+    Technisch genauer: Ein Spieler bekommt bei seinem ersten Login eine Liste
+    von ZTs zugeordnet. Diese Liste ist eine Auswahl aus allen existierenden
+    ZTs.
+    Das Orakel praesentiert einem dann jeweils die leichtesten ZTs aus dieser
+    eigenen Liste (und nicht aus allen ZTs!)
+    Das heisst also, dass euch durch andere Spieler bekannte, leichte ZTs
+    nicht unbedingt auch eure ZTs sein muessen. Und es kann bedeuten, dass
+    eure leichten ZTs etwas schwerer sind. Nicht aufgeben!
+
+    Wenn euch ZTs an nur in einer Quest erreichbaren Stellen auffallen, so
+    wendet euch bitte an den Erzmagier, der fuer die ZTs zustaendig ist
+    (siehe hilfe erzmagier).
+
+ SIEHE AUCH:
+    attribute, stufenpunkte
+
+ LETZTE AeNDERUNG:
+    25. Nov 2007 Gloinson
+
diff --git a/doc/help/zeitung b/doc/help/zeitung
new file mode 100644
index 0000000..cd1c28e
--- /dev/null
+++ b/doc/help/zeitung
@@ -0,0 +1,21 @@
+
+Zeitung im Morgengrauen
+=======================
+
+ BESCHREIBUNG:
+     In Morgengrauen gibt es eine fuer alle verfuegbare Zeitung, die
+     Morgengrauen Presse-Agentur (MPA). Besuche einen Startraum deiner
+     Wahl und nimm dir eine mit.
+
+     Die Zeitung selbst hat dann eine eigene Hilfefunktion.
+
+ BEMERKUNGEN:
+     Eine der wichtigsten Rubriken ist die Rubrik "megaschmarrn". Dort
+     werden alle (!) relevanten Themen besprochen. Regeln siehe
+     Rubrik.
+
+ SIEHE AUCH:
+     teile mit, ebenen
+
+ LETZTE AeNDERUNG:
+    5. Oktober 2004 Gloinson
diff --git a/doc/help/zookeln b/doc/help/zookeln
new file mode 100644
index 0000000..ee6329f
--- /dev/null
+++ b/doc/help/zookeln
@@ -0,0 +1,16 @@
+
+Extra fuer Mesirii gibt es nun auch zum Thema zookeln eine Hilfeseite:
+
+ZOOKELN (TM):  Versehentliches Gelaber auf der falschen Ebene. Besonders
+               spannend, wenn man gerade ueberaus wichtige und geheime
+               Dinge auf -all statt auf -goetter erzaehlt. Oftmals voellig
+               zusammenhangsloses Gelaber, mit dem nur die was anfangen 
+               koennen, die die Ebene eingeschaltet haben, auf der dieser
+               Text gesendet werden sollte.
+
+BEMERKUNGEN:   Ueberfluessig
+
+SIEHE AUCH:    zook
+---------------------------------------------------------------------------
+Letzte Aenderung: 19.10.2005  Miril
+
diff --git a/doc/help/zweitspieler b/doc/help/zweitspieler
new file mode 100644
index 0000000..bc57f74
--- /dev/null
+++ b/doc/help/zweitspieler
@@ -0,0 +1,23 @@
+
+Zweitspieler
+============
+
+ BESCHREIBUNG:
+     Zweitspieler sind Spieler, welche man zusaetzlich zu seinem Haupt-
+     charakter erschaffen hat. Hauptsaechlich nutzt man sie, um fuer den
+     Erstie Sachen zu schleppen, zu sterben, um andere Gilden zu testen oder
+     gar Magier zu werden.
+
+ BEMERKUNGEN:
+     Jeder Spieler im MUD ist verpflichtet, seine Zweities zu markieren, um
+     damit Verantwortlichkeiten besser zuordnen zu koennen.
+
+     Diese Markierung kann man zum Beispiel beim Schmied in der Zwergenstadt
+     oder beim Dorfschreiber im Elfendorf erhalten.  Diese Markierung kann
+     man auch vor anderen Spielern verbergen.
+
+ SIEHE AUCH:
+     zweitiemarkierung, regeln
+
+ LETZTE AeNDERUNG:
+     5. Mai 2004 Gloinson
diff --git a/doc/hilfe.magier b/doc/hilfe.magier
new file mode 100644
index 0000000..92e88d7
--- /dev/null
+++ b/doc/hilfe.magier
@@ -0,0 +1,36 @@
+
+  ---------------------------MAGIERHILFSSEITE------------------------------
+
+Als Magier hat man neben den Spieler- und Seherbefehlen einige weitere
+Befehle zur Verfuegung. Bei der Anzeige der Hilfeseiten wird nicht zwischen
+den Stufen der Magier unterschieden wird. Diese wird aber bei Erlaeuterung
+beruecksichtigt:
+
+Allgemeine Themen und REGELN:	    |  Kommunikation:
+    balance, forscherpunkte,	    |      echoall, echoto, echo, mecho,
+    testspieler, zweitspieler,	    |      mrufe, oropax, remote
+    effizienz, objekte		    |
+				    |
+Bewegung:			    |  Kampf:
+    goto, trans, home, verfolge,    |      zap, frieden, sethands
+    at, in, +			    |
+				    |
+Dateisystem:			    |  Objekte:
+    cd, pwd, ls, cat, head, tail,   |      clone, destruct, upd(ate), load,
+    more, grep, cp, mv, rm, mkdir,  |	   setcmsg, setdmsg
+    rmdir, set, prompt, ed	    |
+				    |  Anderes:
+Information:                        |     heile, zwinge, titel, extralook,
+    mschau, people, pwho, man,      |     traenke, invis, vis, protect, do
+    snoop, sallow, spieler, rman,   |     showpresay, ping, todo
+    localcmd			    |
+				    |  Erzmagierbefehle:
+Regionsmagierbefehle:		    |     addmaster, removemaster, mbanish,
+    regionsmagier, banish, exec     |     shutdown
+				    |
+
+In den Verzeichnissen unter /doc/ findest du weitere Dokumentation:
+- funktionierende Beispiele unter /doc/beispiele/
+- MG-spezifische Informationen unter /doc/MG/
+- einen Programmierkurs unter /doc/KURS/
+
diff --git a/doc/hilfe.seher b/doc/hilfe.seher
new file mode 100644
index 0000000..5281978
--- /dev/null
+++ b/doc/hilfe.seher
@@ -0,0 +1,22 @@
+
+  ----------------------------SEHERHILFSSEITE------------------------------
+
+Als Seher hat man einige zusaetzliche Befehle. Zu den folgenden Befehlen
+kann man mit "hilfe <thema>" eine Erlaeuterung erhalten.
+
+Kommunikation:			    |  Kampf
+    remote,			    |      fluchtrichtung, sethands, review
+    echo (nach Architektenquest)    |
+				    |
+Bewegungsmeldungen:		    |  Aussehen:
+    setmin, setmout, setmmin,	    |      titel, extralook, presay
+    setmmout, review		    |
+
+Hausbau:
+    hausbau, instanthaus, seherhaus,
+    aendere, ausgang, beschreibe, erlaube, kopiere, licht, loesche,
+    meldungen, notiz, schiebe, sperre, spion, uebersicht, verbiete, werfe
+
+Anderes:
+    fehlermeldung, review
+
diff --git a/doc/hilfe.spieler b/doc/hilfe.spieler
new file mode 100644
index 0000000..a2072f8
--- /dev/null
+++ b/doc/hilfe.spieler
@@ -0,0 +1,62 @@
+
+  ---------------------------SPIELERHILFSSEITE-----------------------------
+
+Fuer folgende allgemeine Befehle und Themen kann mit "hilfe <thema>" eine
+Erlaeuterung angezeigt werden:
+
+Einfuehrung und erste Schritte:
+    Grundlegend: 
+      syntax, bewegung, regeln, einfuehrung
+    Charakter:
+      attribute, zaubertraenke, stufen, stufenpunkte
+    Spiel:   
+      abenteuer, gilden, zauberei
+    Neuspieler:  
+      cicerones, tutorial
+
+Kommunikation:
+    Im Raum:	
+      sage, gespraech, frage, antworte
+    Privat:	
+      teile (mit), erzaehle, erwidere, fluestere,
+    MUD-weit:	
+      rufe, ebenen, post, mrufe
+    Anderes:	
+      ignoriere, kobold, weg, parties, senderwiederholung, klingelton
+      teamspeak
+
+Allgemeine Aktionen:
+    gib (an), hole (aus), nimm [aus], stecke (in), wirf (weg),
+    behalte, klettere, reise
+
+Interaktion und Information:
+    schau (an), untersuche, lausche, rieche, suche, i (=inventur),
+    ausruestung, info, kurzinfo, taste
+    inform, erwarte, wer, kwer, kkwer, muds, zeit, finger, idlezeit
+    email, url, ort, icq, wegmeldung
+
+Sonstige Verben:
+    verben (mit eigener Hilfefunktion), adverb, emote, rknuddle, rwinke
+
+Eingaben und Ersetzungen:
+    alias, unalias, ersetzungsanzeige, history
+
+Kampf:
+    toete, stop, vorsicht, zuecke, ziehe (hervor/aus), trage,
+    stecke (weg/zurueck), heilung, sterben, spruchermuedung
+    team, waffenfertigkeiten, gift/krankheiten/flueche
+
+Gildenuebersicht:
+    abenteurer, bierschuettler, chaos, dunkelelfen, kaempfer, karate,
+    klerus, tanjian, uruk-hai, werwoelfe, zauberer
+
+Andere Befehle:
+    ende, schlafe (ein), speichern, lang, kurz, ultrakurz, ausgaenge,
+    passwort, fehler, idee, typo, zeilen, selbstloeschung, spieldauer,
+    spielpause, uhrmeldung, zweitiemarkierung, telnegs, stty, grafik, 
+    telnet
+
+Sonstige Themen:
+    verein, konzept, seher, magier, regionen, editor, buecher, faq,
+    balance, mudrechner, einfuehrung, spiele
+
diff --git a/doc/hook/auto_include b/doc/hook/auto_include
new file mode 100644
index 0000000..a82adda
--- /dev/null
+++ b/doc/hook/auto_include
@@ -0,0 +1,37 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_AUTO_INCLUDE, value)
+
+        <value> being:
+
+          string <text>
+          string <closure>(string base_file, string current_file
+                          , int sys_include)
+
+DESCRIPTION
+        Optional hook specifying a string to be included before
+        the source of every compiled LPC object.
+        Hook setting can be a string or a closure.
+
+        If the setting is a string, it will be automatically included before
+        the source of every compiled LPC object.
+
+        If the setting is a closure, it is called for every file
+        opened by the compiler. <base_file> will be the filename of
+        the compiled object, <current_file> the name of a file
+        included directly or indirectly by the <base_file>. When the
+        <base_file> itself is opened, <current_file> will be 0. For an
+        included file, <sys_include> will be TRUE if it is a <>-type
+        include.
+
+        If the result from the call is a string, it will be included
+        before the actual text of the file.
+
+        In both cases, the string will be included as-is; in
+        particular no terminating '\n' will be added.
+
+HISTORY
+
+SEE ALSO
+        hooks(C), include_dirs(H)
diff --git a/doc/hook/clean_up b/doc/hook/clean_up
new file mode 100644
index 0000000..e00521a
--- /dev/null
+++ b/doc/hook/clean_up
@@ -0,0 +1,45 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_CLEAN_UP, value)
+
+        <value> being:
+
+          int <closure>(int ref, object ob)
+          void|int <name>(int ref)
+
+DESCRIPTION
+        Optional hook to clean up an object.
+        Hook setting can be any closure, or the name of the function
+        to call in the object.
+
+        This hook is called for an object if it hasn't been used
+        for at least TIME_TO_CLEAN_UP seconds, to give it the
+        opportunity to self destruct.
+
+        If the hook is a closure, it is called with the refcount of
+        the object to clean up as first argument, and with the object
+        itself as second. Lambda closures are also bound to the object
+        prior to the call.
+
+        If the hook is the name of an lfun, it is called in the
+        object with its refcount as argument.
+
+        In both calls, the refcount is constructed as:
+
+          ref = 0: the object is a clone, or a blueprint with
+                   replaced program.
+          ref = 1: the object is a swapped or unused blueprint.
+          ref > 1: the object is a used blueprint with <ref> references.
+
+        The cleanup method has the possibility to destruct the
+        object. To survive this time, but try again some time later,
+        the call has to result in a non-zero value.
+
+        If the hook specifies a non-existing lfun, or if the call
+        returns 0, no further attempt to clean up this object will be done.
+
+HISTORY
+
+SEE ALSO
+        hooks(C)
diff --git a/doc/hook/clone_uids b/doc/hook/clone_uids
new file mode 100644
index 0000000..4bf902a
--- /dev/null
+++ b/doc/hook/clone_uids
@@ -0,0 +1,36 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_CLONE_UIDS, value)
+
+        <value> being:
+
+          mixed <closure>(object blueprint, string objectname)
+
+DESCRIPTION
+        Mandatory hooks to determine the uid and euid of cloned
+        objects.  Hook settings can be any closure.
+
+        When an object is cloned, the H_CLONE_UIDS hook is called with
+        the blueprint object as first and the clone's designated name
+        as second argument. The new object already exists, but has 0
+        uids.
+
+        For the result, the following possibilities exist (<num> is
+        a non-zero number, <no-string> is anything but a string):
+
+           "<uid>"                    -> uid = "<uid>", euid = "<uid>"
+           ({ "<uid>", "<euid>" })    -> uid = "<uid>", euid = "<euid>"
+           ({ "<uid>", <no-string> }) -> uid = "<uid>", euid = 0
+
+        If strict-euids is not active, the following results are
+        possible, too:
+
+           <num>                      -> uid = 0, euid = 0
+           ({ <num>, "<euid>" })      -> uid = 0, euid = "<euid>"
+           ({ <num>, <no-string> })   -> uid = 0, euid = 0
+
+HISTORY
+
+SEE ALSO
+        hooks(C), uids(C), load_uids(H)
diff --git a/doc/hook/command b/doc/hook/command
new file mode 100644
index 0000000..de1b609
--- /dev/null
+++ b/doc/hook/command
@@ -0,0 +1,33 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_COMMAND, value)
+
+        <value> being:
+
+          int <closure>(string command, object command_giver)
+          int <name>(string command, object command_giver)
+
+DESCRIPTION
+        Optional hook to parse and execute commands. If this hook is
+        used, it bypasses the normal command parsing done by the
+        driver (including the MODIFY_COMMAND and NOTIFY_FAIL hooks).
+
+        The hook is called with two parameters: the command received
+        from the living (interactive user or NPC), and the living
+        object (the 'command giver') itself. The hook has to return
+        non-0 if the command was found and executed, and 0 otherwise.
+
+        At the time the hook is called, query_command() returns the
+        command string and this_player() returns the living object.
+        query_verb() and query_notify_fail() return 0.
+
+        If the hook is a string, it is the name of an lfun in the
+        command giver; if the hook is a lambda closure, it is bound to
+        the command giver before the call.
+
+HISTORY
+
+SEE ALSO
+        hooks(C), modify_command(H), modify_command_fname(H), notify_fail(H),
+        send_notify_fail(H)
diff --git a/doc/hook/create_clone b/doc/hook/create_clone
new file mode 100644
index 0000000..bf3100e
--- /dev/null
+++ b/doc/hook/create_clone
@@ -0,0 +1,34 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_CREATE_CLONE, value)
+
+        <value> being:
+
+          void <name> (0)
+          int <closure> ( void )
+          int <closure> (object obj_to_init)
+
+DESCRIPTION
+        Optional hooks to initialize a cloned object after creation.
+        Hook setting can be unbound lambda closures, or the name of
+        the function (static or public) to call in the object.
+
+        If the hook is a closure expecting an argument, it is bound
+        to the current object and called with the created object as
+        argument. If the hook as a closure without arguments, it is bound to
+        the object to be initalized and called.
+
+        If the result of the closure call is a non-zero number, it is used
+        as the interval to wait before the first reset(), else the default
+        interval computed from TIME_TO_RESET is used.
+
+        If the hook is defined as the name of an lfun in the object,
+        it is called in the object with 0 as argument, and any result
+        is ignored.
+
+HISTORY
+        LDMud 3.2.10 allowed static functions to be given by name.
+
+SEE ALSO
+        hooks(C), create_ob(H), create_super(H)
diff --git a/doc/hook/create_ob b/doc/hook/create_ob
new file mode 100644
index 0000000..7de7ba3
--- /dev/null
+++ b/doc/hook/create_ob
@@ -0,0 +1,35 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_CREATE_OB, value)
+
+        <value> being:
+
+          void <name> (0)
+          int <closure> ( void )
+          int <closure> (object obj_to_init)
+
+DESCRIPTION
+        Optional hooks to initialize an explictely loaded
+        object/blueprint after creation.  Hook setting can be unbound
+        lambda closures, or the name of the function (static or public) to
+        call in the object.
+
+        If the hook is a closure expecting an argument, it is bound
+        to the current object and called with the created object as
+        argument. If the hook as a closure without arguments, it is bound to
+        the object to be initalized and called.
+
+        If the result of the closure call is a non-zero number, it is used
+        as the interval to wait before the first reset(), else the default
+        interval computed from TIME_TO_RESET is used.
+
+        If the hook is defined as the name of an lfun in the object,
+        it is called in the object with 0 as argument, and any result
+        is ignored.
+
+HISTORY
+        LDMud 3.2.10 allowed static functions to be given by name.
+
+SEE ALSO
+        hooks(C), create_super(H), create_clone(H)
diff --git a/doc/hook/create_super b/doc/hook/create_super
new file mode 100644
index 0000000..a502730
--- /dev/null
+++ b/doc/hook/create_super
@@ -0,0 +1,35 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_CREATE_SUPER, value)
+
+        <value> being:
+
+          void <name> (0)
+          int <closure> ( void )
+          int <closure> (object obj_to_init)
+
+DESCRIPTION
+        Optional hooks to initialize a blueprint which is loaded by
+        inheritance after creation.
+        Hook setting can be unbound lambda closures, or the name of
+        the function (static or public) to call in the object.
+
+        If the hook is a closure expecting an argument, it is bound
+        to the current object and called with the created object as
+        argument. If the hook as a closure without arguments, it is bound to
+        the object to be initalized and called.
+
+        If the result of the closure call is a non-zero number, it is used
+        as the interval to wait before the first reset(), else the default
+        interval computed from TIME_TO_RESET is used.
+
+        If the hook is defined as the name of an lfun in the object,
+        it is called in the object with 0 as argument, and any result
+        is ignored.
+
+HISTORY
+        LDMud 3.2.10 allowed static functions to be given by name.
+
+SEE ALSO
+        hooks(C), create_ob(H), create_clone(H)
diff --git a/doc/hook/default_method b/doc/hook/default_method
new file mode 100644
index 0000000..427a131
--- /dev/null
+++ b/doc/hook/default_method
@@ -0,0 +1,44 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_DEFAULT_METHOD, value)
+
+        <value> being:
+
+          int <name>(mixed & result, string fun, varargs args)
+          int <closure>(mixed & result, object ob, string fun, varargs args)
+
+DESCRIPTION
+        Optional hook to provide default implementation for unresolved
+        calls. Hook setting can be any closure, or the name of the
+        function to call in the object.
+
+        This hook is called whenever a call_other(), call_resolved()
+        or call_out() on named function (as opposed to a closure)
+        couldn't be resolved. The hook has then the opportunity to
+        provide a default implementation.
+
+        Exempt from this behaviour are calls to the master object, to
+        simul-efuns, and calls done with call_direct() and
+        call_direct_resolved().
+
+        If the hook is the name of an lfun, it is called in the target
+        object; otherwise if it is a closure, the target object is
+        passed as parameter <ob>.
+
+        The other parameters passed are the name <fun> of the called
+        function, the arguments <args> passed to the call (if any),
+        and a reference <result> to a variable for the call result.
+
+        If the hook can resolve the call, it has to return the call
+        result in <result> and 1 as function result.
+
+        If the hook can not (or doesn't want to) resolve the call, it
+        has to return 0.
+
+
+HISTORY
+        Introduced in LDMud 3.3.113
+
+SEE ALSO
+        hooks(C)
diff --git a/doc/hook/default_prompt b/doc/hook/default_prompt
new file mode 100644
index 0000000..6231bb4
--- /dev/null
+++ b/doc/hook/default_prompt
@@ -0,0 +1,24 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_DEFAULT_PROMPT, value)
+
+        <value> being:
+
+          string <prompt>
+          string <closure>()
+
+DESCRIPTION
+        Optional hook for the command prompt. If this hook is not used,
+        the driver will use "> " as the command prompt.
+
+        The hook can be given as string or as closure. If it is a
+        closure, it is called with the commandgiver being the calling
+        object. If the result is a string, it will be printed,
+        otherwise ignored.
+
+HISTORY
+        Introduced in LDMud 3.3.163.
+
+SEE ALSO
+        hooks(C), print_prompt(H)
diff --git a/doc/hook/erq_stop b/doc/hook/erq_stop
new file mode 100644
index 0000000..a135a1c
--- /dev/null
+++ b/doc/hook/erq_stop
@@ -0,0 +1,20 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_ERQ_STOP, value)
+
+        <value> being:
+
+          void <closure>()
+
+DESCRIPTION
+        Optional hook to notify the mudlib about the termination of
+        the erq demon.  Hook setting may be any closure.
+
+        The closure is called without arguments and may do whatever it
+        likes to clean-up after the erq.
+
+HISTORY
+
+SEE ALSO
+        hooks(C)
diff --git a/doc/hook/hook b/doc/hook/hook
new file mode 100644
index 0000000..3218445
--- /dev/null
+++ b/doc/hook/hook
@@ -0,0 +1,18 @@
+CONCEPT
+        driver hooks
+
+DESCRIPTION
+        To allow a greater flexibility of the muds, the gamedrivers
+        since 3.2.1 moved several once hardcoded 'underground'
+        activities from the driver into the mudlib. This includes for
+        example the differences between compat and native mode.
+
+        The hooks are set with the privileged efun set_driver_hook().
+        Some of the hooks are mandatory, some not. Most hooks accept
+        unbound lambda closures as values, some also lfun closures or
+        even strings.
+        The hooks are identified by an ordinal number, for which
+        symbolic names are defined in /sys/driverhooks.h.
+
+SEE ALSO
+        native(C), set_driver_hook(E)
diff --git a/doc/hook/include_dirs b/doc/hook/include_dirs
new file mode 100644
index 0000000..6557359
--- /dev/null
+++ b/doc/hook/include_dirs
@@ -0,0 +1,38 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_INCLUDE_DIRS, value)
+
+        <value> being:
+
+          string *<dirs>
+          string <closure>(string include_name, string current_file)
+
+DESCRIPTION
+        Semi-mandatory hook specifying the directories where <>-type
+        include files are searched (this includes ""-includes not
+        found as specified).  Hook setting may be any closure or a
+        string array. If not set, only ""-type includes may be used in LPC
+        programs.
+
+        The hook is called only if a call to master::include_file()
+        does not return a usable filename.
+
+        If the hook setting is a string array, it has to contain the
+        path names of those directories where <>-type includes are to
+        be searched. The directories are searched in the order they
+        appear in the array. The directory name and the name of the
+        actual include file are concatenated, therefore the directory
+        names have to end in '/'. Leading slashes may be omitted.
+
+        If the setting is a closure, it is called with the name of the
+        desired include file as first, and the name of the compiled
+        LPC file as second argument.  Result has to be the complete
+        path name of the include file to use. Leading slashes may be
+        omitted.  If the closure is a lambda closure, it is bound to
+        this_object() prior to execution.
+
+HISTORY
+
+SEE ALSO
+        hooks(C), auto_include(H), include_file(M)
diff --git a/doc/hook/load_uids b/doc/hook/load_uids
new file mode 100644
index 0000000..9d1e160
--- /dev/null
+++ b/doc/hook/load_uids
@@ -0,0 +1,35 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_LOAD_UIDS, value)
+
+        <value> being:
+
+          mixed <closure> (string objectname)
+
+DESCRIPTION
+        Mandatory hooks to determine the uid and euid of loaded or
+        cloned objects.  Hook settings can be any closure.
+
+        When an object is newly loaded, the H_LOAD_UIDS hook is called
+        with the object name as argument.  The new object already
+        exists, but has 0 uids.
+
+        For the result, the following possibilities exist (<num> is
+        a non-zero number, <no-string> is anything but a string):
+
+           "<uid>"                    -> uid = "<uid>", euid = "<uid>"
+           ({ "<uid>", "<euid>" })    -> uid = "<uid>", euid = "<euid>"
+           ({ "<uid>", <no-string> }) -> uid = "<uid>", euid = 0
+
+        If strict-euids is not active, the following results are
+        possible, too:
+
+           <num>                      -> uid = 0, euid = 0
+           ({ <num>, "<euid>" })      -> uid = 0, euid = "<euid>"
+           ({ <num>, <no-string> })   -> uid = 0, euid = 0
+
+HISTORY
+
+SEE ALSO
+        hooks(C), uids(C), clone_uids(H)
diff --git a/doc/hook/modify_command b/doc/hook/modify_command
new file mode 100644
index 0000000..f478312
--- /dev/null
+++ b/doc/hook/modify_command
@@ -0,0 +1,61 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_MODIFY_COMMAND, value)
+
+        <value> being:
+
+          int|string <closure>(string cmd, object player)
+          int|string <name>(string cmd)
+          <mapping>
+
+DESCRIPTION
+        Optional hook to modify commands (both entered or given by a
+        call to command()) before the parser sees them (this includes
+        special commands like 'status').
+
+        Hook setting can be any closure, the name of the function to
+        call in the object, or a mapping.
+
+        For interactives this hook is used only if the interactive
+        object has no command modifier already set by the efun
+        set_modify_command().
+
+        If the hook is a closure, it is called with the entered
+        command as first, and the command giving player as second
+        argument. Lambda closures are bound to the command giving player,
+        unbound-lambda closures remain unbound.
+
+        If the hook is a string, it is used as the name of an lfun
+        in the command giving player, which is called with the command
+        as argument.
+
+        If the hook is a mapping, it is queried with the given command
+        as index, and the data retrieved is used as new command
+        (defaults to 0 if no data is stored for a given command). If
+        an entry is a closure instead of a string, it is called as
+
+          int|string <closure>(string cmd, object player)
+
+        and the result from the call is used as 'the' command.
+
+        The result is treated equal in all three cases:
+         - If the result is a string, it is the new command to execute
+           instead of the given one. Note that it is not possible to
+           make several commands from one this way!
+         - If the result is a non-zero number, the given command is to
+           be ignored. In case of the closure/lfun setting this may
+           mean that the closure/lfun already executed it.
+         - If the result is 0, the originally given command is to be
+           used.
+
+        It is possible for the hook to change the command giver
+        (this_player()) for the execution of the command. This means that
+        even though the commands are execute for the original commandgiver,
+        this_player() will return the changed commandgiver.
+
+HISTORY
+
+SEE ALSO
+        hooks(C), command(H), modify_command_fname(H), notify_fail(H),
+        send_notify_fail(H)
diff --git a/doc/hook/modify_command_fname b/doc/hook/modify_command_fname
new file mode 100644
index 0000000..095ef80
--- /dev/null
+++ b/doc/hook/modify_command_fname
@@ -0,0 +1,44 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_MODIFY_COMMAND_FNAME, value)
+
+        <value> being:
+
+          int|string <name>(string cmd)
+
+DESCRIPTION
+        Mandatory hook specifying the name of the 'modify_command'
+        lfun to call for newly entered commands as result of a
+        set_modify_command().
+
+        Hook setting must be a string.
+
+        If set_modify_command() is used for an interactive user, all
+        newly entered commands are first passed to the function named
+        by this hook.
+
+        The function is called with the command as argument.
+
+        If the result is a string, it is the new command to execute
+        instead of the given one. Note that it is not possible to make
+        several commands from one this way!
+
+        If the result is a non-zero number, the given command is to be
+        ignored. In case of the closure/lfun setting this may mean
+        that the closure/lfun already executed it.
+
+        If the result is 0, the originally given command is to be
+        used.
+
+        It is possible for the hook to change the command giver
+        (this_player()) for the execution of the command. This means
+        that even though the commands are execute for the original
+        commandgiver, this_player() will return the changed
+        commandgiver.
+
+HISTORY
+
+SEE ALSO
+        hooks(C), command(H), modify_command(H), notify_fail(H),
+        send_notify_fail(H)
diff --git a/doc/hook/move_object b/doc/hook/move_object
new file mode 100644
index 0000000..5b2445d
--- /dev/null
+++ b/doc/hook/move_object
@@ -0,0 +1,27 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_MOVE_OBJECT0, value)
+        set_driver_hook(H_MOVE_OBJECT1, value)
+
+        <value> being:
+
+          void <closure>(object item, object dest)
+
+DESCRIPTION
+        Mandatory hooks to implement the efun void move_object().
+        Hook setting must be an unbound lambda closure.
+
+        Upon call, the hook has to perform the move itself (by using
+        set_environment()) and all depending actions (like the calls to
+        init() to add actions).
+
+        The difference lies in the binding of the set hook prior to
+        the call: the H_MOVE_OBJECT0 closure is bound to the current
+        object, the H_MOVE_OBJECT1 to 'item'.
+        If both hooks are set, H_MOVE_OBJECT0 is ignored.
+
+HISTORY
+
+SEE ALSO
+        move_object(E), set_environment(E), hooks(C)
diff --git a/doc/hook/msg_discarded b/doc/hook/msg_discarded
new file mode 100644
index 0000000..5360dfe
--- /dev/null
+++ b/doc/hook/msg_discarded
@@ -0,0 +1,28 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_MSG_DISCARDED, value)
+
+        <value> being:
+
+          string <msg>
+          string <closure>(object user)
+
+DESCRIPTION
+        Optional hook to specify a message or take other measures
+        when a message had to be discarded, because they could not
+        be delivered to the player <user>. If the hook is not set,
+        a standard message is used.
+
+        If the hook is a string, this text will be sent as soon as
+        possible to the player informing about the lost transmission.
+
+        If the hook is a closure, it is the function to be called
+        and the result is used as the message to be sent. Lambda
+        closures are bound to the interactive <user> first.
+
+HISTORY
+        Introduced in LDMud 3.3.719
+
+SEE ALSO
+        hooks(C), configure_interactive(E)
diff --git a/doc/hook/no_echo b/doc/hook/no_echo
new file mode 100644
index 0000000..b592529
--- /dev/null
+++ b/doc/hook/no_echo
@@ -0,0 +1,41 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_TELNET_NEG, value)
+
+        <value> being:
+
+          void <name>(int flag, object user, int no_telnet)
+          void <closure>(int flag, object user, int no_telnet)
+
+DESCRIPTION
+        Optional hook to specifiy how to perform the telnet actions to
+        switch the echo mode (used for e.g. password input_to()s).
+        Hook setting may be any closure or a string.  If not set, a
+        default handling is performed.
+
+        If the setting is a string, it used as name of an lfun to call
+        in the interactive <user>, where <flag> is the echo-flag
+        passed to the input_to() statement.
+
+        If the setting is a closure, it is called with the same
+        arguments, and unbound lambda-closures being bound to
+        this_player() prior to execution.
+
+        <local> is a boolean flag: it is TRUE when input_to() was
+        called with the INPUT_NO_TELNET flag.
+
+        When set, the hook is called whenever the driver needs to
+        change the echo mode, thus you can negotiate about things that
+        are coupled with it, like LINEMODE or character-at-a-time.
+
+        IMPORTANT: If this hook is used, the control of all telnet
+        negotiation is transferred to the mudlib: all incoming negotiations
+        are passed to H_TELNET_NEG, and the sending of no-echo negotiations
+        is handled by this hook.
+
+HISTORY
+        LDMud 3.2.11/LDMud 3.3.593 added the <no_telnet> argument.
+
+SEE ALSO
+        hooks(C), telnet(C), telnet_neg(H)
diff --git a/doc/hook/no_ipc_slot b/doc/hook/no_ipc_slot
new file mode 100644
index 0000000..4d1b0fb
--- /dev/null
+++ b/doc/hook/no_ipc_slot
@@ -0,0 +1,22 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_NO_IPC_SLOT, value)
+
+        <value> being:
+
+          0
+          string <msg>
+
+DESCRIPTION
+        Optional hook specifying the message given to logins
+        rejected due to space limitations (MAX_PLAYER).
+        Hook setting has to be string.
+
+        If set to 0, the default message "Lpmud is full. Come back
+        later." is issued.
+
+HISTORY
+
+SEE ALSO
+        hooks(C)
diff --git a/doc/hook/notify_fail b/doc/hook/notify_fail
new file mode 100644
index 0000000..8a4bacf
--- /dev/null
+++ b/doc/hook/notify_fail
@@ -0,0 +1,33 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_NOTIFY_FAIL, value)
+
+        <value> being:
+
+          string <msg>
+          string <closure>(string entered_command, object cmd_giver)
+
+DESCRIPTION
+        Mandatory hook to issue the default message if an entered
+        command couldn't be parsed and no notify_fail() command is in
+        effect.  Hook setting can be a any closure, or a string.
+
+        If set to a string, it is the message returned to the
+        player.
+
+        If set to a closure, it is called with the command and the
+        command giver as arguments, and the result is used as failure
+        message. Lambda closures are bound to this_player() prior to
+        execution.
+
+        <cmd_giver> is the object which received the command in the
+        first place. It is usually identical with this_player(),
+        unless the H_MODIFY_COMMAND hook changed it.
+
+HISTORY
+        LDMud 3.2.7 added the new 'command_giver' argument.
+
+SEE ALSO
+        hooks(C), command(H), modify_command(H), modify_command_fname(H),
+        send_notify_fail(H)
diff --git a/doc/hook/print_prompt b/doc/hook/print_prompt
new file mode 100644
index 0000000..f864db7
--- /dev/null
+++ b/doc/hook/print_prompt
@@ -0,0 +1,28 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_PRINT_PROMPT, value)
+
+        <value> being:
+
+          void <name>(string prompt).
+          void <closure>(string prompt)
+
+DESCRIPTION
+        Optional hook to print the current command prompt. If this
+        hook is not set, the driver will just print the prompt to the
+        user.
+
+        The hook is called with the prompt string as single argument
+        and has to print the prompt, e.g. using write() or
+        binary_message().
+
+        If the hook is a string, it is the name of an lfun in the
+        command giver. If the hook is a closure, it is called
+        with the command giver as previous object.
+
+HISTORY
+        Introduced in LDMud 3.3.163.
+
+SEE ALSO
+        hooks(C), default_prompt(H)
diff --git a/doc/hook/regexp_package b/doc/hook/regexp_package
new file mode 100644
index 0000000..ae52e3f
--- /dev/null
+++ b/doc/hook/regexp_package
@@ -0,0 +1,23 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+        #include <sys/regexp.h>
+
+        set_driver_hook(H_REGEXP_PACKAGE, value)
+
+        <value> being an integer:
+          0
+          RE_PCRE
+          RE_TRADITIONAL
+
+DESCRIPTION
+        Optional hook to select the default regexp package to use
+        for regular expression functions.
+
+        If set to 0, the default package determined by the driver's
+        configuration/commandline options is used.
+
+HISTORY
+        Introduced in LDMud 3.3.595.
+
+SEE ALSO
+        hooks(C), default_prompt(H), invocation(D), regexp(C)
diff --git a/doc/hook/reset b/doc/hook/reset
new file mode 100644
index 0000000..23f1dea
--- /dev/null
+++ b/doc/hook/reset
@@ -0,0 +1,38 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_RESET, value)
+
+        <value> being:
+
+          void|int <closure> ( void )
+          void <name>(1)
+
+DESCRIPTION
+        Optional hook to reset an object.
+        Hook setting can be unbound lambda closures, or the name of
+        the function (static or public) to call in the object.
+
+        If the hook is a closure, it is bound to the object to be
+        reset and called with no argument.
+
+        If the result of this call is a positive number, it is used as
+        the interval to wait before the next reset().  If the result
+        is 0, the default interval computed from TIME_TO_RESET is
+        used.  If the result is a negative number, the object will not
+        be reset again, unless directed otherwise by set_next_reset().
+
+        If the hook is defined as the name of an lfun in the object,
+        it is called in the object with 1 as argument, and any result
+        is ignored.  In this call the previous_object() is the object
+        initiating the reset.  If the function does not exist, the
+        object won't be reset again.
+
+        Note that an object is only reset by call to this hook if it
+        has been used since the last reset.
+
+HISTORY
+        LDMud 3.2.10 allowed static functions to be given by name.
+
+SEE ALSO
+        hooks(C)
diff --git a/doc/hook/send_notify_fail b/doc/hook/send_notify_fail
new file mode 100644
index 0000000..7f61bb4
--- /dev/null
+++ b/doc/hook/send_notify_fail
@@ -0,0 +1,34 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_SEND_NOTIFY_FAIL, value)
+
+        <value> being:
+
+          void <name>(string msg, object msgobj, object orig_cmd_giver)
+          void <closure>(string msg, object msgobj, object orig_cmd_giver)
+
+DESCRIPTION
+        Optional hook to send the notify fail message, regardless
+        of how it was determined, to the player. If the hook is not
+        set, the message is delivered using tell_object() internally.
+        Hook setting can be a string or a closure.
+
+        If the hook is a string, it is the name of a (possibly static)
+        function to call in the current command giver. If the hook
+        is a closure, it is the function to be called. Lambda closures
+        are bound to the current command giver first.
+
+        The arguments to the call are:
+         - <msg> is the notify fail message to be delivered.
+         - <msgobj> is the object which set the message. It is 0 for
+           the default message.
+         - <orig_cmd_giver> is the object for which the original
+           command was first received. It is usually identical with
+           the current command giver this_player().
+
+HISTORY
+
+SEE ALSO
+        hooks(C), command(H), modify_command(H), modify_command_fname(H),
+        notify_fail(H)
diff --git a/doc/hook/telnet_neg b/doc/hook/telnet_neg
new file mode 100644
index 0000000..0f8b8c1
--- /dev/null
+++ b/doc/hook/telnet_neg
@@ -0,0 +1,72 @@
+SYNOPSIS
+        #include <sys/driver_hooks.h>
+
+        set_driver_hook(H_TELNET_NEG, value)
+
+        <value> being:
+
+          void|mixed <name>(int action, int option [, int * opts ] )
+          void|mixed <closure>(int action, int option [, int * opts ] )
+
+DESCRIPTION
+        Optional hook to specifiy how to perform a single telnet
+        negotiation.  Hook setting may be any closure or a string.  If
+        not set, most telnet options are rejected (read: only a very
+        minimal negotiation takes place).
+
+        The hook is called whenever the driver receives a demand for
+        option negotiation for any option the driver does not handle itself.
+        By default, the driver deals with the following options:
+            TELOPT_ECHO:      Echo option
+            TELOPT_SGA:       Suppress Go Ahead (nearly always on)
+            TELOPT_COMPRESS:  Mud client compression protocol (obsolete)
+            TELOPT_COMPRESS2: Mud client compression protocol V2
+
+        For all other telnet options negotiations, this hook is called.
+
+        The hook has then to perform the negotiation using the efun
+        binary_message().
+
+        Alternatively, if H_NOECHO is set, this hook is called for
+        _all_ telnet data received.
+
+        If the setting is a string, it used as name of an lfun to call
+        in this_player(). Closures are just called, with unbound
+        lambda-closures being bound to this_player() prior to
+        execution.
+
+        The hook is called for a 'DO/DONT/WILL/WONT <opt>' with the action
+        (DO/DONT/...) as the first, and <opt> as the second argument.
+
+        For example:, if the driver receives the sequence
+
+          IAC SB <opt> <opts>...
+
+        followed by IAC SB/SE,  the hook is called with 'SB' as first
+        argument, <opt> as second, and <opts> as an array of integers as
+        third argument.
+
+REMARKS
+        An incomplete list of telnet negotiations this hook is called
+        for includes the following:
+            SB:               Suboption negotiation
+            TELOPT_LINEMODE:  linemode
+            TELOPT_NAWS:      window size
+            TELOPT_TTYPE:     terminal type
+            TELOPT_TM:        timing mark
+            TELOPT_NEWENV:    remote environment variables
+            TELOPT_ENVIRON:   remote environment variables
+            TELOPT_XDISPLOC:  remote X display address
+            TELOPT_TSPEED:    terminal speed
+            TELOPT_BINARY:    binary data, needed for non-ASCII charsets
+            TELOPT_EOR:       TinyFugue prompt marker (together with EOR)
+
+            TELOPT_MSP:       Mud Sound Protocol
+            TELOPT_MXP:       Mud Extension Protocol
+            TELOPT_MSSP:      Mud Server Status Protocol
+            TELOPT_GMCP:      Generic Mud Communication Protocol
+
+HISTORY
+
+SEE ALSO
+        hooks(C), telnet(C), no_echo(H)
diff --git a/doc/infomails/info_ml15 b/doc/infomails/info_ml15
new file mode 100644
index 0000000..0c936bf
--- /dev/null
+++ b/doc/infomails/info_ml15
@@ -0,0 +1,74 @@
+Herzlich willkommen im Kreise der Magier!
+Hallo @WER1,
+
+ich nehme an, dass Dir Dein Sponsor bereits einige Hinweise und Tips
+und vor allen Dingen den weiteren Ablauf Deines Magierdaseins hier im MG
+erlaeutert hat. Trotzdem moechte ich Dir auch noch ein paar Hinweise
+geben, damit nichts schief geht.
+
+Zum einen findest Du zusaetzliche Informationen in den Hilfeseiten zu
+"magier" und "gesellenstueck". Lies Dir die einmal in Ruhe durch und wenn
+Du dazu noch weitere Fragen hast, kannst Du Dich gerne an Deinen Sponsor 
+wenden.
+
+Des weiteren hast Du momentan nur Leserechte in /doc, das ist das 
+Verzeichnis, in dem die gesamte Dokumentation des Morgengrauens
+abgelegt ist. Du kannst Dir die Dateien mit Hilfe des Befehls "man"
+anzeigen lassen. Eine Uebersicht ueber die Struktur der Dokumentation und
+die Themen der einzelnen Unterverzeichnisse verschafft Dir der Befehl
+"more /doc/README".
+Unter /doc/beispiele/ befinden sich ein paar Beispiele der Programmierung 
+mit LPC - auch wenn leider nicht alle auf dem aktuellsten Stand sind.
+
+Der Befehl "man <begriff>" durchsucht alle diese Verzeichnisse automatisch
+nach dem angegebenen Begriff, der z.B. eine Mudlib-Funktion sein kann.
+Wenn er eine passende Manpage gefunden hat, wird Dir diese angezeigt.
+Probier doch z.B. einfach mal "man AddExit" aus, um zu erfahren, wie Du
+in einem Raum einen Ausgang anlegst, oder schau mit "man P_KILL_MSG" nach,
+wie Du Deinem NPC beibringst, seinen Gegner auf der Ebene "Tod" angemessen
+zu verspotten. Auf diese Weise kannst Du Dir einen ersten Ueberblick ueber
+die Funktionalitaet und Arbeitsweise der Mudlib und des Drivers verschaffen.
+Wie immer gilt aber, dass Dein Sponsor, oder auch die anderen Magier Dir
+gerne weiterhelfen, wenn etwas unklar bleiben sollte. Scheue Dich bitte
+nicht, einfach jemanden zu fragen.
+
+Zum Ablauf Deines Gesellenstuecks moechte ich zunaechst nur soviel sagen,
+dass Du erst auf Magierlevel 20 ein eigenes Home-Verzeichnis bekommen wirst,
+in dem Du dann arbeiten kannst. Vorher musst Du den Erzmagiern, insbesondere
+dem fuer die Gesellenstuecke zustaendigen (wer das ist, bekommst Du mittels
+"hilfe erzmagier" heraus), Dein Konzept fuer dieses Gesellenstueck 
+erlaeutern. Sei dabei ruhig ein wenig ausfuehrlicher und schreib ein wenig
+etwas zu sammen. Falls Du ein neues Gebiet schreiben moechtest, ist eine 
+kleine ASCII-Karte als Gebietsueberblick immer gern gesehen.
+
+Ein wichtiger Hinweis zum Konzept: Sprich bitte rechtzeitig mit dem
+zustaendigen Regionsmagier der Region, die Dein GS aufnehmen soll, Deinem
+Sponsor und auch mit dem GS-EM Dein Gesellenstueck ab. Bitte teile uns auch 
+mit, wenn Du waehrend der Programmierung Aenderungen vornimmst. Je besser
+wir uns im Vorfeld absprechen, desto weniger Probleme treten bei der 
+Abnahme durch die beteiligten Instanzen auf. Es hat sich ausserdem gezeigt,
+dass man saemtliche (und damit meine ich wirklich _alle_) Absprachen 
+bezueglich des Konzepts sauber dokumentieren sollte, z.B. per Mail.
+
+Bei der Ausarbeitung eines Konzeptes ist Dir gerne Dein Sponsor oder 
+auch der Regionsmagier behilflich - jeder von denen hat selber schon
+ein GS-Konzept eingereicht und sollte etwas dazu sagen koennen. Falls
+Dir noch ein wenig die Ideen abgehen, schau doch einfach mal auf das
+Projektbrett in der Abenteurer-Gilde: dort sind haeufig Gesellenstueck-
+Projekte ausgeschrieben.
+
+Wenn Dein Konzept vorliegt und vom Umfang und Schwierigkeit her fuer 
+ausreichend befunden wurde, so wirst Du nach ca. 8 Tagen auf Magierlevel
+20 befoerdert.
+Wenn Du noch Fragen hast, Sorgen, Noete o.ae. so stehen Dir Dein Sponsor,
+jeder andere Magier und der GS-EM natuerlich auch gerne zur Verfuegung.
+
+Ich wuensche Dir im Namen der Magierschaft viel Spass beim Programmieren 
+und hoffe, dass Du schon bald eine Mail mit Deinem Konzept an die Erzmagier
+absenden wirst!
+
+Viele Gruesse, Dein Merlin.
+
+P.S.: Wenn Du magst, kannst Du auch gerne die Ebenen -magier und -lpc 
+      betreten, um dort ein wenig mitzulesen oder Deine eigenen Fragen
+      loszuwerden.
diff --git a/doc/infomails/info_ml20 b/doc/infomails/info_ml20
new file mode 100644
index 0000000..0f011a6
--- /dev/null
+++ b/doc/infomails/info_ml20
@@ -0,0 +1,83 @@
+Willkommen auf Magierlevel 20. :-)
+Hallo @WER1,
+
+Du bist gerade von mir auf Magierlevel 20 befoerdert worden. Das 
+bedeutet fuer Dich vor allem, dass Dein Konzept fuer ein Gesellenstueck
+das Wohlwollen der Erzmagier gefunden hat und angenommen wurde. Herzlichen
+Glueckwunsch erst einmal dazu! :-)
+
+Weiterhin wurde jetzt auch ein Home-Verzeichnis unter /players/@WER1
+angelegt, in dem Du die Dateien Deines Gesellenstuecks und anderes ablegen
+kannst. Unter /players/@WER1/workroom.c existiert jetzt auch Dein eigener
+Workroom, in dem Du Dich standardmaessig nach dem Einloggen materialisierst.
+
+Als kleine Hilfestellung fuer Deine Arbeit am Gesellenstueck moechten Dir
+die Erzmagier einige Tips mit auf den Weg geben, die Dir das Leben 
+hoffentlich erleichtern.
+
+1) Zur Programmierung:
+
+   Im Mud selbst kannst Du nur ueber den Zeilen-Texteditor "ed" etwas
+   programmieren. Hilfe zu diesem Werkzeug bekommst Du ueber "man ed"
+   und "man ed0..ed5". Als sinnvolle Alternative steht Dir ein FTP-
+   Zugang zur Verfuegung, ueber den Du die auf Deinem eigenen Rechner
+   geschriebenen Files ins Mud kopieren kannst. Hierbei kannst Du dann
+   Deinen eigenen Editor verwenden. Bitte achte aber darauf, dass Du
+   als Dateiformat das Unix-Format verwendest. Auch unter Windows 
+   beherrschen inzwischen die meisten Editoren dieses Dateiformat.
+
+   Um vernuenftig arbeiten zu koennen, solltest Du Dir das MGTool (zu
+   finden unter /obj/tools/MGtool) beschaffen (die Hilfe hierzu findest
+   Du dann mittels "xman"). Weiterhin sei Dir dringend die Verwendung des
+   Fehlerteufels (siehe "hilfe fehlerteufel") empfohlen, der Dir die
+   Fehlersuche stark erleichtert. Wie Du diese Objekte clonst, kann Dir
+   Dein Sponsor oder ein anderer Magier sicher zeigen.
+
+   Weiterhin solltest Du nun zusaetzlich noch die Ebenen -debug, 
+   -entwicklung und -warnungen einschalten, auf denen Du ueber auftretende
+   Bugs informiert wirst (nicht nur Deine eigenen, aber Du wirst Dich
+   damit sicher schnell zurechtfinden).
+
+   Was formale und auch inhaltliche Anforderungen an Deinen Code angeht,
+   lies Dir bitte "hilfe regionsleitfaden" durch, hier findest Du alles
+   wesentliche kompakt zusammengefasst. Wenn darueber hinaus spezielle
+   Fragen zu der Region bestehen, die Dein Gesellenstueck spaeter aufnehmen
+   soll, kannst Du gerne auch den Regionsmagier ansprechen. Du solltest 
+   aber auf jeden Fall davon ausgehen, dass dieser Dir nur sehr geringe
+   Abweichungen von den im Regionsleitfaden beschriebenen Standards zuge-
+   stehen wird, wenn ueberhaupt (insbesondere wird viel Wert auf eine 
+   sinnvolle Kommentierung Deines Codes gelegt).
+
+2) Zur Fertigstellung und Abnahme des Gesellenstuecks:
+
+   Der uebliche Ablauf ist der, dass der zustaendige Regionsmagier
+   (den Du schon von der Konzeptphase her kennen duerftest) Dein 
+   Gesellenstueck als erster abnimmt und seine Anmerkungen dazu gibt.
+   Wenn diese geklaert und umgesetzt sind, ist der GS-Erzmagier an der
+   Reihe, der Dein Werk nochmals durchsieht und evtl. weitere Kommentare
+   dazu aeussert. Wenn diese beiden Schritte erledigt sind, ist Dein
+   Gesellenstueck fast fertig fuer den Anschluss.
+
+   In der Regel ist jetzt auch der Zeitpunkt gekommen, dass Du zum
+   Vollmagier (Level 21) befoerdert werden kannst und Dein Code in
+   die Region kopiert wird, in der er angeschlossen werden wird.
+
+   Allerdings sei Dir ein Punkt jetzt schon ans Herz gelegt: Solltest
+   Du Objekte in Deinem Gesellenstueck bauen wollen, die von der Balance
+   genehmigt werden muessen (siehe hierzu zunaechst "man balance" und 
+   "man grenzwerte"), solltest Du den noetigen Antrag rechtzeitig stellen,
+   da die Erfahrung zeigt, dass solche Entscheidungen in der Regel nicht
+   von heute auf morgen getroffen werden koennen. 
+   Hilfestellung bei der Frage, was genehmigt werden muss, gibt die
+   Balance gerne auf Anfrage, ansonsten wird aber auch jeder erfahrene
+   Magier aushelfen oder zumindest einen Ansprechpartner vermittln koennen.
+
+Damit ueberlasse ich Dich nun Deiner Arbeit am GS. Wenn Du noch Fragen
+hast (was sicher der Fall sein wird), scheue Dich nicht, sie zu stellen, 
+ob auf einer der Magierebenen oder direkt Deinem Sponsor oder anderen 
+Magiern! Es wird sich sicher jemand finden, der Dir helfen kann oder der
+Dich zumindest auf die richtige Faehrte bringt.
+
+Viele Gruesse und viel Erfolg,
+Dein Merlin.
+
diff --git a/doc/infomails/info_ml21 b/doc/infomails/info_ml21
new file mode 100644
index 0000000..0013306
--- /dev/null
+++ b/doc/infomails/info_ml21
@@ -0,0 +1,32 @@
+Gratulation zur Befoerderung zum Vollmagier
+Hallo @WER1,
+
+ich bin's mal wieder, der Merlin. :-)
+
+Du bist ja jetzt auf Magierlevel 21 angekommen, das heisst, ich kann Dir
+auf diesem Wege zur erfolgreichen Abnahme Deines Gesellenstuecks 
+gratulieren! War ja auch eine Menge Arbeit, oder? ;-)
+
+Bevor Du jetzt aber freudestrahlend anschliessen kannst, moechte ich Dich
+auf einige Dinge hinweisen, die vor dem Anschluss erledigt werden muessen.
+Keine Sorge, das ist nicht allzuviel, und meist recht schnell erledigt.
+
+Als erstes wird sicherlich der Regionsmagier Deinen Code in seine Region
+kopieren (lasst bitte nichts davon in Deinem Home-Verzeichnis zurueck,
+das kann spaeter eine Menge Aerger verursachen). Der Code kann aber erst 
+angeschlossen werden, wenn die Forscherpunkte (FP), Zaubertrank-Fundorte
+(ZTs) und Erstkillstufenpunkte (EKs) beantragt und genehmigt sind
+(die hierfuer zustaendigen EMs sind in "hilfe erzmagier" aufgefuehrt).
+Du wirst hierfuer - je nach Zeitaufwand fuer die beteiligten EMs - eine
+gewisse Wartezeit vor dem Anschluss einkalkulieren muessen. In dieser 
+Zeit kannst Du ja schon einmal einen schoenen Artikel zur stilechten
+Ankuendigung in der MPA entwerfen.
+Weiterhin muessen natuerlich alle Balance-relevanten Objekte genehmigt sein.
+
+Wenn das alles erledigt ist, und damit Dein Werk anschlussfertig, wird
+Dich der Regionsmagier auf Magierlevel 25 befoerdern. Sobald das geschafft
+ist, vergiss nicht, mich zur Party einzuladen! :-)
+
+Liebe Gruesse,
+Dein Merlin
+
diff --git a/doc/infomails/info_ml25 b/doc/infomails/info_ml25
new file mode 100644
index 0000000..1b01a8b
--- /dev/null
+++ b/doc/infomails/info_ml25
@@ -0,0 +1,38 @@
+Du arbeitest jetzt an einer Region mit!
+Hallo @WER1,
+
+mit der Befoerderung auf Magierlevel 25 ist jetzt Dein Gesellenstueck
+endgueltig angeschlossen und wird sicher in Kuerze vielfachen Zuspruch
+von Spielern finden!
+
+Ich wuerde mich freuen, wenn Du auch nach dem Anschluss immer mal ein
+Auge auf Deinen Code werfen wuerdest, um (die gerade in der Anfangszeit
+recht haeufigen) Bugs zu beheben. Ich haette aber natuerlich auch
+ueberhaupt nichts dagegen, wenn Du noch mehr fuers Morgengrauen entwickeln
+wolltest. Wenn Du Ideen dafuer hast, dann sprich doch einfach mit einem
+Regionsmagier, in dessen Region Du ein Plaetzchen dafuer benoetigst! :-)
+
+An dieser Stelle sei Dir erneut der Hinweis auf sorgfaeltige
+Dokumentation Deines Konzepts und der Absprachen mit dem RM dringend ans
+Herz gelegt. Sowas hat ueberhaupt nichts mit Misstrauen Dir oder dem RM
+gegenueber zu tun, sondern einfach damit, dass man auf diese Weise 
+Missverstaendnissen vorbeugen kann - allein die Vorstellung, was ein
+"kleines" oder "groesseres" Gebiet sein soll, unterscheidet sich teils
+erheblich zwischen zwei Personen. (Uebrigens solltest Du fuer den Fall, 
+dass der fuer Deine Region zustaendige Regionsmagier einmal wechseln sollte,
+dem neuen RM Dein Konzept vorlegen, um sicherzugehen, dass dieser davon
+Kenntnis hat und ggf. seine Ansicht beruecksichtigen zu koennen.)
+
+Solltest Du Dich hingegen zu hoeherem berufen fuehlen, und gerne selbst
+eine Region als RM uebernehmen wollen, oder andere Aufgaben als Magier
+wahrnehmen, so kannst Du gerne einen Erzmagier ansprechen oder Dich direkt
+bewerben. Auch wenn man gerne anderes erzaehlt: die beissen alle nicht. ;-)
+
+Achso, eins noch: Wenn Du selbst einen Seher sponsorn moechtest, also
+diesen zum Magier machen, benoetigst Du Magierlevel 26 dafuer. Diesen
+kannst Du durch Befoerderung seitens Deines Regionsmagiers erreichen. 
+Sprich ihn doch einfach mal darauf an.
+
+Viele Gruesse,
+Dein Merlin.
+
diff --git a/doc/infomails/info_ml45 b/doc/infomails/info_ml45
new file mode 100644
index 0000000..6ffd4b8
--- /dev/null
+++ b/doc/infomails/info_ml45
@@ -0,0 +1,21 @@
+Willkommen im Kreis der Regionsmagier!
+Herzlichen Glueckwunsch @WER1!
+
+Ich freue mich, dass Du in den Kreis der Regionsmagier aufgenommen wurdest
+und das MorgenGrauen jetzt einen weiteren aktiven Magier zur Pflege der
+Regionen hat. :-)
+Um Dir die Arbeit zu erleichtern, haben Deine Kollegen haeufig gefragte
+Fragen bzw. haeufige Taetigkeiten eines RMs zusammengestellt. Ich
+empfehle Dir daher, dieses Howto zu lesen, Du findest wichtige Hinweise
+dort: 'man rm-howto'.
+Ebenfalls von Interesse ist fuer Dich sicherlich die Hilfeseite 
+"regionsmagier", welches einige weitere Informationen fuer Dich hat.
+Falls Du einmal Diskussionen fuehren willst (oder Fragen hast), die Du nur mit
+Deinen Regionsmagierkollegen (bzw. RM+) eroertern moechtest, kannst Du hierzu
+die MPA-Rubrik 'regionsmagier' benutzen.
+
+Und nun wuensche ich Dir viel Spass in Deinem neuen Amt!
+
+Liebe Gruesse,
+      Merlin
+
diff --git a/doc/lfun/.default b/doc/lfun/.default
new file mode 100644
index 0000000..d519f69
--- /dev/null
+++ b/doc/lfun/.default
@@ -0,0 +1,16 @@
+muster()
+
+FUNKTION:
+	type muster(...)
+	
+ARGUMENTE:
+	
+BESCHREIBUNG:
+
+RUECKGABEWERT:
+	
+BEMERKUNG:
+	
+BEISPIEL:
+
+SIEHE AUCH:
diff --git a/doc/lfun/.synonym b/doc/lfun/.synonym
new file mode 100644
index 0000000..77c92a5
--- /dev/null
+++ b/doc/lfun/.synonym
@@ -0,0 +1,14 @@
+A_CON QueryAttribute
+A_DEX QueryAttribute
+A_INT QueryAttribute
+A_STR QueryAttribute
+pathd SearchPath
+qpp QueryPossPronoun
+SP_NAME Defend
+SP_NO_ACTIVE_DEFENSE Defend
+SP_NO_ENEMY Defend
+SP_PHYSICAL_ATTACK Defend
+SP_RECURSIVE Defend
+SP_REDUCE_ARMOUR Defend
+SP_SHOW_DAMAGE Defend
+TestIgnoreExt TestIgnore
diff --git a/doc/lfun/AddAction b/doc/lfun/AddAction
new file mode 100644
index 0000000..23183ca
--- /dev/null
+++ b/doc/lfun/AddAction
@@ -0,0 +1,76 @@
+AddAction(L)
+FUNKTION:
+     varargs void AddAction(mixed fun, mixed cmd, int flag, int lvl);
+
+DEFINIERT IN:
+     /std/player/command.c
+
+ARGUMENTE:
+     fun	zu rufende Methode im Spieler oder eine Closure
+     cmd	ausloesendes Kommandoverb
+     flag	unscharf ausfuehren
+     lvl	ab welchem (Magierlevel) funktioniert das Kommando
+
+BESCHREIBUNG:
+     Dem Spieler wird ein neues Kommando definiert. Dieses kann eine Methode
+     in ihm sein, so wie bei allen Spielerkommandos ueblich, kann aber auch
+     eine Closure (Lfun-Closure oder Lambda) enthalten.
+
+     Mittels "flag" kann man die Kommandoerkennung unscharf halten, d.h. wie
+     bei AddCmd(L) und add_action(E) wird ein 'cmd' bei 'flag = 1' auch
+     schon von Praefix-Strings (hier ohne Leerzeichen) getriggert:
+     AddAction([...], "muh", 1, 0) wird zB auch von 'muhtens' oder 'muh4'
+     ausgeloest.
+
+     Mit "lvl" begrenzt man die Ausfuehrbarkeit. Spieler haben ein
+     Magierlevel von 0, Seher von 1.
+
+     Das Kommando wird in P_LOCALCMDS eingetragen. Diese Property wird
+     nicht gespeichert! Effektiv kann man mit AddAction() ein kommando-
+     gebendes Objekt im Spieler einsparen.
+
+BEMERKUNGEN:
+     - es gibt _noch_ kein RemoveAction! Per Hand in P_LOCALCMDS editieren
+       kann zu ernsten Fehlern fuehren.
+     - echte Spielerkommandos kann man damit _nicht_ ueberschreiben,
+       ein AddAction(...,"sag",1,0); funktioniert nicht
+     - ein generelles AddAction(...,"",1,0); geht nicht
+
+BEISPIELE:
+     ...
+     this_player()->AddAction(symbol_function("zeige_mysterium",
+                                              find_object(".../mystzeiger")),
+			      "knorfula",0,0);
+     write(break_string("Wann immer du jetzt das Kommando \"knorfula\" "
+			"eingibst, werden dir Mysterien enthuellt!",78));
+     ...
+
+     // im Objekt "knorfula" ...
+     int zeige_mysterium(string str) {
+       string myst;
+       myst=environment(TP)->QueryMysterium(str);
+       if(myst) {
+         write("Du hast ein Mysterium entdeckt!\n");
+	 write(break_string(myst,78));
+	 say(break_string(
+		TP->Name(WER)+" scheint nach kurzer Konzentration etwas "
+		"entdeckt zu haben!",78));
+       } else {
+         write(break_string(
+		"Leider entdeckst du trotz deiner magischen Faehigkeit "
+		"kein Mysterium in deiner Umgebung.",78));
+	 say(break_string(
+		TP->Name(WER)+" konzentriert sich sichtbar, sieht dann "
+		"jedoch etwas enttaeuscht aus.",78));
+       }
+       return 1;
+     }
+
+SIEHE AUCH:
+			P_LOCALCMDS
+     Fehlermeldungen:	notify_fail(E), _notify_fail(E)
+     Argumentstring:	query_verb(E), _unparsed_args(L)
+     Sonstiges:		replace_personal(E), enable_commands(E)
+     Alternativen:	AddCmd(L), add_action(E)
+
+24. Maerz 2004 Gloinson
diff --git a/doc/lfun/AddAdjective b/doc/lfun/AddAdjective
new file mode 100644
index 0000000..6b58101
--- /dev/null
+++ b/doc/lfun/AddAdjective
@@ -0,0 +1,39 @@
+AddAdjective()
+
+FUNKTION:
+     void AddAdjective(string|string* adj);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     adj
+          String oder Array von String mit den Adjektiven.
+
+BESCHREIBUNG:
+     Zusaetzlich zu den mit AddId() vergebenen Bezeichnern laesst sich mit
+     der Vergabe von Adjektiven die Ansprechbarkeit eines Objektes erhoehen.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Die Adjektive werden nicht dekliniert, man muss also fuer jeden
+     sinnvollen Fall ein Adjektiv uebergeben.
+
+BEISPIELE:
+
+       AddId( ({ "zettel", "blatt" }) );
+       AddAdjective( ({ "kleinen", "kleines" }) );
+
+     Das Objekt reagiert jetzt auf "zettel", "kleinen zettel", "blatt",
+     "kleines blatt" sowie auf die (sprachlich nicht ganz so korrekten)
+     Konstruktionen "kleines zettel", "kleinen blatt", "kleines kleinen
+     zettel", ...
+
+SIEHE AUCH:
+     AddId(), RemoveAdjective() id(), present(), /std/thing/description.c
+
+----------------------------------------------------------------------------
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/AddAmount b/doc/lfun/AddAmount
new file mode 100644
index 0000000..3be8e3f
--- /dev/null
+++ b/doc/lfun/AddAmount
@@ -0,0 +1,25 @@
+AddAmount()
+
+FUNKTION:
+     void AddAmount(int menge);
+
+DEFINIERT IN:
+     /std/unit.c
+
+ARGUMENTE:
+     menge
+          Die Menge, die der Unit hinzugefuegt werden soll (kann auch
+          negativ sein).
+
+BESCHREIBUNG:
+     menge wird zur aktuellen Menge hinzugezaehlt. Sollte das Ergebnis 0
+     werden, wird die Unit in absehbarer Zeit zerstoert.
+
+RUeCKGABEWERT:
+     keiner
+
+SIEHE AUCH:
+     /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:15:50 1996 by Wargon
diff --git a/doc/lfun/AddClass b/doc/lfun/AddClass
new file mode 100644
index 0000000..f97730c
--- /dev/null
+++ b/doc/lfun/AddClass
@@ -0,0 +1,28 @@
+AddClass()
+FUNKTION:
+     void AddClass(string|string* class);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     string/string* class	- String oder Stringarray der Klasse(n)
+
+BESCHREIBUNG:
+     Dem Objekt werden weitere Klassifizierungen hinzugefuegt.
+
+     Die allgemein verfuegbaren Klassen sind unter /sys/class.h definiert.
+
+BEMERKUNGEN:
+     Vor dem 7. Nov 2012 pruefte is_class_member() auch die P_IDS. Damit war
+     zB ein AddId("daemon") gleichbedeutend einem AddClass(CL_DEMON).
+
+     Bitte prueft eure Objekte (NPCs, Krankheiten, Gifte, ...) auf korrekte
+     Klassifizierung.
+
+SIEHE AUCH:
+     RemoveClass(), is_class_member()
+     P_CLASS
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/AddCmd b/doc/lfun/AddCmd
new file mode 100644
index 0000000..aeea8db
--- /dev/null
+++ b/doc/lfun/AddCmd
@@ -0,0 +1,210 @@
+AddCmd(L)
+FUNKTION:
+    varargs void AddCmd(mixed cmd, mixed func, [mixed flag, [mixed id]]);
+
+DEFINIERT IN:
+    /std/thing/commands.c
+
+ARGUMENTE:
+    cmd
+       Verben, auf die reagiert werden soll
+       ODER
+       Regel mit Triggerverb und noetigen Keywords/Synonymen
+    func
+       Funktionsname im selben Objekt oder Closure (Funktionspointer)
+    flag (optional)
+       Unscharfe Ausfuehrung == 1
+       ODER
+       Fehlermeldungen bei Nutzung von Regeln
+    id (optional)
+       eine ID, ueber die das Kommando eindeutig wieder geloescht
+       werden kann
+
+BESCHREIBUNG:
+    Wenn ein Spieler im Einflussbereich des Objektes das Verb benutzt,
+    wird die entsprechende Funktion im Objekt aufgerufen:
+    - die Verben sollten Imperative sein
+    - die Funktion muss 1 fuer ERFOLG (und FPs!) und 0 sonst zurueckgeben
+      (sonst werden evtl. weitere Funktionen mit diesem Kommandoverb gerufen)
+    - innerhalb der Funktionen koennen Fehlermeldungen fuer den totalen
+      Misserfolg des Kommandos mit notify_fail gesetzt werden
+      (anstatt "Wie bitte?")
+
+    AddCmd ist ein dynamischer Ersatz fuer add_action - im Gegensatz
+    zu add_action koennen auch ohne erneuten Aufruf der init() neue
+    Kommandos hinzugefuegt werden.
+
+    AddCmd kann das Leben einfacher machen mit:
+    ### REGELN: ###
+      Bei komplexen Syntaxen kann ein String angegeben werden, der die
+      _notwendigen_ (nicht die erlaubten!) Schluesselworte und deren
+      zulaessige Synonyme beschreibt. Damit kann man sich einen grossen
+      Teil eigener Auswertung ersparen.
+
+      Nur wenn in richtiger Reihenfolge aus JEDER der durch & getrennten
+      Synonymgruppen ein Wort im Spielerkommando enthalten ist, wird
+      die zugehoerige Funktion ausgefuehrt.
+
+      Trenner sind: | fuer Alternativen
+                    & fuer Konjunktionen
+
+      Beispiel:
+        "ritz|ritze|schnitz|schnitze&mit&messer|schnitzmesser&"
+        "herz|herzchen&rinde|baumrinde"
+      wuerde z.B. durch
+        "ritz mit dem Messer ein Herz in die Rinde des Baumes"
+        "schnitz mit Messer Herzchen Baumrinde"
+        "schnitz mit meinem Messer Herzchen in die harte Baumrinde"
+      erfuellt werden.
+
+      Spezialregelteile sind:
+      - @PRESENT: entspricht einem Objekt in Inv oder Env des Spielers
+      - @ID:      entspricht der ID des kommandobesitzenden Objektes
+                  (wo die Kommandomethode definiert ist, ist noch unwichtig)
+      - @PUT_GET_DROP, @PUT_GET_TAKE, @PUT_GET_NONE:
+                  entsprechend den Filteroptionen fuer find_obs()
+      ACHTUNG: Zusaetzliche Ziffern in Verbindung mit den @-Spezialregeln
+               sind schlecht. @-Regeln versuchen gierig, Objekte exakt im
+               Inventory zu matchen ("objekt 3" anstatt "objekt") und miss-
+               interpretieren daher zB die 4 in "stopf objekt 3 in loch 4" als
+               Teil des Objekt-ID-Strings.
+               Interna: 3 Substrings fuer @PRESENT/@ID ("gruener kristall 2")
+                        5 fuer @PUT... ("kristall 2 in beutel 3")
+
+    ### FEHLERMELDUNGEN (bei Anwendung von Regeln): ###
+      Als dritter Parameter koennen auch Fehlermeldungen fuer jeweils
+      fehlende Synonymgruppen (ausser der ersten - den Kommandoverben)
+      angegeben werden. Sie werden in derselben Reihenfolge (!) wie die
+      Synonymgruppen angegeben.
+
+      Mit nicht von Spielern erfuellbaren Regeln und ^-Fehlermeldungen
+      kann man auch ohne Ausfuehrung einer Funktion Texte an Spieler
+      und Umgebung ausgeben. Siehe dazu AddCmd_bsp.
+
+      Trenner sind: | zum Trennen der einzelnen Fehlermeldungen
+                    ^ um
+                       - die Auswertung (ab dieser Fehlermeldung!) mit
+                         "return 1;" zu beenden und
+                       - eine write^say-Meldung zu trennen und
+                       - (fuer funktionslose AddCmd auch FPs zu vergeben!)
+
+      Beispielfehlermeldungen fuer obige Regel:
+        "Womit willst Du schnitzen?|Was willst Du schnitzen?|"
+        "Wohinein willst Du das schnitzen?"
+
+      Es koennen in den Fehlermeldungen folgende Platzhalter benutzt
+      werden:
+      - @verb (ersetzt durch query_verb() ohne beendendes 'e')
+      - @VERB (ersetzt durch capitalize(query_verb()) ohne beendendes 'e')
+      - @WERx, @WESSENx, @WEMx, @WENx: siehe alles aus replace_personal()
+        - @WE..1 ist immer der aktive Spieler
+        - alle folgenden sind die matchenden Parameter der Spielereingabe
+          - (x-1)<=Fehlermeldung (da x=1 Spieler und
+                                  (x-1)>Fehlermeldungsobjekt nicht existent)
+
+      Ausfuehrungsbeispiel:
+        AddCmd("ritz|ritze|schnitz|schnitze&mit&messer|schnitzmesser&"
+               "herz|herzchen&rinde|baumrinde",#'fun,
+              "Willst Du mit etwas @verben?|Womit willst du @verben?|"
+              "Was willst du mit dem @WEM3 @verben?|"
+              "Wohinein willst Du das @WEN4 schnitzen?");
+        1. "ritze" == "Willst Du mit etwas ritzen?"
+        2. "schnitz mit" == "Womit willst du schnitzen?"
+        3. "ritz mit messer" == "Was willst du mit dem messer ritzen?"
+        4. "ritze mit dem messer ein herz" ==
+             "Wohinein willst Du das herz schnitzen?"
+        5. "ritze mit dem messer ein herzchen in die baumrinde"
+             == Erfolg!
+
+    ### UNSCHARFER AUSFUEHRUNG: ###
+      Bei unscharfer Ausfuehrung wird die zugehoerige Methode auch dann
+      ausgefuehrt, wenn das verwendete Verb ein Superstring ist und
+      bisher noch nicht behandelt wurde.
+      Dieses Verhalten sollte nur beim generellen Abfangen von
+      Befehlsgruppen benutzt werden und ist ansonsten veraltet. Es
+      entsprich add_action("fun","kommando",1).
+
+
+      Beispiel:
+        1. AddCmd("klett","fun",1);
+        2. AddCmd("kletter|klettere&hoch",#'fun2,"Wohin klettern?");
+
+        a) "klett"
+        b) "kletter"
+        c) "klettere hoch"
+
+        Ausgefuehrte Funktion bei: 1a, 1b, 1c; 2c
+       (1 wuerde also immer ausgefuehrt)
+        Fehlermeldung bei: 2b ("Wohin klettern?")
+
+BEMERKUNGEN:
+    - Methoden der put_and_get (nimm/nehme) sollten so nicht versucht
+      werden zu ueberschreiben - dazu sind invis Container da
+    - benutzt man fuer <function> eine Closure, kann man die Fkt. auch
+      protected oder private deklarieren _und_ sie kann in einem
+      anderen Objekt sein
+    - bei Regeln wird an die ggf. gerufene Methode als zweiter Parameter
+      ein Array der erfuellenden Eingabeteile uebergeben:
+      "steck&@PRESENT&in&loch" bei Erfuellung -> ({<Objekt>,"in","loch})
+      - bei Nutzung von @PUT_GET_XXX koennen die Parameter wiederum
+        Arrays sein ("jede Hose" -> mehrere gueltige Objekte)
+    - juengere AddCmd ueberschreiben aeltere, bzw. werden vor diesen
+      ausgewertet
+    - @PUT_GET_XXX kosten sehr viel Auswertungszeit
+
+BEISPIELE (SIEHE AUCH ADDCMD_BSP):
+    // SIMPEL: ganz simpel, beinahe wie add_action
+    AddCmd("befiehl","action_befehlen");
+    ...
+    int action_befehlen(string str) {
+     if(!str || !strlen(str))
+      // Fehlermeldung, falls gar keine Funktion 1 dafuer zurueckgibt
+      notify_fail("Was willst du befehlen?!\n");
+     else {
+      write("Du befiehlst \""+str+"\", und alle folgen!\n");
+      say(TP->Name(WER)+" befiehlt \""+str+"\", und du folgst!\n");
+      return 1;  // ERFOLG - Abbruch der Kommandoauswertung
+     }
+     return 0;  // MISSERFOLG - Fehlermeldung oben gesetzt
+    }
+
+    // SIMPEL .. weitere Beispiele
+    AddCmd(({"kletter","klettere"}),"action_klettern" );
+    AddCmd(({"renn","renne"}),#'action_rennen);
+
+    // REGELN: eine komplexere Regel
+    AddCmd("loesch|loesche|ersticke&feuer|brand|flammen&decke|wolldecke",
+           "action_loeschen",
+           "Was willst du loeschen?|Womit willst du loeschen?");
+
+    // REGELN: mit Platzhaltern im Fehlerstring
+    AddCmd("spring|springe|huepf|huepfe&von|vom&baum|ast|eiche",
+           #'action_huepfe,
+           "Willst du von etwas @verben?|Von wo willst du @verben?");
+
+    // SCHLECHT: eine unscharfe Regel - sie sollten eine Ausnahme sein (!)
+    AddCmd("kletter","fun_klettern",1);
+
+    // FALSCH: sehr schlecht, kein Imperativ verwendet
+    // ausserdem sollte man fuer solche Syntaxen AddReadDetail benutzen
+    AddCmd("lese","eval_lesen");
+
+    // SIMPLE REGEL OHNE METHODE
+    // mit Regeln kann man auch Aktivitaeten im Raum erlauben, ohne eine
+    // Funktion aufrufen zu muessen: die letzte Regel ist fuer Spieler
+    // unmoeglich zu erfuellen, die dazugehoerige Fehlermeldung wird mit
+    // dem ^ (write-Flag) versehen und entsprechend an den Spieler
+    // (und den Raum (hinter dem ^)) ausgegeben
+    AddCmd("spring|springe&herunter|runter&\n\bimpossible",0,
+           "Wohin oder wovon willst Du springen?|"
+           "Du springst vom Baum und kommst hart auf.^"
+           "@WER1 springt vom Baum und kommt hart auf.");
+
+SIEHE AUCH:
+    AddCmd_bsp, RemoveCmd(L), init(E)
+    Fehlermeldungen: notify_fail(E), _notify_fail(E)
+    Argumentstring: query_verb(E), _unparsed_args(L)
+    Sonstiges:  replace_personal(E), enable_commands(E)
+    Alternativen: AddAction(L), add_action(E)
+
+30. Aug 2013 Gloinson
diff --git a/doc/lfun/AddCmd_bsp b/doc/lfun/AddCmd_bsp
new file mode 100644
index 0000000..edb8d98
--- /dev/null
+++ b/doc/lfun/AddCmd_bsp
@@ -0,0 +1,320 @@
+ADDCMD() - BEISPIELE
+FUNKTION
+    varargs void AddCmd(mixed cmd, mixed func, mixed flag);
+
+BEMERKUNGEN
+    Die hier aufgefuehrten Komplexbeispiele sind zum Verstaendnis gedacht,
+    daher fuehren sie oft Alternativen auf. Die letzte Variante ist dann
+    jeweils diejenige, welche am leichtesten das Problem loesen koennte.
+    Falls die einem zu komplex ist, hilft vielleicht die vorletzte.
+
+BEISPIELE
+    // SIMPEL: ganz simpel, beinahe wie add_action
+    AddCmd("befiehl","action_befehlen");
+    ...
+    int action_befehlen(string str) {
+     if(!str || !strlen(str))
+      // Fehlermeldung, falls gar keine Funktion 1 dafuer zurueckgibt
+      notify_fail("Was willst du befehlen?!\n");
+     else {
+      write("Du befiehlst \""+str+"\", und alle folgen!\n");
+      say(TP->Name(WER)+" befiehlt \""+str+"\", und du folgst!\n");
+      return 1;		// ERFOLG - Abbruch der Kommandoauswertung
+     }
+     return 0;		// MISSERFOLG - Fehlermeldung oben gesetzt
+    }
+
+    // SIMPEL .. weitere Beispiele
+    AddCmd(({"kletter","klettere"}),"action_klettern" );
+    AddCmd(({"renn","renne"}),#'action_rennen);
+
+    // REGELN: eine komplexere Regel
+    AddCmd("loesch|loesche|ersticke&feuer|brand|flammen&decke|wolldecke",
+           "action_loeschen",
+           "Was willst du loeschen?|Womit willst du loeschen?");
+
+    // REGELN: mit Platzhaltern im Fehlerstring
+    AddCmd("spring|springe|huepf|huepfe&von|vom&baum|ast|eiche",
+           #'action_huepfe,
+           "Willst du von etwas @verben?|Von wo willst du @verben?");
+
+    // SCHLECHT: eine unscharfe Regel - sie sollten eine Ausnahme sein (!)
+    AddCmd("kletter","fun_klettern",1);
+
+    // FALSCH: sehr schlecht, kein Imperativ verwendet
+    // ausserdem sollte man fuer solche Syntaxen AddReadDetail benutzen
+    AddCmd("lese","eval_lesen");
+
+    // SIMPLE REGEL
+    static int action_jump(string str);        // Prototype (wegen closure)
+    ...
+    AddCmd("spring|springe|huepf|huepfe&von&baum|ast",#'action_jump,
+           "Willst Du von etwas @verben?|Wovon willst Du @verben?");
+    ...
+    static int action_jump(string str) {
+      write(break_string("Du springst vom Baum und kommst hart auf!",78));
+      this_player()->move((XXXROOM+"boden"), M_GO, 0,
+                          "springt unelegant vom Baum","faellt vom Baum");
+      this_player()->Defend(random(100),({DT_BLUDGEON}),([SP_RECURSIVE:1]),
+                            this_object());
+      return 1;
+    }
+
+    // SIMPLE REGEL OHNE METHODE
+    // mit Regeln kann man auch Aktivitaeten im Raum erlauben, ohne eine
+    // Funktion aufrufen zu muessen: die letzte Regel ist fuer Spieler
+    // unmoeglich zu erfuellen, die dazugehoerige Fehlermeldung wird mit
+    // dem ^ (write-Flag) versehen und entsprechend an den Spieler
+    // (und den Raum (hinter dem ^)) ausgegeben
+    AddCmd("spring|springe&herunter|runter&\n\bimpossible",0,
+           "Wohin oder wovon willst Du springen?|"
+           "Du springst vom Baum und kommst hart auf.^"
+           "@WER1 springt vom Baum und kommt hart auf.");
+
+## Komplexbeispiel: Regeln mit Fehlermeldungen ##
+ ## Variante 1, OHNE REGELN ##
+    // bei Nichtverwendung von Regeln muss man Parameter selbst auswerten
+    AddCmd(({"bohr","bohre"}),#'action_bohren);
+    ...
+    private static int action_bohren(string str) {
+      string *tmp;
+      notify_fail("Wo willst (etwas) Du bohren?\n");
+      if(!str) return 0;       // Tja, keine Argumente ...
+      tmp=explode(str," ");    // nach " " in Argument-Array aufspalten
+      if((i=member(tmp,"loch"))>=0) { // aha, ab jetzt uebernehmen wir :)
+       if((j=member(tmp[(i+1)..],"in"))<0 &&
+          (j=member(tmp[(i+1)..],"durch"))<0)
+         write("Willst Du das Loch in etwas bohren?\n");
+        else if((i=member(tmp[(j+1)..],"boden"))<0 &&
+                (i=member(tmp[(j+1)..],"erde"))<0)
+         write("In/Durch was willst du das Loch bohren?\n");
+        else {
+         write("Du bohrst ein Loch in den Boden.\n");
+         say(this_player()->Name(WER)+" bohrt ein Loch in den Boden.\n");
+        }
+        return 1;      // "bohre loch" war so eindeutig, dass nur diese
+                       // Methode gemeint sein konnte, also brechen wir die
+                       // weitere Auswertung auf jeden Fall ab (und geben
+                       // eine write-Fehlermeldung)
+      } // end if(..."loch")
+      return 0;        // "bohre" allein muss nicht diese Methode meinen,
+                       // also nur obige notify_fail()-Meldung, falls
+                       // sich nach dieser Methode gar keine sonst
+                       // angesprochen fuehlt
+    } // end fun
+
+ ## Variante 1a, OHNE REGELN ##
+    // prinzipiell koennte die Methode action_bohren auch so
+    // aussehen, ist aber nicht ganz so flexibel:
+    private static int action_bohren(string str) {
+     string tmp;
+     if(!str || (sprintf(str,"loch in erde%s",tmp)!=1 &&
+                 sprintf(str,"loch durch erde%s",tmp)!=1 &&
+                 sprintf(str,"loch in boden%s",tmp)!=1 &&
+                 sprintf(str,"loch durch boden%s",tmp)!=1))
+      notify_fail("Willst Du in irgendwas ein Loch bohren?\n");
+     else {
+      ...
+      return 1;
+     }
+     return 0;
+    }
+
+ ## Variante 2, MIT REGEL ##
+    // das gleiche in etwa mal als einfache Regel
+    AddCmd("bohr|bohre&loch&in|durch&erde|boden",#'action_bohren,
+           "Was willst du (wohin) bohren?|"
+           "Willst du das Loch in etwas bohren?|"
+           "Wohin willst du das Loch bohren?");
+    ...
+    private static int action_bohren(string str, mixed *param) {
+     write("Du bohrst ein Loch in den Boden.\n");
+     say(this_player()->Name(WER)+" bohrt ein Loch in den Boden.\n");
+     ...
+     return 1;
+    }
+
+ ## Variante 3, MIT REGEL UND FEHLERMELDUNG ##
+    // und nun mit Fehlermeldungen mit Ersetzungen, so dass wir mehr
+    // auf die Eingaben des Spielers eingehen
+    AddCmd("bohr|bohre&loch&in|durch&erde|boden",#'action_bohren,
+           "Was willst du (wohin) @verben?|"
+           "Willst du das Loch in etwas @verben?|"
+           "@WER3 was willst du das Loch @verben?");
+    ...
+    private static int action_bohren(string str, mixed *param) ...
+
+ ## Variante 4, MIT REGEL, FEHLERMELDUNG UND RETURN 1 ##
+    // in Variante 1 kam sinnvollerweise sehr frueh der Abbruch mit
+    // "return 1;" und die Ausgabe von write-Fehlermeldungen,
+    // das koennen wir auch
+    AddCmd("bohr|bohre&loch&in|durch&erde|boden",#'action_bohren,
+           "Was willst du (wohin) @verben?|"
+           "Willst du das Loch in etwas @verben?^|"
+           "@WER3 was willst du das Loch @verben?^");
+    ...
+    private static int action_bohren(string str, mixed *param) ...
+
+ ## Variante 5, MIT REGEL, FEHLERMELDUNG, RETURN 1, OHNE FUN ##
+    // und falls in action_bohren() nichts ausser Ausgaben passiert, koennen
+    // wir uns die auch ganz sparen indem wir eine nichterfuellbare Regel
+    // samt Fehlermeldung bauen
+    AddCmd("bohr|bohre&loch&in|durch&erde|boden&\nimpossible",0,
+           "Was willst du (wohin) @verben?|"
+           "Willst du das Loch in etwas @verben?^|"
+           "@WER3 was willst du das Loch @verben?^|"
+           "Du @verbst ein Loch @WER3 den Boden.^@WER1 @verbt "
+           "ein Loch @WER3 den Boden.");
+
+   --- Ende Komplexbeispiel Regeln mit Fehlermeldungen ---
+
+## Komplexbeispiel: Spezialregeln @PRESENT und @ID ##
+ ## Variante 1, OHNE REGELN ##
+    // oft agieren Kommandos auf Objekten im Raum, diese muessen dabei per
+    // present() identifiziert werden:
+    // Beispiel ist ein Geldautomat (den man besser mit einem Container
+    // mit PreventInsert() basteln sollte)
+    AddCmd(({"stopf","stopfe"}),#'action_stopf);
+    ...
+    private static int action_stopf(string str) {
+     string tmp,tmp2;
+     object o;
+
+     if(str && (sprintf("%s in automat%s",tmp,tmp2)==2 ||
+                sprintf("%s in geldautomat%s",tmp,tmp2)==2 ||
+                sprintf("%s in bankomat%s",tmp,tmp2)==2) {
+      o=present(tmp,this_player());
+      if(o) {
+       if(o->QueryProp(...)) {
+        write(break_string(
+         "Du stopfst "+o->name(WEN,1)+" in den Automaten.",78));
+        say(...);
+       } else {
+        write(break_string(
+         "Du versuchst "+o->name(WEN,1)+" in den Automaten zu stopfen, "
+         "aber "+o->QueryPronoun(WER)+" passt nicht hinein.",78));
+        say(...);
+       }
+      } else {
+       write("Was willst du in den Automaten stopfen?\n");
+       say(....);
+      }
+      return 1;
+     }
+     notify_fail("Was willst du wohin stecken?\n");
+     return 0;
+    }
+
+ ## Variante 2, MIT REGEL ##
+    // einerseits koennen wir das Finden von Objekten in Inv und Env
+    // integrieren und uns andererseits das Aufzaehlen aller IDs des
+    // Automaten ersparen
+    AddCmd("steck|stecke&@PRESENT&in&@ID",#'action_stopf,
+           "Was willst du wohin stopfen?|"
+           "Willst du @WEN2 in etwas stopfen?|"
+           "Wohinein willst du @WEN2 stopfen?");
+    ...
+    // dabei werden wie immer die gefunden Matches als Parameterarray
+    // uebergeben ... und die @PRESENT und @ID als Objekte!
+    private static int action_stopf(string str, mixed *param) {
+     if(param[0]->QueryProp(...)) {
+      write(break_string(
+       "Du stopfst "+param[0]->name(WEN,1)+" in den Automaten.",78));
+      say(...);
+     } else {
+      write(break_string(
+       "Du versuchst "+param[0]->name(WEN,1)+" in den Automaten zu "
+       "stopfen, aber "+param[0]->QueryPronoun(WER)+" passt nicht "
+       "hinein.",78));
+      say(...);
+     }
+     return 1;
+    }
+
+   --- Ende Komplexbeispiel Spezialregeln @PRESENT und @ID  ---
+
+## Komplexbeispiel: gleiches Verb, mehrere Regeln ##
+ // Das Problem mehrerer Regeln fuer ein Kommandoverb besteht darin, dass
+ // letztlich nur eine der Fehlermeldungen zum Tragen kommt - welche
+ // genau ist etwas vage.
+ // Dabei kann man sich auf eines verlassen: juengere AddCmd werden
+ // zuerst ausgewertet. Wenn sich das aendert, tretet euren EM.
+
+ ## Problem 1: Mehrere Regeln weil mehrere Zwecke ##
+    ## Variante 1 - GLEICHLAUTENDE FEHLERMELDUNG
+    // fuer alles wird eine identische Fehlermeldung gesetzt, das ist
+    // natuerlich nicht sehr flexibel oder schoen
+    AddCmd("kriech|krieche&hoch|hinauf|hinaus|heraus|raus",#'result_kriech,
+           "Wohin willst Du kriechen?");
+    AddCmd("kriech|krieche&nach&oben",#'result_kriech,
+           "Wohin willst Du kriechen??|Wohin willst Du kriechen?");
+    AddCmd("kriech|krieche&aus&loch|grube|falle",#'result_kriech);
+           "Wohin willst Du kriechen?|Wohin willst Du kriechen?");
+
+    // oder man versucht eine bessere Regel zu schaffen, was hier durch
+    // die Moeglichkeit von zwei oder drei Parameter unmoeglich ist
+
+    ## Variante 2 - EIGENE AUSWERTUNG
+    // es bietet sich also eigene Weiterauswertung an, was durch die
+    // Uebergabe der getriggerten Verben erleichtert wird:
+    AddCmd("kriech|krieche&hoch|hinauf|hinaus|heraus|raus|aus|nach",
+           #'result_kriech,
+           "Wohin willst Du kriechen?");
+    ...
+    static int result_kriech(string str, mixed *extra) {
+      if(member(extra,"aus")>=0 &&
+         !sizeof(({str}),"*.\\<(hoehle|grube|falle)\\>.*"))
+       notify_fail("Woraus willst Du kriechen?\n");
+      else if(member(extra,"nach")>=0 && strstr(str,"oben")<0)
+       notify_fail("In welche Richtung willst Du kriechen?\n");
+      else if(this_player()->QueryAttribute(A_DEX)>10 ||
+              member(holding_root,this_player())) {
+        write("Du kriechst mit Muehe heraus.\n");
+        this_player()->move((XXXROOM+"draussen"), M_GO, 0,
+                            "kriecht mit Muehe aus der Grube",
+                            "kriecht aus einer Grube");
+        return 1;
+      } else
+        write("Du bist zu ungeschickt, halt Dich irgendwo fest.\n");
+        return 1;
+      }
+      return 0;
+    }
+    // (ob sich der Aufwand fuer diese Beispielsyntax lohnt ist fraglich)
+
+ ## Problem 2: mehrere Regeln, weil optionale Parameter ##
+    // Manchmal will man optionale Parameter erlauben, die aber eine
+    // Wirkung zeigen sollen:
+    AddCmd("schlag|schlage&@ID&hart",#'action_schlag_hart,
+           "Was oder wen willst du @verben?|"
+           "Wie willst du @WEN2 schlagen?");
+    AddCmd("schlag|schlage&@ID",#'action_schlag,
+           "Was oder wen willst du @verben?");
+
+    // Da juengere AddCmd aelteren vorgehen, wird die komplexere Regel samt
+    // ihrer Fehlermeldung nie ausgewertet, da ein "schlag ball hart" auch
+    // die zweite Regel triggert.
+
+    // anders herum:
+    AddCmd("schlag|schlage&@ID",#'action_schlag,
+           "Was oder wen willst du @verben?");
+    AddCmd("schlag|schlage&@ID&hart",#'action_schlag_hart,
+           "Was oder wen willst du @verben?|"
+           "Wie willst du @WEN2 schlagen?");
+
+    // Jetzt wird die komplexere Regel zuerst ueberprueft und triggert
+    // auch die richtige Funktion.
+    // Leider kommt die Fehlermeldung nie zum Tragen, denn was durch Regel 2
+    // durchfaellt, triggert entweder Regel 1 oder faellt auch durch Regel 1
+    // durch und ueberschreibt dabei die Meldung.
+
+    AddCmd("schlag|schlage&@ID",#'action_schlag,
+           "Was oder wen willst du wie @verben?");
+    AddCmd("schlag|schlage&@ID&hart",#'action_schlag_hart);
+
+    // Fast perfekt. Besser wird es nicht.
+
+
+    --- Ende Komplexbeispiel mehrere Regeln ---
+
+10 Juni 2004 Gloinson
diff --git a/doc/lfun/AddDefender b/doc/lfun/AddDefender
new file mode 100644
index 0000000..3eb7923
--- /dev/null
+++ b/doc/lfun/AddDefender
@@ -0,0 +1,34 @@
+AddDefender()
+
+FUNKTION:
+	void AddDefender(object friend);
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	friend
+	  Objekt (normal Lebewesen), welches zukuenftig ueber Angriffe
+	  informiert werden soll oder diese sogar abwehrt.
+
+BESCHREIBUNG:
+	Ein Lebewesen, welches angegriffen wird, kann andere Objekte ueber
+	einen solchen Angriff per InformDefend() informieren oder ihnen
+	sogar die Moeglichkeit geben, per DefendOther() direkt in den
+	laufenden Angriff einzugreifen (Schaeden abwehren oder umwandeln).
+	Im Normalfall handelt es sich hierbei um andere Lebewesen, welche
+	als Verteidiger des angegriffenen Lebewesens auftreten: Daher der
+	Name der Funktion.
+	Die Objekte sind in Form eines Arrays in der Property P_DEFENDERS
+	abgespeichert und koennen dort abgerufen werden. Natuerlich kann
+	man weitere Objekte direkt dort eintragen, jedoch sollte man die
+	hierfuer bereitgestellte Funktionen AddDefender() verwenden.
+	Zum Loeschen von Eintraegen im Array steht ebenfalls eine Funktion
+	bereit: RemoveDefender().
+
+SIEHE AUCH:
+	RemoveDefender(), InformDefend(), DefendOther(),
+	P_DEFENDERS, /std/living/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Thu Jul 29 18:48:45 1999 by Patryn
diff --git a/doc/lfun/AddDetail b/doc/lfun/AddDetail
new file mode 100644
index 0000000..229197b
--- /dev/null
+++ b/doc/lfun/AddDetail
@@ -0,0 +1,91 @@
+AddDetail()
+
+FUNKTION:
+    void AddDetail(string|string* keys,
+                   string|string*|mapping|closure desc);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+      String oder Array von Strings mit den Namen der Details.
+    desc
+      String, Mapping, String-Array oder Closure mit/zur Beschreibung.
+
+BESCHREIBUNG:
+    Die Beschreibung der Details <keys> wird gesetzt. Wie die Details
+    bei der Untersuchung aussehen, haengt im wesentlichen vom Typ der
+    Beschreibung <desc> ab:
+      <desc> ist ein String.
+        Beim Untersuchen wird dieser String zurueckgegeben.
+      <desc> ist ein String-Array.
+        Beim Untersuchen wird zufaellig einer der Strings zurueckgegeben.
+      <desc> ist ein Mapping.
+        Das Mapping muss folgenden Aufbau haben:
+          ([0:        "Defaulttext",
+            "rasse1": "r1text", ...]).
+
+        Falls fuer die Rasse des das Detail untersuchenden Spielers ein
+        Eintrag im Mapping existiert, wird der entsprechende Text
+        zurueckgegeben, ansonsten der Defaulttext. Auf diese Weise sind
+        rassenabhaengige Details moeglich. Siehe auch die Beispiele.
+      <desc> ist eine Closure.
+        In diesem Fall wird die Closure ausgefuehrt und das Ergebnis
+        zurueckgegeben. Die Closure bekommt dabei den Namen des Details
+        als Parameter uebergeben.
+
+    Fuer Details koennen Forscherpunkte eingetragen werden.
+
+BEISPIELE:
+    Ein schlichtes Detail:
+
+      AddDetail(({"sofa","couch"}), "Eine kleine Couch.\n");
+
+    Laengere Details sollten hierbei nicht per Hand umgebrochen werden,
+    sondern man kann hierzu die Funktion break_string() nutzen:
+
+      AddDetail("detail", break_string(
+        "Du wolltest es ja nicht anders, jetzt musst Du Dir dieses "
+        "fuerchterlich lange Detail durchlesen!!!", 78));
+
+    Noetige Zeilenumbrueche bei Zeilenlaengen groesser 77 werden so
+    automatisch generiert.
+    Ein rassenabhaengiges Detail:
+
+      AddDetail(({"bett","bettchen"}),
+        ([0      :"Eine kleines Bett.\n", // Der Defaulttext
+          "zwerg":                        // Die Rasse klein schreiben
+                "Das Bett laedt geradezu zu einem Nickerchen ein.\n"]));
+
+    Und nun ein Detail mit Closure (diese Version ersetzt das Verhalten
+     von AddSpecialDetail).
+
+      int hebel_betaetigt;
+      ...
+      string hebel(string str); // Funktion bekannt machen (Prototyping)
+      ...
+      AddDetail(({"hebel","schalter"}), #'hebel);
+      ...
+      string hebel(string key) {
+        if(hebel_betaetigt)
+          return "Der "+capitalize(key)+" steht auf EIN.\n";
+        else
+          return "Der "+capitalize(key)+" steht auf AUS.\n";
+      }
+
+    Man erhaelt verschiedene Ergebnisse beim Untersuchen, je nachdem
+    ob das Flag hebel_betaetigt gesetzt ist oder nicht.
+
+SIEHE AUCH:
+    Setzen:    AddReadDetail(), AddSmells(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveDetail(), RemoveReadDetail(), RemoveSmells(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS,
+               P_TOUCH_DETAILS, P_SPECIAL_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/AddDrink b/doc/lfun/AddDrink
new file mode 100644
index 0000000..6b99e08
--- /dev/null
+++ b/doc/lfun/AddDrink
@@ -0,0 +1,12 @@
+AddDrink()
+
+BEMERKUNGEN:
+
+        Die Funktion AddDrink() sollte NICHT MEHR BENUTZT werden.
+        Bitte AddToMenu() verwenden.
+
+SIEHE AUCH:
+	AddToMenu(), RemoveFromMenu()
+
+----------------------------------------------------------------------------
+Last modified: Fri Mar 03 13:23:00 2000 by Paracelsus
diff --git a/doc/lfun/AddExit b/doc/lfun/AddExit
new file mode 100644
index 0000000..a070682
--- /dev/null
+++ b/doc/lfun/AddExit
@@ -0,0 +1,91 @@
+AddExit()
+FUNKTION:
+     void AddExit(string|string* cmd, closure|string dest);
+
+DEFINIERT IN:
+     /std/room/exits
+
+ARGUMENTE:
+     string/string* cmd
+          die Richtung(en), in die der Ausgang fuehrt
+     string/closure dest
+          das Ziel des Ausgangs mit Text/Closure
+
+BESCHREIBUNG:
+
+     Es wird ein Ausgang in die Richtung(en) cmd eingefuegt. Die Art des
+     Ausgangs haengt ab von dest:
+
+     - ein String:
+       - mit einem Dateinamen:
+         Der Ausgang fuehrt in den Raum, den der Dateiname bezeichnet.
+       - der Form "<msg>#dateiname"
+         Der Ausgang fuehrt in den Raum, den der Dateiname bezeichnet,
+         bei der Benutzung wird jedoch statt "<name> geht nach <richtung>"
+         "<name> geht nach <msg>" ausgegeben.
+     - eine Closure:
+       Die Closure wird bei Nutzung des Ausgangs aufgerufen. Das entspricht
+       eine SpecialExit - in der gerufenen Funktion muss man den Spieler
+       selbst in den Zielraum bewegen.
+       Gegebenenfalls kann das durch AddCmd() ersetzt werden.
+
+BEMERKUNGEN:
+     Man kann fuer den Dateinamen des Zielraumes auch einen relativen Pfad
+     angeben. Die Auswertung erfolgt nach folgendem Schema:
+     - "./<dateiname>"
+       Es wird ein Zielraum relativ zum gleichen Verzeichnis wie dieser
+       Raum angesprochen.
+     - "../<dateiname>"
+       Es wird ein Zielraum relativ zur Verzeichnisebene ueber der
+       dieses Raumes angesprochen (analog mit mehrerern "../..")
+
+     Mittels P_HIDE_EXITS kann man Ausgaenge verstecken.
+
+     Bei der Benutzung eines Ausgangs wird der Hook H_HOOK_EXIT_USE
+     ausgeloest.
+
+BEISPIELE:
+     ### normale Ausgaenge ###
+     // Beim Kommando "sueden" kommt: "<name> geht nach Sueden."
+     AddExit("sueden", "/gilden/abenteurer");
+
+     // Beim Kommando "sueden" kommt: "<name> geht in die Gilde."
+     AddExit("sueden", "in die Gilde#/gilden/abenteurer");
+
+     ### Ausgaenge mit relativen Pfaden ###
+     // Der Name des Raumes sei "/d/inseln/wargon/hafen1"
+     // Dieser Ausgang geht nach "/d/inseln/wargon/kneipe":
+     AddExit("norden", "./kneipe" );
+
+     // Und dieser nach "/d/inseln/anthea/anlege":
+     AddExit("sueden", "../anthea/anlege" );
+
+     ### dynamische Ausgaenge ###
+     // ein Ausgang soll nur von Froeschen benutzbar sein:
+
+     static int lochfkt(string dir);		// Prototyp
+     ...
+     AddExit("loch", #'lochfkt);
+     // auch identisch zu:
+     // AddSpecialExit("loch", #'lochfkt); [eine Closure] oder
+     // AddSpecialExit("loch", "lochfkt"); [ein Funktionsname]
+
+     static int lochfkt(string dir) {
+       if (!(this_player()->QueryProp(P_FROG))) {
+         // Kein Frosch => passt nicht!
+         notify_fail("Du bist zu gross!\n");
+         return 0;
+       }
+       // Meldungen werden im move() gleich mitgegeben
+       return this_player()->move("/room/loch", M_GO, 0,
+                    "huepft ins Loch", "huepft herein");
+     }
+
+SIEHE AUCH:
+     AddSpecialExit(), GetExits()
+     RemoveExit(), RemoveSpecialExit(),
+     H_HOOK_EXIT_USE, P_EXITS, P_HIDE_EXITS, /std/room/exits.c
+     ausgaenge
+
+31.01.2015, Zesstra
+
diff --git a/doc/lfun/AddExp b/doc/lfun/AddExp
new file mode 100644
index 0000000..8baed55
--- /dev/null
+++ b/doc/lfun/AddExp
@@ -0,0 +1,32 @@
+AddExp()
+FUNKTION:
+     int AddExp(int e)
+
+DEFINIERT IN:
+     /std/living/life.c
+
+ARGUMENTE:
+     int e - Anzahl der hinzuzufuegenden (abzuziehenden) XP
+
+BESCHREIBUNG:
+     Dem Living werden e XP auf seine bisherigen P_XP addiert.
+
+     Falls es sich um einen Spieler mit P_KILLS>0 handelt und
+     e positiv ist, bekommt der Spieler keine XP gutgeschrieben.
+
+     P_LAST_XP wird aktualisiert.
+
+BEMERKUNG:
+     - positive und negative Werte sind moeglich
+     - P_XP wird nicht <0 gesetzt.
+
+RUECKGABEWERT:
+     int - neuer XP-Wert
+
+SIEHE AUCH:
+     Funktionen:  do_damage(), DistributeExp()
+     Properties:  P_XP, P_LAST_XP
+     Sonstiges:   P_NO_XP, P_NO_SCORE
+                  create_default_npc()
+
+14.Feb 2007 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/AddExtraLook b/doc/lfun/AddExtraLook
new file mode 100644
index 0000000..526a851
--- /dev/null
+++ b/doc/lfun/AddExtraLook
@@ -0,0 +1,96 @@
+AddExtraLook()
+varargs int AddExtraLook(string look, [int duration, string key,
+                         string lookende, object ob]);
+DEFINIERT IN:
+   /std/living/description.c
+
+BESCHREIBUNG:
+   Der Extralook erscheint in der Langbeschreibung des Lebewesens.
+   Eintraege koennen mit dieser Funktion hinzugefuegt werden. Dies ist der
+   bevorzugte Weg, wenn ansonsten extra ein Objekt im Spielerinventar abgelegt
+   werden muesste.
+   
+   Alle Parameter bis auf <look> sind optional.
+
+ARGUMENTE:
+  - string look:
+    String, der in der Langbeschreibung des Lebewesens zusaetzlich ausgegeben
+    wird.
+    Kann auch ein Funktionsname sein, wenn <ob> angegeben wird (s.u.).
+  - int duration:
+    > 0: Wie lang bleibt der Extralook gueltig (in Sekunden)? Anschliessend 
+         wird er automatisch geloescht.
+    0:   Dieser Eintrag bleibt unbegrenzt gueltig.
+    < 0: Dieser Eintrag bleibt bis zum Ende/Reboot bestehen.
+  - string key:
+    Schluesselwort, unter dem der Eintrag registriert wird und mit dem man ihn
+    auch mittels RemoveExtraLook() entfernen kann. Sollte natuerlich
+    moeglichst eindeutig sein. ;-) Wenn <key> nicht angeben wird, wird der 
+    Objektname (object_name()) benutzt.
+  - string lookende:
+    String, der an das Lebewesen (nur bei Spielern) ausgegeben wird, wenn der
+    eingetragene Extralook abgelaufen ist.
+    Kann auch ein Funktionsname sein, wenn <ob> angegeben wird.
+  - object ob:
+    Wenn hier ein Objekt angegeben wird, werden <look> und <lookende> als
+    Funktonsnamen aufgefasst. Diese Funktionen werden in <ob> aufgerufen, wenn
+    der Extralook des Lebewesen angezeigt wird bzw. der eingetragene Extralook
+    abgelaufen ist. Diese Funktionen bekommen das jeweilige Lebenwesen als
+    Objekt uebergeben. Sie muessen einen String zurueckliefern, der ausgegeben
+    wird. Dieser String wird direkt so ausgeben, also selber fuer Zeilenumbruch
+    etc. sorgen!
+    WICHTIG: Das Objekt sollte nach Moeglichkeit eine Blueprint sein, da das
+    ganze nix mehr ausgibt, sobald der Clone zerstoert wird, falls hier 
+    einer angeben wird. Wenn ihr keine BP uebergebt: Wisst, was ihr tut. ;-)
+
+RUECKGABEWERTE:
+  > 0, falls der Eintrag erfolgreich registriert wurde.
+  < 0 sonst.
+    -1: <key> war nicht gueltig und es konnte keiner ermittelt werden.
+    -2: <look> war kein gueltiger String.
+    -3: <duration> war kein Integer.
+    -4: unter <key> gibt es schon einen Eintrag.
+
+BEMERKUNGEN:
+  Die Strings <look> und <lookende> werden vor Ausgabe durch
+  replace_personal() geschickt, daher ist die Verwendung von @WER1, @WESSEN1
+  usw. moeglich (s. replace_personal). Dies gilt aber _nicht_ fuer den Fall,
+  dass die entsprechenden Funktionen in <ob> gerufen werden, dann muessen die
+  Funktionen selber umbrechen, etc.
+  Nach replace_personal() werden die Strings noch von break_string() auf 78
+  Zeilen umgebrochen, allerdings bleiben dabei vorhandene Umbrueche erhalten.
+  Die Meldung von <lookende> bzw. der Funktionsaufruf erfolgt, wenn der
+  Extralook der Lebewesen das erste Mal nach Ablauf der Gueltigkeit aufgerufen
+  wird.
+
+BEISPIELE:
+  # einfacher Eintrag, "fuer die Ewigkeit"
+  living->AddExtraLook("@WER1 hat den Drachengott der SSP besiegt.");
+
+  # Eintrag der nach 1h automatisch weg ist.
+  living->AddExtraLook("@WER1 ist ganz mit Marmelade bedeckt.", 3600);
+  
+  # Eintrag mit bestimmten Schluessel, damit man ihn wieder entfernen kann.
+  living->AddExtraLook("@WER1 ist ganz mit Marmelade bedeckt.", 3600,
+                       "humni_marmeladen_look");
+  
+  # Mit "Ende"-Meldung, aber kein eigener Schluessel.
+  living->AddExtraLook("@WER1 ist patschnass.", 1200, 0,
+                       "Du bist endlich wieder trocken. Puuh.");
+  
+  # Mit Objekt, was den Extralook dynamisch erzeugt
+  living->AddExtraLook("get_my_special_extralook", 3600, 0, 0, this_object());
+    In diesem Fall muss this_object() natuerlich die Funktion
+    "get_my_special_extralook()" definieren, die einen String zurueckgibt.
+
+  # Mit Objekt, was den Extralook und die Endemeldung dynamisch erzeugt
+  living->AddExtraLook("get_my_special_extralook", 3600, 0,
+                       "extralookende", this_object());
+
+SIEHE AUCH:
+   RemoveExtraLook(),
+   replace_personal(), break_string()
+   P_INTERNAL_EXTRA_LOOK
+
+14.05.2007, Zesstra
+
diff --git a/doc/lfun/AddFixedObject b/doc/lfun/AddFixedObject
new file mode 100644
index 0000000..3e2e172
--- /dev/null
+++ b/doc/lfun/AddFixedObject
@@ -0,0 +1,52 @@
+AddFixedObject()
+
+FUNKTION:
+        varargs void AddFixedObject(string str, int val, mixed ids);
+
+DEFINIERT IN:
+        /std/room/shop.c
+
+ARGUMENTE:
+        str
+          Der absolute Filename eines Objekts, das in quasi beliebiger Menge
+          vom betreffenden Laden verkauft werden soll.
+        val
+          Sofern angegeben der angenommene Wert des Objekts. Falls val nicht
+          angegeben oder 0 ist, wird der Wert aus dem angegebenen Objekt
+          selbst ermittelt.
+          Der Verkaufspreis ist 3 * Wert des Objekts.
+        ids
+          String oder Stringarray mit der ID oder den IDs, ueber die man das
+          Objekt im Laden ansprechen kann. Falls nicht angegeben, wird die
+          ID-Liste aus der blueprint des Objekts ausgelesen.
+
+BESCHREIBUNG:
+        Mit dieser Funktion kann man einem Laden mitteilen, dass ein Objekt
+        in ihm in unbegrenzter Anzahl verkauft werden soll.
+        WICHTIG: Das zu verkaufende Objekt sollte dies insofern unterstuetzen, 
+        dass die Blueprint die notwendigen Informationen
+        (P_SHORT, P_IDS, P_VALUE, P_LONG, P_NAME) beinhaltet. Dies bedeutet im
+        einfachsten Fall, dass im create() auf
+          if (!clonep()) return;
+        verzichtet wird.
+
+RUeCKGABEWERT:
+        keiner
+        
+BEISPIELE:
+        AddFixedObject("/obj/fackel", 5000, "fackel");
+          Der Laden verkauft Fackeln zum Preis von 3*5000 Goldmuenzen und man
+          kann die Fackel (ausser ueber die Inventarnummer) nur mittels der
+          id "fackel" kaufen.
+          
+        AddFixedObject("/obj/fackel");
+          Der Laden verkauft Fackeln zum dreifachen Wert dessen, was im Objekt
+          /obj/fackel.c angegeben ist (derzeit sind das 5 Muenzen) und laesst
+          alle IDs zu, die in /obj/fackel.c angegeben sind. Derzeit ist das
+          auch nur "fackel".
+
+SIEHE AUCH:
+	RemoveFixedObject(), SetStorageRoom(), /std/store.c
+        
+----------------------------------------------------------------------------
+Letzte Aenderung: Sat Nov  9 12:59:25 2002 durch Bambi
diff --git a/doc/lfun/AddFood b/doc/lfun/AddFood
new file mode 100644
index 0000000..2da18e5
--- /dev/null
+++ b/doc/lfun/AddFood
@@ -0,0 +1,12 @@
+AddFood()
+
+BEMERKUNGEN:
+
+        Die Funktion AddFood() sollte NICHT MEHR BENUTZT werden.
+        Bitte AddToMenu() verwenden.
+
+SIEHE AUCH:
+        AddToMenu(), RemoveFromMenu()
+
+----------------------------------------------------------------------------
+Last modified: Fri Mar 03 13:23:00 2000 by Paracelsus
diff --git a/doc/lfun/AddFuel b/doc/lfun/AddFuel
new file mode 100644
index 0000000..bf00350
--- /dev/null
+++ b/doc/lfun/AddFuel
@@ -0,0 +1,29 @@
+AddFuel()
+
+FUNKTION:
+     void AddFuel(int fuel);
+
+DEFINIERT IN:
+     /std/lightsource.c
+
+ARGUMENTE:
+     fuel
+          Die zusaetzliche Brenndauer in Sekunden.
+
+BESCHREIBUNG:
+     Die Brenndauer der Lichtquelle wird um fuel Sekunden verlaengert.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Es werden keine Checks durchgefuehrt! Wenn man seine Lichtquelle
+     nachfuellbar gestalten will, sollte die Nachfuellfunktion vor dem
+     AddFuel()-Aufruf nachsehen, wie voll die Lichtquelle noch ist, und fuel
+     entsprechend begrenzen.
+
+SIEHE AUCH:
+     /std/lightsource.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:16:39 1996 by Wargon
diff --git a/doc/lfun/AddFun b/doc/lfun/AddFun
new file mode 100644
index 0000000..925b5ac
--- /dev/null
+++ b/doc/lfun/AddFun
@@ -0,0 +1,63 @@
+AddFun()
+
+FUNKTION:
+     void AddFun(string fun, int next);
+
+DEFINIERT IN:
+     /std/transport.c
+
+ARGUMENTE:
+     fun
+          Name der Funktion.
+     next
+          Zeit bis zur naechsten Fahrplanstation.
+
+BESCHREIBUNG:
+     Dem Fahrplan wird der Aufruf der Funktion fun, die im Transporter
+     definiert sein muss, hinzugefuegt. Nach Aufruf der Funktion vergehen
+     next Sekunden, bis die naechste Station angefahren wird.
+
+RUeCKGABEWERT:
+     keiner
+
+BEISPIELE:
+     Wenn ein zufaellig ausgewaehlter Passagier eines Schiffes unterwegs
+     seekrank werden soll, koennte man das wie folgt realisieren:
+
+     create()
+     {
+       ...
+
+       AddFun("seekrank", 5);
+       ...
+     }
+
+     seekrank()
+     {
+       object *passagiere, opfer;
+
+       // soll nicht immer passieren
+       if (random(5))
+         return;
+
+       // Opfer auswaehlen
+       passagiere = QueryPassengers();
+       if (sizeof(passagiere))
+         opfer = passagiere[random(sizeof(passagiere))];
+
+       // Und viel Spass...
+       tell_object(opfer,
+         "Du wirst seekrank! Schnell stuerzt Du zur Reling um  Dich zu\n"
+        +"uebergeben.\n");
+       tell_room(this_object(),
+         sprintf("%s ueberkommt die Seekrankheit!\n%s stuerzt an die Reling, "
+                +"um sich zu uebergeben.\n",
+                 capitalize(opfer->name(WEN)),
+                 capitalize(opfer->QueryPronoun(WER))), ({ opfer }) );
+     }
+
+SIEHE AUCH:
+     AddRoute(), AddMsg(), /std/transport.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:16:46 1996 by Wargon
diff --git a/doc/lfun/AddId b/doc/lfun/AddId
new file mode 100644
index 0000000..fa1ce76
--- /dev/null
+++ b/doc/lfun/AddId
@@ -0,0 +1,50 @@
+AddId()
+
+FUNKTION:
+     void AddId(string|string* ids);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     ids
+          String oder Array von Strings mit den Bezeichnungen, mit denen
+          sich sich das Objekt ansprechen lassen soll.
+
+BESCHREIBUNG:
+     Jedes Objekt sollte sich auf die eine oder andere Weise ansprechen
+     lassen. Zu diesem Zweck kann man dem Objekt mit dieser Funktion
+     Bezeichner uebergeben.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Jedes Objekt sollte man zumindest mit seiner Kurzbeschreibung
+     ansprechen koennen! Fuer Abfragen von Questobjeken o.ae. sollte man
+     zusaetzlich IDs verwenden, die Sonderzeichen wie "\n" oder "\t"
+     enthalten, damit sichergestellt ist, dass der Spieler auch wirklich die
+     richtigen Objekte dabeihat.
+
+BEISPIELE:
+
+     AddId( "buch" );
+     AddId( "buechlein" );
+
+     Das Objekt laesst sich jetzt als "buch" und als "buechlein" ansprechen.
+
+     AddId( ({ "buch", "buechlein" }) );
+
+     Diese Zeile bewirkt das gleiche wie die obigen zwei Zeilen.
+
+     AddId( ({ "puzzle", "\nquest_puzzle" }) );
+
+     Der Spieler kann das Objekt als "puzzle" ansprechen, questrelevante
+     Objekte koennen mit der ID "\nquest_puzzle" nach ihm suchen.
+
+SIEHE AUCH:
+     AddAdjective(), RemoveId(), id(), present(), /std/thing/description.c
+
+     -----------------------------------------------------------------------
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/AddInfo b/doc/lfun/AddInfo
new file mode 100644
index 0000000..9de7bd2
--- /dev/null
+++ b/doc/lfun/AddInfo
@@ -0,0 +1,219 @@
+AddInfo()
+FUNKTION:
+     varargs void AddInfo( frage, meldung
+			   [, indent [, [silent [, casebased] ] ] );
+
+DEFINIERT IN:
+     /std/npc/info.c
+
+ARGUMENTE:
+     string/string* frage
+	Schluesseltext(e) auf die Informationen gegeben werden sollen.
+     string/closure meldung
+	Information, die gegeben werden soll/Closure
+     string indent
+	Text, der sich bei mehrzeiligen Meldungen wiederholen soll.
+     int/string silent
+	Ist silent gesetzt, so erfolgt Antwort nur an Fragenden.
+     string/closure casebased
+	Closure mit Returnwert string oder int.
+
+BESCHREIBUNG:
+     Wenn ein Spieler ein NPC mittels "frage <monstername> nach <frage>" nach
+     einer Information mit dem Schluessel 'frage' fragt, so wird die
+     entsprechende 'meldung' ausgegeben (oder die Closure in 'meldung'
+     gerufen und der zurueckgegebene Text ausgegeben). Der Meldung wird
+     der Name des Monsters vorangestellt.
+
+     Frage:
+      Schluessel muessen kleingeschrieben sein, koennen aber Leerzeichen
+      enthalten.
+
+     Meldung:
+      Wenn kein 'indent' angegeben ist, muss man die Meldung selbst
+      umbrechen.
+
+     Indent:
+      Wird ein 'indent' angegeben so wird jeder Zeile hinter dem
+      Monsternamen noch das 'indent' vorangesetzt. Zusaetzlich wird
+      'meldung' auf jeden Fall sauber umgebrochen.
+      Ein typisches indent ist "sagt: ".
+
+     Silent:
+      Bei 'silent'==1 erfolgt keine Textausgabe der Antwortmeldung im Raum,
+      ist 'silent' ein String, so wird jener an alle anderen Spieler ausser
+      dem Fragesteller im Raum ausgegeben.
+
+     Casebased:
+      Die als Closure angegebene Methode entscheidet, ob oder wie der NPC 
+      auf diese Frage antworten soll:
+      - return 0:	normale Antwort mit "meldung"
+      - return 1:	keine Antwort/Antwort mit DEFAULT_NOINFO
+      - return string:	Antwort mit string unter Beruecksichtigung eines
+			indent
+
+     Die Strings von 'silent' und 'meldung' werden geparsed. Dabei werden die
+     Schluesselworte @WER, @WESSEN, @WEM, @WEN durch TP->Name(..) ersetzt und
+     @CAP_WER, @CAP_WESSEN, @CAP_WEM, @CAP_WEN durch capitalize(TP-Name(..)).
+
+     Mittels der in <npc.h> definierten Frage DEFAULT_INFO kann eine
+     Meldung gesetzt werden, die gegeben werden soll, wenn der Spieler
+     etwas fragt, auf das keine Antwort vorgegeben ist (das loest
+     SetProp(P_DEFAULT_INFO, <text>) ab).
+
+BEISPIELE:
+     ### eine Standardantwort setzen ###
+     AddInfo(DEFAULT_INFO, "starrt Dir boese in die Augen.\n");
+     // identisch zu
+     SetProp(P_DEFAULT_INFO, "starrt Dir boese in die Augen.\n");
+
+     ### einfache Beispiele, auch mit casebased ###
+     AddInfo(({"knete","kohle"}),
+	     "sagt: ich habe so etwas nicht.\n");
+     AddInfo("geld",
+	     "Ich habe zwar kein Geld, aber ... blablabla ...",
+	     "sagt: " );
+     AddInfo("muenzen",
+	     "fluestert: Du willst Geld?\n",
+	     0,
+	     "fluestert @WEM etwas zu.\n");
+
+     // "frage monster nach geld": alle im Raum hoeren
+     //  Das Monster sagt: Ich habe zwar kein Geld, aber ...
+     //  Das Monster sagt: ... blablabla ...
+
+     // "frage monster nach muenzen":
+     // - der Fragensteller hoert:
+     //   "Das Monster fluestert: Du willst Geld?"
+     // - alle andere hoeren:
+     //   "Das Monster fluestert <Fragenstellernamen> etwas zu."
+
+     ### dynamisch ###
+     // ein Prototyp, damit wir die Methode bekannt machen
+     static string query_kekse();
+     ...
+     AddInfo(({"keks","kekse"}),
+	     #'query_kekse,		// ein Verweis auf die Funktion
+	     "sagt: ");
+     ...
+     static string query_kekse() {
+      if(present("keks"))
+       return("Ich hab noch welche. Aetsch!");
+      return("Menno. Keine mehr da!");
+     }
+
+     // "frage monster nach keks":
+     // - wenn es noch Kekse hat, hoeren alle:
+     //   "Das Monster sagt: Ich hab noch welche. Aetsch!
+     // - sonst:
+     //   "Das Monster sagt: "Menno. Keine mehr da!
+
+     ### dynamischer ###
+     // ein Prototyp, damit wir die Methode bekannt machen
+     static string query_kekse();
+     static mixed case_fighting();
+     ...
+     AddInfo(({"keks","kekse"}),
+	     #'query_kekse,"		// ein Verweis auf die Funktion
+	     sagt: ",
+	     0,				// nicht silent :)
+	     #'case_fighting);		// noch ein Funktionsverweis
+     ...
+     static string query_kekse() {
+      if(present("keks"))
+       return("Ich hab noch welche. Aetsch!");
+      return("Menno. Keine mehr da!");
+     }
+
+     static mixed case_fighting() {
+      if(InFight())
+       return("Keine Zeit fuer Kekse. Muss kaempfen.");
+      return 0;
+     }
+
+     // "frage monster nach keks":
+     // - wenn es kaempft, hoeren alle:
+     //   "Das Monster sagt: Keine Zeit fuer Kekse. Muss kaempfen.
+     // - sonst, wenn es noch Kekse hat, hoeren alle:
+     //   "Das Monster sagt: Ich hab noch welche. Aetsch!
+     // - sonst:
+     //   "Das Monster sagt: "Menno. Keine mehr da!
+
+
+     ### dynamisch und komplex ###
+     // ein Prototyp, damit wir die Methode bekannt machen
+     static string question_gold();
+     ...
+
+     // "gold" wird eine Closure auf die Methode question_gold()
+     // zugewiesen, ausserdem soll es still bleiben (wir informieren
+     // den Restraum selbst)
+     AddInfo("gold",#'question_gold,"murmelt: ",1);
+     ...
+
+     // los gehts, wir generieren unsere Antwort selbst
+     static string question_gold() {
+      int money;
+      string *y, objstr;
+      object o;
+      // wieviel Kohle hat der Spieler
+      money=this_player()->QueryMoney();
+      y=allocate(0);
+      // und jetzt suchen wir die Dinge aus Gold
+      o=first_inventory(this_player());
+      while(o) {
+       if(o->QueryMaterial(MAT_GOLD)>0 &&
+          strstr(object_name(o),"/obj/money"))
+        y+=({o->name(WER,1)});
+       o=next_inventory(o);
+      }
+
+      // das geht an alle anderen im Raum, silent bietet sich hier
+      // nicht an, weil es mehrere Moeglichkeiten gibt
+      say(break_string(
+       Name(WER,1)+" murmelt "+
+       this_player()->name(WEM,1)+
+       " etwas zu"+
+       ((money || sizeof(y))?
+        " und glotzt "+
+        this_player()->QueryPronoun(WEN)+" gierig an.":
+        "."),78),({this_player()}));
+
+      // und hier die Antwort an den Spieler selbst, mit vielen
+      // Verzweigungen fuer dessen Besitztum
+      return("Ich hab kein Gold bei mir."+
+          ((money || sizeof(y))?
+           " Aber du "+
+           (money?"hast ja jede Menge Kohle bei dir, so etwa "+money+
+            " Muenzen."+
+            (sizeof(y)?
+             " Ausserdem "+
+             ((sizeof(y)==1)?"ist":"sind")+
+             " auch noch "+CountUp(y)+" aus Gold.":
+             ""):
+            (sizeof(y)?" Aber was du so bei dir hast: "+
+             CountUp(y)+
+             (sizeof(y)==1?" ist":" sind")+
+             " aus Gold.":"")):
+           ""));
+     }
+
+     // "frage monster nach gold"
+     // - der Fragesteller hoert zB:
+     //   Das Monster murmelt: Ich hab kein Gold bei mir. Aber du hast ja
+     //   Das Monster murmelt: jede Menge Kohle bei dir, so etwas <number>
+     //   Das Monster murmelt: Muenzen. Ausserdem ist/sind noch <object1>
+     //   Das Monster murmelt: und <object2> aus Gold."
+     // - die Umstehenden hoeren:
+     //   "Das Monster murmelt @WEM etwas zu."
+     //   oder
+     //   "Das Monster murmelt @WEM etwas zu und glotzt ihn/sie gierig an."
+
+SIEHE AUCH:
+     Verwandt:  AddSpecialInfo(L), RemoveInfo(L)
+     Props:     P_PRE_INFO, P_DEFAULT_INFO
+     Files:     /std/npc/info.c
+     Loggen:    P_LOG_INFO
+     Interna:   GetInfoArr, do_frage
+
+7.Apr 2004 Gloinson
diff --git a/doc/lfun/AddItem b/doc/lfun/AddItem
new file mode 100644
index 0000000..8e812ea
--- /dev/null
+++ b/doc/lfun/AddItem
@@ -0,0 +1,159 @@
+AddItem()
+
+FUNKTION:
+	varargs object AddItem(mixed filename,int refresh,mixed props);
+
+DEFINIERT IN:
+	/std/room/items.c
+	/std/npc/items.c
+
+ARGUMENTE:
+	filename
+	  String mit dem Namen des zu erzeugenden Objektes oder Array von
+	  Strings mit den Namen der zu erzeugenden Objekte. Bei einem Array
+	  wird ein Name zufaellig ausgewaehlt.
+	refresh
+	  Wann und wie soll das Objekt erneuert werden:
+	  - REFRESH_NONE      - Kein Refresh bis zum Neuladen des Raums
+                        - oder NPCs.
+	  - REFRESH_DESTRUCT  - Refresh bei Reset, wenn Item zerstoert
+	                        wurde.
+	  - REFRESH_REMOVE    - Refresh bei Reset, wenn Item entfernt wurde.
+	  - REFRESH_ALWAYS    - Neuer Clone bei jedem Reset.
+	  - REFRESH_MOVE_HOME - Objekt wird bei Reset automatisch
+	                        zurueckgeholt, wenn es wegbewegt wurde.
+	                        (nur in Raeumen!)
+	  Bei NPC's gilt zusaetzlich:
+	  - CLONE_WEAR       - Item Anziehen, wenn es eine Ruestung ist.
+	  - CLONE_WIELD      - Item zuecken, wenn es eine Waffe ist.
+	  - CLONE_NO_CHECK   - Zuecken oder Anziehen ohne Ueberpruefungen.
+	props (optional)
+	  Mapping mit denen in einem geclonten Objekt zu setzenden
+	  Properties oder 1 fuer die Erzeugung einer Blueprint.
+
+RUeCKGABEWERT:
+	Innerhalb von Raeumen wird das erzeugte Objekt zurueckgeliefert. Bei
+	NPC's klappt dies leider nicht, da dort die Objekte nicht sofort
+	erzeugt werden, sondern erst, nachdem der NPC an seinen
+	Bestimmungsort transferiert wurde. Daher wird bei NPC immer 0 
+	zurueckgegeben.
+
+BESCHREIBUNG:
+	Abhaengig von <filename> und <props> wird ein Objekt erzeugt und in
+	den Raum bzw. NPC bewegt. Dabei gibt es folgende Moeglichkeiten:
+	- <filename> ist ein Dateiname.
+	  Es wird ein Clone dieser Datei erstellt oder (wenn <props>=1 ist)
+	  deren Blueprint verwendet.
+	- <filename> ist ein Array von Dateinamen.
+	  Es wird eine Datei zufaellig aus dem Array ausgewaehlt und von
+	  dieser Datei ein Clone erstellt oder (wenn <props>=1 ist) deren
+	  Blueprint verwendet.
+	Uebergibt man fuer <props> ein Mapping mit dem Aufbau
+	  ([prop_name:prop_wert,...]),
+	so werden diese Properties im erzeugten Objekt gesetzt.
+	Der Parameter <refresh> gibt an, was waehrend eines Resets im Raum
+	bzw. in NPC's oder was beim Erzeugen von NPC's geschehen soll:
+	In <rooms.h> sind dazu folgende Moeglichkeiten definiert:
+	- REFRESH_NONE
+            Das Objekt wird niemals erneuert; falls es zerstoert wurde, wird
+	    es erst dann wieder erzeugt, wenn der Raum erneut geladen bzw.
+	    der NPC neu erzeugt wird. Man beachte, dass nicht jeder NPC
+	    wirklich refreshende Objekte benoetigt, REFRESH_NONE spart
+	    hierbei sowohl Rechenzeit als auch Speicher!
+	- REFRESH_DESTRUCT
+	    Das Objekt wird nur dann erneuert, wenn es in der Zwischenzeit
+	    zerstoert wurde (bei NPC's ist das zum Beispiel der Fall, wenn
+	    sie getoetet wurden).
+	    REFRESH_NONE & REFRESH_DESTRUCT + Blueprint-Objekt bedeutet bei
+	    NPC's ein Unique-Objekt, es wird also nicht beim Neuerzeugen des
+	    NPC's zurueckgesetzt.
+	- REFRESH_REMOVE
+	    Das Objekt wird erneuert, wenn es sich nicht mehr im Raum bzw.
+	    im NPC befindet. Das kein sein, weil es zerstoert wurde, aber
+	    auch zum Beispiel in folgenden Faellen:
+	    * weil es jemand mitgenommen hat
+	       (in Raeumen bei Gegenstaenden)
+	    * weil es fortgegangen ist
+	       (in Raeumen bei NPC's, die herumlaufen)
+	    * weil es weggeworfen wurde
+	       (in NPC's bei Gegenstaenden)
+	- REFRESH_ALWAYS
+	    Das Objekt wird immer erneuert. Von dieser Refreshmethode sollte
+	    man allerdings Abstand nehmen, da sich sonst mit der Zeit
+	    gewaltige Mengen von Objekten ansammeln koennen!
+	- REFRESH_MOVE_HOME
+	    Das Objekt wird in einen Raum zurueckbewegt, sofern es noch
+	    existiert, jedoch nicht mehr in dem Raum ist. Sinnvoll ist dies
+	    eigentlich nur fuer Lebewesen, funktioniert aber auch bei
+	    beliebigen Objekten. Hauptsaechlich geht es hierbei darum,
+	    herumlaufende NPCs oder bei erzwungenen Bewegungen nicht von
+	    P_GUARD zurueckgehaltene NPCs wieder an einen definierten
+	    Ausgangsort zurueckzubringen.
+	Hat man in Raeumen als <filename> ein Array von Dateinamen
+	uebergeben, so wird beim Reset jedesmal aufs Neue ein zufaelliges
+	Objekt aus der Liste ausgewaehlt (nicht in NPC's).
+	In NPC's gilt der Grundsatz der Vermeidung von ueberfluessigen
+	Objekten im MUD. Neu erzeugt werden Objekte beim Erzeugen eines
+	NPC's oder bei einem Reset im selbigen. Anstatt die Objekte gleich
+	neu zu erschaffen, wird erst geschaut, ob sich identische Objekte
+	schon im Raum befinden. Ist dies der Fall, so nimmt der NPC sie auf,
+	ruft jedoch vorher nochmals create() in ihnen auf!
+	  (noetig wegen moeglicher Veraenderungen an den Objekten)
+	Was dann passiert, haengt von weiteren Angaben in <refresh> ab.
+	Folgende weitere Moeglichkeiten sind in <npc.h> definiert:
+        - CLONE_WEAR
+	  Ist das hinzugefuegte Item eine Ruestung, so wird sie nach
+	  Aufnahme oder Neuerzeugung angezogen.
+        - CLONE_WIELD
+	  Ist das hinzugefuegte Item eine Waffe, so wird sie nach Aufnahme
+	  oder Neuerzeugung gezueckt.
+        - CLONE_NO_CHECK
+	  Hiermit verhindert man eine Ueberpruefung, ob eine Ruestung
+	  angezogen oder eine Waffe gezueckt werden kann. Es ist jedoch
+	  Vorsicht geboten: So kann es ohne weiteres passieren, dass ein NPC
+	  mehrere Ruestungen gleichen Typs angezogen oder mehrere Waffen
+	  gezueckt hat.
+	Benutzt man Blueprints (<props>=1) mit REFRESH_REMOVE oder
+	REFRESH_ALWAYS, so kann es zu ungewollten Ueberraschungen kommen, da
+	die Blueprint dann unabhaengig von ihrem momentanen Aufenthaltsort
+	wieder in den Raum bzw. NPC bewegt wird, von dem sie erzeugt wurde!
+
+BEMERKUNGEN:
+	Wenn man Blueprints benutzt, sollte man daran denken, dass sich von
+	dieser dann keine Clones mehr erstellen lassen!
+	RemoveItem() zum Entfernen von Items ist nur fuer Raeume definiert!
+
+	Die Option CLONE_NEW ist veraltet. Die Objekte werden nun immer
+	neu erzeugt. Die Option darf noch angegeben werden, hat aber keine
+	Bedeutung mehr.
+
+BEISPIELE:
+	// Ein Wuerfel, der sich nach Entfernen erneuert:
+	  AddItem("/obj/misc/wuerfel",REFRESH_REMOVE);
+	// Ein etwas veraenderter Wuerfel:
+	  AddItem("/obj/misc/wuerfel",
+	          REFRESH_REMOVE,
+	          ([P_SHORT :"Ein schwerer Wuerfel",
+	            P_WEIGHT:100]));
+	// Eine Blueprint, die nur einmal im MUD existiert. Wenn sie
+	// zerstoert wurde, wird sie bei Reset neu erzeugt:
+	  AddItem("/mon/angsthase",REFRESH_DESTRUCT,1);
+	// Eine Blueprint, die nur einmal im MUD existiert. Wenn sie aus dem
+	// Raum entfernt wurde, wird sie bei Reset zurueckgeholt:
+	  AddItem("/mon/angsthase",REFRESH_MOVE_HOME,1);
+	// Ein zufaelliges Objekt:
+	  AddItem(({"/obj/misc/lolli",
+	            "/obj/misc/bonbon",
+	            "/obj/misc/bier"}),REFRESH_REMOVE);
+	// Eine Ruestung, die auch angezogen wird (nur in NPC's):
+	  AddItem("/ruestung/sommerkleid",REFRESH_REMOVE|CLONE_WEAR);
+	// Eine Unique-Waffe, die auch gezueckt wird (nur in NPC's):
+	  AddItem("/waffe/zapper",REFRESH_DESTRUCT|CLONE_WIELD,1);
+
+SIEHE AUCH:
+	RemoveItem(), replace_program(), create(), P_GUARD,
+	/std/room/items.c, /std/npc/items.c,
+	/sys/rooms.h, /sys/npc.h
+
+----------------------------------------------------------------------------
+Last modified: Thu Nov 23 13:43:30 CET 2006 by Rumata
diff --git a/doc/lfun/AddKnownPotion b/doc/lfun/AddKnownPotion
new file mode 100644
index 0000000..4d4d296
--- /dev/null
+++ b/doc/lfun/AddKnownPotion
@@ -0,0 +1,25 @@
+AddKnownPotion()
+
+FUNKTION:
+     int AddKnownPotion(int nr)
+
+DEFINIERT IN:
+     /std/player/potion.c
+
+ARGUMENTE:
+     int nr       Nummer eines ZTs
+
+BESCHREIBUNG:
+     Addiert einen ZT als bekannt in einem Spieler. Nur vom Orakel rufbar.
+
+RUeCKGABEWERT:
+     1  Erfolg
+     -1 fehlende Berechtigung
+     -2 Nummer bereits eingetragen
+
+SIEHE AUCH:
+     Sonstiges: zaubertraenke, /secure/potionmaster.c, /room/orakel.c
+     Verwandt:  FindPotion(), RemoveKnownPotion(), InList()
+     Props:     P_POTIONROOMS, P_KNOWN_POTIONROOMS
+
+6.Feb 2016 Gloinson
diff --git a/doc/lfun/AddMaterial b/doc/lfun/AddMaterial
new file mode 100644
index 0000000..d4f50b7
--- /dev/null
+++ b/doc/lfun/AddMaterial
@@ -0,0 +1,76 @@
+AddMaterial()
+FUNKTION:
+     private static varargs void AddMaterial(string mat, int gender,
+                                             mixed names, mixed groups,
+                                             mixed dif) {
+
+DEFINIERT IN:
+     /p/daemon/materialdb.c (MATERIALDB)
+
+ARGUMENTE:
+     string mat
+       Materialstring, definiert in <thing/material.h>
+
+     int gender
+       Geschlecht des einzutragenden Materials
+
+     mixed names
+       Name des Materials:
+       - "<Nominativ>" oder (meist nur Nom. und Gen. noetig)
+       - ({"<Nominativ>","<Genitiv>","<Dativ>","<Akkusativ>"})
+
+     mixed groups
+       Eingruppierung des Materials:
+       - MATGROUP_XXX oder ({MATGROUP_XXX,...})
+       - ([MAT_GROUP_XXX:xx,MATGROUP_YYY:yy,...])
+
+     mixed dif
+       Schwierigkeiten bei der Erkennbarkeit:
+       - int x oder ({MINMAT,x1,MATPOS1,x2,MATPOS2 ...})
+       - xn: Erkennbarkeitsschwierigkeit (100=100%) -100..100
+       - MINMAT: Erkennung zumindest als _dieses_ Material
+                 moeglich
+       - MATPOSn: moegliches Material, erkennbar, wenn
+                  Erkennbarkeitfaehigkeit>=xn
+                  -> das letzte MATPOS muss natuerlich
+                     string mat entsprechen
+
+BESCHREIBUNG:
+     Es wird in die Materialiendatenbank eine neues Material aufgenommen,
+     die Stringkonstante dafuer wird vorher in <thing/material.h> fest-
+     gelegt. Falls der Genitiv nicht Nominativ+"s" entspricht (z.B. "Seide"),
+     sollte dieser explizit angegeben werden.
+     Nach Neuladen der Datenbank ist dieses Material auch per MaterialName(),
+     'erkennbar' (siehe mixed dif, siehe Beispiel) bzw. seinen einzelnen
+     Gruppen zuordnbar.
+
+BEISPIELE:
+     AddMaterial(MAT_NITROGLYCERINE,NEUTER,"Nitroglycerin",
+                 ({MATGROUP_EXPLOSIVE,MATGROUP_FLUID}),
+                 ({MAT_OIL,25,MAT_MISC_EXPLOSIVE,50,MAT_NITROGLYCERINE}));
+
+     Damit wird das Material Nytroglycerin aufgenommen, ein explosiver
+     (damit entflammbarer) sowie fluessiger Stoff. Liegt die Erkennungs-
+     faehigkeit (MaterialName()) unter 25, wird es nur als Oel erkannt,
+     liegt sie unter 50, wird es zumindest als explosives Material erkannt,
+     liegt sie ueber 49, so wird es korrekt erkannt (wie schade :) ).
+
+BEMERKUNGEN:
+     Wird in der create() der Datenbank aufgerufen. Zu beachten:
+     - vor Eintrag eines _neuen_ Materials die Datenbank durchsuchen!
+     - bei den Materialiengruppen die automatischen Abhaengigkeiten in
+       AddMaterial() durchsehen!
+     - bitte Datenbank neu laden
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Methoden:    QueryMaterial(), QueryMaterialGroup(), MaterialList(),
+     Listen:	  AllMaterials(), AllGroups(), Dump()
+		  materialliste, materialgruppen
+     Master:	  ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+     Sonstiges:	  P_MATERIAL_KNOWLEDGE
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/AddMiniQuest b/doc/lfun/AddMiniQuest
new file mode 100644
index 0000000..91e7857
--- /dev/null
+++ b/doc/lfun/AddMiniQuest
@@ -0,0 +1,54 @@
+AddMiniQuest()
+
+FUNKTION:
+    int AddMiniQuest(int stupse, string questgeber, string desc, int active,
+                     string titel, string erledigt, mapping voraussetzungen,
+                     string region, string *erlaubte)
+
+DEFINIERT IN:
+    /secure/questmaster
+
+BESCHREIBUNG:
+    Diese Funktion traegt eine neue Miniquest im Questmaster ein.
+
+ARGUMENTE:
+
+    stupse (>0)  - Anzahl Stufenpunkte, die fuer die MQ gutgeschrieben werden
+    questgeber   - Ladename des Objekts, das GiveMiniQuest() aufruft
+    desc         - Aufgabenbeschreibung der Miniquest
+    active (0/1) - ist die Miniquest aktiv, d.h. spielbar, oder nicht?
+    titel        - Titel der Miniquest, darf weder "in", noch "im" enthalten,
+                   weil dann der Eintrag in der Fraternitas-Bibliothek nicht
+                   gelesen werden kann.
+    erledigt     - Beschreibung der Miniquest, nachdem man sie erledigt hat
+                   Der Text kann in der Bibliothek der kleinen und grossen
+                   Heldentaten in der Fraternitas eingesehen werden.
+    voraussetzungen - Mapping im Format von P_RESTRICTIONS (s. dort), um
+                   die Voraussetzungen festzulegen, die ein Spieler 
+                   erfuellen muss, um die MQ ueberhaupt spielen zu koennen
+                   Wird fuer die regionsbezogenen Informationspunkte/-NPCs
+                   ausgewertet. 0 oder ([]) eintragen, wenn keine 
+                   Voraussetzungen bestehen.
+    region       - Zuordnung der Miniquest zu einer Region; wird fuer der
+                   Bibliothek der Fraternitas verwendet, um die MQs der
+                   einzelnen Regionen herauszufiltern.
+    erlaubte     - Array mit Ladenamen von Objekten, die berechtigt sind,
+                   die Daten der MQ abzufragen, um Spielern einen Hinweis
+                   darauf zu geben, die sie noch nicht bestanden haben.
+
+RUECKGABEWERTE:
+     1: Hat geklappt
+    -1: Parameterformat stimmt nicht (questgeber kein String oder Leerstring,
+        voraussetzungen kein Mapping, region oder titel keine Strings, 
+        erlaubte kein Array)
+    -2: weniger als 1 Stufenpunkt einzutragen versucht
+    -3: Das Array in "erlaubte" ist leer, oder zum angegebenen Questgeber
+        wurde keine Datei gefunden.
+    -4: Der angegebene Questgeber vergibt schon eine andere Miniquest
+
+
+SIEHE AUCH:
+    GiveMiniQuest(L), HasMiniQuest(L)
+    P_RESTRICTIONS
+    /secure/questmaster.c
+
diff --git a/doc/lfun/AddMoney b/doc/lfun/AddMoney
new file mode 100644
index 0000000..229d1cc
--- /dev/null
+++ b/doc/lfun/AddMoney
@@ -0,0 +1,62 @@
+AddMoney(L)
+FUNKTION:
+     public int AddMoney(int amount);
+
+DEFINIERT IN:
+     /std/container/moneyhandler.c
+     /std/living/moneyhandler.c
+     /std/player/moneyhandler.c
+
+ARGUMENTE:
+     int amount
+         Die zufuehrende oder abziehende Geldmenge
+
+BESCHREIBUNG:
+     Dem Spieler wird die in <amount> festgelegte Geldmenge abgezogen oder
+     zugefuehrt.
+
+RUeCKGABEWERT:
+     Technisch gesehen wird Geld mit entsprechendem <amount> erzeugt
+     ("/items/money.c") und mittels "move" in den Spieler bewegt.  Das Ergebnis
+     dieses "move"-Aufrufes wird hier uebergeben, z.B. 1 fuer OK.
+     Die moeglichen Fehler-Konstanten sind in /sys/moving.h definiert, siehe
+     auch Dokumentation zu "move".
+
+BEMERKUNGEN:
+     <amount> kann sowohl positiv als auch negativ sein. Welche Auswirkungen
+     beide Faelle haben, sollte klar sein. Doch sollte bei einem negativen
+     <amount> vorher mittels QueryMoney() abgefragt werden, ob der Spieler
+     auch ueber ausreichend Geld verfuegt.
+     Wird dem Spieler Geld abgezogen, ist darauf zu achten, dieses in der
+     Zentralbank einzuzahlen (s.a.:PayIn() ). 
+     Verschafft man dem Spieler Geld aus dem Nichts, muss es vorher bei der
+     Zentralbank abgebucht (WithDraw()) werden.
+
+     Achtung: Kann der Spieler die in <amount> angebene Geldmenge nicht
+	      tragen, werden ihm keine Muenzen in sein Inventar bewegt.  Die
+	      Fehlermeldung erkennt man an dem Rueckgabewert ME_TOO_HEAVY.
+
+     Im Gegensatz zu Spielern haben alle anderen Objekte (Raeume, NPC, etc.)
+     standardmaessig keinen Moneyhandler. In diesem Fall muss in Lebewesen
+     "/std/living/moneyhandler"
+     und in nicht-Lebewesen
+     "/std/container/moneyhandler"
+     geerbt werden.
+
+BEISPIELE:
+     // gib ihm Geld
+     this_player()->AddMoney(50);
+
+     // nimm ihm Geld
+     if(this_player()->AddMoney(-50)==1)
+      write("Der Ork beklaut dich!\n");
+
+SIEHE AUCH:
+     Geldhandling:	QueryMoney(L)
+     Zentralbank:	PayIn(L), WithDraw(L)
+     Sonstiges:		move(L),
+			/items/money.c, /sys/moving.h, /sys/money.h, /sys/bank.h
+			/std/container/moneyhandler.c
+
+18.02.2013, Zesstra
+
diff --git a/doc/lfun/AddMsg b/doc/lfun/AddMsg
new file mode 100644
index 0000000..974c905
--- /dev/null
+++ b/doc/lfun/AddMsg
@@ -0,0 +1,42 @@
+AddMsg()
+
+FUNKTION:
+     void AddMsg(string msg, int next);
+
+DEFINIERT IN:
+     /std/transport.c
+
+ARGUMENTE:
+     msg
+          Die auszugebende Meldung.
+     next
+          Zeit bis zur naechsten Fahrplanstation.
+
+BESCHREIBUNG:
+     Dem Fahrplan wird die Ausgabe einer Meldung an den Transporter
+     hinzugefuegt. Diese Meldung koennte zum Beispiel das Nahen der
+     naechsten Haltestelle ankuendigen o.ae. Nach Ausgabe der Meldung
+     vergehen next Sekunden, bis die naechste Station angefahren wird.
+
+     Um das Umbrechen der Meldung (normalerweise auf 78 Zeichen pro Zeile)
+     muss sich der Aufrufer selber kuemmern.
+
+RUeCKGABEWERT:
+     keiner
+
+BEISPIELE:
+
+     AddMsg("In der Ferne taucht eine kleine Inseln auf.\n", 10);
+     AddMsg("Das Schiff steuert einen kleinen Steg an.\n");
+     AddRoute(...);
+
+     Nach der Ankuendigung der Insel vergehen 10 Sekunden, bis die naechste
+     Meldung ausgegeben wird. Da bei der zweiten Meldung keine Zeit
+     angegeben war, legt das Schiff direkt nach der Ausgabe der Meldung an.
+
+SIEHE AUCH:
+     AddRoute(), AddFun(), /std/transport.c
+
+----------------------------------------------------------------------------
+25.01.2015, Zesstra
+
diff --git a/doc/lfun/AddPlant b/doc/lfun/AddPlant
new file mode 100644
index 0000000..a79bcff
--- /dev/null
+++ b/doc/lfun/AddPlant
@@ -0,0 +1,58 @@
+AddPlant()
+
+FUNKTION:
+        varargs int AddPlant(string filename, [string|string* npcId]) 
+
+DEFINIERT IN:
+        /std/room/kraeuter.c
+
+ARGUMENTE:
+        filename
+          Der Filename des Krauts das hier gefunden werden soll.
+        npcId
+          Die ID eines NPCs oder die IDs einer Liste von NPCs, der/die das 
+          Kraut bewachen soll/en. Befindet sich ein NPC mit einer dieser IDs
+          im Raum, kann das Kraut nicht gepflueckt werden. Dieses Argument 
+          ist optional!
+
+RUeCKGABEWERT:
+        -1 wenn das Objekt nicht geclont werden konnte
+        >=0 sonst
+
+BESCHREIBUNG:
+        Mit Hilfe dieser Funktion koennen Kraeuter fuer den mudweiten
+        Kraeuterskill recht einfach eingebaut werden. Alles was man
+        noch machen muss, ist den Namen der Pflanze in einem Detail oder
+        der Langbeschreibung zu erwaehnen.
+        Mit dem Befehl "showplant" in /obj/tools/planttool kann man sich 
+        bequem anzeigen lassen, was es alles an Kraeutern gibt, die man 
+        nehmen kann.
+
+BEMERKUNGEN:
+        Damit die Kraeuter von den Spielern zum Brauen von Traenken benutzt
+        werden koennen, muss der Raum erst in einem Master eingetragen werden.
+        Derzeit schickt ihr dazu am besten eine kurze Mail an einen Erzmagier,
+        gerne nimmt Humni die derzeit entgegen.
+        Die Kraeuter wurden von der Balance bereits alle im vorhinein
+        abgenommen. Lediglich die Einhaltung der Kategorien ist zu beachten.
+        Sind Kraeuter nicht im Master konfiguriert (wie z.B. im Homemud), sind
+        alle erzeugten Kraeuter nur "Testkraeuter" mit nur der ID "kraut".
+
+BEISPIELE:
+        #include <items/kraeuter/kraeuterliste.h>
+        inherit "/std/room/kraeuter";
+        inherit "/std/room";
+        
+        void create()
+        {
+          ::create();
+          SetProp(P_INT_LONG, "Du siehst eine Wiese voller Feldklee.\n");
+          AddPlant(FELDKLEE);
+        }
+
+SIEHE AUCH:
+        AddItem();
+
+----------------------------------------------------------------------------
+18.01.2015, Zesstra
+
diff --git a/doc/lfun/AddPluralId b/doc/lfun/AddPluralId
new file mode 100644
index 0000000..fd37bb2
--- /dev/null
+++ b/doc/lfun/AddPluralId
@@ -0,0 +1,27 @@
+AddPluralId()
+
+FUNKTION:
+     void AddPluralId(mixed id);
+
+DEFINIERT IN:
+     /std/unit.c
+
+ARGUMENTE:
+     id
+          Identfikationsstring oder Array von Strings
+
+BESCHREIBUNG:
+     Es werden ein oder mehrere Bezeichner hinzugefuegt, mit denen sich 
+     mehrere Einheiten der Menge ansprechen lassen.
+
+RUeCKGABEWERT:
+     keiner
+
+BEISPIELE:
+     siehe /items/money.c
+
+SIEHE AUCH:
+     AddSingularId(), RemovePluralId(), AddId(), id(), /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Mon Jul 14 11:30:00 1997 by Silvana
diff --git a/doc/lfun/AddPursuer b/doc/lfun/AddPursuer
new file mode 100644
index 0000000..451ea9d
--- /dev/null
+++ b/doc/lfun/AddPursuer
@@ -0,0 +1,29 @@
+AddPursuer()
+
+FUNKTION:
+  void AddPursuer(object pursuer)
+
+ARGUMENTE:
+  pursuer: Objekt, das demjenigen folgen soll, in dem AddPursuer
+           aufgerufen wurde.
+
+FUNKTION:
+  Durch den Aufruf von AddPursuer in einem Objekt, welches living() ist,
+  wird das Object, welches als Argument uebergeben wurde in die Liste
+  der Verfolger eingetragen. Alle Objekte, die in der Verfolgerliste stehen
+  werden bei Bewegungen des Verfolgten in dasselbe Environment bewegt.
+
+RUECKGABEWERT:
+  keiner
+
+BEMERKUNG:
+  Im Verfolger wird PreventFollow mit dem Zielobjekt, in das der Verfolgte 
+  bewegt wird, aufgerufen. Dadurch kann der raeumliche Bereich, in dem 
+  verfolgt wird, eingeschraenkt werden.
+
+BEISPIELE:
+  find_player("jof")->AddPursuer(find_player("kirk"))
+  Danach wird Jof von Kirk verfolgt.
+
+SIEHE AUCH:
+  "RemovePursuer", "PreventFollow"
diff --git a/doc/lfun/AddReadDetail b/doc/lfun/AddReadDetail
new file mode 100644
index 0000000..c8de775
--- /dev/null
+++ b/doc/lfun/AddReadDetail
@@ -0,0 +1,80 @@
+AddReadDetail()
+
+FUNKTION:
+    void AddReadDetail(string|string*keys,
+                       string|string*|mapping|closure desc);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+      String oder Array von Strings mit den Namen der Details.
+    desc
+      String, Mapping, String-Array oder Closure mit/zur Beschreibung.
+
+BESCHREIBUNG:
+    Die Beschreibung der Details <keys> wird gesetzt. Wie die Details
+    beim Lesen ausgegeben werden, haengt im wesentlichen vom Typ der
+    Beschreibung <desc> ab:
+      <desc> ist ein String.
+       Beim Lesen wird dieser String zurueckgegeben.
+      <desc> ist ein String-Array.
+       Beim Lesen wird zufaellig einer der Strings zurueckgegeben.
+      <desc> ist ein Mapping.
+        Das Mapping muss folgenden Aufbau haben:
+          ([0:        "Defaulttext",
+            "rasse1": "r1text", ...]).
+
+        Falls fuer die Rasse des das Detail untersuchenden Spielers ein
+        Eintrag im Mapping existiert, wird der entsprechende Text
+        zurueckgegeben, ansonsten der Defaulttext. Auf diese Weise sind
+        rassenabhaengige Texte moeglich.
+      <desc> ist eine Closure.
+        In diesem Fall wird die Closure ausgefuehrt und das Ergebnis
+        zurueckgegeben. Die Closure bekommt dabei den Namen des Details
+        als Parameter uebergeben.
+
+    Fuer lesbare Details koennen Forscherpunkte eingetragen werden.
+
+    Will man ein lesbares Detail an einem Objekt haben, welches der Spieler
+    mit "lies <id>" (<id> ist eine ID des Objekts) bekommt, muss man ein
+    Detail SENSE_DEFAULT hinzufuegen.
+    (Ein Detail "<id>" hinzuzufuegen, hat einen ganz anderes Effekt! Dieses
+     wuerde vom Spieler mit "lies <id> an <id>" gelesen werden und ist
+     meistens nicht das, was gewuenscht wird.)
+
+BEMERKUNGEN:
+    (1) Auf die 'desc' wird kein process_string() mehr angewendet.
+        Bitte stattdessen lfun closures bzw. 'inline closures'
+        verwenden.
+
+    (2) Im Gegensatz zum Verhalten von AddTouchDetail(), AddSmells() und
+        AddSounds() wirkt ein SENSE_DEFAULT-Detail in einem Raum nicht.
+        Ein einfaches "lies" bleibt dann ohne Rueckgabewert.
+
+BEISPIELE:
+    AddReadDetail( ({ "schild" }),
+      "BETRETEN STRENGSTENS VERBOTEN!\n" );
+
+    AddReadDetail("inschrift",
+                  ([0: "Dort steht: Ein Ring sie zu binden. ....\n",
+                    "elf": "Alles in dir straeubt sich, DAS DA zu lesen.\n"]));
+
+    AddReadDetail("regeln",
+      function string() {
+        this_player()->More("/etc/WIZRULES", 1);
+        return "";
+      });
+
+SIEHE AUCH:
+    Setzen:    AddDetail(), AddSmells(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveDetail(), RemoveReadDetail(), RemoveSmells(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS, P_TOUCH_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/AddResistanceModifier b/doc/lfun/AddResistanceModifier
new file mode 100644
index 0000000..6476f86
--- /dev/null
+++ b/doc/lfun/AddResistanceModifier
@@ -0,0 +1,49 @@
+AddResistanceModifier()
+
+FUNKTION:
+     varargs int AddResistanceModifier(mapping mod, string add)
+
+DEFINIERT IN:
+     /std/living/combat.c
+
+ARGUMENTE:
+     mapping mod:
+      Mapping mit Schadensarten und ihrem Resistenzmodifikator (der im Bereich
+      von -1.0 bis +x liegen kann), z.B. ([DT_FIRE:-1.0]) (Totalresistenz).
+     string add:
+      Ein Identifikator fuer _diesen_ Eintrag des setzenden Objektes.
+
+BESCHREIBUNG:
+     Es werden Resistenzen in dem Objekt gesetzt, die solange bestehen, wie
+     das setzende Objekt existiert, oder nicht RemoveResistanceModifier
+     (mit eventuellem Schluessel add) aufgerufen wird. Zusaetzliche Resistenzen
+     werden eingerechnet.
+
+BEMERKUNGEN:
+     Fuer Ruestungen kann und sollte man P_RESISTANCE_STRENGTHS verwenden.
+
+BEISPIELE:
+     // Oel mit vervierfachtem Feuerschaden
+     int add_action() {
+      ...
+      write(break_string("Du schuettest das Oel ueber "+
+			 npc->name(WEN)+".",78));
+      ...
+      npc->AddResistanceModifier(([DT_FIRE:3.0]), "oel");
+      SetProp(P_INVIS,1);
+      SetProp(P_EXTRA_LOOK, "Ueberall tropft Oel herunter.\n");
+      move(npc,M_NOCHECK);
+      ...
+     }
+
+RUeCKGABEWERT:
+     1 fuer Erfolg
+
+SIEHE AUCH:
+     Modifikatoren:	RemoveResistanceModifier(), P_RESISTANCE_MODIFIER
+     simple Resistenz:	P_RESISTANCE, P_VULNERABILITY
+     Hauptmapping:	P_RESISTANCE_STRENGTHS
+     Berechnung:	CheckResistance(), UpdateResistanceStrengths()
+     anderes:		balance, /std/armour/combat.c, /std/living/combat.c
+
+29.Apr 2002, Gloinson@MG
diff --git a/doc/lfun/AddRoomCmd b/doc/lfun/AddRoomCmd
new file mode 100644
index 0000000..5e206fe
--- /dev/null
+++ b/doc/lfun/AddRoomCmd
@@ -0,0 +1,13 @@
+AddRoomCmd()
+
+SYNTAX:
+	AddRoomCmd( string kommando, string funktion );
+	AddRoomCmd( string *kommandos, string funktion );
+
+FUNKTION:
+	
+	Diese Funktion ist veraltet. Sie wird vollstaendig durch
+	die Funktion AddCmd ersetzt.
+
+SIEHE AUCH:
+	"AddCmd"
diff --git a/doc/lfun/AddRoomMessage b/doc/lfun/AddRoomMessage
new file mode 100644
index 0000000..4a9ca12
--- /dev/null
+++ b/doc/lfun/AddRoomMessage
@@ -0,0 +1,80 @@
+AddRoomMessage()
+
+FUNKTION:
+     void AddRoomMessage(string *msg, int time, mixed *func);
+
+DEFINIERT IN:
+     /std/room/description.c
+
+ARGUMENTE:
+     msg
+          Array von Strings mit den Meldungen.
+     time
+          Der Abstand zwischen zwei Meldungen in Sekunden.
+     func (optional)
+          String oder Array von Strings mit Funktionsnamen
+
+BESCHREIBUNG:
+     Mit dieser Funktion legt man fest, dass in bestimmten Zeitabstaenden
+     Meldungen an den Raum geschickt werden sollen.
+
+     Es wird alle time Sekunden zufaellig eine der in msg angegebenen
+     Meldungen ausgegeben. Hat man auch noch func angegeben, so wird
+     zusaetzlich diese Funktion (bei einem Array: eine zufaellig ausgesuchte
+     Funktion) im Raum aufgerufen. Als Parameter bekommt die Funktion die
+     Nummer der ausgegebenen Meldung.
+
+     Bevor man allerdings jeden Raum mit AddRoomMessage() pflastert, sollte
+     man folgendes bedenken:
+        o Viele Meldungen in vielen Raeumen tendieren dazu, den Spielern auf
+          die Nerven zu gehen!
+        o Da das Timing ueber einen call_out() gesteuert wird, ist das Ganze
+          aus Sicht des GameDrivers auch noch relativ teuer!
+     Fazit: weniger ist mehr!
+
+BEMERKUNGEN:
+     * Falls time < 15 Sekunden ist, wird auf 15 Sekunden aufgerundet.
+     * der Praefix Add... taeuscht hier. Ein Aufruf von AddRoomMessage()
+       ueberschreibt alle vorherigen Werte
+     * THIS_PLAYER() NICHT VERWENDEN!
+     In Funktionen, die durch AddRoomMessage() ausgeloest werden, darf
+     this_player() nicht verwendet werden. AddRoomMessage ist call_out-
+     gesteuert und speichert somit das this_player(). Damit ist this_player()
+     immer der Spieler, der den Raum geladen, also den Raum als erster
+     betreten hat. 
+     Spieler also bitte selbst ueber filter(all_inventory(this_object()),
+                                            #'interactive) suchen.
+
+BEISPIELE:
+     Es soll alle halbe Minute eine Meldung ausgegeben werden. Falls es
+     unter den Fuessen knackt, soll man zudem mit 30%-iger
+     Wahrscheinlichkeit zusammenzucken:
+
+     inherit "/std/room";
+
+     void create() {
+       ::create();
+       AddRoomMessage( ({ "In der Ferne schreit ein Kaeuzchen.\n",
+                          "Es raschelt im Gebuesch.\n",
+                          "Etwas knackt unter Deinen Fuessen.\n" }),
+                        30, ({"sound", "sound_more_rnd"}) );
+       ...
+     }
+
+     void sound(int msg) {
+       if (msg == 2)         // Es hat geknackt...
+         if (random(10) < 3) // Schreck lass nach! ;-)
+           tell_room(this_object(), "Erschrocken faehrst Du zusammen!\n" );
+     }
+
+     // Extra-Beispiel: wir setzen die Wartedauer (Parameter tim) neu
+     void sound_more_rnd() {
+       sound(0);   // die Message-Nummer ist hier unwichtig
+       SetProp(P_MSG_PROB, 25+random(20));  // neue Wartedauer
+     }
+
+SIEHE AUCH:
+     Verwandt: tell_room(), ReceiveMsg()
+     Props:    P_ROOM_MSG, P_FUNC_MSG, P_MSG_PROB
+
+2.Feb 2016 Gloinson
diff --git a/doc/lfun/AddRoute b/doc/lfun/AddRoute
new file mode 100644
index 0000000..3ca0093
--- /dev/null
+++ b/doc/lfun/AddRoute
@@ -0,0 +1,84 @@
+AddRoute()
+FUNKTION:
+    public varargs void AddRoute(string room, int stay, int next, 
+        string harbour_desc, mixed dest_ids, string deststr)
+
+DEFINIERT IN:
+    /std/transport.c
+
+ARGUMENTE:
+    string room
+        Filename der Haltestelle (des Ziels)
+    int stay
+        Aufenthaltszeit an der Haltestelle.
+    int next
+        Fahrtzeit von dort bis zum naechsten Fahrplanpunkt
+    string harbour_desc
+        Name der Haltestelle (fuer QueryArrived)
+    mixed dest_ids
+        kleingeschriebene IDs der Haltestelle
+    string destrstr
+        Unbenutzt / Undefiniert.
+
+BESCHREIBUNG:
+    Dem Fahrplan des Transporters wird eine neue Haltestelle hinzugefuegt.
+
+    Bei Erreichen der Haltestelle wird der Transporter in den Raum 'room'
+    bewegt und sichtbar gemacht. Nun kann man ihn fuer 'stay' Sekunden
+    betreten oder verlassen, bevor er ablegt (unsichtbar gemacht wird).
+    Nach 'next' Sekunden wird dann der naechste Punkt des Fahrplans
+    ausgefuehrt.
+     
+    'harbour_desc' ist ein String, den QueryArrived() zurueckgibt, wenn sich
+    der Transporter an einer Haltestelle befindet. In der Regel ist das ein
+    String, der die Haltestelle naeher beschreibt.
+    
+    'dest_ids' ist ein Array von Strings, die als ID-Code fuer diese 
+    Haltstelle dienen. Das wird zB bei der Ermittlung einer Haltestelle bei 
+    "reise nach" benutzt. Wenn 'dest_ids' nicht gesetzt ist, und auch 
+    P_HARBOUR des Zielhafens nicht korrekt gesetzt ist, werden 
+    grossgeschriebene Begriffe aus 'harbour_desc' verwendet.
+
+BEISPIELE:
+    #1 Hier ein Auszug aus /d/inseln/schiffe/jolle.c:
+
+      AddRoute("/d/ebene/room/PortVain/po_haf2", 40,
+               10, "Hafen von Port Vain");
+
+    Die Jolle wird also beim Anlegen in den Port Vainer Hafen bewegt und
+    laesst sich dort 40 Sekunden lang betreten oder verlassen.
+    QueryArrived() liefert waehrend dieser Zeit den String "Hafen von Port
+    Vain" zurueck. Nach den 40 Sekunden legt die Jolle ab, und nach
+    weiteren 10 Sekunden wird die naechste Station in ihrem Fahrplan
+    angefahren.
+
+    #2 Die Galeere nach Orkhausen:
+      AddRoute(INSEL("steg"), 30, 20, "Verlorene Land ( Orkhausen )" );
+      - haelt 30 Sekunden
+      - reist 20 Sekunden
+      - reist nach INSEL("steg"), bekannt als "Verlorene Land ( Orkhausen )"
+      - da keine 'dest_ids' angegeben sind, waere eine so definierte 
+        Haltstelle nur mit den IDs ansprechbar, die in P_HARBOURS im Zielraum
+        angegeben sind.
+
+    Wenn man nun erreichen wollte, dass das Ziel auch mit dem Kuerzel "vland"
+    ansprechbar ist, kann man zum einen explizite 'dest_ids' eintragen:
+      AddRoute(INSEL("steg"), 30, 20, "Verlorene Land ( Orkhausen )",
+               ({"verlorenes", "land", "orkhausen", "vland"}));
+    Dies laesst sich im Zielhafen aber auch durch Eintragen des Kuerzels
+    "vland" in P_HARBOUR erreichen. Dies hat den Vorteil, dass dieser Hafen
+    dann von allen Transportern, die dort anlegen, unter demselben Namen
+    erreicht werden kann.
+    
+HINWEISE:
+    Dadurch, dass die Eintraege aus P_HARBOUR und 'dest_ids' gleichberechtigt
+    fuer "reise nach <ziel>" verwendet werden koennen, ist es moeglich,
+    dass die Reiseziele auf einem Schiff unter zusaetzlichen Bezeichnungen 
+    bekannt sind, als an Land (zum Beispiel koennte eine fernwestliche
+    Besatzung die Ziele anders nennen).
+
+SIEHE AUCH:
+Funktionen:  AddMsg(L), AddFun(L), /std/transport.c
+Properties:  P_HARBOUR, P_NO_TRAVELING, P_TRAVEL_INFO
+
+2015-Jan-18, Arathorn.
diff --git a/doc/lfun/AddSingularId b/doc/lfun/AddSingularId
new file mode 100644
index 0000000..d956567
--- /dev/null
+++ b/doc/lfun/AddSingularId
@@ -0,0 +1,27 @@
+AddSingularId()
+
+FUNKTION:
+     void AddSingularId(mixed id);
+
+DEFINIERT IN:
+     /std/unit.c
+
+ARGUMENTE:
+     id
+          Identifikationsstring oder Array von Strings
+
+BESCHREIBUNG:
+     Es werden ein oder mehrere Bezeichner hinzugefuegt, mit denen sich eine
+     einzelne Einheit ansprechen laesst.
+
+RUeCKGABEWERT:
+     keiner
+
+BEISPIELE:
+     siehe /items/money.c
+
+SIEHE AUCH:
+     AddPluralId(), RemoveSingularId(), AddId(), id(), /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Mon Jul 14 11:31:00 1997 by Silvana
diff --git a/doc/lfun/AddSmells b/doc/lfun/AddSmells
new file mode 100644
index 0000000..5682f00
--- /dev/null
+++ b/doc/lfun/AddSmells
@@ -0,0 +1,71 @@
+AddSmells()
+
+FUNKTION:
+    void AddSmells(string|string* keys, string|string*|mapping|closure desc);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+      String oder Array von Strings mit den Namen der Details.
+    desc
+      String, Mapping, String-Array oder Closure mit/zur Beschreibung.
+
+BESCHREIBUNG:
+    Diese Funktion entspricht dem AddDetail() fuer Standarddetails, nur
+    koennen hiermit Gerueche realisiert werden:
+    Die Beschreibung der Details <keys> wird gesetzt. Wie die Details
+    bei der Untersuchung aussehen, haengt im wesentlichen vom Typ der
+    Beschreibung <desc> ab:
+      <desc> ist ein String.
+        Beim Untersuchen wird dieser String zurueckgegeben.
+      <desc> ist ein String-Array.
+        Beim Untersuchen wird zufaellig einer der Strings zurueckgegeben.
+      <desc> ist ein Mapping.
+        Das Mapping muss folgenden Aufbau haben:
+          ([0:        "Defaulttext",
+            "rasse1": "r1text", ...]).
+
+        Falls fuer die Rasse des das Detail untersuchenden Spielers ein
+        Eintrag im Mapping existiert, wird der entsprechende Text
+        zurueckgegeben, ansonsten der Defaulttext. Auf diese Weise sind
+        rassenabhaengige Details moeglich. Siehe auch die Beispiele.
+      <desc> ist eine Closure.
+        In diesem Fall wird die Closure ausgefuehrt und das Ergebnis
+        zurueckgegeben. Die Closure bekommt dabei den Namen des Details
+        als Parameter uebergeben.
+
+    Fuer Geruchsdetails koennen Forscherpunkte eingetragen werden.
+
+    Gerueche koennen allgemein einen Raum oder Objekt zugeordnet werden:
+    dafuer muss man als <key> SENSE_DEFAULT uebergeben.
+    
+    Spielerkommandos: "riech", "rieche", "schnupper", "schnuppere"
+
+BEISPIELE:
+    Ein kleines Beispiel fuer rassenabhaengige Gerueche mit Closures:
+      string strafe(string key);
+      ...
+      AddSmells(SENSE_DEFAULT,
+        "Der Geruch von Knoblauch ist ueberwaeltigend!\n");
+      AddSmells(({"knoblauch","geruch"}),
+                ([0:        "Puhh, das stinkt!\n",
+                  "vampir": #'strafe]));
+      ...
+      string strafe(string key) {
+        this_player()->reduce_hit_points(100);
+        return "Der Knoblauch schmerzt dich furchtbar!\n";
+      }
+
+SIEHE AUCH:
+    Setzen:    AddDetail(), AddReadDetail(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveDetail(), RemoveReadDetail(), RemoveSmells(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS, P_TOUCH_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/AddSounds b/doc/lfun/AddSounds
new file mode 100644
index 0000000..b38389a
--- /dev/null
+++ b/doc/lfun/AddSounds
@@ -0,0 +1,63 @@
+AddSounds()
+
+FUNKTION:
+    void AddSounds(string|string* keys, string|string*|mapping|closure desc);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+      String oder Array von Strings mit den Namen der Details.
+    desc
+      String, Mapping, String-Array oder Closure mit/zur Beschreibung.
+
+BESCHREIBUNG:
+    Diese Funktion entspricht dem AddDetail() fuer Standarddetails, nur
+    koennen hiermit Geraeusche realisiert werden:
+    Die Beschreibung der Details <keys> wird gesetzt. Wie die Details
+    bei der Untersuchung aussehen, haengt im wesentlichen vom Typ der
+    Beschreibung <desc> ab:
+      <desc> ist ein String.
+        Beim Untersuchen wird dieser String zurueckgegeben.
+      <desc> ist ein String-Array.
+        Beim Untersuchen wird zufaellig einer der Strings zurueckgegeben.
+      <desc> ist ein Mapping.
+        Das Mapping muss folgenden Aufbau haben:
+          ([0:        "Defaulttext",
+           "rasse1": "r1text", ...]).
+
+        Falls fuer die Rasse des das Detail untersuchenden Spielers ein
+        Eintrag im Mapping existiert, wird der entsprechende Text
+        zurueckgegeben, ansonsten der Defaulttext. Auf diese Weise sind
+        rassenabhaengige Details moeglich. Siehe auch die Beispiele.
+      <desc> ist eine Closure.
+        In diesem Fall wird die Closure ausgefuehrt und das Ergebnis
+        zurueckgegeben. Die Closure bekommt dabei den Namen des Details
+        als Parameter uebergeben.
+
+    Fuer Geraeuschdetails koennen Forscherpunkte eingetragen werden.
+
+    Gerauesche koennen allgemein einen Raum oder Objekt zugeordnet werden:
+    dafuer muss man als <key> SENSE_DEFAULT uebergeben.
+
+    Spielerkommandos: "lausche", "lausch", "hoer", "hoere"
+
+BEISPIELE:
+    Ein kleines Beispiel fuer rassenabhaengige Gerauesche
+      AddSounds(SENSE_DEFAULT, "Die Zwergenmusik uebertoent alles!\n");
+      AddSounds(({"zwergenmusik","musik"}),
+                ([0      : "Seltsamer Krach!\n",
+                  "zwerg": "Das klingt einfach fantastisch!\n"]));
+
+SIEHE AUCH:
+    Setzen:    AddDetail(), AddReadDetail(), AddSmells(),
+               AddTouchDetail()
+    Loeschen:  RemoveDetail(), RemoveReadDetail(), RemoveSmells(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS, P_TOUCH_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/AddSpecialDetail b/doc/lfun/AddSpecialDetail
new file mode 100644
index 0000000..90c5543
--- /dev/null
+++ b/doc/lfun/AddSpecialDetail
@@ -0,0 +1,61 @@
+VERALTET: AddSpecialDetail()
+
+FUNKTION:
+    void AddSpecialDetail(string|string* keys, string func);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+      String oder Array von Strings mit den Namen der Details.
+    func
+      String mit dem Namen der Funktion, die zur Auswertung aufgerufen
+      wird.
+
+BESCHREIBUNG:
+    Es wird ein Detail hinzugefuegt, dessen Inhalt nicht von vornherein
+    feststeht, sondern von aeusseren Bedingungen abhaengt. Zu diesem
+    Zweck wird immer, wenn dieses Detail untersucht wird, die Funktion
+    func aufgerufen, um den aktuellen Zustand des Details zu bestimmen.
+    Der Funktion wird als Parameter das Schluesselwort uebergeben, mit
+    dem das Detail untersucht wurde.
+
+    VERALTET: Bitte AddDetail mit Closure benutzen.
+
+BEISPIELE:
+    Ein zustandsabhaengiges Detail:
+
+      int hebel_betaetigt;
+      string hebel(string key);
+      ...
+      // ALT: AddSpecialDetail( ({ "hebel", "schalter" }), "hebel" );
+      AddDetail(({ "hebel", "schalter" }), #'hebel );
+      ...
+      string hebel(string key)
+      { if(hebel_betaetigt)
+          return "Der "+capitalize(key)+" steht auf EIN.\n";
+        else
+          return "Der "+capitalize(key)+" steht auf AUS.\n";
+      }
+
+    Man erhaelt verschiedene Ergebnisse beim Untersuchen, je nachdem
+    ob das Flag hebel_betaetigt gesetzt ist oder nicht.
+
+BEMERKUNG:
+    Intern werden Details und SpecialDetails im selben Mapping
+    verwaltet.
+    Man kann statt dieser Funktion deshalb auch AddDetail mit Closures
+    nutzen.
+
+SIEHE AUCH:
+    Setzen :   AddDetail(), AddReadDetail(), AddSmells(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveDetail(), RemoveReadDetail(), RemoveSmells(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS, P_TOUCH_DETAILS
+    Veraltet:  RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/AddSpecialExit b/doc/lfun/AddSpecialExit
new file mode 100644
index 0000000..de7e5b8
--- /dev/null
+++ b/doc/lfun/AddSpecialExit
@@ -0,0 +1,57 @@
+AddSpecialExit()
+FUNKTION:
+     void AddSpecialExit(string|string* cmd, string|closure func);
+
+DEFINIERT IN:
+     /std/room/exits
+
+ARGUMENTE:
+     string/string* cmd
+          die Richtung(en), in die der Ausgang fuehrt
+     string/closure func
+          der Name der aufzurufenden Funktion/Closure
+
+BESCHREIBUNG:
+     Es wird ein Ausgang in die Richtung(en) cmd eingefuegt. Wird der
+     Ausgang benutzt, so wird die Closure bzw. Funktion func ausgefuehrt.
+
+     AddSpecialExit(cmd, "func") entspricht:
+     - AddExit(keys, #'func)
+
+BEMERKUNGEN:
+     In func muss man den Spieler selbst in den Zielraum bewegen. Im
+     Erfolgsfall sollte man einen Wert >0 zurueckgeben, im Fehlerfall einen
+     Wert <=0.
+
+     func bekommt als Parameter einen String mit der gewaehlten
+     Bewegungsrichtung uebergeben.
+
+BEISPIELE:
+     // ein Ausgang soll nur von Froeschen benutzbar sein:
+
+     AddSpecialExit("loch", "lochfkt");
+     // der gleiche Aufruf, nur anders:
+     // static int lochfkt(string dir);		// Prototyp
+     // ...
+     // AddSpecialExit("loch", #'lochfkt);
+     // auch identisch zu:
+     // AddExit("loch", #'lochfkt);
+
+     static int lochfkt(string dir) {
+       if (!(this_player()->QueryProp(P_FROG))) {
+         // Kein Frosch => passt nicht!
+         notify_fail("Du bist zu gross!\n");
+         return 0;
+       }
+       // Meldungen werden im move() gleich mitgegeben
+       return this_player()->move("/room/loch", M_GO, 0,
+                    "huepft ins Loch", "huepft herein");
+     }
+
+SIEHE AUCH:
+     AddExit(), GetExits()
+     RemoveExit(), RemoveSpecialExit()
+     H_HOOK_EXIT_USE, P_EXITS, P_HIDE_EXITS, /std/room/exits.c
+     ausgaenge
+
+31.01.2015, Zesstra
diff --git a/doc/lfun/AddSpecialInfo b/doc/lfun/AddSpecialInfo
new file mode 100644
index 0000000..735983f
--- /dev/null
+++ b/doc/lfun/AddSpecialInfo
@@ -0,0 +1,64 @@
+AddSpecialInfo()
+FUNKTION:
+     varargs void AddSpecialInfo( frage, meldung
+			   [, indent [, [silent [, casebased] ] ] );
+
+ARGUMENTE:
+     string/string* frage
+	Schluesseltext(e) auf die Informationen gegeben werden sollen.
+     string/closure function
+	Methodenname im NPC/Closure
+     string indent
+	Text, der sich bei mehrzeiligen Meldungen wiederholen soll.
+     int/string silent
+	Ist silent gesetzt, so erfolgt Antwort nur an Fragenden.
+     string/closure casebased
+	Funktionsname oder Closure mit Returnwert string oder int.
+
+DEFINIERT IN:
+     /std/npc/info.c
+
+BESCHREIBUNG:
+     Wenn ein Spieler ein NPC mittels "frage <monstername> nach <frage>" nach
+     einer Information mit dem Schluessel frage fragt, so wird die Methode
+     "function" gerufen. Die Rueckgabe wird als Meldung ausgegeben.
+
+     Fuer die Beschreibung der weiteren Parameter siehe man AddInfo(L).
+
+     AddSpecialInfo(keys, "function", ...) entspricht:
+     - AddInfo(keys, #'function, ...) 
+
+BEMERKUNGEN:
+     Da AddSpecialInfo() und AddInfo() auf die gleichen Daten zugreifen,
+     kann man Informationen, die mit AddSpecialInfo() gesetzt wurden, auch
+     mit RemoveInfo() entfernen. Es gibt kein RemoveSpecialInfo().
+
+BEISPIELE:
+     // Das folgende Beispiel ist auch unter man AddInfo(L) zu finden.
+     ### dynamisch ###
+     AddSpecialInfo(({"keks","kekse"}),
+		    "query_kekse",	// der Methodenname
+		    "sagt: ");
+     // ist uebrigens das gleiche wie:
+     // static string query_kekse();
+     // ...
+     // AddInfo(({"keks","kekse"}),
+     //		#'query_kekse,		// ein Verweis auf die Methode
+     //		"sagt: ");
+     ...
+     static string query_kekse() {
+      if(present("keks"))
+       return("Ich hab noch welche. Aetsch!");
+      return("Menno. Keine mehr da!");
+     }
+
+     // "frage monster nach keks":
+     // - wenn es noch Kekse hat, hoeren alle:
+     //   "Das Monster sagt: Ich hab noch welche. Aetsch!
+     // - sonst:
+     //   "Das Monster sagt: "Menno. Keine mehr da!
+
+SIEHE AUCH:
+     AddInfo(L), RemoveInfo(L)
+
+7.Apr 2004 Gloinson
diff --git a/doc/lfun/AddSpell b/doc/lfun/AddSpell
new file mode 100644
index 0000000..dbc6869
--- /dev/null
+++ b/doc/lfun/AddSpell
@@ -0,0 +1,149 @@
+AddSpell()
+
+FUNKTION:
+    varargs int AddSpell(int rate, int damage, string TextForEnemy,
+                         string TextForOthers, string|string* dam_type,
+                         string func, int|mapping spellarg)
+
+DEFINIERT IN:
+    /std/npc/combat.c
+
+ARGUMENTE:
+    rate          - Relative Haeufigkeit der Anwendung (*),
+                    muss >= 0 sein
+    damage        - Der Schadenswert fuer Defend(),
+                    muss > 0 sein
+    TextForEnemy  - Text, den der Feind erhalten soll
+    TextForOthers - Text, den andere im Raum erhalten sollen
+    dam_type      - Schadenstyp(en) fuer Defend(),
+                    (Default: ({DT_MAGIC}) )
+    func          - Funktionsname, die nach Anwendung aufgerufen werden soll
+                    (Optional, gerufen an this_object(), bekommt als Argumente
+                     object enemy, int real_damage, string* dam_type)
+    spellarg      - Spell-Argument fuer Defend(), Default ist "1"
+
+BESCHREIBUNG:
+    Ermoeglicht einfache Angriffs-Zaubersprueche fuer NPCs. Das Ausfuehren von
+    Spells verursacht bei dem NPC keine KP-Kosten.
+
+    Mit P_SPELLRATE wird die generelle Wahrscheinlichkeit der Ausfuehrung
+    solcher Spells im Heartbeat angegeben, mit 'rate' kann man die relative
+    Wahrscheinlichkeit der Spells untereinander steuern.
+
+    (*) Relative Haeufigkeit heisst, dass sich alle 'rate' der Spells
+    aufaddieren und ein einzelnes 'rate' dann in Relation zur Gesamtsumme
+    steht. D.h. drei Spells mit 80, 10, 10 (Summe 100) haben die selben
+    Aufruf-Wahrscheinlichkeiten wie drei Spells mit 120, 15, 15 oder drei
+    Spells mit 160, 20, 20.
+
+    Ein Spell wird immer in folgender Reihenfolge abgearbeitet:
+     1. Die Texte werden an die Beteiligten ausgegeben.
+     2. Es wird ggf. SpellDefend gerufen (wenn kein SP_PHYSICAL_ATTACK).
+        Abbruch bei Schutz.
+     3. Im Opfer wird Defend() mit den angegebenen Werten aufgerufen.
+        Abbruch bei Tod/Zerstoerung des Opfers.
+     4. Eine Funktion, so definiert, wird ausgefuehrt.
+
+BEMERKUNGEN:
+    TextForOthers wird vor der Ausgabe der Meldung durch replace_personal()
+    geschickt, d.h. es koennen Platzhalter wie @WER1, @WEMQP1 und aehnliche
+    verwendet werden (siehe auch die Manpage von replace_personal()).
+    Da Ersetzungen nur fuer das Gegnerobjekt beruecksichtigt werden, koennen
+    nur Platzhalter mit Endziffer 1 verwendet werden. Die Ersetzungen werden
+    so gesteuert, dass die eingefuegten Namen nach Satzanfaengen automatisch
+    gross geschrieben werden.
+    Frueher wurden statt replace_personal() die Platzhalter @WER, @WESSEN, 
+    @WEM, @WEN verwendet. Diese funktionieren weiterhin, sollten aber nicht 
+    mehr in neuem Code benutzt werden.
+
+    In der von AddSpell gerufenen Methode "func" koennen speziellere
+    Sachen mit dem aktuellen Feind angestellt werden koennen. Die Methode
+    darf als Sichtbarkeitsmodifikator maximal "", "public" oder "static"
+    (obsolet) haben und muss im selben Objekt definiert sein.
+
+    Will man einen physischen Angriff ausloesen, MUSS <spellarg> ein Mapping
+    mit ([SP_PHYSICAL_ATTACK: 1]) sein. Bei Uebergeben einer 0 oder Weglassen
+    des Werts wird an Defend das Default '1' (da es Spells sind) uebergeben.
+
+    Wenn damage<=0 oder rate<0 oder keine Meldungen uebergeben werden, wird
+    der Spell NICHT eingetragen, sondern die Funktion bricht mit Rueckgabe
+    von 0 ab.
+
+BEISPIELE:
+    // #1 Einfacher NPC mit drei Spells, Gesamtrate = 100, also sind die
+    //    Raten direkt als Prozent Aufrufwahrscheinlichkeit lesbar.
+    AddSpell(80, 400,
+             "Der Hexer greift Dich mit einem kleinen Feuerball an.\n",
+             "Der Hexer greift @WEN mit einem kleinen Feuerball an.\n",
+             ({DT_FIRE, DT_MAGIC}));
+    AddSpell(10, 800,
+             "Der Hexer greift Dich mit einem riesigen Feuerball an.\n",
+             "Der Hexer greift @WEN mit einem riesigen Feuerball an.\n",
+             ({DT_FIRE, DT_MAGIC}));
+    AddSpell(8, 100,
+             "Der Hexer piekst Dir in die Augen!",
+             "Der Hexer piekst @WEM in die Augen!", ({DT_PIERCE}),
+             "augen_stechen");
+    AddSpell(2, 5, (string)0, (string)0, (string*)0, "salto_mortalis");
+
+    (Kleiner Feuerball mit 80% Wahrscheinlichkeit, riesiger mit 10%,
+     "augen_stechen" mit 8%, "salto_mortalis" mit 2%)
+
+    // Die Funktion "augen_stechen" kann dann so aussehen:
+    void augen_stechen(object enemy, int damage, mixed dam_types ) {
+      if (damage>10 && !enemy->QueryProp(P_BLIND)) {
+        enemy->SetProp(P_BLIND, 1);
+        if(enemy->QueryProp(P_BLIND))
+          tell_object(enemy, "Du bist nun blind!\n");
+      }
+    }
+
+    // Zur Funktion "salto_mortalis" gibt es keine Meldungen, dennoch
+    // wird Defend mit: enemy->Defend(5, ({DT_MAGIC}), 1, this_object())
+    // gerufen!
+    void salto_mortalis(object enemy, int damage, mixed dam_types ) {
+      // dem geneigten Leser ueberlassen, den Gegner zu toeten
+    }
+
+    // #2 Physische Angriffe: die Ruestungen sollen beruecksichtigt werden!
+    //    SP_PHYSICAL_ATTACK muss in einem Mapping auf 1 gesetzt werden,
+    //    damit Ruestungen physisch wirken (ansonsten werden nur ihre
+    //    DefendFuncs() ausgewertet). Es muss auch eine physische Schadensart
+    //    enthalten sein!
+    //    SpellDefend() wird bei diesem Flag nicht mehr am Gegner gerufen.
+    AddSpell(100, 200+random(200),
+      "Die kleine Ratte beisst Dich!\n",
+      "@WER wird von einer kleinen Ratte gebissen!\n",
+      ({DT_PIERCE, DT_POISON}), (string)0,
+      ([SP_PHYSICAL_ATTACK:1]));
+
+    // #3 Selektive physische Angriffe (siehe auch man Defend_bsp):
+    //    Will man erreichen, dass einige Ruestungen wirken, andere aber
+    //    nicht oder nur teilweise, kann man das ueber die Spellparameter
+    //    ausfuehrlich steuern:
+
+    // erstmal fuer alle Ruestungsarten einen Schutz von 0% einstellen:
+    mapping armours = map_indices(VALID_ARMOUR_CLASS, #'!);
+    armours[AT_TROUSERS] = 120;  // 120% Schutz durch Hosen
+    armours[AT_BOOT] = 30;       //  30% Schutz durch Stiefel
+
+    AddSpell(20,200+random(200),
+      "Die kleine Ratte beisst Dir blitzschnell in die Wade!\n",
+      "@WER wird von einer kleinen Ratte in die Wade gebissen!\n",
+      ({DT_PIERCE, DT_POISON}), (string)0,
+      ([SP_PHYSICAL_ATTACK:1, SP_NO_ACTIVE_DEFENSE:1,
+        SP_REDUCE_ARMOUR: armours]));
+
+    // SP_NO_ACTIVE_DEFENSE = 1 schaltet aktive Abwehr (Karate/Klerus) ab
+    // SP_REDUCE_ARMOUR enthaelt eine Liste von Ruestungstypen mit ihren
+    // neuen Wirkungsgraden in Prozent. Nicht enthaltene Ruestungen haben
+    // weiterhin 100% Schutzwirkung.
+
+SIEHE AUCH:
+     Sonstiges:  SpellAttack, SpellDefend, Defend, QueryDefend, SelectEnemy
+                 replace_personal
+     Properties: P_DISABLE_ATTACK, P_SPELLRATE, P_AGGRESSIVE
+     Abwehr:     Defend, Defend_bsp, SpellDefend
+     Methoden:   modifiers
+
+11.12.2015 Arathorn
diff --git a/doc/lfun/AddToMenu b/doc/lfun/AddToMenu
new file mode 100644
index 0000000..82407f4
--- /dev/null
+++ b/doc/lfun/AddToMenu
@@ -0,0 +1,233 @@
+AddToMenu()
+
+FUNKTION:
+        varargs string AddToMenu(string  menuetext,
+                                 mixed   ids,
+                                 mapping minfo,
+                                 mixed   rate,
+                                 mixed   msg,
+                                 mixed   refresh,
+                                 mixed   delay,
+                                 mixed   d_msg);
+
+DEFINIERT IN:
+        /std/pub.c
+
+ARGUMENTE:
+        Die Erlaeuterung der Parameter beschraenkt sich im Folgenden
+        zunaechst auf die Grundfunktionalitaet bei Verwendung fester
+        Werte. Die Moeglichkeiten zur Realisierung dynamischen Verhaltens
+        sind in den Fussnoten zu den einzelnen Parametern beschrieben.
+
+        menuetext
+          Der Text steht als kurze Beschreibung im Menue.
+        ids
+          String oder Array von Strings, mit denen sich die Speise bzw. das
+          Getraenk beim Bestellen ansprechen laesst.
+        minfo
+          Mapping mit Eintraegen fuer:
+          P_HP      LP-Heilung in Punkten [*]
+          P_SP      KP-Heilung in Punkten [*]
+          P_FOOD    Saettigungswirkung der Speise [*]
+          P_DRINK   Saettigungswirkung des Getraenks [*]
+          P_ALCOHOL Alkoholgehalt des Getraenks [*]
+          P_VALUE   Preis der Speise bzw. des Getraenks [*]
+        rate [*]
+          Heilrate in Punkten pro HeartBeat.
+        msg [**]
+          Meldung beim Essen:
+          Array mit 2 Strings: (1) fuer Empfaenger, (2) fuer Andere.
+          Verfuegbare Platzhalter sind weiter unten im Abschnitt "Beispiel"
+          dokumentiert.
+        refresh
+          Mapping mit Eintraegen fuer:
+            PR_USER (Kontingent fuer einzelnen Spieler) [*]
+            PR_ALL  (Zusatzkontingent fuer alle) [*]
+          Alternativ: 0 fuer unbegrenzte Verfuegbarkeit
+          Einem Key muessen dabei zwei Werte zugeordnet werden:
+          Der Erste gibt die Hoehe des Kontingents an, der Zweite legt
+          fest, alle wieviel reset()s das Kontingent wieder aufgefuellt
+          wird.
+          Verwendung des Mappings erfordert Inkludieren von pub.h.
+        delay [*]
+          Zahl der Sekunden, um die verzoegert die Heilung eintritt,
+          z.B. weil das Essen erst zubereitet werden muss.
+        d_msg [**]
+          Meldung beim Bestellen, falls die Heilung verzoegert wird
+          Array mit 2 Strings: (1) fuer Empfaenger, (2) fuer Andere.
+
+          
+        [*] Dieser Parameter kann statt eines festen Zahlenwerts mit 
+            folgenden Werten gefuellt werden:
+
+            1) Mapping <racemodifier> der Form:
+                   ([ 0 : <standardwert> ,
+                    <rasse_1> : <wert_1>, 
+                    ... , 
+                    <rasse_n> : <wert_n> ]).
+               Die Eintraege in diesem Mapping werden gegen die Rasse des
+               bestellenden Spielers geprueft und entsprechend die 
+               zugehoerigen Werte verwendet.
+            2) string <func>: Aufruf erfolgt mittels 
+                 call_other(this_object(), func, empfaenger);
+               gerufen (aber: siehe Hinweise).
+            3) closure <func>: Aufruf erfolgt mittels
+                 funcall(func, empfaenger);
+            4) Array der Form  ({string <obj>, string <func>}) oder
+                               ({object <obj>, string <func>})
+               Aufruf erfolgt mittels
+                 call_other(obj, func, empfaenger);
+               (aber: siehe Hinweise). <obj> ist folglich als Objektpointer 
+               oder dessen object_name() anzugeben. <func> wird mittels 
+               function_exists() auf Existenz ueberprueft.
+
+               HINWEISE: im Falle von Lieferverzoegerung ("delay") und
+               Preis (P_VALUE) wird bei allen Funktionsaufrufen NICHT der 
+               Empfaenger, sondern der Zahler uebergeben.
+               Im Falle der Kontingent-Liste ("refresh") kann nur die
+               verfuegbare Menge modifiziert werden, nicht die Zeit
+               bis zum Wieder-Auffuellen.
+
+        [**] Zur Erzeugung variabler Meldungen koennen folgende Werte
+             eingetragen werden:
+
+             1) closure <func>: Aufruf erfolgt mittels 
+                  funcall(func, zahler, empfaenger, ident, minfo);
+             2) string <func>: Aufruf erfolgt mittels
+                  call_other(this_object(), func, zahler, empfaenger, 
+                             ident, minfo);
+             <func> bekommt Zahler und Empfaenger als Objektpointer,
+             ident als String und minfo als Mapping mit den 
+             jeweiligen Heilwerten uebergeben. minfo entspricht hierbei
+             den Daten, die als dritter Parameter an AddToMenu()
+             uebergeben wurden.
+             HINWEIS: wenn in das minfo-Mapping keine int-Festwerte 
+             eingetragen wurden, werden diese gemaess den Regeln unter [*]
+             geprueft; Funktionen/Closures werden ggf. ausgewertet und 
+             deren Rueckgabewerte an die Funktion <func> uebergeben.
+             WICHTIG: Die Rueckgabewerte der Funktion werden nicht 
+             ausgewertet. Jeder, der anstatt einer Meldung einen 
+             Funktionsaufruf programmiert, muss fuer die Ausgabe der
+             Meldungen selbst sorgen.
+                   
+BESCHREIBUNG:
+        Mit dieser Funktion werden Speisen oder Getraenke in die Karte
+        von Kneipen und Restaurants eingefuegt.
+
+RUECKGABEWERT:
+        Rueckgabewert ist ein String "menuentry%d", wobei %d eine Nummer
+        ist, die darueber Auskunft gibt, den wievielten Eintrag in die
+        interne Karte der Kneipe diese Speise bzw. dieses Getraenk
+        darstellt. Im Prinzip handelt es sich bei dem String um einen Key
+        fuer ein Mapping, in dem die Speisen bzw. Getraenke gespeichert
+        sind.
+
+BEMERKUNGEN:
+        Die aelteren Funktionen 'AddDrink' bzw. 'AddFood' werden zwar mithilfe
+        dieser maechtigeren Funktion aus Gruenden der Abwaertskompatibilitaet
+        simuliert, sollen aber nicht mehr eingesetzt werden.
+        
+        Fuer das Testen der Kneipe gibt es in jeder Kneipe den Befehl
+        'pubinit'. Hiermit lassen sich die Speisen und Getraenke durch-
+        checken. Steht in der Ausgabe bei einem Getraenk/Essen ein FAIL,
+        so wird die entsprechende Speise (oder Getraenk) NICHT an Spieler
+        verkauft. Ausnahmen fuer Speisen/Getraenke mit hoeheren maximalen
+        Werten sind durch Balance-EM zu genehmigen.
+
+BEISPIEL:
+
+        include <pub.h>
+
+        create()
+        {
+        AddToMenu("'Opa's Drachenkeule'",({"drachenkeule","keule"}),
+        ([P_HP:63,P_SP:63,P_FOOD:9,P_VALUE:528]), 5,
+        ({"Du isst die Keule mit einem schlechten Gewissen.",
+          "&& isst die Keule mit einem schlechten Gewissen."}),
+        ([ PR_USER : 4; 1 , PR_ALL : 20; 3 ]), 9,
+        ({"Der unsichtbare Kneipier schneidet einem Rentner ein grosses "
+          "Stueck aus dessen Keule und bereitet sie Dir zu. Komisch, muss "
+          "wohl ein Tippfehler auf der Karte gewesen sein.",
+          "Der unsichtbare Kneipier schneidet einem hilflosen Opa ein "
+          "Stueck aus dessen Keule und braet diese fuer &&."}) );
+        }
+
+        1) Name der Speise (des Getraenks) auf der Karte (bei menue).
+
+           AddToMenu("'Opa's Drachenkeule'",     
+
+        2) ids mit denen sich bestellen laesst (z.B. "kaufe keule").
+
+           ({"drachen","drachenkeule","keule"}),
+
+        3) Heilung fuer LP und KP, Saettigung (P_FOOD oder P_DRINK,
+           P_ALCOHOL nach Belieben setzen), Preis (P_VALUE). 
+           HP und SP muessen nicht gleich sein. Speisen und Getraenke,
+           die nur eines von beiden heilen, sind auch moeglich.
+
+           ([P_HP:63,P_SP:63,P_FOOD:9,P_VALUE:528]),
+
+        4) Heilung pro Heartbeat (in diesem Beispiel je 5 KP/LP).
+    
+           5,
+
+        5) Meldungen fuer Spieler und Umstehende die bei Genuss ausgege-
+           ben werden (also NICHT der Bestell-Text).
+
+           ({"Du isst die Keule mit einem schlechten Gewissen.",
+             "&& isst die Keule mit einem schlechten Gewissen."}),
+
+           Verfuegbare Platzhalter sind (pl = this_player()):
+
+           &&  - pl->name(WER,2)
+           &1& - pl->name(WER,2)
+           &2& - pl->name(WESSEN,2)
+           &3& - pl->name(WEM,2)
+           &4& - pl->name(WEN,2)
+           &1# - capitalize(pl->name(WER,2))
+           &2# - capitalize(pl->name(WESSEN,2))
+           &3# - capitalize(pl->name(WEM,2))
+           &4# - capitalize(pl->name(WEN,2))
+           &!  - pl->QueryPronoun(WER)
+           &5& - pl->QueryPronoun(WE);
+           &6& - pl->QueryPronoun(WESSEN)
+           &7& - pl->QueryPronoun(WEM)
+           &8& - pl->QueryPronoun(WEN)
+           &5# - capitalize(pl->QueryPronoun(WER))
+           &6# - capitalize(pl->QueryPronoun(WESSEN))
+           &7# - capitalize(pl->QueryPronoun(WEM))
+           &8# - capitalize(pl->QueryPronoun(WEN))
+          
+        6) Die Speise ist in ihrer Anzahl begrenzt. Fuer jeden Spieler
+           sind 4 Keulen pro reset() da. Ausserdem gibt es noch einen
+           "Notvorrat" von 20 Keulen, der alle 3 reset()s aufgefuellt
+           wird. Aus diesem (so noch vorhanden) werden die Spieler
+           versorgt, wenn ihr "persoenlicher Vorrat" aufgebraucht ist.
+
+           ([ PR_USER : 4; 1 , PR_ALL : 20; 3 ]),
+           
+           HINWEIS: bei Benutzung des Mappings muss <pub.h> inkludiert 
+           werden!
+           
+           Wenn man keine reset-abhaengigen Speisen haben moechte, traegt
+           man hier eine 0 ein.
+
+        7) Die Zahl ist die Wartezeit in Sekunden, die der Wirt z.B. fuer
+           die Zubereitung und Auslieferung an den Spieler braucht.
+
+           9,
+         
+        8) Letztendlich die Meldungen an Spieler und Umstehende, die bei Be-
+           stellung (hier 'kaufe keule') ausgegeben werden.
+
+           ({"Der unsichtbare Kneipier schneidet einem Rentner ein grosses "
+           "Stueck aus dessen Keule und bereitet sie Dir zu. Komisch, muss "
+           "wohl ein Tippfehler auf der Karte gewesen sein.",
+           "Der unsichtbare Kneipier schneidet einem hilflosen Opa ein "
+           "Stueck aus dessen Keule und braet diese fuer &&."}));
+
+SIEHE AUCH:
+        AddFood(), AddDrink(), /sys/pub.h
+        RemoveFromMenu()
+----------------------------------------------------------------------------
+Last modified: Sam, 01. Okt 2011, 23:40 by Arathorn
diff --git a/doc/lfun/AddTouchDetail b/doc/lfun/AddTouchDetail
new file mode 100644
index 0000000..413e3ca
--- /dev/null
+++ b/doc/lfun/AddTouchDetail
@@ -0,0 +1,71 @@
+AddTouchDetail()
+
+FUNKTION:
+    void AddTouchDetail(string|string* keys,
+                        string|string*|mapping|closure desc);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+     String oder Array von Strings mit den Namen der Details.
+    desc
+     String, Mapping, String-Array oder Closure mit/zur Beschreibung.
+
+BESCHREIBUNG:
+    Diese Funktion entspricht dem AddDetail() fuer Standarddetails, nur
+    koennen hiermit (ab)tastbare bzw. fuehlbare Details realisiert werden:
+    Die Beschreibung der Details <keys> wird gesetzt. Wie die Details
+    beim (Ab)Tasten aussehen, haengt im wesentlichen vom Typ der
+    Beschreibung <desc> ab:
+     <desc> ist ein String.
+       Beim Untersuchen wird dieser String zurueckgegeben.
+      <desc> ist ein String-Array.
+        Beim Untersuchen wird zufaellig einer der Strings zurueckgegeben.
+     <desc> ist ein Mapping.
+       Das Mapping muss folgenden Aufbau haben:
+         ([0:        "Defaulttext",
+           "rasse1": "r1text", ...]).
+
+       Falls fuer die Rasse des das Detail untersuchenden Spielers ein
+       Eintrag im Mapping existiert, wird der entsprechende Text
+       zurueckgegeben, ansonsten der Defaulttext. Auf diese Weise sind
+       rassenabhaengige Details moeglich. Siehe auch die Beispiele.
+     <desc> ist eine Closure.
+       In diesem Fall wird die Closure ausgefuehrt und das Ergebnis
+       zurueckgegeben. Die Closure bekommt dabei den Namen des Details
+       als Parameter uebergeben.
+
+    Fuer fuehlbare Details koennen Forscherpunkte eingetragen werden.
+
+    Fuehlbare Details koennen allgemein einen Raum oder Objekt zugeordnet
+    werden: dafuer muss man als <key> SENSE_DEFAULT uebergeben.
+
+    Spielerkommandos: "taste"
+
+BEISPIELE:
+    Ein kleines Beispiel fuer rassenabhaengige, fuehlbare Details mit Closures:
+      string strafe(string key);
+      ...
+      AddTouchDetail(SENSE_DEFAULT, "Du fuehlst einige Knollen\n");
+      AddTouchDetail(({"knollen"}),
+                     ([0:       "Sie fuehlen sich an wie Knoblauchknollen. "
+                                "Riech doch mal daran.\n",
+                       "vampir": #'strafe]));
+
+      string strafe(string key) {
+        this_player()->reduce_hit_points(100);
+        return "Au! Das waren ja Knoblauchknollen!\n";
+      }
+
+SIEHE AUCH:
+    Setzen:    AddDetail(), AddReadDetail(), AddSmells(), AddSounds()
+    Loeschen:  RemoveDetail(), RemoveReadDetail(), RemoveSmells(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS, P_TOUCH_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/AllGroups b/doc/lfun/AllGroups
new file mode 100644
index 0000000..8ffbbca
--- /dev/null
+++ b/doc/lfun/AllGroups
@@ -0,0 +1,21 @@
+AllGroups()
+FUNKTION:
+     string *AllGroups()
+
+DEFINIERT IN:
+     /p/daemon/materialdb.c (MATERIALDB)
+
+BESCHREIBUNG:
+     Gibt ein Array aller existenten Materialgruppen zurueck (ihre
+     Definitionsform wie in <thing/material.h> aufgelistet).
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Listen:	  AllMaterials(), Dump()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/AllMaterials b/doc/lfun/AllMaterials
new file mode 100644
index 0000000..6230e9b
--- /dev/null
+++ b/doc/lfun/AllMaterials
@@ -0,0 +1,21 @@
+AllMaterials()
+FUNKTION:
+     string *AllMaterials()
+
+DEFINIERT IN:
+     /p/daemon/materialdb.c (MATERIALDB)
+
+BESCHREIBUNG:
+     Gibt ein Array aller existenten Materialien zurueck (ihre Definitions-
+     form wie in <thing/material.h> aufgelistet).
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Listen:	  AllGroups(), Dump()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/AssocMember b/doc/lfun/AssocMember
new file mode 100644
index 0000000..349157b
--- /dev/null
+++ b/doc/lfun/AssocMember
@@ -0,0 +1,52 @@
+
+AssocMember()
+
+
+FUNKTION:
+        int AssocMember(object npc)
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        Der zuzuordnende NPC.
+
+BESCHREIBUNG:
+        Ordnet einen NPC einem Spieler zu. Dieser ist dann
+        immer im Team des Spielers, moeglichst in der gleichen Reihe.
+
+RUECKGABEWERT:
+        1, falls Zuordnung erfolgreich, sonst 0.
+
+BEISPIEL:
+        void fun(object pl)
+        {
+          if ( pl && pl->AssocMember(this_object()) )
+            tell_object(pl,break_string(
+              "Ich kaempfe nun auf Deiner Seite!\n",78,"Ein Feuerteufel "
+              "teilt Dir mit:");
+         ...
+        }
+
+BEMERKUNGEN:
+        1. Kann nur von Gilden, Spellbooks, vom Objekt selber und vom
+           zuzuordnenden NPC aufgerufen werden.
+        2. Einem NPC, der selber einem Spieler zugeordnet ist, kann kein
+           NPC zugeordnet werden.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_TEAM_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      DeAssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 04.10.2011, Zesstra
+
diff --git a/doc/lfun/Attack b/doc/lfun/Attack
new file mode 100644
index 0000000..4656ae9
--- /dev/null
+++ b/doc/lfun/Attack
@@ -0,0 +1,22 @@
+Attack()
+
+FUNKTION:
+	void Attack(object enemy)
+
+ARGUMENTE:
+	enemy: Der Feind.
+
+BESCHREIBUNG:
+	Der Feind wird der Staerke der Waffe (bzw. der Haende) entsprechend
+	stark angegriffen.
+
+RUECKGABEWERT:
+	Keiner.
+
+BEMERKUNG:
+	Diese Funktion gibt die Angriffsmeldung aus.
+	Falls mit blossen Haenden angegriffen wird, ist die Staerke
+	(2*Staerke_der_Haende + 10*A_STR)/3
+
+SIEHE AUCH:
+	"Defend", "QueryDamage"
diff --git a/doc/lfun/BecomesNetAlive b/doc/lfun/BecomesNetAlive
new file mode 100644
index 0000000..22a6cdc
--- /dev/null
+++ b/doc/lfun/BecomesNetAlive
@@ -0,0 +1,46 @@
+BecomesNetAlive()
+
+FUNKTION:
+    void BecomesNetAlive(object pl);
+
+GERUFEN VON:
+    /std/player/base.c
+
+ARGUMENTE:
+    pl
+      Spieler, der Verbindung zum MUD wiedererlangt.
+
+BESCHREIBUNG:
+    Spieler, welche die Verbindung zum MUD freiwillig
+    (z.B. per 'schlafe ein') oder unfreiwillig verlieren, gehen in den
+    Netztotenstatus ueber. Sie verbleiben noch eine definierte Zeit in
+    der zuletzt betretenen Umgebung und werden schliesslich automatisch
+    in den Netztotenraum ueberfuehrt.
+    Wird die Verbindung wieder aufgebaut, so wird der Spielercharakter
+    wieder zum Leben erweckt und gegebenenfalls zuvor aus dem
+    Netztotenraum zurueck zu seiner letzten Umgebung teleportiert.
+    Um nun einen Ueberblick darueber zu erhalten, wann ein Spieler die
+    Verbindung zum MUD wiederherstellt, gibt es die Funktion
+    BecomesNetAlive(). Sie wird automatisch in der Umgebung des
+    Spielers, in allen Objekten in der Umgebung des Spielers
+    (nicht rekursiv) und in allen Objekten im Spieler (rekursiv)
+    aufgerufen. Uebergeben wird hierbei das Spielerobjekt.
+
+    Es gibt auch noch die Funktion BecomesNetDead(), mit der man
+    auf aehnliche Weise einschlafende Spieler registrieren kann.
+
+BEISPIELE:
+    In einem NPC waere folgendes denkbar:
+    
+    void BecomesNetAlive(object pl) {
+      tell_room(environment(),break_string(
+        "Guten Morgen "+pl->name(WER)+", auch schon ausgeschlafen?!", 77,
+        Name(WER)+" sagt grinsend: "));
+    }
+    Steht der NPC in einem Raum, wo ein Spieler aufwacht, so wird der
+    Spieler gebuehrend begruesst.
+
+SIEHE AUCH:
+    BecomesNetDead(), PlayerQuit(), /std/player/base.c, /room/netztot.c
+
+24. Aug 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/BecomesNetDead b/doc/lfun/BecomesNetDead
new file mode 100644
index 0000000..fcfed7c
--- /dev/null
+++ b/doc/lfun/BecomesNetDead
@@ -0,0 +1,45 @@
+BecomesNetDead()
+
+FUNKTION:
+    void BecomesNetDead(object pl);
+
+GERUFEN VON:
+    /std/player/base.c
+
+ARGUMENTE:
+    object pl
+      Spieler, der Verbindung zum MUD verliert.
+
+BESCHREIBUNG:
+    Spieler, welche die Verbindung zum MUD freiwillig
+    (z.B. per 'schlafe ein') oder unfreiwillig verlieren, gehen in den
+    Netztotenstatus ueber. Sie verbleiben noch eine definierte Zeit in
+    der zuletzt betretenen Umgebung und werden schliesslich automatisch
+    in den Netztotenraum ueberfuehrt.
+    Um nun einen Ueberblick darueber zu erhalten, wann ein Spieler die
+    Verbindung zum MUD verliert, gibt es die Funktion BecomesNetDead().
+    Sie wird automatisch in der Umgebung des Spielers, in allen Objekten
+    in der Umgebung des Spielers (nicht rekursiv) und in allen Objekten
+    im Spieler (rekursiv) aufgerufen. Uebergeben wird hierbei das
+    Spielerobjekt.
+
+    Es gibt auch noch die Funktion BecomesNetAlive(), mit der man
+    auf aehnliche Weise erwachende Spieler registrieren kann.
+
+BEISPIELE:
+    Es gibt Gebiete, in denen netztote Spieler unerwuenscht sind.
+    Folgendermassen kann man sich ihrer wirkungsvoll entledigen:
+    
+    void BecomesNetDead(object pl) {
+      pl->move("eingangsraum zu gebiet", M_TPORT|M_NOCHECK);
+    }
+    Man schickt diese Spieler wieder zum Eingang des Gebietes.
+    Da der letzte Aufenthaltsort eines Spielers, der in den
+    Netztotenstatus uebergeht, erst nach Aufruf der Funktion
+    BecomesNetDead() abgespeichert wird, wacht der Spieler dann an dem
+    Ort auf, wo man Ihn innerhalb dieser Funktion hinteleportiert hat.
+
+SIEHE AUCH:
+    BecomesNetAlive(), PlayerQuit(), /std/player/base.c, /room/netztot.c
+
+24. Aug 2011, Gloinson
\ No newline at end of file
diff --git a/doc/lfun/CannotSee b/doc/lfun/CannotSee
new file mode 100644
index 0000000..3869541
--- /dev/null
+++ b/doc/lfun/CannotSee
@@ -0,0 +1,30 @@
+CannotSee()
+
+FUNKTION:
+     varargs int CannotSee(int silent);
+
+DEFINIERT IN:
+     /std/living/light.c
+
+ARGUMENTE:
+     silent - Soll an das Lebewesen direkt automatisch eine Meldung
+              ausgegeben werden wenn es nichts sehen kann?
+
+BESCHREIBUNG:
+     Diese Funktion prueft ob das Lebewesen etwas sehen kann, oder nicht.
+     Hierbei wird sowohl das Lichtlevel mit saemtlichen Modifikatoren,
+     als auch Nachtsicht und die Property P_BLIND beruecksichtigt. Da
+     diese Funktion bei zukuenftigen Mudlibaenderungen immer aktualisiert
+     werden duerfte, sollte man sie nach Moeglichkeit benutzen und die
+     Abfragen nicht selbst implementieren.
+
+RUeCKGABEWERT:
+     0, wenn der Spieler etwas sehen kann
+     1, wenn der Spieler nichts sehen kann: Blindheit
+     2, wenn der Spieler nichts sehen kann: zu wenig Licht/keine Nachtsicht
+
+SIEHE AUCH:
+     P_BLIND, P_LIGHT_MODIFIER, P_PLAYER_LIGHT
+
+----------------------------------------------------------------------------
+Last modified: Mon Jan 17 18:22:27 2000 by Padreic
diff --git a/doc/lfun/ChangeMiniQuest b/doc/lfun/ChangeMiniQuest
new file mode 100644
index 0000000..a6bfb7d
--- /dev/null
+++ b/doc/lfun/ChangeMiniQuest
@@ -0,0 +1,48 @@
+FUNKTION:
+    int ChangeMiniQuest(mixed questgeber, int parameter, mixed newvalue)
+
+DEFINIERT IN:
+    /secure/questmaster
+
+BESCHREIBUNG:
+    Diese Funktion aendert einen Parameter einer Miniquest im Questmaster,
+    schreibt fuer diese Aktion einen Log-Eintrag und erstellt das Miniquest-
+    Dumpfile neu.
+
+ARGUMENTE:
+    questgeber - Ladename des Objekts (string), das die Miniquest vergibt, 
+                 oderdie Indexnummer (int) der Miniquest in der MQ-Liste
+    parameter  - Angabe des zu aendernen Parameters (Position des Values
+                 im Miniquests-Mapping):
+                 0 : Miniquest-Stufenpunkte, mind. 1
+                 2 : Aufgabenbeschreibung der Miniquest (string)
+                 3 : Sichtbarkeit der Miniquest (0/1), default ist 1
+                 4 : aktiv/inaktiv (1/0)
+                 5 : Titel der Miniquest
+                 6 : "geschafft"-Beschreibung nach Abschluss der MQ
+                 7 : Voraussetzungen, Mapping im Format von P_RESTRICTIONS
+                 8 : zugeordnete Region, String wie z.B."polar", "gebirge"
+                 9 : erlaubte Abfrageobjekte, Array von Ladenamen, z.B.
+                     ({"/d/region/magier/npc/infonpc"}), es koennen mehrere 
+                     Objekte eingetragen sein
+    newvalue   - neuer Wert fuer den angegebenen Parameter
+
+RUECKGABEWERTE:
+     1: hat geklappt
+     0: Zugriff verweigert
+    -2: ungueltiger Datentyp eines der Argumente, bei Parameter 9 wird
+        ein uebergebenes Array zusaetzlich auf Leerstrings und Elemente
+        geprueft, die keine Strings sind. Wenn das Array ausschliesslich
+        aus solchen Elementen besteht, wird ebenfalls -2 zurueckgegeben.
+
+BEMERKUNGEN:
+    Das Flag "active" laesst sich bequemer ueber die Questmaster-Funktion
+    SwitchMiniQuestActive() umschalten.
+    Der Miniquest-Titel darf kein "in" oder "im" enthalten, weil dann die
+    Eintraege in der Fraternitas-Bibliothek nicht gelesen werden
+    koennen.
+
+SIEHE AUCH:
+   AddMiniQuest(L)
+   P_RESTRICTIONS
+
diff --git a/doc/lfun/ChangeReputation b/doc/lfun/ChangeReputation
new file mode 100644
index 0000000..e7c3af6
--- /dev/null
+++ b/doc/lfun/ChangeReputation
@@ -0,0 +1,45 @@
+ChangeReputation
+FUNKTION:
+     public varargs int ChangeReputation(string repid, int value, int silent)
+
+DEFINIERT IN:
+     /std/player/reputation.c
+
+ARGUMENTE:
+     repid
+       Jede neue Reputationsgruppe muss anfangs mit einer eindeutigen ID von 
+       einem EM in den Reputationsmaster eingetragen werden. Danach kann man 
+       ueber die eindeutige ID <repid> auf sie zugreifen.
+     value
+       Der Wert, um den die Reputation geaendert werden soll. Positive Werte 
+       erhoehen die Reputation, negative verschlechtern sie.
+     silent
+       Ein optionales Flag. Falls gesetzt, wird keine Standardmeldung ueber 
+       die Reputationsaenderung an den Spieler ausgegeben. Man koennte dann 
+       eigene Meldungen ausgeben.
+
+BESCHREIBUNG:
+     Vor der Aenderung wird ein Check auf die UID des ausfuehrenden Objektes
+     ausgefuehrt, "fremde" Reputationen darf man somit nicht veraendern.
+     Man kann aber selbstverstaendlich in begruendeten Faellen mit dem
+     zustaendigen Magier/Regionsmagier sprechen, ob man ebenfalls Zugriff
+     erhaelt. Eingetragen wird dies schlussendlich durch einen EM.
+
+RUeCKGABEWERT:
+     REP_RET_SUCCESS    Reputation wurde veraender.
+     REP_RET_SUCCESSCUT Reputation wurde auf Min / Max veraendert
+     REP_RET_WRONGARGS  Falsche Argumente fuer ChangeRep()
+     REP_RET_INVALIDUID Unzulaessige UID / keine Zugriffsrechte
+     REP_RET_ALREADYMAX Reputation bereits Max / Min
+     REP_RET_INACTIVE   Reputation momentan inaktiv
+     REP_RET_INVALIDREP Reputation nicht vorhanden
+
+BEISPIELE:
+     s. reputation
+
+SIEHE AUCH:
+     reputation
+     GetReputation(), GetReputations()
+
+ZULETZT GEAeNDERT:
+06.04.2009, Zesstra
diff --git a/doc/lfun/CheckFindRestrictions b/doc/lfun/CheckFindRestrictions
new file mode 100644
index 0000000..f9c96e5
--- /dev/null
+++ b/doc/lfun/CheckFindRestrictions
@@ -0,0 +1,23 @@
+CheckFindRestrictions
+
+FUNKTION:
+    varargs int CheckFindRestrictions(object ob, mixed restr, closure qp)
+ 
+DEFINIERT IN:
+    /std/room/shop.c
+ 
+ARGUMENTE:
+    ob     - Objekt
+    restr  - zusaetzliches Argument
+    qp     - symbol_function("QueryProp",ob)
+
+BESCHREIBUNG:
+    Funktion, die FindBestWeapon / FindBestArmours einschraenkt.
+    Wird in Wert ungleich 0 zurueckgeliefert, so wird das Objekt
+    nicht genommen.
+
+RUECKGABEWERT:
+    Normalerweise 0
+
+SIEHE AUCH:
+    FindBestWeapon(), FindBestArmours()
diff --git a/doc/lfun/CheckLightType b/doc/lfun/CheckLightType
new file mode 100644
index 0000000..1a0490a
--- /dev/null
+++ b/doc/lfun/CheckLightType
@@ -0,0 +1,84 @@
+CheckLightType()
+
+FUNKTION:
+	varargs int CheckLightType(int lighttype, int mode);
+
+DEFINIERT IN:
+	/std/thing/description.c
+
+ARGUMENTE:
+	lighttype
+	  Auf diesen Lichttyp wird getestet.
+	mode
+	  Die Methode, nach der der Lichttyp ueberprueft wird.
+
+BESCHREIBUNG:
+        Die Funktion prueft, ob der uebergebene lighttype mit dem in
+        P_LIGHT_TYPE definierten Lichttyp uebereinstimmt. 
+
+        Dabei kann in verschiedenen Modi getestet werden:
+
+        LT_CHECK_ANY
+          Es wird geprueft, ob mindestens einer der in lighttype ueber
+          gebenen Lichttypen im Objekt vorhanden ist. Dies ist das
+          Standardverhalten (default) der Funktion.
+
+        LT_CHECK_ALL     
+          Es wird geprueft, ob alle in lighttype definierten Lichttypen
+          vorhanden sind. Es koennen aber auch mehr Lichttypen definiert
+          sein.
+
+        LT_CHECK_MATCH   
+          Es wird geprueft, ob genau die in lighttype definierten Licht-
+          tyen definiert sind, sind mehr oder weniger vorhanden, gibt die
+          Funktion 0 zurueck.
+
+        LT_CHECK_NONE    
+          Es wird geprueft, ob keiner der uebergebenen Lichttypen vorhanden
+          sind. Ob sonst noch andere Lichttypen vorhanden sind, ist dabei 
+          egal.
+
+RUeCKGABEWERT:
+	0 wenn die geprueften Bedingungen nicht korrekt sind, sonst 
+        ein Wert ungleich 0.
+
+BEISPIELE:
+        In einem Raum scheint die Sonne, ausserdem gibt es dort ein Lager-
+        feuer und ein Objekt mit magischem Gluehen (meine Phantasie streikt
+        grad):
+
+        raum->SetProp( P_LIGHT_TYPE, LT_SUN|LT_OPEN_FIRE|LT_GLOWING );
+
+        Es soll getestet werden, ob an in dem Raum Tageslicht herrscht:
+
+        raum->CheckLightType(LT_DAYLIGHT, LT_CHECK_ANY);
+        raum->CheckLightType(LT_DAYLIGHT); // gleichwertig
+
+        Die Funktion ergibt wahr, da LT_DAYLIGHT unter anderem LT_SUN ent-
+        haelt (vgl man P_LIGHT_TYPES).
+
+        Es soll getestet werden, dass weder Mond noch Sterne im Raum sind:
+
+        raum->CheckLightType(LT_MOON|LT_STARS, LT_CHECK_NONE);
+
+        Die Funktion ergibt wahr, da die beiden nicht gesetzt sind.
+
+        Es soll geprueft werden, ob Mond und Sterne im Raum leuchten:
+
+        raum->CheckLightType(LT_MOON|LT_STARS, LT_CHECK_ALL);
+
+        Die Funktion ergibt falsch, da keins der beiden Lichter vorhanden
+        ist. Sie ergaebe aber auch falsch, wenn einer der beiden Typen
+        vorhanden waer. Nur wenn beide vorhanden sind, gibt LT_CHECK_ALL
+        wahr.
+
+BEMERKUNG:
+        Lighttypes haben nichts mit dem Lichtsystem zu tun. Sie dienen 
+        nur der Beschreibung der Lichtverhaeltnisse an/in einem Objekt.
+        Objekte mit verschiedenen Lichtverhaeltnissen beeinflussen sich
+        gegenseitig nicht.
+
+SIEHE AUCH:
+        /std/thing/description.c, /std/thing/lighttypes.h, P_LIGHT_TYPE
+----------------------------------------------------------------------------
+Last modified: Fri Jun 11 20:47:33 2004 by Vanion
diff --git a/doc/lfun/CheckResistance b/doc/lfun/CheckResistance
new file mode 100644
index 0000000..45486a5
--- /dev/null
+++ b/doc/lfun/CheckResistance
@@ -0,0 +1,20 @@
+CheckResistance()
+
+FUNKTION:
+	CheckRessistance(string* dam_type_list)
+
+ARGUMENTE:
+	dam_type_list: Liste der Schadensarten, die geprueft werden
+
+BESCHREIBUNG:
+	Diese Funktion ueberprueft, gegen welche der angegebenen
+	Schadensarten das Lebewesen resistent oder verletzlich ist und
+	gibt einen Faktor zurueck, der mit dem Schaden multipliziert wird.
+	Dieser Faktor betraegt normalerweise 1, bei bei jeder Resistenz
+	wird er halbiert und bei jeder Verletzlichkeit verdoppelt.
+
+RUECKGABEWERT:
+	Ein Faktor, der mit dem Schaden multipliziert wird, vom Typ "float".
+
+SIEHE AUCH:
+	"Defend"
diff --git a/doc/lfun/CheckSensitiveAttack b/doc/lfun/CheckSensitiveAttack
new file mode 100644
index 0000000..32aec2e
--- /dev/null
+++ b/doc/lfun/CheckSensitiveAttack
@@ -0,0 +1,33 @@
+CheckSensitiveAttack()
+FUNKTION:
+     void CheckSensitiveAttack(int dam, string *dam_type, mixed spell,
+			       object enemy)
+
+DEFINIERT IN:
+     /std/living/inventory.c
+
+ARGUMENTE:
+     dam	- an Defend uebergebener Schaden
+     dam_type	- Schadenstyp(en)
+     spell	- spell, int oder mapping
+     enemy	- Feindesobjekt
+
+BESCHREIBUNG:
+     Wird von Defend() aufgerufen und prueft die Listen in
+     P_SENSITIVE_ATTACK durch.
+     Wenn die Schluessel und Threshold-Werte stimmen, wird
+     trigger_sensitive_attack(enemy,key,dam,spell,options)
+     im Objekt gerufen.
+
+BEMERKUNGEN:
+     Objekte mit P_SENSITIVE mit Schluessel SENSITIVE_ATTACK bitte vorsichtig
+     verwenden, da rechenintensiv.
+
+SIEHE AUCH:
+     P_SENSITIVE
+     InsertSensitiveObject, RemoveSensitiveObject
+     insert_sensitive_inv_trigger, insert_sensitive_inv
+     P_SENSITIVE_ATTACK, P_SENSITIVE_INVENTORY,
+     P_SENSITIVE_INVENTORY_TRIGGER
+
+28.Jan.2001, Gloinson@MG
diff --git a/doc/lfun/CheckSpellFatigue b/doc/lfun/CheckSpellFatigue
new file mode 100644
index 0000000..0d7cc2a
--- /dev/null
+++ b/doc/lfun/CheckSpellFatigue
@@ -0,0 +1,61 @@
+CheckSpellFatigue
+
+FUNKTION:
+    public varargs int CheckSpellFatigue(string key)
+
+DEFINIERT IN:
+    /std/living/skills.c
+    /std/player/skills.c
+    /sys/living/skills.h
+
+ARGUMENTE:
+    string key  : Eindeutiger Name des Spruches, einer Gruppe von Spruechen
+                  oder 0 fuer die globale Spruchermuedung.
+
+BESCHREIBUNG:
+    Diese Funktion dient zum Pruefen von individuellen Spruchermuedungen
+    (Spellfatigue, Spruchsperren).
+    Hiermit lassen sich unabhaengige Ermuedungen/Sperren fuer einzelne
+    Sprueche oder Gruppen von Spruechen gestalten.
+
+    Wird <key> nicht angegeben oder ist 0, wird die globale Spruchsperre
+    geprueft (identisch zu der Property P_NEXT_SPELL_TIME), anderenfalls 
+    die unter <key> gespeicherte Spruchermuedung.
+    Prueft man einen Eintrag ohne Angabe von <key> ist das Ergebnis dieser
+    Funktion identisch zur Abfrage von P_NEXT_SPELL_TIME.
+
+RUeCKGABEWERT:
+    0    Spruchermuedung existiert nicht oder ist abgelaufen.
+
+    >0   Spruchermuedung ist noch nicht abgelaufen, Rueckgabewert ist die
+         Zeit, bei der dieser Eintrag ablaeuft. Der Spruch darf _nicht_
+         ausgefuehrt werden.
+
+BEISPIELE:
+    Ein Spell gehoert zu einer Gruppe von Spells mit dem Namen 'extrasuess'.
+    Er darf nur ausgefuehrt werden, wenn seit 5s kein anderer Spruch aus der
+    Gruppe ausgefuehrt wurde.
+    if (ob->CheckSpellFatigue("extrasuess") {
+      // alte Sperre noch nicht abgelaufen.
+      tell_object(ob, "Deine unendliche Schokotorte ist noch nicht wieder "
+        "nachgewachsen.\n");
+      return ... ;
+    }
+
+BEMERKUNGEN:
+    Die genauen Zeitdauern koennen von Spielern beeinflusst werden, sie
+    unterliegen der jeweiligen Einstellung von 'spruchermuedung', d.h. koennen
+    auf volle 2s aufgerundet werden.
+    Auch wenn diese Funktion zum Verwalten von beliebigen Zeitsperren genutzt
+    werden koennen, beschraenkt euch bitte auf Spruchermuedungen und benutzt
+    ansonsten check_and_update_timed_key(). Falls ihr diesbzgl. weitere/andere
+    Wuensche habt, sprecht den/die Mudlib-EM an.
+
+SIEHE AUCH:
+    SetSpellFatigue(L), DeleteSpellFatigue(L)
+    P_NEXT_SPELL_TIME
+    spruchermuedung
+
+----------------------------------------------------------------------------
+27.03.2010, Zesstra
+
diff --git a/doc/lfun/ClearRingBuffer b/doc/lfun/ClearRingBuffer
new file mode 100644
index 0000000..0095dec
--- /dev/null
+++ b/doc/lfun/ClearRingBuffer
@@ -0,0 +1,34 @@
+ClearRingBuffer()
+
+FUNKTION:
+    protected struct std_ringbuffer ClearRingBuffer(struct std_ringbuffer b);
+
+DEFINIERT IN:
+    /std/util/ringbuffer.c
+    /sys/util/ringbuffer.h
+
+ARGUMENTE:
+    b - der zu loeschende Ringpuffer
+
+BESCHREIBUNG:
+    Diese Funktion loescht alle Daten aus dem Puffer <b> und re-initialisiert
+    ihn.
+    
+RUeCKGABEWERT:
+    Der geloeschte Puffer <b> wird wieder zurueckgegeben.
+
+BEISPIELE:
+    // Ringpuffer anlegen:
+    struct std_ringbuffer buffer=CreateRingBuffer(10, MODE_FIFO);
+    // mit irgendwelchen Daten fuellen...
+    // ...
+    // Puffer loeschen
+    buffer = ClearRingBuffer(buffer);
+    // oder:
+    ClearRingBuffer(buffer);
+
+SIEHE AUCH:
+    CreateRingBuffer(), RingBufferGet(), ResizeRingBuffer()
+
+23.05.2008, Zesstra
+
diff --git a/doc/lfun/Configure b/doc/lfun/Configure
new file mode 100644
index 0000000..9763999
--- /dev/null
+++ b/doc/lfun/Configure
@@ -0,0 +1,76 @@
+Configure()
+
+  public mixed Configure(mixed data)
+
+DEFINIERT IN:
+   Beliebigen Objekten
+
+ARGUMENTE:
+   <data>
+     0 fuer Datenabfrage
+     beliebige Daten, die ein Configure(0) vorher lieferte
+
+BESCHREIBUNG:
+   Viele Objekte werden fuer bestimmte Spieler konfiguriert und so
+   personalisiert. Sie enthalten zusaetzlich zu den Daten, welche waehrend des
+   create() gesetzt wurden, noch weitere spieler-individuelle Daten.
+   Damit diese Objekte im Bedarfsfall neu erstellt werden koennen, sollte ein
+   geeignetes Configure() definfiert werden, um diese Daten abzurufen und in
+   einem neuen Clone (oder neugeladener Blueprint) wieder zu setzen.
+   
+   Existiert Configure() im Objekt, MUSS dieses bei einem Aufruf mit
+   <data>==0 (d.h. Configure(0)) alle individuellen Daten zurueckliefern,
+   die noetig sind, um den aktuellen Zustand des Objektes spaeter
+   wiederherzustellen.
+   Das Objekt darf nach dem Zurueckgeben dieser Daten diese NICHT mehr
+   veraendern, d.h. es muss ggf. eine Kopie erstellt werden (copy, deep_copy)
+
+   Genau diese Daten werden in einem neu geclonten Objekt durch den Aufruf
+   von Configure(data) wieder gesetzt. Das Configure MUSS genau die Daten, die
+   es im alten Objekt zurueckliefert, wieder akzeptieren.
+
+RUeCKGABEWERT:
+   Wenn <data>==0:
+     Alle individuellen Daten in einer beliebigen Form. Ein Mapping bietet
+     sich jedoch an.
+     Nicht erlaubt sind: Objekte und Closures.
+
+   Wenn <data>!=0:
+     1, wenn die Daten zur Konfiguration akzeptiert wurden.
+     0, sonst.
+
+BEMERKUNGEN:
+   Configure() ist nicht verpflichtet, erneut Daten zu akzeptieren, wenn es
+   bereits einmal mit gueltigen Daten aufgerufen wurde.
+   Das Zurueckschreiben der Daten kann auch nach Ablauf laengerer Zeit
+   und/oder in einer anderen Uptime erfolgen.
+
+BEISPIELE:
+   Ein Objekt, welches ein anderes Objekt sicher ersetzt, d.h. die
+   individuelle Konfiguration bleibt erhalten:
+   mixed data;
+   if (call_resolved(data,ob,"Configure",0) == 1) {
+     string obname = load_name(ob);
+     ob->remove();
+     ob = clone_object(obname);
+     if (data) {
+       if (ob->Configure(data) == 1) {
+         printf("Instanz von %s erfolgreich ersetzt, neues Objekt: %O\n",
+             obname,ob);
+       }
+       else {
+         raise_error(sprintf("Configure() in %O akzeptierte Daten nicht!\n",
+             ob));
+       }
+     }
+   }
+   else {
+     printf(break_string(
+         "Das Objekt %O definiert keine Funktion Configure(). Es kann "
+         "nicht sicher ersetzt werden, da unbekannt ist, ob es individuelle "
+         "Daten enthaelt.",78));
+   }
+
+LETZTE AeNDERUNG:
+26.09.2011, Zesstra
+
diff --git a/doc/lfun/ConvMaterialList b/doc/lfun/ConvMaterialList
new file mode 100644
index 0000000..ab4618a
--- /dev/null
+++ b/doc/lfun/ConvMaterialList
@@ -0,0 +1,41 @@
+ConvMaterialList()
+FUNKTION:
+     varargs string ConvMaterialList(mixed mats, int casus, mixed idinf)
+
+DEFINIERT IN:
+     /p/daemon/materialdb.c (MATERIALDB)
+
+ARGUMENTE:
+     mixed mats:  - das zu erkennende Material:
+                    - ein Mapping, ein String oder ein Stringarray
+                      ([MAT_A:y,MAT_B:x,..]) oder MAT_A oder ({MAT_A,MAT_B,..})
+     int casus:   - der Fall: 0..3 -> <language.h>: WAS, WESSEN, WEM, WEN
+     mixed idinf  - Dinge, welche die Faehigkeiten des Erkennens beeinflussen
+		    (siehe "man MaterialList")
+
+BESCHREIBUNG:
+     Man uebergibt ConvMaterialList() eine Liste von Materialien, die die
+     Funktion unter Verwendung von MaterialName() in ihre Bestandteile
+     aufsplittet und mit "und" und "," verknuepft zurueckgibt.
+
+RUECKGABEWERT:
+     string - Materialien, durch Komma und "und" getrennt oder
+              "unbekanntes Material"
+
+BEMERKUNGEN:
+     Wird von /std/thing/description::MaterialList() gerufen.
+     Bitte an Objekten auch MaterialList() verwenden.
+     Ruft direkt MaterialName() auf.
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Methoden:    QueryMaterial(), QueryMaterialGroup(), MaterialList(),
+     Listen:	  AllMaterials(), AllGroups(), Dump()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+     Sonstiges:	  P_MATERIAL_KNOWLEDGE
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/CreateRingBuffer b/doc/lfun/CreateRingBuffer
new file mode 100644
index 0000000..0471af6
--- /dev/null
+++ b/doc/lfun/CreateRingBuffer
@@ -0,0 +1,45 @@
+CreateRingBuffer()
+
+FUNKTION:
+    protected struct std_ringbuffer CreateRingBuffer(int size, int newmode);
+
+DEFINIERT IN:
+    /std/util/ringbuffer.c
+    /sys/util/ringbuffer.h
+
+ARGUMENTE:
+    size    - Groesse des neuen Ringpuffers (int)
+    newmode - Ausgabemodus beim Abrufen des Puffers (int):
+              MODE_FIFO: First-in-First-Out
+              MODE_LIFO: Last-in-First-Out
+
+BESCHREIBUNG:
+    Diese Funktion erstellt einen neuen, leeren Ringpuffer der Groesse <size>
+    und liefert ihn zurueck. Die Daten des Puffers werden spaeter gemaess
+    <newmode> so gespeichert, dass bei der Ausgabe des Puffers mittels
+    RingBufferGet() die entweder die neuesten Daten zuerst (MODE_LIFO) oder
+    die aeltesten Daten zuerst (MODE_FIFO) geliefert werden.
+
+RUeCKGABEWERT:
+    Der neue Ringpuffer. Dieser wird in einer Struct std_ringbuffer
+    gespeichert. Er ist in einer Variable 'mixed' oder in einer mittels
+    'struct std_ringbuffer' angelegten Variable speicherbar.
+
+BEMERKUNGEN:
+    Der gelieferte Ringpuffer sollte nicht per Hand verarbeitet oder
+    genaendert werden, sondern nur ueber die Verwaltungsfunktionen aus
+    /std/util/ringbuffer.c.
+
+BEISPIELE:
+    // Variable anlegen:
+    struct std_ringbuffer buffer;
+    // _oder_: mixed buffer;
+    // neuen Puffer mit max. 50 Elementen anlegen, der bei der Abfrage die
+    // aeltesten Daten zuerst zurueckliefert:
+    buffer = CreateRingBuffer(50, MODE_FIFO);
+
+SIEHE AUCH:
+    RingBufferPut(), RingBufferGet(), ResizeRingBuffer()
+
+23.05.2008, Zesstra
+
diff --git a/doc/lfun/CustomizeObject b/doc/lfun/CustomizeObject
new file mode 100644
index 0000000..4b35092
--- /dev/null
+++ b/doc/lfun/CustomizeObject
@@ -0,0 +1,52 @@
+CustomizeObject()
+
+FUNKTION:
+   string CustomizeObject();
+
+DEFINIERT IN:
+   /std/virtual/v_compiler.c
+
+ARGUMENTE:
+   keine
+
+RUeCKGABEWERT:
+   Den Objektnamen, den das zuletzt erzeugte Objekt (welches gerade die
+   Funktion aufruft) spaeter vom Driver bekommen wird.
+
+BESCHREIBUNG:
+   Diese Funktion ist aus dem Grunde da, da zum Zeitpunkt des Clonens des
+   VC-Objektes (P_STD_OBJECT) dieses Objekt ja noch nicht weiss Wer
+   oder Was es spaeter mal sein wird.
+   Deshalb kann dieses VC-Objekt im create() (und nur da!) die Funktion
+   CustomizeObject() in dem virtual_compiler aufrufen, welches das Objekt
+   geclont hat und bekommt von diesem den Objektnamen zureck, welches es
+   spaeter mal bekommen wird.
+   Da das VC-Objekt vom VC geclont wurde, ist previous_object() im create()
+   des VC-Objektes der VC, in dem man CustomizeObject() ruft.
+
+BEMERKUNGEN:
+   Das CustomizeObject() im Standard-VC gibt nur den zukuenftigen Objektnamen
+   zurueck und macht sonst nix.
+
+BEISPIELE:
+   create() eines VC-Objektes:
+   
+   protected void create() {
+     ...
+     
+     // wer bin ich denn eigentlich?
+     string myname = previous_object()->CustomizeObject();
+     switch(myname) {
+       // Kram konfigurier, ja nach myname... 
+     }
+     
+     ...
+   }
+
+SIEHE AUCH:
+     virtual_compiler
+     CustomizeObject(), Validate(), NoParaObjects(), 
+     P_COMPILER_PATH, P_PARA
+     /std/virtual/v_compiler.c
+----------------------------------------------------------------------------
+21.10.2007, Zesstra
diff --git a/doc/lfun/Damage b/doc/lfun/Damage
new file mode 100644
index 0000000..3258531
--- /dev/null
+++ b/doc/lfun/Damage
@@ -0,0 +1,34 @@
+Damage()
+
+FUNKTION:
+     int Damage(int dam);
+
+DEFINIERT IN:
+     /std/armour/combat.c und
+     /std/weapon/combat.c
+
+ARGUMENTE:
+     dam  Der Wert, mit dem die Waffe/Ruestung beschaedig werden soll.
+
+BESCHREIBUNG:
+     P_WC bzw. P_AC wird um dam reduziert und P_DAMAGED wird um
+     dam erhoeht.
+     Bei dam>0 wird das Objekt beschaedigt, bei dam<0 repariert.
+     Dabei werden sowohl die Obergrenzen (s. /sys/combat.h) wie auch
+     die Untergrenzen (Waffen:30, Ruestungen: 0) fuer P_WC und P_AC
+     beachtet. Es kann auch nicht mehr repariert werden, als vorher
+     beschaedigt wurde.
+
+RUeCKGABEWERT:
+     Der Wert der Beschaedigung, die tatsaechlich vorgenommen wurde.
+
+BEMERKUNGEN:
+     Ist das Objekt in Benutzung, setzt die Funktion Damage automatisch
+     die Properties P_TOTAL_WC bzw. P_TOTAL_AC in dem benutzenden Spieler
+     auf die richtigen Werte.
+
+SIEHE AUCH:
+     /std/armour/combat.c, /std/weapon/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Thu May 22 10:13:23 1997 by Paracelsus
diff --git a/doc/lfun/DeAssocMember b/doc/lfun/DeAssocMember
new file mode 100644
index 0000000..5f35326
--- /dev/null
+++ b/doc/lfun/DeAssocMember
@@ -0,0 +1,48 @@
+
+DeAssocMember()
+
+
+FUNKTION:
+        int DeAssocMember(object npc)
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        Der NPC, der nicht mehr zugeordnet sein soll.
+
+BESCHREIBUNG:
+        Hebt die Zuordnung eines NPCs zu einem Spieler auf.
+
+RUECKGABEWERT:
+        1, falls Aufhebung erfolgreich, sonst 0.
+
+BEISPIEL:
+        void fun(object pl)
+        {
+         if ( pl && pl->DeAssocMember(this_object()) )
+          tell_object(pl,break_string(
+              "Ich kaempfe nun nicht mehr auf Deiner Seite!\n",78,
+              "Ein Feuerteufel teilt Dir mit:");
+         ...
+        }
+
+BEMERKUNGEN:
+        Kann nur von Gilden, Spellbooks, vom Objekt selber und vom
+        zugeordneten NPC aufgerufen werden.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/DeclAdj b/doc/lfun/DeclAdj
new file mode 100644
index 0000000..f6e4244
--- /dev/null
+++ b/doc/lfun/DeclAdj
@@ -0,0 +1,46 @@
+DeclAdj()
+
+FUNKTION:
+     varargs string DeclAdj( string adj, int casus, int demon);
+
+DEFINIERT IN:
+     /std/thing/language.c
+
+ARGUMENTE:
+     adj
+          Das zu deklinierende Adjektiv.
+
+     casus
+          Der Fall, in den es dekliniert werden soll.
+
+     demon
+          Bezieht sich das Adjektiv auf einen bestimmten oder einen
+          unbestimmten Gegenstand?
+
+BESCHREIBUNG:
+     Dekliniert das uebergebene Adjektiv in den angegebenen Fall. Ist demon
+     ungleich Null, so wird das Adjektiv so behandelt, als wuerde es sich
+     auf einen bestimmten Gegenstand beziehen, ansonsten bezieht es sich auf
+     einen unbestimmten Gegenstand.
+
+RUeCKGABEWERT:
+     Das deklinierte Adjektiv. Es wird zusaetzlich noch ein Leerzeichen
+     hinten angefuegt!
+
+BEISPIELE:
+     Zunaechst ein bestimmtes Adjektiv:
+
+     printf("Der %sBall.\n", ball->DeclAdj("gruen", WER, 1);
+
+     Nun ein unbestimmtes Adjektiv:
+
+     printf("Ein %sBall.\n", ball->DeclAdj("gruen", WER, 0);
+
+     Da DeclAdj() "gruene " bzw. "gruener " zurueckgibt, darf zwischen dem
+     "%s" und dem "Ball" kein Leerzeichen stehen!
+
+SIEHE AUCH:
+     /std/thing/language.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:18:05 1996 by Wargon
diff --git a/doc/lfun/Defend b/doc/lfun/Defend
new file mode 100644
index 0000000..e02264a
--- /dev/null
+++ b/doc/lfun/Defend
@@ -0,0 +1,150 @@
+Defend()
+FUNKTION:
+     public int Defend(int dam, string|string* dam_type, int|mapping spell, 
+                        object enemy)
+
+DEFINIERT IN:
+     /std/living/combat
+
+ARGUMENTE:
+     int dam                  initiale Staerke des Angriffs (10 dam ~ 1 HP)
+     string* dam_type         Art(en) des Schadens, der angerichtet werden
+                              soll
+                              Muss ein Array von Schadenstypen sein,
+                              alte Objekte uebergeben hier manchmal strings.
+     int/mapping spell        - 0 fuer normale Angriffe (keine Zauber)
+                              - 1 fuer Zauber (Standardruestungen ignorieren)
+                              - mapping fuer mehr Informationen
+                              Heute bitte nach Moeglichkeit ein Mapping
+                              uebergeben.
+     object enemy             der Feind/Schadenverursacher
+
+BESCHREIBUNG:
+     1. Generell
+     Wenn das Lebewesen angegriffen wird, wird geprueft, wie stark die
+     Ruestungen und koerpereigenen Abwehrkraefte sind und die Staerke des
+     Schadens dementsprechend vermindert.
+     Ggf. wird der Schaden zugefuegt und der Feind in  die Liste der Feinde
+     aufgenommen. Der Schaden betraegt:
+      (dam-Summe(Ruestungsstaerken)-random(P_BODY+A_DEX))*CheckResistance/10
+     aber nicht unter 0.
+
+     2. Der Parameter 'spell'
+     Ist 'spell' 0, dann gilt der Angriff als normale physische Attacke
+     Uebergibt man als 'spell'-Parameter ein Mapping, so gibt es dafuer
+     diverse Flags, die das Ergebnis manipulieren (in new_skills.h
+     enthalten). Nichtangabe eines Flags gilt als 0.
+
+     - SP_PHYSICAL_ATTACK ---------- 0/1
+                1, wenn Ruestungen wirken sollen, 0 sonst
+                -> entspricht !spell, wenn dieses Int ist
+     - SP_NO_ENEMY ----------------- 0/1
+                1, falls der Angriff nicht toedlich ist, es also keinen echten
+                Gegner gibt
+                -> es wird dann reduce_hit_points() gerufen statt do_damage()
+     - SP_NO_ACTIVE_DEFENSE -------- 0/1
+                1, falls aktive Abwehren (wie zurueckschlagende Amulette,
+                Karateabwehren oder Ausweichmanoever) unterbleiben sollen
+                -> zB bei Kratzen durch Dornen oder Fall aus grosser Hoehe
+                   ist aktive Abwehr oder Ausweichen unlogisch
+     - SP_RECURSIVE ---------------- 0/1
+                1, falls der Spell aus einem Defend gerufen wurde (oder einer
+                DefendFunc)
+                -> verhindert Rekursionsprobleme
+     - SP_NAME --------------------- string
+                Name des Spells
+     - SP_REDUCE_ARMOUR ------------ Mapping: keys AT_X/P_BODY, values int>=0
+                Die Schutzwirkung durch P_AC/Magie einer Ruestung wird
+                typabhaengig reduziert. Aufbau eines Mappings im Beispiel:
+                ([AT_BOOTS: 0,  // Stiefel schuetzen gar nicht
+                  P_BODY:  50,  // Koerper zu 50%
+                  AT_BELT: 600  // Guertel zu 600%
+                ])
+                -> alle 'fehlenden' Eintraege wirken normal
+     - SP_SHOW_DAMAGE -------------- 0/1 oder Array von Arrays
+                0, fuer keine Treffermeldung, 1 sonst
+                In einem Array koennen Ersatz-Treffermeldungen definiert
+                werden. Format ist:
+                ({
+                 ({ int lphit1, string mess_me,
+                                string mess_en,
+                                string mess_room }),
+                 ({ lphit2, mess_me, mess_en, mess_room }),
+                 ...
+                 ({ lphitn, mess_me, mess_en, mess_room }),
+                })
+                wobei lphit1<lphit2<...<lphitn sein muss, d.h. das Array-
+                Array aufsteigend sortiert.
+
+                Ist ein Treffer x LP hart, werden die Meldungen des lphit-
+                Arrays ausgegeben, dessen Wert am naehesten unter dem Schaden
+                liegt.
+
+                In den Meldungen mess_me (an den Getroffenen), mess_en (an
+                den Feind), mess_room (an die restlichen Umstehenden) koennen
+                Ersatzstrings wie folgt verwendet werden:
+                @WER1/@WESSEN1/@WEM1/@WEN1 - name(casus) des Getroffenen (TO)
+                @WER2/@WESSEN2/@WEM2/@WEN2 - name(casus) des Feindes (enemy)
+     - EINFO_DEFEND ------------ Mapping
+                Dieses Mapping liefert erweiterte Informationen zu dem
+                bisherigen Ablauf des aktiven Attacks.
+                Die verfuegbaren Informationen sind in der Manpage zu
+                DefendInfo festgehalten.
+
+     3. Reihenfolgen in Defend
+     - das Living wird angegriffen, wenn
+       - P_NO_ATTACK != 0
+       - 'enemy' existiert und kein netztoter Spieler ist
+     - P_DEFENDERS werden durchgegangen (und eventuell benachrichtigt)
+     - P_TMP_ATTACK_HOOK wird abgefragt
+     - die Ruestungen werden vom Schaden gegebenenfalls abgezogen
+     - magischer Ausweichskill beruecksichtigt
+     - sensitive Objekte werden ggf. benachrichtigt
+     - InternalModifyDefend wird gerufen
+     - Koerperabwehr abgezogen
+     - der Schaden an do_damage()/reduce_hit_points() uebergeben
+     - Flucht ueberpruefen mit CheckWimpyAndFlee()
+
+BEMERKUNGEN:
+     Ruestungen wirken konventionell nur, wenn mindestens ein Schadensanteil
+     mechanisch ist und es kein Spell oder ein Spell mit SP_PHYSICAL_ATTACK
+     auf 1 ist.
+
+     Defend() beruecksichtigt magische Verteidigungen, die der Spieler bei
+     sich hat, sollte also aus Fairness gegenueber den Objekten anderer
+     Magier immer dem direkten reduce_hit_points() oder do_damage()
+     vorgezogen werden. Mittels der Flags in 'spell' kann man sehr viel
+     aendern.
+
+RUECKGABEWERT:
+     Hoehe des tatsaechlichen Schadens. Dies kann mehr sein als die
+     Lebenspunkte des Lebewesens.
+
+BEISPIELE (SIEHE AUCH Defend_bsp):
+     // ein simpler Angriff:
+     enem->Defend(100, ({DT_BLUDGEON}), 0, this_object());
+
+     // ein magischer Angriff (ohne Treffermeldung):
+     enem->Defend(100, ({DT_BLUDGEON, DT_FIRE}), 1, this_object());
+
+     // ein magischer Angriff mit Treffermeldung:
+     enem->Defend(100, ({DT_BLUDGEON, DT_FIRE}), ([SP_SHOW_DAMAGE:1]),
+                         this_object());
+
+SIEHE AUCH:
+     Angriff:   Attack(L), P_NO_ATTACK, InsertEnemy(L)
+     Schaden:   P_ENABLE_IN_ATTACK_OUT, P_LAST_MOVE,
+                do_damage(L), reduce_hit_points(L)
+     Schutz:    P_DEFENDERS, InformDefend(L), DefendOther(L)
+                P_ARMOURS, P_AC, P_DEFEND_FUNC, QueryDefend(L)
+                P_BODY, A_DEX
+     Daten:     P_LAST_COMBAT_TIME
+                P_LAST_DAMTYPES, P_LAST_DAMTIME, P_LAST_DAMAGE
+                P_DAMAGE_MSG
+     Resistenz: P_RESISTANCE_STRENGTHS, CheckResistance(L)
+     Sonstiges: CheckSensitiveAttack(L)
+                InternalModifyDefend(L)
+                UseSkill(L),
+                DefendInfo
+
+15.09.2010, Zesstra
diff --git a/doc/lfun/DefendFunc b/doc/lfun/DefendFunc
new file mode 100644
index 0000000..7043e04
--- /dev/null
+++ b/doc/lfun/DefendFunc
@@ -0,0 +1,78 @@
+DefendFunc(L)
+
+FUNKTION:
+     int DefendFunc(string|string *dtyp, int|mappingspell, object enemy);
+
+DEFINIERT IN:
+     eigenen Objekten; fuer /std/armour/combat.c
+
+ARGUMENTE:
+     dtyp
+          Schadenstypen der Angriffsart.
+          Sollte heute ein string* sein.
+     spell
+          0 bei veralteten konventionellen Angriffen im Regelfall jedoch
+          ein Mapping mit weiteren Infos.
+          Bei einem konventionellen Angriff ist spell[SP_PHYSICAL_ATTACK] gleich
+          1.
+     enemy
+          Der angreifende Gegner
+
+BESCHREIBUNG:
+     Anhand der uebergebenen Parameter kann hier ein Ruestungsbonus (oder
+     auch ein Ruestungsmalus) errechnet werden, der zu dem normalen
+     Ruestungswert (abhaengig von der Angriffsart) hinzuaddiert wird.
+
+RUeCKGABEWERT:
+     Der Ruestungsbonus, der zur Ruestungsklasse addiert werden soll.
+
+BEMERKUNGEN:
+     Auch wenn man eine DefendFunc() benutzt, darf der Rueckgabewert
+     zusammen mit der P_AC insgesamt nur in sehr seltenen, wohldurch-
+     dachten Ausnahmefaellen die maximal zulaessige P_AC fuer diesen
+     Ruestungstyp ueberschreiten. In solchen Ausnahmefaellen duerfen
+     die DefendFuncs nicht konstant wirken.
+
+     Bei aktivem Zurueckschlagen IMMER auf Flags wie SP_RECURSIVE und
+     SP_NO_ACTIVE_DEFENSE pruefen und ggf. abbrechen.
+
+BEISPIELE:
+     Eine Ruestung, die bei Angriffen mit Feuer ihre volle Staerke entfaltet
+     und bei Angriffen durch Geister geschwaecht wird:
+
+     void create()
+     {
+       ::create();
+
+       SetProp(P_ARMOUR_TYPE, AT_ARMOUR);
+       SetProp(P_AC, 20);
+       ...
+       // Die DefendFunc() ist in der Ruestung selbst definiert
+       SetProp(P_DEFEND_FUNC, this_object());
+     }
+
+     int DefendFunc(string *dtyp, mixed spell, object enemy)
+     {
+       int prot;
+
+       // Zuerst fragen wir den Angriff durch Feuer ab:
+       if (member(dtyp, DT_FIRE) >= 0)  // Feuer gehoert zu den Schadenstypen
+         prot = 5 + random(10); // Das ergibt maximal 14. Zusammen mit P_AC
+                                // kommt man also maximal auf 14+20 = 34,
+                                // liegt also unter der fuer AT_ARMOUR
+                                // geltenden Obergrenze
+
+       // Und jetzt der Geistertest
+       if (enemy->QueryProp(P_RACE) == "Geist" ||
+           enemy->is_class_member(CL_GHOST))
+         prot -= random(10);
+
+       // Der Rueckgabewert wird auf den aus P_AC errechneten Wert draufgeschlagen
+       return prot;
+     }
+
+SIEHE AUCH:
+     P_DEFEND_FUNC, QueryDefend(), /std/armour/combat.c
+
+----------------------------------------------------------------------------
+Last modified: 18.Jul 2006 Muadib
diff --git a/doc/lfun/DefendInfo b/doc/lfun/DefendInfo
new file mode 100644
index 0000000..460352a
--- /dev/null
+++ b/doc/lfun/DefendInfo
@@ -0,0 +1,168 @@
+DefendInfo
+FUNKTION:
+
+DEFINIERT IN:
+     /sys/combat.h
+
+ARGUMENTE:
+
+BESCHREIBUNG:
+     Die DefendInformationen werden im Runde eines Attack/Defend Vorgangs 
+     in Attack initial angelegt und dem Defend ueber den Parameter spell
+     uebergeben. Ist dieser Parameter ein Mapping, so sind die 
+     DefendInformationen ueber den Key EINFO_DEFEND zu erhalten.
+     Die Informationen liegen in Form eines Mappings vor.
+     Vor Zugriff sollte immer auf die Existenz dieses Keys in dem Mapping
+     geprueft werden, da nicht alle Informationen immer existieren.
+     Die Keys sind in combat.h definiert und sind folgende:
+     
+    ORIGINAL_AINFO - Mapping 
+        Hier ist eine Kopie des originalen ainfo-mappings des Attacks 
+        vorhanden mit folgenden Eintraegen:
+        Immer vorhandene Eintraege:
+          SI_ENEMY              der angegriffene Gegner
+        
+        Angriff mit gezueckter Waffe:
+          P_WEAPON              das Waffenobjekt selbst
+          P_WEAPON_TYPE         P_WEAPON_TYPE der Waffe
+          P_WC                  P_WC der Waffe
+          P_NR_HANDS            P_NR_HANDS der Waffe
+          SI_SKILLDAMAGE_TYPE   P_DAM_TYPE der Waffe
+          SI_SKILLDAMAGE        waffe->QueryDamage(enemy)
+             bei vorhandenem Zweihandskill SK_TWOHANDED wird nur der durch 
+             den Skill modifizierte Schadenswert uebernommen
+          SI_SKILLDAMAGE_MSG    "mit "+waffe->name(WEM,0)
+          SI_SKILLDAMAGE_MSG2   "mit "+waffe->name(WEM,1)
+          SI_SPELL              0
+
+        Angriff mit blossen Haenden:
+          P_WEAPON_TYPE         WT_HANDS
+          P_WC                  P_HANDS[1]
+          SI_SKILLDAMAGE        Schadenswert, aus P_HANDS[1] und A_STR 
+                                berechnet
+          SI_SKILLDAMAGE_TYPE   P_HANDS[2]
+          SI_SKILLDAMAGE_MSG
+          SI_SKILLDAMAGE_MSG2   beides P_HANDS[0]
+          SI_SPELL              0
+          
+        Angriff mit einem Spell (SK_MAGIC_ATTACK):
+          SI_SKILLDAMAGE        Schadenswert des Spells
+          SI_SKILLDAMAGE_TYPE   Schadenstypen des Spells
+          SI_SKILLDAMAGE_MSG    Schadensmeldung des Spells, wenn vorhanden,
+                                sonst "mit magischen Faehigkeiten"
+          SI_SKILLDAMAGE_MSG2   entsprechende Meldung des Spells, wenn 
+                                gesetzt, ansonsten identisch mit 
+                                SI_SKILLDAMAGE_MSG
+          P_WEAPON_TYPE         P_WEAPON_TYPE des Spells, wenn vorhanden,
+                                sonst WT_MAGIC
+          SI_SPELL              SI_SPELL des Spells
+          
+        Hinweise:
+        - Alle Eintraege in diesem Mapping koennen bereits durch
+          InternalModifyAttack() veraendert worden sein.
+        - Die Daten werden mittels deep_copy(ainfo) eingetragen.
+        - Daten in den Eintraegen SI_SKILLDAMAGE* und SI_SPELL koennen bei
+          physikalischen Angriffen durch die Skills FIGHT(P_WEAPON_TYPE) oder
+          SK_FIGHT oder durch einen P_TMP_ATTACK_MOD bzw. Hook vom Typ 
+          H_HOOK_ATTACK_MOD modifiziert worden sein.
+        
+    ORIGINAL_DAM - int
+        Der Originalschaden aus dem Attack
+
+    ORIGINAL_DAM_TYPE - string/string* 
+        Der Originaldamagetyp aus dem Attack
+
+    CURRENT_DAM - int
+        Der momentane Schaden, nach schon erfolgten Modifikationen
+  
+    CURRENT_DAM_TYPE - string/string*
+        Der momentane Damagetyp, nach schon erfolgten Modifikationen
+  
+    ENEMY_INSERTED - int
+        0 oder 1 je nachdem ob der Angreifer schon in der enemy-list
+        vorhanden war oder nicht
+  
+    RFR_REDUCE - int 
+        0 oder reduzierter Schaden durch RFR Modifikation
+  
+    PRESENT_DEFENDERS - Array 
+        Liste der durch InformDefend informierten Defender als Objekt.
+        Ein Defender wird immer NACH InformDefend
+        dazugefuegt
+  
+    DEFENDING_DEFENDER - Array ({})
+        Hat ein durch InformDefend ein Defender verteidigt, so wird
+        fuer diesen Defender ein Eintrag in diesem Array vorgenommen,
+        welcher folgende Struktur besitzt.
+                ({
+                        DEF_DEFENDER - Object
+                          Der Verteidiger, welcher VOR
+                          DefendOther eingefuegt wird
+                        DEF_DAM - int
+                          Der veraenderte Schaden, welcher NACH 
+                          DefendOther eingefuegt wird
+                        DEF_DAMTYPE string/string*  
+                          Die veraenderte Schadensart, welche 
+                          NACH DefendOther eingefuegt wird
+                        DEF_SPELL - Mapping   
+                          Das Mapping des veraenderten Spells, welches
+                          als Kopie NACH DefendOther eingefuegt wird
+                })
+  
+    DEFEND_HOOK - Int/Array 
+        DI_NOHOOK, wenn kein Hook da war, DI_HOOKINTERRUPT, wenn der
+        Hook das Defend unterbricht, DI_HOOK, wenn ein Hook vorhanden 
+        ist, dieser das Defend aber unveraendert laesst.
+        Veraendert ein Hook das Defend, so ist hier ein Array zu finden
+        mit den veraenderten Parametern in der Struktur:
+                ({
+                        HOOK_DAM - int
+                           Der veraenderte Schaden
+                        HOOK_DAMTYPE - string/string*   
+                           Die veraenderte Schadensart
+                        HOOK_SPELL - Mapping 
+                           Das Mapping des veraenderten Spells als Kopie
+                })
+  
+    DEFEND_ARMOURS - Mapping (2 Werte pro Key)
+        Liste der beruecksichtigten Ruestungen. Fuer jede Ruestung
+        wird ein Eintrag vorgenommen, mit dem Objekt der jeweiligen
+        Ruestung als Key. Hierbei werden die Ruestungen erst eingetragen,
+        wenn ihr jeweiliges QueryDefend() aufgerufen wird, d.h. es sind nicht
+        von Anfang an alle getragenen Ruestung drin. Jeder Eintrag im Mapping
+        besitzt die folgenden 2 Werte:
+                DEF_ARMOUR_DAM - int 
+                    Der Schaden NACH QueryDefend (vorher 0)
+                DEF_ARMOUR_PROT - int
+                    Verteidigungswert der Ruestung VOR DefendFunc
+        Bsp: Ich will wissen, wie gut 'ruestung' schuetzte:
+             spell[EINFO_DEFEND][DEFEND_ARMOURS][ruestung,DEF_ARMOUR_PROT]
+
+    DEFEND_GUILD - Array 
+        Eine Liste mit der Modifikation der Gilde mit der Struktur
+                ({
+                        GUILD_DAM - int
+                          Der veraenderte Schaden
+                        GUILD_DAMTYPE - string/string*
+                          Die veraenderte Schadensart
+                })
+  
+    DEFEND_RESI - int
+        Schaden nach CheckResistance
+  
+    DEFEND_BODY - int
+        Schaden nach Beruecksichtigung des Bodies (nur
+        physikalisch)
+  
+    DEFEND_LOSTLP - int
+        Tatsaechlich abgezogene LP
+  
+    DEFEND_CUR_ARMOUR_PROT - int
+        Schutz der Ruestung vor Call der
+        DefendFunc. Ist nur in der DefendFunc definiert. Kann auch aus
+        DEFEND_ARMOURS entnommen werden
+     
+SIEHE AUCH:
+     Attack, Defend
+
+18.Jul 2006 Muadib
diff --git a/doc/lfun/DefendOther b/doc/lfun/DefendOther
new file mode 100644
index 0000000..a0b2277
--- /dev/null
+++ b/doc/lfun/DefendOther
@@ -0,0 +1,85 @@
+DefendOther()
+
+FUNKTION:
+	mixed DefendOther(int dam,mixed dam_type,mixed spell,object enemy);
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	dam
+	  Der Schaden, der voraussichtlich beim zu verteidigenden Lebewesen
+	  verursacht werden soll.
+	dam_type
+	  Der Schadenstyp (oder die Schadenstypen), der beim zu
+	  verteidigenden Lebewesen verursacht werden sollen.
+	spell
+	  Wenn das zu verteidigende Lebewesen mit Spells angegriffen wurde,
+	  so koennte man hier mehr Infos entnehmen.
+	enemy
+	  Der Feind, der ein zu verteidigendes Lebewesen angegriffen hat.
+
+RUeCKGABEWERT:
+	Array mit den Eintraegen der gegebenenfalls veraenderten
+	uebergebenen Parameter: 
+            (1) dam      [Typ int], 
+            (2) dam_type [Typ string*], 
+            (3) spell    [Typ mapping].
+
+BESCHREIBUNG:
+	Es ist moeglich, dass Objekte Angriffe auf Lebewesen abwehren oder
+	umwandeln, sofern diese Objekte bei dem angegriffenen Lebewesen
+	mittels AddDefender() angemeldet wurden und sich der selben Umgebung
+	befinden.
+	Zumeist wird es sich bei den Objekten natuerlich ebenfalls um
+	andere Lebewesen handeln, die das Lebewesen, bei dem sie angemeldet
+	sind, verteidigen sollen.
+	Bei einem Angriff auf das Lebewesen koennen alle Objekte per Aufruf
+	von DefendOther() in einen Angriff eingreifen, wobei die
+	Schadensstaerke, der Schadenstyp (die Schadenstypen),
+	Zusatzinformationen fuer Angriffsspells und der Angreifer als
+	Parameter uebergeben werden.
+	Desweiteren ist zu beachten, dass bei als physikalisch markierten
+	Angriffen in einem Team nur Verteidiger aus der ersten Reihe
+	beruecksichtigt werden und dass bei einem Angriff zufaellig aus
+	allen moeglichen Verteidigern ausgewaehlt wird.
+	Standardmaessig ist diese Funktion in Lebewesen bereits definiert,
+	wobei der Skill SK_DEFEND_OTHER, sofern vorhanden, aufgerufen wird.
+
+BEISPIEL:
+	Sehr beliebt sind in Gilden NPCs, die den Beschwoerer begleiten und
+	verteidigen, z.B. beschworene Daemonen:
+	  inherit "std/npc";
+	  include <properties.h>
+	  object owner;
+	  void create()
+	  { ::create();
+	    SetProp(P_NAME,"Daemon");
+	    ...
+	  }
+	// nach Clonen des Daemons folgende Funktion mit Beschwoerer als
+	// Parameter aufrufen
+	  Identify(object caster)
+	  { if(!objectp(caster))
+	      call_out(#'remove,0);
+	    owner=caster;
+	    owner->AddDefender(this_object());
+	  }
+	// der Daemon wehrt jeden Angriff mit Feuer voll ab, man muss zuerst
+	// den Verteidiger umbringen, um den Beschwoerer toeten zu koennen
+	  mixed DefendOther(int dam,mixed dam_type,mixed spell,object enemy)
+	  { if(sizeof(dam_type)&&member_array(DT_FIRE,dam_type)!=-1)
+	      dam=0;
+	    return({dam,dam_type,spell});
+	  }
+	Soll der Daemon sich auch in ein Team einordnen, in welchem sich der
+	Beschwoerer eventuell befindet, so ist zusaetzlich AssocMember() in
+	diesem Beschwoerer aufzurufen, wobei der Daemon als Parameter
+	uebergeben wird.
+
+SIEHE AUCH:
+	AddDefender(), RemoveDefender(), InformDefend(), Kill(), IsEnemy(),
+	P_DEFENDERS, /std/living/combat.c, /sys/new_skills.h
+
+----------------------------------------------------------------------------
+Last modified: Fri Feb 25 14:45:00 2000 by Paracelsus
diff --git a/doc/lfun/Defend_bsp b/doc/lfun/Defend_bsp
new file mode 100644
index 0000000..06458a5
--- /dev/null
+++ b/doc/lfun/Defend_bsp
@@ -0,0 +1,137 @@
+Defend() - BEISPIELE
+
+FUNKTION:
+     varargs int Defend(int dam, mixed dam_type, mixed spell, object enemy)
+
+BEMERKUNGEN:
+     Die hier aufgefuehrten Komplexbeispiele sind zum Verstaendnis gedacht.
+
+BEISPIELE:
+  1) Ein ordinaerer Biss ins Bein.
+     this_player()->Defend(random(500),
+                           ({DT_PIERCE, DT_RIP}),
+                           0,
+                           this_object());
+
+  2) Ein Biss ins Bein, mit der Hose als 200%ige Ruestung und Rest mit 100%.
+     this_player()->Defend(random(500),
+                           ({DT_PIERCE, DT_RIP}),
+                           ([SP_PHYSICAL_ATTACK: 1,
+                             SP_REDUCE_ARMOUR:   ([AT_TROUSERS: 200])
+                           ]),
+                           this_object());
+
+  3) Der Biss, wenn ein Tier in die Hose gekrochen ist und dieser ohne
+     Treffermeldung und physischen Ruestungsschutz durchgeht.
+     this_player()->Defend(random(500),
+                           ({DT_PIERCE, DT_RIP}),
+                           ([SP_PHYSICAL_ATTACK: 0]),
+                           this_object());
+
+  4) Spell-Parameter
+     // Beispiel fuer einen Spell, der nur vom Helm normal und von einem
+     // Amulett mit 115% aufgehalten wird, alle anderen (angebenenen)
+     // Ruestungen haben 0% Schutzwirkung.
+     // Mit Ausgabe eigener Meldungen: beginnend mit -1, da der verursachte
+     // Schadenswert minimal 0 wird (fuers Ersetzen: Feind: @WEx2,
+     // Spieler: WEx1); maximal wird er (Empfindlichkeiten jetzt mal aussen
+     // vor) 49 (499/10 = 49), nicht 499!!!
+     this_player()->Defend(
+       random(500),
+       ({DT_PIERCE, DT_AIR}),
+       ([SP_PHYSICAL_ATTACK: 1, // wegen DT_PIERCE
+         SP_REDUCE_ARMOUR:   ([AT_ARMOUR:   0,
+                               AT_HELMET: 100,
+                               AT_RING:     0,
+                               AT_GLOVE:    0,
+                               AT_CLOAK:    0,
+                               AT_BOOT:     0,
+                               AT_TROUSERS: 0,
+                               AT_SHIELD:   0,
+                               AT_AMULET: 115,
+                               AT_MISC:     0,
+                               AT_BELT:     0,
+                               AT_QUIVER:   0])
+         SP_SHOW_DAMAGE:
+               ({({-1,"@WER2 schrammt Dich mit einem durchbohrenden Blick.",
+                      "Du schrammst @WEN1 mit einem durchbohrenden Blick.",
+                      "@WER2 schrammt @WEN1 mit einem durchbohrenden Blick."
+                 }),
+                 ({5,"Der durchbohrende Blick von @WEM2 trifft Dich.",
+                     "Dein durchbohrender Blick trifft @WEN1.",
+                     "Der durchbohrende Blick von @WEM2 trifft @WEN1."
+                 }),
+                 ({20,"@WESSEN2 stechender Blick durchbohrt Dich.",
+                      "Dein stechender Blick durchbohrt @WEN1.",
+                      "@WESSEN2 stechender Blick durchbohrt @WEN1."
+               })})
+       ]),
+       this_object());
+
+     // Etwas geschickter geht das Ganze, wenn wir einfach aus der Mudlib
+     // alle existierenden Ruestungen in ein Mapping packen und diese
+     // nullen (damit sind wir auch gegen neue Ruestungstypen sicher):
+     mapping amap = map_indices(VALID_ARMOUR_CLASS,#'!);
+     amap[AT_HELMET]=100;
+     amap[AT_AMULET]=115;
+
+     this_player()->Defend(random(500),
+                           ({DT_PIERCE, DT_AIR}),
+                           ([SP_PHYSICAL_ATTACK: 1,
+                             SP_REDUCE_ARMOUR: amap,
+                             SP_SHOW_DAMAGE: ({ ... (siehe oben)
+
+  5) Der Biss von weiter oben mit Meldung.
+     // Eine Meldung, die nur ausgegeben wird, wenn der Biss auch mindestens
+     // einen LP abzieht.
+     this_player()->Defend(random(500),
+                           ({DT_PIERCE, DT_RIP}),
+                           ([SP_PHYSICAL_ATTACK: 1,
+                             SP_REDUCE_ARMOUR:   ([AT_TROUSERS: 200]),
+                             SP_SHOW_DAMAGE: ({
+                               ({1,"@WER2 beisst Dich ins Bein!",
+                                   "Du beisst @WEN1 ins Bein!",
+                                   "@WER2 beisst @WEN1 ins Bein!"
+                                })           })
+                           ]),
+                           this_object());
+
+  6) DefendFunc() und Defend() in einem Objekt
+     6a)
+     // eine Luftangriffe reflektierende Ruestung:
+     int DefendFunc(string *dtyp, mixed spell, object enemy) {
+       if(member(dtyp, DT_AIR)>=0 && !spell[SP_RECURSIVE])
+         enemy->Defend(random(200),
+                       ({DT_AIR}),
+                       ([SP_RECURSIVE: 1,
+                         SP_SHOW_DAMAGE:
+                         ({"Ein Luftwirbel erfasst auch Dich.",
+                           "Deine Ruestung wirbelt @WEN1 herum.",
+                           "@WESSEN2 Ruestung wirbelt @WEN1 herum."
+                          })
+                       ]),
+                       QueryProp(P_WORN));
+
+       return 0; // -> In diesem Fall gibts keinen Ruestungsbonus!
+     }
+
+     6b)
+     // Eine NUR REINE Luftangriffe reflektierende Ruestung:
+     int DefendFunc(string *dtyp, mixed spell, object enemy) {
+       if(!sizeof(dtyp-({DT_AIR})) && !spell[SP_RECURSIVE])
+         ...
+
+SIEHE AUCH:
+     Angriff:   Attack(L), P_NO_ATTACK, InsertEnemy(L)
+     Schaden:   P_ENABLE_IN_ATTACK_OUT, P_LAST_MOVE, do_damage(L),
+                reduce_hit_points(L), reduce_spell_points(L)
+     Schutz:    P_DEFENDERS, InformDefend(L), DefendOther(L),
+                P_ARMOURS, P_AC, P_DEFEND_FUNC, QueryDefend(L),
+                P_BODY, A_DEX, Defend(L)
+     Daten:     P_LAST_COMBAT_TIME, P_LAST_XP, P_LAST_DAMAGE,
+                P_LAST_DAMTYPES, P_LAST_DAMTIME
+     Resistenz: P_RESISTANCE_STRENGTHS, CheckResistance(L)
+     Sonstiges: CheckSensitiveAttack(L), UseSkill(L),
+                InternalModifyDefend(L)
+
+25. Mai 2011 Gabylon
diff --git a/doc/lfun/DeleteSpellFatigue b/doc/lfun/DeleteSpellFatigue
new file mode 100644
index 0000000..c0049d9
--- /dev/null
+++ b/doc/lfun/DeleteSpellFatigue
@@ -0,0 +1,37 @@
+DeleteSpellFatigue
+
+FUNKTION:
+    public void DeleteSpellFatigue(string key)
+
+DEFINIERT IN:
+    /std/living/skills.c
+    /std/player/skills.c
+    /sys/living/skills.h
+
+ARGUMENTE:
+    string key  : Eindeutiger Name des Spruches, einer Gruppe von Spruechen
+                  oder 0 fuer die globale Spruchermuedung.
+
+BESCHREIBUNG:
+    Diese Funktion dient zum Loeschen von individuellen Spruchermuedungen
+    (Spellfatigue, Spruchsperren).
+
+    Ist <key> 0, wird die globale Spruchsperre geloescht (identisch zu der
+    Property P_NEXT_SPELL_TIME), anderenfalls die unter <key> gespeicherte
+    Spruchermuedung.
+    Loescht man einen Eintrag 0 ist das Ergebnis dieser Funktion identisch zum
+    Loeschen/Nullen von P_NEXT_SPELL_TIME.
+
+BEMERKUNGEN:
+    Spruchsperren (insb. fremde) duerfen nicht ohne weiteres geloescht oder
+    geaendert werden. Dieses bedarf grundsaetzlich der Genehmigung durch die
+    Gildenbalance!
+
+SIEHE AUCH:
+    SetSpellFatigue(L), CheckSpellFatigue(L)
+    P_NEXT_SPELL_TIME
+    spruchermuedung
+
+----------------------------------------------------------------------------
+27.03.2010, Zesstra
+
diff --git a/doc/lfun/DeleteTimedAttrModifier b/doc/lfun/DeleteTimedAttrModifier
new file mode 100644
index 0000000..e51b6c9
--- /dev/null
+++ b/doc/lfun/DeleteTimedAttrModifier
@@ -0,0 +1,30 @@
+DeleteTimedAttrModifier()
+FUNKTION:
+     int DeleteTimedAttrModifier(string key)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+
+ARGUMENTE:
+     key	-	aus P_TIMED_ATTR_MOD zu loeschender Eintrag
+
+BESCHREIBUNG:
+     Der zu key gehoerende Eintrag in P_TIMED_ATTR_MOD wird geloescht und
+     update_max_sp_and_hp ausgeführt.

+

+RUeCKGABEWERT:

+     TATTR_INVALID_ARGS      -     Im Falle eines fehlenden key-Arguments 
+     TATTR_NO_SUCH_MODIFIER  -     Falls der Modifier mit diesem Key nicht
+                                   existiert

+     TATTR_OK                -     Im Erfolgsfall

+     

+     Die Rueckgabewerte sind in /sys/living/attributes.h definiert.

+    
+SIEHE AUCH:

+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+	SetTimedAttrModifier(), QueryTimedAttrModifier(), 

+	P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_TIMED_ATTR_MOD,

+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+----------------------------------------------------------------------------

+Last modified: Tue Jul 27 20:00:20 2004 by Muadib

diff --git a/doc/lfun/DiscoverDoor b/doc/lfun/DiscoverDoor
new file mode 100644
index 0000000..88310d5
--- /dev/null
+++ b/doc/lfun/DiscoverDoor
@@ -0,0 +1,31 @@
+DiscoverDoor()
+
+FUNKTION:
+    varargs int DiscoverDoor(string dname)
+
+ARGUMENTE:
+    dname: Name des Raumes, in dem der Seher das Sehertor kennenlernen soll.
+           Default: Die Umgebung des Sehers.
+
+BESCHREIBUNG:
+    Nachdem diese Funktion aufgerufen wurde, kann der Seher (this_player())
+    das Tor in dem angegebenen Raum immer verwenden.
+
+RUECKGABEWERT:
+    1, falls der Seher ein NEUES Tor kennengelernt hat
+    0, falls er das Tor schon kannte oder kein Seher war
+
+BEMERKUNGEN:
+    Von einem Sehertor wird diese Funktion automatisch beim Betreten des
+    umgebenden Raumes aufgerufen, falls P_SEERDOOR_DISCOVER gesetzt ist. Wenn
+    ein Tor auf diese Art nicht entdeckt werden soll, so darf
+    P_SEERDOOR_DISCOVER nicht gesetzt sein und muss DiscoverDoor() separat,
+    z.B. von einem Questobjekt, aufgerufen werden.
+    Diese Funktion wird von /d/seher/portale/sehertormaster definiert.
+
+BEISPIELE:
+    write("Der Zauberer sagt: Im Nichts wirst Du ein weiteres Tor finden!\n");
+    "/d/seher/portale/sehertormaster"->DiscoverDoor("/room/void");
+
+SIEHE AUCH:
+    DoorIsKnown, ShowDoors, Teleport, GetDoorsMapping
diff --git a/doc/lfun/DistributeExp b/doc/lfun/DistributeExp
new file mode 100644
index 0000000..f02f69a
--- /dev/null
+++ b/doc/lfun/DistributeExp
@@ -0,0 +1,24 @@
+DistributeExp()

+FUNKTION:

+     private static void DistributeExp(object enemy, int exp_to_give)

+

+DEFINIERT IN:

+     /std/living/life.c

+

+ARGUMENTE:

+     object enemy     - toetender Feind

+     int exp_to_give  - zu verteilende XP (== P_XP/100)

+

+BESCHREIBUNG:

+     Das sterbende Wesen verteilt seine XP an seine Feinde.

+

+     Dabei bekommt jeder Gegner seinen Anteil (abhängig von 50% von seinem

+     gemachten Schaden) und einen Teamanteil (die anderen 50% über das

+     gesamte Team addiert und durch die Teamanzahl geteilt).

+

+SIEHE AUCH:

+     Funktionen:  AddExp()

+     Properties:  P_XP

+     Sonstiges:   teamkampf

+

+14.Feb 2007 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/DoDecay b/doc/lfun/DoDecay
new file mode 100644
index 0000000..85cf541
--- /dev/null
+++ b/doc/lfun/DoDecay
@@ -0,0 +1,44 @@
+DoDecay()
+
+FUNKTION:
+      public  int    DoDecay(int silent) 
+
+DEFINIERT IN:
+      /std/unit.c
+
+ARGUMENTE:
+     silent (int)
+       Falls != 0, erfolgt beim Zerfall keine Meldung, d.h. doDecayMessaage()
+       wird nicht gerufen.
+
+RUeCKGABEWERT:
+     Die Funktion gibt die nach dem Zerfall noch uebrig gebliebene Menge
+     zurueck (int).
+
+BESCHREIBUNG:
+     Diese Funktion wird in Clones von Unitobjekten aus der Blueprint gerufen,
+     wenn ein Zerfallsintervall abgelaufen ist (natuerlich nur, wenn in der BP
+     der Zerfall konfiguriert ist).
+     Die Funktion prueft normalerweise via P_UNIT_DECAY_FLAGS, ob der Zerfall
+     stattfinden soll, bestimmt aus P_UNIT_DECAY_QUOTA die zu zerfallende
+     Menge, ruft DoDecayMessage() und reduziert P_AMOUNT.
+     
+     Sie kann auch von Hand gerufen werden, um einen Zerfall auszuloesen, auch
+     wenn mir gerade nicht einfaellt, in welchen Situationen das sinnvoll
+     waere (vielleicht als Spruchmisserfolg. *g*)
+
+BEMERKUNGEN:
+     Wenn man einen anderen Zerfallsmechanismus haben, will muss man diese
+     Funktion wohl ueberschreiben. In fast allen Faellen sollte dies jedoch
+     unnoetig sein. Hat jemand das Verlangen, diese Funktion zu
+     ueberschreiben, ist vielleicht vorher eine Diskussion mit dem Mudlib-EM
+     angebracht.
+
+SIEHE AUCH:
+     unit
+     P_UNIT_DECAY_INTERVAL, P_UNIT_DECAY_FLAGS, P_UNIT_DECAY_QUOTA,
+     P_UNIT_DECAY_MIN
+     DoDecayMessage()
+     /std/unit.c
+
+14.10.2007, Zesstra
diff --git a/doc/lfun/DoDecayMessage b/doc/lfun/DoDecayMessage
new file mode 100644
index 0000000..fc39309
--- /dev/null
+++ b/doc/lfun/DoDecayMessage
@@ -0,0 +1,34 @@
+DoDecayMessage()
+
+FUNKTION:
+      protected void DoDecayMessage(int oldamount, int zerfall);
+      
+DEFINIERT IN:
+      /std/unit.c
+
+ARGUMENTE:
+     oldamount (int)
+        Menge vor dem Zerfall
+     zerfall (int)
+        jetzt zerfallende Menge
+
+BESCHREIBUNG:
+     Diese Funktion wird von DoDecay() gerufen und gibt die Standardmeldungen
+     beim Zerfall von Unitobjekten aus.
+     Hierbei ist an der Unit noch alles unveraendert, wenn diese Funktion
+     gerufen wird, die Reduktion von P_AMOUNT erfolgt direkt im Anschluss.
+     Die Funktion wird nicht gerufen, wenn DoDecay() mit silent!=0 gerufen
+     wird.
+
+BEMERKUNGEN:
+     Will man nicht die Standardzerfallsmeldungen (wovon ich meist ausgehe),
+     kann man diese Funktion ueberschreiben und eigene Meldungen erzeugen.
+
+SIEHE AUCH:
+     unit
+     P_UNIT_DECAY_INTERVAL, P_UNIT_DECAY_FLAGS, P_UNIT_DECAY_QUOTA,
+     P_UNIT_DECAY_MIN
+     DoDecay()
+     /std/unit.c
+
+14.10.2007, Zesstra
diff --git a/doc/lfun/DoUnwear b/doc/lfun/DoUnwear
new file mode 100644
index 0000000..638eda7
--- /dev/null
+++ b/doc/lfun/DoUnwear
@@ -0,0 +1,33 @@
+DoUnwear()
+
+FUNKTION:
+     varargs int DoUnwear(int silent, int all);
+
+DEFINIERT IN:
+     /std/clothing/wear.c
+
+ARGUMENTE:
+     silent
+          Falls ungleich 0, so werden keine Meldungen ausgegeben.
+          Falls (silent&M_NOCHECK) werden auch verfluchte Ruestungen
+          ausgezogen
+     all
+          Ungleich 0, wenn DoUnwear() aus einem "ziehe alles aus" heraus
+          aufgerufen wurde.
+
+BESCHREIBUNG:
+     Es wird versucht, die Ruestung auszuziehen. Dabei werden eine eventuell
+     vorhandene RemoveFunc() und Flueche mit beruecksichtigt.
+
+RUeCKGABEWERT:
+     0, wenn die Ruestung gar nicht getragen war, ansonsten 1.
+
+BEMERKUNGEN:
+     Auch wenn eine 1 zurueckgegeben wird, muss das nicht heissen, dass die
+     Ruestung erfolgreich ausgezogen wurde!
+
+SIEHE AUCH:
+     DoWear(), RemoveFunc(), InformUnwear(), /std/armour/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Sun Jun 27 22:22:00 1999 by Paracelsus
diff --git a/doc/lfun/DoUnwield b/doc/lfun/DoUnwield
new file mode 100644
index 0000000..be27b2c
--- /dev/null
+++ b/doc/lfun/DoUnwield
@@ -0,0 +1,30 @@
+DoUnwield()
+
+FUNKTION:
+     varargs int DoUnwield(int silent);
+
+DEFINIERT IN:
+     /std/weapon/combat.c
+
+ARGUMENTE:
+     silent
+          Ungleich 0, wenn die Waffe ohne Meldungen weggesteckt werden soll.
+
+BESCHREIBUNG:
+     Es wird versucht, die Waffe wegzustecken.
+
+RUeCKGABEWERT:
+     0, wenn die Waffe gar nicht gezueckt war, ansonsten 1.
+
+BEMERKUNGEN:
+     Anhand des Rueckgabewertes laesst sich nicht ersehen, ob die Waffe sich
+     wegstecken liess oder nicht!
+
+     Wenn die Waffe verflucht ist oder (falls definiert) UnwieldFunc() 0
+     zurueckgibt, laesst sie sich nicht wegstecken.
+
+SIEHE AUCH:
+     UnwieldFunc(), InformUnwield(), /std/weapon.c
+
+----------------------------------------------------------------------------
+Last modified: Sun Jun 27 22:24:00 1999 by Paracelsus
diff --git a/doc/lfun/DoWear b/doc/lfun/DoWear
new file mode 100644
index 0000000..0d64e5a
--- /dev/null
+++ b/doc/lfun/DoWear
@@ -0,0 +1,42 @@
+DoWear()
+
+FUNKTION:
+     varargs int DoWear(int silent, int all);
+
+DEFINIERT IN:
+     /std/armour/combat.c
+
+ARGUMENTE:
+     silent
+          Falls ungleich 0, so werden keine Meldungen ausgegeben.
+     all
+          Ungleich 0, wenn DoWear() aus einem "ziehe alles an" heraus
+          aufgerufen wurde.
+
+BESCHREIBUNG:
+     Es wird versucht, die Ruestung anzuziehen. Dabei wird eine eventuell
+     vorhandene WearFunc() mit beruecksichtigt.
+
+RUeCKGABEWERT:
+     0, wenn man die Ruestung gar nicht bei sich hat oder sie schon an hat,
+     ansonsten 1.
+
+BEMERKUNGEN:
+     Auch wenn eine 1 zurueckgegeben wird, muss das nicht heissen, dass die
+     Ruestung erfolgreich angezogen wurde!
+
+     Gruende fuer ein Fehlschlagen des Anziehens koennen sein:
+        o Man hat die Ruestung nicht bei sich.
+        o Man hat die Ruestung schon an.
+        o Man hat schon eine Ruestung des gleichen Typs an.
+        o Der Typ der Ruestung oder die Ruestungsklasse ist illegal.
+        o Falls definiert: WearFunc() gab 0 zurueck.
+        o Falls es sich um einen Schild handelt: Man hat keine Hand mehr
+          frei.
+
+SIEHE AUCH:
+     DoUnwear(), WearFunc(), InformWear(), P_EQUIP_TIME, 
+     /std/armour/combat.c, P_UNWEAR_MSG, P_WEAR_MSG
+
+----------------------------------------------------------------------------
+Last modified: Sun Jun 27 22:22:00 1999 by Paracelsus
diff --git a/doc/lfun/DoWield b/doc/lfun/DoWield
new file mode 100644
index 0000000..9fc436d
--- /dev/null
+++ b/doc/lfun/DoWield
@@ -0,0 +1,41 @@
+DoWield()
+
+FUNKTION:
+     varargs int DoWield(int silent);
+
+DEFINIERT IN:
+     /std/weapon/combat.c
+
+ARGUMENTE:
+     silent
+          Ungleich 0, wenn die Waffe ohne Meldungen gezueckt werden soll.
+
+BESCHREIBUNG:
+     Es wird versucht, die Waffe zu zuecken. Hat man schon eine Waffe
+     gezueckt, so wird versucht, diese wegzustecken. Klappt das nicht, kann
+     die Waffe nicht gezueckt werden.
+
+RUeCKGABEWERT:
+     0, wenn man die Waffe gar nicht bei sich traegt, ansonsten 1.
+
+BEMERKUNGEN:
+     Anhand des Rueckgabewertes laesst sich nicht entscheiden, ob die Waffe
+     sich erfolgreich zuecken liess!
+
+     Gruende, warum sich eine Waffe nicht zuecken lassen kann, sind
+     folgende:
+        o Man traegt sie nicht bei sich (oder sie steckt in einem Beutel
+          o.ae.).
+        o Man hat sie schon gezueckt.
+        o Falls definiert: WieldFunc() gibt 0 zurueck.
+        o Man ist nicht geschickt genug (das haengt von der Waffenklasse
+          ab).
+        o Eine schon gezueckte Waffe laesst sich nicht wegstecken.
+        o Die Waffenklasse ist hoeher als erlaubt.
+        o Man hat nicht genug Haende frei.
+
+SIEHE AUCH:
+     WieldFunc(), InformWield(), P_EQUIP_TIME, /std/weapon.c
+
+----------------------------------------------------------------------------
+Last modified: Wed Apr 08 10:25:00 2004 by Muadib
diff --git a/doc/lfun/DoorIsKnown b/doc/lfun/DoorIsKnown
new file mode 100644
index 0000000..b909e30
--- /dev/null
+++ b/doc/lfun/DoorIsKnown
@@ -0,0 +1,24 @@
+DoorIsKnown()
+
+FUNKTION:
+	int DoorIsKnown()
+
+ARGUMENTE:
+	Keine.
+
+BESCHREIBUNG:
+	Testet, ob der Seher (this_player()) das Tor in seiner momentanen
+	Umgebung schon kennt.
+
+RUECKGABEWERT:
+	Die Nummer des Tores.
+
+BEMERKUNGEN:
+    Diese Funktion wird von /d/seher/portale/sehertormaster definiert.
+
+BEISPIELE:
+    /d/seher/portale/sehertormaster->DoorIsKnown()
+
+SIEHE AUCH:
+    DiscoverDoor, ShowDoors, Teleport, GetDoorsMapping
+
diff --git a/doc/lfun/Dump b/doc/lfun/Dump
new file mode 100644
index 0000000..274fbb4
--- /dev/null
+++ b/doc/lfun/Dump
@@ -0,0 +1,23 @@
+Dump()
+FUNKTION:
+     void Dump()
+
+DEFINIERT IN:
+     /p/daemon/materialdb.c (MATERIALDB)
+
+BESCHREIBUNG:
+     Schreibt alle Materialien samt ihren Gruppen und alle Gruppen mit
+     dazugehoerenden Materialien in DUMPFILE (/p/daemon/save/MATERIALS)
+     Wird in create() der Materialiendatenbank automatisch aufgerufen.
+     Das Dumpfile ist zum Recherchieren der Materialien gedacht.
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Listen:	  AllMaterials(), AllGroups()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/EnemyPresent b/doc/lfun/EnemyPresent
new file mode 100644
index 0000000..365ae3d
--- /dev/null
+++ b/doc/lfun/EnemyPresent
@@ -0,0 +1,19 @@
+EnemyPresent
+FUNKTION:
+     public mixed EnemyPresent()
+
+DEFINIERT IN:
+     /std/living/combat.c
+
+BESCHREIBUNG:
+     Gibt aus der Feindesliste den ersten anwesenden, lebenden und 
+     angreifbaren aktuellen Gegner im Raum zurueck.
+     Damit ist die Funktion identisch zu InFight().
+
+     Will man alle Gegner, auf die diese Kriterien zutreffen, sollte man
+     PresentEnemies() verwenden.
+
+SIEHE AUCH:
+     PresentEnemies(), Infight()
+
+22.03.2009, Zesstra
diff --git a/doc/lfun/Enter b/doc/lfun/Enter
new file mode 100644
index 0000000..7987efa
--- /dev/null
+++ b/doc/lfun/Enter
@@ -0,0 +1,40 @@
+Enter()
+
+FUNKTION:
+     int Enter();
+
+DEFINIERT IN:
+     /std/transport.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Wenn sich der Spieler noch nicht auf dem Transporter befindet, und der
+     Transporter momentan an einer Haltestelle liegt, betritt der Spieler
+     den Transporter.
+
+RUeCKGABEWERT:
+     Null, wenn der Spieler den Transporter nicht betreten konnte, sonst
+     ungleich Null.
+
+BEMERKUNGEN:
+     Es werden keine Tests durchgefuehrt, ob der Transporter ueberhaupt
+     angesprochen wurde! Das muss man selber machen.
+
+BEISPIELE:
+
+     int myEnter(string str)
+     {
+       if (str && id(str))
+         return Enter();
+
+       notify_fail("Was willst Du betreten?\n");
+       return 0;
+     }
+
+SIEHE AUCH:
+     Leave(), /std/transport.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:18:42 1996 by Wargon
diff --git a/doc/lfun/EvalArmour b/doc/lfun/EvalArmour
new file mode 100644
index 0000000..f1d356a
--- /dev/null
+++ b/doc/lfun/EvalArmour
@@ -0,0 +1,21 @@
+EvalArmour()
+
+FUNKTION:
+    int EvalArmour(object ob, closure qp)
+ 
+DEFINIERT IN:
+    /std/room/shop.c
+ 
+ARGUMENTE:
+    ob - Eine Ruestung.
+    qp - symbol_function("QueryProp",ob)
+
+BESCHREIBUNG:
+    Bewertet die Ruestung.
+ 
+RUECKGABEWERT:
+    Max(P_AC,P_EFFECTIVE_AC);
+ 
+SIEHE AUCH:
+    FindBestArmour()
+
diff --git a/doc/lfun/EvalWeapon b/doc/lfun/EvalWeapon
new file mode 100644
index 0000000..1e671a3
--- /dev/null
+++ b/doc/lfun/EvalWeapon
@@ -0,0 +1,21 @@
+EvalWeapon()
+
+FUNKTION:
+    int EvalWeapon(object ob, closure qp)
+ 
+DEFINIERT IN:
+    /std/room/shop.c
+ 
+ARGUMENTE:
+    ob - Eine Waffe.
+    qp - symbol_function("QueryProp",ob)
+
+BESCHREIBUNG:
+    Bewertet die Waffe.
+ 
+RUECKGABEWERT:
+    Max(P_WC,P_EFFECTIVE_WC);
+ 
+SIEHE AUCH:
+    FindBestWeapon()
+
diff --git a/doc/lfun/ExtraAttack b/doc/lfun/ExtraAttack
new file mode 100644
index 0000000..016b5c1
--- /dev/null
+++ b/doc/lfun/ExtraAttack
@@ -0,0 +1,30 @@
+ExtraAttack()
+
+FUNKTION:
+	varargs public void ExtraAttack(object enemy, int ignore_previous);
+
+ARGUMENTE:
+	enemy: Der Feind.
+	ignore_previous: Ein Flag
+
+BESCHREIBUNG:
+	Der Feind wird der Staerke der Waffe (bzw. der Haende) entsprechend
+	stark angegriffen. Hierbei wird Attack() aufgerufen.
+	Ist ignore_previous ungleich 0, dann wird die Erstschlagsperre von
+	Attack ignoriert. Dieser Angriff ist also auch dann moeglich, wenn
+	das Lebewesen eigentlich keinen Schlag mehr in dieser Runde ausfuehren
+	duerfte.
+
+RUECKGABEWERT:
+	Keiner.
+
+BEMERKUNG:
+	Der Einsatz dieser Funktion ist genehmigungspflichtig.
+	Weitere Hinweise siehe "man Attack".
+
+SIEHE AUCH:
+	"Attack"
+	/std/living/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Sun Nov 21 12:32:20 2004 by Bambi
diff --git a/doc/lfun/FilterArmours b/doc/lfun/FilterArmours
new file mode 100644
index 0000000..f934933
--- /dev/null
+++ b/doc/lfun/FilterArmours
@@ -0,0 +1,69 @@
+FilterArmours
+FUNKTION:
+     public object *FilterArmours(closure filterfun, varargs mixed* extra)
+
+DEFINIERT IN:
+     /std/living/clothing.c
+
+ARGUMENTE:
+     closure filterfun
+       Die Closure, die entscheiden soll, ob eine Ruestung im Ergebnisarray
+       enthalten sein soll.
+     
+     mixed extra
+       Beliebig viele Extra-Argumente, die <filterfun> uebergeben werden.
+
+BESCHREIBUNG:
+     Diese Funktion ruft <filterfunc> fuer jede getragene Ruestung des
+     Lebewesen mit der jeweiligen Ruestung als Argument auf und liefert ein
+     Array mit allen Ruestungen zurueck, fuer die <filterfun> einen Wert != 0
+     zurueckliefert.
+     Die <extra> Argumente werden als zusaetzliche Parameter an <filterfun>
+     uebergeben und duerfen keine Referenzen sein.
+     
+     Diese Variante ist zu bevorzugen, wenn man Ruestungen nach bestimmten
+     Kriterien durchsuchen will und QueryArmourByType() nicht ausreichend sein
+     sollte.
+
+RUeCKGABEWERT:
+     Ein Array von Objekten mit allen passenden Ruestungen.
+
+BEISPIELE:
+     1) Ich moechte gerne alle Ruestungen haben, die beschaedigt sind:
+     private int _is_damaged(object ruestung) {
+         return ruestung->QueryProp(P_DAMAGE);
+     }
+     ...
+     object *damaged_armours = PL->FilterArmours(#'_is_damaged);
+
+     2) Ich moechte alle Ruestungen, die groesser als 50cm sind.
+     private int _armour_is_bigger(object ruestung, int size) {
+       return ruestung->QueryProp(P_SIZE) > size;
+     }
+     ...
+     object *big_armours = PL->FilterArmours(#'_amour_is_bigger, 50); 
+
+     3) alle Ruestungen mit einer speziellen ID.
+     private int _has_id(object ruestung, string idstr) {
+       return ruestung->id(idstr);
+     }
+     object *has_id = PL->FilterArmours(#'_has_id, "\ntolleruestung");
+
+     4) alle Ruestungen mit einer speziellen ID, die groesser als 50cm sind.
+     private int _has_id(object ruestung, string idstr, int size) {
+       return ruestung->id(idstr) && ruestung->QueryProp(P_SIZE) > size;
+     }
+     object *has_id = PL->FilterArmours(#'_has_id, "\ntolleruestung", 50);
+
+     5) ueberhaupt alle getragene Ruestung
+     object *rue = PL->FilterArmours(#'objectp)
+
+
+SIEHE AUCH:
+     Wear(), WearArmour(), WearClothing(), Unwear(), UnwearArmour(), 
+     UnwearClothing()
+     P_CLOTHING, P_ARMOURS
+     FilterClothing(), QueryArmourByType()
+
+ZULETZT GEAeNDERT:
+14.03.2009, Zesstra
diff --git a/doc/lfun/FilterClothing b/doc/lfun/FilterClothing
new file mode 100644
index 0000000..95b00e3
--- /dev/null
+++ b/doc/lfun/FilterClothing
@@ -0,0 +1,51 @@
+FilterClothing
+FUNKTION:
+     public object *FilterClothing(closure filterfun, varargs mixed* extra)
+
+DEFINIERT IN:
+     /std/living/clothing.c
+
+ARGUMENTE:
+     closure filterfun
+       Die Closure, die entscheiden soll, ob eine Kleidung im Ergebnisarray
+       enthalten sein soll.
+
+BESCHREIBUNG:
+     Diese Funktion ruft <filterfunc> fuer jede getragene Kleidung des
+     Lebewesen mit der jeweiligen Kleidung als Argument auf und liefert ein
+     Array mit aller Kleidung zurueck, fuer die <filterfun> einen Wert != 0
+     zurueckliefert.
+     Die <extra> Argumente werden als zusaetzliche Parameter an <filterfun>
+     uebergeben und duerfen keine Referenzen sein.
+     
+     Diese Variante ist zu bevorzugen, wenn man die getrage Kleidung nach
+     bestimmten Kriterien durchsuchen will. 
+
+RUeCKGABEWERT:
+     Ein Array von Objekten mit allen passenden Kleidung.
+
+BEISPIELE:
+     1) Ich moechte alle Kleidung, die groesser als 50cm ist.
+     private int _armour_is_bigger(object clothing, int size) {
+       return clothing->QueryProp(P_SIZE) > size;
+     }
+     ...
+     object *big_armours = PL->FilterClothing(#'_amour_is_bigger, 50); 
+
+     2) alle Kleidung mit einer speziellen ID.
+     private int _has_id(object clothing, string idstr) {
+       return clothing->id(idstr);
+     }
+     object *has_id = PL->FilterClothing(#'_has_id, "\ntollekleidung");
+
+     3) ueberhaupt alle getragene Kleidung
+     object *clothing = PL->FilterClothing(#'objectp)
+
+SIEHE AUCH:
+     Wear(), WearArmour(), WearClothing(), Unwear(), UnwearArmour(), 
+     UnwearClothing()
+     P_CLOTHING, P_ARMOURS
+     FilterArmours(), QueryArmourByType()
+
+ZULETZT GEAeNDERT:
+14.03.2009, Zesstra
diff --git a/doc/lfun/FindBestArmours b/doc/lfun/FindBestArmours
new file mode 100644
index 0000000..68e734b
--- /dev/null
+++ b/doc/lfun/FindBestArmours
@@ -0,0 +1,38 @@
+FindBestArmours()
+
+FUNKTION:
+    varargs object *FindBestArmours(mixed type, int maxmon, int maxw,
+                                    mapping minac, mixed restr)
+ 
+DEFINIERT IN:
+    /std/room/shop.c
+ 
+ARGUMENTE:
+    type   - gewuenschter Ruestungstyp / Ruestungstypen
+    maxmon - Geld das ausgegeben werden darf
+    maxw   - Maximales Gewicht
+    minac  - minimale gewuenschte Ruestungsklasse pro Typ
+    restr  - zusaetzliches Argument fuer CheckFindRestrictions()
+
+BESCHREIBUNG:
+    Sucht die besten Ruestungen, die der Laden verkaufen kann.
+ 
+RUECKGABEWERT:
+    Die besten Ruestungen
+ 
+BEMERKUNG:
+    Die Qualitaet der Ruestung wird mit EvalArmour() bestimmt.
+    Haben zwei Ruestungen die gleiche Qualitaet,
+    wird die preiswertere genommen.
+ 
+BEISPIEL:
+    FindBestArmours(AT_ARMOUR,5000)
+    Bestes Ruestung unter 5000 Muenzen.
+
+    FindBestArmours(({AT_ARMOUR,AT_CLOAK,AT_BOOT}),10000,([AT_ARMOUR:20]))
+    Finded beste Ruestung, Umhang und Schuhe, die der Laden fuer
+    insgesamt 10000 Muenzen verkaufen kann, wobei die Ruestung mindestens
+    AC 20 haben muss.
+
+SIEHE AUCH:
+    FindBestWeapon(), CheckFindRestrictions(), EvalArmour()
diff --git a/doc/lfun/FindBestWeapon b/doc/lfun/FindBestWeapon
new file mode 100644
index 0000000..d0ba8f1
--- /dev/null
+++ b/doc/lfun/FindBestWeapon
@@ -0,0 +1,33 @@
+FindBestWeapon()
+
+FUNKTION:
+    varargs object FindBestWeapon(mixed type, int maxmon, int maxw, int hands,
+                                  int minwc, mixed restr)
+ 
+DEFINIERT IN:
+    /std/room/shop.c
+ 
+ARGUMENTE:
+    type   - gewuenschter Waffentyp / Waffentypen
+    maxmon - Geld das ausgegeben werden darf
+    maxw   - Maximales Gewicht
+    hands  - Anzahl Haende, die die Waffe belegen darf
+    minwc  - minimale gewuenschte Waffenklasse
+    restr  - zusaetzliches Argument fuer CheckFindRestrictions()
+
+BESCHREIBUNG:
+    Sucht die beste Waffe, die der Laden verkaufen kann.
+ 
+RUECKGABEWERT:
+    Die beste Waffe :-)
+ 
+BEMERKUNG:
+    Die Qualitaet der Waffe wird mit EvalWeapon() bestimmt.
+    Haben zwei Waffen die gleiche Qualitaet, wird die preiswertere genommen.
+ 
+BEISPIEL:
+    FindBestWeapon(WT_SWORD,5000,1)
+    Bestes einhaendiges Schwert unter 5000 Muenzen.
+
+SIEHE AUCH:
+    FindBestArmours(), CheckFindRestrictions(), EvalWeapon()
diff --git a/doc/lfun/FindDistantEnemyVictim b/doc/lfun/FindDistantEnemyVictim
new file mode 100644
index 0000000..a51c27f
--- /dev/null
+++ b/doc/lfun/FindDistantEnemyVictim
@@ -0,0 +1,32 @@
+FindDistantEnemyVictim()
+
+FUNKTION:
+	object FindDistantEnemyVictim(string wen, object pl, string msg,
+	                              int dist, int dy)
+
+DEFINIERT IN:
+	/std/spellbook.c
+
+ARGUMENTE:
+	wen  - id des gewuenschten Gegners, falls nicht angegeben:
+               SelectEnemy(FindDistantGroup(pl,-1,dist,dy,10000)
+	pl   - Caster.
+	msg  - Nachricht falls Gegner nicht anwesend ist.
+	dist - Entfernung
+	dy   - 2*erlaubte Abweichung von der Entfernung, default 100
+
+BESCHREIBUNG:
+	Findet einen Gegner in Entfernung dist-dy/2 bis dist+dy/2
+	z.B. fuer einen Angriffsspruch.
+	
+RUECKGABEWERT:
+	Der Auserwaehlte :-)
+
+BEMERKUNGEN:
+	1. Der Gegner wird auf jeden Fall angegriffen.
+	2. dist wird mit SA_RANGE modifiziert,
+	   dy wird mit SA_EXTENSION modifiziert.
+	3. Die Entfernung ist relativ zum Spieler.
+
+SIEHE AUCH:
+	teams, FindEnemyVictim, FindNearEnemyVictim, FindFarEnemyVictim
diff --git a/doc/lfun/FindDistantGroup b/doc/lfun/FindDistantGroup
new file mode 100644
index 0000000..4dedab6
--- /dev/null
+++ b/doc/lfun/FindDistantGroup
@@ -0,0 +1,31 @@
+FindDistantGroup()
+
+FUNKTION:
+	varargs object *FindDistantGroup(object pl, int who,
+                                         int dist, int dy, int dx)
+
+DEFINIERT IN:
+	/std/spellbook.c
+
+ARGUMENTE:
+	pl   - Caster
+	who  - 1=Freunde, -1=Gegner, 0=beide
+	dist - Entfernung
+	dy   - Tiefe      (default 100)
+	dx   - Breite     (default 100*MAX_TEAM_ROWLEN)
+
+BESCHREIBUNG:
+	Ermittelt Lebewesen, die sich in Entfernung <dist> in einem Bereich
+	der Breite <dx> und Tiefe <dy> befinden.
+
+RUECKGABEWERT:
+	Array mit den gefundenen Lebewesen.
+
+BEMERKUNGEN:
+	Genauere Hinweise unter "FindDistantGroups".
+	Wer sowohl Freunde wie auch Feinde in getrennten Arrays braucht,
+	sollte FindDistantGroups statt FindDistantGroup verwenden.
+
+SIEHE AUCH:
+	teams, FindDistantGroups
+
diff --git a/doc/lfun/FindDistantGroups b/doc/lfun/FindDistantGroups
new file mode 100644
index 0000000..507b72e
--- /dev/null
+++ b/doc/lfun/FindDistantGroups
@@ -0,0 +1,59 @@
+FindDistantGroups()
+
+FUNKTION:
+	varargs mixed FindDistantGroups(object pl, int dist, int dy, int dx)
+
+DEFINIERT IN:
+	/std/spellbook.c
+
+ARGUMENTE:
+	pl   - Caster
+	dist - Entfernung
+	dy   - Tiefe      (default 100)
+	dx   - Breite     (default 100*MAX_TEAM_ROWLEN)
+
+BESCHREIBUNG:
+	Ermitteld feindliche (bei Spielern NPCs, bei NPCs Spieler) und
+	freundliche (bei Spielern Spieler, bei NPCs NPCs) Lebewesen,
+	die sich in Entfernung <dist> in einem Bereich der Breite <dx>
+	und Tiefe <dy> befinden.
+
+RUECKGABEWERT:
+	Array mit zwei Arrays als Inhalt:
+	({ feindliche Lebewesen, freundliche Lebewesen })
+
+BEMERKUNGEN:
+	Die Entfernungsangaben sind als cm. zu verstehen.
+	Jedes Lebewesen belegt 50cm x 50cm mit Abstand 50cm
+	zum naechsten Lebewesen in jeder Richtung.
+	Die Breitenangabe wirkt sich nur in der Anzahl der
+	Lebewesen aus, die zufaellig pro Reihe ausgewaehlt werden.
+	Die Skillattribute SA_RANGE und SA_EXTENSION werden beruecksichtigt.
+
+BEISPIEL:
+	dist=200, dy=200, dx=200, ein Punkt = 50cm x 50cm
+	   . . . . . . . . . . . . .
+	3. . . . . . . G . . . . . .
+	   . . . . . . . . . . . . .
+	2. . . . . . G . G . . . . .dist+dy/2-+
+	   . . . . . . . . . . . . .          |  
+	1. . . . . G . G . G . . . .     dist +-+ (Gegner G)
+	---.-.-.-.-.-.-.-.-.-.-.-.-.          | |
+	1. . . . . F . F . F . . . .dist-dy/2-+ | (Freunde F)
+	   . . . . . . . . . . . . .            |
+	2. . . . . . F . S . . . . .------------+ (Reihe des Spielers S)
+	   . . . . . . . . . . . . .
+	3. . . . . . . F . . . . . .
+	   . . . . . . . . . . . . .
+	Abgedeckter Bereich:           100cm  bis  300cm
+                Reihe 3:  375cm..425cm         375>300 -> nicht erwischt
+                Reihe 2:  275cm..325cm         275<300 -> erwischt
+	Gegner  Reihe 1:  175cm..225cm 100<175,225<300 -> erwischt
+	Freunde Reihe 1:   75cm..125cm 100<125         -> erwischt
+	        Reihe 2:  -25cm...25cm 100> 25         -> nicht erwischt
+                Reihe 3: -125cm..-75cm 100>-75         -> nicht erwischt
+	Ergebnis: ({({G,G,G,G}),({F,F})})
+	(Maximal 2 Lebewesen pro Reihe bei Breite 200).
+
+SIEHE AUCH:
+	teams, FindDistantGroup
diff --git a/doc/lfun/FindEnemyVictim b/doc/lfun/FindEnemyVictim
new file mode 100644
index 0000000..0ab72fc
--- /dev/null
+++ b/doc/lfun/FindEnemyVictim
@@ -0,0 +1,24 @@
+FindEnemyVictim()
+
+FUNKTION:
+	object FindEnemyVictim(string wen, object pl, string msg)
+
+DEFINIERT IN:
+	/std/spellbook.c
+
+ARGUMENTE:
+	wen - id des gewuenschten Gegners, falls nicht angegeben SelectEnemy.
+	pl  - Caster.
+	msg - Nachricht falls Gegner nicht anwesend ist.
+
+BESCHREIBUNG:
+	Findet einen Gegner, z.B. fuer einen Angriffsspruch.
+	
+RUECKGABEWERT:
+	Der Auserwaehlte :-)
+
+BEMERKUNGEN:
+	Der Gegner wird auf jeden Fall angegriffen.
+
+SIEHE AUCH:
+	FindLivingVictim
diff --git a/doc/lfun/FindFarEnemyVictim b/doc/lfun/FindFarEnemyVictim
new file mode 100644
index 0000000..e43dda6
--- /dev/null
+++ b/doc/lfun/FindFarEnemyVictim
@@ -0,0 +1,30 @@
+FindFarEnemyVictim()
+
+FUNKTION:
+	object FindFarEnemyVictim(string wen, object pl, string msg,
+	                          int min, int max)
+
+DEFINIERT IN:
+	/std/spellbook.c
+
+ARGUMENTE:
+	wen - id des gewuenschten Gegners, SelectFarEnemy falls n/a.
+	pl  - Caster.
+	msg - Nachricht falls Gegner nicht anwesend ist.
+	min - minimale Kampfreihe
+	max - maximale Kampfreihe
+
+BESCHREIBUNG:
+	Findet einen Gegner aus Kampfreihe <min> bis <max>
+	z.B. fuer einen Angriffsspruch.
+	
+RUECKGABEWERT:
+	Der Auserwaehlte :-)
+
+BEMERKUNGEN:
+	1. Der Gegner wird auf jeden Fall angegriffen.
+	2. Die Reihenangaben werden NICHT mit Skillattributen modifiziert.
+	3. Die Angabe der Reihe ist absolut.
+
+SIEHE AUCH:
+	teams, FindEnemyVictim, FindNearEnemyVictim, FindDistantEnemyVictim
diff --git a/doc/lfun/FindGroup b/doc/lfun/FindGroup
new file mode 100644
index 0000000..ec1c693
--- /dev/null
+++ b/doc/lfun/FindGroup
@@ -0,0 +1,76 @@
+FindGroup()
+
+FUNKTION:
+    object*FindGroup(object pl,int who);
+
+DEFINIERT IN:
+    /std/spellbook.c
+
+ARGUMENTE:
+    pl
+      Lebewesen, von welchem die Freunde oder Feinde in der Umgebung
+      gefunden werden sollen.
+    who
+      Flag, welches anzeigt, ob Freunde oder Feinde gefunden werden
+      sollen (Konstanten definiert in '/sys/new_skills.h'):
+        FG_ENEMIES - (Wert -1) Feinde sollen gefunden werden
+        FG_FRIENDS - (Wert  1) Freunde sollen gefunden werden
+        FG_ALL     - (Wert  0) alle Lebewesen sollen gefunden werden
+
+RUeCKGABEWERT:
+    Array mit gefundenen Lebewesen
+
+BESCHREIBUNG:
+    Bei Spells, die sich auf mehrere Gegner auswirken oder bei denen man
+    per Hand ein Opfer auswaehlen moechte, laesst sich mittels der
+    Funktion FindGroup() eine Liste von Lebewesen ermitteln, welche in
+    der Umgebung von <pl> zu finden sind.
+    Je nachdem, was man denn genau vorhat, kann man sich von der
+    Funktion freundlich oder feindlich gesinnte Lebewesen heraussuchen
+    lassen.
+    Will man die freundlich gesinnten Lebewesen ermitteln, so uebergibt
+    man in <who> die Konstante FG_FRIENDS, bei feindlich gesinnten die
+    Konstante FG_ENEMIES, und wenn man alle Lebewesen bekommen moechte
+    schliesslich FG_ALL.
+    Bei der Auswahl gelten folgende Regeln:
+      (1) Lebewesen, mit denen <pl> im Kampf ist, sind grundsaetzlich
+          feindlich gesinnt.
+      (2) Teammitglieder von <pl> sind grundsaetzlich freundlich
+          gesinnt.
+      (3) Spieler sind gegenueber Spielern freundlich gesinnt, NPCs
+          gegenueber NPCs. NPCs kann man hierbei mit Hilfe der Property
+          P_FRIEND den Spielern zuordnen.
+      (4) Daraus folgt natuerlich, dass Spieler und NPCs grundsaetzlich
+          eine feindliche Einstellung gegenueber haben, sofern die NPCs
+          nicht die Property P_FRIEND gesetzt haben
+           (was standardmaessig natuerlich nicht der Fall ist).
+      (5) Netztote werden nicht erkannt.
+      (6) Magier werden nicht erkannt, wenn sie unsichtbar sind.
+      (7) Ein Magier wird als feindlich gesinnt nur dann erkannt, wenn
+          <pl> mit ihm im Kampf ist.
+      (6) Sucht man feindlich gesinnte Lebewesen, so werden die, welche
+          eine von den Properties P_NO_ATTACK oder P_NO_GLOBAL_ATTACK
+          gesetzt haben, nicht erkannt.
+    Die Property P_FRIEND sollte man in NPCs setzen, die dem Spieler
+    hilfreich beiseite stehen, z.B. vom Spieler beschworene HilfsNPCs.
+
+BEISPIELE:
+    Wenn man einen Feuerball nach jemandem wirft, so trifft dieser unter
+    Umstaenden auch andere, wenn er gross genug ist. Man nimmt hierbei
+    an, dass sich die freundlich gesinnten Lebewesen des Gegners auch
+    naeher bei ihm befinden als die feindlich gesinnten:
+      victim->Defend(500,DT_FIRE,([SP_SHOW_DAMAGE:1]),caster);
+      victimList=FindGroup(victim,FG_FRIENDS);
+      map_objects(victimList,
+                      "Defend",
+                      100,
+                      DT_FIRE,
+                      ([SP_SHOW_DAMAGE:1]),
+                      caster);
+    Hiermit trifft man also auch die Freunde von <victim>.
+
+SIEHE AUCH:
+    FindGroupN(), FindGroupP(), P_FRIEND, P_NO_GLOBAL_ATTACK
+
+----------------------------------------------------------------------------
+Last modified: Mon Jan 28 21:45:00 2002 by Tiamak
diff --git a/doc/lfun/FindGroupN b/doc/lfun/FindGroupN
new file mode 100644
index 0000000..8364f77
--- /dev/null
+++ b/doc/lfun/FindGroupN
@@ -0,0 +1,45 @@
+FindGroupN()
+
+FUNKTION:
+	object*FindGroupN(object pl,int who,int n);
+
+DEFINIERT IN:
+	/std/spellbook.c
+
+ARGUMENTE:
+	pl
+	  Lebewesen, von welchem die Freunde oder Feinde in der Umgebung
+	  gefunden werden sollen.
+	who
+	  Flag, welches anzeigt, ob Freunde oder Feinde gefunden werden
+	  sollen (Konstanten definiert in '/sys/new_skills.h'):
+	    FG_ENEMIES - (Wert -1) Feinde sollen gefunden werden
+	    FG_FRIENDS - (Wert  1) Freunde sollen gefunden werden
+	    FG_ALL     - (Wert  0) alle Lebewesen sollen gefunden werden
+	n
+	  Anzahl der Lebewesen, die zurueckgegeben werden sollen.
+	  Hierbei geht vorher noch das Skillattribute SA_EXTENSION ein!
+	  Es wird mindestens 1 Lebewesen zurueckgeliefert (sofern gefunden).
+
+RUeCKGABEWERT:
+	Array mit gefundenen Lebewesen
+
+BESCHREIBUNG:
+	Ausgesucht werden die Lebewesen genauso wie bei FindGroup(), nur
+	dass zum Schluss die Anzahl noch begrenzt wird.
+
+BEISPIELE:
+	Man moechte maximal 5 Feinde finden, die man gleichzeitig mit einem
+	Spell belegen kann:
+	  enemyList=FindGroupN(caster,FG_ENEMIES,5);
+	Dies gilt jedoch nur bei SA_EXTENSION==100, sonst wird
+	dementsprechend mehr oder weniger zurueckgegeben.
+	 (also bei SA_EXTENSION==200 doppelt so viele -> 10 Lebewesen)
+	Das Skillattribute SA_EXTENSION kann auch durch SA_QUALITY
+	veraendert worden sein; das sollte beachtet werden.
+
+SIEHE AUCH:
+	FindGroup(), FindGroupP(), P_FRIEND, P_NO_GLOBAL_ATTACK
+
+----------------------------------------------------------------------------
+Last modified: Mon Jan 25 15:04:31 1999 by Patryn
diff --git a/doc/lfun/FindGroupP b/doc/lfun/FindGroupP
new file mode 100644
index 0000000..efef568
--- /dev/null
+++ b/doc/lfun/FindGroupP
@@ -0,0 +1,46 @@
+FindGroupP()
+
+FUNKTION:
+	object*FindGroupP(object pl,int who,int pr);
+
+DEFINIERT IN:
+	/std/spellbook.c
+
+ARGUMENTE:
+	pl
+	  Lebewesen, von welchem die Freunde oder Feinde in der Umgebung
+	  gefunden werden sollen.
+	who
+	  Flag, welches anzeigt, ob Freunde oder Feinde gefunden werden
+	  sollen (Konstanten definiert in '/sys/new_skills.h'):
+	    FG_ENEMIES - (Wert -1) Feinde sollen gefunden werden
+	    FG_FRIENDS - (Wert  1) Freunde sollen gefunden werden
+	    FG_ALL     - (Wert  0) alle Lebewesen sollen gefunden werden
+	pr
+	  Wahrscheinlichkeit, mit der ein Lebewesen ausgesucht werden soll.
+	  Hierbei geht vorher noch das Skillattribute SA_EXTENSION ein!
+
+RUeCKGABEWERT:
+	Array mit gefundenen Lebewesen
+
+BESCHREIBUNG:
+	Ausgesucht werden die Lebewesen genauso wie bei FindGroup(), nur
+	dass zum Schluss die einzelnen Lebewesen per Zufall ausgewaehlt
+	werden. Es ist also nicht gesichert, dass ueberhaupt ein Lebewesen
+	zurueckgeliefert wird, trotzdem welche gefunden wurden.
+
+BEISPIELE:
+	Man moechte im Schnitt 50% der Feinde finden, die man gleichzeitig
+	mit einem Spell belegt:
+	  enemyList=FindGroupP(caster,FG_ENEMIES,50);
+	Dies gilt jedoch nur bei SA_EXTENSION==100, sonst wird mit
+	dementsprechend mehr oder weniger Wahrscheinlichkeit zurueckgegeben.
+	 (also bei SA_EXTENSION==200 doppelt so viele -> 100%, also alle)
+	Das Skillattribute SA_EXTENSION kann auch durch SA_QUALITY
+	veraendert worden sein; das sollte beachtet werden.
+
+SIEHE AUCH:
+	FindGroup(), FindGroupP(), P_FRIEND, P_NO_GLOBAL_ATTACK
+
+----------------------------------------------------------------------------
+Last modified: Mon Jan 25 15:04:31 1999 by Patryn
diff --git a/doc/lfun/FindNearEnemyVictim b/doc/lfun/FindNearEnemyVictim
new file mode 100644
index 0000000..86d77ce
--- /dev/null
+++ b/doc/lfun/FindNearEnemyVictim
@@ -0,0 +1,25 @@
+FindNearEnemyVictim()
+
+FUNKTION:
+	object FindNearEnemyVictim(string wen, object pl, string msg)
+
+DEFINIERT IN:
+	/std/spellbook.c
+
+ARGUMENTE:
+	wen - id des gewuenschten Gegners, SelectNearEnemy falls n/a.
+	pl  - Caster.
+	msg - Nachricht falls Gegner nicht anwesend ist.
+
+BESCHREIBUNG:
+	Findet einen im Nahkampf erreichbaren Gegner,
+	z.B. fuer einen Angriffsspruch.
+	
+RUECKGABEWERT:
+	Der Auserwaehlte :-)
+
+BEMERKUNGEN:
+	Der Gegner wird auf jeden Fall angegriffen.
+
+SIEHE AUCH:
+	teams, FindEnemyVictim, FindFarEnemyVictim, FindDistantEnemyVictim
diff --git a/doc/lfun/FindPotion b/doc/lfun/FindPotion
new file mode 100644
index 0000000..417c89b
--- /dev/null
+++ b/doc/lfun/FindPotion
@@ -0,0 +1,49 @@
+FindPotion()
+
+FUNKTION:
+     varargs int FindPotion(string s);
+
+DEFINIERT IN:
+     /std/player/potion.c
+
+ARGUMENTE:
+     string s
+       Ausgabetext. Wenn 0/Leerstring, wird Default verwendet.
+
+BESCHREIBUNG:
+     Diese Funktion gibt einem aufrufenden Spieler eventuell diesen ZT.
+     
+     Das aufrufende Spielerobjekt muss dafuer:
+     * diesen ZT im Potionmaster in seiner Liste eingetragen haben
+     * diesen ZT in der Liste der bekannten Traenke haben (durch
+       Orakel also fuer ihn auch freigeschaltet)
+     * darf keine Playerkills haben (P_KILLS)
+     * darf nicht im Editiermodus sein
+     * darf kein Geist sein (Ausnahme: Geisterschloss)
+
+     Wenn alle Kriterien erfolgreich erfuellt sind, wird 's' oder 
+     "Du findest einen Zaubertrank, den Du sofort trinkst." ausgegeben
+     und dem Spieler ggf die Wahl der Attribute gegeben.
+
+RUeCKGABEWERT:
+     0 bei Nichtvergabe, 1 bei erfolgreicher Vergabe.
+
+BEISPIELE:
+     string detail_papiere() {
+       if (this_player()->FindPotion(
+         break_string("Beim Rumwuehlen in den Papieren entdeckst Du einen "
+                      "kleinen Zaubertrank, den Du sofort trinkst.", 78)))
+         return "";  
+         // Es muss ein String zurueckgegeben werden, da man sonst
+         // die Fehlermeldung "Sowas siehst du hier nicht." bekommt
+       else
+         return "Die Papiere sind alle unbeschriftet.\n";
+     }
+
+SIEHE AUCH:
+     Sonstiges: zaubertraenke, /secure/potionmaster.c, /room/orakel.c
+     Verwandt:  AddKnownPotion(), RemoveKnownPotion(), InList()
+     Props:     P_POTIONROOMS, P_KNOWN_POTIONROOMS
+     Befehl:    traenke (fuer Magier zum Einschalten des Findens von ZTs)
+
+6.Feb 2016 Gloinson
diff --git a/doc/lfun/FindRangedTarget b/doc/lfun/FindRangedTarget
new file mode 100644
index 0000000..75970fb
--- /dev/null
+++ b/doc/lfun/FindRangedTarget
@@ -0,0 +1,40 @@
+FindRangedTarget()
+
+FUNKTION:
+    static string FindRangedTarget(string str, mapping shoot)
+
+DEFINIERT IN:
+    /std/ranged_weapon.c
+
+ARGUMENTE:
+    string str    - Schusssyntax
+    mapping shoot - Schussdaten
+
+BESCHREIBUNG:
+    Erhaelt von /std/ranged_weapon::cmd_shoot() die Schussdaten und eine
+    eventuell bereits modifizierte Syntax und versucht einen passenden Gegner
+    im Raum oder im Gebiet (P_SHOOTING_AREA) zu finden.
+    Dieser wird in SI_ENEMY im Mapping 'shoot' eingetragen und ein Wert != 0
+    zurueckgegeben.
+
+RUECKGABEWERT:
+    0     bei Fehlschlag
+    != 0  bei gueltigem SI_ENEMY in 'shoot'
+
+BEMERKUNGEN:
+    'shoot' enthaelt normalerweise folgende Eintraege:
+    * Key P_WEAPON:       die Schusswaffe
+    * Key P_WEAPON_TYPE:  P_AMMUNITION, also die Munitions-ID
+    * Key P_STRETCH_TIME: P_STRETCH_TIME der Waffe
+    * Key P_WC:           P_SHOOTING_WC der Waffe
+
+SIEHE AUCH:
+    Generell:  P_AMMUNITION, P_SHOOTING_WC, P_STRETCH_TIME
+    Methoden:  shoot_dam(L), cmd_shoot(L)
+    Gebiet:    P_RANGE, P_SHOOTING_AREA, P_TARGET_AREA
+    Team:      PresentPosition(L)
+    Suche:     present, SelectFarEnemy(L)
+    Syntax:    _unparsed_args(L)
+    Sonstiges: fernwaffen
+
+28.Jul 2014 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/Flee b/doc/lfun/Flee
new file mode 100644
index 0000000..6a63d89
--- /dev/null
+++ b/doc/lfun/Flee
@@ -0,0 +1,57 @@
+Flee()
+
+FUNKTION:
+        public varargs void Flee( object oldenv, int force )
+
+DEFINIERT IN:
+        /sys/living/combat.h
+        /std/living/combat.c
+
+ARGUMENTE:
+        oldenv
+          Ein Raum oder 0.
+          Wird ein Raum angegeben, dann muss sich der Fluechtende in diesem
+          Raum befinden, damit er versucht, zu fluechten, es sei denn, das
+          optionale Flag "force" ist gesetzt.
+        force
+          1, wenn der spieler unabhaengig von seiner Vorsicht fluechten soll.
+          0 sonst.
+
+BESCHREIBUNG:
+        Flee() wird im heart_beat() oder von CheckWimpyAndFlee() aufgerufen,
+        um den Spieler fluechten zu lassen. Man kann die Funktion im Spieler
+        auch "von Hand" aufrufen, beispielsweise in einem Spell. Man sollte
+        dann force auf 1 setzen, damit der Spieler unabhaengig von seiner
+        Vorsicht fluechtet.
+        Hierbei kann die Flucht dazu fuehren, dass man die Teamreihe wechselt,
+        aber auch, dass man den Raum verlaesst.
+        
+RUeCKGABEWERT:
+        keiner
+
+BEMERKUNGEN:
+
+BEISPIELE:
+        this_player()->Flee(0, 1);
+        // Der Spieler soll fluechten, egal, ob seine Lebenspunkte geringer
+        // als seine Vorsicht sind und unabhaengig von seiner Position.
+        
+        this_player()->Flee( find_object("/gilden/abenteurer") );
+        // Der Spieler soll fluechten, wenn er sich in der Abenteurergilde
+        // befindet (oder wenn diese nicht existiert)
+        
+        this_player()->Flee( "/gilden/abenteurer" );
+        // Der Spieler wird nicht fluechten, da der Vergleich von Dateiname
+        // und dem Raum 0 ergibt.
+
+        this_player()->Flee( find_object("/gilden/abenteurer"), 1);
+        // Der Spieler soll auf jeden Fall fluechten, egal ob er sich in der
+        // Abenteurergilde befindet oder nicht. Grund: Gesetztes force-Flag.
+        
+        
+        
+SIEHE AUCH:
+        CheckWimpyAndFlee(), Defend(), heart_beat(), 
+
+----------------------------------------------------------------------------
+Last modified: Wed Nov 12 14:44:42 2003 by Bambi
diff --git a/doc/lfun/FreeHands b/doc/lfun/FreeHands
new file mode 100644
index 0000000..2bed9b0
--- /dev/null
+++ b/doc/lfun/FreeHands
@@ -0,0 +1,36 @@
+FreeHands
+FUNKTION:
+     public varargs int FreeHands(object ob)
+
+DEFINIERT IN:
+     /std/living/combat.c
+
+ARGUMENTE:
+     ob - das Objekt, dessen Handnutzung vorbei ist
+        - kann 0 sein, dann wird PO benutzt
+
+RUECKGABEWERT:
+     0, wenn kein Objekt uebergeben wurde oder kein PO existiert
+     1, sonst
+
+BESCHREIBUNG:
+     Befreit die Haende eines Livings von ein bestimmten Objekt.
+     Es werden _alle_ Haende wieder freigegeben.
+
+BEISPIELE:
+     > halte seil fest
+     ...
+     this_player()->UseHands(this_object(),2);
+     ...
+
+     > lasse seil los
+     ...
+     this_player()->FreeHands(this_object());
+     ...
+
+SIEHE AUCH:
+     P_HANDS, P_HANDS_USED_BY
+     P_MAX_HANDS, P_USED_HANDS, P_FREE_HANDS
+     UseHands
+
+1.Feb.2004 Gloinson
diff --git a/doc/lfun/GetAquarium b/doc/lfun/GetAquarium
new file mode 100644
index 0000000..cea8d49
--- /dev/null
+++ b/doc/lfun/GetAquarium
@@ -0,0 +1,37 @@
+GetAquarium()
+
+FUNKTION:
+     varargs public string* GetAquarium(object angel)
+
+ARGUMENTE:
+     Das optionale Argument <angel> ist die Angel selbst.
+
+BESCHREIBUNG:
+     Die Funktion wird beim Angeln in Raeumen gerufen, wenn diese als 
+     Gewaessertyp W_USER gesetzt haben (siehe Manpage zu P_WATER).
+     Aus der zurueckgegebenen Liste der Pfade wird einer zufaellig aus-
+     gewaehlt und das Objekt geclont. 
+
+RUECKGABEWERT:
+     String-Array mit Pfadnamen zu den in diesem Raum fangbaren Fischen.
+
+BEMERKUNG:
+     Man kann die Fangchancen durch Mehrfachnennung einzelner Eintraege 
+     modifizieren. Es muss sich bei den Eintraegen um lad- und clonebare
+     Objekte handeln.
+     Fuer selbstprogrammierte Fische ist das Basisobjekt 
+     /std/items/fishing/fish zu verwenden. Einige vorgefertigte Fische 
+     finden sich darueber hinaus in /items/fishing/aquarium/
+
+BEISPIEL:
+     varargs string* GetAquarium(object angel) {
+       return ({"/d/dschungel/rikus/q4e/obj/stichling",
+                "/d/dschungel/rikus/q4e/obj/karpfen",
+                "/p/seher/partymonster/obj/fisch"});
+     }
+
+SIEHE AUCH:
+    Properties: P_WATER, P_FISH
+
+-----------------------------------------------------------------------------
+zuletzt geaendert: 2014-Sep-02, Arathorn
diff --git a/doc/lfun/GetDetail b/doc/lfun/GetDetail
new file mode 100644
index 0000000..4bd7301
--- /dev/null
+++ b/doc/lfun/GetDetail
@@ -0,0 +1,67 @@
+GetDetail()
+
+FUNKTION:
+    varargs string GetDetail(string key, string race, int sense)
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    key
+      Das zu ermittelnde Detail.
+    race
+      Rasse des ermittelnden Objektes (falls es ein Lebewesen ist).
+    sense
+      Die Art des zu untersuchenden Details:
+        Untersuchen, Riechen, Hoeren, Tasten.
+
+BESCHREIBUNG:
+    Die Beschreibung des gewuenschten Details wird ermittelt. Dabei
+    werden rassenspezifische Details beruecksichtigt. Es gibt hierbei
+    verschiedene Detailarten, deren Typ man in <sense> angibt:
+      SENSE_VIEW    - Fuer Defaultdetails zum Untersuchen.
+      SENSE_SMELL   - Fuer Details, die man riechen kann.
+      SENSE_SOUND   - Fuer Details, die man hoeren kann.
+      SENSE_TOUCH   - Fuer Details, die man abtasten kann.
+      SENSE_READ    - Fuer Details, die man lesen kann.
+
+    Dabei ist 0 == SENSE_VIEW.
+
+RUeCKGABEWERT:
+    Die Beschreibung des Details oder 0, wenn es dieses Detail nicht
+    gibt.
+
+BEISPIEL:
+    Im folgenden wird ein kleines Testdetail generiert:
+      AddDetail("test","Das ist ein Test!\n");
+    Im folgenden wird das Detail entfernt, wenn es existiert. Dies ist
+    eigentlich nicht noetig, da RemoveDetail() damit zurechtkommt, aber
+    eventuell sind ja noch weitere Aktionen noetig.
+      if(GetDetail("test"))
+      { RemoveDetail("test");
+        ...
+      }
+    Ein Geruch kann man folgendermassen erzeugen:
+      AddSmells("gold",
+        ([0      :"Gold kann man nicht riechen!\n",
+          "zwerg":"Deine trainierte Nase riecht es muehelos!\n"]));
+    Die Abfrage des Details gestaltet sich recht einfach:
+      GetDetail("gold","zwerg",SENSE_SMELL);
+    Die Funktion liefert das Detail fuer den Zwerg.
+      GetDetail("gold",0,SENSE_SMELL);
+    Die Funktion liefert das Detail fuer die restlichen Rassen.
+      GetDetail("gold",0,SENSE_SOUND);
+    Ein Sounddetail mit dem Namen "gold" existiert nicht, die Funktion
+    liefert 0 zurueck.
+
+SIEHE AUCH:
+    Setzen:    AddDetail(), AddReadDetail(), AddSmells(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveReadDetail(), RemoveSmells(), RemoveDetail(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS,
+               P_TOUCH_DETAILS, P_SPECIAL_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: break_string()
+
+27. Jan 2013 Gloinson
diff --git a/doc/lfun/GetDoorsMapping b/doc/lfun/GetDoorsMapping
new file mode 100644
index 0000000..4c68a9d
--- /dev/null
+++ b/doc/lfun/GetDoorsMapping
@@ -0,0 +1,24 @@
+GetDoorsMapping()
+
+FUNKTION:
+    mapping GetDoorsMapping()
+
+BESCHREIBUNG:
+    Mit dieser Funktion erhaelt man das Mapping der vorhandenen
+    Seherportale.
+    
+    Die Struktur ist von der Form: 
+	([ Pfad: Portalnummer; Portalbeschreibung, ])
+    Es wird eine Kopie dieses Mappings geliefert.
+
+RUECKGABEWERT:
+    Das Sehertormapping
+
+BEMERKUNGEN:
+    Diese Funktion wird von /d/seher/portale/sehertormaster definiert.
+
+BEISPIELE:
+    "/d/seher/portale/sehertormaster"->GetDoorsMapping();
+
+SIEHE AUCH:
+    DoorIsKnown, ShowDoors, Teleport, DiscoverDoor
diff --git a/doc/lfun/GetEnemies b/doc/lfun/GetEnemies
new file mode 100644
index 0000000..334d0bf
--- /dev/null
+++ b/doc/lfun/GetEnemies
@@ -0,0 +1,34 @@
+GetEnemies()
+
+FUNKTION:
+	mapping GetEnemies();
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	keine
+
+RUeCKGABEWERT:
+	Mapping mit bekannten Gegnern als Schluessel und Zeiten als
+	Eintraegen.
+
+BESCHREIBUNG:
+	Diese Funktion liefert das interne Mapping zurueck, in dem alle
+	Gegner abgespeichert sind. Diese Gegner stellen die Schluessel in
+	dem Mapping dar. Als Eintraege sind die Zeiten vermerkt, nach
+	welcher Zeit ein Gegner automatisch wieder vergessen werden soll.
+	Mehr Informationen dazu sind in der Dokumentation zur aehnlich
+	gearteten Funktion QueryEnemies() zu finden, die jedoch zwei Arrays
+	getrennte zurueckliefert.
+	Achtung: Es wird keine Kopie des Mappings erstellt. Saemtliche
+	Veraenderungen in dem Mapping wirken sich unmittelbar auf die
+	interne Gegnerliste aus. Die Funktion sollte deshalb nicht genutzt
+	werden. Gegner ein- und austragen sollte man nur mittels
+	InsertEnemy() und StopHuntFor().
+
+SIEHE AUCH:
+	QueryEnemies(), SetEnemies(), InsertEnemy(), StopHuntFor()
+
+----------------------------------------------------------------------------
+Last modified: Wed May 26 16:47:51 1999 by Patryn
diff --git a/doc/lfun/GetExits b/doc/lfun/GetExits
new file mode 100644
index 0000000..d2f004e
--- /dev/null
+++ b/doc/lfun/GetExits
@@ -0,0 +1,23 @@
+GetExits()
+
+FUNKTION:
+     varargs string GetExits(object viewer);
+
+DEFINIERT IN:
+     /std/room/exits
+
+ARGUMENTE:
+     viewer
+          Derjenige, der sich die Ausgaenge anschaut.
+
+BESCHREIBUNG:
+     Es wird eine Liste der fuer viewer sichtbaren Ausgaenge erstellt.
+
+RUeCKGABEWERT:
+     String mit der Liste der sichtbaren Ausgaenge.
+
+SIEHE AUCH:
+     AddExit(), AddSpecialExit(), /std/room/exits.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:18:47 1996 by Wargon
diff --git a/doc/lfun/GetFValue b/doc/lfun/GetFValue
new file mode 100644
index 0000000..ac17224
--- /dev/null
+++ b/doc/lfun/GetFValue
@@ -0,0 +1,37 @@
+FUNKTION:
+
+	varargs int GetFValue(string vname, mapping map, object pl) 
+
+
+ARGUMENTE:
+
+	vname	: name des parameters aus dem spellmapping
+	map   	: spellmapping
+	pl 	: caster
+
+
+BESCHREIBUNG:
+
+	Berechnet den Wert und den Factor des Parameters in spellmapping.
+
+
+RUECKGABEWERT:
+
+	Berechneter Wert*Factor aus dem Spellmapping.
+
+
+BEMERKUNGEN:
+
+	Ruft GetValue(vname,map,pl)*GetFactor(vname,map,pl)/100 auf.
+
+
+BEISPIEL:
+
+	xxx=GetFValue(SI_SKILLDAMAGE,sinfo,caster);
+
+Siehe auch:
+
+	"GetValue", "GetFactor", "GetOffset", "GetValueO", "GetFValueO"
+
+	Ausfuehrliches Beispiel siehe "GetFValueO".
+
diff --git a/doc/lfun/GetFValueO b/doc/lfun/GetFValueO
new file mode 100644
index 0000000..e954e4b
--- /dev/null
+++ b/doc/lfun/GetFValueO
@@ -0,0 +1,74 @@
+FUNKTION:
+
+	varargs int GetFValueO(string vname, mapping map, object pl) 
+
+
+ARGUMENTE:
+
+	vname	: name des parameters aus dem spellmapping
+	map   	: spellmapping
+	pl 	: caster
+
+
+BESCHREIBUNG:
+
+	'Berechnet' den Wert, den Factor und den Offset des Parameters 
+	in spellmapping.
+
+
+RUECKGABEWERT:
+
+	Berechneter (Wert*Factor)/100+Offset aus dem Spellmapping.
+
+
+BEMERKUNGEN:
+
+	Ruft (GetValue(vname,map,pl)*GetFactor(vname,map,pl))/100+
+	GetOffset(vname,map,pl) auf.
+
+
+BEISPIEL:
+
+	AddSpell("egal",10,
+	([
+	OFFSET(SI_COST):([SM_RACE:(["Zwerg":4]) ]),
+	FACTOR(SI_COST):([SM_RACE:(["Mensch":90]) ]),
+	SI_SKILLDAMAGE:100,
+	OFFSET(SI_SKILLDAMAGE):25,
+	SI_SKILLDAMAGE_TYPE:DT_EXAMPLE,
+	FACTOR(SI_SKILLDAMAGE):([SM_RACE:(["Zwerg":80,"Elf":120]) ]) 
+	]));
+
+	So, was sollen uns diese Zeilen sagen?
+
+	Es wird ein Spruch Names 'egal' ins Spellbook eingetragen. Er kostet
+	regulaer 10 MP. Fuer Zwerge allerdings wird ein Offset von 4 MP 
+	aufgeschlagen. Ausserdem machen Zwerge nur 80% Schaden, Elfen 
+	hingegen 120%. Der Grundschaden betraegt 100 Schadenspunkte, der 
+	Offset des Schadens nochmal 25. Menschen bezahlen fuer diesen 
+	Spruch nur 90% der Kosten.
+
+	Nun die Rechenbeispiele:
+
+	Fuer die Kosten:
+		Value	ValueO	FValue	FValueO
+	Mensch	   10	    10	     9	      9
+	Elf   	   10	    10	    10       10
+	Hobbit	   10	    10	    10	     10
+	Zwerg	   10	    14	    10	     14
+
+	Fuer den Schaden:
+			Value	ValueO	FValue	FValueO
+	Mensch		100	125	100	125
+	Elf  		100	125	120	150
+	Hobbit		100	125	100	125
+	Zwerg		100	125	 80	100
+
+	An diesem Beispiel sieht man deutlich, wie man mit ein paar 
+	Offsets und Faktoren die Wirkung eines Spruches deutlich 
+	veraendern kann. Es sollte bei eigenen Berechnungen immer 
+	GetFValueO benutzt werden.
+
+Siehe auch:
+
+	"GetValue", "GetFactor", "GetOffset", "GetFValue", "GetValueO"
diff --git a/doc/lfun/GetFactor b/doc/lfun/GetFactor
new file mode 100644
index 0000000..42812dd
--- /dev/null
+++ b/doc/lfun/GetFactor
@@ -0,0 +1,37 @@
+FUNKTION:
+
+	varargs int GetFactor(string vname, mapping map, object pl) 
+
+
+ARGUMENTE:
+
+	vname	: name des parameters aus dem spellmapping
+	map   	: spellmapping
+	pl 	: caster
+
+
+BESCHREIBUNG:
+
+	'Berechnet' den Factor des Parameters in spellmapping.
+
+
+RUECKGABEWERT:
+
+	Berechneter Factor aus dem Spellmapping.
+
+
+BEMERKUNGEN:
+
+	Beschraekung auf 10-1000. Werte groesser oder kleiner als diese
+	werden 'abgeschnitten'.
+
+
+BEISPIEL:
+
+	xxx=GetFactor(SI_SKILLDAMAGE,sinfo,caster);
+
+Siehe auch:
+
+	"GetValue", "GetOffset", "GetFValue", "GetValueO", "GetFValueO"
+
+	Ausfuehrliches Beispiel siehe "GetFValueO".
diff --git a/doc/lfun/GetGroupMembers b/doc/lfun/GetGroupMembers
new file mode 100644
index 0000000..ef70aa3
--- /dev/null
+++ b/doc/lfun/GetGroupMembers
@@ -0,0 +1,36 @@
+GetGroupMembers()
+FUNKTION:
+     string *GetGroupMembers(string grp)
+
+DEFINIERT IN:
+     /p/daemon/materialdb.c (MATERIALDB)
+
+ARGUMENTE:
+     string grp - Gruppenname
+
+BESCHREIBUNG:
+     Gibt alle dieser Gruppe zugehoerigen Materialien zurueck. Dazu gut, sich
+     einen Ueberblick ueber die aktuelle Liste zu verschaffen.
+
+RUECKGABEWERT:
+     Array von Strings mit Materialien oder ({})
+
+BEISPIELE:
+     // wir wollen irgend ein Metall haben, nennen dies aber beim Namen
+     int ind;
+     string* likeit;
+     likeit=MATERIALDB->GetGroupMembers(MATGROUP_METAL);
+     ind=random(sizeof(likeit));
+     ...
+     write("Der Schmied sagt: Mir fehlt noch "+
+	   MATERIALDB->MaterialName(likeit[ind], WER, 100)+".\n");
+     ...
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetMatMembership()
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/GetInfoArr b/doc/lfun/GetInfoArr
new file mode 100644
index 0000000..f1e251a
--- /dev/null
+++ b/doc/lfun/GetInfoArr
@@ -0,0 +1,31 @@
+GetInfoArr()
+FUNKTION:
+     static mixed *GetInfoArr(string str)
+
+DEFINIERT IN:
+     /std/npc/info.c
+
+ARGUMENTE:
+     string str	 - Schluesselwort der Frage
+
+BESCHREIBUNG:
+     Sucht nach einem Schluesselwort in den abgelegten Antworten
+     und gibt die Informationen oder einen 0er-Array zurück.
+
+BEMERKUNGEN:
+     Ueberschreibbar :)
+
+RUECKGABEWERT:
+     Siehe Parameter von AddInfo:
+
+     Array mit:
+     ({ <Infostring> (0 fuer keine Antwort),
+        <Indent-String> (Default: 0),
+        <Silent> (Default: 0),
+        <Casebased> (Default: 0)
+     })
+
+SIEHE AUCH:
+     Verwandt: AddInfo, do_frage
+
+31.Mar 2007 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/GetMatMembership b/doc/lfun/GetMatMembership
new file mode 100644
index 0000000..8859a38
--- /dev/null
+++ b/doc/lfun/GetMatMembership
@@ -0,0 +1,55 @@
+GetMatMembership()
+FUNKTION:
+     string *GetMatMembership(string mat)
+
+DEFINIERT IN:
+     /p/daemon/materialdb.c (MATERIALDB)
+
+ARGUMENTE:
+     string mat - ein Material
+
+BESCHREIBUNG:
+     Gibt alle Gruppen, denen das Material angehoert zurueck. Geeignet, um
+     die Eigenschaften eines Materials zu ueberpruefen.
+
+RUECKGABEWERT:
+     Array von Strings mit Materialiengruppen oder ({})
+
+BEISPIELE:
+     // ein weiser Schmied:
+     int i;
+     string *mat, mname, mgroup;
+     mat=m_indices(ob->QueryProp(P_MATERIAL));
+     i=sizeof(mat);
+
+     write("Der Schmied sagt: "+ob->Name(WER)+" besteht aus ...\n");
+     while(i--) {
+      // den Namen erkennen/aussprechen:
+      // Materialien werden allgemein etwas besser erkannt (zu 5%), aber
+      // alles aus Metall wird zu +100% besser erkannt ...
+      mname=MATERIALDB->MaterialName(mat[i], WER,
+	     ({5, ([MATRGROUP_METAL, 100])}));
+
+      // und nur Metalle analysieren ...
+      if(MATERIALDB->MaterialGroup(([mat[i]:100]),MATGROUP_METAL)>=100) {
+       int j;
+       string *mgr;
+       mgr=MATERIALDB->GetMatMembership(mat[i]);
+       j=sizeof(mgr);
+       mgroup=" gehoert zu ";
+       while(j--) {
+        mgroup+=MATERIALDB->GroupName(mgr[j]);
+        if(j>0) mgroup+=", ";
+       }
+      } else mgroup=" kenne ich nicht";
+      printf("%-12.12s: %s\n",mname, mgroup);
+     }
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers()
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/GetOffset b/doc/lfun/GetOffset
new file mode 100644
index 0000000..e3eba54
--- /dev/null
+++ b/doc/lfun/GetOffset
@@ -0,0 +1,38 @@
+FUNKTION:
+
+	varargs int GetOffset(string vname, mapping map, object pl) 
+
+
+ARGUMENTE:
+
+	vname	: name des parameters aus dem spellmapping
+	map   	: spellmapping
+	pl 	: caster
+
+
+BESCHREIBUNG:
+
+	'Berechnet' den Offset des Parameters in spellmapping.
+
+
+RUECKGABEWERT:
+
+	Berechneter Offset aus dem Spellmapping.
+
+
+BEMERKUNGEN:
+
+	Beschraekung auf -10000 bis +10000. Werte groesser oder kleiner 
+	als diese werden 'abgeschnitten'. Mehr als 100% Bonus oder Malus
+	gibts nicht ;-).
+
+
+BEISPIEL:
+
+	xxx=GetOffset(SI_SKILLDAMAGE,sinfo,caster);
+
+Siehe auch:
+
+	"GetValue", "GetFactor", "GetFValue", "GetValueO", "GetFValueO"
+
+	Ausfuehrliches Beispiel siehe "GetFValueO".
diff --git a/doc/lfun/GetOwner b/doc/lfun/GetOwner
new file mode 100644
index 0000000..f6b0f98
--- /dev/null
+++ b/doc/lfun/GetOwner
@@ -0,0 +1,19 @@
+GetOwner(L)
+FUNKTION:
+     string GetOwner();
+
+BESCHREIBUNG:
+     Wird vom Hoerrohr gerufen:
+      /d/inseln/shakedbeer/shakyisland/obj/hoerrohr.c
+     und kann einen Besitzer der Dateien zurueckgeben, der vom Besitzer
+     der Pfade in denen die Datei liegt abweicht.
+
+BEISPIELE:
+     string GetOwner() {
+      return "Tiamak";
+     }
+
+SIEHE AUCH:
+     P_INFO
+
+11. Mai 2004 Gloinson
diff --git a/doc/lfun/GetPhiolenInfos b/doc/lfun/GetPhiolenInfos
new file mode 100644
index 0000000..604d697
--- /dev/null
+++ b/doc/lfun/GetPhiolenInfos
@@ -0,0 +1,97 @@
+GetPhiolenInfos(L)
+
+FUNKTION:
+     mixed *GetPhiolenInfos();
+
+BESCHREIBUNG:
+
+     Wird von der Phiole aufgerufen:
+     /d/inseln/miril/sternquest/obj/phiole.c
+     Der Raum muss /p/service/miril/phiole.h includen.     
+     Mit der Phiole kann ein Spieler durch Tueren schauen. Bei Tueren, die
+     vom Doormaster (/obj/doormaster.c) verwaltet werden, klappt dies auto-
+     matisch. Moechte man das verbieten, so muss die Funktion 
+     GetPhiolenInfos() im entsprechenden Raum definiert sein.
+     Befinden sich Tueren im Raum, die nicht vom Doormaster verwaltet werden,
+     aber bei denen die Phiole funktionieren soll, so kann dies ebenfalls
+     mittels GetPhiolenInfos() geschehen.
+     
+     Funktion der Phiole:
+     Sie projiziert ein Bild dessen, was hinter der Tuer liegt auf die Tuer.
+     Steht die Tuer offen, so sieht der Spieler nur eine Wand, denn es wird
+     gezeigt, was hinter dieser Tuer ist.
+     
+     Ist die Tuer jedoch ge- oder verschlossen, so sieht er auf der Tuer
+     die Langbeschreibung des Raumes, der hinter der Tuer liegt, sowie das
+     Inventar dieses Raumes. 
+
+     Aufbau der Funktion:
+     mixed *GetPhiolenInfos() {
+       return ({
+                ([
+                  PHIOLE_DEST:     string,       
+                  PHIOLE_IDS:      *string,             
+                  PHIOLE_GENDER:   int,           
+                  PHIOLE_STATUS:   int,     (optional)
+                  PHIOLE_LONG:     string,  (optional)
+                  PHIOLE_VERBOTEN: int      (optional)
+                ]),
+                ([
+                  ... Naechste Tuer
+                ])
+              });
+     }
+     
+     PHIOLE_DEST      Dateiname des Zielraums
+     PHIOLE_IDS	      Array der Strings, mit denen sich die Tuer 
+                      ansprechen laesst
+     PHIOLE_GENDER    Geschlecht der Tuer
+     PHIOLE_STATUS    aktueller Status der Tuer:
+                      0 geschlossen/verschlossen, 1 offen
+                      Bei Tueren, die mit NewDoor erzeugt wurden
+                      nicht setzen
+     PHIOLE_LONG      Beschreibung des Zielraums (z.B. bei einer 
+                      Faketuer)
+     PHIOLE_VERBOTEN  0 erlaubt, 1 verboten
+
+BEISPIEL:
+     Ein Raum enthaelt eine Holztuer und ein Portal. Erstere ist mittels 
+     NewDoor(...) gebaut, soll aber nicht zu durchschauen sein, letztere ist 
+     selbstgestrickt. Die erste fuehrt in den Workroom von Jof, die zweite
+     ist nur ein Fake und immer geschlossen. Bei vom Doormaster erzeugten 
+     Tueren sollte PHIOLE_STATUS nicht gesetzt werden, da dies automatisch
+     abgefragt wird.
+     
+     #include "/p/service/miril/phiole.h"
+     ...
+     mixed *GetPhiolenInfos(){
+       return ({
+                ([
+                  PHIOLE_DEST:"/players/jof/workroom",
+                  PHIOLE_IDS:({"holztuer"}),           
+                  PHIOLE_GENDER:FEMALE,                    
+                  PHIOLE_VERBOTEN:1          
+                ]),
+                ([
+                  PHIOLE_DEST:0,
+                  PHIOLE_IDS:({"portal"}),           
+                  PHIOLE_GENDER:NEUTER, 
+                  PHIOLE_STATUS:0,
+                  PHIOLE_LONG:"Du stehst in einer riesigen Schatzkammer und "
+                              "kannst Dein Glueck kaum fassen. Nun brauchst "
+                              "Du nur noch zuzugreifen und bist der reichste "
+                              "Bewohner des MorgenGrauen.",
+                  PHIOLE_VERBOTEN:0
+                ])
+              });
+      }
+      Mittels PHIOLE_LONG laesst sich auch bei erlaubten und verbotenen Tueren 
+      einstellen, was der Spieler statt der normalen Raumbeschreibung und den 
+      im Raum befindlichen Objekten sehen soll. Das koennte z.B. sinnvoll 
+      sein, falls der Spieler zwar die Raumbeschreibung, aber nicht den fiesen 
+      Drachen sehen soll, der sich ebenfalls im Raum befindet.
+      
+SIEHE AUCH:
+      NewDoor(), QueryDoorStatus(), SetDoorStatus(), QueryAllDoors()
+-----------------------------------------------------------------------------
+Letzte Aenderung: Sam, 14. Jan 2006, 12:21, Miril
diff --git a/doc/lfun/GetReputation b/doc/lfun/GetReputation
new file mode 100644
index 0000000..b60cdf8
--- /dev/null
+++ b/doc/lfun/GetReputation
@@ -0,0 +1,26 @@
+GetReputation
+FUNKTION:
+     public int GetReputation(string repid)
+
+DEFINIERT IN:
+     /std/player/reputation.c
+
+ARGUMENTE:
+     repid
+       Eindeutige ID der angefragten Reputation.
+
+BESCHREIBUNG:
+     Liefert den aktuellen Wert der angegebenen Reputation <repid> zurueck.
+
+RUeCKGABEWERT:
+     Zahlenwert, Integer.
+
+BEISPIELE:
+     s. reputation
+
+SIEHE AUCH:
+     reputation
+     ChangeReputation(), GetReputations()
+
+ZULETZT GEAeNDERT:
+06.04.2009, Zesstra
diff --git a/doc/lfun/GetReputations b/doc/lfun/GetReputations
new file mode 100644
index 0000000..81d571e
--- /dev/null
+++ b/doc/lfun/GetReputations
@@ -0,0 +1,23 @@
+GetReputations
+FUNKTION:
+     public mapping GetReputations()
+
+DEFINIERT IN:
+     /std/player/reputation.c
+
+BESCHREIBUNG:
+     Liefert ein Mapping aller im Spieler gespeicherten Reputationen und ihrer
+     Werte zurueck.
+
+RUeCKGABEWERT:
+     Mapping ([(string)repid: (int)wert])
+
+BEISPIELE:
+     s. repuation
+
+SIEHE AUCH:
+     reputation
+     GetReputation(), GetReputations()
+
+ZULETZT GEAeNDERT:
+06.04.2009, Zesstra
diff --git a/doc/lfun/GetValue b/doc/lfun/GetValue
new file mode 100644
index 0000000..1c95119
--- /dev/null
+++ b/doc/lfun/GetValue
@@ -0,0 +1,37 @@
+FUNKTION:
+
+	varargs int GetValue(string vname, mapping map, object pl) 
+
+
+ARGUMENTE:
+
+	vname	: name des parameters aus dem spellmapping
+	map   	: spellmapping
+	pl 	: caster
+
+
+BESCHREIBUNG:
+
+	'Berechnet' den Wert des uebergebenen Parameters aus dem 
+	Spellmapping.
+
+
+RUECKGABEWERT:
+
+	Berechneter Wert aus dem Spellmapping.
+
+
+BEMERKUNGEN:
+
+	
+
+
+BEISPIEL:
+
+	xxx=GetValue(SI_SKILLDAMAGE,sinfo,caster);
+
+Siehe auch:
+
+	"GetFactor", "GetOffset", "GetFValue", "GetValueO", "GetFValueO"
+
+	Ausfuehrliches Beispiel siehe "GetFValueO".
diff --git a/doc/lfun/GetValueO b/doc/lfun/GetValueO
new file mode 100644
index 0000000..e55b8f3
--- /dev/null
+++ b/doc/lfun/GetValueO
@@ -0,0 +1,36 @@
+FUNKTION:
+
+	varargs int GetValueO(string vname, mapping map, object pl) 
+
+
+ARGUMENTE:
+
+	vname	: name des parameters aus dem spellmapping
+	map   	: spellmapping
+	pl 	: caster
+
+
+BESCHREIBUNG:
+
+	Berechnet den Wert und den Offset des Parameters in spellmapping.
+
+
+RUECKGABEWERT:
+
+	Berechneter Wert+Offset aus dem Spellmapping.
+
+
+BEMERKUNGEN:
+
+	Ruft GetValue(vname,map,pl)+GetOffset(vname,map,pl) auf.
+
+
+BEISPIEL:
+
+	xxx=GetValueO(SI_SKILLDAMAGE,sinfo,caster);
+
+Siehe auch:
+
+	"GetValue", "GetFactor", "GetOffset", "GetFValue", "GetFValueO"
+
+	Ausfuehrliches Beispiel siehe "GetFValueO".
diff --git a/doc/lfun/Gildenproperties b/doc/lfun/Gildenproperties
new file mode 100644
index 0000000..30a16c3
--- /dev/null
+++ b/doc/lfun/Gildenproperties
@@ -0,0 +1,131 @@
+Name			Beschreibung
+
+P_GUILD_SKILLS		Property fuer Skills in der Gilde
+P_GUILD_RESTRICTIONS 	Restriktionen fuer den Eintritt in die Gilde
+P_GUILD_DEFAULT_SPELLBOOK  
+P_GUILD_MALE_TITLES 	Maennliche Gildentitel
+P_GUILD_FEMALE_TITLES	Weibliche Gildentitel
+P_GUILD_LEVELS		Gildenstufen
+P_SB_SPELLS 		Property fuer Spells in Spellbook
+P_GLOBAL_SKILLPROPS	Properties der Gilde UND des Spellbooks
+
+Properties des Spielers:
+P_GUILD			String mit dem Namen der Gilde
+P_GUILD_LEVEL		Gildenstufe
+P_GUILD_TITLE		Gildentitel
+P_GUILD_RATING		Rating in der Gilde
+P_NEWSKILLS		mapping mit Skills und Spells
+P_NEXT_SPELL_TIME	Zeit bis der naechste Spell gesprochen werden kann
+P_TMP_ATTACK_HOOK	Angriffs-Ersatzfunktion
+P_TMP_ATTACK_MOD	Angriffs-Modifizierung
+P_TMP_DEFEND_HOOK	Abwehr-Ersatzfunktion
+P_TMP_DIE_HOOK		"Die"-Ersatzfunktion
+P_TMP_MOVE_HOOK		Bewegungs-Ersatzfunktion
+P_DEFENDERS		Verteidiger
+P_PREPARED_SPELL	Vorbereiteter Spell
+P_DEFAULT_GUILD		Standardgilde
+P_MAGIC_RESISTANCE_OFFSET Offset fuer Magie-Resistenzen
+
+Standard-Skills:
+SK_FIGHT		Kampfskill(global)
+SK_SWORDFIGHTING	Schwertkampf
+SK_WEAPONLESS		Waffenloser Kampf
+SK_TWOHANDED       	Zweihandkampf
+SK_CASTING         	Zauber-Skill
+SK_MAGIC_ATTACK    	Magischer Angriff
+SK_MAGIC_DEFENSE   	Magische Abwehr
+SK_NIGHTVISION     	Nachtsicht
+SK_BOOZE           	Sauf-Skill
+SK_INFORM_DEFEND   	
+SK_DEFEND_OTHER		
+SK_CARRY		Trage-Skill
+SK_SPELL_DEFEND		Spruch-Abwehr
+
+Skill-Attribute:
+P_SKILL_ATTRIBUTES	Property fuer Skill-Attribute
+P_SKILL_ATTRIBUTE_OFFSETS   Property fuer Offsets der Skill-Attribute
+SA_QUALITY		Allgemeine Qualitaet
+SA_DAMAGE		Schaden
+SA_SPEED		Geschwindigkeit
+SA_DURATION		Dauer
+SA_EXTENSION		Ausdehnung
+SA_RANGE		Reichweit
+SA_ENEMY_SAVE: identisch zu SA_SPELL_PENETRATION (OBSOLET!)
+SA_SPELL_PENETRATION: Chance des _Casters_, einen Spell durch ein
+                      P_NOMAGIC durchzukriegen.
+
+Skill-Info:
+FACTOR(x)		Faktor
+OFFSET(x)		Offset
+SI_SKILLFUNC		Funktion, die aufgerufen wird
+SI_CLOSURE		Intern (Geschwindigkeitsoptimierung)
+SI_SPELLBOOK		Spellbook, in dem der Spell steht
+SI_SPELLCOST		Kosten des Spells
+SI_TIME_MSG		Die Meldung wird anstelle von "Du bist noch zu 
+			erschoepft von Deinem letzten Spruch." ausgegeben.
+SI_SP_LOW_MSG		Die Meldung wird anstelle von "Du hast zu wenig 
+			Zauberpunkte fuer diesen Spruch." ausgegeben.
+SI_NOMAGIC		Prozentwert, mit dem P_NOMAGIC mgangen werden kann
+SI_SPELLFATIGUE		Erschoepfung - Zeit, in der keine weiteren Spells
+			aufgerufen werden koennen
+SI_SKILLLEARN		Lerngeschwindigkeit in 0.01% pro A_INT/2
+SI_SKILLABILITY		Faehigkeit, diesen Spruch zu benutzen
+SI_SKILLARG		Argumente, die uebergeben wurden
+SI_SKILLRESTR_USE	Beschraenkungen beim Gebrauch
+SI_SKILLRESTR_LEARN	Beschraenkungen beim Lernen
+SI_SKILLINFO		Kurzer Informationstext
+SI_SKILLINFO_LONG	Langer Informationstext
+SI_SKILLDAMAGE		Schaden
+SI_SKILLDAMAGE_TYPE	Art des Schadens
+SI_SKILLDAMAGE_MSG	Meldung die anstelle einer Waffe kommen soll
+SI_SKILLDAMAGE_MSG2	dito fuer den Text fuer andere im Raum
+SI_SPELL		Spell, mit dem angegriffen wurde
+SI_INHERIT		Skill, von dem etwas uebernommen werden soll
+SI_DIFFICULTY		Wert, der die Obergrenze der Faehigkeit abgrenzt
+SI_LASTLIGHT		Fuer Nachtsicht: Wann hat der Spieler zum letzten 
+			mal Licht gesehen.
+SI_SKILLHEAL		Heilung
+SI_USR			selbst definierte Infos
+SI_GUILD		Gilde, falls Auswahl aus mehreren moeglich
+SI_ENEMY		Feind bei Kampfspruechen
+SI_FRIEND		Der zu verteidigende Freund bei DefendOther 
+			und InformDefend
+SI_MAGIC_TYPE		Von was fuer einer Art ist die Magie (s.u.)
+SI_PREPARE_TIME		Zeit die zur Vorbereitung benoetigt wird.
+SI_ATTACK_BUSY_MSG	Meldung, wenn der Spieler zu beschaeftigt ist
+SI_NO_ATTACK_BUSY	Wenn der Spell nicht als Taetigkeit zaehlen/gezaehlt 
+			werden soll, kann man hier 
+			NO_ATTACK_BUSY[_SET|_QUERY] setzen
+SP_NAME			Name des Spells, falls Mapping
+SP_SHOW_DAMAGE		Treffermeldung soll gezeigt werden.
+SP_REDUCE_ARMOUR	AC soll Typabhaengig reduziert werden.
+SP_PHYSICAL_ATTACK	Koerperlicher Angriff
+SP_RECURSIVE		Rekursionen
+
+Skill-Restrictions:
+SR_FUN			Funktion, die weitere Einschraenkungen prueft
+SR_EXCLUDE_RACE		Ausgeschlossene Rassen
+SR_INCLUDE_RACE		Eingeschlossene Rassen
+SR_GOOD			Align < 
+SR_BAD			Align >
+SR_FREE_HANDS		Benoetigte freie Haende
+SR_SEER			Muss Seher sein
+SR_MIN_SIZE		Mindestgroesse
+SR_MAX_SIZE		Maximalgroesse
+SM_RACE			Rassenspezifische Besonderheiten
+
+Magie-Arten:
+MT_ANGRIFF      	Angriff
+MT_SCHUTZ       	Schutz
+MT_ILLUSION     	Illusion
+MT_BEHERRSCHUNG 	Beherrschung
+MT_HELLSICHT    	Hellsicht
+MT_VERWANDLUNG  	Verwandlung
+MT_BESCHWOERUNG 	Beschwoerung
+MT_BEWEGUNG     	Bewegung
+MT_SAKRAL       	'Goettliches'
+MT_HEILUNG      	Heilung
+MT_CREATION     	Erschaffung
+MT_PSYCHO       	Psycho-Kram
+MT_MISC         	Sonstiges
+
diff --git a/doc/lfun/GiveMiniQuest b/doc/lfun/GiveMiniQuest
new file mode 100644
index 0000000..c08418c
--- /dev/null
+++ b/doc/lfun/GiveMiniQuest
@@ -0,0 +1,43 @@
+GiveMiniQuest()
+
+FUNKTION:
+        int GiveMiniQuest(object winner)
+
+DEFINIERT IN:
+        /secure/questmaster
+
+ARGUMENTE:
+	winner
+	  Spielerobjekt, das die Quest bestanden hat.
+
+RUeCKGABEWERT:
+        (Die Defines fuer den Rueckgabewert finden sich in 
+         /secure/questmaster.h)
+         1 : Hat geklappt                           (OK)
+        -1 : Spieler hat die Quest bereits geloest  (MQ_ALREADY_SET)
+        -2 : Ungueltiger Questname                  (MQ_KEY_INVALID)
+        -3 : Unbefugter Zugriff                     (MQ_ILLEGAL_OBJ)
+        -4 : Quest zur Zeit inaktiv                 (MQ_IS_INACTIVE)
+        -5 : Gaeste loesen keine Quests             (MQ_GUEST)
+
+BESCHREIBUNG:
+	Mit dieser Funktion wird nach dem erfolgreichen Loesen einer 
+        MiniQuest die Quest im Spieler eingetragen. Dabei muss der
+        Aufruf in dem Objekt erfolgen, welches im Questmaster 
+        eingetragen ist. Als Argument muss das Spielerobjekt 
+	angegeben werden, welches die Quest bestanden hat.
+        
+BEISPIEL:
+	QM->GiveMiniQuest(this_player());
+
+SIEHE AUCH:
+        HasMiniQuest
+        /secure/questmaster.h, /secure/questmaster.c
+
+HINWEIS:
+        Die Informationen fuer den EM fuer Quests sollten diesem
+        beim Eintragen der Miniquest in den Questmaster per Mail
+        zugeschickt werden. Siehe dazu die Hilfe zu AddMiniQuest.
+
+----------------------------------------------------------------------------
+Zuletzt geaendert: Don, 30. Jan 2014, 22:14:12 von Ark.
diff --git a/doc/lfun/GiveQuest b/doc/lfun/GiveQuest
new file mode 100644
index 0000000..613a501
--- /dev/null
+++ b/doc/lfun/GiveQuest
@@ -0,0 +1,63 @@
+GiveQuest()
+
+FUNKTION:
+        varargs int GiveQuest(string questname, string message)
+
+DEFINIERT IN:
+        /std/player/quests.c
+
+ARGUMENTE:
+        questname
+          Questname, wie er im Questmaster eingetragen wurde.
+        message 
+          Optionale Meldung, die auf dem Abenteuer-Kanal statt der 
+          Standardmeldung gesendet wird.
+          Dabei wird @@name@@ durch den Spielernamen ersetzt.
+
+RUeCKGABEWERT:
+        (Die Defines fuer den Rueckgabewert finden sich in 
+         /secure/questmaster.h)
+         1 : Hat geklappt                           (OK)
+        -1 : Spieler hat die Quest bereits geloest  (GQ_ALREADY_SET)
+        -2 : Ungueltiger Questname                  (GQ_KEY_INVALID)
+        -3 : Unbefugter Zugriff                     (GQ_ILLEGAL_OBJ)
+        -4 : Quest zur Zeit inaktiv                 (GQ_IS_INACTIVE)
+
+BESCHREIBUNG:
+        Mit dieser Funktion wird nach dem erfolgreichen Loesen einer 
+        Quest die Quest im Spieler eingetragen. Dabei muss der Aufruf 
+        in dem Objekt erfolgen, welches im Questmaster eingetragen ist.
+        Zusaetzlich wird der Zeitpunkt eingetragen, an dem die Quest
+        bestanden wurde.
+        
+        Wer sich da nicht sicher ist, kann mit dem Questtool 
+        (/obj/tools/questtool) nachsehen. 
+
+        Nachdem eine Quest als geloest markiert wurde, ist dies in einem 
+        Logfile fuer die Quest im Verzeichnis /log/quest einzutragen. Dazu
+        wird write_file verwendet.
+
+BEISPIEL:
+
+        int quest;
+
+        quest = this_player()->GiveQuest("Zacharias Eispalast");
+
+        if (quest == 1)
+        {
+          write("Du fuehlst, wie Deine Erfahrung ansteigt.\n");
+          write_file("/log/quest/eispalast", 
+                     dtime(time())+"   Aufgabe geloest von "
+                     +this_player()->name()+"\n");
+        } 
+        else if (quest != -1)
+          write( "Die Weltenmaschine will Dir Deine Arbeit "
+                 +"nicht anerkennen.\n"
+                 +"Frage einen Erzmagier um Hilfe.\n" );
+
+SIEHE AUCH:
+        /secure/questmaster.h, /obj/tools/questtool
+        QueryQuest(), write_file(), ModifyQuestTime()
+
+----------------------------------------------------------------------------
+Zuletzt geaendert: Son, 27. Apr 2014, Arathorn
diff --git a/doc/lfun/GroupName b/doc/lfun/GroupName
new file mode 100644
index 0000000..7a81c45
--- /dev/null
+++ b/doc/lfun/GroupName
@@ -0,0 +1,63 @@
+GroupName()
+FUNKTION:
+     string GroupName(string grp)
+
+DEFINIERT IN:
+     /p/daemon/materialdb.c (MATERIALDB)
+
+ARGUMENTE:
+     string grp - ein Gruppenname
+
+BESCHREIBUNG:
+     Gibt die Langbeschreibung des Gruppennamens zurueck.
+
+RUECKGABEWERT:
+     Die Gruppenbeschreibung oder "Unbekanntes"
+
+BEISPIELE:
+     // simpel
+     tmp=m_indices(ob->QueryProp(P_MATERIAL));
+     write("Dieses Objekt gehoert u.a. zur Gruppe "+
+           MATERIALDB->GroupName(MATERIALDB->GetMatMembership(tmp[0])[0])+
+           ".\n");
+     // gibt die erste Gruppenzugehoerigkeit des erste Materials in
+     // P_MATERIAL zurueck (bei MATGROUP_METAL z.B. "... zur Gruppe Metalle.")
+
+     // ein weiser Schmied:
+     int i;
+     string *mat, mname, mgroup;
+     mat=m_indices(ob->QueryProp(P_MATERIAL));
+     i=sizeof(mat);
+
+     write("Der Schmied sagt: "+ob->Name(WER)+" besteht aus ...\n");
+     while(i--) {
+      // den Namen erkennen/aussprechen:
+      // Materialien werden allgemein ganz schlecht erkannt (zu 5%), aber
+      // alles aus Metall wird zu +100% gut erkannt ...
+      mname=MATERIALDB->MaterialName(mat[i], WER,
+	     ({5, ([MATERIAL_SYMMETRIC_RECOGNIZABILITY:
+			({MATGROUP_METAL, 100})])}));
+
+      // und nur Metalle analysieren ...
+      if(MATERIALDB->MaterialGroup(([mat[i]:100]),MATGROUP_METAL)>=100) {
+       int j;
+       string *mgr;
+       mgr=MATERIALDB->GetMatMembership(mat[i]);
+       j=sizeof(mgr);
+       mgroup=" gehoert zu ";
+       while(j--) {
+        mgroup+=MATERIALDB->GroupName(mgr[j]);
+        if(j>0) mgroup+=", ";
+       }
+      } else mgroup=" kenne ich nicht";
+      printf("%-12.12s: %s\n",mname, mgroup);
+     }
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/GuardExit b/doc/lfun/GuardExit
new file mode 100644
index 0000000..0ba53c4
--- /dev/null
+++ b/doc/lfun/GuardExit
@@ -0,0 +1,85 @@
+FUNKTION:
+     protected <int|<string|closure>* >* GuardExit(object room, int hookid,
+                                                   <string|closure>* hdata);
+
+DEFINIERT IN:
+     /std/npc/moving.c
+
+ARGUMENTE:
+     room
+          Der den Hook ausloesende Raum (environment())
+     hookid
+          Die ID des Hooks (H_HOOK_EXIT_USE)
+     hdata
+          Ein Array mit 3 Elementen:
+          ({
+              verb - Das Kommandoverb/Ausgangsname (string)
+              dest - Zielraum (string) oder closure (special exits)
+              msg  - Bewegungsrichtung (string) oder 0
+          })
+
+BESCHREIBUNG:
+     Ist diese Funktion in einem NPC definiert, traegt sich der NPC bei jeder
+     Bewegung automatisch als Konsument von H_HOOK_EXIT_USE ein und diese
+     Funktion wird immer gerufen, wenn ein Lebewesen im gleichen Environment
+     wie der NPC versucht, einen Ausgang zu benutzen.
+     Der NPC kann dann die Bewegung abbrechen (und so den Ausgang
+     blockieren), Ziel oder Bewegungsmeldung aendern oder keine Veraenderung
+     vornehmen.
+     Die Konstanten fuer Hookid und Hookstatus (s. Rueckgabewert) sind in
+     <hook.h> definiert.
+
+RUeCKGABEWERT:
+     Array mit zwei Elementen:
+     ({
+         H_NO_MOD / H_ALTERED / H_CANCELLED,
+         hdata
+     })
+     hdata ist ggf. das geaenderte Array, welches die Funktion uebergeben
+     wird. Weitere Details s. auch /doc/std/hooks
+
+BEMERKUNGEN:
+     Die Anzahl an Konsumenten eines Hooks ist begrenzt. Es ist daher
+     moeglich, dass die Registrierung nicht klappt, wenn zuviele (>5) Waechter
+     im Raum sind. Will man darauf reagieren, muesste man die Registrierung
+     pruefen.
+     Ein NPC, welcher GuardExit() definiert, aber im aktuellen Environment
+     keine Wache halten will, könnte sich selber de-registrieren.
+
+BEISPIELE:
+     <int|<string|closure>* >* GuardExit(object room, int hookid,
+                                         <string|closure>* hdata)
+     {
+       // Nur in Gefaengnisraeumen Wache halten
+       if (strstr(object_name(environment()), "/room/jail")==0)
+       {
+         // Hier gehts nicht raus. ;-)
+         if (hdata[0] == "raus") {
+             tell_object(this_player(), ...);
+             return ({H_CANCELLED, hdata});
+         }
+         // Special exits ersetzen wir durch einen eigenen
+         else if (closurep(hdata[1])) {
+           hdata[1] = #'my_special_exit;
+           return ({H_ALTERED, hdata});
+         }
+         // alle anderen Ausgaenge in die persoenliche Zelle
+         else {
+           tell_object(this_player(), ...);
+           hdata[1] = "/room/jail_"+getuid(this_player());
+           hdata[2] = "Sueden";
+           return ({H_ALTERED, hdata});
+         }
+      }
+      // in allen anderen Raeumen nicht eingreifen
+      return ({H_NO_MOD, hdata});
+    }
+
+
+SIEHE AUCH:
+     AddExit, AddSpecialExit, RemoveExit
+     HRegisterToHook, HRegisterModifier, HUnregisterFromHook
+     /doc/std/hooks
+----------------------------------------------------------------------------
+20.02.2016, Zesstra
+22.05.2016, Mupfel (Beispiel korrigiert)
diff --git a/doc/lfun/Halt b/doc/lfun/Halt
new file mode 100644
index 0000000..06bfda4
--- /dev/null
+++ b/doc/lfun/Halt
@@ -0,0 +1,30 @@
+Halt()
+
+FUNKTION:
+     void Halt();
+
+DEFINIERT IN:
+     /std/transport.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Die Fahrt des Transporters wird abgebrochen. Sie muss explizit mit
+     Start() wieder aufgenommen werden!
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Es gibt keine einfache Moeglichkeit, die Fahrt eines Transporters an
+     der Stelle wieder aufzunehmen, an der sie unterbrochen wurde. Man
+     muesste die aktuelle Position mit QueryPosition() ermitteln, mit Hilfe
+     der AddXXX()-Aufrufe in eine Positionsnummer umwandlen und diese an
+     Start() uebergeben.
+
+SIEHE AUCH:
+     Start(), /std/transport.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:19:23 1996 by Wargon
diff --git a/doc/lfun/HasMiniQuest b/doc/lfun/HasMiniQuest
new file mode 100644
index 0000000..76fc394
--- /dev/null
+++ b/doc/lfun/HasMiniQuest
@@ -0,0 +1,37 @@
+HasMiniQuest()
+
+FUNKTION:
+        int HasMiniQuest(mixed pl, mixed name)
+
+DEFINIERT IN:
+        /secure/questmaster
+
+ARGUMENTE:
+        pl
+          Name des Spielers oder das Spielerobjekt
+        name
+          Filename des Objekts oder das Objekt selbst, welches die Miniquest
+          im Spieler eintragen darf, oder die Nummer (int) der Miniquest
+
+RUeCKGABEWERT:
+         1 : Der Spieler hat die MiniQuest
+         0 : Der Spieler hat die MiniQuest noch nicht
+        -2 : angegebene Miniquest existiert nicht
+        -3 : falsche Datentypen fuer pl oder name uebergeben
+
+BESCHREIBUNG:
+        Mit dieser Funktion kann getestet werden, ob ein Spieler eine 
+        MiniQuest bereits geloest hat. Dabei ist entweder der Filename des 
+        Objektes anzugeben, das die Quest im Spieler eintragen darf oder
+        das Objekt selbst.
+        
+BEISPIEL:
+        if( QM->HasMiniQuest(getuid(this_player()), this_object())!=1 )
+          write("Du bist noch nicht reif genug.\n");
+
+SIEHE AUCH:
+        GiveMiniQuest(L)
+        /secure/questmaster.h, /secure/questmaster.c
+
+----------------------------------------------------------------------------
+Zuletzt geaendert: 2014-Feb-03, Arathorn.
diff --git a/doc/lfun/HitFunc b/doc/lfun/HitFunc
new file mode 100644
index 0000000..3d22075
--- /dev/null
+++ b/doc/lfun/HitFunc
@@ -0,0 +1,68 @@
+HitFunc()
+
+FUNKTION:
+     int HitFunc(object enemy);
+
+DEFINIERT IN:
+     eigenen Objekten, fuer /std/weapon/combat.c
+
+ARGUMENTE:
+     enemy
+          Der Gegner, gegen den die Waffe eingesetzt wird.
+
+BESCHREIBUNG:
+     Die Waffe kann anhand des Gegners enemy entscheiden, ob ein
+     Schadensbonus oder auch ein Schadensmalus wirksam wird. Dieser Bonus
+     wird zu dem normalen Schaden der Waffe hinzuaddiert.
+
+RUeCKGABEWERT:
+     Der Schadensbonus bzw. der Abschlag.
+
+BEMERKUNGEN:
+     Wenn durch den Bonus die Genehmigungsgrenzen der Balance
+     ueberschritten werden koennen, muss man seinen Regionsmagier und
+     die Objekt-Balance konsultieren!
+
+     Werte der HitFunc sollten immer ueber ein random() zurueckgegeben 
+     werden!
+
+     Diese Funktion sollte die Waffe nicht zerstoeren! Falls ihr im Kampf eine
+     Waffe (ggf. in Abhaengigkeit vom Gegner) zerstoeren wollt, nutzt dazu
+     bitte TakeFlaw(). 
+
+BEISPIELE:
+     Eine Waffe, die gegen Orks besonders gut wirkt:
+
+     inherit "std/weapon";
+
+     #include <properties.h>
+     #include <combat.h>
+     #include <class.h>
+
+     create(){
+       if(!clonep(this_object())) {
+           set_next_reset(-1);
+           return;
+       }
+       ::create();
+       /* 
+       zig SetProp's, um die Waffe zu konfigurieren
+       HitFunc() ist in der Waffe selbst definiert 
+       */
+       SetProp(P_HIT_FUNC, this_object());
+     }
+
+     int HitFunc(object enemy)
+     {
+       /* laesst sich der Gegner als Ork ansprechen? */
+       if (enemy->is_class_member(CL_ORC))
+         return random(10+random(50)); /* Ja => Schaden erhoehen */
+
+       return 0;   /* ansonsten keinen zusaetzlichen Schaden anrichten */
+     }
+
+SIEHE AUCH:
+     QueryDefend(), /std/weapon.c
+     TakeFlaw()
+----------------------------------------------------------------------------
+11.08.2007, Zesstra
diff --git a/doc/lfun/Identify b/doc/lfun/Identify
new file mode 100644
index 0000000..836e58b
--- /dev/null
+++ b/doc/lfun/Identify
@@ -0,0 +1,24 @@
+Identify()
+
+FUNKTION:
+     void Identify(object ob);
+
+DEFINIERT IN:
+     /std/corpse.c
+
+ARGUMENTE:
+     ob
+          Das kuerzlich nach langem, hartem Kampf verstorbene Objekt.
+
+BESCHREIBUNG:
+     In dieser Funktion werden Lang- und Kurzbeschreibung der Leiche gesetzt
+     sowie die Verwesung in Gang gesetzt.
+
+RUeCKGABEWERT:
+     keiner
+
+SIEHE AUCH:
+     /std/corpse.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:19:57 1996 by Wargon
diff --git a/doc/lfun/InFight b/doc/lfun/InFight
new file mode 100644
index 0000000..086c5bd
--- /dev/null
+++ b/doc/lfun/InFight
@@ -0,0 +1,32 @@
+InFight()
+
+FUNKTION:
+        mixed InFight()
+
+ARGUMENTE:
+        keine
+
+BESCHREIBUNG:
+        Diese Funktion wurde dazu geschrieben, um herauszufinden,
+        ob sich ein PC/NPC direkt im Kampf befindet. Dazu wird
+        das Array mit den Namen der Feinden des PC/NPCs durch die
+        Funktion environment gefiltert und so festgestellt, ob
+        die Umgebung der beiden Feinde gleich ist.
+
+RUECKGABEWERT: 
+        Als Rueckgabewert enthaelt man entweder 0, wenn das Objekt
+        im Moment keine Feinde hat bzw. die nicht im selben Raum
+        sind, oder aber das Feindobjekt, das als erstes im Array
+        steht und anwesend ist.
+
+BEISPIEL:
+        Selbsterklaerend ;)
+
+BEMERKUNG:
+        InFight() gibt lediglich das Ergebnis von EnemyPresent() zurueck.
+
+SIEHE AUCH:
+        EnemyPresent(), PresentEnemies()
+        /std/living/combat.c
+
+22.03.2009, Zesstra
diff --git a/doc/lfun/InList b/doc/lfun/InList
new file mode 100644
index 0000000..adcd431
--- /dev/null
+++ b/doc/lfun/InList
@@ -0,0 +1,28 @@
+InList()
+
+FUNKTION:
+     int InList(object room, int *potionlist, int *knownlist)
+
+DEFINIERT IN:
+     /secure/potionmaster.c
+
+ARGUMENTE:
+     Implizit: previous_object() - Spielerobjekt, das ZT bekommen soll
+     object room                 - Objekt, welches ZT vergeben will
+     int* potionlist             - Liste der ZTs des Spielers
+     int* knownlist              - Liste der bekannten ZTs des Spielers
+
+BESCHREIBUNG:
+     Prueft, ob 'room' einen ZT vergeben darf und gibt zurueck, ob die
+     Nummer dieses ZTs in der 'knownlist' enthalten ist.
+
+RUeCKGABEWERT:
+     0  Kein aktiver ZT oder nicht in Liste enthalten.
+     1  Aktiver ZT und in Liste.
+
+SIEHE AUCH:
+     Sonstiges: zaubertraenke, /room/orakel.c
+     Verwandt:  FindPotion(), RemoveKnownPotion(), AddKnownPotion()
+     Props:     P_POTIONROOMS, P_KNOWN_POTIONROOMS
+
+6.Feb 2016 Gloinson
diff --git a/doc/lfun/InformAlcoholEffect b/doc/lfun/InformAlcoholEffect
new file mode 100644
index 0000000..0fa5297
--- /dev/null
+++ b/doc/lfun/InformAlcoholEffect
@@ -0,0 +1,43 @@
+InformAlcoholEffect
+
+FUNKTION:
+	void InformAlcoholEffect(object pl, int msgid, int area)
+
+DEFINIERT IN:
+     eigenen Objekten; fuer /std/living/life.c
+ 
+ARGUMENTE:
+	pl
+          Das Lebewesen, das alkoholisiert ist.
+        msgid
+          Flag fuer die Meldung, die ausgegeben wird.
+        area
+          Aufruf erfolgt in Gilde (0) oder im Environment (1).
+
+BESCHREIBUNG:
+        Wenn ein Lebewesen alkoholisiert ist, werden in unregelmaessigen
+        Abstaenden Meldungen an die Umgebung und ans Lebewesen ausgegeben.
+        Gleichzeitig wird die Funktion InformAlcoholEffect in der Gilde
+        und im Environment des Lebewesens aufgerufen.
+        
+        Es gibt vier verschiedene Meldungen (definiert in /sys/health.h):
+        ALC_EFFECT_HICK      (0) : "<Hick>! Oh, Tschuldigung.\n"
+        ALC_EFFECT_RUELPS    (1) : "Du ruelpst.\n"
+        ALC_EFFECT_LOOKDRUNK (2) : "Du fuehlst Dich benommen.\n"
+        ALC_EFFECT_STUMBLE   (3) : "Du stolperst.\n"
+        
+        Wird die Funktion in einem Gildenraum implementiert, so kann man
+        anhand des 3. Parameters unterscheiden, ob die Gilde als Gilde
+        oder als Raum gemeint ist (die Funktion wird je einmal aufgerufen):
+        ALC_EFFECT_AREA_GUILD (0) : der Aufruf betrifft die Gilde,
+        ALC_EFFECT_AREA_ENV   (1) : der Aufruf betrifft die Umgebung.
+
+RUeCKGABEWERT:
+	keiner
+
+SIEHE AUCH:
+        P_ALCOHOL, /sys/health.h
+
+----------------------------------------------------------------------------
+Last modified: Thu May 23 23:08:00 2002 by Mupfel
+
diff --git a/doc/lfun/InformDefend b/doc/lfun/InformDefend
new file mode 100644
index 0000000..eb36921
--- /dev/null
+++ b/doc/lfun/InformDefend
@@ -0,0 +1,62 @@
+InformDefend()
+
+FUNKTION:
+	void InformDefend(object enemy);
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	enemy
+	  Der Feind, der ein zu verteidigendes Lebewesen angegriffen hat.
+
+BESCHREIBUNG:
+	Es ist moeglich, dass Objekte ueber Angriffe auf Lebewesen
+	informiert werden, sofern diese Objekte bei dem angegriffenen
+	Lebewesen mittels AddDefender() angemeldet wurden und sich der
+	selben Umgebung befinden.
+	Zumeist wird es sich bei den Objekten natuerlich ebenfalls um
+	andere Lebewesen handeln, die das Lebewesen, bei dem sie angemeldet
+	sind, verteidigen sollen.
+	Bei einem Angriff auf das Lebewesen werden alle Objekte per Aufruf
+	von InformDefend() darueber informiert, wobei der Angreifer als
+	Parameter uebergeben wird.
+	Standardmaessig ist diese Funktion in Lebewesen bereits definiert,
+	wobei der Skill SK_INFORM_DEFEND, sofern vorhanden, aufgerufen wird.
+
+BEISPIEL:
+	Sehr beliebt sind in Gilden NPCs, die den Beschwoerer begleiten und
+	verteidigen, z.B. beschworene Daemonen:
+	  inherit "std/npc";
+	  include <properties.h>
+	  object owner;
+	  void create()
+	  { ::create();
+	    SetProp(P_NAME,"Daemon");
+	    ...
+	  }
+	// nach Clonen des Daemons folgende Funktion mit Beschwoerer als
+	// Parameter aufrufen
+	  Identify(object caster)
+	  { if(!objectp(caster))
+	      call_out(#'remove,0);
+	    owner=caster;
+	    owner->AddDefender(this_object());
+	  }
+	// folgende Funktion wird automatisch aufgerufen, wenn der
+	// Beschwoerer angegriffen wird
+	  void InformDefend(object enemy)
+	  { if(!IsEnemy(enemy)&&enemy!=owner)
+	      Kill(enemy);
+	  }
+	Soll der Daemon sich auch in ein Team einordnen, in welchem sich der
+	Beschwoerer eventuell befindet, so ist zusaetzlich AssocMember() in
+	diesem Beschwoerer aufzurufen, wobei der Daemon als Parameter
+	uebergeben wird.
+
+SIEHE AUCH:
+	AddDefender(), RemoveDefender(), DefendOther(), Kill(), IsEnemy(),
+	P_DEFENDERS, /std/living/combat.c, /sys/new_skills.h
+
+----------------------------------------------------------------------------
+Last modified: Thu Jul 29 18:48:45 1999 by Patryn
diff --git a/doc/lfun/InformRowChange b/doc/lfun/InformRowChange
new file mode 100644
index 0000000..e2aa8fc
--- /dev/null
+++ b/doc/lfun/InformRowChange
@@ -0,0 +1,23 @@
+InformRowChange()
+
+FUNKTION:
+	varargs void InformRowChange(int from, int to, object caster);
+
+DEFINIERT IN:
+	/std/living/team.c
+
+ARGUMENTE:
+	from   - Reihe, in die der Spieler vorher war.
+	to     - Reihe in der der Spieler jetzt ist.
+	caster - Der Spieler, falls die Funktion in der Gilde aufgerufen wird.
+        
+BESCHREIBUNG:
+	Diese Funktion wird im Spieler und in seiner Gilde aufgerufen,
+	falls sich die aktuelle Kampfreihe in der Teamaufstellung
+	aendert.
+
+RUECKGABEWERT:
+	Keiner.
+
+SIEHE AUCH:
+	teams
diff --git a/doc/lfun/InformUnwear b/doc/lfun/InformUnwear
new file mode 100644
index 0000000..9471028
--- /dev/null
+++ b/doc/lfun/InformUnwear
@@ -0,0 +1,29 @@
+InformUnwear
+
+FUNKTION:
+	protected void InformUnwear(object pl, int silent, int all)
+
+DEFINIERT IN:
+        /std/clothing/wear.c
+
+ARGUMENTE:
+	pl
+          Das Lebewesen, das die Ruestung ausgezogen hat.
+        silent
+          Flag, ob Meldungen unterdruckt werden sollen.
+        all
+          Flag, ob die Ruestung mit "ziehe alles aus"/
+          "ziehe alle ruestungen aus" ausgezogen wurde.
+
+BESCHREIBUNG:
+        Diese Funktion wird aufgerufen, wenn die Ruestung erfolgreich
+        ausgezogen wurde.
+
+RUeCKGABEWERT:
+	keiner
+
+SIEHE AUCH:
+        InformUnwield(), InformWield(), InformWear()
+
+----------------------------------------------------------------------------
+06.10.2007, Zesstra
diff --git a/doc/lfun/InformUnwield b/doc/lfun/InformUnwield
new file mode 100644
index 0000000..ef2d9bc
--- /dev/null
+++ b/doc/lfun/InformUnwield
@@ -0,0 +1,26 @@
+InformUnwield
+
+FUNKTION:
+	protected void InformUnwield(object pl, int silent)
+
+DEFINIERT IN:
+	/std/weapon/combat.c
+
+ARGUMENTE:
+	pl
+          Das Lebewesen, das die Waffe gezueckt hatte.
+        silent
+          Flag, ob Meldungen unterdruckt werden sollen.
+
+BESCHREIBUNG:
+        Diese Funktion wird aufgerufen, wenn die Waffe erfolgreich
+        weggesteckt wurde.
+
+RUeCKGABEWERT:
+	keiner
+
+SIEHE AUCH:
+        InformWield(), InformWear(), InformUnwear()
+
+----------------------------------------------------------------------------
+06.10.2007, Zesstra
diff --git a/doc/lfun/InformWear b/doc/lfun/InformWear
new file mode 100644
index 0000000..4c7c9b4
--- /dev/null
+++ b/doc/lfun/InformWear
@@ -0,0 +1,29 @@
+InformWear
+
+FUNKTION:
+	protected void InformWear(object pl, int silent, int all)
+
+DEFINIERT IN:
+        /std/clothing/wear.c
+
+ARGUMENTE:
+	pl
+          Das Lebewesen, das die Ruestung angezogen hat.
+        silent
+          Flag, ob Meldungen unterdruckt werden sollen.
+        all
+          Flag, ob die Ruestung mit "trage alles"/"trage alle ruestungen"
+          angezogen wurde.
+
+BESCHREIBUNG:
+        Diese Funktion wird aufgerufen, wenn die Ruestung erfolgreich
+        angezogen wurde.
+
+RUeCKGABEWERT:
+	keiner
+
+SIEHE AUCH:
+        InformUnwield(), InformWield(), InformUnwear()
+
+----------------------------------------------------------------------------
+06.10.2007, Zesstra
diff --git a/doc/lfun/InformWield b/doc/lfun/InformWield
new file mode 100644
index 0000000..67c6b98
--- /dev/null
+++ b/doc/lfun/InformWield
@@ -0,0 +1,26 @@
+InformWield
+
+FUNKTION:
+	protected void InformWield(object pl, int silent)
+
+DEFINIERT IN:
+	/std/weapon/combat.c
+
+ARGUMENTE:
+	pl
+          Das Lebewesen, das die Waffe gezueckt hat.
+        silent
+          Flag, ob Meldungen unterdruckt werden sollen.
+
+BESCHREIBUNG:
+        Diese Funktion wird aufgerufen, wenn die Waffe erfolgreich
+        gezueckt wurde.
+
+RUeCKGABEWERT:
+	keiner
+
+SIEHE AUCH:
+        InformUnwield(), InformWear(), InformUnwear()
+
+----------------------------------------------------------------------------
+06.10.2007, Zesstra
diff --git a/doc/lfun/InsertEnemy b/doc/lfun/InsertEnemy
new file mode 100644
index 0000000..03d1d7d
--- /dev/null
+++ b/doc/lfun/InsertEnemy
@@ -0,0 +1,28 @@
+gefunden : lfun/InsertEnemy
+
+FUNKTION:
+        int InsertEnemy(object enemy)
+        
+DEFINIERT IN:
+        /std/living/combat.c
+        
+ARGUMENTE:
+        enemy: Das Lebewesen, das angegriffen werden soll.
+        
+BESCHREIBUNG:
+        Nach Aufruf der Funktion wird das Wesen in die Liste der Feinde
+        aufgenommen.
+        In jedem heart_beat() wird dann ein anwesender Feind
+        angegriffen.
+        
+RUECKGABEWERT:
+        0, falls der Feind nicht existiert, kein Lebewesen ist, ein Geist ist, 
+           oder im Gegner P_NO_ATTACK gesetzt ist, oder schon angegegriffen 
+           wurde
+        1  sonst
+        
+SIEHE AUCH:
+        Kill, IsEnemy, StopHuntFor, P_NO_ATTACK
+
+LETZTE AENDERUNG:
+26.08.2010, Zesstra
diff --git a/doc/lfun/InsertEnemyTeam b/doc/lfun/InsertEnemyTeam
new file mode 100644
index 0000000..e9ade63
--- /dev/null
+++ b/doc/lfun/InsertEnemyTeam
@@ -0,0 +1,46 @@
+
+InsertEnemyTeam()
+
+
+FUNKTION:
+        varargs void InsertEnemyTeam(mixed ens, int rek)
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        ens     object Feind oder object *Feinde
+        rek     Wurde von InsertEnemyTeam aufgerufen
+
+BESCHREIBUNG:
+        Alle Teammitglieder des Feindes bzw. die angegebenen Feinde (aber
+        nicht deren Teammitglieder) werden zu Feinden
+         a) des Spielers und aller Teammitglieder, falls rek==0
+         b) des Spielers, falls rek!=0
+
+RUECKGABEWERT:
+        keiner
+
+BEMERKUNGEN:
+        1. Nur wenn Gegner und Teammitglied im gleichen Raum stehen (aber
+           nicht notwendigerweise im gleichen Raum wie der Spieler) werden
+           sie zu Feinden.
+        2. Falls Teammitglied und Gegner beides Spieler sind, werden sie
+           nicht zu Feinden gemacht.
+        3. Ist der Gegner im eigenen Team, so wird nichts gemacht.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/InsertSensitiveObject b/doc/lfun/InsertSensitiveObject
new file mode 100644
index 0000000..c89cef8
--- /dev/null
+++ b/doc/lfun/InsertSensitiveObject
@@ -0,0 +1,48 @@
+InsertSensitiveObject()
+FUNKTION:
+     void InsertSensitiveObject(object ob, mixed *arg)
+
+DEFINIERT IN:
+     /std/container/inventory.c
+     generalizes /std/living/inventory.c
+
+BESCHREIBUNG:
+     Fuegt "ob" in die Benachrichtigungslisten des Containers ein.
+     Wird von thing/moving.c im Ziel-Environment gerufen, wenn
+     P_SENSITIVE gesetzt ist.
+
+BEMERKUNGEN:
+     Setzt man P_SENSITIVE nicht als Default sondern situationsabhaengig,
+     dann muss man auch InsertSensitiveObject() im Environment
+     auch selbst rufen!
+
+BEISPIEL:
+     // Fackel (inheriting lightsource)
+     // wenn angezuendet, aendert es die Eigenschaften und wird zum
+     // aktiven Objekt - das muss man dem environment() mitteilen
+     static int light(string str) {
+      int i;
+      i=::light(str);
+      if(i && QueryProp(P_LIGHT)>0) {
+       SetProp(P_SENSITIVE,
+        ({({SENSITIVE_INVENTORY_TRIGGER,DT_FIRE,120})}));
+       if(environment())
+        environment()->InsertSensitiveObject(this_object(),
+					     QueryProp(P_SENSITIVE));
+      }
+      return i;
+     }
+
+     - falls ein empfindliches Objekt im environment() ist, dann wird
+       in diesem nun eventuell (Treshold) trigger_sensitive_inv()
+       gerufen
+
+SIEHE AUCH:
+     P_SENSITIVE
+     RemoveSensitiveObject
+     insert_sensitive_inv_trigger, insert_sensitive_inv
+     P_SENSITIVE_ATTACK, P_SENSITIVE_INVENTORY, P_SENSITIVE_INVENTORY_TRIGGER
+     CheckSensitiveAttack
+
+25.Apr.2001, Gloinson@MG
+
diff --git a/doc/lfun/InternalModifyAttack b/doc/lfun/InternalModifyAttack
new file mode 100644
index 0000000..0ac2d19
--- /dev/null
+++ b/doc/lfun/InternalModifyAttack
@@ -0,0 +1,37 @@
+InternalModifyAttack(L)
+
+FUNKTION:
+     protected void InternalModifyAttack(mapping ainfo)
+
+DEFINIERT IN:
+     /std/living/combat
+
+ARGUMENTE:
+     mapping ainfo		Werte aus der Attack
+
+BESCHREIBUNG:
+     Dient dazu noch Aenderungen am Verhalten der Attack() vornehmen zu
+     koennen. Die Parameter werden alle per Referenz uebergeben, Aenderungen
+     wirken also direkt in der Attack()!
+     Einfach ueberschreiben (aber ::InternalModifyAttack(&ainfo) nicht
+     vergessen!
+
+     Aufbau von 'ainfo':
+     ([ SI_ENEMY :            object Angreifer,			(-> Defend)
+        SI_SPELL :           0/1/array Spellparameter,		(-> Defend)
+        P_WEAPON :           - oder Waffe,
+        SI_SKILLDAMAGE_MSG:  string Angriffsmeldungsende an Raum,
+        SI_SKILLDAMAGE_MSG2: string Angriffsmeldungsende an Kaempfende,
+        SI_SKILLDAMAGE:      int Schaden in Zehntel HP (Skills schon drin)
+								(-> Defend),
+        SI_SKILLDAMAGE_TYPE: string/string* Schadenstypen,	(-> Defend)
+        P_WEAPON_TYPE:       string Waffentyp (combat.h),
+        P_WC:		     - oder int WC der Waffe/Hand,
+        P_NR_HANDS:	     - oder int Hands der Waffe/Hand,
+     ]);
+
+SIEHE AUCH:
+     InternalModifyDefend(L)
+     Attack(L)
+
+28.03.2008, Zesstra
diff --git a/doc/lfun/InternalModifyDefend b/doc/lfun/InternalModifyDefend
new file mode 100644
index 0000000..93f0d39
--- /dev/null
+++ b/doc/lfun/InternalModifyDefend
@@ -0,0 +1,29 @@
+InternalModifyDefend(L)
+
+FUNKTION:
+     protected void InternalModifyDefend(int dam, mixed dt, mixed spell,
+				      object enemy)
+
+DEFINIERT IN:
+     /std/living/combat
+
+ARGUMENTE:
+     int dam             Staerke des abzuwehrenden Angriffs (in HP/10)
+     string/string* dt   Art(en) des Schadens, der angerichtet werden soll
+     int/mapping spell   0 fuer normale Angriffe (keine Zauber)
+                         1 fuer Zauber (Standardruestungen ignorieren)
+                         mapping fuer mehr Informationen
+     object enemy        der Feind/Schadenverursacher
+
+BESCHREIBUNG:
+     Dient dazu noch Aenderungen am Verhalten der Defend() vornehmen zu
+     koennen. Die Parameter werden alle per Referenz uebergeben, Aenderungen
+     wirken also direkt in der Defend()!
+     Einfach ueberschreiben, aber ::InternalModifyDefend(&..., &....) nicht
+     vergessen!
+
+SIEHE AUCH:
+     InternalModifyAttack(L)
+     Defend(L)
+
+28.03.2008, Zesstra
diff --git a/doc/lfun/IsEnemy b/doc/lfun/IsEnemy
new file mode 100644
index 0000000..c406cd5
--- /dev/null
+++ b/doc/lfun/IsEnemy
@@ -0,0 +1,31 @@
+IsEnemy()
+
+FUNKTION:
+	int IsEnemy(object wer);
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	wer
+	  Das Objekt, welches darauf ueberprueft werden soll, ob es
+	  derzeit als Gegner bekannt ist.
+
+RUeCKGABEWERT:
+	Flag: 1 bei Feindschaft, 0 sonst
+
+BESCHREIBUNG:
+	Mit dieser Funktion kann man ueberpruefen, ob das Objekt <wer>, also
+	im Normalfall ein Lebewesen, derzeit als Gegner erkannt wird. Nach
+	einer gewissen Zeit laeuft die Feindschaft automatisch aus, sodass
+	man sich nicht darauf verlassen, dass ein Gegner auch zukuenftig als
+	solchiger erkannt wird. (Diese Zeit betraegt normal 300 Sekunden.)
+	Solch eine Feindschaft kann im uebrigen auch einseitig sein! Mehr
+	dazu findet man in der Dokumentation zu StopHuntFor(), einer
+	Funktion, die Frieden zwischen Gegnern stiften soll.
+
+SIEHE AUCH:
+	SelectEnemy(), QueryEnemies(), StopHuntFor()
+
+----------------------------------------------------------------------------
+Last modified: Wed May 26 16:47:51 1999 by Patryn
diff --git a/doc/lfun/IsEqual b/doc/lfun/IsEqual
new file mode 100644
index 0000000..806661e
--- /dev/null
+++ b/doc/lfun/IsEqual
@@ -0,0 +1,39 @@
+IsEqual()
+
+FUNKTION
+    int IsEqual(object ob)
+
+DEFINIERT IN:
+    /std/unit.c
+
+ARGUMENTE:
+    ob      Das Objekt das geprueft werden soll.
+
+BESCHREIBUNG:                                                               
+    Diese Funktion prueft ob zwei Objekte vom gleichen Typ sind, also ob
+    z.B. ob und this_object() beides Muenzen sind oder beides Edelsteine.
+    Bei einem Ergebnis != 0 fasst unit.c diese zwei Objekte automatisch
+    zusammen, wenn ob->IsEqual(this_object()) auch einen Wert != 0 ergibt.
+    Hierbei wird das IsEqual() von beiden beteiligten Objekten gerufen und sie
+    muessen uebereinstimmen, dass sie eigentlich das gleiche Objekt sind.
+    Selbstverstaendlich ist diese Funktion nur im Falle von Unitobjekten
+    sinnvoll.
+
+RUeCKGABEWERT:
+    0 - this_object() ist nicht vom selben Typ wie ob
+    1 - this_object() ist vom gleichen Typ wie ob
+
+BEISPIELE:
+    o Ein Unitobjekt das verschiedene Beschreibungen haben kann...
+
+       int IsEqual(object ob)
+       {
+          if (!(int)::IsEqual(ob)) return 0;
+          return (QueryProp(P_SHORT)==ob->QueryProp(P_SHORT));
+       }
+
+SIEHE AUCH:
+    /std/unit.c
+------------------------------------------------------------------------------
+25.01.2015, Zesstra
+
diff --git a/doc/lfun/IsTeamLeader b/doc/lfun/IsTeamLeader
new file mode 100644
index 0000000..eaa008f
--- /dev/null
+++ b/doc/lfun/IsTeamLeader
@@ -0,0 +1,48 @@
+
+IsTeamLeader()
+
+
+FUNKTION:
+        object IsTeamLeader()
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        keine
+
+BESCHREIBUNG:
+        Testet, ob der Spieler ein Teamleiter ist.
+
+RUECKGABEWERT:
+        Falls der Spieler Teamleiter ist, wird das Teamobjekt zurueckgegeben,
+        sonst 0.
+
+BEISPIEL:
+        string leader_test(object pl)
+        {
+         ...
+
+         if ( objectp(pl->IsTeamLeader()) )
+          return "Als Leiter eines Teams hast Du grosse Verantwortung zu "
+            "tragen!";
+         else
+          return "Wenn Du mal Leiter eines Teams sein moechtes, solltest "
+            "Du Dir vorher der Verantwortung bewusst werden!";
+        }
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/IsTeamMove b/doc/lfun/IsTeamMove
new file mode 100644
index 0000000..844b041
--- /dev/null
+++ b/doc/lfun/IsTeamMove
@@ -0,0 +1,40 @@
+
+IsTeamMove()
+
+
+FUNKTION:
+        object IsTeamMove()
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        keine
+
+BESCHREIBUNG:
+        Testet, ob momentan die Angriffsbewegung ausgefuehrt wird.
+
+RUECKGABEWERT:
+        Falls die Angriffsbewegung gerade ausgefuehrt wird, wird das
+        Teamobjekt zurueckgegeben, sonst 0.
+
+BEMERKUNGEN:
+        Wird intern benoetigt, damit der Begruessungsschlag verzoegert
+        werden kann, bis alle Teammitglieder ihre Angriffsbewegung
+        ausgefuehrt haben.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/IsUnit b/doc/lfun/IsUnit
new file mode 100644
index 0000000..bce8ab0
--- /dev/null
+++ b/doc/lfun/IsUnit
@@ -0,0 +1,23 @@
+IsUnit()
+
+FUNKTION:
+     int IsUnit();
+
+DEFINIERT IN:
+     /std/unit.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Diese Funktion liefert immer 1 zurueck und zeigt damit an, dass es sich
+     bei diesem Objekt um ein Mengenobjekt handelt.
+
+RUeCKGABEWERT:
+     1 bei allen Objekten, die /std/unit.c erben, ansonsten 0.
+
+SIEHE AUCH:
+     /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:20:19 1996 by Wargon
diff --git a/doc/lfun/Kill b/doc/lfun/Kill
new file mode 100644
index 0000000..a8e4540
--- /dev/null
+++ b/doc/lfun/Kill
@@ -0,0 +1,24 @@
+Kill()
+
+FUNKTION:
+	int Kill(object enemy)
+
+ARGUMENTE:
+	enemy: Der boese, boese Feind.
+
+BESCHREIBUNG:
+	Nach Aufruf der Funktion wird der Feind in jedem
+	heart_beat() attackiert.
+
+RUECKGABEWERT:
+	0,  Falls der Feind nicht existiert
+  -1, falls der Feind ein Geist ist
+  -2, falls der Feind P_NO_ATTACK gesetzt hat
+  -3, falls der Angreifer selber P_NO_ATTACK gesetzt hat
+  -4, falls der Feind bereits angegriffen wurde
+	1   falls der Feind erfolgreich als Find registriert wurde.
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+	InsertEnemy
diff --git a/doc/lfun/LearnSkill b/doc/lfun/LearnSkill
new file mode 100644
index 0000000..6373fbb
--- /dev/null
+++ b/doc/lfun/LearnSkill
@@ -0,0 +1,35 @@
+LearnSkill()
+FUNKTION:
+    public varargs void LearnSkill(string sname, int add, int diff)
+
+DEFINIERT IN:
+    /std/living/skills.c
+
+ARGUMENTE:
+    string sname     der zu lernende Skill
+    string add       Anzahl zu lernender Skillpunkte
+    int diff         Schwierigkeit
+
+BESCHREIBUNG:
+    Die Methode laesst einen interaktiven (eingeloggten) Spieler den
+    Skill 'sname' um 'add' Punkte lernen.
+
+    Dabei wird sichergestellt, dass 'add' den Wert MAX_SKILLEARN nicht
+    ueberschreitet, der Skill nicht verschwindet und fuer uebergeordnete
+    Skills (SI_INHERIT) dieser uebergeordnete Skill auch einen Lerneffekt
+    erfaehrt.
+
+    Wird zB von Learn (spellbook) und SpellSuccess (spellbook) gerufen.
+
+SIEHE AUCH:
+    Skills Lernen:  ModifySkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung
+    * Properties:   P_NEWSKILLS
+    Spellbook:      Learn, SpellSuccess
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/Leave b/doc/lfun/Leave
new file mode 100644
index 0000000..b3b52d2
--- /dev/null
+++ b/doc/lfun/Leave
@@ -0,0 +1,40 @@
+Leave()
+
+FUNKTION:
+     int Leave();
+
+DEFINIERT IN:
+     /std/transport.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Wenn sich der Spieler auf einem Transporter befindet, und dieser sich
+     momentan an einer Haltestelle liegt, verlaesst der Spieler den
+     Transporter.
+
+RUeCKGABEWERT:
+     Null, wenn der Spieler den Transporter nicht verlassen konnte, sonst
+     ungleich Null.
+
+BEMERKUNGEN:
+     Es werden keine Tests durchgefuehrt, ob der Transporter ueberhaupt
+     angesprochen wurde! Das muss man selber machen.
+
+BEISPIELE:
+
+     int myLeave(string str)
+     {
+       if (str && id(str))
+         return Leave();
+
+       notify_fail("Was willst Du verlassen?\n");
+       return 0;
+     }
+
+SIEHE AUCH:
+     Enter(), /std/transport.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:20:30 1996 by Wargon
diff --git a/doc/lfun/LimitAbility b/doc/lfun/LimitAbility
new file mode 100644
index 0000000..e6a2810
--- /dev/null
+++ b/doc/lfun/LimitAbility
@@ -0,0 +1,47 @@
+LimitAbility()
+FUNKTION:
+    protected varargs mixed LimitAbility(mixed sinfo, int diff)
+
+DEFINIERT IN:
+    /std/living/skills.c
+
+ARGUMENTE:
+    mixed sinfo      Skill-Informationen
+    int diff         Schwierigkeit
+
+BESCHREIBUNG:
+    Setzt ein Maximum an Skillfertigkeit basierend auf der Schwierigkeit
+    und dem P_LEVEL des Spielers.
+    
+    Folgend eine Kopie (!) der Tabelle aus /std/living/skills:
+    diff|lvl 1:|   3:|   7:| 10:| 13:| 16:| 19:| 22:| 25:| 28:| 31:| 34:|
+    ----+------+-----+-----+----+----+----+----+----+----+----+----+----+
+     -50|   83%|  84%|  86%| 87%| 89%| 90%| 92%| 93%| 95%| 96%| 98%| 99%|
+     -10|   69%|  72%|  74%| 77%| 80%| 82%| 85%| 88%| 91%| 93%| 96%| 99%|
+       0|   66%|  69%|  72%| 75%| 78%| 81%| 84%| 87%| 90%| 93%| 96%| 99%|
+      10|   62%|  65%|  69%| 72%| 75%| 79%| 82%| 85%| 89%| 92%| 95%| 98%|
+      20|   59%|  62%|  66%| 70%| 73%| 77%| 80%| 84%| 88%| 91%| 95%| 98%|
+      30|   55%|  59%|  63%| 67%| 71%| 75%| 79%| 83%| 87%| 90%| 94%| 98%|
+      40|   52%|  56%|  60%| 65%| 69%| 73%| 77%| 81%| 86%| 90%| 94%| 98%|
+      50|   49%|  53%|  58%| 62%| 67%| 71%| 76%| 80%| 85%| 89%| 94%| 98%|
+     100|   32%|  38%|  44%| 50%| 56%| 62%| 68%| 74%| 80%| 86%| 92%| 98%|
+     150|   15%|  22%|  30%| 37%| 45%| 52%| 60%| 67%| 75%| 82%| 90%| 97%|
+     200|   -2%|   7%|  16%| 25%| 34%| 43%| 52%| 61%| 70%| 79%| 88%| 97%|
+     250|  -19%|  -8%|   2%| 12%| 23%| 33%| 44%| 54%| 65%| 75%| 86%| 96%|
+     300|  -36%| -24%| -12%|  0%| 12%| 24%| 36%| 48%| 60%| 72%| 84%| 96%|
+     400|  -70%| -55%| -40%|-25%|-10%|  5%| 20%| 35%| 50%| 65%| 80%| 95%|
+     500| -104%| -86%| -68%|-50%|-32%|-14%|  4%| 22%| 40%| 58%| 76%| 94%|
+     600| -138%|-117%| -96%|-75%|-54%|-33%|-12%|  9%| 30%| 51%| 72%| 93%|
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung
+    * Properties:   P_NEWSKILLS
+    Spellbook:      Learn, SpellSuccess
+
+3. Okt 2011 Gloinson
diff --git a/doc/lfun/MaterialGroup b/doc/lfun/MaterialGroup
new file mode 100644
index 0000000..da51cad
--- /dev/null
+++ b/doc/lfun/MaterialGroup
@@ -0,0 +1,37 @@
+MaterialGroup()
+FUNKTION:
+     int MaterialGroup(mapping mats, string grp)
+
+DEFINIERT IN:
+     /p/daemon/materialdb.c (MATERIALDB)
+
+ARGUMENTE:
+     mapping mats - Materialienmapping
+     string grp   - Materialiengruppe
+
+BESCHREIBUNG:
+     Die Materialien im Mapping werden auf Zugehoerigkeit zu der Gruppe
+     untersucht und der Gesamtanteil dieser Materialiengruppe am Mapping
+     in Prozent zurueckgegeben (wenn das Mapping sich auf 100% aufaddiert).
+
+RUECKGABEWERT:
+     int - prozentualer Anteil der Materialiengruppe -100 ... 100 %
+
+BEISPIELE:
+     if(MATERIALDB->MaterialGroup(
+		([MAT_MISC_STONE:40,MAT_AMETHYST:50,MAT_MISC_METAL:10]),
+		MATGROUP_JEWEL)>50)
+      write("Oh ja, darin sind sehr viele Edelsteine!\n");
+
+BEMERKUNGEN:
+     Wird von /std/thing/description::QueryMaterialGroup() gerufen.
+     Bitte an Objekten auch QueryMaterialGroup() verwenden.
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Master:	  AddMaterial(), ConvMaterialList()
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/MaterialList b/doc/lfun/MaterialList
new file mode 100644
index 0000000..17ef526
--- /dev/null
+++ b/doc/lfun/MaterialList
@@ -0,0 +1,65 @@
+MaterialList(L)
+FUNKTION:
+     varargs string MaterialList(int casus, mixed idinf)
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     int casus	 - der Fall, in dem die Materialien dekliniert werden sollen
+     mixed idinf - Dinge, welche die Faehigkeiten des Erkennens beeinflussen:
+		   Einzelne Werte:
+                   * x: allgemeine Erkennung -100 ... 100
+                   * who: der Spieler - P_MATERIAL_KNOWLEDGE wird abgefragt
+                   * fun: wird evaluiert
+                   * what, kann folgendes enthalten:
+                     - Eintrag fuer Materialien ([MAT_XXX:-100...100])
+                     - Eintrag fuer Materialiengruppen (dito)
+                     - ([MATERIAL_SYMMETRIC_RECOGNIZABILITY: mixed mg])
+                       * mg ein Array:
+                         ({MATGROUP_X1,int z1, MATGROUP_X2, int z2, ...})
+                         wobei bei Zugehoerigkeit von string mat zu Gruppe
+                         z<n> auf die Faehigkeit addiert, andernfalls davon
+                         subtrahiert wird
+		   Array mit obigen Werten:
+                   - alle Parameter des Arrays sind optional und additiv
+                   - ({int x, object who, mapping what, closure fun})
+
+BESCHREIBUNG:
+     Listet die Materialien auf, aus denen ein Objekt besteht.
+     Dabei haengt die Genauigkeit der Materialerkennung von idinf ab. D.h.
+     je nach den Faehigkeiten/der angegebenen Faehigkeit wird zB Wolfram
+     als "Wolfram" oder nur als "Metall" erkannt.
+
+     Wenn ein Spieler etwas identifiziert, sollte auch TP uebergeben werden,
+     bei NPCs koennte das anders aussehen.
+
+RUECKGABEWERT:
+     String mit Liste der Materialien.
+
+BEMERKUNGEN:
+     - es werden nur die Materialien angegeben, nicht die Menge.
+     - ruft ConvMaterialList() an der MATERIALDB
+
+BEISPIELE:
+     // simpel
+     write("Der Gegenstand besteht aus"+ob->MaterialList(WEM,TP)+".\n")
+     // -> "Der Gegenstand besteht aus Gold, Silber und Rubin.\n"
+
+     // komplexer
+     ob->SetProp(P_MATERIAL, ([P_NITROGLYCERINE:90,P_GUNPOWDER:10]));
+     write("Das enthaelt "+ob->MaterialList(WER,TP)+".\n");
+     // -> "Das enthaelt Schwarzpulver und Nitroglycerin."
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Methoden:    QueryMaterial(), QueryMaterialGroup()
+     Listen:	  AllMaterials(), AllGroups(), Dump()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+     Sonstiges:	  P_MATERIAL_KNOWLEDGE
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/MaterialName b/doc/lfun/MaterialName
new file mode 100644
index 0000000..0e338a3
--- /dev/null
+++ b/doc/lfun/MaterialName
@@ -0,0 +1,72 @@
+MaterialName()
+FUNKTION:
+     varargs string MaterialName(string mat, int casus, mixed idinf)
+
+DEFINIERT IN:
+     /p/daemon/materialdb.c (MATERIALDB)
+
+ARGUMENTE:
+     string mat	 - das zu erkennende Material
+     int casus	 - der Fall
+     mixed idinf - Dinge, welche die Faehigkeiten des Erkennens beeinflussen
+		   (siehe "man MaterialList")
+
+BESCHREIBUNG:
+     Diese Funktion sucht unter Beruecksichtigung der Erkennungsbe-
+     schraenkungen des Materials und Faehigkeiten des Spielers den
+     Klarnamen des Materials heraus und gibt den zurueck.
+
+RUECKGABEWERT:
+     string: Materialname oder genereller Name.
+
+BEISPIELE:
+     // der hier mag alle existierenden Juwelen, auch wenn welche ergaenzt
+     // werden sollten
+     // Parameter: 1. ein Juwel, 2. Casus, 3. 100% Erkennung - ob er sie
+     // beim Empfang dann auch zu 100% erkennt, steht hier nicht!
+     string* likeit;
+     likeit=MATERIALDB->GetGroupMembers(MATGROUP_JEWEL);
+     ...
+     write("Der Alte sagt: Ich mag "+
+	   MATERIALDB->MaterialName(likeit[random(sizeof(likeit))], WEN, 100)+
+	   ".\n");
+     ...
+
+     // ein weiser schmied:
+     int i;
+     string *mat, mname, mgroup;
+     mat=m_indices(ob->queryprop(p_material));
+     i=sizeof(mat);
+
+     write("der schmied sagt: "+ob->name(wer)+" besteht aus ...\n");
+     while(i--) {
+      // den namen erkennen/aussprechen:
+      // materialien werden allgemein ganz schlecht erkannt (zu 5%), aber
+      // alles aus metall wird zu +100% gut erkannt ...
+      mname=materialdb->materialname(mat[i], wer,
+	     ({5, ([material_symmetric_recognizability:
+			({matgroup_metal, 100})])}));
+
+      // und nur metalle analysieren ...
+      if(materialdb->materialgroup(([mat[i]:100]),matgroup_metal)>=100) {
+       int j;
+       string *mgr;
+       mgr=materialdb->getmatmembership(mat[i]);
+       j=sizeof(mgr);
+       mgroup=" gehoert zu ";
+       while(j--) {
+        mgroup+=materialdb->groupname(mgr[j]);
+        if(j>0) mgroup+=", ";
+       }
+      } else mgroup=" kenne ich nicht";
+      printf("%-12.12s: %s\n",mname, mgroup);
+     }
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName()
+		  GetGroupMembers(), GetMatMembership()
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/MayAddObject b/doc/lfun/MayAddObject
new file mode 100644
index 0000000..d502da4
--- /dev/null
+++ b/doc/lfun/MayAddObject
@@ -0,0 +1,33 @@
+MayAddObject()
+
+FUNKTION:
+     int MayAddObject(object ob);
+
+DEFINIERT IN:
+     /std/container/restrictions.c
+
+ARGUMENTE:
+     ob - Das Object bei dem geprueft werden soll, ob es noch in den
+          Container passt.
+
+BESCHREIBUNG:
+     Wenn ein Objekt in einen Container bewegt wird, prueft move() ueber
+     diese Funktion, ob das Objekt ueberhaupt noch in den Behaelter hinein
+     passt. Dazu uebergibt move() das Objekt das in den Container bewegt
+     werden soll. (In Raeumen ist diese Funktionen ueberschrieben und
+     liefert immer True zurueck.)
+
+RUeCKGABEWERT:
+     1 - wenn das Objekt noch vom Container aufgenommen werden kann.
+     0 sonst.
+
+BEMERKUNGEN:
+     invis-Objekte passen immer in den Container hinein!
+     move() ruft diese Funktion nicht auf, wenn in den Flags M_NOCHECK
+     gesetzt war!
+
+SIEHE AUCH:
+     MayAddWeight(), PreventInsert(), move(), /std/container/restrictions.c
+
+----------------------------------------------------------------------------
+Last modified: 23.09.2007, Zesstra
diff --git a/doc/lfun/MayAddWeight b/doc/lfun/MayAddWeight
new file mode 100644
index 0000000..1cc4e47
--- /dev/null
+++ b/doc/lfun/MayAddWeight
@@ -0,0 +1,47 @@
+MayAddWeight()
+
+FUNKTION:
+     int MayAddWeight(int gewicht);
+
+DEFINIERT IN:
+     /std/container/restrictions.c
+
+ARGUMENTE:
+     gewicht
+          Das zu pruefende Gewicht.
+
+BESCHREIBUNG:
+     Wenn ein Objekt ein einen Behaelter bewegt wird, prueft move() ueber
+     diese Funktion, ob das Objekt ueberhaupt noch in den Behaelter hinein
+     passt. Dazu uebergibt move() dieser Funktion das Gewicht des zu
+     bewegenden Objektes.
+
+RUeCKGABEWERT:
+     0, wenn der Behaelter noch ein gewicht Gramm wiegendes Objekt aufnehmen
+     kann, -1 im anderen Fall.
+
+BEMERKUNGEN:
+     move() ruft diese Funktion nicht auf, wenn in den Flags M_NOCHECK
+     gesetzt war!
+
+BEISPIELE:
+     Die entsprechende Abfrage in /std/thing/moving.c sieht etwa
+     folgendermassen aus:
+
+     int weight;
+
+     ...
+     weight = QueryProp(P_TOTAL_WEIGHT);   // Behaelter? Ja => Gesamtgewicht
+     if (!weight)
+       weight = QueryProp(P_WEIGHT);       // Nein: einfaches Gewicht
+
+     if (ziel->MayAddWeight(weight) == -1) // Passt es noch rein?
+       return ME_TOO_HEAVY;                // Nein, entspr. Fehler zurueckgeben
+
+     ...
+
+SIEHE AUCH:
+     MayAddObject(), PreventInsert(), move(), /std/container/restrictions.c
+
+----------------------------------------------------------------------------
+Last modified: 23.09.2007, Zesstra
diff --git a/doc/lfun/Message b/doc/lfun/Message
new file mode 100644
index 0000000..3635f6e
--- /dev/null
+++ b/doc/lfun/Message
@@ -0,0 +1,23 @@
+Message()
+
+FUNKTION:
+        varargs int Message(string str, int flag)
+
+ARGUMENTE:
+        str: Den zu uebermittelnden Text.
+	flag: Beschreibt die Art der Meldung naeher, siehe
+	      /sys/player/comm.h
+
+BESCHREIBUNG:
+        Einem Spieler wird der Text str uebermittelt.
+
+RUECKGABEWERT:
+        1 bei erfolgreicher Uebermittlung
+        0 wenn der Kobold aktiv war
+       <0 wenn Spieler oder verwendetes Verb ignoriert
+          werden
+
+SIEHE AUCH:
+     P_IGNORE, TestIgnore()
+
+12. Mai 2004 Gloinson
diff --git a/doc/lfun/ModifyQuestTime b/doc/lfun/ModifyQuestTime
new file mode 100644
index 0000000..0020bad
--- /dev/null
+++ b/doc/lfun/ModifyQuestTime
@@ -0,0 +1,44 @@
+ModifyQuestTime()
+
+FUNKTION:
+        int ModifyQuestTime(string questname, int zeit)
+
+DEFINIERT IN:
+        /std/player/quests.c
+
+ARGUMENTE:
+        questname
+          Questname, wie er im Questmaster eingetragen wurde.
+        zeit
+          Zeitpunkt, zu dem die Quest geloest wurde
+
+RUeCKGABEWERT:
+         1 : Questzeitpunkt erfolgreich nachgetragen
+        -1 : keine Zugriffsrechte (nur EM+ und der Tagebuchmaster erlaubt)
+        -2 : Questliste im Spielerobjekt ist unerwartet kein Mapping
+        -3 : Spieler hat diese Quest noch nicht bestanden
+        -4 : kein gueltiger Zeitpunkt uebergeben (kein Integer, Wert < -1
+             oder Wert > time()).
+
+BESCHREIBUNG:
+        Mit dieser Funktion kann der Zeitpunkt nachgetragen werden, zu
+        dem ein Spieler eine bestimmte Quest bereits geloest hat. 
+        Als Questname wird dazu der Name angegeben, der im Questmaster 
+        eingetragen ist. Der Zeitpunkt ist als Integer-Wert im ueblichen
+        time()-Format anzugeben. Uebergibt man -1 als <zeit>, dann wird
+        der Tagebuchmaster erneut versuchen, das Logfile einzulesen, 
+        wenn der Spieler das naechste mal sein Abenteuertagebuch liest.
+
+HINWEIS:
+        Die Funktion mktime() ist nuetzlich, wenn der Zeitpunkt des 
+        Bestehens einer Quest manuell rekonstruiert werden muss, der
+        Zeitpunkt aber nur als Datums-/Zeitangabe in Textform vorliegt 
+        (etwa aus einem Logfile oder aus der Quest-Feedbackmail).
+
+SIEHE AUCH:
+        /secure/questmaster.h, /obj/tools/questtool
+        GiveQuest(L)
+        mktime(E), dtime(SE)
+
+----------------------------------------------------------------------------
+Zuletzt geaendert: Mon, 27. Jan. 2015, Arathorn
diff --git a/doc/lfun/ModifySkill b/doc/lfun/ModifySkill
new file mode 100644
index 0000000..8dd622f
--- /dev/null
+++ b/doc/lfun/ModifySkill
@@ -0,0 +1,73 @@
+ModifySkill()
+FUNKTION:
+    public varargs void ModifySkill(string sname, mixed val,
+                                    int diff, string gilde)
+
+DEFINIERT IN:
+    /std/living/skills.c
+
+ARGUMENTE:
+    string sname     der zu aendernde Skill
+    mixed val        Aenderungswert: int fuer SI_SKILLABILITY, sonst mapping
+    int diff         Schwierigkeit (optional: default ist existierendes diff)
+    string gilde     Gilde (optional: default ist eigene Gilde)
+
+BESCHREIBUNG:
+    Mit der Methode kann man die Werte eines Skills/Spells veraendern. Dabei
+    wird ein Skill immer in ein Mapping umgewandelt (Skills/Spells koennen
+    auch einfach nur ihren Skillwert enthalten, diese ist aequivalent zu
+    einem Mapping mit ([SI_SKILLABILITY:<Skillwert>]).
+
+    Ist 'val' ein int, wird dieses als SI_SKILLABILITY gesetzt. Falls der
+    Skill nur ein int war, wird ein 'diff'!=0 als SI_DIFFICULTY gesetzt.
+
+    Ist 'val' ein Mapping, wird dieses zum Skillmapping addiert.
+    
+    Etwaige SI_SKILLABILITY-Aenderungen laufen danach noch durch LimitAbility.
+
+BEISPIELE:
+    // #1a: Lerne einen Spell/Skill (einer Gilde)
+    caster->ModifySkill("feuerball", MAX_ABILITY, 100, "abenteurer")
+    // #1b: ... oder ...
+    caster->ModifySkill("feuerball", ([SI_SKILLABILITY: MAX_ABILITY]), 100,
+                        "abenteurer")
+    // #1c: ... oder fuer einen Abenteurer ...
+    caster->ModifySkill("feuerball", ([SI_SKILLABILITY: MAX_ABILITY]), 100);
+    
+    // #2: Setze eine Skill-Funktion fuer einen Kampfskill des Klerus
+    this_player()->ModifySkill(FIGHT(WT_CLUB),
+                               ([SI_SKILLFUNC: "Keulenkampf",
+                                 SI_DIFFICULTY: 100]),
+                               100, "klerus");
+
+    // #3: Speichere im Skill (also Spieler) eine Option fuer diesen Skill
+    // Vorteil: dieser Eintrag wird dem Spell immer uebergeben
+    caster->ModifySkill("blitz", (["klerus_spell_option": 2]));
+
+    (Code in /doc/beispiele/testobjekte/modifyskillspell_test)
+    // #4: Lerne einen unabhaengigen Spell: ACHTUNG
+    // dieser Spell funktioniert nur solange, wie die Closure existiert,
+    // SI_SKILLABILITY und Spell bleiben jedoch im Spieler gespeichert (!)
+    this_player()->ModifySkill("fnrx",
+      ([SI_CLOSURE:
+          function int (object caster, string skill, mapping sinf) {
+            caster->LearnSkill("fnrx", 1);
+            tell_object(caster, "Peng! Dein Skillwert steigt auf "+
+                                caster->QuerySkillAbility("fnrx")+".\n");
+            return ERFOLG;
+          },
+        SI_SKILLABILITY: 8432]),
+      100,
+      "ANY");
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung
+    * Properties:   P_NEWSKILLS
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/ModifySkillAttribute b/doc/lfun/ModifySkillAttribute
new file mode 100644
index 0000000..e5b3669
--- /dev/null
+++ b/doc/lfun/ModifySkillAttribute
@@ -0,0 +1,105 @@
+ModifySkillAttribute()
+FUNKTION:
+    public int ModifySkillAttribute(string atrname, mixed value, 
+                                    int duration)
+
+DEFINIERT IN:
+    /std/living/skill_attributes.c
+
+ARGUMENTE:
+    <atrname>   string
+                Name des zu veraendernden Attributes
+                (Definiert in /sys/living/skill_attributes.h)
+
+    <value>     int oder closure
+                Wert des Modifikators
+                oder
+                eine Closure, welche bei Abfrage des betreffenden SAs
+                abgefragt um den Modifikator zu bestimmen.
+
+    <duration>  int
+                Dauer in Sekunden
+
+BESCHREIBUNG:
+    Aendert temporaer, d.h. fuer eine bestimmte Zeit, ein Skill-Attribut
+    eines Lebewesen, indem ein Modifikator hinzugefuegt wird.
+    
+    Der Standardwert eines SA wird von P_SKILL_ATTRIBUTE_OFFSETS festgelegt
+    oder ist 100, wenn besagte Property leer ist.
+    Alle Modifikatoren (negativ wie positiv) werden addiert und bilden
+    zusammen mit dem Standardwert eine Gesamtsumme.
+    Bei allen SAs ausser SA_QUALITY wird diese Gesamtsumme noch mit
+    SA_QUALITY (welches sich damit auf alle anderen Skill-Attribute
+    auswirkt) multipliziert und das Ergebnis stellt den Endwert des SA dar.
+    (Beispiel s.u.)
+
+    Der Wert eines Modifikators muss zwischen -1000 und 1000 liegen. Der
+    Gesamtwert eines SA kann 10 nicht unter- und 1000 nicht ueberschreiten.
+
+    Falle <value> eine Closure ist, wird diese Closure jedesmal
+    ausgefuehrt, wenn das entsprechende SA abgefragt wird. Der
+    Rueckgabewert dieser Closure stellt dann den Wert des Modifikators
+    dar. Auch dieser muss zwischen -1000 und 1000 liegen. Gibt die
+    Closure keinen int zurueck, wird der Modifikator geloescht.
+
+    Gueltige Skill-Attribute sind momentan:
+    * SA_QUALITY:    Allgemeine Qualitaet: wirkt sich auf alle anderen
+                     Attribute auch aus (multplikativ auf Basis 100)
+    * SA_DAMAGE:     Schaden, den das Lebewesen macht
+    * SA_SPEED:      Geschwindigkeit des Lebewesens (zB Angriffe/Runde)
+    * SA_DURATION:   Spell-/Skilldauer
+    * SA_ENEMY_SAVE: identisch zu SA_SPELL_PENETRATION (OBSOLET!)
+    * SA_SPELL_PENETRATION: Chance des _Casters_, einen Spell durch ein
+                            P_NOMAGIC durchzukriegen.
+    * SA_RANGE:      Reichweite des Lebewesens (eher unbenutzt)
+    * SA_EXTENSION:  "Ausdehnung" bei Gruppen-/Flaechenspells: FindGroupN/P
+
+RUECKGABEWERT:
+    SA_MOD_OK              wenn der Modifikator gesetzt wurde
+    SA_TOO_MANY_MODS       wenn die max. Anzahl an Mods schon erreicht ist.
+    SA_MOD_TOO_SMALL       wenn der Modifikator zu klein ist 
+    SA_MOD_TOO_BIG         wenn der Modifikator zu gross ist
+    SA_MOD_INVALID_ATTR    wenn das gewuenschte SA gar nicht existiert
+    SA_MOD_INVALID_OBJECT  wenn das setzende Objekt ungueltig ist
+    SA_MOD_INVALID_VALUE   wenn der Modifikator ungueltig ist
+    Wenn man nur wissen will, ob die Operation erfolgreich war, empfiehlt
+    es sich, auf == SA_MOD_OK zu pruefen.
+
+BEMERKUNGEN:
+    Nachdem ein Objekt, welches Modifikatoren setzte, zerstoert wurde,
+    werden die Modifikatoren spaetestens ungueltig, sobald in dem
+    manipulierten Lebewesen erneut ModifySkillAttribute() gerufen wird!
+    Bei Closures ist der Mod sofort weg.
+
+BEISPIELE:
+    // sei PL ein Spieler, den mein NPC schwaechen will:
+    PL->ModifySkillAttribute(SA_QUALITY, -75, 13);
+    // Fuer 13s wird SA_QUALITY um 75 reduziert. Dies wirkt sich auf alle
+    // anderen SAs aus! (s. drittes Beispiel)
+
+    // sei PL ein Lebewesen, welchem ich fuer 11s 2 Schlaege pro Kampfrunde
+    // zusaetzlich geben moechte:
+    PL->ModifySkillAttribute(SA_SPEED, 200, 11);
+    // wenn keine weiteres Modifikatoren wirken, hat PL jetzt 3 Schlaege
+    // pro Kampfrunde (Basiswert 100 + 200 == 300 => 3).
+
+    Angenommen, ein Lebewesen hat einen Basiswert von 130 auf SA_SPEED und
+    100 auf SA_QUALITY (P_SKILL_ATTRIBUTE_OFFSETS) und nun 3 Modifikatoren
+    gesetzt: SA_SPEED +100, SA_SPEED -30 und SA_QUALITY von -10:
+    Zunaechst wird SA_QUALITY bestimmt: 100 - 10 = 90  => 0.9
+    Anschliessend wird SA_SPEED bestimmt: 130 + 100 - 30 = 200 => 2
+    Nun wird SA_SPEED noch mit SA_QUALITY multipliziert: 2 * 0.9 = 1.8
+    Das Lebewesen hat nun also im Endeffekt 1.8 Schlaege pro Kampfrunde.
+
+    
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill
+    * Modifikation: QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung, skill_info_liste
+    * Properties:   P_NEWSKILLS
+
+5. Okt 2011 Gloinson
diff --git a/doc/lfun/More b/doc/lfun/More
new file mode 100644
index 0000000..2e73fa2
--- /dev/null
+++ b/doc/lfun/More
@@ -0,0 +1,56 @@
+More()
+
+FUNKTION:
+     varargs public void More(string txt, int file,
+                              mixed ctrl, mixed *ctrlargs, int flags);
+
+DEFINIERT IN:
+     /std/util/pager.c
+
+ARGUMENTE:
+     txt  - entweder ein Text der ausgegeben werden soll, oder ein filename.
+     file - das flag file gibt an, ob es sich bei <txt> um einen text oder
+            einen Filenamen handelt. Bei einem Filenamen wird der Inhalt
+            dieses Files eingelesen und ausgegeben.
+     ctrl - Eine closure, die aufgerufen wird, falls kein <txt> angegeben
+            wurde.
+     ctrlargs - ctrlargs wird als Parameter an ctrl uebergeben.
+     flags - flags wird mit den im Spieler definierten P_MORE_FLAGS
+             kombiniert.
+
+BESCHREIBUNG:
+
+     More() dient der Ausgabe von Texten an Spieler. Mit Hilfe eines
+     PL->More(txt) oder PL->More(txt, 1) ist es sehr einfach laengere Texte
+     an Spieler auszugeben. Bei der Ausgabe werden die persoenlichen
+     Einstellungen des Spielern (wie z.b. Zeilen pro Bildschirmseite)
+     automatisch beruecksichtigt und der Text dadurch ggf. zerstueckelt
+     und in mehreren Schritten nacheinander angezeigt.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Beim einlesen des Files sind die Leserechte des Spieler in dem More()
+     aufgerufen wird von Bedeutung und nicht die Rechte des Objektes das
+     More() aufruft. Spielerobjekte haben im MorgenGrauen jedoch nur sehr
+     eingeschraenkte Leserechte! Ausgegeben werden koennen nur files
+     aus /p/*, /gilden/* und /d/* die _keinen_ code enthalten. Als Code
+     wird hierbei jedes File betrachtet das als vorletztes Zeichen einen .
+     hat (also .c, .h, .o usw.).
+     Will man aus irgendwelchen Gruenden ein File (z.b. aus /players/)
+     ausgeben, so sollte man stattdessen folgendes verwenden:
+     this_player()->More(read_file(filename))
+
+BEISPIELE:
+
+     // Ausgabe eines normalen textes...
+     this_player()->More("Einfach nur mal so ein Test...\n");
+
+     // Ausgabe eines kompletten files
+     this_player()->More("/etc/WIZRULES", 1);
+
+SIEHE AUCH:
+
+----------------------------------------------------------------------------
+Last modified: Mon Feb 22 15:09:18 1999 by Padreic
diff --git a/doc/lfun/NPC_Killed_by b/doc/lfun/NPC_Killed_by
new file mode 100644
index 0000000..37392ba
--- /dev/null
+++ b/doc/lfun/NPC_Killed_by
@@ -0,0 +1,13 @@
+FUNKTION:
+	void NPC_Killed_By(object pl)
+	
+ARGUMENTE:
+	Spieler, der einen NPC gekillt hat.
+	
+BESCHREIBUNG:
+	Wird in der Gilde des Spielers aufgerufen,
+	wenn dieser einen NPC gekillt hat.
+	
+RUECKGABEWERT:
+	Keiner
+	
diff --git a/doc/lfun/Name b/doc/lfun/Name
new file mode 100644
index 0000000..774ac68
--- /dev/null
+++ b/doc/lfun/Name
@@ -0,0 +1,28 @@
+Name()
+
+FUNKTION:
+     varargs string Name(int casus, int demon);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     casus
+          Der Fall, in dem der Name dekliniert werden soll.
+     demon
+          Gibt an, ob der Name mit bestimmtem oder unbestimmtem Artikel
+          versehen werden soll:
+             + demon = 0: Unbestimmter Artikel.
+             + demon = 1: Bestimmter Artikel.
+             + demon = 2: Finde selbst heraus, ob ein bestimmter oder ein
+               unbestimmter Artikel verwendet werden soll.
+
+BESCHREIBUNG:
+     Dieser Funktionsaufruf ist ein Alias fuer 
+     capitalize( name( casus, demon ) )
+
+SIEHE AUCH:
+     name(), /std/thing/description.c
+
+----------------------------------------------------------------------------
+Last modified: Sat Aug  3 11:24:06 2002 by Vanion
diff --git a/doc/lfun/NewDoor b/doc/lfun/NewDoor
new file mode 100644
index 0000000..09f5796
--- /dev/null
+++ b/doc/lfun/NewDoor
@@ -0,0 +1,223 @@
+FUNKTION:
+     varargs int NewDoor(string|string* cmds, string dest, string|string* ids,
+                         mapping|<int|string|string*>* props);
+DEFINIERT IN:
+     /std/room/doors.c
+
+ARGUMENTE:
+     cmds (string|string*)
+          String oder Array von Strings mit den Befehlen, mit denen man
+          durch die Tuer geht (in der Regel Richtungen wie "norden").
+     dest (string)
+          Pfad des Zielraumes, OHNE ".c", sonst wird eine zweiseitige Tuer
+          (wie sie ueblich ist) nicht als solche erkannt.
+     ids (string|string*)
+          String oder Array von Strings mit den Bezeichnern der Tuer. Kann
+          auch 0 sein; in diesem Fall laesst sich die Tuer nur als "tuer"
+          ansprechen.
+     props (mapping|<int|string|string*>*)
+          Die Eigenschaften der Tuer (s.u.).
+
+BESCHREIBUNG:
+     Es wird eine neue Tuer geschaffen. Die Tuer laesst sich, wenn sie
+     geoeffnet ist, mit den in <cmds> angegebenen Befehlen durchschreiten.
+     Man gelangt dann in den Raum <dest>.
+
+     Die Kommandos werden bei geoeffneter Tuer in die Liste der sichtbaren
+     Ausgaenge eingefuegt.
+
+     In <props> lassen sich Aussehen und Eigenschaften der Tuer festlegen.
+     Historisch ist es ein Array mit Paaren von Schluesseln und Werten, d.h.
+     es kommt immer erst ein Element, welches die Eigenschaft bezeichnet und
+     dann ein Element mit dem Wert dieser Eigenschaft.
+     Moderner ist es, ein Mapping mit den entsprechenden Schluesseln und
+     Werten zu uebergeben. Dies ist auch dringend empfohlen.
+
+     In <doorroom.h> sind dazu folgende Eigenschaften definiert:
+     D_FLAGS
+          DOOR_OPEN
+               Die Tuer ist beim Erzeugen geoeffnet.
+          DOOR_CLOSED
+               Die Tuer ist beim Erzeugen geschlossen.
+          DOOR_NEEDKEY
+               Man benoetigt einen Schluessel zum Oeffnen der Tuer.
+          DOOR_CLOSEKEY
+               Man benoetigt einen Schluessel zum Schliessen der Tuer.
+          DOOR_RESET_CL
+               Die Tuer schliesst sich beim Reset.
+          DOOR_RESET_OP
+               Die Tuer oeffnet sich beim Reset.
+
+     D_LONG
+          Die Langbeschreibung der Tuer. 
+          Hier kann ein Mapping eingetragen werden, das als Keys den Tuer-
+          Status hat und die zugehoerige Langbeschreibung dazu. Beispiel:
+          ([ D_STATUS_CLOSED : "Die Tuer ist geschlossen.\n",
+             D_STATUS_OPEN   : "Die Tuer ist offen.\n" ])
+          
+          Default: "Eine Tuer.\n"
+
+     D_SHORT
+          Die Kurzbeschreibung der Tuer. Ein "%s" wird durch "geoeffnet"
+          bzw. "geschlossen" ersetzt.
+
+          Es werden die Kurzbeschreibungen aller im Raum vorhandenen Tueren
+          aneinandergehaengt (es wird jeweils ein Leerzeichen eingeschoben),
+          das Ganze mit break_string() umgebrochen und an P_INT_LONG
+          angehaengt.
+
+          Default: "Eine %se Tuer."
+
+     D_NAME
+          Der Name der Tuer. Dieser Name wird beim Oeffnen und Schliessen
+          der Tuer sowie bei Fehlermeldungen ausgegeben. Man kann wie bei
+          P_NAME einen String oder ein Array von Strings angeben.
+
+          Default: "Tuer"
+
+     D_GENDER
+          Das Geschlecht der Tuer.
+
+          Default: FEMALE
+
+     D_MSGS
+          String oder Array von drei Strings. Diese Strings werden beim
+          Durchschreiten der Tuer an move() als dir bzw. dir, textout und
+          textin uebergeben.
+
+     D_FUNC
+          String mit dem Namen einer Funktion, die im Startraum vor dem
+          Durchschreiten der Tuer aufgerufen werden soll. Diese Funktion
+          kann das Durchschreiten jedoch nicht verhindern!
+
+     D_FUNC2
+          String mit dem Namen einer Funktion, die im Zielraum nach dem
+          Durchschreiten der Tuer aufgerufen werden soll.
+
+     D_TESTFUNC
+          Falls auf den Namen einer Funktion gesetzt, wird diese Funktion
+          vor dem Durchschreiten im Startraum aufgerufen. Wenn sie einen
+          Wert != 0 zurueckliefert, wird die Tuer NICHT durchschritten. 
+
+     D_RESET_MSG
+          Meldung, die beim Reset der Tuer ausgegeben wird.
+
+     D_OPEN_WITH_MOVE
+          Tuer oeffnet automatisch bei Eingabe des Befehls zum 
+          Hindurchgehen.
+          
+RUECKGABEWERT:
+     1, wenn die Tuer ordungsgemaess eingebaut werden konnte, sonst 0.
+
+BEMERKUNGEN:
+     Zwei Tuerseiten gelten als verschiedene Seiten einer Tuer, wenn als
+     Ziel in Raum A Raum B und in Raum B Raum A angegeben ist. Der Zustand
+     wird abgefragt, wenn der Raum betreten wird (init), wenn die Tuer
+     geoeffnet/geschlossen wird, P_INT_LONG oder P_EXITS abgefragt wird
+     und beim Reset.
+
+     Es sind auch Tueren moeglich, die nur auf einer Seite existieren, oder
+     auch solche, die auf beiden Seiten verschieden aussehen oder gar auf
+     einer Seite nur mit einem Schluessel zu oeffnen sind, auf der anderen
+     jedoch kein Schluessel noetig ist.
+
+     Wer aus irgendeinem Grund den Zustand einer Tuer selber abfragen oder
+     veraendern will, kann dafuer in /obj/doormaster die Funktionen
+     QueryDoorStatus(ziel) bzw. SetDoorStatus(ziel,status) aufrufen.
+
+     *** ACHTUNG ***
+     Es gibt eine Questbelohnung (Phiole aus der Sternenlicht-Quest von
+     Miril), mit der man durch Tueren hindurchschauen kann. Derzeit ist das
+     per default fuer alle Tueren erlaubt. Wenn man das nicht moechte,
+     oder andere Texte ausgeben, als die Phiole normalerweise erzeugt,
+     dann kann man dies durch Nutzung bestimmter Funktionen bzw. Flags
+     erreichen. Zur Dokumentation siehe Manpage zu GetPhiolenInfos().
+
+BEISPIELE:
+
+  ** Dies ist eine gewoehnliche Tuer ohne Besonderheiten und ohne
+     Extra-Beschreibung:
+
+     NewDoor("sueden","/players/rochus/room/test1");
+
+  ** Ein Portal:
+
+     NewDoor("norden","/players/rochus/room/test2",
+             "portal",
+             ([ D_NAME:   "Portal",
+                D_GENDER: NEUTER,
+                D_SHORT:  "Im Norden siehst Du ein %ses Portal.",
+                D_LONG:   "Das Portal ist einfach nur gigantisch.\n",
+              ]) );
+
+     Alternativ mit props in alter Arraynotation:
+     NewDoor("norden","/players/rochus/room/test2",
+             "portal",
+             ({ D_NAME,   "Portal",
+                D_GENDER, NEUTER,
+                D_SHORT,  "Im Norden siehst Du ein %ses Portal.",
+                D_LONG,   "Das Portal ist einfach nur gigantisch.\n"
+              }) );
+     
+  ** Tueren mit Bewegungsmeldung:
+
+     NewDoor("norden","/room/see2",
+             ({ D_MSGS,  ({"nach Norden","schwimmt",
+                           "kommt hereingeschwommen"}) }) );
+
+  ** Eine Tuer mit Testfunktion:
+
+     NewDoor("osten","/mein/zielraum",
+             ({ D_TESTFUNC, "blocker_da" }) );
+
+     Die Funktion blocker_da:
+
+     int blocker_da()      // KEINE protected-Funktion! Sie wird sonst NICHT
+     {                     // aufgerufen und ausgewertet!
+       if ( present("mein_fieses_mo\nster",this_object()) )
+        {
+         write("Ein fieses Monster stellt sich Dir in den Weg.\n");
+         return 1;
+        }
+       return 0;           // optional
+     }
+
+  ** Nun noch eine Tuer mit einigen Extras:
+
+     NewDoor("nordwesten","/rooms/kammer",
+             ({"tuer","holztuer"}),
+             ({
+               D_FLAGS,  (DOOR_CLOSED|DOOR_RESET_CL),
+               D_MSGS,   ({"nach Nordwesten","geht",
+                         "kommt durch eine Tuer herein"}),
+               D_SHORT,  "Im Nordwesten siehst Du eine %se Holztuer.",
+               D_LONG,   "Sie trennt den Laden vom dahinterliegenden Raum.\n",
+               D_NAME,   "Holztuer",
+               D_FUNC,   "view",
+               D_FUNC2,  "look_up"
+             }) );
+
+     Im Startraum:
+
+     void view()
+     {
+       write("Der Angestellte wirft Dir einen missbilligenden Blick zu, "
+             "laesst Dich aber passieren.\n");
+     }
+
+     Im Zielraum:
+
+     void look_up()
+     {
+       write("Ein alter Mann schaut kurz zu Dir auf und vertieft sich dann "
+             "wieder in seine Akten.\n");
+     }
+
+
+SIEHE AUCH:
+    QueryDoorKey(), QueryDoorStatus(), SetDoorStatus(),
+    /std/room/doors.c, /obj/doormaster.c, GetPhiolenInfos(), QueryAllDoors()
+
+-----------------------------------------------------------------------------
+08.02.2015, Arathorn
+
diff --git a/doc/lfun/NoParaObjects b/doc/lfun/NoParaObjects
new file mode 100644
index 0000000..804e7b7
--- /dev/null
+++ b/doc/lfun/NoParaObjects
@@ -0,0 +1,28 @@
+********************** VERALTETE LFUN ****************************
+NoParaObjects()
+
+FUNKTION:
+	int NoParaObjects()
+
+DEFINIERT IN:
+	/std/virtual/v_compiler.c
+
+RUeCKGABEWERT:
+    1, wenn der VC keine Parawelt-Objekte erzeugt,
+    0, wenn er es doch tut.
+
+BESCHREIBUNG:
+    Diese Funktion ist veraltet. QueryValidObject() ist genereller und
+    einfacher zu handhaben. Bitte in Zukunft P_PARA im VC setzen, siehe hierzu
+    die Manpage von QueryValidObject().
+
+    Die Funktion gibt an, ob ein Virtual Compiler auch Parawelt-Objekte
+    erzeugen kann.
+    Wichtig: Entweder dieser Funktion im VC definieren, wenn der VC keine
+    Para-Objekte erzeugen wird oder P_PARA passend setzen!
+
+SIEHE AUCH:
+	virtual_compiler, P_PARA, QueryValidObject
+
+----------------------------------------------------------------------------
+04.03.2007, Zesstra
diff --git a/doc/lfun/NotifyDestruct b/doc/lfun/NotifyDestruct
new file mode 100644
index 0000000..48e71ee
--- /dev/null
+++ b/doc/lfun/NotifyDestruct
@@ -0,0 +1,47 @@
+NotifyDestruct()
+
+FUNKTION:
+     public int|string NotifyRemove(object zerstoerer);
+
+ARGUMENTE:
+     zerstoerer
+          Das Objekt, welches destruct() auf dieses Objekt anwendet.
+
+BESCHREIBUNG:
+     Diese Funktion wird vom Master der Mudlib in jedem Objekt gerufen,
+     welches zerstoert wird, um Objekten eine letzte Chance zu geben, sich
+     aufzuraeumen.
+     Wenn ihr diese Funktion selber definiert, achtet bittet darauf, dass sie
+     in keinem Fall Fehler ausloesen sollte, d.h. testet entsprechend
+     gruendlich.
+     Wenn ihr aus /std/ erbt und diese Funktion ueberschreibt,, _muesst_ ihr
+     die geerbte NotifyDestruct() aufrufen, da sonst das Lichtsystem nicht
+     aktualisiert wird.
+
+     Privilegierte Objekte (z.B. Root-Objekte, spezielle Ausnahmen wie der
+     Netztotenraum, Armageddon) koennen die Zerstoerung in letzter Sekunde
+     verhindern, indem sie hier einen Wert != 0 zurueckliefern. Wird ein
+     string zurueckgeliefert, wird dieser die Fehlermeldung des
+     prepare_destruct() im Master sein.
+     Bei nicht-privilegierten Objekten (also fast alle) ist an dieser Stelle
+     _kein_ Abbruch der Zerstoerung moeglich!
+
+RUeCKGABEWERT:
+     Der Rueckgabewert muss ein string oder ein int sein. Allerdings wird der
+     Master den Rueckgabewert nur fuer privilegierte Objekte auswerten.
+
+BEMERKUNGEN:
+     Wie gesagt, bitte keine Fehler ausloesen (auch wenn der Master
+     grundsaetzlich damit klar kommt). Speziell ist ein raise_error() zur
+     Verhinderung eines destructs nutzlos.
+     Bitte macht in dieser Funkion nur das, was _unbedingt_ notwendig ist.
+     Wenn jemand ein destruct() anwendet statt ein remove() zu rufen, hat das
+     in der Regel einen Grund (z.B. um buggende remove() zu umgehen). Insb.
+     ist ein save_object() in remove() und NotifyDestruct() vollstaendig
+     ueberfluessig.
+
+SIEHE AUCH:
+    NotifyLeave(), NotifyInsert(), NotifyRemove()
+
+----------------------------------------------------------------------------
+Last modified: 25.02.2009, Zesstra
diff --git a/doc/lfun/NotifyInsert b/doc/lfun/NotifyInsert
new file mode 100644
index 0000000..aa2c6ec
--- /dev/null
+++ b/doc/lfun/NotifyInsert
@@ -0,0 +1,28 @@
+NotifyInsert()
+
+FUNKTION:
+     void NotifyInsert(object ob, object oldenv);
+
+ARGUMENTE:
+     ob
+          Das Objekt, das in den Behaelter eingefuegt wurde.
+     oldenv
+          Das Objekt, aus dem <ob> kam.
+
+BESCHREIBUNG:
+     Diese Funktion wird im Behaelter aufgerufen, nachdem ein Objekt in
+     besagten Behaelter hinein bewegt wurde. 
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Diese Funktion wird nur im Falle unbelebter Objekte gerufen. Fuer 
+     Lebewesen s. bitte. init().
+
+SIEHE AUCH:
+    NotifyLeave(), PreventInsert(), PreventLeave(), move(), NotifyRemove()
+    exit(), init(), NotifyMove(), PreventMove()
+
+----------------------------------------------------------------------------
+Last modified: 21.05.2012, Zesstra
diff --git a/doc/lfun/NotifyLeave b/doc/lfun/NotifyLeave
new file mode 100644
index 0000000..621fca8
--- /dev/null
+++ b/doc/lfun/NotifyLeave
@@ -0,0 +1,28 @@
+NotifyLeave()
+
+FUNKTION:
+     void NotifyLeave(object ob, object dest);
+
+ARGUMENTE:
+     ob
+          Das Objekt, das aus dem Behaelter entfernt wurde.
+     dest
+          Das Objekt, in das <ob> bewegt wurde.
+
+BESCHREIBUNG:
+     Diese Funktion wird im Behaelter aufgerufen, nachdem ein Objekt aus
+     besagten Behaelter entfernt wurde.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Diese Funktion wird nur im Falle unbelebter Objekte gerufen. Fuer
+     Lebewesen s. bitte. exit().
+
+SIEHE AUCH:
+    NotifyRemove(), NotifyInsert(), PreventInsert(), PreventLeave(), move(),
+    exit(), init(), NotifyMove(), PreventMove()
+
+----------------------------------------------------------------------------
+Last modified: 21.05.2012, Zesstra
diff --git a/doc/lfun/NotifyMove b/doc/lfun/NotifyMove
new file mode 100644
index 0000000..9fe1861
--- /dev/null
+++ b/doc/lfun/NotifyMove
@@ -0,0 +1,58 @@
+
+FUNKTION:
+     protected void NotifyMove(object dest, object oldenv, int method);
+
+DEFINIERT IN:
+     /std/thing/moving.c
+     /std/living/moving.c
+     /std/player/moving.c
+
+ARGUMENTE:
+     dest
+          Das Ziel des Moves bzw. das jetzt aktuelle Environment
+     oldenv
+          Das alte Environment des Objekts
+     method
+          Die Move-Methode(n), mit der/denen bewegt wurde.
+
+BESCHREIBUNG:
+     Diese Funktion wird vom move() im Objekt gerufen, sobald die Bewegung im
+     wesentlichen abgeschlossen ist. Sie soll einerseits das Objekt ueber eine
+     stattgefundene Bewegung informieren, aber auch einige Dinge erledigen,
+     die bei der Bewegung stattfinden muessen (bei Livings z.B. das Team
+     nachholen).
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Diese Funktion kann ueberschrieben werden, damit das Objekt Bewegungen
+     mitgekommt, ohne das move() selber zu ueberschreiben oder einen Move-Hook
+     zu setzen. Dabei aber bitte unbedingt beachten:
+     Das geerbte NotifyMove() _MUSS IN JEDEM FALL_ mit aufgerufen werden!
+     Solltet ihr das vergessen, werden eure Objekte buggen. ;-)
+     Die Funktion darf nur objektintern verwendet werden. Beim Ueberschreiben
+     das 'protected' nicht vergessen!
+
+BEISPIELE:
+     Eine Bombe, die in Seherhaustruhen explodiert:
+
+     protected void NotifyMove(object dest, object oldenv, int method) {
+         ::NotifyMove(dest, oldenv, method); // WICHTIG!
+         if (objectp(dest) &&
+             load_name(dest) == "/d/seher/haeuser/truhe") {
+             if (find_call_out("explodiere")==-1)
+                 call_out("explodiere",900);
+         }
+         else
+             remove_call_out("explodiere");
+     }
+
+
+SIEHE AUCH:
+     PreventLeave(), NotifyInsert(), NotifyLeave(), MayAddObject(),
+     PreventInsertLiving(), PreventLeaveLiving(), NotifyMove(),
+     PreventMove(), MayAddWeight(), move(), /std/container/restrictions.c
+
+----------------------------------------------------------------------------
+Last modified: 04.08.2007, Zesstra
diff --git a/doc/lfun/NotifyPlayerDeath b/doc/lfun/NotifyPlayerDeath
new file mode 100644
index 0000000..9166150
--- /dev/null
+++ b/doc/lfun/NotifyPlayerDeath
@@ -0,0 +1,74 @@
+NotifyPlayerDeath()
+
+FUNKTION:
+  void NotifyPlayerDeath(object victim,object killer,int lost_exp);
+
+DEFINIERT IN:
+  /std/player/life.c
+
+ARGUMENTE:
+  victim
+    Getoeteter Spieler.
+  killer
+    Objekt, welches den Spieler getoetet hat.
+  lost_exp
+    Erfahrungspunkte, die der Spieler durch den Tod verloren hat.
+
+BESCHREIBUNG:
+  Diese Funktion wird aus dem Spielerobjekt heraus immer dann
+  aufgerufen, wenn der Spieler stirbt und zwar:
+    1) im Gegner, der den Spieler getoetet hat,
+    2) im Environment des getoeteten Spielers,
+    3) in der Gilde des Spielers,
+    4) in allen Objekten in diesem Environment und
+    5) in allen Objekten (auch innerhalb Containern) im Spieler.
+  Der Gegner wird hierbei nur einmal informiert, also bei letzteren
+  Faellen herausgefiltert, falls noetig!
+  Hauptaufgabe dieser Funktion ist es somit, auf Tode von Spielern zu
+  reagieren oder selbige einfach nur mitzuloggen.
+
+  Zu dem Zeitpunkt des Aufrufs dieser Funktion ist der Spieler noch _nicht_
+  Geist und noch _nicht_ im Todesraum - dies wird im Anschluss an den Aufruf
+  der NotifyPlayerDeath() durchgefuehrt.
+  
+  Aufgerufen wird die Funktion aus '/std/player/life.c' mittels catch() und
+  werden mit limited() auf max. 150k (Gegner, Environment, Gilde) bzw. 80k 
+  (alle anderen Objekte) Ticks beschraenkt.
+  Fehler in dieser Funktion haben somit keine negativen Auswirkungen
+  auf das Sterben des Spielers.
+
+RUeCKGABEWERT:
+  keiner
+
+BEISPIELE:
+  Folgendes Beispiel demonstriert beispielsweise, wie man Tode von
+  Spielern mitloggen kann (das Beispiel ist hierbei auf den am
+  haeufigsten auftretenden Fall bezogen, dass nur das toetende Objekt
+  den Tod protokollieren soll):
+
+    void NotifyPlayerDeath(object dead, object killer, int lost_exp) 
+    { 
+      if ( intp(lost_exp) && objectp(dead) && objectp(killer) && 
+           killer==this_object() )
+        write_file( "/log/patryn/dead", sprintf(
+          "%s - %s von %s getoetet. XP: -%d", ctime(), getuid(dead),
+           killer->name(WEM), lost_exp) );
+    }
+
+  Bitte beachten: write_file() schreibt ohne Groessenbegrenzung in das
+  Logfile. Da dies auf Dauer bzw. in Gebieten mit hoher Log-Aktivitaet
+  zu Logfiles von enormer Groesse fuehren kann, ist die Verwendung
+  von write_file() nicht empfehlenswert. Ausnahmen koennen natuerlich
+  mit dem zustaendigen Regionsmagier abgesprochen werden, z.B. fuer
+  begrenzte Anwendung etwa bei sehr starken, selten besiegten Gegnern.
+
+  Weiterhin ist es empfehlenswert, das toetende Objekt (killer) nicht
+  im NotifyPlayerDeath() zu zestoeren, sondern etwas zeitversetzt,
+  damit nicht etwa im nachfolgenden NotifyPlayerDeath() eines anderen
+  Objektes (s.o. Reihenfolge) killer==0 ist.
+
+SIEHE AUCH:
+  Defend(), do_damage(), NotifyHpChange(), catch(), write_file(), log_file()
+  P_LAST_DEATH_PROPS
+----------------------------------------------------------------------------
+24.03.2012, Zesstra
diff --git a/doc/lfun/NotifyRemove b/doc/lfun/NotifyRemove
new file mode 100644
index 0000000..48b04fa
--- /dev/null
+++ b/doc/lfun/NotifyRemove
@@ -0,0 +1,26 @@
+NotifyRemove()
+
+FUNKTION:
+     void NotifyRemove(object ob);
+
+ARGUMENTE:
+     ob
+          Das Objekt, das aus dem Behaelter entfernt wurde.
+
+BESCHREIBUNG:
+     Diese Funktion wird im Behaelter aufgerufen, wenn ein Objekt im
+     besagten Behaelter zerstoert wird.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Wird nicht gerufen, wenn ein Spielerobjekt zerstoert wird.
+
+SIEHE AUCH:
+    NotifyLeave(), NotifyInsert(), PreventInsert(), PreventLeave(), move(),
+    NotifyMove(), PreventMove(),
+    BecomesNetDead()
+
+----------------------------------------------------------------------------
+Last modified: 04.08.2007, Zesstra
diff --git a/doc/lfun/NotifyTimedAttrModExpired b/doc/lfun/NotifyTimedAttrModExpired
new file mode 100644
index 0000000..f59a79a
--- /dev/null
+++ b/doc/lfun/NotifyTimedAttrModExpired
@@ -0,0 +1,20 @@
+NotifyTimedAttrModExpired
+FUNKTION:
+     void NotifyTimedAttrModExpired(string key)
+
+DEFINIERT IN:
+     Push-Methode in notify-Objekten.
+
+ARGUMENTE:
+     string key		- der geloeschte Modifier
+
+BESCHREIBUNG:
+     Wird aus dem Lebewesen aus aufgerufen, in dem der entsprechenden
+     Modifier soeben ungueltig geworden ist.     
+
+SIEHE AUCH:
+     Properties: P_ATTRIBUTES, P_TIMED_ATTR_MOD
+     Methoden:	 SetTimedAttrModifier(L), DeleteTimedAttrModifier(L),
+		 QueryTimedAttrModifier(L)
+
+27. Juli 2004 Gloinson
diff --git a/doc/lfun/NotifyXMAttrModLimitViolation b/doc/lfun/NotifyXMAttrModLimitViolation
new file mode 100644
index 0000000..44f39c0
--- /dev/null
+++ b/doc/lfun/NotifyXMAttrModLimitViolation
@@ -0,0 +1,20 @@
+NotifyXMAttrModLimitViolation()
+FUNKTION:
+      void NotifyXMAttrModLimitViolation()
+
+DEFINIERT IN:
+     /std/thing/restrictions.c
+

+BESCHREIBUNG:
+     Wird gerufen wenn die Summe der in P_X_ATTR_MOD oder P_X_ATTR_MOD
+     angegebenen Modifikatoren die Summe aller Modifikatoren im Spieler 
+     ueber den zugelassenen Grenzwert hebt und somit nicht mehr in die
+     Berechnung eingeht.
+     
+SIEHE AUCH:
+     QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+     SetAttr(), SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+     P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_ATTRIBUTES_MODIFIER,

+     P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+
+13.Jun.2004, Muadib
\ No newline at end of file
diff --git a/doc/lfun/Pacify b/doc/lfun/Pacify
new file mode 100644
index 0000000..b90ed83
--- /dev/null
+++ b/doc/lfun/Pacify
@@ -0,0 +1,113 @@
+FUNKTION
+	public int Pacify(object caster)
+
+DEFINIERT IN
+	/std/living/combat.c
+
+BESCHREIBUNG
+	Diese Funktion versucht, ein Lebewesen zu befrieden.
+  Will eine Gilde ein Lebewesen befrieden, muss sie hierfuer diese Funktion
+  in dem Lebewesen aufrufen.
+
+  Ein immer befriedbarer NPC kann durch das Setzen von P_ACCEPT_PEACE in einem
+  Lebewesen realisiert werden.
+
+	Standardmaessig funktioniert die Funktion wie folgt:
+  * Kommt der Versuch vom Spellcaster selbst, ist er immer erfolgreich.
+  * Kommt der Versuch von einem Teamkollegen, ist er immer erfolgreich.
+  * Hat das Lebewesen keine Gegner, ist der Versuch immer erfolglos.
+  In diesen Faellen erfolgt auch keine Erhoehung des Befriedezaehlers.
+  
+  In anderen Faellen wird die in P_PEACE_HISTORY fuer die Gilde des Casters
+  abgelegte Zahl erfolgreicher Befriedungen (ANZ), die Intelligenz des
+  Casters (INT_CASTER) und die Intelligenz des Lebenwesens selber (INT_ME)
+  ermittelt.
+  Anschliessend wird eine Wahrscheinlichkeit w ausgerechnet:
+  
+    w = (INT_CASTER + 10 - ANZ*4) / (INT_ME + 10)
+  
+  Hierbei gibt w die Chance auf eine erfolgreiche Befriedung an. Mittels einer
+  Zufallszahl wird bestimmt, ob der aktuelle Versuch erfolgreich ist. Falls
+  ja, wird der Zaehler fuer die Gilde des Casters in P_PEACE_HISTORY erhoeht.
+
+  Je oefter ein Lebewesen als von einer Gilde schon befriedet wurde, desto
+  unwahrscheinlicher, dass es erneut darauf 'hereinfaellt'.
+
+BEMERKUNGEN:
+  *	Die Funktion kann auch ueberschrieben werden, um ein vom Magier
+    gewuenschtes Verhalten zu realisieren. Ein komplettes Abhaengen von
+    Befriedungen sollte dabei aber die Ausnahme sein!
+  * Diese Funktion verwaltet auch das P_PEACE_HISTORY, speziell die Reduktion
+    der Erfolgszaehler. Ueberschreibt man sie ohne das geerbte Pacify()
+    zu nutzen, wird P_PEACE_HISTORY nicht mehr verwaltet.
+
+RUECKGABEWERTE:
+    1 - das Lebewesen wurde erfolgreich befriedet..
+    0 - der Befriedeversuch ist gescheitert.
+    
+BEISPIELE:
+    Angenommen, der Caster hat eine Intelligenz von 22. Die folgende Tabelle
+    gibt dann die Wahrscheinlichkeiten fuer eine erfolgreiche Befriedung an:
+    (in Abhaengigkeit von eigener Intelligenz und vergangener erfolgreicher
+     Versuche)
+ INT_ME  Erfolgswahrscheinlichkeiten je nach Anzahl erfolgreicher Versuche
+              1       2       3       4       5       6       7       8
+      0     280     240     200     160     120      80      40       0
+      2  233,33     200  166,67  133,33     100   66,67   33,33       0
+      4     200  171,43  142,86  114,29   85,71   57,14   28,57       0
+      6     175     150     125     100      75      50      25       0
+      8  155,56  133,33  111,11   88,89   66,67   44,44   22,22       0
+     10     140     120     100      80      60      40      20       0
+     12  127,27  109,09   90,91   72,73   54,55   36,36   18,18       0
+     14  116,67     100   83,33   66,67      50   33,33   16,67       0
+     16  107,69   92,31   76,92   61,54   46,15   30,77   15,38       0
+     18     100   85,71   71,43   57,14   42,86   28,57   14,29       0
+     20   93,33      80   66,67   53,33      40   26,67   13,33       0
+     22    87,5      75    62,5      50    37,5      25    12,5       0
+     24   82,35   70,59   58,82   47,06   35,29   23,53   11,76       0
+     26   77,78   66,67   55,56   44,44   33,33   22,22   11,11       0
+     28   73,68   63,16   52,63   42,11   31,58   21,05   10,53       0
+     30      70      60      50      40      30      20      10       0
+     32   66,67   57,14   47,62    38,1   28,57   19,05    9,52       0
+     34   63,64   54,55   45,45   36,36   27,27   18,18    9,09       0
+     35   62,22   53,33   44,44   35,56   26,67   17,78    8,89       0
+     36   60,87   52,17   43,48   34,78   26,09   17,39     8,7       0
+     38   58,33      50   41,67   33,33      25   16,67    8,33       0
+     40      56      48      40      32      24      16       8       0
+     42   53,85   46,15   38,46   30,77   23,08   15,38    7,69       0
+     44   51,85   44,44   37,04   29,63   22,22   14,81    7,41       0
+     46      50   42,86   35,71   28,57   21,43   14,29    7,14       0
+     48   48,28   41,38   34,48   27,59   20,69   13,79     6,9       0
+     50   46,67      40   33,33   26,67      20   13,33    6,67       0
+     52   45,16   38,71   32,26   25,81   19,35    12,9    6,45       0
+     54   43,75    37,5   31,25      25   18,75    12,5    6,25       0
+     56   42,42   36,36    30,3   24,24   18,18   12,12    6,06       0
+     58   41,18   35,29   29,41   23,53   17,65   11,76    5,88       0
+     60      40   34,29   28,57   22,86   17,14   11,43    5,71       0
+     62   38,89   33,33   27,78   22,22   16,67   11,11    5,56       0
+     64   37,84   32,43   27,03   21,62   16,22   10,81    5,41       0
+     66   36,84   31,58   26,32   21,05   15,79   10,53    5,26       0
+     68    35,9   30,77   25,64   20,51   15,38   10,26    5,13       0
+     70      35      30      25      20      15      10       5       0
+     72   34,15   29,27   24,39   19,51   14,63    9,76    4,88       0
+     74   33,33   28,57   23,81   19,05   14,29    9,52    4,76       0
+     76   32,56   27,91   23,26    18,6   13,95     9,3    4,65       0
+     78   31,82   27,27   22,73   18,18   13,64    9,09    4,55       0
+     80   31,11   26,67   22,22   17,78   13,33    8,89    4,44       0
+     82   30,43   26,09   21,74   17,39   13,04     8,7    4,35       0
+     84   29,79   25,53   21,28   17,02   12,77    8,51    4,26       0
+     86   29,17      25   20,83   16,67    12,5    8,33    4,17       0
+     88   28,57   24,49   20,41   16,33   12,24    8,16    4,08       0
+     90      28      24      20      16      12       8       4       0
+     92   27,45   23,53   19,61   15,69   11,76    7,84    3,92       0
+     94   26,92   23,08   19,23   15,38   11,54    7,69    3,85       0
+     96   26,42   22,64   18,87   15,09   11,32    7,55    3,77       0
+     98   25,93   22,22   18,52   14,81   11,11    7,41     3,7       0
+    100   25,45   21,82   18,18   14,55   10,91    7,27    3,64       0
+
+SIEHE AUCH
+        P_ACCEPT_PEACE, P_PEACE_HISTORY
+
+LETZTE AENDERUNG
+07.06.2008, Zesstra
+
diff --git a/doc/lfun/PayIn b/doc/lfun/PayIn
new file mode 100644
index 0000000..0714c53
--- /dev/null
+++ b/doc/lfun/PayIn
@@ -0,0 +1,47 @@
+PayIn()
+FUNKTION:
+     varargs void PayIn(int amount, int percent);
+
+DEFINIERT IN:
+     /p/daemon/zentralbank.c
+
+ARGUMENTE:
+     int amount	-	einzuzahlender Betrag
+     int percent -	Bewertungsprozentsatz
+
+BESCHREIBUNG:
+     Es wird Brutto amount Geld in die Bank eingezahlt. Der Prozentsatz legt
+     fest, wieviel tatsaechlich gutgeschrieben wird:
+     Gutschrift = amount*percent/100
+
+     Wird percent nicht angegeben, dann wird der derzeitige Bankbewertungs-
+     massstab fuer Geld angenommen.
+
+BEISPIELE:
+     #include <bank.h>
+     ...
+     AddCmd("spende",#'action_spende,
+	    "Was willst du spenden?");
+     ...
+     int action_spende(string str, extra *o) {
+      int i;
+      if(sscanf("%d muenze",i)==1 && i>0)
+       if(this_player()->QueryMoney(i) && this_player()->AddMoney(-i)) {
+        write("Du spendest "+i+" Muenzen.\n");
+	say(this_player()->Name(WER)+" spendet "+i+" Muenzen.\n");
+	ZENTRALBANK->PayIn(i);
+	
+       } else
+        write("Soviel hast du nicht dabei!\n");
+      ...
+
+BEMERKUNGEN:
+     Unsere Zentralbank ist korrupt, vor allem dadurch, dass in Laeden und
+     an anderen Stellen Geld erzeugt wird.
+
+SIEHE AUCH:
+     Geldhandling:	AddMoney(L), QueryMoney(L)
+     Zentralbank:	WithDraw(L), _query_current_money(L)
+     Sonstiges:		/items/money.c, /sys/bank.h
+
+27. Apr 2004 Gloinson
diff --git a/doc/lfun/PlayerQuit b/doc/lfun/PlayerQuit
new file mode 100644
index 0000000..22e3d6e
--- /dev/null
+++ b/doc/lfun/PlayerQuit
@@ -0,0 +1,20 @@
+PlayerQuit()
+
+FUNKTION:
+    void PlayerQuit(object pl);
+
+GERUFEN VON:
+    /std/player/base.c
+
+ARGUMENTE:
+    object pl
+      Spieler, der Verbindung beendet
+
+BESCHREIBUNG:
+    Wird im environment() gerufen, wenn der Spieler das MUD mit "ende"
+    verlaesst.
+
+SIEHE AUCH:
+    BecomesNetAlive(), BecomesNetDead()
+    
+24. Aug 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/PresentEnemies b/doc/lfun/PresentEnemies
new file mode 100644
index 0000000..9c31efe
--- /dev/null
+++ b/doc/lfun/PresentEnemies
@@ -0,0 +1,71 @@
+FUNKTION:
+	object *PresentEnemies();
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	keine
+
+RUeCKGABEWERT:
+	anwesende Feinde
+
+BESCHREIBUNG:
+	Diese Funktion liefert genau die Feinde zurueck, die sich derzeit im
+	selben Raum befinden. Die Feinde unterliegen hierbei noch gewissen
+	Einschraenkungen:
+	  (1) Sie duerfen als NPC nicht gestorben sein, das heisst, sie
+	      muessen als Objekt noch existieren.
+	  (2) Sie duerfen als Spieler nicht gestorben sein, das heisst, sie
+	      duerfen keine Geister sein (Property P_GHOST nicht gesetzt).
+	  (3) Sie muessen angreifbar sein, das heisst, die Property
+	      P_NO_ATTACK darf nicht gesetzt sein.
+	Wird eine dieser Bedingungen verletzt, so wird der Gegner aus der
+	internen Gegnerliste entfernt. Zusaetzlich gilt:
+	  Netztote werden nur als Gegner erkannt, wenn keine anderen Gegner
+	  zur Verfuegung stehen.
+
+BEISPIEL:
+	Im Folgenden erblinden alle Gegner im Raum:
+	  string blindThemAll()
+	  { ob*livList;
+	    if(!livList=PresentEnemies())
+	      return break_string(
+	 "Mist...keiner da, der blind werden moechte.",77,
+	 Name(WER)+" grummelt: ");
+	    for(i=sizeof(livList);i--;)
+	    { if(livList[i]->QueryProp(P_BLIND))
+	      { tell_object(this_object(),break_string(
+	 livList[i]->Name(WER)+" ist schon blind.",77));
+	        continue;
+	      }
+	      tell_object(this_object(),break_string(
+	 "Du kratzt "+livList[i]->name(WEM)+" die Augen aus.",77));
+	      tell_object(livList[i],break_string(
+	 Name(WER)+" kratzt Dir die Augen aus!",77));
+	      tell_room(environment(this_object()),break_string(
+	 Name(WER)+" kratzt "+livList[i]->name(WEM)
+	+" die Augen aus.",77),({this_object(),livList[i]}));
+	      livList[i]->SetProp(P_BLIND,1);
+	    }
+	    return"";
+	  }
+	Aufgerufen wird das ganze am Besten in einem Chat:
+	  void create()
+	  { ::create();
+	    ...
+	    SetProp(P_CHAT_CHANCE,10);
+	    SetChats(20,({break_string(
+	 "Nicht angreifen, sonst wirst Du noch blind!",77,
+	 Name(WER)+" bruellt: "),
+	                  "@@blindThemAll@@"}));
+	  }
+	Natuerlich sind auch noch mehr Funktionen und Meldungen als Chats
+	moeglich: Die zwei im Beispiel sind im Normalfall etwas wenig.
+
+SIEHE AUCH:
+	SelectEnemy(), QueryEnemies(), IsEnemy(), P_GHOST, P_NO_ATTACK,
+	SetChats, P_CHAT_CHANCE
+
+----------------------------------------------------------------------------
+Last modified: Thu May 27 15:01:48 1999 by Patryn
diff --git a/doc/lfun/PresentEnemyRows b/doc/lfun/PresentEnemyRows
new file mode 100644
index 0000000..339642c
--- /dev/null
+++ b/doc/lfun/PresentEnemyRows
@@ -0,0 +1,40 @@
+
+PresentEnemyRows()
+
+
+FUNKTION:
+        varargs mixed *PresentEnemyRows(object *here)
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        Rueckgabewert von PresentEnemies kann uebergeben werden.
+
+BESCHREIBUNG:
+        Ergibt die feindlichen Reihen.
+
+RUECKGABEWERT:
+        Ein Array, bestehend aus MAX_TEAMROWS Arrays mit den Objekten
+        der anwesenden Feinde.
+
+BEMERKUNGEN:
+        Jede Reihe ist Summe der entsprechenden Reihen der
+        anwesenden Teams.
+        Feinde, die in keinem Team sind, stehen im ersten Array.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentTeamPosition,
+                    SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/PresentPosition b/doc/lfun/PresentPosition
new file mode 100644
index 0000000..ea765cf
--- /dev/null
+++ b/doc/lfun/PresentPosition
@@ -0,0 +1,38 @@
+
+PresentPosition()
+
+
+FUNKTION:
+        varargs int PresentPosition(mixed pmap)
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        Rueckgabewert von PresentTeamRows() oder PresentTeamPositions()
+        kann uebergeben werden.
+
+BESCHREIBUNG:
+        Ergibt die Nummer der Reihe, in der der Spieler gerade steht.
+
+RUECKGABEWERT:
+        Reihennummer im Bereich von 1 bis TEAM_MAXROWS.
+
+BEMERKUNGEN:
+        Ist ein Spieler in keinem Team, so steht er automatisch in Reihe 1.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentRows, PresentEnemyRows, PresentTeamPosition,
+                    SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/PresentRows b/doc/lfun/PresentRows
new file mode 100644
index 0000000..e8c854a
--- /dev/null
+++ b/doc/lfun/PresentRows
@@ -0,0 +1,88 @@
+
+PresentRows()
+
+
+FUNKTION:
+    mixed *PresentRows(object env);
+
+DEFINIERT IN:
+    TEAM_OBJECT (s. <living/team.h>)
+
+ARGUMENTE:
+    object env
+        Das Environment des gewuenschten Objektes.
+
+BESCHREIBUNG:
+    Mit dieser Funktion bekommt man die aktuellen Teamreihen, die im Argument
+    env anwesend sind, des Teams zurueckgegeben. Ist env kein Objekt, so
+    wird environment(this_player()) als solches angenommen.
+
+RUECKGABEWERT:
+    Es wird ein mixed-Array zurueckgegeben, dabei sind die einzelnen Reihen
+    selbst wiederum Arrays mit den Spielerobjekten.
+
+BEISPIEL:
+
+    Ein NPC im Kampf laesst eine Feuerwalze auf ein Team los, welche aber nur
+    Spieler in der ersten und zweiten Teamreihe Schaden zufuegen soll.
+
+    void Attack( object enemy )
+    {
+     ...
+
+     object team = enemy->QueryProp(P_TEAM);
+
+     if ( objectp(team) )
+      {
+       mixed teamrows = team->PresentRows(enemy);
+
+//  Inhalt von "teamrows" zu diesem Zeitpunkt:
+
+//  ({ ({[/dwarf:hauweg]}),({}),({[/elf:spitzohr]}),({}),({}),({}) })
+
+//  In der Umgebung von Zwerg Hauweg steht also noch Elf Spitzohr, und zwar
+//  in der dritten Teamreihe (der hat Glueck gehabt).
+//  Wenn dem Team noch mehr Spieler angehoeren, befinden sie sich gerade
+//  nicht in der Umgebung (sprich im selben Raum) wie Hauweg.
+
+       foreach ( i : 2 )
+        {
+         foreach ( object pl : teamrows[i] )
+          {
+           tell_object(pl,"Der Feuerteufel laesst eine Feuerwalze auf Dich "
+               "und Dein Team los.\n");
+
+           pl->Defend(200+random(200),({DT_FIRE}),([SP_SHOW_DAMAGE:1]),TO);
+          }
+        }
+      }
+     else
+      {
+       tell_object(enemy,"Der Feuerteufel laesst eine Feuerwalze auf Dich "
+           "los.\n");
+
+       enemy->Defend(200+random(200),({DT_FIRE}),([SP_SHOW_DAMAGE:1]),TO);
+      }
+
+     ...
+    }
+
+BEMERKUNG:
+    Man beachte, dass das erste Argument (also Argument 0) die erste
+    Teamreihe ist.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentEnemyRows, PresentTeamPosition,
+                    SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/PresentTeamPositions b/doc/lfun/PresentTeamPositions
new file mode 100644
index 0000000..76adc17
--- /dev/null
+++ b/doc/lfun/PresentTeamPositions
@@ -0,0 +1,40 @@
+
+PresentTeamPositions()
+
+
+FUNKTION:
+        varargs mapping PresentTeamPositions(mixed pmap)
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        Rueckgabewert von PresentTeamRows() kann uebergeben werden.
+
+BESCHREIBUNG:
+        Ermittelt die Reihennummern aller anwesender Teammitglieder.
+
+RUECKGABEWERT:
+        Ein Mapping mit den Reihennummern (von 1 bis MAX_TEAMROWS)
+        der anwesenden Teammitglieder.
+
+BEMERKUNGEN:
+        Kann auch zur Konvertierung anderer Kampfreihen-Arrays in
+        ein Positions-Mapping verwendet werden.
+        Ist der Spieler in keinem Team, so steht er in Reihe 1.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/PresentTeamRows b/doc/lfun/PresentTeamRows
new file mode 100644
index 0000000..38bfa7e
--- /dev/null
+++ b/doc/lfun/PresentTeamRows
@@ -0,0 +1,24 @@
+PresentTeamRows()
+
+FUNKTION:
+	mixed *PresentTeamRows()
+
+DEFINIERT IN:
+	/std/living/team.c
+
+ARGUMENTE:
+	keine
+
+BESCHREIBUNG:
+	Ergibt die Reihen mit den anwesenden Teammitgliedern.
+
+RUECKGABEWERT:
+	Ein Array bestehend aus MAX_TEAMROWS Arrays mit den Objekten
+        der anwesenden Teammitglieder.
+
+BEMERKUNGEN:
+	Wenn der Spieler in keinem Team ist, enthaelt das erste Array
+	nur den Spieler und die anderen Arrays sind leer.
+
+SIEHE AUCH:
+	teams
diff --git a/doc/lfun/PreventFollow b/doc/lfun/PreventFollow
new file mode 100644
index 0000000..100f92d
--- /dev/null
+++ b/doc/lfun/PreventFollow
@@ -0,0 +1,49 @@
+PreventFollow()
+
+FUNKTION:
+  int PreventFollow(object dest)
+
+ARGUMENTE:
+  dest: Zielobjekt, in das der Verfolgte bewegt werden soll.
+
+FUNKTION:
+  In jedem Verfolger, der mit AddPursuer in die Liste der Verfolger 
+  eingetragen wurde, wird vor dem Bewegen in das Zielobjekt die Funktion
+  PreventFollow mit dem Zielobjekt als Argument aufgerufen.
+
+RUECKGABEWERT:
+  0: Verfolger darf in das Zielobjekt folgen
+  1: Verfolger darf in dieses Zielobjekt nicht folgen
+     (Verfolgung bleibt weiterhin aktiv)
+  2: Verfolger darf in dieses Zielobjekt nicht folgen
+     (Verfolgung wird abgebrochen und Verfolger aus der Verfolgerliste
+      ausgetragen)
+
+BEMERKUNG:
+  Durch PreventFollow kann der raeumliche Bereich, in dem verfolgt werden
+  darf, eingeschraenkt werden.
+
+BEISPIELE:
+  Man moechte, dass ein NPC auf einer Insel nicht mit dem Spieler in das
+  Boot steigt, um mit dem Spieler zusammen von der Insel herunterzukommen.
+
+  #define PATH(x) ("/d/.../.../mein/pfad/+(x)")                           
+
+  ...                                          
+
+  int PreventFollow(object boot)                                           
+   {
+    if ( object_name(boot)[0..strlen(PATH("boot"))-1] == PATH("boot") )
+     return 1;
+   }                                                                         
+
+  Diese Funktions-Definition ist sehr flexibel, denn sie erlaubt sowohl
+  spaetere Pfadanpassung als auch mehrere Boote.
+  Da ueber die Funktion strlen() nur bis zum letzten Buchstaben des    
+  angegebenen Strings getestet wird, wird also gleichzeitig auch auf          
+  boot[1], boot[2] usw. getestet.
+
+SIEHE AUCH:
+  "AddPursuer", "RemovePursuer"
+----------------------------------------------------------------------------
+Last modified: Tue Jun 10 13:59:30 2003 by Gabylon
\ No newline at end of file
diff --git a/doc/lfun/PreventInsert b/doc/lfun/PreventInsert
new file mode 100644
index 0000000..55c1533
--- /dev/null
+++ b/doc/lfun/PreventInsert
@@ -0,0 +1,46 @@
+PreventInsert()
+
+FUNKTION:
+     int PreventInsert(object ob);
+
+DEFINIERT IN:
+     /std/container/restrictions.c
+
+ARGUMENTE:
+     ob
+          Das Objekt, das in den Behaelter eingefuegt werden soll.
+
+BESCHREIBUNG:
+     Mit dieser Funktion kann ein Behaelter pruefen, ob er das Objekt ob
+     aufnehmen moechte oder nicht.
+
+RUeCKGABEWERT:
+     0, wenn das Objekt aufgenommen werden kann; ein Wert groesser als 0
+     zeigt an, dass das Objekt nicht aufgenommen werden soll.
+
+BEMERKUNGEN:
+     Wenn ob mit dem Flag M_NOCHECK bewegt wird, wird PreventInsert() zwar
+     aufgerufen, das Objekt wird jedoch auf jeden Fall in den Behaelter
+     bewegt, unabhaengig vom Rueckgabewert!
+
+BEISPIELE:
+     Um zu verhindern, dass man Geld in einen Behaelter stecken kann, sollte
+     man wie folgt vorgehen:
+
+     varargs int PreventInsert(object ob)
+     {
+       // Wenn es Geld ist, erheben wir sofort Einspruch
+       if (ob->id("geld"))
+         return 1;
+       // Ansonsten koennte ein ererbtes Objekt noch Einspruch erheben!
+       else
+         return ::PreventInsert(ob);
+     }
+
+SIEHE AUCH:
+     PreventLeave(), NotifyInsert(), NotifyLeave(), MayAddObject(),
+     PreventInsertLiving(), PreventLeaveLiving(), NotifyMove(),
+     PreventMove(), MayAddWeight(), move(), /std/container/restrictions.c
+
+----------------------------------------------------------------------------
+Last modified: Sat Dec 18 02:00:00 1999 by Tiamak
diff --git a/doc/lfun/PreventInsertLiving b/doc/lfun/PreventInsertLiving
new file mode 100644
index 0000000..78f431f
--- /dev/null
+++ b/doc/lfun/PreventInsertLiving
@@ -0,0 +1,35 @@
+PreventInsertLiving()
+
+FUNKTION:
+     int PreventInsertLiving(object ob);
+
+DEFINIERT IN:
+     /std/container/restrictions.c
+
+ARGUMENTE:
+     ob
+          Das Living, das in den Behaelter eingefuegt werden soll.
+
+BESCHREIBUNG:
+     Mit dieser Funktion kann ein Behaelter pruefen, ob er das Living ob
+     aufnehmen moechte oder nicht.
+
+RUeCKGABEWERT:
+     0, wenn das Living aufgenommen werden kann; ein Wert groesser als 0
+     zeigt an, dass das Living nicht aufgenommen werden soll.
+
+BEMERKUNGEN:
+     Wenn ob mit dem Flag M_NOCHECK bewegt wird, wird PreventInsertLiving() 
+     zwar aufgerufen, das Living wird jedoch auf jeden Fall in den Behaelter
+     bewegt, unabhaengig vom Rueckgabewert!
+
+
+SIEHE AUCH:
+     PreventLeaveLiving(), /std/container/restrictions.c,
+     PreventMove(), PreventInsert(), PreventLeave(),
+     NotifyMove(), NotifyInsert(), NotifyLeave(), NotifyRemove(),
+     move(), init(), exit(),
+     InitAttack(), ExitAttack()
+
+----------------------------------------------------------------------------
+Last modified: 04.08.2007, Zesstra
diff --git a/doc/lfun/PreventLeave b/doc/lfun/PreventLeave
new file mode 100644
index 0000000..85d46a3
--- /dev/null
+++ b/doc/lfun/PreventLeave
@@ -0,0 +1,35 @@
+PreventLeave()
+
+FUNKTION:
+     int PreventLeave(object ob, mixed dest);
+
+DEFINIERT IN:
+     /std/container/restrictions.c
+
+ARGUMENTE:
+     ob
+          Das Objekt, das aus dem Behaelter genommen werden soll.
+     dest 
+          Das Ziel in das das Objekt ob bewegt werden soll.
+
+BESCHREIBUNG:
+     Mit dieser Funktion kann ein Behaelter pruefen, ob er das Objekt ob
+     sich bewegen lassen moechte oder nicht.
+
+RUeCKGABEWERT:
+     0, wenn das Objekt bewegt werden kann; ein Wert groesser als 0
+     zeigt an, dass das Objekt nicht bewegt werden soll.
+
+BEMERKUNGEN:
+     Wenn ob mit dem Flag M_NOCHECK bewegt wird, wird PreventLeave() zwar
+     aufgerufen, das Objekt wird jedoch auf jeden Fall aus dem Behaelter
+     bewegt, unabhaengig vom Rueckgabewert!
+
+SIEHE AUCH:
+     PreventInsert(), NotifyInsert(), NotifyLeave(),
+     MayAddWeight(), move(), /std/container/restrictions.c
+     PreventLeaveLiving(), PreventInsertLiving(), PreventMove(),
+     NotifyMove(), MayAddObject(), NotifyRemove()
+
+----------------------------------------------------------------------------
+Last modified: 04.08.2007, Zesstra
diff --git a/doc/lfun/PreventLeaveLiving b/doc/lfun/PreventLeaveLiving
new file mode 100644
index 0000000..4d13b54
--- /dev/null
+++ b/doc/lfun/PreventLeaveLiving
@@ -0,0 +1,35 @@
+PreventLeaveLiving()
+
+FUNKTION:
+     int PreventLeaveLiving(object ob, mixed dest);
+
+DEFINIERT IN:
+     /std/container/restrictions.c
+
+ARGUMENTE:
+     ob
+          Das Living, das aus dem Behaelter genommen werden soll.
+     dest 
+          Das Ziel in das das Living ob bewegt werden soll.
+
+BESCHREIBUNG:
+     Mit dieser Funktion kann ein Behaelter pruefen, ob er das Living ob
+     sich bewegen lassen moechte oder nicht.
+
+RUeCKGABEWERT:
+     0, wenn das Living bewegt werden kann; ein Wert groesser als 0
+     zeigt an, dass das Living nicht bewegt werden soll.
+
+BEMERKUNGEN:
+     Wenn ob mit dem Flag M_NOCHECK bewegt wird, wird PreventLeave() zwar
+     aufgerufen, das Living wird jedoch auf jeden Fall aus dem Behaelter
+     bewegt, unabhaengig vom Rueckgabewert!
+
+SIEHE AUCH:
+     PreventInsertLiving(), /std/container/restrictions.c
+     PreventMove(), PreventLeave(), PreventInsert(),
+     NotifyMove(), NotifyLeave(), NotifyInsert(), NotifyRemove(),
+     move(), exit(), init(),
+     InitAttack, ExitAttack()
+----------------------------------------------------------------------------
+Last modified: 04.08.2007, Zesstra
diff --git a/doc/lfun/PreventMove b/doc/lfun/PreventMove
new file mode 100644
index 0000000..798ee0b
--- /dev/null
+++ b/doc/lfun/PreventMove
@@ -0,0 +1,81 @@
+PreventInsert()
+
+FUNKTION:
+     protected int PreventMove(object dest, object oldenv, int method);
+
+DEFINIERT IN:
+     /std/thing/moving.c
+     /std/living/moving.c
+     /std/player/moving.c
+
+ARGUMENTE:
+     dest
+          Das Ziel des Moves
+     oldenv
+          Das (Noch-)Environment des Objekts
+     method
+          Die Move-Methode(n), mit der/denen bewegt werden soll
+
+BESCHREIBUNG:
+     Mit dieser Funktion prueft ein Objekt, ob es von 'oldenv' nach 'dest'
+     bewegt werden mag. Dabei wird 'method' beruecksichtigt (z.B. schaltet
+     M_NOCHECK die meisten Pruefungen ab).
+     Bei Gegenstaenden wird z.B. P_NODROP, P_NOGET, das Gewicht, ob das Ziel
+     das Objekt aufnehmen will (MayAddWeight(), PreventInsert) oder auch
+     PreventLeave() geprueft.
+     Bei Lebewesen wird z.B. P_NO_TPORT in 'dest' und 'oldenv' und
+     PreventLeaveLiving/PreventInsertLiving() ausgewertet.
+     Bei Spielern wird auch noch getestet, ob method M_GO oder M_TPORT
+     enthaelt und ob das Ziel nur von Testspielern betreten werden kann.
+
+RUeCKGABEWERT:
+     0, wenn das Objekt bewegt werden darf.
+     Wenn es nicht bewegt werden darf, sind als Rueckgabewerte die in
+     /sys/moving.h definierten Move-Fehlerwerte zulaessig (s. move()). Sollte
+     hier ein ungueltiger Fehlerwert zurueckgegeben werden, wird das move()
+     ebenfalls abgebrochen und ME_DONT_WANT_TO_BE_MOVED zurueckgeben.
+
+BEMERKUNGEN:
+     Diese Funktion kann ueberschrieben, um feinere Kontrolle ueber die
+     Bewegungen des Objekt zu haben. Dabei aber bitte einige Dinge beachten:
+     1. Denkt daran, ggf. M_NOCHECK zu beruecksichtigen und und eure
+     Pruefungen nur zu machen, wenn es nicht in method vorkommt.
+     2. GANZ WICHTIG: Wenn ihr mit euren Pruefungen fertig sein und das Objekt
+     bewegt werden duerfte, die geerbten Pruefungen noch testen, also _IMMER_
+     das geerbte PreventMove() noch aufrufen und dessen Wert
+     zurueckgeben/beruecksichtigen, da sonst Pruefungen des Gewichts etc. 
+     nicht funktionieren oder bei Lebewesen die Prevent*() im Environment 
+     nicht gerufen werden!
+     3. Die Funktion ist nur objektintern zu verwenden, Call-Other von aussen
+     sind nicht moeglich, beim Ueberschreiben 'protected' nicht vergessen.
+     4. Nochmal: Geerbtes PreventMove() _NICHT VERGESSEN_!
+
+BEISPIELE:
+     Ein Objekt, was nur im Sternenlicht aufgenommen werden kann (warum
+     auch immer):
+
+     protected int PreventMove(object dest, object oldenv, int method) {
+       if ( (method & M_NOCHECK) ) {
+           // wenn mit M_NOCHECK bewegt, ist das Sternenlicht egal, nur
+           // geerbtes PreventMove() beruecksichten:
+           return ::PreventMove(dest, oldenv, method);
+       }
+       else if ( method & M_GET) {
+           // wenn es aufgenommen werden soll: nach Sternenlicht im Raum
+           // gucken:
+           if (objectp(dest) && 
+               (dest->QueryProp(P_LIGHT_TYPE) != LT_STARS))
+               return ME_CANT_BE_TAKEN;
+       }
+       // Fall-through:
+       return ::PreventMove(dest, oldenv, method);
+     }
+
+
+SIEHE AUCH:
+     PreventLeave(), NotifyInsert(), NotifyLeave(), MayAddObject(),
+     PreventInsertLiving(), PreventLeaveLiving(), NotifyMove(),
+     PreventMove(), MayAddWeight(), move(), /std/container/restrictions.c
+
+----------------------------------------------------------------------------
+Last modified: 04.08.2007, Zesstra
diff --git a/doc/lfun/Query b/doc/lfun/Query
new file mode 100644
index 0000000..2b39998
--- /dev/null
+++ b/doc/lfun/Query
@@ -0,0 +1,81 @@
+Query()
+FUNKTION:
+     public varargs mixed Query(string name, int Type);
+
+DEFINIERT IN:
+     /std/thing/properties.c
+
+ARGUMENTE:
+     string name - Property, deren Wert(e) ausgelesen werden
+     int type  - Art der gewuenschten Information.
+
+BESCHREIBUNG:
+     Der Wert einer der inneren Eigenschaften der Property 'name' wird
+     zurueckgegeben.  'Type' ist dabei einer der in /sys/thing/properties.h
+     und folgend aufgelisteten F_XXX-Werte:
+
+     F_VALUE (==0, Default)
+         Unter Umgehung einer eventuell vorhandenen Abfragemethode oder
+         _query_'name'() wird der Datenwert der Property 'name'
+         zurueckgegeben.
+     F_MODE
+          Die internen Flags der Property werden zurueckgegeben.Dies koennen
+          (logisch mit & verknuepft) sein:
+          SAVE  - Property soll bei save_object() gespeichert werden
+          PROTECTED - Objekt selbst/EM/Root kann Property manipulieren
+          SECURED  - wie PROTECTED, das Flag kann aber nicht
+                     zurueckgesetzt werden (immer SECURED)
+          NOSETMETHOD - niemand kann Property manipulieren
+                        (auch kein F_SET_METHOD oder _set_'name'())
+     F_SET_METHOD
+          Ein eventuell fuer die Property eingetragene F_SET_METHOD wird
+          zurueckgegeben.
+          (_set_'name'()-Methoden werden so nicht aufgefuehrt!)
+     F_QUERY_METHOD
+          Ein eventuell fuer die Property eingetragene F_QUERY_METHOD wird
+          zurueckgegeben.
+          (_query_'name'()-Methoden werden so nicht aufgefuehrt!)
+
+RUeCKGABEWERT:
+     Die gewuenschte Eigenschaft, abhaengig von 'Type'.
+
+BEMERKUNGEN:
+     - Query() sollte nicht zum regulaeren Auslesen des Inhalt einer
+       Property verwendet werden, da sowohl F_QUERY_METHOD als auch
+       libinterne _query_'name'()-Methoden umgangen werden und das Ergebnis
+       fuer so veraenderte Propertys undefiniert ist
+     - _set_'name'() und _query_'name'() sind alte Propertymethoden und
+       sollten nicht in normalen Objekten benutzt werden ->
+       F_SET_METHOD/F_QUERY_METHOD (ggf. mit PROTECTED) nutzen
+     - F_SET/F_QUERY_METHODs koennen 'protected' (empfohlen) oder 'static'
+       sein. _set_/_query_ duerfen momentan _nicht_ 'protected' sein, fuer
+       diese geht nur 'static' (in diesem Fall empfohlen).
+
+BEISPIELE:
+     // Auslesen des Wertes unter Umgehung einer Abfragemethode
+     Query(P_XYZ, F_VALUE);
+
+     // Auslesen der Flags erfaehrt man mit:
+     Query(P_XYZ, F_MODE);
+
+     // sauberes Programmieren, wir wollen eine F_QUERY_METHOD setzen,
+     // pruefen vorher auf Existenz:
+     if(this_player()->Query(P_FROG, F_QUERY_METHOD) {
+      write(break_string(
+       "Ich kann dich nicht weiter vor Froschsein schuetzen!",
+       "Der Magier winkt ab: ", 78));
+      say(break_string(
+       "Ich kann dich nicht weiter vor Froschsein schuetzen!",
+       "Der Magier sagt zu "+this_player()->name(WEM)+": ", 78));
+     } else {
+      this_player()->Set(P_FROG, #'query_protect_frog, F_QUERY_METHOD);
+      ...
+     }
+
+SIEHE AUCH:
+     Aehnliches: SetProp(L), QueryProp(L), Set(L)
+     Generell:  SetProperties(L), QueryProperties(L)
+     Konzept:  properties, /std/thing/properties.c
+     Sonstiges:  P_AUTOLOADOBJ
+
+28.03.2008, Zesstra
diff --git a/doc/lfun/QueryAllDoors b/doc/lfun/QueryAllDoors
new file mode 100644
index 0000000..3bbbfbb
--- /dev/null
+++ b/doc/lfun/QueryAllDoors
@@ -0,0 +1,32 @@
+FUNKTION:
+     mapping QueryAllDoors();
+
+DEFINIERT IN:
+     /obj/doormaster.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Es werden die Zustaende ALLER Tueren im MorgenGrauen ermittelt.
+
+RUECKGABEWERT:
+     Ein Mapping mit den Zustaenden aller Tueren im MorgenGrauen. Als
+     Schluessel dienen die Dateinamen der Start- und Zielraeume in
+     lexikographischer (alphabetischer) Reihenfolge, getrennt durch ":",
+     der Wert des Keys ist der aktuelle Zustand dieser Tuer, z.B.:
+
+    ([ "/d/inseln/schiffe/jolle:/d/inseln/schiffe/jolle/masch" : -1,
+       ...
+     ]);
+
+     Es gibt also eine Tuer zwischen Jolle und Maschinenraum, die
+     momenten geschlossen ist (-1 = D_STATUS_CLOSED).
+
+
+SIEHE AUCH:
+    NewDoor(), QueryDoorKey(), QueryDoorStatus(), SetDoorStatus(),
+    /std/room/doors.c, /obj/doormaster.c, GetPhiolenInfos(), P_DOOR_INFOS
+
+-----------------------------------------------------------------------------
+Letzte Aenderung: Don, 08.05.2014, Gabylon
diff --git a/doc/lfun/QueryArmourByType b/doc/lfun/QueryArmourByType
new file mode 100644
index 0000000..22d43e9
--- /dev/null
+++ b/doc/lfun/QueryArmourByType
@@ -0,0 +1,64 @@
+QyeryArmourByType()
+
+FUNKTION:
+     mixed QueryArmourByType(string type)
+
+DEFINIERT IN:
+     /std/living/combat.c
+
+ARGUMENTE:
+     string type
+        Typ der Ruestung aus /sys/combat.h, auf die getestet werden soll.
+
+BESCHREIBUNG:
+     Abfrage, ob das Lebewesen eine Ruestung des angegebenen Typs traegt.
+
+     Zurueckgegeben wird je nach Tragestatus und <type>:
+     * 0, falls das Lebewesen die gesuchte Ruestungsart nicht traegt
+     * im Erfolgsfall das Ruestungsobjekt
+     * falls <type> AT_MISC ist:
+       * ({}), wenn es keine AT_MISC-Ruestung traegt
+       * ein Array von AT_MISC-Ruestungen
+     * falls <type> 0 ist: ein Mapping, das diese Informationen mit dem
+       Ruestungstypen als Schluessel enthaelt:
+       ([AT_MISC: ({[object], ...}),
+         AT_...: <object>,
+         ... ])
+
+BEMERKUNG:
+     Ist <type> AT_MISC, so wird auf jeden Fall ein Array zurueckgegeben!
+
+BEISPIELE:
+     Wir wollen wissen, ob this_player() Handschuhe traegt:
+
+     if (objectp(this_player()->QueryArmourByType(AT_GLOVE)))
+       ...
+
+
+     Wir bauen einen Tuersteher, der auf AT_MISC-Kleidung achtet:
+
+     if (sizeof(this_player()->QueryArmourByType(AT_MISC)) > 3) {
+       if(this_player()->ReceiveMsg(
+            "Du darfst nicht passieren, Du hast zuviele "
+            "unpassende Dinge an!",
+            MT_LISTEN|MSG_DONT_STORE, MA_TELL,
+            "Der Waechter teilt Dir mit: ")<MSG_DELIVERED && // taub?
+          this_player()->ReceiveMsg(
+            "Der Waechter haelt dich auf.", MT_LOOK)<MSG_DELIVERED) // blind?
+            this_player()->ReceiveMsg(
+              "Jemand haelt dich auf.", MT_FEEL); // nu aber!
+       // Aufhalten!
+     } else this_player()->ReceiveMsg(
+              "Du darfst passieren, viel Spass im Casino!",
+              MT_LISTEN|MSG_DONT_STORE, MA_TELL,
+              "Der Waechter teilt Dir mit: ");
+       // im Erfolgsfall ist es uns egal, wenn es der Spieler nicht
+       // liest: er wird dann eben "wortlos" durchgelassen
+
+SIEHE AUCH:
+     Wear(), WearArmour(), WearClothing(), Unwear(), UnwearArmour(),
+     UnwearClothing(), FilterClothing(),
+     P_ARMOUR_TYPE, P_CLOTHING, P_ARMOURS,
+     /std/living/combat.c
+
+02.02.2016, Gloinson
diff --git a/doc/lfun/QueryArrived b/doc/lfun/QueryArrived
new file mode 100644
index 0000000..33cf887
--- /dev/null
+++ b/doc/lfun/QueryArrived
@@ -0,0 +1,26 @@
+QueryArrived()
+
+FUNKTION:
+     mixed QueryArrived();
+
+DEFINIERT IN:
+     /std/transport.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Diese Funktion ermittelt, ob sich der Transporter momentan an einer
+     Haltestelle befindet (und bestiegen oder verlassen werden kann) oder ob
+     er unterwegs ist.
+
+RUeCKGABEWERT:
+     Null, wenn der Transporter unterwegs ist. Liegt der Transporter an
+     einer Haltestelle, so wird der mit AddRoute als name uebergebene String
+     zurueckgegeben.
+
+SIEHE AUCH:
+     AddRoute(), /std/transport.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:21:47 1996 by Wargon
diff --git a/doc/lfun/QueryArticle b/doc/lfun/QueryArticle
new file mode 100644
index 0000000..0993b3e
--- /dev/null
+++ b/doc/lfun/QueryArticle
@@ -0,0 +1,92 @@
+QueryArticle()
+
+FUNKTION:
+     varargs string QueryArticle(int casus, int dem, int force);
+
+DEFINIERT IN:
+     /std/thing/language.c
+
+ARGUMENTE:
+     casus
+          Der Fall, in dem der Artikel gewuenscht wird.
+          (Konstanten aus /sys/thing/language.h: WER, WEM, WESSEN, WEN.)
+
+     dem
+          Wird ein bestimmter oder ein unbestimmter Artikel verlangt?
+             + dem = 0: Unbestimmter Artikel!
+             + dem = 1: Bestimmter Artikel!
+             + dem = 2: Finde selbst heraus, welcher Artikel passt!
+
+     force
+          Falls ungleich Null, so wird auf jeden Fall ein Artikel
+          zurueckgegeben, trotz P_ARTICLE == 0.
+
+BESCHREIBUNG:
+     Diese Funktion gibt einen zum Geschlecht des Objektes passenden Artikel
+     zurueck, der in den passenden Fall dekliniert wird.
+
+     Das Herausfinden des passenden Artikels bei 'dem' = 2 bezieht sich auf
+     Situationen, in denen mehrere gleichnamige Objekte im selben Environment
+     sind. Man 'nimmt' dann zB nicht "den Stamm" sondern "einen Stamm".
+
+     Ist P_ARTICLE = 0, so wird ein Leerstring zurueckgegeben, es sei denn,
+     man uebergibt in dem Argument 'force' einen Wert ungleich Null.
+
+BEMERKUNGEN:
+     Achtung: bei gueltigem Artikel wird ein Leerzeichen angehaengt!
+
+     Name()/name() nutzen bereits QueryArticle(), wenn P_ARTICLE gesetzt
+     ist. Deshalb muss man sich "Eines Orks" nicht selbst aus dem
+     QueryArticle() und dem Namen zusammenbasteln, wenn mehrere Orks
+     im Raum herumstehen und man eine Nachricht wie:
+       "Du haust den Ork." und folgend
+       "[Des|Eines] Orks Nase schwillt an."
+     haben moechte:
+       "Du haust "+ork->name(WEN, 1)+". "
+       ork->Name(WESSEN, 2)+" Nase schwillt an."
+
+RUeCKGABEWERT:
+     * gewuenschter Artikel als String plus Leerzeichen (!) ODER
+     * Leerstring
+
+BEISPIELE:
+     // "X haut Y auf die Nase. [Der|Die|Das] ist nicht beeindruckt."
+     // Man will:
+     // * auf jeden Fall einen Artikel, auch wenn kein P_ARTICLE gesetzt ist
+     // * nur den bestimmten Artikel
+     send_room(this_object(),
+       pl1->Name(WER)+" haut "+pl2->name(WEM)+" auf die Nase. "+
+       capitalize(pl2->QueryArticle(WER, 1, 1))+"ist nicht beeindruckt.",
+       MT_LOOK|MT_LISTEN, 0, 0, ({pl1, pl2}));
+
+     // "X gibt dir Y. [Er|Sie|Es] prueft [den|die|das] ..."
+     // Man will:
+     // * auf jeden Fall einen Artikel, auch wenn kein P_ARTICLE gesetzt ist
+     // * nur den bestimmten Artikel
+     send_room(this_object(),
+       pl1->Name(WER)+" gibt "+pl2->name(WEM)+" "+obj->name(WER)+". "+
+       capitalize(pl2->QueryPronoun(WER))+" prueft "+
+       ob->QueryArticle(WEN, 1, 1)+"...",
+       MT_LOOK|MT_LISTEN, 0, 0, ({pl1, pl2}));
+
+     // "Dir faellt X auf den Kopf. Du siehst [die|den|das|eine|einen|eines "
+     // "auf dem Boden liegen. [Sie|Er|Es] sieht blutig aus. Aua. Ist das "
+     // "von dir?"
+     // Man will:
+     // * auf jeden Fall einen Artikel, auch wenn kein P_ARTICLE gesetzt ist
+     // * bestimmte/unbestimmte Artikel, wenn bereits gleiche Gegenstaende
+     //   (zB Kokosnuesse) auf dem Boden liegen ...
+     ob->move(environment(), M_NOCHECK); // vorher machen!
+     pl->ReceiveMsg(
+       "Dir faellt "+ob->name(WER, 0)+" auf den Kopf. Du siehst "+
+       ob->QueryArticle(WEN, 2, 1)+" auf dem Boden liegen. "+
+       capitalize(ob->QueryPronoun(WER))+" sieht blutig ...
+
+SIEHE AUCH:
+     Aehnlich:  SuggestArticle(), query_c_article(), query_g_suffix()
+     Sonstiges: QueryOwn(), QueryDu(),
+                QueryPronoun(), QueryPossPronoun()
+                DeclAdj()
+                name()
+
+9. Jun 2016, Gloinson
diff --git a/doc/lfun/QueryAttribute b/doc/lfun/QueryAttribute
new file mode 100644
index 0000000..60a68c9
--- /dev/null
+++ b/doc/lfun/QueryAttribute
@@ -0,0 +1,32 @@
+QueryAttribute()
+FUNKTION:
+     int QueryAttribute(string attr)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+
+ARGUMENTE:
+     attr       - interessierendes Gesamtattribut
+
+BESCHREIBUNG:
+     Das Attribut samt seiner Offsets (Modifier) wird zurueckgegeben.
+
+RUeCKGABEWERT:
+     Wert des Attributes, bei Spielern nicht groesser als 30.
+
+BEISPIELE:
+     if (this_player()->QueryAttribute(A_CON) > 20)
+       write("Du schaffst es den Berg hoch. Aber nur muehsam.\n");
+
+BENERKUNGEN:
+     Wenn es um Attributabfragen geht, bitte diese Methode verwenden!
+
+SIEHE AUCH:

+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+	SetTimedAttrModifier(), QueryTimedAttrModifier(), 

+	DeleteTimedAttrModifier(),

+	P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_TIMED_ATTR_MOD,

+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+----------------------------------------------------------------------------

+Last modified: Tue Jul 27 20:00:20 2004 by Muadib

diff --git a/doc/lfun/QueryAttributeOffset b/doc/lfun/QueryAttributeOffset
new file mode 100644
index 0000000..f938ab8
--- /dev/null
+++ b/doc/lfun/QueryAttributeOffset
@@ -0,0 +1,26 @@
+QueryAttributeOffset()
+FUNKTION:
+     int QueryAttributeOffset(string attr)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+
+ARGUMENTE:
+     attr	-	gesuchter AttributOffset
+
+BESCHREIBUNG:
+     Die Offsets des Attributs (inklusive Modifier) werden zurueckgegeben.
+
+BEISPIELE:
+     if(this_player()->QueryAttributeOffset(A_STR)<0)
+      write("Du bist geschwaecht.\n");
+
+SIEHE AUCH:

+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+	SetTimedAttrModifier(), QueryTimedAttrModifier(), 

+	DeleteTimedAttrModifier(),

+	P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_TIMED_ATTR_MOD,

+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+----------------------------------------------------------------------------

+Last modified: Tue Jul 27 20:00:20 2004 by Muadib

diff --git a/doc/lfun/QueryBuyFact b/doc/lfun/QueryBuyFact
new file mode 100644
index 0000000..435e8be
--- /dev/null
+++ b/doc/lfun/QueryBuyFact
@@ -0,0 +1,22 @@
+QueryBuyFact()
+
+FUNKTION:
+     int QueryBuyFact();
+
+DEFINIERT IN:
+     /std/laden.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Gibt den mit SetBuyFact() gesetzten Faktor zurueck.
+
+RUeCKGABEWERT:
+     Der Einkaufspreismultiplikator.
+
+SIEHE AUCH:
+     SetBuyFact(), /std/laden.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:21:57 1996 by Wargon
diff --git a/doc/lfun/QueryBuyValue b/doc/lfun/QueryBuyValue
new file mode 100644
index 0000000..261a435
--- /dev/null
+++ b/doc/lfun/QueryBuyValue
@@ -0,0 +1,52 @@
+QueryBuyValue()

+

+Funktion:

+    static varargs int QueryBuyValue(mixed ob, object client)

+

+Definiert in:

+    /std/room/shop

+

+Argumente:

+    ob: 

+      Das zu kaufende Objekt (String oder object).

+      Im Normalfall handelt es sich um ein Objekt. Ausnahme sind 

+      Gegenstaende, die mit AddFixedObject() hinzugefuegt wurden.

+    client: 

+      Der Kaeufer.

+

+Beschreibung:

+    Ermittelt den Preis, den <client> fuer <ob> zu bezahlen hat.

+

+Rueckgabewert:

+    Der Preis als Integer.

+

+Beispiel:

+    Ein Haendler, der Spielern die ihm geholfen haben einen Rabatt von 10% 

+    gewaehrt:

+    

+    object *helpers;

+    protected void create()

+    {

+      ::create();

+      helpers=({});

+      ...

+    }

+    

+    static varargs int QueryBuyValue(mixed ob, object client)

+    {

+      if(member(helpers,client)!=-1)

+      {

+        return ::QueryBuyValue(ob,client)*9/10;

+      }

+      return ::QueryBuyValue(ob,client);

+    }

+

+Siehe auch:

+    Funktionen:

+      AddFixedObject(), RemoveFixedObject(), SetStorageRoom(), 

+      QueryStorageRoom(), QueryBuyFact(), sell_obj(), buy_obj()

+    Properties:

+      P_KEEPER, P_MIN_STOCK, P_STORE_CONSUME

+

+------------------------------------------------------------------------------

+Letzte Aenderung: 21.05.2014, Bugfix
\ No newline at end of file
diff --git a/doc/lfun/QueryCoinsPerUnits b/doc/lfun/QueryCoinsPerUnits
new file mode 100644
index 0000000..948c1b0
--- /dev/null
+++ b/doc/lfun/QueryCoinsPerUnits
@@ -0,0 +1,31 @@
+QueryCoinsPerUnit()
+
+FUNKTION:
+     int *QueryCoinsPerUnits();
+
+DEFINIERT IN:
+     /std/unit.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Liefert das Wertverhaeltnis fuer die Einheiten zurueck.
+
+RUeCKGABEWERT:
+     Ein Array von zwei Zahlen. Die erste Zahl ist der Wert der in der
+     zweiten Zahl angegebenen Einheiten.
+
+BEISPIELE:
+     Steht im Unit-Objekt folgende Wertzuweisung:
+
+       SetCoinsPerUnits(7,2);
+
+     so liefert QueryCoinsPerUnits() als Ergebnis ({7, 2}).
+
+SIEHE AUCH:
+     SetCoinsPerUnits(), SetGramsPerUnits(), QueryGramsPerUnit(),
+     /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:22:02 1996 by Wargon
diff --git a/doc/lfun/QueryDamage b/doc/lfun/QueryDamage
new file mode 100644
index 0000000..99c4fed
--- /dev/null
+++ b/doc/lfun/QueryDamage
@@ -0,0 +1,39 @@
+QueryDamage()
+
+FUNKTION:
+     int QueryDamage(object enemy);
+
+DEFINIERT IN:
+     /std/weapon/combat.c
+
+ARGUMENTE:
+     enemy
+          Der Gegner, gegen den die Waffe eingesetzt wird.
+
+BESCHREIBUNG:
+     Dies ist die zentrale Funktion der Waffe. Sie wird in jeder Kampfrunde
+     von Attack() aus aufgerufen und gibt den Schaden zurueck, den der
+     Gegner abwehren muss.
+
+     In den Schaden gehen sowohl die Staerke der Waffe als auch die Staerke
+     des Traegers der Waffe ein.
+
+     Wurde eine HitFunc() angegeben, so wird diese mit den gleichen
+     Parametern wie QueryDamage() aufgerufen.
+
+RUeCKGABEWERT:
+     Die Staerke des Schlages fuer diese Kampfrunde. Sie ermittelt sich als
+     Zufallszahl zwischen 0 und einem Wert, der sich aus der Staerke der
+     Waffe und der Staerke ihres Traegers ergibt. Das Ergebnis des
+     HitFunc()-Aufrufs wird zu dieser Zahl hinzugezaehlt.
+
+BEMERKUNGEN:
+     Auch wenn man eine HitFunc() verwendet, darf der Rueckgabewert
+     insgesamt nicht groesser als 200 werden. Im Zweifelsfall sollte 
+     man sich mit der zustaendigen Balance besprechen!
+
+SIEHE AUCH:
+     HitFunc(), Attack(), /std/weapon.c, grenzwerte
+
+----------------------------------------------------------------------------
+Last modified: Fre Feb 16.02.01 12:58:00 von Tilly
diff --git a/doc/lfun/QueryDefend b/doc/lfun/QueryDefend
new file mode 100644
index 0000000..4d3d3e0
--- /dev/null
+++ b/doc/lfun/QueryDefend
@@ -0,0 +1,45 @@
+QueryDefend()
+
+FUNKTION:
+     int QueryDefend(string|string* dtyp, int|mapping spell, object enemy);
+
+DEFINIERT IN:
+     /std/armour/combat.c
+
+ARGUMENTE:
+     dtyp  - Schadenstypen der Angriffsart
+     spell - 0       bei konventionellem Angriff,
+             != 0    bei Angriff mit einem nichtphysischen Spell,
+             mapping bei genaueren Angaben zur Wirkung
+     enemy - Der angreifende Gegner
+
+BESCHREIBUNG:
+     Dies ist die zentrale Funktion einer Ruestung. Sie wird in jeder
+     Kampfrunde aus /std/living/combat::Defend() fuer jede Ruestung aufgerufen,
+     die der Spieler angezogen hat.
+
+     Der Schutzwert von P_AC entfaltet seine Wirkung nur bei konventionellen
+     Angriffen:
+     * wenn 'spell' 0 ist (bei Aufruf aus der Defend heraus ausgeschlossen)
+     * wenn 'spell' ein Mapping mit dem Flag SP_PHYSICAL_ATTACK != 0 UND
+                    in 'dtyp' mindestens ein physischer Schaden enthalten ist
+
+RUeCKGABEWERT:
+     Die Ruestungsstaerke in dieser Kampfrunde. Sie ermittelt sich als
+     Zufallszahl zwischen 0 und P_AC, zuzueglich des Ergebnisses des
+     DefendFunc()-Aufrufs.
+
+BEMERKUNGEN:
+     Auch wenn man eine DefendFunc() benutzt, darf der Rueckgabewert
+     insgesamt nicht groesser werden als der fuer den Ruestungstyp
+     maximal zulaessige!
+
+SIEHE AUCH:
+     Ruestungen: P_ARMOUR_TYPE, P_NR_HANDS, P_ARMOURS, P_WORN
+     Schutz:     P_AC, Defend(), DefendFunc
+     Sonstiges:  P_EQUIP_TIME, P_LAST_USE, P_DAM_TYPE
+     Verwandt:   QueryArmourByType(L), P_WEAPON, FilterClothing(),
+                 FilterArmours()
+     Resistenz:  P_RESISTANCE_STRENGTHS, CheckResistance(L)
+
+28.Jul 2014 Gloinson
diff --git a/doc/lfun/QueryDisguise b/doc/lfun/QueryDisguise
new file mode 100644
index 0000000..c30aa3f
--- /dev/null
+++ b/doc/lfun/QueryDisguise
@@ -0,0 +1,29 @@
+QueryDisguise()
+
+FUNKTION:
+     mixed QueryDisguise();
+
+DEFINIERT IN:
+     ???
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Prueft, ob der Spieler durch einen Shadow (meistens der Tarnhelm) 
+     'manipuliert' ist.
+
+RUeCKGABEWERT:
+     0, wenn dies nicht der Fall ist, ansonsten die Beschreibung des Shadow
+     vom Typ string.
+
+BEMERKUNGEN:
+     In Waffen / Ruestungen u.ae. die P_RESTRICTIONS gesetzt haben,
+     eruebrigt sich eine Abfrage auf QueryDisguise(), da dies bereits im
+     restriction_checker erledigt wird.
+
+SIEHE AUCH:
+     P_RESTRICTIONS, /std/restriction_checker.c
+
+----------------------------------------------------------------------------
+Last modified: Mon Mar 26 14:48:20 2001 von Tilly
diff --git a/doc/lfun/QueryDoorKey b/doc/lfun/QueryDoorKey
new file mode 100644
index 0000000..cfd6a29
--- /dev/null
+++ b/doc/lfun/QueryDoorKey
@@ -0,0 +1,48 @@
+FUNKTION:
+     mixed QueryDoorKey();
+
+DEFINIERT IN:
+     versch. Schluesseln
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Diese Funktion wird in einem Schluessel aufgerufen, wenn man mit diesem
+     eine Tuer auf- oder abschliessen will. Anhand des Rueckgabewertes wird
+     entschieden, ob der Schluessel passt oder nicht.
+
+RUECKGABEWERT:
+     String oder Array von Strings der Raumpfade, deren gemeinsame Tueren
+     sich mit diesem Schluessel auf- bzw. abschliessen lassen. Die Keys sind
+     dabei die Raumpfade, getrennt durch ein ":". Dabei muessen die Pfade
+     in lexikographischer (alphabetischer) Reihenfolge sortiert sein:
+
+     "<name_raum_1>:<name_raum_2>"
+
+BEISPIELE:
+     Ein Schluessel, mit dem sich eine einzige Tuer oeffnen laesst (falls es
+     jemals eine Tuer zwischen Karate- und Abenteurergilde geben sollte...):
+
+     string QueryDoorKey()
+     {
+       return "/gilden/abenteurer:/gilden/karate";
+     }
+
+     Ein Schluessel, der in mehreren Tueren passt:
+
+     string* QueryDoorKey()
+     {
+       return ({ "/gilden/abenteurer:/players/wargon/workroom",
+                 "/gilden/abenteurer:/gilden/karate",
+                 "/players/jof/workroom:/players/wargon/workroom"
+              });
+     }
+
+
+SIEHE AUCH:
+    NewDoor(), QueryDoorStatus(), SetDoorStatus(), P_DOOR_INFOS,
+    /std/room/doors.c, /obj/doormaster.c, GetPhiolenInfos(), QueryAllDoors()
+
+-----------------------------------------------------------------------------
+Letzte Aenderung: Don, 08.05.2014, Gabylon
diff --git a/doc/lfun/QueryDoorStatus b/doc/lfun/QueryDoorStatus
new file mode 100644
index 0000000..c1ee6b8
--- /dev/null
+++ b/doc/lfun/QueryDoorStatus
@@ -0,0 +1,28 @@
+FUNKTION:
+     int QueryDoorStatus(string dest);
+
+DEFINIERT IN:
+     /obj/doormaster.c
+
+ARGUMENTE:
+     <dest> = Zielraum der Tuer.
+
+BESCHREIBUNG:
+     Es wird der Zustand der Tuer, die von diesem Raum nach <dest> fuehrt,
+     ermittelt.
+
+RUeCKGABEWERT:
+     0 bei nicht vorhandene Tuer, ansonsten einer der folgenden Zustaende (aus
+       <doorroom.h>):
+
+       D_STATUS_LOCKED - Tuer abgeschlossen
+       D_STATUS_CLOSED - Tuer geschlossen
+       D_STATUS_OPEN   - Tuer geoeffnet
+
+
+SIEHE AUCH:
+    NewDoor(), QueryDoorKey(), SetDoorStatus(), P_DOOR_INFOS,
+    /std/room/doors.c, /obj/doormaster.c, GetPhiolenInfos(), QueryAllDoors()
+
+-----------------------------------------------------------------------------
+Letzte Aenderung: Don, 08.05.2014, Gabylon
diff --git a/doc/lfun/QueryDu b/doc/lfun/QueryDu
new file mode 100644
index 0000000..9be62a8
--- /dev/null
+++ b/doc/lfun/QueryDu
@@ -0,0 +1,39 @@
+QueryDu()
+
+FUNKTION:
+     varargs string QueryDu(int casus, int gender, int anzahl);
+
+DEFINIERT IN:
+     /std/thing/language.c
+
+ARGUMENTE:
+     casus
+          Der Fall fuer die Anrede.
+
+     gender
+          Das Geschlecht des anzuredenden Objektes.
+
+     anzahl
+          Ist nur ein Objekt anzusprechen oder mehrere?
+
+BESCHREIBUNG:
+     Diese Funktion liefert die dem Anlass entsprechende Anrede fuer ein
+     Objekt.
+
+RUeCKGABEWERT:
+     Ein String mit der Anrede.
+
+BEISPIELE:
+
+     printf("%s setzt %s auf den Boden.\n",
+           capitalize(QueryDu(WER, TP->QueryProp(P_GENDER), SINGULAR),
+           QueryDu(WEN, TP->QueryProp(P_GENDER), SINGULAR));
+
+     (In den meisten Faellen kann man hier auch direkt "Du" und "dich"
+     einsetzen.)
+
+SIEHE AUCH:
+     /std/thing/language.c
+     QueryPossPronoun(), QueryOwn(), QueryPronoun()
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:22:27 1996 by Wargon
diff --git a/doc/lfun/QueryEnemies b/doc/lfun/QueryEnemies
new file mode 100644
index 0000000..7c393b4
--- /dev/null
+++ b/doc/lfun/QueryEnemies
@@ -0,0 +1,33 @@
+FUNKTION:
+	mixed QueryEnemies();
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	keine
+
+RUeCKGABEWERT:
+	Array mit Array aus bekannten Gegnern und Array aus Zeiten
+
+BESCHREIBUNG:
+	Diese Funktion enthaelt ein Array, das zwei Elemente enthaelt, die
+	wiederum Arrays sind:
+	  1. Array: Die bekannten Gegner als Objektpointer.
+	  2. Array: Die zugeordneten Zeiten, wie lange ein Gegner noch als
+	            solcher bekannt sein soll.
+	Im Normalfall wird ein Gegner dann bekannt, wenn man gezielt
+	jemanden atackiert, oder wenn man einen Angriff abwehren muss.
+	Dann wird der Gegner intern abgespeichert, und es wird eine Zeit
+	gesetzt, die dann runtergezaehlt wird. Ist die Zeit auf 0, so wird
+	der Gegner wieder automatisch ausgetragen.
+	(Standardmaessig betraegt diese Zeit 600 Sekunden (300 Heartbeats).)
+	Man kann sich die Gegner auch in Form eines Mappings zurueckgeben
+	lassen. Dies erreicht man mittels der Funktion GetEnemies().
+
+SIEHE AUCH:
+	Kill(), Attack(), Defend(), do_my_heart_beat(), PresentEnemies(),
+	GetEnemies(), SelectEnemy(), QueryPreferedEnemy(), P_PREFERED_ENEMY
+
+----------------------------------------------------------------------------
+29.12.2007, Zesstra
diff --git a/doc/lfun/QueryFlaw b/doc/lfun/QueryFlaw
new file mode 100644
index 0000000..8137d81
--- /dev/null
+++ b/doc/lfun/QueryFlaw
@@ -0,0 +1,41 @@
+QueryFlaw()
+
+FUNKTION:
+     mixed *QueryFlaw();
+
+DEFINIERT IN:
+     /std/armour/combat.c,
+     /std/weapon/combat.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     QueryFlaw() liefert Informationen ueber den Grad der Abnutzung der
+     Waffe bzw. Ruestung. Der Zustand wird als Array mit der Zahl der
+     TakeFlaw()-Aufrufe und dem Datum des ersten TakeFlaw()-Aufrufs
+     zurueckgegeben.
+
+RUeCKGABEWERT:
+     Ein Array mit drei Elementen:
+       1. der aktuelle Zaehlerstand
+       2. Zeit der ersten Benutzung
+       3. die Zeit der ersten Benutzung als String
+
+BEISPIELE:
+     Den aktuellen Abnutzungsgrad einer Ruestung kann man sich wie folgt
+     anzeigen lassen:
+
+     mixed *flaw;
+
+     flaw = ruestung->QueryFlaw();
+
+     printf("Zaehlerstand: %d, erster Schlag: %s\n", flaw[0], flaw[2]);
+     // oder analog:
+     printf("Zaehlerstand: %d, erster Schlag: %s\n", flaw[0], dtime(flaw[1]));
+
+SIEHE AUCH:
+     TakeFlaw(), /std/armour/combat.c, /std/weapon/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:22:32 1996 by Wargon
diff --git a/doc/lfun/QueryGenderString b/doc/lfun/QueryGenderString
new file mode 100644
index 0000000..967a16e
--- /dev/null
+++ b/doc/lfun/QueryGenderString
@@ -0,0 +1,23 @@
+QueryGenderString()
+
+FUNKTION:
+     string QueryGenderString();
+
+DEFINIERT IN:
+     /std/thing/language.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Es wird ein String mit dem Geschlecht des Objektes zurueckgegeben
+     ("maennlich", "weiblich", "saechlich").
+
+RUeCKGABEWERT:
+     Der String mit dem Geschlecht.
+
+SIEHE AUCH:
+     /std/thing/language.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:23:00 1996 by Wargon
diff --git a/doc/lfun/QueryGramsPerUnits b/doc/lfun/QueryGramsPerUnits
new file mode 100644
index 0000000..3549e54
--- /dev/null
+++ b/doc/lfun/QueryGramsPerUnits
@@ -0,0 +1,31 @@
+QueryGramsPerUnits()
+
+FUNKTION:
+     int *QueryGramsPerUnits();
+
+DEFINIERT IN:
+     /std/unit.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Liefert das Gewichtsverhaeltnis fuer die Einheiten zurueck.
+
+RUeCKGABEWERT:
+     Ein Array von zwei Zahlen. Die erste Zahl ist das Gewicht der in der
+     zweiten Zahl angegebenen Einheiten.
+
+BEISPIELE:
+     Steht im Unit-Objekt folgende Gewichtszuweisung:
+
+       SetGramsPerUnits(4,1);
+
+     so liefert QueryGramsPerUnits() als Ergebnis ({4, 1}).
+
+SIEHE AUCH:
+     SetCoinsPerUnits(), QueryCoinsPerUnits() SetGramsPerUnits(),
+     /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:22:39 1996 by Wargon
diff --git a/doc/lfun/QueryGroupedKeys b/doc/lfun/QueryGroupedKeys
new file mode 100644
index 0000000..ce7e316
--- /dev/null
+++ b/doc/lfun/QueryGroupedKeys
@@ -0,0 +1,26 @@
+QueryGroupedKeys()
+
+FUNKTION:
+    mixed *QueryGroupedKeys()
+
+DEFINIERT IN:
+    /secure/questmaster.c
+
+ARGUMENTE:
+    keine
+
+RUECKGABEWERT:
+    Array mit Arrays mit den Schluesselwoertern der Quests
+
+BESCHREIBUNG:
+    Diese Funktion liefert ein Array mit mehreren Arrays zurueck, die
+    die Schluesselwoerter der Quests enthalten. Dabei enthaelt das 
+    erste Array die Schluesselwoerter der Quests der ersten Gruppe etc.
+    Das letzte Array enthaelt die Gruppe der optionalen Quests.
+
+SIEHE AUCH:
+    GiveQuest, QueryQuest, /secure/questmaster.h
+
+----------------------------------------------------------------------------
+Zuletzt geaendert: Son, 19. Nov 2000, 13:22:15 von Zook.
+
diff --git a/doc/lfun/QueryGuest b/doc/lfun/QueryGuest
new file mode 100644
index 0000000..cc9971b
--- /dev/null
+++ b/doc/lfun/QueryGuest
@@ -0,0 +1,33 @@
+QueryGuest()
+FUNKTION:
+     int QueryGuest();
+
+DEFINIERT IN:
+     /std/player/base
+
+BESCHREIBUNG:
+     Auf der uid basierende Hilfsfunktion, um Gaeste zu identifizieren.
+
+RUeCKGABEWERT:
+     1, wenn Spielerobjekt ein Gast ist; 0 sonst
+
+BEISPIELE:
+     if(this_interactive()->QueryGuest())
+     {
+       (this_interactive()->ReceiveMsg(
+          "Wir bedienen hier nur ordentliche Charaktere.",
+          MT_LISTEN, MA_SAY,
+          "Der Wirt sagt: ") != MSG_SENSE_BLOCK) ||
+       (this_interactive()->ReceiveMsg(
+          "Der Wirt gestikuliert dich hinaus.",
+          MT_LOOK, MA_LOOK) != MSG_SENSE_BLOCK) ||
+       (this_interactive()->ReceiveMsg(
+          "Irgendwer stupst dich an. Du sollst wohl gehen.",
+          MT_FEEL, MA_FEEL));
+       return 1;
+     }
+
+SIEHE AUCH:
+     getuid()
+
+14. Mai 2015 Gloinson
diff --git a/doc/lfun/QueryHealInfo b/doc/lfun/QueryHealInfo
new file mode 100644
index 0000000..07b7542
--- /dev/null
+++ b/doc/lfun/QueryHealInfo
@@ -0,0 +1,26 @@
+QueryHealInfo()
+
+FUNKTION:
+     string QueryHealInfo();
+
+DEFINIERT IN:
+     /std/corpse.c,
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     QueryHealInfo() liefert einen Standardtext zurueck, der grob Auskunft 
+     darueber gibt, welche Auswirkungen das Essen einer Leiche haette. 
+     Diese Info kann z.B. von Gilden verwendet werden, die P_HEAL (s.dort)
+     einer Leiche nicht selbst auswerten wollen.
+
+RUeCKGABEWERT:
+     Ein nicht umbrochener String. Fuer Zeilenumbrueche kann derjenige,
+     der den Text ausliest, selbst sorgen.
+
+SIEHE AUCH:
+     /std/corpse.c, P_HEAL
+
+----------------------------------------------------------------------------
+Last modified: 31.03.2008, Arathorn
diff --git a/doc/lfun/QueryMaterial b/doc/lfun/QueryMaterial
new file mode 100644
index 0000000..71e3a33
--- /dev/null
+++ b/doc/lfun/QueryMaterial
@@ -0,0 +1,34 @@
+QueryMaterial(L)
+FUNKTION:
+     int QueryMaterial(string mat)
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     string mat -	Material, auf das getestet werden soll
+
+BESCHREIBUNG:
+     Testet, ob ein Gegenstand aus dem angegebenen Material besteht
+     und gibt dessen Anteil zurueck.
+     Die Rueckgabe ist im Wertebereich -100 (Antigruppen) bis +100 (%).
+
+RUECKGABEWERT:
+     Anteil in Prozent.
+
+BEISPIELE:
+     if(ob->QueryMaterial(MAT_IVORY)<=0)
+       write("Daraus kannst Du keine Billiardkugeln schnitzen!\n");
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Methoden:    QueryMaterialGroup(), MaterialList(),
+     Listen:	  AllMaterials(), AllGroups(), Dump()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+     Sonstiges:	  P_MATERIAL_KNOWLEDGE
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/QueryMaterialGroup b/doc/lfun/QueryMaterialGroup
new file mode 100644
index 0000000..c3b6860
--- /dev/null
+++ b/doc/lfun/QueryMaterialGroup
@@ -0,0 +1,50 @@
+QueryMaterialGroup(L)
+FUNKTION:
+     int QueryMaterialGroup(string grp)
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     string grp		- Materialgruppe, auf die getestet werden soll
+
+BESCHREIBUNG:
+     Liefert eine Angabe, zu welchem Anteil das Objekt aus Materialien
+     dieser Gruppe besteht.
+     Die Rueckgabe ist im Wertebereich -100 (Antigruppen) bis +100 (%).
+
+RUECKGABEWERT:
+     Anteil in Prozent.
+
+BEMERKUNGEN:
+     Ruft MaterialGroup() an der MATERIALDB.
+
+BEISPIELE:
+     // kann man damit was anfangen?
+     if(ob->QueryMaterialGroup(MATGROUP_METAL)<50)
+       write("Der Schmied sagt: Daraus kann ich kein Schwert fertigen.\n");
+
+     // verbrennt das Ding?
+     if(ob->QueryMaterialGroup(MATGROUP_INFLAMMABLE)>50) {
+       write(ob->Name(WER)+" geht in Flammen auf.\n");
+       ob->remove();
+     }
+
+     // wie magnetisch ist es denn?
+     if(ob->QueryMaterialGroup(MATGROUP_MAGNETIC)>50)
+      write(break_string(
+       ob->Name(WER)+" flutscht Dir aus der Hand und bleibt am Magneten "
+		     "kleben!",78));
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Methoden:    QueryMaterial(), MaterialList(),
+     Listen:	  AllMaterials(), AllGroups(), Dump()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+     Sonstiges:	  P_MATERIAL_KNOWLEDGE
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/QueryMaxQP b/doc/lfun/QueryMaxQP
new file mode 100644
index 0000000..9fa0f5a
--- /dev/null
+++ b/doc/lfun/QueryMaxQP
@@ -0,0 +1,27 @@
+QueryMaxQP()
+
+FUNKTION:
+    int QueryMaxQP()
+
+DEFINIERT IN:
+    /secure/questmaster.c
+
+ARGUMENTE:
+    keine
+
+RUECKGABEWERT:
+    Abenteuerpunkte der Pflichtquests
+
+BESCHREIBUNG:
+    Diese Funktion liefert die Abenteuerpunkte der als Pflichtquest
+    eingetragenen Abenteuer zurueck. Pflichtquest bedeutet entweder,
+    dass die Quest zwingend geloest werden muss oder dass sie zu
+    der 80%-Regel gehoert.
+
+SIEHE AUCH:
+    GiveQuest, QueryQuest, /secure/questmaster.h, QueryGroupedKeys,
+    QueryOptQP, QueryTotalQP
+
+----------------------------------------------------------------------------
+Zuletzt geaendert: Sam, 25. Nov 2000, 14:04:28 von Zook.
+
diff --git a/doc/lfun/QueryMoney b/doc/lfun/QueryMoney
new file mode 100644
index 0000000..899b50e
--- /dev/null
+++ b/doc/lfun/QueryMoney
@@ -0,0 +1,28 @@
+QueryMoney()
+FUNKTION:
+     int QueryMoney()
+
+DEFINIERT IN:
+     /std/player/moneyhandler.c
+
+BESCHREIBUNG:
+     Testet, ob ein Spieler, Objekt, Raum oder Npc ueber eine definierte 
+     Geldmenge verfuegt, oder nicht.
+
+RUECKGABEWERT:
+     Geldmenge im Besitz des abgefragten Spielers
+
+BEISPIELE:
+     int i;
+     i=50+random(10);
+     if(!this_player()->QueryMoney())
+       write("Du besitzt keine Muenzen!\n");
+     if(this_player()->QueryMoney() < i)
+       write("Du besitzt nicht die erforderlichen "+i+" Muenzen.\n");
+
+SIEHE AUCH:
+     Geldhandling:	AddMoney(L)
+     Zentralbank:	PayIn(L), WithDraw(L), _query_current_money(L)
+     Sonstiges:		/items/money.c
+
+Last modified: Die,  1. Aug 2000, 16:39:06 by Tilly
diff --git a/doc/lfun/QueryName b/doc/lfun/QueryName
new file mode 100644
index 0000000..d26cd1a
--- /dev/null
+++ b/doc/lfun/QueryName
@@ -0,0 +1,24 @@
+QueryName()
+
+FUNKTION:
+     mixed QueryName();
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     DIESE FUNKTION SOLLTE NICHT MEHR BENUTZT WERDEN!
+     Es wird der Inhalt der Property P_NAME zuueckgegeben; dies laesst sich
+     jedoch genau so gut mit QueryProp(P_NAME) bewerkstelligen!
+
+RUeCKGABEWERT:
+     String oder Array von Strings mit dem Namen des Objektes.
+
+SIEHE AUCH:
+     QueryProp(), Name(), name(), /std/thing/description.c
+
+----------------------------------------------------------------------------
+Last modified: Sat Aug  3 11:21:05 2002 by Vanion
diff --git a/doc/lfun/QueryOpenMiniQuestsForPlayer b/doc/lfun/QueryOpenMiniQuestsForPlayer
new file mode 100644
index 0000000..40246ae
--- /dev/null
+++ b/doc/lfun/QueryOpenMiniQuestsForPlayer
@@ -0,0 +1,54 @@
+FUNKTION:
+    mapping QueryOpenMiniQuestsForPlayer(object player)
+
+DEFINIERT IN:
+    /secure/questmaster
+
+BESCHREIBUNG:
+    Diese Funktion gibt die Liste der offenen Miniquests des Spielers als
+    Mapping zurueck.
+
+ARGUMENTE:
+    player - das interessierende Spielerobjekt
+
+RUECKGABEWERTE:
+    Mapping mit der Liste der Miniquests, fuer die das abfragende Objekt
+    zustaendig ist, oder leeres Mapping, wenn der Spieler keine MQs mehr
+    offen hat.
+
+    Die Liste enthaelt die Miniquestnummer als Key. Diesem sind zwei Werte
+    zugeordnet: zum einen ein Miniquest-Aufgabentext, und zum anderen -
+    falls der Spieler eine der Vorbedingungen fuer die Miniquest nicht
+    erfuellt - ein Hinweistext, der Auskunft gibt, welche Bedingung noch
+    zu erfuellen ist ("Seherstatus fehlt"). Diese Hinweistexte entsprechen
+    denen aus check_restrictions() in /std/restriction_checker.c. Der 
+    jeweils andere Text wird auf 0 gesetzt.
+
+    Die Struktur des Mappings ist daher folgende:
+      ([ MQ-Nummer : <Aufgabenstellung> ; <Hinderungsgrund> ])
+    
+    Beispiel: ein Spieler hat die Miniquests 18 und 49 noch nicht geloest,
+    erfuellt aber nur fuer Miniquest 49 die Anforderungen. Miniquest 18
+    erfordert den Seherstatus. Dann saehe das Mapping so aus:
+      ([ 18 : 0                    ; "Dazu musst Du erst Seher werden.\n",
+         49 : "Aufgabentext_zu_49" ; 0 ])
+
+    Jedes abfragende Objekt muss daher dieses Mapping zunaecht geeignet
+    auf seinen Inhalt pruefen, um zu ermitteln, welche Meldung jeweils
+    auszugeben ist.
+
+BEMERKUNGEN:
+    Das abfragende Objekt muss von einem Erzmagier oder Gott (z.B. dem
+    zustaendigen Quest-EM) im Questmaster als zugriffsberechtigt bei den-
+    jenigen Miniquests eingetragen sein, fuer die es die entsprechenden
+    Miniquest-Hinweise ausgeben darf. Diese Berechtigung ist mit dem 
+    Quest-EM abzustimmen. Anderen Objekten wird ein leeres Mapping zurueck-
+    gegeben.
+
+SIEHE AUCH:
+    AddMiniQuest(L), ChangeMiniQuest(L)
+    P_RESTRICTIONS
+    erzmagier
+
+----------------------------------------------------------------------------
+Last modified: 6. Juni 2014, Arathorn.
diff --git a/doc/lfun/QueryOwn b/doc/lfun/QueryOwn
new file mode 100644
index 0000000..b1a55f6
--- /dev/null
+++ b/doc/lfun/QueryOwn
@@ -0,0 +1,32 @@
+QueryOwn()
+
+FUNKTION:
+     varargs string QueryOwn(int casus)
+
+DEFINIERT IN:
+     /std/thing/language.c
+
+ARGUMENTE:
+     casus
+          Der Fall fuer die Anrede.
+
+BESCHREIBUNG:
+     Diese Funktion liefert das dem Anlass entsprechende Possessiv-
+     pronomen fuer den Spieler selber (Dein/Deine).
+
+RUeCKGABEWERT:
+     Ein String mit dem Pronomen.
+
+BEISPIELE:
+
+     printf("%s %s loest sich auf.\n",
+           capitalize(obj->QueryOwn(WER)),obj->name(WER));
+
+     (In den meisten Faellen kann man hier auch direkt "Dein" oder "Deine"
+      einsetzen.)
+
+SIEHE AUCH:
+     /std/thing/language.c
+
+----------------------------------------------------------------------------
+Last modified: Sun Aug 10 23:55:27 2003 by Mandragon
diff --git a/doc/lfun/QueryPassengers b/doc/lfun/QueryPassengers
new file mode 100644
index 0000000..a71af67
--- /dev/null
+++ b/doc/lfun/QueryPassengers
@@ -0,0 +1,29 @@
+QueryPassengers()
+
+FUNKTION:
+     object *QueryPassengers();
+
+DEFINIERT IN:
+     /std/transport.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Diese Funktion ermittelt die momentan auf dem Transporter befindlichen
+     Passagiere.
+
+RUeCKGABEWERT:
+     Array von Objekten mit den Passagieren.
+
+BEMERKUNGEN:
+     Es werden nur die Passagiere zurueckgegeben, die sich in dem
+     eigentlichen Transporterobjekt befinden! Wenn der Transporter noch um
+     zusaetzliche Raeume vergroessert wird, werden unter Umstaenden nicht
+     alle Passagiere erfasst!
+
+SIEHE AUCH:
+     /std/transport.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:22:48 1996 by Wargon
diff --git a/doc/lfun/QueryPosition b/doc/lfun/QueryPosition
new file mode 100644
index 0000000..4d5179d
--- /dev/null
+++ b/doc/lfun/QueryPosition
@@ -0,0 +1,29 @@
+QueryPosition()
+
+FUNKTION:
+     string *QueryPosition();
+
+DEFINIERT IN:
+     /std/transport.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Es wird ein Array von zwei Strings mit Kennzeichnern der letzten (oder
+     aktuellen) und der naechsten Station im Fahrplan zurueckgegeben.
+
+     Die Kennzeichner sind dabei die ersten Argumente der entsprechenden
+     AddXXX()-Aufrufen, also der Name der Haltestelle bei AddRoute, der Text
+     der Meldung bei AddMsg() und der Name der Funktion bei AddFun().
+
+RUeCKGABEWERT:
+     Array mit zwei Strings. Der erste String gibt den Kennzeichner der
+     momentanen Fahrplanstation an, der zweite String den Kennzeichner der
+     naechsten Fahrplanstation.
+
+SIEHE AUCH:
+     AddRoute(), AddMsg(), AddFun(), /std/transport.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:22:52 1996 by Wargon
diff --git a/doc/lfun/QueryPossPronoun b/doc/lfun/QueryPossPronoun
new file mode 100644
index 0000000..c3f4106
--- /dev/null
+++ b/doc/lfun/QueryPossPronoun
@@ -0,0 +1,48 @@
+QueryPossPronoun()
+
+FUNKTION:
+     varargs string QueryPossPronoun(mixed what, int casus, int anzahl);
+
+DEFINIERT IN:
+     /std/thing/language.c
+
+ARGUMENTE:
+     what
+          Geschlecht des Objektes, das besessen wird, oder das Objekt
+          selbst.
+
+     casus
+          Der Fall, in dem das Objekt besessen wird.
+
+     anzahl
+          Handelt es sich um nur ein Objekt oder um mehrere?
+
+BESCHREIBUNG:
+     Diese Funktion gibt ein Possessivpronomen zurueck, das das
+     Besitzverhaeltnis eines Objektes zu diesem Objekt anzeigt.
+
+RUeCKGABEWERT:
+     Das Possessivpronomen im entsprechenden Fall.
+
+BEMERKUNGEN:
+     what und casus beziehen sich auf das Objekt, welches besessen wird, und
+     nicht auf den Besitzer!!!
+
+BEISPIELE:
+     Um eine korrekte Ausgabe beim Haendeklatschen zu erreichen, koennte man
+     zB. folgende Zeile verwenden:
+
+      printf("%s klatscht in %s Haende.\n",this_player()->name(WER),
+           this_player()->QueryPossPronoun(FEMALE, WEN, PLURAL));
+
+     FEMALE, da "die Hand" weiblich ist, WEN, da man im Akkusativ klatscht,
+     und PLURAL, da man dazu beide Haende braucht.
+
+     Ein beliebter Fehler ist es, als Fall WESSEN zu verwenden (in WESSEN
+     Haende klatscht man? => in seine).
+     Die richtige Frage waere aber: WEN klatscht man? (in die Haende)
+
+SIEHE AUCH:
+     QueryOwn(), QueryPronoun(), /std/thing/language.c
+----------------------------------------------------------------------------
+Last modified: Wed Jan 11 13:13:35 CET 2006 by Rumata
diff --git a/doc/lfun/QueryPrayRoom b/doc/lfun/QueryPrayRoom
new file mode 100644
index 0000000..0a097d3
--- /dev/null
+++ b/doc/lfun/QueryPrayRoom
@@ -0,0 +1,33 @@
+QueryPrayRoom()
+
+FUNKTION:
+     public string QueryPrayRoom()
+
+DEFINIERT IN:
+     /std/player/base.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+    Dies ist der Raum, in den der Spieler nach dem Ende der Todessequenz
+    bewegt wird, d.h. ein Raum, wo er beten kann, um einen neuen Koerper zu
+    erhalten.
+    Der Raum wird als String angegeben (kein Objekt).
+
+    Jede Rasse hat einen Default fuer diese Funktion, welcher mit
+    SetDefaultPrayRoom() gesetzt wird. Dieser Default kann mit der Property
+    P_PRAY_ROOM ueberlagert werden. Wird die Property auf 0 dgesetzt, wird
+    dieser Default aktiv.
+
+RUeCKGABEWERT:
+    Der Objektname des Betraums (string)
+
+
+SIEHE AUCH:
+     P_PRAY_ROOM
+     SetDefaultPrayRoom
+
+----------------------------------------------------------------------------
+21.05.2013, Zesstra
+
diff --git a/doc/lfun/QueryPreferedEnemy b/doc/lfun/QueryPreferedEnemy
new file mode 100644
index 0000000..267d27d
--- /dev/null
+++ b/doc/lfun/QueryPreferedEnemy
@@ -0,0 +1,35 @@
+FUNKTION:
+	object QueryPreferedEnemy();
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	keine
+
+RUeCKGABEWERT:
+	bevorzugter Gegner
+
+BESCHREIBUNG:
+	Diese Funktion liefert unter folgenden Bedingungen zufaellig eines
+	der Lebewesen als bevorzugten Gegner zurueck, welche in der
+	Property P_PREFERED_ENEMY in einem Array eingetragen sind:
+	  (1) Der erste Eintrag des erwaehnten Propertyarrays enthaelt
+	      einen Wert zwischen 0 und 100, der die Wahrscheinlichkeit
+	      dafuer angibt, dass ein Lebewesen als Gegner bevorzugt werden
+	      soll. Es wird also nicht immer bevorzugt, wenn dort ein Wert
+	      kleiner 100 steht! In diesem Fall wird eine 0 zurueckgegeben.
+	  (2) Das per Zufall aus den Arrayelementen ab Element 2 gewaehlte
+	      Lebewesen muss auch wirklich existieren. Ist dies nicht der
+	      Fall, wird das nunmehr leere Element aus dem Array entfernt
+	      und eine 0 zurueckgeliefert.
+	  (3) Das Lebewesen muss derzeit auch wirklich Feind sein! Ist dies
+	      nicht der Fall, wird eine 0 zurueckgegeben.
+	Will man eine andere Bevorzugung von Gegnern erreichen,
+	ueberschreibt man am besten diese Funktion.
+
+SIEHE AUCH:
+	SelectEnemy(), IsEnemy(), P_PREFERED_ENEMY
+
+----------------------------------------------------------------------------
+Last modified: Wed May 26 16:47:51 1999 by Patryn
diff --git a/doc/lfun/QueryPronoun b/doc/lfun/QueryPronoun
new file mode 100644
index 0000000..fd041cb
--- /dev/null
+++ b/doc/lfun/QueryPronoun
@@ -0,0 +1,32 @@
+QueryPronoun()
+
+FUNKTION:
+     string QueryPronoun(int casus);
+
+DEFINIERT IN:
+     /std/thing/language.c
+
+ARGUMENTE:
+     casus
+          Der Fall, in dem das Personalpronomen verlangt wird.
+
+BESCHREIBUNG:
+     Es wird ein Personalpronomen ("er", "sie", "es") im entsprechenden Fall
+     fuer dieses Objekt berechnet.
+
+RUeCKGABEWERT:
+     Das Pronomen im entsprechenden Fall.
+
+BEISPIELE:
+
+     printf("Du versucht, %s zu nehmen, aber %s ist zu schwer.\n",
+           ob->name(WEN), ob->QueryPronoun(WER));
+
+     Hier wird immer das richtige Pronomen verwendet, egal, welches
+     Geschlecht das Objekt ob hat.
+
+SIEHE AUCH:
+     SuggestArticle(), QueryPossPronoun(), /std/thing/language.c
+     QueryOwn()
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:23:14 1996 by Wargon
diff --git a/doc/lfun/QueryProp b/doc/lfun/QueryProp
new file mode 100644
index 0000000..ee752e0
--- /dev/null
+++ b/doc/lfun/QueryProp
@@ -0,0 +1,39 @@
+QueryProp()
+FUNKTION:
+     mixed QueryProp(string name);
+
+DEFINIERT IN:
+     /std/thing/properties.c
+
+ARGUMENTE:
+     string name	- abzufragende Property
+
+BESCHREIBUNG:
+     Der Datenwert der Property 'name' wird zurueckgegeben.
+
+     Existiert eine F_QUERY_METHOD oder eine _query_'name'()-Methode fuer
+     diese Property, so wird diese aufgerufen und ihr 'Value' uebergeben.
+     Eine F_QUERY_METHOD hat dabei Vorrang vor _query_'name'(), d.h.
+     _query_'name'() wird nach erfolgreicher F_QUERY_METHOD nicht mehr
+     gerufen.
+
+     (Diese Methoden nutzen dann Set(), um auf den Datenwert der Property
+      'name' zurueckzugreifen. Teilweise werden aber auch interne Variablen
+      so oeffentlich gemacht und sind nicht in der ueber Set/Query
+      verfuegbaren Property 'name' abgelegt.)
+
+RUeCKGABEWERT:
+     Der Datenwert der Property.
+     0, falls diese nicht existiert.
+
+BEISPIELE:
+     // wie hoch sind die aktuelle LP des Spielers?
+     hp = this_player()->QueryProp(P_HP);
+
+SIEHE AUCH:
+     Aehnliches:	SetProp(L), Set(L), Query(L)
+     Generell:		SetProperties(L), QueryProperties(L)
+     Konzept:		properties, /std/thing/properties.c
+     Sonstiges:		P_AUTOLOADOBJ
+
+15.Dez 2004 Gloinson
diff --git a/doc/lfun/QueryProperties b/doc/lfun/QueryProperties
new file mode 100644
index 0000000..aa740b5
--- /dev/null
+++ b/doc/lfun/QueryProperties
@@ -0,0 +1,30 @@
+QueryProperties()
+FUNKTION:
+     mapping QueryProperties()
+
+DEFINIERT IN:
+     /std/thing/properties.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Diese Funktion liefert ein Mapping mit allen fuer das Objekt
+     definierten Properties zurueck.
+
+RUeCKGABEWERT:
+     Ein Mapping mit den Properties. Das Mapping hat folgenden Aufbau:
+	([ name: wert; flags; set_method; query_method,
+	   name2: ... ]);
+
+BEMERKUNGEN:
+     - diese Funktion wird von restore_object() und save_object()
+     - F_QUERY_METHODs und _query_'prop'()-Methoden haben keine Auswirkung
+       auf die Wertefelder!
+
+SIEHE AUCH:
+     Aehnliches:	SetProp(L), QueryProp(L), Set(L), Query(L)
+     Generell:		SetProperties(L) 
+     Konzept:		properties, /std/thing/properties.c
+
+1.Mai 2004 Gloinson
diff --git a/doc/lfun/QueryQuest b/doc/lfun/QueryQuest
new file mode 100644
index 0000000..295ad16
--- /dev/null
+++ b/doc/lfun/QueryQuest
@@ -0,0 +1,34 @@
+QueryQuest()
+
+FUNKTION:
+        int QueryQuest(string questname)
+
+DEFINIERT IN:
+        /std/player/quests.c
+
+ARGUMENTE:
+        questname
+          Questname, wie er im Questmaster eingetragen wurde.
+
+RUeCKGABEWERT:
+        (Die Defines fuer den Rueckgabewert finden sich in 
+         /secure/questmaster.h)
+         1 : Spieler hat die Quest bereits geloest  (OK)
+         0 : Ungueltiger Questname oder der Spieler
+             hat die Quest noch nicht.              (QQ_KEY_INVALID)
+         2 : Gaeste koennen keine Quest loesen      (QQ_QUEST)
+
+BESCHREIBUNG:
+	Mit dieser Funktion kann getestet werden, ob ein Spieler eine 
+        bestimmte Quest bereits geloest hat. Als Questname wird dazu
+        der Name angegeben, der im Questmaster eingetragen wurde.
+
+        Wer sich da nicht sicher ist, kann mit dem Questtool 
+        (/obj/tools/questtool) nachsehen. 
+
+SIEHE AUCH:
+        /secure/questmaster.h, /obj/tools/questtool
+        GiveQuest
+
+----------------------------------------------------------------------------
+Zuletzt geaendert: Mon, 17. Jul 2000, 12:16:41 von Zook.
diff --git a/doc/lfun/QueryQuestTime b/doc/lfun/QueryQuestTime
new file mode 100644
index 0000000..2410d6a
--- /dev/null
+++ b/doc/lfun/QueryQuestTime
@@ -0,0 +1,29 @@
+QueryQuestTime()
+
+FUNKTION:
+        int QueryQuestTime(string questname)
+
+DEFINIERT IN:
+        /std/player/quests.c
+
+ARGUMENTE:
+        questname
+          Questname, wie er im Questmaster eingetragen wurde.
+
+RUeCKGABEWERT:
+        Questzeitpunkt in Sekunden seit Epoch als positiver Integer-Wert
+         0 : Tagebuchmaster hat keine Daten in den Logfiles gefunden und
+             macht das auf diese Weise kenntlich
+        -1 : Questzeitpunkt unbekannt
+
+BESCHREIBUNG:
+        Mit dieser Funktion kann der Zeitpunkt abgefragt werden, zu
+        dem ein Spieler eine bestimmte Quest geloest hat. 
+        Als Questname wird dazu der Name angegeben, der im Questmaster 
+        eingetragen ist.
+
+SIEHE AUCH:
+        GiveQuest(L), QueryQuest(L), ModifyQuestTime(L)
+
+----------------------------------------------------------------------------
+Zuletzt geaendert: 19. Dez. 2015, Arathorn
diff --git a/doc/lfun/QueryRealAttribute b/doc/lfun/QueryRealAttribute
new file mode 100644
index 0000000..7ee9f83
--- /dev/null
+++ b/doc/lfun/QueryRealAttribute
@@ -0,0 +1,26 @@
+QueryRealAttribute()
+FUNKTION:
+     int QueryRealAttribute(string attr)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+
+ARGUMENTE:
+     attr       -       das interessierende Attribut
+
+BESCHREIBUNG:
+     Das reale Attribut (ohne Offsets) wird zurueckgegeben.
+
+BEISPIELE:
+     if(this_player()->QueryRealAttribute(A_INT)>15)
+      write("Du bist auch so ganz schoen clever.\n");
+
+SIEHE AUCH:

+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+	SetTimedAttrModifier(), QueryTimedAttrModifier(), 

+	DeleteTimedAttrModifier(),

+	P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_TIMED_ATTR_MOD,

+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+----------------------------------------------------------------------------

+Last modified: Tue Jul 27 20:00:20 2004 by Muadib

diff --git a/doc/lfun/QuerySellValue b/doc/lfun/QuerySellValue
new file mode 100644
index 0000000..544cb29
--- /dev/null
+++ b/doc/lfun/QuerySellValue
@@ -0,0 +1,43 @@
+FUNKTION:
+
+      varargs int QuerySellValue(object ob, object client);
+
+DEFINIERT IN:
+
+      /std/trading_price.c
+
+BESCHREIBUNG:
+
+      Diese Funktion kann dazu genutzt werden, den Verkaufspreis in einem 
+      Laden global zu veraendern. Sofern dies zugunsten der Spieler geschieht,
+      muss dafuer die Zustimmung des zustaendigen Regionsmagiers und der
+      Balance eingeholt werden. Zudem sollte es nicht in jedem x-beliebigen 
+      Laden genutzt werden, sondern nur in recht abgelegenen Gebieten, in
+      die man nicht einfach skripten kann.
+
+      Ein Beispiel ist der Laden auf dem Kutter in Port Vain, der nur in
+      laengeren Intervallen mal zugaenglich ist.
+
+BEISPIEL:
+
+      Ein Laden zahlt 10% ueber dem normalen Verkaufspreis:
+
+      private int sell_factor = 110;
+
+      varargs int QuerySellValue(object ob, object client) {
+        // Es wird nicht naeher geprueft, ob <ob> ein gueltiger Objektpointer
+        // ist, da der Laden selbst sicherstellt, dass das so ist. Wenn 
+        // das doch einmal nicht der Fall sein sollte, liegt der Fehler
+        // woanders. Einen Bug auszuloesen ist dann sinnvoll.
+
+        // Basispreis ermitteln 
+        int preis = ::QuerySellValue(ob, client);
+        // und den Bonus aufschlagen.
+        preis = (sell_factor * preis)/100; 
+
+        // Nicht mehr auszahlen, als das Objekt an sich wert ist.
+        return min(preis, ob->QueryProp(P_VALUE));
+      }
+
+----------------------------------------------------------------------------
+LETZTE AENDERUNG: 18-Jun-2015, Arathorn
diff --git a/doc/lfun/QuerySkill b/doc/lfun/QuerySkill
new file mode 100644
index 0000000..017b9f9
--- /dev/null
+++ b/doc/lfun/QuerySkill
@@ -0,0 +1,35 @@
+QuerySkill()
+FUNKTION:
+    public varargs mapping QuerySkill(string sname, string gilde)
+
+DEFINIERT IN:
+    /std/living/skills.c
+    
+ARGUMENTE:
+    string sname    Name des abzufragenden Skill
+    string gilde    Name der Gilde, unter der der Skill gesucht werden soll
+
+BESCHREIBUNG:
+    Diese Funktion liefert das Skillmappings des Skills 'snme' im Lebewesen
+    zurueck. Diese enthaelt Eintraege, die in der Man-Page skill_info_liste
+    beschrieben sind.
+
+    Falls 'gilde' nicht angegeben wird, wird der Skill zuerst fuer die Gilde
+    P_GUILD des Lebewesens gesucht, ansonsten in den allgemeinen Skills
+    "ANY" (es wird NICHT in Gildenobjekt oder Spellbook etwas abgefragt).
+
+RUECKGABEWERT:
+    Ein Mapping mit Skillinfos oder 0, falls der Skill nicht vorhanden ist.
+    Das Mapping ist eine Kopie.
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung, skill_info_liste
+    * Properties:   P_NEWSKILLS
+
+5. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/QuerySkillAbility b/doc/lfun/QuerySkillAbility
new file mode 100644
index 0000000..5cfd815
--- /dev/null
+++ b/doc/lfun/QuerySkillAbility
@@ -0,0 +1,29 @@
+QuerySkillAbility()
+FUNKTION:
+    public varargs int QuerySkillAbility(string sname, string gilde)
+
+DEFINIERT IN:
+    /std/living/skills.c
+    
+ARGUMENTE:
+    string sname        Name des abzufragenden Skill
+    string gilde         Gilde, unter der der Skill gesucht werden soll
+
+BESCHREIBUNG:
+    Diese Funktion liefert einen Wert zurueck, der aussagt, wie gut das
+    Lebewesen den Skill 'sname' beherrscht (Key SI_SKILLABILITY im
+    Skillmapping).
+    Dieser Wert kann zwischen -MAX_ABILITY und MAX_ABILITY liegen,
+    normal ist 0 bis MAX_ABILITY (10000 - Skill perfektioniert).
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung, skill_info_liste
+    * Properties:   P_NEWSKILLS
+
+5. Okt 2011 Gloinson
diff --git a/doc/lfun/QuerySkillAttribute b/doc/lfun/QuerySkillAttribute
new file mode 100644
index 0000000..d0b1bd1
--- /dev/null
+++ b/doc/lfun/QuerySkillAttribute
@@ -0,0 +1,58 @@
+QuerySkillAttribute()
+FUNKTION:
+    public int QuerySkillAttribute(string atrname)
+
+DEFINIERT IN:
+    /std/living/skill_attributes.c
+    
+ARGUMENTE:
+    string atrname            Name des abzufragenden Attributs
+    
+BESCHREIBUNG:
+    Mit dieser Funktion kann man den Wert bestimmter Attribute
+    abfragen, dabei werden das abgefragte Attribut, Todesfolgen,
+    SA_QUALITY und Werte in P_SKILL_ATTRIBUTE_OFFSETS
+    beruecksichtigt.
+    
+    Momentane Skills siehe ModifySkillAttribute.
+
+RUECKGABEWERT:
+    Der Wert des Attributs. Ist nichts bestimmtes gesetzt, wird
+    der Standardwert 100 zurueckgegeben.
+    Der Rueckgabewert liegt zwischen 10 bis 1000 (Prozent).
+    
+BEMERKUNG:
+    Die Funktion ist zwar als 'varargs' definiert, gibt man allerdings
+    keinen Attributnamen an, wird immer 100 zurueckgegeben.
+    
+BEISPIEL:
+    // ein Spieler kann ein Stueck Kaese stibitzen, wenn er schnell
+    // genug ist ... (15% ueber normal)
+    if(this_player()->QuerySkillAttribute(SA_SPEED)>=115) {
+      tell_object(this_player(),
+        "Du schnappst das Stueck Kaese aus der Falle.\n");
+      obj kaese = clone_object(...);
+      [...]
+    } else {
+      mapping amap=map_indices(VALID_ARMOUR_CLASS,#'!);
+      amap[AT_GLOVE]=100;
+      tell_object(this_player(),
+        "Du bist zu langsam und die Falle schnappt hungrig zu.\n");
+      this_player()->Defend(random(100),
+                           ({DT_PIERCE, DT_SQUEEZE}),
+                           ([SP_PHYSICAL_ATTACK: 1,
+                             SP_REDUCE_ARMOUR: amap,
+                             SP_SHOW_DAMAGE: 0]));
+    }
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung, skill_info_liste
+    * Properties:   P_NEWSKILLS
+
+5. Okt 2011 Gloinson
diff --git a/doc/lfun/QuerySkillAttributeModifier b/doc/lfun/QuerySkillAttributeModifier
new file mode 100644
index 0000000..507c51b
--- /dev/null
+++ b/doc/lfun/QuerySkillAttributeModifier
@@ -0,0 +1,90 @@
+QuerySkillAttributeModifier()
+FUNKTION:
+    public varargs mapping QuerySkillAttributeModifier(object caster, 
+                                                    string *atrnames)
+
+DEFINIERT IN:
+    /std/living/skill_attributes.c
+
+ARGUMENTE:
+    <caster>    object
+                Objekt, welches die gesuchten Mods gesetzt hat
+
+    <atrnames>  string*
+                Array von Skill-Attributen, welche durchsucht werden sollen.
+
+BESCHREIBUNG:
+    Diese Funktion liefert alle Mods von <caster> auf den Skill-
+    Attributen <atrnames> in einem Mapping zurueck.
+    Wird <atrnames> nicht angegeben oder ist ein leeres Array, werden alle
+    Skill-Attribute nach Mods abgesucht.
+    Wird <caster> nicht angeben oder ist 0, so werden alle Mods der 
+    gewuenschten Skill-Attribute geliefert.
+    Dementsprechend bekommt man alle Mods aller Skill-Attribute, wenn keins
+    von beidem angegeben wird.
+
+RUECKGABEWERT:
+    ([]), falls keine Modifikatoren gefunden wurden.
+    In anderen Faellen ist die Datenstruktur des Mappings wie folgt:
+    ([ atrname1: ([ <caster>: <value>; <duration> ]),
+       atrname2: ([ <caster>: <value>; <duration> ]) ])
+    Die Schluessel im Mapping sind die jeweiligen Skill-Attribute, die Werte
+    des Mappings sind erneut Mappings, welche als Schluessel die Objekte
+    haben, welche die Mods gesetzt haben. In diesem Mapping gehoeren zu jedem
+    Schluessel zwei Werte, den Wert des Modifikators und die Ablaufzeit.
+    <value> kann hierbei ein int oder eine closure sein, <duration> ist ein
+    int, <caster> ist ein Objekt.
+    Ggf. kann das innere Mapping natuerlich auch mehrere Modifikatoren
+    enthalten (also caster1, caster2, usw.).
+
+BEMERKUNGEN:
+
+BEISPIELE:
+     Ein Objekt moechte seinen bestehenden Modifikator um 20 und die
+     Gueltigkeit um 13 erhoehen.
+     mapping res = ob->QuerySkillAttributeModifier(this_object(),
+                                                  ({SA_DAMAGE}) );
+     if (member(res, SA_DAMAGE) && member(res[SA_DAMAGE], this_object())) {
+          // alten Mod ueberschreiben und Werte dabei anheben.
+          ob->ModifySkillAttributeModifier(SA_DAMAGE,
+              res[SA_DAMAGE][this_object(),0] + 20,
+              res[SA_DAMAGE][this_object(),1] + 13 );
+     }
+     else
+          // neuen Mod setzen.
+          ob->ModifySkilAttributeModifier(SA_DAMAGE, 20, 13);
+      
+     Ein Objekt hat den Fluch der unpraezisen Schnelligkeit, welcher SA_DAMAGE
+     reduziert, sofern das Lebewesen einen positiven Modifikator auf SA_SPEED
+     hat:
+     mapping res = ob->QuerySkillAttributeModifier(0, ({SA_SPEED}) );
+     if (member(res, SA_SPEED) {
+         int val, int dur;
+         foreach(object caster, int v, int d: res[SA_SPEED]) {
+             // groessten Mod rausfinden, dabei keine closures
+             // beruecksichtigen.
+             if (intp(v) && v>val) {
+                 val=v;
+                 dur=d;
+             }
+         }
+         if (val > 0) {
+             // pos. Mod auf SA_SPEED gefunden, entsprechenden neg. Mod auf
+             // SA_DAMAGE setzen, der zum gleichen Zeitpunkt ungueltig wird
+             // wie der Mod auf SA_SPEED.
+             if (ob->ModifySkillAttribute(SA_DAMAGE, -val, dur) == SA_MOD_OK)
+                tell_object(ob, "tolle Fluchmeldung.");
+         }
+     }
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung, skill_info_liste
+    * Properties:   P_NEWSKILLS
+
+14.08.2008, Zesstra
diff --git a/doc/lfun/QuerySkillBonus b/doc/lfun/QuerySkillBonus
new file mode 100644
index 0000000..1a4ef47
--- /dev/null
+++ b/doc/lfun/QuerySkillBonus
@@ -0,0 +1,65 @@
+QuerySkillBonus()
+
+FUNKTION:
+     int QuerySkillBonus(object caster, object target, mapping sinfo)
+
+DEFINIERT IN:
+     beliebigen Objekten
+
+ARGUMENTE:
+     object caster
+          der Benutzer eines Skills/Spells (Lebewesen)
+     object target
+          das Ziel eines Skills/Spells (beliebiges Objekt oder 0)
+     mapping sinfo
+          das Skillinfomapping
+
+BESCHREIBUNG:
+     Diese Funktion wird von der Gilde des Casters im Environment und ggf.
+     auch im Ziel eines Skills/Spells gerufen.
+     Die Gilde uebergibt neben Caster und Ziel ein Mapping mit Skillinfos (s.
+     SI Konstanten aus new_skills.h fuer Details), welches alle wesentlichen
+     Informationen ueber den benutzten Skill/Spell enthaelt.
+
+     QuerySkillBonus() liefert einen Bonus (oder Malus) zurueck, den der
+     Aufrufer als Faktor in der Berechnung des Effekts des Skills
+     beruecksichtigen kann (aber nicht muss).
+     Der Bonus/Malus wird hierbei als ganzzahliger 0.01-Prozentwert aufgefasst
+     (10000 == 100% == keine Veraenderung, 1 == 0.01%).
+
+     Diese Funktion kann in beliebigen Objekten (re-)definiert werden. Im
+     Falle mobiler Objekte oder anhaltender Effekte ist jedoch eine
+     Balancegenehmigung erforderlich, sofern kampfrelevante Skills beeinflusst
+     werden.
+     Eine flaechendeckende Reduzierung von Skills/Gildenfaehigkeiten ist
+     explizit _nicht_ erwuenscht und soll auf einzelne Raeume und Objekte
+     beschraenkt sein.
+
+BEMERKUNGEN:
+     Das Mapping <sinfo> kann in dieser Funktion geaendert werden. Dieses kann
+     allerdings sehr weitreichende Folgen haben, speziell bei mangelnden
+     Kenntnissen ueber Interna des Skillsystems. Daher bitte von Aenderungen
+     absehen bzw. vorher mit dem jeweiligen Gildenmagier und/oder der
+     Gildenbalance abklaeren.
+     Die Bedeutung der Werte in <sinfo> kann je nach Gilde variieren. Im
+     Zweifelsfall bitte bei den jeweiligen Gildenmagiern nachfragen. 
+     Die Gilde kann diese Funktion rufen, muss aber nicht. Ebenso kann sie das
+     Ergebnis beruecksichtigen, muss aber nicht.
+
+BEISPIELE:
+     In einem Raum sollen Heilzauber besonders effizient sein:
+     int QuerySkillBonus(object caster, object target, mapping sinfo) {
+        if (pointerp(sinfo[SI_MAGIC_TYPE])
+            && member(sinfo[SI_MAGIC_TYPE], MT_HEILUNG) > -1)
+        {
+            return 12000 + random(3000); // bonus von 120-150%
+        }
+        return 10000;
+     }
+
+SIEHE AUCH:
+     gilden-doku
+     <new_skills.h>
+
+LETZTE AeNDERUNG:
+19.08.2013, Zesstra
diff --git a/doc/lfun/QueryStorageRoom b/doc/lfun/QueryStorageRoom
new file mode 100644
index 0000000..b62e40d
--- /dev/null
+++ b/doc/lfun/QueryStorageRoom
@@ -0,0 +1,21 @@
+QueryStorageRoom()

+

+Funktion:

+    string QueryStorageRoom()

+

+Definiert in:

+    /std/room/shop

+

+Rueckgabewert:

+    Dateiname des Lagers, in dem die aufgekauften Gegenstaende aufbewahrt 

+    werden.

+

+Siehe auch:

+    Funktionen:

+      AddFixedObject(), RemoveFixedObject(), SetStorageRoom(), 

+      QueryBuyValue(), QueryBuyFact(), sell_obj(), buy_obj()

+    Properties:

+      P_KEEPER, P_MIN_STOCK, P_STORE_CONSUME

+

+------------------------------------------------------------------------------

+Letzte Aenderung: 21.05.2014, Bugfix

diff --git a/doc/lfun/QueryTimedAttrModifier b/doc/lfun/QueryTimedAttrModifier
new file mode 100644
index 0000000..1908608
--- /dev/null
+++ b/doc/lfun/QueryTimedAttrModifier
@@ -0,0 +1,42 @@
+QueryTimedAttrModifier()
+FUNKTION:
+     mapping QueryTimedAttrModifier(string key)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+
+ARGUMENTE:
+     key	-	aus P_TIMED_ATTR_MOD abzufragender Eintrag
+
+BESCHREIBUNG:
+     Der zu key gehoerende Eintrag in P_TIMED_ATTR_MOD wird abgefragt.

+

+RUeCKGABEWERT:

+     Ein leeres Mapping im Falle eines fehlerhaften key-Argumentes oder 
+     eines nicht existenten Keys.

+     

+     Ansonsten wird ein Mapping der Form

+     

+        ([

+           Key : ([ Mapping mit den Modifikatoren ]) ;

+                 Ablaufzeit ; Ablaufobjekt ; Nachrichtenempfaenger 

+        ])    

+

+     zurueckgegeben.Die Ablaufzeit ist hierbei die Zeit in Sekunden seit

+     dem 1. Jan 1970, 0.0:0 GMT, das Ablaufobjekt ist das Objekt an dessen

+     Existenz die Attributveraenderungen gebunden ist und der

+     Nachrichtenempfaenger ist dasjenigen Objekte welches im Falle 

+     durch den Aufruf von "NotifyTimedAttrModExpired" benachrichtigt 

+     wird sobald das Attribut abgelaufen ist. 

+     Der Funktion NotifyTimedAttrModExpired wird als Argument der key

+     der abgelaufenen Attributveraenderung uebergeben.

+     Das Mapping ist eine Kopie der Originaldatenstruktur zu diesem Key.

+    
+SIEHE AUCH:

+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+	SetTimedAttrModifier(), DeleteTimedAttrModifier(),

+	P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_TIMED_ATTR_MOD,

+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+----------------------------------------------------------------------------

+Last modified: Tue Jul 27 20:00:20 2004 by Muadib

diff --git a/doc/lfun/QueryTotalQP b/doc/lfun/QueryTotalQP
new file mode 100644
index 0000000..cf15cc0
--- /dev/null
+++ b/doc/lfun/QueryTotalQP
@@ -0,0 +1,25 @@
+QueryTotalQP()
+
+FUNKTION:
+    int QueryTotalQP()
+
+DEFINIERT IN:
+    /secure/questmaster.c
+
+ARGUMENTE:
+    keine
+
+RUECKGABEWERT:
+    Abenteuerpunkte saemtlicher Quests
+
+BESCHREIBUNG:
+    Diese Funktion liefert die Abenteuerpunkte der saemtlicher aktivierter
+    Quests zurueck.
+
+SIEHE AUCH:
+    GiveQuest, QueryQuest, /secure/questmaster.h, QueryGroupedKeys,
+    QueryMaxQP, QueryOptQP
+
+----------------------------------------------------------------------------
+Zuletzt geaendert: Sam, 25. Nov 2000, 14:04:28 von Zook.
+
diff --git a/doc/lfun/QueryUser b/doc/lfun/QueryUser
new file mode 100644
index 0000000..129a0a5
--- /dev/null
+++ b/doc/lfun/QueryUser
@@ -0,0 +1,50 @@
+QueryUser()
+
+FUNKTION:
+     public object QueryUser()
+
+DEFINIERT IN:
+     /std/npc/combat.c
+     /std/clothing/wear.c
+     /std/weapon/combat.c
+     alle Objekte, in denen es darueber hinaus noetig ist
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Liefert den aktuellen Nutzer (Lebewesen) eines Items oder NPCs.
+     
+     Diese Funktion wird z.B. von get_killing_player() benutzt, um
+     herauszufinden, zu welchem Spieler denn das Objekt gehoert, was den
+     toedlichen Schaden verursacht hat.
+
+     Im Falle eines NPCs ist dies standardmaessig der Spieler, bei dem der
+     NPC als Helfer-NPC eingetragen ist (s. RegisterHelperNPC).
+     Im Falle einer Ruestung ist es das Lebewesen, welches sie gerade traegt.
+     Im Falle einer Waffe ist es das Lebewesen, welches sie gerade gezueckt
+     hat.
+     Alle anderen Objekte enthalten keinen Default fuer diese Funktion.
+
+RUeCKGABEWERT:
+     Das nutzende Lebewesen, falls es ermittelt werden konnte, sonst 0.
+
+BEMERKUNGEN:
+     Sollte in allen Objekten definiert werden, welche Lebewesen Schaden
+     zufuegen, ohne dass das verursachende Lebewesen dabei als Feind im
+     Defend() angeben wird.
+     Der gelieferte Nutzer muss explizit kein Spieler sein. Es waere z.B.
+     moeglich, dass von einem Spieler kontrollierter NPC einen Bumerang nutzt.
+
+BEISPIELE:
+     Ein von einem Spieler beschworenes Wesen wirft einen Bumerang nach einem
+     Feind.
+     Dann liefert QueryUser() im Bumerang den NPC als Nutzer und
+     QueryUser() im NPC wiederum den Spieler.
+     
+SIEHE AUCH:
+     RegisterHelperNPC(), get_killer_player()
+     P_WORN, P_WIELDED
+----------------------------------------------------------------------------
+12.11.2013, Zesstra
+
diff --git a/doc/lfun/QueryValidObject b/doc/lfun/QueryValidObject
new file mode 100644
index 0000000..4622d65
--- /dev/null
+++ b/doc/lfun/QueryValidObject
@@ -0,0 +1,47 @@
+QueryValidObject()
+
+FUNKTION:
+     public int QueryValidObject(string oname);
+
+DEFINIERT IN:
+     /std/virtual/v_compiler.c
+
+ARGUMENTE:
+     oname
+       Objektname, der geprueft werden soll (kompletter Pfad mit / am Anfang) 
+
+RUeCKGABEWERT:
+     <=0 - falls VC nicht zustaendig ist.
+     >0 - falls der VC sich fuer das Objekt zustaendig erklaert.
+
+BESCHREIBUNG:
+     Ueber die Funktion laesst sich herausfinden, ob ein VC sich fuer das
+     gewuenschte Objekt zustaendig fuehlt. Dabei wird Validate(),
+     P_COMPILER_PATH, NoParaObjects() und P_PARA im VC ausgewertet:
+     1. Zuerst wird mit Validate() geprueft, ob der Filename (ohne Pfad) ok ist.
+     2. wird geguckt, ob das angefragte Objekt im richtigen Pfad liegt 
+        (P_COMPILER_PATH).
+     3. wenn das angefragte Objekt ein Para-Objekt ist:
+       a) wird NoParaObjects() geprueft, wenn das !=0 ist, sind gar keine Para-
+          Objekte erlaubt.
+       b) wird P_PARA _im VC_ abgefragt, dort kann man ein Array aller 
+          erlaubten Para-Dimensionen reinschreiben. Fuer alle anderen erklaert 
+          sich der VC fuer nicht zustaendig. Wenn P_PARA nicht gesetzt ist, 
+          sind alle erlaubt. Ein leeres Array ({}) wuerde einem 
+          NoParaObjects() {return 1;} entsprechen.
+
+BEMERKUNGEN:
+     Diese Funktion wird vom move abgefragt. Bitte auf jeden Fall P_PARA oder
+     NoParaObjects() passend definieren, sonst buggts.
+
+     Wenn jemand mit dem oben beschrieben Standardverhalten nicht gluecklich
+     ist, kann man die Funktion passend ueberschreiben.
+
+
+SIEHE AUCH:
+     virtual_compiler
+     CustomizeObject(), Validate(), NoParaObjects(), 
+     P_COMPILER_PATH, P_PARA
+     /std/virtual/v_compiler.c
+----------------------------------------------------------------------------
+21.10.2007, Zesstra
diff --git a/doc/lfun/ReceiveMsg b/doc/lfun/ReceiveMsg
new file mode 100644
index 0000000..6443bcd
--- /dev/null
+++ b/doc/lfun/ReceiveMsg
@@ -0,0 +1,250 @@
+ReceiveMsg()
+
+public varargs int ReceiveMsg(string msg, int msg_typ, string msg_action,
+                              string msg_prefix, mixed origin)
+
+DEFINIERT IN:
+    /std/living/comm.c
+    /std/npc/comm.c
+    /std/player/comm.c
+    /std/room/items.c
+
+ARGUMENTE:
+    string msg
+      String mit der uebermittelten Nachricht. Muss nicht umgebrochen sein,
+      da ReceiveMsg() das ebenfalls erledigt.
+    int msg_typ
+      Nachrichtentyp(en) fuer Filterung und Flags fuer Behandlung/Umbruch.
+      Siehe unten fuer mit | kombinierbare Konstanten.
+    string msg_action (optional)
+      Ausloesende Aktion, wird auch fuer Ignorieren verwendet.
+      Default ist query_verb(). Siehe unten fuer alternative Konstanten.
+    string msg_prefix (optional)
+      String, welcher ggf. im break_string() als indent verwendet wird.
+      Default ist 0 (kein Indent)
+    mixed origin (<object>) (optional)
+      Das die Meldung ausloesende Objekt, wird fuer Ignorieren verwendet.
+      Default: previous_object()
+
+BESCHREIBUNG:
+    ReceiveMsg() wird benutzt, um einem Lebewesen eine Nachricht zu senden.
+    Dabei ruft es das sonst uebliche tell_object() fuer Spieler bzw.
+    catch_tell() im NPC auf.
+
+    Gerade fuer Nachrichten an Spieler bietet die Methode weitere Features,
+    die bisher sonst haendisch zu implementieren waren:
+    1) Pruefung auf Empfangbarkeit, je nach 'msg_typ', zB
+       MT_LOOK nur an Nichtblinde
+       MT_LISTEN nur an Nichttaube
+       MT_DEBUG nur an Magier/Testspieler
+    2) Pruefen auf Ignorieren von
+       - Aktion ('msg_action')
+         - mit 'msg_action' || query_verb()
+       - Urheber ('origin')
+         - fuer sendende Spieler mit origin->query_realname()
+         - fuer sendende NPCs mit origin->Name(RAW))
+    3) Speicherung in der tmhist (Typ MT_COMM)
+    4) Speicherung im Kobold (Typ MT_COMM + aktiver Kobold)
+    5) Umbrechen unter Beruecksichtigung von indent, 'msg_typ'-Flags
+       fuer Umbruch und P_PREPEND_BS
+
+    Raeume definieren standardmaessig ebenfalls ein ReceiveMsg(), welches in
+    jedem Objekt im Raum ReceiveMsg() mit den uebergebenen Argumenten aufruft.
+    In diesem Fall ist der Rueckgabe der kleinste von allen gerufenen
+    ReceiveMsg() zurueckgelieferte Wert.
+    
+RUeCKGABEWERT:
+    > 0 fuer Erfolg, < 0 fuer Misserfolg, s.u.
+    MSG_DELIVERED    bei erfolgreicher Zustellung
+    MSG_BUFFERED     bei Zwischenspeicherung durch den Kobold
+
+    MSG_FAILED       nicht naeher spezifizierter Fehler bei Zustellung
+    MSG_IGNORED      Nachricht wurde ignoriert (Absender, origin)
+    MSG_VERB_IGN     Nachricht wurde ignoriert (Kommandoverb, msg_action)
+    MSG_MUD_IGN      Absendendes Mud wurde ignoriert.
+    MSG_SENSE_BLOCK  Passende Sinne des Empfaenger sind blockiert (z.B.
+                     blind, taub)
+    MSG_BUFFER_FULL  Kobold kann sich nichts mehr merken
+
+BEMERKUNGEN:
+    Fuer saemtliche Alternativmeldungen im Falle einer nicht erfolgreich
+    zugestellten Nachricht ist der Aufrufer von ReceiveMsg() verantwortlich.
+
+    NPCs:
+    * ReceiveMsg() ruft zwecks Abwaertskompatibilitaet catch_tell() auf
+    * empfohlen ist, in NPCs nun ReceiveMsg() zu ueberschreiben.
+      * catch_tell() muss weiterhin fuer andere tell_object()
+        ueberschrieben  werden
+
+BEISPIELE:
+    #1.1 Nachricht an einen Spieler, zB in der Wueste
+    this_player()->ReceiveMsg("Die Sonne brennt dir auf den Kopf.",
+                              MT_FEEL|MT_LOOK);
+
+    #1.2 Nachricht an einen Spieler von einem NPC mit Indent
+    // bei aktivem Editor+Kobold landet dieser Text auch im Kobold
+    this_player()->ReceiveMsg("Du haust ja ganz schoen rein!",
+                              MT_COMM|MT_FAR|MSG_DONT_STORE,
+                              MA_TELL,
+                              "Arkshat teilt dir mit: ");
+
+    #1.3 Nachricht an einen Spieler mit Fallback-Kaskade
+    // Achtung, bei MT_COMM oder Ignorieren gibt es natuerlich auch
+    // Misserfolgs-Rueckgaben. Bei einem normalen Kommando wie diesem
+    // hier ist das unproblematisch und daher sinnvoll:
+    if(this_player()->ReceiveMsg(
+         "Du drueckst den Knopf und es oeffnet sich knirschend "
+         "ein kleines Fach in der Wand.", MT_LOOK) < MSG_DELIVERED &&
+       this_player()->ReceiveMsg(
+         "Du drueckst den Knopf und irgend etwas scheint sich "
+         "knirschend zu oeffnen. Das Geraeusch kam von der Wand.",
+         MT_LISTEN) < MSG_DELIVERED) // leider blind UND taub ... also:
+      this_player()->ReceiveMsg(
+        "Du drueckst den Knopf und irgend etwas scheint zu passieren, "
+        "aber leider siehst und hoerst du nichts.", MT_FEEL);
+
+
+    #2.1 Im NPC als Empfaenger auf ein TM reagieren
+    public varargs int ReceiveMsg(string msg, int msg_typ, string msg_action,
+                                  string msg_prefix, mixed origin) {
+      int ret = MSG_DELIVERED;  // Default
+
+      // eine OOC-Kommunikation?
+      if(msg_typ&MT_COMM) {
+        if(strstr(msg, "hilfe")>=0)
+          if(environment(origin)==environment()) {
+            origin->ReceiveMsg("Ich werd dir gleich helfen!",
+                               MT_COMM|MSG_DONT_STORE, MA_TELL,
+                               "Arkshat teilt dir mit: ");
+          } else {
+            origin->ReceiveMsg("Hilf dir selbst, dann hilft dir Gott!",
+                               MT_COMM|MT_FAR|MSG_DONT_STORE,
+                               MA_TELL,
+                               "Arkshat teilt dir mit: ");
+          }
+        else if(...)
+        [...]
+      } else if(msg_typ&MT_LISTEN && msg_action==MA_SAY) {
+        [...]
+      }
+
+      return ret;
+    }
+
+
+    #3.1 als Sender an viele, Variante mit eigenem filter
+    // Achtung: siehe 3.3. send_room() loest vieles.
+    // Living nickt nur seinen Nichtgegnern zu
+    object *all = filter(all_inventory(environment(this_player())),
+                         #'living) - ({this_player()});
+    all -= this_player()->PresentEnemies();
+    all->ReceiveMsg(this_player()->Name()+
+                    " nickt dir verstohlen zu und scheint bereit.",
+                    MT_LOOK, MA_EMOTE);
+
+    #3.2 als Sender an viele, Variante mit einzelnem Iterieren
+    // Achtung: siehe 3.3. send_room() loest vieles.
+    // Living trinkt etwas, jeder im Raum soll es sehen oder hoeren
+    object ob = first_inventory(environment(this_player()));
+    do {
+      if(living(ob) && ob!=this_player())
+        ob->ReceiveMsg(this_player()->Name()+" trinkt einen Schnaps aus.",
+                       MT_LOOK|MT_LISTEN,
+                       MA_DRINK);
+      ob = next_inventory(ob);
+    } while(ob);
+
+    #3.3 als Sender an viele, Variante mit send_room
+    // Living gruesst seine Freunde
+    // send_room() ruft ReceiveMsg mit allen entsprechenden Parametern
+    object *exclude = this_player()->PresentEnemies();
+    send_room(this_object(),
+              this_player()->Name()+" gruesst dich.",
+              MT_LOOK|MT_LISTEN,
+              MA_EMOTE,
+              0,
+              exclude);
+
+    #3.4 als Sender an viele mit send_room und ReceiveMsg()
+    // Living gruesst seine Freunde, seine Feinde sehen das
+    // send_room() ruft ReceiveMsg mit allen entsprechenden Parametern
+    object *exclude = this_player()->PresentEnemies();
+    send_room(this_object(),
+              this_player()->Name()+" gruesst dich.",
+              MT_LOOK|MT_LISTEN, MA_EMOTE, 0, exclude);
+    exclude->ReceiveMessage(
+      this_player()->Name()+" gruesst, aber nicht dich.",
+      MT_LOOK|MT_LISTEN, MA_EMOTE);
+
+KONSTANTEN FUER PARAMETER:
+    Saemtlich in "/sys/living/comm.h". Hier nicht notwendigerweise
+    immer aktuell oder vollstaendig.
+
+    <msg_typ>
+      MT_UNKNOWN      unspez. Nachrichtentyp (nicht verwenden). Es wird
+                      versucht, aufgrund <msg_action> den Typ zu erraten.
+      MT_LOOK         alles, was man sieht
+      MT_LISTEN       alles, was man hoert
+      MT_FEEL         alles, was man fuehlt
+      MT_TASTE        alles, was man schmeckt
+      MT_SMELL        alles, was man riecht
+      MT_MAGIC        alle sonstigen (uebersinnlichen) Wahrnehmungen
+      MT_NOTIFICATION Statusmeldungen, Kommandobestaetigungen
+      MT_COMM         alle OOC-Kommunikation, d.h. nicht durch o.g. Sinne
+                      abgedeckt.
+      MT_FAR          alles, was aus der Ferne / einem anderen Raum kommt.
+                      muss mit min. einem anderen Typ kombiniert werden
+      MT_DEBUG        Debugmeldungen, sehen nur Magier im Magiermodus
+      MT_NEWS         Mails & MPA
+
+      MSG_DONT_BUFFER Nachricht darf nicht im Kobold gespeichert werden
+      MSG_DONT_STORE  Nachricht darf nicht in die Comm-History
+      MSG_DONT_WRAP   Nachricht nicht per break_string umbrechen
+      MSG_DONT_IGNORE Nachricht kann nicht ignoriert werden
+
+      MSG_BS_LEAVE_LFS    wie BS_LEAVE_MY_LFS fuer break_string()
+      MSG_BS_SINGLE_SPACE wie BS_SINGLE_SPACE fuer break_string()
+      MSG_BS_BLOCK        wie BS_BLOCK fuer break_string()
+      MSG_BS_NO_PARINDENT wie BS_NO_PARINDENT fuer break_string()
+      MSG_BS_INDENT_ONCE  wie BS_INDENT_ONCE fuer break_string()
+      MSG_BS_PREP_INDENT  wie BS_PREPEND_INDENT fuer break_string()
+
+    <msg_action> (optional)
+      MA_UNKNOWN     Unspez. Aktion. Es wird der Default query_verb()
+                     benutzt bzw. versucht, die Aktion zu erraten.
+      MA_PUT         Jemand legt etwas hin und gibt jemanden etwas
+      MA_TAKE        Jemand nimmt etwas
+      MA_MOVE_IN     Jemand betritt den Raum
+      MA_MOVE_OUT    Jemand verlaesst den Raum
+      MA_MOVE        Jemand bewegt sich 
+      MA_FIGHT       Jemand kaempft
+      MA_WIELD       Jemand zueckt eine Waffe
+      MA_UNWIELD     Jemand steckt eine Waffe weg
+      MA_WEAR        Jemand zieht etwas an
+      MA_UNWEAR      Jemand zieht etwas aus
+      MA_EAT         Jemand isst etwas
+      MA_DRINK       Jemand trinkt etwas
+      MA_SPELL       Jemand wirkt einen Spell
+      MA_LOOK        Jemand sieht etwas an, untersucht etwas
+      MA_LISTEN      Jemand horcht oder lauscht an etwas
+      MA_FEEL        Jemand betastet etwas
+      MA_SMELL       Jemand schnueffelt herum
+      MA_SENSE       Jemand macht eine uebersinnliche Wahrnehmung
+      MA_READ        Jemand liest etwas
+      MA_USE         Jemand benutzt etwas
+      MA_SAY         Jemand sagt etwas
+      MA_REMOVE      Etwas verschwindet
+      // MA_CHAT        Chatkrams (z.B. teile-mit, Teamkampfchat)
+      MA_CHANNEL     Ebenen
+      MA_EMOTE       (r)Emotes, Soulverben (remotes mit Typ MT_COMM|MT_FAR)
+      MA_SHOUT       Rufen (nicht: shout()!)
+      MA_SHOUT_SEFUN Rufen ueber shout(SE)
+
+SIEHE AUCH:
+    Verwandt: send_room(SE)
+    Lfuns:    TestIgnore(L)
+    Efuns:    tell_object(E), catch_tell(L), catch_msg(L)
+              query_verb(E), query_once_interactive(E), break_string(SE)
+
+13.03.2016, Zesstra
+
diff --git a/doc/lfun/RegisterEvent b/doc/lfun/RegisterEvent
new file mode 100644
index 0000000..8bc5d8d
--- /dev/null
+++ b/doc/lfun/RegisterEvent
@@ -0,0 +1,82 @@
+
+FUNKTION:
+     int RegisterEvent(string eid, string fun, object listener);
+
+DEFINIERT IN:
+     /p/daemon/eventd.c
+DEKLARIERT IN:
+     /sys/events.h
+
+ARGUMENTE:
+     string eid,
+       Die ID des Events, fuer den man sich registrieren will. Da dieser
+       String fuer alle Events jeweils eindeutig sein muss, empfiehlt es sich,
+       fuer eigene Events z.B. als Praefix den eigenen Magiernamen zu nehmen,
+       z.B. "zesstra_vulkanausbruch".
+       ACHTUNG: IDs, die mit '_evt_lib_' beginnen, sind AUSSCHLIESSLICH der
+       Mudlib vorbehalten!
+     string fun,
+       Name der Funktion, die im Objekt listener bei Auftreten des Events
+       gerufen werden soll.
+     object listener,
+       Das Objekt, das als Lauscher fuer diesen Event registriert werden soll.
+
+BESCHREIBUNG:
+     Das Objekt 'listener' wird als Lauscher dieses Events registriert. Ab
+     diesem Moment wird immer dann, wenn ein Event mit der ID 'eid' ausgeloest
+     wird, in 'listener' die Funktion 'fun' aufgerufen (zeitverzoegert, meist
+     0-2s).
+     
+     Die Funktion wird dabei immer mit 3 Argumenten aufgerufen:
+     listener->fun(eid, triggerob, data);
+     Hierbei ist 'eid' die jeweilige Event-ID und 'triggerob' ist das Objekt,
+     welches den Event ausloeste. 
+     'data' kann jeder LPC-Datentyp sein und enthaelt die Daten, die das
+     triggernde Objekt an alle Listener uebermitteln will (damit sind Datentyp
+     und Inhalt komplett abhaengig vom Event!)
+
+     Existiert bisher noch kein Event mit der ID 'eid', wird implizit ein
+     neuer Event erstellt, der erstmal nur das sich gerade registrierende
+     Objekt als Lauscher hat.
+
+
+RUeCKGABEWERT:
+     1 fuer Erfolg, <=0 fuer Misserfolg.
+     1   - Erfolg, 'listener' wurde eingetragen.
+     -1  - falsche Argumente wurden uebergeben
+     -2  - nicht-oeffentlicher Event und 'listener' wurde nicht fuer diesen
+           Event freigegeben (momentan gibt es noch keine nicht-oeffentlichen
+           Events)
+     -3  - 'fun' in 'listener' ist nicht vorhanden oder nicht von aussen 
+           aufrufbar (protected, static, private).
+     
+BEMERKUNGEN:
+     Wenn 'listener' bereits fuer den Event registriert wird, wird die alte
+     Registrierung ueberschrieben (als ggf. gilt dann der jetzt uebergebene
+     Funktionsname).
+     Die Funktion 'fun' sollte sparsam mit den Eval-Ticks umgehen. Momentan
+     ist die max. Menge an Ticks auf 30000 beschraenkt. Dies kann bei
+     Problemen auch jederzeit reduziert werden!
+     Der EVENTD merkt sich Event-Lauscher nicht ueber Reboots hinaus.
+     Sollte sich eine Blueprint anmelden, sich zerstoeren und neugeladen
+     werden, ist die neue Blueprint noch angemeldet, weil das neue Objekt
+     unter dem alten Namen wiedergefunden wird. Dies gilt _nicht_ fuer
+     Clones!
+
+BEISPIELE:
+     1. Ein Objekt moechte ueber Spielertode informiert werden:
+          EVENTD->RegisterEvent(EVT_LIB_PLAYER_DEATH, "spieler_ist_tot", 
+                                this_object());
+        Ab jetzt wird im Objekt jedes Mal, wenn ein Spieler stirbt, die 
+        Funktion "spieler_ist_tot" aufgerufen.
+
+     2. Ein Objekt will informiert werden, wenn der Event
+        "boing_zwergenkoenig_angriff" ausgeloest wird:
+           EVENTD->RegisterEvent("boing_zwergenkoenig_angriff","alarm",
+                                  this_object());
+
+SIEHE AUCH:
+     events, eventd, UnregisterEvent(), TriggerEvent()
+
+----------------------------------------------------------------------------
+Last modified: 15.08.2007, Zesstra
diff --git a/doc/lfun/RegisterHelperNPC b/doc/lfun/RegisterHelperNPC
new file mode 100644
index 0000000..170e8b1
--- /dev/null
+++ b/doc/lfun/RegisterHelperNPC
@@ -0,0 +1,87 @@
+RegisterHelperNPC()
+FUNKTION:
+     public int RegisterHelperNPC(object npc, int flags);
+
+DEFINIERT IN:
+     /std/player/combat.c
+     /sys/living/combat.h
+
+ARGUMENTE:
+     object npc
+       Objekt des helfenden NPC, der angemeldet werden soll.
+
+     int flags
+       ver-oder-te Konstanten, die die Art des Helfer-NPC beschreiben (s.u.)
+
+BESCHREIBUNG:
+     Mit dieser Funktion wird ein einem Spieler helfender NPC im Spieler
+     angemeldet. Hierdurch kann spaeter herausgefunden werden, welche NPC
+     einem Spieler helfen und ggf. den von diesen verursachten Schaden im
+     Kampf dem Spieler zuzurechnen.
+
+     Die Flags sind eine der folgenden Konstanten oder eine beliebige durch
+     ver-oder-ung gebildete Kombination.
+
+     Momentan gibt es 2 Klassen von Helfer-NPC:
+     + GUILD_HELPER: der Helfer-NPC ist ein Gilden-NPC
+     + MISC_HELPER:  der Helfer-NPC ist irgendein NPC, der nicht zu einer Gilde
+                     gehoert.
+
+     Zusaetzlich zu den Klassen gibt es noch weitere Flags, die etwas ueber
+     den Helfer-NPC sagen:
+
+     + EXCLUSIVE_HELPER: dieser Helfer-NPC duldet keinen weiteren NPC der
+                         gleichen Klasse.
+     + ACTIVE_HELPER: ist dieses Flag gesetzt, ist der NPC mehr als nur reiner
+                      Schlagfaenger.
+
+     Wird EXCLUSIVE_HELPER gesetzt und es ist in der gleichen Klasse schon ein
+     NPC angemeldet, schlaegt die Registrierung fehl.
+     Ist in der gleichen Klasse bereits ein NPC mit EXCLUSIVE_HELPER
+     angemeldet, schlaegt die Registierung ebenfalls fehl, auch wenn der neue
+     NPC kein EXCLUSIVE_HELPER setzt.
+
+RUeCKGABEWERT:
+     1, wenn die Registrierung erfolgreich war.
+     0 sonst. In diesem Fall darf der NPC dem Spieler NICHT helfen, weder
+       passiv (Schlagfaenger), noch aktiv.
+
+BEMERKUNGEN:
+     Diese Funktion setzt bei der Erfolg die Property P_HELPER_NPC in <npc>
+     auf passende Werte.
+     Bitte auf gar keinen Fall die numerischen Werte der Konstanten fuer
+     <flags> nutzen, diese koennen sich jederzeit aendern.
+
+BEISPIELE:
+     1. Ein nicht-exklusiver Gilden-NPC, der dem Spieler folgt.
+     if (spieler->RegisterHelperNPC(this_object(), GUILD_HELPER) == 1) {
+       move(environment(spieler), M_GO);
+       spieler->AddPursuer(this_object());
+       // meldung ausgebene...
+     }
+
+     2a. Ein exklusiver Nicht-Gilden-NPC
+     if (spieler->RegisterHelperNPC(this_object(),
+                                    MISC_HELPER|EXCLUSIVE_HELPER) == 1) {
+       move(environment(spieler), M_GO);
+     }
+
+     2b. Ein nicht-exklusiver Nicht-Gilde-NPC, der nach 2a. kommt.
+     if (spieler->RegisterHelperNPC(this_object(), MISC_HELPER) == 1) {
+       // ... wenn der NPC aus 2a noch existiert, trifft dies hier nie zu.
+     }
+
+     3. Ein exklusiver NPC, der weitere Gilden- und sonstige NPC ausschliesst
+        (Solche Kombination bitte mit der Gilden-Balance abstimmen.)
+     if (spieler->RegisterHelperNPC(this_object(), 
+                            MISC_HELPER|GUILD_HELPER|EXCLUSIVE_HELPER) == 1) {
+       move(environment(spieler), M_GO);
+     }
+
+     4. Die Registrierung ohne Klasse schlaegt fehl, z.B.:
+       spieler->RegisterHelperNPC(this_object(), 0);
+       spieler->RegisterHelperNPC(this_object(), EXCLUSIVE_HELPER);
+
+SIEHE AUCH:
+    UnregisterHelperNPC()
+    P_HELPER_NPC
diff --git a/doc/lfun/RegisterHelperObject b/doc/lfun/RegisterHelperObject
new file mode 100644
index 0000000..aa1cb66
--- /dev/null
+++ b/doc/lfun/RegisterHelperObject
@@ -0,0 +1,113 @@
+
+FUNKTION:
+     int RegisterHelperObject(object helper, int type, 
+                              string|closure callback);
+
+DEFINIERT IN:
+     /std/living/helpers.c
+
+ARGUMENTE:
+     object helper
+       Das Objekt, das bei einem Lebewesen als Hilfsobjekt registriert 
+       werden soll. Das Objekt muss sich dazu nicht im Inventar des
+       Lebewesens befinden.
+     int type
+       Helfertyp, einer der in /sys/living/helpers.h definierten Typen:
+       - HELPER_TYPE_AERIAL fuer die Flug-/Segelunterstuetzung
+       - HELPER_TYPE_AQUATIC fuer Tauchunterstuetzung
+     string|closure callback
+        Closure oder Funktionsname als String; dies ist die Funktion, die im
+        Objekt helper gerufen wird, um abzufragen, ob es sich aktuell fuer
+        zustaendig erklaert oder nicht.
+
+BESCHREIBUNG:
+     Das Objekt "helper" wird als Hilfsobjekt fuer bestimmte Aktivitaeten wie
+     zum Beispiel Fliegen oder Tauchen registriert. 
+     
+     Die als Callback uebergebene Funktion wird bei der Abfrage der 
+     P_*_HELPERS-Properties (siehe unten) aufgerufen und deren 
+     Rueckgabewert in der Property vermerkt. 
+     
+     Der Rueckgabewert der Callback-Methode muss im Wertebereich 
+     0..10000 liegen, wobei ein Wert von 0 bedeutet, dass das Hilfsobjekt 
+     sich gerade nicht zustaendig fuehlt. Bei den Werten >0 ist es dem 
+     abfragenden Objekt ueberlassen, wie es diese Daten bewertet.
+
+     Die Funktion muss existieren und public sein, d.h. von aussen gerufen 
+     werden koennen.
+     
+     Die Funktion bekommt das Spielerobjekt und das abfragende Objekt als
+     Parameter uebergeben.
+
+HINWEIS:
+     Es ist ein Unterschied, ob sich ein Objekt via UnregisterHelperObject()
+     als Helfer austraegt, oder ob der Aufruf der Callback-Funktion eine
+     0 zurueckgibt. Im letzteren Fall ist das Objekt nach wie vor 
+     registriert und kann trotzdem vom abfragenden Objekt als 
+     funktionsfaehig angesehen werden.
+
+RUECKGABEWERTE:
+      1  Objekt wurde erfolgreich registriert (HELPER_SUCCESS)
+     -1  angegebenes Hilfsobjekt existiert nicht (HELPER_NO_CALLBACK_OBJECT)
+     -2  angegebenes Hilfsobjekt ist bereits fuer diese Art der
+         Unterstuetzung registriert (HELPER_ALREADY_LISTED)
+
+BEISPIELE:
+     a) Eine luftgefuellte Blase will sich als Tauch-Helfer am Spieler
+     anmelden und ist zustaendig, solange sich noch Luft in ihr befindet:
+
+     int luft = 5; // globale Variable z.B. fuer Luftinhalt von 5 Atemzuegen
+
+     // Registrierung im Spielerobjekt
+     if ( TP->RegisterHelperObject(ME, HELPER_TYPE_AQUATIC,
+          "DivingCallback") == HELPER_SUCCESS ) {
+       tell_object(TP, "Du kannst jetzt mit Hilfe der Luftblase unter Wasser "
+         "atmen.\n");
+     }
+
+     // hier koennen natuerlich auch noch komplexere Dinge ablaufen, z.B.
+     // wenn ein Wert zurueckgegeben werden soll, der nicht nur 0 oder 1 ist.
+     public int DivingCallback(object player, object caller) {
+       return (environment(ME)==player && luft>0);
+     }
+
+     b) Ein Spieler befindet sich in einem Raum mit einem steilen Abhang, den
+     man hinunterspringen kann. Dies geht aber nur dann gefahrlos, wenn es
+     Hilfsobjekte gibt, die den (Segel)flug erlauben:
+
+     // cmd_springen() sei die Funktion, die bei der Eingabe des Befehls durch
+     // den Spieler aufgerufen wird.
+     static int cmd_springen(string str) {
+       [...]
+       // Property abfragen
+       mapping helpers = TP->QueryProp(P_AERIAL_HELPERS);
+       // Liste der Werte fuer die einzelnen Unterstuetzungsobjekte ermitteln
+       int *values = m_values(helpers,0);
+       // Spieler schonmal runterbewegen, die Folgen seines Handelns spuert
+       // er dann, wenn er unten angekommen ist.
+       TP->move(zielraum, M_GO|M_SILENT);
+       // "helpers" ist immer ein Mapping, das pruefen wir nicht extra.
+       // Wenn die Liste der Objekte noch mindestens ein Element enthaelt,
+       // nachdem alle unzustaendigen Hilfsobjekte (Rueckgabewert der
+       // Callback-Funktion == 0) rausgerechnet wurden, existiert mindestens
+       // ein geeignetes Hilfsobjekt.
+       if ( sizeof(values-({0})) ) {
+         tell_object(TP, "Sanft segelst Du den Hang hinab.\n");
+       }
+       else {
+         tell_object(TP, BS("Todesmutig springst Du den Hang hinab und "
+           "schlaegst hart unten auf. Du haettest vielleicht daran denken "
+           "sollen, Dir geeignete Hilfsmittel fuer so eine Aktion zu "
+           "besorgen.");
+         TP->Defend(800, ({DT_BLUDGEON}), ([SP_NO_ACTIVE_DEFENSE:1]), ME);
+       }
+       return 1;
+     }
+
+SIEHE AUCH:
+     Funktionen:  UnregisterHelperObject()
+     Properties:  P_HELPER_OBJECTS, P_AERIAL_HELPERS, P_AQUATIC_HELPERS
+     Sonstiges:   /sys/living/helpers.h
+
+05.02.2015 Arathorn
+
diff --git a/doc/lfun/RemoveAdjective b/doc/lfun/RemoveAdjective
new file mode 100644
index 0000000..48605dd
--- /dev/null
+++ b/doc/lfun/RemoveAdjective
@@ -0,0 +1,24 @@
+RemoveAdjective()
+
+FUNKTION:
+     void RemoveAdjective(string|string* adj);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     adj
+          String oder Array von String mit den Adjektiven.
+
+BESCHREIBUNG:
+     Falls einige der vorhandenen Adjektive nicht mehr verwendet werden
+     sollen, koennen sie mit dieser Funktion entfernt werden.
+
+RUeCKGABEWERT:
+     keiner
+
+SIEHE AUCH:
+     AddAdjective(), RemoveId(), /std/thing/description.c
+
+----------------------------------------------------------------------------
+20.01.2015, Zesstra
diff --git a/doc/lfun/RemoveClass b/doc/lfun/RemoveClass
new file mode 100644
index 0000000..3eeda6c
--- /dev/null
+++ b/doc/lfun/RemoveClass
@@ -0,0 +1,23 @@
+RemoveClass()
+
+FUNKTION:
+     void RemoveClass(string|string* class);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     string/string* class	- String oder Stringarray der Klasse(n)
+
+BESCHREIBUNG:
+     Dem Objekt werden einige der mit AddClass() gesetzten Klassen wieder 

+     entzogen.
+     Die allgemein verfuegbaren Klassen sind unter /sys/class.h definiert.
+
+SIEHE AUCH:
+     AddClass(), is_class_member()
+     P_CLASS
+
+----------------------------------------------------------------------------
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/RemoveCmd b/doc/lfun/RemoveCmd
new file mode 100644
index 0000000..25941c6
--- /dev/null
+++ b/doc/lfun/RemoveCmd
@@ -0,0 +1,60 @@
+RemoveCmd(L)
+FUNKTION:
+    varargs int RemoveCmd(mixed cmd, int norule, mixed id)
+
+DEFINIERT IN:
+    /std/thing/commands.c
+
+ARGUMENTE:
+    com
+         String oder Array von Strings mit den zu entfernenden Kommandos.
+    norule
+         Kommandos mit Regeln werden nicht entfernt (ausser bei cmd==0)
+    id
+         eine ID, mit der man ein vorher mit dieser ID gespeichertes
+         Kommando eindeutig lvschen kann
+
+BESCHREIBUNG:
+    Mit AddCmd() hinzugefuegte Kommandos koennen mit diesem Befehl wieder
+    abgemeldet werden. Die entfernten Kommandos sind direkt nach dem
+    RemoveCmd()-Aufruf nicht mehr ansprechbar.
+
+    Wird ein Regelstring angegeben, so wird die identische AddCmd-
+    Regel entfernt.
+
+BEMERKUNGEN:
+    Uebergibt man fuer com eine 0, so werden alle definierten Kommandos
+    entfernt!
+
+RUECKGABEWERT:
+    Anzahl der entfernten Kommandos.
+
+BEISPIELE:
+    (1) AddCmd("test");
+    (2) AddCmd("test|teste&mit&parameter");
+    (3) AddCmd(({"test"}),1);
+    (4) AddCmd("test",0,0,"XYZ");
+    (5) AddCmd("test&mit&parameter",0,0,"XYZ");
+
+    RemoveCmd(0);
+    - entfernt alle Kommandos
+    RemoveCmd("test",1);
+    - entfernt (1) und (3)
+    RemoveCmd("test");
+    - entfernt (1), (3) und einen Teil von (2),
+      es verbleibt "teste&mit&parameter"
+    RemoveCmd("test|teste&mit&parameter"
+    - entfernt (2)
+    RemoveCmd("test",0,"XYZ");
+    - entfernt (4) und (5)
+    RemoveCmd("test",1,"XYZ");
+    - entfernt (4), nicht (5)
+    RemoveCmd(0,0,"XYZ");
+    - entfernt (4) und (5)
+
+SIEHE AUCH:
+			AddCmd(L), AddCmd_bsp
+    Sonstiges:		replace_personal(E), enable_commands(E), init(E)
+    Alternativen:	AddAction(L), add_action(E)
+
+24.Maerz 2004 Gloinson
diff --git a/doc/lfun/RemoveDefender b/doc/lfun/RemoveDefender
new file mode 100644
index 0000000..aad46df
--- /dev/null
+++ b/doc/lfun/RemoveDefender
@@ -0,0 +1,36 @@
+RemoveDefender()
+
+FUNKTION:
+	void RemoveDefender(object friend);
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	friend
+	  Objekt (normal Lebewesen), welches zukuenftig nicht mehr ueber
+	  Angriffe informiert werden soll und diese auch nicht mehr abwehrt.
+
+BESCHREIBUNG:
+	Ein Lebewesen, welches angegriffen wird, kann andere Objekte ueber
+	einen solchen Angriff per InformDefend() informieren oder ihnen
+	sogar die Moeglichkeit geben, per DefendOther() direkt in den
+	laufenden Angriff einzugreifen (Schaeden abwehren oder umwandeln).
+	Im Normalfall handelt es sich hierbei um andere Lebewesen, welche
+	als Verteidiger des angegriffenen Lebewesens auftreten: Daher der
+	Name der Funktion. Ausserdem besteht die Einschraenkung, dass diese
+	Objekte in der gleichen Umgebung sein muessen, wie das zu
+	verteidigende Lebewesen.
+	Die Objekte sind in Form eines Arrays in der Property P_DEFENDERS
+	abgespeichert und koennen dort abgerufen werden. Natuerlich kann
+	man alte Objekte direkt dort loeschen, jedoch sollte man die
+	hierfuer bereitgestellte Funktionen RemoveDefender() verwenden.
+	Zum Hinzufuegen von Eintraegen im Array steht ebenfalls eine
+	Funktion bereit: AddDefender().
+
+SIEHE AUCH:
+	AddDefender(), InformDefend(), DefendOther(),
+	P_DEFENDERS, /std/living/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Thu Jul 29 18:48:45 1999 by Patryn
diff --git a/doc/lfun/RemoveDetail b/doc/lfun/RemoveDetail
new file mode 100644
index 0000000..f73af2a
--- /dev/null
+++ b/doc/lfun/RemoveDetail
@@ -0,0 +1,57 @@
+RemoveDetail()
+
+FUNKTION:
+    void RemoveDetail(mixed *keys);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+      String oder Array von Strings mit den zu entfernenden Details.
+
+BESCHREIBUNG:
+    Entfernt die in <keys> angegebenen Details aus der Liste der
+    vorhandenen Details. Uebergibt man fuer <keys> eine 0, so werden
+    saemtliche Details entfernt!
+
+BEISPIEL:
+    Ein kleines Beispiel, bei dem eine Oeffnung erscheint und wieder
+    verschwindet, je nachdem, ob man eine Luke oeffnet oder schliesst.
+      int oeffneLuke(string str);
+      int schliesseLuke(string str);
+      ...
+      AddCmd("oeffne", #'oeffneLuke);
+      AddCmd("schliesse", #'schliesseLuke);
+      ...
+      int oeffneLuke(string str) {
+        if(str!="luke" || GetDetail("oeffnung"))
+          return 0;
+        AddDetail("oeffnung","Du siehst eine kleine Oeffnung.\n");
+        return 1;
+      }
+
+      int schliesseLuke(string str) {
+        if(str!="luke" || !GetDetail("oeffnung"))
+          return 0;
+        RemoveDetail("oeffnung"); // Detail wieder entfernen
+        return 1;
+      }
+
+BEMERKUNGEN:
+    Da intern Details und SpecialDetails im gleichen Mapping verwaltet
+    werden, lassen sich mit dieser Funktion auch SpecialDetails
+    entfernen.
+    Die Funktion RemoveSpecialDetail() sollte also nicht genutzt werden!
+
+SIEHE AUCH:
+    Setzen:    AddDetail(), AddReadDetail(), AddSmells(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveReadDetail(), RemoveSmells(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS,
+               P_TOUCH_DETAILS, P_SPECIAL_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+8. Juli 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/RemoveExit b/doc/lfun/RemoveExit
new file mode 100644
index 0000000..edc61f2
--- /dev/null
+++ b/doc/lfun/RemoveExit
@@ -0,0 +1,28 @@
+RemoveExit()
+FUNKTION:
+     void RemoveExit(string|string* cmd);
+
+DEFINIERT IN:
+     /std/room/exits
+
+ARGUMENTE:
+     string/string* cmd
+          Richtung(en), die entfernt werden sollen.
+
+BESCHREIBUNG:
+     Die in cmd angegebenen Ausgaenge werden wieder entfernt.
+
+     Ist cmd = 0, so werden alle Ausgaenge entfernt.
+
+BEMERKUNGEN:
+     Ausgaenge werden an der gleichen Stelle gespeichert:
+     - RemoveExit() greift auf die gleichen Daten wie RemoveSpecialExit()
+       zu, entfernt also auch spezielle Ausgaenge!
+
+SIEHE AUCH:
+     AddExit(), AddSpecialExit(), GetExits()
+     RemoveSpecialExit(),
+     H_HOOK_EXIT_USE, P_EXITS, P_HIDE_EXITS, /std/room/exits.c
+     ausgaenge
+
+31.01.2015, Zesstra
diff --git a/doc/lfun/RemoveExtraLook b/doc/lfun/RemoveExtraLook
new file mode 100644
index 0000000..1ba196e
--- /dev/null
+++ b/doc/lfun/RemoveExtraLook
@@ -0,0 +1,39 @@
+RemoveExtraLook()
+
+int RemoveExtraLook(string look);
+
+DEFINIERT IN:
+   /std/living/description.c
+
+BESCHREIBUNG:
+   Der Extralook erscheint in der Langbeschreibung des Lebewesens.
+   Eintraege koennen mit dieser Funktion (vorzeitig) wieder entfernt werden.
+
+ARGUMENTE:
+  - string key:
+    Schluesselwort, unter dem der Eintrag, den man entfernen moechte, von
+    AddExtraLook() registriert wurde.
+
+RUECKGABEWERTE:
+  > 0, falls der Eintrag erfolgreich entfernt wurde.
+  < 0 sonst.
+    -1: keinen (gueltigen) <key> uebergeben.
+    -2: kein Eintrag fuer <key> gefunden.
+
+BEMERKUNGEN:
+  Beim Entfernen mit dieser Funktion wird die "Endemeldung" des entfernten
+  Eintrages nicht ausgegeben.
+
+BEISPIELE:
+  # Extralook registrieren.
+  living->AddExtraLook("@WER1 wird von einer Horde Daemonen verfolgt.",
+                       "ennox_daemonenhordenverfolgerlook");
+  # Nun kann der Eintrag auch wieder entfernt werden:
+  living->RemoveExtraLook("ennox_daemonenhordenverfolgerlook");
+
+SIEHE AUCH:
+   AddExtraLook(),
+   P_INTERNAL_EXTRA_LOOK
+
+14.05.2007, Zesstra
+
diff --git a/doc/lfun/RemoveFixedObject b/doc/lfun/RemoveFixedObject
new file mode 100644
index 0000000..7616b2b
--- /dev/null
+++ b/doc/lfun/RemoveFixedObject
@@ -0,0 +1,40 @@
+RemoveFixedObject()
+
+FUNKTION:
+	void RemoveFixedObject(string filename);
+
+DEFINIERT IN:
+	/std/laden.c
+
+ARGUMENTE:
+	str
+	  Dateiname des Objektes, welches nicht mehr dauerhaft im Laden sein
+	  soll.
+
+RUeCKGABEWERT:
+        keiner
+
+BESCHREIBUNG:
+	Objekte, die mittels der Funktion AddFixedObject() im Laden
+	eingebunden werden, sind dort staendig verfuegbar. Um diesen Zustand
+	wieder aufzuheben, kann man sie mittels dieser Funktion wieder
+	entfernen. Eventuell im Lager befindliche Objekte dieser Art werden
+	hierbei nicht zerstoert und koennen durchaus noch gekauft werden,
+	bis sie alle sind. Danach gibt es sie dort nicht mehr!
+
+BEISPIELE:
+	Im allen Laeden gibt es schon ein Objekt, welches dort
+	standardmaessig erhaeltlich ist. Folgende Zeile sorgt dafuer:
+	  AddFixedObject("/obj/boerse",80,({"boerse","geldboerse"}));
+	Wenn man das in seinem Laden nicht wuenscht, kann man die Geldboerse
+	mittels folgender Zeile wieder aus dem Laden loeschen:
+	  RemoveFixedObject("/obj/boerse");
+	Es ist nicht moeglich, keine oder mehrere Objekte anzugeben, um auf
+	diese Weise alle bzw. mehrere Objekte als nicht mehr staendig
+	verfuegbar zu markieren.
+
+SIEHE AUCH:
+	AddFixedObject(), SetStorageRoom(), /std/store.c
+
+----------------------------------------------------------------------------
+Last modified: Thu Jun 18 14:19:19 1998 by Patryn
diff --git a/doc/lfun/RemoveFromMenu b/doc/lfun/RemoveFromMenu
new file mode 100644
index 0000000..a8326af
--- /dev/null
+++ b/doc/lfun/RemoveFromMenu
@@ -0,0 +1,32 @@
+RemoveFromMenu()
+
+FUNKTION:
+     int RemoveFromMenu(mixed ids)
+
+DEFINIERT IN:
+     /std/pub.c
+
+ARGUMENTE:
+     ids - string oder string*
+	   String oder Array von Strings, mit denen sich die Speise bzw.
+           das Getraenk beim Bestellen ansprechen laesst.
+
+RUECKGABEWERT:
+     int - Anzahl entfernter Einzelmenueeintraege
+
+BESCHREIBUNG:
+     Mit dieser Methode kann man id-basiert Speisen und Getraenke
+     wieder von der Karte entfernen (wenn zB nur saisonbasiert
+     bestimmte Dinge angeboten werden sollen).
+     
+BEMERKUNGEN:
+     - sich zwischen zwei Speisen ueberschneidende IDs fuehren zu
+       undefiniertem Verhalten!
+     - bei Benutzung von RemoveFromMenu sollte man in Delay-Speisen oder
+       -Getraenken auf die Nutzung vom ident-Parameter in den
+       Serviernachrichten verzichten (es sei, man weiss was man tut)
+
+SIEHE AUCH:
+     AddToMenu(), AddFood(), AddDrink(), /sys/pub.h
+
+15. Februar 2009 Gloinson
diff --git a/doc/lfun/RemoveFunc b/doc/lfun/RemoveFunc
new file mode 100644
index 0000000..054abe4
--- /dev/null
+++ b/doc/lfun/RemoveFunc
@@ -0,0 +1,87 @@
+RemoveFunc()
+
+FUNKTION:
+     int RemoveFunc(object ruest, int info, object user);
+
+DEFINIERT IN:
+     eigenen Objekten (fuer /std/clothing/wear)
+
+ARGUMENTE:
+     ruest (object)
+          Die Ruestung/Kleidung, die ausgezogen werden soll.
+     info (int)
+          Bei (info&M_SILENT) wird keine Meldung ueber das Ausziehen
+          ausgegeben.
+          Bei (info&M_NOCHECK) wird die Kleidung ausgezogen, egal was diese
+          Funktion zurueckgibt. Dies tritt insbesondere dann auf, wenn der
+          Spieler, der die Ruestung traegt, stirbt und die Ruestung in
+          die Leiche bewegt wird.
+     user (object)
+          Das Lebewesen, welches die Ruestung/Kleidung gerade traegt und sie
+          ausziehen will.
+
+BESCHREIBUNG:
+     Mit dieser Funktion kann man pruefen, ob sich das Kleidungsstueck bzw.
+     Ruestung <ruest> von this_player() ausziehen laesst oder nicht.
+     Kann die Ruestung ausgezogen werden, so muss ein Wert ungleich 0
+     zurueckgegeben werden.
+     
+     Diese Funktion meldet nur einen _Wunsch_ an. Dieser kann ignoriert
+     werden (z.B. bei bestimmten Bewegungen, Tod des Spielers etc.).
+     Unabhaengig vom Wert dieser Funktion kann das Ausziehen noch Scheitern
+     oder Erzwungen werden.
+     Wenn ihr sicher sein wollt, dass der Spieler ein Objekt angezogen hat,
+     benutzt bitte InformUnwear().
+
+RUeCKGABEWERT:
+     0, wenn sich die Ruestung nicht ausziehen laesst, ansonsten ungleich 0.
+
+BEMERKUNGEN:
+     Verfluchte Ruestungen, die sich erst nach Entfernung des Fluches wieder
+     ausziehen lassen, sollte man besser mit P_CURSED realisieren (man spart
+     die RemoveFunc() ein).
+     Bitte nicht drauf verlassen, dass this_player() das ausziehende Lebewesen
+     ist.
+     Die Reihenfolge der Argumente ist etwas unschoen, aber leider wurde <user>
+     erheblich spaeter hinzugefuegt und es war unmoeglich, einige hundert
+     Objekte zu aendern.
+
+BEISPIELE:
+     Ein Umhang, den man nur mit guter Einstellung wieder ausziehen kann:
+
+     inherit "std/armour.c";
+
+     #include <properties.h>
+     #include <moving.h>
+
+     create()
+     {
+       ::create();
+
+       SetProp(P_ARMOUR_TYPE, AT_CLOAK);
+       /* zig weitere SetProp's, um den Umhang zu konfigurieren */
+
+       /* RemoveFunc() ist im Umhang selbst zu finden */
+       SetProp(P_REMOVE_FUNC, this_object());
+     }
+
+     int RemoveFunc(object me, int info, object user)
+     {
+       if (user->QueryProp(P_ALIGN) >= 0 || (info&M_NOCHECK))
+         return 1;   /* gute Charaktere koennen den Umhang ausziehen */
+
+       /* Ansonsten geben wir evtl. einen entsprechenden Hinweis aus: */
+       if (!(info&M_SILENT))
+           write( "Der Umhang wird von Deiner Bosheit so sehr "
+                 +"angezogen, dass Du ihn\nnicht mehr ausziehen kannst!\n");
+       return 0;
+     }
+
+SIEHE AUCH:
+     P_WEAR_MSG, P_UNWEAR_MSG, P_WIELD_MSG, P_UNWIELD_MSG
+     DoUnwear(), DoWear(), InformWear(), InformUnwear()
+     /std/clothing/wear.c
+
+----------------------------------------------------------------------------
+02.07.2013, Zesstra
+
diff --git a/doc/lfun/RemoveId b/doc/lfun/RemoveId
new file mode 100644
index 0000000..85e7099
--- /dev/null
+++ b/doc/lfun/RemoveId
@@ -0,0 +1,26 @@
+RemoveId()
+
+FUNKTION:
+     void RemoveId(string|string* ids);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     ids
+          String oder Array von Strings mit den Bezeichnungen, die aus der
+          Liste der IDs des Objektes entfernt werden sollen.
+
+BESCHREIBUNG:
+     Wenn ein Objekt sich nicht mehr mit gewissen Bezeichnern ansprechen
+     lassen soll, kann man diese mit dieser Funktion entfernen.
+
+RUeCKGABEWERT:
+     keiner
+
+SIEHE AUCH:
+     AddId(), RemoveAdjective(), /std/thing/description.c
+
+----------------------------------------------------------------------------
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/RemoveInfo b/doc/lfun/RemoveInfo
new file mode 100644
index 0000000..5ced62c
--- /dev/null
+++ b/doc/lfun/RemoveInfo
@@ -0,0 +1,24 @@
+RemoveInfo()
+FUNKTION:
+     void RemoveInfo(string key)
+
+DEFINIERT IN:
+     /std/npc/info.c
+
+ARGUMENTE:
+     key	zu loeschender Schluessel
+
+BESCHREIBUNG:
+     Loescht die Antwort zum entsprechenden Frageschluessel.
+
+BEISPIEL:
+     // erstellen und loeschen
+     AddInfo("gold","sagt: Ich habe keines.\n");
+     RemoveInfo("gold");
+
+SIEHE AUCH:
+     AddInfo(L), AddSpecialInfo(L)
+     P_PRE_INFO, P_DEFAULT_INFO
+     /std/npc/info.c
+
+17.Aug.2003 Gloinson
diff --git a/doc/lfun/RemoveItem b/doc/lfun/RemoveItem
new file mode 100644
index 0000000..aa454f7
--- /dev/null
+++ b/doc/lfun/RemoveItem
@@ -0,0 +1,41 @@
+RemoveItem()
+
+FUNKTION:
+	void RemoveItem(mixed file);
+
+DEFINIERT IN:
+	/std/room/items.c
+
+ARGUMENTE:
+	file
+	     String oder Array von Strings mit dem Namen des zu entfernenden
+	     Objekts.
+
+BESCHREIBUNG:
+	Das mit AddItem(file) dem Raum hinzugefuegte Objekt wird wieder aus
+	der Liste der Objekte entfernt.
+	Wurde bei AddItem() ein Array von Dateinamen uebergeben, so muss das
+	selbe Array auch bei RemoveItem() uebergeben werden!
+	Falls das Objekt, das durch den AddItem()-Aufruf erzeugt wurde, sich
+	noch im Raum befindet, wird es durch den RemoveItem()-Aufruf zerstoert.
+
+RUECKGABEWERT:
+	keiner
+
+BEISPIELE:
+	Ein muellschluckerfreier Laden laesst sich wie folgt erzeugen:
+
+	inherit "/std/laden";
+	#include <properties.h>
+
+	create()
+	{
+	  ::create();  // Hier wird u.a. der Muellschlucker erzeugt
+
+	  RemoveItem("/obj/entsorg"); // und weg damit!
+
+	  SetProp(...);  // und die normale Beschreibung...
+	}
+
+SIEHE AUCH:
+	AddItem(), /std/room/items.c
diff --git a/doc/lfun/RemoveKnownPotion b/doc/lfun/RemoveKnownPotion
new file mode 100644
index 0000000..5b1418c
--- /dev/null
+++ b/doc/lfun/RemoveKnownPotion
@@ -0,0 +1,25 @@
+RemoveKnownPotion()
+
+FUNKTION:
+     int RemoveKnownPotion(int nr)
+
+DEFINIERT IN:
+     /std/player/potion.c
+
+ARGUMENTE:
+     int nr       Nummer eines ZTs
+
+BESCHREIBUNG:
+     Entfernt einen bekannten ZT aus einen Spieler. Nur vom Orakel rufbar.
+
+RUeCKGABEWERT:
+     1  Erfolg
+     -1 fehlende Berechtigung
+     -2 Nummer nicht eingetragen
+
+SIEHE AUCH:
+     Sonstiges: zaubertraenke, /secure/potionmaster.c, /room/orakel.c
+     Verwandt:  FindPotion(), AddKnownPotion(), InList()
+     Props:     P_POTIONROOMS, P_KNOWN_POTIONROOMS
+
+6.Feb 2016 Gloinson
diff --git a/doc/lfun/RemovePluralId b/doc/lfun/RemovePluralId
new file mode 100644
index 0000000..0960e02
--- /dev/null
+++ b/doc/lfun/RemovePluralId
@@ -0,0 +1,28 @@
+RemovePluralId()
+
+FUNKTION:
+     void RemovePluralId(mixed id);
+
+DEFINIERT IN:
+     /std/unit.c
+
+ARGUMENTE:
+     id
+          Identfikationsstring oder Array von Strings
+
+BESCHREIBUNG:
+     Es werden ein oder mehrere Bezeichner entfernt, mit denen sich 
+     mehrere Einheiten der Menge ansprechen lassen.
+
+RUECKGABEWERT:
+     keiner
+
+BEISPIELE:
+     siehe /items/money.c
+
+SIEHE AUCH:
+     RemoveSingularId(), AddPluralId(), AddId(), id(), /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Mon Jul 14 11:30:00 1997 by Silvana
+
diff --git a/doc/lfun/RemovePursuer b/doc/lfun/RemovePursuer
new file mode 100644
index 0000000..8ba2358
--- /dev/null
+++ b/doc/lfun/RemovePursuer
@@ -0,0 +1,26 @@
+RemoveRursuer()
+
+FUNKTION:
+  void RemovePursuer(object pursuer)
+
+ARGUMENTE:
+  pursuer: Objekt, das aus der Verfolgerliste des Objektes, in dem
+           RemovePursuer aufgerufen wurde, ausgetragen werden soll.
+
+FUNKTION:
+  Durch den Aufruf von RemovePursuer in einem Objekt, welches living() ist,
+  wird das Object, welches als Argument uebergeben wurde aus der Liste
+  der Verfolger ausgetragen.
+
+RUECKGABEWERT:
+  keiner
+
+BEMERKUNG:
+  keine
+
+BEISPIELE:
+  find_player("jof")->RemovePursuer(find_player("kirk"))
+  Danach wird Jof nicht mehr von Kirk verfolgt.
+
+SIEHE AUCH:
+  "AddPursuer", "PreventFollow"
diff --git a/doc/lfun/RemoveReadDetail b/doc/lfun/RemoveReadDetail
new file mode 100644
index 0000000..f357954
--- /dev/null
+++ b/doc/lfun/RemoveReadDetail
@@ -0,0 +1,31 @@
+RemoveReadDetail()
+
+FUNKTION:
+    void RemoveReadDetail(string|string* keys);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+         String oder Array von Strings mit den zu entfernenden Details.
+
+BESCHREIBUNG:
+    Entfernt die in keys angegebenen Details aus der Liste der vorhandenen
+    lesbaren Details.
+
+BEMERKUNGEN:
+    Uebergibt man fuer keys eine 0, so werden saemtliche lesbaren Details
+    entfernt!
+
+SIEHE AUCH:
+    Setzen:    AddDetail(), AddReadDetail(), AddSmells(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveDetail(), RemoveSmells(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS, P_TOUCH_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/RemoveResistanceModifier b/doc/lfun/RemoveResistanceModifier
new file mode 100644
index 0000000..0e630fb
--- /dev/null
+++ b/doc/lfun/RemoveResistanceModifier
@@ -0,0 +1,37 @@
+RemoveResistanceModifier()
+
+FUNKTION:
+     varargs void RemoveResistanceModifier(string add);
+
+DEFINIERT IN:
+     /std/living/combat.c
+
+ARGUMENTE:
+     string add:
+      Ein eventueller Identifikator fuer einen gesetzten Modifikator.
+
+BESCHREIBUNG:
+     Die von einem Objekt im Zielobjekt gesetzte Resistenz wird geloescht,
+     der Schluessel add wird dazu benutzt, eine bestimmte Resistenz zu
+     loeschen (so kann ein setzendes Objekt mehrere verschiedene Re-
+     sistenzen setzen und selektiv loeschen).
+
+BEISPIELE:
+     // unser Oel aus AddResistanceModifier() verbrennt endgueltig
+     varargs void trigger_sensitive_attack() {
+      ...
+      if(environment() && living(environment())) {
+       environment()->RemoveResistanceModifier("oel");
+       tell_object(environment(),"Das Oel verbrennt endgueltig.\n");
+      }
+      remove();
+     }
+
+SIEHE AUCH:
+     Modifikatoren:	AddResistanceModifier, P_RESISTANCE_MODIFIER
+     simple Resistenz:	P_RESISTANCE, P_VULNERABILITY
+     Hauptmapping:	P_RESISTANCE_STRENGTHS
+     Berechnung:	CheckResistance(), UpdateResistanceStrengths()
+     anderes:		balance, /std/armour/combat.c, /std/living/combat.c
+
+29.Apr 2002, Gloinson@MG
diff --git a/doc/lfun/RemoveRoute b/doc/lfun/RemoveRoute
new file mode 100644
index 0000000..e301719
--- /dev/null
+++ b/doc/lfun/RemoveRoute
@@ -0,0 +1,23 @@
+RemoveRoute()
+
+FUNKTION:
+     void RemoveRoute();
+
+DEFINIERT IN:
+     /std/transport.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Der Transporter wird angehalten und der gesamte Fahrplan wird
+     geloescht. Damit lassen sich zum Beispiel variable Routen konstruieren.
+
+RUeCKGABEWERT:
+     keiner
+
+SIEHE AUCH:
+     AddRoute(), AddMsg(), AddFun(), /std/transport.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:24:26 1996 by Wargon
diff --git a/doc/lfun/RemoveSensitiveObject b/doc/lfun/RemoveSensitiveObject
new file mode 100644
index 0000000..5f6e0dc
--- /dev/null
+++ b/doc/lfun/RemoveSensitiveObject
@@ -0,0 +1,56 @@
+RemoveSensitiveObject()
+FUNKTION:
+     void RemoveSensitiveObject(object ob)
+
+DEFINIERT IN:
+     /std/container/inventory.c
+     generalizes /std/living/inventory.c
+
+ARGUMENTE:
+     ob - zu entfernendes Objekt
+
+BESCHREIBUNG:
+     Entfernt ob aus den Benachrichtigungslisten des Containers.
+     Wird von thing/moving.c im alten Environment gerufen, wenn
+     P_SENSITIVE gesetzt ist.
+     Ruft dazu RemoveSensitiveObjectFromList().
+
+BEMERKUNGEN:
+     Setzt man P_SENSITIVE nicht als Default sondern situationsabhaengig,
+     dann muss man auch RemoveSensitiveObject im Environment
+     auch selbst rufen!
+
+BEISPIEL:
+     // Fackel (inheriting lightsource)
+     void create() {
+     ...
+       SetProp(P_SENSITIVE,
+        ({({SENSITIVE_INVENTORY_TRIGGER,DT_FIRE,120})}));
+     ...
+     }
+
+     // wenn die Fackel geloescht wird, verliert sie ihre aktive
+     // Eigenschaft und muss das dem environment() mitteilen
+     static int extinguish(string str) {
+      int i;
+      i=::extinguish(str);
+      if(i && QueryProp(P_LIGHT)<=0) {
+       SetProp(P_SENSITIVE,0);
+       if(environment())
+        environment()->RemoveSensitiveObject(this_object());
+      }
+      return i;
+     }
+
+     - empfindliche Objekte wie Eiszapfen koennen jetzt wieder gefahrlos
+       in das selbe environment() bewegt werden
+
+SIEHE AUCH:
+     P_SENSITIVE
+     InsertSensitiveObject
+     insert_sensitive_inv_trigger, insert_sensitive_inv
+     P_SENSITIVE_ATTACK, P_SENSITIVE_INVENTORY, P_SENSITIVE_INVENTORY_TRIGGER
+     CheckSensitiveAttack
+
+25.Apr.2001, Gloinson@MG
+
diff --git a/doc/lfun/RemoveSingularId b/doc/lfun/RemoveSingularId
new file mode 100644
index 0000000..016999c
--- /dev/null
+++ b/doc/lfun/RemoveSingularId
@@ -0,0 +1,28 @@
+RemoveSingularId()
+
+FUNKTION:
+     void RemoveSingularId(mixed id);
+
+DEFINIERT IN:
+     /std/unit.c
+
+ARGUMENTE:
+     id
+          Identifikationsstring oder Array von Strings
+
+BESCHREIBUNG:
+     Es werden ein oder mehrere Bezeichner entfernt, mit denen sich eine
+     einzelne Einheit ansprechen laesst.
+
+RUECKGABEWERT:
+     keiner
+
+BEISPIELE:
+     siehe /items/money.c
+
+SIEHE AUCH:
+     AddSingularId(), RemovePluralId(), AddId(), id(), /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Mon Jul 14 11:31:00 1997 by Silvana
+
diff --git a/doc/lfun/RemoveSkillAttributeModifier b/doc/lfun/RemoveSkillAttributeModifier
new file mode 100644
index 0000000..71dfd10
--- /dev/null
+++ b/doc/lfun/RemoveSkillAttributeModifier
@@ -0,0 +1,59 @@
+RemoveSkillAttributeModifier()
+FUNKTION:
+    public int RemoveSkillAttributeModifier(object caster, string atrname)
+
+DEFINIERT IN:
+    /std/living/skill_attributes.c
+
+ARGUMENTE:
+    <atrname>   string
+                Name des Skill-Attributes, von dem der Modifikator geloescht
+                werden soll.
+                (Definiert in /sys/living/skill_attributes.h)
+
+    <caster>    object
+                Objekt, dessen Modifikator wieder entfernt werden soll.
+
+BESCHREIBUNG:
+    Entfernt den Modifikator, den Object <caster> gesetzt hat, wieder. Dies
+    ist nur notwendig, wenn der Effekt vor Ablauf der Gueltigkeit des
+    Modifikators aufgehoben werden soll.
+
+RUECKGABEWERT:
+    SA_MOD_REMOVED         wenn der Modifikator geloescht wurde
+    SA_MOD_NOT_FOUND       wenn der Modifikator nicht gefunden wurde
+    Wenn man nur wissen will, ob die Operation erfolgreich war, empfiehlt es
+    sich, auf == SA_MOD_REMOVED zu pruefen.
+
+BEISPIELE:
+    // eine Waffe setzt im InformWield() einen Bonus auf SA_DAMAGE fuer 10min
+    protected void InformWield(object pl, int silent) {
+      if (objectp(pl)) {
+        if (pl->ModifySkillAttribute(SA_DAMAGE, 20, 600) == SA_MOD_OK)
+          // Erfolgsmeldung an Spieler
+        else
+          // Misserfolgsmeldung an Spieler.
+      }
+    }
+
+    // wenn der Spieler die Waffe vor Ablauf der 600s wegstecken will, muss
+    // der Bonus natuerlich auch wieder raus
+    protected void InformUnwield(object pl, int silent) {
+      if (objectp(pl))
+        pl->RemoveSkillAttributeModifier(this_object(), SA_DAMAGE);
+        // falls kein solcher Mod mehr gesetzt war, liefert RSAM()
+        // SA_MOD_NOT_FOUND zurueck. Auswertung des Rueckgabewertes ist
+        // vernachlaessigt.
+    }
+    
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung, skill_info_liste
+    * Properties:   P_NEWSKILLS
+
+13.08.2008, Zesstra
diff --git a/doc/lfun/RemoveSmells b/doc/lfun/RemoveSmells
new file mode 100644
index 0000000..cc72207
--- /dev/null
+++ b/doc/lfun/RemoveSmells
@@ -0,0 +1,38 @@
+RemoveSmells()
+
+FUNKTION:
+    void RemoveSmells(string|string* keys);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+      String oder Array von Strings mit den zu entfernenden Details.
+
+BESCHREIBUNG:
+    Diese Funktion entspricht dem RemoveDetail() fuer Standarddetails,
+    nur koennen hiermit Gerueche entfernt werden:
+    Entfernt die in <keys> angegebenen Details aus der Liste der
+    vorhandenen Details. Uebergibt man fuer <keys> eine 0, so werden
+    saemtliche Details entfernt!
+
+BEISPIEL:
+    Zuerst erzeugen wir ein kleines Detail, mit dem wir am Rauch riechen
+    koennen. Das Feuer brennt langsam ab und das Detail wird wieder
+    entfernt:
+
+      AddSmells("rauch",* H U S T *\n");
+      call_out(#'RemoveSmells, 100, "rauch");
+
+SIEHE AUCH:
+    Setzen:    AddDetail(), AddReadDetail(), AddSmells(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveDetail(), RemoveReadDetail(), RemoveSmells(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS, P_TOUCH_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/RemoveSounds b/doc/lfun/RemoveSounds
new file mode 100644
index 0000000..50d900a
--- /dev/null
+++ b/doc/lfun/RemoveSounds
@@ -0,0 +1,46 @@
+RemoveSounds()
+
+FUNKTION:
+    void RemoveSounds(string|string* keys);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+      String oder Array von Strings mit den zu entfernenden Details.
+
+BESCHREIBUNG:
+    Diese Funktion entspricht dem RemoveDetail() fuer Standarddetails,   
+    nur koennen hiermit Gerauesche entfernt werden:
+    Entfernt die in <keys> angegebenen Details aus der Liste der
+    vorhandenen Details. Uebergibt man fuer <keys> eine 0, so werden
+    saemtliche Details entfernt!
+
+BEISPIEL:
+    Wir lassen etwas Musik spielen und wenn wir den Plattenspieler
+    ausmachen, dann endet sie und man hoert auch nichts mehr:
+
+      int ausmachen(string str);
+      ...
+      AddSounds(({"musik","hip-hop"}) ,"Klingt nach Hip-Hop...\n");
+      AddCmd("mach|mache&plattenspieler&aus", #'ausmachen,
+             "Was willst du (aus)machen?|Willst du den ausmachen?^");
+      ...
+      int ausmachen(string str) {
+        if(!GetDetail("musik", 0, SENSE_SOUND)) // existiert Musikdetail ?
+          return("Der Plattenspieler laeuft doch gar nicht!\n", 1);
+        RemoveSounds(0);
+         return 1;
+      }
+
+SIEHE AUCH:
+    Setzen:    AddDetail(), AddReadDetail(), AddSmells(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveDetail(), RemoveReadDetail(), RemoveSmells(),
+               RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS, P_TOUCH_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+8. Juli 2011 Gloinson
diff --git a/doc/lfun/RemoveSpecialDetail b/doc/lfun/RemoveSpecialDetail
new file mode 100644
index 0000000..ebec4cb
--- /dev/null
+++ b/doc/lfun/RemoveSpecialDetail
@@ -0,0 +1,37 @@
+VERALTET: RemoveSpecialDetail()
+
+FUNKTION:
+    void RemoveSpecialDetail(string|string* keys);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+      String oder Array von Strings mit den zu entfernenden Details.
+
+BESCHREIBUNG:
+    Entfernt die in keys angegebenen Details aus der Liste der
+    vorhandenen SpecialDetails.
+
+    VERALTET: Bitte RemoveDetail() benutzen.
+
+BEMERKUNGEN:
+    Uebergibt man fuer keys eine 0, so werden saemtliche SpecialDetails
+    entfernt!
+    Da intern Details und SpecialDetails im gleichen Mapping verwaltet
+    werden, lassen sich mit dieser Funktion auch Details entfernen.
+    Man sollte diese Funktion deshalb nicht mehr verwenden, siehe
+    AddDetail mit Closures.
+
+SIEHE AUCH:
+    Setzen :   AddDetail(), AddReadDetail(), AddSmells(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveDetail(), RemoveReadDetail(), RemoveSmells(),
+               RemoveSounds(), RemoveTouchDetail()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS, P_TOUCH_DETAILS
+    Veraltet:  AddSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/RemoveSpecialExit b/doc/lfun/RemoveSpecialExit
new file mode 100644
index 0000000..69a18e4
--- /dev/null
+++ b/doc/lfun/RemoveSpecialExit
@@ -0,0 +1,29 @@
+RemoveSpecialExit()
+FUNKTION:
+     void RemoveSpecialExit(string|string* cmd);
+
+DEFINIERT IN:
+     /std/room/exits
+
+ARGUMENTE:
+     string/string* cmd
+          Richtung(en), die entfernt werden sollen.
+
+BESCHREIBUNG:
+     Die in cmd angegebenen Ausgaenge werden wieder entfernt.
+
+     Ist cmd = 0, so werden alle Ausgaenge entfernt.
+
+BEMERKUNGEN:
+     Ausgaenge werden an der gleichen Stelle gespeichert:
+     - RemoveSpecialExit() greift auf die gleichen Daten wie RemoveExit()
+       zu, entfernt also auch normale Ausgaenge!
+
+SIEHE AUCH:
+     AddExit(), AddSpecialExit(), AddGuardedExit(), GetExits()
+     RemoveExit(),
+     P_EXITS, P_HIDE_EXITS, /std/room/exits.c
+     ausgaenge
+
+31.01.2015, Zesstra
+
diff --git a/doc/lfun/RemoveTouchDetail b/doc/lfun/RemoveTouchDetail
new file mode 100644
index 0000000..8b816a4
--- /dev/null
+++ b/doc/lfun/RemoveTouchDetail
@@ -0,0 +1,54 @@
+RemoveTouchDetail()
+
+FUNKTION:
+    void RemoveTouchDetail(string|string* keys);
+
+DEFINIERT IN:
+    /std/thing/description.c
+
+ARGUMENTE:
+    keys
+      String oder Array von Strings mit den zu entfernenden Details.
+
+BESCHREIBUNG:
+    Diese Funktion entspricht dem RemoveDetail() fuer Standarddetails,
+    nur koennen hiermit (ab)tastbare Details entfernt werden:
+    Entfernt die in <keys> angegebenen Details aus der Liste der
+    vorhandenen Details. Uebergibt man fuer <keys> eine 0, so werden
+    saemtliche tastbaren/fuehlbaren Details entfernt!
+
+BEISPIEL:
+    Zuerst wird ein Detail "boden" erzeugt, das abgetastet werden kann.
+    Dieses kann durch Betaetigen eines Knopfes, aeh, entfernt werden.
+
+      int knopf();
+      void knopf2();
+
+      AddTouchDetail("boden", "Er ist aus Stein.\n");
+      AddCmd("drueck|druecke&knopf", #'knopf, "Was willst du druecken?");
+
+      void knopf() {
+        tell_room(this_object(), break_string(
+          this_player()->Name(WER)+" drueckt einen Knopf, der dabei satt "+
+          "klackt.", 78));
+
+        if(find_call_out(#'knopf2)<=0)
+          call_out(#'knopf2, 1);
+      }
+      
+      void knopf2() {
+        tell_room(this_object(), "Uhoh. Der Boden klappt weg. Du faellst.");
+        RemoveTouchDetails("boden");
+      }
+
+SIEHE AUCH:
+    Setzen:    AddDetail(), AddReadDetail(), AddSmells(), AddSounds(),
+               AddTouchDetail()
+    Loeschen:  RemoveDetail(), RemoveReadDetail(), RemoveSmells(),
+               RemoveSounds()
+    Daten:     P_DETAILS, P_READ_DETAILS, P_SMELLS, P_SOUNDS, P_TOUCH_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail(), P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/ResizeRingBuffer b/doc/lfun/ResizeRingBuffer
new file mode 100644
index 0000000..c5a6c08
--- /dev/null
+++ b/doc/lfun/ResizeRingBuffer
@@ -0,0 +1,44 @@
+ResizeRingBuffer()
+
+FUNKTION:
+    protected struct std_ringbuffer buf RingBufferPut(
+                                                   struct std_ringbuffer buf, 
+                                                   int size);
+
+DEFINIERT IN:
+    /std/util/ringbuffer.c
+    /sys/util/ringbuffer.h
+    
+ARGUMENTE:
+    buf  - Ringpuffer, dessen Groesse geaendert werden soll
+    size - neue Groesse (int) 
+
+BESCHREIBUNG:
+    Diese Funktion erstellt einen neuen Ringpuffer der Groesse <size>, welcher
+    den gleichen Modus hat wie <buf> und die gleichen Daten enthaelt.
+    Ist der neue Puffer kleiner als <buf>, so kommt es hierbei zu
+    Datenverlust.
+    <buf> wird nicht veraendert. Ist die Groesse von <buf> gleich der
+    neuen gewuenschten Groesse, wird letztendlich der Ringpuffer kopiert. 
+    Je nach Groesse von <buf> und Wert von <size> kann dies eine teure
+    Angelegenheit sein.
+
+RUeCKGABEWERT:
+    Der neue Ringpuffer mit Groesse <size>.
+
+BEISPIELE:
+    // Ringpuffer anlegen:
+    struct std_ringbuffer buffer = CreateRingBuffer(5, MODE_FIFO);
+    // 5 Werte reinschreiben:
+    foreach(int i: 5) RingBufferPut(buffer, i);
+    // Groesse aendern
+    buffer = ResizeRingBuffer(buffer, 10);
+    // Daten als Array ermitteln:
+    mixed array = RingBufferGet(buffer);
+    // array enthaelt: ({0,0,0,0,0,0,1,2,3,4})
+
+SIEHE AUCH:
+    RingBufferGet(), RingBufferPut(), CreateRingBuffer()
+
+23.05.2008, Zesstra
+
diff --git a/doc/lfun/RingBufferGet b/doc/lfun/RingBufferGet
new file mode 100644
index 0000000..8d6b467
--- /dev/null
+++ b/doc/lfun/RingBufferGet
@@ -0,0 +1,41 @@
+RingBufferGet()
+
+FUNKTION:
+    protected mixed RingBufferGet(struct std_ringbuffer buf);
+
+DEFINIERT IN:
+    /std/util/ringbuffer.c
+    /sys/util/ringbuffer.h
+
+ARGUMENTE:
+    buf - Ringpuffer, welcher ausgeben werden soll
+
+BESCHREIBUNG:
+    Diese Funktion liefert die Daten des Ringpuffers in Form eines Arrays
+    zurueck, welches dann weiterverarbeitet werden kann.
+    Die Reihenfolge der Daten ist entsprechend des beim Anlegen des
+    Ringpuffers angebenen Modes:
+    MODE_FIFO: aelteste Daten zuerst
+    MODE_LIFO: neueste Daten zuerst
+
+BEMERKUNGEN:
+    Aenderungen am Array beeinflussen die Daten des Ringpuffers nicht. Aber:
+    Hierbei werden die Daten nicht tief kopiert. D.h. enthaelt der Ringpuffer
+    Referenzen auf weitere Daten, zeigen der Ringpuffer und das hier
+    gelieferte Array auf die gleichen Daten.
+
+BEISPIELE:
+    // Ringpuffer anlegen:
+    struct std_ringbuffer buffer = CreateRingBuffer(10, MODE_FIFO);
+    // 15 Werte reinschreiben:
+    foreach(int i: 15) RingBufferPut(buffer, i);
+    // Werte abrufen:
+    mixed array=RingBufferGet(buffer);
+    // array enthaelt nun:
+    // ({5,6,7,8,9,10,11,12,13,14}) 
+
+SIEHE AUCH:
+    CreateRingBuffer(), RingBufferPut(), ResizeRingBuffer()
+
+23.05.2008, Zesstra
+
diff --git a/doc/lfun/RingBufferPut b/doc/lfun/RingBufferPut
new file mode 100644
index 0000000..82a78bb
--- /dev/null
+++ b/doc/lfun/RingBufferPut
@@ -0,0 +1,30 @@
+RingBufferPut()
+
+FUNKTION:
+    protected void RingBufferPut(struct std_ringbuffer buf, mixed val);
+
+DEFINIERT IN:
+    /std/util/ringbuffer.c
+    /sys/util/ringbuffer.h
+
+ARGUMENTE:
+    buf - Ringpuffer, in den <val> geschrieben werden soll
+    val - neues Datum
+
+BESCHREIBUNG:
+    Diese Funktion schreibt <val> in den Ringpuffer <buf>. Hierbei wird ggf.
+    das aelteste im Puffer existierende Datum ueberschrieben. <buf> muss eine
+    von CreateRingBuffer() oder ResizeRingBuffer() zurueckgelieferter
+    Ringpuffer sein.
+
+BEISPIELE:
+    // Ringpuffer anlegen:
+    struct std_ringbuffer buffer = CreateRingBuffer(10, MODE_FIFO);
+    // 15 Werte reinschreiben:
+    foreach(int i: 15) RingBufferPut(buffer, i);
+
+SIEHE AUCH:
+    RingBufferGet(), CreateRingBuffer(), ResizeRingBuffer()
+
+23.05.2008, Zesstra
+
diff --git a/doc/lfun/SearchPath b/doc/lfun/SearchPath
new file mode 100644
index 0000000..035fdcb
--- /dev/null
+++ b/doc/lfun/SearchPath
@@ -0,0 +1,83 @@
+SearchPath
+
+FUNKTION:
+     public int SearchPath(string from, string to, int para,
+                           closure callback)
+
+DEFINIERT IN:
+     /p/daemon/pathd.c
+     <sys/path.d>
+
+ARGUMENTE:
+     from     - Der Startpunkt
+     to       - Der Endpunkt
+     para     - Die Parawelt in der gesucht wird (Normalwelt: 0)
+     callback - Closure, die am Ende der Pfadsuche gerufen wird
+
+BESCHREIBUNG:
+    Diese Funktion berechnet - sofern moeglich - den kuerzesten Pfad zwischen
+    <from> und <to> in der (Para-)Welt <para>.
+    
+    Die Pfadsuche wird anhand von Daten ueber von Spielern gelaufene Wege
+    durchgefuehrt. D.h. Gebiete, die von Spielern nicht (in letzter Zeit mal)
+    betreten werden, sind auch dem Pfaddaemon nicht bekannt. Auch kann es
+    Gebiete geben, wo zwar gebietsintern Pfade bekannt sind, aber keine Wege
+    in den Rest vom MG.
+
+    Da diese Suche im Allgemeinen SEHR aufwendig sein kann, wird sie meistens
+    nicht sofort fertig, sondern dauert eine Weile. Wenn sie fertig ist, wird
+    die Closure <callback> aufgerufen und ihr die Argumente <from>, <to>,
+    <parawelt>, <kosten>, <path> und <cmds> uebergeben. Die Bedeutung
+    dieser Argumente ist unten erklaert.
+
+    Eine Suche nach einem Pfad in einer Parawelt fuehrt durch Raeume der
+    gewuenschen Parawelt und durch Raeume der Normalwelt.
+
+RUeCKGABEWERT:
+     1      - Suche wurde gestartet
+     2      - Pfad gefunden (und callback gerufen)
+    -1      - es laeuft schon ein Suchauftrage fuer die UID des anfragenden
+              Objektes
+    -2      - es laufen bereits zuviele Suchanfragen
+    -3      - <from> und/oder <to> sind nicht bekannt
+    
+    An <callback> uebergebene Argumente am Ende der Pfadsuche:
+        <from> - Startpunkt des Weges (string)
+        <to>   - Zielpunkt des Weges (string)
+        <para> - Parawelt des Weges (int)
+        <costs>- Kosten des Wege. (int) Je hoeher, desto
+                 laenger/unguenstiger. 0, wenn kein Pfad gefunden
+        <path> - Array mit Raumnamen, die durchlaufen werden (string*)
+                 0, wenn kein Pfad gefunden
+        <cmds> - Array mit Kommandos zum Ablaufen des Weges (string*)
+                 0, wenn kein Pfad gefunden
+
+BEMERKUNGEN:
+   Es ist natuerlich nicht dazu gedacht, Spielern fertige Routen zwischen
+   Orten zu geben - bzw. nur in Ausnahmefaellen.
+   Pfadabfrgen werden geloggt.
+
+   Die Angabe <costs> sagt grob etwas ueber die Laenge und vor allem ueber die
+   "Qualitaet" des Pfades aus. Die steigt bei Paraweltwechseln, wenig
+   abgelaufenen Verbindungen zwischen Raeumen oder wenn eine Verbindung kein
+   normaler Ausgang ist.
+
+   Die Closure <callback> sollte nicht zuviele Ticks verbrauchen.
+
+BEISPIEL:
+   #include <pathd.h>
+   void suchergebnis(string from, string to, int para, int costs, string*
+        path, string* cmds) {
+        tell_object(find_player("zesstra"), sprintf(
+            "Ergebnis Pfadsuche von %s nach %s in Para %d fuer %d:\n %O\n %O\n",
+            from, to, para, costs, path, cmds));
+   };
+
+   ...
+   mixed res=PATHD->SearchPath("/gilden/abenteurer",
+                               "/d/ebene/dancer/lucky/room/pova_la3",
+                               0, #'suchergebnis);
+
+----------------------------------------------------------------------------
+22.12.2015, Zesstra
+
diff --git a/doc/lfun/SelectEnemy b/doc/lfun/SelectEnemy
new file mode 100644
index 0000000..863e0c0
--- /dev/null
+++ b/doc/lfun/SelectEnemy
@@ -0,0 +1,32 @@
+SelectEnemy()
+
+FUNKTION:
+	varargs object SelectEnemy(object*here);
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	here
+	  Gegner, aus welchen ausgewaehlt werden soll (optional!)
+
+RUeCKGABEWERT:
+	Ausgewaehlter Gegner, der angegriffen werden soll.
+
+BESCHREIBUNG:
+	Diese Funktion waehlt einen Gegner aus, der angegriffen werden soll.
+	Werden keine Gegner im Parameter <here> angegeben, so wird der
+	Rueckgabewert der Funktion PresentEnemies() verwandt, welche die
+	aktuell anwesenden Gegner im Raum liefert.
+	Sind keine Gegner anwesend, so wird 0 zurueckgeliefert.
+	Danach wird geschaut, ob Gegner bevorzugt ausgewaehlt werden sollen.
+	Dies geschieht mittels der Funktion QueryPreferedEnemy().
+	Auch dieser bevorzugte Gegner muss im selben Raum sein! Wenn nicht,
+	wird doch irgendein anderer Gegner ausgewaehlt und zurueckgegeben.
+
+SIEHE AUCH:
+	PresentEnemies(), QueryPreferedEnemy(), P_PREFERED_ENEMY,
+	InsertEnemy(), StopHuntFor()
+
+----------------------------------------------------------------------------
+Last modified: Thu May 27 15:01:48 1999 by Patryn
diff --git a/doc/lfun/SelectFarEnemy b/doc/lfun/SelectFarEnemy
new file mode 100644
index 0000000..d574dcc
--- /dev/null
+++ b/doc/lfun/SelectFarEnemy
@@ -0,0 +1,45 @@
+
+SelectFarEnemy()
+
+
+FUNKTION:
+        varargs object SelectFarEnemy(object *here, int min, int max,
+                                      int forcefrom)
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        here       - Rueckgabewert von PresentEnemies()
+        min        - minimale Kampfreihe
+        max        - maximale Kampfreihe
+        forcefrom  - Gegner MUSS aus uebergebenem Array ausgewaehlt werden
+
+BESCHREIBUNG:
+        Waehlt einen Feind aus Reihe <min> bis <max> aus.
+
+RUECKGABEWERT:
+        Der Auserwaehlte :-)
+
+BEMERKUNGEN:
+        1. Wenn in den angegeben Reihen kein Feind ist oder <max> kleiner
+           als <min> ist, wird auch keiner zurueckgegeben.
+        2. Aus Effizienzgruenden sollte forcefrom nur benutzt werden, wenn
+           es wirklich noetig ist (s. hierzu auch SelectNearEnemy()).
+        3. 0 <= <min> <= <max> < MAX_TEAMROWS
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/SelectNearEnemy b/doc/lfun/SelectNearEnemy
new file mode 100644
index 0000000..998bee8
--- /dev/null
+++ b/doc/lfun/SelectNearEnemy
@@ -0,0 +1,49 @@
+
+SelectNearEnemy()
+
+
+FUNKTION:
+        varargs object SelectNearEnemy(object *here, int forcefrom)
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        here      - Rueckgabewert von PresentEnemies()
+        forcefrom - Gegner MUSS aus uebergebenem Array ausgewaehlt werden
+
+BESCHREIBUNG:
+        Waehlt einen im Nahkampf erreichbaren Feind aus
+
+RUECKGABEWERT:
+        Der Auserwaehlte :-)
+
+BEMERKUNGEN:
+        1. Falls der Spieler in einem Team ist und in einer hinteren Reihe
+           steht, wird kein Feind ausgewaehlt, es sei denn, der Spieler hat
+           einen Kampf mit einem Teammitglied angefangen.
+        2. Wenn ein bevorzugter Feind in einer der hinteren Reihen eines
+           Teams steht, wird solange das Team bevorzugt.
+        3. Auch Feinde in den hinteren Reihen koennen im Nahkampf erreichbar
+           sein, wenn die vorderen Reihen nicht genuegend Deckung bieten.
+        4. ACHTUNG: Der Auserwaehlte kommt nicht notwendigerweise aus dem
+           uebergebenen Array, wenn forcefrom=0 ist. Normalerweise ist dieses
+           Verhalten beabsichtigt, damit jemand, der sich in der Reihe vor
+           einen Gegner stellt, angegriffen wird, auch wenn er noch nicht
+           Feind ist.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/Set b/doc/lfun/Set
new file mode 100644
index 0000000..42e6930
--- /dev/null
+++ b/doc/lfun/Set
@@ -0,0 +1,137 @@
+Set()
+FUNKTION:
+     public varargs mixed Set(string name, mixed Value, int Type, int extern);
+
+DEFINIERT IN:
+     /std/thing/properties.c
+     /sys/thing/properties.h (Prototyp)
+
+ARGUMENTE:
+     name - Property, die manipuliert werden soll
+     Value - der zu setzende/aendernde Wert
+     Type - die Eigenschaft der Property, die manipuliert werden soll
+     extern - Interne Verwendung:
+    Wurde Set ueber SetProp von aussen gerufen.
+
+BESCHREIBUNG:
+     Eine der inneren Eigenschaften der Property 'name' wird veraendert.
+     'Type' ist dabei einer der in /sys/thing/properties.h definierten
+     F_XXX - Werte:
+
+     F_VALUE (==0, default)
+       Setzt den Inhalt der Property auf 'Value'. Aehnlich "SetProp",
+       umgeht jedoch eine etwaige F_SET_METHOD oder _set_'name'()-Methode.
+     F_MODE
+     F_MODE_AS
+     F_MODE_AD
+       Aendert eines der internen Flags der Property. F_MODE negiert den
+       vorherigen Wert vom Flag 'Value', F_MODE_AS setzt und F_MODE_AD
+       loescht ihn.
+       Verfuegbare interne Flags:
+         SAVE 
+           Property wird bei save_object() gespeichert
+         PROTECTED 
+           Flag setzbar durch:   beliebiges Objekt
+           Flag loeschbar durch: this_object(), ROOT, EM+
+           Inhalt der Property veraendern sowie Set- und Query-Methoden
+           setzen oder loeschen duerfen nur noch this_object(), ROOT, EM+
+           WARNUNG: Dieses Flag nicht leichtfertig bei Spielern setzen!
+         SECURED  
+           Flag setzbar durch:   this_object(), ROOT, EM+
+           Flag loeschbar durch: Niemanden!
+           Inhalt der Property veraendern sowie Set- und Query-Methoden
+           setzen oder loeschen duerfen nur noch this_object(), ROOT, EM+
+         NOSETMETHOD 
+           Property nicht mehr ueber SetProp() aenderbar
+           (damit entfallen auch SET_METHOD, _set_'name')
+     F_SET_METHOD
+       Aendert den Eintrag fuer eine SetMethod - eine Closure, die anstatt
+       des Setzens der Property beim Aufruf von SetProp mit 'Value'
+       aufgerufen wird.
+     F_QUERY_METHOD
+       Aendert den Eintrag fuer eine QueryMethod - eine Closure, die anstatt
+       des Lesens der Property beim Aufruf von QueryProp aufgerufen wird.
+
+RUeCKGABEWERT:
+     Das Ergebnis der Manipulation bzw. einer der definierten
+     Fehlercodes.
+
+BEMERKUNGEN:
+     - Set() sollte nicht zum regulaeren Manipulieren des Inhalts einer
+       Property verwendet werden, da sowohl F_SET_METHOD als auch libinterne
+       _set_'name'()-Methoden umgangen werden und das Ergebnis fuer so
+       veraenderte Properties undefiniert ist.
+     - eine gesetzte F_SET/F_QUERY_METHOD hat bei SetProp/QueryProp Vorrang
+       vor einer _set/_query_method
+       -> _set/_query wird nach erfolgreichem Ruf von F_XXX_METHOD ignoriert
+     - F_SET/F_QUERY_METHOD sollte normalerweise Vorzug vor einer
+       _set_/_query_* gegeben werden.
+
+    SetMethods/QueryMethods:
+     - falls ihr Query/SetMethods an oeffentlichen Properties setzen
+       wollt, prueft bitte vorher, ob dort nicht schon eine (fremde) gesetzt
+       ist und brecht ggf. euer Set() ab, um nicht die Arbeit anderer
+       Mitmagier zu sabotieren (z.B. P_HANDS)
+     - Properties sollten mit Query- oder SetMethoden nur so lange wie
+       noetig belegt werden
+       -> manche Properties sollte man als Wert setzen, _auch wenn_ die
+          Spieler sie zuruecksetzen koennten (zB P_MSGIN/P_MSGOUT)
+     - auf keinen Fall den Wert speichern, ueberschreiben, rueckschreiben,
+       das fuehrt fast immer zu Problemen.
+     - Set/QueryMethoden sollten nicht als Trigger/Listener fuer ein
+       Ereignis (z.B. P_DIE_MSG fuer das Ereignis "Tod des Spielers")
+       missbraucht werden
+       -> es gibt sichere und saubere Moeglichkeiten (NotifyPlayerDeath),
+          und wenn nicht, wendet euch an den EM eures Vertrauens
+     - F_SET/F_QUERY_METHODs koennen 'protected' (empfohlen) oder 'static'
+       sein. _set_/_query_ duerfen momentan _nicht_ 'protected' sein, fuer
+       geht nur 'static' (in diesem Fall empfohlen).
+
+BEISPIELE:
+     ### Aendern von Eigenschaften einer Property ###
+     // Setzen des SAVE-Flags (bei save_object() mitzuspeichern)
+     Set(P_XYZ, SAVE, F_MODE_AS);
+
+     // Loeschen des SAVE-Flags
+     Set(P_XYZ, SAVE, F_MODE_AD);
+     
+     // Negieren des bisherigen SAVE-Flags
+     Set(P_XYZ, SAVE, F_MODE);
+     // Hinweis: das Setzen des Flags funktioniert mittels F_MODE nur dann,
+     // wenn sichergestellt ist, dass es vorher nicht gesetzt war. Die 
+     // sichere Variante ist daher, F_MODE_AS zu verwenden.
+
+     // Sperren des SetProp/SET_METHOD-Zugriffs:
+     Set(P_XYZ, NOSETMETHOD, F_MODE_AS);
+
+     // Vorlaeufiger Zugriffsschutz fuer eine Property:
+     Set(P_XYZ, PROTECTED, F_MODE_AS);
+
+     // Permanenter Zugriffsschutz fuer eine Property:
+     Set(P_XYZ, SECURED, F_MODE_AS);
+
+     ### Setzen einer SetMethod/QueryMethod ###
+     // Setzen einer internen SetMethod
+     mixed foo(mixed val);
+     ...
+     Set(P_XYZ, #'foo, F_SET_METHOD);
+     ...
+
+     // Setzen einer QueryMethod bei einem anderen Objekt
+     mixed bar();
+     ...
+     other->Set(P_XYZ, #'bar, F_QUERY_METHOD);
+     ...
+
+     // Der Vollstaendigkeit halber sei das Aendern einer Property unter
+     // Umgehung von Set-Methoden angegeben. Es ist aber aus o.g. Gruenden
+     // zu empfehlen, diese Variante nicht zu verwenden.
+     Set(P_XYZ, "bla", F_VALUE);
+
+SIEHE AUCH:
+     Aehnliches: SetProp(L), QueryProp(L), Query(L)
+     Generell:  SetProperties(L), QueryProperties(L)
+     Konzept:  properties, /std/thing/properties.c
+     Sonstiges:  P_AUTOLOADOBJ
+
+06. Jan 2008 Arathorn
diff --git a/doc/lfun/SetAttackChats b/doc/lfun/SetAttackChats
new file mode 100644
index 0000000..87203a1
--- /dev/null
+++ b/doc/lfun/SetAttackChats
@@ -0,0 +1,68 @@
+SetAttackChats()
+
+FUNKTION:
+     void SetAttackChats(int chance, mixed strs);
+
+DEFINIERT IN:
+     /std/npc/chat.c
+
+ARGUMENTE:
+     chance
+          Prozentuale Wahrscheinlichkeit einer Ausgabe
+     strs
+          Stringarray mit den Monsterchats
+
+BESCHREIBUNG:
+     Der NPC gibt mit der Wahrscheinlichkeit 'chance' einen zufaellig
+     gewaehlten Text aus 'strs' von sich. Die Arrayelemente koennen 
+     auch Funktionen ("@@func@@") oder Closures enthalten, die dann
+     aufgerufen werden und deren Rueckgabewerte das Monster dann ausgibt.
+
+RUECKGABEWERT:
+     keiner
+     
+BEMERKUNGEN:
+     AttackChats werden nur im Kampf ausgegeben. Ansonsten ist SetChats()
+     zu verwenden.
+     
+     'chance' wird in der Property P_ACHAT_CHANCE abgelegt. Um einen NPC
+     voruebergehend 'stillzulegen', kann man P_ACHAT_CHANCE auf 0 setzen.
+     
+     Man kann AttackChats nutzen, um Monsterspells zu realisieren. Besser
+     ist es aber, dafuer 'AddSpell' zu verwenden.
+     
+BEISPIELE:
+
+     SetAttackChats( 20,
+       ({ "Der Ork tritt Dir in den Hintern.\n",
+          "Der Ork bruellt: Lebend kommst Du hier nicht raus!\n",
+          "Der Ork blutet schon aus mehreren Wunden.\n",
+          "@@knirsch@@" }) );
+          
+     string knirsch()
+     {
+       object *ruestung, *tmp, helm;
+       
+       ruestung = this_player()->QueryProp(P_ARMOURS); // getragene Ruestung
+       tmp = filter_objects(ruestung, "id", AT_HELMET);
+       // Wenn der Spieler einen Helm traegt, enthaelt 'tmp' jetzt
+       // ein Array mit dem Helmobjekt als einzigem Element
+       if (sizeof(tmp)) helm = tmp[0];
+	 if (objectp(helm))
+	 {
+	   // AC nur dann senken, wenn sie nicht schon 0 ist.
+	   if (helm->QueryProp(P_AC)>0) {
+	     helm->Damage(1);
+	     return "Der Ork beschaedigt Deinen Helm!\n";
+	   }
+	   return "";
+	 }
+	 else
+	   return ""; // keine Meldung
+     }
+
+SIEHE AUCH:
+     P_ACHAT_CHANCE, SetChats(), /std/npc/chat.c, AddSpell()
+
+LETZTE AENDERUNG:
+	Don, 27.12.2007, 16:35:00 von Arathorn
diff --git a/doc/lfun/SetAttr b/doc/lfun/SetAttr
new file mode 100644
index 0000000..8ecf381
--- /dev/null
+++ b/doc/lfun/SetAttr
@@ -0,0 +1,31 @@
+SetAttr()
+FUNKTION:
+     private static int SetAttr(string attr, int val)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+
+ARGUMENTE:
+     attr       - zu setzendes Attribut
+     val        - Wert
+
+BESCHREIBUNG:
+     Filtert den Wert durch _filterattr(). Dadurch werden STR, INT, CON, DEX
+     auf 20 begrenzt. Setzt dann das Attribut.
+     Offsets werden hier nicht beruecksichtigt, QueryAttribute() gibt also
+     val + etwaige Offsets/Modifier zurueck.
+
+RUeCKGABEWERT:
+     Tatsaechlich gesetzter Wert.
+
+BEMERKUNGEN:
+     Wird von SetAttribute() und SetRealAttribute() aufgerufen.
+
+SIEHE AUCH:
+     QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+     SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+     TestAttributeLock(),

+     P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_ATTRIBUTES_MODIFIER,

+     P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+
+13.Nov.2002, Gloinson
\ No newline at end of file
diff --git a/doc/lfun/SetAttribute b/doc/lfun/SetAttribute
new file mode 100644
index 0000000..bab54f2
--- /dev/null
+++ b/doc/lfun/SetAttribute
@@ -0,0 +1,28 @@
+SetAttribute()
+FUNKTION:
+     int SetAttribute(string attr, int val)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+
+ARGUMENTE:
+     attr       - zu setzendes Attribut
+     val        - Wert
+
+BESCHREIBUNG:
+     Nimm val als Gesamtwert des Attributes, d.h. zieht etwaige Offsets vor
+     Setzen ab. (QueryAttribute gibt dann also val zurueck)
+
+RUeCKGABEWERT:
+     Den durch SetAttr gefilterten gesetzten Wert.
+     Bitte nicht auf Spieler anwenden!
+
+SIEHE AUCH:

+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+	SetTimedAttrModifier(), QueryTimedAttrModifier(), 

+	DeleteTimedAttrModifier(),

+	P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_TIMED_ATTR_MOD,

+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+----------------------------------------------------------------------------

+Last modified: Tue Jul 27 20:00:20 2004 by Muadib

diff --git a/doc/lfun/SetBuyFact b/doc/lfun/SetBuyFact
new file mode 100644
index 0000000..b5154c7
--- /dev/null
+++ b/doc/lfun/SetBuyFact
@@ -0,0 +1,36 @@
+SetBuyFact()
+
+FUNKTION:
+     void SetBuyFact(int fact);
+
+DEFINIERT IN:
+     /std/laden.c
+
+ARGUMENTE:
+     fact
+          Der Einkaufspreismultiplikator.
+
+BESCHREIBUNG:
+     Der Preis, der beim Kauf eines Objekts im Laden zu entrichten ist,
+     errechnet sich aus seinem Wert ( P_VALUE), multipliziert mit dem Faktor
+     fact. Dieser Preis ist konstant und nicht von der aktuellen Marktlage
+     abhaengig.
+
+     Der Defaultwert ist 3. Ein hoeherer Wert entspricht einem teuren Laden,
+     waehrend ein kleinerer Wert zu kleinen Preisen fuehrt.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Normalerweise kommt man ohne diese Funktion aus: in teuren Laeden wird
+     nicht viel eingekauft (es sei denn, es liegen standardmaessig gute
+     Objekte im Speicher), einem Billigladen wird bald das Geld ausgehen mit
+     der Folge, dass der Ladenbesitzer nichts mehr ankaufen kann und sein
+     Lager gaehnend leer ist.
+
+SIEHE AUCH:
+     QueryBuyFact(), /std/laden.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:24:52 1996 by Wargon
diff --git a/doc/lfun/SetChats b/doc/lfun/SetChats
new file mode 100644
index 0000000..3f425a8
--- /dev/null
+++ b/doc/lfun/SetChats
@@ -0,0 +1,69 @@
+SetChats()
+
+FUNKTION:
+	void SetChats(int chance,mixed strs);
+
+DEFINIERT IN:
+	/std/npc/chat.c
+
+ARGUMENTE:
+	chance
+	  Prozentuale Wahrscheinlichkeit einer Ausgabe
+	strs
+	  Stringarray mit den Monsterchats
+
+BESCHREIBUNG:
+	Der NPC gibt mit der Wahrscheinlichkeit <chance> pro Heartbeat einen
+	zufaellig gewaehlten Text aus dem Array <strs> von sich.
+	Die Arrayelemente koennen auch Closures oder
+	process_string()-Funktionen ("@@func@@") enthalten, die dann
+	aufgerufen werden und deren Rueckgabewerte das Monster dann ausgibt.
+	 (Fuer keine Ausgabe dann Leerstring "" zurueckgeben!)
+	In diesen Funktionen ist this_player() das Monster selbst!
+	Fuer Zeilenumbrueche ist immer selbst zu sorgen.
+
+RUECKGABEWERT:
+	keiner
+
+BEISPIELE:
+	Ein einfaches Beispiel:
+	  // Prototype fuer Closure.
+	  static string info1();
+	  void create()
+	  { ...
+	    SetChats(20,
+	   ({"Der Ork sagt: Hau ab, bevor ich Dich fresse.\n",
+	     "Der Ork grinst Dich unverschaemt an.\n",
+	     "Der Ork wedelt mit seinem Saebel vor Deinem Gesicht herum.\n",
+	     "Der Ork droht Dir mit der Faust.\n",
+             #'info1,
+	     "@@info2@@"}));
+          }
+	  // Funktion als Closure. Prototype notwendig!
+	  static string info1()
+	  { if(QueryProp(P_HP)<QueryProp(P_ALIGN))
+	      return"Gleich werde ich von hier fliehen!\n";
+	    return"";
+	  }
+	  // Funktion als process_string().
+	  string info2()
+	  { return QueryProp(P_HP)==QueryProp(P_MAX_HP)?
+	 "Der Ork grinst: Mir geht es fantastisch!\n":
+	 "Der Ork seufzt: Mir ging es wirklich schon mal besser.\n";
+	  }
+
+BEMERKUNGEN:
+	Im Kampf werden keine Chats ausgegeben. Es ist dann SetAttackChats()
+	zu verwenden.
+	Funktionen als process_string() sollte man nicht mehr verwenden.
+	<chance> wird in der Property P_CHAT_CHANCE abgelegt. Um einen NPC
+	voruebergehend 'stillzulegen', kann man P_CHAT_CHANCE auf 0 setzen.
+  Wenn kein Spieler anwesend ist, haben NPC in der Regel keinen Heartbeat,
+  weswegen dann auch die Chats ausgeschaltet sind.
+  Spieler koennen NPC 'stillen', d.h. Chats und AttackChats abschalten.
+
+SIEHE AUCH:
+	P_CHAT_CHANCE, SetAttackChats(), process_string()
+
+----------------------------------------------------------------------------
+Last modified: Sat Jan 18 18:48:06 2003 by Patryn
diff --git a/doc/lfun/SetCoinsPerUnits b/doc/lfun/SetCoinsPerUnits
new file mode 100644
index 0000000..07e3879
--- /dev/null
+++ b/doc/lfun/SetCoinsPerUnits
@@ -0,0 +1,38 @@
+SetCoinsPerUnits()
+
+FUNKTION:
+     void SetCoinsPerUnits(int coins, int units);
+
+DEFINIERT IN:
+     /std/unit.c
+
+ARGUMENTE:
+     coins
+          Zahl der Muenzen
+     units
+          Zahl der Einheiten
+
+BESCHREIBUNG:
+     Es wird festgelegt, wieviel eine bestimmte Menge der Einheiten kostet.
+     Eine einzelne Einheit kostet coins/units Muenzen.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Bei der Preisberechnung wird abgerundet. Hat man also ein Verhaeltnis
+     von einer Muenze pro zwei Einheiten, so ist der Preis einer einzelnen
+     Einheit 0!
+
+BEISPIELE:
+     Eine Einheit soll 3.5 Muenzen wert sein:
+
+     /* 7 Muenzen / 2 Einheiten = 3.5 Muenzen / Einheit */
+     SetCoinsPerUnits(7,2);
+
+SIEHE AUCH:
+     QueryCoinsPerUnits(), SetGramsPerUnits(), QueryGramsPerUnits(),
+     /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:24:57 1996 by Wargon
diff --git a/doc/lfun/SetDoorStatus b/doc/lfun/SetDoorStatus
new file mode 100644
index 0000000..1948637
--- /dev/null
+++ b/doc/lfun/SetDoorStatus
@@ -0,0 +1,29 @@
+FUNKTION:
+     void SetDoorStatus(string dest, int status);
+
+DEFINIERT IN:
+     /obj/doormaster.c
+
+ARGUMENTE:
+     <dest>   = Zielraum der Tuer.
+     <status> = Der neue Zustand der Tuer.
+
+BESCHREIBUNG:
+     Der Zustand der Tuer, die von diesem Raum nach <dest> fuehrt, wird auf
+     <status> geaendert. Hierbei muss <status> einer der drei folgenden Werte
+     aus <doorroom.h> sein:
+
+       D_STATUS_LOCKED - Tuer abgeschlossen
+       D_STATUS_CLOSED - Tuer geschlossen
+       D_STATUS_OPEN   - Tuer geoeffnet
+
+RUECKGABEWERT:
+     keiner
+
+
+SIEHE AUCH:
+    NewDoor(), QueryDoorKey(), QueryDoorStatus(), P_DOOR_INFOS,
+    /std/room/doors.c, /obj/doormaster.c, GetPhiolenInfos(), QueryAllDoors()
+
+-----------------------------------------------------------------------------
+Letzte Aenderung: Don, 08.05.2014, Gabylon
diff --git a/doc/lfun/SetEnemies b/doc/lfun/SetEnemies
new file mode 100644
index 0000000..6c79a56
--- /dev/null
+++ b/doc/lfun/SetEnemies
@@ -0,0 +1,33 @@
+SetEnemies()
+
+FUNKTION:
+     mapping SetEnemies(object *myenemies);
+
+DEFINIERT IN:
+     /std/living/combat.c
+
+ARGUMENTE:
+     myenemies
+	  Array: ({Gegnerarray, Zeitenarray})
+
+RUeCKGABEWERT:
+     Internes Mapping mit bekannten Gegnern und Zeiten.
+
+BESCHREIBUNG:
+     Normalerweise fuegt man einzelne Gegner mittels InsertEnemy() ein und
+     loescht alte Feinde mittels StopHuntFor().
+
+     Um jedoch mit einem Schlag viele Veraenderungen vorzunehmen, kann man
+     die Funktion SetEnemies() nutzen.
+
+     Ihr uebergibt man ein Array mit einem Array mit den gewuenschten Gegnern
+     als und einem zweiten, gleichgrossen Array mit den Zeiten, wie lange
+     diese Gegner aktuell sein sollen, als Eintraege.
+
+     Die Funktion traegt diese Werte als Gegner mit entsprechender
+     Feindeszeit ein.
+
+SIEHE AUCH:
+     InsertEnemy(), StopHuntFor(), SelectEnemy(), PresentEnemies()
+
+10.Feb 2005 Gloinson
diff --git a/doc/lfun/SetGramsPerUnits b/doc/lfun/SetGramsPerUnits
new file mode 100644
index 0000000..fcbf9a4
--- /dev/null
+++ b/doc/lfun/SetGramsPerUnits
@@ -0,0 +1,32 @@
+SetGramsPerUnits()
+
+FUNKTION:
+     void SetGramsPerUnits(int grams, int units);
+
+DEFINIERT IN:
+     /std/unit.c
+
+ARGUMENTE:
+     grams
+          Gewicht in Gramm
+     units
+          Zahl der Einheiten
+
+BESCHREIBUNG:
+     Es wird festgelegt, wieviel eine bestimmte Menge der Einheiten wiegt.
+     Eine einzelne Einheit wiegt grams/units Gramm.
+
+RUeCKGABEWERT:
+     keiner
+
+BEISPIELE:
+     Vier Einheiten sollen 3 Gramm wiegen:
+
+     SetGramsPerUnits(3,4);
+
+SIEHE AUCH:
+     SetCoinsPerUnits(), QueryCoinsPerUnits(), QueryGramsPerUnits(),
+     /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:25:09 1996 by Wargon
diff --git a/doc/lfun/SetProp b/doc/lfun/SetProp
new file mode 100644
index 0000000..b2602fb
--- /dev/null
+++ b/doc/lfun/SetProp
@@ -0,0 +1,47 @@
+SetProp()
+FUNKTION:
+     public mixed SetProp(string name, mixed Value);
+
+DEFINIERT IN:
+     /std/thing/properties.c
+     /sys/thing/properties.h (Prototyp)
+
+ARGUMENTE:
+     name	- Property, deren Wert veraendert werden soll.
+     Value	- Wert, auf den der Inhalt der Property gesetzt werden soll
+
+BESCHREIBUNG:
+     Der Datenwert der Property 'name' wird auf den Wert 'Value' gesetzt.
+
+     Existiert eine F_SET_METHOD oder eine _set_'name'()-Methode fuer
+     diese Property, so wird diese aufgerufen und ihr 'Value' uebergeben.
+     Eine F_SET_METHOD hat dabei Vorrang vor _set_'name'(), d.h.
+     _set_'name'() wird nach erfolgreicher F_QUERY_METHOD nicht mehr
+     gerufen.
+
+     (Diese Methoden nutzen dann Set(), um den Datenwert der Property
+      'name' zu aendern. Teilweise werden aber auch interne Variablen so
+      oeffentlich gemacht und sind nicht in der ueber Set/Query verfuegbaren
+      Property 'name' abgelegt.)
+
+RUeCKGABEWERT:
+     Der Wert, der nun in der Property gespeichert ist.
+     In der Regel ist das 'Value'. Wenn die Property ueber eine SET_METHOD
+     oder eine _set_'name'()-Funktion verfuegt und diese 'Value' aendert
+     (zum Beispiel, indem sie 'Value' an einen bestimmten erlaubten
+     Wertebereich anpasst), kann der Rueckgabewert jedoch auch veraendert
+     sein.
+
+     Wenn die Property nicht veraendert werden darf, wird -1 zurueckgegeben.
+
+BEISPIELE:
+     // geben wir dem Zwerg eine Kurzbeschreibung
+     SetProp(P_SHORT, "Ein kleiner Zwerg");
+
+SIEHE AUCH:
+     Aehnliches:	QueryProp(L), Set(L), Query(L)
+     Generell:		SetProperties(L), QueryProperties(L)
+     Konzept:		properties, /std/thing/properties.c
+
+15.Dez 2004 Gloinson
+
diff --git a/doc/lfun/SetProperties b/doc/lfun/SetProperties
new file mode 100644
index 0000000..4a1fa1d
--- /dev/null
+++ b/doc/lfun/SetProperties
@@ -0,0 +1,27 @@
+SetProperties()
+FUNKTION:
+     void SetProperties(mapping props);
+
+DEFINIERT IN:
+     /std/thing/properties.c
+
+ARGUMENTE:
+     props	- Mapping mit den Daten fuer alle Properties
+
+BESCHREIBUNG:
+     Diese Funktion setzt angegebene Properties auf einen Schlag.
+     Mapping muss wie folgt aufgebaut sein:
+	([ name: wert; flags; set_method; query_method,
+	   name2: ... ]);
+
+BEMERKUNGEN:
+     - diese Funktion wird von restore_object() verwendet, um nicht alle
+       restaurierten Properties einzeln setzen zu muessen.
+     - SECURED/PROTECTED/NOSETMETHOD Properties werden beruecksichtigt
+
+SIEHE AUCH:
+     Aehnliches:	SetProp(L), QueryProp(L), Set(L), Query(L)
+     Generell:		QueryProperties(L)
+     Konzept:		properties, /std/thing/properties.c
+
+1.Mai 2004 Gloinson
diff --git a/doc/lfun/SetRealAttribute b/doc/lfun/SetRealAttribute
new file mode 100644
index 0000000..68ea14c
--- /dev/null
+++ b/doc/lfun/SetRealAttribute
@@ -0,0 +1,31 @@
+SetRealAttribute()
+FUNKTION:
+     int SetRealAttribute(string attr, int val)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+
+ARGUMENTE:
+     attr       - zu setzendes Attribut
+     val        - Wert
+
+BESCHREIBUNG:
+     Setzt den realen Wert des Attributes, d.h. das reine Attribut,
+     wenn moeglich. Lediglich eine Alias-Methode fuer SetAttr().
+     (QueryAttribute() gibt also val + etwaige Offsets/Modifier zurueck)
+
+RUeCKGABEWERT:
+     Den gesetzten Wert.
+
+BEMERKUNGEN:
+     Bitte nicht auf Spieler anwenden!
+
+SIEHE AUCH:

+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+	SetTimedAttrModifier(), QueryTimedAttrModifier(), 

+	DeleteTimedAttrModifier(),

+	P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_TIMED_ATTR_MOD,

+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+----------------------------------------------------------------------------

+Last modified: Tue Jul 27 20:00:20 2004 by Muadib

diff --git a/doc/lfun/SetSpellFatigue b/doc/lfun/SetSpellFatigue
new file mode 100644
index 0000000..c34627b
--- /dev/null
+++ b/doc/lfun/SetSpellFatigue
@@ -0,0 +1,75 @@
+SetSpellFatigue
+
+FUNKTION:
+    public varargs int SetSpellFatigue(int duration, string key)
+
+DEFINIERT IN:
+    /std/living/skills.c
+    /std/player/skills.c
+    /sys/living/skills.h
+
+ARGUMENTE:
+    int duration: Wie lang soll die Spruchermuedung andauern?
+    string key  : Eindeutiger Name des Spruches, einer Gruppe von Spruechen
+                  oder 0 fuer die globale Spruchermuedung.
+
+BESCHREIBUNG:
+    Diese Funktion dient zum Verwalten von individuellen Spruchermuedungen
+    (Spellfatigue, Spruchsperren).
+    Hiermit lassen sich unabhaengige Ermuedungen/Sperren fuer einzelne
+    Sprueche oder Gruppen von Spruechen gestalten.
+
+    <duration> ist die Zeit (in Sekunden), welche die Spruchermuedung
+    anhalten soll (nicht die Endzeit).
+
+    Wird <key> nicht angegeben oder ist 0, wird die globale Spruchsperre
+    gesetzt (identisch zu der Property P_NEXT_SPELL_TIME), anderenfalls die
+    unter <key> gespeicherte Spruchermuedung.
+    Setzt man einen Eintrag ohne Angabe von <key> bedeutet dies damit auch,
+    dass der Wert von P_NEXT_SPELL_TIME geaendert wird.
+
+RUeCKGABEWERT:
+    -1    Der Eintrag <key> ist noch nicht abgelaufen, es wurde _keine_
+          neue Spruchermuedung/-Sperre gespeichert.
+
+    >0    Eintrag wurde gespeichert, Rueckgabewert ist die Zeit, zu der die
+          Sperre ablaeuft.
+
+BEISPIELE:
+    Ein Spell gehoert zu einer Gruppe von Spells mit dem Namen 'extrasuess'.
+    Er darf nur ausgefuehrt werden, wenn seit 5s kein anderer Spruch aus der
+    Gruppe ausgefuehrt wurde.
+    if (CalculateSpellSuccess(...) > x) {
+      // Spellfatigue setzen (und Erfolg pruefen!)
+      if (ob->SetSpellFatigue(5, "extrasuess") > -1) {
+        tell_object(ob, "Du fuetterst " + enemy->Name(WEN) + " mit einem "
+          "Stueck suesser Schokotorte.\n");
+        ...
+      }
+      else {
+        // Sauerei! Zu ermuedet fuer diesen Spruch. Fehlermdeldung ...
+      }
+    }
+    Dieses setzt natuerlich voraus, dass alle anderen Sprueche der Gruppe
+    "extrasuess" den gleichen <key> pruefen und setzen.
+    (Will man vor CalculateSpellSuccess() wissen, ob der Spruch ueberhaupt
+     gewirkt werden duerfte, sollte man hierzu dann CheckSpellFatigue()
+     verwenden.)
+
+BEMERKUNGEN:
+    Die genauen Zeitdauern koennen von Spielern beeinflusst werden, sie
+    unterliegen der jeweiligen Einstellung von 'spruchermuedung', d.h. koennen
+    auf volle 2s aufgerundet werden. (Dies ist nicht der Fall bei NPC.)
+    Auch wenn diese Funktion zum Verwalten von beliebigen Zeitsperren genutzt
+    werden koennen, beschraenkt euch bitte auf Spruchermuedungen und benutzt
+    ansonsten check_and_update_timed_key(). Falls ihr diesbzgl. weitere/andere
+    Wuensche habt, sprecht den/die Mudlib-EM an.
+
+SIEHE AUCH:
+    CheckSpellFatigue(L), DeleteSpellFatigue(L)
+    P_NEXT_SPELL_TIME
+    spruchermuedung
+
+----------------------------------------------------------------------------
+27.03.2010, Zesstra
+
diff --git a/doc/lfun/SetStorageRoom b/doc/lfun/SetStorageRoom
new file mode 100644
index 0000000..4655a1e
--- /dev/null
+++ b/doc/lfun/SetStorageRoom
@@ -0,0 +1,74 @@
+SetStorageRoom()
+
+FUNKTION:
+        void SetStorageRoom(string store);
+
+DEFINIERT IN:
+        /std/room/shop.c
+
+ARGUMENTE:
+        store
+          Dateiname des Lagers, in dem die aufgekauften Objekte aufbewahrt
+          werden.
+
+RUeCKGABEWERT:
+        keiner
+
+BESCHREIBUNG:
+        Mit dieser Funktion wird dem Laden bekannt gegeben, welches Objekt
+        als Lager fuer angekaufte Objekte dienen soll. Jeder Laden muss
+        explizit einen eigenen Lagerraum setzen, ansonsten wird ein 
+        Laufzeitfehler ausgeloest und der Laden ist nicht ladbar.
+        
+        Das Speicherobjekt sollte /std/store.c erben, da dort einige
+        Aufraeumfunktionen definiert sind. Entscheidend fuer selbige sind
+        die Properties P_MIN_STOCK und P_STORE_CONSUME.
+
+BEISPIELE:
+        Der Raum 'myladen.c' koennte etwa so aussehen:
+          
+          // myladen.c
+          inherit "/std/shop";
+          #include <properties.h>
+          
+          protected void create() {
+            ::create();
+            SetProp(...); // Beschreibung wie bei normalen Raeumen
+            SetStorageRoom("/d/beispiel/mystore");
+            AddFixedObject("/items/fackel");
+          }
+
+        In diesem Laden wird nun die Standardfackel als mengenmaessig 
+        unbegrenzter Artikel verkauft.
+
+        Der zugehoerige Lagerraum 'mystore.c' kann dann im einfachsten Fall
+        so aussehen:
+          
+          // mystore.c
+          inherit "/std/store";
+          #include <rooms.h>  // Fuer AddItem-Konstanten
+          
+          protected void create() {
+            ::create();
+            // KEINE weiteren Beschreibungen!
+            // 1. erbt der Speicher keine Beschreibungsmodule,
+            // 2. sollen da eh nur Sachen und keine Spieler rein!
+            AddItem("/items/schaufel", REFRESH_REMOVE);
+          }
+        
+        Der Laden verkauft nun auch Schaufeln, jedoch nur eine pro Reset.
+
+HINWEISE:        
+        Fuer standardmaessig verkaufte Waren beachte man den Unterschied 
+        zwischen den Funktionen AddItem() und AddFixedObject().
+        Die erstgenannte Funktion ist im Lager zu verwenden, letztere jedoch
+        im Laden.
+
+SIEHE AUCH:
+        Allgemeines:  laden
+        Funktionen:   AddItem(L), AddFixedObject(L), RemoveFixedObject(L) 
+        Basisobjekte: /std/store.c
+        Properties:   P_MIN_STOCK, P_STORE_CONSUME
+
+----------------------------------------------------------------------------
+Last modified: 19-Jun-2015, Arathorn
diff --git a/doc/lfun/SetTimedAttrModifier b/doc/lfun/SetTimedAttrModifier
new file mode 100644
index 0000000..5804823
--- /dev/null
+++ b/doc/lfun/SetTimedAttrModifier
@@ -0,0 +1,92 @@
+SetTimedAttrModifier()
+FUNKTION:
+     int SetTimedAttrModifier(string key, mapping modifier, 
+                              int outdated, object dependent, mixed notify) 
+DEFINIERT IN:
+     /std/living/attributes.c
+
+ARGUMENTE:
+     key	-	in P_TIMED_ATTR_MOD vorzunehmender oder zu 
+                        aendernder Eintrag

+     modifier   -       Mapping mit den Attributveraenderungen

+     outdated   -       Zeitpunkt zu dem die Attributveraenderungen

+                        ablaufen sollen in Sekunden seit dem

+                        1. Jan 1970, 0.0:0 GMT oder 0

+     dependent  -       Objekt dessen Existenz eine Bedingung fuer die

+                        Attributveraenderung sein soll oder 0

+     notify     -       Objekt oder File das mittels 
+                        NotifyTimedAttrModExpired ueber

+                        den Attributablauf informiert werden soll

+
+BESCHREIBUNG:
+     Der zu key gehoerende Eintrag wird in P_TIMED_ATTR_MOD angelegt oder

+     modifiziert und update_max_sp_and_hp aufgerufen.

+     Es empfiehlt sich auf die Eindeutigkeit des string-Parameters key
+     besonderes Augenmerk zu legen.
+

+     Unter dem Key key wird in P_TIMED_ATTR_MOD ein Eintrag vorgenommen,

+     welcher die Attribute des Livings um die in modifier stehenden Offsets

+     veraendert.

+

+     Diese Veraenderung ist solange aktiv bis entweder die in outdated

+     stehende Zeit ueberschritten ist oder das in dependent uebergebene

+     Objekt nicht mehr existiert.

+     Sind beide Argumente 0 so laeuft die Attributveraenderung nicht ab

+     und kann durch DeleteTimedAttrModifier geloescht werden.

+     Laeuft die Attributveraenderung ab, so wird der in notify angegebene

+     Empfaenger mittels Aufruf NotifyTimedAttrModExpired davon 
+     benachrichtigt.

+     Der Funktion NotifyTimedAttrModExpired wird als Argument der key

+     der abgelaufenen Attributveraenderung uebergeben.

+

+BEISPIELE:

+     Ein NPC kann einen Spieler auf die folgende Weise solange die

+     Attribute um eins herabsetzen bis entweder eine Stunde verstrichen

+     ist oder der NPC nicht mehr existiert zum Beispiel weil er getoetet

+     wurde.

+

+       player->SetTimedAttrModifier( player->query_real_name(),

+                                     ([A_STR:-1,A_INT:-1,A_DEX:-1,A_CON:-1]),

+                                     time()+3600,

+                                     this_object(),

+                                     this_object()

+                                   );

+

+     Will der NPC nun noch darauf reagieren, dass die Attributmodifikation

+     durch Timeout abgelaufen ist, so koennte dies folgendermassen geschehen.

+

+       void NotifyTimedAttrModExpired(string str)

+       {

+           // Die Funktion wird aus dem Lebewesen gerufen, in welchem der Mod
+           // gerade abgelaufen ist. Also Meldungsausgabe an
+           // previous_object().
+           tell_object(previous_object()
+               ,"Da hast Du aber nochmal Glueck gehabt.\n");

+       }

+

+

+RUeCKGABEWERT:

+     TATTR_INVALID_ARGS      -     Im Falle eines fehlenden key-Arguments,

+                                   eines fehlenden modifier-Arguments oder

+                                   eines bereits abgelaufenen

+                                   outdatet-Arguments

+     TATTR_OK                -     Im Erfolgsfall

+

+     Die Rueckgabewerte sind in /sys/living/attributes.h definiert.

+
+SIEHE AUCH:

+     Attribute:  QueryAttribute(), SetAttribute()

+                 SetRealAttribute(), QueryRealAttribute(),

+                 QueryAttributeOffset(),

+                 UpdateAttributes()

+     Methoden:   QueryTimedAttrModifier(), DeleteTimedAttrModifier()

+     Callback:   NotifyTimedAttrModExpired()

+     Properties: P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS

+		 P_X_ATTR_MOD, P_M_ATTR_MOD

+		 P_TIMED_ATTR_MOD

+     Sonstiges:  /std/living/attributes.c

+
+LETZTE Aenderung:
+15.02.2009, Zesstra
+
+
diff --git a/doc/lfun/ShowDoors b/doc/lfun/ShowDoors
new file mode 100644
index 0000000..ecd36b0
--- /dev/null
+++ b/doc/lfun/ShowDoors
@@ -0,0 +1,23 @@
+ShowDoors()
+
+FUNKTION:
+	void ShowDoors()
+
+ARGUMENTE:
+	Keine.
+
+BESCHREIBUNG:
+	Zeigt alle Sehertor an, die der Seher (this_player()) schon kennt.
+	Falls in seiner Umgebung ein Tor steht, so wird dieses eingeklammert.
+
+RUECKGABEWERT:
+	Keiner.
+
+BEMERKUNGEN:
+    Diese Funktion wird von /d/seher/portale/sehertormaster definiert.
+
+BEISPIELE:
+    /d/seher/portale/sehertormaster->ShowDoors()
+
+SIEHE AUCH:
+    DiscoverDoor, DoorIsKnown, Teleport, GetDoorsMapping
diff --git a/doc/lfun/ShowPropList b/doc/lfun/ShowPropList
new file mode 100644
index 0000000..64bc4cb
--- /dev/null
+++ b/doc/lfun/ShowPropList
@@ -0,0 +1,49 @@
+ShowPropList()
+
+FUNKTION:
+     void ShowPropList(string *props);
+
+DEFINIERT IN:
+     /std/thing/util.c
+
+ARGUMENTE:
+     props
+          Array von Strings mit den Namen der Properties, die ausgegeben
+          werden sollen.
+
+BESCHREIBUNG:
+     Gibt die Inhalte der in props angegebenen Properties aus.
+
+RUeCKGABEWERT:
+     keiner
+
+BEISPIELE:
+     Sei test.c folgendes Programm:
+
+     inherit "std/thing";
+     inherit "std/thing/util";
+
+     create() {
+       ::create();
+
+       SetProp(P_SHORT, "Kurz");
+       SetProp(P_NAME, ({ "Name", "Namens", "Namen", "Namen" }));
+       SetProp("me", this_object() );
+     }
+
+     Mit xcall test->ShowPropList( ({ P_SHORT, P_NAME, "me" }) ); erhielte
+     man dann folgende Ausgabe:
+
+     *short: "Kurz"
+     *name: ({ "Name", "Namens", "Namen", "Namen" })
+     *me: OBJ(/players/wargon/test#12345)
+
+BEMERKUNGEN:
+     Mit dem Befehl xprops des MGtools lassen sich uebrigens saemtliche
+     Properties eines Objekte auf einen Schlag ausgeben.
+
+SIEHE AUCH:
+     /std/ting/util.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:25:26 1996 by Wargon
diff --git a/doc/lfun/SkillResTransfer b/doc/lfun/SkillResTransfer
new file mode 100644
index 0000000..6983eea
--- /dev/null
+++ b/doc/lfun/SkillResTransfer
@@ -0,0 +1,28 @@
+SkillResTransfer()
+
+FUNKTION:
+     protected void SkillResTransfer(mapping from_M, mapping to_M)
+
+DEFINIERT IN:
+     /std/living/skill_utils
+
+ARGUMENTE:
+     mapping from_M: Mapping mit zu kopierenden Werten
+     mapping to_M:   Zielmapping
+
+BESCHREIBUNG:
+     Interne Methode, die zB (!) waehrend der Ausfuehrung von Attack() durch
+     Skills oder P_TMP_ATTACK_MOD geaenderte Werte selektiert in das
+     Hauptangriffsmapping uebertraegt.
+
+     Derzeit werden folgende Werte kopiert:
+     SI_SKILLDAMAGE, SI_SKILLDAMAGE_MSG, SI_SKILLDAMAGE_MSG2,
+     SI_SKILLDAMAGE_TYPE, SI_SPELL
+
+BEMERKUNGEN:
+     * wird an mehreren Stellen, nicht nur der living/combat verwendet
+
+SIEHE AUCH:
+     P_TMP_ATTACK_MOD, UseSkill (Waffenfaehigkeiten)
+
+18.Jul 2014 Gloinson
diff --git a/doc/lfun/SpellAttack b/doc/lfun/SpellAttack
new file mode 100644
index 0000000..0e24fd9
--- /dev/null
+++ b/doc/lfun/SpellAttack
@@ -0,0 +1,71 @@
+SpellAttack()
+
+FUNKTION:
+        void SpellAttack(object enemy)
+
+ARGUMENTE:
+        enemy: Der Feind.
+
+BESCHREIBUNG:
+        Diese Funktion wird in jedem Heartbeat eines NPCs ausgefuehrt,
+        falls nicht P_DISABLE_ATTACK gesetzt ist (Paralyse).
+        Standardmaessig tut diese Funktion nichts, aber man kann sie
+        ueberschreiben, damit in jedem Heartbeat Angriffe mit Spells
+        ausgefuehrt werden.
+
+        Man sollte hierbei ein random() einbauen, damit der NPC nicht
+        in jedem Heartbeat auch wirklich einen Angriff ausfuehrt.
+        Ausserdem sollte man auch fuer eventuelle Ausgaben sorgen.
+
+RUECKGABEWERT:
+        Keiner.
+
+BEMERKUNG:
+        Die AttackChats, die mittels SetAttackChats gesetzt werden
+        koennen, macht im Grunde nichts anderes, aber Chats sind halt
+        keine Angriffe. :-)
+
+BEISPIELE:
+        Im Grunde ist dieses simple Beispiel eine Nachbildung von
+        Attack-Chats und soll dementsprechend nur der Anschauung dienen.
+
+        void SpellAttack(object enemy) 
+        {
+          // mit 80% Wahrscheinlichkeit wird nichts gemacht.
+          switch(random(5))
+          {
+            case 0: 
+              write("Der Ork tritt Dir in den Hintern.\n");
+              return;
+            case 1: 
+              write("Der Ork bruellt: Lebend kommst Du hier nicht raus!\n");
+              return;
+            case 2:
+              write("Der Ork blutet schon aus mehreren Wunden.\n");
+              return;
+            case 3:
+              write(knirsch(enemy));
+              return;
+            default:
+              return;
+          }
+        }
+
+        string knirsch(object enemy)
+        {
+           if (objectp(enemy))
+             helm = enemy->QueryArmourByType(AT_HELMET);
+           if (objectp(helm))
+           {
+             helm->Damage(1);
+             return "Der Ork beschaedigt Deinen Helm!\n";
+           }
+           else
+             return ""; // keine Meldung
+        }
+
+SIEHE AUCH:
+        "Attack", "SetAttackChats", /std/npc/combat.c
+
+LETZTE AENDERUNG:
+ Don, 27.02.2003, 12:50:00 von Hirudo
diff --git a/doc/lfun/SpellDefend b/doc/lfun/SpellDefend
new file mode 100644
index 0000000..79537d5
--- /dev/null
+++ b/doc/lfun/SpellDefend
@@ -0,0 +1,43 @@
+SpellDefend()
+FUNKTION:
+     public int SpellDefend(object caster,mapping sinfo);
+
+DEFINIERT IN:
+     /std/living/combat.c
+
+ARGUMENTE:
+     object caster	- Gegner
+     mapping sinfo	- Zusatzinformationen zum Spell
+
+BESCHREIBUNG:
+     Ueber den Skill SK_SPELL_DEFEND mit den Aufrufparametern
+       SI_ENEMY    : <caster>
+     und
+       SI_SKILLARG : <sinfo>
+     wird eine Abwehrchance in 0.01%-Schritten fuer einen
+     Spell ermittelt, also 0% - 100% bzw. als Rueckgabewert
+     0 - 10000.
+     
+     Weiterhin wird automatisch P_MAGIC_RESISTANCE_OFFSET und der Skill
+     SK_SPELL_DEFEND beruecksichtigt.
+
+RUeCKGABEWERT:
+     Die Abwehrchance in 0.01%-Schritten.
+     
+     Fuer Spieler wird dieser Rueckgabewert auf 3333 maximal, also 33,33%
+     Abwehrmoeglichkeit beschraenkt.
+
+BEMERKUNGEN:
+     Die Spellbooks muessen selbst auf die Auswertung dieser Funktion
+     achten! Dies geschieht nur im Falle von TryGlobalAttackSpell()
+     und bei Spells fuer NPCs mittels P_SPELLS automatisch!
+
+     Bitte bei NPCs nicht pauschal 100% / 10000 zurueckgeben. Danke.
+
+SIEHE AUCH:
+     Verwandt:     P_MAGIC_RESISTANCE_OFFSET
+     Aehnlich:     P_NOMAGIC
+     Generell:     TryGlobalAttackSpell, /std/spellbook.c
+     Sonstiges:    UseSkill, SK_SPELL_DEFEND
+
+29.Dez 2007 Gloinson
diff --git a/doc/lfun/SpellInform b/doc/lfun/SpellInform
new file mode 100644
index 0000000..369b201
--- /dev/null
+++ b/doc/lfun/SpellInform
@@ -0,0 +1,31 @@
+SpellInform
+
+FUNKTION:
+        void SpellInform(caster,spell,sinfo);
+
+DEFINIERT IN:
+        selber zu definieren
+
+ARGUMENTE:
+        pl
+          Das Lebewesen, das erfolgreich gezaubert hat.
+        spell
+          Der Name des gezauberten Spells
+        sinfo
+          Das komplette Spellmapping des gezauberten Spells
+
+BESCHREIBUNG:
+        Diese Funktion wird vom Spellbuch einer Gilde in der Umgebung
+        (Environment) eines Lebewesens aufgerufen, wenn immer das Lebewesen
+        einen Spell _erfolgreich_ gezaubert hat.
+        Bemerkung: Bitte vermeiden, aufwaendige Sachen in der Funktion zu
+        machen, da sonst u.U. deswegen Gilden anfangen zu buggen. Falls es
+        sein muss, macht dann lieber einen call_out mit 2s Wartezeit.
+
+RUeCKGABEWERT:
+        keiner
+
+SIEHE AUCH:
+        
+----------------------------------------------------------------------------
+16.04.2007, Zesstra
diff --git a/doc/lfun/Start b/doc/lfun/Start
new file mode 100644
index 0000000..89f63ef
--- /dev/null
+++ b/doc/lfun/Start
@@ -0,0 +1,25 @@
+Start()
+
+FUNKTION:
+     varargs void Start(int pos);
+
+DEFINIERT IN:
+     /std/transport.c
+
+ARGUMENTE:
+     pos
+          Fahrplanstation, an der begonnen werden soll.
+
+BESCHREIBUNG:
+     Diese Funktion schickt den Transporter auf die Strecke. Dabei beginnt
+     der Weg an der mit pos bezeichneten Stelle im Fahrplan. Default ist 0,
+     d.h. der Transporter beginnt seinen Weg an der ersten Fahrplanposition.
+
+RUeCKGABEWERT:
+     keiner
+
+SIEHE AUCH:
+     Halt(), /std/transport.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:25:30 1996 by Wargon
diff --git a/doc/lfun/StopHuntFor b/doc/lfun/StopHuntFor
new file mode 100644
index 0000000..738b920
--- /dev/null
+++ b/doc/lfun/StopHuntFor
@@ -0,0 +1,52 @@
+StopHuntFor()
+
+FUNKTION:
+  varargs int StopHuntFor(object arg,int silent);
+
+DEFINIERT IN:
+  /std/living/combat.c
+
+ARGUMENTE:
+  arg
+    Der Gegner, welcher nicht mehr bekaempft werden soll.
+  silent
+    Flag, welches gesetzt anzeigt, dass die beiden Ex-Streithaehne
+    ueber das einseitige Friedensangebot nicht informiert werden
+    sollen.
+
+RUeCKGABEWERT:
+  Flag: Bei 0 war der Gegner nicht auffindbar, bei 1 Erfolg.
+
+BESCHREIBUNG:
+  Mit dieser Funktion kann man ein Lebewesen <arg> als Gegner
+  austragen. Im Normalfall erhalten sowohl das aktuelle Objekt, als
+  auch der Gegner eine Information darueber. Dies kann jedoch mit dem
+  gesetzten Flag <silent> unterbunden werden.
+  Es ist auch moeglich, auf diese Meldung Einfluss zu nehmen, indem
+  man die Funktion StopHuntText() ueberschreibt, welche dafuer
+  verantwortlich ist.
+  Achtung: Um zwischen beiden Streithaehnen Frieden zu schliessen,
+  muss der eine Gegner jeweils bei dem anderen ausgetragen werden. Ein
+  einzelnes StopHuntFor() ist sozusagen nur ein einseitiges
+  Friedensangebot.
+
+BEMERKUNGEN:
+  Soll ein Viech unter bestimmten Umstaenden nicht angreifbar sein, ist in
+  keinem Fall StopHuntFor() im Defend() zu verwenden, sondern P_NO_ATTACK.
+  Grund: Stoppt man unliebsame Kaempfe jeweils am Anfang vom Defend, kann ein
+  Gegner gefahrlos Angriffsspells ausfuehren (und ueben), ohne dass die Gefahr
+  besteht, dass der NPC zurueckschlaegt.
+
+BEISPIELE:
+  Man will aus irgendeinem Grund den Kampf zwischen sich und Gegner enemy
+  einstellen:
+  ...
+  StopHuntFor(enemy); // enemy nicht mehr bekaempfen
+  enemy->StopHuntFor(this_object()); // enemy soll mich nicht bekaempfen.
+  ...
+
+SIEHE AUCH:
+  StopHuntText(), SelectEnemy(), QueryEnemies(), IsEnemy()
+
+----------------------------------------------------------------------------
+16.03.2008, Zesstra 
diff --git a/doc/lfun/StopHuntText b/doc/lfun/StopHuntText
new file mode 100644
index 0000000..1ca8cfb
--- /dev/null
+++ b/doc/lfun/StopHuntText
@@ -0,0 +1,58 @@
+StopHuntText()
+
+FUNKTION:
+	void StopHuntText(object arg);
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	arg
+	  Der Gegner, welcher nicht mehr bekaempft wird.
+
+BESCHREIBUNG:
+	Mit der Funktion StopHuntFor() kann man ein Lebewesen als Gegner
+	austragen. Dabei erhalten sowohl der Gegner, als auch das Lebewesen,
+	welches (einseitig!) Frieden schliesst, jeweils eine Meldung, sofern
+	man dies nicht mittels eines Flags unterbindet.
+	Es ist nun moeglich, auf diese Meldung Einfluss zu nehmen, indem
+	man die Funktion StopHuntText() ueberschreibt, welche dafuer
+	verantwortlich ist.
+
+BEISPIEL:
+	Ein Lebewesen moechte einen Kampf sofort abbrechen, wenn es von
+	einem Frosch angegriffen wird:
+	  int Defend(int dam,mixed dam_type,mixed spell,object enemy)
+	  { if(enemy&&enemy->QueryProp(P_FROG))
+	    { if(StopHuntFor(enemy))
+              { // wenn Frosch angreifen will, der noch kein Gegner war
+                tell_object(arg,
+         this_object()->Name(WER)+" kaempft nicht mit Dir.\n"
+	+"Wahrscheinlich werden Froesche verschont.\n");
+	        tell_object(this_object(),
+	 "Der bloede Frosch wollte Dich doch tatsaechlich angreifen.\n");
+              }
+	      enemy->StopHuntFor(this_object(),1);
+              return 0;
+            }
+	    return::Defend(dam,dam_type,spell,enemy);
+	  }
+	  // wird nur aufgerufen, wenn der Gegner irgendwann Frosch wurde
+	  void StopHuntText(object arg)
+	  { tell_object(arg,
+	 this_object()->Name(WER)+" jagd Dich nicht mehr.\n"
+	+"Wahrscheinlich werden Froesche verschont.\n");
+	    tell_object(this_object(),
+	"Dein Gegner ist doch tatsaechlich ploetzlich Frosch geworden!\n");
+	  }
+	Warum braucht man nun das erste StopHuntFor(), wenn doch Gegner erst
+	in ::Defend() eingetragen werden, welches doch gar nicht ausgefuehrt
+	wird, wenn der Gegner ein Frosch ist? Man beachte hierbei, dass der
+	Gegner ja auch waehrend des Kampfes oder waehrend der Feindschaft
+	irgendwann Frosch werden koennte und dann schon Gegner war.
+
+SIEHE AUCH:
+	StopHuntFor(), SelectEnemy(), QueryEnemies(), IsEnemy()
+
+----------------------------------------------------------------------------
+Last modified: Wed May 26 16:47:51 1999 by Patryn
diff --git a/doc/lfun/SuggestArticle b/doc/lfun/SuggestArticle
new file mode 100644
index 0000000..7a4ba5c
--- /dev/null
+++ b/doc/lfun/SuggestArticle
@@ -0,0 +1,35 @@
+SuggestArticle()
+
+FUNKTION:
+     varargs int SuggestArticle(string name);
+
+DEFINIERT IN:
+     /std/thing/language.c
+
+ARGUMENTE:
+     name
+          Der Name zur Entscheidungshilfe.
+
+BESCHREIBUNG:
+     Diese Funktion versucht herauszufinden, ob der Artikel zu diesem Objekt
+     ein unbestimmter oder besser ein bestimmter Artikel sein sollte. Die
+     Vorgehensweise ist folgende: Gibt es in der Umgebung dieses Objektes
+     ein weiteres Objekt, das den Namen name besitzt, so wird ein
+     unbestimmter Artikel vorgeschlagen, ansonsten ein bestimmter.
+
+RUeCKGABEWERT:
+     0, wenn ein unbestimmter Artikel geeignet ist, ansonsten 1.
+
+BEMERKUNGEN:
+     Der Vergleich erfolgt mittels der Property P_NAME. Um Erfolg zu haben,
+     sollte man diese Funktion daher in der Form
+
+     SuggestArticle(QueryProp(P_NAME))
+
+     aufrufen.
+
+SIEHE AUCH:
+     QueryArticle(), name(), /std/thing/language.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:25:34 1996 by Wargon
diff --git a/doc/lfun/SwapRows b/doc/lfun/SwapRows
new file mode 100644
index 0000000..7d5d928
--- /dev/null
+++ b/doc/lfun/SwapRows
@@ -0,0 +1,40 @@
+
+SwapRows()
+
+
+FUNKTION:
+    int SwapRows( object ob1, object ob2 )
+
+DEFINIERT IN:
+    /std/living/team.c
+
+ARGUMENTE:
+    ob1, ob2 - Spieler, die die Reihen tauschen sollen.
+
+BESCHREIBUNG:
+    Die angegebenen Spieler tauschen die Reihen.
+
+RUECKGABEWERT:
+    1 bei erfolgreichem Tausch, 0 sonst.
+
+BEMERKUNG:
+    Der Tausch wird nur durchgefuehrt, wenn die angegebenen Spieler auch
+    tatsaechlich im Team sind und damit kein NPC vor einen Spieler gestellt
+    wuerde.
+    Moechte man wissen, ob ein Spieler eine Reihe gewechselt hat, muss man
+    sich der Hilfe eines Hooks bedienen: H_HOOK_TEAMROWCHANGE
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_TEAM_LEADER, P_TEAM_ASSOC_MEMBERS,
+                    P_TEAM_NEWMEMBER
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/TakeFlaw b/doc/lfun/TakeFlaw
new file mode 100644
index 0000000..7bfd03a
--- /dev/null
+++ b/doc/lfun/TakeFlaw
@@ -0,0 +1,75 @@
+TakeFlaw()
+
+FUNKTION:
+     varargs void TakeFlaw(object enemy); (Waffen)
+     varargs void TakeFlaw(mixed dam_types,mapping einfos) (Ruestungen)
+
+DEFINIERT IN:
+     /std/armour/combat.c,
+     /std/weapon/combat.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Diese Funktion wird in Waffen und Ruestungen waehrend des Kampfes
+     aufgerufen. In einer Waffe erfolgt der Aufruf bei jedem Schlag mit
+     dieser Waffe, bei Ruestungen wird TakeFlaw() in einer zufaellig
+     ausgewaehlten getragenen Ruestung aufgerufen.
+     Waffen bekommen das Gegnerobjekt uebergeben, Ruestungen die erweiterten
+     DefendInfos (s. dort fuer Details). Aufgrund dieser Informationen kann
+     man den Schaden, den ein Gegenstand nimmt, flexibler gestalten (z.B. bei
+     einer Waffe in Abhaengigkeit von P_BODY des Gegners.)
+
+     Soweit man die Funktion nicht ueberlaedt, bewirkt sie nichts weiter als
+     das Erhoehen eines Zaehlers, der mit QueryFlaw() abgefragt werden kann.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Die Waffen-/ Ruestungsklasse wird nicht automatisch reduziert! Wenn
+     eine Waffe oder Ruestung sich abnutzen soll, muss man TakeFlaw()
+     ueberladen und dort entsprechend handeln, oder (fuer einfache
+     Faelle) die Property P_QUALITY setzen.
+
+BEISPIELE:
+     Eine Waffe, deren Waffenklasse alle 20 Schlaege um 1 abnimmt:
+
+     inherit "std/weapon";
+
+     #include <properties.h>
+     #include <combat.h>
+
+     create()
+     {
+       /* Das Uebliche... */
+     }
+
+     TakeFlaw()
+     {
+       int flaw;
+
+       /* erst mal den Zaehler erhoehen... */
+       ::TakeFlaw();
+
+       /* jetzt den aktuellen Zaehlerstand abfragen */
+       flaw = QueryFlaw()[0];
+
+       /* Abzug nur jeden 20. Schlag */
+       if (!(flaw % 20)) {
+         /* So, jetzt fuer den Schaden sorgen. Hierfuer benutzt */
+         /* man am sichersten die eingebaute Funktion Damage() */
+         Damage(1);
+       }
+     }
+
+     Dieses einfache Beispiel haette natuerlich auch ueber ein
+     SetProp(P_QUALITY,20); im create() realisiert werden koennen.
+
+SIEHE AUCH:
+     QueryFlaw(), Damage(), DefendInfo, P_QUIALITY, /std/armour/combat.c,
+     /std/weapon/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Thu May 22 10:30:10 1997 by Paracelsus
diff --git a/doc/lfun/TeamFlee b/doc/lfun/TeamFlee
new file mode 100644
index 0000000..fb317a0
--- /dev/null
+++ b/doc/lfun/TeamFlee
@@ -0,0 +1,40 @@
+
+TeamFlee()
+
+
+FUNKTION:
+        int TeamFlee()
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        keine
+
+BESCHREIBUNG:
+        Spieler wird zur Flucht in hintere Reihe veranlasst, falls er dies
+        eingestellt hat.
+
+RUECKGABEWERT:
+        1, falls der Spieler in eine hintere Reihe fliehen wollte und nach
+        dem Versuch nicht mehr in der ersten Reihe steht, sonst 0.
+
+BEMERKUNGEN:
+        Beim Teamleiter fuehrt der Fluchtversuch dazu, dass sein Team nicht
+        mehr folgt.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/TeamMembers b/doc/lfun/TeamMembers
new file mode 100644
index 0000000..28ca93b
--- /dev/null
+++ b/doc/lfun/TeamMembers
@@ -0,0 +1,38 @@
+
+TeamMembers()
+
+
+FUNKTION:
+        object *TeamMembers()
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        keine
+
+BESCHREIBUNG:
+        Liefert Teammitglieder des Teams des Spielers.
+
+RUECKGABEWERT:
+        Array mit ALLEN Teammitgliedern.
+
+BEMERKUNGEN:
+        Falls der Spieler in keinem Team ist, enthaelt das Array nur den
+        Spieler.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/TeamPrefix b/doc/lfun/TeamPrefix
new file mode 100644
index 0000000..efbebb7
--- /dev/null
+++ b/doc/lfun/TeamPrefix
@@ -0,0 +1,37 @@
+
+TeamPrefix()
+
+
+FUNKTION:
+        string TeamPrefix()
+
+DEFINIERT IN:
+        /std/living/team.c
+
+ARGUMENTE:
+        keine
+
+BESCHREIBUNG:
+        Ergibt Team-Prefix eines Spielers.
+
+RUECKGABEWERT:
+        "[Team Teamname] ", falls der Spieler in einem Team ist,
+        ""                  sonst.
+
+BEMERKUNGEN:
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/lfun/Teleport b/doc/lfun/Teleport
new file mode 100644
index 0000000..b902d07
--- /dev/null
+++ b/doc/lfun/Teleport
@@ -0,0 +1,28 @@
+Teleport()
+
+FUNKTION:
+    int Teleport(string ziel)
+
+ARGUMENTE:
+    ziel: Nummer des Sehertors, zu dem teleportiert werden soll.
+
+BESCHREIBUNG:
+    Teleportiert den Seher (this_player()) zu dem Tor mit der angegebenen
+    Nummer. Falls keine Nummer angegeben wird, wird mit ShowDoors() die
+    Liste der bekannten Tore angezeigt und auf die Eingabe einer Nummer
+    gewartet.
+
+RUECKGABEWERT:
+    0, falls der Seher nicht neben einem Tor steht, dass er kennt.
+    1  sonst.
+
+BEMERKUNGEN:
+    Der Seher muss in einem Raum stehen, in dem sich ein Sehertor befindet,
+    dass er kennt. Der Seher muss auch das angegebene Ziel kennen.
+    Diese Funktion wird von /d/seher/portale/sehertormaster definiert.
+
+BEISPIELE:
+    /d/seher/portale/sehertormaster->Teleport(1)
+
+SIEHE AUCH:
+    DiscoverDoor, DoorIsKnown, ShowDoors, GetDoorsMapping
diff --git a/doc/lfun/TestIgnore b/doc/lfun/TestIgnore
new file mode 100644
index 0000000..3e664c0
--- /dev/null
+++ b/doc/lfun/TestIgnore
@@ -0,0 +1,66 @@
+TestIgnore()
+
+FUNKTION:
+     public int TestIgnore(string|string* arg)
+
+DEFINIERT IN:
+     /std/player/comm.c
+
+ARGUMENTE:
+     arg
+         String oder Array von Strings, die getestet werden sollen,
+         Format jeweils: [spieler].aktion[.qualifizierer]
+
+RUeCKGABEWERT:
+     0, wenn arg nicht ignoriert wird
+     MSG_IGNORED, wenn (min. ein Element von) arg ignoriert wird
+
+BESCHREIBUNG:
+     Es wird geprueft, ob der Spieler irgendeinen Eintrag auf seiner Liste
+     hat, der dazu fuehrt, dass <arg> ignoriert wird. Hierbei kommen je nach
+     den Angaben in <arg> folgende Regeln zum Tragen:
+     1) spieler
+        Wird der Spieler ignoriert?
+     2) .aktion
+        Ignoriert der Spieler .aktion, d.h. die Aktion komplett (OHNE
+        Qualifizierer)?
+     3) spieler.aktion
+        Ignoriert der Spieler spieler, .aktion oder spieler.aktion?
+     4) spieler.aktion.qualifizierer
+        Ignoriert der Spieler spieler, .aktion, spieler.aktion oder
+        spieler.aktion.qualifizierer?
+     5) .aktion.qualifizierer
+        Ignoriert der Spieler .aktion oder .aktion.qualifizierer?
+
+     Da TestIgnore() damit durchaus etwas aufwendiger sein kann, sollte
+     man dies nicht unnoetig oft aufrufen. (Braucht man das Ergebnis z.B.
+     kurz spaeter nochmal, koennte man das Ergebnis speichern.) Wird der
+     Qualifizierer nicht gebraucht, sollte man ihn weglassen.
+
+BEISPIEL:
+     if (!this_player()->TestIgnore("andy"))
+       tell_object(this_player(), "Andy teilt Dir mit: Hallo!\n");
+
+     // Beispiel fuer eine Ignore-Check fuer Aktion (kratzen) fuer einen
+     // Spieler (this_player()) an einem anderen Spieler (target)
+     if (!target->TestIgnore( ({getuid(this_player()) + ".kratze",
+                                   getuid(this_player()) + ".kratz"}) ))
+     {
+       tell_object(target, this_player()->Name()+" kratzt dich.\n");
+       tell_object(this_player(), "Du kratzt "+target->Name()+".\n");
+     }
+     else
+       tell_object(this_player(), target->Name()+" ignoriert dich.\n");
+
+     // allumfassender Ignorier-Check in einer Gilde (Klerus) auf
+     // eine Aktion (kurieren) fuer einen bestimmten Spieler (den caster)
+     // an einem zu kurierenden Spieler (target)
+     if (target->TestIgnore(getuid(caster)+".kuriere.klerus"))
+       tell_object(caster, break_string(
+         target->Name()+" ignoriert deinen Versuch.", 78));
+
+SIEHE AUCH:
+     P_IGNORE, AddIgnore, RemoveIgnore, TestIgnoreSimple, /std/player/comm.c
+
+26.04.2014 Zesstra
+
diff --git a/doc/lfun/TestIgnoreSimple b/doc/lfun/TestIgnoreSimple
new file mode 100644
index 0000000..542632e
--- /dev/null
+++ b/doc/lfun/TestIgnoreSimple
@@ -0,0 +1,55 @@
+TestIgnoreSimple()
+
+FUNKTION:
+     public int TestIgnoreSimple(string *arg)
+
+DEFINIERT IN:
+     /std/player/comm.c
+
+ARGUMENTE:
+     arg
+         Liste von Strings, die getestet werden sollen
+
+BESCHREIBUNG:
+     TestIgnoreSimple() prueft, ob der Spieler min. einen der uebergebenen
+     Eintraege auf seiner Ignoriereliste hat.
+     Falls man mehrere Eintraege pruefen muss/moechte, ist es schneller, alle
+     Eintraege in einem zu uebergeben anstatt fuer jeden einzeln 
+     TestIgnoreSimple() aufzurufen.
+
+RUeCKGABEWERT:
+     1, falls es mindestens eine Uebereinstimmungen von arg und der
+     Ignoriere-Liste des Spielers gibt.
+     0, sonst.
+
+BEISPIEL:
+     if (!this_player()->TestIgnoreSimple(({"andy"})))
+       tell_object(this_player(), "Andy teilt Dir mit: Hallo!\n");
+
+     // Beispiel fuer eine Ignore-Check fuer Aktion (kratzen) fuer einen
+     // Spieler (this_player()) an einem anderen Spieler (target)
+     if (!target->TestIgnoreSimple(getuid(this_player()),
+                             getuid(this_player())+".kratz",
+                             getuid(this_player())+".kratze",
+                             ".kratz", ".kratze"}))) {
+       tell_object(target, this_player()->Name()+" kratzt dich.\n");
+       tell_object(this_player(), "Du kratzt "+target->Name()+".\n");
+     } else
+       tell_object(this_player(), target->Name()+" ignoriert dich.\n");
+
+     // allumfassender Ignorier-Check in einer Gilde (Klerus) auf
+     // eine Aktion (kurieren) fuer einen bestimmten Spieler (den caster)
+     // an einem zu kurierenden Spieler (target)
+     if (target->TestIgnoreSimple(({getuid(caster),
+                              getuid(caster)+".kuriere",
+                              getuid(caster)+".kuriere.klerus",
+                              ".kuriere",
+                              ".kuriere.klerus"})))
+       tell_object(caster, break_string(
+         target->Name()+" ignoriert deinen Versuch.", 78));
+
+SIEHE AUCH:
+     P_IGNORE, AddIgnore, RemoveIgnore, TestIgnore, /std/player/comm.c
+
+26.04.2014 Zesstra
+
diff --git a/doc/lfun/TestLimitViolation b/doc/lfun/TestLimitViolation
new file mode 100644
index 0000000..9344299
--- /dev/null
+++ b/doc/lfun/TestLimitViolation
@@ -0,0 +1,21 @@
+TestLimitViolation()
+FUNKTION:
+      status TestLimitViolation(mapping check)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+

+PARAMETER:

+     check	- Mapping mit Attributen: ([<attr>:<wert>])

+
+BESCHREIBUNG:
+     Prueft, ob die Summe der in check enthaltenen Modifikatoren die Summe
+     aller Modifikatoren im Spieler ueber den zugelassenen Grenzwert hebt.
+     
+SIEHE AUCH:
+     QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+     SetAttr(), SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+     P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_ATTRIBUTES_MODIFIER,

+     P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+
+13.Jun.2004, Muadib
\ No newline at end of file
diff --git a/doc/lfun/TriggerEvent b/doc/lfun/TriggerEvent
new file mode 100644
index 0000000..7b0089f
--- /dev/null
+++ b/doc/lfun/TriggerEvent
@@ -0,0 +1,59 @@
+
+FUNKTION:
+     varargs int TriggerEvent(string eid, mixed data);
+
+DEFINIERT IN:
+     /p/daemon/eventd.c
+DEKLARIERT IN:
+     /sys/events.h
+
+ARGUMENTE:
+     string eid,
+       Die ID des Events, der ausgeloest werden soll.
+       Da dieser String fuer alle Events jeweils eindeutig sein muss, 
+       empfiehlt es sich, fuer eigene Events z.B. als Praefix den eigenen 
+       Magiernamen zu nehmen, z.B. "zesstra_vulkanausbruch".
+       ACHTUNG: IDs, die mit 'evt_lib_' beginnen, sind AUSSCHLIESSLICH der
+       Mudlib vorbehalten!
+
+     mixed data,
+       Daten, die jeder Lauscher uebergeben bekommt. Kann ein beliebiger
+       Datentyp sein. Ggf. ein Mapping oder Array benutzen.
+
+BESCHREIBUNG:
+     Der Event mit der ID 'eid' wird ausgeloest. Mit kurzer Verzoegerung
+     (meist 0-2s) werden alle fuer 'eid' registrierten Lauscher durch Aufruf
+     einer Funktion in ihnen informiert:
+     listener->fun(eid, triggerob, data);
+     'triggerob' ist hierbei das Objekt, welche TriggerEvent() gerufen hat,
+     'data' das, was das triggernde Objekte als Daten weiterverteilen moechte.
+     Die einzelnen fun() in den lauschenden Objekten muessen wissen, was sie
+     mit 'data' anfangen sollen. ;-)
+
+RUeCKGABEWERT:
+     1 fuer Erfolg, <=0 fuer Misserfolg.
+     1   - Erfolg, Event 'eid' wurde ausgeloest.
+     -1  - falsche Argumente wurden uebergeben
+     -2  - nicht-oeffentlicher Event und das triggernde Objekt wurde nicht
+           fuer diesen Event freigegeben (momentan gibt es noch keine
+           nicht-oeffentlichen Events)
+     -3  - Event 'eid' existiert nicht, d.h. es gibt keine Lauscher.
+     -4  - Es gibt zuviele nicht verarbeitete Events.
+
+BEMERKUNGEN:
+
+
+BEISPIELE:
+     1. Ein Waechter wird angegriffen:
+        EVENTD->TriggerEvent("xand_holzfaellerlager_angriff", 
+                             (["angreifer": enemy,
+                               "ort": environment(this_object()) ]) );
+        Alle anderen angemeldeten Waechter der Lagers werden nun informiert
+        und koennen ihrerseits reagieren (dem ersten Waechter zuhilfe kommen
+        oder auch die Lagertore schliessen).
+
+SIEHE AUCH:
+     events, eventd, UnregisterEvent(), RegisterEvent()
+
+----------------------------------------------------------------------------
+Last modified: 15.08.2007, Zesstra
diff --git a/doc/lfun/UnregisterEvent b/doc/lfun/UnregisterEvent
new file mode 100644
index 0000000..0de361a
--- /dev/null
+++ b/doc/lfun/UnregisterEvent
@@ -0,0 +1,49 @@
+
+FUNKTION:
+     int UnregisterEvent(string eid, object listener);
+
+DEFINIERT IN:
+     /p/daemon/eventd.c
+DEKLARIERT IN:
+     /sys/events.h
+
+ARGUMENTE:
+     string eid,
+       Die ID des Events, vom dem man sich abmelden will.
+     object listener,
+       Das Objekt, das als Lauscher ausgetragen werden soll.
+
+BESCHREIBUNG:
+     Das Objekt 'listener' wird als Lauscher dieses Events ausgetragen. Ab
+     diesem Moment wird es bei Events vom Typ 'eid' nicht mehr informiert.     
+
+     Hat der Event 'eid' im Anschluss keine Lauscher mehr, wird er implizit
+     geloescht.
+
+RUeCKGABEWERT:
+     1 fuer Erfolg, <=0 fuer Misserfolg.
+     1   - Erfolg, 'listener' wurde eingetragen.
+     -1  - falsche Argumente uebergeben
+     -2  - 'listener' ist nicht fuer 'eid' registriert.
+    
+BEMERKUNGEN:
+     Wenn sich ein Objekt vor Zerstoerung nicht abmeldet, wird es ggf. beim
+     naechsten Auftreten von 'eid' automatisch ausgetragen.
+     Falls Blueprints nach Neuladen nicht automatisch angemeldet sein sollen,
+     sollten sie sich im remove() explizit abmelden.
+
+BEISPIELE:
+     1. Ein Objekt moechte nicht mehr ueber Spielertode informiert werden:
+          EVENTD->UnregisterEvent(EVT_LIB_PLAYER_DEATH, this_object());
+
+     2. Ein Objekt moechte sich bei Zerstoerung abmelden:
+       varargs int remove(int silent) {
+           ...
+           EVENTD->UnregisterEvent("zesstra_vulkanausbruch",this_object());
+       }
+
+SIEHE AUCH:
+     events, eventd, UnregisterEvent(), RegisterEvent()
+
+----------------------------------------------------------------------------
+Last modified: 15.08.2007, Zesstra
diff --git a/doc/lfun/UnregisterHelperNPC b/doc/lfun/UnregisterHelperNPC
new file mode 100644
index 0000000..de384fd
--- /dev/null
+++ b/doc/lfun/UnregisterHelperNPC
@@ -0,0 +1,45 @@
+UnregisterHelperNPC()
+FUNKTION:
+     public int UnregisterHelperNPC(object npc);
+
+DEFINIERT IN:
+     /std/player/combat.c
+
+ARGUMENTE:
+     object npc
+       Objekt des helfenden NPC, der abgemeldet werden soll.
+
+BESCHREIBUNG:
+     Mit dieser Funktion wird ein einem Spieler helfender NPC im Spieler
+     wieder abgemeldet, wenn dieser dem Spieler ab jetzt nicht mehr hilft.
+
+     Wenn ein Helfer-NPC zerstoert wird, ist der Aufruf nicht noetig.
+     Bleibt das Objekt des NPC aber existent, bitte auf jeden Fall wieder
+     ordentlich abmelden, da ansonsten ggf. der Spieler unnoetig blockiert
+     wird.
+
+RUeCKGABEWERT:
+     1, wenn die Abmeldung erfolgreich war.
+     0 sonst, z.B. wenn der NPC gar nicht als Helfer registriert war.
+
+BEMERKUNGEN:
+     Diese Funktion setzt bei der Erfolg die Property P_HELPER_NPC in <npc>
+     auf 0.
+
+BEISPIELE:
+     1. Ein NPC, der dem Spieler nicht mehr helfen will und normalerweisen im
+        Raum verbleiben soll:
+     tell_object(spieler, "Ich mag Dich nicht mehr, Du bist doof!\n");
+     if (spieler->UnregisterHelperNPC(this_object()) != 1) {
+       // das ist ja bloed...
+       remove(0);
+     }
+     else {
+       tell_room(environment(),
+           Name()+" dreht " +spieler->Name(WEM) + " schmollend den Ruecken "
+           "zu.\n");
+     }
+
+SIEHE AUCH:
+    UnregisterHelperNPC()
+    P_HELPER_NPC
diff --git a/doc/lfun/UnregisterHelperObject b/doc/lfun/UnregisterHelperObject
new file mode 100644
index 0000000..eefd84e
--- /dev/null
+++ b/doc/lfun/UnregisterHelperObject
@@ -0,0 +1,47 @@
+FUNKTION:
+     int UnregisterHelperObject(object helper, int type);
+
+DEFINIERT IN:
+     /std/living/helpers.c
+
+ARGUMENTE:
+     object helper
+       Das Objekt, das als Hilfsobjekt deregistriert werden soll.
+     int type
+       Helfertyp, einer der in /sys/living/helpers.h definierten Typen:
+       - HELPER_TYPE_AERIAL fuer die Flug-/Segelunterstuetzung
+       - HELPER_TYPE_AQUATIC fuer Tauchunterstuetzung
+
+BESCHREIBUNG:
+     Das als Hilfsobjekt fuer bestimmte Aktivitaeten wie zum Beispiel Tauchen
+     oder Fliegen bei einem Lebewesen registrierte Objekt "helper" meldet
+     sich bei diesem ab.
+     Hinweis: fuer eine temporaer gueltige "Nicht-Zustaendigkeit" kaeme auch
+     in Frage, in dieser Zeit einfach "0" zurueckzugeben, statt sich
+     komplett abzumelden.
+
+RUECKGABEWERTE:
+      1  Objekt wurde erfolgreich ausgetragen (HELPER_SUCCESS)
+     -1  angegebenes Hilfsobjekt existiert nicht (HELPER_NO_CALLBACK_OBJECT)
+     -3  angegebenes Hilfsobjekt war gar nicht angemeldet
+         (HELPER_NOTHING_TO_UNREGISTER)
+
+BEISPIEL:
+     Eine luftgefuellte Blase hatte sich als Tauch-Helfer am Spieler
+     angemeldet, ist jetzt aber verbraucht und meldet sich daher ab:
+
+     // Austragen im Spielerobjekt
+     void BlaseAustragen() {
+       [...]
+       if ( TP->UnregisterHelperObject(ME, HELPER_TYPE_AQUATIC)
+            == HELPER_SUCCESS )
+         remove();
+     }
+
+SIEHE AUCH:
+     Funktionen:  RegisterHelperObject()
+     Properties:  P_HELPER_OBJECTS, P_AERIAL_HELPERS, P_AQUATIC_HELPERS
+     Sonstiges:   /sys/living/helpers.h
+
+19.02.2013 Arathorn
+
diff --git a/doc/lfun/Unwear b/doc/lfun/Unwear
new file mode 100644
index 0000000..624c7fc
--- /dev/null
+++ b/doc/lfun/Unwear
@@ -0,0 +1,38 @@
+Unwear()
+FUNKTION:
+     public int Unwear(object ob) 
+
+DEFINIERT IN:
+     /std/living/clothing.c
+
+ARGUMENTE:
+     object ob
+       Die Ruestung oder Kleidung, die ausgezogen wird.
+
+BESCHREIBUNG:
+     Das Lebewesen, in dem diese Funktion gerufen wird, zieht die Ruestung
+     oder das Kleidungsstueck <ob> aus.
+     ABER: 'Ausziehen' bedeutet in diesem Kontext lediglich, dass die
+     Ruestung/Kleidung aus P_ARMOURS bzw. P_CLOTHING ausgetragen wird. Es
+     finden zur Zeit keine Pruefungen statt, ob das Lebewesen den Gegenstand
+     ueberhaupt ausziehen kann. Genausowenig werden Funktionen wie
+     InformUnwear()/RemoveFunc() gerufen oder etwaige Stat-Boni deaktiviert.
+
+     Die Funktion ist nur dazu gedacht, im Zuge des Ausziehens eines Objekts
+     von diesem im Lebewesen gerufen zu werden.
+
+RUeCKGABEWERT:
+     1, wenn das Ausziehen erfolgreich war.
+     0 sonst.
+
+BEMERKUNGEN:
+     Nicht von Hand aufrufen, es sei denn man weiss genau, was man tut. Und am
+     besten auch dann nicht.
+
+SIEHE AUCH:
+     Wear(), WearArmour(), WearClothing(), UnwearArmour(), UnwearClothing()
+     P_CLOTHING, P_ARMOURS
+     FilterClothing(), FilterArmours()
+
+ZULETZT GEAeNDERT:
+14.03.2009, Zesstra
diff --git a/doc/lfun/UnwearArmour b/doc/lfun/UnwearArmour
new file mode 100644
index 0000000..86b55da
--- /dev/null
+++ b/doc/lfun/UnwearArmour
@@ -0,0 +1,38 @@
+UnwearArmour()
+FUNKTION:
+     public int UnwearArmour(object ob) 
+
+DEFINIERT IN:
+     /std/living/clothing.c
+
+ARGUMENTE:
+     object ob
+       Die Ruestung, die ausgezogen wird.
+
+BESCHREIBUNG:
+     Das Lebewesen, in dem diese Funktion gerufen wird, zieht die Ruestung
+     <ob> aus.
+     ABER: 'Ausziehen' bedeutet in diesem Kontext lediglich, dass die
+     Ruestung/Kleidung aus P_ARMOURS ausgetragen wird. Es finden zur Zeit
+     keine Pruefungen statt, ob das Lebewesen den Gegenstand ueberhaupt
+     ausziehen kann. Genausowenig werden Funktionen wie
+     InformUnwear()/RemoveFunc() gerufen oder etwaige Stat-Boni deaktiviert.
+
+     Die Funktion ist nur dazu gedacht, im Zuge des Ausziehens eines Objekts
+     von diesem im Lebewesen gerufen zu werden.
+
+RUeCKGABEWERT:
+     1, wenn das Ausziehen erfolgreich war.
+     0 sonst.
+
+BEMERKUNGEN:
+     Nicht von Hand aufrufen, es sei denn man weiss genau, was man tut. Und am
+     besten auch dann nicht.
+
+SIEHE AUCH:
+     Wear(), WearArmour(), WearClothing(), Unwear(), UnwearClothing()
+     P_CLOTHING, P_ARMOURS
+     FilterClothing(), FilterArmours()
+
+ZULETZT GEAeNDERT:
+14.03.2009, Zesstra
diff --git a/doc/lfun/UnwearClothing b/doc/lfun/UnwearClothing
new file mode 100644
index 0000000..0556654
--- /dev/null
+++ b/doc/lfun/UnwearClothing
@@ -0,0 +1,38 @@
+UnwearClothing()
+FUNKTION:
+     public int UnwearClothing(object ob) 
+
+DEFINIERT IN:
+     /std/living/clothing.c
+
+ARGUMENTE:
+     object ob
+       Das Kleidungsstuck, das ausgezogen wird.
+
+BESCHREIBUNG:
+     Das Lebewesen, in dem diese Funktion gerufen wird, zieht das
+     Kleidungsstueck <ob> aus.
+     ABER: 'Ausziehen' bedeutet in diesem Kontext lediglich, dass die
+     Ruestung/Kleidung aus P_CLOTHING ausgetragen wird. Es finden zur Zeit
+     keine Pruefungen statt, ob das Lebewesen den Gegenstand ueberhaupt
+     ausziehen kann. Genausowenig werden Funktionen wie
+     InformUnwear()/RemoveFunc() gerufen.
+
+     Die Funktion ist nur dazu gedacht, im Zuge des Ausziehens eines Objekts
+     von diesem im Lebewesen gerufen zu werden.
+
+RUeCKGABEWERT:
+     1, wenn das Ausziehen erfolgreich war.
+     0 sonst.
+
+BEMERKUNGEN:
+     Nicht von Hand aufrufen, es sei denn man weiss genau, was man tut. Und am
+     besten auch dann nicht.
+
+SIEHE AUCH:
+     Wear(), WearArmour(), WearClothing(), Unwear(), UnwearArmour()
+     P_CLOTHING, P_ARMOURS
+     FilterClothing(), FilterArmours()
+
+ZULETZT GEAeNDERT:
+14.03.2009, Zesstra
diff --git a/doc/lfun/UnwieldFunc b/doc/lfun/UnwieldFunc
new file mode 100644
index 0000000..9e4929c
--- /dev/null
+++ b/doc/lfun/UnwieldFunc
@@ -0,0 +1,52 @@
+UnwieldFunc()
+
+FUNKTION:
+     int UnwieldFunc(object weapon, int info, object user);
+
+DEFINIERT IN:
+     eigenen Objekten, fuer /std/weapon/combat.c
+
+ARGUMENTE:
+     weapon (object)
+          Die Waffe, die weggesteckt werden soll.
+     info (int)
+          Bei (info&M_SILENT) wird keine Meldung ueber das Wegstecken
+          ausgegeben.
+          Bei (info&M_NOCHECK) wird die Waffe auch weggesteckt, wenn
+          sie verflucht ist. Die tritt insbesondere dann auf, wenn der
+          Spieler, der die Waffe benutzt, stirbt und die Waffe in
+          die Leiche bewegt wird.
+     user (object)
+          Das Lebewesen, welches die Waffe gerade gezueckt hat und sie nun
+          ausziehen will.
+
+BESCHREIBUNG:
+     Hier koennen zusaetzliche Abfragen vorgenommen werden, ob sich die
+     Waffe <weapon> wegstecken laesst oder nicht.
+
+RUeCKGABEWERT:
+     0, wenn sich die Waffe nicht wegstecken laesst, ansonsten ungleich 0.
+
+BEMERKUNGEN:
+     Verfluchte Waffen, die sich erst nach Entfernung des Fluches wegstecken
+     lassen, sollte man besser mit P_CURSED realisieren.
+     Selbst wenn man einen Wert ungleich Null zurueckgibt, ist das noch
+     keine Garantie, dass sich die Waffe auch wirklich zuecken laesst! Der
+     Spieler koennte zum Beispiel noch eine Waffe gezueckt haben, die sich
+     nicht wegstecken laesst, etc.
+     Wenn ihr sicher sein wollt, dass der Spieler ein Objekt gezueckt hat,
+     benutzt bitte InformWear().
+     Bitte nicht drauf verlassen, dass this_player() das Lebewesen ist,
+     welches die Waffe gezueckt und wegstecken will.
+     Die Reihenfolge der Argumente ist etwas unschoen, aber leider wurde <user>
+     erheblich spaeter hinzugefuegt und es war unmoeglich, einige hundert
+     Objekte zu aendern.
+
+SIEHE AUCH
+     P_WIELD_MSG, P_UNWIELD_MSG, P_WEAR_MSG, P_UNWEAR_MSG
+     DoWield(), DoUnwield(), InformWield(), InformUnwield(), 
+     UnwieldFunc, WieldFunc() 
+     /std/weapon/combat.c
+
+----------------------------------------------------------------------------
+02.02.2009, Zesstra
diff --git a/doc/lfun/UpdateAttributes b/doc/lfun/UpdateAttributes
new file mode 100644
index 0000000..ef5a24f
--- /dev/null
+++ b/doc/lfun/UpdateAttributes
@@ -0,0 +1,32 @@
+UpdateAttributes()
+FUNKTION:
+     void UpdateAttributes()
+
+DEFINIERT IN:
+     /std/living/attributes.c
+
+BESCHREIBUNG:
+     Rechnet damit alle Attributmodifier der im Inventory befindlichen 

+     (P_X_ATTR_MOD, P_X_HEALTH_MOD) und getragenen/gezueckten 

+     (P_M_HEALTH_MOD, P_M_ATTR_MOD) Objekte und aller Attributoffsets 

+     zusammen und speichert sie in einer intern fuer Attribute 

+     verwendete Variablen.
+     Berechnet darauf basierend HP und SP neu.
+     

+     Die Bedingungen fuer die ueber P_TIMED_ATTR_MOD gesetzten 

+     Attributveraenderungen werden im Heartbeat in der Funktion

+     attribute_hb ueberprueft.

+
+BEMERKUNGEN:
+     Sollte nach Einbringen neuer Modifikatorobjekte am Living gerufen
+     werden.
+
+SIEHE AUCH:

+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+	SetTimedAttrModifier(), QueryTimedAttrModifier(), 

+	DeleteTimedAttrModifier(),

+	P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_TIMED_ATTR_MOD,

+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+----------------------------------------------------------------------------

+09.05.2007 by Zesstra

diff --git a/doc/lfun/UpdateResistanceStrengths b/doc/lfun/UpdateResistanceStrengths
new file mode 100644
index 0000000..48747b3
--- /dev/null
+++ b/doc/lfun/UpdateResistanceStrengths
@@ -0,0 +1,26 @@
+UpdateResistanceStrengths()
+
+FUNKTION:
+     public void UpdateResistanceStrengths()
+
+DEFINIERT IN:
+     /std/living/combat.c
+
+BESCHREIBUNG:
+     Die Funktion wird intern mehrmals (durch Defend, AddResistanceModifier
+     und RemoveResistanceModifier) aufgerufen. In ihr wird das Resistenz-
+     mapping zusammengerechnet und Eintraege geloescht, deren Eintraege
+     invalid sind oder deren setzende Objekte geloescht wurden.
+
+RUeCKGABEWERT:
+     keiner
+
+SIEHE AUCH:
+     Berechnung:	CheckResistance(), Defend()
+     Modifikatoren:	AddResistanceModifier, RemoveResistanceModifier(),
+			P_RESISTANCE_MODIFIER
+     simple Resistenz:	P_RESISTANCE, P_VULNERABILITY
+     Hauptmapping:	P_RESISTANCE_STRENGTHS
+     anderes:		balance, /std/armour/combat.c, /std/living/combat.c
+
+29.Apr 2002, Gloinson@MG
diff --git a/doc/lfun/UseHands b/doc/lfun/UseHands
new file mode 100644
index 0000000..a4fbc8d
--- /dev/null
+++ b/doc/lfun/UseHands
@@ -0,0 +1,37 @@
+UseHands
+FUNKTION:
+     public varargs int UseHands(object ob, int num)
+
+DEFINIERT IN:
+     /std/living/combat.c
+
+ARGUMENTE:
+     ob  - das Objekt, das die Haende belegen soll
+     num - die Anzahl der zu belegenden Haende    
+
+RUECKGABEWERT:
+     1, fuer Erfolg
+     0, sonst     
+
+BESCHREIBUNG:
+     Belegt, wenn moeglich Haende eines Livings durch ein bestimmtes
+     Objekt. Wenn die Anzahl der freien Haende (P_MAX_HANDS-P_USED_HANDS)
+     kleiner ist als "num", dann schlaegt diese Belegung fehl.
+
+BEISPIELE:
+     > halte seil fest
+     ...
+     this_player()->UseHands(this_object(),2);
+     ...
+
+     > lasse seil los
+     ...
+     this_player()->FreeHands(this_object());
+     ...
+
+SIEHE AUCH:
+     P_HANDS, P_HANDS_USED_BY
+     P_MAX_HANDS, P_USED_HANDS, P_FREE_HANDS
+     FreeHands
+
+1.Feb.2004 Gloinson
diff --git a/doc/lfun/UseSkill b/doc/lfun/UseSkill
new file mode 100644
index 0000000..3b12bd8
--- /dev/null
+++ b/doc/lfun/UseSkill
@@ -0,0 +1,48 @@
+UseSkill()
+FUNKTION:
+    public varargs mixed UseSkill(string skill, mapping args)
+
+DEFINIERT IN:
+    /std/living/skills.c
+
+ARGUMENTE:
+    string skill     Skill-Name
+    mapping args     Argumente (veraenderte Skillmapping-Informationen)
+
+BESCHREIBUNG:
+    Benutzt einen Skill. Dieser Skill sollte (als grossgeschriebener Skill)
+    im Living vorliegen und das Living darf kein Geist sein.
+    
+    Die Argumente 'args' werden temporaer auf das Skillmapping des Living
+    addiert (also nur fuer diesen Aufruf und SI_INHERIT gueltig).
+    
+    Eine ausfuehrbare Skill-Funktion zum Skill wird in folgender
+    Reihenfolge bestimmt:
+    - eine gesetzte SI_CLOSURE nutzen
+    - ein gesetztes SI_SKILLFUNC in der gesetzten Gilde nutzen
+    - im Living die Funktion "StdSkill_"+skill (zB Waffenskills) nutzen
+    - QuerySkillAbility() nutzen
+    Die so bestimmte Skill-Funktion wird dann als SI_CLOSURE im Spieler
+    gesetzt und ist bis zur Zerstoerung der entsprechenden Objekte gueltig.
+    Die Methode wird dann gerufen (der Skill also angewandt).
+    
+    Standardmaessig gibt ein UseSkill() also einfach den SI_SKILLABILITY-Wert
+    eines Skills zurueck, es sei denn, eine Funktion wurde fuer den Skill
+    an einer der oben genannten Stellen implementiert.
+    
+    Ein eventuell uebergeordneter Skill (SI_INHERIT) wird mit dem durch den
+    Aufruf der Skill-Funktion veraenderten Mapping mit UseSkill(skill, args)
+    ebenfalls noch ausgefuehrt, bevor das Resultat zurueckgegeben wird.
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSpell
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung, skill_info_liste
+    * Properties:   P_NEWSKILLS
+    Spellbook:      Learn, SpellSuccess, Erfolg, Misserfolg
+
+4. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/UseSpell b/doc/lfun/UseSpell
new file mode 100644
index 0000000..ddf1c4d
--- /dev/null
+++ b/doc/lfun/UseSpell
@@ -0,0 +1,77 @@
+UseSpell()
+FUNKTION:
+    public varargs int UseSpell(string str, string spell)
+
+DEFINIERT IN:
+    /std/living/skills.c
+
+ARGUMENTE:
+    string str       Spell-Optionen
+    string spell     optionaler Spellname
+
+BESCHREIBUNG:
+    Benutzt einen Spell, dessen Spellname 'spell' ggf ueber query_verb()
+    ermittelt wird. Dieser Spell sollte (als kleingeschriebener
+    Skill/Spell) im Living vorliegen.
+    
+    Die Argumente 'str' werden als SI_SKILLARG temporaer in das
+    Skillmapping eingetragen (also nur fuer diesen Aufruf gueltig).
+    
+    Eine ausfuehrbare Spell-Funktion zum Spell wird in folgender
+    Reihenfolge bestimmt:
+    - eine gesetzte SI_CLOSURE nutzen
+    - "UseSpell" an einem gesetzten SI_SPELLBOOK nutzen
+    - "UseSpell" an der gesetzten P_GUILD nutzen
+      - [UseSpell der Gilde sucht iA ebenfalls nur den Spell am Spellbook]
+    - eine Closure mit Rueckgabewert 0 erstellen
+    Die so bestimmte Spell-Funktion wird dann als SI_CLOSURE im Spieler
+    gesetzt und ist bis zur Zerstoerung der entsprechenden Objekte gueltig.
+    Die Methode wird dann gerufen (der Spell also angewandt).
+
+    Standardmaessig gibt ein UseSpell() also 0 zurueck, es sei denn, eine
+    Funktion wurde fuer den Spell an einer der oben genannten Stellen
+    implementiert.
+
+    SI_INHERIT ist fuer Spells beim Aufruf wirkungslos (gilt aber bei
+    LearnSkill normal).
+
+    Ein Durchlauf von UseSpell durch den Spieler sieht in etwa so aus:
+      1) Die Methode wird als Empfaenger fuer Kommandos bekannt gemacht.
+         Das passiert mit der Anweisung
+         'add_action("UseSpell", "", 1);' in den Dateien player/base.c und
+         in living/npc.c.
+
+      2) /std/living/skills::UseSpell wird durch Kommando oder Heartbeat
+         gerufen.
+      
+      3) UseSpell() ermittelt eine SI_CLOSURE oder delegiert diesen Aufruf
+         an die gueltige Gilde/das Spellbook weiter.
+      
+      4) Eine gueltige Closure wird ausgefuehrt. UseSpell() uebergibt dabei
+         die Spell/Skill-Informationen und SI_SKILLARG.
+      
+      4.1.) Im Normalfall einer Gilde/Spellbook landet der Aufruf ueber
+            /std/gilden_ob::UseSpell() in /std/spellbook::UseSpell() und
+            dieses ruft eine Spellfunktion im Spellbook auf.
+            Die Spellfunktion arbeitet mit den Spell-Informationen und
+            gibt ein ERFOLG, MISSERFOLG oder 0 dafuer zurueck, ob das
+            Spellbook Lernen/Fehlermeldung oder nichts machen soll.
+            Dementsprechend werden P_SP, P_ATTACK_BUSY und
+            P_NEXT_SPELL_TIME im Spellbook geaendert.
+
+      5.) Der Aufruf der Closure kehrt zurueck und wird zurueckgegeben.
+          Damit ist der 0-Rueckgabewert der Spellfunktion im Spellbook
+          aequivalent einem nicht ausgefuehrten Kommando.
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSkill
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung, skill_info_liste
+    * Properties:   P_NEWSKILLS
+    Spellbook:      UseSpell (spellbook), Learn, SpellSuccess
+
+5. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/Validate b/doc/lfun/Validate
new file mode 100644
index 0000000..10f4791
--- /dev/null
+++ b/doc/lfun/Validate
@@ -0,0 +1,47 @@
+Validate()
+
+FUNKTION:
+   string Validate(string oname);
+
+DEFINIERT IN:
+   /std/virtual/v_compiler.c
+
+ARGUMENTE:
+   oname
+       Objektname, der geprueft werden soll 
+
+RUeCKGABEWERT:
+   
+BESCHREIBUNG:
+   Diese Funktion hat die Aufgabe zu ueberpruefen ob ein Objekt welches
+   geladen werden soll, in dem VC  ueberhaupt erlaubt ist. Dieser 
+   Funktion wird nur der reine Filename uebergeben, ohne Pfad!
+   Diese Funktion macht im Standard-VC in /std/ nichts weiter, als
+   das '.c' am File Namen abzuschneiden.
+   Sollte der Dateiname gueltig sein liefert die Funktion als Rueckgabewert
+   den Filenamen ohne .c und sonst 0.
+
+BEMERKUNGEN:
+   Am besten ruft man in seinem Validate() das ::Validate(), was einem die
+   Arbeit abnimmt, ein .c am Ende zu entfernen.
+
+BEISPIEL:
+   string Validate(string oname) {
+     string raum, spieler;
+     //.c abschneiden
+     oname=::Validate(oname);
+     
+     // folgt der Raum dem Muster "arena|name"? Wenn nein -> ungueltig,
+     // 0 zureckgeben, sonst den Filenamen.
+     if(sscanf(oname,"%s|%s",raum,spieler)<2 || raum!="arena")
+        return 0;
+     return oname;
+   }
+
+SIEHE AUCH:
+     virtual_compiler
+     CustomizeObject(), Validate(), NoParaObjects(), 
+     P_COMPILER_PATH, P_PARA
+     /std/virtual/v_compiler.c
+----------------------------------------------------------------------------
+27.10.2007, Zesstra
diff --git a/doc/lfun/Wear b/doc/lfun/Wear
new file mode 100644
index 0000000..bfc8ad2
--- /dev/null
+++ b/doc/lfun/Wear
@@ -0,0 +1,37 @@
+Wear()
+FUNKTION:
+     public int Wear(object ob) 
+
+DEFINIERT IN:
+     /std/living/clothing.c
+
+ARGUMENTE:
+     object ob
+       Die Ruestung oder Kleidung, die angezogen wird.
+
+BESCHREIBUNG:
+     Das Lebewesen, in dem diese Funktion gerufen wird, zieht die Ruestung
+     oder das Kleidungsstueck <ob> an.
+     ABER: 'Anziehen' bedeutet in diesem Kontext lediglich, dass die
+     Ruestung/Kleidung in P_ARMOURS bzw. P_CLOTHING eingetragen wird. Es
+     finden zur Zeit keine Pruefungen statt, ob das Lebewesen den Gegenstand
+     ueberhaupt anziehen kann. Genausowenig werden Funktionen wie InformWear()
+     gerufen oder etwaige Stat-Boni aktiviert.
+     Die Funktion ist nur dazu gedacht, im Zuge des Anziehens eines Objekts
+     von diesem im Lebewesen gerufen zu werden.
+
+RUeCKGABEWERT:
+     1, wenn das Anziehen erfolgreich war.
+     0 sonst.
+
+BEMERKUNGEN:
+     Nicht von Hand aufrufen, es sei denn man weiss genau, was man tut. Und am
+     besten auch dann nicht.
+
+SIEHE AUCH:
+     WearArmour(), WearClothing(), Unwear(), UnwearArmour(), UnwearClothing()
+     P_CLOTHING, P_ARMOURS
+     FilterClothing(), FilterArmours()
+
+ZULETZT GEAeNDERT:
+14.03.2009, Zesstra
diff --git a/doc/lfun/WearArmour b/doc/lfun/WearArmour
new file mode 100644
index 0000000..9fd8e95
--- /dev/null
+++ b/doc/lfun/WearArmour
@@ -0,0 +1,37 @@
+WearArmour()
+FUNKTION:
+     public int WearArmour(object ob) 
+
+DEFINIERT IN:
+     /std/living/clothing.c
+
+ARGUMENTE:
+     object ob
+       Die Ruestung, die angezogen wird.
+
+BESCHREIBUNG:
+     Das Lebewesen, in dem diese Funktion gerufen wird, zieht die Ruestung
+     <ob> an.
+     ABER: 'Anziehen' bedeutet in diesem Kontext lediglich, dass die
+     Ruestung/Kleidung in P_ARMOURS eingetragen wird. Es finden zur Zeit keine
+     Pruefungen statt, ob das Lebewesen den Gegenstand ueberhaupt anziehen
+     kann. Genausowenig werden Funktionen wie InformWear() gerufen oder
+     etwaige Stat-Boni aktiviert.
+     Die Funktion ist nur dazu gedacht, im Zuge des Anziehens eines Objekts
+     von diesem im Lebewesen gerufen zu werden.
+
+RUeCKGABEWERT:
+     1, wenn das Anziehen erfolgreich war.
+     0 sonst.
+
+BEMERKUNGEN:
+     Nicht von Hand aufrufen, es sei denn man weiss genau, was man tut. Und am
+     besten auch dann nicht.
+
+SIEHE AUCH:
+     Wear(), WearClothing(), Unwear(), UnwearArmour(), UnwearClothing()
+     P_CLOTHING, P_ARMOURS
+     FilterClothing(), FilterArmours()
+
+ZULETZT GEAeNDERT:
+14.03.2009, Zesstra
diff --git a/doc/lfun/WearClothing b/doc/lfun/WearClothing
new file mode 100644
index 0000000..17c0280
--- /dev/null
+++ b/doc/lfun/WearClothing
@@ -0,0 +1,37 @@
+WearClothing()
+FUNKTION:
+     public int WearClothing(object ob) 
+
+DEFINIERT IN:
+     /std/living/clothing.c
+
+ARGUMENTE:
+     object ob
+       Das Kleidungsstuck, das angezogen wird.
+
+BESCHREIBUNG:
+     Das Lebewesen, in dem diese Funktion gerufen wird, zieht das
+     Kleidungsstueck <ob> an.
+     ABER: 'Anziehen' bedeutet in diesem Kontext lediglich, dass die
+     Ruestung/Kleidung in P_CLOTHING eingetragen wird. Es finden zur Zeit
+     keine Pruefungen statt, ob das Lebewesen den Gegenstand ueberhaupt
+     anziehen kann. Genausowenig werden Funktionen wie InformWear() gerufen.
+
+     Die Funktion ist nur dazu gedacht, im Zuge des Anziehens eines Objekts
+     von diesem im Lebewesen gerufen zu werden.
+
+RUeCKGABEWERT:
+     1, wenn das Anziehen erfolgreich war.
+     0 sonst.
+
+BEMERKUNGEN:
+     Nicht von Hand aufrufen, es sei denn man weiss genau, was man tut. Und am
+     besten auch dann nicht.
+
+SIEHE AUCH:
+     Wear(), WearArmour(), Unwear(), UnwearArmour(), UnwearClothing()
+     P_CLOTHING, P_ARMOURS
+     FilterClothing(), FilterArmours()
+
+ZULETZT GEAeNDERT:
+14.03.2009, Zesstra
diff --git a/doc/lfun/WearFunc b/doc/lfun/WearFunc
new file mode 100644
index 0000000..626fd64
--- /dev/null
+++ b/doc/lfun/WearFunc
@@ -0,0 +1,80 @@
+WearFunc()
+
+FUNKTION:
+     int WearFunc(object ruest, int silent, object user);
+
+DEFINIERT IN:
+     eigenen Objekten (fuer /std/clothing/wear)
+
+ARGUMENTE:
+     ruest (object)
+          Die Ruestung/Kleidung, die angezogen werden soll.
+     silent (int)
+          Ob dabei eine Meldung ausgegeben wird.
+     user (object)
+          Das Lebewesen, welches die Ruestung/Kleidung anziehen will.
+
+BESCHREIBUNG:
+     Mit dieser Funktion kann man pruefen, ob sich das Kleidungsstueck bzw.
+     Ruestung <ruest> von this_player() anziehen laesst oder nicht.
+     Kann die Ruestung angezogen werden, so muss ein Wert ungleich 0
+     zurueckgegeben werden.
+
+RUeCKGABEWERT:
+     0, wenn sich die Ruestung nicht anziehen laesst, ansonsten ungleich 0.
+
+BEMERKUNGEN:
+     Bitte nicht darauf verlassen, dass der Spieler das Objekt auch wirklich
+     anzieht, wenn man hier 1 zurueckgibt.
+     Speziell bei Schilden kann das Anziehen trotz eines Rueckgabewertes 
+     != 0 immer noch schief gehen, wenn der Spieler keine Hand mehr frei hat.
+     Wenn ihr sicher sein wollt, dass der Spieler ein Objekt angezogen hat,
+     benutzt bitte InformWear().
+     Bitte nicht drauf verlassen, dass this_player() das ausziehende Lebewesen
+     ist.
+     Die Reihenfolge der Argumente ist etwas unschoen, aber leider wurde <user>
+     erheblich spaeter hinzugefuegt und es war unmoeglich, einige hundert
+     Objekte zu aendern.
+
+BEISPIELE:
+     Ein Helm, der nur von Elfen getragen werden kann:
+
+     inherit "std/armour.c";
+
+     #include <properties.h>
+
+     create()
+     {
+       ::create();
+
+       SetProp(P_ARMOUR_TYPE, AT_HELMET);
+       /* zig weitere SetProp's, um den Helm zu konfigurieren */
+
+       /* WearFunc() ist im Helm selbst zu finden */
+       SetProp(P_WEAR_FUNC, this_object());
+     }
+
+     int WearFunc(object me, int silent, object user)
+     {
+       if (user->QueryProp(P_RACE) == "Elf")
+         return 1;   /* Elfen duerfen den Helm tragen */
+
+       /* Die anderen Rassen sollten zumindest erfahren koennen, wieso
+          sie den Helm nicht tragen koennen... */
+       if (!silent)
+           write( "Der Helm rutscht Dir immer ueber Deine runden "
+                 +"Ohren.\n" );
+       return 0;
+     }
+
+     Gibt jetzt ein Nicht-Elf "trage helm" ein, so bekommt er die Meldung
+     "Der Helm rutscht Dir immer ueber Deine runden Ohren.", Elfen dagegen
+     passt das Teil wie angegossen.
+
+SIEHE AUCH:
+     P_WEAR_MSG, P_UNWEAR_MSG, P_WIELD_MSG, P_UNWIELD_MSG
+     DoWear(), DoUnwear(), InformUnwear(), InformWear()
+     /std/clothing/wear.c
+
+----------------------------------------------------------------------------
+02.02.2009, Zesstra
diff --git a/doc/lfun/WieldFunc b/doc/lfun/WieldFunc
new file mode 100644
index 0000000..23e299f
--- /dev/null
+++ b/doc/lfun/WieldFunc
@@ -0,0 +1,77 @@
+WieldFunc()
+
+FUNKTION:
+     int WieldFunc(object weapon, int silent, object user);
+
+DEFINIERT IN:
+     eigenen Objekten (fuer /std/weapon/combat)
+
+ARGUMENTE:
+     weapon (object)
+          Die Waffe, die gezueckt werden soll.
+     silent (int)
+          Ob dabei eine Meldung ausgegeben werden soll.
+     user (object)
+          Das Lebewesen, welches die Waffe zuecken will.
+
+BESCHREIBUNG:
+     In dieser Funktion kann man zusaetzliche Abfragen vornehmen, ob sich
+     die Waffe <weapon> von <user> zuecken laesst oder nicht.
+
+RUeCKGABEWERT:
+     0, wenn die Waffe nicht gezueckt werden kann, sonst ungleich 0.
+
+BEMERKUNGEN:
+     Selbst wenn man einen Wert ungleich Null zurueckgibt, ist das noch
+     keine Garantie, dass sich die Waffe auch wirklich zuecken laesst! Der
+     Spieler koennte zum Beispiel noch eine Waffe gezueckt haben, die sich
+     nicht wegstecken laesst, etc.
+     Wenn ihr sicher sein wollt, dass der Spieler ein Objekt gezueckt hat,
+     benutzt bitte InformWield().
+     Bitte nicht drauf verlassen, dass this_player() das Lebewesen ist,
+     welches die Waffe zuecke will.
+     Die Reihenfolge der Argumente ist etwas unschoen, aber leider wurde <user>
+     erheblich spaeter hinzugefuegt und es war unmoeglich, einige hundert
+     Objekte zu aendern.
+
+BEISPIELE:
+     Eine Waffe, die sich nicht von Zwergen zuecken laesst:
+
+     inherit "std/weapon";
+
+     #include <properties.h>
+     #include <combat.h>
+
+     create()
+     {
+       ::create();
+
+       ... /* zig SetProp's, um die Waffe zu konfigurieren */
+
+       /* WieldFunc() ist in der Waffe selbst zu finden */
+       SetProp(P_WIELD_FUNC, this_object());
+     }
+
+     int WieldFunc(object weapon, int silent, object user)
+     {
+       /* Nicht-Zwerge duerfen die Waffe zuecken */
+       if (user->QueryProp(P_RACE) != "Zwerg")
+         return 1;
+
+       /* Ansonsten sagen wir evtl., warum das Zuecken nicht klappt... */
+       if (!silent)
+         write( "Deine kleinen Haendchen koennen den Griff nicht "+
+                "umklammern.\n");
+
+       /* ...und brechen das Zuecken ab. */
+       return 0;
+     }
+
+SIEHE AUCH:
+     P_WIELD_MSG, P_UNWIELD_MSG, P_WEAR_MSG, P_UNWEAR_MSG
+     DoWield(), DoUnwield(), InformUnwield(), InformWield() 
+     UnwieldFunc, WieldFunc 
+     /std/weapon/combat.c
+
+---------------------------------------------------------------------------
+02.02.2009, Zesstra
diff --git a/doc/lfun/WithDraw b/doc/lfun/WithDraw
new file mode 100644
index 0000000..d1532a2
--- /dev/null
+++ b/doc/lfun/WithDraw
@@ -0,0 +1,42 @@
+WithDraw()
+FUNKTION:
+     int WithDraw(int amount);
+
+DEFINIERT IN:
+     /p/daemon/zentralbank.c
+
+ARGUMENTE:
+     int amount	- angeforderte Geldmenge
+
+BESCHREIBUNG:
+     Damit wird bei der Zentralbank eine bestimmte Menge Geld angefordert.
+     Der Rueckgabewert haengt vom Kassenstand der Zentralbank ab und ist
+     entweder amount oder amount/3.
+
+RUeCKGABEWERT:
+     Menge des bei der Zentralbank "abgehobenen" Geldes.
+
+BEISPIELE:
+     #include <bank.h>
+     ...
+     if(ZENTRALBANK->WithDraw(50000)<50000)
+      write(break_string(
+       "Leider koennen wir ihnen keinen vollen Zuschuss zu ihrem Hotelbau "
+       "geben!",
+       "Der Beamte sagt: ",78));
+      say(break_string(
+       "Leider koennen wir ihnen keinen vollen Zuschuss zu ihrem Hotelbau "
+       "geben!",
+       "Der Beamte sagt zu "+this_player()->name(WEM)+": ",78));
+      ...
+
+BEMERKUNGEN:
+     Unsere Zentralbank ist korrupt, vor allem dadurch, dass in Laeden und
+     an anderen Stellen Geld erzeugt wird.
+
+SIEHE AUCH:
+     Geldhandling:	AddMoney(L), QueryMoney(L)
+     Zentralbank:	PayIn(L), _query_current_money(L)
+     Sonstiges:		/items/money.c, /sys/bank.h
+
+27. Apr 2004 Gloinson
diff --git a/doc/lfun/__INIT b/doc/lfun/__INIT
new file mode 100644
index 0000000..208b655
--- /dev/null
+++ b/doc/lfun/__INIT
@@ -0,0 +1,12 @@
+SYNOPSIS:
+	__INIT
+
+DESCRIPTION:
+	This function is constructed automagically by the parser at
+	compiler, if the parser was compiled with #define
+	INITIALISATION__INIT. This function is not intended to be
+	defined by the lpc objects, and never to be called from lpc
+	objects. This man page is here just for completeness.
+
+SEE ALSO:
+	initialisation(LPC)
diff --git a/doc/lfun/_query_current_money b/doc/lfun/_query_current_money
new file mode 100644
index 0000000..16b7ee5
--- /dev/null
+++ b/doc/lfun/_query_current_money
@@ -0,0 +1,32 @@
+_query_current_money()
+FUNKTION:
+     int _query_current_money()
+
+DEFINIERT IN:
+     /p/daemon/zentralbank.c
+
+BESCHREIBUNG:
+     Es wird zurueckgegeben, wieviel Geld die Zentralbank besitzt.
+
+BEISPIELE:
+     #include <bank.h>
+     ...
+     if(ZENTRALBANK->_query_current_money()<30000) {
+      write(break_string(
+       "Leider koennen wir ihren Bausparvertrag derzeit nicht einloesen.",
+       "Der Beamte sagt: ",78));
+      say(break_string(
+       "Leider koennen wir ihren Bausparvertrag derzeit nicht einloesen.",
+       "Der Beamte sagt zu "+this_player()->name(WEM)+": ",78));
+     }
+
+BEMERKUNGEN:
+     Unsere Zentralbank ist korrupt, vor allem dadurch, dass in Laeden und
+     an anderen Stellen Geld erzeugt wird.
+
+SIEHE AUCH:
+     Geldhandling:	AddMoney(L), QueryMoney(L)
+     Zentralbank:	WithDraw(L), PayIn(L)
+     Sonstiges:		/items/money.c, /sys/bank.h
+
+27. Apr 2004 Gloinson
diff --git a/doc/lfun/_unparsed_args b/doc/lfun/_unparsed_args
new file mode 100644
index 0000000..4e527b8
--- /dev/null
+++ b/doc/lfun/_unparsed_args
@@ -0,0 +1,29 @@
+_unparsed_args()
+FUNKTION:
+     varargs string _unparsed_args(int level)
+
+DEFINIERT IN:
+     /std/player/command.c:
+
+ARGUMENTE:
+     level
+          Gibt an, wieviel des Textes "weggeparsed" werden soll.
+
+BESCHREIBUNG:
+     Gibt den Text des letzten Befehls des Spielers (ohne das Befehlswort
+     selbst) zurueck. "level" gibt dabei an, wieviel geparsed wird:
+     level = 0 : Gar kein Parsing.
+                 -> entspricht einem query_command()
+     level = 1 : Der Text wird in Kleinbuchstaben umgewandelt, doppelte
+                 Leerzeichen werden entfernt.
+     level = 2 : Semikoli, Kommata und Doppelpunkte werden in Leerzeichen
+		 umgewandelt, doppelte Leerzeichen und Artikel (der, die,
+		 das, ein,...) werden entfernt.
+
+RUeCKGABEWERT:
+     Der geparste Text.
+
+SIEHE AUCH:
+     query_command()
+
+7.Aug 2007 Gloinson
diff --git a/doc/lfun/access_rights b/doc/lfun/access_rights
new file mode 100644
index 0000000..9859761
--- /dev/null
+++ b/doc/lfun/access_rights
@@ -0,0 +1,75 @@
+access_rights()
+
+FUNKTION:
+    int access_rights(string euid, string file);
+
+DEFINIERT IN:
+    access_rights.c (muss man selber schreiben)
+
+PARAMETER:
+    euid
+        Die euid desjenigen, der auf das Verzeichnis schreiben will
+    file
+        Name der Datei, auf die zugegriffen werden soll
+
+BESCHREIBUNG:
+    access_rights() wird immer dann aufgerufen, wenn jemand, der nicht
+    sowieso schon schreibberechtigt ist, in die Datei/das Verzeichnis file
+    schreiben oder loeschen will.
+
+    Anhand von euid kann man dann entscheiden, ob der Schreibzugriff erlaubt
+    wird oder nicht.
+
+RUECKGABEWERT:
+    0, wenn der Zugriff verweigert wird,
+    1, wenn der Zugriff erlaubt wird.
+
+BEISPIELE:
+    /* /d/inseln/wargon/access_rights.c */
+
+    int access_rights(string euid, string file)
+    {
+      string dir, rest;
+
+      // Catweazle darf auf alles zugreifen (*argl* ;^)
+      if (euid == "catweazle")
+        return 1;
+
+      // Rechte auf einzelne Verzeichnisse ermitteln:
+      if (sscanf(file, "%s/%s", dir, rest) != 2)
+        rest = file;
+
+      // Jof und Boing duerfen an Tarko Reub rumpfuschen:
+      if (dir == "tarko" && (euid == "jof" || euid == "boing"))
+        return 1;
+
+      // Anthea darf die Karten von Aurora und der Piratenhoehle bearbeiten:
+      if (dir == "MAPS" && 
+          member( ({"Aurora", "Piratenhoehle" }), rest) >= 0 && 
+          euid == "anthea")
+        return 1;
+
+      // alle anderen duerfen nicht!
+      return 0;
+    }
+
+BEMERKUNGEN:
+    file ist immer relativ zu dem Verzeichnis, in dem das access_rights.c
+    liegt! Will also jemand auf /d/inseln/wargon/tarko/macros.h schreiben,
+    wird file "tarko/macros.h" uebergeben.
+
+    In Verzeichnissen von Magiern mit einem Level >= ELDER_LVL wird das
+    access_rights.c NICHT ausgewertet (da damit andere Magier zB. an
+    Erzmagierrechte gelangen koennten).
+
+    Es wird immer nur EIN access_rights.c ausgewertet, naemlich das in der
+    tiefsten Verzeichnisebene.
+
+    Man kann sowohl in seinen Regionsverzeichnissen als auch in seinen
+    Homeverzeichnissen access_rights.c-Dateien anlegen.
+
+    GANZ WICHTIG!!!
+    Fuer die Dateien, die evtl. von anderen angelegt werden, ist man immer
+    noch selbst verantwortlich! Wenn jemand also ein Gebiet bei Dir an-
+    schliesst, muss es erst von den verantwortlichen Regionsmagiern abgesegnet
+    sein!
diff --git a/doc/lfun/buffer_hp b/doc/lfun/buffer_hp
new file mode 100644
index 0000000..c3e7b1b
--- /dev/null
+++ b/doc/lfun/buffer_hp
@@ -0,0 +1,76 @@
+FUNKTION:
+    int buffer_hp( int val, int rate );
+
+DEFINIERT IN:
+    /std/living/life.c
+
+ARGUMENTE:
+    val:  Gesamte Heilung.
+    rate: LP-Rate.
+        
+BESCHREIBUNG:
+    Erhoeht die LP eines Spielers automatisch insgesamt um den Wert "val".
+    Pro heart_beat() wird ihm dabei der Wert "rate" zugefuehrt.
+    Sollen neben P_HP noch weitere Props manipuliert werden - bspw. zur
+    P_FOOD - bietet sich die Funktion consume() an.
+
+RUECKGABEWERTE:
+    Der getankte Wert pro heart_beat().
+
+BEMERKUNG:
+    Sollte von jeder tragbaren Heilung genutzt werden, welche den Spieler
+    darauf schliessen lassen kann, auf natuerlichem und nichtmagischem Weg
+    (Essen, Trinken) geheilt worden zu sein.
+
+BEISPIEL:
+    #define TP this_player()
+    ...
+
+    int heilung=1;
+    ...
+
+    create()
+    {
+     ::create();
+     SetProp(P_NAME,"Heilpflanze");
+     ...
+
+     AddCmd("iss","eat");
+    }
+
+    int eat(string str)
+    {
+      notify_fail("WAS willst Du essen?\n");
+      if ( !str || !id(str) )
+       return 0;
+      ...
+
+      if ( !TP->eat_food(25) )
+       return 1;
+
+      TP->buffer_hp(20,5);
+      TP->buffer_sp(80,10);
+      heilung--;
+      write(BS("Du fuehlst langsam, wie Deine Kraefte zurueckkehren."));
+
+      return 1;
+    }
+
+    reset()
+    {
+      heilung=1;
+      ::reset();
+    }
+
+    Es wird durch eat_food getestet, ob der Spieler noch genuegend essen kann.
+    Wenn ja, kriegt unser Held die 25 automatisch oben drauf und ausserdem
+    20 LP in 5-LP-Schritten und 80 KP in 10-LP-Schritten gutgeschrieben.
+
+SIEHE AUCH:
+     Aehnlich:  heal_self, restore_spell_points, restore_hit_points, 
+                buffer_sp
+     Tanken:    consume, drink_alcohol, drink_soft, eat_food
+     Props:     P_SP, P_HP,
+     Konzepte:  heilung
+
+9. August 2015 Gloinson
diff --git a/doc/lfun/buffer_sp b/doc/lfun/buffer_sp
new file mode 100644
index 0000000..dc7341c
--- /dev/null
+++ b/doc/lfun/buffer_sp
@@ -0,0 +1,36 @@
+FUNKTION:
+    int buffer_sp( int val, int rate );
+
+DEFINIERT IN:
+    /std/living/life.c
+
+ARGUMENTE:
+    val:  Gesamte Heilung.
+    rate: KP-Rate.
+        
+BESCHREIBUNG:
+    Erhoeht die KP eines Spielers automatisch insgesamt um den Wert "val".
+    Pro heart_beat() wird ihm dabei der Wert "rate" zugefuehrt.
+    Sollen neben P_SP noch weitere Props manipuliert werden - bspw. zur
+    P_FOOD - bietet sich die Funktion consume() an.
+
+RUECKGABEWERTE:
+    Der getankte Wert pro heart_beat().
+
+BEMERKUNG:
+    Sollte von jeder tragbaren Heilung genutzt werden, welche den Spieler
+    darauf schliessen lassen kann, auf natuerlichem und nichtmagischem Weg
+    (Essen, Trinken) geheilt worden zu sein.
+
+BEISPIEL:
+    s. Bsp. zu "buffer_hp"
+
+SIEHE AUCH:
+     Aehnlich:  heal_self, restore_spell_points, restore_hit_points, 
+                buffer_hp
+     Tanken:    consume, drink_alcohol, drink_soft, eat_food
+     Props:     P_SP, P_HP,
+     Konzepte:  heilung
+
+9. August 2015 Gloinson
+
diff --git a/doc/lfun/buy_obj b/doc/lfun/buy_obj
new file mode 100644
index 0000000..bf1995b
--- /dev/null
+++ b/doc/lfun/buy_obj
@@ -0,0 +1,42 @@
+buy_obj()
+
+FUNKTION:
+	static string buy_obj(mixed ob, int short);
+
+DEFINIERT IN:
+	/std/shop.c
+
+ARGUMENTE:
+	ob - der Gegenstand bei dem geprueft werden soll, ob der Laden ihn
+	     an this_player() verkauft. Sollte es sich hierbei um ein
+	     FixedObject handeln, wird ein String uebergeben, ansonsten ein
+	     object.
+        short - Bisher noch nicht in Benutzung. Aber fuer die Zukunft
+             vorgesehn, falls man mehrere Objekte auf einmal kauft.
+             Ein auswerten ist keine Pflicht, waere aber praktisch, damit
+             der Scroll dabei nicht zu gross wird.
+
+RUeCKGABEWERT:
+        Ein String was der Haendler sagen soll wieso der Gegenstand nicht
+	verkauft wird. Der String wird dabei wie folgt umgebrochen:
+        break_string(str, 78, Name(WER, 1)+" sagt: ")
+BESCHREIBUNG:
+	Durch ueberschreiben dieser Funktion ist es moeglich bestimmte
+	Objekte (wie z.b. Questobjekte) nur an ausgewaehlte Spieler zu
+	verkaufen). Aber auch abfragen ob der Laden ueberhaupt mit
+	this_player() handelt, sind moeglich.
+
+BEISPIELE:
+	static string buy_obj(mixed ob, int short)
+	{
+	   if (PL->QueryProp(P_RACE)=="Zwerg")
+	      return "Ich verkaufe nichts an Zwerge!";
+	   return ::buy_obj(ob, short);
+	}
+
+SIEHE AUCH:
+	sell_obj(), AddFixedObject(), RemoveFixedObject(), SetStorageRoom(),
+        /std/shop.c
+
+----------------------------------------------------------------------------
+Last modified: Thu Mar 4 15:26:13 1999 by Padreic
diff --git a/doc/lfun/catch_msg b/doc/lfun/catch_msg
new file mode 100644
index 0000000..3bf09c9
--- /dev/null
+++ b/doc/lfun/catch_msg
@@ -0,0 +1,15 @@
+catch_msg()
+
+SYNOPSIS:
+    void catch_msg(mixed *arr, object obj)
+
+DESCRIPTION:
+    When say(), tell_object()  or tell_room() are used with an array 
+    as message, the array will be passed to catch_message() in all living
+    objects that can hear it, instead of writing to the user resp.
+    sending to catch_tell(). This can be used to implement
+    communication protocols between livings. The second denotes
+    the object that has sent the message.
+
+SEE ALSO:
+ say(E), tell_room(E), tell_object(E), catch_tell(L)
diff --git a/doc/lfun/catch_tell b/doc/lfun/catch_tell
new file mode 100644
index 0000000..b64832b
--- /dev/null
+++ b/doc/lfun/catch_tell
@@ -0,0 +1,25 @@
+catch_tell()
+
+SYNOPSIS:
+	void catch_tell(string)
+
+DESCRIPTION:
+	When a message is sent to a noninteractive player, via say(),
+	tell_object, tell_room(), printf() or write(), it will get to the
+	function catch_tell(string). This will enable communications between
+	NPCs and from a player to an NPC.
+
+	Also, if an interactive object is being shadowed and the
+	shadow has catch_tell() defined, it will receive all output
+	that would otherwise be written to the user.
+
+	If a message is sent by an interactive object, catch_tell() is
+	not called in that object, to prevent recursive calls. Thus
+	catch_tell() in interactive objects can be used to filter the
+	output that goes to the users.
+
+	The efun shout() sends to interactive objects only.
+
+SEE ALSO:
+	enable_commands(E), say(E), tell_object(E), tell_room(E),
+	write(E), catch_msg(L) 
diff --git a/doc/lfun/check_and_update_timed_key b/doc/lfun/check_and_update_timed_key
new file mode 100644
index 0000000..513c261
--- /dev/null
+++ b/doc/lfun/check_and_update_timed_key
@@ -0,0 +1,93 @@
+check_and_update_timed_key()
+
+FUNKTION:
+       public int check_and_update_timed_key(int duration, string key)
+       
+DEFINIERT IN:
+       /std/living/life.c    
+
+ARGUMENTE:
+       int duration: In wieviel Sekunden wird <key> wieder freigebeben,
+                     (z.B. wann kann der Spieler an dieser Stelle eine neue 
+                     Heilung bekommen).
+       string key  : Eindeutiger Name, wird zusammen mit <duration>
+                     gespeichert.
+
+BESCHREIBUNG:
+       Diese Funktion hat die Aufgabe, Zeitsperren verschiedenster Art
+       einfach zu ermoeglichen (z.B. die Realisierung charakter-abhaengiger
+       Heilstellen u.ae.).
+
+       <key> muss eindeutig sein, am besten verwendet man den eigenen
+       Magiernamen (und ggf. nen Gebietsnamen) als Teil des Strings.
+
+       Die Funktion ist definiert in /std/living/life.c. Somit funktioniert
+       sie auch bei NPCs. Die Daten werden in P_TIMING_MAP gespeichert, sind
+       also gegen "ende" resistent. (werden allerdings nach Ablauf ggf.
+       'aufgeraeumt')
+
+       Das Mapping P_TIMING_MAP ist NICHT zur Abfrage und / oder Manipulation
+       'per Hand' vorgesehen.
+
+RUeCKGABEWERT:
+       0    Irgendein Fehler im Aufruf, evtl. existiert die Funktion (noch)
+            nicht im jew. Objekt.
+
+      -1    Alles okay. Neuer Zeitpunkt wird automatisch gespeichert. In
+            diesem Fall darf der Spieler geheilt werden.
+
+      >0    Key noch gesperrt, in dem Fall darf also nicht geheilt werden. 
+            Der Rueckgabewert ist der Zeitpunkt, ab dem <key> wieder frei ist,
+            laesst sich daher dazu nutzen, um dem Spieler einen Anhaltspunkt
+            zu geben, wann er die Stelle wieder nutzen kann, etwa:
+
+            "Die Schale ist erst halb voll, Du musst noch etwas warten."
+
+BEISPIELE:
+       Eine Heilstelle soll jedem Spieler alle 5min zur Verfuegung stehen:
+
+       AddCmd(({"trink","trinke"}),"trink_cmd");
+
+       int trink_cmd(string str){
+         ...
+         ...
+         /*
+         Der key sollte natuerlich eine etwas eindeutigere Kennzeichnung
+         wie etwa "tilly_trinken" bekommen, auch wenn er durch einen
+         anderen (gleichnamigen) nicht ueberschrieben werden kann.
+
+         Trifft diese Abfrage hier zu, kann dem Spieler Heilung o.ae. zu-
+         gefuehrt werden. Die neue Zeit (duration) wird automatisch gesetzt.
+         */
+         if(this_player()->check_and_update_timed_key(300,"jof_trinken")==-1){
+           if(this_player()->drink_soft(2)){
+             this_player()->heal_self(50);
+             write("Du fuehlst Dich sichtlich erfrischt.\n");
+             return 1;
+            }
+           else{
+             write("Du hast schon zuviel getrunken.\n");
+             return 1;
+            }
+          }
+         else {
+           write("Du trinkst und denkst . o O (Hmm, nicht schlecht).\n");
+           return 1;
+          }
+         return 0;
+        }
+
+BEMERKUNGEN: 
+       Auch bei dieser Funktion ist darauf zu achten, dass Properties wie
+       P_FOOD, P_DRINK und P_ALCOHOL beruecksichtigt werden.
+       Heilstellen sind dem zustaendigen Magier fuer Heilungs-Balance zu 
+       melden. Wer dies momentan ist, kann dem Mailalias heilungs_balance
+       entnommen werden.
+
+SIEHE AUCH:
+       check_timed_key, eat_food, drink_alcohol, drink_soft, heal_self,
+       restore_spell_points, reduce_hit_point
+
+----------------------------------------------------------------------------
+08.01.2012, Zesstra
+
diff --git a/doc/lfun/check_restrictions b/doc/lfun/check_restrictions
new file mode 100644
index 0000000..2c815cd
--- /dev/null
+++ b/doc/lfun/check_restrictions
@@ -0,0 +1,149 @@
+check_restrictions()
+FUNKTION:
+    string check_restrictions(object pl, mapping restr)
+
+DEFINIERT IN:
+    /std/restriction_checker.c
+
+ARGUMENTE:
+    object pl        geprueftes Lebewesen
+    mapping restr    Mapping mit Restriktionen, s.u.
+
+BESCHREIBUNG:
+    Die Methode wird verwendet, um Restriktionen (zum Beispiel fuer das
+    Casten eines Spells) zu pruefen. Sie wird von Spellbook und
+    Gildenobjekt direkt genutzt.
+
+    Ihr wird dabei ein Spielerobjekt sowie ein Mapping mit den jeweiligen
+    Restriktionen uebergeben. Die aktuell moeglichen Keys des Mappings sind
+    weiter unten gelistet.
+
+BEMERKUNGEN:
+    Es wird bei der Rasse P_REAL_RACE geprueft. Der Tarnhelm funktioniert
+    also nicht.
+
+    Bei Erweiterungsvorschlaegen wendet euch bitte an einen EM oder
+    inheritet im Zweifelsfall nach Absprache.
+    NIEMALS solchen Code einfach KOPIEREN. Spaeter muss nur irgendwer
+    eurem alten Code hinterherraeumen.
+
+Aktuelle Liste der pruefbaren Parameter:
+    P_LEVEL
+      Mindeststufe, die das Lebewesen besitzen muss, um die Aktion
+      auszufuehren.
+    P_GUILD_LEVEL
+      Gildenlevel, das das Lebewesen mindestens erreicht haben muss, um die
+      Aktion auszufuehren.
+    SR_SEER
+      Ist gesetzt, wenn das Lebewesen Seher sein muss.
+      Auswertung nur fuer Interactives, NSC ignorieren das Flag.
+    P_XP
+      Mindestmenge an Erfahrungspunkten, die ein Lebewesen besitzen muss,
+      um die Aktion auszufuehren.
+    P_QP
+      Mindestmenge an Abenteuerpunkten, die das Lebewesen haben muss.
+    P_ALCOHOL
+      Menge an Alkohol, unter der der Alkoholspiegel des Lebewesen liegen
+      muss, um die Aktion noch ausfuehren zu koennen.
+    P_DRINK
+      Menge an Fluessigkeit, unter der der Fluessigkeitsspiegel des
+      Lebewesen liegen muss, um die Aktion noch ausfuehren zu koennen.
+    P_FOOD
+      Beinhaltet die Menge an Nahrung, unter der der Nahrungsspiegel des
+      Spielers liegen muss, um die Aktion noch ausfuehren zu koennen.
+    P_DEAF
+      Ist gesetzt, falls der Spieler nicht taub sein darf.
+    P_FROG
+      Ist gesetzt, falls der Spieler kein Frosch sein darf.
+    P_BLIND
+      Ist gesetzt, falls der Spieler nicht blind sein darf.
+      Achtung: das ist nicht gleichbedeutend mit dem Umstand, dass er evtl.
+      nichts mehr sehen kann. Auch andere Gruende (zum Beispiel Dunkelheit)
+      koennen bewirken, dass ein Spieler nichts mehr sieht.
+    A_INT, A_DEX, A_CON, A_STR
+      Jeweilige Mindesthoehe eines Attribut, um eine Aktion ausfuehren zu
+      koennen.
+    SR_BAD, SR_GOOD
+      Gibt an, wie [minimal] boese bzw. wie [maximal] gut ein Charakter sein
+      darf, um eine Aktion ausfuehren zu koennen.
+    SR_MIN_SIZE, SR_MAX_SIZE
+      Gibt die minimale, bzw. die maximale Groesse an, die ein Charakter
+      maximal haben darf, um eine Aktion ausfuehren zu koennen.
+    SR_FREE_HANDS
+      Gibt an, wieviele freie Haende ein Charakter fuer diese Aktion
+      besitzen muss.
+    SR_EXCLUDE_RACE
+      Mitglieder aller in dieser Liste aufgefuehrten Rassen koennen
+      diese Aktion nicht ausfuehren.
+    SR_INCLUDE_RACE
+      Mitglieder aller NICHT in dieser Liste aufgefuehrten Rassen koennen
+      diese Aktion nicht ausfuehren.
+    SM_RACE
+      Hier kann pro Rasse ein Mapping mit besonderen (nur) fuer diese Rasse
+      geltenden Einschraenkungen vorgenommen werden. Als Keys sind die
+      in dieser Manpage beschriebenen Keys erlaubt, wobei SM_RACE nicht
+      rekursiv ausgewertet wird.
+      Der Rassenname ist gross geschrieben und "*" steht fuer alle Rassen.
+    SR_EXCLUDE_GUILD
+    SR_INCLUDE_GUILD
+      Diese beiden Keys verhalten sich wie SR_*_RACE, nur dass hier Gilden
+      genannt werden.
+    SR_FUN
+      Hier kann eine Funktion in verschiedenen Formen zum Pruefen der
+      Restriktionen angegeben werden, siehe execute_anything().
+      Das kann nuetzlich sein, um andere Restriktionen zu pruefen,
+      wie das Bestehen von Miniquests oder andere Faehigkeiten/Flags.
+      Ist der Test nicht bestanden, gibt die Funktion einen String zurueck.
+    SR_PROP
+      Hier kann ein Mapping mit Properties und zugehoerigen Werten angegeben
+      werden, die jeweils auf Identitaet geprueft werden. Zusaetzlich sollte
+      eine Meldung angegeben werden, die als Fehlermeldung ausgegeben wird,
+      wenn der Spieler die Bedingung nicht erfuellt. Es sollte immer eine
+      passende Meldung fuer den Spieler eingebaut werden. Beispiel:
+      ([ SR_PROP: ([P_AUSGANG_ENTDECKT: 1; "Dein Schwert fluestert "
+          "veraergert: Ich werde Dir erst dann zu Diensten sein, wenn Du "
+          "Dich als wuerdig erwiesen hast!"]) ])
+      Aufgrund der Meldung wird empfohlen, SR_PROP nicht in Restriktionen
+      einzusetzen, die massenweise in Savefiles landen (z.B.
+      Spielersavefiles).
+    SR_QUEST
+      Hier kann ein String-Array mit den Namen (Keys) der Quest(s) angegeben
+      werden, die der Spieler bestanden haben muss, um die Aktion ausfuehren
+      zu koennen.
+    SQ_MINIQUEST
+      Hier kann entweder ein String-Array mit den Ladenamen der vergebenden
+      Objekte oder ein Int-Array mit den Index-Nummern (IDs) der
+      Miniquest(s) (empfohlen!) angegeben werden, die der Spieler bestanden
+      haben muss, um die Aktion ausfuehren zu koennen.
+
+BEISPIELE:
+    // #1 Levelbeschraenkung in der Abenteurergilde
+    AddSpell("feuerball",20,
+             ([SI_SKILLRESTR_LEARN:([P_LEVEL:15]), ...
+
+    // #2 Glaubenstest im Klerus
+    AddSpell("bete",
+             ([SI_SKILLRESTR_LEARN: ([P_GUILD_LEVEL : LVL_NOVIZE,
+                                      SR_FUN : #'glaubensTest ]), ...
+    // mit
+    static string glaubensTest(object pl) {
+      if (pl->QueryProp(K_STRENGTH) < 8000)
+        return ("Deine Glaubensstaerke laesst zu wuenschen uebrig!\n");
+      return 0;
+    }
+
+    // #3 SM_RACE-Modifikation der Restriktionen:
+    //    haertere Restriktionen fuer Zwerge
+    //    - hoeheres Level
+    //    - zusaetzlich A_STR pruefen
+    ([P_LEVEL:15,
+      A_INT:10,
+      SM_RACE: (["Zwerg": ([P_LEVEL:17, A_STR:20])])])
+    // ist identisch zu
+    ([SM_RACE: (["*":     ([P_LEVEL:15, A_INT:10]),
+                 "Zwerg": ([P_LEVEL:17, A_INT:10, A_STR:20])])])
+
+SIEHE AUCH:
+    execute_anything(L), AddSpell (Gilde), P_RESTRICTIONS
+
+03. Januar 2014, Arathorn
diff --git a/doc/lfun/check_timed_key b/doc/lfun/check_timed_key
new file mode 100644
index 0000000..81baff7
--- /dev/null
+++ b/doc/lfun/check_timed_key
@@ -0,0 +1,34 @@
+check_timed_key()
+
+FUNKTION:
+       public int check_timed_key(string key)
+       
+DEFINIERT IN:
+       /std/living/life.c    
+
+ARGUMENTE:
+       string key  : Eindeutiger Name
+
+BESCHREIBUNG:
+       Diese Funktion hat die Aufgabe, mittels check_and_update_timed_key()
+       gespeicherte Zeitsperren zu pruefen, aber dabei nicht zu veraendern.
+
+       Es MUSS bei der eigentlichen Aktion (z.B. Heilung des Spielers) der
+       Rueckgabewert von check_and_update_timed_key() beruecksichtigt werden,
+       sollte dieses nicht -1 geliefert haben, MUSS <key> als gesperrt
+       betrachtet werden, d.h. check_timed_key() hat nur informativen
+       Charakter.
+
+RUeCKGABEWERT:
+       0    Die Zeitsperre <key> ist abgelaufen.
+
+       >0   <key> ist noch gesperrt.
+            Der Rueckgabewert ist der Zeitpunkt, ab dem <key> wieder frei ist,
+
+SIEHE AUCH:
+       check_and_update_timed_key, eat_food, drink_alcohol, drink_soft,
+       heal_self, restore_spell_points, reduce_hit_point
+
+----------------------------------------------------------------------------
+08.01.2012, Zesstra
+
diff --git a/doc/lfun/clean_up b/doc/lfun/clean_up
new file mode 100644
index 0000000..97ddf09
--- /dev/null
+++ b/doc/lfun/clean_up
@@ -0,0 +1,43 @@
+clean_up()
+FUNKTION:
+     int clean_up(int ref);
+
+DEFINIERT IN:
+     /std/room.c
+     man kann die Funktion jedoch auch in beliebigen Objekten selbst
+     definieren.
+
+ARGUMENTE:
+     ref
+             + 0 bei gecloneten Objekten
+             + 1 bei einfachen geladenen Objekten
+             + >1 bei Objekten, die geerbt wurden oder als Blueprint dienen
+             + <0, wenn clean_up() von aussen aufgerufen wurde (das muss man
+               selbst beachten!)
+
+BESCHREIBUNG:
+     Wenn ein Objekt seit langer Zeit nicht mehr benutzt wurde, kann es sich
+     hier selbst zerstoeren. Das sollte das Objekt allerdings nur tun, wenn
+     ref kleiner oder gleich 1 ist.
+
+RUeCKGABEWERT:
+     Der Rueckgabewert hat nur dann eine Bedeutung, wenn sich das Objekt
+     nicht selbst zerstoert hat. Wird 0 zurueckgegeben, so wird clean_up()
+     erst dann wieder aufgerufen, nachdem das Objekt aus- und wieder
+     eingeswappt wurde.
+
+     Ein Rueckgabewert ungleich 0 zeigt an, dass das Objekt sich
+     wahrscheinlich in der naechsten clean_up()-Runde zerstoeren kann, wenn
+     in der Zwischenzeit zB. noch einmal reset() aufgerufen wurde.
+
+BEMERKUNGEN:
+     Standardmaessig definieren nur Raeume clean_up().
+
+     Die Zeiten zwischen zwei Aufrufen von clean_up() betragen momentan
+     einen Tag (86400 Sekunden).
+
+SIEHE AUCH:
+     reset(), P_NEVER_CLEAN
+     memory
+
+21. Maerz 2004 Gloinson
diff --git a/doc/lfun/cmd_shoot b/doc/lfun/cmd_shoot
new file mode 100644
index 0000000..ccd52ce
--- /dev/null
+++ b/doc/lfun/cmd_shoot
@@ -0,0 +1,31 @@
+cmd_shoot()
+
+FUNKTION:
+    static int cmd_shoot(string str)
+
+DEFINIERT IN:
+    /std/ranged_weapon.c
+
+ARGUMENTE:
+    string str    - Schusssyntax
+
+BESCHREIBUNG:
+    Kommandofunktion der Fernwaffe. Enthaelt die Vorbereitung und den Schuss.
+
+BEMERKUNGEN:
+    Ist in der Fernwaffe mit AddCmd( ({"schiess", "schiesse"}), "cmd_shoot");
+    angebunden. Will man eine andere Syntax haben, kann man mit
+    AddCmd/RemoveCmd diese umsetzen.
+
+BEISPIELE:
+    RemoveCmd(({"schiess", "schiesse"})); // entferne Default-Kommando
+    AddCmd(({"schleuder", "schleudere"}), #'cmd_shoot);
+
+SIEHE AUCH:
+    Generell:  P_AMMUNITION, P_SHOOTING_WC, P_STRETCH_TIME
+    Methoden:  shoot_dam(L), cmd_shoot(L)
+    Kommandos: AddCmd(L), RemoveCmd(L)
+    Syntax:    _unparsed_args(L)
+    Sonstiges: fernwaffen
+
+28.Jul 2014 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/command_me b/doc/lfun/command_me
new file mode 100644
index 0000000..cdb1ef2
--- /dev/null
+++ b/doc/lfun/command_me
@@ -0,0 +1,81 @@
+command_me()
+FUNKTION:
+    int command_me(string str)
+
+ARGUMENTE:
+    string str - auszufuehrendes Kommando
+
+DEFINIERT IN:
+    /std/npc.c, /std/player/command.c
+
+BESCHREIBUNG:
+    Fuehrt 'str' wie ein Kommando aus, welches direkt vom Living
+    abgegeben wurde.
+
+    Der Rueckgabewert ist >=1 fuer Erfolg und 0 fuer Misserfolg. 
+    Rueckgabewert ist im Erfolgsfall die Hoehe der EvalCost in Ticks. 
+
+    command_me() leitet den Befehl an command()(E) weiter und erlaubt
+    dadurch auch den Aufruf von sonst durch "static", "protected" oder
+    "private" vor externem Aufruf geschuetzten Kommando-Funktionen.
+
+    Kommandos mit oeffentlichen Kommandofunktionen (also damit alle mit
+    AddCmd definierten Kommandos) koennen auch durch command()(E)
+    von aussen ausgeloest werden.
+
+BEMERKUNGEN:    
+    - beruecksichtigt Aliase, d.h. wenn man Aliase eines Spielers
+      ignorieren will, muss man ein \\ (fuer ein \ im Eingabestring)
+      vor den Befehl stellen
+    - fuer objektinterne Kommandos funktioniert also auch command()(E)
+
+BEISPIEL:
+    (Code des Testraum ist in /doc/beispiele/testobjekte/command_me_testraum)
+    // #1: Testraum, zwinge Spieler bei "schleiche heran" zum Furzen
+    //     command_me() ist hier noetig, da das Furzen eine geschuetzte
+    //     Kommandofunktion hat
+    inherit "/std/room";
+
+    void create() {
+      ::create();
+      AddCmd("schleiche&heran|herum", "action_schleichen");
+    }
+    
+    void init() {
+      ::init();
+      add_action("action_kriechen", "kriech", 1);
+    }
+
+    static int action_schleichen(string str) {
+      string tmp = this_player()->QueryProp(P_RACE);
+      // ... 
+      if(tmp[<1]=='e') tmp=tmp[0..<2];
+      write(break_string("Du versuchst leise zu schleichen, dabei passiert "
+        "dir aber ein allzu "+
+        (tmp=="Mensch"?"menschliches":lower_case(tmp)+"isches")+
+        " Missgeschick. Verflucht!", 78));
+      this_player()->command_me("\\furze");
+      return 1;
+    }
+    
+    static int action_kriechen(string str) {
+      write(break_string("Deine Knie tun zu sehr weh dafuer.", 78));
+      tell_room(this_object(), break_string(this_player()->Name(WER)+
+        " knackt mit den Knien.", 78));
+      return 1;
+    }
+    
+    // #2 (in obigem Raum): zwinge Spieler in Raum zu obigem Kommando
+    //    Beide Kommandos gelingen, da Kommando mit AddCmd definiert.
+    find_player("naddl")->command_me("schleiche herum")
+    command("schleiche herum", find_player("naddl")); 
+
+    // #3 (in obigem Raum): zwinge Spieler in Raum zu obigem Kommando
+    find_player("naddl")->command_me("krieche")
+    //    Folgendes Kommando schlaegt fehl, da Kommandofunktion static ist.
+    command("krieche", find_player("naddl"));
+    
+SIEHE AUCH:
+     enable_commands(E), command(E)
+
+06 Sep 2012 Gloinson
diff --git a/doc/lfun/consume b/doc/lfun/consume
new file mode 100644
index 0000000..9979ec4
--- /dev/null
+++ b/doc/lfun/consume
@@ -0,0 +1,85 @@
+public varargs int consume(mapping cinfo, int testonly)
+
+FUNKTION:
+    public varargs int consume(mapping cinfo, int testonly);
+
+    Aenderung der Gesundheit eines Lebewesens durch etwas Konsumierbares.
+
+DEFINIERT IN:
+    /std/living/life.c
+
+ARGUMENTE:
+    cinfo
+        Mapping mit Informationen ueber die Gesundheitsaenderung
+        Heilung.
+    testonly
+        Gibt an, ob nur die Bedingungen abgetestet werden sollen,
+        oder auch die Wirkung eintreten soll.
+
+RUECKGABEWERT:
+    1 erfolgreich konsumiert
+    0 keine oder falsche Aenderungsdaten in cinfo (nicht benutzbar)
+   <0 Bedingung fuer konsumieren nicht erfuellt, Bitset aus:
+      HC_MAX_FOOD_REACHED    - Kann nichts mehr essen
+      HC_MAX_DRINK_REACHED   - Kann nichts mehr trinken
+      HC_MAX_ALCOHOL_REACHED - Kann nichts mehr saufen
+      HC_HOOK_CANCELLETION   - durch H_HOOK_CONSUME abgebrochen
+
+BESCHREIBUNG:
+    Die Funktion stellt eine Moeglichkeit zur Verfuegung, die Aenderung
+    der Gesundheit eines Lebewesens beim Konsumieren von irgendetwas (z.B in
+    einer Kneipe, durch eine Heilstellte oder tragbare Tanke, ...) zentral zu
+    erledigen. Sie vereint in sich die Pruefung auf Durchfuerbarkeit der
+    Aenderung und Anwendung der Aenderung.
+
+    Der erste Parameter gibt die Eigenschaften der Aenderung an, der zweite ob
+    ausschliesslich die Pruefung auf Anwendbarkeit erfolgen soll.
+
+    Das Mapping cinfo hat folgende Struktur:
+    a) Einfache Angabe der betroffenen Properties. In neuem Code bitte nicht
+       machen, dort ein Mapping wie unter b) beschrieben nutzen!
+
+    b) Strukturiert in Effekte und Bedingungen mit folgenden Schluesseln:
+      H_EFFECTS - Mapping der zu aendernden Properties mit dem Umfang der
+                  Aenderung, erlaubte Properties siehe H_ALLOWED_EFFECTS
+
+      H_CONDITIONS - Mapping der zu pruefenden Properties mit dem Umfang der
+                     Aenderung, erlaubte Properties siehe H_ALLOWED_CONDITIONS
+
+      H_DISTRIBUTION - Verteilung der Aenderung fuer P_SP, P_HP
+                       HD_INSTANT bzw. 0: instante Heilung
+                       1 - 50: angebene Zahl pro Heartbeat
+                       HD_STANDARD: 5 pro Heartbeat
+
+    Aenderungen koennen sowohl positiv als auch negativ sein.
+
+BEMERKUNGEN:
+    Hierbei aber bitte beachten, dass Tanken/Entanken sowie Heilungen ggf. von
+    der (Heilungs-)Balance genehmigt werden muessen!
+
+BEISPIELE:
+    Heilung um 100 KP, 50 LP, aber nur wenn 30 P_FOOD gegessen werden kann:
+
+    consume( ([H_EFFECTS: ([P_HP:50, P_SP:100]),
+               H_CONDITIONS: ([P_FOOD:30]) ]) );
+
+    Heilung um 100 KP und Vergiftung um 2, wenn 15 Alkohol getrunken werden
+    koennen. Die SP werden zeitverzoegert mit 10 pro Heartbeat zugefuehrt.
+
+    consume(([H_EFFECTS: ([P_SP: 100, P_POISON: 2]),
+              H_CONDITIONS: ([P_ALCOHOL: 15]),
+              H_DISTRIBUTION: 10]) )
+
+SIEHE AUCH:
+     Aehnlich:  drink_alcohol, eat_food, drink_soft
+     Heilung:   heal_self, restore_spell_points, restore_hit_points, 
+                buffer_hp, buffer_sp
+     Timing:    check_and_update_timed_key
+     Enttanken: defuel_drink, defuel_food
+     Props:     P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+LETZTE AeNDERUNG:
+    29.05.2015, Boing
+
diff --git a/doc/lfun/create b/doc/lfun/create
new file mode 100644
index 0000000..9582609
--- /dev/null
+++ b/doc/lfun/create
@@ -0,0 +1,78 @@
+create()
+
+FUNKTION:
+     protected void create();
+     void create();
+
+DEFINIERT IN:
+     allen Standardobjekten
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Diese Funktion wird aufgerufen, wenn ein Objekt geladen oder geclont
+     wird.
+     In dieser Funktion wird das Objekt initialisiert und konfiguriert.
+     Waehrend des create() kann es einen this_player()/this_interactive()
+     geben, muss aber nicht!
+     Im create() hat das Objekt noch kein environment().
+     create() wird nur gerufen, wenn das Objekte geclont oder explizit geladen
+     wird. Wenn es aufgrund eines inherit in einem anderen Objekt vom Driver
+     geladen wird, wird das create() nicht ausgefuehrt (s. create_super()).
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Erbt man von anderen Objekten, so besteht die erste Aktion innerhalb
+     von create() normalerweise darin, in diesen Objekten create()
+     aufzurufen.
+     Die Funktion kann protected oder static sein (aber nicht private). Es
+     duerfte fuer die meisten Objekte sinnvoll sein, create() protected zu
+     deklarieren.
+
+     Um Speicher zu sparen, kann man bei Blueprints von der Konfigurierung
+     absehen (siehe Beispiel). Dies sollte man allerdings nicht bei Objekten
+     machen, von denen keine Clones erzeugt werden sollen (zB. bei Raeumen). 
+
+     Man sollte bei Abbruch des create() in BP unbedingt set_next_reset(-1);
+     rufen, da sonst die (nicht konfigurierte) BP resetten kann und dann
+     buggt.
+
+BEISPIELE:
+     Ein Gegenstand wuerde wie folgt konfiguriert:
+
+     inherit "std/thing";
+
+     #include <properties.h>
+
+     create()
+     {
+       // Wenn wir die Blueprint sind: NICHT konfigurieren!
+       // Bei normalen Raeumen oder Transportern sind diese beiden
+       // Zeilen wegzulassen!!!
+       if (!clonep(this_object())) {
+           set_next_reset(-1);    // wichtig, damit die BP nicht resettet.
+           return;
+       }
+
+       // Ansonsten die allgemeinen Eigenschaften von /std/thing
+       // konfigurieren...
+       ::create();
+
+       // ...und nun unsere speziellen Eigenschaften:
+       SetProp(P_NAME, "Muell");
+       SetProp(P_SHORT, "Muell");
+       SetProp(P_LONG, "Voellig unnuetzer Muell!\n");
+       SetProp(P_ARTICLE, 0);
+       SetProp(P_VALUE, 0);
+       SetProp(P_GENDER, MALE);
+     }
+
+SIEHE AUCH:
+     create(L), reset(L)
+     hook(C), create(H), create_ob(H), create_super(H, reset(H)
+     create(A), reset(A)
+----------------------------------------------------------------------------
+22.10.2007, Zesstra
diff --git a/doc/lfun/create_default_npc b/doc/lfun/create_default_npc
new file mode 100644
index 0000000..b331c5c
--- /dev/null
+++ b/doc/lfun/create_default_npc
@@ -0,0 +1,55 @@
+create_default_npc()
+FUNKTION:
+     varargs void create_default_npc( int level, int maxhp );
+
+BENUTZUNG:
+     inherit "std/npc";
+
+FUNKTION:
+     Setze die Daten eines Monsters auf einen gewissen Level.
+
+     Der Level sollte zwischen 1 und 20 liegen. Die Stats werden auf diesen
+     Level gesetzt und mehrere andere Werte, so dass das Monster von der
+     Staerke her einem Spieler gleichen Levels entspricht.
+
+     Wird der (optionale) Parameter maxhp weggelassen, wird dieser berechnet
+     nach:
+          maxhp = 42 + 8 * level
+
+     Die genauen Werte sind:
+          P_LEVEL : level
+          P_MAX_HP: maxhp
+          P_MAX_SP: maxhp
+          P_HANDS : 10 * level
+          P_BODY  : (20/3) * level
+          P_XP    : 50 * level * maxhp (== 5 * P_HANDS * max_hp)
+
+          A_STR, A_INT, A_DEX, A_CON : level
+
+BEMERKUNG:
+     Diese Funktion sollte nur im create() eines Monsters benutzt werden.
+     Oben beschriebene Werte, die vor dem Aufruf der Funktion gesetzt
+     wurden, werden durch die neuen Werte ersetzt.
+
+     Ab einem Aufruf mit Level 20 werden P_XP = 202000 gesetzt, also ein
+     Kill-Stup vergeben (siehe P_XP).
+
+BEISPIEL:
+     create_default_npc(17) ergibt:
+
+          P_LEVEL : 17
+          P_MAX_HP: 178
+          P_MAX_SP: 178
+          P_HANDS : 170
+          P_BODY  : 113
+          P_XP    : 151300
+
+          A_STR, A_INT, A_DEX, A_CON : 17
+
+SIEHE AUCH:
+     Funktionen:  AddExp(), GiveKillScore()
+     Properties:  P_XP
+                  P_LEVEL, P_MAX_HP, P_MAX_SP, P_HANDS, P_BODY
+     Sonstiges:   npcs
+
+14.Feb 2007 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/create_super b/doc/lfun/create_super
new file mode 100644
index 0000000..14725a6
--- /dev/null
+++ b/doc/lfun/create_super
@@ -0,0 +1,49 @@
+create_super()
+
+FUNKTION:
+     protected void create_super();
+
+DEFINIERT IN:
+     allen Standardobjekten
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Diese Funktion wird immer dann aufgerufen, wenn ein Objekt implizit durch
+     ein 'inherit' in einem erbenden Objekte geladen wird.
+     Normalweise muss man dieses so geladene Objekte nicht konfigurieren, weil
+     nur das Programm dieses Objektes fuer die erbenden Objekte interessant
+     ist, nicht seine Daten.
+     Was hier aber z.B. sinnvoll ist, ist das Abschalten des Resets, denn ein
+     Objekt, was nur dazu dient, dass es von anderen geerbt wird, braucht
+     keinen Reset, auch wenn es ein reset() definiert (was in erbenden Objekte
+     benutzt wird).
+     Die create_super() in den Standardobjekten tun momentan genau dieses.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Wenn ihr von /std/ erbt, braucht ihr euch in aller Regel um diese
+     Funktion keine Gedanken machen.
+     Ihr koennt diese Funktion aber in Objekten selber definieren, die nur zum
+     Erben gedacht sind. Dies kann sinnvoll ein, wenn ihr nicht von /std/ erbt
+     und ein reset() im Objekt habt oder was machen wollt, was ueber das
+     Abschalten des Resets hinausgeht.
+
+BEISPIELE:
+     Gegeben sei folgende Vererbungskette:
+     /std/room -> /d/inseln/zesstra/stdraum -> /d/inseln/zesstra/raum
+
+     Wird nun 'raum' geladen und 'stdraum' ist noch nicht geladen, wird der
+     Driver implizit von selbst 'stdraum' laden (weil 'raum' das Programm von
+     'stdraum' braucht). Bei diesem Laden wird das create() in 'stdraum' nicht
+     ausgefuehrt, sondern stattdessen create_super().
+    
+SIEHE AUCH:
+     create(L), reset(L)
+     hook(C), create(H), create_ob(H), create_super(H, reset(H)
+     create(A), reset(A)
+----------------------------------------------------------------------------
+22.10.2007, Zesstra
diff --git a/doc/lfun/defuel_drink b/doc/lfun/defuel_drink
new file mode 100644
index 0000000..67e834d
--- /dev/null
+++ b/doc/lfun/defuel_drink
@@ -0,0 +1,59 @@
+FUNKTION:
+    int defuel_drink();
+
+DEFINIERT IN:
+    /std/living/life.c
+
+ARGUMENTE:
+    Keine.
+        
+BESCHREIBUNG:
+    Enttankt den Spieler automatisch um einen gewissen Fluessigkeits-Wert,
+    sofern der Spieler ueber einer bestimmten Enttank-Grenze liegt und seit
+    seinem letzten Enttanken eine gewisse Zeit vergangen ist.
+    Alle diese Werte sind rassenabhaengig.
+    Ausserdem wird dem Spieler eine gewisse Menge Alkohol entzogen. Er wird
+    also mit jedem fluessigen Enttanken etwas nuechterner.
+
+    Es ist also NICHT moeglich, Einfluss auf die Menge des Enttankens
+    zu nehmen. Das ist hier so gewollt.
+
+    Hat der Spieler mindestens 
+    * P_DEFUEL_LIMIT_DRINK in P_DRINK
+    kann er alle
+    * P_DEFUEL_TIME_DRINK
+    um
+    * (x=P_DRINK*P_DEFUEL_AMOUNT_DRINK/2) + random(x)
+      (also um (50 bis 100 * P_DRINK) Prozent)
+    enttanken.
+
+RUECKGABEWERTE:
+    DEFUEL_TOO_SOON: -2, wenn Enttankintervallzeiten zu kurz.
+    DEFUEL_TOO_LOW:  -1, wenn Enttankgrenze noch nicht erreicht.
+    NO_DEFUEL:        0, wenn Enttanken nicht noetig war (Spieler war leer)
+    >0, wenn Erfolg (enttankte Wert wird zurueckgegeben).
+
+    (Konstanten kommen aus /sys/defuel.h)
+
+BEMERKUNG:
+    Bitte defuel_drink() benutzen und nicht P_DRINK oder P_MAX_DRINK des
+    manipulieren!
+
+    Es gibt keine eigene Methode fuer die Verringerung von P_ALCOHOL.
+
+    Achtung: Nur Toiletten sollten diese Funktion im Spieler aufrufen!
+
+BEISPIEL:
+    s. Bsp. zu defuel_food()
+
+SIEHE AUCH:
+     Aehnlich:  defuel_food
+     Tanken:    consume, drink_alcohol, drink_soft, eat_food
+     Heilung:   heal_self, restore_spell_points, restore_hit_points, 
+                buffer_hp, buffer_sp
+     Timing:    check_and_update_timed_key
+     Props:     P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/lfun/defuel_food b/doc/lfun/defuel_food
new file mode 100644
index 0000000..1e89110
--- /dev/null
+++ b/doc/lfun/defuel_food
@@ -0,0 +1,85 @@
+FUNKTION:
+    int defuel_food();
+
+DEFINIERT IN:
+    /std/living/life.c
+
+ARGUMENTE:
+    Keine.
+
+BESCHREIBUNG:
+    Enttankt den Spieler automatisch um einen gewissen Essens-Wert,
+    sofern der Spieler ueber einer bestimmten Enttank-Grenze liegt und seit
+    seinem letzten Enttanken eine gewisse Zeit vergangen ist.
+    Alle diese Werte sind rassenabhaengig.
+
+    Es ist also NICHT moeglich, Einfluss auf die Menge des Enttankens
+    zu nehmen. Das ist hier so gewollt.
+
+    Hat der Spieler mindestens 
+    * P_DEFUEL_LIMIT_FOOD in P_FOOD
+    kann er alle
+    * P_DEFUEL_TIME_FOOD
+    um
+    * (x=P_DRINK*P_DEFUEL_AMOUNT_FOOD/2) + random(x)
+      (also um (50 bis 100 * P_FOOD) Prozent)
+    enttanken.
+
+RUECKGABEWERTE:
+    DEFUEL_TOO_SOON: -2, wenn Enttankintervallzeiten zu kurz.
+    DEFUEL_TOO_LOW:  -1, wenn Enttankgrenze noch nicht erreicht.
+    NO_DEFUEL:        0, wenn Enttanken nicht noetig war (Spieler war leer)
+    >0, wenn Erfolg (enttankte Wert wird zurueckgegeben).
+
+    (Konstanten kommen aus /sys/defuel.h)
+
+BEMERKUNG:
+    Bitte defuel_food() benutzen und nicht P_FOOD oder P_MAX_FOOD des
+    Spielers manipulieren!
+
+    Achtung: Nur Toiletten sollten diese Funktion im Spieler aufrufen!
+
+BEISPIEL:
+    int action_enttanken() {
+      string msg;
+      int val = this_player()->defuel_food();
+
+      switch (val) {
+        case DEFUEL_TOO_SOON:
+          msg = "Du warst doch erst vor kurzem auf Toilette...";
+          break;
+        case DEFUEL_TOO_LOW:
+          msg = "Du versuchst Dich zu entleeren, aber irgendwie will "
+                "das nicht so recht klappen.";
+          break;
+        case NO_DEFUEL:
+          msg = "Du hast seit langem nichts gegessen, wie willst Du dann "
+                "was loswerden koennen?";
+          break;
+        default:
+          string qualifier;
+          int fuzzypercent = (90+random(20)) *
+                             val/this_player()->QueryProp(P_MAX_FOOD);
+          switch(fuzzypercent) {
+            case 0..50:  qualifier = "etwas"; break;
+            case 51..75: qualifier = "enorm"; break;
+            default:     qualifier = "unglaublich"; break;
+          }
+          msg = "Du entleerst Dich "+qualifier"+. Puh, das tat gut!";
+          break;
+      }
+      tell_object(this_player(), break_string(msg, 78));
+      return 1;
+    }
+
+SIEHE AUCH:
+     Aehnlich:  defuel_drink
+     Tanken:    consume, drink_alcohol, drink_soft, eat_food
+     Heilung:   heal_self, restore_spell_points, restore_hit_points, 
+                buffer_hp, buffer_sp
+     Timing:    check_and_update_timed_key
+     Props:     P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/lfun/deregister_modifier b/doc/lfun/deregister_modifier
new file mode 100644
index 0000000..cbb541e
--- /dev/null
+++ b/doc/lfun/deregister_modifier
@@ -0,0 +1,24 @@
+deregister_modifier()
+FUNKTION:
+     void deregister_modifier(object modifier)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+

+PARAMETER:

+     modifier	- Objekt mit P_X_ATTR_MOD oder P_M_ATTR_MOD

+
+BESCHREIBUNG:
+     Meldet einen Modifier im Spieler ab. Wird durch RemoveSensitiveObject
+     beziehungsweise beim Ausziehen oder Wegstecken gerufen.
+     Muss nicht direkt gerufen werden. Bei Veraenderungen von Modifikatoren
+     sollte stattdessen UpdateAttributes gerufen werden.     
+
+SIEHE AUCH:
+     QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+     SetAttr(), SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+     register_modifier(),P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, 
+     P_ATTRIBUTES_MODIFIER, P_X_ATTR_MOD, P_M_ATTR_MOD, 
+     /std/living/attributes.c

+
+13.Jun.2004, Muadib
\ No newline at end of file
diff --git a/doc/lfun/die b/doc/lfun/die
new file mode 100644
index 0000000..ce055f8
--- /dev/null
+++ b/doc/lfun/die
@@ -0,0 +1,45 @@
+die()
+
+FUNKTION:
+     public varargs void die(int poisondeath,int extern);
+
+DEFINIERT IN:
+     /std/living/life.c
+
+ARGUMENTE:
+     int poisondeath
+	  Dieses Flag sollte bei einem Gifttod (P_POISON) gesetzt sein.
+     int extern
+	  Intern.
+
+BESCHREIBUNG:
+     Das Lebewesen stirbt, meist automatisch von do_damage() ausgeloest, wenn
+     0 HP unterschritten werden. In diesem Fall wird der Kampf beendet, Gift,
+     Alkohol, Trink- und Esswerte, Blindheit, Taubheit u.s.w. auf Null
+     gesetzt oder geloescht.
+
+     Es wird automatisch eine Leiche (siehe auch P_CORPSE, P_NOCORPSE) nebst
+     Todesmeldungen (siehe auch P_DIE_MSG) erzeugt, und fuer Spieler werden
+     Killstupse vergeben, sofern notwendig.
+
+     Ueber den Hook P_TMP_DIE_HOOK kann man jedoch auf den Automatismus
+     Einfluss nehmen, z.B. koennte ein temporaerer Todesbann-Zauber das
+     Sterben fuer kurze Zeit verhindern.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Diese Funktion sollte nur selten direkt verwendet werden. Meist ist der
+     uebliche Weg ueber Defend() -> do_damage() -> die() die logisch bessere
+     und balancetechnisch guenstigere Loesung.
+
+SIEHE AUCH:
+     Todesursachen:	Defend(L), do_damage(L), P_POISON
+     Verwandt:		P_TMP_DIE_HOOK, P_DEADS
+     Todesmeldungen:	P_KILL_NAME, P_KILL_MSG, P_MURDER_MSG, P_DIE_MSG
+			P_ZAP_MSG, P_ENEMY_DEATH_SEQUENCE
+     Sonstiges:		P_CORPSE, P_NOCORPSE, /std/corpse.c
+
+----------------------------------------------------------------------------
+Last modified: Mon May 14 16:20:34 2001 by Patryn
diff --git a/doc/lfun/doUnwearMessage b/doc/lfun/doUnwearMessage
new file mode 100644
index 0000000..e029ea2
--- /dev/null
+++ b/doc/lfun/doUnwearMessage
@@ -0,0 +1,28 @@
+doUnwearMessage()
+
+FUNKTION:
+	void doUnwearMessage(object worn_by, int all);
+
+DEFINIERT IN:
+	/std/armour/combat.c
+
+ARGUMENTE:
+	worn_by
+          Das Lebewesen, dass die Ruestung bisher getragen hat
+        all
+          Wurde "ziehe alles aus"/"ziehe alle ruestungen aus" eingegeben,
+          so ist all gesetzt, ansonsten nicht.
+
+BESCHREIBUNG:
+        Diese Funktion gibt beim Ausziehen einer Ruestung die entspr.
+        Meldung an den Traeger und die Umgebung aus.
+
+RUeCKGABEWERT:
+	keiner
+
+SIEHE AUCH:
+        doWearMessage(), doWieldMessage(), doUnwieldMessage()
+
+----------------------------------------------------------------------------
+Last modified: Sat Jun 26 15:41:00 1999 by Paracelsus
+
diff --git a/doc/lfun/doUnwieldMessage b/doc/lfun/doUnwieldMessage
new file mode 100644
index 0000000..dbf07ce
--- /dev/null
+++ b/doc/lfun/doUnwieldMessage
@@ -0,0 +1,25 @@
+doUnwieldMessage()
+
+FUNKTION:
+	void doUnwieldMessage(object wielded_by);
+
+DEFINIERT IN:
+	/std/weapon/combat.c
+
+ARGUMENTE:
+	wielded_by
+          Das Lebewesen, dass die (Parier)Waffe bisher gezueckt hatte.
+
+BESCHREIBUNG:
+        Diese Funktion gibt beim Wegstecken einer Waffe die entspr.
+        Meldung an den Benutzer und die Umgebung aus.
+
+RUeCKGABEWERT:
+	keiner
+
+SIEHE AUCH:
+        doWearMessage(), doUnwearMessage(), doWieldMessage()
+
+----------------------------------------------------------------------------
+Last modified: Sat Jun 26 15:32:00 1999 by Paracelsus
+
diff --git a/doc/lfun/doWearMessage b/doc/lfun/doWearMessage
new file mode 100644
index 0000000..1962d5c
--- /dev/null
+++ b/doc/lfun/doWearMessage
@@ -0,0 +1,26 @@
+doWearMessage()
+
+FUNKTION:
+	varargs void doWearMessage(int all);
+
+DEFINIERT IN:
+	/std/armour/combat.c
+
+ARGUMENTE:
+	all
+          Wurde "trage alles"/"trage alle ruestungen" eingegeben, so
+          ist all gesetzt, ansonsten nicht.
+
+BESCHREIBUNG:
+        Diese Funktion gibt beim Anziehen einer Ruestung die entspr.
+        Meldung an den Traeger und die Umgebung aus.
+
+RUeCKGABEWERT:
+	keiner
+
+SIEHE AUCH:
+        doUnwearMessage(), doWieldMessage(), doUnwieldMessage()
+
+----------------------------------------------------------------------------
+Last modified: Sat Jun 26 15:30:00 1999 by Paracelsus
+
diff --git a/doc/lfun/doWieldMessage b/doc/lfun/doWieldMessage
new file mode 100644
index 0000000..2315faa
--- /dev/null
+++ b/doc/lfun/doWieldMessage
@@ -0,0 +1,24 @@
+doWieldMessage()
+
+FUNKTION:
+	void doWieldMessage();
+
+DEFINIERT IN:
+	/std/weapon/combat.c
+
+ARGUMENTE:
+	keine
+
+BESCHREIBUNG:
+        Diese Funktion gibt beim Zuecken einer Waffe die entspr.
+        Meldung an den Benutzer und die Umgebung aus.
+
+RUeCKGABEWERT:
+	keiner
+
+SIEHE AUCH:
+        doWearMessage(), doUnwearMessage(), doUnwieldMessage()
+
+----------------------------------------------------------------------------
+Last modified: Sat Jun 26 15:33:00 1999 by Paracelsus
+
diff --git a/doc/lfun/do_damage b/doc/lfun/do_damage
new file mode 100644
index 0000000..2d58839
--- /dev/null
+++ b/doc/lfun/do_damage
@@ -0,0 +1,44 @@
+do_damage(L)
+FUNKTION:
+     int do_damage(int dam,mixed enemy);
+
+DEFINIERT IN:
+     /std/living/life.c
+
+ARGUMENTE:
+     int dam
+	  Die abzuziehenden Lebenspunkte (HP).
+     object enemy
+	  Das Objekt, welches den Schaden zufuegt.
+
+BESCHREIBUNG:
+     Dem Lebewesen werden <dam> HP abgezogen. Falls weniger als 0 HP uebrig
+     bleiben, so stirbt es automatisch mittels die().
+     Ein Lebewesen, welches sich bewegt hat, trifft die ersten Kampfrunden
+     sehr viel schlechter, um Rein-Raus-Attacken zu verhindern. Dieses
+     Vorgehen kann man mittels P_ENABLE_IN_ATTACK_OUT deaktivieren.
+     Lebewesen, welche P_NO_ATTACK gesetzt haben, kann man mit dieser
+     Funktion nicht schaden.
+
+RUeCKGABEWERT:
+     Der tatsaechlich verursachte Schaden.
+
+BEMERKUNGEN:
+     Beim Gegner <enemy>, falls vorhanden, werden XP und ALIGN entsprechend
+     angepasst, im Opfer wird der Gegner in P_KILLER vermerkt, der Kampf wird
+     beendet.
+     Diese Funktion sollte nur selten direkt verwendet werden. Meist ist der
+     uebliche Weg ueber Defend() -> do_damage() -> die() die logisch bessere
+     und balancetechnisch guenstigere Loesung, da Defend() magische
+     Verteidigungen, die der Spieler bei sich hat beruecksichtigt.
+     Es sollte also allein schon aus Fairness gegenueber den Objekten
+     anderer Magier Defend() immer dem direkten reduce_hit_points() oder
+     do_damage() vorgezogen werden. Mittels der Flags in 'spell' kann man
+     sehr viele Parameter beeinflussen.
+
+SIEHE AUCH:
+     Verwandt:	Defend(L), reduce_hit_points(L), die(L)
+     Props:	P_NO_ATTACK, P_ENABLE_IN_ATTACK_OUT, P_KILLER
+		P_XP, P_ALIGN
+
+23.Feb.2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/do_frage b/doc/lfun/do_frage
new file mode 100644
index 0000000..dc08610
--- /dev/null
+++ b/doc/lfun/do_frage
@@ -0,0 +1,21 @@
+do_frage()
+FUNKTION:
+     int do_frage(string text)
+
+DEFINIERT IN:
+     /std/npc/info.c
+
+ARGUMENTE:
+     string str	 - Schluesselwort der Frage
+
+BESCHREIBUNG:
+     Bearbeitet ein Schluesselwort, das mit "frage <wen> nach " uebergeben
+     wurde. Gibt entsprechende Antworten formatiert aus.
+
+BEMERKUNGEN:
+     Recht komplex, besser nicht ueberschreiben wenn man es nicht versteht.
+
+SIEHE AUCH:
+     Verwandt:  GetInfoArr, AddInfo
+
+31.Mar 2007 Gloinson
diff --git a/doc/lfun/do_unwear b/doc/lfun/do_unwear
new file mode 100644
index 0000000..cecfa34
--- /dev/null
+++ b/doc/lfun/do_unwear
@@ -0,0 +1,34 @@
+do_unwear()
+
+FUNKTION:
+     varargs int do_unwear(string str, int silent);
+
+DEFINIERT IN:
+     /std/armour/combat.c
+
+ARGUMENTE:
+     str
+          String der normalerweise dem Parameter des "ziehe"-Kommandos
+          entspricht.
+
+     silent
+          1, wenn das Ausziehen ohne Meldungen durchgefuehrt werden soll.
+
+BESCHREIBUNG:
+     Diese Funktion stellt fest, ob die Ruestung sich durch str
+     identifizieren laesst und angezogen ist und ruft im Erfolgsfall
+     UnWear(silent) auf.
+
+RUeCKGABEWERT:
+     1, wenn alles gut ging, ansonsten 0.
+
+BEMERKUNGEN:
+     Wenn man Ruestungen "von aussen" ausziehen will, sollte man direkt
+     UnWear() verwenden!
+
+SIEHE AUCH:
+     do_wear(), UnWear(), RemoveFunc(), InformUnwear(),
+     /std/armour/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Sun Jun 27 22:29:00 1999 by Paracelsus
diff --git a/doc/lfun/do_wear b/doc/lfun/do_wear
new file mode 100644
index 0000000..fe227d6
--- /dev/null
+++ b/doc/lfun/do_wear
@@ -0,0 +1,27 @@
+do_wear()
+
+FUNKTION:
+     varargs int do_wear(string str, int silent);
+
+DEFINIERT IN:
+     /std/armour/combat.c
+
+ARGUMENTE:
+     str
+          String, der normalerweise dem Parameter des
+          "ziehe/trage"-Kommandos entspricht.
+     silent
+          1, wenn das Anziehen ohne Meldungen durchgefuehrt werden soll.
+
+BESCHREIBUNG:
+     Wenn sich die Ruestung mit "str" identifizieren laesst, wird versucht,
+     sie mittels DoWear() anzuziehen.
+
+RUeCKGABEWERT:
+     1, wenn sich die Ruestung anziehen liess, sonst 0.
+
+SIEHE AUCH:
+     do_unwear(), DoWear(), WearFunc(), InformWear(), /std/armour/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Sun Jun 27 22:31:00 1999 by Paracelsus
diff --git a/doc/lfun/drink_alcohol b/doc/lfun/drink_alcohol
new file mode 100644
index 0000000..28ed1dd
--- /dev/null
+++ b/doc/lfun/drink_alcohol
@@ -0,0 +1,83 @@
+FUNKTION:
+    public varargs int drink_alcohol(int strength, int testonly, string mytext)
+
+DEFINIERT IN:
+    /std/living/life.c
+
+ARGUMENTE:
+    strength: wird zur aktuellen Saettigung P_ALCOHOL dazu addiert
+    testonly: Ist das Flag gesetzt, wird dem Spieler kein ALCOHOL zugefuehrt.
+              Darf nur zum Testen der Heilstelle verwendet werden und muss
+              im normalen Betrieb auf '0' stehen!
+    mytext: Wer selber einen Text bei Misserfolg ausgeben lassen moechte,
+            darf sich hier was nettes ausdenken.
+            Achtung: Das unterdrueckt nicht die "Nuechtern"-Meldung, die bei
+            negativem strength auftreten kann, wenn P_ALCOHOL wieder 0 ist.
+
+BESCHREIBUNG:
+    Es wird geprueft, ob dem Spieler der angegebene Wert fuer 'strength'
+    auf seine aktuelle P_ALCOHOL addiert werden kann oder nicht. Falls
+    das moeglich ist und testonly = 0, wird P_ALCOHOL entsprechend
+    aktualisiert.
+
+    Sollen neben P_ALCOHOL noch weitere Props manipuliert werden - bspw. zur
+    Heilung eines Lebewesens - bietet sich die Funktion consume() an.
+
+RUECKGABEWERT:
+    0 bei [potentiellem] Misserfolg (strength + P_ALCOHOL > P_MAX_ALCOHOL)
+    1 bei [potentiellem] Erfolg
+    * potentiell bezieht sich hier auf Nutzung mit 'testonly' != 0
+
+BEMERKUNG:
+    drink_alocohol() bitte anstatt eigener Manipulationen von P_ALCOHOL und
+    P_MAX_ALCOHOL verwenden.
+
+    Achtung: Immer erst VOR einer Heilung ausfuehren und bei Erfolg heilen.
+
+    Bei Heilstellen sollte eine evtl. Heilung des Spielers mit der eigens
+    dafuer eingerichteten Funktion check_and_update_timed_key realisiert
+    werden.
+
+BEISPIEL:
+    int heilstelle() {
+      if(this_player()->drink_alcohol(10, 0,
+                                      "Du prustest in den Schnaps. "
+                                      "Der passt nicht mehr rein.\n")) {
+        // Platz fuer 10 "Alkohol" war noch, diese sind jetzt bereits addiert
+        // Nachricht an den Spieler:
+        tell_object(this_player(),
+                    break_string("Du trinkst den Schnaps aus.", 78));
+
+        // Nachricht an andere Livings im Raum
+        object ob = first_inventory(environment(this_player()));
+        do {
+          if(living(ob) && ob!=this_player())
+            ob->ReceiveMsg(this_player()->Name()+" trinkt einen Schnaps aus.",
+                           MT_LOOK|MT_LISTEN,
+                           MA_DRINK);
+          ob = next_inventory(ob);
+        } while(ob);
+
+        // Rassenabhaengige Heilung: Sofort oder in Schritten
+        // Tragbare Heilungen sollten auch eher buffer_hp/_sp benutzen.
+        if(this_player()->QueryProp(P_REAL_RACE)=="Schnapsdrossel")
+          this_player()->heal_self(30);
+        else {
+          this_player()->buffer_hp(30,5);
+          this_player()->buffer_sp(30,5);
+        }
+      }
+
+      return 1;
+     }
+SIEHE AUCH:
+     Aehnlich:  consume, eat_food, drink_soft
+     Heilung:   heal_self, restore_spell_points, restore_hit_points, 
+                buffer_hp, buffer_sp
+     Timing:    check_and_update_timed_key
+     Enttanken: defuel_drink, defuel_food
+     Props:     P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/lfun/drink_soft b/doc/lfun/drink_soft
new file mode 100644
index 0000000..4ab6e07
--- /dev/null
+++ b/doc/lfun/drink_soft
@@ -0,0 +1,83 @@
+FUNKTION:
+    public varargs int drink_soft(int strength, int testonly, string mytext)
+
+DEFINIERT IN:
+    /std/living/life.c
+
+ARGUMENTE:
+    strength: Wird zu dem augenblicklichen Saettigungsgrad (P_DRINK) addiert.
+    testonly: Ist das Flag gesetzt, wird dem Spieler kein DRINK zugefuehrt.
+              Darf nur zum Testen der Heilstelle verwendet werden und muss
+              im normalen Betrieb auf '0' stehen!
+    mytext: Wer selber einen Text bei Misserfolg ausgeben lassen moechte,
+            darf sich hier was nettes ausdenken.
+            Achtung: Das unterdrueckt nicht die "Durst"-Meldung, die bei
+            negativem strength auftritt, wenn P_DRINK wieder 0 ist.
+
+BESCHREIBUNG:
+    Es wird geprueft, ob dem Spieler der angebene Wert "strength" auf
+    aktuelle P_DRINK addiert werden kann oder nicht. Ist dies moeglich,
+    wird es gemacht, es sei denn das testonly != 0.
+
+    Sollen neben P_DRINK noch weitere Props manipuliert werden - bspw. zur
+    Heilung eines Lebewesens - bietet sich die Funktion consume() an.
+
+RUECKGABEWERT:
+     0, wenn strength + P_DRINK > P_MAX_DRINK
+    >0, wenn Erfolg
+
+BEMERKUNG:
+    drink_soft() bitte anstatt eigener Manipulationen von P_DRINK und
+    P_MAX_DRINK verwenden.
+
+    Achtung: Immer erst VOR einer Heilung ausfuehren und bei Erfolg heilen.
+
+    Bei Heilstellen sollte eine evtl. Heilung des Spielers mit der eigens
+    dafuer eingerichteten Funktion check_and_update_timed_key realisiert
+    werden.
+
+BEISPIEL:
+    int heilstelle() {
+      if(this_player()->drink_soft(10, 0, "Du blubberst nicht ueberzeugt "
+                                   "in der Limonade. Das ist zu viel.\n")) {
+        // Platz fuer 10 "Trinken" war noch, diese sind jetzt bereits addiert
+        // Nachricht an den Spieler:
+        tell_object(this_player(), break_string("Du nimmst einen grossen "
+                    "Schluck zuckersuesse Limonade.", 78));
+
+        // alle anderen im Raum bekommen es ggf auch mit:
+        // 1) filter(..., #'living) ergibt die Lebewesen im Raum
+        // 2) filter_objects(..., "ReceiveMsg") ruft ReceiveMsg an jedem
+        // 3) ReceiveMsg(...) bekommt die Folgeparameter
+        filter_objects(filter(all_inventory(environment(this_player())),
+                              #'living) - ({this_player()}),
+                       "ReceiveMsg",
+                       this_player()->Name()+
+                         " trinkt einen Schluck Limonade.",
+                       MT_LOOK|MT_LISTEN,
+                       MA_DRINK);
+
+        // Rassenabhaengige Heilung: Sofort oder in Schritten
+        // Tragbare Heilungen sollten auch eher buffer_hp/_sp benutzen.
+        if(this_player()->QueryProp(P_REAL_RACE)=="Saufziege")
+          this_player()->heal_self(30);
+        else {
+          this_player()->buffer_hp(30,5);
+          this_player()->buffer_sp(30,5);
+        }
+      }
+
+      return 1;
+     }
+
+SIEHE AUCH:
+     Aehnlich:  consume, drink_alcohol, eat_food
+     Heilung:   heal_self, restore_spell_points, restore_hit_points, 
+                buffer_hp, buffer_sp
+     Timing:    check_and_update_timed_key
+     Enttanken: defuel_drink, defuel_food
+     Props:     P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/lfun/drop b/doc/lfun/drop
new file mode 100644
index 0000000..ab8dd19
--- /dev/null
+++ b/doc/lfun/drop
@@ -0,0 +1,45 @@
+drop()
+
+FUNKTION:
+    public varargs int drop(object o, mixed msg);
+
+DEFINIERT IN:
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+    object o
+        Das Objekt, das fallengelassen werden soll.
+    mixed msg
+        Eine optionale Meldung, die anstelle von P_DROP_MSG oder der
+        Standardmeldung verwendet wird, oder -1, um die Meldung zu
+        unterdruecken.
+
+BESCHREIBUNG:
+    Der Spieler oder NPC laesst das Objekt fallen. Gibt o->move() keinen
+    positiven Wert zurueck, beispielsweise weil das Objekt verflucht ist,
+    bekommt er eine entsprechende Fehlermeldung.
+
+RUECKGABEWERT:
+    Wenn das Fallenlassen geklappt hat, 1, ansonsten 0.
+
+BEMERKUNG:
+    Diese Funktion ist dann sinnvoll, wenn man den Spieler ein Objekt
+    fallenlassen lassen und sich nicht selbst um die Fehlerbehandlung kuemmern
+    moechte - und da unzaehlige verschiedene Dinge schiefgehen koennen und
+    manche Objekte eigene Fehlermeldungen definieren, eigentlich immer.
+
+    Die Funktion prueft nicht, ob der Spieler/NPC das Objekt ueberhaupt hat,
+    das muss man ggf. selbst ermitteln.
+
+BEISPIEL:
+    if (this_player()->drop(obj, ({
+            "Du wirfst @WEN2 in den Saeureteich.\n",
+            "@WER1 wirft @WENU2 in den Saeureteich.\n" })))
+        obj->remove();
+
+SIEHE AUCH:
+    move(L), P_DROP_MSG, drop_objects(L), P_NOINSERT_MSG, P_NOLEAVE_MSG,
+    P_TOO_MANY_MSG, P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_NODROP
+
+----------------------------------------------------------------------------
+Last modified: Thu Aug 28 22:20:37 2008 by Amynthor
diff --git a/doc/lfun/drop_obj b/doc/lfun/drop_obj
new file mode 100644
index 0000000..2f25e0f
--- /dev/null
+++ b/doc/lfun/drop_obj
@@ -0,0 +1,28 @@
+drop_obj()
+
+FUNKTION
+    int drop_obj(object ob);
+
+DEFINIERT IN:
+
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+
+    ob  Das Objekt, das von dem Lebewesen, in dem diese Funktion aufgerufen
+        wird, fallengelassen werden soll.
+
+BESCHREIBUNG:
+
+    Das Lebewesen, in dem diese Funktion aufgerufen werden soll, laesst
+    den angegebenen Gegenstand fallen, wenn dies moeglich ist.
+
+RUeCKGABEWERT:
+    1, wenn das Objekt fallen gelassen wurde oder dies nicht moeglich war.
+    (in diesem Fall wird auch direkt eine Textausgabe ausgegeben)
+    0 sonst, in diesem Fall wird in notify_fail eine passende Ausgabe
+    plaziert.
+
+SIEHE AUCH:
+
+    find_obs(), give_obj(), pick_obj(), put_obj(), /std/living/put_and_get.c
diff --git a/doc/lfun/drop_objects b/doc/lfun/drop_objects
new file mode 100644
index 0000000..c1045f4
--- /dev/null
+++ b/doc/lfun/drop_objects
@@ -0,0 +1,48 @@
+drop_objects()
+
+FUNKTION:
+    public varargs int drop_objects(string str, mixed msg);
+
+DEFINIERT IN:
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+    string str
+        Was fallengelassen werden soll.
+    mixed msg
+        Eine optionale Meldung, die anstelle von P_DROP_MSG oder der
+        Standardmeldung verwendet wird, oder -1, um die Meldung zu
+        unterdruecken.
+
+BESCHREIBUNG:
+    Der Spieler oder NPC laesst die in <str> benannten Sachen fallen.
+    Kann er ein Objekt nicht fallenlassen, bekommt er eine entsprechende
+    Fehlermeldung. Wenn keine Objekte auf <str> passen, wird per
+    _notify_fail() eine Meldung gesetzt, aber noch nicht ausgegeben.
+
+RUECKGABEWERT:
+    Wenn <str> irgendwelche vorhandenen Sachen sind, 1, sonst 0.
+
+BEMERKUNG:
+    Wenn die Funktion 1 zurueckgibt, heisst das noch nicht, dass der Spieler
+    etwas fallengelassen hat! Er hat es nur versucht, d.h. auf jeden Fall eine
+    Meldung bekommen. Gibt die Funktion 0 zurueck, hat er noch keine bekommen.
+
+BEISPIEL:
+    private int cmd_opfern(string str)
+    {
+        notify_fail("WAS moechtest Du opfern?\n");
+
+        if (!this_player()->drop_objects(str, ({ "Du opferst @WEN2.",
+                                                 "@WER1 opfert @WENU2.\n" })))
+            return 0;
+
+        filter_objects(this_player()->moved_objects(), "remove");
+        return 1;
+    }
+
+SIEHE AUCH:
+    move(L), drop(L), P_DROP_MSG, find_objects(L), moved_objects(L)
+
+----------------------------------------------------------------------------
+Last modified: Fri Jul 25 10:59:37 2008 by Amynthor
diff --git a/doc/lfun/eat_food b/doc/lfun/eat_food
new file mode 100644
index 0000000..ec0102d
--- /dev/null
+++ b/doc/lfun/eat_food
@@ -0,0 +1,86 @@
+FUNKTION:
+    public varargs int eat_food(int food, int testonly, string mytext)
+
+DEFINIERT IN:
+    /std/living/life.c
+
+ARGUMENTE:
+    food: Wird zu dem augenblicklichen Saettigungsgrad (P_FOOD) addiert.
+    testonly: Ist das Flag gesetzt, wird dem Spieler kein FOOD zugefuehrt.
+              Darf nur zum Testen der Heilstelle verwendet werden und muss
+              im normalen Betrieb auf '0' stehen!
+    mytext: Wer selber einen Text bei Misserfolg ausgeben lassen moechte,
+            darf sich hier was nettes ausdenken.
+            Achtung: Das unterdrueckt nicht die "Hunger"-Meldung, die bei
+            negativem food auftreten kann, wenn P_FOOD wieder 0 ist.
+
+BESCHREIBUNG:
+    Es wird geprueft, ob dem Spieler der angebene Wert "strength" auf seine
+    aktuelle P_FOOD addiert werden kann oder nicht. Ist dies moeglich, wird
+    wird es gemacht, es sei denn das testonly != 0.
+
+    Sollen neben P_FOOD noch weitere Props manipuliert werden - bspw. zur
+    Heilung eines Lebewesens - bietet sich die Funktion consume() an.
+
+RUECKGABEWERT:
+     0, wenn strength + P_FOOD > P_MAX_FOOD.
+    >0, wenn Erfolg.
+
+BEMERKUNG:
+    eat_food() bitte anstatt eigener Manipulationen von P_FOOD und
+    P_MAX_FOOD verwenden.
+
+    Achtung: Immer erst VOR einer Heilung ausfuehren und bei Erfolg heilen.
+
+    Bei Heilstellen sollte eine evtl. Heilung des Spielers mit der eigens
+    dafuer eingerichteten Funktion check_and_update_timed_key realisiert
+    werden.
+
+BEISPIEL:
+    int heilstelle() {
+      // Wenn auf das P_FOOD des Spielers die angegebenen 10 nicht mehr addiert
+      // addiert werden koennen (weil sonst P_MAX_FOOD ueberschritten wird),
+      // wird die Fehlermeldung ausgegeben, dass der Spieler nichts mehr
+      // essen/trinken kann.
+      // Bei gesetztem 'mytext' wird 'mytext' an den Spieler ausgegeben.
+      // Ansonsten wird die Standardmeldung ausgegeben.
+      if (!this_player()->eat_food(10, 0, "Der Keks ist einfach "
+                                          "zuviel fuer Dich.\n") )
+        return 1;
+
+      // Spieler hatte noch ausreichend Spielraum bei P_FOOD. Die 10 sind 
+      // schon addiert worden. Jetzt Nachricht ausgeben:
+      tell_object(this_player(), break_string("Du knabberst ein bisschen an "
+                  "dem Keks herum und fuehlst Dich gleich viel besser.", 78));
+
+      // alle Lebewesen im Raum bekommen das auch mit
+      foreach(object ob:
+              (filter(all_inventory(environment(this_player())), #'living) -
+               ({this_player()})))
+        ob->ReceiveMsg(this_player()->Name()+" knuspert einen Keks weg.",
+                       MT_LOOK|MT_LISTEN,
+                       MA_EAT);
+
+      // Rassenabhaengige Heilung: Sofort oder in Schritten
+      // Tragbare Heilungen sollten auch eher buffer_hp/_sp benutzen.
+      if(this_player()->QueryProp(P_REAL_RACE)=="Kruemelmonster")
+        this_player()->heal_self(30);
+      else {
+        this_player()->buffer_hp(30,5);
+        this_player()->buffer_sp(30,5);
+      }
+
+      return 1;
+    }
+
+SIEHE AUCH:
+     Aehnlich:  consume, drink_alcohol, drink_soft
+     Heilung:   heal_self, restore_spell_points, restore_hit_points, 
+                buffer_hp, buffer_sp
+     Timing:    check_and_update_timed_key
+     Enttanken: defuel_drink, defuel_food
+     Props:     P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/lfun/execute_anything b/doc/lfun/execute_anything
new file mode 100644
index 0000000..9c889f0
--- /dev/null
+++ b/doc/lfun/execute_anything
@@ -0,0 +1,42 @@
+execute_anything()
+FUNKTION:
+    protected mixed execute_anything(mixed fun, mixed args, ...)
+
+DEFINIERT IN:
+    /std/util/executer.c
+
+ARGUMENTE:
+    mixed fun        auszufuehrende Funktion/Array/Closure ... anything
+    mixed args       weitere Argumente, werden weiter uebergeben
+
+BESCHREIBUNG:
+    Fuehrt "alles" aus:
+    
+    'fun' kann sein:
+    - eine Closure
+      [funcall(fun, args, ...)]
+    - einen String als Funktion im Objekt
+      [call_other(this_object(), fun, args, ...)]
+    - ein Array mit Objekt/Objektnamen + Funktion als Aufruf der Funktion am
+      Objekt/Objektnamen:
+      [call_other(array[0], array[1], args...)]
+
+    Wird execute_anything() aus dem restriction_checker gerufen, wird als
+    erster Parameter immer das gepruefte Living uebergeben.
+
+BEMERKUNGEN:
+    Es kann sich anbieten, die gerufene Funktion mit varargs-Argumenten zu
+    gestalten, damit ihr mehr Argumente uebergeben werden koennen als in der
+    Funktionsdefinition angegeben sind. 
+
+    Der Restriktionspruefer (/std/restriction_checker) versteht im Falle eines
+    Arrays mit Objekt/Objektnamen + Funktion auch ein Array mit mehr als 2
+    Elementen. Die weiteren Elemente werden dann zu weiteren Argumenten der
+    gerufenen Funktion. Hierbei wird <pl> ggf. aber als erstes Argument vor
+    alle anderen eingefuegt.
+
+SIEHE AUCH:
+    check_restrictions, varargs
+
+31.12.2013, Zesstra
+
diff --git a/doc/lfun/exit b/doc/lfun/exit
new file mode 100644
index 0000000..190db91
--- /dev/null
+++ b/doc/lfun/exit
@@ -0,0 +1,33 @@
+exit()
+
+FUNKTION:
+  varargs void exit(object liv, object dest);
+
+DEFINIERT IN:
+  /std/room/description.c
+
+ARGUMENTE:
+  liv - (object) Das bewegte Lebewesen.
+  dest - (object) Das Environment, in welches bewegt wird.
+
+BESCHREIBUNG:
+  Diese Funktion wird immer dann aufgerufen, wenn ein Lebewesen den
+  Raum verlaesst. Standardmaessig werden hier ggf. die Raummeldungen
+  abgestellt, man kann aber auch eigene Checks einhaengen. In dem Fall MUSS
+  man aber das geerbte exit() auf jeden Fall aufrufen.
+
+RUeCKGABEWERT:
+  keiner
+
+BEMERKUNG:
+  Man beachte, dass this_player() 0 sein kann, wenn z.B. ein netztoter Spieler
+  Spieler den Raum verlaesst.
+  Man beachte ausserdem, dass this_player() nicht unbedingt das bewegte Living
+  sein muss. Wenn z.B. jemand ein Lebewesen bewegt, ist TP der, der die
+  Bewegung durchfuehrt, nicht das Lebewesen.
+
+SIEHE AUCH:
+  init(), this_player(), previous_object(), caller_stack(),
+  NotfiyLeave(), NotifyRemove(), NotifyDestruct()
+----------------------------------------------------------------------------
+Last modified: 25.02.2009, Zesstra
diff --git a/doc/lfun/find_obs b/doc/lfun/find_obs
new file mode 100644
index 0000000..8f44384
--- /dev/null
+++ b/doc/lfun/find_obs
@@ -0,0 +1,38 @@
+find_obs()
+
+FUNKTION
+    object* find_obs(string str, int meth)
+
+DEFINIERT IN:
+
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+
+    str     Der String der geparsed werden soll.
+    meth    Mit Hilfe dieses Parameters koennen bestimmte Bereichs-
+            eingrenzungen vorgenommen werden (definiert in moving.h):
+
+            PUT_GET_NONE - keinerlei Bereichseingrenzung.
+            PUT_GET_TAKE - es handelt sich um ein Nehmen von Gegenstaenden
+                           also wird das inventory ausgenommen.
+            PUT_GET_DROP - es handelt sich um das Entfernen von Gegenstaenden
+                           also wird das environment ausgenommen.           
+                                                                            
+BESCHREIBUNG:                                                               
+                                                                            
+    Der String (str) muss folgendes Format haben damit Objekte gefunden
+    werden.
+
+    <gegenstand> [aus container] [in mir|im raum]
+
+    <gegenstand> kann hierbei sowohl eine Objekt-ID als auch ein
+    Gruppenbezeichner wie z.b. "alles" sein.
+
+RUeCKGABEWERT:
+
+    Ein Array mit allen Objekten die sich angesprochen fuehlen, oder aber 0.
+
+SIEHE AUCH:
+
+    drop_obj(), give_obj(), pick_obj(), put_obj(), /std/living/put_and_get.c
diff --git a/doc/lfun/get_killing_player b/doc/lfun/get_killing_player
new file mode 100644
index 0000000..16685b9
--- /dev/null
+++ b/doc/lfun/get_killing_player
@@ -0,0 +1,39 @@
+get_killing_player()
+
+FUNKTION:
+     protected object get_killing_player()
+
+DEFINIERT IN:
+     /std/living/life.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Liefert im Tod (nach dem toetenden do_damage()) das Spielerobjekt, was
+     den Tod wohl zu verantworten hat, falls es ermittelt werden kann. Es
+     werden registrierte Helfer-NPC und einige schadenverursachende Objekte
+     beruecksichtigt. Hierbei wird QueryUser() in den Objekten abgefragt.
+
+     Es benoetigt ein gueltiges P_KILLER, d.h. falls das Lebewesen vergiftet
+     wurde oder das toetende Objekt aus sonstigen Gruenden nicht in P_KILLER
+     steht, funktioniert es nicht.
+     Auch gibt es bestimmt Objekte, die fuer Spieler toeten koennen, die die
+     diese Funktion nicht kennt.
+     (Dies gilt beides ebenso fuer /p/service/mupfel/getkill.c, ist also kein
+      Grund, jenes statt dieser Funktion zu nutzen.)
+
+RUeCKGABEWERT:
+     Das Objekt des Spielers, falls es ermittelt werden konnte, sonst 0.
+
+BEMERKUNGEN:
+    Der Name des Spieler ist mittel Name() ermittelbar. Will man die Info, 
+    womit ein Spieler den Kill ggf. gemacht hat, kann man P_KILLER
+    auswerten/nutzen.
+
+SIEHE AUCH:
+     QueryUser
+     P_KILLER
+----------------------------------------------------------------------------
+11.11.2013, Zesstra
+
diff --git a/doc/lfun/gilde/AddSkill b/doc/lfun/gilde/AddSkill
new file mode 100644
index 0000000..21db504
--- /dev/null
+++ b/doc/lfun/gilde/AddSkill
@@ -0,0 +1,33 @@
+AddSkill()
+FUNKTION:
+    varargs int AddSkill(string sname, mapping ski)
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+ARGUMENTE:
+    string sname       Name des Skills
+    mapping ski        Skill-Mapping
+
+BESCHREIBUNG:
+    Fuegt den Skill zur Liste der in dieser Gilde lernbaren Skills
+    hinzu. Das Mapping enthaelt Informationen, die der Gildenraum
+    bzw. das Gildenobjekt zum Skill herausgeben und die das Lernen
+    des Skills beeinflussen.
+
+RUECKGABWERT:
+    1 fuer Erfolg
+
+BEISPIEL:
+    AddSkill( FIGHT(WT_CLUB), ([ OFFSET(SI_SKILLLEARN) : 1 ]) );
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, LearnSpell, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSpell (gilde), QuerySpell, QuerySkill
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:     GuildRating
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/gilde/AddSpell b/doc/lfun/gilde/AddSpell
new file mode 100644
index 0000000..c0ca935
--- /dev/null
+++ b/doc/lfun/gilde/AddSpell
@@ -0,0 +1,47 @@
+AddSpell()
+FUNKTION:
+    varargs int AddSpell(string verb, mapping ski)
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+ARGUMENTE:
+    string verb        Name des Spells
+    mapping ski        Skill-Mapping
+
+BESCHREIBUNG:
+    Fuegt den Spell zur Liste der in dieser Gilde lernbaren Spells
+    hinzu. Das Mapping enthaelt Informationen, die der Gildenraum
+    bzw. das Gildenobjekt zum Spell herausgeben und die das Lernen
+    des Spells beeinflussen.
+
+RUECKGABWERT:
+    1 fuer Erfolg
+
+BEMERKUNGEN:
+    Siehe das Verhalten von QuerySpell (gilde) zum Zusammenfuegen
+    der AddSpell-Informationen aus Gilde und Spellbook. Relevant
+    zB fuer Lernrestriktionen.
+
+BEISPIEL:
+    AddSpell("entfluche",
+      ([SI_SKILLRESTR_LEARN :
+        ([P_GUILD_LEVEL: LVL_WANDER,
+          SR_FUN:        #'glaubensTest]),
+        SI_DIFFICULTY: 100,
+        SI_SKILLINFO:  "Wanderprediger ab Stufe 7",
+        SI_SKILLINFO_LONG: break_string(
+          "Um jemanden von einem laestigen Sprachfluch zu befreien, "
+          "sollte man diese Anrufung benutzen [...]", 78),
+        SI_GOD:        LEMBOLD]));
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, LearnSpell, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSkill, QuerySpell, QuerySkill, QuerySkill
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:     GuildRating
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+
+3. Okt 2011 Gloinson
diff --git a/doc/lfun/gilde/GuildRating b/doc/lfun/gilde/GuildRating
new file mode 100644
index 0000000..7bfb6b2
--- /dev/null
+++ b/doc/lfun/gilde/GuildRating
@@ -0,0 +1,27 @@
+GuildRating()
+FUNKTION:
+    int GuildRating(object pl)
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+ARGUMENTE:
+    object pl          Spieler, der geratet werden soll
+
+BESCHREIBUNG:
+    Gibt das Guild-Rating des Spielers im Bereich 0-MAX_ABILITY zurueck
+    und schreibt sie in P_GUILD_RATING. Wichtig fuer Levelbestimmung!
+    
+    Normalerweise ist das der Mittelwert der Skill-Abilities aller Skills
+    und Spells, das Verhalten kann aber ueberschrieben werden.
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, LearnSpell, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSpell (gilde), AddSkill, QuerySpell, QuerySkill
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS, P_GUILD_RATING
+    Gildenfkt.:
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+
+10. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/gilde/InitialSkillAbility b/doc/lfun/gilde/InitialSkillAbility
new file mode 100644
index 0000000..28a2689
--- /dev/null
+++ b/doc/lfun/gilde/InitialSkillAbility
@@ -0,0 +1,31 @@
+InitialSkillAbility()
+FUNKTION:
+    static int InitialSkillAbility(mapping ski, object pl)
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+ARGUMENTE:
+    mapping ski     Der zu lernende Skill
+    object  pl      Spieler
+
+BESCHREIBUNG:
+    Gibt den initialen Ability-Wert fuer einen neu zu lernenden Skill (Spell)
+    zurueck. Die Standardformel benutzt nur Intelligenz und SI_SKILLLEARN und
+    kann in der Gilde ueberschrieben werden.
+
+BEMERKUNGEN:
+    Der zurueckgegebene Wert wird noch in das Spieler-Skillsystem eingegeben
+    und daher kann der real gelernte Wert abweichen
+
+SIEHE AUCH:
+    Skills:         LimitAbility, ModifySkill
+    GObj Lernen:    LearnSkill, LearnSpell
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSpell (gilde), AddSkill, QuerySpell, QuerySkill
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:     GuildRating
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/gilde/LearnSkill b/doc/lfun/gilde/LearnSkill
new file mode 100644
index 0000000..c381959
--- /dev/null
+++ b/doc/lfun/gilde/LearnSkill
@@ -0,0 +1,35 @@
+LearnSkill()
+FUNKTION:
+    int LearnSkill(string skill)
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+ARGUMENTE:
+    string skill     Der zu lernende Skill
+
+BESCHREIBUNG:
+    Diese Funktion ueberprueft den Spieler auf Gildenzugehoerigkeit
+    und ruft, falls kein 'skill' angegeben ist die SkillListe()
+    fuer Skills auf.
+
+    Falls ein Argument angegeben wird, wird bei dem Spieler dieser Skill
+    initialisiert, sofern er die notwendigen Anforderungen erfuellt hat.
+
+RUECKGABEWERT:
+    0 bei Fehler, 1 bei Erfolg.
+
+BEMERKUNGEN:
+    Fuer die Lebewesen-Skills gibt es eine gleichnamige Funktion.
+    Siehe dafuer LearnSkill.
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSpell, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSpell (gilde), AddSkill, QuerySpell, QuerySkill
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:     GuildRating
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/gilde/LearnSpell b/doc/lfun/gilde/LearnSpell
new file mode 100644
index 0000000..55c5840
--- /dev/null
+++ b/doc/lfun/gilde/LearnSpell
@@ -0,0 +1,32 @@
+LearnSpell()
+FUNKTION:
+    varargs int LearnSpell(string spell, object pl)
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+ARGUMENTE:
+    string spell     Der zu lernende Spell
+    object pl        lernender Spieler, wenn 0, wird this_player() genommen
+
+BESCHREIBUNG:
+    Diese Funktion ueberprueft den Spieler auf Gildenzugehoerigkeit
+    und ruft, falls kein 'spell' angegeben ist die SkillListe()
+    fuer Spells auf.
+
+    Falls ein Argument angegeben wird, wird bei dem Spieler dieser Spell
+    initialisiert, sofern er die notwendigen Anforderungen erfuellt hat.
+
+RUECKGABEWERT:
+    0 bei Fehler, 1 bei Erfolg.
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSpell (gilde), AddSkill, QuerySpell, QuerySkill
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:     GuildRating
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/gilde/QuerySkill b/doc/lfun/gilde/QuerySkill
new file mode 100644
index 0000000..59dce12
--- /dev/null
+++ b/doc/lfun/gilde/QuerySkill
@@ -0,0 +1,33 @@
+QuerySkill()
+FUNKTION:
+    mapping QuerySkill(string skill)
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+ARGUMENTE:
+    string skill       Name des Skills
+
+BESCHREIBUNG:
+    Liefert fuer diesen Skill die Gildeninformationen in einem
+    Mapping zurueck.
+
+BEISPIEL:
+    // /gilden/klerus->QuerySkill(FIGHT(WT_CLUB)) gibt zB zurueck:
+    ([SI_FUN:                "Fight_club",
+      OFFSET(SI_SKILLLEARN): 1
+      SI_SKILLRESTR_USE:     ([SR_FUN:#'gilden/klerus->spellTest()]),
+      OFFSET(SI_SKILLLEARN): #'gilden/klerus->learnOffset,
+      OFFSET(SI_SPELLCOST):  #'gilden/klerus->costOffset,
+      FACTOR(SI_SPELLCOST):  #'gilden/klerus->costFactor])
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, LearnSpell, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSkill, AddSpell, QuerySpell
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:     GuildRating
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/gilde/QuerySpell b/doc/lfun/gilde/QuerySpell
new file mode 100644
index 0000000..22f1965
--- /dev/null
+++ b/doc/lfun/gilde/QuerySpell
@@ -0,0 +1,54 @@
+QuerySpell()
+FUNKTION:
+    mapping QuerySpell(string spell)
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+ARGUMENTE:
+    string spell       Name des Spells
+
+BESCHREIBUNG:
+    Liefert fuer diesen Spell die Gilden- und Spellbookinformationen
+    zusammengefasst in ein Mapping zurueck.
+    Die Gildeninformationen haben dabei Vorrang (d.h. eine Lernrestriktion
+    im Spellbook wird benutzt, bei unterschiedlichen Werten fuer das gleiche
+    Attribut geht der Wert in der Gilde vor).
+
+BEISPIEL:
+    // /gilden/klerus->QuerySpell("entfluche") gibt zB zurueck:
+    ([SI_SPELLFATIGUE: 2,
+      SI_SP_LOW_MSG:   "Deine Konzentration reicht nicht aus, das Interesse "
+                       "der Goetter zu "wecken.\n",
+      SI_TIME_MSG:     "So schnell koennen sich die Goetter nicht wieder um "
+                       "Dich kuemmern!\n",
+      SI_SKILLLEARN:   5,
+      OFFSET(SI_SKILLLEARN):15
+      SI_SKILLRESTR_LEARN:
+      ([P_LEVEL:       7,
+        P_GUILD_LEVEL: LVL_WANDER,
+        SR_FUN:        #'gilden/klerus->glaubensTest]),
+      SI_DIFFICULTY:   100,
+      SI_SKILLINFO:    "Wanderprediger ab Stufe 7",
+      SI_SKILLINFO_LONG:
+        "Um jemanden von einem laestigen Sprachfluch zu befreien, "
+        "sollte man diese Anrufung benutzen [...]",
+      SP_NAME:         "entfluche",
+      SI_SKILLRESTR_USE: ([ SR_FREE_HANDS : 0 ]),
+      SI_MAGIC_TYPE:   ({MT_SAKRAL})
+      SI_GOD:          LEMBOLD,
+      SI_SKILLRESTR_USE:     ([SR_FUN:#'gilden/klerus->spellTest()]),
+      OFFSET(SI_SKILLLEARN): #'gilden/klerus->learnOffset,
+      OFFSET(SI_SPELLCOST):  #'gilden/klerus->costOffset,
+      FACTOR(SI_SPELLCOST):  #'gilden/klerus->costFactor])
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, LearnSpell, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSkill, AddSpell, QuerySkill
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:     GuildRating
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+
+3. Okt 2011 Gloinson
diff --git a/doc/lfun/gilde/SkillListe b/doc/lfun/gilde/SkillListe
new file mode 100644
index 0000000..8cc2b39
--- /dev/null
+++ b/doc/lfun/gilde/SkillListe
@@ -0,0 +1,27 @@
+SkillListe()
+FUNKTION:
+    int SkillListe(int what)
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+ARGUMENTE:
+    int what        Rueckgabeoption:
+
+BESCHREIBUNG:
+    Gibt eine Text mit Liste mit lernbaren Skills/Spells zurueck:
+    
+    Fuer 'what': 0 - alle; 1 - Spells; 2 - Skills.
+    
+    Zeigt Eintraege aus SI_SKILLINFO.
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, LearnSpell, InitialSkillAbility
+    * Verwalten:    AddSpell (gilde), AddSkill, QuerySpell, QuerySkill
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:     GuildRating
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+    Skills:         skill_info_liste
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/gilde/UseSpell b/doc/lfun/gilde/UseSpell
new file mode 100644
index 0000000..96823cc
--- /dev/null
+++ b/doc/lfun/gilde/UseSpell
@@ -0,0 +1,32 @@
+UseSpell()
+FUNKTION:
+    varargs int UseSpell(object caster, string spell, mapping sinfo)
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+ARGUMENTE:
+    object caster      Spieler, der Spell nutzt
+    string spell       Spell-Name
+    mapping sinfo      Spell-Informationen
+
+BESCHREIBUNG:
+    Prueft, ob der Spell in der Gilde definiert ist und ruft ihn dann
+    ggf in aus dem Spell-Mapping gelesenen Gilden-SI_SPELLBOOK auf.
+
+    Wird von living/skills::UseSpell gerufen, wenn dieses die SI_CLOSURE,
+    also die Funktion eines Spells sucht, fuer den kein SI_SPELLBOOK
+    explizit angegeben wurde.
+
+RUECKGABWERT:
+    Rueckgabewert des Spells aus dem Spellbook oder 0.
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, LearnSpell, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSpell (gilde), AddSkill, QuerySpell, QuerySkill
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:     GuildRating
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/gilde/austreten b/doc/lfun/gilde/austreten
new file mode 100644
index 0000000..4770bef
--- /dev/null
+++ b/doc/lfun/gilde/austreten
@@ -0,0 +1,34 @@
+austreten()
+FUNKTION:
+    varargs int austreten(int loss)
+
+ARGUMENTE:
+    int loss       Prozentsatz, um den sich die Gildenskills verschlechtern
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+BESCHREIBUNG:
+    Austrittsfunktion der Gilde. Prueft die Restriktionen der Gilde und
+    laesst this_player() ggf austreten. Das Austreten aus der Standard-
+    gilde ist dabei nicht moeglich.
+
+    Der Gildenmaster loest ggf ein EVT_GUILD_CHANGE aus. Dabei werden
+    E_OBJECT, E_GUILDNAME, E_LAST_GUILDNAME entsprechend gesetzt.
+
+    Der Gildenmaster senkt auch die Skill/Spell-Faehigkeiten um 'loss' bzw.
+    normalerweise mindestens 20%.
+
+    Durch Ueberschreiben koennen hier zB Abschiedsmeldungen gesendet werden.
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, LearnSpell, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSpell (gilde), AddSkill, QuerySpell, QuerySkill
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:
+    * Ein/Austritt: beitreten, bei_oder_aus_treten
+    * Props dafuer: P_GUILD_RESTRICTIONS
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/gilde/bei_oder_aus_treten b/doc/lfun/gilde/bei_oder_aus_treten
new file mode 100644
index 0000000..0a5de78
--- /dev/null
+++ b/doc/lfun/gilde/bei_oder_aus_treten
@@ -0,0 +1,25 @@
+bei_oder_aus_treten()
+FUNKTION:
+    int bei_oder_aus_treten(string str)
+
+ARGUMENTE:
+    string str       Spielerparameter
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+BESCHREIBUNG:
+    Aus- oder Eintrittsfunktion, ruft beitreten() bzw. austreten() auf.
+    Wird im Std-Gildenraum per AddCmd zur Verfuegung gestellt.
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, LearnSpell, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSpell (gilde), AddSkill, QuerySpell, QuerySkill
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:
+    * Ein/Austritt: beitreten, austreten
+    * Props dafuer: P_GUILD_RESTRICTIONS
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/gilde/beitreten b/doc/lfun/gilde/beitreten
new file mode 100644
index 0000000..3938f7b
--- /dev/null
+++ b/doc/lfun/gilde/beitreten
@@ -0,0 +1,28 @@
+beitreten()
+FUNKTION:
+    int beitreten()
+
+DEFINIERT IN:
+    /std/gilden_ob.c
+
+BESCHREIBUNG:
+    Beitrittsfunktion der Gilde. Prueft die Gilde selbst im Gildenmaster,
+    dann die Restriktionen der Gilde und laesst this_player() ggf eintreten.
+
+    Der Gildenmaster loest ggf ein EVT_GUILD_CHANGE aus. Dabei werden
+    E_OBJECT, E_GUILDNAME, E_LAST_GUILDNAME entsprechend gesetzt.
+
+    Durch Ueberschreiben koennen hier zB Standard-Skills und Spells den
+    Spieler bei Eintritt gelehrt werden.
+
+SIEHE AUCH:
+    GObj Lernen:    LearnSkill, LearnSpell, InitialSkillAbility
+    * Anzeigen:     SkillListe
+    * Verwalten:    AddSpell (gilde), AddSkill, QuerySpell
+    * Nutzen:       UseSpell (gilde)
+    * Properties:   P_GUILD_SKILLS, P_GLOBAL_SKILLPROPS
+    Gildenfkt.:
+    * Ein/Austritt: bei_oder_aus_treten, austreten
+    * Props dafuer: P_GUILD_RESTRICTIONS
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/give b/doc/lfun/give
new file mode 100644
index 0000000..39a2624
--- /dev/null
+++ b/doc/lfun/give
@@ -0,0 +1,43 @@
+give()
+
+FUNKTION:
+    public varargs int give(object o, object dest, mixed msg);
+
+DEFINIERT IN:
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+    object o
+        Das Objekt, das uebergeben werden soll.
+    object dest
+        Der Spieler oder NPC, der das Objekt bekommen soll.
+    mixed msg
+        Eine optionale Meldung, die anstelle von P_GIVE_MSG oder der
+        Standardmeldung verwendet wird, oder -1, um die Meldung zu
+        unterdruecken.
+
+BESCHREIBUNG:
+    Der Spieler oder NPC uebergibt dem Empfaenger das Objekt. Gibt o->move()
+    keinen positiven Wert zurueck, beispielsweise weil das Objekt verflucht
+    ist oder der Empfaenger es nicht mehr tragen kann, bekommt er eine
+    entsprechende Fehlermeldung.
+
+RUECKGABEWERT:
+    Wenn die Uebergabe geklappt hat, 1, ansonsten 0.
+
+BEMERKUNG:
+    Diese Funktion ist dann sinnvoll, wenn man den Spieler ein Objekt
+    weitergeben lassen und sich nicht selbst um die Fehlerbehandlung kuemmern
+    moechte - und da unzaehlige verschiedene Dinge schiefgehen koennen und
+    manche Objekte eigene Fehlermeldungen definieren, eigentlich immer.
+
+    Die Funktion prueft nicht, ob der Spieler/NPC der Objekt ueberhaupt hat,
+    das muss man ggf. selbst ermitteln.
+
+SIEHE AUCH:
+    move(L), P_GIVE_MSG, give_objects(L), give_notify(L),
+    P_NOINSERT_MSG, P_NOLEAVE_MSG, P_TOO_MANY_MSG,
+    P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_NODROP
+
+----------------------------------------------------------------------------
+Last modified: Thu Aug 28 22:21:19 2008 by Amynthor
diff --git a/doc/lfun/give_notify b/doc/lfun/give_notify
new file mode 100644
index 0000000..010d68e
--- /dev/null
+++ b/doc/lfun/give_notify
@@ -0,0 +1,59 @@
+give_notify()
+FUNKTION:
+     void give_notify(object obj);
+
+DEFINIERT IN:
+     /std/npc/put_and_get.c
+
+ARGUMENTE:
+     obj
+       an den NPC uebergebenes Objekt
+RUeCKGABEWERT:
+     keiner
+
+BESCHREIBUNG:
+     Diese Funktion wird automatisch immer dann aufgerufen, wenn ein
+     Lebewesen (welches kein Spielercharakter ist) ein Objekt uebergeben
+     bekommt. Dies muss jedoch ueber die Funktionalitaet von
+     put_and_get.c geschehen sein, innerhalb von move() wird die Funktion
+     nicht aufgerufen!
+
+BEISPIEL:
+     Oftmals will man in Quests erreichen, dass einem NPC ein bestimmtes
+     Item als Beweis der Erfuellung einer bestimmten Aufgabe ueberbracht
+     wird. Folgendermasse kann dies realisiert werden:
+     void create() {
+       ::create();
+       ...
+       SetProp(P_REJECT,({REJECT_GIVE,
+         Name(WER)+" sagt: Das brauche ich nicht!\n"}));
+       ...
+     }
+
+     void quest_ok(object obj) { ...
+       // Vernichtung des Questobjektes und Questtexte
+       // Questbelohnung und Questanerkennung
+     }
+
+     void give_notify(object obj) {
+       if(obj->id("\nquestitem")) // Questitem bekommen?
+         quest_ok(obj);
+       else
+         ::give_notify(obj);  // P_REJECT soll sonst greifen
+     }
+     Der Aufruf von ::give_notify() stellt sicher, dass ein Objekt
+     zurueckgegeben wird, sofern es nicht das gesuchte ist. Erreicht wird
+     dies ueber P_REJECT (siehe Bemerkungen).
+
+BEMERKUNGEN:
+     Speziell in NPCs ist diese Funktion normalerweise dafuer
+     verantwortlich, dass mittels der Property P_REJECT die Annahme von
+     Objekten verweigert werden kann. Ueberschreibt man sie, so sollte
+     man gegebenenfalls darauf achten, dass diese Standardfunktion
+     ebenfalls aufgerufen wird.
+
+SIEHE AUCH:
+     P_REJECT, show_notify(), 
+     /std/npc/put_and_get.c, /std/living/put_and_get.c
+
+22. Oktober 2013, Arathorn.
diff --git a/doc/lfun/give_obj b/doc/lfun/give_obj
new file mode 100644
index 0000000..59f8c99
--- /dev/null
+++ b/doc/lfun/give_obj
@@ -0,0 +1,27 @@
+give_obj()
+
+FUNKTION
+    int give_obj(object ob, object where)
+
+DEFINIERT IN:
+
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+
+    ob      Das Objekt, das abgegeben werden soll.
+    where   Das Lebewesen, dass das Objekt erhaelt.
+
+BESCHREIBUNG:
+
+    Das Lebewesen, in dem diese Funktion aufgerufen werden soll, gibt
+    den angegebenen Gegenstand (ob) an das angegebene Lebewesen (where).
+
+RUeCKGABEWERT:
+    1, wenn das Objekt uebergeben wurde oder die Uebergabe nicht moeglich war.
+    (in diesem Fall wird auch direkt eine Textausgabe ausgegeben)
+    0 sonst, in diesem Fall wird in notify_fail eine passende Ausgabe plaziert.
+
+SIEHE AUCH:
+
+    pick_obj(), drop_obj(), put_obj(), find_obs(), /std/living/put_and_get.c
diff --git a/doc/lfun/heal_self b/doc/lfun/heal_self
new file mode 100644
index 0000000..5f60547
--- /dev/null
+++ b/doc/lfun/heal_self
@@ -0,0 +1,49 @@
+heal_self()
+
+FUNKTION:
+    void heal_self(int points);
+
+ARGUMENTE:
+    points: Die dem Lebewesen zukommende Heilung.
+
+BESCHREIBUNG:
+    Dem Lebewesen werden points Lebens- und Konzentrationspunkte 
+    gutgeschrieben und auf die aktuellen addiert. Es werden aber nicht
+    die maximalen Werte ueberschritten.
+
+RUECKGABEWERT:
+    Keiner
+
+BEISPIELE:
+    
+    AddCmd("pflueck&beere","pfluecke_cmd","Was moechtest Du pfluecken?");
+
+    int pfluecke_cmd(string str){
+        write("Du pflueckst eine Beere, isst sie und fuehlst Dich gleich "
+             +"viel besser.\n");
+        this_player()->heal_self(30);
+        return 1;
+    }
+
+    Der Spieler bekommt hier pro Beere die er pflueckt und isst je 30 LP/KP
+    zu seinen momentanen.
+
+BEMERKUNGEN: 
+    heal_self wird gerne fuer Heilstellen in Gebieten genommen, in denen ein
+    Spieler diese Heilung auch wirklich braucht. Dennoch ist der Einsatz un-
+    bedingt mit der Heilungsbalance abzusprechen und darauf zu achten, dass
+    pro reset() nur eine bestimmte Anzahl an Heilungen ausgegeben werden.
+
+    Bei Heilstellen sollte eine evtl. Heilung des Spielers mit der eigens
+    dafuer eingerichteten Funktion check_and_update_timed_key realisiert
+    werden.
+
+SIEHE AUCH:
+    Verwandt:   restore_spell_points, restore_hit_points, buffer_hp
+		buffer_sp, check_and_update_timed_key
+    Gegenparts: do_damage, reduce_hit_points, reduce_spell_points
+    Props:      P_HP, P_SP
+    Konzept:    heilung
+
+----------------------------------------------------------------------------
+Last modified: 27.05.2007 by Ennox
diff --git a/doc/lfun/heart_beat b/doc/lfun/heart_beat
new file mode 100644
index 0000000..6a314bd
--- /dev/null
+++ b/doc/lfun/heart_beat
@@ -0,0 +1,42 @@
+heart_beat()
+
+FUNKTION:
+     protected void heart_beat();
+
+DEFINIERT IN:
+     /std/living/life.c
+     /std/living/combat.c
+     und anderen...
+     kann auch in beliebigen Objekten selbst definiert werden.
+
+BESCHREIBUNG:
+     Diese Funktion wird alle zwei Sekunden vom GameDriver aufgerufen. Sie
+     regelt in der MG-MudLib das Heilen von Spielern und Monstern, den
+     Ablauf von Kampfrunden, die Verdauung etc.
+
+     Da heart_beat() ziemlich viele Ressourcen des GameDrivers verbraet,
+     sollte man Objekte mit heart_beat() nur so selten wie moeglich
+     benutzen! Und selbst dann sollte der heart_beat() nicht die ganze Zeit
+     ueber laufen, sondern sich so bald wie moeglich abschalten.
+
+     Das Ein- und Ausschalten des heart_beat()s erfolgt mit
+     set_heart_beat().
+
+BEMERKUNGEN:
+     1. Teuer, teuer, teuer!
+     2. Soll euer Viech pro "echtem" Heartbeat mehrere Kampfrunden haben,
+        benutzt dafuer SA_SPEED und ruft auf gar keinen Fall mehrfach 
+        ::heart_beat() auf. Also _NICHT_
+        void heart_beat() {
+            ::heart_beat();
+            ::heart_beat(); }
+        sondern:
+        SetProp(P_SKILL_ATTRIBUTE_OFFSETS, ([SA_SPEED: 200]) );
+
+SIEHE AUCH:
+     Efuns:     set_heart_beat(), absolute_hb_count(), set_next_reset()
+     Fehler:    make_immortal(L)
+
+----------------------------------------------------------------------------
+22.3.2008, Zesstra
+
diff --git a/doc/lfun/id b/doc/lfun/id
new file mode 100644
index 0000000..7cf5aed
--- /dev/null
+++ b/doc/lfun/id
@@ -0,0 +1,76 @@
+id()
+
+FUNKTION:
+     varargs int id(string str, int lvl);
+
+DEFINIERT IN:
+     /std/thing/description.c
+     /std/player/base.c
+
+ARGUMENTE:
+     string str
+          String, auf den getestet werden soll.
+     int lvl
+          In /std/player/base.c benutzt. Wenn der Spieler unsichtbar ist
+          und lvl kleiner ist als sein Level, wird 0 zurueckgegeben.
+
+BESCHREIBUNG:
+     Es wird geprueft, ob sich das Objekt mit str ansprechen laesst. Dazu
+     wird str mit dem Inhalt der Property P_IDS verglichen. Falls
+     P_ADJECTIVES gesetzt ist, werden auch alle Adjektivkombinationen mit
+     den Bezeichnern geprueft.
+
+     Besondere Bedeutung hat diese Funktion bei Mengenobjekten: Anhand von
+     str wird vermerkt, welche Menge des Objektes angesprochen wird. Es
+     werden dabei mehrere Faelle unterschieden:
+     o str ist einer der mit AddId() angegebener Bezeichner. In diesem
+       Fall ist die angesprochene Menge die Gesamtmenge.
+     o str ist einer der mit AddSingularId() angegebenen Bezeichner. Die
+       angesprochene Menge ist in diesem Fall 1.
+     o str ist einer der mit AddPluralId() angegebenen Bezeichner. Die
+       angesprochene Menge ist in diesem Fall . die Gesamtmenge.
+     o Hat str die Form "<n> <id>", wobei <n>=1 und <id> eine SingularId
+       oder 1 < <n> <= der Gesamtmenge und <id> eine PluralId, so ist die
+       angesprochene Menge = <n>.
+     Wie gesagt, gilt dies einzig und allein bei Mengenobjekten!
+
+RUeCKGABEWERT:
+     1, wenn sich das Objekt von str angesprochen fuehlt, ansonsten 0.
+
+BEISPIELE:
+     Angenommen, ein Objekt habe folgende Bezeichner:
+
+     AddId( "murmel" );
+     AddAdjective( "runde" );
+
+     Dann liefern die angegebenen id()-Aufrufe folgende Ergebnisse:
+
+     id( "murmel" );         => 1
+     id( "runde murmel" );   => 1
+     id( "kleine murmel" );  => 0
+     id( "runder ball" );    => 0
+     id( "runde murmel 2" ); => 1, wenn dies die zweite Murmel in der
+                                   gleichen Umgebung ist, ansonsten 0
+
+     Bei einem Haufen von 100 Muenzen haette man zB.:
+
+     AddId( "geld" );
+     AddSingularId( "muenze" );
+     AddPluralId( "muenzen" );
+
+     Nach fuehlen sich nach den folgenden id()-Aufrufen folgende Anzahlen
+     angesprochen:
+
+     id( "geld" );       => 100 Muenzen
+     id( "muenze" );     => 1 Muenze
+     id( "muenzen" );    => 100 Muenzen
+     id( "1 muenze" );   => 1 Muenze
+     id( "42 muenzen" ); => 42 Muenzen
+
+     id() liefert in all diesen Faellen 1 zurueck.
+
+SIEHE AUCH:
+     AddId(), AddAdjective(), AddSingularId(), AddPluralId(), present(),
+     match_ids(), /std/thing/description.c, /std/unit.c
+
+6. Sep 2012 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/init b/doc/lfun/init
new file mode 100644
index 0000000..f9d0cad
--- /dev/null
+++ b/doc/lfun/init
@@ -0,0 +1,65 @@
+init()
+
+FUNKTION:
+	void init();
+
+DEFINIERT IN:
+	/std/room/description.c
+
+ARGUMENTE:
+	keine
+
+BESCHREIBUNG:
+	init() wird immer dann aufgerufen, wenn ein lebendes Objekt in die
+	Naehe anderer Objekte bewegt wird oder ein nicht lebendiges Objekt
+	in die Naehe von Lebewesen gelangt. init() wird dabei in allen
+	Objekten aufgerufen, bei denen es notwendig ist.
+
+	Der Hauptzweck dieser Funktion besteht darin, den Objekten
+	untereinander ihre jeweiligen Befehle zugaenglich zu machen.
+	Waehrend dies in anderen MudLibs durch eine Reihe von
+	add_action()-Aufrufen im Rumpf von init() geschah, geschieht dies in
+	der MG-MudLib bei Objekten, die /std/thing/commands.c erben
+	 (das sind zB. alle Standardobjekte) quasi automatisch
+	 (dort werden die Befehle dann per AddCmd() im create() angemeldet,
+	  man spart sich die Implementierung von init() und dem Mud somit
+	  Speicher). Der andere Weg ist aber natuerlich immer noch moeglich.
+
+RUeCKGABEWERT:
+	keiner
+
+BEMERKUNGEN:
+	Der Ablauf der init()-Kette ist wie folgt:
+	o Ist das Objekt X, welches ins Zielobjekt D bewegt wurde, ein
+	  Lebewesen, so wird in D init() aufgerufen, wobei this_player() auf
+	  X gesetzt ist.
+	o Dann wird fuer jedes Objekt C in D folgendes ausgefuehrt:
+	  + Ist C ein Lebewesen, dann wird init() in X aufgerufen, wobei
+	    this_player() auf C gesetzt ist.
+	  + Ist X ein Lebewesen, dann wird init() in C aufgerufen, wobei
+	  this_player() auf X gesetzt ist.
+	o Schliesslich wird in dem Fall, dass D lebendig ist, in X init()
+	  aufgerufen, wobei this_player() auf D gesetzt ist.
+
+BEISPIELE:
+	D sei ein Raum, in dem sich das Lebewesen L1 sowie die Gegenstaende
+	N1 und N2 befinden.
+	Betritt ein Spieler X diesen Raum, so werden folgende init()s
+	aufgerufen:
+
+	  D->init();  // this_player() == X
+	  X->init();  // this_player() == L1
+	  L1->init(); // this_player() == X
+	  N1->init(); // this_player() == X
+	  N2->init(); // this_player() == X
+
+	Gelangt dagegen ein nichtlebendiges Objekt nach D, so sieht das Ganze
+	wie folgt aus:
+
+	  X->init();    // this_player() == L1
+
+SIEHE AUCH:
+	exit(), AddCmd(), add_action(),
+  NotifyInsert()
+----------------------------------------------------------------------------
+Last modified: 04.08.2007, Zesstra
diff --git a/doc/lfun/insert_sensitive_inv b/doc/lfun/insert_sensitive_inv
new file mode 100644
index 0000000..5b54df4
--- /dev/null
+++ b/doc/lfun/insert_sensitive_inv
@@ -0,0 +1,21 @@
+insert_sensitive_inv
+FUNKTION:
+     void insert_sensitive_inv(object ob, string key,
+			       int threshold, mixed *opt)
+
+DEFINIERT IN:
+     /std/container/inventory.c
+
+BESCHREIBUNG:
+     Diese Funktion sucht, ob beim Hinzufuegen eines sensitiven Objekts
+     schon ein Objekt da ist, dass dieses ausloest.
+
+SIEHE AUCH:
+     P_SENSITIVE
+     InsertSensitiveObject, RemoveSensitiveObject
+     insert_sensitive_inv_trigger
+     P_SENSITIVE_ATTACK, P_SENSITIVE_INVENTORY,
+     P_SENSITIVE_INVENTORY_TRIGGER
+     CheckSensitiveAttack
+
+28.Jan.2001, Gloinson@MG
diff --git a/doc/lfun/insert_sensitive_inv_trigger b/doc/lfun/insert_sensitive_inv_trigger
new file mode 100644
index 0000000..766f0f5
--- /dev/null
+++ b/doc/lfun/insert_sensitive_inv_trigger
@@ -0,0 +1,21 @@
+insert_sensitive_inv_trigger
+FUNKTION:
+     void insert_sensitive_inv_trigger(object ob, string key,
+				       int threshold, mixed *opt)
+
+DEFINIERT IN:
+     /std/container/inventory.c
+
+BESCHREIBUNG:
+     Diese Funktion sucht, ob ein sensitives Objekt im Inventory ist,
+     das durch dieses (soeben eingefuegte) Objekt ausgeloest wird.
+
+SIEHE AUCH:
+     P_SENSITIVE
+     InsertSensitiveObject, RemoveSensitiveObject
+     insert_sensitive_inv
+     P_SENSITIVE_ATTACK, P_SENSITIVE_INVENTORY,
+     P_SENSITIVE_INVENTORY_TRIGGER
+     CheckSensitiveAttack
+
+28.Jan.2001, Gloinson@MG
diff --git a/doc/lfun/int_long b/doc/lfun/int_long
new file mode 100644
index 0000000..7513e87
--- /dev/null
+++ b/doc/lfun/int_long
@@ -0,0 +1,64 @@
+int_long()
+FUNKTION:
+     varargs string int_long(mixed viewer, mixed viewpoint, int flags)
+
+DEFINIERT IN:
+     /std/room/description.c
+
+ARGUMENTE:
+     mixed viewer	- der Betrachter des Raumes
+     mixed viewpoint	- 0/Objekt/Array der/die Ausgangspunkt(e) des
+			  Betrachtens (und damit nicht sichtbar!)
+     int flags		- Modifikatoren fuer die Anzeige
+			  (siehe "man make_invlist", wird mit 3 verUNDet!)
+
+BESCHREIBUNG:
+     Es wird die Beschreibung des Rauminneren erstellt. Dabei wird die
+     Langbeschreibung des Raumes, die enthaltenen Objekte (exklusive
+     aller viewpoints (normalerweise nur der Betrachter)) und Ausgaenge,
+     wenn vom Viewer eingeschaltet dargestellt.
+     Falls der Raum innerhalb eines anderen Raumes liegt und selbst
+     transparent ist, wie zusaetzlich die Kurzbeschreibung des Aussen-
+     raumes angezeigt.
+
+     Ist Viewer ein Magier mit eingeschaltetem Magiermodus, so wird der
+     Beschreibung der Dateiname des Raumes vorangestellt.
+
+RUeCKGABEWERT:
+     Die Langbeschreibung des Raumes von innen.
+
+BEMERKUNGEN:
+     Die Trennung von viewer und viewpoint hat durchaus ihren Sinn. So ist
+     es zum Beispiel moeglich, einen Raum "mit den Augen eines Anderen" zu
+     betrachten. Dabei saehe man sich selbst, wenn man im Raum waere.
+
+BEISPIELE:
+     // in diesem Raum sieht man keine Mitspieler im "schau" oder beim
+     // Betreten (vielleicht ist es zu neblig)
+     // dazu werden einfach alle Interactives zu den viewpoints addiert
+     string int_long(object viewer, mixed viewpoints, int flags) {
+      if(!pointerp(viewpoints)) viewpoints=({viewpoints});
+      return ::int_long(&viewer,
+			viewpoints+
+			filter(all_inventory(this_object()),
+				     #'interactive),
+			&flags);
+     }
+
+     string int_short(object viewer, mixed viewpoints) {
+      if(!pointerp(viewpoints)) viewpoints=({viewpoints});
+      return ::int_short(&viewer,
+			 viewpoints+
+			 filter(all_inventory(this_object()),
+				      #'interactive));
+     }
+
+SIEHE AUCH:
+     Aehnliches:	int_short()
+     Properties:	P_INT_LONG, P_SHORT
+			P_HIDE_EXITS, P_SHOW_EXITS
+			P_TRANSPARENT
+     Kommandokette:	make_invlist(), short()
+     Sonstiges:		P_REFERENCE_OBJECT, P_WANTS_TO_LEARN
+
+11. Mai 2004 Gloinson
diff --git a/doc/lfun/int_short b/doc/lfun/int_short
new file mode 100644
index 0000000..e4a8d3a
--- /dev/null
+++ b/doc/lfun/int_short
@@ -0,0 +1,58 @@
+int_short()
+FUNKTION:
+     string int_short(mixed viewer, mixed viewpoint);
+
+DEFINIERT IN:
+     /std/room/description.c
+
+ARGUMENTE:
+     mixed viewer	- der Betrachter des Raumes
+     mixed viewpoint	- 0/Objekt/Array der/die Ausgangspunkt(e) des
+			  Betrachtens (und damit nicht sichtbar!)
+
+BESCHREIBUNG:
+     Es wird eine kurze  Beschreibung des Rauminneren erstellt. Dabei wird
+     die Kurzbeschreibung des Raumes, die enthaltenen Objekte (exklusive
+     aller viewpoints (normalerweise nur der Betrachter)) und Ausgaenge,
+     wenn vom Viewer eingeschaltet dargestellt.
+
+     Ist Viewer ein Magier mit eingeschaltetem Magiermodus, so wird der
+     Beschreibung der Dateiname des Raumes vorangestellt.
+
+RUeCKGABEWERT:
+     Die Kurzbeschreibung des Raumes von innen.
+
+BEMERKUNGEN:
+     Die Trennung von viewer und viewpoint hat durchaus ihren Sinn. So ist
+     es zum Beispiel moeglich, einen Raum "mit den Augen eines Anderen" zu
+     betrachten. Dabei saehe man sich selbst, wenn man im Raum waere.
+
+BEISPIELE:
+     // in diesem Raum sieht man keine Mitspieler im "schau" oder beim
+     // Betreten (vielleicht ist es zu neblig)
+     // dazu werden einfach alle Interactives zu den viewpoints addiert
+     string int_long(object viewer, mixed viewpoints, int flags) {
+      if(!pointerp(viewpoints)) viewpoints=({viewpoints});
+      return ::int_long(&viewer,
+			viewpoints+
+			filter(all_inventory(this_object()),
+				     #'interactive),
+			&flags);
+     }
+
+     string int_short(object viewer, mixed viewpoints) {
+      if(!pointerp(viewpoints)) viewpoints=({viewpoints});
+      return ::int_short(&viewer,
+			 viewpoints+
+			 filter(all_inventory(this_object()),
+				      #'interactive));
+     }
+
+SIEHE AUCH:
+     Aehnliches:	int_long()
+     Properties:	P_INT_SHORT, P_SHORT
+			P_HIDE_EXITS, P_SHOW_EXITS
+     Kommandokette:	make_invlist(), short()
+     Sonstiges:		P_REFERENCE_OBJECT, P_WANTS_TO_LEARN
+
+11. Mai 2004 Gloinson
diff --git a/doc/lfun/is_class_member b/doc/lfun/is_class_member
new file mode 100644
index 0000000..7019447
--- /dev/null
+++ b/doc/lfun/is_class_member
@@ -0,0 +1,155 @@
+is_class_member()
+FUNKTION:
+     int is_class_member(string|string* class);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     string/string* class	- String oder Stringarray der Klasse(n)
+
+BESCHREIBUNG:
+     Es wird getestet, ob das Objekt in eine der in class angegebenen
+     Klassen faellt. In diesen Test werden die folgenden Eigenschaften des
+     Objektes einbezogen:
+
+       1. Die Rasse des Objektes (bei Lebewesen),
+       2. die IDs des Objektes und
+       3. die explizit angegebenen Klassen des Objektes.
+       4. einigen impliziten Klassen, die sich aus den Klassen in 3 ergeben.
+
+     Die moeglichen Klassen sind in /sys/class.h definiert. Momentan stehen
+     folgende Klassen zur Verfuegung:
+
+     CL_AMMUNITION
+          Das Objekt eignet sich als Munition.
+     CL_ANIMAL
+          Das Objekt ist ein Tier.
+     CL_ARACHNID
+          Das Objekt in ein Spinnenwesen.
+     CL_BIGBANG
+          Dieses Objekt kann mehreren Lebewesen auf einmal Schaden zufuegen.
+     CL_BIRD
+          Ein Vogel.
+     CL_CRAWLING
+          Dieses Wesen bewegt sich kriechend.
+     CL_CURSE
+          Das Objekt ist ein Fluch (zB. ein Sprachfluch).
+     CL_DEMON
+          Bei dem Objekt handelt es sich um einen Daemon.
+     CL_DISEASE
+          Eine Krankheit.
+     CL_DRAGON
+          Ein Drache.
+     CL_DWARF
+          Fuer unsere kleinen Gaeste...
+     CL_ELF
+          Elfen aller Art.
+     CL_ELEMENTAL
+          Ein Elementar irgendeiner Art. Material setzen waere angebracht.
+     CL_EXPLOSIVE
+          Bei dem Objekt handelt es sich um einen Sprengstoff.
+     CL_FELINE
+          Felinen und andere katzenartigen Lebewesen.
+     CL_FISH
+          Fische - keine Meeressaeuger!
+     CL_FLYING
+          Dieses Wesen bewegt sich fliegend.
+     CL_FROG
+          Froesche - auch gefroschte Spieler.
+     CL_GHOST
+          Geister und geisterhafte Wesen.
+     CL_GHOUL
+          Ein Ghoul. Er faellt automatisch in die Klasse CL_UNDEAD.
+     CL_GIANT
+          Ein riesiges Lebewesen.
+     CL_GNOME
+          Ein Gnom.
+     CL_GOBLIN
+          Ein Goblin.
+     CL_HOBBIT
+          Ein Hobbit.
+     CL_HOBGOBLIN
+          Ein Hobgoblin. Er faellt automatisch auch in die Klasse CL_GOBLIN.
+     CL_HUMAN
+          Ein Mensch.
+     CL_INORGANIC
+          Anorganische Lebewesen wie Metallmonster
+     CL_INSECT
+          Insekten (Nicht mit Spinnen verwechseln)
+     CL_LIVING
+          Lebewesen im allgemeinen.
+     CL_MAMMAL
+          Saeugetiere.
+     CL_MAMMAL_LAND
+          Landsaeugetiere
+     CL_MAMMAL_WATER
+          Meeressaeuger.
+     CL_ORC
+          Ein Ork.
+     CL_PLANT
+          Pflanzen und pflanzenartige Monster.
+     CL_POISON
+          Das Objekt ist selbst ein Gift
+     CL_POISONOUS
+          Das Objekt kann einen Spieler/NPC vergiften.
+     CL_REPTILE
+          Reptilien.
+     CL_SHADOW
+          Schattenwesen.
+     CL_SKELETON
+          Ein Skelett. Es faellt automatisch in die Klasse CL_UNDEAD.
+     CL_SLIME
+          Fuer Einzeller und aehnliches Schleimgetier
+     CL_SNAKE
+          Schlangen.
+     CL_SWIMMING
+          Dieses Wesen bewegt sich schwimmend.
+     CL_TROLL
+          Ein Troll.
+     CL_UNDEAD
+          Ein untotes Lebewesen.
+     CL_WALKING
+          Dieses Wesen bewegt sich gehend.
+     CL_VAMPIRE
+          Ein Vampir. Er faellt automatisch in die Klasse CL_UNDEAD.
+     CL_ZOMBIE
+          Ein Zombie. Er faellt automatisch in die Klasse CL_UNDEAD.
+
+     Implizite Klassen:
+     Bei einigen Klassen wird im AddClass() automatisch eine oder mehrere
+     weiterer Klassen hinzugefuegt und im RemoveClass() die entsprechenden
+     impliziten Klassen auch automatisch entfernt.
+     Wuenscht man diese impliziten Klassen nicht, muss man nach dem AddClass()
+     diese mittels eines entsprechenden RemoveClass() entfernen.
+     Die impliziten Klassen einer Klasse lassen sich mit Hilfe der Funktion
+     QueryImplicitClasses() in CLASSDB herausfinden:
+       CLASSDB->QueryImplicitClasses(...)
+     Momentan sind dies:
+     CL_ZOMBIE:       CL_UNDEAD
+     CL_SKELETON:     CL_UNDEAD
+     CL_GHOUL:        CL_UNDEAD
+     CL_VAMPIRE:      CL_UNDEAD
+     CL_HOBGOBLIN:    CL_GOBLIN
+     CL_MAMMAL_LAND:  CL_MAMMAL, CL_ANIMAL
+     CL_MAMMAL_WATER: CL_MAMMAL, CL_ANIMAL
+     CL_SNAKE:        CL_REPTILE
+     CL_ARACHNID:     CL_ANIMAL
+     CL_BIRD:         CL_ANIMAL
+     CL_FISH:         CL_ANIMAL
+     CL_FROG:         CL_ANIMAL
+     CL_INSECT:       CL_ANIMAL
+     CL_MAMMAL:       CL_ANIMAL
+     CL_REPTILE:      CL_ANIMAL
+     CL_SNAKE:        CL_ANIMAL
+
+RUeCKGABEWERT:
+     1, wenn das Objekt in eine der angegebenen Klassen faellt, ansonsten 0.
+
+SIEHE AUCH:
+     AddClass(), RemoveClass(), /std/thing/description.c
+     P_CLASS
+
+----------------------------------------------------------------------------
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/lfun b/doc/lfun/lfun
new file mode 100644
index 0000000..eadd45a
--- /dev/null
+++ b/doc/lfun/lfun
@@ -0,0 +1,12 @@
+NAME:
+	lfun()
+
+DESCRIPTION:
+	This directory contains descriptions for the lfuns used by
+	Amylaar's version of the LPC parser.
+
+	These are functions that are applied by the parser to the LPC
+	objects on various occasions.
+
+SEE ALSO:
+	efun(E), master(M), concepts(C), lpc(LPC), driver(D)
diff --git a/doc/lfun/locate_objects b/doc/lfun/locate_objects
new file mode 100644
index 0000000..1d56e9a
--- /dev/null
+++ b/doc/lfun/locate_objects
@@ -0,0 +1,54 @@
+locate_objects()
+
+FUNKTION:
+     object *locate_objects(string desc, int info);
+
+DEFINIERT IN:
+     /std/container/restrictions.c
+
+ARGUMENTE:
+     desc
+          Die Umschreibung des gesuchten Objektes.
+
+     info
+          Ist ungleich 0, wenn diese Funktion von /std/living/put_and_get.c
+          aus aufgerufen wurde.
+
+BESCHREIBUNG:
+     Diese Funktion erweitert die Funktionalitaet von present_objects()
+     insofern, als dass es moeglich ist, auch noch Behaelter innerhalb des
+     Behaelters zu durchsuchen. Das genaue Verhalten haengt von desc ab:
+
+     Ist desc von der Form "<id>", so wird das Ergebnis von
+     present_objects(desc) zurueckgegeben.
+
+     Ist desc von der Form "<gesucht> in <id>", so wird in allen Objekten,
+     die von present_objects("<id>") erfasst wurden,
+     locate_objects("<desc>") aufgerufen. Zurueckgegeben werden alle auf
+     diese Weise gefundenen Objekte.
+
+RUeCKGABEWERT:
+     Array von Objekten, die auf die oben geschilderte Art und Weise
+     ermittelt wurden. Konnte kein Objekt ermittelt werden, wird ein leeres
+     Array zurueckgegeben.
+
+BEMERKUNGEN:
+     Theoretisch sollte es moeglich sein, ueber desc rekursiv mehrere
+     Behaelterebenen erfassen zu koennen (etwa mit "schwert in beutel in
+     beutel in wargon"). In der aktuellen Implementierung klappt das jedoch
+     nicht; nach dem ersten "in" ist Schluss!
+
+BEISPIELE:
+     Was steckt alles dem Beutel, den der Spieler bei sich traegt?
+
+     object *obs;
+     obs = this_player()->locate_objects("alles in beutel");
+
+     Traegt der Spieler keinen Beutel bei sich oder ist dieser leer, so wird
+     ein leeres Array zurueckgegeben.
+
+SIEHE AUCH:
+     present_objects(), /std/container/restrictions.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:20:36 1996 by Wargon
diff --git a/doc/lfun/logon b/doc/lfun/logon
new file mode 100644
index 0000000..700f1e0
--- /dev/null
+++ b/doc/lfun/logon
@@ -0,0 +1,15 @@
+logon()
+
+SYNOPSIS:
+	int logon(void)
+
+DESCRIPTION:
+	When the parser accepts a new connection, it first calls
+	connect() in the master object and then applies logon() to
+	the object that is returned by the master. That will usually be
+	a special login object, that is expected to clone and get going
+	a user shell or player object.
+	Should return 0 on failure, everything else means success.
+
+SEE ALSO:
+	connect(M)
diff --git a/doc/lfun/long b/doc/lfun/long
new file mode 100644
index 0000000..34ac4f9
--- /dev/null
+++ b/doc/lfun/long
@@ -0,0 +1,31 @@
+long()
+FUNKTION:
+     varargs string long(int mode);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     int mode		- Modifikatoren fuer die Anzeige, falls this_object()
+			  ein Container ist
+			  (siehe "man make_invlist")
+
+BESCHREIBUNG:
+     Der Inhalt der Property P_LONG wird ausgewertet und zurueckgegeben.
+     Falls das Objekt ein Container und transparent (offen) ist, wird
+     zudem make_invlist() auf den Inhalt zurueckgegeben.
+
+RUeCKGABEWERT:
+     Die Langbeschreibung des Objektes.
+
+BEMERKUNGEN:
+     Durch Ueberladen von long() lassen sich noch weitere Eigenschaften des
+     Objektes anzeigen. Behaelter koennen zum Beispiel noch ihren Inhalt
+     anzeigen, Lebewesen ihren Gesundheitszustand, o.ae.
+
+SIEHE AUCH:
+     Aehnliches:	short()
+     Properties:	P_LONG, P_INVIS, P_TRANSPARENT
+     Sonstiges:		make_invlist()
+
+11. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/make_immortal b/doc/lfun/make_immortal
new file mode 100644
index 0000000..3ff4900
--- /dev/null
+++ b/doc/lfun/make_immortal
@@ -0,0 +1,27 @@
+make_immortal()

+

+FUNKTION:

+     void make_immortal();

+

+DEFINIERT IN:

+     /std/npc/combat.c

+

+BESCHREIBUNG:

+     Macht den NPC für 5 Minuten unangreifbar und heilt ihn.

+

+     Wird bei Fehlern im Herzschlag des NPCs gerufen um Bugnutzung

+     zu unterbinden.

+

+     Ausschrift:

+     "... versteckt sich hinter einem Fehler im Raum-Zeit-Gefuege und

+      entgeht so voruebergehend allen Angriffen."

+

+BEMERKUNGEN:

+     Nicht missbrauchen.

+

+SIEHE AUCH:

+     Methoden:       heart_beat(), static _check_immortality()

+     Master:         heart_beat_error()

+     Properties:     "time_to_mortality"

+

+1.Juni 2007 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/make_invlist b/doc/lfun/make_invlist
new file mode 100644
index 0000000..effbcca
--- /dev/null
+++ b/doc/lfun/make_invlist
@@ -0,0 +1,38 @@
+make_invlist()
+FUNKTION:
+     varargs mixed make_invlist(object viewer, mixed inv, int flags)
+
+DEFINIERT IN:
+     /std/container/description.c
+
+ARGUMENTE:
+     object viewer	- das Objekt, welches sich den Inhalt ansieht (in
+			  der Regel this_player())
+     mixed inv		- ein Array von Objekten, die in die Liste
+			  aufgenommen werden sollen
+     int flags		- Folgende Werte koennen verODERt werden:
+			  1: das Inv nicht als String, sondern als ein
+			     Array zurueckgeben
+			  2: gleiche Objekte nicht zusammengefassen
+			  4: unterdrueckt die Ausgabe des Dateinamens hinter
+			     jedem trotz eingeschaltetem Magiermodus
+
+BESCHREIBUNG:
+     Die Kurzbeschreibungen der Objekte in inv werden zu einer Liste
+     zusammengefasst (eine Zeile pro Objekt). Unsichtbare Objekte tauchen in
+     dieser Liste nicht auf.
+
+     Ist viewer ein Magier mit eingeschaltetem Magiermodus, so wird hinter
+     die Kurzbeschreibungen noch der Dateiname des jeweiligen Objektes in
+     eckigen Klammern angegeben. Unsichtbare Objekte erscheinen in diesem
+     Fall als Filenamen.
+
+RUeCKGABEWERT:
+     Ein String oder ein Array, die das inv beschreiben.
+
+SIEHE AUCH:
+     Kommandokette:	short()
+     Properties:	P_SHORT, P_INVIS, P_WANTS_TO_LEARN
+     Sonstiges:		P_REFERENCE_OBJECT
+
+Last modified: Tue Oct 15 10:10:00 2002 by Rikus
diff --git a/doc/lfun/master/AddWizardForUID b/doc/lfun/master/AddWizardForUID
new file mode 100644
index 0000000..c58463b
--- /dev/null
+++ b/doc/lfun/master/AddWizardForUID
@@ -0,0 +1,47 @@
+AddWizardForUID()
+
+FUNKTION:
+    string* AddWizardForUID(string uid, string wizard);
+
+DEFINIERT IN:
+    /secure/master/userinfo.c
+
+ARGUMENTE:
+    uid
+      Die UID, fuer die man einen (weiteren) verantwortlichen Magier
+      explizit eintragen moechte.
+    wizard
+      Der Magier, den man eintragen moechte.
+
+BESCHREIBUNG:
+    Die Funktion traegt einen Magier 'wizard' explizit als verantwortlichen
+    Magier fuer die UID 'uid' ein. Hierbei kann 'uid' sogar der Name eines
+		anderen Magiers sein, dessen UIDs 'wizard' sozusagen "adoptiert".
+
+		Berechtigt zum Eintragen von Magiern fuer bestimmte UIDs sind alle Magier,
+    die (implizit oder explizit) verantwortlich fuer die jeweilige UID sind.
+    Z.B. kann Zesstra ohne weiteres jemand weiteren als verantwortlich fuer
+    d.inseln.zesstra eintragen.
+
+RUeCKGABEWERT:
+    Zurueckgeliefert wird ein Array von Strings, jedes Element ist eine UID,
+    fuer die dier Magier jetzt explizit eingetragen ist.
+
+BEMERKUNGEN:
+    Es ist nicht noetig, z.B. Zesstra als verantwortlich fuer d.inseln.zesstra
+    einzutragen, da sie ohnehin schon implizit dafuer zustaendig ist. Auch
+    fuer RMs bzw. GMs muss ihre Region bzw. Gilde nicht explizit eingetragen 
+    werden.
+
+BEISPIELE:
+    master()->AddWizardForUID("p.waterluh","zook");
+		
+		string *uids = master()->AddWizardForUID("jof","zook");
+    printf("Zook ist nun explizit zustaendig fuer: %s\n",CountUp(uids));
+
+SIEHE AUCH:
+    QueryWizardsForUID(), QueryUIDsForWizard,
+		RemoveWizardFromUID()
+
+20.02.2007, Zesstra
+
diff --git a/doc/lfun/master/QueryUIDAlias b/doc/lfun/master/QueryUIDAlias
new file mode 100644
index 0000000..0ea844c
--- /dev/null
+++ b/doc/lfun/master/QueryUIDAlias
@@ -0,0 +1,43 @@
+QueryUIDAlias()
+
+FUNKTION:
+    varargs string* QueryUIDsForWizard(string uidalias, int recursive);
+
+DEFINIERT IN:
+    /secure/master/userinfo.c
+
+ARGUMENTE:
+    uidalias
+      UID, die expandiert werden soll.
+    recursive (optional)
+      Gibt an, ob QueryUIDAlias() (indirekt) rekursiv aufgerufen wurde.
+      Sollte normalerweise nicht per Hand gesetzt werden.
+
+BESCHREIBUNG:
+    Die Funktion ermittelt aus einer "Alias-UID" die UID, fuer die sie steht.
+    Hierbei werden folgende UID-Aliase beruecksichtigt:
+    "region":    d.region.* + region + d.region
+    "gilde":     GUILD.gilde, GUILD.gildenspellbook, p.gilde
+    "p":         p.* (ohne p.service)
+    "p.service": p.service.*
+    "magierid":  QueryUIDsForWizard()
+
+    Das Ergebnis dieser Funktion wird laengere Zeit gecachet (bis zu 24h).
+
+RUeCKGABEWERT:
+    Zurueckgeliefert wird ein Array von Strings, jedes Element ist eine UID.
+    Sollte uidaliase keines der o.g. sein, wird ein ({uidalias}) geliefert.
+
+BEISPIELE:
+    string *uids = master()->QueryUIDAlias("schattenwelt");
+    // uids enthaelt nun:
+    // ({"d.anfaenger","anfaenger","d.anfaenger.ark","d.anfaenger.ennox",
+    //   "d.anfaenger.humni","d.anfaenger.kiria","d.anfaenger.konzepte",
+    //   "d.anfaenger.miril"})
+
+SIEHE AUCH:
+    QueryWizardsForUID(), 
+		AddWizardForUID(), RemoveWizardFromUID()
+
+16.12.2007, Zesstra
+
diff --git a/doc/lfun/master/QueryUIDsForWizard b/doc/lfun/master/QueryUIDsForWizard
new file mode 100644
index 0000000..a4028c1
--- /dev/null
+++ b/doc/lfun/master/QueryUIDsForWizard
@@ -0,0 +1,42 @@
+QueryUIDsForWizard()
+
+FUNKTION:
+    varargs string* QueryUIDsForWizard(string wizname, int recursive);
+
+DEFINIERT IN:
+    /secure/master/userinfo.c
+
+ARGUMENTE:
+    wizname
+      Der Magier, fuer den man die UIDs ermitteln will, fuer die er
+      zustaendig ist.
+    recursive (optional)
+      Gibt an, ob QueryUIDsForWizard() (indirekt) rekursiv aufgerufen wurde.
+      Sollte normalerweise nicht per Hand gesetzt werden.
+
+BESCHREIBUNG:
+    Die Funktion ermittelt die UIDs, fuer die dieser Magier zustaendig ist.
+    Dabei wird impliziert beruecksichtigt, wenn der Magier RM einer Region
+    oder Gildenmagier einer Gilde ist, ebenso wie Verzeichnisse in den
+    Regionen oder in /p/service.
+    Ausserdem wird nachgeschaut, fuer welche UIDs dieser Magier explizit
+    eingetragen worden ist.
+    Wenn z.B. Magier A auch fuer alle UIDs von Magier B zustaendig sein
+    sollte, liefert die Funktion auch die UIDs von B zurueck.
+
+RUeCKGABEWERT:
+    Zurueckgeliefert wird ein Array von Strings, jedes Element ist eine UID.
+    Sollte fuer einen Namen keine UID ermittelbar sein, ist das Arrays leer.
+
+BEISPIELE:
+    string *uids = master()->QueryUIDsForWizard("ennox");
+    // uids enthaelt nun:
+    // ({"ennox","d.anfaenger.ennox","d.schattenwelt.ennox",
+    //   "p.service.ennox","GUILD.chaos","p.chaos"})
+
+SIEHE AUCH:
+    QueryWizardsForUID(), 
+		AddWizardForUID(), RemoveWizardFromUID()
+
+16.12.2007, Zesstra
+
diff --git a/doc/lfun/master/QueryWizardsForUID b/doc/lfun/master/QueryWizardsForUID
new file mode 100644
index 0000000..93095b6
--- /dev/null
+++ b/doc/lfun/master/QueryWizardsForUID
@@ -0,0 +1,46 @@
+QueryWizardsForUID()
+
+FUNKTION:
+    varargs string* QueryWizardsForUID(string uid, int recursive);
+
+DEFINIERT IN:
+    /secure/master/userinfo.c
+
+ARGUMENTE:
+    uid
+      Die UID, fuer die man die Magier ermitteln will, die fuer sie
+      zustaendig sind.
+    recursive (optional)
+      gibt an, ob das QueryWizardsForUID() (indirekt) aus einem 
+      QueryWizardsForUID() heraus gerufen wurde. Sollte nicht manuell gesetzt
+      werden.
+
+BESCHREIBUNG:
+    Die Funktion ermittelt die Magier, die fuer diese UID zustaendig sind.
+
+RUeCKGABEWERT:
+    Zurueckgeliefert wird ein Array von Strings, jedes Element ist ein Magier.
+    Sollte fuer eine UID kein Magier ermittelbar sein, ist das Array leer.
+    Wenn z.B. fuer die UID der Magier "Atamur" gefunden wird, aber fuer alle 
+    UIDs von "Atamur" auch der Magier "Rumata" zustaendig sein sollte, wird 
+    "Rumata" ueber eine rekursive Suche gefunden.
+
+BEMERKUNGEN:
+    Wenn die UID den Magier nicht implizit enthaelt (z.B. GUILD.gilde, im 
+    Gegensatz zu d.region.magier), findet diese Funktion nur Magier, fuer die 
+    seit Laden des Master bereits einmal get_userinfo() oder 
+    QueryUIDsForWizard() im Master gerufen wurde, was z.B. Einloggen passiert.
+    Magier, die lang nicht einloggten, werden also manchmal nicht gefunden,
+    was in der Regel nicht schlimm sein sollte, da sie ja ohnehin den
+    entsprechenden Code gerade nicht warten...
+
+BEISPIELE:
+    string *wiz = master()->QueryWizards("GUILD.klerus");
+    // wiz enthaelt nun: ({"morgoth","magdalena"})
+
+SIEHE AUCH:
+    QueryUIDsForWizard(), 
+		AddWizardForUID(), RemoveWizardFromUID()
+
+16.12.2007, Zesstra
+
diff --git a/doc/lfun/master/RemoveWizardFromUID b/doc/lfun/master/RemoveWizardFromUID
new file mode 100644
index 0000000..da97132
--- /dev/null
+++ b/doc/lfun/master/RemoveWizardFromUID
@@ -0,0 +1,38 @@
+RemoveWizardFromUID()
+
+FUNKTION:
+    string* RemoveWizardFromUID(string uid, string wizard);
+
+DEFINIERT IN:
+    /secure/master/userinfo.c
+
+ARGUMENTE:
+    uid
+      Die UID, fuer die man einen zustaendigen Magier austragen will.
+    wizard
+      Der Magier, den man austragen moechte.
+
+BESCHREIBUNG:
+    Die Funktion loescht die UID 'uid' aus der Liste der UIDs, fuer die
+    'wizard' explizit zustaendig ist.
+
+    Berechtigt zum Austragen von Magiern fuer bestimmte UIDs sind alle Magier,
+    die (implizit oder explizit) verantwortlich fuer die jeweilige UID sind.
+    Man kann sich auch selber austragen. ;-)
+
+RUeCKGABEWERT:
+    Zurueckgeliefert wird ein Array von Strings, jedes Element ist eine UID,
+    fuer die dier Magier jetzt explizit eingetragen ist.
+
+BEISPIELE:
+    master()->RemoveWizardFromUID("p.waterluh","zook");
+		
+    string *uids = master()->RemoveWizardFromUID("jof","zook");
+    printf("Zook ist nun explizit zustaendig fuer: %s\n",CountUp(uids));
+
+SIEHE AUCH:
+    QueryWizardsForUID(), QueryUIDsForWizard()
+		AddWizardForUID()
+
+20.02.2007, Zesstra
+
diff --git a/doc/lfun/match_ids b/doc/lfun/match_ids
new file mode 100644
index 0000000..d2c985b
--- /dev/null
+++ b/doc/lfun/match_ids
@@ -0,0 +1,47 @@
+match_ids()
+
+FUNKTION:
+     int match_ids(string *list);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     *list	String-Array mit zu testenden IDs
+
+BESCHREIBUNG:
+     Die Liste der uebergebenen IDs wird mit den IDs des Objektes
+     UND-Verknuepft. Die Groesse des resultierenden Arrays wird
+     zurueckgegeben.
+     Diese Funktion erlaubt also das gleichzeitige Pruefen auf
+     mehrere IDs. Allerdings werden - im Gegensatz zu id() -
+     Adjektive und Ausdruecke der Art "<ID> <nummer>" nicht
+     beruecksichtigt.
+     Ebenso werden Spezialitaeten wie Unitobjekte und Objekte mit
+     ueberschriebenem id() nicht beruecksichtigt! Im Zweifelsfall ist daher
+     doch die Nutzung von id() zu empfehlen.
+
+RUeCKGABEWERT:
+     Anzahl der zutreffenden IDs.
+
+BEISPIELE:
+     Angenommen, ein Objekt habe folgende Bezeichner:
+
+     AddId( ({"murmel","kugel","glasmurmel","glaskugel"}) );
+     AddAdjective( "rund" );
+
+     Dann liefern die angegebenen match_ids()-Aufrufe folgende Ergebnisse:
+
+     match_ids( ({"murmel","stein"}) );         => 1
+     match_ids( ({"murmel","kugel"}) );         => 2
+     match_ids( ({"runde murmel"}) );           => 0
+     match_ids( ({"murmel 2"}) );               => 0, auch wenn dies die 
+                                               zweite Murmel in der
+                                               gleichen Umgebung ist
+
+SIEHE AUCH:
+     AddId(), AddAdjective(), AddSingularId(), AddPluralId(), present(),
+     id(), /std/thing/description.c, /std/unit.c
+
+----------------------------------------------------------------------------
+Last modified: Sat Mar 15 21:40:00 2000 by Paracelsus
diff --git a/doc/lfun/move b/doc/lfun/move
new file mode 100644
index 0000000..b323c34
--- /dev/null
+++ b/doc/lfun/move
@@ -0,0 +1,212 @@
+move()
+
+FUNKTION: 
+     Fuer unbelebte Gegenstaende (/std/thing):
+       varargs int move(object|string dest, int method);
+
+     Fuer Lebewesen (/std/living, /std/npc, usw.):
+       varargs int move(object|string dest, int method, string dir, 
+                        string textout, string textin);
+
+DEFINIERT IN:
+     /std/thing/moving.c
+     /std/living/moving.c
+
+ARGUMENTE:
+     dest
+          Das Zielobjekt (entweder der Dateiname oder das Objekt selbst).
+
+     method
+          Die Art der Bewegung (eine der unten aufgefuehrten Arten; es
+          koennen auch mehrere zusammenge-ODER-t werden).
+
+     dir
+          Die Richtung, in die ein Lebewesen geht. Normalerweise ist das die
+          eingeschlagene Laufrichtung (zB. "nach Norden").
+
+     textout
+          Verlaesst ein Lebewesen einen Raum, so wird diese Meldung in den
+          Raum geschickt. Ist bei dir ein String angegeben, so wird dieser
+          noch an textout angehaengt. Der Name des Lebewesens wird der
+          Meldung in jedem Fall vorangestellt.
+
+     textin
+          Dieser Text wird im Zielraum ausgegeben, wenn ein Lebewesen ihn
+          betritt. Bei normaler Fortbewegung ist das "kommt an". Dem Text
+          wird noch der Name des Spielers vorangestellt.
+
+BESCHREIBUNG:
+     Es wird versucht, das Objekt in das Zielobjekt dest zu bewegen.
+     Abhaengig vom momentanen Aufenthaltsort und dem Zielobjekt ist die
+     Bewegungsmethode method zu waehlen.
+
+     In <moving.h> sind folgende Konstanten fuer die Art der Bewegung
+     definiert:
+     M_NOCHECK
+          Es werden keine Abfragen durchgefuehrt, ob das Objekt ueberhaupt
+          in das Zielobjekt hineinpasst (was zB. aus Gewichtsgruenden der
+          Fall sein koennte).
+
+     M_GO
+          Ein Lebewesen bewegt sich gehend von einem Raum in den naechsten.
+          Bei normalem Gehen wird diese Methode benutzt; man sollte sie auch
+          benutzen, wenn man Spieler ueber einen SpecialExit in einen
+          benachbarten Raum bewegt.
+
+     M_TPORT
+          Ein Lebewesen wird von einem Raum in einen anderen teleportiert.
+          Im Gegensatz zu M_GO kann ein Raum verhindern, dass man ihn
+          mittels M_TPORT verlaesst oder betritt.
+
+     M_NO_SHOW
+          Beim Bewegen von Lebewesen bekommen diese die Beschreibung des
+          Zielraumes nicht ausgegeben.
+
+     M_NO_ATTACK
+          Beim Bewegen von Lebewesen bekommen diese keinen
+          Begruessungsschlag, wenn ein Feind im Zielraum steht.
+
+     M_SILENT
+          Es werden beim Bewegen keine Meldungen ausgegeben. Dieser
+          Parameter wirkt sich nur auf das Bewegen von Lebenwesen aus.
+
+     M_GET
+          Das Objekt wird von einem unbelebten Objekt (zB. einem Raum, einer
+          Leiche, einem Beutel) in ein lebendes Objekt (Spieler oder NPC)
+          bewegt.
+
+     M_PUT
+          Das Objekt wird von einem lebenden Objekt in ein unbelebtes Objekt
+          bewegt.
+
+     M_GIVE
+          Das Objekt wird von einem Lebewesen an ein anderes Lebewesen
+          weitergereicht.
+
+     M_MOVE_ALL (Nur fuer Objekte, die /std/unit.c erben)
+          Es wird die gesamte Menge bewegt, nicht nur ein Teil.
+
+     Soll das Objekt auf jeden Fall und ohne jede Abfrage bewegt werden, so
+     reicht es, als method M_NOCHECK zu uebergeben.
+
+     Waffen und Ruestungen werden, soweit sie gezueckt bzw. angezogen sind,
+     beim Bewegen auf jeden Fall weggesteckt bzw. ausgezogen. Ist in method
+     M_SILENT enthalten, so geschieht dies ohne Meldungen.
+
+     Die erste Art des Funktionsaufrufs ist sowohl beim Bewegen von
+     Lebewesen als auch von unbelebten Objekten moeglich. Die zweite Art
+     laesst sich nur bei Lebewesen anwenden.
+
+ANMERKUNG:
+     Diese Funktion sollte nicht (mehr) ueberschrieben werden. Stattdessen
+     greift bitte auf PreventMove() und NotifyMove() zurueck. RMs sind
+     aufgerufen, Objekt mit ueberschriebenen move() nur noch dann
+     anzuschliessen, wenn der Zweck sonst nicht erreicht werden kann. Solltet
+     ihr move() ueberschreiben: Seid euch sehr genau im klaren, was move()
+     genau macht. ;-)
+     
+     Wenn Livings bewegt werden, sorgt move() automatisch in Abhaengigkeit
+     von P_PARA dafuer, dass das Lebewesen in der korrekten (Parallel-)Welt
+     landet.
+
+     Bei Gegenstaenden wird ebenfalls versucht, die richtige Zielwelt
+     auszuwaehlen (damit z.B. in der Parallelwelt geworfene Bumerangs auch nur
+     innerhalb der Parallelwelt fliegen). Um Rechenzeit zu sparen, wird das
+     allerdings nur versucht, wenn 'dest' ein Filename ist und kein Objekt.
+
+     Grund: bei Zielobjekten handelt es sich meist um Bewegungen in das Inv
+     oder Env eines Spielers - und die sind uninteressant. Raumwechsel dagegen
+     erfolgen fast immer unter Angabe eines Filenamens anstatt eines Objektes.
+
+RUeCKGABEWERT:
+     Alle Rueckgabewerte sind als symbolische Konstanten in <moving.h>
+     definiert. (MOVE_OK ist 1, alle anderen sind <0 und symbolisieren Fehler.
+     Traditionell erfolgt die Pruefung auf erfolgreiches Move mit == 1, in
+     Zukunft wird == MOVE_OK empfohlen.)
+     
+     MOVE_OK
+          Die Bewegung wurde erfolgreich abgeschlossen.
+
+     ME_PLAYER
+          Lebewesen lassen sich nicht ohne weiteres bewegen. Es muss
+          mindestens eine der Methoden M_NOCHECK, M_GO oder M_TPORT
+          angegeben werden.
+
+     ME_TOO_HEAVY
+          Das Zielobjekt kann dieses Objekt aus Gewichtsgruenden nicht mehr
+          aufnehmen.
+
+     ME_CANT_TPORT_IN
+          Das Zielobjekt verbietet das Teleportieren in sich hinein (nur bei
+          M_TPORT ohne M_NOCHECK).
+
+     ME_CANT_TPORT_OUT
+          Der Raum, in dem sich das Lebewesen befindet, verbietet das
+          Teleportieren aus sich hinaus (nur bei M_TPORT ohne M_NOCHECK).
+
+     ME_CANT_BE_DROPPED
+          Das Objekt kann nicht fallen gelassen werden (zB. weil P_NODROP
+          gesetzt ist).
+
+     ME_CANT_BE_TAKEN
+          Das Objekt kann nicht genommen werden (zB. weil P_NOGET gesetzt
+          ist).
+
+     ME_CANT_BE_INSERTED
+          Das Zielobjekt verhindert das Einfuegen aus bestimmten Gruenden.
+
+     ME_CANT_LEAVE_ENV
+          Der Container verhindert ein verlassen des Objektes
+
+     ME_TOO_HEAVY_FOR_ENV
+          Ein Objekt kann einen Behaelter nicht verlassen, da es dem 
+          Lebewesen sonst zu schwer wuerde.
+
+     TOO_MANY_OBJECTS
+          Das Zielobjekt kann soviele Objekte nicht mehr aufnehmen.
+
+     ME_NOT_ALLOWED
+          Raeume mit gesetzter Property P_NO_PLAYERS koennen nur von
+          Testspielern und Magiern betreten werden. Bei Spielern oder
+          Gildentesties gibt es diese Fehlermeldung.
+     ME_WAS_DESTRUCTED
+          Das Objekt hat sich entweder im Verlaufe der Bewegung selbst
+          zerstoert oder wurde zerstoert, sodass move() nicht erfolgreich
+          beendet werden konnte. (Bsp: sensitive Objekte)
+
+     ME_DONT_WANT_TO_BE_MOVED
+          Das Objekt moechte nicht bewegt werden.
+
+BEISPIELE:
+        o Ein Objekt "gibt sich" dem Spieler:
+
+          move(this_player(), M_GET);
+
+        o Ein Lebewesen wird in die Abenteurergilde teleportiert:
+
+          lv->move("/gilden/abenteurer", M_TPORT);
+
+        o Ein Spieler "wird in die Gilde gegangen":
+
+          this_player()->move("/gilden/abenteurer", M_GO, "in die Gilde");
+
+          Spieler, die mit ihm im gleichen Raum stehen, sehen folgende
+          Meldung:
+          "<name> geht in die Gilde."
+
+        o Ein Spieler schwimmt durchs Meer:
+
+          this_player()->move("meer_xy", M_GO, "nach Norden", "schwimmt",
+                              "schwimmt herein");
+
+          Spieler in seinem Startraum sehen "<name> schwimmt nach Norden.",
+          Spieler in seinem Zielraum sehen "<name> schwimmt herein."
+
+SIEHE AUCH:
+     move_object(), remove(), setmin, setmmin, setmout, setmmout, review,
+     PreventInsert(), PreventLeave(), PreventInsertLiving(),
+     PreventLeaveLiving(), PreventMove(), NotifyInsert(), NotifyLeave(),
+     NotifyMove(), NotifyRemove(), init(), exit(),
+     P_NO_PLAYERS, P_PARA, /std/thing/moving.c, /std/living/moving.c
+     -----------------------------------------------------------------------
+2015-Jan-19, Arathorn
diff --git a/doc/lfun/moved_objects b/doc/lfun/moved_objects
new file mode 100644
index 0000000..edcbd6e
--- /dev/null
+++ b/doc/lfun/moved_objects
@@ -0,0 +1,30 @@
+moved_objects()
+
+FUNKTION:
+    public object *moved_objects(void);
+
+DEFINIERT IN:
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+    keine
+
+BESCHREIBUNG:
+    Mit dieser Funktion lassen sich die Objekte ermitteln, die das Lebewesen
+    beim letzten Aufruf von drop_objects(L) oder einer aehnlichen Funktion
+    bewegt hat.
+
+RUECKGABEWERT:
+    Array mit den zuletzt bewegten Objekten, oder ein leeres Array, falls
+    keine Objekte auf die Beschreibung des letzten drop_objects() / 
+    pick_objects() / put_objects() / give_objects() passten.
+
+BEISPIELE:
+    siehe drop_objects() und give_objects()
+
+SIEHE AUCH:
+    drop_objects(L), give_objects(L), pick_objects(L), put_objects(L),
+    show_objects(L), NotifyMove(L)
+
+----------------------------------------------------------------------------
+Last modified: Thu Aug 28 22:19:26 2008 by Amynthor
diff --git a/doc/lfun/moved_where b/doc/lfun/moved_where
new file mode 100644
index 0000000..8bb7e9c
--- /dev/null
+++ b/doc/lfun/moved_where
@@ -0,0 +1,28 @@
+moved_where()
+
+FUNKTION:
+    public object moved_where(void);
+
+DEFINIERT IN:
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+    keine
+
+BESCHREIBUNG:
+    Mit dieser Funktion laesst sich ermitteln, wohin das Lebewesen zuletzt
+    mittels put_objects(L) oder give_objects(L) etwas bewegt oder wem es mit
+    show_objects(L) etwas gezeigt hat.
+
+RUECKGABEWERT:
+    Der Container oder das Lebewesen, wohin die Gegenstaende bewegt wurden.
+
+BEISPIEL:
+    siehe give_objects()
+
+SIEHE AUCH:
+    put_objects(L), give_objects(L), show_objects(L), NotifyInsert(L),
+    P_LAST_CONTENT_CHANGE
+
+----------------------------------------------------------------------------
+Last modified: Thu Aug 28 22:16:15 2008 by Amynthor
diff --git a/doc/lfun/muster b/doc/lfun/muster
new file mode 100644
index 0000000..d519f69
--- /dev/null
+++ b/doc/lfun/muster
@@ -0,0 +1,16 @@
+muster()
+
+FUNKTION:
+	type muster(...)
+	
+ARGUMENTE:
+	
+BESCHREIBUNG:
+
+RUECKGABEWERT:
+	
+BEMERKUNG:
+	
+BEISPIEL:
+
+SIEHE AUCH:
diff --git a/doc/lfun/name b/doc/lfun/name
new file mode 100644
index 0000000..26e50fa
--- /dev/null
+++ b/doc/lfun/name
@@ -0,0 +1,50 @@
+name()
+
+FUNKTION:
+     varargs string name(int casus, int demon);
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     casus
+          Der Fall, in dem der Name dekliniert werden soll.
+     demon
+          Gibt an, ob der Name mit bestimmtem oder unbestimmtem Artikel
+          versehen werden soll:
+             + demon = 0: Unbestimmter Artikel.
+             + demon = 1: Bestimmter Artikel.
+             + demon = 2: Finde selbst heraus, ob ein bestimmter oder ein
+               unbestimmter Artikel verwendet werden soll.
+
+BESCHREIBUNG:
+     Diese Funktion ermittelt den Namen des Objektes im gewuenschten Fall
+     und mit dem angegebenen Artikel. Moegliche Werte fuer casus sind in
+     <thing/language.h> definiert. Weiterhin werden auch (falls angegeben)
+     die Namensadjektive dekliniert und in den Namen eingebaut.
+
+RUeCKGABEWERT:
+     String mit dem Namen des Objektes.
+
+BEMERKUNGEN:
+     Falls P_ARTICLE gesetzt ist, werden weder Artikel noch Namensadjektive
+     in den Namen eingebaut.
+
+     Wenn man als casus RAW angibt, wird der Name im Nominativ ohne Artikel
+     und Namensadjektive zurueckgegeben.
+
+BEISPIELE:
+     Wenn das Objekt ein Ball mit P_NAME="Ball" und P_NAME_ADJ="klein" ist,
+     so liefern die folgenden Aufrufe die angegebenen Ergebnisse:
+
+     name(WER,0);    => "ein kleiner Ball"
+     name(WESSEN,1); => "des kleinen Balls"
+     name(RAW);      => "Ball"
+     name(WEM,2);    => "einem kleinen Ball" oder "dem kleinen Ball",
+                        abhaengig davon, wieviele Baelle gerade da sind.
+
+SIEHE AUCH:
+     /std/thing/description.c, Name()
+
+----------------------------------------------------------------------------
+Last modified: Sat Aug  3 11:28:55 2002 by Vanion
diff --git a/doc/lfun/notify_player_change b/doc/lfun/notify_player_change
new file mode 100644
index 0000000..1765af6
--- /dev/null
+++ b/doc/lfun/notify_player_change
@@ -0,0 +1,56 @@
+void notify_player_change(string/object who, int rein [, int invis])
+
+FUNKTION:
+    void /notify_player_change(object who, int rein)
+    void /std/player/base::notify_player_change(string who, int rein,
+                                                int invis)
+  
+GERUFEN VON:
+    /std/player/base.c (d.h. alle Spielershells/-Objekte)
+
+ARGUMENTE:
+    string who
+      getuid() eines Spielers
+    object who
+      Spieler-Objekt
+    int rein
+      0 fuer das MUD verlassende, 1 fuer hereinkommende Spieler
+    int invis
+      1 fuer unsichtbare Spieler (Magier)
+
+BESCHREIBUNG:
+    Diese Funktion wird von Lebewesen fuer hereinkommende und das Spiel
+    verlassende Spieler an verschiedenen Stellen aufgerufen:
+    
+    * in anderen Spielern mit notify_player_change() mit drei Parametern
+      - dies dient fuer die "erwarte"-Funktionalitaet
+    * in der Gilde des Spielern mit zwei Parameter
+      - damit koennen Gilden notwendige Anpassungen vornehmen
+
+    Diese Funktionen werden auch gerufen, wenn Magier "invis -e" bzw.
+    "vis e" benutzen.
+
+BEISPIELE:
+    // in einer Gilde:
+    void notify_player_change(object pl, int rein) {
+      if (rein && objectp(pl)) {
+        // Checks, ob Spielerskills in Ordnung sind
+        mapping bete = pl->QuerySkill("bete");
+        
+        if (!mappingp(bete)) {
+          if (IS_LEARNER(pl) || pl->QueryProp(P_TESTPLAYER)) {
+            tell_object(pl, break_string(
+              "Du bist ein kaputter Test-Kleriker ...", 78,
+              "Arkshat teilt dir mit: "));
+            // notduerftige Reparaturen
+          } else
+            raise_error("Klerus: Kaputter oder gesetzer Kleriker!\n");
+        }
+      }
+    }
+
+SIEHE AUCH:
+    RegisterEvent mit (EVT_LIB_LOGIN, EVT_LIB_LOGOUT)
+    erwarte
+
+1. Sep 2011 Gloinson
diff --git a/doc/lfun/obsolete/AddHpHook b/doc/lfun/obsolete/AddHpHook
new file mode 100644
index 0000000..e227951
--- /dev/null
+++ b/doc/lfun/obsolete/AddHpHook
@@ -0,0 +1,29 @@
+********************* OBSOLETE LFUN ***********************************
+* Diese Efun bitte nicht mehr benutzen, sondern stattdessen die       *
+* Hooks (s. /doc/std/hooks).                                          *
+***********************************************************************
+AddHpHook()
+FUNKTION:
+     int AddHpHook(object ob)
+
+DEFINIERT IN:
+     /std/player/life.c
+
+ARGUMENTE:
+     ob - das Objekt, das sich eintragen moechte.
+
+BESCHREIBUNG:
+     Traegt ein Objekt in P_HP_HOOKS ein, wenn es nicht schon darin steht.
+
+     Aendern sich beim Spieler dann HP oder KP (nicht durch Set()), wird
+     an allen eingetragenen Objekten NotifyHpChange() gerufen.
+
+RUECKGABEWERT:
+     1, wenn Erfolg, 0 sonst
+
+SIEHE AUCH:
+     Gegenpart:	RemoveHpHook()
+     Props:	P_HP_HOOKS, P_HP
+     Verwandt:	reduce_hit_points(), do_damage(), buffer_hp()
+
+23.Feb.2004 Gloinson
diff --git a/doc/lfun/obsolete/AddInsertHook b/doc/lfun/obsolete/AddInsertHook
new file mode 100644
index 0000000..5e80272
--- /dev/null
+++ b/doc/lfun/obsolete/AddInsertHook
@@ -0,0 +1,59 @@
+********************* OBSOLETE LFUN ***********************************
+* Diese Efun bitte nicht mehr benutzen, sondern stattdessen die       *
+* Hooks (s. /doc/std/hooks).                                          *
+***********************************************************************
+AddInsertHook()
+
+FUNKTION:
+     void AddInsertHook(object ob);
+
+DEFINIERT IN:
+     /std/player/restrictions.c
+
+ARGUMENTE:
+     ob - Das Objekt, das informiert werden soll, wenn ein Objekt dem
+          Spielerinventar hinzugefuegt wurde.
+
+BESCHREIBUNG:
+     (Diese Funktionalitaet wurde ersetzt durch den allgemeinen Hook
+      H_HOOK_INSERT und ist nur noch aus Gruenden der Kompatibilitaet
+      vorhanden.)
+
+     Diese Funktion wird im Spielerobjekt aufgerufen, um das Objekt ob als
+     Hook-Listener anzumelden. Auf diese Weise eingetragene Listener
+     werden informiert, wenn ein Objekt ins Spielerinventar bewegt wurde.
+     Technisch wird die Bewegung ueber NotifyInsert() im Spielerobjekt
+     detektiert, und im Listener-Objekt wird die Funktion InsertNotify()
+     gerufen, die als Parameter das neu ins Spielerinventar bewegte Objekt
+     uebergeben bekommt.
+
+RUeCKGABEWERT:
+     keiner
+
+BEMERKUNGEN:
+     Das Listener-Objekt muss sich ebenfalls im Spielerinventar befinden,
+     ansonsten wird der eingetragene Hook wieder geloescht.
+
+BEISPIEL:
+     
+     a) Objekt "ob" wird im Spieler als Listener angemeldet:
+        this_player()->AddInsertHook(ob);
+
+     b) Objekt "new" wird ins Spielerinventar bewegt, das Spielerobjekt
+        informiert "ob" darueber:
+        ob->InsertNotify(new);
+
+     c) Das Listener-Objekt "ob" reagiert darauf, z.B. indem es die Fackel
+        loescht, sofern sie vorher brannte:
+        void InsertNotify(object new) {
+          if ( objectp(new) && new->id("\nfackel") && 
+               new->QueryProp(P_LIGHTED) )
+            new->unlight();
+          return;
+        }
+
+SIEHE AUCH:
+    NotifyInsert(), RemoveInsertHook(), QueryInsertHooks()
+
+----------------------------------------------------------------------------
+Last modified: 14.04.2010, Arathorn
diff --git a/doc/lfun/obsolete/ModifySkillAttributeOld b/doc/lfun/obsolete/ModifySkillAttributeOld
new file mode 100644
index 0000000..65d9afc
--- /dev/null
+++ b/doc/lfun/obsolete/ModifySkillAttributeOld
@@ -0,0 +1,90 @@
+ModifySkillAttributeOld()
+
+FUNKTION:
+    varargs int ModifySkillAttributeOld(object caster, string atrname, 
+                                        int value, int duration, mixed fun)
+
+DEFINIERT IN:
+    /std/living/skill_attributes.c
+
+ARGUMENTE:
+    <caster>    IGNORIERT
+                frueher Objekt, das die Modifikation vornimmt, heute ist
+                dieses Argument ohne Bedeutung und wird ignoriert.
+
+    <atrname>   STRING
+                Name des zu veraendernden Attributes
+                (Definiert in /sys/living/skill_attributes.h)
+
+    <value>     INT
+                Neuer Wert des Attributs (Standard: 100)
+
+    <duration>  INT
+                Dauer in Sekunden
+
+    <fun>       NICHT MEHR UNTERSTUETZT - ModifySkillAttribute() nutzen!!
+
+BESCHREIBUNG:
+    Diese Funktion existiert nur aus Kompatibilitaetsgruenden. Bitte in neuen
+    Objekten NICHT mehr verwenden und in alten nach Moeglichkeit ausbauen!
+
+    Aendert ein Skill-Attribut eines Living.
+    
+    Fuer <value> ist folgendes zu beachten:
+    Frueher handelte es sich um einen multiplikativen Faktor. 100 hatte die
+    Bedeutung von 1 und veraenderte nichts. Heute sind die Modifikatoren
+    additiv. Diese Funktion macht eine einfache Umrechnung, indem sie vom hier
+    uebergeben Wert 100 abzieht. (In der Annahme, dass frueher meist eh nur 
+    ein Modifikator zur gleichen Zeit aktiv war.)
+    Gibt man hier also hier eine 100 an, wird ein Modifikator von 0 einge-
+    tragen, der nichts aendert, bei 200 wird ein Modifikator von 100 einge-
+    tragen, bei 50 einer von -50, welcher das Skillattribute folglich 
+    reduziert.
+
+    Es sind momentan max. 5 gleichzeitige Skillattribute-Modifikatoren pro SA
+    zulaessig.
+
+RUECKGABEWERT:
+     0  wenn der Wert ungueltig ist oder aus sonstigem Grunde nicht gesetzt
+        werden konnte (fuer bessere Diagnostik -> ModifySkillAttribute()).
+    >0  wenn alles okay war
+
+BEMERKUNGEN:
+    Frueher musste ein setzendes Objekt ein groesseres P_LEVEL haben als das
+    Objekt, welches einen vorherigen Modifikator gesetzt hat, um diesen zu
+    ueberschreiben. Dies ist inzwischen ohne Bedeutung.
+
+BEISPIELE:
+    Ein NPC:
+
+    void
+    create() {
+    .
+    .
+    .
+    AddSpell(1, 1,
+      "Der fuerchterliche NPC haut Dir auf den Kopf.\n",
+      "Der fuerchterliche NPC haut @WEN auf den Kopf.\n",
+      DT_MAGIC, "schwaechen");
+    .
+    .
+    }
+
+    schwaechen(object enemy, int damage, mixed *dam_type) {
+      int ergebnis;
+      ergebnis = enemy->ModifySkillAttributeOld(this_object(), SA_QUALITY, 50, 5);
+      if (ergebnis > 0)
+          tell_object(enenmy, "Du fuehlst Dich ganz schwach.\n");
+    }
+    
+    Der NPC schwaecht seinen Gegner erheblich! Alles wird fuer 5 Sekunden um
+    50, d.h. 0.5 Skillattribute reduziert (50 - 100 => -50 als Modifikator).
+
+SIEHE AUCH:
+    P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS,
+    ModifySkillAttribute, QuerySkillAttribute(),
+    RemoveSkillAttributeModifer(), QuerySkillAttributeModifier()
+
+-----------------------------------------------------------------------------
+07.08.2008 Zesstra
+
diff --git a/doc/lfun/obsolete/NotifyGiveQuest b/doc/lfun/obsolete/NotifyGiveQuest
new file mode 100644
index 0000000..7e29798
--- /dev/null
+++ b/doc/lfun/obsolete/NotifyGiveQuest
@@ -0,0 +1,37 @@
+********************* OBSOLETE LFUN ***********************************
+* Diese Efun bitte nicht mehr benutzen, sondern stattdessen die       *
+* Events (siehe "man events").                                        *
+***********************************************************************
+
+NotifyGiveQuest()
+
+FUNKTION:
+     void NotifyGiveQuest(object pl, string key);
+     
+DEFINIERT IN:
+     /std/gilden_ob.c
+     
+ARGUMENTE:
+     pl     Der Spieler, der eine Quest geloest hat.
+     key    Name der geloesten Quest.
+     
+BESCHREIBUNG:
+     Die Funktion wird in der Gilde des Spielers aufgerufen, wenn der 
+     Spieler eine gueltige Quest besteht - und zwar unabhaengig davon,
+     ob er sie bereits geloest hat, oder nicht.
+     
+RUECKGABEWERT:
+     keiner
+     
+BEMERKUNGEN:
+     Die Funktion ist dafuer gedacht, von Gildenprogrammierern ueberschrieben
+     zu werden, wenn beispielsweise bestimmte Quests als Aufstiegsbedingung
+     verlangt werden, diese aber in der entsprechenden Gilde geloest sein
+     muessen (z.B. Obergladiator als Level-5-Zauberer).
+     
+SIEHE AUCH:
+     /std/gilden_ob.c
+     /std/player/quests.c
+
+----------------------------------------------------------------------------
+Last modified: Fri Oct 4 10:17:00 1996 by Silvana
diff --git a/doc/lfun/obsolete/NotifyHpChange b/doc/lfun/obsolete/NotifyHpChange
new file mode 100644
index 0000000..6f82b03
--- /dev/null
+++ b/doc/lfun/obsolete/NotifyHpChange
@@ -0,0 +1,47 @@
+********************* OBSOLETE LFUN ***********************************
+* Diese Efun bitte nicht mehr benutzen, sondern stattdessen die       *
+* Hooks (s. /doc/std/hooks).                                          *
+***********************************************************************
+NotifyHpChange()
+
+FUNKTION:
+	void NotifyHpChange();
+
+DEFINIERT IN:
+	/std/player/life.c
+
+ARGUMENTE:
+	keine
+
+BESCHREIBUNG:
+	Wenn sich die Lebenspunkte eines Spielers aendern, so werden davon
+	auch andere Objekte unterrichtet, sofern diese mittels der Funktion
+	AddHpHook() bei eben diesem Spieler angemeldet wurden.
+	Fortan wird dann die Funktion NotifyHpChange() in diesen
+	angemeldeten Objekten aufgerufen, wenn sich die Property P_HP des
+	Spielers aendert. Es werden hierbei keine Argumente an
+	NotifyHpChange() uebergeben, die aktuellen Lebenspunkte kann man ja
+	auch ohne weiteres ueber die Property P_HP in Erfahrung bringen und
+	aeltere Werte muss man sich gesondert merken. Zu beachten ist, dass
+	die Property P_HP bei Aufruf der Funktion NotifyHpChange() bereits
+	den neuen Wert enthaelt.
+	Bei dem Spieler angemeldete Objekte, die von Lebenspunkteaenderungen
+	informiert werden sollen, werden automatisch aus der Liste entfernt,
+	wenn sie zerstoert wurden. Diese Liste ist in der Property
+	P_HP_HOOKS zu finden. Per Hand kann man sie auch explizit mittels
+	der Funktion RemoveHpHook() entfernen.
+	Stirbt ein Spieler, so wird die Funktion NotifyPlayerDeath()
+	aufgerufen und nicht NotifyHpChange()!
+
+RUeCKGABEWERT:
+	keiner
+
+BEISPIELE:
+	ist in Arbeit
+
+SIEHE AUCH:
+	P_HP, P_HP_HOOKS, AddHpHook(), RemoveHpHook(),
+	Defend(), do_damage(), NotifyPlayerDeath()
+
+----------------------------------------------------------------------------
+Last modified: Thu Nov 19 13:54:33 1998 by Patryn
diff --git a/doc/lfun/obsolete/QueryEnemy b/doc/lfun/obsolete/QueryEnemy
new file mode 100644
index 0000000..bf0b583
--- /dev/null
+++ b/doc/lfun/obsolete/QueryEnemy
@@ -0,0 +1,24 @@
+********************* OBSOLETE LFUN ***********************************
+FUNKTION:
+	object QueryEnemy();
+
+DEFINIERT IN:
+	/std/living/combat.c
+
+ARGUMENTE:
+	keine
+
+RUeCKGABEWERT:
+	gefundener Gegner
+
+BESCHREIBUNG:
+	Diese Funktion liefert zufaellig einen der anwesenden Gegner
+	zurueck, sofern keine Gegner bevorzugt ausgewaehlt werden.
+
+BEMERKUNGEN:
+  Diese Lfun existiert nicht mehr. Bitte SelectEnemy() benutzen.
+
+SIEHE AUCH:
+	SelectEnemy(), QueryPreferedEnemy(), P_PREFERED_ENEMY
+----------------------------------------------------------------------------
+07.02.2009, Zesstra
diff --git a/doc/lfun/obsolete/QueryInsertHooks b/doc/lfun/obsolete/QueryInsertHooks
new file mode 100644
index 0000000..c43d036
--- /dev/null
+++ b/doc/lfun/obsolete/QueryInsertHooks
@@ -0,0 +1,31 @@
+********************* OBSOLETE LFUN ***********************************
+* Diese Efun bitte nicht mehr benutzen, sondern stattdessen die       *
+* Hooks (s. /doc/std/hooks).                                          *
+***********************************************************************
+QueryInsertHooks()
+
+FUNKTION:
+     object *QueryInsertHooks();
+
+DEFINIERT IN:
+     /std/player/restrictions.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     (Diese Funktionalitaet wurde ersetzt durch den allgemeinen Hook
+      H_HOOK_INSERT und ist nur noch aus Gruenden der Kompatibilitaet
+      vorhanden.)
+
+     Diese Funktion gibt die aktuell beim Spielerobjekt angemeldeten
+     Listener-Objekte zurueck.
+
+RUeCKGABEWERT:
+     Array aus Objektpointern oder leeres Array
+
+SIEHE AUCH:
+    NotifyInsert(), AddInsertHook(), RemoveInsertHook()
+
+----------------------------------------------------------------------------
+Last modified: 14.04.2010, Arathorn
diff --git a/doc/lfun/obsolete/QueryOptQP b/doc/lfun/obsolete/QueryOptQP
new file mode 100644
index 0000000..f6f72c5
--- /dev/null
+++ b/doc/lfun/obsolete/QueryOptQP
@@ -0,0 +1,25 @@
+QueryOptQP()
+
+FUNKTION:
+    int QueryOptQP()
+
+DEFINIERT IN:
+    /secure/questmaster.c
+
+ARGUMENTE:
+    keine
+
+RUECKGABEWERT:
+    Abenteuerpunkte der optionalen Quests
+
+BESCHREIBUNG:
+    Diese Funktion liefert die Abenteuerpunkte der optionalen
+    Abenteuer zurueck. 
+
+SIEHE AUCH:
+    GiveQuest, QueryQuest, /secure/questmaster.h, QueryGroupedKeys,
+    QueryMaxQP, QueryTotalQP
+
+----------------------------------------------------------------------------
+Zuletzt geaendert: Sam, 25. Nov 2000, 14:04:28 von Zook.
+
diff --git a/doc/lfun/obsolete/RemoveHpHook b/doc/lfun/obsolete/RemoveHpHook
new file mode 100644
index 0000000..d3177f1
--- /dev/null
+++ b/doc/lfun/obsolete/RemoveHpHook
@@ -0,0 +1,25 @@
+********************* OBSOLETE LFUN ***********************************
+* Diese Efun bitte nicht mehr benutzen, sondern stattdessen die       *
+* Hooks (s. /doc/std/hooks).                                          *
+***********************************************************************
+RemoveHpHook()
+FUNKTION:
+     int RemoveHpHook(object ob)
+
+DEFINIERT IN:
+     /std/player/life.c
+
+ARGUMENTE:
+     ob - das Objekt, das sich austragen moechte.
+
+BESCHREIBUNG:
+     Entfernt den Eintrag fuer dieses Objekt in P_HP_HOOKS.
+
+RUECKGABEWERT:
+     1, wenn Erfolg, 0 sonst
+
+SIEHE AUCH:
+     Gegenpart:	AddHpHook()
+     Props:	P_HP_HOOKS, P_HP
+
+23.Feb.2004 Gloinson
diff --git a/doc/lfun/obsolete/RemoveInsertHook b/doc/lfun/obsolete/RemoveInsertHook
new file mode 100644
index 0000000..f232eed
--- /dev/null
+++ b/doc/lfun/obsolete/RemoveInsertHook
@@ -0,0 +1,31 @@
+********************* OBSOLETE LFUN ***********************************
+* Diese Efun bitte nicht mehr benutzen, sondern stattdessen die       *
+* Hooks (s. /doc/std/hooks).                                          *
+***********************************************************************
+RemoveInsertHook()
+
+FUNKTION:
+     void RemoveInsertHook(object ob);
+
+DEFINIERT IN:
+     /std/player/restrictions.c
+
+ARGUMENTE:
+     ob - Das Objekt, das als Listener aus der Liste ausgetragen werden soll
+
+BESCHREIBUNG:
+     (Diese Funktionalitaet wurde ersetzt durch den allgemeinen Hook
+      H_HOOK_INSERT und ist nur noch aus Gruenden der Kompatibilitaet
+      vorhanden.)
+
+     Diese Funktion wird im Spielerobjekt aufgerufen, um ein als Listener
+     eingetragenes Hook-Objekt ob wieder auszutragen.
+
+RUeCKGABEWERT:
+     keiner
+
+SIEHE AUCH:
+    NotifyInsert(), AddInsertHook(), QueryInsertHooks()
+
+----------------------------------------------------------------------------
+Last modified: 14.04.2010, Arathorn
diff --git a/doc/lfun/obsolete/TestAttributeLock b/doc/lfun/obsolete/TestAttributeLock
new file mode 100644
index 0000000..126e931
--- /dev/null
+++ b/doc/lfun/obsolete/TestAttributeLock
@@ -0,0 +1,25 @@
+********************* OBSOLETE LFUN ***********************************
+TestAttributeLock()
+FUNKTION:
+     string TestAttributeLock(mapping check)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+

+PARAMETER:

+     check	- Mapping mit Attributen: ([<attr>:<wert>])

+
+BESCHREIBUNG:
+     Prueft, ob eines der im Mapping enthaltenen Attribute blockiert

+     ist (bereits durch einen anderen Modifier belegt wurde).
+     
+     Da Modifier nicht mehr direkt blockieren ist diese Funktion obsolet
+     und in Livings inzwischen nicht mehr existent.
+
+SIEHE AUCH:
+     QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+     SetAttr(), SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+     P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_ATTRIBUTES_MODIFIER,

+     P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c

+
+11.05.2007, Zesstra
diff --git a/doc/lfun/obsolete/extra_look b/doc/lfun/obsolete/extra_look
new file mode 100644
index 0000000..753a8ed
--- /dev/null
+++ b/doc/lfun/obsolete/extra_look
@@ -0,0 +1,19 @@
+********************* VERALTETE LFUN ******************************
+* Diese LFUN ist veraltet. Bitte benutzt sie NICHT mehr, sondern  *
+* stattdessden AddExtraLook().                                    *
+*******************************************************************
+
+extra_look()
+
+FUNKTION:
+    string extra_look();
+
+BESCHREIBUNG:
+    Kann in Objekt definiert sein. Wenn ein Living (std/living/description)
+    das Objekt enthaelt, wird zu dessen long() der zurueckgegebene String
+    hinzugefuegt.
+
+SIEHE AUCH:
+    AddExtraLook()
+
+25.Jan 2015 Gloinson
diff --git a/doc/lfun/obsolete/paramove b/doc/lfun/obsolete/paramove
new file mode 100644
index 0000000..96686a2
--- /dev/null
+++ b/doc/lfun/obsolete/paramove
@@ -0,0 +1,45 @@
+****************************************************************************
+************************* VERALTETE LFUN ***********************************
+************************* DO NOT USE!    ***********************************
+****************************************************************************
+paramove()
+
+FUNKTION:
+     int paramove();
+
+DEFINIERT IN:
+     /std/room/para.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Bewegt den Spieler ggf. in eine Paralleldimension.
+
+RUeCKGABEWERT:
+     1, wenn der Spieler die Dimension gewechselt hat.
+
+BEISPIEL:
+     init(){
+       if(!paramove()) ::init();
+      }
+
+BEMERKUNGEN:
+     DIESE FUNKTION NICHT MEHR BENUTZEN!
+
+     Diese Funktion sollte nur aus einem init() aufgerufen werden!
+
+     Fuer die Entscheidung, in welchem Raum ein Spieler in Abhaengigkeit 
+     von P_PARA landet, ist die Funktion move() zustaendig. Als Magier 
+     muss man sich darum nicht gesondert kuemmern. Das heisst aber auch, 
+     dass beim Anschluss eines Normalweltraumes automatisch alle in dem 
+     Verzeichnis mit gleichem Namen vorhandenen Parallelweltraeume mit 
+     angeschlossen werden.
+
+     Deswegen ist paramove() veraltet und sollte nicht mehr genutzt werden.
+
+SIEHE AUCH:
+     /std/room/para.c, P_PARA, P_NO_PLAYERS, move
+
+----------------------------------------------------------------------------
+Last modified: 05.08.2007, Zesstra
diff --git a/doc/lfun/pick b/doc/lfun/pick
new file mode 100644
index 0000000..59bf51f
--- /dev/null
+++ b/doc/lfun/pick
@@ -0,0 +1,48 @@
+pick()
+
+FUNKTION:
+    public varargs int pick(object o, mixed msg);
+
+DEFINIERT IN:
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+    object o
+        Das Objekt, das aufgehoben werden soll.
+    mixed msg
+        Eine optionale Meldung, die anstelle von P_PICK_MSG oder der
+        Standardmeldung verwendet wird, oder -1, um die Meldung zu
+        unterdruecken.
+
+BESCHREIBUNG:
+    Der Spieler oder NPC nimmt das Objekt auf. Gibt o->move() keinen positiven
+    Wert zurueck, beispielsweise weil das Objekt zu schwer ist oder nicht
+    genommen werden darf, bekommt er eine entsprechende Fehlermeldung.
+
+RUECKGABEWERT:
+    Wenn das Aufnehmen geklappt hat, 1, ansonsten 0.
+
+BEMERKUNG:
+    Diese Funktion ist dann sinnvoll, wenn man den Spieler ein Objekt
+    aufnehmen lassen und sich nicht selbst um die Fehlerbehandlung kuemmern
+    moechte - und da unzaehlige verschiedene Dinge schiefgehen koennen und
+    manche Objekte eigene Fehlermeldungen definieren, eigentlich immer.
+
+    Die Funktion prueft nicht, ob sich das Objekt ueberhaupt in der Reichweite
+    des Spielers/NPC befindet, das muss man ggf. selbst ermitteln.
+
+BEISPIEL:
+    ob = clone_object(WEINGUMMI);
+
+    if (this_player()->pick(ob, ({ "Du nimmst @WENU2 aus dem Regal.",
+                                   "@WER1 nimmt @WENU2 aus dem Regal." })))
+        weingummi--;
+    else
+        ob->remove();
+
+SIEHE AUCH:
+    move(L), P_PICK_MSG, pick_objects(L), P_NOINSERT_MSG, P_NOLEAVE_MSG,
+    P_TOO_MANY_MSG, P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_NOGET
+
+----------------------------------------------------------------------------
+Last modified: Thu Aug 28 22:21:41 2008 by Amynthor
diff --git a/doc/lfun/pick_obj b/doc/lfun/pick_obj
new file mode 100644
index 0000000..a4c7c17
--- /dev/null
+++ b/doc/lfun/pick_obj
@@ -0,0 +1,27 @@
+pick_obj()
+
+FUNKTION
+    int pick_obj(object ob)
+
+DEFINIERT IN:
+
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+
+    ob      Das Objekt, das genommen werden soll.
+
+BESCHREIBUNG:
+
+    Das Lebewesen, in dem diese Funktion aufgerufen werden soll, hebt
+    den angegebenen Gegenstand (ob) auf, falls es ihm moeglich ist.
+
+RUeCKGABEWERT:
+    1, wenn das Objekt genommen wurde oder dies nicht moeglich war. (in diesem
+    Fall wird auch direkt eine Textausgabe ausgegeben)
+    0 sonst, in diesem Fall wird in notify_fail eine passende Ausgabe
+    plaziert
+
+SIEHE AUCH:
+
+    drop_obj(), find_obs(), give_obj(), put_obj(), /std/living/put_and_get.c
diff --git a/doc/lfun/pick_objects b/doc/lfun/pick_objects
new file mode 100644
index 0000000..92c083d
--- /dev/null
+++ b/doc/lfun/pick_objects
@@ -0,0 +1,39 @@
+pick_objects()
+
+FUNKTION:
+    public varargs int pick_objects(string str, int flag, mixed msg);
+
+DEFINIERT IN:
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+    string str
+        Was aufgehoben werden soll.
+    int flag
+        Muss das Objekt irgendwo drinstecken (flag = 1), oder darf es einfach
+        so herumliegen (flag = 0)? Dieses Argument ist hauptsaechlich fuer das
+        Kommando "hole" gedacht, in der Regel braucht man es nicht anzugeben.
+    mixed msg
+        Eine optionale Meldung, die anstelle von P_PICK_MSG oder der
+        Standardmeldung verwendet wird, oder -1, um die Meldung zu
+        unterdruecken.
+
+BESCHREIBUNG:
+    Der Spieler oder NPC nimmt die in <str> benannten Sachen. Kann er ein
+    Objekt nicht nehmen, bekommt er eine entsprechende Fehlermeldung. Wenn
+    keine Objekte auf <str> passen, wird per _notify_fail() eine Meldung
+    gesetzt, aber noch nicht ausgegeben.
+
+RUECKGABEWERT:
+    Wenn <str> irgendwelche vorhandenen Sachen sind, 1, sonst 0.
+
+BEMERKUNG:
+    Wenn die Funktion 1 zurueckgibt, heisst das noch nicht, dass der Spieler
+    etwas genommen hat! Er hat es nur versucht, d.h. auf jeden Fall eine
+    Meldung bekommen. Gibt die Funktion 0 zurueck, hat er noch keine bekommen.
+
+SIEHE AUCH:
+    move(L), pick(L), P_PICK_MSG, find_objects(L), moved_objects(L)
+
+----------------------------------------------------------------------------
+Last modified: Fri Jul 25 10:58:43 2008 by Amynthor
diff --git a/doc/lfun/present_objects b/doc/lfun/present_objects
new file mode 100644
index 0000000..15cf4be
--- /dev/null
+++ b/doc/lfun/present_objects
@@ -0,0 +1,32 @@
+present_objects()
+
+FUNKTION:
+     object *present_objects(string desc);
+
+DEFINIERT IN:
+     /std/container/restrictions.c
+
+ARGUMENTE:
+     desc
+          Umschreibung des gesuchten Objektes oder "alles" oder "alle".
+
+BESCHREIBUNG:
+     Diese Funktion gibt die Objekte im Inneren des Behaelters zurueck, die
+     sich mit desc ansprechen lassen. In dem Fall, dass "alle(s)"
+     angefordert wird, sind das alle sichtbaren Objekte, ansonsten das erste
+     Objekt, das sich mit desc ansprechen laesst.
+     Objekte, die P_INVIS gesetzt haben, zaehlen als nicht ansprechbar, im
+     Gegensatz zu solchen Objekten, die keine P_SHORT haben.
+
+RUeCKGABEWERT:
+     Ein Array von Objekten mit den geforderten Eigenschaften.
+
+     Befindet sich kein Objekt im Behaelter, das sich durch desc ansprechen
+     laesst, so wird ein leeres Array zurueckgegeben.
+
+SIEHE AUCH:
+     locate_objects(), /std/container/restrictions.c
+
+----------------------------------------------------------------------------
+03.03.2013, Zesstra
+
diff --git a/doc/lfun/put b/doc/lfun/put
new file mode 100644
index 0000000..33e7eaf
--- /dev/null
+++ b/doc/lfun/put
@@ -0,0 +1,43 @@
+put()
+
+FUNKTION:
+    public varargs int put(object o, object dest, mixed msg);
+
+DEFINIERT IN:
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+    object o
+        Das Objekt, das irgendwo hingesteckt werden soll.
+    object dest
+        Der Behaelter, in den das Objekt gesteckt werden soll.
+    mixed msg
+        Eine optionale Meldung, die anstelle von P_PUT_MSG oder der
+        Standardmeldung verwendet wird, oder -1, um die Meldung zu
+        unterdruecken.
+
+BESCHREIBUNG:
+    Der Spieler oder NPC steckt das Objekt in einen Behaelter. Gibt o->move()
+    keinen positiven Wert zurueck, beispielsweise weil er das Objekt nicht
+    weggeben darf oder der Behaelter schon voll ist, bekommt er eine
+    entsprechende Fehlermeldung.
+
+RUECKGABEWERT:
+    Wenn das Bewegen geklappt hat, 1, ansonsten 0.
+
+BEMERKUNG:
+    Diese Funktion ist dann sinnvoll, wenn man den Spieler ein Objekt irgendwo
+    hinstecken lassen und sich nicht selbst um die Fehlerbehandlung kuemmern
+    moechte - und da unzaehlige verschiedene Dinge schiefgehen koennen und
+    manche Objekte eigene Fehlermeldungen definieren, eigentlich immer.
+
+    Die Funktion prueft nicht, ob sich das Objekt und der Behaelter ueberhaupt
+    in der Reichweite des Spielers/NPC befinden, das muss man ggf. selbst
+    ermitteln.
+
+SIEHE AUCH:
+    move(L), P_PUT_MSG, put_objects(L), P_NOINSERT_MSG, P_NOLEAVE_MSG,
+    P_TOO_MANY_MSG, P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_NOGET, P_NODROP
+
+----------------------------------------------------------------------------
+Last modified: Thu Aug 28 22:21:58 2008 by Amynthor
diff --git a/doc/lfun/put_obj b/doc/lfun/put_obj
new file mode 100644
index 0000000..1f1b299
--- /dev/null
+++ b/doc/lfun/put_obj
@@ -0,0 +1,30 @@
+put_obj()
+
+FUNKTION
+    int put_obj(object ob, object where)
+
+DEFINIERT IN:
+
+    /std/living/put_and_get.c
+
+ARGUMENTE:
+
+    ob      Das Objekt, das irgendwo hineingelegt werden soll.
+    where   Das (tote) Objekt, in das etwas hineingelegt werden soll.
+
+BESCHREIBUNG:
+
+    Das Lebewesen, in dem diese Funktion aufgerufen werden soll, legt
+    den angegebenen Gegenstand (ob) in das angegebene Zielobjekt (where).
+    Dabei sollte es sich bei where um einen Container/Raum handeln.
+    Ist where ein Lebewesen, verwendet man besser give_obj().
+
+RUeCKGABEWERT:
+    1, wenn das Objekt weggelegt wurde oder dies nicht moeglich war. (in diesem
+    Fall wird auch direkt eine Textausgabe ausgegeben)
+    0 sonst, in diesem Fall wird in notify_fail eine passende Ausgabe
+    plaziert
+
+SIEHE AUCH:
+
+    drop_obj(), find_obs(), give_obj(), pick_obj(), /std/living/put_and_get.c
diff --git a/doc/lfun/query_prevent_shadow b/doc/lfun/query_prevent_shadow
new file mode 100644
index 0000000..80b0c7d
--- /dev/null
+++ b/doc/lfun/query_prevent_shadow
@@ -0,0 +1,34 @@
+query_prevent_shadow(L)
+FUNKTION:
+     varargs int query_prevent_shadow(object shadower)
+
+PARAMETER:
+     object shadower	- da Objekt, das eine Beschattung beantragt
+
+BESCHREIBUNG:
+     Diese Methode kann in Objekten definiert werden, die nicht beschattet
+     werden wollen oder anhand des Objektes shadower entscheiden wollen ob
+     sie beschattet werden wollen.
+
+     Gibt die Funktion 0 zurueck, wird ein Shadow auf das Objekt erlaubt,
+     sonst schlaegt es fehl.
+
+BEISPIEL:
+     // generell keine Beschattung
+     int query_prevent_shadow(object who) {
+      return 1;
+     }
+
+     // Beschattung durch offizielle Objekte erlaubt
+     int query_prevent_shadow(object who) {
+      if(who && !strstr(object_name(who),"/std/player"))
+       return 0;
+      return 1;
+     }
+
+SIEHE AUCH:
+     Rechte:	     query_allow_shadow(M)
+     Generell:	     shadow(E), unshadow(E)
+     Informationen:  query_shadowing(E)
+
+20. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/query_real_name b/doc/lfun/query_real_name
new file mode 100644
index 0000000..d7633de
--- /dev/null
+++ b/doc/lfun/query_real_name
@@ -0,0 +1,15 @@
+query_real_name()
+
+SYNOPSIS:
+	string query_real_name(void)
+
+DESCRIPTION:
+	The result of this_player()->query_real_name() is used as
+	default argument for the efun wizlist().
+
+	If LOG_SHOUT was #defined in the parser at compile time, the
+	efun shout will use query_real_name() to log the shouter's
+	name.
+
+SEE ALSO:
+	shout(E), wizlist(E)
diff --git a/doc/lfun/query_weight_contents b/doc/lfun/query_weight_contents
new file mode 100644
index 0000000..13980f8
--- /dev/null
+++ b/doc/lfun/query_weight_contents
@@ -0,0 +1,20 @@
+query_weight_contents()
+
+FUNKTION:
+     int query_weight_contents()
+
+DEFINIERT IN:
+     /std/container/restrictions.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Gibt das Gewicht des Inhaltes des Behaelters zurueck (ohne
+     Beruecksichtigung von P_WEIGHT_PERCENT!)
+
+RUeCKGABEWERT:
+     Das Gewicht des Behaelterinhaltes.
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:23:32 1996 by Wargon
diff --git a/doc/lfun/reduce_hit_points b/doc/lfun/reduce_hit_points
new file mode 100644
index 0000000..39b3368
--- /dev/null
+++ b/doc/lfun/reduce_hit_points
@@ -0,0 +1,43 @@
+reduce_hit_points()
+FUNKTION:
+    int reduce_hit_points(int damage)
+
+DEFINIERT IN:
+    /std/living/life.c
+
+ARGUMENTE:
+    int damage	- der zugefuegte Schaden
+
+BESCHREIBUNG:
+    Dem Lebewesen werden damage Lebenspunkte abgezogen, aber der
+    Wert wird hinterher nicht kleiner als 1 sein und das Lebewesen
+    wird dadurch nicht sterben.
+
+RUECKGABEWERT:
+    Die verbleibenden Lebenspunkte.
+
+BEISPIELE:
+    write("Ploetzlich schiesst eine scheussliche Kreatur aus der Pfuetze "+
+          "heraus und\nbeisst Dich ins Bein, sie verschwindet so schnell, "+
+          "wie sie gekommen ist.\n");
+    this_player()->reduce_hit_points(50);
+    (Auszug aus /players/boing/friedhof/room/cat1x9)
+
+BEMERKUNGEN:
+    damage kann auch ein negativer Wert sein, dann werden dem Lebewesen
+    diese Lebenspunkte gutgeschrieben und auf die aktuellen Lebenspunkte
+    addiert. Da dies eine Form der Heilung ist, nur nach Ruecksprache mit
+    dem Regionsmagier verwenden.
+
+    Bei Heilstellen sollte eine evtl. Heilung des Spielers mit der eigens
+    dafuer eingerichteten Funktion check_and_update_timed_key realisiert
+    werden.
+
+SIEHE AUCH:
+    Gegenpart:	restore_hit_points()
+    Verwandt:	do_damage(), Defend(), reduce_spell_points()
+    Props:	P_HP
+    Konzept:	heilung
+
+----------------------------------------------------------------------------
+Last modified: Sat Dec 13 01:00:47 1999 by Tilly
diff --git a/doc/lfun/reduce_spell_points b/doc/lfun/reduce_spell_points
new file mode 100644
index 0000000..13c0630
--- /dev/null
+++ b/doc/lfun/reduce_spell_points
@@ -0,0 +1,27 @@
+reduce_spell_points()
+FUNKTION:
+    void reduce_spell_points(int points)
+
+DEFINIERT IN:
+    /std/living/life.c
+
+ARGUMENTE:
+    points: Anzahl der Konzentrationspunkte die abgezogen werden sollen.
+
+BESCHREIBUNG:
+    Dem Lebewesen werden points Konzentrationspunkte abgezogen. Falls
+    das Lebewesen weniger Konzentrationspunkte hat, als abgezogen werden
+    sollen, werden sie auf 0 gesetzt.
+
+BEISPIELE:
+    write("Das boese boese Monster schaut Dich grimmig an und labt sich an "
+         +"Deiner Konzentration.\n");
+    this_player()->reduce_spell_points(50);
+
+SIEHE AUCH:
+    Gegenpart:	restore_spell_points(L)
+    Verwandt:	reduce_hit_points(L), buffer_sp(L)
+    Props:	P_SP
+    Konzept:	heilung
+
+23.Feb.2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/register_modifier b/doc/lfun/register_modifier
new file mode 100644
index 0000000..359a050
--- /dev/null
+++ b/doc/lfun/register_modifier
@@ -0,0 +1,24 @@
+register_modifier()
+FUNKTION:
+     void register_modifier(object modifier)
+
+DEFINIERT IN:
+     /std/living/attributes.c
+

+PARAMETER:

+     modifier	- Objekt mit P_X_ATTR_MOD oder P_M_ATTR_MOD

+
+BESCHREIBUNG:
+     Registriert einen Modifier im Spieler. Wird durch InsertSensitiveObject
+     beziehungsweise beim Anziehen oder Zuecken gerufen.
+     Muss nicht direkt gerufen werden. Bei Veraenderungen von Modifikatoren
+     sollte stattdessen UpdateAttributes gerufen werden.     
+
+SIEHE AUCH:
+     QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),

+     SetAttr(), SetAttribute(), SetRealAttribute(), UpdateAttributes(),

+     deregister_modifier(), P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, 
+     P_ATTRIBUTES_MODIFIER,P_X_ATTR_MOD, P_M_ATTR_MOD, 
+     /std/living/attributes.c

+
+13.Jun.2004, Muadib
\ No newline at end of file
diff --git a/doc/lfun/remove b/doc/lfun/remove
new file mode 100644
index 0000000..21fc59a
--- /dev/null
+++ b/doc/lfun/remove
@@ -0,0 +1,34 @@
+remove()
+
+FUNKTION:
+     varargs int remove(int silent);
+
+DEFINIERT IN:
+     /std/thing/moving.c
+     /std/living/moving.c
+     /std/room/moving.c
+
+ARGUMENTE:
+     silent
+          Falls ungleich 0, so werden beim Zerstoeren keine Meldungen
+          ausgegeben.
+
+BESCHREIBUNG:
+     Beim Aufruf dieser Funktion entfernt sich das Objekt selbst. Durch
+     Ueberladen dieser Funktion kann man diesen Vorgang noch durch die
+     Ausgabe von Meldungen kommentieren, oder irgendwelche Daten
+     abspeichern, oder das Zerstoeren ganz verhindern (auf diesem Weg... Mit
+     destruct() kann das Objekt immer noch direkt zerstoert werden!)
+
+RUeCKGABEWERT:
+     1, wenn sich das Objekt erfolgreich selbst zerstoert hat, sonst 0.
+
+BEMERKUNGEN:
+     Nach einem erfolgreichen ::remove() gelten die selben Einschraenkungen
+     wie nach einem destruct()!
+
+SIEHE AUCH:
+     destruct()
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:23:40 1996 by Wargon
diff --git a/doc/lfun/remove_multiple b/doc/lfun/remove_multiple
new file mode 100644
index 0000000..6340fd1
--- /dev/null
+++ b/doc/lfun/remove_multiple
@@ -0,0 +1,58 @@
+remove_multiple()
+FUNKTION:
+     public varargs int remove_multiple(int limit, mixed fun);
+
+DEFINIERT IN:
+     /std/container/items.c
+
+ARGUMENTE:
+     limit: Anzahl gleicher Objekte, die verbleiben sollen.
+     fun:   Funktionsname (string) oder Closure.
+
+BESCHREIBUNG:
+     Wird diese Funktion aufgerufen, werden alle gleichen Objekte
+     (standardmaessig definiert als gleicher Ladename, gleiche
+      Kurzbeschreibung und gleiche Langbeschreibung), deren Anzahl <limit>
+     ueberschreitet, entfernt.
+     
+     Ausnahmen: Lebewesen und per AddItem() hinzugefuegte Objekte.
+
+     Mit dem Argument <fun> lassen sich die Kriterien fuer die "Gleichheit"
+     aendern.
+     Ist <fun> ein Funktionsname, wird diese Funktion an allen in Frage
+     kommenenden Objekten im Container gerufen.
+     Ist <fun> eine Closure, werden sie fuer jedes in Frage kommende Objekt
+     einmal gerufen und ihr das Objekt uebergeben.
+     Als 'gleich' werden alle Objekte betrachtet, fuer die <fun> den gleichen
+     Wert zurueckliefert.
+     Objekte, fuer die <fun> 0 zurueckliefert, werden ignoriert.
+
+BEMERKUNGEN:
+     1) Raeume rufen remove_multiple(3) standardmaessig im reset(), jedoch
+        nur, wenn kein Spieler anwesend ist und mehr als 10 Objekte im Raum
+        sind.
+     2) remove_multipe(0) entfernt alle entfernbaren Objekte (s. Ausnahmen).
+
+BEISPIELE:
+     Ein Container enthaelt 5 Fackeln. Im reset() ruft man nun
+     remove_multiple(3) auf. Anschliessend sind noch 3 Fackeln uebrig.
+     
+     Alle verbleibenden Objekte im Container sollen unterschiedliche Namen
+     haben:
+     remove_multiple(1, "name");
+
+     Ein Container soll nur ein Objekt behalten, welches groesser als 70 cm
+     ist:
+     int groessen_filter(object ob) {
+       if (ob->QueryProp(P_SIZE > 70)) return 1;
+       return 0; // damit das Objekt ignoriert wird.
+       // Alternativ koennte man statt 0 auch object_name(ob) zurueckliefern,
+       // dann sind diese Objekte alle unterschiedlich.
+     }
+     ...
+     remove_multiple(1, #'groessen_filter);
+
+SIEHE AUCH:
+     reset()
+
+31.01.2009, Zesstra
diff --git a/doc/lfun/reset b/doc/lfun/reset
new file mode 100644
index 0000000..07a22ee
--- /dev/null
+++ b/doc/lfun/reset
@@ -0,0 +1,88 @@
+reset()
+FUNKTION:
+     void reset();
+     protected void reset();
+
+BESCHREIBUNG:
+     reset() wird vom GameDriver in jedem Objekt aufgerufen, um dem Objekt
+     die Gelegenheit zu geben, sich wieder in einen definierten Zustand zu
+     versetzen: Raeume und Monster erzeugen per AddItem() eingefuegte und
+     zwischenzeitlich entfernte Objekte neu, Tueren schliessen sich ...
+
+     Solange das Objekt "benutzt" wird, wird reset() regelmaessig alle
+     45 Minuten (+/-15 Minuten) mit einer Aufloesung von 2s aufgerufen
+     (d.h. der Driver prueft und ruft nur alle 2 Sekunden reset() auf
+     allen Objekten).
+
+     Wird eine Objekt nicht mehr "benutzt", d.h. wird an einem Objekt nicht
+     von aussen (durch call_other etc.) _nach_ einem reset() eine Methode
+     bzw. LFun gerufen, so bekommt dieses Objekt keinen weiteren reset().
+
+     Ein Funktionsaufruf am Objekt schaltet den reset() wieder ein.
+     Bei einem Objekt in einem Container waere das zB das Benutzen des
+     Containers (Hineinlegen/Herausnehmen/Hineinsehen). Das kann
+     sehr lange dauern.
+
+     Die Abschaltung kann man verhindern, indem man im reset() per call_out()
+     eine Funktion im Objekt ruft. Dies aber bitte _nur_ machen, wenn das
+     Objekt _unbedingt_ auf einen staendigen Reset angewiesen ist, auch wenn
+     es nicht "benutzt" wird.
+
+     Aendern laesst sich die Zeit zwischen den Aufrufen von reset() mit
+     set_next_reset(). Die Aufloesung von 2s kann man nicht aendern.
+
+BEMERKUNGEN:
+     - man kann reset() nutzen, um Ereignisse auszuloesen:
+       - es ist billiger als lange call_out()
+       - siehe Warnung bezueglich Abschalten des reset
+     - man kann reset() als protected oder static definieren, wenn man nicht
+       moechte, dass die Funktion von aussen gerufen werden kann. Dies
+       verhindert Einflussnahme von aussen, kann aber auch Debugmassnahmen
+       erschweren. Es ist aber dennoch fuer einige Objekte sinnvoll.
+     - der Driver ruft reset() unabhaengig von zusaetzlichen, "manuellen"
+       Rufen von reset()
+       - keine Rufe von reset() mit call_out() aus reset() (Callout-Ketten-
+         bildung droht), fuer solche Faelle ist set_next_reset(E) da!
+     - bei Blueprints sollte der reset() in der Regel abgeschaltet werden,
+       sofern er nicht auf wichtige Aufgaben in der BP zu tun hat:
+       protected void create() {
+         if(!clonep(ME)) {
+             set_next_reset(-1);
+             return;
+         }
+         ::create();
+         ...
+       }
+
+BEISPIELE:
+     // ein NPC, der bei jedem reset() schaut, ob um ihn herum bessere
+     // Ausruestung liegt als die, die er selbst gerade traegt:
+
+     ...
+     void reset() {
+       ::reset();
+
+       if(clonep(this_object()) && environment()) {
+         object o;
+         o=first_inventory(environment());
+         while(o) {
+             look_for_good_weapons_and_use_them_if_better(o);
+             o=next_inventory(o);
+         }
+       }
+     }
+
+     // ein reset fuer einen Gegenstand, der vielleicht in
+     // in einem Container landet und dann weiter einen reset
+     // bekommen muss/soll
+
+     void reset() {
+       // irgend ein Code hier
+       call_other(this_object(), "???"); // einfach nur was aufrufen
+     }
+
+SIEHE AUCH:
+     clean_up(), set_next_reset(E), query_next_reset(E)
+     memory
+
+letzte Aenderung: 2009-01-14 Rumata
diff --git a/doc/lfun/restore_hit_points b/doc/lfun/restore_hit_points
new file mode 100644
index 0000000..7ae3457
--- /dev/null
+++ b/doc/lfun/restore_hit_points
@@ -0,0 +1,33 @@
+restore_hit_points()
+FUNKTION:
+    int restore_hit_points(int heal)
+
+ARGUMENTE:
+    int heal	- der zu heilende Betrag
+
+BESCHREIBUNG:
+    Dem Lebewesen werden heal Lebenspunkte aufgeschlagen. Die HP
+    steigen nicht ueber P_MAX_HP.
+
+RUECKGABEWERT:
+    Die verbleibenden Lebenspunkte.
+
+BEISPIELE:
+    write("Ploetzlich schiesst eine scheussliche Kreatur aus der Pfuetze "+
+          "heraus und\nschleimt dich heilend voll, sie verschwindet so, "+
+          "wie sie gekommen ist.\n");
+    this_player()->restore_hit_points(50);
+
+BEMERKUNGEN:
+    Bei Heilstellen sollte eine evtl. Heilung des Spielers mit der eigens
+    dafuer eingerichteten Funktion check_and_update_timed_key realisiert
+    werden.
+    Ansonsten bitte buffer_hp() benutzen und die Konzeptseite lesen!
+
+SIEHE AUCH:
+    Gegenpart:	reduce_hit_points()
+    Verwandt:	buffer_hp(), heal_self(), restore_spell_points()
+    Props:	P_HP
+    Konzept:	heilung
+
+23.Feb.2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/restore_spell_points b/doc/lfun/restore_spell_points
new file mode 100644
index 0000000..87c2a6c
--- /dev/null
+++ b/doc/lfun/restore_spell_points
@@ -0,0 +1,37 @@
+restore_spell_points()
+FUNKTION:
+    void restore_spell_points(int points)
+
+ARGUMENTE:
+    points: Anzahl der Konzentrationspunkte die gutgeschrieben werden sollen.
+
+BESCHREIBUNG:
+    Dem Lebewesen werden points Konzentrationspunkte gutgeschrieben. Falls
+    Punkte abgezogen werden sollen und das Lebewesen nicht ueber <points>
+    Konzentrationspunkte verfuegt, werden sie auf 0 gesetzt.
+
+RUECKGABEWERT:
+    Keiner
+
+BEISPIELE:
+    write("Das boese boese Monster schaut Dich suess an und gibt dir mehr "
+         +"Konzentration.\n");
+    this_player()->restore_spell_points(50);
+
+BEMERKUNGEN:
+    Da das Benutzen der Funktion eine Heilung bedeutet, sollte man bei
+    Verwendung auf jeden Fall Ruecksprache mit seinem RM nehmen, bzw
+    die Heilstelle bei der Heilungsbalance genehmigen lassen.
+
+    Bei Heilstellen sollte eine evtl. Heilung des Spielers mit der eigens
+    dafuer eingerichteten Funktion check_and_update_timed_key realisiert
+    werden.
+    Ansonsten bitte buffer_sp() benutzen und die Konzeptseite lesen!
+
+SIEHE AUCH:
+    Gegenpart:	reduce_spell_points(L)
+    Verwandt:	buffer_sp(L), restore_hit_points(L)
+    Props:	P_SP
+    Konzept:	heilung
+
+23.Feb.2004 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/save_me b/doc/lfun/save_me
new file mode 100644
index 0000000..d7f9e0a
--- /dev/null
+++ b/doc/lfun/save_me
@@ -0,0 +1,21 @@
+save_me(L)
+FUNKTION:
+    void save_me(mixed value_items)
+
+DEFINIERT IN:
+    /std/player/base.c
+
+ARGUMENTE:
+    value_items
+       Ungleich 0, wenn Wert der Gegenstaende berechnet werden soll.
+
+BESCHREIBUNG:
+    Speichert einen Spieler, seine Autoloader, bestimmt sein GuildRating
+    und berechnet/speichert auf Anforderung den Wert der getragenen
+    Gegenstaende.
+
+SIEHE AUCH:
+    Props:       P_CARRIED_VALUE, P_AUTOLOADOBJ, P_LAST_LOGOUT
+    Kommandos:   save, ende
+
+1.September 2008 Gloinson
diff --git a/doc/lfun/second_life b/doc/lfun/second_life
new file mode 100644
index 0000000..b5abfd5
--- /dev/null
+++ b/doc/lfun/second_life
@@ -0,0 +1,30 @@
+second_life()
+
+FUNKTION:
+	varargs int second_life(object obj);
+
+DEFINIERT IN:
+	/std/player/life.c
+
+ARGUMENTE:
+	obj
+	  Leiche des Lebewesens.
+
+BESCHREIBUNG:
+        Diese Funktion wird im die() des Lebewesens aufgerufen, wenn sicher
+        ist, dass es stirbt. Ueblicherweise ist diese Funktion nur im Spieler
+        definiert und regelt EP-Verlust und dergleichen. Ein Shadow wuerde
+        diese Funktion ueberlagern, um zu verhindern, dass ein Spieler stirbt.
+
+RUeCKGABEWERT:
+        1, wenn das Lebewesen nicht stirbt, sonst 0
+
+BEMERKUNG:
+        Bei NPCs sollte man direkt die() ueberschreiben, wenn man nicht
+        moechte, dass sie sterben.
+
+SIEHE AUCH:
+        die()
+
+----------------------------------------------------------------------------
+Last modified: 2015-Jan-19, Arathorn 
diff --git a/doc/lfun/sell_obj b/doc/lfun/sell_obj
new file mode 100644
index 0000000..8c362f1
--- /dev/null
+++ b/doc/lfun/sell_obj
@@ -0,0 +1,52 @@
+sell_obj()

+

+Funktion:

+    static string sell_obj(object ob, int short)

+

+Definiert in:

+    /std/room/shop

+

+Argumente:

+    ob:

+      Das anzukaufende Objekt

+    short:

+      Gibt an, ob der Verkaeufer nur ein Objekt (0) oder mehrere (1) 

+      verkauft. (Verkaufe alles etc.)

+

+Beschreibung:

+    Ermittelt ob der Laden bereit ist, <ob> anzukaufen.

+

+Rueckgabewert:

+    Meldung die ausgegeben wird, wenn ein Objekt abgelehnt wird oder 0.

+

+Bemerkung:

+    Man sollte im normalfall _niemals_ einfach 0 zurueckgeben, sondern das 

+    geerbte sell_obj() aus /std/room/shop, damit beispielsweise P_NOBUY 

+    beachtet wird.

+

+Beispiel:

+    Ein Schmied, der nur Waffen ankauft:

+    

+    protected void create()

+    {

+      ...

+    }

+    

+    static string sell_obj(object ob, int short)

+    {

+      if(!ob->QueryProp(P_WEAPON_TYPE))

+      {

+        return "Ich bin nur an Waffen interessiert.";

+      }

+      return ::sell_obj(ob,short);

+    }

+

+Siehe auch:

+    Funktionen:

+      AddFixedObject(), RemoveFixedObject(), SetStorageRoom(), 

+      QueryStorageRoom(), QueryBuyValue(), QueryBuyFact(), buy_obj()

+    Properties:

+      P_KEEPER, P_MIN_STOCK, P_STORE_CONSUME

+

+------------------------------------------------------------------------------

+Letzte Aenderung: 21.05.2014, Bugfix

diff --git a/doc/lfun/set_object_next_reset b/doc/lfun/set_object_next_reset
new file mode 100644
index 0000000..68dfa2f
--- /dev/null
+++ b/doc/lfun/set_object_next_reset
@@ -0,0 +1,25 @@
+set_object_next_reset()
+
+FUNKTION:
+	int set_object_next_reset(object ob, int zeit );
+
+DEFINIERT IN: /secure/debug.c
+
+ARGUMENTE:
+	ob: Das Objekt, dess Reset gesetzt werden soll.
+  zeit: Zeit bis zum naechsten Reset von ob.
+
+FUNKTION:
+  Die Funktion ruft letztendlich set_next_reset() in <ob> auf und setzt daher
+  dessen Resetzeit auf einen neuen Wert. Dies ist zu Debugzwecken gedacht.
+
+  Die Benutzung ist nur fuer EM+ moeglich.
+
+RUECKGABEWERT:
+	Gibt den Rueckgabewert des set_next_reset()-Aufrufs in <ob> zurueck.
+
+SIEHE AUCH:
+	set_next_reset(E)
+
+10.10.2007, Zesstra
+
diff --git a/doc/lfun/shoot_dam b/doc/lfun/shoot_dam
new file mode 100644
index 0000000..5d82d02
--- /dev/null
+++ b/doc/lfun/shoot_dam
@@ -0,0 +1,40 @@
+shoot_dam()
+
+FUNKTION:
+    static int shoot_dam(mapping shoot)
+
+DEFINIERT IN:
+    /std/ranged_weapon.c
+
+ARGUMENTE:
+    mapping shoot - Schussdaten
+
+BESCHREIBUNG:
+    Erhaelt von /std/ranged_weapon::cmd_shoot() die Schussdaten und berechnet
+    den Schaden der Waffe, basierend auf den P_SHOOTING_WC von Waffe und
+    Munition sowie der Geschicklichkeit des Schuetzen. HitFuncs der Munition
+    und Skills werden hier ebenfalls beruecksichtigt.
+
+RUECKGABEWERT:
+    Schaden. Ebenfalls in 'shoot' unter SI_SKILLDAMAGE aktualisiert.
+
+BEMERKUNGEN:
+    'shoot' enthaelt normalerweise folgende Eintraege:
+    * Key P_WEAPON:       die Schusswaffe
+    * Key P_WEAPON_TYPE:  P_AMMUNITION, also die Munitions-ID
+    * Key P_STRETCH_TIME: P_STRETCH_TIME der Waffe
+    * Key P_WC:           P_SHOOTING_WC der Waffe
+    * Key P_SHOOTING_WC:  P_SHOOTING_WC der Munition
+    * Key P_AMMUNITION:   Munitionsobjekt (eventuell Unit)
+    * Key SI_ENEMY:       gueltigen Gegner
+    * Key SI_SKILLDAMAGE_TYPE:  Schaden (aus P_DAM_TYPE der Munition)
+    * Key SI_SKILLDAMAGE_MSG/2: Munitionsname
+
+SIEHE AUCH:
+    Generell:  P_AMMUNITION, P_SHOOTING_WC, P_STRETCH_TIME
+    Methoden:  FindRangedTarget(L), cmd_shoot(L)
+    Skills:    UseSkill(L), SkillResTransfer(L)
+    Attribute: QueryAttribute
+    Sonstiges: fernwaffen, HitFunc
+
+28.Jul 2014 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/short b/doc/lfun/short
new file mode 100644
index 0000000..a9734c2
--- /dev/null
+++ b/doc/lfun/short
@@ -0,0 +1,31 @@
+short()
+FUNKTION:
+     public varargs string short();
+
+DEFINIERT IN:
+     /std/thing/description.c
+
+ARGUMENTE:
+     keine
+
+BESCHREIBUNG:
+     Der Inhalt der Property P_SHORT wird ausgewertet, mit ".\n"
+     abgeschlossen und zurueckgegeben.
+
+RUeCKGABEWERT:
+     Die Kurzbeschreibung als String oder 0, falls das Objekt unsichtbar
+     ist.
+
+BEMERKUNGEN:
+     Durch Ueberladen von short() lassen sich noch weitere Eigenschaften des
+     Objektes zeigen. Fackeln zeigen zum Beispiel an, ob sie brennen,
+     Ruestungen, ob sie angezogen sind und Waffen, ob sie gezueckt sind.
+
+SIEHE AUCH:
+     Aehnliches:	long()
+     Properties:	P_SHORT, P_INVIS
+     Sonstiges:		make_invlist()
+
+----------------------------------------------------------------------------
+20.01.2015, Zesstra
+
diff --git a/doc/lfun/show_notify b/doc/lfun/show_notify
new file mode 100644
index 0000000..ae09adc
--- /dev/null
+++ b/doc/lfun/show_notify
@@ -0,0 +1,44 @@
+give_notify()
+FUNKTION:
+     void show_notify(object obj)
+
+DEFINIERT IN:
+     /std/living/put_and_get.c
+
+ARGUMENTE:
+     obj - dem Lebewesen gezeigtes Objekt
+
+RUeCKGABEWERT:
+     keiner
+
+BESCHREIBUNG:
+     Diese Funktion wird automatisch immer dann aufgerufen, wenn einem
+     Lebewesen (welches kein Spielercharakter ist) ein Objekt gezeigt wird.
+     Dies funktioniert nur dann, wenn der Standardbefehl der Spielershell
+     verwendet wird ("zeige <name> <gegenstand>"). Selbstgebautes "zeige"
+     funktioniert nicht.
+
+BEISPIEL:
+     Oftmals will man in Quests erreichen, dass einem NPC ein bestimmtes
+     Item als Beweis der Erfuellung einer bestimmten Aufgabe vorgezeigt
+     wird. Folgendermassen kann dies realisiert werden:
+
+     void quest_ok(object obj) { ...
+       // z.B. Vernichtung des Questobjektes und Questtexte
+       // Questbelohnung und Questanerkennung, etc.
+     }
+
+     void show_notify(object obj) {
+       if(obj->id("\nquestitem")) // Ist das das geforderte Questobjekt?
+         quest_ok(obj);
+     }
+
+BEMERKUNGEN:
+     Da es nur um das Vorzeigen von Gegenstaenden geht, die nicht den 
+     Besitzer wechseln, sind Mechanismen wie P_REJECT in diesem Fall 
+     nicht erforderlich.
+
+SIEHE AUCH:
+     give_notify(), /std/npc/put_and_get.c, /std/living/put_and_get.c
+
+22. Oktober 2013 Arathorn
diff --git a/doc/lfun/spellbook/AddSpell b/doc/lfun/spellbook/AddSpell
new file mode 100644
index 0000000..78686d1
--- /dev/null
+++ b/doc/lfun/spellbook/AddSpell
@@ -0,0 +1,54 @@
+AddSpell()
+FUNKTION:
+    varargs int AddSpell(string verb, int kosten, mixed ski) 
+
+DEFINIERT IN:
+    /std/spellbook.c
+
+ARGUMENTE:
+    string verb     Name des Spells
+    int kosten      normale Kosten (Grundkosten)
+    mixed ski       Skillmapping mit allen Eintraegen zum Spell
+
+BESCHREIBUNG:
+    Addiert einen Eintrag fuer den Spell im Spellbook. Wenn dieser
+    Spell direkt vom Spieler (SI_SPELLBOOK) oder (normalerweise)
+    ueber ein Gildenobjekt aufgerufen wird, wird das Mapping auf
+    die Skillinformationen aus Spieler und Gilde addiert und
+    beeinflusst damit den Aufruf der letztlichen Spellfunktion.
+
+BEMERKUNGEN:
+    Siehe das Verhalten von QuerySpell (gilde) zum Zusammenfuegen
+    der AddSpell-Informationen aus Gilde und Spellbook. Relevant
+    zB fuer Lernrestriktionen.
+
+BEISPIEL:
+    AddSpell("kampfschrei", 30,
+         ([SI_SKILLRESTR_LEARN:([P_LEVEL:13]),
+           SI_MAGIC_TYPE: ({MT_PSYCHO}),
+           SI_SPELL:      ([
+             SP_NAME: "Kampfschrei",
+             SP_SHOW_DAMAGE:
+               ({({ -1, "Dir geschieht jedoch nichts.",
+                   "@WEM1 geschieht jedoch nichts.",
+                   "@WEM1 geschieht jedoch nichts." }),
+                 ({  0, "Du kannst darueber aber nur lachen.",
+                   "@WER1 kann darueber aber nur lachen.",
+                   "@WER1 kann darueber aber nur lachen." }),
+                 ({ 10, "Dir droehnen die Ohren.",
+                   "@WEM1 droehnen die Ohren.",
+                   "@WEM1 droehnen die Ohren." })
+               })])
+         ]));
+
+SIEHE AUCH:
+    Spellbook Lernen: Learn, SpellSuccess, Erfolg, Misserfolg
+    * Verwalten:      QuerySpell
+    * Angriff:        TryAttackSpell, TryDefaultAttackSpell,
+                      TryGlobalAttackSpell
+    * Properties:     P_GLOBAL_SKILLPROPS, P_SB_SPELLS
+    Skills Lernen:    LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:        UseSpell, UseSkill
+    * sonstig:        spruchermuedung, skill_info_liste
+
+5. Okt 2011 Gloinson
diff --git a/doc/lfun/spellbook/Erfolg b/doc/lfun/spellbook/Erfolg
new file mode 100644
index 0000000..625bf4f
--- /dev/null
+++ b/doc/lfun/spellbook/Erfolg
@@ -0,0 +1,28 @@
+Erfolg()
+FUNKTION:
+    void Erfolg(object caster, string spell, mapping sinfo)
+
+DEFINIERT IN:
+    /std/spellbook.c
+
+ARGUMENTE:
+    object caster    Spell sprechender Spieler
+    string spell     Spellname
+    mapping sinfo    Spell-Info-Mapping mit allen Informationen
+
+BESCHREIBUNG:
+    Wird bei Erfolg eines Spells gerufen. Ruft SpellInform() am
+    Environment.
+
+SIEHE AUCH:
+    Sonstiges:        SpellInform
+    Spellbook Lernen: Learn, SpellSuccess, Misserfolg
+    * Verwalten:      AddSpell, QuerySpell
+    * Angriff:        TryAttackSpell, TryDefaultAttackSpell,
+                      TryGlobalAttackSpell
+    * Properties:     P_GLOBAL_SKILLPROPS, P_SB_SPELLS
+    Skills Lernen:    LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:        UseSpell, UseSkill
+    * sonstig:        spruchermuedung, skill_info_liste
+
+5. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/spellbook/Learn b/doc/lfun/spellbook/Learn
new file mode 100644
index 0000000..4e2431a
--- /dev/null
+++ b/doc/lfun/spellbook/Learn
@@ -0,0 +1,33 @@
+Learn()
+FUNKTION:
+    void Learn(object caster, string spell, mapping sinfo)
+
+DEFINIERT IN:
+    /std/spellbook.c
+
+ARGUMENTE:
+    object caster   Derjenige, der den Spruch spricht.
+    string spell    Der gesprochene Spell
+    mapping sinfo   Mapping mit allen moeglichen Informationen zum Spell
+
+BESCHREIBUNG:
+    Diese Funktion wird von der Funktion "Misserfolg" aus dem 
+    Spellbook aufgerufen. Hier lernt der Spieler den Spruch, der 
+    nicht geglueckt ist.
+
+BEMERKUNGEN:
+    Kann auch ueberschrieben werden, wenn man komplexe Lern-Aenderungen
+    vornehmen will. Andere Attribute sind ueber SI_LEARN_ATTRIBUTE
+    setzbar.
+
+SIEHE AUCH:
+    Spellbook Lernen: SpellSuccess, Erfolg, Misserfolg
+    * Verwalten:      AddSpell, QuerySpell
+    * Angriff:        TryAttackSpell, TryDefaultAttackSpell,
+                      TryGlobalAttackSpell
+    * Properties:     P_GLOBAL_SKILLPROPS, P_SB_SPELLS
+    Skills Lernen:    LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:        UseSpell, UseSkill
+    * sonstig:        spruchermuedung, skill_info_liste
+
+5. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/spellbook/Misserfolg b/doc/lfun/spellbook/Misserfolg
new file mode 100644
index 0000000..7ad530b
--- /dev/null
+++ b/doc/lfun/spellbook/Misserfolg
@@ -0,0 +1,53 @@
+Misserfolg()
+FUNKTION:
+    void Misserfolg(object caster, string spell, mapping sinfo) 
+
+DEFINIERT IN:
+    /std/spellbook.c
+
+ARGUMENTE:
+    object caster    Spell sprechender Spieler
+    string spell     Spellname
+    mapping sinfo    Spell-Info-Mapping mit allen Informationen
+
+BESCHREIBUNG:
+    Wird bei Misserfolg eines Spells im Spellbook aufgerufen und
+    ruft die Lernfunktion Learn() nach einer Fehlermeldung.
+    
+    Kann ueberschrieben werden, um die Meldungen anzupassen.
+
+BEISPIEL:
+    // Misserfolge im Klerus mit angepassten Meldungen
+    void Misserfolg(object caster, string spell, mapping sinfo) {
+      switch(spell) {
+        case "begrabe":
+          tell_object(caster, BS(
+            "Du begraebst Deine Hoffnungen, dass Du diese Anrufung jemals "
+            "perfekt beherrschen wirst."));
+          tell_room(environment(caster),
+            caster->Name(WER)+" tritt die Leiche lustlos.\n", ({caster}));
+          break;
+        case "blitz":
+        [...]
+      }
+        
+      int old_abil = sinfo[SI_SKILLABILITY];
+      Learn(caster, spell, sinfo);
+      int new_abil = caster->QuerySkillAbility(spell);
+      if (old_abil < new_abil)
+        tell_object(caster, "Die Goetter schenken Dir eine Erleuchtung.\n");
+      else
+        tell_object(caster, "Leider lernst Du nicht aus Deinem Fehler.\n"); 
+    }
+
+SIEHE AUCH:
+    Spellbook Lernen: Learn, SpellSuccess, Erfolg
+    * Verwalten:      AddSpell, QuerySpell
+    * Angriff:        TryAttackSpell, TryDefaultAttackSpell,
+                      TryGlobalAttackSpell
+    * Properties:     P_GLOBAL_SKILLPROPS, P_SB_SPELLS
+    Skills Lernen:    LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:        UseSpell, UseSkill
+    * sonstig:        spruchermuedung, skill_info_liste
+
+5. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/spellbook/QuerySpell b/doc/lfun/spellbook/QuerySpell
new file mode 100644
index 0000000..0cadec2
--- /dev/null
+++ b/doc/lfun/spellbook/QuerySpell
@@ -0,0 +1,26 @@
+QuerySpell()
+FUNKTION:
+    mapping QuerySpell(string spell)
+
+DEFINIERT IN:
+    /std/spellbook.c
+
+ARGUMENTE:
+    string spell    Name des Spells
+
+BESCHREIBUNG:
+    Gibt das Spellmapping aus P_SB_SPELLS fuer diesen Spell zurueck.
+    
+    Hier enthaelt QuerySpell nur die Spellbook-Informationen.
+
+SIEHE AUCH:
+    Spellbook Lernen: Learn, SpellSuccess, Erfolg, Misserfolg
+    * Verwalten:      AddSpell
+    * Angriff:        TryAttackSpell, TryDefaultAttackSpell,
+                      TryGlobalAttackSpell
+    * Properties:     P_GLOBAL_SKILLPROPS, P_SB_SPELLS
+    Skills Lernen:    LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:        UseSpell, UseSkill
+    * sonstig:        spruchermuedung, skill_info_liste
+
+5. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/spellbook/SpellSuccess b/doc/lfun/spellbook/SpellSuccess
new file mode 100644
index 0000000..ec44884
--- /dev/null
+++ b/doc/lfun/spellbook/SpellSuccess
@@ -0,0 +1,31 @@
+SpellSuccess()
+FUNKTION:
+    int SpellSuccess(object caster, mapping sinfo)
+
+DEFINIERT IN:
+    /std/spellbook.c
+
+ARGUMENTE:
+    object caster    Spell sprechender Spieler
+    mapping sinfo    Spell-Info-Mapping mit allen Informationen
+
+BESCHREIBUNG:
+    Berechnet den Erfolg der Anwendung eines Spells aus seiner
+    SI_SKILLABILITY und dem Skill SK_CASTING im Spieler. Laesst
+    den Spieler bei besonders gutem Gelingen SK_CASTING lernen.
+
+BEMERKUNGEN:
+    SK_CASTING muss fuer die SK_CASTING-Boni beherrscht werden.
+    Das ist zB im Klerus ab bestimmtem Level der Fall.
+
+SIEHE AUCH:
+    Spellbook Lernen: Learn, Erfolg, Misserfolg
+    * Verwalten:      AddSpell, QuerySpell
+    * Angriff:        TryAttackSpell, TryDefaultAttackSpell,
+                      TryGlobalAttackSpell
+    * Properties:     P_GLOBAL_SKILLPROPS, P_SB_SPELLS
+    Skills Lernen:    LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:        UseSpell, UseSkill
+    * sonstig:        spruchermuedung, skill_info_liste
+
+5. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/lfun/spellbook/TryAttackSpell b/doc/lfun/spellbook/TryAttackSpell
new file mode 100644
index 0000000..76d75b8
--- /dev/null
+++ b/doc/lfun/spellbook/TryAttackSpell
@@ -0,0 +1,48 @@
+** gilden-doku
+ o TryAttackSpell(opfer,schaden,typen,is_spell,caster,info)
+   Versucht den Angriffs-Spruch auf den Gegner anzuwenden. Die
+   mittleren 4 Werte sind die, die auch bei Defend uebergeben werden.
+   Dabei wird die Abwehrfaehigkeit des Gegners gegen Magie und das
+   Skill-Attribut SA_DAMAGE automatisch beruecksichtigt. 
+
+FUNKTION:
+
+int TryAttackSpell(object victim, int damage, mixed dtypes,
+                   mixed is_spell, object caster, mapping sinfo)
+
+
+ARGUMENTE:
+
+        victim   : Das arme Opfer.
+        damage   : Der Schaden.
+        dtypes   : Die Schadensarten.
+	      is_spell : Ist es ein Spell? Werden noch Spezielle Parameter 
+	           uebergeben (als mapping) ?
+        caster   : Derjenige, der den Spruch spricht.
+        sinfo    : Mapping mit allen moeglichen Informationen zum Spell
+
+
+BESCHREIBUNG:
+
+	Diese Funktion wird vom Spellbook aufgerufen, wenn der Spieler
+	einen Angriffsspell gemacht hat und damit Schaden anrichten will.
+
+
+RUECKGABEWERT:
+
+	Der Wert, der vom Defend() des Gegners zurueckgeliefert wird.
+
+
+BEMERKUNGEN:
+
+	Zu erst wird ueberprueft, ob das Ziel ueberhaupt angreifbar ist. Dies
+	verhindert das ueben von Spells an unangreifbaren NPCs.
+	Als naechstes wird die Faehigkeit, Spells abzuwehren ueberprueft.
+	Falls beide Abfragen ok sind, wird Defend aufgerufen.
+
+
+Siehe auch:
+
+TryDefaultAttackSpell (to be written)
+------------------------------------------------------------------------------
+07.10.2007, Zesstra
diff --git a/doc/lfun/trigger_sensitive_attack b/doc/lfun/trigger_sensitive_attack
new file mode 100644
index 0000000..129b38c
--- /dev/null
+++ b/doc/lfun/trigger_sensitive_attack
@@ -0,0 +1,75 @@
+trigger_sensitive_attack()
+
+FUNKTION:
+     varargs void trigger_sensitive_attack(object enemy, string key, int
+     dam, mixed spell, mixed *options);
+
+DEFINIERT IN:
+     eigenen sensitiven Objekten, wird aufgerufen von
+     /std/living/inventory.c
+
+ARGUMENTE:
+     enemy
+          Der Gegner, der die Aktion ausgeloest hat.
+     key
+          Der ausloesende Schadenstyp.
+     dam
+          Der angerichtete Schaden.
+     spell
+          Wie bei Defend().
+     options
+          Array mit den in P_SENSITIVE angegebenen Optionen fuer diese
+          Aktion.
+
+BESCHREIBUNG:
+     Wenn der bei einem Angriff zugefuegte Schaden den in P_SENSITIVE
+     angegebenen Grenzwert uebersteigt sowie der als Schluessel angegebene
+     Schadenstyp in den Schaedenstypen des Angriffes vorkommt, wird diese
+     Funktion aufgerufen und kann entsprechend reagieren.
+
+RUeCKGABEWERT:
+     keiner
+
+BEISPIELE:
+     Eine Fackel, die sich bei Feuerattacken selbst entzuendet und bei
+     Wasserattacken verloescht, koennte man wie folgt implementieren:
+
+     inherit "/std/lightsource.c"
+
+     #include <properties.h>
+     #include <sensitive.h>
+     #include <combat.h>
+
+     create()
+     {
+       ::create();
+
+       SetProp(...); // die ueblichen Eigenschaften definieren
+
+       SetProp(P_SENSITIVE,
+           //  Ereignis          Key       Grenze (keine Optionen)
+         ({ ({ SENSITIVE_ATTACK, DT_FIRE,  100 }),
+            ({ SENSITIVE_ATTACK, DT_WATER, 100 }) }) );
+     }
+
+     varargs void
+     trigger_sensitive_attack(object enemy, string key,
+                              int dam, mixed spell)
+     {
+       // Es soll nicht verschwiegen werden, dass das Entzuenden und
+       // Loeschen einer Lichtquelle so leider nicht funktioniert...
+       if (key == DT_FIRE && !QueryProp(P_LIGHTED)) {
+         SetProp(P_LIGHTED, 1);
+         tell_object(environment(), "Die Fackel faengt Feuer.\n");
+       }
+       else if (key == DT_WATER && QueryProp(P_LIGHTED)) {
+         SetProp(P_LIGHTED, 0);
+         tell_object(environment(), "Die Fackel verlischt.\n");
+       }
+     }
+
+SIEHE AUCH:
+     trigger_sensitive_inv(), sensitive Objekte
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 15:56:25 1996 by Wargon
diff --git a/doc/lfun/trigger_sensitive_inv b/doc/lfun/trigger_sensitive_inv
new file mode 100644
index 0000000..a417ebe
--- /dev/null
+++ b/doc/lfun/trigger_sensitive_inv
@@ -0,0 +1,40 @@
+trigger_sensitive_inv()
+
+FUNKTION:
+     varargs void trigger_sensitive_inv(object ob, string key, int wert,
+     mixed *optionen1, mixed *optionen2);
+
+DEFINIERT IN:
+     eigenen Objekten, wird aufgerufen von /std/container/inventory.c
+
+ARGUMENTE:
+     ob
+          Das ausloesende Objekt
+     key
+          Der Schluessel, der die Aktion ausloeste.
+     wert
+          Der Grenzwert des ausloesenden Objektes.
+     optionen1
+          Die Optionen des ausloesenden Objektes.
+     optionen2
+          Die Optionen des reagierenden Objektes.
+
+BESCHREIBUNG:
+     Diese Funktion wird in Objekten des sensitiven Typs SENSITIVE_INVENTORY
+     aufgerufen, wenn sie in Kontakt mit Objekten des Typs
+     SENSITIVE_INVENTORY_TRIGGER treten, die den gleichen Schluessel
+     aufweisen und einen hoeheren Grenzwert haben.
+
+     Anhand der Parameter koennen dann die noetigen Aktionen ausgeloest
+     werden.
+
+RUeCKGABEWERT:
+     keiner
+
+BEISPIELE:
+
+SIEHE AUCH:
+     trigger_sensitive_attack(), sensitive Objekte
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:26:10 1996 by Wargon
diff --git a/doc/lfun/wield_me b/doc/lfun/wield_me
new file mode 100644
index 0000000..427504e
--- /dev/null
+++ b/doc/lfun/wield_me
@@ -0,0 +1,10 @@
+wield_me()
+
+BEMERKUNGEN:
+     wield_me wurde durch DoWield ersetzt.
+
+SIEHE AUCH:
+     DoWieldFunc(), /std/weapon.c
+
+----------------------------------------------------------------------------
+Last modified: Wed Apr 08 10:25:00 2004 by Muadib
diff --git a/doc/libhooks/H_HOOK_EXIT_USE b/doc/libhooks/H_HOOK_EXIT_USE
new file mode 100644
index 0000000..e4e4e53
--- /dev/null
+++ b/doc/libhooks/H_HOOK_EXIT_USE
@@ -0,0 +1,30 @@
+HOOK: 
+   H_HOOK_EXIT_USE
+
+DEFINIERT IN:
+   /sys/hook.h
+
+GERUFEN VON:
+   /std/room/exits.c
+
+BESCHREIBUNG:
+   Wird ausgeloest, wenn ein Lebewesen einen Raumausgang benutzt.
+
+EVENT-DATEN:
+   Es wird ein Array uebergeben:
+   ({ <verb>, <destroom>, <message> })
+      <verb>     : eingegebenes Kommandoverb
+      <destroom> : Zielraum (string) oder closure
+      <message>  : Bewegungsmeldung (ggf. 0)
+
+RUeCKGABEWERTE:
+   ({ <status>, <daten> })
+      <status>  : H_NO_MOD, H_CANCELLED, H_ALTERED
+      <daten>   : wie uebergebenes Datenarray
+
+SIEHE AUCH:
+   HRegisterToHook(), HUnregisterFromHook(), HookFlow()
+
+-----------------------------------------------------------------------------
+7.2.2016, Zesstra
+
diff --git a/doc/libhooks/H_HOOK_HP b/doc/libhooks/H_HOOK_HP
new file mode 100644
index 0000000..e80791f
--- /dev/null
+++ b/doc/libhooks/H_HOOK_HP
@@ -0,0 +1,28 @@
+HOOK:
+   H_HOOK_HP
+
+DEFINIERT IN:
+   /sys/hook.h
+
+AUSGELOeST VON:
+   /std/living/life.c
+
+BESCHREIBUNG:
+   Wird ausgeloest, wenn sich die P_HP eines Spielers aendern.
+   Dieser Hook laeuft nach der Aenderung der Lebenspunkte. Es ist nicht
+   moeglich, diesen Hook abzubrechen oder die Lebenspunkte zu aendern.
+
+EVENT-DATEN:
+   Es werden die neuen Lebenspunkte des Spielers uebergebe.
+
+RUeCKGABEWERTE:
+   ({ <status>, <daten> })
+      <status>  : H_NO_MOD
+      <daten>   : Lebenspunkte
+
+SIEHE AUCH:
+   HRegisterToHook(), HUnregisterFromHook(), HookFlow()
+
+-----------------------------------------------------------------------------
+7.2.2016, Zesstra
+
diff --git a/doc/libhooks/H_HOOK_SP b/doc/libhooks/H_HOOK_SP
new file mode 100644
index 0000000..c64f98e
--- /dev/null
+++ b/doc/libhooks/H_HOOK_SP
@@ -0,0 +1,27 @@
+HOOK:
+   H_HOOK_SP
+
+DEFINIERT IN:
+   /sys/hook.h
+
+AUSGELOeST VON:
+   /std/living/life.c
+
+BESCHREIBUNG:
+   Wird ausgeloest, wenn sich die P_SP eines Spielers aendern.
+   Dieser Hook laeuft nach der Aenderung der Konzentrationspunkte. Es ist
+   nicht moeglich, diesen Hook abzubrechen oder die Lebenspunkte zu aendern.
+
+EVENT-DATEN:
+   Es werden die neuen Konzentrationspunkte des Spielers uebergeben.
+
+RUeCKGABEWERTE:
+   ({ <status>, <daten> })
+      <status>  : H_NO_MOD
+      <daten>   : Konzentrationspunkte
+
+SIEHE AUCH:
+   HRegisterToHook(), HUnregisterFromHook(), HookFlow()
+
+-----------------------------------------------------------------------------
+7.2.2016, Zesstra
diff --git a/doc/master/compile_object b/doc/master/compile_object
new file mode 100644
index 0000000..1b55658
--- /dev/null
+++ b/doc/master/compile_object
@@ -0,0 +1,11 @@
+SYNOPSIS
+	object compile_object(string filename)
+
+DESCRIPTION
+	This function is caled if the interpreter cannot find the
+	file for an object to be loaded. The master object has now
+	the opportunity to return an object that will then serve as if
+	compiled from the given filename.
+
+	If 0 is returned, the usual ``Could not load descr for'' error
+	will occur.
diff --git a/doc/master/connect b/doc/master/connect
new file mode 100644
index 0000000..a1c7315
--- /dev/null
+++ b/doc/master/connect
@@ -0,0 +1,13 @@
+SYNOPSIS
+	object connect(void)
+
+DESCRIPTION
+	Return a login object that the requested connection should be
+	bound to. Note that the connection is not bound yet.
+
+	The lfun logon() will be applied to the login object after
+	binding the connection to it. That lfun has to return != 0 to
+	indicate success.
+
+SEE ALSO
+	logon(A), disconnect(M), interactive(E), exec(E)
diff --git a/doc/master/dangling_lfun_closure b/doc/master/dangling_lfun_closure
new file mode 100644
index 0000000..6bfaeb5
--- /dev/null
+++ b/doc/master/dangling_lfun_closure
@@ -0,0 +1,26 @@
+SYNOPSIS
+	void dangling_lfun_closure()
+
+DESCRIPTION
+	Handle a dangling lfun-closure.
+
+	This is called when the gamedriver executes a closure using a
+	vanished lfun. A proper handling is to raise a runtime error.
+
+	Technical:
+	  Upon replacing programs (see efun replace_program()), any
+	  existing lambda closures of the object are adjusted to the
+	  new environment. If a closure uses a lfun which vanished in
+	  the replacement process, the reference to this lfun is
+	  replaced by a reference to this function. The error will
+	  then occur when the execution of the adjusted lambda reaches
+	  the point of the lfun reference. There are two reasons for
+	  the delayed handling. First is that the program replacement
+	  and with it the closure adjustment happens at the end of a
+	  backend cycle, outside of any execution thread: noone would
+	  see the error at this time.  Second, smart closures might
+	  know/recognize the program replacement  and skip the call to
+	  the vanished lfun.
+
+SEE ALSO
+	closures(LPC)
diff --git a/doc/master/disconnect b/doc/master/disconnect
new file mode 100644
index 0000000..f7a9f3d
--- /dev/null
+++ b/doc/master/disconnect
@@ -0,0 +1,13 @@
+SYNOPSIS
+	void disconnect(object ob)
+
+DESCRIPTION
+	Handle the loss of the IP connection for the (formerly)
+	interactive object ob. The connection can be lost because the
+	the underlying transport connection was lost (``netdead''), or
+	because of a call to exec() or remove_interactive(). The
+	connection will be unbound upon return from this call.
+
+SEE ALSO
+	connect(M), remove_player(M), remove_interactive(E), exec(E),
+	interactive(E)
diff --git a/doc/master/epilog b/doc/master/epilog
new file mode 100644
index 0000000..eb51d75
--- /dev/null
+++ b/doc/master/epilog
@@ -0,0 +1,24 @@
+SYNOPSIS
+	void epilog(void)		/* compat in 3.2 */
+	string *epilog(int eflag)	/* !compat or 3.2.1 */
+
+DESCRIPTION
+	Perform final actions before opening the system to users.
+	The semantics of this function differ for compat and !compat
+	mode, and between 3.2 and 3.2.1.
+
+	Compat in 3.2: the objects from the INIT_FILE (#defined to
+	"room/init_file" at compile time in the parser) are already
+	loaded at this time. Normally there is nothing left to do for
+	this function.
+
+	!Compat and 3.2.1: the argument is the number of -e options that were
+	given to the parser on the commandline. Normally it is just 0
+	or 1. The function should return an array of strings, which
+	traditionally denote the objects to be preloaded with
+	master->preload(). Any other return value is interpreted as
+	``no object to preload''. The resulting strings will be passed
+	one at a time as arguments to preload().
+
+SEE ALSO	
+	preload(M), master(M)
diff --git a/doc/master/external_master_reload b/doc/master/external_master_reload
new file mode 100644
index 0000000..57913f0
--- /dev/null
+++ b/doc/master/external_master_reload
@@ -0,0 +1,10 @@
+SYNOPSIS
+	void external_master_reload()
+
+DESCRIPTION
+	Master was reloaded on external request by SIGUSR1.
+	It will be called after inaugurate_master() of course.
+	If you plan to do additional magic here, you're welcome.
+
+SEE ALSO
+	inaugurate_master(M)
diff --git a/doc/master/flag b/doc/master/flag
new file mode 100644
index 0000000..e04e91d
--- /dev/null
+++ b/doc/master/flag
@@ -0,0 +1,41 @@
+SYNOPSIS
+	void flag(string arg)
+
+DESCRIPTION
+	Evaluate an argument given as option '-f' to the driver.
+	If several '-f' options are given, this function will be
+	called sequentially with all given arguments.
+	This function can be used to pass the master commands via
+	arguments to the driver. This is useful when building a new
+	mudlib from scratch. It is called only when the system is
+	started.
+
+EXAMPLE
+	// The code given implements these commands:
+	//  '-fcall <ob> <fun> <arg>': call function <fun> in object <ob> with
+	//				argument <arg>.
+	//  '-fshutdown': shutdown the system immediately.
+	// Thus, starting the driver as
+	//	 'parse "-fcall foo bar Yow!" -fshutdown' would
+	// first do foo->bar("Yow!") and then shut down the system.
+
+	{
+	  string obj, fun, rest;
+
+	  if (arg == "shutdown")
+	  {
+	    shutdown();
+	    return;
+	  }
+	  if (sscanf(arg, "call %s %s %s", obj, fun, rest) >= 2)
+	  {
+	    write(obj+"->"+fun+"(\""+rest+"\") = ");
+	    write(call_other(obj, fun, rest));
+	    write("\n");
+	    return;
+	  }
+	  write("master: Unknown flag "+arg+"\n");
+	}
+
+SEE ALSO
+	master(M)
diff --git a/doc/master/get_bb_uid b/doc/master/get_bb_uid
new file mode 100644
index 0000000..5a4dc8b
--- /dev/null
+++ b/doc/master/get_bb_uid
@@ -0,0 +1,18 @@
+SYNOPSIS
+	string get_bb_uid(void)
+
+DESCRIPTION
+	Is called to get the ``backbone id''. Objects whose creator is
+	the backbone id are ``trusted'', and will automagically get
+	the uid and euid of the object that caused to load or clone
+	them.
+
+	The backbone id is also temporary given to objects while being
+	called via process_string().
+
+HISTORY
+	Since 3.2.1, get_bb_uid() is needed just for process_string()
+	if no this_object() is present.
+
+SEE ALSO
+	uids(C), creator_file(M), creator(E), get_root_id(M)
diff --git a/doc/master/get_ed_buffer_save_file_name b/doc/master/get_ed_buffer_save_file_name
new file mode 100644
index 0000000..50a23c3
--- /dev/null
+++ b/doc/master/get_ed_buffer_save_file_name
@@ -0,0 +1,22 @@
+SYNOPSIS
+	string get_ed_buffer_save_object_name(string edited_file)
+
+DESCRIPTION
+	This function is called when an interactive user object is
+	destructed or looses connection through remove_interactive()
+	while editing with ed() the file edite_file (emergency save).
+	this_player() is set to the object that loosing connection.
+	The function should return a file name for the emergency save
+	file.
+
+EXAMPLE
+	string get_ed_buffer_save_object_name(string file) {
+	  return "/players/"+getuid(this_player())+"/.dead_ed_files/"
+		 + explode(file, "/")[<1];
+	}
+
+	This breaks up file into its components and stores it in the
+	user's emergency save directory under the file's basename.
+
+SEE ALSO
+	ed(E), destruct(E), remove_interactive(E), valid_write(M)
diff --git a/doc/master/get_master_uid b/doc/master/get_master_uid
new file mode 100644
index 0000000..a0c6737
--- /dev/null
+++ b/doc/master/get_master_uid
@@ -0,0 +1,12 @@
+SYNOPSIS
+	string get_master_uid(void)
+
+DESCRIPTION
+	Return the string to be used as root-uid.
+	Under !native, the function is expendable.
+
+HISTORY
+	Introduced in 3.2.1@40 replacing get_root_uid().
+
+SEE ALSO
+	get_bb_uid(M), get_master_uid(M), uids(C), creator_file(M), creator(E)
diff --git a/doc/master/get_simul_efun b/doc/master/get_simul_efun
new file mode 100644
index 0000000..88adba0
--- /dev/null
+++ b/doc/master/get_simul_efun
@@ -0,0 +1,24 @@
+SYNOPSIS
+	mixed get_     simul_efun(void)      // 3.2
+	string|string* get_simul_efun(void)  // 3.2.1
+
+DESCRIPTION
+	Load the simul_efun object(s) and return one or more paths of it.
+// Note that the object(s) must be loaded by this function!
+//
+// When you return an array of strings, the first string is taken as path
+// to the simul_efun object, and all other paths are used for backup
+// simul_efun objects to call simul_efuns that are not present in the
+// main simul_efun object. This allows to remove simul_efuns at runtime 
+// without getting errors from old compiled programs that still use the 
+// obsolete simul_efuns. A side use of this mechanism is to provide 
+// a 'spare' simul_efun object in case the normal one fails to load.
+//
+	Should return either the object_name of the simul_efun object as
+	a string, or an array containing as first element a string
+	with the file name of the simul_efun object and the following
+	elements strings with file names of alternate simul_efun
+	objects that will be used if the first one cannot be loaded.
+
+SEE ALSO
+	simul_efun(C)
diff --git a/doc/master/get_wiz_name b/doc/master/get_wiz_name
new file mode 100644
index 0000000..6056965
--- /dev/null
+++ b/doc/master/get_wiz_name
@@ -0,0 +1,13 @@
+SYNOPSIS
+	string get_wiz_name(string file)
+
+DESCRIPTION
+	Argument is a file name, which we want to get the owner of.
+	This used for the wizlist, to determine who gets the score for
+	the file being used.
+
+	For 3.2.1, it is used only to score errors to the right wizard.
+
+SEE ALSO
+	wizlist(E), get_wiz_info(E), query_real_name(A),
+	query_creator(M), getuid(E)
diff --git a/doc/master/handle_external_signal b/doc/master/handle_external_signal
new file mode 100644
index 0000000..6fe81ef
--- /dev/null
+++ b/doc/master/handle_external_signal
@@ -0,0 +1,26 @@
+SYNOPSIS
+        #include <signals.h>
+
+        int handle_external_signal(int signal)
+
+DESCRIPTION
+        If the driver receives a signal from the OS it forwards it to the
+        mudlib master by calling this function. The signal received by the
+        driver is given in <signal> and may be one of the following:
+        SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2.
+
+        If this function returns != 0, the driver will assume the signal has
+        been dealt with and take NO further action.
+        The exception is SIGTERM, which can't be handled. The driver will
+        perform a graceful shutdown of the game after this function returns.
+
+        If the master does not handle the signal (returns 0 or this function
+        doe not exit), the driver will perform the following default actions:
+
+        SIGHUP:  begin a graceful shutdown
+        SIGINT:  send itself an unhandled SIGINT. This usually causes an
+                 immediate and non-graceful shutdown.
+        SIGUSR1: the driver will reload the master object
+        SIGUSR2: the driver will re-open its debug log file
+                 (this will happen the next time the driver writes to it)
+
diff --git a/doc/master/heart_beat_error b/doc/master/heart_beat_error
new file mode 100644
index 0000000..d4d15f2
--- /dev/null
+++ b/doc/master/heart_beat_error
@@ -0,0 +1,24 @@
+SYNOPSIS
+	mixed heart_beat_error(object culprit, string err, string prg,
+			       string curobj, int line)
+
+DESCRIPTION
+	This function is called when a runtime error occurs while
+	executing the heart_beat() function of the object culprit. prg
+	is program where the actual error happened, in object curobj
+	at the given line.
+
+	At time of call, the heart_beat has been turned off.
+	Return anything != 0 to restart the heart_beat in culprit.
+
+	If culprit is a user, it should at least get the message ``You
+	have no heartbeat''. A more advanced handling would destruct
+	the offending object curobj and and allow the heartbeat to
+	restart.
+
+	Note that prg denotes the program actually executed (which
+	might be an inherited one) whereas curobj is just the
+	offending object.
+
+SEE ALSO
+	set_heart_beat(E), heart_beat(A), runtime_error(M)
diff --git a/doc/master/inaugurate_master b/doc/master/inaugurate_master
new file mode 100644
index 0000000..85c9526
--- /dev/null
+++ b/doc/master/inaugurate_master
@@ -0,0 +1,30 @@
+SYNOPSIS
+	void inaugurate_master(int arg)
+
+DESCRIPTION
+	This function is called in the master object after it has been
+	created and is fully functional. Note that the create()
+	function is of little use in the master object, because at the
+	time it is called there is no simul_efun object yet, and the
+	interpreter does not know anything about the user-ids and
+	access permissions.
+
+	The argument <arg> denotes why this function is called:
+	  arg = 0: the mud just started, this is the first master of all.
+	      = 1: the master was destructed and then reactivated
+	           (because a new one couldn't be loaded).
+	      = 2: the master was destructed and then reactivated, but
+	           additionally lost all variable contents.
+	      = 3: this is a reloaded master.
+
+	This function has at least to set up the driverhooks to use
+	(in 3.2.1). Also, any mudwho or wizlist handling has to be
+	initialized here.
+
+	Besides that, do whatever you feel you need to do, 
+	e.g. set_auto_include_string(), or give the master a decent euid.
+
+SEE ALSO
+	initialisation(M), create(M), simul_efun(C), get_bb_id(M),
+	get_root_id(M), get_master_uid(M), flag(M),
+	reactivate_destructed_master(M)
diff --git a/doc/master/include_file b/doc/master/include_file
new file mode 100644
index 0000000..68568ee
--- /dev/null
+++ b/doc/master/include_file
@@ -0,0 +1,32 @@
+SYNOPSIS
+        mixed include_file (string file, string compiled_file, int sys_include)
+
+DESCRIPTION
+        Generate the pathname of an included file.
+
+        Arguments:
+          previous_object(): The object causing the compile.
+          file             : The name given in the #include directive.
+          compiled_file    : The object file which is just compiled.
+                             (compat: name given without leading "/")
+          sys_include      : TRUE for #include <> directives.
+
+        Result:
+          0: use the normal include filename generation (""-includes are
+             used as they are, <>-includes are handled according to
+             H_INCLUDE_DIRS).
+
+          <path>: the full absolute pathname of the file to include,
+                  without parentdir parts ("/../"). Leading slashes ("/")
+                  may be omitted.
+
+          else: The include directive is not legal.
+
+        If the function does not generate a valid pathname, the driver
+        will next try to resolve the include using the H_INCLUDE_DIRS hook.
+
+HISTORY
+        Introduced in LDMud 3.2.8.
+
+SEE ALSO
+        hooks(C), inherit_file(M), include_dirs(H)
diff --git a/doc/master/inherit_file b/doc/master/inherit_file
new file mode 100644
index 0000000..7914fac
--- /dev/null
+++ b/doc/master/inherit_file
@@ -0,0 +1,26 @@
+SYNOPSIS
+        mixed inherit_file (string file, string compiled_file)
+
+DESCRIPTION
+        Generate the pathname of an inherited object.
+
+        Arguments:
+          previous_object(): The object causing the compile.
+          file             : The name given in the inherit directive.
+          compiled_file    : The object file which is just compiled.
+                             (compat: name given without leading "/")
+
+        Result:
+          0: use the given filename as it is.
+
+          <path>: the full absolute pathname of the file to inherit,
+                  without parentdir parts ("/../"). Leading slashes ("/")
+                  are ignored.
+
+          else: The inherit directive is not legal.
+
+HISTORY
+        Introduced in LDMud 3.2.8.
+
+SEE ALSO
+        hooks(C), include_file(M)
diff --git a/doc/master/initialisation b/doc/master/initialisation
new file mode 100644
index 0000000..e08d34a
--- /dev/null
+++ b/doc/master/initialisation
@@ -0,0 +1,16 @@
+SYNOPSIS
+	Initialization of the master object (since 3.2.1).
+
+	As since 3.2.1 the lfuns which are called to initialize
+	objects after a load are defined through driver hooks, and
+	these hooks are cleared prior to a master (re)load, the first
+	function called is inaugurate_master(). Anyway it's not very
+	sensible to do anything earlier as the master is not
+	recognized as such at that time, and so a number of
+	(important) things would not work.
+	Which lfun is called during runtime to reset the master is also
+	depending on the driverhook settings. Arbitrary actions may be done
+	on a reset.
+
+SEE ALSO
+	inaugurate_master(M)
diff --git a/doc/master/log_error b/doc/master/log_error
new file mode 100644
index 0000000..afc3385
--- /dev/null
+++ b/doc/master/log_error
@@ -0,0 +1,11 @@
+SYNOPSIS
+	void log_error(string file, string err)
+
+DESCRIPTION
+	Announce a compiler-time error in the named file. Whenever the
+	LPC compiler detects an error, this function is called. It
+	should at least log the error in a file, and also announce it
+	to the active user.
+
+SEE ALSO
+	runtime_error(M)
diff --git a/doc/master/make_path_absolute b/doc/master/make_path_absolute
new file mode 100644
index 0000000..e0a0019
--- /dev/null
+++ b/doc/master/make_path_absolute
@@ -0,0 +1,10 @@
+SYNOPSIS
+	string make_path_absolute(string str)
+
+DESCRIPTION
+	Absolutize a relative filename str given to the editor. Should
+	return the full pathname of the file to use. Any non-string
+	result will act as ``bad file name''.
+
+SEE ALSO
+	ed(E), valid_write(M)
diff --git a/doc/master/master b/doc/master/master
new file mode 100644
index 0000000..7d88855
--- /dev/null
+++ b/doc/master/master
@@ -0,0 +1,30 @@
+NAME
+	master
+
+DESCRIPTION
+	This directory contains descriptions for the functions that
+	Amylaar's version of the LPC parser, expects to find in the
+	master object (similar to lfuns, but for the master object
+	only). The name of the master object is hardcoded in the
+	parser (to "secure/master").
+
+	The master is the gateway between the interpreter and the
+	mudlib to perform actions with mudlib specific effects. Calls
+	to the master by the interpreter have an automatic catch() in
+	effect.
+
+	Note that the master is loaded first of all objects. Thus you
+	shouldn't inherit an other object, nor is the compiler able to
+	search include files (read: they must be specified with full
+	path).
+
+	Amylaar says: actually, you can inherit, but the file will be
+	loaded then before the master, which isn't good for most
+	files.
+
+	Refer to 'master-3.2' and 'master-3.2.1' for the surveys of
+	the masters internals. 
+
+SEE ALSO
+	master-3.2(M), master-3.2.1(M)
+	efun(E), applied(A), concepts(C), driver(D), lpc(LPC)
diff --git a/doc/master/master-3.2 b/doc/master/master-3.2
new file mode 100644
index 0000000..b55a931
--- /dev/null
+++ b/doc/master/master-3.2
@@ -0,0 +1,248 @@
+NAME
+	master for LPMud 3.2
+
+DESCRIPTION
+	This directory contains descriptions for the functions that
+	Amylaar's version of the LPC parser, expects to find in the
+	master object (similar to lfuns, but for the master object
+	only). The name of the master object is hardcoded in the
+	parser (to "secure/master").
+
+	The master is the gateway between the interpreter and the
+	mudlib to perform actions with mudlib specific effects. Calls
+	to the master by the interpreter have an automatic catch() in
+	effect.
+
+	Note that the master is loaded first of all objects. Thus you
+	shouldn't inherit an other object, nor is the compiler able to
+	search include files (read: they must be specified with full
+	path).
+
+	Amylaar says: actually, you can inherit, but the file will be
+	loaded then before the master, which isn't good for most
+	files.
+
+	A short survey of the things that happen at system startup
+	time:
+
+	The Initialisation functions are called after (re)loading the
+	master to establish the most basic operation parameters.
+
+	The initialisation of LPMud on startup follows this schedule:
+	  - The interpreter evaluates the commandline options and
+	    initializes itself.
+	  - The master is loaded, and thus it's create() (!compat only) and
+	    its reset() is called, though it's not much use, since
+	    most of the important things won't work already.
+	  - get_root_uid() is called. If the result is valid, it
+	    becomes the masters uid and euid.
+	  - get_bb_uid() is called.
+	  - inaugurate_master() is called.
+	  - flag() is called for each given '-f' commandline option.
+	  - get_simul_efun() is called.
+	  - define_include_dirs() is called.
+	  - Preloading is done:
+	      compat : The filenames of the objects are read from
+		       INIT_FILE and the objects are loaded. Then
+		       epilog() is called.
+	      !compat: epilog() is called. If it returns an array of
+		       strings, it is considered holding the filenames
+		       of the objects to preload. They are then given
+		       one at a time as argument to preload() which
+		       does the actual preloading.
+	  - The interpreter sets up the IP communication and enters
+	    the backend loop.
+
+	If the master is reloaded during system operation, this
+	actions are taken:
+	  - The master is loaded, and thus it's create() (!compat only) and
+	    its reset() is called.
+	  - Any auto-include string is cleared.
+	  - get_root_uid() is called. If the result is valid, it becomes the
+	    masters uid and euid.
+	  - inaugurate_master() is called.
+
+	If the master was destructed, but couldn't be reloaded, the old
+	master object could be reactivated. In that case:
+	  - reactivate_destructed_master() is called.
+	  - inaugurate_master() is called.
+
+
+	Security hint: most of this functions are not useful to be
+	called directly from other objects and can be made private or
+	static. Unlike create(), these functions that are applied to
+	the master object are found by the interpreter even if not
+	publicly accessible.
+
+
+
+	A short reference to all expected master functions...
+	----------------------------------------------------------------
+	    Initialisation
+
+	void create ()	// !compat
+	  Initialize the object. Not very useful, though.
+
+	void reset (int flag)  // !native
+	void reset ()	       // native
+	  Initialize (compat only) or reset the object.
+
+	void inaugurate_master ()
+	  Perform mudlib specific setup of the master.
+
+	void flag (string arg)
+	  Evaluate an argument given as option '-f' to the driver.
+
+	string *define_include_dirs ()
+	  Define where the include files are searched.
+
+	void	epilog ()	    // compat
+	string *epilog (int eflag)  // !compat
+	  Perform final actions before opening the system to users.
+	  The semantics of this function differ for compat and !compat mode.
+
+	void preload (string file)  // !compat
+	  Preload a given object.
+
+	void external_master_reload ()
+	  Called after a reload of the master on external request.
+
+	void reactivate_destructed_master (int removed)
+	  Reactivate a formerly destructed master.
+
+	string|string * get_simul_efun ()
+	  Load the simul_efun object and return one or more paths of it.
+
+	----------------------------------------------------------------
+	    Handling of user connections
+
+	object connect ()
+	  Handle the request for a new connection.
+
+	void disconnect (object obj)
+	  Handle the loss of an IP connection.
+
+	void remove_player (object user)
+	  Remove a user object from the system.
+
+	-----------------------------------------------------------------
+	    Runtime Support
+
+	object compile_object (string filename)
+	  Compile an virtual object.
+
+	string get_wiz_name (string file)
+	  Return the author of a file.
+
+	string object_name (object obj)
+	  Return a printable name for an object.
+
+	mixed prepare_destruct (object obj)
+	  Prepare the destruction of the given object.
+
+	void quota_demon (void)
+	  Handle quotas in times of memory shortage.
+
+	void receive_udp (string host, string msg)
+	  Handle a received IMP message.
+
+	void slow_shut_down (int minutes)
+	  Schedule a shutdown for the near future.
+
+	void notify_shutdown ()
+	  Notify the master about an immediate shutdown.
+
+	-----------------------------------------------------------------
+	    Error Handling
+
+	void dangling_lfun_closure ()
+	  Handle a dangling lfun-closure.
+
+	void log_error (string file, string err)
+	  Announce a compiler-time error.
+
+	mixed heart_beat_error (object culprit, string err,
+				string prg, string curobj, int line)
+	  Announce an error in the heart_beat() function.
+
+	void runtime_error (string err, string prg, string curobj, int line)
+	  Announce a runtime error.
+
+	-----------------------------------------------------------------
+	    Security and Permissions
+
+	int privilege_violation (string op, mixed who, mixed arg3, mixed arg4)
+	  Validate the execution of a privileged operation.
+
+	int query_allow_shadow (object victim)
+	  Validate a shadowing.
+
+	int query_player_level (string what)
+	  Check if the user is of high enough level for several things.
+
+	int valid_exec (string name)
+	  Validate the rebinding of an IP connection by usage of efun
+	  exec().
+
+	int valid_query_snoop (object obj)
+	  Validate if the snoopers of an object may be revealed by
+	  usage of the efun query_snoop().
+
+	int valid_snoop (object snoopee, object snooper)
+	  Validate the start/stop of a snoop.
+
+	------------------------------------------------------------------
+	    Userids and depending Security
+
+	string creator_file (mixed obj)
+	  Return the name of the creator of an object.
+	  This is called in every mode!
+
+	string get_root_uid ()	// !compat
+	  Return the string to be used as root-uid.
+
+	string get_bb_uid()  // !compat
+	  Return the string to be used as root-uid.
+
+	int valid_seteuid (object obj, string neweuid)	// !compat
+	  Validate the change of an objects euid by efun seteuid().
+
+	int|string valid_read (string path, string euid, string fun, object caller)
+	int|string valid_write (string path, string euid, string fun, object caller)
+	  Validate a reading/writing file operation.
+
+	-----------------------------------------------------------------
+	    ed() Support
+
+	string make_path_absolute (string str)
+	  Absolutize a relative filename given to the editor.
+
+	int save_ed_setup (object who, int code)
+	  Save individual settings of ed for a wizard.
+
+	int retrieve_ed_setup (object who)
+	  Retrieve individual settings of ed for a wizard.
+
+	string get_ed_buffer_save_object_name (string file)
+	  Return a filename for the ed buffer to be saved into.
+
+	----------------------------------------------------------------
+	    parse_command() Support  (!compat)
+
+	string *parse_command_id_list ()
+	  Return generic singular ids.
+
+	string *parse_command_plural_id_list ()
+	  Return generic plural ids.
+
+	string *parse_command_adjectiv_id_list ()
+	  Return generic adjective ids.
+
+	string *parse_command_prepos_list ()
+	  Return common prepositions.
+
+	string parse_command_all_word()
+	  Return the one(!) 'all' word.
+
+SEE ALSO
+	master(M), efun(E), applied(A), concepts(C), driver(D), lpc(LPC)
diff --git a/doc/master/master-3.2.1 b/doc/master/master-3.2.1
new file mode 100644
index 0000000..03e4339
--- /dev/null
+++ b/doc/master/master-3.2.1
@@ -0,0 +1,231 @@
+NAME
+	master for LPMud 3.2.1
+
+DESCRIPTION
+	This directory contains descriptions for the functions that
+	Amylaar's version of the LPC parser, expects to find in the
+	master object (similar to lfuns, but for the master object
+	only). The name of the master object is hardcoded in the
+	parser (to "secure/master").
+
+	The master is the gateway between the interpreter and the
+	mudlib to perform actions with mudlib specific effects. Calls
+	to the master by the interpreter have an automatic catch() in
+	effect.
+
+	Note that the master is loaded first of all objects. Thus you
+	shouldn't inherit an other object, nor is the compiler able to
+	search include files (read: they must be specified with full
+	path).
+
+	Amylaar says: actually, you can inherit, but the file will be
+	loaded then before the master, which isn't good for most
+	files.
+
+	A short survey of the things that happen at system startup
+	time:
+
+	The Initialisation functions are called after (re)loading the
+	master to establish the most basic operation parameters.
+
+	The initialisation of LPMud on startup follows this schedule:
+	  - The interpreter evaluates the commandline options and
+	    initializes itself.
+	  - The master is loaded, but since the driverhooks are not set yet,
+	    no standard initialisation lfun is called.
+	  - get_master_uid() is called. If the result is valid, it becomes the
+	  - inaugurate_master() is called.
+	  - flag() is called for each given '-f' commandline option.
+	  - get_simul_efun() is called.
+	  - the WIZLIST is read in.
+	  - epilog() is called. If it returns an array of strings,
+	    they are given one at a time as argument to preload().
+	    Traditionally, these strings are the filenames of the objects to
+	    preload, which preload() then does.
+	  - The interpreter sets up the IP communication and enters
+	    the backend loop.
+
+	If the master is reloaded during system operation, this
+	actions are taken:
+	  - The master is loaded, and its initialisation lfun is
+	    called according to the settings of the driverhooks (if set).
+	  - Any auto-include string and all driverhooks are cleared.
+	  - get_master_uid() is called. If the result is valid, it becomes the
+	    masters uid and euid.
+	  - inaugurate_master() is called.
+
+	If the master was destructed, but couldn't be reloaded, the old
+	master object could be reactivated. In that case:
+	  - reactivate_destructed_master() is called.
+	  - inaugurate_master() is called.
+
+
+	Security hint: most of this functions are not useful to be
+	called directly from other objects and can be made private or
+	static. Unlike create(), these functions that are applied to
+	the master object are found by the interpreter even if not
+	publicly accessible.
+
+
+
+	A short reference to all expected master functions...
+	----------------------------------------------------------------
+	    Initialisation
+
+	void inaugurate_master ()
+	  Perform mudlib specific setup of the master.
+
+	string get_master_uid ()
+	  Return the string to be used as uid (and -euid) of a
+	  (re)loaded master. 
+
+	void flag (string arg)
+	  Evaluate an argument given as option '-f' to the driver.
+
+	string *define_include_dirs ()
+	  Define where the include files are searched.
+
+	string *epilog (int eflag)
+	  Perform final actions before opening the system to users.
+
+	void preload (string file)
+	  Preload a given object.
+
+	void external_master_reload ()
+	  Called after a reload of the master on external request.
+
+	void reactivate_destructed_master (int removed)
+	  Reactivate a formerly destructed master.
+
+	string|string * get_simul_efun ()
+	  Load the simul_efun object and return one or more paths of it.
+
+	----------------------------------------------------------------
+	    Handling of user connections
+
+	object connect ()
+	  Handle the request for a new connection.
+
+	void disconnect (object obj)
+	  Handle the loss of an IP connection.
+
+	void remove_player (object user)
+	  Remove a user object from the system.
+
+	void stale_erq (closure callback)
+	  Notify the loss of the erq demon.
+
+	-----------------------------------------------------------------
+	    Runtime Support
+
+	object compile_object (string filename)
+	  Compile an virtual object.
+
+	string get_wiz_name (string file)
+	  Return the author of a file.
+
+	string object_name (object obj)
+	  Return a printable name for an object.
+
+	mixed prepare_destruct (object obj)
+	  Prepare the destruction of the given object.
+
+	void quota_demon (void)
+	  Handle quotas in times of memory shortage.
+
+	void receive_udp (string host, string msg, int port)
+	  Handle a received IMP message.
+
+	void slow_shut_down (int minutes)
+	  Schedule a shutdown for the near future.
+
+	void notify_shutdown ()
+	  Notify the master about an immediate shutdown.
+
+	-----------------------------------------------------------------
+	    Error Handling
+
+	void dangling_lfun_closure ()
+	  Handle a dangling lfun-closure.
+
+	void log_error (string file, string err)
+	  Announce a compiler-time error.
+
+	mixed heart_beat_error (object culprit, string err,
+				string prg, string curobj, int line)
+	  Announce an error in the heart_beat() function.
+
+	void runtime_error (string err, string prg, string curobj, int line)
+	  Announce a runtime error.
+
+	-----------------------------------------------------------------
+	    Security and Permissions
+
+	int privilege_violation (string op, mixed who, mixed arg3, mixed arg4)
+	  Validate the execution of a privileged operation.
+
+	int query_allow_shadow (object victim)
+	  Validate a shadowing.
+
+	int query_player_level (string what)
+	  Check if the user is of high enough level for several things.
+
+	int valid_exec (string name)
+	  Validate the rebinding of an IP connection by usage of efun
+	  exec().
+
+	int valid_query_snoop (object obj)
+	  Validate if the snoopers of an object may be revealed by
+	  usage of the efun query_snoop().
+
+	int valid_snoop (object snoopee, object snooper)
+	  Validate the start/stop of a snoop.
+
+	------------------------------------------------------------------
+	    Userids and depending Security
+
+	string get_bb_uid()  // euids
+	  Return the string to be used as root-uid.
+
+	int valid_seteuid (object obj, string neweuid)	// euids
+	  Validate the change of an objects euid by efun seteuid().
+
+	int|string valid_read (string path, string euid, string fun, object caller)
+	int|string valid_write (string path, string euid, string fun, object caller)
+	  Validate a reading/writing file operation.
+
+	-----------------------------------------------------------------
+	    ed() Support
+
+	string make_path_absolute (string str)
+	  Absolutize a relative filename given to the editor.
+
+	int save_ed_setup (object who, int code)
+	  Save individual settings of ed for a wizard.
+
+	int retrieve_ed_setup (object who)
+	  Retrieve individual settings of ed for a wizard.
+
+	string get_ed_buffer_save_object_name (string file)
+	  Return a filename for the ed buffer to be saved into.
+
+	----------------------------------------------------------------
+	    parse_command() Support  (!compat, SUPPLY_PARSE_COMMAND defined)
+
+	string *parse_command_id_list ()
+	  Return generic singular ids.
+
+	string *parse_command_plural_id_list ()
+	  Return generic plural ids.
+
+	string *parse_command_adjectiv_id_list ()
+	  Return generic adjective ids.
+
+	string *parse_command_prepos_list ()
+	  Return common prepositions.
+
+	string parse_command_all_word()
+	  Return the one(!) 'all' word.
+
+SEE ALSO
+	master(M), efun(E), applied(A), concepts(C), driver(D), lpc(LPC)
diff --git a/doc/master/notify_shutdown b/doc/master/notify_shutdown
new file mode 100644
index 0000000..d23cd98
--- /dev/null
+++ b/doc/master/notify_shutdown
@@ -0,0 +1,29 @@
+SYNOPSIS
+        varargs void notify_shutdown (string crash_reason)
+
+DESCRIPTION
+        Notify the master about an immediate shutdown. If <crash_reason> is 0,
+        it is a normal shutdown, otherwise it is a crash and <crash_reason>
+        gives a hint at the reason.
+
+        The function has the opportunity to perform any cleanup operation,
+        like informing the mudwho server that the mud is down. This can not be
+        done when remove_player() is called because the udp connectivity is
+        already gone then.
+
+        If the gamedriver shuts down normally , this is the last function
+        called before the mud shuts down the udp connections and the accepting
+        socket for new players.
+
+        If the gamedriver crashes, this is the last function called before the
+        mud attempts to dump core and exit. WARNING: Since the driver is in an
+        unstable state, this function may not be able to run to completion!
+        The following crash reasons are defined:
+          "Fatal Error": an internal sanity check failed.
+
+HISTORY
+        LDMud 3.2.9 added the <crash_reason> argument and that the function
+        is called for a crash at all.
+
+SEE ALSO
+        slow_shut_down(M), remove_player(M),
diff --git a/doc/master/object_name b/doc/master/object_name
new file mode 100644
index 0000000..2990b6d
--- /dev/null
+++ b/doc/master/object_name
@@ -0,0 +1,10 @@
+SYNOPSIS
+	string object_name(object ob)
+
+DESCRIPTION
+	Return a printable name for an object. This function is called
+	by sprintf() to print a meaningful name in addition to the
+	normal object_name().
+
+SEE ALSO
+	sprintf(E), object_name(E)
diff --git a/doc/master/parse_command_all_word b/doc/master/parse_command_all_word
new file mode 100644
index 0000000..b57ab05
--- /dev/null
+++ b/doc/master/parse_command_all_word
@@ -0,0 +1,9 @@
+SYNOPSIS
+	string *parse_command_all_word(void)
+
+DESCRIPTION
+	Used by parse_command(). Return the word for ``all'' in the
+	installations native language.
+
+SEE ALSO
+	parse_command(E)
diff --git a/doc/master/parse_command_prepos_list b/doc/master/parse_command_prepos_list
new file mode 100644
index 0000000..24706ce
--- /dev/null
+++ b/doc/master/parse_command_prepos_list
@@ -0,0 +1,9 @@
+SYNOPSIS
+	string *parse_command_prepos_list(void)
+
+DESCRIPTION
+	Used by parse_command(). Return an array of common
+	prepositions in the installations native language.
+
+SEE ALSO
+	parse_command(E)
diff --git a/doc/master/preload b/doc/master/preload
new file mode 100644
index 0000000..95f27d7
--- /dev/null
+++ b/doc/master/preload
@@ -0,0 +1,18 @@
+SYNOPSIS
+	void preload(string file)
+
+DESCRIPTION
+	Load the object with the given file name, that was returned by
+	epilog(). It is task of the epilog()/preload() pair to ensure
+	the validity of the given strings (e.g. filtering out comments
+	and blank lines). For preload itself a call_other(file, "???")
+	s sufficient, but it should be guarded by a catch() to avoid
+	premature blockings. Also it is wise to change the master's
+	euid from root_uid to something less privileged for the time
+	of the preload.
+
+	You can of course do anything else with the passed strings -
+	preloading is just the traditional task.
+
+SEE ALSO
+	epilog(M), master(M)
diff --git a/doc/master/prepare_destruct b/doc/master/prepare_destruct
new file mode 100644
index 0000000..3121b88
--- /dev/null
+++ b/doc/master/prepare_destruct
@@ -0,0 +1,22 @@
+SYNOPSIS
+	mixed prepare_destruct(object obj)
+
+DESCRIPTION
+	Prepare the destruction of the object obj. Return 0 if the
+	object is ready for destruction, any other value will abort
+	the attempt. If a string is returned, an error with the string
+	as message will be issued.
+
+	The interpreter calls this function whenever an object shall
+	be destructed. It expects, that this function cleans the
+	inventory of the object, or the destruct will fail.
+	Furthermore, the function could notify the former inventory
+	objects that their holder is under destruction (useful to move
+	users out of rooms which re updated); and it could announce
+	systemwide the destruction(quitting) of users.
+
+	Strange things will happen if the mastor object does not
+	provide this function.
+
+SEE ALSO
+	remove_player(M), destruct(E)
diff --git a/doc/master/privilege_violation b/doc/master/privilege_violation
new file mode 100644
index 0000000..957a4b9
--- /dev/null
+++ b/doc/master/privilege_violation
@@ -0,0 +1,63 @@
+SYNOPSIS
+	int privilege_violation(string op, mixed who, mixed arg3, mixed arg4)
+
+DESCRIPTION
+	Validate the execution of a privileged operation.
+	op denotes the requested operation, who is the object
+	requesting the operation (object_name or object pointer), arg3
+	and arg4 are additional arguments, depending on the operation.
+
+	The function should return >0 to grant the privilege, 0 to
+	indicate that the caller was probably misleaded and the error
+	might be fixed, and anything else to indicate a real
+	violation, that will be handled as run time error.
+
+	The privileged operations are:
+	attach_erq_demon  Attach the erq demon to object <arg> with
+	                  flag <args>. 
+	bind_lambda	  Bind a lambda-closure to object arg3.
+	call_out_info	  Return an array with all call_out
+			  informations.
+	nomask simul_efun Attempt to get an efun <arg> via efun:: when
+	                  it is shadowed by a nomask type simul_efun.
+	rename_object	  The object who tries to rename the object
+			  arg3 to the name arg4.
+	send_udp	  Send UDP-data to host arg3.
+	set_auto_include_string Set the string automatically included
+			  by the compiler into every object.
+	get_extra_wizinfo Get the additional wiz-list info for user
+			  arg3.
+	set_extra_wizinfo Set the additional wiz-list info for user
+			  arg3.
+	set_extra_wizinfo_size Set the size of the additional user
+			  info in the wiz-list to arg3.
+	set_driver_hook   : Set hook <arg> to <arg2>.
+	set_this_object	  Set this_object() to arg3.
+	shadow_add_action Add an action to function arg4 that is
+			  shadowed by the object arg3.
+	symbol_variable	  Attempt to make a symbol from a hidden
+			  inherited variable. arg3 is the object in
+			  question, arg4 the number of the variable in
+			  the variable table.
+	wizlist_info	  Return an array with all wiz-list
+			  information.
+
+	call_out_info() can return the arguments to functions and
+	lambda closures to be called by call_out(); you should
+	consider that read access to closures, mappings and arrays
+	means write access and/or other privileges. 
+	wizlist_info() will return an array which holds, among others,
+	the extra wizlist field. While a toplevel array, if found,
+	will be copied, this does not apply to nested arrays or to any
+	mappings. You might also have some sensitive closures there.
+	send_udp() should be watched as it could be abused to mess up
+	the IMP.
+	The xxx_extra_wizinfo operations are necessary for a proper
+	wizlist and should therefore be restricted to admins.
+	All other operations are potential sources for direct security
+	breaches - any use of them should be scrutinized closely.
+
+SEE ALSO	
+	simul_efun(C), call_out_info(E), shadow(E), add_action(E),
+	wizlist(E), set_this_object(E), rename_object(E),
+	bind_lambda(E), send_udp(E), set_auto_include_string(E)
diff --git a/doc/master/query_allow_shadow b/doc/master/query_allow_shadow
new file mode 100644
index 0000000..47d2e95
--- /dev/null
+++ b/doc/master/query_allow_shadow
@@ -0,0 +1,23 @@
+query_allow_shadow(M)
+FUNKTION:
+     int query_allow_shadow(object victim)
+
+DEFINITION:
+     /secure/master.c
+
+BESCHREIBUNG:
+     Gibt 1 zurueck, wenn das Objekt victim von previous_object() beschattet 
+     werden darf, 0 sonst.
+
+     Die Funktion prueft, ob das Objekt victim ein Root-Objekt ist, oder
+     ob es beim Aufruf von query_prevent_shadow() 1 zurueckgibt.
+
+BEMERKUNGEN:
+     Wird automatisch bei shadow() ausgefuehrt.
+
+SIEHE AUCH:
+     Rechte:	     query_prevent_shadow(L)
+     Generell:	     shadow(E), unshadow(E)
+     Informationen:  query_shadowing(E)
+
+20. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/master/query_player_level b/doc/master/query_player_level
new file mode 100644
index 0000000..484e18e
--- /dev/null
+++ b/doc/master/query_player_level
@@ -0,0 +1,15 @@
+SYNOPSIS
+	int query_player_level(string what)
+
+DESCRIPTION
+	Check if the user is of high enough level for several things.
+
+	The argument denotes the action for which permission is to be
+	checked:
+
+	"trace"		Is the user allowed to use tracing?
+	"inspect memory" Is the user allowed to issue the
+			special command "showsmallnewmalloced"? 
+
+SEE ALSO
+	trace(E), showsmallnewmalloced(D), malloc(D), status(D), memory(C)
diff --git a/doc/master/quota_demon b/doc/master/quota_demon
new file mode 100644
index 0000000..f7f8d95
--- /dev/null
+++ b/doc/master/quota_demon
@@ -0,0 +1,18 @@
+SYNOPSIS
+	void quota_demon(void)
+
+DESCRIPTION
+	Handle quotas in times of memory shortage.
+
+	This function is called during the final phase of a garbage
+	collection if the reserved user area couldn't be reallocated.
+	This function (or a called demon) has now the opportunity to
+	remove some (still active) objects from the system. If this does
+	not free enough memory to reallocate the user reserve,
+	slow_shut_down() will be called to start Armageddon.
+
+	Up to now, the wizlist lacks various informations needed to
+	detect the memory-hungriest users.
+
+SEE ALSO
+	slow_shut_down(M), wizlist(E)
diff --git a/doc/master/reactivate_destructed_master b/doc/master/reactivate_destructed_master
new file mode 100644
index 0000000..e90d2a1
--- /dev/null
+++ b/doc/master/reactivate_destructed_master
@@ -0,0 +1,15 @@
+SYNOPSIS
+	void reactivate_destructed_master(int flag)
+
+DESCRIPTION
+	This function is called in an already destructed master object
+	if no new master object could be loaded. flag will be 1 if the
+	old master object could be reclaimed form the list of objects
+	that were marked for destruction but not yet terminated. If
+	flag is 0, all variables of the object have been set to 0 and
+	must be re-initialized.
+
+	After this function, inaugurate_master() will be applied again.
+
+SEE ALSO
+	destruct(E), inaugurate_master(M), master(M)
diff --git a/doc/master/receive_udp b/doc/master/receive_udp
new file mode 100644
index 0000000..a1b58c5
--- /dev/null
+++ b/doc/master/receive_udp
@@ -0,0 +1,17 @@
+SYNOPSIS
+	void receive_udp(string host, string msg, int hostport)
+
+DESCRIPTION
+	Handle a received IMP message.
+
+	This function is called for every message received on the IMP
+	port. Usually it is passed on to some object that handles
+	inter mud communications.
+
+HISTORY
+	The 'hostport' argument was added in 3.2.1.
+
+SEE ALSO
+	send_udp(E), query_udp_port(E)
+
+29.10.2006 Zesstra
diff --git a/doc/master/remove_player b/doc/master/remove_player
new file mode 100644
index 0000000..b70a1fb
--- /dev/null
+++ b/doc/master/remove_player
@@ -0,0 +1,14 @@
+SYNOPSIS
+	void remove_player(object ob)
+
+DESCRIPTION
+	Remove an interactive user object ob from the system. This
+	function is called by the interpreter to expell remaining
+	users from the system on shutdown in a polite way. If this
+	functions fails to quit/destruct the user, he will be
+	destructed the hard way by the interpreter.
+
+	This function must not cause runtime errors.
+
+SEE ALSO
+	remove_interactive(E), slow_shut_down(M)
diff --git a/doc/master/retrieve_ed_setup b/doc/master/retrieve_ed_setup
new file mode 100644
index 0000000..4d92ef0
--- /dev/null
+++ b/doc/master/retrieve_ed_setup
@@ -0,0 +1,9 @@
+SYNOPSIS
+	int retrieve_ed_setup(object command_giver)
+
+DESCRIPTION
+	Should return an integer that gives the ed setup flags for the
+	user that is denoted by command_giver.
+
+SEE ALSO
+	save_ed_setup(M)
diff --git a/doc/master/runtime_error b/doc/master/runtime_error
new file mode 100644
index 0000000..d109df8
--- /dev/null
+++ b/doc/master/runtime_error
@@ -0,0 +1,16 @@
+SYNOPSIS
+	void runtime_error(string err, string prg, string curobj, int line)
+
+DESCRIPTION
+	This function has to announce a runtime error to the active
+	user. If the user has enough privileges, it might give him the
+	full error message together with the source line. Else it
+	should issue a decent message ("Your sensitive mind notices a
+	wrongness in the fabric of space").
+
+	Note that prg denotes the program actually executed (which
+	might be an inherited one) whereas curobj is just the
+	offending object.
+
+SEE ALSO
+	log_error(M), heart_beat_error(M), raise_error(E)
diff --git a/doc/master/runtime_warning b/doc/master/runtime_warning
new file mode 100644
index 0000000..80864ab
--- /dev/null
+++ b/doc/master/runtime_warning
@@ -0,0 +1,27 @@
+SYNOPSIS
+        void runtime_warning( string msg, string curobj, string prog, int line
+                            , int inside_catch)
+
+DESCRIPTION
+        This function is called to let the mudlib handle a runtime warning,
+        e.g. by logging it into a database.
+
+        <msg> is the warning message.
+        <curobj> is the name of the current object which caused the message
+              (the object itself might already be destructed), or 0 if there
+              is none.
+        <prog>, <line> determine the name of the program and the line where
+              the error occured if the current object exists, otherwise
+              they are 0.
+        <inside_catch> : != 0 if the warning occurs inside a catch().
+
+        The driver is limited to three nested warnings, to prevent
+        an endless recursion in case runtime_warning() itself causes
+        warnings.
+
+HISTORY
+        Introduced in LDMud 3.3.551.
+        LDMud 3.3.705 added the <inside_catch> argument.
+
+SEE ALSO
+        runtime_error(M)
diff --git a/doc/master/save_ed_setup b/doc/master/save_ed_setup
new file mode 100644
index 0000000..feaaf71
--- /dev/null
+++ b/doc/master/save_ed_setup
@@ -0,0 +1,33 @@
+SYNOPSIS
+	int save_ed_setup(object who, int code)
+
+DESCRIPTION
+	Save individual option settings of the builtin ed, encoded
+	into code, for the user denoted by who. These functions are
+	located in the master object so that the local gods can decide
+	what strategy they want to use. suggestions:
+	A setup file for every user.
+		advantages:	transparent to the user
+				independent of user count
+		disadvantage:	extra file access at ed invocation
+	An array in the master object, users are searched by member_array
+		advantage:	easy to implement
+		disadvantage:	performance degradation with high user counts
+	An AVL-tree to access users by name
+		advantage:	can fit any need
+		disadvantage:	hard to implement, will need more
+				overhead on small and medium
+				installations than it can ever make
+				good by lg(usercount) complexity
+	Dedicated flags in every user object.
+		advantages:	easy to implement
+				independent of user count
+				Will also work for nusers w/o file
+				access privileges.
+		disadvantage:	care has to be taken to avoid
+				collision with other uses of the flags
+				in the user object
+
+SEE ALSO
+	ed(E), retrieve_ed_setup(M), valid_write(M),
+	get_ed_buffer_save_object_name(M)
diff --git a/doc/master/slow_shut_down b/doc/master/slow_shut_down
new file mode 100644
index 0000000..9a7fe8d
--- /dev/null
+++ b/doc/master/slow_shut_down
@@ -0,0 +1,40 @@
+SYNOPSIS
+	void slow_shut_down(int minutes)
+
+DESCRIPTION
+	Schedule a shutdown for the near future. minutes is the
+	desired time in minutes till the shutdown:
+	 six, if just the user reserve has been put into use.
+	 one, if the (smaller) master reserve has been put into use as
+	      well.
+
+	The interpreter calls this function when it runs low on
+	memory. At this time, it has freed its reserve, but since it
+	won't last long, the interpreter needs to be shut down. The
+	delay is to give the users the opportunity to finish their
+	current tasks, but don't take the 'minutes' for granted, just
+	deduce the urgency from it. 
+	It is possible that the driver may reallocate some memory
+	after the function has been called, and then run again into a
+	low memory situation, calling this function again.
+	This function might load an 'Armageddon' object and tell it
+	what to do. It is the Armageddon object then which performs
+	the shutdown.
+
+	Technical: The memory handling of the interpreter includes
+	three reserved areas: user, system and master. All three are
+	there to insure that the system shuts down gracefully when the
+	memory runs out: the user area to give the users time to
+	quit normally, the others to enable emergency-logouts when the
+	user reserve is used up as well.
+	The areas are allocated at start of the interpreter, and
+	released when no more memory could be obtained from the host.
+	In such a case, one of the remaining areas is freed (so the
+	operation can continue a short while) and a garbage collection
+	is initiated. If the garbage collection recycles enough memory
+	(either true garbage or by the aid of the quota_demon) to
+	reallocate the areas, all is fine, else the system shut down
+	is invoked by a call to this function. 
+
+SEE ALSO
+	quota_demon(M), notify_shutdown(M), shutdown(E), malloc(D), memory(C)
diff --git a/doc/master/stale_erq b/doc/master/stale_erq
new file mode 100644
index 0000000..a05ca31
--- /dev/null
+++ b/doc/master/stale_erq
@@ -0,0 +1,17 @@
+SYNOPSIS
+	void stale_erq (closure callback)
+
+DESCRIPTION
+	Notify the loss of the erq demon.
+	Argument is the callback closure set for an erq request.
+
+	If the erq connection dies prematurely, the driver will call
+	this lfun for every pending request with set callback. This
+	function should notify the originating object that the answer
+	will never arrive. 
+
+HISTORY
+	Introduced in 3.2.1@61.
+
+SEE ALSO
+	erq(C)
diff --git a/doc/master/valid_exec b/doc/master/valid_exec
new file mode 100644
index 0000000..2c0b67b
--- /dev/null
+++ b/doc/master/valid_exec
@@ -0,0 +1,15 @@
+SYNOPSIS
+	int valid_exec(string name, object ob, object obfrom)
+
+DESCRIPTION
+	Validate the rebinding of an IP connection by usage of efun
+	exec(). Arguments are the <name> of the _program_ attempting
+	to rebind the connection, the object <ob> to receive the
+	connection, and the object <obfrom> giving the connection.
+	Note that the program name is not the object_name() of the
+	object, and has no leading slash.
+
+	Return 0 to disallow the action, any other value to allow it.
+
+SEE ALSO
+	exec(E)
diff --git a/doc/master/valid_query_snoop b/doc/master/valid_query_snoop
new file mode 100644
index 0000000..e92378a
--- /dev/null
+++ b/doc/master/valid_query_snoop
@@ -0,0 +1,12 @@
+SYNOPSIS
+	valid_query_snoop(object ob)
+
+DESCRIPTION
+	Should return 1 if previous_object() (the one that called the
+	efun query_snoop()) is allowed to query wether ob is being
+	snooped, 0 if not.
+
+	The master object is always allowed to use query_snoop().
+
+SEE ALSO
+	valid_snoop(M), query_snoop(E), snoop(E)
diff --git a/doc/master/valid_read b/doc/master/valid_read
new file mode 100644
index 0000000..2dafbae
--- /dev/null
+++ b/doc/master/valid_read
@@ -0,0 +1,32 @@
+SYNOPSIS
+	string valid_read(string path, string uid, string func, object ob)
+
+DESCRIPTION
+	This function is called to check if the object ob with the
+	user-id uid has read permissions for the file given by path
+	for the operation named by func. It should return 0 if
+	permission is denied, or the normalized path if permission is
+	granted. You can also return 1 to indicate that the path can
+	be used unchanged.
+
+	The returned pathname must not contain ``..'', a leading /
+	will be stripped by the interpreter.
+
+	Func denotes the efun call or other operation that caused
+	valid_read() to be called:
+	ed_start (check if the file to be edited is readable),
+	file_size,
+	get_dir,
+	print_file (efun cat()),
+	read_bytes,
+	read_file,
+	restore_object,
+	tail.
+
+	Note that this function is called in compat mode as well. If
+	you need to be compatible with the old 2.4.5-mudlib, redirect
+	these calls to the valid_read/valid_write in the user
+	object.
+
+SEE ALSO
+	valid_write(M), make_path_absolute(M)
diff --git a/doc/master/valid_seteuid b/doc/master/valid_seteuid
new file mode 100644
index 0000000..3afc0d2
--- /dev/null
+++ b/doc/master/valid_seteuid
@@ -0,0 +1,9 @@
+SYNOPSIS
+	int valid_seteuid(object ob, string newid)
+
+DESCRIPTION
+	Should return 1 if ob is allowed to set its euid to newid.
+	Objects are always allowed to set their euid to 0.
+
+SEE ALSO
+	seteuid(E), uids(C)
diff --git a/doc/master/valid_snoop b/doc/master/valid_snoop
new file mode 100644
index 0000000..5bff987
--- /dev/null
+++ b/doc/master/valid_snoop
@@ -0,0 +1,8 @@
+SYNOPSIS
+	int valid_snoop(object me, object you)
+
+DESCRIPTION
+	Should return 1 if me is allowed to snoop you, 0 if not.
+
+SEE ALSO
+	snoop(E), query_snoop(E), valid_query_snoop(M)
diff --git a/doc/master/valid_write b/doc/master/valid_write
new file mode 100644
index 0000000..31fe43b
--- /dev/null
+++ b/doc/master/valid_write
@@ -0,0 +1,33 @@
+SYNOPSIS
+	string valid_write(string path, string uid, string func, object ob)
+
+DESCRIPTION
+	This function is called to check if the object ob with the
+	user-id uid has write permissions to the file given by path
+	for the operation named by func. It should return 0 if
+	permission is denied, or the normalized path if permission is
+	granted. You can also return 1 to indicate that the path can
+	be used unchanged.
+
+	The returned pathname must not contain ``..'', a leading /
+	will be stripped by the interpreter.
+
+	Func denotes the efun call or other operation that caused
+	valid_write() to be called:
+	cindent,
+	do_rename (efun rename(), for the old and then for the new name),
+	ed_start (whenever the builtin ed tries to write to a file),
+	mkdir,
+	remove_file (efun rm()),
+	rmdir,
+	save_object,
+	write_bytes,
+	write_file.
+
+	Note that this function is called in compat mode as well. If
+	you need to be compatible with the old 2.4.5-mudlib, redirect
+	these calls to the valid_read/valid_write in the user
+	object.
+
+SEE ALSO
+	valid_read(M), make_path_absolute(M)
diff --git a/doc/materials/MatDB b/doc/materials/MatDB
new file mode 100644
index 0000000..6536585
--- /dev/null
+++ b/doc/materials/MatDB
@@ -0,0 +1,42 @@
+Der Master hat (erstmal) kein AddMaterial mehr. Neue Materialien werden jetzt
+ganz einfach durch eine neue Datei im Verzeichnis mit den Materialien und den
+Aufruf der Update()-Funktion in der Datenbank erzeugt. Das gleiche gilt fuer
+neue Materialgruppen.
+Nach einem Update sollte man den Materialheader und die Doku neu generieren
+und einspielen, damit alles schoen konsistent bleibt (anders als bisher).
+Das Format der Materialdateien schaut man sich am besten am Beispiel an.
+
+Die alten Funktionen des Masters existieren weiterhin. Es gibt ein paar neue,
+um abhaengige Files zu erzeugen und die Datenbank neu aufzubauen:
+
+----------------------------------------
+
+void Update()
+
+Erneutes Einlesen der Materialdaten. Das Einlesen passiert asynchron, falls es
+zu lange dauert. Nach Abschluss des Einlesens werden die neuen Daten aktiv.
+
+----------------------------------------
+
+vargargs void GenMatList(string filename)
+
+Generiert Datei mit registrierten Materialien fuer die Dokumentation. Falls
+kein Dateiname angegeben wird, wird "materialliste" verwendet.
+
+----------------------------------------
+
+vargargs void GenMatGroupList(string filename)
+
+Generiert Datei mit registrierten Materialgruppen fuer die
+Dokumentation. Falls kein Dateiname angegeben wird, wird "materialgruppen"
+verwendet.
+
+----------------------------------------
+
+vargargs void GenHeaderFile(string filename)
+
+Generiert Datei mit Definitionen der moeglichen Materialien und Gruppen. Der
+Inhalt entspricht der bisherigen /sys/thing/material.h.  Wenn kein Dateiname
+angegeben wird, wird "material.h" verwendet.
+
+----------------------------------------
\ No newline at end of file
diff --git a/doc/materials/groups/MATGROUP_ACIDIC b/doc/materials/groups/MATGROUP_ACIDIC
new file mode 100644
index 0000000..bcfb8e7
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_ACIDIC
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_ACIDIC
+
+Name:
+Saeuren
diff --git a/doc/materials/groups/MATGROUP_BASIC b/doc/materials/groups/MATGROUP_BASIC
new file mode 100644
index 0000000..5d5b208
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_BASIC
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_BASIC
+
+Name:
+Laugen
diff --git a/doc/materials/groups/MATGROUP_BIO b/doc/materials/groups/MATGROUP_BIO
new file mode 100644
index 0000000..b4ff899
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_BIO
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_BIO
+
+Name:
+biologische Materialien
diff --git a/doc/materials/groups/MATGROUP_CLOTH b/doc/materials/groups/MATGROUP_CLOTH
new file mode 100644
index 0000000..be4ed49
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_CLOTH
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_CLOTH
+
+Name:
+Stoffe
diff --git a/doc/materials/groups/MATGROUP_CONIFER_WOOD b/doc/materials/groups/MATGROUP_CONIFER_WOOD
new file mode 100644
index 0000000..75ff5b3
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_CONIFER_WOOD
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_CONIFER_WOOD
+
+Name:
+Nadelhoelzer
diff --git a/doc/materials/groups/MATGROUP_DEAD b/doc/materials/groups/MATGROUP_DEAD
new file mode 100644
index 0000000..89e33a7
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_DEAD
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_DEAD
+
+Name:
+Ueberreste von Lebewesen
diff --git a/doc/materials/groups/MATGROUP_DECIDUOUS_WOOD b/doc/materials/groups/MATGROUP_DECIDUOUS_WOOD
new file mode 100644
index 0000000..579c4dc
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_DECIDUOUS_WOOD
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_DECIDUOUS_WOOD
+
+Name:
+Laubhoelzer
diff --git a/doc/materials/groups/MATGROUP_DRUG b/doc/materials/groups/MATGROUP_DRUG
new file mode 100644
index 0000000..a59b232
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_DRUG
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_DRUG
+
+Name:
+Drogen und Heiltraenke
diff --git a/doc/materials/groups/MATGROUP_EATABLE b/doc/materials/groups/MATGROUP_EATABLE
new file mode 100644
index 0000000..354edd6
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_EATABLE
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_EATABLE
+
+Name:
+Essbares
diff --git a/doc/materials/groups/MATGROUP_ELECTRICAL b/doc/materials/groups/MATGROUP_ELECTRICAL
new file mode 100644
index 0000000..04d3bb6
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_ELECTRICAL
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_ELECTRICAL
+
+Name:
+geladene Materialien
diff --git a/doc/materials/groups/MATGROUP_ELEMENTAL b/doc/materials/groups/MATGROUP_ELEMENTAL
new file mode 100644
index 0000000..d923437
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_ELEMENTAL
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_ELEMENTAL
+
+Name:
+(altertuemliche) Elemente
diff --git a/doc/materials/groups/MATGROUP_EXPLOSIVE b/doc/materials/groups/MATGROUP_EXPLOSIVE
new file mode 100644
index 0000000..3f4d208
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_EXPLOSIVE
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_EXPLOSIVE
+
+Name:
+Sprengstoffe
diff --git a/doc/materials/groups/MATGROUP_FLEXIBLE b/doc/materials/groups/MATGROUP_FLEXIBLE
new file mode 100644
index 0000000..b91143d
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_FLEXIBLE
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_FLEXIBLE
+
+Name:
+biegsame Materialien
diff --git a/doc/materials/groups/MATGROUP_FLUID b/doc/materials/groups/MATGROUP_FLUID
new file mode 100644
index 0000000..13f74f6
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_FLUID
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_FLUID
+
+Name:
+Fluessigkeiten
diff --git a/doc/materials/groups/MATGROUP_GAS b/doc/materials/groups/MATGROUP_GAS
new file mode 100644
index 0000000..f1d2436
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_GAS
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_GAS
+
+Name:
+Gase
diff --git a/doc/materials/groups/MATGROUP_HERBAL b/doc/materials/groups/MATGROUP_HERBAL
new file mode 100644
index 0000000..047b408
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_HERBAL
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_HERBAL
+
+Name:
+pflanzliche Materialien
diff --git a/doc/materials/groups/MATGROUP_HOLY b/doc/materials/groups/MATGROUP_HOLY
new file mode 100644
index 0000000..8609c2c
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_HOLY
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_HOLY
+
+Name:
+heilige Materialien
diff --git a/doc/materials/groups/MATGROUP_INFLAMMABLE b/doc/materials/groups/MATGROUP_INFLAMMABLE
new file mode 100644
index 0000000..3e87537
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_INFLAMMABLE
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_INFLAMMABLE
+
+Name:
+entflammbare Materialien
diff --git a/doc/materials/groups/MATGROUP_INVIS b/doc/materials/groups/MATGROUP_INVIS
new file mode 100644
index 0000000..741baef
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_INVIS
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_INVIS
+
+Name:
+unsichtbare Materialien
diff --git a/doc/materials/groups/MATGROUP_JEWEL b/doc/materials/groups/MATGROUP_JEWEL
new file mode 100644
index 0000000..7aab392
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_JEWEL
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_JEWEL
+
+Name:
+Edelsteine
diff --git a/doc/materials/groups/MATGROUP_LIVING b/doc/materials/groups/MATGROUP_LIVING
new file mode 100644
index 0000000..6c02462
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_LIVING
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_LIVING
+
+Name:
+Lebewesen
diff --git a/doc/materials/groups/MATGROUP_MAGIC b/doc/materials/groups/MATGROUP_MAGIC
new file mode 100644
index 0000000..20b8287
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_MAGIC
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_MAGIC
+
+Name:
+Magisches
diff --git a/doc/materials/groups/MATGROUP_MAGNETIC b/doc/materials/groups/MATGROUP_MAGNETIC
new file mode 100644
index 0000000..d43c3b3
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_MAGNETIC
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_MAGNETIC
+
+Name:
+anziehende Materialien
diff --git a/doc/materials/groups/MATGROUP_METAL b/doc/materials/groups/MATGROUP_METAL
new file mode 100644
index 0000000..f3979af
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_METAL
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_METAL
+
+Name:
+Metalle
diff --git a/doc/materials/groups/MATGROUP_MINERAL b/doc/materials/groups/MATGROUP_MINERAL
new file mode 100644
index 0000000..31eda4c
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_MINERAL
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_MINERAL
+
+Name:
+Mineralien
diff --git a/doc/materials/groups/MATGROUP_MISC b/doc/materials/groups/MATGROUP_MISC
new file mode 100644
index 0000000..b19c7d9
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_MISC
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_MISC
+
+Name:
+verschiedene Materialien, die nicht naeher zugeordnet sind (als fest, fluessig, gasfoermig)
diff --git a/doc/materials/groups/MATGROUP_PAPER b/doc/materials/groups/MATGROUP_PAPER
new file mode 100644
index 0000000..f8ed507
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_PAPER
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_PAPER
+
+Name:
+Papieraehliches
diff --git a/doc/materials/groups/MATGROUP_POISONOUS b/doc/materials/groups/MATGROUP_POISONOUS
new file mode 100644
index 0000000..1f2e26c
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_POISONOUS
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_POISONOUS
+
+Name:
+Gifte
diff --git a/doc/materials/groups/MATGROUP_PRECIOUS_METAL b/doc/materials/groups/MATGROUP_PRECIOUS_METAL
new file mode 100644
index 0000000..3d0fb1a
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_PRECIOUS_METAL
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_PRECIOUS_METAL
+
+Name:
+Edelmetalle
diff --git a/doc/materials/groups/MATGROUP_RADIOACTIVE b/doc/materials/groups/MATGROUP_RADIOACTIVE
new file mode 100644
index 0000000..1f2be19
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_RADIOACTIVE
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_RADIOACTIVE
+
+Name:
+Unbeschreibliches
diff --git a/doc/materials/groups/MATGROUP_SOLID b/doc/materials/groups/MATGROUP_SOLID
new file mode 100644
index 0000000..3c01ddb
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_SOLID
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_SOLID
+
+Name:
+feste Materialien
diff --git a/doc/materials/groups/MATGROUP_STONE b/doc/materials/groups/MATGROUP_STONE
new file mode 100644
index 0000000..7a76e5c
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_STONE
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_STONE
+
+Name:
+Steine
diff --git a/doc/materials/groups/MATGROUP_TROPICAL_WOOD b/doc/materials/groups/MATGROUP_TROPICAL_WOOD
new file mode 100644
index 0000000..02fa072
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_TROPICAL_WOOD
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_TROPICAL_WOOD
+
+Name:
+Tropenhoelzer
diff --git a/doc/materials/groups/MATGROUP_UNHOLY b/doc/materials/groups/MATGROUP_UNHOLY
new file mode 100644
index 0000000..44d961b
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_UNHOLY
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_UNHOLY
+
+Name:
+daemonische Materialien
diff --git a/doc/materials/groups/MATGROUP_WOOD b/doc/materials/groups/MATGROUP_WOOD
new file mode 100644
index 0000000..5466d1d
--- /dev/null
+++ b/doc/materials/groups/MATGROUP_WOOD
@@ -0,0 +1,5 @@
+Gruppenid:
+MATGROUP_WOOD
+
+Name:
+Hoelzer
diff --git a/doc/materials/materials/MAT_ADAMANT b/doc/materials/materials/MAT_ADAMANT
new file mode 100644
index 0000000..ebb435c
--- /dev/null
+++ b/doc/materials/materials/MAT_ADAMANT
@@ -0,0 +1,20 @@
+Materialid:
+MAT_ADAMANT              "adamant"
+
+Name:
+"Adamant" "Adamants" "Adamant" "Adamant"
+
+Geschlecht:
+N
+
+Beschreibung:
+hartes Elfenmetall
+
+Erkennbarkeit:
+MAT_MISC_METAL:10
+MAT_ADAMANT
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_PRECIOUS_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_AGATE b/doc/materials/materials/MAT_AGATE
new file mode 100644
index 0000000..e5456c6
--- /dev/null
+++ b/doc/materials/materials/MAT_AGATE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_AGATE                "agate"
+
+Name:
+"Achat" "Achats" "Achat" "Achat"
+
+Geschlecht:
+M
+
+Beschreibung:
+Farbspiel
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:0
+MAT_QUARTZ:20
+MAT_AGATE
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_AIR b/doc/materials/materials/MAT_AIR
new file mode 100644
index 0000000..a9d4a80
--- /dev/null
+++ b/doc/materials/materials/MAT_AIR
@@ -0,0 +1,19 @@
+Materialid:
+MAT_AIR                  "air"
+
+Name:
+"Luft" "Luft" "Luft" "Luft"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_GAS:100
+MATGROUP_INVIS:100
+MATGROUP_ELEMENTAL:100
diff --git a/doc/materials/materials/MAT_ALCOHOL b/doc/materials/materials/MAT_ALCOHOL
new file mode 100644
index 0000000..4ef80d4
--- /dev/null
+++ b/doc/materials/materials/MAT_ALCOHOL
@@ -0,0 +1,18 @@
+Materialid:
+MAT_ALCOHOL              "alcohol"
+
+Name:
+"Alkohol" "Alkohols" "Alkohol" "Alkohol"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_FLUID:100
diff --git a/doc/materials/materials/MAT_ALUMINIUM b/doc/materials/materials/MAT_ALUMINIUM
new file mode 100644
index 0000000..ecb282d
--- /dev/null
+++ b/doc/materials/materials/MAT_ALUMINIUM
@@ -0,0 +1,20 @@
+Materialid:
+MAT_ALUMINIUM            "Al"
+
+Name:
+"Aluminium" "Aluminiums" "Aluminium" "Aluminium"
+
+Geschlecht:
+N
+
+Beschreibung:
+weich behandelt hart
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_SILVER:15
+MAT_ALUMINIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_AMBER b/doc/materials/materials/MAT_AMBER
new file mode 100644
index 0000000..eca9ee0
--- /dev/null
+++ b/doc/materials/materials/MAT_AMBER
@@ -0,0 +1,22 @@
+Materialid:
+MAT_AMBER                "amber"
+
+Name:
+"Bernstein" "Bernsteins" "Bernstein" "Bernstein"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_JEWEL:100
+MATGROUP_BIO:100
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
+MATGROUP_DEAD:100
diff --git a/doc/materials/materials/MAT_AMETHYST b/doc/materials/materials/MAT_AMETHYST
new file mode 100644
index 0000000..86cdd53
--- /dev/null
+++ b/doc/materials/materials/MAT_AMETHYST
@@ -0,0 +1,21 @@
+Materialid:
+MAT_AMETHYST             "amethyst"
+
+Name:
+"Amethyst" "Amethysts" "Amethyst" "Amethyst"
+
+Geschlecht:
+M
+
+Beschreibung:
+violett (Quarz)
+
+Erkennbarkeit:
+MAT_MISC_JEWEL:25
+MAT_AMETHYST
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_ANTIMON b/doc/materials/materials/MAT_ANTIMON
new file mode 100644
index 0000000..12311f7
--- /dev/null
+++ b/doc/materials/materials/MAT_ANTIMON
@@ -0,0 +1,20 @@
+Materialid:
+MAT_ANTIMON              "antimon"
+
+Name:
+"Antimon" "Antimons" "Antimon" "Antimon"
+
+Geschlecht:
+N
+
+Beschreibung:
+weich, sproede, silberweiss
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_SILVER:15
+MAT_ANTIMON
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_AQUAMARINE b/doc/materials/materials/MAT_AQUAMARINE
new file mode 100644
index 0000000..e401412
--- /dev/null
+++ b/doc/materials/materials/MAT_AQUAMARINE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_AQUAMARINE           "aquamarine"
+
+Name:
+"Aquamarin" "Aquamarins" "Aquamarin" "Aquamarin"
+
+Geschlecht:
+M
+
+Beschreibung:
+blau
+
+Erkennbarkeit:
+MAT_MISC_JEWEL:25
+MAT_AQUAMARINE
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_ARSENIC b/doc/materials/materials/MAT_ARSENIC
new file mode 100644
index 0000000..ef12eb7
--- /dev/null
+++ b/doc/materials/materials/MAT_ARSENIC
@@ -0,0 +1,21 @@
+Materialid:
+MAT_ARSENIC              "arsenic"
+
+Name:
+"Arsen" "Arsens" "Arsen" "Arsen"
+
+Geschlecht:
+N
+
+Beschreibung:
+giftiges Halbmetall
+
+Erkennbarkeit:
+MAT_SALT:50
+MAT_MISC_METAL:66
+MAT_ARSENIC
+
+Gruppenzugehoerigkeit:
+MATGROUP_POISONOUS:100
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_ASBESTOS b/doc/materials/materials/MAT_ASBESTOS
new file mode 100644
index 0000000..215b546
--- /dev/null
+++ b/doc/materials/materials/MAT_ASBESTOS
@@ -0,0 +1,20 @@
+Materialid:
+MAT_ASBESTOS             "asbestos"
+
+Name:
+"Asbest" "Asbests" "Asbest" "Asbest"
+
+Geschlecht:
+M
+
+Beschreibung:
+grauweiss
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_POISONOUS:100
+MATGROUP_MINERAL:100
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_ASH_WOOD b/doc/materials/materials/MAT_ASH_WOOD
new file mode 100644
index 0000000..531c1df
--- /dev/null
+++ b/doc/materials/materials/MAT_ASH_WOOD
@@ -0,0 +1,26 @@
+Materialid:
+MAT_ASH_WOOD             "ash_wood"
+
+Name:
+"Eschenholz" "Eschenholzes" "Eschenholz" "Eschenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+flexibel, magisch
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:15
+MAT_ASH_WOOD
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_MAGIC:100
+MATGROUP_BIO:100
+MATGROUP_HERBAL:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
+MATGROUP_DEAD:100
diff --git a/doc/materials/materials/MAT_ASPHALT b/doc/materials/materials/MAT_ASPHALT
new file mode 100644
index 0000000..8b03e4f
--- /dev/null
+++ b/doc/materials/materials/MAT_ASPHALT
@@ -0,0 +1,18 @@
+Materialid:
+MAT_ASPHALT              "asphalt"
+
+Name:
+"Asphalt" "Asphalts" "Asphalt" "Asphalt"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_ASPIRIN b/doc/materials/materials/MAT_ASPIRIN
new file mode 100644
index 0000000..5a7a6cc
--- /dev/null
+++ b/doc/materials/materials/MAT_ASPIRIN
@@ -0,0 +1,19 @@
+Materialid:
+MAT_ASPIRIN              "aspirin"
+
+Name:
+"Aspirin" "Aspirins" "Aspirin" "Aspirin"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_SALT:50
+MAT_ASPIRIN
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
+MATGROUP_DRUG:100
diff --git a/doc/materials/materials/MAT_BALSA b/doc/materials/materials/MAT_BALSA
new file mode 100644
index 0000000..963af4a
--- /dev/null
+++ b/doc/materials/materials/MAT_BALSA
@@ -0,0 +1,23 @@
+Materialid:
+MAT_BALSA                "balsa"
+
+Name:
+"Balsaholz" "Balsaholzes" "Balsaholz" "Balsaholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+weicher/leichter als Kork
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_HERBAL:100
+MATGROUP_TROPICAL_WOOD:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
+MATGROUP_DEAD:100
diff --git a/doc/materials/materials/MAT_BARYT b/doc/materials/materials/MAT_BARYT
new file mode 100644
index 0000000..516ffa2
--- /dev/null
+++ b/doc/materials/materials/MAT_BARYT
@@ -0,0 +1,21 @@
+Materialid:
+MAT_BARYT                "baryt"
+
+Name:
+"Schwerspat" "Schwerspates" "Schwerspat" "Schwerspat"
+
+Geschlecht:
+M
+
+Beschreibung:
+weiss, hohe Dichte
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:33
+MAT_ORTHOKLAS:50
+MAT_BARYT
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_BASALT b/doc/materials/materials/MAT_BASALT
new file mode 100644
index 0000000..c858830
--- /dev/null
+++ b/doc/materials/materials/MAT_BASALT
@@ -0,0 +1,18 @@
+Materialid:
+MAT_BASALT               "basalt"
+
+Name:
+"Basalt" "Basalts" "Basalt" "Basalt"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_BEECH b/doc/materials/materials/MAT_BEECH
new file mode 100644
index 0000000..1eaf815
--- /dev/null
+++ b/doc/materials/materials/MAT_BEECH
@@ -0,0 +1,24 @@
+Materialid:
+MAT_BEECH                "beech"
+
+Name:
+"Buchenholz" "Buchenholzes" "Buchenholz" "Buchenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+hart, roetlich
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:5
+MAT_BEECH
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
+MATGROUP_DEAD:100
diff --git a/doc/materials/materials/MAT_BIRCH b/doc/materials/materials/MAT_BIRCH
new file mode 100644
index 0000000..7b950c7
--- /dev/null
+++ b/doc/materials/materials/MAT_BIRCH
@@ -0,0 +1,24 @@
+Materialid:
+MAT_BIRCH                "birch"
+
+Name:
+"Birkenholz" "Birkenholzes" "Birkenholz" "Birkenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+magisch, zaeh, suess
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:5
+MAT_BIRCH
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
+MATGROUP_DEAD:100
diff --git a/doc/materials/materials/MAT_BLOOD b/doc/materials/materials/MAT_BLOOD
new file mode 100644
index 0000000..3f4b2b1
--- /dev/null
+++ b/doc/materials/materials/MAT_BLOOD
@@ -0,0 +1,19 @@
+Materialid:
+MAT_BLOOD                "blood"
+
+Name:
+"Blut" "Bluts" "Blut" "Blut"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_BIO:100
+MATGROUP_FLUID:100
+MATGROUP_DEAD:100
diff --git a/doc/materials/materials/MAT_BONE b/doc/materials/materials/MAT_BONE
new file mode 100644
index 0000000..3800823
--- /dev/null
+++ b/doc/materials/materials/MAT_BONE
@@ -0,0 +1,19 @@
+Materialid:
+MAT_BONE                 "bone"
+
+Name:
+"Knochen" "Knochens" "Knochen" "Knochen"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
+MATGROUP_DEAD:100
diff --git a/doc/materials/materials/MAT_BRASS b/doc/materials/materials/MAT_BRASS
new file mode 100644
index 0000000..de2f4d7
--- /dev/null
+++ b/doc/materials/materials/MAT_BRASS
@@ -0,0 +1,20 @@
+Materialid:
+MAT_BRASS                "brass"
+
+Name:
+"Messing" "Messings" "Messing" "Messing"
+
+Geschlecht:
+N
+
+Beschreibung:
+bis 80% Kupfer, Rest Zink
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_GOLD:15
+MAT_BRASS
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_BRONCE b/doc/materials/materials/MAT_BRONCE
new file mode 100644
index 0000000..657d24e
--- /dev/null
+++ b/doc/materials/materials/MAT_BRONCE
@@ -0,0 +1,18 @@
+Materialid:
+MAT_BRONCE               "bronce"
+
+Name:
+"Bronze" "Bronze" "Bronze" "Bronze"
+
+Geschlecht:
+F
+
+Beschreibung:
+Kupferlegierung (mit Zinn)
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_BRUYERE b/doc/materials/materials/MAT_BRUYERE
new file mode 100644
index 0000000..479500a
--- /dev/null
+++ b/doc/materials/materials/MAT_BRUYERE
@@ -0,0 +1,24 @@
+Materialid:
+MAT_BRUYERE              "bruyere"
+
+Name:
+"Bruyereholz" "Bruyereholzes" "Bruyereholz" "Bruyereholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+sehr hart und zaeh, fuer Pfeifen
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:5
+MAT_BRUYERE
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
+MATGROUP_DEAD:100
diff --git a/doc/materials/materials/MAT_CANNABIS b/doc/materials/materials/MAT_CANNABIS
new file mode 100644
index 0000000..a81cd39
--- /dev/null
+++ b/doc/materials/materials/MAT_CANNABIS
@@ -0,0 +1,21 @@
+Materialid:
+MAT_CANNABIS             "cannabis"
+
+Name:
+"Hanf" "Hanfs" "Hanf" "Hanf"
+
+Geschlecht:
+M
+
+Beschreibung:
+auch THC
+
+Erkennbarkeit:
+MAT_MISC_HERBAL:33
+MAT_CANNABIS
+
+Gruppenzugehoerigkeit:
+MATGROUP_HERBAL:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
+MATGROUP_DRUG:100
diff --git a/doc/materials/materials/MAT_CARBON b/doc/materials/materials/MAT_CARBON
new file mode 100644
index 0000000..05a05a2
--- /dev/null
+++ b/doc/materials/materials/MAT_CARBON
@@ -0,0 +1,18 @@
+Materialid:
+MAT_CARBON               "C"
+
+Name:
+"Kohlenstoff" "Kohlenstoffs" "Kohlenstoff" "Kohlenstoff"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_CARTON b/doc/materials/materials/MAT_CARTON
new file mode 100644
index 0000000..68c836c
--- /dev/null
+++ b/doc/materials/materials/MAT_CARTON
@@ -0,0 +1,20 @@
+Materialid:
+MAT_CARTON               "carton"
+
+Name:
+"Pappe" "Pappe" "Pappe" "Pappe"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_PAPER:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_CEDAR b/doc/materials/materials/MAT_CEDAR
new file mode 100644
index 0000000..cc6ee93
--- /dev/null
+++ b/doc/materials/materials/MAT_CEDAR
@@ -0,0 +1,25 @@
+Materialid:
+MAT_CEDAR                "cedar"
+
+Name:
+"Zedernholz" "Zedernholzes" "Zedernholz" "Zedernholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+magisch und weich
+
+Erkennbarkeit:
+MAT_MISC_CONIFER_WOOD:5
+MAT_CEDAR
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_MAGIC:100
+MATGROUP_BIO:100
+MATGROUP_HERBAL:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
+MATGROUP_DEAD:100
+MATGROUP_CONIFER_WOOD:100
diff --git a/doc/materials/materials/MAT_CERAMIC b/doc/materials/materials/MAT_CERAMIC
new file mode 100644
index 0000000..4f71b1c
--- /dev/null
+++ b/doc/materials/materials/MAT_CERAMIC
@@ -0,0 +1,17 @@
+Materialid:
+MAT_CERAMIC              "ceramic"
+
+Name:
+"Keramik" "Keramik" "Keramik" "Keramik"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_CHALK b/doc/materials/materials/MAT_CHALK
new file mode 100644
index 0000000..5775e91
--- /dev/null
+++ b/doc/materials/materials/MAT_CHALK
@@ -0,0 +1,17 @@
+Materialid:
+MAT_CHALK                "chalk"
+
+Name:
+"Kreide" "Kreide" "Kreide" "Kreide"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_CHEESE b/doc/materials/materials/MAT_CHEESE
new file mode 100644
index 0000000..143d544
--- /dev/null
+++ b/doc/materials/materials/MAT_CHEESE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_CHEESE             "cheese"
+
+Name:
+"Kaese" "Kaeses" "Kaese" "Kaese"
+
+Geschlecht:
+M
+
+Beschreibung:
+weich, essbar
+
+Erkennbarkeit:
+MATGROUP_EATABLE:0
+MATGROUP_BIO:0
+MAT_CHEESE
+
+Gruppenzugehoerigkeit:
+MATGROUP_EATABLE:100
+MATGROUP_SOLID:100
+MATGROUP_BIO:100
diff --git a/doc/materials/materials/MAT_CHIFFON b/doc/materials/materials/MAT_CHIFFON
new file mode 100644
index 0000000..2995a4a
--- /dev/null
+++ b/doc/materials/materials/MAT_CHIFFON
@@ -0,0 +1,21 @@
+Materialid:
+MAT_CHIFFON              "chiffon"
+
+Name:
+"Chiffon" "Chiffons" "Chiffon" "Chiffon"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
+MATGROUP_CLOTH:100
diff --git a/doc/materials/materials/MAT_CHITIN b/doc/materials/materials/MAT_CHITIN
new file mode 100644
index 0000000..081e65d
--- /dev/null
+++ b/doc/materials/materials/MAT_CHITIN
@@ -0,0 +1,19 @@
+Materialid:
+MAT_CHITIN               "chitin"
+
+Name:
+"Chitin" "Chitins" "Chitin" "Chitin"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
+MATGROUP_DEAD:100
diff --git a/doc/materials/materials/MAT_CHLOR b/doc/materials/materials/MAT_CHLOR
new file mode 100644
index 0000000..f32ea88
--- /dev/null
+++ b/doc/materials/materials/MAT_CHLOR
@@ -0,0 +1,20 @@
+Materialid:
+MAT_CHLOR                "chlor"
+
+Name:
+"Chlorgas" "Chlorgases" "Chlorgas" "Chlorgas"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_GAS:20
+MAT_CHLOR
+
+Gruppenzugehoerigkeit:
+MATGROUP_POISONOUS:100
+MATGROUP_GAS:100
+MATGROUP_INVIS:100
diff --git a/doc/materials/materials/MAT_CHROMIUM b/doc/materials/materials/MAT_CHROMIUM
new file mode 100644
index 0000000..aa1d15f
--- /dev/null
+++ b/doc/materials/materials/MAT_CHROMIUM
@@ -0,0 +1,20 @@
+Materialid:
+MAT_CHROMIUM             "chromium"
+
+Name:
+"Chrom" "Chroms" "Chrom" "Chrom"
+
+Geschlecht:
+N
+
+Beschreibung:
+silberweiss, weich, zaeh
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_SILVER:15
+MAT_CHROMIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_CINNABAR b/doc/materials/materials/MAT_CINNABAR
new file mode 100644
index 0000000..2a519ea
--- /dev/null
+++ b/doc/materials/materials/MAT_CINNABAR
@@ -0,0 +1,20 @@
+Materialid:
+MAT_CINNABAR             "cinnabar"
+
+Name:
+"Zinnober" "Zinnobers" "Zinnober" "Zinnober"
+
+Geschlecht:
+N
+
+Beschreibung:
+bleigrau bis scharlachrot
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:20
+MAT_CINNABAR
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_CLAY b/doc/materials/materials/MAT_CLAY
new file mode 100644
index 0000000..ea9795d
--- /dev/null
+++ b/doc/materials/materials/MAT_CLAY
@@ -0,0 +1,17 @@
+Materialid:
+MAT_CLAY                 "clay"
+
+Name:
+"Ton" "Tons" "Ton" "Ton"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_CLOTH b/doc/materials/materials/MAT_CLOTH
new file mode 100644
index 0000000..83a4d15
--- /dev/null
+++ b/doc/materials/materials/MAT_CLOTH
@@ -0,0 +1,21 @@
+Materialid:
+MAT_CLOTH                "cloth"
+
+Name:
+"Stoff" "Stoffes" "Stoff" "Stoff"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_CLOTH:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_COBALT b/doc/materials/materials/MAT_COBALT
new file mode 100644
index 0000000..513b32a
--- /dev/null
+++ b/doc/materials/materials/MAT_COBALT
@@ -0,0 +1,20 @@
+Materialid:
+MAT_COBALT               "Co"
+
+Name:
+"Cobalt" "Cobalts" "Cobalt" "Cobalt"
+
+Geschlecht:
+N
+
+Beschreibung:
+hartes, sproedes Legierungmet.
+
+Erkennbarkeit:
+MAT_MISC_METAL:25
+MAT_COBALT
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_MAGNETIC:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_COCAINE b/doc/materials/materials/MAT_COCAINE
new file mode 100644
index 0000000..d69d413
--- /dev/null
+++ b/doc/materials/materials/MAT_COCAINE
@@ -0,0 +1,19 @@
+Materialid:
+MAT_COCAINE              "cocaine"
+
+Name:
+"Kokain" "Kokains" "Kokain" "Kokain"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_CHALK:50
+MAT_COCAINE
+
+Gruppenzugehoerigkeit:
+MATGROUP_DRUG:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_CONCRETE b/doc/materials/materials/MAT_CONCRETE
new file mode 100644
index 0000000..bb711cc
--- /dev/null
+++ b/doc/materials/materials/MAT_CONCRETE
@@ -0,0 +1,18 @@
+Materialid:
+MAT_CONCRETE             "concrete"
+
+Name:
+"Beton" "Betons" "Beton" "Beton"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_COPPER b/doc/materials/materials/MAT_COPPER
new file mode 100644
index 0000000..7c351a6
--- /dev/null
+++ b/doc/materials/materials/MAT_COPPER
@@ -0,0 +1,19 @@
+Materialid:
+MAT_COPPER               "Cu"
+
+Name:
+"Kupfer" "Kupfers" "Kupfer" "Kupfer"
+
+Geschlecht:
+N
+
+Beschreibung:
+weich, rot
+
+Erkennbarkeit:
+MAT_MISC_METAL:5
+MAT_COPPER
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_CORK b/doc/materials/materials/MAT_CORK
new file mode 100644
index 0000000..a4fc3c6
--- /dev/null
+++ b/doc/materials/materials/MAT_CORK
@@ -0,0 +1,22 @@
+Materialid:
+MAT_CORK                 "cork"
+
+Name:
+"Kork" "Korks" "Kork" "Kork"
+
+Geschlecht:
+M
+
+Beschreibung:
+weich
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_COTTON b/doc/materials/materials/MAT_COTTON
new file mode 100644
index 0000000..a2d94ca
--- /dev/null
+++ b/doc/materials/materials/MAT_COTTON
@@ -0,0 +1,21 @@
+Materialid:
+MAT_COTTON               "cotton"
+
+Name:
+"Baumwolle" "Baumwolle" "Baumwolle" "Baumwolle"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_CLOTH:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_CRYSTAL b/doc/materials/materials/MAT_CRYSTAL
new file mode 100644
index 0000000..aab2eb5
--- /dev/null
+++ b/doc/materials/materials/MAT_CRYSTAL
@@ -0,0 +1,19 @@
+Materialid:
+MAT_CRYSTAL              "crystal"
+
+Name:
+"Kristall" "Kristalls" "Kristall" "Kristall"
+
+Geschlecht:
+M
+
+Beschreibung:
+klar bis weiss
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_DETONATING_GAS b/doc/materials/materials/MAT_DETONATING_GAS
new file mode 100644
index 0000000..80a88e5
--- /dev/null
+++ b/doc/materials/materials/MAT_DETONATING_GAS
@@ -0,0 +1,21 @@
+Materialid:
+MAT_DETONATING_GAS       "oxyhydrogen"
+
+Name:
+"Knallgas" "Knallgases" "Knallgas" "Knallgas"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_GAS:20
+MAT_DETONATING_GAS
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_GAS:100
+MATGROUP_EXPLOSIVE:100
+MATGROUP_INVIS:100
diff --git a/doc/materials/materials/MAT_DIAMOND b/doc/materials/materials/MAT_DIAMOND
new file mode 100644
index 0000000..b9f9fc0
--- /dev/null
+++ b/doc/materials/materials/MAT_DIAMOND
@@ -0,0 +1,22 @@
+Materialid:
+MAT_DIAMOND              "diamond"
+
+Name:
+"Diamant" "Diamants" "Diamant" "Diamant"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_JEWEL:0
+MAT_CRYSTAL:25
+MAT_DIAMOND
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_DUST b/doc/materials/materials/MAT_DUST
new file mode 100644
index 0000000..d5a5c49
--- /dev/null
+++ b/doc/materials/materials/MAT_DUST
@@ -0,0 +1,17 @@
+Materialid:
+MAT_DUST                 "dust"
+
+Name:
+"Staub" "Staubs" "Staub" "Staub"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_EARTH b/doc/materials/materials/MAT_EARTH
new file mode 100644
index 0000000..8ff28e0
--- /dev/null
+++ b/doc/materials/materials/MAT_EARTH
@@ -0,0 +1,18 @@
+Materialid:
+MAT_EARTH                "earth"
+
+Name:
+"Erde" "Erde" "Erde" "Erde"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_ELEMENTAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_EBONY b/doc/materials/materials/MAT_EBONY
new file mode 100644
index 0000000..933ce66
--- /dev/null
+++ b/doc/materials/materials/MAT_EBONY
@@ -0,0 +1,24 @@
+Materialid:
+MAT_EBONY                "ebony"
+
+Name:
+"Ebenholz" "Ebenholzes" "Ebenholz" "Ebenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+schwarz
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:15
+MAT_EBONY
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_ELEKTRON b/doc/materials/materials/MAT_ELEKTRON
new file mode 100644
index 0000000..11d64f9
--- /dev/null
+++ b/doc/materials/materials/MAT_ELEKTRON
@@ -0,0 +1,20 @@
+Materialid:
+MAT_ELEKTRON             "elektron"
+
+Name:
+"Elektron" "Elektrons" "Elektron" "Elektron"
+
+Geschlecht:
+N
+
+Beschreibung:
+bis 75% Gold, Rest Silber
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_GOLD:66
+MAT_ELEKTRON
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_EMERALD b/doc/materials/materials/MAT_EMERALD
new file mode 100644
index 0000000..daad2c0
--- /dev/null
+++ b/doc/materials/materials/MAT_EMERALD
@@ -0,0 +1,21 @@
+Materialid:
+MAT_EMERALD              "emerald"
+
+Name:
+"Smaragd" "Smaragds" "Smaragd" "Smaragd"
+
+Geschlecht:
+M
+
+Beschreibung:
+gruen
+
+Erkennbarkeit:
+MAT_MISC_JEWEL:25
+MAT_EMERALD
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_FAT b/doc/materials/materials/MAT_FAT
new file mode 100644
index 0000000..98e2a03
--- /dev/null
+++ b/doc/materials/materials/MAT_FAT
@@ -0,0 +1,22 @@
+Materialid:
+MAT_FAT                  "fat"
+
+Name:
+"Fett" "Fetts" "Fett" "Fett"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_FOOD:-50
+MAT_FAT
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_EATABLE:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_FEATHER b/doc/materials/materials/MAT_FEATHER
new file mode 100644
index 0000000..1f51ecc
--- /dev/null
+++ b/doc/materials/materials/MAT_FEATHER
@@ -0,0 +1,21 @@
+Materialid:
+MAT_FEATHER              "feather"
+
+Name:
+"Federn" "Federn" "Federn" "Federn"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_FELT b/doc/materials/materials/MAT_FELT
new file mode 100644
index 0000000..ead7d49
--- /dev/null
+++ b/doc/materials/materials/MAT_FELT
@@ -0,0 +1,21 @@
+Materialid:
+MAT_FELT                 "felt"
+
+Name:
+"Filz" "Filzes" "Filz" "Filz"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_CLOTH:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_FIR b/doc/materials/materials/MAT_FIR
new file mode 100644
index 0000000..0fd017e
--- /dev/null
+++ b/doc/materials/materials/MAT_FIR
@@ -0,0 +1,24 @@
+Materialid:
+MAT_FIR                  "fir"
+
+Name:
+"Tannenholz" "Tannenholzes" "Tannenholz" "Tannenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+trocken (Douglasie)
+
+Erkennbarkeit:
+MAT_MISC_CONIFER_WOOD:15
+MAT_FIR
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_CONIFER_WOOD:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_FIRE b/doc/materials/materials/MAT_FIRE
new file mode 100644
index 0000000..81b94aa
--- /dev/null
+++ b/doc/materials/materials/MAT_FIRE
@@ -0,0 +1,19 @@
+Materialid:
+MAT_FIRE                 "fire"
+
+Name:
+"Feuer" "Feuers" "Feuer" "Feuer"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_GAS:100
+MATGROUP_INVIS:100
+MATGROUP_ELEMENTAL:100
diff --git a/doc/materials/materials/MAT_FLESH b/doc/materials/materials/MAT_FLESH
new file mode 100644
index 0000000..3f61e56
--- /dev/null
+++ b/doc/materials/materials/MAT_FLESH
@@ -0,0 +1,21 @@
+Materialid:
+MAT_FLESH                "flesh"
+
+Name:
+"Fleisch" "Fleisches" "Fleisch" "Fleisch"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_FOOD:-50
+MAT_FLESH
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_BIO:100
+MATGROUP_EATABLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_FLINT b/doc/materials/materials/MAT_FLINT
new file mode 100644
index 0000000..4ea5ca9
--- /dev/null
+++ b/doc/materials/materials/MAT_FLINT
@@ -0,0 +1,18 @@
+Materialid:
+MAT_FLINT                "flint"
+
+Name:
+"Feuerstein" "Feuersteins" "Feuerstein" "Feuerstein"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_FLUORITE b/doc/materials/materials/MAT_FLUORITE
new file mode 100644
index 0000000..4052ad4
--- /dev/null
+++ b/doc/materials/materials/MAT_FLUORITE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_FLUORITE             "fluorite"
+
+Name:
+"Flussspat" "Flussspates" "Flussspat" "Flussspat"
+
+Geschlecht:
+M
+
+Beschreibung:
+oft fluoreszierend
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:33
+MAT_ORTHOKLAS:50
+MAT_FLUORITE
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_FUNGUS b/doc/materials/materials/MAT_FUNGUS
new file mode 100644
index 0000000..1f845a3
--- /dev/null
+++ b/doc/materials/materials/MAT_FUNGUS
@@ -0,0 +1,19 @@
+Materialid:
+MAT_FUNGUS               "fungus"
+
+Name:
+"Pilz" "Pilzs" "Pilz" "Pilz"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
+MATGROUP_LIVING:100
diff --git a/doc/materials/materials/MAT_GARNET b/doc/materials/materials/MAT_GARNET
new file mode 100644
index 0000000..17a4f83
--- /dev/null
+++ b/doc/materials/materials/MAT_GARNET
@@ -0,0 +1,20 @@
+Materialid:
+MAT_GARNET               "garnet"
+
+Name:
+"Granat" "Granats" "Granat" "Granat"
+
+Geschlecht:
+M
+
+Beschreibung:
+rot, Edelstein: Karfunkel
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
+MATGROUP_JEWEL:100
diff --git a/doc/materials/materials/MAT_GHOST b/doc/materials/materials/MAT_GHOST
new file mode 100644
index 0000000..bbdd8fc
--- /dev/null
+++ b/doc/materials/materials/MAT_GHOST
@@ -0,0 +1,20 @@
+Materialid:
+MAT_GHOST                "ghost"
+
+Name:
+"Geist" "Geists" "Geist" "Geist"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_BIO:100
+MATGROUP_GAS:100
+MATGROUP_DEAD:100
+MATGROUP_INVIS:100
diff --git a/doc/materials/materials/MAT_GLASS b/doc/materials/materials/MAT_GLASS
new file mode 100644
index 0000000..5da7e13
--- /dev/null
+++ b/doc/materials/materials/MAT_GLASS
@@ -0,0 +1,17 @@
+Materialid:
+MAT_GLASS                "glass"
+
+Name:
+"Glas" "Glasses" "Glas" "Glas"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_GNEISS b/doc/materials/materials/MAT_GNEISS
new file mode 100644
index 0000000..a03eeea
--- /dev/null
+++ b/doc/materials/materials/MAT_GNEISS
@@ -0,0 +1,19 @@
+Materialid:
+MAT_GNEISS               "gneiss"
+
+Name:
+"Gneiss" "Gneisss" "Gneiss" "Gneiss"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_STONE:35
+MAT_GNEISS
+
+Gruppenzugehoerigkeit:
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_GOLD b/doc/materials/materials/MAT_GOLD
new file mode 100644
index 0000000..2dc051b
--- /dev/null
+++ b/doc/materials/materials/MAT_GOLD
@@ -0,0 +1,19 @@
+Materialid:
+MAT_GOLD                 "Au"
+
+Name:
+"Gold" "Goldes" "Gold" "Gold"
+
+Geschlecht:
+N
+
+Beschreibung:
+weiss bis gelb/rot
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
+MATGROUP_PRECIOUS_METAL:100
diff --git a/doc/materials/materials/MAT_GRANITE b/doc/materials/materials/MAT_GRANITE
new file mode 100644
index 0000000..7e4da7a
--- /dev/null
+++ b/doc/materials/materials/MAT_GRANITE
@@ -0,0 +1,18 @@
+Materialid:
+MAT_GRANITE              "granite"
+
+Name:
+"Granit" "Granits" "Granit" "Granit"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_GRAPHITE b/doc/materials/materials/MAT_GRAPHITE
new file mode 100644
index 0000000..49d7f44
--- /dev/null
+++ b/doc/materials/materials/MAT_GRAPHITE
@@ -0,0 +1,23 @@
+Materialid:
+MAT_GRAPHITE             "graphite"
+
+Name:
+"Graphit" "Graphits" "Graphit" "Graphit"
+
+Geschlecht:
+N
+
+Beschreibung:
+metallisch grau
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:20
+MAT_LEAD:40
+MAT_CARBON:60
+MAT_GRAPHITE
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_GUNPOWDER b/doc/materials/materials/MAT_GUNPOWDER
new file mode 100644
index 0000000..3eec675
--- /dev/null
+++ b/doc/materials/materials/MAT_GUNPOWDER
@@ -0,0 +1,19 @@
+Materialid:
+MAT_GUNPOWDER            "gunpowder"
+
+Name:
+"Schwarzpulver" "Schwarzpulvers" "Schwarzpulver" "Schwarzpulver"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_EXPLOSIVE:100
diff --git a/doc/materials/materials/MAT_HAIR b/doc/materials/materials/MAT_HAIR
new file mode 100644
index 0000000..3ffd5c3
--- /dev/null
+++ b/doc/materials/materials/MAT_HAIR
@@ -0,0 +1,21 @@
+Materialid:
+MAT_HAIR                 "hair"
+
+Name:
+"Haare" "Haare" "Haaren" "Haaren"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_HELIUM b/doc/materials/materials/MAT_HELIUM
new file mode 100644
index 0000000..b4504c0
--- /dev/null
+++ b/doc/materials/materials/MAT_HELIUM
@@ -0,0 +1,19 @@
+Materialid:
+MAT_HELIUM               "He"
+
+Name:
+"Helium" "Heliums" "Helium" "Helium"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_GAS:20
+MAT_HELIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_GAS:100
+MATGROUP_INVIS:100
diff --git a/doc/materials/materials/MAT_HORN b/doc/materials/materials/MAT_HORN
new file mode 100644
index 0000000..dad2359
--- /dev/null
+++ b/doc/materials/materials/MAT_HORN
@@ -0,0 +1,19 @@
+Materialid:
+MAT_HORN                 "horn"
+
+Name:
+"Horn" "Horns" "Horn" "Horn"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_HOWALGO b/doc/materials/materials/MAT_HOWALGO
new file mode 100644
index 0000000..aaeab73
--- /dev/null
+++ b/doc/materials/materials/MAT_HOWALGO
@@ -0,0 +1,22 @@
+Materialid:
+MAT_HOWALGO              "howalgo"
+
+Name:
+"Howalgoerz" "Howalgoerzes" "Howalgoerz" "Howalgoerz"
+
+Geschlecht:
+N
+
+Beschreibung:
+Zwergenwaffenstahl
+
+Erkennbarkeit:
+MAT_MISC_METAL:10
+MAT_STEEL:65
+MAT_HOWALGO
+
+Gruppenzugehoerigkeit:
+MATGROUP_MAGNETIC:100
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
+MATGROUP_PRECIOUS_METAL:100
diff --git a/doc/materials/materials/MAT_ICE b/doc/materials/materials/MAT_ICE
new file mode 100644
index 0000000..e8b04d3
--- /dev/null
+++ b/doc/materials/materials/MAT_ICE
@@ -0,0 +1,17 @@
+Materialid:
+MAT_ICE                  "ice"
+
+Name:
+"Eis" "Eises" "Eis" "Eis"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_INK b/doc/materials/materials/MAT_INK
new file mode 100644
index 0000000..ed43d5e
--- /dev/null
+++ b/doc/materials/materials/MAT_INK
@@ -0,0 +1,17 @@
+Materialid:
+MAT_INK                  "ink"
+
+Name:
+"Tinte" "Tinte" "Tinte" "Tinte"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLUID:100
diff --git a/doc/materials/materials/MAT_IRIDIUM b/doc/materials/materials/MAT_IRIDIUM
new file mode 100644
index 0000000..e122588
--- /dev/null
+++ b/doc/materials/materials/MAT_IRIDIUM
@@ -0,0 +1,23 @@
+Materialid:
+MAT_IRIDIUM              "Ir"
+
+Name:
+"Iridium" "Iridiums" "Iridium" "Iridium"
+
+Geschlecht:
+N
+
+Beschreibung:
+schwer, giftig
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_SILVER:5
+MAT_PLATINUM:25
+MAT_IRIDIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_POISONOUS:100
+MATGROUP_SOLID:100
+MATGROUP_PRECIOUS_METAL:100
diff --git a/doc/materials/materials/MAT_IRON b/doc/materials/materials/MAT_IRON
new file mode 100644
index 0000000..7015cb0
--- /dev/null
+++ b/doc/materials/materials/MAT_IRON
@@ -0,0 +1,19 @@
+Materialid:
+MAT_IRON                 "Fe"
+
+Name:
+"Eisen" "Eisens" "Eisen" "Eisen"
+
+Geschlecht:
+N
+
+Beschreibung:
+weich
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_MAGNETIC:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_IRON_WOOD b/doc/materials/materials/MAT_IRON_WOOD
new file mode 100644
index 0000000..77890f6
--- /dev/null
+++ b/doc/materials/materials/MAT_IRON_WOOD
@@ -0,0 +1,24 @@
+Materialid:
+MAT_IRON_WOOD            "iron_wood"
+
+Name:
+"Eisenholz" "Eisenholzes" "Eisenholz" "Eisenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+extrem hart (tropisch)
+
+Erkennbarkeit:
+MAT_OAK:25
+MAT_IRON_WOOD
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_TROPICAL_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_IVORY b/doc/materials/materials/MAT_IVORY
new file mode 100644
index 0000000..e6ad852
--- /dev/null
+++ b/doc/materials/materials/MAT_IVORY
@@ -0,0 +1,19 @@
+Materialid:
+MAT_IVORY                "ivory"
+
+Name:
+"Elfenbein" "Elfenbeins" "Elfenbein" "Elfenbein"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_JADE b/doc/materials/materials/MAT_JADE
new file mode 100644
index 0000000..b0d1950
--- /dev/null
+++ b/doc/materials/materials/MAT_JADE
@@ -0,0 +1,20 @@
+Materialid:
+MAT_JADE                 "jade"
+
+Name:
+"Jade" "Jade" "Jade" "Jade"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_JOFIUM b/doc/materials/materials/MAT_JOFIUM
new file mode 100644
index 0000000..9c2cbc1
--- /dev/null
+++ b/doc/materials/materials/MAT_JOFIUM
@@ -0,0 +1,19 @@
+Materialid:
+MAT_JOFIUM               "jofium"
+
+Name:
+"Jofium" "Jofiums" "Jofium" "Jofium"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
+MATGROUP_PRECIOUS_METAL:100
diff --git a/doc/materials/materials/MAT_LACE b/doc/materials/materials/MAT_LACE
new file mode 100644
index 0000000..36db45f
--- /dev/null
+++ b/doc/materials/materials/MAT_LACE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_LACE                 "lace"
+
+Name:
+"Spitze" "Spitze" "Spitze" "Spitze"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_CLOTH:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_LARIX b/doc/materials/materials/MAT_LARIX
new file mode 100644
index 0000000..9eda4cd
--- /dev/null
+++ b/doc/materials/materials/MAT_LARIX
@@ -0,0 +1,25 @@
+Materialid:
+MAT_LARIX                "larix"
+
+Name:
+"Laerchenholz" "Laerchenholzes" "Laerchenholz" "Laerchenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+sehr harzig, biegsam
+
+Erkennbarkeit:
+MAT_MISC_CONIFER_WOOD:5
+MAT_LARIX
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_CONIFER_WOOD:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_LATEX b/doc/materials/materials/MAT_LATEX
new file mode 100644
index 0000000..78876a2
--- /dev/null
+++ b/doc/materials/materials/MAT_LATEX
@@ -0,0 +1,19 @@
+Materialid:
+MAT_LATEX                "latex"
+
+Name:
+"Gummi" "Gummis" "Gummi" "Gummi"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLEXIBLE:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_LAUGHING_GAS b/doc/materials/materials/MAT_LAUGHING_GAS
new file mode 100644
index 0000000..91da6e3
--- /dev/null
+++ b/doc/materials/materials/MAT_LAUGHING_GAS
@@ -0,0 +1,20 @@
+Materialid:
+MAT_LAUGHING_GAS         "laughing_gas"
+
+Name:
+"Lachgas" "Lachgases" "Lachgas" "Lachgas"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_GAS:100
+MATGROUP_EXPLOSIVE:100
+MATGROUP_INVIS:100
diff --git a/doc/materials/materials/MAT_LAZURITE b/doc/materials/materials/MAT_LAZURITE
new file mode 100644
index 0000000..6ed358a
--- /dev/null
+++ b/doc/materials/materials/MAT_LAZURITE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_LAZURITE             "lazurite"
+
+Name:
+"Lapislazuli" "Lapislazulis" "Lapislazuli" "Lapislazuli"
+
+Geschlecht:
+M
+
+Beschreibung:
+tiefblau (ultramarin)
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:20
+MAT_LAZURITE
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
+MATGROUP_JEWEL:100
diff --git a/doc/materials/materials/MAT_LEAD b/doc/materials/materials/MAT_LEAD
new file mode 100644
index 0000000..2bfafc9
--- /dev/null
+++ b/doc/materials/materials/MAT_LEAD
@@ -0,0 +1,20 @@
+Materialid:
+MAT_LEAD                 "Pb"
+
+Name:
+"Blei" "Bleis" "Blei" "Blei"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLEXIBLE:100
+MATGROUP_METAL:100
+MATGROUP_POISONOUS:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_LEATHER b/doc/materials/materials/MAT_LEATHER
new file mode 100644
index 0000000..34c8e75
--- /dev/null
+++ b/doc/materials/materials/MAT_LEATHER
@@ -0,0 +1,21 @@
+Materialid:
+MAT_LEATHER              "leather"
+
+Name:
+"Leder" "Leders" "Leder" "Leder"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_CLOTH:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_LIGHTNING b/doc/materials/materials/MAT_LIGHTNING
new file mode 100644
index 0000000..20d55ca
--- /dev/null
+++ b/doc/materials/materials/MAT_LIGHTNING
@@ -0,0 +1,19 @@
+Materialid:
+MAT_LIGHTNING            "lightning"
+
+Name:
+"Blitz" "Blitzes" "Blitz" "Blitz"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_ELECTRICAL:100
+MATGROUP_GAS:100
+MATGROUP_INVIS:100
diff --git a/doc/materials/materials/MAT_LIME b/doc/materials/materials/MAT_LIME
new file mode 100644
index 0000000..ed61dea
--- /dev/null
+++ b/doc/materials/materials/MAT_LIME
@@ -0,0 +1,24 @@
+Materialid:
+MAT_LIME                 "lime"
+
+Name:
+"Lindenholz" "Lindenholzes" "Lindenholz" "Lindenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+weich
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:15
+MAT_LIME
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_LIMESTONE b/doc/materials/materials/MAT_LIMESTONE
new file mode 100644
index 0000000..01ea90a
--- /dev/null
+++ b/doc/materials/materials/MAT_LIMESTONE
@@ -0,0 +1,18 @@
+Materialid:
+MAT_LIMESTONE            "limestone"
+
+Name:
+"Kalkstein" "Kalksteins" "Kalkstein" "Kalkstein"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_LINEN b/doc/materials/materials/MAT_LINEN
new file mode 100644
index 0000000..fa4deb0
--- /dev/null
+++ b/doc/materials/materials/MAT_LINEN
@@ -0,0 +1,21 @@
+Materialid:
+MAT_LINEN                "linen"
+
+Name:
+"Leinen" "Leinen" "Leinen" "Leinen"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_CLOTH:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MAGNESIUM b/doc/materials/materials/MAT_MAGNESIUM
new file mode 100644
index 0000000..dd5cbbb
--- /dev/null
+++ b/doc/materials/materials/MAT_MAGNESIUM
@@ -0,0 +1,21 @@
+Materialid:
+MAT_MAGNESIUM            "Mg"
+
+Name:
+"Magnesium" "Magnesiums" "Magnesium" "Magnesium"
+
+Geschlecht:
+N
+
+Beschreibung:
+brennbar
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_ALUMINIUM:30
+MAT_MAGNESIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MAHAGONI b/doc/materials/materials/MAT_MAHAGONI
new file mode 100644
index 0000000..0d8929c
--- /dev/null
+++ b/doc/materials/materials/MAT_MAHAGONI
@@ -0,0 +1,24 @@
+Materialid:
+MAT_MAHAGONI             "mahagoni"
+
+Name:
+"Mahagoni" "Mahagonis" "Mahagoni" "Mahagoni"
+
+Geschlecht:
+N
+
+Beschreibung:
+rot bis dunkelrot
+
+Erkennbarkeit:
+MAT_MISC_TROPICAL_WOOD:-5
+MAT_MAHAGONI
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_TROPICAL_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_MALACHITE b/doc/materials/materials/MAT_MALACHITE
new file mode 100644
index 0000000..7771010
--- /dev/null
+++ b/doc/materials/materials/MAT_MALACHITE
@@ -0,0 +1,20 @@
+Materialid:
+MAT_MALACHITE            "malachite"
+
+Name:
+"Malachit" "Malachits" "Malachit" "Malachit"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:20
+MAT_MALACHITE
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_MAPLE b/doc/materials/materials/MAT_MAPLE
new file mode 100644
index 0000000..9ebbd98
--- /dev/null
+++ b/doc/materials/materials/MAT_MAPLE
@@ -0,0 +1,24 @@
+Materialid:
+MAT_MAPLE                "maple"
+
+Name:
+"Ahornholz" "Ahornholzes" "Ahornholz" "Ahornholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:25
+MAT_MAPLE
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_MARBLE b/doc/materials/materials/MAT_MARBLE
new file mode 100644
index 0000000..a48942f
--- /dev/null
+++ b/doc/materials/materials/MAT_MARBLE
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MARBLE               "marble"
+
+Name:
+"Marmor" "Marmors" "Marmor" "Marmor"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MERCURY b/doc/materials/materials/MAT_MERCURY
new file mode 100644
index 0000000..cf3fd88
--- /dev/null
+++ b/doc/materials/materials/MAT_MERCURY
@@ -0,0 +1,19 @@
+Materialid:
+MAT_MERCURY              "Hg"
+
+Name:
+"Quecksilber" "Quecksilbers" "Quecksilber" "Quecksilber"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_POISONOUS:100
+MATGROUP_FLUID:100
diff --git a/doc/materials/materials/MAT_METHANE b/doc/materials/materials/MAT_METHANE
new file mode 100644
index 0000000..d93c576
--- /dev/null
+++ b/doc/materials/materials/MAT_METHANE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_METHANE              "methane"
+
+Name:
+"Methan" "Methans" "Methan" "Methan"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_GAS:20
+MAT_METHANE
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_GAS:100
+MATGROUP_EXPLOSIVE:100
+MATGROUP_INVIS:100
diff --git a/doc/materials/materials/MAT_MILK b/doc/materials/materials/MAT_MILK
new file mode 100644
index 0000000..7aea040
--- /dev/null
+++ b/doc/materials/materials/MAT_MILK
@@ -0,0 +1,17 @@
+Materialid:
+MAT_MILK                 "milk"
+
+Name:
+"Milch" "Milch" "Milch" "Milch"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLUID:100
diff --git a/doc/materials/materials/MAT_MISC b/doc/materials/materials/MAT_MISC
new file mode 100644
index 0000000..d9aa554
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC
@@ -0,0 +1,17 @@
+Materialid:
+MAT_MISC                 "misc"
+
+Name:
+"verschiedenes Material" "verschiedenen Materials" "verschiedenem Material" "verschiedenen Material"
+
+Geschlecht:
+N
+
+Beschreibung:
+allg. Material (default)
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MISC:100
diff --git a/doc/materials/materials/MAT_MISC_ACID b/doc/materials/materials/MAT_MISC_ACID
new file mode 100644
index 0000000..ac8d15f
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_ACID
@@ -0,0 +1,19 @@
+Materialid:
+MAT_MISC_ACID            "misc_acid"
+
+Name:
+"Saeure" "Saeure" "Saeure" "Saeure"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_ACIDIC:100
+MATGROUP_FLUID:100
+MATGROUP_POISONOUS:100
diff --git a/doc/materials/materials/MAT_MISC_BASE b/doc/materials/materials/MAT_MISC_BASE
new file mode 100644
index 0000000..cd2cfd1
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_BASE
@@ -0,0 +1,19 @@
+Materialid:
+MAT_MISC_BASE            "misc_base"
+
+Name:
+"Lauge" "Lauge" "Lauge" "Lauge"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLUID:100
+MATGROUP_BASIC:100
+MATGROUP_POISONOUS:100
diff --git a/doc/materials/materials/MAT_MISC_CONIFER_WOOD b/doc/materials/materials/MAT_MISC_CONIFER_WOOD
new file mode 100644
index 0000000..1089538
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_CONIFER_WOOD
@@ -0,0 +1,23 @@
+Materialid:
+MAT_MISC_CONIFER_WOOD    "misc_conifer_wood"
+
+Name:
+"Nadelholz" "Nadelholzes" "Nadelholz" "Nadelholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_CONIFER_WOOD:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_MISC_DEAD b/doc/materials/materials/MAT_MISC_DEAD
new file mode 100644
index 0000000..7a4437a
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_DEAD
@@ -0,0 +1,19 @@
+Materialid:
+MAT_MISC_DEAD            "misc_dead"
+
+Name:
+"sterbliche Ueberreste" "sterbliche Ueberrestes" "sterbliche Ueberreste" "sterbliche Ueberreste"
+
+Geschlecht:
+M
+
+Beschreibung:
+allg. totes Material
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_DECIDUOUS_WOOD b/doc/materials/materials/MAT_MISC_DECIDUOUS_WOOD
new file mode 100644
index 0000000..733456e
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_DECIDUOUS_WOOD
@@ -0,0 +1,23 @@
+Materialid:
+MAT_MISC_DECIDUOUS_WOOD  "misc_deciduous_wood"
+
+Name:
+"Laubholz" "Laubholzes" "Laubholz" "Laubholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_MISC_DRUG b/doc/materials/materials/MAT_MISC_DRUG
new file mode 100644
index 0000000..be59196
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_DRUG
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_DRUG            "misc_drug"
+
+Name:
+"Droge" "Droge" "Droge" "Droge"
+
+Geschlecht:
+F
+
+Beschreibung:
+allg. Drogen
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DRUG:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_EXPLOSIVE b/doc/materials/materials/MAT_MISC_EXPLOSIVE
new file mode 100644
index 0000000..3446878
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_EXPLOSIVE
@@ -0,0 +1,19 @@
+Materialid:
+MAT_MISC_EXPLOSIVE       "misc_explosive"
+
+Name:
+"explosives Material" "explosiven Materials" "explosivem Material" "explosiven Material"
+
+Geschlecht:
+N
+
+Beschreibung:
+allg. Sprengstoffe
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MISC:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_EXPLOSIVE:100
diff --git a/doc/materials/materials/MAT_MISC_FLEXIBLE b/doc/materials/materials/MAT_MISC_FLEXIBLE
new file mode 100644
index 0000000..978d37c
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_FLEXIBLE
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_FLEXIBLE        "misc_flexible"
+
+Name:
+"biegsames Material" "biegsamen Materials" "biegsamem Material" "biegsamen Material"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_FLUID b/doc/materials/materials/MAT_MISC_FLUID
new file mode 100644
index 0000000..fca926d
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_FLUID
@@ -0,0 +1,17 @@
+Materialid:
+MAT_MISC_FLUID           "misc_fluid"
+
+Name:
+"Fluessigkeit" "Fluessigkeits" "Fluessigkeit" "Fluessigkeit"
+
+Geschlecht:
+F
+
+Beschreibung:
+allg. Fluessigkeit
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLUID:100
diff --git a/doc/materials/materials/MAT_MISC_FOOD b/doc/materials/materials/MAT_MISC_FOOD
new file mode 100644
index 0000000..244c440
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_FOOD
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_FOOD            "misc_food"
+
+Name:
+"Essen" "Essens" "Essen" "Essen"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_EATABLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_GAS b/doc/materials/materials/MAT_MISC_GAS
new file mode 100644
index 0000000..b8f90bb
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_GAS
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_GAS             "misc_gas"
+
+Name:
+"Gas" "Gases" "Gas" "Gas"
+
+Geschlecht:
+N
+
+Beschreibung:
+allg. Gas
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_GAS:100
+MATGROUP_INVIS:100
diff --git a/doc/materials/materials/MAT_MISC_HERBAL b/doc/materials/materials/MAT_MISC_HERBAL
new file mode 100644
index 0000000..645c8f3
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_HERBAL
@@ -0,0 +1,21 @@
+Materialid:
+MAT_MISC_HERBAL          "misc_herbal"
+
+Name:
+"Kraeuter" "Kraeuter" "Kraeuter" "Kraeuter"
+
+Geschlecht:
+F
+
+Beschreibung:
+allg. Kraeuter
+
+Erkennbarkeit:
+MAT_MISC_PLANT:15
+MAT_MISC_HERBAL
+
+Gruppenzugehoerigkeit:
+MATGROUP_HERBAL:100
+MATGROUP_BIO:100
+MATGROUP_LIVING:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_HOLY b/doc/materials/materials/MAT_MISC_HOLY
new file mode 100644
index 0000000..8d265aa
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_HOLY
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_HOLY            "misc_holy"
+
+Name:
+"heiliges Material" "heiligen Materials" "heiligem Material" "heiligen Material"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_HOLY:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_INVIS b/doc/materials/materials/MAT_MISC_INVIS
new file mode 100644
index 0000000..e17ffde
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_INVIS
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_INVIS           "misc_invis"
+
+Name:
+"" "" "" ""
+
+Geschlecht:
+F
+
+Beschreibung:
+allg. unsichtbares Material
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MISC:100
+MATGROUP_INVIS:100
diff --git a/doc/materials/materials/MAT_MISC_JEWEL b/doc/materials/materials/MAT_MISC_JEWEL
new file mode 100644
index 0000000..6d1327f
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_JEWEL
@@ -0,0 +1,20 @@
+Materialid:
+MAT_MISC_JEWEL           "misc_jewel"
+
+Name:
+"Edelstein" "Edelsteins" "Edelstein" "Edelstein"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_MISC_LIVING b/doc/materials/materials/MAT_MISC_LIVING
new file mode 100644
index 0000000..581a01f
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_LIVING
@@ -0,0 +1,19 @@
+Materialid:
+MAT_MISC_LIVING          "misc_living"
+
+Name:
+"Lebewesen" "Lebewesens" "Lebewesen" "Lebewesen"
+
+Geschlecht:
+N
+
+Beschreibung:
+allg. lebendes Material
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
+MATGROUP_LIVING:100
diff --git a/doc/materials/materials/MAT_MISC_MAGIC b/doc/materials/materials/MAT_MISC_MAGIC
new file mode 100644
index 0000000..0886b59
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_MAGIC
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_MAGIC           "misc_magic"
+
+Name:
+"Magie" "Magie" "Magie" "Magie"
+
+Geschlecht:
+F
+
+Beschreibung:
+allg. magisches Material, das sich nicht naeher bestimmen laesst (wie Oktarin, Birnbaumholz)
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MISC:100
+MATGROUP_MAGIC:100
diff --git a/doc/materials/materials/MAT_MISC_MAGNETIC b/doc/materials/materials/MAT_MISC_MAGNETIC
new file mode 100644
index 0000000..2d48539
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_MAGNETIC
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_MAGNETIC        "misc_magnetic"
+
+Name:
+"magnetisches Material" "magnetischen Materials" "magnetischem Material" "magnetischen Material"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MAGNETIC:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_METAL b/doc/materials/materials/MAT_MISC_METAL
new file mode 100644
index 0000000..61b871c
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_METAL
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_METAL           "misc_metal"
+
+Name:
+"Metall" "Metalls" "Metall" "Metall"
+
+Geschlecht:
+N
+
+Beschreibung:
+allg. Metall
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_MINERAL b/doc/materials/materials/MAT_MISC_MINERAL
new file mode 100644
index 0000000..91da98d
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_MINERAL
@@ -0,0 +1,19 @@
+Materialid:
+MAT_MISC_MINERAL         "misc_mineral"
+
+Name:
+"Mineral" "Minerals" "Mineral" "Mineral"
+
+Geschlecht:
+N
+
+Beschreibung:
+allg. Mineralien
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_MISC_PLANT b/doc/materials/materials/MAT_MISC_PLANT
new file mode 100644
index 0000000..ff805bc
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_PLANT
@@ -0,0 +1,20 @@
+Materialid:
+MAT_MISC_PLANT           "misc_plant"
+
+Name:
+"pflanzliches Material" "pflanzlichen Materials" "pflanzlichem Material" "pflanzlichen Material"
+
+Geschlecht:
+N
+
+Beschreibung:
+allg. planzliches Material ohne Holzanteil
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_HERBAL:100
+MATGROUP_BIO:100
+MATGROUP_LIVING:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_POISON b/doc/materials/materials/MAT_MISC_POISON
new file mode 100644
index 0000000..82754f1
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_POISON
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_POISON          "misc_poison"
+
+Name:
+"giftiges Material" "giftigen Materials" "giftigem Material" "giftigen Material"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MISC:100
+MATGROUP_POISONOUS:100
diff --git a/doc/materials/materials/MAT_MISC_SOLID b/doc/materials/materials/MAT_MISC_SOLID
new file mode 100644
index 0000000..3b75ce8
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_SOLID
@@ -0,0 +1,17 @@
+Materialid:
+MAT_MISC_SOLID           "misc_solid"
+
+Name:
+"verschiedenes Material" "verschiedenen Materials" "verschiedenem Material" "verschiedenen Material"
+
+Geschlecht:
+N
+
+Beschreibung:
+allg. Feststoffe
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_STONE b/doc/materials/materials/MAT_MISC_STONE
new file mode 100644
index 0000000..6375be7
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_STONE
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_STONE           "misc_stone"
+
+Name:
+"Stein" "Steines" "Stein" "Stein"
+
+Geschlecht:
+M
+
+Beschreibung:
+allg. Gestein
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_TROPICAL_WOOD b/doc/materials/materials/MAT_MISC_TROPICAL_WOOD
new file mode 100644
index 0000000..0376c5b
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_TROPICAL_WOOD
@@ -0,0 +1,23 @@
+Materialid:
+MAT_MISC_TROPICAL_WOOD   "misc_tropical_wood"
+
+Name:
+"Tropenholz" "Tropenholzes" "Tropenholz" "Tropenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_TROPICAL_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_MISC_UNHOLY b/doc/materials/materials/MAT_MISC_UNHOLY
new file mode 100644
index 0000000..364206a
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_UNHOLY
@@ -0,0 +1,18 @@
+Materialid:
+MAT_MISC_UNHOLY          "misc_unholy"
+
+Name:
+"unheiliges Material" "unheiligen Materials" "unheiligem Material" "unheiligen Material"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_UNHOLY:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_MISC_WOOD b/doc/materials/materials/MAT_MISC_WOOD
new file mode 100644
index 0000000..3937e91
--- /dev/null
+++ b/doc/materials/materials/MAT_MISC_WOOD
@@ -0,0 +1,22 @@
+Materialid:
+MAT_MISC_WOOD            "misc_wood"
+
+Name:
+"Holz" "Holzes" "Holz" "Holz"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_MITHRIL b/doc/materials/materials/MAT_MITHRIL
new file mode 100644
index 0000000..90fe713
--- /dev/null
+++ b/doc/materials/materials/MAT_MITHRIL
@@ -0,0 +1,19 @@
+Materialid:
+MAT_MITHRIL              "mithril"
+
+Name:
+"Mithril" "Mithrils" "Mithril" "Mithril"
+
+Geschlecht:
+N
+
+Beschreibung:
+Zwergenruestungsmetall
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
+MATGROUP_PRECIOUS_METAL:100
diff --git a/doc/materials/materials/MAT_MOONSTONE b/doc/materials/materials/MAT_MOONSTONE
new file mode 100644
index 0000000..083beda
--- /dev/null
+++ b/doc/materials/materials/MAT_MOONSTONE
@@ -0,0 +1,20 @@
+Materialid:
+MAT_MOONSTONE            "moonstone"
+
+Name:
+"Mondstein" "Mondsteines" "Mondstein" "Mondstein"
+
+Geschlecht:
+M
+
+Beschreibung:
+gelb, blaeulich, fahl
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:25
+MAT_MOONSTONE
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_MORPHIUM b/doc/materials/materials/MAT_MORPHIUM
new file mode 100644
index 0000000..f3d383a
--- /dev/null
+++ b/doc/materials/materials/MAT_MORPHIUM
@@ -0,0 +1,20 @@
+Materialid:
+MAT_MORPHIUM             "morphium"
+
+Name:
+"Morphium" "Morphiums" "Morphium" "Morphium"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_SALT:50
+MAT_MORPHIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_HERBAL:100
+MATGROUP_DRUG:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_NEUTRONIUM b/doc/materials/materials/MAT_NEUTRONIUM
new file mode 100644
index 0000000..b64c289
--- /dev/null
+++ b/doc/materials/materials/MAT_NEUTRONIUM
@@ -0,0 +1,19 @@
+Materialid:
+MAT_NEUTRONIUM           "Neutr"
+
+Name:
+"Neutronium" "Neutroniums" "Neutronium" "Neutronium"
+
+Geschlecht:
+N
+
+Beschreibung:
+ultraschwer, unzerstoerbar
+
+Erkennbarkeit:
+MAT_MISC_METAL:-25
+MAT_NEUTRONIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_NICKEL b/doc/materials/materials/MAT_NICKEL
new file mode 100644
index 0000000..3bf0935
--- /dev/null
+++ b/doc/materials/materials/MAT_NICKEL
@@ -0,0 +1,20 @@
+Materialid:
+MAT_NICKEL               "nickel"
+
+Name:
+"Nickel" "Nickels" "Nickel" "Nickel"
+
+Geschlecht:
+N
+
+Beschreibung:
+zaeh, fest, silberweiss
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_SILVER:15
+MAT_NICKEL
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_NITROGLYCERINE b/doc/materials/materials/MAT_NITROGLYCERINE
new file mode 100644
index 0000000..fffa06e
--- /dev/null
+++ b/doc/materials/materials/MAT_NITROGLYCERINE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_NITROGLYCERINE       "nitroglycerine"
+
+Name:
+"Nitroglycerin" "Nitroglycerins" "Nitroglycerin" "Nitroglycerin"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_OIL:25
+MAT_MISC_EXPLOSIVE:50
+MAT_NITROGLYCERINE
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_EXPLOSIVE:100
+MATGROUP_FLUID:100
diff --git a/doc/materials/materials/MAT_NUTTREE b/doc/materials/materials/MAT_NUTTREE
new file mode 100644
index 0000000..7dd88ff
--- /dev/null
+++ b/doc/materials/materials/MAT_NUTTREE
@@ -0,0 +1,24 @@
+Materialid:
+MAT_NUTTREE              "nuttree"
+
+Name:
+"Nussbaumholz" "Nussbaumholzes" "Nussbaumholz" "Nussbaumholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+hell, polierfaehig
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:15
+MAT_NUTTREE
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_OAK b/doc/materials/materials/MAT_OAK
new file mode 100644
index 0000000..ca256da
--- /dev/null
+++ b/doc/materials/materials/MAT_OAK
@@ -0,0 +1,24 @@
+Materialid:
+MAT_OAK                  "oak"
+
+Name:
+"Eichenholz" "Eichenholzes" "Eichenholz" "Eichenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+hart
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:5
+MAT_OAK
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_OBSIDIAN b/doc/materials/materials/MAT_OBSIDIAN
new file mode 100644
index 0000000..865203b
--- /dev/null
+++ b/doc/materials/materials/MAT_OBSIDIAN
@@ -0,0 +1,20 @@
+Materialid:
+MAT_OBSIDIAN             "obsidian"
+
+Name:
+"Obsidian" "Obsidians" "Obsidian" "Obsidian"
+
+Geschlecht:
+N
+
+Beschreibung:
+schwarz
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:10
+MAT_OBSIDIAN
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_OCTARINE b/doc/materials/materials/MAT_OCTARINE
new file mode 100644
index 0000000..4ef0d29
--- /dev/null
+++ b/doc/materials/materials/MAT_OCTARINE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_OCTARINE             "octarine"
+
+Name:
+"Oktarin" "Oktarins" "Oktarin" "Oktarin"
+
+Geschlecht:
+M
+
+Beschreibung:
+magischer Muell :)
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MAGIC:100
+MATGROUP_STONE:100
+MATGROUP_JEWEL:100
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_OIL b/doc/materials/materials/MAT_OIL
new file mode 100644
index 0000000..7de5102
--- /dev/null
+++ b/doc/materials/materials/MAT_OIL
@@ -0,0 +1,18 @@
+Materialid:
+MAT_OIL                  "oil"
+
+Name:
+"Oel" "Oels" "Oel" "Oel"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_FLUID:100
diff --git a/doc/materials/materials/MAT_OLIVIN b/doc/materials/materials/MAT_OLIVIN
new file mode 100644
index 0000000..89a1e3b
--- /dev/null
+++ b/doc/materials/materials/MAT_OLIVIN
@@ -0,0 +1,20 @@
+Materialid:
+MAT_OLIVIN               "olivin"
+
+Name:
+"Olivin" "Olivins" "Olivin" "Olivin"
+
+Geschlecht:
+N
+
+Beschreibung:
+gruen bis braun
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:20
+MAT_OLIVIN
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_ONYX b/doc/materials/materials/MAT_ONYX
new file mode 100644
index 0000000..cb5363a
--- /dev/null
+++ b/doc/materials/materials/MAT_ONYX
@@ -0,0 +1,22 @@
+Materialid:
+MAT_ONYX                 "onyx"
+
+Name:
+"Onyx" "Onyxes" "Onyx" "Onyx"
+
+Geschlecht:
+M
+
+Beschreibung:
+Unterart Achat, schwarz
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:0
+MAT_QUARTZ:20
+MAT_AGATE:75
+MAT_ONYX
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_OPAL b/doc/materials/materials/MAT_OPAL
new file mode 100644
index 0000000..89e9707
--- /dev/null
+++ b/doc/materials/materials/MAT_OPAL
@@ -0,0 +1,21 @@
+Materialid:
+MAT_OPAL                 "opal"
+
+Name:
+"Opal" "Opals" "Opal" "Opal"
+
+Geschlecht:
+M
+
+Beschreibung:
+weiss, rot, blau, bunt
+
+Erkennbarkeit:
+MAT_MISC_JEWEL:25
+MAT_OPAL
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_OPIUM b/doc/materials/materials/MAT_OPIUM
new file mode 100644
index 0000000..e680829
--- /dev/null
+++ b/doc/materials/materials/MAT_OPIUM
@@ -0,0 +1,20 @@
+Materialid:
+MAT_OPIUM                "opium"
+
+Name:
+"Opium" "Opiums" "Opium" "Opium"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_HERBAL:50
+MAT_OPIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_HERBAL:100
+MATGROUP_DRUG:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_ORTHOKLAS b/doc/materials/materials/MAT_ORTHOKLAS
new file mode 100644
index 0000000..de100b6
--- /dev/null
+++ b/doc/materials/materials/MAT_ORTHOKLAS
@@ -0,0 +1,20 @@
+Materialid:
+MAT_ORTHOKLAS            "orthoklas"
+
+Name:
+"Feldspat" "Feldspates" "Feldspat" "Feldspat"
+
+Geschlecht:
+M
+
+Beschreibung:
+weiss bis grau, gruenlich
+
+Erkennbarkeit:
+MAT_MISC_MINERAL:33
+MAT_ORTHOKLAS
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_OSMIUM b/doc/materials/materials/MAT_OSMIUM
new file mode 100644
index 0000000..7ab21a0
--- /dev/null
+++ b/doc/materials/materials/MAT_OSMIUM
@@ -0,0 +1,22 @@
+Materialid:
+MAT_OSMIUM               "Os"
+
+Name:
+"Osmium" "Osmiums" "Osmium" "Osmium"
+
+Geschlecht:
+N
+
+Beschreibung:
+schwerstes Metall
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_SILVER:5
+MAT_PLATINUM:25
+MAT_OSMIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
+MATGROUP_PRECIOUS_METAL:100
diff --git a/doc/materials/materials/MAT_PAPER b/doc/materials/materials/MAT_PAPER
new file mode 100644
index 0000000..4a7820d
--- /dev/null
+++ b/doc/materials/materials/MAT_PAPER
@@ -0,0 +1,21 @@
+Materialid:
+MAT_PAPER                "paper"
+
+Name:
+"Papier" "Papiers" "Papier" "Papier"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_PAPER:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_PAPYRUS b/doc/materials/materials/MAT_PAPYRUS
new file mode 100644
index 0000000..e5ee8c1
--- /dev/null
+++ b/doc/materials/materials/MAT_PAPYRUS
@@ -0,0 +1,21 @@
+Materialid:
+MAT_PAPYRUS              "papyrus"
+
+Name:
+"Papyrus" "Papyruss" "Papyrus" "Papyrus"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_PAPER:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_PARFUM b/doc/materials/materials/MAT_PARFUM
new file mode 100644
index 0000000..a704fd9
--- /dev/null
+++ b/doc/materials/materials/MAT_PARFUM
@@ -0,0 +1,17 @@
+Materialid:
+MAT_PARFUM               "parfum"
+
+Name:
+"Parfuem" "Parfuems" "Parfuem" "Parfuem"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLUID:100
diff --git a/doc/materials/materials/MAT_PATENT_LEATHER b/doc/materials/materials/MAT_PATENT_LEATHER
new file mode 100644
index 0000000..c4fc177
--- /dev/null
+++ b/doc/materials/materials/MAT_PATENT_LEATHER
@@ -0,0 +1,20 @@
+Materialid:
+MAT_PATENT_LEATHER       "patent_leather"
+
+Name:
+"Lackleder" "Lackleders" "Lackleder" "Lackleder"
+
+Geschlecht:
+N
+
+Beschreibung:
+Kunstleder
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLEXIBLE:100
+MATGROUP_BIO:100
+MATGROUP_CLOTH:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_PEARL b/doc/materials/materials/MAT_PEARL
new file mode 100644
index 0000000..eb56f10
--- /dev/null
+++ b/doc/materials/materials/MAT_PEARL
@@ -0,0 +1,20 @@
+Materialid:
+MAT_PEARL                "pearl"
+
+Name:
+"Perle" "Perle" "Perle" "Perle"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_BIO:100
+MATGROUP_DEAD:100
+MATGROUP_SOLID:100
+MATGROUP_JEWEL:100
diff --git a/doc/materials/materials/MAT_PEARLMOTHER b/doc/materials/materials/MAT_PEARLMOTHER
new file mode 100644
index 0000000..f058dbc
--- /dev/null
+++ b/doc/materials/materials/MAT_PEARLMOTHER
@@ -0,0 +1,19 @@
+Materialid:
+MAT_PEARLMOTHER          "pearlmother"
+
+Name:
+"Perlmutt" "Perlmutts" "Perlmutt" "Perlmutt"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_PEAR_WOOD b/doc/materials/materials/MAT_PEAR_WOOD
new file mode 100644
index 0000000..d31ffb5
--- /dev/null
+++ b/doc/materials/materials/MAT_PEAR_WOOD
@@ -0,0 +1,25 @@
+Materialid:
+MAT_PEAR_WOOD            "pear_wood"
+
+Name:
+"Birnbaumholz" "Birnbaumholzes" "Birnbaumholz" "Birnbaumholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+zaeh, trocken, magisch
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:35
+MAT_PEAR_WOOD
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_MAGIC:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_PELT b/doc/materials/materials/MAT_PELT
new file mode 100644
index 0000000..e15566d
--- /dev/null
+++ b/doc/materials/materials/MAT_PELT
@@ -0,0 +1,21 @@
+Materialid:
+MAT_PELT                 "pelt"
+
+Name:
+"Fell" "Felles" "Fell" "Fell"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_CLOTH:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_PENICILLIN b/doc/materials/materials/MAT_PENICILLIN
new file mode 100644
index 0000000..7d79344
--- /dev/null
+++ b/doc/materials/materials/MAT_PENICILLIN
@@ -0,0 +1,21 @@
+Materialid:
+MAT_PENICILLIN           "penicillin"
+
+Name:
+"Penicillin" "Penicillins" "Penicillin" "Penicillin"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_CHALK:50
+MAT_PENICILLIN
+
+Gruppenzugehoerigkeit:
+MATGROUP_BIO:100
+MATGROUP_DEAD:100
+MATGROUP_DRUG:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_PERGAMENT b/doc/materials/materials/MAT_PERGAMENT
new file mode 100644
index 0000000..7941178
--- /dev/null
+++ b/doc/materials/materials/MAT_PERGAMENT
@@ -0,0 +1,22 @@
+Materialid:
+MAT_PERGAMENT            "pergament"
+
+Name:
+"Pergament" "Pergaments" "Pergament" "Pergament"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_PAPER:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_PHOSPHORUS b/doc/materials/materials/MAT_PHOSPHORUS
new file mode 100644
index 0000000..c5a0fbb
--- /dev/null
+++ b/doc/materials/materials/MAT_PHOSPHORUS
@@ -0,0 +1,18 @@
+Materialid:
+MAT_PHOSPHORUS           "P"
+
+Name:
+"Phosphor" "Phosphors" "Phosphor" "Phosphor"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_POISONOUS:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_PINE b/doc/materials/materials/MAT_PINE
new file mode 100644
index 0000000..64e1cdc
--- /dev/null
+++ b/doc/materials/materials/MAT_PINE
@@ -0,0 +1,24 @@
+Materialid:
+MAT_PINE                 "pine"
+
+Name:
+"Kieferholz" "Kiefernholzes" "Kieferholz" "Kieferholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+harzig (Pinie, Kienspan)
+
+Erkennbarkeit:
+MAT_MISC_CONIFER_WOOD:15
+MAT_PINE
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_CONIFER_WOOD:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_PLASTIC b/doc/materials/materials/MAT_PLASTIC
new file mode 100644
index 0000000..6c55327
--- /dev/null
+++ b/doc/materials/materials/MAT_PLASTIC
@@ -0,0 +1,18 @@
+Materialid:
+MAT_PLASTIC              "plastic"
+
+Name:
+"kuenstliches Material" "kuenstlichen Materials" "kuenstlichem Material" "kuenstlichen Material"
+
+Geschlecht:
+N
+
+Beschreibung:
+Kunststoff
+
+Erkennbarkeit:
+MAT_MISC_SOLID:35
+MAT_PLASTIC
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_PLATINUM b/doc/materials/materials/MAT_PLATINUM
new file mode 100644
index 0000000..c1671b5
--- /dev/null
+++ b/doc/materials/materials/MAT_PLATINUM
@@ -0,0 +1,21 @@
+Materialid:
+MAT_PLATINUM             "Pt"
+
+Name:
+"Platin" "Platins" "Platin" "Platin"
+
+Geschlecht:
+N
+
+Beschreibung:
+grauweiss
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_SILVER:5
+MAT_PLATINUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
+MATGROUP_PRECIOUS_METAL:100
diff --git a/doc/materials/materials/MAT_PLUTONIUM b/doc/materials/materials/MAT_PLUTONIUM
new file mode 100644
index 0000000..e5a2857
--- /dev/null
+++ b/doc/materials/materials/MAT_PLUTONIUM
@@ -0,0 +1,20 @@
+Materialid:
+MAT_PLUTONIUM            "Pu"
+
+Name:
+"Plutonium" "Plutoniums" "Plutonium" "Plutonium"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_DUST:65
+MAT_PLUTONIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_RADIOACTIVE:100
+MATGROUP_POISONOUS:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_POPLAR b/doc/materials/materials/MAT_POPLAR
new file mode 100644
index 0000000..8cf6eed
--- /dev/null
+++ b/doc/materials/materials/MAT_POPLAR
@@ -0,0 +1,24 @@
+Materialid:
+MAT_POPLAR               "poplar"
+
+Name:
+"Pappelholz" "Pappelholzes" "Pappelholz" "Pappelholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+sehr weich, faserig
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:25
+MAT_POPLAR
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_QUARTZ b/doc/materials/materials/MAT_QUARTZ
new file mode 100644
index 0000000..da78786
--- /dev/null
+++ b/doc/materials/materials/MAT_QUARTZ
@@ -0,0 +1,19 @@
+Materialid:
+MAT_QUARTZ               "quartz"
+
+Name:
+"Quarz" "Quarzes" "Quarz" "Quarz"
+
+Geschlecht:
+M
+
+Beschreibung:
+klar bis grau, auch gelb
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_ROBINIA b/doc/materials/materials/MAT_ROBINIA
new file mode 100644
index 0000000..3c169a7
--- /dev/null
+++ b/doc/materials/materials/MAT_ROBINIA
@@ -0,0 +1,24 @@
+Materialid:
+MAT_ROBINIA              "robinia"
+
+Name:
+"Robinienholz" "Robinienholzes" "Robinienholz" "Robinienholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+sehr hart
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:5
+MAT_ROBINIA
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_ROSEWOOD b/doc/materials/materials/MAT_ROSEWOOD
new file mode 100644
index 0000000..b9ff362
--- /dev/null
+++ b/doc/materials/materials/MAT_ROSEWOOD
@@ -0,0 +1,24 @@
+Materialid:
+MAT_ROSEWOOD             "rosewood"
+
+Name:
+"Palisander" "Palisanders" "Palisander" "Palisander"
+
+Geschlecht:
+M
+
+Beschreibung:
+schokoladenbraun
+
+Erkennbarkeit:
+MAT_MISC_TROPICAL_WOOD:-5
+MAT_ROSEWOOD
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_TROPICAL_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_RUBY b/doc/materials/materials/MAT_RUBY
new file mode 100644
index 0000000..2f03c75
--- /dev/null
+++ b/doc/materials/materials/MAT_RUBY
@@ -0,0 +1,21 @@
+Materialid:
+MAT_RUBY                 "ruby"
+
+Name:
+"Rubin" "Rubins" "Rubin" "Rubin"
+
+Geschlecht:
+M
+
+Beschreibung:
+rot
+
+Erkennbarkeit:
+MAT_MISC_JEWEL:25
+MAT_RUBY
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_SALPETER b/doc/materials/materials/MAT_SALPETER
new file mode 100644
index 0000000..aaca1be
--- /dev/null
+++ b/doc/materials/materials/MAT_SALPETER
@@ -0,0 +1,17 @@
+Materialid:
+MAT_SALPETER             "salpeter"
+
+Name:
+"Salpeter" "Salpeters" "Salpeter" "Salpeter"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SALT b/doc/materials/materials/MAT_SALT
new file mode 100644
index 0000000..1d54a9e
--- /dev/null
+++ b/doc/materials/materials/MAT_SALT
@@ -0,0 +1,18 @@
+Materialid:
+MAT_SALT                 "natriumchloride"
+
+Name:
+"Salz" "Salzes" "Salz" "Salz"
+
+Geschlecht:
+N
+
+Beschreibung:
+Kochsalz
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_EATABLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SAND b/doc/materials/materials/MAT_SAND
new file mode 100644
index 0000000..af33552
--- /dev/null
+++ b/doc/materials/materials/MAT_SAND
@@ -0,0 +1,17 @@
+Materialid:
+MAT_SAND                 "sand"
+
+Name:
+"Sand" "Sands" "Sand" "Sand"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SANDAL b/doc/materials/materials/MAT_SANDAL
new file mode 100644
index 0000000..f76590d
--- /dev/null
+++ b/doc/materials/materials/MAT_SANDAL
@@ -0,0 +1,24 @@
+Materialid:
+MAT_SANDAL               "sandal"
+
+Name:
+"Sandelholz" "Sandelholzes" "Sandelholz" "Sandelholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+gut riechend, kostbar, hart
+
+Erkennbarkeit:
+MAT_MISC_TROPICAL_WOOD:-5
+MAT_SANDAL
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_TROPICAL_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_SAPPHIRE b/doc/materials/materials/MAT_SAPPHIRE
new file mode 100644
index 0000000..436800c
--- /dev/null
+++ b/doc/materials/materials/MAT_SAPPHIRE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_SAPPHIRE             "sapphire"
+
+Name:
+"Saphir" "Saphirs" "Saphir" "Saphir"
+
+Geschlecht:
+M
+
+Beschreibung:
+blau
+
+Erkennbarkeit:
+MAT_MISC_JEWEL:25
+MAT_SAPPHIRE
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_SATIN b/doc/materials/materials/MAT_SATIN
new file mode 100644
index 0000000..f2a6e3b
--- /dev/null
+++ b/doc/materials/materials/MAT_SATIN
@@ -0,0 +1,21 @@
+Materialid:
+MAT_SATIN                "satin"
+
+Name:
+"Satin" "Satins" "Satin" "Satin"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_CLOTH:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SHADOW b/doc/materials/materials/MAT_SHADOW
new file mode 100644
index 0000000..5de459b
--- /dev/null
+++ b/doc/materials/materials/MAT_SHADOW
@@ -0,0 +1,17 @@
+Materialid:
+MAT_SHADOW               "shadow"
+
+Name:
+"Schatten" "Schattens" "Schatten" "Schatten"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_MISC:100
diff --git a/doc/materials/materials/MAT_SHIT b/doc/materials/materials/MAT_SHIT
new file mode 100644
index 0000000..12d05d4
--- /dev/null
+++ b/doc/materials/materials/MAT_SHIT
@@ -0,0 +1,17 @@
+Materialid:
+MAT_SHIT                 "shit"
+
+Name:
+"Scheisse" "Scheisse" "Scheisse" "Scheisse"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SILK b/doc/materials/materials/MAT_SILK
new file mode 100644
index 0000000..eabdec8
--- /dev/null
+++ b/doc/materials/materials/MAT_SILK
@@ -0,0 +1,22 @@
+Materialid:
+MAT_SILK                 "silk"
+
+Name:
+"Seide" "Seide" "Seide" "Seide"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_CLOTH:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SILVER b/doc/materials/materials/MAT_SILVER
new file mode 100644
index 0000000..e4447e5
--- /dev/null
+++ b/doc/materials/materials/MAT_SILVER
@@ -0,0 +1,19 @@
+Materialid:
+MAT_SILVER               "Ag"
+
+Name:
+"Silber" "Silbers" "Silber" "Silber"
+
+Geschlecht:
+N
+
+Beschreibung:
+weiss
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
+MATGROUP_PRECIOUS_METAL:100
diff --git a/doc/materials/materials/MAT_SKIN b/doc/materials/materials/MAT_SKIN
new file mode 100644
index 0000000..231c8f0
--- /dev/null
+++ b/doc/materials/materials/MAT_SKIN
@@ -0,0 +1,20 @@
+Materialid:
+MAT_SKIN                 "skin"
+
+Name:
+"Haut" "Haut" "Haut" "Haut"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLEXIBLE:100
+MATGROUP_DEAD:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SLATE b/doc/materials/materials/MAT_SLATE
new file mode 100644
index 0000000..87fd40c
--- /dev/null
+++ b/doc/materials/materials/MAT_SLATE
@@ -0,0 +1,18 @@
+Materialid:
+MAT_SLATE                "slate"
+
+Name:
+"Schiefer" "Schiefers" "Schiefer" "Schiefer"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_STONE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SLIME b/doc/materials/materials/MAT_SLIME
new file mode 100644
index 0000000..49c08a9
--- /dev/null
+++ b/doc/materials/materials/MAT_SLIME
@@ -0,0 +1,17 @@
+Materialid:
+MAT_SLIME                "slime"
+
+Name:
+"Schleim" "Schleims" "Schleim" "Schleim"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLUID:100
diff --git a/doc/materials/materials/MAT_SOAP b/doc/materials/materials/MAT_SOAP
new file mode 100644
index 0000000..b791da9
--- /dev/null
+++ b/doc/materials/materials/MAT_SOAP
@@ -0,0 +1,18 @@
+Materialid:
+MAT_SOAP                 "soap"
+
+Name:
+"Seife" "Seifes" "Seife" "Seife"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLUID:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SODIUM b/doc/materials/materials/MAT_SODIUM
new file mode 100644
index 0000000..30c8db4
--- /dev/null
+++ b/doc/materials/materials/MAT_SODIUM
@@ -0,0 +1,21 @@
+Materialid:
+MAT_SODIUM               "Na"
+
+Name:
+"Natrium" "Natriums" "Natrium" "Natrium"
+
+Geschlecht:
+N
+
+Beschreibung:
+Wasser-Vorsicht
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_ALUMINIUM:30
+MAT_SODIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SPRUCE b/doc/materials/materials/MAT_SPRUCE
new file mode 100644
index 0000000..699f4f9
--- /dev/null
+++ b/doc/materials/materials/MAT_SPRUCE
@@ -0,0 +1,24 @@
+Materialid:
+MAT_SPRUCE               "spruce"
+
+Name:
+"Fichtenholz" "Fichtenholzes" "Fichtenholz" "Fichtenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+trocken, bruechig
+
+Erkennbarkeit:
+MAT_MISC_CONIFER_WOOD:15
+MAT_SPRUCE
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_CONIFER_WOOD:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_STEEL b/doc/materials/materials/MAT_STEEL
new file mode 100644
index 0000000..fdc6cad
--- /dev/null
+++ b/doc/materials/materials/MAT_STEEL
@@ -0,0 +1,19 @@
+Materialid:
+MAT_STEEL                "steel"
+
+Name:
+"Stahl" "Stahls" "Stahl" "Stahl"
+
+Geschlecht:
+N
+
+Beschreibung:
+hart, flexibel
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_MAGNETIC:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_STRAW b/doc/materials/materials/MAT_STRAW
new file mode 100644
index 0000000..0882c0c
--- /dev/null
+++ b/doc/materials/materials/MAT_STRAW
@@ -0,0 +1,20 @@
+Materialid:
+MAT_STRAW                "straw"
+
+Name:
+"Stroh" "Strohs" "Stroh" "Stroh"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_HERBAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_SULFUR b/doc/materials/materials/MAT_SULFUR
new file mode 100644
index 0000000..caf8a5b
--- /dev/null
+++ b/doc/materials/materials/MAT_SULFUR
@@ -0,0 +1,20 @@
+Materialid:
+MAT_SULFUR               "S"
+
+Name:
+"Schwefel" "Schwefels" "Schwefel" "Schwefel"
+
+Geschlecht:
+M
+
+Beschreibung:
+gelb
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_TALLOW b/doc/materials/materials/MAT_TALLOW
new file mode 100644
index 0000000..72ebf0b
--- /dev/null
+++ b/doc/materials/materials/MAT_TALLOW
@@ -0,0 +1,20 @@
+Materialid:
+MAT_TALLOW               "tallow"
+
+Name:
+"Talg" "Talgs" "Talg" "Talg"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_DEAD:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_TAR b/doc/materials/materials/MAT_TAR
new file mode 100644
index 0000000..edad3ca
--- /dev/null
+++ b/doc/materials/materials/MAT_TAR
@@ -0,0 +1,17 @@
+Materialid:
+MAT_TAR                  "tar"
+
+Name:
+"Teer" "Teers" "Teer" "Teer"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_TEAK b/doc/materials/materials/MAT_TEAK
new file mode 100644
index 0000000..3a2d767
--- /dev/null
+++ b/doc/materials/materials/MAT_TEAK
@@ -0,0 +1,24 @@
+Materialid:
+MAT_TEAK                 "teak"
+
+Name:
+"Teakholz" "Teakholzes" "Teakholz" "Teakholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_TROPICAL_WOOD:25
+MAT_TEAK
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_TROPICAL_WOOD:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_THALLIUM b/doc/materials/materials/MAT_THALLIUM
new file mode 100644
index 0000000..45ce091
--- /dev/null
+++ b/doc/materials/materials/MAT_THALLIUM
@@ -0,0 +1,22 @@
+Materialid:
+MAT_THALLIUM             "thallium"
+
+Name:
+"Thallium" "Thalliums" "Thallium" "Thallium"
+
+Geschlecht:
+N
+
+Beschreibung:
+giftig, schwer, weich
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_LEAD:50
+MAT_THALLIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_FLEXIBLE:100
+MATGROUP_METAL:100
+MATGROUP_POISONOUS:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_TIN b/doc/materials/materials/MAT_TIN
new file mode 100644
index 0000000..8f29454
--- /dev/null
+++ b/doc/materials/materials/MAT_TIN
@@ -0,0 +1,22 @@
+Materialid:
+MAT_TIN                  "Sn"
+
+Name:
+"Zinn" "Zinns" "Zinn" "Zinn"
+
+Geschlecht:
+N
+
+Beschreibung:
+sehr weich, weiss
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_SILVER:15
+MAT_LEAD:30
+MAT_TIN
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_TITANIUM b/doc/materials/materials/MAT_TITANIUM
new file mode 100644
index 0000000..7c54374
--- /dev/null
+++ b/doc/materials/materials/MAT_TITANIUM
@@ -0,0 +1,20 @@
+Materialid:
+MAT_TITANIUM             "Ti"
+
+Name:
+"Titan" "Titans" "Titan" "Titan"
+
+Geschlecht:
+N
+
+Beschreibung:
+aeusserst hart
+
+Erkennbarkeit:
+MAT_MISC_METAL:10
+MAT_STEEL:20
+MAT_TITANIUM
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_TNT b/doc/materials/materials/MAT_TNT
new file mode 100644
index 0000000..b9af3ec
--- /dev/null
+++ b/doc/materials/materials/MAT_TNT
@@ -0,0 +1,19 @@
+Materialid:
+MAT_TNT                  "tnt"
+
+Name:
+"TNT" "TNT" "TNT" "TNT"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_EXPLOSIVE:100
diff --git a/doc/materials/materials/MAT_TOOTH b/doc/materials/materials/MAT_TOOTH
new file mode 100644
index 0000000..e4588a5
--- /dev/null
+++ b/doc/materials/materials/MAT_TOOTH
@@ -0,0 +1,20 @@
+Materialid:
+MAT_TOOTH                "tooth"
+
+Name:
+"Zahn" "Zahns" "Zahn" "Zahn"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_BIO:100
+MATGROUP_DEAD:100
+MATGROUP_SOLID:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_TOPAZ b/doc/materials/materials/MAT_TOPAZ
new file mode 100644
index 0000000..d39d5e1
--- /dev/null
+++ b/doc/materials/materials/MAT_TOPAZ
@@ -0,0 +1,21 @@
+Materialid:
+MAT_TOPAZ                "topaz"
+
+Name:
+"Topas" "Topas" "Topas" "Topas"
+
+Geschlecht:
+M
+
+Beschreibung:
+gelb
+
+Erkennbarkeit:
+MAT_MISC_JEWEL:25
+MAT_TOPAZ
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_TUNGSTEN b/doc/materials/materials/MAT_TUNGSTEN
new file mode 100644
index 0000000..7a7c643
--- /dev/null
+++ b/doc/materials/materials/MAT_TUNGSTEN
@@ -0,0 +1,19 @@
+Materialid:
+MAT_TUNGSTEN             "W"
+
+Name:
+"Wolfram" "Wolframs" "Wolfram" "Wolfram"
+
+Geschlecht:
+N
+
+Beschreibung:
+sehr hart, hitzebestaendig
+
+Erkennbarkeit:
+MAT_MISC_METAL:25
+MAT_TUNGSTEN
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_TURQUOISE b/doc/materials/materials/MAT_TURQUOISE
new file mode 100644
index 0000000..dfe30f9
--- /dev/null
+++ b/doc/materials/materials/MAT_TURQUOISE
@@ -0,0 +1,21 @@
+Materialid:
+MAT_TURQUOISE            "turquoise"
+
+Name:
+"Tuerkis" "Tuerkis" "Tuerkis" "Tuerkis"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+MAT_MISC_JEWEL:25
+MAT_TURQUOISE
+
+Gruppenzugehoerigkeit:
+MATGROUP_MINERAL:100
+MATGROUP_SOLID:100
+MATGROUP_JEWEL:100
+MATGROUP_STONE:100
diff --git a/doc/materials/materials/MAT_VELVET b/doc/materials/materials/MAT_VELVET
new file mode 100644
index 0000000..e2a5634
--- /dev/null
+++ b/doc/materials/materials/MAT_VELVET
@@ -0,0 +1,21 @@
+Materialid:
+MAT_VELVET               "velvet"
+
+Name:
+"Samt" "Samts" "Samt" "Samt"
+
+Geschlecht:
+M
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_CLOTH:100
+MATGROUP_BIO:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_WATER b/doc/materials/materials/MAT_WATER
new file mode 100644
index 0000000..900498f
--- /dev/null
+++ b/doc/materials/materials/MAT_WATER
@@ -0,0 +1,18 @@
+Materialid:
+MAT_WATER                "water"
+
+Name:
+"Wasser" "Wassers" "Wasser" "Wasser"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_ELEMENTAL:100
+MATGROUP_FLUID:100
diff --git a/doc/materials/materials/MAT_WAX b/doc/materials/materials/MAT_WAX
new file mode 100644
index 0000000..baa163c
--- /dev/null
+++ b/doc/materials/materials/MAT_WAX
@@ -0,0 +1,17 @@
+Materialid:
+MAT_WAX                  "wax"
+
+Name:
+"Wachs" "Wachses" "Wachs" "Wachs"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_WECKAMIN b/doc/materials/materials/MAT_WECKAMIN
new file mode 100644
index 0000000..d22becb
--- /dev/null
+++ b/doc/materials/materials/MAT_WECKAMIN
@@ -0,0 +1,18 @@
+Materialid:
+MAT_WECKAMIN             "weckamine"
+
+Name:
+"Aufputschmittel" "Aufputschmittels" "Aufputschmittel" "Aufputschmittel"
+
+Geschlecht:
+N
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DRUG:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_WILLOW b/doc/materials/materials/MAT_WILLOW
new file mode 100644
index 0000000..0e5f0ef
--- /dev/null
+++ b/doc/materials/materials/MAT_WILLOW
@@ -0,0 +1,25 @@
+Materialid:
+MAT_WILLOW               "willow"
+
+Name:
+"Weidenholz" "Weidenholzes" "Weidenholz" "Weidenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+weich und flexibel
+
+Erkennbarkeit:
+MAT_MISC_DECIDUOUS_WOOD:5
+MAT_WILLOW
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_HERBAL:100
+MATGROUP_DECIDUOUS_WOOD:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
diff --git a/doc/materials/materials/MAT_WOODEN_PLANT b/doc/materials/materials/MAT_WOODEN_PLANT
new file mode 100644
index 0000000..0c9c795
--- /dev/null
+++ b/doc/materials/materials/MAT_WOODEN_PLANT
@@ -0,0 +1,22 @@
+Materialid:
+MAT_WOODEN_PLANT         "wooden_plant"
+
+Name:
+"Baum" "Baumes" "Baum" "Baum"
+
+Geschlecht:
+M
+
+Beschreibung:
+Pflanzen mit Holzanteil (Baeume, Gebuesche)
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_HERBAL:100
+MATGROUP_BIO:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_SOLID:100
+MATGROUP_WOOD:100
+MATGROUP_LIVING:100
diff --git a/doc/materials/materials/MAT_WOOL b/doc/materials/materials/MAT_WOOL
new file mode 100644
index 0000000..8e2ae07
--- /dev/null
+++ b/doc/materials/materials/MAT_WOOL
@@ -0,0 +1,22 @@
+Materialid:
+MAT_WOOL                 "wool"
+
+Name:
+"Wolle" "Wolle" "Wolle" "Wolle"
+
+Geschlecht:
+F
+
+Beschreibung:
+- nicht vorhanden -
+
+Erkennbarkeit:
+- keine Einschraenkung -
+
+Gruppenzugehoerigkeit:
+MATGROUP_DEAD:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_CLOTH:100
+MATGROUP_SOLID:100
diff --git a/doc/materials/materials/MAT_YEW b/doc/materials/materials/MAT_YEW
new file mode 100644
index 0000000..cef8f66
--- /dev/null
+++ b/doc/materials/materials/MAT_YEW
@@ -0,0 +1,26 @@
+Materialid:
+MAT_YEW                  "yew"
+
+Name:
+"Eibenholz" "Eibenholzes" "Eibenholz" "Eibenholz"
+
+Geschlecht:
+N
+
+Beschreibung:
+zaeh, biegsam
+
+Erkennbarkeit:
+MAT_MISC_CONIFER_WOOD:5
+MAT_YEW
+
+Gruppenzugehoerigkeit:
+MATGROUP_INFLAMMABLE:100
+MATGROUP_BIO:100
+MATGROUP_SOLID:100
+MATGROUP_POISONOUS:100
+MATGROUP_WOOD:100
+MATGROUP_HERBAL:100
+MATGROUP_CONIFER_WOOD:100
+MATGROUP_DEAD:100
+MATGROUP_FLEXIBLE:100
diff --git a/doc/materials/materials/MAT_ZINC b/doc/materials/materials/MAT_ZINC
new file mode 100644
index 0000000..4970e17
--- /dev/null
+++ b/doc/materials/materials/MAT_ZINC
@@ -0,0 +1,22 @@
+Materialid:
+MAT_ZINC                 "zinc"
+
+Name:
+"Zink" "Zinks" "Zink" "Zink"
+
+Geschlecht:
+N
+
+Beschreibung:
+weich, bestaendig
+
+Erkennbarkeit:
+MAT_MISC_METAL:0
+MAT_SILVER:15
+MAT_LEAD:30
+MAT_ZINC
+
+Gruppenzugehoerigkeit:
+MATGROUP_METAL:100
+MATGROUP_FLEXIBLE:100
+MATGROUP_SOLID:100
diff --git a/doc/mcmd/.readme b/doc/mcmd/.readme
new file mode 100644
index 0000000..017db98
--- /dev/null
+++ b/doc/mcmd/.readme
@@ -0,0 +1,3 @@
+Hier findest Du Hilfeseiten zu den Befehlen, die die Magiershell zur
+Verfuegung stellt.
+
diff --git a/doc/mcmd/.synonym b/doc/mcmd/.synonym
new file mode 100644
index 0000000..1f6138a
--- /dev/null
+++ b/doc/mcmd/.synonym
@@ -0,0 +1,3 @@
+heal heile
+peace frieden
++ goto
diff --git a/doc/mcmd/addguildmaster b/doc/mcmd/addguildmaster
new file mode 100644
index 0000000..3bab6dd
--- /dev/null
+++ b/doc/mcmd/addguildmaster
@@ -0,0 +1,25 @@
+
+addguildmaster
+--------------
+
+ ERZMAGIERKOMMANDO:
+    addguildmaster <name> <gilde>
+
+ ARGUMENTE:
+
+     <name>
+        Name des Magiers
+     <gilde>
+        Name einer Gilde
+
+ BESCHREIBUNG:
+    Der Magier <name> wird zum Gildenmagier fuer die Gilde <gilde> 
+    ernannt.
+
+    Dieser Befehl steht nur Goettern zur Verfuegung.
+
+ SIEHE AUCH:
+    removeguildmaster, gilden
+
+ LETZTE AeNDERUNG:
+    2003-12-10, Zook
diff --git a/doc/mcmd/addmaster b/doc/mcmd/addmaster
new file mode 100644
index 0000000..20689e6
--- /dev/null
+++ b/doc/mcmd/addmaster
@@ -0,0 +1,24 @@
+
+addmaster
+---------
+
+ ERZMAGIERKOMMANDO:
+    addmaster <name> <region>
+
+ ARGUMENTE:
+
+     <name>
+        Name des Magiers
+     <region>
+        Eine Region
+
+ BESCHREIBUNG:
+    Der Magier <name> wird in der Region <region> zum Regionsmagier ernannt.
+
+    Dieser Befehl steht nur Erzmagiern und Goettern zur Verfuegung.
+
+ SIEHE AUCH:
+    removemaster, regionen
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/at b/doc/mcmd/at
new file mode 100644
index 0000000..0315b73
--- /dev/null
+++ b/doc/mcmd/at
@@ -0,0 +1,33 @@
+
+at
+--
+
+ MAGIERKOMMANDO:
+    at <name> <kommando>
+
+ ARGUMENTE:
+
+     <raum>
+        Name eines Lebewesens
+     <kommando>
+        das auzufuehrende Kommando
+
+ BESCHREIBUNG:
+    Du wirst, unsichtbar fuer die Anwesenden, in den Raum transportiert, in
+    dem sich das Lebewesen <name> befindet. Dort fuehrst Du das Kommando
+    <kommando> aus und gelangst automatisch wieder in den Raum zurueck, in dem
+    Du vorher warst.
+
+ BEISPIELE:
+    Catweazle macht mal wieder Faxen?
+
+    > at catweazle wuerge catweazle
+
+    Die Umstehenden sehen dann zwar die Meldungen des Kommandos, aber man
+    selbst befindet sich sofort wieder in Sicherheit :)
+
+ SIEHE AUCH:
+    in, goto
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/banish b/doc/mcmd/banish
new file mode 100644
index 0000000..07104ef
--- /dev/null
+++ b/doc/mcmd/banish
@@ -0,0 +1,38 @@
+
+banish
+------
+
+ MAGIERKOMMANDO:
+    banish [-f] <name> [<grund>]
+
+ ARGUMENTE:
+
+    -f (optional)
+        Sperrung erzwingen
+     <name>
+        Der zu sperrende Name
+     <grund> (optional)
+        Der Grund der Sperre
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kann man bestimmte Namen sperren, so dass sich keine
+    Spieler mit gleichem Namen einloggen koennen. Sinnvoll ist dies z.B. bei
+    Namen von NPCs oder Oertlichkeiten, wo es ggf. zu Verwirrungen kommen
+    koennte.
+
+    Als gute Praxis sollte man in den Grund auch angeben, fuer welchen 
+    Magier der Name gesperrt wurde. 
+
+    Mit der Option -f kann man auch Namen von existenten Spielern banishen.
+    Diese koennen sich weiterhin noch einloggen, aber sobald der Spieler sich
+    loescht, ist der Name gesperrt.
+
+    Mit dem Grund 'loeschen' koennen gesperrte Namen wieder freigegeben werden.
+
+    Dieser Befehl kann nur von Regions- und hoeheren Magiern ausgefuehrt werden.
+
+ SIEHE AUCH:
+    mbanish, tbanish, sbanish
+
+ LETZTE AeNDERUNG:
+    Don,  2. Mai 2013, 11:45:08 von Zook
diff --git a/doc/mcmd/cat b/doc/mcmd/cat
new file mode 100644
index 0000000..64ab02b
--- /dev/null
+++ b/doc/mcmd/cat
@@ -0,0 +1,23 @@
+
+cat
+---
+
+ MAGIERKOMMANDO:
+    cat <datei>
+
+ ARGUMENTE:
+
+     <datei>
+        Die auszugebende Datei
+
+ BESCHREIBUNG:
+    Bis zu 40 Zeilen der Datei <datei> werden ausgegeben. Beim Abbruch
+    erscheint die Meldung
+
+    *****TRUNCATED****
+
+ SIEHE AUCH:
+    head, tail, more
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/cd b/doc/mcmd/cd
new file mode 100644
index 0000000..8d774b6
--- /dev/null
+++ b/doc/mcmd/cd
@@ -0,0 +1,37 @@
+
+cd
+--
+
+ MAGIERKOMMANDO:
+    cd [ - ] [ -l ] [ -s ] [ <Verzeichnis> ]
+
+ ARGUMENTE:
+
+     <Verzeichnis>  neuer Pfad, der gesetzt werden soll
+
+ BESCHREIBUNG:
+    Das aktuelle Verzeichnis wird auf <Verzeichnis> gesetzt.
+
+    Laesst man <Verzeichnis> weg, wird das aktuelle Verzeichnis auf das
+    Heimatverzeichnis gesetzt. Bei einem Magier mit Magierlevel 15 ist dies
+    das Verzeichnis `/doc', ab Magierlevel 20 das Verzeichnis
+    `/players/<Magiername>'.
+
+    Bei Eindeutigkeit koennen Platzhalter verwendet werden. Die Befehle 
+    cd /std/player/shadows und cd /*/pl*/sh* bewirken dasselbe.
+
+    Mit der Option - wird direkt ins letzte Verzeichnis gewechselt.
+
+    Mit den Optionen -l und -s kann man die Behandlung von `.readme'-Dateien
+    im Zielverzeichnis einstellen. Mit der Option -s wird die Ausgabe der
+    `.readme'-Dateien verhindert, mit -l dagegen erlaubt.
+
+    Ueber die Variable 'CD_SHORT' kann eingestellt werden, ob `.readme'-
+    Dateien grundsaetzlich unterdrueckt werden sollen. Dazu einfach 
+    'set CD_SHORT 1' eingeben
+
+ SIEHE AUCH:
+    prompt, pwd, ls, set
+
+ LETZTE AENDERUNG:
+    03.06.2015, Bugfix
diff --git a/doc/mcmd/clone b/doc/mcmd/clone
new file mode 100644
index 0000000..04f4627
--- /dev/null
+++ b/doc/mcmd/clone
@@ -0,0 +1,36 @@
+
+clone
+-----
+
+ MAGIERKOMMANDO:
+    clone [-f] <objektname>
+
+ ARGUMENTE:
+
+     <objektname> Dateiname des Objektes, das erzeugt werden soll
+
+ OPTIONEN:
+
+     -f:  Erzwingt den Versuch des Clonens ohne Ueberpruefung, ob eine
+          Datei existiert. Diese Option sollte nur in Ausnahmefaellen
+          verwendet werden, da Fehlschlaege sofort als Fehlermeldung im
+          Driverlog auftauchen.
+
+ BESCHREIBUNG:
+    Eine Kopie von einem Objekt, das im File <objektname> beschrieben ist,
+    wird erzeugt. Das Objekt befindet sich danach im Inventar des Magiers
+    oder (wenn in dem Objekt P_NOGET gesetzt ist) im gleichen Raum wie der
+    Magier.
+
+    Die Blueprint wird zu nichts anderem gebraucht, als Kopien zu erzeugen.
+
+    Wenn ein Objekt noch nicht geladen ist, wird es neu geladen.
+
+    Raeume sollten niemals geclont werden, da sie immer nur einmal existieren
+    sollten und nicht in mehrern Kopien.
+
+ SIEHE AUCH:
+    load, destruct, upd, setcmsg
+
+ LETZTE AENDERUNG:
+    Mit, 05.06.2003, 18:00:00 von Mandragon
diff --git a/doc/mcmd/cp b/doc/mcmd/cp
new file mode 100644
index 0000000..b5cf5b5
--- /dev/null
+++ b/doc/mcmd/cp
@@ -0,0 +1,45 @@
+
+cp
+--
+
+ MAGIERKOMMANDO:
+    cp [ -irfv ] <von> <nach>
+    cp [ -irfv ] <von_1> [ ... <von_n> ] <verz>
+    cp [ -irfvm ] <von_1> [ ... <von_n> ] <verz> <maske>
+ 
+ ARGUMENTE:
+
+     <von>   Name der Quelldatei
+     <von_x> Name von Quelldateien oder -verzeichnissen
+     <nach>  Name der Zieldatei
+     <verz>  Name des Zielverzeichnisses
+     <maske> Dateimaske
+
+ BESCHREIBUNG:
+    Die Datei <von> wird nach Datei <nach> kopiert, bzw. die Datei(en) <von_1>
+    (- <von_n>) in das Verzeichnis <verz>.
+
+    Die Dateinamen koennen auch Muster wie * oder ? enthalten.
+
+    Zusaetzlich koennen folgende Flags angegeben werden:
+
+     -i  Vor jedem kopieren wird gefragt, ob kopiert werden soll
+     -r  Unterverzeichnisse werden rekursiv kopiert
+     -f  Vor dem Ueberschreiben einer existierenden Datei wird nicht gefragt
+     -v  Es wird jeder Arbeitsschritt auf dem Bildschirm ausgegeben
+     -m  Es werden nur Dateien kopiert, die der Maske <maske> entsprechen.
+         Letzere wird im glob-Format angegeben. (z.B. *.c, save*.o)
+    
+ BEISPIELE:
+
+    > cp datei1 datei2
+    > cp *.c temp
+    > cp /doc/s* .
+    > cp -irm /doc ~/KURS *.c 
+    > cp -rf ~mandragon/meloran /d/inseln/mandragon
+
+ SIEHE AUCH:
+    mv, rm, mkdir, rmdir
+
+ LETZTE AENDERUNG:
+    Mit, 02.10.2002, 02:00:00 von Mandragon
diff --git a/doc/mcmd/destruct b/doc/mcmd/destruct
new file mode 100644
index 0000000..e85ed4c
--- /dev/null
+++ b/doc/mcmd/destruct
@@ -0,0 +1,30 @@
+
+destruct
+--------
+
+ MAGIERKOMMANDO:
+    destruct <objekt>
+
+ ARGUMENTE:
+
+     <objekt>   ID eines Objektes
+
+ BESCHREIBUNG:
+    Wenn es ein Objekt in der Umgebung (Inventory, Raum) gibt, das auf
+    <objekt> anspricht, wird dieses Objekt zerstoert.
+
+    In dem Objekt wird remove() aufgerufen, um es zu zerstoeren. Falls dies
+    nicht zur Zerstoerung des Objektes fuehrt (weil das Objekt gar kein
+    remove() definiert oder die remove()-Funktion das Zerstoeren verhindert),
+    bleibt das Objekt erhalten.
+
+ BEMERKUNGEN:
+    Spieler duerfen niemals zerstoert werden!
+
+ SIEHE AUCH:
+    load, clone, upd, setdmsg,
+    remove(L), destruct(E)
+
+ LETZTE AENDERUNG:
+    11.06.2014, Zesstra
+
diff --git a/doc/mcmd/do b/doc/mcmd/do
new file mode 100644
index 0000000..180f453
--- /dev/null
+++ b/doc/mcmd/do
@@ -0,0 +1,30 @@
+
+do
+--
+
+ MAGIERKOMMANDO:
+    do <cmds>
+
+ ARGUMENTE:
+
+     <cmds>
+        Liste von Befehlen (mit `;' getrennt)
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kann man mehrere andere Befehle nacheinander ausfuehren.
+    Dies laesst sich zum Beispiel im Zusammenhang mit Aliasen ausnutzen.
+
+    Die einzelnen Befehle werden durch Semikolons (`;') getrennt; in den
+    Befehlen selbst duerfen keine Semikolons vorkommen.
+
+ BEISPIELE:
+    Wenn man sein Erscheinen in der Abenteurergilde mit einem freundlichen
+    Gruss verbinden moechte, koennte man folgendermassen vorgehen:
+
+    > do goto /gilden/abenteurer; sag Hallo!
+
+ SIEHE AUCH:
+    alias
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/echoall b/doc/mcmd/echoall
new file mode 100644
index 0000000..3abefd3
--- /dev/null
+++ b/doc/mcmd/echoall
@@ -0,0 +1,24 @@
+
+echoall
+-------
+
+ MAGIERKOMMANDO:
+    echoall <text>
+
+ ARGUMENTE:
+
+     <text>
+        ein beliebiger Text
+
+ BESCHREIBUNG:
+    Der Text <text> wird an alle Spieler im MUD geschickt.
+
+    Der Befehl sollte mit groesster Sorgfalt behandelt werden. In diesem
+    Zusammenhang sei auch nochmal darauf hingewiesen, dass Echos in fremdem
+    Namen nicht erlaubt sind.
+
+ SIEHE AUCH:
+    vertrag, emote, echo, echoto, mecho, teile (mit)
+
+ LETZTE AeNDERUNG:
+    Tue, 02.09.1997, 16:54:18 von Wargon
diff --git a/doc/mcmd/echoto b/doc/mcmd/echoto
new file mode 100644
index 0000000..5528312
--- /dev/null
+++ b/doc/mcmd/echoto
@@ -0,0 +1,26 @@
+
+echoto
+------
+
+ MAGIERKOMMANDO:
+    echoto <name> <text>
+
+ ARGUMENTE:
+
+     <name>
+        Name eines (eingeloggten) Spielers oder NPCs
+     <text>
+        ein beliebiger Text
+
+ BESCHREIBUNG:
+    Der Text <text> wird an das Lebewesen <name> geschickt.
+
+    Der Befehl sollte mit Sorgfalt behandelt werden. In diesem Zusammenhang
+    sei auch nochmal darauf hingewiesen, dass Echos in fremdem Namen nicht
+    erlaubt sind.
+
+ SIEHE AUCH:
+    vertrag, emote, echo, echoall, mecho, teile (mit)
+
+ LETZTE AeNDERUNG:
+    Tue, 02.09.1997, 16:54:38 von Wargon
diff --git a/doc/mcmd/ed b/doc/mcmd/ed
new file mode 100644
index 0000000..8fc5be1
--- /dev/null
+++ b/doc/mcmd/ed
@@ -0,0 +1,47 @@
+
+ed
+--
+
+ MAGIERKOMMANDO:
+    ed <datei> [...]
+
+ ARGUMENTE:
+
+     <datei> [...]
+        Name(n) der zu bearbeitenden Datei(en)
+
+ BESCHREIBUNG:
+    Startet den Editor nacheinander mit jeder angegebenen Datei.
+
+    Die Dateinamen koennen auch Muster wie * oder ? enthalten.
+
+    Der Editor ist im grossen und ganzen kompatibel zum UNIX Editor ed. Er ist
+    zeilenorientiert und funktioniert nach einem einfachen Prinzip.
+
+    Es gibt zwei Modi: Kommandomodus und Eingabemodus
+
+     Kommandomodus:
+
+         * Hat das Prompt `:'
+         * Akzeptiert Befehle der Art [<addresse>]<befehl>
+           Hierbei besteht <adresse> aus einer Zeilennummer oder aus einer
+           Angabe die mehrere Zeilen adressiert: zeile1,zeile2
+           Die Befehle koennen mit dem Befehl `h' abgerufen werden.
+
+     Eingabemodus:
+
+         * Hat keinen Prompt.
+         * Akzeptiert Eingaben, bis auf einer einzelnen Zeile ein `.'
+           eingegeben wird, das wechselt wieder in den Kommandomodus.
+
+ BEISPIELE:
+
+    > ed test.c
+    > ed *.c
+
+ SIEHE AUCH:
+    more, head, tail, load, clone
+    ed0(LPC), ed1(LPC), ed2(LPC), ed3(LPC), ed4(LPC), ed5(LPC)
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/exec b/doc/mcmd/exec
new file mode 100644
index 0000000..6390f0f
--- /dev/null
+++ b/doc/mcmd/exec
@@ -0,0 +1,29 @@
+
+exec
+----
+
+ MAGIERKOMMANDO:
+    exec <datei>
+
+ ARGUMENTE:
+
+     <datei>
+        Dateiname des Zielobjektes
+
+ BESCHREIBUNG:
+    Es wird versucht, einen Clone von <datei> zu erschaffen und Deine
+    Verbindung zum MUD an diesen Clone weiterzugeben. Das Objekt sollte dazu
+    moeglichst eine Rassenshell, zumindest aber `/std/player/base.c' geerbt
+    haben.
+
+    Das Objekt muss die gleiche UID besitzen wie Du (sprich: es muss sich in
+    einem Deiner Verzeichnisse befinden).
+
+    Dieser Befehl kann nur von Regions- und hoeheren Magiern ausgefuehrt
+    werden.
+
+ SIEHE AUCH:
+    exec(E)
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/frieden b/doc/mcmd/frieden
new file mode 100644
index 0000000..5f8f10f
--- /dev/null
+++ b/doc/mcmd/frieden
@@ -0,0 +1,24 @@
+
+frieden
+-------
+
+ MAGIERKOMMANDO:
+    frieden [<name>]
+
+ ARGUMENTE:
+
+     <name> (optional)
+        Name eines Lebewesens
+
+ BESCHREIBUNG:
+    Ohne Argument werden alle Kaempfe in dem Raum, in dem Du Dich gerade
+    befindest abgebrochen.
+
+    Mit Argument bricht das Lebewesen <name> den Kampf ab (d.h. es selbst
+    schlaegt nicht mehr zu, und auch seine Feinde greifen es nicht mehr an).
+
+ SIEHE AUCH:
+    stop
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/goto b/doc/mcmd/goto
new file mode 100644
index 0000000..587dda0
--- /dev/null
+++ b/doc/mcmd/goto
@@ -0,0 +1,52 @@
+
+goto
+----
+
+ MAGIERKOMMANDO:
+    goto <ziel>
+    +<magier>
+
+ ARGUMENTE:
+
+     <ziel>
+        Pfadname oder Name eines Lebewesens
+     <magier>
+        Name eines Magiers (mindestens Magierlevel 20)
+
+ BESCHREIBUNG:
+    Mit diesen Befehlen gelangst Du an die gewuenschten Orte im MorgenGrauen.
+
+    Ist <ziel> ein Pfadname, so gelangst Du direkt in den angegebenen Raum.
+
+    Ist <ziel> dagegen der Name eines (eingeloggten) Spielers oder eines NPCs,
+    so wirst Du in den Raum teleportiert, in dem sich das entsprechende
+    Lebewesen aufhaelt.
+    
+    Wenn mehere NPCs des Namens existieren, kann man mit 'goto name nr' den
+    gewuenschten auswaehlen.
+
+    Eine schnelle Art, in den Arbeitsraum des Magiers <magier> zu gelangen,
+    ist das Kommando +<magier>.
+
+    Wenn Du Dich im Verfolgemodus befindet, wird dieser durch den Befehl
+    abgeschaltet.
+
+ BEISPIELE:
+    In Wargons Arbeitsraum steht immer ein NPC namens Errol. Deshalb kann man
+    auf folgende drei Arten in Wargons Arbeitsraum gelangen:
+
+    > goto ~wargon/workroom
+    > goto errol
+    > +wargon
+    
+    Es gibt im Morgengrauen zahlreiche Rehe. Zu den einzelnen gelangt man mit
+    > goto reh 1
+    > goto reh 2
+    > goto reh 3
+    > ...
+
+ SIEHE AUCH:
+    trans, in, at, setmmin, setmmout
+
+ LETZTE AeNDERUNG:
+    Sat, 29.07.2000, 18:00:00 von Silvana
diff --git a/doc/mcmd/grep b/doc/mcmd/grep
new file mode 100644
index 0000000..5b5a72f
--- /dev/null
+++ b/doc/mcmd/grep
@@ -0,0 +1,55 @@
+
+grep
+----
+
+ MAGIERKOMMANDO:
+    grep [ -chilnvrf ] <regexp> <name> [...]
+    grep [ -chilnvrmf ] <regexp> <name> [...] [<maske>]
+
+ ARGUMENTE:
+
+     <regexp>       regulaerer Suchausdruck
+     <name> [...]   Die zu durchsuchende(n) Datei(en) (oder Verzeichnisse)
+     <maske>        Maske, der eine Datei entsprechen muss (glob)
+
+ BESCHREIBUNG:
+    In den angegebenen Dateien wird nach dem regulaeren Ausdruck <regexp>
+    gesucht.
+
+    Die Dateinamen koennen auch Muster wie * oder ? enthalten.
+
+    Zusaetzlich koennen folgende Flags angegeben werden:
+
+     -c  Es wird nur die Zahl der gefundenen Zeilen pro Datei ausgegeben.
+     -h  Die Dateinamen werden nicht mit ausgegeben.
+     -i  Gross- und Kleinschreibung werden ignoriert.
+     -l  Die Dateinamen werden immer mit ausgegeben.
+     -n  Die Zeilennummern der gefundenen Zeilen werden ausgegeben.
+     -v  Es werden die Zeilen ausgegeben, in denen <regexp> *nicht* gefunden
+         wurde.
+     -r  Handelt es sich bei <name> um ein Verzeichnis, werden rekursiv
+         alle darin befindlichen Dateien durchsucht.
+     -m  Es werden nur Dateien durchsucht, die auf die Dateimaske <maske>
+         passen. Letztere wird in glob-Form angegeben.
+     -f  Alle Ausgaben von grep werden in die Datei grep.out im Verzeichnis
+         des Magiers (/players/magiername) ausgegeben.
+
+ BEMERKUNGEN:
+    Das Durchsuchen sehr langer Dateien oder von Verzeichnissen mit sehr
+    vielen Dateien sollte man moeglichst vermeiden, da dies sehr lagintensiv
+    ist.
+
+ BEISPIELE:
+
+    > grep P_N /sys/*.h
+    > grep -n ".*[ab]*" datei.c
+    > grep -rm "P_BLABLUBB" /std *.c -> sucht alle Dateien in /std, die
+                                        auf .c enden und P_BLABLUBB
+                                        enthalten 
+
+ SIEHE AUCH:
+    regexp(E), more, cat, head, tail
+
+ LETZTE AENDERUNG:
+    Mit, 02.10.2002, 02:00:00 von Mandragon
+ 
\ No newline at end of file
diff --git a/doc/mcmd/head b/doc/mcmd/head
new file mode 100644
index 0000000..c16ad5b
--- /dev/null
+++ b/doc/mcmd/head
@@ -0,0 +1,20 @@
+
+head
+----
+
+ MAGIERKOMMANDO:
+    head <datei>
+
+ ARGUMENTE:
+
+     <datei>
+        Die auszugebende Datei
+
+ BESCHREIBUNG:
+    Die ersten zehn Zeilen der Datei <datei> werden ausgegeben.
+
+ SIEHE AUCH:
+    cat, tail, more
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/heile b/doc/mcmd/heile
new file mode 100644
index 0000000..226982e
--- /dev/null
+++ b/doc/mcmd/heile
@@ -0,0 +1,27 @@
+
+heile
+-----
+
+ MAGIERKOMMANDO:
+    heile <ziel>
+
+ ARGUMENTE:
+
+     <ziel>
+        Das zu heilende Lebewesen
+
+ BESCHREIBUNG:
+    Das angegebene Lebewesen wird vollstaendig geheilt.
+
+    Der Befehl darf nur fuer Tests benutzt werden. Man sollte darauf achten,
+    dass man keine Spieler damit beeinflusst, und zwar weder im Guten (indem
+    man den Spieler heilt) noch im Boesen (indem man das Monster heilt, gegen
+    das der Spieler gerade kaempft).
+
+    Magier-Lehrlingen steht dieser Befehl noch nicht zur Verfuegung.
+
+ SIEHE AUCH:
+    vertrag
+
+ LETZTE AeNDERUNG:
+    Tue, 02.09.1997, 16:54:45 von Wargon
diff --git a/doc/mcmd/home b/doc/mcmd/home
new file mode 100644
index 0000000..48fc907
--- /dev/null
+++ b/doc/mcmd/home
@@ -0,0 +1,23 @@
+
+home
+----
+
+ MAGIERKOMMANDO:
+    home
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Du wirst in Deinen Workroom transportiert.
+
+    Magier-Lehrlingen steht dieser Befehl noch nicht zur Verfuegung, da sie
+    auch noch keinen Workroom haben.
+    Wenn Du Dich im Verfolgemodus befindet, wird dieser durch den Befehl
+    abgeschaltet.
+
+ SIEHE AUCH:
+    goto
+
+ LETZTE AeNDERUNG:
+    Sat, 29.07.2000, 18:00:00 von Silvana
diff --git a/doc/mcmd/ignoriere b/doc/mcmd/ignoriere
new file mode 100644
index 0000000..43aabf4
--- /dev/null
+++ b/doc/mcmd/ignoriere
@@ -0,0 +1,30 @@
+
+ignoriere
+---------
+
+ MAGIERKOMMANDO:
+    ignoriere <magier>.debug
+
+ ARGUMENTE:
+
+     <magier>
+        Name des Magiers, dessen Bugmeldungen ignoriert werden sollen.
+
+ BESCHREIBUNG:
+    Mit Hilfe dieses Befehls kann man gezielt die Bugs eines bestimmten 
+    Magiers ignorieren.
+    
+    Es werden die Bug-Meldungen auf den Kanaelen 'debug' und auf 
+    'entwicklung' ignoriert. 
+
+    Beim Durchsehen der Kanal-History (-deb*20) werden die Bugs allerdings 
+    nicht ignoriert.
+
+    '-deb backtrace' liefert ein Backtrace zu dem letzten aufgetretenen Bug,
+    voellig unabhaengig, ob der Aufrufende den Bug ignoriert.
+
+ SIEHE AUCH:
+    ignoriere (Spielerkommando)
+
+ LETZTE AENDERUNG:
+    Don, 01.08.2002, 17:15:00 von Vanion
diff --git a/doc/mcmd/in b/doc/mcmd/in
new file mode 100644
index 0000000..eb9609f
--- /dev/null
+++ b/doc/mcmd/in
@@ -0,0 +1,29 @@
+
+in
+--
+
+ MAGIERKOMMANDO:
+    in <raum> <kommando>
+
+ ARGUMENTE:
+
+     <raum>
+        Dateiname eines Raumes
+     <kommando>
+        das auzufuehrende Kommando
+
+ BESCHREIBUNG:
+    Du wirst, unsichtbar fuer die Anwesenden, in den Raum <raum>
+    transportiert, fuehrst das Kommando <kommando> aus und gelangst
+    automatisch wieder in den Raum zurueck, in dem Du vorher warst.
+
+ BEISPIELE:
+    Ein Blick auf die aktuelle Liste der Abenteuer:
+
+    > in /gilden/abenteurer liste
+
+ SIEHE AUCH:
+    at, goto
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/invis b/doc/mcmd/invis
new file mode 100644
index 0000000..face1a0
--- /dev/null
+++ b/doc/mcmd/invis
@@ -0,0 +1,26 @@
+
+invis
+-----
+
+ MAGIERKOMMANDO:
+    invis [e]
+
+ ARGUMENTE:
+    Wird der Paramater ´e` angegeben, erhalten die Mitspieler die
+    Erwartemeldungen wie beim Ausloggen.
+
+ BESCHREIBUNG:
+    Du wirst unsichtbar. Kein Spieler kann Dich jetzt mehr sehen.
+
+    Jeder Magier sollte sich ueberlegen, wann es sinnvoll ist, unsichtbar zu
+    sein, und wann er besser sichtbar ist.
+
+    Es besteht ausserdem die Moeglichkeit, sich direkt unsichtbar einzuloggen
+    (auch wenn man beim letzten Ausloggen sichtbar war). Dazu muss man bei der
+    Passwortabfrage als ersten Buchstaben ein - eingeben.
+
+ SIEHE AUCH:
+    vis
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/load b/doc/mcmd/load
new file mode 100644
index 0000000..4ff4db4
--- /dev/null
+++ b/doc/mcmd/load
@@ -0,0 +1,23 @@
+
+load
+----
+
+ MAGIERKOMMANDO:
+    load <datei> [...]
+
+ ARGUMENTE:
+
+     <datei> [...]  Ein oder mehrere Dateinamen
+
+ BESCHREIBUNG:
+    Das Objekt, welches in <datei> definiert ist, wird geladen. Wenn es bereits
+    geladen ist, passiert nichts.
+
+    Raeume koennen auch geladen werden, indem man sie betritt. Die Blueprint
+    eines Objektes kann auch durch Clonen des Objektes geladen werden.
+
+ SIEHE AUCH:
+    clone, destruct, upd
+
+ LETZTE AENDERUNG:
+    Mit, 02.10.2002, 02:00:00 von Mandragon
\ No newline at end of file
diff --git a/doc/mcmd/localcmd b/doc/mcmd/localcmd
new file mode 100644
index 0000000..1a7cb3d
--- /dev/null
+++ b/doc/mcmd/localcmd
@@ -0,0 +1,27 @@
+
+localcmd
+--------
+
+ MAGIERKOMMANDO:
+    localcmd
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Eigentlich sollte dieses Kommando alle Befehle anzeigen, die Du aufgrund
+    des Raumes, in dem Du gerade stehst, aufgrund der der Dinge in dem Raum
+    und in Deinem Inventar sowie aufgrund Deiner eigenen Stellung ausfuehren
+    kannst.
+
+    Aus technischen Gruenden werden allerdings nur die Befehle angezeigt, die
+    ueber add_action() in die Befehlsliste eingefuegt werden; das MorgenGrauen
+    verfuegt jedoch ueber einen anderen Mechanismus, um in Objekten Kommandos
+    zu definieren.
+
+ SIEHE AUCH:
+    man, hilfe,
+    AddCmd(L), add_action(E)
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/ls b/doc/mcmd/ls
new file mode 100644
index 0000000..3469941
--- /dev/null
+++ b/doc/mcmd/ls
@@ -0,0 +1,90 @@
+
+ls
+--
+
+ MAGIERKOMMANDO:
+    ls [ -alrtsug ] [ <name> ... ]
+
+ ARGUMENTE:
+
+     <name>  Datei- oder Verzeichnisname (es koennen auch mehrere sein)
+
+ BESCHREIBUNG:
+    Die angegeben Dateien und Verzeichnisse werden aufgelistet. Laesst man
+    <name> weg, wird der Inhalt des aktuellen Verzeichnisses aufgelistet.
+
+    Die Flags haben folgende Bedeutung:
+
+     -a  Auch unsichtbare Dateien (deren Namen mit . beginnen) werden
+         angezeigt.
+     -l  Eine ausfuehrliche Liste, in der auch Laenge und Datum der Datei
+         angegeben werden.
+     -u  In Verbindung mit -l wird auch die UID der Dateien ausgegeben.
+     -g  In Verbindung mit -l wird auch die GID der Dateien ausgegeben.
+     -r  Die Sortierreihenfolge wird umgekehrt.
+     -t  Die Liste wird nach dem Erstellungsdatum sortiert.
+     -s  Die Liste wird nach der Groesse der Dateien geordnet.
+
+ EINSTELLUNGEN:
+    Verzeichnisse und geladene Objekte werden, abhaengig von der aktuellen
+    Einstellung des Terminaltyps, besonders angezeigt.
+
+    Diese Einstellungen lassen sich ueber zwei Umgebungsvariablen aendern,
+    wenn man als Terminaltyp `vt100' oder `ansi' eingestellt hat. Bei der
+    Einstellung `dumb' wird bei Verzeichnissen ein / angehaengt und bei
+    geladenen Objekten ein *.
+
+     LS_DIR  Hier kann man die Darstellung von Verzeichnissen konfigurieren.
+             Der Default ist bei `vt100'-Terminals Fettschrift und bei
+             `ansi'-Terminals blaue Fettschrift.
+     LS_OBJ  Diese Variable beinhaltet die Einstellung fuer geladene Objekte.
+             Der Default ist bei `vt100'-Terminals inverse Darstellung und bei
+             `ansi'-Terminals rote Schrift.
+     LS_VC   Diese Variable beinhaltet die Einstellung fuer Objekte, die von
+             einem Virtual Compiler erzeugt wurden. Default ist wie bei LS_OBJ.
+
+    Die Variablen koennen die nachfolgend aufgefuehrten Werte annehmen. Um
+    mehrere Effekte zu kombinieren, muss man sie mit einem + trennen.
+
+    `vt100' und `ansi':
+
+     none    Keine Hervorhebungen
+     bold    Fettschrift
+     blink   blinkende Schrift
+     invers  Schrift- und Hintergrundfarbe werden vertauscht
+
+    Nur `ansi':
+
+     black     Textfarbe schwarz
+     red       Textfarbe rot
+     green     Textfarbe gruen
+     yellow    Textfarbe gelb
+     blue      Textfarbe blau
+     purple    Textfarbe purpur
+     cyan      Textfarbe cyan
+     white     Textfarbe weiss
+     bblack    Hintergrundfarbe schwarz
+     bred      Hintergrundfarbe rot
+     bgreen    Hintergrundfarbe gruen
+     byellow   Hintergrundfarbe gelb
+     bblue     Hintergrundfarbe blau
+     bpurple   Hintergrundfarbe purpur
+     bcyan     Hintergrundfarbe cyan
+     bwhite    Hintergrundfarbe weiss
+
+ BEISPIELE:
+
+    > ls -l
+    > ls -a /doc
+
+    Verzeichnisse in Fettschrift anzeigen, geladene Dateien rot auf gelbem
+    Hintergrund:
+
+    > set LS_DIR bold
+    > set LS_OBJ red+byellow
+
+ SIEHE AUCH:
+    cd, pwd, set, stty
+
+ LETZTE AENDERUNG:
+    Mit, 02.10.2002, 02:00:00 von Mandragon
\ No newline at end of file
diff --git a/doc/mcmd/man b/doc/mcmd/man
new file mode 100644
index 0000000..3130ad5
--- /dev/null
+++ b/doc/mcmd/man
@@ -0,0 +1,61 @@
+
+man
+---
+
+ MAGIERKOMMANDO:
+    man [-i] <name/nummer>
+    man [-i] <name> [nummer]
+    man [-mi] <maske>
+    man [-ri] <maske>
+
+ ARGUMENTE:
+
+     <name> [...]   Name der gewuenschten Hilfeseite
+     <maske>        Wortteil einer Hilfeseite (kann * und ? enthalten)
+     <[nummer]>	    Nummer einer von mehreren Hilfsseiten
+
+ BESCHREIBUNG:
+    Im Hilfeverzeichnis (`/doc') wird nach der entsprechenden Hilfeseite
+    gesucht und diese dann angezeigt.
+
+    Wenn der Parameter -m verwendet wurde, dann werden alle Hilfeseiten
+    aufgelistet, deren Name die Maske enthaelt.
+
+    Der Parameter -r ermoeglicht es, einen Ausdruck im Regexp-Format
+    anzugeben.
+
+    Mit Hilfe der Nummer kann man sich das genaue Ausschreiben einer
+    bestimmten Hilfsseite bei der Anzeige mehrere sparen.
+
+    Die Option -i (interaktiver HTML-Modus) ist zur Zeit nicht aktiviert.
+
+ ACHTUNG:
+
+    Nach dem Einspielen von neuen MAN-Seiten muss der betreffende Magier
+    per 'xcall /p/daemon/mand->update_cache()' den Cache aktualisieren.
+    Dieser wird automatisch nur woechentlich aktualisiert.
+
+ BEISPIELE:
+    Eine einfache Hilfeseite (naemlich diese):
+
+    > man man
+
+    Welche Hilfeseiten haben etwas mit Haenden zu tun?
+
+    > man -m *hands*
+
+    Welche Hilfesseiten sagen uns etwas ueber Waffen?
+
+    > man waffen
+      Es wurden folgende potentiell passenden Seiten gefunden:
+      --------------------------------------------------------
+      1: help/waffenfertigkeiten 2: std/weapon 3: wiz/waffen
+      --------------------------------------------------------
+    > man 3 ODER	> man wiz/waffen ODER	> man waffen 3
+      [Anzeige von /wiz/waffen]
+
+ SIEHE AUCH:
+    hilfe rman
+
+ LETZTE AENDERUNG:
+    20. Maerz 2004 Gloinson
diff --git a/doc/mcmd/mbanish b/doc/mcmd/mbanish
new file mode 100644
index 0000000..c1f5754
--- /dev/null
+++ b/doc/mcmd/mbanish
@@ -0,0 +1,31 @@
+
+mbanish
+-------
+
+ ERZMAGIERKOMMANDO:
+    mbanish <name> [<grund>]
+
+ ARGUMENTE:
+
+     <name>
+        Name des Opfers
+     <grund> (optional)
+        Der Grund der Sperre
+
+ BESCHREIBUNG:
+    Mit diesem Befehl laesst sich verhindern, dass der Spieler <name> von
+    beliebigen Magiern gesponsort (d.h. zum Magier gemacht) werden kann (aus
+    welchem <grund> auch immer).
+
+    Nur ein Erzmagier oder Gott kann in diesem Fall ein Sponsoring vornehmen.
+
+    Mit dem Grund 'loeschen' koennen gesperrte Namen wieder freigegeben werden.
+    Ohne Parameter wird die Liste aller gesperrten Namen ausgegeben.
+
+    Dieser Befehl kann nur von Erzmagiern und Goettern ausgefuehrt werden.
+
+ SIEHE AUCH:
+    banish, tbanish, sbanish
+
+ LETZTE AeNDERUNG:
+    Mon, 20.12.1999, 23:30:00 von Tiamak
diff --git a/doc/mcmd/mecho b/doc/mcmd/mecho
new file mode 100644
index 0000000..569d780
--- /dev/null
+++ b/doc/mcmd/mecho
@@ -0,0 +1,22 @@
+
+mecho
+-----
+
+ MAGIERKOMMANDO:
+    mecho <text>
+
+ ARGUMENTE:
+
+     <text>
+        ein beliebiger Text
+
+ BESCHREIBUNG:
+    Der Text <text> wird an alle Magier im MUD geschickt.
+
+    Der Befehl sollte mit groesster Sorgfalt behandelt werden.
+
+ SIEHE AUCH:
+    vertrag, emote, echo, echoall, echoto, teile (mit)
+
+ LETZTE AeNDERUNG:
+    Tue, 02.09.1997, 16:54:57 von Wargon
diff --git a/doc/mcmd/mkdir b/doc/mcmd/mkdir
new file mode 100644
index 0000000..f0615dc
--- /dev/null
+++ b/doc/mcmd/mkdir
@@ -0,0 +1,43 @@
+
+mkdir
+-----
+
+ MAGIERKOMMANDO:
+    mkdir [-vr] <verz> [...]
+
+ ARGUMENTE:
+
+     <verz> [...]
+        Name des neuen Verzeichnisses (es koennen auch mehrere sein)
+
+ BESCHREIBUNG:
+    Die angegebenen Verzeichnisse werden neu angelegt.
+
+    Die Flags haben folgende Bedeutung:
+
+    -v  Genau ausgeben, was gemacht wird
+    -r  Rekursiv die OBERverzeichnisse von <verz> mitanlegen, wenn sie nicht
+        schon existieren.
+    -p  Das gleiche wie -r (alias eingefuehrt, weil -p einfach viel
+        verbreiteter ist).
+ 
+ BEISPIELE:
+
+    > mkdir new
+
+    Soll jetzt in `new/' noch das Verzeichnis `foo/bar/' angelegt werden, so
+    darf man nicht folgenden Aufruf verwenden
+
+    > mkdir new/foo/bar      ; So nicht!
+
+    sondern koennte z.B folgendermassen vorgehen:
+
+    > mkdir new/foo new/foo/bar           oder
+    > mkdir -r /new/foo/bar
+
+ SIEHE AUCH:
+    rmdir
+
+ LETZTE AENDERUNG:
+    26.01.2013, Zesstra
+
diff --git a/doc/mcmd/more b/doc/mcmd/more
new file mode 100644
index 0000000..1ec675b
--- /dev/null
+++ b/doc/mcmd/more
@@ -0,0 +1,42 @@
+
+more
+----
+
+ MAGIERKOMMANDO:
+    more <datei> [...]
+
+ ARGUMENTE:
+
+     <datei> [...]
+        Dateiname(n)
+
+ BESCHREIBUNG:
+    Die Datei(en) <datei> wird/werden stueckchenweise angezeigt. Nach jeder
+    ausgegebenen Seite wird gestoppt und es koennen verschiedene Aktionen
+    durchgefuehrt werden:
+
+     <RETURN>, f, F
+               Eine Seite vorwaerts
+     d, D      Eine halbe Seite vorwaerts
+     u, U      Eine halbe Seite zurueck
+     b, B      Eine Seite zurueck
+     <zeile>   Springe zu Zeile <zeile>
+     ?         Hilfetext ausgeben
+     q, x      Anzeige der aktuellen Datei abbrechen
+     /<regexp>
+               regulaeren Ausdruck <regexp> suchen
+
+    Die Dateinamen koennen auch Muster wie * oder ? enthalten.
+
+    Die Laenge einer Seite kann man mit dem Befehl `zeilen' einstellen.
+
+ BEISPIELE:
+
+    > more /std/npc.c
+    > more *.h
+
+ SIEHE AUCH:
+    cat, head, tail, zeilen
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/mschau b/doc/mcmd/mschau
new file mode 100644
index 0000000..5dd26a1
--- /dev/null
+++ b/doc/mcmd/mschau
@@ -0,0 +1,40 @@
+
+mschau
+------
+
+ MAGIERKOMMANDO:
+    mschau <schalter>
+
+ ARGUMENTE:
+
+     <schalter>
+        'an', 'ein' oder 'ja' schaltet den Magiermodus ein
+        'aus' oder 'nein' schaltet den Magiermodus aus
+        '+debug' schaltet Debugmeldungen ein
+        '-debug' schaltet Debugmeldungen aus
+
+ BESCHREIBUNG:
+    Manchmal moechte man etwas ausprobieren, was nur Spielern moeglich ist,
+    zum Beispiel beim Testen von Abenteuern o.ae. Dazu dient dieser Befehl,
+    mit dem man die meisten Faehigkeiten eines Magiers deaktivieren kann.
+
+    Nach einem `mschau aus' sieht man keine Dateinamen mehr von Raeumen und
+    Objekten. Die meisten Magierbefehle sind abgeschaltet und man kann auch
+    wieder sterben wie ein normaler Spieler.
+
+    Mit `mschau an' kehrt man wieder in den Magiermodus zurueck.
+
+    Zusaetzlich kann dieses Kommando noch umschalten, ob Magier Debugmeldungen
+    (das sind Meldungen, die mit dem Typ MT_DEBUG an ReceiveMsg() uebergeben
+     werden) wahrnehmen moechten oder nicht.
+
+ BEMERKUNGEN:
+    Auch mit ausgeschaltetem Magiermodus unterliegt man noch den Bedingungen
+    des Magiervertrags!
+
+ SIEHE AUCH:
+    vertrag, schau (an), i (=inventur)
+
+ LETZTE AeNDERUNG:
+    1.5.2014, Zesstra
+
diff --git a/doc/mcmd/mv b/doc/mcmd/mv
new file mode 100644
index 0000000..32c5368
--- /dev/null
+++ b/doc/mcmd/mv
@@ -0,0 +1,51 @@
+
+mv
+--
+
+ MAGIERKOMMANDO:
+    mv [ -irfv ]  <alt> <neu>
+    mv [ -irfv ]  <von_1> [ ... <von_n> ] <nach>
+    mv [ -irfvm ] <von_1> [ ... <von_n> ] <nach> <maske>
+
+ ARGUMENTE:
+
+     <alt>    Alter Datei- oder Verzeichnisname
+     <neu>    Neuer Datei- oder Verzeichnisname
+     <von_1>
+     <von_n>  Name der Quelldateien und -verzeichnisse
+     <nach>   Das Zielverzeichnis
+     <maske>  Dateimaske fuer Datei- oder Verzeichnisnamen
+
+ BESCHREIBUNG:
+    Mit dem ersten Aufruf wird die Datei bzw. das Verzeichnis <alt> in <neu>
+    umbenannt.
+
+    Mit dem zweiten Aufruf werden die Quelldateien <von_1> bis <von_n> in das
+    Zielverzeichnis <verz> verschoben.
+
+    In der zweiten Variante koennen die Dateinamen auch Muster wie * oder ?
+    enthalten.
+
+    Die Optionen haben folgende Bedeutung:
+
+     -i  Vor dem Ueberschreiben einer existierenden Datei wird eine
+         Sicherheitsabfrage durchgefuehrt.
+     -r  Es werden rekursiv Unterverzeichnisse verschoben, falls das
+         Zielverzeichnis schon existiert
+     -f  Evtl. auftretende Probleme, die sonst einen Abbruch bewirken wuerden,
+         werden ignoriert. Auch `-i' wird ausser Kraft gesetzt.
+     -v  Es wird detailliert ausgegeben, was gemacht wird
+     -m  Es werden nur Dateien verschoben, die dem Muster <maske> entsprechen
+
+ BEISPIELE:
+
+    > mv datei1 datei2
+    > mv * temp
+    > mv /players/hate/*.c ../test
+    > mv -ifrm ~mandragon/meloran /d/inseln/mandragon/meloran *.c
+
+ SIEHE AUCH:
+    cp, rm
+
+ LETZTE AENDERUNG:
+    Don, 03.10.2002, 18:00:00 von Mandragon
diff --git a/doc/mcmd/oropax b/doc/mcmd/oropax
new file mode 100644
index 0000000..66bc1b2
--- /dev/null
+++ b/doc/mcmd/oropax
@@ -0,0 +1,30 @@
+
+oropax
+------
+
+ MAGIERKOMMANDO:
+    oropax [<stufe>]
+
+ ARGUMENTE:
+
+     <stufe> (optional)
+        Magierlevel, ab dem Rufe durchgelassen werden
+
+ BESCHREIBUNG:
+    Du steckst Dir Oropax in die Ohren. Nun kannst Du nichts mehr hoeren, was
+    gerufen, gesagt oder Dir mitgeteilt wird. Das gilt allerdings nur fuer
+    Leute mit einem Magierlevel, der kleiner ist als <stufe>.
+
+    Die hoechstmoegliche Stufe ist Dein eigener Magierlevel.
+
+    Rufst Du den Befehl ohne Argument auf, wird das Oropax wieder entfernt.
+
+    Auf die Kommunikation ueber die Ebenen wirkt das Oropax nicht, da diese
+    Art der Kommunikation auf geistiger Ebene verlaeuft und nicht ueber den
+    Gehoergang.
+
+ SIEHE AUCH:
+    ignoriere
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/people b/doc/mcmd/people
new file mode 100644
index 0000000..b5adeed
--- /dev/null
+++ b/doc/mcmd/people
@@ -0,0 +1,30 @@
+
+people
+------
+
+ MAGIERKOMMANDO:
+    people
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Dieses Kommando listet Informationen ueber alle eingeloggten Spieler und
+    Magier auf:
+
+     * von welcher Internetnummer sie kommen
+     * den aktuellen Namen.
+     * den Magierlevel
+     * das Alter
+     * Idle flag `I', wenn der Spieler laenger als 5 Minuten nichts mehr
+       gemacht hat
+     * wo er ist
+
+    Ausserdem wird die Anzahl der aktiven Spieler, die Anzahl der Kommandos
+    pro Sekunde und die Anzahl der kompilierten Zeilen pro Sekunde angezeigt.
+
+ SIEHE AUCH:
+    wer, pwho
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/ping b/doc/mcmd/ping
new file mode 100644
index 0000000..9daf7b3
--- /dev/null
+++ b/doc/mcmd/ping
@@ -0,0 +1,19 @@
+
+ping
+----
+
+ MAGIERKOMMANDO:
+    ping <spieler>
+
+ ARGUMENTE:
+
+     <spieler>
+        ein (eingeloggter) Spieler
+
+ BESCHREIBUNG:
+    Der Spieler <spieler> wird angepiept. Dabei wird bei dem Spieler eine
+    Meldung und, falls sein Rechner dies unterstuetzt, ein Signalton
+    ausgegeben.
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/prompt b/doc/mcmd/prompt
new file mode 100644
index 0000000..eb9b0de
--- /dev/null
+++ b/doc/mcmd/prompt
@@ -0,0 +1,43 @@
+
+prompt
+------
+
+ MAGIERKOMMANDO:
+    prompt [<text>]
+
+ ARGUMENTE:
+
+     <text> (optional)
+        ein beliebiger Text
+
+ BESCHREIBUNG:
+    Die Eingabeaufforderung ("prompt") wird auf die angegebene Meldung <text>
+    gesetzt.
+
+    Ohne Argumente wird das Defaultprompt (`> ') eingestellt.
+
+    Um Leerzeichen an Anfang und Ende des Prompts zu ermoeglichen, kann <text>
+    in Anfuehrungszeichen gesetzt werden. Ausserdem gibt es noch folgende
+    besondere Zeichenfolgen:
+
+     \h  wird durch den Namen des Muds ersetzt
+     \u  wird durch Deinen Namen ersetzt
+     \w  wird durch das aktuelle Verzeichnis ersetzt
+     \t  wird durch die aktuelle Zeit ersetzt
+     \n  an dieser Stelle wird ein Zeilenumbruch eingefuegt
+
+ BEISPIELE:
+
+    > prompt "\h:\u:\w> "
+
+    ergibt bei Wargon, wenn er sich in seinem Homeverzeichns befindet:
+
+    MorgenGrauen:Wargon:/players/wargon> _
+
+    (der Unterstrich soll den Cursor anzeigen)
+
+ SIEHE AUCH:
+    pwd
+
+ LETZTE AeNDERUNG:
+    Mon, 08.02.1999, 22:15:00 von Tiamak
diff --git a/doc/mcmd/protect b/doc/mcmd/protect
new file mode 100644
index 0000000..592876a
--- /dev/null
+++ b/doc/mcmd/protect
@@ -0,0 +1,32 @@
+
+protect
+-------
+
+ MAGIERKOMMANDO:
+    protect <prop>
+
+ ARGUMENTE:
+
+     <prop>
+        Name der zu schuetzenden Property
+
+ BESCHREIBUNG:
+    Das Zustand des PROTECTED-Flags der Property <prop> wird umgeschaltet.
+
+    Man muss dabei allerdings beachten, dass man nicht den Namen der Property
+    aus `/sys/properties.h' verwenden darf, sondern den String nehmen muss,
+    der sich hinter dem P_XYZ verbirgt.
+
+    Magier-Lehrlingen steht dieser Befehl noch nicht zur Verfuegung.
+
+ BEISPIELE:
+    Wenn Du verhindern willst, dass jemand anderes Deinen Titel (P_TITLE =
+    "title") aendern kann:
+
+    > protect title
+
+ SIEHE AUCH:
+    `/std/thing/properties.c'
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/pwd b/doc/mcmd/pwd
new file mode 100644
index 0000000..b8d5f42
--- /dev/null
+++ b/doc/mcmd/pwd
@@ -0,0 +1,18 @@
+
+pwd
+---
+
+ MAGIERKOMMANDO:
+    pwd
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Der aktuelle Pfad wird angezeigt.
+
+ SIEHE AUCH:
+    cd, prompt, ls
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/pwho b/doc/mcmd/pwho
new file mode 100644
index 0000000..79cd382
--- /dev/null
+++ b/doc/mcmd/pwho
@@ -0,0 +1,35 @@
+
+pwho
+----
+
+ MAGIERKOMMANDO:
+    pwho
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Dieses Kommando gibt die wichtigsten Charakterwerte aller eingeloggten
+    Spieler und Seher aus.
+
+    Als Infomation erhaelt man
+
+     * den Level
+     * den Namen
+     * die Erfahrungspunkte
+     * die Abenteuerpunkte
+     * Intelligenz
+     * Kraft
+     * Geschicklichkeit
+     * Ausdauer
+     * Angriffsstaerke
+     * Abwehrstaerke
+     * Angriffsstaerke mit blossen Haenden
+     * momentane Lebenspunkte
+     * maximale Lebenspunkte
+
+ SIEHE AUCH:
+    wer, people
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/removeguildmaster b/doc/mcmd/removeguildmaster
new file mode 100644
index 0000000..74ce300
--- /dev/null
+++ b/doc/mcmd/removeguildmaster
@@ -0,0 +1,24 @@
+
+removeguildmaster
+-----------------
+
+ ERZMAGIERKOMMANDO:
+    removeguildmaster <name> <gilde>
+
+ ARGUMENTE:
+
+     <name>
+        Name des Magiers
+     <gilde>
+        Name der Gilde
+
+ BESCHREIBUNG:
+    Der Magier <name> ist fortan nicht mehr Gildenmagier der Gilde <gilde>.
+
+    Dieser Befehl steht nur Goettern zur Verfuegung.
+
+ SIEHE AUCH:
+    addguildmaster, gilden
+
+ LETZTE AeNDERUNG:
+    2003-12-10, Zook.
diff --git a/doc/mcmd/removemaster b/doc/mcmd/removemaster
new file mode 100644
index 0000000..b7f5d23
--- /dev/null
+++ b/doc/mcmd/removemaster
@@ -0,0 +1,25 @@
+
+removemaster
+------------
+
+ ERZMAGIERKOMMANDO:
+    removemaster <name> <region>
+
+ ARGUMENTE:
+
+     <name>
+        Name des Magiers
+     <region>
+        Eine Region
+
+ BESCHREIBUNG:
+    Der Magier <name> wird von der Liste der Regionsmagier der Region <region>
+    gestrichen.
+
+    Dieser Befehl steht nur Erzmagiern und Goettern zur Verfuegung.
+
+ SIEHE AUCH:
+    addmaster, regionen
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/rm b/doc/mcmd/rm
new file mode 100644
index 0000000..8378a02
--- /dev/null
+++ b/doc/mcmd/rm
@@ -0,0 +1,35 @@
+
+rm
+--
+
+ MAGIERKOMMANDO:
+    rm [ -irv ] <dateiname> [...]
+    rm [ -irmv ] <dateiname> [...] [<maske>]
+
+ ARGUMENTE:
+
+     <dateiname> Name einer Datei oder eines Verzeichnisses
+                 (es koennen auch mehrere sein)
+
+ BESCHREIBUNG:
+    Die angegebenen Dateien oder Verzeichnisse werden geloescht.
+    Die Dateinamen koennen auch Muster wie * oder ? enthalten.
+
+    Die Optionen haben folgende Bedeutung:
+
+     -r  Unterverzeichnisse werden rekursiv geloescht.
+     -i  Bei jeder Datei wird nachgefragt, ob sie geloescht werden soll.
+     -m  Es sind nur Dateien betroffen, die der Maske <maske> entsprechen
+     -v  Es wird jeder Arbeitsschritt ausgegeben
+
+ BEISPIELE:
+
+    > rm test1.c
+    > rm *.bak
+    > rm -rm / *~     
+
+ SIEHE AUCH:
+    cp, mv
+
+ LETZTE AeNDERUNG:
+    Thu, 17.10.2002, 16:30:00 von Mandragon
diff --git a/doc/mcmd/rman b/doc/mcmd/rman
new file mode 100644
index 0000000..95f4d5a
--- /dev/null
+++ b/doc/mcmd/rman
@@ -0,0 +1,28 @@
+
+rman
+---
+
+ MAGIERKOMMANDO:
+    rman <hilfsseite> <mudname>
+
+ ARGUMENTE:
+
+     <hilfsseite>   Name der gewuenschten Hilfeseite
+     <mudname>      Name eines bekannten Muds
+
+ BESCHREIBUNG:
+    An das Mud <mudname> wird eine Anfrage geschickt, ob es eine Hilfe-
+    Seite zum Thema <hilfsseite> bereitstellen kann. Die Antwort ist
+    vom Mud abhaengig.
+
+ BEISPIELE:
+   
+    Im Pk-Mud die Hilfeseite zu PKs anfordern:
+
+    > rman pk pk-mud
+
+ SIEHE AUCH:
+    hilfe man udpq
+
+ LETZTE AENDERUNG:
+    Die, 06.06.2003, 18:00:00 von Mandragon
diff --git a/doc/mcmd/rmdir b/doc/mcmd/rmdir
new file mode 100644
index 0000000..02b4182
--- /dev/null
+++ b/doc/mcmd/rmdir
@@ -0,0 +1,24 @@
+
+rmdir
+-----
+
+ MAGIERKOMMANDO:
+    rmdir [-v] <verz> [...]
+
+ ARGUMENTE:
+
+     <verz>  Das zu loeschende Verzeichnis (es koennen auch mehrere sein)
+
+ BESCHREIBUNG:
+    Die angegebenen Verzeichnisse werden geloescht. Die Verzeichnisse muessen
+    dazu leer sein.
+
+    Das Flag -v sorgt dafuer, das detaillierte Informationen ueber den Fort-
+    schritt ausgegeben werden.
+
+
+ SIEHE AUCH:
+    mkdir, rm
+
+ LETZTE AENDERUNG:
+    Mit, 02.10.2002, 02:00:00 von Mandragon
diff --git a/doc/mcmd/sallow b/doc/mcmd/sallow
new file mode 100644
index 0000000..18750d2
--- /dev/null
+++ b/doc/mcmd/sallow
@@ -0,0 +1,23 @@
+
+sallow
+------
+
+ MAGIERKOMMANDO:
+    sallow <name>
+
+ ARGUMENTE:
+
+     <name>
+        Name eines Magiers
+
+ BESCHREIBUNG:
+    Du erlaubst dem Magier <name> Dich zu snoopen.
+
+    Dies ist nur noetig, wenn ein Magier von gleichem oder geringerem Level
+    Dich snoopen soll.
+
+ SIEHE AUCH:
+    snoop
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/sbanish b/doc/mcmd/sbanish
new file mode 100644
index 0000000..a7c847f
--- /dev/null
+++ b/doc/mcmd/sbanish
@@ -0,0 +1,55 @@
+
+sbanish
+-------
+
+ MAGIERKOMMANDO:
+    sbanish <adresse> <tage>
+
+ ARGUMENTE:
+
+     <adresse>
+        IP-Adresse des Opfers
+     <tage>
+        Die Dauer der Sperrung
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kann man verhindern, dass sich von bestimmten Adressen
+    aus Gaeste einloggen oder neue Spieler angelegt werden..
+    
+    Die Dauer wird in Tagen angegeben. Eine Angabe von -1 setzt eine 
+    unbefristete Sperre; mit einer Dauer von 0 Tagen loescht man eine gesetzte 
+    Sperre.
+
+    Die Angabe von 'sbanish' ohne Parameter gibt eine Liste aller gesperrten
+    Adressen aus.
+
+    Die zu sperrende IP-Adresse kann in den folgenden Formen angegeben werden:
+      - 'n.n.n.n': einzelne Adresse
+      - 'n.n.n.n/x': Subnetzwerk in CIDR-Notation mit Subnetzmaske x.
+                     2^(32-x) Adressen
+      - 'n.n.n.n/m.m.m.m': Subnetzwerk in CIDR-Notation mit Subnetzmaske 
+                           m.m.m.m
+      - 'n.n.n': Class-C-Netz n.n.n (255 Adressen)
+      - 'n.n': Class-B-Netz n.n (65536 Adressen)
+
+    Dieser Befehl steht eingeschraenkt ab Level 26 zur Verfuegung:
+
+      - ab Level 26 kann man bis zu zehn einzelne IPs fuer einen Tag sperren
+        und eigene Sperrungen wieder aufheben
+      - ab Level 40 kann man bis zu zehn IPs und/oder Subnetze mit bis zu 255
+        Adressen fuer einen Tag sperren und eigene Sperrungen wieder aufheben
+      - Hilfssheriffs und Erzmagier koennen beliebig viele IPs bzw. Subnetze
+        beliebiger Groesse fuer beliebige Zeitraeume sperren sowie fremde
+        Sperrungen wieder aufheben
+
+ BEMERKUNGEN:
+    Ein sbanish fuehrt ausdruecklich _nicht_ dazu, dass sich von den
+    entsprechenden Adressen keine bestehenden Spieler mehr einloggen
+    koennen.
+
+ SIEHE AUCH:
+    banish, mbanish, tbanish
+
+ LETZTE AeNDERUNG:
+ 20.01.2013, Zesstra
+
diff --git a/doc/mcmd/set b/doc/mcmd/set
new file mode 100644
index 0000000..6d73fda
--- /dev/null
+++ b/doc/mcmd/set
@@ -0,0 +1,49 @@
+
+set
+---
+
+ MAGIERKOMMANDO:
+    set [<var> [<wert>]]
+
+ ARGUMENTE:
+
+     <var>
+        eine beliebige Zeichenkette
+     <wert>
+        der dazugehoerige Wert
+
+ BESCHREIBUNG:
+    Die Variable <var> wird auf den Wert <wert> gestezt.
+
+    Laesst man <wert> weg, wird die Variable <var> geloescht.
+
+    Ganz ohne Argumente werden die momentan definierten Variablen ausgegeben.
+
+    Die Variablen wirken sich bei den meisten Dateikommandos aus. Man kann sie
+    verwenden, um sich Abkuerzungen zu oft benutzten Dateien oder
+    Verzeichnissen zu legen.
+
+ BESONDERE VARIABLEN
+    Einige Umgebungsvariablen haben eine feste Bedeutung:
+
+     LS_OBJ   Diese Variablen sind im Zusammenhang mit dem ls-Befehl von
+     LS_DIR   Bedeutung und steuern die farbliche Darstellung der Ausgabe 
+     LS_VC    
+
+     CD_SHORT Wenn auf 1 gesetzt, wird bei 'cd' eine .readme-Datei nicht mit
+              ausgegeben.
+
+ BEISPIELE:
+    Eine Abkuerzung fuer einen Pfad:
+
+    > set ls /d/inseln/wargon/luftschloss
+
+    Nun kann man mit einem einfachen `cd $ls' in das Verzeichnis gelangen.
+    Ein cd $ls/mon fuehrt nach /d/inseln/wargon/luftschloss/mon.
+
+ SIEHE AUCH:
+    cd, ls, rm, mv, cat, head, tail, more
+
+ LETZTE AENDERUNG:
+    Mit, 02.10.2002, 02:00:00 von Mandragon
+ 
\ No newline at end of file
diff --git a/doc/mcmd/setcmsg b/doc/mcmd/setcmsg
new file mode 100644
index 0000000..9944937
--- /dev/null
+++ b/doc/mcmd/setcmsg
@@ -0,0 +1,33 @@
+
+setcmsg
+-------
+
+ MAGIERKOMMANDO:
+    setcmsg [<text>]
+
+ ARGUMENTE:
+
+     <text> (optional)
+        Die neue Meldung
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kannst Du die Meldung festlegen, die andere Leute im
+    gleichen Raum sehen, wenn Du einen Gegenstand clonst. Vor den Text wird
+    noch Dein Name gestellt.
+
+    Ohne Argument wird die Defaultmeldung eingestellt.
+
+ BEISPIELE:
+    Angenommen, Wargon stellt folgende Clone-Meldung ein:
+
+    > setcmsg holt etwas aus den tiefen Taschen seines Mantels
+
+    Clont Wargon nun irgend etwas, sehen die Spieler im gleichen Raum:
+
+    Wargon holt etwas aus den tiefen Taschen seines Mantels.
+
+ SIEHE AUCH:
+    setdmsg, review, clone
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/setdmsg b/doc/mcmd/setdmsg
new file mode 100644
index 0000000..611c7e5
--- /dev/null
+++ b/doc/mcmd/setdmsg
@@ -0,0 +1,33 @@
+
+setdmsg
+-------
+
+ MAGIERKOMMANDO:
+    setdmsg [<text>]
+
+ ARGUMENTE:
+
+     <text> (optional)
+        Die neue Meldung
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kannst Du die Meldung festlegen, die andere Leute im
+    gleichen Raum sehen, wenn Du einen Gegenstand zerstoerst. Vor den Text
+    wird noch der Name des Gegenstandes gestellt.
+
+    Ohne Argument wird die Defaultmeldung eingestellt.
+
+ BEISPIELE:
+    Angenommen, Wargon stellt folgende Destruct-Meldung ein:
+
+    > setdmsg wird von Wargon annihiliert
+
+    Zerstoert Wargon nun z.B. eine Fackel, sehen die Spieler im gleichen Raum:
+
+    Eine Fackel wird von Wargon annihiliert.
+
+ SIEHE AUCH:
+    setcmsg, review, destruct
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/shell b/doc/mcmd/shell
new file mode 100644
index 0000000..46cd3ca
--- /dev/null
+++ b/doc/mcmd/shell
@@ -0,0 +1,47 @@
+
+   ----------------------MAGIER_HILFS_SEITE------------------------
+
+Als Magier hat man neben den Spieler- und Seherbefehlen einige weitere
+Befehle zur Verfuegung. Eine Erlaeuterung zu den folgenden Themen und
+Kommandos kann man mit "hilfe <kommando>" erhalten. Wobei hier nicht
+zwischen den Stufen der Magier unterschieden wird. Dies wird aber jeweils
+bei der Erlaeuterung erwaehnt.
+
+ALLGEMEINE THEMEN:
+    balance, forscherpunkte, testspieler, zweitspieler
+
+KOMMUNIKATION:
+    echoall, echoto, echo, mecho, mrufe, oropax, remote
+
+BEWEGUNG:
+    goto, trans, home, verfolge, setmin, setmout, setmmin, setmmout,
+    at, in, +
+
+KAMPF:
+    zap, frieden, sethands
+
+DATEISYSTEM:
+    cd, pwd, ls, cat, head, tail, more, grep, cp, mv, rm, mkdir,
+    rmdir, set, prompt, ed
+
+OBJEKTE:
+    clone, destruct, upd(ate), load, setcmsg, setdmsg
+
+INFO:
+    mschau, people, pwho, localcmd, man, snoop, sallow, spieler, rman
+
+ANDERES:
+    heile, zwinge, title, extralook, traenke, invis, vis, protect, do,
+    showpresay, ping, todo
+
+REGIONSMAGIERBEFEHLE UND -THEMEN:
+    regionsmagier banish, exec
+
+ERZMAGIERBEFEHLE:
+    addmaster, removemaster, mbanish, shutdown
+
+In den Verzeichnissen unter /doc/* findest Du viele weitere Dokumentationen.
+Unter anderem findest Du einen Programmierkurs in LPC/MorgenGrauen unter
+  /doc/KURS/*
+Ausserdem sollten unbedingt die Dokumentationen unter /doc/MG/* beachtet werden.
+
diff --git a/doc/mcmd/showpresay b/doc/mcmd/showpresay
new file mode 100644
index 0000000..1b3e786
--- /dev/null
+++ b/doc/mcmd/showpresay
@@ -0,0 +1,23 @@
+
+showpresay
+----------
+
+ MAGIERKOMMANDO:
+    showpresay [ein|an|aus]
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Normalerweise wird das Presay bei den Kommunikationsbefehlen nicht mit
+    ausgegeben. Moechte man das dennoch, so kann die Ausgabe mit diesem Befehl
+    aktiviert und deaktiviert werden.
+
+    Man sollte das Presay nur mit ausgeben lassen, wenn es kurz ist (nicht
+    laenger als 10 Zeichen).
+
+ SIEHE AUCH:
+    presay
+
+ LETZTE AeNDERUNG:
+    Tue, 17.12.2002, 12:00:00 von Mandragon
diff --git a/doc/mcmd/shutdown b/doc/mcmd/shutdown
new file mode 100644
index 0000000..8fce2bb
--- /dev/null
+++ b/doc/mcmd/shutdown
@@ -0,0 +1,26 @@
+
+shutdown
+--------
+
+ ERZMAGIERKOMMANDO:
+    shutdown <grund>
+
+ ARGUMENTE:
+
+     <grund>
+        Der Grund fuer den Neustart
+
+ BESCHREIBUNG:
+    Mit diesem Befehl wird das Spiel SOFORT heruntergefahren. Man sollte
+    diesen Befehl nur benutzen, wenn es wirklich dringend notwendig ist; fuer
+    einen Neustart mit Verzoegerung steht Armageddon immer gern zur
+    Verfuegung. Er sollte eigentlich immer gewaehlt werden, da die Spieler
+    dann auch Zeit haben, sich auf den Neustart einzustellen.
+
+    Dieser Befehl steht nur Erzmagiern und Goettern zur Verfuegung.
+
+ SIEHE AUCH:
+    `/obj/shut' (Armageddon)
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/snoop b/doc/mcmd/snoop
new file mode 100644
index 0000000..f8d8ddc
--- /dev/null
+++ b/doc/mcmd/snoop
@@ -0,0 +1,45 @@
+
+snoop
+-----
+
+ MAGIERKOMMANDO:
+    snoop <name> [<flags>]
+    snoop
+
+ ARGUMENTE:
+
+     <name>
+        Name eines Spielers
+     <flags> (optional)
+        'l' und/oder 'f'
+
+ BESCHREIBUNG:
+    Du siehst und hoerst alles aus der Sicht des Spielers <name>. Das kann
+    auch ein Magier sein, wenn er einen kleineren Level hat als Du.
+
+    Die Flags haben folgende Bedeutung:
+
+     f (force)
+        Falls der Spieler schon von einem Magier mit niedrigerem Level
+        gesnooped wird, wird diesem der Snoop weggenommen (ein Spieler kann
+        immer nur von *einem* Magier gleichzeitig gesnooped werden).
+
+        Ohne das Flag wird nur eine Mitteilung ausgegeben, dass der Spieler
+        schon gesnooped wird.
+
+     l (locked)
+        Gibt man dieses Flag an, so koennen Magier mit kleinerem Level den
+        Spieler nicht snoopen, waehrend man selbst es gerade tut.
+
+        Laesst man das Flag weg, und ein Magier mit kleinerem Level will den
+        gleichen Spieler snoopen, so wird er gewissermassen dazwischen
+        geklinkt: Der Spieler wird nun von diesem Magier gesnooped, und der
+        Magier wiederum von Dir.
+
+    Mit der zweiten Form (ohne Argumente) wird das Snoopen wieder beendet.
+
+ SIEHE AUCH:
+    sallow, vertrag, snoop()
+
+ LETZTE AeNDERUNG:
+    Tue, 02.09.1997, 16:55:04 von Wargon
diff --git a/doc/mcmd/spieler b/doc/mcmd/spieler
new file mode 100644
index 0000000..15a5c22
--- /dev/null
+++ b/doc/mcmd/spieler
@@ -0,0 +1,26 @@
+
+spieler
+-------
+
+ MAGIERKOMMANDO:
+    spieler aus ip <ip-adresse>
+    spieler aus ip von <spieler-name>
+
+ ARGUMENTE:
+     <ip-adresse>   IP-Adresse im Format x.x.x.x
+     <spieler-name> Name eines eingeloggten Spielers
+
+ BESCHREIBUNG:
+    Dieser Befehl zeigt alle Spieler an, die ueber die IP-Adresse
+    <ip-adresse> oder dieselbe IP-Adresse eingeloggt sind wie der
+    Spieler <spieler-name>.
+ 
+    Es werden zudem Informationen wie Einlogdatum, Idlezeit und
+    Zweitieeigenschaft angezeigt.	
+
+ SIEHE AUCH:
+    people, pwho, query_ip_number(E)
+
+
+ LETZTE AENDERUNG:
+    Fre, 04.10.2002, 13:00:00 von Mandragon
diff --git a/doc/mcmd/suender b/doc/mcmd/suender
new file mode 100644
index 0000000..f838b62
--- /dev/null
+++ b/doc/mcmd/suender
@@ -0,0 +1,57 @@
+
+suender
+-------
+
+ ERZMAGIER- UND DEPUTY-KOMMANDO:
+
+    suender ?                    Gibt eine Liste aller eingetragenen 
+                                 "Suender" aus.
+
+    suender <name>               Listet die "Suenden" des Spielers/Magiers
+                                 <name> auf.
+
+    suender +<name> <text>       Fuegt einen "Suendeneintrag" fuer den
+    suender +<name> -f <text>    Spieler/Magier <name> hinzu. Durch Angabe
+                                 der Option -f koennen Eintraege fuer
+                                 bereits geloeschte Spieler/Magier
+                                 nachgetragen werden.
+
+    suender -<name> <nr>         Loescht den "Suendeneintrag" <nr> des
+                                 Spielers/Magiers <name>. Dieses Kommando
+                                 ist Erzmagiern vorbehalten.
+
+    suender !                    Erzeugt ein Dump-File mit allen Eintraegen
+
+    suender *                    Zeigt ALLE Eintraege an (per More() ;)
+
+ ARGUMENTE:
+
+     <name>
+        Name des Spielers/Magiers
+
+     <text>
+        Infotext. Sollte enthalten: 
+          a) ob es eine Verwarnung, eine Bestrafung oder Loeschung ist
+          b) den Grund.
+          c) Bei Nachtraeglichen Eintraegen einen entspr. Hinweis.
+        Der Text wird automatisch um das Datum und den Namen des
+        Eintragenden ergaenzt. 
+
+     <nr>
+        Die Nummer des Eintrags. Nach jeder Loeschung werden die Eintraege
+        neu durchnummeriert!
+
+ BESCHREIBUNG:
+
+    Verwaltung der vom Sheriff und seinem Deputy durchgefuehrten Massnahmen,
+    damit andere Erzmagier/Goetter schneller nachschlagen koennen, ob sie
+    z.B. eine Strafe aufheben duerfen.
+
+    Dieser Befehl steht nur Erzmagiern und Deputies zur Verfuegung.
+
+ SIEHE AUCH:
+
+    sheriff
+
+ LETZTE AeNDERUNG:
+    Die, 15.08.2000, 11:00:00 von Paracelsus
diff --git a/doc/mcmd/tail b/doc/mcmd/tail
new file mode 100644
index 0000000..e4adb59
--- /dev/null
+++ b/doc/mcmd/tail
@@ -0,0 +1,21 @@
+
+tail
+----
+
+ MAGIERKOMMANDO:
+    tail <datei>
+
+ ARGUMENTE:
+
+     <datei>
+        Die auszugebende Datei
+
+ BESCHREIBUNG:
+    Die letzten Zeilen der Datei <datei> werden ausgegeben. Dabei wird ein
+    Maximum von 1000 Byte nicht ueberschritten.
+
+ SIEHE AUCH:
+    cat, head, more
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/tbanish b/doc/mcmd/tbanish
new file mode 100644
index 0000000..227a228
--- /dev/null
+++ b/doc/mcmd/tbanish
@@ -0,0 +1,26 @@
+
+tbanish
+-------
+
+ ERZMAGIERKOMMANDO:
+    tbanish <name> <tage>
+
+ ARGUMENTE:
+
+     <name>
+        Name des Opfers
+     <tage>
+        Die Dauer der Spielpause
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kann man Spielern eine Spielpause auferlegen. Die Dauer
+    wird in Tagen angegeben. Eine Angabe von -1 setzt eine unbefristete
+    Spielpause; mit einer Dauer von 0 Tagen loescht man eine gesetzte Sperre.
+
+    Dieser Befehl kann nur von Erzmagiern und Goettern ausgefuehrt werden.
+
+ SIEHE AUCH:
+    banish, mbanish, sbanish
+
+ LETZTE AeNDERUNG:
+    Mon, 20.12.1999, 23:30:00 von Tiamak
diff --git a/doc/mcmd/todo b/doc/mcmd/todo
new file mode 100644
index 0000000..6373b14
--- /dev/null
+++ b/doc/mcmd/todo
@@ -0,0 +1,35 @@
+
+todo
+----
+
+ MAGIERKOMMANDO:
+    todo neu
+    todo anzeigen [<nummer1>] [bis <nummer2>]
+    todo loeschen <nummer1>
+    todo verschieben [<nummer1>] [nach <ziel>]
+
+ ARGUMENTE:
+
+     <nummer1>
+     <nummer2> Nummer eines Eintrags in der Todo-Liste
+     <ziel>    Nummer eines Eintrags in der Todo-Liste oder 'oben' oder 'unten'
+
+ BESCHREIBUNG:
+    Mit dem Befehl 'todo' kann ein Magier seine Todo-Liste verwalten. Vier
+    grundsaetzliche Optionen stehen hier zur Verfuegung:
+
+    'todo neu' legt einen neuen Eintrag an.
+    'todo loeschen' loescht den Eintrag <nummer1>
+    'todo anzeigen' zeigt entweder alle Eintraege an, einen einzelnen Eintrag
+                    oder einen Ausschnitt aus der Todo-Liste. Falls <nummer2>
+                    kleiner ist, als <nummer1> erfolgt die Ausgabe rueckwaerts.
+    'todo verschieben' verschiebt einen Eintrag an eine bestimmte Stelle oder
+                    einen Schritt nach oben oder unten.
+
+    Negative Nummern bei 'todo anzeigen' werden vom Ende der Todo-Liste aus
+    gezaehlt.
+
+ SIEHE AUCH:
+
+ LETZTE AENDERUNG:
+    Mit, 02.10.2002, 02:00:00 von Mandragon
\ No newline at end of file
diff --git a/doc/mcmd/traenke b/doc/mcmd/traenke
new file mode 100644
index 0000000..1e4d305
--- /dev/null
+++ b/doc/mcmd/traenke
@@ -0,0 +1,28 @@
+
+traenke
+-------
+
+ MAGIERKOMMANDO:
+    traenke
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Mit diesem Kommando kann man bestimmen, ob man ueberall, wo ein
+    Zaubertrank versteckt ist, auch einen Trank finden kann.
+
+    Ansonsten findet man nur die Traenke, die man in seiner Liste stehen hat.
+
+    Der Befehl wirkt als Umschalter: Kann man Traenke finden, so wird das
+    Verhalten nun ausgeschaltet, und umgekehrt.
+
+ HINWEIS:
+    Wenn man mit eingeschaltetem "traenke" Zaubertraenke findet, werden
+    diese NICHT aus der ZT-Liste ausgetragen.
+
+ SIEHE AUCH:
+    attribute, zaubertraenke
+
+ LETZTE AeNDERUNG:
+   13.08.2015, Arathorn 
diff --git a/doc/mcmd/trans b/doc/mcmd/trans
new file mode 100644
index 0000000..0c57be4
--- /dev/null
+++ b/doc/mcmd/trans
@@ -0,0 +1,24 @@
+
+trans
+-----
+
+ MAGIERKOMMANDO:
+    trans <name>
+
+ ARGUMENTE:
+
+     <name>
+        Name eines Lebewesens
+
+ BESCHREIBUNG:
+    Das Lebewesen <name> wird zu Dir transportiert.
+
+    Spieler duerfen dadurch nicht im Spiel beeinflusst werden!
+
+    Magier-Lehrlingen steht dieser Befehl noch nicht zur Verfuegung.
+
+ SIEHE AUCH:
+    vertrag, goto
+
+ LETZTE AeNDERUNG:
+    Tue, 02.09.1997, 16:55:14 von Wargon
diff --git a/doc/mcmd/udpq b/doc/mcmd/udpq
new file mode 100644
index 0000000..4fff71e
--- /dev/null
+++ b/doc/mcmd/udpq
@@ -0,0 +1,47 @@
+
+udpq
+----
+
+ MAGIERKOMMANDO:
+    udpq <mud> <type>
+
+ ARGUMENTE:
+
+     <mud>
+        ein MUD aus der `muds'-Liste
+     <type>
+        Art der gewuenschten Information
+
+ BESCHREIBUNG:
+    Mit diesem Kommando kann man Informationen ueber den UDP-Service und den
+    INETD von anderen MUDs aus der `muds'-Liste abfragen.
+
+    Folgende Informationen (<type>) koennen abgefragt werden:
+
+     commands
+        UDP-Kommandos, die das MUD versteht
+     email
+        EMail-Adresse, mit der das MUD bzw. seine Administratoren zu erreichen
+        sind
+     hosts
+        Die Liste der Hosts, mit denen das MUD in Verbindung steht (im selben
+        Format wie `/etc/INETD_HOSTS'
+     inetd
+        Die Versionsnummer des verwendeten INETD
+     list
+        Die Liste der Informationen, die man mit dem `udpq'-Befehl von dem MUD
+        erhalten kann (das koennen mehr oder weniger Punkte sein, als in
+        dieser Liste hier vorhanden sind, je nach Version des INETD auf der
+        anderen Seite)
+     mud_port
+        Der Port, unter dem das MUD laeuft
+     time
+        Die Systemzeit des MUDs
+     version
+        Die Versionsnummer des verwendeten GameDrivers
+
+ SIEHE AUCH:
+    muds
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/upd b/doc/mcmd/upd
new file mode 100644
index 0000000..d66236a
--- /dev/null
+++ b/doc/mcmd/upd
@@ -0,0 +1,79 @@
+
+upd
+---
+
+ MAGIERKOMMANDO:
+    upd [ -abcdhfilmrv ] <datei> [...]
+
+ ARGUMENTE:
+
+     <datei> [...]
+        die zu bearbeitende(n) Datei(en)
+
+ BESCHREIBUNG:
+    Dieses spezielle Kommando stellt ein dateibasiertes Update dar. Es
+    zerstoert und/oder laedt geladene Objekte (Blueprints).
+
+    Die Dateinamen koennen auch Muster wie * oder ? enthalten.
+
+    Wird ein Objekt nicht nur zerstoert, sondern auch neu erzeugt, so wird
+    Configure() benutzt, sofern es eines hat, d.h. die Objektdaten werden
+    vor der Zerstoerung mittels Configure() abgerufen und im neuen Objekt mit
+    Configure() wieder gesetzt.
+
+    Zusaetzlich koennen folgende Flags angegeben werden:
+
+     -a  Alle Instanzen (Clones) der Datei werden zerstoert. Da dazu die
+         gesamte Objektliste durchsucht werden muss und diese Funktion
+         entsprechend zeitaufwendig ist, sollte man dieses nur mit Bedacht
+         benutzen.
+     -b  Falls es beim Update einen Fehler gab, wird versucht, ein Backtrace
+         aus `/<rechner>.debug.log' zu lesen.
+     -C  Die Nutzung von Configure() wird erzwungen, d.h. Objekte, die kein
+         oeffentliches Configure() haben, werden nicht zerstoert/ersetzt.
+         (im Zusammenspiel mit -l oder -r, inkompatibel mit -c)
+     -c  Die Properties des Originalobjektes werden kopiert. In diesem Fall
+         wird Configure() _nicht_ benutzt (im Zusammenspiel mit -l und -r,
+         inkompatibel mit -C)
+     -d  Beim Zerstoeren der Instanzen werden auch evtl. in ihnen vorhandene
+         Objekte zerstoert. Nuetzlich, um z.B. NPCs samt ihrer Ausruestung zu
+         entsorgen.
+     -f  Alle Instanzen (Clones) der Datei werden gesucht.
+     -h  Es wird kein remove versucht, sondern sofort destruct verwendet.
+         Bitte nur in Ausnahmefaellen verwenden.
+     -i  Nicht nur die Datei selbst wird zerstoert, sondern auch alle geerbten
+         Klassen (soweit sie geladen sind).
+     -l  Die Datei wird geladen (aehnlich wie beim Befehl load, allerdings
+         wird das alte Objekt zerstoert, falls es schon geladen ist).
+     -m  MAKE - wie -i, aber es werden nur Objekte zerstoert, die aelter sind
+         als die Datei
+     -r  Wenn ein Objekt zerstoert wurde, wird versucht, es neu zu laden.
+     -s  Alle Erfolgsmeldungen untergeordneter Prozesse unterdruecken.
+     -v  Der volle Abhaengigkeitsbaum wird ausgegeben (im Zusammenspiel mit
+         -m)
+
+    Es koennen auch Objekte angegeben werden, die nicht als Datei existieren,
+    d.h. man kann auch Instanzen mit dem # im Namen zerstoeren. Ausserdem
+    koennen nur Objekte, die man selbst schreiben darf, zerstoert/geladen
+    werden.
+
+    `upd' merkt sich das zuletzt geladene oder geclonte Objekt! Dies wirkt
+    sich vor allem dann aus, wenn man ein Objekt zum testen immer wieder
+    zerstoeren, updaten, laden und clonen muss. Diese Reihe von Kommandos wird
+    durch `upd -mr' ohne Argument auf das zuletzt geladene oder gelonte Objekt
+    vollzogen.
+
+ BEISPIELE:
+
+    > upd *.c
+    > upd -r *
+    > upd -mv test.c
+    > upd -r test#12345
+    > upd -ar /d/inseln/mandragon/meloran/obj/alle/meloran_obj
+
+ SIEHE AUCH:
+    destruct, load
+    Configure()
+
+ LETZTE AENDERUNG:
+    Tue, Oct 10 18:50:00 2000 von Mandragon
diff --git a/doc/mcmd/verfolge b/doc/mcmd/verfolge
new file mode 100644
index 0000000..8cff48c
--- /dev/null
+++ b/doc/mcmd/verfolge
@@ -0,0 +1,40 @@
+
+verfolge
+--------
+
+ MAGIERKOMMANDO:
+    verfolge [<name>]
+
+ ARGUMENTE:
+
+    <name> (optional)
+       Name eines eingeloggten Spielers oder eines NPCs. Der angegebene
+       Name muss eindeutig sein, jedoch reichen die Anfangsbuchstaben aus.
+       Wenn das der Fall ist, wird zuerst nach einem Spieler dieses Namens
+       gesucht, danach nach einem NPC im selben Raum wie der Magier, 
+       anschliessend nach einem NPC irgendwo sonst im Mud. Falls sich das 
+       Zielobjekt nicht im selben Raum wie der Magier befindet, wird er
+       hinbewegt.
+
+       Der Magier kann sich nicht selbst verfolgen.
+
+       Es werden nur Lebewesen gefunden, die ein Environment haben, denn
+       nur diese koennen sinnvoll verfolgt werden.
+
+ BESCHREIBUNG:
+    Du verfolgst den Spieler oder den NPC <name> durch das ganze MUD.
+
+    'verfolge' ohne Parameter bricht eine aktive Verfolgung ab.
+
+ BEISPIEL:
+    'verfolge hum' beginnt die Verfolgung einer Hummel. Wenn jedoch Humni
+    eingeloggt sein sollte, ist die Eingabe nicht eindeutig und es wird
+    eine Fehlermeldung ausgegeben. In diesem Fall koennte man die Angabe 
+    durch Hinzufuegen eines Zeichens praezisieren, beispielsweise 
+    'verfolge humm'.
+
+ SIEHE AUCH:
+    goto, snoop
+
+ LETZTE AeNDERUNG:
+    Thu, 21.04.2016 von Arathorn
diff --git a/doc/mcmd/vis b/doc/mcmd/vis
new file mode 100644
index 0000000..f9f3b92
--- /dev/null
+++ b/doc/mcmd/vis
@@ -0,0 +1,19 @@
+
+vis
+---
+
+ MAGIERKOMMANDO:
+    vis [e]
+
+ ARGUMENTE:
+    Wird der Paramater ´e` angegeben, erhalten die Mitspieler die
+    Erwartemeldungen wie beim Einloggen.
+
+ BESCHREIBUNG:
+    Falls Du unsichtbar warst, wirst Du nun wieder sichtbar.
+
+ SIEHE AUCH:
+    invis
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/mcmd/zap b/doc/mcmd/zap
new file mode 100644
index 0000000..b0bf515
--- /dev/null
+++ b/doc/mcmd/zap
@@ -0,0 +1,25 @@
+
+zap
+---
+
+ MAGIERKOMMANDO:
+    zap <name>
+
+ ARGUMENTE:
+
+     <name>
+        Name eines Lebewesens
+
+ BESCHREIBUNG:
+    Das Lebewesen <name> wird mit einem Schlag getoetet. Dabei werden die
+    Meldungen aus der Property P_ZAP_MSG ausgegeben.
+
+    Spieler duerfen mit diesem Befehl nicht beeinflusst werden!
+
+    Magier-Lehrlingen steht dieser Befehl noch nicht zur Verfuegung.
+
+ SIEHE AUCH:
+    vertrag, toete
+
+ LETZTE AeNDERUNG:
+    Tue, 02.09.1997, 16:55:20 von Wargon
diff --git a/doc/mcmd/zwinge b/doc/mcmd/zwinge
new file mode 100644
index 0000000..5f57f73
--- /dev/null
+++ b/doc/mcmd/zwinge
@@ -0,0 +1,27 @@
+
+zwinge
+------
+
+ MAGIERKOMMANDO:
+    zwinge <name> [zu] <kommando>
+
+ ARGUMENTE:
+
+     <name>
+        Name eines Lebewesens
+     <kommando>
+        Die auzufuehrende Aktion
+
+ BESCHREIBUNG:
+    Das angegebene Lebewesen wird dazu gezwungen, die angegebene Aktion
+    auszufuehren. Allerdings beherrschen insbesondere NPCs nicht alle Befehle.
+
+    Spieler duerfen mit diesem Befehl nicht beeinflusst werden!
+
+    Magier-Lehrlingen steht dieser Befehl noch nicht zur Verfuegung.
+
+ SIEHE AUCH:
+    vertrag
+
+ LETZTE AeNDERUNG:
+    Tue, 02.09.1997, 16:55:24 von Wargon
diff --git a/doc/missing b/doc/missing
new file mode 100644
index 0000000..ea65a92
--- /dev/null
+++ b/doc/missing
@@ -0,0 +1,8 @@
+Fehlende Manpages, die mal geschrieben werden muessten:
+======================================================
+
+* P_MAGIC_RESISTANCE_OFFSET
+* P_DEFUEL_LIMIT_FOOD, P_DEFUEL_LIMIT_DRINK
+* P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+* P_DEFUEL_AMOUNT_FOOD, P_DEFUEL_AMOUNT_DRINK
+* 
diff --git a/doc/obj/doormaster b/doc/obj/doormaster
new file mode 100644
index 0000000..c3df97b
--- /dev/null
+++ b/doc/obj/doormaster
@@ -0,0 +1,26 @@
+Der doormaster.c dient als zentrale Verwaltungsstelle fuer die Tueren im
+MorgenGrauen. In der Regel kommt man mit diesem Objekt nicht in Beruehrung,
+da die Kommunikation ueber /std/room/doors.c laeuft.
+
+Folgende Funktionen stehen zur Erzeugung, Abfrage und Manipulation von Tueren
+bereit:
+
+ * NewDoor()
+     Tuer einbauen. Wird im ueblicherweise im create() eines Raums benutzt.
+
+ * QueryDoorStatus()
+     Status einer Tuer abfragen.
+
+ * SetDoorStatus()
+     Status einer Tuer setzen.
+
+ * QueryAllDoors()
+     Gibt ein Mapping mit allen Tueren im MG und deren Zustaenden zurueck.
+
+
+SIEHE AUCH:
+    NewDoor(), QueryDoorKey(), QueryDoorStatus(), SetDoorStatus(),
+    /std/room/doors.c, GetPhiolenInfos(), QueryAllDoors(), P_DOOR_INFOS
+
+-----------------------------------------------------------------------------
+Letzte Aenderung: Don, 08.05.2014, Gabylon
diff --git a/doc/obsolete/add_verb b/doc/obsolete/add_verb
new file mode 100644
index 0000000..ca71933
--- /dev/null
+++ b/doc/obsolete/add_verb
@@ -0,0 +1,27 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+        void add_verb(string str)
+
+DESCRIPTION:
+        This function is connected to the add_action() function. It
+        will set up the command str to trigger a call to the function
+	set up by the previous call to add_action().
+        
+        This function is now obsolete as the verb can be given directly
+	with add_action(). add_verb() remains for compatibility.
+
+	[marion Sun Jan 19 1992 Don't use it. This file is retained
+	because of somewhat nostalgic reasons.]
+
+HISTORY
+  Removed in LDMud 3.3 and LP "03.02.1@150",
+  obsoleted by add_action().
+
+SEE ALSO:
+        add_action(E), query_verb(E), add_xverb(E), remove_action(E)
+
+29.10.2006 Zesstra
\ No newline at end of file
diff --git a/doc/obsolete/add_xverb b/doc/obsolete/add_xverb
new file mode 100644
index 0000000..bc81dbb
--- /dev/null
+++ b/doc/obsolete/add_xverb
@@ -0,0 +1,28 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS
+	void add_xverb(string str)
+
+DESCRIPTION
+	This function is connected to the add_action() function. It
+	will set up the command str to trigger a call to the function
+	set up by the previous call to add_action().
+	
+	add_xverb() differs from add_verb() in that only leading
+	characters of the input line must match with str. It basically
+	is the same as setting flag in add_action().
+
+	[marion Sun Jan 19 1992 Don't use it. This file is retained
+	because of somewhat nostalgic reasons.]
+
+HISTORY
+  Removed in LDMud 3.3 and LP "03.02.1@150",
+  obsoleted by add_action().
+
+SEE ALSO
+	add_action(E), query_verb(E), add_verb(E)
+
+29.10.2006 Zesstra
\ No newline at end of file
diff --git a/doc/obsolete/allocate_mapping b/doc/obsolete/allocate_mapping
new file mode 100644
index 0000000..22e1252
--- /dev/null
+++ b/doc/obsolete/allocate_mapping
@@ -0,0 +1,33 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+	mapping m_allocate(int size, int width)
+
+DESCRIPTION:
+	Reserve memory for a mapping.
+
+	size is the number of entries (i.e. keys) to reserve, width is
+	the number of data items per entry. If the optional width is
+	omitted, 1 is used as default.
+
+	This is useful only when you are going to construct a mapping
+	whose approximate size you know beforehand, to save on malloc
+	overhead. If you don't fill in data for all the allocated
+	elements, any leftovers will be freed after the current
+	function execution ended. It is also useful if you want the
+	mapping to have a certain width even if you don't provide
+	all the data items for the keys yet.
+
+HISTORY
+  Renamed to 'm_allocate()' in LDMud 3.2.6 and LP "03.02.1@150".
+  Since LDMud 3.2.9, not available if driver is compiled without
+    USE_DEPRECATED.
+  Removed in LDMud 3.3 and LP "03.02.1@150"
+
+SEE ALSO:
+	mappings(LPC), walk_mapping(E), get_type_info(E)
+
+29.10.2006 Zesstra
\ No newline at end of file
diff --git a/doc/obsolete/assoc b/doc/obsolete/assoc
new file mode 100644
index 0000000..bd5a945
--- /dev/null
+++ b/doc/obsolete/assoc
@@ -0,0 +1,31 @@
+OPTIONAL, VERALTET
+SYNOPSIS
+        int   assoc(mixed key, mixed *keys);
+        mixed assoc(mixed key, mixed *alist [, mixed fail]);
+        mixed assoc(mixed key, mixed *keys, mixed *data [, mixed fail]);
+
+BESCHREIBUNG
+        Alle drei Aufrufe suchen nach einem <key> in einem <alist> (einem
+        Array von zwei Arrays gleicher Groesse) oder in einem geordneten
+        Array <keys>. Der Versuch, in einem anderen Konstrukt zu suchen,
+        fuehrt zu einem unvorhersehbaren Ergebnis.
+
+        Komplexitaet: O( lg(n) ), wobei <n> die Anzahl Keys ist.
+
+        1.  Form: Key-Suche
+            <key> wird im Array <keys> gesucht. Das Resultat ist der Index,
+            in dem <key> gefunden wurde. Wird <key> nicht gefunden, liefert
+            assoc() -1.
+
+        2.  Form: Suche in Alist.
+            <key> wird in der <alist> gesucht, das Resultat sind die Werte,
+            die zu <key> gehoeren, wenn <key> gefunden wird. Wenn <key> nicht
+            gefunden wird, wird 0 zurueck geliefert oder <fail>, falls
+            angegeben.
+
+        Damit das Sinn macht, muss <data> so geordnet sein, dass es zu <key>
+        passt. Diese Form der Suche ist deshalb vorwiegend fuer
+        multidimensionale Alists geeignet.
+
+SIEHE AUCH
+        alists(LPC), insert_alist(E), order_alist(E)
diff --git a/doc/obsolete/copy_mapping b/doc/obsolete/copy_mapping
new file mode 100644
index 0000000..ea25eab
--- /dev/null
+++ b/doc/obsolete/copy_mapping
@@ -0,0 +1,36 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+	mapping copy(mapping)
+
+DESCRIPTION:
+        Create a shallow copy of <arg> and return it. For arrays and mappings
+        this means that a new array or mapping is created with copies of the
+        original content. Embedded arrays and mappings are copied by reference!
+
+EXAMPLE
+        mixed *a, *b;
+
+        a = ({ 1, ({ 21, 22 }) });
+        b = copy(a);
+        a[0] = -1; a[1][0] = -21;
+         --> a is now ({ -1, ({ -21, 22 }) })
+             b is now ({  1, ({ -21, 22 }) })
+
+WARNING
+	Keep in mind, that a copy of a mapping, that contains arrays/mappings,
+	contains references to these Mappings/Arrays, not real copies. If you
+	don't take care, you can a) change the original mappings while working
+	on the copy and b) create recursive mappings, that leak memory.
+
+HISTORY
+  Superseeded by the copy() efun.
+  Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SEE ALSO:
+	mappings(LPC)
+
+26. September 2006, Zesstra
\ No newline at end of file
diff --git a/doc/obsolete/creator b/doc/obsolete/creator
new file mode 100644
index 0000000..44c320e
--- /dev/null
+++ b/doc/obsolete/creator
@@ -0,0 +1,22 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS
+        string creator(object ob)
+
+DESCRIPTION
+        This functions is not available in native mode, which uses
+        user-ids instead.
+        Returns the creator (i.e. the name of the wizard or domain) of
+        the object. Default for ob is this_object().
+
+HISTORY
+        Since 3.2.1@47, this efun is an alias for getuid().
+
+SEE ALSO
+        getuid(E), native(C), uids(C)
+
+Zesstra, 04.02.2007
+
diff --git a/doc/obsolete/creator_file b/doc/obsolete/creator_file
new file mode 100644
index 0000000..c4fe6ef
--- /dev/null
+++ b/doc/obsolete/creator_file
@@ -0,0 +1,32 @@
+
+   *****************************************************************
+   * ACHTUNG: WIRD VOM DRIVER NICHT MEHR GERUFEN! NICHT VERWENDEN! *
+   *****************************************************************
+
+SYNOPSIS
+	string creator_file(mixed ob)
+
+DESCRIPTION
+	Return the name of the creator of a newly created object, i.e.
+	the name of the user that is responsible for the LPC source
+	file the object was loaded from. If the function returns 0,
+	the object can't be loaded and is destructed again
+	immediately.
+
+	In !compat mode, the returned string serves as the initial uid
+	(``cuid'') of the object. Objects whose cuid is the
+	backbone-id will then get the uid of the currently active user
+	as their userid instead.
+
+	Under compat mode this function is called as well.
+
+	If this function is not provided by the master object, no
+	other object can be loaded.
+
+HISTORY
+	Dropped in 3.2.1, replaced by the _UID driver hooks.
+
+SEE ALSO
+	uids(C), creator(E)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/define_include_dirs b/doc/obsolete/define_include_dirs
new file mode 100644
index 0000000..4436308
--- /dev/null
+++ b/doc/obsolete/define_include_dirs
@@ -0,0 +1,24 @@
+
+   *****************************************************************
+   * ACHTUNG: WIRD VOM DRIVER NICHT MEHR GERUFEN! NICHT VERWENDEN! *
+   *****************************************************************
+
+SYNOPSIS
+	string *define_include_dirs(void)
+
+DESCRIPTION
+	Return an array of string patterns giving the absolut paths
+	where to search an include file. The patterns have to have a
+	%s at the place where the name given in the #include statement
+	has to be inserted.
+
+EXAMPLE
+	define_include_dirs() { return ({ "sys/%s", "lib/%s" }); }
+
+HISTORY
+	Dropped in 3.2.1, replaced by H_INCLUDE_DIRS hook.
+
+SEE ALSO
+	master(M)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/efun308 b/doc/obsolete/efun308
new file mode 100644
index 0000000..9ebb42d
--- /dev/null
+++ b/doc/obsolete/efun308
@@ -0,0 +1,27 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS
+	void efun308(object item, object env)
+
+DESCRIPTION
+	The item is moved into its new environment env, which may be 0.
+	This efun is to be used in the move_object() hook, as it does
+	nothing else than moving the item - no calls to init() or such.
+
+	Don't use it in your own objects!
+
+HISTORY
+	Introduced in 3.2.1@1
+  renamed to 'set_environment()' in LDMud 3.2.6 and LP "03.02.1@150".
+  Since LDMud 3.2.9, not available if driver is compiled without
+    USE_DEPRECATED.
+  Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SEE ALSO
+	remove(A), init(A), move_object(E), transfer(E), hooks(C),
+	native(C)
+
+29.10.2006 Zesstra
\ No newline at end of file
diff --git a/doc/obsolete/exclude_array b/doc/obsolete/exclude_array
new file mode 100644
index 0000000..9457e03
--- /dev/null
+++ b/doc/obsolete/exclude_array
@@ -0,0 +1,27 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+	mixed *exclude_array(mixed *arr, int from, int to)
+
+DESCRIPTION:
+  Deletes an section from an array.
+
+  Please use the slicing operator:
+  arr = arr[0..from-1]+arr[to+1..<1]
+  or even better:
+  arr[from..to] = ({});
+
+  This efun is temporarily a simulated efun until is finally
+  droppped.
+
+HISTORY
+        Removed in LDMud 3.3.
+
+SEE ALSO
+        [](E)
+
+16.12.2008 Zesstra
+
diff --git a/doc/obsolete/extract b/doc/obsolete/extract
new file mode 100644
index 0000000..de40e00
--- /dev/null
+++ b/doc/obsolete/extract
@@ -0,0 +1,26 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS
+        string extract(string str, int from, int to)
+        string extract(string str, int from)
+
+DESCRIPTION
+	DO NOT USE THIS EFUN ANYMORE! IT HAS BEEN REMOVED FROM THE DRIVER.
+
+        Extract a substring from a string.
+        This is the old notation for str[from..to] and supported
+        only for hysterical raisins.
+        
+        In LDMud this efun is temporarily a simulated efun until is finally
+        droppped.
+
+HISTORY
+        Removed in LDMud 3.3.
+
+SEE ALSO
+        [](E)
+
+11.11.2006 Zesstra
diff --git a/doc/obsolete/file_name b/doc/obsolete/file_name
new file mode 100644
index 0000000..81f42dc
--- /dev/null
+++ b/doc/obsolete/file_name
@@ -0,0 +1,36 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+  string object_name()
+  string object_name(object ob)
+
+DESCRIPTION:
+  Get the file name of an object or if no argument is given of the current
+  object. If the object is a cloned object, then it will not have a
+  corresponding file name, but rather a new name based on the original
+  file name.
+  The returned name always begins with '/' (absolute path),
+	except when the parser runs in COMPAT (-o) mode.
+
+EXAMPLES:
+  find_object(object_name(ob)) == ob
+  This is guaranteed to be true for all objects ob that are not
+  destructed.
+  
+  sizeof(explode(object_name(ob), "#")) == 1
+  This is always true if ob is a blue print.
+
+HISTORY
+  In LDMud 3.2.6 renamed to object_name(), this old name is
+  available as alias.
+  Since LDMud 3.2.9, not available if driver is compiled without
+  USE_DEPRECATED.
+  Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SEE ALSO:
+   find_object(E)
+
+29.10.2006 Zesstra
\ No newline at end of file
diff --git a/doc/obsolete/filter_array b/doc/obsolete/filter_array
new file mode 100644
index 0000000..772cb03
--- /dev/null
+++ b/doc/obsolete/filter_array
@@ -0,0 +1,151 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+filter_array(E)
+
+FUNKTION:
+     mixed *filter_array(mixed *arr, string fun, string|object ob
+                         [, mixed extra, ...])
+     mixed *filter_array(mixed *arr, closure cl [, mixed extra, ...])
+     mixed *filter_array(mixed *arr, mapping map)
+
+PARAMETER:
+     arr	- zu filterndes Array
+     fun/cl	- zu rufende Methode/Closure
+     map	- filterndes Mapping
+     ob		- Objekt/Dateiname, an dem Methode gerufen werden soll
+     extra	- weitere Parameter fuer Methode/Closure
+
+BESCHREIBUNG:
+     Filtert die Elemente aus 'arr' durch die Methode 'fun', die Closure 'cl'
+     oder das Mapping 'map' in ein neues Array.
+     Fuer jedes Element aus 'arr' wird 'fun' oder 'cl' mit dem Element als
+     erstem Parameter [und folgend den optionalen Extra-Parametern] gerufen
+     bzw. 'map' mit dem Element indiziert.
+
+     Wenn der Aufruf
+	ob->fun(element [, extra1, extra2, ...]) bzw.
+	funcall(cl, element [, extra1, extra2, ...])
+     als Rueckgabewert !=0 zurueckgibt oder das Element als Schluessel im
+     Mapping enthalten ist:
+	member(map, element) == 1
+     dann wird das Element in das neue Array aufgenommen, sonst nicht.
+
+
+     Verwendung von Methoden:
+	Wenn bei der Angabe von 'fun' kein Objekt 'ob' in Form eines Strings
+	oder Objekts angegeben wird, wird this_object() angenommen.
+
+     Verwendung von Closures:
+	Es koennen sowohl Lfun-Closures als auch Lambda-Closures verwendet
+	werden. Lfun-Closures koennen, wenn im selben Objekt vorhanden auch
+	'private' oder/und 'static' deklariert sein, muessen aber zu dem
+	Zeitpunkt der Verwendung bekannt sein (Funktionsprototypen benutzen).
+
+RUeCKGABEWERT:
+     Gefiltertes Array mit Filterbedingung erfuellenden Elementen.
+
+BEMERKUNGEN:
+     (1) Achtung, die Elemente in 'arr' werden nicht tief kopiert, sind sie
+     also selbst Arrays oder Mappings, so fuehrt eine spaetere Aenderung im
+     Rueckgabe-Arrays zur Aenderung im Ursprungsarray:
+
+     int *i, *j;
+     i=({({1,2,3}),({4,5,6})});
+     j=filter_array(i, #'sizeof);	// filtert leere Arrays heraus
+     j[0][0]=8;
+
+     fuehrt zu: i==j==({({8,2,3}),({4,5,6})});
+
+     (2) Das Kopieren in das Rueckgabemapping erfolgt fuer jedes Element nach
+     Ausfuehrung der Filtermethode. Aenderungen der Werte im Array in dieser
+     Methode (globale Variable/Uebergabe als Referenz an filter_array)
+     schlagen sich also im Rueckgabearray nieder.
+
+BEISPIEL:
+     ### Filtere alle Lebewesen in einem Raum in ein Array ###
+     filter_array(all_inventory(this_object()),#'living);
+
+
+     ### Filtere alle tauben Spieler im Raum in ein Array ###
+     static int filter_isdeaf(object who) {
+       return(living(who) && who->QueryProp(P_DEAF));
+     }
+     ...
+     filter_array(all_inventory(this_object()), #'filter_isdeaf);
+
+
+     ### Filtern von Idlern (>=1 Sekunde idle) ###
+     // Folgend identische Resultate, aber andere Ansaetze:
+
+     #1: nutzt die Efun query_idle() als Lfun-Closure (ideal hier)
+     idle_usr = filter_array(users(), #'query_idle );
+
+     #2,3: mit Filtermethode
+     int check_if_idle(object user) {
+      return query_idle(user);
+     }
+      #2: filtert mittels der Lfun im selben Objekt die Idler in das
+          Rueckgabearrays
+      idle_usr = filter_array(users(), "check_if_idle");
+      idle_usr = filter_array(users(), "check_if_idle", this_object());
+
+      #3: ruft die Lfun check_if_idle() als Lfun-Closure (Funktionspointer)
+      idle_usr = filter_array(users(), #'check_if_idle );
+
+     #4: nutzt eine Lambda-Closure (langsamer, nicht fuer alle leserlich)
+     idle_usr = filter_array(users(), lambda(({'u}), ({#'query_idle, 'u})));
+
+
+     ### Filtern von Idlern (>=20 Sekunden idle) mit Extraparameter ###
+     // Folgend identische Resultate, aber andere Ansaetze:
+
+     #1: die Efun koennen wir nicht mehr direkt nutzen, weil sie
+         diesen Parameter nicht unterstuetzt
+     // idle_usr = filter_array(users(), #'query_idle );
+
+     #2,3: mit Filtermethode ... mit neuem Parameter
+     int check_if_idle(object user, int length) {
+      return query_idle(user)>length;
+     }
+      #2: filtert mittels der Lfun im selben Objekt die Idler in das
+          Rueckgabearrays ... mit drittem Parameter!
+      idle_usr = filter_array(users(), "check_if_idle", this_object(), 20);
+
+      #3: ruft die Lfun check_if_idle() als Lfun-Closure (Funktionspointer)
+      idle_usr = filter_array(users(), #'check_if_idle, 20);
+
+     #4: nutzt eine Lambda-Closure (langsamer, nicht fuer alle leserlich)
+     idle_usr =
+       filter_array(users(),
+         lambda(({'u, 'l}), ({#'>,({#'query_idle, 'u}),'l})),
+         20);
+
+AeQUIVALENZCODE (nicht empfohlen, nur zum Verstaendnis!):
+     int i;
+     mixed *ret; mixed *input;
+
+     ret=allocate(0);
+     i=sizeof(input);
+     while(i--)
+       if(ob->fun(input[i] [, extra1, extra2, ...]))
+       // if(funcall(cl, input[i] [, extra1, extra2, ...]))
+       // if(member(map, input[i]))
+         ret+=({input[i]});
+HISTORY
+    Since LDMud 3.2.6 obsoleted by efun filter().
+    Since LDMud 3.2.9, not available if driver is compiled without
+      USE_DEPRECATED.
+    Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SIEHE AUCH:
+     Arrays:		fmap_array(E)
+     Objektarrays:	filter_objects(E), map_objects(E)
+     Mappings:		filter(E), map(E)
+
+     Sonstiges:		sort_array(E), unique_array()
+			alist, transpose_array(E)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/filter_array.eng b/doc/obsolete/filter_array.eng
new file mode 100644
index 0000000..6737929
--- /dev/null
+++ b/doc/obsolete/filter_array.eng
@@ -0,0 +1,60 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+filter_array(E)
+SYNOPSIS
+     mixed *filter_array(mixed *arr, string fun, string|object ob
+                         [, mixed extra, ...])
+     mixed *filter_array(mixed *arr, closure cl [, mixed extra, ...])
+     mixed *filter_array(mixed *arr, mapping map [, mixed extra, ...])
+
+DESCRIPTION
+        Returns an array holding the items of arr filtered through
+        ob->fun(element, extra, ...), the closure cl, or the mapping map.
+        The function 'fun' in 'ob' resp. the closure 'cl' is called
+        for each element in arr with that element as parameter. The
+        extra and following parameters are in each call if given.
+        The mapping 'map' is likewise indexed by each element.
+        If ob->fun(arr[index], extra) returns != 0 resp.
+        map[arr[index]] exists, the element is included in the
+        returned array.
+
+        If arr is not an array, an error occurs.
+
+        The extra argument(s) are optional and must not be protected
+        references like &(i[0]).
+        If <ob> is omitted, or neither a string nor an object, it
+        defaults to this_object().
+
+        Since 3.2.1@36, the second arg can also be a mapping. Then
+        only the elements of the array which belong to the map (as
+        keys) will be returned (i.e. map[arr[index]] != 0).
+
+EXAMPLE
+        int check_if_idle(object user) { return query_idle(user); }
+
+        ...
+
+        object *idle_users;
+
+        idle_users = filter_array(users(), "check_if_idle");
+        /* equivalent but smaller and faster */
+        idle_users = filter_array(users(), #'query_idle );
+
+        Now idle_users contains all users that have been idle for more
+        than 1 second.
+
+HISTORY
+    Since LDMud 3.2.6 obsoleted by efun filter().
+    Since LDMud 3.2.9, not available if driver is compiled without
+      USE_DEPRECATED.
+    Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SEE ALSO:
+        Similar filter:	filter_array(E), filter_object(E), filter(E)
+        Mapping:	map(E), map_objects(E), map(E)
+        Related:	sort_aray(E), unique_array(E)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/filter_mapping b/doc/obsolete/filter_mapping
new file mode 100644
index 0000000..a70c724
--- /dev/null
+++ b/doc/obsolete/filter_mapping
@@ -0,0 +1,126 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+filter_mapping(E)
+
+FUNKTION:
+     mapping filter(mapping map, string fun, string|object ob
+                            [, mixed extra, ...])
+     mapping filter(mapping map, closure cl [, mixed extra, ...])
+
+PARAMETER:
+     map	- zu filterndes Mapping
+     fun/cl	- zu rufende Methode/Closure
+     ob		- Objekt/Dateiname, an dem Methode gerufen werden soll
+     extra	- weitere Parameter fuer Methode/Closure
+
+BESCHREIBUNG:
+     Filtert die Elemente (jeweils Schluessel) aus 'map' durch die
+     Methode 'fun' oder die Closure 'cl' in ein neues Mapping.
+     Fuer jedes Element aus 'map' wird 'fun' oder 'cl' mit dem Schluessel als
+     erstem Parameter [und folgend den optionalen Extra-Parametern] gerufen.
+
+     Wenn der Aufruf
+	ob->fun(element [, extra1, extra2, ...]) bzw.
+	funcall(cl, element [, extra1, extra2, ...])
+     als Rueckgabewert !=0 zurueckgibt dann wird der Schluessel+Werte in das
+     neue Array aufgenommen, sonst nicht.
+
+     Wenn auf die Werte zugegriffen werden muss, kann das Mapping 'map'
+     zusaetzlich als 'extra'-Parameter uebergeben werden. Alternativ kann man
+     walk_mapping() benutzen und das Rueckgabemapping selbst erstellen.
+
+
+     Verwendung von Methoden:
+	Wenn bei der Angabe von 'fun' kein Objekt 'ob' in Form eines Strings
+	oder Objekts angegeben wird, wird this_object() angenommen.
+
+     Verwendung von Closures:
+	Es koennen sowohl Lfun-Closures als auch Lambda-Closures verwendet
+	werden. Lfun-Closures koennen, wenn im selben Objekt vorhanden auch
+	'private' oder/und 'static' deklariert sein, muessen aber zu dem
+	Zeitpunkt der Verwendung bekannt sein (Funktionsprototypen benutzen).
+
+RUeCKGABEWERT:
+     Gefiltertes Mapping mit Filterbedingung erfuellenden Elementen.
+
+BEMERKUNGEN:
+     (1) Achtung, die Elemente in 'map' werden nicht tief kopiert, sind sie
+     also selbst Arrays oder Mappings, so fuehrt eine spaetere Aenderung im
+     Rueckgabe-Mapping zur Aenderung im Ursprungsmapping:
+
+     mapping m,n;
+     m=([1:({1,2}),0:({2,3,4})]);
+     n=filter(m, #'!);		// filtert alle Keys !=0 heraus
+     n[0][0]=8;
+
+     fuehrt zu: n=([0:({8,3,4})])
+		m=([0:({8,3,4}),1:({1,2})])
+
+     (2) Das Kopieren in das Rueckgabemapping erfolgt fuer jedes Element nach
+     Ausfuehrung der Filtermethode. Aenderungen der Werte im Mapping in dieser
+     Methode (globale Variable/Uebergabe als Referenz an filter)
+     schlagen sich also im Rueckgabemapping nieder.
+
+BEISPIELE:
+     ### erfundene Liste mit Spielern saeubern ... ###
+     mapping x=([ [/human:liafar]:  20,
+		  [/dwarf:mesirii]: 50,
+		  [/elf:zarniya]:   40,
+		  [/feline:turbo]:  30]);
+
+     int is_badguy(object who) {
+      if(who->InFight()) return 1;
+      return 0;
+     }
+
+     mapping result=filter(x, #'is_badguy);
+     // 'result' enthaelt nur noch kaempfende Spieler
+
+     ### erfundene Liste ueber ihre Werte saeubern ###
+     int is_badguy(object who, mapping m) {
+      if(m[x]<30) return 1;
+      return 0;
+     }
+
+     mapping result=filter(x, #'is_badguy, &x); // Referenz
+     // 'result' enthaelt nur Spieler mit Werten >= 30
+
+AeQUIVALENZCODE (nicht empfohlen, nur zum Verstaendnis!):
+     int i, width;
+     mapping ret; mapping input;
+     mixed *index;
+
+     width=get_type_info(input)[1];
+     ret=m_allocate(0, width);
+     index=m_indices(input);
+     i=sizeof(index);
+     while(i--)
+       if(ob->fun(index[i] [, extra1, extra2, ...]))
+       // if(funcall(cl, index[i] [, extra1, extra2, ...]))
+       {
+         int j;
+         j=width;
+
+         while(j--)
+           ret[index[i],j]=input[index[i],j];
+       }
+
+HISTORY
+    Since LDMud 3.2.6 obsoleted by efun filter_indices().
+    Since LDMud 3.2.9, not available if driver is compiled without
+      USE_DEPRECATED.
+    Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SIEHE AUCH:
+     Arrays:		filter_array(E), map(E)
+     Objektarrays:	filter_objects(E), map_objects(E)
+     Mappings:		map(E)
+
+     Sonstiges:		walk_mapping(E), m_contains(E)
+			member()
+			m_indices(E), m_values(E)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/get_root_uid b/doc/obsolete/get_root_uid
new file mode 100644
index 0000000..c1de324
--- /dev/null
+++ b/doc/obsolete/get_root_uid
@@ -0,0 +1,20 @@
+
+   **********************************************************
+  * ACHTUNG: WIRD VOM DRIVER NICHT MEHR GERUFEN! NICHT VERWENDEN! *
+   **********************************************************
+
+SYNOPSIS
+	string get_root_uid(void)
+
+DESCRIPTION
+	Return the string to be used as root-uid.
+	Under !native, the function is expendable.
+
+HISTORY
+	In 3.2.1@40, get_root_uid() was renamed to get_master_uid()
+	and got a new semantic.
+
+SEE ALSO
+	get_bb_uid(M), get_master_uid(M), uids(C), creator_file(M), creator(E)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/initialisation b/doc/obsolete/initialisation
new file mode 100644
index 0000000..e270376
--- /dev/null
+++ b/doc/obsolete/initialisation
@@ -0,0 +1,48 @@
+OBSOLETE
+CONCEPT
+	initialisation
+
+DESCRIPTION
+	There are two different flavours of initialisations,
+	selectable in config.h at driver compile time:
+
+	i)  #undef INITIALIZATION_BY___INIT
+	------------------------------------
+
+	Initialisation is done at compile time. This is fast and costs
+	no extra code in the program.  Allowed expressions currently
+	include integer literals, string literals, integer operators,
+	string addition, bracketing, array constructors, the empty
+	mapping and order_alist() .
+	When an object with initialised variables is cloned or
+	inherited, all initialised variables are copied from the
+	blueprint.  A special application of this feature is to have
+	an initialised non-empty array or a mapping; it will be shared
+	by all clones or inheriting objects unless an assignment to
+	the variable - as opposed to an assignment to an element of
+	the array/mapping - is done in all clones etc.  To prevent
+	unauthorised chenges in initialised arrays/mappings, you can
+	declare the variables as ``private static'' or use a nomask
+	reset/create that checks for undesired inheritance.
+
+	ii) #define INITIALIZATION_BY___INIT
+	-------------------------------------
+
+	Creates a function named __INIT() for all variable
+	initialisations and for calls to __INIT() in all inherited
+	objects, and runs this function at object creation time.
+	Any efun can be used in the expressions for variable
+	initialisations, even ones with severe side effects, like
+	destruct() or shutdown() . The code created for __INIT() is a
+	little worse than a medium-skilled lpc-programmer would
+	generate, because it is scattered all over the program.
+	It is possible (though discouraged) to overload __INIT() with
+	selfwritten functions.
+
+HISTORY
+  Since LDMud 3.3, order_alist() is no longer accepted without
+    INITIALIZATION_BY___INIT.
+  LDMud 3.3.378 replaced this static choice of initialisation
+    methods by compile-time pragmas.
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/intersect_alist b/doc/obsolete/intersect_alist
new file mode 100644
index 0000000..4bbbccb
--- /dev/null
+++ b/doc/obsolete/intersect_alist
@@ -0,0 +1,14 @@
+OPTIONAL
+SYNOPSIS
+        mixed * intersect_alist (mixed * list1, mixed * list2)
+
+DESCRIPTION
+        Does a fast set intersection on alist key vectors (NOT on full alists!).
+        The operator '&' does set intersection on arrays in general.
+
+EXAMPLE
+        new_list = intersect_alist(list1, list2);
+
+SEE ALSO
+        filter(E), assoc(E), insert_alist(E), map(E),
+        member_array(E), order_alist(E), sort_array(E), unique_array(E)
diff --git a/doc/obsolete/is_clone b/doc/obsolete/is_clone
new file mode 100644
index 0000000..d25b9d4
--- /dev/null
+++ b/doc/obsolete/is_clone
@@ -0,0 +1,41 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+FUNKTION:
+	is_clone(mixed ob)
+
+ARGUMENTE:
+	ob - Das zu pruefende Objekt
+
+BESCHREIBUNG:
+	is_clone stellt fest, ob ob ein geclontes Objekt ist oder nicht.
+
+RUECKGABEWERT:
+	1, wenn ob ein geclontes Objekt ist, ansonsten 0.
+
+HINWEIS:
+  Dies ist eine Simul Efun, die inzwischen ueberflussig ist und nur aus
+  Kompatibilitaetsgruenden noch existiert. Bitte die Efun 'clonep' verwenden!
+
+BEISPIELE:
+	inherit "std/thing";
+
+	#include <properties.h>
+
+	create()
+        {
+          if (!is_clone(this_object())) {
+              set_next_reset(-1);
+              return;
+          }
+
+          :: create();
+          ...
+        }
+
+siehe auch: clonep
+
+Zesstra, 04.02.2007
+
diff --git a/doc/obsolete/m_sizeof b/doc/obsolete/m_sizeof
new file mode 100644
index 0000000..2326b47
--- /dev/null
+++ b/doc/obsolete/m_sizeof
@@ -0,0 +1,21 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+	int sizeof(mapping map)
+
+DESCRIPTION:
+	Return the number of indices in mapping 'map'.
+
+HISTORY
+  Since LDMud 3.2.9, not available if driver is compiled without
+    USE_DEPRECATED.
+  Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SEE ALSO:
+	mappingp(E), mkmapping(E), m_indices,(E) m_values(E),
+	m_delete(E), widthof(E)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/m_width b/doc/obsolete/m_width
new file mode 100644
index 0000000..a6fc7a6
--- /dev/null
+++ b/doc/obsolete/m_width
@@ -0,0 +1,15 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+	int widthof(mapping map)
+
+DESCRIPTION:
+	Return the number of values per index in mapping 'map'. It means
+	the width of the mapping 'map'.
+
+SEE ALSO:
+	mappingp(E), mkmapping(E), m_indices,(E) m_values(E),
+	m_delete(E), sizeof(E)
diff --git a/doc/obsolete/map_array b/doc/obsolete/map_array
new file mode 100644
index 0000000..41329db
--- /dev/null
+++ b/doc/obsolete/map_array
@@ -0,0 +1,90 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+map_array(E)
+
+FUNKTION:
+     mixed *map(mixed *arr, string fun, object ob [, mixed extra])
+     mixed *map(mixed *arr, closure cl [, mixed extra])
+
+PARAMETER:
+     arr	- zu mappendes Array
+     fun/cl	- zu rufende Methode/Closure
+     ob		- Objekt/Dateiname, an dem Methode gerufen werden soll
+     extra	- weitere Parameter fuer Methode/Closure
+
+BESCHREIBUNG:
+     Mapped die Elemente aus 'arr' durch die Methode 'fun' oder die Closure 'cl'
+     in ein neues Array.
+     Fuer jedes Element aus 'arr' wird 'fun' oder 'cl' mit dem Element als
+     erstem Parameter [und folgend den optionalen Extra-Parametern] gerufen
+     bzw. 'map' mit dem Element indiziert.
+
+     Der Rueckgabewert der Methode/Closure wird in an der Indexposition des
+     Elementes in das neue Array eingetragen.
+
+     Verwendung von Methoden:
+	Wenn bei der Angabe von 'fun' kein Objekt 'ob' in Form eines Strings
+	oder Objekts angegeben wird, wird this_object() angenommen.
+
+     Verwendung von Closures:
+	Es koennen sowohl Lfun-Closures als auch Lambda-Closures verwendet
+	werden. Lfun-Closures koennen, wenn im selben Objekt vorhanden auch
+	'private' oder/und 'static' deklariert sein, muessen aber zu dem
+	Zeitpunkt der Verwendung bekannt sein (Funktionsprototypen benutzen).
+
+RUeCKGABEWERT:
+     Array mit Rueckgabewerten der Methode/Closure.
+
+BEISPIELE:
+     ### ersetze Strings durch ihre Grosschreibung ###
+     map(({"abc","cde"}), #'capitalize); == ({"Abc","Cde"})
+
+
+     ### ersetze in einem Spielerobjektarray die Objekte durch ihre UID ###
+     // Folgend identische Resultate, aber andere Ansaetze:
+
+     #1: mit der Efun etuid direkt (ideal hier)
+     map(users(), #'getuid);
+
+     #2,3: mit Mapmethode
+     string make_uid(object o) {
+      return getuid(o);
+     }
+      #2: als LFun-Aufruf der Mapmethode
+      map(users(), "make_uid");
+      map(users(), "make_uid", this_object());
+
+      #3: als Lfun-Closure-Aufruf der Mapmethode
+      map(users(), #'make_uid);
+
+     #4: mit Lambda-Closure (langsamer, nicht fuer alle leserlich)
+     map(users(), lambda(({'x}), ({#'getuid, 'x})));
+
+AeQUIVALENZCODE (nicht empfohlen, nur zum Verstaendnis!):
+     int i;
+     mixed *ret; mixed *input;
+
+     i=sizeof(input);
+     ret=allocate(i);
+     while(i--)
+       ret[i]=ob->fun(input[i] [, extra1, extra2, ...]));
+       // ret[i]=funcall(cl, input[i] [, extra1, extra2, ...]);
+
+HISTORY
+    Since LDMud 3.2.6 obsoleted by map().
+    Since LDMud 3.2.9, not available if driver is compiled without
+      USE_DEPRECATED.
+    Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SIEHE AUCH:
+     Arrays:		filter_array(E)
+     Objektarrays:	filter_objects(E), map_objects(E)
+     Mappings:		filter(E), map(E)
+
+     Sonstiges:		sort_array(E), unique_array()
+			alist, transpose_array(E)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/map_mapping b/doc/obsolete/map_mapping
new file mode 100644
index 0000000..22c84c0
--- /dev/null
+++ b/doc/obsolete/map_mapping
@@ -0,0 +1,86 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+map_mapping(E)
+
+FUNKTION:
+     mapping map(mapping m, string fun, object ob [, mixed extra])
+     mapping map(mapping m, closure cl [, mixed extra])
+
+PARAMETER:
+     arr	- zu mappendes Array
+     fun/cl	- zu rufende Methode/Closure
+     ob		- Objekt/Dateiname, an dem Methode gerufen werden soll
+     extra	- weitere Parameter fuer Methode/Closure
+
+BESCHREIBUNG:
+     Mapped die Elemente (jeweils Schluessel) aus 'map' durch die Methode
+     'fun' oder die Closure 'cl' in ein neues Mapping.
+     Fuer jedes Element aus 'map' wird 'fun' oder 'cl' mit dem Schluessel als
+     erstem Parameter [und folgend den optionalen Extra-Parametern] gerufen.
+
+     Der Rueckgabewert der Methode/Closure wird in fuer den Schluessel als
+     Datenwert in das neue Mapping eingetragen.
+
+     ACHTUNG: Alle anderen Daten bei Mapping mit Breite>1 verfallen!
+
+     Verwendung von Methoden:
+	Wenn bei der Angabe von 'fun' kein Objekt 'ob' in Form eines Strings
+	oder Objekts angegeben wird, wird this_object() angenommen.
+
+     Verwendung von Closures:
+	Es koennen sowohl Lfun-Closures als auch Lambda-Closures verwendet
+	werden. Lfun-Closures koennen, wenn im selben Objekt vorhanden auch
+	'private' oder/und 'static' deklariert sein, muessen aber zu dem
+	Zeitpunkt der Verwendung bekannt sein (Funktionsprototypen benutzen).
+
+RUeCKGABEWERT:
+     Mapping mit Schluessel:Rueckgabewerten der Methode/Closure.
+
+BEISPIELE:
+     // ersetze in einem Mapping die Datenwerte durch das Doppelte,
+     // nutze dabei die Datenwerte des Altmappings durch Uebergabe als
+     // extra-Parameter
+
+     // Anmerkung: Das geht mit walk_mapping() eleganter!
+
+     int do_double(string key, mapping m, int mult) {
+      return m[key]*mult;
+     }
+
+     mapping x, y;
+     x=(["a":2, "b":3]);
+     y=map((["a":2, "b":3]), #'do_double, &x, 3);
+
+     y == (["a":6,"b":9])
+
+AeQUIVALENZCODE (nicht empfohlen, nur zum Verstaendnis!):
+     int i;
+     mapping ret; mapping input;
+     mixed *index;
+
+     ret=m_allocate(0, 1);
+     index=m_indices(input);
+     i=sizeof(index);
+     while(i--)
+       ret[index[i]]=ob->fun(index[i] [, extra1, extra2, ...]))
+       // ret[index[i]]=funcall(cl, index[i] [, extra1, extra2, ...]);
+
+HISTORY
+    Since LDMud 3.2.6 obsoleted by map_indices().
+    Since LDMud 3.2.9, not available if driver is compiled without
+      USE_DEPRECATED.
+    Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SIEHE AUCH:
+     Arrays:		filter_array(E), map(E)
+     Objektarrays:	filter_objects(E), map_objects(E)
+     Mappings:		filter(E)
+
+     Sonstiges:		walk_mapping(E), m_contains(E)
+			member()
+			m_indices(E), m_values(E)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/mapping_contains b/doc/obsolete/mapping_contains
new file mode 100644
index 0000000..88df897
--- /dev/null
+++ b/doc/obsolete/mapping_contains
@@ -0,0 +1,24 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+	int m_contains(mixed &data1, ..., &dataN, map, key)
+
+DESCRIPTION:
+	If the mapping contains the key map, the corresponding values
+	are assigned to the data arguments, which massed be passed by
+	reference, and 1 is returned. If key is not in map, 0 is
+	returned and the data args are left unchanged.
+
+HISTORY
+  Renamed to 'm_contains()' in LDMud 3.2.6.
+  Since LDMud 3.2.9, not available if driver is compiled without
+    USE_DEPRECATED.
+  Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SEE ALSO:
+	mappings(LPC), member(E)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/member_array b/doc/obsolete/member_array
new file mode 100644
index 0000000..1b948e6
--- /dev/null
+++ b/doc/obsolete/member_array
@@ -0,0 +1,30 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+    int member_array(mixed item, mixed *arr)
+    int member_array(mixed item, string arr)
+
+DESCRIPTION:
+    DO NOT USE THIS EFUN - IT HAS BEEN REMOVED FROM THE DRIVER!
+    Returns the index of the first occurence of item in array arr,
+  	or occurence of a character in a string. If not found, then -1
+  	is returned.
+        
+    If you want to search through an alist, use assoc() because
+    member_array() is good for unsorted but assoc() is faster for
+    sorted arrays.
+        
+    In LDMud this efun is temporarily a simulated efun until is finally
+    droppped.
+
+HISTORY
+    Superseeded by member().
+    Removed in LDMud 3.3.
+
+SEE ALSO:
+        alists(LPC), mappings(LPC), assoc(E), slice_array(E)
+
+11.11.2006 Zesstra
diff --git a/doc/obsolete/obsolete b/doc/obsolete/obsolete
new file mode 100644
index 0000000..f6e805e
--- /dev/null
+++ b/doc/obsolete/obsolete
@@ -0,0 +1,12 @@
+NAME
+    obsolete
+
+DESCRIPTION
+    This directory contains descriptions for features removed from
+    the game driver, since they can come in handy when reworking
+    old LPC code.
+
+SEE ALSO
+    applied(A), efun(E), master(M), concepts(C), lpc(LPC), driver(D)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/order_alist b/doc/obsolete/order_alist
new file mode 100644
index 0000000..32b1c20
--- /dev/null
+++ b/doc/obsolete/order_alist
@@ -0,0 +1,36 @@
+VERALTET
+SYNOPSIS
+        mixed *order_alist(mixed *keys, mixed *data, ...);
+        mixed *order_alist(mixed *list);
+
+BESCHREIBUNG
+        Diese Funktion erzeugt eine Alist.
+
+        Gibt man zwei oder mehr Argumente an, muss das erste Argument
+        ein Array von Keys enthalten, die nachfolgenden Argumente
+        sind Arrays von Datenelementen. Alle <data> Argumente muessen
+        die gleiche Groesse (also die gleiche Anzahl Elemente) wie <keys>
+        haben.
+
+        Gibt man nur ein Argument <list> an, so muss es sich dabei um ein
+        Array handeln, das als erste Element ein Array von Keys und als
+        weitere Elemente Arrays mit Datenelementen enthaelt. Alle Elemente
+        von <list> muessen die gleiche Groesse haben.
+
+        order_alist() liefert ein Array zurueck, das das sortierte <keys>
+        Array und die gleich sortierten <data> Arrays enthaelt. Auf die
+        <data> Arrays wird die gleiche Permutation wie auf das <key> Array
+        angewendet.
+
+        Die Komplexitaet ist O(n*lg(n)+n*m), wobei n die Anzahl Elemente im
+        <keys> Array darstellt, m die Anzahl <data> Arrays + 1.
+
+        Die Dimensionen der Arrays werden gegenueber LISP genau umgekehrt
+        verwendet, um ein schnelleres Suchen zu ermoeglichen.
+
+        Keys muessen vom Typ Integer, String oder Object sein. Die Typen
+        koennen auch gemischt sein.
+
+SIEHE AUCH
+        alists(LPC), mappings(LPC), insert_alist(E), assoc(E),
+        transpose_array(E)
diff --git a/doc/obsolete/parse_command b/doc/obsolete/parse_command
new file mode 100644
index 0000000..2039161
--- /dev/null
+++ b/doc/obsolete/parse_command
@@ -0,0 +1,72 @@
+int parse_command(string str, mixed source, string pattern, var1, var2 ...);
+
+Parses commands given in "str" against the pattern in "pattern" and
+returns 1 if it matches. "source" is either an object or an array of objects.
+This is essentially a 'hotted' sscanf and it has a similar syntax, although
+parse_command works on word basis where sscanf works on character basis.
+
+.ip str
+Given command
+.ip source
+Either an array holding the accessible objects, or
+an object from which to recurse and create
+the list of accessible objects, normally
+ob = environment(this_player()) .
+
+.ip pattern
+Parse pattern as list of words and formats:
+.nf
+		Syntax:
+			'word' 		obligatory text (One word)
+			[word]		optional text (One word)
+			/		Alternative marker
+			%o		Single item, object
+			%l		Single living object
+			%s		Any text (multiple words)
+			%w              Any word
+			%p		Preposition
+			%i		Any items
+			%d              Number 0- or tx(0-99)
+.fi
+Example string: " 'get' / 'take' %i " .
+Items as in %o and %i can on many forms, some examples:
+.nf
+			apple, two apples, twentyfirst apple
+			apples, all apples, all green apples, all green ones
+.fi
+
+.ip varN
+This is the list of result variables as in sscanf.
+One variable is needed for each %_.
+The return types of different %_ is:
+.nf
+	%o	Returns an object
+	%l	Returns an object
+	%s	Returns a string of words
+	%w      Returns a string of one word
+	%p	Can on entry hold a list of word in array
+		or an empty variable
+		Returns:
+		   if empty variable: a string
+		   if array: array[0]=matched word
+	%i	Returns a special array on the form:
+		[0] = (int) given numeric prefix
+		       =0: all or a pluralform given
+		       >0: numeral given: two, three, four...
+		       <0: order given: second, third ...
+		[1..n] (object) Objectpointers
+		       A list of the POSSIBLE objects that can match
+		       the given %i. No choosing of third or such.
+	%d      Returns a number
+.fi
+.lp
+Example:
+
+a=parse_command("take apple",environment(this_player()),
+	 " 'get' / 'take' %i ",items);
+
+HISTORY
+  LDMud 3.3.258 and LP "03.02.1@150" removed the compat-mode 
+  parse_command().
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/query_imp_port b/doc/obsolete/query_imp_port
new file mode 100644
index 0000000..b6a7c3d
--- /dev/null
+++ b/doc/obsolete/query_imp_port
@@ -0,0 +1,21 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+	int query_udp_port(void)
+
+DESCRIPTION:
+	Returns the port number that is used for the inter mud
+	protocol.
+
+HISTORY
+  LDMud 3.2.9 renamed this efun to query_udp_port(). This version
+  is available if the driver is compiled with USE_DEPRECATED.
+  Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SEE ALSO:
+	send_udp(E), receive_udp(M)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/receive_imp b/doc/obsolete/receive_imp
new file mode 100644
index 0000000..b42e78f
--- /dev/null
+++ b/doc/obsolete/receive_imp
@@ -0,0 +1,25 @@
+
+   **********************************************************
+  * ACHTUNG: WIRD VOM DRIVER NICHT MEHR GERUFEN! NICHT VERWENDEN! *
+   **********************************************************
+
+SYNOPSIS
+	void receive_udp(string host, string msg, int hostport)
+
+DESCRIPTION
+	Handle a received IMP message.
+
+	This function is called for every message received on the IMP
+	port. Usually it is passed on to some object that handles
+	inter mud communications.
+
+HISTORY
+	The 'hostport' argument was added in 3.2.1.
+  LDMud 3.2.9 renamed this method to receive_udp(); this old version
+  is supported if the driver is compiled with USE_DEPRECATED.
+  Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SEE ALSO
+	send_udp(E), query_udp_port(E)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/send_imp b/doc/obsolete/send_imp
new file mode 100644
index 0000000..c0f8e40
--- /dev/null
+++ b/doc/obsolete/send_imp
@@ -0,0 +1,23 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS:
+	int send_udp(string host, int port, string message)
+
+DESCRIPTION:
+	Sends The message in an UDP packet to the given host and port
+	number. Causes a privilege violation.
+	Returns 1 on success, 0 on failure.
+
+HISTORY
+  LDMud 3.2.9 renamed this efun to send_udp(), and also changed the
+  privilege violation string and the apply names. This old version
+  is available if the driver is compiled with USE_DEPRECATED.
+  Removed in LDMud 3.3 and LP "03.02.1@150".
+
+SEE ALSO:
+	receive_udp(M)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/set_auto_include_string b/doc/obsolete/set_auto_include_string
new file mode 100644
index 0000000..5a3823d
--- /dev/null
+++ b/doc/obsolete/set_auto_include_string
@@ -0,0 +1,28 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS
+        void set_auto_include_string (string arg)
+
+DESCRIPTION
+
+        The arg will be automatically included into every compiled LPC
+        object. This is useful to enforce global definitions, e.g.
+        ``#pragma combine_strings'' or ``#pragma strict_types''.  The
+        calling object needs to be privileged by the master object.
+
+        Note that the auto-include-string is cleared when the master
+        object is reloaded.
+
+HISTORY
+        LDMud 3.2.9 replaced this efun with driver hook H_AUTO_INCLUDE.
+          This old version is available if the driver is compiled
+          with USE_DEPRECATED.
+        Removed in LDMud 3.3.
+
+SEE ALSO
+        set_driver_hook(E), privilege_violation(M), pragma(LPC), master(M)
+
+29.10.2006 Zesstra
diff --git a/doc/obsolete/slice_array b/doc/obsolete/slice_array
new file mode 100644
index 0000000..620bdc9
--- /dev/null
+++ b/doc/obsolete/slice_array
@@ -0,0 +1,27 @@
+
+     **********************************************************
+     *  ACHTUNG: EFUN EXISTIERT NICHT MEHR! NICHT VERWENDEN!  *
+     **********************************************************
+
+SYNOPSIS
+        mixed *slice_array(mixed *array, int from, int to)
+
+DESCRIPTION
+	DO NOT USE THIS EFUN ANYMORE. IT HAS BEEN REMOVED FROM THE DRIVER.
+
+        Returns an array that is a slice of the array <arr> from the
+        index <from> to the index <to>.
+
+        This is the old notation for arr[from..to] and supported
+        only for hysterical raisins.
+
+        In LDMud this efun is temporarily a simulated efun until is finally
+        droppped.
+
+HISTORY
+        Removed in LDMud 3.3.
+
+SEE ALSO
+        [](E)
+
+11.11.2006 Zesstra
diff --git a/doc/pcmd/.readme b/doc/pcmd/.readme
new file mode 100644
index 0000000..8891327
--- /dev/null
+++ b/doc/pcmd/.readme
@@ -0,0 +1,4 @@
+Hier liegen Hilfen zu den Befehlen, ueber die man schon als normaler
+Spieler verfuegt.
+Seher- und Magierbefehle sind andernorts dokumentiert.
+
diff --git a/doc/pcmd/.synonym b/doc/pcmd/.synonym
new file mode 100644
index 0000000..0d25025
--- /dev/null
+++ b/doc/pcmd/.synonym
@@ -0,0 +1,67 @@
+ali alias
+ansi stty
+aus ausgaenge
+ausgang ausgaenge
+betrachte schau
+betr schau
+blind grafik
+blindheit grafik
+disconnect schlafe
+durchsuche suche
+ebene ebenen
+- ebenen
+; emote
+: emote
+erzaehle teile
+erzaehl teile
+farben stty
+farbe stty
+fluester fluestere
+frag frage
+gemote emote
+hist history
+histlen history
+histmin history
+^ history
+& history
+ignorier ignoriere
+i inventur
+inv inventur
+kanaele ebenen
+kanal ebenen
+keepalive telnet
+kkwho kkwer
+kletter klettere
+kwho kwer
+lass wirf
+lese lies
+md detail
+mruf mrufe
+passwd passwort
+password passwort
+punkte info
+quit ende
+rfluester rfluestere
+riech rieche
+rknuddel rknuddle
+ruf rufe
+rwink rwinke
+sag sage
+save speichern
+schlaf schlafe
+schnuppere rieche
+schnupper rieche
+score info
+steck stecke
+such suche
+terminal stty
+tm teile
+unali unalias
+untersuche schau
+unt schau
+vt100 stty
+werfe wirf
+werf wirf
+who wer
+zieh ziehe
+zueck zuecke
diff --git a/doc/pcmd/adverb b/doc/pcmd/adverb
new file mode 100644
index 0000000..2db3008
--- /dev/null
+++ b/doc/pcmd/adverb
@@ -0,0 +1,64 @@
+
+adverb
+------
+
+ KOMMANDO:
+    adverb <abk> <text>
+    adverb [ # | $ ]
+    adverb ? <abk>
+    adverb <abk>
+
+ ARGUMENTE:
+
+     <abk>
+        Eine Abkuerzung fuer ein Adverb
+     <text>
+        Der Text des Adverbs
+
+ BESCHREIBUNG:
+
+    Mit Adverbien koennen einige Verben genauer beschrieben werden. So
+    kann man einfach nur laecheln (laechel), gluecklich laecheln (laechel 
+    glue) oder scrollen (laechel stra und glue und stol und vers). Mit
+    einem / kann man an das Verb noch etwas eigenes einfuegen (jammere 
+    laut und /wie es nur Zauberer koennen).
+
+    Mit dem Kommando adverb kann man die definierten Adverbien anzeigen
+    lassen oder auch eigene Adverbien definieren. Die einzelnen Aufrufe
+    haben folgende Funktion:
+
+    1. Es wird ein neues Adverb mit der Abkuerzung <abk> zu dem Text <text>
+       definiert. Die Abkuerzung kann einen bis sechs Buchstaben lang sein.
+
+    2. Ohne weitere Parameter wird die komplette Adverbienliste angezeigt.
+       `adverb #' zeigt nur die selbstdefinierten Adverbien an, waehrend
+       `adverb $' nur die Standardadverbien anzeigt.
+
+    3. Es wird geprueft, ob es schon ein Adverb zu der Abkuerzung <abk> gibt.
+
+    4. Das Adverb mit der Abkuerzung <abk> wird aus der Liste geloescht. Dies
+       geht jedoch nur mit selbstdefiinierten Adverbien, nicht mit den
+       Standardadverbien!
+
+ BEISPIELE:
+    Ein Adverb `ratlos' mit der Abkuerzung `rat' wird geschaffen:
+
+    > adverb rat ratlos
+    OK, neues Adverb "ratlos" mit der Abkuerzung "rat".
+
+    Wir sehen nach, wie `rat' definiert ist:
+
+    > adverb ? rat
+    Die Abkuerzung rat gehoert zum Adverb:
+    ratlos
+
+    Wir loeschen es wieder:
+
+    > adverb rat
+    OK, Adverb "ratlos" geloescht.
+
+ SIEHE AUCH:
+    verben, adverbien
+
+ LETZTE AeNDERUNG:
+    Thu, 29.03.2008, 12:00:00 von Vanion
diff --git a/doc/pcmd/alias b/doc/pcmd/alias
new file mode 100644
index 0000000..7426f5c
--- /dev/null
+++ b/doc/pcmd/alias
@@ -0,0 +1,100 @@
+
+alias
+-----
+
+ KOMMANDO:
+    alias <alias> <befehl>
+    alias [ -a ] [ <alias> | <abk>* ]
+
+ ARGUMENTE:
+
+     <alias>
+        Der Name des Alias
+     <befehl>
+        Der Text, durch den das Alias ersetzt wird
+     <abk>
+        Die Abkuerzung eines Alias
+     -a
+        Option, um Aliase so auszugeben, wie man sie definiert
+
+ BESCHREIBUNG:
+    (Statt `alias' kann man auch `ali' verwenden!)
+
+    Mit diesem Kommando kann man sich Abkuerzungen fuer oft benutzte Befehle
+    erstellen sowie die schon definierten Aliase ansehen.
+
+    Definiert wird ein Alias mit dem ersten der obigen Kommandos. <alias> darf
+    dabei nur ein Wort umfassen, waehrend <befehl> beliebig lang sein darf.
+
+    Wenn Du von nun an das Alias eingibst, so fuehrst Du den ausgeschriebenen
+    Befehl wortwoertlich aus. Die folgenden Worte im ausgeschriebenen Befehl
+    haben jedoch eine besondere Bedeutung:
+
+     $*      Steht fuer alles, was auf das Alias folgt.
+     $<n>    Steht fuer das <n>te Wort nach dem Alias.
+     $<n>*   Steht fuer alle Worte ab dem <n>ten (einschliesslich).
+
+    Mit dem zweiten der obigen Kommandos kannst Du Dir die Aliase anzeigen
+    lassen. Ohne Parameter werden dabei saemtliche Aliase angezeigt, mit
+    `alias <alias>' wird die Definition von <alias> angezeigt, und mit `alias
+    <abk>*' werden alle Aliase gezeigt, die mit <abk> beginnen.
+
+    Nimmt man die Option -a hinzu (nur sinnvoll, wenn man sich Aliase anzeigen
+    laesst), so werden die Aliase so angezeigt, dass der Text cut-and-paste-
+    faehig ist, d.h. man kann den Text so als Befehl eingeben, wie er aus-
+    gegeben wird (z.B. um einem Zweitcharakter aliases des Erstcharakters zu
+    geben).
+
+    Soll das Zeichen "$" oder das Zeichen "&" im Alias vorkommen ("&" ist aus
+    historischen Gruenden genau wie "$" benutzbar), so muss es mit einem \
+    "gequoted" werden. Soll ein \ vorkommen, muss auch dieser gequoted werden
+    (\\).
+
+    Beginnt eine Befehlszeile mit einem "\", so werden die Aliase nicht
+    ersetzt. Wenn man als Aliasnamen einen auch sonst gueltigen Befehl
+    verwendet, kann man auf diese Weise auf den eigentlichen Befehl zugreifen.
+
+ BEISPIELE:
+    Erst mal ein paar (mehr oder weniger nuetzliche) Aliase anlegen:
+
+    > alias ul untersuche leiche
+    > alias tmh teile highlander mit $*
+    > alias tmz teile zook mit $*
+    > alias weg teile $1 mit Ich bin jetzt weg, \$ verdienen!
+
+    Wenn man sich die Aliase ansehen will:
+
+    > alias
+     tmh    = teile highlander mit $*
+     ul     = untersuche leiche
+     weg    = teile $1 mit Ich bin jetzt weg, \$ verdienen!
+
+    > alias weg
+     weg    = teile $1 mit Ich bin jetzt weg, \$ verdienen!
+
+    > alias tm*
+     tmh    = teile highlander mit $*
+     tmz    = teile zook mit $*
+
+    Und folgendermassen lassen sich die Aliase benutzen:
+
+    > tmh Hi Sheriff!
+    Du teilst Highlander mit: Hi Sheriff!
+
+    > tmz Oh mein Gott! :-)
+    Du teilst Zook mit: Oh mein Gott!
+
+    > weg boing
+    Du teilst Boing mit: Ich bin jetzt weg, $ verdienen!
+
+    > \weg Arbeiten...
+    Du bist jetzt als abwesend gekennzeichnet.
+
+    Das letzte Beispiel zeigt, wie man an einen Befehl kommt, der von einem
+    Alias "ueberladen" wurde.
+
+ SIEHE AUCH:
+    unalias, ersetzungsanzeige
+
+ LETZTE AeNDERUNG:
+    Thu, 11.03.1999, 15:30:00 von Highlander
diff --git a/doc/pcmd/angriffsmeldung b/doc/pcmd/angriffsmeldung
new file mode 100644
index 0000000..5f7ca54
--- /dev/null
+++ b/doc/pcmd/angriffsmeldung
@@ -0,0 +1,54 @@
+
+angriffsmeldung
+---------------
+
+ KOMMANDO:
+    angriffsmeldung an/ein | aus
+
+ BESCHREIBUNG:
+    Schaltet die Anzeige der Angriffsmeldung:
+    > Du greifst XYZ mit ABC an.
+    fuer jede Kampfrunde ein oder aus.
+    
+    Ist "angriffsmeldung" ausgeschaltet, bekommt man nur neue
+    Angriffsmeldungen (zB bei Waffenwechseln) von sich angezeigt.
+
+    Ist sie eingeschaltet, bekommt man in jeder Kampfrunde auch
+    die eigene Angriffsmeldung angezeigt.
+
+ BEISPIEL:
+    > angriffsmeldung an
+    Du siehst saemtliche Angriffsmeldungen von Dir.
+    > toete grille
+    Ok.
+    Du greifst die Grille mit blossen Haenden an.   <--
+    Du triffst eine Grille hart.
+    Die Grille greift Dich an.
+    Eine Grille kitzelt Dich am Bauch.
+    Du greifst die Grille mit blossen Haenden an.   <--
+    Du triffst eine Grille hart.
+    Die Grille greift Dich an.
+    Eine Grille verfehlt Dich.
+    Du greifst die Grille mit blossen Haenden an.   <--
+    Du triffst eine Grille sehr hart.
+
+    > angriffsmeldung aus
+    Du siehst nur neue Angriffsmeldungen von Dir.
+    > toete grille
+    Ok.
+    Du greifst die Grille mit blossen Haenden an.   <--
+    Du triffst eine Grille hart.
+    Die Grille greift Dich an.
+    Eine Grille verfehlt Dich.
+    Du triffst eine Grille hart.
+    Eine Grille verfehlt Dich.
+    Du triffst eine Grille hart.
+    Eine Grille verfehlt Dich.
+    Du triffst eine Grille hart.
+    > zueck dolch
+    Du zueckst den Trolldolch.
+    Du greifst die Grille mit dem Trolldolch an.    <--
+    Du kratzt eine Grille.
+
+ LETZTE AeNDERUNG:
+    1.September 2008 Gloinson
diff --git a/doc/pcmd/antworte b/doc/pcmd/antworte
new file mode 100644
index 0000000..d08dc15
--- /dev/null
+++ b/doc/pcmd/antworte
@@ -0,0 +1,22 @@
+
+antworte
+--------
+
+ KOMMANDO:
+    antworte [<wem>] <was>
+
+ ARGUMENTE:
+
+     <wem> (optional)
+        Das Lebewesen, dem Du antworten willst
+     <was>
+        Die Antwort
+
+ BESCHREIBUNG:
+    Man beantwortet eine Frage.
+
+ SIEHE AUCH:
+    frage, sage, gespraech
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/ausgaenge b/doc/pcmd/ausgaenge
new file mode 100644
index 0000000..ed55734
--- /dev/null
+++ b/doc/pcmd/ausgaenge
@@ -0,0 +1,28 @@
+
+ausgaenge
+---------
+
+ KOMMANDO:
+    ausgaenge [auto]
+    ausgang [auto]
+    aus [auto]
+
+ ARGUMENTE:
+
+     auto (optional)
+        Schaltet automatische Ausgangsanzeige aus/ein
+
+ BESCHREIBUNG:
+    Mit `ausgaenge' werden alle unmittelbar sichtbaren Ausgaenge aus dem Raum,
+    in dem man sich gerade befindet, ausgegeben. Dieser Befehl ist von Nutzen,
+    wenn man sich die Raumbeschreibung nicht durchlesen will. Mit `ausgang
+    auto' werden die unmittelbar sichtbaren Ausgaenge automatisch ausgegeben,
+    ein weiteres `ausgang auto' schaltet die automatische Anzeige wieder ab.
+
+    *Aber*: Es gibt Raeume, die versteckte Ausgaenge haben!
+
+ SIEHE AUCH:
+    schau (an), kurz, bewegung
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/ausruestung b/doc/pcmd/ausruestung
new file mode 100644
index 0000000..6c620d2
--- /dev/null
+++ b/doc/pcmd/ausruestung
@@ -0,0 +1,23 @@
+
+ausruestung
+--------
+
+ KOMMANDO:
+    ausruestung [-k] [farben]
+
+ ARGUMENTE:
+    farben  -  startet die Farbkonfiguration.
+    -k      -  gibt eine Kurzvariante ohne ASCII-Grafik aus.
+
+ BESCHREIBUNG:
+    Zeigt eine graphische Ausgabe der getragenen Ausruestung an.
+    Mittels des Arguments -k wird unabhaengig von 'grafik ein/aus' eine
+    kuerzere Version ohne ASCII-Grafik angezeigt.
+    Die Farbkonfiguration erlaubt, die Darstellung fuer ungewoehnlich
+    eingestellte terminals anzupassen.
+    
+ SIEHE AUCH:
+    behalte, inventur
+
+ LETZTE AeNDERUNG:
+    14.08.2009, Zesstra
diff --git a/doc/pcmd/avatar b/doc/pcmd/avatar
new file mode 100644
index 0000000..49b0d1f
--- /dev/null
+++ b/doc/pcmd/avatar
@@ -0,0 +1,45 @@
+
+avatar
+---------
+
+ KOMMANDO:
+    avatar [<uri>]
+
+ ARGUMENTE:
+
+     <uri> (optional)
+        Die neue URI zum Avatar
+
+ BESCHREIBUNG:
+    Spieler koennen sich mit diesem Kommando einen graphischen Avatar (d.h.
+    ein Bild) geben, welchen manche Clients anzeigen koennen.
+    Hierzu setzt der Spieler hier eine URI, welche auf ein Bild im Netz
+    verweist, welches ggf. vom Client anderer Spieler heruntergeladen wird.
+    Spieler koennen diese Avatar-URI mit dem Befehl 'avatar' anzeigen,
+    aendern und loeschen.
+    Ist das Argument dieses Befehls 'keine', so wird die Avatar-URI
+    geloescht.
+
+    Avatar-URIs anderer Spieler lassen sich mit 'finger -a' ausgeben.
+
+    Wird ein Charakter auf der MG-Homepage gefingert, wird das Bildchen ggf.
+    ebenfalls mit angezeigt. Hierzu muss allerdings eine URL angegeben werden,
+    welche mit http:// oder https:// beginnt.
+ 
+ BEMERKUNGEN:
+    Auf der MG-Homepage werden die Bilder immer auf eine Hoehe von 150 Pixeln
+    skaliert (Breite entsprechend proportional).
+    Um anderen Spielern nicht unnoetige Wartezeiten zu beschweren, sollte die
+    Groesse des Bildes nicht zu riesig sein. Bilder im Bereich von 20-200kB
+    sollten voellig ausreichen.
+
+ BEISPIELE:
+    avatar http://mg.mud.de/avatars/zesstra.png
+    avatar keine
+
+ SIEHE AUCH:
+    Persoenliche Details: email, url, icq, ort
+    Sonstiges:            finger
+
+ LETZTE AENDERUNG:
+    25.08.2011, Zesstra
diff --git a/doc/pcmd/behalte b/doc/pcmd/behalte
new file mode 100644
index 0000000..2264c19
--- /dev/null
+++ b/doc/pcmd/behalte
@@ -0,0 +1,29 @@
+
+behalte
+-------
+
+ KOMMANDO:
+    behalte [[<objekt>] [alles] [nichts]]
+
+ ARGUMENTE:
+
+     <objekt>
+        ein Objekt in Deinem Inventar
+
+     alles/nichts
+        wie es der Parameter schon sagt
+
+ BESCHREIBUNG:
+    Man kann mit diesem Befehl Gegenstaende markieren, so dass sie bei
+    `verkaufe alles' nicht mehr automatisch verkauft werden. Ist der
+    betreffende Gegenstand bereits markiert, wird die Markierung aufgehoben.
+    Saemtliche Sachen lassen sich mit `behalte alles' markieren, mit
+    `behalte nichts' kann man saemtliche Markierungen aufheben.
+
+    Ohne Argument aufgerufen zeigt der Befehl alle markierten Gegenstaende an.
+
+    Angezogene Ruestungen und gezueckte Waffen werden uebrigens automatisch
+    behalten.
+
+ LETZTE AeNDERUNG:
+    Tue, 27.10.1998, 16:00:00 von Rikus
diff --git a/doc/pcmd/bug b/doc/pcmd/bug
new file mode 100644
index 0000000..f440a91
--- /dev/null
+++ b/doc/pcmd/bug
@@ -0,0 +1,61 @@
+
+fehler
+------
+
+KOMMANDO:
+    bug <text>
+    bug <objekt>:<text>
+   
+ARGUMENTE:
+
+    <text>
+        Eine Mitteilung
+    <objekt>
+        Ein Referenzobjekt, das neben Dir liegt oder in Deinem Inventar ist
+
+BESCHREIBUNG:
+    Nobody's perfect. Dies gilt besonders fuer unsere Magier :)
+
+    Wer viel programmiert, kann auch viele Fehler machen. Die meisten Fehler
+    werden zwar schon in der Testphase gefunden und ausgemerzt, aber einige
+    Fehler bleiben bis zum Anschluss eines neuen Gebietes oder Objekts
+    unentdeckt.
+
+    Wenn Du einen solchen Fehler gefunden hast, sei es, weil ein 'Fehler im
+    Raum-Zeit-Gefuege' aufgetreten ist, sei es, weil irgendwelche seltsamen
+    Zeichen in der Beschreibung eines Objektes auftauchen, so kannst Du den
+    Verantwortlichen mit diesem Befehl davon unterrichten.
+    (Anmerkung: FiRZG werden auch automatisch protokolliert und zustaendigen
+     Magiern zur Verfuegung gestellt.)
+
+    Nicht zoegern, wenn Du einen Fehler findest, Fehlermitteilungen sind
+    *sehr* willkommen!
+    Solltest Du Dir einmal nicht sicher sein, ob es sich bei einem bestimmten
+    Verhalten um einen Fehler handelt oder nicht, zoegere nicht, bei dem
+    zustaedigen Magier oder auch bei Spielern und anderen eingeloggten Magiern
+    zu fragen.
+
+    Wenn Du einen Typo an einem bestimmten Objekt oder NPC entdeckst, kannst
+    Du dieses/diesen hinter einem Doppelpunkt angeben, damit der fuer das
+    Objekt verantwortliche Magier die Meldung bekommt.
+    Wenn das Objekt nicht zu finden ist, wird die ganze Eingabe als
+    Meldung aufgefasst.
+
+    Ein Hinweis auf die Regeln hier noch: Das Verschweigen und Ausnutzen von
+    Fehlern ist anderen Spielern gegenueber unfair. Bitte unterlasst das,
+    sondern berichtet die Fehler. Bugausnutzung zu eigenem Vorteil oder
+    anderer Nachteil ist ansonsten durchaus ein Fall fuer Sheriff. ;-)
+
+BEMERKUNG:
+    Der Befehl 'bug' bezieht sich bei Nichtangabe eines Bezugsobjektes
+    auf das letzte betrachtete Objekt. Hast Du also ein Objekt untersucht,
+    dann wird die Meldung fuer diese Objekt abgegeben. Hast Du aber den
+    Raum untersucht oder 'schau' eingegeben, wird die Meldung fuer Deinen
+    momentanen Raum abgesetzt.
+
+SIEHE AUCH:
+    typo, idee, detail, regeln, bezugsobjekt
+
+LETZTE AeNDERUNG:
+    12. Okt 2011 Gloinson
+
diff --git a/doc/pcmd/cicerone b/doc/pcmd/cicerone
new file mode 100644
index 0000000..922ebdf
--- /dev/null
+++ b/doc/pcmd/cicerone
@@ -0,0 +1,49 @@
+
+cicerone
+--------
+
+ KOMMANDO:
+    cicerone [<zustand>]
+
+
+ ARGUMENTE:
+
+    <zustand>
+      'ein', 'aus', 'status' oder 'zahl'
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kannst Du Dich selbst zu einem Cicerone, einem
+    Pfadfinder, Fremdenfuehrer oder Neulingsgehilfen ernennen oder
+    Dich wieder als solcher abmelden. Du weist damit dann explizit
+    darauf hin, dass Du bereit bist, Neulingen hier im MG zu helfen.
+
+    'zahl' macht Dich zum Cicerone, allerdings wirst Du neuen Spielern und bei
+    "kwer cicerone" nur dann angezeigt, wenn Du weniger als 'zahl' Minuten
+    idle bist. So muss man nicht dran denken, den Ciceronestatus
+    explizit auszuschalten, wenn man oefter laengerere Zeit idle ist.
+    'zahl' muss min. 1 und max. 1440 (1 Tag) sein.
+
+    'cicerone' ohne Parameter oder mit dem Parameter 'status' zeigt
+    Dir die aktuelle Einstellung an.
+
+    Bevor Du Dich allerdings darauf einlaesst solltest Du die
+    Hilfeseite zu cicerones lesen, dort werden die Aufgaben der
+    Cicerones naeher spezifiziert.
+
+    Cicerone kannst Du ab einem Spielerlevel von 20 werden.
+
+BEISPIELE:
+    cicerone ein -> Du bist permanent Cicerone (bis Ende/Reboot)
+    cicerone aus -> Du bist kein Cicerone
+    cicerone 5   -> Du bist Cicerone und wirst nur angezeigt, wenn Du weniger
+                    als 5min idle bist.
+
+BEMERKUNGEN:
+    Bitte benutzt die Zeitangabe, wenn ihr beim Idlen doch eher nicht auf den
+    Monitor schaut/erreichbar seid. Idle Cicerones sind wenig hilfreich.
+
+SIEHE AUCH:
+    einfuehrung, cicerones
+
+LETZTE AeNDERUNG:
+    11. Sep 2013 Gloinson
diff --git a/doc/pcmd/detail b/doc/pcmd/detail
new file mode 100644
index 0000000..f684d0b
--- /dev/null
+++ b/doc/pcmd/detail
@@ -0,0 +1,52 @@
+detail
+------
+
+KOMMANDO:
+    detail <text>
+    detail <objekt>:<text>
+    md <text>
+    md <objekt>:<text>
+   
+ARGUMENTE:
+
+    <text>
+        Eine Mitteilung
+    <objekt>
+        Ein Referenzobjekt, das neben Dir liegt oder in Deinem Inventar ist
+
+BESCHREIBUNG:
+    Es soll tatsaechlich immer noch passieren, das der ein oder andere
+    gewissenhafte Forscher, einen Raum oder ein Objekt, etc untersucht
+    und dabei so gruendlich vorgeht, das ihm doch wirklich ein Detail
+    ins Auge springt, welches NICHT beschrieben ist.
+
+    Die Zeiten, das Magier mit so einer offenkundigen Vergesslichkeit 
+    und Nachlaessigkeit ungeschoren davonkommen, sind aber entgueltig 
+    vorbei! Denn DIR ist ein Instrument in die Hand gelegt, mit dem Du
+    diese Magier 'erinnern' kannst.
+
+    <detail text> setzt eine Meldung ueber ein fehlendes Detail in 
+    das entsprechende Log-File des zustaendigen Magiers.
+
+    Bei <text> sollte es sich um einen kurzen, knappen Hinweis handeln,
+    der es dem Magier ermoeglicht, ueber die Notwendigkeit, das fehlende
+    Detail zu ergaenzen schnell entscheiden zu koennen.
+
+    Wenn Du ein Detail an bestimmten Objekt oder NPC vermisst, kannst Du
+    dieses/diesen hinter einem Doppelpunkt angeben, damit der fuer das
+    Objekt verantwortliche Magier die Meldung bekommt.
+    Wenn das Objekt nicht zu finden ist, wird die ganze Eingabe als
+    Meldung aufgefasst.
+
+BEMERKUNG:
+    Der Befehl 'detail' bezieht sich bei Nichtangabe eines Bezugsobjektes
+    auf das letzte betrachtete Objekt. Hast Du also ein Objekt untersucht,
+    dann wird die Meldung fuer diese Objekt abgegeben. Hast Du aber den
+    Raum untersucht oder 'schau' eingegeben, wird die Meldung fuer Deinen
+    momentanen Raum abgesetzt.
+
+SIEHE AUCH:
+    typo, idee, bug, bezugsobjekt
+
+LETZTE AeNDERUNG:
+    12. Okt 2011 Gloinson
diff --git a/doc/pcmd/ebenen b/doc/pcmd/ebenen
new file mode 100644
index 0000000..e77f966
--- /dev/null
+++ b/doc/pcmd/ebenen
@@ -0,0 +1,81 @@
+
+Die Ebenen
+==========
+    Die Ebenen sind eine Kommunikationsform, welche es einem ermoeglicht,
+    eine Unterhaltung ueber weite Strecken hinweg mit mehreren Personen zu
+    fuehren. Diese Form der Unterhaltung ist dem Rufen vorzuziehen, da es
+    fuer Personen, welche sich nicht auf der Ebene befinden, geraeuschlos
+    ist.
+
+    Betreten und Verlassen:
+     -<name>+
+        Ebene <name> einschalten.
+     -<name>-
+        Ebene <name> ausschalten.
+     ebenen neu <Name> <Text>
+        Erzeugt eine neue Ebene <Name> mit der Bezeichung <Text>. Dieses
+        Kommando ist erst ab Stufe 5 moeglich!
+     ebenen an
+        Schaltet die vorher ausgeschalteten Ebenen an.
+        Ist vorher kein `ebene aus' erfolgt, so werden alle nicht
+        eingeschalteten Ebenen eingeschaltet.
+     ebenen aus
+        Schaltet die Ebenen aus.
+
+    Reden auf einer Ebene:
+     -<name>'<Text>
+     -<name> <Text>
+        Sendet die Meldung <Text> auf die Ebene
+     -<name>:<Text>
+        Sendet das `emote' <Text> auf die Ebene
+     -<name>;<Text>
+        Erzeugt ein Genitiv-`emote'
+
+    Ebenennamen:
+     Man kann eine Ebene mit dem vollen Namen ansprechen:
+        "-anfaenger Hallo!"   oder   "-abenteuer Hilfe!"
+     es reicht aber auch schon ein eindeutig von anderen Ebenen abgrenzender
+     Teil des Namens:
+        "-an Hallo!"          oder   "-ab Hilfe!"
+
+     Gueltige Namen setzen sich zusammen aus den Buchstaben a-z, A-Z sowie
+     #$%&@<>-. Das # hat dabei eine Sonderaufgabe: Damit wird der
+     gleichnamige IRC-Channel erzeugt bzw. betreten.
+
+    Informationen und Ebenengeschichte:
+     -<name>?
+        Alle Teilnehmer der Ebene <name> anzeigen.
+     -<name>*<Laenge>
+        Die letzten <Laenge> Meldungen der Ebene <name> anzeigen. Wird die
+        Laenge weggelassen, ist Standard und Maximum 200.
+     -?
+        Alle verfuegbaren Ebenen anzeigen.
+     -!
+        Alle eingeschalteten Ebenen anzeigen.
+     ebenen beschreibung <name> <Text>
+        Veraendert die Beschreibung der Ebene <name>. Dies funktioniert nur,
+        wenn man der Meister der Ebene ist.
+
+    Abkuerzungen/Alias fuer Ebenennamen:
+     Es gibt einige vordefinierte Aliase fuer Ebenen (wie -a fuer -allgemein
+     oder -b fuer -abenteuer). Weitere kann man sich selbst definieren, wenn
+     Ebenennamen zu lang oder mit Sonderzeichen versehen sind:
+
+     ebenen a[bkuerzung]
+        Zeigt alle eingestellten Abkuerzungen an.
+     ebenen a[bkuerzung] s[tandard]
+        Stellt die Standardabkuerzungen ein und zeigt diese an.
+     ebenen <xx>=<Name>
+        Stellt die Abkuerzung <xx> fuer die Ebene <Name> ein. Leerzeichen
+	sind *nicht* zulaessig vor oder hinter dem Gleichheitszeichen '='!
+     ebenen <xx>=
+        Loescht die Abkuerzung <xx>
+     ebenen standard <Name>
+        Setzt die Ebene <Name> als Standard.
+	Damit wird automatisch bei "- <Text>" auf dieser Ebene geredet.
+
+ SIEHE AUCH:
+    irc, senderwiederholung
+
+ LETZTE AeNDERUNG:
+    1. Juli 2006 Ennox
diff --git a/doc/pcmd/email b/doc/pcmd/email
new file mode 100644
index 0000000..4ecb1d9
--- /dev/null
+++ b/doc/pcmd/email
@@ -0,0 +1,28 @@
+
+email
+-----
+
+ KOMMANDO:
+    email [<adresse>]
+
+ ARGUMENTE:
+
+     <adresse> (optional)
+        Die neue Adresse
+
+ BESCHREIBUNG:
+    Ohne Argumente gibt `email' die aktuelle EMail-Adresse aus. Mit Argument
+    wird sie auf die neue Adresse gesetzt.
+
+    Hinweis: eine gueltige EMail-Adresse erleichtert es _erheblich_ im
+    Bedarfsfall ein neues Passwort fuer den Charakter zu setzen. In vielen
+    Faellen ist dies ohne EMail-Adresse nicht moeglich.
+
+SIEHE AUCH:
+    Einstellungen:        emailanzeige
+    Persoenliche Details: email, url, icq, messenger, ort
+    Sonstiges:            finger, wer, kwer
+
+LETZTE AeNDERUNG:
+    12.09.2011, Zesstra
+
diff --git a/doc/pcmd/emailanzeige b/doc/pcmd/emailanzeige
new file mode 100644
index 0000000..02f52b2
--- /dev/null
+++ b/doc/pcmd/emailanzeige
@@ -0,0 +1,22 @@
+KOMMANDO:
+	emailanzeige [Personenkreis]
+
+ARGUMENTE:
+	[Personenkreis]
+	  Die Personen, die beim finger-Befehl Deine Email angezeigt
+	  bekommen.
+
+BESCHREIBUNG:
+	Mit diesem Befehl kann man festlegen, welche Personen beim
+	finger-Befehl die EMail-Adresse angezeigt bekommen, die Du mit
+	den Befehl "email" oder beim ersten Login angegeben hast.
+	Als Optionen stehen zur Verfuegung: alle, freunde, niemand.
+	Achtung: Magier bekommen die Email-Adresse immer angezeigt,
+	auch dann, wenn man "emailanzeige niemand" eingibt.
+
+SIEHE AUCH:
+        Persoenliche Details: email, url, icq, messenger, ort
+        Sonstiges:            finger
+
+----------------------------------------------------------------------------
+Last modified: Mon Okt 16 20:32:44 2000 by Silvana
diff --git a/doc/pcmd/emote b/doc/pcmd/emote
new file mode 100644
index 0000000..e1df4ed
--- /dev/null
+++ b/doc/pcmd/emote
@@ -0,0 +1,64 @@
+
+emote
+-----
+
+ KOMMANDO:
+    emote <text1>[#<name>#<text2>[#<text3>]]
+    :<text1>[#<name>#<text2>[#<text3>]]
+    ;<text1>[#<name>#<text2>[#<text3>]]
+
+ ARGUMENTE:
+
+     <text1>, <text2>, <text3>
+        Zeichenketten
+     <name>
+        Name eines Lebewesens im gleichen Raum wie Du
+
+ BESCHREIBUNG:
+    Gibt den Text <text1> an alle im Raum aus. Dein Name wird dem Text
+    vorangestellt. Verwendet man die `;'-Form, wird Dein Name im Genitiv
+    gewaehlt.
+
+    Werden ein Name und weiterere Texte angegeben, so wird an <name> statt des
+    ersten Textes <text2> ausgegeben (ebenfalls mit Deinem Namen
+    vorangestellt) und an Dich <text3> (ohne Namen).
+
+    Das `emote'-Kommando beherrscht man uebrigens erst, nachdem man die
+    Amaryllis-Quest geloest hat.
+
+    Ist man nur einfacher Spieler (also weder Seher noch Magier), so wird auch
+    noch ein `> ' vor Namen und Text gesetzt.
+
+ BEISPIELE:
+    Angenommen, Wargon und Saphis stehen mit einigen anderen Leuten zusammen
+    in einem Raum.
+
+    Hier ein paar Eingaben von Wargon mit den entsprechenden Ausgaben bei den
+    Anwesenden:
+
+    > :niest.
+
+    |Wargon:| Wargon niest.
+    |Saphis:| Wargon niest.
+    |andere:| Wargon niest.
+
+    > :aergert Saphis.#saphis#aergert Dich.
+
+    |Wargon:| Wargon aergert Saphis.
+    |Saphis:| Wargon aergert Dich.
+    |andere:| Wargon aergert Saphis.
+
+    > :aergert Saphis.#saphis#aergert Dich.#Du aergerst Saphis.
+
+    |Wargon:| Du aergerst Saphis.
+    |Saphis:| Wargon aergert Dich.
+    |andere:| Wargon aergert Saphis.
+
+    Wie man sieht, lassen sich mit der letzten Form und dem alias-Befehl
+    eigene Befehle herstellen (allerdings muss man auf Adverbien verzichten).
+
+ SIEHE AUCH:
+    verben, (ab hier nur fuer Seher) remote, echo
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/ende b/doc/pcmd/ende
new file mode 100644
index 0000000..03dcc99
--- /dev/null
+++ b/doc/pcmd/ende
@@ -0,0 +1,28 @@
+
+ende
+----
+
+ KOMMANDO:
+    ende
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Man verlaesst das Spiel sofort und auf der Stelle (siehe aber auch unter
+    BEMERKUNGEN). Alle Gegenstaende werden fallen gelassen, nur autoladene
+    Gegenstaende (z.B. Geld) behaeltst Du ueber das Spielende hinaus. Wenn Du
+    Dich wieder einloggst, startest Du an Deinem lokalen Startpunkt.
+
+    Der aktuelle Spielstand wird vor dem Verlassen automatisch abgespeichert.
+
+ BEMERKUNGEN:
+    Da einige Leute diesen Befehl gebrauchten, um sich im Kampf Vorteile zu
+    verschaffen, funktioniert der `ende'-Befehl erst zwei Minuten nach der
+    letzten Kampfhandlung.
+
+ SIEHE AUCH:
+    speichern, schlafe (ein)
+
+ LETZTE AeNDERUNG:
+    Tue, 02.09.1997, 15:05:00 von Wargon
diff --git a/doc/pcmd/entgifte b/doc/pcmd/entgifte
new file mode 100644
index 0000000..f3edf65
--- /dev/null
+++ b/doc/pcmd/entgifte
@@ -0,0 +1,25 @@
+
+entgifte
+--------
+
+ KOMMANDO:
+    entgifte [<ziel>]
+
+ ARGUMENTE:
+
+     <ziel> (optional)
+        Das zu entgiftende Lebewesen.
+
+ BESCHREIBUNG:
+    Eigentlich ist dies eine Anrufung des Heiligen Ordens. Allerdings koennen
+    auch Angehoerige anderer Gilden diese Anrufung lernen, nachdem sie das
+    Abenteuer "Verhaengnis des Duesterwalds" geloest haben.
+
+    Falls das <ziel> vergiftet ist, so wird seine Vergiftung gelindert oder
+    gar geheilt (je nach Guete der Anrufung). Ohne Angabe von <ziel> wird
+    die eigene Vergiftung gelindert oder geheilt.
+
+    Die Anwendung der Anrufung kostet 30 Magiepunkte.
+
+ LETZTE AENDERUNG:
+    Fre, 26.11.1999, 15:16:00 von Tilly
diff --git a/doc/pcmd/ersetzungsanzeige b/doc/pcmd/ersetzungsanzeige
new file mode 100644
index 0000000..c2131e2
--- /dev/null
+++ b/doc/pcmd/ersetzungsanzeige
@@ -0,0 +1,30 @@
+
+ersetzungsanzeige
+-----------------
+
+ KOMMANDO:
+    ersetzungsanzeige <stufe>
+
+ ARGUMENTE:
+
+     <stufe>
+        0, 1 oder 2
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kann man sich die Ersetzungen bei der Benutzung von
+    Befehlsgeschichte und Aliasen anzeigen lassen.
+
+    `ersetzungsanzeige 1' zeigt dabei die Ersetzungen der History an und
+    `ersetzungsanzeige 2' die Ersetzungen von History und Aliasen.
+
+    Mit `ersetzungsanzeige 0' schaltet man die Anzeige wieder ab.
+
+    Dieser Befehl ist hauptsaechlich dann nuetzlich, wenn man sich davon
+    ueberzeugen will, ob eine anscheinend richtige Syntax z.B. von einem
+    laengst vergessenen Alias verhunzt wird.
+
+ SIEHE AUCH:
+    alias, history
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/erwarte b/doc/pcmd/erwarte
new file mode 100644
index 0000000..7a65b96
--- /dev/null
+++ b/doc/pcmd/erwarte
@@ -0,0 +1,52 @@
+KOMMANDO:
+     erwarte [-u|<Spieler>]
+     erwarte [<Spieler>] wegen [<Grund>]
+     erwarte [an|ein|aus]
+
+ARGUMENTE:
+     <Spieler>
+       Hier wird angegeben, welchen Spieler man erwartet.
+     <Grund>
+       (1) Textmeldung, welche beim Einloggen des erwarteten Spielers kommt.
+       (2) Bei Angabe von 'nichts' oder 'loeschen' wird <Grund> geloescht.
+     -u
+       Unter Verwendung dieser Option werden die erwarteten Spielernamen
+       in der chronologischen Reihenfolge ihres Eintrags in der
+       Erwarte-Liste ausgegeben. Ohne diese Option wird die Liste
+       alphabetisch geordnet praesentiert.
+
+BESCHREIBUNG:
+     Die Ankunft und das Weggehen der Spieler, die mit `Erwarte <Name>'
+     markiert wurden, werden mit einer Meldung und einem Piepton
+     bestaetigt.
+     `erwarte' ohne Argument zeigt die Liste der zur Zeit erwarteten
+     Spieler in alphabetischer Reihenfolge an. Die Option '-u' kann die
+     Liste auch in der chronologischen Reihenfolge ihrer Eintraege
+     anzeigen.
+     Ein erneuter Aufruf von `erwarte <Spieler>' entfernt <Spieler>
+     wieder aus der Liste.
+
+     Erwartet man einen Spieler aus einem ganz bestimmten Grund, und
+     moechte man bei dessen Einloggen auch daran erinnert werden, was man
+     von ihm wollte, so ist `erwarte <Spieler> wegen <Grund>' nuetzlich.
+     Beim Einloggen des Spielers <Name> erscheint eine Textmeldung mit
+     Inhalt <Grund>.
+     Mit `erwarte wegen' ist diese spezielle Erwarteliste abfragbar und
+     mit `erwarte <Spieler> wegen', ob und aus welchen Grund man Spieler
+     <Spieler> erwartet. Wird als <Grund> `nichts' oder `loeschen'
+     angegeben, entfernt man den Eintrag wieder.
+     Spieler koennen 10 Textmeldungen setzen, Seher 20, Magier 40 und
+     Erzmagier 80.
+     Mit 'erwarte aus' kann man Erwarte-Meldungen voruebergehend
+     deaktivieren. 'erwarte ein' bzw. 'erwarte an' aktiviert es dann
+     wieder, ohne dass die Liste der Erwarteten verloren geht.
+
+BEMERKUNGEN:
+     Der von 'erwarte' erzeugte Piepton kann sich in gewissen
+     RL-Umgebungen recht stoerend auswirken. Dagegen kann man sich jedoch
+     mittels des Kommandos 'ton AUS' wehren.
+
+SIEHE AUCH:
+     ton, inform
+
+6.Feb 2016 Gloinson
diff --git a/doc/pcmd/erwidere b/doc/pcmd/erwidere
new file mode 100644
index 0000000..14cae37
--- /dev/null
+++ b/doc/pcmd/erwidere
@@ -0,0 +1,28 @@
+
+erwidere
+--------
+
+ KOMMANDO:
+    erwidere <text>
+
+ ARGUMENTE:
+
+     <text>
+        Der zu erwidernde Text
+
+ BESCHREIBUNG:
+    Wenn Dir jemand etwas mitteilt, kannst Du mit diesem Kommando direkt
+    darauf antworten, ohne den Namen des Absenders eingeben zu muessen.
+
+    Aber aufgepasst: Du erwiderst wirklich nur die *letzte* Mitteilung! Wenn
+    Du also den zu erwidernden Text schreibst, und jemand anderes teilt Dir in
+    der Zwischenzeit auch noch etwas mit, so bekommt *er* die Antwort
+    zugeschickt!
+    
+    Der gleiche Effekt kann mit "teile , mit <text>" erzielt werden.
+
+ SIEHE AUCH:
+    teile (mit)
+
+ LETZTE AeNDERUNG:
+    Thu, 08.02.2007, 18:23:00 von Muadib
diff --git a/doc/pcmd/fehler b/doc/pcmd/fehler
new file mode 100644
index 0000000..98a6675
--- /dev/null
+++ b/doc/pcmd/fehler
@@ -0,0 +1,30 @@
+fehler
+------
+
+KOMMANDO:
+    fehler
+
+BESCHREIBUNG:
+    Wer viel programmiert, kann auch viele Fehler machen. Die meisten
+    Probleme werden zwar schon vor dem Anschluss des neuen Gebietes oder
+    Objektes entdeckt, aber einiges bleibt bis zum Anschluss unentdeckt.
+    Manche Idee ist dem Magier auch nicht so klar, wie dem Spieler, der
+    spaeter einmal durchlaeuft.
+
+    Zum Melden dieser Fehler gibt es vier Kommandos, die durch das Kommando
+    'fehler' gelistet werden:
+    * 'bug'    - Meldung eines Fehlers / einer Fehlfunktion
+    * 'typo'   - Meldung eines Tippfehler
+    * 'detail' - Vorschlaege, ein fehlendes Detail zu ergaenzen
+    * 'idee'   - Vorschlaege mit Ideen zur Erweiterung
+
+BEMERKUNG:
+    Frueher hatte dieses Kommando die gleiche Funktionalitaet wie 'bug',
+    zu viele Spieler haben aber all die verschiedenen Fehlerarten nur als
+    'fehler' abgesetzt, daher wurde das geaendert.
+
+SIEHE AUCH:
+    bug, typo, idee, detail
+
+LETZTE AeNDERUNG:
+    12. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/pcmd/finger b/doc/pcmd/finger
new file mode 100644
index 0000000..b5ce936
--- /dev/null
+++ b/doc/pcmd/finger
@@ -0,0 +1,55 @@
+
+finger
+------
+
+ KOMMANDO:
+    finger [-n] [-p] [-s] [-v] [-a] <name> | <name>@<mud>
+
+ ARGUMENTE:
+
+     <name>
+        Name eines Spielers
+     <mud>
+        Name eines MUDs aus der `muds'-Liste
+
+ BESCHREIBUNG:
+    Gibt ein paar zusaetzliche Informationen ueber den Spieler <name> aus.
+    Hierbei ist einerlei, ob der Spieler eingeloggt, netztot oder ausgeloggt
+    ist.
+
+    Seher und Magier koennen sich eine .plan-Datei anlegen, deren Inhalt unter
+    den Spielerinformationen ausgegeben wird. Manchmal moechte man diese
+    jedoch gar nicht sehen. In diesem Fall kann man die Ausgabe des .plan mit
+    `-n' unterdruecken.
+
+    Moechte man die .plan-Datei defaultmaessig unterdruecken, kann man sich
+    ein Alias der Form
+
+    'alias finger finger -n $*'
+
+    erstellen.
+
+    Moechte man dann trotzdem einmal einen .plan sehen, kann man dies mit der
+    Option `-p' bewerkstelligen.
+
+    Mit dem Schalter '-a' kann man sich zusaetzlich zu den anderen Angaben
+    auch die Avatar-URI des Spielers anzeigen lassen (natuerlich nur, sofern
+    er auch eine gesetzt hat).
+
+    Gibt man den Schalter `-s' an, so wird das Verwandtschaftsverhaeltnis des
+    Magiers ausgegeben.
+
+    Im Gegensatz zu Spielern sehen Magier auch, wenn ein Magier zuletzt
+    unsichtbar anwesend war. Um dieses Verhalten zu unterdruecken, ist die
+    Option `-v' vorhanden.
+
+    Ist der Spieler als abwesend gekennzeichnet, zeigt 'finger' die
+    Wegmeldung des Spielers an.
+    Will man nur wissen, ob ein Spieler als abwesend gekennzeichnet ist, 
+    kann man den Befehl 'wegmeldung' benutzen.
+
+ SIEHE AUCH:
+    avatar, email, url, messenger, icq, ort, weg, (wegmeldung)
+
+ LETZTE AeNDERUNG:
+    27.05.2015, Boing
diff --git a/doc/pcmd/fluestere b/doc/pcmd/fluestere
new file mode 100644
index 0000000..7e73e4a
--- /dev/null
+++ b/doc/pcmd/fluestere
@@ -0,0 +1,25 @@
+
+fluestere
+---------
+
+ KOMMANDO:
+    fluestere <wem> zu <text>
+
+ ARGUMENTE:
+
+     <wem>
+        Das Lebwesen, dem Du etwas zufluestern willst
+     <text>
+        Der zu fluesternde Text
+
+ BESCHREIBUNG:
+    Gibt eine Mitteilung an das angegebene Lebewesen (meistens ein Spieler),
+    die niemand anderes hoeren kann. Das Lebewesen muss sich im selben Raum
+    befinden.
+
+ SIEHE AUCH:
+    sage, frage, antworte, teile (mit), gespraech, rufe, ebenen, weg,
+    ignoriere, kobold
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/frage b/doc/pcmd/frage
new file mode 100644
index 0000000..da9681b
--- /dev/null
+++ b/doc/pcmd/frage
@@ -0,0 +1,31 @@
+
+frage
+-----
+
+ KOMMANDO:
+    frage <npc> nach <was>
+    frage <spieler> <was>
+
+ ARGUMENTE:
+
+     <npc>
+        Ein NPC, von dem man etwas wissen will
+     <spieler>
+        Ein Spieler, von dem man etwas wissen will
+     <was>
+        Die Frage an sich
+
+ BESCHREIBUNG:
+    Man befragt ein Monster oder einen Spieler ueber irgendetwas. Nicht alle
+    Monster haben auch Antworten einprogrammiert und bei Spielern kann man
+    erst recht nicht wissen, ob sie antworten.
+
+    Wenn ein NPC auf eine Deiner Meinung nach sinnvolle Frage keine sinnvolle
+    Antwort weiss, kannst Du seinem Programmierer mit dem idee-Befehl einen
+    kleinen Denkanstoss geben.
+
+ SIEHE AUCH:
+    antworte
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/gespraech b/doc/pcmd/gespraech
new file mode 100644
index 0000000..fc877c8
--- /dev/null
+++ b/doc/pcmd/gespraech
@@ -0,0 +1,25 @@
+
+gespraech
+---------
+
+ KOMMANDO:
+    gespraech 
+
+ ARGUMENTE:
+    <-s> (optional, auch: -still, -silent geht)
+
+ BESCHREIBUNG:
+    Alle weiteren Eingaben werden genauso unmittelbar an alle Spieler im
+    selben Raum weitergegeben, als wenn man den sage-Befehl benutzt haette.
+
+    Wird als Argument '-s' (fuer still/silent) angegeben, werden Deine Eingaben
+    nicht in Form von "Du sagst: ..." Dir selber angezeigt.
+
+    Nach Eingabe von "**" koennen wieder beliebige Befehle eingegeben werden.
+
+ SIEHE AUCH:
+    sage, frage, antworte, fluestere, teile (mit), rufe, ebenen, weg,
+    ignoriere, kobold
+
+ LETZTE AeNDERUNG:
+    10.08.2009, Zesstra
diff --git a/doc/pcmd/gib b/doc/pcmd/gib
new file mode 100644
index 0000000..ff8e3f9
--- /dev/null
+++ b/doc/pcmd/gib
@@ -0,0 +1,23 @@
+gib
+---
+
+ KOMMANDO:
+    gib <ziel> <objekt>
+
+ ARGUMENTE:
+
+    <objekt>
+        Der wegzugebende Gegenstand
+    <ziel>
+        Ein Spieler oder NPC im gleichen Raum
+
+ BESCHREIBUNG:
+    Du gibst den Gegenstand <objekt> an den Spieler oder NPC <ziel>. Man
+    sollte sich genau ueberlegen, wem man was gibt, gerade NPCs geben ungern
+    Sachen einfach zurueck.
+
+ SIEHE AUCH:
+    nimm [aus], hole (aus)
+
+ LETZTE AeNDERUNG:
+    14. Okt 2011 Gloinson
diff --git a/doc/pcmd/grafik b/doc/pcmd/grafik
new file mode 100644
index 0000000..305860d
--- /dev/null
+++ b/doc/pcmd/grafik
@@ -0,0 +1,24 @@
+
+grafik
+------
+
+ KOMMANDO:
+     grafik [ein|aus]
+
+ BESCHREIBUNG:
+     Mit "grafik aus" kannst du die Anzeige von ASCII-Grafiken fuer
+     Dich unterdruecken. Nach dem Befehl "grafik ein" werden Dir solche
+     Grafiken wieder angezeigt.
+     Rufst Du den Befehl ohne Parameter auf, dann wird Dir angezeigt,
+     ob Du Grafiken sehen moechtest oder nicht.
+
+     *** ACHTUNG ***
+     Dieser Befehl wurde am 18. Oktober 2005 eingefuehrt. Daher
+     ist davon auszugehen, dass die Einstellung erst nach und nach
+     an den jeweiligen Stellen im MorgenGrauen beruecksichtigt wird.
+     Wenn Dir eine solche Stelle auffaellt, wo es noch nicht 
+     beruecksichtigt wird, dann schreibe doch bitte eine E-Mail an
+     Zook, Ennox oder Bugfix
+     
+ LETZE AENDERUNG:
+     2009-09-10 von Zook
diff --git a/doc/pcmd/hilfe b/doc/pcmd/hilfe
new file mode 100644
index 0000000..fffbffb
--- /dev/null
+++ b/doc/pcmd/hilfe
@@ -0,0 +1,79 @@
+
+hilfe
+-----
+
+ KOMMANDO:
+    hilfe [[gilde <gildenname>] <thema>]
+
+ ARGUMENTE:
+
+     <thema>
+        Das Thema oder Kommando, zu dem Du eine Hilfe haben moechtest
+     <gildenname>
+        Name einer Gilde (fuer gildenspezifische Seiten)
+
+ BESCHREIBUNG:
+    Gibt Hilfsseiten ueber die Befehle und einige allgemeine Themen aus. Ohne
+    <thema> wird eine Seite mit den Themen gezeigt, zu denen es Hilfsseiten
+    gibt.
+
+    Bei Hilfen zu Zauberspruechen gibt es eine Besonderheit zu beachten:
+    Einige Gilden haben Zaubersprueche mit gleichen Namen, aber etwas
+    unterschiedlicher Wirkung, unterschiedlichen Kosten oder Lernbedingungen.
+    Deshalb kann man noch angeben, von welcher Gilde man die entsprechende
+    Hilfeseite haben moechte. Standardmaessig lassen sich nur die Seiten der
+    Gilde erreichen, in der man gerade Mitglied ist.
+
+    Es gibt (noch?) nicht von allen Gilden Hilfeseiten zu den einzelnen
+    Spruechen und Faehigkeiten. Momentan sind dies nur die Abenteurer,
+    Bierschuettler, Karateka und Kleriker. Es wird jedoch auch auf den
+    jeweiligen Gildenhilfeseiten noch darauf hingewiesen.
+
+ SCHREIBWEISEN:
+    Viele Kommandos verstehen mehrere unterschiedliche Aufrufe. Ausserdem sind
+    manche Bestandteile der Eingabezeile fest vorgegeben, andere dagegen
+    variabel. Um nicht jeden moeglichen Aufruf in eine eigene Zeile packen zu
+    muessen, gibt es folgende Konventionen in der Schreibweise der
+    KOMMANDO-Zeile:
+
+     fest
+        Feste Bestandteile, die auch genau so eingegeben werden.
+     <variabel>
+        Variable Bestandteile. Sie geben an, was genau der Befehl bewirken
+        soll.
+     [ Optionen ]
+        Teile in eckigen Klammern sind optional.
+     Alter | Nativen
+        Alternative Formulierungen werden durch einen senkrechten Strich
+        voneinander getrennt.
+
+    Als Beispiel mag die Kommandozeile der `hilfe'-Befehls selbst herhalten:
+    hilfe [[gilde <gildenname>] <thema>]
+
+    Aufgedroeselt wuerde das so aussehen:
+
+     hilfe
+     hilfe <thema>
+     hilfe gilde <gildenname> <thema>
+
+    Die aeusseren eckigen Klammern zeigen an, dass saemtliche Parameter
+    optional sind und weggelassen werden koennen. Dies entspricht dem oberen
+    Aufruf.
+
+    Die inneren eckigen Klammern zeigen an, dass auch der Gildenteil
+    weggelassen werden kann. Man erhaelt dann eine Hilfe zu allgemeinen Themen
+    (der mittlere Aufruf). Das <thema> selbst ist dabei variabel.
+
+    Die dritte Zeile schliesslich besteht aus dem festen Bestandteil "gilde"
+    sowie den Variablen <gildenname> und <thema>.
+
+    Anstelle der doppelten eckigen Klammern haette man das Ganze auch als
+    Alternative formulieren koennen:
+
+    hilfe [<thema> | gilde <gildenname> <thema>]
+
+ SIEHE AUCH:
+    gilden
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/history b/doc/pcmd/history
new file mode 100644
index 0000000..7574363
--- /dev/null
+++ b/doc/pcmd/history
@@ -0,0 +1,75 @@
+
+history
+-------
+
+ KOMMANDO:
+    history (oder: hist)
+    &<nr>
+    &<text>
+    histlen <n>
+    histmin <n>
+    ^<falsch>^<richtig>^
+
+ ARGUMENTE:
+
+     <nr>
+        Die Nummer eines Befehls in der History
+     <text>
+        Der Anfang eines Befehls in der History
+     <n>
+        Eine Zahl
+     <falsch>
+        Fehlerhafter Text
+     <richtig>
+        Korrigierter Text
+
+ BESCHREIBUNG:
+    Die letzten Befehle, die Du eingegeben hast, werden gespeichert. Auf diese
+    gespeicherten Befehle kannst Du zugreifen, ohne sie komplett neu eingeben
+    zu muessen:
+
+    `&<nr>' fuehrt den Befehl mit Nummer <nr> aus, soweit er sich noch in der
+    Befehlsgeschichte befindet. Man kann jedoch auch negative Nummern
+    verwenden: `&-0' fuehrt den letzten Befehl nochmal aus, `&-1' den
+    vorletzten, und so weiter.
+
+    `&<text>' fuehrt den letzten Befehl aus, der mit <text> anfaengt (sofern
+    vorhanden).
+
+    Wird eine identische Befehlszeile mehrfach hintereinander angegeben, so
+    erscheint sie nur nur ein einziges Mal in der Befehlsgeschichte (auch,
+    wenn die Wiederholungen ueber `&-0' erfolgten).
+
+    Die komplette Befehlsgeschichte kannst Du Dir mit `history' (oder
+    abgekuerzt `hist') anzeigen lassen. Der `history'-Befehl selbst erscheint
+    jedoch nicht in der Geschichte.
+
+    Die Laenge der Befehlsgeschichte betraegt standardmaessig 40 Befehle, dies
+    kann man aber mit dem Befehl `histlen <n>' aendern. Die voreingestellten
+    40 Befehle bilden allerdings das Maximum, welches nicht ueberschritten
+    werden kann.
+
+    Normalerweise landet *jeder* Befehl in der Befehlsgeschichte. Will man
+    aber z.B. die Bewegungsbefehle nicht in der History haben, so kann man mit
+    `histmin <n>' festlegen, ab welcher Laenge die Befehle aufgenommen werden.
+    Mit `histmin 4' werden also nur Befehlszeilen mit mindestens vier
+    Buchstaben Laenge aufgenommen. Dies stellt uebrigens eine sinnvolle
+    Groesse dar, da bei kuerzeren Zeilen die Eingabe des &-Kommandos mehr
+    Aufwand darstellt als die Neueingabe des Befehls.
+
+    Tippfehler im letzten eingegebenen Befehl lassen sich wie folgt
+    korrigieren: `^<falsch>^<richtig>^'. Das letzte ^ kann man auch weglassen.
+    Zum Beispiel:
+
+    > tm jpf Hi!
+    Kein solcher Spieler!
+    > ^jpf^jof
+    Du teilst Jof mit: Hi!
+
+    Hier haette uebrigens auch `^p^o' genuegt.
+
+ SIEHE AUCH:
+    ersetzungsanzeige, alias
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/hole b/doc/pcmd/hole
new file mode 100644
index 0000000..5b55e5d
--- /dev/null
+++ b/doc/pcmd/hole
@@ -0,0 +1,23 @@
+
+hole (aus)
+----------
+
+ KOMMANDO:
+    hole <objekt> aus <behaelter>
+
+ ARGUMENTE:
+
+     <objekt>
+        Der gewuenschte Gegenstand
+     <behaelter>
+        Ein Behaelter
+
+ BESCHREIBUNG:
+    Mit diesem Kommando kannst Du Gegenstaende aus Behaeltern nehmen.
+    Allerdings muss der Behaelter dazu geoeffnet sein.
+
+ SIEHE AUCH:
+    stecke, nimm [aus]
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/icq b/doc/pcmd/icq
new file mode 100644
index 0000000..f28b0e4
--- /dev/null
+++ b/doc/pcmd/icq
@@ -0,0 +1,21 @@
+
+icq
+---
+
+ KOMMANDO:
+    icq [<nummer>]
+
+ ARGUMENTE:
+
+     <nummer> (optional)
+        Die neue Nummer
+
+ BESCHREIBUNG:
+    Wenn Du eine ICQ-Nummer hast kannst Du sie mit diesem
+    Kommando eintragen.
+    Ohne <nummer> wird die aktuelle Nummer angezeigt, ist sie 'keine',
+    so wird die Nummer geloescht.
+
+ SIEHE AUCH:
+    Persoenliche Details: email, url, messenger, ort
+    Sonstiges:            finger
diff --git a/doc/pcmd/idee b/doc/pcmd/idee
new file mode 100644
index 0000000..41342c6
--- /dev/null
+++ b/doc/pcmd/idee
@@ -0,0 +1,45 @@
+idee
+----
+
+KOMMANDO:
+    idee <text>
+    idee <objekt>:<text>
+
+ARGUMENTE:
+
+    <text>
+        Eine Mitteilung
+    <objekt>
+        Ein Referenzobjekt, das neben Dir liegt oder in Deinem Inventar ist
+
+BESCHREIBUNG:
+    Speichert eine Mitteilung ueber eine Idee, wie man das Spiel verbessern
+    oder verschoenern kann. Beispiele waeren weitere Details in Raeumen oder
+    an Objekten; Vorschlaege zur Verbesserung der Syntax an bestimmten
+    Stellen; Ideen, wie man das Ambiente eines Raumes noch verbessern kann.
+    Die Magier werden versuchen, alle Mitteilungen in die Tat umzusetzen,
+    soweit sie ihnen sinnvoll erscheinen :)
+
+    Wenn Du einen Tippfehler gefunden hast, so kannst Du den Verantwortlichen
+    mit diesem Befehl davon unterrichten.
+    Alle Vorschlaege sind *herzlich willkommen*; zoegere also nicht, wenn Du
+    eine gute Idee hast!
+
+    Wenn Du eine Idee fuer ein bestimmten Objekt oder NPC hast, kannst Du
+    dieses/diesen hinter einem Doppelpunkt angeben, damit der fuer das Objekt
+    verantwortliche Magier die Meldung bekommt.
+    Wenn das Objekt nicht zu finden ist, wird die ganze Eingabe als
+    Meldung aufgefasst.
+
+BEMERKUNG:
+    Der Befehl 'idee' bezieht sich bei Nichtangabe eines Bezugsobjektes
+    auf das letzte betrachtete Objekt. Hast Du also ein Objekt untersucht,
+    dann wird die Meldung fuer diese Objekt abgegeben. Hast Du aber den
+    Raum untersucht oder 'schau' eingegeben, wird die Meldung fuer Deinen
+    momentanen Raum abgesetzt.
+
+SIEHE AUCH:
+    bug, typo, detail, bezugsobjekt
+
+LETZTE AeNDERUNG:
+    12. Okt 2011 Gloinson
diff --git a/doc/pcmd/idlezeit b/doc/pcmd/idlezeit
new file mode 100644
index 0000000..9a459ec
--- /dev/null
+++ b/doc/pcmd/idlezeit
@@ -0,0 +1,19 @@
+
+idlezeit
+--------
+
+ KOMMANDO:
+    idlezeit <name>
+
+ ARGUMENTE:
+
+     <name>
+        Name eines Spielers
+
+ BESCHREIBUNG:
+    Dieser Befehl gibt die Zeit aus, die ein Spieler passiv ist.
+    Dann muss man nicht jedes mal den Spieler fingern, wenn man wissen
+    will, ob er gerade ansprechbar ist oder nicht.
+
+ LETZTE AeNDERUNG:
+    Mit, 25. Feb 2004, 12:46:00 von Rikus
diff --git a/doc/pcmd/ignoriere b/doc/pcmd/ignoriere
new file mode 100644
index 0000000..87e9b27
--- /dev/null
+++ b/doc/pcmd/ignoriere
@@ -0,0 +1,134 @@
+
+ignoriere
+---------
+
+ KOMMANDO:
+    ignoriere <spieler> | <spieler>.<aktion> | .<aktion>
+    ignoriere <spieler>@<mud> | <spieler>@ | @<mud>
+    ignoriere <spieler>.<aktion>.<einschraenkung>
+    ignoriere
+
+ ARGUMENTE:
+
+     <aktion>
+        Die zu ignorierende Aktion (Kommando)
+     <spieler>
+        Name des zu ignorierenden Spielers
+     <mud>
+        Name eines MUDs
+     <einschraenkung>
+        Dies kann die ignorierte Aktion nochmal einschraenken (s.u.)
+
+ BESCHREIBUNG:
+    Wenn Dir jemand maechtig auf die Nerven geht oder Dir einige Aktionen
+    nicht in den Kram passen, kannst Du Dich mit diesem Kommando vor den
+    Auswirkungen schuetzen.
+
+    Folgende Moeglichkeiten bestehen:
+
+     'ignoriere <spieler>'
+        Saemtliche Meldungen, die der Spieler <spieler> erzeugt, werden
+        unterdrueckt. Dies ist eine sehr drastische Massnahme!
+
+     'ignoriere .<aktion>'
+        Alle Meldungen, die <aktion> erzeugt, werden unterdrueckt. Es wird
+        meistens jedoch auf die genaue Schreibweise geachtet (Ausnahmen s.u.);
+        wenn man also niemals geweckt werden will, muss man sowohl `.wecke'
+        als auch `.weck' ignorieren!
+
+     'ignoriere <spieler>.<aktion>'
+        Eine Kombination der obigen Moeglichkeiten: Die Aktion <aktion>
+        wird nur dann ignoriert, wenn sie vom Spieler <spieler> kommt. Andere
+        Meldungen von <spieler> werden jedoch weiterhin akzeptiert.
+
+     'ignoriere <spieler>.ebenen.<ebene>'
+     'ignoriere <spieler>.ebenen'
+        So kann man die Aeusserungen eines Spielers auf einer bestimmten Ebene
+        oder auf allen Ebenen ignorieren, ohne ihn gleich komplett zu
+        ignorieren.
+        Ein Beispiel waere 'ignoriere tilly.ebenen.allgemein'. Damit bekommt
+        man nur die Aeusserungen auf der Ebene <Allgemein> nicht mit, alle
+        anderen schon.
+        BTW: das funktioniert zur Zeit nicht fuer Spieler aus anderen Muds.
+     
+     'ignoriere <spieler>.mail'
+        Ignoriert Post (mudinterne Mail), die von <spieler> abgesandt wurde.
+
+     'ignoriere <spieler>.news'
+        Ignoriert MPA-Artikel von <spieler> bei nn. (Nicht bei gezieltem Lesen
+        von Artikeln.)
+
+     'ignoriere <spieler>@<mud>'
+        Alle Mitteilungen des Spielers <spieler> aus dem Mud <mud> werden
+        abgeblockt.
+
+     'ignoriere <spieler>@'
+        Die Mitteilungen von allen Spielern namens <spieler> in anderen Muds
+        werden abgeblockt. Dies ist eine sehr drastische Massnahme, die man
+        sich gut ueberlegen sollte. Schliesslich koennen sich hinter dem
+        gleichen Namen in unterschiedlichen Muds auch unterschiedliche Leute
+        verbergen!
+
+     'ignoriere @<mud>'
+        Es werden saemtliche Mitteilungen aus dem Mud <mud> ignoriert. Auch
+        dies ist eine recht drastische Massnahme!
+
+     'ignoriere'
+        Die Liste der ignorierten Aktionen und Spieler wird angezeigt.
+
+    Folgende Aktionen gibt es, welche auch andere Schreibweisen umfassen:
+      sage (sage, sag, '), emote (emotes, remotes), rufe (rufe, ruf),
+      teilemit (teile-mits), ... (Liste noch unvollstaendig)
+
+    Will man einen Eintrag wieder entfernen, muss man einfach noch einmal das
+    entsprechende `ignoriere'-Kommando eingeben.
+
+    Neben Kommunikationskommandos von Spieler zu Spieler lassen sich auch noch
+    folgende Dinge abwehren (allen diesen Faellen laesst sich ein Spielername
+    voranstellen, wenn man das nur bei einem bestimmten Spieler ignorieren
+    will):
+
+     Spendieren in der Kneipe
+
+         * .spendiere
+         * .spendiere.getraenke
+         * .spendiere.alkohol
+         * .spendiere.essen
+
+     Gefunden werden durch andere Spieler
+     
+         Hiermit wird verhindert, dass fremde Spieler den eigenen
+         Aufenthaltsort ermitteln koennen (noch nicht vollstaendig
+         implementiert).
+         * .finde (schliesst alle nachfolgenden ein)
+         * .finde.glaswuerfel
+         * .finde.feldstecher
+
+     Verschiedenes
+
+         * .shout
+           Die Aktion 'shout' ist nicht das gleiche wie 'rufe', sondern
+           umfasst Meldungen aus den Regionen, wie die Ankunft der Hydra oder
+           das Loesen bestimmter Quests.
+         * .maus.<verb>
+         * .knuddelmaus.<verb>
+           Diese beiden ignorieren die Diddlmaus.
+
+     Meldungen aus den Arenen
+
+         * .arena (alle u.g.)
+         * .arena.seherwettbewerb (Seherwettbewerb Wuestenarena)
+         * .arena.gladiator (Herausforderung Gladiatoren)
+         * .arena.duell (Duellaufforderungen der Wuestenarena)
+         * .arena.fegefeuer (Duellankuendigungen Fegefeuer)
+         * .arena.fegefeuerscroll (Kampfscroll Fefefeuer)
+         * .arena.fegefeuerduell (Duellaufforderungen Fegefeuer)
+         * .arena.schlachtfeld (Schlachtfeld im verl. Land)
+
+ SIEHE AUCH:
+    sage, frage, antworte, fluestere, teile (mit), gespraech, rufe, ebenen,
+    weg, kobold
+
+ LETZTE AENDERUNG:
+    02.10.2013, Zesstra
+
diff --git a/doc/pcmd/info b/doc/pcmd/info
new file mode 100644
index 0000000..c65ac6b
--- /dev/null
+++ b/doc/pcmd/info
@@ -0,0 +1,33 @@
+
+info
+----
+
+ KOMMANDO:
+    info
+    punkte
+
+ BESCHREIBUNG:
+    Diese Kommandos geben die wichtigsten Daten ueber Deinen Charakter aus.
+    Dazu gehoeren:
+
+     * Dein voller Name mit Presay und Titel,
+     * Deine Rasse,
+     * Deine Charaktereinstellung,
+     * koerperliche Attribute wie Geschlecht, Groesse und Gewicht,
+     * Deine Spielerstufe,
+     * die Gilde, der Du angehoerst, und die Stufe dort,
+     * Erfahrungs- und Abenteuerpunkte,
+     * Deine Attribute,
+     * Deine koerperliche und geistige Verfassung,
+     * ggf. Vorsichtsmodus und Fluchtrichtung,
+     * der Grad Deiner Kenntnis des MorgenGrauen,
+     * ggf. Vergiftungsgrad, Abwesenheit und Feinde sowie
+     * Dein Alter.
+
+    `info' zeigt alle diese Daten an, waehrend `punkte' nur die wichtigsten
+    Daten anzeigt.
+
+ SIEHE AUCH:
+    kurzinfo
+
+12. Mar 2006 Gloinson
diff --git a/doc/pcmd/inform b/doc/pcmd/inform
new file mode 100644
index 0000000..b7c08d0
--- /dev/null
+++ b/doc/pcmd/inform
@@ -0,0 +1,29 @@
+
+inform
+------
+
+ KOMMANDO:
+    inform [<zustand>]
+
+ ARGUMENTE:
+
+     <zustand>
+        `ein' oder `aus'
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kannst Du den Informationsmodus ein- oder ausschalten.
+    Hast Du den Informationsmodus eingeschaltet, so wirst Du immer ueber das
+    Ein- und Ausloggen von Spielern informiert.
+
+    `inform' ohne Parameter zeigt Dir den aktuellen Zustand des
+    Informationsmodus an.
+
+ BEMERKUNGEN:
+    Wenn Du nur ueber das Ein- und Ausloggen bestimmter Spieler informiert
+    werden willst, solltest Du den `erwarte'-Befehl benutzen.
+
+ SIEHE AUCH:
+    erwarte
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/inventur b/doc/pcmd/inventur
new file mode 100644
index 0000000..d91138d
--- /dev/null
+++ b/doc/pcmd/inventur
@@ -0,0 +1,49 @@
+
+inventur
+--------
+
+ KOMMANDO:
+    i [<flags>]
+    inv [<flags>]
+    inventur [<flags>]
+
+ ARGUMENTE:
+
+     <flags>
+        Flags, um die Anzeige zu konfigurieren
+
+ BESCHREIBUNG:
+    Es wird eine kurze Beschreibung aller Objekte ausgegeben, die Du bei Dir
+    traegst. Normalerweise werden alle Objekte angezeigt und nach Waffen,
+    Ruestungen und Verschiedenes sortiert. Dies kann man allerdings mit den
+    <flags> aendern:
+
+     -1  Einspaltige Ausgabe
+     +a  Nur Autoloader anzeigen
+     -a  Keine Autoloader anzeigen
+     +b  Nur Objekte zeigen, die man bei "verkaufe alles" behaelt
+     -b  Kein Objekt zeigen, das man bei "verkaufe alles" behaelt
+     -f  Inventar nicht unterteilen oder gleiche Objekte zusammenfassen
+     +r  Nur Ruestungen anzeigen
+     -r  Keine Ruestungen anzeigen
+     -s  Ausgabe alphabetisch sortieren
+     +v  Alles zeigen, was nicht Ruestung oder Waffe ist
+     -v  Alles zeigen, was Ruestung oder Waffe ist
+     +w  Nur Waffen anzeigen
+     -w  Keine Waffen anzeigen
+
+    Die Argumente sind auch kombinierbar.
+
+    Bei den Flags +/-a und +/-b ist folgendes zu beachten: Kombiniert man sie
+    mit den anderen Flags, so findet eine logische UND-Verkuepfung statt. Mit
+
+    inv +r +a
+
+    werden also nicht alle Ruestungen sowie alle Autoloader angezeigt, sondern
+    nur alle Autoload-Ruestungen.
+
+ SIEHE AUCH:
+    behalte, ausruestung
+
+ LETZTE AeNDERUNG:
+    Tue, 27.02.2001, 17:25:00 von Tiamak
diff --git a/doc/pcmd/kkwer b/doc/pcmd/kkwer
new file mode 100644
index 0000000..23bbd70
--- /dev/null
+++ b/doc/pcmd/kkwer
@@ -0,0 +1,19 @@
+
+kkwer
+-----
+
+ KOMMANDO:
+    kkwer
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Es wird eine alphabetisch sortierte Liste mit den Namen aller momentan im
+    MorgenGrauen eingeloggten Spieler ausgegeben.
+
+ SIEHE AUCH:
+    wer, kwer
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/klettere b/doc/pcmd/klettere
new file mode 100644
index 0000000..67d06a5
--- /dev/null
+++ b/doc/pcmd/klettere
@@ -0,0 +1,29 @@
+
+klettere
+--------
+
+ KOMMANDO:
+    klettere hoch | runter
+    klettere aus | auf | ueber | in | aus <objekt>
+
+ ARGUMENTE:
+
+     <objekt>
+        Ein Gegenstand
+
+ BESCHREIBUNG:
+    Das ist der Standardbefehl, um ein Hindernis zu ueberwinden. Manchmal
+    reichen aber auch die normalen Richtungsbefehle aus. Manche Hindernisse
+    stellen ein Raetsel dar, das mit raffinierteren Methoden ueberwunden
+    werden will.
+
+ BEMERKUNG:
+    Wenn irgendwo noch der Befehl `klettere' in anderer Syntax benoetigt wird,
+    bitte eine `fehler'-Meldung hinterlassen, damit die Magier es etwas
+    leichter haben, die Befehle zu vereinheitlichen.
+
+ SIEHE AUCH:
+    bewegung, fehler
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/klingelton b/doc/pcmd/klingelton
new file mode 100644
index 0000000..f855d2e
--- /dev/null
+++ b/doc/pcmd/klingelton
@@ -0,0 +1,23 @@
+KOMMANDO:
+	klingelton aus
+        klingelton 1..3600 
+
+ARGUMENTE:
+                  : aktuellen Status anzeigen  
+	  1..3600 : Klingelton maximal aller n Sekunden 
+	  AUS     : Klingelton ausschalten.
+
+BESCHREIBUNG:
+        Um Mitteilungen im Scroll nicht zu verpassen, wird bei eingeschaltetem
+        Klingelton in den voreingestellten Zeitintervallen ein Tonsignal vom
+        Mud erzeugt. Das ist besonders fuer blinde Spieler von Vorteil, denen
+        sonst Mitteilungen schnell mal verloren gehen.
+        Der Ton wird nur produziert, wenn man mit 'ton ein' die Tonausgabe
+        des Muds aktiviert hat.
+        Mitteilungen sind in diesem Zusammenhang 'sag' und 'teile mit'.
+ 
+SIEHE AUCH:
+	teile mit, sag, klingelton, grafik aus, ton, keepalive
+
+LETZTE AENDERUNG:
+   16. Mai 2007  Ennox
diff --git a/doc/pcmd/kobold b/doc/pcmd/kobold
new file mode 100644
index 0000000..0971658
--- /dev/null
+++ b/doc/pcmd/kobold
@@ -0,0 +1,51 @@
+
+kobold
+------
+
+ KOMMANDO:
+    kobold [<schalter>]
+
+ ARGUMENTE:
+
+     <schalter> (optional)
+        `ein' oder `aus'
+
+ BESCHREIBUNG:
+    Der Kobold hat mehrere Funktionen, die mit dem Speichern von Mitteilungen
+    beim Bearbeiten oder Lesen eines Textes oder anderere Aktionen in
+    Zusammenhang stehen.
+
+    Mit `kobold ein' schaltet man den Kobold so ein, dass er sich jede
+    Mitteilung merkt, die waehrend einer der oben genannten Aktionen an einen
+    gesendet wurde. Wenn man damit fertig ist, kann dann mittels `kobold',
+    ohne Argumente, abgerufen werden, was er sich gemerkt hat. Das sollte aber
+    im Normalfall nicht noetig sein, da sich der Kobold automatisch meldet.
+
+    Jeder gespeicherten Meldung ist die Uhrzeit des Empfangs angehaengt.
+
+    Wuenscht man keine solche Ruhemassnahme, kann man den Kobold mit `kobold
+    aus' wieder abschalten und jede Mitteilung, Rufe und andere Meldungen
+    werden immer sofort weitergeleitet.
+
+    Standardmaessig ist der Kobold ausgeschaltet.
+
+    Der Kobold kann sich max. 255 Mitteilungen merken, sind diese
+    ueberschritten, gehen neuere Mitteilungen verloren!
+
+    Abgeblockt werden folgende Meldungen:
+    mrufe, Ebenen, Mitteilungen (erzaehle, teile mit)
+
+    Nicht abgeblockt werden folgende Meldungen:
+    sage, jegliche lokalen Emotionen sowie sonstige lokale (also im selben
+    Raum) erzeugte Meldungen, rufe, ...
+
+    *Merke*: Der Kobold funktioniert nur, wenn man einen Text liest und more
+    benutzt wird oder wenn man einen Text editiert (z.B. beim Postschreiben,
+    Artikelschreiben).
+
+ SIEHE AUCH:
+    rufe, teile (mit), weg, ebenen
+
+ LETZTE AeNDERUNG:
+    05.10.2013, Zesstra
+
diff --git a/doc/pcmd/kurz b/doc/pcmd/kurz
new file mode 100644
index 0000000..be1d907
--- /dev/null
+++ b/doc/pcmd/kurz
@@ -0,0 +1,20 @@
+
+kurz
+----
+
+ KOMMANDO:
+    kurz
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Schaltet auf den "Kurz"-Modus um. Wenn Du Dich im Kurzmodus befindest,
+    siehst Du nur eine kurze Beschreibung aller Raeume. Fuer eine laengere
+    Beschreibung kannst Du das Kommando `schau' benutzen.
+
+ SIEHE AUCH:
+    lang, ultrakurz, schau, ausgaenge
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/kurzinfo b/doc/pcmd/kurzinfo
new file mode 100644
index 0000000..af1fe68
--- /dev/null
+++ b/doc/pcmd/kurzinfo
@@ -0,0 +1,17 @@
+
+kurzinfo
+--------
+
+ KOMMANDO:
+    kurzinfo
+    kurzinfo -k
+
+ BESCHREIBUNG:
+    Dieses Kommando gibt Deine aktuellen Lebens- und Konzentrationspunkte aus.
+    'kurzinfo -k' gibt eine verkuerzte Angabe ohne ASCII-Graphik aus, genauso 
+    als haettest Du deren Ausgabe mit 'grafik aus' abgeschaltet.
+
+ SIEHE AUCH:
+    info 		
+
+ 09.08.2009, Zesstra
diff --git a/doc/pcmd/kwer b/doc/pcmd/kwer
new file mode 100644
index 0000000..7af9e0b
--- /dev/null
+++ b/doc/pcmd/kwer
@@ -0,0 +1,90 @@
+
+kwer
+----
+
+ KOMMANDO:
+    kwer [[alle] [in <stadt>] [region <region>] [bei <spieler>] [spieler]
+         [seher] [magier] [erzmagier] [goetter] [mensch] [zwerg] [elf]
+         [feline] [hobbit] [regionsmitarbeiter|mitarbeiter <region>]
+         [regionsmagier <region>] [testies] [zweities] [magierzweities]
+         [erwartete] [gilde <gilde>] [gildenmagier <gilde>]
+	 [frosch] [idle] [idlezeit <min>] [icq] [cicerone]
+         [www|url] [ssl|stunnel] [und|oder|ausser] ...]
+
+ ARGUMENTE:
+
+     <stadt>
+        Name der gewuenschten Stadt (Herkunft des Login)
+     <region>
+        Name der gewuenschten Region (in Morgengrauen)
+     <spieler>
+        Name eines Spielers
+     <gilde>
+        Name einer Gilde
+
+ BESCHREIBUNG:
+    Es wird eine kurze Liste aller in MorgenGrauen anwesenden Spieler
+    ausgegeben, dabei werden erwartete Spieler mit einem vorgestellten
+    "*" markiert.
+    Bei aktiviertem ANSI sind Magier unterstrichen, erwartete Spieler
+    in rot und Froesche in gruen angezeigt.
+    
+
+    Die Buchstaben am Ende bedeuten:
+
+     [s]   Spieler
+     [S]   Seher
+
+     [z]   Zweitspieler
+     [Z]   Zweitspieler mit Seherlevel
+     [n]   unsichtbar markierter Zweitspieler
+     [N]   unsichtbar markierter Zweitspieler mit Seherlevel
+
+     [t]   Testspieler
+     [T]   Testspieler mit Seherlevel
+
+     [L]   Magierlehrling
+     [m]   Vollmagier, ohne Region
+     [M]   Vollmagier, mit Region
+     [H]   Hilfsmagier
+     [R]   Regionschef
+     [W]   Weiser
+     [E]   Erzmagier
+     [G]   Gott
+
+    Die Informationen [n] und [N] sowie die Liste der SSL-Nutzer bekommen 
+    nur Magier. Die direkte Abfrage der Magierzweities steht nur 
+    Erzmagiern zur Verfuegung.
+
+    Die Idle-Zeit ist an einem Buchstaben vor dem Level zu erkennen:
+   
+     [i]   > 2 Minuten
+     [I]   > 10 Minuten
+     [j]   > 30 Minuten
+     [J]   > 120 Minuten
+   
+    "Idle" bedeutet, laenger keine Taste zu betaetigen.
+
+     [w]   Der Spieler hat eine WEG-Meldung gesetzt.
+
+    Direkt vor der Idlezeit ist zu erkennen, ob der Spieler den
+    Hardcoremodus spielt:
+     [c]   Der Spieler spielt Hardcore und ist noch am Leben.
+     [+]   Der Spieler spielt Hardcore und ist bereits verstorben.
+
+    Man kann die Ausgabe auf gewisse Gruppen einschraenken, diese Gruppen
+    koennen mit `und', `oder' und `ausser' verknuepft werden. Dies geschieht
+    mit Linksklammerung (also `a oder b und c' wird als `(a oder b) und c'
+    interpretiert).
+
+ BEISPIELE:
+
+    > kwer alle ausser goetter
+    > kwer in muenster oder in koeln und seher
+
+ SIEHE AUCH:
+    wer, kkwer, ort
+
+ LETZTE AeNDERUNG:
+    2015-12-02, Arathorn
+
diff --git a/doc/pcmd/lang b/doc/pcmd/lang
new file mode 100644
index 0000000..b75deba
--- /dev/null
+++ b/doc/pcmd/lang
@@ -0,0 +1,19 @@
+
+lang
+----
+
+ KOMMANDO:
+    lang
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Schaltet auf den "Lang"-Modus um. Im Langmodus siehst Du die komplette
+    Beschreibung der Raeume, die Du betrittst.
+
+ SIEHE AUCH:
+    kurz, ultrakurz, schau, ausgaenge
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/lausche b/doc/pcmd/lausche
new file mode 100644
index 0000000..dcde75e
--- /dev/null
+++ b/doc/pcmd/lausche
@@ -0,0 +1,32 @@
+
+lausch(e) (an)
+--------------
+
+KOMMANDO
+	lausch(e)
+	lausch(e) <was> [an <objekt> | am <objekt>] [in mir | im raum]
+
+ARGUMENTE
+	<objekt>
+	  Ein Gegenstand bzw. Lebewesen, an dem lauschen will.
+
+BESCHREIBUNG
+	`lausch' bzw. `lausche' ist das allgemeine Kommando, um Geraeusche
+	an Gegenstaenden, Lebewesen oder in Raeumen wahrzunehmen. Hierbei
+	laesst `lausch(e)' ohne Argumente den Spieler die Hauptgeraeusche in
+	dem Raum wahrnehmen, in dem er sich derzeit befindet. An allen
+	anderen Gegenstaenden bzw. Lebewesen im selben Raum oder im eigenen
+	Inventar kann man mit `lausch(e) (an)' ebenfalls Geraeusche
+	wahrnehmen. Nicht immer kann man jedoch etwas Besonderes hoeren.
+
+	Da bei Objekten mit gleichen Geraeuscharten zuerst an im Raum
+	liegenden Objekt gelauscht wird, kann man an Sachen, die man bei
+	sich traegt, auf alle Faelle mit dem Zusatz `in mir' lauschen.
+
+SIEHE AUCH:
+  bezugspunkt
+  schau, rieche, taste, lies
+
+LETZTE AENDERUNGEN:
+  18.01.2013, Zesstra
+
diff --git a/doc/pcmd/lies b/doc/pcmd/lies
new file mode 100644
index 0000000..9045beb
--- /dev/null
+++ b/doc/pcmd/lies
@@ -0,0 +1,51 @@
+
+lies
+----
+
+KOMMANDO
+	lies <was> [an <objekt> | am <objekt>] [in mir | im raum]
+
+ARGUMENTE
+  <was>
+    Name eines Gegenstandes, Lebewesens oder Detail, welches man lesen will.
+	<objekt>
+	  Ein Gegenstand oder ein Lebewesen, an dem man lesen will.
+
+BESCHREIBUNG
+  'lies' ist das allgemeine Kommando, um lesbare Details an Gegenstaenden,
+  Lebewesen oder in Raeumen (z.B. ein Schild an einem Rucksack) oder an sich
+  lesbare Gegenstaende (z.B. eine Schriftrolle) wahrzunehmen.
+
+  Da bei Objekten mit gleichen lesbaren Details zuerst die im Raum liegenden
+  Objekte gelesen werden, kann man an Sachen, die man bei sich traegt, auf
+  alle Faelle mit dem Zusatz `in mir' riechen.
+
+  Das Kommando benutzt ggf. das aus dem Kommando 'schau' bzw. 'untersuche'
+  bekannte Bezugsobjekt.
+
+BEISPIELE:
+  > lies tafel
+
+  > lies tafel in raum
+
+  > lies widmung an buchstuetzen in mir
+  
+  > lies schild an biene in raum
+
+  > lies buch in paket
+
+  > unt paket
+  > lies index an buch
+  (Liegt ein Buch mit lesbarem Detail "index" im Paket, liest man diesen mit
+   dem zweiten Kommando.)
+
+  > lies index an buch in paket in raum
+  (Liest den Index eines Buches in einem Paket im Raum.)
+
+SIEHE AUCH:
+  bezugspunkt
+  schau, rieche, lausche, taste
+
+LETZTE AENDERUNGEN:
+  18.01.2013, Zesstra
+
diff --git a/doc/pcmd/messenger b/doc/pcmd/messenger
new file mode 100644
index 0000000..79138ef
--- /dev/null
+++ b/doc/pcmd/messenger
@@ -0,0 +1,37 @@
+
+messenger
+---------
+
+ KOMMANDO:
+    messenger [<id>]
+
+ ARGUMENTE:
+
+     <id> (optional)
+        Die neue Messenger-ID
+
+ BESCHREIBUNG:
+    Wenn Du einen Messenger-Dienst benutzt, so kannst Du dort
+    Deine ID eintragen. Es wird einfach der Text gespeichert, den
+    Du uebergibst und dann beim finger-Befehl angezeigt. Du solltest
+    also mit hinzuschreiben, um welchen Dienst es sich handelt. 
+
+    Wird kein Parameter uebergeben, so wird der aktuelle Eintrag
+    angezeigt. Gibst Du "keine" oder "loeschen" an, wird der
+    Eintrag geloescht.
+
+    Hinweis zum Zusammenspiel mit dem Befehl ICQ. Hast Du zugleich
+    auch eine ICQ-Nummer gesetzt, so wird diese automatisch im
+    Finger-Befehl mit angegeben. 
+
+ BEISPIELE:
+
+    messenger ICQ: 1234   
+    messenger MSN: foo@bar.com
+
+ SIEHE AUCH:
+    Persoenliche Details: email, url, icq, ort
+    Sonstiges:            finger
+
+ LETZTE AENDERUNG:
+    2006-01-05 von Zook.
diff --git a/doc/pcmd/mrufe b/doc/pcmd/mrufe
new file mode 100644
index 0000000..3b164ce
--- /dev/null
+++ b/doc/pcmd/mrufe
@@ -0,0 +1,37 @@
+
+mrufe
+-----
+
+ KOMMANDO:
+    mrufe <text>
+
+ ARGUMENTE:
+
+     <text>
+        Der Text, der an die Magier gerufen wird
+
+ BESCHREIBUNG:
+    Die Meldung <text> wird an alle eingeloggten Magier ausgegeben.
+
+    Dieser Befehl darf nur in NOTFAELLEN verwendet werden!!!
+
+    Notfaelle sind z.B. fehlerhafte Raeume, aus denen man nicht mehr
+    herauskommt, o.ae. (*aber*: nicht jeder Raum ohne sichtbare Ausgaenge ist
+    fehlerhaft!) Man sollte sich erst vergewissern, ob man wirklich
+    feststeckt.
+
+    Kein Notfall ist z.B. ein mrufe, wenn man einen Brief an einen Magier
+    zustellen will.
+    Missbraeuchliche Benutzung dieses Befehls sorgt dafuer, dass sich die
+    Magier auch in wirklichen Notlagen nicht mehr um das ge`mrufe' kuemmern,
+    sondern sich koestlich ueber die Spieler amuesieren. Es liegt also bei
+    euch, wie ernst ihr genommen werden moechtet.
+
+    Magier sollten lieber die Magierebene verwenden (-Magier+).
+
+ SIEHE AUCH:
+    teile (mit), ebenen
+
+ LETZTE AeNDERUNG:
+ 16.09.2008, Zesstra
+
diff --git a/doc/pcmd/muds b/doc/pcmd/muds
new file mode 100644
index 0000000..f9aa67e
--- /dev/null
+++ b/doc/pcmd/muds
@@ -0,0 +1,20 @@
+
+muds
+----
+
+ KOMMANDO:
+    muds
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Gibt eine Liste der MUDs aus, mit denen Du von hier aus Kontakt aufnehmen
+    kannst. Dazu gehoeren z.B. die Abfrage der Anwesenheitsliste, das
+    Versenden von Mitteilungen oder die Information ueber Spieler.
+
+ SIEHE AUCH:
+    wer, teile (mit), finger
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/nimm b/doc/pcmd/nimm
new file mode 100644
index 0000000..039f0a5
--- /dev/null
+++ b/doc/pcmd/nimm
@@ -0,0 +1,34 @@
+
+nimm [aus]
+----------
+
+ KOMMANDO:
+    nimm <objekt> [aus <behaelter>]
+    nimm alles [aus <behaelter>]
+
+ ARGUMENTE:
+
+     <objekt>
+        Der zu nehmende Gegenstand
+     <behaelter> (optional)
+        Der Behaelter, in dem sich das Objekt der Begierde befindet
+
+ BESCHREIBUNG:
+    Man nimmt den Gegenstand <objekt> vom Boden auf oder aus einem <behaelter>
+    heraus.
+
+    Waehlt man als <objekt> `alles', so nimmt man alles, was sich nehmen
+    laesst (soweit man es noch tragen kann).
+
+    Mit 'nimm behaltenes' kann man alle Gegenstaende, die man frueher
+    einmal "behalten" (s. 'behalte') hat, aus dem aktuellen Raum aufnehmen.
+    Steht man in einem Raum, wo man gestorben ist und liegt dort der eigene
+    Kram in Form eines "Haufen Krempels", kann man mittels 'nimm eigenes'
+    alles aus diesem (und nur diesem) Haufen aufnehmen. Dieses schliesst
+    uebrigens 'nimm behaltenes' ein.
+
+ SIEHE AUCH:
+    stecke (in), hole (aus), wirf (weg), behalte
+
+ LETZTE AeNDERUNG:
+    24.05.2007, Zesstra
diff --git a/doc/pcmd/ort b/doc/pcmd/ort
new file mode 100644
index 0000000..472003b
--- /dev/null
+++ b/doc/pcmd/ort
@@ -0,0 +1,27 @@
+
+ort
+---
+
+ KOMMANDO:
+    ort [<name>|loeschen]
+
+ ARGUMENTE:
+
+     <name> (optional)
+        Der neue Ortsname
+     loeschen (optional)
+        Loescht den gesetzten Ortsnamen wieder
+
+ BESCHREIBUNG:
+     Wenn Du aus einem Ort kommst, der nicht automatisch erkannt
+     werden kann, kannst Du mit diesem Befehl eintragen, von
+     woher Du WIRKLICH kommst.
+     Die Angabe wird lediglich als zusaetzliche Information beim
+     "finger"-Befehl angezeigt sowie wahlweise bei "wer".
+     Falls Du z.B. aus Frankfurt eingeloggt bist und als Ort Muenchen
+     angibst, wirst Du sowohl bei "kwer in frankfurt" als auch bei
+     "kwer in muenchen" gefunden.
+     
+ SIEHE AUCH:
+    Persoenliche Details: email, url, icq, messenger
+    Sonstiges:            finger, wer, kwer
diff --git a/doc/pcmd/passwort b/doc/pcmd/passwort
new file mode 100644
index 0000000..191b5b2
--- /dev/null
+++ b/doc/pcmd/passwort
@@ -0,0 +1,20 @@
+
+passwort
+--------
+
+ KOMMANDO:
+    passwort
+    password
+    passwd
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Aendert das Passwort. Zur Sicherheit muss zuerst das alte Passwort
+    eingegeben werden. Daraufhin wird das neue Passwort zweimal erfragt. Das
+    Passwort wird nur dann geaendert, wenn die beiden Eingaben des neues
+    Passwortes uebereinstimmen.
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/reise b/doc/pcmd/reise
new file mode 100644
index 0000000..6e461a3
--- /dev/null
+++ b/doc/pcmd/reise
@@ -0,0 +1,103 @@
+KOMMANDO:
+        reise
+        reise route
+        reise [mit transportmittel] nach|zu|zum|zur|ins|ans (zielort)
+        reise aus|nicht
+
+ARGUMENTE:
+        <transportmittel>       Ein Transportmittel Deiner Wahl
+        <zielort>               Dein Reiseziel
+
+FUNKTION:
+       Wenn Du Dein Reiseziel (oder das Verkehrsmittel) bereits
+       kennst, kannst Du mit diesem Befehl Deine Reise einfacher
+       gestalten.
+       
+       Benutzt Du den reise-Befehl, so wirst Du automatisch ein
+       Transportmittel betreten (falls dies moeglich ist) und es
+       am Zielort (falls angegeben) auch wieder verlassen.
+
+       Folgende Argumente / Funktionen werden unterstuetzt:
+
+reise 
+       Deine aktuelle Reiseroute wird ausgegeben. Du kannst sie mit
+       o.g. Argumenten immer wieder aendern.
+
+reise route:
+       Damit kannst Du feststellen, welche Verkehrsmittel verkehren 
+       und welche Routen sie anbieten.
+
+reise mit <xxx> nach|zu|zum|zur|ins|ans <zzz>:
+       Die wohl genaueste Moeglichkeit, eine Reiseroute festzulegen.
+
+       xxx steht hier fuer eine eindeutige Kennzeichnung des Transport-
+       mittels (z.B. 'piratenschiff' statt 'schiff').
+
+       zzz benennt den genauen Zielort (z.B. 'sonneninsel' statt 'insel').
+
+       Passen mehrere Transporter (oder Zielorte) auf die von Dir 
+       angebenen Namen, so wird der naechstmoegliche Transporter und
+       das naechstgelegene Ziel ausgewaehlt.
+
+       Die Bezeichnung fuer <zzz> reagiert teilweise nicht auf unter-
+       schiedliche Schreibweisen. Am besten, Du nimmst immer die, die 
+       auch bei 'reise route' angegeben wird.
+
+reise mit <xxx>:
+       Hier legst Du nur fest, WOMIT Du fahren willst. Du faehrst solange
+       mit xxx, bis Du den Transporter eigenhaendig verlaesst, oder einen 
+       Zielort mittels 'reise nach zzz' angibst.
+
+reise nach <zzz>:
+       Hier legst Du nur fest, WOHIN Du fahren willst. Das Transportmittel
+       wird automatisch ermittelt, wobei anwesende Transporter natuerlich
+       bevorzugt werden.
+
+       Da <zzz> nicht immer auf unterschiedliche Schreibweisen reagiert, 
+       nimmst Du am besten die Bezeichnung, die auch bei 'reise route'
+       angegeben wird.
+
+reise aus|nicht:
+       Mit diesem Befehl loeschst Du Deine Reiseroute. Du musst das
+       gewuenschte Transportmittel dann wieder per Hand betreten und 
+       verlassen.
+
+BEMERKUNGEN:
+       Eine festgelegte Reiseroute ist nur dort wirksam, wo Du sie 
+       festgelegt hast. Nach erfolgreicher Reise wird sie automatisch 
+       geloescht, ansonsten bleibt sie bis zum expliziten Loeschen 
+       bestehen.
+
+       Nicht jedes Transportmittel / Ziel kann mittels 'reise' ausge-
+       waehlt werden. Es besteht keinerlei Anspruch darauf, das 'reise'
+       ueberall zu funktionieren hat.
+
+BEISPIELE:
+       reise ins verlorene land
+       
+       reise ans ende der welt
+       reise zum ende der welt
+       
+       reise nach port vain
+  
+       Einige Zielorte sind auch per Kuerzel zu erreichen:
+       - Verlorenes Land: vland
+       - Werwolfinsel: wwi
+       - Port Vain: pv
+       - Akhar Nth'tar: ti
+       - Insel am Ende der Welt: edw, iaedw, weltende
+       - Feuerinsel: fi
+       - Magieinsel: minsel
+       - Insel der Toten: idt, toteninsel
+
+       Generell sollten die Namen als Reiseziele verwendet werden, die
+       auch bei 'reise route' angezeigt werden. Gleiches gilt natuerlich
+       auch fuer die Namen der Transporter. So ist eine zielgenaue und 
+       perfekte Reise schon vorprogrammiert.
+
+       Mit ein bisschen Geduld und Testen wirst Du sicher schnell raus-
+       bekommen, wie Du welchem Befehl einsetzen musst um an Dein Ziel 
+       zu gelangen.
+     
+LETZTE AENDERUNG:
+       2015-Jan-18, Arathorn 
diff --git a/doc/pcmd/report b/doc/pcmd/report
new file mode 100644
index 0000000..240ddc0
--- /dev/null
+++ b/doc/pcmd/report
@@ -0,0 +1,77 @@
+KOMMANDO:
+        report [ein|aus|vorsicht]
+
+ARGUMENTE:
+        (nichts)      : aktuelle Einstellung anzeigen
+        aus           : alle eingeschalteten Report-Modi ausschalten
+        ein           : Report von LP/KP/Gift einschalten
+        vorsicht      : Vorsicht-Report zusaetzlich einschalten
+                        (nur Seher: Fluchtrichtung wird auch gemeldet)
+        senden        : Report jetzt sofort mit aktuellen Einstellungen
+                        senden
+
+BESCHREIBUNG:
+        Wenn der Report eingeschaltet wird, wird bei Aenderungen an den LP
+        des Spielers eine Meldung an diesen ausgegeben, die den neuen LP-Wert
+        beinhaltet. Falls freigeschaltet (s.u.), gilt das gleiche fuer 
+        Aenderungen an den KP und am Giftstatus. Jede Aenderung an einem 
+        dieser Werte loest die Ausgabe aller drei Werte gemeinsam aus.
+        Ausschalten laesst sich der Report nur komplett mit "report aus".
+
+        Wenn der Vorsicht-Report eingeschaltet ist, wird in einer separaten
+        Zeile zusaetzlich die Vorsicht sowie bei Sehern die Fluchtrichtung
+        gemeldet. Die Ausgabe ist unabhaengig von dem LP/KP/Gift-Report
+        und findet wie bei diesem nur dann statt, wenn eine Aenderung an
+        Vorsicht oder Fluchtrichtung eingetreten ist, dann aber werden beide
+        Werte gemeinsam ausgegeben.
+
+        "report senden" kann zum Testen verwendet werden oder wenn der
+        Client nach dem Verbinden alle Daten einmal braucht. (Nutzt man
+        GMCP, werden die Daten per GMCP gesendet.) 
+
+HINWEISE:
+        - Die Anzeige der LP ist sofort nach Charaktererstellung verfuegbar.
+        - Die Anzeige der KP ist erst nach Abschluss der Quest "Hilf den 
+          Gnarfen" verfuegbar. Solange dies nicht der Fall ist, werden
+          nicht die KP, sondern die Zeichen ### angezeigt.
+        - Die Anzeige des Giftstatus ist erst nach Abschluss der Quest
+          "Katzenjammer" verfuegbar. Solange dies nicht der Fall ist,
+          wird der Giftstatus als "(nicht verfuegbar)" gemeldet.
+        - Die Anzeige der Vorsicht ist erst nach Abschluss der Quest 
+          "Schrat kann nicht einschlafen" verfuegbar. Mit Erreichen des
+          Seher-Status zeigt diese Option zusaetzlich die eingestellte
+          Fluchtrichtung an. Sehern, die diese Quest nicht abgeschlossen
+          haben, steht keines von beiden zur Verfuegung. Spielercharakteren
+          wird die Fluchtrichtung bis zum Erreichen des Seherstatus als
+          "(nicht verfuegbar)" gemeldet.
+        - Nach dem Bestehen einer der genannten Quests muss ein bereits
+          eingeschalteter Report einmal aus- und wieder eingeschaltet
+          werden, um die Aenderung zu aktivieren.
+        - Der Vorsicht-Report laesst sich auch alleine benutzen, jedoch 
+          muessen zuvor alle Reportfunktionen mit "report aus" abgeschaltet
+          werden, um danach den Vorsicht-Report separat einzuschalten.
+
+BEISPIELE:
+        Angenommen, ein Spieler, der zunaechst mit "report ein" den LP-/KP-
+        und Gift-Report eingeschaltet hat und gerade gegen eine gefaehrliche
+        Spinne kaempft, habe bereits einige Treffer erlitten und auch einige
+        KP fuer Zaubersprueche ausgegeben und daher aktuell nur noch
+        154 LP, 187 KP und sei nicht vergiftet. Wird er nun von der Spinne
+        gebissen und vergiftet, wird diese Reportmeldung ausgegeben:
+            LP: 154, KP: 187, Gift: leicht.
+        Wenn die Spinne bei diesem Biss aber gleich auch Schaden verursacht,
+        und der Spieler 6 LP verliert, wuerde sofort ein weiterer Report
+        ausgeloest:
+            LP: 148, KP: 187, Gift: leicht.
+        Wenn der Spieler jedoch nicht gerne questet und ihm daher nur der
+        LP-Report zur Verfuegung steht, wuerde nur eine der beiden Aenderungen
+        gemeldet, naemlich die LP-Aenderung, und somit folgender Report
+        ausgegeben:
+            LP: 148, KP: ###, Gift: (nicht verfuegbar).
+
+SIEHE AUCH:
+        vorsicht, fluchtrichtung, leben, heilung, kampf
+
+LETZTE AENDERUNG:
+   09.01.2015, Zesstra
+
diff --git a/doc/pcmd/rfluestere b/doc/pcmd/rfluestere
new file mode 100644
index 0000000..e8247a8
--- /dev/null
+++ b/doc/pcmd/rfluestere
@@ -0,0 +1,27 @@
+
+rfluestere
+---------
+
+ KOMMANDO:
+    rfluestere <wem> zu <text>
+
+ ARGUMENTE:
+
+     <wem>
+        Das Lebwesen, dem Du aus der Ferne etwas zufluestern willst
+     <text>
+        Der zu fluesternde Text
+
+ BESCHREIBUNG:
+    Gibt eine Mitteilung an das angegebene Lebewesen aus, die niemand anderes
+    hoeren kann. Das Lebewesen muss sich in einem anderen Raum befinden.
+    Dieser Befehl ist fast aehnlich zum 'teile (mit)'-Befehl, allerdings kann
+    letzterer in einer TM-History beim Sender und/oder Empfaenger gespeichert
+    werden, waehrend dies bei 'rfluestere' nicht passieren kann.
+
+ SIEHE AUCH:
+    sage, frage, antworte, teile (mit), gespraech, rufe, ebenen, weg,
+    ignoriere, kobold, tmhist
+
+ LETZTE AeNDERUNG:
+    10.08.2008, Zesstra
diff --git a/doc/pcmd/rieche b/doc/pcmd/rieche
new file mode 100644
index 0000000..60c4a82
--- /dev/null
+++ b/doc/pcmd/rieche
@@ -0,0 +1,32 @@
+
+riech(e) (an)
+--------------
+
+KOMMANDO
+	riech(e)
+	riech(e) <was> [an <objekt> | am <objekt>] [in mir | im raum]
+
+ARGUMENTE
+	<objekt>
+	  Ein Gegenstand bzw. Lebewesen, an dem riechen will.
+
+BESCHREIBUNG
+	`riech' bzw. `rieche' ist das allgemeine Kommando, um Gerueche
+	an Gegenstaenden, Lebewesen oder in Raeumen wahrzunehmen. Hierbei
+	laesst `riech(e)' ohne Argumente den Spieler die Hauptgerueche in
+	dem Raum wahrnehmen, in dem er sich derzeit befindet. An allen
+	anderen Gegenstaenden bzw. Lebewesen im selben Raum oder im eigenen
+	Inventar kann man mit `riech(e) (an)' ebenfalls Gerueche
+	wahrnehmen. Nicht immer kann man jedoch etwas Besonderes riechen.
+
+	Da bei Objekten mit gleichen Geruchsarten zuerst an im Raum
+	liegenden Objekt gerochen wird, kann man an Sachen, die man bei
+	sich traegt, auf alle Faelle mit dem Zusatz `in mir' riechen.
+
+SIEHE AUCH:
+  bezugspunkt
+  schau, lausche, taste, lies
+
+LETZTE AENDERUNGEN:
+  18.01.2013, Zesstra
+
diff --git a/doc/pcmd/rknuddle b/doc/pcmd/rknuddle
new file mode 100644
index 0000000..046aee8
--- /dev/null
+++ b/doc/pcmd/rknuddle
@@ -0,0 +1,21 @@
+
+rknuddle
+--------
+
+ KOMMANDO:
+    rknuddle <name>
+
+ ARGUMENTE:
+
+     <name>
+        Ein Spieler, der nicht im gleichen Raum ist
+
+ BESCHREIBUNG:
+    Du knuddelst einen [bestimmten] Spieler, der sich nicht im gleichen Raum
+    befindet, in dem Du Dich aufhaeltst.
+
+ SIEHE AUCH:
+    verben, rwinke
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/rufe b/doc/pcmd/rufe
new file mode 100644
index 0000000..a5c1217
--- /dev/null
+++ b/doc/pcmd/rufe
@@ -0,0 +1,27 @@
+
+rufe
+----
+
+ KOMMANDO:
+    rufe <text>
+
+ ARGUMENTE:
+
+     <text>
+        Der zu rufende Text
+
+ BESCHREIBUNG:
+    Du rufst so laut <text>, dass man Dich im ganzen MorgenGrauen hoeren kann.
+    Da das MorgenGrauen allerdings ziemlich gross ist, verbraucht ein Ruf ein
+    gewisses Mass an geistiger Energie (sprich: Magiepunkten).
+
+    Rufe werden im allgemeinen als stoerend empfunden; man sollte lieber die
+    Ebenen benutzen. Diese sind nach Themen eingeteilt, und ihre Nutzung ist
+    kostenlos.
+
+ SIEHE AUCH:
+    sage, frage, antworte, fluestere, teile (mit), gespraech, ebenen, mrufe,
+    weg, ignoriere, kobold
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/rwinke b/doc/pcmd/rwinke
new file mode 100644
index 0000000..ea9bf33
--- /dev/null
+++ b/doc/pcmd/rwinke
@@ -0,0 +1,21 @@
+
+rwinke
+------
+
+ KOMMANDO:
+    rwinke <name>
+
+ ARGUMENTE:
+
+     <name>
+        Ein Spieler, der nicht im gleichen Raum ist
+
+ BESCHREIBUNG:
+    Du winkst einem [bestimmten] Spieler zu, der sich nicht im gleichen Raum
+    befindet, in dem Du Dich aufhaeltst.
+
+ SIEHE AUCH:
+    verben, rknuddle
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/sage b/doc/pcmd/sage
new file mode 100644
index 0000000..e81779e
--- /dev/null
+++ b/doc/pcmd/sage
@@ -0,0 +1,23 @@
+
+sage
+----
+
+ KOMMANDO:
+    sage <text>
+
+ ARGUMENTE:
+
+     <text>
+        Was man so zu sagen hat
+
+ BESCHREIBUNG:
+    Der Text <text> wird an alle Spieler im selben Raum ausgegeben. Ueberlange
+    Zeilen werden umgebrochen, und vor jede Zeile wird noch
+    `<Dein_Name> sagt: ' gesetzt.
+
+ SIEHE AUCH:
+    frage, antworte, fluestere, teile (mit), gespraech, rufe, ebenen, weg,
+    ignoriere, kobold, klingelton, senderwiederholung
+
+ LETZTE AeNDERUNG:
+    01.07.2007 von Ennox
diff --git a/doc/pcmd/schau b/doc/pcmd/schau
new file mode 100644
index 0000000..a7a6f6d
--- /dev/null
+++ b/doc/pcmd/schau
@@ -0,0 +1,72 @@
+
+schau (an)
+----------
+
+ KOMMANDO:
+    schau
+    schau -f | genau
+    schau -k | kurz
+    schau [-f|genau] <was> [an <objekt> | am <objekt>] [in mir | im raum] an
+    untersuche <was> [an <objekt> | am <objekt>] [in mir | im raum]
+
+ ARGUMENTE:
+
+     <was>
+        Ein Gegenstand oder Detail
+     <objekt>
+        Ein weiterer Gegenstand (kein Detail)
+
+ BESCHREIBUNG:
+    (Statt `untersuche' kann man auch `unt', `betrachte' oder `betr'
+    verwenden.)
+
+    `schau' ist das allgemeine Kommando, um Gegenstaende und Raeume zu
+    untersuchen. `schau' ohne Argumente gibt eine Beschreibung des Raumes, in
+    dem der Spieler sich befindet. Alle anderen Gegenstaende, Monster und
+    Spieler im selben Raum oder im eigenen Inventar koennen mit `schau (an)'
+    untersucht werden.
+
+    `schau' muss aber nicht unbedingt alle Geheimnisse des Gegenstandes
+    verraten.
+
+    `unt[ersuche]' bzw. `betr[achte]' macht genau dasselbe, ist aber manchen
+    vielleicht lieber.
+
+    Da bei Objekten mit gleicher Benennung zuerst das im Raum Liegende
+    untersucht wird, kann man Sachen, die man bei sich traegt, auf alle Faelle
+    mit dem Zusatz `in mir' untersuchen.
+
+    Betrachtet man ein <objekt> im Raum, so wird es als neuer Bezugspunkt fuer
+    weitere Betrachtungen genommen. D.h. man kann als naechstes ein Detail an
+    dem Objekt betrachten, ohne eingeben zu muessen `unt <detail> an
+    <objekt>', sondern es reicht dann `unt <detail>'. Wird das Detail an dem
+    eben betrachteten Objekt nicht gefunden, so wird der Bezugspunkt wieder
+    auf die Umgebung und die Objekte im Spieler gesetzt.
+    
+    Die Option '-f' bzw. 'genau' sorgt dafuer, dass gleichartige Objekte
+    bei der Langbeschreibung des Raumes oder der Betrachtung eines Behaelters
+    nicht zusammengefasst werden. 
+    Das kann nuetzlich sein, wenn verschiedene Objekte mit gleicher
+    Bezeichnung (beispielsweise verschiedene Schwerter) im Raum liegen
+    und man wissen will, welches davon man mit 'nimm schwert 2' nun
+    wirklich nimmt.
+    
+    'schau -k' oder 'schau kurz' geben die Kurzbeschreibung der aktuellen
+    Umgebung aus, wie sie auch bei Bewegungen im Kurz-Modus erscheint.
+
+    'schau' ohne ein Argument loescht das sog. Bezugsobjekt.
+
+ ACHTUNG:
+    Manche Raeume definieren ihr eigenes Verb: `untersuche'. Hier koennen
+    `unt' und `untersuche' zu *verschiedenen* Ergebnissen fuehren. Sollte euch
+    das auffallen, setzt bitte mit "fehler" einen Fehler in diesem Raum / an
+    dem Objekt ab.
+
+ SIEHE AUCH:
+    bezugspunkt
+    rieche, lausche, taste, lies
+    kurz, ausgaenge
+
+ LETZTE AeNDERUNG:
+   20.02.2013, Zesstra
+
diff --git a/doc/pcmd/schlafe b/doc/pcmd/schlafe
new file mode 100644
index 0000000..c55c63c
--- /dev/null
+++ b/doc/pcmd/schlafe
@@ -0,0 +1,37 @@
+
+schlafe ein
+-----------
+
+ KOMMANDO:
+    schlafe ein
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Unterbricht die Verbindung zum Spiel. Alle Gegenstaende, die man bei der
+    Eingabe des Kommandos bei sich hat, behaelt man. Beim Neu-Einloggen kommt
+    man normalerweise an der gleichen Stelle heraus, an der man das Spiel
+    verlassen hat. Von dieser Regel kann es aber auch Ausnahmen geben, von
+    denen die wichtigste die Seherhaeuser sind: schlaeft man in einem
+    Seherhaus ein, wacht man vor dem Haus wieder auf (Ausnahme von der
+    Ausnahme: der Hausbesitzer selbst ist davon nicht betroffen).
+    Grund fuer diese Massnahme ist, dass Spieler so nicht beabsichtigt
+    oder unbeabsichtigt im Seherhaus eingeschlossen werden koennen.
+
+    ACHTUNG: Falls das Spiel abstuerzt oder neu gestartet wird, verliert man
+    alle seine Gegenstaende und startet beim Einloggen wieder an seinem
+    lokalen Startpunkt!
+    (In der Regel gibt es dann aber eine (kleine) Entschaedigung fuer die
+    verlorenen Gegenstaende (ohne Gewaehr). )
+
+    ACHTUNG: Abenteuer koennen unter Umstaenden nach dem Wiederaufwachen nicht
+    mehr weitergespielt werden.
+
+                           BENUTZUNG AUF EIGENE GEFAHR!
+
+ SIEHE AUCH:
+    speichern, ende, keepalive
+
+ LETZTE AeNDERUNG:
+    Sat, 04.01.2003, 14:45:00 von Wargon
diff --git a/doc/pcmd/selbstloeschung b/doc/pcmd/selbstloeschung
new file mode 100644
index 0000000..6832023
--- /dev/null
+++ b/doc/pcmd/selbstloeschung
@@ -0,0 +1,25 @@
+KOMMANDO:
+	Selbstloeschung
+
+ARGUMENTE:
+	keine
+
+BESCHREIBUNG:
+	Der Charakter wird unwiderruflich aus dem Spiel entfernt. Auch evtl.
+	noch vorhandene Briefe werden entfernt.
+	Wenn man Seher oder Magier ist, wird der Name des Charakters
+	zusaetzlich gebanisht, damit sich nicht aus Versehen ein Neuling mit
+	einem beruehmten oder aber auch beruechtigten Namen einloggt, was
+	sicher zu einiger Verwirrung fuehren wuerde.
+	Will man nur auf absehbare (oder auch unabsehbare) Zeit nicht mehr 
+	spielen, so sollte man besser den Befehl 'Spielpause' benutzen.
+
+	Als letzten Vorschlag: Wenn Du Dich zu diesem Schritt entschlossen hast,
+	ueberlege Dir, ob Du den Mitspielern, die Du kennst, nicht noch eine
+	Mitteilung schicken magst, sie koennten sich sonst Sorgen machen. 
+
+SIEHE AUCH:
+	spieldauer, spielpause, loeschskript
+
+----------------------------------------------------------------------------
+Letzte Aenderung: 14.02.2007 von Zesstra
diff --git a/doc/pcmd/senderwiederholung b/doc/pcmd/senderwiederholung
new file mode 100644
index 0000000..0addfa9
--- /dev/null
+++ b/doc/pcmd/senderwiederholung
@@ -0,0 +1,32 @@
+KOMMANDO:
+	senderwiederholung [<Status>]
+
+ARGUMENTE:
+	<Status>
+             : aktuellen Status anzeigen  
+	  EIN: Senderwiederholung einschalten.
+	  AUS: Senderwiederholung ausschalten.
+
+BESCHREIBUNG:
+        Bei der Kommunikation per teile mit und sag wird der Sender vor dem
+        mitgeteilten Text dargestellt. Bisher erfolgte das vor jeder Zeile.
+        Mit 'senderwiederholung aus' wird der Sender nur noch einmal darge-
+        stellt. Entweder am Anfang der Zeile, wenn es nur eine Zeile ins-
+        gesamt ist. Oder vor dem Text, wenn es mehrere Zeilen sind.
+
+BEISPIEL:
+         'senderwiederholung ein' (standard)
+         Ennox teilt Dir mit: das ist ein
+         Ennox teilt Dir mit: laengerer Beispieltext
+         Ennox teilt Dir mit: fuer 'senderwiederholung'
+
+         'senderwiederholung aus'
+         Ennox teilt Dir mit: 
+          das ist ein laengerer Beispieltext 
+          fuer 'senderwiederholung'
+
+SIEHE AUCH:
+	teile mit, sage, klingelton, grafik aus, ebenen
+
+LETZTE AENDERUNG:
+   16. Mai 2007  Ennox
diff --git a/doc/pcmd/sonnenfehler b/doc/pcmd/sonnenfehler
new file mode 100644
index 0000000..ddd4cba
--- /dev/null
+++ b/doc/pcmd/sonnenfehler
@@ -0,0 +1,25 @@
+sonnenfehler
+------------
+
+KOMMANDO:
+     sonnenfehler <text>
+   
+ARGUMENTE:
+     <text>
+        Eine Mitteilung
+
+BESCHREIBUNG:
+     Wenn du als Dunkelelf meinst, dass die Sonne in dem (Innen)raum,
+     in dem du dich gerade befindest nicht angebracht ist, dann setz
+     doch bitte eine kurze Meldung ab.
+
+     Diese Fehler werden ausserhalb der Region gesammelt und bearbeitet,
+     eine sehr lange Abwesenheit des RMs sollte dich also nicht hindern
+     den Fehler zu melden.
+
+SIEHE AUCH:
+     typo, idee, detail, fehler
+
+LETZTE AeNDERUNG:
+     26. Sep 2010, Gloinson
+
diff --git a/doc/pcmd/speichern b/doc/pcmd/speichern
new file mode 100644
index 0000000..e131bfe
--- /dev/null
+++ b/doc/pcmd/speichern
@@ -0,0 +1,23 @@
+
+speichern
+---------
+
+ KOMMANDO:
+    speichern
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Sichere alle Daten, die Deinen Charakter betreffen. Dieses Kommando wird
+    beim Ausloggen und regelmaessig automatisch durchgefuehrt. Eigentlich ist
+    dieses Kommando nur fuer besonders aengstliche Spieler, allerdings kann es
+    bei einem Absturz des Spiels auch sehr sinnvoll sein.
+
+    Eine Restaurierung findet nur statt, wenn Du Dich ins Spiel einloggst.
+
+ SIEHE AUCH:
+    ende, schlafe (ein)
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/spieldauer b/doc/pcmd/spieldauer
new file mode 100644
index 0000000..026875b
--- /dev/null
+++ b/doc/pcmd/spieldauer
@@ -0,0 +1,32 @@
+KOMMANDO:
+	Spieldauer <Dauer> Minute[n] fuer <Anzahl> Tag[e]
+	Spieldauer <Dauer> Stunde[n] fuer <Anzahl> Tag[e]
+
+ARGUMENTE:
+	<Dauer>
+	  Dauer der Spielzeit pro Tag in Minuten oder Stunden
+	<Anzahl>
+	  Dauer der Spielzeitbeschraenkung in Tagen
+
+BESCHREIBUNG:
+	Wenn man unbedingt einmal eine Mudpause einlegen muesste, weil man
+	sich um RL-Dinge wie Klausuren, Diplomarbeiten und aehnliche
+	Nebensaechlichkeiten zu kuemmern hat, und wenn man genau weiss, dass
+	man sich aus eigenem Willen nicht vom Einloggen fernhalten kann, so
+	kann man sich mit 'Spielpause' eine Zwangspause genehmigen.
+	Weniger drastisch wirkt der Befehl 'spieldauer'. Hiermit schraenkt
+	man die pro Tag zur Verfuegung stehende Zeit im MUD ein. Somit kann
+	man jeden Tag noch schnell seine Mails lesen oder kurz Freunde
+	begruessen. Ist die angegebene Zeit um, so wird man automatisch aus
+	dem MUD geworfen und kann sich erst am naechsten Tag wieder
+	einloggen. Gibt man als Anzahl der Tage 0 ein, so wird die
+	Beschraenkung abgeschaltet. Da man damit die Beschraenkung jederzeit
+	wieder aufheben kann, gehoert also trotzdem viel Disziplin dazu,
+	diese einzuhalten. Der anfangs erwaehnte Befehl 'Spielpause' ist
+	also deutlich wirkungsvoller.
+
+SIEHE AUCH:
+	Spielpause, Selbstloeschung
+
+----------------------------------------------------------------------------
+15.11.2015, Zesstra
diff --git a/doc/pcmd/spielpause b/doc/pcmd/spielpause
new file mode 100644
index 0000000..7a3f1aa
--- /dev/null
+++ b/doc/pcmd/spielpause
@@ -0,0 +1,34 @@
+KOMMANDO:
+	Spielpause <Anzahl> Tag[e]
+	Spielpause bis <Datum>
+
+ARGUMENTE:
+	<Anzahl>
+	  Dauer der Spielpause in Tagen
+	<Datum>
+	  Datum, bis zu dem die Spielpause gelten soll.
+	  Im Format tt.mm.[jj]
+
+BESCHREIBUNG:
+	Wenn man unbedingt einmal eine Mudpause einlegen muesste, weil man
+	sich um RL-Dinge wie Klausuren, Diplomarbeiten und aehnliche
+	Nebensaechlichkeiten zu kuemmern hat, und wenn man genau weiss, dass
+	man sich aus eigenem Willen nicht vom Einloggen fernhalten kann, so
+	kann man sich mit diesem Befehl eine Zwangspause genehmigen.
+	Der Tag, an dem man sich zur Spielpause entschliesst, wird in die
+	Laenge der Pause mit eingeschlossen. Mit `Spielpause 1 Tag' kann man
+	sich also am naechsten Tag wieder einloggen.
+	Wenn man nicht weiss, wie lang die Spielpause sein soll, kann man
+	als Anzahl -1 angeben. Um sich wieder einloggen zu koennen, muss man
+	dann allerdings einem Gott oder Erzmagier Bescheid sagen (mit Hilfe
+	eines Gastes. ;)
+	Die Spielpause tritt mit dem naechsten Ausloggen in Kraft. Dabei ist
+	es egal, ob man sich mit 'Ende' oder 'Schlafe ein' ausloggt.
+	Entscheidet man sich, doch keine Pause antreten zu wollen, so kann
+	man die Aktion mit 'Spielpause 0 Tage' wieder rueckgaengig machen.
+
+SIEHE AUCH:
+	spieldauer, selbstloeschung, loeschskript
+
+----------------------------------------------------------------------------
+Last modified: Mon Okt 16 20:32:44 2000 by Silvana
diff --git a/doc/pcmd/spotte b/doc/pcmd/spotte
new file mode 100644
index 0000000..e70a1cd
--- /dev/null
+++ b/doc/pcmd/spotte
@@ -0,0 +1,20 @@
+spotte
+------
+
+ KOMMANDO:
+    spotte <text>
+    spotte :<emote>
+
+ ARGUMENTE:
+    <text>
+        Was man so ueber seine Opfer laestern moechte.
+
+ BESCHREIBUNG:
+    Mit dem Kommando "spotte" kann man auf dem Moerder-Kanal seine Gefuehle
+    zum gerade erlegten Monster ausdruecken. Das klappt natuerlich nur bei
+    selbst erlegten Monstern und auch nur als direkte Antwort auf die letzten
+    Worte des NPCs.
+
+ SIEHE AUCH:
+    ebenen, toete
+
diff --git a/doc/pcmd/spruchermuedung b/doc/pcmd/spruchermuedung
new file mode 100644
index 0000000..004def3
--- /dev/null
+++ b/doc/pcmd/spruchermuedung
@@ -0,0 +1,44 @@
+
+spruchermuedung
+--------
+
+KOMMANDO:
+    spruchermuedung <alt | neu | normal>
+
+ARGUMENT:
+    alt:    schaltet die ungenauere alte Spruchermuedung ein
+
+    neu:
+    normal: schaltet die neue (normale) Spruchermuedung ein
+
+BESCHREIBUNG:
+    Zaubersprueche (im weitesten Sinne, also auch z.B. wunder bei Klerikern
+    und Sonderangriffe bei Kaempfern erzwingen in der Regel eine kurze
+    (Ermuedungs-)Pause, bis man den naechsten Spruch sprechen kann.
+    Diese Pausen waren auch technischen Gruenden frueher immer Vielfache von
+    2s und kuerzere Pausen gab es nicht. Ausserdem begannen diese Pausen immer
+    zum Beginn einer Kampfrunde.
+    Seit einiger Zeit sind diese Pausen grundsaetzlich sekundengenau. Einige
+    Spieler finden dies jedoch zu ungewohnt. Diese koennen mit diesem Befehl
+    einen alternativen Modus fuer die Spruchermuedung einschalten, welcher das
+    alte Verhalten weitgehend wiederherstellt.
+
+    Aber: benutzt man die alte Spruchermuedung, werden alle Ermuedungspausen
+          auf Vielfache von 2s aufgerundet, d.h. sie werden ggf. laenger. Dies
+          gilt auch fuer etwaige Mali (z.B. wird aus einem Malus von 1s u.U.
+          ein Malus von 2s) und etwaige Boni sind oft unwirksam, weil sie auf
+          Vielfache von 2s abgerundet werden (z.B. -1s auf 0).
+          Ausserdem sind Sprueche auf Grundlage der sekundengenauen
+          Ermuedungspausen berechnet/balanciert.
+
+BEMERKUNGEN:
+    Kann nicht im Kampf oder vor Ablauf von 3min nach einem Kampf geaendert
+    werden.
+
+BEISPIELE:
+    spruchermuedung alt -> Ermuedungspausen werden 2s aufgerundet
+    spruchermuedung neu -> sekundengenaue Ermuedungspausen
+
+LETZTE AeNDERUNG:
+2010-03-02, Zesstra
+
diff --git a/doc/pcmd/stecke b/doc/pcmd/stecke
new file mode 100644
index 0000000..d2a0ca1
--- /dev/null
+++ b/doc/pcmd/stecke
@@ -0,0 +1,31 @@
+
+stecke
+------
+
+ KOMMANDO:
+    stecke <objekt> in <behaelter>
+    stecke <waffe> weg/zurueck
+
+ ARGUMENTE:
+
+     <objekt>
+        Ein beliebiges Objekt in Deiner Reichweite
+     <behaelter>
+        Ein Behaelter
+     <waffe>
+        Die Waffe, die Du gezueckt hast
+
+ BESCHREIBUNG:
+    `stecke (in)' ist das allgemeine Kommando, um Gegenstaende in einem
+    Behaelter zu deponieren. Behaelter koennen nicht alles aufnehmen, aber
+    meist ist es leichter, schwere Gegenstaende in einem Behaelter zu
+    transportieren.
+
+    `stecke (weg)' ist das allgemeine Kommando, um zum Kampf gezueckte Waffen
+    wieder abzulegen.
+
+ SIEHE AUCH:
+    nimm, zuecke
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/stop b/doc/pcmd/stop
new file mode 100644
index 0000000..89b68cd
--- /dev/null
+++ b/doc/pcmd/stop
@@ -0,0 +1,22 @@
+
+stop
+----
+
+ KOMMANDO:
+    stop [<gegner>]
+
+ ARGUMENTE:
+
+     <gegner> (optional)
+        Einer Deiner Gegner
+
+ BESCHREIBUNG:
+    Du hoerst auf, Deine Gegner (oder nur den Gegner <gegner>) zu verfolgen.
+    Allerdings kann man nicht aufhoeren, verfolgt zu werden.
+
+ SIEHE AUCH:
+     Verwandt: toete
+     Anderes:  kaempfen, heilung, leben, tod 
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/stty b/doc/pcmd/stty
new file mode 100644
index 0000000..6d5d0c4
--- /dev/null
+++ b/doc/pcmd/stty
@@ -0,0 +1,44 @@
+
+stty
+----
+
+ KOMMANDO:
+    stty [<typ>]
+    stty reset
+
+ ARGUMENTE:
+
+     <typ>
+        Terminaltyp: `dumb', `vt100' oder `ansi'
+
+ BESCHREIBUNG:
+    Mit diesem Kommando kannst Du einstellen, an was fuer einem Terminal Du
+    sitzt. Folgende Typen werden momentan unterstuetzt:
+
+     `ansi'    das Terminal versteht Farbcodes und Hervorhebungen wie
+               Fettschrift
+     `vt100'   das Terminal ist einfarbig, kennt aber Fettschrift u.ae.
+     `dumb'    das Terminal versteht keine Hervorhebungen
+
+    Der Terminaltyp wird u.a. bei Kommandos wie "wer" benutzt um bestimmte
+    Woerter oder Phrasen hervorzuheben.
+
+    Nach dem Einstellen des Terminaltyp sollte bei `vt100' und `ansi' eine
+    Ausgabe erfolgen, welche die moeglichen Textattribute oder Farben
+    darstellt. Bei ANSI wird das Ganze von einem lesbaren Text gefolgt. Sollte
+    also der folgende Satz
+
+    Sollte dieser Text hier nicht richtig lesbar sein, benutze
+    das Kommando stty reset!
+
+    nicht erscheinen oder nur schwer lesbar sein, sollte man mit stty reset
+    die Farben wiederherstellen oder ein anderes Terminal einstellen.
+
+ ACHTUNG:
+    Manche Terminals sind u.U. nicht ganz ANSI kompatibel, was dazu fuehrt,
+    dass die Farbe nicht ausgeschaltet wird. Sollte soetwas passieren bitte
+    das Kommando stty reset benutzen, welches einen weissen Hintergrund und
+    schwarzen Vordergrund einstellt.
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/suche b/doc/pcmd/suche
new file mode 100644
index 0000000..e436c44
--- /dev/null
+++ b/doc/pcmd/suche
@@ -0,0 +1,43 @@
+
+suche
+----------
+
+ KOMMANDO:
+    suche <was>
+    such  <was>
+    durchsuche <was>
+
+ ARGUMENTE:
+
+     <was>
+        Ein Gegenstand oder Detail
+
+ BESCHREIBUNG:
+    Nicht alle Geheimnisse dieser Welt offenbaren sich auf den ersten Blick.
+    Bei einigen muss mein ein zweites mal hinschauen. Manche Dinge verbergen 
+    sich aber auch diesem genaueren Blick. Genau Dann ist dieser Befehl 
+    sinnvoll.
+
+    Sicher bist Du schonmal auf einem Weg oder in einem Wald ueber einen 
+    Laubhaufen gestolpert und hast Dich gefragt, welche Schaetze er wohl 
+    birgt. Ein gezieltes 'suche laub' oder 'durchsuche laub' bringt da so
+    manch nuetzliches zum Vorschein.
+
+ ACHTUNG:
+    Leider ist das Verhalten von 'such', 'suche' und 'untersuche' jeweis 
+    abhaengig von der Kreativitaet des programmierenden Magiers. Auch wenn 
+    die drei Befehle aehnlich klingen, heisst das nicht, dass sie auch das 
+    gleiche machen. 
+
+    Manchmal findet man etwas, wenn man 'durchsuche laub' macht, waehrend 
+    'suche laub' nur ein lahmes 'Du suchst, findest aber nichts.' zum 
+    Vorschein bringt.
+
+    Oft findet man aber in der Raumbeschreibung Hinweise, welcher Begriff 
+    hier passend ist.
+
+ SIEHE AUCH:
+    rieche, lausche, schau, untersuche
+
+ LETZTE AeNDERUNG:
+    Mon, 01.08.2002, 14:00:00 von Vanion
diff --git a/doc/pcmd/syntaxsammlung b/doc/pcmd/syntaxsammlung
new file mode 100644
index 0000000..ed5bd97
--- /dev/null
+++ b/doc/pcmd/syntaxsammlung
@@ -0,0 +1,45 @@
+
+syntaxsammlung
+-------------
+
+ KOMMANDO:
+    syntaxsammlung [ja | nein]
+
+ ARGUMENTE:
+    nichts, ja oder nein
+
+ BESCHREIBUNG:
+    Um Befehle im MG flexibler zu machen und Spieler und Magier zu entlasten,
+    indem das Spiel besser die Absicht des Spielers bei einem Befehl erkennt,
+    sammeln wir (anonyme) Statistiken ueber Befehle, welche von Spielern
+    benutzt werden, sofern Spieler damit einverstanden sind.
+    Mit diesem Befehl kannst Du also mithelfen, Syntaxen im MG zu verbessern.
+
+    Wenn Du einverstanden bist, speichern wir anonym (d.h. ohne Deinen
+    Charnamen), welche Deiner Befehle erfolgreich und nicht erfolgreich waren.
+    Uebliche Kommunikationsbefehle (z.B. sage, teile-mit, rfluester, ebenen,
+    teamrufe, mrufe, rufe) werden dabei nicht gespeichert.
+    
+    Mit 'syntaxsammlung ja' kannst Du die Speicherung einschalten, mit
+    'syntaxsammlung nein' kannst Du sie ausschalten.
+
+    Die gesammelten Daten koennen nur von Erzmagiern oder Goettern eingesehen
+    werden. Allerdings kann es sein, dass diese anderen Magiern Beispiele aus
+    der Sammlung zeigen, sofern das Beispiel keinen Rueckschluss auf einen
+    Spieler zulaesst.
+
+    Uebrigens sind vor allem Kommandos beim Forschen interessant, weniger
+    beim Metzeln, Ideln, Chatten etc. (Kommunikation s.o.)
+    Daher werden zur Zeit nur Befehle erfasst, welche mehr als 2 Woerter
+    enthalten.
+
+    Ein Beispiel, was gespeichert wird:
+    Befehl                   |Gegend|Erfolg|Anzahl|FP
+    unt jeden zweiten vorhang|ebene |0     |1     |0
+
+    Erfolg ist alles, was kein Notifyfail ausloest, Anzahl, wie oft diese Syntax
+    schonmal benutzt wurde und FP, ob diese Syntax (irgendwo) einen FP gegeben
+    hat.
+
+ LETZTE AeNDERUNG:
+    18.06.2016, Zesstra
diff --git a/doc/pcmd/taste b/doc/pcmd/taste
new file mode 100644
index 0000000..2d173f8
--- /dev/null
+++ b/doc/pcmd/taste
@@ -0,0 +1,47 @@
+
+taste (ab)
+beruehre
+--------------
+
+KOMMANDO
+    taste
+    taste <was> [an <objekt> | am <objekt>] [in mir | im raum] [ab]
+    beruehre <was> [an <objekt> | am <objekt>] [in mir | im raum]
+
+ARGUMENTE
+    <objekt>
+      Ein Gegenstand bzw. Lebewesen, das man abtasten will.
+
+BESCHREIBUNG
+    "taste" ist das allgemeine Kommando, um (ab)tastbare Details an
+    Gegenstaenden, Lebewesen oder in Raeumen wahrzunehmen. Hierbei
+    laesst "taste" ohne Argumente den Spieler ungezielt im Raum rumtasten, in
+    dem er sich derzeit befindets. Ob man hierbei ueberhaupt irgendwas
+    wahrnimmt, haengt vom Magier des Raumes ab.
+    An allen anderen Gegenstaenden bzw. Lebewesen im selben Raum oder im
+    eigenen Inventar kann man mit "taste (an)" ebenfalls tasten. Nicht immer
+    kann man jedoch etwas (er)tasten.
+
+    Da bei Objekten mit gleichen tastbaren/fuehlbaren Details zuerst an im
+    Raum liegende Objekte abgetastet werden, kann man an Sachen, die man bei
+    sich traegt, auf alle Faelle mit dem Zusatz "in mir" abtasten.
+
+    Ertastbare Details wurden im Morgengrauen erst 2010 standardisiert.
+    Erwartet nicht, dass es in allen Raeumen / an allen Gegenstaenden tastbare
+    Details gibt - sie sind eher selten.
+
+BEISPIELE
+    taste boden ab
+    taste boden (identisch zu "taste boden ab")
+
+    taste rucksack in mir ab
+    taste baumstumpf im raum ab
+    taste schnalle an rucksack in mir ab
+
+SIEHE AUCH:
+    bezugspunkt
+    schau, rieche, lausche, lies
+
+LETZTE AENDERUNGEN:
+    13.03.2016, Zesstra
+
diff --git a/doc/pcmd/teile b/doc/pcmd/teile
new file mode 100644
index 0000000..b5a0c36
--- /dev/null
+++ b/doc/pcmd/teile
@@ -0,0 +1,57 @@
+
+teile (mit)
+-----------
+
+ KOMMANDO:
+    teile <wem> mit <text>
+    teile .[<nr>] mit <text>
+    teile <wem>@<mud> mit <text>
+    (Abkuerzung: tm <wem> <text>)
+    
+    erzaehle <wem> <text>
+    erzaehle .[<nr>] <text>
+    erzaehle <wem>@<mud> <text>
+
+ ARGUMENTE:
+
+     <wem>
+        Name des Spielers, dem Du etwas erzaehlen willst
+     <nr>
+        Eine Nummer in der Mitteilungsgeschichte
+     <mud>
+        Name eines MUDs aus der `muds'-Liste
+     <text>
+        Die Mitteilung selbst
+
+ BESCHREIBUNG:
+    Sendet dem angegebenen Spieler eine Nachricht. Es spielt keine Rolle, wo
+    er sich im Spiel befindet. Mitteilen verbraucht *keine* Magiepunkte.
+
+    Mit `teile <wem>@<mud> mit <text>' kann man auch Spielern in anderen MUDs
+    etwas mitteilen. <mud> muss nicht der komplette Mudname sein; es genuegt
+    auch eine eindeutige Abkuerzung.
+
+    Das Kommando merkt sich, wem Du alles etwas mitteilst. Mit `tmhist' kannst
+    Du Dir ansehen, wem Du schon alles etwas mitgeteilt hast. Fuer weitere
+    Mitteilungen kannst Du anstelle des Namens auch die Nummer angeben, *aber
+    Achtung!* Die Liste wird danach wieder neu sortiert!
+
+    Bearbeitet der Spieler einen Text oder liest er irgendetwas, so werden
+    Mitteilungen u.U. gespeichert (dies wird mitgeteilt). Die gespeicherten
+    Mitteilungen erfaehrt der andere Spieler, wenn er mit dem Lesen oder
+    Bearbeiten fertig ist.
+
+    Sofern Du Deine Teile-mit-History (s. tmhist) eingeschaltet hast, werden
+    aus- und eingehende TMs gespeichert. Hat Dein Gespraechspartner seine
+    History eingeschaltet, wird ein TM, was Du ihm schickst, bei ihm
+    gespeichert. Moechtest Du dies nicht, kannst Du den Befehl 'rfluester'
+    verwenden, bei welchem in keinem Fall der Inhalt eines Gespraechs
+    gespeichert wird.
+
+ SIEHE AUCH:
+    erwidere, sage, frage, antworte, fluestere, gespraech, rufe, ebenen, weg,
+    ignoriere, kobold, muds, senderwiederholung, klingelton, tmhist,
+    rfluestere
+
+ LETZTE AeNDERUNG:
+    09.08.2008, Zesstra
diff --git a/doc/pcmd/telnegs b/doc/pcmd/telnegs
new file mode 100644
index 0000000..5dfcc57
--- /dev/null
+++ b/doc/pcmd/telnegs
@@ -0,0 +1,22 @@
+KOMMANDO:
+        telnegs [einstellung]
+
+ARGUMENTE:
+        einstellung
+          * ein - Schaltet die Anzeige ein
+          * aus - Schaltet die Anzeige aus
+
+          Wird kein Argument angegeben, wird die aktuelle Einstellung
+          angezeigt.
+    
+BESCHREIBUNG:
+          Man kann festlegen, ob man Aenderungen der Fenstergroesse
+          angezeigt bekommen will oder nicht. Dies setzt voraus, dass
+          man ein Telnet benutzt, das Telnetnegotiations unterstuetzt.
+
+
+SIEHE AUCH:
+        zeilen, telnet
+----------------------------------------------------------------------------
+Last modified: Sun Nov  7 23:00:00 1999 by Tiamak
+
diff --git a/doc/pcmd/telnet b/doc/pcmd/telnet
new file mode 100644
index 0000000..7e11a2a
--- /dev/null
+++ b/doc/pcmd/telnet
@@ -0,0 +1,34 @@
+
+telnet
+------
+
+ KOMMANDO:
+    telnet <kommando> <argumente>
+
+ ARGUMENTE:
+
+     <kommando>
+        Eines von den unten angebenen Kommandos, um bestimmte Aspekte der
+        Kommunikation des Muds mit dem Client einzustellen.
+
+     <arguments>
+        Argumente fuer das jeweilige telnet Kommando
+
+ BESCHREIBUNG:
+    Dies ist ein Sammelkommando, um Einfluss auf die Eigenschaften der
+    Kommunikation vom Mud mit dem Client nehmen zu koennen oder sich diese
+    anzeigen zu lassen.
+    Moegliche Kommandos sind:
+    * keepalive
+      Stellt ein, dass regelmaessig Daten vom MG zum Client geschickt werden,
+      damit die Verbindung aufrechterhalten wird.
+      Fuer Details s. Hilfeseite telnet_keepalive
+    * rttime
+      Ausgabe der letzten gemessenen "round-trip time" vom MG zum Client
+      in Mikrosekunden (1s == 1000000 Mikrosekunden)
+
+ SIEHE AUCH:
+    telnegs, telnet keepalive, telnet gmcp
+
+ LETZTE AeNDERUNG:
+    03.02.2013, Zesstra
diff --git a/doc/pcmd/telnet_keepalive b/doc/pcmd/telnet_keepalive
new file mode 100644
index 0000000..eecbfd7
--- /dev/null
+++ b/doc/pcmd/telnet_keepalive
@@ -0,0 +1,42 @@
+
+telnet keepalive
+----------------
+
+ KOMMANDO:
+    telnet keepalive <ein|aus>
+
+ ARGUMENTE:
+
+     <ein>
+        Schaltet das Telnet-Keep-Alive ein.
+     <aus>
+        Schaltet das Telnet-Keep-Alive aus.
+
+ BESCHREIBUNG:
+    Einige Spieler und Magier haben immer mal wieder Probleme, dass sie bei
+    laengerer Inaktivitaet rausfliegen, weil irgendein Router im Netzwerk die
+    inaktive Verbindung abbricht oder weil aus aehnlichen Gruenden die SSL
+    Verbindung terminiert wird. Daher besitzen die Spielershells ein
+    einschaltbares 'Telnet-Keep-Alive'.
+
+    Diese Funktion schickt alle 4 min Daten an eure Clients, die diese
+    nicht anzeigen, aber ihrerseits beantworten/bestaetigen (sofern der Client
+    dies unterstuetzt). So gibt es in der Verbindung zum Mud min. alle 4 min
+    Datenverkehr. Das sollte Verbindungsabbruechen durch Inaktivitaet
+    vorbeugen.
+
+    Bitte schaltet diese Funktion aber nur ein, wenn ihr sie braucht, d.h.
+    ohne sie Probleme mit Verbindungsabbruechen beim Ideln habt.
+    Habt ihr Verbindungsabbrueche, ohne Idle zu sein, wird euch dieses
+    Keep-Alive nichts helfen.
+
+    Ohne Argumente wird der aktuelle Zustand angezeigt.
+
+    Hinweis fuer Magier: Magier im Magiermodus (mschau ein) kommen leider
+    nicht in den Genuss dieses Features, weil sie keinen Heartbeat haben.
+
+ SIEHE AUCH:
+    telnegs, telnet gmcp
+
+ LETZTE AeNDERUNG:
+    08.12.2015, Zesstra
diff --git a/doc/pcmd/titel b/doc/pcmd/titel
new file mode 100644
index 0000000..0921202
--- /dev/null
+++ b/doc/pcmd/titel
@@ -0,0 +1,32 @@
+
+titel
+-----
+
+ KOMMANDO:
+    titel <text>
+    titel 0
+
+ ARGUMENTE:
+
+     <text>
+        Der Titeltext.
+
+ BESCHREIBUNG:
+    Der Text wird als neuer Titel gesetzt. Dabei sollte der Titel nicht mit
+    einem Spielertitel identisch oder einem solchen sehr aehnlich sein. Der
+    uebergebene Text darf ein Backspace (\b) am Anfang enhalten, wenn
+    darauf ein Komma (,) oder ein Hochkomma (') folgt.
+
+    Wird kein Argument uebergeben, so wird der aktuelle Titel geloescht.
+
+    Wird 0 als Argument uebergeben, so wird der Titel durch den aktuellen
+    Gildentitel ersetzt.
+    
+    Das Kommando steht Spielern ab Level 21 zur Verfuegung, sofern sie sich
+    in der Fraternitas Dono Archmagorum diese Faehigkeit beibringen lassen.
+
+ SIEHE AUCH:
+    stufen, presay (Seherkommando)
+
+ LETZTE AeNDERUNG:
+    Fri, 25.05.2007, 19:57:00 - Miril
diff --git a/doc/pcmd/tmhist b/doc/pcmd/tmhist
new file mode 100644
index 0000000..d5f6cf6
--- /dev/null
+++ b/doc/pcmd/tmhist
@@ -0,0 +1,38 @@
+
+tmhist
+-----------
+
+ KOMMANDO:
+    tmhist
+    tmhist aus
+    tmhist namen
+    tmhist ein
+    tmhist <spielername>
+    tmhist <nr>
+
+ BESCHREIBUNG:
+    Ohne Argument wird die aktuelle Teile-mit-History ausgegeben.
+    'tmhist aus' schaltet die Speicherung komplett aus,
+    'tmhist namen' speichert nur die Namen der Gespraechspartner und einige
+    statistische Daten.
+    'tmhist ein' speichert Namen der Gespraechspartner sowie die Inhalte der
+    Gespraeche.
+    'tmhist <name>' gibt das Gespraech mit dem angegebenen Spieler wieder,
+    sofern so ein Gespraech existiert und der Inhalt gespeichert wurde.
+    'tmhist <nr>' gibt das Gespraech mit der Nummer <nr> in der History
+    wieder.
+
+    Die Gespraechsinhalte werden beim Ausloggen mittels 'schlafe ein'
+    innerhalb von ca. 4s geloescht, bei 'ende' sofort. Bricht die Verbindung
+    zum MorgenGrauen ab, werden die Gespraechsinhalte nach 15min geloescht.
+    Es werden max. 20 Gespraeche mit jeweils max. 50 Nachrichten gespeichert.
+
+    Alle mit dem Befehl 'rfluester' mitgeteilten Mitteilungen werden nicht
+    gespeichert.
+
+ SIEHE AUCH:
+    erwidere, sage, frage, antworte, fluestere, gespraech, rufe, ebenen, weg,
+    ignoriere, kobold, muds, senderwiederholung, klingelton, teile, rfluestere
+
+ LETZTE AeNDERUNG:
+    Sa, 09.08.2008, Zesstra
diff --git a/doc/pcmd/toete b/doc/pcmd/toete
new file mode 100644
index 0000000..6893ac6
--- /dev/null
+++ b/doc/pcmd/toete
@@ -0,0 +1,32 @@
+
+toete
+-----
+
+ KOMMANDO:
+    toete <monster>
+    toete alle
+
+ ARGUMENTE:
+
+     <monster>
+        Der zu toetende Gegner
+
+ BESCHREIBUNG:
+    Startet eine Attacke auf das angegebene Monster. Es muss sich im selben
+    Raum befinden. Es ist moeglich, aus einem laufenden Kampf heraus zu
+    fliehen. Wenn man das fliehende Monster faengt, darf man den ersten Schlag
+    ausfuehren (umgekehrt gilt das allerdings auch!).
+
+    Es ist zwar auch moeglich, andere Spieler zu toeten. Dies ist jedoch unter
+    Strafe gestellt, es sei denn, der Kampf findet in extra dafuer
+    vorgesehenen Gebieten statt.
+
+    Mit 'toete alle' greift man ALLE potentiellen Gegner im Raum an, man
+    sollte sich also gut ueberlegen, wo man dieses Kommando einsetzt.
+
+ SIEHE AUCH:
+     Verwandt: stop, angriffsmeldung
+     Anderes:  kaempfen, heilung, leben, tod 
+
+ LETZTE AeNDERUNG:
+    Mon, 25.08.2003, 15:30:00 von Boing
diff --git a/doc/pcmd/ton b/doc/pcmd/ton
new file mode 100644
index 0000000..e61189e
--- /dev/null
+++ b/doc/pcmd/ton
@@ -0,0 +1,26 @@
+KOMMANDO:
+	ton [<Status>]
+
+ARGUMENTE:
+	<Status>
+	  EIN: Ton einschalten.
+	  AUS: Ton ausschalten.
+
+BESCHREIBUNG:
+	Die meisten Terminals koennen einfache Pieptoene erklingen lassen,
+	was auch an einigen Stellen hier im MUD zum Einsatz kommt.
+	Genannt seien hierbei vorrangig 'erwarte' und 'wecke', welche einen
+	Grossteil der zweifelhaften Geraeuschkulisse ausmachen. Nun ist es
+	nicht immer guenstig, die schwer arbeitenden Menschen in einem
+	Rechenzentrum mit offensichtlich nicht studienbezogenen Toenen zu
+	stoeren. Wer nicht die Moeglichkeit hat, diese stoerenden Laute am
+	Computer direkt abzuschalten, dem sei mit diesem Befehl geholfen:
+	  'ton EIN' - Es werden Toene vom MUD erzeugt (Standard).
+	  'ton AUS' - Es werden keine Toene vom MUD erzeugt.
+	'ton' ohne Parameter zeigt den aktuellen Status.
+
+SIEHE AUCH:
+	erwarte, wecke
+
+----------------------------------------------------------------------------
+11.11.2006 Zesstra
diff --git a/doc/pcmd/trage b/doc/pcmd/trage
new file mode 100644
index 0000000..22b1704
--- /dev/null
+++ b/doc/pcmd/trage
@@ -0,0 +1,24 @@
+
+trage
+-----
+
+ KOMMANDO:
+    trage <kleidung>
+
+ ARGUMENTE:
+
+     <kleidung>
+        Das anzulegende Kleidungsstueck
+
+ BESCHREIBUNG:
+    `trage' ist ein Kommando um Kleidung anzulegen. Robuste Kleidung bietet
+    nur dann Schutz gegen Angriffe, wenn sie angezogen wurde.
+
+    Man kann normalerweise von jeder Art Kleidung nur ein Stueck gleichzeitig
+    tragen (zwei Huete uebereinander saehen ja auch etwas laecherlich aus...).
+
+ SIEHE AUCH:
+    zuecke, ziehe (an/aus)
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/tutorial b/doc/pcmd/tutorial
new file mode 100644
index 0000000..a187f96
--- /dev/null
+++ b/doc/pcmd/tutorial
@@ -0,0 +1,28 @@
+
+tutorial
+--------
+
+ BESCHREIBUNG:
+
+   Im Startraum jeder Rasse kann man mit dem Befehl 'tutorial' das
+   Neuspielertutorial beginnen. Im Tutorial bekommt man von einem
+   alten Steinbeisser die wichtigsten Faehigkeiten fuer das Ueberleben
+   im MorgenGrauen beigebracht.
+
+   Es werden: Bewegung, Untersuchen, Kommunikation, Aktionen, Quests,
+   Benutzung von Containern, Lichtquellen, Handel und Kampf behandelt.
+
+   Das Tutorial kann per 'ueberspringe tutorial' verlassen werden, sobald
+   Du das erste Mal dem Steinbeisser begegnet bist. Du hast jederzeit die 
+   Moeglichkeit, es wieder zu beginnen, indem Du den Befehl 'tutorial' in
+   Deinem Startraum eingibst.
+
+   Die Geschwindigkeit der Informationen innerhalb des Tutorials ist
+   mittels 'schneller', 'langsamer', 'pause', 'weiter', 'abbruch' und
+   'neustart' steuerbar.  
+
+SIEHE AUCH: einfuehrung, cicerones, hilfe
+
+ LETZTE AeNDERUNG:
+   07.05.2016, Arathorn
+
diff --git a/doc/pcmd/typo b/doc/pcmd/typo
new file mode 100644
index 0000000..6470489
--- /dev/null
+++ b/doc/pcmd/typo
@@ -0,0 +1,48 @@
+typo
+----
+
+KOMMANDO:
+    typo <text>
+    typo <objekt>:<text>
+
+ARGUMENTE:
+
+    <text>
+        Eine Mitteilung
+    <objekt>
+        Ein Referenzobjekt, das neben Dir liegt oder in Deinem Inventar ist
+
+BESCHREIBUNG:
+    In den Beschreibungen von Raeumen und Objekten kommt es immer wieder mal
+    zu Tippfehlern, die auch die Testphase des Objektes/Gebietes unerkannt
+    ueberstanden haben.
+
+    Wenn Du einen Tippfehler gefunden hast, so kannst Du den Verantwortlichen
+    mit diesem Befehl davon unterrichten.
+
+    Fuer den Magier ist es sehr hilfreich, wenn Du bei in Meldung angibst,
+    wo genau der Tippfehler zu finden ist (also z.B, welches Detail den
+    Fehler enthaelt).
+
+    Zoegere nicht, wenn Du einen Tippfehler findest; Fehlermitteilungen sind
+    *sehr* willkommen!
+
+    Wenn Du einen Typo an einem bestimmten Objekt oder NPC entdeckst, kannst
+    Du dieses/diesen hinter einem Doppelpunkt angeben, damit der fuer das
+    Objekt verantwortliche Magier die Meldung bekommt.
+    Wenn das Objekt nicht zu finden ist, wird die ganze Eingabe als
+    Meldung aufgefasst.
+
+BEMERKUNG:
+    Der Befehl 'typo' bezieht sich bei Nichtangabe eines Bezugsobjekts
+    auf das letzte betrachtete Objekt. Hast Du also ein Objekt untersucht,
+    dann wird die Meldung fuer diese Objekt abgegeben. Hast Du aber den
+    Raum untersucht oder 'schau' eingegeben, wird die Meldung fuer Deinen
+    momentanen Raum abgesetzt.
+
+SIEHE AUCH:
+    bug, idee, detail, bezugsobjekt
+
+LETZTE AeNDERUNG:
+    03.06.2013, Zesstra
+
diff --git a/doc/pcmd/uhrmeldung b/doc/pcmd/uhrmeldung
new file mode 100644
index 0000000..65b4dcf
--- /dev/null
+++ b/doc/pcmd/uhrmeldung
@@ -0,0 +1,33 @@
+
+uhrmeldung
+----------
+
+ KOMMANDO:
+    uhrmeldung [<meldung>]
+
+ ARGUMENTE:
+
+     <meldung>
+        Deine ganz persoenliche Uhrmeldung
+
+ BESCHREIBUNG:
+    Ohne Argumente wird Deine aktuelle Uhrmeldung angezeigt.
+
+    Mit dem Argument <meldung> kann man sich eine eigene Uhrmeldung setzen.
+    Der Platzhalter fuer die Uhrzeit in der Meldung ist `%d'.
+
+    Es darf nur *ein* `%d' in der Meldung vorkommen!
+
+    Mit dem Befehl "uhrmeldung 0" kannst Du Deine Uhrmeldung auf die
+    Standardmeldung zuruecksetzen.
+
+ BEISPIELE:
+
+    > uhrmeldung Schon %d Uhr, und immer noch im MUD.
+
+    Das gibt dann zB. abends um zehn:
+
+    Schon 22 Uhr, und immer noch im MUD.
+
+ LETZTE AeNDERUNG:
+    Sam, 08.03.2008, 13:08:00 von Arathorn
diff --git a/doc/pcmd/ultrakurz b/doc/pcmd/ultrakurz
new file mode 100644
index 0000000..f98bda7
--- /dev/null
+++ b/doc/pcmd/ultrakurz
@@ -0,0 +1,21 @@
+
+ultrakurz
+---------
+
+ KOMMANDO:
+    ultrakurz
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Schaltet auf den "Ultrakurz"-Modus um. Wenn Du Dich in diesem Modus
+    befindest, siehst Du *keine* Raumbeschreibung - nuetzlich, wenn man
+    altbekannte Pfade ablaeuft. Mit den Kommandos `lang' und `kurz' kannst Du
+    diesen Modus wieder beenden.
+
+ SIEHE AUCH:
+    lang, kurz, schau, ausgaenge
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/unalias b/doc/pcmd/unalias
new file mode 100644
index 0000000..2621c13
--- /dev/null
+++ b/doc/pcmd/unalias
@@ -0,0 +1,46 @@
+
+unalias
+-------
+
+ KOMMANDO:
+    unalias <alias>
+    unalias <muster>
+
+ ARGUMENTE:
+
+     <alias>
+        Das zu entfernende Alias
+     <muster>
+        Muster, das die zu entfernenden Aliase beschreibt
+        (Das Muster muss ein sog. 'regular expression' sein.)
+
+ BESCHREIBUNG:
+    (Statt `unalias' kann man auch `unali' verwenden!)
+
+    Es werden ein oder mehrere Aliase geloescht.
+
+    *Achtung!* Es erfolgt keine Sicherheitsabfrage!
+
+ BEISPIELE:
+    Ein einzelnes Alias loeschen:
+
+    > unalias weg
+
+    Alle "tm"-Aliase loeschen (man beachte den . vor dem *):
+
+    > unalias tm.*
+
+    Alle Aliase loeschen, die auf Ziffern liegen:
+
+    > unalias [0-9]
+
+    Alle Aliase loeschen:
+
+    > unalias .*
+
+ SIEHE AUCH:
+    alias
+
+ LETZTE AeNDERUNG:
+   02.10.2012, Zesstra
+
diff --git a/doc/pcmd/url b/doc/pcmd/url
new file mode 100644
index 0000000..5bcad42
--- /dev/null
+++ b/doc/pcmd/url
@@ -0,0 +1,34 @@
+
+url
+---
+
+ KOMMANDO:
+    url [<adresse>]
+
+ ARGUMENTE:
+
+     <adresse> (optional)
+        Die neue Adresse
+
+ BESCHREIBUNG:
+    Wenn Du eine eigene HomePage im Internet hast und sie fuer Deine
+    Mitspieler erreichbar machen willst, so kannst Du das mit diesem
+    Kommando tun.
+
+    Ohne <adresse> wird die aktuelle Adresse angezeigt, ist sie 'keine',
+    so wird die Adresse geloescht.
+
+    Aus <adresse> werden saemtliche HTML-Tags entfernt!
+
+    Zu finden sind diese Informationen:
+
+     http://mg.mud.de/
+     -> Information
+        -> Online
+
+ SIEHE AUCH:
+    Persoenliche Details: email, icq, messenger, ort
+    Sonstiges:            finger
+
+ LETZTE AeNDERUNG:
+    Sun, 05.07.1997, 17:00:00 von Rikus
diff --git a/doc/pcmd/verben b/doc/pcmd/verben
new file mode 100644
index 0000000..8ccda58
--- /dev/null
+++ b/doc/pcmd/verben
@@ -0,0 +1,29 @@
+
+verben
+------
+
+ KOMMANDO:
+    verben
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Mit diesem Befehl werden alle moeglichen Verben der Seele angezeigt. Diese
+    Verben haben in der Regel keinen tiefen Sinn, sondern dienen dazu, eine
+    gewisse Stimmung zu verbreiten.
+
+    Da dies eine ganze Menge an Verben sind, deren Funktion meist eh auf der
+    Hand liegt, existieren fuer sie keine separaten Hilfeseiten.
+
+    Eine kurze Hilfe zu jedem Verb kannst Du jedoch jederzeit mit `<verb> -h'
+    erhalten.
+
+    Oft genutzte Verben sind: `lache', `laechel [an]', `danke', `argl',
+    `grinse', `umarme', `wuschel', ...
+
+ SIEHE AUCH:
+    adverb
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/vorsicht b/doc/pcmd/vorsicht
new file mode 100644
index 0000000..47484ac
--- /dev/null
+++ b/doc/pcmd/vorsicht
@@ -0,0 +1,35 @@
+
+vorsicht
+--------
+
+ KOMMANDO:
+    vorsicht <lp>
+
+ ARGUMENTE:
+
+     <lp>
+        Zahl zwischen 0 und der max. Zahl Deiner Lebenspunkte
+
+ BESCHREIBUNG:
+    Schaltet den `Vorsichtsmodus' ein. Wenn Du Dich im Vorsichtsmodus
+    befindest, rennen Deine Beine mit Dir fort, sobald Deine Lebenspunkte
+    unter den angegebenen Wert gesunken sind, und Du mit dem naechsten Schlag
+    in der naechsten Kampfrunde an der Reihe bist.
+
+    Die Begruessungsschlaege, die man eventuell austeilt oder einsteckt,
+    fallen *nicht* in eine Kampfrunde. Entsprechend wird der Vorsichtsmodus
+    hier auch *nicht* aktiv!
+
+    Der Defaultmodus ist der Prinz-Eisenherz-Modus `vorsicht 0'.
+
+    Der Vorsichtsmodus funktioniert nicht immer. Er kann Dich zwar retten,
+    wenn Du eine langsame Verbindung oder muede Finger besitzt, aber es kann
+    immer passieren, dass ein Monster Dir mehr Lebenspunkte nimmt als Du noch
+    hast. Manchmal finden Deine Beine auch keinen Fluchtweg.
+
+ SIEHE AUCH:
+    toete, report
+    (nur Seher): fluchtrichtung
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/wecke b/doc/pcmd/wecke
new file mode 100644
index 0000000..b0ca4e0
--- /dev/null
+++ b/doc/pcmd/wecke
@@ -0,0 +1,24 @@
+KOMMANDO:
+	wecke <Lebewesen>
+
+ARGUMENTE:
+	<Lebewesen>
+	  Hier gibt man das Lebewesen an, bei welchem man sich bemerkbar
+	  machen moechte.
+
+BESCHREIBUNG:
+	Will man sich bei insbesondere herumlungernden (auch ideln genannt)
+	Spielern bemerkbar machen, so kann einem dieses Kommando
+	weiterhelfen. Es erzeugt beim zu Weckenden nebst einem kleinen Text,
+	wer verzweifelt um Aufmerksamkeit ringt, auch einen kurzen Piepton.
+
+BEMERKUNGEN:
+	Dieser Ton kann sich in gewissen RL-Umgebungen recht stoerend
+	auswirken. Der zu Weckende kann sich jedoch dagegen mit dem Kommando
+	'ton AUS' wehren.
+
+SIEHE AUCH:
+	ton
+
+----------------------------------------------------------------------------
+11.11.2006 Zesstra
diff --git a/doc/pcmd/weg b/doc/pcmd/weg
new file mode 100644
index 0000000..6420fba
--- /dev/null
+++ b/doc/pcmd/weg
@@ -0,0 +1,30 @@
+
+weg
+---
+
+ KOMMANDO:
+    weg [<text>]
+
+ ARGUMENTE:
+
+     <text>
+        Erklaert die Abwesenheit
+
+ BESCHREIBUNG:
+    Ist man einmal fuer laengere Zeit nicht anwesend und kann nicht auf
+    Mitteilungen reagieren, so kann man den Grund mit diesem Kommando kund und
+    zu wissen tun.
+
+    Wenn Dir nun jemand etwas mitteilt, erhaelt er die Meldung
+
+    <name> ist gerade nicht da: <text>
+
+    und braucht sich nicht zu wundern, wieso keine Antwort kommt.
+
+    Ohne Argument wird die Kennzeichnung wieder aufgehoben.
+
+ SIEHE AUCH:
+    teile (mit), erzaehle, wegmeldung
+
+ LETZTE AeNDERUNG:
+    25. Apr 2011 Gloinson
diff --git a/doc/pcmd/wegmeldung b/doc/pcmd/wegmeldung
new file mode 100644
index 0000000..9461e69
--- /dev/null
+++ b/doc/pcmd/wegmeldung
@@ -0,0 +1,19 @@
+
+wegmeldung
+------
+
+ KOMMANDO:
+    wegmeldung <name>
+
+ ARGUMENTE:
+
+     <name>
+        Name eines Spielers
+
+ BESCHREIBUNG:
+    Dieser Befehl gibt die Weg-Meldung eines Spieler aus, wenn dieser eine 
+    hat. Dann muss man nicht jedes mal den Spieler fingern, wenn man wissen
+    will, ob der grad weg ist oder nicht.
+
+ LETZTE AeNDERUNG:
+    Son, 11. Aug 2002, 19:07:00 von Vanion
diff --git a/doc/pcmd/wer b/doc/pcmd/wer
new file mode 100644
index 0000000..57250ce
--- /dev/null
+++ b/doc/pcmd/wer
@@ -0,0 +1,51 @@
+
+wer
+---
+
+ KOMMANDO:
+    wer [<mud>|<option>]
+
+ ARGUMENTE:
+
+     <mud> (optional)
+        Ein MUD-Name aus der `muds'-Liste
+
+    <optionen> (optional)
+        -s  zeigt statt der Gilde die Herkunft des Mudders an
+        -k  Kurzform der Liste; statt Presay+Titel nur Name, dafuer aber
+            Gilde und Herkunft
+
+ BESCHREIBUNG:
+    Es wird angezeigt, wer sich gerade im MorgenGrauen (oder alternativ im MUD
+    <mud>) befindet. Das Format der Liste fremder MUDs haengt immer von dem
+    jeweiligen MUD ab; in der Liste des MorgenGrauen wird jeder Anwesende wie
+    folgt aufgefuehrt:
+
+    [X] Presay Name Titel - Rasse/Gilde
+
+    Die Buchstaben in eckigen Klammern am Anfang bedeuten:
+
+     [s]   Spieler
+     [S]   Seher
+     [z]   Zweitspieler
+
+     [t]   Testspieler
+     [T]   Testspieler mit Seherlevel
+
+     [L]   Magierlehrling
+     [m]   Vollmagier, ohne Region
+     [M]   Vollmagier, mit Region
+     [H]   Hilfsmagier
+     [R]   Regionschef
+     [W]   Weiser
+     [E]   Erzmagier
+     [G]   Gott
+
+    Die Liste wird in drei Schritten sortiert (von grob nach fein):
+    Magierlevel -> Spielerlevel -> alphabetisch.
+
+ SIEHE AUCH:
+    kwer, kkwer, muds
+
+ LETZTE AeNDERUNG:
+    Wed, 05.04.2000, 01:00:00 von Tiamak
diff --git a/doc/pcmd/wirf b/doc/pcmd/wirf
new file mode 100644
index 0000000..71e555f
--- /dev/null
+++ b/doc/pcmd/wirf
@@ -0,0 +1,25 @@
+
+wirf (weg)
+----------
+
+ KOMMANDO:
+    wirf <objekt> weg
+    wirf alles weg
+    lass <objekt> fallen
+    lass alles fallen
+
+ ARGUMENTE:
+
+     <objekt>
+        Das wegzuwerfende Objekt
+
+ BESCHREIBUNG:
+    Mit diesem Kommando kannst Du Gegenstaende aus Deinem Besitz wieder
+    loswerden. Allerdings lassen sich manche Gegenstaende nicht so ohne
+    weiteres weglegen!
+
+ SIEHE AUCH:
+    stecke (in), nimm [aus]
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/zeilen b/doc/pcmd/zeilen
new file mode 100644
index 0000000..fc55941
--- /dev/null
+++ b/doc/pcmd/zeilen
@@ -0,0 +1,41 @@
+
+zeilen
+------
+
+ KOMMANDO:
+    zeilen [<zahl>]
+    zeilen auto[ -<zahl>]
+    zeilen abs[olut] | rel[ativ]
+
+ ARGUMENTE:
+
+     <zahl>
+        Eine ganze Zahl zwischen 0 und 100
+
+ BESCHREIBUNG:
+    Hiermit kann man die Anzahl der Zeilen einstellen, nach der ein `more'
+    (z.B. in der Topliste) anhaelt.
+
+    Bei 0 haelt das `more' nicht an.
+
+    Mit der Einstellung `auto' wird die Zeilenzahl automatisch ermittelt,
+    sofern Dein Telnet dies unterstuetzt (siehe auch `hilfe telnegs').
+    Um nach dem `more' noch etwas Platz zu haben, kann man z.B. mit
+    `zeilen auto -3' die Zeilenzahl bei automatischer Einstellung verkleinern.
+
+    Mit `zeilen' wird der derzeitig eingestellte Wert angezeigt.
+    Defaultmaessig ist <zahl> auf 20 gesetzt.
+
+    Normalerweise wird in der Promptzeile des `more' eine relative Angabe
+    gemacht, wieviel Prozent des Textes man schon gesehen hat. Mit
+    `zeilen absolut' (auch als `zeilen abs' abkuerzbar) kann man auf eine
+    alternative Darstellung umschalten. Dabei werden die Nummer der untersten
+    angezeigten Zeile sowie die Gesamtzeilenzahl des Textes angezeigt.
+
+    Mit `zeilen rel' kann man wieder auf die relative Anzeige zurueckschalten.
+
+ SIEHE AUCH:
+    telnegs
+
+ LETZTE AeNDERUNG:
+    Sun, 07.11.1999, 23:30:00 von Tiamak
diff --git a/doc/pcmd/zeit b/doc/pcmd/zeit
new file mode 100644
index 0000000..719c8f6
--- /dev/null
+++ b/doc/pcmd/zeit
@@ -0,0 +1,17 @@
+
+zeit
+----
+
+ KOMMANDO:
+    zeit
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Die aktuelle Uhrzeit samt Datum wird ausgegeben.
+
+ QUERVERWEISE: uhrmeldung, zeitzone
+
+ LETZTE AeNDERUNG:
+    Sat Jul  6 14:04:42 2002 von Bambi.
\ No newline at end of file
diff --git a/doc/pcmd/zeitzone b/doc/pcmd/zeitzone
new file mode 100644
index 0000000..631694b
--- /dev/null
+++ b/doc/pcmd/zeitzone
@@ -0,0 +1,45 @@
+
+zeitzone
+----------
+
+ KOMMANDO:
+    zeitzone [<zeitdifferenz>]
+
+ ARGUMENTE:
+
+     <zeitdifferenz>
+        Entweder: Ein ganzzahliger Wert, der den Zeitunterschied zwischen
+                  Deiner Zeitzone und Berlin in Stunden angibt.
+        Oder: UTC +/-i   Deine Zeitzone im Bezug auf die koordinierte
+                         Weltzeit UTC.
+
+ BESCHREIBUNG:
+    Ohne Argumente wird Deine aktuelle Zeitzone angezeigt.
+
+    Mit dem Argument <zeitdifferenz> kann man die eigene Zeitzone
+    einstellen. Diese Zeitdifferenz wird bei der stuendlichen Uhrmeldung
+    und beim Befehl (uhr)zeit beruecksichtigt.
+
+ BEISPIELE:
+
+    > zeitzone UTC 0
+    
+    Sollte man angeben, wenn man in London wohnt.
+    
+    > zeitzone UTC -10
+    
+    Sollte man angeben, wenn man auf Hawaii wohnt.
+    
+    > zeitzone 0
+    
+    Ist die Standard-Einstellung und entspricht der gleichen Zeitzone
+    wie Berlin.
+ 
+ ANMERKUNGEN:
+    Die mit "zeitzone" angegebenen Zeitzonen beruecksichtigen keine lokalen
+    Sommerzeiten - unter anderem, weil die sehr laenderspezifisch sind. Zudem
+    sind keine halben Stunden Zeitunterschiede beruecksichtigt, wie sie in
+    einigen australischen Territorien ueblich sind.
+
+ LETZTE AeNDERUNG:
+    Sat, 30.09.2000, 21:00:00 von Silvana
diff --git a/doc/pcmd/ziehe b/doc/pcmd/ziehe
new file mode 100644
index 0000000..6d766e1
--- /dev/null
+++ b/doc/pcmd/ziehe
@@ -0,0 +1,29 @@
+
+ziehe
+-----
+
+ KOMMANDO:
+    ziehe <kleidung> an | aus
+    ziehe <waffe> hervor
+
+ ARGUMENTE:
+
+     <kleidung>
+        Ein Kleidungsstueck
+     <waffe>
+        Eine Waffe
+
+ BESCHREIBUNG:
+    `ziehe (an/aus)' ist das allgemeine Kommando, um Kleidung an- und
+    abzulegen. Robuste Kleidung bietet nur dann Schutz gegen Angriffe, wenn
+    sie angezogen wurde.
+
+    `ziehe (hervor)' ist das allgemeine Kommando, um eine Waffe zum Kampf in
+    die Hand zu nehmen; nur wenn die Waffe hervorgezogen wurde, wird sie im
+    Kampf zum Angriff eingesetzt.
+
+ SIEHE AUCH:
+    trage, zuecke, stecke (weg)
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/zuecke b/doc/pcmd/zuecke
new file mode 100644
index 0000000..31140e2
--- /dev/null
+++ b/doc/pcmd/zuecke
@@ -0,0 +1,23 @@
+
+zuecke
+------
+
+ KOMMANDO:
+    zuecke <waffe>
+
+ ARGUMENTE:
+
+     <waffe>
+        Eine Waffe
+
+ BESCHREIBUNG:
+    `zuecke' ist das allgemeine Kommando, um eine Waffe zum Kampf in die Hand
+    zu nehmen; nur wenn die Waffe gezueckt wurde, wird sie im Kampf zum
+    Angriff eingesetzt. Es kann immer nur eine Waffe im Kampf eingesetzt
+    werden. Schwere Waffen koennen auch behindern.
+
+ SIEHE AUCH:
+    ziehe (an/aus), stecke (weg/zurueck)
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/pcmd/zweitiemarkierung b/doc/pcmd/zweitiemarkierung
new file mode 100644
index 0000000..4a906d3
--- /dev/null
+++ b/doc/pcmd/zweitiemarkierung
@@ -0,0 +1,30 @@
+KOMMANDO:
+        Zweitiemarkierung [Status]
+
+ARGUMENTE:
+        Status
+          Hier gibt man an, ob und wie Spieler jemanden als Zweitie
+          erkennen:
+          * unsichtbar - kein Spieler sieht, ob man Zweitie ist
+          * sichtbar   - Spieler sieht, dass man Zweitie ist, aber nicht
+                          wessen Zweitie man ist
+          * Name       - alle Spieler sehen, wessen Zweitie man ist
+
+BESCHREIBUNG:
+        Jeder Spieler im MUD ist verpflichtet, seine Zweities zu markieren,
+        um damit Verantwortlichkeiten besser zuordnen zu koennen.
+        Es ist jedoch nicht noetig, dass andere Spieler diese Information
+        erhalten, da dies nur fuer Magier interessant ist. Dieser Befehl
+        ermoeglicht es nun, diese Information vor anderen Spielern ganz oder
+        auch nur teilweise zu verbergen.
+        Man kann angeben, ob man als Zweitie erkannt wird und ob sogar
+        erkannt wird, wessen Zweitie man ist.
+        Die Standardeinstellung ist hierbei, dass andere Spieler erkennen,
+        ob man Zweitie ist, nicht jedoch, wessen Zweitie man ist.
+
+SIEHE AUCH:
+        regeln, Zweitspieler, Testspieler, Magier
+
+----------------------------------------------------------------------------
+Last modified: Son, 27. Okt 2002, 10:00:00, Vanion
+
diff --git a/doc/props/.svn-commit.tmp.swp b/doc/props/.svn-commit.tmp.swp
new file mode 100644
index 0000000..9931e7d
--- /dev/null
+++ b/doc/props/.svn-commit.tmp.swp
Binary files differ
diff --git a/doc/props/.synonym b/doc/props/.synonym
new file mode 100644
index 0000000..bc3b73a
--- /dev/null
+++ b/doc/props/.synonym
@@ -0,0 +1,54 @@
+autoattack P_AGGRESSIVE
+P_AUTOATTACK P_AGGRESSIVE
+hochzeit P_MARRIED
+SI_SKILLFUNC skill_info_liste
+SI_CLOSURE skill_info_liste
+SI_SKILLABILITY skill_info_liste
+SI_SKILLDAMAGE_TYPE skill_info_liste
+SI_SKILLDAMAGE_MSG skill_info_liste
+SI_SKILLDAMAGE_MSG2 skill_info_liste
+SI_INHERIT skill_info_liste
+SI_DIFFICULTY skill_info_liste
+SI_LASTLIGHT skill_info_liste
+SI_TESTFLAG skill_info_liste
+SI_GUILD skill_info_liste
+SI_ENEMY skill_info_liste
+SI_FRIEND skill_info_liste
+SI_MAGIC_TYPE skill_info_liste
+SI_LAST_USE skill_info_liste
+SI_LEARN_PROB skill_info_liste
+SI_SKILLDURATION skill_info_liste
+SI_SPELLBOOK skill_info_liste
+SM_RACE skill_info_liste
+SI_SPELLCOST skill_info_liste
+SI_TIME_MSG skill_info_liste
+SI_SP_LOW_MSG skill_info_liste
+SI_PREPARE_MSG skill_info_liste
+SI_PREPARE_BUSY_MSG skill_info_liste
+SI_PREPARE_ABORT_MSG skill_info_liste
+SI_NOMAGIC skill_info_liste
+SI_NOMAGIC_MSG skill_info_liste
+SI_SPELLFATIGUE skill_info_liste
+SI_X_SPELLFATIGUE skill_info_liste
+SI_SKILLLEARN skill_info_liste
+SI_LEARN_ATTRIBUTE skill_info_liste
+SI_SKILLARG skill_info_liste
+SI_SKILLRESTR_USE skill_info_liste
+SI_SKILLRESTR_LEARN skill_info_liste
+SI_SKILLINFO skill_info_liste
+SI_SKILLINFO_LONG skill_info_liste
+SI_SKILLDAMAGE skill_info_liste
+SI_SKILLDAMAGE_BY_ROW skill_info_liste
+SI_SPELL skill_info_liste
+SI_COLLATERAL_DAMAGE skill_info_liste
+SI_NUMBER_ENEMIES skill_info_liste
+SI_NUMBER_FRIENDS skill_info_liste
+SI_DISTANCE skill_info_liste
+SI_WIDTH skill_info_liste
+SI_DEPTH skill_info_liste
+SI_SKILLHEAL skill_info_liste
+SI_USR skill_info_liste
+SI_PREPARE_TIME skill_info_liste
+SI_ATTACK_BUSY_MSG skill_info_liste
+SI_NO_ATTACK_BUSY skill_info_liste
+SI_ATTACK_BUSY_AMOUNT skill_info_liste
diff --git a/doc/props/P_ABILITIES b/doc/props/P_ABILITIES
new file mode 100644
index 0000000..b82a0f6
--- /dev/null
+++ b/doc/props/P_ABILITIES
@@ -0,0 +1,9 @@
+NAME:
+    P_ABILITIES                   "abilities"                   
+
+DEFINIERT IN:
+    /sys/living/attributes.h
+
+BESCHREIBUNG:
+     *** OBSOLET! ***
+     Siehe P_NEWSKILLS.
diff --git a/doc/props/P_AC b/doc/props/P_AC
new file mode 100644
index 0000000..c62d786
--- /dev/null
+++ b/doc/props/P_AC
@@ -0,0 +1,28 @@
+P_AC
+
+NAME:
+     P_AC "ac"
+
+DEFINIERT IN:
+     <armour.h>
+
+BESCHREIBUNG:
+     Diese Property beschreibt die Ruestungsklasse (engl: armour class),
+     also den Schutz, den die Ruestung dem Traeger verleiht. Je hoeher der
+     Wert (als Zahl), um so besser ist die Ruestung. Negative Werte bewirken
+     negativen Schutz, d.h. der Schaden wird vergroessert statt verringert.
+
+BEMERKUNGEN:
+     Query- und Setmethoden auf P_AC sollten unbedingt vermieden werden. Sie
+     fuehren in der Regel zu massiven Inkonsistenzen im Mechanismus der
+     Ruestungsbeschaedigung und -reparatur.
+     Fuer jeden Ruestungstyp ist in <combat.h> eine Obergrenze definiert,
+     die man nicht ueberschreiten darf.
+     Ruestungen vom Typ AT_MISC haben immer AC 0 und werden mit keinen 
+     hoeheren Werten genemigt.
+
+SIEHE AUCH:
+     /std/armour.c, P_DAMAGED, Damage() P_TOTAL_AC
+
+----------------------------------------------------------------------------
+02.10.2007, Zesstra
diff --git a/doc/props/P_ACCEPT_PEACE b/doc/props/P_ACCEPT_PEACE
new file mode 100644
index 0000000..07b63a0
--- /dev/null
+++ b/doc/props/P_ACCEPT_PEACE
@@ -0,0 +1,18 @@
+PROPERTY
+     P_ACCEPT_PEACE                           "accept_peace"
+
+DEFINIERT IN 
+	/sys/combat.h
+
+BESCHREIBUNG
+	Mittels setzen Dieser Prop lassen sich leicht NPCs bauen,
+	die von jedem zu befrieden sind. Wenn die Property == 1 ist,
+	ist der NPC immer wieder befriedbar, sonst fuehrt der NPC das
+	uebliche Verhalten aus.
+
+SIEHE AUCH
+  QueryPacify(),
+  P_PEACE_HISTORY
+
+LETZTE AENDERUNG
+01.05.2008, Zesstra
diff --git a/doc/props/P_ACHATS b/doc/props/P_ACHATS
new file mode 100644
index 0000000..4097620
--- /dev/null
+++ b/doc/props/P_ACHATS
@@ -0,0 +1,8 @@
+NAME:
+    P_ACHATS                      "achats"                      
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Chats, die das Monster im Kampf ausgibt.
diff --git a/doc/props/P_ACHAT_CHANCE b/doc/props/P_ACHAT_CHANCE
new file mode 100644
index 0000000..9ccdf16
--- /dev/null
+++ b/doc/props/P_ACHAT_CHANCE
@@ -0,0 +1,8 @@
+NAME:
+    P_ACHAT_CHANCE                "achat_chance"                
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Wahrscheinlichkeit fuer die Attack-Chat-Ausgabe.
diff --git a/doc/props/P_ACTUAL_NOTIFY_FAIL b/doc/props/P_ACTUAL_NOTIFY_FAIL
new file mode 100644
index 0000000..435a646
--- /dev/null
+++ b/doc/props/P_ACTUAL_NOTIFY_FAIL
@@ -0,0 +1,27 @@
+********************** VERALTETE PROPERTY *********************************
+NAME:
+     P_ACTUAL_NOTIFY_FAIL          "actual_notify_fail"          
+
+DEFINIERT IN:
+     /sys/player.h
+
+ACHTUNG:
+     Diese Prop wird nicht mehr gesetzt (und auch nicht mehr abgefragt), da LD
+     eine efun hat, um das Objekt zu ermitteln, was als letztes ein
+     notify_fail() gesetzt hat, ein Speichern im Spieler also voellig
+     ueberfluessig ist.
+
+BESCHREIBUNG:
+     Ist im Spielerobjekt  gesetzt und enthaelt das Objekt, welches zuletzt
+     eine Fehlermeldung mit notify_fail()/_notify_fail() erfolgreich
+     waehrend des aktuellen Kommandos abgespeichert hat.
+
+BEMERKUNGEN:
+     Dient dazu, bei _notify_fail() zu ueberpruefen, ob schon vorher eine
+     Fehlermeldung gesetzt wurde.
+
+SIEHE AUCH:
+     AddCmd(), add_action()
+     notify_fail(), _notify_fail()
+
+10.03.2007, Zesstra
diff --git a/doc/props/P_ADJECTIVES b/doc/props/P_ADJECTIVES
new file mode 100644
index 0000000..3b44a4f
--- /dev/null
+++ b/doc/props/P_ADJECTIVES
@@ -0,0 +1,22 @@
+P_ADJECTIVES
+
+NAME:
+     P_ADJECTIVES "adjectives"
+
+DEFINIERT IN:
+     <thing/description.h>
+
+BESCHREIBUNG:
+     Hier steht ein Array von Strings, welche die Identifizierung des
+     Objektes ergaenzen. Die Verwaltung erfolgt ueber die Funktionen
+     AddAdjective() und RemoveAdjective().
+
+BEMERKUNGEN:
+     Man sollte an dieser Property nicht "von Hand" herumfummeln, sondern
+     immer die zugehoerigen Funktionen benutzen!
+
+SIEHE AUCH:
+     /std/thing/description.c, AddAdjective(), RemoveAdjective()
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 20:22:24 1996 by Wargon
diff --git a/doc/props/P_AERIAL_HELPERS b/doc/props/P_AERIAL_HELPERS
new file mode 100644
index 0000000..2606884
--- /dev/null
+++ b/doc/props/P_AERIAL_HELPERS
@@ -0,0 +1,45 @@
+P_AERIAL_HELPERS
+
+NAME:
+     P_AERIAL_HELPERS "lib_p_aerial_helpers"
+
+DEFINIERT IN:
+     <living/helpers.h>
+
+BESCHREIBUNG:
+     Diese Property kann in allen Lebewesen abgefragt werden, um die Objekte
+     zu ermitteln, die fuer die Unterstuetzung beim Fliegen/Segeln bei diesem 
+     Lebewesen registriert haben. Die Daten werden als Mapping der folgenden
+     Form zurueckgeliefert:
+     ([ Objekt : Rueckgabewert von dessen Callback-Methode ])
+     Eine Erlaeuterung dazu findet sich in der Dokumentation zu 
+     RegisterHelperObject().
+
+BEMERKUNGEN:
+     Diese Property kann nur abgefragt werden.
+     Es ist erwuenscht, dass entsprechende, neu geschaffene Stellen jegliche 
+     Helfer akzeptieren, deren Callback-Methode >0 zurueckgibt.
+
+BEISPIEL:
+     Um zu ermitteln, ob der Spieler mindestens ein Objekt bei sich hat, das 
+     beim Fliegen hilft, sucht man alle Objekte aus dem Mapping heraus, die
+     einen Wert >0 eingetragen haben und prueft deren Anzahl:
+
+     mapping aerial = this_player()->QueryProp(P_AERIAL_HELPERS);
+     object* helpers = filter( aerial, function int (object h) {
+                         return (aerial[h]>0); });
+     if ( sizeof(helpers) ) {
+       tell_object(this_player(), "Du erhebst Dich mit Hilfe "+
+         helpers[0]->name(WESSEN,1)+" elegant in die Luefte.\n");
+     }
+     else {
+       tell_object(this_player(), "Du hast nichts zum Fliegen bei Dir.\n");
+     }
+
+SIEHE AUCH:
+     Methoden:    RegisterHelperObject(L), UnregisterHelperObject(L)
+     Properties:  P_HELPER_OBJECTS, P_AQUATIC_HELPERS
+
+----------------------------------------------------------------------------
+12.03.2016, Arathorn
+
diff --git a/doc/props/P_AGE b/doc/props/P_AGE
new file mode 100644
index 0000000..ad6388a
--- /dev/null
+++ b/doc/props/P_AGE
@@ -0,0 +1,8 @@
+NAME:
+    P_AGE                         "age"                         
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Alter des Spielers in Heart-Beats (1 HB == 2 Sekunden)
diff --git a/doc/props/P_AGGRESSIVE b/doc/props/P_AGGRESSIVE
new file mode 100644
index 0000000..b10d97e
--- /dev/null
+++ b/doc/props/P_AGGRESSIVE
@@ -0,0 +1,39 @@
+NAME:
+    P_AGGRESSIVE                  "aggressive"                  
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+	Gesetzt, wenn das Wesen von sich aus Angriffe startet.
+
+	Ueblicherweise nimmt man als Wert 1, man kann jedoch auch
+	einen kleineren Wert nehmen wenn es nur mit einer bestimmten
+	Wahrscheinlichkeit automatisch angreifen soll.
+
+	Der Wert von Spieler und Monster wird addiert, um zu entscheiden,
+	ob ein Spieler angegriffen werden soll,	man kann P_AGGRESSIVE
+	also auch bei Spielern setzen, so dass sie von allen Monstern
+	angegriffen werden.
+
+	Bei Monstern (und NUR bei diesen) kann man hier auch ein Mapping
+	angeben, das als Keys Namen von Properties des Spielers enthaelt
+	und als Values Mappings, in denen steht welcher Wert bei welchen
+	Wert fuer die Property genommen werden soll (Beispiele folgen).
+	Mit Key 0 kann man einen Default-Wert (sowohl in inneren Mappings
+	wie auch im aeusseren Mapping) festlegen. Default-Werte werden
+	genommen, falls keine anderen gesetzt sind, also Vorsicht mit
+	0-Eintraegen (Tip: 0.0 ist in LPC ungleich 0).
+	Bei mehreren Properties werden alle gesetzten Werte gemittelt.
+
+BEISPIELE:
+	SetProp(P_AGGRESSIVE,([P_RACE:(["Zwerg":1, "Elf":0.0, 0:0.5])]))
+	Zwerge werden immer automatisch angegriffen, Elfen nie und
+	alle anderen mit 50% Wahrscheinlichkeit.
+	Man beachte, dass hier 0.0 genommen werden musste anstelle von 0,
+	weil sonst Elfen auch 50% Wahrscheinlichkeit bekommen haetten.
+
+	SetProp(P_AGGRESSIVE,([P_RACE:(["Zwerg":0.3]),
+	                       P_GUILD:(["Chaos":0.7])]))
+	Zwerge werden mit 30% Wahrscheinlichkeit angegriffen,
+	Chaoten mit 70% und Zwerg-Chaoten mit 50%.
diff --git a/doc/props/P_ALCOHOL b/doc/props/P_ALCOHOL
new file mode 100644
index 0000000..63005b7
--- /dev/null
+++ b/doc/props/P_ALCOHOL
@@ -0,0 +1,22 @@
+NAME:
+     P_ALCOHOL			"alcohol"
+
+DEFINIERT IN:
+     /sys/living/life.h
+
+BESCHREIBUNG:
+
+     - Lebewesen
+       Numerischer Wert fuer den Grad der Betrunkenheit eines Lebewesens.
+       Der maximale Wert steht in P_MAX_ALCOHOL.
+
+     - Speisen/Kneipen
+       Numerischer Wert fuer den Grad, den eine Portion der Speise den
+       Konsumenten betrunken macht
+
+SIEHE AUCH:
+	P_MAX_ALCOHOL, P_ALCOHOL_DELAY, consume,
+	P_FOOD, P_DRINK, wiz/food, 
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_ALCOHOL_DELAY b/doc/props/P_ALCOHOL_DELAY
new file mode 100644
index 0000000..4c6d2c1
--- /dev/null
+++ b/doc/props/P_ALCOHOL_DELAY
@@ -0,0 +1,10 @@
+NAME:
+    P_ALCOHOL_DELAY                 "alcohol_delay"                     
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Anzahl der heart_beats, bis P_ALCOHOL um einen Punkt sinkt.
+     Aenderungen dieser Property in Spielern beduerfen der 
+     Genehmigung des EM fuer Balance.
diff --git a/doc/props/P_ALC_FULL_MSG b/doc/props/P_ALC_FULL_MSG
new file mode 100644
index 0000000..bd84a86
--- /dev/null
+++ b/doc/props/P_ALC_FULL_MSG
@@ -0,0 +1,24 @@
+NAME:
+     P_ALC_FULL_MSG                "std_food_alc_full_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung an den Konsumenten, wenn Alkohol konsumiert werden soll,
+     obwohl dieser nicht mehr Alkohol vertraegt.
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "Soviel Alkohol vertraegst Du nicht mehr."
+
+SIEHE AUCH:
+     P_ALCOHOL, P_MAX_ALCOHOL, wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_ALIGN b/doc/props/P_ALIGN
new file mode 100644
index 0000000..f1d3465
--- /dev/null
+++ b/doc/props/P_ALIGN
@@ -0,0 +1,17 @@
+NAME:
+    P_ALIGN                       "align"                       
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Numerischer Wert fuer Gut- oder Boesheit des Wesens.
+
+     Kann Werte von -1000 (Boese wie der Teufel hoechstpersoenlich)
+     bis +1000 (Jesus, bist Du's ?) annehmen.
+
+     Werte ausserhalb dieser Skala werden zwar teilweise verwendet,
+     das Setzen derselben sollte jedoch UNBEDINGT unterbleiben.
+
+----------------------------------------------------------------------------
+Last modified: Sat Jul 12 17:00:00 by Mandragon
diff --git a/doc/props/P_ALLOWED_SHADOW b/doc/props/P_ALLOWED_SHADOW
new file mode 100644
index 0000000..39482fd
--- /dev/null
+++ b/doc/props/P_ALLOWED_SHADOW
@@ -0,0 +1,24 @@
+NAME:
+    P_ALLOWED_SHADOW              "allowed_shadow"
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+
+     Normalerweise koennen nur Shadows an Spieler gebunden werden, die in 
+     /std/player/shadows/ liegen. 
+     
+     Zu Testzwecken kann in dieser Property der Pfad eines Shadows eingetragen
+     werden. Damit wird die oben beschriebene Regel fuer diesen Spieler und 
+     diesen Shadow ausser Kraft gesetzt.
+
+BEMERKUNG: 
+
+     Der Spieler muss ein Testspieler sein. Ansonsten wird diese Property
+     ignoriert.
+
+     Die Property ist secured gesetzt. Das heisst, nur EM+ koennen
+     diese Property setzen und loeschen.
+------------------------------------------------------------------------------
+Last modified: Sun Mar 21 00:27:46 2004 by Vanion
diff --git a/doc/props/P_AMMUNITION b/doc/props/P_AMMUNITION
new file mode 100644
index 0000000..f8903af
--- /dev/null
+++ b/doc/props/P_AMMUNITION
@@ -0,0 +1,42 @@
+P_AMMUNITION
+
+NAME:
+    P_AMMUNITION     "munition"
+
+DEFINIERT IN:
+    <combat.h>
+
+BESCHREIBUNG:
+    Enthaelt die fuer eine Waffe gueltige Munition als String. Die
+    Munition muss diesen String als ID besitzen.
+
+    Fuer die Munitionsobjekte gilt:
+    * es kann ein Skill am Spieler definiert werden, dieser wirkt dann
+      zusaetzlich zum generellen SK_SHOOT-Skill.
+    * sie koennen eine HitFunc besitzten, die beim Schuss abgefragt wird
+
+BEMERKUNGEN:
+    Bitte das #define MUN_MISC(x) benutzen. Munition sollte auch immer
+    in Deutsch und Plural angegeben werden, da P_AMMUNITION direkt
+    fuer Ausgaben an den Spieler ausgewertet wird.
+
+    Momentan sind vier Munitionsarten in der combat.h vordefiniert:
+    MUN_ARROW, MUN_STONE, MUN_BOLT, MUN_MISC
+
+BEISPIELE:
+    // fuer ein kleines Blasrohr im Blasrohr
+    SetProp(P_AMMUNITION, MUN_MISC("Erbsen"));
+
+    // Entsprechend in der Munition:
+    AddId(MUN_MISC("Erbsen"));
+
+SIEHE AUCH:
+    Generell:  P_SHOOTING_WC, P_STRETCH_TIME
+    Methoden:  FindRangedTarget(L), shoot_dam(L), cmd_shoot(L)
+    Gebiet:    P_RANGE, P_SHOOTING_AREA, P_TARGET_AREA
+    Waffen:    P_WEAPON_TYPE, P_WC, P_EQUIP_TIME, P_NR_HANDS
+    Kampf:     Attack(L), Defend(L), P_DISABLE_ATTACK, P_ATTACK_BUSY
+    Team:      PresentPosition(L)
+    Sonstiges: fernwaffen
+
+29.Jul 2014 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_AMOUNT b/doc/props/P_AMOUNT
new file mode 100644
index 0000000..a44e45a
--- /dev/null
+++ b/doc/props/P_AMOUNT
@@ -0,0 +1,8 @@
+NAME:
+    P_AMOUNT                      "amount"                      
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Anzahl der Objekte, fuer die das Objekt steht.
diff --git a/doc/props/P_AQUATIC_HELPERS b/doc/props/P_AQUATIC_HELPERS
new file mode 100644
index 0000000..33a07ab
--- /dev/null
+++ b/doc/props/P_AQUATIC_HELPERS
@@ -0,0 +1,46 @@
+P_AQUATIC_HELPERS
+
+NAME:
+     P_AQUATIC_HELPERS "lib_p_aquatic_helpers"
+
+DEFINIERT IN:
+     <living/helpers.h>
+
+BESCHREIBUNG:
+     Diese Property kann in allen Lebewesen abgefragt werden, um die Objekte
+     zu ermitteln, die fuer die Unterstuetzung beim Tauchen bei diesem 
+     Lebewesen registriert haben. Die Daten werden als Mapping der folgenden
+     Form zurueckgeliefert:
+     ([ Objekt : Rueckgabewert von dessen Callback-Methode ])
+     Eine Erlaeuterung dazu findet sich in der Dokumentation zu 
+     RegisterHelperObject().
+
+BEMERKUNGEN:
+     Diese Property kann nur abgefragt werden.
+     Es ist erwuenscht, dass entsprechende, neu geschaffene Stellen jegliche 
+     Helfer akzeptieren, deren Callback-Methode >0 zurueckgibt.
+
+BEISPIEL:
+     Um zu ermitteln, ob der Spieler mindestens ein Objekt bei sich hat, das 
+     beim Tauchen hilft, sucht man alle Objekte aus dem Mapping heraus, die
+     einen Wert >0 eingetragen haben und prueft deren Anzahl:
+
+     mapping aquatic = this_player()->QueryProp(P_AQUATIC_HELPERS);
+     object* helpers = filter( aquatic, function int (object h) {
+                         return (aquatic[h]>0); });
+     if ( sizeof(helpers) ) {
+       tell_object(this_player(), "Du stuerzt Dich in die Fluten und "
+         "stellst ueberrascht fest, dass Du mit Hilfe "+
+         helpers[0]->name(WESSEN,1)+" sogar unter Wasser atmen kannst!\n");
+     }
+     else {
+       tell_object(this_player(), "Du hast nichts zum Tauchen bei Dir.\n");
+     }
+
+SIEHE AUCH:
+     Methoden:    RegisterHelperObject(L), UnregisterHelperObject(L)
+     Properties:  P_HELPER_OBJECTS, P_AERIAL_HELPERS
+
+----------------------------------------------------------------------------
+06.04.2016, Arathorn
+
diff --git a/doc/props/P_ARMOURS b/doc/props/P_ARMOURS
new file mode 100644
index 0000000..3156bf6
--- /dev/null
+++ b/doc/props/P_ARMOURS
@@ -0,0 +1,25 @@
+P_ARMOURS
+
+NAME:
+     P_ARMOURS                     "armours"
+
+DEFINIERT IN:
+     <living/combat.h>
+
+BESCHREIBUNG:
+     Array mit den getragenen Schutzbekleidungen des Lebewesen.
+
+     Bitte nach Moeglichkeit davon absehen, diese Property zu beschreiben, da
+     es hierbei zu Inkonsistenzen kommen kann.
+     
+     Falls ihr die Ruestungen des Lebewesens, ggf. mit bestimten Kriterien,
+     ermitteln wollt, benutzt hierfuer bitte die Funktion FilterArmours()
+     statt selber ueber dieses Array zu laufen.
+
+SIEHE AUCH:
+     Verwandt:		QueryArmourByType(L), P_WEAPON, FilterClothing(), 
+                  FilterArmours()
+     Ruestungen:	P_AC, P_ARMOUR_TYPE, P_NR_HANDS
+     Sonstiges:		P_EQUIP_TIME, P_LAST_USE
+
+14.03.2009, Zesstra
diff --git a/doc/props/P_ARMOUR_TYPE b/doc/props/P_ARMOUR_TYPE
new file mode 100644
index 0000000..35978f2
--- /dev/null
+++ b/doc/props/P_ARMOUR_TYPE
@@ -0,0 +1,46 @@
+P_ARMOUR_TYPE
+
+NAME:
+     P_ARMOUR_TYPE "armour_type"
+
+DEFINIERT IN:
+     <armour.h>
+
+BESCHREIBUNG:
+     In dieser Property wird der Typ einer Ruestung festgehalten. Man sollte
+     hier nur die in <combat.h> definierten Konstanten verwenden:
+
+        AT_AMULET     Amulett
+        AT_ARMOUR     Ruestung
+        AT_BELT       Guertel
+        AT_BOOT       Schuhe
+        AT_CLOAK      Umhang
+        AT_GLOVE      Handschuhe
+        AT_HELMET     Helm
+        AT_QUIVER     Koecher
+        AT_RING       Ring
+        AT_SHIELD     Schild
+        AT_TROUSERS   Hosen
+        AT_MISC       Sonstiges
+
+     Der Ruestungstyp AT_MISC ist schnoedem Tand und anderem nutzlosen
+     Kram vorbehalten. Auf keinen Fall darf eine AT_MISC-Ruestung ueber
+     eine AC > 0 verfuegen noch irgendwie kampfrelevante Bedeutung be-
+     sitzen. Ruestungen des Typs AT_MISC, die KEINE DefendFunc benoetigen,
+     muessen mittels /std/clothing als einfaches Kleidungsstueck realisiert
+     werden.
+
+BEMERKUNGEN:
+     Die P_AC wird bei AT_MISC-Ruestungen gar nicht erst ausgewertet.
+     DefendFuncs werden zwar ausgewertet, aber bitte ueberlegt euch gut, ob
+     ihr sie braucht (Rechenzeit im Kampf ist kritisch!) und ob sie seitens 
+     der Balance in eurem Fall erlaubt sind.
+
+SIEHE AUCH:
+     Verwandt:          QueryArmourByType(L), P_WEAPON
+     Ruestungen:        P_AC, P_NR_HANDS (Schilde)
+     Sonstiges:         P_EQUIP_TIME, P_LAST_USE
+     Code:              /std/armour.c, /std/clothing.c
+     Gildenergaenzung:  P_GLOVE_FINGERLESS
+
+27. Mai 2015, Arathorn
diff --git a/doc/props/P_ARRIVEMSG b/doc/props/P_ARRIVEMSG
new file mode 100644
index 0000000..02d5759
--- /dev/null
+++ b/doc/props/P_ARRIVEMSG
@@ -0,0 +1,8 @@
+NAME:
+    P_ARRIVEMSG                   "arrivemsg"                   
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+     Meldung, wenn der Transporter anlegt.
diff --git a/doc/props/P_ARTICLE b/doc/props/P_ARTICLE
new file mode 100644
index 0000000..1517865
--- /dev/null
+++ b/doc/props/P_ARTICLE
@@ -0,0 +1,15 @@
+NAME:
+    P_ARTICLE                     "article"                     
+
+DEFINIERT IN:
+    /sys/thing/language.h
+
+BESCHREIBUNG:
+     Gibt an, ob in der Beschreibung ein Artikel ausgegeben werden soll
+     oder nicht.
+
+     Wenn ein Artikel angegeben werden soll, so wird 1 gesetzt, sonst 0.
+     Diese Property ist aus historischen Gruenden auf 1 voreingestellt
+     und intern invertiert. (d.h., beim Auslesen per Query kommt als 
+     Ergebnis genau das falsche heraus). Bitte beachtet das bei Set- bzw.
+     Query-Funktionen.
\ No newline at end of file
diff --git a/doc/props/P_ATTACK_BUSY b/doc/props/P_ATTACK_BUSY
new file mode 100644
index 0000000..b55dc5b
--- /dev/null
+++ b/doc/props/P_ATTACK_BUSY
@@ -0,0 +1,44 @@
+NAME:
+    P_ATTACK_BUSY                 "attack_busy"                 
+
+DEFINIERT IN:
+    /sys/living/combat.h
+
+BESCHREIBUNG:
+    Ueber diese Property kann festgestellt werden, ob ein Spieler noch 
+    Spezialwaffen (zB Flammenkugel) einsetzen kann.
+    
+    Ist der Wert bei Abfrage ungleich 0, dann darf der Spieler in dieser
+    Runde keine Aktion mehr durchfuehren. Mit SetProp(P_ATTACK_BUSY, 1)
+    wird eine Aktion verbraucht.
+
+    Intern wird relativ fein gerechnet, ein SetProp(P_ATTACK_BUSY, x)
+    wird in das Abziehen von x*100 Punkten umgerechnet. Der Wert freier
+    Aktionen pro Runde berechnet sich wie folgt:
+    
+    Spieler: 100 + QuerySkillAttribute(SA_SPEED)
+    Seher:   Spieler + 200 + QueryProp(P_LEVEL)
+
+    Das Maximum liegt bei 500.
+    Damit betraegt die Anzahl der moeglichen Aktionen pro Runde: Wert/100,
+    also maximal 5 Aktionen pro Runde.
+
+    Zaubersprueche zaehlen im Normalfall auch als eine Aktion.
+
+BEMERKUNGEN:
+    Benutzt man P_ATTACK_BUSY fuer eine sich nicht sofort verbrauchende
+    Sache, kann ein Seher dieses Objekt im Normalfall dreimal pro Runde
+    benutzen. Wenn ungewollt, muss das ueber einen Zeitmarker selbst
+    verhindert werden.
+    
+BEISPIELE:
+    (Code eines Objektes ist in
+     /doc/beispiele/testobjekte/attack_busy_sensitive_testobj.c)
+    // einfacher Test auf ATTACK_BUSY und anschliessendes Setzen
+    if (this_player()->QueryProp(P_ATTACK_BUSY)) {
+      write("Du hast dafuer momentan einfach nicht mehr die Puste.\n");
+      return 1;
+    }
+    this_player()->SetProp(P_ATTACK_BUSY, 1);
+
+7. Mar 2011 Gloinson
diff --git a/doc/props/P_ATTRIBUTES b/doc/props/P_ATTRIBUTES
new file mode 100644
index 0000000..29da911
--- /dev/null
+++ b/doc/props/P_ATTRIBUTES
@@ -0,0 +1,58 @@
+NAME:
+	P_ATTRIBUTES			"attributes"
+
+DEFINIERT IN:
+	/sys/living/attributes.h
+
+BESCHREIBUNG:
+	Diese Property enthaelt ein Mapping mit den Attributen des
+	Lebewesens. Die Schluessel kennzeichnen hierbei das jeweilige
+	Attribut. Die verschiedenen Standardattribute findet man in
+	/sys/living/attributes.h, welche derzeit folgende Moeglichkeiten
+	bieten:	- A_STR (Kraft)
+		- A_INT (Intelligenz)
+		- A_DEX (Geschick)
+		- A_CON (Konstitution)
+	Sie werden automatisch an verschiedenen Stellen in der MUDLib
+	ausgewertet, zum Beispiel bestimmt A_INT die maximal moeglichen
+	Konzentrationspunkte und A_CON die maximal moeglichen Lebenspunkte.
+
+BEMERKUNGEN:
+        Keine echte Property, _query_attributes() und _set_attributes() sind 

+        in /std/living/attributes.c implementiert.
+
+	Es bietet sich an, zum Erfragen der Attributwerte die Funktion
+	QueryAttribute() zu nutzen, da es auch moegliche Offsets gibt,
+	so beispielsweise ueber die Properties P_ATTRIBUTES_OFFSETS bzw.
+	P_ATTRIBUTES_MODIFIER im Lebewesen selbst, oder auch ueber
+	P_X_ATTR_MOD bzw. P_M_ATTR_MOD in Objekten im Lebewesen.
+	Kurzfristige zeit- oder objektgebundene Attributveraenderungen
+	koennen ueber die Property P_TIMED_ATTR_MOD realisiert werden.
+
+	Es gibt auch zahlreiche andere Dienstfunktionen fuer diesen sehr
+	balancekritischen Bereich, siehe unten.
+
+BEISPIEL:
+	Ein moegliches Ergebnis fuer einen frischen Level 1 Spieler waere:
+	  QueryProp(P_ATTRIBUTES);
+	  Ergebnis: (["int":1,"con":1,"str":1,"dex":1])
+	Hinzu kommen eventuell moegliche Rassenboni, die mittels
+	P_ATTRIBUTE_OFFSETS realisiert werden, Zwerge sind beispielsweise
+	recht stark:
+	  QueryProp(P_ATTRIBUTES_OFFSETS);
+	  Ergebnis: (["int":1,"con":1,"str":1,"dex":3])
+	Jetzt bekaeme man (sofern keine weiteren Offsets realisiert sind)
+	mittels QueryAttribute() insgesamt:
+	  QueryAttribute(A_DEX);
+	  Ergebnis: 4
+
+SIEHE AUCH:
+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),
+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),
+	SetTimedAttrModifier(), QueryTimedAttrModifier(),
+	DeleteTimedAttrModifier(),
+	P_ATTRIBUTES_OFFSETS, P_ATTRIBUTES_MODIFIER,P_TIMED_ATTR_MOD,
+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c
+
+----------------------------------------------------------------------------
+Last modified: Tue Jul 27 20:00:20 2004 by Muadib
diff --git a/doc/props/P_ATTRIBUTES_MODIFIER b/doc/props/P_ATTRIBUTES_MODIFIER
new file mode 100644
index 0000000..f5ba513
--- /dev/null
+++ b/doc/props/P_ATTRIBUTES_MODIFIER
@@ -0,0 +1,56 @@
+NAME:
+    P_ATTRIBUTES_MODIFIER         "attributes_modifier"
+
+DEFINIERT IN:
+    /sys/living/attributes.h
+
+BESCHREIBUNG:
+    In dieser Property werden Attribut-Modifikatoren gespeichert, die
+    laengere Zeit wirksam sein sollen, tlw. auch ueber einen Reboot
+    hinweg.
+    Intern werden die Modifikatoren in einem Mapping der Form
+
+        ([ Setzer-Key : ([ A_xy : Wert, ... ]) , ... ])
+
+    gespeichert. Das Setzen folg hingegen in der Form
+
+        spieler->SetProp(P_ATTRIBUTES_MODIFIER, ([ A_xy : Wert, ... ]));
+    oder
+        spieler->SetProp(P_ATTRIBUTES_MODIFIER, ({ Setzer-Key, ([ ... ]) }));
+
+    Bei der ersten Variante wird hierbei der Filename des setzenden Objektes
+    als Setzer-Key genommen.
+    Es koennen also durchaus von mehreren Objekten Modifier gesetzt werden.
+    Bekannte Modifier sind:
+
+        #death      Attribut-Abzug durch Todesfolgen      (Mudlib)
+        #drain      Statabzug durch NPCs                  (Paracelsus)
+        #frosch     Staerken-Abzug bei Froeschen          (Mudlib)
+
+BEMERKUNGEN:
+    Keine echte Property, _query_attributes_modifier() und
+    _set_attributes_modifier() sind in /std/living/attributes.c
+    implementiert
+    - SetProp/QueryProp nutzen!
+    - Wenn ein Modifier nicht mehr gebracht wird, nicht die Attributswerte auf
+      0 setzen, sondern den ganzen Eintrag! also:
+      SetProp(P_ATTRIBUTES_MODIFIER, ([]) );
+      oder: SetProp(P_ATTRIBUTES_MODIFIER, 0 ); 
+      aber nicht: SetProp(P_ATTRIBUTES_MODIFIER, ([A_STR:0]));
+
+BEISPIELE:
+    // ein Bonus ... 'ende'-fest (muss also per uid gesichert werden)
+    player->SetProp(P_ATTRIBUTES_MODIFIER,
+                    ({"g_klerus_segen", ([A_CON:5, A_INT:5])}));
+    ...
+    player->SetProp(P_ATTRIBUTES_MODIFIER, ({"g_klerus_segen", 0}));
+
+SIEHE AUCH:
+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),
+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),
+	SetTimedAttrModifier(), QueryTimedAttrModifier(),
+	DeleteTimedAttrModifier(),
+	P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_TIMED_ATTR_MOD,
+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c
+----------------------------------------------------------------------------
+Last modified: Tue Jul 27 20:00:20 2004 by Muadib
diff --git a/doc/props/P_ATTRIBUTES_OFFSETS b/doc/props/P_ATTRIBUTES_OFFSETS
new file mode 100644
index 0000000..3ebe2b7
--- /dev/null
+++ b/doc/props/P_ATTRIBUTES_OFFSETS
@@ -0,0 +1,27 @@
+NAME:
+	P_ATTRIBUTES_OFFSETS		"attributes_offsets"
+
+DEFINIERT IN:
+	/sys/living/attributes.h
+
+BESCHREIBUNG:
+	Diese Property enthaelt ein Mapping mit Offsets, die zu den
+	Attributen eine Lebewesens addiert werden. Diese koennen auch
+	negativ sein! Realisiert werden damit beispielsweise Rassenboni.
+	Es gibt noch weitere Moeglichkeiten, Attributoffsets anzugeben.
+	Fuer weiteres siehe P_ATTRIBUTES.
+
+BEMKERUNGEN:
+        Keine echte Property, _query_attributes_offsets() und 

+        _set_attributes_offsets() sind in /std/living/attributes.c 

+        implementiert.
+
+SIEHE AUCH:
+	QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),
+	SetAttribute(), SetRealAttribute(), UpdateAttributes(),
+	SetTimedAttrModifier(), QueryTimedAttrModifier(),
+	DeleteTimedAttrModifier(),
+	P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_TIMED_ATTR_MOD,
+	P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c
+----------------------------------------------------------------------------
+Last modified: Tue Jul 27 20:00:20 2004 by Muadib
diff --git a/doc/props/P_AUTH_INFO b/doc/props/P_AUTH_INFO
new file mode 100644
index 0000000..5ca65c5
--- /dev/null
+++ b/doc/props/P_AUTH_INFO
@@ -0,0 +1,8 @@
+NAME:
+    P_AUTH_INFO                   "auth_info"                   
+
+DEFINIERT IN:
+    /sys/player.h
+
+BESCHREIBUNG:
+     Username des Spielers, wenn bei ihm ein AUTHD laeuft
diff --git a/doc/props/P_AUTOLOAD b/doc/props/P_AUTOLOAD
new file mode 100644
index 0000000..6ea69c9
--- /dev/null
+++ b/doc/props/P_AUTOLOAD
@@ -0,0 +1,15 @@
+NAME:
+    P_AUTOLOAD                    "autoload"                    
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Mapping mit der Menge der Autoloadobjekte und den zugeh.
+     Properties.
+
+BEMERKUNGEN:
+     Funktioniert momentan nicht. Die Methode wurde entfernt. Doku bleibt
+     hier bis der Fall geklaert ist. (Stand 27.Aug 2006)
+
+27.Aug 2006 Gloinson
diff --git a/doc/props/P_AUTOLOADOBJ b/doc/props/P_AUTOLOADOBJ
new file mode 100644
index 0000000..32b433d
--- /dev/null
+++ b/doc/props/P_AUTOLOADOBJ
@@ -0,0 +1,119 @@
+NAME:
+    P_AUTOLOADOBJ                 "autoloadobj"
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Hiermit kann prinzipiell angegeben werden ob ein Objekt ueber das
+     Ausloggen eines Spielers (Reboot/ende) behalten werden soll.
+
+     Als Inhalt der Property koennen permanente Eigenschaften des Objektes
+     angegeben werden.
+     Beim Einloggen wird das Objekt neu erzeugt und P_AUTOLOADOBJ auf die
+     gespeicherten Werte gesetzt. Die Werte muessen allerdings selbst
+     verwaltet werden.
+
+     Bitte geht nicht davon aus, dass es waehrend des Setzens/Abfragens dieser
+     Prop einen this_player() oder ein this_interactive() geben muss.
+     Speziell ist this_interactive() nicht == /secure/login!
+     Ebenfalls muss das Objekt beim Setzen/Abfragen nicht in einem Spieler
+     sein.
+
+BEMERKUNGEN:
+     Autoloadobjekte werden beim Ausloggen nicht fallengelassen!
+
+BEISPIELE:
+     ## Variante 1: simples Objekt, bleibt einfach nur erhalten,
+     ##             Variablen werden nicht gesichert ##
+     void create() {
+      ...
+      SetProp(P_AUTOLOADOBJ,1);
+      ...
+     }
+
+
+     ## Variante 2: Speicherung mehrerer Variablen ueber
+     ##             P_AUTOLOADOBJ (elegante Verwaltung)
+
+     // ein paar #defines fuer die Plaetze in der Speichervariablen
+     #define MY_AL_SHORT    0
+     #define MY_AL_ATTRM    1
+     #define MY_AL_OWNER    2
+     #define MY_AL_DESTRUCT 3
+
+     // die Variablen, die erhalten bleiben sollen
+     static object owner;
+     static int destruct_time;
+
+     // diese hier wird gerufen, wenn der Spieler gespeichert wird,
+     // wir packen also alle aktuellen Variablen in eine und geben die
+     // zum Speichern heraus ... wir nehmen hier ein Array (statt
+     // zB eines Mappings), weil das am wenigsten Platz braucht
+     static mixed* _query_autoloadobj() {
+      mixed *ret;
+      ret=allocate(4);
+      ret[MY_AL_SHORT] = QueryProp(P_SHORT);      // SHORT merken
+      ret[MY_AL_ATTRM] = QueryProp(P_M_ATTR_MOD); // einen Modifikator merken
+      ret[MY_AL_OWNER] = getuid(owner);           // ah, ein Besitzer!
+      ret[MY_AL_DESTRUCT]=destruct_time-time();   // und eine Lebensdauer!
+
+      return ret;
+
+      /*
+      // normalerweise wuerde man das einfach so schreiben:
+      return (({QueryProp(P_SHORT),
+                QueryProp(P_M_ATTR_MOD),
+                getuid(owner),
+                destruct_time-time()}));
+      */
+     }
+
+     // diese hier wird gerufen, wenn das Objekt neu im Spieler
+     // erzeugt wurde (Login), also packen wir das Speicherarray wieder
+     // aus und in alle entsprechenden Variablen
+     static mixed* _set_autoloadobj(mixed *new) {
+      // wenn das Format nicht mit dem oben uebereinstimmt ist was
+      // schiefgelaufen
+      if(pointerp(new) && !owner && sizeof(new)>4 &&
+         (owner=find_player(new[MY_AL_OWNER]))) {
+       // los, zuweisen!
+
+       SetProp(P_SHORT,      new[MY_AL_SHORT]);
+       SetProp(P_M_ATTR_MOD, new[MY_AL_ATTRM]);
+       destruct_time=        time()+new[MY_AL_DESTRUCT];
+
+       call_out(#'remove,new[3]);
+      } else call_out(#'remove,0);
+
+      return new;
+     }
+
+
+     ## Variante 3: und das gleiche mit Set/Querymethoden ##
+     // Prototypen fuer Set und Query-Methoden -> man Set
+     static mixed *my_query_autoloadobj();
+     static mixed *my_set_autoloadobj(mixed *new);
+
+     void create() {
+      // Binden der Methoden
+      Set(P_AUTOLOADOBJ, #'my_query_autoloadobj, F_QUERY_METHOD);
+      Set(P_AUTOLOADOBJ, #'my_set_autoloadobj, F_SET_METHOD);
+
+      // die werden nur von mir veraendert!
+      Set(P_AUTOLOADOBJ, PROTECTED, F_MODE_AS);
+      ...
+     }
+
+     static mixed *my_query_autoloadobj () {
+       // s.o.
+     }
+
+     static mixed *my_set_autoloadobj (mixed *new) {
+       // s.o.
+     }
+
+SIEHE AUCH:
+     P_AUTOLOAD, SetProp
+
+24.Aug.2006 Gloinson@MG
diff --git a/doc/props/P_AVATAR_URI b/doc/props/P_AVATAR_URI
new file mode 100644
index 0000000..3275399
--- /dev/null
+++ b/doc/props/P_AVATAR_URI
@@ -0,0 +1,22 @@
+NAME:
+    P_AVATAR_URI                    "p_lib_avataruri"
+
+DEFINIERT IN:
+    /sys/living/description.h
+
+BESCHREIBUNG:
+    Lebewesen speichern in der Prop P_AVATAR_URI eine URI (string), welche auf
+    ein Bild im Netz verweist, welches ein Client fuer dieses Lebewesen
+    anzeigen kann.
+    Spieler koennen diese Avatar-URI mit dem Befehl 'avatar' anzeigen,
+    aendern und loeschen.
+    Avatar-URIs anderer Spieler lassen sich mit 'finger -a' ausgeben.
+
+BEMERKUNGEN:
+    Diese Property kann nur vom Spieler oder einem EM modifiziert werden.
+
+SIEHE AUCH:
+    avatar
+
+LETZTER AENDERUNG:
+    03.9.2011, Zesstra
diff --git a/doc/props/P_AVERAGE_SIZE b/doc/props/P_AVERAGE_SIZE
new file mode 100644
index 0000000..4a61208
--- /dev/null
+++ b/doc/props/P_AVERAGE_SIZE
@@ -0,0 +1,8 @@
+NAME:
+    P_AVERAGE_SIZE                "average_size"                
+
+DEFINIERT IN:
+    /sys/player/description.h
+
+BESCHREIBUNG:
+     Durchschnittliche Groesse eines Wesens dieser Rasse (derzeit nur Player)
diff --git a/doc/props/P_AVERAGE_WEIGHT b/doc/props/P_AVERAGE_WEIGHT
new file mode 100644
index 0000000..18557cc
--- /dev/null
+++ b/doc/props/P_AVERAGE_WEIGHT
@@ -0,0 +1,8 @@
+NAME:
+    P_AVERAGE_WEIGHT                "average_weight"
+
+DEFINIERT IN:
+    /sys/player/description.h
+
+BESCHREIBUNG:
+     Durchschnittliche Gewicht eines Wesens dieser Rasse (derzeit nur Player)
diff --git a/doc/props/P_AWAY b/doc/props/P_AWAY
new file mode 100644
index 0000000..ba2c938
--- /dev/null
+++ b/doc/props/P_AWAY
@@ -0,0 +1,8 @@
+NAME:
+    P_AWAY                        "away"                        
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     String der ausgegeben wird, wenn man weg ist und eine Mitteilung bekommt.
diff --git a/doc/props/P_BAD_MSG b/doc/props/P_BAD_MSG
new file mode 100644
index 0000000..e067f2a
--- /dev/null
+++ b/doc/props/P_BAD_MSG
@@ -0,0 +1,27 @@
+NAME:
+     P_BAD_MSG                     "std_food_bad_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung, wenn eine Speise gerade verdirbt.
+     Befindet sich die Speise in einem Spieler, geht die Meldung an genau
+     diesen, liegt die Speise im Raum, geht die Meldung an alle anwesenden
+     Spieler.
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "@WER1 verdirbt."
+
+SIEHE AUCH:
+     P_LIFETIME, P_RESET_LIFETIME, P_NO_BAD,
+     wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_BLIND b/doc/props/P_BLIND
new file mode 100644
index 0000000..a80814b
--- /dev/null
+++ b/doc/props/P_BLIND
@@ -0,0 +1,35 @@
+NAME:
+    P_BLIND                       "blind"                       
+
+DEFINIERT IN:
+    /sys/player/viewcmd.h
+
+BESCHREIBUNG:
+    1, wenn der Spieler blind ist und nichts mehr sehen kann.
+
+    Wird von CannotSee() bei 'schau' und Betreten von Raeumen 
+    u.ae. ausgewertet.
+
+    P_BLIND kann auch auf einen String gesetzt werden, der 
+    dann statt des 'Du bist blind' ausgegeben wird.
+
+BEISPIELE:
+
+    this_player()->SetProp(P_BLIND,1);
+
+    this_player()->SetProp(P_BLIND,"Du hast Dir vorhin so schoen die "
+                                  +"Augen ausgekratzt ... deswegen "
+                                  +"siehst Du nun nichts mehr.\n");    
+BEMERKUNGEN:
+    Um herauszufinden, ob ein Spieler etwas sehen kann oder nicht,
+    sollte man lieber if(this_player()->CannotSee() != 0) pruefen
+    statt if(this_player()->QueryProp(P_BLIND)).
+
+    Denn CannotSee() beruecksichtigt auch Nachtsicht (bzw. hier 
+    eine nicht aktivierte) und die Lichtmodifikatoren.
+
+SIEHE AUCH:
+    P_LIGHT_MODIFIER, P_PLAYER_LIGHT, CannotSee
+
+----------------------------------------------------------------------------
+Letzte Aenderung: Sa 02.11.02, 00:30:00 Uhr, von Tilly
diff --git a/doc/props/P_BODY b/doc/props/P_BODY
new file mode 100644
index 0000000..7d04581
--- /dev/null
+++ b/doc/props/P_BODY
@@ -0,0 +1,9 @@
+NAME:
+    P_BODY                        "body"                        
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Numerischer Wert fuer die Abwehrstaerke des blanken Koerpers
+     des Wesens.
diff --git a/doc/props/P_BRIEF b/doc/props/P_BRIEF
new file mode 100644
index 0000000..33ef486
--- /dev/null
+++ b/doc/props/P_BRIEF
@@ -0,0 +1,8 @@
+NAME:
+    P_BRIEF                       "brief"                       
+
+DEFINIERT IN:
+    /sys/player/viewcmd.h
+
+BESCHREIBUNG:
+     Ist gesetzt, wenn der Spieler nur die Kurzbeschreibung sehen will.
diff --git a/doc/props/P_BUFFER b/doc/props/P_BUFFER
new file mode 100644
index 0000000..268f719
--- /dev/null
+++ b/doc/props/P_BUFFER
@@ -0,0 +1,9 @@
+NAME:
+    P_BUFFER                      "buffer"                      
+
+DEFINIERT IN:
+    /sys/player/comm.h
+
+BESCHREIBUNG:
+    Zeigt an, ob der Kobold des Spielers aktiv oder nicht aktiv ist.
+
diff --git a/doc/props/P_BUY_ONLY_PLANTS b/doc/props/P_BUY_ONLY_PLANTS
new file mode 100644
index 0000000..46747c4
--- /dev/null
+++ b/doc/props/P_BUY_ONLY_PLANTS
@@ -0,0 +1,15 @@
+NAME:
+    P_BUY_ONLY_PLANTS              "lib_p_buy_only_plants"
+
+DEFINIERT IN:
+    /sys/room/description.h
+
+BESCHREIBUNG:
+    Diese Property kann auf 1 gesetzt werden, wenn ein Laden nur Kraeuter
+    ankaufen darf. Hierzu muss /std/room/kraeuterladen.c geerbt werden,
+    da nur dieses Objekt die Property beruecksichtigt.
+
+SIEHE AUCH:
+    /std/room/kraeuterladen.c
+----------------------------------------------------------------------------
+Last modified: 02.04.2015 Arathorn 
diff --git a/doc/props/P_CALLED_FROM_IP b/doc/props/P_CALLED_FROM_IP
new file mode 100644
index 0000000..d9427d2
--- /dev/null
+++ b/doc/props/P_CALLED_FROM_IP
@@ -0,0 +1,8 @@
+NAME:
+    P_CALLED_FROM_IP              "called_from_ip"              
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Letzte IP-Adr, von der aus sich der Spieler eingeloggt hat.
diff --git a/doc/props/P_CAN_FLAGS b/doc/props/P_CAN_FLAGS
new file mode 100644
index 0000000..ba3eb2a
--- /dev/null
+++ b/doc/props/P_CAN_FLAGS
@@ -0,0 +1,9 @@
+NAME:
+    P_CAN_FLAGS                   "can_flags"                   
+
+DEFINIERT IN:
+    /sys/player/can.h
+
+BESCHREIBUNG:
+    Flags die bestimmte Befehle freischalten:
+    CAN_EMOTE, CAN_ECHO, CAN_REMOTE, CAN_PRESAY
diff --git a/doc/props/P_CAP_NAME b/doc/props/P_CAP_NAME
new file mode 100644
index 0000000..7fbe408
--- /dev/null
+++ b/doc/props/P_CAP_NAME
@@ -0,0 +1,9 @@
+NAME:
+    P_CAP_NAME                    "cap_name"                    
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Name des Spielers, der dekliniert und ausgegen wird.
+     NOT YET IMPLEMENTED.
diff --git a/doc/props/P_CARRIED_VALUE b/doc/props/P_CARRIED_VALUE
new file mode 100644
index 0000000..7df9b63
--- /dev/null
+++ b/doc/props/P_CARRIED_VALUE
@@ -0,0 +1,8 @@
+NAME:
+    P_CARRIED_VALUE               "carried"                     
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Entschaedigung, die der Spieler beim Einloggen erhaelt.
diff --git a/doc/props/P_CHATS b/doc/props/P_CHATS
new file mode 100644
index 0000000..633cdea
--- /dev/null
+++ b/doc/props/P_CHATS
@@ -0,0 +1,8 @@
+NAME:
+    P_CHATS                       "chats"                       
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Alist mit Strings, die das Monster zufaellig ausgibt.
diff --git a/doc/props/P_CHAT_CHANCE b/doc/props/P_CHAT_CHANCE
new file mode 100644
index 0000000..b6acf08
--- /dev/null
+++ b/doc/props/P_CHAT_CHANCE
@@ -0,0 +1,8 @@
+NAME:
+    P_CHAT_CHANCE                 "chat_chance"                 
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Wahrscheinlichkeit, mit der die Chats ausgegeben werden.
diff --git a/doc/props/P_CLASS b/doc/props/P_CLASS
new file mode 100644
index 0000000..e074394
--- /dev/null
+++ b/doc/props/P_CLASS
@@ -0,0 +1,19 @@
+P_CLASS
+NAME:
+     P_CLASS						"class"
+
+DEFINIERT IN:
+     <thing/description.h>
+
+BESCHREIBUNG:
+     Die Klassifizierung eines Objektes, soweit sie nicht schon ueber die 
+     Rasse oder die IDs des Objektes erfolgt ist. Zum Setzen dieser Property 
+     sollte man AddClass() benutzen, zur Klassenabfrage steht 
+     is_class_member() zur Verfuegung.
+     Die moeglichen Klassen findet man in /sys/class.h
+
+SIEHE AUCH:
+     AddClass(), RemoveClass(), is_class_member()
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 20:30:09 1996 by Wargon
diff --git a/doc/props/P_CLOCKMSG b/doc/props/P_CLOCKMSG
new file mode 100644
index 0000000..8b72d68
--- /dev/null
+++ b/doc/props/P_CLOCKMSG
@@ -0,0 +1,8 @@
+NAME:
+    P_CLOCKMSG                    "clockmsg"                    
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Die Meldung wird zur vollen Stunde ausgegeben
diff --git a/doc/props/P_CLONER b/doc/props/P_CLONER
new file mode 100644
index 0000000..659b614
--- /dev/null
+++ b/doc/props/P_CLONER
@@ -0,0 +1,12 @@
+NAME:
+    P_CLONER                      "cloner"                      
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Enthaelt einen String mit dem Namen desjenigen, der das Objekt gecloned 
+     hat.
+
+SIEHE AUCH:
+     P_CLONE_TIME
diff --git a/doc/props/P_CLONE_MSG b/doc/props/P_CLONE_MSG
new file mode 100644
index 0000000..d9c96a2
--- /dev/null
+++ b/doc/props/P_CLONE_MSG
@@ -0,0 +1,8 @@
+NAME:
+    P_CLONE_MSG                   "clone_msg"                   
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Meldung, die beim Clonen eines Obj ausgegegen wird (nur Magier)
diff --git a/doc/props/P_CLONE_TIME b/doc/props/P_CLONE_TIME
new file mode 100644
index 0000000..a904846
--- /dev/null
+++ b/doc/props/P_CLONE_TIME
@@ -0,0 +1,14 @@
+NAME:
+    P_CLONE_TIME                   "clone_time"                      
+
+DEFINIERT IN:
+    /sys/thing/description.h
+
+BESCHREIBUNG:
+     Enthaelt die Clone-Time eines Objektes.
+     Heutzutage obsolet, bitte stattdessen lieber die Efun object_time()
+     benutzen.
+
+SIEHE AUCH:
+     Verwandt: object_time(E)
+     Aehnlich: program_time(E)
diff --git a/doc/props/P_CLOTHING b/doc/props/P_CLOTHING
new file mode 100644
index 0000000..793f098
--- /dev/null
+++ b/doc/props/P_CLOTHING
@@ -0,0 +1,22 @@
+P_CLOTHINGS
+
+NAME:
+     P_CLOTHING                     "std:clothing"
+
+DEFINIERT IN:
+     <living/clothing.h>
+
+BESCHREIBUNG:
+     Array mit der getragenen nicht-schuetzenden Kleidung des Lebewesen.
+     
+     Falls ihr die Kleidung des Lebewesens, ggf. mit bestimten Kriterien,
+     ermitteln wollt, benutzt hierfuer bitte die Funktion FilterClothing()
+     statt selber ueber dieses Array zu laufen.
+
+     Diese Property kann nur vom Lebewesen selber beschrieben werden.
+     
+SIEHE AUCH:
+     Verwandt:		QueryArmourByType(L), FilterClothing(), FilterArmours()
+                  Wear(), Unwear(), P_ARMOURS
+
+14.03.2009, Zesstra
diff --git a/doc/props/P_CMSG b/doc/props/P_CMSG
new file mode 100644
index 0000000..9001722
--- /dev/null
+++ b/doc/props/P_CMSG
@@ -0,0 +1,8 @@
+NAME:
+    P_CMSG                        "clonemsg"                    
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     *** OBSOLET! *** Siehe P_CLONE_MSG
diff --git a/doc/props/P_CNT_STATUS b/doc/props/P_CNT_STATUS
new file mode 100644
index 0000000..cd92324
--- /dev/null
+++ b/doc/props/P_CNT_STATUS
@@ -0,0 +1,9 @@
+NAME:
+    P_CNT_STATUS                  "cnt_status"                  
+
+DEFINIERT IN:
+    /sys/container.h
+
+BESCHREIBUNG:
+     Status des Containers (offen, geschlossen, abgeschlossen)
+     siehe auch /sys/container.h
diff --git a/doc/props/P_COMBATCMDS b/doc/props/P_COMBATCMDS
new file mode 100644
index 0000000..c29a65f
--- /dev/null
+++ b/doc/props/P_COMBATCMDS
@@ -0,0 +1,31 @@
+NAME:
+    P_COMBATCMDS                  "combatcmds"                  
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Fuer den Kampf gebrauchbare Befehle spezieller Objekte (damit auch
+     Monster sie automatisch richtig anwenden koennen)
+     Der Inhalt von P_COMBATCMDS ist ein Mapping, der Key ist das Kommando,
+     um den Gegenstand zu benutzen (also z.B. "wirf flammenkugel"), und der
+     Value ein weiteres Mapping mit Zusatzinfos (definiert in /sys/combat.h).
+     Folgende Keys sind definiert:
+     - C_MIN, C_AVG, C_MAX:
+       minimaler, mittlerer und maximaler Schaden, den das
+       Objekt macht. Alle Angaben in LEBENSPUNKTEN, d.h. Defend-Einheiten/10.
+       Bei einem Aufruf wie 'enemy->Defend(200+random(200), ...)' ist dann
+       C_MIN=20, C_AVG=30, C_MAX=40.
+     - C_DTYPES:
+       Array mit dem Schadenstyp oder den Schadenstypen. Beim Eisstab
+       wuerde der Eintrag dann 'C_DTYPES:({DT_COLD})' lauten.
+     - C_HEAL:
+       Sollte das Kampfobjekt ueber die Moeglichkeit verfuegen, den Anwender
+       irgendwie zu heilen, so wird hier die Heilung in LP/MP eingetragen.
+       Das funktioniert auch bei Objekten, die nur heilen, also sonst
+       nichts mit Kampf zu tun haben.
+       Im Lupinental z.B. gibt es Pfirsiche, die beim Essen 5LP heilen. Da
+       kann man dann 'SetProp(P_COMBATCMDS, (["iss pfirsich":([C_HEAL:5])]))'
+       eintragen.
+     Es sind auch mehrere Kommandos moeglich, z.B. bei Objekten, die sowohl
+     heilen als auch Kampfwirkung haben.
diff --git a/doc/props/P_COMMANDS b/doc/props/P_COMMANDS
new file mode 100644
index 0000000..527d1a1
--- /dev/null
+++ b/doc/props/P_COMMANDS
@@ -0,0 +1,47 @@
+P_COMMANDS
+NAME:
+     P_COMMANDS "commands"
+
+DEFINIERT IN:
+     <thing/commands.h>
+
+BESCHREIBUNG:
+     Diese Property enthaelt ein Mapping mit den Befehlen, die dem Objekt
+     zugeordnet sind.
+
+     Sie sollte nicht von Hand manipuliert werden, sondern nur ueber die
+     Funktionen AddCmd() und RemoveCmd().
+
+     Das Mapping hat folgenden Aufbau:
+
+     ([ befehl : ({funktion1,...});
+                 ({flag1,...});
+                 ({regel1,...});
+                 ({id1, ...}),
+        ... ])
+
+     Die Eintraege entsprechen den Parametern des AddCmd()-Aufrufs, sind
+     aber in anderer Form. Als Beispiel:
+
+     AddCmd(verb,fun1,1);
+     AddCmd(verb+syn1a|syn1b&syn2a|syn2b|syn2c,fun2,
+	   error1_notify|error2_notify^error2_write);
+     -->
+     ([verb:({fun1,fun2});					  // funs
+	    ({1,({error1_notify, error2_write^error2_say, 1})});  // flags
+            ({0,({({syn1a,syn1b}),({syn2a,syn2b,syn2c})})});	  // rules
+            0])							  // IDs
+
+     Aufgeschluesselt sehen die einzelnen Arrays folgendermassen aus:
+
+     Rules: ({<Regelsatz fuer fun1>, ({<1. Synonymgruppe>,
+				       <2. Synonymgruppe, ...}), ...})
+     Flags: ({<Flag fuer fun1>, ({<Fehlermeldung 1. Synonymgruppe>, ... ,
+				  [, Index fuer write anstatt notify_fail]}),
+	      ... })
+     IDs:   0 oder ({<ID fuer fun1>}) oder ({0, <ID fuer fun2>}) ...
+
+SIEHE AUCH:
+     /std/thing/commands.c, AddCmd(), RemoveCmd()
+
+08.Dez.2003 Gloinson
diff --git a/doc/props/P_COMPILER_PATH b/doc/props/P_COMPILER_PATH
new file mode 100644
index 0000000..ecd2743
--- /dev/null
+++ b/doc/props/P_COMPILER_PATH
@@ -0,0 +1,21 @@
+NAME:
+    P_COMPILER_PATH               "compiler_path"               
+
+DEFINIERT IN:
+    /sys/v_compiler.h
+
+BESCHREIBUNG:
+    Directory in dem ein Virtual Compiler Objekte erzeugt.
+    Muss im virtual_compiler.c gesetzt werden.
+
+    Der VirtualCompiler muss nicht zwingend in diesem Verzeichnis
+    liegen, um zu funktionieren, sollte es aber, um die Zuordnung des VCs zu
+    "seinen" Objekten zu erleichern.
+
+BEISPIEL:
+    SetProp(P_COMPILER_PATH,"/d/region/magier/vc/");
+
+SIEHE AUCH:
+    P_STD_OBJECT, virtual_compiler
+-------------------------------------------------------------------------
+Letzte Aenderung: 23.10.2007, von Zesstra
diff --git a/doc/props/P_CONSUME_MSG b/doc/props/P_CONSUME_MSG
new file mode 100644
index 0000000..8b8a05a
--- /dev/null
+++ b/doc/props/P_CONSUME_MSG
@@ -0,0 +1,24 @@
+NAME:
+     P_CONSUME_MSG                 "std_food_consume_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung an den Raum exklusive Konsument, wenn eine Speise konsumiert
+     wird.
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "@WER2 konsumiert @WEN1."
+
+SIEHE AUCH:
+     /std/food.c, wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_CONTAINER b/doc/props/P_CONTAINER
new file mode 100644
index 0000000..ed15ca5
--- /dev/null
+++ b/doc/props/P_CONTAINER
@@ -0,0 +1,8 @@
+NAME:
+    P_CONTAINER                   "container"                   
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_CONTENTS b/doc/props/P_CONTENTS
new file mode 100644
index 0000000..1212da7
--- /dev/null
+++ b/doc/props/P_CONTENTS
@@ -0,0 +1,8 @@
+NAME:
+    P_CONTENTS                    "contents"                    
+
+DEFINIERT IN:
+    /sys/container.h
+
+BESCHREIBUNG:
+     *** OBSOLET! ***
diff --git a/doc/props/P_CORPSE b/doc/props/P_CORPSE
new file mode 100644
index 0000000..bcb0ab6
--- /dev/null
+++ b/doc/props/P_CORPSE
@@ -0,0 +1,25 @@
+NAME:
+    P_CORPSE		"corpse"
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Hier kann man angeben, welche Art von Leiche beim Tod des NPCs
+    hinterlassen wird. Damit die Leiche auf dem Moerder-Kanal senden
+    kann, muss das Leichen-Objekt /std/corpse sein oder erben.
+
+BEISPIELE:
+    Die uebliche Standardleiche befindet sich unter "/std/corpse.c",
+    welches auch die Defaulteinstellung fuer diese Property darstellt:
+      SetProp(P_CORPSE,"/std/corpse");
+    Man koennte aber auch einen Zombie entstehen lassen:
+      SetProp(P_CORPSE,PATH("zombie"));
+    PATH kennzeichnet hierbei den Pfad, unter welchem "zombie.c" zu
+    finden ist.
+
+SIEHE AUCH:
+    P_NOCORPSE, P_ZAP_MSG, P_DIE_MSG, P_MURDER_MSG, P_KILL_MSG
+
+----------------------------------------------------------------------------
+Last modified: Mon Apr 07 11:02:06 2003 by Mandragon
diff --git a/doc/props/P_CURRENTDIR b/doc/props/P_CURRENTDIR
new file mode 100644
index 0000000..87bbd5c
--- /dev/null
+++ b/doc/props/P_CURRENTDIR
@@ -0,0 +1,15 @@
+NAME:
+    P_CURRENTDIR                  "currentdir"
+
+DEFINIERT IN:
+    /sys/shells.h
+
+BESCHREIBUNG:
+    Momentanes Verzeichnis in dem der Magier ist.
+    (nur fuer Magier von Belang)
+
+Siehe auch:
+    P_CURRENTDIR
+
+Letzte Aenderung:
+    03.06.2015, Bugfix
diff --git a/doc/props/P_CURSED b/doc/props/P_CURSED
new file mode 100644
index 0000000..d9486fe
--- /dev/null
+++ b/doc/props/P_CURSED
@@ -0,0 +1,35 @@
+P_CURSED
+
+NAME:
+     P_CURSED "cursed"
+
+DEFINIERT IN:
+     <properties.h>
+
+BESCHREIBUNG:
+     Ruestungen und Waffen, die sich, einmal angezogen bzw. gezueckt, nicht
+     wieder entfernen lassen sollen, kann man ueber diese Property
+     realisieren. Die Waffe oder Ruestung hat dann in der Regel negative
+     Auswirkungen auf den Traeger...
+
+     Setzt man diese Property auf eine Zahl ungleich 0, so bekommt man bei
+     dem Versuch, den verfluchten Gegenstand auszuziehen bzw. wegzustecken
+     eine Defaultmeldung.
+
+     Traegt man dagegen einen String ein, so wird dieser beim Versuch, den
+     Gegenstand loszuwerden, ausgegeben.
+
+BEMERKUNGEN:
+     Der 'Fluch' wird erst wirksam, wenn das Opfer die Waffe zueckt bzw. die
+     Ruestung anzieht. Ist dies erst einmal geschehen, helfen nur noch
+     Zaubersprueche oder fluchbrechende Institutionen.
+
+     Moechte man, dass der Gegenstand entfluchbar ist, dann sollte er auch
+     ansprechbar sein (gueltige ID, nicht unsichtbar), da das durch derzeitige
+     Entfluchmoeglichkeiten vorausgesetzt wird.
+
+SIEHE AUCH:
+     /std/armour.c, /std/weapon.c
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 14:45:28 1996 by Wargon
diff --git a/doc/props/P_DAMAGED b/doc/props/P_DAMAGED
new file mode 100644
index 0000000..671e0e0
--- /dev/null
+++ b/doc/props/P_DAMAGED
@@ -0,0 +1,28 @@
+P_DAMAGED
+
+NAME:
+     P_DAMAGED "item_damaged"
+
+DEFINIERT IN:
+     <combat.h>
+
+BESCHREIBUNG:
+     Ruestungen und Waffen koennen im Eifer des Gefechts beschaedigt werden.
+     Der Grad der Beschaedigung sollte in dieser Property festgehalten
+     werden.
+
+     Bei Waffen ergibt sich die urspruengliche Waffenklasse aus der Summe
+     von aktueller Waffenklasse und dem Wert von P_DAMAGED:
+     altes P_WC = aktuelles P_WC + P_DAMAGED.
+
+     Analoges gilt fuer die Ruestungsklasse einer beschaedigten Ruestung:
+     altes P_AC = aktuelles P_AC + P_DAMAGED.
+
+     P_DAMAGED bitte niemals direkt setzen, sondern immer ueber
+     die Funktion Damage() manipulieren!
+
+SIEHE AUCH:
+     /std/armour.c, /std/weapon.c, TakeFlaw(), QueryFlaw(), Damage()
+
+----------------------------------------------------------------------------
+02.10.2007, Zesstra
diff --git a/doc/props/P_DAMAGE_MSG b/doc/props/P_DAMAGE_MSG
new file mode 100644
index 0000000..8923b19
--- /dev/null
+++ b/doc/props/P_DAMAGE_MSG
@@ -0,0 +1,47 @@
+P_DAMAGE_MSG
+
+NAME:
+     P_DAMAGE_MSG      "std_p_dam_msg"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+     In dieser Property lassen sich individuelle Treffer-/Schadensmeldungen
+     fuer dieses Lebewesen festlegen. Sie werden verwendet, falls bei
+     eingehendem Schaden der Aufrufer von Defend() Schadensmeldungen wuenscht
+     (d.h. SP_SHOW_DAMAGE != 0), jedoch keine eigenen Meldungen vorgibt.
+
+     Enthaelt diese Property kein Array, werden ggf. die Standardmeldungen
+     ausgegeben.
+
+     Datenstruktur der Property:
+       ({
+        ({ int lphit1, string mess_me,
+                       string mess_en,
+                       string mess_room }),
+        ({ lphit2, mess_me, mess_en, mess_room }),
+        ...
+        ({ lphitn, mess_me, mess_en, mess_room }),
+       })
+       wobei lphit1<lphit2<...<lphitn sein muss, d.h. das Array-
+       Array ist aufsteigend sortiert.
+
+     Ist ein Treffer x LP hart, werden die Meldungen des lphit-
+     Arrays ausgegeben, dessen Wert am naechsten unter dem Schaden
+     liegt.
+
+     In den Meldungen mess_me (an den Getroffenen), mess_en (an
+     den Feind), mess_room (an die restlichen Umstehenden) koennen
+     Ersatzstrings wie folgt verwendet werden:
+     @WER1/@WESSEN1/@WEM1/@WEN1 - name(casus) des Getroffenen (TO)
+     @WER2/@WESSEN2/@WEM2/@WEN2 - name(casus) des Feindes (enemy)
+
+BEISPIEL:
+     
+
+SIEHE AUCH:
+     Defend()
+     /std/living/combat.c
+
+15.09.2010, Zesstra
diff --git a/doc/props/P_DAM_DESC b/doc/props/P_DAM_DESC
new file mode 100644
index 0000000..59938c7
--- /dev/null
+++ b/doc/props/P_DAM_DESC
@@ -0,0 +1,35 @@
+P_DAM_DESC
+
+NAME:
+     P_DAM_DESC "dam_desc"
+
+DEFINIERT IN:
+     <weapon/description.h>
+
+BESCHREIBUNG:
+     In dieser Property befindet sich ein String oder String-Array, durch 
+     den die Langbeschreibung einer Ruestung oder Waffe ergaenzt wird,
+     wenn diese Beschaedigt ist.
+
+BEMERKUNGEN:
+     Wird ein String gesetzt, so wird dieser angezeigt, wenn die Waffe
+     mehr als die Haelfte beschaedigt ist. Bei einem Array wird der
+     Text entsprechend dem Grad der Beschaedigung ausgewaehlt; das Array
+     muss in der Reihenfolge zunehmender Beschaedigung sortiert sein.
+     
+     Bei Waffen ist P_DAM_DESC defaultmaessig auf DFLT_DAM_DESC gesetzt,
+     bei Ruestungen auf 0.
+
+BEISPIELE:
+     SetProp(P_DAM_DESC,"ist beschaedigt");
+     SetProp(P_DAM_DESC,({
+         "ist etwas beschaedigt",
+         "ist beschaedigt",
+         "ist beschaedigt",
+         "ist sehr beschaedigt"}) );
+
+SIEHE AUCH:
+     /std/weapon/description.c
+
+----------------------------------------------------------------------------
+Last modified: Mon Oct 14 15:29:00 1996 by Paracelsus
diff --git a/doc/props/P_DAM_TYPE b/doc/props/P_DAM_TYPE
new file mode 100644
index 0000000..56fb4bb
--- /dev/null
+++ b/doc/props/P_DAM_TYPE
@@ -0,0 +1,22 @@
+P_DAM_TYPE
+
+NAME:
+     P_DAM_TYPE "dam_type"
+
+DEFINIERT IN:
+     <weapon.h>
+
+BESCHREIBUNG:
+     Was fuer eine Art von Schaden richtet die Waffe an? Hier kann man einen
+     String oder ein Array von Strings angeben, je nachdem, welche Effekte
+     die Waffe ausloesen kann. Man sollte hier nur die in <combat.h>
+     definierten Schadenstypen verwenden.
+
+     Fragt man diese Property ab, bekommt man uebrigens immer ein Array von
+     Strings zurueck.
+
+SIEHE AUCH:
+     /std/weapon.c
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 15:23:43 1996 by Wargon
diff --git a/doc/props/P_DEADS b/doc/props/P_DEADS
new file mode 100644
index 0000000..ca44cd7
--- /dev/null
+++ b/doc/props/P_DEADS
@@ -0,0 +1,9 @@
+NAME:
+    P_DEADS                       "deads"                       
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Anzahl der Tode des Spielers seit Einfuehrung dieser Property (irgendwann
+     im Dezember 94)
diff --git a/doc/props/P_DEAF b/doc/props/P_DEAF
new file mode 100644
index 0000000..8745dfd
--- /dev/null
+++ b/doc/props/P_DEAF
@@ -0,0 +1,10 @@
+NAME:
+    P_DEAF                        "deaf"                        
+
+DEFINIERT IN:
+    /sys/player/comm.h
+
+BESCHREIBUNG:
+     Der Spieler ist taub. Falls hier ein String steht, wird dieser bei
+     "teile ... mit" an den Mitteilenden ausgegeben, ansonsten kommt nur
+     "Soundso ist leider gerade taub.\n"
diff --git a/doc/props/P_DEATH_MSG b/doc/props/P_DEATH_MSG
new file mode 100644
index 0000000..4e044bf
--- /dev/null
+++ b/doc/props/P_DEATH_MSG
@@ -0,0 +1,48 @@
+DEFINIERT IN:

+        /sys/living/combat.h

+

+BESCHREIBUNG:

+        In dieser Property kann man ein Array ablegen, das beim Tod eines

+        Spielers ausgewertet und der darin enthaltene String

+        anschliessend auf dem Todeskanal gesendet wird.

+        Der Array muss folgenden Aufbau haben:

+

+          SetProp( P_DEATH_MSG, ({ Text, Flag }) )

+          

+        Text: Der Text kann beliebig eingegeben werde. Allerdings darf 

+              er nicht mit einem '\n' abgeschlossen werden.

+        

+        Flag: Hier kann man drei Arten von Sendemethoden waehlen.

+              1. MSG_SAY      Normales Senden

+              2. MSG_GEMOTE   Genitiv-Emote

+              3. MSG_EMOTE    Emote

+

+

+BEISPIEL:

+        Der Spieler soll direkt nach seinem Tod eine Meldung auf dem 

+        Todeskanal senden.

+        

+        Nachricht auf dem Todes-Kanal:

+	

+        [Tod:Spieler] Ich will keine Beleidsbekundungen!

+        

+        void spieler_stirbt()

+	{

+         this_player()->SetProp( P_DEATH_MSG, ({ "Ich will keine "

+                                        "Beleidsbekundungen!", MSG_SAY}) );

+         this_player()->die();

+        }

+        

+        Nachricht auf dem Todes-Kanal:

+	

+        [Tod:Spieler liebt es zu sterben.]

+        

+        void spieler_stirbt()

+        {

+         this_player()->SetProp( P_DEATH_MSG, ({ "liebt es zu sterben.",

+                                                 MSG_EMOTE }) );

+         this_player()->die();

+        }

+

+SIEHE AUCH:

+        P_MURDER_MSG, P_FORCE_MURDER_MSG

diff --git a/doc/props/P_DEATH_SPONSORED_BY b/doc/props/P_DEATH_SPONSORED_BY
new file mode 100644
index 0000000..52d7d42
--- /dev/null
+++ b/doc/props/P_DEATH_SPONSORED_BY
@@ -0,0 +1,25 @@
+NAME:
+    P_DEATH_SPONSORED_BY          "responsible_wizard_for_death"
+
+DEFINIERT IN:
+    /sys/living/combat.h
+
+BESCHREIBUNG:
+    Diese Property hat fuer den Spielverlauf keinerlei Bedeutung.
+    Doch gibt es Magier, die ihren Namen gern in /log/KILLS lesen.
+    Wird ein Spieler durch einen Npc getoetet, ist normalerweise der
+    Magier fuer den Tod verantwortlich, in dessen Verzeichnis sich 
+    das Monster befindet. 
+    Doch gibt es nun auch den Fall, dass sich das Monster in einem 
+    Verzeichnis /alle/mon/ befindet, oder der Spieler durch eine
+    Aktion im Raum oder an einem Objekt stirbt.
+
+    Ist in einem solchen Monster, Raum oder Objekt diese Property
+    gesetzt, wird der dort angebene String an das Log-File ueber-
+    geben.
+
+BEISPIEL:
+    SetProp(P_DEATH_SPONSORED_BY,"tilly");
+ 
+----------------------------------------------------------------------------
+Last modified: Don Feb 15 14:01:00 2001 von Tilly
diff --git a/doc/props/P_DEFAULT_GUILD b/doc/props/P_DEFAULT_GUILD
new file mode 100644
index 0000000..28ca4e6
--- /dev/null
+++ b/doc/props/P_DEFAULT_GUILD
@@ -0,0 +1,25 @@
+NAME:
+	P_DEFAULT_GUILD			"default_guild"
+
+DEFINIERT IN:
+	/sys/new_skills.h
+
+BESCHREIBUNG:
+	Diese Property enthaelt den Namen der Gilde, welcher der Spieler
+	standardmaessig angehoert. Der Name wird hierbei in Form eines
+	kleingeschriebenen Strings angegeben. Ist P_GUILD gleich Null, so
+	wird bei der Abfrage selbiger Property hierfuer der Eintrag von
+	P_DEFAULT_GUILD eingesetzt.
+
+BEMERKUNGEN:
+	Nach dem ersten Einloggen des Spielers wird ebenfalls dieser Eintrag
+	genutzt, um die Gildenzugehoerigkeit festzulegen. Dies kann dazu
+	genutzt werden, um eine rassenabhaengige Bestimmung der
+	Standardgilde zu gewaehrleisten
+	 (Felinen -> Katzenkrieger, andere Rassen -> Abenteurer).
+
+SIEHE AUCH:
+	P_GUILD, P_VISIBLE_GUILD
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_DEFAULT_NOTIFY_FAIL b/doc/props/P_DEFAULT_NOTIFY_FAIL
new file mode 100644
index 0000000..611e8df
--- /dev/null
+++ b/doc/props/P_DEFAULT_NOTIFY_FAIL
@@ -0,0 +1,8 @@
+NAME:
+    P_DEFAULT_NOTIFY_FAIL         "default_notify_fail"         
+
+DEFINIERT IN:
+    /sys/player.h
+
+BESCHREIBUNG:
+     Welche Fehlermeldung kommt, wenn kein Objekt ein notify_fail macht?
diff --git a/doc/props/P_DEFENDERS b/doc/props/P_DEFENDERS
new file mode 100644
index 0000000..b471d45
--- /dev/null
+++ b/doc/props/P_DEFENDERS
@@ -0,0 +1,30 @@
+NAME:
+	P_DEFENDERS			"defenders"
+
+DEFINIERT IN:
+	/sys/new_skills.h
+
+BESCHREIBUNG:
+	Diese Property wird in Lebewesen gesetzt, welche zum Beispiel durch
+	andere Lebewesen verteidigt werden. Die Verteidiger muessen
+	natuerlich bekannt sein, damit sie per InformDefend() ueber Angriffe
+	informiert werden und per DefendOther() in den laufenden Angriff
+	eingreifen koennen (zum Beispiel Schaeden abwehren oder umwandeln).
+	Es muessen jedoch nicht unbedingt Lebewesen oder echte Verteidiger
+	sein, auch beliebige Objekte koennen ueber Angriffe informiert
+	werden und in diese eingreifen. Allerdings besteht die
+	Einschraenkung, dass diese Objekte in der gleichen Umgebung sein
+	muessen, wie das zu verteidigende Lebewesen oder im zu verteidigenden
+	Lebewesen selbst.
+	Die Objekte, welche dies betrifft, sind in Form eines Arrays in
+	der Property P_DEFENDERS abgelegt.
+	Gesetzt und geloescht werden sollten die Eintraege dieses Arrays
+	jedoch nur mittels der dafuer bereitgestellten Funktionen
+	AddDefender() und RemoveDefender().
+
+SIEHE AUCH:
+	AddDefender(), RemoveDefender(), InformDefend(), DefendOther(),
+	/std/living/combat.c
+
+----------------------------------------------------------------------------
+Last modified: 21.09.2007, Zesstra
diff --git a/doc/props/P_DEFEND_FUNC b/doc/props/P_DEFEND_FUNC
new file mode 100644
index 0000000..02f1e8c
--- /dev/null
+++ b/doc/props/P_DEFEND_FUNC
@@ -0,0 +1,31 @@
+P_DEFEND_FUNC
+
+NAME:
+     P_DEFEND_FUNC "defend_func"
+
+DEFINIERT IN:
+     <armour.h>
+
+BESCHREIBUNG:
+     Falls ein Objekt eine DefendFunc() fuer die Ruestung definiert, so muss
+     dieses Objekt in dieser Property eingetragen sein.
+
+     Die Auswertung dieser Property erfolgt in QueryDefend().
+
+BEMERKUNGEN:
+     1. Es funktioniert _nicht_ hier eine Closure reinzuschreiben.
+     2. Diese Prop laesst sich _nicht_ sinnvoll mit Set() setzen, also z.B.
+        keine Query-Methoden hier reinzuschreiben.
+     3. Definieren von _set_defend_func() oder Set-Methoden via Set()
+        funktioniert auch nicht, zumindest nicht mit dem gewuenschten
+        Ergebnis. ;-)
+     4. Bei Verwendung bitte Balance-Richtlinien beachten!
+
+BEISPIELE:
+     Siehe das Beispiel zu DefendFunc()
+
+SIEHE AUCH:
+     /std/armour.c, DefendFunc(), balance, grenzwerte
+
+----------------------------------------------------------------------------
+Last modified: Sat May 18 15:26:17 1996 by Wargon
diff --git a/doc/props/P_DEFUEL_AMOUNT_DRINK b/doc/props/P_DEFUEL_AMOUNT_DRINK
new file mode 100644
index 0000000..558e261
--- /dev/null
+++ b/doc/props/P_DEFUEL_AMOUNT_DRINK
@@ -0,0 +1,20 @@
+NAME:
+    P_DEFUEL_AMOUNT_DRINK                          "defuel_amount_drink"
+
+DEFINIERT IN:
+    /sys/defuel.h
+
+BESCHREIBUNG:
+    Enthaelt die rassenabhaengige Enttankvorgabe fuer Trinken.
+    
+SIEHE AUCH:
+     Aehnlich:  P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+                P_DEFUEL_LIMIT_FOOD, P_DEFUEL_LIMIT_DRINK,
+                P_DEFUEL_AMOUNT_FOOD
+     Methoden:  defuel_drink, defuel_food
+     Tanken:    consume, drink_alcohol, drink_soft, eat_food
+     Weitere:   P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/props/P_DEFUEL_AMOUNT_FOOD b/doc/props/P_DEFUEL_AMOUNT_FOOD
new file mode 100644
index 0000000..a8554be
--- /dev/null
+++ b/doc/props/P_DEFUEL_AMOUNT_FOOD
@@ -0,0 +1,20 @@
+NAME:
+    P_DEFUEL_AMOUNT_FOOD                          "defuel_amount_food"
+
+DEFINIERT IN:
+    /sys/defuel.h
+
+BESCHREIBUNG:
+    Enthaelt die rassenabhaengige Enttankvorgabe fuer Essen.
+    
+SIEHE AUCH:
+     Aehnlich:  P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+                P_DEFUEL_LIMIT_FOOD, P_DEFUEL_LIMIT_DRINK,
+                P_DEFUEL_AMOUNT_DRINK
+     Methoden:  defuel_drink, defuel_food
+     Tanken:    consume, drink_alcohol, drink_soft, eat_food
+     Weitere:   P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/props/P_DEFUEL_LIMIT_DRINK b/doc/props/P_DEFUEL_LIMIT_DRINK
new file mode 100644
index 0000000..0a59df4
--- /dev/null
+++ b/doc/props/P_DEFUEL_LIMIT_DRINK
@@ -0,0 +1,21 @@
+NAME:
+    P_DEFUEL_LIMIT_DRINK                          "defuel_limit_drink"
+
+DEFINIERT IN:
+    /sys/defuel.h
+
+BESCHREIBUNG:
+    Enthaelt die rassenabhaengige minimale Menge an Trinken, ab dem ein
+    Enttankvorgang fuer den Spieler moeglich ist.
+    
+SIEHE AUCH:
+     Aehnlich:  P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+                P_DEFUEL_LIMIT_FOOD,
+                P_DEFUEL_AMOUNT_FOOD, P_DEFUEL_AMOUNT_DRINK
+     Methoden:  defuel_drink, defuel_food
+     Tanken:    consume, drink_alcohol, drink_soft, eat_food
+     Weitere:   P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/props/P_DEFUEL_LIMIT_FOOD b/doc/props/P_DEFUEL_LIMIT_FOOD
new file mode 100644
index 0000000..26e33fd
--- /dev/null
+++ b/doc/props/P_DEFUEL_LIMIT_FOOD
@@ -0,0 +1,21 @@
+NAME:
+    P_DEFUEL_LIMIT_FOOD                          "defuel_limit_food"
+
+DEFINIERT IN:
+    /sys/defuel.h
+
+BESCHREIBUNG:
+    Enthaelt die rassenabhaengige minimale Menge an Essen, ab dem ein
+    Enttankvorgang fuer den Spieler moeglich ist.
+    
+SIEHE AUCH:
+     Aehnlich:  P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+                P_DEFUEL_LIMIT_DRINK,
+                P_DEFUEL_AMOUNT_FOOD, P_DEFUEL_AMOUNT_DRINK
+     Methoden:  defuel_drink, defuel_food
+     Tanken:    consume, drink_alcohol, drink_soft, eat_food
+     Weitere:   P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/props/P_DEFUEL_TIME_DRINK b/doc/props/P_DEFUEL_TIME_DRINK
new file mode 100644
index 0000000..de5eaf0
--- /dev/null
+++ b/doc/props/P_DEFUEL_TIME_DRINK
@@ -0,0 +1,21 @@
+NAME:
+    P_DEFUEL_TIME_DRINK                          "defuel_time_drink"
+
+DEFINIERT IN:
+    /sys/defuel.h
+
+BESCHREIBUNG:
+    Enthaelt die rassenabhaengige minimale Zeit zwischen einzelnen
+    Enttankvorgaengen fuer Trinken eines Spielers.
+    
+SIEHE AUCH:
+     Aehnlich:  P_DEFUEL_TIME_FOOD,
+                P_DEFUEL_LIMIT_FOOD, P_DEFUEL_LIMIT_DRINK,
+                P_DEFUEL_AMOUNT_FOOD, P_DEFUEL_AMOUNT_DRINK
+     Methoden:  defuel_drink, defuel_food
+     Tanken:    consume, drink_alcohol, drink_soft, eat_food
+     Weitere:   P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/props/P_DEFUEL_TIME_FOOD b/doc/props/P_DEFUEL_TIME_FOOD
new file mode 100644
index 0000000..d7bdd79
--- /dev/null
+++ b/doc/props/P_DEFUEL_TIME_FOOD
@@ -0,0 +1,21 @@
+NAME:
+    P_DEFUEL_TIME_FOOD                          "defuel_time_food"
+
+DEFINIERT IN:
+    /sys/defuel.h
+
+BESCHREIBUNG:
+    Enthaelt die rassenabhaengige minimale Zeit zwischen einzelnen
+    Enttankvorgaengen fuer Essen eines Spielers.
+    
+SIEHE AUCH:
+     Aehnlich:  P_DEFUEL_TIME_DRINK,
+                P_DEFUEL_LIMIT_FOOD, P_DEFUEL_LIMIT_DRINK,
+                P_DEFUEL_AMOUNT_FOOD, P_DEFUEL_AMOUNT_DRINK
+     Methoden:  defuel_drink, defuel_food
+     Tanken:    consume, drink_alcohol, drink_soft, eat_food
+     Weitere:   P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_FOOD, P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, enttanken, food
+
+9. August 2015 Gloinson
diff --git a/doc/props/P_DEPARTMSG b/doc/props/P_DEPARTMSG
new file mode 100644
index 0000000..515c8bd
--- /dev/null
+++ b/doc/props/P_DEPARTMSG
@@ -0,0 +1,8 @@
+NAME:
+    P_DEPARTMSG                   "departmsg"                   
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+     Meldung, mit der ein Transporter ablegt.
diff --git a/doc/props/P_DESCRIPTION b/doc/props/P_DESCRIPTION
new file mode 100644
index 0000000..d275cc7
--- /dev/null
+++ b/doc/props/P_DESCRIPTION
@@ -0,0 +1,8 @@
+NAME:
+    P_DESCRIPTION                 "description"                 
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Beschreibung des Spielers
diff --git a/doc/props/P_DESTROY_BAD b/doc/props/P_DESTROY_BAD
new file mode 100644
index 0000000..d563af9
--- /dev/null
+++ b/doc/props/P_DESTROY_BAD
@@ -0,0 +1,29 @@
+NAME:
+     P_DESTROY_BAD                 "std_food_destroy_bad"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Speichert den Wert fuer das Verhalten, wenn eine Speise verdirbt.
+     Dieses Verhalten wird in make_bad() umgesetzt.
+     
+     DESTROY_BAD   : Die Speise wird beim Verderben zerstoert
+                     bzw. der Behaelter wird geleert
+     DESTROY_NEVER : Die Speise wird beim Verderben nicht zerstoert
+     pos. Integer  : Anzahl der Sekunden, die zwischen Verderben
+                     und Zerstoeren der Speise liegen sollen
+     
+BEMERKUNGEN:
+     Ist ein positiver Integer-Wert in dieser Property gespeichert, wird nach
+     Ausfuehren der Methode make_bad() nach Ablauf der angegebenen Sekunden
+     ein weiterer Reset ausgeloest, der make_destroy() aufruft.
+     
+DEFAULT:
+     Initial ist diese Property auf DESTROY_BAD gesetzt.
+
+SIEHE AUCH:
+     /std/food.c, wiz/food
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_DESTRUCT_MSG b/doc/props/P_DESTRUCT_MSG
new file mode 100644
index 0000000..4b7e7e9
--- /dev/null
+++ b/doc/props/P_DESTRUCT_MSG
@@ -0,0 +1,8 @@
+NAME:
+    P_DESTRUCT_MSG                "destruct_msg"                
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Meldung, die beim Destructen Obj ausgegegen wird (nur Magier)
diff --git a/doc/props/P_DETAILS b/doc/props/P_DETAILS
new file mode 100644
index 0000000..e23513a
--- /dev/null
+++ b/doc/props/P_DETAILS
@@ -0,0 +1,25 @@
+NAME:
+    P_DETAILS            "details"
+
+DEFINIERT IN:
+    /sys/thing/description.h
+
+BESCHREIBUNG:
+    Diese Property enthaelt ein Mapping, in der Details im Objekt
+    definiert werden und Beschreibungen, die ausgegeben werden, wenn man
+    sich diese Details anschaut.
+
+BEMERKUNGEN:
+    Man kann diese Property nicht per SetProp() veraendern, sondern muss die
+    entsprechenden Funktionen nutzen.
+    AddSpecialDetail() und RemoveSpecialDetail() sollten nicht mehr
+    verwendet werden.
+
+SIEHE AUCH:
+    Setzen:    AddDetail()
+    Loeschen:  RemoveDetail()
+    Aehnlich:  P_SPECIAL_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail()
+    Sonstiges: GetDetail(), break_string()
+
+27. Jan 2013 Gloinson
diff --git a/doc/props/P_DIE_MSG b/doc/props/P_DIE_MSG
new file mode 100644
index 0000000..99f1d7c
--- /dev/null
+++ b/doc/props/P_DIE_MSG
@@ -0,0 +1,40 @@
+P_DIE_MSG
+
+NAME:
+     P_DIE_MSG			"die_msg"
+
+DEFINIERT IN:
+     /sys/properties.h
+
+BESCHREIBUNG:
+     In dieser Property uebergibt man einen String, der ausgegeben wird, wenn
+     das Lebewesen stirbt. Ist die Property nicht gesetzt, so wird als String
+     benutzt:
+	" faellt tot zu Boden.\n".
+
+     Der Name des Lebewesens wird dem String vor der Ausgabe vorangestellt.
+     Der Satzumbruch am Zeilenende und das Leerzeichen nach dem Namen des
+     Lebewesens muss man selbst angegeben. Es sollte allerdings beachtet
+     werden, dass ein Lebewesen, das durch Gift getoetet wird, eine spezielle
+     nicht zu beeinflussende Meldung erhaelt. Es wird dann als String
+     benutzt:
+	" wird von Gift hinweggerafft und kippt um.\n".
+
+BEISPIELE:
+     Bei einem mitkaempfenden Schatten waere es eher unlogisch, wenn nach
+     dessen 'Tod' eine Leiche zurueckbliebe. Eine logische Konsequenz waere
+     folgende Meldung:
+	SetProp(P_DIE_MSG," loest sich auf.\n");
+	SetProp(P_NOCORPSE,1);
+
+     Damit dann auch wirklich keine Leiche zurueckbleibt, wird zusaetzlich
+     die Property P_NOCORPSE gesetzt.
+
+SIEHE AUCH:
+     Tod:		die(L)
+     Todesmeldungen:	P_KILL_NAME, P_KILL_MSG, P_MURDER_MSG
+			P_ZAP_MSG, P_ENEMY_DEATH_SEQUENCE
+     Sonstiges:		P_CORPSE, P_NOCORPSE, /std/corpse.c
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_DISABLE_ATTACK b/doc/props/P_DISABLE_ATTACK
new file mode 100644
index 0000000..25e051d
--- /dev/null
+++ b/doc/props/P_DISABLE_ATTACK
@@ -0,0 +1,46 @@
+NAME:
+    P_DISABLE_ATTACK              "disable_attack"              
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Das Lebewesen kann nicht angreifen. Beim Setzen der Property ist es
+    wichtig, SetProp() zu benutzen und die Anzahl der Kampfrunden anzugeben,
+    die das Lebewesen paralysiert sein soll.
+
+    Ein negativer Wert ist nicht gueltig. Bei mehr als 30 Kampfrunden wird
+    die Paralyse auf 30 Kampfrunden gekappt.
+
+    Fuer jede Paralyse bekommt ein Living eine ebenso lange Schutzzeit nach
+    der Paralyse gewaehrt. Versucht man, vor Ablauf der Paralyse
+    P_DISABLE_ATTACK hoeher zu setzen (oder setzt man innerhalb der folgenden
+    Schutzzeit P_DISABLE_ATTACK auf > 0), wird DISABLE_TOO_EARLY von SetProp
+    zurueck gegeben.
+    Eine Reduktion von einem P_DISABLE_ATTACK ist moeglich, allerdings
+    reduziert dies _nicht_ eine einmal gesetzte Schutzzeit.
+
+    Einen Gegner,der nie paralysiert werden koennen soll, kann man einfach
+    per 
+
+    Set(P_DISABLE_ATTACK, function int () {return 0;}, F_SET_METHOD);
+
+    erstellen, da bei diesem der Wert von P_DISABLE_ATTACK nicht mehr mit
+    einem normalen SetProp()-Aufruf geaendert werden kann.
+
+BEISPIELE:
+    // Gegner 17 Runden lang paralysieren, ohne Ruecksicht auf fruehere P.
+    // (in diesem Moment ist das Living bis time() + 4 * 17 vor laengerer
+    //  oder erneuter Paralyse geschuetzt)
+    en->SetProp(P_DISABLE_ATTACK, 17);
+    // Der Gegner kann jetzt fuer 34 Kampfrunden nicht erneut paralysiert
+    // werden.
+    // Paralyse reduzieren auf 10 Kampfrunden
+    en->SetProp(P_DISABLE_ATTACK, 10);
+    // Die Schutzzeit ist immer noch bei 34 Kampfrunden, nicht bei 20.
+
+SIEHE AUCH:
+    P_NEXT_DISABLE_ATTACK
+
+19.08.2014, Zesstra
+
diff --git a/doc/props/P_DISABLE_COMMANDS b/doc/props/P_DISABLE_COMMANDS
new file mode 100644
index 0000000..f0318e0
--- /dev/null
+++ b/doc/props/P_DISABLE_COMMANDS
@@ -0,0 +1,74 @@
+P_DISABLE_COMMANDS
+
+NAME:
+     P_DISABLE_COMMANDS             "p_lib_disablecommands"
+
+DEFINIERT IN:
+     /sys/player/command.h
+
+BESCHREIBUNG:
+    Mit dieser Prop kann man verhindern, dass Kommandos eines Spielers
+    verarbeitet werden. Dies ist z.B. in Sequenzen nuetzlich, wo der Spieler
+    rein passiv konsumieren soll.
+    In diese Property muss ein Array mit 2 oder 3 Elementen geschrieben 
+    werden:
+       1) Endzeitpunkt in Sekunden seit 1.1.1970 (int)
+       2) Meldung (String oder Closure)
+       3) (optional) Array von trotzdem erlaubten Verben (string*)
+         (nur ausgewertet, wenn die Meldung ein String ist)
+    
+    Ist die Meldung ein String, wird dieser einfach bei jedem Kommando so wie
+    er ist an den Spieler ausgegeben und das Kommando abgebrochen.
+    Ist die Meldung eine Closure wird diese bei jedem Kommando aufgerufen und
+    das Kommando abgebrochen, wenn sie einen Rueckgabewert != 0 zurueckgibt.
+    In diesem Fall ist die gerufene Funktion dafuer verantwortlich, geeignete
+    Meldungen an den Spieler auszugeben!
+    Der Funktion wird der vom Spieler eingebene Befehl (string) als erstes
+    Argument uebergeben. Zu diesem Zeitpunkt wurde alle Aliase schon
+    ausgewertet. Die Funktion kann den Kommandogeber via this_player()
+    ermitteln.
+    Fuer weitere Informationen steht auch command_stack() zur Verfuegung,
+    allerdings ist dort die Alias-Ersetzung nicht beruecksichtigt.
+
+    Die Ausnahmeliste wird nur fuer simple Strings als Meldung ausgewertet,
+    wird eine Closure verwendet, kann diese besser selber die Ausnahmen
+    bestimmen.
+    
+    Fragt man diese Prop ab, erhaelt man ein Array mit 4 Elementen: setzendes
+    Objekt (object), Ablaufzeit (int), Meldung (String oder Closure) und
+    Liste von Ausnahmen (string*).
+
+BEMERKUNGEN:
+    1. Die Prop wird fuer Magier mit eingeschaltetem P_WANTS_TO_LEARN
+       ignoriert.
+    2. Wenn das Objekt zerstoert wird, was die Prop gesetzt hat, wird der
+       Eintrag unwirksam.
+    3. Wenn diese Prop gesetzt und nicht abgelaufen ist, kann nur das gleiche
+       Objekt sie mit neuen Daten ueberschreiben. Alle anderen Objekte koennen
+       die Prop nur loeschen. Dies soll verhindern, dass Magier unabsichtlich
+       einfach anderer Leute Eintraege ueberschreiben. Dementsprechend: Lasst
+       die Finger davon, wenn die schon gesetzt ist. ;-)
+    4. Diese Prop darf _ausschliesslich_ mit SetProp() und QueryProp() benutzt
+       werden, Set() und Query() funktionieren _nicht_.
+    5. Es gibt einige Verben, die NIE blockiert werden. Zur Zeit sind dies
+       "mrufe", "mschau", "bug", "idee", "typo" und "detail".
+    6. Bitte nicht missbrauchen, speziell nicht dazu, die Kommandos von
+       Spieler zu ueberwachen/mitzuschreiben. Das Setzen dieser Prop wird 
+       geloggt.
+
+BEISPIEL:
+   In einem Raum startet eine Sequenz, in der der Spieler nichts machen soll:
+
+   if (!pl->QueryProp(P_DISABLE_COMMANDS))
+      pl->SetProp(P_DISABLE_COMMANDS,
+            ({ time() + 120, "Du bist tief in Deinem Traum gefangen!\n" }) );
+   else // ... Fehlerbehandlung, falls Prop schon gesetzt ...
+   
+   Soll die Prop aus irgendeinem Grund (vorzeitig) geloescht werden:
+   pl->SetProp(P_DISABLE_COMMANDS, 0);
+
+SIEHE AUCH:
+     command(), query_command(), command_stack(), modify_command(), 
+     this_player()
+
+01.12.2012, Zesstra
diff --git a/doc/props/P_DISTRIBUTION b/doc/props/P_DISTRIBUTION
new file mode 100644
index 0000000..529c53e
--- /dev/null
+++ b/doc/props/P_DISTRIBUTION
@@ -0,0 +1,16 @@
+NAME:

+     P_DISTRIBUTION                "std_food_distribution"

+

+DEFINIERT IN:

+     /sys/food.h

+

+BESCHREIBUNG:

+     Verteilung der Heilung ueber mehrere Heartbeats.

+     Dieser Wert wird im entry_info als H_DISTRIBUTION dem consume() im

+     Living uebergeben.

+     

+SIEHE AUCH:

+     consume

+

+------------------------------------------------------------------------------

+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_DMSG b/doc/props/P_DMSG
new file mode 100644
index 0000000..f43fbb4
--- /dev/null
+++ b/doc/props/P_DMSG
@@ -0,0 +1,8 @@
+NAME:
+    P_DMSG                        "destmsg"                     
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     *** OBSOLET! *** Siehe P_DESTRUCT_MSG
diff --git a/doc/props/P_DOMAIN b/doc/props/P_DOMAIN
new file mode 100644
index 0000000..80fd1f7
--- /dev/null
+++ b/doc/props/P_DOMAIN
@@ -0,0 +1,31 @@
+NAME:
+    P_DOMAIN                                        "lib_p_domain"
+
+DEFINIERT IN:
+    /sys/room/description.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt die Region, in der ein Raum liegt, sofern er
+     in /d/ liegt.
+
+     Falls ein Raum nicht in /d/ liegt, aber es eigentlich ein Gebiet ist,
+     welches eindeutig in einer Region zugeordnet ist, kann der Magier
+     hier gezielt einen anderen Wert setzen.
+     
+     Bitte keine Regionen faelschen!
+
+BEISPIEL:
+     In /d/inseln/zesstra/vulkanweg/room/r1 liefert
+     QueryProp(P_DOMAIN)
+     ein "Inseln" zurueck.
+
+     In einem Raum der Chaosgilde:
+     SetProp(P_DOMAIN, "Polar");
+     damit der Raum als Raum der Region Polar angezeigt wird.
+
+SIEHE AUCH:
+     gmcp
+
+----------------------------------------------------------------------------
+23.02.2013, Zesstra
+
diff --git a/doc/props/P_DOOR_INFOS b/doc/props/P_DOOR_INFOS
new file mode 100644
index 0000000..8b5f71b
--- /dev/null
+++ b/doc/props/P_DOOR_INFOS
@@ -0,0 +1,44 @@
+NAME:
+    P_DOOR_INFOS                  "door_infos"
+
+DEFINIERT IN:
+    /sys/doorroom.h
+
+BESCHREIBUNG:
+    Mapping mit Informationen ueber eine im Raum per NewDoor() definierte
+    Tuer. Diese Infos werden ueber /std/room/doors.c an den /obj/doormaster.c
+    weitergegeben und dem Raum, der die Tuer besitzt, als Property gesetzt.
+    Werden mehrere Tueren in einem Raum eingebunden, enthaelt das Mapping
+    entsprechend viele Eintraege.
+
+    Dieses Mapping dient zur internen Verwaltung der Tueren im
+    /obj/doormaster.c und sollte nicht per Hand veraendert werden!
+
+    Die Eintraege im Mapping haben folgende keys (definiert in
+    /sys/doorroom.h):
+
+    D_DEST : Zielraum (string)
+    D_CMDS : Befehl(e), um durch die Tuer zu gehen (string oder *string)
+    D_IDS  : IDs der Tuer (string oder *string)
+    D_FLAGS : Besondere Eigenschaften der Tuer (Tuer braucht Schluessel etc.)
+    D_LONG  : Langbeschreibung (string)
+    D_SHORT : Kurzbeschreibung (string)
+    D_NAME  : Name (string oder *string)
+    D_GENDER  : Geschlecht
+    D_FUNC    : Funktion, die VOR dem Durchschreiten der Tuer aufgerufen wird
+    D_MSGS    : Bewegungsmeldungen
+    D_FUNC2   : Funktion, die NACH dem Durchschreiten der Tuer aufgerufen wird
+    D_TESTFUNC  : Funktion die im Sartraum testet, ob die Tuer durchschritten
+                  werden darf
+    D_RESET_MSG : Meldungen beim Tuer-Reset
+    D_OPEN_WITH_MOVE : Falls gesetzt, wird die Tuer auch mit dem
+                       Bewegungsbefehl geoeffnet und durchschritten, falls
+                       Oeffnen erfolgreich
+
+
+SIEHE AUCH:
+    NewDoor(), QueryDoorKey(), QueryDoorStatus(), SetDoorStatus(),
+    /std/room/doors.c, /obj/doormaster.c, GetPhiolenInfos(), QueryAllDoors()
+
+-----------------------------------------------------------------------------
+Letzte Aenderung: Don, 08.05.2014, Gabylon
diff --git a/doc/props/P_DO_DESTRUCT b/doc/props/P_DO_DESTRUCT
new file mode 100644
index 0000000..7e9a230
--- /dev/null
+++ b/doc/props/P_DO_DESTRUCT
@@ -0,0 +1,17 @@
+NAME:
+    P_DO_DESTRUCT                 "do_destruct"                 
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Flag, ob sich die Lichtquelle am Ende der Leuchtzeit selbst
+     zerstoert. 
+
+SIEHE AUCH:
+     Basisklassen: /std/lightsource.c
+     Properties: P_FUEL, P_LIGHTDESC, P_LIGHT
+     Methoden: AddFuel(L)
+
+LETZTE AENDERUNG:
+    22. Dez. 2015, Arathorn
diff --git a/doc/props/P_DRINK b/doc/props/P_DRINK
new file mode 100644
index 0000000..f2fe982
--- /dev/null
+++ b/doc/props/P_DRINK
@@ -0,0 +1,25 @@
+NAME:
+     P_DRINK                       "drink"
+
+DEFINIERT IN:
+     /sys/living/life.h
+
+BESCHREIBUNG:
+
+     - Lebewesen
+       Numerischer Wert fuer den Saettigungsgrad eines Lebewesens mit
+       Getraenken. Der maximale Wert steht in P_MAX_DRINK.
+
+     - Speisen/Kneipen
+       Numerischer Wert fuer den Saettigungsgrad einer Portion eines
+       Getraenkes. Ist diese Property nicht gesetzt oder zusaetzlich
+       P_FOOD gesetzt, kann man diese Speise nicht trinken. Eine
+       funktionierende Speise _muss_ entweder P_FOOD oder P_DRINK
+       groesser 0 gesetzt haben.
+     
+SIEHE AUCH:
+     P_MAX_DRINK, P_DRINK_DELAY, consume
+     P_FOOD, P_ALCOHOL, wiz/food
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_DRINK_DELAY b/doc/props/P_DRINK_DELAY
new file mode 100644
index 0000000..0118843
--- /dev/null
+++ b/doc/props/P_DRINK_DELAY
@@ -0,0 +1,10 @@
+NAME:
+    P_DRINK_DELAY                 "drink_delay"                     
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Anzahl der heart_beats, bis P_DRINK um einen Punkt sinkt.
+     Aenderungen dieser Property in Spielern beduerfen der 
+     Genehmigung des EM fuer Balance.
diff --git a/doc/props/P_DRINK_FULL_MSG b/doc/props/P_DRINK_FULL_MSG
new file mode 100644
index 0000000..2903af3
--- /dev/null
+++ b/doc/props/P_DRINK_FULL_MSG
@@ -0,0 +1,24 @@
+NAME:
+     P_DRINK_FULL_MSG              "std_food_drink_full_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung an den Konsumenten, wenn ein Getraenk getrunken werden soll,
+     obwohl dieser nichts mehr trinken kann.
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "So viel kannst Du im Moment nicht trinken."
+
+SIEHE AUCH:
+     P_DRINK, P_MAX_DRINK, wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_DROP_MSG b/doc/props/P_DROP_MSG
new file mode 100644
index 0000000..5a4c9d7
--- /dev/null
+++ b/doc/props/P_DROP_MSG
@@ -0,0 +1,62 @@
+P_DROP_MSG
+NAME:
+     P_DROP_MSG				"drop_message" 
+
+DEFINIERT IN:
+     /sys/living/put_and_get.h
+
+BESCHREIBUNG:
+     Mit P_DROP_MSG kann man die Meldung, die man beim Ablegen eines
+     Objektes bekommt, modifizieren.
+
+     Folgende Werte sind moeglich:
+	
+     o 0
+       Es wird eine Standardmeldung ausgegeben. Dies ist Voreinstellung. 
+	  
+     o NO_PNG_MSG       == -1
+       Es wird keinerlei Meldung ausgegeben
+	  
+     o Ein Array aus Strings 
+       Der erste String wird an den Spieler ausgegeben, der zweite 
+       (optionale) an den Raum. 
+	  
+       Die Strings werden durch die Funktion replace_personal() geparst.
+	Objekt1 - Spieler
+        Objekt2 - das Objekt, das fallengelassen wird
+	  
+       Wird der zweite String nicht angegeben, erfolgt keine Meldung an
+       den Raum.
+				
+BEISPIEL:
+     void create() {
+      ...
+      SetProp( P_SHORT, "Ugars Handaxt" );
+      SetProp( P_LONG, break_string(
+       "Dieses ist eine Kampfaxt, wie sie Orks normalerweise benutzen. "
+       "Da Du Zeit hast, sie Dir anzuschauen, ist der Besitzer wohl "
+       "bereits bei Lars.",78 ));
+	  
+      SetProp( P_NAME, "Axt" );
+      AddId( ({"handaxt","axt"}) );
+      SetProp( P_GENDER, FEMALE );
+	  
+      SetProp( P_DROP_MSG, ({
+       "Du schmeisst @WEN2 hin.",
+       "@WER1 schmeisst Dir @WENU2 vor die Fuesse.\n"}));
+      ...
+     }
+
+     Will Ugar seine Axt ablegen und gibt "lasse axt fallen" ein, werden 
+     folgende Meldungen ausgegeben:
+	
+     Ugar: "Du schmeisst die Axt hin."
+     Raum: "Ugar schmeisst Dir eine Axt vor die Fuesse."
+	
+SIEHE AUCH:
+     Aehnliches: P_PICK_MSG, P_PUT_MSG, P_GIVE_MSG, P_WEAR_MSG, P_WIELD_MSG
+     Fehler:     P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_TOO_MANY_MSG,
+                 P_NOINSERT_MSG, P_NOLEAVE_MSG, P_NODROP, P_NOGET 
+     Sonstiges:  replace_personal(E), drop_obj(L), /std/living/put_and_get.c
+
+14. Maerz 2004 Gloinson
diff --git a/doc/props/P_EARMUFFS b/doc/props/P_EARMUFFS
new file mode 100644
index 0000000..97e6b1c
--- /dev/null
+++ b/doc/props/P_EARMUFFS
@@ -0,0 +1,9 @@
+NAME:
+    P_EARMUFFS                    "earmuffs"                    
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Shouts von Spielern und Magiern mit Magierlevel < earmuffs werden
+     abgeblockt (Nur fuer Magier).
diff --git a/doc/props/P_EATER_MSG b/doc/props/P_EATER_MSG
new file mode 100644
index 0000000..99ae4d3
--- /dev/null
+++ b/doc/props/P_EATER_MSG
@@ -0,0 +1,23 @@
+NAME:
+     P_EATER_MSG                   "std_food_eater_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung an den Konsumenten, wenn eine Speise konsumiert wird.
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "Du konsumierst @WEN1."
+
+SIEHE AUCH:
+     /std/food.c, wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_EFFECTIVE_AC b/doc/props/P_EFFECTIVE_AC
new file mode 100644
index 0000000..ff7dd53
--- /dev/null
+++ b/doc/props/P_EFFECTIVE_AC
@@ -0,0 +1,87 @@
+P_EFFECTIVE_AC
+
+NAME:
+     P_EFFECTIVE_AC      "effective_ac"
+
+DEFINIERT IN:
+     <combat.h>
+
+BESCHREIBUNG:
+     Diese Property kommt sowohl in Ruestungen als auch in Waffen, die
+     schuetzen sollen, zum Einsatz.
+
+     In Ruestungen kann hier der Effektivwert der Ruestungsklasse vermerkt
+     werden, wenn diese noch durch eine DefendFunc() modifiziert wird
+     (soweit sich ein solcher Effektivwert angeben laesst).
+
+     In einigen Gilden koennen Waffen auch als Ruestung eingesetzt werden
+     (z.B. beim Parieren eines Angriffs). In dieser Property kann man die
+     Ruestungsklasse eintragen, die die Waffe bei solchen Aktionen aufweisen
+     soll. Dabei ist man an die ueblichen Beschraenkungen der
+     Ruestungsklasse gebunden! (s. /sys/combat.h)
+
+BERMERKUNGEN:
+     Das Kaempferspellbook verwendet fuer Paraden etc. P_EFFECTIVE_AC anstatt
+     P_AC, wenn verfuegbar.
+
+BEISPIELE:
+     * DefendFuncs: 
+       Der doppelte Mittelwert der DefendFunc wird zur Basis-AC dazuaddiert,
+       da sich der 'Schutzwert = random(Basis-AC) + absolut(DefendFunc-Wert)'
+       berechnet.
+
+     // #1 Eine Ruestung mit P_AC von 35 und randomisierter DefendFunc
+        SetProp(P_AC, 35);
+        SetProp(P_DEFEND_FUNC, this_object());
+
+        int DefendFunc(...) {
+          return random(20);                       // Mittelwert: 10
+        }
+        -> SetProp(P_EFFECTIVE_AC, 55);            // 35 + 2*10 = 55
+
+     // #2 Eine Ruestung mit P_AC von 35 und teilrandomisierter DefendFunc
+        SetProp(P_AC, 35);
+        SetProp(P_DEFEND_FUNC, this_object());
+
+        int DefendFunc(...) {
+          return 20 + random(10);                  // Mittelwert: 20 + 5
+        }
+        -> SetProp(P_EFFECTIVE_AC, 85);            // 35 + 2*(20+5) = 85
+
+     * Sonderfunktion im Kontext der Kaempfergilde:
+       Auch wenn der eigentliche AC-Wert durch keine DefendFunc oAe
+       modifiziert wird, sind abweichende Werte in P_EFFECTIVE_AC zB in der
+       Kaempfergilde fuer Paraden oder aehnliches sinnvoll. Maximalwert ist
+       dafuer der doppelte Wert des Basis-AC-Wertes.
+
+     // #3 Ein schon sehr gutes Schild, welches bei der Schildparade aber
+     //    noch besser schuetzen soll.
+        SetProp(P_ARMOUR_TYPE, AT_SHIELD);
+        SetProp(P_AC, 38);
+        SetProp(P_EFFECTIVE_AC, 55);
+
+     // #4 Ein sehr labbriges Schild schuetzt zwar gegen normale Schlaege,
+     //    ist zum Parieren aber irgendwie ungeeignet weil unkontrollierbar.
+        SetProp(P_ARMOUR_TYPE, AT_SHIELD);
+        SetProp(P_AC, 38);
+        SetProp(P_EFFECTIVE_AC, 20);
+
+     * Waffen:
+       P_EFFECTIVE_AC wird im Kaempferspellbook als Bonus dazugezaehlt! Daher
+       sollten gute Parierwaffen auch einen niedrigeren P_WC-Wert haben.
+       Reine Parierwaffen duerfen den maximalen AC-Wert von Schilden als
+       Maximum gesetzt haben - die Balance klaert ggf, ob das auch auf den
+       Gildenparierwert anwendbar ist.
+
+     // #5 Eine maessige Hellebarde/Axtwaffe mit Parierhaken.
+        SetProp(P_WEAPON_TYPE, WT_AXE);
+        SetProp(P_WC, 100);
+        SetProp(P_EFFECTIVE_AC, 25);
+
+SIEHE AUCH:
+     Waffen:     P_WC, P_TOTAL_WC, P_EFFECTIVE_WC, HitFunc()
+     Ruestungen: P_AC, P_TOTAL_AC, DefendFunc()
+     Files:      /std/weapon.c, /std/weapon/combat.c
+     Balance:    waffen, ruestungen, properties, kaempferboni
+
+6. Nov 2011 Gloinson
diff --git a/doc/props/P_EFFECTIVE_WC b/doc/props/P_EFFECTIVE_WC
new file mode 100644
index 0000000..0328629
--- /dev/null
+++ b/doc/props/P_EFFECTIVE_WC
@@ -0,0 +1,86 @@
+P_EFFECTIVE_WC
+
+NAME:
+     P_EFFECTIVE_WC     "effective_wc"
+
+DEFINIERT IN:
+     <combat.h>
+
+BESCHREIBUNG:
+     Diese Property kommt sowohl in Waffen als auch Ruestungen, die Schaden
+     machen sollen, zum Einsatz.
+
+     Falls die Staerke einer Waffe noch durch eine HitFunc() modifiziert
+     wird, sollte hier der Effektivwert der Waffenklasse vermerkt werden,
+     soweit er sich angeben laesst.
+     Diese Property dient vor allem dazu, eine Waffe mit HitFunc() korrekt
+     einzuschaetzen.
+
+     In einigen Gilden koennen Ruestungen auch als Waffen eingesetzt werden
+     (z.B. ein Paar Schuhe zum Treten). In dieser Property kann man die
+     Waffenklasse eintragen, die die Ruestung bei solchen Aktionen aufweisen
+     soll. Dabei ist man an die ueblichen Beschraenkungen der Waffenklasse
+     gebunden! (s. /sys/combat.h)
+     Der Ruestung kann man dann auch einen Schadenstyp mit auf den Weg
+     geben.
+
+BEMERKUNGEN:
+     Das Kaempferspellbook verwendet bei Ruestungen P_AC, wenn
+     P_EFFECTIVE_WC nicht gesetzt ist.
+
+BEISPIELE:
+     * HitFuncs:
+       Der doppelte Mittelwert der HitFunc wird zur Basis-WC dazuaddiert,
+       da sich der 'Angriffswert = random(Basis-WC) + absolut(HitFunc-Wert)'
+       berechnet.
+
+     // #1 Waffe mit Basis-WC 120 und randomisierter HitFunc
+        SetProp(P_WC, 120);
+        SetProp(P_HIT_FUNC, this_object());
+       
+        int HitFunc(...) {
+          return random(30);                       // Mittelwert: 15
+        }
+        -> SetProp(P_EFFECTIVE_WC, 150);           // 120 + 2*15 = 150
+     
+     // #2 Waffe mit Basis-WC 120 und teilweise absoluter HitFunc
+        SetProp(P_WC, 120);
+        SetProp(P_HIT_FUNC, this_object());
+       
+        int HitFunc(...) {
+          return 30 + random(10);                  // Mittelwert: 30 + 5
+        }
+        -> SetProp(P_EFFECTIVE_WC, 190);           // 120 + 2*(30+5) = 190
+
+     * Ruestungen (zB Gildennutzung):
+       Der Maximalwert fuer die P_EFFECTIVE_WC bei Kaempfern ist der jeweils
+       doppelte maximale P_AC-Wert. Angabe eines Schadenstyps ist sinnvoll.
+
+     // #3 Ein paar Schuhe, mit maximalem Schlag-/Saeureschaden.
+        SetProp(P_ARMOUR_TYPE, AT_BOOT);
+        SetProp(P_AC, 2);
+        SetProp(P_DAM_TYPE, ({DT_BLUDGEON,DT_ACID}));
+        -> SetProp(P_EFFECTIVE_WC, 12);  // hoechstmoeglicher Wert bei
+                                         // Schuhen, da deren max. P_AC = 6
+     // aequivalent und zukunftssicher:
+        -> SetProp(P_EFFECTIVE_WC, 2 * VALID_ARMOUR_CLASS[AT_BOOT]);
+
+     // #4 Ein Schild mit spitzem Dorn. (Stichschaden beim Schildstoss.)
+        SetProp(P_ARMOUR_TYPE, AT_SHIELD);
+        SetProp(P_AC, 5);
+        SetProp(P_DAM_TYPE, ({DT_PIERCE}));
+        SetProp(P_EFFECTIVE_WC, 55);
+
+     // #5 Ein Gummischild ist schlecht fuer Angriffe. BOING!
+        SetProp(P_ARMOUR_TYPE, AT_SHIELD);
+        SetProp(P_AC, 30);
+        SetProp(P_DAM_TYPE, ({DT_BLUDGEON}));
+        SetProp(P_EFFECTIVE_WC, 10);
+
+SIEHE AUCH:
+     Waffen:     P_WC, P_TOTAL_WC, HitFunc()
+     Ruestungen: P_AC, P_TOTAL_AC, P_EFFECTIVE_AC, DefendFunc()
+     Files:      /std/weapon.c, /std/weapon/combat.c
+     Balance:    waffen, ruestungen, properties, kaempferboni
+
+6. Nov 2011 Gloinson
diff --git a/doc/props/P_EMPTY_MSG b/doc/props/P_EMPTY_MSG
new file mode 100644
index 0000000..7248d8e
--- /dev/null
+++ b/doc/props/P_EMPTY_MSG
@@ -0,0 +1,24 @@
+NAME:
+     P_EMPTY_MSG                   "std_food_eater_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung an den Konsumenten, wenn versucht wird, eine aufgebrauchte Speise
+     (also einen leeren Behaelter) zu konsumieren.
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise (also den leeren Behaelter)
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "@WER1 ist bereits leer."
+
+SIEHE AUCH:
+     /std/food.c, wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_EMPTY_PROPS b/doc/props/P_EMPTY_PROPS
new file mode 100644
index 0000000..a8156d7
--- /dev/null
+++ b/doc/props/P_EMPTY_PROPS
@@ -0,0 +1,33 @@
+NAME:
+     P_EMPTY_PROPS                 "std_food_empty_props"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Mapping mit Properties fuer den leeren Behaelter, der uebrig bleibt,wenn
+     eine Speise aufgebraucht ist. Alle enthaltenen Properties werden gesetzt,
+     wenn keine Portionen mehr vorhanden sind.
+     Bereits in diesen Properties eingetragene Werte werden ueberschrieben!
+     Wenn diese Property nicht gesetzt ist, wird die Speise zerstoert, wenn
+     alle Portionen aufgebraucht ist - es bleibt also kein Behaelter zurueck.
+     Achtung: Es werden keine closures in diesem Mapping unterstuetzt!
+     
+BEMERKUNGEN:
+     Bei der Abfrage von P_VALUE und P_WEIGHT in der Speise, werden die dazu
+     in P_EMPTY_PROPS gespeicherten Werte verwendet, um das Gewicht/Wert des
+     leeren Behaelters zum Gesamtwert der Speise zu addieren.
+     Man kann alle Properties in dieses Mapping eintragen, die sich in der
+     Speise per SetProp setzen lassen. Zusaetzlich kann man Arrays in P_IDS
+     und P_ADJECTIVES speichern, die per Add-Methode in der Speise
+     hinzugefuegt werden, nachdem die im create() der Speise hinzugefuegten
+     Ids/Adjectives dort entfernt wurden.
+     
+BEISPIELE:
+     Beispiele zur Verwendung findet man unter /doc/beispiele/food
+
+SIEHE AUCH:
+     /std/food.c, wiz/food
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_ENABLE_IN_ATTACK_OUT b/doc/props/P_ENABLE_IN_ATTACK_OUT
new file mode 100644
index 0000000..d60a807
--- /dev/null
+++ b/doc/props/P_ENABLE_IN_ATTACK_OUT
@@ -0,0 +1,33 @@
+NAME:
+	P_ENABLE_IN_ATTACK_OUT		"enable_in_attack_out"
+
+DEFINIERT IN:
+	/sys/combat.h
+
+BESCHREIBUNG:
+	Normalerweise wird die bekannte Taktik Rein-Angriff-Raus
+	standardmaessig unterbunden, damit NPCs auch eine Chance haben, sich
+	zu verteidigen. Hierzu wird der Schaden innerhalb do_damage()
+	durch einen Wert geteilt, der sich aus der Verweildauer des
+	Angreifers im Raum ergibt (bis zu 3 Sekunden).
+	Da manche NPCs so konzeptioniert wurden, dass man sie nur mit der
+	erwaehnten Taktik toeten kann, kann man sie auch explizit erlauben
+	(manche NPCs verwenden auch eigene Methoden, diese Taktik zu
+	 verbieten, und sie waere dann doppelt abgefangen).
+	Hierzu setzt man einfach die Property P_ENABLE_IN_ATTACK_OUT im NPC.
+
+BEISPIEL:
+	Das folgende Beispiel erlaubt einfach mal die angesprochene Taktik:
+	  void create()
+	  { ...
+	    SetProp(P_ENABLE_IN_ATTACK_OUT,1);
+	    ...
+	  }
+	Jetzt kann man den NPC mit Rein-Angriff-Raus auch wirkungsvoll
+	bekaempfen.
+
+SIEHE AUCH:
+	do_damage(), P_LAST_MOVE, /std/living/life.c
+
+-----------------------------------------------------------------------------
+Last modified: Sat Jan 30 12:53:00 1999 by Patryn
diff --git a/doc/props/P_ENEMY_DAMAGE b/doc/props/P_ENEMY_DAMAGE
new file mode 100644
index 0000000..216339d
--- /dev/null
+++ b/doc/props/P_ENEMY_DAMAGE
@@ -0,0 +1,31 @@
+P_ENEMY_DAMAGE
+
+NAME:
+     P_ENEMY_DAMAGE "enemy_damage"
+
+DEFINIERT IN:
+     <living/life.h>
+
+BESCHREIBUNG:
+     In dieser Property ist vermerkt, welches Wesen diesem Wesen wieviel
+     Schaden zugefuegt hat.
+
+     Die Property enthaelt ein Mapping, das den Angreifern den
+     errechten Schaden zuordnet:
+     ([ <obname1>: <damage>; <owner>, ... ])
+
+       <obname1>: Name des Objekts, das den Schaden verursacht hat (string),
+                  z.B. "/magier:zesstra"
+       <damage> : Schadensmenge (int)
+       <owner>  : wenn das schadensverursachende Wesen ein NPC war/ist,
+                  welches P_HELPER_NPC gesetzt hatte und somit einem Spieler
+                  hilft, steht hier das Objekt des jeweiligen Spielers
+                  (object)
+
+BEMERKUNGEN:
+     Diese Property laesst sich nur abfragen!
+
+SIEHE AUCH:
+     P_HELPER_NPC
+----------------------------------------------------------------------------
+26.10.2009, Zesstra
diff --git a/doc/props/P_ENEMY_DEATH_SEQUENCE b/doc/props/P_ENEMY_DEATH_SEQUENCE
new file mode 100644
index 0000000..260d325
--- /dev/null
+++ b/doc/props/P_ENEMY_DEATH_SEQUENCE
@@ -0,0 +1,44 @@
+P_ENEMY_DEATH_SEQUENCE
+
+NAME:
+     P_ENEMY_DEATH_SEQUENCE        "enemy_death_sequence"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+     Ueber diese Property kann Einfluss auf die Todessequenz eines getoeten
+     Spielers genommen werden. Sie muss im toetenden Objekt, d.h. dem
+     Objekt welches die()/do_damage()/Defend() im Spieler gerufen hat,
+     gesetzt sein.
+
+     Es gibt folgende gueltige Werte:
+     - string: Pfad zu einer eigenen Todessequenz im gueltigen Format
+     - mixed*  Eine Todessequenz im Format des Todesraumes:
+               ({<int gesamtlaenge>,
+                 ([<int index1>: <string umgebrochene Meldung1>,
+                   <int index2>: <string umgebrochene Meldung2>,
+                   ...])
+               })
+     - mapping In die Standard-Lars-Todessequenz einzufuegende Zeilen:
+               ([<int zeitindex>: <string umgebrochener Text>])
+
+BEISPIELE:
+     // Pfad zu einer eigenen DSQ
+     SetProp(P_ENEMY_DEATH_SEQUENCE,  ".../passende_dsq.txt");
+
+     // eigene DSQ im Todesraumformat:
+     SetProp(P_ENEMY_DEATH_SEQUENCE,
+             ({ 2, ([1: "DU LERNST AUS DEINEM FEHLER.\n"])}));
+
+     // Einfuegen einer Meldung (des NPCs) in die Standard-Todessequenz
+     SetProp(P_ENEMY_DEATH_SEQUENCE,
+             ([5: "Du hoerst "+name(WEN)+" hoehnisch kichern.\n"]));
+
+SIEHE AUCH:
+     Tod:            die(L)
+     Todesmeldungen: P_KILL_NAME, P_KILL_MSG, P_DIE_MSG, P_MURDER_MSG
+                     P_ZAP_MSG, P_NEXT_DEATH_SEQUENCE
+     Sonstiges:      P_CORPSE, P_NOCORPSE, /room/death/death_room.c
+
+10. Nov 2011 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_ENTERCMDS b/doc/props/P_ENTERCMDS
new file mode 100644
index 0000000..0f7e3d9
--- /dev/null
+++ b/doc/props/P_ENTERCMDS
@@ -0,0 +1,33 @@
+NAME:
+    P_ENTERCMDS                   "leavecmds"                   
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+    Ein Array mit Befehlen, die zum Betreten des Transporters fuehren. 
+
+BEISPIEL:
+    SetProp(P_ENTERCMDS,({ "betrete","betritt" }) );
+
+    Gibt der Spieler nun 'betrete xxx' ein, so sorgt /std/transport.c 
+    dafuer, das der Spieler auch auf oder in den Transporter bewegt 
+    wird. Vorausgesetzt natuerlich, er ist an einem Haltepunkt ange-
+    langt und es ist genuegend Platz dort.
+
+BEMERKUNGEN:
+    Um /std/transport.c nicht aufzublaehen, werden weitere Argumente wie
+    etwa 'betrete das|die|den xxx' _nicht_ unterstuetzt!
+
+    Hier muss der verantwortliche Magier schon eine eigene Loesung finden
+    und in seinen Transporter schreiben.
+
+    Sollen Kommandos zum Betreten UND Verlassen eines Transporters ver-
+    wendet werden koennen, muessen diese in P_TRAVEL_CMDS gesetzt werden.
+
+SIEHE AUCH:
+    P_LEAVEFAIL, P_ENTERFAIL, P_LEAVECMDS, P_TRAVEL_CMDS, transporter,
+
+LETZTE AENDERUNG:
+    Don, 24.01.2002, 10:15:07h von Tilly
+    
\ No newline at end of file
diff --git a/doc/props/P_ENTERFAIL b/doc/props/P_ENTERFAIL
new file mode 100644
index 0000000..2ecd1e6
--- /dev/null
+++ b/doc/props/P_ENTERFAIL
@@ -0,0 +1,24 @@
+NAME:
+    P_ENTERFAIL                   "enterfail"                   
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+     Meldung an den Spieler, wenn er einen vollen Transporter betreten 
+     will. Ist die Propertie ein Array, so wird das erste Element als
+     Meldung an den Spieler, das zweite als Meldung an die Mitspieler 
+     im Raum geschickt.
+
+BEISPIEL:
+     SetProp(P_ENTERFAIL,"Dort ist wirklich kein Platz mehr fuer Dich");
+     
+     SetProp(P_ENTERFAIL, ({ "Dort ist wirklich kein Platz mehr fuer Dich",
+                             "versucht, auf die Kutsche zu klettern, wird "
+                            +"aber wieder heruntergeschickt" }) );
+
+SIEHE AUCH:
+     P_ENTERMSG, P_LEAVEFAIL, P_LEAVEMSG, transporter
+
+LETZTE AENDERUNG:
+    Don, 24.01.2002, 10:15:07h von Tilly
diff --git a/doc/props/P_ENTERMSG b/doc/props/P_ENTERMSG
new file mode 100644
index 0000000..2a2128e
--- /dev/null
+++ b/doc/props/P_ENTERMSG
@@ -0,0 +1,18 @@
+NAME:
+    P_ENTERMSG                    "entermsg"                    
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+     Array mit zwei Meldungen, eine fuer den Raum, den der Spieler
+     verlaesst, und eine fuer den Transporter, in den er geht.
+
+BEISPIEL:
+     SetProp(P_ENTERMSG, ({ "klettert in die Kutsche","klettert herein" }) );
+
+SIEHE AUCH:
+     P_ENTERFAIL, P_LEAVEFAIL, P_LEAVEMSG, transporter
+
+LETZTER AENDERUNG:
+    Don, 24.01.2002, 10:15:07h von Tilly
diff --git a/doc/props/P_ENV_MSG b/doc/props/P_ENV_MSG
new file mode 100644
index 0000000..b39e2c1
--- /dev/null
+++ b/doc/props/P_ENV_MSG
@@ -0,0 +1,26 @@
+NAME:
+     P_ENV_MSG                     "std_food_env_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung an den Konsumenten, wenn eine Speise konsumiert werden soll,
+     die nicht im eigenen Inventory liegt.
+     Wenn diese Property leer ist, kann man die Speise dann sogar
+     konsumieren!
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "Vielleicht solltest Du @WEN1 vorher nehmen."
+
+SIEHE AUCH:
+     wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_ENV_TOO_HEAVY_MSG b/doc/props/P_ENV_TOO_HEAVY_MSG
new file mode 100644
index 0000000..8c73ac1
--- /dev/null
+++ b/doc/props/P_ENV_TOO_HEAVY_MSG
@@ -0,0 +1,31 @@
+NAME:
+    P_ENV_TOO_HEAVY_MSG                      "env_too_heavy_msg"                      
+
+DEFINIERT IN:
+    /sys/thing/moving.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt eine Meldung, die ausgegeben wird, wenn jemand
+     versucht, ein Objekt in einen Behaelter zu legen, und dieser dann fuer
+     sein Environment zu schwer wird.
+     Die Property ist im Behaelter zu setzen.
+     Ist diese Property nicht oder auf einen nicht-String-Wert gesetzt,
+     so wird die Standardmeldung ausgegeben.
+     ("<Behaelter> wuerde dir dann zu schwer werden.")
+     Der String in der Property wird noch durch replace_personal()
+     verarbeitet, das zu bewegende Objekt wird als erstes, der Behaelter als
+     zweites Objekt angegeben. Danach wird der String auf 78 Zeichen
+     umgebrochen.
+     Das Setzen eines leeren Strings unterdrueckt die Ausgabe einer Meldung
+     ganz.
+
+BEISPIELE:
+     SetProp(P_ENV_TOO_HEAVY_MSG, "Mit @WEM1 koenntest du den Rucksack nicht"
+                                  " mehr tragen.");
+
+SIEHE AUCH:
+     Aehnliches: P_TOO_HEAVY_MSG, P_TOO_MANY_MSG, P_NOINSERT_MSG,
+                 P_NOLEAVE_MSG, P_NODROP, P_NOGET 
+     Erfolg:     P_PICK_MSG, P_DROP_MSG, P_GIVE_MSG, P_PUT_MSG,
+                 P_WEAR_MSG, P_WIELD_MSG
+     Sonstiges:  replace_personal(E), /std/living/put_and_get.c
diff --git a/doc/props/P_EQUIP_TIME b/doc/props/P_EQUIP_TIME
new file mode 100644
index 0000000..71b2f35
--- /dev/null
+++ b/doc/props/P_EQUIP_TIME
@@ -0,0 +1,26 @@
+P_EQUIP_TIME
+
+NAME:
+      P_EQUIP_TIME			"equip_time"
+
+DEFINIERT IN:
+      /sys/combat.h
+
+BESCHREIBUNG:
+      Innerhalb von Waffen und Ruestungen wird in dieser Property

+      registriert, wann diese zuletzt gezueckt bzw. angezogen wurden.
+      Innerhalb der Funktionen wield_me() in '/std/weapon/combat' bzw.

+      DoWear() in '/std/armour/combat' wird hierbei jeweils folgende Aktion

+      ausgefuehrt:
+	SetProp(P_EQUIP_TIME,time());
+
+SIEHE AUCH:
+      Verwandt:		P_LAST_USE
+      Waffen:		P_UNWIELD_TIME
+			DoWield()
+      Ruestungen:	DoWear()
+      Sonstiges:	time()
+			/std/weapon/combat.c, /std/armour/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Sun Jul 26 23:59:12 1998 by Patryn
diff --git a/doc/props/P_EVAL_FACTORS b/doc/props/P_EVAL_FACTORS
new file mode 100644
index 0000000..04e881e
--- /dev/null
+++ b/doc/props/P_EVAL_FACTORS
@@ -0,0 +1,8 @@
+NAME:
+    P_EVAL_FACTORS                "inpc_eval_factors"           
+
+DEFINIERT IN:
+    /sys/inpc/eval.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_EVAL_OFFSETS b/doc/props/P_EVAL_OFFSETS
new file mode 100644
index 0000000..d82ef39
--- /dev/null
+++ b/doc/props/P_EVAL_OFFSETS
@@ -0,0 +1,8 @@
+NAME:
+    P_EVAL_OFFSETS                "inpc_eval_offsets"           
+
+DEFINIERT IN:
+    /sys/inpc/eval.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_EXITS b/doc/props/P_EXITS
new file mode 100644
index 0000000..3b3856f
--- /dev/null
+++ b/doc/props/P_EXITS
@@ -0,0 +1,9 @@
+NAME:
+    P_EXITS                       "exits"                       
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Mapping aller unmittelbar sichtbaren Ausgaenge mit zugehoerigen
+     Nachbarraeumen. Sollte nur mittels AddExit() benutzt werden.
diff --git a/doc/props/P_FAO b/doc/props/P_FAO
new file mode 100644
index 0000000..62f33e1
--- /dev/null
+++ b/doc/props/P_FAO
@@ -0,0 +1,19 @@
+P_FAO
+
+NAME:
+     P_FAO      "fraternitasdonoarchmagorum"
+
+DEFINIERT IN:
+     <player/fao.h>
+
+BESCHREIBUNG:
+     Fraternitas-relevante Property.
+
+     Genaue Informationen unbekannt.
+     Schreibender Zugriff ist nur EMs oder dem FAOMASTER (s. Headerfile)
+     moeglich.
+
+SIEHE AUCH:
+     P_FAO_PORTALS
+
+1.September 2008 Gloinson
diff --git a/doc/props/P_FAO_PORTALS b/doc/props/P_FAO_PORTALS
new file mode 100644
index 0000000..465750a
--- /dev/null
+++ b/doc/props/P_FAO_PORTALS
@@ -0,0 +1,22 @@
+P_FAO_PORTALS
+
+NAME:
+     P_FAO_PORTALS      "fraternitasdonoarchmagorumPORTALS"
+
+DEFINIERT IN:
+     <player/fao.h>
+
+BESCHREIBUNG:
+     Fraternitas-relevante Property.
+
+     Enthaelt eine Array mit einer Liste der ueber die Fraternitas
+     gefundenen und benutzbaren Seher-Portale.
+
+     Genaue Informationen unbekannt.
+     Schreibender Zugriff ist nur EMs oder dem FAOMASTER (s. Headerfile) 
+     moeglich.
+
+SIEHE AUCH:
+     P_FAO_PORTALS, P_SEERDOORS
+     
+1.September 2008 Gloinson
diff --git a/doc/props/P_FISH b/doc/props/P_FISH
new file mode 100644
index 0000000..c694c09
--- /dev/null
+++ b/doc/props/P_FISH
@@ -0,0 +1,75 @@
+NAME:
+    P_FISH                        "fish"
+
+DEFINIERT IN:
+    /sys/fishing.h
+
+BESCHREIBUNG:
+    Enthaelt Einstellungen zu allem, was mit Fischen zu tun hat. 
+    Kann in Fischen, Raeumen und Koedern gesetzt werden. Die verfuegbaren 
+    Optionen und Funktionsweisen sind in den nachfolgenden Abschnitten 
+    aufgefuehrt.
+
+    Fische:
+    *******
+    Die Property legt zusaetzliche Eigenschaften fest:
+
+      F_NOROTTEN
+        Fisch fault nicht; ggf. sollte hier auch gleich F_NOHEAL gesetzt 
+        werden, weil sonst eine unverderbliche tragbare Tanke erzeugt wuerde.
+      F_NOTHUNGRY
+        Fisch frisst Koeder nur, wenn er auch wirklich nachher an der Angel
+        haengt. Ist er zu schwer fuer die Angel und reisst ab, bleibt der
+        Koeder erhalten.
+      F_REPLACE
+        Fisch soll sich beim Entfernen von der Angel verwandeln. Hierzu ist
+        die Funktion ReplaceFish() im Fisch zu definieren, die sich um die
+        Verwandlung kuemmert (z.B. Monster clonen und Fisch zerstoeren; ein
+        Beispiel findet sich in /d/ebene/fraggle/angel2/obj/saegefisch.c).
+      F_NOHEAL
+        Fisch heilt nicht bei Verzehr
+
+    Raum (OPTIONAL):
+    ****************
+    Legt die Fischdichte des Gewaessers fest. Der eingestellte Wert wirkt 
+    sich auf die Wartezeit aus, die der Angler verbringen muss, bis ein 
+    Fisch anbeisst. Berechnung im Detail (alle Zahlenwerte in Sekunden):
+    - Basis-Wartezeit: delay = 80
+    - Die Werte von P_FISH von Raum und Koeder werden addiert:
+      summe = raum->QueryProp(P_FISH) + koeder->QueryProp(P_FISH)
+      -> positive Werte (Bonus) werden auf 60 begrenzt und mit Zufalls-
+         komponente von <delay> abgezogen:
+         delay -= random(summe)
+      -> negative Werte (Malus) werden auf -300 begrenzt und mit Zufalls-
+         komponente auf <delay> aufgeschlagen:
+         delay += random(-summe)
+
+    Zusaetzlich wird ein weiterer Malus auf die Wartezeit faellig, falls 
+    P_WATER in der Angel und/oder P_WATER im Koeder nicht zum aktuellen 
+    Gewaesser passen. Der Malus betraegt jeweils 60+random(60) Sekunden.
+    
+    Der Standardwert fuer P_FISH im Raum ist 0 und bedeutet 100 % Bestand.
+    Positive Werte erhoehen die Dichte, negative senken sie. Positive Werte 
+    sollten nicht >100 sein.
+
+    Sofern sich die Werte fuer P_FISH in den empfohlenen Grenzen bewegen,
+    koennen Abzuege fuer falsches Gewaesser ueblicherweise kaum durch
+    Boni auf Angel oder Koeder kompensiert werden. Ausserdem ist zu
+    bedenken, dass es keine Boni fuer richtige Gewaesserwahl gibt.
+ 
+
+    Koeder (OPTIONAL):
+    ******************
+    Ein Koeder kann mittels P_FISH die Fischdichte modifizieren, um hierueber
+    ein besseres Beissen der Fische zu simulieren (reduziert die Wartezeit
+    beim Angeln, siehe oben unter "Raum"). Wenn also der Raum einen Wert
+    von -100 gesetzt hat und der Koeder +100, entspricht die Fischdichte im 
+    Gewaesser wieder dem Normalwert.
+
+SIEHE AUCH:
+
+    Properties: P_WATER
+    Methoden:   GetAquarium(L)
+
+------------------------------------------------------------------------------
+Zuletzt geaendert: 2014-Aug-21, Arathorn
diff --git a/doc/props/P_FLAGS b/doc/props/P_FLAGS
new file mode 100644
index 0000000..3c54aea
--- /dev/null
+++ b/doc/props/P_FLAGS
@@ -0,0 +1,8 @@
+NAME:
+    P_FLAGS                       "can_flags"                   
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Flags des Spielers
diff --git a/doc/props/P_FOLLOW_SILENT b/doc/props/P_FOLLOW_SILENT
new file mode 100644
index 0000000..7662ad1
--- /dev/null
+++ b/doc/props/P_FOLLOW_SILENT
@@ -0,0 +1,9 @@
+NAME:
+    P_FOLLOW_SILENT               "follow_silent"               
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Wenn diese Property 1 ist, wird der MOVE vom verfolge Silent ausge-
+     fuehrt.
diff --git a/doc/props/P_FOOD b/doc/props/P_FOOD
new file mode 100644
index 0000000..769694f
--- /dev/null
+++ b/doc/props/P_FOOD
@@ -0,0 +1,24 @@
+NAME:
+     P_FOOD                        "food"
+
+DEFINIERT IN:
+     /sys/living/life.h
+
+BESCHREIBUNG:
+
+     - Lebewesen
+       Numerischer Wert fuer den Saettigungsgrad eines Lebewesens.
+       Der maximale Wert steht in P_MAX_FOOD.
+
+     - Speisen/Kneipen
+       Numerischer Wert fuer den Saettigungsgrad einer Portion einer
+       Speise. Ist diese Property nicht gesetzt, kann man diese
+       Speise nicht essen. Eine funktionierende Speise _muss_ entweder
+       P_FOOD oder P_DRINK groesser 0 gesetzt haben.
+
+SIEHE AUCH:
+     P_MAX_FOOD, P_FOOD_DELAY, consume,
+     P_DRINK, P_ALCOHOL, wiz/food
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_FOOD_DELAY b/doc/props/P_FOOD_DELAY
new file mode 100644
index 0000000..11ccf6e
--- /dev/null
+++ b/doc/props/P_FOOD_DELAY
@@ -0,0 +1,10 @@
+NAME:
+    P_FOOD_DELAY                 "food_delay"                     
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Anzahl der heart_beats, bis P_FOOD um einen Punkt sinkt.
+     Aenderungen dieser Property in Spielern beduerfen der 
+     Genehmigung des EM fuer Balance.
diff --git a/doc/props/P_FOOD_FULL_MSG b/doc/props/P_FOOD_FULL_MSG
new file mode 100644
index 0000000..a331ec5
--- /dev/null
+++ b/doc/props/P_FOOD_FULL_MSG
@@ -0,0 +1,24 @@
+NAME:
+     P_FOOD_FULL_MSG               "std_food_full_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung an den Konsumenten, wenn eine Speise gegessen werden soll,
+     obwohl dieser nichts mehr essen kann.
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "Du bist zu satt, das schaffst Du nicht mehr."
+
+SIEHE AUCH:
+     P_FOOD, P_MAX_FOOD, wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_FORCE_MURDER_MSG b/doc/props/P_FORCE_MURDER_MSG
new file mode 100644
index 0000000..cf0157c
--- /dev/null
+++ b/doc/props/P_FORCE_MURDER_MSG
@@ -0,0 +1,39 @@
+NAME:
+	P_FORCE_MURDER_MSG		"force_murder_msg"
+
+DEFINIERT IN:
+	/sys/properties.h
+
+BESCHREIBUNG:
+	Ob der Tod eines NPCs auf dem Moerder-Kanal erscheint, haengt davon
+	ab, wie oft und welche Art von NPCs in der letzten Zeit getoetet
+	wurden. Zum Beispiel ist es eher selten, dass ein schwacher NPC auf
+	dem Kanal erscheint, wenn kuerzlich viele starke NPCs getoetet
+	wurden. Mittels der Property P_FORCE_MURDER_MSG kann man auf diese
+	Regelung Einfluss nehmen.
+	Ein Wert groesser Null erzwingt die Meldung auf dem Moerder-Kanal,
+	ein Wert kleiner Null unterdrueckt sie generell. Letzteres ist
+	insbesondere fuer allzuoft getoetete Monster sinnvoll, um den
+	Moerder-Kanal nicht uebermaessig zu belasten. Mit dem Erzwingen der
+	Meldungen sollte man somit vorsichtig sein: Weniger ist oftmals
+	besser als zu viel!
+	Die Defaulteinstellung von P_FORCE_MURDER_MSG ist natuerlich Null
+	und fuehrt zur bereits beschriebenen opferabhaengigen Meldung.
+
+BEISPIELE:
+	Ein grosser starker NPC, der getoetet wurde, sollte schon eine tolle
+	Meldung auf dem Moerder-Kanal erzeugen. In der Property
+	P_MURDER_MSG koennen hierzu alternative Texte zu den normal per
+	Zufall ausgewaehlten angegeben werden:
+	  SetProp(P_MURDER_MSG,
+                  "Nicht schlecht, aber das schafft eh nur einer!");
+	  SetProp(P_FORCE_MURDER_MSG,1);
+	Zwar ist es bei grossen NPCs hinreichend wahrscheinlich, dass es
+	infolge derer Staerke zur automatischen Ausgabe einer Moerdermeldung
+	kommt, aber mit der letzten Zeile wurde dies absolut sichergestellt.
+
+SIEHE AUCH:
+	P_MURDER_MSG, P_ZAP_MSG, P_KILL_MSG, P_DIE_MSG, P_CORPSE, P_NOCORPSE
+
+----------------------------------------------------------------------------
+Last modified: Tue Nov 10 02:15:26 1998 by Patryn
diff --git a/doc/props/P_FREE_HANDS b/doc/props/P_FREE_HANDS
new file mode 100644
index 0000000..6143cad
--- /dev/null
+++ b/doc/props/P_FREE_HANDS
@@ -0,0 +1,22 @@
+P_FREE_HANDS
+NAME:
+    P_FREE_HANDS                  "free_hands"
+
+DEFINIERT IN:
+    /sys/living/combat.h
+
+BESCHREIBUNG:
+    Anzahl der freien Haende.
+    Effektiv nur ein die Differenz von P_MAX_HANDS und P_USED_HANDS.
+
+BEMERKUNGEN:
+    Keine echte Property. Die Methode /std/living/combat::_query_free_hands
+    stellt die Daten zusammen. Nicht setzen!
+
+SIEHE AUCH:
+    P_HANDS, P_HANDS_USED_BY
+    P_MAX_HANDS, P_USED_HANDS
+    UseHands, FreeHands
+    /std/weapon.c, /std/spellbook.c
+
+1. Okt 2012, Gloinson
diff --git a/doc/props/P_FRIEND b/doc/props/P_FRIEND
new file mode 100644
index 0000000..ded080c
--- /dev/null
+++ b/doc/props/P_FRIEND
@@ -0,0 +1,24 @@
+NAME:
+	P_FRIEND			"friend"
+
+DEFINIERT IN:
+	<combat.h>
+
+BESCHREIBUNG:
+	Setzt man diese Property in einem NPC auf einen Wert ungleich Null,
+	so wird der NPC bei Zauberspruechen, die auf bestimmte Gruppen
+	wirken, der Gruppe der Spieler und nicht der Gruppe der NPCs
+	zugeordnet. Ausserdem wird der NPC bei einem "toete alle" nicht mit
+	angegriffen.
+	Wurde der NPC einem Spieler per AssocMember() zugeordnet, so
+	befindet sich der NPC automatisch im jeweiligen Team des Spielers
+	 (moeglichst auch in der selben Kampfreihe), und die Property hat
+	dann automatisch das Spielerobjekt als Wert.
+	Da dieser Wert in diesem Fall natuerlich ungleich Null ist, wird der
+	NPC dann auch der Gruppe der Spieler zugeordnet.
+
+SIEHE AUCH:
+	FindGroup(), AssocMember(), P_TEAM_ASSOC_MEMBERS, /std/living/team.c
+
+----------------------------------------------------------------------------
+Last modified: Tue Dec 29 17:02:55 1998 by Patryn
diff --git a/doc/props/P_FROG b/doc/props/P_FROG
new file mode 100644
index 0000000..5f64eac
--- /dev/null
+++ b/doc/props/P_FROG
@@ -0,0 +1,8 @@
+NAME:
+    P_FROG                        "frog"                        
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Gesetzt, wenn der Spieler ein Frosch ist.
diff --git a/doc/props/P_FUEL b/doc/props/P_FUEL
new file mode 100644
index 0000000..890ca60
--- /dev/null
+++ b/doc/props/P_FUEL
@@ -0,0 +1,26 @@
+NAME:
+    P_FUEL                        "fuel"                        
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Numerischer Wert fuer die Zeitdauer, die die Lichtquelle noch
+     leuchten kann. Standardmaessig ist der Wert auf 20 gesetzt.
+
+     Setzt man die Property auf einen Wert, der groesser ist als der vorige
+     Maximalwert, wird dieser auf den neuen Wert erhoeht. Aendert man den
+     Brennstoffvorrat der Lichtquelle hingegen mittels AddFuel(), so wird
+     der Wert von P_FUEL ueber den Maximalwert hinaus erhoeht.
+
+HINWEIS:
+     Fuer Aenderungen an der Property kann auch die Funktion AddFuel()
+     verwendet werden. 
+
+SIEHE AUCH:
+     Basisklassen: /std/lightsource.c
+     Properties:   P_LIGHTDESC, P_DO_DESTRUCT, P_LIGHT
+     Methoden:     AddFuel(L)
+
+LETZTE AENDERUNG:
+    22. Dez. 2015, Arathorn
diff --git a/doc/props/P_FUNC_MSG b/doc/props/P_FUNC_MSG
new file mode 100644
index 0000000..b4f9d1e
--- /dev/null
+++ b/doc/props/P_FUNC_MSG
@@ -0,0 +1,21 @@
+NAME:
+    P_FUNC_MSG                    "func_msg"                    
+
+DEFINIERT IN:
+    /sys/room/description.h
+
+BESCHREIBUNG:
+     Liste mit Funktionen, die zufaellig im Raum aufgerufen werden.
+
+     Hier ist nur eine zur rufende lokale Methode als String oder eine
+     Sammlung davon als Stringarray gespeichert.
+
+ANMERKUNGEN:
+     Bitte AddRoomMessage() zum Hinzufuegen/Ueberschreiben benutzen!
+
+SIEHE AUCH:
+     LFuns:    AddRoomMessage()
+     Verwandt: tell_room(), ReceiveMsg()
+     Props:    P_ROOM_MSG, P_MSG_PROB
+
+2.Feb 2016 Gloinson
diff --git a/doc/props/P_FW_ALWAYS_READABLE b/doc/props/P_FW_ALWAYS_READABLE
new file mode 100644
index 0000000..31700ed
--- /dev/null
+++ b/doc/props/P_FW_ALWAYS_READABLE
@@ -0,0 +1,8 @@
+NAME:
+    P_FW_ALWAYS_READABLE          "fw_always_readable"          
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_FW_UNDERSTAND b/doc/props/P_FW_UNDERSTAND
new file mode 100644
index 0000000..ff8f6e6
--- /dev/null
+++ b/doc/props/P_FW_UNDERSTAND
@@ -0,0 +1,8 @@
+NAME:
+    P_FW_UNDERSTAND               "fw_understand"               
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_GENDER b/doc/props/P_GENDER
new file mode 100644
index 0000000..93f73dc
--- /dev/null
+++ b/doc/props/P_GENDER
@@ -0,0 +1,9 @@
+NAME:
+    P_GENDER                      "gender"                      
+
+DEFINIERT IN:
+    /sys/thing/language.h
+
+BESCHREIBUNG:
+     Grammatikalisches Geschlecht des Objektes:
+     (Definiert in language.h) MALE, FEMALE oder NEUTER
diff --git a/doc/props/P_GHOST b/doc/props/P_GHOST
new file mode 100644
index 0000000..9cbf309
--- /dev/null
+++ b/doc/props/P_GHOST
@@ -0,0 +1,8 @@
+NAME:
+    P_GHOST                       "ghost"                       
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Gesetzt, wenn der Spieler tot ist.
diff --git a/doc/props/P_GIVE_MSG b/doc/props/P_GIVE_MSG
new file mode 100644
index 0000000..bd6a624
--- /dev/null
+++ b/doc/props/P_GIVE_MSG
@@ -0,0 +1,65 @@
+P_GIVE_MSG
+NAME:
+     P_GIVE_MSG				"give_message"
+
+DEFINIERT IN:
+     /sys/living/put_and_get.h
+
+BESCHREIBUNG:
+     Mit P_GIVE_MSG kann man die Meldung, die man beim Uebergeben eines
+     Objektes bekommt, modifizieren.
+
+     Folgende Werte sind moeglich:
+
+     o 0
+       Es wird eine Standardmeldung ausgegeben. Dies ist Voreinstellung.
+
+     o NO_PNG_MSG	== -1
+       Es wird keinerlei Meldung ausgegeben
+
+     o Ein Array aus Strings
+       Der erste String wird an den Spieler ausgegeben, der zweite
+       (optionale) an den Raum, der dritte (ebenfalls optionale) an den
+       Empfaenger.
+
+       Die Strings werden durch die Funktion replace_personal() geparst.
+	Objekt1 - Spieler
+        Objekt2 - das Objekt, das uebergeben wird
+	Objekt3 - Empfaenger
+
+       Wird der zweite String nicht angegeben, erfolgt keine Meldung an den
+       Raum. Beim Fehlen des dritten gibt es keine Meldung an den Empfaenger.
+
+BEISPIEL:
+     void create() {
+      ...
+      SetProp( P_SHORT, "Etwas Sand" );
+      SetProp( P_LONG, break_string(
+	"Ein wenig magischer Sand. Sehr feinkruemelig.",78 ));
+
+      SetProp( P_NAME, "Sand" );
+      AddId( ({"sand"}) );
+      SetProp( P_GENDER, MALE );
+
+      SetProp( P_GIVE_MSG, ({
+       "Du laesst @WEN2 in @WESSEN3 Haende rieseln.",
+       "@WER1 laesst @WENU2 in @WESSEN3 Haende rieseln.",
+       "@WER1 laesst @WENU2 in deine Haende rieseln."}));
+       ...
+      }
+
+     Das ganze fuehrt bei Ugars "gib sand an peter" zu folgenden
+     Meldungen:
+
+     Ugar: "Du laesst den Sand in Peters Haende rieseln."
+     Raum: "Ugar laesst Sand in Peters Haende rieseln."
+     Peter: "Ugar laesst Sand in deine Haende rieseln."
+
+SIEHE AUCH:
+     Aehnliches: P_DROP_MSG, P_PUT_MSG, P_PICK_MSG, P_SHOW_MSG
+     Fehler:     P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_TOO_MANY_MSG,
+                 P_NOINSERT_MSG, P_NOLEAVE_MSG, P_NODROP, P_NOGET 
+     Sonstiges:  replace_personal(E), give(L), give_objects(L),
+		 give_notify(L), /std/living/put_and_get.c
+
+14. Maerz 2004 Gloinson
diff --git a/doc/props/P_GLOBAL_SKILLPROPS b/doc/props/P_GLOBAL_SKILLPROPS
new file mode 100644
index 0000000..fc12c1e
--- /dev/null
+++ b/doc/props/P_GLOBAL_SKILLPROPS
@@ -0,0 +1,27 @@
+NAME:
+    P_GLOBAL_SKILLPROPS        "sm_global"                   
+
+DEFINIERT IN:
+    /sys/new_skills.h
+
+BESCHREIBUNG:
+    Diese Gilden- und Spellbookproperty enthaelt Eigenschaften, die
+    alle Spells eines Spellbooks bzw. alle Skills und Spells einer Gilde
+    haben sollen.
+
+BEISPIELE:
+    SetProp(P_GLOBAL_SKILLPROPS,
+      ([SI_SKILLRESTR_USE:     ([SR_FUN: #'spellTest]),
+        OFFSET(SI_SKILLLEARN): #'learnOffset,
+        OFFSET(SI_SPELLCOST):  #'costOffset,
+        FACTOR(SI_SPELLCOST):  #'costFactor]));
+
+SIEHE AUCH:
+    GObj Verwalten:   LearnSkill, LearnSpell, InitialSkillAbility
+    * Properties:     P_GUILD_SKILLS
+    Spellbook Lernen: Learn, SpellSuccess, Erfolg, Misserfolg
+    * Verwalten:      AddSpell, QuerySpell
+    * Properties:     P_SB_SPELLS
+    Skills:           P_NEWSKILLS, spruchermuedung, skill_info_liste
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_GUARD b/doc/props/P_GUARD
new file mode 100644
index 0000000..ebeb989
--- /dev/null
+++ b/doc/props/P_GUARD
@@ -0,0 +1,50 @@
+P_GUARD
+NAME:
+     P_GUARD				"guard"
+
+DEFINIERT IN:
+     /sys/guard.h
+
+BESCHREIBUNG:
+     Diese Property gibt an, ob ein NPC aus einem Raum entfernt werden darf
+     oder nicht. Abgefragt werden muss dies von den Items oder Spells, die
+     den NPC zu einer Bewegung zwingen wollen. Es wird nicht automatisch
+     darauf geachtet!
+
+     Entscheidend hierbei ist ein in der Property enthaltene (ganzzahliger)
+     Zahlenwert zwischen 0 und 100, der hierbei den Grad der
+     'Bewachungsstaerke' eines NPCs angibt. Bei 0 laesst sich das Lebewesen
+     immer zu einer Bewegung ueberreden, bei 100 ueberhaupt nicht. Dazwischen
+     gibt es die Wahrscheinlichkeit dafuer an.
+
+BEMERKUNGEN:
+     - alle von /std/npc abgeleiteten NPCs haben standardmaessig P_GUARD
+       auf 100 gesetzt, sind also nicht fortfuehrbar
+     - bei der Erzeugung von NPCs mit P_GUARD < 100 AddItem() mit dem
+       Parameter REFRESH_MOVE_HOME verwenden, damit sie bei einem Raumreset
+       gegebenenfalls an ihren Ausgangsort zurueckkehren. 
+     - gildenspezifische weitere Abfragen auf Level oAe bitte bei Gilden-
+       magiern erfragen
+
+BEISPIELE:
+     // ein Test
+     if(random(100)<=liv->QueryProp(P_GUARD))
+      cannotMoveNPC(); // NPC darf nicht bewegt werden!
+     else
+      moveNPC();       // NPC darf bewegt werden
+
+     // ein wegfuehrbarer NPC
+     void create() {
+      ::create();
+      ...
+      SetProp(P_GUARD,50);
+      ...
+     }
+     // mit 50% Wahrscheinlichkeit (pro Versuch) laesst sich der NPC nun
+     // fortfuehren
+
+
+SIEHE AUCH:
+     AddItem()
+
+13.April 2004 Gloinson
diff --git a/doc/props/P_GUILD b/doc/props/P_GUILD
new file mode 100644
index 0000000..bcffb4c
--- /dev/null
+++ b/doc/props/P_GUILD
@@ -0,0 +1,22 @@
+P_GUILD
+NAME:
+     P_GUILD				"guild"                       
+
+DEFINIERT IN:
+     /sys/properties.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt den Namen der Gilde, welcher das Lebewesen
+     angehoert. Der Name der Gilde ist hierbei kleingeschrieben als String
+     definiert.
+
+BEMERKUNGEN:
+     Bei Spielern ist der Wert dieser Property niemals Null, denn sie
+     gehoeren standardmaessig der in P_DEFAULT_GUILD stehenden Gilde an.
+     Ueber die gesetzte P_GUILD werden auch aktive Skills ermittelt.
+
+SIEHE AUCH:
+     P_DEFAULT_GUILD, P_VISIBLE_GUILD
+     P_GUILD_TITLE, P_SUBGUILD_TITLE
+
+26. Maerz 2004 Gloinson
diff --git a/doc/props/P_GUILD_DEACTIVATE_SKILLS b/doc/props/P_GUILD_DEACTIVATE_SKILLS
new file mode 100644
index 0000000..019da30
--- /dev/null
+++ b/doc/props/P_GUILD_DEACTIVATE_SKILLS
@@ -0,0 +1,38 @@
+PROPERTY:
+
+  P_GUILD_DEACTIVATE_SKILLS     "guild_deactivate_skills"
+
+DEFINIERT IN: 
+
+  new_skills.h
+
+BESCHREIBUNG:
+
+  Diese Property erlaubt es, Gildenmitgliedern die benutzung von ANY-
+  Skills zu untersagen. Dies sind einerseits die allgemeinen Waffenskills,
+  andererseits koennte das auch der entgifte-Spruch der Duesterwald -
+  Quest sein.
+
+  Wert dieser Property ist ein Mapping, das als Keys die einzelnen
+  Skills und als Wert 1 enthaelt, wenn ein Skill deaktiviert
+  werden soll und 0, falls nicht.
+
+  Diese Property wird nur bei einem Neuerzeugen des Spielers neu 
+  ausgelesen, da es sonst zuviel Rechenzeit kostet.
+
+BEISPIEL:
+
+  Jede Gilde, die keine Waffenskills benutzen soll (oder selbst welche hat)
+  enthaelt folgenden Befehl:
+
+    SetProp(P_GUILD_DEACTIVATE_SKILLS,
+    ([FIGHT(WT_SWORD):1,
+    FIGHT(WT_AXE):1,
+    FIGHT(WT_CLUB):1,
+    FIGHT(WT_SPEAR):1,
+    FIGHT(WT_KNIFE):1,
+    FIGHT(WT_WHIP):1]));
+    
+LETZTE AENDERUNG: 
+
+2003-01-30, 14:15, Humni
diff --git a/doc/props/P_GUILD_DEFAULT_SPELLBOOK b/doc/props/P_GUILD_DEFAULT_SPELLBOOK
new file mode 100644
index 0000000..9ad9150
--- /dev/null
+++ b/doc/props/P_GUILD_DEFAULT_SPELLBOOK
@@ -0,0 +1,23 @@
+NAME:
+	P_GUILD_DEFAULT_SPELLBOOK	"guild_sb"                    
+
+DEFINIERT IN:
+	/sys/new_skills.h
+
+BESCHREIBUNG:
+	Diese Gildenproperty enthaelt den Namen des Spellbooks, welches
+	standardmaessig von der Gilde verwendet wird.
+
+BEMERKUNGEN:
+	Bei Spells kann sie mit SI_SPELLBOOK ueberschrieben werden.
+
+BEISPIELE:
+	Bei Zauberern waere folgendes denkbar:
+	  SetProp(P_GUILD_DEFAULT_SPELLBOOK,"magie_sp");
+	Das Spellbook ist hierbei unter "/spellbooks/magie_sp.c" zu finden.
+
+SIEHE AUCH:
+	P_GUILD
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_GUILD_FEMALE_TITLES b/doc/props/P_GUILD_FEMALE_TITLES
new file mode 100644
index 0000000..b1528e9
--- /dev/null
+++ b/doc/props/P_GUILD_FEMALE_TITLES
@@ -0,0 +1,24 @@
+NAME:
+	P_GUILD_FEMALE_TITLES		"guild_female_titles"         
+
+DEFINIERT IN:
+	/sys/new_skills.h
+
+BESCHREIBUNG:
+	Diese Gildenproperty enthaelt die Stufenbezeichnungen fuer
+	weibliche Gildenmitglieder. Als Schluessel dienen hierbei die
+	Gildenstufen und die entsprechenden Eintraege sind dann die zur
+	Stufe gehoerigen Gildentitel.
+
+BEISPIELE:
+	Eine Programmierergilde koennte folgende Stufenbezeichnungen
+	beinhalten:
+	  SetProp(P_GUILD_FEMALE_TITLES,([1:"die Anfaengerin",
+	                                  2:"die Fortgeschrittene",
+	                                  3:"die Professionelle ;)"]));
+
+SIEHE AUCH:
+	P_GENDER, P_GUILD_LEVEL, P_GUILD_TITLE, P_GUILD_MALE_TITLES
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_GUILD_LEVEL b/doc/props/P_GUILD_LEVEL
new file mode 100644
index 0000000..4920449
--- /dev/null
+++ b/doc/props/P_GUILD_LEVEL
@@ -0,0 +1,23 @@
+NAME:
+	P_GUILD_LEVEL			"guild_level"                 
+
+DEFINIERT IN:
+	/sys/new_skills.h
+
+BESCHREIBUNG:
+	Diese Property enthaelt die Gildenstufe des Lebewesens, welche nicht
+	unbedingt seiner Spielerstufe entspricht.
+
+BEMERKUNGEN:
+	Bei manchen Gilden entspricht die Gildenstufe standardmaessig der
+	Spielerstufe (Abenteurer, Katzenkrieger). In der Property selbst
+	wird eigentlich ein Mapping eingetragen, welches die Gildennamen als
+	Schluessel und die dazugehoerige Gildenstufe als Eintrag enthaelt.
+	Bei der Abfrage der Property wird jedoch die Gilde beruecksichtigt
+	und die aktuelle Gildenstufe zurueckgeliefert.
+
+SIEHE AUCH:
+	P_GUILD, P_GUILD_TITLE, P_GUILD_MALE_TITLES, P_GUILD_FEMALE_TITLES
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_GUILD_LEVELS b/doc/props/P_GUILD_LEVELS
new file mode 100644
index 0000000..80d2237
--- /dev/null
+++ b/doc/props/P_GUILD_LEVELS
@@ -0,0 +1,35 @@
+NAME:
+	P_GUILD_LEVELS			"guild_levels"                
+
+DEFINIERT IN:
+	/sys/new_skills.h
+
+BESCHREIBUNG:
+	Diese Gildenproperty enthaelt ein Mapping mit den
+	Stufenbeschraenkungen fuer die jeweiligen Gildenstufen. Als
+	Schluessel dient der jeweilige Gildenlevel und die entsprechenden
+	Eintraege sind wiederum Mappings, in welchen die Beschraenkungen
+	angegeben sind.
+
+BEMERKUNGEN:
+	Die Beschraenkungen fuer Level 1 sollten auch fuer die
+	Eintrittsbeschraenkungen der Gilde gelten. Letztere kann man in der
+	Property P_GUILD_RESTRICTIONS angeben.
+
+BEISPIELE:
+	Das folgende Beispiel zeigt ein paar moegliche Abfragen:
+	  string check(object pl);
+	  SetProp(P_GUILD_LEVELS,([1:([P_LEVEL:3,P_QP:100,SR_FUN:#'check]),
+	                           2:([A_INT:10,P_LEVEL:25]),
+	                           3:([A_INT:20,P_LEVEL:50])]));
+	  string check(object pl)
+	  { if(pl->QueryProp(P_MALE))
+	      return 0;
+	    return "Fuer Frauen ist das nichts!\n";
+	  }
+
+SIEHE AUCH:
+	P_GUILD_LEVEL, P_GUILD_RESTRICTIONS, /std/restriction_checker.c
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_GUILD_MALE_TITLES b/doc/props/P_GUILD_MALE_TITLES
new file mode 100644
index 0000000..27e05a5
--- /dev/null
+++ b/doc/props/P_GUILD_MALE_TITLES
@@ -0,0 +1,24 @@
+NAME:
+	P_GUILD_MALE_TITLES		"guild_male_titles"           
+
+DEFINIERT IN:
+	/sys/new_skills.h
+
+BESCHREIBUNG:
+	Diese Gildenproperty enthaelt die Stufenbezeichnungen fuer
+	maennliche Gildenmitglieder. Als Schluessel dienen hierbei die
+	Gildenstufen und die entsprechenden Eintraege sind dann die zur
+	Stufe gehoerigen Gildentitel.
+
+BEISPIELE:
+	Eine Programmierergilde koennte folgende Stufenbezeichnungen
+	beinhalten:
+	  SetProp(P_GUILD_MALE_TITLES,([1:"der Anfaenger",
+	                                2:"der Fortgeschrittene",
+	                                3:"der Profi"]));
+
+SIEHE AUCH:
+	P_GENDER, P_GILD_LEVEL, P_GUILD_TITLE, P_GUILD_FEMALE_TITLES
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_GUILD_PREPAREBLOCK b/doc/props/P_GUILD_PREPAREBLOCK
new file mode 100644
index 0000000..e429a49
--- /dev/null
+++ b/doc/props/P_GUILD_PREPAREBLOCK
@@ -0,0 +1,37 @@
+P_GUILD_PREPAREBLOCK (int)
+
+NAME:
+  P_GUILD_PREPAREBLOCK                           "guild_prepareblock" 
+
+DEFINIERT IN:
+  /sys/new_skills.h
+
+BESCHREIBUNG:
+  Diese Property enthaelt die Information, ob das Lebewesen in
+  der Konzentrationsphase eines Spells weiterkaempft oder ob
+  die Angriffe derweil ausgesetzt werden.
+
+BEMERKUNGEN:
+  In der Property selbst wird eigentlich ein Mapping eingetragen,
+  welches die Gildennamen als Schluessel und das dazugehoerige
+  Block-Flag als Eintrag enthaelt. Bei der Abfrage der Property wird
+  jedoch die Gilde beruecksichtigt und das aktuelle Flag
+  zurueckgeliefert.
+  Dementsprechend das diese Prop nicht mit Set() manipuliert werden, 
+  bitte ausschliessliche SetProp() verwenden.
+
+BEISPIELE:
+  Die Property sollte natuerlich nur von der Gilde gesetzt werden
+  und auch nur bei Gildenmitgliedern
+
+  if ( IsGuildMember(pl) )
+    pl->SetProp(P_GUILD_PREPAREBLOCK,1);
+
+  Resultat: Kein Ausfuehren von Attack(), wenn pl sich auf einen
+  Zauberspruch konzentriert.
+
+SIEHE AUCH:
+  P_PREPARED_SPELL
+
+----------------------------------------------------------------------------
+18.03.2008, Zesstra
diff --git a/doc/props/P_GUILD_RATING b/doc/props/P_GUILD_RATING
new file mode 100644
index 0000000..df5800a
--- /dev/null
+++ b/doc/props/P_GUILD_RATING
@@ -0,0 +1,22 @@
+NAME:
+	P_GUILD_RATING			"guild_rating"                
+
+DEFINIERT IN:
+	/sys/new_skills.h
+
+BESCHREIBUNG:
+	In dieser Property wird die Einstufung des Spielers innerhalb
+	seiner Gilde festgelegt. Der dafuer zu ermittelnde Wert muss in
+	einem Bereich von 0 bis 10000 liegen. Wie sich die Einstufung
+	zusammensetzt, bleibt der jeweiligen Gilde ueberlassen.
+
+BEMERKUNGEN:
+	Der Wert muss von der Gilde ermittelt werden! Meist setzt er sich
+	aus den Faehigkeiten des Mitglieds zusammen und mitunter fliessen
+	auch Gildenquests oder aehnliches mit ein.
+
+SIEHE AUCH:
+    P_NEWSKILLS, GuildRating
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_GUILD_RESTRICTIONS b/doc/props/P_GUILD_RESTRICTIONS
new file mode 100644
index 0000000..8bbabc1
--- /dev/null
+++ b/doc/props/P_GUILD_RESTRICTIONS
@@ -0,0 +1,37 @@
+NAME:
+    P_GUILD_RESTRICTIONS        "guild_rest"                  
+
+DEFINIERT IN:
+    /sys/new_skills.h
+
+BESCHREIBUNG:
+    Diese Gildenproperty enthaelt ein Mapping mit den
+    Eintrittsbeschraenkungen fuer die jeweilige Gilde.
+
+BEMERKUNGEN:
+    Im allgemeinen sollte das Mapping mit den Eintrittsbeschraenkungen
+    mit den Beschraenkungen fuer Level 1 im Mapping von P_GUILD_LEVELS
+    uebereinstimmen.
+
+BEISPIELE:
+    Ein paar moegliche Abfragen waeren folgende:
+    string check(object pl);
+
+    SetProp(P_GUILD_RESTRICTIONS,
+            ([P_LEVEL:3,
+              P_QP:100,
+              SR_FUN:#'check]));
+
+    string check(object pl) {
+      if(pl->QueryProp(P_MALE))
+        return 0;
+      return "Fuer Frauen ist das nichts!\n";
+    }
+
+SIEHE AUCH:
+    Gildenfkt.:
+    * Ein/Austritt: beitreten, bei_oder_aus_treten, austreten
+    * Sonstiges:    P_GUILD_LEVELS
+    Sonstiges:      /std/restriction_checker.c
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_GUILD_SKILLS b/doc/props/P_GUILD_SKILLS
new file mode 100644
index 0000000..d7e30a2
--- /dev/null
+++ b/doc/props/P_GUILD_SKILLS
@@ -0,0 +1,24 @@
+P_GUILD_SKILLS
+NAME:
+    P_GUILD_SKILLS            "guild_skills"                
+
+DEFINIERT IN:
+    /sys/new_skills.h
+
+BESCHREIBUNG:
+    Diese Gildenproperty enthaelt ein Mapping mit allen Skills und
+    Spells der Gilde.
+
+BEMERKUNGEN:
+    Man sollte diese Property sollte nicht per Hand veraendern, sondern
+    die Funktionen AddSkill() bzw. AddSpell() nutzen.
+
+SIEHE AUCH:
+    GObj Verwalten:   LearnSkill, LearnSpell, InitialSkillAbility
+    * Sonstiges:      GuildRating
+    Spellbook Lernen: Learn, SpellSuccess, Erfolg, Misserfolg
+    * Verwalten:      AddSpell, QuerySpell
+    * Properties:     P_SB_SPELLS, P_GLOBAL_SKILLPROPS, P_GUILD_RATING
+    Skills:           P_NEWSKILLS, spruchermuedung, skill_info_liste
+
+3. Okt 2011 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_GUILD_TITLE b/doc/props/P_GUILD_TITLE
new file mode 100644
index 0000000..4554349
--- /dev/null
+++ b/doc/props/P_GUILD_TITLE
@@ -0,0 +1,37 @@
+P_GUILD_TITLE
+NAME:
+     P_GUILD_TITLE			"guild_title"                 
+
+DEFINIERT IN:
+     /sys/new_skills.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt den Gildentitel des Lebewesens, welcher der
+     Gildenstufe des Lebewesens entspricht und jedoch nur angezeigt wird,
+     wenn P_TITLE des Lebewesens gleich Null ist.
+
+BEMERKUNGEN:
+     In der Property selbst wird eigentlich ein Mapping eingetragen, welches
+     die Gildennamen als Schluessel und die dazugehoerigen Gildentitel als
+     Eintrag enthaelt. Bei der Abfrage der Property wird jedoch die Gilde
+     beruecksichtigt und der aktuelle Gildentitel zurueckgeliefert.
+
+BEISPIELE:
+	Fuer eine Programmierergilde waere folgendes denkbar:
+	  SetProp(P_GUILD_MALE_TITLES,([1:"der Anfaenger",
+	                                2:"der Fortgeschrittene",
+	                                3:"der Profi"]));
+	  SetProp(P_GUILD_FEMALE_TITLES,([1:"die Anfaengerin",
+	                                  2:"die Fortgeschrittene",
+	                                  3:"die Professionelle"]));
+	Ein weibliches Gildenmitglied mit der dritten Gildenstufe und ohne
+	P_TITLE wuerde dann den Titel "die Professionelle" tragen. Das hat
+	zwar nichts mit Programmierern zu tun, aber wie heisst eigentlich
+	ein weiblicher "Profi"?! :)
+
+SIEHE AUCH:
+     P_TITLE, P_GUILD, P_GUILD_LEVEL,
+     P_GUILD_MALE_TITLES, P_GUILD_FEMALE_TITLES,
+     P_SUBGUILD_TITLE
+
+26. Maerz 2004 Gloinson
diff --git a/doc/props/P_HANDS b/doc/props/P_HANDS
new file mode 100644
index 0000000..9eda7c5
--- /dev/null
+++ b/doc/props/P_HANDS
@@ -0,0 +1,57 @@
+P_HANDS
+NAME:
+     P_HANDS                    "hands"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt ein dreielementiges Array mit
+     den folgenden Eintraegen:
+     1. Element: String mit der Meldung, wenn ohne Waffen angegriffen
+                 wird.
+     2. Element: Waffenklasse, wenn ohne Waffen angegriffen wird.
+     3. Element: Array mit Schadensarten (frueher auch: Schadensart)
+
+     eim Setzen dieser Property wird auch P_TOTAL_WC mit veraendert,
+     wenn das Lebewesen keine Waffe gezueckt hat. Steckt das Lebewesen
+     eine gezueckte Waffe weg, so wird ebenfalls P_TOTAL_WC mit der
+     Waffenklasse aus P_HANDS aktualisiert. Zueckt das Lebewesen eine
+     Waffe, so wird P_TOTAL_WC mit der Waffenklasse der Waffe (P_WC)
+     ueberschrieben. Die Property P_TOTAL_WC enthaelt somit immer die
+     aktuelle Angriffsstaerke des Lebewesen.
+
+BEMERKUNGEN
+     In alten Objekten kann es vorkommen, dass das dritte Element (Angabe des
+     Schadenstyps) fehlt. Dies ist eine Altlast, die Angabe des Schadenstypes
+     ist NICHT optional.)
+
+BEISPIEL:
+     Ein starker Tausendfuessler mit Schlagschaden:
+
+       SetProp(P_HANDS,({" mit tausend kleinen Fuesschen",1000, 
+                         ({DT_BLUDGEON}) }));
+
+
+     Ein Saeurededaemon:
+       SetProp(P_HANDS,
+         ({
+           " mit saeuretriefenden Klauen",
+           250,
+           ({DT_RIP, DT_ACID})
+         })
+       );
+
+     Hier wurden gleich zwei Schadensarten angegeben, also wird
+     Mischschaden verursacht. Man sollte es jedoch nicht zu sehr
+     uebertreiben! Mehr als zwei oder drei gleichzeitig verwendete
+     Schadensarten lassen sich kaum mehr begruenden.
+
+SIEHE AUCH:
+     P_HANDS_USED_BY
+     P_MAX_HANDS, P_USED_HANDS, P_FREE_HANDS
+     UseHands, FreeHands
+     P_TOTAL_WC, P_WC
+     /std/living/combat.c, /std/weapon/combat.c, /sys/combat.h
+
+22.02.2014 Zesstra
diff --git a/doc/props/P_HANDS_USED_BY b/doc/props/P_HANDS_USED_BY
new file mode 100644
index 0000000..dd0e5fe
--- /dev/null
+++ b/doc/props/P_HANDS_USED_BY
@@ -0,0 +1,21 @@
+P_HANDS_USED_BY
+NAME:
+     P_HANDS_USED_BY           "hands_used_by"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+     Enthaelt eine Liste mit den Objekten, die derzeit die Haende
+     des Livings belegen. Dabei koennen Objekte mehrmals auftauchen,
+     je nachdem wie viele Haende sie belegen.
+
+BEMERKUNGEN:
+     Darf nur ueber UseHands() und FreeHands() manipuliert werden.
+
+SIEHE AUCH:
+     P_HANDS
+     P_MAX_HANDS, P_USED_HANDS, P_FREE_HANDS
+     UseHands, FreeHands
+
+1.Feb.2004 Gloinson
diff --git a/doc/props/P_HARBOUR b/doc/props/P_HARBOUR
new file mode 100644
index 0000000..c0e1048
--- /dev/null
+++ b/doc/props/P_HARBOUR
@@ -0,0 +1,66 @@
+NAME:
+    P_HARBOUR                                  "harbour_name"                   
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+    Array mit eindeutiger Bezeichnung des 'Hafens'
+
+BEMERKUNGEN:
+    Diese Property wird in Raeumen gesetzt, die als Anleger fuer Transporter
+    dienen sollen. Sie enthaelt ein Array aus zwei Elementen, einem String
+    und einem String-Array, zum Beispiel:
+
+    ({ "zur Sonneninsel", ({"sonneninsel"}) }) oder 
+    ({ "nach Titiwu", ({"titiwu"}) })
+
+    Damit bekommt der Spieler bei einer Abfrage seiner Reiseroute mittels
+    "reise route" eine Ausgabe wie 
+      'Du reist mit dem Floss nach Titiwu' oder
+      'Du reist mit dem Wal zur Sonneninsel'.
+
+    Das zweite Element der Property enthaelt eine Liste der IDs, mit denen
+    sich der Hafen mit dem Befehl "reise nach <ID>" ansprechen laesst. Im
+    Beispiel oben wuerde also "reise nach sonneninsel" und 
+    "reise nach titiwu" akzeptiert werden. Der erste Eintrag in dieser ID-
+    Liste wird in der Ausgabe des Befehls "reise route" verwendet, sollte
+    also den Anlegeort korrekt bezeichnen und nicht z.B. eine Abkuerzung
+    enthalten.
+    Es bietet sich an, bei bestimmten, deklinierbaren Bezeichnungen alle
+    Varianten einzutragen, z.B. "verlorenes land" und zusaetzlich
+    "verlorene land" und "verlorenen land", damit alle Varianten von 
+    "reise nach ..." funktionieren.
+
+    Ist in einem Hafen diese Property nicht gesetzt, so bekommt der 
+    Spieler keinerlei Hinweis darauf, wohin ihn ein bestimmter Trans-
+    porter befoerdert. 
+    Stattdessen erhaelt er die Bezeichnung 'unbekannt'.
+
+HINWEISE:
+    Wird der zweite Parameter in dieser Property, d.h. die Liste der 
+    Anleger-IDs, nicht korrekt gesetzt, kann das dazu fuehren, dass Spieler
+    den hier anlegenden Transporter nicht benutzen koennen, weil es
+    keine sinnvolle Syntax gibt, um den gewuenschten Zielhafen zu finden.
+
+    Zu achten ist auch darauf, das der erste Eintrag unveraendert in einer 
+    Meldung an den Spieler ausgegeben wird, d.h. Gross-und Kleinschreibung
+    sollte korrekt sein.
+
+HISTORIE:
+    Frueher war der zweite Eintrag in dieser Property ein einzelner String.
+    Es existiert eine SetMethode auf dieser Property, die solche Daten in
+    altem Code auf die neue Datenstruktur umbiegt. Dies fuehrt dazu, dass
+    bei solchen Objekten die im geladenen Raum enthaltenen Daten nicht mit
+    den Daten im File uebereinstimmen. Dies moege aber bitte niemand 
+    zum Anlass nehmen, in neuem Code veraltete Daten in die Property zu 
+    schreiben!
+    
+SIEHE AUCH:
+  Properties:     P_NO_TRAVELING, P_TRAVEL_INFO
+  Funktionen:     AddRoute(L)
+  Spielerbefehle: reise
+  weitere Doku:   /d/inseln/schiffe/HowTo
+
+LETZTE AENDERUNG:
+2015-Jan-18, Arathorn
diff --git a/doc/props/P_HAUS_ERLAUBT b/doc/props/P_HAUS_ERLAUBT
new file mode 100644
index 0000000..8ac6495
--- /dev/null
+++ b/doc/props/P_HAUS_ERLAUBT
@@ -0,0 +1,29 @@
+NAME:
+    P_HAUS_ERLAUBT                "haus_erlaubt"                
+
+DEFINIERT IN:
+    /sys/room/description.h
+
+BESCHREIBUNG:
+     Hier darf ein Seherhaus abgestellt werden
+
+BEMERKUNGEN:
+     Diese Property sollte nicht per default in Raeumen auf einen Wert > 0
+     gesetzt werden um einem Wildwuchs an Seherhaus(-ruinen) vorzubeugen.
+     Auch sei darauf geachtet, dass in Raeumen die P_INDOORS auf einen 
+     Wert > 0 haben, per default nicht gebaut werden kann.     
+     Hier lieber die Property per Hand auf 1 setzen und nach dem Aufstellen
+     des Hauses wieder loeschen.
+      
+     xcall $h->SetProp(P_HAUS_ERLAUBT,1);
+
+     Angemerkt sei noch, dass der Magier dem der Raum gehoert ueber Hausbau
+     entscheidet und nicht der Regionsmagier. In jedem Fall Ruecksprache 
+     halten falls der entsprechende Magier zumindest ab und an anwesend sein
+     sollte.
+
+     Unter /doc/beispiele/misc liegt ein File, in dem dokumentiert ist,
+     wie nach Aenderungen am Seherhaus zu verfahren ist.
+
+----------------------------------------------------------------------------
+Last modified: Fri Nov 26 15:41:47 1999 by Tilly
diff --git a/doc/props/P_HB b/doc/props/P_HB
new file mode 100644
index 0000000..eee2dcb
--- /dev/null
+++ b/doc/props/P_HB
@@ -0,0 +1,9 @@
+NAME:
+    P_HB                          "hb"                          
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Diese Property wird gesetzt, wenn das Monster immer einen
+     heart_beat haben soll. (VORSICHT, nur wenn es WIRKLICH sein muss!)
diff --git a/doc/props/P_HEAL b/doc/props/P_HEAL
new file mode 100644
index 0000000..242802f
--- /dev/null
+++ b/doc/props/P_HEAL
@@ -0,0 +1,32 @@
+NAME:
+     P_HEAL                        "heal"                        
+
+DEFINIERT IN:
+     /sys/properties.h
+
+BESCHREIBUNG:
+     Numerischer Wert, der beim Verzehr einer Leiche zu den Lebenspunkten 
+     desjenigen hinzugezaehlt wird, der die Leiche isst. Der Wert kann auch 
+     negativ sein. Falls die Leiche den maximalen Verfallszustand erreicht 
+     hat, wird dieser Wert automatisch auf -4 gesetzt, sofern nicht schon
+     ein geringerer Wert eingetragen ist.
+
+     Werte unter -4 sind sehr sparsam und nur in begruendeten Einzelfaellen
+     zu setzen. Die Regionsmagier werden auf sinnvolle Wertebereiche in
+     ihrem Zustaendigkeitsbereich achten und ggf. Grenzwerte fuer ihre 
+     Region festlegen.
+
+     Die Gilden koennen P_HEAL abfragen und eigene, grobe Informationstexte
+     ausgeben, die den Spieler ueber den zu erwartenden Effekt beim Essen
+     einer Leiche informieren.
+
+     Positive Werte von P_HEAL sind bei der Heilungsbalance als Heilstelle
+     zu beantragen.
+
+     Fuer nicht allzu harte NPCs sollte P_HEAL auf 0 gesetzt sein. Dieser
+     Wert ist gleichzeitig der Standardwert.
+
+SIEHE AUCH:
+     /std/corpse, QueryHealInfo()
+     
+31.03.2008 Arathorn
diff --git a/doc/props/P_HELPER_NPC b/doc/props/P_HELPER_NPC
new file mode 100644
index 0000000..eb71e47
--- /dev/null
+++ b/doc/props/P_HELPER_NPC
@@ -0,0 +1,30 @@
+P_HELPER_NPC
+
+NAME:
+     P_HELPER_NPC             "std:helper_npc
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+    Mit dieser Prop laesst sich herausfinden, ob ein NPC einem Spieler hilft
+    bzw. welche NPC einem Spieler helfen.
+    Wird diese Prop in einem NPC abgefragt, enthaelt sie ein Array 
+      ({ spielerobjekt, flags }) oder 0.
+    Wird diese Prop in einem Spieler abgefragt, enthaelt sie ein Mapping
+      ([npcobjekt1: flags1, npc-objekt2: flags2]).
+
+    <flags> ist ein Integer, der eine Reihe von ver-oder-ten Konstanten (s.
+    RegisterHelperNPC) enthalten kann.
+
+BEMERKUNGEN:
+    Diese Prop wird automatisch von RegisterHelperNPC() und
+    UnregisterHelperNPC() verwaltet. Bitte aendert sie nicht per Hand.
+
+BEISPIEL:
+    s. RegisterHelperNPC().
+
+SIEHE AUCH:
+     RegisterHelperNPC(), UnregisterHelperNPC()
+
+10.03.2009, Zesstra
diff --git a/doc/props/P_HELPER_OBJECTS b/doc/props/P_HELPER_OBJECTS
new file mode 100644
index 0000000..bdf031d
--- /dev/null
+++ b/doc/props/P_HELPER_OBJECTS
@@ -0,0 +1,25 @@
+P_HELPER_OBJECTS
+
+NAME:
+     P_HELPER_OBJECTS "lib_p_helper_objects"
+
+DEFINIERT IN:
+     <living/helpers.h>
+
+BESCHREIBUNG:
+     Diese Property enthaelt eine Liste von Hilfsobjekten, die das Lebewesen,
+     in dem sie gesetzt ist, bei bestimmten Aktivitaeten wie Tauchen oder
+     Fliegen/Segeln unterstuetzt. Die Property enthaelt ein Mapping der Form
+     ([ HELPER_TYPE : ({ Callback-Closures }) ]).
+
+BEMERKUNGEN:
+     Externe Manipulationen an dieser Property bitte unterlassen, zum Ein-
+     und Austragen von Objekten gibt es die unten aufgefuehrten Methoden.
+
+SIEHE AUCH:
+     Methoden:    RegisterHelperObject(L), UnregisterHelperObject(L)
+     Properties:  P_AERIAL_HELPERS, P_AQUATIC_HELPERS
+
+----------------------------------------------------------------------------
+18.02.2013, Arathorn
+
diff --git a/doc/props/P_HIDE_EXITS b/doc/props/P_HIDE_EXITS
new file mode 100644
index 0000000..0c92a4a
--- /dev/null
+++ b/doc/props/P_HIDE_EXITS
@@ -0,0 +1,35 @@
+P_HIDE_EXITS
+NAME:
+     P_HIDE_EXITS                  "hide_exits"
+
+DEFINIERT IN:
+     /sys/room/exits.h
+
+BESCHREIBUNG:
+     Ist diese Property in einem Raum auf einen Integerwert ungleich 0
+     gesetzt, werden einem Spieler fuer diesen Raum keine Ausgaenge angezeigt.
+     Auch der Text "Keine sichtbaren Ausgaenge." (oder so) kommt nicht.
+     Alternativ kann man auch ein array von strings uebergeben. In diesem
+     Fall werden all die Ausgaenge nicht angezeigt, deren Index in P_EXITS
+     in dem array vorkommt.
+     In diesem Fall wird ggf. der Text "Keine sichtbaren Ausgaenge."
+     ausgegeben.
+
+BEISPIEL:
+     AddExit("raus", "/ganz/wo/anders");
+     AddExit("weiter", "/in/der/naehe");
+
+     // KEINE Ausgaenge anzeigen. Noch nicht mal, dass man keine sieht.
+     SetProp(P_HIDE_EXITS, 1);
+
+     // Der Ausgang weiter wird nicht angezeigt.
+     SetProp(P_HIDE_EXITS, ({"weiter"}) );
+
+     // Keinen Ausgang zeigen, aber den Text, dass keiner sichtbar, ausgeben.
+     SetProp(P_HIDE_EXITS, ({"weiter", "raus"}) );
+
+SIEHE AUCH:
+     Aehnliches:	P_SHOW_EXITS
+     Sonstiges:		AddExit(), GetExits(), int_long(), int_short()
+
+25. Apr 1996 Highlander
\ No newline at end of file
diff --git a/doc/props/P_HISTMIN b/doc/props/P_HISTMIN
new file mode 100644
index 0000000..e84d90c
--- /dev/null
+++ b/doc/props/P_HISTMIN
@@ -0,0 +1,8 @@
+NAME:
+    P_HISTMIN                     "histmin"                     
+
+DEFINIERT IN:
+    /sys/player.h
+
+BESCHREIBUNG:
+     Minimale Laenge, die eine Zeile haben muss, um in die History zu kommen
diff --git a/doc/props/P_HIT_FUNC b/doc/props/P_HIT_FUNC
new file mode 100644
index 0000000..c5b3b31
--- /dev/null
+++ b/doc/props/P_HIT_FUNC
@@ -0,0 +1,22 @@
+P_HIT_FUNC
+
+NAME:
+     P_HIT_FUNC "hit_func"
+
+DEFINIERT IN:
+     <weapon.h>
+
+BESCHREIBUNG:
+     Falls ein Objekt eine HitFunc() fuer die Waffe definiert, so muss
+     dieses Objekt in dieser Property eingetragen werden.
+
+     Die Auswertung dieser Property erfolgt in QueryDamage().
+
+BEISPIELE:
+     Siehe das Beispiel zu HitFunc()
+
+SIEHE AUCH:
+     /std/weapon.c, HitFunc()
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 15:37:35 1996 by Wargon
diff --git a/doc/props/P_HOMEPAGE b/doc/props/P_HOMEPAGE
new file mode 100644
index 0000000..7d90438
--- /dev/null
+++ b/doc/props/P_HOMEPAGE
@@ -0,0 +1,8 @@
+NAME:
+    P_HOMEPAGE                    "homepage"                    
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Die Homepage eines Spielers (mit dem Befehl 'url' zu setzen).
diff --git a/doc/props/P_HP b/doc/props/P_HP
new file mode 100644
index 0000000..f2aea74
--- /dev/null
+++ b/doc/props/P_HP
@@ -0,0 +1,25 @@
+NAME:
+    P_HP                          "hp"
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+
+     - Lebewesen
+       Anzahl der Lebenspunkte des Wesens.
+
+     - Speisen/Kneipen
+       Heilwirkung einer Portion der Speise auf die Lebenspunkte
+       des Konsumenten
+
+SIEHE AUCH:
+     Props:		P_MAX_HP
+     Veraenderung:	reduce_hit_points(), restore_hit_points()
+			do_damage(L), Defend(L)
+			buffer_hp()
+     Ueberwachung:	AddHpHook(L)
+     Speisen/Kneipen:   std/pub, wiz/food, consume
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_HP_DELAY b/doc/props/P_HP_DELAY
new file mode 100644
index 0000000..a807ad9
--- /dev/null
+++ b/doc/props/P_HP_DELAY
@@ -0,0 +1,10 @@
+NAME:
+    P_HP_DELAY                 "hp_delay"                     
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Anzahl der heart_beats, bis P_HP um einen Punkt regeneriert.
+     Aenderungen dieser Property in Spielern beduerfen der 
+     Genehmigung des EM fuer Balance.
diff --git a/doc/props/P_HP_HOOKS b/doc/props/P_HP_HOOKS
new file mode 100644
index 0000000..28f1a1e
--- /dev/null
+++ b/doc/props/P_HP_HOOKS
@@ -0,0 +1,19 @@
+NAME:
+    P_HP_HOOKS                    "hp_hooks"                    
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Welche Objekte sollen bei HP-Aenderungen benachrichtigt werden?
+     Diese Property bitte NICHT manipulieren! Zum Ein- und Austragen stehen
+     hierfuer AddHpHook() und RemoveHpHook() bereit.
+
+BEMERKUNGEN:
+    Das neuere Hooksystem (s. Manpage std/hooks) stellt ebenfalls Hooks zur
+    Ueberwachung von P_HP bereit.
+
+SIEHE AUCH:
+    AddHpHook(), RemoveHpHook(),
+    NotifyHpChange()
+    std/hooks
diff --git a/doc/props/P_HUNTTIME b/doc/props/P_HUNTTIME
new file mode 100644
index 0000000..377ab18
--- /dev/null
+++ b/doc/props/P_HUNTTIME
@@ -0,0 +1,35 @@
+P_HUNTTIME (int)
+
+NAME:
+     P_HUNTTIME					"p_lib_hunttime"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+    Mit dieser Property laesst sich festlegen, wie lange ein Monster einen 
+    Gegner verfolgt (also automatisch angreift), nachdem dieser geflohen ist.
+    Die Angabe erfolgt in Sekunden.
+    Die Standardzeit ist 600s (300 Heartbeats).
+
+BEMERKUNGEN:
+    1. Wenn der Standardwert akzeptabel ist, bitte die Prop auch nicht setzen.
+    2. Enthaelt die Property keinen Integer oder ist sie <= 0, wird sie 
+       ignoriert und der Standardwert von 600s verwendet.
+    3. Kaempft man mit einem Lebenwesen mit einem groesseren Wert als man 
+       selber und man selber hat das Verfolgen eingestellt, der Gegner aber 
+       nicht, wird dieser beim Reinkommen den Kampf neu starten (und den 
+       ersten Schlag haben).
+
+BEISPIEL:
+    Ein NPC soll sich erst eine Stunde nach Flucht des Gegners beruhigen.
+    protected void create() {
+      ...
+      SetProp(P_HUNTTIME, 3600);
+    }
+
+SIEHE AUCH:
+     InsertSingleEnemy, InsertEnemy
+     /std/living/combat.c
+
+13.03.2008, Zesstra
diff --git a/doc/props/P_IDS b/doc/props/P_IDS
new file mode 100644
index 0000000..311407b
--- /dev/null
+++ b/doc/props/P_IDS
@@ -0,0 +1,25 @@
+P_IDS
+
+NAME:
+     P_IDS "ids"
+
+DEFINIERT IN:
+     <thing/description.h>
+
+BESCHREIBUNG:
+     In dieser Property steht ein Array von den Strings, mit denen sich das
+     Objekt ansprechen laesst. Die Verwaltung dieser Property erfolgt ueber
+     die Funktionen AddId() und RemoveId().
+
+     Der Inhalt dieser Property wird von den Funktionen id() und (indirekt)
+     present() ausgewertet.
+
+BEMERKUNGEN:
+     Man sollte an dieser Property nicht "von Hand" herumfummeln, sondern
+     immer die zugehoerigen Funktionen benutzen!
+
+SIEHE AUCH:
+     /std/thing/description.c, AddId(), RemoveId()
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 20:17:36 1996 by Wargon
diff --git a/doc/props/P_IGNORE b/doc/props/P_IGNORE
new file mode 100644
index 0000000..d95fd14
--- /dev/null
+++ b/doc/props/P_IGNORE
@@ -0,0 +1,22 @@
+NAME:
+    P_IGNORE                      "ignore"
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+    Array mit Spielern, Kommandos, Aktionen u.ae. die ignoriert werden.
+    In aller Regel sollte die Ignorierepruefung durch Aufruf von TestIgnore()
+    im Spieler erfolgen und nicht selber P_IGNORE durchsucht werden.
+
+BEMERKUNGEN:
+    Die Daten in dieser Property werden intern als Mapping gespeichert, nicht
+    als Array von Strings. Bitte nicht mit Set/Query() an dieser Property
+    herumbasteln.
+
+SIEHE AUCH:
+    TestIgnore, /std/player/comm.c
+
+----------------------------------------------------------------------------
+Last modified: 02.10.2011, Zesstra
+
diff --git a/doc/props/P_INDOORS b/doc/props/P_INDOORS
new file mode 100644
index 0000000..c1ab07a
--- /dev/null
+++ b/doc/props/P_INDOORS
@@ -0,0 +1,33 @@
+NAME:
+     P_INDOORS                     "indoors"
+
+DEFINIERT IN:
+     /sys/room/description.h
+
+BESCHREIBUNG:
+     Gesetzt, wenn von dem Raum aus der Himmel nicht sichtbar ist.
+
+     Objekte oder Gilden werten diese Property teilweise aus, fuer
+     Innenraeume sollte sie also sinnvollerweise gesetzt werden.
+
+     Licht wird _nicht_ durch P_INDOORS beeinflusst, muss also
+     selbst angepasst werden.
+
+BEISPIEL
+     // Ein dunkler Innenraum:
+     SetProp(P_INDOORS, 1);            // ein Innenraum liegt innen :)
+     SetProp(P_LIGHT, 0);              // Licht auf 0
+
+     // Ein richtig heller Aussenraum:
+     SetProp(P_INDOORS, 0);
+     SetProp(P_LIGHT, 2);
+
+     // Ein heller Aussenraum mit Mondlicht (gut fuer Delfen)
+     SetProp(P_INDOORS, 0);
+     SetProp(P_LIGHT, 1);
+     SetProp(P_LIGHT_TYPE, LT_STARS|LT_MOON);
+
+SIEHE AUCH
+     P_LIGHT, P_LIGHT_ABSORPTION, P_LIGHT_TYPE
+
+25.Aug 2008 Gloinson
diff --git a/doc/props/P_INFO b/doc/props/P_INFO
new file mode 100644
index 0000000..06dabe6
--- /dev/null
+++ b/doc/props/P_INFO
@@ -0,0 +1,18 @@
+P_INFO
+NAME:
+     P_INFO                        "info"                        
+
+DEFINIERT IN:
+     /sys/properties.h
+
+BESCHREIBUNG:
+     Geheime Information, die ggf. ueber einen Zauberspruch
+     von Spielern ermittelt werden kann.
+     
+BEISPIEL:
+     SetProp(P_INFO,"Du ergruendest das Geheimnis.\n")
+
+SIEHE AUCH:
+     GetOwner(L)
+
+24. April 2006, Vanion 
diff --git a/doc/props/P_INFORMME b/doc/props/P_INFORMME
new file mode 100644
index 0000000..4736933
--- /dev/null
+++ b/doc/props/P_INFORMME
@@ -0,0 +1,14 @@
+NAME:
+    P_INFORMME                    "informme"                    
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Enthaelt das Flag, ob der Spieler ueber ein-/ausloggende Spieler
+    informiert werden will.
+
+SIEHE AUCH:
+    Spielerkommando: inform
+
+6.Feb 2016 Gloinson
diff --git a/doc/props/P_INPC_HOME b/doc/props/P_INPC_HOME
new file mode 100644
index 0000000..31fea0b
--- /dev/null
+++ b/doc/props/P_INPC_HOME
@@ -0,0 +1,8 @@
+NAME:
+    P_INPC_HOME                   "inpc_home"                   
+
+DEFINIERT IN:
+    /sys/inpc/walking.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_INPC_LAST_ENVIRONMENT b/doc/props/P_INPC_LAST_ENVIRONMENT
new file mode 100644
index 0000000..2b7437a
--- /dev/null
+++ b/doc/props/P_INPC_LAST_ENVIRONMENT
@@ -0,0 +1,8 @@
+NAME:
+    P_INPC_LAST_ENVIRONMENT       "inpc_last_environment"       
+
+DEFINIERT IN:
+    /sys/inpc/walking.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_INPC_LAST_PLAYER_CONTACT b/doc/props/P_INPC_LAST_PLAYER_CONTACT
new file mode 100644
index 0000000..f32cd02
--- /dev/null
+++ b/doc/props/P_INPC_LAST_PLAYER_CONTACT
@@ -0,0 +1,8 @@
+NAME:
+    P_INPC_LAST_PLAYER_CONTACT    "inpc_last_player_contact"    
+
+DEFINIERT IN:
+    /sys/inpc/walking.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_INPC_WALK_AREA b/doc/props/P_INPC_WALK_AREA
new file mode 100644
index 0000000..3d7dfc6
--- /dev/null
+++ b/doc/props/P_INPC_WALK_AREA
@@ -0,0 +1,8 @@
+NAME:
+    P_INPC_WALK_AREA              "inpc_walk_area"              
+
+DEFINIERT IN:
+    /sys/inpc/walking.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_INPC_WALK_DELAYS b/doc/props/P_INPC_WALK_DELAYS
new file mode 100644
index 0000000..f510764
--- /dev/null
+++ b/doc/props/P_INPC_WALK_DELAYS
@@ -0,0 +1,8 @@
+NAME:
+    P_INPC_WALK_DELAYS            "inpc_walk_delay"             
+
+DEFINIERT IN:
+    /sys/inpc/walking.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_INPC_WALK_FLAGS b/doc/props/P_INPC_WALK_FLAGS
new file mode 100644
index 0000000..3e8749b
--- /dev/null
+++ b/doc/props/P_INPC_WALK_FLAGS
@@ -0,0 +1,8 @@
+NAME:
+    P_INPC_WALK_FLAGS             "inpc_walk_flags"             
+
+DEFINIERT IN:
+    /sys/inpc/walking.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_INPC_WALK_MODE b/doc/props/P_INPC_WALK_MODE
new file mode 100644
index 0000000..22da99d
--- /dev/null
+++ b/doc/props/P_INPC_WALK_MODE
@@ -0,0 +1,8 @@
+NAME:
+    P_INPC_WALK_MODE              "inpc_walk_mode"              
+
+DEFINIERT IN:
+    /sys/inpc/walking.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_INPC_WALK_ROUTE b/doc/props/P_INPC_WALK_ROUTE
new file mode 100644
index 0000000..4d11cb1
--- /dev/null
+++ b/doc/props/P_INPC_WALK_ROUTE
@@ -0,0 +1,8 @@
+NAME:
+    P_INPC_WALK_ROUTE             "inpc_walk_route"             
+
+DEFINIERT IN:
+    /sys/inpc/walking.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_INTERMUD b/doc/props/P_INTERMUD
new file mode 100644
index 0000000..6d4bd59
--- /dev/null
+++ b/doc/props/P_INTERMUD
@@ -0,0 +1,13 @@
+NAME:
+    P_INTERMUD                    "intermud"                    
+
+DEFINIERT IN:
+    /sys/player/comm.h
+
+BESCHREIBUNG:
+   Die Bedeutung dieser Property ist in den praehistorischen Untiefen
+   der Mudlib verlorengegangen.
+   Wird nicht mehr benutzt.
+   Nicht benutzen.
+   Ignorieren.
+
diff --git a/doc/props/P_INTERNAL_EXTRA_LOOK b/doc/props/P_INTERNAL_EXTRA_LOOK
new file mode 100644
index 0000000..160125d
--- /dev/null
+++ b/doc/props/P_INTERNAL_EXTRA_LOOK
@@ -0,0 +1,39 @@
+NAME:
+	P_INTERNAL_EXTRA_LOOK			"internal_extralook"
+
+DEFINIERT IN:
+	/sys/living/description.h
+
+BESCHREIBUNG:
+	Diese Property enthaelt ein Mapping, in dem alle einzelnen
+  Extra-Look-Eintraege des Livings enthalten sind. Dabei weden die Daten von
+  AddExtraLook() und RemoveExtraLook() verwaltet. Fragt man diese Prop mit
+  QueryProp() ab, erhaelt man die Ausgabe der gueltigen Extralooks des
+  Livings. Bei Abfrage per Query() erhaelt man ein Mapping mit allen
+  Eintraegen und deren Daten. Jeder Wert im Mapping ist erneut ein Mapping, 
+  welches folgende Keys enthalten kann:
+  - "xllook": String, der im Extralook des Living angezeigt wird.
+  - "xlduration": Zeitstempel (int), der angibt, wie lang der Eintrag gueltig
+                  ist. 0 == "Unendlich", negative Zahlen besagen, dass der
+                  Eintrag nur bis Reboot/Ende gueltig sein und abs(xlduration)
+                  ist der Zeitpunkt des Eintrages dieses Extralooks.
+  - "xlende": String, der nach Ablaufen an das Living ausgegeben wird.
+  - "xlfun": Funktion, die gerufen wird und den String zurueckliefern muss, 
+             der ausgeben werden soll.
+  - "xlendefun": Funktion, die gerufen wird, wenn der Eintrag abgelaufen ist
+                 und den String zurueckliefern muss, der dann ans Living
+                 ausgeben wird.
+  - "xlobjectname": Objekt, in dem die o.a. Funktionen gerufen werden.
+
+BEMERKUNG:
+  DIESE PROPERTY BITTE NIEMALS PER HAND MIT Set()/SetProp() AENDERN!
+  Die Daten in dieser Prop werden vom Living selber verwaltet. Extralooks
+  koennen mittel AddExtraLook() eingetragen und mit RemoveExtraLook() entfernt
+  werden.
+
+SIEHE AUCH:
+  long(), /std/living/description.c, /std/player/base.c
+  AddExtraLook(), RemoveExtraLook()
+
+----------------------------------------------------------------------------
+13.05.2007, Zesstra
diff --git a/doc/props/P_INT_LIGHT b/doc/props/P_INT_LIGHT
new file mode 100644
index 0000000..04504a6
--- /dev/null
+++ b/doc/props/P_INT_LIGHT
@@ -0,0 +1,20 @@
+NAME:
+    P_INT_LIGHT                       "int_light"
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Gibt den Lichtlevel an, der _in_ einem Objekt ist. Ein Abfragen dieser
+    Property kann z.B. in Raeumen sinnvoll sein, wenn es z.B. um das
+    aufwachen einer Eule oder einer Fledermaus geht. Zum Abfragen ob ein
+    Spieler etwas sehen kann, bitte auf jeden Fall P_PLAYER_LIGHT benutzen!
+
+    Bitte _nur_ ueber QueryProp auf diese Property zugreifen,
+    da das Lichtlevel ggf. neu berechnet werden muss!
+
+    Ein direktes setzen dieser Property ist NICHT moeglich, hierzu bitte
+    P_LIGHT benutzen!
+
+SIEHE AUCH:
+    P_LIGHT, P_TOTAL_LIGHT, P_PLAYER_LIGHT, P_LIGHT_MODIFIER, CannotSee()
diff --git a/doc/props/P_INT_LONG b/doc/props/P_INT_LONG
new file mode 100644
index 0000000..3e59249
--- /dev/null
+++ b/doc/props/P_INT_LONG
@@ -0,0 +1,32 @@
+P_INT_LONG
+NAME:
+     P_INT_LONG                    "int_long"
+
+DEFINIERT IN:
+     /sys/room/description.h
+
+BESCHREIBUNG:
+     Enthaelt Beschreibung, die man bekommt, wenn man sich in dem
+     Container (jeder Raum ist einer) umschaut als String.
+
+     Der Text sollte bereits umgebrochen sein.
+
+     Diese Property bestimmt die Ansicht des Containers von innen.
+     Fuer die Aussen(lang)ansicht muss P_LONG benutzt werden.
+
+BEMERKUNGEN:
+     - Beschreibungen fuer etwaige Tueren im Raum werden in int_long()
+       hinzugefuegt. (Frueher wurde dies in einer Querymethode auf diese Prop
+       gemacht.)
+
+BEISPIELE:
+     SetProp(P_INT_LONG, break_string(
+      "Du stehst in einem total spannenden Testraum. Seine absolute "
+      "Nichtigkeit erfuellt dich mit ehrfuerchtigem Grauen.",78));
+
+SIEHE AUCH:
+     Aehnliches:	P_INT_SHORT
+     Sonstiges:		int_long(), P_LONG
+			process_string(), break_string()
+
+04.06.2009, Zesstra
diff --git a/doc/props/P_INT_SHORT b/doc/props/P_INT_SHORT
new file mode 100644
index 0000000..9c6a0af
--- /dev/null
+++ b/doc/props/P_INT_SHORT
@@ -0,0 +1,36 @@
+P_INT_SHORT
+NAME:
+     P_INT_SHORT			"int_short"
+
+DEFINIERT IN:
+     /sys/thing/description.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt die Innen-Kurzbeschreibung des Containers
+     als String oder Closure (mit String-Rueckgabewert).
+
+     Container sind hierbei z.B. Raeume.
+     ACHTUNG: Die Kurzbeschreibung sollte dabei weder mit einem
+	      Satzzeichen noch mit einem "\n" abgeschlossen sein
+	      (dies wird von den zustaendigen Funktionen erledigt).
+
+     Man sollte die Property nicht auf 0 setzen.
+
+     Diese Property bestimmt die Ansicht des Containers von innen.
+     Fuer die Aussen(kurz)ansicht muss P_SHORT benutzt werden.
+
+BEMERKUNGEN:
+     - int_short() (bei Bewegung) filtert P_INT_SHORT durch process_string()
+       -> daher sind Closures moeglich
+
+BEISPIELE:
+     // ein Gang sieht natuerlich so aus
+     SetProp(P_INT_SHORT, "Ein Gang");
+
+SIEHE AUCH:
+     Aehnliches:	P_INT_LONG
+     Sonstiges:		int_short(), P_SHORT
+			process_string(), break_string()
+
+----------------------------------------------------------------------------
+Last modified: Thu May 31 15:34:05 2001 by Patryn
diff --git a/doc/props/P_INVIS b/doc/props/P_INVIS
new file mode 100644
index 0000000..7022ea0
--- /dev/null
+++ b/doc/props/P_INVIS
@@ -0,0 +1,50 @@
+NAME:
+     P_INVIS                       "invis"                       
+
+DEFINIERT IN:
+     /sys/player/base.h
+
+BESCHREIBUNG:
+     Die Property P_INVIS dient dazu, Objekte als unsichtbar zu kennzeichnen.
+     Hierbei versucht P_INVIS die moeglichen Interaktionen mit Spielern zu
+     minimieren (im Gegensatz zu einer fehlenden P_SHORT, welche das Objekt
+     nur einfach nicht-sichtbar macht).
+     
+     Man sollte drei Arten von unsichtbaren Objekten unterscheiden:
+
+     - Gegenstaende
+       Setzt man P_INVIS auf eine Zahl ungleich 0, wird der Gegenstand
+       unsichtbar und der Name zu "etwas". Zusaetzlich koennen Spieler ihn
+       nicht mehr ansprechen, d.h. nehmen, wegwerfen, weitergeben etc.
+       (Bei magier-eigenen Kommandos ist dies evtl. nicht umgesetzt...)
+       Setzt man P_SHORT auf 0, wird der Gegenstand nur nicht mehr in
+       der Inventarliste von Behaeltern/Raeumen angezeigt, er behaelt aber
+       seinen Namen und ist durch Spieler ansprechbar, wenn sie eine ID
+       kennen.
+
+     - NPCs
+       Bei gesetztem P_INVIS wird der NPC unsichtbar und sein Name wird zu
+       "Jemand". Zusaetzlich koennen Spieler ihn nicht mehr ansprechen, z.B.
+       toeten oder knuddeln.
+       (Bei magier-eigenen Kommandos ist dies evtl. nicht umgesetzt...)
+       Setzt man P_SHORT auf 0, wird der NPC nur nicht mehr in der
+       Inventarliste von Behaeltern/Raeumen angezeigt, er behaelt aber seinen
+       Namen und ist durch Spieler ansprechbar, wenn sie eine ID kennen. Auch
+       angreifen und kaempfen ist moeglich.
+     
+     - Magier
+       Magier macht man unsichtbar, indem man ihnen die Property P_INVIS auf
+       einen Wert <> 0 setzt.
+       *  Spieler duerfen nicht unsichtbar gemacht werden!               *
+       *  Wird ein Magier unsichtbar gemacht, muss man ihm die Property	 *
+       *  P_INVIS auf den Wert setzen, den die Property P_AGE zu diesem	 *
+       *  Zeitpunkt hat (keine F_QUERY_METHOD !).				                 *
+       Setzt man die Property auf den Wert 1, so erhaelt ein Spieler,
+       wenn er den entsp. Magier fingert, die Ausgabe: Alter: 00:00:02,
+       was genauso verraeterisch ist, wie ein Alter, dass bei einem
+       scheinbar nicht eingeloggten Magier immer weiter hochgezaehlt
+       wird.
+
+----------------------------------------------------------------------------
+27.05.2015, Zesstra
+
diff --git a/doc/props/P_IP_NAME b/doc/props/P_IP_NAME
new file mode 100644
index 0000000..7b503ea
--- /dev/null
+++ b/doc/props/P_IP_NAME
@@ -0,0 +1,8 @@
+NAME:
+    P_IP_NAME                     "ip_name"                     
+
+DEFINIERT IN:
+    /sys/player.h
+
+BESCHREIBUNG:
+     Rechnername des Interactives
diff --git a/doc/props/P_IS_ARTILLERY b/doc/props/P_IS_ARTILLERY
new file mode 100644
index 0000000..051b20f
--- /dev/null
+++ b/doc/props/P_IS_ARTILLERY
@@ -0,0 +1,15 @@
+NAME:
+	P_IS_ARTILLERY			"artillery"
+
+DEFINIERT IN:
+	/sys/combat.h
+
+BESCHREIBUNG:
+	Is ein Objekt Artillerie, so muss diese Property
+	gesetzt sein. (Derzeit einfach auf 1 setzen.)
+
+SIEHE AUCH:
+	artillerie
+
+----------------------------------------------------------------------------
+Last modified: Sam,  5. Jul 2003, 22:07:12 by Zook.
diff --git a/doc/props/P_ITEMS b/doc/props/P_ITEMS
new file mode 100644
index 0000000..add4255
--- /dev/null
+++ b/doc/props/P_ITEMS
@@ -0,0 +1,9 @@
+NAME:
+    P_ITEMS                       "items"                       
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Definition von Gegenstaenden, die in dem Raum liegen sollen.
+     Erklaerung in einem Extrafile.
diff --git a/doc/props/P_I_HATE_ALCOHOL b/doc/props/P_I_HATE_ALCOHOL
new file mode 100644
index 0000000..f60a38f
--- /dev/null
+++ b/doc/props/P_I_HATE_ALCOHOL
@@ -0,0 +1,23 @@
+NAME:
+    P_I_HATE_ALCOHOL                        "i_hate_alcohol"
+
+DEFINIERT IN:
+    /sys/inpc/boozing.h
+
+BESCHREIBUNG:
+    Numerischer Wert, der die Obergrenze an P_ALCOHOL in einem Monster
+    definiert, welcher beim eigenstaendigen Tanken beruecksichtigt wird.
+
+BEMERKUNG:
+    Geht der Npc (und nur fuer solche ist diese Prop gedacht) eigen-
+    staendig tanken, werden vor dem Bestellen die Getraenke und Speisen
+    auf ihren Alkoholgehalt geprueft und mit dem aktuellen Wert von
+    P_ALCOHOL im Verhaeltnis zu P_I_HATE_ALCOHOL verglichen. Laege der
+    Wert fuer P_ALCOHOL dann ueber dem von P_I_HATE_ALCOHOL, wird dieses
+    Getraenk (diese Speise) nicht bestellt.
+
+SIEHE AUCH:
+     P_ALCOHOL, P_MAX_ALCOHOL
+
+----------------------------------------------------------------------------
+Last modified: Sam Apr 14 12:35:00 2001 by Tilly
diff --git a/doc/props/P_KEEPER b/doc/props/P_KEEPER
new file mode 100644
index 0000000..c0275ba
--- /dev/null
+++ b/doc/props/P_KEEPER
@@ -0,0 +1,14 @@
+NAME:
+    P_KEEPER                       "shop_keeper"
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    In diese Property kann man in Kneipen und Laeden einen String schreiben.
+    Dann wird vor den Transaktionen geprueft, ob ein NPC anwesend ist,
+    der diesen String als ID hat.
+    Ist der NPC nicht vorhanden, kann nichts ge- oder verkauft werden.
+
+----------------------------------------------------------------------------
+Last modified: Mon Aug 23 14:29:00 1999 by Paracelsus
\ No newline at end of file
diff --git a/doc/props/P_KEEP_ON_SELL b/doc/props/P_KEEP_ON_SELL
new file mode 100644
index 0000000..8a21407
--- /dev/null
+++ b/doc/props/P_KEEP_ON_SELL
@@ -0,0 +1,8 @@
+NAME:
+    P_KEEP_ON_SELL                "keep_on_sell"                
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Bei "verkaufe alles" wird das Objekt behalten.
diff --git a/doc/props/P_KILLER b/doc/props/P_KILLER
new file mode 100644
index 0000000..83f6b18
--- /dev/null
+++ b/doc/props/P_KILLER
@@ -0,0 +1,27 @@
+NAME:
+    P_KILLER                      "killer"                      
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+   Diese Property enthaelt das Objekt, welches das Lebewesen als letztes
+   getoetet hat. Sie wird von do_damage(), heart_beat() (Vergiftungen) und
+   die() (bei direkten Aufrufen) automatisch gesetzt. Ein manuelles
+   Setzen vor Aufruf von do_damage() oder die() hat keinerlei Wirkung!
+   Sinnvollerweise liest man diese Property im NotifyPlayerDeath() aus,
+   spaeteres Auslesen ist unzuverlaessig, da der Killer inzwischen zerstoert
+   sein koennte.
+   Diese Property sollte _nicht_ per Hand gesetzt werden, schon gar nicht
+   waehrend eines NotifyPlayerDeath(), weil es evtl. noch andere Objekte gibt,
+   die sich dafuer interessieren!
+
+BEMERKUNGEN:
+   Normalerweise steht hier ein Objekt drin (s.o.). Es gibt allerdings eine
+   Ausnahme: Stirbt ein Lebewesen an Gift, enthaelt P_KILLER den String
+   "gift".
+
+SIEHE AUCH:
+   do_damage()
+
+29.08.2008, Zesstra
diff --git a/doc/props/P_KILLS b/doc/props/P_KILLS
new file mode 100644
index 0000000..3dae72d
--- /dev/null
+++ b/doc/props/P_KILLS
@@ -0,0 +1,10 @@
+NAME:
+    P_KILLS                       "playerkills"                 
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Anzahl der Spieler, die dieser Spieler schon getoetet hat.
+     Unerlaubte Manipulation ist ein SCHWERES VERGEHEN gegen
+     die Mudreglen.
diff --git a/doc/props/P_KILL_MSG b/doc/props/P_KILL_MSG
new file mode 100644
index 0000000..0a9e3bb
--- /dev/null
+++ b/doc/props/P_KILL_MSG
@@ -0,0 +1,80 @@
+P_KILL_MSG
+
+NAME:
+	P_KILL_MSG			"kill_msg"
+
+DEFINIERT IN:
+	/sys/properties.h
+
+BESCHREIBUNG:
+     Wenn ein Spieler getoetet wird, so erscheint dazu eine kurze Information
+     auf dem Todeskanal. Um dem toetenden Objekt zusaetzlich die Moeglichkeit
+     zu geben, noch etwas mehr auf diesem Kanal auszugeben, kann man in
+     dieser Property einen String uebergeben.
+     Noetige Zeilenumbrueche werden hierbei automatisch generiert.
+
+     Es ist auch moeglich anzugeben, ob Emotes verwendet werden und ob das
+     toetende Objekt ein Plural-Objekt ist. Hierzu uebergibt man ein Array
+     der Gestalt:
+
+	({Killmessage,Emotes}) bzw. ({Killmessage,Emotes,PLURAL})
+
+     Der Eintrag <Killmessage> stellt hierbei die Meldung selbst dar, PLURAL
+     gibt an, dass es sich um ein Plural-Objekt handelt und <Emotes> kann
+     folgende Werte annehmen:
+
+	MSG_SAY    - Meldung wird normal ausgegeben.
+	MSG_EMOTE  - Meldung erscheint als Emote.
+	MSG_GEMOTE - Meldung erscheint als Genitiv-Emote.
+	MSG_EMPTY  - Meldung erscheint ohne zuvorige Angabe des
+	               toetenden Objektes.
+
+     Moechte man die Meldung noch etwas "persoenlicher" ;-) gestalten, so
+     kann man den Platzhalter %s verwenden. An dessen Stelle wird dann der
+     Name des Verblichenen eingesetzt.
+
+BEISPIEL:
+     Ein nettes Beispiel ist das folgende: Wenn ein Spieler sich als
+     Drachentoeter bewehrt hat, so kann er traditionell in seinem Blut baden.
+     Hin und wieder ist jedoch auch der Drache erfolgreich, dem man eine
+     lustige Zusatzmeldung fuer den Todeskanal uebergeben kann:
+
+     void create() {
+       ::create();
+       ...
+       SetProp(P_KILL_MSG,"Jetzt bade ich mal in DEINEM Blut, %s!");
+       ...
+     }
+
+
+     Falls der 'Killer' ein Plural-Objekt oder -NPC war, koennte eine Meldung
+     auch folgendermassen aussehen:
+
+     SetProp(P_KILL_MSG,({"haun sich jetzt die Hucke voll.",
+			  MSG_EMOTE,
+			  PLURAL}));
+
+     wobei P_KILL_NAME hier natuerlich auf "Eine Menge Orks" oder
+     dergleichen gesetzt sein sollte. Auf dem Kanal waere dann dies zu
+     lesen:
+
+	[Tod:Lars] Eine Menge Orks haben gerade Orktoeter umgebracht.
+	[Tod:Eine Menge Orks haun sich jetzt die Hucke voll.]
+
+
+     Weiteres Beispiel.
+     Man habe einen NPC, dessen Killname als Plural aufzufassen ist, der aber
+     keinen zusaetlichen Text auf -Tod bringen soll.
+
+     SetProp(P_NAME, "Eine Horde Gummibaeren");
+     SetProp(P_KILL_NAME, "Viele kleine Gummibaeren");
+     SetProp(P_KILL_MSG, ({0, 0, 1}));
+
+SIEHE AUCH:
+     Tod:		die(L)
+     Todesmeldungen:	P_KILL_NAME, P_DIE_MSG, P_MURDER_MSG
+			P_ZAP_MSG, P_ENEMY_DEATH_SEQUENCE
+     Sonstiges:		P_CORPSE, P_NOCORPSE, /room/death/death_room.c
+
+----------------------------------------------------------------------------
+Last modified: Wed Aug 21 14:36:04 2002 by Bambi
diff --git a/doc/props/P_KILL_NAME b/doc/props/P_KILL_NAME
new file mode 100644
index 0000000..3f214ab
--- /dev/null
+++ b/doc/props/P_KILL_NAME
@@ -0,0 +1,43 @@
+P_KILL_NAME
+
+NAME:
+     P_KILL_NAME			"kill_name"
+
+DEFINIERT IN:
+     /sys/properties.h
+
+BESCHREIBUNG:
+     Wenn ein Spieler getoetet wird, so erscheint eine kurze Information auf
+     dem Todeskanal. Im Normalfall werden die Informationen aus P_NAME,
+     P_ARTICLE und P_GENDER des toetenden Objektes verwendet, um einen Namen
+     fuer eben dieses Objekt zu kreieren. Manchmal moechte man jedoch einen
+     davon unabhaengigen Namen dort stehen haben. Dann kommt die Property
+     P_KILL_NAME zum Einsatz.
+     Man sollte beachten, dass der Name des Toetenden nicht zu lang sein
+     sollte, denn obwohl bei einer Todesmeldung automatisch umgebrochen wird,
+     kann es doch ziemlich stoeren. Wenn das toetende Objekt ein Plural-
+     Objekt ist, so kann man dies zusaetzlich in der Property P_KILL_MSG
+     angeben.
+
+BEISPIEL:
+     Eine Wolke, die wahlweise zwischen verschiedenen Zustaenden mutiert,
+     koennte mal eine Eiswolke, mal eine Giftwolke oder auch mal eine
+     Feuerwolke sein. Fuer den Todeskanal soll jedoch immer erscheinen:
+     '[Tod:] Eine mutierende Wolke hat gerade <Spieler> umgebracht.'
+
+     void create()
+     {
+       ::create();
+       ...
+       SetProp(P_KILL_NAME,"Eine mutierende Wolke");
+       ...
+     }
+
+SIEHE AUCH:
+     Tod:		die(L)
+     Todesmeldungen:	P_KILL_MSG, P_DIE_MSG, P_MURDER_MSG
+			P_ZAP_MSG, P_ENEMY_DEATH_SEQUENCE
+     Sonstiges:		P_CORPSE, P_NOCORPSE, /room/death/death_room.c
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_KNOWN_POTIONROOMS b/doc/props/P_KNOWN_POTIONROOMS
new file mode 100644
index 0000000..f5adb97
--- /dev/null
+++ b/doc/props/P_KNOWN_POTIONROOMS
@@ -0,0 +1,19 @@
+NAME:
+    P_KNOWN_POTIONROOMS                 "known_potionrooms"                 
+
+DEFINIERT IN:
+    /sys/player/potion.h
+
+BESCHREIBUNG:
+    Array mit den Nummern der Raeume, in denen der Spieler Zauber-
+    traenke finden kann. Die Zuordnung der Raeume und Nummern
+    geschieht im Potionmaster.
+
+    Nur lesbare _query - Property.
+
+SIEHE AUCH:
+    Sonstiges: zaubertraenke, /secure/potionmaster.c, /room/orakel.c
+    Verwandt:  FindPotion(), AddKnownPotion(), RemoveKnownPotion(), InList()
+    Props:     P_POTIONROOMS
+
+6.Feb 2016 Gloinson
diff --git a/doc/props/P_LASTDIR b/doc/props/P_LASTDIR
new file mode 100644
index 0000000..eae9071
--- /dev/null
+++ b/doc/props/P_LASTDIR
@@ -0,0 +1,15 @@
+NAME:
+    P_LASTDIR                  "p_lib_lastdir"
+
+DEFINIERT IN:
+    /sys/shells.h
+
+BESCHREIBUNG:
+    Das jeweils letzte Verzeichnis, in dem der Magier war.
+    (Nur fuer Magier von Belang)
+
+Siehe auch:
+    P_CURRENTDIR
+
+Letzte Aenderung:
+    03.05.2016, Zesstra
diff --git a/doc/props/P_LAST_COMBAT_TIME b/doc/props/P_LAST_COMBAT_TIME
new file mode 100644
index 0000000..b3df96d
--- /dev/null
+++ b/doc/props/P_LAST_COMBAT_TIME
@@ -0,0 +1,23 @@
+NAME:
+	P_LAST_COMBAT_TIME		"last_combat_time"
+
+DEFINIERT IN:
+	/sys/combat.h
+
+BESCHREIBUNG:
+	In dieser Property wird genau die Zeit festgehalten, zu der ein
+	Lebewesen zuletzt einen Angriff abgewehrt oder einen Angriff
+	durchgefuehrt hat. Die Property enthaelt diese Information hierbei
+	immer in Form eines Integerwertes.
+	Dieser Wert koennte z.B. wichtig sein, wenn man wissen moechte, wann
+	ein Lebewesen zuletzt gekaempft hat. Es ist beispielsweise nicht
+	moeglich, waehrend oder auch unmittelbar nach einem Kampf den Befehl
+	'Ende' zu nutzen, da dies zur Flucht missbraucht werden kann. Dafuer
+	wird der Wert der Property zuvor ausgewertet.
+
+SIEHE AUCH:
+	P_LAST_DAMTIME, P_LAST_DAMAGE, P_LAST_DAMTYPES, Attack(), Defend(),
+	/std/living/combat.c, /std/living/life.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 26 16:43:00 1999 by Patryn
diff --git a/doc/props/P_LAST_COMMAND_ENV b/doc/props/P_LAST_COMMAND_ENV
new file mode 100644
index 0000000..576e34d
--- /dev/null
+++ b/doc/props/P_LAST_COMMAND_ENV
@@ -0,0 +1,15 @@
+P_LAST_COMMAND_ENV
+NAME:
+    P_LAST_COMMAND_ENV            "last_command_env"            
+
+DEFINIERT IN:
+    /sys/player.h
+
+BESCHREIBUNG:
+     Der Raum, in dem das letzte Kommando eingegeben wurde.
+
+BEMERKUNGEN:
+     Keine echte Property, _query_last_command_env() ist
+     in /std/players/command.c implementiert.
+
+14.Feb.2004 Gloinson
diff --git a/doc/props/P_LAST_CONTENT_CHANGE b/doc/props/P_LAST_CONTENT_CHANGE
new file mode 100644
index 0000000..0fa64bb
--- /dev/null
+++ b/doc/props/P_LAST_CONTENT_CHANGE
@@ -0,0 +1,16 @@
+NAME:
+    P_LAST_CONTENT_CHANGE         "last_content_change"         
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Wann wurde zum letzten Mal was ins Obj gestopft oder rausgenommen?
+     Wichtig fuer den Weight-Cache
+
+ANMERKUNG:
+     Die Property kann nur ueber QueryProp() und SetProp() ausgelesen bzw.
+     gesetzt werden. Query() und Set() funktionieren *nicht*.
+
+     Ausserdem fuehrt ein Setzen per SetProp() zu einer Erhoehung des 
+     Wertes um eins - unabhaengig vom gesetzten Wert.
diff --git a/doc/props/P_LAST_DAMAGE b/doc/props/P_LAST_DAMAGE
new file mode 100644
index 0000000..8d618b1
--- /dev/null
+++ b/doc/props/P_LAST_DAMAGE
@@ -0,0 +1,22 @@
+NAME:
+	P_LAST_DAMAGE			"last_damage"
+
+DEFINIERT IN:
+	/sys/living/combat.h
+
+BESCHREIBUNG:
+	In dieser Property wird genau die Schadensstaerke festgehalten,
+	welche ein Lebewesen zuletzt abbekommen hat. Die Property enthaelt
+	diese Information hierbei immer in Form eines Integerwertes.
+	Auch die Staerke des Giftschadens durch die Wirkung einer Vergiftung
+	wird hier festgehalten.
+	Dieser Wert koennte z.B. wichtig sein, wenn man wissen moechte,
+	welche Schadensstaerke nach Einwirkung von Defendern, Ruestung,
+	Hooks und Skills uebriggeblieben ist.
+
+SIEHE AUCH:
+	P_LAST_DAMTIME, P_LAST_DAMTYPES, Defend(),
+	/std/living/combat.c, /std/living/life.c
+
+----------------------------------------------------------------------------
+Last modified: Tue Jan 26 12:34:29 1999 by Patryn
diff --git a/doc/props/P_LAST_DAMTIME b/doc/props/P_LAST_DAMTIME
new file mode 100644
index 0000000..72ffb51
--- /dev/null
+++ b/doc/props/P_LAST_DAMTIME
@@ -0,0 +1,22 @@
+NAME:
+	P_LAST_DAMTIME			"last_damtime"
+
+DEFINIERT IN:
+	/sys/living/combat.h
+
+BESCHREIBUNG:
+	In dieser Property wird genau die Zeit festgehalten, zu der ein
+	Lebewesen zuletzt einen Schaden abbekommen hat. Die Property
+	enthaelt diese Information hierbei immer in Form eines
+	Integerwertes.
+	Auch der Zeitpunkt der Einwirkung von Giftschaden durch die Wirkung
+	einer Vergiftung wird hier festgehalten.
+	Dieser Wert koennte z.B. wichtig sein, wenn man wissen moechte, wann
+	ein Lebewesen zuletzt verletzt wurde.
+
+SIEHE AUCH:
+	P_LAST_COMBAT_TIME, P_LAST_DAMAGE, P_LAST_DAMTYPES, Defend(),
+	/std/living/combat.c, /std/living/life.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 26 16:44:38 1999 by Patryn
diff --git a/doc/props/P_LAST_DAMTYPES b/doc/props/P_LAST_DAMTYPES
new file mode 100644
index 0000000..9d7b060
--- /dev/null
+++ b/doc/props/P_LAST_DAMTYPES
@@ -0,0 +1,21 @@
+NAME:
+	P_LAST_DAMTYPES			"last_damtypes"
+
+DEFINIERT IN:
+	/sys/living/combat.h
+
+BESCHREIBUNG:
+	In dieser Property werden genau die Schadensarten festgehalten,
+	welche ein Lebewesen zuletzt abbekommen hat. Die Property enthaelt
+	diese Information hierbei immer in Form eines Arrays.
+	Auch der Giftschaden durch die Wirkung einer Vergiftung wird hier
+	festgehalten.
+	Dieser Wert koennte z.B. wichtig sein, wenn man nach dem Tod eines
+	Lebewesens feststellen moechte, durch was es umgekommen ist.
+
+SIEHE AUCH:
+	P_LAST_DAMAGE, P_LAST_DAMTIME, Defend(),
+	/std/living/combat.c, /std/living/life.c
+
+----------------------------------------------------------------------------
+Last modified: Tue Jan 26 12:34:29 1999 by Patryn
diff --git a/doc/props/P_LAST_DEATH_PROPS b/doc/props/P_LAST_DEATH_PROPS
new file mode 100644
index 0000000..712e04e
--- /dev/null
+++ b/doc/props/P_LAST_DEATH_PROPS
@@ -0,0 +1,18 @@
+NAME:
+    P_LAST_DEATH_PROPS                "last_death_props"
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt nach dem Tod eines Spielers ein Mapping mit 
+     den Werte aller Properties, die im die() zurueckgesetzt werden.
+
+     Auf diese Weise kann ein Objekt auch im NotifyPlayerDeath() noch 
+     auf die Werte zurueckgreifen, obwohl sie bereits geloescht sind.
+
+     Folgende Properties werden so gesichert:
+   
+     P_POISON, P_FROG, P_ALCOHOL, P_DRINK, P_FOOD , P_BLIND, P_DEAF, 
+     P_MAX_HANDS, P_PARA, P_NO_REGENERATION , P_HP, P_SP, P_LAST_DEATH_TIME
+
diff --git a/doc/props/P_LAST_DEATH_TIME b/doc/props/P_LAST_DEATH_TIME
new file mode 100644
index 0000000..71bd787
--- /dev/null
+++ b/doc/props/P_LAST_DEATH_TIME
@@ -0,0 +1,8 @@
+NAME:
+    P_LAST_DEATH_TIME                "last_death_time"
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Der Zeitpunkt des letzten Todes des Spielers.
diff --git a/doc/props/P_LAST_LOGIN b/doc/props/P_LAST_LOGIN
new file mode 100644
index 0000000..9287654
--- /dev/null
+++ b/doc/props/P_LAST_LOGIN
@@ -0,0 +1,16 @@
+NAME:
+    P_LAST_LOGIN                  "last_login"                  
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Zeitpunkt des letzten Logins
+
+BEMERKUNGEN:
+     Gegen aeussere Aenderung und Set/QueryMethoden geschuetzt.
+
+SIEHE AUCH:
+     P_LAST_LOGOUT, save_me
+
+28. Jan 2013 Gloinson
diff --git a/doc/props/P_LAST_LOGOUT b/doc/props/P_LAST_LOGOUT
new file mode 100644
index 0000000..296ae92
--- /dev/null
+++ b/doc/props/P_LAST_LOGOUT
@@ -0,0 +1,20 @@
+NAME:
+    P_LAST_LOGOUT                 "last_logout"                 
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Zeitpunkt des letzten Logouts. Anhand dieser Zeit werden die Feindes-
+     listen und in Abwesenheit eingefuegte Gegenstaende aktualisiert.
+
+     Dieses Datum wird bei Magiern nicht aktualisiert, wenn sie unsichtbar
+     sind/sich unsichtbar ausloggen.
+
+BEMERKUNGEN:
+     Gegen aeussere Aenderung und Set/QueryMethoden geschuetzt.
+
+SIEHE AUCH:
+     P_LAST_LOGIN, P_INVIS, save_me, init, StopHuntFor
+
+28. Jan 2013 Gloinson
diff --git a/doc/props/P_LAST_MOVE b/doc/props/P_LAST_MOVE
new file mode 100644
index 0000000..6b202a2
--- /dev/null
+++ b/doc/props/P_LAST_MOVE
@@ -0,0 +1,21 @@
+NAME:
+	P_LAST_MOVE			"last_move"
+
+DEFINIERT IN:
+	/sys/thing/moving.h
+
+BESCHREIBUNG:
+	In dieser Property wird die Zeit der letzten Bewegung eines
+	Lebewesens festgehalten. Selbsterklaerend ist auch der dazugehoerige
+	Quellcode innerhalb move() in '/std/living/moving.c':
+	  SetProp(P_LAST_MOVE,time());
+	Wichtig ist diese Property insbesondere fuer die Unterbindung von
+	Rein-Angriff-Raus-Taktiken. Dieser Modus ist standardmaessig in jedem
+	NPC aktiviert und kann ueber die Property P_ENABLE_IN_ATTACK_OUT
+	ausgeschalten werden.
+
+SIEHE AUCH:
+	move(), time(), P_ENABLE_IN_ATTACK_OUT, /std/living/moving.c
+
+-----------------------------------------------------------------------------
+Last modified: Sat Jan 30 12:53:00 1999 by Patryn
diff --git a/doc/props/P_LAST_USE b/doc/props/P_LAST_USE
new file mode 100644
index 0000000..5ca875d
--- /dev/null
+++ b/doc/props/P_LAST_USE
@@ -0,0 +1,20 @@
+P_LAST_USE
+
+NAME:
+     P_LAST_USE "last_use"
+
+DEFINIERT IN:
+     <properties.h>
+
+BESCHREIBUNG:
+     Diese Property wird in Ruestungen und Waffen dazu benutzt um
+     festzuhalten, wann die Ruestung/Waffe zuletzt im Kampf benutzt
+     wurde.
+
+SIEHE AUCH:
+     Ruestungen:	QueryDefend(L)
+     Waffen:		QueryDamage(L)
+     Sonstiges:		P_EQUIP_TIME, P_UNWIELD_TIME
+
+----------------------------------------------------------------------------
+Last modified: Fri Feb 06 10:15:00 1998 by Paracelsus
diff --git a/doc/props/P_LAST_WEAR_ACTION b/doc/props/P_LAST_WEAR_ACTION
new file mode 100644
index 0000000..1a4d224
--- /dev/null
+++ b/doc/props/P_LAST_WEAR_ACTION
@@ -0,0 +1,24 @@
+PROPERTY:
+
+	P_LAST_WEAR_ACTION    "last_wear_action"
+
+DEFINIERT IN: 
+
+	/sys/armour.h (und damit auch in properties.h)
+
+BESCHREIBUNG:
+
+	Diese Prop gibt an, welche An/Auszieh-Aktion ein Spieler zuletzt
+	durchgefuehrt hat. Sie ist ein zweielementiges Array, wobei der
+	erste Eintrag angibt, ob der Spieler sich an- oder ausgezogen
+	hat (WA_WEAR, WA_UNWEAR, auch in armour.h definiert) und der
+	zweite den Zeitpunkt.
+
+	Die Property wird (unter anderem?) dafuer verwendet festzustellen ob
+	der Spieler in der letzten Runde schon einen Ruestungswechsel
+	vorgenommen hat und einen entgegengesetzten zu unterbinden.
+
+LETZTE AENDERUNG: 
+
+2003-01-29, 17:30 von Humni
+
diff --git a/doc/props/P_LAST_XP b/doc/props/P_LAST_XP
new file mode 100644
index 0000000..e5ee055
--- /dev/null
+++ b/doc/props/P_LAST_XP
@@ -0,0 +1,27 @@
+NAME:
+    P_LAST_XP                        "last_xp"
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+    Ein Array vom Typ ({ pfad, xp }).
+
+    Der Eintrag "pfad" gibt das Gebiet an, in dem ein Spieler zuletzt XP
+    bekommen hat. Dabei wird aus "/d/ebene/magier/room/raum1.c" dann
+    "/d/ebene/magier/room".
+
+    Der Wert "xp" zeigt an, wieviele XP der Spieler _in diesem Gebiet_
+    gesammelt hat. Sobald er auch nur einen XP in einem anderen Gebiet
+    bekommt, zeigt P_LAST_XP nur noch diesen neu erhaltenen XP an.
+
+    Der Anwendungszweck waere z.B. eine Heilstelle, die nur dann Wirkung
+    zeigt, wenn der Spieler wirklich in dem betreffenden Gebiet gemetzelt hat
+    und nicht nur zum Tanken hergerannt ist oder eine Unterscheidung, ob
+    jemand metzelt oder nur uebt (ueber die XP).
+
+SIEHE AUCH:
+     Funktionen:  AddExp()
+     Verwandt:    P_NO_XP, P_XP
+
+14.Feb 2007 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_LEAVECMDS b/doc/props/P_LEAVECMDS
new file mode 100644
index 0000000..e31cf8a
--- /dev/null
+++ b/doc/props/P_LEAVECMDS
@@ -0,0 +1,32 @@
+NAME:
+    P_LEAVECMDS                   "leavecmds"                   
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+    Ein Array mit Befehlen, die zum Verlassen des Transporters fuehren. 
+
+BEISPIEL:
+    SetProp(P_LEAVECMDS,({ "verlass","verlasse" }) );
+
+    Gibt der Spieler nun 'verlasse xxx' ein, so sorgt /std/transport.c 
+    dafuer, das der Spieler auch von oder aus dem Transporter bewegt 
+    wird. Vorausgesetzt natuerlich, er ist an einem Haltepunkt angelangt.
+
+BEMERKUNGEN:
+    Um /std/transport.c nicht aufzublaehen, werden weitere Argumente wie
+    etwa 'verlasse das|die|den xxx' _nicht_ unterstuetzt!
+
+    Hier muss der verantwortliche Magier schon eine eigene Loesung finden
+    und in seinen Transporter schreiben.
+
+    Sollen Kommandos zum Betreten UND Verlassen eines Transporters ver-
+    wendet werden koennen, muessen diese in P_TRAVEL_CMDS gesetzt werden.
+
+SIEHE AUCH:
+    P_LEAVEFAIL, P_ENTERFAIL, P_ENTERCMDS, P_TRAVEL_CMDS, transporter,
+
+LETZTE AENDERUNG:
+    Don, 24.01.2002, 10:15:07h von Tilly
+    
\ No newline at end of file
diff --git a/doc/props/P_LEAVEFAIL b/doc/props/P_LEAVEFAIL
new file mode 100644
index 0000000..a54b4a1
--- /dev/null
+++ b/doc/props/P_LEAVEFAIL
@@ -0,0 +1,26 @@
+NAME:
+    P_LEAVEFAIL                   "leavefail"                   
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+     Meldung an den Spieler, wenn er ausserhalb der Anlegezeiten einen 
+     Transporter verlassen will. Ist die Propertie ein Array, so wird 
+     das erste Element als Meldung an den Spieler, das zweite als 
+     Meldung an die Mitspieler im Transporter geschickt. Ist der Eintrag
+     dagegen ein simpler String, so wird die Meldung nur an den Spieler
+     ausgegeben.
+
+BEISPIEL:
+     SetProp(P_LEAVEFAIL, "Die Wildgaense fliegen viel zu hoch" );
+
+     SetProp(P_LEAVEFAIL, ({ "Die Wildgaense fliegen viel zu hoch",
+                             "versucht, vom Ruecken der Wildgans zu "
+                            +"klettern und besinnt sich dann doch" }) );
+                             
+SIEHE AUCH:
+     P_LEAVEMSG, P_ENTERMSG, P_ENTERFAIL, transporter
+
+LETZTE AENDERUNG:
+    Don, 24.01.2002, 10:15:07h von Tilly
diff --git a/doc/props/P_LEAVEMSG b/doc/props/P_LEAVEMSG
new file mode 100644
index 0000000..592bbc4
--- /dev/null
+++ b/doc/props/P_LEAVEMSG
@@ -0,0 +1,19 @@
+NAME:
+    P_LEAVEMSG                    "leavemsg"                    
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+     Array mit zwei Meldungen. Der erste Eintrag wird an den Transporter
+     ausgegeben, der zweite an den Raum in den der Spieler kommt.
+
+BEISPIEL:
+     SetProp(P_LEAVEMSG, ({ "klettert vom Ruecken der Wildgans",
+                            "kommt vom Ruecken einer Wildgans herunter" }) );
+
+SIEHE AUCH: 
+     P_ENTERMSG, P_LEAVEFAIL, P_ENTERFAIL, transporter
+
+LETZTE AENDERUNG:
+    Don, 24.01.2002, 10:15:07h von Tilly
diff --git a/doc/props/P_LEP b/doc/props/P_LEP
new file mode 100644
index 0000000..7628b1c
--- /dev/null
+++ b/doc/props/P_LEP
@@ -0,0 +1,9 @@
+NAME:
+    P_LEP                         "lep"                         
+
+DEFINIERT IN:
+    /sys/player.h
+
+BESCHREIBUNG:
+     Levelpunkte eines Spielers
+     NICHT VON HAND SETZEN!!!
diff --git a/doc/props/P_LEVEL b/doc/props/P_LEVEL
new file mode 100644
index 0000000..40be5d0
--- /dev/null
+++ b/doc/props/P_LEVEL
@@ -0,0 +1,33 @@
+NAME:
+    P_LEVEL                       "level"                       
+
+DEFINIERT IN:
+    /sys/living/description.h
+
+BESCHREIBUNG:
+
+     Spieler-Level (!= Magierlevel)
+
+     In Krankheits- (CL_DISEASE) und Giftobjekten (CL_POISON) drueckt 
+     P_LEVEL aus, wie schwer die Krankheit/das Gift ist. Je nachdem, wie 
+     hoch oder niedrig der Level gesetzt wird, muss z.B. ein Kleriker mehr 
+     oder weniger oft kurieren, um  eine (ggf. stufenweise) Heilung zu 
+     bewirken.
+
+     In Fluchobjekten (CL_CURSE) gibt P_LEVEL ebenfalls die Schwere des
+     Fluches an, jedoch ist hier zu beachten, dass z.B. Kleriker aktuell
+     keine stufenweise Schwaechung bewirken koennen, sondern entweder den
+     Fluch entfernen koennen oder komplett scheitern. 
+     Der Fluchlevel ist hier grob als Chance des Scheiterns in einem 
+     Wertebereich von 1-100 zu sehen, was bedeutet, dass ein Fluchlevel 
+     von 100 als permanenter, nicht entfernbarer Fluch anzusehen ist.
+
+     Genaueres ist in der Klerusgilde nachzulesen oder beim GM Klerus zu
+     erfragen.
+
+SIEHE AUCH:
+     Properties:  P_GUILD_LEVEL
+     Allgemeines: /doc/wiz/gift, /doc/help/gift
+     Funktionen:  AddClass(L), is_class_member(L)
+
+Letzte Aenderung: 2015-Feb-02, Arathorn.
diff --git a/doc/props/P_LIFETIME b/doc/props/P_LIFETIME
new file mode 100644
index 0000000..250c287
--- /dev/null
+++ b/doc/props/P_LIFETIME
@@ -0,0 +1,30 @@
+NAME:

+     P_LIFETIME                    "std_food_lifetime"

+

+DEFINIERT IN:

+     /sys/food.h

+

+BESCHREIBUNG:

+     Zeit in Sekunden, die die Speise haltbar ist.

+     Mit Setzen dieser Property wird der Wert fuer P_RESET_LIFETIME und

+     dort gespeichert.

+     Nach dieser Zeit wird diese Speise schlecht und je nach Konfiguration

+     der Speise eventuell zerstoert. Sichergestellt wird dies durch den

+     Aufruf von reset() nach dieser Anzahl Sekunden.

+     Moechte man eine Speise haben, die niemals verdirbt

+     (genehmigungspflichtig!), nutzt man die Property P_NO_BAD

+     

+BEMERKUNGEN:

+     Sobald der Countdown zum Schlechtwerden der Speise laeuft, also ein

+     Spieler damit in Beruehrung kam, zeigt SetProp auf diese Property keine

+     Wirkung mehr.

+

+DEFAULT:

+     Ohne Setzen von P_LIFETIME ,P_RESET_LIFETIME und P_NO_BAD verdirbt die

+     Speise nach einem Reset, also zwischen 30 und 60 Minuten

+

+SIEHE AUCH:

+     wiz/food, P_RESET_LIFETIME, P_NO_BAD

+

+------------------------------------------------------------------------------

+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_LIGHT b/doc/props/P_LIGHT
new file mode 100644
index 0000000..251e927
--- /dev/null
+++ b/doc/props/P_LIGHT
@@ -0,0 +1,62 @@
+NAME:
+    P_LIGHT                       "light"
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Gibt den Lichtlevel eines Objektes an, d.h. wie hell das Objekt von sich
+    aus leuchtet. Moechte man den gesamten Lichtlevel haben der von einem
+    Objekt ausgeht, so sollte man P_TOTAL_LIGHT nehmen, das den Inhalt eines
+    Containers direkt mit verrechnet.
+
+    Bitte _nur_ ueber SetProp bzw. QueryProp zugreifen, da es fuer die
+    Berechnung wichtig ist, das in allen Containern P_LAST_CONTENT_CHANGE
+    gesetzt wird um ein neuberechnen des Lichtlevels auszuloesen!
+
+ANMERKUNG:
+    Um ein ungefaehres Gefuehl davon zu bekommen was ein Lichtlevel in
+    etwa bedeutet hier einige allgemeine Beispiele von Gegenstaenden.
+    Grundsaetzlich sollten Lichtlevel <0 und >2 nur in Ruecksprache mit dem
+    Balanceteam benutzt werden.
+
+    Lichtlevel -1,  z.B. ein schwarzer Ring, von dem eine kleine dunkle Aura
+                    ausgeht, die den Spieler umgibt.
+    Lichtlevel  0,  der Gegenstand beeinflusst das Licht ueberhaupt nicht
+    Lichtlevel  1,  der Spieler haelt eine kleine Lichtquelle in Haenden,
+                    dieses kann ein Leuchtpfirsich, eine Fackel oder ein
+                    Feuerschwert oder aehnliches sein.
+    Lichtlevel  2,  eine etwas groessere Lichtquelle, die aber immer noch
+                    nicht den Raum beleuchtet sondern lediglich dem Spieler
+                    einen Lichtschein gewaehrt mit dem er sehen kann.
+    Lichtlevel >2,  extrem helle Lichtquellen, die den gesamten Raum
+                    ausleuchten, in der Regel wohl eher magischer Natur.
+                    Solche Lichtquellen sollten sich mit der Zeit
+                    verbrauchen, dem Spieler Schaden zufuegen oder
+                    aehnliches, damit die Spieler nicht defaultmaessig mit
+                    einer solchen Lichtquelle durchs MG ziehn.
+
+    Daraus ergeben sich dann folgende Konsequenzen fuer Raeume:
+    Lichtlevel  >1: Der Raum ist sehr hell erleuchtet und kann von Spielern
+                    nicht oder nur sehr schwer abgedunkelt werden. Ein Wert
+                    von 2-3 kann interessant sein, wenn die NPCs im Raum
+                    ueber keine Nachtsicht verfuegen. Ueber ein Abdunkeln des
+                    Raumes kann der Spieler dann doch den Nachtkampf nutzen.
+    Lichtlevel   1: Der Raum ist erleuchtet und die Spieler koennen ohne
+                    weitere Lichtquellen sehen...
+    Lichtlevel   0: Ein dunkler Raum in dem man mit jeder Fackel sehen kann.
+    Lichtlevel  -1: man benoetigt zwei einfache Lichtquellen oder Nachtsicht
+                    um in diesem Raum etwas sehen zu koennen.
+    Lichtlevel  -2: Man benoetigt schon eine besondere Lichtquelle um in
+                    diesem Raum noch etwas sehen zu koennen. Solche
+                    Lichtquellen sind nichts fuer Anfaenger oder mittlere
+                    Spieler da sie schwer zu beschaffen und in der Regel
+                    auch einige Handicaps haben.
+    Lichtlevel <-2: Der Raum ist wirklich absolut stockduster und
+                    Lichtquellen die solch einen Raum ausleuchten koennen,
+                    sind ausserordentlich selten und haben immer ihre
+                    Tuecken. Diese Lichtlevel sollten nur mit Vorsicht
+                    genossen werden.
+
+SIEHE AUCH:
+    P_TOTAL_LIGHT, P_INT_LIGHT, P_PLAYER_LIGHT, P_LIGHT_MODIFIER, CannotSee()
diff --git a/doc/props/P_LIGHTDESC b/doc/props/P_LIGHTDESC
new file mode 100644
index 0000000..5898db8
--- /dev/null
+++ b/doc/props/P_LIGHTDESC
@@ -0,0 +1,34 @@
+NAME:
+    P_LIGHTDESC                   "lightdesc"                   
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    String oder Array von Strings mit Adjektiven, die das Leuchten der 
+    Lichtquelle beschreiben (z.B. leuchtend, brennend, gluehend).
+    Standardmaessig ist die Property auf "brennend" gesetzt.
+
+    Wenn ein Array angegeben wird, werden die Beschreibungen passend auf
+    die Benndauer aufgeteilt und das zur aktuell noch vorhandenen Rest-
+    Brenndauer passende Adjektiv ausgegeben. Das Array wird dabei rueck-
+    aerts durchlaufen, d.h. fuer eine frisch entzuendete Lichtquelle wird
+    der letzte Eintrag des Arrays verwendet (s. Beispiele).
+
+BEISPIELE:
+    Eine einfache Oellampe:
+
+    SetProp(P_LIGHTDESC, "angezuendet");
+
+    Eine Fackel mit mehreren Brennstadien (aus /items/fackel.c):
+
+    SetProp(P_LIGHTDESC, ({"glimmend","flackernd","leicht flackernd",
+                         "brennend","hell lodernd","frisch angezuendet"}));
+
+SIEHE AUCH:
+    Basisklassen: /std/lightsource.c
+    Properties:   P_FUEL, P_DO_DESTRUCT, P_LIGHT
+    Methoden:     AddFuel(L)
+
+LETZTE AENDERUNG:
+    22. Dez. 2015, Arathorn
diff --git a/doc/props/P_LIGHTED b/doc/props/P_LIGHTED
new file mode 100644
index 0000000..9029b02
--- /dev/null
+++ b/doc/props/P_LIGHTED
@@ -0,0 +1,8 @@
+NAME:
+    P_LIGHTED                     "lighted"                     
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Flag, ob die Lichtquelle in Betrieb ist.
diff --git a/doc/props/P_LIGHT_ABSORPTION b/doc/props/P_LIGHT_ABSORPTION
new file mode 100644
index 0000000..213120f
--- /dev/null
+++ b/doc/props/P_LIGHT_ABSORPTION
@@ -0,0 +1,15 @@
+NAME:
+    P_LIGHT_ABSORPTION                  "light_absorption"
+
+DEFINIERT IN:
+    /sys/room/description.h
+
+BESCHREIBUNG:
+    In Raeumen verteilt sich aufgrund ihrer Groesse das Licht und gerade in
+    groesseren Raeumen kann eine kleine Fackel unter Umstaenden nicht mehr
+    ausreichen den gesamten Raum auszuleuchten. In diesem Fall kann man
+    ueber diese Property das Verhalten des Lichts steuern.
+    Ein "normaler" durchschnittlicher Raum hat hier den Defaultwert 1.
+
+SIEHE AUCH:
+    P_TOTAL_LIGHT, P_INT_LIGHT, P_PLAYER_LIGHT, P_LIGHT_MODIFIER, CannotSee()
diff --git a/doc/props/P_LIGHT_MODIFIER b/doc/props/P_LIGHT_MODIFIER
new file mode 100644
index 0000000..2409c79
--- /dev/null
+++ b/doc/props/P_LIGHT_MODIFIER
@@ -0,0 +1,49 @@
+NAME:
+    P_LIGHT_MODIFIER                     "light_modifier"
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Veraendert das Lichtlevel das von einem Lebewesen wahrgenommen wird.
+    Der Wert dieser Property wird additiv in P_PLAYER_LIGHT beruecksichtigt.
+    Es ist hiermit z.B. moeglich eine Sonnenbrille zu programmieren, diese
+    veraendert ja nicht das tatsaechliche Lichtlevel, sondern verdunkelt nur
+    die Sicht.
+
+ANMERKUNG:
+    Damit NPCs in der Lage sind solche Gegenstaende richtig einzuschaetzen,
+    sollte diese Property in jedem Gegenstand der einen Light-Modifier setzt,
+    auch gesetzt sein. Das veraendern dieser Property in Spielern durch NPCs
+    oder Gegenstaende ist selbstverstaendlich genehmigungspflichtig.
+
+BEISPIELE:
+       // Ein NPC der auch in relativ dunklen Raeumen mit Lichtlevel -2
+       // noch sehen kann...
+       create_default_npc(10);
+       SetProp(P_LIGHT_MODIFIER, 3);
+
+       // Eine Sonnenbrille, die das Lichtlevel um eins senkt.
+
+       create()
+       {
+          :
+          SetProp(P_ARMOUR_TYPE, AT_GLASSES);
+          SetProp(P_LIGHT_MODIFIER, -1);
+          :
+       }
+
+       // Achtung: Falls pl Query- oder Set-Methoden auf P_LIGHT_MODIFIER hat,
+       // wird diese Methode hier furchtbar schief gehen und im besten Fall
+       // nichts veraendern. In realen Objekten empfiehlt sich zumindest eine
+       // Pruefung im Vorfeld, ob eine Query-/Set-Methode gesetzt ist.
+       protected void InformWear(object pl, int silent, int all) {
+           pl->SetProp(P_LIGHT_MODIFIER, pl->QueryProp(P_LIGHT_MODIFIER) -1);
+       }
+
+       protected void InformUnwear(object pl, int silent, int all) {
+           pl->SetProp(P_LIGHT_MODIFIER, pl->QueryProp(P_LIGHT_MODIFIER) +1);
+       }
+
+SIEHE AUCH:
+    P_TOTAL_LIGHT, P_INT_LIGHT, P_PLAYER_LIGHT, P_LIGHT_MODIFIER, CannotSee()
diff --git a/doc/props/P_LIGHT_TRANSPARENCY b/doc/props/P_LIGHT_TRANSPARENCY
new file mode 100644
index 0000000..4af4415
--- /dev/null
+++ b/doc/props/P_LIGHT_TRANSPARENCY
@@ -0,0 +1,14 @@
+NAME:
+    P_LIGHT_TRANSPARENCY             "light_transparency"
+
+DEFINIERT IN:
+    /sys/container.h
+
+BESCHREIBUNG:
+    Wieviel Licht schluckt ein Container, d.h. wieviel Licht dringt aus
+    einem Behaelter noch nach draussen. Bei Containern die _nicht_
+    transparent sind, liefert eine _query_method hier immer 999 zurueck.
+    Defaultmaessig steht diese Property auf 2.
+
+SIEHE AUCH:
+    P_TOTAL_LIGHT, P_INT_LIGHT, P_PLAYER_LIGHT, P_LIGHT_MODIFIER, CannotSee()
diff --git a/doc/props/P_LIGHT_TYPE b/doc/props/P_LIGHT_TYPE
new file mode 100644
index 0000000..07322e0
--- /dev/null
+++ b/doc/props/P_LIGHT_TYPE
@@ -0,0 +1,75 @@
+NAME:
+    P_LIGHT_TYPE                       "light_type"
+
+DEFINIERT IN:
+    /sys/thing/description.h
+
+BESCHREIBUNG:
+    Gibt an, was fuer ein Licht in einem Objekt vorherrscht. 
+    
+    Es sind verschiedene 'atomare' Lichttypen, vordefiniert:
+    LT_MISC         Unbestimmt, keine Angaben.
+
+    LT_SUN          Sonnenlicht.
+    LT_MOON         Mondlicht 
+    LT_STARS        Sternenlicht.
+    
+    LT_DIFFUSE      Indirektes Tageslicht. (z.B. im Wald)
+
+    LT_CANDLE       Kerzenlicht.
+    LT_TORCH        Fackelschein.
+    LT_OPEN_FIRE    Sonstiges offenes Feuer. (Lagerfeuer etc.)
+    
+    LT_MAGIC        Irgendeine magische Lichtquelle.
+
+    LT_GLOWING      Eine selbstleuchtende Lichtquelle.
+
+    LT_DARKNESS     Kein wirkliches Licht, aber auch Dunkelheit soll
+                    explizit gesetzt werden koennen.
+
+    In einem Objekt koennen mehrere Lichttypen gesetzt werden. Dies
+    geschieht durch logische Oder-Verknuepfungen, siehe man operators.
+
+    Wenn in einem Raum mehr als ein Lichttyp gesetzt ist, bedeutet das, 
+    normalerweise, dass mehrere Lichtquellen verschiedenen Typs im Raum 
+    sind.
+
+    Es gibt zudem noch Lichttypen, die zusammengesetzt sind:
+
+    LT_DAYLIGHT    Tageslicht (Sonne/Diffuse)
+    LT_NATURAL     Natuerliches Licht (Daylight/Sterne/Mond)
+    LT_ARTIFICIAL  Kuenstliches Licht (Magie/Feuer/Gluehen)
+    LT_FIRE        Feuer (Kerzen/Fackeln/offenes Feuer)
+
+BEISPIELE:
+    Ein Objekt soll ein geheimnisvolles Gluehen von sich geben:
+    
+    objekt->SetProp( P_LIGHT_TYPE, LT_GLOWING )
+
+    Soll ein Raum beschrieben werden, der durch Sternenlicht erhellt ist,
+    in dem aber zusaetzlich noch ein Lagerfeuer brennt, sieht die Syntax
+    folgendermassen aus:
+    
+    raum->SetProp( P_LIGHT_TYPE, LT_STARS|LT_OPEN_FIRE );
+
+    Einer brennenden Hose kann der Lichttyp fuer offenes Feuer mitgegeben 
+    werden. Es muss jedoch nicht zwingend der Lichttyp fuer magische
+    Lichtquellen benutzt werden. Es ist klar, dass es irgendwas mit Magie
+    zu tun hat, wenn brennende Spieler durch die Gegend laufen, ohne zu 
+    schreien. P_LIGHT_TYPE sollte jedoch das fassbare Licht beschreiben.
+    LT_MAGIC ist also eher eine Notloesung fuer Licht, dessen Ursache man
+    nicht erklaeren kann.
+
+
+ANMERKUNG:
+    P_LIGHT_TYPE dient ausschliesslich der Beschreibung des Lichtes, das 
+    vorherrscht. Es ist nicht verbunden mit dem Lichtsystem, und soll es
+    auch nicht werden.
+
+    Die Empfindlichkeit der Dunkelelfen gegen Sonnenlicht wird ueber diese
+    Property gesteuert. Soll ein Raum mit (P_INDOORS==0) so dunkel sein, dass
+    sie nicht in Gefahr sind, sollten nicht LT_MISC oder LT_SUN gesetzt
+    sein.
+
+SIEHE AUCH:
+    CheckLightType, /std/thing/description.h, operators
diff --git a/doc/props/P_LIQUID b/doc/props/P_LIQUID
new file mode 100644
index 0000000..1631a8e
--- /dev/null
+++ b/doc/props/P_LIQUID
@@ -0,0 +1,8 @@
+NAME:
+    P_LIQUID                      "w_max_wasserfuellmenge"      
+
+DEFINIERT IN:
+    /sys/fishing.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_LOCALCMDS b/doc/props/P_LOCALCMDS
new file mode 100644
index 0000000..5a7b515
--- /dev/null
+++ b/doc/props/P_LOCALCMDS
@@ -0,0 +1,12 @@
+NAME:
+    P_LOCALCMDS                   "localcmds"                   
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Ein Array von Arrays aller Befehle die im Spieler selbst definiert sind.
+    ({ ({ "befehl", "funktion", flag, wizlevel }) })
+    Wenn flag gesetzt ist werden nur die ersten Zeichen die auch in Befehl
+    angegeben sind mit dem Verb ueberprueft. Siehe auch
+    add_action("funktion", "befehl", 1) und AddCmd("befehl", "funktion", 1).
diff --git a/doc/props/P_LOCATION b/doc/props/P_LOCATION
new file mode 100644
index 0000000..97344d8
--- /dev/null
+++ b/doc/props/P_LOCATION
@@ -0,0 +1,19 @@
+NAME:
+    P_LOCATION                   "location"
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+    Hier kann der Spieler mit dem Befehl "ort" den Ort setzen,
+    von dem er kommt bzw. zu kommen glaubt ;)
+    Um wieder den automatisch ermittelten Ort anzuzeigen, ist
+    P_LOCATION auf 0 zu setzen.
+    
+SIEHE AUCH:
+    ort
+
+----------------------------------------------------------------------------
+Last modified: Sat Jul 01 23:16:00 2000 by Mupfel
+    
+    
diff --git a/doc/props/P_LOG_INFO b/doc/props/P_LOG_INFO
new file mode 100644
index 0000000..b6bd8dc
--- /dev/null
+++ b/doc/props/P_LOG_INFO
@@ -0,0 +1,32 @@
+NAME:
+    P_LOG_INFO                    "log_info"                    
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Wenn diese Property gesetzt ist wird jede Frage, die ein
+     Monster nicht beantworten kann, im Report-File des
+     zustaendigen Magiers geloggt.
+
+     Es ist jedoch auch moeglich, direkt einen Filenamen anzugeben.
+     Bei diesen wird dann jedoch automatisch ein /log/ vorne angehaengt.
+
+BEISPIELE:
+     SetProp(P_LOG_INFO,1);           // Alle fehlenden Infos dieses
+                                         Monsters werden in das Report-
+                                         File unter /log/report/ gelogt.
+
+     SetProp(P_LOG_INFO,"tilly/opa"); // Alle fehlenden Infos dieses
+                                         Monsters werden in das File
+                                         monster unter /log/tilly/ ge-
+                                         logt.
+BEMERKUNGEN:
+     Bei nachtraeglich angeschlossenen Monstern empfiehlt sich Variante 
+     2 der Beispiele. Ein muehsames Suchen (und Loeschen) der fehlenden 
+     Infos des Monsters im Report-File eruebrigt sich dann naemlich.
+
+SIEHE AUCH:
+     P_LOG_FILE, write_file(), log_file(), AddInfo
+
+Letzte Aenderung: 13.09.04 Tilly@MorgenGrauen
diff --git a/doc/props/P_LONG b/doc/props/P_LONG
new file mode 100644
index 0000000..04a3659
--- /dev/null
+++ b/doc/props/P_LONG
@@ -0,0 +1,29 @@
+P_LONG
+NAME:
+     P_LONG					"long"
+
+DEFINIERT IN:
+     <thing/description.h>
+
+BESCHREIBUNG:
+     Die Langbeschreibung des Objektes als String oder Closure (diese muss
+     einen String zurueckgeben). Die Langbeschreibung wird beim Untersuchen
+     des Objektes ausgegeben. Sie sollte umgebrochen sein.
+
+     Diese Property bestimmt die Ansicht des Objektes von aussen. Fuer die
+     Innen(lang)ansicht von Raeumen muss man P_INT_LONG benutzen.
+
+BEMERKUNGEN:
+     - long() ("untersuche") filtert P_LONG durch process_string()
+       -> daher sind Closures moeglich
+
+BEISPIELE:
+
+     SetProp(P_LONG, "Diese Axt ist eine furchtbare Waffe!\n");
+
+SIEHE AUCH:
+     Aehnliches:	P_SHORT, long()
+     Sonstiges:		P_INT_LONG, process_string(), break_string()
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 20:10:18 1996 by Wargon
diff --git a/doc/props/P_LONG_EMPTY b/doc/props/P_LONG_EMPTY
new file mode 100644
index 0000000..1a9cce2
--- /dev/null
+++ b/doc/props/P_LONG_EMPTY
@@ -0,0 +1,8 @@
+NAME:
+    P_LONG_EMPTY                  "w_longdesc_empty"            
+
+DEFINIERT IN:
+    /sys/fishing.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_LONG_FULL b/doc/props/P_LONG_FULL
new file mode 100644
index 0000000..3b997f6
--- /dev/null
+++ b/doc/props/P_LONG_FULL
@@ -0,0 +1,8 @@
+NAME:
+    P_LONG_FULL                   "w_longdesc_full"             
+
+DEFINIERT IN:
+    /sys/fishing.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_MAGIC b/doc/props/P_MAGIC
new file mode 100644
index 0000000..b3a0bb7
--- /dev/null
+++ b/doc/props/P_MAGIC
@@ -0,0 +1,8 @@
+NAME:
+    P_MAGIC                       "magic"                       
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Dieses Objekt ist magisch.
diff --git a/doc/props/P_MAGIC_RESISTANCE_OFFSET b/doc/props/P_MAGIC_RESISTANCE_OFFSET
new file mode 100644
index 0000000..e6120a8
--- /dev/null
+++ b/doc/props/P_MAGIC_RESISTANCE_OFFSET
@@ -0,0 +1,33 @@
+NAME:
+     P_MAGIC_RESISTANCE_OFFSET                     "mag_res_offset"                     
+
+DEFINIERT IN:
+     /sys/new_skills.h
+
+BESCHREIBUNG:
+     Mapping mit ganzzahligen Prozentwerten in 0.01%-Schritten
+     (0-10000) zur Resistenz/Empfindlichkeit gegen bestimmte
+     Magieklassen.
+
+     Die Property wird in der Methode SpellDefend() ausgewertet und
+     fuer einen auf den NPC/Spieler gesprochenen Spell werden die
+     Werte fuer all dessen Magieklassen aufaddiert. Daher sind auch
+     negative Werte fuer einzelne Magieklassen sinnvoll.
+
+     P_MAGIC_RESISTANCE_OFFSET und SpellDefend werden von den Spellbooks
+     ausgewertet. Der Einfluss ist daher abhängig vom Spellbook.
+
+BEISPIELE:
+     // Goblins
+     SetProp(P_MAGIC_RESISTANCE_OFFSET,
+               ([MT_ANGRIFF:600,         // 6% Resistenz gegen Angriff
+	         MT_ILLUSION:500,        // 6% Resistenz gegen Illusionen
+                 MT_VERWANDLUNG:-300,    // 3% Empfindlichkeit
+		 MT_HELLSICHT:-750,      // 7.5% Empfindlichkeit
+		 MT_BEHERRSCHUNG:250])); // 2.5% Resistenz
+
+SIEHE AUCH:
+     Verwandt:     SpellDefend
+     Aehnlich:     P_NOMAGIC
+
+29.Dez 2007 Gloinson
diff --git a/doc/props/P_MAILADDR b/doc/props/P_MAILADDR
new file mode 100644
index 0000000..e1b849d
--- /dev/null
+++ b/doc/props/P_MAILADDR
@@ -0,0 +1,8 @@
+NAME:
+    P_MAILADDR                    "mailaddr"                    
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     EMailadresse des Spielers.
diff --git a/doc/props/P_MAP_RESTRICTIONS b/doc/props/P_MAP_RESTRICTIONS
new file mode 100644
index 0000000..fd422c0
--- /dev/null
+++ b/doc/props/P_MAP_RESTRICTIONS
@@ -0,0 +1,39 @@
+NAME:
+    P_MAP_RESTRICTIONS                      "lib_p_map_restrictions"
+
+DEFINIERT IN:
+    /sys/rooms.h
+
+BESCHREIBUNG:
+     Mit dieser Property laesst sich beeinflussen, welche Informationen ueber
+     den Raum das Morgengrauen dem Client zukommen laesst (zur Zeit nur via
+     GMCP, aber es mag irgendwann auch andere Wege geben).
+     Beispielsweise sollen vielleicht in einem Labyrinth keine eindeutigen
+     Raum-IDs uebermittelt werden.
+
+     Als Werte duerfen alle MR_-Konstanten aus <rooms.h> verwendet werden.
+     Diese duerfen auch ver-ODER-t werden, wenn mehr als eine davon gelten
+     soll.
+
+     Moegliche Werte:
+       MR_NOUID - verhindert, dass die eindeutige Raum-ID uebertragen wird.
+       MR_NOINFO - verhindert, dass ueberhaupt irgendetwas an den Client
+                   uebermittelt wird. Damit kriegt er ggf. auch nicht mit,
+                   dass er Raum gewechselt wurde. Ist fuer Sequenzraeume
+                   gedacht. Bitte SEHR sparsam einsetzen.
+
+     Die Verwendung dieser Property sollte der Ausnahmefall sein!
+
+BEISPIEL:
+     (in einem Raum)
+     SetProp(P_MAP_RESTRICTIONS, MR_NOUID);
+
+     Nun bekommt der Client zwar die Kurzbeschreibung des Raums, die Region
+     und die sichtbaren Ausgaenge, aber keine UID mehr uebermittelt.
+
+SIEHE AUCH:
+     gmcp
+
+----------------------------------------------------------------------------
+23.02.2013, Zesstra
+
diff --git a/doc/props/P_MARRIED b/doc/props/P_MARRIED
new file mode 100644
index 0000000..768404e
--- /dev/null
+++ b/doc/props/P_MARRIED
@@ -0,0 +1,12 @@
+NAME:
+    P_MARRIED                     "married"                     
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Enthaelt einen String mit der uid des Partners
+     (sofern vorhanden)
+
+SIEHE AUCH:
+     scheidung
diff --git a/doc/props/P_MATERIAL b/doc/props/P_MATERIAL
new file mode 100644
index 0000000..5be1bef
--- /dev/null
+++ b/doc/props/P_MATERIAL
@@ -0,0 +1,79 @@
+P_MATERIAL
+NAME:
+     P_MATERIAL					"material"
+
+DEFINIERT IN:
+     <thing/material.h>
+
+BESCHREIBUNG:
+     Mapping mit prozentualen Anteilen von Materialien, aus denen ein Objekt 
+     besteht.
+
+BEMERKUNGEN:
+     Bei SetProp kann man zu Vereinfachung auch ein einzelnes Material oder 
+     ein Array aus Materialien angeben. Die Anteile werden dann 
+     gleichmaessig verteilt.
+
+BEISPIELE:
+     // 100% Eis
+     SetProp(P_MATERIAL, MAT_ICE);
+     // QueryProp(P_MATERIAL) -> ([MAT_ICE:100])
+
+     // 50% Eis, 50% Misc-Holz
+     SetProp(P_MATERIAL, ({ MAT_ICE, MAT_MISC_WOOD }));
+     // QueryProp(P_MATERIAL) -> ([MAT_ICE:50, MAT_MISC_WOOD: 50])
+
+     // 60% Eis, 40% Misc-Holz
+     SetProp(P_MATERIAL, ([ MAT_ICE: 60, MAT_MISC_WOOD: 40 ]));
+
+     // 100% Papier
+     SetProp(P_MATERIAL, MAT_PAPER);
+     // QueryProp(P_MATERIAL) -> ([MAT_PAPER:100])
+
+     // 50% Silber, 50% Gold
+     SetProp(P_MATERIAL, ({MAT_SILVER, MAT_GOLD}))
+     // QueryProp(P_MATERIAL) -> ([MAT_SILVER:50,MAT_GOLD:50])
+
+     // ein weiser Schmied, der was daraus macht:
+     int i;
+     string *mat, mname, mgroup;
+     mat=m_indices(ob->QueryProp(P_MATERIAL));
+     i=sizeof(mat);
+
+     write("Der Schmied sagt: "+ob->Name(WER)+" besteht aus ...\n");
+     while(i--) {
+      // den Namen erkennen/aussprechen:
+      // Materialien werden allgemein ganz schlecht erkannt (zu 5%), aber
+      // alles aus Metall wird zu +100% gut erkannt ...
+      mname=MATERIALDB->MaterialName(mat[i], WER,
+	     ({5, ([MATERIAL_SYMMETRIC_RECOGNIZABILITY:
+			({MATGROUP_METAL, 100})])}));
+
+      // und nur Metalle analysieren ...
+      if(MATERIALDB->MaterialGroup(([mat[i]:100]),MATGROUP_METAL)>=100) {
+       int j;
+       string *mgr;
+       mgr=MATERIALDB->GetMatMembership(mat[i]);
+       j=sizeof(mgr);
+       mgroup=" gehoert zu ";
+       while(j--) {
+        mgroup+=MATERIALDB->GroupName(mgr[j]);
+        if(j>0) mgroup+=", ";
+       }
+      } else mgroup=" kenne ich nicht";
+      printf("%-12.12s: %s\n",mname, mgroup);
+     }
+
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: /sys/thing/material.h
+     Methoden:    QueryMaterial(), QueryMaterialGroup(), MaterialList(),
+     Listen:	  AllMaterials(), AllGroups(), Dump()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+     Sonstiges:	  P_MATERIAL_KNOWLEDGE
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_MATERIAL_KNOWLEDGE b/doc/props/P_MATERIAL_KNOWLEDGE
new file mode 100644
index 0000000..c954083
--- /dev/null
+++ b/doc/props/P_MATERIAL_KNOWLEDGE
@@ -0,0 +1,39 @@
+P_MATERIAL_KNOWLEDGE
+NAME:
+     P_MATERIAL_KNOWLEDGE				"material_knowledge"
+
+DEFINIERT IN:
+     <thing/material.h>
+
+BESCHREIBUNG:
+     Mapping, Closure oder Integer mit Regeln zur Materialienerkennung. Die
+     Regeln sind in "man materialerkennung" zu lesen.
+
+     Diese werden bei Angabe eines Spielerobjektes in MaterialList() oder
+     MaterialName() an diesem abgefragt und hat Einfluss darauf, ob ein
+     Material genau, generell oder gar nicht erkannt wird.
+
+     In den einzelnen Rassenshells sind Defaultwerte dafuer angegeben.
+
+
+BEISPIELE:
+     // Erkennungsbonus auf diverse Materialgruppen und
+     // Erkennungsbonus/-malus auf biologische/nichtbiologische Materialien
+     SetProp(P_MATERIAL_KNOWLEDGE,
+	([MATGROUP_WOOD:20,
+	  MATGROUP_METAL:20,
+	  MATGROUP_ELEMENTAL:20,
+	  MATGROUP_CLOTH:20,
+	  MATERIAL_SYMMETRIC_RECOGNIZABILITY: ({MATGROUP_BIO,5})]));
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Methoden:    QueryMaterial(), QueryMaterialGroup(), MaterialList(),
+     Listen:	  AllMaterials(), AllGroups(), Dump()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_MAX_ALCOHOL b/doc/props/P_MAX_ALCOHOL
new file mode 100644
index 0000000..10a64c3
--- /dev/null
+++ b/doc/props/P_MAX_ALCOHOL
@@ -0,0 +1,21 @@
+NAME:
+	P_MAX_ALCOHOL			"max_alcohol"
+
+DEFINIERT IN:
+	/sys/living/life.h
+
+BESCHREIBUNG:
+	Numerischer Wert fuer den maximalen Grad der Betrunkenheit eines
+	Lebewesens.
+
+ANMERKUNGEN:
+	Der Wert von P_MAX_ALCOHOL ist bei den einzelnen Rassen verschieden,
+	ebenso wie der fuer P_ALCOHOL_DELAY. Die genauen Werte stehen in den
+	Rassen-Shells (/std/shells).
+
+SIEHE AUCH:
+	P_ALCOHOL, P_ALCOHOL_DELAY, drink_alcohol,
+	P_MAX_FOOD, P_MAX_DRINK
+
+----------------------------------------------------------------------------
+Last modified: Tue Dec 18 12:16:10 2001 by Patryn
diff --git a/doc/props/P_MAX_DRINK b/doc/props/P_MAX_DRINK
new file mode 100644
index 0000000..940eddc
--- /dev/null
+++ b/doc/props/P_MAX_DRINK
@@ -0,0 +1,21 @@
+NAME:
+	P_MAX_DRINK			"max_drink"
+
+DEFINIERT IN:
+	/sys/living/life.h
+
+BESCHREIBUNG:
+	Numerischer Wert fuer die maximale Saettigung eines Lebewesens mit
+	Getraenken.
+
+ANMERKUNGEN:
+	Der Wert von P_MAX_DRINK ist bei den einzelnen Rassen verschieden,
+	ebenso wie der fuer P_DRINK_DELAY. Die genauen Werte stehen in den
+	Rassen-Shells (/std/shells).
+
+SIEHE AUCH:
+	P_DRINK, P_DRINK_DELAY, drink_soft,
+	P_MAX_FOOD, P_MAX_ALCOHOL
+
+----------------------------------------------------------------------------
+Last modified: Tue Dec 18 12:16:10 2001 by Patryn
diff --git a/doc/props/P_MAX_FOOD b/doc/props/P_MAX_FOOD
new file mode 100644
index 0000000..5f49cfe
--- /dev/null
+++ b/doc/props/P_MAX_FOOD
@@ -0,0 +1,20 @@
+NAME:
+	P_MAX_FOOD			"max_food"
+
+DEFINIERT IN:
+	/sys/living/life.h
+
+BESCHREIBUNG:
+	Numerischer Wert fuer die maximale Saettigung eines Lebewesens.
+
+ANMERKUNGEN:
+	Der Wert von P_MAX_FOOD ist bei den einzelnen Rassen verschieden, 
+	ebenso wie der fuer P_FOOD_DELAY. Die genauen Werte stehen in den
+	Rassen-Shells (/std/shells).
+
+SIEHE AUCH:
+	P_FOOD, P_FOOD_DELAY, eat_food,
+	P_MAX_DRINK, P_MAX_ALCOHOL
+
+----------------------------------------------------------------------------
+Last modified: Tue Dec 18 12:16:10 2001 by Patryn
diff --git a/doc/props/P_MAX_HANDS b/doc/props/P_MAX_HANDS
new file mode 100644
index 0000000..96b1ae8
--- /dev/null
+++ b/doc/props/P_MAX_HANDS
@@ -0,0 +1,27 @@
+P_MAX_HANDS
+NAME:
+     P_MAX_HANDS                   "max_hands"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+     Anzahl der Haende, die ein Wesen hat.
+
+BEMERKUNGEN:
+     An dieser Propertie sollte bei Spielern nur im Rahmen der
+     Gilden 'gespielt' werden.
+
+     Fuer Magier, die in ihren Npc gerne alle Properties setzen,
+     gilt folgendes:
+
+                  Setzt diese Propertie NIE auf 0 !
+
+     Ohne Haende wird kein Npc irgendeinen Spieler angreifen koennen.
+
+SIEHE AUCH:
+     P_HANDS, P_HANDS_USED_BY
+     P_USED_HANDS, P_FREE_HANDS
+     UseHands, FreeHands
+
+1.Feb.2004 Gloinson
diff --git a/doc/props/P_MAX_HP b/doc/props/P_MAX_HP
new file mode 100644
index 0000000..13a84c4
--- /dev/null
+++ b/doc/props/P_MAX_HP
@@ -0,0 +1,14 @@
+NAME:
+    P_MAX_HP                      "max_hp"
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Maximale Anzahl der Lebenspunkte.
+
+SIEHE AUCH:
+     Props:	P_HP
+     Verwandt:	UpdateAttributes()
+
+21.April 2006 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_MAX_OBJECTS b/doc/props/P_MAX_OBJECTS
new file mode 100644
index 0000000..a76f0d1
--- /dev/null
+++ b/doc/props/P_MAX_OBJECTS
@@ -0,0 +1,22 @@
+NAME:
+    P_MAX_OBJECTS                  "max_objects"                  
+
+DEFINIERT IN:
+    /sys/container.h
+
+BESCHREIBUNG:
+     Maximale Anzahl an Objekten, die in einen Container passen.
+     P_MAX_OBJECTS sollte im Normfall zwischen 0 - 100 liegen.
+
+BEMERKUNGEN:
+     Soll es sich um einen herausragenden Container handeln, der zum
+     Beispiel das Gewicht um mehr als 50% reduziert, sollte P_MAX_OBJECTS
+     einen kleineren Wert haben. Das Verhaeltnis von P_MAX_WEIGHT,
+     P_WEIGHT_PERCENT und dieser Property sollte stimmen.
+
+BEISPIELE:
+     Als Beispiel sollte man sich das Postpaket ansehen und sich an dessen
+     Werten orientieren (/p/service/loco/obj/parcel).
+
+SIEHE AUCH:
+     P_MAX_WEIGHT, P_WEIGHT_PERCENT, P_LIGHT_TRANSPARENCY, container
diff --git a/doc/props/P_MAX_PASSENGERS b/doc/props/P_MAX_PASSENGERS
new file mode 100644
index 0000000..b0bc6a5
--- /dev/null
+++ b/doc/props/P_MAX_PASSENGERS
@@ -0,0 +1,9 @@
+NAME:
+    P_MAX_PASSENGERS              "maxpass"                     
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+     Numerischer Wert fuer die maximale Anzahl von Wesen in dem Transporter.
+     0 bedeutet unbeschaenkte Spielerzahl.
diff --git a/doc/props/P_MAX_POISON b/doc/props/P_MAX_POISON
new file mode 100644
index 0000000..35b8e9b
--- /dev/null
+++ b/doc/props/P_MAX_POISON
@@ -0,0 +1,8 @@
+NAME:
+    P_MAX_POISON                  "max_poison"                  
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Maximale Vergiftung
diff --git a/doc/props/P_MAX_SP b/doc/props/P_MAX_SP
new file mode 100644
index 0000000..307aaf5
--- /dev/null
+++ b/doc/props/P_MAX_SP
@@ -0,0 +1,14 @@
+NAME:
+    P_MAX_SP                      "max_sp"
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Maximale Anzahl der Magiepunkte.
+
+SIEHE AUCH:
+     Props:	P_SP
+     Verwandt:	UpdateAttributes()
+
+21.April 2006 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_MAX_WEIGHT b/doc/props/P_MAX_WEIGHT
new file mode 100644
index 0000000..7e2f16b
--- /dev/null
+++ b/doc/props/P_MAX_WEIGHT
@@ -0,0 +1,19 @@
+NAME:
+    P_MAX_WEIGHT                  "max_weight"                  
+
+DEFINIERT IN:
+    /sys/container.h
+
+BESCHREIBUNG:
+     Maximales Gewicht in Gramm, das in dem Container verstaut werden
+     kann.
+
+BEMERKUNGEN:
+     Das Verhaeltnis von P_WEIGHT_PERCENT, P_MAX_OBJECTS und dieser Property
+     sollte stimmen. Eine feste Grenze gibt es nicht. Als Beispiel kann 
+     das Postpaket von Loco unter /p/servive/loco/obj herangezogen werden.
+     Bessere Werte als in diesem sollte es nicht, und wenn doch nur gut be-
+     gruendet, geben.
+
+SIEHE AUCH:
+     P_WEIGHT_PERCENT, P_MAX_OBJECTS, P_LIGHT_TRANSPARENCY, container
diff --git a/doc/props/P_MESSAGE_BEEP b/doc/props/P_MESSAGE_BEEP
new file mode 100644
index 0000000..9e40fa5
--- /dev/null
+++ b/doc/props/P_MESSAGE_BEEP
@@ -0,0 +1,19 @@
+NAME:
+    P_MESSAGE_BEEP                        "message_beep"
+
+DEFINIERT IN:
+    /sys/player/comm.h
+
+BESCHREIBUNG:
+     Wertebereich: int=0..3600 (Sekunden)
+     Wenn gesetzt wird in der Kommunikation des Spielers in den angegebenen
+     Zeitraeumen ein Signalton ausgegeben. Wird in player/comm.c in comm_beep()
+     verarbeitet.
+     Ausgabe erfolgt nur, wenn P_VISUALBELL nicht gesetzt ist.
+     Wird im Spielerobjekt gespeichert!
+
+SIEHE AUCH:
+     klingelton, ton, P_VISUALBELL, P_MESSAGE_LAST_BEEP
+
+LETZTE AENDERUNG:
+   16. Mai 2007  Ennox
diff --git a/doc/props/P_MESSAGE_PREPEND b/doc/props/P_MESSAGE_PREPEND
new file mode 100644
index 0000000..ee44dda
--- /dev/null
+++ b/doc/props/P_MESSAGE_PREPEND
@@ -0,0 +1,18 @@
+NAME:
+    P_MESSAGE_PREPEND                        "message_prepend"
+
+DEFINIERT IN:
+    /sys/player/comm.h
+
+BESCHREIBUNG:
+     Wertebereich: int = 0,1
+     Wenn vom Ziel eingeschaltet, wird von teile mit (_tell) und sag (_communicate) das 
+     BS_PREPEND_INDENT Flag fuer break_string genutzt, um den Indent (Sender) ggf.
+     vor dem Textblock anzuzeigen.
+     Wird im Spielerobjekt gespeichert!
+
+SIEHE AUCH:
+     grafik aus, break_string, senderwiederholung
+
+LETZTE AENDERUNG:
+   16. Mai 2007  Ennox
diff --git a/doc/props/P_MIN_STOCK b/doc/props/P_MIN_STOCK
new file mode 100644
index 0000000..d896fb3
--- /dev/null
+++ b/doc/props/P_MIN_STOCK
@@ -0,0 +1,37 @@
+NAME:
+	P_MIN_STOCK			"min_stock"
+
+DEFINIERT IN:
+	/sys/bank.h
+
+BESCHREIBUNG:
+	P_MIN_STOCK enthaelt die Anzahl an Objekten, die ein Lager eines
+	Ladens minimal behaelt. Standardmaessig entspricht dies 20 Objekten
+	und sollte auch nicht wesentlich erhoeht werden. Nur fuer
+	Anfaengergebiete waeren Werte zwischen 20 und 60 akzeptabel. In die
+	Berechnung der Anzahl von Objekten gehen keine Objekte ein, die im
+	Laden mittels AddFixedObject() staendig verfuegbar gemacht worden
+	sind und auch keine Objekte, die per AddItem() im Lager hinzugefuegt
+	wurden und nach jedem Reset aufgefrischt werden.
+	Bei jedem Reset wird nun aus Speicher- und Laggruenden das Lager um
+	eine bestimmte Prozentzahl an Objekten dezimiert. Entscheidend
+	dafuer ist der Wert in der Property P_STORE_CONSUME.
+	Beide hier erwaehnten Properties sollten ueberigens nur mittels
+	QueryProp/SetProp ausgelesen bzw. veraendert werden.
+
+BEISPIEL:
+	In '/std/store.c' befindet sich bereits ein gutes Beispiel, da dort
+	der Standardwert von 20 Objekten bereitgestellt wird:
+	  create()
+	  { ...
+	    SetProp(P_MIN_STOCK,20);
+	  }
+	Diesen Wert kann man in einem davon abgeleiteten eigenen Lager
+	natuerlich auch veraendern.
+
+SIEHE AUCH:
+	P_STORE_CONSUME, SetStorageRoom(), /std/store.c, /std/shop.c
+	AddItem(), RemoveItem(), AddFixedObject(), RemoveFixedObject()
+
+----------------------------------------------------------------------------
+Last modified: 19-Jun-2015, Arathorn 
diff --git a/doc/props/P_MMSGIN b/doc/props/P_MMSGIN
new file mode 100644
index 0000000..cd8e84e
--- /dev/null
+++ b/doc/props/P_MMSGIN
@@ -0,0 +1,9 @@
+NAME:
+    P_MMSGIN                      "mmsgin"                      
+
+DEFINIERT IN:
+    /sys/player/moving.h
+
+BESCHREIBUNG:
+     String mit der Meldung, die beim Verlassen eines Raumes mit M_TPORT
+     an die uebrigen Anwesenden ausgegeben wird.
diff --git a/doc/props/P_MMSGOUT b/doc/props/P_MMSGOUT
new file mode 100644
index 0000000..defd09f
--- /dev/null
+++ b/doc/props/P_MMSGOUT
@@ -0,0 +1,9 @@
+NAME:
+    P_MMSGOUT                     "mmsgout"                     
+
+DEFINIERT IN:
+    /sys/player/moving.h
+
+BESCHREIBUNG:
+     String mit der Meldung, die beim Betreten eines Raumes mit M_TPORT
+     an die uebrigen Anwesenden ausgegeben wird.
diff --git a/doc/props/P_MSGIN b/doc/props/P_MSGIN
new file mode 100644
index 0000000..cf14a91
--- /dev/null
+++ b/doc/props/P_MSGIN
@@ -0,0 +1,9 @@
+NAME:
+    P_MSGIN                       "msgin"                       
+
+DEFINIERT IN:
+    /sys/player/moving.h
+
+BESCHREIBUNG:
+     String mit der Meldung, die beim Betreten eines Raumes mit M_GO
+     an die uebrigen Anwesenden ausgegeben wird.
diff --git a/doc/props/P_MSGOUT b/doc/props/P_MSGOUT
new file mode 100644
index 0000000..0587afb
--- /dev/null
+++ b/doc/props/P_MSGOUT
@@ -0,0 +1,9 @@
+NAME:
+    P_MSGOUT                      "msgout"                      
+
+DEFINIERT IN:
+    /sys/player/moving.h
+
+BESCHREIBUNG:
+     String mit der Meldung, die beim Verlassen eines Raumes mit M_GO
+     an die uebrigen Anwesenden ausgegeben wird.
diff --git a/doc/props/P_MSG_PROB b/doc/props/P_MSG_PROB
new file mode 100644
index 0000000..d520067
--- /dev/null
+++ b/doc/props/P_MSG_PROB
@@ -0,0 +1,19 @@
+NAME:
+    P_MSG_PROB                    "msg_prob"                    
+
+DEFINIERT IN:
+    /sys/room/description.h
+
+BESCHREIBUNG:
+     Parameter fuer die Wartezeit in Sekunden bis zur naechsten Ausgabe
+     einer Raumnachricht.
+     Wird in AddRoomMessage() explizit mitgesetzt. Koennte natuerlich von
+     einer Nachrichtenmethode auch regelmaessig geaendert werden, um
+     mehr Zufall in die Intervalle zu bringen.
+
+SIEHE AUCH:
+     LFuns:    AddRoomMessage()
+     Props:    P_ROOM_MSG, P_MSG_PROB
+     Verwandt: call_out()
+
+2.Feb 2016 Gloinson
diff --git a/doc/props/P_MUD_NEWBIE b/doc/props/P_MUD_NEWBIE
new file mode 100644
index 0000000..bebb77f
--- /dev/null
+++ b/doc/props/P_MUD_NEWBIE
@@ -0,0 +1,17 @@
+NAME:
+    P_MUD_NEWBIE                      "_lib_mud_newbie" 
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+    Der Spieler hat bei Erstellung des Charakters angegeben, noch nie in einem
+    Mud gespielt zu haben. In diesem Fall enthaelt die Property den
+    Zeitstempel der Charaktererstellung.
+
+    ACHTUNG: Diese Prop wird nicht gespeichert, d.h. nachdem ein solcher
+    Spieler "ende" gemacht hat oder ein Reboot erfolgte, ist diese Information
+    verloren.
+
+SIEHE AUCH:
+
diff --git a/doc/props/P_MURDER_MSG b/doc/props/P_MURDER_MSG
new file mode 100644
index 0000000..85bbf81
--- /dev/null
+++ b/doc/props/P_MURDER_MSG
@@ -0,0 +1,63 @@
+P_MURDER_MSG
+
+NAME:
+     P_MURDER_MSG			"murder_msg"
+
+DEFINIERT IN:
+     /sys/properties.h
+
+BESCHREIBUNG:
+     In dieser Property kann man einen String oder eine Closure ablegen
+     dessen Wert bzw. deren Resultat beim Tod des NPCs auf dem
+     Moerder-Kanal erscheint.
+     Normalerweise ist die Property nicht gesetzt, woraufhin zufaellig
+     eine Meldung generiert wird.
+
+     Ob der Tod eines NPCs auf dem Moerder-Kanal erscheint, haengt davon ab,
+     wie oft und welche Art von NPCs in der letzten Zeit getoetet wurden. Zum
+     Beispiel ist es eher selten, dass ein schwacher NPC auf dem Kanal
+     erscheint, wenn kuerzlich viele starke NPCs getoetet wurden. Allerdings
+     kann man auf diese Regelung mittels der Property P_FORCE_MURDER_MSG
+     Einfluss nehmen.
+
+     Wird in einen String der Platzhalter %s eingefuegt, so erscheint an der
+     Stelle spaeter der Name des Moerders.
+
+BEISPIELE:
+     // Zum Beispiel koennte man ja bei einer Ratte, die getoetet wird,
+     // folgendes auf dem Moerder-Kanal ausgeben lassen:
+     SetProp(P_MURDER_MSG,
+       "Ratten aller MUDs, vereinigt euch gegen %s!");
+
+
+     // Um auch mal eine Closure zu zeigen: die Ratte könnte auch ihre
+     // Meldung erst bei ihrem Tod erstellen lassen:
+     private string moerder_meldung() {
+       return ({"Achweh!", "Au!", "Weia!"})[random(3)];
+     }
+
+     SetProp(P_MURDER_MSG, #'moerder_meldung);
+
+BEMERKUNGEN:
+     - P_NOCORPSE:
+       Ist in dem Npc die Property P_NOCORPSE gesetzt, so wird die
+       Moerdermeldung nicht auf dem Kanal gesendet, da diese Ausgabe ueber
+       /std/corpse laeuft.
+       Will man dennoch eine Meldung, so sollte man /std/corpse im die()
+       selbst clonen, daran Identify(this_object()) rufen und das geclonte
+       Objekt wieder entsorgen.
+
+     - Closures:
+       Closures bieten sich an, wenn ein zentrales Objekt für mehrere NPCs
+       bestimmte Moerdermeldungen generieren soll. Dann muss nur noch bei
+       den NPCs die Closure, die auf die erstellende Methode zeigt gesetzt
+       werden.
+
+SIEHE AUCH:
+     Tod:		die(L)
+     Verwandt:		P_FORCE_MURDER_MSG
+     Todesmeldungen:	P_KILL_NAME, P_KILL_MSG, P_DIE_MSG
+			P_ZAP_MSG, P_ENEMY_DEATH_SEQUENCE
+     Sonstiges:		P_CORPSE, P_NOCORPSE, /std/corpse.c
+
+30. Mai 2006, Gloinson
\ No newline at end of file
diff --git a/doc/props/P_M_ATTR_MOD b/doc/props/P_M_ATTR_MOD
new file mode 100644
index 0000000..8a79717
--- /dev/null
+++ b/doc/props/P_M_ATTR_MOD
@@ -0,0 +1,42 @@
+NAME:
+    P_M_ATTR_MOD                  "magic_attributes_modifier"
+
+DEFINIERT IN:
+    /sys/living/attributes.h
+
+BESCHREIBUNG:
+    Mapping, das die Attribute des Spielers veraendert, der diese Ruestung
+    bzw. Waffe traegt bzw. benutzt.
+
+    Zu beachten:
+    P_M_ATTR_MOD kann problemlos durch ein SetProp() gesetzt werden. Es wird
+    nur dann beruecksichtigt, wenn die Ruestung/Waffe getragen/benutzt wird.
+    Beim Tragen/Ausziehen/Zuecken/Wegstecken wird im Spieler automatisch
+    UpdateAttributes() aufgerufen.
+
+    Fuer Krankheiten etc. oder Objekte, deren *Besitz* allein schon die
+    Attribute veraendern sollen, verwendet man besser P_X_ATTR_MOD.
+
+    P_X_ATTR_MOD und P_M_ATTR_MOD duerfen einen gemeinsamen kumulierten
+    positiven Grenzwert nicht ueberschreiten. Dieser Grenzwert,
+    CUMULATIVE_ATTR_LIMIT, ist in /sys/living/attributes.h definiert.
+
+BEMERKUNGEN:
+    Die Werte sollten moeglichst nicht dynamisch geaendert werden.
+    Wenn doch, muss mit TestLimitViolation() am Spieler auf Validitaet
+    geprueft und ggf. mit UpdateAttributes() an ihm upgedatet werden.
+
+BEISPIELE:
+    // Dem Spieler, der das Objekt benutzt, wird 2 von A_INT abgezogen und
+    // dafuer 1 auf A_STR aufaddiert.
+    SetProp(P_M_ATTR_MOD, ([A_INT:-2, A_STR:1]) );
+
+SIEHE AUCH:
+    QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),
+    SetAttribute(), SetRealAttribute(), UpdateAttributes(),
+    SetTimedAttrModifier(), QueryTimedAttrModifier(),
+    DeleteTimedAttrModifier(),
+    P_X_HEALTH_MOD, P_M_HEALTH_MOD, P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS,
+    P_TIMED_ATTR_MOD, P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c
+
+02.02.2016, Gloinson
diff --git a/doc/props/P_M_HEALTH_MOD b/doc/props/P_M_HEALTH_MOD
new file mode 100644
index 0000000..a4c89e6
--- /dev/null
+++ b/doc/props/P_M_HEALTH_MOD
@@ -0,0 +1,40 @@
+NAME:
+     P_M_HEALTH_MOD                  "magic_health_modifier"
+
+DEFINIERT IN:
+     /sys/living/attributes.h
+
+BESCHREIBUNG:
+     Mapping, mit dem die maximalen Lebenspunkte und Magiepunkte eines 
+     Spielers veraendert werden, der diese Ruestung/Waffe traegt/benutzt. Im 
+     Gegensatz zu P_M_ATTR_MOD erfolgt hier jedoch keine Blockade.
+
+     Zu beachten: P_M_HEALTH_MOD kann problemlos durch ein SetProp() gesetzt 
+     werden, es wird nur beruecksichtigt, wenn die Ruestung/Waffe 
+     getragen/benutzt wird. Beim tragen/ausziehen/zuecken/wegstecken wird im 
+     Spieler automatisch UpdateAttributes() aufgerufen.
+
+     Fuer Krankheiten etc. verwendet man besser die Property P_X_HEALTH_MOD.
+
+     Bitte beachten: Die positiven Modifier werden nicht mehr 1:1 auf die
+     Lebens- und Magiepunkte draufaddiert. Stattdessen wird nur noch ein 
+     gewisser Teil dort hinzuaddiert, der mit groesserer Menge von Punkten
+     zunimmt, und im unteren Bereich grob dem Wert entspricht. Das 
+     theoretische Maximum ist insgesamt 149.
+
+BEMERKUNGEN:
+     Die Werte sollten moeglichst nicht dynamisch geaendert werden.
+     Wenn doch, muss mit TestLimitViolation() am Spieler auf Validitaet 
+     geprueft werden und mit UpdateAttributes() an ihm ggf. upgedatet.
+
+BEISPIEL:
+     SetProp(P_M_HEALTH_MOD,([P_HP:5,P_SP:-5]));
+     // Dem Spieler, der das Objekt benutzt, wird P_MAX_HP um 5 erhoeht und
+     // P_MAX_SP um 5 erniedrigt.
+
+SIEHE AUCH:
+     P_X_HEALTH_MOD, P_X_ATTR_MOD, P_M_ATTR_MOD, balance
+
+LETZTE AeNDERUNG:
+    Fre,11.05.2007, 00:20 von Humni
+
diff --git a/doc/props/P_NAME b/doc/props/P_NAME
new file mode 100644
index 0000000..f609cea
--- /dev/null
+++ b/doc/props/P_NAME
@@ -0,0 +1,65 @@
+P_NAME
+
+NAME:
+     P_NAME "name"
+
+DEFINIERT IN:
+     <thing/description.h>
+
+BESCHREIBUNG:
+     In dieser Property wird der Name eines Objektes vermerkt. Wenn der Name
+     regelmaessig dekliniert wird, reicht ein einfacher String. Wird der
+     Name unregelmaessig dekliniert, so kann man ein Array von vier Strings
+     mit dem Namen im Nominativ, Genitiv, Dativ und Akkusativ (in dieser
+     Reihenfolge) angeben.
+
+     Die Funktion name() behandelt recht viele Sonderfaelle; man sollte den
+     Namen also erst einmal in der Form eines einzelnen Strings pruefen.
+
+     Eine Sonderrolle nehmen Unit-Objekte ein: Hier kann man einen Namen
+     fuer den Singular und einen Namen fuer den Plural vergeben.
+
+     Setzt man P_NAME eines Unit-Objekts auf einen einfachen String, so wird
+     dieser als Name sowohl im Singular als auch im Plural verwendet.
+
+     Uebergibt man ein Array von Strings, so wird der erste Eintrag fuer den
+     Singular und der zweite Eintrag fuer den Plural benutzt.
+
+     Bei Unit-Objekten ist es nicht moeglich, auch noch zwischen den
+     verschiedenen Faellen zu unterscheiden.
+
+BEMERKUNGEN:
+     Diese Property sollte nur den reinen Namen enthalten; will man dem
+     Namen noch Adjektive voranstellen, so sollte man dies mit P_NAME_ADJ
+     bewerkstelligen, also statt
+
+     SetProp(P_NAME, ({ "alter Hut", "alten Huts",
+                        "alten Hut", "alten Hut" }) );
+
+     besser
+
+     SetProp(P_NAME, "Hut");
+     SetProp(P_NAME_ADJ, "alt");
+
+     Bei Lebewesen wird lower_case(P_NAME) (bei Arrays das erste Element 
+     daraus) automatisch als LivingName gesetzt.
+
+BEISPIELE:
+     Ein regelmaessig deklinierbarer Name:
+
+     SetProp(P_NAME, "Arkshat");
+
+     Hier ein Beispiel fuer einen unregelmaessig deklinierbaren Namen:
+
+     SetProp(P_NAME, ({ "Drache", "Drachen", "Drachen", "Drachen" }));
+
+     Und schliesslich der Name eines allseits bekannten Unit-Objektes:
+
+     SetProp(P_NAME, ({ "Muenze", "Muenzen" }));
+
+SIEHE AUCH:
+     /std/thing/description.c, name(), P_NAME_ADJ, set_living_name(),
+     find_living(), find_livings()
+
+----------------------------------------------------------------------------
+Last modified: 19. Okt. 2015, Arathorn. 
diff --git a/doc/props/P_NAME_ADJ b/doc/props/P_NAME_ADJ
new file mode 100644
index 0000000..2bf15e6
--- /dev/null
+++ b/doc/props/P_NAME_ADJ
@@ -0,0 +1,55 @@
+P_NAME_ADJ
+
+NAME:
+     P_NAME_ADJ "name_adj"
+
+DEFINIERT IN:
+     <thing/description.h>
+
+BESCHREIBUNG:
+     Diese Property enthaelt ein oder mehrere Adjektive in Form eines Arrays
+     von Strings. Es ist nur der Wortstamm anzugeben! Die Adjektive werden
+     von der Funktion name() dekliniert und vor den Namen gesetzt, wirken
+     also als Aufzaehlung von Adjektiven vor dem Namen.
+
+     Die hier angegebenen Adjektive dienen nur zur Ausgabe! Soll sich das
+     Objekt auch ueber Adjektive ansprechen lassen, muss man diese mit
+     AddAdjective() uebergeben.
+
+     Soll das Objekt nur ein einzelnes Namensadjektiv besitzen, kann man dem
+     SetProp()-Aufruf auch einen String uebergeben; gespeichert wird die
+     Property aber in jedem Fall als Array.
+
+     Wenn ein Adjektiv unregelmaessig ist, kann man die vier Faelle auch
+     als Array uebergeben. Man muss dann aber Arrays schachteln, damit von den
+     mehrfachen Adjektiven unterschieden werden kann.
+	
+BEISPIELE:
+     SetProp(P_NAME, "Hut");
+     SetProp(P_NAME_ADJ, "alt");
+
+     name(WESSEN,1)      => "des alten Huts"
+
+
+     // Zwei Adjektive, gleichrangig zu Substantiv
+     SetProp(P_NAME_ADJ, ({"alt", "gammelig"}));
+
+     name(WESSEN,1)      => "des alten, gammeligen Huts"
+
+
+     // Zwei Adjektive, erstes ist Attribut zu zweitem
+     falsch:  SetProp(P_NAME_ADJ, ({"gruen", "gestreift"}));
+              name(WESSEN,1)      => "des gruenen, gestreiften Huts"
+     richtig: SetProp(P_NAME_ADJ, ({"gruen gestreift"}));
+              name(WESSEN,1)      => "des gruen gestreiften Huts"
+
+     // Unregelmaessiges Adjektiv
+     SetProp( P_NAME_ADJ,({({"rosa","rosa","rosa","rosa"})});
+              name(WESSEN,1)         => "des rosa Huts"
+     // Ohne inneres Array haette man 4 mal rosa als Adjektiv
+     // => "des rosaen, rosaen, rosaen, rosaen Huts"
+
+SIEHE AUCH:
+     /std/thing/description.c, name(), P_NAME, DeclAdj()
+
+23.April 2007 Rumata
diff --git a/doc/props/P_NEEDED_QP b/doc/props/P_NEEDED_QP
new file mode 100644
index 0000000..e38f9eb
--- /dev/null
+++ b/doc/props/P_NEEDED_QP
@@ -0,0 +1,15 @@
+NAME:
+    P_NEEDED_QP                   "needed_qp"                   
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     APs, die man fuer den Seherstatus braucht
+
+     Diese Property ist mittlerweile obsolet. Die
+     aktuell geltenden Seher-Anforderungen stehen in
+     /secure/lepmaster.h
+
+LETZTE AENDERUNG:
+     2006-09-30, Zook.
\ No newline at end of file
diff --git a/doc/props/P_NETDEAD_ENV b/doc/props/P_NETDEAD_ENV
new file mode 100644
index 0000000..b2d6317
--- /dev/null
+++ b/doc/props/P_NETDEAD_ENV
@@ -0,0 +1,24 @@
+NAME:
+    P_NETDEAD_ENV                  "netdead_env"
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+    Diese Property kann in netztoten Spielern abgefragt werden,
+		um herauszufinden, in welchem Raum sie netztot geworden sind.
+
+		Es wird der selbe Wert zurueckgegeben, den die Mudlib benutzen
+		wuerde, um die Spieler beim reconnect wieder an den richtigen
+		ort zu bringen. Das ist normalerweise das Raumobjekt oder der
+		Dateiname des Raums (zum Beispiel so dieser ein clone war).
+
+		Bei nicht netztoten Spielern wird 0 zurueckgegeben.
+
+BEMERKUNGEN:
+    Diese Property ist read-only.
+
+SIEHE AUCH:
+    P_NETDEAD_INFO
+
+2009-08-04 Rumata
diff --git a/doc/props/P_NETDEAD_INFO b/doc/props/P_NETDEAD_INFO
new file mode 100644
index 0000000..83c36fd
--- /dev/null
+++ b/doc/props/P_NETDEAD_INFO
@@ -0,0 +1,91 @@
+NAME:
+    P_NETDEAD_INFO                "netdead_info"                
+
+DEFINIERT IN:
+    /sys/player.h
+
+BESCHREIBUNG:
+     Wird im Raum X gesetzt und wirkt nur, falls dieser Raum ein '#' im
+     object_name() hat (normale Clones, zB "/room/void#10153018").
+     
+     Bei Einschlafen eines Spielers in diesem Raum werden die Werte aus
+     der Property im Spieler gespeichert (Netztoteninformationen).
+
+     Ist beim Aufwachen des Spielers das Raumobjekt X zerstoert worden, dann
+     wird bei der Blueprint von X per SetProp() die gespeicherte Information
+     gesetzt. Der Rueckgabewert des SetProp wird als Pfad zu einem Ausweich-
+     Aufwach-Raum interpretiert und der Spieler wird in dem Fall dorthin
+     bewegt.
+
+BEMERKUNGEN:
+     Zum Clonen von Raeumen sollten Virtual Compiler benutzt werden:
+     Wird in den erzeugten Objektnamen KEIN '#' verwendet, dann ist diese
+     Property nicht sinnvoll und wird nicht verwendet. Ein ordentlicher
+     VC kann Bewegen eines Spielers in dessen alten, nicht mehr existierenden
+     Raum oder einen Ersatzraum beim Aufwachen selbst loesen.
+
+BEISPIELE:
+     // #1: geclonter Raum mit Ausweich-Aufwachraum: Klerus-Gilde
+     #include <properties.h>
+     inherit "/std/room";
+     
+     void create() {
+       ::create();
+
+       SetProp(P_NETDEAD_INFO, "/gilden/klerus");
+       SetProp(P_LIGHT, 1);
+     }
+     
+     // #2: komplexerer Beispielraum fuer P_NETDEAD_INFO-Funktionalitaet
+     // Siehe auch: /doc/beispiele/testobjekte/netdead_info_testraum.c
+     #include <properties.h>
+     inherit "/std/room";
+
+     void create() {
+       ::create();
+
+       if (clonep(this_object()))
+         // setze Informationen, die im Netztoten gespeichert werden sollen
+         Set(P_NETDEAD_INFO, random(5));
+       else
+         // Blueprint: hier kann man zu einem Cloneraum gehen
+         AddExit("cloneraum", #'create_room);
+
+       // Set-Method, um die Informationen aus P_NETDEAD_INFO beim Aufwachen
+       // in der Blueprint auswerten zu koennen
+       Set(P_NETDEAD_INFO, #'create_destiny, F_SET_METHOD);
+       SetProp(P_LIGHT, 1);
+     }
+     
+     // Raum entfernen, normalerweise so KEINE GUTE IDEE!
+     void BecomesNetDead(object pl) {
+       call_out(#'remove, 30);
+     }
+
+     // erzeuge einen Cloneraum und bewege den Spieler dahin
+     int create_room(string dir) {
+       object dest = clone_object(object_name(this_object()));
+       this_player()->move(dest, M_NOCHECK);
+       return 1;
+     }
+     
+     // Set-Method fuer P_NETDEAD_INFO: gibt Pfad zurueck
+     // benutze die Informationen aus dem jetzt aufwachenden Netztoten, um
+     // einen alternativen Aufwachraum zu ermitteln, da der Einschlafraum
+     // zerstoert ist
+     string create_destiny(mixed val) {
+       if (intp(val)) {
+         switch (val) {
+           case 0:
+             return "/d/ebene/room/PortVain/po_haf1";
+           case 1:
+             return "/gilden/klerus";
+           case 2:
+             return "/gilden/karate";
+           default:
+         }
+         return "/d/ebene/room/waldweg4";
+       }
+     }
+
+2. Jan 2012 Gloinson
diff --git a/doc/props/P_NEVERDROP b/doc/props/P_NEVERDROP
new file mode 100644
index 0000000..80364ce
--- /dev/null
+++ b/doc/props/P_NEVERDROP
@@ -0,0 +1,29 @@
+NAME:
+	P_NEVERDROP			"neverdrop"
+
+DEFINIERT IN:
+	/sys/thing/moving.h
+
+BESCHREIBUNG:
+	Objekte, welche diese Property gesetzt haben, werden beim Tod eines
+	Lebewesens nicht automatisch in die Leiche oder in den umgebenden
+	Raum (z.B. bei bei gesetztem P_NOCORPSE) transportiert.
+
+BEMERKUNGEN:
+	Soll das Objekt vom Lebewesen nicht weggelegt werden koennen, so ist
+	die Property P_NODROP zu verwenden.
+	Beide Properties zusammen stellen sicher, dass ein Objekt nicht
+	weitergegeben werden kann.
+
+BEISPIELE:
+	Eine dauerhafte Belohnung, die auch beim Tod des Spielers bei ihm
+	verbleiben soll, setzt das so:
+	  SetProp(P_NEVERDROP,1);
+	Sollen auch Reboots ueberstanden werden, ist zusaetzlich
+	P_AUTOLOADOBJ zu setzen.
+
+SIEHE AUCH:
+	P_NODROP, P_NOGET, P_NOCORPSE, P_AUTOLOADOBJ, /std/living/life.c
+
+----------------------------------------------------------------------------
+Last modified: Thu Jun 14 22:26:29 2001 by Patryn
diff --git a/doc/props/P_NEVER_CLEAN b/doc/props/P_NEVER_CLEAN
new file mode 100644
index 0000000..5a84820
--- /dev/null
+++ b/doc/props/P_NEVER_CLEAN
@@ -0,0 +1,35 @@
+NAME:
+	P_NEVER_CLEAN			" never clean "                   
+
+DEFINIERT IN:
+	/sys/rooms.h
+
+BESCHREIBUNG:
+	Normalerweise wird ein Raum nach 2 Resets zerstoert, wenn er waerend
+	dieser Zeit von keinem Lebewesen betreten wurde und wenn
+	keine REFRESH_NONE- oder REFRESH_DESTRUCT-Objekte existieren, die
+	nicht mehr im Raum vorhanden sind.
+	Mit dieser Property kann man den sogenannten Clean-Up unterbinden.
+
+BEISPIEL:
+	Der folgende Raum wird nicht mehr zerstoert, wenn er einmal geladen
+	wurde:
+	  #include <properties.h>
+	  // #include <rooms.h> ... wird schon von properties.h included!
+	  inherit "std/room";
+	  void create()
+	  { ::create();
+	    SetProp(P_SHORT,"Ein toller Raum");
+	    ...
+	    SetProp(P_NEVER_CLEAN,1);
+	    ...
+	  }
+	Man sollte die Anwendung nicht uebertreiben! Wichtig ist diese
+	Funktion zum Beispiel fuer Raeume, die gleichzeitig Masterobjekte
+	darstellen.
+
+SIEHE AUCH:
+	/std/room.c
+
+----------------------------------------------------------------------------
+Last modified: Wed Feb  3 00:54:32 1999 by Patryn
diff --git a/doc/props/P_NEWSKILLS b/doc/props/P_NEWSKILLS
new file mode 100644
index 0000000..66ba1ff
--- /dev/null
+++ b/doc/props/P_NEWSKILLS
@@ -0,0 +1,20 @@
+NAME:
+	P_NEWSKILLS			"newskills"                   
+
+DEFINIERT IN:
+	/sys/new_skills.h
+
+BESCHREIBUNG:
+	In dieser Property sind saemtliche Skills und Spells vermerkt, die
+	das Lebewesen kennt.
+
+BEMERKUNGEN:
+	Man sollte diese Property nicht per Hand veraendern, sondern die
+	Funktionen von "/std/living/skills.c" nutzen.
+
+SIEHE AUCH:
+	ModifySkill(), LearnSkill(), UseSkill(), /std/living/skills.c,
+	P_GUILD_SKILLS, P_SB_SPELLS
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_NEXT_DEATH_SEQUENCE b/doc/props/P_NEXT_DEATH_SEQUENCE
new file mode 100644
index 0000000..4ae7e7a
--- /dev/null
+++ b/doc/props/P_NEXT_DEATH_SEQUENCE
@@ -0,0 +1,48 @@
+P_NEXT_DEATH_SEQUENCE
+
+NAME:
+     P_NEXT_DEATH_SEQUENCE         "p_lib_next_death_sequence"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+     Im Spieler kann damit dessen eigene Todessequenz fuer den naechsten
+     Tod festgelegt werden. Nach einem Tod (egal welche Todessequenz
+     gewaehlt wurde) wird die Property geloescht und muesste neu gesetzt
+     werden.
+
+     Es gibt folgende gueltige Werte:
+     - string: Pfad zu einer eigenen Todessequenz im gueltigen Format
+     - mixed*  Eine Todessequenz im Format des Todesraumes:
+               ({<int gesamtlaenge>,
+                 ([<int index1>: <string umgebrochene Meldung1>,
+                   <int index2>: <string umgebrochene Meldung2>,
+                   ...])
+               })
+     - mapping In die Standard-Lars-Todessequenz einzufuegende Zeilen:
+               ([<int zeitindex>: <string umgebrochener Text>])
+
+BEMERKUNGEN:
+     Eine Todessequenz eines Gegners, festgelegt ueber
+     P_ENEMY_DEATH_SEQUENCE hat Vorrang vor dieser Property.
+
+BEISPIELE:
+     // Pfad zu einer eigenen DSQ
+     SetProp(P_NEXT_DEATH_SEQUENCE,  ".../passende_dsq.txt");
+
+     // eigene DSQ im Todesraumformat:
+     SetProp(P_NEXT_DEATH_SEQUENCE,
+             ({ 2, ([1: "Der Tod entlaesst dich eilig.\n"])}));
+
+     // Einfuegen einer Meldung in die Standard-Todessequenz
+     SetProp(P_NEXT_DEATH_SEQUENCE,
+             ([5: "Du fuehlst dich etwas daemlich.\n"]));
+
+SIEHE AUCH:
+     Tod:            die(L)
+     Todesmeldungen: P_KILL_NAME, P_KILL_MSG, P_DIE_MSG, P_MURDER_MSG
+                     P_ZAP_MSG, P_ENEMY_DEATH_SEQUENCE
+     Sonstiges:      P_CORPSE, P_NOCORPSE, /room/death/death_room.c
+
+10. Nov 2011 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_NEXT_DISABLE_ATTACK b/doc/props/P_NEXT_DISABLE_ATTACK
new file mode 100644
index 0000000..7019ac6
--- /dev/null
+++ b/doc/props/P_NEXT_DISABLE_ATTACK
@@ -0,0 +1,31 @@
+PROPERTY
+  P_NEXT_DISABLE_ATTACK    "next_diable_attack"
+
+DEFINIERT IN 
+  combat.h
+
+BESCHREIBUNG
+  Diese Property gibt an, wann der NPC das naechste Mal paralysiert
+  werden kann. Ueblicherweise wird sie automatisch beim Setzen
+  von P_DISABLE_ATTACK gesetzt. Sie gibt einen Zeitpunkt wie
+  die Funktion time() an, an dem zum ersten Mal wieder Paralyse
+  moeglich ist.
+
+  Will man einen NPC schreiben, der immer paralysierbar ist und nicht erst
+  nach einer gewissen Wartezeit nach der letzten Paralyse, laesst sich dies
+  durch eine Set-Methode auf P_NEXT_DISABLE_ATTACK erreichen:
+    
+  Set(P_NEXT_DISABLE_ATTACK, function int () {return 0;}, F_SET_METHOD);
+
+  Diese Set-Methode verhindert das Setzen von P_NEXT_DISABLE_ATTACK mittels
+  eines SetProp-Aufrufes.
+
+BEMERKUNGEN:
+  Die Zeit zum Schutz vor erneuter Paralyse existiert absichtlich. Bitte
+  waegt sorgfaeltig ab, bevor ihr diese Property an Gegnern/Spielern
+  manipuliert.
+
+SIEHE AUCH:
+  P_DISABLE_ATTACK
+
+21.Jul 2014 Gloinson
diff --git a/doc/props/P_NOBUY b/doc/props/P_NOBUY
new file mode 100644
index 0000000..561bbba
--- /dev/null
+++ b/doc/props/P_NOBUY
@@ -0,0 +1,10 @@
+NAME:
+    P_NOBUY                       "nobuy"                       
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Wenn diese Property gesetzt ist, wird das Objekt nach einem
+     Verkauf im Laden zerstoert, damit es nicht wieder von einem Spieler
+     gekauft werden kann.
diff --git a/doc/props/P_NOCORPSE b/doc/props/P_NOCORPSE
new file mode 100644
index 0000000..799391a
--- /dev/null
+++ b/doc/props/P_NOCORPSE
@@ -0,0 +1,30 @@
+NAME:
+	P_NOCORPSE			"nocorpse"
+
+DEFINIERT IN:
+	/sys/properties.h
+
+BESCHREIBUNG:
+	Diese Property ist gesetzt, wenn im Todesfall kein Leichnam
+	automatisch erzeugt werden soll.
+
+BEMERKUNGEN:
+	In diesem Fall wird die Property P_CORPSE ignoriert, mit der man
+	ein spezielles Leichenobjekt angeben kann, sofern nicht die
+	Standardleiche "/std/corpse.c" verwendet werden soll.
+	Da die Moerdermeldungen ueber ebendiese Objekt laufen, werden
+	hierbei auch keine ausgegeben.
+
+BEISPIELE:
+	Das Lebewesen soll keine Leiche hinterlassen, weil es zu Staub
+	zerfaellt:
+	  SetProp(P_DIE_MSG," zerfaellt zu Staub!\n");
+	  SetProp(P_NOCORPSE,1)
+	Es wurde auch gleich die Sterbemeldung dementsprechend gesetzt.
+
+SIEHE AUCH:
+	P_CORPSE, P_ZAP_MSG, P_DIE_MSG, P_MURDER_MSG, P_KILL_MSG,
+	P_NEVERDROP, /std/corpse.c
+
+----------------------------------------------------------------------------
+Last modified: Thu Jun 14 22:26:29 2001 by Patryn
diff --git a/doc/props/P_NODRINK_MSG b/doc/props/P_NODRINK_MSG
new file mode 100644
index 0000000..3f847c7
--- /dev/null
+++ b/doc/props/P_NODRINK_MSG
@@ -0,0 +1,25 @@
+NAME:
+     P_NODRINK_MSG                 "std_food_nodrink_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung an den Konsumenten, wenn versucht wird, ein Nicht-Getraenk
+     zu trinken. Sobald eine Speise einen Wert in P_FOOD setzt, gilt es als
+     Nicht-Getraenk.
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "@WEN1 kann man nicht trinken!"
+
+SIEHE AUCH:
+     P_FOOD, P_DRINK, wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_NODROP b/doc/props/P_NODROP
new file mode 100644
index 0000000..37eb5b5
--- /dev/null
+++ b/doc/props/P_NODROP
@@ -0,0 +1,39 @@
+NAME:
+	P_NODROP			"nodrop"                      
+
+DEFINIERT IN:
+	/sys/thing/moving.h
+
+BESCHREIBUNG:
+	Ist diese Property in einem Objekt gesetzt, so kann ein Lebewesen
+	das Objekt nicht weglegen.
+	Als Standardmeldung kommt in diesem Fall beispielsweise:
+	  Du kannst <Objektname> nicht wegwerfen!
+	  Du kannst <Objektname> nicht weggeben.
+	Man kann auch eine alternative Meldung angeben, wobei selbstaendig
+	auf einen korrekten Zeilenumbruch zu achten ist.
+
+BEMERKUNGEN:
+	Soll ein Objekt beim Tod des Lebewesens oder bei Ende eines Spielers
+	nicht in der Leiche bzw. im Raum zurueckgelassen werden, so ist
+	die Property P_NEVERDROP zu nutzen.
+	Beide Properties zusammen stellen sicher, dass ein Objekt nicht
+	weitergegeben werden kann.
+
+BEISPIELE:
+	Ein schwer zu erkaempfender Dolch koennte folgendes beinhalten,
+	um nicht weitergegeben werden zu koennen:
+	  SetProp(P_NODROP,1);
+	Informativer jedoch ist eine eigene Meldung:
+	  SetProp(P_NODROP,
+	 "Den Dolch hast Du Dir hart erkaempft, nicht wegwerfen!\n");
+
+SIEHE AUCH:
+     Aehnliches: P_NOGET, P_NEVERDROP, P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG,
+                 P_TOO_MANY_MSG, P_NOINSERT_MSG, P_NOLEAVE_MSG,  
+     Erfolg:     P_PICK_MSG, P_DROP_MSG, P_GIVE_MSG, P_PUT_MSG,
+                 P_WEAR_MSG, P_WIELD_MSG
+     Sonstiges:  replace_personal(E), /std/living/put_and_get.c
+
+----------------------------------------------------------------------------
+Last modified: Thu Jun 14 22:26:29 2001 by Patryn
diff --git a/doc/props/P_NOFOOD_MSG b/doc/props/P_NOFOOD_MSG
new file mode 100644
index 0000000..c367b7c
--- /dev/null
+++ b/doc/props/P_NOFOOD_MSG
@@ -0,0 +1,25 @@
+NAME:
+     P_NOFOOD_MSG                  "std_food_nofood_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung an den Konsumenten, wenn versucht wird, ein Getraenk zu essen.
+     Sobald eine Speise keinen Wert in P_FOOD und einen Wert in P_DRINK
+     setzt, gilt es als Getraenk.
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "@WEN1 kann man nicht essen!"
+
+SIEHE AUCH:
+     P_FOOD, P_DRINK, wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_NOGET b/doc/props/P_NOGET
new file mode 100644
index 0000000..af3ed1d
--- /dev/null
+++ b/doc/props/P_NOGET
@@ -0,0 +1,31 @@
+NAME:
+	P_NOGET				"noget"
+
+DEFINIERT IN:
+	/sys/thing/moving.h
+
+BESCHREIBUNG:
+	Ist diese Property in einem Objekt gesetzt, so kann ein Lebewesen
+	das Objekt nicht nehmen.
+	Als Standardmeldung kommt in diesem Fall beispielsweise:
+	  Du kannst <Objektname> nicht nehmen.
+	  Du kannst <Objektname> so nirgendwo reinstecken.
+	Man kann auch eine alternative Meldung angeben, wobei selbstaendig
+	auf einen korrekten Zeilenumbruch zu achten ist.
+
+BEISPIELE:
+	Ein Objekt, welches fest im Raum verankert ist, kann natuerlich
+	nicht entfernt werden, z.B. ein angebundenes Seil:
+	  SetProp(P_NOGET,"Das Seil ist fest am Baum verknotet!\n");
+	In einem Kommando zum Losknoten koennte man die Property dann
+	loeschen, um ein Wegnehmen zu ermoeglichen.
+
+SIEHE AUCH:
+     Aehnliches: P_NODROP, P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG,
+                 P_TOO_MANY_MSG, P_NOINSERT_MSG, P_NOLEAVE_MSG 
+     Erfolg:     P_PICK_MSG, P_DROP_MSG, P_GIVE_MSG, P_PUT_MSG,
+                 P_WEAR_MSG, P_WIELD_MSG
+     Sonstiges:  replace_personal(E), /std/living/put_and_get.c
+
+----------------------------------------------------------------------------
+Last modified: Thu Jun 14 22:26:29 2001 by Patryn
diff --git a/doc/props/P_NOINSERT_MSG b/doc/props/P_NOINSERT_MSG
new file mode 100644
index 0000000..a79994e
--- /dev/null
+++ b/doc/props/P_NOINSERT_MSG
@@ -0,0 +1,46 @@
+NAME:
+    P_NOINSERT_MSG                      "noinsert_msg"                      
+
+DEFINIERT IN:
+    /sys/thing/moving.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt eine Meldung, die ausgegeben wird, wenn
+     jemand versucht, ein Objekt in einen Behaelter zu bewegen und der
+     Behaelter dieses im PreventInsert() verhindert.
+     Die Property ist im Zielbehaelter zu setzen.
+     Ist diese Property nicht oder auf einen nicht-String-Wert gesetzt,
+     so wird die Standardmeldung ausgegeben.
+     ("<Objekt> kannst Du dort nicht hineinstecken.")
+     Der String in der Property wird noch durch replace_personal()
+     verarbeitet, das zu bewegende Objekt wird als erstes, der Zielbehaelter
+     als zweites Objekt angegeben. Danach wird der String auf 78 Zeichen
+     umgebrochen.
+     Das Setzen eines leeren Strings unterdrueckt die Ausgabe einer Meldung
+     ganz.
+
+BEISPIELE:
+     1. Ein Kochtopf laesst im PreventInsert nur bestimmte Objekte zu, die zu
+     einer Suppe gehoeren. Fuer eine passende Meldung wird im Topf jetzt die
+     Property gesetzt:
+     SetProp(P_NOINSERT_MSG, "Du kannst @WEN1 nicht in den Kochtopf tun, da"
+	                     " gehoeren doch nur Suppenzutaten rein!");
+     Wenn jemand jetzt versucht, eine Muenze reinzustecken, dann wuerde
+     folgende Meldung erscheinen:
+	Du kannst die Muenze nicht in den Kochtopf tun, da gehoeren doch nur
+	Suppenzutaten rein!
+
+     2. Ein Rucksack soll in einer bestimmten Reihenfolge gepackt werden, dazu
+     kann im PreventInsert die Meldung je nach Bedarf gesetzt werden:
+     if (<objekt noch nicht an der Reihe>)
+	SetProp(P_NOINSERT_MSG, "@WEN1 solltest du erst spaeter einpacken.");
+     else if (<objekt schon im Rucksack>)
+	SetProp(P_NOINSERT_MSG, "Aber @WER1 ist doch schon eingepackt!");
+     else ...
+
+SIEHE AUCH:
+     Aehnliches: P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_TOO_MANY_MSG,
+                 P_NOLEAVE_MSG, P_NODROP, P_NOGET 
+     Erfolg:     P_PICK_MSG, P_DROP_MSG, P_GIVE_MSG, P_PUT_MSG,
+                 P_WEAR_MSG, P_WIELD_MSG
+     Sonstiges:  replace_personal(E), /std/living/put_and_get.c
diff --git a/doc/props/P_NOLEAVE_MSG b/doc/props/P_NOLEAVE_MSG
new file mode 100644
index 0000000..c1ad0f1
--- /dev/null
+++ b/doc/props/P_NOLEAVE_MSG
@@ -0,0 +1,33 @@
+NAME:
+    P_NOLEAVE_MSG                      "noleave_msg"                      
+
+DEFINIERT IN:
+    /sys/thing/moving.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt eine Meldung, die ausgegeben wird, wenn
+     jemand versucht, ein Objekt aus einem Behaelter zu entfernen und der
+     Behaelter dieses im PreventLeave() verhindert.
+     Die Property ist im verhindernden Behaelter zu setzen.
+     Ist diese Property nicht oder auf einen nicht-String-Wert gesetzt,
+     so wird die Standardmeldung ausgegeben.
+     ("Du kannst <Objekt> nicht nehmen.")
+     Der String in der Property wird noch durch replace_personal()
+     verarbeitet, das zu bewegende Objekt wird als erstes, der verhindernde
+     Behaelter als zweites Objekt angegeben. Danach wird der String auf 78
+     Zeichen umgebrochen.
+     Das Setzen eines leeren Strings unterdrueckt die Ausgabe einer Meldung
+     ganz.
+
+BEISPIELE:
+     Nur Bierschüttler sollen eine Bierflasche aus einem Kasten nehmen
+     koennen, neben einer entsprechenden Behandlung im PreventLeave setzt man
+     dazu die Property:
+     SetProp(P_NOLEAVE_MSG, "Nur Bierschuettler duerfen das!");
+
+SIEHE AUCH:
+     Aehnliches: P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_TOO_MANY_MSG,
+                 P_NOINSERT_MSG, P_NODROP, P_NOGET 
+     Erfolg:     P_PICK_MSG, P_DROP_MSG, P_GIVE_MSG, P_PUT_MSG,
+                 P_WEAR_MSG, P_WIELD_MSG
+     Sonstiges:  replace_personal(E), /std/living/put_and_get.c
diff --git a/doc/props/P_NOMAGIC b/doc/props/P_NOMAGIC
new file mode 100644
index 0000000..bdee7f7
--- /dev/null
+++ b/doc/props/P_NOMAGIC
@@ -0,0 +1,21 @@
+NAME:
+    P_NOMAGIC                     "nomagic"                     
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Angabe in Prozent, mit welcher Wahrscheinlichkeit Magie fehlschlaegt.
+     
+     Fuer einen Raum ist es eine generelle Aussage, wie wahrscheinlich ein
+     Spell in ihm fehlschlaegt. Bei NPC zeigt es an, wie wahrscheinlich
+     ein auf ihn gesprochener Spell fehlschlaegt.
+
+BEISPIEL:
+     // in einem Raum keine Spells zulassen
+     SetProp(P_NOMAGIC, 100)
+
+SIEHE AUCH:
+     Aehnlich:     P_MAGIC_RESISTANCE_OFFSET, SpellDefend
+
+29.Dez 2007 Gloinson
diff --git a/doc/props/P_NOSELL b/doc/props/P_NOSELL
new file mode 100644
index 0000000..48b0ddb
--- /dev/null
+++ b/doc/props/P_NOSELL
@@ -0,0 +1,33 @@
+NAME:
+    P_NOSELL                      "nosell"
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Wenn diese Property gesetzt ist, kann das Objekt nicht in einem
+     Laden verkauft werden.
+     Gibt man in der Property einen String an, wird dieser ausgegeben,
+     ansonsten erfolgt eine Meldung "Du kannst <NAME> nicht verkaufen!"
+
+     Diese Meldung beinhaltet auch den Namen des in P_NAME einge-
+     tragenen Besitzer des Ladens. Ist dies nicht gesetzt, wird per
+     default 'Der Haendler' ausgegeben.
+
+BEISPIEL:
+     SetProp(P_NOSELL,"Den Schrott behaeltst Du lieber selber.");
+
+     ==> Apu sagt: Den Schrott behaeltst Du lieber selber.
+     ==> Der Haendler sagt: Den Schrott behaeltst Du lieber selber.
+
+     SetProp(P_NOSELL,1);
+
+     ==> Apu sagt: Du kannst <name> nicht verkaufen!
+     ==> Der Haendler sagt: Du kannst <name> nicht verkaufen!
+
+SIEHE AUCH:
+     P_NOBUY, P_NODROP, P_KEEPER
+
+----------------------------------------------------------------------------
+03.09.2010, Zesstra
+
diff --git a/doc/props/P_NO_ASCII_ART b/doc/props/P_NO_ASCII_ART
new file mode 100644
index 0000000..9b40cde
--- /dev/null
+++ b/doc/props/P_NO_ASCII_ART
@@ -0,0 +1,20 @@
+NAME:
+    P_NO_ASCII_ART                   "no_ascii_art"
+
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+    Diese Property kann der Spieler mit dem Befehl "grafik aus" auf
+    1 setzen. Damit zeigt er an, dass er keine ASCII Art sehen moechte.
+
+    Wer ASCII-Art einsetzt, sollte an diesen Stellen die Property 
+    abfragen und textliche Umschreibungen oder Alternativloesungen
+    einbauen.
+    
+SIEHE AUCH:
+    grafik
+
+----------------------------------------------------------------------------
+    Letzte Aenderung: 2005-10-18, Zook.   
diff --git a/doc/props/P_NO_ATTACK b/doc/props/P_NO_ATTACK
new file mode 100644
index 0000000..0f39180
--- /dev/null
+++ b/doc/props/P_NO_ATTACK
@@ -0,0 +1,82 @@
+P_NO_ATTACK
+
+NAME:
+     P_NO_ATTACK "no_attack"
+
+DEFINIERT IN:
+     <living/combat.h>
+
+BESCHREIBUNG:
+     Wenn ein NPC nicht angreifbar sein soll (weil er zum Beispiel in einer
+     Gilde oder einer Quest Informationen vermittelt oder aehnlichen), sollte
+     man diese Property auf einen Wert ungleich Null setzen. Sie wird immer
+     abgefragt, wenn ermittelt wird, ob ein Lebewesen prinzipiell angreifbar
+     ist. D.h. auch, dass nach Abfragen von P_NO_ATTACK _nicht_ immer ein
+     Kampf gestartet wird und dass man dabei _nicht_ im Kampf sein muss!
+
+     Gibt man hier einen String an (mit einem Satzzeichen und "\n" abge-
+     schlossen), wird dieser bei direkten Angriffen ausgegeben. Bei anderen
+     Datentypen wird eine Defaultmeldung ausgegeben. Die Defaultmeldung
+     lautet: "<Name> laesst sich nicht angreifen!\n"
+
+     Mit direkten Angriffen sind 'toete <name>' und Angriffszauber gemeint
+     (bzw. alles, was living/life::Kill(), spellbook::TryAttackSpell(),
+     spellbook::TryDefaultAttackSpell() und spellbook::FindEnemyVictim()
+     aufruft).
+
+ACHTUNG:
+
+  1) Zum Thema QueryMethoden auf P_NO_ATTACK
+     Grundsaetzlich legt man entweder eine Query-Methode auf P_NO_ATTACK:
+        Set(P_NO_ATTACK, #'my_no_attack, F_QUERY_METHOD);
+     oder definiert eine Funktion _query_no_attack() im NPC.
+
+     Wie muss nun eine solche Funktion aussehen? Z.B.:
+     
+     int|string my_no_attack() {
+       if (!objectp(this_player())) return 0;
+       if (opfer==getuid(this_player()) || this_player()==this_object())
+         return(0);
+       return(1); //nicht angreifbar
+     }
+
+     Diese Funktion macht den NPC nun nur fuer den Spieler 'opfer' angreifbar.
+     Stattdessen kann natuerlich auch jede andere Bedingung genutzt werden.
+
+     Aber warum die zweite Bedingung, this_player()==this_object()?
+     Warum sollte der NPC sich selber angreifen duerfen?
+
+     Das liegt an folgenden 2 Dingen:
+
+     1. Kaempfer kriegen bei eingeschaltetem Fokus Probleme, wenn man das 
+     nicht macht. Das liegt an folgendem: Wenn der NPC angreift, ruft er 
+     natuerlich Defend() im Spieler auf. Dieses schaut nach, ob der Spieler 
+     den Skill SK_MAGICAL_DEFENSE hat. Dieser ist bei Kaempfern das Parieren.
+     Dieses schaut nach, ob der Fokus aktiv ist, wenn ja, wird dem 
+     ge'fokus'te Gegner besonders gut ausgewichen. Zu diesem Zweck wird die 
+     Liste der Feind im Raum erstellt mit PresentEnemies() abgerufen. Dieses 
+     fragt aber in allen (potentiellen) Gegnern P_NO_ATTACK ab und beendet 
+     den Kampf mit allen Gegnern, die nicht angreifbar sind. Bei dieser 
+     Abfrage ist jedoch TP==NPC, weil der ja angreift. Wenn er nun 1 
+     zurueckgibt, wird der Kampf an der Stelle beendet. 
+
+     2. Wenn der NPC den Spieler angreift, wird im Spieler InsertEnemy(NPC)
+     aufgerufen. Auch diesem Fall findet die Abfrage von P_NO_ATTACK statt, 
+     da InsertEnemy() ja erstmal rausfinden muss, ob der Gegner angreifbar 
+     ist, bevor er in die Feindliste eingetragen wird. Da der NPC den 
+     Angriff beginnt, ist TP der NPC. Wenn die Query-Methode auf P_NO_ATTACK
+     hier abbricht, wird der NPC nicht in die Feindliste des Spielers 
+     eingetragen. Dann bekaempft der NPC den Spieler, aber der Spieler nicht
+     den NPC.
+
+
+  2) P_NO_ATTACK des NPC wird z.B. beim Kampf eines Kaempfers mit dem NPC 
+     pro Kampfrunde um die 10mal abgerufen. Wenn der Kaempfer nur eine 
+     Attacke macht. Wenn er noch Sonderattacken machen, Spells ausfuehrt, 
+     etc. wird das noch mehr. D.h. was auch immer ihr in der Query-Methode 
+     im NPC macht: 
+     Es sollte schnell sein, jeder Tick an Rechenzeit zaehlt hier xfach!
+
+
+LETZTE AENDERUNG:
+09.11.2015, Arathorn
diff --git a/doc/props/P_NO_BAD b/doc/props/P_NO_BAD
new file mode 100644
index 0000000..60df1e7
--- /dev/null
+++ b/doc/props/P_NO_BAD
@@ -0,0 +1,23 @@
+NAME:
+     P_NO_BAD                      "std_food_no_bad"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Flag, ob die Speise ewig haltbar ist.
+     0: Speise verdirbt nach der Anzahl Sekunden, die in P_LIFETIME
+        angegeben ist bzw. nach einem Reset
+     1: Speise verdirbt nicht
+     
+     ACHTUNG: Diese Property darf nur in Absprache mit der Balance
+              geaendert werden.
+     
+DEFAULT:
+     Initial ist diese Property auf 0 gesetzt.
+
+SIEHE AUCH:
+     /std/food.c, wiz/food
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_NO_GLOBAL_ATTACK b/doc/props/P_NO_GLOBAL_ATTACK
new file mode 100644
index 0000000..01df4e9
--- /dev/null
+++ b/doc/props/P_NO_GLOBAL_ATTACK
@@ -0,0 +1,20 @@
+P_NO_GLOBAL_ATTACK
+
+NAME:
+     P_NO_GLOBAL_ATTACK "no_global_attack"
+
+DEFINIERT IN:
+     <combat.h>
+
+BESCHREIBUNG:
+     Setzt man diese Property in einem NPC auf einen Wert ungleich 0, so
+     wird der NPC bei einem "toete alle" nicht angegriffen.
+
+     Damit kann man zB. NPCs, die dem eigenen Schutz dienen (als Folge von
+     Zauberspruechen o.ae.) vor versehentlichen Angriffen schuetzen.
+
+SIEHE AUCH:
+     /std/npc.c, P_FRIEND
+
+----------------------------------------------------------------------------
+Last modified: Sat May 18 15:26:28 1996 by Wargon
diff --git a/doc/props/P_NO_PARA_TRANS b/doc/props/P_NO_PARA_TRANS
new file mode 100644
index 0000000..578c245
--- /dev/null
+++ b/doc/props/P_NO_PARA_TRANS
@@ -0,0 +1,15 @@
+NAME:
+	P_NO_PARA_TRANS				"no_para_trans"
+
+DEFINIERT IN:
+	/sys/properties.h
+
+BESCHREIBUNG:
+	Wenn in einem Raum diese Property gesetzt ist, darf dort kein
+	Wechsel in oder aus der Paralellwelt erfolgen. Objekte die so
+	einen Wechsel ermoeglichen sind dafuer verantwortlich diese
+	Property abzufragen.
+
+SIEHE AUCH:
+	P_NO_TPORT
+
diff --git a/doc/props/P_NO_PLAYERS b/doc/props/P_NO_PLAYERS
new file mode 100644
index 0000000..062b915
--- /dev/null
+++ b/doc/props/P_NO_PLAYERS
@@ -0,0 +1,37 @@
+NAME:
+    P_NO_PLAYERS                     "no_players"                     
+
+DEFINIERT IN:
+    /sys/rooms.h
+
+BESCHREIBUNG:
+    Wenn in einem Raum die Property P_NO_PLAYERS auf einen Wert != 0 gesetzt
+    ist, kann dieser von Spielern auf normalem Wege nicht mehr betreten werden.
+    Magier und Testspieler(*) koennen den Raum betreten; Spieler muessen mit
+    M_NOCHECK hineinbewegt werden.
+
+    Auf diese Weise koennen Gebiete, die noch nicht offiziell angeschlossen
+    sind, vor 'unbeabsichtigtem' Betreten durch Spieler geschuetzt werden.
+
+    Moechte man zu einem schon angeschlossenen Gebiet nachtraeglich eine
+    Parallelwelt einbauen, so sollte P_NO_PLAYERS *dringend* in den
+    Parallelweltraeumen gesetzt werden, bis die Parallelwelt ausdruecklich
+    fuer Spieler freigegeben wird. Andernfalls sind alle Parallelweltraeume,
+    zu denen angeschlossene Normalweltraeume mit gleichem Filenamen existieren,
+    automatisch durch Spieler erreichbar!
+
+    (*) Ausschliesslich Testspieler, die auf den Namen eines existierenden
+    Magiers markiert sind, koennen 'geschuetzte' Raeume betreten.
+    Gildentesties werden wie Spieler behandelt.
+
+ANMERKUNG:
+    Im Gegensatz zu Bewegungen von Livings wird bei der Bewegung von Gegen-
+    staenden P_NO_PLAYERS nur beim Wechsel der Welt ausgewertet, um z.B. zu
+    verhindern, dass Bumerangs in noch nicht angeschlossene Gebiete fliegen.
+
+    Moechte man in seinem eigenen Gebiet mit Bumerangs o.ae. testen, muss
+    in diesen P_TESTPLAYER gesetzt sein. Das ist zwar eher ein Missbrauch
+    der Property, aber ein Umkompieren vom Werfer war auf Dauer zu teuer. ;-)
+
+SIEHE AUCH:
+    P_PARA, move
diff --git a/doc/props/P_NO_REGENERATION b/doc/props/P_NO_REGENERATION
new file mode 100644
index 0000000..b8dea8c
--- /dev/null
+++ b/doc/props/P_NO_REGENERATION
@@ -0,0 +1,34 @@
+P_NO_REGENERATION
+
+NAME:
+     P_NO_REGENERATION    "no_regeneration"
+
+DEFINIERT IN:
+     <living/life.h> und <health.h>
+
+BESCHREIBUNG:
+     Durch das Setzen dieser Property kann man verhindern, das ein Lebewesen
+     sich regeneriert.
+     Es gibt sieben moegliche Werte, die man durch verodern kombinieren
+     kann:
+     NO_REG_HP        : es werden keine HP regeneriert
+     NO_REG_BUFFER_HP : es werden beim "tanken" keine HP regeneriert
+     NO_REG_SP        : es werden keine SP regeneriert
+     NO_REG_BUFFER_SP : es werden beim "tanken" keine SP regeneriert
+     NO_REG_ALCOHOL   : der Alkoholspiegel wird nicht gesenkt
+     NO_REG_DRINK     : der Fluessigkeitsspiegel wird nicht gesenkt
+     NO_REG_FOOD      : der Nahrungsspiegel wird nicht gesenkt
+     sowie die Konstante NO_REG, die eine Kombination aller moeglichen
+     Werte darstellt (quasi das groesstmoegliche Uebel ;).
+
+BEISPIELE:
+     Dieses Lebewesen heilt nur beim "tanken" in der Kneipe, ansonsten
+     nicht:
+     
+     SetProp( P_NO_REGENERATION, NO_REG_HP|NO_REG_SP );
+
+SIEHE AUCH:
+     /std/living/life.c
+
+----------------------------------------------------------------------------
+Last modified: 14-05-2001 by Mupfel
diff --git a/doc/props/P_NO_SCORE b/doc/props/P_NO_SCORE
new file mode 100644
index 0000000..6c4a2d2
--- /dev/null
+++ b/doc/props/P_NO_SCORE
@@ -0,0 +1,51 @@
+NAME:
+     P_NO_SCORE               "no_score"
+
+DEFINIERT IN:
+     /secure/scoremaster.h
+
+BESCHREIBUNG:
+     Die Property stellt ein Flag innerhalb von Lebewesen dar, welches
+     standardmaessig nicht gesetzt ist. In diesem Fall werden
+     Erstkillstufenpunkte an den Angreifer vergeben, sofern er ein Opfer
+     toetet.
+
+     Innerhalb eines Teams koennen Erstkillstufenpunkte auch an
+     Mitglieder vergeben werden, die das Lebewesen nicht selbst getoetet
+     haben. Voraussetzung hierfuer ist, dass derjenige, der den letzten
+     Schlag ausfuehrte, den Kill schon hat. Danach werden Mitglieder des
+     Teams gesucht, welche den Kill noch nicht haben und in der Formation
+     moeglichst weit vorne stehen.
+
+     Mit der gesetzten Property P_NO_SCORE im Opfer erreicht man nun,
+     dass diese Gutschrift fuer den/die Angreifer unterbunden wird.
+
+BEISPIEL:
+     Folgendermassen unterbindet man die Vergabe von
+     Erstkillstufenpunkten fuer den Tod eines NPC's:
+
+       include "/secure/scoremaster.h"
+       inherit "std/npc";
+       void create() {
+         ::create();
+         ...
+         SetProp(P_NO_SCORE,1);
+       }
+
+     Damit kann P_XP einen Wert haben, der eigentlich zum automatischen
+     Eintragen von Erstkillstufenpunkten fuer ein Lebewesen fuehrt, und
+     trotzdem wird dieser Eintrag nicht vorgenommen.
+     Sinnvoll ist dies insbesondere bei Lebewesen, die nicht jeder
+     Spieler erreichen kann (man moechte doch eine gewisse
+     Chancengleichheit fuer das Erreichen von Stufenpunkten bieten).
+
+BEMERKUNGEN:
+     Auch die Vergabe von Erfahrungspunkten kann explizit unterbunden
+     werden. Hierfuer gibt es die aehnlich geartete Property P_NO_XP.
+
+SIEHE AUCH:
+     Funktionen:  GiveKillScore(), do_damage()
+     Verwandt:    P_NO_XP
+     Sonstiges:   P_XP
+
+14.Feb 2007 Gloinson
diff --git a/doc/props/P_NO_STD_DRINK b/doc/props/P_NO_STD_DRINK
new file mode 100644
index 0000000..a91e92d
--- /dev/null
+++ b/doc/props/P_NO_STD_DRINK
@@ -0,0 +1,19 @@
+NAME:
+	P_NO_STD_DRINK			"no_std_drink"
+
+DEFINIERT IN:
+	/sys/pub.h
+
+BESCHREIBUNG:
+        Durch setzen dieser Property in einer Kneipe sorgt man dafuer, dass 
+        "Standard-Drinks" (z.B. Gluehwein im Dezember) nicht in das Menue
+        der Kneipe aufgenommen werden.
+
+BEMERKUNGEN:
+        Keine.
+
+SIEHE AUCH:
+	/std/room/pub.c
+
+----------------------------------------------------------------------------
+Last modified: Sat Mar 04 22:42:00 2000 by Paracelsus
diff --git a/doc/props/P_NO_TPORT b/doc/props/P_NO_TPORT
new file mode 100644
index 0000000..40350e5
--- /dev/null
+++ b/doc/props/P_NO_TPORT
@@ -0,0 +1,15 @@
+NAME:
+    P_NO_TPORT                    "tport"                       
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Kann folgende Werte annnehmen (definiert in moving.h):
+     NO_TPORT_IN	= Man kann nicht in den Raum hinein teleportieren.
+     NO_TPORT_OUT = Man kann nicht aus dem Raum hinaus teleportieren.
+     NO_TPORT	= Weder noch.
+
+SIEHE AUCH:
+	P_NO_PARA_TRANS
+
diff --git a/doc/props/P_NO_TRAVELING b/doc/props/P_NO_TRAVELING
new file mode 100644
index 0000000..61425bc
--- /dev/null
+++ b/doc/props/P_NO_TRAVELING
@@ -0,0 +1,25 @@
+NAME:
+    P_NO_TRAVELING                   "no_traveling"                   
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+    Hier steht der allgemeine reise-Befehl nicht zur Verfuegung.
+
+BEMERKUNGEN:
+    P_NO_TRAVELING wird in Transportern gesetzt wenn Spieler ihn 
+    nicht mehr 'automatisch' mittels des 'reise'-Befehls betreten
+    koennen sollen.
+
+    Sie bekommen in dem Transporter und in den Zielraeumen auch 
+    keinerlei Hinweise darauf, wohin sie evtl. reisen koennten.
+    
+    Standardmaessig ist P_NO_TRAVELING natuerlich 0.
+
+SIEHE AUCH:
+    reise
+
+LETZTER AENDERUNG:
+    Don, 24.01.2002, 10:15:07h von Tilly
+    
\ No newline at end of file
diff --git a/doc/props/P_NO_XP b/doc/props/P_NO_XP
new file mode 100644
index 0000000..f12fe8d
--- /dev/null
+++ b/doc/props/P_NO_XP
@@ -0,0 +1,43 @@
+NAME:
+     P_NO_XP                    "no_xp"
+
+DEFINIERT IN:
+     /sys/living/life.h
+
+BESCHREIBUNG:
+     Im Normalfall bekommt man im Kampf gegen einen Gegner fuer Treffer
+     und beim Toeten eine XP-Gutschrift.
+
+     Ist P_NO_XP gesetzt, so erhält man keinerlei XP-Gutschriften
+     für den Kampf oder den Tod des NPCs.
+
+BEISPIEL:
+     Folgendermassen unterbindet man die Vergabe von Erfahrungspunkte
+     fuer den Angriff eines NPC's:
+
+       include "/sys/living/life.h"
+       inherit "std/npc";
+       void create() {
+         ::create();
+         ...
+         SetProp(P_NO_XP,1);
+       }
+
+     Damit kann P_XP trotzdem einen Wert im NPC haben, der
+     Erstkillstufenpunkte fuer Lebewesen automatisch eintraegt!
+
+     Auch fuer das kurzzeitige Unterbinden der Vergabe von
+     Erfahrungspunkten ist diese Property sinnvoller, als P_XP im NPC
+     auf 0 zu setzen.
+
+BEMERKUNGEN:
+     Auch die Vergabe von Erstkillstufenpunkten kann explizit unterbunden
+     werden. Hierfuer gibt es die aehnlich geartete Property P_NO_SCORE.
+
+SIEHE AUCH:
+     Funktionen:  AddExp(), DistributeExp(), do_damage()
+     Properties:  P_XP, P_LAST_XP
+     Verwandt:    P_NO_SCORE
+     Sonstiges:   P_TOTAL_WC, create_default_npc()
+
+14.Feb 2007 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_NPC b/doc/props/P_NPC
new file mode 100644
index 0000000..69d1270
--- /dev/null
+++ b/doc/props/P_NPC
@@ -0,0 +1,8 @@
+NAME:
+    P_NPC                         "is_npc"                      
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Gesetzt bei Monstern.
diff --git a/doc/props/P_NPC_FASTHEAL b/doc/props/P_NPC_FASTHEAL
new file mode 100644
index 0000000..3f588b9
--- /dev/null
+++ b/doc/props/P_NPC_FASTHEAL
@@ -0,0 +1,21 @@
+NAME:
+	P_NPC_FASTHEAL			"npc_fastheal"
+
+DEFINIERT IN:
+	/sys/pub.h
+
+BESCHREIBUNG:
+        Durch setzen dieser Property in einer Kneipe sorgt man dafuer, dass 
+        bei NPCs, die dort "tanken", die Lebens- und Konzentrationspunkte
+        direkt erhoeht werden und nicht, wie bei ungesetzter Property, in
+        die jew. Buffer geschrieben werden.
+
+BEMERKUNGEN:
+        Die Benutzung dieser Property sollte nicht unbedingt zum Standard
+        werden.
+
+SIEHE AUCH:
+	/std/room/pub.c
+
+----------------------------------------------------------------------------
+Last modified: Wed Sep 29 13:58:00 1999 by Paracelsus
diff --git a/doc/props/P_NR_HANDS b/doc/props/P_NR_HANDS
new file mode 100644
index 0000000..b0827a9
--- /dev/null
+++ b/doc/props/P_NR_HANDS
@@ -0,0 +1,31 @@
+P_NR_HANDS
+NAME:
+     P_NR_HANDS "nr_hands"
+
+DEFINIERT IN:
+     <weapon.h>
+
+BESCHREIBUNG:
+     Wieviele Haende muss man frei haben, um die Waffe zuecken oder den
+     Schild tragen zu koennen?
+     Dieser Wert muss mindestens 1 betragen!
+
+     Sollen Spieler die Waffe benutzen koennen, so sind hier nur die Werte 1
+     und 2 moeglich. Falls die Waffe nur von Monstern benutzbar sein soll,
+     kann man hier auch hoehere Werte eintragen (dazu muss man beim Monster
+     P_MAX_HANDS entsprechend hoch setzen). Als Beispiel sei hier nur das
+     vierhaendige Schwert aus dem Friedhof genannt.
+
+     Defaultmaessig sind alle Waffen Zweihaender.
+
+     Diese Property kann auch bei Zaubern benutzt werden, bei denen man eine
+     oder mehrere Haende frei haben muss.
+
+SIEHE AUCH:
+     P_HANDS, P_HANDS_USED_BY
+     P_MAX_HANDS, P_USED_HANDS, P_FREE_HANDS
+     UseHands, FreeHands
+     /std/weapon.c, /std/spellbook.c
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 15:00:02 1996 by Wargon
diff --git a/doc/props/P_ORAKEL b/doc/props/P_ORAKEL
new file mode 100644
index 0000000..cf383da
--- /dev/null
+++ b/doc/props/P_ORAKEL
@@ -0,0 +1,9 @@
+NAME:
+    P_ORAKEL                      "orakel"                      
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Wenn diese Property gesetzt ist, kann der Wanderer in diesen
+     Raum hinein.
diff --git a/doc/props/P_ORIG_FILE_NAME b/doc/props/P_ORIG_FILE_NAME
new file mode 100644
index 0000000..2064360
--- /dev/null
+++ b/doc/props/P_ORIG_FILE_NAME
@@ -0,0 +1,8 @@
+NAME:
+    P_ORIG_FILE_NAME                "original_object_name"               
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     In einer Leiche der Filename des Gestorbenen.
diff --git a/doc/props/P_ORIG_NAME b/doc/props/P_ORIG_NAME
new file mode 100644
index 0000000..96a5d73
--- /dev/null
+++ b/doc/props/P_ORIG_NAME
@@ -0,0 +1,8 @@
+NAME:
+    P_ORIG_NAME                   "original_name"               
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     In einer Leiche der Name des Gestorbenen. (name(RAW))
diff --git a/doc/props/P_PARA b/doc/props/P_PARA
new file mode 100644
index 0000000..87cc92b
--- /dev/null
+++ b/doc/props/P_PARA
@@ -0,0 +1,54 @@
+NAME:
+    P_PARA                        "para"                        
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Nummer der Parallelwelt, in der sich ein Spieler befindet.
+
+    Ist die Property P_PARA auf Null gesetzt, so befindet sich der Spieler in
+    der 'Normalwelt'. Gibt es bei einer Bewegung dieses Spielers mehrere
+    moegliche Zielraeume mit identischem Namen aber unterschiedlichen Endungen
+    'name.c', 'name^1.c', 'name^2.c' etc., so wird der Spieler in den Raum
+    'name.c' bewegt. 
+
+    Wird die Property P_PARA auf einen Wert n>0 gesetzt, so landet der Spieler
+    bei einer Bewegung im Raum 'name^n.c'. Ist kein Raum mit entsprechender
+    Endung vorhanden, wird der Spieler stattdessen in den Normalweltraum
+    bewegt.
+
+    Diese Prop kann auch in einem Virtual Compiler gesetzt werden. In diesem
+    Fall schraenkt sie die Dimensionen ein, in denen der VC Objekte erzeugt.
+    Die Prop kann eine einzelne Ziffer (Int) oder ein Array von Ints 
+    aufnehmen, dann ist der VC fuer alle angegeben Dimensionen zustaendig. 
+    Ein leeres Array erlaubt gar keine Para-Objekte.
+
+ANMERKUNG:
+    Die Endung '^0' kennzeichnet _nicht_ die Normalwelt. So lange kein Ausgang
+    explizit auf den Raum 'name^0.c' verweist, wird kein Spieler den Raum
+    betreten koennen. Deshalb kann man die Endung '^0' z.B. dazu benutzen, um
+    eigene Standardraeume fuer ein Gebiet zu schreiben, die dann sowohl von
+    den Normal- als auch von den Parallelweltraeumen inheritet werden.
+
+    Raeume mit Endungen '^n.c', bei denen 'n' keine positive ganze Zahl ist,
+    werden nicht beachtet.
+
+    Fuer die Entscheidung, in welchem Raum ein Spieler in Abhaengigkeit von
+    P_PARA landet, ist die Funktion move() zustaendig. Als Magier muss man sich
+    darum nicht gesondert kuemmern. Das heisst aber auch, dass beim Anschluss
+    eines Normalweltraumes automatisch alle in dem Verzeichnis mit gleichem
+    Namen vorhandenen Parallelweltraeume mit angeschlossen werden.
+
+    Sollen einzelne Parallelweltraeume noch nicht angeschlossen werden, so muss
+    in ihnen die Property P_NO_PLAYERS gesetzt werden. Diese Raeume sind dann
+    nur durch Magier und Testspieler zu betreten (und zu testen).
+
+    In Paraweltraeumen liefert P_PARA 'n' zurueck.
+    Man kann also z.B. in NPCs einfach ueber environment()->QueryProp(P_PARA) 
+    abfragen, in welcher Parawelt sich dieser gerade befindet.
+
+SIEHE AUCH:
+    P_NO_PLAYERS, move, pararaeume
+    
+25.Jan 2015 Gloinson
diff --git a/doc/props/P_PARRY b/doc/props/P_PARRY
new file mode 100644
index 0000000..03550df
--- /dev/null
+++ b/doc/props/P_PARRY
@@ -0,0 +1,31 @@
+P_PARRY
+
+NAME:
+     P_PARRY "parry"
+
+DEFINIERT IN:
+     <combat.h>
+
+BESCHREIBUNG:
+     Diese Property legt fest, inwiefern eine Waffe als Parierwaffe
+     genutzt werden kann. Moegliche Werte:
+
+         PARRY_NOT     Eine reine Angriffswaffe ohne Parierfunktion.
+
+         PARRY_TOO     Eine kombinierte Angriffs- und Parierwaffe.
+
+         PARRY_ONLY    Eine reine Parierwaffe. Diese kann zusaetzlich
+                       zu einer normalen Waffe gezueckt werden.
+
+     Man sollte nur die in <combat.h> definierten Konstanten verwenden.
+
+BEMERKUNGEN:
+     Durch diese Propertie laesst sich _kein_ Parade-Bonus fuer Trves 
+     setzen! Alle Gilden haben etwas davon. Vor Verwendung bitte mit
+     der Objekt-Balance absprechen.
+
+SIEHE AUCH:
+     /std/weapon/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Sat Jun 01 13:28:45 2001 by Tilly
diff --git a/doc/props/P_PARRY_WEAPON b/doc/props/P_PARRY_WEAPON
new file mode 100644
index 0000000..c873bfb
--- /dev/null
+++ b/doc/props/P_PARRY_WEAPON
@@ -0,0 +1,17 @@
+P_PARRY_WEAPON
+
+NAME:
+     P_PARRY_WEAPON "parry_weapon"
+
+DEFINIERT IN:
+     <combat.h>
+
+BESCHREIBUNG:
+     Diese Property gibt an, welche Parierwaffe ein Spieler derzeit
+     gezueckt hat.
+
+SIEHE AUCH:
+     /std/weapon/combat.c, /std/living/combat.c
+
+----------------------------------------------------------------------------
+Last modified: Sat Jun 26 15:23:00 1999 by Paracelsus
diff --git a/doc/props/P_PEACE_HISTORY b/doc/props/P_PEACE_HISTORY
new file mode 100644
index 0000000..f3cb2d2
--- /dev/null
+++ b/doc/props/P_PEACE_HISTORY
@@ -0,0 +1,42 @@
+P_PEACE_HISTORY
+
+NAME:
+     P_PEACE_HISTORY      "_peace_history"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+    In dieser Prop wird nach Gilden getrennt gespeichet, wie oft das Lebewesen
+    in letzter Zeit befriedet worden ist. Diese Information geht in die
+    Chance auf eine zukuenftige Befriedung ein.
+    Die Zaehler werden im Durchschnitt alle 2700s um 2-3 reduziert.
+    Die Datenstruktur ist ein Array, welches einen Zeitstempel als erstes
+    Element und ein Mapping als zweites enthaelt. Das Mapping enthaelt unter
+    den Gildennamen als Keys den ganzzahligen Zaehler erfolgreicher
+    Befriedungen von Spielern dieser Gilde.
+
+BEMERKUNGEN:
+    * Diese Property sollte niemals direkt geaendert werden. Bitte greift also
+      nur lesend darauf zu. Sollte hiermit Schindluder getrieben werden,
+      werden die Daten vor externer Aenderung geschuetzt.
+    * Die Datenstruktur in dieser Prop kann in Zukunft u.U. geaendert werden.
+      Daher aendert sie am besten auch nicht im eigenen NPC oder seid darauf
+      gefasst, irgendwann Hand anlegen zu muessen.
+    * Die Aktualisierung (auch die Reduktion) findet im Zuge eines
+      QueryPacify() statt, nicht im Reset des Lebewesens.
+
+BEISPIEL:
+    In P_PEACE_HISTORY steht:
+    ({1209654597, (["zauberer": 3, "klerus": 4]) })
+    Bei der Berechnung der naechsten Befriede-Chance gehen bei Zauberern also
+    3 erfolgreiche Versuche, bei Klerikern 4 erfolgreiche Versuche ein.
+    Der Zeitwert an erster Stelle des Arrays wird der bei der Berechnung der
+    naechsten Reduktion der Zaehler beruecksichtigt. (Genaues: s. combat.c)
+
+SIEHE AUCH:
+     P_PEACE_ACCEPT
+     QueryPacify()
+     /std/living/combat.c
+
+01.05.2008, Zesstra
diff --git a/doc/props/P_PERM_STRING b/doc/props/P_PERM_STRING
new file mode 100644
index 0000000..d7904b7
--- /dev/null
+++ b/doc/props/P_PERM_STRING
@@ -0,0 +1,11 @@
+NAME:
+    P_PERM_STRING                 "perm_string"                 
+
+DEFINIERT IN:
+    /sys/player/comm.h
+
+BESCHREIBUNG:
+     Fuer Sprachflueche, Property ist im Spieler-Objekt zu setzen. In dem
+     Objekt, das in P_PERM_STRING gespeichert ist, wird bei Sprachbefehlen
+     permutate_string(str) aufgerufen und der zurueckgegebene String 
+     stattdessen ausgegeben.
diff --git a/doc/props/P_PICK_MSG b/doc/props/P_PICK_MSG
new file mode 100644
index 0000000..f77d508
--- /dev/null
+++ b/doc/props/P_PICK_MSG
@@ -0,0 +1,60 @@
+P_PICK_MSG
+NAME:
+     P_PICK_MSG				"pick_message"
+
+DEFINIERT IN:
+     /sys/living/put_and_get.h
+
+BESCHREIBUNG:
+     Mit P_PICK_MSG kann man die Meldung, die man beim Aufnehmen eines
+     Objektes bekommt, modifizieren.
+
+     Folgende Werte sind moeglich:
+
+     o 0
+       Es wird eine Standardmeldung ausgegeben. Dies ist Voreinstellung.
+
+     o NO_PNG_MSG       == -1
+       Es wird keinerlei Meldung ausgegeben
+
+     o Ein Array aus Strings
+       Der erste String wird an den Spieler ausgegeben, der zweite
+       (optionale) an den Raum.
+
+       Die Strings werden durch die Funktion replace_personal() geparst.
+	Objekt1 - Spieler
+        Objekt2 - das Objekt, das genommen wird
+
+       Wird der zweite String nicht angegeben, erfolgt keine Meldung an den
+       Raum.
+
+BEISPIEL:
+     void create() {
+      ...
+      SetProp( P_SHORT, "Etwas Sand" );
+      SetProp( P_LONG, break_string(
+       "Ein wenig magischer Sand. Sehr feinkruemelig.",78 ));
+
+      SetProp( P_NAME, "Sand" );
+      AddId( ({"sand"}) );
+      SetProp( P_GENDER, MALE );
+
+      SetProp( P_PICK_MSG, ({
+       "Du schaufelst @WEN2 in deine Hand.",
+       "@WER1 schaufelt @WEN2 in eine Hand."}));
+      ...
+     }
+
+     Das ganze fuehrt bei Ugars "nimm sand" zu folgenden
+     Meldungen:
+
+     Ugar: "Du schaufelst den Sand in deine Hand."
+     Raum: "Ugar schaufelt den Sand in eine Hand."
+
+SIEHE AUCH:
+     Aehnliches: P_DROP_MSG, P_PUT_MSG, P_GIVE_MSG, P_WEAR_MSG, P_WIELD_MSG
+     Fehler:     P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_TOO_MANY_MSG,
+                 P_NOINSERT_MSG, P_NOLEAVE_MSG, P_NODROP, P_NOGET 
+     Sonstiges:  replace_personal(E), pick_obj(L), /std/living/put_and_get.c
+
+14. Maerz 2004 Gloinson
diff --git a/doc/props/P_PILE_NAME b/doc/props/P_PILE_NAME
new file mode 100644
index 0000000..e54dc72
--- /dev/null
+++ b/doc/props/P_PILE_NAME
@@ -0,0 +1,9 @@
+NAME:
+    P_PILE_NAME                   "file_name"               
+
+DEFINIERT IN:
+    /sys/container/properties.h
+
+BESCHREIBUNG:
+     In einer Leiche der Name des Gestorbenen im Dativ. (name(WEM))
+     Wird vom Haufen benoetigt.
diff --git a/doc/props/P_PLAYER_LIGHT b/doc/props/P_PLAYER_LIGHT
new file mode 100644
index 0000000..ddd59b5
--- /dev/null
+++ b/doc/props/P_PLAYER_LIGHT
@@ -0,0 +1,28 @@
+NAME:
+    P_PLAYER_LIGHT                       "player_light"
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Gibt den Lichtlevel an, mit dem ein Lebewesen sieht, ein Abfragen dieser
+    Property kann z.B. sinnvoll sein wenn man abfragen will ob ein Spieler
+    genug Licht dabei hat um ein bestimmtes Detail untersuchen zu koennen.
+
+    Bitte _nur_ ueber QueryProp auf diese Property zugreifen,
+    da das Lichtlevel ggf. neu berechnet werden muss!
+
+    Ein direktes setzen dieser Property ist NICHT moeglich. Es ist jedoch
+    moeglich entweder eine Closure zu benutzen oder den Wert ueber einen
+    P_LIGHT_MODIFIER zu veraendern.
+
+    Um zu erreichen, das ein NPC Nachtsicht bekommt, gibt es nun 3 Varianten.
+    - eine closure:
+        Set(P_PLAYER_LIGHT, function int () {return 1;} , F_QUERY_METHOD) 
+      dieses bedeutet, dass der NPC in jeder Dunkelheit perfekt sehen kann.
+    - das setzen von einem P_LIGHT_MODIFIER
+    - das benutzen des stdskills. Hierzu schreibt man in das create() des
+      NPCs einfach ein: ModifySkill(SK_NIGHTVISION, 10000);
+
+SIEHE AUCH:
+    P_LIGHT_MODIFIER, P_LIGHT, P_TOTAL_LIGHT, P_INT_LIGHT, CannotSee()
diff --git a/doc/props/P_PLURAL b/doc/props/P_PLURAL
new file mode 100644
index 0000000..0a5e3a5
--- /dev/null
+++ b/doc/props/P_PLURAL
@@ -0,0 +1,39 @@
+NAME:
+    P_PLURAL                      "plural"
+
+DEFINIERT IN:
+    /sys/thing/language.h
+
+BESCHREIBUNG:
+    Mit Hilfe von P_PLURAL koennen auch nicht Unit Objekte als Pluralobjekte
+    markiert werden. Bei einem Wert > 1 wird der Wert ausserdem auch noch in
+    den Namen eingefuegt. Sollte man in eigenem Code zulassen wollen, das
+    etwas mit bestimmten Objekten geschieht, dann sollte man die Verben
+    entsprechen konjugieren.
+
+BEMERKUNGEN:
+    Wirkt nicht auf Todesmeldungen -> siehe dafuer P_KILL_MSG
+
+BEISPIELE:
+    SetProp(P_NAME, "Stiefel"); SetProp(P_PLURAL, 2);
+    name(WER, 1) -> "die zwei Stiefel"
+
+    SetProp(P_NAME, "Stiefel"); SetProp(P_PLURAL, 1);
+    name(WER, 1) -> "die Stiefel"
+
+    // Ein Beispiel fuer das konjugieren von Verben
+    static int cmd_opfer(string str)
+    {
+       int i;
+       object *obs;
+       notify_fail("Was moechtest Du opfern?\n");
+       if (!str || !sizeof(obs=PL->find_obs(str))) return 0;
+       for (i=sizeof(obs)-1; i>=0; i--)
+          if (obs[i]->QueryProp(P_VALUE)<=0)
+            write(obs[i]->Name(WER)+" "
+                 +(ob->QueryProp(P_PLURAL) ? "sind" : "ist")
+                 +" doch gar nichts wert.\n");
+          else obs[i]->remove();
+    }
+
+26. Juni 2004 Gloinson
diff --git a/doc/props/P_POISON b/doc/props/P_POISON
new file mode 100644
index 0000000..2b7ea7b
--- /dev/null
+++ b/doc/props/P_POISON
@@ -0,0 +1,8 @@
+NAME:
+    P_POISON                      "poison"                      
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Wie stark wir vergiftet sind (0-11)
diff --git a/doc/props/P_POISON_DELAY b/doc/props/P_POISON_DELAY
new file mode 100644
index 0000000..aba57c6
--- /dev/null
+++ b/doc/props/P_POISON_DELAY
@@ -0,0 +1,13 @@
+NAME:
+    P_POISON_DELAY                     "poison_delay"                     
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Anzahl der heart_beats nach denen sich die Giftwirkung erneut 
+     zeigt. Je kleiner der Wert, desto empfindlicher ist das Lebewesen
+     gegen Gift.
+     Aenderungen dieser Property in Spielern beduerfen der Genehmigung
+     des EMs fuer Balance.
+     
diff --git a/doc/props/P_PORTIONS b/doc/props/P_PORTIONS
new file mode 100644
index 0000000..cb134e6
--- /dev/null
+++ b/doc/props/P_PORTIONS
@@ -0,0 +1,21 @@
+NAME:
+     P_PORTIONS                    "std_food_portions"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     In dieser Property steht die Anzahl der Portionen einer Speise.
+     Es duerfen nur Werte > -1 gesetzt werden. Ist diese Property 0,
+     wird die Speise als leer bzw. verbraucht angesehen und kann nicht
+     konsumiert werden.
+     
+DEFAULT:
+     Initial ist diese Property auf 1 gesetzt, die Speise ist also mit
+     einem Bissen/Schluck verbraucht bzw. leer.
+
+SIEHE AUCH:
+     /std/food.c, wiz/food
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_POST b/doc/props/P_POST
new file mode 100644
index 0000000..53a6f74
--- /dev/null
+++ b/doc/props/P_POST
@@ -0,0 +1,64 @@
+NAME:
+	P_POST				"Post"
+
+DEFINIERT IN:
+	/mail/post.h
+
+BESCHREIBUNG:
+	In dieser Property laesst sich die Versendeerlaubnis von Paketen
+	regeln. Hierbei gibt es zum einen die postlagernden Pakete, die man
+	in einer Post abholen muss, und es gibt die sogenannten
+	Kurierpakete, welche direkt und unmittelbar zugestellt werden.
+	Nicht immer ist es erwuenscht, dass Pakete aus der Ferne in einen
+	Raum geschickt werden duerfen. Dies duerfte insbesondere innerhalb
+	von Gebieten interessant sein, in welche man nur beschraenkt viele
+	Objekte mitfuehren kann. Mit dieser Property nun ist es leicht
+	moeglich, dies zu verbieten. Man kann auch in den Objekten selbst
+	angeben, ob diese per postlagerndem Paket bzw. Kurierpaket
+	verschickt werden duerfen. Dies duerfte zum Beispiel bei Komponenten
+	fuer Spells oder fuer Unique-Objekte interessant sein.
+	Folgende Werte sind moeglich, wobei in Raeumen und Objekten
+	Standardmaessig PP_DEFAULT genutzt wird:
+
+	  PP_FORBIDDEN		-2	// auf jeden Fall verboten
+	  PP_NO_EXPRESS		-1	// Kurierpakete verboten
+	  PP_DEFAULT		 0	// Default
+	  PP_NORMAL_ALLOWED	 1	// postlagernde Pakete erlaubt
+	  PP_ALLOWED		 2	// auf jeden Fall erlaubt
+
+	Raeume, die von /std/post.c abgeleitet wurden, nutzen als Standard
+	natuerlich PP_ALLOWED.
+
+BEISPIEL:
+	Um Kurierpakete fuer einen Raum zu verbieten, nutzt man die
+	Funktionalitaet dieser Property folgendermassen:
+
+	  include "/mail/post.h"
+	  ...
+	  void create()
+	  { ::create();
+	    ...
+	    SetProp(P_POST,PP_NO_EXPRESS);
+	    ...
+	  }
+
+	Objekte selbst koennte man folgendermassen aus Paketen verbannen,
+	welche versendet werden sollen:
+
+	  include "/mail/post.h"
+	  ...
+	  void create()
+	  { ::create();
+	    ...
+	    SetProp(P_POST,PP_FORBIDDEN);
+	    ...
+	  }
+
+	In letzterem Fall funktionieren im Gegensatz zum ersten Beispiel
+	auch keine postlagernden Pakete mehr.
+
+SIEHE AUCH:
+	/std/post.c, /std/mailcabin.c, /p/service/loco/std/mailcabin.c
+
+----------------------------------------------------------------------------
+Last modified: Sun Sep  6 19:34:37 1998 by Patryn
diff --git a/doc/props/P_POTIONROOMS b/doc/props/P_POTIONROOMS
new file mode 100644
index 0000000..5820be2
--- /dev/null
+++ b/doc/props/P_POTIONROOMS
@@ -0,0 +1,19 @@
+NAME:
+    P_POTIONROOMS                 "potionrooms"                 
+
+DEFINIERT IN:
+    /sys/player/potion.h
+
+BESCHREIBUNG:
+    Array mit den Nummern der Raeume, in denen der Spieler noch Zauber-
+    traenke hat. Die Freischaltung als bekannt geschieht im Orakel.
+    Die Zuordnung der Raeume und Nummern geschieht im Potionmaster.
+
+    Nur lesbare _query - Property.
+
+SIEHE AUCH:
+    Sonstiges: zaubertraenke, /secure/potionmaster.c, /room/orakel.c
+    Verwandt:  FindPotion(), AddKnownPotion(), RemoveKnownPotion(), InList()
+    Props:     P_KNOWN_POTIONROOMS
+
+6.Feb 2016 Gloinson
diff --git a/doc/props/P_PRAY_ROOM b/doc/props/P_PRAY_ROOM
new file mode 100644
index 0000000..3c8f9ff
--- /dev/null
+++ b/doc/props/P_PRAY_ROOM
@@ -0,0 +1,36 @@
+NAME:
+    P_PRAY_ROOM                      "_lib_p_pray_room"                  
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+    Dies ist der Raum, in den der Spieler nach dem Ende der Todessequenz
+    bewegt wird, d.h. ein Raum, wo er beten kann, um einen neuen Koerper zu
+    erhalten.
+    Der Raum muss als String angegeben werden (kein Objekt).
+
+    Diese Property wird im Spieler rebootfest gespeichert.
+
+    Der jeweils gueltige Betraum wird im Spieler mittels QueryPrayRoom()
+    ermittelt. Jede Rasse hat einen Default fuer diese Funktion. Wird die
+    Property auf 0 dgesetzt, wird dieser Default wiederhergestellt.
+
+    Von einer Ueberlagerung mittels Query- oder gar Setmethoden wird
+    abgeraten. Ebenso lasst bitte die Modusbits unveraendert.
+
+    Vor einer Aenderung dieser Property sollte geprueft werden, ob sie 0 ist.
+    Wenn nicht, sollte von einem Setzen der Property abgesehen werden, da dann
+    schon jemand anders den Betraum des Spielers geaendert hat. (Gleiches gilt
+    fuers Loeschen.) Bitte niemals den Inhalt der Property woanders speichern
+    und spaeter zurueckschreiben.
+
+    Eine dauerhafte Aenderung des Betraums des Spielers muss mit dem EM
+    Rassen und dem EM Gilden abgestimmt werden.
+
+SIEHE AUCH
+    QueryPrayRoom(), SetDefaultPrayRoom()
+
+LETZTE AeNDERUNG:
+21.05.2013, Zesstra
+
diff --git a/doc/props/P_PREFERED_ENEMY b/doc/props/P_PREFERED_ENEMY
new file mode 100644
index 0000000..eed58e6
--- /dev/null
+++ b/doc/props/P_PREFERED_ENEMY
@@ -0,0 +1,26 @@
+NAME:
+	P_PREFERED_ENEMY		"pref_enemy"
+
+DEFINIERT IN:
+	/sys/living/combat.h
+
+BESCHREIBUNG:
+	Diese Property enthaelt ein Array mit folgenden Eintraegen:
+	  Eintrag 1:      Integerwert zwischen 0 und 100, welcher die
+	                  Wahrscheinlichkeit dafuer angibt, dass ein
+	                  Lebewesen bevorzugt bei einem Angriff gewaehlt
+	                  werden soll.
+	  Eintraege ab 2: Lebewesen, aus welchen per Zufall eines
+	                  ausgewaehlt wird, welches beim aktuellen Angriff
+	                  bevorzugt wird.
+	Es ist zu beachten, dass solch ein bevorzugtes Opfer natuerlich auch
+	wirklich Gegner sein muss und auch im selben Raum zu stehen hat, wie
+	der Angreifer. Ist dies nicht der Fall, wird dann doch irgendein
+	anderer Gegner aus diesem Raum genommen.
+
+SIEHE AUCH:
+	QueryPreferedEnemy(), IsEnemy(), SelectEnemy(), Attack(),
+	/std/living/combat.c, /std/living/life.c
+
+----------------------------------------------------------------------------
+Last modified: Wed May 26 16:44:38 1999 by Patryn
diff --git a/doc/props/P_PRESAY b/doc/props/P_PRESAY
new file mode 100644
index 0000000..c5ec2d2
--- /dev/null
+++ b/doc/props/P_PRESAY
@@ -0,0 +1,9 @@
+NAME:
+    P_PRESAY                      "presay"                      
+
+DEFINIERT IN:
+    /sys/player/description.h
+
+BESCHREIBUNG:
+     Presay des Spielers. Erscheint vor dem Namen in Kurz/Langbeschreibung.
+     Erscheint auch in name(), also in sag, ruf, teile mit usw.
diff --git a/doc/props/P_PREVENT_PILE b/doc/props/P_PREVENT_PILE
new file mode 100644
index 0000000..39ddf14
--- /dev/null
+++ b/doc/props/P_PREVENT_PILE
@@ -0,0 +1,22 @@
+NAME:
+    P_PREVENT_PILE                   "prevent_pile"
+
+DEFINIERT IN:
+    /sys/container/properties.h
+
+BESCHREIBUNG:
+    Wenn in einem Raum diese Property gesetzt ist, endet das Verwesen einer
+    Leiche damit, dass ihr Inventar in dem Raum verteilt wird. Ist diese
+    Property nicht gesetzt, entsteht ein "Haufen", der das Inventar
+    enthaelt.
+
+    Diese Property sollte nur in Ausnahmefaellen benutzt werden!
+
+    Diese Property ist vor allem fuer "Store-Rooms" gedacht, in denen die
+    Magier die Leiche eines Spieler ablegen und in denen nachher die
+    gesammelten Sachen von einer anderen Stelle aus gesucht werden. In
+    diesem Fall wuerden Spieler sonst die Moeglichkeit haben, einen Haufen
+    als Ganzes zu bekommen, das soll aber *NIE* passieren.
+
+----------------------------------------------------------------------------
+Last modified: Tue May 15 13:56:18 CEST 2007 by Rumata
diff --git a/doc/props/P_PRE_INFO b/doc/props/P_PRE_INFO
new file mode 100644
index 0000000..48f1d9e
--- /dev/null
+++ b/doc/props/P_PRE_INFO
@@ -0,0 +1,55 @@
+NAME:
+        P_PRE_INFO                        "npc_pre_info"
+
+DEFINIERT IN:
+        /sys/npc.h
+
+BESCHREIBUNG:
+        Ist die Property in einem NPC definiert, so wird ihr Ergebnis
+        ausgewertet, bevor eine Frage an das Infosystem uebergeben wird.
+        
+        Moegliche Werte:
+        - numerischer Wert ungleich 0
+          => der NPC gibt _keinerlei_ Antwort, die Frage fuehrt sozusagen
+             ins Leere
+
+        - Stringwert
+          => wird als Rueckgabe an den Fragenden ausgegeben, umstehende 
+             Personen bekommen den Text:
+            "XY ist nicht gewillt, Spieler YZ zu antworten".
+            Der Fragende selbst bekommt bei angegebenem Stringwert:
+            "XY " + Stringwert.
+
+        - Closure
+          => die Antwort bzw. Reaktion des NPCs obliegt ganz der 
+             angegebenen Closure. Diese muss dabei einen String oder 
+             Ganzzahlen-Wert zurueckgeben
+
+BEISPIEL:
+        Ein NPC der manchmal herumrennt, um z.B. eine Aufgabe zu verrichten,
+        koennte so lange Chats abschalten, z.B.
+
+          SetProp(P_CHAT_CHANCE,0); // NPC latscht los
+        
+        Und eine Weile spaeter:
+        
+          SetProp(P_CHAT_CHANCE,5); // NPC ruht wieder, quasselt rum
+        
+        Waehrend des Herumlaufens, also wenn er nicht automatisch schwatzt,
+        soll er auch keinerlei Fragen beantworten:
+          
+          Set(P_PRE_INFO, function mixed () {
+            return (QueryProp(P_CHAT_CHANCE) ? 0 : 
+              "hat gerade keine Zeit fuer Dich."); 
+            }, F_QUERY_METHOD);
+
+HINWEISE:
+        Bitte beachten, dass der interne Name der Property "npc_pre_info" 
+        ist und somit nur das Ueberschreiben von _query_npc_pre_info() 
+        funktioniert. 
+
+SIEHE AUCH:
+        AddInfo(), /std/npc/info.c
+
+----------------------------------------------------------------------------
+Last modified: 01.03.2016 by Arathorn
diff --git a/doc/props/P_PROMPT b/doc/props/P_PROMPT
new file mode 100644
index 0000000..b380215
--- /dev/null
+++ b/doc/props/P_PROMPT
@@ -0,0 +1,8 @@
+NAME:
+    P_PROMPT                      "prompt"                      
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Das Prompt (Nur fuer Magier).
diff --git a/doc/props/P_PUB_NOT_ON_MENU b/doc/props/P_PUB_NOT_ON_MENU
new file mode 100644
index 0000000..07029b0
--- /dev/null
+++ b/doc/props/P_PUB_NOT_ON_MENU
@@ -0,0 +1,21 @@
+NAME:
+	P_PUB_NOT_ON_MENU			"pub_not_on_menu"
+
+DEFINIERT IN:
+	/sys/pub.h
+
+BESCHREIBUNG:
+        In diese Property kann man einen String schreiben, der ausgegeben
+        wird, wenn der Spieler etwas bestellt, was es in der Kneipe nicht
+        gibt.
+
+BEMERKUNGEN:
+        Die Standardmeldung ist:
+        "Tut mir leid, das fuehren wir nicht! Wir sind ein anstaendiges
+         Lokal!\n"
+
+SIEHE AUCH:
+	/std/room/pub.c
+
+----------------------------------------------------------------------------
+Last modified: Sat Mar 04 22:46:00 2000 by Paracelsus
diff --git a/doc/props/P_PUB_NO_KEEPER b/doc/props/P_PUB_NO_KEEPER
new file mode 100644
index 0000000..8f4c26e
--- /dev/null
+++ b/doc/props/P_PUB_NO_KEEPER
@@ -0,0 +1,19 @@
+NAME:
+	P_PUB_NO_KEEPER				"pub_no_keeper"
+
+DEFINIERT IN:
+	/sys/pub.h
+
+BESCHREIBUNG:
+        In diese Property kann man einen String schreiben, der ausgegeben
+        wird, wenn der in P_KEEPER eingetragene NPC nicht anwesend ist.
+
+BEMERKUNGEN:
+        Die Standardmeldung ist:
+        "Es ist niemand anwesend, der Dich bedienen koennte.\n"
+
+SIEHE AUCH:
+	/std/room/pub.c
+
+----------------------------------------------------------------------------
+Last modified: Son Apr 11 19:28:00 2010 by Caldra
diff --git a/doc/props/P_PUB_NO_MONEY b/doc/props/P_PUB_NO_MONEY
new file mode 100644
index 0000000..9ff293a
--- /dev/null
+++ b/doc/props/P_PUB_NO_MONEY
@@ -0,0 +1,21 @@
+NAME:
+	P_PUB_NO_MONEY				"pub_no_money"
+
+DEFINIERT IN:
+	/sys/pub.h
+
+BESCHREIBUNG:
+        In diese Property kann man einen String schreiben, der ausgegeben
+        wird, wenn der Spieler nicht ueber genug Geld verfuegt, um das
+        gewuenschte Gericht zu bezahlen.
+        Fuer den Preis kann man ein %d als Platzhalter einfuegen.
+
+BEMERKUNGEN:
+        Die Standardmeldung ist:
+        "Das kostet %d Goldstuecke, und Du hast nicht so viel!\n"
+
+SIEHE AUCH:
+	/std/room/pub.c
+
+----------------------------------------------------------------------------
+Last modified: Sat Mar 04 22:48:00 2000 by Paracelsus
diff --git a/doc/props/P_PUB_UNAVAILABLE b/doc/props/P_PUB_UNAVAILABLE
new file mode 100644
index 0000000..13aaa4f
--- /dev/null
+++ b/doc/props/P_PUB_UNAVAILABLE
@@ -0,0 +1,20 @@
+NAME:
+	P_PUB_UNAVAILABLE			"pub_unavailable"
+
+DEFINIERT IN:
+	/sys/pub.h
+
+BESCHREIBUNG:
+        In diese Property kann man einen String schreiben, der ausgegeben
+        wird, wenn in einer Kneipe ein Getraenk oder eine Speise nicht mehr
+        vorraetig ist.
+
+BEMERKUNGEN:
+        Die Standardmeldung ist:
+        "Davon ist leider nichts mehr da.\n"
+
+SIEHE AUCH:
+	/std/room/pub.c
+
+----------------------------------------------------------------------------
+Last modified: Sat Mar 04 22:44:00 2000 by Paracelsus
diff --git a/doc/props/P_PURSUERS b/doc/props/P_PURSUERS
new file mode 100644
index 0000000..c35bfe2
--- /dev/null
+++ b/doc/props/P_PURSUERS
@@ -0,0 +1,14 @@
+NAME:
+    P_PURSUERS                    "pursuers"                    
+
+DEFINIERT IN:
+    /sys/living/moving.h
+
+BESCHREIBUNG:
+     Enthaelt Verfolger - nicht von Hand manipulieren!
+
+SIEHE AUCH:
+     AddPursuer(L), RemovePursuer(L)
+
+-----------------------------------------------------------------------------
+16.06.2016, Arathorn
diff --git a/doc/props/P_PUT_MSG b/doc/props/P_PUT_MSG
new file mode 100644
index 0000000..5f6caef
--- /dev/null
+++ b/doc/props/P_PUT_MSG
@@ -0,0 +1,62 @@
+P_PUT_MSG
+NAME:
+     P_PUT_MSG				"put_message"
+
+DEFINIERT IN:
+     /sys/living/put_and_get.h
+
+BESCHREIBUNG:
+
+     Mit P_PUT_MSG kann man die Meldung, die man beim Hineinstecken eines
+     Objektes in einen Container bekommt, modifizieren.
+
+     Folgende Werte sind moeglich:
+
+     o 0
+       Es wird eine Standardmeldung ausgegeben. Dies ist Voreinstellung.
+
+     o NO_PNG_MSG	== -1
+       Es wird keinerlei Meldung ausgegeben
+
+     o Ein Array aus Strings
+       Der erste String wird an den Spieler ausgegeben, der zweite
+       (optionale) an den Raum.
+
+       Die Strings werden durch die Funktion replace_personal() geparst.
+	Objekt1 - Spieler
+        Objekt2 - das Objekt, das irgendwohin gesteckt wird
+	Objekt3 - der Container
+
+       Wird der zweite String nicht angegeben, erfolgt keine Meldung an den
+       Raum.
+
+BEISPIEL:
+     void create() {
+      ...
+      SetProp( P_SHORT, "Etwas Sand" );
+      SetProp( P_LONG, break_string(
+       "Ein wenig magischer Sand. Sehr feinkruemelig.",78 ));
+
+      SetProp( P_NAME, "Sand" );
+      AddId( ({"sand"}) );
+      SetProp( P_GENDER, MALE );
+
+      SetProp( P_PUT_MSG, ({
+       "Du laesst @WEN2 in @WENU3 rieseln.",
+       "@WER1 laesst @WEN2 in @WENU3 rieseln."}));
+      ...
+     }
+
+     Das ganze fuehrt bei Ugars "stecke sand in paket" zu folgenden
+     Meldungen:
+
+     Ugar: "Du laesst den Sand in ein Paket rieseln."
+     Raum: "Ugar laesst den Sand in ein Paket rieseln."
+
+SIEHE AUCH:
+     Aehnliches: P_PICK_MSG, P_DROP_MSG, P_GIVE_MSG, P_WEAR_MSG, P_WIELD_MSG
+     Fehler:     P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_TOO_MANY_MSG,
+                 P_NOINSERT_MSG, P_NOLEAVE_MSG, P_NODROP, P_NOGET 
+     Sonstiges:  replace_personal(E), put_obj(L), /std/living/put_and_get.c
+
+14. Maerz 2004 Gloinson
diff --git a/doc/props/P_QP b/doc/props/P_QP
new file mode 100644
index 0000000..08fb5a4
--- /dev/null
+++ b/doc/props/P_QP
@@ -0,0 +1,13 @@
+NAME:
+    P_QP                          "questpoints"                 
+
+DEFINIERT IN:
+    /sys/player/quest.h
+
+BESCHREIBUNG:
+     Anzahl der Questpunkte, die ein Spieler hat.
+
+HINWEIS:
+     Nur Abfragen der Property mit QueryProp() liefert den korrekten Wert,
+     da eine Quermethode fuer die Auslieferung der Daten sorgt. Query()
+     oder gar QueryProperties() enthalten u.U. einen veralteten Wert.
diff --git a/doc/props/P_QUALITY b/doc/props/P_QUALITY
new file mode 100644
index 0000000..3a4118c
--- /dev/null
+++ b/doc/props/P_QUALITY
@@ -0,0 +1,38 @@
+P_QUALITY
+
+NAME:
+     P_QUALITY "quality"
+
+DEFINIERT IN:
+     <combat.h>
+
+BESCHREIBUNG:
+     Ruestungen und Waffen koennen im Eifer des Gefechts beschaedigt werden.
+     Setzt man die Property P_QUALITY auf einen Wert n (n>0), so wird
+     eine Waffe (Ruestung) bei jedem n-ten Schlag (Treffer) beschaedigt,
+     d.h. P_WC (P_AC) wird um 1 erniedrigt und P_DAMAGED um 1 erhoeht.
+
+BEISPIEL:
+     inherit "/std/weapon";
+
+     ...
+     #include <combat.h>
+
+     create()
+     {
+       ...
+       SetProp(P_QUALITY,200);
+       ...
+     }
+
+     Diese Waffe wuerde bei jedem 200. Schlag etwas beschaedigt.
+
+BEMERKUNG:
+     Defaultmaessig ist P_QUALITY auf 0 gesetzt, d.h. die entspr.
+     Waffe/Ruestung wird nicht beschaedigt.
+
+SIEHE AUCH:
+     /std/armour.c, /std/weapon.c, TakeFlaw(), QueryFlaw(), Damage()
+
+----------------------------------------------------------------------------
+Last modified: Thu May 22 10:42:39 1997 by Paracelsus
diff --git a/doc/props/P_QUESTS b/doc/props/P_QUESTS
new file mode 100644
index 0000000..446ea90
--- /dev/null
+++ b/doc/props/P_QUESTS
@@ -0,0 +1,8 @@
+NAME:
+    P_QUESTS                      "quests"                      
+
+DEFINIERT IN:
+    /sys/player/quest.h
+
+BESCHREIBUNG:
+     Liste der geloesten Quests.
diff --git a/doc/props/P_QUEST_ITEM b/doc/props/P_QUEST_ITEM
new file mode 100644
index 0000000..e4eebb5
--- /dev/null
+++ b/doc/props/P_QUEST_ITEM
@@ -0,0 +1,46 @@
+NAME:
+	P_QUEST_ITEM				"quest_item" 
+
+DEFINIERT IN:
+	/sys/quest_items.h
+
+BESCHREIBUNG:
+        Diese Property gibt an, ob es sich bei dem Objekt um ein Quest-
+	relevantes Objekt handelt.
+	
+BEISPIEL:
+        Ein Schwert (Notung) koennte folgendermassen definiert sein:
+
+	create()
+	{
+	  ::create() ;
+	  SetProp(P_SHORT, "Notung das neidliche Schwert") ;
+	  SetProp(P_LONG, "Das aus seinen Truemmern neu geschmiedete Schwert " 
+	                  "Notung.\n");
+
+	  SetProp(P_NAME, "Notung");
+	  SetProp(P_GENDER, NEUTER);
+	  SetProp(P_ARTICLE,0);
+	  AddId(({"notung","schwert","Notung", "\nNotung"}));
+	
+	  SetProp(P_WEAPON_TYPE, WT_SWORD);
+	  SetProp(P_DAM_TYPE, DT_BLUDGEON);
+
+	  SetProp(P_QUEST_ITEM,QI_OBJ);
+	  ...
+	}
+	    
+	Andere Magier koennen nun auf Notung Ruecksicht nehmen, und zum
+	Beispiel davon absehen, es bei einem NPC-Spell zu destructen.
+
+BEMERKUNGEN:
+        Alle questrelevanten Objekte sollten auf diese Weise markiert werden.
+	
+	Questrelevante Objekte anderer Magier sollten immer mit Vorsicht 
+	behandelt werden.
+
+SIEHE AUCH:
+	"/sys/quest_items.h"
+
+----------------------------------------------------------------------------
+Last modified: Thu Jul 10 07:08:32 2003 by Vanion
diff --git a/doc/props/P_RACE b/doc/props/P_RACE
new file mode 100644
index 0000000..7ed77cd
--- /dev/null
+++ b/doc/props/P_RACE
@@ -0,0 +1,30 @@
+NAME:
+	P_RACE				"race"
+
+DEFINIERT IN:
+	/sys/living/description.h
+
+BESCHREIBUNG:
+	Die Rasse eines Lebewesens kann ueber diese Property ermittelt bzw.
+	gesetzt werden. Es empfiehlt sich hierbei, Rassen nur in Form von
+	grossgeschriebenen Strings zu setzen. Leichen erhalten mittels
+	dieser Property automatisch die Rasse der Lebewesen, aus denen sie
+	hervorgegangen sind.
+	Der Sinn des Ganzen liegt darin, das Spiel differenzierter zu
+	gestalten und auf Rassenspezifika einzugehen. Zum Beispiel koennen
+	Elfen weniger Alkohol vertragen als Zwerge, was in '/std/pub'
+	beruecksichtigt wurde.
+
+BEISPIEL:
+	void create()
+	{ ::create();
+	  // Definitionen
+	  SetProp(P_RACE,"Ork");
+	  // weitere Definitionen
+	}
+
+SIEHE AUCH:
+	/std/npc.c, /std/pub.c
+
+----------------------------------------------------------------------------
+Last modified: Mon Sep 15 21:15:49 2003 by Vanion
diff --git a/doc/props/P_RACESTRING b/doc/props/P_RACESTRING
new file mode 100644
index 0000000..b75f46b
--- /dev/null
+++ b/doc/props/P_RACESTRING
@@ -0,0 +1,9 @@
+NAME:
+    P_RACESTRING                  "racestring"                  
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Gibt eine dem Geschlecht angepasste Beschreibung der Rasse zurueck
+     ("Zwerg" oder "Zwergin" etc.)
diff --git a/doc/props/P_RACE_DESCRIPTION b/doc/props/P_RACE_DESCRIPTION
new file mode 100644
index 0000000..5b2ebfb
--- /dev/null
+++ b/doc/props/P_RACE_DESCRIPTION
@@ -0,0 +1,8 @@
+NAME:
+    P_RACE_DESCRIPTION            "racedescr"                   
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Beschreibung der Vor/Nachteile einer Rasse.
diff --git a/doc/props/P_RANGE b/doc/props/P_RANGE
new file mode 100644
index 0000000..37a98b7
--- /dev/null
+++ b/doc/props/P_RANGE
@@ -0,0 +1,26 @@
+P_RANGE
+
+NAME:
+    P_RANGE     "range"
+
+DEFINIERT IN:
+    <combat.h>
+
+BESCHREIBUNG:
+    Legt die Reichweite einer Schusswaffe fest. Ist ein Schuetze in einem
+    Raum mit gueltigem P_TARGET_AREA (andere Raum) oder environment() und
+    ist fuer diesen Raum P_SHOOTING_AREA festgelegt, dann kann er mit seiner
+    Schusswaffe in diesen anderen Raum schießen, wenn die P_RANGE groesser
+    als P_SHOOTING_AREA ist.
+
+BEISPIELE:
+    // Langbogen mit 100 Reichweite
+    SetProp(P_RANGE, 100);
+
+SIEHE AUCH:
+    Generell:  P_AMMUNITION, P_SHOOTING_WC, P_STRETCH_TIME
+    Methoden:  FindRangedTarget(L), shoot_dam(L), cmd_shoot(L)
+    Gebiet:    P_SHOOTING_AREA, P_TARGET_AREA
+    Sonstiges: fernwaffen
+
+29.Jul 2014 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_READ_DETAILS b/doc/props/P_READ_DETAILS
new file mode 100644
index 0000000..035a2cc
--- /dev/null
+++ b/doc/props/P_READ_DETAILS
@@ -0,0 +1,21 @@
+NAME:
+    P_READ_DETAILS                "read_details"                
+
+DEFINIERT IN:
+    /sys/thing/description.h
+
+BESCHREIBUNG:
+    Mapping mit den Daten der Details, die durch Lesen ermittelt werden
+    koennen.
+
+BEMERKUNGEN:
+    Bitte nur mit den entsprechenden Methoden veraendern!
+
+SIEHE AUCH:
+    Setzen:    AddReadDetail()
+    Loeschen:  RemoveReadDetail()
+    Aehnlich:  AddDetail(), P_DETAILS
+    Veraltet:  P_READ_MSG
+    Sonstiges: GetDetail(), break_string()
+
+27. Jan 2013 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_READ_NEWS b/doc/props/P_READ_NEWS
new file mode 100644
index 0000000..b64a252
--- /dev/null
+++ b/doc/props/P_READ_NEWS
@@ -0,0 +1,8 @@
+NAME:
+    P_READ_NEWS                   "read_news"                   
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Welche Artikel bereits gelesen wurde (frueher: in der MPA)
diff --git a/doc/props/P_REAL_RACE b/doc/props/P_REAL_RACE
new file mode 100644
index 0000000..88279a6
--- /dev/null
+++ b/doc/props/P_REAL_RACE
@@ -0,0 +1,72 @@
+NAME:
+	P_REAL_RACE				"real_race"
+
+DEFINIERT IN:
+	/sys/living/description.h
+
+BESCHREIBUNG:
+        Diese Property enthaelt die Rasse des Livings. Sie darf nicht durch 
+	Shadows ueberschrieben werden.
+	
+	Wirklich interessant ist sie, wenn ein Spieler sich tarnt. Dort kann
+	man mit dieser Property trotz Tarnung feststellen, welche Rasse der
+	Spieler hat.
+
+	Bei NPC enthaelt sie den gleichen Wert wie P_RACE. Wenn P_REAL_RACE
+	allerdings gesetzt wird, kann man damit einen getarnten NPC simu-
+	lieren, da dann P_RACE und P_REAL_RACE voneinander abweichen.
+	
+BEISPIEL:
+        Ein Zwerg mag Zwergenbrot, fuer Elfen ist es giftig. Selbst wenn der
+	Elf sich als Zwerg tarnt, wird ihm durch lembas sicher uebel werden:
+
+        int futter(string arg)
+        {
+          notify_fail("Was willst Du essen?\n");
+          if(!arg || !id(arg)) return 0;
+
+          notify_fail("Du kannst nichts mehr essen.\n");
+          if(!this_player()->eat_food(55)) return 0;
+
+          write("Du isst ein Stueck Zwegenbrot. Du versuchst es zumindest!\n");
+          say(sprintf("%s beisst in ein Stueck Zwergenbrot. Zahnschmerz!!!\n",
+              this_player()->Name()));
+
+
+          switch( this_player()->QueryProp(P_REAL_RACE) )
+          {
+          case "Zwerg":
+	    if ((this_player()->QueryProp(P_RACE))!="Zwerg")
+	      write("Zur Tarnung spuckst Du etwas von dem Brot aus!\n"); 
+            this_player()->buffer_hp(100,10);
+            this_player()->buffer_sp(100,10);
+            break;
+
+          case "Elf":
+	    write("Das Zwergenbrot brennt wie Feuer auf Deiner Zunge!");
+	    // Getarnt?
+	    if ((this_player()->QueryProp(P_RACE))!="Elf")
+	      write(" Deine Tarnung nutzt Dir da wenig.\n"
+            else write("\n");
+            this_player()->restore_spell_points(-100);
+            this_player()->do_damage(100,this_object());
+            break;
+
+          default:
+	    write("Du bekommst nur wenig davon herunter..\n");
+            this_player()->buffer_hp(10,1);
+            this_player()->buffer_sp(10,2);
+            break;
+          }
+  
+          remove();
+  
+          return 1;
+        }
+
+	
+SIEHE AUCH:
+	/std/living/description.c, /sys/living/description.h, P_RACE
+
+----------------------------------------------------------------------------
+Last modified: Mon Sep 15 21:15:49 2003 by Vanion
diff --git a/doc/props/P_REFERENCE_OBJECT b/doc/props/P_REFERENCE_OBJECT
new file mode 100644
index 0000000..e2a6405
--- /dev/null
+++ b/doc/props/P_REFERENCE_OBJECT
@@ -0,0 +1,27 @@
+P_REFERENCE_OBJECT
+NAME:
+     P_REFERENCE_OBJECT            "reference_object"
+
+DEFINIERT IN:
+     /sys/player/description.h
+
+BESCHREIBUNG:
+     In dieser Propertie wird das aktuelle Bezugsobjekt eines Spielers 
+     gespeichert. Untersucht der Spieler ein Monster, so ist dies das 
+     Monsterobjekt, untersucht der Spieler sich selber, ist es das 
+     Spielerobjekt.
+
+     Der Inhalt dieser Propertie besteht also immer aus dem zuletzt 
+     betrachteten Objekt. Sei es ein Raum, eine Ruestung, ein Monster oder 
+     was auch immer. Bewegungsbefehle und andere Kommandos werden nicht 
+     beruecksichtigt.
+
+     Einzig wenn der Spieler 'schau' tippt, wird der Inhalt der Propertie 
+     geloescht und betraegt 0.
+
+SIEHE AUCH:
+     Sonstiges:		P_INT_LONG, P_LONG, P_SHORT
+			int_long(), long(), short(), make_invlist()
+
+----------------------------------------------------------------------------
+Letzte Aenderung: 29.06.02, 23:50:00 h, von Tilly
diff --git a/doc/props/P_REJECT b/doc/props/P_REJECT
new file mode 100644
index 0000000..4af07f3
--- /dev/null
+++ b/doc/props/P_REJECT
@@ -0,0 +1,51 @@
+NAME:
+	P_REJECT			"reject"
+
+DEFINIERT IN:
+	/sys/properties.h
+
+BESCHREIBUNG:
+	Diese Property zeigt standardmaessig nur in NPCs eine Wirkung. Mit
+	ihr laesst sich sehr einfach einstellen, wie sich ein solcher NPC
+	gegenueber Gegenstaenden verhalten soll, welche ihm zugesteckt
+	werden. Hierbei besteht die Property aus 2 Elementen, welche
+	bestimmt, was der NPC mit Dingen tuen soll, die ihm gegeben werden.
+	Standardmaessig behaelt der NPC die Sachen einfach.
+	Folgende Moeglichkeiten gibt es ausserdem:
+	  1. Arrayelement: Art der Handlung. (aus "/sys/moving.h")
+	    REJECT_GIVE: Der NPC gibt das Objekt zurueck.
+	    REJECT_DROP: Der NPC laesst das Objekt fallen.
+	    REJECT_KEEP: Der NPC behaelt das Objekt doch.
+	    REJECT_LIGHT_MODIFIER: Der NPC nimmt keine Gegenstaende an, die
+	      sein Lichtlevel veraendern und damit Einfluss auf sein
+	      Kampfverhalten haben koennten.
+	  2. Arrayelement: Meldung, mit welcher der NPC die Handlung
+	                   kommentiert.
+	    Der Meldung wird nichts automatisch vorangestellt und ein
+	    abschliessender Zeilenumbruch ist ebenfalls selbstaendig
+	    vorzunehmen. Mit einem Leerstring ist somit auch gar keine
+	    Rueckmeldung moeglich.
+
+BEISPIEL:
+	Der NPC schmeisst alle ihm gegebenen Gegenstaende einfach weg:
+	  void create()
+	  { ::create();
+	    ...
+	    SetProp(P_REJECT,({REJECT_GIVE,
+	    Name(WER)+" murmelt: Was soll ich denn damit?!\n"}));
+	    ...
+	  }
+	Manchmal ist das recht nuetzlich, z.B. kann man so eigentlich schwer
+	zu toetende NPCs dagegen schuetzen, dass man ihnen angezuendetes
+	Dynamit oder aehnliches ueberreicht.
+
+BEMERKUNGEN:
+	Innerhalb von NPCs ist die Funktion give_notify() fuer die
+	automatische Auswertung dieser Property verantwortlich; das sollte
+	man insbesondere beim Ueberschreiben dieser Funktion beachten!
+
+SIEHE AUCH:
+	give_notify(), /std/npc/put_and_get.c
+
+-----------------------------------------------------------------------------
+Last modified: Mon Apr 23 16:54:07 2001 by Patryn
diff --git a/doc/props/P_REMOVE_FUNC b/doc/props/P_REMOVE_FUNC
new file mode 100644
index 0000000..2d431fa
--- /dev/null
+++ b/doc/props/P_REMOVE_FUNC
@@ -0,0 +1,25 @@
+P_REMOVE_FUNC
+
+NAME:
+     P_REMOVE_FUNC "remove_func"
+
+DEFINIERT IN:
+     <armour.h>
+
+BESCHREIBUNG:
+     Falls ein Objekt eine RemoveFunc() fuer die Ruestung oder Kleidung 
+     definiert, so muss dieses Objekt in dieser Property eingetragen sein.
+
+     Die Auswertung dieser Property erfolgt im Laufe des DoUnwear() in
+     der nicht-oeffentlichen Funktionen _check_unwear_restrictions().
+
+BEISPIELE:
+     Siehe das Beispiel zu RemoveFunc()
+
+SIEHE AUCH:
+     /std/armour.c, /std/clothing.c, clothing, armours
+     RemoveFunc()
+
+
+Letzte Aenderung:
+15.02.2009, Zesstra
\ No newline at end of file
diff --git a/doc/props/P_REMOVE_MSG b/doc/props/P_REMOVE_MSG
new file mode 100644
index 0000000..9eb1d8d
--- /dev/null
+++ b/doc/props/P_REMOVE_MSG
@@ -0,0 +1,28 @@
+NAME:
+     P_REMOVE_MSG                  "std_food_remove_msg"
+
+DEFINIERT IN:
+     <sys/food.h>
+
+BESCHREIBUNG:
+     Meldung, wenn eine verdorbene Speise gerade vernichtet wird.
+     Diese Meldung erscheint nur, wenn in P_DESTROY_BAD ein positiver
+     Integer-Wert gesetzt ist.
+     Befindet sich die Speise in einem Spieler, geht die Meldung an genau
+     diesen, liegt die Speise im Raum, geht die Meldung an alle anwesenden
+     Spieler.
+     
+BEMERKUNGEN:
+     Diese Meldung wird von replace_personal mit den Argumenten:
+     1. Speise
+     2. Konsument
+     verarbeitet, kann als entsprechende Platzhalter enthalten
+     
+DEFAULT:
+     "@WER1 zerfaellt zu Staub."
+
+SIEHE AUCH:
+     P_DESTROY_BAD, wiz/food, replace_personal
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_RESET_LIFETIME b/doc/props/P_RESET_LIFETIME
new file mode 100644
index 0000000..378cf0c
--- /dev/null
+++ b/doc/props/P_RESET_LIFETIME
@@ -0,0 +1,31 @@
+NAME:

+     P_RESET_LIFETIME              "std_food_lifetime_reset"

+

+DEFINIERT IN:

+     /sys/food.h

+

+BESCHREIBUNG:

+     Zeit in Resets, die die Speise haltbar ist.

+     Jeder einzelne Reset wird in seiner Laenge zufaellig festgelegt und

+     zu einer Gesamtanzahl von Sekunden zusammengefasst. Diese Zeit in

+     Sekunden wird dann in P_LIFETIME gespeichert.

+     Nach dieser Zeit wird diese Speise schlecht und je nach Konfiguration

+     der Speise eventuell zerstoert. Sichergestellt wird dies durch den

+     Aufruf von reset() nach dieser Anzahl "Resets".

+     Moechte man eine Speise haben, die niemals verdirbt

+     (genehmigungspflichtig!), nutzt man die Property P_NO_BAD

+     

+BEMERKUNGEN:

+     Sobald der Countdown zum Schlechtwerden der Speise laeuft, also ein

+     Spieler damit in Beruehrung kam, zeigt SetProp auf diese Property keine

+     Wirkung mehr.

+

+DEFAULT:

+     Ohne Setzen von P_LIFETIME ,P_RESET_LIFETIME und P_NO_BAD verdirbt die

+     Speise nach einem Reset, also zwischen 30 und 60 Minuten

+

+SIEHE AUCH:

+     wiz/food, P_LIFETIME, P_NO_BAD

+

+------------------------------------------------------------------------------

+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_RESISTANCE b/doc/props/P_RESISTANCE
new file mode 100644
index 0000000..ff3de74
--- /dev/null
+++ b/doc/props/P_RESISTANCE
@@ -0,0 +1,37 @@
+P_RESISTANCE
+NAME:
+     P_RESISTANCE                  "resistance"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+WICHTIG:
+     DIESE PROPERTY IST VERALTET! BITTE P_RESISTANCE_STRENGTHS
+     VERWENDEN! AUCH FUNKTIONIERT Set() NICHT WIE ES SOLLTE.
+
+BESCHREIBUNG:
+     Hiermit koennen die Resistenzen eines Lebewesens sehr einfach gesetzt
+     werden. Es kann ein Array mit Schadensarten gesetzt werden, jeder
+     Eintrag eines Schadens verdoppelt die Resistenz gegen diesen.
+
+BEMERKUNGEN:
+     - P_RESISTANCE_STRENGTHS spiegelt diese Eintraege hier wieder
+     - um genauere Werte anzugeben einen AddResistanceModifier() oder
+       P_RESISTANCE_STRENGTHS benutzen.
+     - P_RESISTANCE kann und wird nicht aus P_RESISTANCE_STRENGTHS
+       upgedatet
+
+BEISPIELE:
+     // ein NPC mit halbierter Feuerempfindlichkeit und
+     // geviertelter Windempfindlichkeit
+     SetProp(P_RESISTANCE, ({DT_FIRE, DT_AIR, DT_AIR}));
+
+SIEHE AUCH:
+     simple Resistenz:	P_RESISTANCE
+     Modifikatoren:	AddResistanceModifier, RemoveResistanceModifier(),
+			P_RESISTANCE_MODIFIER
+     Hauptmapping:	P_RESISTANCE_STRENGTHS
+     Berechnung:	CheckResistance(), UpdateResistanceStrengths()
+     anderes:		balance, /std/armour/combat.c, /std/living/combat.c
+
+1.Dez 2004, Gloinson
diff --git a/doc/props/P_RESISTANCE_MODIFIER b/doc/props/P_RESISTANCE_MODIFIER
new file mode 100644
index 0000000..0a0538e
--- /dev/null
+++ b/doc/props/P_RESISTANCE_MODIFIER
@@ -0,0 +1,26 @@
+NAME:
+     P_RESISTANCE_MODIFIER             "rstr:mod"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+     Hier werden die Resistenzmodifikatoren in einem Mapping abgespeichert.
+
+     Format:
+
+     (["me":<Aufaddition aller Resistenz/Empfindlichkeitsmodifikationen>;0,
+       "<Objektname>#<Schluessel>":<Resistenzmapping>;<Objekreferenz>,
+       ...])
+
+BEMERKUNGEN:
+     Nur ueber AddResistanceModifier(), RemoveResistanceModifier() aendern!
+
+SIEHE AUCH:
+     Modifikatoren:	AddResistanceModifier, RemoveResistanceModifier()
+     simple Resistenz:	P_RESISTANCE, P_VULNERABILITY
+     Hauptmapping:	P_RESISTANCE_STRENGTHS
+     Berechnung:	CheckResistance(), UpdateResistanceStrengths()
+     anderes:		balance, /std/armour/combat.c, /std/living/combat.c
+
+29.Apr 2002, Gloinson@MG
diff --git a/doc/props/P_RESISTANCE_STRENGTHS b/doc/props/P_RESISTANCE_STRENGTHS
new file mode 100644
index 0000000..ed24d2d
--- /dev/null
+++ b/doc/props/P_RESISTANCE_STRENGTHS
@@ -0,0 +1,92 @@
+NAME:
+     P_RESISTANCE_STRENGTHS     "resistance_strengths"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+BESCHREIBUNG:
+     Lebewesen:
+
+     Mapping mit Schadensarten, gegen die das Lebewesen resistent oder
+     anfaellig ist. Durch eine _query_method werden alle existierenden
+     Resistenzen hier zusammengefasst.
+
+     Die Staerke der Resistenz oder Anfaelligkeit wird numerisch aus-
+     gedrueckt:
+     - Resistenzen gehen bis von 0 bis -1.0 (absolute Resistenz)
+       - -0.5 == halbierter Schaden, -0.75 == geviertelter Schaden
+       -> in % des "aufgehaltenen" Schadens interpretierbar
+     - Empfindlichkeiten gehen von 0 bis MAX_INT
+       -  1.0 == verdoppelter Schaden, 3.0 == vervierfachter Schaden
+       -> als Faktor (x+1.0) interpretierbar
+
+     Ruestungen:
+
+     Mapping mit Prozentwerten der Maximalwerte fuer Ruestungen des
+     entsprechenden Typs. Dabei sind positive Werte Resistenzen und
+     negative Empfindlichkeiten. Dabei duerfen die Prozentwerte nur
+     im Bereich von +100 bis -800 (-1000 ?!) liegen.
+
+     Bei Ruestungen ist es damit unnoetig, Resistenzen mittels
+     AddResistanceModifier() im Traeger zu setzen, da dies bereits
+     in /std/armour automatisch gemacht wird. Der Schluessel fuer
+     den Eintrag ist dabei P_ARMOUR_TYPE.
+
+     Die Maximalwerte sind derzeit:
+      AT_CLOAK, AT_RING, AT_AMULET: max 10% Resistenz
+      AT_SHIELD, AT_ARMOUR:  max 15% Resistenz
+      alle anderen:   max 5% Resistenz
+
+BEMERKUNGEN:
+     Ruestungen:
+     * die Property muss _nach_ P_ARMOUR_TYPE gesetzt werden (_set-Method)
+
+     Lebewesen:
+     * -1.0 bedeutet bereits absolute Resistenz, bei kleineren Werten werden
+       die anderen Schadensanteile im Kampf u.U. nicht mehr wie gewuenscht
+       bilanziert. Bitte daher drauf verzichten. ;-)
+     * Aenderungen an P_RESISTANCE und P_VULNERABILITY werden direkt in 
+       P_RESISTANCE_STRENGTHS gespeichert:
+       -> daher niemals im Nachhinein bei fremden NPCs P_RESISTANCE_STRENGTHS
+          manipulieren, dafuer gibt es AddResistanceModifier
+     * QueryProp(P_RESISTANCE_STRENGTHS) enthaelt die gesamten Resistenzen
+       P_RESISTANCE, P_VULNERABILITY, P_RESISTANCE_MODIFIER (_query-Method)
+
+     Die Werte in P_RESISTANCE_STRENGTHS sollten nur in Ausnahmefaellen oder
+     gut begruendet den Hoechstwert haben. Ein Eiswesen kann zwar sehr
+     resistent gegen Kaelteschaden sein, sollte aber zu gleichem Teil auch
+     anfaellig auf Feuerschaden sein.
+
+     Auf dieser Property liegt eine Querymethode, welche eine Kopie der
+     Daten zurueckliefert.
+
+BEISPIELE:
+     // ein Eistroll
+     SetProp(P_RESISTANCE_STRENGTHS,([DT_FIRE:0.5,DT_COLD:-0.5,
+                                      DT_MAGIC:0.1]));
+
+     Feuerangriffe werden mit 1.5 multipliziert, magische mit 1.1 und
+     der Schadenswert von Kaelteangriffen wird halbiert. Zum Beispiel
+     wuerde
+     Defend(100, ({DT_FIRE,DT_MAGIC}), ...)
+     einen Schaden von 130 statt den normalen 100 verursachen.
+
+
+     // eine Eisruestung
+     SetProp(P_RESISTANCE_STRENGTHS, ([DT_COLD:50, DT_FIRE:-100]));
+
+     Dieses Kettenhemd schuetzt nun mit 50% des Maximalwertes gegen
+     Kaelte (also 0.5*15%=7,5% Resistenz) und macht mit dem Maximal-
+     wert anfaellig gegen Feuer (1*15%=15% Empfindlichkeit).
+
+     ! der Code spricht zusaetzlich von
+       Empfindlichkeit=(Empfindlichkeit/4)*5 -> 15/4*5=18.75% !
+
+SIEHE AUCH:
+     simple Resistenz: P_RESISTANCE, P_VULNERABILITY
+     Modifikatoren: AddResistanceModifier, RemoveResistanceModifier(),
+     P_RESISTANCE_MODIFIER
+     Berechnung: CheckResistance(), UpdateResistanceStrengths()
+     anderes:  balance, /std/armour/combat.c, /std/living/combat.c
+
+6.Feb 2016 Gloinson
diff --git a/doc/props/P_RESTRICTIONS b/doc/props/P_RESTRICTIONS
new file mode 100644
index 0000000..34890ff
--- /dev/null
+++ b/doc/props/P_RESTRICTIONS
@@ -0,0 +1,140 @@
+NAME:
+    P_RESTRICTIONS                                "restrictions"
+
+DEFINIERT IN:
+    /sys/combat.h
+    (Die SR_*-Parameter sind in /sys/new_skills.h definiert.)
+
+BESCHREIBUNG:
+    Enthaelt ein mapping mit den zu pruefenden Einschraenkungen.
+
+    In dieser Property lassen sich Restriktionen setzen, die vor dem
+    Zuecken einer Waffe / Anziehen einer Ruestung oder Kleidung geprueft
+    werden und dies gegebenfalls verhindern, ohne gleich auf eine evtl.
+    vorhandene WieldFunc / WearFunc zuzugreifen.
+
+    Die Auswertung erfolgt ueber den Aufruf von check_restrictions()
+    in /std/restriction_checker.c
+
+    Folgende Keys werden in dem Mapping ausgewertet:
+
+    P_LEVEL
+      Mindeststufe, die das Lebewesen besitzen muss, um die Aktion
+      auszufuehren.
+    P_GUILD_LEVEL
+      Gildenlevel, das das Lebewesen mindestens erreicht haben muss, um die
+      Aktion auszufuehren.
+    SR_SEER
+      Ist gesetzt, wenn das Lebewesen Seher sein muss.
+      Auswertung nur fuer Interactives, NSC ignorieren das Flag.
+    P_XP
+      Mindestmenge an Erfahrungspunkten, die ein Lebewesen besitzen muss,
+      um die Aktion auszufuehren.
+    P_QP
+      Mindestmenge an Abenteuerpunkten, die das Lebewesen haben muss.
+    P_ALCOHOL
+      Menge an Alkohol, unter der der Alkoholspiegel des Lebewesen liegen
+      muss, um die Aktion noch ausfuehren zu koennen.
+    P_DRINK
+      Menge an Fluessigkeit, unter der der Fluessigkeitsspiegel des
+      Lebewesen liegen muss, um die Aktion noch ausfuehren zu koennen.
+    P_FOOD
+      Beinhaltet die Menge an Nahrung, unter der der Nahrungsspiegel des
+      Spielers liegen muss, um die Aktion noch ausfuehren zu koennen.
+    P_DEAF
+      Ist gesetzt, falls der Spieler nicht taub sein darf.
+    P_FROG
+      Ist gesetzt, falls der Spieler kein Frosch sein darf.
+    P_BLIND
+      Ist gesetzt, falls der Spieler nicht blind sein darf.
+      Achtung: das ist nicht gleichbedeutend mit dem Umstand, dass er evtl.
+      nichts mehr sehen kann. Auch andere Gruende (zum Beispiel Dunkelheit)
+      koennen bewirken, dass ein Spieler nichts mehr sieht.
+    A_INT, A_DEX, A_CON, A_STR
+      Jeweilige Mindesthoehe eines Attribut, um eine Aktion ausfuehren zu
+      koennen.
+    SR_BAD, SR_GOOD
+      Gibt an, wie [minimal] boese bzw. wie [maximal] gut ein Charakter sein
+      darf, um eine Aktion ausfuehren zu koennen.
+    SR_MIN_SIZE, SR_MAX_SIZE
+      Gibt die minimale, bzw. die maximale Groesse an, die ein Charakter
+      maximal haben darf, um eine Aktion ausfuehren zu koennen.
+    SR_FREE_HANDS
+      Gibt an, wieviele freie Haende ein Charakter fuer diese Aktion
+      besitzen muss.
+    SR_EXCLUDE_RACE
+      Mitglieder aller in dieser Liste aufgefuehrten Rassen koennen
+      diese Aktion nicht ausfuehren.
+    SR_INCLUDE_RACE
+      Mitglieder aller NICHT in dieser Liste aufgefuehrten Rassen koennen
+      diese Aktion nicht ausfuehren.
+    SM_RACE
+      Hier kann pro Rasse ein Mapping mit besonderen (nur) fuer diese Rasse
+      geltenden Einschraenkungen vorgenommen werden. Als Keys sind die
+      in dieser Manpage beschriebenen Keys erlaubt, wobei SM_RACE nicht
+      rekursiv ausgewertet wird.
+      Der Rassenname ist gross geschrieben und "*" steht fuer alle Rassen.
+    SR_EXCLUDE_GUILD
+    SR_INCLUDE_GUILD
+      Diese beiden Keys verhalten sich wie SR_*_RACE, nur dass hier Gilden
+      genannt werden.
+    SR_FUN
+      Hier kann eine Funktion in verschiedenen Formen zum Pruefen der
+      Restriktionen angegeben werden, siehe execute_anything().
+      Das kann nuetzlich sein, um andere Restriktionen zu pruefen,
+      wie das Bestehen von Miniquests oder andere Faehigkeiten/Flags.
+      Ist der Test nicht bestanden, gibt die Funktion einen String zurueck.
+    SR_PROP
+      Hier kann ein Mapping mit Properties und zugehoerigen Werten angegeben
+      werden, die jeweils auf Identitaet geprueft werden. Zusaetzlich sollte
+      eine Meldung angegeben werden, die als Fehlermeldung ausgegeben wird,
+      wenn der Spieler die Bedingung nicht erfuellt. Es sollte immer eine
+      passende Meldung fuer den Spieler eingebaut werden. Beispiel:
+      ([ SR_PROP: ([P_AUSGANG_ENTDECKT: 1; "Dein Schwert fluestert "
+          "veraergert: Ich werde Dir erst dann zu Diensten sein, wenn Du "
+          "Dich als wuerdig erwiesen hast!"]) ])
+      Aufgrund der Meldung wird empfohlen, SR_PROP nicht in Restriktionen 
+      einzusetzen, die massenweise in Savefiles landen (z.B. 
+      Spielersavefiles).
+    SR_QUEST
+      Hier kann ein String-Array mit den Namen (Keys) der Quest(s) angegeben
+      werden, die der Spieler bestanden haben muss, um die Aktion ausfuehren
+      zu koennen.
+    SR_MINIQUEST
+      Hier kann entweder ein String-Array mit den Ladenamen der vergebenden
+      Objekte oder ein Int-Array mit den Index-Nummern (IDs) der
+      Miniquest(s) (empfohlen!) angegeben werden, die der Spieler bestanden
+      haben muss, um die Aktion ausfuehren zu koennen.
+
+
+
+BEMERKUNGEN:
+    Diese Property eignet sich hervorragend dafuer, einige Grundbedingungen
+    fuer das Nutzen der Waffe / Ruestung / Kleidung zu stellen ohne gleich
+    eine Wield- oder WearFunc setzen und auswerten zu muessen.
+
+    Denkbar waere der Einsatz bei hochwertigen Waffen / Ruestungen / Kleidung,
+    z.B. aus der Para-Welt oder solchen, die sich nah am Limit der geltenden
+    Grenzwerte fuer P_WC / P_AC bewegen oder sogar (nach Genehmigung durch
+    die Balance) darueber.
+
+BEISPIEL:
+    Mindeststufe 25: SetProp(P_RESTRICTIONS,([P_LEVEL:25]));
+    Keine Menschen:  SetProp(P_RESTRICTIONS,([SR_EXCLUDE_RACE:({"Mensch"})]));
+    Alignment >499:  SetProp(P_RESTRICTIONS,([SR_GOOD:500]));
+
+    Komplexeres Beispiel
+
+    Quest "Diamond Club" bestanden, magiereigene Property P_AUSGANG_GEFUNDEN
+    muss gesetzt sein, Stufe 10, nicht taub, max. 45 Food:
+    SetProp(P_RESTRICTIONS, ([ P_LEVEL: 10, P_DEAF: 1, P_FOOD: 45,
+      SR_PROP: ([P_AUSGANG_GEFUNDEN:1]), SR_QUEST:({"Diamond Club"}) ]));
+
+SIEHE AUCH:
+    check_restrictions(L)
+    WieldFunc(L), WearFunc(L), RemoveFunc(L), UnwieldFunc(L),
+    P_WIELD_FUNC, P_WEAR_FUNC, P_REMOVE_FUNC, P_UNWIELD_FUNC
+    /std/armour/wear.c, /std/weapon/combat.c, clothing, armours, weapon
+
+LETZTE AeNDERUNG:
+03. Januar 2014, Arathorn
diff --git a/doc/props/P_ROOM_MSG b/doc/props/P_ROOM_MSG
new file mode 100644
index 0000000..c3324f2
--- /dev/null
+++ b/doc/props/P_ROOM_MSG
@@ -0,0 +1,20 @@
+NAME:
+    P_ROOM_MSG                    "room_msg"                    
+
+DEFINIERT IN:
+    /sys/room/description.h
+
+BESCHREIBUNG:
+     Liste mit Meldungen, die zufaellig im Raum ausgegeben werden.
+
+     Hier sind nur die Textmeldungen als String-Array gespeichert.
+
+ANMERKUNGEN:
+     Bitte AddRoomMessage() zum Hinzufuegen/Ueberschreiben benutzen!
+
+SIEHE AUCH:
+     LFuns:    AddRoomMessage()
+     Verwandt: tell_room(), ReceiveMsg()
+     Props:    P_FUNC_MSG, P_MSG_PROB
+
+2.Feb 2016 Gloinson
diff --git a/doc/props/P_ROOM_TYPE b/doc/props/P_ROOM_TYPE
new file mode 100644
index 0000000..d9dd746
--- /dev/null
+++ b/doc/props/P_ROOM_TYPE
@@ -0,0 +1,23 @@
+NAME:
+    P_ROOM_TYPE                       "room_type"                       
+
+DEFINIERT IN:
+    /sys/rooms.h
+
+BESCHREIBUNG:
+    In P_ROOM_TYPE wird die Art des Raumes durch entsprechende Flags
+    beschrieben.
+
+    Bisher unterstuetzt werden:
+        - RT_SHOP       fuer Raeume, die /std/room/shop inheriten
+        - RT_PUB        fuer Raeume, die /std/room/pub inheriten
+
+BEISPIEL:
+    Wenn ein NPC abfragen moechte, ob er sich in einer Kneipe aufhaelt (um
+    selbststaendig tanken zu koennen) koennte eine Abfrage z.B. so aussehen:
+
+        if ( environment() &&
+             environment()->QueryProp(P_ROOM_TYPE) & RT_PUB ){
+
+            ... tanken ...
+        }
diff --git a/doc/props/P_SB_SPELLS b/doc/props/P_SB_SPELLS
new file mode 100644
index 0000000..84aa723
--- /dev/null
+++ b/doc/props/P_SB_SPELLS
@@ -0,0 +1,23 @@
+NAME:
+    P_SB_SPELLS            "sb_spells"                   
+
+DEFINIERT IN:
+    /sys/new_skills.h
+
+BESCHREIBUNG:
+    In dieser Spellbookproperty sind saemtliche Sprueche des Spellbooks
+    vermerkt. Veraendert wird sie durch AddSpell().
+
+BEMERKUNGEN:
+    Man sollte diese Property nicht per Hand veraendern, sondern die
+    Funktion AddSpell() nutzen.
+
+SIEHE AUCH:
+    GObj Verwalten:   LearnSkill, LearnSpell, InitialSkillAbility
+    * Properties:     P_GUILD_SKILLS
+    Spellbook Lernen: Learn, SpellSuccess, Erfolg, Misserfolg
+    * Verwalten:      AddSpell, QuerySpell
+    * Properties:     P_GLOBAL_SKILLPROPS
+    Skills:           P_NEWSKILLS, spruchermuedung, skill_info_liste
+
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
\ No newline at end of file
diff --git a/doc/props/P_SCREENSIZE b/doc/props/P_SCREENSIZE
new file mode 100644
index 0000000..fc9dd15
--- /dev/null
+++ b/doc/props/P_SCREENSIZE
@@ -0,0 +1,8 @@
+NAME:
+    P_SCREENSIZE                  "screensize"                  
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Bildschirmgroesse in Zeilen (fuer More)
diff --git a/doc/props/P_SECOND b/doc/props/P_SECOND
new file mode 100644
index 0000000..4aa835a
--- /dev/null
+++ b/doc/props/P_SECOND
@@ -0,0 +1,21 @@
+NAME:
+    P_SECOND                      "second"                      
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Wenn diese Prop gesetzt ist, ist der Spieler ein Zweitie. Inhalt der
+     Prop ist ein String mit dem (lowercase) Namen des Ersties.
+
+BEISPIEL:
+     if (this_player()->QueryProp(P_SECOND)=="notstrom") {
+       tell_object(this_player(), "Nicht alles aufessen!\n");
+     }
+
+SIEHE AUCH:
+     Properties: P_SECOND_MARK
+     Sonstiges:  /secure/zweities.c
+
+----------------------------------------------------------------------------
+Last modified: 18-Jun-2015, Arathorn.
diff --git a/doc/props/P_SECOND_MARK b/doc/props/P_SECOND_MARK
new file mode 100644
index 0000000..c1c0c42
--- /dev/null
+++ b/doc/props/P_SECOND_MARK
@@ -0,0 +1,18 @@
+NAME:
+    P_SECOND_MARK                 "second_mark"
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Gibt an, wie mit der Zweitie-Markierung eines Spielers umgegangen wird.
+
+     -1  'unsichtbare' Markierung; im wer/kwer wird bei diesem Zweitie s
+         oder S angezeigt.
+
+      0  'sichtbare' Markierung; im wer/kwer wird bei diesem Zweitie z oder
+         Z angezeigt. Der Name des Ersties ist beim Fingern jedoch nur
+         fuer Magier sichtbar.
+
+      1  Markierung 'sichtbar + Name'; wie 0, nur dass beim Fingern alle
+         Spieler den Namen des Ersties sehen koennen.
diff --git a/doc/props/P_SEERDOORS b/doc/props/P_SEERDOORS
new file mode 100644
index 0000000..8ffb852
--- /dev/null
+++ b/doc/props/P_SEERDOORS
@@ -0,0 +1,26 @@
+P_SEERDOORS
+
+NAME:
+     P_SEERDOORS      "rw_sehertore"
+
+DEFINIERT IN:
+     /d/seher/portale/sehertor.h
+
+BESCHREIBUNG:
+     Sehertor-relevante Property.
+
+     Enthaelt ein Mapping mit den Wertepaaren
+     ([ Seher-Portal-Nummer: x ])
+     mit x != 0 fuer entdeckte Tore.
+     
+     0 hat ein Sonderverhalten fuer mobile Tore.
+
+BEMERKUNG:
+     Auf gar keinen Fall in Spielern manipulieren! Und bitte das enthaltene
+     Mapping nicht von einem Spieler abfragen und P_SEERDOORS in einem
+     Testspieler zuweisen!
+     
+SIEHE AUCH:
+     P_FAO_PORTALS
+     
+1.September 2008 Gloinson
diff --git a/doc/props/P_SEERDOOR_ALLOWED b/doc/props/P_SEERDOOR_ALLOWED
new file mode 100644
index 0000000..b8225e3
--- /dev/null
+++ b/doc/props/P_SEERDOOR_ALLOWED
@@ -0,0 +1,15 @@
+NAME:
+    P_SEERDOOR_ALLOWED		"rw_sehertor_erlaubt"                          
+
+DEFINIERT IN:
+    /d/seher/portale/sehertor.h
+
+BESCHREIBUNG:
+     Diese Property muss in einem Raum gesetzt sein, soll
+     ein Seher dort ein mobiles Sehertor abstellen duerfen.
+     Zusaetzlich darf der Raum nicht in PARA liegen und muss
+     als eigenes File existieren.
+     Es ist darauf zu achten, Sehertore nicht in Questgebieten,
+     direkt an Tanken oder aehnlichen Plaetzen zu erlauben.
+     Es gilt die Einschaetzung des fuer den Raum Verantwortlichen.
+
diff --git a/doc/props/P_SENSITIVE b/doc/props/P_SENSITIVE
new file mode 100644
index 0000000..23d1fee
--- /dev/null
+++ b/doc/props/P_SENSITIVE
@@ -0,0 +1,125 @@
+P_SENSITIVE
+NAME:
+     P_SENSITIVE                   "sensitive"
+
+DEFINIERT IN:
+     /sys/thing/moving.h
+
+BESCHREIBUNG:
+     Diese Property kann in Objekten gesetzt werden, die auf bestimmte
+     Schadensarten empfindlich reagieren sollen. Moegliche Anwendungsfaelle:
+     - Das Lebewesen, in dessen Inventar sich ein Objekt befindet, erleidet
+       einen Angriff mit der fraglichen Schadensart (Beispiel: eine 
+       Pusteblume, die bei Angriff mit Luftschaden auseinanderfaellt).
+     - Zwei Objekte treffen im gleichen Environment aufeinander, wobei
+       eines empfindlich auf eine Schadensart reagiert, und das zweite diese
+       Schadensart mitbringt, d.h. die Empfindlichkeit ausloesen kann.
+       (Beispiel: ein feuerempfindlicher Postsack wird angesengt, wenn eine
+        brennende Fackel ins gleiche Inventar kommt.)
+       Das Ausloesen dieser Empfindlichkeit ist unabhaengig davon, welches 
+       Objekt zuerst da war.
+
+     Die Property enthaelt ein Array aus Arrays:
+       ({<sensprops_1>, <sensprops_2>, ..., <sensprops_n>})
+     
+     wobei jeder Eintrag <sensprops> jeweils folgende Struktur hat:
+       ({string list_key, string damtype, int treshold, mixed options })
+     
+     Die Eintraege haben dabei folgende Bedeutung:
+     
+     list_key: Kann einen von folgenden drei Werten annehmen 
+          1) SENSITIVE_INVENTORY, passive Eigenschaft; zeigt an, dass das
+             Objekt empfindlich auf aktive Objekte reagiert, die in das
+             Inventory des Containers hineinbewegt werden
+          2) SENSITIVE_ATTACK, passive Eigenschaft; zeigt an, dass das 
+             Objekt empfindlich auf aeussere Einfluesse bzw. Angriffe 
+             auf ein Lebewesen reagiert, in dessen Inventar es sich befindet
+          3) SENSITIVE_INVENTORY_TRIGGER, aktive Eigenschaft; zeigt an, dass
+             das Objekt eine Ausstrahlung auf andere Objekte im Inventar
+             hat. Wird ausgeloest, wenn das Objekt ins Inventar hineinbewegt
+             wird.
+     damtype: eine Schadensart (DT_FIRE, DT_WATER, ...)
+     treshold: hat zwei Bedeutungen abhaengig von dem Wert in list_key:
+          1) Fuer Objekte mit SENSITIVE_INVENTORY oder SENSITIVE_ATTCK ist
+             dies der Schadenswert, ab dem das Objekt benachrichtigt werden 
+             soll.
+             Hier wird ein Wert in "Defend-Einheiten" erwartet, d.h. das
+             Zehnfache dessen, was am Ende in LP abgezogen wuerde.
+          2) Fuer Objekte mit SENSITIVE_INVENTORY_TRIGGER ist dies der 
+             Schadenswert, mit dem das Objekt andere bereits im Inventar
+             des Containers befindliche Objekte beeinflusst, die eine 
+             entsprechende Empfindlichkeit gesetzt haben
+     options: Optionale Daten, die der programmierende Magier selbst
+            definieren kann. Werden an die in den betroffenen Objekten
+            aufgerufenen Funktionen durchgereicht.
+
+     Ein SENSITIVE_ATTACK-Objekt, dessen Trigger-Bedingungen erfuellt sind,
+     wird durch folgenden Funktionsaufruf darueber informiert:
+       trigger_sensitive_attack(object enemy, string damtype, int damage,
+                 mixed spell, mixed options)
+     
+     Ein SENSITIVE_INVENTORY-Objekt, dessen Trigger-Bedingungen erfuellt sind,
+     wird durch folgenden Funktionsaufruf darueber informiert:
+       trigger_sensitive_inv(object whodid, string damtype, int threshold,
+                 mixed options, mixed options)
+
+     Die beiden Funktionen muessen selbst ge-/ueberschrieben werden.
+
+BEMERKUNGEN:
+     1. P_SENSITIVE-Objekte kosten Rechenzeit bei jedem Angriff oder jedem
+        move() - daher bitte sparsam verwenden
+     2. Ist P_SENSITIVE nicht statisch, sondern wird es situationsabhaengig 
+        geaendert, muss man das environment() jeweils selbst ueber seine 
+        neue Empfindlichkeit benachrichtigen. Dies geschieht mit den 
+        Funktionen RemoveSensitiveObject() bzw.InsertSensitiveObject(), 
+        siehe deren Manpages.
+
+BEISPIEL:
+     Beispiel 1:
+     Beispielcode eines Objektes mit SENSITIVE_ATTACK und SENSITIVE_INVENTORY
+     siehe hier: /doc/beispiele/testobjekte/attack_busy_sensitive_testobj.c
+
+     Beispiel 2:
+     Ein Eiszapfen, der bei Feuerangriffen oder bei heissen Gegenstaenden im
+     gemeinsamen Environment zerschmelzen soll:
+
+     void create() {
+       SetProp( P_SENSITIVE, ({ ({SENSITIVE_ATTACK,     DT_FIRE, 100}),
+                                 ({SENSITIVE_INVENTORY, DT_FIRE, 100}) }) );
+       [...]
+     }
+
+     varargs void trigger_sensitive_attack() {
+       remove();
+     }
+
+     varargs void trigger_sensitive_inv() {
+       call_out("remove",0);  // verzoegert, wegen move()
+     }
+
+     varargs int remove(int silent) {
+       if(!silent) {
+         object room = all_environment(this_object())[<1];
+         tell_room(room, Name()+" zerschmilzt.\n");
+       }
+       return ::remove();
+     }
+
+     - wird eine Fackel mit
+       SetProp(P_SENSITIVE,({({SENSITIVE_INVENTORY_TRIGGER,DT_FIRE,250})}))
+       in das gleiche environment() wie dieser Zapfen bewegt wird, loest
+       diese im Zapfen trigger_sensitive_inv() aus
+     - wird ein Angriff mit DT_FIRE und initialem Schaden > 100 auf das
+       environment() veruebt, loest dies im Zapfen trigger_sensitive_attack()
+       aus
+
+SIEHE AUCH:
+     Properties: P_SENSITIVE_ATTACK, P_SENSITIVE_INVENTORY,
+                 P_SENSITIVE_INVENTORY_TRIGGER
+     Funktionen: InsertSensitiveObject(L), RemoveSensitiveObject(L),
+                 CheckSensitiveAttack(L), Defend(), 
+                 insert_sensitive_inv(L), insert_sensitive_inv_trigger(L),
+                 trigger_sensitive_inv(L), trigger_sensitive_attack(L)
+     Defines:    /sys/sensitive.h
+
+Letzte Aenderung: 10. Januar 2015, Arathorn
diff --git a/doc/props/P_SENSITIVE_ATTACK b/doc/props/P_SENSITIVE_ATTACK
new file mode 100644
index 0000000..b1586eb
--- /dev/null
+++ b/doc/props/P_SENSITIVE_ATTACK
@@ -0,0 +1,24 @@
+P_SENSITIVE_ATTACK
+NAME:
+    P_SENSITIVE_ATTACK            "sensitive_attack"
+
+DEFINIERT IN:
+    /sys/sensitive.h
+
+BESCHREIBUNG:
+    Hier steht die Liste der zu informierenden Objekte, die potentiell
+    auf einen Angriff reagieren koennten.
+    Wird von InsertSensitiveObject() und RemoveSensitiveObject()
+    geschrieben und in CheckSensitiveAttack() ausgewertet.
+
+BEMERKUNGEN:
+    Nicht selbst veraendern - bitte P_SENSITIVE fuer Eintraege benutzen.
+
+SIEHE AUCH:
+     P_SENSITIVE
+     InsertSensitiveObject, RemoveSensitiveObject
+     insert_sensitive_inv_trigger, insert_sensitive_inv
+     P_SENSITIVE_INVENTORY_TRIGGER, P_SENSITIVE_INVENTORY
+     CheckSensitiveAttack
+
+25.Apr.2001, Gloinson@MG
diff --git a/doc/props/P_SENSITIVE_INVENTORY b/doc/props/P_SENSITIVE_INVENTORY
new file mode 100644
index 0000000..1e75423
--- /dev/null
+++ b/doc/props/P_SENSITIVE_INVENTORY
@@ -0,0 +1,24 @@
+NAME:
+    P_SENSITIVE_INVENTORY         "sensitive_inv"
+
+DEFINIERT IN:
+    /sys/sensitive.h
+
+BESCHREIBUNG:
+    Hier steht die Liste der zu informierenden Objekte, die potentiell
+    auf ein anderes Objekt mit gesetztem P_SENSITIVE_INVENTORY_TRIGGER
+    reagieren koennten.
+    Wird von InsertSensitiveObject() und RemoveSensitiveObject()
+    geschrieben.
+
+BEMERKUNGEN:
+    Nicht selbst veraendern - bitte P_SENSITIVE fuer Eintraege benutzen.
+
+SIEHE AUCH:
+     P_SENSITIVE
+     InsertSensitiveObject, RemoveSensitiveObject
+     insert_sensitive_inv_trigger, insert_sensitive_inv
+     P_SENSITIVE_INVENTORY_TRIGGER, P_SENSITIVE_ATTACK
+     CheckSensitiveAttack
+
+25.Apr.2001, Gloinson@MG
diff --git a/doc/props/P_SENSITIVE_INVENTORY_TRIGGER b/doc/props/P_SENSITIVE_INVENTORY_TRIGGER
new file mode 100644
index 0000000..df6feae
--- /dev/null
+++ b/doc/props/P_SENSITIVE_INVENTORY_TRIGGER
@@ -0,0 +1,24 @@
+P_SENSITIVE_INVENTORY_TRIGGER
+NAME:
+    P_SENSITIVE_INVENTORY_TRIGGER "sensitive_inv_trigger"
+
+DEFINIERT IN:
+    /sys/sensitive.h
+
+BESCHREIBUNG:
+    Hier steht die Liste der aktiven Objekte, die eine potentielle
+    "Ausstrahlung" auf andere Objekte haben.
+    Wird von InsertSensitiveObject() und RemoveSensitiveObject()
+    geschrieben.
+
+BEMERKUNGEN:
+    Nicht selbst veraendern - bitte P_SENSITIVE fuer Eintraege benutzen.
+
+SIEHE AUCH:
+     P_SENSITIVE
+     InsertSensitiveObject, RemoveSensitiveObject
+     insert_sensitive_inv_trigger, insert_sensitive_inv
+     P_SENSITIVE_ATTACK, P_SENSITIVE_INVENTORY
+     CheckSensitiveAttack
+
+25.Apr.2001, Gloinson@MG
diff --git a/doc/props/P_SHOOTING_AREA b/doc/props/P_SHOOTING_AREA
new file mode 100644
index 0000000..770564f
--- /dev/null
+++ b/doc/props/P_SHOOTING_AREA
@@ -0,0 +1,24 @@
+P_SHOOTING_AREA
+
+NAME:
+    P_SHOOTING_AREA     "shooting_area"
+
+DEFINIERT IN:
+    <combat.h>
+
+BESCHREIBUNG:
+    Legt die 'Groesse' eines Raumes fest. Ein Schuetze kann mit seiner
+    Fernkampfwaffe nur dann aus diesem Raum in einen anderen Raum schiessen,
+    wenn die P_RANGE seiner Waffe mindestens gleich ist.
+
+    Erreichbare Raeume sind entweder das environment() oder der in
+    P_SHOOTING_AREA festgelegte Raum.
+
+SIEHE AUCH:
+    Generell:  P_AMMUNITION, P_SHOOTING_WC, P_STRETCH_TIME
+    Methoden:  FindRangedTarget(L), shoot_dam(L), cmd_shoot(L)
+    Gebiet:    P_RANGE, P_TARGET_AREA
+    Raeume:    P_NEVER_CLEAN
+    Sonstiges: fernwaffen
+
+29.Jul 2014 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_SHOOTING_WC b/doc/props/P_SHOOTING_WC
new file mode 100644
index 0000000..35ca215
--- /dev/null
+++ b/doc/props/P_SHOOTING_WC
@@ -0,0 +1,31 @@
+P_SHOOTING_WC
+
+NAME:
+    P_SHOOTING_WC     "shooting_wc"
+
+DEFINIERT IN:
+    <combat.h>
+
+BESCHREIBUNG:
+    Legt in einer Fernkampfwaffe UND ihrer Munition die Waffenstaerke fest.
+    Bei einem Schuss wird die Summe kombiniert mit der Geschicklichkeit
+    zur Berechnung der Angriffstaerke benutzt.
+
+BEISPIELE:
+    SetProp(P_SHOOTING_WC, 70);     // Langbogen
+    SetProp(P_SHOOTING_WC, 50);     // Kurzbogen
+
+    SetProp(P_SHOOTING_WC, 40);     // Bambuspfeil
+    SetProp(P_SHOOTING_WC, 60);     // Aluminiumpfeil
+
+    // So haetten Langbogen mit Aluminiumpfeil eine P_SHOOTING_WC von 70+60.
+
+SIEHE AUCH:
+    Generell:  P_AMMUNITION, P_STRETCH_TIME
+    Methoden:  FindRangedTarget(L), shoot_dam(L), cmd_shoot(L)
+    Gebiet:    P_RANGE, P_SHOOTING_AREA, P_TARGET_AREA
+    Waffen:    P_WEAPON_TYPE, P_WC, P_EQUIP_TIME, P_NR_HANDS
+    Kampf:     Attack(L), Defend(L), P_DISABLE_ATTACK, P_ATTACK_BUSY
+    Sonstiges: fernwaffen
+
+29.Jul 2014 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_SHORT b/doc/props/P_SHORT
new file mode 100644
index 0000000..b6502d4
--- /dev/null
+++ b/doc/props/P_SHORT
@@ -0,0 +1,41 @@
+P_SHORT
+NAME:
+     P_SHORT				"short"
+
+DEFINIERT IN:
+     /sys/thing/description.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt die Kurzbeschreibung des Objektes als String 
+     oder Closure (diese muss einen String zurueckgeben).
+
+     ACHTUNG: Die Kurzbeschreibung sollte dabei weder mit einem
+	      Satzzeichen noch mit einem "\n" abgeschlossen sein
+	      (dies wird von den zustaendigen Funktionen erledigt).
+
+     Setzt man diese Property auf 0, so ist das Objekt unsichtbar, allerdings
+     ansprechbar, wenn der Spieler eine ID des Objektes kennt. D.h. Objekte
+     koennen mitgenommen, weggeworfen oder ggf. auch angegriffen werden. Will
+     man dies nicht, sollte man das Objekt mit P_INVIS unsichtbar machen.
+
+     Diese Property bestimmt die Ansicht des Objektes von aussen. Fuer die
+     Innen(kurz)ansicht von Raeumen muss man P_INT_LONG benutzen.
+
+BEMERKUNGEN:
+     Die Funktion, die die Kurzbeschreibung ausgibt (short()), filtert P_SHORT
+     durch process_string(). Von der Nutzung dieses Features wird in neuem
+     Code abgeraten.
+     Soll eine dyn. Kurzbeschreibung geschaffen werden, bitte eine
+     F_QUERY_METHOD einsetzen oder short() passend ueberschreiben.
+
+BEISPIELE:
+     // eine Axt sieht natuerlich so aus:
+     SetProp(P_SHORT, "Eine Axt");
+
+SIEHE AUCH:
+     Aehnliches:	P_LONG, short()
+     Sonstiges:		P_INT_SHORT, process_string()
+
+----------------------------------------------------------------------------
+27.05.2015, Zesstra
+
diff --git a/doc/props/P_SHORT_CWD b/doc/props/P_SHORT_CWD
new file mode 100644
index 0000000..a6343ae
--- /dev/null
+++ b/doc/props/P_SHORT_CWD
@@ -0,0 +1,8 @@
+NAME:
+    P_SHORT_CWD                   "short_cwd"                   
+
+DEFINIERT IN:
+    /sys/shells.h
+
+BESCHREIBUNG:
+     .readme bei cd ausgeben oder nicht
diff --git a/doc/props/P_SHOWEMAIL b/doc/props/P_SHOWEMAIL
new file mode 100644
index 0000000..82d1348
--- /dev/null
+++ b/doc/props/P_SHOWEMAIL
@@ -0,0 +1,10 @@
+NAME:
+     P_SHOWEMAIL                        "showemail"
+
+DEFINIERT IN:
+     /sys/player/base.h
+
+BESCHREIBUNG:
+     Eintrag, wer die E-Mail im "finger" zu sehen bekommen soll:
+     0, "alle", "freunde"
+     Kann durch "emailanzeige" (/std/player/base.c) geaendert werden.
diff --git a/doc/props/P_SHOW_ALIAS_PROCESSING b/doc/props/P_SHOW_ALIAS_PROCESSING
new file mode 100644
index 0000000..cb49cf8
--- /dev/null
+++ b/doc/props/P_SHOW_ALIAS_PROCESSING
@@ -0,0 +1,8 @@
+NAME:
+    P_SHOW_ALIAS_PROCESSING       "show_alias_processing"       
+
+DEFINIERT IN:
+    /sys/player.h
+
+BESCHREIBUNG:
+     Arbeit des Parsers beobachten (debugging)
diff --git a/doc/props/P_SHOW_EXITS b/doc/props/P_SHOW_EXITS
new file mode 100644
index 0000000..9c8d87b
--- /dev/null
+++ b/doc/props/P_SHOW_EXITS
@@ -0,0 +1,15 @@
+NAME:
+    P_SHOW_EXITS                  "show_exits"
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Im Spieler gesetzt, wenn der Spieler die offensichtlichen Ausgaenge
+     immer automatisch sehen will.
+
+SIEHE AUCH:
+     Aehnliches:	P_HIDE_EXITS
+     Sonstiges:		AddExit(), GetExits(), int_long(), int_short()
+
+11. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_SHOW_INV b/doc/props/P_SHOW_INV
new file mode 100644
index 0000000..73a2ffe
--- /dev/null
+++ b/doc/props/P_SHOW_INV
@@ -0,0 +1,19 @@
+P_SHOW_INV
+
+NAME:
+     P_SHOW_INV "show_inv"
+
+DEFINIERT IN:
+     <thing/description.h>
+
+BESCHREIBUNG:
+     Wenn diese Property auf einen Wert ungleich 0 gesetzt ist, wird das
+     Objekt, soweit es sich in einem Spieler befindet, in dessen
+     Langbeschreibung angezeigt. Zur Anzeige wird der Name des Objektes
+     verwendet.
+
+SIEHE AUCH:
+     /std/thing/description.c
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 20:36:05 1996 by Wargon
diff --git a/doc/props/P_SHOW_MSG b/doc/props/P_SHOW_MSG
new file mode 100644
index 0000000..8841154
--- /dev/null
+++ b/doc/props/P_SHOW_MSG
@@ -0,0 +1,51 @@
+P_SHOW_MSG
+NAME:
+    P_SHOW_MSG                          "show_message"
+
+DEFINIERT IN:
+    /sys/living/put_and_get.h
+
+BESCHREIBUNG:
+    Mit P_SHOW_MSG kann man die Meldungen, die beim Vorzeigen eines Objektes
+    ausgegeben werden, modifizieren.
+
+    Folgende Werte sind moeglich:
+
+    o 0
+      Es wird eine Standardmeldung ausgegeben. Dies ist Voreinstellung.
+
+    o NO_PNG_MSG        == -1
+      Es wird keinerlei Meldung ausgegeben
+
+    o Ein Array aus Strings
+      Der erste String wird an den Spieler ausgegeben, der zweite
+      (optionale) an den Raum, der dritte (ebenfalls optionale) an den
+      Empfaenger.
+
+      Die Strings werden durch die Funktion replace_personal() geparst.
+        Objekt1 - Spieler
+        Objekt2 - das Objekt, das uebergeben wird
+        Objekt3 - Empfaenger
+
+      Wird der zweite String nicht angegeben, erfolgt keine Meldung an den
+      Raum. Beim Fehlen des dritten gibt es keine Meldung an den Empfaenger.
+
+BEISPIEL:
+    SetProp(P_SHOW_MSG, ({
+      "Du haeltst @WEM3 @WEN2 unter die Nase.",
+      "@WER1 haelt @WEM3 @WENU2 unter die Nase.",
+      "@WER1 haelt Dir @WENU2 unter die Nase."
+    }));
+
+    Das fuehrt bei Ugars "zeig peter medaille" zu folgenden Meldungen:
+
+    Ugar: "Du haeltst Peter die Medaille unter die Nase."
+    Raum: "Ugar haelt Peter eine Medaille unter die Nase."
+    Peter: "Ugar haelt Dir eine Medaille unter die Nase."
+
+SIEHE AUCH:
+     Aehnliches: P_DROP_MSG, P_PUT_MSG, P_PICK_MSG, P_GIVE_MSG
+     Sonstiges:  replace_personal(E), show(L), show_objects(L),
+                 show_notify(L), /std/living/put_and_get.c
+
+3. Juni 2008 Amynthor
diff --git a/doc/props/P_SIBLINGS b/doc/props/P_SIBLINGS
new file mode 100644
index 0000000..15d0977
--- /dev/null
+++ b/doc/props/P_SIBLINGS
@@ -0,0 +1,9 @@
+NAME:
+    P_SIBLINGS                     "siblings"                     
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Enthaelt einen String mit den Blutsbruedern eines Spielers
+     (sofern vorhanden).
diff --git a/doc/props/P_SIZE b/doc/props/P_SIZE
new file mode 100644
index 0000000..b5fdb72
--- /dev/null
+++ b/doc/props/P_SIZE
@@ -0,0 +1,31 @@
+NAME:
+    P_SIZE                        "size"                        
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Groesse des Lebewesens bzw. Laenge der Waffe (in cm).
+
+     Wird keine Waffenlaenge explizit angegeben, so sind die Defaultwerte
+     fuer die entsprechenden Waffentypen folgende:
+
+    	WT_SWORD  : 100
+    	WT_AXE    :  80
+    	WT_CLUB   :  80
+    	WT_SPEAR  : 180
+    	WT_KNIFE  :  20
+    	WT_WHIP   : 200
+    	WT_STAFF  : 150
+
+BEMERKUNGEN:
+     1. Uebertreibt es bitte mit der Groesse nicht, auch sehr grosse NPCs 
+        sollten nicht ueber 1000000 liegen. Sonst kriegt die Karategilde 
+        Probleme.
+     2. Negative Werte fuer P_SIZE sind nicht moeglich, da dies zum einen
+        voellig unsinnig ist und zum anderen evtl. zu Problemen mit Waffen
+        fuehrt, die Schaden in Abhaengigkeit von P_SIZE machen und sich
+        darauf verlassen, dass nur positive Werte vorkommen.
+
+LETZTE AENDERUNG:
+    2006-09-29, von Zesstra
diff --git a/doc/props/P_SKILLS b/doc/props/P_SKILLS
new file mode 100644
index 0000000..02a76c7
--- /dev/null
+++ b/doc/props/P_SKILLS
@@ -0,0 +1,15 @@
+NAME:
+	P_SKILLS			"skills"                      
+
+DEFINIERT IN:
+	/sys/player/skills.h
+
+BESCHREIBUNG:
+	Diese Property sollte nicht mehr verwendet werden. Sie wurde
+	vollstaendig durch P_NEWSKILLS ersetzt.
+
+SIEHE AUCH:
+	P_NEW_SKILLS
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_SKILL_ATTRIBUTES b/doc/props/P_SKILL_ATTRIBUTES
new file mode 100644
index 0000000..6eb5b97
--- /dev/null
+++ b/doc/props/P_SKILL_ATTRIBUTES
@@ -0,0 +1,50 @@
+NAME:
+    P_SKILL_ATTRIBUTES        "skill_attr"
+
+DEFINIERT IN:
+    /sys/living/skill_attributes.h
+
+BESCHREIBUNG:
+    In dieser Prop stehen alle nicht-permanenten Modifikatoren der
+    Skill-Attribute.
+    Die Datenstruktur ist ein Mapping mit den SA-Namen als Schluessel und
+    jeweils drei Werten pro Schluessel.
+    Der erste Wert ist ein Array mit drei Werten: der Summe der stat.
+    Modifier, dem Zeitpunkt an dem dies Summe ungueltig wird und der
+    Gesamtzahl aktiver Modifikatoren.
+    Der zweite Wert enthaelt ein Mapping mit allen statischen Modifikatoren
+    und den Objekten dieser Mods als Schluessel. Die beiden Werte dieses
+    Mappings sind der Wert des Modifikators (int) und die Ablaufzeit (int).
+    Der dritte Wert enthaelt ein Mapping mit allen dynamischen
+    Modifikatoren und den Objekten dieser Mods als Schluessel. Die beiden
+    Werte dieses Mappings sind die zu rufende Closure (closure) und die
+    Ablaufzeit des Mods (int).
+
+    ([ SA_ATTR: ({Summe_Stat_Modifier, Zeitpunkt, AnzahlModifier, });
+                ([ ob1:value;duration,
+                   ob2:value;duration, ...]);  // stat. Modifier
+                ([ ob1:closure;duration,
+                   ob2:closure;duration, ...])     // dyn. Modifier
+                ,
+       SA_ATTR2: ({...}); ([]); ([]),
+       SA_ATTR3: ({...}); ([]); ([]),
+    ])
+
+BEMERKUNGEN:
+    Diese Property darf AUF GAR KEINEN FALL per Hand manipuliert werden,
+    dafuer gibt es die Funktionen ModifySkillAttribute() und
+    RemoveSkillAttributeModifier().
+    Zum Auslesen stehen QuerySkillAttribute() und
+    QuerySkillAttributeModifier() zur Verfuegung.
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung, skill_info_liste
+    * Properties:   P_NEWSKILLS
+
+13.09.2008, Zesstra
\ No newline at end of file
diff --git a/doc/props/P_SKILL_ATTRIBUTE_OFFSETS b/doc/props/P_SKILL_ATTRIBUTE_OFFSETS
new file mode 100644
index 0000000..2568f76
--- /dev/null
+++ b/doc/props/P_SKILL_ATTRIBUTE_OFFSETS
@@ -0,0 +1,32 @@
+NAME:
+    P_SKILL_ATTRIBUTE_OFFSETS       "skill_attr_offsets"                        
+
+DEFINIERT IN:
+    /sys/living/skill_attributes.h
+
+BESHREIBUNG:
+
+    Der Wert der Property ist ein Mapping: ([Attributname: Wert])
+    In dieser Property stehen permanente Abweichungen der Skillattribute
+    vom Standardwert 100.
+
+    Zu den Moeglichen Attributwerten, siehe P_SKILL_ATTRIBUTES.
+
+    Die Werte duerfen zwischen 10 und 1000 liegen.
+
+BEMERKUNG:
+    Diese Property sollte AUF GAR KEINEN FALL in einem Spieler gesetzt
+    werden, ohne Ruecksprachen mit allerhoechsten Stellen!
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung, skill_info_liste
+    * Properties:   P_NEWSKILLS
+
+31.12.2013, Zesstra
+
diff --git a/doc/props/P_SMELLS b/doc/props/P_SMELLS
new file mode 100644
index 0000000..b510ce1
--- /dev/null
+++ b/doc/props/P_SMELLS
@@ -0,0 +1,24 @@
+NAME:
+    P_SMELLS            "smell_details"
+
+DEFINIERT IN:
+    /sys/thing/description.h
+
+BESCHREIBUNG:
+    Diese Property entspricht dem P_DETAILS fuer Standarddetails,
+    nur werden hierin Gerueche gespeichert:
+    Diese Property enthaelt ein Mapping, in der Details im Objekt
+    definiert werden und Beschreibungen, die ausgegeben werden, wenn man
+    sich diese Details anschaut.
+
+BEMERKUNGEN:
+    Man sollte diese Property nicht per Hand veraendern, sondern die
+    Funktionen nutzen.
+
+SIEHE AUCH:
+    Setzen:    AddSmells()
+    Loeschen:  RemoveSmells()
+    Aehnlich:  AddDetail(), P_DETAILS
+    Sonstiges: GetDetail(), break_string()
+
+27. Jan 2013 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_SNOOPFLAGS b/doc/props/P_SNOOPFLAGS
new file mode 100644
index 0000000..4c47e39
--- /dev/null
+++ b/doc/props/P_SNOOPFLAGS
@@ -0,0 +1,8 @@
+NAME:
+    P_SNOOPFLAGS                  "snoopflags"                  
+
+DEFINIERT IN:
+    /sys/snooping.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_SOUNDS b/doc/props/P_SOUNDS
new file mode 100644
index 0000000..9b9a350
--- /dev/null
+++ b/doc/props/P_SOUNDS
@@ -0,0 +1,24 @@
+NAME:
+    P_SOUNDS            "sound_details"
+
+DEFINIERT IN:
+    /sys/thing/description.h
+
+BESCHREIBUNG:
+    Diese Property entspricht dem P_DETAILS fuer Standarddetails,   
+    nur werden hierin Gerauesche gespeichert:
+    Diese Property enthaelt ein Mapping, in der Details im Objekt
+    definiert werden und Beschreibungen, die ausgegeben werden, wenn man
+    sich diese Details anschaut.
+
+BEMERKUNGEN:
+    Man sollte diese Property nicht per Hand veraendern, sondern die
+    Funktionen nutzen.
+
+SIEHE AUCH:
+    Setzen:    AddSounds()
+    Loeschen:  RemoveSounds()
+    Aehnlich:  AddDetail(), P_DETAILS
+    Sonstiges: GetDetail(), break_string()
+
+27. Jan 2013 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_SP b/doc/props/P_SP
new file mode 100644
index 0000000..02bab65
--- /dev/null
+++ b/doc/props/P_SP
@@ -0,0 +1,23 @@
+NAME:
+    P_SP                          "sp"
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+
+     - Lebewesen
+       Anzahl der Konzentrationspunkte des Wesens.
+
+     - Speisen/Kneipen
+       Heilwirkung einer Portion der Speise auf die Konzentrationspunkte
+       des Konsumenten
+
+SIEHE AUCH:
+     Props:		P_MAX_SP
+     Veraenderung:	reduce_spell_points(), restore_spell_points()
+			buffer_sp()
+     Speisen/Kneipen:   std/pub, wiz/food, consume
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_SPECIAL_DETAILS b/doc/props/P_SPECIAL_DETAILS
new file mode 100644
index 0000000..2eb2123
--- /dev/null
+++ b/doc/props/P_SPECIAL_DETAILS
@@ -0,0 +1,23 @@
+NAME:
+    P_SPECIAL_DETAILS             "special_details"             
+
+DEFINIERT IN:
+    /sys/thing/description.h
+
+BESCHREIBUNG:
+    Mapping von Details, die beim Anschauen eine Funktion starten.
+
+BEMERKUNGEN:
+    Dies ist keine "echte" Property. Die Daten werden bei der Abfrage in einer
+    Querymethode dynamisch aus P_DETAILS extrahiert. Dementsprechend
+    funktioniert es auch nicht, hier eine Query- oder Setmethode von aussen
+    drauf zu legen.
+
+SIEHE AUCH:
+    Setzen:    AddDetail()
+    Loeschen:  RemoveDetail()
+    Daten:     P_DETAILS
+    Veraltet:  AddSpecialDetail(), RemoveSpecialDetail()
+    Sonstiges: GetDetail(), break_string()
+
+27. Jan 2013 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_SPECIAL_EXITS b/doc/props/P_SPECIAL_EXITS
new file mode 100644
index 0000000..1914366
--- /dev/null
+++ b/doc/props/P_SPECIAL_EXITS
@@ -0,0 +1,9 @@
+NAME:
+    P_SPECIAL_EXITS               "special_exits"               
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Dito, aber anstatt des Nachbarraums wird eine Funktion (im Raum)
+     angegebem, die bei Eingabe der Richtung ausgefuehrt wird.
diff --git a/doc/props/P_SPELLRATE b/doc/props/P_SPELLRATE
new file mode 100644
index 0000000..1672a33
--- /dev/null
+++ b/doc/props/P_SPELLRATE
@@ -0,0 +1,8 @@
+NAME:
+    P_SPELLRATE                   "spellrate"                   
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     NPC-Spellrate (in %)
diff --git a/doc/props/P_SPELLS b/doc/props/P_SPELLS
new file mode 100644
index 0000000..17112fb
--- /dev/null
+++ b/doc/props/P_SPELLS
@@ -0,0 +1,8 @@
+NAME:
+    P_SPELLS                      "spells"                      
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     NPC-Spells
diff --git a/doc/props/P_SP_DELAY b/doc/props/P_SP_DELAY
new file mode 100644
index 0000000..84f6372
--- /dev/null
+++ b/doc/props/P_SP_DELAY
@@ -0,0 +1,10 @@
+NAME:
+    P_SP_DELAY                 "sp_delay"                     
+
+DEFINIERT IN:
+    /sys/living/life.h
+
+BESCHREIBUNG:
+     Anzahl der heart_beats, bis die Magiepunkte um einen Punkt steigen.
+     Aenderungen dieser Property in Spielern beduerfen der 
+     Genehmigung des EM fuer Balance.
diff --git a/doc/props/P_START_HOME b/doc/props/P_START_HOME
new file mode 100644
index 0000000..25fff2c
--- /dev/null
+++ b/doc/props/P_START_HOME
@@ -0,0 +1,8 @@
+NAME:
+    P_START_HOME                  "start_home"                  
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Raum, in dem der Spieler nach dem Einloggen landen soll
diff --git a/doc/props/P_STD_OBJECT b/doc/props/P_STD_OBJECT
new file mode 100644
index 0000000..87e21da
--- /dev/null
+++ b/doc/props/P_STD_OBJECT
@@ -0,0 +1,38 @@
+NAME:
+    P_STD_OBJECT                  "std_object"                  
+
+DEFINIERT IN:
+    /sys/v_compiler.h
+
+BESCHREIBUNG:
+   Enthaelt den Namen eines Files welches als Standard-Objekt fuer den 
+   Virtual Compiler gelten soll.
+
+   In diesem File werden das generelle Aussehen, Ausgaenge, Funktionen
+   usw. der VC-generierten Raeume / Objekte festgelegt.
+
+   Dieses File ist ein 'normales' .c - File, welches geclont wird und
+   anschliessend umbenannt wird.
+   
+   Ganz wichtig: Falls euer Standardobjekt (direkt oder indirekt) von
+   /std/room.c erbt, solltet ihr darauf achten, dass euer Objekt ausser dem
+   create() noch eine weitere (beliebige) Funktion hat.  
+   Ansonsten wuerde das Programm eures Standardobjekts automatisch durch
+   /std/room.c ersetzt, was in der Regel zu schwer zu findenen Bugs fuehrt.
+
+BEISPIEL:
+   (create eines VCs)
+   protected void create() {
+     ...
+     SetProp(P_STD_OBJECT,"/d/region/magier/vc/std_raum");
+     ...
+   }
+
+   Was in diesem std_raum.c nun steht, wird in jedem VC-Clone
+   verfuegbar. Sei es Details, Gerueche, auch Objekte die per 
+   AddItem eingebunden sind, ...
+
+SIEHE AUCH:
+   P_COMPILER_PATH, virtual_compiler
+-----------------------------------------------------------------------
+Letzte Aenderung: 22.10.07 von Zesstra
diff --git a/doc/props/P_STORE_CONSUME b/doc/props/P_STORE_CONSUME
new file mode 100644
index 0000000..7b65641
--- /dev/null
+++ b/doc/props/P_STORE_CONSUME
@@ -0,0 +1,51 @@
+NAME:
+	P_STORE_CONSUME			"store_consume"
+
+DEFINIERT IN:
+	/sys/bank.h
+
+BESCHREIBUNG:
+	Diese Property ist entscheidend dafuer, wieviel Prozent an Objekten
+	bei jedem Reset in einem Lager eines Ladens vernichtet werden. Dies
+	geschieht aus Speicher- und Laggruenden. Es verbleibt dabei jedoch
+	eine Grundmenge an Objekten, deren Anzahl in der Property
+	P_MIN_STOCK festgehalten ist. Standardwert fuer P_STORE_CONSUME ist
+	hierbei 30%, aber in oft benutzten Laeden kann man dort ruhig einen
+	hoeheren Wert eintragen. Erlaubt sind Werte zwischen 0% und 100%.
+	Aufgeraeumt werden jedoch keine Objekte, die mittels AddItem() im
+	Lager eingebunden wurden. Mittels der Ladenfunktion AddFixedObject()
+	als staendig verfuegbar markierte Objekte werden natuerlich auch
+	nicht beruecksichtigt.
+	Beide hier erwaehnten Properties sollten ueberigens nur mittels
+	QueryProp/SetProp ausgelesen bzw. veraendert werden.
+
+BEISPIEL:
+	Ein eigener haeufig benutzter Laden koennte ein Lager in folgender
+	Form erhalten:
+	  // myStore
+	  #include <bank.h>
+	  inherit "std/store";
+	  void create()
+	  { ::create();
+	    SetProp(P_STORE_CONSUME,90);
+	    // keine weiteren Beschreibungen, Spieler sollen da drin
+	    // schliesslich nicht rumwuseln
+	  }
+	Um das Lager dem Laden zuzuweisen, nutzt man folgendes:
+	  // myShop
+	  inherit "std/laden";
+	  void create()
+	  { ::create();
+	    SetStorageRoom("pfad/myStore");
+	    // Beschreibungen folgen
+	    ...
+	  }
+	Es werden hierbei waehrend jedes Lager-Resets 90% der im Lager
+	befindlichen Objekte vernichtet.
+
+SIEHE AUCH:
+	P_MIN_STOCK, SetStorageRoom(), /std/store.c, /std/shop.c
+	AddItem(), RemoveItem(), AddFixedObject(), RemoveFixedObject()
+
+----------------------------------------------------------------------------
+Last modified: 19-Jun-2015, Arathorn 
diff --git a/doc/props/P_STRETCH_TIME b/doc/props/P_STRETCH_TIME
new file mode 100644
index 0000000..dfef4f1
--- /dev/null
+++ b/doc/props/P_STRETCH_TIME
@@ -0,0 +1,20 @@
+P_STRETCH_TIME
+
+NAME:
+    P_STRETCH_TIME     "stretch_time"
+
+DEFINIERT IN:
+    <combat.h>
+
+BESCHREIBUNG:
+    Enthaelt die Zeit in Runden (2s), die man braucht, um eine Fernwaffe zu
+    spannen/benutzen. Zaehlt seit dem letzten Schuss oder der Zeit des
+    Zueckens (P_EQUIP_TIME).
+
+SIEHE AUCH:
+    Generell:  P_AMMUNITION, P_SHOOTING_WC
+    Methoden:  FindRangedTarget(L), shoot_dam(L), cmd_shoot(L)
+    Gebiet:    P_RANGE, P_SHOOTING_AREA, P_TARGET_AREA
+    Sonstiges: fernwaffen
+
+29.Jul 2014 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_SUBGUILD_TITLE b/doc/props/P_SUBGUILD_TITLE
new file mode 100644
index 0000000..58a59d5
--- /dev/null
+++ b/doc/props/P_SUBGUILD_TITLE
@@ -0,0 +1,20 @@
+P_SUBGUILD_TITLE
+NAME:
+     P_SUBGUILD_TITLE		"subguild_title"                       
+
+DEFINIERT IN:
+     /sys/new_skills.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt eventuelle Zusatztitel eines Spielers, den er
+     innerhalb einer Gilde traegt. Das kann z.B. ein Clan sein, ein Zweig oder
+     einfach nur der Gildenrang.
+
+BEMERKUNGEN:
+     Inhalt der Property kann 0 sein oder ein String.  Ein Zusatztitel kann
+     mittels P_VISIBLE_SUBGUILD_TITLE vorgetaeuscht werden.
+
+SIEHE AUCH:
+     P_GUILD_TITLE, P_VISIBLE_SUBGUILD_TITLE
+
+26. Maerz 2004 Gloinson
diff --git a/doc/props/P_TARGET_AREA b/doc/props/P_TARGET_AREA
new file mode 100644
index 0000000..9d2cec9
--- /dev/null
+++ b/doc/props/P_TARGET_AREA
@@ -0,0 +1,75 @@
+P_TARGET_AREA
+
+NAME:
+    P_TARGET_AREA     "target_area"
+
+DEFINIERT IN:
+    <combat.h>
+
+BESCHREIBUNG:
+    Kann in einem Raum gesetzt werden, um einen anderen, von dort aus mit
+    Fernkampfwaffen beschiessbaren Raum als Objekt oder Objektnamen (zu
+    einem geladenen Objekt) festzulegen.
+
+BEMERKUNGEN:
+    Ein Schuetze kann nur in den anderen Raum schiessen, wenn die P_RANGE
+    seiner Waffe mindest gleich der P_SHOOTING_AREA des Raums (nicht des
+    Zielraums) ist.
+
+    Idealerweise sollte in mit P_TARGET_AREA angegebenen Raeumen auch
+    P_NEVER_CLEAN gesetzt sein.
+
+BEISPIELE:
+    // #1 Ein Baum-Raum (/std/room)
+    void create() {
+      ::create();
+      SetProp(P_INT_SHORT, "Auf einem Baum");
+      SetProp(P_INT_LONG, break_string("Du hockst auf einem Baum und kannst "
+        "auf die Lichtung unter Dir sehen.\n");
+
+      AddExit("unten", RAEUME("lichtung"));
+
+      SetProp(P_TARGET_AREA, RAEUME("lichtung"));  // Lichtung beschiessbar
+      SetProp(P_SHOOTING_AREA, 15);                // 15 Entfernung
+    }
+
+    // #2 Ein Elefanten-Transporter (/std/transport)
+    // Er trampelt durch mehrere Raeume durch und der Schuetze kann vom
+    // Ruecken des Elefanten aus auf Gegner draussen schiessen.
+    void create() {
+      ::create();
+      SetProp(P_NAME, "Kampfelefant");
+      AddId(({"elefant", "kampfelefant")});
+      SetProp(P_GENDER, MALE);
+      SetProp(P_SHORT, "Ein Kampfelefant");
+      SetProp(P_INT_SHORT, "Auf einem Kampfelefanten");
+      // P_LONG, P_INT_LONG
+
+      SetProp(P_ENTERCMDS, ({"kletter", "erkletter"}));
+      SetProp(P_LEAVECMDS, ({"verlass", "verlasse"}));
+
+      SetProp(P_ARRIVEMSG, ({"Der Elefant trampelt in einen Raum.\n",
+                             "Ein Kampfelefant trampelt herein.\n"}));
+      SetProp(P_DEPARTMSG, ({"Der Elefant trampelt weiter.\n",
+                             "Der Kampfelefant trampelt weiter.\n"}));
+
+      SetProp(P_SHOOTING_AREA, 8); // weiter als 8 sollte man schiessen
+
+      AddRoute(RAEUME("schlachtfeld"), 20+random(10), 6, "Schlachtfeld");
+      AddRoute(RAEUME("burgtor"), 20+random(10), 6, "Burgtor");
+      AddRoute(RAEUME("burghof"), 20+random(10), 6, "Burghof");
+      AddRoute(RAEUME("halle"), 20+random(10), 6, "Halle");
+      AddRoute(RAEUME("bresche"), 20+random(10), 6, "Bresche");
+      // ...
+
+      Start();
+    }
+
+SIEHE AUCH:
+    Generell:  P_AMMUNITION, P_SHOOTING_WC, P_STRETCH_TIME
+    Methoden:  FindRangedTarget(L), shoot_dam(L), cmd_shoot(L)
+    Gebiet:    P_RANGE, P_SHOOTING_AREA
+    Raeume:    P_NEVER_CLEAN
+    Sonstiges: fernwaffen
+
+29.Jul 2014 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_TEAM b/doc/props/P_TEAM
new file mode 100644
index 0000000..f03109d
--- /dev/null
+++ b/doc/props/P_TEAM
@@ -0,0 +1,24 @@
+NAME:
+	P_TEAM                         "team"
+
+DEFINIERT IN:
+	/sys/living/team.h
+
+BESCHREIBUNG:
+	Liefert das Teamobjekt, falls Spieler in einem Team ist, sonst 0.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD, P_TEAM_AUTOFOLLOW,
+                    P_TEAM_COLORS, P_TEAM_LEADER, P_TEAM_NEWMEMBER,
+                    P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      DeAssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/props/P_TEAM_ASSOC_MEMBERS b/doc/props/P_TEAM_ASSOC_MEMBERS
new file mode 100644
index 0000000..c4f95b8
--- /dev/null
+++ b/doc/props/P_TEAM_ASSOC_MEMBERS
@@ -0,0 +1,30 @@
+NAME:
+	P_TEAM_ASSOC_MEMBERS           "team_assoc_members"
+
+DEFINIERT IN:
+	/sys/living/team.h
+
+BESCHREIBUNG:
+	Array mit den zugeordneten NPCs des Spielers bzw. der Spieler,
+	dem dieser NPC zugeordnet ist.
+	Zugeordnete NPCs sind automatisch im Team des Spielers.
+
+BEMERKUNG:
+	Der Zugriff auf diese Property sollte ueber AssocMember()
+	bzw. DeAssocMember() erfolgen.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_TEAM_ATTACK_CMD, P_TEAM_AUTOFOLLOW,
+                    P_TEAM_COLORS, P_TEAM_LEADER, P_TEAM_NEWMEMBER,
+                    P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      DeAssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/props/P_TEAM_ATTACK_CMD b/doc/props/P_TEAM_ATTACK_CMD
new file mode 100644
index 0000000..3f77349
--- /dev/null
+++ b/doc/props/P_TEAM_ATTACK_CMD
@@ -0,0 +1,24 @@
+NAME:
+	P_TEAM_ATTACK_CMD              "team_attack_cmd"
+
+DEFINIERT IN:
+	/sys/living/team.h
+
+BESCHREIBUNG:
+	Angriffsbefehl des Spielers, nicht setzbar.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_AUTOFOLLOW,
+                    P_TEAM_COLORS, P_TEAM_LEADER, P_TEAM_NEWMEMBER,
+                    P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      DeAssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/props/P_TEAM_AUTOFOLLOW b/doc/props/P_TEAM_AUTOFOLLOW
new file mode 100644
index 0000000..45be50f
--- /dev/null
+++ b/doc/props/P_TEAM_AUTOFOLLOW
@@ -0,0 +1,24 @@
+NAME:
+	P_TEAM_AUTOFOLLOW              "team_autofollow"
+
+DEFINIERT IN:
+	/sys/living/team.h
+
+BESCHREIBUNG:
+	Folgewunsch des Spielers, nicht setzbar.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_COLORS, P_TEAM_LEADER, P_TEAM_NEWMEMBER,
+                    P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      DeAssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/props/P_TEAM_COLORS b/doc/props/P_TEAM_COLORS
new file mode 100644
index 0000000..d23815d
--- /dev/null
+++ b/doc/props/P_TEAM_COLORS
@@ -0,0 +1,25 @@
+NAME:
+	P_TEAM_COLORS                  "team_colors"
+
+DEFINIERT IN:
+	/sys/living/team.h
+
+BESCHREIBUNG:
+	Grenzwerte fuer farbige Anzeige im Teaminfo.
+	Array mit 4 Werten: ({ lp_rot, lp_gelb, sp_rot, sp_gelb })
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_LEADER, P_TEAM_NEWMEMBER,
+                    P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      DeAssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/props/P_TEAM_LEADER b/doc/props/P_TEAM_LEADER
new file mode 100644
index 0000000..a123c37
--- /dev/null
+++ b/doc/props/P_TEAM_LEADER
@@ -0,0 +1,24 @@
+NAME:
+	P_TEAM_LEADER                  "team_leader"
+
+DEFINIERT IN:
+	/sys/living/team.h
+
+BESCHREIBUNG:
+	Liefert das Teamobjekt, falls Spieler Anfuehrer eines Teams ist.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_NEWMEMBER,
+                    P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      DeAssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/props/P_TEAM_NEWMEMBER b/doc/props/P_TEAM_NEWMEMBER
new file mode 100644
index 0000000..f506c5b
--- /dev/null
+++ b/doc/props/P_TEAM_NEWMEMBER
@@ -0,0 +1,25 @@
+NAME:
+	P_TEAM_NEWMEMBER               "potential_team_member"
+
+DEFINIERT IN:
+	/sys/living/team.h
+
+BESCHREIBUNG:
+	Enthaelt das Objekt des Teamleaders, sobald ein Spieler um
+	Teamaufnahme gebeten hat, sonst 0.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      DeAssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/props/P_TEAM_WANTED_ROW b/doc/props/P_TEAM_WANTED_ROW
new file mode 100644
index 0000000..7cd3c36
--- /dev/null
+++ b/doc/props/P_TEAM_WANTED_ROW
@@ -0,0 +1,24 @@
+NAME:
+	P_TEAM_WANTED_ROW              "team_wanted_row"
+
+DEFINIERT IN:
+	/sys/living/team.h
+
+BESCHREIBUNG:
+	Gewuenschte Reihe des Spielers (von 1 bis MAX_TEAMROWS)
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD, P_TEAM_AUTOFOLLOW,
+                    P_TEAM_COLORS, P_TEAM_LEADER, P_TEAM_NEWMEMBER,
+                    P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      DeAssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/props/P_TEAM_WIMPY_ROW b/doc/props/P_TEAM_WIMPY_ROW
new file mode 100644
index 0000000..a832255
--- /dev/null
+++ b/doc/props/P_TEAM_WIMPY_ROW
@@ -0,0 +1,28 @@
+NAME:
+	P_TEAM_WIMPY_ROW               "team_wimpy_row"
+
+DEFINIERT IN:
+	/sys/living/team.h
+
+BESCHREIBUNG:
+	Fluchtreihe des Spielers, von 1 bis MAX_TEAMROWS.
+
+BEMERKUNG:
+	Wenn die Fluchtreihe <=1 ist, ist die Flucht in eine hintere Reihe
+	deaktiviert.
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD, P_TEAM_AUTOFOLLOW,
+                    P_TEAM_COLORS, P_TEAM_LEADER, P_TEAM_NEWMEMBER,
+                    P_TEAM_WANTED_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      DeAssocMember, InsertEnemyTeam, SelectNearEnemy,
+                    SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/props/P_TELNET_RTTIME b/doc/props/P_TELNET_RTTIME
new file mode 100644
index 0000000..fc30011
--- /dev/null
+++ b/doc/props/P_TELNET_RTTIME
@@ -0,0 +1,25 @@
+NAME:
+    P_TELNET_RTTIME                                  "p_lib_telnet_rttime"
+
+DEFINIERT IN:
+    /secure/telnetneg.h
+
+BESCHREIBUNG:
+    In dieser Properties steht die letzte gemessene 'round-trip' Zeit
+    (in Mikrosekunden) einer 'Telnet timing mark' vom MUD zum Client und
+    zurueck.
+
+    Voraussetzung hierfuer ist allerdings, dass das Telnet des Spielers
+    Telnetnegotiations unterstuetzt und 'telnet keepalive' eingeschaltet
+    ist, ansonsten bleibt diese Property 0.
+    Die meisten Telnets/Clients antworten zumindest eine Ablehnung auf
+    die 'timing marks', so dass trotzdem eine Zeit bestimmt werden kann.
+
+    Die Prop kann nicht gesetzt werden bzw. es hat keinen Effekt.
+
+SIEHE AUCH:
+    P_TTY_COLS, P_TTY_ROWS, P_TTY_SHOW, P_TTY, P_TTY_TYPE
+
+LETZTE AeNDERUNG:
+    03.02.2013, Zesstra
+
diff --git a/doc/props/P_TESTPLAYER b/doc/props/P_TESTPLAYER
new file mode 100644
index 0000000..6f64ade
--- /dev/null
+++ b/doc/props/P_TESTPLAYER
@@ -0,0 +1,29 @@
+NAME:
+    P_TESTPLAYER                  "testplayer"                  
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Gesetzt, wenn der Spieler ein Testspieler ist. Enthaelt die UID des
+     Magiers, dem dieser Testie (momentan) gehoert.
+     
+     Bei Testspielern duerfen Skills geaendert werden, sie duerfen gezappt
+     werden und - ihre eigentliche Aufgabe - nicht angeschlossene Gebiete
+     testen.
+
+     AUSNAHMEN: Gildentesties duerfen nur sehr eingeschraenkt manipuliert
+                werden werden, da sie im ganzen Mud rumlaufen koennen,
+                Spielerkontakt haben und nach Abschluss der Tests ggf. sogar
+                die Testiemarkierung entfernt werden kann.
+                
+     Fuer Spielertesties, die von einem Spieler kontrolliert werden, gelten
+     teilweise besondere Regeln, s. 'spielertesties'.
+
+BEMERKUNGEN: 
+     P_TESTPLAYER kann nur per SetProp() gesetzt werden und das auch nur ein
+     Mal! Geloescht werden kann das Flag nur von EM+.
+
+ZULETZT GEAeNDERT:
+05.01.2010, Zesstra
+
diff --git a/doc/props/P_TIMED_ATTR_MOD b/doc/props/P_TIMED_ATTR_MOD
new file mode 100644
index 0000000..1175c50
--- /dev/null
+++ b/doc/props/P_TIMED_ATTR_MOD
@@ -0,0 +1,59 @@
+NAME:
+    P_TIMED_ATTR_MOD         "timed_attr_mod"
+
+DEFINIERT IN:
+    /sys/living/attributes.h
+
+BESCHREIBUNG:
+    In dieser Property werden Attribut-Modifikatoren gespeichert, die
+    nicht ueber laengere Zeit wirksam sein sollen.
+    Die Wirksamkeit der Modifikatoren kann an Zeit und Objekte
+    gebunden werden.
+
+    Intern werden die Modifikatoren in einer Datenstruktur der Form
+
+    ({
+       ({ Ablaufzeiten }),
+       ([ Key : Ablaufobjekt ]),
+       ([ Key : ([ Mapping mit den Modifikatoren ]);
+         Ablaufzeit ; Ablaufobjekt ; Nachrichtenempfaenger
+       ])
+    })
+
+    gespeichert mit:
+    * Ablaufzeiten:  Zeit in Sekunden seit 1. Jan 1970, 0.0:0 GMT
+    * Ablaufobjekte: Objekte, an deren Existenz die Attribut-
+                     veraenderungen gebunden sind
+    * Nachrichtenempfaenger:
+      Objekte/Klassen, welche ueber abgelaufene Attributveraenderung
+      durch den Aufruf von "NotifyTimedAttrModExpired" (mit key als
+      Argument) benachrichtigt werden.
+
+    Das Setzen der Werte erfolgt NUR ueber die Methoden SetTimedAttrModifier
+    und DeleteTimedAttrModifier.
+
+    Die Daten zu einem Key koennen ueber QueryTimedAttrModifier abgefragt
+    werden. Die Abfrage mittels QueryProp liefert eine Kopie der gueltigen
+    Datenstruktur, die per Query nicht (siehe Bemerkungen).
+
+    Die Bedingungen fuer die ueber P_TIMED_ATTR_MOD gesetzten
+    Attributveraenderungen werden im Heartbeat in der Funktion
+    attribute_hb ueberprueft. Eine verminderte Funktionalitaet im
+    Falle von Magiern ist somit kein Fehlerfall.
+
+BEMERKUNGEN:
+    Keine echte Property. Die Methode _query_timed_attr_mod() in
+    /std/living/attributes.c stellt die Daten zusammen.
+
+    ACHTUNG: Bitte nur die bereitgestellten Methoden zur Manipulation
+             benutzen! Setzen als Property hat keinen Effekt.
+
+SIEHE AUCH:
+    QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),
+    SetAttribute(), SetRealAttribute(), UpdateAttributes(),
+    SetTimedAttrModifier(), QueryTimedAttrModifier(),
+    DeleteTimedAttrModifier(),
+    P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS, P_ATTRIBUTES_MODIFIER,
+    P_X_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c
+----------------------------------------------------------------------------
+Last modified: Tue Jul 27 20:00:20 2004 by Muadib
diff --git a/doc/props/P_TIMEZONE b/doc/props/P_TIMEZONE
new file mode 100644
index 0000000..8243d07
--- /dev/null
+++ b/doc/props/P_TIMEZONE
@@ -0,0 +1,10 @@
+NAME:
+    P_TIMEZONE                 "timezone"
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Ein Integer-Wert, der bei der Uhrzeitmeldung und beim Befehl
+     "(uhr)zeit" beruecksichtig wird. Gibt die Anzahl der Stunden
+     Zeitabweichung von Berliner Zeit an.
diff --git a/doc/props/P_TITLE b/doc/props/P_TITLE
new file mode 100644
index 0000000..49afd57
--- /dev/null
+++ b/doc/props/P_TITLE
@@ -0,0 +1,8 @@
+NAME:
+    P_TITLE                       "title"                       
+
+DEFINIERT IN:
+    /sys/player/description.h
+
+BESCHREIBUNG:
+     Titel des Spielers. Erscheint hinter dem Namen in Kurz/Langbeschreibung.
diff --git a/doc/props/P_TOO_HEAVY_MSG b/doc/props/P_TOO_HEAVY_MSG
new file mode 100644
index 0000000..27ef315
--- /dev/null
+++ b/doc/props/P_TOO_HEAVY_MSG
@@ -0,0 +1,31 @@
+NAME:
+    P_TOO_HEAVY_MSG                      "too_heavy_msg"                      
+
+DEFINIERT IN:
+    /sys/thing/moving.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt eine Meldung, die ausgegeben wird, wenn jemand
+     versucht, ein Objekt in einen Behaelter zu legen, fuer den dieses Objekt
+     zu schwer ist.
+     Die Property ist im Behaelter zu setzen.
+     Ist diese Property nicht oder auf einen nicht-String-Wert gesetzt,
+     so wird die Standardmeldung ausgegeben.
+     ("<Objekt> passt in <Behaelter> nicht mehr rein.")
+     Der String in der Property wird noch durch replace_personal()
+     verarbeitet, das zu bewegende Objekt wird als erstes, der Behaelter als
+     zweites Objekt angegeben. Danach wird der String auf 78 Zeichen
+     umgebrochen.
+     Das Setzen eines leeren Strings unterdrueckt die Ausgabe einer Meldung
+     ganz.
+
+BEISPIELE:
+     SetProp(P_TOO_HEAVY_MSG, "Wenn du @WEN1 noch in den Beutel stecken"
+			      " wuerdest, wuerde er reissen.");
+
+SIEHE AUCH:
+     Aehnliches: P_ENV_TOO_HEAVY_MSG, P_TOO_MANY_MSG, P_NOINSERT_MSG,
+                 P_NOLEAVE_MSG, P_NODROP, P_NOGET 
+     Erfolg:     P_PICK_MSG, P_DROP_MSG, P_GIVE_MSG, P_PUT_MSG,
+                 P_WEAR_MSG, P_WIELD_MSG
+     Sonstiges:  replace_personal(E), /std/living/put_and_get.c
diff --git a/doc/props/P_TOO_MANY_MSG b/doc/props/P_TOO_MANY_MSG
new file mode 100644
index 0000000..e23ce32
--- /dev/null
+++ b/doc/props/P_TOO_MANY_MSG
@@ -0,0 +1,31 @@
+NAME:
+    P_TOO_MANY_MSG                      "too_many_msg"                      
+
+DEFINIERT IN:
+    /sys/thing/moving.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt eine Meldung, die ausgegeben wird, wenn jemand
+     versucht, ein Objekt in einen Behaelter zu legen, der schon die maximale
+     Anzahl an Objekten enthaelt.
+     Die Property ist im Behaelter zu setzen.
+     Ist diese Property nicht oder auf einen nicht-String-Wert gesetzt,
+     so wird die Standardmeldung ausgegeben.
+     ("Dafuer ist nicht mehr genug Platz in <Behaelter>.")
+     Der String in der Property wird noch durch replace_personal()
+     verarbeitet, das zu bewegende Objekt wird als erstes, der Behaelter als
+     zweites Objekt angegeben. Danach wird der String auf 78 Zeichen
+     umgebrochen.
+     Das Setzen eines leeren Strings unterdrueckt die Ausgabe einer Meldung
+     ganz.
+
+BEISPIELE:
+     SetProp(P_TOO_MANY_MSG, "Aber der Korb hat doch nur drei Faecher, die"
+			     " sind alle schon voll.");
+
+SIEHE AUCH:
+     Aehnliches: P_TOO_HEAVY_MSG, P_ENV_TOO_HEAVY_MSG, P_NOINSERT_MSG,
+                 P_NOLEAVE_MSG, P_NODROP, P_NOGET 
+     Erfolg:     P_PICK_MSG, P_DROP_MSG, P_GIVE_MSG, P_PUT_MSG,
+                 P_WEAR_MSG, P_WIELD_MSG
+     Sonstiges:  replace_personal(E), /std/living/put_and_get.c
diff --git a/doc/props/P_TOTAL_AC b/doc/props/P_TOTAL_AC
new file mode 100644
index 0000000..222cffa
--- /dev/null
+++ b/doc/props/P_TOTAL_AC
@@ -0,0 +1,23 @@
+NAME:
+    P_TOTAL_AC                    "total_ac"                    
+
+DEFINIERT IN:
+    /sys/living/combat.h
+
+BESCHREIBUNG:
+     Numerischer Wert der Abwehrstaerke des Wesens.
+     Dieser wird durch Aufaddieren der P_AC aller getragenen Ruestungen
+     bestimmt. Aus diesem Grund ist das Abfragen dieser Property ziemlich
+     teuer. Falls das Ergebnis mehrfach kurz hintereinander gebraucht wird,
+     sollte die Property auf jeden Fall nur einmal abgefragt und der Wert
+     gespeichert werden.
+
+BEMERKUNGEN:
+    Auf diese Property sollte nicht mittels Query() oder Set() zugegriffen
+    werden, das Setzen von Query- oder Setmethoden bitte auf jeden Fall
+    unterlassen.
+
+SIEHE AUCH:
+    P_AC
+
+05.09.2008, Zesstra
diff --git a/doc/props/P_TOTAL_LIGHT b/doc/props/P_TOTAL_LIGHT
new file mode 100644
index 0000000..41006d2
--- /dev/null
+++ b/doc/props/P_TOTAL_LIGHT
@@ -0,0 +1,23 @@
+NAME:
+    P_TOTAL_LIGHT                 "total_light"
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Gibt das Lichtlevel an, das von einem Objekt ausgeht. Hierzu wird das
+    eigene Lichtlevel P_LIGHT mit dem gesamten Inhalt eines Containers
+    verrechnet.
+
+    Bitte _nur_ ueber QueryProp auf diese Property zugreifen,
+    da das Lichtlevel ggf. neu berechnet werden muss!
+
+    Ein direktes setzen dieser Property ist NICHT moeglich, hierzu bitte
+    P_LIGHT benutzen!
+
+BEMERKUNGEN:
+    Das ist die VON einem Objekt ausgehende Lichtstaerke. Fuer das IN einem
+    Raum herrschende Licht bitte P_INT_LIGHT abfragen!
+
+SIEHE AUCH:
+    P_LIGHT, P_INT_LIGHT, P_PLAYER_LIGHT, P_LIGHT_MODIFIER, CannotSee()
diff --git a/doc/props/P_TOTAL_OBJECTS b/doc/props/P_TOTAL_OBJECTS
new file mode 100644
index 0000000..5f01567
--- /dev/null
+++ b/doc/props/P_TOTAL_OBJECTS
@@ -0,0 +1,16 @@
+NAME:
+    P_TOTAL_OBJECTS                "total_objects"                
+
+DEFINIERT IN:
+    /sys/container.h
+
+BESCHREIBUNG:
+     Anzahl der Objekte im Container. Diese Property kann man nur abfragen!
+     Es werden nur Objekte gezaehlt, deren Methode short() einen
+     Wert != 0 zurueckgibt. Insofern koennen Spielern beliebig
+     viele unsichtbare Objekte gegeben werden ohne sie zu behindern.
+
+SIEHE AUCH:
+     P_MAX_OBJECTS
+
+26.Jan 2005 Gloinson
diff --git a/doc/props/P_TOTAL_WC b/doc/props/P_TOTAL_WC
new file mode 100644
index 0000000..35ef4a9
--- /dev/null
+++ b/doc/props/P_TOTAL_WC
@@ -0,0 +1,27 @@
+NAME:
+	P_TOTAL_WC			"total_wc"
+
+DEFINIERT IN:
+	/sys/properties.h
+
+BESCHREIBUNG:
+	In dieser Property wird der numerische Wert der Angriffsstaerke
+	eines Lebewesens registriert.
+  Hierzu werden die P_WC von P_WEAPON bzw. P_HANDS sowie die Kraft des
+  Lebewesens beruecksichtigt.
+	Nicht eingerechnet in die Angriffsstaerke sind natuerlich Extraspells und
+  -skills des Angreifers.
+  Braucht man den Wert mehrfach kurz hintereinander, sollte man die Prop aber
+  nur einmal abfragen und den Wert speichern (sofern sich in der Zwischenzeit
+  nichts an der Waffe, den Hands oder den Attributen des Lebenwesens aendert).
+
+BEMERKUNGEN:
+  Auf diese Property sollte nicht mittels Query() oder Set() zugegriffen 
+  werden, das Setzen von Query- oder Setmethoden bitte auf jeden Fall 
+  unterlassen.
+
+SIEHE AUCH:
+	P_HANDS, P_WC, P_XP
+
+----------------------------------------------------------------------------
+05.09.2008, Zesstra
diff --git a/doc/props/P_TOTAL_WEIGHT b/doc/props/P_TOTAL_WEIGHT
new file mode 100644
index 0000000..2b62db1
--- /dev/null
+++ b/doc/props/P_TOTAL_WEIGHT
@@ -0,0 +1,8 @@
+NAME:
+    P_TOTAL_WEIGHT                "total_weight"                
+
+DEFINIERT IN:
+    /sys/thing/restrictions.h
+
+BESCHREIBUNG:
+     Gewicht incl. Inhalt in Gramm. P_WEIGHT_PERCENT wird beruecksichtigt.
diff --git a/doc/props/P_TOUCH_DETAILS b/doc/props/P_TOUCH_DETAILS
new file mode 100644
index 0000000..d0670f6
--- /dev/null
+++ b/doc/props/P_TOUCH_DETAILS
@@ -0,0 +1,24 @@
+NAME:
+    P_TOUCH_DETAILS            "touch_details"
+
+DEFINIERT IN:
+    /sys/thing/description.h
+
+BESCHREIBUNG:
+    Diese Property entspricht dem P_DETAILS fuer Standarddetails,
+    nur werden hierin Gerueche gespeichert:
+    Diese Property enthaelt ein Mapping, in der Details im Objekt
+    definiert werden und Beschreibungen, die ausgegeben werden, wenn man
+    sich diese Details anschaut.
+
+BEMERKUNGEN:
+    Man sollte diese Property nicht per Hand veraendern, sondern die
+    Funktionen nutzen.
+
+SIEHE AUCH:
+    Setzen:    AddTouchDetail()
+    Loeschen:  RemoveTouchDetail()
+    Aehnlich:  AddDetail(), P_DETAILS
+    Sonstiges: GetDetail(), break_string()
+
+27. Jan 2013 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_TPORT_COST_IN b/doc/props/P_TPORT_COST_IN
new file mode 100644
index 0000000..d15e948
--- /dev/null
+++ b/doc/props/P_TPORT_COST_IN
@@ -0,0 +1,9 @@
+NAME:
+    P_TPORT_COST_IN               "tport_cost_in"               
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     In einem Raum mit Sehertor: Kostenanteil, um sich in den Raum zu
+     teleportieren
diff --git a/doc/props/P_TPORT_COST_OUT b/doc/props/P_TPORT_COST_OUT
new file mode 100644
index 0000000..369a35d
--- /dev/null
+++ b/doc/props/P_TPORT_COST_OUT
@@ -0,0 +1,9 @@
+NAME:
+    P_TPORT_COST_OUT              "tport_cost_out"              
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     In einem Raum mit Sehertor: Kostenanteil, sich aus dem Raum heraus
+     zu teleportieren
diff --git a/doc/props/P_TRANK_FINDEN b/doc/props/P_TRANK_FINDEN
new file mode 100644
index 0000000..8db2365
--- /dev/null
+++ b/doc/props/P_TRANK_FINDEN
@@ -0,0 +1,9 @@
+NAME:
+    P_TRANK_FINDEN                "trank_finden"                
+
+DEFINIERT IN:
+    /sys/player/potion.h
+
+BESCHREIBUNG:
+     Wenn die Property auf 1 steht kann immer ein Zaubertrank gefunden
+     werden, auch wenn er nicht in der Liste des Spielers steht.
diff --git a/doc/props/P_TRANSPARENT b/doc/props/P_TRANSPARENT
new file mode 100644
index 0000000..a081b00
--- /dev/null
+++ b/doc/props/P_TRANSPARENT
@@ -0,0 +1,29 @@
+P_TRANSPARENT
+NAME:
+     P_TRANSPARENT                 "transparent"
+
+DEFINIERT IN:
+     /sys/container.h
+
+BESCHREIBUNG:
+     ist != 0, wenn in einen Container hinein (offen) oder aus einem 
+     hinausgeschaut werden kann.
+
+     Schaut man aus einem hinaus, erhaelt der Spieler normalerweise die 
+     Meldung 'Ausserhalb siehst Du:'. Diese kann jedoch durch eine eigene, 
+     stimmigere  Meldung ersetzt werden, wenn in P_TRANSPARENT ein String 
+     mit dieser Meldung angegeben wird.
+
+BEISPIEL:
+     SetProp(P_TRANSPARENT,1); -> normale Meldung
+
+     SetProp(P_TRANSPARENT,"Vom Ruecken des Pferdes aus siehst Du:\n");
+
+     Diese Meldung ist natuerlich nur dann sinnvoll, wenn es sich
+     auch tatsaechlich um ein Pferd handelt :-)
+
+SIEHE AUCH:
+     int_long()
+
+----------------------------------------------------------------------------
+Last modified: Mon Jul 18 24:00:00 2001 by Tilly
diff --git a/doc/props/P_TRAVEL_CMDS b/doc/props/P_TRAVEL_CMDS
new file mode 100644
index 0000000..32f6f65
--- /dev/null
+++ b/doc/props/P_TRAVEL_CMDS
@@ -0,0 +1,38 @@
+NAME:
+    P_TRAVEL_CMDS                   "travel_cmds"                   
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+    Ein Array mit Befehlen, die zum Verlassen UND Betreten des Trans-
+    porters fuehren. 
+
+BEISPIEL:
+    void create()
+    {
+      ::create();
+
+      SetProp(P_TRAVEL_CMDS,({ "steig","steige" }) );
+
+    }
+
+    Als Parameter werden hier ausschliesslich 'auf,in' und 'von,aus'
+    verarbeitet.
+
+    steige auf|in  <xxx>    fuehrt also zum Betreten des Transporters,
+    steige von|aus <xxx>    dagegen fuehrt zum Verlassen desselben.
+
+BEMERKUNGEN:
+    Um /std/transport.c nicht aufzublaehen, werden weitere Parameter wie
+    etwa 'steige auf|in das|die|den xxx' _nicht_ unterstuetzt!
+
+    Hier muss der verantwortliche Magier schon eine eigene Loesung finden
+    und in seinen Transporter schreiben.
+
+SIEHE AUCH:
+    P_LEAVEFAIL, P_ENTERFAIL, P_ENTERCMDS, P_LEAVECMDS, transporter,
+
+LETZTER AENDERUNG:
+    Don, 24.01.2002, 10:15:07h von Tilly
+    
\ No newline at end of file
diff --git a/doc/props/P_TRAVEL_INFO b/doc/props/P_TRAVEL_INFO
new file mode 100644
index 0000000..b3a502e
--- /dev/null
+++ b/doc/props/P_TRAVEL_INFO
@@ -0,0 +1,31 @@
+NAME:
+    P_TRAVEL_INFO                 "travel_info"
+
+DEFINIERT IN:
+    /sys/transport.h
+
+BESCHREIBUNG:
+    Array mit Informationen zur vom Spieler gewuenschten Reiseroute.
+
+    [0]        Der Raum (object), in dem die Reiseroute momentan 
+               'aktiv' ist. Nur hier wird sie beruecksichtigt.
+
+    [1]        Das gewuenschte Transportmittel (object) falls 
+               gewaehlt. Ansonsten 0.
+
+    [2]        Der gewuenschte Zielort (string) oder 0 (ziellos).
+
+    [3]        Der gewuenschte Zielort als Richtung (string), falls
+               gewaehlt (z.B. 'zur Feuerinsel'). Sonst 0. Wird aus
+               P_HARBOUR des Zielraumes ausgelesen.
+
+BEMERKUNGEN:
+    Diese Property wird von /std/transport.c sowie std/player/travel.c
+    verwendet, und sollte NICHT von anderen Objekten oder per Hand 
+    veraendert werden!
+
+SIEHE AUCH:
+    /std/transport.c, /std/player/travel.c, reise
+
+LETZTER AENDERUNG:
+    Don, 24.01.2002, 10:15:07h von Tilly
diff --git a/doc/props/P_TRAY b/doc/props/P_TRAY
new file mode 100644
index 0000000..34c9d90
--- /dev/null
+++ b/doc/props/P_TRAY
@@ -0,0 +1,8 @@
+NAME:
+    P_TRAY                        "tray"                        
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    *** KEINE BESCHREIBUNG VORHANDEN ***
diff --git a/doc/props/P_TTY b/doc/props/P_TTY
new file mode 100644
index 0000000..9e4aded
--- /dev/null
+++ b/doc/props/P_TTY
@@ -0,0 +1,28 @@
+NAME:
+    P_TTY                         "tty"
+
+DEFINIERT IN:
+    /secure/telnetneg.h
+
+BESCHREIBUNG:
+     Name der Terminalemulation, die der Spieler nutzt.
+     Es existieren bisher "dumb", "vt100" und "ansi".
+
+	
+ANMERKUNG:
+     Farben duerfen ausschliesslich bei P_TTY=="ansi" benutzt werden.
+     Bei nicht farbfaehigen Terminals koennen ANSI-Codes die gesamte
+     Ausgabe zerschiessen!
+
+     Die Attribute fett, unterstrichen, blinkend und invers koennen auch
+     schon von vt100-Terminals dargestellt werden. Aber nicht ueberall
+     sind alle Attribute/Farben implementiert.
+
+     Bei allen ANSI-Codes sind drei Sachen zu beachten:
+     
+        1) Sparsam benutzen! Aufgezwungene Hervorhebungen koennen
+	   Spieler ganz schnell nerven.
+
+	2) Nicht jeder benutzt dieselbe Hintergrundfarbe!
+
+	3) Sparsam benutzen! Beser noch: nur im Notfall!
diff --git a/doc/props/P_TTY_COLS b/doc/props/P_TTY_COLS
new file mode 100644
index 0000000..1716a33
--- /dev/null
+++ b/doc/props/P_TTY_COLS
@@ -0,0 +1,22 @@
+NAME:
+    P_TTY_COLS                                  "tty_cols"
+
+DEFINIERT IN:
+    /secure/telnetneg.h
+
+BESCHREIBUNG:
+    In dieser Properties steht die Anzahl der Spalten, die das 
+    Terminalfenster des Spielers derzeit hat.
+
+    Voraussetzung hierfuer ist allerdings, dass das Telnet des Spielers
+    Telnetnegotiations unterstuetzt, ansonsten bleibt diese Property
+    leer.
+    Das Setzen der Property aendert die Fenstergroesse des Spielers
+    natuerlich nicht.
+
+SIEHE AUCH:
+    P_TTY_ROWS, P_TTY_TYPE, P_TTY_SHOW
+
+LETZTE AeNDERUNG:
+    Sat, 06.02.1999, 14:00:00 von Paracelsus
+
diff --git a/doc/props/P_TTY_ROWS b/doc/props/P_TTY_ROWS
new file mode 100644
index 0000000..1294d9b
--- /dev/null
+++ b/doc/props/P_TTY_ROWS
@@ -0,0 +1,22 @@
+NAME:
+    P_TTY_ROWS                                  "tty_rows"
+
+DEFINIERT IN:
+    /secure/telnetneg.h
+
+BESCHREIBUNG:
+    In dieser Properties steht die Anzahl der Zeilen, die das
+    Terminalfenster des Spielers derzeit hat.
+
+    Voraussetzung hierfuer ist allerdings, dass das Telnet des Spielers
+    Telnetnegotiations unterstuetzt, ansonsten bleibt diese Property
+    leer.
+    Das Setzen der Property aendert die Fenstergroesse des Spielers
+    natuerlich nicht.
+
+SIEHE AUCH:
+    P_TTY_COLS, P_TTY_TYPE, P_TTY_SHOW
+
+LETZTE AeNDERUNG:
+    Sat, 06.02.1999, 14:00:00 von Paracelsus
+
diff --git a/doc/props/P_TTY_SHOW b/doc/props/P_TTY_SHOW
new file mode 100644
index 0000000..f727433
--- /dev/null
+++ b/doc/props/P_TTY_SHOW
@@ -0,0 +1,17 @@
+NAME:
+    P_TTY_SHOW                                  "tty_show"
+
+DEFINIERT IN:
+    /secure/telnetneg.h
+
+BESCHREIBUNG:
+    Bei Telnets, die Telnetnegotiations unterstuetzen, wird eine Aenderung
+    der Fenstergroesse dem Spielerobjekt mitgeteilt. Steht in P_TTY_SHOW
+    ein Wert ungleich Null, wird dem Spieler diese Aenderung mitgeteilt.
+
+SIEHE AUCH:
+    P_TTY_ROWS, P_TTY_COLS, P_TTY_TYPE, telnegs
+
+LETZTE AeNDERUNG:
+    Sat, 06.02.1999, 14:00:00 von Paracelsus
+
diff --git a/doc/props/P_TTY_TYPE b/doc/props/P_TTY_TYPE
new file mode 100644
index 0000000..6935b23
--- /dev/null
+++ b/doc/props/P_TTY_TYPE
@@ -0,0 +1,23 @@
+NAME:
+    P_TTY_TYPE                                  "tty_type"
+
+DEFINIERT IN:
+    /secure/telnetneg.h
+
+BESCHREIBUNG:
+    In dieser Properties steht der Terminaltyp, den ein Spieler lokal auf
+    seinem Rechner verwendet.
+
+    Voraussetzung hierfuer ist allerdings, dass das Telnet des Spielers
+    Telnetnegotiations unterstuetzt, ansonsten bleibt diese Property
+    leer. Die meisten Telnets/Clients geben ihren Terminaltyp allerdings
+    nicht preis.
+    Das Setzen der Property aendert den Terminaltyp des Spielers
+    natuerlich nicht.
+
+SIEHE AUCH:
+    P_TTY_COLS, P_TTY_ROWS, P_TTY_SHOW
+
+LETZTE AeNDERUNG:
+    Sat, 06.02.1999, 14:00:00 von Paracelsus
+
diff --git a/doc/props/P_UNIT_DECAY_FLAGS b/doc/props/P_UNIT_DECAY_FLAGS
new file mode 100644
index 0000000..f924ff7
--- /dev/null
+++ b/doc/props/P_UNIT_DECAY_FLAGS
@@ -0,0 +1,70 @@
+P_UNIT_DECAY_FLAGS
+
+NAME:
+     P_UNIT_DECAY_FLAGS					"unit_decay_flags"
+
+DEFINIERT IN:
+     /sys/unit.h
+
+BESCHREIBUNG:
+     Mit dieser Prop kann das Zerfallsverhalten gesteuert werden, entweder
+     fuer alle Clones durch Setzen in der Blueprint oder fuer einzelne Clones.
+
+     In dieser Prop koennen momentan 4 Flags gesetzt werden:
+     - NO_DECAY: 
+          Zerfall ist abgeschaltet.
+     - NO_DECAY_UNTIL_MOVE: 
+          Der Zerfall ist solange ausgesetzt, bis dieses Objekt in ein anderes
+          Env bewegt wird. Setzt also ein NPC beim AddItem() diese Prop,
+          zerfaellt seine Unit nicht, bis sie bewegt wurde (Leiche, Spieler
+          etc.). Hierbei zaehlt das move() nicht, wenn das Objekt noch kein
+          Env hatte, es zaehlen nur Moves von einem Env in ein anderes Env.
+          Dieses Flag sollte nur in Clones gesetzt werden.
+     - INACCURATE_DECAY
+          Sollen z.b. 45.34 Einheiten zerfallen, wird der Rest von 0.34
+          normalerweise als Wahrscheinlichkeit aufgefasst, dass eine
+          zusaetzliche Einheit zerfaellt. Dieses Flag sorgt dafuer, dass
+          dieser Rest weggeworfen wird und einfach 45 Einheiten zerfallen.
+          Gleichzeitig wird bei diesem Flag aber _immer min_ 1 Einheit
+          zerstoert!
+     - ABSOLUTE_DECAY
+          P_UNIT_DECAY_QUOTA wird nicht als prozentualer Anteil aufgefasst,
+          sondern als absolute Zahl, d.h. es zerfallen immer einfach
+          P_UNIT_DECAY_QUOTA Einheiten.
+
+     Diese Flags koennen z.B. genutzt werden, den Zerfall fuer einzelne
+     Objekte temporaer oder dauerhaft abzuschalten, auch wenn alle anderen
+     Clones weiterzerfallen.
+
+     Diese Prop kann in der Blueprint gesetzt werden. In diesem Fall wird
+     allerdings NO_DECAY_UNTIL_MOVE ignoriert, weil die BP ja nie bewegt
+     wuerde. NO_DECAY in der BP schaltet den Zerfallsprozess (temporaer) fuer
+     alle Clones aus. Ist nie ein Zerfall gewuenscht, sollte in der Blueprint
+     aber besser P_UNIT_DECAY_INTERVAL auf 0 gesetzt werden!
+
+     Ist die Prop in einem einzelnen Clone nicht explizit gesetzt,
+     liefert ein klon->QueryProp(P_UNIT_DECAY_FLAGS) den in der Blueprint
+     eingestellten Wert zurueck.
+     
+BEMERKUNGEN:
+     * Setzt man diese Prop in einem Clone auf 0, wird der Wert aus der
+       Blueprint zurueckgeben. Hierbei wird allerdings ein NO_DECAY_UNTIL_MOVE
+       ausgefiltert, da dies den Zerfall fuer alle Objekte dauerhaft stoppen
+       wuerde, weil BPs nicht bewegt werden.
+     * Die Flags koennen "verodert" werden:
+       SetProp(P_UNIT_DECAY_FLAGS, NO_DECAY_UNTIL_MOVE | ABSOLUTE_DECAY);
+
+BEISPIEL:
+     // Dieser NPC hat tolle Pfeile, die sollen aber nicht zerfallen, solange
+     // sie im Inventar des NPCs sind:
+     AddItem("/d/tolleregion/tollermagier/obj/pfeile", REFRESH_NONE,
+         ([ P_AMOUNT: 50+random(50),
+            P_UNIT_DECAY_FLAGS: NO_DECAY_UNTIL_MOVE ]) );
+
+SIEHE AUCH:
+     unit
+     P_UNIT_DECAY_INTERVAL, P_UNIT_DECAY_QUOTA, P_UNIT_DECAY_MIN
+     DoDecay, DoDecayMessage
+     /std/unit.c
+
+14.10.2007, Zesstra
diff --git a/doc/props/P_UNIT_DECAY_INTERVAL b/doc/props/P_UNIT_DECAY_INTERVAL
new file mode 100644
index 0000000..93154cd
--- /dev/null
+++ b/doc/props/P_UNIT_DECAY_INTERVAL
@@ -0,0 +1,37 @@
+P_UNIT_DECAY_INTERVAL
+
+NAME:
+     P_UNIT_DECAY_INTERVAL					"unit_decay_interval"
+
+DEFINIERT IN:
+     /sys/unit.h
+
+BESCHREIBUNG:
+     Diese Prop bestimmt, wie oft ein Zerfall der entsprechenden Unitobjekte
+     durchgefuehrt wird. Das Intervall ist in Sekunden anzugeben (int).
+     Die Prop muss in der Blueprint der entsprechenden Unitobjekte gesetzt
+     werden, in Clones kann sie nicht gesetzt werden.
+     Die Blueprint resettet dann in diesem Intervall und ruft in allen ihren
+     Clones (und denen alter Versionen der gleichen BP!) DoDecay() auf,
+     woraufhin die Clones den Zerfall durchfuehren.
+     Ist die Prop in der Blueprint nicht gesetzt, erfolgt kein Zerfall.
+
+BEMERKUNGEN:
+     * Ist die Blueprint nicht geladen, erfolgt kein Zerfall der Clones.
+     * Ein Setzen dieser Prop beinhaltet immer auch einen Aufruf von
+       set_next_reset() auf das ensprechende Intervall.
+     * Die Prop kann in den Clones abgefragt werden und liefert das in der
+       Blueprint eingestellte Intervall.
+     * Von einer Manipulation per Set() wird dringend abgeraten.
+     * Die Prop kann nur vom Objekt selber, vom Programmierer des Objekts, vom
+       RM der entsprechenden Region, von einem Weisen oder von einem Objekt
+       gesetzt werden, welches die gleiche UID hat.
+
+BEISPIEL:
+
+SIEHE AUCH:
+     unit
+     P_UNIT_DECAY_QUOTA, P_UNIT_DECAY_FLAGS, P_UNIT_DECAY_MIN
+     DoDecay(), DoDecayMessage()
+
+13.10.2007, Zesstra
diff --git a/doc/props/P_UNIT_DECAY_MIN b/doc/props/P_UNIT_DECAY_MIN
new file mode 100644
index 0000000..79f9c80
--- /dev/null
+++ b/doc/props/P_UNIT_DECAY_MIN
@@ -0,0 +1,51 @@
+P_UNIT_DECAY_MIN
+
+NAME:
+     P_UNIT_DECAY_MIN					                    "unit_decay_min"
+
+DEFINIERT IN:
+     /sys/unit.h
+
+BESCHREIBUNG:
+     Diese Prop bestimmt, wieviele Einheiten der Unitobjekten mindestens
+     uebrig bleiben sollen. 
+     Faellt die Menge eines Unitobjekts unter diesen Wert, zerfaellt diese
+     Unit solange nicht weiter, bis der Wert wieder ueberschritten wird.
+     Die Prop kann in der Blueprint und in den einzelnen Clones gesetzt
+     werden.
+     Ist die Prop in einem einzelnen Clone nicht explizit gesetzt,
+     liefert ein QueryProp(P_UNIT_DECAY_MIN) den in der Blueprint
+     eingestellten Wert zurueck und die Unit zerfaellt bis zu dieser
+     Mindestmenge..
+     D.h. man sollte diese Prop in der Blueprint setzen und in einzelnen
+     Clones nur soweit diese abweichende Werte haben sollen.
+     Es sind nur Werte zwischen 0 und 100 zulaessig. Auf diese Art laesst sich
+     die minidestens uebrig bleibende Menge aller Clones durch Aendern einer
+     Prop in der Blueprint aendern.
+
+BEMERKUNGEN:
+     * Setzt man diese Prop in einem Clone auf 0, wird der Wert aus er
+       Blueprint zum Zerfall benutzt.
+     * Will man fuer ein bestimmtes Unitobjekt kein Minimum haben, also dass
+       dieses Objekt zerfaellt, bis nichts mehr da ist, die Blueprint hat aber
+       einen Minimalwert gesetzt, sollte diese Prop im betreffenden Objekt auf
+       -1 gesetzt werden.
+     * Diese Prop sollte vorsichtig angewandt werden, da Spieler so den
+       Zerfall von Units stoppen koennen, indem sie die Units entsprechend
+       aufteilen, so dass jedes Einzelobjekt unter dem Minimum liegt.
+
+BEISPIEL:
+     // es soll min. 1 Einheit uebrig bleiben.
+     SetProp(P_UNIT_DECAY_MIN, 1);
+
+     // die Blueprint hat ein Minimum von 10 gesetzt, dieser Clone soll
+     // aber zerfallen, bis nix mehr da ist.
+     klon->SetProp(P_UNIT_DECAY_MIN, -1);
+
+SIEHE AUCH:
+     unit
+     P_UNIT_DECAY_INTERVAL, P_UNIT_DECAY_FLAGS, P_UNIT_DECAY_QUOTA
+     DoDecay, DoDecayMessage
+     /std/unit.c
+
+14.10.2007, Zesstra
diff --git a/doc/props/P_UNIT_DECAY_QUOTA b/doc/props/P_UNIT_DECAY_QUOTA
new file mode 100644
index 0000000..9144564
--- /dev/null
+++ b/doc/props/P_UNIT_DECAY_QUOTA
@@ -0,0 +1,45 @@
+P_UNIT_DECAY_QUOTA (int)
+
+NAME:
+     P_UNIT_DECAY_QUOTA					"unit_decay_quota"
+
+DEFINIERT IN:
+     /sys/unit.h
+
+BESCHREIBUNG:
+     Diese Prop bestimmt, welcher Anteil der einzelnen Unitobjekte pro Zerfall
+     zerstoert wird. Dieser Anteil wird als ganze Zahl zwischen 0 und 10000
+     ausgedrueckt. 1 entspricht einem Zerfall von 0.01%, 10000 entspricht
+     100%.
+     Momentan sind keine Werte < 0 zulaessig, die einem Zuwachs entsprechend
+     wurden.
+
+     Falls das Flag ABSOLUTE_DECAY (s. P_UNIT_DECAY_FLAGS) gesetzt ist, steht
+     die Zahl in dieser Prop fuer die absolute Anzahl an zu zerstoerenden
+     Einheiten.
+
+     Die Prop kann in der Blueprint und in den einzelnen Clones gesetzt
+     werden.
+     Ist die Prop in einem einzelnen Clone nicht explizit gesetzt,
+     liefert ein QueryProp(P_UNIT_DECAY_QUOTA) den in der Blueprint
+     eingestellten Wert zurueck und die Unit zerfaellt zu diesem Anteil.
+     D.h. man sollte diese Prop in der Blueprint setzen und in einzelnen
+     Clones nur soweit diese abweichende Zerfallsraten haben sollen.
+
+BEMERKUNGEN:
+     * Setzt man diese Prop in einem Clone auf 0, wird der Wert aus er
+       Blueprint zum Zerfall benutzt.
+     * Will man den Zerfall fuer ein bestimmtes Unitobjekt abschalten, sollte
+       man P_UNIT_DECAY_FLAGS benutzen.
+
+BEISPIEL:
+     // pro Zerfallsintervall sollen 12% zerfallen.
+     SetProp(P_UNIT_DECAY_QUOTA, 1200);
+
+SIEHE AUCH:
+     unit
+     P_UNIT_DECAY_INTERVAL, P_UNIT_DECAY_FLAGS, P_UNIT_DECAY_MIN
+     DoDecay, DoDecayMessage
+     /std/unit.c
+
+14.03.2008, Zesstra
diff --git a/doc/props/P_UNWEAR_MSG b/doc/props/P_UNWEAR_MSG
new file mode 100644
index 0000000..1ee0d80
--- /dev/null
+++ b/doc/props/P_UNWEAR_MSG
@@ -0,0 +1,56 @@
+P_UNWEAR_MSG
+NAME:
+     P_UNWEAR_MSG                       "unwear_msg"     
+
+DEFINIERT IN:
+     /sys/armour.h
+
+BESCHREIBUNG:
+     Zweiteiliges Array mit Meldungen, die beim Ausziehen einer Ruestung 
+     oder Kleidung an den Spieler und die Umgebung ausgegeben werden.
+     Der erste Eintrag geht an den Spieler, der zweite Eintrag an die
+     Umgebung. Zeilenumbrueche werden automatisch gemacht, existierende
+     jedoch beruecksichtigt.
+
+     Platzhalter fuer Spieler ist @WExxx1, fuer die Waffe @WExxx2 (siehe
+     man replace_personal()).
+
+     [Wegen Abwaertskompatibilitaet ist auch noch der Platzhalter %s
+      moeglich, wobei in der eigenen Meldung %s fuer den Waffennamen steht,
+      in der an den Raum das erste %s fuer den Spielernamen, das zweite fuer
+      den Waffennamen.]
+
+BEISPIELE:
+    SetProp(P_NAME, "Mantel");
+    SetProp(P_UNWEAR_MSG,
+     ({"Du reisst Dir @WEN2 vom Leib.",
+       "@WER1 reisst sich @WENU2 vom Leib." }));
+
+    -> beim Ausziehen durch Urk:
+       Urk bekommt: Du reisst dir den Mantel vom Leib.
+       Der Raum:    Urk reisst sich einen Mantel vom Leib.
+
+    SetProp(P_UNWEAR_MSG,
+     ({"Dir wird furchtbar warm. So eine Hitze aber auch. Schnell "
+       "schluepfst Du aus Deiner dicken Ruestung. Aaaah, was fuer "
+       "eine Wohltat.",
+       "@WEM1 scheint ploetzlich warm zu werden. Schnell schluepft "
+       "@WERQP1 aus @WEMQPPFS1 dicken Ruestung. Du hoffst instaendig, "
+       "das es noch etwas waermer wird ... "}));
+
+    -> beim Ausziehen durch Urk:
+       Urk bekommt: Dir wird furchtbar warm. So eine Hitze aber auch.
+		    Schnell schluepfst Du aus Deiner dicken Ruestung.
+		    Aaaah, was fuer eine Wohltat.
+       Der Raum:    Urk scheint ploetzlich warm zu werden. Schnell
+		    schluepft er aus seiner dicken Ruestung. Du hoffst
+		    instaendig, das es noch etwas waermer wird ...
+SIEHE AUCH:
+     Aehnliches: P_WEAR_MSG, P_WIELD_MSG, P_UNWIELD_MSG
+                 P_DROP_MSG, P_PUT_MSG, P_GIVE_MSG, P_PICK_MSG
+     Funktionen: WearFunc, UnwearFunc
+     Sonstiges:  replace_personal(E), /std/armour/wear.c, armours
+                 clothing, /std/clothing.wear.c
+
+LETZTE AeNDERUNG:
+15.02.2009, Zesstra
\ No newline at end of file
diff --git a/doc/props/P_UNWIELD_FUNC b/doc/props/P_UNWIELD_FUNC
new file mode 100644
index 0000000..da0fa7c
--- /dev/null
+++ b/doc/props/P_UNWIELD_FUNC
@@ -0,0 +1,19 @@
+P_UNWIELD_FUNC
+
+NAME:
+     P_UNWIELD_FUNC "unwield_func"
+
+DEFINIERT IN:
+     <weapon.h>
+
+BESCHREIBUNG:
+     Falls ein Objekt eine UnwieldFunc() fuer die Waffe definiert, so muss
+     dieses Objekt in dieser Property eingetragen werden.
+
+     Die Auswertung dieser Property erfolgt in DoUnwield().
+
+SIEHE AUCH:
+     /std/weapon.c, UnwieldFunc()
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 15:44:08 1996 by Wargon
diff --git a/doc/props/P_UNWIELD_MSG b/doc/props/P_UNWIELD_MSG
new file mode 100644
index 0000000..49a6466
--- /dev/null
+++ b/doc/props/P_UNWIELD_MSG
@@ -0,0 +1,59 @@
+P_UNWIELD_MSG
+NAME:
+     P_UNWIELD_MSG                       "unwield_msg"                       
+
+DEFINIERT IN:
+     /sys/weapon.h
+
+BESCHREIBUNG:
+     Zweiteiliges Array mit Meldungen, die beim Wegstecken einer 
+     Waffe an den Spieler und die Umgebung ausgegeben werden.
+
+     Der erste Eintrag geht an den Spieler, der zweite Eintrag an die
+     Umgebung.  Zeilenumbrueche werden automatisch gemacht, existierende
+     jedoch beruecksichtigt.
+
+     Platzhalter fuer Spieler ist @WExxx1, fuer die Waffe @WExxx2 (siehe
+     man replace_personal()).
+
+     [Wegen Abwaertskompatibilitaet ist auch noch der Platzhalter %s
+      moeglich, wobei in der eigenen Meldung %s fuer den Waffennamen steht,
+      in der an den Raum das erste %s fuer den Spielernamen, das zweite fuer
+      den Waffennamen.]
+
+BEISPIELE:
+    SetProp(P_NAME, "Streitkolben");
+    SetProp(P_UNWIELD_MSG,
+     ({ "Du steckst @WEN2 zurueck und atmest erstmal tief durch.", 
+        "@WER1 steckt @WENU2 zurueck und atmet erstmal tief durch." }));
+
+    -> beim Wegstecken durch Urk:
+       Urk bekommt: Du steckst den Streitkolben zurueck und atmest erstmal
+		    tief durch.
+       Der Raum:    Urk steckt einen Streitkolben zurueck und atmet erstmal
+		    tief durch.
+
+    SetProp(P_UNWIELD_MSG,
+     ({"Du steckst die schwere Keule zurueck. Zufaellig landet sie "
+       "dabei auf Deinem Fuss. Laut schreiend humpelst Du in der "
+       "Gegend herum.",
+       "@WER1 steckt eine schwere Keule zurueck. Dummerweise landet diese "
+       "direkt auf dem eigenen Fuss. Aua, das tat sicher weh ... nicht "
+       "umsonst humpelt @WERQP1 jetzt schreiend durch die Gegend."}));
+
+    -> beim Wegstecken durch Urk:
+       Urk bekommt: Du steckst die schwere Keule zurueck. Zufaellig landet
+		    sie dabei auf Deinem Fuss. Laut schreiend humpelst Du in
+		    der Gegend herum.
+       Der Raum:    Urk steckt eine schwere Keule zurueck. Dummerweise
+		    landet diese direkt auf dem eigenen Fuss. Aua, das tat
+                    sicher weh ... nicht umsonst humpelt er jetzt schreiend
+                    durch die Gegend.
+
+SIEHE AUCH:
+     Aehnliches: P_WIELD_MSG, P_WEAR_MSG, P_UNWEAR_MSG
+                 P_DROP_MSG, P_PUT_MSG, P_GIVE_MSG, P_PICK_MSG
+     Funktionen: UnwieldFunc, WieldFunc
+     Sonstiges:  replace_personal(E), /std/weapon/combat.c
+
+29. Maerz 2004 Gloinson
diff --git a/doc/props/P_UNWIELD_TIME b/doc/props/P_UNWIELD_TIME
new file mode 100644
index 0000000..43128d4
--- /dev/null
+++ b/doc/props/P_UNWIELD_TIME
@@ -0,0 +1,19 @@
+P_UNWIELD_TIME
+
+NAME:
+      P_UNWIELD_TIME			"unwield_time"
+
+DEFINIERT IN:
+      /sys/weapon.h

+
+BESCHREIBUNG:
+      Enthaelt den Zeitpunkt zu dem ein Living eine Waffe weggesteckt hat und

+      ist im Living gesetzt.

+
+SIEHE AUCH:

+      Verwandt:		P_WEAPON, P_WIELDED, DoUnwield()

+			P_LAST_USE
+      Sonstiges:	P_EQUIP_TIME

+			time()
+
+10.Feb 2005 Gloinson

diff --git a/doc/props/P_USED_HANDS b/doc/props/P_USED_HANDS
new file mode 100644
index 0000000..456d32c
--- /dev/null
+++ b/doc/props/P_USED_HANDS
@@ -0,0 +1,21 @@
+P_USED_HANDS
+NAME:
+    P_USED_HANDS                  "used_hands"
+
+DEFINIERT IN:
+    /sys/living/combat.h
+
+BESCHREIBUNG:
+    Anzahl der Haende in Benutzung.
+    Effektiv nur ein sizeof(P_HANDS_USED_BY).
+
+BEMERKUNGEN:
+    Keine echte Property. Die Methode /std/living/combat::_query_used_hands
+    stellt die Daten zusammen. Nicht setzen!
+
+SIEHE AUCH:
+    P_HANDS, P_HANDS_USED_BY
+    P_MAX_HANDS, P_FREE_HANDS
+    UseHands, FreeHands
+
+1. Okt 2012, Gloinson
diff --git a/doc/props/P_VALID_GUILDS b/doc/props/P_VALID_GUILDS
new file mode 100644
index 0000000..2734ad9
--- /dev/null
+++ b/doc/props/P_VALID_GUILDS
@@ -0,0 +1,23 @@
+NAME:
+	P_VALID_GUILDS			"valid_guilds"                
+
+DEFINIERT IN:
+	/sys/new_skills.h
+
+BESCHREIBUNG:
+	Diese Property enthaelt die zugelassenen Gilden in Form von
+	kleingeschriebenen Gildennamen, welche in einem Array
+	zusammengefasst sind. Sie ist nur fuer den Gildenmaster selbst von
+	Bedeutung.
+
+BEISPIELE:
+	Abfrage der zugelassenen Gilden:
+	  find_object("/obj/gildenmaster")->QueryProp(P_VALID_GUILDS)
+	Das ergibt zum Beispiel:
+          ({"abenteurer","zauberer","klerus","kaempfer"})
+
+SIEHE AUCH:
+	P_GUILD, /obj/gildenmaster.c
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_VALUE b/doc/props/P_VALUE
new file mode 100644
index 0000000..d9b5961
--- /dev/null
+++ b/doc/props/P_VALUE
@@ -0,0 +1,26 @@
+NAME:
+    P_VALUE                       "value"                       
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+
+     - Objekte
+       Wert des Objektes in Goldmuenzen. Diesen Wert erhaelt man beim
+       Verkauf. Kaufen kostet ein Vielfaches hiervon.
+
+     - Speisen/Kneipen
+       Wert einer Portion der Speise.
+       
+BEMERKUNGEN:
+     In tragbaren Speisen (erben von /std/food) setzt man mit SetProp
+     den Wert _einer_ Portion. Per QueryProp erhaelt man aber den Gesamt-
+     wert der Speise inclusive des eventuell vorhandenen Behaelters. Der
+     Wert des Behaelters wird dabei aus P_EMPTY_PROPS[P_VALUE] gelesen.
+     
+SIEHE AUCH:
+     Speisen: std/pub, wiz/food, P_EMPTY_PROPS
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_VALUE_PER_UNIT b/doc/props/P_VALUE_PER_UNIT
new file mode 100644
index 0000000..da779c5
--- /dev/null
+++ b/doc/props/P_VALUE_PER_UNIT
@@ -0,0 +1,14 @@
+NAME:
+    P_VALUE_PER_UNIT              "value_per_unit"              
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Wert in Goldstuecken pro Untereinheit.
+
+BEMERKUNGEN:
+     Deprecated. Bitte SetCoinsPerUnits() (U_CPU) benutzen.
+
+25.Aug 2015 Gloinson
+
diff --git a/doc/props/P_VARIABLES b/doc/props/P_VARIABLES
new file mode 100644
index 0000000..5e7fb5c
--- /dev/null
+++ b/doc/props/P_VARIABLES
@@ -0,0 +1,16 @@
+
+NAME:
+    P_VARIABLES "variables"                 
+
+DEFINIERT IN:
+    /sys/magier.h
+
+BESCHREIBUNG:
+	
+     Interne Variable der Magiershell in dem die mit dem 'Set'-Befehl
+     gesetzten Variablen gespeichert werden.
+     
+     NICHT VON HAND VERAENDERN! IMMER 'SET' VERWENDEN!
+
+----------------------------------------------------------------------------
+Letzte Aenderung: 13.02.2003 22:00:00 von Mandragon
diff --git a/doc/props/P_VISIBLE_GUILD b/doc/props/P_VISIBLE_GUILD
new file mode 100644
index 0000000..df267b5
--- /dev/null
+++ b/doc/props/P_VISIBLE_GUILD
@@ -0,0 +1,25 @@
+P_VISIBLE_GUILD
+NAME:
+     P_VISIBLE_GUILD			"visible_guild"                       
+
+DEFINIERT IN:
+     /sys/properties.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt die sichtbare Gilde des Lebewesens in Form eines
+     kleingeschriebenen Strings, also die Gilde, die bei Spielern zum
+     Beispiel bei 'kwer' oder 'finger' angezeigt wird. So kann man fremde
+     Gilden testen und trotzdem nach aussen hin in der gleichen Gilde wie
+     zuvor bleiben.
+
+BEISPIEL:
+     Wenn man gerne nach aussen hin Zauberer bleiben moechte:
+	  pl->SetProp(P_VISIBLE_GUILD,"zauberer");
+     Nach aussen hin bleibt man jetzt auch Zauberer, wenn P_GUILD eine
+     andere Gilde angibt.
+
+SIEHE AUCH:
+     P_GUILD, P_DEFAULT_GUILD
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/P_VISIBLE_SUBGUILD_TITLE b/doc/props/P_VISIBLE_SUBGUILD_TITLE
new file mode 100644
index 0000000..e398f02
--- /dev/null
+++ b/doc/props/P_VISIBLE_SUBGUILD_TITLE
@@ -0,0 +1,21 @@
+P_VISIBLE_SUBGUILD_TITLE
+NAME:
+     P_VISIBLE_SUBGUILD_TITLE		"visible_subguild_title"                       
+DEFINIERT IN:
+     /sys/new_skills.h
+
+BESCHREIBUNG:
+     Diese Property dient dazu, als Magier einen Zusatztitel innerhalb einer
+     Gilde vorzutaeuschen, ohne den tatsaechlichen P_SUBGUILD_TITLE zu
+     aendern.
+
+BEMERKUNGEN:
+     Inhalt der Property kann 0 sein oder ein String.
+     Wenn der Inhalt 0 ist, wird bei QueryProp der P_SUBGUILD_TITLE
+     durchgereicht.
+
+SIEHE AUCH:
+     P_GUILD_TITLE, P_SUBGUILD_TITLE
+
+----------------------------------------------------------------------------
+Last modified: Mon Aug 13 21:20:00 2001 by Nachtwind
diff --git a/doc/props/P_VISUALBELL b/doc/props/P_VISUALBELL
new file mode 100644
index 0000000..19e8519
--- /dev/null
+++ b/doc/props/P_VISUALBELL
@@ -0,0 +1,37 @@
+NAME:
+	P_VISUALBELL			"visualbell"
+
+DEFINIERT IN:
+	/sys/properties.h
+
+BESCHREIBUNG:
+	Die Property stellt ein Flag innerhalb von Spielern dar, welches
+	standardmaessig nicht gesetzt ist. In diesem Fall werden Toene,
+	welche innerhalb einiger Funktionen erzeugt werden, auch wirklich an
+	den Spieler geschickt.
+	Setzt man die Property, so erhaelt der Spieler keine Toene mehr.
+
+BEISPIEL:
+	Pieptoene werden durch den ASCII-Code 0x7 praesentiert. Ausgeben
+	kann man diesen folgendermassen:
+	  if(!IS_WIZARD(caster)&&!victim->QueryProp(P_VISUALBELL))
+	    tell_object(victim,sprintf("%c",7));
+	Das waere beispielsweise ein Codestueck aus einem Piepspell. :)
+	Das Opfer bekommt den Piepton hierbei nur ab, wenn der Caster ein
+	Magier ist oder das Spieleropfer die Property P_VISUALBELL gesetzt
+	hat (kann mit Kommando 'ton' vom Spieler beeinflusst werden).
+
+BEMERKUNGEN:
+  Achtung: P_VISUALBELL steht auf 1, wenn der Spieler _keine_ Piepstoene
+	hoeren will!
+	Die Funktionalitaet dieser Property wirkt nur soweit, wie sie auch
+	von tonerzeugenden Befehlen selbst unterstuetzt wird. Es ist darauf
+	zu achten, dass P_VISUALBELL zu diesem Zweck grundsaetzlich
+	ausgewertet wird! Eine Ausnahme sei hierbei zugelassen: Magier
+	koennen Spielern grundsaetzlich Toene zusenden.
+
+SIEHE AUCH:
+	ton, wecke, erwarte, P_WAITFOR, /std/player/base.c
+
+----------------------------------------------------------------------------
+Last modified: 07.02.2007 by Zesstra
diff --git a/doc/props/P_VULNERABILITY b/doc/props/P_VULNERABILITY
new file mode 100644
index 0000000..7e1c676
--- /dev/null
+++ b/doc/props/P_VULNERABILITY
@@ -0,0 +1,37 @@
+NAME:
+     P_VULNERABILITY               "vulnerability"
+
+DEFINIERT IN:
+     /sys/living/combat.h
+
+WICHTIG:
+     DIESE PROPERTY IST VERALTET! BITTE P_RESISTANCE_STRENGTHS
+     VERWENDEN! AUCH FUNKTIONIERT Set() NICHT WIE ES SOLLTE.
+
+BESCHREIBUNG:
+     Hiermit koennen die Empfindlichkeiten eines Lebewesens definiert
+     werden. Es kann ein Array mit Schadensarten gesetzt werden, jeder
+     Eintrag eines Schadens verdoppelt die Empfindlichkeit gegen
+     diesen.
+
+BEMERKUNGEN:
+     - P_RESISTANCE_STRENGTHS spiegelt die Eintraege hier wieder
+     - um genauere Werte anzugeben einen AddResistanceModifier() oder
+       P_RESISTANCE_STRENGTHS benutzen.
+     - P_VULNERABILITY kann und wird nicht aus P_RESISTANCE_STRENGTHS
+       upgedatet
+
+BEISPIELE:
+     // ein NPC mit verdoppelter Eisempfindlichkeit und
+     // vervierfachter Wasserempfindlichkeit
+     SetProp(P_VULNERABILITY, ({DT_COLD, DT_WATER, DT_WATER}));
+
+SIEHE AUCH:
+     simple Resistenz:	P_RESISTANCE
+     Hauptmapping:	P_RESISTANCE_STRENGTHS
+     Modifikatoren:	AddResistanceModifier, RemoveResistanceModifier(),
+			P_RESISTANCE_MODIFIER
+     Berechnung:	CheckResistance(), UpdateResistanceStrengths()
+     anderes:		balance, /std/armour/combat.c, /std/living/combat.c
+
+1.Dez 2004, Gloinson
diff --git a/doc/props/P_WAITFOR b/doc/props/P_WAITFOR
new file mode 100644
index 0000000..a8cf8ee
--- /dev/null
+++ b/doc/props/P_WAITFOR
@@ -0,0 +1,14 @@
+NAME:
+     P_WAITFOR                     "waitfor"                     
+
+DEFINIERT IN:
+     /sys/player/base.h
+
+BESCHREIBUNG:
+     Die Erwarte-Liste. Ein Array mit den Namen der Erwarteten.
+
+SIEHE AUCH:
+     erwarte
+     P_WAITFOR_REASON, P_WAITFOR_FLAGS
+
+16. Feb 2008 Gloinson
diff --git a/doc/props/P_WAITFOR_FLAGS b/doc/props/P_WAITFOR_FLAGS
new file mode 100644
index 0000000..f137dd1
--- /dev/null
+++ b/doc/props/P_WAITFOR_FLAGS
@@ -0,0 +1,17 @@
+P_WAITFOR_FLAGS
+NAME:
+     P_WAITFOR_FLAGS                  "waitfor_flags"                     
+
+DEFINIERT IN:
+     /sys/player/base.h
+
+BESCHREIBUNG:
+     Ein Int. Bisher bekannte Flags:
+     
+     0x01 - "erwarte aus"
+
+SIEHE AUCH:
+     erwarte
+     P_WAITFOR, P_WAITFOR_REASON
+
+16. Feb 2008 Gloinson
diff --git a/doc/props/P_WAITFOR_REASON b/doc/props/P_WAITFOR_REASON
new file mode 100644
index 0000000..b19c821
--- /dev/null
+++ b/doc/props/P_WAITFOR_REASON
@@ -0,0 +1,18 @@
+P_WAITFOR_REASON
+NAME:
+     P_WAITFOR_REASON                  "waitfor_reason"                     
+
+DEFINIERT IN:
+     /sys/player/base.h
+
+BESCHREIBUNG:
+     Ein Mapping mit den Erwarteten als Schluessel und einem Grund als
+     Key, zB:
+     
+     (["Zook":"muh muh"])
+
+SIEHE AUCH:
+     erwarte (erwarte <wen> wegen <was>)
+     P_WAITFOR, P_WAITFOR_FLAGS
+
+16. Feb 2008 Gloinson
diff --git a/doc/props/P_WANTS_TO_LEARN b/doc/props/P_WANTS_TO_LEARN
new file mode 100644
index 0000000..a1553bd
--- /dev/null
+++ b/doc/props/P_WANTS_TO_LEARN
@@ -0,0 +1,12 @@
+NAME:
+    P_WANTS_TO_LEARN              "wants_to_learn"              
+
+DEFINIERT IN:
+    /sys/player/base.h
+
+BESCHREIBUNG:
+     Gesetzt, wenn der Magier die Filenamen sehen will.
+     (Nur fuer Magier). Wird diese Property auf 0 gesetzt, gehen auch
+     einige andere Dinge nicht mehr - verfolge zB. Eigentlich sollten
+     dann auch die Magierbefehle wie "goto" usw unterbunden werden -
+     das kommt vielleicht noch.
diff --git a/doc/props/P_WATER b/doc/props/P_WATER
new file mode 100644
index 0000000..6be4fe6
--- /dev/null
+++ b/doc/props/P_WATER
@@ -0,0 +1,110 @@
+NAME:
+    P_WATER                       "water"                       
+
+DEFINIERT IN:
+    /sys/fishing.h
+
+BESCHREIBUNG:
+    Enthaelt den Gewaessertyp. Kann in Raeumen, Angeln und Wasserbehaeltern
+    verwendet werden. Die verfuegbaren Optionen und Funktionsweisen sind in 
+    den nachfolgenden Abschnitten aufgefuehrt.
+
+    Raum:
+    *****
+      Legt den Typ des Gewaessers fest, das es in diesem Raum gibt. Von
+      diesem Typ haengt ab, welche Arten von Fischen es hier standardmaessig
+      gibt und welche Arten von Angeln verwendet werden koennen. 
+      
+      Beispiel:
+
+      SetProp(P_WATER, W_HARBOR);
+      
+      Folgende
+      Typen stehen zur Verfuegung, von denen in Raeumen nur einer gesetzt
+      werden darf:
+
+      Salzwasser:
+        W_BEACH   Strand: Scholle, Flunder, Rochen, Seezunge, Katzenhai
+        W_HARBOR  Hafen: Dorsch, Rochen, Seezunge, Hering, Katzenhai
+        W_OCEAN   Ozean/Meer: Hai, Thunfisch, Kabeljau, Schwertfisch, Seehase,
+                  Seeteufel, Seewolf
+
+      Suesswasser:
+        W_RIVER   Fluss: Piranha, Lachs, Forelle, Bachsaibling
+        W_POOL    Teich: Stichling, Goldfisch, Schlei, Karpfen, Goldorfe
+        W_LAKE    See: Karpfen, Barsch, Hecht, Seesaibling
+        W_ROCK    Bergbach: Lachs, Forelle, Bachsaibling
+        W_STREAM  Bach: Stichling, Bachforelle, Neuauge, Bachsaibling
+
+      Sonstige:
+        W_USER    wenn dieser Gewaessertyp gesetzt wird, MUSS der Raum 
+                  zusaetzlich die Funktion GetAquarium() definieren, die
+                  eine Liste der hier fangbaren Fische zurueckgeben muss.
+                  Beispiel:
+
+                  string* GetAquarium(){ 
+                    return ({"/d/ebene/fraggle/angel/fisch"}); 
+                  }
+        W_DEAD    Lebloses Wasser. Enthaelt keine Fische, man kann
+                  aber die Standardflasche fuellen.
+
+        W_OTHER   1024   // Flasche enthaelt Fluessigkeit!=Wasser
+
+
+    Angel:
+    ******
+      Angeln sind ueblicherweise auf bestimmte Anwendungsbereiche ausgelegt.
+      Ob eine Angel in einem Gewaesser benutzt werden kann, haengt davon ab,
+      ob P_WATER in der Angel den Gewaessertyp des Raumes enthaelt. Von den
+      oben genannten Typen koennen mehrere ver-ODER-t gesetzt werden.
+      Verwendung einer fuer das oertliche Gewaesser ungeeigneten Angel fuehrt
+      zu einer um 60+random(60) Sekunden verlaengerten Wartezeit beim Angeln.
+      
+      Beispiel: Setzt man den Gewaessertyp mit 
+
+        SetProp(P_WATER, W_HARBOR|W_OCEAN);
+
+      schaltet das die Angel sowohl fuer Haefen, als auch fuer offene Meere
+      (Ozeane) frei.
+
+      Folgende kombinierte Gewaessertypen sind fuer einfache Angeln 
+      vordefiniert:
+
+      Kurze Standardangeln:
+        W_SHORT W_HARBOR|W_RIVER|W_POOL|W_LAKE|W_ROCK|W_USER|W_OCEAN|W_STREAM
+      Spezielle Strandruten:
+        W_LONG  W_BEACH|W_USER
+      funktioniert in allen Salzgewaessern:
+        W_SALT  W_HARBOR|W_OCEAN|W_BEACH
+      funktioniert in allen Suessgewaessern:
+        W_SWEET W_RIVER|W_POOL|W_LAKE|W_ROCK|W_STREAM
+
+      Hinweis: W_DEAD ist in diesen Kombinationen nicht enthalten, da es
+      in solchen Gewaessern ohnehin keine Fische gibt.
+      Die Kombi-Typen enthalten W_USER, um bei entsprechenden Gewaessern
+      zu vermeiden, dass es dort standardmaessig einen Malus auf die 
+      Wartezeit gibt. Standardwert fuer P_WATER in Angeln ist ebenfalls 
+      W_USER.
+
+    Koeder:
+    *******
+      Auch Koeder koennen fuer die Verwendung in bestimmten Gewaessern besser
+      geeignet sein als in anderen, z.B. eine Seeschnecke fuer Salzwasser,
+      ein Mehlwurm hingegen fuer Suesswasser. Gesetzt wird P_WATER hierfuer
+      auf die oben aufgefuehrten Werte.
+      Verwendung eines ungeeigneten Koeders fuehrt zu einer um 60+random(60)
+      Sekunden laengeren Wartezeit beim Angeln.
+
+    Wasserbehaelter:
+    ****************
+      Die Property gibt an, ob der Behaelter Wasser enthaelt oder nicht.
+      Der Wert sollte immer auf den Typ jenes Gewaessers gesetzt sein, aus
+      dem der Behaelter aufgefuellt wurde.
+
+SIEHE AUCH:
+
+    Properties: P_FISH
+    Methoden:   GetAquarium(L)
+
+------------------------------------------------------------------------------
+Zuletzt geaendert: 2014-Aug-21, Arathorn
diff --git a/doc/props/P_WC b/doc/props/P_WC
new file mode 100644
index 0000000..042de3d
--- /dev/null
+++ b/doc/props/P_WC
@@ -0,0 +1,37 @@
+NAME:
+	P_WC				"wc"
+
+DEFINIERT IN:
+	/sys/weapon.h
+
+BESCHREIBUNG:
+	Die Waffenklasse (engl: weapon class), also die Staerke der Waffe,
+	stellt einen numerischen Wert dar, der umso groesser ist, desto mehr
+	Schaden eine Waffe im Kampf anrichtet. Beim Zuecken oder Wegstecken
+	einer Waffe durch ein Lebewesen wird innerhalb des Lebewesens auch
+	die Property P_TOTAL_WC aktualisiert, welche somit immer die
+	aktuelle Angriffsstaerke enthaelt. Beim Zuecken erhaelt sie hierbei
+	die Waffenklasse der Waffe und beim Wegstecken die Angriffsstaerke
+	aus der Property P_HANDS (Kaempfen mit blossen Haenden).
+	Die Waffenklasse von einhaendigen Waffen sollte 150 nicht
+	ueberschreiten, die Obergrenze fuer zweihaendige Waffen liegt bei
+	200. Ausnahmen von dieser Regel beduerfen der Absprache mit dem
+	Erzmagier fuer Ruestungen, Waffen und Monster!
+	Negative Werte bewirken keinen Schaden, allerdings auch keine
+	Heilung.
+
+BEMERKUNGEN:
+	Query- und Setmethoden auf P_WC sollten unbedingt vermieden werden. Sie
+	fuehren in der Regel zu massiven Inkonsistenzen im Mechanismus der 
+	Ruestungsbeschaedigung und -reparatur.
+	Auch mit einer HitFunc() duerfen die Obergrenzen nicht ohne
+	Absprache ueberschritten werden! Ausserdem ist es ratsam, die
+	zusaetzlichen Kampfeigenschaften in P_EFFECTIVE_WC gesondert
+	anzugeben.
+
+SIEHE AUCH:
+	/std/weapon.c, /std/weapon/combat.c
+	P_DAMAGED, P_EFFECTIVE_WC, P_WEAPON_TYPE
+	Damage()
+----------------------------------------------------------------------------
+02.10.2007, Zesstra
diff --git a/doc/props/P_WEAPON b/doc/props/P_WEAPON
new file mode 100644
index 0000000..5b1854c
--- /dev/null
+++ b/doc/props/P_WEAPON
@@ -0,0 +1,17 @@
+P_WEAPON
+
+NAME:
+     P_WEAPON                      "weapon"
+
+DEFINIERT IN:
+     /sys/properties.h
+
+BESCHREIBUNG:
+      Momentan gezueckte Waffe. Im Living gesetzt.
+
+SIEHE AUCH:
+      Verwandt:		P_ARMOURS
+      Waffen:		P_WC, P_WEAPON_TYPE, P_DAM_TYPE, P_NR_HANDS, P_PARRY
+      Sonstiges:	P_UNWIELD_TIME, P_EQUIP_TIME, P_LAST_USE
+
+10.Feb 2005 Gloinson
diff --git a/doc/props/P_WEAPON_TEACHER b/doc/props/P_WEAPON_TEACHER
new file mode 100644
index 0000000..913cfe7
--- /dev/null
+++ b/doc/props/P_WEAPON_TEACHER
@@ -0,0 +1,20 @@
+P_WEAPON_TEACHER   "weapon_teacher"
+----------------
+
+DEFINIERT IN:
+-------------
+
+combat.h
+
+BESCHREIBUNG
+------------
+
+Diese Property wird in einem Azubi gesetzt (zur Zeit nur fuer die 
+Kaempfer-Gilde), der selbst ueber die allgemeinen Waffenskills
+verfuegt.
+
+In diese Property wird der Name eines Kaempfergilden-Ausbilders
+eingetragen.
+
+Unter Anleitung des Ausbilders lernt der Azubi dann etwas schneller
+die allgemeinen Waffenskills.
diff --git a/doc/props/P_WEAPON_TYPE b/doc/props/P_WEAPON_TYPE
new file mode 100644
index 0000000..06f3c0b
--- /dev/null
+++ b/doc/props/P_WEAPON_TYPE
@@ -0,0 +1,31 @@
+P_WEAPON_TYPE
+
+NAME:
+     P_WEAPON_TYPE "weapon_type"
+
+DEFINIERT IN:
+     <weapon.h>
+
+BESCHREIBUNG:
+     Um was fuer eine Waffe handelt es sich? Es stehen verschiedene
+     Typen zur Auswahl. Man sollte hier nur die in <combat.h> definierten
+     Konstanten verwenden:
+
+        WT_AMMU           Munition fuer Fernkampfwaffen
+        WT_AXE            Axt
+        WT_CLUB           Keule
+        WT_HANDS          blosse Haende
+        WT_KNIFE          Messer, Dolch
+        WT_RANGED_WEAPON  Fernkampfwaffe
+        WT_SPEAR          Speer
+        WT_STAFF          Kampfstab
+        WT_SWORD          Schwert
+        WT_WHIP           Peitsche
+        WT_MISC           Sonstiges
+
+    Der Waffentyp WT_MISC ist schnoedem Tand und nutzlosem Kram vorbehalten.
+    Waffen dieses Typs duerfen keine P_WC > 0 besitzen oder kampfrelevante
+    Bedeutung haben.
+
+----------------------------------------------------------------------------
+Letzte Aenderung: 27. Mai 2015, Arathorn.
diff --git a/doc/props/P_WEAR_FUNC b/doc/props/P_WEAR_FUNC
new file mode 100644
index 0000000..edc9bcc
--- /dev/null
+++ b/doc/props/P_WEAR_FUNC
@@ -0,0 +1,24 @@
+P_WEAR_FUNC
+
+NAME:
+     P_WEAR_FUNC "wear_func"
+
+DEFINIERT IN:
+     <armour.h>
+
+BESCHREIBUNG:
+     Falls ein Objekt eine WearFunc() fuer die Ruestung / Kleidung definiert, 
+     so muss dieses Objekt in dieser Property eingetragen sein.
+
+     Die Auswertung dieser Property erfolgt in Laufe eines DoWear() in der
+     nicht-oeffentlichen Funktion _check_wear_restrictions()..
+
+BEISPIELE:
+     Siehe das Beispiel zu WearFunc()
+
+SIEHE AUCH:
+     armours, clothing, /std/clothing/wear.c, /std/armour/wear.c
+     WearFunc(), InformWear()
+
+LETZTE AeNDERUNG:
+15.02.2009, Zesstra
\ No newline at end of file
diff --git a/doc/props/P_WEAR_MSG b/doc/props/P_WEAR_MSG
new file mode 100644
index 0000000..31407fa
--- /dev/null
+++ b/doc/props/P_WEAR_MSG
@@ -0,0 +1,61 @@
+P_WEAR_MSG
+NAME:
+    P_WEAR_MSG                       "wear_msg"                       
+
+DEFINIERT IN:
+    /sys/armour.h
+
+BESCHREIBUNG:
+     Zweiteiliges Array mit Meldungen, die beim Anziehen einer Ruestung oder
+     Kleidung an den Spieler und die Umgebung ausgegeben werden.
+     Der erste Eintrag geht an den Spieler, der zweite Eintrag an die
+     Umgebung. Zeilenumbrueche werden automatisch gemacht, existierende
+     jedoch beruecksichtigt.
+
+     Platzhalter fuer Spieler ist @WExxx1, fuer die Waffe @WExxx2 (siehe
+     man replace_personal()).
+
+     [Wegen Abwaertskompatibilitaet ist auch noch der Platzhalter %s
+      moeglich, wobei in der eigenen Meldung %s fuer den Waffennamen steht,
+      in der an den Raum das erste %s fuer den Spielernamen, das zweite fuer
+      den Waffennamen.]
+
+BEISPIELE:
+    SetProp(P_NAME, "Helm");
+    SetProp(P_WEAR_MSG,
+     ({"Du stuelpst die @WEN2 ueber.", 
+       "@WER1 stuelpt sich @WENU2 ueber."}));
+
+    -> beim Anziehe durch Urk:
+       Urk bekommt: Du stuelpst dir den Helm ueber.
+       Der Raum:    Urk stuelpt sich einen Helm ueber.
+
+    SetProp(P_WEAR_MSG,
+     ({"Als Du Dir den langen Mantel ueberziehst, steckst Du erstmal "
+       "mit Deinem dicken Schaedel fest. Doch nach einem kraeftigen "
+       "Ruck bist Du endlich durch und siehst wieder etwas.",
+       "@WER1 zieht sich einen langen Mantel ueber und bleibt "
+       "prompt mit dem dicken Schaedel stecken. Doch nach einem "
+       "kraeftigen Ruck kann @WERQP1 wieder etwas sehen und grinst Dich "
+       "verlegen an."}));
+
+    -> beim Anziehen durch Urk:
+       Urk bekommt: Als Du Dir den langen Mantel ueberziehst, steckst Du
+		    erstmal mit Deinem dicken Schaedel fest. Doch nach einem
+		    kraeftigen Ruck bist Du endlich durch und siehst wieder
+		    etwas.
+
+       Der Raum:    Urk zieht sich einen langen Mantel ueber und bleibt
+		    prompt mit dem dicken Schaedel stecken. Doch nach
+		    einem kraeftigen Ruck kann er wieder etwas sehen und
+		    grinst Dich verlegen an.
+
+SIEHE AUCH:
+     Aehnliches: P_UNWEAR_MSG, P_WIELD_MSG, P_UNWIELD_MSG
+                 P_DROP_MSG, P_PUT_MSG, P_GIVE_MSG, P_PICK_MSG
+     Funktionen: WearFunc, UnwearFunc, InformWear()
+     Sonstiges:  replace_personal(E), clothing, /std/clothing/wear.c
+                 armour, /std/armour/wear.c
+
+LETZTE AeNDERUNG:
+15.02.2009
\ No newline at end of file
diff --git a/doc/props/P_WEIGHT b/doc/props/P_WEIGHT
new file mode 100644
index 0000000..22af5f0
--- /dev/null
+++ b/doc/props/P_WEIGHT
@@ -0,0 +1,26 @@
+NAME:
+    P_WEIGHT                      "weight"                      
+
+DEFINIERT IN:
+    /sys/thing/restrictions.h
+
+BESCHREIBUNG:
+
+     - Objekte
+       Das Gewicht eines Objetes in Gramm.
+
+     - Speisen
+       Gewicht einer Portion der Speise.
+       
+BEMERKUNGEN:
+     In tragbaren Speisen (erben von /std/food) setzt man mit SetProp
+     das Gewicht _einer_ Portion. Per QueryProp erhaelt man aber das
+     Gesamtgewicht der Speise inclusive des eventuell vorhandenen Behaelters.
+     Das Gewicht des Behaelters wird dabei aus P_EMPTY_PROPS[P_WEIGHT]
+     gelesen.
+     
+SIEHE AUCH:
+     Speisen: wiz/food, P_EMPTY_PROPS
+
+------------------------------------------------------------------------------
+Last modified: Thu Oct 28 12:15:00 2010 by Caldra
\ No newline at end of file
diff --git a/doc/props/P_WEIGHT_PERCENT b/doc/props/P_WEIGHT_PERCENT
new file mode 100644
index 0000000..7bb1ad3
--- /dev/null
+++ b/doc/props/P_WEIGHT_PERCENT
@@ -0,0 +1,25 @@
+NAME:
+    P_WEIGHT_PERCENT              "weight_percent"              
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Diese Property gibt an, wieviel Prozent des Gewichts des Inhaltes
+     "nach aussen" wiedergegeben werden.
+
+BEMERKUNGEN:
+     Alle Werte die < 50% liegen sollten sehr gut begruendet und mit Vor-
+     sicht verwendet werden. Hier koennten dann zum Beispiel P_MAX_OBJECTS
+     auf einen kleinen Wert begrenzt werden.
+
+     Container die hier einen Wert ueber dem des Postpakets haben, sollten
+     auch schwer zu erreichen sein. Auf jeden Fall mit dem Regionsmagier
+     besprechen!
+
+BEISPIELE:
+     Um sich zu orientieren kann das Postpaket von Loco als Beispiel hin-
+     zugezogen werden (/p/service/loco/obj/parcel).
+
+SIEHE AUCH:
+     P_MAX_OBJECTS, P_MAX_WEIGHT, P_LIGHT_TRANSPARENCY, container
diff --git a/doc/props/P_WEIGHT_PER_UNIT b/doc/props/P_WEIGHT_PER_UNIT
new file mode 100644
index 0000000..2796b7b
--- /dev/null
+++ b/doc/props/P_WEIGHT_PER_UNIT
@@ -0,0 +1,13 @@
+NAME:
+    P_WEIGHT_PER_UNIT             "weight_per_unit"             
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Gewicht in Gramm pro Untereinheit.
+
+BEMERKUNGEN:
+     Deprecated. Bitte SetGramsPerUnits() (U_GPU) benutzen.
+
+25.Aug 2015 Gloinson
diff --git a/doc/props/P_WIELDED b/doc/props/P_WIELDED
new file mode 100644
index 0000000..bce8580
--- /dev/null
+++ b/doc/props/P_WIELDED
@@ -0,0 +1,21 @@
+P_WIELDED
+
+NAME:
+     P_WIELDED "wielded"
+
+DEFINIERT IN:
+     <weapon.h>
+
+BESCHREIBUNG:
+     Ist diese Property gesetzt, dann ist die Waffe gerade gezueckt. Der
+     Traeger ist in der Property vermerkt.
+
+BEMERKUNGEN:
+     Diese Property laesst sich nur abfragen!
+
+SIEHE AUCH:
+     /std/weapon.c
+     P_WEAPON
+
+----------------------------------------------------------------------------
+Last modified: 2015-Jul-11, Arathorn 
diff --git a/doc/props/P_WIELD_FUNC b/doc/props/P_WIELD_FUNC
new file mode 100644
index 0000000..fac481b
--- /dev/null
+++ b/doc/props/P_WIELD_FUNC
@@ -0,0 +1,22 @@
+P_WIELD_FUNC
+
+NAME:
+     P_WIELD_FUNC "wield_func"
+
+DEFINIERT IN:
+     <weapon.h>
+
+BESCHREIBUNG:
+     Falls ein Objekt eine WieldFunc() fuer die Waffe definiert, so muss
+     dieses Objekt in dieser Property eingetragen werden.
+
+     Die Auswertung dieser Property erfolgt in wield_me().
+
+BEISPIELE:
+     Siehe das Beispiel zu WieldFunc()
+
+SIEHE AUCH:
+     /std/weapon.c, WieldFunc()
+
+----------------------------------------------------------------------------
+Last modified: Sun May 19 15:40:02 1996 by Wargon
diff --git a/doc/props/P_WIELD_MSG b/doc/props/P_WIELD_MSG
new file mode 100644
index 0000000..357a304
--- /dev/null
+++ b/doc/props/P_WIELD_MSG
@@ -0,0 +1,57 @@
+P_WIELD_MSG
+NAME:
+     P_WIELD_MSG                       "wield_msg" 
+
+DEFINIERT IN:
+     /sys/weapon.h
+
+BESCHREIBUNG:
+
+     Zweiteiliges Array mit Meldungen, die beim Zuecken einer Waffe an den
+     Spieler und die Umgebung ausgegeben werden.
+     Der erste Eintrag geht an den Spieler, der zweite Eintrag an die
+     Umgebung.  Zeilenumbrueche werden automatisch gemacht, existierende
+     jedoch beruecksichtigt.
+
+     Platzhalter fuer Spieler ist @WExxx1, fuer die Waffe @WExxx2 (siehe
+     man replace_personal()).
+
+     [Wegen Abwaertskompatibilitaet ist auch noch der Platzhalter %s
+      moeglich, wobei in der eigenen Meldung %s fuer den Waffennamen steht,
+      in der an den Raum das erste %s fuer den Spielernamen, das zweite fuer
+      den Waffennamen.]
+
+BEISPIELE:
+    SetProp(P_NAME, "Streitkolben");
+    SetProp(P_WIELD_MSG,
+     ({"Du zueckst @WEN2 und stoesst einen markerschuetternden Schrei aus.", 
+       "@WER1 zueckt @WENU2 und stoesst einen markerschuetternden Schrei "
+       "aus." }));
+
+    -> beim Zuecken durch Urk:
+       Urk bekommt: Du zueckst den Streitkolben und stoesst einen
+		    markerschuetternden Schrei aus.
+       Der Raum:    Urk zueckt einen Streitkolben und stoesst einen
+		    markerschuetternden Schrei aus.
+
+    SetProp(P_WIELD_MSG,
+     ({"Du zueckst den klobigen Streitkolben und fuchtelst damit "
+       "wild vor Deiner Nase herum.",
+       "@WER1 zueckt einen klobigen Streitkolben und fuchtelt "
+       "damit wild vor der eigenen Nase herum. Hoffentlich verletzt "
+       "@WERQP1 sich dabei nicht ..."}));
+
+    -> beim Zuecken durch Urk:
+       Urk bekommt: Du zueckst den klobigen Streitkolben und fuchtelst
+                    damit wild vor Deiner Nase herum.
+       Der Raum:    Urk zueckt einen klobigen Streitkolben und fuchtelt
+		    damit wild vor der eigenen Nase herum. Hoffentlich
+                    verletzt er sich dabei nicht ...
+
+SIEHE AUCH:
+     Aehnliches: P_UNWIELD_MSG, P_WEAR_MSG, P_UNWEAR_MSG
+                 P_DROP_MSG, P_PUT_MSG, P_GIVE_MSG, P_PICK_MSG
+     Funktionen: UnwieldFunc, WieldFunc
+     Sonstiges:  replace_personal(E), /std/weapon/combat.c
+
+29. Maerz 2004 Gloinson
diff --git a/doc/props/P_WIMPY b/doc/props/P_WIMPY
new file mode 100644
index 0000000..97633fa
--- /dev/null
+++ b/doc/props/P_WIMPY
@@ -0,0 +1,15 @@
+NAME:
+    P_WIMPY                       "wimpy"                       
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Numerischer Wert. Das Lebewesen flieht, wenn die Lebenspunkte
+     unter diesen Wert sinken.
+
+SIEHE AUCH:
+     P_WIMPY_DIRECTION
+
+----------------------------------------------------------------------------
+Letzte Aenderung: Mon Feb 12 17:50:47 2001 von Tilly
diff --git a/doc/props/P_WIMPY_DIRECTION b/doc/props/P_WIMPY_DIRECTION
new file mode 100644
index 0000000..57d2f01
--- /dev/null
+++ b/doc/props/P_WIMPY_DIRECTION
@@ -0,0 +1,22 @@
+NAME:
+    P_WIMPY_DIRECTION             "wimpy_dir"                   
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+     Fluchtrichtung eines Spielers oder NPCs
+
+BEMERKUNGEN:
+     Die Fluchtrichtung kann nicht nur ein Ausgang (sueden, osten, ...)
+     sein, sondern auch ein Kommando, welches der Spieler beim Anschlagen
+     ausfuehrt (z.b. <kletter seil hoch> oder <rufe Elender Mist!>).
+
+     Ausgefuehrt wird die Fluchtrichtung per command(), wenn die LP des 
+     Lebewesens unter die mit <vorsicht> angegebe LP-Grenze sinkt.
+
+SIEHE AUCH:
+     P_WIMPY
+
+----------------------------------------------------------------------------
+Letzte Aenderung: Mon Feb 12 17:46:47 2001 von Tilly
diff --git a/doc/props/P_WIZ_DEBUG b/doc/props/P_WIZ_DEBUG
new file mode 100644
index 0000000..cf7ac21
--- /dev/null
+++ b/doc/props/P_WIZ_DEBUG
@@ -0,0 +1,20 @@
+NAME:
+    P_WIZ_DEBUG                    "std_p_wizdebug"
+
+DEFINIERT IN:
+    /sys/player/comm.h
+
+BESCHREIBUNG:
+     Gesetzt, wenn der Magier (oder ein Testspieler) Debugmeldungen wahrnehmen
+     moechte.
+     Debugmeldungen sind Nachrichten, die mit dem Typ MT_DEBUG an ReceiveMsg()
+     uebergeben werden.
+     
+     Die Werte von P_WIZ_DEBUG sind zur Zeit 0 oder 1, das kann sich aber
+     jederzeit aendern.
+     Magier aendern diese Prop bitte ueber "mschau".
+
+SIEHE AUCH:
+     mschau
+     P_WANTS_TO_LEARN
+
diff --git a/doc/props/P_WORN b/doc/props/P_WORN
new file mode 100644
index 0000000..7afd03e
--- /dev/null
+++ b/doc/props/P_WORN
@@ -0,0 +1,40 @@
+P_WORN
+
+NAME:
+     P_WORN "worn"
+
+DEFINIERT IN:
+     <armour.h>
+
+BESCHREIBUNG:
+     Mittels dieser Property laesst sich ermitteln, ob eine Ruestung bzw. 
+     Kleidung derzeit getragen wird und wenn ja, von wem.
+
+     Entweder enthaelt die Property den Wert 0, oder sie enthaelt den
+     Traeger der Ruestung / Kleidung (als Objekt).
+
+BEMERKUNGEN:
+     Diese Property laesst sich nur abfragen!
+
+BEISPIELE:
+     Eine DefendFunc() koennte dem Traeger der Ruestung wie folgt etwas
+     mitteilen:
+
+     // Die Ruestung gibt Schutz gegen Feuer
+     int DefendFunc(string *dtyp, mixed spell, object enemy)
+     {
+       if (member(dtyp, DT_FIRE) != -1) {
+         // P_WORN ist auf jeden Fall gesetzt, da sonst die
+         // DefendFunc ueberhaupt nicht aufgerufen wuerde!
+         tell_object(QueryProp(P_WORN),
+           "Die Flammen zuengeln nur leicht ueber die Ruestung.\n");
+         return 10;
+       }
+       return 0;
+     }
+
+SIEHE AUCH:
+     clothing, /std/clothing.c, armour, /std/armour.c
+
+LETZTE AeNDERUNG:
+15.02.2009, Zesstra
\ No newline at end of file
diff --git a/doc/props/P_XP b/doc/props/P_XP
new file mode 100644
index 0000000..097ec65
--- /dev/null
+++ b/doc/props/P_XP
@@ -0,0 +1,59 @@
+NAME:
+     P_XP                    "xp"
+
+DEFINIERT IN:
+     /sys/living/life.h
+
+BESCHREIBUNG:
+     Diese Property enthaelt die Anzahl der Erfahrungspunkte, die ein
+     Lebewesen erreicht hat. Dies geschieht insbesondere durch
+     Kampfhandlungen, wobei es sowohl fuer Einzelschlaege als auch fuer
+     das Toeten eines Opfers Punkte gibt.
+
+     Bei einzelnen Schlaegen ist die Vergabe von Erfahrungspunkten davon
+     abhaengig, wie stark man das Opfer getroffen hat, und welche
+     Gesamtwaffenklasse es hat (damage*P_TOTAL_WC/10).
+
+     Beim Todesschlag erhaelt man zusaetzlich die Erfahrungspunkte des
+     Opfers geteilt durch 100 (P_XP/100). Dieser Wert wird allerdings
+     gegebenenfalls auf ein Team aufgeteilt, sofern der Angreifer sich in
+     einem solchigen befindet.
+
+BEISPIEL:
+     NPC's gibt man im allgemeinen einen levelabhaengigen Sockelwert an
+     Erfahrungspunkten mit, da sie nicht allzuoft selbst Gegner toeten
+     und somit kaum die Moeglichkeit haben, diese Punkte selbst
+     anzusammeln. Trotzdem sollen sie ja dem Spieler eine gewisse Anzahl
+     an Erfahrungspunkten liefern, wenn sie getoetet werden:
+
+       include "/sys/living/life.h"
+       inherit "std/npc";
+       void create() {
+         ...
+         SetProp(P_XP,25000000);
+       }
+
+     Beim Toeten gibt es nun 25.000.000/100 = 250.000 Erfahrungspunkte.
+     Damit wird der NPC sogar automatisch fuer die Vergabe von
+     Erstkillstufenpunkten vorgesehen.
+
+     Die Funktion create_default_npc() setzt P_XP und andere Eigenschaften
+     auf geeignete Werte.
+
+BEMERKUNGEN:
+     Die Vergabe von Erstkillstufenpunkten kann man ueber die Property
+     P_NO_SCORE unterbinden, die Vergabe von Erfahrungspunkten ueber
+     P_NO_XP. Automatisch werden Lebewesen fuer Erstkillstufenpunkte
+     vorgesehen, sofern sie eine der folgenden Grenzen ueberschritten
+     haben:
+       SCORE_LOW_MARK:  200000 (1 Stufenpunkt)
+       SCORE_HIGH_MARK: 600000 (2 Stufenpunkte)
+     Definiert sind die Konstanten in "/secure/scoremaster.h".
+
+SIEHE AUCH:
+     Funktionen:  AddExp(), do_damage()
+     Verwandt:    P_NO_XP, P_LAST_XP
+     Sonstiges:   P_NO_SCORE, create_default_npc()
+                  P_TOTAL_WC
+
+14.Feb 2007 Gloinson
\ No newline at end of file
diff --git a/doc/props/P_X_ATTR_MOD b/doc/props/P_X_ATTR_MOD
new file mode 100644
index 0000000..c6a8bda
--- /dev/null
+++ b/doc/props/P_X_ATTR_MOD
@@ -0,0 +1,46 @@
+NAME:
+    P_X_ATTR_MOD                  "extern_attributes_modifier"
+
+DEFINIERT IN:
+    /sys/living/attributes.h
+
+BESCHREIBUNG:
+    Mapping, das die Attribute des Spielers veraendert, der das Objekt bei
+    sich hat.
+
+    Zu beachten:
+    Diese Property bitte _ausschliesslich_ mit SetProp aendern, weil damit
+    gleichzeitig UpdateAttributes() im Lebewesen aufgerufen und ggf. das
+    Objekt als Statmodifizierer registriert wird.
+
+    Diese Property ist fuer Krankheiten, Flueche etc. gedacht. Bei
+    Waffen/Ruestungen, die die Attribute beeinflussen sollen, verwendet
+    man besser P_M_ATTR_MOD.
+
+    P_X_ATTR_MOD und P_M_ATTR_MOD duerfen einen gemeinsamen kumulierten
+    positiven Grenzwert nicht ueberschreiten. Dieser Grenzwert,
+    CUMULATIVE_ATTR_LIMIT, ist in /sys/living/attributes.h definiert.
+
+BEMERKUNGEN:
+    Die Methode /std/thing/restrictions::_set_extern_attributes_modifier()
+    benachrichtigt tragende Livings ueber Aenderungen.
+    Bitte beim "Loeschen" der Prop nicht den Wert des jew. Attributes im
+    uebergebenen Mapping als 0 uebergeben, sondern das Key/Werte-Paar ganz
+    entfernen und bzw. ein leeres Mapping oder 0 uebergeben.
+
+BEISPIEL:
+    // Dem Lebewesen, das das Objekt bei sich hat, wird 2 von A_INT abgezogen
+    SetProp(P_X_ATTR_MOD,([A_INT:-2]));
+
+    // Stats wiederherstellen:
+    SetProp(P_X_ATTR_MOD,([]));
+
+SIEHE AUCH:
+    QueryAttribute(), QueryRealAttribute(), QueryAttributeOffset(),
+    SetAttribute(), SetRealAttribute(), UpdateAttributes(),
+    SetTimedAttrModifier(), QueryTimedAttrModifier(),
+    DeleteTimedAttrModifier(),
+    P_X_HEALTH_MOD, P_M_HEALTH_MOD, P_ATTRIBUTES, P_ATTRIBUTES_OFFSETS,
+    P_TIMED_ATTR_MOD, P_M_ATTR_MOD, P_M_ATTR_MOD, /std/living/attributes.c
+
+02.02.2016, Gloinson
diff --git a/doc/props/P_X_HEALTH_MOD b/doc/props/P_X_HEALTH_MOD
new file mode 100644
index 0000000..d78d2bf
--- /dev/null
+++ b/doc/props/P_X_HEALTH_MOD
@@ -0,0 +1,36 @@
+NAME:
+    P_X_HEALTH_MOD                  "extern_health_modifier"
+
+DEFINIERT IN:
+    /sys/living/attributes.h
+
+BESCHREIBUNG:
+    Mapping, mit dem die maximalen Lebenspunkte und Magiepunkte eines
+    Spielers veraendert werden, der dieses Objekt bei sich traegt.
+
+    Zu beachten: Diese Property bitte _ausschliesslich_ mit SetProp
+    aendern, weil damit gleichzeitig UpdateAttributes() im
+    Lebewesen aufgerufen und ggf. das Objekt als Statmodifizierer 
+    registriert wird.
+
+    Bei Ruestungen/Waffen, die diese Wirkung entfalten sollen, verwendet
+    man besser P_M_HEALTH_MOD.
+
+BEMERKUNGEN:
+    Bitte bei "Loeschen" der Prop nicht den Wert des jew. Attributes im 
+    uebergebenen Mapping als 0 uebergeben, sondern das Key/Werte-Paar ganz 
+    entfernen und ggf. ein leeres Mapping oder 0 uebergeben.
+
+BEISPIEL:
+    // Dem Spieler, der das Objekt bei sich traegt, wird P_MAX_HP um 5
+    // erhoeht und P_MAX_SP um 5 erniedrigt.
+    SetProp(P_X_HEALTH_MOD,([P_HP:5,P_SP:-5]));
+    // Stats wiederherstellen:
+    SetProp(P_X_HEALTH_MOD,([]);
+
+SIEHE AUCH:
+    P_M_HEALTH_MOD, P_X_ATTR_MOD, P_M_ATTR_MOD, balance
+
+LETZTE AeNDERUNG:
+    Sat, 06.02.1999, 14:00:00 von Paracelsus
+
diff --git a/doc/props/P_ZAP_MSG b/doc/props/P_ZAP_MSG
new file mode 100644
index 0000000..fbabff4
--- /dev/null
+++ b/doc/props/P_ZAP_MSG
@@ -0,0 +1,28 @@
+P_ZAP_MSG
+
+NAME:
+      P_ZAP_MSG			"zap_msg"
+
+DEFINIERT IN:
+      /sys/properties.h
+
+BESCHREIBUNG:
+      Die Property enthaelt ein dreielementiges Array mit den folgenden

+      Eintraegen:
+	  1.) Meldung, die der Magier beim Zappen bekommt.
+	  2.) Meldung, die die Spieler im Raum beim Zappen bekommen.
+	  3.) Meldung, die das Opfer beim Zappen bekommt.
+
+      Mit @@wer@@, @@wessen@@, ... kann der Name des Opfers und mit @@ich@@

+      der Name des Magiers in die Meldung eingewoben werden.
+
+      Die Property ist in Magiern gesetzt und gilt nur dort.
+
+SIEHE AUCH:
+     Tod:		die(L)
+     Todesmeldungen:	P_KILL_NAME, P_KILL_MSG, P_DIE_MSG, P_MURDER_MSG
+			P_ENEMY_DEATH_SEQUENCE
+     Sonstiges:		P_CORPSE, P_NOCORPSE, /room/death/death_room.c
+
+----------------------------------------------------------------------------
+Last modified: Wed Jan 14 19:17:06 1998 by Patryn
diff --git a/doc/props/U_REQ b/doc/props/U_REQ
new file mode 100644
index 0000000..cb395db
--- /dev/null
+++ b/doc/props/U_REQ
@@ -0,0 +1,63 @@
+NAME:
+    U_REQ                         "u_req"
+
+DEFINIERT IN:
+    /sys/unit.h
+
+BESCHREIBUNG:
+     Die Prop kann in Unitobjekten gesetzt werden.
+     Sie gibt die Anzahl der Einheiten an, die an der Unit manipuliert werden
+     sollen, falls mit weniger als P_AMOUNT umgegegangen werden soll.
+     
+     Die Prop wird automatisch gesetzt, wenn id() an einem Unitobjekt gerufen
+     wird und die ID grundsaetzlich zutreffend ist, die aus der ID ermittelte
+     Zahl aber kleiner als P_AMOUNT ist.
+     Sie kann auch manuell mittel SetProp() (aber nicht Set()) gesetzt werden.
+
+     U_REQ wird beim Bewegen und Zerstoeren, bei Ermittlung von Wert und
+     Gewicht beruecksichtigt.
+
+     U_REQ wird vom Unitobjekt automatisch wieder auf P_AMOUNT gesetzt, wenn
+     sich query_verb() oder debug_info(DINFO_EVAL_NUMBER) veraendert haben.
+     (DINFO_EVAL_NUMBER ist eine Zahl, die sich jedesmal erhoeht, wenn der
+      Driver eine neue Berechnung/Ausfuehrung beginnt. Diese Nummer wird fuer
+      jeden vom driver initiierten Aufruf von LPC-Code erhoeht, z.B. bei jedem
+      Kommando, call_out, heart_beat etc. Details s. debug_info().)
+
+     Ebenso wird U_REQ bei der Vereinigung mit einem anderen (gleichen)
+     Objekt auf P_AMOUNT des vereinigten Objektes gesetzt.
+
+
+BUGS
+    Viele. Dies ist ein uebler Hack. Geht aber nicht ohne.
+    Diese Prop war endlos lang gar nicht dokumentiert. Hier beschrieben ist
+    das Verhalten, was zur Zeit vorliegt. Dies mag unterschiedlich zu dem
+    irgendwann intendierten sein.
+
+
+BEISPIELE
+    object o = clone_object("unitobjekt");
+    o->SetProp(P_AMOUNT, 100);   // ab jetzt hat o 100 Einheiten.
+    o->move(npc, M_GET);         // ob mit 100 Einheiten wird bewegt
+    o->SetProp(U_REQ, 50);
+    o->move(anderernpc, M_GIVE); // 50 Einheiten werden bewegt, 50 verbleiben
+    (Technisch: das Objekt wird auf 50 Einheiten geaendert, bewegt und in der
+     alten Umgebung wird ein neues Objekt mit 50 Einheiten erzeugt.)
+
+    o->SetProp(U_REQ, 42);
+    o->remove(1);               // 42 Einheiten von den 50 werden zerstoert.
+    (Technisch: P_AMOUNT wird einfach um U_REQ reduziert.)
+
+    # gib 18 muenzen an blupp
+    Hierbei wird ob->id("18 muenzen") gerufen, was U_REQ im Geldobjekt auf 18
+    setzt. Bei der Bewegung bekommt blupp daher das Objekt mit P_AMOUNT==18
+    und der Rest wird im Abgebenden neu erzeugt.
+    # gib geld an flubbel
+    Das U_REQ aus dem verherigen Kommando ist jetzt nicht mehr gueltig. Zwar
+    ist es das gleiche Kommandoverb, aber DINFO_EVAL_NUMBER ist jetzt
+    anders.
+
+
+ZULETZT GEAeNDERT:
+16.01.2015, Zesstra
+
diff --git a/doc/props/gildenprops/.synonym b/doc/props/gildenprops/.synonym
new file mode 100644
index 0000000..17607d8
--- /dev/null
+++ b/doc/props/gildenprops/.synonym
@@ -0,0 +1,9 @@
+K_BRAWLING_DT kaempferboni
+K_BRAWLING_MSG kaempferboni
+K_BRAWLING_WC kaempferboni
+K_CRITICAL_HIT kaempferboni
+K_DISTRACTING_WEAPON kaempferboni
+K_KO kaempferboni
+K_NO_HONING kaempferboni
+K_THROWING_WEAPON kaempferboni
+K_WEAPON_SHATTER kaempferboni
diff --git a/doc/props/gildenprops/P_GLOVE_FINGERLESS b/doc/props/gildenprops/P_GLOVE_FINGERLESS
new file mode 100644
index 0000000..64faec6
--- /dev/null
+++ b/doc/props/gildenprops/P_GLOVE_FINGERLESS
@@ -0,0 +1,16 @@
+P_GLOVE_FINGERLESS
+
+NAME:
+     P_GLOVE_FINGERLESS                     "fingerfreie_handschuhe"
+
+DEFINIERT IN:
+     /p/katzenkrieger/kkrieger.h
+
+BESCHREIBUNG:
+     So gekennzeichnete Handschuhe sind fingerlos und koennen
+     waehrend "krallenschlag" getragen werden.
+
+SIEHE AUCH:
+     Verwandt:	P_ARMOUR_TYPE, AT_GLOVE
+
+10.Okt 2006 Gloinson
\ No newline at end of file
diff --git a/doc/props/gildenprops/P_Z_AUTOSHIELD b/doc/props/gildenprops/P_Z_AUTOSHIELD
new file mode 100644
index 0000000..f69b9ad
--- /dev/null
+++ b/doc/props/gildenprops/P_Z_AUTOSHIELD
@@ -0,0 +1,26 @@
+P_Z_AUTOSHIELD
+
+NAME:
+     P_Z_AUTOSHIELD				"z_autoshield"
+
+DEFINIERT IN:
+     /p/zauberer/wiznpc.h
+
+BESCHREIBUNG:
+     Mit dieser Property  kann man einen automatischen 
+     Schutzzauber in Zauberer-NPCs einstellen, dessen 
+     Vorhandensein dann in jeder Kampfrunde ueberprueft
+     wird, z.B. "schutz","schutzhuelle","zauberschild".
+
+BEMERKUNGEN:
+     "zauberschild" ist ein Magisterspruch und kann nur in 
+     bestimmten Zeitabstaenden benutzt werden. Wer also als
+     Autoshield nur Zauberschild benutzt, blockiert damit
+     alle anderen Spells, solange der Magisterspruch nicht
+     gesprochen werden kann.
+     Abhilfe: _query_next_master_spell_time()  return 0; }
+
+SIEHE AUCH:
+     /p/zauberer/text/wiznpc.doku
+
+21.Okt 2006 Chagall
diff --git a/doc/props/gildenprops/P_Z_INFO b/doc/props/gildenprops/P_Z_INFO
new file mode 100644
index 0000000..d82d38a
--- /dev/null
+++ b/doc/props/gildenprops/P_Z_INFO
@@ -0,0 +1,53 @@
+P_Z_INFO
+
+NAME:
+     P_Z_INFO						"z_info"
+
+DEFINIERT IN:
+     /p/zauberer/wiznpc.h
+
+BESCHREIBUNG:
+     Diese Property muss gesetzt werden, wenn man den
+     Zauberergilden-Standard-NPC nutzt. Sie setzt die
+     Grundeinstellungen des NPCs und generiert das 
+     Newskills-Mapping. 
+
+     Die Property ist wie folgt aufgebaut:
+
+     * MIN (minimale Skillability im Bereich von 0 - 10000)
+     * MAX (maximale Skillability im Bereich von 0 - 10000)
+     * LEV (Gildenlevel)
+     * ZWEIG (Gildenzweig)
+
+
+BEMERKUNGEN:
+     Fuer die Argumente LEV und ZWEIG stehen folgende Auswahl-
+     moeglichkeiten zur Verfuegung.
+
+     LEV:
+        Z_ANFAENGER	0
+	Z_LEHRLING	1
+	Z_MEISTER	2
+	Z_ERZMEISTER	3
+
+     ZWEIG:
+        ZZW_ANGRIFF		  1
+	ZZW_ABWEHR		  2
+	ZZW_ILLUSION		  4
+	ZZW_BEHERRSCHUNG	  8
+	ZZW_HELLSICHT		 16
+	ZZW_VERWANDLUNG		 32
+	ZZW_SCHWARZ		 64	INAKTIV
+	ZZW_WEISS		128
+	ZZW_ALLE		511
+
+BEISPIEL:
+     SetProp(P_Z_INFO, ({9000, 9500, Z_ERZMEISTER, ZZW_ANGRIFF|ZZW_ABWEHR}));
+     erzeugt einen Erzmagister-PC, der alle Lehrlings- sowie die Magister und
+     Erzmagister-Sprueche Angriff und Abwehr mit 90-95% beherrscht.
+
+
+SIEHE AUCH:
+     /p/zauberer/text/wiznpc.doku
+
+21.Okt 2006 Chagall
diff --git a/doc/props/gildenprops/P_Z_NO_MATERIAL b/doc/props/gildenprops/P_Z_NO_MATERIAL
new file mode 100644
index 0000000..4b040a0
--- /dev/null
+++ b/doc/props/gildenprops/P_Z_NO_MATERIAL
@@ -0,0 +1,18 @@
+P_Z_NO_MATERIAL
+
+NAME:
+     P_Z_NO_MATERIAL                     "npc_braucht_keine_kmp"
+
+DEFINIERT IN:
+     /p/zauberer/zauberer.h
+
+BESCHREIBUNG:
+     Setzt man diese Property in einem NPC, so benoetigt er fuer die
+     Sprueche der Zauberergilde keine Komponenten.
+
+BEMERKUNGEN:
+     NIEMALS diese Property einfach so in einem Spieler setzen.
+
+SIEHE AUCH:
+
+21.Okt 2006 Chagall
diff --git a/doc/props/gildenprops/P_Z_NO_NOCONN b/doc/props/gildenprops/P_Z_NO_NOCONN
new file mode 100644
index 0000000..332dd14
--- /dev/null
+++ b/doc/props/gildenprops/P_Z_NO_NOCONN
@@ -0,0 +1,22 @@
+P_Z_NO_NOCON
+
+NAME:
+     P_Z_NO_NOCON					"no_nocon"
+
+DEFINIERT IN:
+     /p/zauberer/wiznpc.h
+
+BESCHREIBUNG:
+     Der Standardzauberergildennpc setzt SI_NO_CONSEQUENCES, damit
+     die Gildenpcs nicht den negativen Auswirkungen beim Misslingen
+     der Sprueche geschuetzt sind. Setzt man diese Property vor
+     P_Z_INFO auf 0, wird das Flag nicht gesetzt.
+
+
+BEMERKUNGEN:
+     Muss vor P_Z_INFO gesetzt werden, damit es wirksam ist.
+
+SIEHE AUCH:
+     /p/zauberer/text/wiznpc.doku, P_Z_INFO
+
+21.Okt 2006 Chagall
diff --git a/doc/props/gildenprops/P_Z_NO_SPELL_SUPPORT b/doc/props/gildenprops/P_Z_NO_SPELL_SUPPORT
new file mode 100644
index 0000000..4a1ab28
--- /dev/null
+++ b/doc/props/gildenprops/P_Z_NO_SPELL_SUPPORT
@@ -0,0 +1,25 @@
+P_Z_NO_SPELL_SUPPORT
+
+NAME:
+     P_Z_NO_SPELL_SUPPORT	"zauberer_ruestung_unterstuetzt_noch_nicht"
+
+DEFINIERT IN:
+     /p/zauberer/zauberer.h
+
+BESCHREIBUNG:
+     Normalerweise unterstuetzt eine Ruestung den Zauberer, sobald sie im
+     entsprechenden Ruestungsmaster eingetragen ist. Moechte man allerdings
+     die Unterstuetzung an bestimmte Bedingungen knuepfen, z.B. das loesen
+     einer Miniquest, so kann man diese Property auf 1 setzen. Die Ruestung
+     unterstuetzt dann nicht. Um die Unterstuetzung wieder zu aktivieren,
+     setzt man die Property wieder auf 0.
+
+BEMERKUNGEN:
+     NIEMALS diese Property einfach so in fremden Zaubererruestungen 
+     setzen. Sollte der Gildenmagier erfahren, dass z.B. ein NPC
+     einfach so die Unterstuetzung der Ruestungen ausschaltet, wird
+     diese Property wieder deaktiviert.
+
+SIEHE AUCH:
+
+21.Okt 2006 Chagall
diff --git a/doc/props/gildenprops/kaempferboni b/doc/props/gildenprops/kaempferboni
new file mode 100644
index 0000000..50e4be5
--- /dev/null
+++ b/doc/props/gildenprops/kaempferboni
@@ -0,0 +1,264 @@
+
+Kaempferboni und deren Implementation
+-------------------------------------
+-------------------------------------
+
+Bei den Kaempfern gibt es einige Properties, die, in Waffen oder Ruestungen
+gesetzt, der Kampfverlauf eines Spielers erheblich beeinflussen koennen.
+
+Zu beachten ist, dass die Abnahme von Waffen oder Ruestungen mit Kaempferboni
+allein der Balance obliegt. Der Gildenmagier der Kaempfer steht aber gerne
+mit Rat und Tat zur Seite.
+
+
+Abschnitt A
+-----------
+
+In Waffen koennen nachfolgende, in /p/kaempfer/kaempfer.h definierten,
+Properties gesetzt werden. Die meisten davon fungieren als 'Boni' und werden
+dem Spieler auch mittels 'schaetz <waffe>' angezeigt:
+
+
+1 Waffenschlagbonus - K_BRAWLING_WC (INT) - "k_brawling_wc"
+
+  Wenn die Waffe eine zusaetzlich gefaehrliche Stelle besitzt - z.B. einen
+  harten Dorn am Stielende, eine Spitze am Ruecken einer Axtklinge, Zacken
+  am Dolchgriff - kann man der Waffe einen Waffenschlagbonus geben.
+  Dies bedeutet, dass der Waffenschlag um ein paar Prozente verstaerkt wird,
+  da der Spieler natuerlich versucht, immer genau mit diesem 'feature'
+  den Waffenschlag auszufuehren (der Waffenschlag ist kurz gesagt ein
+  unerwarteter Schlag, der nicht mit dem 'normalen' Waffenende ausgefuehrt
+  wird, der Gegner wird dadurch ueberrascht -> mehr Schaden).
+  Da solch ein 'feature' doch recht auffaellig ist, sollte es in der
+  Langbeschreibung der Waffe auf jeden Fall erwaehnt werden.
+
+  Interessant zu wissen waere noch, dass Zweihandwaffen einen generellen
+  zusaetzlichen Bonus auf den Waffenschlag bekommen und dass es eine
+  Abstufung gibt, nach der die Waffengattungen die Hoehe des Basiswertes
+  gesetzt bekommen, wobei Speere den hoechsten und Messer den niedrigsten
+  besitzen:
+
+  Speere - Kampfstaebe - Aexte - Keulen - Schwerter - Messer
+
+  Der max. Bonus fuer diese Property betraegt 30, wobei 1-10 -> geringer
+  Bonus, 11-20 -> guter Bonus, 21-30 -> sehr guter Bonus.
+
+  Bitte beachten: ein Zweihand-Speer mit max. P_WC und max. K_BRAWLING_WC
+  haut entsprechend gut rein und sollte nur schwer zu ergattern sein, bzw.
+  noch andere Auflagen haben (ggf. unique, personalisiert, etc.)
+
+
+2 Waffenschlagschaden - K_BRAWLING_DT (STRING) - "k_brawling_dt"
+
+  Wenn die Waffe, mit der der Kaempfer einen Waffenschlag ausfuehrt, ein
+  'feature' hat, mit dem er diesen Schlag ausfuehrt, kann dieses 'feature'
+  einen anderen Waffenschlagschaden besitzen. Z.B. kann ein Schwert, welches
+  normalerweise DT_SLASH macht, besonders lange und spitze Parierstangen
+  besitzen, die vielleicht auch noch vergiftet sind. Dann kann der Magier
+  ({DT_PIERCE,DT_POISON}) setzen, so dass beim Waffenschlag immer ein
+  Mischschaden aus Stiche und Gift erfolgt.
+
+
+3 Waffenschlagsmeldung - K_BRAWLING_MSG (STRING/STRING*) - k_brawling_msg"
+
+  In diese Property kann man hineinschreiben, mit welchem Teil der Waffe
+  der Waffenschlag ausgefuehrt wird. Angenommen, es bietet sich an, mit
+  einer Waffe stets den Waffenschlag mit einem grossen Knauf am Griff
+  auszufuehren, wird schlicht und einfach "mit einem grossen Knauf am
+  Griff der Schlachtaxt" in die Property gesetzt.
+  Sollte sich bei der Programmierung ergeben, dass es sich anbietet, der
+  Waffe mehr als nur eine guenstige Stelle anzudichten mit der man den
+  Waffenschlag ausfuehren kann, so setzt man ein Array, z.B. ({"mit einem
+  grossen Knauf am Griff der Schlachtaxt","mit der breiten Seite der "
+  "Schlachtaxtklinge"}). Insgesamt ist darauf zu achten, dass die Meldungen
+  'vollstandig' sind. Das Array kann beliebige Groesse annehmen, es wird
+  dann zufaellig eine Meldung beim Schlag ausgesucht.
+
+  Es empfiehlt sich, jede Waffe mit dieser Property zu schmuecken, die
+  K_BRAWLING_WC gesetzt haben, da die Waffenschlagmeldungen damit im Kampf
+  'individualisiert' werden. In der Praxis wird es jedoch daran scheitern,
+  dass es viel zu viele alte Waffen gibt, die keiner mehr anfassen moechte.
+  Daher wird auf Standardmeldungen zurueckgegriffen, sollte diese Property
+  nicht gesetzt sein.
+
+
+4 Waffenbruchbonus - K_WEAPON_SHATTER (INT) - "k_weapon_shatter"
+
+  Waffen, die besonders fuer den Waffenbruch konstruiert wurden, koennen
+  einen Bonus einbringen, der in dieser Property angegeben wird. Natuerlich
+  eignen sich die verschiedenen Waffentypen wieder unterschiedlich gut fuer
+  einen Waffenbruch: Keulen (meist aufgrund ihres Gewichts) am besten, Messer
+  am schlechtesten, alle anderen dazwischen (Axt - Schwert - Stab - Speer).
+  Dabei kriegen alle Waffen, die u.a. Schlagschaden verursachen, nochmal
+  einen kleinen Bonus obendrauf.
+
+  Der max. Bonus fuer diese Property betraegt 50, wobei 1-10 -> geringer
+  Bonus, 11-30 -> guter Bonus, 31-50 -> sehr guter Bonus.
+
+  Bei gut gelungenem Waffenbruch wird die Waffe des Gegners beschaedigt, wenn
+  die Technik sehr gut gelingt, kann es auch sein, dass dem Gegner die Waffe
+  aus der Hand geschlagen wird (der Spieler kann sie allerdings nicht
+  aufheben und der NPC zueckt sie nach ein paar Kampfrunden wieder).
+
+
+5 Bonus fuer Finte/Waffentrick - K_DISTRACTING_WEAPON (INT) -
+  "k_distracting_weapon"
+
+  Waffen, die fuer den Gegner aufgrund ihrer Bauweise besonders irritierend
+  sein koennen, koennen einen Bonus fuer Finte und Waffentrick haben. Dabei
+  wird der Gegner bei einer Finte bzw. einem Waffentrick NOCH mehr verwirrt,
+  als er es ohnehin schon nur durch die angewandte Technik wird.
+  Ein gutes Beispiel hierfuer ist z.B. der Kriegshamster: ein Hamster, der
+  auf einem Holzstab aufgespiesst ist, sollte fuer den Gegner schon SEHR
+  irritierend sein ;).
+  Die Waffengattung hat nur wenig Einfluss auf Finte/Waffentrick.
+
+  Der max. Bonus fuer diese Property betraegt 50, wobei 1-10 -> geringer
+  Bonus, 11-30 -> guter Bonus, 31-50 -> sehr guter Bonus.
+
+
+6 Todesstossbonus - K_CRITICAL_HIT (INT) - "k_critical_hit"
+
+  Man stelle sich eine Waffe mit besonders spitzer, langer Klinge vor oder
+  eine magische Waffe, die dem geschwaechten Gegner die Seele entreisst.
+  Diese Eigenschaften verleihen dem Spieler beim Todesstoss einen
+  entsprechenden Bonus von bis zu 100%.
+
+  Es ist moeglich, dass ein und dasselbe 'feature' sowohl dem Waffenschlag
+  als auch dem Todesstoss den Bonus stellt, z.B. zwei Hiebklingen auf dem
+  Klingenruecken einer grossen Axt. Auch dies sollte man deutlich aus der
+  Langbeschreibung herauslesen koennen.
+
+  Der max. Bonus fuer diese Property betraegt 100, wobei 100 eine Verdopplung
+  der P_WC beim Todesstoss bedeutet!
+  Ansonsten bedeutet 1-20 -> geringer Bonus, 21-60 -> guter Bonus,
+  61-100 -> sehr guter Bonus.
+
+
+7 Waffenwurfbonus - K_THROWING_WEAPON (INT) - "k_throwing_weapon"
+
+  Wenn eine Waffe besonders gut zum Werfen geeignet ist, z.B. ein Wurfdolch,
+  dann kann diese Property gesetzt werden. Natuerlich ist der Grundwert wieder
+  von der Waffengattung abhaengig. Es gilt, dass man Messer und Speere
+  grundsaetzlich am besten werfen - und dabei gut Schaden machen - kann, am
+  schlechtesten schneiden Keulen und Kampfstaebe ab.
+
+  Der max. Bonus fuer diese Property betraegt 50, wobei 1-20 -> geringer
+  Bonus, 21-40 -> guter Bonus, 31-50 -> sehr guter Bonus.
+
+  Zu beachten ist hierbei, dass ein sehr hoher Bonus nur bei Waffen mit etwas
+  geringerer P_WC vergeben werden sollte. Ein reines Wurfmesser ist nunmal im
+  normalen Kampf nicht die gefaehrlichste aller Waffen (speziell
+  ausbalanciert, keinen richtigen Griff, etc.).
+  Natuerlich kann es einen Wurfspeer mit max. P_WC und sehr hohem
+  Waffenwurfbonus geben, allerdings mit den ueblich hohen Restriktionen.
+
+
+8 KO-Schlag-Bonus - K_KO (INT) - "k_ko"
+
+  Waffen, die besonders fuer einen KO-Schlag geeignet sind, koennen einen
+  Bonus mit dieser Property bekommen. Eine entsprechende Waffe koennte z.B.
+  ein lederumwickelter Pruegel sein, denn man will den Gegner ja nur KO
+  schlagen und nicht gleich toeten.
+
+  Der max. Bonus fuer diese Property betraegt 50, wobei 1-20 -> geringer
+  Bonus, 21-30 -> guter Bonus, 31-50 -> sehr guter Bonus.
+
+
+9 Kein Waffenschaerfen - K_NO_HONING (INT) - "k_no_honing"
+
+  Wenn eine Waffe aus irgendeinem Grund nicht geschaerft werden kann oder
+  darf, muss man diese Property auf 1 setzen.
+  Eine Erklaerung dafuer sollte in der P_LONG bzw. P_INFO erfolgen.
+
+
+Abschnitt B
+-----------
+
+Die beiden Properties, P_EFFECTIVE_AC und P_EFFECTIVE_WC, welche in
+<combat.h> definiert sind, sind eigentlich nur dazu da, um Ruestungen und
+Waffen, die eine DefendFunc() bzw. HitFunc() besitzen, korrekt vom Spieler
+einschaetzen lassen zu koennnen.
+
+Das Kaempferspellbook verwendet diese Properties darueberhinaus wie folgt:
+
+
+1 Schutzboni in Waffen - P_EFFECTIVE_AC (INT) - "effective_ac"
+
+  Ist diese Property in einer Waffe gesetzt, geht das Kaempferspellbook
+  davon aus, dass diese Waffe mehr oder weniger die Faehigkeit besitzt,
+  auch wie eine Ruestung schuetzen zu koennen. Da man eine Waffe aber nicht
+  anziehen, sondern nur vor sich hertragen kann, kann auch der max.
+  Ruestungsschutz einer Waffe nur gleich dem max. Ruestungsschutz eines
+  Schildes entsprechen.
+  Eine gesetzte P_EFFECTIVE_AC in einer Waffe wird dem Spieler als mehr
+  oder weniger gute 'Paradewaffe' im 'schaetz' angezeigt und geht sowohl bei
+  der Waffenparade als auch beim Block als Bonus mit ein.
+
+  Z.B. koennte ein leichtes Schwert, was aufgrund seiner Bauweise mehr fuer
+  den defensiven Kampf ausgelegt ist (extralange Parierstangen, verstaerkter
+  Handschutz im Griffbereich, ...) wie ein maessiges Schild wirken. Die
+  Vorteile liegen auf der Hand: der Spieler bekommt verstaerkten Schutz,
+  kann aber weiterhin eine Zweihandwaffe fuehren.
+
+  Der max. Bonus fuer diese Property betraegt 40, wobei 1-20 -> geringer
+  Bonus, 21-30 -> guter Bonus, 31-40 -> sehr guter Bonus.
+
+  Zu beachten ist hier, dass sehr gute Parierwaffen mit P_EFFECTIVE_AC > 30
+  moeglichst deutlich unter der max. WC liegen sollten.
+
+  Anmerkungen:
+  Eine gesetzte P_EFFECTIVE_AC in einem Schild kann den Bonus fuer die
+  Schildparade nach oben oder unten beeinflussen. Moechte man ein Schild
+  herstellen, welches speziell bei der Schildparade der Kaempfer besser
+  als 'normal' schuetzt, sollte man hier einen Wert eintragen, der deutlich
+  groesser als die P_AC des Schildes ist.
+
+  Eine gesetzte P_EFFECTIVE_AC in einer anderen Ruestung hat nur den Nutzen,
+  auf deren erhoehten (und nicht sofort sichtbaren) Verteidigungswert
+  hinzuweisen, der durch eine DefendFunc() realisiert wird.
+
+
+2 Angriffsboni in Ruestungen - P_EFFECTIVE_WC (INT) - "effective_wc"
+
+  Fuer die Kaempfer koennen folgende Ruestungstypen modifiziert werden:
+  AT_TROUSERS (Hosen), AT_HELMET (Kopfbedeckung), AT_BOOT (Fusskleidung),
+  AT_ARMOUR (Koerperruestung), AT_SHIELD (Schilde).
+  Ist in einer dieser Typen P_EFFECTIVE_WC gesetzt, so macht diese Ruestung
+  bei einem Angriff mit einer Spezialattacke (Kniestoss, Kopfstoss, Fusstritt,
+  Ellbogenschlag und Schildstoss) entsprechend mehr bzw. weniger Schaden als
+  ohne diese Property. Eine entsprechende Begruendung fuer eine Verstaerkung
+  oder Schwaechung sollte auch hier fuer den Spieler offensichtlich sein
+  (Dornen am Schild, verstaerkter Kniebereich, Zacken am Helm, etc.).
+
+  Wenn man der Ruestung einen Bonus geben moechte, muss man darauf achten,
+  dass P_EFFECTIVE_WC hoeher ist als die P_AC der Ruestung! Sollte
+  P_EFFECTIVE_WC niedriger als P_AC sein, wird dennoch P_EFFECTIVE_WC als
+  Angriffswert genommen. Dies stellt natuerlich eine Schwaechung der
+  Spezialattacke dar. Moeglicherweise ist aber genau das gewollt, wenn eine
+  Ruestung, die sehr gut schuetzt, nur geringen Kaempferbonus aufweisen soll.
+
+  Beispiel: ein Schild aus Hartgummi kann recht gut Schlaege aller Art
+  abfangen (-> P_AC 35). Will der Kaempfer jedoch einen Schildstoss damit
+  machen, fehlt ihm aufgrund der Beschaffenheit die Wucht, eher daempft es
+  den Schildstoss noch ein wenig (-> P_EFFECTIVE_WC 25).
+
+  Der Maximalwert fuer die P_EFFECTIVE_WC bei Kaempfern ist der jeweils
+  doppelte maximale P_AC-Wert (s. 'man ruestungen').
+
+  Die Angabe eines Schadenstyps (P_DAM_TYPE) in einer Ruestung kann dann
+  sinnvoll sein, wenn bei der Spezialattacke ein spezieller Schaden gemacht
+  werden soll. Beispielsweise sollten Flammenstiefel logischerweise DT_FIRE
+  und DT_BLUDGEON oder DT_PIERCE bei einem Kampftritt verursachen. Es MUSS
+  (logischerweise) mindestens ein physikalischer Schadenstyp enthalten sein.
+  Wird kein Schadenstyp angegeben, wird auf Standardtypen zurueckgegriffen.
+
+
+SIEHE AUCH:
+     Waffen:     P_WC, P_TOTAL_WC, P_EFFECTIVE_WC, HitFunc()
+     Ruestungen: P_AC, P_TOTAL_AC, P_EFFECTIVE_AC, DefendFunc()
+     Files:      /std/weapon.c, /std/weapon/combat.c
+     Balance:    waffen, ruestungen, properties
+
+-----------------------------------------------------------------------------
+26.10.2012, Gabylon
diff --git a/doc/props/obsolete/P_BALANCED_WEAPON b/doc/props/obsolete/P_BALANCED_WEAPON
new file mode 100644
index 0000000..e785f2c
--- /dev/null
+++ b/doc/props/obsolete/P_BALANCED_WEAPON
@@ -0,0 +1,35 @@
+********************* UNGENUTZTE PROPERTY *****************************
+* Diese Property wird von der Mudlib NICHT ausgewertet und kann       *
+* als veraltet gelten.                                                *
+* Momentan ist auch keine Gilde bekannt, die mehr macht, als sie      *
+* auszugeben.                                                         *
+***********************************************************************
+NAME:
+        P_BALANCED_WEAPON  "balanced_weapon"
+
+DEFINIERT IN:
+        /sys/weapon.h
+
+BESCHREIBUNG:
+        Die Property gibt an, ob eine Waffe ausbalanciert ist oder nicht.
+        Die beiden moeglichen Werte sind logischerweise:
+
+                WP_BALANCED       balanciert
+                WP_UNBALANCED     unbalanciert
+
+        Die WP_* sind ebenfalls in <weapon.h> definiert.
+
+BEISPIELE:
+        a) Eine ausbalancierte Waffe ist z.B. ein Kampfstab.
+
+            SetProp(P_BALANCED_WEAPON,WP_BALANCED);
+
+        b) Eine nicht ausbalancierte Waffe ist z.B. eine Keule.
+
+            SetProp(P_BALANCED_WEAPON,WP_UNBALANCED);
+
+SIEHE AUCH:
+        P_TECHNIQUE, /std/weapon/combat.c
+
+LETZTE AeNDERUNG:
+15.02.2009, Zesstra
diff --git a/doc/props/obsolete/P_DEFAULT_INFO b/doc/props/obsolete/P_DEFAULT_INFO
new file mode 100644
index 0000000..814928c
--- /dev/null
+++ b/doc/props/obsolete/P_DEFAULT_INFO
@@ -0,0 +1,28 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet. Bitte nicht mehr in neuem Code nutzen. *
+***********************************************************************
+
+NAME:
+    P_DEFAULT_INFO                "default_info"
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Default-Antwort eines Npc, wenn er auf das Schluesselwort durch den
+    Spieler kein AddInfo parat hat.
+
+BEMERKUNG:
+    Diese Property sollte nicht mehr benutzt werden. Stattdessen bitte
+    AddInfo(DEFAULT_INFO,...) verwenden.
+    Dem in dieser Prop angegeben String wird immer der Name des Npc
+    vorausgestellt. Will man dies verhindern, muss man sie ueberschreiben.
+
+BEISPIEL:
+    SetProp(P_DEFAULT_INFO,"bohrt gelangweilt in der Nase.\n");
+
+SIEHE AUCH:
+    AddInfo
+
+----------------------------------------------------------------------------
+17.08.2010, Zesstra
diff --git a/doc/props/obsolete/P_EXTRA_LOOK b/doc/props/obsolete/P_EXTRA_LOOK
new file mode 100644
index 0000000..3777a25
--- /dev/null
+++ b/doc/props/obsolete/P_EXTRA_LOOK
@@ -0,0 +1,35 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet. Bitte benutzt sie NICHT mehr, sondern  *
+* stattdessden AddExtraLook().                                        *
+***********************************************************************
+
+NAME:
+	P_EXTRA_LOOK			"extralook"
+
+DEFINIERT IN:
+	/sys/living/description.h
+
+BESCHREIBUNG:
+	Diese Property enthaelt einen String. Sie wird entweder in Lebewesen
+	direkt oder in Objekten gesetzt wird, die im Besitz von Lebewesen
+	sein koennen.
+	Diese Strings erscheinen dann zusaetzlich in der Langbeschreibung
+	des Lebewesens bzw. des Besitzers (wenn das Objekt sich direkt im
+	 Lebewesen befindet, jedoch nicht in einem Behaelter im Lebewesen).
+	Fuer den Zeilenumbruch muss man selbst sorgen.
+
+BEISPIEL:
+	Ein Spieler hat eine Pfeife im Mund. In dieser Pfeife setzt man z.B.
+	in der Funktion zum Pfeife Rauchen folgendes:
+	  SetProp(P_EXTRA_LOOK,break_string(
+	 this_player()->Name(WER)+" ist ganz umnebelt.",78);
+
+BEMERKUNG:
+  BITTE NICHT MEHR BENUTZEN!
+
+SIEHE AUCH:
+	long(), /std/living/description.c, /std/player/base.c
+  AddExtraLook(), RemoveExtraLook()
+
+----------------------------------------------------------------------------
+13.05.2007, Zesstra
diff --git a/doc/props/obsolete/P_LAST_KILLER b/doc/props/obsolete/P_LAST_KILLER
new file mode 100644
index 0000000..27eea8f
--- /dev/null
+++ b/doc/props/obsolete/P_LAST_KILLER
@@ -0,0 +1,20 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet und wird bald aus der Mudlib entfernt.  *
+***********************************************************************
+
+NAME:
+    P_LAST_KILLER                 "last_killer"                 
+
+DEFINIERT IN:
+    /sys/player.h
+
+BESCHREIBUNG:
+     Letzter Moerdes des Wesens.
+     Diese Property wurde nur in Spielern gesetzt und auch dann nicht immer.
+     Bitte stattdessen P_KILLER abfragen, welche in NPC und Spielern gesetzt
+     wird.
+
+SIEHE AUCH:
+    P_KILLER, die()
+
+05.09.2008, Zesstra
diff --git a/doc/props/obsolete/P_LAST_PEACE_TIME b/doc/props/obsolete/P_LAST_PEACE_TIME
new file mode 100644
index 0000000..762233e
--- /dev/null
+++ b/doc/props/obsolete/P_LAST_PEACE_TIME
@@ -0,0 +1,27 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet und wird bald aus der Mudlib entfernt.  *
+***********************************************************************
+PROPERTY
+
+	P_LAST_PEACE_TIME	"last_peace_time"
+
+DEFINIERT IN
+
+	/std/living/combat.c
+
+BESCHREIBUNG
+
+	Diese Property gibt an, wann zuletzt versucht wurde, einen NPC zu
+	befrieden. Bitte nach Moeglichkeit nur lesend verwenden. Des weiteren
+	wird sie nur im ueblichen Verhalten von QueryPacify gesetzt, und nur
+	dann, wenn P_ACCEPT_PEACE nicht gesetzt ist.
+
+SIEHE AUCH
+
+	QueryPacify, P_ACCEPT_PEACE
+
+LETZTE AENDERUNG
+
+	2004-03-17, 14:30 von Humni
+	
+
diff --git a/doc/props/obsolete/P_LOG_FILE b/doc/props/obsolete/P_LOG_FILE
new file mode 100644
index 0000000..1eafe40
--- /dev/null
+++ b/doc/props/obsolete/P_LOG_FILE
@@ -0,0 +1,45 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property wird nicht mehr ausgewertet.                         *
+***********************************************************************
+NAME:
+    P_LOG_FILE                   "log_file"
+
+DEFINIERT IN:
+    /sys/thing/properties.h
+
+BESCHREIBUNG:
+    Enthaelt einen String auf einen Filenamen. 
+
+    Werden zu dem Objekt (Raum, Monster, ...) Fehlermeldungen abgesetzt, 
+    werden diese in das angegebene File umgeleitet. Die Eintragung in
+    die per Default fuer Fehlermeldungen vorgesehenen Dateien erfolgt
+    nicht.
+
+BEMERKUNGEN:
+    P_LOG_FILE wird ausgewertet wie log_file().
+
+    Das heisst, es wird AUF JEDEN FALL nach /log geschrieben!
+
+    Direkt in /log kann NICHT geschrieben werden, es muss also ein 
+    Unterverzeichnis mit Eurem Magiernamen vorhanden sein.
+
+BEISPIELE:
+    SetProp(P_LOG_FILE,"tilly_log");          // FALSCH, es wuerde versucht, 
+                                                 das File /log/tilly_log 
+                                                 anzulegen
+    SetProp(P_LOG_FILE,"/log/tilly_log");     // FALSCH, es wuerde versucht, 
+                                                 das File /log/log/tilly_log
+                                                 anzulegen
+    SetProp(P_LOG_FILE,"/d/ebene/tilly_log"); // FALSCH, s.o.
+
+    SetProp(P_LOG_FILE,"tilly/tilly_log");    // RICHTIG!
+
+    Im letzten Beispiel werden alle Eintraege in das File tilly_log ge-
+    schrieben, das sich im Verzeichnis /log/tilly/ befindet.
+
+    Das Unterverzeichnis /tilly in /log muss zuvor angelegt werden.
+
+SIEHE AUCH:
+    P_LOG_INFO, write_file(), log_file(),
+
+Letzte Aenderung: 13.09.04 Tilly@MorgenGrauen
diff --git a/doc/props/obsolete/P_NEXT_SPELL_TIME b/doc/props/obsolete/P_NEXT_SPELL_TIME
new file mode 100644
index 0000000..4742456
--- /dev/null
+++ b/doc/props/obsolete/P_NEXT_SPELL_TIME
@@ -0,0 +1,30 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet. Bitte nicht mehr in neuem Code nutzen. *
+***********************************************************************
+
+NAME:
+    P_NEXT_SPELL_TIME             "next_spell"
+
+DEFINIERT IN:
+    /sys/new_skills.h
+
+BESCHREIBUNG:
+    Wann kann das naechste Mal gezaubert werden?
+    Dies ist eine globale Spruchermuedung/-Sperre.
+    Flexiblere Sperren koennen mittels SetSpellFatigue/CheckSpellFatigue()
+    verwaltet werden.
+
+    Diese Property ist keine echte Property, sondern liefert nur das
+    Ergebnis von von CheckSpellFatigue() zurueck bzw. ruft beim Setzen
+    SetSpellFatigue(<spruchsperre>, 0) auf.
+    Diese Prop sollte _nicht_ mittels Query- oder Setmethoden manupuliert
+    werden, da sonst Inkonsistenzen zum Ergebnis von CheckSpellFatigue()
+    auftreten.
+
+SIEHE AUCH:
+    SetSpellFatigue(L), CheckSpellFatigue(L)
+    spruchermuedung
+
+ZULETZT GEAeNDERT:
+14.03.2010, Zesstra
+
diff --git a/doc/props/obsolete/P_READ_MSG b/doc/props/obsolete/P_READ_MSG
new file mode 100644
index 0000000..5c79fdc
--- /dev/null
+++ b/doc/props/obsolete/P_READ_MSG
@@ -0,0 +1,32 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet. Bitte nicht mehr in neuem Code nutzen. *
+***********************************************************************
+NAME:
+    P_READ_MSG                "read_msg"                
+
+DEFINIERT IN:
+    /sys/properties.h
+
+BESCHREIBUNG:
+    Diese Property ist veraltet. Ihre Funktion wird von
+    AddReadDetail(SENSE_DEFAULT, ...) uebernommen.
+    
+    Hier koennen Informationen gespeichert werden, die beim Lesen
+    des Objektes ausgegeben werden.
+     
+    Fuer das Identifizieren des zu lesenden Objektes wird der gleiche
+    Mechanismus benutzt wie fuer lesbare und andere Details.
+
+    Die Benutzung von process_string() ist in dieser Prop nicht mehr erlaubt.
+
+BEISPIEL:
+    AddId(({"rolle", "schriftrolle"}));
+    SetProp(P_READ_MSG,
+       "Du oeffnest die Rolle und liest: LOREM IPSUM ...\n");
+     
+SIEHE AUCH:
+    Details:   AddReadDetail(), RemoveReadDetail(), P_READ_DETAILS
+    Sonstiges: break_string()
+
+09.12.2012, Zesstra
+
diff --git a/doc/props/obsolete/P_TECHNIQUE b/doc/props/obsolete/P_TECHNIQUE
new file mode 100644
index 0000000..1657027
--- /dev/null
+++ b/doc/props/obsolete/P_TECHNIQUE
@@ -0,0 +1,44 @@
+********************* UNGENUTZTE PROPERTY *****************************
+* Diese Property wird von der Mudlib NICHT ausgewertet und kann       *
+* als veraltet gelten.                                                *
+* Momentan ist auch keine Gilde bekannt, die mehr macht, als sie      *
+* auszugeben.                                                         *
+***********************************************************************
+NAME:
+	P_TECHNIQUE				"technique"
+
+DEFINIERT IN:
+	/sys/weapon.h
+
+BESCHREIBUNG:
+        Die Technik(en), mit denen eine Waffe im Kampf eingesetzt werden
+        kann. Folgende Techniken stehen zur Verfuegung:
+
+                TQ_STROKE       Streichtechnik
+                TQ_THRASH       Schlagtechnik
+                TQ_THRUST       Stosstechnik
+                TQ_WHIP         Peitschtechnik
+
+        Die Techniken sind ebenfalls in <weapon.h> definiert und auf der
+        man-page 'techniken' naeher erlaeutert.
+
+BEMERKUNGEN:
+        Man kann einer Waffe eine oder mehrere Techniken zuweisen.
+
+BEISPIELE:
+        a) Eine Waffe, die nur mit einer Peitschtechnik eingesetzt wird,
+           also typischerweise eine Peitsche, aber auch ein Morgenstern:
+
+            SetProp(P_TECHNIQUE,TQ_WHIP);
+
+        b) Eine Waffe, die sowohl mit der Schlag- als auch der Stosstechnik
+           eingesetzt wird, also z.B. eine Hellebarde:
+
+            SetProp(P_TECHNIQUE,({TQ_THRASH,TQ_THRUST}));
+
+SIEHE AUCH:
+        techniken, P_BALANCED_WEAPON, /std/weapon/combat.c
+
+LETZTE AeNDERUNG:
+15.02.2009, Zesstra
+
diff --git a/doc/props/obsolete/P_TMP_ATTACK_HOOK b/doc/props/obsolete/P_TMP_ATTACK_HOOK
new file mode 100644
index 0000000..bdc8152
--- /dev/null
+++ b/doc/props/obsolete/P_TMP_ATTACK_HOOK
@@ -0,0 +1,73 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet. Bitte nicht mehr in neuem Code nutzen. *
+***********************************************************************
+P_TMP_ATTACK_HOOK
+
+NAME:
+    P_TMP_ATTACK_HOOK             "attack_hook"
+
+DEFINIERT IN:
+    /sys/new_skills.h
+
+BESCHREIBUNG:
+     Mittels dieser Property koennen die Attacken eines Livings ggf.
+     abgebrochen werden noch bevor Waffen oder Skills zum ausgewertet
+     wurden.
+
+     Es wird an dem Living die Property als mindestens 3-elementiges Array:
+     ({zeitpunkt, objekt, methode, ...})
+     gesetzt und die Methode 'methode' wird dann waehrend des Attack() des
+     Lebewesens in 'objekt' aufgerufen, solange time()<'zeitpunkt'.
+
+     Der Methode wird als Parameter der Gegner uebergeben.
+
+     Gibt die Methode 0 als Rueckgabewert zurueck, wird die Attacke sofort
+     kommentarlos abgebrochen.
+
+BEMERKUNGEN:
+     - Bitte das neuere Hooksystem (s. Manpage std/hooks) benutzen.
+     - falls die Zeit abgelaufen oder das Objekt zerstoert ist, wird die
+       Property auf 0 gesetzt
+     - vor dem Setzen immer auf die Existenz eines gueltigen Hooks
+       pruefen, einfaches Ueberschreiben fuehrt einerseits zu Fehlern
+       im Code anderer und ist andererseits unfair gegenueber ihnen
+
+BEISPIELE:
+     *** der Spieler erhaelt eine Verwundung, die ihn manchmal behindert ***
+     if(!pointerp(tmp=TP->QueryProp(P_TMP_ATTACK_HOOK)) ||
+        sizeof(tmp)<3 || tmp[0]<=time()) {
+       TP->SetProp(P_TMP_ATTACK_HOOK,
+		   ({time()+3600, this_object(), "test_hurt"}));
+     ...
+
+     // die entsprechende Methode, die bei jedem Angriff ueber Attack()
+     // gerufen wird ...
+     int test_hurt(object enemy) {
+
+       // mit 10% Chance generell und 20% Chance bei groesseren Gegnern
+       // bricht der Angriff ab ... previous_object() ist natuerlich
+       // der Angreifer
+       if(!random(10) ||
+          (enemy->QueryProp(P_SIZE)>previous_object()->QueryProp(P_SIZE) &&
+           !random(5)) {
+
+          tell_object(previous_object(),
+            "Deine Wunde schmerzt dich zu sehr um anzugreifen.\n");
+          tell_room(environment(previous_object()),
+            previous_object()->Name(WER,1)+" zuckt vor Schmerzen zusammen.\n",
+            ({previous_object()}));
+          return 0;
+       }
+
+       // ansonsten geht der Angriff weiter
+       return 1;
+     }
+
+SIEHE AUCH:
+     Angriff:	Attack(L)
+     Schutz:    Defend(L)
+     Verwandt:  InternalModifyAttack(L), P_TMP_ATTACK_MOD	
+     Hooks:	P_TMP_DIE_HOOK, P_TMP_MOVE_HOOK, P_TMP_DEFEND_HOOK
+     neue Hooks: std/hooks
+
+08.12.2008, Zesstra
diff --git a/doc/props/obsolete/P_TMP_ATTACK_MOD b/doc/props/obsolete/P_TMP_ATTACK_MOD
new file mode 100644
index 0000000..3491c0c
--- /dev/null
+++ b/doc/props/obsolete/P_TMP_ATTACK_MOD
@@ -0,0 +1,112 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet. Bitte nicht mehr in neuem Code nutzen. *
+***********************************************************************
+P_TMP_ATTACK_MOD
+
+NAME:
+     P_TMP_ATTACK_MOD              "attack_mod"
+
+DEFINIERT IN:
+     /sys/new_skills.h
+
+BESCHREIBUNG:
+     Mittels dieser Property koennen die Attacken eines Livings temporaer
+     veraendert werden.
+
+     Es wird an dem Living die Property als mindestens 3-elementiges Array
+     ({zeitpunkt, objekt, methode, ...})
+     gesetzt und die Methode 'methode' wird dann waehrend des Attack() des
+     Lebewesens in 'objekt' aufgerufen, solange time()<'zeitpunkt'.
+
+     Der Methode wird beim Aufruf ein Kopie des Mappings uebergeben, in dem
+     die bisherigen Werte des Angriffes vermerkt sind.
+     Aufbau von Mapping 'ainfo':
+     ([ SI_ENEMY :           object Angreifer,			(-> Defend)
+        SI_SPELL :           0/1/array Spellparameter,		(-> Defend)
+        P_WEAPON :           - oder Waffe,
+        SI_SKILLDAMAGE_MSG:  string Angriffsmeldungsende an Raum,
+        SI_SKILLDAMAGE_MSG2: string Angriffsmeldungsende an Kaempfende,
+        SI_SKILLDAMAGE:      int Schaden in Zehntel HP (Skills bis auf Rasse
+			     drin!)				(-> Defend),
+        SI_SKILLDAMAGE_TYPE: string/string* Schadenstypen,	(-> Defend)
+        P_WEAPON_TYPE:       string Waffentyp (combat.h),
+        P_WC:		     - oder int WC der Waffe/Hand,
+        P_NR_HANDS:	     - oder int Hands der Waffe/Hand,
+     ]);
+
+     Gibt die Methode:
+     - 0 oder kein Mapping zurueck, fuehrt das zum Abbruch der Attacke
+       -> da inzwischen Waffen abgefragt wurden, koennen schon Meldungen
+          von diesen ausgegeben worden sein
+     - ein leeres Mapping ( ([]) ) zurueck, fuehrt das zu keiner Modifikation
+     - ein Mapping mit veraenderten Werten zurueck, werden diese in das
+       Angriffsmapping kopiert
+       Die geaenderten Werte werden teilweise von SkillResTransfer() in
+       den eigentlichen Angriff uebernommen.
+
+BEMERKUNGEN:
+     - falls die Zeit abgelaufen oder das Objekt zerstoert ist, wird die
+       Property auf 0 gesetzt
+     - vor dem Setzen immer auf die Existenz eines gueltigen Modifiers
+       pruefen, einfaches Ueberschreiben fuehrt einerseits zu Fehlern
+       im Code anderer und ist andererseits unfair gegenueber ihnen
+
+BEISPIELE:
+     *** ein besonder heisser Segen modifiziert die Attacken des Spielers ***
+     int action_segen() {
+       ...
+       mixed *tmp;
+
+       // pruefen, ob nicht ein anderer Modifier existiert
+       if(!pointerp(tmp=TP->QueryProp(P_TMP_ATTACK_MOD)) ||
+          sizeof(tmp)<3 || tmp[0]<=time()) {
+
+         object wield;
+         wield=TP->QueryProp(P_WEAPON);
+
+         write(break_string(
+           "Der Priester der Flamme weiht "+
+           (wield?wield->name(WEN,1):"deine Haende")+".", 78));
+
+         // setzen des Modifiers .. 30-40 Sekunden gueltig
+         TP->SetProp(P_TMP_ATTACK_MOD,
+	              ({time()+30+random(10), this_object(), "modfun"}));
+        } else ...
+        ...
+      return 1;
+     }
+
+     // die eigentlich Methode, die waehrend des Angriffs gerufen wird
+     mapping modfun(mapping ainfo) {
+       mapping ret;
+
+       // Returnwert ist immer ein Mapping, damit die Attacke weitergeht
+       ret=m_allocate(0,1);
+
+       // magische Waffen oder Sprueche werden nicht verbessert
+       if(ainfo[P_WEAPON_TYPE]!=WT_MAGIC) {
+         // sonst Verbesserungen ... Feuer addieren ...
+         ret[SI_SKILLDAMAGE_TYPE]=(ainfo[SI_SKILLDAMAGE_TYPE]||({}))+
+				({DT_FIRE});
+	 // ... und bei Waffe Meldungen anpassen
+         if(ainfo[P_WEAPON]) {
+           ret[SI_SKILLDAMAGE_MSG]=
+             " mit sengendem "+ainfo[P_WEAPON]->name(RAW);
+           ret[SI_SKILLDAMAGE_MSG2]=
+             " mit sengendem "+ainfo[P_WEAPON]->name(RAW);
+         }
+       }
+
+       return ret;
+     }
+
+SIEHE AUCH:
+     Angriff:	Attack(L)
+     Schutz:    Defend(L)
+     Verwandt:  InternalModifyAttack(L)
+		P_TMP_ATTACK_HOOK
+		P_TMP_DEFEND_HOOK
+     Sonstiges: SkillResTransfer(L)
+     Hooks:	P_TMP_DIE_HOOK, P_TMP_MOVE_HOOK
+
+10.Feb 2005 Gloinson
diff --git a/doc/props/obsolete/P_TMP_DEFEND_HOOK b/doc/props/obsolete/P_TMP_DEFEND_HOOK
new file mode 100644
index 0000000..986e63a
--- /dev/null
+++ b/doc/props/obsolete/P_TMP_DEFEND_HOOK
@@ -0,0 +1,110 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet. Bitte nicht mehr in neuem Code nutzen. *
+***********************************************************************
+P_TMP_DEFEND_HOOK
+
+NAME:
+     P_TMP_DEFEND_HOOK             "defend_hook"
+
+DEFINIERT IN:
+     /sys/new_skills.h
+
+BESCHREIBUNG:
+     Mittels dieser Property koennen die Abwehren eines Livings temporaer
+     veraendert werden.
+
+     Es wird an dem Living die Property als mindestens 3-elementiges Array
+     ({zeitpunkt, objekt, methode, ...})
+     gesetzt und die Methode 'methode' wird dann waehrend des Defend() des
+     Lebewesens in 'objekt' aufgerufen, solange time()<'zeitpunkt'.
+
+     Der Methode werden die Parameter der Defend() uebergeben:
+     int dam, mixed dam_type, mixed spell, object enemy
+     - spell ist definitiv ein Mapping - ein an Defend() uebergebener
+       int-Wert wurde aequivalent umgesetzt.
+     - dam_type ist definitiv ein Array von Schadenswerten oder einem Wert
+
+     Zudem findet der Aufruf der Methode nach dem Abarbeiten der P_DEFENDERS
+     statt, diese koennen also weitere Modifikationen vorgenommen haben.
+
+     Gibt die Funktion:
+     - 0 zurueck, wird Defend() abgebrochen (und damit der Schaden voellig
+       abgefangen) - nur noch die Flucht wird geprueft
+     - ein 3-elementiges Array ({schaden, schadenstypen, spell}) zurueck,
+       werden diese Werte in der Defend() an Stelle der uebergebenen Werte
+       verwendet
+     - weder noch zurueck, wird das Ergebnis ignoriert und die Defend laeuft
+       regulaer weiter
+
+BEMERKUNGEN:
+     - Bitte das neuere Hooksystem (s. Manpage std/hooks) benutzen.
+     - falls die Zeit abgelaufen oder das Objekt zerstoert ist, wird die
+       Property auf 0 gesetzt
+     - vor dem Setzen immer auf die Existenz eines gueltigen Hooks
+       pruefen, einfaches Ueberschreiben fuehrt einerseits zu Fehlern
+       im Code anderer und ist andererseits unfair gegenueber ihnen
+
+BEISPIEL:
+     *** ein gruener Schutzzauber ***
+     // Setzen der Prop
+     ...
+     tmp=TP->QueryProp(P_TMP_DEFEND_HOOK);
+
+     // ein etwas ausfuehrlicher Check, ob wir ueberschreiben koennen,
+     // Existenz && Gueltigkeit
+     if(pointerp(tmp) && sizeof(tmp) && intp(tmp[0]) && (time()<tmp[0]))
+      write("Der Zauber klappt nicht!\n");
+     else {
+      // der Zauber gilt eine Stunde
+      TP->SetProp(P_TMP_DEFEND_HOOK,({time()+3600,TO,"abwehrfun");
+      write("Ein Zauber legt sich auf dich.\n");
+     }
+     ...
+
+     // die gerufene Methode
+     mixed abwehrfun(int dam, string* dam_type, mapping spell, object en) {
+      // keine rekursiven Schaeden abfangen ... mindestens ein magischer
+      // muss auch dabei sein
+      if((!mappingp(spell) || !spell[SP_RECURSIVE]) &&
+         sizeof(filter(dam_type,MAGICAL_DAMAGE_TYPES))) {
+
+       // mit 10% Whkeit schuetzt der Zauber total
+       if(!random(10)) {
+        tell_object(previous_object(),
+          "Dein Zauber gleisst rund um dich gruen auf.\n");
+        tell_room(environment(previous_object()), break_string(
+          previous_object()->Name(WESSEN)+" Haut gleisst rund um "+
+          previous_object()->QueryPronoun(WEN)+" gruen auf.",78),
+          ({previous_object()}));
+
+        // manchmal geht der Zauber dabei endgueltig kaputt
+        if(!random(10)) previous_object()->SetProp(P_TMP_DEFEND_HOOK, 0);
+
+        return 0;			// volles Abfangen des Angriffs
+       }
+
+       // der Zauber schuetzt auf jeden Fall immer ein bischen
+       tell_object(previous_object(),
+         "Dein Zauber flackert rund um dich gruen auf.\n");
+       tell_room(environment(previous_object()), break_string(
+         previous_object()->Name(WESSEN)+" Haut flackert rund um "+
+         previous_object()->QueryPronoun(WEN)+" gruen auf.",78),
+         ({previous_object()}));
+       dam=(7+random(2))*dam/10;	// Schaden reduzieren
+
+       return(({dam, dam_type, spell}));
+      }
+
+      // der Zauber schuetzt dann gar nicht ...
+      return 1;
+     }
+
+SIEHE AUCH:
+     Angriff:	Attack(L)
+     Schutz:    Defend(L)
+     Verwandt:	InternalModifyDefend(L), P_TMP_ATTACK_MOD
+     Hooks:	P_TMP_DIE_HOOK, P_TMP_MOVE_HOOK, P_TMP_ATTACK_HOOK
+     neue Hooks: std/hooks
+
+08.12.2008, Zesstra
+
diff --git a/doc/props/obsolete/P_TMP_DIE_HOOK b/doc/props/obsolete/P_TMP_DIE_HOOK
new file mode 100644
index 0000000..cd18917
--- /dev/null
+++ b/doc/props/obsolete/P_TMP_DIE_HOOK
@@ -0,0 +1,70 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet. Bitte nicht mehr in neuem Code nutzen. *
+***********************************************************************
+P_TMP_DIE_HOOK
+
+NAME:
+    P_TMP_DIE_HOOK                "die_hook"
+
+DEFINIERT IN:
+    /sys/new_skills.h
+
+BESCHREIBUNG:
+     Mittels dieser Property kann der Tod eines Livings abgewendet werden.
+
+     Es wird an dem Living die Property als mindestens 3-elementiges Array
+     ({zeitpunkt, objekt, methode, ...})
+     gesetzt und die Methode 'methode' wird dann waehrend des die() des
+     Lebewesens in 'objekt' aufgerufen, solange time()<'zeitpunkt'.
+     Bei Geistern wird der Hook nicht gerufen.
+
+     Der Methode wird ein int uebergeben, ob das Living Opfer von Gift
+     (P_POISON) war.
+
+     Gibt die Funktion einen Wert != 0 zurueck, wird die() sofort abgebrochen
+     und das Living stirbt nicht.
+
+BEMERKUNGEN:
+    - Bitte das neuere Hooksystem (s. Manpage std/hooks) benutzen.
+    - falls die Zeit abgelaufen oder das Objekt zerstoert ist, wird die
+      Property auf 0 gesetzt
+    - vor dem Setzen immer auf die Existenz eines gueltigen Hooks
+      pruefen, einfaches Ueberschreiben fuehrt einerseits zu Fehlern
+      im Code anderer und ist andererseits unfair gegenueber ihnen
+
+BEISPIELE:
+     *** ein besonderer Giftschutz .. wirkt beim Tod ***
+     // pruefen, ob nicht ein anderer Modifier existiert
+     if(!pointerp(tmp=TP->QueryProp(P_TMP_DIE_HOOK)) ||
+        sizeof(tmp)<3 || tmp[0]<=time()) {
+       TP->SetProp(P_TMP_DIE_HOOK,
+	           ({time()+120+random(10), this_object(), "prevent_die"}));
+
+     // die gerufene Methode
+     int prevent_die(int poison) {
+       int ret;
+
+       if(poison) {
+         tell_object(previous_object(),
+           "Ein Zauber reinigt im Moment des Todes dein Blut!\n");
+         tell_object(environment(previous_object()),
+           previous_object()->Name(WER,1)+" wird von Lichtern umhuellt.\n",
+           ({previous_object()}));
+
+         ret=1;
+       }
+
+       // wir helfen nur einmal ... und ein Tod vernichtet die Hilfe auch
+       previous_object()->SetProp(P_TMP_DIE_HOOK, 0);
+
+       return ret;
+     }
+
+SIEHE AUCH:
+     Tod:	die(L)
+     Sonstiges: P_POISON, P_KILLS, P_GHOST
+     Hooks:	P_TMP_MOVE_HOOK, P_TMP_ATTACK_HOOK, P_TMP_DEFEND_HOOK
+     neue Hooks: std/hooks
+
+08.12.2008, Zesstra
+
diff --git a/doc/props/obsolete/P_TMP_MOVE_HOOK b/doc/props/obsolete/P_TMP_MOVE_HOOK
new file mode 100644
index 0000000..c28a067
--- /dev/null
+++ b/doc/props/obsolete/P_TMP_MOVE_HOOK
@@ -0,0 +1,42 @@
+********************* VERALTETE PROPERTY ******************************
+* Diese Property ist veraltet. Bitte nicht mehr in neuem Code nutzen. *
+***********************************************************************
+NAME:
+    P_TMP_MOVE_HOOK             "move_hook"
+
+DEFINIERT IN:
+    /sys/new_skills.h
+
+BESCHREIBUNG:
+    Mindestens 3-elementiges Array ({zeitpunkt, objekt, funktion, ...}).
+    Die Funktion wird im 'objekt' mit den gleichen Parametern wie move()
+    nach der Abfrage auf P_NO_TPORT aufgerufen, wenn der 'zeitpunkt'
+    noch nicht ueberschritten ist. Wenn die Funktion etwas anderes als ein
+    5-elementiges Array ({dest, methods, direction, textout, textin})
+    oder -1 zurueckgibt, wird move() normal ausgefuehrt, ansonsten werden die
+    5 move-Parameter durch die Array-Eintraege ersetzt bzw. wird bei einem
+    Rueckgabewert von -1 das move() abgebrochen. In letzterem Fall ist
+    die Funktion dafuer verantwortlich, eine entspr. Meldung an den
+    Spieler auszugeben!
+
+HINWEIS:
+    Falls man einem Spieler einen Move-Hook setzt, ist es ratsam, im
+    Move-Hook zu pruefen, ob das Spielerobjekt nach Abarbeitung der Hook-
+    Funktion noch lebt. Ansonsten wird ein doppeltes move() ausgefuehrt:
+    in den Todesraum und direkt wieder zurueck zur Leiche.
+
+BEMERKUNGEN:
+     - Bitte das neuere Hooksystem (s. Manpage std/hooks) benutzen.
+     - falls die Zeit abgelaufen oder das Objekt zerstoert ist, wird die
+       Property auf 0 gesetzt
+     - vor dem Setzen immer auf die Existenz eines gueltigen Hooks
+       pruefen, einfaches Ueberschreiben fuehrt einerseits zu Fehlern
+       im Code anderer und ist andererseits unfair gegenueber ihnen
+
+SIEHE AUCH:
+     Bewegung:	move(L), NotifyMove(), PreventMove()
+     Hooks:	P_TMP_DIE_HOOK, P_TMP_DEFEND_HOOK, P_TMP_ATTACK_HOOK
+     neue Hooks: std/hooks
+
+----------------------------------------------------------------------------
+08.12.2008, Zesstra
diff --git a/doc/props/skill_info_liste b/doc/props/skill_info_liste
new file mode 100644
index 0000000..011f616
--- /dev/null
+++ b/doc/props/skill_info_liste
@@ -0,0 +1,315 @@
+SI_*
+DEFINIERT IN:
+    /sys/newskills.h
+
+BESCHREIBUNG:
+    Die folgende Liste der SI-Typen ist grob nach Gueltigkeit fuer Skills
+    und Spells sortiert.
+    
+    Anwendungsorte der SI_-Werte sind:
+    - /std/living/combat und von dort gerufene Skills (Kampf)
+    - /std/gilden_ob und in Gilden lernbare Spells/Skills
+    - /std/spellbook und von Spellbooks ausgefuehrte Spells
+    - /std/living/life und von dort gerufene Skills (Alkohol)
+    - /std/shells/* und entsprechende Rassenskills
+
+    Im Skillsystem unter /std/living/skills wird vor auf Informationen
+    aus dem Teil #1 Ruecksicht genommen. Einige dieser Werte
+    werden auch permanent im Spieler gespeichert (wie zB die
+    SI_SKILLABILITY).
+
+AKTUELLE LISTE: (5. Oktober 2011)
+ #1 Skills und Spells:
+    SI_SKILLFUNC: string
+     Beinhaltet den Namen der Methode, die die eigentliche Funktionalitaet
+     des Skills/Spells implementiert.
+     Falls nicht angegeben, wird bei Spells durch UseSpell() das Verb, das
+     der Spieler eingegeben hat, als Spell-Methodenname angenommen.
+     Im Gildenobjekt kann hier abweichend von Spell-Namen stehen, wie die
+     Spellfunktion im Spellbook heisst.
+
+    SI_CLOSURE: closure
+     Optimiert den Zugriff fuer den internen Gebrauch, indem die gefundene
+     Spell/Skillfunktion darin direkt gespeichert wird und so lange gueltig
+     ist, bis das/die Objekt(e)/Closure(s) zerstoert sind.
+     Kann theoretisch auch fuer externe Skills durch (Autoload-)Objekte
+     benutzt werden.
+
+    SI_SKILLABILITY: int
+     Faehigkeit, den Spell/Skill zu benutzen. Normal ist von
+     -MAX_ABILITY bis MAX_ABILITY.
+
+    SI_SKILLDAMAGE_TYPE: string*
+     Art des Schadens eines Angriffs: siehe Schadenstypen in /sys/combat.h
+
+    SI_SKILLDAMAGE_MSG: string
+     Meldung anstatt der Waffe oder Haende-Meldung.
+    SI_SKILLDAMAGE_MSG2: string
+     Meldung anstatt der Waffe oder Haende-Meldung fuer den Raum.
+
+    SI_INHERIT: string
+     Enthaelt den Namen des Skills/Spells, von dem geerbt wird. Das
+     bedeutet einerseits, das LearnSkill() auch diesen uebergeordneten
+     Skill beeinflusst und andererseits, dass bei Skills auch dieser
+     uebergeordnete Skill im Rahmen einer Skill-Anwendung gerufen wird.
+
+    SI_DIFFICULTY: int / [Spells: mixed]
+     Schwierigkeit eines Skills/Spells. Beeinflusst die jeweils oberen
+     Schranken fuer das Lernen eines Skills/Spells in Abhaengigkeit 
+     von Spielerlevel.
+     Wenn in einem Spell-Info-Mapping kein Wert steht wird bei Spells
+     automatisch SI_SPELLCOST genommen, der Wert im Spell-Info-Mapping
+     geht beim Lernen aber immer vor Parametern.
+    FACTOR(SI_DIFFICULTY): int / mixed
+    OFFSET(SI_DIFFICULTY): int / mixed
+     Nur fuer Spellbooks/Spells. Der mixed-Parameter kann Funktionen
+     enthalten. Siehe unten bei SI_SPELLCOST.
+
+    SI_LASTLIGHT: int
+     Zeitpunkt, bis wann der Standardskills SK_NIGHTVISION den Spieler
+     sehen laesst.
+
+    SI_TESTFLAG: int
+     Wenn gesetzt, dann soll der Skill nicht genutzt, sondern nur getestet
+     werden, ob er erfolgreich waere. Entspricht also in etwa dem Aufruf
+     von SpellSuccess() in Spellbooks, wird aber bei UseSkill() als
+     Skill-Parameter uebergeben.
+     Der Skill muss entsprechend implementiert sein!
+
+    SI_GUILD: string
+     Angabe der Gilde, aus der der Skill stammt. Sinnvoll, wenn der Skill
+     nicht aus der aktuellen P_GUILD stammt. Fuer generelle Skills/Spells
+     zB waere das "ANY".
+     Gilt nicht fuer Spells!
+
+    SI_ENEMY: object
+     Aktuelles Feindesobjekt, wird bei Skill-Anwendung aus dem Kampf heraus
+     von std/living/combat.c an den Skill uebergeben.
+     Beispiel: Standardwaffenskills, SK_MAGIC_DEFENSE, SK_MAGIC_ATTACK,
+               SK_TWOHANDED, SK_SPELL_DEFEND, SK_INFORM_DEFEND und
+               SK_DEFEND_OTHER.
+
+    SI_FRIEND: object
+     Zu verteidigendes Freundesobjekt, wird bei Skill-Anwendung aus dem
+     Kampf heraus von std/living/combat.c an den Skill uebergeben.
+     Beispiele: SK_INFORM_DEFEND und SK_DEFEND_OTHER.
+
+    SI_MAGIC_TYPE: string*
+     Enthaelt ein Array der Magietypen, die zum Einsatz kommen. Die Angabe
+     geschieht zB im Spellbook und beeinflusst SpellDefend().
+
+    SI_LAST_USE: int (eventuell obsolet)
+     Letzte Nutzung des Skills.
+    
+    SI_LEARN_PROB: int (eventuell obsolet)
+     Lernwahrscheinlichkeit.
+
+    SI_SKILLDURATION: int
+     Dauer des Skills/Spells. Momentan nicht allzu verbreitet in Nutzung.
+
+ #2 nur fuer Spells:
+    SI_SPELLBOOK: string
+     Kann direkt das enthaltende Spellbook fuer einen Spell enthalten.
+
+    SM_RACE: mapping
+      Mapping, das als Key die Rasse und als Value ein Mapping X
+      enthaelt. Dieses Mapping X wird direkt zu sinfo hinzugefuegt und
+      ueberschreibt damit bei Bedarf Defaultwerte durch rassenspezifische
+      Werte.
+
+    SI_SPELLCOST: int / mixed
+    FACTOR(SI_SPELLCOST): int / mixed
+    OFFSET(SI_SPELLCOST): int / mixed
+     Beinhaltet die Werte, die fuer die Berechnung der Spellkosten
+     zustaendig sind.
+     Dabei gilt: Realkosten = OFFSET(COST) + (COST * FACTOR(COST))/100
+     Die einzelnen Eintraege koennen anstatt eines fixen Wertes auch den
+     Verweis auch eine Funktion in Form von Closure/Methodenname/Array mit
+     Objekt/Objektname und Methodenname enthalten. Siehe dazu auch
+     execute_anything().
+
+    SI_TIME_MSG: string
+     Meldung, die dem Spieler mitteilt, dass er noch nicht wieder einen
+     Spell casten kann. Falls dieser Eintrag nicht gesetzt ist, wird
+     ein Standardtext ausgegeben.
+
+    SI_SP_LOW_MSG: string
+     Meldung, die dem Spieler mitteilt, dass er zu wenige
+     Konzentrationspunkte besitzt, um den Spell zu casten. Falls dieser
+     Eintrag nicht gesetzt ist, wird ein Standardtext ausgegeben.
+
+    SI_PREPARE_MSG: string
+     Meldung, die dem Spieler ausgegeben werden soll, wenn er einen Spell
+     vorbereitet. Ansonsten wird ein Standardtext ausgegeben.
+
+    SI_PREPARE_BUSY_MSG: string
+     Meldung, die dem Spieler ausgegeben werden soll, wenn er schon diesen
+     Spell vorbereitet. Ansonsten wird ein Standardtext ausgegeben.
+
+    SI_PREPARE_ABORT_MSG: string
+     Meldung, die dem Spieler ausgegeben werden soll, wenn er die
+     Vorbereitung dieses Spells durch einen anderen Spell unterbricht.
+     Ansonsten wird ein Standardtext ausgegeben.
+
+    SI_NOMAGIC: int
+     Wert zwischen 0 und 100 (oder mehr), der gegen die P_NOMAGIC-Wirkung
+     eines Raumes wirkt.
+     Je hoeher der Wert ist, desto unwahrscheinlicher ist es, dass ein
+     Raum den Spell durch Antimagie stoert. Ein Raum sollte nur Werte
+     zwischen 0 und 100 gesetzt haben. Ist der Wert des Raums groesser
+     als der hier angegeben, dann blockiert er Magie mit einer gewissen
+     eben seiner angegebenen Wahrscheinlichkeit.
+
+    SI_NOMAGIC_MSG: string
+     Meldung, die bei Fehlschlag durch P_NOMAGIC des Raumes ausgegeben
+     wird. Ansonsten wird ein Standardtext ausgegeben.
+      
+    SI_SPELLFATIGUE: int / mixed
+    FACTOR(SI_SPELLFATIGUE): int / mixed
+    OFFSET(SI_SPELLFATIGUE): int / mixed
+     Werte, die fuer die Berechnung der Wieder-Cast-Zeit benutzt werden.
+     Die Berechnung und die moeglichen Angaben (Closure etc.) sind
+     identisch zu SI_SPELLCOST.
+     Das Spellbook gibt bei Nicht-Wieder-Bereitschaft SI_TIME_MSG aus.
+
+    SI_X_SPELLFATIGUE: mapping mit ([string key: int val])
+     Werte, die fuer die Berechnung der Wieder-Cast-Zeit benutzt werden.
+     Spellbook-Casten: Mapping wird durch Aufruf von CheckSpellFatigue(<key>)
+     am Caster gefiltert. Nur wenn das resultierende Mapping leer ist (kein
+     <key> hat einen gueltigen Fatigue-Eintrag), ist Spell castbar, sonst
+     Ausgabe von SI_TIME_MSG.
+     Nach Spellbook-Casten: Setzen der Fatigue fuer alle <val> > 0 mit <key>.
+
+    SI_SKILLLEARN: int / mixed
+    FACTOR(SI_SKILLLEARN): int / mixed
+    OFFSET(SI_SKILLLEARN): int / mixed
+     Werte, die fuer die Berechnung der Lerngeschwindigkeit benutzt
+     werden, normalerweise pro A_INT/2 je 0.01% (also 1 von MAX_ABILITY).
+     Die Berechnung und die moeglichen Angaben (Closure etc.) sind
+     identisch zu SI_SPELLCOST.
+
+    SI_LEARN_ATTRIBUTE: mapping
+     Mapping, welches die Attribute, nach denen gelernt werden soll
+     als Keys enthaelt. Der Value legt die Gewichtung fest, denn bei
+     mehreren Attributen wird ein Mittelwert gebildet.
+     Der Normalfall entspraeche ([A_INT: 1]) fuer SI_LEARN_ATTRIBUTE.
+
+    SI_NO_LEARN
+     Wenn man (temporaer!) nicht will, dass dieser Skill gelernt wird.
+     Muss von den Spellbooks beachtet werden.
+     Sollte niemals im Spieler abgespeichert werden. Oder permanent in
+     Gilde/Spellbook gesetzt sein. Sondern im Laufe einesr Nutzung in der
+     jew. Kopie von sinfo gesetzt werden, die gerade genutzt wird.
+    
+     SI_SKILLARG: string
+     Beinhaltet den Text, den der Spieler nach dem Spellkommando eingegeben
+     hat. Z.B. stuende bei "krallenschlag ork 2" der Text "ork 2" im
+     Parameter.
+
+    SI_SKILLRESTR_USE: mixed
+     Einschraenkungen fuer das Nutzen des Spells.
+     Wird normalerweise direkt im Spellbook fuer den Spell eingetragen.
+     Die einzelnen Moeglichkeiten werden in der manpage zu
+     "check_restrictions" erlaeutert.
+     
+    SI_SKILLRESTR_LEARN: mixed
+     Einschraenkungen fuer das Lernen des Spells.
+     Wird normalerweise direkt im Gildenobjekt fuer den Spell
+     eingetragen.
+     Die einzelnen Moeglichkeiten werden in der manpage zu
+     "check_restrictions" erlaeutert.
+
+    SI_SKILLINFO: string
+    SI_SKILLINFO_LONG: string
+     Beschreibung des Spells. Wird im Gildenobjekt eingetragen und
+     SI_SKILLINFO wird von SkillListe angezeigt.
+
+    SI_SKILLDAMAGE: int / mixed
+    FACTOR(SI_SKILLDAMAGE): int / mixed
+    OFFSET(SI_SKILLDAMAGE): int / mixed
+     Werte, die fuer die Schadenshoehenberechnung des Spells benutzt
+     werden (random ueber den Gesamtwert normalerweise).
+     Die Berechnung und die moeglichen Angaben (Closure etc.) sind
+     identisch zu SI_SPELLCOST.
+
+    SI_SKILLDAMAGE_BY_ROW
+     Ein Mapping mit Boni fuer den Angriff aus bestimmten Kampfreihen.
+     Funktioniert nur bei Verwendung von TryDefaultAttackSpell-Spells
+     aus dem Spellbook.
+     Der Key steht fuer eine bestimmte Reihe, sein Wert fuer den
+     randomisierten Bonus fuer Lebewesen, die aus dieser Reihe casten.
+    OFFSET(SI_SKILLDAMAGE_BY_ROW)
+     Ein Mapping mit fixem Wert fuer Row-Boni im Kampf.
+
+     Beispiel: AddSpell("feuerball", 20,
+                        ([SI_SKILLDAMAGE:50,
+                          OFFSET(SI_SKILLDAMAGE):100,
+                          SI_SKILLDAMAGE_BY_ROW:([2:40,3:20}),
+                          OFFSET(SI_SKILLDAMAGE_BY_ROW):([2:20]), ...
+     gibt
+      Reihe 1: random(50)+100;
+      Reihe 2: random(50)+100+random(40)+20
+      Reihe 3: random(50)+100+random(20)
+     ACHTUNG: Hier gilt nicht die selbe Struktur wie bei SI_SPELLCOST!
+
+    SI_SPELL: mapping
+     Dieser Eintrag enthaelt verschiedene Unterwerte, die den Spell
+     gerade fuer Angriffs-Spells genauer beschreiben. Siehe Defend()
+     fuer eine Beschreibung der verschiedenen Eintraege (die so auch
+     in das Spellbook uebernommen werden koennen).
+
+    SI_COLLATERAL_DAMAGE: int
+     Hiermit kann man einen Prozentwert von SI_SKILLDAMAGE (inklusive
+     Offsets und Factor) fuer Kollateralschaden an Freunden im definierten
+     Bereich eines Flaechenspells (TryGlobalAttackSpell()) angeben.
+     10 bedeutet bei OFFSET(SI_SKILLDAMAGE)=100 zB 10% von 100 = 10 = 1 LP
+     an mit reduce_hit_points() verursachtem Schaden.
+    
+    SI_NUMBER_ENEMIES, SI_NUMBER_FRIENDS: int
+     Wird von TryGlobalAttackSpell() im Spell gesetzt und enthaelt die
+     Anzahl der im angegebenen Bereich befindlichen Feinde und Freunde.
+     Ist dementsprechend von informativem Nutzen fuer den Angegriffenen
+     und das Spellbook NACH Aufruf von TryGlobalAttackSpell().
+    
+    SI_DISTANCE, SI_WIDTH, SI_DEPTH: int
+     Limitiert die Entfernung, Tiefen und Breite der Wirkung eines
+     TryGlobalAttackSpell()
+
+    SI_SKILLHEAL: int
+     Konvention fuer Spells im Spellbook, dort den Heilwert zu hinterlegen,
+     hat keine Auswirkungen unter /std/.
+
+    SI_USR: mixed
+     Fuer selbst definierte Infos, spellbookabhaengig.
+
+    SI_PREPARE_TIME: int
+     Dauer der Zeit fuer zu praeparierende Spells.
+
+    SI_ATTACK_BUSY_MSG: string
+     Meldung, die dem Spieler mitteilt, dass er schon zu beschaeftigt
+     mit einem Angriff ist, um einen Spell zu casten. Falls dieser
+     Eintrag nicht gesetzt ist, wird  ein Standardtext ausgegeben.
+
+    SI_NO_ATTACK_BUSY: int
+     Enthaelt als Flag NO_ATTACK_BUSY_QUERY wenn der Spell NICHT wie
+     eine Spezialwaffe die Aktionen einschraenkt. Siehe P_ATTACK_BUSY.
+
+    SI_ATTACK_BUSY_AMOUNT: int
+     Menge des P_ATTACK_BUSY fuer diesen Spell. Falls dieser Wert nicht
+     gesetzt ist, aber auch SI_NO_ATTACK_BUSY nicht mit
+     NO_ATTACK_BUSY_QUERY ausgezeichnet ist wird 1 angenommen.
+
+SIEHE AUCH:
+    Skills Lernen:  LearnSkill, ModifySkill, LimitAbility
+    * Nutzung:      UseSpell, UseSkill
+    * Abfragen:     QuerySkill, QuerySkillAbility
+    * Modifikation: ModifySkillAttribute, QuerySkillAttribute,
+                    QuerySkillAttributeModifier, RemoveSkillAttributeModifier
+      * Properties: P_SKILL_ATTRIBUTES, P_SKILL_ATTRIBUTE_OFFSETS
+    * sonstig:      spruchermuedung
+    * Properties:   P_NEWSKILLS
+    Spellbook:      UseSpell, Learn, SpellSuccess
+    Gilden:         gilden-doku
+    Kampf:          Defend
+
+7. Nov 2012 Gloinson
diff --git a/doc/props/svn-commit.tmp b/doc/props/svn-commit.tmp
new file mode 100644
index 0000000..24ffe76
--- /dev/null
+++ b/doc/props/svn-commit.tmp
@@ -0,0 +1,6 @@
+
+--This line, and those below, will be ignored--
+
+M    P_FUEL
+M    P_LIGHTDESC
+M    P_DO_DESTRUCT
diff --git a/doc/scmd/.readme b/doc/scmd/.readme
new file mode 100644
index 0000000..b4d69ca
--- /dev/null
+++ b/doc/scmd/.readme
@@ -0,0 +1,6 @@
+Hier befinden sich die Hilfeseiten, die fuer Seher interessant sind:
+
+ * Beschreibung der neuen Befehle
+ * allgemeine Informationen zum Bau eines Seherhauses
+ * Beschreibung der Befehle, die die Seherhaeuser zur Verfuegung stellen
+
diff --git a/doc/scmd/.synonym b/doc/scmd/.synonym
new file mode 100644
index 0000000..256e3b7
--- /dev/null
+++ b/doc/scmd/.synonym
@@ -0,0 +1,3 @@
+r: remote
+r; remote
+
diff --git a/doc/scmd/aendere b/doc/scmd/aendere
new file mode 100644
index 0000000..e2447ef
--- /dev/null
+++ b/doc/scmd/aendere
@@ -0,0 +1,52 @@
+
+aendere
+-------
+
+ SEHERHAUSKOMMANDO:
+    aendere <haus> lang
+    aendere detail <det>
+    aendere lesbares detail <det>
+    aendere befehl <bef>
+    aendere <truhe>
+    aendere meldungen
+
+ ARGUMENTE:
+
+     <haus>
+        eine gueltige Bezeichnung des Hauses. Gueltige Bezeichnungen sind
+        innerhalb des Hauses "haus" und "raum", ausserhalb des Hauses die
+        Begriffe, mit denen sich das Haus ansprechen laesst.
+     <det>
+        Name des zu aendernden (lesbaren) Details. Es kann immer nur ein
+        (lesbares) Detail gleichzeitig geaendert werden!
+     <bef>
+        Name des zu aendernden Befehls (Verb + Parameter)
+     <truhe>
+        eine gueltige Bezeichnung der Truhe.
+
+ BESCHREIBUNG:
+    Mit diesem Befehl ist es moeglich, schon vorhandene Beschreibungen zu
+    aendern. Dies ist zum Beispiel noetig, wenn man (oder ein Spieler) einen
+    Tippfehler gefunden hat, oder wenn man eine Beschreibung noch ein wenig
+    erweitern moechte.
+
+    Bei zu aendernden (lesbaren) Details und Befehlen werden alle mehrfach
+    auftretenden Texte gleichzeitig geaendert (d.h. wenn man die Details
+    "wand" und "waende" mit den gleichen Beschreibungen versehen hat, wird bei
+    aendere detail wand auch der Text von "waende" geaendert).
+
+    Geaendert werden koennen die Langbeschreibungen des Hauses (innen und
+    aussen), die Langbeschreibung der Truhe, Details, lesbare Details, Befehle
+    sowie die Liste der Rueckmeldungen von Spielern (also typo, fehler und
+    idee). Das ist recht nuetzlich, damit man die Meldungen, die man schon
+    abgearbeitet hat, wieder los wird.
+
+    Der zu aendernde Text wird in den Editor geladen und kann wie gewohnt
+    bearbeitet werden. Der Cursor befindet sich dabei zu Beginn ganz am Ende
+    des Textes.
+
+ SIEHE AUCH:
+    beschreibe, befehl, loesche, kopiere, meldungen, uebersicht, notiz
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/ausgang b/doc/scmd/ausgang
new file mode 100644
index 0000000..2b950d6
--- /dev/null
+++ b/doc/scmd/ausgang
@@ -0,0 +1,53 @@
+
+ausgang
+-------
+
+ SEHERHAUSKOMMANDO:
+    ausgang <richtung> <nr>
+    ausgang <richtung> <name> <nr>
+
+ ARGUMENTE:
+
+     <richtung>
+        Richtung des gewuenschten Ausgangs. Es sind nur die acht
+        Himmelsrichtungen sowie "oben" und "unten" erlaubt. Die Richtung muss
+        ausgeschrieben werden, d.h. es muss heissen ausgang suedwesten 2 statt
+        ausgang sw 2!
+     <nr>
+        Nummer des Zielraumes, zu dem der Ausgang gehen soll.
+     <name>
+        Name des Sehers, zu dessen Haus der Ausgang fuehren soll. Dies ist
+        nicht ohne weiteres moeglich, s.u.!
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kann man dem Raum, in dem man gerade steht, einen
+    weiteren Ausgang hinzufuegen. Im Zielraum entsteht dabei automatisch ein
+    Ausgang in die Rueckrichtung.
+
+    Es sind folgende Regeln zu beachten:
+
+     * es darf in diesem Raum noch keinen Ausgang in die Zielrichtung geben!
+     * es darf im Zielraum noch keinen Ausgang in die Rueckrichtung geben (die
+       Rueckrichtung erhaelt man, wenn man in der Hinrichtung "osten" und
+       "westen", "norden" und "sueden" sowie "oben" und "unten" jeweils
+       gegeneinander austauscht)!
+     * wenn der Ausgang ins eigene Haus geht, darf <nr> nicht die Nummer des
+       Raumes sein, in dem man gerade steht!
+     * es muss in dem Haus natuerlich schon ein Raum mit der Nummer <nr>
+       existieren!
+
+    Will man sein Haus mit einem anderen Haus verbinden, so ist zusaetzlich
+    folgendes zu beachten:
+
+     * das Zielhaus muss sich im gleichen Raum befinden wie das eigene Haus!
+     * der Besitzer des Zielhauses hat Dir mit erlaube den Zugriff auf sein
+       Haus gewaehrt!
+
+    Die Nummer des Raumes, in dem Du stehst, wird bei dem Kommando uebersicht
+    mit ausgegeben (wenn Du ueber mehr als einen Raum verfuegst).
+
+ SIEHE AUCH:
+    sperre, uebersicht
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/befehl b/doc/scmd/befehl
new file mode 100644
index 0000000..4f99b3c
--- /dev/null
+++ b/doc/scmd/befehl
@@ -0,0 +1,207 @@
+
+befehl
+------
+
+ SEHERHAUSKOMMANDO:
+    beschreibe befehl <bef>
+
+ ARGUMENTE:
+
+     <bef>
+        Liste der zu beschreibenden Befehle. Bei mehreren Befehlen muessen
+        diese durch Kommata voneinander getrennt sein. Es duerfen nur Verben
+        angegeben werden, ohne Parameter!
+
+ BESCHREIBUNG:
+    Beginnt mit der Beschreibung des Befehles <bef> (oder mehrere, falls es
+    sich bei <bef> um eine Liste von Verben handelt).
+
+    Die Standard-Hauskommandos (beschreibe, loesche, etc.) duerfen NICHT
+    beschrieben werden! Einzige Ausnahmen: "oeffne", "schliesse" und
+    "schliess".
+
+    Ausgangsbefehle (Himmelsrichtungen, "oben", "unten" und "raus") koennen
+    beschrieben werden; aber nur dann, wenn es auch einen entsprechenden
+    Ausgang gibt. Sperrt man hinterher einen Ausgang, zu dem es einen Befehl
+    gibt, so wird der Befehl wieder geloescht!
+
+    Anschliessend wird man nach den Parametern fuer den Befehl gefragt. Auch
+    hier koennen mehrere Parameter durch Kommata voneinander getrennt werden;
+    ausserdem kann man hier mehrere Woerter benutzen (siehe dazu auch das
+    Beispiel unten). Man kann allerdings auch ganz auf Parameter verzichten
+    und einfach <RETURN> druecken.
+
+    Schliesslich wird man nach dem Text gefragt, der als Reaktion auf den
+    Befehl ausgegeben werden soll. Dazu steht wieder der Editor zur
+    Verfuegung. Man gibt in einem Rutsch sowohl den Text fuer denjenigen ein,
+    der den Befehl ausfuehrt, als auch den Text fuer die Leute, die sich im
+    gleichen Raum befinden. Die Texte werden durch eine Zeile mit einem @@
+    voneinander getrennt.
+
+    In den Texten sind verschiedene Platzhalter moeglich, um in den einzelnen
+    Befehlen differenziertere Aktionen zuzulassen:
+
+     * Der Name des/der Ausfuehrenden laesst sich mit @WER, @WESSEN, @WEM und
+       @WEN einbauen (jeweils im entsprechenden Fall).
+     * Personalpronomina sind mit @PWER, ... moeglich. Es ist dabei jedoch zu
+       beachten, dass sie *kleingeschrieben* eingefuegt werden! Man sollte sie
+       also nicht unbedingt am Satzanfang benutzen.
+     * Possessivpronomina: Das ist etwas komplizierter, denn hier muessen noch
+       Geschlecht und Anzahl des Objektes angegeben werden, auf das sich das
+       Possessivpronomen bezieht. Der Aufbau des Platzhalters:
+       @B, gefolgt von M, F oder N fuer das Geschlecht des Bezugsobjektes
+       (Maskulinum, Femininum oder Neutrum), S oder P fuer die Anzahl
+       (Singular oder Plural) und schliesslich WER, WESSEN, WEM oder WEN fuer
+       den Fall des Pronomens an sich.
+       Das fuehrt dann zu Konstruktionen wie @BMSWER oder @BFPWEN. Am besten,
+       man probiert es einfach mal aus (siehe aber auch die Beispiele).
+       Auch hier gilt uebrigens wieder: Die Pronomina werden kleingeschrieben
+       eingefuegt!
+
+    Weitere Moeglichkeiten sind rassen-, namens- und geschlechtsspezifische
+    Ausgaben.
+ 
+    Als Trenner dienen @G fuer geschlechtsspezifische Ausgaben und @RE (fuer
+    Elfen), @RH (fuer Hobbits), @RM (fuer Menschen), @RZ (fuer Zwerge), 
+    @RF (fuer Felinen), @RD (fuer Dunkelelfen) sowie @RG (fuer Goblins) fuer 
+    rassenspezifische Ausgaben.
+
+    Als spezielle "Rasse" gibt es darueberhinaus noch @RA fuer Ausgaben, die
+    nur die Spieler gehen, die im Haus besondere Rechte haben (siehe erlaube).
+
+    Darueberhinaus kann man Ausgaben fuer bestimmte Spieler vorsehen. Der
+    Trenner hierfuer ist @NAME:spieler, wobei 'spieler' fuer den Namen des
+    entsprechenden Spielers steht.
+
+    Aehnlich wie bei der Trennung zwischen dem Text fuer den Ausfuehrenden und
+    dem Text fuer die Umstehenden muessen die Trenner in einer Extrazeile
+    stehen.
+
+    Bei @G wird der Text ueber dem Trenner bei maennlichen und der Text unter
+    dem Trenner bei weiblichen Befehlsausfuehrern ausgegeben.
+
+    Bei den Rassen- und Namenstrennern wird der Text unterhalb des Trenners
+    ausgegeben, der der Rasse bzw. dem Namen des Ausfuehrenden entspricht.
+
+    Wenn fuer eine Rasse kein eigener Text angegeben ist, wird der Text vor
+    dem ersten Rassen- bzw. Namenstrenner ausgegeben (und wenn gar kein
+    Rassentrenner angegeben wurde, wird der Text fuer alle Rassen
+    ausgegeben ;)
+
+    Innerhalb der rassenspezifischen Texte kann wieder mit @G
+    geschlechtsspezifisch unterschieden werden.
+
+    Um eine etwas andere Meldung als "Wie bitte?" zu bekommen, wenn ein Befehl
+    mit einem ungueltigen Parameter eingegeben wurde, kann man den speziellen
+    Parameter @nf@ angeben. In den Text kann der ungueltige Parameter mit dem
+    Platzhalter @PARA integriert werden.
+
+    Wenn die Umstehenden keine Meldung bekommen sollen, kann man den @@ und
+    den zweiten Text einfach weglassen.
+
+ BEISPIELE:
+    Zunaechst ein ganz einfacher Text:
+
+      beschreibe befehl drueck, druecke
+      => Bitte Parameter eingeben.
+      ]knopf, auf knopf
+      => Bitte Text eingeben, der fuer diesen Befehl ausgegeben werden soll.
+      ]Du drueckst auf den Knopf, aber nichts passiert.
+      ].
+
+    (Wenn jemand nun "druecke knopf" eingibt, erhaelt er die Meldung "Du
+    drueckst auf den Knopf, aber nichts passiert." Andere Spieler, die sich im
+    gleichen Raum aufhalten, bekommen keine Meldung)
+
+    Jetzt ein Befehl mit Ausgabe an die Umstehenden:
+
+      beschreibe befehl zieh, ziehe
+      => Bitte Parameter eingeben.
+      ]faden, an faden
+      => Bitte Text eingeben, der fuer diesen Befehl ausgegeben werden soll.
+      ]Du ziehst an dem Faden. Es gibt einen lauten Knall, und Du siehst nur
+      ]noch wirbelnde Punkte.
+      ]@@
+      ]Es gibt einen lauten Knall, und die Konfettikanone huellt @WEN in
+      ]eine wirbelnde Konfettiwolke.
+      ].
+
+    (Wenn Wargon an dem Faden zieht, bekommt er die obere Meldung, und die
+    Umstehenden bekommen "Es gibt einen lauten Knall, und die Konfettikanone
+    huellt Wargon in eine wirbelnde Konfettiwolke.")
+
+    Hier jetzt ein Beispiel mit Geschlechtertrennung:
+
+      beschreibe befehl schau
+      => Bitte Parameter eingeben.
+      ]in spiegel
+      => Bitte Text eingeben...
+      ]Hm, Du muesstest Dich mal wieder rasieren.
+      ]@G
+      ]Dein MakeUp ist ziemlich verschmiert.
+      ].
+
+    (Schaut ein Spieler in den Spiegel, wird er auf seine Rasur hingewiesen;
+    eine Spielerin dagegen auf ihr MakeUp.)
+
+    Etwas komplexer: Rassentrenner und Possessivpronomen:
+
+      beschreibe befehl schau
+      => Bitte Parameter eingeben.
+      ]auf tisch
+      => Bitte Text eingeben...
+      ]Du siehst einen Haufen Papiere auf dem Tisch liegen.
+      ]@RZ
+      ]Der Tisch ist zu hoch fuer Dich.
+      ]@@
+      ]@WER untersucht den Tisch.
+      ]@RZ
+      ]@WER versucht verzweifelt, einen Blick auf den Tisch zu werfen,
+      ]aber @BMSWER kleiner Koerperbau laesst das nicht zu.
+      ].
+
+    (Keine Chance fuer Zwerge! ;) @BMSWER wird bei maennlichen Zwergen durch
+    "sein" und bei weiblichen Zwergen durch "ihr" ersetzt.)
+
+    Das gleiche Beispiel wie gerade, diesmal jedoch zusaetzlich mit speziellen
+    Texten fuer Wargon und Saphis:
+
+      beschreibe befehl schau
+      => Bitte Parameter eingeben.
+      ]auf tisch
+      => Bitte Text eingeben...
+      ]Du siehst einen Haufen Papiere auf dem Tisch liegen.
+      ]@NAME:Wargon
+      ]Ein Haufen Papier. Schaff doch mal wieder Ordnung!
+      ]@NAME:Saphis
+      ]Ein Haufen Papier. Sag Deinem Chef mal, er soll hier wieder Ordnung
+      ]schaffen!
+      ]@RZ
+      ]Der Tisch ist zu hoch fuer Dich.
+      ]@@
+      ]@WER untersucht den Tisch.
+      ]@NAME:Wargon
+      ]Wargon untersucht den Tisch und seufzt.
+      ]@RZ
+      ]@WER versucht verzweifelt, einen Blick auf den Tisch zu werfen,
+      ]aber @BMSWER kleiner Koerperbau laesst das nicht zu.
+      ].
+
+    Und zum Schluss ein Beispiel fuer ein geaendertes "Wie bitte?"
+
+      beschreibe befehl drueck,druecke
+      => Bitte Parameter eingeben.
+      ]@nf@
+      => Bitte Text eingeben...
+      ]Wieso willst Du denn @PARA druecken?
+      ].
+
+    (Wenn man jetzt zB. "druecke hand" eingibt, und dieser Befehl noch nicht
+    beschrieben wurde, kommt als Meldung: "Wieso willst Du denn Hand
+    druecken?")
+
+ SIEHE AUCH:
+    aendere, beschreibe
+
+ LETZTE AeNDERUNG:
+    Sun, 03.12.2000, 18:30:00 von Wargon
diff --git a/doc/scmd/beschreibe b/doc/scmd/beschreibe
new file mode 100644
index 0000000..12cd000
--- /dev/null
+++ b/doc/scmd/beschreibe
@@ -0,0 +1,195 @@
+
+beschreibe
+----------
+
+ SEHERHAUSKOMMANDO:
+    beschreibe <haus> lang
+    beschreibe <haus> kurz
+    beschreibe detail <det>
+    beschreibe lesbares detail <det>
+    beschreibe befehl <bef> - Siehe befehl!
+    beschreibe haustuer
+    beschreibe tuerzustand
+    beschreibe truhe
+
+ ARGUMENTE:
+
+     <haus>
+        eine gueltige Bezeichnung des Hauses. Gueltige Bezeichnungen sind
+        innerhalb des Hauses "haus" und "raum", ausserhalb des Hauses die
+        Begriffe, mit denen sich das Haus ansprechen laesst.
+     <det>
+        Liste der zu beschreibenden (lesbaren) Details. Bei mehreren Details
+        muessen diese durch Kommata voneinander getrennt sein!
+
+ BESCHREIBUNG:
+    Dieses Kommando dient zur allgemeinen Beschreibung des Hauses. Von den
+    moeglichen Anwendungen ist beschreibe haustuer nur draussen erlaubt.
+    beschreibe <haus> lang ist sowohl innen als auch aussen benutzbar; alle
+    anderen Anwendungen gelten nur innerhalb des Hauses.
+
+    Nach Eingabe des Kommandos befindet man sich bei den meisten Arten in dem
+    selben Editor, wie man ihn auch von der Post oder der Zeitung gewohnt ist.
+    Man hat also auch hier die Moeglichkeit, sich mit "~h" eine Hilfeseite des
+    Editors mit allen moeglichen Kommandos anzeigen zu lassen.
+
+    Falls es vorkommen sollte, dass waehrend der Beschreibung die Verbindung
+    zum MUD zusammenbricht, sperrt der Editor! In diesem Fall kann man mit
+    "~r" die unterbrochene Beschreibung wieder aufnehmen. (Das ~r muss als
+    Befehl im Haus angegeben werden!)
+
+    Hier nun die Moeglichkeiten zur Beschreibung:
+
+     beschreibe <haus> lang
+        Hiermit wird die Langbeschreibung des Hauses begonnen. Es gibt zwei
+        Faelle: Befindet man sich innerhalb des Hauses, so gibt man jetzt den
+        Text ein, den man beim Betreten des Hauses (bei eingeschaltetem
+        Lang-Modus) und bei einem schau im Haus bekommt. Befindet man sich
+        ausserhalb des Hauses, so gibt man jetzt den Text ein, den man beim
+        Untersuchen des Hauses bekommt. An den Text fuer die
+        Aussenbeschreibung wird immer eine Zeile mit dem aktuellen Zustand der
+        Haustuer angehaengt.
+
+     beschreibe <haus> kurz
+        Beginnt die Eingabe der Kurzbeschreibung des Hauses. Dies ist der
+        Text, den man mit eingeschaltetem Kurz-Modus beim Betreten des Hauses
+        bekommt.
+
+        Die Kurzbeschreibung kann nur im Inneren des Hauses geaendert werden.
+        Sie darf nicht laenger sein als eine Zeile!
+
+        Wenn Dein Haus auch von aussen anders aussehen soll, wende Dich bitte
+        an Wargon.
+
+     beschreibe detail <det>
+        Beginnt die Beschreibung des Details <det>. Als Details sollten
+        moeglichst alle Substantive beschrieben werden, die in der
+        Langbeschreibung des Hausinneren und in den anderen Details vorkommen
+        (natuerlich nur, soweit das sinnvoll ist). Den Text der Details
+        bekommt man bei dem Kommando "untersuche <det>" ausgegeben.
+
+     beschreibe lesbares detail <det>
+        Beginnt die Beschreibung eines lesbaren Details. Der Text wird bei dem
+        Kommando "lies <det>" ausgegeben.
+
+     beschreibe befehl <bef>
+        Da dies mittlerweile recht komplex geworden ist, gibt es eine
+        Extrahilfeseite. Du erreichst sie mit "hilfe befehl".
+
+     beschreibe haustuer
+        Erlaubt die Beschreibung der Haustuer. Hier ist nur eine Zeile
+        erlaubt. Diese Zeile wird an die Langbeschreibung des Hauses von
+        aussen angehaengt. Wenn dort keine Haustuer erscheinen soll (was zB.
+        bei einem See oder einer Hoehle wenig Sinn macht), kann man hier mit
+        einem Leerstring (direkt <RETURN> druecken) die Tuer unsichtbar
+        machen.
+
+        Mit dem Platzhalter %s wird der aktuelle Zustand der Haustuer in die
+        Beschreibung eingebaut (in der Form "geoeffnet", "geschlossen" oder
+        "abgeschlossen").
+
+     beschreibe tuerzustand
+        Auch den Zustand der Haustuer (geoeffnet, geschlossen, abgeschlossen)
+        kann man in eigene Worte fassen.
+
+        Die Zustaende muessen in der oben angegebenen Reihenfolge eingegeben
+        werden, und zwar in EINER Zeile, durch Kommata getrennt!
+        Es muessen immer ALLE drei Zustaende angegeben werden!
+        Man sollte nach Moeglichkeit nur ein Wort pro Zustand verwenden!
+
+     beschreibe truhe
+        Beginnt die Beschreibung der Truhe. Im Gegensatz zu den anderen
+        Beschreibungen erfolgt die Beschreibung der Truhe in mehreren
+        Schritten. Will man die jeweiligen Vorgaben nicht aendern, so genuegt
+        es, <RETURN> zu druecken.
+
+        Zuerst wird man nach dem Geschlecht der Truhe gefragt. Hier muss man
+        den passenden Buchstaben angeben (m fuer maennlich, w fuer weiblich
+        oder s fuer saechlich).
+
+        Nun wirst Du nach dem Namen gefragt, der zum Beispiel beim Oeffnen
+        oder Schliessen der Truhe ausgegeben wird. Falls der Name
+        unregelmaessig dekliniert wird, muessen alle vier Faelle in der
+        Reihenfolge Nominativ, Genitiv, Dativ, Akkusativ, durch Kommata
+        getrennt, eingegeben werden.
+
+        Schliesslich kannst Du noch Adjektive angeben, die zusammen mit dem
+        Namen ausgegeben werden.
+
+        Aus den bisher eingegebenen Daten wird nun die Kurzbeschreibung der
+        Truhe erstellt. Entspricht sie nicht Deinen Vorstellungen, gelangst Du
+        wieder in die Geschlechtsabfrage.
+
+        Ansonsten wird nun nach den IDs gefragt, mit denen sich die Truhe
+        ansprechen lassen soll. Mehrere IDs muessen mit Kommata voneinander
+        getrennt werden. Es koennen auch IDs verwendet werden, die aus
+        mehreren Wortern bestehen. Die Vorgaben koennen wieder mit <RETURN>
+        uebernommen werden.
+        Da einige Spezialisten es immer wieder schafften, hier schon die
+        Langbeschreibung einzugeben (und somit hinterher die Truhe nicht mehr
+        ansprechen konnten), laesst sich die Truhe fuer den
+        `beschreibe'-Befehl immer als `truhe' ansprechen. Zum Ansehen, Oeffnen
+        etc. muss man allerdings die hier angegebenen IDs verwenden.
+
+        Nun erfolgt die Langbeschreibung der Truhe. Bei dieser steht wieder
+        der Editor zur Verfuegung. Bei Abbruch oder einem Leerstring wird die
+        alte Langbeschreibung nicht geaendert.
+
+        Zum guten Schluss kann man auch noch das Material der Truhe aendern.
+        Im Hauptmenue kann man eine Materialgruppe waehlen, und dort dann das
+        gewuenschte Material.
+
+        Die Beschreibung der Truhe kann natuerlich nur geaendert werden, wenn
+        man im gleichen Raum steht wie die Truhe. ;-)
+
+ BEISPIELE:
+    Ein lesbares Detail wird beschrieben:
+
+      beschreibe lesbares detail zettel, notiz
+      => Bitte Beschreibung fuer 'zettel, notiz' eingeben.
+      ]Bin gerade metzeln. Blutsaugerschwerter bitte an der Pforte
+      ]abgeben.
+      ].
+
+    Die Truhe soll in eine kleine Kiste aus Holz umgewandelt werden:
+
+      beschreibe truhe
+      => Beschreibung der Truhe
+      => Wenn die Vorgaben nicht geaendert werden sollen, einfach weiter
+      => mit <RETURN>.
+
+      =>Geschlecht (m, w, s): [w]
+      ]w
+      => Das Geschlecht ist jetzt weiblich.
+      => Nun musst Du den Namen eingeben. ...
+      => Name: [Truhe]
+      ]Kiste, Kiste, Kiste, Kiste
+      => Der Name ist jetzt 'Kiste, Kiste, Kiste, Kiste'
+      => Du kannst jetzt noch Namensadjektive angeben. ...
+      => Namensadjektive: []
+      ]klein
+      => Namensadjektive lauten klein.
+      => Die Kurzbeschreibung lautet damit:
+      => Eine kleine Kiste (geschlossen).
+      => Falls das Ergebnis nicht Deinen ...
+      => Stimmt die Kurzbeschreibung so? (j/n)
+      ]j
+
+      =>IDs (durch Kommata getrennt):
+      =>[truhe,holztruhe]
+      ]kiste
+      => OK, IDs lauten 'kiste'
+
+      => Bitte Langbeschreibung angeben...
+      ]In dieser Kiste lagert Wargon wichtige Dinge.
+      ].
+
+      => Bitte das Material der Kiste betimmen:
+      ...
+
+ SIEHE AUCH:
+    editor,
+    aendere, befehl, loesche, kopiere, meldungen, uebersicht, notiz, licht
+
+ LETZTE AeNDERUNG:
+    Thu, 25.09.1997, 17:40:30 von Wargon
diff --git a/doc/scmd/echo b/doc/scmd/echo
new file mode 100644
index 0000000..22b6294
--- /dev/null
+++ b/doc/scmd/echo
@@ -0,0 +1,34 @@
+
+echo
+----
+
+ SEHERKOMMANDO:
+    echo <text>
+
+ ARGUMENTE:
+
+     <text>
+        ein beliebiger Text
+
+ BESCHREIBUNG:
+    Der Text <text> wird an alle Spieler im selben Raum gesendet.
+
+    Bei Sehern verbraucht dieses Kommando 50 Magiepunkte. Fuer Magier ist es
+    kostenlos.
+
+    ACHTUNG: Die Benutzung dieses Befehls wird geloggt! D.h. dass er nicht
+    zur Benutzung geeignet ist, wenn man nicht moechte, dass der gesendete
+    Text von Magiern nachgelesen werden kann. Dies ist aufgrund von Missbrauch
+    leider notwendig. In diesem Zusammenhang sei auch nochmal darauf 
+    hingewiesen, dass Echos in fremdem Namen nicht erlaubt sind und mit
+    dem Entzug der echo-Faehigkeit (plus (r)emote als Bonus) geahndet
+    werden koennen.
+
+BEMERKUNGEN:
+    Die Faehigkeit wird erst nach der Loesung des Quest "Hilf dem
+    Architekten." freigeschaltet.
+
+ SIEHE AUCH:
+    sag, emote
+
+1. Maerz 2013 Gloinson
diff --git a/doc/scmd/erlaube b/doc/scmd/erlaube
new file mode 100644
index 0000000..1e2916a
--- /dev/null
+++ b/doc/scmd/erlaube
@@ -0,0 +1,31 @@
+
+erlaube
+-------
+
+ SEHERHAUSKOMMANDO:
+    erlaube <name>
+
+ ARGUMENTE:
+
+     <name>
+        Ein oder mehrere Namen (durch Kommata getrennt).
+
+ BESCHREIBUNG:
+    Der Spieler <name> erhaelt die Erlaubnis, Dein Haus auf- und abschliessen
+    zu duerfen. Weiterhin darf dieser Spieler auch die Truhe in Deinem Haus
+    oeffnen.
+
+    Auch wenn ein Nachbar sein Haus mit Deinem verbinden will, muss ihm das
+    mit diesem Kommando erlaubt werden.
+
+    Dieser Befehl funktioniert nur im Eingangsraum des Hauses, und die Liste
+    der Leute mit erteilter Erlaubnis wird auch nur im Eingangsraum bei dem
+    Befehl uebersicht mit ausgegeben; die Liste gilt bei Ausgaengen jedoch
+    fuer das gesamte Haus (man kann also nicht selektiv Raeume zur Verbindung
+    freigeben).
+
+ SIEHE AUCH:
+    verbiete, uebersicht, ausgang
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/extralook b/doc/scmd/extralook
new file mode 100644
index 0000000..260f8ec
--- /dev/null
+++ b/doc/scmd/extralook
@@ -0,0 +1,29 @@
+
+extralook
+---------
+
+ SEHERKOMMANDO:
+    extralook <text>
+
+ ARGUMENTE:
+
+     <text>
+        Die Beschreibung.
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kannst Du Deine Beschreibung um zusaetzlichen Text
+    erweitern.
+
+    Der Text erscheint nach Deiner normalen Langbeschreibung und vor der
+    Auflistung Deines Inventars.
+
+    Gibst Du als <text> nur ein '?' an, wird Dir ein aktueller Extralook
+    ausgegeben.
+    Laesst Du <text> ganz weg, oeffnet sich ein Editor, in dem Du Deinen neuen
+    Extralook eingeben kannst.
+
+ SIEHE AUCH:
+    untersuche
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/fehlermeldung b/doc/scmd/fehlermeldung
new file mode 100644
index 0000000..6785e0e
--- /dev/null
+++ b/doc/scmd/fehlermeldung
@@ -0,0 +1,20 @@
+
+fehlermeldung
+-------------
+
+ SEHERKOMMANDO:
+    fehlermeldung [<text>]
+
+ ARGUMENTE:
+
+     <text>
+        Text der neuen Fehlermeldung (optional)
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kannst Du Dir eine neue Fehlermeldung anstelle des
+    ueblichen "Wie bitte?" setzen.
+
+    Laesst Du <text> weg, wird wieder auf "Wie bitte?" zurueckgeschaltet.
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/fluchtrichtung b/doc/scmd/fluchtrichtung
new file mode 100644
index 0000000..3b9a565
--- /dev/null
+++ b/doc/scmd/fluchtrichtung
@@ -0,0 +1,40 @@
+
+fluchtrichtung
+--------------
+
+ SEHERKOMMANDO:
+    fluchtrichtung <befehl>
+
+ ARGUMENTE:
+
+     <befehl>
+        Der Befehl fuer die bevorzugte Fluchtrichtung.
+
+ BESCHREIBUNG:
+    Setzt die bevorzugte Fluchtrichtung fuer den Vorsichtsmodus.
+
+    Diese Richtung wird bei der Flucht mit einer gewissen Wahrscheinlichkeit
+    als erstes ausprobiert, falls moeglich. Die Wahrscheinlichkeit betraegt
+    60% bei Stufe 30 und nimmt mit jeder weiteren Stufe um ca. 1,3 Prozent-
+    punkte zu.
+
+    Falls die Flucht in diese Richtung nicht gelingt, werden die anderen
+    Richtungen wie bisher ausprobiert.
+
+ BEISPIELE:
+    Wenn man bevorzugt nach Osten fliehen will:
+
+    > fluchtrichtung osten
+        oder
+    > fluchtrichtung o
+
+    Wenn man "traditionell" fluechten will, dies aber kommentieren moechte,
+    kann man auch folgendes nehmen:
+
+    > fluchtrichtung sag Oh, shit!
+
+ SIEHE AUCH:
+    vorsicht, report
+
+ LETZTE AeNDERUNG:
+    Mit, 07.01.2004,  von Rikus
diff --git a/doc/scmd/hausbau b/doc/scmd/hausbau
new file mode 100644
index 0000000..eae04f7
--- /dev/null
+++ b/doc/scmd/hausbau
@@ -0,0 +1,40 @@
+
+Allgemeines zum Hausbau
+=======================
+
+ ALLGEMEIN:
+    Um ein Haus zu bauen, muss man sich in die Filiale der MorgenGrauen-Bank
+    (Der Bank in Ihrem Ruecken) begeben und am Antragschalter einen
+    Bausparvertrag erwerben. Hat man diesen unterzeichnet (ebenfalls am
+    Antragschalter), kann man sich an die Finanzierung seines Hauses begeben.
+    Alles weitere hierzu ist im Vertrag beschrieben.
+
+    Solltest Du es Dir dann doch noch einmal anders ueberlegen, kannst Du den
+    Vertrag jederzeit zerreissen. Schon eingezahlte Raten sind allerdings
+    unwiderruflich verloren!
+
+    Hast Du die Finanzierungsphase hinter Dich gebracht, kannst Du Dir am
+    Ausgabeschalter ein Instanthaus abholen. Dann kannst Du Dich auf die Suche
+    nach einem geeigneten Bauplatz machen. Das weitere Vorgehen ist mit "hilfe
+    instanthaus" erfahrbar.
+
+    Soll das Haus spaeter um neue Raeume erweitert werden, musst Du Dich
+    wieder zum Antragschalter begeben. Nun musst Du jedoch einen Ausbauvertrag
+    beantragen. Das Sparen erfolgt auf die gleiche Weise wie beim Hausbau,
+    allerdings wird der Ausbau nach Aufbringen der Summe ohne eigenes Zutun
+    vorgenommen (es gibt also keinen Instantraum).
+
+    Man kann neun weitere Raeume dazukaufen, so dass man maximal ueber ein
+    10-Zimmer-Haus verfuegen kann.
+
+    Es ist allerdings auch moeglich, sein Haus mit einem Nachbarhaus zu
+    verbinden (natuerlich nur in gegenseitigem Einverstaendnis). Naeheres
+    hierzu erfaehrt man mit "hilfe ausgang".
+
+ SIEHE AUCH:
+    instanthaus, seherhaus,
+    aendere, ausgang, beschreibe, erlaube, kopiere, licht, loesche, meldungen,
+    notiz, schiebe, sperre, spion, uebersicht, verbiete, werfe
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/instanthaus b/doc/scmd/instanthaus
new file mode 100644
index 0000000..e3d2f2b
--- /dev/null
+++ b/doc/scmd/instanthaus
@@ -0,0 +1,31 @@
+
+Allgemeines zum Instanthaus
+===========================
+
+ ALLGEMEIN:
+    Das Instanthaus ist die Keimzelle Deines Seherhauses. Wenn Du meinst, eine
+    geeignete Stelle gefunden zu haben, kannst Du es fallenlassen und giessen,
+    damit es zu einem richtigen Haus wird.
+
+    Wenn die Stelle nicht als Bauplatz gekennzeichnet ist, bekommst Du eine
+    Meldung, an welchen Magier Du Dich wenden kannst. Dieser entscheidet dann,
+    ob Du dort bauen darfst oder ob Du Dir besser eine andere Stelle suchen
+    solltest.
+
+ KOMMANDOS:
+    Das Instanthaus stellt folgende zwei Befehle zur Verfuegung:
+
+     lass haus fallen
+        Du laesst das Haus fallen. Das geht allerdings nur "draussen" und nur,
+        wenn Deine Umgebung als Bauplatz gekennzeichnet ist. Du kannst es
+        probehalber fallenlassen, um herauszufinden, an wen Du Dich fuer die
+        Kennzeichnung wenden musst.
+     giesse haus
+        Wenn Du das Haus erfolgreich abgestellt hast, kannst Du es durch das
+        Giessen zu einem "richtigen" Haus erwecken.
+
+ SIEHE AUCH:
+    hausbau, seherhaus, regionen
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/kopiere b/doc/scmd/kopiere
new file mode 100644
index 0000000..c167780
--- /dev/null
+++ b/doc/scmd/kopiere
@@ -0,0 +1,72 @@
+
+kopiere
+-------
+
+ SEHERHAUSKOMMANDO:
+    kopiere detail <det1> nach <det2>
+    kopiere lesbares detail <det1> nach <det2>
+    kopiere befehl <bef1> nach <bef2>
+    kopiere befehl <bef1> <para1> nach <bef2>
+    kopiere befehl <bef1> <para1> nach <bef2> <para2>
+
+ ARGUMENTE:
+
+     <det1>
+        (lesbares) Detail, dessen Text kopiert werden soll
+     <det2>
+        Liste der zu kopierenden (lesbaren) Details. Bei mehreren Details
+        muessen diese durch Kommata voneinander getrennt sein!
+     <bef1>
+        Zu kopierender Befehl. Dies darf nur ein einzelnes Verb sein!
+     <bef2>
+        Verb oder Liste von Verben fuer den Zielbefehl.
+     <para1>
+        Parameter, die den zu kopierenden Befehl genauer angeben.
+     <para2>
+        Parameter, die den Zielbefehl genauer angeben.
+
+ BESCHREIBUNG:
+
+     kopiere detail <det1> nach <det2>
+     kopiere lesbares detail <det1> nach <det2>
+        Dieser Befehl beschreibt das (lesbare) Detail <det2> mit dem Text von
+        <det1>. Sehr nuetzlich, wenn man nachtraeglich ein Detail mit einem
+        weiteren Namen ansprechbar machen will und keine Lust hat, den Text
+        nochmal zu tippen.
+
+     kopiere befehl <bef1> nach <bef2>
+        Kopiert den Befehl <bef1> zu dem Befehl <bef2> mit allen Parametern.
+        Wenn es <bef2> schon gab, so wird dessen Parameterliste um neue
+        Parameter ergaenzt.
+
+     kopiere befehl <bef1> <para1> nach <bef2>
+        Kopiert nur den angegebenen Parameter von <bef1> nach <bef2>.
+
+     kopiere befehl <bef1> <para1> nach <bef2> <para2>
+        Wie oben, allerdings kann der Parameter auch geaendert werden.
+
+ BEISPIELE:
+    Das Detail "wand" soll sich auch als "waende" ansprechen lassen:
+
+    > kopiere detail wand nach waende
+
+    Alle "drueck"-Befehle sollen sich auch mit "druecke" ansprechen lassen:
+
+    > kopiere befehl drueck nach druecke
+
+    Statt "druecke hebel" soll man jetzt auch "ziehe hebel" verwenden koennen.
+    "druecke knopf" soll jedoch nicht durch "ziehe knopf" ergaenzt werden:
+
+    > kopiere befehl druecke hebel nach ziehe
+        oder
+    > kopiere befehl druecke hebel nach ziehe hebel
+
+    Der Text von "druecke hebel" wird jetzt auch bei "ziehe faden" ausgegeben:
+
+    > kopiere befehl druecke hebel nach ziehe faden
+
+ SIEHE AUCH:
+    aendere, beschreibe, loesche, uebersicht
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/licht b/doc/scmd/licht
new file mode 100644
index 0000000..e3c576b
--- /dev/null
+++ b/doc/scmd/licht
@@ -0,0 +1,20 @@
+
+licht
+-----
+
+ SEHERHAUSKOMMANDO:
+    licht an | aus
+
+ BESCHREIBUNG:
+    "licht aus" verdunkelt den Raum. Dabei wird der Lichtlevel des Raumes so
+    gesetzt, dass der Gesamtlichtlevel (berechnet sich aus dem des Raumes und
+    dem aller anwesenden Lichtquellen) gerade 0 ergibt.
+
+    "licht an" macht gerade das Gegenteil: der Lichtlevel des Raumes wird so
+    gesetzt, dass der Raum gerade einen Gesamtlichtlevel von 1 hat.
+
+ SIEHE AUCH:
+    beschreibe
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/loesche b/doc/scmd/loesche
new file mode 100644
index 0000000..c70b063
--- /dev/null
+++ b/doc/scmd/loesche
@@ -0,0 +1,64 @@
+
+loesche
+-------
+
+ SEHERHAUSKOMMANDO:
+    loesche detail <det>
+    loesche lesbares detail <det>
+    loesche befehl <bef>
+    loesche alles
+    loesche alle <was>
+
+ ARGUMENTE:
+
+     <det>
+        Liste der zu loeschenden (lesbaren) Details.
+     <bef>
+        Liste der zu loeschenden Befehle.
+     <was>
+        `befehle', `details' oder `lesbare details'
+
+ BESCHREIBUNG:
+    Dieser Befehl dient zum Loeschen von Details und Befehlen. Die einzelnen
+    Anwendungen:
+
+     loesche detail <det>
+     loesche lesbares detail <det>
+        Loescht das (lesbare) Detail <det>. Es koennen auch mehrere (lesbare)
+        Details auf einmal geloescht werden; man muss sie dann mit Kommata
+        voneinander trennen.
+
+     loesche befehl <bef>
+        Loescht den Befehl <bef>. Wenn es sich bei <bef> um ein einfaches Verb
+        handelt, werden alle Befehle zu diesem Verb geloescht. Handelt es sich
+        um ein Verb mit Parameter, so wird nur dieser ganz spezielle Befehl
+        geloescht.
+
+     loesche alles
+        Es werden ALLE Details, lesbaren Details und Befehle auf einen Schlag
+        geloescht. Dazu muss man noch die nachfolgende Sicherheitsabfrage mit
+        `ja' beantworten.
+
+     loesche alle [befehle|details|lesbaren details]
+        Es werden alle Beschreibungen der jeweiligen Sparte geloescht. Auch
+        hier gibt es eine Sicherheitsabfrage, die mit `ja' beantwortet werden
+        muss.
+
+ BEISPIELE:
+    Die Details `wand' und `waende' sollen geloescht werden:
+
+    > loesche detail wand, waende
+
+    Alle druecke-Befehle sollen geloescht werden:
+
+    > loesche befehl druecke
+
+    Nur die Befehle druecke knopf und druecke platte sollen geloescht werden:
+
+    > loesche befehl druecke knopf, druecke platte
+
+ SIEHE AUCH:
+    aendere, beschreibe
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/meldungen b/doc/scmd/meldungen
new file mode 100644
index 0000000..812ff72
--- /dev/null
+++ b/doc/scmd/meldungen
@@ -0,0 +1,28 @@
+
+meldungen
+---------
+
+ SEHERHAUSKOMMANDO:
+    meldungen
+    meldungen <nr>
+    meldungen hier
+
+ ARGUMENTE:
+
+     <nr>
+        Eine Raumnummer Deines Hauses
+
+ BESCHREIBUNG:
+    "meldungen" gibt die eingegangenen Rueckmeldungen von Spielern in Deinem
+    Haus aus (also Typo-, Idee- und Fehlermeldungen).
+
+    "meldungen <nr>" gibt nur die Meldungen aus Raum <nr> aus.
+
+    "meldungen hier" gibt nur die Meldungen aus dem Raum aus, in dem Du gerade
+    stehst.
+
+ SIEHE AUCH:
+    aendere, beschreibe
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/notiz b/doc/scmd/notiz
new file mode 100644
index 0000000..230ce0e
--- /dev/null
+++ b/doc/scmd/notiz
@@ -0,0 +1,31 @@
+
+notiz
+-----
+
+ SEHERHAUSKOMMANDO:
+    notiz <text>
+
+ ARGUMENTE:
+
+     <text>
+        Eine kurze Mitteilung
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kannst Du eine kurze Notiz an der Haustuer anbringen.
+    Aus diesem Grund funktioniert dieser Befeh auch nur aussen!
+
+    Ohne Parameter wird die aktuelle Notiz geloescht.
+
+    Die Notiz wird in der Form
+
+    An der Tuer befindet sich eine Notiz: '<text>'.
+
+    an die Langbeschreibung des Hauses angehaengt.
+
+    Die Notiz wird uebrigens nicht mit gespeichert.
+
+ SIEHE AUCH:
+    beschreibe
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/presay b/doc/scmd/presay
new file mode 100644
index 0000000..c72ff21
--- /dev/null
+++ b/doc/scmd/presay
@@ -0,0 +1,26 @@
+
+presay
+------
+
+ SEHERKOMMANDO:
+    presay <text>
+
+ ARGUMENTE:
+
+     <text>
+        Der Text fuer das Presay.
+
+ BESCHREIBUNG:
+    Das Presay wird in der Kurzbeschreibung des Charakters vor den Namen
+    gesetzt.
+
+    `presay' ohne <text> loescht das momentan gesetzte Presay.
+
+    Magier koennen erreichen, dass ihr Presay auch bei den
+    Kommunikationsbefehlen mit ausgegeben wird.
+
+ SIEHE AUCH:
+    titel, showpresay (nur Magier)
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/remote b/doc/scmd/remote
new file mode 100644
index 0000000..701328b
--- /dev/null
+++ b/doc/scmd/remote
@@ -0,0 +1,53 @@
+
+remote
+------
+
+ SEHERKOMMANDO (erst nach Bestehen der Kristallkugelquest verfuegbar):
+    remote <spieler> <text>
+    r: <spieler> <text>
+    r; <spieler> <text>
+
+ ARGUMENTE:
+
+     <spieler>
+        Name des Ziels.
+     <text>
+        Ein beliebiger Text.
+
+ BESCHREIBUNG:
+    Der Text <text> wird an den Spieler <spieler> ausgegeben. Dem Text wird
+    dabei der eigene Name vorangestellt, und zwar bei `remote' und `r:' im
+    Nominativ und bei `r;' im Genitiv.
+
+    Die Ausgabe erfolgt in folgender Form:
+
+    <eigener Name> text aus der Ferne.
+
+    Das "aus der Ferne" laesst sich durch den Platzhalter # auch an beliebiger
+    Stelle in <text> einbauen.
+
+    Wenn man statt des "aus der Ferne" lieber ein "in der Ferne" haben moechte,
+    so kann man den Platzhalter ^ verwenden.
+
+    Falls man den Platzhalter selber ausgeben moechte, kann man ihn einfach
+    verdoppeln. Aus ## wird dann # und aus ^^ wird ^.
+
+ BEISPIELE:
+    Wenn Wargon mit seinem Tester Saphis unzufrieden ist, kommt es ab und an
+    zu folgenden unschoenen Szenen:
+
+    Wargons Eingaben:
+    > r; saphis Zorn trifft Dich
+    > r: saphis gibt Dir # einen Tritt
+    > r: saphis aergert sich ^ ueber Dich
+
+    Und die Ausgaben bei Saphis:
+    > Wargons Zorn trifft Dich aus der Ferne.
+    > Wargon gibt Dir aus der Ferne einen Tritt.
+    > Wargon aergert sich in der Ferne ueber Dich.
+
+ SIEHE AUCH:
+    emote, echo, verben
+
+ LETZTE AeNDERUNG:
+    Mon, 02.07.2001, 19:00:00 von Tiamak
diff --git a/doc/scmd/review b/doc/scmd/review
new file mode 100644
index 0000000..fa31665
--- /dev/null
+++ b/doc/scmd/review
@@ -0,0 +1,24 @@
+
+review
+------
+
+ SEHERKOMMANDO:
+    review
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Es werden die Meldungen ausgegeben, die Anwesende erhalten, wenn Du einen
+    Raum betrittst oder verlaesst, sowie die Meldungen, wenn Du einen Gegner
+    mit blossen Haenden angreifst.
+
+    Bei Magiern werden ausserdem noch die Meldungen angezeigt, die von den
+    Befehlen clone und destruct ausgegeben werden.
+
+ SIEHE AUCH:
+    setmin, setmout, setmmin, setmmout, sethands
+    Nur Magier: setcmsg, setdmsg
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/schiebe b/doc/scmd/schiebe
new file mode 100644
index 0000000..ac891aa
--- /dev/null
+++ b/doc/scmd/schiebe
@@ -0,0 +1,27 @@
+
+schiebe
+-------
+
+ SEHERHAUSKOMMANDO:
+    schiebe <truhe> nach <richtung>
+
+ ARGUMENTE:
+
+     <truhe>
+        Ein gueltiger Bezeichner der Truhe
+     <richtung>
+        Die Richtung, in die die Truhe geschoben werden soll.
+
+ BESCHREIBUNG:
+    Mit diesem Befehl kann man seine Truhe innerhalb seines Hauses frei
+    verschieben.
+
+    Der Zielraum muss zum eigenen Haus gehoeren; auch wenn man ein
+    benachbartes Seherhaus mitnutzt, kann die Truhe dort nicht hingeschoben
+    werden!
+
+ SIEHE AUCH:
+    beschreibe
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/seherhaus b/doc/scmd/seherhaus
new file mode 100644
index 0000000..5610607
--- /dev/null
+++ b/doc/scmd/seherhaus
@@ -0,0 +1,58 @@
+
+Allgemeines zu Seherhaeusern
+============================
+
+ ALLGEMEIN:
+    Die eigenen vier Waende! Wer traeumt nicht davon? Eine Insel der
+    Privatsphaere in der laermenden Oeffentlichkeit des MorgenGrauen, ganz
+    nach eigenem Geschmack einricht- und ausbaubar!
+
+    Und wer hat sich nicht schon immer einmal gewuenscht, sich mit `ende'
+    ausloggen zu duerfen, ohne Angst um seine Ausruestung zu haben?
+
+    Ein weiterer Vorteil: Nach dem Ausloggen (also per `ende' oder nach einem
+    Reboot) betritt man beim naechsten Einloggen das Spiel in den eigenen vier
+    Waenden und nicht mehr am Startpunkt seiner Rasse.
+
+ SPEZIELLES:
+    Im Eingangsraum des Hauses befindet sich eine Truhe, in der man wichtige
+    Gegenstaende aufbewahren kann, ohne befuerchten zu muessen, dass sie von
+    zufaellig vorbeischauenden Spielern entwendet werden. Man sollte
+    allerdings nicht vergessen, die Truhe auch wieder zu schliessen, nachdem
+    man Sachen hineingelegt hat... ;^)
+
+    Sowohl Gegenstaende in der geschlossenen Truhe wie auch im abgeschlossenen
+    Haus sind zwar vor fremdem Zugriff geschuetzt, aber nach einem Reboot wie
+    ueblich verloren (und wie man leicht einsehen kann, gibt es dafuer auch
+    KEINE Entschaedigung!).
+
+    Erwirbt man im Laufe der Zeit weitere Raeume, um sein Haus auszubauen, so
+    wird die Numerierung der Raeume wichtig:
+    Der Eingangsraum hat die Nummer 0, die weiteren Raeume sind fortlaufend
+    von 1 bis 9 durchnumeriert.
+    Die Nummer des Raumes, in dem man sich gerade befindet, wird bei dem
+    Befehl `uebersicht' angezeigt.
+
+    Wenn man das Haus abgeschlossen hat, kann es niemand mehr betreten. Es ist
+    allerdings NICHT moeglich, jemanden im Haus einzusperren!
+
+    Beschreibungen etc. sind nur im *eigenen* Haus moeglich! Die einzige
+    Aenderung, die man an einem anderen Haus vornehmen kann, ist das Schaffen
+    einer Verbindung (siehe dazu das `ausgang'-Kommando).
+
+    Typo- und Ideemeldungen, die in Deinem Haus von anderen Spielern abgesetzt
+    werden, landen in einer Reportdatei, die Du mit dem Befehl `meldungen'
+    ansehen und mit dem `aendere'-Befehl bearbeiten kannst.
+
+    Jeder Raum des Seherhauses darf maximal 100 einzelne Objekte beinhalten.
+    Dabei gibt es zwei Unterscheidungen:
+       345 Muenzen.         --> Das ist ein Objekt.
+       Ein Titanring. (4)   --> Das sind 4 Objekte.
+
+ SIEHE AUCH:
+    aendere, ausgang, beschreibe, erlaube, hausbau, instanthaus, kopiere,
+    licht, loesche, meldungen, notiz, schiebe, sperre, spion, uebersicht,
+    verbiete, werfe
+
+ LETZTE AeNDERUNG:
+    Thu, 21.07.1997, 20:00:00 von Zook.
diff --git a/doc/scmd/sethands b/doc/scmd/sethands
new file mode 100644
index 0000000..bcc62fb
--- /dev/null
+++ b/doc/scmd/sethands
@@ -0,0 +1,25 @@
+
+sethands
+--------
+
+ SEHERKOMMANDO:
+    sethands <text>
+
+ ARGUMENTE:
+
+     <text>
+        Der Text, der beim Kaempfen mit "den blossen Haenden" erscheinen soll.
+
+ BESCHREIBUNG:
+    Normalerweise erscheint beim Kaempfen ohne Waffen eine Meldung:
+
+    >  Du greifst <Gegner> mit blossen Haenden an.
+                           ^=================^
+
+    Diesen Text kann man durch <text> ersetzen.
+
+ SIEHE AUCH:
+    review
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/setmin b/doc/scmd/setmin
new file mode 100644
index 0000000..69f87a0
--- /dev/null
+++ b/doc/scmd/setmin
@@ -0,0 +1,24 @@
+
+setmin
+------
+
+ SEHERKOMMANDO:
+    setmin [<text>]
+
+ ARGUMENTE:
+
+     <text> (optional)
+        Die neue Eingangsmeldung.
+
+ BESCHREIBUNG:
+    Mit diesem Kommando kann die Meldung definiert werden, die andere Spieler
+    sehen, wenn man in den Raum hineingeht. Dabei wird dem Text <text> noch
+    Dein Name vorangestellt.
+
+    Ohne <text> wird wieder die Defaultmeldung aktiv.
+
+ SIEHE AUCH:
+    setmout, setmmin, setmmout, review
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/setmmin b/doc/scmd/setmmin
new file mode 100644
index 0000000..408141c
--- /dev/null
+++ b/doc/scmd/setmmin
@@ -0,0 +1,24 @@
+
+setmmin
+-------
+
+ SEHERKOMMANDO:
+    setmmin [<text>]
+
+ ARGUMENTE:
+
+     <text> (optional)
+        Die neue Eingangsmeldung.
+
+ BESCHREIBUNG:
+    Mit diesem Kommando kann die Meldung definiert werden, die andere Spieler
+    sehen, wenn man in den Raum teleportiert wird. Dabei wird dem Text <text>
+    noch Dein Name vorangestellt.
+
+    Ohne <text> wird auf die Defaultmeldung zurueckgeschaltet.
+
+ SIEHE AUCH:
+    setmin, setmout, setmmout, review
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/setmmout b/doc/scmd/setmmout
new file mode 100644
index 0000000..9fa1567
--- /dev/null
+++ b/doc/scmd/setmmout
@@ -0,0 +1,24 @@
+
+setmmout
+--------
+
+ SEHERKOMMANDO:
+    setmmout [<text>]
+
+ ARGUMENTE:
+
+     <text> (optional)
+        Die neue Ausgangsmeldung.
+
+ BESCHREIBUNG:
+    Mit diesem Kommando kann die Meldung definiert werden, die andere Spieler
+    sehen, wenn man aus dem Raum teleportiert wird. Dabei wird dem Text <text>
+    noch Dein Name vorangestellt.
+
+    Laesst man <text> weg, wird wieder die Defaultmeldung verwendet.
+
+ SIEHE AUCH:
+    setmout, setmmin, setmin, review
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/setmout b/doc/scmd/setmout
new file mode 100644
index 0000000..be7614a
--- /dev/null
+++ b/doc/scmd/setmout
@@ -0,0 +1,27 @@
+
+setmout
+-------
+
+ SEHERKOMMANDO:
+    setmout [<text>]
+
+ ARGUMENTE:
+
+     <text> (optional)
+        Die neue Ausgangsmeldung.
+
+ BESCHREIBUNG:
+    Mit diesem Kommando kann die Meldung definiert werden, die andere Spieler
+    sehen, wenn man aus dem Raum herausgeht. Dabei wird dem Text <text> noch
+    Dein Name vorangestellt.
+
+    Enthaelt <text> ein #, so wird dieses Zeichen in der Meldung durch die
+    jeweilige Richtung ersetzt.
+
+    Ohne <text> wird wieder die Defaultmeldung verwendet.
+
+ SIEHE AUCH:
+    setmmout, setmin, setmmin, review
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/sperre b/doc/scmd/sperre
new file mode 100644
index 0000000..804512c
--- /dev/null
+++ b/doc/scmd/sperre
@@ -0,0 +1,22 @@
+
+sperre
+------
+
+ SEHERHAUSKOMMANDO:
+    sperre <richtung>
+
+ ARGUMENTE:
+
+     <richtung>
+        Eine der acht Himmelsrichtungen, "oben" oder "unten".
+
+ BESCHREIBUNG:
+    Der Ausgang, der in die angegebene Richtung fuehrt, wird entfernt. Der
+    entsprechende Ausgang vom Zielraum zurueck verschwindet dabei automatisch
+    mit.
+
+ SIEHE AUCH:
+    ausgang, uebersicht
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/spion b/doc/scmd/spion
new file mode 100644
index 0000000..1390e77
--- /dev/null
+++ b/doc/scmd/spion
@@ -0,0 +1,22 @@
+
+spion
+-----
+
+ SEHERHAUSKOMMANDO:
+    spion
+
+ ARGUMENTE:
+    keine
+
+ BESCHREIBUNG:
+    Der Tuerspion: Dieses Kommando zeigt, wer oder was sich vor Deiner
+    Haustuer tummelt. Recht nuetzlich, wenn jemand anklopft und man sich
+    vergewissern will, ob man ihn auch einlassen will.
+
+    Dieses Kommando ist nur im Eingangsraum verfuegbar.
+
+ SIEHE AUCH:
+    seherhaus
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/titel b/doc/scmd/titel
new file mode 100644
index 0000000..ff00511
--- /dev/null
+++ b/doc/scmd/titel
@@ -0,0 +1,29 @@
+
+titel
+-----
+
+ SEHERKOMMANDO:
+    titel <text>
+    titel 0
+
+ ARGUMENTE:
+
+     <text>
+        Der Titeltext.
+
+ BESCHREIBUNG:
+    Der Text wird als neuer Titel gesetzt. Dabei sollte der Titel nicht mit
+    einem Spielertitel identisch oder einem solchen sehr aehnlich sein. Der
+    uebergebene Text darf ein Backspace (\b) am Anfang enhalten, wenn
+    darauf ein Komma (,) oder ein Hochkomma (') folgt.
+
+    Wird kein Argument uebergeben, so wird der aktuelle Titel geloescht.
+
+    Wird 0 als Argument uebergeben, so wird der Titel durch den aktuellen
+    Gildentitel ersetzt.
+
+ SIEHE AUCH:
+    stufen, presay
+
+ LETZTE AeNDERUNG:
+    Sat, 30.09.2000, 01:34:00 von Bierchen
diff --git a/doc/scmd/uebersicht b/doc/scmd/uebersicht
new file mode 100644
index 0000000..6e30063
--- /dev/null
+++ b/doc/scmd/uebersicht
@@ -0,0 +1,25 @@
+
+uebersicht
+----------
+
+ SEHERHAUSKOMMANDO:
+    uebersicht
+
+ ARGUMENTE:
+    uebersicht [erlaubt]
+
+ BESCHREIBUNG:
+    Dieses Kommando zeigt, in welchem Raum seines Hauses man sich gerade
+    befindet, welche Details und lesbaren Details man hier beschrieben hat,
+    und welche Ausgaenge wohin fuehren.
+
+    Steht man im Eingangsraum seines Hauses, wird zusaetzlich die Liste
+    derjenigen Leute ausgegeben, die das Haus und die Truhe oeffnen bzw.
+    (ab)schliessen duerfen. Gibt man im Eingangsraum den Parameter "erlaubt"
+    an, wird nur diese Liste ausgegeben.
+
+ SIEHE AUCH:
+    beschreibe, loesche, kopiere, ausgang, sperre, erlaube, verbiete
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/verbiete b/doc/scmd/verbiete
new file mode 100644
index 0000000..a7e88c4
--- /dev/null
+++ b/doc/scmd/verbiete
@@ -0,0 +1,23 @@
+
+verbiete
+--------
+
+ SEHERHAUSKOMMANDO:
+    verbiete <name>
+
+ ARGUMENTE:
+
+     <name>
+        Ein oder mehrere Name(n) (durch Kommata getrennt).
+
+ BESCHREIBUNG:
+    Dem Spieler <name> wird die Erlaubnis, Dein Haus auf- und abschliessen
+    sowie die Truhe oeffnen zu duerfen, wieder entzogen.
+
+    Dieser Befehl ist nur im Eingangsraum des Hauses verfuegbar!
+
+ SIEHE AUCH:
+    erlaube, uebersicht
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/scmd/werfe b/doc/scmd/werfe
new file mode 100644
index 0000000..154041d
--- /dev/null
+++ b/doc/scmd/werfe
@@ -0,0 +1,33 @@
+
+werfe
+-----
+
+ SEHERHAUSKOMMANDO:
+    werfe <was> raus
+    werfe alle raus
+    werfe alles raus
+
+ ARGUMENTE:
+
+     <was>
+        Das aus dem Haus zu werfende Objekt.
+
+ BESCHREIBUNG:
+    Wenn Dir in Deinem Haus jemand fuerchterlich auf die Nerven geht, kannst
+    Du ihn mit diesem Kommando vor die Haustuer komplimentieren.
+
+    Falls Dir alle Anwesenden auf die Nerven gehen, kannst Du sie auch alle
+    auf einen Schlag entsorgen.
+
+    Die dritte Alternative dagegen dient dazu, saemtliche leblosen
+    Gegenstaende aus dem Haus zu werfen (obwohl das Anlegen wilder Muellkippen
+    vor dem Haus nicht gerade zum guten Ton gehoert ;)
+
+    Das Kommando wirkt uebrigens nur in dem Raum, in dem es angewendet wird,
+    und nicht etwa im ganzen Haus.
+
+ SIEHE AUCH:
+    seherhaus
+
+ LETZTE AeNDERUNG:
+    Thu, 24.07.1997, 09:00:00 von Wargon
diff --git a/doc/sefun/CountUp b/doc/sefun/CountUp
new file mode 100644
index 0000000..5515780
--- /dev/null
+++ b/doc/sefun/CountUp
@@ -0,0 +1,25 @@
+FUNKTION:
+	public varargs string CountUp( string *s, string sep, string lastsep );
+
+ARGUMENTE:
+	*s
+	  Array von Strings mit Woertern.
+  sep
+    String, der zwischen den Woerten in der Aufzaehlung eingefuegt wird.
+    Standard: ", "
+  lastsep
+    String, der zwischen dem vorletzten und letzten Element eingefuegt wird.
+    Standard: " und "
+
+BESCHREIBUNG:
+	Die Woerter Wort_1 bis Wort_n aus dem Stringaray werden als
+	Aufzaehlung in der Form
+	  Wort_1<sep>Wort_2, ... Wort_n-1<lastsep>Wort_n
+	zusammengesetzt. Mit Standardwerten also:
+	  Wort_1, Wort_2, ... Wort_n-1 und Wort_n
+
+RUeCKGABEWERT:
+	String als Aufzaehlung der einzelnen Woerter.
+
+----------------------------------------------------------------------------
+15.03.2008, Zesstra
diff --git a/doc/sefun/_notify_fail b/doc/sefun/_notify_fail
new file mode 100644
index 0000000..84aa4de
--- /dev/null
+++ b/doc/sefun/_notify_fail
@@ -0,0 +1,18 @@
+simul_efun::_notify_fail(E)
+FUNKTION:
+     void _notify_fail(string str)
+
+ARGUMENTE:
+     str
+	umgebrochener Fehlermeldungsstring
+
+BESCHREIBUNG:
+     Identisches Verhalten zu notify_fail(E), bis darauf, dass bei bereits
+     gesetzter Fehlermeldung _keine_ neue Fehlermeldung gesetzt wird.
+
+SIEHE AUCH:
+     notify_fail(E), P_ACTUAL_NOTIFY_FAIL
+     add_action(E), AddCmd(L), AddAction(L),
+     query_verb(E)
+
+24 Maerz 2004 Gloinson
diff --git a/doc/sefun/break_string b/doc/sefun/break_string
new file mode 100644
index 0000000..52d4d7b
--- /dev/null
+++ b/doc/sefun/break_string
@@ -0,0 +1,90 @@
+FUNKTION:
+    string break_string(string str)
+    string break_string(string str, int width)
+    string break_string(string str, int width, string indent)
+    string break_string(string str, int width, int space)
+    string break_string(string str, int width, string indent, int flags)
+    string break_string(string str, int width, int space, int flags)
+
+ARGUMENTE:
+    str    - umzubrechender String
+    width  - optional: maximale Zeilenlaenge (default 78)
+    indent - optional: String, der vor jeder umgebrochenen Zeile erscheint
+    space  - optional: Anzahl der Leerzeichen vor jeder umgebrochenen Zeile
+    flags  - optional: hiermit laesst sich das Verhalten von break_string()
+             aendern; moegliche Flags siehe Punkt 'Beschreibung'
+
+BESCHREIBUNG:
+    In der ersten Form wird der String 'str' durch Einfuegen von "\n" so um-
+    gebrochen, dass bei einer anschliessenden Ausgabe keine Zeile laenger
+    als 'width' Zeichen ist. Eventuell schon in 'str' vorhandene "\n" werden
+    dabei vorher entfernt.
+
+    Gibt man zusaetzlich noch einen String 'indent' an, so wird dieser vor
+    jede der umgebrochenen Zeilen gesetzt.
+
+    Analog wird bei der Angabe der Zahl 'space' ein String mit 'space' Leer-
+    zeichen vor jede umgebrochene Zeile gesetzt.
+
+    Zusaetzlich gibt es folgende optionale Flags, die man beliebig kombinieren
+    kann:
+
+        BS_LEAVE_MY_LFS  -  schon im Text vorhandene "\n" werden beibehalten
+        BS_SINGLE_SPACE  -  doppelte Leerzeichen sowie Leerzeichen nach Zeilen-
+                            umbruechen werden entfernt
+        BS_BLOCK         -  der Text wird im Blocksatz formatiert
+        BS_NO_PARINDENT  -  bei Blocksatz mit vorgegebenen Zeilenumbruechen
+                            (BS_BLOCK|BS_LEAVE_MY_LFS) werden Zeilen nach "\n"
+                            normalerweise mit einem Leerzeichen begonnen.
+                            Um das Einfuegen des fuehrenden Leerzeichens zu
+                            unterdruecken, muss BS_NO_PARINDENT angegeben werden
+        BS_INDENT_ONCE   -  die erste Zeile des Textes wird mit vorangestelltem
+                            'indent' ausgegeben; alle folgenden Zeilen bekommen
+                            einen Leerstring vorangestellt
+        BS_PREPEND_INDENT - der Ident wird dem Text vorangestellt sofern der 
+                            Indent + Text laenger als eine Zeile ist. Der Text
+			    wird um ein Leerzeichen eingerueckt, was mittels
+                            BS_NO_PARINDENT verhindert werden kann.
+
+RUECKGABEWERT:
+    Der umgebrochene Text.
+
+    Laufzeit-Fehler, wenn der Indent laenger ist als die vorgegebene Breite.
+
+BEISPIELE:
+    write(break_string("Dies ist ein laengerer Text. Nur so als Beispiel.",27));
+
+        => Dies ist ein laengerer
+           Text. Nur so als Beispiel.
+
+    write(break_string("Mit indent sieht das so aus", 30, "Wargon sagt: "));
+
+        => Wargon sagt: Mit indent sieht
+           Wargon sagt: das so aus
+
+    write(break_string("Mit indent sieht das so aus", 30, "Wargon sagt: ",
+                        BS_INDENT_ONCE));
+
+        => Wargon sagt: Mit indent sieht
+                        das so aus
+
+    write(break_string("Mit Leerzeichen sieht das so aus", 30, 2));
+
+        =>   Mit Leerzeichen sieht das so
+             aus...
+
+    write(break_string("Ich will es\naber vorformatiert!",60,
+                        "Wargon sagt: ", BS_LEAVE_MY_LFS));
+
+        => Wargon sagt: Ich will es
+           Wargon sagt: aber vorformatiert!
+
+    write(break_string("Ich will es\naber vorformatiert!",30,
+                        "Wargon sagt: ", BS_PREPEND_INDENT));
+
+        => Wargon sagt:
+            Ich will es aber 
+            vorformatiert!
+
+SIEHE AUCH:
+    senderwiederholung
diff --git a/doc/sefun/broken_count_bits b/doc/sefun/broken_count_bits
new file mode 100644
index 0000000..09e56b8
--- /dev/null
+++ b/doc/sefun/broken_count_bits
@@ -0,0 +1,29 @@
+SYNOPSIS
+        int count_bits (string str)
+
+DESTRIPTION
+        Count the number of set bits in bitstring <str> and return the number
+        as result.
+
+NOTE
+	Bitstrings store 6 Bits per Character. Consequently, the functions for
+	manipulating bitstrings (see below) do generally not work on most
+	strings. An exception is this (s)efun. It accepts strings which are
+	not correct bitstrings (like getuid(PL)), BUT: It does NOT work
+	correctly on them! The results are NOT the correct number of bits!
+	Additionally, count_bits() in LDMud rejects such strings with an error
+	instead of returning false results, as all the other functions for
+	bitstrings do as well.
+
+EXAMPLES
+        string s;
+
+        s = set_bit("", 3); s = set_bit(s, 15);
+
+        count_bits(s) --> returns 2
+
+SEE ALSO
+        clear_bit(E), set_bit(E), test_bit(E), next_bit(E), last_bit(E),
+        or_bits(E), xor_bits(E), invert_bits(E), copy_bits(E)
+
+19.12.2006, Zesstra
diff --git a/doc/sefun/cindent b/doc/sefun/cindent
new file mode 100644
index 0000000..72119b3
--- /dev/null
+++ b/doc/sefun/cindent
@@ -0,0 +1,9 @@
+SYNOPSIS:
+        int cindent(string file)
+
+DESCRIPTION:
+        Indent a file using an LPC-enhanced version of the GNU indent
+	program, which is modelled after the Berkeley indent(1).
+
+SEE ALSO:
+	ed(E)
diff --git a/doc/sefun/debug_info b/doc/sefun/debug_info
new file mode 100644
index 0000000..18747b0
--- /dev/null
+++ b/doc/sefun/debug_info
@@ -0,0 +1,490 @@
+DEPRECATED
+SYNOPSIS
+        #include <debug_info.h>
+
+        mixed debug_info(int flag)
+        mixed debug_info(int flag, mixed arg)
+        mixed debug_info(int flag, mixed arg2, mixed arg3)
+
+BESCHREIBUNG
+        Sammelt entsprechend den Angaben in <flag> gewisse intere Debuginfos
+        des Treibers. <flag> kann dabei folgende in debug_info.h definierte
+        Werte enthalten:
+
+        DINFO_OBJECT   (0): Angezeigt werden Informationen zum in <arg>
+            spezifizierten Objekt, zum Beispiel heart_beat,
+            enable_commands etc. Die Funktion liefert 0 zurueck.
+
+        DINFO_MEMORY   (1): Angezeigt werden Informationen zu
+            Speicherbelegung und -ausnutzung des in <arg> spezifizierten
+            Objekts, zum Beispiel Anzahl Strings, Variablen, geerbte Files,
+            Objektgroesse etc. Die Funktion liefert 0 zurueck.
+
+        DINFO_OBJLIST  (2): debug_info() liefert Objekte aus der globalen
+            Objektliste. Wenn <arg2> nicht angegeben wird, wird das erste
+            Element aus der Objektliste gelierfert, wenn <arg2> eine Zahl n
+            ist, das n-te Element. Ist <arg2> ein Objekt, werden die
+            nachfolgenden Objekte in der Objektliste zurueck geliefert.
+            Das optionale Argument <arg3> bezeichnet die Anzahl zurueck
+            gelieferter Objekte. Wenn <arg3> 0 ist, wird ein einzelnes
+            Objekt zurueck geliefert. Wenn <arg3> eine Zahl m enthaelt, wird
+            ein Array mit hoechstens m Elementen zurueck geliefert. Auf
+            diese Weise kann ein Array mit saemtlichen Objekten im Spiel
+            erzeugt werden, wenn fuer <arg3> __INT_MAX__ gesetzt wird (eine
+            entsprechende maximale Arraygroesse vorausgesetzt).
+
+        DINFO_MALLOC   (3): Entsprichend der Eingabe des 'malloc'-Kommandos.
+            Es muessen keine weiteren Argumente angegeben werden.
+
+        DINFO_STATUS   (4): Angezeigt wird die Statusinformation des Drivers.
+            Optional kann das Argument <arg> die Werte 0, "tables", "swap",
+            "malloc" oder andere vom Driver akzeptierte Argumente enthalten.
+            Das Resultat ist ein druckbarer String, der die Statusinformation
+            enthaelt, oder 0, wenn ein ungueltiges Argument angegeben wurde.
+
+        DINFO_DUMP     (5): Die durch <arg2> angeforderte Information wird
+            in ein File geschrieben, das man mit <arg3> angeben kann. Wird
+            <arg3> nicht angegeben, wird eine Standarddatei verwendet.
+            debug_info() ueberprueft mittels master->valid_write(), ob es
+            das File schreiben kann. Falls bereits eine entsprechende Datei
+            existiert, wird diese ueberschrieben. Die Funktion liefert 1
+            bei Erfolg, 0 sonst.
+
+            <arg2> == "objects": liefert Informationen ueber alle Objekte im
+                Spiel und schreibt diese standardmaessig in die Datei
+                /OBJ_DUMP, dem valid_write() wird 'objdump' uebergeben.
+                Die Datei enthaelt fuer jedes Objekt eine Zeile, in der
+                jeweils folgende Informationen aufgelistet sind:
+                  - Name des Objekts (object_name)
+                  - Groesse im Speicher, gemeinsamer genutzter Speicher nur
+                    einmal gezaehlt
+                  - Groesse im Speicher, wenn es keine gemeinsam genutzte
+                    Daten geben wuerde
+                  - Anzahl Referenzen
+                  - 'HB', wenn das Objekt einen heart_beat hat, sonst nichts.
+                  - der Name der Umgebung oder '--', wenn das Objekt keine
+                    Umgebung hat
+                  - in Klammern die Anzahl der durch das Objekt verursachten
+                    Verarbeitungsschritten (execution ticks)
+                  - der Swap-Status:
+                      > nichts, wenn das Objekt nicht geswapt wurde
+                      > 'PROG SWAPPED', wenn nur das Programm geswapt wurde
+                      > 'VAR SWAPPED', wenn nur die Variablen geswapt wurden
+                      > 'SWAPPED', wenn beide geswapt wurden
+                  - die Zeit, zu der das Objekt geladen wurde.
+
+            <arg2> == "destructed": liefert Informationen ueber alle
+                zerstoerten Objekte und schreibt diese standardmaessig in
+                die Datei /DEST_OBJ_DUMP, dem valid_write() wird 'objdump'
+                uebergeben. Die Datei enthaelt fuer jedes Objekt eine Zeile,
+                in der jeweils folgende Informationen aufgelistet sind:
+                  - Name des Objekts (object_name)
+                  - Anzahl der Referenzen
+                  - 'NEW', wenn das Objekt in diesem Verarbeitungszyklus
+                    zerstoert wurde, nichts wenn es bereits fruehre zerstoert
+                    worden war.
+
+            <arg2> == "opcodes": liefert Nutzungsinformationen ueber die
+                opcodes. Standardmaessig wird in die Datei /OPC_DUMP
+                geschrieben. valid_write() wird 'opcdump' uebergeben.
+
+            <arg2> == "memory": liefert eine Liste aller allokierten
+                Speicherbloecke.
+                Standardmaessig wird in die Datei /MEMORY_DUMP geschrieben;
+                valid_write() wird 'memdump' uebergeben.  Existiert die
+                Datei bereits, werden die neuen Daten angehaengt.
+                Wenn der Allokator einen Speicherabzug nicht unterstuetzt,
+                wird keine Datei geschrieben und immer 0 zurueckgegeben.
+                Diese Funktion ist am nuetzlichsten wenn der Allokator
+                mit MALLOC_TRACE und MALLOC_LPC_TRACE kompiliert
+                wurde.
+
+
+        DINFO_DATA    (6): Liefert Rohdaten ueber gewisse, durch <arg2>
+            spezifizierte Aspekte des Treibers. Das Resultat der Funktion ist
+            ein Array mit der Information oder 0, falls <arg2> keinen
+            gueltigen Wert enthalten hat.
+            Ist <arg3> eine Zahl, die kleiner ist als die Anzahl Elemente im
+            Resultat-Array, liefert die Funktion nur das entsprechende
+            Element zurueck.
+            Zulaessige Werte fuer <arg2> sind: DID_STATUS, DID_SWAP und
+            DID_MALLOC.
+
+            <arg2> == DID_STATUS (0): Liefert die "status" und "status table"
+                Information. Folgende Indizes sind definiert:
+                  - int DID_ST_BOOT_TIME
+                        die Zeit (time()), zu der das Mud gestartet wurde
+                  - int DID_ST_ACTIONS
+                  - int DID_ST_ACTIONS_SIZE
+                        die Anzahl und Groesse im Speicher der allozierten
+                        Actions.
+                  - int DID_ST_SHADOWS
+                  - int DID_ST_SHADOWS_SIZE
+                        Anzahl und Groesse im Speicher aller allozierten
+                        Shadows.
+                  - int DID_ST_OBJECTS
+                  - int DID_ST_OBJECTS_SIZE
+                        Anzahl und Groesse im Speicher aller Objekte.
+                  - int DID_ST_OBJECTS_SWAPPED
+                  - int DID_ST_OBJECTS_SWAP_SIZE
+                        Anzahl und Groesse im Speicher der geswapten
+                        Variablenbloecke der Objekte
+                  - int DID_ST_OBJECTS_LIST
+                        Anzahl Objekte in der Objektliste
+                  - int DID_ST_OBJECTS_NEWLY_DEST
+                        Anzahl der frisch zerstoerten Objekte (d.h. Objekte,
+                        die in diesem Verarbeitungszyklus zerstoert wurden)
+                  - int DID_ST_OBJECTS_DESTRUCTED
+                        Anzahl der zerstoerten, aber noch immer referenzierten
+                        Objekte, ohne die unter DID_ST_OBJECTS_NEWLY_DEST
+                        bereits gezaehlten.
+                  - int DID_ST_OBJECTS_PROCESSED
+                        Anzahl der gelisteten Objekte, die im letzten
+                        Verarbeitungszyklus verarbeitet wurden.
+                  - float DID_ST_OBJECTS_AVG_PROC
+                        Durchschnittlicher Anteil der pro Zyklus verarbeiteten
+                        Objekte, ausgedrueckt in Prozent (0 .. 1.0)
+                  - int DID_ST_OTABLE
+                        Anzahl eingetragener Objekte in der Objekttabelle
+                  - int DID_ST_OTABLE_SLOTS
+                        Anzahl von Hashplaetzen, die von jeder Objekttabelle
+                        bereitgestellt werden
+                  - int DID_ST_OTABLE_SIZE
+                        Durch die Objekttabelle belegter Speicher
+                  - int DID_ST_HBEAT_OBJS
+                        Anzahl Objekte mit einem heart_beat()
+                  - int DID_ST_HBEAT_CALLS
+                        Anzahl aktiver heart_beat() Zyklen bisher (d.h.
+                        Zyklen, in denen mindestens eine heart_beat() Funktion
+                        aufgerufen wurde)
+                  - int DID_ST_HBEAT_CALLS_TOTAL
+                        Gesamtzahl von heart_beat() Zyklen bisher.
+                  - int DID_ST_HBEAT_SLOTS
+                  - int DID_ST_HBEAT_SIZE
+                        Anzahl und Groesse aller allozierten Eintraege in der
+                        heart_beat() Tabelle.
+                  - int DID_ST_HBEAT_PROCESSED
+                        Anzahl heart_beat()s im letzten Zyklus
+                  - float DID_ST_HBEAT_AVG_PROC
+                        Durchschnittlicher Anteil der pro Zyklus aufgerufenen
+                        heart_beat()s, ausgedrueckt in Prozent (0 .. 1.0)
+                  - int DID_ST_CALLOUTS
+                        Anzahl und Groesse aller laufenden call_out()s
+                  - int DID_ST_ARRAYS
+                  - int DID_ST_ARRAYS_SIZE
+                        Anzahl und Groesse aller Arrays
+                  - int DID_ST_MAPPINGS
+                  - int DID_ST_MAPPINGS_SIZE
+                        Anzahl und Groesse aller Mappings
+                  - int DID_ST_PROGS
+                  - int DID_ST_PROGS_SIZE
+                        Anzahl und Groesse aller Programme
+                  - int DID_ST_PROGS_SWAPPED
+                  - int DID_ST_PROGS_SWAP_SIZE
+                        Anzahl und Groesse der geswapten Programme
+                  - int DID_ST_USER_RESERVE
+                  - int DID_ST_MASTER_RESERVE
+                  - int DID_ST_SYSTEM_RESERVE
+                        Momentane Groesse der drei Speicherreserven
+                  - int DID_ST_ADD_MESSAGE
+                  - int DID_ST_PACKETS
+                  - int DID_ST_PACKET_SIZE
+                        Anzahl Aufrufe von add_message(), Anzahl und Groesse
+                        der versendeten Pakete.
+                        Wenn der Driver nicht mit COMM_STAT kompiliert wurde,
+                        liefern alle drei Werte immer -1 zurueck.
+                  - int DID_ST_APPLY
+                  - int DID_ST_APPLY_HITS
+                        Anzahl Aufrufen von apply_low(), und wie viele davon
+                        Cache-Treffer waren. Wenn der Driver nicht mit
+                        APPLY_CACHE_STAT kompiliert wurde, liefern beide
+                        Werte immer -1.
+                  - int DID_ST_STRINGS
+                  - int DID_ST_STRING_SIZE
+                        Anzahl unterschiedlicher Strings in der Stringtabelle,
+                        sowie ihre Groesse
+                  - int DID_ST_STR_TABLE_SIZE
+                        Groesse der String Tabelle
+                  - int DID_ST_STR_REQ
+                  - int DID_ST_STR_REQ_SIZE
+                        Gesamte Anzahl von String Allokationen, und ihre
+                        Groesse
+                  - int DID_ST_STR_SEARCHES
+                  - int DID_ST_STR_SEARCH_LEN
+                        Anzahl Suchvorgaenge in der Stringtabelle und die
+                        Gesamtlaenge des Suchstrings
+                  - int DID_ST_STR_FOUND
+                        Anzahl erfolgreicher Suchvorgaenge
+                  - int DID_ST_STR_ENTRIES
+                        Anzahl Eintraege (Hash Ketten) in der String Tabelle
+                  - int DID_ST_STR_ADDED
+                        Anzahl zur String Tabelle bisher hinzugefuegter
+                        Strings
+                  - int DID_ST_STR_DELETED
+                        Anzahl aus der String Tabelle bisher geloeschter
+                        Strings
+                  - int DID_ST_STR_COLLISIONS
+                        Anzahl zu einer existierenden Hash Kette hinzugefuegte
+                        Strings
+                  - int DID_ST_RX_CACHED
+                        Anzahl gecacheter regulaerer Ausdruecke (regular
+                        expressions)
+                  - int DID_ST_RX_TABLE
+                  - int DID_ST_RX_TABLE_SIZE
+                        Anzahl Plaetze in der Regexp Cache Tabelle und
+                        Speicherbedarf der gecacheten Ausdruecke
+                  - int DID_ST_RX_REQUESTS
+                        Anzahl Anfragen fuer neue regexps
+                  - int DID_ST_RX_REQ_FOUND
+                        Anzahl gefundener regexps in der regexp Cache Tabelle
+                  - int DID_ST_RX_REQ_COLL
+                        Anzahl angefragter regexps, die mit einer bestehenden
+                        regexp kollidierten
+                  - int DID_ST_MB_FILE
+                        Die Groesse des 'File' Speicherpuffers
+                  - int DID_ST_MB_SWAP
+                        Die Groesse des 'Swap' Speicherpuffers
+
+            <arg2> == DID_SWAP (1): Liefert "status swap"-Information:
+                  - int DID_SW_PROGS
+                  - int DID_SW_PROG_SIZE
+                        Anzahl und Groesse der geswappten Programmbloecke
+                  - int DID_SW_PROG_UNSWAPPED
+                  - int DID_SW_PROG_U_SIZE
+                        Anzahl und Groesse der nicht geswappten Bloecke
+                  - int DID_SW_VARS
+                  - int DID_SW_VAR_SIZE
+                        Anzahl und Groesse der geswappten Variablenbloecke
+                  - int DID_SW_FREE
+                  - int DID_SW_FREE_SIZE
+                        Anzahl und Groesse der freien Bloecke in der
+                        Auslagerungsdatei
+                  - int DID_SW_FILE_SIZE
+                        Groesse der Auslagerungsdatei
+                  - int DID_SW_REUSED
+                        Gesamter wiederverwendeter Speicherplatz in der
+                        Auslagerungsdatei
+                  - int DID_SW_SEARCHES
+                  - int DID_SW_SEARCH_LEN
+                        Anzahl und Gesamtlaenge der Suchvorgaenge nach
+                        wiederverwendbaren Bloecken in der Auslagerungsdatei
+                  - int DID_SW_F_SEARCHES
+                  - int DID_SW_F_SEARCH_LEN
+                        Anzahl und Gesamtlaenge der Suchvorgaenge nach einem
+                        Block, der frei gemacht werden kann.
+                  - int DID_SW_COMPACT
+                        TRUE wenn der Swapper im Compact-Modus laeuft
+                  - int DID_SW_RECYCLE_FREE
+                        TRUE wenn der Swapper gerade einen freien Block
+                        wiederverwendet
+
+            <arg2> == DID_MEMORY (2): Liefert die "status malloc"-Information:
+                  - string DID_MEM_NAME
+                        Der Name des Allokators: "sysmalloc", "smalloc",
+                        "slaballoc"
+                  - int DID_MEM_SBRK
+                  - int DID_MEM_SBRK_SIZE
+                        Anzahl und Groesse der Speicherbloecke, die vom
+                        Betriebssystem angefordert wurden (smalloc, slaballoc)
+                  - int DID_MEM_LARGE
+                  - int DID_MEM_LARGE_SIZE
+                  - int DID_MEM_LFREE
+                  - int DID_MEM_LFREE_SIZE
+                        Anzahl und Groesse der grossen allozierten bzw.
+                        freien Bloecke (smalloc, slaballoc)
+                  - int DID_MEM_LWASTED
+                  - int DID_MEM_LWASTED_SIZE
+                        Anzahl und Groesse der unbrauchbaren grossen
+                        Speicherfragmente (smalloc, slaballoc)
+                  - int DID_MEM_CHUNK
+                  - int DID_MEM_CHUNK_SIZE
+                        Anzahl und Groesse kleiner Speicherbloecke (chunk
+                        blocks; smalloc, slaballoc)
+                  - int DID_MEM_SMALL
+                  - int DID_MEM_SMALL_SIZE
+                  - int DID_MEM_SFREE
+                  - int DID_MEM_SFREE_SIZE
+                        Anzahl und groesse der allozierten bzw. freien
+                        kleinen Speicherbloecke (smalloc, slaballoc)
+                  - int DID_MEM_SWASTED
+                  - int DID_MEM_SWASTED_SIZE
+                        Anzahl und Groesse der unbrauchbar kleinen
+                        Speicherfragmente (smalloc, slaballoc)
+                  - int DID_MEM_MINC_CALLS
+                  - int DID_MEM_MINC_SUCCESS
+                  - int DID_MEM_MINC_SIZE
+                        Anzahl Aufrufe von malloc_increment(), Anzahl der
+                        erfolgreichen Aufrufe und die Groesse des auf diese
+                        Art allozierten Speichers (smalloc, slaballoc)
+                  - int DID_MEM_PERM
+                  - int DID_MEM_PERM_SIZE
+                        Anzahl und Groesse permanenter (non-GCable)
+                        Allokationen (smalloc, slaballoc)
+                  - int DID_MEM_CLIB
+                  - int DID_MEM_CLIB_SIZE
+                        Anzahl und Groesse der Allokationen durch Clib
+                        Funktionen (nur smalloc, slaballoc mit SBRK_OK)
+                  - int DID_MEM_OVERHEAD
+                        Overhead fuer jede Allokation (smalloc, slaballoc)
+                  - int DID_MEM_ALLOCATED
+                        Der Speicher, der durch die Speicherverwaltung
+                        alloziert wurde, inklusive den Overhead fuer die
+                        Speicherverwaltung (smalloc, slaballoc)
+                  - int DID_MEM_USED
+                        Der Speicher, der durch den Driver belegt ist, ohne
+                        den durch die Speicherverwaltung belegten Speicher
+                        (smalloc, slaballoc)
+                  - int DID_MEM_TOTAL_UNUSED
+                        Der Speicher, der vom System zur Verfuegung gestellt,
+                        aber vom Treiber nicht benoetigt wird.
+                  - int DID_MEM_AVL_NODES          (smalloc, slaballoc)
+                        Anzahl der AVL-Knoten, die zur Verwaltung der
+                        freien grossen Speicherbloecke verwendet werden
+                        (nur smalloc). Dieser Wert kann in Zukunft
+                        wieder verschwinden.
+                  - mixed * DID_MEM_EXT_STATISTICS (smalloc, slaballoc)
+                        Detaillierte Statistiken des Allokators sofern
+                        diese aktiviert wurden; 0 anderenfalls.
+
+                        Dieser Wert kann in Zukunft wieder verschwinden.
+
+                        Das Array enthaelt NUM+2 Eintraege, wobei NUM
+                        Anzahl der verschiedenen 'kleinen'
+                        Blockgroessen ist. Eintrag [NUM] beschreibt
+                        die uebergrossen 'kleinen' Bloecke, Eintrag
+                        [NUM+1] beschreibt summarisch die 'grossen'
+                        Bloecke. Jeder Eintrag ist ein Array mit
+                        diesen Feldern:
+
+                        int DID_MEM_ES_MAX_ALLOC:
+                          Maximale Anzahl allokierter Bloecke dieser
+                          Groesse.
+
+                        int DID_MEM_ES_CUR_ALLOC:
+                          Derzeitige Anzahl allokierter Bloecke dieser
+                          Groesse.
+                          Current number of allocated blocks of this size.
+
+                        int DID_MEM_ES_MAX_FREE:
+                          Maximale Anzahl freier Bloecke dieser
+                          Groesse.
+
+                        int DID_MEM_ES_CUR_FREE:
+                          Derzeitige Anzahl freier Bloecke dieser
+                          Groesse.
+
+                        float DID_MEM_ES_AVG_XALLOC:
+                          Durchschnittliche Zahl von Allokationen pro
+                          Sekunde.
+
+                        float DID_MEM_ES_AVG_XFREE:
+                          Durchschnittliche Zahl von Deallokationen pro
+                          Sekunde.
+
+                      Die Durchschnittsstatistiken schliessen interne
+                      Umsortierungen der Blocklisten nicht ein.
+
+
+        DINFO_TRACE    (7): Liefert die 'trace' Information aus dem
+            Call Stack entsprechend der Spezifikation in <arg2>. Das Resultat
+            ist entweder ein Array (dessen Format nachstehend erlaeutert ist)
+            oder ein druckbarer String. Wird <arg2> weggelasen entspricht
+            dies DIT_CURRENT.
+
+            <arg2> == DIT_CURRENT (0): Momentaner Call Trace
+                   == DIT_ERROR   (1): Letzter Fehler Trace (caught oder
+                        uncaught)
+                   == DIT_UNCAUGHT_ERROR (2): Letzer Fehler Trace, der nicht
+                        gefangen werden konnte (uncaught)
+
+            Die Information wird in Form eines Array uebergeben.
+
+            Die Fehlertraces werden nur geaendert, wenn ein entsprechender
+            Fehler auftritt; ausserdem werden sie bei einem GC (Garbage
+            Collection) geloescht. Nach einem Fehler, der nicht gefangen
+            werden konnte (uncaught error), weisen beide Traces auf das
+            gleiche Array, sodass der ==-Operator gilt.
+
+            Wenn das Array mehr als ein Element enthaelt, ist das erste
+            Element 0 oder der Name des Objekts, dessen heart_beat() den
+            laufenden Zyklus begonnen hat; alle nachfolgenden Elemente
+            bezeichnen den Call Stack, beginnen mit der hoechsten
+            aufgerufenen Funktion.
+
+            Alle Eintraege im Array sind wiederum Arrays mit folgenden
+            Elementen:
+              - int[TRACE_TYPE]: Der Typ der aufrufenden Funktion
+                    TRACE_TYPE_SYMBOL (0): ein Funktionssymbol (sollte nicht
+                                           vorkommen)
+                    TRACE_TYPE_SEFUN  (1): eine simul-efun
+                    TRACE_TYPE_EFUN   (2): eine Efun Closure
+                    TRACE_TYPE_LAMBDA (3): eine lambda Closure
+                    TRACE_TYPE_LFUN   (4): eine normale Lfun
+              - int[TRACE_NAME]
+                    _TYPE_EFUN    : entweder der Name der Funktion oder der
+                                    Code einer Operator-Closure
+                    _TYPE_LAMBDA  : die numerische Lambda-ID
+                    _TYPE_LFUN    : der Name der Lfun
+              - string[TRACE_PROGRAM]:  Der Name des Programms mit dem Code
+              - string[TRACE_OBJECT]:   Der Name des Objekts, fuer das der
+                                        Code ausgefuehrt wurde
+              - int[TRACE_LOC]:
+                    _TYPE_LAMBDA  : Der Offset des Programms seit Beginn des
+                                    Closure-Codes
+                    _TYPE_LFUN    : Die Zeilennummer.
+
+            <arg2> == DIT_STR_CURRENT (3): Liefert Informationen ueber den
+                momentanen Call Trace als druckbarer String.
+
+            <arg2> == DIT_CURRENT_DEPTH (4): Liefert die Zahl der Frames auf
+                dem Control Stack (Rekursionstiefe).
+
+        DINFO_EVAL_NUMBER (8): gibt die Nummer der aktuellen Berechnung
+            zurueck. Diese Nummer wird fuer jeden vom driver initiierten
+            Aufruf von LPC-Code erhoeht, also bei Aufruf von:
+              - Kommandos (die per add_action hinzugefuegt wurden)
+              - heart_beat, reset, clean_up
+              - Aufrufe durch call_out oder input_to
+              - master applies, die auf externe Ereignisse zurueckgehen
+              - driver hooks genauso
+              - Rueckrufen von send_erq
+              - logon in interaktiven Objekten
+
+           Dieser Zaehler kann z.B. benutzt werden, um zu verhindern, dass
+           bestimmte Aktionen mehrfach innerhalb eines heart_beat()
+           ausgefuehrt werden. Eine andere Anwendungsmoeglichkeit sind
+           Zeitstempel zur Sortierung zur Sortierung von Ereignissen.
+
+           Es ist zu beachten, dass der Zaehler ueberlaufen kann, insbesondere
+           auf 32-bit-Systemen. Er kann demzufolge auch negativ werden.
+
+
+GESCHICHTE
+        Seit 3.2.7 liefert DINFO_STATUS die Information zurueck, anstatt sie
+            nur auszugeben.
+        DINFO_DUMP wurde in 3.2.7 eingefuehrt.
+        LDMud 3.2.8 fuegte die Datengroesse des Objekts zum Resultat von
+            DINFO_MEMORY hinzu, ausserdem die DINFO_DATA Abfrage und die
+            verschiedenen DID_MEM_WASTED Statistiken.
+        LDMud 3.2.9 fuegte DINFO_TRACE, das Indizierungs-Feature von
+            DINFO_DATA, den 'destructed'-DINFO_DUMP, die DID_MEM_CLIB*,
+            die DID_MEM_PERM*, ausserdem DID_ST_OBJECTS_NEWLY_DEST,
+            DID_ST_OBJECTS_DEST, DID_MEM_OVERHEAD, DID_MEM_ALLOCATED,
+            DID_MEM_USED, DID_MEM_TOTAL_UNUSED, DID_ST_HBEAT_CALLS_TOTAL
+            und die found / added / collision Stringstatistiken.
+        LDMud 3.2.10 fuegte die Erzeugungszeit zu DINFO_DUMP:"objects" hinzu,
+            entfernte DID_MEM_UNUSED aus DINFO_DATA:DID_MEMORY, fuegte
+            DINFO_DATA:DID_STATUS DID_ST_BOOT_TIME, DID_ST_MB_FILE und
+            DID_ST_MB_SWAP hinzu und entfernte DID_ST_CALLOUT_SLOTS daraus,
+            fuegte das dritte Argument zu DINFO_OBJLIST hinzu, und veraenderte
+            die Bedeutung von DID_ST_CALLOUT_SIZE und DID_ST_HBEAT_SIZE
+            bzw. _SLOTS.
+        LDMud 3.3.533 fuegte DID_MEM_AVL_NODES zu DINFO_DATA:DID_MEMORY
+            hinzu.
+        LDMud 3.3.603 fuegte DID_MEM_EXT_STATISTICS zu DINFO_DATA:DID_MEMORY
+            hinzu.
+        LDMud 3.3.718 fuegte DIT_CURRENT_DEPTH to DINFO_TRACE hinzu.
+        LDMud 3.3.719 fuegte DINFO_EVAL_NUMBER hinzu.
+
+SIEHE AUCH
+        trace(E), traceprefix(E), malloc(D), status(D), dumpallobj(D)
diff --git a/doc/sefun/deep_present b/doc/sefun/deep_present
new file mode 100644
index 0000000..78c0983
--- /dev/null
+++ b/doc/sefun/deep_present
@@ -0,0 +1,21 @@
+FUNKTION:
+        object deep_present(string what)
+        object deep_present(object what)
+        object deep_present(string what, object ob)
+        object deep_present(object what, object ob)
+
+ARGUMENTE:
+        what - Objekt oder ID des Objektes, nach dem gesucht werden soll
+        ob - Objekt, in dem gesucht werden soll
+
+BESCHREIBUNG:
+        deep_present() sucht in this_object() (oder in ob, falls angegeben)
+        nach dem Objekt what oder einem Objekt, das auf what anspricht.
+        Im Gegensatz zu present() wird aber das komplette Inventory berueck-
+        sichtigt (also zB. auch der Inhalt von Beuteln).
+
+RUECKGABEWERT:
+        das gefundene Objekt oder 0
+
+SIEHE AUCH:
+        present(E)
diff --git a/doc/sefun/dtime b/doc/sefun/dtime
new file mode 100644
index 0000000..6624642
--- /dev/null
+++ b/doc/sefun/dtime
@@ -0,0 +1,24 @@
+FUNKTION:
+	string dtime(int time)
+
+ARGUMENTE:
+	time - Umzuwandelndes Datum in Sekunden seit 1.1.1970, 0:0:0h
+
+BESCHREIBUNG:
+	Wandelt das Datum time in einen deutschsprachigen String der Form
+	"<wtag>, <tag>. <mon> <jahr>, <std>:<min>:<sek>" um.
+
+RUECKGABEWERT:
+	Der String mit dem umgewandelten Datum.
+
+BEMERKUNGEN:
+	Als time wird meistens das Ergebnis von time() benutzt.
+  strftime() stellt eine wesentlich flexiblere Moeglichkeit der Ausgabe von
+  Zeiten dar.
+
+BEISPIELE:
+	datum = dtime(time());
+        => datum = "Mon,  6. Mar 1994, 15:00:08"
+
+SIEHE AUCH:
+	ctime(E), strftime(E), time(E)
diff --git a/doc/sefun/dump_netdead b/doc/sefun/dump_netdead
new file mode 100644
index 0000000..c73b2c0
--- /dev/null
+++ b/doc/sefun/dump_netdead
@@ -0,0 +1,13 @@
+FUNKTION:
+        string *dump_netdead()
+
+BESCHREIBUNG:
+        Gibt ein Array mit den Namen aller zur Zeit netztoten Spieler
+        zurueck.
+
+RUECKGABEWERT:
+        Ein Stringarray mit den Namen der netztoten Spieler.
+        gibt.
+
+SIEHE AUCH:
+        find_netdead(E)
diff --git a/doc/sefun/enable_commands b/doc/sefun/enable_commands
new file mode 100644
index 0000000..6508b5f
--- /dev/null
+++ b/doc/sefun/enable_commands
@@ -0,0 +1,26 @@
+SYNOPSIS
+        void enable_commands();
+
+BESCHREIBUNG
+        Erlaubt dem Objekt, Kommandos zu verwenden, die normalerweise Usern
+        zugaenglich sind. Der Aufruf markiert das Objekt als "living". Dies
+        wird fuer Spieler und alle von /std/npc abgeleiteten Objekte
+        bereits von der Mudlib erledigt und sollte nicht nochmals gemacht
+        werden.
+
+        Diese Funktion darf nicht ausserhalb von create() (oder reset(0) im
+        Compat-Modus) aufgerufen werden, weil der Kommandogeber auf dieses
+        Objekt gesetzt wird.
+
+BEISPIEL
+        void create()
+        {
+            enable_commands();
+            ...
+        }
+
+        Dies markiert das Objekt als "living".
+
+SIEHE AUCH
+        command(E), living(E), disable_commands(E), native(C), hooks(C)
+        set_living_name(E)
diff --git a/doc/sefun/file_time b/doc/sefun/file_time
new file mode 100644
index 0000000..6c24baa
--- /dev/null
+++ b/doc/sefun/file_time
@@ -0,0 +1,5 @@
+FUNKTION:
+	int file_time(string filename);
+
+Liefert den Zeitpunkt der letzten Modifikation des Files in Sekunden seit
+dem 1.1.1970, 0:00. Kann per ctime() in ein lesbares Format gebracht werden.
diff --git a/doc/sefun/find_living b/doc/sefun/find_living
new file mode 100644
index 0000000..106dd63
--- /dev/null
+++ b/doc/sefun/find_living
@@ -0,0 +1,22 @@
+SYNOPSIS:
+        object find_living(string str)
+
+BESCHREIBUNG:
+        Findet das erste "lebende" Objekt, welches per set_living_name() den
+        Namen <str> setzte.
+        
+        Das Objekt muss ausserdem per enable_commands() als Lebewesen
+        markiert worden sein. Dies ist fuer alle von /std/npc erbenden NPCs
+        _automatisch_ der Fall und sollte daher nicht nochmal explizit gemacht
+        werden.
+
+BEISPIEL:
+        object ob;
+        ob = find_living("Public Enemy");
+
+SIEHE AUCH:
+        find_player(E), enable_commands(E), set_living_name(E)
+
+LETZTE AeNDERUNG:
+09.10.2011, Zesstra
+
diff --git a/doc/sefun/find_livings b/doc/sefun/find_livings
new file mode 100644
index 0000000..cc9228f
--- /dev/null
+++ b/doc/sefun/find_livings
@@ -0,0 +1,24 @@
+FUNKTION:
+        object *find_livings(string name)
+
+ARGUMENTE:
+        name - der living_name der gesuchten Lebewesen
+
+BESCHREIBUNG:
+        Diese Funktion liefert ein Array mit allen Lebewesen, die den gleichen
+        living_name haben.
+
+RUECKGABEWERT:
+        Array mit den Lebewesen oder 0, wenn es kein Lebewesen diesen Namens
+        gibt.
+
+BEISPIELE:
+        ob = find_livings("herkules");
+        => ob = ({ [/human:herkules], 
+                   [/d/inseln/wargon/luftschloss/mon/herkules] })
+
+SIEHE AUCH:
+        find_living(E), set_living_name(E), find_player(E), find_netdead(E)
+
+LETZTE AENDERUNG:
+19. Okt. 2015, Arathorn
diff --git a/doc/sefun/find_netdead b/doc/sefun/find_netdead
new file mode 100644
index 0000000..d44ee9e
--- /dev/null
+++ b/doc/sefun/find_netdead
@@ -0,0 +1,25 @@
+FUNKTION:
+        object find_netdead(string name)
+
+ARGUMENTE:
+        name - Name des gesuchten Spielers
+
+BESCHREIBUNG:
+        Falls der Spieler name netztot ist, liefert diese Funktion das Spieler-
+        objekt zurueck.
+
+        Akzeptiert auch die UUID statt einer UID. In diesem Fall erfolgt aber
+        nur eine Pruefung, ob die UID des gefundenen Spielers zur angegebenen
+        UUID passt (d.h. "jof_-1" wuerde dann ggf. auch das Spielerobjekt Jof
+        zurueckliefern, wenn das die UUID "Jof_1234" hat).
+
+
+RUECKGABEWERT:
+        Der netztote Spieler oder 0, falls es keinen Netztoten diesen Namens
+        gibt.
+
+SIEHE AUCH:
+        find_living(E), find_player(E)
+
+LETZT AeNDERUNG:
+06.01.2009, Zesstra
diff --git a/doc/sefun/find_player b/doc/sefun/find_player
new file mode 100644
index 0000000..57c85f3
--- /dev/null
+++ b/doc/sefun/find_player
@@ -0,0 +1,42 @@
+FUNKTION:
+        object find_player(string uid)
+
+BESCHREIBUNG:
+        Findet den Spieler mit dem Namen bzw. der User-ID <uid>.
+        
+        Akzeptiert auch die UUID statt einer UID. In diesem Fall erfolgt aber
+        nur eine Pruefung, ob die UID des gefundenen Spielers zur angegebenen
+        UUID passt (d.h. "jof_-1" wuerde dann ggf. auch das Spielerobjekt Jof
+        zurueckliefern, wenn das die UUID "Jof_1234" hat).
+
+        Rueckgabewert ist das Spielerobjekt (wenn Spieler anwesend),
+        ansonsten 0.
+
+BEISPIEL:
+
+        object ob;
+        ob = find_player("deepthought");
+
+        if(ob)
+          tell_object(ob,"Tach Du!\n");
+
+        oder auch 
+
+        if(ob = find_player("deepthought"))
+          tell_object(ob,"Tach Du!\n");
+
+ANMERKUNGEN:
+
+        Via find_player() werden auch unsichtbare Magier gefunden. In 
+        Objekten, die fuer Spieler gedacht sind, muss dies dann extra
+        per Abfrage auf if(ob->QueryProp(P_INVIS)) getestet werden.
+
+        Netztote Spieler und Monster werden nicht gefunden da find_player
+        den Namen aus set_living_name() verwendet, der in player.c ge-
+        setzt wird.
+        
+SIEHE AUCH:
+        find_living(E), set_living_name(E), find_object(E), find_netdead(E)
+
+----------------------------------------------------------------------------
+Letzte Aenderung: 06.01.2009, Zesstra 
diff --git a/doc/sefun/getuuid b/doc/sefun/getuuid
new file mode 100644
index 0000000..5b34343
--- /dev/null
+++ b/doc/sefun/getuuid
@@ -0,0 +1,17 @@
+SYNOPSIS:
+    string getuuid(object ob)
+
+DESCRIPTION:
+    Liefert eine eindeutige (get unique uid) UID fuer einen Spieler.
+    Wird zusammengesetzt aus der UID des Spielers und seinem
+    Erstlogin-Datum.
+
+    Nach einer Selbstloeschung und neuem Login erhaelt der Spieler eine
+    neue UUID, bei einer Restaurierung behaelt er seine alte UUID.
+
+    Wenn die Funktion ohne Parameter aufgerufen wird, wird per Default
+    this_object() genommen.
+    
+
+SEE ALSO:
+        getuid(E)
diff --git a/doc/sefun/iso2ascii b/doc/sefun/iso2ascii
new file mode 100644
index 0000000..fe7757a
--- /dev/null
+++ b/doc/sefun/iso2ascii
@@ -0,0 +1,17 @@
+FUNKTION:
+	public string iso2ascii( string str );
+
+ARGUMENTE:
+	str
+	  String, in welchem Zeichen ersetzt werden sollen.
+
+BESCHREIBUNG:
+	In dem String werden alle Nicht-ASCII-Zeichen durch ASCII-Zeichen
+	ersetzt, und zwar Umlaute in der bekannten Form und alle anderen
+	durch ein Fragezeichen.
+
+RUeCKGABEWERT:
+	String mit ASCII-Zeichen.
+
+----------------------------------------------------------------------------
+Last modified: Fri Jul  6 19:36:09 2001 by Patryn
diff --git a/doc/sefun/log_file b/doc/sefun/log_file
new file mode 100644
index 0000000..cf7e03b
--- /dev/null
+++ b/doc/sefun/log_file
@@ -0,0 +1,31 @@
+FUNKTION:
+	int log_file(string file, string text)
+  int log_file(string file, string text, int size_to_break)
+
+ARGUMENTE:
+	file - Name der Datei, in die geschrieben werden soll
+	text - Der Text, der geschrieben werden soll
+	size_to_break - Groesse, ab der ein neues File begonnen wird (optional)
+
+BESCHREIBUNG:
+	log_file schreibt den Text text in die Datei /log/file.
+
+RUECKGABEWERT:
+	1 bei Erfolg oder 0, falls ein Fehler beim Schreiben auftrat.
+
+BEMERKUNGEN:
+	Wenn die Groesse von file vor dem Schreiben 50000 Bytes ueberschreitet,
+	wird sie in file.old umbenannt. Eine schon vorhandene Datei file.old
+	wird dabei geloescht. Der Text wird nach dem Umbenennen geschrieben.
+	Wird 'size_to_break' angegeben und ist dies > 0, wird dieser Wert (in 
+	Bytes) statt der 50000 Bytes zum Rotieren des Logfiles benutzt.
+
+BEISPIELE:
+	log_file( "report/wargon.rep", "TYPO von bla in blubb:\ntest\n");
+	In /log/report/wargon.rep finde ich nun die neueste Typomeldung... ;)
+
+SIEHE AUCH:
+	write_file(E)
+
+09.06.2014, Zesstra
+
diff --git a/doc/sefun/lowerchar b/doc/sefun/lowerchar
new file mode 100644
index 0000000..0ec62bf
--- /dev/null
+++ b/doc/sefun/lowerchar
@@ -0,0 +1,20 @@
+FUNKTION:
+	int lowerchar(int char)
+
+ARGUMENTE:
+	char - Das umzuwandelnde Zeichen
+
+BESCHREIBUNG:
+	Wenn char ein Grossbuchstabe ist, so wird es in einen Kleinbuchstaben
+	umgewandelt. Andere Zeichen werden von dieser Funktion nicht beein-
+        flusst.
+
+RUECKGABEWERT:
+	Das umgewandelte Zeichen.
+
+BEISPIELE:
+	printf("%c\n", lowerchar('A')); => a
+	printf("%c\n", lowerchar('1')); => 1
+
+SIEHE AUCH:
+	lower_case(E), lowerstring(E), upperstring(E), capitalize(E)
diff --git a/doc/sefun/lowerstring b/doc/sefun/lowerstring
new file mode 100644
index 0000000..30748fc
--- /dev/null
+++ b/doc/sefun/lowerstring
@@ -0,0 +1,20 @@
+FUNKTION:
+	string lowerstring(string str)
+
+ARGUMENTE:
+	str - Der umzuwandelnde String.
+
+BESCHREIBUNG:
+	Alle Grossbuchstaben in str werden in Kleinbuchstaben umgewandelt.
+
+RUECKGABEWERT:
+	Der umgewandelte String.
+
+BEMERKUNGEN:
+	lowerstring ist mit lower_case identisch!
+
+BEISPIELE:
+	s = lowerstring("So, McLaud...\n"); => s = "so, mclaud...\n"
+
+SIEHE AUCH:
+	lower_case(E), lowerchar(E), upperstring(E), capitalize(E)
diff --git a/doc/sefun/m_copy_delete b/doc/sefun/m_copy_delete
new file mode 100644
index 0000000..cb1c97b
--- /dev/null
+++ b/doc/sefun/m_copy_delete
@@ -0,0 +1,43 @@
+FUNKTION:
+	mapping m_copy_delete(mapping map, mixed key)
+
+ARGUMENTE:
+	map - das Mapping, aus dem geloescht werden soll.
+	key - der zu loeschende Eintrag
+
+BESCHREIBUNG:
+	Aus dem Mapping map wird der Eintrag key geloescht (wenn er in map vor-
+	handen ist). map wird dabei nicht veraendert.
+
+RUECKGABEWERT:
+	Eine (flache !) Kopie von map ohne den Eintrag key, d.h. enthaltene
+	Mappings/Arrays werden nicht kopiert.
+
+BEMERKUNGEN:
+	Das urspruengliche Mapping wird bei dieser Operation nicht veraendert!
+	Wenn man nur einen Wert aus dem Mapping loeschen will und die Kopie nicht
+	braucht, bietet sich efun::m_delete(mapping,key) sehr an, da die Erstellung
+  einer Kopie sehr aufwendig sein kann.
+
+BEISPIELE:
+	mapping m1, m2;
+
+        m1 = ([ "a":1, "b":2, "c":3 ]);
+
+        m2 = m_copy_delete(m1, "b");
+           => m1 = ([ "a":1, "b":2, "c":3 ])
+	      m2 = ([ "a":1, "c":3 ])
+
+        m_copy_delete(m1, "a");
+           => (es hat sich nichts geaendert)
+
+        m1 = m_copy_delete(m1, "a");
+           => m1 = ([ "b":2, "c":3 ])
+
+        Im letzten Fall sollte aber besser efun::m_delete(m1, "a") benutzt 
+        werden, da ansonsten das Mapping unnoetig kopiert wird und Rechen-
+        leistung frisst. 
+
+SIEHE AUCH:
+  efun::m_delete(E), mappingp(E), mkmapping(E), m_indices,(E) m_values(E),
+  sizeof(E), widthof(E)
diff --git a/doc/sefun/match_living b/doc/sefun/match_living
new file mode 100644
index 0000000..44801a8
--- /dev/null
+++ b/doc/sefun/match_living
@@ -0,0 +1,30 @@
+match_living(sefun)
+FUNKTION:
+     varargs mixed match_living( string str, int players_only,
+				 string *exclude)
+
+ARGUMENTE:
+     string str		- Kuerzel, nach dem die living_names durchsucht
+			  werden soll
+     int players_only	- 1, um nur Spieler (Interactives) zu suchen
+     string *exlude	- welche Namen sollen ignoriert werden
+
+
+BESCHREIBUNG:
+     Sucht alle Lebewesen, deren Namen mit str beginnen.
+
+RUECKGABEWERT:
+     Ein String, falls es ein Lebewesen mit dem Namen str gibt (der Name
+     muss genau passen).
+     -1, wenn es mehrere Lebewesen gibt, deren Namen mit str beginnen.
+     -2, wenn es kein Lebewesen gibt, dessen Name mit str beginnt.
+
+BEISPIELE:
+     match_living("wargon"); => "wargon", wenn Wargon eingeloggt ist.
+     match_living("war");    => "wargon", wenn es kein anderes Lebewesen
+                                gibt, dessen Name mit "war" beginnt.
+
+SIEHE AUCH:
+     find_living(E), find_player(E), find_netdead(E)
+
+27. Mai 2004 Gloinson
diff --git a/doc/sefun/md5 b/doc/sefun/md5
new file mode 100644
index 0000000..1e65090
--- /dev/null
+++ b/doc/sefun/md5
@@ -0,0 +1,30 @@
+DEPRECATED
+SYNOPSIS
+        string md5 (string arg [ , int iterations ] )
+        string md5 (int *  arg [ , int iterations ] )
+
+BESCHREIBUNG
+        Berechnet den MD5-Hashwert von <arg>.
+        Das Argument kann ein String, oder ein Array von Zahlen sein (von
+        welchen nur das unterste Byte betrachted wird).
+
+        Das Ergebnis wird als 32-stelliger Hexadezimalwert geliefert.
+
+        Ist das <iterations> Argument eine Zahl groesser 0, berechnet der
+        Driver den Digest mit diese Anzahl an Wiederholungen. Fehlt die
+        Angabe, fuehrt der Driver die Digest-Berechnung einmal aus.
+
+BEISPIEL
+        string s;
+
+        s = md5("Hallo");
+        s = md5( ({ 'H', 'e', 'l', 'l', 'o' }) )
+        s = md5( ({ 'H', 'e', 'l', 'l', 'o' }), 2 )
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.9
+        LDMud 3.2.12 fuehrte Zaehlenarrays als Argument ein, also auch
+          die Anzahl der Wiederholungen.
+
+SIEHE AUCH
+        crypt(E), md5_crypt(E), sha1(E), hash(E), hmac(E)
diff --git a/doc/sefun/mkdirp b/doc/sefun/mkdirp
new file mode 100644
index 0000000..a05d4e6
--- /dev/null
+++ b/doc/sefun/mkdirp
@@ -0,0 +1,21 @@
+FUNKTION:
+        int mkdirp(string dir)
+
+ARGUMENTE:
+        dir - Name des zu erstellenden Verzeichnisses (absolut)
+
+BESCHREIBUNG:
+        Erzeugt das Verzeichnis <dir>. Dies muss als abs. Pfad angegeben
+        werden.
+        Wenn noetig, wird die ganze Verzeichnishierarchie rekursiv erstellt.
+        
+RUECKGABEWERT:
+        0 - Verzeichnis konnte nicht erstellt werden
+        1 - Verzeichnis wurde erstellt oder existierte bereits
+
+SIEHE AUCH:
+        mkdir(E)
+
+LETZT AeNDERUNG:
+26.01.2013, Zesstra
+
diff --git a/doc/sefun/notify_fail b/doc/sefun/notify_fail
new file mode 100644
index 0000000..1970367
--- /dev/null
+++ b/doc/sefun/notify_fail
@@ -0,0 +1,85 @@
+FUNKTION:
+     #include <notify_fail.h>
+
+     varargs void notify_fail(string str, int prio)
+     varargs void notify_fail(closure cl, int prio)
+
+ARGUMENTE:
+     str   Meldung die an den Spieler anstatt des 'Wie bitte' ausgegeben
+           werden soll
+     cl    Closure, die bei Fehlschlag ausgefuehrt werden soll
+     prio  Prioritaet dieses Objekts bei diesem Setzen der Meldung
+
+BESCHREIBUNG:
+     Merkt sich den angegebenen str und gibt ihn im Falle einer inkorrekten
+     Eingabe des Spielers anstatt eines 'Wie bitte' aus.
+
+     Gedacht ist notify_fail, um dem Spieler eine bessere Hilfestellung
+     bei Kommandos / Eingaben zu geben, um ihn u.U. auf die richtige
+     Syntax hinzuweisen.
+
+     Wird notify_fail mehrfach (durch verschiedene Objekte o.ae.) auf-
+     gerufen, wird der letzte erfolgreiche Aufruf gewertet. Eine Meldung wird
+     dann tatsaechlich gesetzt, wenn die Prioritaet dieses Objekts gleich
+     gross oder groesser ist als die Prioritaet des Objekts, was das bisher
+     gueltige notify_fail() gesetzt hat. Folgende Prioritaeten sind
+     vordefiniert und werden automatisch ermittelt:
+     NF_NL_OWN    100         // eigenes Objekt (soul) ueberschreibt kaum was
+     NF_NL_THING  100000
+     NF_NL_ROOM   1000000     // Raeume ueberschreiben sonstigen Krams
+     NF_NL_LIVING 10000000    // Lebewesen ueberschreiben auch Raeume
+     2 weitere vordefinierte koennen von Magier angegeben werden:
+     NF_NL_NONE   -1          // wird von allem ueberschrieben
+     NF_NL_MAX    __INT_MAX__ // hoechste Prioritaet, ueberschreibt alles
+
+     Wird eine Closure als Argument gegeben, wird sie im Fehlerfall
+     (also erst wenn ein Kommando endgueltig fehlgeschlagen hat)
+     ausgefuehrt und hat die Fehlermeldung als Resultat
+     zurueckzugeben. Die Closure erhaelt als Argument den
+     originalen Befehlsgeber; in der Regel dies ist this_player(),
+     was aber vom MODIFY_CMD hook geaendert werden kann.
+
+     notify_fail() erkennt verschachtelte Kommandos (siehe Efun
+     command()), und ein notify_fail() in einem Unterkommando
+     hat keinen Einfluss auf das uebergeordnete Kommando.
+
+BEMERKUNGEN:
+     - solange man sich nicht absolut sicher ist, dass ein bestimmtes Objekt
+       mit dem Kommando gemeint ist (Identifikation ueber id()), sollte man
+       - notify_fail(str); return 0;
+       nutzen anstatt mit
+       - write(str) return 1;
+       die Kommandoauswertung abzubrechen (und anderen Objekten die Chance
+       zu nehmen das Kommando zu erfuellen)
+     - Kommandos werden uebrigens oft erst vom betretenen Raum, dann von den
+       Objekten abgearbeitet (je nachdem wann diese dazukamen)
+     - die Prioritaet wird momentan nicht gespeichert, sie ist nur beim Setzen
+       relevant. Will spaeter ein anderes Objekt eine Meldung setzen, wird
+       fuer das eigene Objekt die Standardprioritaet ermittelt, auch wenn man
+       eine andere hier uebergeben hat
+     - Die NF_NL_* sind in /sys/notify_fail.h defniert.
+
+BEISPIELE:
+     Ein Raum erwartet die korrekte Eingabe von 'iss suppe':
+
+     int iss_cmd(string str){
+       // zu Anfang der Funktion das notify_fail definieren
+       notify_fail("Moechtest Du vielleicht von der Suppe essen?\n");
+
+       // Spieler hat nur 'iss' ohne Parameter eingegeben oder einen anderen
+       // Parameter angegeben ... Abbruch!
+       // Falls kein weiteres Objekt das Kommando erfuellt oder das
+       // notify_fail() mit einer eigenen Meldung ueberschreibt, wird obige
+       // Meldung an den Spieler ausgegeben.
+
+       if(!str || str!="suppe") return 0;
+       // ab hier ist die Eingabe dann wirklich 'suppe' und die Funktion
+       // kann beliebig fortgefuehrt werden
+       ...
+       return   1;
+
+SIEHE AUCH:
+     add_action(E), AddCmd(L), AddAction(L),
+     query_verb(E), query_notify_fail(E)
+
+8.Aug 2007 Gloinson
diff --git a/doc/sefun/object_info b/doc/sefun/object_info
new file mode 100644
index 0000000..3945651
--- /dev/null
+++ b/doc/sefun/object_info
@@ -0,0 +1,129 @@
+DEPRECATED
+SYNOPSIS
+        #include <objectinfo.h>
+
+        mixed * object_info(object ob, int what)
+        mixed * object_info(object ob, int what, int index)
+
+DESCRIPTION
+        Returns some internal information about object <ob>, collected
+        in an array. Argument <what> determines which information is
+        returned.
+
+        The result is usually an array. However, if <index> is specified,
+        the result is the value from position <index> of the array which
+        would have been returned normally.
+
+        The possible values of <what>, and the indices of the returned
+        arrays are defined in the include file <objectinfo.h>, and may
+        change in future versions of the driver!
+
+
+        <what> == OINFO_BASIC:
+
+          This call returns basic information about <ob>:
+
+          int [OIB_HEART_BEAT]:       1 if <ob> has a heart_beat, 0 else.
+          int [OIB_IS_WIZARD]:        1 if <ob> has the wizard flag set,
+                                        0 else.
+            This entry is always 0 when set_is_wizard() is not available.
+          int [OIB_ENABLE_COMMANDS]:  1 if <ob> can give commands, 0 else.
+          int [OIB_CLONE]:            1 if <ob> is a clone, 0 else.
+          int [OIB_DESTRUCTED]:       1 if <ob> is destructed, 0 else.
+          int [OIB_SWAPPED]:          1 if <ob> is swapped, 0 else.
+          int [OIB_ONCE_INTERACTIVE]: 1 if <ob> was once interactive, 0 else.
+          int [OIB_RESET_STATE]:      1 if <ob> is (still) reset, 0 else.
+          int [OIB_WILL_CLEAN_UP]:    1 if <ob> will call clean_up(), 0 else.
+          int [OIB_LAMBDA_REFERENCED]: 1 if <ob> has lambdas, 0 else.
+          int [OIB_SHADOW]:           1 if <ob> has a shadow structure tied
+                                        to it, 0 if not.
+          int [OIB_REPLACED]:         1 if the program for <ob> was replaced,
+                                      0 else.
+          int [OIB_NEXT_RESET]:   time of the next reset
+          int [OIB_TIME_OF_REF]:  time of the last call to <ob>
+          int [OIB_NEXT_CLEANUP]: time of the next data cleanup
+          int [OIB_REF]:          number of references to <ob>
+          int [OIB_GIGATICKS] and [OIB_TICKS]: together, these numbers
+            give the accumulated evaluation cost spend in <ob>
+          int [OIB_SWAP_NUM]:     the swap number for <ob>s program,
+                                  or -1 if not swapped.
+          int [OIB_PROG_SWAPPED]: 1 if <ob>'s program is swapped, 0 else.
+          int [OIB_VAR_SWAPPED]:  1 if <ob>'s variables are swapped, 0 else.
+          int [OIB_NAME]:         <ob>'s object name.
+          int [OIB_LOAD_NAME]:    <ob>'s load name.
+          object [OIB_NEXT_ALL]:  next object in the object list.
+          object [OIB_PREV_ALL]:  previous object in the object list.
+
+
+        <what> == OINFO_POSITION:
+
+          This call returns information about <ob>'s position in the
+          global list of objects:
+
+          object [OIP_NEXT]: next object in the object list.
+          object [OIP_PREV]: previous object in the object list.
+          int    [OIP_POS]:  position of <ob> in list, counting from 0 up.
+
+          This call can be expensive in computing time.
+
+
+        <what> == OINFO_MEMORY:
+
+          This call returns information about the program <ob> uses:
+
+          int    [OIM_REF]:            number of references to the program.
+          string [OIM_NAME]:           name of program.
+          int    [OIM_PROG_SIZE]:      size of the program.
+          int    [OIM_NUM_FUNCTIONS]:  number of functions in the program.
+          int    [OIM_SIZE_FUNCTIONS]: size needed for the function structs.
+          int    [OIM_NUM_VARIABLES]:  number of variables in the program.
+          int    [OIM_SIZE_VARIABLES]: size needed for the variable structs.
+          int    [OIM_NUM_STRINGS]:    number of strings in the program.
+          int    [OIM_SIZE_STRINGS]:   size needed for the string pointers.
+          int    [OIM_SIZE_STRINGS_DATA]: size needed for the string data,
+                                       scaled down according to the extend of
+                                       data sharing.
+          int    [OIM_SIZE_STRINGS_TOTAL]: unmodified size needed for the
+                                       string data.
+          int    [OIM_NUM_INCLUDES]:   number of included files in the program.
+          int    [OIM_NUM_INHERITED]:  number of inherited programs.
+          int    [OIM_SIZE_INHERITED]: size needed for the inherit structs.
+          int    [OIM_TOTAL_SIZE]:     total size of the program.
+          int    [OIM_DATA_SIZE]:      total size of the values held in the
+                                       object's variables, scaled down
+                                       according to the extend of data
+                                       sharing.
+          int    [OIM_DATA_SIZE_TOTAL]: unmodified total size of the values
+                                       held in the object's variables
+          int    [OIM_NO_INHERIT]:     1 if the program can't be inherited.
+          int    [OIM_NO_CLONE]:       1 if the program/blueprint can't be
+                                       cloned.
+          int    [OIM_NO_SHADOW]:      1 if the program's functions can't be
+                                       shadowed.
+          int    [OIM_SHARE_VARIABLES]:  1 if clones of this program share
+                                       their initial variable values with
+                                       the blueprint.
+
+          This call swaps in the program if necessary.
+          Note that the OIM_SIZE_xxx entries only give the size spent on
+          the structures and pointers, not the size of the variable data,
+          function code, and strings themselves.
+
+HISTORY
+        Introduced in LDMud 3.2.6.
+        Changes in LDMud 3.2.7:
+          - new basic result OIB_REPLACED.
+          - basic result OIB_IS_WIZARD is always 0 if set_is_wizard()
+              is not available.
+          - basic result OIB_APPROVED is gone.
+        LDMud 3.2.8 added OIM_DATA_SIZE to the result of OINFO_MEMORY.
+        LDMud 3.2.9 added the index mechanism, OIM_NUM_INCLUDES,
+          OIM_NO_INHERIT, OIM_NO_SHADOW, OIM_NO_CLONE, OIM_SIZE_STRINGS_DATA,
+          OIM_SIZE_STRINGS_TOTAL, and OIM_DATA_SIZE_TOTAL to the result
+          of OINFO_MEMORY.
+        LDMud 3.3.378 added the OIM_SHARE_VARIABLES to the result
+          of OINFO_MEMORY.
+        LDMud 3.3.654 added the OIB_NEXT_CLEANUP to the result of OINFO_BASIC.
+
+SEE ALSO
+        debug_info(E)
diff --git a/doc/sefun/obsolete/exclude_alist b/doc/sefun/obsolete/exclude_alist
new file mode 100644
index 0000000..a967a14
--- /dev/null
+++ b/doc/sefun/obsolete/exclude_alist
@@ -0,0 +1,4 @@
+SYNOPSIS:
+	mixed *exclude_alist(int i, mixed *alist)
+
+Remove element i from alist.
diff --git a/doc/sefun/obsolete/remove_alist b/doc/sefun/obsolete/remove_alist
new file mode 100644
index 0000000..cf9bc61
--- /dev/null
+++ b/doc/sefun/obsolete/remove_alist
@@ -0,0 +1,3 @@
+mixed *remove_alist(mixed key, mixed *alist)
+
+Removes element associated by key key from alist.
diff --git a/doc/sefun/old_explode b/doc/sefun/old_explode
new file mode 100644
index 0000000..6b13d8e
--- /dev/null
+++ b/doc/sefun/old_explode
@@ -0,0 +1,28 @@
+FUNKTION:
+	string *old_explode(string str, string del)
+
+ARGUMENTE:
+	str - Der String, der aufgespaltet werden soll.
+	del - Der String, nach dem str aufgespalten werden soll.
+
+BESCHREIBUNG:
+	Durch Ausschneiden von del wird der String str in ein Array von Strings
+	zerlegt. Dieses Array wird anschliessend zuruckgegeben.
+
+RUECKGABEWERT:
+	Das Array mit den Bestandteilen der Zerlegung.
+
+BEMERKUNGEN:
+	Das Verhalten von old_explode() entspricht dem dem explode()-Verhalten,
+	das in /doc/efun/explode als "altes" Verhalten bezeichnet wird, d.h.
+	Leerstrings an Anfang und Ende des zerlegten Strings werden entfernt!
+
+BEISPIELE:
+	strs = explode( "nimm alles", " "); => strs = ({ "nimm", "alles" })
+	strs = explode( "abc", "abc" );     => strs = ({ })
+	strs = explode( "ein test", "" );   => strs = ({ "ein test" })
+	strs = explode( "a b", "a");        => strs = ({ " b" });
+
+SIEHE AUCH:
+	explode(E), new_explode(E), efun::explode(E), sscanf(E)
+        implode(E), regexplode(E)
diff --git a/doc/sefun/process_call b/doc/sefun/process_call
new file mode 100644
index 0000000..c00a039
--- /dev/null
+++ b/doc/sefun/process_call
@@ -0,0 +1,21 @@
+simul_efun::process_call(E)
+FUNKTION:
+     int process_call()
+
+BESCHREIBUNG:
+     Gibt zurueck, ob die Ausfuehrung zum derzeitigen Zeitpunkt durch
+     process_string() ausgerufen wurde.
+
+BEISPIELE:
+     process_string("@@current_long@@");
+     ...
+     string current_long() {
+      if(process_call())
+       return("Dieser String wurde durch ein process_string eingefuegt.");
+      else return("Du hast die Funktion direkt gerufen!");
+     }
+
+SIEHE AUCH:
+     notify_fail(E), process_string(E), replace_personal(E)
+
+28. Maerz 2004 Gloinson
diff --git a/doc/sefun/process_string b/doc/sefun/process_string
new file mode 100644
index 0000000..75c9290
--- /dev/null
+++ b/doc/sefun/process_string
@@ -0,0 +1,57 @@
+process_string(E)
+FUNKTION:
+     string process_string(string str)
+     string process_string(closure cl)
+
+BESCHREIBUNG:
+     Durchsucht den String str nach Vorkommnissen von Substrings, die "Wert
+     durch Funktionsaufruf zu ersetzen" andeuten. Die Form ist: @@, gefolgt
+     durch einen impliziten Funktionsaufruf.
+
+     Der zu ersetzenden Substring hat die Form:
+     @@function[:filename][|argument1|argument2]@@
+
+     Die entsprechende Funktion muss einen String zurueckgeben, oder der
+     process_string() uebergebene String str wird nicht modifiziert.
+
+     process_string() arbeitet nicht rekursiv, object_name und argument sind
+     optionale Werte.
+
+     Falls eine Closure angegeben wurde, wird diese lediglich gerufen
+     und nicht gefiltert.
+
+ANMERKUNGEN:
+     - Die Funktion, die gerufen werden soll, _muss_ mit einem Buchstaben
+		   anfangen, '_' ist nicht moeglich!
+     - folgendes Properties und Details werden bei der Abfrage ueber
+       process_string() gefiltert:
+       P_LONG, P_SHORT, P_READ_MSG, AddReadDetail()-Details und NPC-Chats
+       P_INT_LONG ueber int_long(), P_INT_SHORT ueber int_short()
+     - die Nutzung kann zu Sicherheitsproblemen fuehren, siehe auch
+       process_call()
+
+BEISPIEL:
+     // komplette Ersetzung ...
+     SetProp(P_LONG,"@@current_long@@");
+     ...
+     string current_long() {
+      if(x) return(break_string("Die Beschreibung."));
+      else return(break_string("Die andere Beschreibung."));
+     }
+
+     -> bei Abfrage: "Die Beschreibung." oder "Die andere Beschreibung."
+
+
+     // Teilersetzung
+     SetProp(P_SHORT, "Ein @@farbenfun|huebsch@@ Ding");
+     ...
+     string farbenfun(string str) {
+      return(str+" "+"gelbes");
+     }
+
+     -> bei Abfrage: "Ein huebsch gelbes Ding."
+
+SIEHE AUCH:
+     notify_fail(E), process_call(E), replace_personal(E)
+
+22. Nov. 2006 Arathorn
diff --git a/doc/sefun/query_editing b/doc/sefun/query_editing
new file mode 100644
index 0000000..010c98e
--- /dev/null
+++ b/doc/sefun/query_editing
@@ -0,0 +1,13 @@
+DEPRECATED
+SYNOPSIS
+        mixed query_editing(object ob);
+
+BESCHREIBUNG
+        Liefert 1, wenn <ob> interaktiv ist (das heisst, es gibt einen realen
+        Benutzer mit einer Socketverbindung zum Mud) und gerade mit ed() eine
+        Datei bearbeitet. Wenn ed() mit einem Funktionsnamen als zweites
+        Argument aufgerufen wird, wird das Objekt, aus dem ed() aufgerufen
+        wurde, geliefert, sonst 0.
+
+SIEHE AUCH
+        ed(E)
diff --git a/doc/sefun/query_idle b/doc/sefun/query_idle
new file mode 100644
index 0000000..7c0a0a5
--- /dev/null
+++ b/doc/sefun/query_idle
@@ -0,0 +1,8 @@
+SYNOPSIS
+        int query_idle(object ob);
+
+BESCHREIBUNG
+        Gibt an, seit wie vielen Sekunden ein Player Objekt <ob> idle ist.
+
+SIEHE AUCH
+        interactive(E)
diff --git a/doc/sefun/query_input_pending b/doc/sefun/query_input_pending
new file mode 100644
index 0000000..8033cec
--- /dev/null
+++ b/doc/sefun/query_input_pending
@@ -0,0 +1,11 @@
+DEPRECATED
+SYNOPSIS
+        object query_input_pending(object ob);
+
+BESCHREIBUNG
+        Wenn <ob> interaktiv und ein input_to() haengig ist, so liefert die
+        Efun das Objekt zurueck, welches den input_to() aufgerfuen hat. Ist
+        kein input_to() haengig, liefert die Funktion 0.
+
+SIEHE AUCH
+        input_to(E), find_input_to(E), input_to_info(E), remove_input_to(E)
diff --git a/doc/sefun/query_ip_name b/doc/sefun/query_ip_name
new file mode 100644
index 0000000..d16298f
--- /dev/null
+++ b/doc/sefun/query_ip_name
@@ -0,0 +1,12 @@
+GESCHUETZT
+SYNOPSIS
+        string query_ip_name(object ob);
+
+BESCHREIBUNG
+        Liefert den IP-Namen des Users <ob> oder des aktuellen Benutzers, wenn
+        <ob> nicht angegeben wurde. Der IP-Name wird durch den asynchronen
+        Prozess hname ermittelt. Wenn der IP-Name nicht ermittelt werden kann,
+        liefert query_ip_name() die IP-Nummer zurueck.
+
+SIEHE AUCH
+        query_ip_number(E)
diff --git a/doc/sefun/query_ip_number b/doc/sefun/query_ip_number
new file mode 100644
index 0000000..a462aba
--- /dev/null
+++ b/doc/sefun/query_ip_number
@@ -0,0 +1,24 @@
+GESCHUETZT
+SYNOPSIS
+        string query_ip_number(object ob);
+        string query_ip_number(mixed & ob);
+
+BESCHREIBUNG
+        Liefert die IP-Nummer des Benutzers <ob> oder des aktuellen Benutzers,
+        wenn <ob> nicht angegeben wurde.
+
+        Wenn <ob> als Referenz angegeben wird (dann muss es ein gueltiges
+        Objekt sein), wird dieses bei der Ausgabe auf die struct sockaddr_in
+        gesetzt. struct sockaddr_in ist ein Integer-Array, mit einem Integer
+        pro Adressbyte:
+
+            ob[0 .. 1] : sin_family
+            ob[2 .. 3] : sin_port
+            ob[4 .. 7] : sin_addr
+            ob[8 ..15] : nicht definiert.
+
+AENDERUNGEN
+        Die Rueckgabe von struct sockaddr_in wurde in 3.2.1@81 eingefuehrt.
+
+SIEHE AUCH
+        query_ip_name(E)
diff --git a/doc/sefun/query_limits b/doc/sefun/query_limits
new file mode 100644
index 0000000..954577a
--- /dev/null
+++ b/doc/sefun/query_limits
@@ -0,0 +1,56 @@
+DEPRECATED
+SYNOPSIS
+        #include <sys/rtlimits.h>
+
+        int *query_limits();
+        int *query_limits(int default);
+
+BESCHREIBUNG
+        Liefert ein Array mit den momentan gueltigen Laufzeit Limiten bzw.
+        die standardmaessigen Laufzeit Limiten, wenn <default> wahr ist.
+        Die Eintraege im gelieferten Array bedeuten:
+
+        int[LIMIT_EVAL]:        die maximalen Eval Kosten
+        int[LIMIT_ARRAY]:       die maximale Anzahl Array Eintraege
+        int[LIMIT_MAPPING]:     die maximale Anzahl Mapping Eintraege
+        int[LIMIT_BYTE]:        die maximale Anzahl Bytes, die mit read_bytes()
+                                /write_bytes() bearbeitet werden koennen
+        int[LIMIT_FILE]:        die maximale Anzahl Bytes, die mit read_file()
+                                /write_file() bearbeitet werden koennen
+        int[LIMIT_CALLOUTS]:    die maximale Anzahl gleichzeitiger call_out()s
+        int[LIMIT_COST]:        wie die aktuellen Kosten einzurechnen sind
+
+        Ausser fuer LIMIT_COST ein Limit von '0' (auch LIMIT_UNLIMITED)
+        bedeutet 'keine Limit'.
+
+        LIMIT_COST hat diese Bedeutungen:
+          
+          wert > 0: Maximal <wert> fuer als Kosten fuer die aktuelle Ausfuehrung
+                    verwendet, ungeachtet wie lange sie tatsaechlich dauert.
+               = 0: ist die derzeite LIMIT_EVAL groesser als die vorherige
+                    LIMIT_EVAL, kostet die aktuelle Ausfuehrung nur 10
+                    Ticks; andernfalls werden die gesamten Kosten angerechnet.
+                < 0: (-wert)% der aktuellen Ausfuehrungskosten werden
+                     angerechnet.
+
+BEMERKUNGEN:
+        "Aktuelle Kosten" bei LIMIT_COST hat im Falle der Benutzung von
+        limited() die Bedeutung von "im limited verbrauchte Kosten", steuert
+        also, wieviel der im Durchlaufen der Funktion im limited()
+        verbrauchten Ticks mit dem Ende von limited() angezogen wird.
+
+BEISPIELE
+        query_limits()
+        --> liefert die momentan gueltigen Laufzeit Limiten.
+        query_limits(1)
+        --> liefert die standardmaessigen Laufzeit Limiten.
+
+AENDERUNGEN
+        Eingefuehrt in LDMud 3.2.7.
+        LIMIT_CALLOUTS wurde in LDMud 3.2.9 eingefuehrt.
+
+SIEHE AUCH
+        limited(E), set_limits(E)
+
+16.05.2007, Zesstra
+
diff --git a/doc/sefun/query_mud_port b/doc/sefun/query_mud_port
new file mode 100644
index 0000000..2e5c6d2
--- /dev/null
+++ b/doc/sefun/query_mud_port
@@ -0,0 +1,23 @@
+DEPRECATED
+SYNOPSIS
+        int query_mud_port(void)
+        int query_mud_port(object user)
+        int query_mud_port(int num)
+
+DESCRIPTION
+        Returns the port number the parser uses for user connections.
+
+        If no argument is given, the port for this_player() is
+        returned. If this_player() is not existing or not interactive,
+        the first port number open for connections is returned.
+
+        If an user object is given, the port used for its connection
+        is returned.
+        If a positive number is given, the <num>th port number the
+        parser uses for connections is returned (given that there are
+        that many ports).
+        If -1 is given, the number of ports open for connections is
+        returned.
+
+SEE ALSO
+        query_udp_port(E)
diff --git a/doc/sefun/query_next_reset b/doc/sefun/query_next_reset
new file mode 100644
index 0000000..03bbd7e
--- /dev/null
+++ b/doc/sefun/query_next_reset
@@ -0,0 +1,27 @@
+simul_efun::query_next_reset(E)
+FUNKTION:
+     varargs int query_next_reset(object ob)
+
+ARGUMENTE:
+     ob - das interessierende Objekt; wenn nicht angegeben, wird 
+          this_object() verwendet
+
+BESCHREIBUNG:
+     Diese sefun gibt den Zeitpunkt des naechsten Resets des Objektes ob 
+     zurueck. Die Angabe erfolgt in Sekunden, die seit dem 01. Januar 1970 
+     00:00:00 GMT verstrichen sind, analog zu time().
+
+     In der Regel erfolgt der Reset im naechsten Backend-Zyklus nach dem 
+     Faelligkeitszeitpunkt,  d.h. momentan in den nachfolgenden 2s.
+     Allerdings kann dies auch mal nen Zyklus laenger dauern (4s), wenn der
+     Driver viel zu tun hat.
+
+BEMERKUNGEN:
+     Diese sefun ist object_info()-Abfragen vorzuziehen, da die Parameter und
+     Rueckgabewerte von object_info() bei verschiedenen Gamedriverversionen
+     variieren koennen.
+
+SIEHE AUCH:
+     call_out(E), object_info(E), reset(L), set_next_reset(E), time(E)
+
+28.07.2014 Arathorn
diff --git a/doc/sefun/query_once_interactive b/doc/sefun/query_once_interactive
new file mode 100644
index 0000000..c794223
--- /dev/null
+++ b/doc/sefun/query_once_interactive
@@ -0,0 +1,8 @@
+SYNOPSIS
+        int query_once_interactive(object obj);
+
+BESCHREIBUNG
+        Wahr, wenn <obj> interaktiv ist oder dies einmal war.
+
+SIEHE AUCH
+        remove_interactive(E)
diff --git a/doc/sefun/query_shadowing b/doc/sefun/query_shadowing
new file mode 100644
index 0000000..3ce5d6a
--- /dev/null
+++ b/doc/sefun/query_shadowing
@@ -0,0 +1,18 @@
+DEPRECATED
+query_shadowing(E)
+FUNKTION:
+     object query_shadowing(object obj)
+
+BESCHREIBUNG:
+     Die Funktion gibt das derzeit vom Objekt <obj> beschattete Objekt
+     victim oder 0 zurueck.
+
+BEISPIELE:
+     // B beschattet A
+     query_shadowing(find_object(B)) == A
+
+SIEHE AUCH:
+     Generell:	     shadow(E), unshadow(E)
+     Rechte:	     query_allow_shadow(M), query_prevent_shadow(L)
+
+23.02.2016, Zesstra
\ No newline at end of file
diff --git a/doc/sefun/query_snoop b/doc/sefun/query_snoop
new file mode 100644
index 0000000..4da8838
--- /dev/null
+++ b/doc/sefun/query_snoop
@@ -0,0 +1,10 @@
+DEPRECATED
+SYNOPSIS
+        object query_snoop(object victim)
+
+DESCRIPTION
+        Returns the user who currently snoops victim. The calling
+        object must be privileged by the master object.
+
+SEE ALSO
+        snoop(E)
diff --git a/doc/sefun/query_wiz_grp b/doc/sefun/query_wiz_grp
new file mode 100644
index 0000000..c45cfe1
--- /dev/null
+++ b/doc/sefun/query_wiz_grp
@@ -0,0 +1,15 @@
+FUNKTION:
+        query_wiz_grp(string wiz)
+        query_wiz_grp(object wiz)
+
+ARGUMENTE:
+        wiz - Spieler, dessen Magierlevelgruppe ermittelt werden soll
+
+BESCHREIBUNG:
+        Diese Funktion ermittelt die Magiergruppe, der der Spieler wiz angehoert.
+
+RUECKGABEWERT:
+        Die Magierlevelgruppe des Spielers.
+
+SIEHE AUCH:
+        /secure/wizlevels.h
diff --git a/doc/sefun/query_wiz_level b/doc/sefun/query_wiz_level
new file mode 100644
index 0000000..03e89cb
--- /dev/null
+++ b/doc/sefun/query_wiz_level
@@ -0,0 +1,43 @@
+FUNKTION:
+	int query_wiz_level(object ob)
+	int query_wiz_level(string ob)
+
+ARGUMENTE:
+	ob - Der Spieler/das Objekt, dessen Magierlevel ermittelt werden soll.
+       Es kann auch eine UID (z.B. "zesstra", "d.inseln.zesstra") als String
+       uebergeben werden.
+
+BESCHREIBUNG:
+	query_wiz_level() liefert den Magierlevel des Objekts ob zurueck.
+	Normale Spieler haben einen Magierlevel von 0, Seher normalerweise
+  einen von 1 (auf jeden Fall < 10).
+  Objekte bekommen folgende Level:
+  /d/           - 25
+  /p/           - 21
+  /obj/         - 0
+  /std/         - 0
+  /gilden/      - 30
+  /spellbooks/  - 30
+  /players/     - entsprechend Magier, 20 - 111
+  /secure/      - 100
+
+RUECKGABEWERT:
+	Der Magierlevel des Spielers/Objekts.
+
+BEMERKUNGEN:
+	Wird als Parameter ein String mit einem Spielernamen uebergeben, so 
+	kann auch der Magierlevel eines nicht eingeloggten Spielers heraus-
+	gefunden werden.
+
+BEISPIELE:
+	lv = query_wiz_level(find_player("jof"))
+	   => lv = 111, falls Jof eingeloggt ist.
+	lv = query_wiz_level("jof")
+           => lv = 111 in jedem Fall.
+
+SIEHE AUCH:
+	/secure/wizlevels.h
+
+LETZTE AeNDERUNG:
+15.10.2007, Zesstra
+
diff --git a/doc/sefun/replace_personal b/doc/sefun/replace_personal
new file mode 100644
index 0000000..2a18dc7
--- /dev/null
+++ b/doc/sefun/replace_personal
@@ -0,0 +1,84 @@
+FUNKTION:
+     varargs string replace_personal(string str, mixed *obs [, int caps]);
+
+DEFINIERT IN:
+     /secure/simul_efun.c
+
+ARGUMENTE:
+     str    - zu bearbeitender String
+     obs    - Liste von Objekt1, Objekt2, ..., Objekt9
+              (Objekte oder Strings)
+     caps   - 1 fuer Grossschreibung des Ersetzten nach Satzenden (.,?,!,")
+              0 sonst
+
+BESCHREIBUNG:
+     Ersetzt in Strings
+     @WERx, @WESSENx, @WEMx, @WENx durch
+        Objectx->name(<casus>, 1);
+     @WERUx, @WESSENUx, @WEMUx, @WENUx durch
+        Objectx->name(<casus>);
+     @WERQPx, @WESSENQPx, @WEMQPx, @WENQPx durch
+        Objectx->QueryPronoun(<casus>);
+     @WERQAx, @WESSENQAx, @WEMQAx, @WENQAx durch
+        Objectx->QueryArticle(<casus>, 1, 1);
+     @WERQPPGNx, @WESSENQPPGNx, @WEMQPPGNx, @WENQPPGNx durch
+        Objectx->QueryPossPronoun(<gender>, <casus>, <number>);
+     oder den entsprechenden String bei "Objektx".            
+
+BEMERKUNGEN:
+     x steht fuer die Position des Objekts/Strings in *obs, beginnend bei 1.
+     
+     Besonderheiten beim Possessivpronomen (@WERQPPGNx):
+     G muss durch das Geschlecht (M, F oder N) und N durch den Numerus (S 
+     oder P) ersetzt werden. 
+     Alle Angaben, auch der CASUS, beziehen sich dabei auf das Objekt, welches
+     besessen wird, nicht auf den Besitzer! Dieser ist bereits durch x 
+     bestimmt.
+     
+RUeCKGABEWERT:
+     durchersetzter String 
+     
+Beispiele:
+
+     replace_personal("@WER1", ({find_player("gloinson")}))
+     == "Gloinson"
+     
+     replace_personal("@WEMQP1", ({find_player("gloinson")}))
+     == "ihm"     
+     
+     // unbestimmter und bestimmter Artikel:
+     replace_personal("@WER1 zueckt @WENU2 und verhaut @WEN3.", 
+                      ({find_player("gloinson"),
+                        find_object("/obj/mpa"), 
+                        find_object("/obj/wanderer")}))
+     == "Gloinson zueckt eine Zeitung und verhaut den Wanderer."
+     
+     // Beim Possessivpronomen beziehen sich WEN, F und P (Akkusativ,
+     // Femininum, Plural) auf die Taschen, nicht auf Kessa:
+     replace_personal("@WER1 steckt @WESSEN2 Turnschuhe in @WENQPPFP1 "
+                      "Taschen.", 
+                      ({find_player("kessa"), 
+                        find_player("gloinson")}))
+     == "Kessa steckt Gloinsons Turnschuhe in ihre Taschen."
+
+     // Ein Beispiel mit laengerem *obs:
+     replace_personal("@WER1 zieht @WENQPPMP1 neuen Turnschuhe an. @WER2 ist "
+                      "so beeindruckt, dass @WERQP2 @WEMQP1 @WENU3 auf die "
+                      "@WEN4 haut und die Schuhe in @WEMQPPFS2 Tasche "
+                      "verschwinden laesst. @WERU5 schaut zu und kichert "
+                      "irre. Wenn das @WER6 gesehen haette!",
+                      ({find_player("gloinson"), 
+                        find_player("kessa"),
+                        find_object("/obj/mpa"),                        
+                        "Birne",
+                        find_object("/obj/wanderer"),
+                        find_netdead("jof")}),1)
+     == "Gloinson zieht seine neuen Turnschuhe an. Kessa ist so beeindruckt, "
+        "dass sie ihm eine Zeitung auf die Birne haut und die Schuhe in ihrer "
+        "Tasche verschwinden laesst. Ein Wanderer schaut zu und kichert "
+        "irre. Wenn das Jof gesehen haette!"
+
+SIEHE AUCH:
+     AddCmd(L)
+
+05. September 2015, Arathorn
diff --git a/doc/sefun/save_object b/doc/sefun/save_object
new file mode 100644
index 0000000..b1743b1
--- /dev/null
+++ b/doc/sefun/save_object
@@ -0,0 +1,19 @@
+SYNOPSIS
+        mixed save_object(string name);
+
+BESCHREIBUNG
+        Diese Simul-Efun unterscheidet sich in einigen Details von der
+        Driver-Efun save_object() (s. auch dort! Wichtig!).
+        1. diese sefun speichert auch die mit F_SAVE markierten Properties
+        eines Objektes ab (was die efun des Driver nicht tut!).
+        2. das Format, in dem gespeichert wird, kann bei der sefun nicht
+        ausgewaehlt werden (ist aber auch nicht noetig!), sondern ein
+        mudlib-weiter Standard wird benutzt.
+        3. will man nicht in einem File speichern, sondern das, was im File
+        stehen wurde, als String zurueckhaben, muss man 0 als 'name'
+        uebergeben.
+
+SIEHE AUCH
+        save_object(E), restore_object(E), save_value(E)
+03.08.2007, Zesstra
+
diff --git a/doc/sefun/send_room b/doc/sefun/send_room
new file mode 100644
index 0000000..24b05fa
--- /dev/null
+++ b/doc/sefun/send_room
@@ -0,0 +1,30 @@
+FUNKTION:
+varargs void send_room(object|string room, string msg, int msg_type,
+                       string msg_action, string msg_prefix,
+                       object *exclude, object origin)
+
+BESCHREIBUNG:
+        Sendet msg an alle Objekte in room durch Aufruf von ReceiveMsg() mit
+        den uebergebenen Argumenten.
+        Zur Bedeutung der Argumente siehe Manpage von ReceiveMsg().
+
+        Wenn das Raumobjekt mit seinem Namen angegeben ist, wird das Objekt
+        unter diesem Namen gesucht und und geladen, falls notwendig.
+
+        Mit dem Array <*exclude> kann man verhindern, dass die Nachricht an
+        die darin enthaltenen Objekte gesendet wird.
+        Das ist sinnvoll, wenn zB ein Spieler Ausloeser einer Meldung ist
+        und diese selbst nicht erhalten soll.
+
+        origin gibt an, welches Objekt die Meldung ausloest (muss nicht das
+        sendende Objekt selber) und wird vor allem fuer die Ignorierepruefung
+        verwendet. Default ist das sendende Objekt.
+
+        Letztendlich ist die sefun vergleichbar zu tell_room().
+
+SIEHE AUCH
+        ReceiveMsg(L)
+        tell_object(E), tell_room(E), say(E), write(E)
+
+13.03.2016, Zesstra
+
diff --git a/doc/sefun/set_heart_beat b/doc/sefun/set_heart_beat
new file mode 100644
index 0000000..837759e
--- /dev/null
+++ b/doc/sefun/set_heart_beat
@@ -0,0 +1,23 @@
+SYNOPSIS
+        int set_heart_beat(int flag);
+
+BESCHREIBUNG
+        Schaltet den Heartbeat ein (1) oder aus (0). Der Treiber wendet die
+        Lfun heart_beat() auf das aktuelle Objekt alle __HEARTBEAT_INTERVAL__
+        Sekunden an, wenn der Heartbeat eingeschaltet ist. Ein Shadow
+        auf der Lfun wird ignoriert.
+        
+        Der Heartbeat sollte immer ausgeschaltet werden, wenn er nicht
+        gebraucht wird. Das reduziert die Systemauslastung.
+
+        Liefert '1' bei Erfolg, '0' bei Fehler.
+
+        Das Abschalten eines bereits abgeschalteten Heartbeats (und umgekehrt
+        das Einschalten eines bereits eingeschalteten Heartbeats) zaehlt
+        als Fehler.
+
+BEMERKUNGEN
+        __HEARTBEAT_INTERVAL__ ist in MG = 2 Sekunden
+
+SIEHE AUCH
+        heart_beat(A), call_out(E)
diff --git a/doc/sefun/set_light b/doc/sefun/set_light
new file mode 100644
index 0000000..62390c4
--- /dev/null
+++ b/doc/sefun/set_light
@@ -0,0 +1,17 @@
+SYNOPSIS:
+        int set_light(int n)
+
+DESCRIPTION:
+        An object is by default dark. It can be set to not dark by
+        calling set_light(1). The environment will then also get this
+        light. The returned value is the total number of lights in
+        this room. So if you call set_light(0) it will return the
+        light level of the current object.
+        
+        Note that the value of the argument is added to the light of
+        the current object.
+
+BUGS:
+	This handling of light by the parser is inappropriate for most
+	purposes: If you put a burning candle into a safe, the safe
+	will start to emit light.
diff --git a/doc/sefun/set_living_name b/doc/sefun/set_living_name
new file mode 100644
index 0000000..360a3ce
--- /dev/null
+++ b/doc/sefun/set_living_name
@@ -0,0 +1,25 @@
+SYNOPSIS:
+        void set_living_name(string name)
+
+BESCHREIBUNG:
+        Setzt einen "Lebewesennamen" fuer das Objekt, indem Name und Objekt in
+        eine Tabelle eingetragen werden, welche von find_living() durchsucht 
+        wird. Nach Setzen des Namens kann das Objekt per find_living() 
+        gefunden werden.
+
+        Das Objekt muss ausserdem per enable_commands() als Lebewesen
+        markiert worden sein. Dies ist fuer alle von /std/npc erbenden NPCs
+        _automatisch_ der Fall und sollte daher nicht nochmal explizit gemacht
+        werden.
+
+        Alle von /std/npc erbenden NPCs setzen ebenfalls automatisch einen
+        LivingName, der lower_case(P_NAME) entspricht.
+
+        Ein Objekt kann nur einen Namen haben, mit dem es per find_living()
+        gesucht werden kann.
+
+SIEHE AUCH:
+        find_living(E), find_livings(E), find_player(E), enable_commands(E)
+
+LETZTE AeNDERNG:
+19.10.2015, Arathorn
diff --git a/doc/sefun/set_object_heart_beat b/doc/sefun/set_object_heart_beat
new file mode 100644
index 0000000..a7ba4ef
--- /dev/null
+++ b/doc/sefun/set_object_heart_beat
@@ -0,0 +1,15 @@
+FUNKTION:
+        int set_object_heart_beat(object ob, int on)
+
+ARGUMENTE:
+        ob - Objekt, dessen heart_beat veraendert werden soll
+        on - Soll der heart_beat ein- oder ausgeschaltet werden?
+
+BESCHREIBUNG:
+        Der heart_beat des Objektes wird ein- (on=1) oder aus- (on=0) geschaltet.
+
+RUECKGABEWERT:
+        1, wenn das Setzen geklappt hat, ansonsten 0.
+
+SIEHE AUCH:
+        set_heart_beat(E), heart_beat(L), call_out(E)
diff --git a/doc/sefun/sha1 b/doc/sefun/sha1
new file mode 100644
index 0000000..00bb6cc
--- /dev/null
+++ b/doc/sefun/sha1
@@ -0,0 +1,22 @@
+DEPRECATED
+SYNOPSIS
+        string sha1 (string arg)
+        string sha1 (int *  arg)
+
+BESCHREIBUNG
+        Berechnet den SHA1-Hashwert von <arg>.
+        Das Argument kann ein String, oder ein Array von Zahlen sein (von
+        welchen nur das unterste Byte betrachted wird).
+
+BEISPIEL
+        string s;
+
+        s = sha1("Hello");
+        s = sha1( ({ 'H', 'e', 'l', 'l', 'o' })
+
+HISTORY
+        Eingefuehrt in LDMud 3.3.523.
+        LDMud 3.3.712 fuehrte Zaehlenarrays als Argument ein.
+
+SIEHE AUCH
+        crypt(E), md5(E)
diff --git a/doc/sefun/shadow b/doc/sefun/shadow
new file mode 100644
index 0000000..9309dea
--- /dev/null
+++ b/doc/sefun/shadow
@@ -0,0 +1,143 @@
+FUNKTION:
+     object shadow(object ob, int flag)
+
+ARGUMENTE:
+     object ob		- das vom shadow betroffene Objekt
+     int flag		- 0 fuer eine Shadow-Existenzabfrage
+                      1 fuer Shadow durch previous_object()
+
+BESCHREIBUNG:
+     Wenn <flag> nicht 0 ist, wird das aktuelle Objekt dem Objekt obj
+     als Shadow uebergeworfen. Bei Erfolg wird das geshadowte Objekt
+     zurueckgegeben, sonst 0.
+     Wenn <flag> 0 ist, wird entweder 0 oder das geshadowte Objekt
+     zurueck gegeben.
+
+     Wenn ein Objekt A ein Objekt B beschattet, werden alle call_other() fuer
+     B auf A umgeleitet. Wenn die an B gerufene Funktion in A existiert, so
+     wird sie in A gerufen, bei Nichtexistenz in B.
+     A ist das einzige Objekt, welche die beschatteten Funktionen mit 
+     call_other() in B aufrufen kann, selbst B kann nicht per call_other() 
+     diese Funktion rufen.
+     Alle intern verwendeten Funktionen arbeiten jedoch weiterhin normal.
+
+     Das aufrufende Objekt muss vom Master-Objekt die Erlaubnis haben,
+     als Shadow zu wirken.
+
+     Es gibt folgende Kriterien fuer eine erfolgreiche Beschattung:
+     - das zu beschattende Objekt ob:
+       - ist weder ein access_rights-Objekt noch ein ROOT-Objekt
+       - gibt beim Aufruf von query_prevent_shadow(beschatter) eine 0
+         zurueck
+       - beschattet selbst niemanden
+       - hat kein '#pragma no_shadow' gesetzt
+     - der Beschatter:
+       - wird nicht selbst (direkt) beschattet
+       - beschattet noch niemanden (sonst folgt direkter Abbruch)
+       - hat kein environment()
+       - definiert/beschattet keine Methode, die im beschatteten Objekt ob 
+         als nomask definiert ist
+
+     Beschattet man ein Objekt A mit einem Objekt B und dann das Objekt A
+     zusaetzlich mit einem Objekt C, so wird eine Beschattungshierarchie
+     erstellt:
+
+     B macht shadow(A, 1)
+     B->A
+     C macht shadow(A, 1)
+     C->B->A
+
+BEISPIELE:
+     // wenn: B beschattet A, dann
+     shadow(find_object(A), 0) == B
+
+
+     // 3 Objekte beschatten in Hierarchie (liegt auch im Pfad)
+     --- aa.c ---
+     void fun() {
+         printf("%O [a] fun()\n", this_object());
+     }
+
+     void fun3() {
+         printf("%O [a] fun3()\n", this_object());
+     }
+
+     --- bb.c ---
+     int fun() {
+         printf("%O [b] fun()\n", this_object());
+         find_object("/doc/beispiele/shadow/aa")->fun();
+     }
+
+     void fun2() {
+         printf("%O [b] fun2()\n", this_object());
+         find_object("/doc/beispiele/shadow/aa")->fun3();
+         this_object()->fun3();
+     }
+
+     void do_shadow(object target) { shadow(target, 1); }
+
+     --- cc.c ---
+     int fun() {
+         printf("%O [c] fun()\n", this_object());
+         find_object("/doc/beispiele/shadow/aa")->fun();
+     }
+
+     void fun3() {
+         printf("%O [c] fun3()\n", this_object());
+     }
+
+     void do_shadow(object target) { shadow(target, 1); }
+
+     // darauf arbeitender Code
+
+     object a, b, c;
+
+     destruct("/doc/beispiele/shadow/aa");
+     a = load_object("/doc/beispiele/shadow/aa");
+     destruct("/doc/beispiele/shadow/bb");
+     b = load_object("/doc/beispiele/shadow/bb");
+     destruct("/doc/beispiele/shadow/cc");
+     c = load_object("/doc/beispiele/shadow/cc");
+
+     b->do_shadow(a);
+     c->do_shadow(a);
+     printf("--- a->fun() ---\n");
+     a->fun();
+     printf("--- b->fun() ---\n");
+     b->fun();
+     printf("--- c->fun() ---\n");
+     c->fun();
+     printf("--- b->fun2() ---\n");
+     b->fun2();
+
+     // ... und seine Ausgabe:
+
+     --- a->fun() ---
+     /doc/beispiele/shadow/cc [c] fun()
+     /doc/beispiele/shadow/bb [b] fun()
+     /doc/beispiele/shadow/aa [a] fun()
+     --- b->fun() ---
+     /doc/beispiele/shadow/cc [c] fun()
+     /doc/beispiele/shadow/bb [b] fun()
+     /doc/beispiele/shadow/aa [a] fun()
+     --- c->fun() ---
+     /doc/beispiele/shadow/cc [c] fun()
+     /doc/beispiele/shadow/bb [b] fun()
+     /doc/beispiele/shadow/aa [a] fun()
+     --- b->fun2() ---
+     /doc/beispiele/shadow/bb [b] fun2()
+     /doc/beispiele/shadow/aa [a] fun3()
+     /doc/beispiele/shadow/cc [c] fun3()
+
+     // Der erste Aufruf von b::fun2() in a findet sofort a::fun3()! Der
+     // Driver nimmt an, dass alle Shadows ab c bei Rufen von b nach a
+     // schon ihre Chance hatten.
+     // Der zweite Aufruf allerdings ist auf b und wird beim Durchgeben
+     // an a von c uebernommen.
+
+SIEHE AUCH:
+     Generell:	     shadow(E)
+     Rechte:	     query_allow_shadow(M), query_prevent_shadow(L)
+     Informationen:  query_shadowing(E)
+
+8.Aug 2007 Gloinson
\ No newline at end of file
diff --git a/doc/sefun/shout b/doc/sefun/shout
new file mode 100644
index 0000000..ead266f
--- /dev/null
+++ b/doc/sefun/shout
@@ -0,0 +1,69 @@
+FUNKTION:
+     varargs void shout( string text, mixed where )
+
+DEFINIERT IN:
+     /secure/simul_efun.c
+
+ARGUMENTE:
+     text
+          Der Text, der ausgegeben werden soll
+
+     where [optional]
+          Wo soll man den Text ueberall hoeren koennen?
+
+BESCHREIBUNG:
+     Der Text 'text' wird an alle Spieler in einem Gebiet ausgegeben.
+     Wird der Parameter 'where' weggelassen bzw. ist er null, so geht der
+     Text an alle Spieler im Mud. Das catch_tell() von NPCs wird nicht
+     ausgeloest.
+
+     Ist 'where' eine Zahl != 0, so wird der Text nur an Spieler ausgegeben,
+     die sich im selben Gebiet aufhalten wie this_player(). Dabei wird die
+     Zugehoerigkeit zum selben Gebiet an den ersten zwei Ebenen des Pfades
+     der Raeume festgemacht. Befindet sich this_player() z.B. in
+     "/d/ebene/irgendwo", so geht der Text an alle Spieler, deren Aufenthalts-
+     orte in "/d/ebene/*" liegen.
+
+     Fuer 'where' kann auch direkt ein Pfad angegeben werden. So wuerde ein
+     'shout( txt, "/players/" )' an alle Spieler gehen, die sich in
+     (eigentlich nicht erwuenschten) Raeumen in /players/* befinden.
+
+     Um mit einem Aufruf gleich mehrere Pfade abzudecken, kann auch ein Array
+     von Strings uebergeben werden. Alle Pfade werden als 'regular expression'
+     interpretiert. Dadurch ist es moeglich, die Zielraeume auf einfache Art
+     sehr genau einzuschraenken.
+
+     HINWEIS: Bitte ueberleg vor jedem shout() genau, ob es wirklich noetig
+     ist, dass _jeder_ etwas davon mitbekommt oder ob es nicht vielleicht
+     sinnvoller ist, das Zielgebiet etwas einzuschraenken. Im Zweifelsfall
+     sollte der zustaendige RM aufpassen, dass die Spieler nicht durch allzu
+     zahlreiche shouts belaestigt werden.
+
+RUeCKGABEWERT:
+     keiner
+
+BEISPIELE:
+     shout( "Du spuerst, wie ein Zittern durch das MorgenGrauen geht.\n" );
+        Der allseits bekannte Text wird an alle Spieler im MG ausgegeben.
+
+     shout( "Du hoerst eine gewaltige Explosion.\n", 1 );
+        Von der Explosion bekommen alle Spieler in derselben Gegend etwas mit,
+        aber nicht am anderen Ende des Muds.
+
+     shout( "Irgendwo ist ein Baum umgefallen.\n", "/d/vland/" );
+        ... gibt eine Meldung aus, die keinen wirklich interessiert. Aber es
+        trifft eh nur Leute in einem unwichtigen Teil des MorgenGrauen. ;-)
+
+     shout( "Aufwachen Du Idler!\n", "/players/.*/workroom" );
+        Mit Hilfe von regular expressions kann man recht einfach z.B. alle
+        Workrooms auf einmal adressieren.
+
+     shout( "Halloooooo, Echoooooo!\n", ({ "/d/gebirge/", "/d/ebene/" }) );
+        Wenn der Spieler hoch oben auf dem Berg laut genug ruft, hoert man
+        ihn auch noch weit in der Ebene.
+
+SIEHE AUCH:
+     write(), say(), tell_object(), tell_room(), regexp()
+
+----------------------------------------------------------------------------
+Last modified: Sun Nov 28 03:00:00 1999 by Tiamak
diff --git a/doc/sefun/time2string b/doc/sefun/time2string
new file mode 100644
index 0000000..4dae719
--- /dev/null
+++ b/doc/sefun/time2string
@@ -0,0 +1,53 @@
+FUNKTION:
+	string time2string( string format, int time )
+	
+ARGUMENTE:
+	format: String, der das Format der Zeitausgabe festlegt.
+	time: Eine Anzahl von Sekunden, die ausgegeben werden soll.
+
+ERGEBNIS:
+	Zeit in String-Form.
+
+BESCHREIBUNG:
+	Der Formatstring wird zurueckgegeben, wobei bestimmte Ersetzungs-
+	symbole durch passende Daten, die aus der Zeit berechnet werden,
+	ersetzt werden. Die Ersetzungssymbole funktionieren aehnlich
+	denen aus 'printf' bekannten Symbolen. Insbesondere kann eine
+	Feldbreite mit angegeben werden.
+
+	Folgende Ersetzungssymbole sind erlaubt:
+	%%	wird durch ein Prozent (%) ersetzt.
+	%n, %w, %d, %h, %m, %s
+		  werden durch die Anzahl der Monate, Wochen, Tage, Stunden, Minuten oder
+      Sekunden ersetzt. Die Funktion erkennt, welches die groesste benutzte
+		  Zeiteinheit ist und rechnet die keineren so um, dass sie zwischen 0 und
+      jeweiligen Maximum der Zeiteinheit liegen (59, 23 etc.) liegen.
+ 	%N	wird durch die Worte 'Woche' oder 'Wochen' ersetzt,
+      je nachdem welchen Wertd %n haette.
+ 	%W	wird durch die Worte 'Woche' oder 'Wochen' ersetzt,
+		  je nachdem welchen Wert %w haette.
+	%D	wird durch die Worte 'Tag' oder 'Tage' ersetzt,
+		  je nachdem welchen Wert %d haette.
+	%H,%M,%S
+		  werden durch die Worte 'Stunde(n)', 'Minute(n)' bzw. 'Sekunde(n)'
+      ersetzt.
+	%X	wird durch die groesste Zeiteinheit ersetzt, die nicht Null ist. Wenn
+      bei %X die Feldbreite 0 angegeben wird (also %0X), dann wird nicht der
+      ausgeschriebene Name, sonder eine Abkuerzung fuer die Zeiteinheit
+      ausgegeben. (Das sind dann 'd','h','m' oder 's'.)
+	%x	wird durch den numerischen Wert dieser Zeiteinheit
+		  ersetzt.
+			
+BEISPIELE:
+	time2string( "%m %M", 60 ) -> "1 Minute"
+	time2string( "%m %M", 120 ) -> "2 Minuten"
+	time2string( "%s %S", 125 ) -> "125 Sekunden"
+	time2string( "%m %M und %s %S" ) -> "2 Minuten und 5 Sekunden"
+	time2string( "%d:%02h:%02m:%02s", 10000 ) -> "0:02:46:40"
+	time2string( "%x %X", 3600 ) -> "1 Stunde"
+	time2string( "%x %0X", 3600 ) -> "1 h"
+	time2string( "%x %X", 360000 ) -> "4 Tage"
+	time2string( "%x %0X", 360000 ) -> "4 d"
+
+SIEHE AUCH:
+	sprintf(E)
diff --git a/doc/sefun/update_actions b/doc/sefun/update_actions
new file mode 100644
index 0000000..b28cf9f
--- /dev/null
+++ b/doc/sefun/update_actions
@@ -0,0 +1,38 @@
+FUNKTION:
+        void update_actions()
+
+ARGUMENTE:
+        keine
+
+BESCHREIBUNG:
+        Falls eine Aktion ein add_action() ausgeloest hat, werden mit dieser
+        Funktion die neuen Befehle bei allen Lebewesen im aufrufenden Objekt
+        bzw. in der Umgebung des aufrufenden Objektes aktiv.
+
+RUECKGABEWERT:
+        keiner
+
+BEMERKUNGEN:
+        Diese Funktion wird eigentlich nur benoetigt, wenn man mit add_action()
+        anstelle von AddCmd() arbeitet (zB. bei Objekten, die nicht
+        /std/thing/commands inheriten).
+
+BEISPIELE:
+        /* Normalerweise sollte man es SO gerade nicht machen. Stattdessen
+         * sollte die "kletter"-Funktion pruefen, ob die Luke geoeffnet ist, 
+         * und sich im Fehlerfall beschweren.
+         * So aber dient es als schoenes Beispiel fuer update_actions() ;)
+         */
+        int oeffne(string str)
+        {
+          if( str == "luke" ) {
+            write( "Du oeffnest die Luke. Du kannst jetzt nach unten klettern.\n");
+            add_action("kletter", "kletter", 1);
+            update_actions();
+            return 1;
+          }
+          return 0;
+        }
+
+SIEHE AUCH:
+  add_action(E), AddCmd(L), RemoveCmd(L)
diff --git a/doc/sefun/upperstring b/doc/sefun/upperstring
new file mode 100644
index 0000000..665ed16
--- /dev/null
+++ b/doc/sefun/upperstring
@@ -0,0 +1,17 @@
+FUNKTION:
+	string upperstring(string str)
+
+ARGUMENTE:
+	str - Der umzuwandelnde String.
+
+BESCHREIBUNG:
+	Alle Kleinbuchstaben in str werden in Grossbuchstaben umgewandelt.
+
+RUECKGABEWERT:
+	Der umgewandelte String.
+
+BEISPIELE:
+	s = upperstring( "Na, Sterblicher!\n") => s = "NA, STERBLICHER!\n"
+
+SIEHE AUCH:
+	lowerstring(E), lower_case(E), capitalize(E)
diff --git a/doc/sefun/uptime b/doc/sefun/uptime
new file mode 100644
index 0000000..ee7a536
--- /dev/null
+++ b/doc/sefun/uptime
@@ -0,0 +1,16 @@
+FUNKTION:
+        string uptime()
+
+ARGUMENTE:
+        keine
+
+BESCHREIBUNG:
+        Liefert die aktuelle Uptime des MorgenGrauens.
+
+RUECKGABEWERT:
+        Die Uptime als Zeitstring.
+
+BEISPIELE:
+        ut = uptime() => ut = "14 Tage, 9 Stunden, 3 Minuten und 7 Sekunden."
+
+SIEHE AUCH:
diff --git a/doc/sefun/wizlist b/doc/sefun/wizlist
new file mode 100644
index 0000000..a2ba7bd
--- /dev/null
+++ b/doc/sefun/wizlist
@@ -0,0 +1,66 @@
+SYNOPSIS:
+  varargs void wizlist(string name, int sortkey ) 	
+
+DESCRIPTION:
+  wizlist() liefert eine Liste mit verschiedenen Verbrauchsdaten 
+	zu Magiern. Es werden dabei normalerweise 21 Eintraege ausgegeben:
+  10 vor <name>, <name> selbst und 10 nach <name>.
+
+  Die Liste ist dabei folgendermassen aufgebaut:
+
+	1. WL_NAME
+	   Gesammelt werden die Daten pro UID. Hierbei zaehlt aber jede EUID, nicht
+     nur die von Magiern.
+     Das heisst, Objekte unter /d/polar/vanion/eispalast
+	   werden nicht unter "vanion" sondern unter "d.polar.vanion"
+	   abgerechnet.
+
+	2. WL_COMMANDS
+     Anzahl der diesem Objekt zugeordneten commands.
+
+	3. WL_COMMANDS * 100 / total_cmd 
+     Prozentualer Anteil an den gesamt verbrauchten commands.
+
+	4. WL_COST (links in der eckigen Klammer)
+     Anzahl der verbrauchten eval ticks. Dies ist zeitlich gewichtet, d.h.
+     nimmt im Lauf der Zeit ab, wenn nichts mehr dazu kommt.
+
+  5. WL_TOTAL_GIGACOST (rechts in der eckigen Klammer)
+     Anzahl der insgesamt verbrauchten eval ticks in Giga-Ticks.
+     Nicht zeitlich gewichtet.
+
+	6. WL_HEART_BEATS
+     Anzahl der ausgeloesten heart beats.
+  
+  7. WL_ARRAY_TOTAL
+
+  8. WL_MAPPING_TOTAL
+
+PARAMETERS: 
+  Wird name angegeben, wird erzwungen, dass die erwaehnte EUID mit
+  in der Liste dargestellt wird. Wird name nicht angegeben, wird es
+  automatisch auf this_player()->query_real_name() gesetzt.
+
+  Wird als name "TOP100" angegeben, wird die Top-100-Liste ausgegeben.
+
+  Wird als name "ALL" angegeben, wird die vollstaendige Liste aus-
+  gegeben.
+
+  Durch Angabe von sortkey kann die Liste nach einer der Spalten 
+  sortiert werden. Gueltige Werte sind die in /sys/wizlist.h ange-
+  gebenen Defines.
+	
+EXAMPLE: 
+  > xeval wizlist("vanion", WL_HEART_BEATS)
+      Erzwingt Vanions Eintrag in der Liste und sortiert die Liste anhand
+      der durch die EUIDs ausgefuehrten heart beats.
+
+  > xeval wizlist("ALL", WL_EVAL_COST)
+      Zeigt die komplette Liste nach eval ticks-Verbauch sortiert an.
+
+SEE ALSO:
+      wizlist_info(E)
+
+LAST UPDATED:
+  09.05.2015, Zesstra
+
diff --git a/doc/sefun/wizlist_info b/doc/sefun/wizlist_info
new file mode 100644
index 0000000..2a5c127
--- /dev/null
+++ b/doc/sefun/wizlist_info
@@ -0,0 +1,39 @@
+GESCHUETZT
+SYNOPSIS
+        #include <sys/wizlist.h>
+
+        *mixed wizlist_info();
+
+BESCHREIBUNG
+        Liefert ein Array mit Eintraegen aus der Wizlist (der internen
+        Magierliste). Die Benutzung muss durch das Masterobjekt erlaubt
+        werden.
+
+        Das Resultat ist ein Array mit einem Eintrag fuer jeden Magier (uid).
+        Jeder Eintrag enthaelt wiederum ein Array mit folgenden Elementen:
+
+            string  w[WL_NAME]          Name des Magiers
+            int w[WL_COMMANDS]          Gewichtete Anzahl Kommandi, die von
+                                        Objekten dieses Gottes ausgefuehrt
+                                        wurden
+            int w[WL_COSTE]             Gewichtete Summe der Eval-Kosten
+            int w[WL_GIGACOST]          Gewichtete Summe der Eval-Kosten
+            int W[WL_TOTAL_COST]        Totale Summe der Eval-Kosten
+            int w[WL_TOTAL_GIGACOST]    Totale Summe der Eval-Kosten
+            int w[WL_HEART_BEAT]        Gewichtete Anzahl der heat_beat()s
+            int w[WL_CALL_OUT]          Reserviert fuer call_out()s
+                                        (bisher nicht implementiert)
+            int w[WL_ARRAY_TOTAL]       Totale Groesse aller Arrays in
+                                        Elementen
+            mixed w[WL_EXTRA]           Die eigentliche Wizlist-Info
+
+        Die "gewichteten" Werte verfallen pro Stunde um 10%.
+
+AENDERUNGEN
+        LDMud 3.2.10 trennte das alte WL_EVAL_COST in WL_COST und WL_GIGACOST,
+        um laengeren Uptimes gerecht zu werden. Ausserdem wurde
+        WL_TOTAL_COST und WL_TOTAL_GIGACOST eingefuehrt.
+
+SIEHE AUCH
+        privilege_violation(M), set_extra_wizinfo_size(E),
+        get_extra_wizinfo(E), set_extra_wizinfo(E)
diff --git a/doc/std/.synonym b/doc/std/.synonym
new file mode 100644
index 0000000..508ee32
--- /dev/null
+++ b/doc/std/.synonym
@@ -0,0 +1,9 @@
+armours armour
+ruestung armour
+ruestungen armour
+weapons weapon
+waffen weapon
+waffe weapon
+units unit
+vc virtual_compiler
+ringbuffer util
diff --git a/doc/std/armour b/doc/std/armour
new file mode 100644
index 0000000..9721cfc
--- /dev/null
+++ b/doc/std/armour
@@ -0,0 +1,87 @@
+ STANDARDKLASSE:
+	"/std/armour"
+ 
+ BENUTZUNG:
+        inherit "/std/armour";
+ 
+        #include <properties.h>
+        #include <combat.h>
+
+ PROPERTIES:
+     Grundlegend:
+	P_AC		setzbar: Ruestungsklasse == Schutz
+	P_ARMOUR_TYPE	setzbar: Ruestungstyp
+
+     Besondere Attribute und Anforderungen fuer Traeger:
+	P_RESTRICTIONS	setzbar: Anforderungen an Traeger
+	P_M_ATTR_MOD	setzbar: Attributmodifikator fuer Traeger
+	P_RESISTANCE_STRENGTHS
+			setzbar: Resistenzmodifikator fuer Traeger
+	P_NR_HANDS	setzbar: notwendige Handanzahl (zB Schilde)
+	P_CURSED	setzbar: Verfluchung (nicht ausziehbar)
+
+     Meldungen und Zeitpunkte:
+	P_EQUIP_TIME	enthaelt den Zeitpunkt des Anziehens
+	P_LAST_USE	enthaelt den Zeitpunkt des letzten Treffers
+	P_WORN		enthaelt den Traeger
+	P_WEAR_MSG	setzbar: eigene Anziehmeldungen
+	P_UNWEAR_MSG	setzbar: eigene Ausziehmeldungen
+
+     Dynamisches Verhalten in Kampf und beim Anziehen:
+	P_WEAR_FUNC	setzbar: Objekt mit Anziehfunktion "WearFunc()"
+	P_REMOVE_FUNC	setzbar: Objekt mit Ausziehfunktion "RemoveFunc()"
+	P_DEFEND_FUNC	setzbar: Objekt mit Defensivfunktion "DefendFunc()"
+
+     Zusaetzliche Eigenschaften:
+	P_DAMAGED	enthaelt den Ausmass des Schadens an Waffe
+	P_QUALITY	setzbar: Qualität/Haltbarkeit der Waffe
+	P_EFFECTIVE_AC	setzbar: falls DefendFunc AC nicht sichbar aendert
+	P_DAM_TYPE	setzbar: interessant bei Kaempferangriffen
+
+     Zusaetzlich sind alle Properties aus /std/thing verfuegbar, also
+     bitte auch folgende setzen:
+	P_MATERIAL	setzbar: Zusammensetzung
+	P_SIZE		setzbar: Groesse
+	P_WEIGHT	setzbar: Gewicht
+
+ MAKROS:
+     Gueltige Ruestungstypen (definiert in "/sys/combat.h").
+ 
+ ERLAEUTERUNG:
+     Basisklasse fuer alle Ruestungen im Spiel. Sie ist von "/std/clothing"
+     abgeleitet und enthaelt alle zusaetzliche Funktionalitaet
+     fuer den Kampf.
+
+     Vor der Programmierung von Ruestungen sollte /doc/MG/waffen_werte
+     gelesen werden. Die Regeln darin sind verbindlich und sollten nur
+     in Ausnahmefaellen und mit Absprache mit dem Erzmagier fuer 
+     Waffen/Ruestungen/Monster ueberschritten werden.
+     Gerade in DefendFuncs sollte auf die korrekte Behandlung der
+     uebergebenen Spellflags geachtet werden - insbesondere SP_RECURSIVE
+     und SP_NO_ACTIVE_DEFENSE.
+
+     Die Maximalwerte und Werte fuer Zerstoerung bei Verkauf sind
+     /sys/combat.h zu entnehmen: VALID_ARMOUR_CLASS/KEEP_ARMOUR_CLASS.
+
+ VERERBUNGSBAUM:
+     [/std/armour]
+     ..... [/std/thing/properties]
+     ..... [/std/thing/language]
+     ..... [/std/thing/commands]
+     ..... [/std/thing/restrictions]
+     ..... [/std/thing/envchk]
+     ..... [/std/armour/description]
+     .......... [/std/clothing/description]
+     .............. [/std/thing/description]
+     ..... [/std/armour/wear]
+     .......... [/std/clothing/wear]
+     ..... [/std/clothing/moving]
+     .......... [/std/thing/moving]
+     ..... [/std/armour/combat]
+
+ SIEHE AUCH:
+     P_ARMOURS, P_LAST_WEAR_ACTION, P_TOTAL_AC
+     Attack(), Defend(), /doc/wiz/ruestungen, balance
+
+03.08.2007, Zesstra
+
diff --git a/doc/std/clothing b/doc/std/clothing
new file mode 100644
index 0000000..91ccb55
--- /dev/null
+++ b/doc/std/clothing
@@ -0,0 +1,77 @@
+ STANDARDKLASSE:
+	"/std/clothing"
+ 
+ BENUTZUNG:
+        inherit "/std/clothing";
+ 
+        #include <properties.h>
+        #include <clothing.h>
+
+ PROPERTIES:
+
+     Besondere Attribute und Anforderungen fuer Traeger:
+	P_RESTRICTIONS	setzbar: Anforderungen an Traeger
+	P_CURSED	setzbar: Verfluchung (nicht ausziehbar)
+
+     Meldungen und Zeitpunkte:
+	P_EQUIP_TIME	enthaelt den Zeitpunkt des Anziehens
+	P_WORN		enthaelt den Traeger
+	P_WEAR_MSG	setzbar: eigene Anziehmeldungen
+	P_UNWEAR_MSG	setzbar: eigene Ausziehmeldungen
+
+     Dynamisches Verhalten in Kampf und beim Anziehen:
+	P_WEAR_FUNC	setzbar: Objekt mit Anziehfunktion "WearFunc()"
+	P_REMOVE_FUNC	setzbar: Objekt mit Ausziehfunktion "RemoveFunc()"
+
+     Zusaetzlich sind alle Properties aus /std/thing verfuegbar, also
+     bitte auch folgende setzen:
+	P_MATERIAL	setzbar: Zusammensetzung
+	P_SIZE		setzbar: Groesse
+	P_WEIGHT	setzbar: Gewicht
+
+ 
+ ERLAEUTERUNG:
+     Basisklasse fuer alle Kleidungen im Spiel. Sie ist von "/std/thing"
+     abgeleitet.
+     Kleidung belegt niemals einen Ruestungsslot und ist nur fuer Kram
+     gedacht, der keine kampfrelevante Funktionalitaet beeinhaltet.
+     Momentan ist die Anzahl an Kleidungsstuecken, die ein Spieler anziehen
+     kann, nicht begrenzt.
+     Kleidung sind im Prinzip Ruestungen ohne alle in den Ruestungen
+     enthaltene kampfrelevante Funktionalitaet.
+     (BTW: Kleidungen koennen allerdings, sofern unbedingt notwendig, mittels
+      P_SENSITIVE, P_SENSITIVE_ATTACK, etc. auch indirekt am Kampf teilnehmen,
+      in dem Sinne, dass sie auf eingehenden Schaden reagieren. Hierbei darf
+      allerdings in keinem Fall in den Kampfverlauf eingegriffen werden.)
+
+     Spezielle Properties:
+     SetProp(P_WEAR_FUNC, ob)
+	Setzt das Objekt, in dem die Funktion WearFunc() definiert
+	wird. WearFunc() wird beim Versuch die Kleidung anzuziehen
+	aufgerufen, gibt sie 0 zurueck so scheitert der Versuch.
+     SetProp(P_REMOVE_FUNC, ob)
+	Setzt das Objekt, in dem die Funktion RemoveFunc() definiert
+	ist. Die Funktion wird beim Versuch die Kleidung auszuziehen
+	aufgerufen, gibt sie 0 zurueck, so scheitert der Versuch.
+
+
+ VERERBUNGSBAUM:
+     [/std/clothing]
+     ..... [/std/thing/properties]
+     ..... [/std/thing/language]
+     ..... [/std/thing/commands]
+     ..... [/std/thing/restrictions]
+     ..... [/std/thing/envchk]
+     ..... [/std/clothing/description]
+     .......... [/std/thing/description]
+     ..... [/std/clothing/moving]
+     .......... [/std/thing/moving]
+     ..... [/std/clothing/wear]
+
+ SIEHE AUCH:
+     armours,
+     FilterClothing, FilterArmours, Wear(), Unwear(), WearArmour(),
+     WearClothing(), UnwearArmour(), UnwearClothing() 
+
+15.02.2009, Zesstra
+
diff --git a/doc/std/container b/doc/std/container
new file mode 100644
index 0000000..667078a
--- /dev/null
+++ b/doc/std/container
@@ -0,0 +1,49 @@
+OBJECT:
+	container 
+
+
+DESCRIPTION:
+	A cointainer is a special object which can contain other objects.
+	It provides the same functions as the thing(S) class, but adds
+	some functions for the handling of contents. It defines some
+	properties related to what and how much is contained in it.
+
+	The following additional properties are defined for a container:
+	P_MAX_WEIGHT, P_WEIGHT_CONTENTS
+
+	Note that the weight of a container is handled especially:
+	SetProp(P_WEIGHT,weight) sets the weight of the container
+	without contents, while QueryProp(P_WEIGHT) returns the total
+	weight of the container plus contents.
+
+	For now, you may use the following function to add or remove
+	weight from the container.
+
+	AddWeight(weight)
+	  Adds <weight> to the container. If its OK, 1 is returned,
+	  otherwise 0. Only if the weight fits into the container, the
+	  weight is adjusted.
+
+	MayAddWeight(weight)
+	  Like AddWeight, but if the weight fits inside the container,
+	  the total weight is NOT updated. You may use this function to
+	  test if an object fits into a container.
+
+	NOTE: Currently, these functions may be called from outside.
+	In a alter stage, movement of objects will be done by calling
+	the move() function in the object to be moved. The move()
+	function of that object will then call (May)AddWeight() itself
+	to determine if it may enter the container.
+
+
+INHERITANCE TREE:
+	container
+	  |-thing/moving
+	  |-thing/properties
+	  |-container/description
+	  |   `-thing/description
+	  `-container/restrictions
+
+
+SEE ALSO:
+	thing(S)
diff --git a/doc/std/corpse b/doc/std/corpse
new file mode 100644
index 0000000..d4e2d47
--- /dev/null
+++ b/doc/std/corpse
@@ -0,0 +1,76 @@
+OBJEKT:
+      corpse
+
+
+SYNOPSIS:
+      inherit "std/corpse";
+
+
+BESCHREIBUNG:
+      Die Standardleiche ist ein spezieller Container. Er beinhaltet die
+      zurueckgelassene Ausruestung gestorbener NPCs und Spieler.
+      Die Standardleiche verfault mit der Zeit und stellt ein paar
+      Funktionalitaeten zur Verfuegung, naemlich das Essen der Leiche
+      und das Verspotten auf dem Moerderkanal.
+      Die Funktion
+        void Identify( object ob );
+      wird im die() eines Livings aufgerufen, damit die Leiche Informationen
+      aus dem Living auslesen kann, ehe dieser endgueltig stirbt. Schreibt
+      man eine eigene Leiche, dann ist dies die geeignete Moeglichkeit, der
+      Leiche Informationen zukommen zu lassen.
+
+
+BEISPIELE:
+      // Eine eigene Leiche.
+      #inherit "std/corpse";
+
+      void create()
+      {
+        ::create();
+        // Irgendwas eigenes...
+      }
+
+      /*
+       * Dieser Funktion wird der getoetete Living uebergeben.
+       */
+      void Identify(object ob)
+      {
+        ::Identify(ob);
+        if (ob)
+        {
+          ob->QueryProp("meine_tolle_property");
+        }
+      }
+
+      /*
+       * Das ist die für "iss leiche" aufgerufene Funktion.
+       */
+      int mampf( string str )
+      {
+        notify_fail("Was willst Du essen?\n");
+        if (!str || !id(str) ) return 0;
+        tell_object(this_player(), "Diese Leiche kann man nicht essen.\n");
+        return 1;
+      }
+
+
+
+BEMERKUNG:
+      Man kann eigene Leichen schreiben, die von der Standardleiche erben
+      oder auch nicht. Man sollte dann aber die im die() aufgerufenen
+      Funktionen implementieren:
+        corpse->Identify(object ob);
+	corpse->SetProp(***); // Fuer P_HEAL
+	corpse->move(***); // Um sie ins environment() des Toten zu bekommen.
+      Die Leiche sollte, muss aber kein Container sein, damit die Ausruestung
+      hineinbewegt werden kann.
+      Wenn man nicht von der Standardleiche erbt, kommt sie nicht auf -Moerder
+      und man kann sie nicht verspotten. Damit dies geht, muss (!) man von der
+      Standardleiche erben.
+
+SIEHE AUCH:
+      P_NOCORPSE, P_CORPSE, P_HEAL, QueryHealInfo()
+
+
+----------------------------------------------------------------------------
+Letzte Aenderung: 31.03.2008, Arathorn
diff --git a/doc/std/hooks b/doc/std/hooks
new file mode 100644
index 0000000..de308bd
--- /dev/null
+++ b/doc/std/hooks
@@ -0,0 +1,278 @@
+Das neue Hooksystem
+(Implementierung von Muadib, ueberarbeitet von Zesstra)
+
+EINLEITUNG
+==========
+Das neue Hooksystem baut nicht mehr auf der Eintragung eines Hooks in einer
+Property auf. Dadurch wird es moeglich, dass nicht nur ein Objekt sich als
+Hook eintraegt.
+
+Es gibt verschiedenen Arten von Hooks, die man eintragen kann:
+* Listener (H_LISTENER)
+            diese Hooks werden ueber ein Ereignis nur informiert,
+            koennen aber nicht eingreifen oder aendern.
+            max. Anzahl: 5
+* Data-Modifier (H_DATA_MODIFICATOR)
+            diese Hooks duerfen die Daten eines Ereignisses aendern.
+            max. Anzahl: 3
+* Hook-Modifier (H_HOOK_MODIFICATOR)
+            diese Hooks duerfen die Daten des Ereignisses aendern und
+            zusaetzlich das Ereignis auch abbrechen.
+            max. Anzahl: 2
+* Surveyor (H_HOOK_SURVEYOR)
+            diese Hooks duerfen alles oben beschriebene. Zusaetzlich werden
+            sie aber auch gefragt, wenn andere Objekte einen Hook eintragen
+            wollen, einen Hook abbrechen wollen oder Daten aendern wollen.
+            Oder anders: Surveyorhooks entscheiden, was andere duerfen.
+            Kein normales Objekte sollte diese Art von Hook eintragen. Der RM
+            muss die Verwendung eines Surveyors genehmigen.
+            max. Anzahl: 1
+
+Ausserdem lassen sich Hooks noch mit unterschiedlicher Prioritaet eintragen,
+welche dann in der entsprechenden Reihenfolge abgearbeitet werden. Wenn ein
+neuer Hook eingetragen wird, aber eigentlich die max. Anzahl schon erreicht
+wird, wird der Konsument mit der niedrigsten Prioritaet geloescht. In diesem
+Fall wird die superseededHook() im verdraengten Konsumenten gerufen.
+
+Um das neue Hook-System zu realisieren, gibt es zwei wesentliche Klassen.
+Die Objekte, die die Eintragung von Hooks erlauben, erben hierzu von
+hook_provider. Objekte, die einen Surveyor-Hook eintragen wollen, sollten von
+der Klasse hook_surveyor erben. Objekte mit normalen Hooks brauchen nichts zu
+erben.
+
+
+Welche Hooks gibt es zur Zeit in der Basis-Mudlib?
+=================================================
+* H_HOOK_MOVE
+  Bei Bewegung eines Objektes ausgeloest. Kann Bewegung beeinflussen oder
+  abbrechen.
+* H_HOOK_DIE
+  Beim Tod eines Lebewesens ausgeloest. Kann den Tod abbrechen oder
+  <poisondeath> abaendern.
+* H_HOOK_DEFEND
+  Im Defend() eines Lebenwesens ausgeloest. Kann das Defend() abbrechen und
+  Daten des Defend() aendern.
+* H_HOOK_ATTACK
+  Im Attack() eines Lebenwesens ausgeloest. Kann das Attack() abbrechen.
+* H_HOOK_HP
+  Bei Veraenderung der HP (LP) eines Lebewesens gerufen. (Zur Zeit keine
+  Datenveraenderung moeglich)
+* H_HOOK_SP
+  Bei Veraenderung der SP (KP) eines Lebewesens gerufen. (Zur Zeit keine
+  Datenveraenderung moeglich)
+* H_HOOK_ATTACK_MOD
+  Wird im Attack() ausgeloest, nachdem die fuer den Angriff wichtigen Daten
+  ermittelt und berechnet wurden. Diese koennen dann vom Hookonsumenten
+  nochmal geaendert werden. Auch ein Abbruch des Attack() ist hier noch
+  moeglich.
+* H_HOOK_ALCOHOL
+  Bei Veraenderung von P_ALCOHOL im Lebewesens gerufen.
+* H_HOOK_FOOD
+  Bei Veraenderung von P_FOOD im Lebewesens gerufen.
+* H_HOOK_DRINK
+  Bei Veraenderung von P_DRINK im Lebewesens gerufen.
+* H_HOOK_POISON
+  Bei Veraenderung von P_POISON im Lebewesens gerufen.
+* H_HOOK_CONSUME
+  Beim Konsumieren von Speisen und Getraenken in Kneipen im Lebewesens
+  gerufen.
+* H_HOOK_TEAMROWCHANGE
+  Bei Teamreihenwechsel vom Lebewesen ausgeloest.
+* H_HOOK_INSERT
+  Wird von Spielerobjekten ausgeloest, wenn ein Objekt ins Spielerinventar
+  bewegt wird. (Keine Datenveraenderung moeglich)
+* H_HOOK_EXIT_USE
+  Wird von einem Raum ausgeloest, wenn ein Lebewesen einen Ausgang benutzt.
+  Abbruch und Aenderung der Daten des Ausgangs moeglich.
+
+HOOK-KONSUMENTEN
+================
+Der Hook-Provider ruft bei Ausloesen des Hooks in allen Konsumenten eine
+bestimmte Methode auf. Wenn bei der Registrierung eines Objekts keine Closure
+angeben wurde, die in diesem Fall gerufen werden, wird standardmaessig die
+lfun HookCallback() gerufen (gibt man eine Closure an, bekommt sie die
+gleichen Argumente und es werden die beschriebenen Rueckgabewerte erwartet):
+  * mixed HookCallback(object hookSource, int hookid, mixed hookData)
+  Diese Methode wird in jedem Hook-Konsumenten eines Hook-Providers
+  aufgerufen, solange die Verarbeitung nicht vorher abgebrochen wurde.
+  Die Reihenfolge der Abarbeitung wird nach Liste (Surveyor,
+  Hook-Modifikator, Data-Modifikator, Listener) und dort nach Prioritaet
+  durchgefuehrt.
+  Ein Surveyor-Hook kann verhindern, dass Hooks bestimmte Aenderungen
+  durchfuehren.
+
+  Rueckgabewert ist ein Array, das die folgenden Werte beinhaltet.
+
+  H_RETCODE Gibt an, welcher Hook-Typ verwendet wurde.
+      H_NO_MOD => Nichts wurde veraendert.
+      H_ALTERED => Daten wurden veraendert.
+      H_CANCELLED => Hook-Kette soll abgebrochen werden.
+                  => Ausserdem soll die Hook-ausloesende Stelle
+                     abgebrochen werden. Z.B. wird das Defend()
+                     abgebrochen, wenn ein H_HOOK_DEFEND
+                     mit cancelled beantwortet wird.
+  H_RETDATA Gibt die (evtl. geaenderten) Daten an.
+      mixed-Objekt, das wie der Parameter hookData aufgebaut ist.
+
+Ein Objekt darf sich mehrfach fuer den gleichen Hook registrieren. Allerdings
+ist fuer jede Registrierung eine andere Closure noetig.
+
+  * void superseededHook(int hookid, object hookprovider)
+    Wird gerufen, wenn der Konsument von einem anderen mit hoeherer Prioritaet
+    verdraengt wurde.
+
+
+HOOK-PROVIDER
+=============
+  Der Hook-Provider bietet eine Menge von Methoden an, die eine Konfiguration
+  ermoeglichen und die Eintragung von Hook-Konsumenten erlauben. Im
+  Normalfall sollte er geerbt und nicht modifiziert werden (ausser natuerlich,
+  die vom Objekte bereitgestellten Hooks einzutragen).
+
+  * int* HListHooks();
+  Diese Methode liefert eine Liste von Hooktypen, fuer die das Objekt
+  Eintragungen annimmt. Hier koennte beispielsweise eine Liste mit den
+  Eintraegen fuer Attack-, Defend- und Move-Hooks stehen.
+
+
+  * protected void offerHook(int hookid, int offerstate);
+  Diese Methode dient dazu, einen bestimmten Hook (z.B. H_HOOK_MOVE)
+  anzubieten. Nur Hooks, die hiermit angeboten wurden, stehen zur
+  Registrierung zur Verfuegung.
+  'offerstate': 0 (nicht verfuegbar), 1 (verfuegbar/angeboten)
+
+
+  * int HRegisterToHook(int hookid, mixed consumer, int hookprio,
+                       int consumertype, int timeInSeconds);
+  Registriert ein Objekt oder eine Closure als Hook-Konsument.
+  Parameter:
+  'hookid'        gibt den Hook-Typ an, z.B. den Defend-Hook.
+                  Man kann sich nur fuer Hooktypen eintragen, die die Methode
+                  HListHooks() angeboten hat.
+  'consumer'      Wenn ein Objekt, wird das Objekt eingetragen und spaeter
+                  HookCallback() gerufen.
+                  Wenn eine Closure, wird das Objekt der Closure eingetragen
+                  und spaeter diese Closure gerufen.
+  'hookprio'      Gibt die Prioritaet an, mit der der Hook laufen soll.
+                  Diese Angabe bestimmt die Reihenfolge, in der die Hooks
+                  in der Liste der Hooks eingetragen werden. Die Prioritaet
+                  ist H_HOOK_LIBPRIO(x), H_HOOK_GUILDPRIO(x) oder
+                  H_HOOK_OTHERPRIO(x). x darf 0, 1 oder 2 sein (je niedriger,
+                  desto hoeher die Prioritaet).
+  'consumertype'  Gibt an, um welche Art von Hook es sich handelt.
+                  Es gibt vier festgelegten Typen, die fuer alle Hooks
+                  existieren koennen. Die Methode HConsumerTypeIsAllowed()
+                  gibt Aufschluss darueber, welche Hook-Typen existieren.
+                  Die Hook-Typen sind in hook.h definiert.
+  'timeInSeconds' gibt die Laufzeit des Hooks an. Falls 0 eingetragen wird,
+                  laeuft der Hook ewig.
+  Rueckgabewerte:
+    1 - Registrierung erfolgreich
+  <=0 - Registrierung fehlgeschlagen:
+        -1 : Hook unbekannt
+        -2 : consumer ist keine closure und es konnte kein Callback auf
+             HookCallback im consumer erstellt werden.
+        -3 : consumer ist bereits registriert
+        -4 : consumertyp ist nicht erlaubt
+        -5 : hookprio ist nicht erlaubt
+        -6 : Surveyor hat Registrierung nicht erlaubt
+        -7 : zuviele Hooks registriert / kein Hookeintrag frei
+
+  * int HUnregisterFromHook(int hookid, mixed consumer);
+  Hebt die Registrierung von <consumer> fuer einen bestimmten Hook-Typ wieder
+  auf.
+  Parameter:
+  'hookid'    Die Kennung des Hook-Typs, z.B. die Kennung des Attack-Hooks.
+  'consumer'  Das Objekt oder die Closure, die/das nicht mehr registriert sein
+              soll. Bei einer Closure wird genau diese ausgetragen. Bei der
+              Angabe eines Objekts wird versucht, die Closure auf
+              HookCallback() in diesem Objekt auszutragen.
+  Rueckgabewerte:
+  0 - 'consumer' nicht als Konsument gefunden
+  1 - Austragen erfolgreich
+
+  * int HConsumerTypeIsAllowed(int type, object consumer);
+  Diese Methode liefert 1 zurueck, wenn ein bestimmter Konsumenten-Typ
+  (fuer diesen Konsumenten) erlaubt wird.
+  Die Standardmethode liefert immer 1 (true) zurueck. Erbende Objekte
+  koennen diese Methode ueberschreiben, wenn sie nicht alle Hooktypen
+  anbieten.
+
+
+  * int HPriorityIsAllowed(int prio, object consumer);
+  Diese Methode gibt an, ob eine bestimmte Prioritaet (fuer den angegebenen
+  Konsumenten) erlaubt ist. Die Standardmethode liefert immer 1 (true)
+  zurueck. Erbende Objekte koennen diese Methode ueberschreiben, wenn
+  sie die verfuegbaren Hook-Prioritaeten einschraenken wollen.
+
+
+  * int HIsHookConsumer(int hookid, mixed consumer);
+  Ist <consumer> ein Objekt, liefert die Methode die Anzahl, wie oft dieses
+  Objekt (mit verschiedenen Closures) fuer den Hook <hookid> eingetragen ist.
+  Ist <consumer> eine Closure, liefert diese Methode 1, wenn diese
+  Closure fuer den Hook <hookid> eingetragen ist.
+
+
+  * protected mapping HCopyHookMapping();
+  Diese Methode liefert eine Kopie des Hook-Mappings.
+  ACHTUNG: diese Daten sollten das Objekt NIEMALS verlassen. (Ausser fuer
+           Debugzwecke)
+
+
+
+HOOK-SURVEYOR
+=============
+  Objekte mit Surveyorhooks muessen eine Menge von Methoden definieren, die
+  der Hookprovider aufruft:
+
+  * status HookRegistrationCallback(
+                object registringObject,
+                int hookid,
+                object hookSource,
+                int registringObjectsPriority,
+                int registringObjectsType)
+  Diese Methode wird vom Hook-Provider aufgerufen, wenn der Hook-Konsument
+  als Surveyor eingetragen ist und ein weiterer Hook eingetragen werden soll.
+  Gibt diese Methode 0 zurueck, dann verbietet der Konsument, dass der
+  andere Konsument als Hook eingetragen wird.
+
+  * int HookCancelAllowanceCallback(
+                object cancellingObject,
+                int hookid,
+                object hookSource,
+                int cancellingObjectsPriority,
+                mixed hookData)
+  Diese Methode wird aufgerufen, um herauszufinden, ob ein bestimmter
+  anderer Hook die Ausfuehrung der Hook-Kette unterbrechen darf.
+  Nur Hooks im Bereich H_HOOK_MODIFICATOR werden der Methode uebergeben.
+
+  * int HookModificationAllowanceCallback(
+                object modifyingObject,
+                int hookid,
+                object hookSource,
+                int modifyingObjectsPriority,
+                mixed hookData)
+  Diese Methode wird aufgerufen, um herauszufinden, ob ein bestimmter
+  anderer Hook die Daten des Hooks veraendern darf oder nicht.
+  Es werden die Hooks in den Bereichen H_HOOK_MODIFICATOR und
+  H_DATA_MODIFICATOR (in dieser Reihenfolge) aufgerufen.
+
+
+WAS KOSTET DAS?
+  Das Ausloesen eines Hooks per HookFlow() kostet 111 Ticks und ca. 7 us, wenn
+  es gar keinen gibt, der drauf lauscht (sozusagen Fixkosten).
+  Pro H_LISTENER kommen dann 31 Ticks und ca. 2 us dazu.
+
+  Gibts einen Surveyor-Hook (der wird dann gefragt, ob andere Objekte die
+  Daten des Hooks aendern oder die Hookverarbeitung abbrechen duerfen):
+  Fixkosten: 155 Ticks, 11 us.
+  Plus pro Data-Modifier:
+  106 Ticks, 5.6 us
+  Plus pro Hook-Modifier, der aber nur Daten aendert:
+  112 Ticks, 6.4 us
+  Und ein Hook-Modifier, der den Hook abbricht:
+  76 Ticks, 4 us
+
+  (Macht der Surveyor natuerlich irgendwas anderes als 'return 1;', wirds
+  natuerlich entsprechend teurer.)
+
diff --git a/doc/std/living b/doc/std/living
new file mode 100644
index 0000000..89d47bd
--- /dev/null
+++ b/doc/std/living
@@ -0,0 +1,120 @@
+
+OBJECT:
+	living 
+
+
+SYNOPSIS:
+	inherit "std/living";
+
+	#include <properties.h>
+	#include <living.h>
+
+
+DESCRIPTION:
+	This is the generic living object which 'lives', can move,
+	can fight.
+
+	It has a number of 'stats', also called 'attributes' in
+	the new system. These include the former stats 
+	strength, dexterity, intelligence and constitution.
+	These attributes determine the life functions and
+	variables like hitpoints and spellpoints, but also the
+	ability to perform certain actions like spellcasting or
+	swordfighting. Attributs are implemented as a property
+	P_ATTRIBUTES. Attributes can be set by using the functions
+
+	SetAttribute(string attribute, int value)
+	  Sets <attribute> to <value>. Certain restrictions might
+	  exist, and the requested attribute might not be set.
+	  Attributes are integer values, and are in the range
+	  of 1 (very bad) to 20 (excellent and highest possible
+	  for a normal human).
+
+	int QueryAttribute(string attribute)
+	  Returns value of <attribute>
+
+	Abilities. A living creature may have special abilities
+	to perform certain actions. These may be very different
+	types of actions, like fighting with a sword, casting
+	a fireball or climbing up a tree. Abilities are
+	implemented as the P_ABILITIES property. An ability may
+	be accompanied by an object which may define special
+	actions and which uses the values of P_ABILITES, for 
+	example a spellbook will store spell levels there or
+	a weapon uses a weapon ability (skill). The abilities
+	can be manipulated:
+
+	SetAbility(string ability, int value)
+	  Sets <ability> to <value>.
+
+	int QueryAbility(string ability)
+	  Ask the value of <ability>.
+
+	Equipment. Weaponclass and armourclass are defined. Unless
+	a weapon is used, the wc is 0. Unless an armour is used,
+	the ac is 0. (P_WC and P_AC)
+
+	Live variables. A living creature has several needs just
+	because it is alive. The complex life functions are
+	reducced to some crucial factors.
+
+	P_HP
+	  These are mainly determined by the constitution and give
+	  the ability of the body to absorb damage. If the
+	  hitpoints go below zero, the creature is dead.
+
+	P_SP
+	  Spellpoints (sometimes called mana) describe the magical
+	  (spiritual) capability of the creature. It is mainly
+	  determined by Intelligence and Wisdom. It is up to the
+	  creature how good it uses the spellpoint resource.
+	  Spellpoints will go down when supernatural actions are
+	  performed by the creature.
+
+	P_MAX_SP, P_MAX_HP
+	  Readonly variable which are computed from attributes.
+
+	P_FOOD, P_DRINK, P_ALCOHOL, (P_POISON)
+	  These three variables describe hunger, thirst and
+	  intoxication by alcohol. The max is dependent on the
+	  constitution of the living, and of the weight.
+	  These variables automatically decrease with time.
+	  When food or drink goes below zero, the creature
+	  suffers hunger or thirst and will lose hitpoints.
+
+	P_MAX_FOOD, P_MAX_DRINK, P_MAX_ALCOHOL
+	  Readonly variables which are computed from attributes.
+
+	P_WEIGHT, P_SIZE
+	  These variables describe the physical proportions of
+	  the creatures body. Both variables are standard properties
+	  and are also used in normal objects.
+
+	P_MAX_WEIGHT 
+	  This is a readonly variable which describes how much
+	  a living creature may carry around.
+
+	P_GENDER
+	  This is obvious. Gender may be MALE, FEMALE or NEUTER,
+	  as defined in <living.h>. Player gender may be only
+	  MALE or FEMALE.
+
+	P_RACE
+	  A string which defines the race.
+
+	P_ALIGNMENT
+	  The alignment of the living, ranging frO
+
+	P_AGE
+	  The age of the living creature in seconds
+	
+
+INHERITANCE TREE:
+	living
+	  |-thing/moving
+	  |-thing/properties
+	  `-thing/description
+
+
+SEE ALSO:
+	properties(C), units(C)
diff --git a/doc/std/obsolete/door b/doc/std/obsolete/door
new file mode 100644
index 0000000..15bad30
--- /dev/null
+++ b/doc/std/obsolete/door
@@ -0,0 +1,22 @@
+****************************************************************************
+************************* VERALTETE LFUN ***********************************
+************************* DO NOT USE!    ***********************************
+****************************************************************************
+
+BITTE STATT DER FUNKTION Door() IMMER  NewDoor() BENUTZEN.
+
+	AddDoor(string command, string roomfile, string doorname,
+		string doordesc, string keyname, int status)
+	  Creates a door which leads in direction <command> to the
+	  room <roomfile>. The door has the short description
+	  <doorname> and the long description <doordesc>, when you
+	  look at it. If a door with the same <doorname> exists in
+	  the other room, the open/close/locked status will be
+	  the same in both rooms at all time. Doors are locked on
+	  reset() if keyname is not 0. If <keyname> is given, the
+	  door is locked and can be opened only with a key which
+	  matches <keyname> with its id(). <status> can be set
+	  to an initial value of DOOR_STATUS_OPEN, DOOR_STATUS_CLOSED
+	  or DOOR_STATUS_LOCKED. These status codes are defined in
+	  <doors.h>.
+
diff --git a/doc/std/pub b/doc/std/pub
new file mode 100644
index 0000000..21544a7
--- /dev/null
+++ b/doc/std/pub
@@ -0,0 +1,95 @@
+Um die Funktionalitaet einer Kneipe nutzen zu koennen, muss der Raum eine
+der folgenden Dateien importieren:
+
+'/std/pub.c'         - die Komplettloesung inclusive der Abhaengigkeit zu
+                       '/std/room'
+
+'/std/room/pub.c'    - die Mindestloesung, bei der man selbst die Abhaengig-
+                       keit zu einem Raum definieren muss
+
+------------------------------------------------------------------------------
+Eine Zusammenfassung der Pub-Properties (fuer genaueres siehe manpages)
+aus '/sys/pub.h'
+
+P_NPC_FASTHEAL       - NPCs tanken hier auf die "schnelle" Art
+
+P_NO_STD_DRINK       - Der Pub soll keine Standard-Getraenke anbieten
+
+P_PUB_UNAVAILABLE    - Meldung, wenn etwas nicht mehr vorhanden ist
+
+P_PUB_NOT_ON_MENU    - Meldung, wenn etwas nicht im Menue steht
+
+P_PUB_NO_MONEY       - Meldung, wenn Spieler nicht genug Geld hat
+
+P_PUB_NO_KEEPER      - Meldung, wenn der P_KEEPER nicht anwesend ist
+
+aus '/sys/room/description.h'
+
+P_KEEPER             - Id des Kneipeninhabers, der anwesend sein muss, 
+                       damit die Kneipe genutzt werden kann
+
+------------------------------------------------------------------------------
+Methoden zur Manipulation des Menues (fuer genaueres siehe manpages)
+
+AddToMenu            - fuegt einen Eintrag zum Menue hinzu
+
+RemoveFromMenu       - entfernt einen Eintrag aus dem Menue
+
+------------------------------------------------------------------------------
+Ablauf und Manipulationsmoeglichkeiten in einer Kneipe
+
+Wunsch wurde ueber 'kaufe', 'bestelle' oder 'spendiere' abgegeben:
+
+search_what()        - prueft, dass nicht mehrere Wuensche auf einmal
+                       geaeussert werden und ruft consume_something() auf
+
+consume_something()  - prueft die Anwesenheit der beteiligten Personen
+                     - prueft die Anwesenheit des Kneipenbesitzers
+                     - prueft das Ignorieren beim Spendieren
+                     - prueft die Verfuegbarkeit von genuegend Geld beim
+                       Besteller ( check_solvency() )
+                     - prueft die Verfuegbarkeit des Gewuenschten
+                       ( CheckAvailability() )
+                     - prueft, ob der Empfaenger in der Lage ist, das
+                       Bestellte zu konsumieren
+                       ( ueber empfaenger->consume(testonly) )
+
+                     - fuehrt die Bezahlung durch ( do_pay() )
+                     - fuehrt die Vergabe von FPs durch ( GiveEP() )
+                     - entnimmt das Bestellte aus der Menge des Angebotes
+                       ( DecreaseAvailibility() )
+                     - prueft, ob eine Verzoegerung der Lieferung erfolgen
+                       soll und uebergibt im Negativfall gleich an
+                       do_deliver()
+                     - Ausgabe der Meldungen zur Bestellung
+                     - verzoegerter Aufruf von do_deliver()
+                     
+do_deliver()         - prueft nochmal die Anwesenheit des Empfaengers
+                     - prueft, ob Heilung erlaubt ist (ueber Pubmaster)
+                     - prueft P_NPC_FASTHEAL und korrigiert die Angaben, die
+                       aus dem Menueeintrag kommen
+                     - fuehrt das Konsumieren beim Empfaenger durch
+                       ( empfaenger->consume() )
+                     - Ausgabe der Meldung zum Konsumieren
+
+pub_init()           - Moeglichkeit fuer Magier, die Werte der angebotenen
+                       Speisen und Getraenke zu ueberpruefen
+                     - erweiterte Moeglichkeiten zum Pruefen von korrekten
+                       Menueeintraegen bietet das Pub-Tool
+                       ( '/obj/tools/pubtool.c' )
+
+Hinweis: Um diese Pruefungen und Aktionen sicher zu gewaehrleisten, sollte
+         man consume_something oder do_deliver NICHT ueberschreiben!
+         Das fuehrt bei Aenderungen mit grosser Wahrscheinlichkeit zu
+         Problemen!
+         Ueberschreibt besser die Methoden, die die Funktionalitaet
+         beinhaltet, die ihr wirklich anpassen wollt.
+         Falls das nicht geht, sprecht lieber mit einem RM+, ob eine Loesung
+         dafuer gefunden werden kann. Der Standard-Pub wurde in der
+         Vergangenheit so immer modularer und besser.
+
+
+SIEHE AUCH: AddToMenu, RemoveFromMenu, consume, QueryMoney, AddMoney 
+
+------------------------------------------------------------------------------
+Last modified: Son Apr 11 19:28:00 2010 by Caldra
diff --git a/doc/std/random b/doc/std/random
new file mode 100644
index 0000000..489214d
--- /dev/null
+++ b/doc/std/random
@@ -0,0 +1,71 @@
+Es gibt im MG zwei Quellen von Pseudo-Zufallszahlen.
+
+1) random()
+  Diese efun liefert eine Zufallszahl aus dem Pseudo-Zufallszahlengenerator
+  (PRNG) des Drivers. (s. Manpage).
+  Wenn keine besonderen Gruenden vorliegen, sollte immer diese verwendet
+  werden.
+
+
+2) /std/util/rand-glfsr.c
+  Falls jemand aus irgendeinem Grund eine Sequenz von Pseudo-Zufallszahlen
+  braucht, die spaeter reproduzierbar sein soll (d.h. der Seed muss
+  konfigurierbar sein), gibt es unter /std/util/rand-glfsr.c einen simplen,
+  nicht allzu guten und sehr ineffizienten Pseudo-Zufallszahlengenerator.
+
+Interface:
+* public varargs void init(int seed, int newp)
+  Setzt das Seed (und das Erzeugungspolynom, wenn gewuenscht, wovon ich
+  aber abraten wuerde). Der Seed darf nicht 0 sein.
+* public void InitWithUUID(string uuid)
+  Berechnet einen Seed aus der uuid und initialisiert den PRNG damit (und dem
+  Default-Polynom).
+* public int nextbit()
+  Liefert ein zufaelliges Bit (also 0 oder 1).
+* public int nextbits(int count)
+  Liefert <count> Bits (max 64) in einem int.
+* public int random(int n)
+  Liefert eine Zufallszahl zwischen 0 und n-1, in welcher 32 bits an 
+  Pseudozufall stecken.
+  Das Seed von der Blueprint wird vermutlich staendig veraendert (d.h.
+  verlasst euch nicht drauf, dass es konstant bleibt), wollt ihr eine
+  'private' Instanz mit eurem Seed, clont das Objekt (aber verliert den Clone
+  nicht).
+
+  Und um es nochmal zu sagen:
+  Die IMHO einzige sinnvolle Anwendung ist, wenn man aus irgendeinem Grund
+  das seed selber waehlen muss, damit man die Sequenz von Pseudozufall immer
+  wieder reproduzieren kann.
+  In allen anderen Faellen nehmt besser das random() vom Driver.
+
+Der PRNG benutzt ein Linear Feedback Shift Register (in Galois-Variante). Die
+Periodenlaenge des per Default benutzten Erzeugngspolynoms ist 2^32 - 1, d.h.
+nach 4294967295 Bits wiederholt sich die Sequenz.
+Das Default-Polynom ist:
+x^32 + x^31 + x^28 + x^27 + x^24 + x^23 + x^20 + x^19 + x^16 + x^15 + x^12
+     + x^11 + x^8 + x^7 + x^5 + x^3 + 1
+(Zahlenwert: 0x1999999a8)
+
+Wer ein anderes Polynom will, kann das per init() konfigurieren, sollte sich
+aber schlau machen, welche sinnvoll nutzbar sind.
+
+
+Ein paar statistische Daten eines Stroms von 40960 Bits:
+Value Char Occurrences Fraction
+  0            20474   0.499854
+  1            20486   0.500146
+
+Total:         40960   1.000000
+
+Entropy = 1.000000 bits per bit.
+
+Optimum compression would reduce the size
+of this 40960 bit file by 0 percent.
+
+Chi square distribution for 40960 samples is 0.00, and randomly
+would exceed this value 95.27 percent of the times.
+
+Arithmetic mean value of data bits is 0.5001 (0.5 = random).
+Monte Carlo value for Pi is 3.109026964 (error 1.04 percent).
+Serial correlation coefficient is -0.008399 (totally uncorrelated = 0.0).
+
diff --git a/doc/std/room b/doc/std/room
new file mode 100644
index 0000000..94e575f
--- /dev/null
+++ b/doc/std/room
@@ -0,0 +1,143 @@
+STANDARDOBJEKT FUER RAEUME:
+
+BENUTZUNG:
+      inherit "/std/room";
+      
+      #include <properties.h>
+      #include <rooms.h>            // Fuer AddItem() und NewDoor()
+
+BESCHREIBUNG:
+      Ein Raum ist ein Objekt, das von Spielern oder anderen Lebewesen
+      betreten werden kann. Er hat Lang- und Kurzbeschreibungen von
+      innen und aussen (zum Beispiel Schiffe oder aehnliche Raeume in
+      Raeumen), die sich der Spieler je nach seinen Beduerfnissen
+      anzeigen lassen kann (kurz/lang-Modus).
+
+   Properties:
+
+      P_INT_SHORT
+            Kurzbeschreibung innerhalb des Raumes
+      P_INT_LONG
+            Langbeschreibung innerhalb des Raumes
+      P_SHORT
+            Kurzbeschreibung von aussen (nur noetig, wenn man den Raum
+            auch wirklich von aussen sehen kann)
+      P_LONG
+            Langbeschreibung von aussen (siehe P_SHORT)
+      Um Situationsbezogene Beschreibungen zu bekommen, kann man 
+      Querymethoden auf die jeweiligen Properties setzen, siehe Dokumentation
+      zu Set().
+
+      P_LIGHT
+            Lichtlevel des Raums (0 dunkel, 1 hell)
+      P_LIGHT_TYPE
+            Lichtart, mit der der Raum beleuchtet ist, zB LT_SUNLIGHT
+      P_INDOORS
+            Wenn es sich um einen Aussenraum handelt, 0. 1 fuer Innenraeume.
+      P_TRANSPARENT
+            Ist ungleich 0, wenn man in den Raum von aussen reinschauen
+            kann, oder von innen nach aussen
+      P_NO_TPORT
+            Zum verhindern von Teleportversuchen in oder aus dem Raum
+      P_NOMAGIC
+            Im Raum kann nicht gezaubert werden.
+  P_WATER
+    Im Raum kann geangelt werden, und man kann ggf. sogar Fische fangen.
+
+   Funktionen:
+
+  AddExit(string|string* kmnd, string zielraum)
+            <kmnd> kann ein String sein oder eine Liste von Strings
+            mit den Kommandos mit denen der Spieler in <zielraum>
+            landen soll.
+      RemoveExit(string|string* kmnd)
+            <kmnd> ist ein String oder eine Liste von Strings, deren
+            Ausgaenge entfernt werden sollen. Dabei muss man etwas vor-
+            sichtig sein, da ein Spieler, der zum Zeitpunkt des Aufrufs
+            im Raum ist, die Aktion fuer sich noch definiert hat.
+            Also: Nur benutzen, wenn man weiss, was man tut!
+      AddSpecialExit(string|string* kmnd, string funktion)
+            <funktion> wird aufgerufen, wenn der Spieler das Kommando
+            <kmnd> tippt (oder eins der Kommandos, wenn <kmnd> eine
+            Liste ist). Nuetzlich zum Beispiel um einen Spieler nur
+            durchzulassen, wenn ein Waechter nicht da ist.
+            P_AERIAL_HELPERS/P_AQUATIC_HELPERS
+              Sollte es gewuenscht sein, dass bestimmte Raeume z.B. nur 
+              fliegend erreicht werden koennen, oder dass man fuer einen 
+              bestimmten Ausgang tauchen koennen muss, sind diese 
+              Properties hilfreich, die im Spielerobjekt definiert sind 
+              und die dort aktuell registrierten Hilfsobjekte fuers 
+              Fliegen/Segeln und Tauchen enthalten.
+      RemoveSpecialExit(string|string* kmnd)
+            siehe RemoveExit()
+      NewDoor()
+            Fuegt dem Raum eine Tuer hinzu, die in einen zweiten Raum fuehrt,
+            und die der Spieler (ggf. mit einem Schluessel) oeffnen und 
+            schliessen kann.
+      
+      Um Raeume interessanter zu machen, kann man ihnen Details, die 
+      vom Spieler angeschaut werden koennen, einbauen:
+
+      AddDetail(string|string* keys, string descr)
+            Eine Beschreibung von Details, die vom Spieler angeschaut
+            werden koennen wird dem Raum gegeben. <keys> kann ein
+            String sein oder eine Liste von Strings. <descr> ist
+            die Beschreibung, die der Spieler erhaelt.
+      RemoveDetail(string|string* keys)
+            Entfernt ein Detail aus einem Raum. <keys> kann ein String
+            sein oder eine Liste von Strings.
+      AddSpecialDetail(string|string* keys, string function)
+            Im Prinzip wie AddDetail(), nur wird als zweites Argument
+            kein fester String angegeben, sondern eine Funktion, die
+            einen String zurueckgeben muss. Damit kann man variable
+            Details programmieren.
+      RemoveSpecialDetail(string|string* keys)
+            siehe RemoveDetail()
+      AddReadDetail(string|string* keys, string desc)
+            <desc> wird ausgegeben, wenn ein Spieler den <key> liest.
+            <keys> kann wieder ein String oder eine Liste von Strings
+            sein.
+      RemoveReadDetail(string|string* keys)
+            siehe RemoveDetail()
+      AddRoomMessage(string* msg, int prob, string|mixed *func)
+            Alle <prob> Sekunden wird aus einer Liste von Meldungen
+            (<msg>) zufaellig eine ausgesucht, die dann im Raum er-
+            scheint. Wenn das Argument <func> angegeben ist, wird
+            eine Funktion diesen Namens aufgerufen, der der Index
+            der ausgegebenen Meldung als Argument uebergeben wird.
+            Func darf auch einen Array von Funktionen enthalten.
+
+      AddCmd(string|string* cmd, string func, int flag)
+            Im Raum wird beim Kommando <cmd> (oder einem der Kommandos)
+            die Funktion <func> aufgerufen. <func> muss 1 zurueck-
+            geben, wenn die Funktion die Kombination aus Kommando
+            und Argumenten akzeptiert, ansonsten 0. Alles was ein
+            Spieler hinter <cmd> noch angibt, wird der Funktion als
+            Argument uebergeben. Wenn <flag> gesetzt ist, wird jedes
+            Kommando, das mit <cmd> beginnt akzeptiert.
+      RemoveCmd(string|string* cmd)
+            Entfernt ein Kommando aus dem Raum.
+            ACHTUNG: Bei Spielern, die zum Zeitpunkt des Funktions-
+            aufrufs noch im Raum sind, ist das Kommando noch definiert.
+            Also: Nur benutzen, wenn man genau weiss, was man tut.
+
+      AddItem(string filename, int refresh)
+            Ein Objekt mit Pfad <filename> wird erzeugt und in den
+            Raum bewegt, dabei gibt es vier verschiedene Modi, die
+            ueber das Argument <refresh> angegeben werden:
+            REFRESH_NONE: Das Objekt wird nur beim allerersten mal
+                  erzeugt, und dann nie wieder (erst beim reboot)
+            REFRESH_DESTRUCT: Ein neues Objekt wird erst erzeugt, 
+                  wenn das alte zerstoert ist. So kann man sicher
+                  gehen, dass nur ein Exemplar im Spiel ist.
+            REFRESH_REMOVE: Beim reset wird ein neues Objekt erzeugt,
+                  wenn sich das alte nicht mehr im Raum befindet.
+            REFRESH_ALWAYS: Bei jedem reset wird ein neues Objekt 
+                  erzeugt. VORSICHT: Kann zu riesigen Mengen von
+                  Objekten fuehren, wenn ein Raum lange nicht be-
+                  sucht wird.
+            Die Konstanten REFRESH_* sind in <rooms.h> definiert.
+
+      Auf die Properties P_EXITS, P_SPECIAL_EXITS, P_DETAILS, 
+      P_SPECIAL_DETAILS, P_READ_DETAILS, P_COMMANDS und P_ITEMS
+      sollte NICHT direkt zugegriffen werden.
diff --git a/doc/std/room.doku b/doc/std/room.doku
new file mode 100644
index 0000000..e1774c6
--- /dev/null
+++ b/doc/std/room.doku
@@ -0,0 +1,132 @@
+STANDARDOBJEKT FUER RAEUME:
+
+BENUTZUNG:
+	inherit "std/room";
+	
+	#include <properties.h>
+	#include <rooms.h>		// Fuer AddItem() und NewDoor()
+
+BESCHREIBUNG:
+	Ein Raum ist ein Objekt, dass von Spielern oder anderen Lebewesen
+	betreten werden kann. Er hat Lang- und Kurzbeschrreibungen von
+	innen und aussen (zum Beispiel Schiffe oder aehnliche Raeume in
+	Raeumen), die sich der Spieler je nach seinen Beduerftnissen
+	anzeigen lassen kann (kurz/lang-Modus).
+
+   Properties:
+
+	P_INT_SHORT
+		Kurzbeschreibung innerhalb des Raumes
+	P_INT_LONG
+		Langbeschreibung innerhalb des Raumes
+	P_SHORT
+		Kurzbeschriebung von aussen (nur noetig, wenn man den Raum
+		auch wirklich von aussen sehen kann)
+	P_LONG
+		Langbeschreibung von aussen (siehe P_SHORT)
+	Um Situationsbezogene Beschreibungen zu bekommen, kann man 
+	int_short(), int_long(), short(), long() ueberschreiben, oder
+	den process_string Mechanismus verwenden (siehe entsprechende
+	Dokumentation)
+
+	P_LIGHT
+		Lichtlevel des Raums (0 dunkel, 1 hell)
+	P_INDOORS
+		1 Wenn der Raum nicht im Freien ist
+	P_TRANSPARENT
+		Ist ungleich 0, wenn man in den Raum von aussen reinschauen
+		kann, oder von innen nach aussen
+	P_NO_TPORT
+		Zum verhindern von Teleportversuchen in oder aus dem Raum
+		(siehe auch /doc/properties.h)
+	P_NOMAGIC
+		Im Raum kann nicht gezaubert werden.
+
+   Funktionen:
+	AddExit(string|string* kmnd, string zielraum)
+		<kmnd> kann ein String sein oder eine Liste von Strings
+		mit den Kommandos mit denen der Spieler in <zielraum>
+		landen soll.
+	RemoveExit(string|string* kmnd)
+		<kmnd> ist ein String oder eine Liste von Strings, deren
+		Ausgaenge entfernt werden sollen. Dabei muss man etwas vor-
+		sichtig sein, da ein Spieler, der zum Zeitpunkt des Aufrufs
+		im Raum ist, die Aktion fuer sich noch definiert hat.
+		Also: Nur benutzen, wenn man weiss, was man tut!
+	AddSpecialExit(string|string* kmnd, string funktion)
+		<funktion> wird aufgerufen, wenn der Spieler das Kommando
+		<kmnd> tippt (oder eins der Kommandos, wenn <kmnd> eine
+		Liste ist). Nuetzlich zum Beispiel um einen Spieler nur
+		durchzulassen, wenn ein Waechter nicht da ist.
+	RemoveSpecialExit(string|string* kmnd)
+		siehe RemoveExit()
+	NewDoor()
+		siehe /doc/std/door
+	
+	Um Raeume interessanter zu machen, kann man ihnen Details, die 
+	vom Spieler angeschaut werden koennen, einbauen:
+
+	AddDetail(string|string* keys, string descr)
+		Eine Beschreibung von Details, die vom Spieler angeschaut
+		werden koennen wird dem Raum gegeben. <keys> kann ein
+		String sein oder eine Liste von Strings. <descr> ist
+		die Beschreibung, die der Spieler erhaelt.
+	RemoveDetail(string|string* keys)
+		Entfernt ein Detail aus einem Raum. <keys> kann ein String
+		sein oder eine Liste von Strings.
+	AddSpecialDetail(string|string* keys, string function)
+		Im Prinzip wie AddDetail(), nur wird als zweites Argument
+		kein fester String angegeben, sonder eine Funktion, die
+		einen String zurueckgeben muss. Damit kann man variable
+		Details programmieren.
+	RemoveSpecialDetail(string|string* keys)
+		siehe RemoveDetail()
+	AddReadDetail(string|string* keys, string desc)
+		<desc> wird ausgegeben, wenn ein Spieler den <key> liest.
+		<keys> kann wieder ein String oder eine Liste von Strings
+		sein.
+	RemoveReadDetail(string|string* keys)
+		siehe RemoveDetail()
+	AddRoomMessage(string* msg, int prob, string|string *func)
+		Alle <prob> Sekunden wird aus einer Liste von Meldungen
+		(<msg>) zufaellig eine ausgesucht, die dann im Raum er-
+		scheint. Wenn das Argument <func> angegeben ist, wird
+		eine Funktion diesen Namens aufgerufen, der der Index
+		der ausgegebenen Meldung als Argument uebergeben wird.
+		func darf auch einen Array von Funktionen enthalten. Es wird
+		dann zufaellig eine ausgewaehlt.
+
+	AddCmd(string|string* cmd, string func, int flag)
+		Im Raum wird beim Kommando <cmd> (oder einem der Kommandos)
+		die Funktion <func> aufgerufen. <func> muss 1 zurueck-
+		geben, wenn die Funktion die Kombination aus Kommando
+		und Argumenten akzeptiert, ansonsten 0. Alles was ein
+		Spieler hinter <cmd> noch angibt, wird der Funktion als
+		Argument uebergeben. Wenn <flag> gesetzt ist, wird jedes
+		Kommando, das mit <cmd> beginnt akzeptiert.
+	RemoveCmd(string|string* cmd)
+		Entfernt ein Kommando aus dem Raum.
+		ACHTUNG: Bei Spielern, die zum Zeitpunkt des Funktions-
+		aufrufs noch im Raum sind, ist das Kommando noch definiert.
+		Also: Nur benutzen, wenn man genau weiss, was man tut.
+
+	AddItem(string filename, int refresh)
+		Ein Objekt mit Pfad <filename> wird erzeugt und in den
+		Raum bewegt, dabei gibt es vier verschiedene Modi, die
+		ueber das Argument <refresh> angegeben werden:
+		REFRESH_NONE: Das Objekt wird nur beim allerersten mal
+			erzeugt, und dann nie wieder (erst beim reboot)
+		REFRESH_DESTRUCT: Ein neues Objekt wird erst erzeugt, 
+			wenn das alte zerstoert ist. So kann man sicher
+			gehen, dass nur ein Exemplar im Spiel ist.
+		REFRESH_REMOVE: Beim reset wird ein neues Objekt erzeugt,
+			wenn sich das alte nicht mehr im Raum befindet.
+		REFRESH_ALWAYS: Bei jedem reset wird ein neues Objekt 
+			erzeugt. VORSICHT: Kann zu riesigen Mengen von
+			Objekten fuehren, wenn ein Raum lange nicht be-
+			sucht wird.
+		Die Konstanten REFRESH_* sind in <rooms.h> definiert.
+
+	Auf die Properties P_EXITS, P_SPECIAL_EXITS, P_DETAILS, 
+	P_SPECIAL_DETAILS, P_READ_DETAILS, P_COMMANDS und P_ITEMS
+	sollte NICHT direkt zugegriffen werden.
diff --git a/doc/std/schluessel b/doc/std/schluessel
new file mode 100644
index 0000000..5a23db2
--- /dev/null
+++ b/doc/std/schluessel
@@ -0,0 +1,24 @@
+STANDARDOBJEKT FUER SCHLUESSEL:
+
+BENUTZUNG
+	inherit "/d/unterwelt/std/schluessel"
+
+BESCHREIBUNG   
+	Du erzeugst einen Schluessel der nur auf eine bestimmte Tuer
+	passt.
+
+    Funktionen:
+
+	create():
+		setzt ein paar Defaultwerte.
+
+	SetDoorKey( string str):
+		gibt den Schluessel eine spezielle Beschreibung, die 
+		auf die Tuer passen muss.
+
+	QueryDoorKey()
+		fragt den string des Schluessels ab.
+
+SIEHE AUCH:
+	tuer
+
diff --git a/doc/std/sequencer b/doc/std/sequencer
new file mode 100644
index 0000000..d9b17a3
--- /dev/null
+++ b/doc/std/sequencer
@@ -0,0 +1,58 @@
+Version 2.0 des Sequencers von Don Rumata 2.7.93
+
+Manchmal soll ein NPC (Monster) eine Serie von Taetigkeiten
+hintereinander weg ausgefuerht werden. Dieses kann man
+mit diesem Mudul relativ einfach realisieren.
+
+Folgende Ereignisse koennen eine Sequenz ausloesen:
+
+	TellEvent: Es wird etwas in dem Raum, in dem der npc sich
+		befindet, gesagt.
+	GiveEvent: Es wird dem npc etwas gegeben.
+
+Weitere Ereignisse koennen durch den Befehl Load() selber
+programmiert werden.
+
+Wie sieht ein Programm aus?
+
+	Ein Programm ist eine Liste von Befehlen.
+	Jeder Befehl ist eine Liste, bestehend aus einem Kommando
+	und einer Zahl.
+	Das Kommendo wird aehnlich der Befehle, die ein Spieler ein-
+	gibt ausgefuehrt.
+	Vorsicht: NPCs koennen nur einen Teil der Befehle, die ein
+	Spieler kann, dafuer aber immer 'echo' und 'emote'.
+	Die Zahl gibt die Anzahl der Sekunden an, in der der naechste
+	Befehl ausgefuehrt wird.
+
+Folgende Funktionen sind im Modul implementiert:
+
+	Vorbereiten von Funktionen, die eine Sequenz ausloesen:
+
+	RegisterTell( funktion, programm )
+		Wenn dem npc etwas gesagt wird, so wird die gesagte Meldung
+		an die Funktion uebergeben. Gibt die Funktionen nicht 0
+		zurueck, wird das Programm gestartet.
+		
+	RegisterGive( funktion, programm )
+		Wird dem npc etwas gegeben, so wird das Objekt an die
+		Funktion uebergeben. Gibt die Funktion nicht 0 zurueck, so
+		wird das Programm gestartet.
+
+	Load( programm )
+		Starte das angegebene Programm.
+
+Anederungen an bestehenden Std-Funktionen:
+
+	give_notify() gibt eine 1 zurueck, wenn das Objekt akzeptiert
+		wurde. (Es muss - falls gewuenscht - dann von Hand zuruech-
+		gegeben werden. (give_obj(ob,this_player())) in dieser
+		Funktion.
+
+	Mittels add_action() kann man im create() des NPCs eigene
+	Verben fuer den NPC einfuehren.
+
+Es kann immer nur eine Funktion (egal ob via Tell, Give oder Load) angemeldet
+sein. Es kann immer nur ein Programm gleichzeitig laufen.
+
+Ideen und Bugreports an Rumata
diff --git a/doc/std/thing b/doc/std/thing
new file mode 100644
index 0000000..445b462
--- /dev/null
+++ b/doc/std/thing
@@ -0,0 +1,205 @@
+STANDARD KLASSE:
+	"/std/thing" 
+
+BENUTZUNG:
+	inherit "/std/thing";
+
+	#include <thing.h>
+	#include <properties.h>
+
+PROPERTIES:
+	<thing/commands>
+		P_COMMANDS	-- Befehle assoziiert mit dem Objekt
+	<thing/description>
+		P_NAME		-- Name
+		P_NAME_ADJ	-- Namensadjektiv
+		P_SHORT		-- Kurzbeschreibung
+		P_LONG		-- Langbeschreibung
+		P_IDS		-- Identifikatoren (Feld von Zeichenketten)
+		P_ADJECTIVES	-- Adjektive zur Beschreibung
+	<thing/language>
+		P_ARTICLE	-- Artikel des Objekts
+		P_GENDER	-- Geschlecht des Objekts
+	<thing/moving>
+		P_NODROP	-- Kann nicht fallengelassen werden
+		P_NOGET		-- Kann nicht genommen werden
+	<thing/properties>
+		P_UID		-- User ID
+		P_EUID		-- Effektive User ID
+	<thing/restrictions>
+		P_WEIGHT	-- Gewicht des Objekts
+		P_TOTAL_WEIGHT	-- Gewicht inklusive des Inhalts
+	
+MAKROS:
+	<thing/properties>
+		F_VALUE		-- Propertywert
+		F_MODE		-- Propertymodus umschalten
+		F_MODE_AS	-- Propertymodus setzen
+		F_MODE_AD	-- Propertymodus loeschen
+		  SAVE		  -- Property wird gespeichert
+		  PROTECTED	  -- Property is geschuetzt
+		  SECURED	  -- Property kann nie mehr geaendert werden
+		  NOSETMETHOD	  -- Property besitzt keine Setzfunktion
+		F_SET_METHOD	-- Funktion zum setzen der Property
+		F_QUERY_METHOD	-- Funktion zum lesen der Property
+	<thing/language>
+		MALE, FEMALE, NEUTER 	-- Geschlechter
+		WER, WESSEN, WEM, WEN 	-- Fall
+		RAW 			-- fuer Namen ohne Artikel
+		SINGULAR, PLURAL 	-- Zeiten
+	
+FUNKTIONEN:
+	"/std/thing/commands":
+		varargs void AddCmd(mixed cmd, mixed func, int flag);
+		varargs void RemoveCmd(mixed cmd);
+	"/std/thing/description":
+		varargs int id(string str, int lvl);
+		void AddId(mixed str);
+		void AddAdjective(mixed str);
+		string name(int casus,int demon);
+	"/std/thing/language":
+		varargs string QueryArticle(int casus, int dem, int force);
+		varargs int SuggestArticle(string id);
+		varargs string QueryPossPronoun(mixed what, int casus, 
+		                                int number);
+		string QueryPronoun(int casus);
+		varargs string QueryDu(int casus,int gender, int zahl);
+		string QueryGenderString();
+		varargs string DeclAdj(string adj, int casus, int demon);
+	"/std/thing/properties":
+		varargs mixed Set(string name, mixed Value, int Type); 
+		varargs mixed Query(string name, int Type);
+		mixed SetProp(string name, mixed Value);
+		mixed QueryProp(string name);
+		void SetProperties(mapping props);
+		mapping QueryProperties();
+	"/std/thing/moving":
+		int move(mixed dest,int method);
+		varargs int remove();
+
+ERLAEUTERUNG:
+	"/std/thing" ist eine generelle Klasse fuer Objekte aller Art.
+	Es bietet generelle Funktionalitaet zur Erstellung von neuen
+	Klassen und Objekten. Sie kann mittels der zur Verfuegung
+	stehenden Funktionen konfiguriert werden:
+
+	** "/std/thing/commands":
+	varargs void AddCmd(mixed cmd, mixed func, int flag);
+	  Um dem Objekt die Moeglichkeit zu geben, auf Befehle zu reagieren,
+	  kann mit AddCmd() Kommandos definiert werden, die eine bestimmte
+	  Funktion (func) aufrufen. flag entscheidet darueber, ob das Kommando
+	  (cmd) abgekuerzt werden kann (flag = 1) oder nicht (flag = 0).
+
+	varargs void RemoveCmd(mixed cmd);
+	  Man kann die Kommandos jederzeit wieder mit der Funktion RemoveCmd()
+	  entfernen.
+
+	** "/std/thing/description":
+	varargs int id(string str, int lvl);
+	  Die Funktion id() behandelt die Identifikatoren eines Objektes, 
+	  welche in der Property P_IDS gespeichert sind. Jedesmal, wenn ein
+	  Objekt durch Kommandos wie "nimm", "lass fallen" etc referenziert
+	  werden soll, wird id() mit dem Namen des Objektes aufgerufen. Dabei
+	  werden dann die Elemente von P_IDS mit str verglichen und das Ergeb-
+	  nis (WAHR/FALSCH == 1/0) zurueckgegeben. lvl ist fuer die Behandlung
+	  von Lebewesen notwendig, siehe living.
+	
+	void AddId(mixed str);
+	  Mittels dieser Funktion kann ein oder mehrere neue Identifikatoren
+	  der Liste der bestehenden, Standard ist "ding", hinzugefuegt werden.
+
+	void AddAdjective(mixed str);
+	  Um den Objekten die Moeglichkeit zu geben, sich besser zu beschreiben
+	  oder zur besseren Identifizierung, kann man mit AddAdjective() dem
+	  Objekt naeher beschreibende Adjektive hinzufuegen. Diese sind in
+	  P_ADJECTIVES gespeichert.
+
+	string name(int casus,int demon);
+	  Diese Funktion liefert den Namen des Objektes mit allen zusaetzlichen
+	  Attributen im gewuenschten Fall (casus), demonstrativ (demon = 1) 
+	  oder nicht demonstrativ (demon = 0).
+
+	** "/std/thing/language":
+	varargs string QueryArticle(int casus, int dem, int force);
+	  Artikel im passenden Fall sowie demonst. bzw undemonst. zurueck-
+	  geben. force ueberschreibt das SetArticle-Flag.
+
+	varargs int SuggestArticle(string id);
+	  Empfehle einen Artikel fuer das Objekt, getestet wird, ob ausser
+	  diesem Objekt sich ein anderes Objekt mit der id im selben
+	  Raum befindet.
+
+	varargs string QueryPossPronoun(mixed what, int casus, int number);
+	  Gib ein Pronomen zurueck, das ausdrueckt, das ein Objekt diesem
+	  Objekt gehoert. Dabei ist what das Geschlecht des Objektes.
+
+	string QueryPronoun( casus );
+	  Er/Sie/Es u.ae. zurueckgeben.
+
+	varargs string QueryDu(int casus,int gender, int zahl);
+	  Du im passenden Fall zurueckgeben (siehe QueryArticle()).
+
+	string QueryGenderString();
+	  Gibt das Geschlecht in Worten zurueck.
+
+	varargs string DeclAdj(string adj, int casus, int demon); 
+	  Dekliniere Adjektive adj mit Casus casus.
+
+	** "/std/thing/properties":
+	varargs mixed Set(string name, mixed Value, int Type); 
+	  Setze einen Wert einer Property direkt, wobei der Typ des Wertes
+	  (Value) durch F_VALUE, F_MODE, F_SET_METHOD, F_QUERY_METHOD (Type)
+	  angegeben wird. 
+	  F_VALUE -- darf ein beliebiger Wert sein
+	  F_MODE  -- muss eine Zahl sein (SAVE, PROTECTED, SECURED, 
+	             NOSETMETHOD)
+	  F_SET_METHOD, F_QUERY_METHOD -- 0 oder eine Closure
+
+	varargs mixed Query(string name, int Type);
+	  Lies den Wert einer Property aus. Dabei gibt Type an, welchen Wert
+	  (F_VALUE, F_MODE, F_SET_METHOD, F_QUERY_METHOD) man auslesen will.
+
+	mixed SetProp(string name, mixed Value);
+	  Setze den Wert einer Property. Dabei wird, falls vorhanden, der Wert
+	  noch durch die F_SET_METHOD behandelt (z.B. Typueberpruefung).
+	  Dies sollte die Standardmethode zum Setzen einer Property sein!
+  
+	mixed QueryProp(string name);
+	  Lies den Wert einer Property, wobei der eigentliche Wert durch die,
+	  falls vorhanden, F_QUERY_METHOD behandelt wird. Als Beispiel dient
+	  hier die Behandlung des Gesamtgewichts, welches durch Abfragen von
+	  P_TOTAL_WEIGHT ausgelesen werden kann; hierbei errechnet die 
+	  F_QUERY_METHOD von P+_TOTAL_WEIGHT erst das totale Gewicht.
+	  Dies sollte die Standardmethode sein um eine Property auszulesen!
+
+	void SetProperties(mapping props);
+	  Diese Funktion kann die in props gespeicherten Properties im Objekt
+	  unterbringen. Dabei werden schon vorher als PROTECTED oder SECURED
+	  gekennzeichnete Properties NICHT ueberschrieben.
+
+	mapping QueryProperties();
+	  Liefert ein mapping, welches von SetProperties() behandelt werden
+	  kann.
+
+	** "/std/thing/moving":
+	int move(mixed dest,int method);
+	  Jedesmal, wenn ein Objekt bewegt werden soll, wird die Funktion
+	  move() aufgerufen, welche dann das eigentliche Bewegen ausfuehrt.
+	  Dazu muss der Funktion ein Ziel (dest) als Zeichenkette oder als
+	  Objekt und eine Methode der Bewegung uebergeben werden.
+	
+	varargs int remove();
+	  Wird ein Objekt zerstoert, wird die Funktion remove() aufgerufen.
+	  Rueckgabewert 1 bedeutet Erfolg und 0 Misserfolg der Zerstoerung.
+
+VERERBUNGSBAUM:
+	thing
+	  |-thing/commands
+	  |-thing/description
+	  |-thing/language
+	  |-thing/moving
+	  |-thing/properties
+	  `-thing/restrictions
+
+SIEHE AUCH:
+	properties, closures 
diff --git a/doc/std/transport b/doc/std/transport
new file mode 100644
index 0000000..3000c97
--- /dev/null
+++ b/doc/std/transport
@@ -0,0 +1,264 @@
+> FUNKTIONSWEISE VON TRANSPORTERN <
+Version 1.0 (04.05.1993) von Rumata
+===================================
+
+
+INHALT:
+======
+
+	1.	Allgemeines
+	2.	Properties
+	3.	Haltestellen
+	4.	Steuerung
+	5.	Betreten und verlassen
+	6.	Extras
+	7.	Kommandos in Trnasportern
+
+
+1. ALLGEMEINES
+==============
+
+	Ein Transporter ist ein Raum mit besonderen Eigenschaften.
+	Waehrend normale Raume miteinnder starr verbunden sind,
+	kann dieser Raum seine Verbindung zu anderen Raeumen mit
+	der Zeit aendern.
+
+	Jeder Transporter besitzt einen 'Kurs' den er abfaehrt; nach der
+	letzten 'Haltestelle' beginnt der Kurs wieder von Vorne.
+	Will man einen Kurs setzen, der entlang einer Strecke hin-
+	und zurueck fuehrt, so muss man jede Haltestelle einmal fuer
+	den Hinweg und einmal fuer den Rueckweg einfuegen.
+
+	Ein Beispiel, wie man einen solchen Transporter programmiert,
+	ist in /doc/beispiele/wolke.c zu sehen (und zu laden :-).
+
+2. PROPERTIES
+=============
+
+	P_ENTERMSG
+		Array mit zwei Meldungen in der Art von msgin und
+		msgout.  Die erste Meldung ist fuer den Raum, den der
+		Spieler verlaesst, und die zweite fuer den Transporter,
+		in den er geht.
+
+	P_LEAVEMSG
+		Array mit zwei Meldungen, eine fuer den Transporter, den
+		er verlaesst, und eine fuer den Raum, in den er kommt.
+
+	P_ENTERFAIL
+		Meldung an ein Wesen, wenn den vollen Transporter
+		betreten will. Ist die Prop. ein Array, so wird das erste
+		Element als Meldung an das Wesen, das zweite als Meldung
+		an die Mitspieler im Raum geschickt. Ist die Property
+		eine Closure, wird diese ausgefuehrt, wenn ein Spieler
+		vergeblich versucht, den Transporter zu betreten.
+
+	P_LEAVEFAIL
+		Meldung an ein Wesen, wenn es ausserhalb der Anlegezeiten
+		den Transporter verlassen will. Ist die Prop. ein Array,
+		so wird das erste Element als Meldung an das Wesen, das
+		zweite als Meldung an die Mitspieler im Transporter
+		geschickt. Ist die Property eine Closure, wird diese
+		ausgefuehrt, wenn ein Spieler vergeblich versucht, den
+		Transporter zu verlassen.
+
+	P_ARRIVEMSG
+		Ein Array mit zwei Meldungen. Das erste Element ist die
+		Meldung, die die Wesen im Transporter bekommen. Die
+		zweite Meldung ist fuer die Wesen in dem Raum, an dem
+		der Transporter anlegt. 
+
+	P_DEPARTMSG
+		Dito fuer das Ablegen.
+
+	P_MAX_PASSENGERS
+		Numerischer Wert fuer die maximale Anzahl von Wesen, die
+		sich in dem Transporter aufhalten duerfen.
+		0 bedeutet unbeschaenkte Spielerzahl.
+
+
+3. HALTESTELLEN
+===============
+
+	Es gibt 3 Sorten von Haltestellen:
+
+	1.) Raeume als Haltestellen.
+
+	FUNKTION:
+	
+		AddRoute( raum, verweil, next );
+		oder
+		AddRoute( raum, verweil, next, code );
+
+	PARAMETER:
+
+		raum	- Raum, an dem der Transporter halten soll.
+		verweil	- Zeit in Sekunden, die der Transporter in dem
+			  Raum verbleibt, bis er weiterfaehrt.
+		next	- Zeit in Sekunden bis zur naechsten Halte-
+			  stelle.
+
+		code	- Text, der von der Funktion QueryArrived
+			  zurueckgegeben wird, wenn der Transporter
+			  an dieser Haltestelle angelegt hat.
+			  Wird dieser Parameter nicht angegeben, wird
+			  ein leerer String als code gesetzt.
+
+	2.) Meldungen als Haltestellen.
+
+	FUNKTION:
+
+	      AddMsg( text, next );
+
+	PARAMETER:
+
+		text	- Text, der Meldung, die in dem Transporter
+			  ausgegeben wird.
+		next	- Zeit in Sekunden bis zur naechsten Halte-
+			  stelle.
+
+	3.) Funktionsaufrufe als Haltestellen.
+
+	FUNKTION:
+
+		AddFun( fun, next );
+
+	PARAMETER:
+
+		fun	- Name einer Funktion, die im Transporter
+			  aufgerufen wird.
+		next	- Zeit in Sekunden bis zur naechsten Halte-
+			  stelle.
+
+4. STEUERUNG
+============
+
+	FUNKTION
+
+		Start();
+		oder
+		Start(nummer);
+
+	BESCHREIBUNG:
+
+		Der Transporter soll sofort an der Haltestelle mit
+		der angegebenen Nummer (keine Nummer heisst an der ersten)
+		anlegen und von da ab seinen Kurs fahren.
+
+	FUNKTION:
+
+		Halt();
+
+	BESCHREIBUNG:
+
+		Halte die Fahrt an. Der Transporter bleibt stehen, wo
+		er ist.
+
+
+5. BETRETEN UND VERLASSEN
+=========================
+
+	Der Transporter hat keine Verben programmiert, mit denen man
+	den Transporter betreten oder verlassen kann.
+	Dafuer muessen mit AddCmd() Verben eingefuehrt werden, die
+	auf selbstgeschriebene Funktionen zugreifen.
+
+	FUNKTION:
+
+		Enter();
+
+	BESCHREIBUNG:
+
+		Teste, ob fuer dun aktuellen Spieler (this_player())
+		noch Platz im Transporter ist und ob er sich ausserhalb
+		des Transporters befindet. Wenn ja, bewege ihn in den
+		Transporter. Wenn der Transporter eine Reaktion (Bewegen des
+		Spielers oder Ueberlauf), so gib eine 1 zurueck sonst eine 0.
+
+		Wenn der Transporter nicht da (also unsichtbar) ist, tue fuer
+		die Spieler ausserhalb des Transporters so,
+		als ob das Verb nicht definiert wurde.
+
+		Dieser Wert kann im allgemeinen direkt als returnwert benutzt
+		werden.
+
+	FUNKTION:
+
+		Leave();
+
+	BESCHREIBUNG:
+
+		Wenn der Momentane Spieler nicht im Transporter ist, so gib
+		eine Fehlermeldung aus. Anderenfalls teste, ob der
+		Transporter an einen Raum angelegt hat. Wenn nein, gebe
+		die LEAVEFAIL Meldung aus; und wenn alles ok ist, so
+		bewege den Spieler aus dem Transporter.
+
+6. EXTRAS
+=========
+
+	FUNKTION:
+
+		QueryArrived();
+
+	BESCHREIBUNG:
+
+		Gebe den Code des Raumes zurueck, an dem der Transporter
+		gerade anliegt. Hat der Raum keinen Code, gebe einen
+		Leerstring zurueck. Liegt der Transporter an keinem Raum
+		an, so gebe 0 zurueck.
+
+		Diese Funktion bietet sich an um mittels process_string
+		die Ankunfts- und Abfahrtsmeldungen abwechslungsreich
+		zu gestalten.
+
+	FUNKTION:
+
+		QueryPassengers(),
+
+	BESCHREIBUNG:
+
+		Gebe eine Liste mit allen Lebewesen, die sich auf/in dem
+		Transporter befinden, zurueck.
+
+	FUNKTION:
+
+		RemoveRoute();
+
+	BESCHREIBUNG:
+
+		Halte den Transporter an und loesche dessen Kurs.
+
+7. KOMMANDOS IN TRANSPORTERN
+============================
+
+	Da sich der Transporter in dem Raum befindet, in dem er angelegt
+	hat, sind alle Kommandos des Transporters auch dort verfuegbar.
+	Selbst wenn der Transporter abgelegt hat, steht er noch immer
+	unsichtbar dort herum.
+
+	Deshabl muss in den Kommandos vorsorge grtroffen werden, dass nur
+	die richtigen Spieler (die drinnen und die draussen) die richtigen
+	Kommandos ausfuehren koennen.
+
+	KOMMANDOS FUER SPIELER AUSSERHALB:
+
+		Es muss abgetestet werden, ob der Spieler nicht im
+		Transporter ist, und ob der Transporter angelegt hat.
+
+		if( environment(this_player())==this_object() 
+			|| !QueryArrived() ) return 0;
+
+	KOMMANDOS FUER SPIELER INNERHALB:
+
+		Abtesten, ob der Spieler wirklich im Transport ist.
+
+		if( environment(this_player())!=this_object() )
+			return 0;
+
+		Vorsicht auch bei AddExits innerhalb des Transporters,
+		aber daran arbiete ich noch... :-)
+
+	Die Funktionen Enter() und Leave() haben diese Funktionalitaet
+	eingebaut.
+
diff --git a/doc/std/tuer b/doc/std/tuer
new file mode 100644
index 0000000..489fd84
--- /dev/null
+++ b/doc/std/tuer
@@ -0,0 +1,42 @@
+STANDARDOBJEKT FUER TUEREN:
+
+BENTUZUNG:
+	inherit "std/tuer";
+
+	#include <tuer.c>
+	
+BESCHREIBUNG:
+	Basisklasse fuer alle Tueren im Spiel. Sie ist von /std/thing
+	abgeleitet.
+
+    Funktionen:
+
+	AddDoor2( strin short,
+		  string long,
+		  mixed id,
+		  mixed dest,
+		  string com,
+		  int flags,
+		  string key,
+		  string func,
+		  object obj )
+
+		Erzeugt eine neue Tuer, wobei die Parameter folgendes angeben:
+
+		short 	= Kurzbeschreibung der Tuer
+		long	= Ausfuehrliche Beschreibung der Tuer
+		id	= Name[n] der Tuer
+		dest	= Kommando zum Durchschreiten der Tuer, z.B. 'norden'
+
+		Optionale Parameter:
+
+		flags	= Flags aus tuer.h
+		key	= String, der im Schluessel definiert sein muss, damit
+			  er 'passt'.
+		func	= Funktion die beim Durchschreiten der Tuer in obj
+			  aufgerufen werden soll.
+		obj	= Objekt in dem func aufgerufen werden soll.
+
+SIEHE AUCH:
+	schluessel
+
diff --git a/doc/std/unit b/doc/std/unit
new file mode 100644
index 0000000..3c75059
--- /dev/null
+++ b/doc/std/unit
@@ -0,0 +1,102 @@
+Units
+
+DEFINIERT IN: 
+        /std/unit.c
+
+BESCHREIBUNG:
+        Das Unit-Objekt kann dazu verwendet werden, um groessere Mengen
+        eines Objektes - wie Muenzen - zu erzeugen.
+
+        Das grundlegende Konzept ist, das Unit-Objekte nicht mehr nur 
+        ueber ihre Namen, sondern auch ueber ihre Menge angesprochen 
+        werden kann.
+
+        So wird das id() der Muenzen nicht nur 'muenzen' sondern auch 
+        '3 muenzen' verstehen und auswerten koennen.
+
+        move() kann Teile des ganzen Unit-Objekts bewegen, ein neuer
+        Clone wird erzeugt und diesem der verbleibende Rest der Menge
+        zugewiesen. Nach erfolgreichem move() schaut das Unit-Objekt am
+        Zielort, ob dort evtl. bereits Units des gleichen Typs vorhanden
+        sind und vereinigt sich ggf. mit ihnen, in dem das alte Unit-
+        Objekt zerstoert wird.
+
+        Unit-Objekte werden in einigen Dinger anders programmiert, dazu
+        gehoert folgendes:
+
+        P_SHORT                 ES WIRD KEINE P_SHORT GESETZT!
+
+        P_AMOUNT                Die Menge des Unit-Objekts 'von Haus aus',
+                                also beim Clonen (daher meistens 1)
+
+        P_NAME                  Bei Unit-Objekten kann hier ein String-Array
+                                in der Form ({ SINGULAR_NAME,PLURAL_NAME })
+                                angegeben werden
+
+        SetCoinsPerUnits        Wieviel ist eine bestimmte Menge wert
+        NICHT P_VALUE
+
+        SetGramsPerUnits        Wieviel wiegt eine bestimmte Menge
+        NICHT P_WEIGHT
+
+        AddSingularId		String-Array mit den Singular-IDs 
+        AddPluralId		String-Array mit den Plural-IDs
+
+        zusaetzlich koennen natuerlich IDs per AddId vergeben werden
+
+        Weiterhin verfuegen Unit-Objekte ueber die Moeglichkeit eines
+        automatischen Zerfalls. Dieser ist steuerbar ueber die Props
+        P_UNIT_DECAY_INTERVAL, P_UNIT_DECAY_QUOTA, P_UNIT_DECAY_FLAGS,
+        P_UNIT_DECAY_MIN. Hierbei wird der Zerfall von der Blueprint des
+        Unit-Objektes gesteuert, was als Zerfallsmaster fungiert, weil Clones
+        nicht unbedingt immer nen Reset haben muessen. Die entsprechenden
+        Props sollten daher in der Blueprint konfiguriert werden. (s. Manpages 
+        und Beispiel)
+
+BEISPIELE:
+        Wir basteln uns ein ganz einfaches Unit-Objekt:
+
+        inherit "/std/unit";
+
+        protected void create()
+        {
+          if(!clonep(this_object())) {
+              set_next_reset(-1);
+              return;
+          }
+          ::create();
+          SetProp(P_LONG,"Das ist die kleine Leiche einer Ameise.\n");
+          SetProp(P_NAME,({ "Ameisenleiche","Ameisenleichen" }));
+          SetProp(P_AMOUNT,1);       // es handelt sich um 1 einziges Objekt
+          SetCoinsPerUnits(20,10);   // 10 Objekte sind 20 Muenzen wert
+          SetGramsPerUnits(1,10);    // 10 Objekte wiegen 1 Gramm
+          
+          AddSingularId( ({ "leiche","ameisenleiche" }) );
+          AddPluralId( ({ "leichen","ameisenleichen" }) );
+          
+          AddId("\n-MeineAmeisenleiche");
+        }
+
+        Wir basteln nun ein Unitobjekt, was zerfallen soll:
+        inherit "/std/unit";
+        protected void create() {
+            // Konfig s.o. ...
+            if (!clonep()) {
+              // Zerfall alle 3h
+              SetProp(P_UNIT_DECAY_INTERVAL, 3*3600);
+              // es zerfallen jeweils 10%
+              SetProp(P_UNIT_DECAY_QUOTA, 1000);
+              // es sollen min. 10 Einheiten jeweils uebrigbleiben
+              SetProp(P_UNIT_DECAY_MIN, 10);
+            }
+        }
+
+SIEHE AUCH:
+        P_AMOUNT, P_NAME, P_UNIT_DECAY_INTERVAL, P_UNIT_DECAY_QUOTA, 
+        P_UNIT_DECAY_MIN, P_UNIT_DECAY_FLAGS
+        DoDecay(), doDecayMessage(), SetGramsPerUnits(), SetCoinsPerUnits(), 
+        AddSingularId(), AddPluralId(), QueryCoinsPerUnits(),
+        QueryGramsPerUnits()
+        /std/unit.c
+
+14.10.2007, Zesstra
diff --git a/doc/std/util b/doc/std/util
new file mode 100644
index 0000000..8bf3527
--- /dev/null
+++ b/doc/std/util
@@ -0,0 +1,34 @@
+DEFINIERT IN: 
+     /std/*.c
+
+BESCHREIBUNG:
+     Diese Mangepage beschreibt kein Einzelobjekt, sondern eine Sammlung von
+     Programmen.
+     Im Verzeichnis /std/util/ liegen mehrere Helferklassen, die Funktionen
+     bereitstellen, die nicht nur einem Objekt verwendet werden.
+
+     Momentan sind dies:
+     executer.c   - stellt eine Funkton execute_anything() beret.
+     input.c      - stellt eine Funtion intput() bereit, welche inptu_to()
+                    kapselt.
+     pager.c      - Stellt die Funktion More() und ihre Helfer bereit.
+     ringbuffer.c - Stellt einen Ringpuffer und die notwendigen Verwaltungs-
+                    funktionen bereit.
+
+     Ringbuffer.c:
+     ------------
+     Dies ist ein Puffer (Array) einer bestimmten Groesse. Sobald er voll ist
+     wird automatisch die jeweils aeltesten Daten ueberschrieben, sobald etwas
+     neues hineingeschrieben wird. Ein Ringpuffer mit einer Groesse von 30
+     speichert also die letzten 30 hineingeschriebenen Objekte.
+     * CreateRingBuffer(): erstellt einen neuen Ringpuffer.
+     * ResizeRingBuffer(): aendert die Groesse eines Ringpuffers.
+     * RingBufferPut(): schreibt ein neues Datum in den Puffer.
+     * RingBufferGet(): liefert die Daten des Ringpuffers zurueck.
+
+SIEHE AUCH:
+     CreateRingBuffer, ResizeRingBuffer, RingBufferPut, RingBufferGet
+
+LETZTE AeNDERUNG:
+23.05.2008, Zesstra
+
diff --git a/doc/std/vererbungsbaeume b/doc/std/vererbungsbaeume
new file mode 100644
index 0000000..2df21cc
--- /dev/null
+++ b/doc/std/vererbungsbaeume
@@ -0,0 +1,293 @@
+
+  Vererbungsbaeume der Basis-MUDlib
+  =================================
+
+Einfache Gegenstaende
+---------------------
+
+Die einfachsten Objekte der Basis-MudLib. Die Module, die von thing.c
+geerbt werden, enthalten die wichtigsten Funktionen und treten auch in
+den anderen Standardobjekten immer wieder auf.
+
+Da die Ausgabe des Namens eines Objektes (dieser wird in den
+description-Modulen definiert) immer mit einer Deklination verbunden ist,
+findet man in jedem Objekt, welches ein description.c erbt, auch
+thing/language.
+
+thing
+  |- thing/properties    | Verwaltungsfunktionen des Property-Systems
+  |- thing/description   | Aussehen, Name, IDs des Objektes
+  |- thing/moving        | Bewegen und Zerstoeren des Objektes
+  |- thing/language      | Deklination von Namen, Adjektiven etc.; Pronomina
+  |- thing/commands      | vom Objekt definierte Kommandos
+  `- thing/restrictions  | Gewicht: standardmaessig 1Kg
+
+
+Ruestungen
+----------
+
+Bis auf thing/restrictions.c verfuegt armour.c ueber saemtliche Funktionalitaet
+die auch thing.c zur Verfuegung stellt. Zusaetzlich gibt es noch ein Modul,
+welches die Ruestung erst zur Ruestung macht: armour/combat.c
+
+armour                  | evtl. ein "(angezogen") an Kurzbeschreibung haengen
+  |- thing/properties   | Auch Ruestungen haben Eigenschaften
+  |- thing/description  | Aussehen, Name, IDs der Ruestung
+  |- thing/commands     | Kommandos sind auch moeglich
+  |- armour/moving      | Beim Bewegen/Zerstoeren: Ruestung ausziehen...
+  |    `- thing/moving  | ...und dann erst Bewegen/Zerstoeren
+  |- armour/combat      | Ruestungsklasse/-typ, Kampffunktion
+  `- thing/language     | und noch was zum deklinieren.
+
+
+Waffen
+------
+
+Wie Ruestungen, unterscheiden sich Waffen von einfachen Gegenstaenden im
+wesentlichen nur durch die Kampffunktionen.
+
+weapon                  | evtl. ein "(gezueckt)" an Kurzbeschreibung haengen
+  |- weapon/moving      | Waffe beim Bewegen/Zerstoeren erst wegstecken...
+  |    `- thing/moving  | ...und dann erst bewegen/zerstoeren
+  |- thing (s.o.)       | Ansonsten alle thing-Eigenschaften
+  `- weapon/combat      | Waffenklasse/-art, Schadenstyp, Kampffunktion
+
+
+Lichtquellen
+------------
+
+Lichtquellen sind (im Sinne der MudLib) normale Gegenstaende, die zusaetzlich
+die Eigenschaft haben, dass sie leuchten koennen (und auch verbrennen koennen).
+
+lightsource       | Alles, was zum Leuchten noetig ist
+  `- thing (s.o.) | ansonsten ein ganz normaler Gegenstand
+
+
+unit-Objekte
+------------
+
+unit-Objekte sind Gegenstaende, bei denen es sinnvoll ist, dass ein einzelnes
+Objekt eine gewisse Anzahl von gleichartigen Objekten repraesentiert. Das
+beste Beispiel hierfuer ist das liebe Geld: statt 1000 einzelner Muenzen im
+Inventory zu haben, hat man nur ein einziges Geldobjekt, das einer Menge von
+1000 Muenzen entspricht (die bei Kauf-/Verkaufsaktionen um eine entsprechende
+Menge erniedrigt oder erhoeht wird).
+Hierdurch wird a) die Uebersichtlichkeit erhoeht und b) natuerlich massig
+Speicher gespart.
+
+unit               | alle unit-Eigenschaften (Gewicht, Menge, Bewegen, ...)
+  `- thing (s.o.)  | ansonsten sind es normale Gegenstaende
+
+
+Spellbooks
+----------
+
+Fuer Gildenprogrammierer ist dies das Grundobjekt fuer das Zauberverzeichnis
+der Gilde.
+ACHTUNG: Obwohl thing.c geerbt wird, ist das Spellbook nicht zum clonen und
+"unter den Arm klemmen" gedacht! Vielmehr stellt thing.c hier im wesentlichen 
+nur das Property- und das Sprachmodul zur Verfuegung!
+
+spellbook                | Allgemeine Funktionen fuer Zaubersprueche
+  |- thing (s.o.)        | hier nur wg. Properties und Deklinationen
+  `- restriction_checker | fuer Einschraenkungen beim Zaubern
+
+
+Behaelter
+---------
+
+Die bisher beschriebenen Objekte kann man zwar mit sich herumtragen (ausser
+Spellbooks), aber man kann nichts hineinstecken. Hierzu sind einige weitere
+Funktionen noetig, die container.c zur Verfuegung stellt.
+Im wesentlichen sind das in container/restrictions.c Funktionen zum Aus-
+waehlen von Objekten im Behaelter, zum Testen, ob der Behaelter noch weitere
+Objekte aufnehmen kann (gewichtsabhaengig) und zur Ermittlung des Gesamt-
+gewichts des Behaelters.
+In container/description.c wird der Inhalt des Behaelters in eine Beschreibung
+umgewandelt.
+
+container
+  |- thing/properties        | DAS zentrale Modul...
+  |- thing/moving            | Bewegen/Zerstoeren des Behaelters
+  |- thing/commands          | Kommandos sind moeglich
+  |- container/description   | Beschreibung des Inhalts
+  |    `- thing/description  | Lang- und Kurzbeschreibung
+  |- thing/language          | Deklinationsmodul
+  `- container/restrictions  | Gesamtgewicht, Objektauswahl
+       `- thing/restrictions | Standardgewicht: 1Kg
+
+Leichen
+-------
+
+Leichen sind Behaelter mit der zusaetzlichen Eigenschaft, dass sie mit der
+Zeit zerfallen. Ausserdem geben sie ggf. noch eine Meldung ueber den
+Moerderkanal aus
+
+corpse                | Zerfallen, Moerdermeldung
+  `- container (s.o.) | sonst normaler Behaelter
+
+
+Raeume und ihre Abkoemmlinge
+----------------------------
+
+Raeume sind prinzipiell Behaelter mit Lang- und Kurzbeschreibung von INNEN
+und weiteren untersuchbaren Details (auch lesbare Details) sowie
+Ausgaengen und der Moeglichkeit vordefinierte Objekte im Raum zu plazieren.
+
+room
+  |- thing/properties     | Eigenschaften des Raumes
+  |- thing/language       | das obligatorische Sprachmodul
+  |- room/moving          | nur Zerstoeren; KEIN(!) Bewegen
+  |- room/restrictions    | Raeume werden nie voll
+  |    `- container/restrictions  | sonst die gleiche Funktionalitaet wie
+  |	    `- thing/restrictions | beim normalen Behaelter
+  |- room/description     | Raumbeschreibung, Details, etc.
+  |    `- container/description   | Beschreibung des Inhalts
+  |	    `- thing/description  | Beschreibung von aussen (selten sichtbar)
+  |- room/exits           | Verwaltung der Ausgaenge
+  |- room/commands        | notify_fail()s fuer "lies", "suche", "such"
+  |    `- thing/commands  | sonst normale Kommandobehandlung
+  |- room/items           | Verwaltung von Objekten, die im Raum sein sollen
+  `- room/doors           | Tueren (besondere Ausgaenge)
+
+
+Die Kneipe erweitert den Standardraum um Funktionen zur Definition der
+Speisen und Getraenke sowie um Befehle zum Bestellen und Ausgeben.
+
+pub
+  `- room (s.o.)
+
+
+Auch der Laden baut direkt auf dem Standardraum auf. Hier werden noch die
+ueblichen Ein- und Verkaufsbefehle zur Verfuegung gestellt.
+Jeder Laden benoetigt zusaetzlich einen Speicher, in dem verkaufte Objekte
+gelagert werden. Wenn der Laden schon beim ersten Betreten ueber ein
+gewisses Warensortiment verfuegen soll, kann man die mit AddItem()-Aufrufen
+im Speicher bewerkstelligen.
+
+laden
+  `- room (s.o.)
+
+store
+  |- thing/properties  | noetig fuer room/items.c
+  `- room/items        | vordefinierte Objekte im Speicher
+
+
+Die Post ist von der Programmierung her genau so zu behandeln wie ein
+normaler Raum. Die Postkabinen werden automatisch zur Verfuegung gestellt;
+man braucht sich in der Hinsicht um nichts zu kuemmern.
+
+post
+  `- (...) room
+
+
+Schiffe und aehnliche Transporter werden durch "bewegliche" Raeume realisiert.
+So hat man alle Beschreibungsmoeglichkeiten eines Raumes zur Verfuegung und
+kann (wie bei der Jolle geschehen) Ausgaenge in weitere Raume des Transporters
+legen (diese sind normale Raeume, KEINE Transporter!).
+Desweiteren sind Transporter die einzigen (Standard-)Raume, bei denen man
+auch die aeussere Lang- und Kurzbeschreibung zu sehen bekommt, weil man
+nicht nur in einem Transporter sein kann, sondern auch daneben stehen kann.
+
+transport         | Funktionen zur Festlegung der Route, An- und Ablegen
+  |- room (s.o.)  | die normalen Raumfunktionen
+  `- thing/moving | und hier steckt die Beweglichkeit
+
+
+Gilden
+------
+
+Gilden gibt es in zwei Ausfuehrungen: einmal als einfaches Gildenobjekt, dann
+aber auch als Gildenraum (wie zB. die Abenteurergilde).
+Waehrend das Gildenobjekt (gilden_ob) Funktionen zum Lernen von Faehigkeiten
+und Zauberspruechen, dem Gildenein- und -austritt sowie zum gildeninternen
+Aufstieg zur Verfuegung stellt (was zB. auch von einem NPC ausgeuebt werden
+koennte), verfuegt der Gildenraum zusaetzlich noch ueber Funktionen zum
+normalen Stufenaufstieg aufgrund von Abenteuer- und Erfahrungspunkten, die
+Questliste und die Kostenabfrage.
+
+gilden_ob                | Ein-/Austritt, Lernen, Gildenaufstieg
+  `- restriction_checker | Beschraenkungen bei obigen Aktionen
+
+gilden_room
+  |- gilde               | Stufenaufstieg, Questliste, Kosten
+  |    `- room (s.o.)    | normale Raumfunktionen
+  `- gilden_ob (s.o.)    | Gildenaufstieg etc.
+
+NPCs
+----
+
+Das Opfer. Zumindest meistens...
+
+npc
+  |- thing/properties            | Eigenschaften des NPC
+  |- living/description          | Ausgabe des Gesundheitszustandes...
+  |    `- container/description  | ...zusaetzlich zu seinem Inhalt...
+  |	    `- thing/description | ...und seiner Beschreibung
+  |- living/life                 | Die Lebensfunktionen, Essen, Trinken, Gift
+  |- living/attributes           | Die Verwaltung der Stats
+  |- living/moving               | Bewegen von Lebewesen
+  |- living/skills               | Funktionen fuer Faehigkeiten und Sprueche
+  |    `- living/std_skills      | und einige Standardfaehigkeiten
+  |- npc/combat                  | NPC-spezifische Kampffunktionen
+  |    `- living/combat          | der Kampf an sich
+  |- npc/chat                    | Sprueche a la Andy
+  |- npc/comm                    | Basiskommunikation: "sag", "echo", "emote"
+  |- container/restrictions      | wie bei Behaeltern
+  |    `- thing/restrictions
+  |- thing/language              | obligatorisch...
+  |- npc/info                    | Antworten auf Fragen
+  |- npc/put_and_get             | Reaktion auf erhaltene Gegenstaende
+  |    `- living/put_and_get     | Geben und Nehmen von Objekten
+  `- npc/guard                   | fuer Wach-NPCs
+
+Der intelligente NPC befindet sich noch in der Entwicklung.
+
+inpc
+  |- npc (s.o.)  | Erst mal ein ganz normaler NPC...
+  |- inpc/nobank | ...der gegen Bankzweitis vorgehen kann...
+  |    `- player/moneyhandler | ...und selbst Geld mitschleppt; ...
+  |- select      | ...die beste Ausruestung erkennt und auch benutzt...
+  `- boozing     | ...und in der Kneipe die beste Heilmoeglichkeit findet!
+
+
+Spieler- und Magiershell
+------------------------
+
+shells/magier                  | Hier auch noch Spielershells:
+  |- player/base               | Einloggen, Grundfunktionen
+  |    |- player/restrictions  | Maximale Zuladung (Staerke), InsertHooks
+  |    |    `- container/restrictions   | Maximalgewicht, Zuladungstest
+  |    |	 `- thing/restrictions  | Defaultgewicht
+  |    |- living/attributes    | Stats
+  |    |- living/combat        | Kampffunktionen
+  |    |- living/put_and_get   | Nehmen und Geben von Objekten
+  |    |- thing/properties     | DAS zentrale Modul...
+  |    |- thing/language       | Deklinationen
+  |    |- player/description   | Waffe/Ruestungen ausgeben
+  |    |    `- living/description           | Gesundheitszustand anzeigen
+  |    |	 `- container/description   | Ermittlung des Inhalts
+  |    |	      `- thing/description  | Kurz- und Langbeschreibung; IDs
+  |    |- player/moving        | Bewegen/Zerstoeren: Zusatzchecks
+  |    |    `- living/moving   | Bewegen/Zerstoeren von Lebewesen
+  |    |- player/life          | Zusatzchecks bei Lebensfunktionen
+  |    |    `- living/life     | allgemeine Lebensfunktionen
+  |    |- player/comm          | allgemeine Kommunikation
+  |    |    `- player/channel  | Kommunikation ueber die Kanaele
+  |    |- player/moneyhandler  | Geldverwaltung (auch fuer NPCs geeignet)
+  |    |- player/command       | Aliase, History, Parser
+  |    |- living/skills        | allg. Faehigkeitsfunktionen
+  |    |    `- living/std_skills | Standardfaehigkeiten
+  |    |- player/quests        | Verwaltung geloester Abenteuer
+  |    |- player/potion        | Verwaltung gefundener Zaubertraenke
+  |    |- player/soul          | Seelenkommandos (hilfe verben ;)
+  |    |- player/viewcmd       | Untersuchen etc.
+  |    |- more                 | More() fuer lange Texte
+  |    `- user_filter          | Hilfsmodul fuer "kwer in muenster" etc.
+  |                               | Ab hier: Magiershell
+  |- shells/filesys/filesys         | allgemeines Modul zur Dateibehandlung
+  |    |- shells/filesys/manual     | der "hilfe"-Befehl der Magier
+  |    |- shells/filesys/primitives | low-level-Routinen
+  |    |- shells/filesys/asynchron  | zur Vermeidung von Lags
+  |    `- shells/filesys/make       | komfortables updaten von Objekten
+  `- player/objects                 | Objekte clonen, updaten, zerstoeren
diff --git a/doc/std/virtual_compiler b/doc/std/virtual_compiler
new file mode 100644
index 0000000..ac8ec3d
--- /dev/null
+++ b/doc/std/virtual_compiler
@@ -0,0 +1,82 @@
+Was sind 'virtuelle Objekte'?
+=============================
+Virtuelle Objekte sind Objekte fuer die es keine explizite Quelldatei gibt.
+Ansonsten sind die Objekte (fast) genauso wie alle anderen auch.
+
+Was macht ein Virtual Compiler?
+===============================
+Wenn der Driver ein Objekt "/pfad/objekt" laden soll, guckt er erstmal nach,
+ob es so ein File (/pfad/objekt.c) auf der Platte gibt. Wenn ja, laedt er es
+ganz normal. Wenn nicht, passiert folgendes:
+
+1. Der Driver fragt den Master der Mudlib nach dem Objekt.
+2. Der Master guckt im Verzeichnis "/pfad/" nach, ob es ein virtual_compiler.c
+   dort gibt. Wenn ja, wird dieses virtual_compiler.c gefragt, ob es sich 
+   dafuer zustaendig fuehlt, ein Objekt mit dem Namen "/pfad/objekt" zu 
+   erzeugen.
+3. Wenn sich der VC fuer zustaendig fuehlt, clont er ein Objekt (z.B. sein
+   Standardobjekt, "stdob.c"), konfiguriert es evtl. noch ein bisschen und 
+   gibt es an den Master zurueck. Das erzeugte Objekt heisst an dieser Stelle
+   z.B. "/pfad/stdob#42" (weil es ein Clon von "/pfad/stdob" ist).
+4. Der Master gibt dieses erzeugte Objekt an den Driver zurueck.
+5. Der Driver benennt das vom Master erhaltene Objekt "/pfad/stdob#42" um in
+   das gewuenschte Objekt "/pfad/objekt". Von diesem Moment an ist es, als ob
+   es ein File "/pfad/objekt.c" gaebe, was dieses Objekt definiert.
+
+Das ist grundsaetzlich das, was ein VC macht.
+
+Was macht der VC in /std/?
+==========================
+Erstmal das oben beschriebene. Fuer welchen Pfad sich dieser VC zustaendig
+fuehlt und welches Objekt er jeweils clonen soll, wird per P_COMPILER_PATH und
+P_STD_OBJECT konfiguriert.
+Zusaetzlich fuehrt er besagte Liste seiner erzeugten Objekte, damit bei einem
+'ls' auf das Verzeichnis diese Objekte mit angezeigt werden.
+Weiterhin zerstoert er alle diese Objekte in seinem eigenen Remove.
+Ausserdem hat er 2 Funktionen, mit denen er entscheiden kann, ob er fuer ein
+bestimmtes angefragtes Objekt zustaendig ist, Validate() und
+QueryValidObject() und eine Funktion CustomizeObject(), die einem frisch
+geclonten Objekt sagen kann, welchen Namen es spaeter einmal haben soll (s.
+jeweilige Manapages).
+
+Wie baut man sich seinen eigenen VC?
+====================================
+Sagen wir, ihr wollt einen VC bauen, der jedem Spieler seinen eigenen Raum
+gibt, damit jeder Spieler in diesem Raum ein Monster allein erlegen muss,
+ohne dass andere Spieler mit drin sind.
+Dann braucht ihr zuerst mal einen Raum, der die Vorlage fuer alle VC-Raeume
+sein soll, z.B. std_arena.c. Diesen legt ihr in das Verzeichnis, wo der VC hin
+soll, z.B. /d/region/magier/vc/.
+In diesem Verzeichnis legt ihr nun ein File virtual_compiler.c an, welches
+/std/virtual/v_compiler erbt.
+Im create() setzt ihr nun P_COMPILER_PATH auf "/d/region/magier/vc/" und
+P_STD_OBJECT auf "/d/region/magier/vc/std_arena".
+Soll euer VC keine Para-Raeume erzeugen, setzt ihr P_PARA auf ({}).
+Sodann muesst ihr ein Validate() formulieren (s. Manpage), welches prueft, ob
+ein spaeter gewuenschtes Objekt den richtigen Namen (ohne Pfad) hat, z.B.
+'arena|spielername' und in diesem Fall genau diesen Namen zurueckliefert und
+in anderen Faellen 0.
+Das wars. Zumindest in diesem einfachen Fall seid ihr im wesentlichen fertig.
+Ihr koennt einen Spieler nun mit
+  pl->move("/d/region/magier/vc/arena|spielername",M_GO)
+in 'seinen' persoenlichen Raum bewegen.
+
+Bitte beachtet allerdings noch die Hinweise und Beschreibungen in den Manpages
+zu den einzelnen Funktionen und Properties.
+
+
+Beispiele:
+==========
+1. /doc/beispiele/virtual/zesstra/virtual_compiler.c
+   VC, der jedem Spieler einen eigenen Raum gibt. Da hier der Inhalt aller
+   Raeume gleich ist (alle Spieler sollen gleiche Bedingungen haben), ist
+   keine Konfigurierung mit Hilfe von CustomizeObject() noetig.
+
+2. /doc/beispiele/virtual/hate/vr_compiler.c
+   Dies ist ein Beispiel fuer einen sehr allgemeinen VC, der es 
+   erlaubt anzugeben in welchem Bereich sich die x und y Koordinaten befinden
+   und dann Raeume erzeugt, welche Ausgaenge in alle vier Himmelsrichtungen 
+   haben.
+
+-----------------------------------------------------------------------------
+27.10.2007, Zesstra
diff --git a/doc/std/weapon b/doc/std/weapon
new file mode 100644
index 0000000..3894c02
--- /dev/null
+++ b/doc/std/weapon
@@ -0,0 +1,78 @@
+ STANDARDKLASSE:
+	"/std/weapon"
+
+ BENUTZUNG:
+        inherit "std/weapon";
+
+	#include <properties.h>
+	#include <combat.h>
+
+ PROPERTIES:
+     Grundlegend:
+        P_WC		setzbar: Waffenklasse == Angriffsstaerke
+	P_WEAPON_TYPE	setzbar: Waffentyp
+	P_DAM_TYPE	setzbar: Schadenstypen
+	P_NR_HANDS	setzbar: Anzahl benoetigter Haende
+
+     Besondere Attribute und Anforderungen fuer Traeger:
+	P_RESTRICTIONS	setzbar: Anforderungen an Traeger
+	P_M_ATTR_MOD	setzbar: Attributmodifikator fuer Traeger
+	P_CURSED	setzbar: Verfluchung (nicht wegsteckbar)
+	P_PARRY		setzbar: Parierwaffe ?
+	P_AC		setzbar: Schutzfaktor einer Parierwaffe
+
+     Meldungen und Zeitpunkte:
+	P_EQUIP_TIME	enthaelt den Zeitpunkt des Anziehens
+	P_LAST_USE	enthaelt den Zeitpunkt des letzten Angriffs damit
+	P_WIELDED	enthaelt den Traeger
+	P_WIELD_MSG	setzbar: eigene Zueckmeldung
+	P_UNWIELD_MSG	setzbar: eigene Wegsteckmeldung
+
+     Dynamisches Verhalten in Kampf und beim Anziehen:
+	P_WIELD_FUNC	setzbar: Objekt mit Zueckfunktion "WieldFunc()"
+	P_UNWIELD_FUNC	setzbar: Objekt mit Wegsteckfunktion "UnwieldFunc()"
+	P_HIT_FUNC	setzbar: Objekt mit Angriffsfunktion "HitFunc()"
+
+     Zusaetzliche Eigenschaften:
+	P_DAMAGED	enthaelt den Ausmass des Schadens an Waffe
+	P_QUALITY	setzbar: Qualität/Haltbarkeit der Waffe
+	P_EFFECTIVE_AC	setzbar: falls HitFunc WC nicht sichbar aendert
+	P_EFFECTIVE_WC	setzbar: falls Parieren AC nicht sichbar aendert
+
+     Zusaetzlich sind alle Properties aus /std/thing verfuegbar, also
+     bitte auch folgende setzen:
+	P_MATERIAL	setzbar: Zusammensetzung
+	P_SIZE		setzbar: Groesse
+	P_WEIGHT	setzbar: Gewicht
+
+ MAKROS:
+     Gueltige Waffen- und Schadenstypen (definiert in "/sys/combat.h").
+
+ BESCHREIBUNG:
+     Basisklasse fuer alle Waffen im Spiel. Sie ist von /std/thing
+     abgeleitet und enthaelt alle zusaetzliche Funktionalitaet
+     fuer den Kampf.
+
+     Vor der Programmierung von Waffen sollte /doc/wiz/waffen
+     gelesen werden. Die Regeln darin sind verbindlich und sollten nur
+     in Ausnahmefaellen und mit Absprache mit dem Erzmagier fuer 
+     Waffen/Ruestungen/Monster ueberschritten werden.
+
+ VERERBUNGSBAUM:
+     [/std/weapon]
+     ..... [/std/thing/properties]
+     ..... [/std/thing/language]
+     ..... [/std/thing/commands]
+     ..... [/std/thing/restrictions]
+     ..... [/std/weapon/moving]
+     .......... [/std/thing/moving]
+     ..... [/std/weapon/description]
+     .......... [/std/thing/description]
+     ..... [/std/weapon/combat]
+
+
+ SIEHE AUCH:
+     P_WEAPON, P_PARRY_WEAPON, P_TOTAL_WC, P_TOTAL_AC, P_UNWIELD_TIME
+     Attack(), Defend(), /doc/wiz/waffen
+
+ 20 Maerz 2004 Gloinson
diff --git a/doc/wiz/.readme b/doc/wiz/.readme
new file mode 100644
index 0000000..40d049c
--- /dev/null
+++ b/doc/wiz/.readme
@@ -0,0 +1,2 @@
+Hier sind allgemeine Themen und Regelungen fuer Magier beschrieben.
+
diff --git a/doc/wiz/.synonym b/doc/wiz/.synonym
new file mode 100644
index 0000000..6c9523f
--- /dev/null
+++ b/doc/wiz/.synonym
@@ -0,0 +1,17 @@
+sponsor befoerderungen
+sponsoring befoerderungen
+befoerderung befoerderungen
+npc npcs
+materialien material
+flueche gift
+fluch gift
+gifte gift
+vergiftung gift
+krankheit gift
+krankheiten gift
+armour ruestungen
+armours ruestungen
+ruestung ruestungen
+weapon waffen
+weapons waffen
+waffe waffen
diff --git a/doc/wiz/anfaenger b/doc/wiz/anfaenger
new file mode 100644
index 0000000..d240909
--- /dev/null
+++ b/doc/wiz/anfaenger
@@ -0,0 +1,116 @@
+Anfaengergebiete
+
+Vielleicht eruebrigt es sich, einen laenglichen Leitfaden zu schreiben,
+weil dies eigentlich Gebiete wie viele andere sein sollten:
+ausreichend beschrieben, logisch durchdacht, balanciert, was NPC und
+Objekte wie Waffen und Ruestungen betrifft, kurzum, es muss Spass fuer
+Spieler machen.
+
+Einiges unterscheidet aber ein solches Gebiet von anderen, denn wie der
+Name schon sagt, sollte man dort eher hauptsaechlich Anfaenger (einmal
+willkuerlich als Spielstufe 1-5 bezeichnet) erwarten.
+
+1. Erreichbarkeit
+Viele Anfaengergebiete befinden sich natuerlich in unmittelbarer Naehe
+einer Rasse, z.B. die Hochebene bei den Zwergen, der Park der Freunde
+bei den Elfen oder der kleine Dschungel im NW von Katzmandu. Hierbei
+ist die Naehe fuer eine Rasse gegeben, so dass zumindest die kleinen
+Spieler dieser Rasse das Gebiet leicht erreichen koennen. Natuerlich
+ist es fuer andere Rassen nicht unbedingt einfach, einige weit entfernt
+gelegene Anfaengergebiete zu erreichen, ohne Dutzende von Raeumen zu
+durchlaufen.
+Man kann auch nicht sofort davon ausgehen, dass sich ein Anfaenger von
+Anfang an eine Karte fuer Wege anlegt. Insofern ist es sinnvoll, wenn
+es Anfaengergebiete in unmittelbarer Naehe zu den Startpunkten der
+Rassen gibt. Die koennen dann entweder unter dem Ebenenpfad oder auch
+unter /d/anfaenger stehen.
+Allgemein zugaengliche Anfaengergebiete sollten zumindest ohne grossen
+Aufwand vom Startpunkt der Rassen aus zu erreichen sein. Das koennte
+durch spezielle Hilfsmittel geschehen, durch NPC, die einen den Weg
+dorthin weisen (vielleicht durch Ueberarbeitung von bestehenden Files)
+oder durch teleportierende Objekte, die nur fuer Anfaenger benutzbar
+sind. Ziel ist es nicht, den Spielern das Laufen und selbststaendige
+Erkunden abzunehmen, sondern nur die Gefahr, sich ueber laengere
+Strecken hin zu "verfransen".
+
+2. Bekanntheitsgrad
+Diesem Punkt sollte man ein wenig Zeit widmen. Am einfachsten ist die
+Bekanntheit zu erreichen, wenn im Gebiet eine Quest spielt, da dann
+frueher oder spaeter zwangslaeufig eine Frage gestellt wird. Ist es
+eine Miniquest, koennte eine Erfolgsmeldung ueber die Ebene Anfaenger
+flimmern statt ueber die Ebene Abenteuer. Wenn es passt, koennten
+auch diverse NPC mit Informationen versorgt werden, oder andere Objekte,
+die man an prominenter Stelle plaziert (Notizen, Buecher, Aufzeichnungen
+oder aehnliches).
+
+3. NPC
+Auch wenn es nur "kleine" NPC sind, kann man in sie mindestens so viel
+Energie fuer Informationen, Verhalten und Details stecken wie fuer
+"grosse". Sie sollten keinesfalls aggressiv sein. Hoher Body (~ >70)
+oder hohe Hands (~ >100) sind zu vermeiden, da man als Anfaenger noch
+kein Gefuehl dafuer hat, was die Schadensmeldungen "hart", "krachende
+Knochen" oder "kitzelt Dich" in etwa an Schaden machen. Dementsprechend
+waere es auch sinnvoll, die NPC zu schreiben. Bedrohlich aussehende
+Drachen, die immer verfehlen, sind eigentlich ebenso wenig plausibel
+wie kleine Ameisen, die einen mit der Saeure immer sehr hart treffen.
+Im Zweifel einfach einen Lvl 1 Testspieler anlegen und ausprobieren,
+mal ganz ohne Waffe (wie komme ich als "nackter" Neuling an meine erste
+Waffe?), mal mit einer schwachen, mal mit einer etwas besseren Waffe.
+Als Ausnahme darf hier gerne auch mal ein eigentlich schwacher NPC
+Killstufenpunkte (Killstupse) vergeben.
+
+4. Cleaning Up
+Hoffentlich besuchen dann auch viele das Gebiet. Eventuell empfiehlt es
+sich dann (wie bei vielen anderen Gebieten aber auch) zu ueberlegen,
+was passiert, wenn eine Unzahl geschriebener Objekte, die viele dann
+doch nicht gebrauchen/mitnehmen, im Raum herumliegt. Bei Ruestungen und
+Waffen sind die Argumente CLONE_WIELD und CLONE_WEAR in der Funktion
+AddItem vorteilhaft, bei anderen Objekten koennte man in deren Reset
+nachsehen, ob sie laengere Zeit in einem Raum des Gebiets oder im Gebiet
+selbst "herumgammeln". Es bleibt zwar zu hoffen, dass sie irgendwann
+dem Ausswappen des Raumes anheimfallen, aber es sieht im allgemeinen
+nicht schoen aus, wenn Objekte oder gar quest-/miniquestrelevante Teile
+sich in einem oder mehreren Raeumen stapeln.
+
+5. ZTs
+Sie helfen, das Gebiet bekannter zu machen (siehe Punkt 2), sollten
+aber einfach zu finden sein (Detail, einfacher Befehl) und nicht gleich
+eine ganze Miniquest beinhalten.
+
+6. Unterstuetzung von wichtigen Standarditems
+Dazu gehoeren immer noch Seil, Fackel oder Schaufel. Eventuell braucht
+man sie fuer die eine oder andere Aufgabe, ihre Benutzung und die damit
+verbundene Syntax sollte dem Anfaenger danach gelaeufig sein.
+
+7. Unterstuetzung von weit verbreiteten Befehlen
+Dazu zaehlen "oeffne", "nimm", "suche/durchsuche", "schliesse", etc.,
+also das, was in vielen Quests auch mitunter gefragt ist.
+
+8. Vielfalt in dem, was man "tun" kann, einfache Umsetzung
+Man koennte diesen Punkt auch unter "einfacher Code fuer Anfaenger"
+zusammenfassen, das ginge aber manchmal zu weit. Wichtig in diesem
+Zusammenhang ist auch, dem Anfaenger spielerisch im Text, durch Details
+oder Infos von NPC die Syntaxen, wenn sie denn einmal abseits der so
+ueblichen Kommandos sind, ein wenig in den Mund zu legen:
+(Spieler weiss, dass in diesem Raum ein ZT fuer ihn ist, es hat was mit
+dem Baum zu tun, vor dem er steht)
+"unt baum" ->
+"Der Baum ist recht beeindruckend und hoch. Dennoch koenntest Du ohne
+ weiteres auf ihn klettern." ->
+"kletter auf baum/baum hoch" ->
+Erfolg
+
+9. Belohnungen
+Sie duerfen ruhig grosszuegig sein: ZTs (Zaubertraenke), FP (Forscher-
+punkte), EK (Erstkills/Killstupse), AP (Abenteuerpunkte/Questpunkte)
+und MQP (Miniquestpunkte) sind wohl dosiert und abwechslungsreich zu
+verteilen und sollen dem Spieler helfen, den anfangs langen Weg bis
+zum naechsten Level zu verkuerzen.
+
+Wie ueberhaupt hilft eine gute Dokumentation auch einem Anfaenger unter
+den Magiern, selbst schnell eine eigene Referenzsammlung fuer einfache
+Dinge in LPC zu erstellen. Man koennte also sagen, wie der Spieler im
+Anfaengergebiet waechst auch der Magier, der es geschrieben hat.
+
+-----------------------------------------------------------------------
+Last modified: Tue Mar 21 01:46:35 2006 by Ark
diff --git a/doc/wiz/angriff b/doc/wiz/angriff
new file mode 100644
index 0000000..a1ee7b8
--- /dev/null
+++ b/doc/wiz/angriff
@@ -0,0 +1,18 @@
+--------Angreifer
+Attack()
+	Holt die Waffe des Spielers.
+	Berechnet den Angriffstyp und gibt die
+	Angriffsmeldung aus.
+
+	Aufruf von:
+
+--------Verteidiger     
+Defend( hp, typ )
+	Gibt die Verteidigungsmeldung aus.
+	Berechnet die Anzahl der HP die abgezogen wird.
+
+	Aufruf von:
+
+do_damage( hp )
+
+	Berechnen der HP. Gegebenenfalls sterben etc.
diff --git a/doc/wiz/artillerie b/doc/wiz/artillerie
new file mode 100644
index 0000000..349cec4
--- /dev/null
+++ b/doc/wiz/artillerie
@@ -0,0 +1,61 @@
+Regeln fuer Artillerie-Objekte
+==============================
+
+1. Definition von Artillerie
+----------------------------
+
+Unter dem Begriff "Artillerie" fasst man alle Objekte zusammen, die
+zusaetzlich zu den vorhandenen Gildenfaehigkeiten durch ein vom
+Spieler initiiertes Kommando direkt Schaden an einem oder mehreren
+Gegnern verursachen.
+
+Waffen und Ruestungen, welche per Kommando Schaden verursachen (z. B.
+Eisstab, Ring vom Schlammdrachen), fallen unter diese
+Definition. Waffen und Ruestungen, die "von sich aus" (ueblicherweise
+per Hit-/DefendFunc) Schaden verursachen (z. B. Todesdrachenpanzer,
+Goblinring), fallen nicht unter diese Definition, sind aber natuerlich
+nach wie vor genehmigungspflichtig.
+
+2. Basisanforderungen
+---------------------
+ 
+Solche Artillerie muss folgenden Anforderungen genuegen:
+
+ a. Artillerie _muss_ P_ATTACK_BUSY beachten (setzen/abfragen).
+ b. Artillerie _muss_ bei Verwendung Konzentrationspunkte 
+    verbrauchen.
+ c. Artillerie darf nicht (bzw. nur begrenzt) gehortet werden
+    koennen.
+ d. Artillerie darf bei Paralyse nicht angewendet werden koennen
+    (P_DISABLE_ATTACK).
+ e. Artillerie muss durch Setzen der Property P_IS_ARTILLERY 
+    gekennzeichnet sein. 
+ f. Artillerie, die Munition benutzt oder selbst Munition ist (wie z.B.
+    Wurfsterne), muss das Unitobjekt und den dortigen Mechanismus zum
+    Zerfall von Objekten nutzen. Dabei sind P_UNIT_DECAY_INTERVAL und
+    P_UNIT_DECAY_QUOTA so zu setzen, dass die Munition eine Halbwertszeit
+    zwischen 5 und 10  Tagen hat, der ihrer Verfuegbarkeit angemessen
+    ist. Dies laesst sich durch geeignetes Einstellen des
+    Prozentsatzes und/oder der Resetzeit erreichen.
+
+    Beispiele:
+    Setzt man p=1% Zerfall pro Reset an, dann muss der Reset
+    fuer eine Halbwertszeit von 5 Tagen dann 
+    t=ln(0.5)/ln(0,99)*5*24*3600 s dauern, das sind 6224 s.
+
+    Moechte man lieber den normalen Reset, der im Mittel 45 min, d.h. 160
+    Resets in 5 Tagen betraegt, so erhaelt man folgenden Prozentsatz:
+    p = 1-0,5^(1/160) d.h. ca. 0,43%.
+
+Details werden fallweise entschieden.
+
+
+3. Inkraftreten
+---------------
+
+Diese Regeln treten am 5. August 2003 in Kraft.
+Revision am 5. April 2008
+Revision am 7. April 2011
+
+--------------------------------------------------------------------------
+Letzte Aenderung: Sam,  7. April 2011 von Humni
diff --git a/doc/wiz/autoload b/doc/wiz/autoload
new file mode 100644
index 0000000..f3cd444
--- /dev/null
+++ b/doc/wiz/autoload
@@ -0,0 +1,32 @@
+AUTO_LOADING:
+	Rumata 26.08.92
+
+	DER NEUE AUTOLOAD-MECHANISMUS
+
+FUNKTIONEN:
+	void SetProp( P_AUTOLOADOBJ, mixed wert );
+	mixed QueryProp( P_AUTOLOADOBJ );
+
+ERLAEUTERUNG:
+	Es gibt jetzt eine Property P_AUTOLOADOBJ, die jedes Objekt
+	setzen kann. Ist der Wert dieser Property nicht 0, so gilt
+	das Objekt als Autoloading-Objekt. 
+	WICHTIG: Auch Werte wie "" oder ({ }) sind nicht 0.
+
+	Mit der Funktion QueryProp( P_AUTOLOADOBJ ) kann dieser Wert
+	abgefragt werden.
+
+BEMERKUNGEN:
+	Die Property P_AUTOLOADOBJ sollte nicht mit der
+	Property P_AUTO_LOAD verwechselt werden. Diese speichert
+	die Autoload-Informationen im Spieler.
+
+	Die Property P_AUTOLOADOBJ kann selbstverstaendlich auch als
+	Built-in-Property implementiert werden.
+
+BEISPIEL:
+	Ein einfaches Beispiel steht in ***
+	Ein leistungsfaehiges Beispiel steht in /obj/tools/muschel.c
+
+SIEHE AUCH:
+	"doc/MG/properties"
diff --git a/doc/wiz/balance b/doc/wiz/balance
new file mode 100644
index 0000000..efea050
--- /dev/null
+++ b/doc/wiz/balance
@@ -0,0 +1,78 @@
+Beurteilung und Genehmigung von Waffen und Ruestungen im MG
+===========================================================
+
+    
+    Was bedeutet Balance im Morgengrauen? Die Balance (bzw. ihr Fehlen)
+    wird vor allem dann wahrgenommen, wenn Spieler sich gelangweilt durch
+    Gebiete zappen, die vor zwei oder drei Jahren noch als anspruchsvoll
+    galten, oder wenn ein Magier feststellen muss, dass sein schoenes 
+    Riesenmonster nichtmal dazu kommt, seine ausgefeilten Zaubersprueche
+    anzuwenden, weil es nach durchschnittlich zweieinhalb Runden schon
+    tot ist. Fehlentwicklungen dieser Art zu verhindern ist Aufgabe der 
+    Balance. Sie stellt universelle Grenzwerte und Masstaebe, die als
+    gemeinsamer Nenner fuer alle Magier in diesem Mud eine gewisse 
+    Ausgewogenheit garantieren. Anhand dieser Werte kann jeder Magier auf
+    der einen Seite einschaetzen, wie begehrt ein neues Objekt sein wird,
+    und auf der anderen weiss er ungefaehr, was auf seine armen Monster
+    zukommen wird, wenn Spieler mit den Objekten anderer Magier angreifen.
+    
+    Diese Aufgabe zu bewaeltigen kann jedoch in einem so riesigen Mud keine 
+    Instanz allein leisten. Ein grosse Verantwortung lastet auch auf den 
+    Regionsmagiern, die innerhalb ihres Verantwortungsbereichs dafuer Sorge
+    tragen sollten, dass aussergewoehnliche Objekte in Gebieten nicht zu
+    gehaeuft auftreten und nicht zu leicht zu erreichen sind.
+
+    Wie laeuft nun eine Genehmigung durch die Balance ab? 
+    Zuallererst sollte sich ein Magier ueber den aktuellen Stand der Balance-
+    regeln informieren. Zum einen weiss er dann sofort, welche Ideen ohnehin
+    keine Chance auf Genehmigung haben, zum anderen inspirieren vielleicht 
+    sogar die zahlreichen Hinweise und technischen Details das ein oder 
+    andere Objekt. Als naechstes steht eine kritische Pruefung der eigenen
+    Ideen an. Braucht das Mud wirklich den 132. Panzer, der gegen Feuer
+    schuetzt? Gibt es nicht irgendwo ohnehin schon eine Waffe dieses Typs
+    mit fast identischen Werten? Addiert sich die Wirkung eines geplanten
+    Kampfobjekts vielleicht irgendwie mit vorhandenen Objekten? Diese und
+    aehnliche Fragen frueh zu klaeren kann spaeter viel Frust vermeiden. Das
+    Balanceteam kann auch zu diesem Zeitpunkt schon ein Ansprechpartner sein,
+    da seine Mitglieder einen guten Ueberblick ueber die meisten heraus-
+    ragenden Objekte im Mud haben und gerne als Berater zur Verfuegung 
+    stehen. Die Magier des Teams koennen natuerlich auch zu programmier-
+    technischen Fragen den einen oder anderen Tip geben.
+
+    Sind die Objekte dann programmiert und ist auch der Regionsmagier mit
+    ihnen einverstanden, wird schliesslich der Antrag gestellt. (Per
+    "mail balance"). Der Antrag sollte folgenden Informationen enthalten:
+
+    - Name des Objekts
+    - Vollstaendiger Filename
+    - Alle relevanten Werte: AC bzw. WC, P_NR_HANDS, Gewicht, Wert, ...
+    - Defend/HitFuncs, in Form einer praezisen Beschreibung der 
+      Funktionsweise und des Wertebereichs.
+    - Alle sonstigen Informationen ueber Eigenschaften, die den Kampf 
+      beeinflussen oder dem Spieler von Nutzen sind
+    - Fundort und subjektive Einschaetzung des Aufwands, um diesen 
+      Gegenstand zu ereichen.
+
+    Die Genehmigung erfolgt dann per mail, es sei denn es gibt begruendete
+    Einwaende. In diesem Fall kommt die Genehmigung mit leicht geaenderten
+    Werten oder sogar eine Ablehnung. Im letzteren Fall steht es dem Magier
+    natuerlich frei, das Objekt zu veraendern und neu zu beantragen.
+    Normalerweise ergibt sich dann auch schnell im persoenlichen Gespraech
+    mit Mitgliedern der Balance, wo das Problem genau liegt und welche 
+    Alternativen es gibt. 
+
+    Anzumerken bleibt noch, dass Objekte, die nach der Genehmigung noch in
+    irgendeiner Weise positiv veraendert werden, natuerlich neu genehmigt
+    werden muessen. Bei kleineren Aenderungen (Gewicht o.ae.) reicht
+    unter Umstaenden eine informelle Anfrage beim Balanceteam.
+
+ SIEHE AUCH
+
+     balanceantrag, ruestungen, waffen, fernwaffen, uniques, npcs, objects,
+     attributsveraenderungen, resistenzen, kampfobjekte, grenzwerte,
+     nachteile
+
+
+------------------------------------------------------------------------------
+ LETZTE AENDERUNG:
+    Son, 18.10.2005, 18:54:00 von Miril
diff --git a/doc/wiz/balanceantrag b/doc/wiz/balanceantrag
new file mode 100644
index 0000000..6124640
--- /dev/null
+++ b/doc/wiz/balanceantrag
@@ -0,0 +1,133 @@
+Hilfe zu Balanceantraegen
+=========================
+
+Um die Arbeit der Balance zu erleichtern und damit die Wartezeit fuer die
+Magier zu verkuerzen, gibt es einige Dinge zu beachten. Das Ganze liest sich
+auf den ersten Blick sehr buerokratisch, ist es aber nicht. Die Anleitung soll
+nur so ausfuehrlich wie moeglich sein, um Missverstaendnissen vorzubeugen.
+Zuerst einmal gibt es grundsaetzlich zwei Arten von Antraegen:
+
+1. Eine Vorabanfrage
+
+Viele Magier scheuen die Arbeit, ein Objekt zu programmieren, das anschliessend
+nicht von der Balance genehmigt wird. Das ist verstaendlich und Vorabanfragen
+werden nicht minder beachtet als konkrete Antraege. Trotzdem sollte man so eine
+Anfrage sorgfaeltig formulieren. Sie sollte das Problem klar umreissen und
+moeglichst eindeutig sein. Auch ein Hinweis darauf, wo der Magier Probleme mit
+der Balancierung erwartet und in welchem Wertebereich eventuelle Zahlen
+erwartet werden, sollte nicht vergessen werden.
+
+Eine Antwort auf eine Vorabanfrage ist allerdings keine Genehmigung eines
+Objekts. Es ist lediglich als Information zu sehen, ob die Balancemitglieder 
+so ein Objekt ueberhaupt fuer genehmigungfaehig halten, gegebenenfalls mit
+angepassten Werten oder leicht veraenderten Eigenschaften. Mit einer Antwort
+auf eine Vorabanfrage wollen wir dem Wunsch der Magier entsprechen, zu
+erfahren, ob es sich lohnt, Arbeit in das Objekt zu investieren, bindend ist
+so eine Antwort allerdings nicht.
+
+2. Ein Antrag
+
+Hier geht es um die Festschreibung aller kampfrelevanten Eigenschaften,
+trotzdem sollten alle Eigenschaften, die Einfluss auf das Spielgeschehen haben,
+mit angegeben werden, auch wenn die Objektbalance nicht direkt fuer diese
+Eigenschaft zustaendig ist. Eine Waffe, die den Traeger ab und zu heilt,
+muss auch bei der Heilungsbalance beantragt werden, trotzdem darf diese
+Eigenschaft nicht vor der Objektbalance verschwiegen werden. Alle Werte
+sollten tabellarisch aufgefuehrt werden. Eine Uebersicht, ueber die von der
+Balance benoetigten Werte:
+
+Kurzbeschreibung:        P_SHORT
+Pfadangabe:              momentan und nach Anschluss
+Typ des Objekt:          Schwert, Hose, Ring...
+Waffen/Ruestungsklasse:  P_WC, P_AC
+Schadensart:             P_DAM_TYPE
+Benoetigte Haende:       P_NR_HANDS
+Wert:                    P_VALUE
+Gewicht:                 P_WEIGHT
+HitFunc:                 Mit Erlaeuterung der Umstaende,
+                         Begruendung und Werte/Wahrscheinlichkeiten
+Andere Boni:             Kaempfer, Zauberer, Heilungen, Resistenzen
+                         evtl mit rollensp. Begruendung
+Restriktionen:           P_RESTRICTIONS, Paralyse, Zeitbeschraenkungen 
+
+Eine Begruendung sollte die Eigenschaft rollenspieltechnisch einbetten und
+nicht beschreiben, warum das MG diese Eigenschaft in einem Objekt unbedingt
+noch braucht. Weiterhin sollte man die Erreichbarkeit kurz umreissen und die
+Haeufigkeit, mit der das Objekt an Spieler gelangt. Auch hierbei gilt, dass
+man das Objekt moeglichst kurz, aber eindeutig beschreibt.
+
+Die Genehmigung oder Ablehnung eines Antrags durch die Objektbalance ist
+bindend. Eigenschaften, die in der Antwort aufgefuehrt sind, sind
+festgeschrieben und beduerfen bei Aenderung eines Neuantrages (das geht in
+der Regel recht schnell, wenn der Magier nicht das Objekt komplett umkrempelt).
+Sollten nicht festgeschriebene Eigenschaften, die das Spielgeschehen
+beeinflussen (die es eigentlich nicht geben sollte), geaendert werden, muss die
+Balance informiert werden. Bei jeglichem Mailverkehr zu einem Objekt, sollte
+die btop Nummer angegeben werden, die in der ersten Mail zu dem Objekt
+angegeben sein sollte. Ist dies nicht der Fall, erfrage diese Nummer bitte bei
+einem der Teammitglieder. Nach Genehmigung des Objektes, sollte diese Nummer
+auch in den Kopf der Datei(en) geschrieben werden.
+
+Bei einem Antrag kommt es immer wieder zu Misverstaendnissen oder
+Zweideutigkeiten. Daher ist es die Regel, dass noch mal nachgefragt wird,
+wie denn genau etwas ablaufen oder implementiert werden soll. Eine schnelle
+Beantwortung dieser Fragen beschleunigt natuerlich auch den
+Genehmigungsprozess. Missfallen dem Balanceteam einzelne Eigenschaften,
+Implementierungen oder Werte, werden haeufig Vorschlaege gemacht, die die
+Akzeptanz eines Objektes durch die Balance entscheidend verbessern. Die
+Mitglieder bemuehen sich, den Genehmigungsprozess so reibungslos und
+konfliktarm wie moeglich ablaufen zu lassen. Solche Aenderungsvorschlage sind
+weder Belehrungen noch boese gemeint, sondern sollen dazu dienen die
+Balancierung zu vereinfachen, zu beschleunigen und Bedenken von
+Balancemitgliedern entgegenzukommen.
+
+Zum Schluss noch ein Antrag, wie er aussiehen koennte:
+
+Antrag fuer ein Zweihandschwert
+-------------------------------
+
+Name: Der Perforator
+Pfad: /d/region/magier/gebiet/obj/perforator.c
+      /d/region/magier/gebiet/obj/loch.c
+P_WEAPON_TYPE:  WT_SWORD
+P_WC:           200
+P_NR_HANDS:     2
+P_VALUE:        15000
+P_WEIGHT:       1500
+P_DAM_TYPE:     ({DT_PIERCE, DT_UNHOLY})
+HitFunc:        unter Umstaenden random(50)
+K_BRAWLING_WC:  25
+K_CRITICAL_HIT: 80
+
+Pseudo-Unique: Es gibt 10 Stueck, wird ein Elftes gebraucht, wird
+das mit der aeltesten Clonezeit zerstoert.
+
+Restriktionen: MiniQuest, Seher, Beteiligung beim Ermetzeln
+
+Beschreibung: Der Perforator perforiert den Gegner. Ist noch kein Loch
+in dem Gegner, wird eins reingepiekt und macht dabei zusaetzlich random(50)
+Schaden. Danach ist ein Loch im Gegner und er verliert jede Runde 10 +
+random(10) HP durch Blutverlust per reduce_hit_points() fuer 10 Runden.
+Ist der Traeger des Schwerts noch im Raum, bekommt er diesen Schaden
+gutgeschrieben, weil das Schwert das Blut trinkt. Ist das Loch weg, gibt es
+kein weiteres.
+
+Das Schwert hat ein schweres, mit Dornen versehenes Heft (BRAWLING_WC) und
+ist rasiermesserscharf, so dass es sich fuer einen Todesstoss gut eignet.
+Das Schwert hat einen Fluch, der es blutdurstig macht und DT_UNHOLY erklaert.
+
+Um das Schwert zu bekommen, braucht man eine Miniquest, die ca 20 AP geben
+wuerde. Am Ende kommt man an einen Endgegner, den man im Team hauen muss,
+alle Mitglieder muessen die Miniquest gerade machen. Zuecken kann das Schwert
+nur jemand, der die Miniquest schon hat und mindestens 20% des Schadens gemacht
+hat. Von dem Schwert gibt es immer nur 10 Stueck, das aelteste wird beim
+clonen des NPC zerstoert und ein Neues erschaffen.
+
+-----------------------------------
+
+ SIEHE AUCH
+
+ ruestungen, waffen, fernwaffen, uniques, npcs, objects, balance
+ attributsveraenderungen, resistenzen, kampfobjekte, grenzwerte, nachteile
+------------------------------------------------------------------------------
+letze Aenderung: 18.10.2005 23:41 - Miril
diff --git a/doc/wiz/befoerderungen b/doc/wiz/befoerderungen
new file mode 100644
index 0000000..f680c08
--- /dev/null
+++ b/doc/wiz/befoerderungen
@@ -0,0 +1,66 @@
+Eine kleine Uebersicht ueber die Befoerderungspolitik im MG
+-----------------------------------------------------------
+
+ Level 1 -> 15:
+     WANN:
+        Man muss Seher sein. (s. auch "hilfe magier")
+     WER:
+        26+ (siehe auch "hilfe vertrag")
+     RECHTE:
+        Eingeschraenkte Leserechte auf der Lib.
+
+ Level 15 -> 20:
+     WANN:
+        Mindestens eine Woche seit dem Anstieg auf 15, damit
+        Einsprueche gegen den Charakter nicht uebergangen werden.
+        Konzept fuer GS
+     WER:
+        Prinzipiell 66+
+        Normalerweise aber EM Gesellenstuecke und Zook
+     RECHTE:
+        Leserechte auf allgemeine Verzeichnisse, eigenes Verzeichnis, Clonen.
+
+ _Alternativ_ gibt es weitere Moeglichkeit:
+ Level 0 -> 20:
+     WANN:
+        Man muss einen Seher als Erstie und ein von den EMs genehmigtes
+        Konzept fuer ein GS haben. Desweiteren verpflichtet man sich, das
+        GS innerhalb von 6 Monaten fertigzustellen. (s. "hilfe magier")
+     WER:
+        EMs (siehe auch "hilfe vertrag")
+     RECHTE:
+        Eingeschraenkte Leserechte auf der Lib.
+
+
+ Level 20 -> 21:
+     WANN:
+        nach GS-Abnahme
+     WER:
+        EM fuer Gesellenstuecke.
+     RECHTE:
+        Normale Leserechte.
+
+ Level 21 -> 25:
+     WANN:
+        Magier hat etwas fuer eine Region geproggt, was angeschlossen
+        wurde/wird.
+     WER:
+        45
+     RECHTE:
+        Schreibrechte in der Domain. Vergabe durch zustaendigen RM.
+
+ Level 25 -> 26+:
+     WANN:
+        Magier wird fuer faehig und selbststaendig gehalten.
+     WER:
+        45
+     RECHTE:
+        Kann nun selbst Spieler sponsoren.
+ 
+ Vorsicht: dieses Dokument ist im stetigen Wandel und stellt nur die
+ allgemeine Politik dar - sich drauf berufen gilt nicht.
+
+SIEHE AUCH:
+     erzmagier, balance
+
+27.10.2009, Zesstra
\ No newline at end of file
diff --git a/doc/wiz/benennung b/doc/wiz/benennung
new file mode 100644
index 0000000..fe94ebc
--- /dev/null
+++ b/doc/wiz/benennung
@@ -0,0 +1,158 @@
+AUTOR   MG, den 22.08.92, Don Rumata
+	This file is part of the Morgengrauen-Mudlib from Jof and Rumata
+
+VERSION 1.1
+
+AKTUALISIERT: MG, den 14.10.93, Chris
+UEBERARBEITET: am 24.08.94 von Viper
+
+THEMA   BENENNUNG VON OBJEKTEN IM MorgenGrauen
+
+INHALT:
+	I.      Einleitung
+	II.     Funktionen, die alle Objekte besitzen muessen
+	III.    Unterstuetzung der Funktionen durch die MG_mudlib
+	IV.     Benennung von Raeumen
+	V.      Benennung von Monstern
+
+I.      EINLEITUNG
+
+	Jedes Objekt in der Mudlib muss auf folgende Weisen identifiziert
+	werden koennen:
+
+	1.)     Es muss einen Namen haben, der innerhalb eines Satzes das
+		Objekt bezeichnen kann.
+
+	2.)     Es muss eine Kurzbeschreibung besitzen.
+
+	3.)     Es muss eine ausfuehrliche Beschreibung besitzen.
+
+	4.)     Es muss eine Menge von Synonymen kennen, mit denen ein
+		Spieler das Objekt "ansprechen" kann.
+
+II.     FUNKTIONEN, DIE ALLE OBJEKTE BESITZEN MUESSEN
+
+	Jedes Objekt im MorgenGrauen sollte folgende Funktionen 
+	implementiert haben:
+
+	1.)     name( fall, dem )
+
+	Diese Funktion soll den Namen des Objektes im jeweiligen
+	Fall mit Artikel (wenn es einen besitzt) zurueckgeben, so
+	dass der Rueckgabewert in einen Satz eingearbeitet werden
+	kann.
+
+	"fall" dient zur Deklination des Objektnamens.
+
+	"dem" bestimmt, welche Artikel benutzt werden sollen.
+	Moegliche Werte fuer "dem":
+
+	0       Benutze unbestimmte Artikel.
+	
+	1       Benutze bestimmte Artikel.
+	
+	2       Benutze bestimmte Artikel, wenn sich mit dem Objekt
+		kein gleichartiges Objekt im selben Raum befindet,
+		sonst benutze einen unbestimmten.
+
+		Statt der 2 kann auch ein String uebergeben werden.
+		In diesem Fall wird getestet, ob sich ein Objekt mit
+		dem String als id im selben Raum befindet.
+
+	2.)     short()
+
+	Diese Funktion liefert eine Beschreibung, die ausgegeben
+	wird, wenn der Raum, in dem das Objekt sich befindet,
+	betrachtet wird.
+
+	3.)     long()
+
+	Diese Funktion liefert eine Beschreibung, die ausgegeben
+	wird, wenn das Objekt angeschaut wird. Im Unterschied zur
+	2.4.5 Mudlib wird die Beschreibung jedoch nicht ausgegeben,
+	sondern als return-Wert zurueckgegeben.
+
+	4.)     id( str )
+
+	Diese Funktion soll 1 zurueckgeben, wenn str eine Zeichen-
+	folge ist, die das Objekt bezeichnen koennte. Diese Zeichen-
+	folge wird in kleinen Buchstaben uebergeben.
+
+	5.)     _query_invis()
+
+	Wenn ein Objekt unsichtbar ist, soll es beim Aufruf dieser
+	Funktion 1 zurueckgeben. Sichtbare Objekte brauchen diese
+	Funktion nicht zu implementieren.
+
+III.    UNTERSTUETZUNG DER FUNKTIONEN DURCH DIE MG_MUDLIB
+
+	Wenn ein eigenes Objekt aus den Standardobjekten abgeleitet
+	wird, sind alle diese Funktionen bereits fertig definiert.
+	Es muessen beim Erzeugen der Objekte nur noch die entsprechenden
+	Werte mitgeteilt werden. Dieses geschieht mit folgenden Funk-
+	tionen, die am besten im create() aufgerufen werden.
+
+	1.)     SetProp( P_NAME, string );
+
+	In name() wird der uebergebene String dekliniert und automatisch
+	mit Artikeln versehen.  Diese Funktion bedenkt auch Faelle, an 
+	die ihr wahrscheinlich nie gedacht habt. Der Genitiv von der 
+	Lahme ist z.B. des Lahmen! Auch fuer andere Faelle bietet diese 
+	Funktion maechtige Unterstuetzung.
+
+	Wenn der String nicht mit Artikeln versehen werden soll, so kann
+	man das mittels der Funktion "SetProp(P_ARTICLE, 0 )" erreichen.
+
+	Damit die Funktion name() ueberhaupt den richtigen Artikel 
+	auswaehlen kann, muss man mit "SetProp( P_GENDER, gender)" das
+	Gechlecht des Objektes bekannt machen. Als gender kann man 
+	MALE oder 1, FEMALE oder 2 und NEUTER oder 0 verwenden.
+
+	Ist das Objekt unsichtbar, so darf string trotzdem nicht 0 sein;
+	stattdessen ist "SetProp( P_INVIS, 1 )" aufzurufen. (Die Zeile
+	"#include <properties.h>" nicht vergessen. :-))
+
+	Da dieses Verfahren sehr fehleranfaellig ist, ist fuer den Namen
+	ein Array von Namen zugelassen, so dass fuer jeden Fall ein Wort
+	uebergeben werden kann. Beispiel.:
+	SetProp( P_NAME, ({ "Mensch", "Menschen", "Menschen", "Menschen" }) );
+
+	2.)     SetProp( P_SHORT, string )
+	
+	In short() wird der uebergebene String ausgegeben. Mittels des
+	process_string-Mechanismus (siehe /doc/efun/process_string)
+	koennen auch varibale Teile in dem String vorkommen.
+	string braucht fuer unsichtbare Objekte nicht auf 0 gesetzt
+	werden.
+
+	3.)     SetProp( P_LONG, string )
+
+	Dito.
+
+	4.)     AddId( string );
+
+	Nehme den String in die Menge der Zeichenketten auf, auf die die
+	eingebaute id-Funktion mit 1 antworten soll.
+
+	5.)     Es reicht, SetProp( P_INVIS, 1 ) aufzurufen, wenn das
+	Objekt unsichtbar wird, und SetProp( P_INVIS, 0 ), wenn es wieder
+	sichtbar wird.
+
+IV.     BENENNUNG VON RAEUMEN
+
+	Bei Raeumen wird die long() bzw. short()-Beschreibung nur dann
+	benutzt, wenn der Raum *von aussen* angeschaut wird. Fuer eine
+	Beschreibung des Raumen von innen sind folgende Funktionen
+	bereitgestellt:
+
+	1.)     SetProp( P_INT_SHORT, string );
+
+	Gebe eine Beschreibung des Raumes fuer den "kurz"-Modus aus.
+
+	2.)     SetProp( P_INT_LONG, string );
+
+	Gebe eine ausfuehrliche Beschreibung des Raumes zurueck.
+
+V.      BENENNUNG VON MONSTERN
+
+	Siehe oben unter /doc/MG/BANISH.
diff --git a/doc/wiz/called_by_player b/doc/wiz/called_by_player
new file mode 100644
index 0000000..897f546
--- /dev/null
+++ b/doc/wiz/called_by_player
@@ -0,0 +1,8 @@
+void BecomesNetDead(object player) wird im Raum aufgerufen, wenn dort ein
+	Spieler netztot wird. player ist der Spieler.
+
+void PlayerQuit(object player) wird im Raum aufgerufen, wenn darin ein
+	Spieler quittet.
+
+void exit() wird im Raum aufgerufen, wenn ein Lebewesen rausgemoved wird.
+	Das Lebewesen ist previous_object()
diff --git a/doc/wiz/enttanken b/doc/wiz/enttanken
new file mode 100644
index 0000000..9a47d47
--- /dev/null
+++ b/doc/wiz/enttanken
@@ -0,0 +1,99 @@
+Enttanken
+=========
+
+                               Generelles:
+                               **********
+
+ALLE Enttank-Moeglichkeiten MUESSEN ortsabhaenig sein.
+Ausnahmen KANN es fuer Questbelohnungen geben.
+
+Toiletten
+---------
+Toiletten rufen die Methoden "defuel_drink" bzw. "defuel_food" im Spieler auf.
+Es werden keine Parameter uebergeben. Rueckgabewerte sind entweder die Fehler
+NO_DEFUEL, wenn man nichts zu Enttanken hat, DEFUEL_TOO_LOW, wenn man nicht
+genug im Magen/Blase hat, DEFUEL_TOO_SOON, wenn man noch nicht
+wieder enttanken darf, ODER der enttankte Wert.
+Beispiel hierzu siehe "man defuel_food".
+
+Man darf DANN enttanken, wenn man mindestens den Fuellwert P_DEFUEL_LIMIT_FOOD
+bzw. P_DEFUEL_LIMIT_DRINK hat und das letzte Enttanken mindestens
+P_DEFUEL_TIME_FOOD bzw. P_DEFUEL_TIME_DRINK her ist.
+Ist dies der Fall, kann man P_DRINK/FOOD*P_DEFUEL_AMOUNT_DRINK/FOOD enttanken,
+wobei dies zur Haelfte ueber ein Random geglaettet wird.
+
+Wer regulaer ueber "defuel_drink" enttankt, enttankt auch automatisch eine
+gewisse Menge an Alkohol. Diese Menge ist von der enttankten Menge, von dem
+im Koerper sich befindenen Alkohol und vom Gewicht des Spielers abhaengig.
+
+Alle genannten Props sind rassenabhaengig.
+Die Berechnungen sind gekapselt in "defuel_food/drink".
+
+Andere Objekte
+--------------
+
+Hier bietet sich an, die zeitliche Nutzung der Enttanke spielerbezogen 
+mittels der Methode  check_and_update_timed_key zu steuern. Dabei sollte
+der zeitliche Abstand nicht zu knapp sein, in der Regel im Bereich von 
+ca. einer Stunde. Der eigentliche Enttankvorgang im Spieler geschieht 
+mittels eat_food, drink_soft oder drink_alcohol durch Uebergabe negativer 
+Werte. Der Betrag dieses Wertes sollte der Erreichbarkeit angemessen sein,
+d.h. leichter erreichbare Enttanken sollten auch nicht zu viel enttanken,
+wenn sie den Spieler komplett enttanken sollen, muessen sie entsprechend
+schwer zu erreichen sein.
+Da die Enttanken ortsabhaengig sind und in der Regel erst erforscht 
+werden muessen, ist eine weitere Begrenzung momentan nicht vorgesehen. 
+
+                              Spezifisches:
+                              ************
+
+----------------------------
+Rassenbeschreibungen fuer Berechnungen in "defuel_food/drink":
+----------------------------
+
+MENSCHEN
+sind wie immer nichts Besonderes und definieren das absolute Mittelmass.
+
+ZWERGE
+koennen mehr in Blase und Magen haben und koennen auch so richtig abladen.
+Dafuer muessen sie laenger warten, bis es sich lohnt, zu enttanken.
+
+ELFEN
+sind inkontinent und Kleinmengengeber.
+
+DUNKELELFEN
+sind von den Werten her in etwa wie Elfen.
+
+HOBBITS
+koennen essen bis zum Umfallen. Sie laden dann richtig ab, muessen aber auch
+entsprechend warten.
+
+FELINEN
+sind den Menschen aehnlich.
+
+ORKS
+sind auch nichts Besonderes. 
+
+Wer die genauen Werte einsehen moechte, moege in den shells nachgucken.
+
+                                Logisches:
+                                *********
+
+Jede (!) Moeglichkeit zur Enttankung, muss dem zustaendigen Magier
+fuer Heilungs-Balance gemeldet und von diesem genehmigt werden. Wer
+diesen Posten momentan innehat, kann dem MailAlias "heilungs_balance"
+entnommen werden.
+
+Siehe auch:
+----------
+     Tanken:    consume, drink_alcohol, eat_food, drink_soft
+     Heilung:   heal_self, restore_spell_points, restore_hit_points, 
+                buffer_hp, buffer_sp
+     Timing:    check_and_update_timed_key
+     Enttanken: defuel_drink, defuel_food
+     Props:     P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_DRINK
+     Konzepte:  heilung, food
+
+----------------------------------------------------------------------------
+Last modified: 02.11.2005 - Miril
diff --git a/doc/wiz/familien b/doc/wiz/familien
new file mode 100644
index 0000000..39bf831
--- /dev/null
+++ b/doc/wiz/familien
@@ -0,0 +1,24 @@
+Eine Familie umfasst den Erstie und alle Zweities, sprich alle diese haben den
+gleichen "Familiennamen". Dieser Name ist in der Regel die UUID des Ersties
+dieser Familie. Falls aber der Erstie sich aendern sollte (z.B. Magierwerdung
+eines Zweities) und sich der Familienname nicht aendern soll, dann koennen
+wir den Namen dieser Familie beibehalten (d.h. alter Erstie).
+
+Will man wissen, welcher Familie ein Char angehoert (egal, ob Erstie oder
+Zweitie), dann geht das mit:
+# xcall /secure/zweities->QueryFamilie(player_object)
+
+Des Weiteren liefert dieses Objekt auch noch zu jedem Zweitie den Erstie und
+zu jedem Erstie die Liste aller bekannten Zweities:
+# xcall /secure/zweities->QueryErstieName(player_object)
+# xcall /secure/zweities->QueryErstieUUID(player_object)
+# xcall /secure/zweities->QueryZweities(player_object)
+
+Der Datenbestand ist (noch) nicht vollstaendig, daher fehlen da noch viele
+Chars. Die werden aber in absehbarer Zeit dort nachgetragen.
+
+Die Familie wird in Zukunft genutzt, um Dinge zu personalisieren, welche fuer
+den Spieler, aber nicht fuer den Char individuell sein sollen. Sprich:
+personalisiert man irgendwas ueber die Familie, koennen alle Chars dieser
+Familie das irgendwas nutzen.
+
diff --git a/doc/wiz/fehlerteufel b/doc/wiz/fehlerteufel
new file mode 100644
index 0000000..7b0b916
--- /dev/null
+++ b/doc/wiz/fehlerteufel
@@ -0,0 +1,181 @@
+Der Fehlerteufel
+================
+
+die Mudlib speichert die aufgetretenen Fehler und Warnungen. Dabei werden 
+folgende Daten mitgespeichert:
+- Zeit (erstes Mal, zuletzt)
+- In welchem Objekt und welchem Programm der Fehler war
+- Zeilenr.
+- Fehlermeldung
+- Objekt, dessen Heartbeat, den Fehler ausloeste
+- this_interactive() bzw. this_player()
+- den Befehl, den TI eingegeben hat
+- das Environment von TI oder TP
+- wie oft der Fehler schon auftrat
+- der Backtrace
+- ob der Fehler in einem catch() auftrat
+
+Zusaetzlich werden von Spielern per "bug", "fehler" oder "sonnenfehler"
+gemeldete Fehler gespeichert.
+
+Der Fehlerteufel dient nun dazu, sich diese Infos darstellen zu lassen. Nach 
+dem Clonen von /obj/tools/fehlerteufel habt ihr folgende Moeglichkeiten:
+
+- fehlerliste <auswahl s. Kommando fehlermodus>
+  Dieser Befehl stellt euch eine Liste aller bekannten Fehler dar, die unter
+  einer UID auftraten, fuer die ihr zustaendig seid (z.B. "zesstra", 
+  "d.inseln.zesstra", "GUILD.magierpack", s. auch QueryUIDsForWizard()). Die
+  Liste umfasst die eindeutige Fehler-ID, den Ladenamen des fehlerausloesenden
+  Objekts und die UID.
+  Je nach Argument werden div. Fehler oder Warnungen ausgegeben. Wird keins 
+  von beiden angegeben, wird je nach Einstellung von 'fehlermodus' aufgelistet.
+
+- fehlerabfrage <id>
+  Hiermit lassen sich die Detail-Informationen zu <id> anzeigen. <id> bekommt
+  ihr entweder aus "fehlerliste" oder von -debug/-entwicklung/-warnungen, dort
+  werden diese IDs mit ausgegeben. Ohne Angabe der <id> wird der letzte
+  betrachtete Fehler ausgegeben.
+  Neben der numerischen ID ist hierbei auch die Hash-ID moeglich.
+  Es kann auch der Ladename eines Objekts angegeben werden, dann werden alle
+  zu diesem Objekt gehoerenden Eintraege auf einmal angezeigt, z.B.
+    fehlerabfrage /d/unterwelt/raeume/wald3
+  Der Ladename muss mit einem / beginnen.
+
+- fehlerloeschen <id>
+  Fehler mit der ID <id> zum Loeschen markieren (z.B. wenn gefixt). Die 
+  eigentlich Loeschung findet innerhalb von 24h statt. Die Fehler werden nach 
+  diesem Befehl nicht mehr in der Fehlerliste angezeigt. Ohne Angabe der <id>
+  wird der letzte benutzte Fehler geloescht.
+  Wiederholt man den Befehl, wird die Loeschmarkierung wieder entfernt.
+  Es kann auch der Ladename eines Objekts angegeben werden, dann werden alle
+  zu diesem Objekt gehoerenden Eintraege auf einmal geloescht, z.B.
+    fehlerloeschen d/unterwelt/raeume/wald3
+
+- fehlereingabe <objekt>
+  Hiermit laesst sich per Hand ein Eintrag fuer das angegebene Objekt im
+  Fehlerteufel erstellen. Das Objekt kann mit einer seiner IDs oder seinem
+  vollstaendigen Objektnamen angegeben werden. Wird kein Objekt angegeben,
+  wird die Umgebung des Magiers als Objekt genutzt. Bsp:
+    fehlereingabe seil
+    fehlereingaeb /obj/seil#5635
+
+- fehlerrefresh
+  Der Fehlerteufel hat einen Zwischenspeicher fuer die Fehlerliste. Dieser 
+  Befehl loescht diesen Zwischenspeicher und holt die Fehlerliste neu.
+
+- fehlerfilter
+  Hiermit laesst sich ein Filter fuer UIDs einstellen. (s.u.) Ist er aktiv, 
+  werden per 'fehlerliste' keine Fehler mehr angezeigt, die im Filter
+  eingetragen wurden.
+
+- fehlermodus <fehler|warnungen>
+  Einstellen, welche Arten von Eintraegen im Fehlerteufel vom Kommando
+  'fehlerliste' ausgegeben werden soll:
+    laufzeitfehler: Laufzeitfehler werden ausgegeben (-debug, -entwicklung)
+    laufzeitwarnungen: Laufzeitwarnungen werden ausgegeben (-warnungen)
+    ladezeitfehler: Fehler beim Laden eines Objekts werden ausgegeben  
+    ladezeitwarnungen: Warnungen beim Laden eines Objekts werden ausgegeben
+    fehlerhinweise: Fehler, die Spieler/Magier abgesetzt haben
+    ideen: Ideen, die Spieler/Magier abgesetzt haben
+    typos: Typos, die Spieler/Magier abgesetzt haben
+    md: Fehlende Details, die Spieler/Magier abgesetzt haben
+    fehler: alle moeglichen Fehler werden ausgegeben
+    warnungen: alle moeglichen Warnungen werden ausgegeben
+    alle: alle Fehler + Warnungen werden ausgegeben
+    
+    Ohne Argument: aktuellen Modus ausgeben.
+
+- fehlermonitor
+  Hiermit lassen sich UIDs angeben, die man zusaetzlich zu den eigenen
+  auch noch beobachten will.
+  Hierbei sind auch einige UID-Aliase moeglich, z.B. 'magier', 'region', 
+  'p.service', 'p' oder 'gilde'.
+  BTW: nach Aenderung dieser Liste sollte man "fehlerrefresh" aufrufen.
+
+- flock <id> <bemerkung>
+  So markierte Fehler werden nicht mehr automatisch (nach 31 Tagen ohne
+  Aenderung) geloescht. ABER: Solcherart gesperrte Fehler werden momentan
+  _niemals_ automatisch geloescht, deshalb denkt bitte daran, sie entweder
+  selber zu loeschen oder sie wieder freizugeben, wenn ihr sie nicht mehr
+  braucht.
+
+- funlock <id> <bemerkung>
+  Gibt den Fehler wieder zum automatischen Loeschen frei.
+
+- ffix <id> <bemerkung zum fix>
+  Fehler wird als "gefixt" markiert und eine Mail an alle fuer das buggende
+  Objekte zustaendigen Magier geschickt (soweit bekannt). Anschliessend 
+  werden die Fehler vom Fehlerteufel nicht mehr angezeigt.
+  Im Falle von durch Spieler berichteten Fehlern wird dem jeweiligen Spieler
+  beim Fixen ebenfalls eine Mail geschickt.
+  Als gefixt markierte Fehler werden nach einiger Zeit geloescht.
+  Es empfiehlt sich fuer eigene Fehler, diese nach Beheben einfach zu
+  Loeschen, wenn man keine Mail ueber den Fehler erhalten moechte.
+
+- funfix <id> <bemerkung warum der Bugfix scheisse war ;)>
+  Wenn ihr bis zu der "Archivierung" gefixter Fehler feststellt, dass ihr den 
+  Fehler wohl doch nicht gefixt habt, koennt ihr das wieder rueckgaengig 
+  machen.
+
+- fnotiz <id> <bemerkung>
+  Wenn euch an so einem Fehler was auffaellt oder ihr eine Idee habt, koennt ihr
+  das so also "verewigen". Notizen kann jeder Magier an Fehler dranhaengen,
+  dafuer braucht man keine besonderen (Schreib)Rechte. Die Notizen werden vom
+  Fehlerteufel ausgegeben und stehen auch im CHANGELOG.
+  Anmerkung: Es gibt (absichtlich) keine Moeglichkeit, Notizen wieder zu
+  loeschen.
+
+- fuebertrage <id> <newuid> <bemerkung>
+  Wenn ihr einen Fehler habt, fuer den ihr nicht zustaendig seid, weil der
+  Ursprung des Fehlers nicht in einem eurer Files liegt, koennt ihr diesen
+  Fehler an eine andere UID mit einer Bemerkung uebertragen. Danach ist der
+  Magier von <newuid> zustaendig und hat Schreibrechte auf den Fehler.
+  <newuid> kann z.B. sowas wie 'd.inseln.zesstra' oder 'zesstra' sein. Liegt
+  der Fehler in einem Objekt in /d/, solltet ihr auf jeden Fall
+  d.region.magier benutzen, damit ggf. der RM der Region auch zustaendig wird.
+
+UID-Filter:
+  a) fehlerfilter an
+     Schaltet den Filter ein
+  b) fehlerfilter aus
+     schaltet den Filter aus
+  c) fehlerfilter alle
+     schaltet den Filter ein _und_ schreibt alle UIDs, fuer die man zustaendig
+     ist, in den Filter, es wird dann nichts mehr angezeigt.
+  d) fehlerfilter keine
+     schaltet den Filter ein _und_ loescht den Filter, d.h. es wird trotzdem
+     erstmal noch nix gefiltert..
+  e) fehlerfilter +zesstra +d.inseln.zesstra -d.ebene.zesstra
+     Fuegt "zesstra" und "d.inseln.zesstra" den aktuellen Filtereinstellungen
+     hinzu und loescht "d.ebene.zesstra" aus dem Filter.
+     Beliebige Kombinationen moeglich. Ohne - oder + am Anfang wird jeweils
+     invertiert, also hinzugefuegt, wenn noch nicht drin und entfernt, wenn
+     schon in der Liste.
+
+Fehler-Monitor:
+  a) fehlermonitor keine
+     Loescht alle beobachteten UIDs
+  b) fehlermonitor +atamur -d.polar.atamur
+     Fuegt "atamur" der Beobachtungsliste hinzu und entfernt "d.polar.atamur".
+     Beliebige Kombinationen moeglich. Ohne - oder + am Anfang wird jeweils
+     invertiert, also hinzugefuegt, wenn noch nicht drin und entfernt, wenn
+     schon in der Liste.
+
+Zugriffsrechte:
+  - Lesen und Anhaengen (Notizen, Loeschsperren) darf jeder Magier
+  - Loeschen, Fixen und UID neu zuordnen duerfen fuer die UID zustaendige Magier
+  - Fixes zurueckziehen darf jeder Magier (solange issue nicht expired)
+  - EM+ duerfen alles
+
+Randbemerkung:
+  Moechte man nicht, dass vom Magier selbst ausgeloeste Fehler in /players/
+  protokolliert werden, kann der Magier _in sich_ die Prop P_DONT_LOG_ERRORS
+  (aus /secure/errord.h) auf 1 setzen.
+  ACHTUNG: das bedeutet, dass ueber Fehler keine Informationen mehr
+  vorliegen, die beim Fixen helfen koennen. Bedenkt das bitte, wenn ihr die
+  Prop setzt.
+ 
+
+SIEHE AUCH:
+ QueryUIDsForWizard(), QueryWizardForUID()
+
diff --git a/doc/wiz/fernkampfwaffen b/doc/wiz/fernkampfwaffen
new file mode 100644
index 0000000..f8dd285
--- /dev/null
+++ b/doc/wiz/fernkampfwaffen
@@ -0,0 +1,128 @@
+Zusammenfassung zu Fernkampfwaffen
+==================================
+
+  Schusswaffe:
+  ============
+    Standardobjekt: /std/ranged_weapon.c
+
+    P_WC:           gibt den Schaden im Nahkampf an (wenn man damit auf einen
+                    Gegner einpruegelt. Default 30.
+    P_QUALITY:      nur beim Nahkampf relevant. Default 100.
+    P_NR_HANDS:     Boegen und Armbrueste sind in jedem Fall zweihaendig.
+    P_SHOOTING_WC:  Die Basis-Waffenklasse der Fernwaffe.
+    P_RANGE:        Reichweite der Waffe. Default 50.
+    P_STRETCH_TIME: Anzahl der Runden, die zum Laden/Spannen der Waffe
+                    benoetigt werden. Default 1.
+    P_AMMUNITION:   Benoetigter Munitionstyp.
+
+    Eine HitFunc wirkt sich auf die P_WC aus, nicht auf P_SHOOTING_WC!
+
+    Methoden:
+    static int cmd_shoot(string str);
+    * Befehlsauswertung und Schussvorbereitung, wird durch
+      AddCmd( ({"schiess", "schiesse"}), "cmd_shoot");
+      verknuepft
+
+    static int shoot_dam(mapping shoot);
+    * Schadensermittlung
+
+    static string FindRangedTarget(string str, mapping shoot);
+    * Gegnerermittlung
+
+  Munition:
+  =========
+    Standardobjekt: Nutzung von /std/unit ist empfohlen
+
+    P_SHOOTING_WC: legt die Munitionsstaerke fest
+    P_DAM_TYPE:    legt die Schadensart des Schussangriffs fest
+
+    Eine HitFunc in der Munition wirkt sich auf P_SHOOTING_WC aus!
+
+  Allgemeines:
+  ============
+    In der Waffe legt man ueber P_AMMUNITION fest, welche Art Munition
+    damit verschossen werden kann. In der Munition muss diese Munition
+    auch als ID gesetzt werden.
+
+    Ueber P_STRETCH_TIME legt man fest, alle wieviel Runden die
+    Fernkampfwaffe abgefeuert werden kann (default: 1).
+
+    Der verursachte Schaden wird aus der Staerke der Fernkampfwaffe
+    und der Staerke der Munition bestimmt. In beiden wird diese in der
+    Property P_SHOOTING_WC angegeben.
+
+    Zusaetzlich kann man in P_RANGE die Reichweite der Fernkampfwaffe
+    festlegen. Hat man einen Raum A, der sich innerhalb eines anderen
+    Raumes befindet (z.B. Transporter), so kann man mittels P_SHOOTING_AREA
+    dessen 'Groesse' festlegen. Mit einer Waffe, deren P_RANGE mindestens
+    gleich diesem Zahlenwert ist, kann aus diesem Raum heraus geschossen
+    werden.
+    Alternativ kann man in A einen anderen Raum B)in die Property
+    P_TARGET_AREA schreiben.
+
+BEISPIEL:
+    // #1 siehe auch /doc/beispiele/fernwaffen fuer ladbaren Code
+
+    // #2 eine einfache, aber gute Schleuder
+    inherit "/std/ranged_weapon";
+
+    void create() {
+      if (!clonep(this_object())) return;
+      ::create();
+
+      SetProp(P_SHORT, "Eine Schleuder");
+      SetProp(P_INFO,
+        "Die Syntax lautet: schleuder <geschoss> auf <ziel>\n");
+      // sonstige Objektprops ...
+
+      RemoveCmd(({"schiess", "schiesse"})); // entferne Default-Kommando
+      AddCmd(({"schleuder", "schleudere"}), #'cmd_shoot);
+
+      SetProp(P_WC, 10);          // eine Schleuder ist erstmal harmlos
+      SetProp(P_DAM_TYPE, DT_WHIP);
+
+      SetProp(P_SHOOTING_WC, 90); // ziemlich gute Schleuder
+      SetProp(P_NR_HANDS, 1);     // einhaendig
+      SetProp(P_WEAPON_TYPE, WT_RANGED_WEAPON);
+      SetProp(P_AMMUNITION, MUN_STONE);
+      SetProp(P_STRETCH_TIME, 1);
+      SetProp(P_RANGE, 30);       // 30 weit kann man damit nur schleudern
+    }
+
+    // #3 passende Munition
+    inherit "/std/unit";
+
+    void create() {
+      if (!clonep(this_object())) return;
+      ::create();
+
+      SetProp(P_NAME, ({"Schleuderstein", "Schleudersteine"}) );
+      SetProp(P_LONG, break_string(
+        "Hervorragend geformte Schleudersteine aus Murmelnit.", 78));
+      SetProp(P_GENDER, MALE);
+      SetProp(P_AMOUNT, 1);
+      SetProp(P_SHOOTING_WC, 50);
+      SetProp(P_DAM_TYPE, ({DT_BLUDGEON}));
+      SetProp(P_WEAPON_TYPE, WT_AMMU);
+      SetProp(P_MATERIAL, MAT_STONE);
+
+      SetGramsPerUnits(60,1);
+      SetCoinsPerUnits(25,1);
+
+      AddId(MUN_STONE);
+      AddSingularId(({"stein", "schleuderstein"}));
+      AddPluralId(({"steine", "schleudersteine"}));
+      AddClass(CL_AMMUNITION);
+    }
+
+SIEHE AUCH:
+    Generell:  P_SHOOTING_WC, P_STRETCH_TIME, P_AMMUNITION
+    Methoden:  FindRangedTarget(L), shoot_dam(L)
+    Gebiet:    P_RANGE, P_SHOOTING_AREA, P_TARGET_AREA
+    Waffen:    P_WEAPON_TYPE, P_WC, P_EQUIP_TIME, P_NR_HANDS
+    Kampf:     Attack(L), Defend(L), P_DISABLE_ATTACK, P_ATTACK_BUSY
+    Team:      PresentPosition(L)
+    Sonstiges: P_NOGET
+    Balance:   fernwaffen, waffen, balance
+
+29.Jul 2014 Gloinson
\ No newline at end of file
diff --git a/doc/wiz/fernwaffen b/doc/wiz/fernwaffen
new file mode 100644
index 0000000..947c517
--- /dev/null
+++ b/doc/wiz/fernwaffen
@@ -0,0 +1,118 @@
+
+ FERNWAFFEN
+
+  a. Allgemeines
+     Fernwaffen sind eine relativ neue Ergaenzung zum Kampfsystem dieses Muds, 
+     die mit der Einfuehrung des Teamkampfes erst moeglich wurde.
+     Schon bisher gab es einzelne Fernwaffen (z.B. Robins Bogen), die aber
+     Inselloesungen und Sonderfaelle waren. Das neue Standardobjekt fuer 
+     Fernwaffen ist nun /std/ranged_weapon.c, das die noetige Funktionalitaet 
+     bietet und leicht ueber Properties zu konfigurieren ist. Mit Fernwaffen 
+     sind uebrigens ausnahmslos Waffen gemeint, die Munition benoetigen, 
+     nicht etwa Speere oder dergleichen.
+
+  ** Bisher sind alle Fernwaffen ausnahmslos genehmigungspflichtig, das  **
+  ** gilt auch fuer jegliche Munition.                                   **
+     
+  b. Properties
+     P_WC
+        Vorsicht mit dieser Property. Bei Fernwaffen gibt sie _nur_ den
+        Schaden bei Zweckentfremdung der Waffe als Knueppel an. 
+        Dementsprechend ist dieser Wert extrem niedrig zu halten.
+        Standardwert ist hier 30, der auch nur in Ausnahmefaellen 
+        ueberschritten werden sollte. Kaum eine Armbrust oder ein Bogen 
+        taugt nunmal als grossartige Nahkampfwaffe.
+
+     P_QUALITY
+        Wird nur beim Nahkampf mit der Waffe beachtet. Standardmaessig auf
+        100 gesetzt, da Boegen und Armbrueste leicht beschaedigt werden,
+        wenn man damit auf jemanden einpruegelt.
+
+     P_HIT_FUNC 
+        HitFunc fuer den _Nahkampf_. Hat beim Schuss mit der Waffe keinen
+        Einfluss. 
+                
+     P_NR_HANDS
+        Boegen und Armbrueste sind in jedem Fall zweihaendig. Einhaendige
+        Fernwaffen sind aber denkbar (Schlingen zum Schleudern kleiner 
+        Steine z.B.)
+        
+     P_SHOOTING_WC
+        Die Basis-Waffenklasse der Fernwaffe. Zu ihr wird die Waffenklasse
+        der Munition addiert, um den endgueltigen Angriffswert beim Schuss
+        zu berechnen.
+     
+     P_RANGE 
+        Reichweite der Waffe in Metern. Wichtig, wenn aus Containern 
+        (Wachtuermen, Schiffen, etc.) nach aussen geschossen wird.
+        Damit das funktioniert, muss dieser Wert hoeher sein als der
+        im Container definierte (steht dort in der P_SHOOTING_AREA).
+    
+     P_STRETCH_TIME
+        Anzahl der Runden, die zum Laden/Spannen der Waffe benoetigt
+        werden. 1 ist hier der Standardwert, das bedeutet, es kann jede
+        Runde geschossen werden.
+     
+     P_AMMUNITION
+        Benoetigter Munitionstyp. Hier ist eine der moeglichen Konstanten
+        (MUN_*) einzusetzen (z.B. MUN_ARROW fuer Boegen).
+     
+     P_NOGET
+        Hat bei Fernwaffen eine zusaetzliche Bedeutung. Wenn gesetzt, muss
+        die Waffen nicht gezueckt werden, um sie abfeuern zu koennen. Das
+        ist z.B. fuer Katapulte gedacht, die im Raum stehen.
+        
+     Fuer die Munition gibt es kein Standardobjekt. Wichtig ist nur, dass 
+     die entsprechenden Properties gesetzt sind. Normalerweise sollte die 
+     Munition natuerlich eine Unit sein, aber auch Einzelobjekte (ein
+     besonderer Pfeil oder ein grosser Stein fuer ein Katapult) sind 
+     moeglich.
+
+     Properties fuer die Munition sind:
+     
+     P_SHOOTING_WC
+        Die Waffenklasse der Munition. Wird zur WC der Waffe addiert.
+        
+     P_DAM_TYPE
+        Schadenstyp der Munition. Sollte normalerweise DT_PIERCE fuer 
+        Pfeile aller Art und DT_BLUDGEON fuer stumpfe Munition wie Steine
+        sein. Magische Schadensarten sind aber natuerlich moeglich.
+        
+     P_HIT_FUNC
+        HitFunc, die beim Schuss mit der Munition benutzt wird.
+        
+     Ausserdem muss in der Munition mittels AddId() der entsprechende
+     Munitionstyp gesetzt werden, z.B. MUN_ARROW.
+
+  c. Spezialwaffen/Fernwaffen mit Sonderfunktionen
+  
+     Siehe Hinweise zu entsprechenden Nahkampfwaffen.
+     
+  d. Genehmigungsgrenzen
+     Alle Waffen dieser Art sind grundsaetzlich genehmigungspflichtig.
+     Folgende Werte sollten allerdings Obergrenzen darstellen, die
+     im Normalfall nicht zu Ueberschreiten sind:
+     
+     Waffe kann jede Runde abgefeuert werden:  P_SHOOTING_WC 100
+     Waffe braucht eine Ladezeit            :  P_SHOOTING_WC 130
+     
+     Die Obergrenze fuer Munition liegt bei :  P_SHOOTING_WC 60.
+
+  e. Nachbemerkung
+     Seid bitte vorsichtig mit P_SHOOTING_AREA in Raeumen/Containern. 
+     Bisher ist dieses Schiessen von Raum zu Raum weitestgehend ungetestet,
+     und es ist nicht klar, welche Probleme das verursachen kann. Wenn 
+     eventuelle Ziele keine Moeglichkeit haben, sich zu wehren oder 
+     wegzulaufen, ist schnell jegliche Balance dahin. Die Regionsmagier
+     haben bei Abnahme von Gebieten darauf zu achten, dass diese Property
+     nur in wenigen, gut begruendeten Raeumen gesetzt wird.
+  
+ SIEHE AUCH
+
+     balance, ruestungen, waffen, uniques, npcs, grenzwerte,
+     attributsveraenderungen, resistenzen, kampfobjekte,
+     fernkampfwaffen
+
+------------------------------------------------------------------------------
+ LETZTE AeNDERUNG:
+    Don, 10.08.2000, 22:30:00 von Paracelsus
diff --git a/doc/wiz/food b/doc/wiz/food
new file mode 100644
index 0000000..698e3f6
--- /dev/null
+++ b/doc/wiz/food
@@ -0,0 +1,223 @@
+FOOD, DRINK & ALCOHOL
+=====================
+
+    Es wird empfohlen, jede tragbare Heilung ueber "/std/food"
+    zu implementieren. Hier braucht man nur wenige Properties setzen, um
+    sicherzugehen, dass die Heilung korrekt verlaeuft.
+
+    Bitte bei der Realisierung von tragbaren Tanken IMMER mit der Balance
+    sprechen. Die Heilung unterliegt genauer Kontrolle. Ewig haltbare Speisen
+    sind sowieso genehmigungspflichtig und nur mit guter Begruendung und
+    strengen Auflagen erlaubt.
+
+FEATURES:
+    Unterstuetzt wird das Konsumieren von Speisen per Essen oder Trinken auch
+    in mehreren Portionen. Die Speisen verderben und werden vernichtet.
+    Es sind Lebensmittel mit Behaelter moeglich, so dass dieser leer
+    zurueckbleibt, wenn der Inhalt gegessen oder vernichtet wurde.
+    Die Wirkung von Speisen kann neben der Zufuehrung von Lebens- und Konzen-
+    trationspunkten erweitert werden. Die Wirkung verdorbener Speisen kann 
+    getrennt definiert werden.
+    Wert und Gewicht der Speise werden in Abhaengigkeit der Restmengen immer
+    korrekt berechnet.
+
+REALISIERUNG:
+    Die Realisierung der Timer laeuft ueber die Resets der Speise. Callouts
+    werden lediglich verwendet, um den Aufruf des Resets zu garantieren.
+    Es wird auch geprueft, dass der Aufruf der Resets per Hand nichts
+    durcheinander bringt.
+
+    Des Weiteren ist sichergestellt, dass der Spieler nicht mit Futter in
+    Beruehrung kommt, dessen Timer zum Verderben initialisiert ist.
+
+    Das Konzept ist dem Heilungskonzept der Kneipen angepasst worden.
+    Dem entsprechend sind die Properties sehr aehnlich.
+
+PROPERTIES:
+
+      P_PORTIONS : Anzahl der Portionen (wie oft kann man abbeissen / 
+                   einen Schluck nehmen)
+
+          P_FOOD : Fuellgrad der Speise pro Portion, muss gesetzt sein, wenn
+                   P_DRINK nicht gesetzt ist
+         P_DRINK : Fuellgrad der Fluessigkeit pro Portion, muss gesetzt sein,
+                   wenn P_FOOD nicht gesetzt ist
+       P_ALCOHOL : Alkohollevel pro Portion 
+
+        P_WEIGHT : Gewicht pro Portion (bei QueryProp(P_WEIGHT) wird das
+                   komplette Gewicht aller Portionen + eventuell Behaelter 
+                   zurueckgegeben)
+         P_VALUE : Wert pro Portion  (bei QueryProp(P_VALUE) wird der
+                   komplette Wert aller Portionen + eventuell Behaelter 
+                   zurueckgegeben)
+
+            P_HP : Anzahl der LP, die prop Portion geheilt/geschwaecht werden
+            P_SP : Anzahl der KP, die prop Portion geheilt/geschwaecht werden
+  P_DISTRIBUTION : Verteilung der Heilung auf die Heartbeats
+                   (siehe Hilfe zu H_DISTRIBUTION in consume)
+
+      P_LIFETIME : Zeit in Sekunden, bis die Speise verdirbt (>0)
+                   Zaehlt ab der ersten Inbesitznahme durch einen Spieler
+P_RESET_LIFETIME : Zeit in Resets, bis die Speise verdirbt (>0)
+                   Die Laenge der einzelnen Resets wird wie ueblich berechnet
+                   und P_LIFETIME entsprechend auf durchschnittlich
+                   P_RESET_LIFETIME * 45 * 60 gesetzt.
+                   (existiert, da bisher meistens in Resets gerechnet wurde)
+                   Sollte sinnvollerweise nur als SetProp(P_RESET_LIFETIME)
+                   verwendet werden, kann aber auch abgefragt werden
+
+   P_EMPTY_PROPS : Mapping mit Werten des Behaelters
+                   Alle hier angegebenen Werte (ausser P_PORTIONS) werden
+                   in der Speise gesetzt, wenn die letzte Portion konsumiert
+                   wurde. Wenn diese Prop leer ist, wird das Objekt zerstoert,
+                   wenn keine Portionen mehr da sind.
+                   Achtung: es werden keine closures unterstuetzt! 
+           P_IDS :   Wert in P_EMPTY_PROPS
+                     Liste der IDs des leeren Behaelters
+                     Achtung: es werden keine closures unterstuetzt!
+    P_ADJECTIVES :   Wert in P_EMPTY_PROPS
+                     Liste der Adjektive des leeren Behaelters
+                     Achtung: es werden keine closures unterstuetzt!
+         P_VALUE :   Wert in P_EMPTY_PROPS
+                     Wert des leeren Behaelters
+        P_WEIGHT :   Wert in P_EMPTY_PROPS
+                     Gewicht des leeren Behaelters
+
+        P_NO_BAD : Flag, ob die Speise verderben kann
+                   Darf nur in Absprache mit der Balance auf 1 gesetzt werden!
+
+   P_DESTROY_BAD : Wert, was beim Verderben mit dem Object passiert
+                   DESTROY_BAD   = Die Speise wird beim Verderben zerstoert
+                                   bzw. der Behaelter wird geleert (default)
+                   DESTROY_NEVER = Die Speise wird beim Verderben nicht
+                                   zerstoert
+                   pos. integer  = Anzahl der Sekunden, die zwischen Verderben
+                                   und Zerstoeren der Speise liegen sollen
+
+MESSAGES: (durchlaufen replace_personal() und koennen somit Platzhalter
+          fuer Speise (1. Argument) und Konsument (2. Argument) enthalten)
+
+   P_CONSUME_MSG : fuer den Raum, wenn konsumiert wird
+     P_EATER_MSG : an den Konsumenten
+     P_EMPTY_MSG : wenn leere Behaelter konsumiert werden
+    P_NOFOOD_MSG : wenn Getraenke gegessen werden
+   P_NODRINK_MSG : wenn Futter getrunken wird
+       P_BAD_MSG : wenn Speisen verderben
+    P_REMOVE_MSG : wenn Speisen vernichtet werden
+                   (nur wenn sie nicht beim Verderben vernichtet werden)
+       P_ENV_MSG : wenn im Raum liegende Speisen konsumiert werden
+                   (ist diese Prop leer, geht das sogar!)
+ P_FOOD_FULL_MSG : wenn man nix mehr essen kann
+P_DRINK_FULL_MSG : wenn man nix mehr trinken kann
+  P_ALC_FULL_MSG : wenn man keinen Alkohol mehr vertraegt
+
+METHODEN:
+             consume : wird beim Konsumieren aufgerufen, wenn klar ist,
+                       dass was zum Konsumieren da ist.
+                       gibt das Ergebnis von try_consume() zurueck
+         try_consume : bereitet die Daten auf und fuehrt living->consume() aus
+                       kann zur Pruefung mit testonly-Flag aufgerufen werden
+                       gibt das Ergebnis von living->consume() zurueck
+     success_consume : wird von consume() aufgerufen, wenn Konsumieren klappt
+                       gibt die Meldungen zum Konsumieren aus
+      failed_consume : wird aufgerufen, wenn Konsumieren nicht klappt
+                       gibt den Grund fuer den Fehlschlag aus
+                       (je nach Ergebnis von living->consume())
+                       
+         consume_bad : Aendert die Daten fuer living->consume(), wenn eine
+                       verdorbene Speise konsumiert wird
+                       Standard: Heilung wirkt nicht, Vergiftung +1
+            make_bad : wird aufgerufen, wenn die Speise gerade verdirbt
+                       Gibt die Meldungen beim Verderben aus
+                       Vernichtet das Objekt, wenn das gewollt ist 
+                       Die Methode wird auch aufgerufen, wenn nur noch der
+                       Behaelter existiert! Also den Test is_not_empty() nicht
+                       vergessen!
+        make_destroy : wird aufgerufen, wenn die Speise bereits verdorben ist
+                       und die Zeit aus P_DESTROY_BAD abgelaufen ist.
+                       Gibt die Meldung zum Zerstoeren aus und ruft
+                       make_empty aus, um die Speise bzw. den Inhalt des
+                       Behaelters zu zerstoeren. Ausserdem wird der Reset
+                       abgeschaltet.
+                       Die Methode wird auch aufgerufen, wenn nur noch der
+                       Behaelter existiert! Also den Test is_not_empty() nicht
+                       vergessen!
+                       
+          make_empty : wird aufgerufen, wenn die letzte Portion konsumiert wird
+                       Macht aus einem vollen Behaelter einen leeren oder
+                       vernichtet das Objekt. Der Behaelter verdirbt also im
+                       Normalfall nicht!
+
+                init : Startet den Timer zum Verderben der Speise
+                       (start_lifetime()), wenn sich das Objekt in einem
+                       Spieler befindet.
+                       Prueft, ob Laufzeiten fehlerhafterweise nicht beendet
+                       wurden und holt es dann nach
+          NotifyMove : Startet den Timer zum Verderben der Speise
+                       (start_lifetime()) auch, wenn die Speise in einen
+                       Spieler bewegt wird, ohne dass init() aufgerufen
+                       wurde.
+               reset : wird fuer 3 Situationen verwendet:
+                       1: Das Futter wurde bewegt, der Timer aber noch nicht 
+                          initialisiert -> es wird geprueft, ob der Timer
+                          gestartet werden muss.
+                       2: Der Timer ist abgelaufen ->
+                          laesst die Speise verderben und vernichtet sie
+                          gegebenenfalls
+                       3: Der Timer nach dem Verderben ist abgelaufen ->
+                          vernichtet die Speise
+                       Es wird immer geprueft, ob die Resets zeitgerecht
+		       aufgerufen wurden und die Aktionen nicht zur falschen
+		       Zeit passieren
+
+        is_not_empty : Flag, ob noch Portionen vorhanden sind
+        is_drinkable : Flag, ob Speise trinkbar ist
+                       (nur wenn P_DRINK gesetzt und P_FOOD NICHT gesetzt ist)
+          is_eatable : Flag ob Speise essbar ist
+                       (wenn P_FOOD gesetzt (P_DRINK ist egal))
+              is_bad : Flag, ob Speise verdorben ist
+
+get_current_lifetime : Zeit in Sekunden, wie lange der Timer zum Verderben
+                       laeuft (ist immer 0, wenn P_NO_BAD gesetzt ist)
+      start_lifetime : startet den Timer zum Verderben der Speise falls keine
+                       Gruende dagegen sprechen, wird intern aufgerufen, kann
+                       aber in speziellen Situationen auch manuell aufgerufen
+                       werden (siehe oben unter REALISIERUNG)
+             message : es werden Meldungen zu Statusaenderungen verarbeitet
+                       und korrekt ausgegeben. Es wird der Name der auszu-
+                       gebenen Property uebergeben.
+                       Es werden alle notwendigen Ersetzungen per
+                       replace_personal gemacht und geprueft, ob dem Besitzer
+                       oder dem Raum die Meldung ausgegeben werden muss.
+                       Hierueber sollten nur Meldungen ausgegeben werden, die
+                       durch Aenderungen an der Speise im Reset ausgeloest
+                       werden, also im reset selbst und in den make_*-Methoden.
+
+
+STANDARD:
+    Wenn man nichts in seinem Futter ueberschreibt, dann hat es
+    folgendes Standardverhalten.
+    Es muss immer P_FOOD oder P_DRINK explizit groesser 0 gesetzt werden,
+    sonst laesst sich das Lebensmittel nicht konsumieren!
+
+    - Haltbarkeit: 1 Reset (30 + random(30) Minuten)
+    - der Ablauf der Haltbarkeit startet, sobald ein Spieler die Speise nimmt
+    - Wenn die Speise schlecht wird, wird sie zerstoert
+    - Die Speise hat keinen Behaelter
+    - Es gibt nur einen Schluck/Bissen, dann ist die Speise weg
+    - Gewicht: 50 Gramm / Wert: 10 Muenzen
+    - Man heilt weder LP noch KP
+    - Man wuerde ansonsten 5 LP/KP pro Heartbeat heilen
+    - Man kann die Speise nur konsumieren, wenn man sie in der Hand haelt
+    - Wenn man die verdorbene Speise konsumieren koennte, wuerde man nicht
+      heilen sondern vergiftet werden (P_POISON+1)
+
+BEISPIELE:
+    Beispiele zu tragbaren Speisen und Getraenken kann man unter
+    doc/beispiele/food/ nachschauen
+
+SIEHE AUCH:
+    consume, wiz/heilung, 
+
+LETZTE AeNDERUNG:
+    25.09.2010, Caldra
diff --git a/doc/wiz/forscherpunkte b/doc/wiz/forscherpunkte
new file mode 100644
index 0000000..a7f84d1
--- /dev/null
+++ b/doc/wiz/forscherpunkte
@@ -0,0 +1,65 @@
+
+Forscherpunkte
+==============
+
+ FORSCHERPUNKTE:
+    Damit die Spieler beim Erkunden neuer Gebiete auch an Ortskenntnis
+    gewinnen, muessen an bestimmten Stellen Forscherpunkte (FP) vergeben
+    werden. Dazu ist in den meisten Faellen keine Aenderung an den Objekten
+    selbst noetig, die Punkte muessen jedoch von einem Erzmagier in die
+    FP-Liste eingetragen werden.
+
+    DIE FORSCHERPUNKTE MUESSEN EINGETRAGEN WERDEN, BEVOR DAS GEBIET DEN
+    SPIELERN ZUGAENGLICH GEMACHT WIRD!
+
+    Die Testphase ist eine gute Gelegenheit, sich darueber Gedanken zu machen,
+    wo welche FP vergeben werden koennten.
+
+    Fuer das Eintragen der Forscherpunkte ist derzeit Miril zustaendig.
+
+ WAS FUeR FP KANN ICH VERGEBEN?
+    Folgende Typen von Forscherpunkten stehen zur Verfuegung:
+
+     * Details. Darunter fallen auch SpecialDetails.
+     * ReadDetails.
+     * Sounds und Smells.
+     * Kommandos, soweit sie mit AddCmd() angemeldet werden.
+       Kommandos, die mit add_action() angemeldet werden, koennen aus
+       technischen Gruenden nicht beruecksichtigt werden.
+       Kommandos, die als FPs eingetragen sind, geben genau dann den
+       FP, wenn 1 zurueckgegeben wird.
+     * Ausgaenge bei Raeumen. Darunter fallen auch SpecialExits.
+     * Infos bei NPCs. Das Defaultinfo laesst sich allerdings nicht ohne
+       weiteres verwenden.
+     * Getraenke und Speisen in Kneipen.
+     * Besondere Aktionen sind auch moeglich, dazu muss jedoch noch etwas am
+       Code hinzugefuegt werden. Diese Art von Forscherpunkten sollte man nach
+       Moeglichkeit meiden.
+
+    Pro Objekt laesst sich nur ein FP vergeben. Die FP-Dichte sollte etwa bei
+    einem FP pro zehn Raeume liegen.
+
+    In Questgebieten sollten die FP NICHT fuer Details oder Aktionen vergeben
+    werden, die einen direkt in der Quest weiterbringen, sondern eher fuer
+    solche, die zu den entscheidenen Stellen fuehren. Damit werden Spieler,
+    die sich alles genau ansehen, belohnt, wohingegen Komplettloeser, die nur
+    das noetigste eingeben, leer ausgehen.
+
+ WIE SAG ICHS MEINEM ERZMAGIER?
+    Um die FP eintragen zu koennen, brauchen die Erzmagier eine Liste mit den
+    noetigen Informationen. Zu den noetigen Informationen gehoeren:
+
+     * der Dateiname des Objektes
+     * die Art des FP
+     * die Schluesselwoerter, mit denen der FP angesprochen werden soll
+
+    Die Schluesselwoerter sind normalerweise die Schluessel, die bei den
+    jeweiligen AddXXX()-Aufrufen im Objekt angegeben werden.
+
+    Beispiel:
+
+    /players/wargon/workroom: detail wand, waende
+    /players/wargon/mon/errol: info drachen
+
+ LETZTE AeNDERUNG:
+	Thu, 2013-04-04 von Humni
diff --git a/doc/wiz/ftp b/doc/wiz/ftp
new file mode 100644
index 0000000..bca4c5a
--- /dev/null
+++ b/doc/wiz/ftp
@@ -0,0 +1,41 @@
+FTP-Zugang fuer Magier (Zesstra, 12. Dez 2010, 23:17:52):
+Ok, wie in bekanntmachungen geschrieben, waren unsere (und die anderer Muds)
+Versuche bislang erfolglos, das mod_mud-Modul fuer den proftpd wieder ans
+Laufen zu bringen und ich habe als Zwischenloesung zumindest erstmal was
+anderes gebaut.
+
+Allerdings kann die aktuelle Loesung nicht 'live' beim Dateizugriff das Mud
+fragen, ob der User das auch darf. Daher sind alle Magier leider erstmal auf
+ihr Homeverzeichnis beschraenkt - dort duerfen sie natuerlich alles schreiben
+und lesen. Von dort/dorthin muss man dann im Mud mit cp/mv arbeiten.
+
+Wer seinen FTP-Zugang aktivieren will, muss - bis ich das in die Magiershell
+eingebaut habe - folgende zwei Schritte durchfuehren:
+1) xeval master()->update_ftp_access(getuid(this_interactive()), 1)
+   (Deaktivieren mit update_ftp_access(getuid(this_interactive()), 0)
+2) mit passwd ein neues PW setzen
+3) bis zur naechsten vollen Stunde warten
+
+Schritt 2) ist leider zwingend noetig, da wir das PW nicht im Klartext
+speichern und pure-ftpd unsere verschluesselten/gehashten Passwoerter nicht
+versteht. Deswegen muessen wir fuer FTP das PW anders speichern, was wir nur
+beim Neusetzen eines PWs koennen.
+
+Ich moechte euch noch um eins bitten: Wenn ihr was per FTP hin- und
+herschiebt, was dann weiter (z.B. nach /d) verschoben wird (oder von dort ihr
+euer Homeverzeichnis): loescht bitte den Kram aus eurem /players/<magier>
+wieder, denn a) geht das alles sonst ueberfluessig ins Backup und b) wird die
+Uebersicht deutlich schlechter, wenn man x Kopien des gleichen Krams im Mud
+verteilt hat.
+
+BTW: Dieser FTP-Zugang wird deaktiviert, falls sich ein Magier laenger als 2
+Wochen nicht im Mud einloggt, dann muesst ihr die Prozedur da oben
+wiederholen.
+
+Sorry fuer die Unannehmlichkeiten, aber so ists erstmal besser als gar nix.
+Falls wider Erwarten jemand beim mod_mud aushelfen mag, ist er uebrigens
+herzlich willkommen.
+
+Zesstra
+Stand: Februar 2011
+
diff --git a/doc/wiz/gift b/doc/wiz/gift
new file mode 100644
index 0000000..a2c4d9e
--- /dev/null
+++ b/doc/wiz/gift
@@ -0,0 +1,132 @@
+Krankheiten, Gifte und Flueche
+==============================
+
+     Einmal abgesehen vom einfachen Setzen von P_POISON im Spieler lassen 

+     sich Gifte und Krankheiten auch als Objekte ausprogrammieren, die dem 

+     Spieler in mehr oder weniger regelmaessigen Abstaenden Lebenspunkte 

+     abziehen. Auch Flueche koennen nicht nur als P_CURSED in Waffen und 

+     Ruestungen, sondern auch als Objekte vorliegen, und dem Spieler das 

+     Leben schwermachen
+
+     Um ein Objekt als Gift zu kennzeichnen, wird die Klasse CL_POISON 

+     gesetzt, fuer eine Krankheit ist CL_DISEASE und fuer einen Fluch 

+     CL_CURSE zu setzen (mit AddClass, siehe dort). Zusaetzlich wird die 

+     Schwere der Erkrankung bzw. Vergiftung in der Property P_LEVEL 

+     abgelegt, wobei P_LEVEL einen Wert zwischen 1 und 100 haben sollte. 

+     Mitglieder der Klerikergilde und andere Heiler koennen dann je nach 

+     P_LEVEL den betroffenen Spieler mehr oder weniger gut heilen. (Kleriker 

+     koennen CL_POISON-Vergiftungen heilen, einige Heiler jedoch nicht.)
+
+     Eine eindeutige Unterscheidung zwischen Giften, Krankheiten und 

+     Fluechen zu treffen, ist schwer, denn die Grenzen verschwimmen. 

+     Trotzdem hier eine grobe Klassifizierung:
+
+     Gifte : bringt der Spieler sich meist selbst bei (in dem er z.B.
+             einen giftigen Pilz isst). Oft auch sind die Stellen, wo
+             ein Spieler sich vergiften kann, irgendwie gekennzeichnet,
+             so dass eine Vergiftung umgangen werden kann.
+
+             Bei Giften wird durch das Heilen der Level des Giftobjekts
+             abhaengig vom Erfolg gesenkt. Ist der Level <= 0, wird das
+             Objekt vom Kleriker-Spellbook entfernt. Heiler sollten das
+             aehnlich machen. Logisch waere es daher, den Schaden, den das
+             Objekt macht, vom momentanen Level abhaengig zu machen.
+
+     Krankheit: werden dem Spieler durch Fremdeinwirkung beigebracht, auch
+             durch Ansteckung bei einem anderen Spieler oder NPC.
+             Bei ansteckenden Krankheiten ist auf die Ansteckrate zu achten
+             und darauf, dass die Krankheit mit der Zeit auch wieder
+             ausstirbt. Also entweder bei jeder Generation der Krankheit
+             das Ansteckungsrisiko senken oder einmal infizierte Spieler
+             immunisieren. Es sollten sich 2 idelnde Spieler nicht immer
+             wieder gegenseitig bis in alle ewig anstecken koennen. Auch
+             ist darauf zu achten, dass Netztote nicht angesteckt werden
+             koennen bzw. Netztote niemanden anstecken, da sich sonst die
+             Krankheit im Netztotenraum verbreiten kann.
+
+             Das Heilen geschieht im Kleriker-Spellbook wie bei Gift.
+
+     Flueche: werden wie Krankheiten durch Fremdeinwirkung beigebracht
+             (der Spieler wird halt verflucht). Ausserdem ist die Wirkung 

+             von Fluechen oft nicht auf einfaches Abziehen von Lebenspunkten 

+             beschraenkt, sondern der Spieler kann z.B. nicht mehr richtig 

+             sprechen (Sprachfluch ueber P_PERM_STRING), ist in der Bewegung 

+             eingeschraenkt oder greift wahllos NPCs an.
+
+             Hier ist das Entfluchen durch einen Kleriker anders. Findet
+             das Spellbook ein CL_CURSE-Objekt im Inv des Spielers, wird
+             gegen das Level des Objekt gewuerfelt. Bei Erfolg wird das
+             Objekt entfernt, bei Misserfolg passiert nichts!
+
+             Als Anhaltspunkte fuer den Level:
+             - < Level 10 sind einfach zu entfluche
+             - 10 - 20 sind fuer kleine Kleriker schon enorm schwierig
+               fuer max. Kleriker gut zu entfluchen.
+             - ueber 20 gelingt es auch einem max. Kleriker nicht immer
+               beim ersten mal.
+             - ab 30 muss der max. Kleriker schon mal tanken gehen
+             - Ueber Level 40 liegt die Chance schon im Promillebereich!!!
+             - Level 100 laesst sich ueberhaupt nicht mehr entfluchen.
+
+             Will man dem Spieler also eine reelle (und nicht nur 

+             mathematische) Chance lassen, sollte der Fluchlevel unter 40 

+             bleiben.
+
+     Das Schadensobjekt selbst ist unsichtbar, meist autoload und loest die 

+     Schadensfunktion ueber Callouts aus.
+
+ BEISPIEL:
+     Hier ein Beispiel fuer einen Giftpilz (gefunden bei Silvana, in
+     /d/dschungel/silvana/weg/obj/pilz.c):
+     ----------------------------------------------------------------------
+     #pragma strong_types
+     #include "../pfad.h"
+
+     inherit "/std/thing";
+
+     void create() {
+       if(!clonep(TO)) {
+           set_next_reset(-1);
+           return;
+       }
+       ::create();
+
+       SetProp(P_SHORT,0);
+       SetProp(P_INVIS,1);
+       SetProp(P_LONG,0);
+       SetProp(P_NODROP,1);
+       SetProp(P_NEVERDROP,1);
+       SetProp(P_AUTOLOADOBJ,1);
+       SetProp(P_WEIGHT,0);
+       SetProp(P_NAME,"Pilzvergiftung");
+       SetProp(P_KILL_NAME,"Eine Pilzvergiftung");
+       SetProp(P_GENDER,FEMALE);
+       SetProp(P_ARTICLE,1);
+       SetProp(P_LEVEL,10);
+       call_out("next_step",2);
+       AddClass(CL_POISON);
+     }
+
+     void next_step();
+
+     void next_step() {
+       object pl;
+       if(!(pl=TOE) || !query_once_interactive(pl) ||
+          pl->QueryProp(P_GHOST)){
+         remove();
+         return ;
+       }
+       call_out(#'next_step ,5);
+       if(!interactive(pl)) return;
+       tell_object(pl,
+       		   "Dein Bauch schmerzt. Du windest Dich in Kraempfen.\n");
+       if(ENV(pl)) tell_room(ENV(pl),
+        pl->Name()+" windet sich vor Schmerzen am Boden.\n",({ pl }));
+       pl->do_damage(QueryProp(P_LEVEL)*2 + random(10),TO);
+     }
+
+ SIEHE AUCH:
+    P_POISON, P_CURSED, P_PERM_STRING
+
+ LETZTE AeNDERUNG:
+   11.08.2007, Zesstra
diff --git a/doc/wiz/gilden-doku b/doc/wiz/gilden-doku
new file mode 100644
index 0000000..08e3aa7
--- /dev/null
+++ b/doc/wiz/gilden-doku
@@ -0,0 +1,496 @@
+Gilden 
+*******
+
+Gilden sind dazu da, Spielern besondere Faehigkeiten zu verleihen. Dies
+koennen Zaubersprueche (Spells) sein, es kann aber auch andere Faehigkeiten
+(Skills) geben. Als Spell gilt jede Faehigkeit, die ein Spieler mit einem
+Befehl direkt aufrufen muss. Damit auch andere Gilden die gleichen
+Zaubersprueche verwenden koennen, muessen die Sprueche in eigenen
+Spellbooks abgelegt werden. Eine Gilde kann Sprueche aus beliebigen
+Spellbooks verwenden und diese ggf. leicht modifizieren. 
+
+Gildenobjekt 
+=============
+
+Eine Gilde muss ein Objekt haben, bei dem der Spieler der Gilde beitreten
+bzw. austreten und die Faehigkeiten der Gilde erwerben kann. Gewoehnlich
+ist dies ein Raum, der "/std/gilden_room" inheritet, es kann aber auch ein
+anderes Objekt sein, fuer diesen Fall ist "/std/gilden_ob" vorgesehen. 
+
+Die Beitrittsbedingungen fuer die Gilde werden in Form eines 
+Restriction-Mappings in der Property P_GILDEN_RESTRICTIONS
+abgelegt. 
+
+Das Spellbook, in dem die Spells der Gilde stehen, muss in
+P_GUILD_DEFAULT_SPELLBOOK genannt sein. Es wird automatisch
+"/spellbooks/" vorne an den Namen angefuegt. Die Spells, die aus diesem
+Spellbook verwendet werden sollen, koennen dann einfach mit
+AddSpell(name) ausgewaehlt werden. Wenn ein Spruch modifiziert werden
+soll so kann ein Mapping mit zusaetzlichen Informationen als zweites
+Argument angegeben werden. In diesem kann man dann auch ein anderes
+Spellbook als das Default-Spellbook mit ([SI_SPELLBOOK:name])
+angeben. In P_GLOBAL_SKILLPROPS kann ein Mapping angegeben
+werden, das alle Spells und Skills modifiziert. P_GLOBAL_SKILLPROPS
+und P_GILDEN_DEFAULT_SPELLBOOK muessen uebrigens gesetzt
+werden bevor mit AddSpell/Skill Spells oder Skills hinzugefuegt werden. 
+
+Fuer andere Faehigkeiten sind AddSkill und LearnSkill vorgesehen.
+LearnSkill wird im Gegensatz zu LearnSpell jedoch nicht automatisch vom
+/std/gilden_room mit "lerne" aufgerufen, weil i.A. diese Faehigkeiten auf
+andere Art erworben werden, z.B. beim Gildeneintritt oder durch
+Trainingsstunden. Mit LearnSkill kann man nur solche Faehigkeiten
+erwerben, die mit AddSkill angefuegt wurden. 
+
+Skills werden ueblicherweise durch den Aufruf von UseSkill im Spieler
+verwendet. Wenn der Spieler in einer Gilde ist und eine Funktion unter
+SI_SKILLFUNC zu finden ist, so wird diese im Gildenobjekt aufgerufen,
+sonst wird versucht StdSkill_Name im Spieler aufzurufen. Wenn auch das
+fehlschlaegt wird nur der Wert unter SI_ABILITY zurueckgegeben. 
+
+Es stehen folgende Funktionen zur Benutzung zur Verfuegung: 
+
+ o QuerySpell(name)
+   Liefert die Informationen zu diesem Spell 
+ o QuerySkill(name)
+   Liefert die Informationen zu dieser Faehigkeit 
+ o AddSpell(name,info)
+   Spell wird hinzugefuegt 
+ o AddSkill(name,info)
+   Faehigkeit wird zugefuegt 
+ o LearnSpell(name)
+   Spieler lernt den angegebenen Spell, falls moeglich. Liste der Spells
+   wird ausgegeben, falls keiner angegeben ist. 
+ o LearnSkill(name)
+   Spieler erwirbt diese Faehigkeit, falls moeglich. Liste aller
+   Faehigkeiten wird ausgegeben, falls keine angegeben ist. 
+ o GildenName()
+   Liefert den Namen dieser Gilde. 
+ o InitialSkillAbility(info,spieler)
+   Rechnet den Anfangswert fuer SI_SKILLABILITY aus. 
+ o SkillListe(x)
+   Es wird angezeigt, welche Spells/Skills der Spieler lernen kann.
+   Dabei bedeutet x: 
+    o 1: Nur Spells anzeigen 
+    o 2: Nur Skills anzeigen 
+    o 3: Beides anzeigen 
+
+Von diesen Funktionen stehen in /std/gilden_room automatisch
+bei_oder_austreten und LearnSpell dem Spieler zur Verfuegung. 
+
+Spellbook 
+==========
+
+Spellbooks stellen die Spells zur Verfuegung, die Spieler oder Monster
+verwenden koennen. Alle Spellbooks sollten /std/spellbook inheriten. In der
+einfachsten Form kann ein Spell wie folgt hinzugefuegt werden: 
+AddSpell(verb,kosten,level)
+Dabei ist "verb" sowohl der Name des Verbs, mit dem der Spruch
+aufgerufen werden soll, wie auch der Name der Funktion, die dabei
+aufgerufen wird. "kosten" sind die Magiepunkte, die fuer den Spruch
+benoetigt werden und "level" ist der Spielerlevel, der noetig ist, um diesen
+Spruch zu lernen. 
+
+In der flexibleren Form werden Spells mit 
+AddSpell(verb,kosten,info)
+hinzugefuegt. Dabei ist "info" ein Mapping, in dem alle anderen 
+Spell-Informationen stehen. Dabei kann z.B. eine andere Funktion als das
+Verb als Eintrag 
+SI_SKILLFUNC:name
+angegeben werden. Wenn zum Lernen eine bestimmte Stufe erforderlich ist
+so muss 
+SI_SKILLRESTR_LEARN:([P_LEVEL:level])
+eingetragen sein. Es sollten alle Werte, von denen ein Spell abhaengt, in dem
+Mapping eingetragen sein. Dadurch haben Gilden die Moeglichkeit, Spells
+mit Offsets und Faktoren zu modifizieren. 
+
+In P_GLOBAL_SKILLPROPS kann ein Mapping stehen, dass bei jedem
+Spell zum Info addiert wird. Dieses sollte gesetzt werden, bevor die Spells
+mit AddSpell hinzugefuegt werden. 
+
+Die Benutzung von Spells laeuft wie folgt ab: 
+
+ o Zuerst wird ueberprueft, ob der Spieler den Spruch verwenden darf.
+   Dazu wird die Funktion CanTrySpell aufgerufen. Diese prueft
+   normalerweise, ob der Spieler kein Geist ist und ob er die
+   Einschraenkungen erfuellt, die als SI_SKILLRESTR_USE
+   angegeben sind. 
+ o Als naechstes wird geprueft, ob der Spieler noch genug Magiepunkte
+   hat. Diese stehen im Mapping unter SI_SPELLCOST. 
+ o Als letztes wird geprueft, ob der Spieler noch erschoepft ist von
+   seinem letzten Spruch. 
+ o Nun wird die eigentliche Funktion des Spells aufgerufen, wenn es die
+   Umgebung zulaesst. Die Funktion muss einen positiven Wert
+   zurueckgeben, wenn der Spruch gelungen ist, und einen negativen,
+   wenn er misslungen ist. Falls der Spruch aus irgend einem Grund
+   nicht anwendbar ist soll 0 zurueckgegeben werden. 
+ o Bei Erfolg oder Misserfolg werden die Magiepunkte abgezogen und
+   der Spieler ist fuer die naechste Zeit erschoepft. Die Zeitspanne ist
+   im Mapping unter SI_SPELLFATIGUE zu finden. 
+ o Bei Erfolg wird die Funktion "Erfolg" aufgerufen, bei Misserfolg
+   die Funktion "Misserfolg" 
+ o Die Funktion "Misserfolg" ruft normalerweise die Funktion "Learn"
+   auf, damit der Spieler aus seinen Fehlern lernt. 
+
+Die eigentliche Spellfunktion sollte, falls der Spell anwendbar ist, mit
+SpellSuccess pruefen, ob er erfolgreich ist oder nicht. Dabei gelten Werte
+groesser Null als Erfolg. In der Spellfunktion sollten, falls moeglich, 
+SkillAttribute des Spielers sowie Faktoren und Offsets beruecksichtigt
+werden. Fuer beides stehen einfach zu handhabende Funktionen zur
+Verfuegung. Dies ist zwar etwas mehr Arbeit, dafuer geschehen dann Dinge
+wie Interaktionen zwischen den Spells fast automatisch. 
+
+Folgende Funktionen stellt das Standard-Spellbook zur Verfuegung: 
+
+ o QuerySpell(name)
+   Liefert Informations-Mapping zu diesem Spell. 
+ o AddSpell(name,kosten,info)
+   Fuegt Spell mit angegebenen Kosten und dem
+   Informations-Mapping ins Spellbook ein. 
+ o TryAttackSpell(opfer,schaden,typen,is_spell,caster,info)
+   Versucht den Angriffs-Spruch auf den Gegner anzuwenden. Die
+   mittleren 4 Werte sind die, die auch bei Defend uebergeben werden.
+   Dabei wird die Abwehrfaehigkeit des Gegners gegen Magie und das
+   Skill-Attribut SA_DAMAGE automatisch beruecksichtigt. 
+ o TryDefaultAttackSpell(opfer,caster,info,is_spell)
+   Wie TryAttackSpell, nur werden Schaden und Schadenstypen
+   automatisch aus dem Informations-Mapping entnommen. Bei beiden
+   Funktionen sollte als is_spell uebrigens ein String stehen, z.B.
+   "Feuerball", damit es leichter moeglich ist, Monster zu schreiben, die
+   auf diese reagieren. 
+ o SpellSuccess(caster,info)
+   Ermittelt, ob der Spell funktioniert oder fehlschlaegt. Dabei wird
+   auch eine evtl. vorhandene Spellcasting-Faehigkeit (SK_CASTING)
+   beruecksichtigt. Ohne Spellcasting-Faehigkeit liegt das Ergebnis
+   zwischen -MAX_ABILITY und +MAX_ABILITY, mit dieser
+   Faehigkeit koennen die Werte zwischen -2*MAX_ABILITY und
+   +2*MAX_ABILITY liegen. Werte kleiner oder gleich Null sollen
+   als Fehlschlag interpretiert werden. 
+   Wer will, kann Werte ueber +MAX_ABILITY als besonders gut
+   gelungene Spells interpretieren und bei Werten unter
+   -MAX_ABILITY unangenehme Wirkungen ausloesen, z.B. kann
+   sich der Spell dann gegen den Spieler richten... 
+   Wenn ein Spieler die Spellcasting-Faehigkeit hat und ein Spruch
+   besonders gut gelingt, so freut er sich und verbessert diese
+   Faehigkeit. 
+ o CanTrySpell(caster,info)
+   Ermittelt, ob der Spieler den Spruch anwenden darf. Normalerweise
+   ist diese der Fall, wenn er kein Geist ist und die Bedingungen
+   erfuellt, die unter SI_SKILLRESTR_USE im Mapping eingetragen
+   sind. 
+ o Learn(caster,spell,info)
+   Diese Funktion wird normalerweise bei Misserfolg aufgerufen,
+   damit der Spieler aus seinen Fehlern lernt. Dabei wird
+   ueblicherweise die Intelligenz des Spielers beruecksichtigt. Fuer je 2
+   Stufen A_INT bekommt der Spieler SI_SKILLLEARN hinzu. 
+
+   Moechte man ein anderes Attribut zum lernen verwenden kann man dies
+   in Form eines Mappings in SI_LEARN_ATTRIBUTE tun.
+
+   SI_LEARN_ATTRIBUTE:([A_STR:1]) macht das Lernen rein staerkeabhaengig,
+   SI_LEARN_ATTRIBUTE:([A_STR:1,A_INT:2]) bildet den gewichteten Mittelwert
+   von STR und zweifacher INT.  
+
+ o Erfolg(caster,spell,info)
+   Diese Funktion wird bei Erfolg aufgerufen. 
+ o Misserfolg (caster,spell,info)
+   Diese Funktion wird bei Misserfolg aufgerufen. 
+ o FindVictim(wen,spieler,msg)
+   "wen" wird in der Umgebung des Spielers gesucht. Falls diese
+   Variable Null ist wird zufaellig ein Feind ausgewaehlt. Falls
+   niemand gefunden wird, so wird "msg" ausgegeben. 
+ o FindLivingVictim(wen,spieler,msg)
+   Wie FindVictim, nur wird zusaetzlich ueberprueft, ob es ein
+   Lebewesen ist. 
+ o FindEnemyVictim(wen,spieler,msg)
+   Wie FindLivingVictim, nur der Spieler selbst wird ausgenommen
+   und wenn es vorher noch kein Feind war, so wird Kill aufgerufen
+   damit es hinterher garantiert einer ist. 
+ o FindGroup(spieler,wen)
+   Bei Spielern findet die Funktion alle Monster im Raum, wenn "wen"
+   negativ ist, alle Spieler wenn "wen" positiv ist und alle Lebewesen
+   wenn "wen" Null ist. Bei Monstern ist es genau umgekehrt. Es sollte
+   jedoch FindGroupP mit 100% verwendet werden. 
+ o FindGroupN(spieler,wen,n)
+   Wie FindGroup, jedoch maximal n Personen. Das Skill-Attribut
+   SA_EXTENSION wird automatisch beruecksichtigt. 
+ o FindGroupP(spieler,wen,prozent)
+   Wie FindGroup, jedoch jede Person mit der angegebenen
+   Wahrscheinlichkeit. Das Skill-Attribut SA_EXTENSION wird
+   automatisch beruecksichtigt. 
+
+Neue Funktionen im Living 
+==========================
+
+ o QuerySkillAttribute(name)
+   Hiermit kann das Skill-Attribut mit dem angegebenen Namen
+   abgefragt werden. 
+ o SetSkillAttribute(caster,name,wert,dauer,func)
+   Hiermit kann das angegebene Skill-Attribut vom caster fuer die
+   angegebene Dauer auf einen Wert gesetzt werden. Es kann eine
+   Funktion angegeben werden, die den Wert statt dessen liefern soll. 
+ o QuerySkill(name)
+   Dies liefert die spielerspezifischen Skill-Informationen. 
+ o QuerySkillAbility(name)
+   Dies liefert von den Skill-Informationen nur SI_ABILITY. 
+ o ModifySkill(name,info,diff)
+   Modifiziert die Skill-Informationen. Wenn "info" ein Mapping ist,
+   so wird es zu dem alten Mapping "addiert" (also die angegebenen
+   Werte geaendert), wenn nur ein Wert angegeben ist, wird
+   angenommen dass es sich dabei um SI_ABILITY handelt. 
+ o LearnSkill(name,add,diff)
+   Addiert den angegebenen Wert zu SI_ABILITY. Dabei ist "diff" der
+   Schwierigkeitsgrad von diesem Spell/Skill. Durch den
+   Schwierigkeitsgrad SI_ABILITY abhaengig vom Level begrenzt. 
+ o UseSpell(arg,spell)
+   Das Lebewesen benutzt den Spell mit den angegebenen Argumenten.
+   Wenn kein Spell angegeben ist, so wird query_verb() verwendet. 
+ o UseSkill(skill,arg)
+   Das Lebewesen benutzt die Faehigkeit. 
+
+Neue Properties/Funktionen in Living/Combat 
+============================================
+
+Einige Sprueche erfordern es, das Verhalten bei Attack und Defend ziemlich
+weitreichend zu aendern. Dafuer wurden folgende Properties und
+Funktionen eingebaut: 
+
+ o P_TMP_ATTACK_HOOK
+   Hier kann ein Array der Form ({Endzeitpunkt,Objekt,Funktion})
+   stehen. Solange der Endzeitpunkt noch nicht ueberschritten wurde
+   und das angegebene Objekt existiert, wird anstelle von Attack die
+   Funktion in dem Objekt aufgerufen. Wenn die Funktion 0 liefert
+   wird der Rest von Attack nicht mehr ausgefuehrt. 
+ o P_TMP_DEFEND_HOOK
+   Wie P_ATTACK_HOOK, nur mit Defend. Damit sind z.B.
+   Sprueche moeglich, die fuer kurze Zeit eine magische Schutzhuelle
+   erschaffen. Wenn die Funktion 0 liefert wird der Rest von Defend
+   nicht mehr ausgefuehrt. Wenn es ein Array der Form
+   ({damage,dt,is_spell}) ergibt wird es wie bei DefendOther
+   interpretiert. 
+ o P_DEFENDERS
+   Liste von Lebewesen, die mit InformDefend(enemy) informiert
+   werden sollen, sobald ein neuer Feind hinzukommt. Bei einem
+   zufaellig ausgewaehltem Lebewesen aus dieser Liste wird ausserdem
+   DefendOther mit den Argumenten von Defend aufgerufen. 
+ o AddDefender(friend)
+   Fuegt Lebewesen in P_DEFENDERS ein, wenn es noch nicht in der
+   Liste ist. 
+ o InformDefend(enemy)
+   Siehe oben. 
+ o DefendOther(dam,dt,is_spell,enemy)
+   Mit dieser Funktion kann man Lebewesen erschaffen, die Schaden
+   von anderen abwenden oder modifizieren. Wenn diese Funktion ein
+   Array ({dam,dt,is_spell}) zurueckgibt so werden bei dem zu
+   verteidigenden Lebewesen diese Werte genommen anstelle der alten.
+   Man kann also z.B. ein Monster erschaffen, das ein
+   feuerempfindliches anderes Monster verteidigt, indem es z.B.
+   Feuerbaelle in Eishagel verwandelt. 
+
+Standard-Skills 
+================
+
+Folgende Faehigkeiten werden schon beruecksichtigt und sind auch
+vordefiniert. Wenn sie unveraendert uebernommen werden sollen muss nur
+SI_ABILITY gesetzt werden. 
+
+ o SK_SWORDFIGHTING
+   Schwertkampf. Bis zu 33+A_STR+A_DEX Aufschlag bei
+   Schwertern, wenn jemand diese Faehigkeit zu 100% hat. 
+ o SK_WEAPONLESS
+   Kampf mit blossen Haenden. Bis zu 100+A_STR+3*A_DEX
+   Aufschlag. 
+ o SK_TWOHANDED
+   Kampf mit zweihaendigen Waffen. Bis zu 33+A_STR Aufschlag. 
+ o SK_NIGHTVISION
+   Wer diese Faehigkeit zu 100% hat braucht 20 Sekunden pro
+   fehlendem Lichtlevel um sich an die Dunkelheit zu gewoehnen. 
+ o SK_BOOZE
+   Mit 100% dieser Faehigkeit wird bei jedem alkoholischen Getraenk
+   80% vom Alkoholgehalt abgezogen. 
+
+Folgende Faehigkeiten werden beruecksichtigt, sind aber nicht vordefiniert: 
+
+ o SK_MAGIC_ATTACK
+   Wenn diese Faehigkeit vorhanden ist, wird die Funktion unter
+   SI_SKILLFUNC im Gildenobjekt aufgerufen, falls der Spieler sonst
+   mit blossen Haenden angreifen wuerde. Wenn dabei ein Mapping
+   zurueckgegeben wird, so werden die Werte von
+   SI_SKILLDAMAGE, SI_SKILLDAMAGE_TYPE und
+   SI_SKILLDAMAGE_MSG genommen anstelle der Werte in
+   P_HANDS. 
+ o SK_MAGIC_DEFENSE
+   Wenn hier unter SI_SKILLFUNC eine Funktion eingetragen ist, so
+   wird sie bei Defend im Gildenobjekt aufgerufen und bekommt im
+   Informations-Mapping SI_SKILLDAMAGE,
+   SI_SKILLDAMAGE_TYPE und SI_SPELL uebergeben. Wenn sie
+   ein Mapping zurueckgibt werden hieraus SI_SKILLDAMAGE und
+   SI_SKILLDAMAGE_TYPE entnommen und ersetzen die alten
+   Werte von "dam" und "dam_type". 
+ o FIGHT(Waffentyp)
+   Falls diese Faehigkeit vorhanden ist wird der entsprechenden
+   Funktion in SI_SKILLDAMAGE der bisherige Schaden uebergeben.
+   Falls sie ein Mapping zurueckliefert wird an dieser Stelle auch der
+   neue Schaden erwartet. 
+ o SK_FIGHT
+   Wie Fight(Waffentyp), nur wird diese Faehigkeit, falls vorhanden,
+   bei jeder Waffe benutzt und kann auch zusaetzlich andere Werte fuer
+   SI_SKILLDAMAGE_TYPE ergeben. Waffe und Waffentyp werden
+   uebrigens in SI_WEAPON und SI_WEAPON_TYPE uebergeben. 
+ o SK_CASTING
+   Spellcasting. Die Wahrscheinlichkeit, dass der Spell gelingt, steigt
+   bei 100% dieser Faehigkeit auf das Doppelte. Nur mit dieser
+   Faehigkeit ist es moeglich, ueber die Maximalgrenzen zu kommen,
+   so dass dann auch Spells besonders gut gelingen koennen. 
+
+Temporaere Property-Aenderungen 
+================================
+
+Oft muessen Spells irgendwo Properties fuer kurze Zeit veraendern, wie
+z.B. P_LIGHT oder P_NOMAGIC in Raeumen. Fuer diesen Zweck kann
+man in /obj/tmp_prop_master die Funktion SetTmpProp aufrufen. Diese
+Funktion erwartet das Objekt, in dem die Property zu setzen ist, den Namen
+der Property, den zeitweiligen Wert und den Zeitpunkt, bis zu dem diese
+Aenderung gelten soll. 
+
+Skill Informationen 
+++++++++++++++++++++
+
+In den Informationsmappings zu den Spells/Skills sollten alle (zusaetzlich)
+noetigen Informationen stehen, denn nur wenn z.B. ein Feuerball in einem
+Spellbook als Schaden 300 eingetragen hat und diesen Wert dem Mapping
+entnimmt, kann eine andere Gilde diesen Spruch recyclen und mit Schaden
+400 anbieten, natuerlich sollte er dann auch in der Gilde mehr kosten. 
+
+SIEHE AUCH: skill_info_liste
+
+Faktoren und Offsets 
+---------------------
+
+Man kann in dem Informations-Mapping zu jedem numerischen Wert
+"Name" noch zwei zusaetzliche Werte FACTOR("Name") und
+OFFSET("Name") eintragen und diese Werte automatisch zur eigentlichen
+Wertbestimmung beruecksichtigen. Mit folgenden Funktionen sollte man
+im Spellbook dem Mapping Werte entnehmen: 
+
+ o GetValue(name,map,spieler) 
+ o GetOffset(name,map,spieler)
+   OFFSET(name). 
+ o GetFactor(name,map,spieler)
+   Ergibt FACTOR(name), falls ungleich Null, sonst 100. 
+ o GetFValue(name,map,spieler)
+   Ergibt (Wert*Faktor)/100. 
+ o GetValueO(name,map,spieler)
+   Ergibt Wert+Offset. 
+ o GetFValueO(name,map,spieler)
+   Ergibt (Wert*Faktor)/100+Offset. 
+
+Nach Moeglichkeit sollte man davon im Spellbook GetFValueO benutzen,
+wenn es angebracht ist. Auf jeden Fall sollten von den drei Werten
+moeglicht viele auf eine angemessene Weise beruecksichtigt werden, denn
+dadurch bekommt das Gildenobjekt feinere Kontrollmoeglichkeiten, wenn
+ein Spruch modifiziert werden soll. Es ist dann fuer die Gilde aeusserst
+einfach festzulegen, dass z.B. Zwerge bei allen Angriffsspruechen 20%
+mehr Schaden verursachen und beim Feuerball Elfen einen hoeheren
+garantierten Wert hinzubekommen. 
+
+Funktionen 
+-----------
+
+Wenn ein Spellbook eine der oben angesprochenen Funktionen benutz, um
+einen numerischen Wert zu ermitteln und anstelle des Wertes steht etwas,
+das als Funktion interprtiert werden kann, so wird diese Funktion
+ausgewertet und das Ergebnis als Wert genommen. Als Funktion
+interpretiert werden kann: 
+
+ o Eine Closure 
+ o Ein Array ({objekt,funktionsname}) oder ({objektname,funktionsname}) 
+ o Ein Funktionsname. Hierbei sollte man sich jedoch darueber im
+   klaren sein, in welchem Objekt versucht wird die Funktion
+   aufzurufen. Ueblicherweise geschieht dies im Spellbook, jedoch
+   werden SI_DIFFICULTY, SI_SKILLLEARN und SI_SPELLCOST
+   auch beim Lernen benoetigt und dies geschieht vom Gildenobjekt
+   aus. Wenn bei diesen 3 Eintraegen eine Funktion den Wert liefern
+   soll, so muss sie in eine der drei anderen Formen eingetragen werden,
+   damit das richtige Objekt ermittelt werden kann. 
+
+SIEHE AUCH: execute_anything
+   
+Fuer nicht-numerische Werte kann man GetData verwenden. Dabei werden
+jedoch nur closures automatisch ausgewertet. 
+
+Skill Attribute 
+++++++++++++++++
+
+Skill-Attribute sind Attribute, die alle anderen Skills beeinflussen koennen.
+Normalerweise sind alle Skill-Attribute 100%, sie koennen jedoch fuer
+kurze Zeit auf andere Werte zwischen 10% und 1000% gesetzt werden. Bei
+der Abfrage der Attribute werden 3 Werte beruecksichtigt: 
+
+ o Wert, der vom Lebewesen selbst gesetzt wurde. 
+ o Wert, der von einem anderen unter 100% gesetzt wurde. Wenn
+   mehrere andere Lebewesen den Wert unter 100% gesetzt haben so
+   gilt die Aenderung von dem mit dem hoechsten Level. Eine solche
+   Aenderung wird ueblicherweise von einem Gegner veranlasst. 
+ o Wert, der von einem anderen ueber 100% gesetzt wurde. Auch hier
+   gilt die Aenderung von dem Lebewesen mit dem hoechsten Level. 
+
+Wenn z.B. ein Spieler seine Geschwindigkeit fuer zwei Minuten auf 200%
+und ein Monster sie fuer eine Minute auf 25% setzt, so ist sie eine Minute
+lang 50% und die naechste Minute 200% bevor sie wieder auf 100% gesetzt
+wird. 
+
+SIEHE AUCH: ModifySkillAttribute
+
+Restriction Mappings 
++++++++++++++++++++++
+
+Mit Restriction Mappings koennen Einschraenkungen auesserst einfach
+angegeben werden. In dem Mapping wird einfach nur angegeben, was durch
+welchen Wert eingeschraenkt werden soll. Wenn z.B. mindestens Level 15
+und Intelligenz 10 verlangt wird, so ist das Mapping
+([P_LEVEL:15,A_INT:10]). Folgende Einschraenkungen koennen verlangt
+werden: 
+
+SIEHE AUCH: check_restrictions
+
+Einschraenkungen werden mit check_restrictions(spieler,mapping)
+ueberprueft. Die Funktion liefert 0 zurueck wenn der Spieler alle
+Einschraenkungen erfuellt und einen String mit der Begruendung, falls eine
+Einschraenkung nicht erfuellt ist. 
+
+Programmierrichtlinien 
+=======================
+
+ o In Spellbooks moeglichst oft Faktoren und Offsets beruecksichtigen. 
+ o Die Skill-Attribute beruecksichtigen, falls moeglich. 
+ o Alles Spells muessen eine Verzoegerungszeit haben, in der kein
+   weiterer Spell anwendbar ist. Hiervon kann es Ausnahmen geben, wenn
+   das Gildenkonzept es anders vorsieht (z.B. bei den Kaempfern) oder
+   sonst reguliert.
+ o Kostenlose Spells sollte es nicht geben. Falls doch, dann nur mit sehr
+   hoher Verzoegerungszeit, sonst lassen die Leute nur ihr Frontend
+   spielen. 
+ o Jeder Skill sollte eine levelabhaengige maximale Faehigkeit haben.
+   D.h., wenn SI_DIFFICULTY gesetzt ist sollte der Wert groesser als
+   -100 sein. 
+ o Spells duerfen nicht Monster beliebig hoher Staerke einfach
+   umhauen. Es sollte nur bis zu einer Maximalstaerke moeglich sein. 
+ o Der Schaden, den ein Spruch bewirkt, darf von der Staerke nicht
+   groesser sein, als der, den eine Waffe mit WC 25*SP bewirken
+   wuerde. Auch hier sollte man ein wenig gesunden Menschenverstand
+   spielen lassen - es kommt auch immer drauf an, ob ein Angriff
+   magisch ist oder physikalisch.
+ o Die Heilung sollte nicht die dafuer noetigen SP ueberschreiten. 
+   Ausnahmen fuer explizite Heilgilden (Klerus) kann es geben.
+
+Auswirkung von SI_DIFFICULTY 
+-----------------------------
+
+Folgende Maximalwerte sind fuer SI_ABILITY bei den angegebenen Leveln
+moeglich, wenn SI_DIFFICULTY auf den Wert in der linken Spalte gesetzt
+ist. 
+
+SIEHE AUCH: LimitAbility oder /std/living/skills::LimitAbility
+
+5. Okt 2011 Gloinson
+ 
diff --git a/doc/wiz/git b/doc/wiz/git
new file mode 100644
index 0000000..7200ff3
--- /dev/null
+++ b/doc/wiz/git
@@ -0,0 +1,23 @@
+Nutzung von Git im MorgenGrauen
+===============================
+
+Was ist Git?
+  Git ist eine Software zur (verteilten) Versionsverwaltung von Dateien.
+  (http://git-scm.com/, http://de.wikipedia.org/wiki/Git)
+  Fuer Windows gibt es ein Paket, welches auch die Integration in den Explorer
+  anbietet:
+  https://code.google.com/p/gitextensions/
+
+
+SIEHE AUCH
+  git-repositories: Repository-Verwaltung im Mud
+  git-howto: Wie git benutzt wird
+  git-workflow: Ein simples Beispiel eines Arbeitsflusses mit Git
+  git-kooperation: Erweiterung fuer Fortgeschrittene zu git-workflow
+  git-sync: Wie die Synchronisierung zw. git-Repos und Mudlib ablaeuft
+  git-faq: haeufig gestellte Fragen/Probleme
+  git-links: Verweise ins WWW
+
+LETZTE AeNDERUNG:
+  25.03.2011, Zesstra
+
diff --git a/doc/wiz/git-exclude b/doc/wiz/git-exclude
new file mode 100644
index 0000000..439b5db
--- /dev/null
+++ b/doc/wiz/git-exclude
@@ -0,0 +1,27 @@
+Beim Import von Verzeichnissen aus dem Mud in ein git Repository und bei der
+Synchronisierung zwischen Repositories und Mudlib werden die folgenden
+Verzeichnisse und Dateien komplett ignoriert.
+
+# fuer div. SCMs benutzte Verzeichnisse
+- .svn/
+- .git/
+- .gitignore
+- CVS/
+# Savefiles
+- *.o
+# logs and rep files
+- *.log
+- log/
+- *.rep
+- *.err
+# backups of editors
+- *~
+- *.swp
+# this should als never be imported - marks a directory to be imported.
+- git-mud-import
+# keine gepackten Archive
+- *.gz
+- *.bz2
+- *.zip
+- *.tar
+
diff --git a/doc/wiz/git-faq b/doc/wiz/git-faq
new file mode 100644
index 0000000..7ed4826
--- /dev/null
+++ b/doc/wiz/git-faq
@@ -0,0 +1,83 @@
+Haeufig gestellte Fragen zum Thema Git im Morgengrauen
+======================================================
+
+* Was muss ich machen, damit mein Git-Repo automatisch mit dem MG
+* synchronisiert wird?
+  Eine Synchronisation findet automatisch statt, wenn man einen Import eines
+  Verzeichnisses aus dem Mud durchfuehrt.
+  Macht man dies nicht, sondern erstellt sich unabhaengig vom Mud das Repo,
+  muss man sich an einen Erzmagier mit Shellzugang wenden.
+
+* Aufnahme als Regionsmitarbeiter/Regionsmagier/Weiser/Erzmagier/Gott
+  Dies ist zurzeit nur durch einen EM moeglich.
+
+* Wie benutze ich Git unter Windows?
+  GitHub hat eine Anleitung fuer msysgit, welche im wesentlichen auch fuers MG
+  brauchbar ist:
+  http://help.github.com/win-set-up-git/
+  Eine weitere Moeglichkeit ist hier angeben:
+  http://rogerdudler.github.io/git-guide/
+  Einige in Frage kommende Git-Pakete sind hier kurz vorgestellt:
+  http://www.makeuseof.com/tag/5-windows-git-clients-git-job/
+  Eine Anleitung fuer die Nutzung von Putty als SSH-Client unter Windows
+  findet sich in contrib/putty.mkd auf https://github.com/sitaramc/gitolite/
+
+* Wie kann ich mir die Geschichte meines Repos graphisch anzeigen lassen?
+  Da gibt es verschiedene Loesungen, vor allem auch abhaengig vom
+  Betriebssystem. Auf allen geht vermutlich 'gitk' und 'git gui'.
+  Auf MacOS gibt es auch 'GitX'.
+
+* Kann man Aenderungen/Diffs/ farbig markiert anzeigen?
+  > git log -p --color-words
+  Alternativ kann man .git/config folgende Parameter setzen:
+  [color]
+  	color.diff=auto
+	color.grep=auto
+	color.status=auto
+
+  Wenn man es generell bunt haben will, setzt man einfach
+  [color]
+  	color.ui=auto
+
+  in die Konfigurationsdatei.
+
+* Warum soll ich denn die color-Einstellungen auf auto und nicht true setzen?
+  Der Wert auto bewirkt, dass git nur dann die Ausgaben einfaerbt, wenn diese
+  nach STDOUT gehen. Ansonsten bekommt man den ASCII-kodierten Farbstring in
+  die Ausgabedatei geschrieben.
+
+* Wie kann ich eine Repository loeschen?
+  Zur Zeit ist dies nur durch einen EM mit Shellzugang auf dem MG-Rechner
+  moeglich.
+
+* Kann ich an einem Gebiet, fuer das ich keinen Schreibzugriff habe, helfen
+* einen Bug zu fixen?
+  Ja - sofern Du Leserechte auf das Repository hast. Du kannst das Repo dann
+  forken, d.h. eine Kopie erstellen. Die beste Methode hierfuer ist
+  > ssh git@mg.mud.de fork d/gebirge/zook/wald players/zesstra/public/zwald
+  Hierbei wird ein Clone des Repos erstellt und sich gemerkt, welches das
+  Original war. In Deinem Repo kannst Du nun einen Bugfix machen. Bist Du
+  fertig, sagst Du dem Gebietsmagier (oder einem zustaendigen RM) Bescheid und
+  bittest ihn, den entsprechenden Branch (z.B. syntax_bugfix) zu pullen.
+
+* Wie vermeide ich einen 'merge commit', wenn ich lokale Aenderungen in einem
+* Zweig habe, in den ich Aenderungen aus dem MG pullen moechte?
+  Eine Moeglichkeit hierfuer ist das Pullen mit 'git pull --rebase', um git
+  einen implizites Rebase beim Pull durchfuehren zu lassen.
+
+
+Was ist git?
+Wo krieg ich git her?
+Wie kann ich das Repository clonen?
+Wie kann ich ein Changelog anzeigen lassen?
+Wie kann ich ein Changelog mit Diff anzeigen lassen?
+
+SIEHE AUCH:
+  git-repositories: Repository-Verwaltung im Mud
+  git-howto: Wie git benutzt wird
+  git-workflow: Ein simples Beispiel eines Arbeitsflusses mit Git
+  git-sync: Wie die Synchronisierung zw. git-Repos und Mudlib ablaeuft
+  git-links: Verweise ins WWW
+
+10.03.2015 Amaryllis
+
diff --git a/doc/wiz/git-howto b/doc/wiz/git-howto
new file mode 100644
index 0000000..8a6acdd
--- /dev/null
+++ b/doc/wiz/git-howto
@@ -0,0 +1,111 @@
+Git-Benutzung im MorgenGrauen
+=============================
+
+0. Einleitung
+  Hier soll kurz beschrieben werden, wie man nun an die Repositories auf dem
+  Mudrechner rankommt.
+  Es wird an dieser Stelle vorausgesetzt, dass der Leser/Magier
+  grundsaetzlich weiss, was Git ist und wie es benutzt wird. Hier sollen
+  lediglich Besonderheiten im Zusammenhang mit dem MG erlaeutert werden.
+  Ebenso wird vorausgesetzt, dass der Magier Git und einen SSH-Client auf
+  seinem Rechner installiert hat.
+
+  Wer jetzt noch nix von Git weiss, sei auf die reichhaltig im Netz
+  verfuegbare Doku verwiesen: s. a. git-links.
+
+
+1. Zugriffsrechte auf Git-Repositories
+  Zunaechst muss man sich als Git-Nutzer eintragen lassen. Hierzu braucht man
+  ein SSH-Schluesselpaar, welches man z.B. mittels ssh-keygen erstellen
+  kann. Den _oeffentlichen_ Schluessel (.pub am Ende) legt man dann als
+  <magier>.pub in sein Homeverzeichnis und spricht einen EM (z.B. Zesstra)
+  an.
+
+  Mittels des Befehls
+  > ssh git@mg.mud.de info
+  kann man sich anzeigen lassen, auf welche existierenden Repositories man
+  welche Zugriffsrechte hat (R: lesen, W: schreiben). Beispiel:
+    R   W   (zesstra) d/inseln/zesstra/vulkanweg
+  In diesem Fall hat der Benutzer Lese- und Schreibrechte auf
+  d/inseln/zesstra/vulkanweg. Das Repository gehoert zesstra.
+
+  Zusaetzlich umfasst die Ausgabe auch die Zugriffsrechte aller _moeglichen_
+  (aber noch nicht existierenden) Repos.
+  Wichtig ist hier das Erstellungsrecht (C). Beispiel:
+  C  R   W  d/unterwelt/zesstra/[a-zA-Z]{1}[a-zA-Z0-9_.\-]*
+  Hier hat der Benutzer auf alle Repos unterhalb von d/unterwelt/zesstra/
+  (wobei alle diese Repos mit einem Buchstaben beginnen und ansonsten nur
+   Buchstaben, Zahlen, _, . und - enthalten duerfen) Lese-, Schreib- und
+  Erstellungsrechte.
+
+2. Ein existierendes Repository clonen
+  Dies erfolgt ganz simpel mit dem Befehl:
+  > git clone git@mg.mud.de:players/zesstra/testgebiet <zielverzeichnis>
+  Das Zielverzeichnis ist hierbei beliebig. Empfehlung: alle MG-Repos in
+  einem Verzeichnis sammeln und dort die Verzeichnisstruktur aus dem Mud
+  beibehalten:
+  > git clone git@mg.mud.de:players/zesstra/testgebiet
+    players/zesstra/testgebiet
+  Damit Aenderungen spaeter auch Euren Magiernamen tragen, geht nun bitte in
+  Euer geclontes Repo und setzt Namen und eMail-Adresse:
+  > git config user.name "Magier"
+  > git config user.email "user@example.com"
+  
+3. Ein neues Repository erstellen.
+    Voraussetzung: das Verzeichnis im Mud existiert.
+    Dies geht, wenn ihr Schreibzugriff auf das Verzeichnis im Mud habt. Legt
+    einfach in dem Verzeichnis eine Datei namens "git-mud-import" an (Inhalt
+    ist egal) und wartet bis zur naechsten vollen Stunde.
+    ACHTUNG: das Verzeichnis im Mud darf NICHT leer sein, sondern muss min.
+    eine Datei (ggf. in einem Unterverzeichnis) enthalten!
+ 
+  Anmerkungen:
+  Existiert ein Repo bereits, ist ein automatischer Import aus dem Mud nicht
+  mehr moeglich.
+  Bei einem "git clone" auf ein noch nicht existierendes Repo wird das 
+  das Repo automatisch angelegt - dieses Repo wird dann aber nicht mit
+  dem Mud synchronisiert!
+  Daher: erst (erfolgreich) importieren, dann clonen. 
+
+4. Arbeiten mit dem Repo
+  Hierzu sei zuerst einmal auf die allgemein im Netz verfuegbare Dokumentation
+  zu Git und natuerlich seine Manpages verwiesen.
+  Einen beispielhaften Arbeitsablauf findet sich in der Manpage git-workflow.
+
+5. Synchronisation mit dem Mud
+  Repos koennen bei einem 'git push' von aussen automatisch die Aenderungen
+  des master-Branches ins Mud uebertragen. Desweiteren koennen Aenderungen
+  aus dem Mud automatisch in das Repo importiert werden.
+  Auf diese Weise ist das Verwenden von FTP fast ueberfluessig.
+  Details sind in der Manpage git-sync angegeben.
+
+6. Loeschen von Repositories
+  Git-Repos, die von euch selber GEHOEREN (Schreibrechte allein reichen nicht)
+  koennen geloescht und - zumindest eine Weile auch wieder restauriert werden.
+
+6.1. Loeschen
+  > ssh git@mg.mud.de D trash players/caldra/nebelberge
+  players/caldra/nebelberge moved to trashcan.
+
+6.2. Muelleimer anzeigen
+  > ssh git@mg.mud.de D list-trash
+  players/caldra/nebelberge/2011-11-28_22:35:55
+
+6.3. Restaurieren
+  > ssh git@mg.mud.de D restore players/caldra/nebelberge/2011-11-28_22:35:55
+  players/caldra/nebelberge/2011-11-28_22:35:55 restored to
+  players/caldra/nebelberge
+
+  Es versteht sich von selbst, dass Ihr mit diesem Mittel sehr zurueckhaltend
+  umgehen solltet. Bei Missbrauch wird ggf. ein Backup eingespielt und diese
+  Moeglichkeit wieder geloescht.
+
+SIEHE AUCH:
+  git-repositories: Repository-Verwaltung im Mud
+  git-workflow: Ein simples Beispiel eines Arbeitsflusses mit Git
+  git-sync: Wie die Synchronisierung zw. git-Repos und Mudlib ablaeuft
+  git-faq: haeufig gestellte Fragen/Probleme
+  git-links: Verweise ins WWW
+
+29.01.2013 Gloinson
+Letzte Aenderung: 02.07.2014 Notstrom
diff --git a/doc/wiz/git-kooperation b/doc/wiz/git-kooperation
new file mode 100644
index 0000000..acbda6e
--- /dev/null
+++ b/doc/wiz/git-kooperation
@@ -0,0 +1,68 @@
+Erweiterung zum Git-Workflow:
+=============================
+
+Bei manchen Projekten will man mit anderen Magiern kooperieren, aber:
+* die Dateien im MUD fuer die Spieler unveraendert lassen
+* in einem ordentlichen Zweig zusammenarbeiten
+
+Dazu kann man Zweige auch remote, also im MUD erstellen. Da nur der
+'master'-Zweig in das MUD selbst synchronisiert wird, kann man ueber
+das MUD so die Repositories auf mehr als einen Computer/mehr als einer
+Person synchronisieren, ohne die Spieler mit seiner Entwicklungsarbeit
+zu behelligen:
+
+# Alternativen in/zu Schritt 4: Kooperation in einem remote Zweig.
+Falls ich mit anderen Leuten meinen Code teilen will, dieser aber nicht im
+MUD im 'master'-Zweig auftauchen (also als Dateiaenderung fuer alle Spieler
+gelten) soll, kann ich auch nur meinen Zweig selbst ins MUD schicken:
+> git checkout neue_kampftaktik
+> git push -u git@mg.mud.de:/dings/bums neue_kampftaktik
+
+Als Antwort duerfte sowas wie:
+ * [new branch]      neue_kampftaktik -> neue_kampftaktik
+dort stehen.
+
+Mit
+> git pull
+koennen wir uns diese Aenderungen am MUD-Repository holen. Der Zweig
+'neue_kampftaktik' ist jetzt ein Zweig auch im MUD und alle Leute,
+die sich jetzt das Repository /dings/bums clonen, steht genau dieser
+Zweig mit all unseren Aenderungen jetzt auch zur Verfuegung.
+
+Unser lokaler Zweig 'neue_kampftaktik' bekommt aber die Aenderungen
+an diesem Zweig anderer eventuell noch nicht ganz mit. Mit
+> git branch --set-upstream neue_kampftaktik remotes/origin/neue_kampftaktik
+sagen wir dem lokalen Zweig, dass er ab jetzt mit dem remotes-Zweig
+'neue_kampftaktik' verbunden ist.
+
+Damit bekommen wir etwaige remote-Aenderungen in diesem Zweig nach einem
+> git pull
+bei einem folgenden
+> git checkout neue_kampftaktik
+direkt mitgeteilt, eventuell in der Form:
+  Your branch is behind 'origin/neue_kampftaktik' by 1 commit, and can be
+  fast-forwarded.
+Das ist sehr einfach durch ein
+> git merge origin/neue_kampftaktik
+korrigierbar und schon koennen wir selber wieder an dem aktualisierten Zweig
+arbeiten und Aenderungen pushen. Siehe Schritt 5.
+
+Ziel einer solchen Zusammenarbeit ist natuerlich immer, irgendwann auch
+wieder den aus Schritt 4 bekannten Merge gegen den Zweig 'master' durchzu-
+fuehren, damit die Spieler was davon haben.
+Wenn wir also irgendwann diesen Merge durchgefuehrt haben und der Zweig
+'neue_kampftaktik' unnoetig geworden ist, koennen wir ihn auf der Seite
+des MUDs mit:
+> git push git@mg.mud.de:/dings/bums :neue_kampftaktik
+aufraeumen. Der einzige Unterschied zum Erstellen des Zweiges auf MUD-Seite
+ist tatsaechlich der ':' vor dem Namen des Zweigs.
+Achtung: das geht momentan (noch) nicht und auf 'master' ohnehin nie.
+
+SIEHE AUCH
+  git-repositories: Repository-Verwaltung im Mud
+  git-howto: Wie git benutzt wird
+  git-workflow: Ein simples Beispiel eines Arbeitsflusses mit Git
+  git-sync: Wie die Synchronisierung zw. git-Repos und Mudlib ablaeuft
+  git-faq: haeufig gestellte Fragen/Probleme
+
+02. Feb 2013 Gloinson
diff --git a/doc/wiz/git-links b/doc/wiz/git-links
new file mode 100644
index 0000000..02592a2
--- /dev/null
+++ b/doc/wiz/git-links
@@ -0,0 +1,38 @@
+Liste von nuetzlichen und weiterfuehrenden Links im Netz
+========================================================
+
+  * Das 'offizielle' Tutorial
+    http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html
+  * Git - SVN Crash Course (fuer Leute, die SVN kennen)
+    http://git.or.cz/course/svn.html
+  * Everyday git with 20 commands or so
+    http://www.kernel.org/pub/software/scm/git/docs/everyday.html
+    (sortiert nach Developers, Integrators, Admins)
+  * Das git Community Book (sehr empfehlenswert)
+    http://book.git-scm.com/
+  * Understanding git conceptually
+    http://www.eecs.harvard.edu/~cduan/technical/git/
+  * Git cheat sheets
+    http://help.github.com/git-cheat-sheets/
+    http://jan-krueger.net/development/git-cheat-sheet-extended-edition
+    https://github.com/AlexZeitler/gitcheatsheet/blob/master/gitcheatsheet.pdf
+    http://www.cheat-sheets.org/saved-copy/git-cheat-sheet.pdf
+    http://cheat.errtheblog.com/s/git
+  * Guides/Howtos von GitHub
+    http://help.github.com/
+  * Difference of merge and pull
+    http://longair.net/blog/2009/04/16/git-fetch-and-merge/
+  * Div. Tips und Tricks
+    http://longair.net/blog/2009/04/25/a-few-git-tips/
+  * Git fuer Windows
+    https://code.google.com/p/gitextensions/
+    (Mit Integration in den Explorer)
+  * Git-Benutzung unter Windows
+    http://rogerdudler.github.io/git-guide/
+    http://help.github.com/win-set-up-git/
+  * Git-Interna verstaendlich erklaert:
+    Git For Ages 4 And Up 
+    https://www.youtube.com/watch?v=1ffBJ4sVUb4
+
+10.03.2015 Amaryllis
+
diff --git a/doc/wiz/git-repositories b/doc/wiz/git-repositories
new file mode 100644
index 0000000..9ed7c33
--- /dev/null
+++ b/doc/wiz/git-repositories
@@ -0,0 +1,72 @@
+Git-Repositories im MorgenGrauen
+================================
+
+Die folgenden Repositories stehen fuer die Benutzung durch Magier bereit bzw.
+lassen sich durch Magier bei Bedarf (leer oder durch Import von Verzeichnissen
+aus dem Mud) anlegen:
+
+* /d/region/magier/*
+  Verzeichnisse unterhalb der Magierebene in den Regionen lassen sich in
+  git-Repos aufnehmen.
+  Anlegen: Regionsmitarbeiter, Magier mit Schreibzugriff auf den Pfad
+  Schreibzugriff: Eigentuemer (Magier), Regionsmagier dieser Region, Weise
+  Lesezugriff: s. Schreibzugriff (wegen secure/)
+
+* /p/service/magier/*
+  Anlegen: Weise, Magier mit Schreibzugriff auf den Pfad
+  Schreibzugriff: Eigentuemer (Magier), Weise
+  Lesezugriff: alle Magier (>= 20)
+  
+* /players/magier/*
+  Anlegen: Magier selber, Magier mit Schreibzugriff auf den Pfad
+  Schreibzugriff: Magier selber
+  Lesezugriff: Magier selber
+
+* /players/magier/public/*
+  Anlegen: Magier selber, Magier mit Schreibzugriff auf den Pfad
+  Schreibzugriff: Magier selber, Weise
+  Lesezugriff: jeder Magier (>= 20)
+
+* playground/magier/*
+  Spielwiese zum Testen von Magiern. Soll zum Rumspielen und Testen von Git
+  dienen.
+  Diese Repos werden NICHT mit dem Mud synchronisiert.
+  Diese Repos werden automatisch geloescht, wenn sie laenger als 14 Tage nicht
+  veraendert werden.
+  Anlegen: jeder Magier
+  Schreibzugriff: Magier selber, Weise
+  Lesezugriff: jeder Lehrling (und hoeher) (>= 15)
+
+Uebrigens geht es explizit NICHT, sein gesamtes ~ in ein Repo zu fassen.
+
+Wenn man sein kompletten Regionsverzeichnis (/d/region/magier) in ein Repo
+importiert, sind anschliessend keine einzelnen Repos unterhalb dieses
+Verzeichnisses moeglich (bzw. fuehren zu Problemem)! Ebenso kann das komplette
+Magierverzeichnis nicht mehr als Repo importiert werden, wenn es unter ihm
+schon Repos gibt.
+
+Erzmagier und Goetter haben uebrigens auf _alle_ Repositories Lese- und
+Schreibzugriff. Sie sind auch die einzigen, die in den Repositories einen sog.
+Rewind durchfuehren koennen - d.h. die Versionsgeschichte im Nachhinein
+aendern.
+Eine Beruecksichtigung von access_rights.c, ACCESS_RIGHTS etc. findet hierbei
+derzeit NICHT statt.
+
+Zum Loeschen von Repositories siehe Punkt 6 in git-howto.
+
+Ein (automatischer) Import bestehender Verzeichnisse aus dem Mud ist
+moeglich. In diesem Fall werden das so erstellte Repository und die Mudlib
+automatisch synchronisiert, wenn jemand von aussen in das Repository pusht.
+Hierbei wird _versucht_ etwaige gleichzeitige Aenderung im Git-Repo und in der
+Mudlib sinnvoll zu 'mergen' - im Falle von Konflikten ist dies nicht immer
+moeglich, weswegen Magier auf das Ergebnis solcher automatisierter Merges ein
+Auge werfen sollten.
+
+SIEHE AUCH:
+  git-howto: Wie git benutzt wird
+  git-workflow: Ein simples Beispiel eines Arbeitsflusses mit Git
+  git-sync: Wie die Synchronisierung zw. git-Repos und Mudlib ablaeuft
+  git-faq: haeufig gestellte Fragen/Probleme
+  git-links: Verweise ins WWW
+
+29.01.2013 Gloinson.
diff --git a/doc/wiz/git-sync b/doc/wiz/git-sync
new file mode 100644
index 0000000..87c5d9f
--- /dev/null
+++ b/doc/wiz/git-sync
@@ -0,0 +1,49 @@
+Synchronisation zwischen git-Repositories und Mud
+=================================================
+
+I. Push von aussen ins Mud.
+
+Am Ende es Pushes (im sog. post-update hook) wird ein Script gestartet,
+was folgendes macht:
+
+1) Wenn fuer die Synchronisation mit dem Mud aktiv ist, wird mit der Mudlib
+   gesynct. Wenn nicht: Ende
+2) Zunaechst wird in einem lokalen Clone des git-Repositories ein temporaerer
+   Branch zum Mergen von Aenderungen aus dem Mud angelegt (auto-mud-sync),
+   welcher bei dem Tag last-auto-mud-sync startet und dieser Branch
+   ausgecheckt.
+3) Mit Hilfe von rsync werden alle Aenderungen aus der Mudlib hereinkopiert.
+4) Wenn Aenderungen existieren, wird ein neuer Commit auf dem Branch gemacht.
+5) Anschliessend wird Branch master ausgecheckt.
+6) Wenn es Aenderungen gab, die in 4) commitet wurden, wird jetzt der Branch
+   auto-mud-sync mit diesem Commit in master gemergt.
+   Kommt es hierbei zu Konflikten, werden die _automatisch_ zugunsten des
+   Standes der git-Repositories aufgeloest, d.h. es gehen ggf. Aenderungen aus
+   dem Mud verloren. Es kann hierbei daher passieren, dass von 8 im Mud
+   geaenderten Zeilen nur 6 uebernommen werden, weil 2 Zeilen in Konflikt mit
+   den Aenderungen im git-Repository stehen. Daher ist es wichtig, das
+   Ergebnis dieses Merges im Nachhinein zu pruefen und ggf. zu korrigieren!
+7) Falls es Aenderungen gab, wird jetzt der in 7) erstellte Merge-Commit ins
+   git-Repository gepusht.
+8) Der jetzt gemergte Stand wird per rsync ins Mud kopiert.
+9) Das Tag last-auto-mud-sync wird aktualisiert.
+
+
+II. Automatischer regelmaessiger Commit vom Mud
+
+Jeden Tag um 05:11 wird via cronjob und das Script ~/scripts/git-autocommit
+fuer jedes Repo in ~/git-repositories/ das unter I. beschriebene Script
+~/scripts/git-sync2lib ausgefuehrt.
+
+
+III. Import von Verzeichnissen aus dem Mud
+
+Zu jeder vollen Stunde wird in allen Verzeichnissen unter /d/, /p/ und
+/players/ die Datei 'git-mud-import' gesucht. Alle Verzeichnisse, in denen
+diese existiert, werden in gitolite importiert und gleichzeitig auch ein Clone
+in ~/git-repositories/ erstellt, d.h. dass die Synchronisationsmassnahmen
+unter I. und II. fuer dieses neue git-Repository aktiv sind.
+
+LETZTE AeNDERUNG:
+05.04.2011, Zesstra
+
diff --git a/doc/wiz/git-workflow b/doc/wiz/git-workflow
new file mode 100644
index 0000000..655c76d
--- /dev/null
+++ b/doc/wiz/git-workflow
@@ -0,0 +1,151 @@
+Typischer Arbeitsablauf
+=======================
+
+(Es gibt andere Arbeitsweisen, aber dies hier ist eine, die sich bewaehrt
+ hat.)
+
+Nehmen wir an, ich moechte etwas neues einbauen, einen Bug fixen etc.
+Alle der folgenden Schritt werden auf eurem eigenen Rechner in eurem lokalen
+Clone des jeweiligen Repositories durchgefuehrt.
+
+# Schritt 1: Repository clonen und/oder updaten
+> git clone git@mg.mud.de:/dings/bums
+> git checkout master
+> git pull
+Zuerst einmal wird ein checkout des Zweiges 'master' gemacht. Und in diesen
+Zweig hol ich mir erstmal den aktuellen Stand aus dem Mud (pull).
+
+Jetzt moechte ich alle Aenderungen erstmal in einem separaten Zweig machen.
+Warum? Weil dann der 'master' (das ist der aktive Zweig im Mud!) immer in
+einem benutzbaren Zustand ist. Desweiteren kann man einen Zweig einfach
+wegwerfen, wenn irgendwas groesseres schiefgelaufen ist...
+Ausserdem ist es dann einfacher, zwischenzeitliche Aenderungen aus dem Mud zu
+integrieren.
+
+# Schritt 2: Neuen Zweig erstellen
+> git checkout -b neue_kampftaktik
+Hiermit wird  ein neuer Zweig erstellt und gleichzeitig in ihn gewechselt.
+
+Hier mach ich jetzt alle moeglichen Arbeiten und Basteleien, die ich fuer die
+neue Kampftaktik brauche. Tipps dazu:
+* Viele und haeufige Commits machen! Je kleiner einzelne Commits sind, desto
+  besser kann man Aenderungen spaeter verfolgen (was z.B. super ist, wenn
+  jemand was abnehmen muss!) und desto besser kann man einzelne Aenderungen
+  spaeter bei Bedarf auch rueckgaengig machen, wenn man merkt, dass man
+  stellenweise Unsinn gebaut hat. ;-)
+* Thematisch unterschiedliche Dinge in verschiedene Commits packen. Also zB
+  erst Syntaxfehler editieren und commiten, dann eine neue Methode fuer
+  etwas ganz andere schreiben und commiten.
+
+# Schritt 3.1: Aenderungen pruefen
+> git status
+Hiermit lasse ich mir anzeigen, welche Dateien nicht-committete Aenderungen
+enthalten (oder neu sind/geloescht wurden).
+
+> git diff
+Dies zeigt mir alle nicht-committeten Aenderungen an - zeilenweise verglichen
+mit dem vorherigen Zustand.
+
+# Schritt 3.2: Aenderungen in lokales Repository commiten
+> git add <file>                    // einzelne Files auswaehlen
+ODER
+> git add -A ./                     // alle Files auswaehlen
+Hiermit merke alle gemachten Aenderungen fuer den naechsten Commit vor.
+Ich koennte hier aber auch einzelne Dateien auswaehlen oder sogar nur
+bestimmte Teile der Aenderungen in einer Datei. (Hierzu bitte die
+Git-Doku bemuehen.)
+
+> git commit
+Hiermit erstelle ich einen Commit, der die bisherigen Aenderungen umfasst.
+Dabei wird ein Editor geoeffnet, in den ich eine informative Nachricht ueber
+meine Aenderungen hinterlassen kann. Das ist besonders wichtig, wenn ich in
+fremden Gebieten arbeite, aber auch fuer mich und einen etwaigen abnehmenden
+Magier sehr sinnvoll.
+Anregung: Die erste Zeile ist das sog. Betreff des Commits - vergleichbar mit
+dem Betreff einer eMail. Anschliessend sollte eine leere Zeile folgen und
+danach eine laengere Beschreibung eingeben werden, sofern noetig/sinnvoll.
+
+Wenn ich an diesem Punkt mit dem Bugfix oder Feature noch nicht fertig bin:
+einfach die letzten 4 Befehle aus Schritt 3 beliebig oft wiederholen, d.h.
+beliebig viele weitere Commits machen.
+
+# Schritt 4: Aenderungen in lokalen Master-Zweig mergen
+Bin ich dann schliesslich aber mal fertig, gehe ich erstmal zurueck zum
+master-Zweig:
+
+> git checkout master
+Huch! Ploetzlich sind alle Dateien auf dem alten Stand! Keine Sorge,
+unsere Aenderungen sind im Zweig 'neue_kampftaktik' sicher verwahrt.
+
+Achtung: wenn ihr mit anderen zusammen arbeitet, koennte jemand
+anderes im MUD Aenderungen vorgenommen haben. Ein einfaches
+> git pull
+um die Dateien im 'master'-Zweig auf den neuesten Stand zu bringen,
+zeigt euch auch Aenderungen. Wenn da jetzt
+  'Already up-to-date.'
+steht, ist alles in Butter, ansonsten siehe unten bei 4.1.extra.
+
+> git merge neue_kampftaktik
+Mit diesem Kommando hole ich nun alle Aenderungen aus meinem Zweig
+'neue_kampftaktik' in den Zweig 'master' (merge).
+
+# Schritt 5: Aenderungen in das MUD-Repository uebertragen
+Jetzt bin ich bereit, die Aenderungen ins Mud zu uebertragen:
+> git push 
+Job done!
+Hier kommen jetzt div. Ausgaben vom Mud, die etwas ueber den Erfolg und
+Misserfolg des Pushes sagen. ;-)
+Wenn am Ende steht
+  'Your changes were copied to the mudlib.'
+ist alles erfolgreich.
+
+Steht am Ende ein
+  'Your changes were merged successfull with changes in the mudlib and the
+   merged state was copied to the mudlib. Do not forget to pull the merge
+   commit!" 
+ist an sich auch alles gut. Aber dann gab es im Mud eben doch noch
+Aenderungen, die es nicht im Git-Repo gab, die gemerged wurden. In diesem
+Fall sollte man den aktuellen Zustand sich nochmal holen:
+> git pull
+Und dann anschauen, dieser Merge auch das richtige gemacht hat:
+> git log -p
+Hiermit kriege ich eine schoene Liste aller Commits angezeigt und -p sorgt
+dafuer, dass dabei alle Aenderungen angezeigt werden, nicht nur die
+Commit-Nachricht.
+
+# Sonderfaelle und erweiterte Moeglichkeiten
+# Schritt 4.1.extra: Zwischenzeitliche Aenderungen im MUD beruecksichtigen
+
+Es koennte sein, dass man fuer den Branch ne ganze Weile gebraucht hat -
+und dass waehrend meiner Arbeit jemand anders Aenderungen (im Mud oder
+Repo) gemacht hat.
+
+Diese Aenderungen sollte man sich wie geschrieben als erstes nach dem
+Umschalten zum master-Zweig holen:
+> git pull
+
+Jetzt geh ich wieder in meinen Zweig (ohne -b)
+> git checkout neue_kampftaktik
+und mache ein sog. Rebase. Damit verschiebe ich sozusagen, den Punkt,
+an dem mein Zweig vom 'master' abzweigt und tue so, als ob die eben
+geholten Aenderungen schon da gewesen waeren, als ich den Zweig erstellte.
+(Andere Sichtweise: ich nehme meine Zweig und setz ihn auf den aktuellen
+ 'master' dran.)
+> git rebase master
+
+Der Vorteil ist: wenn jetzt was kaputt gegangen ist, es also Konflikte gibt,
+dann gibt es die nur in meinem Zweig 'neue_kampftaktik' und dort kann ich
+sie in aller Ruhe reparieren. Sowohl der 'master' im MUD als auch mein
+lokaler 'master' sind intakt.
+
+Und jetzt geht es wie oben weiter.
+
+SIEHE AUCH
+  git-repositories: Repository-Verwaltung im Mud
+  git-howto: Wie git benutzt wird
+  git-kooperation: Ein ueber git-workflow hinausgehendes Beispiel zur
+                   Synchronisation bzw Kooperation mehrerer Magier/Rechner
+  git-sync: Wie die Synchronisierung zw. git-Repos und Mudlib ablaeuft
+  git-faq: haeufig gestellte Fragen/Probleme
+
+02. Feb 2013 Gloinson
diff --git a/doc/wiz/hausbau b/doc/wiz/hausbau
new file mode 100644
index 0000000..6c9c2a5
--- /dev/null
+++ b/doc/wiz/hausbau
@@ -0,0 +1,40 @@
+Hilfeseite fuer Magier: Hausbau im MorgenGrauen
+-----------------------------------------------
+
+Als Magier wird man desoefteren von Spielern gebeten, Ihnen beim
+Hausbau behilflich zu sein. Diese Bitte erfolgt meistens als
+
+1) 'Kannst Du hier in dem Raum wo ich bin, es mal so einrichten,
+    dass ich mein Haus abstellen und giessen kann'
+
+und 
+
+2) 'Ich moecht hier gerne mein Haus abstellen, aber es soll un-
+    sichtbar sein, eine andere Kurzbeschreibung haben und man 
+    soll es nicht mit <betrete> betreten koennen, oder .... '.
+
+
+Fuer 1) ist es relativ einfach. Man begebe sich als Magier in den 
+Raum, zuecke sein MG-Tool und tippe ein 
+
+              'xcall $h->SetProp(P_HAUS_ERLAUBT,1)'.
+
+Dann fordere man den Spieler auf, sein Haus abzustellen und gies-
+sen und setze diese Property wieder auf 0.
+
+ACHTUNG: Das Abstellen eines Hauses muss unbedingt vorher mit dem 
+-------  Magier abgesprochen werden, dem dieser Raum gehoert! Bei 
+         inaktiven Magier ist dies der zustaendige Regionsmagier.
+
+Bei 2) verfaehrt man, wie fuer den Fall 1) geschildert. Jedoch sind 
+dazu noch andere Dinge notwendig. Da diese den Rahmen einer Hilfe-
+seite sprengen, liegt unter 
+
+                       /doc/beispiele/misc/seherhaus.c
+
+ein File, in dem alles ausfuehrlich dokumentiert ist.
+
+SIEHE AUCH: P_HAUS_ERLAUBT
+
+------------------------------------------------------------------------------
+LETZTE AeNDERUNG: Sam, 19.05.2001, 23:00:00 von Tilly
diff --git a/doc/wiz/heilung b/doc/wiz/heilung
new file mode 100644
index 0000000..3d1b6c8
--- /dev/null
+++ b/doc/wiz/heilung
@@ -0,0 +1,156 @@
+Heilung von Spielern durch Objekte, Raeume, portable Heilung
+============================================================
+
+                               Generelles:
+                               **********
+
+Neben den bekannten Heilstellen fuer Spieler (den Kneipen sowie gildeninterne
+Faehigkeiten) gibt es noch die Moeglichkeit, den Spielern Heilung durch Ver-
+wendung der LFUNs "heal_self", "restore_hit_points", "restore_spell_points",
+"buffer_hp" und "buffer_sp" Heilung zukommen zu lassen.
+
+Dies wird meist ueber Raeume gemacht, in denen der Spieler ein bestimmtes
+Kommando ausfuehren muss oder ueber Objekte, die der Spieler mit sich tragen
+kann ('tragbare Tanken'), um Heilung zu erfahren.
+
+ES WIRD EMPFOHLEN, jede mobile Heilung ueber "/std/food" zu implementieren.
+Dort muessen lediglich ein paar Properties gesetzt werden, um sicherzugehen,
+dass diese Heilungsregeln eingehalten werden.
+Gleichzeitig wird auch sichergestellt, dass z.B. Props von Containern, die
+Heilung enthalten, nach Leerung korrekt gesetzt werden.
+
+Neben diesen Moeglichkeiten gibt es auch noch Enttankungen, also die
+Moeglichkeit, im Spieler eine der Properties P_DRINK, P_FOOD oder P_ALCOHOL
+ueber die LFUNs "defuel_food/drink" oder "reduce_food/drink/alcohol" zu
+mindern. Dies ist eine Form der Heilung, da der Spieler danach wieder
+regulaer essen und trinken kann. Sie muss allerdings ortsgebunden sein.
+
+Grundsaetzlich kann eine Heilstelle natuerlich auch Schaden in einem Spieler
+verursachen. Das macht dann Sinn, wenn er z.B. der - fuer diese Heilstelle -
+'falschen' Gilde oder Rasse angehoert. Dabei MUSS fuer den Spieler aber
+vorher deutlich erkennbar gewesen sein, dass diese 'Heilstelle' fuer ihn
+nicht geeignet ist.
+
+                              Spezifisches:
+                              ************
+
+Bei jeder Form von Heilung MUSS "eat_food" (bei Essen) oder "drink_soft" (bei
+Trinken) und ggf. "drink_alcohol" verwendet werden! Diese Funktionen sorgen
+fuer einen unkomplizierten Ablauf der Heilung, da sie pruefen, ob der Spieler
+noch genuegend Tankkapazitaeten hat und wenn ja, die entsprechenden Properties
+(P_DRINK, P_FOOD oder P_ALCOHOL) automatisch raufsetzen.
+
+"drink_alcohol" ist in diesem Zusammenhang besonders wichtig, da es auf einen
+evtl. vorhandenen Saufskill prueft!
+
+Die Heilung selber muss dann aber noch ueber "heal_self", "restore_hit_points",
+"restore_spell_points", "buffer_hp" oder "buffer_sp" geschehen!
+
+Ortsgebundene Heilung:
+---------------------
+Kneipen: Hier ist klar, dass, je hoeher die Heilung ist, umso teurer die
+         Heilung sein muss. Ausserdem MUSS "buffer_hp" verwendet werden.
+         Ausnahme hiervon ist die Kneipe bei den Eistrollen im Warok.
+         Mit dem Pubtool ("/obj/tools/pubtool") kann man pruefen, ob die
+         Werte der Kneipe in Ordnung gehen.
+
+Raeume:  Es gibt viele ortsgebundene Heilungsstellen im MG, welche in erster
+         Linie dazu da sind, den Spielern *in diesem Gebiet* eine Tankmoeg-
+         lichkeit zur Verfuegung zu stellen (-> Drakonien). Hierfuer sollte
+         man die Property P_LAST_XP benutzen, was aber allerdings keine
+         Pflicht ist, wenn man moechte, dass auch "gebietsfremde" Spieler
+         hier tanken gehen koennen (-> SSP). Dann wiederum sollte man aber
+         darauf achten, dass sie nicht zu gut sind, u.U. schwer zu erreichen
+         sind (-> T'emong), Blockmonster den Weg versperren (-> SSP) etc.
+
+         In jedem Fall aber MUSS eine Begrenzung der Tankmoeglichkeit sicher-
+         gestellt werden; entweder erfolgt die Limitierung durch Reset oder
+         durch Zeitbegrenzung (-> check_and_update_timed_key).
+
+         "eat_food" bzw. "drink_soft" MUSS bei den Tanken gesetzt werden, die
+         den Spieler darauf schliessen lassen, Nahrung aufgenommen zu haben
+         (-> "iss beeren", "trinke schleim", etc.).
+         Bei allen anderen (-> "rieche blume", etc.) sollten Qualitaet und
+         Quantitaet im Rahmen bleiben.
+         Diese ortsgebundenen Heilungen duerfen Instant-Tanken sein.
+
+Tragbare Heilung:
+----------------
+Tragbare Heilung sollte nach max 5 Resets verderben, die Wirkung vermindern
+oder verloren gehen. Sie sollte nicht beliebig im Spieler oder anderswo
+hortbar sein. Eine begrenzte Anzahl pro Reset kommt hierbei auch immer gut
+(gute Beispiele hierfuer: Drops von Twingi, Heilblaetter von Zook).
+
+Richtwerte fuer Aufloesung oder Wirkungsminderung von tragbarer Heilung:
+
+                        Heilung         Reset-Zeit
+                          > 200    :     30- 60 min
+                      150 - 200    :     60-120 min
+                      100 - 150    :     90-180 min
+                       50 - 100    :    120-240 min
+                          <  50    :    150-300 min
+
+Diese Richtwerte sind gute Anhaltspunkte, koennen aber bei Bedarf mit dem
+Erzmagier der Heilungs-Balance individuell abgestimmt werden.
+
+'Wirkungsminderung' ist hierbei im Sinne von *deutlich* gemeint.
+
+*** NEU - NEU - NEU - NEU - NEU - NEU - NEU - NEU - NEU - NEU - NEU - NEU  ***
+
+   Tragbare Heilungen, die im weitesten Sinne aus Lebensmitteln bestehen,
+  duerfen nicht mehr instant sein, sondern MUESSEN "buffer_hp/sp" verwenden!
+
+*** NEU - NEU - NEU - NEU - NEU - NEU - NEU - NEU - NEU - NEU - NEU - NEU  ***
+
+Bei tragbaren Heilungsmoeglichkeiten, die weder aus Essbarem, noch aus Trink-
+barem besteht (bei denen es also nicht logisch waere, "buffer_hp/sp" zu
+benutzen), MUSS "check_and_update_timed_key" verwendet werden, da sie sonst zu
+kritisch werden. Dann darf es natuerlich auch sowas geben. Beispielsweise kann
+man solche Objekte gut als (Mini-)Questbelohnung integrieren.
+
+Hier gilt: Je hoeher die Heilung, um so hoeher die Zeitdauer, nach der der
+Spieler diese Moeglichkeit wieder in Anspruch nehmen darf.
+(Richtwerte: heal_self(50): ~ 1800 sec., heal_self(150): ~ 3600 sec.)
+
+Auch bei diesen Objekten ist es ein MUSS, die Heilungsmoeglichkeiten einmal
+erschoepfen zu lassen, sie also entweder 'verderben' oder sich verabschieden
+(-> Dschinn) zu lassen oder sie 'stillegen', da sie dann einfach eine zeitlang
+Ruhe brauchen, um sich neu 'aufzuladen'.
+
+Questbelohnungen:
+----------------
+Diese Objekte stellen eine Ausnahme unter den tragbaren Heilungen dar. Hier
+kann auf "buffer_hp/sp" verzichtet werden; dafuer muessen aber andere Regeln
+beachtet werden: das Objekt darf pro Reset nur begrenzt heilen bzw. nach der
+Anwendung sich selber zerstoeren (-> gelber Stein). Oder es muss vorher eine
+erhebliche Menge LP/KP investiert werden, um das Objekt zu nutzen
+(-> Infernoblock).
+
+Enttanken:
+---------
+Die Moeglichkeit zur Enttankung MUSS an einen festen Ort gebunden sein. Sie
+darf also nicht durch tragbare Objekte hervorgerufen werden koennen. Ein
+Beispiel hierfuer sind die Toiletten von Wurzel in Wilhelmsburg oder die
+Fleischreste in der SSP. Weiteres hierzu siehe -> "man enttanken".
+
+                                 Logisches:
+                                 *********
+
+Jede (!) Moeglichkeit zur Heilung, abgesehen von regulaeren Kneipen, muss dem
+zustaendigen Magier fuer Heilungs-Balance gemeldet und von diesem genehmigt
+werden. Wer diesen Posten momentan innehat, kann dem MailAlias
+"heilungs_balance" entnommen werden.
+
+Siehe auch:
+----------
+     Tanken:    consume, drink_alcohol, eat_food, drink_soft
+     Heilung:   heal_self, restore_spell_points, restore_hit_points, 
+                buffer_hp, buffer_sp
+     Timing:    check_and_update_timed_key
+     Enttanken: defuel_drink, defuel_food
+     Props:     P_DRINK, P_FOOD, P_ALCOHOL, P_SP, P_HP,
+                P_DEFUEL_TIME_DRINK
+     Konzepte:  enttanken, food
+
+----------------------------------------------------------------------------
+17.09.2010, Zesstra
diff --git a/doc/wiz/kampfobjekte b/doc/wiz/kampfobjekte
new file mode 100644
index 0000000..39d6e28
--- /dev/null
+++ b/doc/wiz/kampfobjekte
@@ -0,0 +1,60 @@
+ SONSTIGE KAMPFOBJEKTE:
+
+    Hierzu zaehlen alle Sachen, die irgendeinen Einfluss auf den Kampf nehmen
+    und weder `/std/weapon.c' noch `/std/armour.c' sind. Also die sogenannte
+    Artillerie, Eisstab und artverwandtes, Wurfsterne, Parasteine, Bumis...
+    sowie saemtliche tragbaren Heilmoeglichkeiten und unterstuetzende NPC.
+
+    Prinzipiell sind alle diese Sachen genehmigungspflichtig.
+    Auto-Selbstfahrer, also Sachen, die ohne Eingriff des Spielers agieren,
+    sind unerwuenscht und sollten vermieden werden. Sorry fuer die
+    Laggeplagten...
+
+    Kampfobjekte sollten P_ATTACK_BUSY setzen und auch vor der Anwendung
+    abfragen, damit sie nicht unbegrenzt hintereinander genutzt werden
+    koennen. Magische Sachen sollten entweder aufgeladen werden muessen oder
+    ihre magische Energie verlieren, damit sie nicht unendlich haltbar sind.
+    Ausserdem hat Magie normalerweise auch unerwuenschte Nebenwirkungen. Die
+    Spieler sollen die moeglichen Vorteile gegen Nachteile oder Nebenwirkungen
+    des Gegenstands abwaegen. Auch bei diesen Objekten *muss* P_INFO gesetzt
+    werden, um eine Begruendung fuer die besondere Wirkung und z.B. einen
+    Hinweis auf die Benutzung zu liefern.
+    Sie sollten keinesfalls kaeuflich erworben werden koennen.
+
+    Tragbare Heilstellen sollten aeusserst spaerlich verwendet werden. Handelt
+    es sich um irgendeine Form von Fressalien (Brot, Pfirsich,
+    Schnellhaerter...) so muss P_FOOD oder P_DRINK und evtl. P_ALCOHOL gesetzt
+    werden. Ansonsten sollten solche Dinge wenigstens irgendwelche
+    Auswirkungen auf den Spieler haben ausser der Heilung. Ein sogenanntes
+    "Restrisiko" waere nicht verkehrt...
+
+    Sie sollten niemals unbegrenzt erworben werden koennen, schon gar nicht
+    kaeuflich! Ausserdem ist eine gewisse Verfallszeit sehr sinnvoll und
+    erhoeht die Chancen auf Genehmigung. Richtwerte: erhoehtes Risiko nach 1.
+    Reset, unbrauchbar nach 2-3 Resets.
+
+    Objekte, die Auskunft ueber ein- und/oder ausgehende Schaeden geben, 
+    sind genehmigungspflichtig. Solche Objekte sollten nach Moeglichkeit
+    eine Waffe oder Kleidungsstueck - dies jedoch nicht AT_MISC - sein.
+    Dass solche Objekte auch ueber Luecken in der Angabe der Schaeden und
+    Nachteile verfuegen sollten, ist eigentlich klar. 
+    Solche Nachteile liessen sich ueber eine Mindest-Schadenshoehe oder
+    random realisieren. Informationen ueber mehrere Schadenstypen durch 
+    ein und das selbe Objekt sind nicht erwuenscht.
+
+    Ebenfalls genehmigungspflichtig sind alle Objekte, die in irgend einer
+    Weise Einfluss auf spezifische Gildenfaehigkeiten oder Besonderheiten
+    nehmen. Hierzu sollte neben der Balance auch noch der Gildenmagier
+    befragt werden. 
+    
+    Hinsichtlich Komponenten der Zauberer ist der Gildenchef der
+    Ansprechpartner.
+    
+ SIEHE AUCH
+
+     balance, ruestungen, waffen, fernwaffen, uniques, npcs, objects,
+     attributsveraenderungen, resistenzen, grenzwerte, artillerie
+
+------------------------------------------------------------------------------
+ LETZTE AeNDERUNG:
+    So, 26.01.2003, 18:50:00 von Humni
diff --git a/doc/wiz/katzenkrieger.testspieler b/doc/wiz/katzenkrieger.testspieler
new file mode 100644
index 0000000..33c2ac9
--- /dev/null
+++ b/doc/wiz/katzenkrieger.testspieler
@@ -0,0 +1,37 @@
+Magier-Manpage
+
+Gildentestie "Katzenkrieger".
+
+
+Es gibt in der Katzenkriegergilde eine Funktion, mit der man einen
+Testspieler erschaffen kann, welcher der Katzenkriegergilde angehoert.
+Die Funktion heisst "make_testplayer" und erwartet als einzigen Parameter
+ein Spieler-Objekt.
+
+
+
+Die Funktion verhaelt sich wie folgt.
+
+Der Spieler muss ein Testspieler sein.
+Der Spieler sollte in die Gilde eintreten koennen.
+Der Spieler lernt alle Spells und Skills der Gilde, die er auf seinem
+derzeitigen Spielerlevel lernen kann.
+Die Spells haben je den hoechstmoeglichen Skillwert, den der Spieler auf
+seinem Level erreichen kann, es sei denn, er kannte den Spell zuvor schon.
+Dann bleibt der Spell unveraendert.
+
+
+
+Um einen Testspieler zu erschaffen, geht man wie folgt vor.
+
+Man nehme einen (eigenen!) Testspieler, z.B. "bambs".
+Man setze ihm das gewuenschte Spielerlevel, z.B. 11
+  xcall bambs->SetProp(P_LEVEL, 11);
+Man gehe in den Gildenraum der Katzenkrieger.
+  goto /gilden/katzenkrieger
+Man rufe die Funktion auf, die einen Katzenkrieger aus dem Testspieler macht.
+  xcall $h->make_testplayer(find_player("bambs"));
+Der Testspieler sollte nun Katzenkrieger sein.
+
+******************************************************************************
+Letzte Aenderung: Tue Jan  7 23:03:16 2003 durch Bambi
\ No newline at end of file
diff --git a/doc/wiz/knete b/doc/wiz/knete
new file mode 100644
index 0000000..e4d1f3e
--- /dev/null
+++ b/doc/wiz/knete
@@ -0,0 +1,81 @@
+NAME:
+		*** KNETE ***
+
+BESCHREIBUNG:
+	Die Knete ist ein in LPC geschriebener Lisp-Interpreter. Dieser hat
+	die besondere Eigenschaft, die Lisp-Quellen direkt in den vom 
+	Amylaar GameDriver angebotenen closures zu uebersetzen. Dadurch ist
+	mit der Knete alles das moeglich, was auch mit LPC moeglich ist.
+	Es ist ein generisches, frei programmierbares Hilfsmittel.
+
+	Die Knete befindet sich unter: "/obj/tools/lisp"
+
+FUNKTIONSBESCHREIBUNG:
+	Die Knete kann im Prinzip fast alles, was ein einfacher Lisp-
+	Interpreter kann. Ausnahmen sind Tupel (Bsp: (1 . 2)), welche nicht
+	implementiert sind. Die Grundlegenden Funktionen, wie define, setq,
+	cons, cdr, car etc werden beim Laden der Knete angezeigt. Je nach
+	Zeit, werden eventuelle auch weitere Standardfunktionen hinzu-
+	kommen, um die Knete moeglichst common-lisp kompatibel zu machen.
+
+BENUTZUNG:
+	Zu allererst sollte man wissen, dass dies hier keine Einfuehrung in
+	Lisp ist oder sein soll! Die wichtigsten Merkmal der Knete werden
+	aufgefuehrt an einigen kleinen Beispielen. Wer Lisp kennenlernen
+	moechte kann dies mit den handelsueblichen Buechern tun.
+
+	Wer dennoch basteln moechte: Lisp ist eine Sprache in Prefixnotation,
+	d.h. der Funktionsname steht immer als erstes und dann kommen die
+	Argumente. Ein Ausdruck ist mit den ihn umgebenden Klammern komplett.
+	  Beispiel: (+ 1 1) ;;; errechnet die Summe aus 1 und 1
+	Solche Klammerausdruecke koennen beliebig geschachtelt werden.
+	  Beispiel: (+ 1 (+ (+ 1 1) 1))
+
+	Es stehen alle efuns, sowie einige lfuns zur Verfuegung! Zu den efuns
+	gehoeren auch +,-,*,/,%,!,[,[<,[<.. etc, also alle Operatoren von LPC.
+
+	Die Knete hat zwei Modi:
+		a) Laden von Lisp aus einer Datei
+		b) Verarbeiten von Eingaben aus der Kommandozeile
+
+	Zu a)
+	  Mit der Funktion "load" koennen Dateien eingelesen und interpretiert
+	  werden. Die Funktion hat nur ein Argument, und das ist der Dateiname.
+
+	  Beispiel: (load "/players/hate/lisp.l")
+	
+	Zu b)
+	  Wenn die Knete gecloned wurde, koennen jederzeit Lispausdruecke
+	  eingegeben werden, welche dann interpretiert werden. Dabei sollte
+	  beachtet werden, dass die aeusserste Klammer nicht notwendig ist!
+
+	  Beispiel: (+ 1 (+ 1 1)) koennte man auch schreiben als:
+	            + 1 (+ 1 1)
+
+	  Dies ist vor allem dann interessant, wenn man die Moeglichkeiten
+	  der Knete als alias-Werkzeug in betracht zieht. Somit ist es
+	  moeglich, eine Funktion zu schreiben und diese dann wie ein alias
+	  zu benutzen.
+
+	  Beispiel: (defun who () (users)) ;;; gibt das users array aus
+	             Jetzt muss nur noch who eingegeben werden und das
+		     array wird ausgegeben.
+
+	  Da Lisp wesentlich komplexere Ausdruecke ermoeglicht, als der
+	  normale alias-Interpreter, liegen die Vorteile auf der Hand.
+
+KONFIGURATION:
+	Die Knete laesst sich fuer jeden Nutzer konfigurieren, indem sie eine
+	Datei "/players/<name>/lisp.l" aus dessen Heimatverzeichnis laedt.
+
+	Ein Beispiel, was man damit machen kann befindet sich unter:
+	  "/players/hate/lisp.l"
+	Die MUD Spezifischen Teile und Abfragen koennen ignoeriert werden.
+
+BUGS:
+	Es gibt momentan noch ein Problem, wenn auf der Kommandozeile
+	Klammern fehlen. Dann kommt eine Meldung: "*Missing 2 )"
+	In diesem Fall einfach zweimal hintereinander ) auf jeweils einer
+	einzelnen Zeile eingeben! Dieses Problem tritt vor allem dann auf,
+	wenn man zum Beispiel ein :( eingibt.
+
diff --git a/doc/wiz/laden b/doc/wiz/laden
new file mode 100644
index 0000000..2de2ea3
--- /dev/null
+++ b/doc/wiz/laden
@@ -0,0 +1,15 @@
+LAEDEN IM MORGENGRAUEM
+----------------------
+
+Laeden im MorgenGrauen sollten grundsaetzlich /std/shop erben.
+
+Des Weiteren benoetigt man einen StorageRoom, also einen Raum, in dem der
+Laden seine Gegenstaende verwaltet. Dieser wird mit "SetStorageRoom"
+gesetzt. Die Hilfeseite zu diesem Befehl hat auch ein Beispiel.
+
+Objekte, die der Laden immer verkaufen soll, kann man mit "AddFixedObject"
+setzen.
+
+Siehe auch: SetStorageRoom, AddFixedObject, QuerySellValue
+
+Letzte Aenderung: Humni, 2015-06-17
diff --git a/doc/wiz/leitfaden b/doc/wiz/leitfaden
new file mode 100644
index 0000000..7d806c1
--- /dev/null
+++ b/doc/wiz/leitfaden
@@ -0,0 +1,487 @@
+
+
+ACHTUNG: DIESER LEITFADEN IST FURCHTBAR VERALTET! :-(
+
+
+DER LEITFADEN 'WIE SCHLIESSE ICH ERFOLGREICH MEIN GEBIET AN'.
+(in sieben Schritten zum Erfolg, von Tsunami und Feuerwehr)
+-------------------------------------------------------------
+
+Dieser Leitfaden ist absolut unverbindlich und kein Reglement. Die 
+Reihenfolge muss nicht eingehalten werden, und auch die Anmerkungen 
+zu den einzelnen Punkten sind nicht zwingend zu befolgen.
+
+Fuer Schnellleser:
+
+1) Raeume/Objekte/NPCs proggen
+2) Alles Testen und Mail an die Regionsmagier (RM) schicken.
+3) RMs pruefen Code und weisen auf moegliche Probleme hin.
+4) Antrag Balance verfassen ('goto /players/paracelsus/office').
+5) Forscherpunkte beantragen (hilfe erzmagier).
+6) Mail an RMs, dass nun alles i.O. ist.
+7) RMs schliessen Gebiet an.
+
+Nun etwas ausfuehrlicher.
+
+-------------------------------------------------------------
+P H A S E 1
+-------------------------------------------------------------
+Du bist nun also Magier, voller Ideen und Tatendrang. Bevor Du wild drauflos
+programmierst, ein paar Tips, um Dir und den zustaendigen RMs das Leben zu 
+erleichtern:
+
+Fertige Dir eine ASCII-Karte an, und schreibe einen kurzen Text, der Dein 
+neues Gebiet beschreibt. Frage schon mal Deinen RM, wo ungefaehr dieses
+neue Gebiet angeschlossen werden koennte. So kannst Du Dir ein paar Gedanken
+machen, wie die Anschlussraeume ausschauen koennten.
+
+Erstelle sinnvolle Unterverzeichnise: es hat sich folgendes bewaehrt:
+Ein Hauptverzeichnis mit dem Namen des Gebietes (kurz, wenns geht). Dort
+kommen dann die Unterverzeichnise rein: z.B. '+/doc +/mon +/obj +/rooms'. 
+In das '+/doc' kommen dann Deine Karten und Texte. Falls Du mit 
+'NotifyPlayerDeath()' Deine Kerben loggen willst, oder andere wichtige 
+Sachen loggen moechtest, dann schreibe diese Files in das '/log/<deinname>/' 
+Verzeichnis. 
+In das '+/mon' Verzeichnis kommen alle NPCs usw.
+
+Wenn Du ein Textfile aufrufen willst, dass den Inhalt der Unterverzeichnise
+beschreibt, so lege einfach eine '.readme' Datei im entsprechenden 
+Verzeichnis an. Dieses File wird bei jedem 'cd' aufgerufen.
+
+Bevor Du jetzt losschlaegst, ueberlege Dir ein Define-File, worin Du die
+ganzen Pfade definierst. Denn wenn Du zuerst alles in Deinem Player-
+Verzeichnis proggst, und vor Anschluss wird das Gebiet in das Regions-
+verzeichnis gemoved, will niemand die ganzen Pfadnamen abaendern.
+Ein Beispiel: in Dein 'gebiet.h' (gib ihm einen praktischen Namen der zu 
+Deinem Gebiet passt: eismine.h, hoehle.h oder keller.h usw) kaeme sowas 
+in der Art:
+
+#define EISMINE(x) ("/d/polar/tsunami/eismine/"+x)
+#define ROOM(x) (EISMINE("rooms/"+x))
+#define MON(x) (EISMINE("mon/"+x))
+#define OBJ(x) (EISMINE("obj/"+x))
+
+Dein Raum included dann das File: #include "../gebiet.h"
+Dein Ausgang in einen anderen Raum machst Du dann folgendermassen:
+AddExit("norden", ROOM("raum02"));
+
+Ein NPC wuerde somit folgendermassen in den Raum gestellt:
+AddItem(MON("eiself"), REFRESH_DESTRUCT);
+
+Wenn spaeter aus irgend einem Grund Dein Gebiet verschoben werden muss, dann
+kann der RM einfach die erste Zeile in 'gebiet.h' abaendern. Das spart Zeit
+und Nerven.
+
+Weiter bietet das Define-File die Moeglichkeit, laengere Befehle zu
+verkuerzen. Ein Beispiel: Du moechtest Namen des Spielers (gross geschrieben)
+ausgeben. Der Befehl dazu waere: this_player()->Name(WER)
+Das ist ziemlich lang. Definiere in Deinem Define-File doch einfach:
+#define TPN this_player()->Name(WER)
+und schon wird Dein Code etwas uebersichtlicher. Die Anwendung waere z.B.
+say(TPN+" popelt in der Nase.\n");
+
+Wenn Du automatische Zeilenumbrueche moechtest, dann schreibe folgendes
+in Dein Define File:
+#define BS(x) break_string(x, 78 , "", BS_LEAVE_MY_LFS)
+BS_LEAVE_MY_LFS bewirkt, dass Du immer noch selbst Zeilenumbrueche einbauen
+kannst mit \n. Und so wird BS dann benutzt:
+AddDetail( ({"wand","waende"}),BS(
+  "Die Waende bestehen wie alles hier aus purem Saphir. In die noerdliche "+
+  "Wand wurde ein schrecklich gezacktes und scharfkantiges Loch gesprengt."));
+
+Um dies zu benutzen musst Du allerdings noch #include <break_string.h> in das 
+Headerfile setzen (vor die #define BS(x)...)-Zeile), sonst kennt der 
+Precompiler den Ausdruck BS_LEAVE_MY_LFS nicht.
+
+Du wirst Dein Gebiet evtl. in Deinem Home-Verzeichnis (/players/...)
+schreiben, da Du noch nicht den Sprung zu Level 21 geschafft hast. Du
+solltest aber wissen, dass Deine RegionsmagierInnen nur Gebiete annehmen
+werden, welche im Regionsverzeichnis /d/.../dein_name/ gespeichert sind. Das
+ist wichtig, denn in Deinem Heim-Verzeichnis koennen sie nicht schreiben und
+so keine Fehler korrigieren. Denke daran! Benutze unbedingt die Defines,
+denn vor dem Anschluss wirst Du das Gebiet in das Regionsverzeichnis
+schieben muessen.
+
+Viele Spieler 'loggen', also speichern gerne Tode oder Aktionen von Spielern
+in ihren Heim-Verzeichnissen. Dieses Schreiben funktioniert nicht von
+Regionsverzeichnissen in Heim-Verzeichnisse. Benutze auch hier ein define!
+
+Nun kommen wir zum proggen. Falls Du noch nie programmiert hast, ist es
+sinnvoll, sich Beispielfiles anzuschauen. Wuehl Dich durch das '/doc'
+Verzeichnis oder noch besser: frag Deinen Papimagier/Deine Mamimagierin oder
+den RM, ob er Dir ein paar seiner Files zuschickt. Hast Du diese Huerde
+ueberwunden, kann es losgehen. Wenn Du anfaengst zu proggen und nicht weisst, 
+wie man einen bestimmten Befehl benutzt, kannst Du die 'man page' konsultieren 
+(das Manual = Online Hilfe). Tipp einfach 'man <befehl>' und schon kommt die 
+entsprechende Hilfeseite, falls vorhanden.
+
+Ein paar Worte zu den Raeumen: Details sind hier sehr wichtig.
+Mach viele Details und beschreibe sie nett. 'unt boden' -> 'Der ist unter Dir.'
+ist nicht sehr interessant. Ein Raum mit weniger als 20 Details wird
+mittlerweile als ungenuegend angesehen, auch wenn es noch ein paar Gebiete
+im MG gibt, wo die Raeume fast keine Details haben (diese Zahl schwankt
+natuerlich von Region zu Region -- trotzdem: je mehr Details, desto besser). 
+AddSmells und AddSounds solltest Du ebenfalls nicht vergessen. Der Spieler 
+von Welt will auch riechen und hoeren koennen. Wenn Du einen Raum erstellst, 
+ueberlege Dir auch witzige und/oder sinnvolle Befehle, die man in dem Raum 
+ausfuehren kann. Bei laengeren Prozeduren ist es fuer den RM hilfreich, wenn 
+Du im jeweiligen Raum einen kurzen Kommentar absetzt.
+
+Schau, dass Du ein atmosphaerisch schoenes Gebiet programmierst. Schau,
+dass es sich mit seiner Umgebung vertraegt. Bevor Du ein abgestuerztes
+Raumschiff von der Vega programmierst, frage lieber Deinen RM, denn
+wahrscheinlich passt es thematisch nicht in seine Region (welche Region auch
+immer es ist :-)).
+
+Achte darauf, dass die Toedlichkeit Deines Gebiets sich mit Deiner Umgebung
+vertraegt. Ueber-Schwere-Mega-Monster sollten von kleinen Spielern nicht
+problemlos erreicht werden koennen. Benutze nicht ganz so toedliche
+'Block-Monster', welche den Zugang versperren. So muss ein Spieler erst
+seine Wuerdigkeit beweisen, bevor er an die harten Monster kommt. Tigonen im
+Glockenwald sind KEINE gute Idee.
+
+Viele Magier finden es lustig, Spieler in ihren Gebieten durch Fallen
+sterben zu lassen. Das sind z.B. Knoepfe, die man drueckt, und die dann sofort
+this_player()->die() aufrufen. Nachdem vor einiger Zeit die Todesfolgen
+verschaerft wurden, sind derartige Fallen nicht mehr gern gesehen. Vermeide
+sie wenn moeglich. Spieler machen jeden Muell, aber lass sie nicht dafuer
+sterben, wenn Du es anders regeln kannst. Jede Todesfalle ist dem RM
+aufzuzueigen.
+
+Die Objekte: dazu gehoeren auch Waffen und Ruestungen, sowie alle anderen
+schraengen Dinge, die Dir noch so einfallen werden. Bevor Du Dir Deine
+erste Mega-Super-Alles-Kaputtschlag-Axt oder den Alle-Schaeden-Abwehr-Panzer
+zusammenbastelst, setz Dich hin, atme ruhig durch und tipp dann 'man balance'.
+Dort stehen alle wichtigen Eckdaten, die Du befolgen solltest. Und denk daran,
+nicht zuviele exklusiven Waffen und/oder Ruestungen in einem einigen Gebiet.
+Wenn Du grosse Objekte schreibst, ist hier auch wieder ein kleiner Kommentar
+im File sehr hilfreich fuer den RM. Denk daran, Du bist vielleicht irgendwann
+einmal nicht mehr erreichbar und dann ist es gut, wenn im File geschrieben
+steht, was das Programm ungefaehr macht.
+
+Jetzt kommen wir zu ein paar haeufigen Problemen:
+Erzeugst Du ein Objekt, das in einen Spieler geschoben wird, musst Du 
+unbedingt den Return-Wert abfragen, ob der Spieler das noch tragen kann:
+Ein Beispiel: Eine eigene Funktion 'nehmen'.
+In das create() vom Raum kommt:
+AddCmd(({"nimm","nehm","nehme"}),"nehmen");
+
+Die Funktion 'nehmen' erzeugt eine Zigarre. Sie prueft, ob einen Zigarre
+schon im Raum liegt, dann nimmt der Spieler diese. Falls keine Zigarre
+rumliegt, wird eine neue erzeugt und in den Spieler geschoben. 
+Damit ein Spieler nicht hunderte Zigarren erzeugen kann, wurde eine
+globale Variable 'int zaehler; zaehler=5; oben im create() vom Raum erstellt. 
+Nach einem Raumreset steht der Zaehler wieder auf 5, wenn man das in der
+reset() Funktion (siehe unten) definiert.
+
+nehmen(string str)
+{
+  object ob;
+
+   notify_fail("Was moechtest Du nehmen?\n");
+   if(!str) return 0;
+   if(str=="zigarre") {
+   if(present("zigarre",this_object())) return 0; 
+   // so wird die Zigarre IM Raum genommen
+
+     if (zaehler) {   
+       write(BS("Du nimmst eine Zigarre aus dem Fach."));
+       say(TPN+" nimmt eine Zigarre aus einem Fach.\n",this_player());
+       // TPN: siehe oben, Abschnitt 'Defines'
+       ob=clone_object(OBJ("zigarre"));
+       if (ob->move(this_player(),M_GET) !=1) {
+       // dieses '!=1' faengt ME_TOO_HEAVY, ME_TOO_MANY_OBJECTS, 
+       // und ME_CANT_BE_INSERTED ab
+
+           write("Du kannst die Zigarre nicht mehr tragen. Sie faellt zu "+
+           "Boden.\n");
+           ob->move(environment(TP),M_PUT);
+           }
+         zaehler--;
+         return 1;
+         }
+     write(BS("So ein Pech. Jemand hat die besten Stuecke bereits genommen."));
+     return 1;
+     }
+  // und damit man auch andere Sachen als Zigarren im Raum nehmen kann:
+  return 0;
+  
+}
+
+reset()
+{
+    ::reset();
+    // nach 30-45 min kann man wieder 5 neue Zigarren nehmen
+    zaehler=5;
+}
+
+Die NPCs. Es gibt (noch) keine Vorschrift, wie stark maximal NPCs sein duerfen,
+aber schau Dir trotzdem 'man balance' zu diesem Thema an.
+NPCs werden oft schmerzlich von ihren Magiern vernachlaessigt. Oft
+steht der grosse Drache auf dem Speicher, kann Dich in einem Schlag umhaun,
+weiss aber nicht einmal seinen Namen und besitzt nicht ein Detail. Kuemmer
+Dich um Deine NPCs. Gib ihnen eine schoene Beschreibung, lass sie etwas
+ueber sich erzaehlen, gib ihnen Details, gib ihnen Kleidung.
+
+Oft will man NPCs durch Gebiete laufen lassen, so dass sie mal Land und
+Leute kennenlernen. Das ist etwas rechnerlastig, und auch wenn wir jetzt
+jede Menge Power unter der Haube haben, sollte man einige Dinge beachten:
+* Es gibt einige sehr schoene Standard-Lauf-NPCs. Es bietet sich an, diese
+  zu benutzen. Sie haben alle noetigen Eigenschaften, unter anderem, dass sie 
+  stehen bleiben, wenn sie keine Spieler mehr antreffen. Das spart 
+  Performance. Ausserdem werden sie oft von einem Master gesteuert, und das 
+  spart call_outs.
+* Aufwendig ist besonders das Bewegen von NPCs. Rennende NPCs sind doch
+  oede. Lass sie sich langsam bewegen. Jedes schoene Gespraech mit einem NPC 
+  geht in die Hose, wenn der NPC staendig vor einem weglaeuft.
+
+Denke generell daran, uebersichtlich zu programmieren. Benutze nicht
+mehr als 78 Zeichen pro Zeile, fuege ruhig Leerzeilen ein, um die Struktur
+zu unterstreichen. Ruecke in Schleifen den Code ein paar (z.B. 3) Zeichen
+ein. Kommentare machen Deinen RM gluecklich! Wenn Du einst vielleicht nicht
+mehr da bist, wird irgend jemand Deinen Code warten muessen. Denk an den
+armen Kerl! Fuege bei Deinen Objekten einen Hinweis darauf ein, wovon und
+wofuer sie benutzt werden! Wenn Du die Wahl hast, Code in einer
+fuerchterlich effizienten, aber unuebersichtlichen Weise zu schreiben, oder
+etwas weniger effizient, aber intuitiv und einleuchtend, nimm die
+einleuchtende Weise. 
+
+Zaubertraenke, Gildenobjekte, Gildenquests
+
+Zaubertraenke
+
+Zaubertraenke sind wichtig, und obwohl es sie schon an vielen Stellen im MG
+gibt, sind sie immer noch gefragt. Man sollte beim Einbauen von ZTs daran
+denken, sie nicht tief in Quest-Gebieten zu verstecken, so dass ein Spieler
+keine Quest nochmal spielen muss, nur um den ZT zu bekommen. Ausserdem
+sollten sie nicht zu schwer versteckt werden. Ein Zaubertrank in einem Buch
+bekommt man z.B. durch:
+
+create() {
+...
+AddReadDetail("buch","@@det_buch@@");
+}
+
+string det_buch() 
+{
+ if (this_player()->FindPotion("Du findest Macht in diesem Buch...\n")) 
+   return "";
+ else
+   return "Du findest keine Macht in diesem Buch. Wie schade...\n";
+}
+
+Denke daran, auch einen Vers zu schreiben, welcher im Orakel den Tip fuer
+diesen ZT darstellt. Du kennst diese Verse sicher schon zur Genuege :-).
+Du musst den Zaubertrank spaeter zusammen mit den Forscherpunkten anmelden.
+
+Gilden- und Miniquests.
+
+Gildenquests sind kleine Aufgaben, nicht gross genug fuer richtige Quests,
+welche von den Mitgliedern einer Gilde geloest werden muessen, bevor sie
+aufsteigen koennen. Die Dinger sind voll im Kommen. Zauberer-, Kaempfer-
+oder Chaos-Gildenmagier sind immer hinter denen her. Quests wie 'Finde das
+verschollene Buch' oder 'Befreie den Verurteilten' lassen sich oft ohne
+grossen Aufwand in Gebiete einbauen. Sie garantieren auch eine gewisse
+permanente Besucherzahl im Gebiet :-)). Bevor man diese Quests einbaut,
+sollte man das prinzipielle OK der Gildenmagier holen, nachher muss man
+ihnen die Quest vorstellen, damit sie sie bewerten und freigeben.
+
+Will man eine kleine Aufgabe nicht gildenabhaengig, sondern fuer alle
+Spieler einbauen und dafuer Stufenpunkte vergeben, dann kann man die Aufgabe
+auch als Miniquest eintragen lassen, und zwar beim gleichen EM, der auch die
+FPs eintraegt. Es folgen Code-Beispiele fuer die Vergabe der Quests...
+
+int befreie(string wen) 
+{
+  notify_fail("Wen moechtest Du befreien?\n");
+  if (wer != "gefangener") 
+    return 0;
+  
+  // Hier werden die Stufenpunkte fuer die Miniquest vergeben...
+  // Falls man keine Miniquest angemeldet hat, laesst man das natuerlich weg
+  SCOREMASTER->GiveMiniQuest(this_player());
+
+  // Hier sind die Kaempfer. Der Code kann variieren...
+  call_other("/p/kaempfer/std/k_master","SetzeAufgabe",getuid(this_player()),
+    "Befreie den Gefangenen");
+
+  // Letztes Beispiel: Hier sind die Zauberer:
+  call_other("/p/zauberer/npc/test","GiveQuest",this_player(),
+    "Befreie den Gefangenen");
+  ...
+}
+
+Gildenobjekte
+
+Gildenobjekte wirken noch besser als Gildenquesten, um Spieler in Gebiete zu
+locken :-). Dies sollte jedoch kein Anlass sein, ein Gebiet mit derartigen
+Dingen zu ueberladen aber dafuer die Details und die Atmosphaere zu
+vergessen. Ganz generell gilt: Maessige Dich! Weniger ist mehr!
+
+Ganz generell sind Gildenobjekte z.B. Utensilien der Zauberer, Waffen mit
+Kaempfer-Boni fuer Kaempfer oder Kreiden etc. fuer Chaoten. Diese Objekte
+muessen natuerlich von den Gildenmagiern genehmigt werden, bevor das Gebiet
+angeschlossen wird. Sprecht frueh genug mit den Gildenmagiern. Sie werden
+euch sicher genug Beispiele fuer derartige Objekte geben, weshalb hier auf
+derartiges verzichtet wird.
+
+-------------------------------------------------------------
+P H A S E 2
+-------------------------------------------------------------
+Du hast nun alle Raeume/Objekte/NPCs erstellt. Nun geht es in die Testphase.
+Erzeuge Dir einen Testspieler, damit Du Deine Raummeldungen wie z.B. mittels
+tell_room() auf ihre Richtigkeit ueberpruefen kannst. Schau Dir in Ruhe
+Deine Sachen an und versuche, die unsinnigsten Sachen darin zu tun. Das werden
+die Spieler naemlich nachher auch machen. Ist Deiner Meinung nach alles OK,
+schreib eine Email an Deine zustaendigen RMs. Sehr hilfreich (fuer die RMs)
+waere eine zusaetzliche Liste (siehe auch Phase 4) mit:
+
+1) allen NPCs (welche Objekte tragen sie) und allfaellige Sonderfunktionen
+   ganz kurz erlaeutert.
+2) Eine Liste aller Objekte. Bei komplizierten Objekten eine kurze Beschreibung
+3) Eine Liste, in welchem Raum liegt welches Objekt/ZT/Heilstelle/NPC
+
+Diese Liste kannst Du natuerlich in Dein /doc Verzeichnis legen, was dann auch
+spaeter sehr hilfreich sein kann.
+
+Falls sich Deine RMs nicht melden, sprich sie an, mail ihnen nochmal.
+Vielleicht haben sie Deine Mail vergessen. RMs sind prinzipiell vergesslich.
+Sie unterhalten sich jedoch sicherlich gern mit Dir. Keine falsche
+Bescheidenheit, ran an den Feind.
+
+- Testspieler -   oder   - Zwischen Legalitaet und Wahnsinn -
+
+Kaum ein Magier kann die merkwuerdigen, oft widersinnigen und kranken
+Gedankengaenge eines Spielers nachvollziehen. Deshalb ist es eine gute
+Sache, sein Gebiet von Spielern testen zu lassen. Sie koennen die Staerke
+der NPCs gut einschaetzen und korrigieren, kennen die geheimen Kniffe der
+Gilden und machen generell das, woran man nie gedacht hat.
+
+Spielertesties sind so eine Sache. Als erstes muss man kompetente Spieler
+finden, welche bereit sind, das Gebiet zu testen. Dies ist nicht einfach,
+denn die wirklich guten Tester sind oft hoffnungslos ueberbelegt. Hat man
+einen willigen Spieler gefunden, kreiert man sich einen Testspieler, indem
+man einen neuen Spieler einloggt und im P_TESTPLAYER auf den eigenen Namen
+setzt. Das kannst Du z.B. so machen: 
+xcall testi->SetProp(P_TESTPLAYER,"<Magier>") wobei <Magier> Dein Name ist.
+
+Danach darf man ihn wild in Gilden eintreten lassen, ihm seine 
+Werte setzen und ihm Ausruestung clonen. Es gibt einige schoene
+Testspieler-Tools, mit denen sich Testies Ruestungen setzen und sich heilen
+koennen.
+
+Hat man Spieler und Testspieler, so kommt man an die legalen Probleme. Alles
+wichtige erfaehrt man durch 'hilfe testspieler'. Dort steht, dass
+Testspieler bei Erzmagiern angemeldet werden muessen, welche sie dann in ein
+Ueberwachungstool eintragen, so dass seine Handlungen geloggt werden. 
+Tatsache ist, dass sich aber kaum einer seine Testspieler bei einem
+Erzmagier anmeldet und die meisten EMs das selbst nicht tun. (Testies neuer
+Gilden moegen hier eine Ausnahme bilden).
+
+Generell beachte folgendes: Mach keine Spieler zu Testspielern, denen Du nicht
+traust oder die Du nicht kennst. Wenn ein Spieler mit seinen Tools durchs MG
+laeuft und Unsinn anstellt, bist Du mit Schuld, egal ob der Testie
+angemeldet ist oder nicht. Wenn Du einen Spieler durch ein Gebiet laufen
+laesst, bevor die FPs eingetragen wurden, dann ist das nicht legal, aber es
+wird keiner meckern, wenn Du ihn ueberwachst und somit aufpasst, dass er
+keinen Unsinn ausserhalb des Gebiets anstellt. Wenn Du einen Spieler durch
+ein Gebiet mit FPs laufen laesst und der Spieler die von ihm gefundenen FPs
+bekommen will, so musst Du ihn bei einem Erzmagier anmelden, denn FPs kann
+nur ein Erzmagier setzen.
+
+Oh, und schreibe Dir die Namen und Passwoerter Deiner Testspieler auf. Falls
+man mehrere hat, vergisst man verflucht schnell die Namen :-).
+
+-------------------------------------------------------------
+P H A S E 3
+-------------------------------------------------------------
+Die RMs schauen alle Files an, die Du erzeugt hast. Danach werden sie Dir
+eine Liste zurueckschicken mit allen Problemfaellen, die sie gefunden haben.
+Je nach Umfang Deines Gebietes kann das natuerlich etwas dauern.
+Hast Du die Informationen zurueckbekommen, kannst Du Dich an den Feinschliff
+machen. In der Zwischenzeit solltest Du Phase 4 und Phase 5 erledigen.
+
+-------------------------------------------------------------
+P H A S E 4
+-------------------------------------------------------------
+Falls Du Objekte hast, die genehmigungspflichtig sind (man balance), so musst
+Du einen Antrag an das Balanceteam stellen. In der Zwischenzeit sollte Dein
+Magierlevel so hoch sein, dass Du den Befehl 'goto' benutzen kannst. Es
+gibt einen Extra Raum fuer die Balanceantraege: 
+goto /players/paracelsus/office
+Dort gibt es dann eine gute Hilfeseite.
+Wichtiges zum Antrag: mach bitte nicht fuer jedes Objekt einen eigenen
+Antrag, sondern schreibe Dir einen grossen Antrag, in den alle Deine
+Objekte reinkommen, die der Balance vorgelegt werden muessen (siehe Phase 2:
+aus der Liste, die Du erstellt hast). Dazu solltest Du jedes Objekt kurz 
+beschreiben. Dazu kommen die wichtigsten Werte, bei Waffen z.B. Waffenart, 
+P_WC, P_NR_HANDS, P_DAM_TYPE, P_WEIGHT, und eine kurze Beschreibung der 
+Sonderfunktionen.
+Das Bewilligen Deiner Objekte kann eine ziemlich lange Zeit in Anspruch
+nehmen (Wochen). Hier nicht ungeduldig sein. Das dauert seine Zeit.
+Wenn Du Objekte proggst, die bestimmt durch die Balance muessen, gib doch
+Deinem RM fruehzeitig Bescheid, dass er sich das anguckt. Dann kannst Du 
+die Sachen bereits beantragen, und in der Zeit, bis die Antraege 
+bewilligt wurden, die anderen Dinge proggen. Dieser Ansatz gilt aber eher
+fuer Magier, die bereits etwas angeschlossen haben und neue Gebiete 
+erstellen. Fuer den Neuling ist es empfehlenswerter, alles fertigzumachen
+und dann zu beantragen. Grund: siehe oben: So kannst Du vorweisen, dass Du
+Dir Muehe gegeben hast, tolle Sachen auf die Beine zu stellen, und nicht
+einfach einen NPC in die Welt stellen willst, mit hunderten toller Objekte.
+
+-------------------------------------------------------------
+P H A S E 5
+-------------------------------------------------------------
+Gleichzeitig solltest Du die Forscherpunkte beantragen. Welcher Erzmagier
+dafuer zustaendig ist, findest Du mit 'man erzmagier' heraus. Die 
+Faustregel lautet: etwa 1 FP pro 10 Raeume. Vermeide FP fuer Details, damit
+die 'Untersuche-Skripte' keine Chance haben. Geh lieber so vor: 
+Detail: 'gras' -> 'Du wuerdest Dich am liebsten ins Gras legen.'
+Wenn der Spieler dann eintippt 'lieg ins gras' oder so aehnlich, gib dafuer
+den FP, aber nicht fuers Untersuchen vom Gras.
+Wofuer Du alles Forscherpunkte eintragen kannst, erfaehrst Du durch
+'hilfe forscherpunkte'.
+Mache ein paar Vorschlaege, und schick sie dann dem zustaendigen Erzmagier.
+Der wird aus Deinen Vorschlaegen ein paar rauspicken und anschliessen.
+Aber bevor Du das tust, muss Dein Gebiet zwingend schon im Regionsverzeichnis 
+liegen, damit keine FPs aus /players eingetragen werden.
+
+Gleichzeit mit den FPs meldest Du auch MiniQuests und Zaubertraenke
+bei diesem Erzmagier an. Du musst angeben, wieviele Stufenpunkte die
+Miniquest bringen soll und in welche der 3 Kategorien einfach/mittel/schwer
+Deine Zaubertraenke fallen.
+
+Denke daran, spaetestens jetzt die Gildenquests und Gildenobjekte bei den
+Gildenmagiern anzumelden. Unangemeldetes darf nicht angeschlossen werden.
+Wende Dich also frueh genug an diese Leute.
+
+-------------------------------------------------------------
+P H A S E 6
+-------------------------------------------------------------
+Ist alles von der Balance genehmigt worden, und sind die FP angeschlossen,
+solltest Du Deine RMs informieren. Falls Dein Gebiet immer noch im
+Heimverzeichnis liegt, wird es nun in dein Regionsverzeichnis
+verschoben. Du stehst nun kurz vor dem Anschliessen. Ueberleg Dir doch
+schon eine nette Botschaft, die Du dann in der MPA veroeffentlichen wirst.
+
+-------------------------------------------------------------
+P H A S E 7
+-------------------------------------------------------------
+GRATULATION! Du hast es geschafft! Aber jetzt nicht auf den Lohrbeeren
+ausruhen. Die ersten Spieler werden sich einfinden, und viele sind
+wandelnde Konversationslexikons, die auch den kleinsten Grammatikfehler 
+in Deinen Beschreibungen finden. Andere Spieler werden Dir neue Vorschlaege
+fuer commands zusenden, die in die entsprechenden Raeume passen wuerden.
+In /log/report/<magiername>.rep wird automatisch Dein Report-File erzeugt,
+in das die Spieler die gefundenen Typos, Bugs und sonstige Ideen reinschreiben.
+Schau Dir Dein Repfile an, und arbeite es ab so gut es geht.
+Nur so wird das Gebiet zu einem wirklich 'gepflegten' Gebiet.
+Denke daran, das rep-File nach dem Abarbeiten zu loeschen, sonst wird
+es zu voll.
+
+
+SIEHE AUCH:
+    balance, hilfe magier, /doc/wiz/
+
+----------------------------------------------------------------------------
+Last modified: 16:00 2003-02-22 by Humni
diff --git a/doc/wiz/licht b/doc/wiz/licht
new file mode 100644
index 0000000..d995a23
--- /dev/null
+++ b/doc/wiz/licht
@@ -0,0 +1,34 @@
+Eine Zusammenfassung aller Lichtproperties (fuer genaueres siehe manpages)
+
+P_LIGHT              - gibt an wie stark eine Lichtquelle selbst leuchtet.
+
+P_TOTAL_LIGHT        - gibt das Lichtlevel an, das von einem Objekt ausgeht,
+                       dieses kann != P_LIGHT sein, z.B. weil eine Fackel in
+                       einem Paket liegt.
+
+P_INT_LIGHT          - Lichtlevel in einem Objekt, z.B. ein Raum
+                       gibt an wie gut dieses Objekt ausgeleuchtet ist.
+
+P_PLAYER_LIGHT       - gibt an mit welchem Lichtlevel der Spieler gerade sieht
+
+P_LIGHT_MODIFIER     - veraendert den Lichtlevel der von einem Spieler
+                       wahrgenommen wird, z.B. eine Sonnenbrille
+
+P_LIGHT_ABSORPTION   - Wieviel Licht wird vom Raum geschluckt? wirkt sich
+                       direkt auf P_INT_LIGHT des Raumes aus. Andere Spieler
+                       profitieren von den Lichtquellen ihrer Mitspieler
+                       weniger bei hoeheren Werten. Default: 1.
+
+P_LIGHT_TRANSPARENCY - Wieviel Licht schluckt ein Container und wieviel
+                       Licht dringt nach aussen. Defaulttransparenz: 2
+
+Hinweis: Alle Objekte mit Lichtlevel <0 und >2 sowie alle Objekte, die
+         P_LIGHT_MODIFIER oder Closures setzen sind genehmigungspflichtig!
+         Dafuer sind die Regionsmagier zustaendig.
+
+
+SIEHE AUCH: P_LIGHT
+
+------------------------------------------------------------------------------
+ LETZTE AeNDERUNG:
+    2002-11-21, 17.30 von Zook
diff --git a/doc/wiz/lupe b/doc/wiz/lupe
new file mode 100644
index 0000000..28e23fe
--- /dev/null
+++ b/doc/wiz/lupe
@@ -0,0 +1,209 @@
+ALLGEMEINES:
+	Die Lupe benutzt einen Stapel (Stack), um Objekte zu bearbeiten.
+	Einige Befehle schieben Objekte auf den Stapel, andere wiederum
+	holen sie wieder vom Stapel herunter und benutzen sie fuer die
+	verschiedensten Zwecke.
+	Man kann in einer Zeile mehrere Kommandos gleichzeitig angeben
+	(durch Leerzeichen getrennt).
+	Eine Zeile wird immer von links nach rechts abgearbeitet.
+
+SCHREIBWEISEN:
+	<arg>	- Eine Reihe von Zeichen, die weder Leerzeichen noch Punkte
+		  (.) enthaelt. Falls doch Leerzeichen oder Punkte in <arg>
+		  auftauchen, muss man die Zeichenkette zwischen "...",
+		  '...' oder |...| einschliessen.
+	<nr>	- Eine (positive oder negative) ganze Zahl.
+	<filename> - Ein Dateiname. Fuer ihn gilt das Gleiche wie fuer <arg>:
+		  Wenn er Punkte enthaelt, muss man ihn zwischen "...",
+		  '...' oder |...| einschliessen.
+		  Es sind alle Variationen moeglich, die man auch aus der
+		  Magiershell kennt: absolute Dateinamen (beginnen mit "/"),
+		  Dateinamen relativ zum eigenen Homeverzeichnis ("~/")
+		  oder zum Homeverzeichnis eines anderen Magiers ("~name/"),
+		  Dateinamen in einer Region ("+region/") und Dateinamen
+		  relativ zum aktuellen Verzeichnis (alles andere ;).
+	<func>	- Name einer LPC-Funktion.
+	TOS	- Das Objekt ganz oben auf dem Stapel (Top Of Stack).
+
+DER STAPEL:
+	Bei dem Stapel handelt es sich um einen LIFO-Stapel (Last In - First
+	Out), d.h. das Objekt, das zuletzt auf den Stapel geschoben wurde,
+	wird als erstes bearbeitet.
+	Bei der Bearbeitung wird der TOS in der Regel vom Stapel entfernt
+	(Ausnahmen: =, stk und #), der Stapel wird also im Laufe der Zeit
+	wieder kleiner.
+	Der Stapel kann maximal zehn Elemente aufnehmen. Dieses Limit wird
+	man bei "normalem Betrieb" allerdings selten erreichen.
+	Sollte es trotzdem einmal eng werden, stehen einem noch zehn
+	Variablen zur Verfuegung, in denen man zusaetzlich Objekte (vom
+	Stapel) ablegen kann.
+
+BEFEHLE:
+	Es gibt drei unterschiedliche Befehlsgruppen:
+	a) Befehle, die ein Objekt auf den Stapel schieben;
+	b) Befehle, die mit Objekten auf dem Stapel arbeiten;
+	c) Befehle, die ohne den Stapel auskommen.
+	Einige Befehle setzen voraus, das der TOS ein Lebewesen oder gar ein
+	Spielerobjekt ist; andere Befehle sind erst ab einem bestimmten
+	Magierlevel zugaenglich. Dies ist bei den entsprechenden Befehlen
+	jeweils vermerkt.
+
+    Diese Befehle schieben ein neues Objekt auf den Stapel:
+	creat <filename> Macht das Gleiche wie 'new', das Objekt wird aller-
+			dings nicht in Dein Inventory gesteckt.
+	here		Das Objekt, in dem man gerade steht, auf den Stapel
+			schieben.
+	lv <arg>	Schiebt das Lebewesen mit dem living_name <arg> auf
+			den Stapel. ACHTUNG! Das muss dann nicht unbedingt
+			das Lebewesen sein, das man dort eigentlich haben
+			will! Es wird einfach das erste Objekt genommen,
+			dessen living_name passt! Wenn man etwas mit einem
+			NPC vorhat, der im gleichen Raum steht, spricht man
+			ihn besser mit here.name an.
+	me		Sich selbst auf den Stapel schieben.
+	new <filename>	Cloned das mit <filename> angegebene Objekt und
+			schiebt es auf den Stapel. Anschliessend befindet es
+			sich in Deinem Inventory.
+	ob <filename>	Laedt das Objekt, das mit <filename> angegeben ist,
+			und schiebt es auf den Stapel.
+	pl <arg>	Schiebt das Spielerobjekt mit dem Namen <arg> auf den
+			Stapel.
+			Es werden auch netztote Spieler berucksichtigt.
+	push <filename> Schiebt das Objekt, das mit <filename> angegeben ist,
+			auf den Stapel.
+	<filename>	Macht das gleiche wie 'ob', falls <filename> mit "/"
+			oder "~" beginnt.
+
+    Die naechsten Befehle schalten Optionen der Lupe an/aus/um:
+	desc		Die zusaetzliche Angabe der Kurzbeschreibung des
+			aktuellen Objektes wird unterdrueckt.
+	rec		Schaltet in den "rekursiv"-Modus um. Dieser Modus
+			wird von folgenden Befehlen genutzt:
+			'inv', 'cln' und 'clnof'
+			Nach Ausfuehrung eines dieser Befehle wird der
+			"rekursiv"-Modus automatisch wieder abgestellt.
+	norec		Stellt den "rekursiv"-Modus "von Hand" ab.
+
+    Diese Befehle schieben ebenfalls ein neues Objekt auf den Stapel. Sie
+    arbeiten dabei allerdings relativ zum TOS. Man muss also schon mindestens
+    ein Objekt auf den Stapel geschoben haben. Der alte TOS wird dabei in der
+    Regel entfernt.
+	.<nr>		Schiebt das <nr>te Objekt im Inventory des TOS auf den
+			Stapel.
+	.<arg>		Schiebt das Objekt mit der ID <arg> im Inventory des
+			TOS auf den Stapel.
+	<<nr>		Schiebt die Variable <nr> auf den Stapel. Als <nr>
+			sind Werte zwischen 0 und 9 moeglich.
+	@<nr>		Schiebt das <nr>te Objekt von oben noch einmal auf den
+			Stapel. Der alte TOS hat die Nummer 0. Weder der alte
+			TOS noch das verschobene Objekt werden vom Stapel ent-
+			fernt.
+			@1 ist analog zu 'over'.
+	copy		Legt eine Kopie des TOS an (inkl. aller Propertywerte)
+			und schiebt diese Kopie auf den Stapel. Die Kopie
+			befindet sich danach in Deinem Inventory.
+	dup		Schiebt den TOS doppelt auf den Stapel.
+	env		Schiebt das Objekt, in dem sich der TOS befindet, auf
+			den Stapel.
+	over		Schiebt das Objekt, das sich unter dem TOS befindet,
+			nochmal auf den Stapel. Dabei werden weder der alte
+			TOS noch das Objekt unter ihm entfernt.
+	result		Wenn in einem Objekt eine Funktion aufgerufen wurde,
+			und diese Funktion wieder ein Objekt zurueckgegeben
+			hat, so wird dieses Objekt auf den Stapel geschoben.
+	swap		Tauscht TOS und das darunter liegende Element gegen-
+			einander aus.
+
+    Die naechsten Befehle arbeiten mit den auf dem Stapel liegenden Objekten.
+    Dabei werden die bearbeiteten Objekte in der Regel vom Stapel entfernt.
+	><nr>		Speichert den TOS in der Variablen <nr>. Als <nr>
+			sind Werte zwischen 0 und 9 moeglich.
+	=		Zeigt den TOS. Dieser bleibt dabei unveraendert.
+	#		Der Stack bleibt ueber das naechste Kommando hinaus
+			erhalten.
+	inv		Zeigt das Inventory des TOS. Der TOS bleibt dabei
+			unveraendert.
+	cln		Entfernt alle Objekte aus dem Inventory des TOS. Es
+			werden allerdings keine Spieler entfernt.
+	clnof <arg>	Entfernt alle Objekte, die auf die ID <arg> anspre-
+			chen, aus dem Inventar des TOS.
+	clr		Loescht den gesamten Stack.
+	Dest		Der TOS wird zerstoert. Das geht allerdings NICHT,
+			wenn der TOS ein Spielerobjekt oder die Lupe selbst
+			ist.
+	dest		Wie Dest, allerdings wird zuerst TOS->remove() auf-
+			gerufen, um dem TOS die Moeglichkeit zu geben, noch
+			hinter sich aufzuraeumen.
+	dinfo		Gibt einige GameDriver-interne Informationen wie zB.
+			die Zeit des naechsten reset()-Aufrufs oder die an-
+			gesammelte evalcost des TOS aus.
+	disco		Unterbricht die Verbindug des TOS (muss ein aktiver
+			Spieler sein) zum MG (disconnect).
+	hl		Zeigt die Historyliste des TOS an (hierbei muss es
+			sich um einen Spieler handeln).
+			Dieser Befehl steht ab ELDER_LVL (50) zur Verfuegung.
+			!!!Fuer die Benutzung gilt das Gleiche wie fuer das
+			Snoopen!!!
+	idle		Gibt an, wie lange der TOS schon idlet. (Der TOS
+			sollte dabei natuerlich ein Spieler sein).
+	info		Gibt einige Informationen ueber den TOS aus.
+	inherit_list	Gibt den Vererbungsbaum des TOS aus (allerdings nicht
+			sehr baumfoermig ;)
+	inv		Zeigt das Inventory des TOS an.
+
+	make		Fuehrt ein Update des TOS durch. Geht nicht bei
+			Spielern! Dafuer gibt es renew.
+	minfo		Gibt einige Informationen ueber den Speicherverbrauch
+			des TOS aus.
+	move, mov, mo	Das Objekt, das unter dem TOS steht, wird in den TOS
+			gemoved. Beide Objekte werden dabei vom Stapel ent-
+			fernt.
+	_move, _mov, _mo, _mv
+			Das Objekt, das unter dem TOS steht, wird in den TOS
+			gemoved, ohne dass dort init() aufgerufen wird. Beide
+			Objekte werden dabei vom Stapel entfernt.
+			Dieser Befehl steht ab ARCH_LVL (60) zur Verfuegung.
+	pop		Entfernt den TOS.
+	renew		Aehnlich wie 'make', der TOS muss allerdings ein
+			Spieler sein. ACHTUNG! Man sollte nicht "einfach so"
+			Spieler 'renew'en, sondern nur dann, wenn es wirklich
+			gerechtfertigt ist!
+	scan, sc	Kombiniert 'stat' und finger. Der TOS muss ein Lebe-
+			wesen sein.
+	stat		Gibt Informationen ueber Zustand, Ausruestung, ge-
+			loeste Quests etc. des TOS aus. Der TOS muss dabei
+			ein Lebewesen sein.
+	stk		Zeigt den gesamten Stack. Dieser bleibt dabei unver-
+			aendert.
+	[<func>]	Ruft im TOS die Funktion <func> ohne Parameter auf.
+			Falls diese Funktion ein Objekt zurueckgibt, kann man
+			dieses anschliessend mit result auf den Stapel
+			schieben.
+	[<func> <arg1> ... <argn>]
+			Ruft im TOS die Funktion <func> mit den Parametern
+			<arg1> bis <argn> auf. Die Parameter sind mit Leer-
+			zeichen zu trennen. Enthalten die Parameter selbst
+			Leerzeichen, so sind sie in "...", '...' oder |...|
+			einzuschliessen.
+			Mittels @<nr> kann man auch Objekte, die sich auf dem
+			Stapel befinden, als Argumente angeben.
+
+    Die folgenden Befehle kommen ohne Objekte auf dem Stack aus:
+	call_out	Gibt eine Liste aller Objekte mit einem laufenden
+			call_out aus.
+	dump		Schreibt die Listen aller Objekte mit aktivem
+			heart_beat und aller Objekte mit laufendem call_out
+			in die Datai LISTS.LUPE in Dein Homeverzeichnis.
+	dumphists	Schreibt die Befehlshistory aller momentan einge-
+			loggten Spieler nach /log/ARCH/HD.
+			Dieser Befehl steht erst ab ARCH_LVL (60) zur
+			Verfuegung.
+	heart_beat	Gibt eine Liste aller Objekte mit aktivem heart_beat
+			aus.
+	rusage		Zeigt einige Infos ueber den Ressourcenge-/-verbrauch
+			des Muds an.
+	swho		Zeigt (so gut wie) alle aktiven Snoops.
+	vars		Zeigt die belegten Variablen an.
+
+----------------------------------------------------------------------------
+Last modified: Wed Jun 26 14:49:02 1996 by Wargon
\ No newline at end of file
diff --git a/doc/wiz/material b/doc/wiz/material
new file mode 100644
index 0000000..cbf8022
--- /dev/null
+++ b/doc/wiz/material
@@ -0,0 +1,98 @@
+Materialien
+===========
+
+     Materialien in Morgengrauen koennen ueber die Property P_MATERIAL jedem 
+     Objekt im Spiel zugeordnet werden. Ueber die Materialiendatenbank 
+     stehen dafuer jede Menge Materialien zur Verfuegung. Wenn moeglich, 
+     sollten diese auch benutzt werden, es werden Defaultmaterialien 
+     gesetzt, diese entsprechen aber selten eurer Realitaet.
+
+     Die Materialiendatenbank /secure/materialdb.c haelt saemtliche 
+     Informationen ueber die verwendbaren Materialien, die unter 
+     /sys/thing/material.h aufgefuehrt sind. Es empfiehlt sich SEHR, Verweise
+     auf die MatDB nur in Form von MATERIALDB zu formulieren.
+     Sie verwaltet auch die Eigenschaften einzelner Materialien. Es gibt 
+     diverse Materialiengruppen wie "entflammbar", "Metall", "biologisches 
+     Material", denen die Materialien entsprechend zugeordnet sind. Diese 
+     Informationen koennen mit entsprechenden Methoden abgerufen/ausgewertet 
+     werden.
+     Darueber hinaus sind Materialien bei der Erkennung mit anderen 
+     Materialien verwechselbar. Wie das genau funktioniert, ist in "man 
+     materialerkennung" ausgefuehrt.
+
+     P_MATERIAL:
+       Die Property P_MATERIAL ist grundsaetzlich ein Mapping, in dem zu 
+       jedem Material der Anteil an dem Objekt stehen sollte, Z.B. kann 
+       ein Speer zu 80% aus Holz und zu 20% aus Metall (Speerspitze) 
+       bestehen:
+
+	SetProp(P_MATERIAL, ([MAT_MISC_METAL:20,MAT_MISC_WOOD:80]))
+
+       (Zur Vereinfachung ist es erlaubt, bei SetProp ein einzelnes Material 
+        oder ein Array aus Materialien anzugeben, beides wird automatisch 
+        umgewandelt in ein Mapping mit gleichen Anteilen aller Materialien.)
+
+     Defaults für P_MATERIAL:
+	Waffen generell:	   ([MAT_MISC_METAL:100])
+	Schwerter:		   ([MAT_MISC_METAL:100])
+    	Messer:			   ([MAT_MISC_METAL:80, MAT_MISC_WOOD:20])
+	Aexte:			   ([MAT_MISC_METAL:50, MAT_MISC_WOOD:50])
+	Speere:			   ([MAT_MISC_METAL:20, MAT_MISC_WOOD:80])
+	Keulen:			   ([MAT_MISC_WOOD:100])
+	Ruestungen generell:	   ([MAT_LEATHER:100])
+	Koerperruestungen, Helme,
+	Ringe, Amulette, Schilde:  ([MAT_MISC_METAL:100])
+	Umhaenge, Hosen:	   ([MAT_CLOTH:100])
+	Handschuhe, Schuhe:	   ([MAT_LEATHER:100])
+	andere Dinge:		   ([MAT_MISC:100])
+
+      Uebersicht ueber die Methoden an Objekten (/std/thing/description):
+	int QueryMaterial(string mat):
+	- gibt % des Materialanteils von mat am Objekt zurueck
+	int QueryMaterialGroup(string grp):
+	- gibt % des Materialgruppenanteils von grp am Objekt zurueck
+	string MaterialList(int casus, mixed idinf):
+	- gibt String mit Zusammensetzung des Objektes zurueck
+
+      Uebersicht ueber die Methoden am Master (MATERIALDB):
+	string *GetMatMembership(string mat):
+	- alle Gruppen, in denen das Material mat ist
+	string *GetGroupMembers(string grp):
+	- alle Materialien, die in der Gruppe grp sind
+	string MaterialName(string mat, int casus, mixed idinf):
+	- ergibt die spielerlesbare Angabe eines Materials mat
+	string GroupName(string grp):
+	- ergibt spielerlesbare Angabe der Gruppe grp
+	string ConvMaterialList(mixed mats, int casus, mixed idinf):
+  	- wird von MaterialList() mit den Materialien des Objektes gerufen
+	void Dump():
+  	- schreibt Materialien und Gruppen in /p/daemon/save/MATERIALS
+	string *AllMaterials():
+	- Liste aller aktuellen Materialien
+	string *AllGroups():
+	- Liste aller aktuellen Gruppen
+
+      Falls euch ein Material fehlt:
+	- falls es kein genau passendes Material gibt, sollte man das am 
+	  ehesten passende MAT_MISC-Material verwenden.
+	- falls es sich lohnen wuerde, das bisher noch nicht definierte 
+	  Material einzubauen (wenn es haeufig genug verwendet wird), bitte 
+	  Mail an einen EM (-> man AddMaterial() )
+	- verschiedene Eigenschaften lassen sich kombinieren. Z.B. besteht 
+	  ein (unbekanntes) explosives Gas zu 100% aus Gas und zu 100% aus 
+	  explosivem Material. Insofern kann man es mit
+          SetProp(P_MATERIAL, ([MAT_MISC_GAS:100, MAT_MISC_EXPLOSIVE:100]))
+          zusammensetzen.
+
+SIEHE AUCH:
+     Konzepte:	  materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Methoden:    QueryMaterial(), QueryMaterialGroup(), MaterialList(),
+     Listen:	  AllMaterials(), AllGroups(), Dump()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+     Sonstiges:	  P_MATERIAL_KNOWLEDGE
+
+7. Mai 2004 Gloinson
diff --git a/doc/wiz/materialerkennung b/doc/wiz/materialerkennung
new file mode 100644
index 0000000..976106f
--- /dev/null
+++ b/doc/wiz/materialerkennung
@@ -0,0 +1,79 @@
+Materialerkennung
+=================
+
+     Bei MaterialName(mat,casus,idinf) und MaterialList(casus,idinf) id der 
+     Parameter <idinf> als Angabe dafuer gedacht, wie gut Materialien 
+     erkannt werden sollen. Es sind folgende Arten von Angaben moeglich:
+
+      - Ein Array
+	Die einzelnen Arrayelemente werden nach untenstehenden Regeln 
+	behandelt und deren Ergebnisse addiert.
+      - Ein Objekt
+	In diesem Fall wird
+	ob->QueryProp(P_MATERIAL_KNOWLEDGE)
+	nach untenstehenden Regeln behandelt.
+	Diese Property ist fuer rassenabhaengige Faehigkeiten gedacht.
+      - Eine Zahl
+	In diesem Fall wird der Wert direkt als Faehigkeit angesehen, das 
+	Material erkennen zu koennen.
+      - Eine Closure
+	In diesem Fall wird der Returnwert von
+	funcall(closure,material,gruppen_in_denen_diese_material_ist)
+	genommen.
+      - Ein Mapping
+	In einem Mapping sind 3 Arten von Eintraegen moeglich:
+	- Ist im Mapping unter <mat> eine Zahl eingetragen, wird diese 
+	  genommen und die weiteren Eintraege nicht weiter ausgewertet.
+        - Die Werte von allen Gruppen im Mapping, zu denen das Material
+          gehoert, werden addiert.
+        - Falls das Mapping einen Eintrag MATERIAL_SYMMETRIC_RECOGNIZABILITY 
+          hat, der ein Array von der 	  Form
+		({gruppe1, wert1, gruppe2, wert2, ...})
+	  ist, wird fuer jede dieser Gruppen der zugehoerige Wert 
+	  addiert, falls das Material in der Gruppe ist, und sonst 
+	  abgezogen.
+          Im Beispiel: ({MATGROUP_BIO: 5}) gibt einen Erkennbonus von 5% auf 
+          alle biologischen Materialien, aber einen Malus von -5% auf alle 
+          nichtbiologischen Materialien.
+
+     Schwer erkennbare Materialien haben in der Materialdatenbank eine 
+     zusaetzliche Angabe der Form
+	({mat1, wert2, mat3, wert4, mat5, ... wert(n-1), mat(n)})
+     wobei mat(n) das Material selber ist.
+
+     Das erkannte Material ist das Material mat(k), bei dem die
+	wert(k-1) <= Erkennungsfaehigkeit < wert(k+1)
+     ist. Bei Erkennungsfaehigkeit 100 oder mehr wird das Material auf jeden 
+     Fall erkannt.
+
+     Der Wert fuer Durchschnittsspieler ist 0.
+
+BEISPIEL:
+     Angenommen, bei Platin waere die Angabe in der Datenbank
+	({Metall, -20, Silber, 20, Platin})
+     Ein Spieler ohne besondere Erkennungsfaehigkeiten (also Wert 0) wuerde 
+     also Platin fuer Silber halten, jemand, der von Metallen keine Ahnung 
+     hat (beispielsweise mit der Angabe ([MATGROUP_METAL:-25]) in 
+     P_MATERIAL_KNOWLEDGE) sieht nur noch, dass es ein Metall ist und jemand 
+     mit ueberdurchschnittlicher Faehigkeit erkennt Platin als das, was es 
+     ist.
+
+     P_MATERIAL_KNOWLEDGE koennte z.B. fuer Rassen wie folgt gesetzt werden:
+	Elf:   ([MATGROUP_WOOD:30])
+	Zwerg: ([MATGROUP_STONE:30])
+     oder etwas komplexer
+	Zwerg: ([MATGROUP_STONE:30,
+		 MATERIAL_SYMMETRIC_RECOGNIZABILITY: ({MATGROUP_BIO: -5})])
+
+SIEHE AUCH:
+     Konzepte:	  material
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Methoden:    QueryMaterial(), QueryMaterialGroup(), MaterialList(),
+     Listen:	  AllMaterials(), AllGroups(), Dump()
+		  materialliste, materialgruppen
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+     Sonstiges:	  P_MATERIAL_KNOWLEDGE
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/wiz/materialgruppen b/doc/wiz/materialgruppen
new file mode 100644
index 0000000..5201bae
--- /dev/null
+++ b/doc/wiz/materialgruppen
@@ -0,0 +1,53 @@
+Materialgruppen
+===============
+
+  Die ebenfalls kommentierte Liste unter /sys/thing/material.h ist immer
+  aktueller als diese hier.
+
+  MATGROUP_SOLID          feste Dinge
+  MATGROUP_FLUID          Fluessigkeiten
+  MATGROUP_GAS            Gase
+
+  MATGROUP_WOOD           Hoelzer
+  MATGROUP_DECIDUOUS_WOOD Laubhoelzer
+  MATGROUP_CONIFER_WOOD   Nadelhoelzer
+  MATGROUP_TROPICAL_WOOD  Tropenhoelzer
+  MATGROUP_JEWEL          Edelsteine
+  MATGROUP_MINERAL        Mineralien
+  MATGROUP_STONE          Steine
+  MATGROUP_METAL          Metalle
+  MATGROUP_PRECIOUS_METAL Edelmetalle
+  MATGROUP_PAPER          Papier und Aehnliches
+  MATGROUP_CLOTH          Stoffe
+
+  MATGROUP_ELEMENTAL      (altertuemliche) Elemente
+  MATGROUP_BIO            Alles, was lebt oder lebte
+  MATGROUP_LIVING         Lebewesen
+  MATGROUP_HERBAL         Pflanzliches
+  MATGROUP_DEAD           Ueberreste von Lebewesen
+  MATGROUP_DRUG           Drogen, Heiltraenke
+
+  MATGROUP_INFLAMMABLE    Entflammbares
+  MATGROUP_EXPLOSIVE      Explosives
+  MATGROUP_POISONOUS      Giftiges
+  MATGROUP_MAGIC          Magisches
+  MATGROUP_RADIOACTIVE    Radioaktives
+  MATGROUP_ELECTRICAL     Elektrisches
+  MATGROUP_MAGNETIC       Magnetisches
+  MATGROUP_ACIDIC         Saures
+  MATGROUP_EATABLE        Essbares
+  MATGROUP_FLEXIBLE       Biegsames
+  MATGROUP_INVIS          Unsichtbares
+
+SIEHE AUCH:
+     Konzepte:	  material, materialerkennung
+     Grundlegend: P_MATERIAL, /sys/thing/material.h
+     Methoden:    QueryMaterial(), QueryMaterialGroup(), MaterialList(),
+     Listen:	  AllMaterials(), AllGroups(), Dump()
+		  materialliste
+     Master:	  AddMaterial(), ConvMaterialList(), MaterialGroup(),
+		  GroupName(), MaterialName(),
+		  GetGroupMembers(), GetMatMembership()
+     Sonstiges:	  P_MATERIAL_KNOWLEDGE
+
+7. Mai 2004 Gloinson
\ No newline at end of file
diff --git a/doc/wiz/mudlib-codestyle b/doc/wiz/mudlib-codestyle
new file mode 100644
index 0000000..77b4baa
--- /dev/null
+++ b/doc/wiz/mudlib-codestyle
@@ -0,0 +1,36 @@
+Wollt ihr Code in der allgemeinen, oeffentlichen Mudlib anschliessen, bitte
+beachtet die folgenden Hinweise zum Codestyle (nicht erschoepfend):
+
+* Einrueckungen per Leerzeichen, nicht Tabs
+* Einrueckung von 2 Leerzeichen pro Ebene
+* praegnante und viele Kommentare
+* keine lambdas
+* Bei inline-closures die function-Syntax statt der (: :)-Syntax verwenden.
+* else, else if in eine eigene Zeile
+* { am Beginn von Bloecken soll in eine eigene Zeile.
+* Nach ifs, Loops & Co: umfasst der davon kontrollierte Code mehr als eine
+  physische Zeile Code, einen Block mit { } formulieren.
+* keine return fun(), 0;
+* (type) Casts sollten vermieden werden (Ausnahme: (type)call_other).
+  (type) konvertieren nur, wenn die Typen zur Compilezeit bekannt und
+  unterschiedlich sind. Daher bei gewuenschten Konversionen to_type() nehmen.
+* Pfade, die absolut sind, sollen auch mit / beginnen, z.B. 
+  inherit "/std/thing", nicht inherit "std/thing"
+
+Benennung von Properties:
+* der interne Name von Properties in der Basis-Mudlib beginnt immer mit
+  "p_lib_". Niemand sonst sollte Properties mit diesem Praefix erstellen.
+
+Sonstiges:
+* In der Mudlib wird keine neue Funktion(alitaet) angeschlossen, bevor die
+  Dokumentation dafuer fertig ist.
+  Am liebsten ist mir, bei der Konzeptentwicklung zuerst die fertige
+  Dokumentation (Manpage) fuer Nutzer/Spieler zu entwickeln, bevor ein Magier
+  ueberhaupt eine Zeile Code schreibt.
+* Patches muessen eine Zusammenfassung haben, welche kurz erlaeutert,
+  was dieser Patch fixen/aendern/verbessern soll und auf welche Weise
+  diese umgesetzt wird. (Anders gesagt: eine Commit-Message)
+
+LETZTE AeNDERUNG:
+  15.8.2015, Zesstra
+
diff --git a/doc/wiz/muster_raum.c b/doc/wiz/muster_raum.c
new file mode 100644
index 0000000..74e4c99
--- /dev/null
+++ b/doc/wiz/muster_raum.c
@@ -0,0 +1,82 @@
+/*
+ *----------------------------------------------------------------------
+ * Ein kleiner Beispielraum.
+ * Er laesst sich als /obj/doc/muster_raum ansprechen.
+ *
+ * Dieses File ist Teil der MorgenGrauen-Mudlib von Jof und Rumata
+ * Letzte Aenderung: 17.08.92
+ * #include eingefuegt: Rumata 10.11.98
+ *----------------------------------------------------------------------
+ */
+#pragma strong_types,rtt_checks
+
+inherit "/std/room";
+	/* Diese Zeile deutet an, dass der Raum aus der Standard mudlib */
+	/* abgeleitet wird. Diese Zeile sollte in JEDEM Raum vorkommen. */
+
+#include <properties.h>
+	/* Diese Zeile definiert die grossbuchstabigen Namen, die in den */
+	/* SetProp-Befehlen benutzt werden. Es gibt noch weitere *.h     */
+	/* Dateien, die andere nuetzliche Dinge definieren.              */
+
+protected void create()
+{
+	::create();
+		/* Diese Zeile initialisiert die Standard-attribute. */
+		/* Sie darf nicht fehlen ! */
+
+	SetProp(P_INT_SHORT, "Muster-raum" );
+		/* Die Beschreibung des Raumes fuer Spieler, die den */
+		/* kurz/brief-Modus eingestellt haben. */
+
+	SetProp(P_INT_LONG, "Dieses ist ein Musterraum, der nur zur Erlaeuterung\n"
+		+ "dient. Ein unangenehmer Raum voller Sterne und schraeger\n"
+		+ "Striche. Im Sueden kannst Du in vertrautere Raeume fliehen.\n"
+		+ "Im Norden ist etwas seltsames.\n" );
+		/* Diese Beschreibung bekommen Spieler, die den kurz- */
+		/* Modus nicht benutzen, oder wenn sie "schau" eingeben. */
+
+	SetProp(P_LIGHT, 1 );
+		/* Diese Zahl ist 1 fuer Raeume, in denen man ohne */
+		/* Lichtquelle etwas sehen kann und 0 fuer Raeume, */
+		/* in denen man eine Solche braucht. */
+
+	AddExit( "sueden", "room/adv_guild" );
+		/* Baue einen Ausgang nach Sueden, er wird mittels des */
+		/* Kommandos "exits" angezeigt. */
+
+	AddSpecialExit( "norden", "go_nord" );
+		/* Wenn der Spieler versucht, diesen Ausgang zu */
+		/* benutzen, so wird er nicht in einen anderen Raum */
+		/* bewegt, sondern es wird im Raum die angegebene */
+		/* Funktion aufgerufen. */
+
+	AddCmd( "hilfe", "gib_hilfe" );
+		/* Dieses Kommando ruft die Funktion "gib_hilfe" auf, */
+		/* wenn der Spieler das Kommando "hilfe" eingibt. */
+		/* Das Kommando taucht nicht in der "exits-liste" auf. */
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ * So jetzt ist der Raum fetig.
+ * Wirklich? Nein, fast.
+ * Die oben angegebenen Funktion fehlen noch, aber das sind bereits
+ * "Extras".
+ *----------------------------------------------------------------------
+ */
+
+int go_nord()
+{
+	write( "Hmm das war wohl doch nur die Wand.\n" );
+	return 1;
+		/* Das Kommando wirde erfolgeich beendet. */
+}
+
+int gib_hilfe()
+{
+	write( "Gehe nach Sueden, und Du kommst in die Abenteurer-Gilde.\n" );
+	return 1;
+}
+
diff --git a/doc/wiz/nachteile b/doc/wiz/nachteile
new file mode 100644
index 0000000..d37e56f
--- /dev/null
+++ b/doc/wiz/nachteile
@@ -0,0 +1,51 @@
+----------------------------------------------------------------------------
+NACHTEILE
+----------------------------------------------------------------------------
+Bei der Genehmigung von Objekten im Balanceteam kommt immer wieder die Frage
+auf, welche Nachteile das Objekt denn hat, wenn es schon so viele Vorteile
+besitzt. Generell gilt, dass starke Objekte nur dann genehmigt werden, wenn
+sie auch ueber entsprechend grosse Nachteile verfuegen.
+
+Daher hier mal eine kleine Auflistung dessen, was als solcher betrachtet
+wird:
+
+- Gewicht (nicht bei Keulen fuer Kleriker)
+
+- Anfaelligkeiten gegen Schadenarten
+
+- LP-KP-Abzug (Obergrenzen, jede Runde, jeder Reset)
+- Absenken der Maxwerte von LP, KP
+- Absenken von Attributen
+- Absenken von SA_SPEED, SA_QUALITY usw.
+
+- Schaden/Nutzen abhaengig vom Alignment
+- Generelle Restriktionen
+
+- Objekt muss an einer bestimmten Stelle (z.B. durch das Toeten einen NPC)
+  aufgeladen werden
+- wiederkehrende Kosten (ausser LP/KP auch sowas wie Geld/Items/Aufgabe)
+
+- Behinderung anderer Objekte
+- Belegung von Slots (Set)
+
+- Skillobjekt
+
+- Begrenzte Haltbarkeit
+- Abnutzung (z.B. P_QUALITY, oder einfach durch Zeit/Nichtbenutzung)
+- Umstaende, die das Objekt zerstoeren
+- Begrenzte Wirkung (z.B. Waffe wirkt nur gegen eine Rasse)
+
+- Unique
+- Unique mit begrenzter Anzahl
+
+Generell sollten Paraobjekte die Restriktion Seher besitzen, da sie auch nur
+von Sehern geholt werden koennen. Aehnliche Restriktionen sollten fuer 
+Objekte aus Gebieten gelten, die erst ab einer gewissen Stufe betreten 
+werden koennen.
+
+Objekte, die nur in eng begrenzten Gebieten wirken (z.B. Questitems wie der 
+Spinnentoeter) muessen bei der Balance nicht beantragt werden, selbst wenn
+sie Werte besitzen, die die Balancegrenzen ueberschreiten.
+
+----------------------------------------------------------------------------
+Letzte Aenderung: 18.10.2005 - Miril
diff --git a/doc/wiz/netz b/doc/wiz/netz
new file mode 100644
index 0000000..2dd8cf4
--- /dev/null
+++ b/doc/wiz/netz
@@ -0,0 +1,27 @@
+Stand: 17.08.92
+Ueberarbeitet am 24.08.94 von Viper
+
+Die Netzphilosophie im MorgenGrauen
+-----------------------------------
+
+Die wichtigen Wege und Verbindungen in MorgenGrauen sollen eine Art
+Netzstruktur  besitzen, d.h. es wird nicht am Rand angebaut (wie im 
+alten NF), sondern mitten drin.
+
+Beispiel:
+
+Man stelle sich eine Strasse aus 6 Raeumen vor. Am Anfang verlaeuft
+die Strasse von Osten nach Westen ohne besondere Details am  Weges-
+rand. Mit der Zeit bauen einige Magier aus dieser Region  noerdlich
+und suedlich ihre Raeume und Quests an. Irgendwann jedoch sind alle 
+Raeume ausgeschoepft. Dann kann  der  Erzmagier der Region ZWISCHEN 
+die bestehenden Raeume der Strasse weitere Strassenraeume einfuegen. 
+Dort kann dann wieder Noerdlich und Suedlich angebaut werden. In den 
+neuen Strassenteilen  sollten natuerlich keine Monster oder Hinder-
+nisse aufgestellt werden, so dass die Strasse fuer die SpielerInnen 
+lediglich etwas laenger wird, dort aber keine neuen Probleme auf ihn
+/sie warten.
+
+Zusaetzliche Hindernisse sollten nur nach Absprache mit einem Erz-
+magier (das sind die Magier ab Level 60) in bestehende Strassen
+eingebaut werden.
diff --git a/doc/wiz/npcs b/doc/wiz/npcs
new file mode 100644
index 0000000..0242e4c
--- /dev/null
+++ b/doc/wiz/npcs
@@ -0,0 +1,121 @@
+ HINWEISE ZU NPCS:
+
+  a. Allgemeines / Hinweise / Tipps
+
+     Dies sind Richtlinien, keine Vorschriften!!!!
+
+     Nicht jeder NPC braucht einen living_name. Nur in besonderen NPCs
+     sollte man einen solchen setzen. Alles andere ist unsinnig und belastet
+     nur unnoetig den Speicher.
+
+     Ein Monster sollte niemals zu viel Zeug mit sich herumtragen. Ein bis
+     zwei Sachen reichen im Normalfall.
+
+     Niemals sollte ein Monster allein mit mehreren Top-Qualitaets-Sachen
+     (sehr gute Waffe plus gute Ruestung[en]) ausgestattet sein. Das waere
+     doch zu einfach.
+
+     Die Haerte der Monster ist im Prinzip egal; pflastert die Gebiete ruhig
+     mit Hydren.
+     Nur: Anfaengern oder kleineren Spielern zugaengliche Gebiete sollten
+     keine Autoattackmonster enthalten, die die "Kleinen" gleich umnieten.
+     Solche Gegenden sollten entweder mit Wachen bestueckt oder mit einigen
+     zunehmend haerteren Monstern einen "Vorgeschmack" bieten, damit man
+     nicht unbedarft reinrennt. Unfaelle wird's dennoch geben. Was soll's,
+     Rochus liebt seine Statistik... :)
+
+     Auch bei NPCs kann die Property P_INFO gesetzt werden, dies sollte
+     insbesondere bei starken und speziellen NPCs gemacht werden.
+
+     Bei NPCs, die besondere Spells benutzen, sollten die RMs darauf achten,
+     dass nicht ploetzlich die ganze Gegend von NPCs wimmelt, von denen
+     jeder seinen Gegner blind oder taub macht oder vergiftet.
+
+     Nur als Tipp:
+     Monster mit Attack-Chats sind erheblich kniffliger zu killen und machen
+     auch um ihrer Selbst willen mehr Spass als bloede Hau-Drauf-Monster,
+     die zwar ueber unendlich HP verfuegen, aber im Prinzip nur Geduld
+     brauchen, um sie zu besiegen. Phantasie ist gefragt.
+
+  b. Eigenschaften
+      Fuer einfache Monster bitte create_default_npc() benutzen!
+
+      Ansonsten:
+      P_HANDS
+	Bei Monstern ohne Waffen sollte die Property P_HANDS in allen drei
+	Werten dem Erscheinungsbild des Monsters entsprechen, also in Text,
+	Schadenshoehe und Schadensart.
+	Auch bei Monstern mit einer Waffe sollte P_HANDS vernuenftig gesetzt
+	werden, also nicht zu hoch. Darauf zu achten ist Aufgabe der RMs.
+
+      P_BODY
+	Monster sollten nicht ueber undurchdringliche Bodies verfuegen, weil
+	sie dann nur magisch zu schlachten sind. Bodies ueber 150 (oder
+	P_TOTAL_AC ueber 150 incl. Ruestungen!) sind kaum noch
+	"herkoemmlich" zu killen. Normale Monster, also nicht-magische, sind
+	eigentlich immer anfaellig gegen die physikalischen Schadenstypen.
+	Also bitte da keine P_RESISTANCE setzen.
+
+      P_RESISTANCE/P_VULBERABILITY/P_RESISTANCE_STRENGTHS
+      P_NOMAGIC
+	Die RESISTANCE der Monster sollte sich auf ein oder zwei
+	Schadenstypen begrenzen, und die sollten auch begruendbar sein. Ein
+	normales Wesen gegen BLUDGEON, SLASH oder PIERCE unempfindlich zu
+	machen ist unlogisch und sollte vermieden werden. Gleiches gilt fuer
+	Empfindlichkeiten.
+	Ebenso sollte es Gruende geben, warum ein Monster eine Magie
+	unwirksam macht.
+
+      P_XP
+	Der Toetungsbonus in XP, die der Killer erhaelt, betragen 1/100 des
+	Wertes, der in P_XP angegeben wird. Der Wert selbst sollte der
+	Haerte des Monsters angemessen sein, als Faustregel fuer
+	'gewoehnliche' NPCs gilt:
+
+	  P_XP = 5 * WC * MAX_HP
+
+	Die RMs sollten darauf achten, dass hier keine ueberhoehten Werte
+	gesetzt werden.
+
+	Fuer Attack-Spells und aehnlich fieses kann ein Bonus gegeben
+	werden. Fuer questrelevante NPCs oder "Informations-NPCs", die nicht
+	unbedingt als Metzelobjekte herumstehen, kann man auch die EP
+	senken, um sie fuer EP-Sammler uninteressant zu machen.
+
+      P_MURDER_MSG/P_KILL_MSG
+	Nicht jeder NPC braucht seine eigene Moerder- oder Killermeldung.
+	Bei normalen 	NPCs sollte man also darauf verzichten.
+
+      P_DIE_MSG/P_NOCORPSE
+	Wenn ein NPC ein wenig anders umfaellt als sonst, traegt das auch
+	zum Spiel bei.
+
+      P_GUARD
+	Wenn der NPC nichts bewacht und nicht uebermaessig stark ist, sollte
+	man ihn auch fortlocken koennen. Setzt P_GUARD entsprechend und
+	lasst den NPC mit AddItem und REFRESH_MOVE_HOME erzeugen.
+
+      AddClass
+	Wenn ihr euren NPC in den definierten Klassen einordnen koennt, dann
+	macht das bitte. Besitzer von Spezialwaffen oder Ruestungen werden
+	es euch danken.
+
+  c. Hilfs-/Begleit-NPCs
+
+      Bei solchen NPC wird auf Ausgewogenheit Wert gelegt, der Programmierer
+      sollte sich schon vor Antrag-Stellung an die Balance Gedanken darueber
+      machen, wie sich der NPC selber verhaelt.
+      Beispiel waere hier Unvertraeglichkeit mit anderen NPCs,
+      Aggressivitaet gegenueber anderen NPCs / Spielern, Wankel- muetigkeit,
+      Unzuverlaessigkeit, zeitliche Begrenzung, Schutz oder Schaden in
+      Abhaengigkeit von Align, Gilde des Spielers, Angriff, vom Ort des
+      Geschehens oder der Begabung des Spielers fuer Magie u.ae. Der
+      Phantasie sind hier keine Grenzen gesetzt.
+
+SIEHE AUCH:
+     Verwandt:    balance, ruestungen, waffen, fernwaffen, uniques,
+                  grenzwerte, attributsveraenderungen, resistenzen,
+                  kampfobjekte, begleitnpcs
+     Funktionen:  create_default_npc
+
+14.Feb 2007 Gloinson
diff --git a/doc/wiz/obj_geruest b/doc/wiz/obj_geruest
new file mode 100644
index 0000000..fc1f955
--- /dev/null
+++ b/doc/wiz/obj_geruest
@@ -0,0 +1,134 @@
+/*
+ *----------------------------------------------------------------------
+ * obj_geruest
+ * Dieses File ist Teil der Morgengrauen-Mudlib.
+ * Dieses File wird optimal formatiert, wenn Tabulatoren 4 Zeichen breit
+ * sind.
+ *
+ * Allgemeines Geruest fuer ein Objekt. Wer eigene Objekte schreibt,
+ * sollte sie nicht ganz so intensiv kommentieren wie dieses hier, aber
+ * hier sollen gleichzeitig, die grundlegenden Mechanismen erlaeutert
+ * werden.
+ *
+ * 16.08.92 Rumata
+ * Ueberarbeitet am 24.08.94 von Viper
+ *----------------------------------------------------------------------
+ */
+inherit "std/thing";            /* Leite aus den std-Objekten ab */
+
+#include <language.h>           /* Stelle Namen der Faelle und der Geschlechter */
+							/* dem Objekt zur Verfuegung. */
+
+create()
+{
+	::create();
+		/* Diese Zeile NIEMALS vergessen!!! */
+
+	AddId( "ball" );        
+	AddId( "strandball" );
+		/* Unter diesen Bezeichnungen fuehlt der Ball sich "angesprochen" */
+		/* Mann sollte mit Ids nicht sparen, damit die Spieler nicht ewig  */
+		/* nur nach dem richtigen Namen eines Objektes suchen muessen. */
+
+	SetProp( P_NAME, "Ball" );
+	SetProp( P_GENDER, MALE );
+	SetProp( P_ARTICLE, 1 );
+		/* Diese Meldungen ermoeglichen es der Mudlib, das Objekt richtig */
+		/* zu deklinieren. SetProp(P_ARTICLE, 1) kann wegfallen, da 1 Default ist. */
+
+	SetProp( P_SHORT, "Ein bunter Ball" );
+	SetProp( P_LONG, "Was fuer ein huebscher kleiner Strandball.\n"
+			+"Wem koenntest Du den mal zuwerfen?\n" );
+		/* Beschreibungen zur Betrachtung des Balls. */
+
+	SetProp( P_VALUE, 10 );
+	SetProp( P_WEIGHT, 250 );
+		/* Bei diesen Werten sollte die Verhaeltnismaessigkeit */
+		/* nicht aus den Augen gelassen werden. */
+}
+
+init()
+{
+	::init();
+		/* NICHT VERGESSEN */
+		/* Wenn keine eigenen Aktionen definiert werden sollen, so kann die */
+		/* Funktion init ganz weggelassen werden. Ein init, das nur */
+		/* ::init() aufruft ist ueberfluessig. */
+
+	add_action( "wurf", "werf", 1 );
+	add_action( "wurf", "wirf" );
+		/* werfe, werf oder wirf jemandem den Ball zu. Man sollte darauf */
+		/* achten, dass natuerliche Saetze als Eingabe akzeptiert werden. */
+}
+
+/*************************************************************************/
+/************* Die grundlegenden Funktionen enden hier *******************/
+/*************************************************************************/
+
+
+/* Eine Beispielaktion fuer den Ball. */
+wurf( argument )
+{
+	/* "werf ball rumata zu" wird als Argument "ball rumata zu" bekommen. */
+	string arg1, arg2;
+	string ziel;
+	object zielObj;
+
+	if ( sscanf( argument, "%s %s zu", arg1, arg2 ) != 2 ) return 0;
+		/* Rueckgabe von 0 bedeutet, dass dieses Objekt mit der Eingabe */
+		/* nichts anfangen konnte, und dass der Gamedriver das naechste */
+		/* Objekt befragen soll. */
+
+	if ( id( arg1 ) )
+			/* Welches der Argumente spricht den Ball an ? */
+			/* Das andere muss dann das Ziel des Balls sein. */
+		ziel = arg2;
+	else if ( id( arg2 ) )
+		ziel = arg1;
+	else
+		return 0;
+			/* Keines der Argumente war der Ball. */
+
+	zielObj = present( ziel, environment() );
+		/* Suche ein Objekt, das sich als Ziel angesprochen fuehlt. */
+
+	if ( ! zielObj )
+	{
+		write( "Hier ist kein \"" + ziel + "\".\n" );
+		return 1;
+			/* Hier wird 1 zurueckgegeben, da der Ball das Kommando den  */
+			/* Befehl bearbeiten konnte. Er hat zwar kein Ziel gefunden, */
+			/* aber man kann ja nicht alles haben. :-) */
+	}
+	
+	if( !living( zielObj ) )
+	{
+		write( "Und wie soll " + zielObj->name(WER,1) + " den Ball fangen?\n" );
+		return 1;
+			/* Nur lebende Wesen sollen Baelle fangen koennen. */
+	}
+
+	write( "Du wirfst " + zielObj->name(WEN,1) + " den Ball zu.\n" );
+			/* Mitteilung an den Werfer */
+	say( PL->name(WER,2) + " wirft " + zielObj->name(WEM,2)
+		+ " einen Ball zu.\n", zielObj );
+			/* Mitteilung an alle Wesem im selben Raum. */
+	tell_object( zielObj, "Hoppla, " + PL->name(WER,2)
+		+ " wirft Dir einen Ball zu.\n" );
+			/* Mitteilung an den Beworfenen */
+
+	if ( move( zielObj ) ) return 1;
+		/* Es hat geklappt. */
+
+	move( environment() );
+		/* Im Fehlerfall soll der Ball zu Boden fallen. */
+
+	write( "Aber " + zielObj->QueryPronoun(WER)
+		+ " kann ihn nicht fangen.\nDer Ball plumpst zu Boden.\n" );
+	say( "Aber " + zielObj->QueryPronoun(WER)
+		+ " kann ihn nicht fangen.\nDer Ball plumpst zu Boden.\n", zielObj );
+	tell_object( zielObj,
+		"Aber Du kannst ihn nicht fangen.\nDer Ball plumpst zu Boden.\n" );
+
+	return 1;
+}
diff --git a/doc/wiz/pararaeume b/doc/wiz/pararaeume
new file mode 100644
index 0000000..9263da6
--- /dev/null
+++ b/doc/wiz/pararaeume
@@ -0,0 +1,84 @@
+Para-Raeume
+
+    Grundsaetzlich wird ein Para-Raum dadurch gekennzeichnet, dass an den 
+    Dateinamen ein ^n angehaengt wird, n gibt die Dimension an. Ein Raum in 
+    Para 1 wuerde also raum^1.c heissen.
+
+    Man muss sich nicht selbst darum kuemmern, dass Spieler in den richtigen
+    Para-Raum bewegt werden, das passiert im move().
+
+    Damit man bei gleichen Raeumen, die sich nur durch beispielsweise die
+    Anwesenheit eines fiesen Monsters unterscheiden nicht alles doppelt
+    beschreiben muss, sollte man das bekannte Konzept der Vererbung
+    verwenden. Hierfuer gibt es zwei Ansaetze:
+
+LOESUNG 1:
+    Habe ich einen Raum, der in Para exakt so ist wie in Normal, wo nur eine
+    Sache hinzugefuegt wird, schreibe ich meinen Raum in normal so wie ich es
+    immer tun wuerde, erbe diesen im Para-Raum und setze mein fieses Monster
+    hinein.
+    Einziger Unterschied im Normal-Raum: Damit er immer initialisiert wird,
+    muss ich create_super() erstellen und darin create aufrufen. Sonst buggt
+    der Normalraum beim Betreten, falls der Pararaum zuerst betreten wurde,
+    weil der Normalraum dann zwar geladen aber selbst nicht initialisiert
+    wurde.
+
+    Beispiel:
+
+    raum.c:
+    inherit "/std/room";
+
+    protected void create() {
+      ::create();
+      ...
+    }
+
+    protected void create_super() {
+      create();
+    }
+
+    raum^1.c:
+    inherit "raum";
+
+    protected void create() {
+      ::create();
+      AddItem("fieses_monster",REFRESH_REMOVE);
+    }
+
+LOESUNG 2:
+    Habe ich einen Raum, wo auch in Normal Dinge existieren, die es nicht in
+    Para geben soll, kann ich einen Raum mit der Dimensionsnummer 0 erstellen.
+    Dieser wird niemals automatisch betreten, ausser ein Ausgang fuehrt
+    explizit dort hin.
+
+    Beispiel:
+
+    raum^0.c:
+    inherit "/std/room";
+
+    protected void create() {
+      ::create();
+      ...
+    }
+
+    raum.c:
+    inherit "raum^0";
+
+    protected void create() {
+      ::create();
+      AddItem("harmloses_monster",REFRESH_REMOVE);
+    }
+
+    raum^1.c:
+    inherit "raum^0";
+
+    protected void create() {
+      ::create();
+      AddItem("fieses_monster",REFRESH_REMOVE);
+    }
+
+
+SIEHE AUCH:
+    move(L), create_super(L), P_PARA(P)
+
+Letzte Aenderung: 18.01.2015, Bugfix
diff --git a/doc/wiz/parierwaffen b/doc/wiz/parierwaffen
new file mode 100644
index 0000000..64cb9bc
--- /dev/null
+++ b/doc/wiz/parierwaffen
@@ -0,0 +1,18 @@
+==============================================================================
+                 Regeln zur Programmierung von Parierwaffen:
+==============================================================================
+
+Standardobjekt: /std/weapon.c
+
+Siehe hierzu: /doc/wiz/balance   (oder 'man balance')
+
+Aktuelle Grenzwerte:
+====================
+
+   Parier-Typ   Genehmigungsgrenze
+  ------------ --------------------
+  PARRY_ONLY   Wie Rustungen vom Typ AT_SHIELD
+  PARRY_TOO    Generelle Genehmigungspflicht
+
+==============================================================================
+Paracelsus@MorgenGrauen, 18/08/1999
diff --git a/doc/wiz/potionmaster b/doc/wiz/potionmaster
new file mode 100644
index 0000000..6b8bfd2
--- /dev/null
+++ b/doc/wiz/potionmaster
@@ -0,0 +1,197 @@
+Der Potionmaster (/secure/potionmaster)
+=======================================
+
+BESCHREIBUNG
+
+    Funktion: Verwaltung der Zaubertraenke, die Spieler finden koennen.
+
+    Zaubertraenke koennen von beliebigen Objekten im Spiel vergeben werden.
+    Jeder Spruch ist in einer eigenen Datei in /secure/ARCH/ZT hinterlegt,
+    wobei der Dateiname das allgemeine Format <nr>.zt haben muss. <nr>
+    entspricht hierbei der ZT-Nummer.
+
+    Die Tips zu den Traenken werden vom Orakel /room/orakel ausgegeben,
+    sofern der Spieler genug Stufenpunkte fuer einen neuen Spruch erspielt
+    hat (siehe hierzu "hilfe zt"). Die Tips werden dabei aus den o.g. Dateien
+    eingelesen. Der ZT wird von dem vergebenden Objekt durch Aufruf von
+    FindPotion(string msg) im Spielerobjekt gutgeschrieben.
+
+    In dieser Datei sind die Funktionen des Potionmasters dokumentiert.
+    Inhalt:
+
+    1. Zaubertraenke eintragen und aktivieren/deaktivieren
+    2. Zaubertraenke in eine Liste einsortieren
+    3. Zaubertraenke verlegen
+    4. Daten zu Zaubertraenken abfragen
+    5. Ein neuer ZT: Was tun?
+    6. Format der Zaubertrank-Tips
+    7. Wo finde ich eigentlich?
+    8. Verschiedenes
+
+
+FUNKTIONEN
+
+1. Zaubertraenke eintragen und aktivieren/deaktivieren
+
+   int AddPotionroom(string room, int list)
+      Der Raum <room> wird als neuer ZT-Fundort eingetragen und in die Liste
+      mit der Nummer <list> eingefuegt. room muss hierbei der load_name() 
+      des Objekts sein, unt <list> darf Werte von 0 bis 7 haben. Der ZT
+      wird dabei aktiviert.
+      
+      Rueckgabewerte:
+      <num> Nummer des naechsten neuen ZTs, <num-1> ist die Nummer des
+            neu eingetragenen ZTs.
+      -1    Zugriff verweigert
+      -2    <room> kein String, <list> ist nicht im Bereich 0-7
+      -3    <room> ist nicht ladbar
+      -4    <room> vergibt schon einen (anderen) ZT
+      -6    Datei mit ZT-Spruch (/secure/ARCH/ZT/<num>.zt) existiert nicht.
+
+   int ActivateRoom(string room)
+      Der ZT im Raum <room> wird aktiviert, d.h. er kann aktiv von dem Raum
+      vergeben werden. Technisch wird er aus der Liste der inaktiven ZTs
+      ausgetragen. <room> muss hierbei der load_name() des Raumes sein.
+
+      Rueckgabewerte:
+      <num>  Nummer des aktivierten ZTs
+      -1     Zugriff verweigert
+      -5     ungueltiger ZT (Raum ist nicht als ZT-Objekt eingetragen)
+      -8     <room> ist bereits aktiviert
+
+   int DeactivateRoom(string room)
+      Der ZT im Objekt <room> wird deaktiviert, d.h. er kann nicht mehr
+      von dem Objekt vergeben werden. Technisch wird er in die Liste der
+      inaktiven ZTs eingetragen. <room> muss hierbei der load_name() des
+      Raumes sein.
+
+      Rueckgabewerte:
+      <num>  Nummer des geaenderten ZTs
+      -1     Zugriff verweigert
+      -5     ungueltiger ZT
+      -9     <room> ist bereits inaktiv
+
+2. Zaubertraenke in eine Liste einsortieren
+
+   int SetListNr(string room, int list)
+      Traegt den Zaubertrank in Raum <room> in die Liste <list> ein. 
+      <room> muss hierbei der load_name() des Objekts sein.
+
+      Rueckgabewerte:
+      <num>  Nummer des geaenderten ZTs
+      -1     Zugriff verweigert
+      -5     ungueltiger ZT
+      -7     <list> ausserhalb der zugelassenen Werte, d.h. <0 oder >7
+
+3. Zaubertraenke verlegen
+
+   int ChangeRoomPath(string old, string new)
+      Der Zaubertrank in Raum <old> wird in den Raum <new> umgetragen.
+
+      Rueckgabewerte:
+      <num> Nummer des erfolgreich geaenderten Zaubertranks
+      -1    Zugriff verweigert
+      -2    <new> oder <old> oder beide sind keine Strings
+      -3    <old> vergibt keinen ZT, es gibt also nichts zu verlegen, oder
+            <new> ist nicht ladbar, dorthin kann also nicht verlegt werden
+      -4    <new> hat schon einen Zaubertrank
+
+4. Daten zu Zaubertraenken abfragen
+
+   mixed QueryPotionData(int num)
+      Abfrage der Daten zu dem ZT mit der Nummer <num>. Ausgegeben wird ein
+      Mapping der Form ([ num : Raumpfad; Listennummer ])
+
+   int QueryInactivePotions()
+      Liefert ein Array mit den Nummern der aktuell deaktivierten ZTs zurueck.
+
+   int QueryActive(mixed potion)
+      Gibt zu einer ZT-Nummer oder einem Raumpfad an, ob der betreffende ZT
+      aktiv ist oder nicht. Wenn ja, wird die entsprechende ZT-Nummer
+      zurueckgegeben.
+      
+      Rueckgabewerte:
+      <num> Nummer des abgefragten ZTs
+      -1    Zugriff verweigert
+      -5    ungueltiger ZT
+      -11   ZT ist nicht aktiv
+   
+   string GetFilenameByNumber(int nr)
+      Abfrage des Dateinamens zu Zaubertrank Nummer <nr>.
+      
+      Rueckgabewerte:
+      <num> Nummer des abgefragten ZTs
+      -1    Zugriff verweigert
+      -5    ungueltiger ZT
+
+   int HasPotion(object obj)
+      Abfrage der Zaubertranknummer, die von dem Objekt <obj> vergeben wird.
+      
+      Rueckgabewerte:
+      <num> Nummer des abgefragten ZTs
+      -1    Zugriff verweigert
+      -3    Raum vergibt keinen ZT
+
+   int GetListByNumber(int nr)
+      Abfrage der Liste aktiver ZTs, in der der ZT mit der Nummer <nr>
+      eingetragen ist.
+      Rueckgabe: Nummer der Liste, in der der ZT enthalten ist, sonst -5.
+
+   int GetInactListByNumber(int nr)
+      Abfrage der Liste inaktiver ZTs, in der der ZT mit der Nummer <nr>
+      eingetragen ist.
+      Rueckgabe: Nummer der Liste, in der der ZT enthalten ist, sonst -10.
+
+5. Ein neuer ZT: Was tun?
+
+   AddPotionRoom(newroom, liste) aufrufen. Wenn ein Fehler auftritt, muss
+   noch der ZT-Tip in dem unter 6. beschriebenen Format hinterlegt werden.
+   Der zu verwendende Dateiname ist in der Fehlermeldung angegeben.
+
+6. Format der Zaubertrank-Tips
+
+   Ein Beispiel mit zwei Tips fuer denselben Zaubertrank:
+
+   Die Zaubermaus wollts wohl verstecken,
+   Im Buecherwald sollst nichts entdecken,
+   Denn Trinkgenuss ist dort verpoent,
+   So ist mans auch RL gewoehnt.
+   XXXXX
+   Der Raum ist prall und star gefuellt,
+   Mit viel Papier, das gern verhuellt
+   In Zauberbuchform manches Od,
+   Das hilft Dir dann aus Deiner Not.
+   %%%%%
+
+   Die Prozentzeichen sind das Endezeichen, danach koennen eventuelle
+   Kommentare stehen. XXXXX ist das Trennzeichen zwischen zwei Tips
+   zum selben ZT. Auswirkung ist, dass der Spieler bei jedem Aufruf
+   seiner Zaubertrank-Liste zufaellig einen Spruch aus der Liste 
+   angezeigt bekommt.
+
+7. Wo finde ich eigentlich?
+
+   - die Zaubertrank-Tips:           /secure/ARCH/ZT/*.zt
+   - die Gesamtliste der ZTs:        /secure/ARCH/POTIONS.dump
+   - das Savefile des Potionmasters: /secure/ARCH/potions.o
+   - das Logfile fuer Aenderungen:   /log/ARCH/POTIONS_MOD.log
+
+8. Verschiedenes
+
+   mixed TipLesen(int nr)
+      Das Orakel auf der Hochebene ruft die Zaubertrank-Tipps ab, diese
+      werden von der Platte gelesen und als String an das Orakel 
+      zurueckgeliefert.
+   int DumpList()
+      Die Komplettliste der ZTs in das Dumpfile /secure/ARCH/POTIONS.dump
+      schreiben.
+
+SIEHE AUCH:
+   Spielerbefehle: zaubertraenke
+   Magierbefehle:  traenke
+   Properties:     P_POTIONROOMS, P_KNOWN_POTIONROOMS
+   Anleitung:      wiz/zaubertraenke
+   ZT finden:      FindPotion(L)
+
+2013-Mai-30 Arathorn
+
diff --git a/doc/wiz/quests b/doc/wiz/quests
new file mode 100644
index 0000000..5c45023
--- /dev/null
+++ b/doc/wiz/quests
@@ -0,0 +1,160 @@
+Leitfaden fuer die Questerstellung/-abnahme:
+============================================
+
+
+Quests im MorgenGrauen:
+-----------------------
+
+Beginnen wir mit einem Zitat:
+
+  Boing sagt: 'Das MorgenGrauen ist ein Questmud.'
+
+Das MorgenGrauen unterscheidet sich von einigen anderen Muds, indem
+eben ein sehr grosser Wert auf Quests gelegt wird. Mittlerweile haben
+wir ja auch schon eine ordentliche Anzahl Quests. Trotzdem ist es
+wichtig und wird immer wieder gerne gesehen, wenn neue Quests geproggt
+werden. Darueberhinaus bleibt man als Magier natuerlich auch laenger
+im Spiel bekannt. Gerade weil Quests auch eine wichtige Anforderung
+zum Sehertum sind, muss ihnen ein besonderes Augenmerk seitens des
+programmierenden Magiers gewidmet werden und besonders sorgfaeltig
+gearbeitet werden. Dazu gibt es nun im folgenden ein paar Hinweise,
+die man als Questprogrammierer einhalten sollte, damit die Quest
+genehmigt werden kann und bei den Spielern gut ankommt.
+
+Questideen:
+-----------
+
+Dies ist der wichtigste Bestandteil einer Quest. Hier sollte man sich
+sehr viel Muehe geben, denn mit dem Konzept steht und faellt eine
+Quest. Damit Spieler Spass an einer Quest haben, ist eine gute Story,
+ein gutes Konzept unabdingbar. Ein wichtiger Erfolgsfaktor ist an
+dieser Stelle auch, sich rechtzeitig mit dem betroffenen Regionsmagier
+und dem Questerzmagier abzusprechen. Spaeter hat man da vielleicht
+keine Gelegenheit mehr und hat sich viel Arbeit gemacht und muss
+wieder viel aendern. Daher: VORHERIGER ABSPRACHE VERMEIDET
+AERGER. Insbesondere bei Quests mit hohem Metzelanteil sollte
+unbedingt Ruecksprache gehalten werden, da solche Quests nicht so
+gerne gesehen sind. 
+
+Questumsetzung:
+---------------
+
+Bei der Umsetzung kann man sich an die Regeln halten, die allgemein
+fuer den Anschluss von Gebieten gelten (NPCs, Balance...). Besondere
+Aufmerksamkeit verdienen die Teile, die zur Loesung der Quest
+notwendig sind (Questitems, Questmaster...)  Es empfiehlt sich auch in
+dieser Phase, immer mal Ruecksprache mit dem Regionsmagier oder
+Questerzmagier zu halten.
+
+Eine Questbelohnung wird von Spielern immer wieder gerne
+genommen. Dabei sollte man allerdings ein gewisses Augenmass
+anlegen. Nicht fuer jede Quest muss es eine super tolle
+Auto-Load-Belohnung geben. 
+
+Fuer das konkrete Umsetzen seien noch ein paar Hinweise gegeben:
+
+ - Fuer die Atmosphaere und die Details sollte man sehr viel Muehe
+   verwenden, so dass sich dem Spieler der logische Aufbau
+   erschliesst. Es ist fuer Spieler ausserordentlich aergerlich, eine
+   Quest zu spielen, deren Bestandteile irgendwie in der Luft
+   haengen. 
+ - Es sollte sich sehr gut ueberlegt werden, wie die Quest reagiert, 
+   wenn mehrere Spieler gleichzeitig die Quest spielen wollen (das
+   wird inbesondere direkt nach dem Anschluss der Fall sein).
+   Dies bezieht sich insbesondere auf Quest-Objekte, die einem Spieler
+   evtl. nicht zur Verfuegung stehen, weil sie gerade ein andere
+   genommen hat, oder Quest-NPCs, die wichtige Auskuenfte haben,
+   aber leider umgenietet wurden. Wichtige Quest-NPCs sollten nach
+   Moeglichkeit besser nicht als Metzelopfer konzipiert werden.
+ - Nach Moeglichkeit sollte es nicht das hundertste Labyrinth geben.
+ - Werden fuer die Quest allgemeine Objekte (wie Schaufeln, Seile
+   etc.) benoetigt, so sollten auch die an anderer Stelle im Mud
+   erhaeltlichen Objekte funktionieren oder es sollte fuer den Spieler
+   erkennbar sein, wieso nur ein spezielles Objekt den Zweck erfuellt.
+ - Questobjekte sollten fuer Spieler nicht erstellbar sein (->
+   Seherbeutel). 
+ - Wird der Questfortschritt in einem Master gespeichert, so empfiehlt
+   es sich, die Daten zu den Spielern nach dem Loesen der Quest wieder
+   zu loeschen (ebenso bei Spielern, die sich geloescht haben).
+ - Es sollten sich auch Gedanken gemacht werden, wie die Quest beim
+   Einschlafen, Beenden oder bei einem Crash reagiert. Es muss also
+   sichergestellt sein, dass der Spieler dann beim naechsten 
+   Questversuch nicht in einen inkonsistenten Zustand landet.
+ - Gut ist, den Questverlauf ein wenig variable zu gestalten, so
+   dass die Quest nicht ohne weiteres durchskriptbar ist.
+ - Bei Pflichtquests bitte beachten, dass NPCs, die zwingend zu toeten
+   sind, nicht zu stark sind.
+ - Das Objekt, welches die Quest nach dem Loesen setzen soll, muss einen 
+   Aufruf von GiveQuest enthalten. Desweiteren ist beim ersten Loesen 
+   in ein Questlogfile im Verzeichnis /log/quest ein Eintrag zu machen, 
+   dass der Spieler die Quest bestanden hat. Diesen Eintrag bitte 
+   mit write_file schreiben.
+  
+Questtest (Magier):
+-------------------
+
+Nun kommt ein entscheidender Schritt fuer die Quest. Denn jetzt
+schauen auch einmal Magierkollegen ueber die Quest und koennen auf
+Bugs, logische Fehler und kleinere Probleme hinweisen. Um moeglichst
+viele derartiger Dinge abzufangen, sollte man auf den Questtest viel
+Zeit verwenden und Magierkollegen bitten, die Quest einmal zu testen. 
+
+Questtest (Spieler):
+--------------------
+
+Wenn es moeglich ist, sollte auch ein Spieler die Quest einmal
+testen. Jedoch sind dazu die Regeln fuer Spielertesties ('man
+testies') einzuhalten. Es ist insbesondere darauf zu achten, dass
+wenn dem Testie eventuelle Forscherpunkte o.ae. zugesprochen werden,
+die negativen Seiten der Quest nicht wegfallen duerfen (soweit sie
+nicht auf Fehlern beruhen). 
+
+Abnahme der Quest:
+------------------
+
+Sofern noch nicht geschehen, muss nun der Regionsmagier die Quest
+abnehmen. (Ist der Programmierer selbst der Regionsmagier, sollte er
+einen Magierkollegen bitten, dies zu machen (fuer die meisten Regionen
+gibt es ja mehr als einen RM).) Wenn dies alles geschen ist, kann der
+Questerzmagier die Quest abnehmen. 
+
+Die Bearbeitung der Quest geht am schnellsten, wenn dem Quest-EM
+folgende Infos vorliegen (z.B. in einer Mail):
+
+ - Eine kurze Beschreibung der Quest, also worum geht es ueberhaupt?
+ - Eine Loesungsskizze (bitte nicht im Home-Verzeichnis rumliegen
+   lassen)
+ - Eine Beschreibung der technischen Loesung, also wie ist das ganze
+   programmiert? 
+ - In welchem Objekt wird die Quest mittels GiveQuest gesetzt?
+ - Eine Aufstellung, welche Files zu der Quest gehoeren und wo sie
+   sich befinden.
+ - Eine Liste der Abhaengigkeiten zu anderen Teilen im Mud? (Gilden-NPCs,
+   MNPCs..)
+ - Eine Einschaetzung der Schwierigkeit der Quest. Wie gut sollte man
+   sein? Welchen Level? Was fuer Stats?...
+ - Einen Vorschlag, wieviele APs fuer die Quest vergeben werden sollen.
+ - Einen Vorschlag, welchen Spruch der Wanderer den Spielern sagen
+   soll.
+ - Eine Liste der Magier und Spieler, die die Quest schon getestet haben.
+
+Danach schaut sich der Quest-EM die Quest an und legt mit dem
+Programmierer zusammen APs, Schwierigkeitsgrad, den Spruch fuer den
+Wanderer etc. fest. Dann wird die Quest vom Quest-EM eingetragen.
+
+Questanschluss:
+---------------
+
+Zum Anschluss der Quest sollte man nach Moeglichkeit ebenfalls online
+sein, um eventuelle Probleme, Bugs zu beseitigen. Auch sollte man nach
+Anschluss der Quest gewissenhaft sein Repfile abarbeiten um Typos,
+Ideen und Bugs abzuarbeiten.
+
+Siehe auch:
+-----------
+
+ QueryQuest, GiveQuest, write_file
+
+------------------------------------------------------------------------------
+Zuletzt geaendert: Mon, 17. Jul 2000, 12:16:41 von Zook.
+
diff --git a/doc/wiz/quests.doc b/doc/wiz/quests.doc
new file mode 100644
index 0000000..24c91c8
--- /dev/null
+++ b/doc/wiz/quests.doc
@@ -0,0 +1,66 @@
+ Das Questsystem wird in MorgenGrauen von einem zentralen Questhandler
+gesteuert. Dieser stellt die folgenden Funktionen zur Verfuegung:
+
+AddQuest(string questname, int questpoints, int experience,
+         string *allowedobj, string hint, int difficulty, int needed)
+ Diese Funktion definiert eine Quest und gibt sie zur Benutzung durch die
+ Spieler frei. Sie darf nur von Erzmagiern aufgerufen werden.
+ Bedeutung der Parameter:
+   questname gibt den Namen der zu definierenden Quest an. Es darf bereits
+             eine Quest dieses Namens geben, ihre Parameter werden dann
+             geaendert.
+   questpoints gibt die Zahl der Questpunkte an, die der Spieler fuer die
+               Loesung dieser Quest angerechnet bekommt. Muss >0 sein.
+   experience gibt die Zahl der Erfahrungspunkte an, die der Spieler fuer
+              eine Quest bekommen kann. DIESE ZAHL KANN <0 SEIN!
+   allowedobj ist ein Array mit den Filenamen der Objekte, die diese Quest als
+              durch einen Spieler geloest kennzeichnen duerfen. Darueberhinaus
+              duerfen Erzmagier dies immer tun.
+   hint ist ein String, der Tips zur Loesung der Quest enthaelt. Dieser String
+        wird dem Spieler vom Orakel als Hinweis gegeben.
+   difficulty ist eine Zahl zwischen 0 und 20, die den "Schwierigkeitsgrad"
+              der Quest angibt. 0 hat eine besondere Bedeutung, naemlich die,
+              das keine Einschaetzung vorliegt.
+   needed legt fest, ob die Quest von einem Spieler geloest werden muss, be-
+          vor er Magier werden kann. Falls needed !=0 ist, MUSS er die Quest
+          loesen, unabhaengig von der 90%-Regel.
+
+RemoveQuest(string questname);
+ Gegenstueck zu AddQuest, loescht eine Quest. Kann natuerlich ebenfalls nur
+ von Erzmagiern aufgerufen werden. DIE SPIELER, DIE DIE QUEST SCHON GELOEST
+ HABEN, BEHALTEN DIE ENTSPRECHENDEN QUESTPUNKTE !!
+
+QueryReadyForWiz(object player)
+ Dieser Funktion muss ein Playerobjekt uebergeben bekommen und prueft, ob
+ der Spieler seitens der Quests bereit ist zur Aufstufung zum Magier, dh
+ ob er 90% der QP sowie alle zwingend vorgeschriebenen Quests (siehe
+ AddQuest, Parameter needed) geloest hat. Falls dies der Fall ist, liefert
+ die Funktion eine 1 zurueck. Wenn er die 90% nicht hat, eine -1. Falls
+ ihm noetige Quests fehlen, eine Liste der nicht geloesten, noetigen Quests.
+
+QueryQuest(questname)
+ Liefert eine -1, falls keine Quest dieses Names eingetragen ist, sonst
+ einen Array mit den Daten der Quest, in der Reihenfolge, in der sie in
+ AddQuest eingegeben werden. Dabei ist questpoints das erste Arrayelement.
+
+QueryAdvanceExp(object player)
+ Stellt fest, ob der Spieler player genuegend Questpunkte hat, um seine
+ Erfahrung zu erhoehen.
+
+-----------------------------------------------------------------------------
+ Weiterhin enthaelt jedes Playerobjekt ein Quest-Modul, das die folgenden
+Funktionen offeriert:
+
+GiveQuest(string questname)
+ Markiert eine Quest bei dem Player als geloest. Es wird getestet, ob die
+ Aktion von einem "allowed_object" vorgenommen wird. Die Questpunkte werden
+ entsprechend geupdated.
+
+QueryQuests()
+ Gibt eine Alist mit den Namen der vom Player geloesten Quests zurueck.
+
+QueryQP()
+ Gibt die Anzahl der vom Player erreichten Questpunkte zurueck.
+
+QueryQuest(string questname)
+ Stellt fest, ob ein Spieler die Quest geloest hat oder nicht.
diff --git a/doc/wiz/regionsleitfaden b/doc/wiz/regionsleitfaden
new file mode 100644
index 0000000..0528d02
--- /dev/null
+++ b/doc/wiz/regionsleitfaden
@@ -0,0 +1,200 @@
+
+Hallo, willkommen in den unendlichen Weiten der Regionsverzeichnisse!
+
+Du moechtest in einer Region mitprogrammieren? Prima, neue kreative 
+Mitarbeiter sind jederzeit willkommen! Du bist herzlich eingeladen, mit 
+Deinen Ideen zur Entwicklung beizutragen.
+
+Um die Programmierung und anschliessende Abnahme fuer alle Beteiligten
+moeglichst reibungslos zu gestalten, seien Dir die in dieser Hilfeseite
+genannten Dinge ans Herz gelegt. Diese lassen sich in folgende Kategorien
+einteilen:
+
+1) Formales zum Codestil
+2) Inhaltliche Anforderungen
+3) Was nicht akzeptiert wird
+4) Was ist vor dem Anschluss zu beachten?
+
+
+1) Formales zum Codestil
+
+o  #pragma strong_types,save_types soll in allen Files verwendet werden,
+   ab Driver-Version LD_3.5.x wird auch rrtt_checks dringend empfohlen.
+
+o  Der Code soll keine Zeilen mit mehr als 78 Zeichen enthalten.
+
+o  Der Code soll sauber eingerueckt und sorgfaeltig formatiert sein, aber
+   bitte ohne Tabulatoren.
+
+o  Verwende keine Lambda-Closures! Was auch immer Du vorhast: Es geht
+   ohne. Es sei ausdruecklich auf die Moeglichkeit von inline-Closures
+   verwiesen, wenn Du unbedingt vermeiden willst, eine separate Funktion
+   zu schreiben.
+
+o  Kommentiere Deinen Code! Insbesondere dort, wo komplexere Objekt-
+   Interaktionen stattfinden, oder wo implizit besondere Eigenschaften
+   (z.B. der Mudlib, oder mathematische "Features") genutzt werden, die im
+   Code nicht auf den ersten Blick ersichtlich oder durchschaubar sind.
+   Daumenregel: "Wenn Du laenger als eine Minute ueber eine Zeile nachdenken
+   musstest, kommentiere sie." ;-) Rechne immer damit, dass jemand, der
+   Deinen Code liest, keine Ahnung hat, was Du da eigentlich tust. :-)
+
+o  Wirf bitte nach Abschluss der Arbeiten Platzhalter-Code raus (z.B. leere
+   AddDetail()-Anweisungen) und entferne nicht fuer das Gebiet benoetigte
+   Dateien aus den Verzeichnissen.
+
+o  Speicherung von Daten in secure-Verzeichnissen soll bitte nur sehr
+   sparsam erfolgen und nur in Abstimmung mit dem RM.
+
+o  save_object() bitte sehr sparsam verwenden (nicht bei jeder Daten-
+   aenderung, bei Bedarf in reset/remove).
+
+o  Wenn Defines zum Einsatz kommen, verwende sie bitte moeglichst sparsam
+   und sorge dafuer, dass Defines klar als solche erkennbar sind. Ausser in
+   Faellen, wo es gar nicht anders geht, solltest Du keine Code-Defines
+   verwenden, die mehr umfassen als einfache Funktionsaufrufe wie z.B.
+      #define TP this_player()
+   Fuer uebliche Standardfaelle existiert bereits eine Headerdatei in der 
+   Mudlib unter /sys/defines.h. 
+
+o  Solltest Du bestimmte Ereignisse in Deinem Gebiet loggen wollen (z.B.
+   (Mini-)Questabschluesse oder besondere Kills), dann benutze bitte 
+   log_file(), so dass die Logfiles nach /log/ geschrieben werden. Zusaetzlich
+   werden so erstellte Logfiles automatisch bei Erreichen einer bestimmten
+   Dateigroesse rotiert, so dass sich der Platzverbrauch in Grenzen haelt.
+   Das Protokollieren mittels write_file() in Regionsverzeichnissen unter 
+   /d/ ist grundsaetzlich NICHT erwuenscht. 
+   Nach Absprache KANN es fuer SELTENE und WICHTIGE Meldungen erlaubt werden,
+   mittels write_file(() nach /log/ zu schreiben.
+
+o  Wenn Du in Deinem Gebiet Daten oder Code ablegst, der nicht fuer 
+   jedermanns Augen bestimmt ist (Questloesungen, Gebietskarten, Savefiles
+   von questrelevanten (Master-)Objekten), solltest Du in Abstimmung mit
+   Deinem Regionsmagier ueberlegen, diese in ein ./secure/-Verzeichnis
+   zu verschieben, damit sichergestellt ist, dass auch tatsaechlich nur
+   berechtigte Magier darauf Zugriff erhalten. Denn bedenke, dass Lese-
+   und Schreibrechte nur fuer Codedateien geprueft werden, jedoch nicht
+   fuer beliebige sonstige Textdateien.
+
+o  Es sei ausdruecklich auf die Manpages "goodstyle", "effizienz", etc.
+   verwiesen.
+
+
+Das soll jetzt nicht heissen, dass der Anschluss von Code kategorisch
+abgelehnt werden wird, der diese Formalien nicht erfuellt. (Ausnahme: Lambda-
+Closures werden in den Regionen nicht mehr akzeptiert.) Ich moechte aber
+wirklich nachdruecklich darum bitten, sie aus einem einfachen Grund
+einzubauen: Du wirst nicht immer der einzige sein, der Deinen Code lesen und
+warten muss, auch in Deiner Abwesenheit koennen Bugs auftreten. Und dann ist
+es wesentlich einfacher, einen Minimalstandard zu haben, der es allen
+ermoeglicht, den Code auch im MG zu lesen und dort zu fixen. Denn nicht immer
+wird es moeglich sein, sich Dateien herunterzuladen und lokal zu bearbeiten.
+
+Zum Bugfixing an dieser Stelle aus aktuellem Anlass eine Anmerkung: Es wird
+von jedem aktiven Regionsmitarbeiter erwartet, dass er einen Fehlerteufel
+(/obj/tools/fehlerteufel) besitzt und dessen Fehlerliste regelmaessig
+durchsieht und aufgetretene Fehler und Warnungen behebt. (Okt. 2007)
+
+
+2) Inhaltliche Anforderungen:
+
+o  Wenn Du ein komplett neues Gebiet schreiben moechtest, das in einer Region 
+   seinen Platz finden soll, sprich bitte die thematische Ausrichtung mit
+   dem RM ab, bevor Du anfaengst, Code zu schreiben. Falls von Deinen Ideen
+   irgendetwas nicht hierher passen sollte, laesst sich das mit wesentlich
+   weniger Frust im Vorfeld klaeren, als wenn hinterher das halbe Gebiet
+   umgebaut werden muesste. Sollte sich die konzeptionelle Ausrichtung 
+   oder der Umfang waehrend der Programmierung grundlegend aendern, besprich
+   dies bitte kurz mit dem RM.
+
+o  Ob neue oder alte Rechtschreibung, ist im wesentlichen Dir selbst ueber-
+   lassen, jedoch waere es schoen, wenn Du die Spieler-Anreden wie "frueher"
+   ueblich gross schreiben wuerdest ("Du", "Dich", "Dein").
+
+o  Es muessen in jedem Raum gewisse Standarddetails vorhanden sein, z.B.
+   Boden, Decke, Waende, Himmel (Sonne, Wolken), etc. Dies kann man sehr
+   bequem mit dem "Otester" pruefen.
+   Es sei aber ausdruecklich darauf hingewiesen, dass der Otester keinesfalls
+   benutzt werden sollte, um saemtliche vorhandenen Substantive bis ins
+   kleinste zu beschreiben. Es ist schoen, wenn Objekte moeglichst voll-
+   staendig beschrieben werden, aber man sollte es auch nicht uebertreiben.
+
+o  Was bitte nur in Ausnahmefaellen gemacht werden sollte, ist, Details, 
+   Infos oder Lang-/Kurzbeschreibungen aus Standardraeumen zu inheriten.
+
+o  Falls Du Beschreibungen/Ausgaben in ASCII-Grafik einbinden moechtest, achte
+   bitte darauf, dass Du auf jeden Fall einen kurzen Alternativtext mit-
+   lieferst und diesen ausgibst, wenn der Spieler P_NO_ASCII_ART gesetzt hat.
+   Es besteht immer die Moeglichkeit, dass Spieler mit Sehschwaeche oder
+   Blinde Deine Objekte anschauen, und diese kommen mit Objekten in reiner
+   ASCII-Grafik nicht zurecht.
+   (Siehe auch die Manpage zu P_NO_ASCII_ART und "hilfe grafik".)
+
+o  Eine Anmerkung zu den Schadensarten DT_HOLY und DT_UNHOLY soll nicht
+   unerwaehnt bleiben: Es wird von vielen Spielern und Magiern als logisch
+   und atmosphaerisch nicht begruendbar empfunden, wenn Gegenstaende oder
+   NPCs diese beiden Schadensarten gleichermassen einsetzen bzw. verursachen
+   koennen. Vergleichbares gilt fuer die gleichzeitige Abwehr beider 
+   Schadensarten. Wenngleich bisher diesbezueglich keine klare Einigung
+   erzielt werden konnte, soll an dieser Stelle dennoch empfohlen werden,
+   von einer gleichzeitigen Nutzung von DT_HOLY und DT_UNHOLY abzusehen.
+   Der zustaendige Regionsmagier muss aber auf jeden Fall in Kenntnis
+   gesetzt werden und entsprechende Objekte zur Pruefung vorgelegt bekommen,
+   falls diese Nutzung dennoch fuer unumgaenglich gehalten wird.
+
+
+3) Was keinesfalls akzeptiert wird
+
+o  Files, die in /players liegen, incl. Headerfiles. Dies erschwert das
+   Reparieren von Bugs ungemein und bringt eine gewisse Unuebersichtlichkeit
+   mit sich. Bitte auch nichts aus /players inheriten (Ausnahmen hiervon sind 
+   aufgrund eines neuen Driver-Features nur noch mit Hilfe eines Erzmagiers 
+   moeglich. Kurz gesagt: es gibt eine Whitelist von Objekten, die das 
+   duerfen. Alle anderen duerfen das per Default nicht.).
+
+
+4) Was ist vor dem Anschluss zu beachten?
+
+o  Code muss vollstaendig fertig sein. Das heisst insbesondere, dass 
+   Debug- und nicht mehr benoetigter Code entfernt wurde und dass alle 
+   erforderlichen Kommentare vorhanden sind.
+
+o  Abnahme durch den RM und ggf. den fuer Gesellenstuecke zustaendigen EM
+   muss erledigt sein. Regionsmagier sind angehalten, eigene Gebiete, die zum
+   Anschluss in der eigenen Region geplant sind, von anderen Magiern abnehmen
+   lassen.
+
+o  Der Magier ist angehalten, das Gebiet durch Testspieler testen zu lassen
+   (siehe hierzu die Testspieler-Regeln). Ob ein Spieltest als Voraussetzung
+   fuer die Abnahme gefordert wird, entscheidet der zustaendige RM. Er kann
+   den Test auch selbst durchfuehren oder ganz darauf verzichten.
+
+o  Quests und Miniquests muessen vom zustaendigen Quest-EM getestet,
+   eingetragen und aktiviert worden sein.
+
+o  Feedback der Testspieler und testenden Magier sollte umgesetzt sein.
+
+o  Forscherpunkte muessen vom zustaendigen EM eingetragen worden sein.
+
+o  Balance-Genehmigungen (Objekte, Heilung, ggf. Gildenbalance) muessen 
+   vorliegen; die genehmigten Eckparameter sollen nachvollziehbar im 
+   Gebietsverzeichnis dokumentiert sein: entweder im jeweiligen File, oder
+   als Textfile z.B. in einem Doku-Verzeichnis.
+
+o  Kraeuter fuer den Kraeuterskill muessen genehmigt und eingetragen worden 
+   sein (dies kann jeder EM mit dem Kraeutertool erledigen).
+
+o  Hinweis: Erstkills muessen NICHT ZWINGEND vom zustaendigen EM eingetragen
+   worden sein. Die Erstkill-NPCs werden automatisch in einer vorlaeufigen
+   Liste gesammelt und vom EM entweder freigegeben oder abgelehnt. Auch
+   Sonder-Stufenpunkte fuer EKs muessen nicht vor dem Anschluss beantragt
+   oder genehmigt worden sein, auch diese Eintragung laesst sich erledigen,
+   wenn der betreffende NPC das erste Mal getoetet wurde.
+
+o  Auch die fuer den Ruestungsskill so beliebten Ruestungen muessen nicht
+   vor dem Anschluss eingetragen werden. Neue Ruestungen werden automatisch
+   beim zustaendigen Masterobjekt angemeldet, sobald sie das erste Mal
+   repariert werden.
+
+Und nun viel Spass bei der Programmierung! 
+
diff --git a/doc/wiz/regionsmagier b/doc/wiz/regionsmagier
new file mode 100644
index 0000000..fae5f9f
--- /dev/null
+++ b/doc/wiz/regionsmagier
@@ -0,0 +1,108 @@
+
+Regionsmagier
+=============
+
+Vorbemerkung: Dieses Dokument ist nicht auf dem aktuellen Stand,
+Anregungen dazu nehme ich, Zook, gerne entgegen. Ich werde das
+Dokument dann nach und nach erweitern, berichtigen.
+
+    Hier sind einige Verhaltensregeln fuer Regionsmagier zusammengetragen. Sie
+    sollten tunlichst beachtet werden, damit es nicht zu Aerger und
+    Enttaeuschungen fuer Spieler und Magier kommt.
+
+    Einstellen von neuen Mitarbeitern:
+
+     * Man sollte dem neuen Magier *vorher* sagen, was fuer Richtlinien fuer
+       die Region gelten.
+     * Der neue Magier sollte zuerst seine Plaene erlaeutern und mit dem RM
+       absprechen, ob das in die Region passt.
+     * Zum Aufnehmen in die Region einfach ein Verzeichnis mit dem Magiernamen
+       anlegen, dann zu Merlin gehen und sagen
+
+       merlin mach <xxx> zum magier / merlin mach <xxx> zur magierin
+
+    Zum Testen von neuen Gebieten:
+
+     * Generell gilt: ERST testen, dann anschliessen.
+     * Logik beachten!
+     * Syntax von Befehlen beachten, kann der Spieler draufkommen?
+     * Objekte pruefen, genaueres siehe unten beim Punkt Balance.
+     * Jedes Monster kurz umhauen (nicht zappen), geht prima mit einem
+       entsprechend hohen Wert fuer P_HANDS. Dann `tail /log/NPC_XP' und die
+       XP-Vergabe ueberpruefen.
+     * Auch den Code anschauen, unnoetige oder schlecht programmierte Dinge
+       bemaengeln, natuerlich mit Verbesserungsvorschlag.
+     * typischer Fehler: darauf achten, ob beim Bewegen von Objekten in den
+       Spieler der Rueckgabewert von move() ueberprueft wird und die
+       entsprechenden Massnahmen ergriffen werden, wenn der Spieler nichts
+       mehr tragen kann.
+     * Bei sich bewegenden Monstern mit dem Magier besprechen, ob das wirklich
+       noetig ist, bzw. ob man den Takt verlaengern kann. Lauf-NPCs sind
+       CPU-Fresser.
+     * Vor dem Anschluss MUESSEN Forscherpunkte eingetragen werden. Das macht
+       der entsprechende Erzmagier, momentan ist das Rikus. Naeheres dazu
+       steht unter `hilfe forscherpunkte'.
+
+    Ein paar grobe Regeln zur Balance:
+
+     * Dies sind Richtlinien, die evtl. nicht mehr aktuell sind. Die aktuellen
+       Regeln, die von den Magiern befolgt werden muessen, stehen im
+       Verzeichnis `/doc/REGELN' sowie insbesondere in `/doc/wiz/balance'.
+     * gute Waffen (WC ueber 190) sollten nur selten ins Spiel kommen, das
+       heisst es sollte keine 5 NPCs auf einem Haufen mit solchen Waffen geben
+       (als Beispiel). Ausserdem sollten diese Waffen schwer zu erlangen sein.
+     * sehr gute Waffen (200 oder mehr) sollten sehr selten sein und sehr
+       schwierig zu bekommen. Extrem gute Waffen koennen ruhig existieren,
+       allerdings sollten sie dann einmalig sein (Beispiel: Hauruck).
+     * Artillerie: Gibt es schon mehr als genug, sollte eigentlich nur noch in
+       extremem Ausnahmefaellen neu reinkommen. Als Artillerie bezeichne ich
+       alles, was im Kampf zusaetzlich hilft (Flammenkugeln, Wurfsterne,
+       Eisstab).
+     * Heilung: tragbare Heilung ist generell nicht erlaubt, allerdings kann
+       man Ausnahmen machen bei entsprechenden Nachteilen, z.b. hohe
+       Saettigung, gleichzeitige Vergiftung, Abhaengigkeit ... der Fantasie
+       sind keine Grenzen gesetzt.
+     * Ausgeglichenheit: Darauf achten, dass nicht nur Mega-Monster rumlaufen,
+       sondern fuer jeden etwas dabei ist. Dabei sollten harte Monster nicht
+       ohne weiteres zu erreichen sein; man sollte spuerbaren Widerstand
+       ueberwinden muessen, bevor man sie erreicht.
+
+Programmhierhinweise: [Ergaenzung vom 2003-03-06, Zook]
+
+Bitte achtet darauf, dass beim Programmieren Eurer Regionsmitarbeiter
+gewisse Standards eingehalten werden und dass typische Fehler vermieden
+werden. Im folgenden ein paar Hinweise:
+
+ * Bei Laeden bitte darauf achten, dass nicht unnoetig "verkaufe" mit
+   einem AddCmd ueberschrieben wird, wenn statt dessen buy_obj() oder
+   sell_obj() verwendet werden kann.
+   Um Laeden besser anzupassen, kann dort auch P_GENDER, P_NAME und
+   P_ARTICLE gesetzt werden.
+ * Bei Waffen kann und sollte die Funktion "is_unsafe" verwendet werden,
+   wenn NPCs die Waffe nicht zuecken sollten. Z.B. liefert einen 
+   Runenschwert bei Elfen-NPCs eine 1 zurueck, sonst eine 0.
+ * Es gibt eine Property P_PLURAL. Die sollte man auch verwenden. 
+   Siehe dazu die Hilfeseite.
+ * Zum Hantieren mit Gegenstaenden stellt put&get einige komfortable
+   Funktionen zur Verfuegung: pick_obj(), drop_obj(), put_obj(), 
+   give_obj().
+   Um Objekte auszuwaehlen kann find_obs() genutzt werden.
+   Prueft ein Objekt, ob es selbst gemeint ist (z.B. in einem AddCmd)
+   bitte pruefen, ob id() verwendet werden sollte.
+   
+
+    Sonstiges:
+
+     * Wenn man ein Verzeichnis unter /d/<region> anlegt, muss man darauf
+       achten, dass der entsprechende Name gebanisht wird, sonst ist ein
+       Spieler der diesen Namen hat automatisch Mitarbeiter der Region (das
+       betrifft Verzeichnisse, die nicht fuer Regionsmitglieder, sondern fuer
+       andere Aufgaben gedacht sind).
+     * Ein Name wird gebanisht indem man in die Gilde geht und dort `banish
+       <name>' eingibt. Bitte keine direkten Funktionsaufrufe im master!
+
+ SIEHE AUCH:
+    forscherpunkte, balance, banish
+
+ LETZTE AeNDERUNG:
+    Don,  6. Feb 2003, 13:30:56 von Zook.
diff --git a/doc/wiz/reputation b/doc/wiz/reputation
new file mode 100644
index 0000000..6e1ac20
--- /dev/null
+++ b/doc/wiz/reputation
@@ -0,0 +1,164 @@
+THEMA:
+  Reputationen
+
+
+ALLGEMEINE BESCHREIBUNG:
+  Man kann bei einer Gruppierung/Volk/Partei/etc. Ruf gewinnen oder verlieren 
+  und somit entweder deren Respekt erlangen oder auf ihrer Abschussliste 
+  landen.
+
+  Am Beispiel der Goblins im Walddorf Skoga: Dort gibt es das Freie Volk, das 
+  aus seinem Heimatdorf vertrieben wurde, und die Goblins, welche das Dorf
+  besetzt haben. Hilft man einer Seite, baut man Ruf auf und erhaelt 
+  beispielsweise Zugang zu Heilstellen und speziellen Items, waehrend die 
+  andere Seite zunehmend misstrauisch reagiert und einen schlussendlich ohne 
+  zu fragen attackiert.
+
+  Es muessen nicht immer mehrere Seiten involviert sein. Auch eine einzelne 
+  Partei ist denkbar, der man helfen kann und in deren Laeden/Kneipen man mit 
+  steigendem Ruf z.B. Verguenstigungen, mehr Informationen, Bonusitems und 
+  Aehnliches erhaelt.
+
+  Denkbar ist es auch, das Ganze zu vernetzen. Beispielsweise koennte ein 
+  Verwandter eines Mitglieds von obengenanntem Freien Volk vor den Angriffen 
+  ins Polargebiet umgezogen sein, aber von des Spielers Heldentaten gehoert 
+  haben und ihm so freundlicher gesonnen sein.
+
+
+METHODEN (s. a. einzelne Manpages):
+
+  int ChangeReputation(string repid, int value, int silent)
+      Die Funktion ist in jedem Spielerobjekt vordefiniert.
+      Vor der Aenderung wird ein Check auf die UID des ausfuehrenden Objektes
+      ausgefuehrt, "fremde" Reputationen darf man somit nicht veraendern.
+      Man kann aber selbstverstaendlich in begruendeten Faellen mit dem
+      zustaendigen Magier/Regionsmagier sprechen, ob man ebenfalls Zugriff
+      erhaelt. Eingetragen wird dies schlussendlich durch einen EM.
+
+
+  int GetReputation(string repid)
+      Ebenfalls im Spielerobjekt vordefiniert. Liefert den aktuellen Wert der
+      angegebenen Reputation zurueck.
+
+
+  mapping GetReputations()
+      Ebenso vordefiniert. Liefert ein Mapping aller im Spieler gespeicherten
+      Reputationen und ihrer Werte zurueck.
+
+
+BEISPIELE:
+
+  // Eine kleine Aufgabe fuer das "Freie Volk" bringt dem Spieler etwas 
+  // Ansehen.
+
+  void QuestGeloest(object pl) {
+    // Silent, wir geben eine eigene Meldung aus.
+    pl->ChangeReputation("freiesvolk", 250, 1);
+
+    tell_object(pl, "Du befreist einen Gefangenen. Das freie Volk wird es "
+      "Dir danken.\n");
+  }
+
+  // Ein NPC des "Schaedlspalta-Klans". Wenn er getoetet wird, verlieren die 
+  // toetenden Spieler Ruf bei dieser Fraktion.
+ 
+  #include <properties.h>
+
+  inherit "/std/npc.c";
+
+  void create() {
+    npc::create();
+    SetProp(P_SHORT, "Ein Goblin des Schaedlspalta-Klans");
+    ...
+  }
+
+  varargs void die(int poison, int ext) {
+    object *enemies;
+    int value;
+    // Begleit-NPCs brauchen keine Reputation
+    enemies = filter(PresentEnemies(), #'interactive);
+    // 50 Reputationsabzug pro Kill, aufgeteilt auf alle Gegner
+    value = -50 / sizeof(enemies);
+    foreach(object pl : enemies) {
+      pl->ChangeReputation("schaedlspalta", value);
+      // Optional koennte ihre "Feindfraktion" das gutheissen:
+      // pl->ChangeReputation("freiesvolk", abs(value));
+    }
+    return npc::die(poison, ext);
+  }
+
+  // Jeder beteiligte Feind kriegt nun eine Meldung, die in etwa lauten 
+  // koennte: "Dein Ansehen beim Schaedlspalta-Klan hat sich etwas 
+  // verschlechtert." Der NPC koennte nun Spieler automatisch angreifen, 
+  // sobald ihr Ruf zu tief gesunken ist:
+  
+  #include <reputation.h>
+
+  void init() {
+    npc::init();
+    if(objectp(this_player()) && !IsEnemy(this_player()) && 
+       this_player()->GetReputation("schaedlspalta") <= REP_DISLIKED)
+      InsertEnemy(this_player());
+  }
+
+  // Ein NPC rueckt bestimmte Informationen erst raus, sobald das Ansehen
+  // des Spielers hoch genug ist:
+
+  #include <properties.h>
+  #include <reputation.h>
+
+  inherit "/std/npc.c";
+
+  string InfoGeheimplan();
+
+  void create() {
+    npc::create();
+    SetProp(P_SHORT, "Der Kommandant des freien Volkes");
+    ...
+    AddInfo("geheimplan", #'InfoGeheimplan, "sagt: ");
+  }
+
+  string InfoGeheimplan() {
+    if(this_player()->GetReputation("freiesvolk") < REP_TRUSTED)
+      return "Das geht Dich ueberhaupt nichts an!";
+    return "Nun, ich vertraue Dir. Also, heute um Mitternacht ... ... ...";
+  }   
+
+  // Pruefung der (wichtigsten) Rueckgabewerte:
+
+  #include <reputations.h>
+  
+  void QuestGeloest(object pl) {
+    string msg;
+
+    // Sonstige Meldungen
+    ...
+
+    switch(pl->ChangeReputation("freiesvolk", 500, 1)) {
+      // Reputation bereits Max
+      case REP_RET_ALREADYMAX:
+        msg = "Dein Ansehen beim freien Volk kann sich nicht mehr weiter "
+              "verbessern.";
+        break;
+      // Reputation waere hoeher als Max geworden, daher auf Max gesetzt
+      case REP_RET_SUCCESSCUT:
+        msg = "Dein Ansehen beim freien Volk hat sich etwas verbessert, "
+              "aber weiter steigern kannst Du es nicht mehr.";
+        break;
+      // Reputation erfolgreich geaendert
+      case REP_RET_SUCCESS:
+        msg = "Dein Ansehen beim freien Volk hat sich etwas verbessert.";
+        break;
+      default:
+        // Technischer Fehler
+        break;
+    }
+
+    if(stringp(msg) && strlen(msg))
+      tell_object(pl, break_string(msg, 78));
+  }
+
+SIEHE AUCH: 
+
+LETZTE AENDERUNG:
+  2009-22-03, 12:30:00 von Nibel
diff --git a/doc/wiz/rm-howto b/doc/wiz/rm-howto
new file mode 100644
index 0000000..75b9ac7
--- /dev/null
+++ b/doc/wiz/rm-howto
@@ -0,0 +1,334 @@
+RM - HOWTO
+**********
+
+Vorlaeufiges Inhaltsverzeichnis:
+
+1) Allgemeines
+   - Fehlerteufel
+   - Logtool
+   - Kommentierung von Aenderungen
+   - eigene Anschluesse
+2) Abnahme von Code/Gebieten
+   - Balance/Genehmigungen
+   - Konzeptionelle Eignung
+   - formale Pruefung
+   - Gemeinschaftsarbeiten
+3) Verlegung von Dateien
+4) Seherhaeuser
+   - Sonderwuensche/Unsichtbarkeit
+   - anderer Befehl zum Betreten
+   - Verweise auf Beispielcode
+5) Debuggen von Code
+6) besondere Funktionen/Funktionaelitaeten
+   - catch_tell() / ReceiveMsg()
+   - move()
+   - Attack() / Defend()
+   - call_out()
+   - remove() / destruct()
+   - for()-Schleifen
+   - write_file()
+   - Verwalten von charakterbezogenen Daten
+
+1) Allgemeines
+==============
+
+o Um stets einen Ueberblick ueber die in Deiner Region (hoffentlich selten)
+  auftretenden Bugs und Warnungen zu behalten, solltest Du einen
+  Fehlerteufel (/obj/tools/fehlerteufel.c) besitzen, bedienen koennen und
+  regelmaessig dessen Listen durchsehen. Deinen Regionsmitarbeitern solltest
+  Du, allein schon, um zusaetzliche Arbeit fuer Dich selbst zu reduzieren,
+  dieses Werkzeug ebenfalls an die Hand geben und sie auffordern, ihren
+  Kram moeglichst umfassend selbst zu reparieren.
+  Neuen Magiern wird dieses Tool uebrigens automatisch von Merlin in die
+  Hand gedrueckt, so dass sie sich vom ersten Tag an daran gewoehnen koennen.
+
+o Es ist empfehlenswert, das Logtool zu besitzen (/obj/tools/logtool.c) und
+  in diesem fuer jedes Repfile eines Regionsmitarbeiters einen Eintrag vor-
+  zusehen. Warum das? Es ist bereits vorgekommen, dass Spieler ernsthaft
+  argumentiert haben, sie duerften einen von ihnen entdeckten Bug ausnutzen,
+  weil sie ihn ja gemeldet haetten (im Repfile!), er aber nicht repariert
+  worden sei (was nicht verwundert, da er im Repfile in der Mehrzahl der
+  Faelle weit unterhalb des Wahrnehmungshorizonts der allermeisten
+  Regionsmagier schwebt).
+
+o Wenn Du in fremdem Code Aenderungen vornehmen musst, die mehr beruehren
+  als nur ein paar Tippfehler in Details oder Infos, dann kommentiere den
+  defekten Code aus und fuege den reparierten mit Kommentar ein. Es reicht
+  an dieser Stelle aus, Deinen Namen und das Datum zu vermerken. In den
+  Header der Datei solltest Du unbedingt ein Changelog einfuegen, in dem
+  das Datum der Aenderung, der Name des Ausfuehrenden (Deiner) sowie die
+  vorgenommene Aenderung mit Begruendung bzw. u.U. Bug-ID aus dem Fehler-
+  teufel einzutragen ist. Wir haben im MG leider keinen wirkungsvollen
+  Debugger und Bugtracker-Mechanismus, so dass wir zur Erhaltung einer
+  gewissen Code-Hygiene Bugfixes mit Hilfe solcher Eintragungen dokumentieren
+  und rueckverfolgen muessen. Insbesondere ist ein solcher Eintrag wichtig,
+  um im Falle von Folge-Bugs die Ursache schneller zu finden.
+
+o Willst Du in Deiner eigenen Region ein Gebiet anschliessen, solltest Du
+  diesen vor Anschluss entweder vom Co-RM oder von einem RM einer anderen
+  Region lesen lassen. Auch wenn Du ein guter Programmierer bist, findet ein
+  zweites Paar Augen oft Dinge, an die man nicht gedacht hat.
+
+
+2) Code wird zur Abnahme vorgelegt
+==================================
+
+o Bei Waffen und Ruestungen unbedingt auf Einhaltung der Balance-Vorgaben
+  achten.
+
+o Sofern ein Objekt eine Balance-Genehmigung besitzt, muss die BTOP-Nummer,
+  die die Balance als eindeutige ID fuer jeden Antrag vergibt, im Header
+  des betreffenden Objektes erkennbar eingetragen sein.
+
+o Vor Anschluss sollte man Ruecksprache mit verschiedenen Instanzen halten,
+  um sicherzustellen, dass der Magier alle wesentlichen Punkte
+  beruecksichtigt hat:
+  - liegen alle Balance-Genehmigungen vor?
+  - sind die FP eingetragen?
+  - sind die ZTs eingetragen und getestet?
+  - sind Sonder-EK-Stupse genehmigt und eingetragen?
+
+o Fuer neue Gebiete ist eine grundsaetzliche Pruefung des Konzepts auf
+  Regionstauglichkeit sinnvoll, damit nicht alteingesessene Institutionen
+  oder Orte ploetzlich zur zweiten Wahl abgewertet werden - beispielsweise
+  einen grossen, neuen Hauptort in der Ebene anzuschliessen, der Port Vain
+  voraussehbar den Rang ablaufen wuerde.
+
+o Alle NPCs sollten vor Anschluss einmal gekillt werden, um sie auf
+  grundsaetzliche Kampf-Funktionsfaehigkeit zu pruefen.
+
+o Haben NPCs Namen, sollte ueberlegt werden, diese Namen ggf. zu banishen.
+  (hilfe banish).
+
+o Es existiert ein Shell-Skript, mit dessen Hilfe man offensichtliche
+  Formfehler in einem kompletten Verzeichnis ermitteln kann (Umlaute im
+  Code, zu lange Zeilen etc.), wobei bezueglich der Formalien auch auf
+  den Regionsmitarbeiter-Leitfaden fuer neue Projekte verwiesen werden
+  soll. Das Skript hierzu ist auf Anfrage bei Zesstra erhaeltlich. Der
+  Regionsmagier hat hierbei die Entscheidungsfreiheit, die Berichte dieses
+  Skripts dem Programmierer des neuen Gebiets als Anhaltspunkte zur
+  Verfuegung zu stellen, oder die Abarbeitung der gemeldeten Punkte als
+  Voraussetzung vor dem Anschluss vorzuschreiben, aber auch jede Abstufung
+  dazwischen ist OK. :-)
+  Zusaetzlicher Tip: Das Skript differenziert zwischen eigentlich nicht
+  akzeptablen Konstrukten und zumindest fragwuerdigen, d.h. man kann diese
+  Unterscheidung an den Verantwortlichen mit entsprechenden Forderungen
+  weitergeben.
+  Das Skript ist unter ftp://mg.mud.de/Software/src_check/ erhaeltlich.
+
+o Sollte ein zur Abnahme anstehendes Projekt eine (grundsaetzlich
+  selbstverstaendlich zulaessige) Gemeinschaftsarbeit sein, sollte man
+  als Regionsmagier darauf bestehen, eine sauber getrennte Codebasis
+  in unterschiedlichen Verzeichnissen vorgelegt zu bekommen, oder aber
+  eine Auflistung, welcher Beitrag von welchem Magier stammt.
+  Wenn diese Auflistung sich bis auf Funktionsebene erstrecken sollte
+  (z.B. "DoWield() ist von X, Details von Y, DefendFunc von Z"), ist
+  das unschoen und an sich abzulehnen.
+
+
+3) Verlegung von Dateien
+========================
+
+o Sollte ein Objekt aus einer anderen Region bzw. allgemein aus einem
+  anderen Verzeichnis (z.B. /players) in Deine Region verlegt werden
+  muessen, sind VOR dem Umhaengen folgende Punkte zu beachten:
+  -- Forscherpunkte muessen umgetragen werden (EM ansprechen),
+  -- Erstkill-Stufenpunkte muessen umgetragen werden (EM ansprechen),
+  -- Zaubertraenke umtragen lassen (EM ansprechen),
+  -- in Padreics Ruestungsskill umtragen lassen (EM ansprechen),
+  -- evtl. im Gebiete vorhandene Seherhaeuser umziehen,
+  -- evtl. im Gebiet vorhandene Kraeuterskill-Kraeuter beruecksichtigen,
+  -- evtl. Briefempfaenger der Postquest beruecksichtigen,
+  -- ueber die Mudlib greppen lassen, um eventuell in anderen Regionen
+     verwendete Referenzen auf die alten Pfade des umziehenden Codes
+     zu finden und dort umzustellen. Hierbei sind in Ausnahmefaellen von
+     fiesem Code auch in Spielersavefiles gespeicherte Pfade zu
+     beruecksichtigen (*ARGL*).
+
+
+4) Seherhaeuser
+===============
+
+o Die Erlaubnis zum Bau eines Seherhauses in einem Gebiet haengt einzig und
+  allein von dem verantwortlichen Magier ab. Sollte dieser laenger nicht
+  erreichbar sein (auch nicht ueber externe Mail!), liegt die Entscheidung
+  beim Regionsmagier, der aber in jedem Fall die Eignung des Bauplatzes
+  in der Umgebung bewerten muss.
+
+o Fuer Sonderwuensche bezueglich Unsichtbarkeit von Seherhaeusern oder
+  besonderer Kommandos zum Betreten des Hauses sei auf die Datei
+  /doc/beispiele/misc/seherhaus.c verwiesen, wo die Vorgehensweise
+  erlaeutert wird. Ein Beispiel einer sehr umfangreichen Implementierung
+  findet sich in /d/ebene/room/hp_str8a.c.
+
+o Bei geaenderten Befehlen zum Betreten muss beachtet werden, dass bei einer
+  Standardimplementierung die Erlaube-Liste umgangen wird, d.h. ohne
+  besondere Vorkehrungen u.U. JEDER das Haus ungehindert betreten kann.
+  Es ist hingegen moeglich, die Erlaube-Liste abzufragen und entsprechend
+  zu behandeln, ein Beispiel hierfuer ist in /d/ebene/room/hp_str8a.c
+  nachzulesen (derzeit jedoch auf Spielerwunsch deaktiviert).
+
+
+5) Debuggen von Code
+====================
+
+o Nach dem Reparieren eines Objektes ist es meist erforderlich, das
+  betreffende Objekt neu zu laden. Falls es sich dabei um Objekte handelt,
+  die z.B. in einem Raum mittels AddItem() hinzugefuegt wurden (wie etwa
+  ein NPC), dann ist es am besten, die Datei mit dem Befehl
+  <upd -ad datei.c> zu aktualisieren und somit saemtliche Clones zu
+  zerstoeren. Wenn man mittels <upd -ar datei.c> die bestehenden Clones
+  ersetzen wuerde, wuerden diese aus der Item-Liste des clonenden Raumes
+  ausgetragen, so dass dieser Raum dann im reset() neue Items erzeugt und
+  diese in der Folge doppelt existieren wuerden.
+
+
+6) besondere Funktionen
+=======================
+
+Es kommt haeufig vor, dass Funktionen ueberschrieben werden muessen, und das
+ist auch normalerweise vollkommen normal und nicht beanstandenswert. Jedoch
+sollte man bei bestimmten Funktionen einiges Augenmerk auf die korrekte
+Ausfuehrung richten. Einige Beispiele sind nachfolgend aufgefuehrt:
+
+o catch_tell() / ReceiveMsg()
+  Die Reaktion von Objekten, insbesondere NPCs, auf eingehende Textmeldungen
+  laesst sich nutzen, um schoene und stimmungsvolle Gebiete mit dynamisch
+  reagierenden Bewohnern zu schaffen. Es laesst sich auf der dunklen Seite
+  der Macht hingegen auch verwenden, um ueble Konstrukte zu realisieren,
+  fuer die es seit Ewigkeiten geeignete Implementierungen gibt, und die
+  demzufolge niemals durch eine Endabnahme durch einen RM durchschluepfen
+  duerfen. Ein paar reale NPC-Beispiele aus der Praxis:
+
+  Schnipsel 1)
+
+  if (sscanf(str, "  %s greift den Priester %s",name1, dummy) == 2)
+  {
+    pl = find_player(lower_case(name1));
+    if (!pl || !living(pl))
+      return 1;
+    else
+      Kill(pl);
+  }
+  Zweck:     Simulation von AddDefender() und InformDefend()/DefendOther()
+  Kommentar: Absolutes No-Go! Mit Echo-Faehigkeiten von Spielern (Gilde oder
+             anderweitig) ist hiermit ein indirekter Playerkill moeglich.
+             Inakzeptable Implementierung.
+
+
+  Schnipsel 2)
+
+  if (sscanf(str, "%s sagt: %s\n", wer,was))
+  {
+    if (lower_case(was)=="ja" )
+      this_player()->move(zielraum, M_TPORT);
+  }
+  Zweck:     Ausloesen eines Kommandos, ohne "sage ..." als Befehl
+             ueberschreiben zu muessen.
+  Kommentar: Ausnutzbar als Remote-Fluchtteleport, indem man die Meldung
+             mittels teile-mit an den NPC sendet:
+             "Robert teilt Dir mit: sagt: ja", was ungeprueft ein move() 
+             zur Folge hat. Offensichtlich ebenso ungeeignet wie das 
+             vorige Beispiel.
+
+
+  Schnipsel 3)
+
+  if (sscanf(lower_case(str),"%s sagt: %sversteck%s",s1,s2,s3))
+    tell_room(environment(),sprintf("Der Fisch sagt: Das ist aber keine "
+      "grosse Hilfe, %s.\n",capitalize(s1)),({TO}));
+
+  sieht erstmal harmlos aus, fuehrt aber mit der Anweisung
+
+  SetChats(8, ({ "Der Fisch sagt: Wo kann ich mich nur verstecken?"}) );
+
+  dazu, dass der NPC dauernd mit sich selber schwatzt. Kein kritischer Bug
+  im eigentlichen Sinn, aber auf jeden Fall der Atmosphaere im Gebiet
+  sehr abtraeglich.
+
+o move()
+  Ueberschreiben von move() ist eine extrem heikle Angelegenheit, bei der
+  beim kleinsten Fehler massive Probleme resultieren koennen, jedoch meist
+  nicht offensichtlich ist, woher das resultiert. Als allgemeine Empfehlung
+  sollte gelten, dass move() NIE ueberschrieben wird, und wenn, dann muss
+  ausfuehrlich und aufmerksam geprueft werden, was da passiert, und ob der
+  gewuenschte Effekt nicht anders sauberer erreicht werden kann.
+  Als zusaetzlicher Hinweis sei auf NotifyMove() und PreventMove() verwiesen,
+  mit deren Hilfe sich die allermeisten Faelle erschlagen lassen, in denen
+  Magier faelschlicherweise glauben, move() ueberschreiben zu muessen.
+
+o Defend()/Attack()
+  hier ist ein beliebter Fehler einfach der, dass man am Ende der Funktion
+  ::Defend() bzw. ::Attack() ruft, aber im Codeblock vorher das Objekt
+  durch eigenen Tod oder anderes schon zerstoert wurde. Dann geht das schief.
+  Einfach mal hinschauen - es ist aber kein wirklich gravierender Fehler,
+  da sowas im Kampf meist ziemlich schnell auffaellt.
+
+o call_out()
+  Hierzu zwei Hinweise: zum einen fuehrt call_out("x",0) seit der Umstellung
+  auf LDmud als Driver nicht mehr implizit zu einem call_out("x",1), wie es
+  zuvor war, sondern tatsaechlich zu einem fast sofortigen Funktionsaufruf der
+  Funktion x() - mit allen Konsequenzen, inklusive eines "too long eval"-
+  Bugs. Wer eine echte Verzoegerung braucht, muss mindestens call_out("x",1)
+  verwenden.
+  Zum anderen wurde mit LDmud die Granularitaet des reset()-Aufrufes auf
+  Heartbeat-Genauigkeit (2 s) verfeinert, so dass man bequem laengere
+  Verzoegerungen auf die Verwendung von reset() umstellen kann.
+
+o Zerstoeren von Objekten mit remove() oder destruct()
+  Man sollte einen sehr kritischen Blick auf Konstrukte werfen, die
+  nach einem remove() noch weiteren Code ausfuehren (Ausnahme: echte efuns 
+  und Compiler-Konstrukte wie return).
+  Wenn man Objekte zerstoeren will oder muss, sollte man immer zuerst 
+  remove() verwenden. Destruct muss dem absoluten Ausnahmefall vorbehalten 
+  bleiben. Man sollte im Hinterkopf behalten, dass Objekte gute Gruende haben
+  koennen, sich einem remove() zu verweigern.
+
+o for()-Schleifen
+  Eigentlich keine Funktion, aber an dieser Stelle doch passend:
+  for()-Schleifen sollten generell durch foreach()-Konstruktionen ersetzt
+  werden, da dies signifikant und messbar schneller ist. Die naechst-
+  schnellere Variante waere etwa folgendes, sofern die Reihenfolge der
+  Abarbeitung unerheblich ist (i ist integer, arr ist ein mixed-Array):
+
+  for ( i=sizeof(arr); i--; ) {
+    tu_etwas_mit(arr[i]);
+  }
+
+o write_file()
+  Benutzung dieser Funktion nur in begruendeten Ausnahmefaellen abnehmen, da
+  keinerlei Begrenzung der Dateigroesse existiert. Insbesondere bei Logfiles
+  entstehen hierdurch im Laufe der Zeit Monsterdateien, die nur Plattenplatz
+  verbrauchen, aber kaum zu ueberschauen sind. Statt write_file() wird in
+  den allermeisten Faellen log_file() mit der Standardgroesse von 50 kB fuer
+  Logfile und eine ggf. vorhandene Altversion (*.OLD) ausreichend sein.
+
+o Verwalten von charakterbezogenen Daten
+  Als Beispiel seien hier Statusdaten fuer den Ablauf von Quests genannt,
+  oder Highscores in irgendwelchen Toplisten. Fuer die Umsetzung dieser
+  Datenerfassung werden typischerweise zwei Techniken eingesetzt. Zum
+  einen kann ein Masterobjekt erstellt werden, das die Daten sammelt und
+  mittels save_object() dauerhaft speichert. Zum anderen kann man auch
+  Daten in einer Property im Spieler ablegen und diese je nach Anwendungs-
+  fall auf SAVE setzen.
+
+  Bei der ersten Variante wird man ueblicherweise die UID oder UUID des 
+  Spielers als Indizierungsmerkmal verwenden. Der erste Fallstrick ist nun,
+  dass die UID bei Spielern (nicht Sehern) ggf. nach einer Selbstloeschung
+  neu vergeben werden kann, so dass die Daten inkonsistent werden bzw. der
+  Spieler scheinbar schon in Eurem Master eingetragen ist, obwohl es sich
+  eigentlich um jemand ganz anderes handelt.
+  Als Abhilfemassnahme bietet sich folgendes an: 
+  a) UUID verwenden, da diese fuer jeden Spieler eindeutig ist.
+  b) Hinweis: find_player() kann auch mit UUIDs umgehen und das zugehoerige
+     Spielerobjekt ermitteln.
+  c) Man sollte ggf. auf das Mudlib-Event EVT_LIB_PLAYER_DELETION lauschen, 
+     um Spieler auszutragen, die sich loeschen, wodurch die zugehoerigen
+     Daten obsolet werden.
+
+  Bei der zweiten Variante muss man verschiedene Dinge beruecksichtigen:
+  a) nicht endlos viele Properties machen, sondern pro Thema (z.B. Quest)
+     einfach eine.
+  b) nur die Properties speichern, bei denen das wirklich noetig ist.
+  c) Speichert man Properties und die Aufgabe wird erledigt, d.h. der 
+     Inhalt der Property obsolet, muss die Property wieder geloescht werden.
diff --git a/doc/wiz/ruestungen b/doc/wiz/ruestungen
new file mode 100644
index 0000000..6c7425b
--- /dev/null
+++ b/doc/wiz/ruestungen
@@ -0,0 +1,212 @@
+ RUeSTUNGEN:
+     a. Allgemeines
+
+	Auch hier gelten die aktuellen Genehmigungsgrenzwerte. Alles, was
+	diese AC-Grenzwerte uebersteigt, ist prinzipiell 
+	genehmigungspflichtig. Die RMs sind gefordert, fuer eine ausgewogene
+	Bestueckung der Ruestungen in ihrem Gebiet zu sorgen. Nicht nur die
+	oberen Schutzwerte gilt es abzudecken, auch die weniger guten 
+	Ruestungen sollte es geben.
+
+	Als Richtwert sollte gelten: Eine gute Lederruestung hat ca. AC 20, 
+	eine stabile Jeans etwa AC 5, eine gehaertete Lederkappe AC 5. Nur 
+	so als Idee...
+
+	Besonders existierende Ruestungen wie der Lichtpanzer etc. sind 
+	definitiv zu gut, bzw. er ist fuer eine so leicht erreichbare 
+	Ruestung zu gut. Hier sollten die RMs verstaerkt darauf achten, dass 
+	nicht unbedingt der Durchschnitt der ACs bei 4/5 der Max-AC liegt, 
+	sondern dass auch hier auf eine Ruestung mit hoher AC viele mit 
+	niedrigerer AC kommen.
+
+	Ruestungen vom Typ AT_MISC duerfen keinerlei Veraenderungen im 
+	Spieler verursachen, keine Attribute veraendern noch sonstwie 
+	kampfrelevante Bedeutung besitzen oder Spieler/Gegner manipulieren. 
+	Die AC solcher Ruestungen ist immer 0 und wird nicht anders ge-
+	nehmigt. Weiter duerfen solche Ruestungen nicht ueber eine Hit-
+	und/oder DefendFunc verfuegen.
+
+     b. Properties
+
+      P_WEIGHT
+	Bitte realistisch halten. Ringe mit weit ueber 100 Gramm sind 
+	Schwachsinn, ebenso Hosen mit Gewichten unter 200 usw.
+  (BTW: Spieler koennen bei Max-Staerke (30) 33200 gr zzgl. etwaige
+   Trageskills der Gilde tragen.)
+
+      P_VALUE
+	Wert der Ruestungen sollte auch nicht zu gross sein und sich ein 
+	wenig an der AC orientieren. Ebenfalls: RMs, schaut genau hin.
+
+      P_ARMOUR_TYPE
+	Bitte sinnvoll benutzen! Kleider als Hosen definieren oder 
+	aehnliches ist unsinnig und sollte nicht genehmigt werden. Sind mal 
+	wieder die RMs fuer zustaendig. Bitte achtet drauf!
+
+	Folgende Ruestungstypen sind moeglich:
+
+	AT_ARMOUR
+		Alles was irgendwie den Torso schuetzt. Also vorn und 
+		hinten, wie ein Pullover, ein Kettenhemd, ein Panzer etc.
+	AT_CLOAK
+		Umhaenge, auch im weiteren Sinn wie Decken und Schleier. Im 
+		Prinzip das, was normalerweise nur den Ruecken schuetzt, bei 
+		Bedarf aber auch vorn herum gewickelt werden koennte.
+	AT_HELMET
+		Kopfbedeckungen jeder Art, vom normalen Hut ueber Kronen bis 
+		hin zu Stirnbaendern (wenn sie schuetzen sollen)
+	AT_TROUSERS
+		Hosen. Alles was zum ueberwiegenden Teil dazu gedacht ist, 
+		die Beine und/oder den Popo zu schuetzen, also auch 
+		Schuerzen oder Leggins. Natuerlich gehoeren auch Badehosen 
+		oder Lendenschuerze hierher. AC dann selbstverstaendlich 
+		gering.
+	AT_BOOT
+		Schuhe, Stiefel und Fussbekleidungen jeder Art.
+	AT_GLOVE
+		Handschuhe oder alles, was man so zum Schutz der oberen 
+		Extremitaeten benutzt, wie Armschoner, Glacehandschuhe, 
+		Faeustlinge, Boxhandschuhe etc.
+	AT_QUIVER
+		Koecher und aehnliches, in denen man Munition fuer 
+		Schusswaffen unterbringen kann. Schuetzen tut sowas 
+		natuerlich nicht.
+	AT_SHIELD
+		Alles, was man so anstelle der eigenen Arme in einen 
+		gegnerischen Schlag halten kann und das nicht offiziell als 
+		Waffe deklariert ist. Es sollte nicht grade aus Papier 
+		bestehen, und das Gewicht ist mit entscheidend fuer die 
+		Guete des Schildes. Sie werden sehr wenig im MG genutzt, was 
+		eigentlich seltsam ist. Schliesslich erreicht ein guter 
+		Schild die Guete von Helm, Hose und Handschuhen zusammen 
+		oder 3/4 der Qualitaet einer Ruestung. Andererseits: es gibt 
+		auch nicht sehr viele Schilde. Da herrscht Bedarf!
+	AT_RING
+		Ringe. Prinzipiell sollte gelten, das Ringe praktisch keine 
+		Schutzwirkung haben (also AC 1 oder max. 2). Nur, und dafuer 
+		sind die Dinger da, wenn sie magisch sind koennen sie 
+		zusaetzliche Funktionen haben.
+	AT_AMULET
+		Im Prinzip dasselbe wie bei AT_RING, nur werden die Dinger 
+		meist an Kordeln um den Hals oder als Broschen an den 
+		Klamotten getragen, koennten also je nach Groesse 
+		tatsaechlich mehr Schutz bieten als ein Ring. Aber auch hier 
+		gilt: AC>2 schreit nach Erklaerung und sollte magischen 
+		Dingern vorbehalten bleiben.
+	AT_BELT
+		Guertel aller Art (z.B. Waffenguertel oder Magisterguertel 
+		der Zauberer). Schutzwirkung hat sowas natuerlich kaum.
+	AT_MISC
+		Alles, was man sonst noch so anziehen kann, was aber eher 
+		als Zierrat gedacht ist. AC immer 0. Bitte *keine* 
+		Kleidungsstuecke, die in eine der anderen Kategorien passen, 
+		als AT_MISC definieren. Dann lieber die AC sehr tief. Wenn 
+		es eh als Gag gedacht ist koennen die Spieler sich auch bei 
+		Bedarf umziehen.
+		Solche Ruestungen duerfen keinerlei (!) Bedeutung im Kampf 
+		fuer den Spieler haben. Keinerlei Veraenderungen im/an 
+		Spieler/Gegner verursachen oder aehnliches.
+
+      P_AC
+	siehe Tabelle weiter unten
+
+      P_EFFECTIVE_AC
+	Falls eine DefendFunc vorhanden ist und die regulaere AC veraendert, 
+	sollte sie gesetzt werden. Es wird der Durchschnittswert der AC 
+	incl. der DefendFunc gesetzt. Dient der Kaempfergilde zur 
+	Einschaetzung.
+
+      P_DAM_TYPE
+	Auch Ruestungen koennen einen Damage-Typ haben. Nutzen tut das 
+	bisher nur die Kaempfergilde, aber schaden tut's keinem. Default ist 
+	DT_BLUDGEON.
+
+      P_NOBUY
+	Alle Ruestungen ab 2/3 der maximal fuer den jeweiligen Ruestungstyp 
+	zulaessigen AC werden beim Verkauf im Laden einbehalten. Dennoch 
+	sollten besondere Ruestungen P_NOBUY gesetzt haben. Insbesondere 
+	waere das fuer alles mit DefendFuncs zu wuenschen, aber auch Sachen, 
+	die als Gag gedacht sind. Dafuer koennen sich die Spieler ruhig 
+	etwas recken.
+
+    c. Spezialruestungen/Ruestungen mit Sonderfunktion
+
+	Alle Ruestungen, die ueber eine DefendFunc oder aehnliches verfuegen 
+	sind genehmigungspflichtig.
+
+	Prinzipiell sollten die geltenden Grenzwerte (s.u.) nicht 
+	ueberschritten werden; Ausnahmen koennen unter Umstaenden genehmigt 
+	werden. Immer ist der Return-Wert per random() zurueckzuliefern, da 
+	der Wert ohne weiteres random() aufaddiert wird.
+
+	WearFuncs, die Restriktionen vorsehen (Geschlechtsabhaengigkeit,
+	Rassen- oder Gildenrestriktionen) sollten die RMs ueberpruefen.
+
+	Saemtliche Sonderfunktionen, wie Heilungen, Sonderattacken auf 
+	Gegner, Waffenbeschaedigungen etc. muessen genehmigt werden.
+
+  Die Properties, mit denen Handschuhe 'handfrei' bzw 'fingerfrei' gemacht
+  werden, duerfen nur vergeben werden, wenn die Handschuhe den Schaden der Hand
+  nicht veraendern und die Beschreibung der Handschuhe dazu passt.
+
+
+    d. AC- und Genehmigungsgrenzen
+
+	Folgende Grenzwerte gelten derzeit:
+
+	+--------------+--------+----------------------------+
+	| Ruestungstyp | MAX_AC | Genehmigungsgrenze (inkl.) |
+	+--------------+--------+----------------------------+
+	| AT_AMULET    |    2   |	      --	     |
+	+--------------+--------+----------------------------+
+	| AT_ARMOUR    |   50   |	      38	     |
+	+--------------+--------+----------------------------+
+	| AT_BELT      |    2   |	      --	     |
+	+--------------+--------+----------------------------+
+	| AT_BOOT      |    6   |		5	     |
+	+--------------+--------+----------------------------+
+	| AT_CLOAK     |   10   |		8	     |
+	+--------------+--------+----------------------------+
+	| AT_GLOVE     |    5   |		5	     |
+	+--------------+--------+----------------------------+
+	| AT_HELMET    |   15   |	      13	     |
+	+--------------+--------+----------------------------+
+	| AT_QUIVER    |    0   |	      --	     |
+	+--------------+--------+----------------------------+
+	| AT_RING      |    2   |	      --	     |
+	+--------------+--------+----------------------------+
+	| AT_SHIELD    |   40   |	      28	     |
+	+--------------+--------+----------------------------+
+	| AT_TROUSERS  |   15   |	      13	     |
+	+--------------+--------+----------------------------+
+	+--------------+--------+----------------------------+
+	| AT_MISC      |    0   |	      --	     |
+	+--------------+--------+----------------------------+
+
+     e. Ruestungen fuer Zauberer
+
+	Zauberer werden normalerweise durch Ruestungen behindert. Gibt man 
+	der Ruestung jedoch die in `/p/zauberer/zauberer.h' definierte ID 
+	GILDEN_ROBEN_ID, behindert die Ruestung Zauberer nicht mehr. Das 
+	Setzen dieser ID ist auf jeden Fall mit dem Chef der Zauberergilde 
+	abzuklaeren sowie mit der Objektbalance!
+
+	Zaubererruestungen koennen ausserdem bestimmte Zaubersprueche 
+	unterstuetzen, naeheres dazu ebenfalls beim Zauberergildenchef sowie 
+	bei der Objektbalance.
+
+     f. Ruestungen mit sonstigen Gildenfaehigkeiten
+
+	Ruestungen, die in irgend einer Weise die Faehigkeiten der Gilden 
+	spezifisch veraendern (zum Beispiel die Schadensart der Karatekas 
+	oder des akshara der Tanjian veraendern) sind auf jeden Fall mit den 
+	Gildenchefs der jeweiligen Gilde sowie mit der Objektbalance 
+	abzusprechen.
+
+ SIEHE AUCH:
+     balance, waffen, fernwaffen, uniques, npcs, grenzwerte,
+     attributsveraenderungen, resistenzen, kampfobjekte
+
+ LETZTE AeNDERUNG:
+  18.10.2010, Zesstra
+
diff --git a/doc/wiz/schadenstypen b/doc/wiz/schadenstypen
new file mode 100644
index 0000000..b8f0909
--- /dev/null
+++ b/doc/wiz/schadenstypen
@@ -0,0 +1,47 @@
+Schadenstypen:
+
+Es werden zwei Arten von Schadenstypen unterschieden:
+
+a) physikalische Schadenstypen
+
+   DT_BLUDGEON      Schlagschaden, z.B. Keulen
+   DT_EXPLOSION     Schaden durch eine Explosion
+   DT_PIERCE        Stechschaden, z.B. Lanzen
+   DT_RIP           Reissender Schade, z.B. Krallen
+   DT_SLASH         Schnittschaden, z.B. Schwerter
+   DT_SQUEEZE       Quetschschaden, z.B. Tentakel
+   DT_WHIP          Peitschenschaden, z.B. Peitschen
+
+   Alle physikalischen Schadenstypen stehen auch in dem Mapping
+   PHYSICAL_DAMAGE_TYPES.
+
+b) magische Schadenstypen
+
+   DT_ACID          Saeureschaden
+   DT_AIR           Luft(mangel)schaden
+   DT_COLD          Kaelteschaden
+   DT_FIRE          Feuerschaden
+   DT_HOLY          Heiliger Schaden
+   DT_LIGHTNING     Blitzschaden
+   DT_MAGIC         Genereller magischer Schaden
+   DT_POISON        Giftschaden
+   DT_SOUND         Laermschaden
+   DT_TERROR        Angstschaden
+   DT_UNHOLY        Unheiliger/daemonischer Schaden
+   DT_WATER         Wasserschaden
+
+   Alle magischen Schadenstypen stehen auch in dem Mapping
+   MAGICAL_DAMAGE_TYPES.
+
+Eine Liste aller definierten Schadenstypen steht ausserdem in dem
+Array ALL_DAMAGE_TYPES.
+
+Alle Schadenstypen und zusammengefasste Gruppen sind definiert in
+<combat.h>
+
+Waffen duerfen maximal 50% mag. Schaden machen. Die Summe der mag.
+Schaeden darf maximal 66% betragen. Es _muss_ immer mind. ein phy.
+Schaden gesetzt sein.
+
+----------------------------------------------------------------------------
+Last modified: Mit, 03.01 12:00:54 2002 von Tilly
diff --git a/doc/wiz/scheidung b/doc/wiz/scheidung
new file mode 100644
index 0000000..d44c5d8
--- /dev/null
+++ b/doc/wiz/scheidung
@@ -0,0 +1,95 @@
+
+                          Scheidung einer MG-Ehe
+                          ======================
+
+  Die Hochzeit im MorgenGrauen ist ein sehr unterschiedlich durchgefuehrtes
+  Ritual, das mit der Hilfe von NPCs oder auch Spielern vorgenommen werden
+  kann. Aufgrund dieser Vielfalt muessen einige Dinge beachtet werden, wenn
+  man eine Hochzeit rueckgaengig machen will.
+
+  Da dabei auch Properties im Spieler geloescht, ggf. Logfiles editiert und
+  Objekte im Spielerinventar zerstoert werden muessen, empfiehlt es sich,
+  diesen Vorgang von einem Magier mit entsprechender Erfahrung und insbeson-
+  dere den noetigen Schreibrechten in der Unterwelt durchfuehren zu lassen.
+
+  1) Grundsaetzliches zur Hochzeit
+ 
+     a) P_MARRIED:
+
+        Property im Spieler, die den Namen des Ehepartners als lowercase 
+        String enthaelt (UID, z.B. "boing"). Diese Property wird von allen 
+        Objekten, die eine Hochzeit durchfuehren koennen, identisch
+        verwendet.
+
+     b) Priester:
+
+        Neben Mitgliedern der Klerus-Gilde koennen Hochzeiten von folgenden
+        NPCs durchgefuehrt werden:
+        /d/unterwelt/chris/monster/priester.c     (Kapelle in der Unterwelt)
+        /d/polar/files.chaos/feuerwehr/mon/cl2_xruur.c       (Chaospriester)
+
+     c) Verlobungs- und Eheringe:
+        
+        Als Eheringe kommen verschiedene Objekte im MG zum Einsatz:
+        /d/unterwelt/chris/objekte/ring.c       (Ring vom Unterweltpriester)
+        /d/polar/files.chaos/feuerwehr/obj/cl2_ehering.c    (schwarzer Dorn)
+        /gilden/files.klerus/ehering.c                (Ring der Klerusgilde)
+
+        Zusaetzlich existiert noch ein Verlobungsring von Rikus:
+        /players/rikus/obj/verlring.c
+        Ein verheirateter Spieler kann diesen Ring zusaetzlich im Inventar
+        haben. Er wird bei der eigentlichen Hochzeit nicht zerstoert.
+
+        Die beiden Ehepartner sowie das Datum des Hochzeitstags werden 
+        zusaetzlich zur Spieler-Property (s. 1a) als Autoload-Daten 
+        in den Ringen abgespeichert.
+
+     d) Logfiles:
+ 
+        Je nachdem, welcher Priester die Hochzeit durchfuehrt, wird der
+        erfolgreiche Abschluss der Zeremonie in unterschiedlichen Logs
+        protokolliert.
+
+        - Die Bluthochzeit der Chaoten in
+          /d/polar/files.chaos/feuerwehr/save/BuchDerHassenden
+        - Die Hochzeit der Kleriker in
+          /log/klerus/HEIRAT
+        - Die Hochzeit in der Unterwelt-Kapelle in
+          /d/unterwelt/chris/HOCHZEITSBUCH
+          und
+          /log/hochzeiten.chris
+
+  2) Vorgehensweise zur Aufloesung der Ehe
+
+     a) Loeschen von P_MARRIED in _beiden_ Spielern (SetProp(P_MARRIED,0))
+        und anschliessendes Speichern des Spielers
+
+     Ist der zweite Spieler netztot, kann die Property direkt gesetzt werden.
+     Sollte der zweite Spieler nicht existieren, weil er inzwischen 
+     geloescht wurde, reicht es, die Property im verbleibenden Spieler 
+     zu aendern (dies wird haeufig auch der Grund der Bitte um Scheidung
+     sein).
+
+     Sollte der zweite Spieler in der laufenden Uptime noch nicht eingeloggt
+     gewesen sein, kann die Property nur von einem Erzmagier mit Hilfe 
+     der Funktion __create_player_dummy() zurueckgesetzt werden.
+
+     b) Zerstoeren _beider_ Eheringe. Falls jedoch von dem Ehepartner 
+        kein Spielerobjekt existiert, ist es nicht moeglich, dessen 
+        Ehering zu zerstoeren.
+
+     c) zerstoeren evtl. vorhandener Verlobungsringe, falls gewuenscht.
+
+     d) Im Fall von Hochzeiten, die in der Unterweltkapelle durchgefuehrt
+        wurden, ist es offenbar Tradition, dass die Scheidung dort in Form
+        eines Zusatzes zum Logfile-Eintrag nachgetragen wird. Beispiel:
+        [...] "Cassandra und Viper  (ges.: Don, 02. Nov 1995, Wargon)"
+        Dies soll auch in Zukunft beibehalten werden. Fuer die Aenderung
+        sind Schreibrechte in der Region Unterwelt erforderlich.
+
+
+  SIEHE AUCH:
+     Properties:  P_MARRIED
+     Klerusgilde: man traue (/doc/g.klerus/traue)
+
+Arathorn, 04.01.2010
diff --git a/doc/wiz/scoremaster b/doc/wiz/scoremaster
new file mode 100644
index 0000000..c32319f
--- /dev/null
+++ b/doc/wiz/scoremaster
@@ -0,0 +1,213 @@
+Der Scoremaster
+===============
+
+BESCHREIBUNG
+
+  Funktion: Verwaltung der Erstkill-Stufenpunkte, die fuer das Toeten von
+  NPCs vergeben werden. Fuer alle NPCs, die mehr als 200000 XP
+  (SCORE_LOW_MARK) geben, wird ein Stufenpunkt vergeben. Ab 600000 XP
+  (SCORE_HIGH_MARK) sind es 2 Stufenpunkte. Ueber diese Werte hinausgehende
+  Punkte muessen beantragt und manuell ueber den Scoremaster eingetragen
+  werden.
+
+  Jeder NPC, der mindestens einen Stufenpunkt gibt, wird automatisch in
+  eine Liste temporaerer EKs eingetragen, die vom EK-Maintainer einzeln
+  bestaetigt werden muessen. Dieses Verfahren ist erforderlich, weil der
+  bis vor einiger Zeit eingesetzte Mechanismus, EKs automatisch einzutragen,
+  dazu gefuehrt hat, dass eigentlich nicht als EK vorgesehene oder erlaubte,
+  aber auch laengst wieder abgehaengte oder gar nie angeschlossene NPCs
+  eingetragen waren. Da aus dem Scoremaster auch die von Brumni in der
+  Fraternitas ausgegebenen EK-Tips abgefragt werden, ist das natuerlich fatal
+  fuer Spieler, wenn sie Tips bekommen, die sie nicht erreichen koennen.
+
+  Die Liste der Erstkills ist ein Mapping, das als Keys eine fortlaufende
+  Nummer enthaelt, zu der jeweils die Daten des NPCs zugeordnet sind. Diese
+  Nummer dient vor allem auch zur Indizierung des Bitstrings im Spieler,
+  in dem die Erstkills gespeichert werden.
+
+  Diese Datei dokumentiert die Funktionalitaet des Scoremasters fuer die
+  Benutzung durch EM und EK-Maintainer, jedoch nicht die vollstaendige
+  interne Arbeitsweise und Verwaltung der Daten.
+
+  Inhaltsverzeichnis dieser Dokumentation
+  1) Neueintragung von EKs
+  2) Verwaltung der unbestaetigten EKs
+  3) Aenderungen an bestehenden EKs
+  4) Spielerbezogene Daten verwalten
+  5) Daten von NPCs abfragen
+  6) Permanentes Loeschen von EKs
+  7) Verwaltung der EK-Tips von Brumni
+  8) Sonstige Funktionen (nur von NPCs gerufen)
+
+
+RUECKGABEWERTE:
+
+  SCORE_INVALID_ARG   -1
+  SCORE_NO_PERMISSION -2
+
+
+FUNKTIONEN
+
+1) Neueintragung von EKs
+
+  NewNPC(string key, int score)
+  AddNPC(string key, int score) [veraltet, leitet an NewNPC weiter]
+        Neuen NPC eintragen, key ist hierbei der Pfad, score die Punktzahl
+        Die ID des EKs wird automatisch ausgewaehlt, indem der naechste
+        freie Platz im Mapping belegt wird.
+
+
+2) Verwaltung der unbestaetigten EKs
+
+  ConfirmScore(mixed key)
+        unbestaetigten EK mit der Nummer oder dem Pfad "key" genehmigen
+    =>  EK in den Spielern setzen und Statistik hochzaehlen,
+    =>  EK aus der Liste der unbestaetigten EKs in die Liste der aktiven
+        uebertragen,
+
+  RejectScore(mixed key)
+        unbestaetigten EK mit der Nummer oder dem Pfad "key" ablehnen
+    =>  Eintrag aus der Liste der unbestaetigten EKs loeschen
+    =>  Bit-Nr. in die Liste der freien Nummern eintragen
+
+  DumpUnconfirmedScores()
+        unbestaetigte NPCs an den abfragenden Magier ausgeben
+
+
+3) Aenderungen an bestehenden EKs
+
+  SetScore(mixed key, int score)
+        Punktzahl fuer bereits eingetragenen NPC aendern, key ist der Pfad
+        oder die NPC-Nummer, score die neue Punktzahl
+
+  RemoveScore(mixed key)
+        Setzt die Punktzahl auf 0. Solche EKs koennen spaeter durch Angabe
+        einer neuen Punktzahl reaktiviert werden.
+        Alternativ kann fuer diese Funktion auch SetScore(mixed key, 0)
+        verwendet werden.
+        key kann hierbei der Pfad oder die NPC-Nummer sein
+
+  MoveScore(mixed oldkey, string newpath)
+        Verlegt einen EK
+        oldkey ist der aktuelle Pfad oder die Nummer
+        newpath ist der neue Pfad
+
+
+4) Spielerbezogene Daten verwalten
+
+  HasKill(mixed pl, mixed npc)
+        fragt ab, ob der Spieler "pl" den Kill "npc" hat
+        pl kann hierbei der Spielername oder das Spielerobjekt sein,
+        npc der Pfad oder dessen Nummer
+
+  SetScoreBit(string pl, int bit)
+        Mit dieser Funktion wird dem Spieler "pl" der EK mit der Nummer
+        "bit" gutgeschrieben.
+
+  ClearScoreBit(string pl, int bit)
+        Mit dieser Funktion wird dem Spieler "pl" der EK mit der Nummer
+        "bit" permanent ausgetragen.
+
+  QueryKillPoints(mixed pl)
+        liefert die Anzahl der Stufenpunkte zurueck, die dem Spieler pl
+        durch die aktiven EKs gutgeschrieben wurden
+
+  getFreeEKsForPlayer(object player)
+        liefert alle EKs, die aktiv sind, und die der Spieler noch nicht
+        hat, in einem Mapping entsprechend der Liste "npcs" zurueck.
+
+  QueryAllKills(string pl)
+        liefert alle Kills des Spielers als Bitstring zurueck, auch solche,
+        die momentan ausgetragen/deaktiviert sind
+        pl ist hierbei der Spielername als String
+
+  QueryKills(string pl)
+        liefert die aktiven Kills des Spielers als Bitstring zurueck
+        pl ist hierbei der Spielername als String
+
+
+5) Daten von NPCs abfragen
+
+  QueryNPCbyNumber(int num)
+        liefert die Daten des NPCs mit der Nummer "num" als Array zurueck
+
+  QueryNPCbyObject(object o)
+        liefert die Daten des NPCs mit dem Objekt "o" als Array zurueck
+
+
+6) Permanentes Loeschen von EKs
+
+  MarkEKForLiquidation(mixed key)
+        entfernt einen EK endgueltig und unwiderruflich und gibt die Nr.
+        wieder frei.
+        Technisch wird der EK erstmal in eine Liste eingetragen. Im Reset
+        iteriert der Master ueber alle Spieler-Savefiles und loescht den EK
+        aus allen Spielern. Nach Abschluss wird der Eintrag in "npcs"
+        geloescht und seine Nr. in die Liste freier Nummern eingetragen.
+
+  UnmarkEKForLiquidation(mixed key)
+        Entfernt die Loeschmarkierung von einem EK
+    =>  Dies geht nur, solange nach einem MarkEKForLiquidation() noch kein
+        reset() gelaufen ist!
+
+  QueryLiquidationMarks()
+        Fragt die fuer Loeschung vorgesehenen EKs ab.
+
+  RestoreEK(string key, int bit, int score)
+        restauriert die Daten eines frueher geloeschten, in den Spielern noch
+        enthaltenen EKs. Moeglich nur dann, wenn der EK frueher mal geloescht
+        wurde, aber in den Bitstrings in den Spielern noch eingetragen ist.
+        Es werden Pfad, Nr. und Punkte benoetigt.
+        Fuer nach dem Umbau des Scoremasters geloeschte EKs nicht mehr
+        moeglich, weil diese permanent aus den Bitstrings ausgetragen wird.
+
+
+7) Verwaltung der EK-Tips von Brumni
+
+  addTip(mixed key,string tip)
+        Traegt fuer den NPC mit der Nummer oder dem Pfad "key" einen EK-Tip-
+        Text fuer Brumni ein.
+
+  changeTip(mixed key,string tip)
+        Aendert den durch Brumni auszugebenden EK-Tip fuer den NPC mit der
+        Nummer oder dem Pfad "key". Der neue Tip wird dabei als 2. Parameter
+        "tip" uebergeben.
+
+  removeTip(mixed key)
+        Loescht den durch Brumni auszugebenden EK-Tip-Spruch. Der Tip als
+        solcher bleibt bestehen - anschliessend wird wieder der Standard-
+        Spruch ausgegeben.
+
+  getTip(mixed key)
+        Gibt den Tip-Spruch fuer den NPC mit der Nummer oder dem Pfad
+        "key" zurueck, oder den Standard-Spruch. Liefert fuer nicht
+        eingetragenen "key" den Leerstring zurueck.
+
+  QueryTipObjects(mixed player)
+        Gibt die Objektnamen der EK-Tips des jeweiligen Spielers zurueck.
+
+  allTipsForPlayer(object player)
+        Gibt den Gesamtstring aller Tips des Spielers "player" zurueck.
+
+  playerMayGetTip(object player)
+        Fragt ab, ob der Spieler "player" einen Tip bekommen kann.
+
+  giveTipForPlayer(object player)
+        Waehlt einen zufaelligen EK aus, den der Spieler noch nicht hat, und
+        traegt diesen im Master in die Liste ein, die mit allTipsForPlayer()
+        abgefragt werden kann.
+
+
+8) Sonstige Funktionen (nur von NPCs gerufen)
+
+  GiveKill(object pl, int bit)
+        schreibt dem Spieler "pl" den EK mit der Nummer "bit" gut.
+        Diese Funktion wird ausschliesslich aus NPCs heraus gerufen.
+
+  QueryNPC(int score)
+        Wird vom getoeteten NPC gerufen, um zu ermitteln, ob dieser einen
+        Erstkill gibt. Wenn der EK noch nicht existiert, wird dieser im
+        Master zunaechst in der Liste der unbestaetigten EKs eingetragen
+        Direkte Abfrage dieser Funktion von aussen z.B. durch Magier nicht
+         moeglich, weil nicht sinnvoll.
+
diff --git a/doc/wiz/seil b/doc/wiz/seil
new file mode 100644
index 0000000..4086842
--- /dev/null
+++ b/doc/wiz/seil
@@ -0,0 +1,68 @@
+
+Dokumentation fuer das Std-Seil: /obj/seil.c
+
+Abhaengigkeiten: /sys/thing/seil.h
+
+Das Standard-Seil ermoeglichst das Festbinden und Loesen eines Seiles an
+Objecten und Raeumen. Es kann im ganzen Morgengrauen verwendet werden.
+
+in den Objecten, die festgebunden werden, wird die Propertie P_TIED gesetzt.
+Sie enthaelt ein Mappng der Form:
+
+([
+
+ objectid: ([ "player" : playerid, "time" : timestamp ])
+
+])
+
+Wenn ein Object festgebunden wird, so wird die Funktion tie() in dem Object
+aufgerufen. Die Funktion muss in dem Object vorhanden sein. Liefert die
+Funktion 1 zurueck, darf man ein Seil daran binden.
+
+Aus der Funktion heraus kann im Seil in der Propertie P_TIE_USER ausgelesen
+werden, welche User die Aktion ausgeloest hat.
+(Diese Daten werden aus Kompatibilitaetsgruenden nicht an die Fkt. direkt
+uebergeben.)
+
+Wird ein Seil wieder losgebunden, so wird die Funktion 
+untie()
+in dem Object aufgerufen. 
+
+Damit ein Seil in einem Raum festgebunden werden kann, muss der Raum eine
+id() bekommen - wie ein normales Object.
+
+In den Funktionen tie() und untie() kann jeweils ueberprueft werden, ob ein
+Spieler ein Seil benutzen darf oder nicht. Liefern die Funktionen 0 zurueck,
+so wird die Benutzung des Seiles verweigert.
+
+Die Funktion seil->query_tied_to_ob() liefert das Object zurueck, an welches
+ein Seil gebunden ist oder 0;
+
+Bei der Benutzung eines Seiles im Raum wird zur Beschreibung die Funktion
+name() aufgerufen. Es kann also P_NAME gesetzt werden oder direkt name() im
+Raum ueberschrieben werden.
+
+Seile koennen ueber NPC's/Raeume und Zauber gesteuert werden:
+
+varargs int binde_seil(string ziel, string msg)
+
+	Ziel beschreibt das Object oder Raum, wo es festgebunden werden soll
+	msg ist die Ausgabe. Wird msg nicht gesetzt, so wird eine 
+	    Standard-Ausgabe ausgegeben.
+
+varargs int loese_seil(string msg)
+	Das Seil wird geloest - es wird dabei die msg in den Raum
+        ausgegeben. Ist msg nicht definiert, wird eine Standardausgabe
+    	erzeugt.
+
+
+Beide Funktionen werden wir von enem Spieler behandelt - es werden tie() und
+untie() in den festgebundenen Objecten ausgewertet.
+
+Eine weitere Propertie ist P_TIE_AUTO.
+Dieser Wert steht per Default auf 1 und erlaubt damit eine automatische
+Benutzung des Seiles ueber die Funktionen binde_seil() und loese_seil().
+Ist diese Propertie auf 0, so koennen nur Spieler das Seil benutzen.
+
+
+Letzte Änderung: 25.6. Gando
diff --git a/doc/wiz/sensitive b/doc/wiz/sensitive
new file mode 100644
index 0000000..9a920a1
--- /dev/null
+++ b/doc/wiz/sensitive
@@ -0,0 +1,67 @@
+Sensitive Objekte
+
+Man kann ein Objekt mit der Property P_SENSITIVE dazu veranlassen, auf
+gewisse Dinge zu reagieren. Wenn diese Property benutzt wird, sollte sie ein
+Array von Arrays enthalten, das so aussieht:
+
+({ ({ ereignis1, key1, grenzwert1 (,Optionen1) }),
+   ({ ereignis2, key2, grenzwert2 (,Optionen2) }),
+   ...
+   ({ ereignisN, keyN, grenzwertN (,OptionenN) }) })
+
+Folgende moegliche Ereignisse sind in <sensitive.h> definiert:
+
+SENSITIVE_ATTACK - reagiert auf Angriff.
+     In diesem Fall ist der Key der Schadenstyp und der Grenzwert der
+     Schaden, ab dem die Funktion trigger_sensitive_attack() in dem Objekt
+     aufgerufen wird. Diese Funktion bekommt uebergeben:
+       1. Den Feind
+       2. Den Key (nicht ALLE Schadenstypen)
+       3. Den Schaden
+       4. Das Argument spell von Defend()
+       5. Die Optionen, und zwar alles nach dem Grenzwert als Array.
+
+     Bemerkungen:
+        o Der Schaden nach Abzug durch Ruestungen und vor Beruecksichtigung
+          von Resistenz und Koerperschutz ist ausschlaggebend.
+        o Ein Ereignis kann mit verschiedenen Keys mehrfach angegeben
+          werden.
+SENSITIVE_INVENTORY - reagiert auf bestimmte Objekte im Inventory.
+     Ein solches Objekt reagiert auf andere Objekte empfindlich, die
+     sensitiv sind und SENSITIVE_INVENTORY_TRIGGER mit dem gleichen Key und
+     einem hoeheren Grenzwert ausloesen.
+
+     Es ist dabei egal, welches der beiden Objekte zuerst da war. In einem
+     solchen Objekt wird dann die Funktion trigger_sensitive_inv()
+     aufgerufen, und zwar mit folgenden Argumenten:
+       1. Das ausloesende Objekt (Das mit Ereignis
+          SENSITIVE_INVENTORY_TRIGGER)
+       2. Der Key
+       3. Der Grenzwert des ausloesenden Objekts
+       4. Die Optionen des ausloesenden Objekts
+       5. Die Optionen des reagierenden Objekts
+SENSITIVE_INVENTORY_TRIGGER - siehe oben
+     Mit diesem Objekt selber geschieht nichts, es loest nur Funktionen in
+     anderen Objekten aus (siehe oben).
+
+In Planung ist noch:
+
+SENSITIVE_ENVIRONMENT_CHANGE (falls im Inventory von Lebewesen)
+
+Die Einsatzmoeglichkeiten sensitiver Objekte sind vielfaeltig:
+
+   * Dynamit koennte jetzt SENSITIVE_ATTACK mit DT_FIRE und z.B. Grenzwert
+     150 haben, die Funktion trigger_sensitive_attack() sollte dann z.B. die
+     Zuendschnur anzuenden...
+   * Bei Nitroglycerin entsprechend mit DT_BLUDGEON, einem sehr kleinen
+     Grenzwert und einer trigger_sensitive_attack()-Funktion, die BUMM
+     macht...
+   * Es soll auch Dinge geben, die man besser NICHT gleichzeitig am gleichen
+     Ort aufbewahren sollte... Fuer solche Dinge sind SENSITIVE_INVENTORY
+     und SENSITIVE_INVENTORY_TRIGGER da.
+   * SENSITIVE_ENVIRONMENT_CHANGE ist dafuer vorgesehen, dass Objekte im
+     Inventory empfindlich auf Tauchen oder Aufenthalt an heissen Orten usw.
+     reagieren, dies ist jedoch noch in Planung.
+
+----------------------------------------------------------------------------
+Last modified: Wed May 8 10:00:28 1996 by Wargon
diff --git a/doc/wiz/sheriff b/doc/wiz/sheriff
new file mode 100644
index 0000000..4fb8366
--- /dev/null
+++ b/doc/wiz/sheriff
@@ -0,0 +1,41 @@
+Was ist zu tun, wenn ich jemanden bei einem Regelverstoss erwische?
+===================================================================
+
+        Grundsaetzlich solltet ihr den Sheriff, einen der Deputies oder
+        einen Erzmagier benachrichtigen.
+
+        Ist kein Magier aus dieser Gruppe anwesend oder sind diese nicht
+        ansprechbar, so empfiehlt sich die folgende Handlungsweise.
+
+        Sprecht den Spieler auf sein Fehlverhalten an und weist ihn darauf
+        hin, dass dies an den Sheriff gemeldet wird, damit er die
+        Gelegenheit hat seine Sicht der Situation festzuhalten und
+        ebenfalls an den Sheriff zu senden.
+        Dieser Hinweis sollte deutlich erfolgen und auch eventuelle Taub-
+        oder Blindheit beruecksichtigen, es empfiehlt sich tell_object()
+        bzw. echoto zu verwenden.
+
+        Sollte der Spieler nicht reagieren und mit seinem Verhalten andere
+        Spieler im Spiel beeintraechtigen, so sollte er nach /room/jail
+        befoerdert werden. Verwendet dazu bitte die Methode move im
+        Player, anstatt ins Jail zu gehen und ihn dann zu transen. Bei
+        letzterem setzt Ihr euch selbst fest.
+
+        Bitte seht von Zaps, Disconnects oder Aehnlichem ab, soweit andere
+        Moeglichkeiten bleiben. Bei den, durchaus nicht unueblichen,
+        obszoenen Level-1-Rufern ist ein Disconnect durchaus angebracht.
+
+        Sendet dem Sheriff eine kurze Beschreibung der Situation und was
+        Ihr unternommen habt.
+
+        Bitte ueberprueft sorgfaeltig bevor Ihr handelt, ob es sich
+        ueberhaupt um einen Regelverstoss handelt und ob die Situation
+        nicht auch ohne obige Eingriffe geklaert werden kann.
+
+
+ SIEHE AUCH:
+    leitfaden
+
+ LETZTE AeNDERUNG:
+    Wed, 30.08.2000, 11:35:00 von Muadib
+
diff --git a/doc/wiz/simul_efuns b/doc/wiz/simul_efuns
new file mode 100644
index 0000000..8670801
--- /dev/null
+++ b/doc/wiz/simul_efuns
@@ -0,0 +1,47 @@
+Allgemein nuetzliche simul_efun's:
+
+file_time(string filename) liefert die Accesstime des Files in Sekunden seit
+	1.1.1970 (das kann man als input fuer ctime oder dtime verwenden).
+
+query_wiz_level(mixed player) bekommt einen Spielernamen oder ein Spieler-
+	Object als argument und liefert den Magierlevel (siehe auch 
+	/secure/wizlevels.h)
+
+query_wiz_grp(mixed player) wie query_wiz_level, liefert aber die Magier-
+	Gruppe (auch wizlevels.h)
+
+mixed *exclude_array(mixed *arr,int from,int to) liefert den array ohne die
+	arr[0..from-1]+arr[to+1..<1]
+
+mixed *remove_alist(mixed key,mixed *alist) nimmt das Elem. mit Schluessel
+	key aus der Alist heraus.
+
+mixed *exclude_alist(int i,mixed *alist) nimmt das Element mit der Nummer i
+	aus der Alist.
+
+string dtime(int wann) wie ctime, aber deutsch (macht aus der Zahl, die time
+	usw liefern, einen lesbaren String)
+
+varags string
+	break_string(string str, int width, mixed indent, int leave_my_lfs)
+	bricht einen String um auf Weite width, man kann einen indent-String
+	angeben, der vor jeden Teilstring gehaengt wird, oder auch eine Zahl
+	dann werden entsprechen viele Leerzeichen dorthin gehaengt. Der letzte
+	Parameter legt fest, ob bereits existierende Linefeeds erhalten bleiben
+	oder nicht.
+
+string uptime() liefert genau diese in lesbarer Form.
+
+public object *all_environment(object ob) liefert einen Array mit dem re-
+	kursiven Environment (den Beutel, in dem ob steckt, den Spieler,
+	der den Beutel traegt, und den Raum, in dem sich der Spieler aufhaelt.
+
+varargs mixed match_living(string str, int players_only)
+	bekommt einen String. Falls der GENAU dem Namen eines nicht netztoten
+	Spielers entspricht, bekommt man den. Falls er genau dem Namen eines
+	Monsters entspricht und man nicht players_only!=0 angegeben hat,
+	liefert er den Namen des Monsters. Ansonsten sucht er nach einem 
+	Spieler, dessen Name mit std anfaengt. Gibt es keinen, returned
+	er eine -2, gibt es mehr als einen, eine -1, bei genau einem den
+	Namen.
+
diff --git a/doc/wiz/skills.doc b/doc/wiz/skills.doc
new file mode 100644
index 0000000..685b383
--- /dev/null
+++ b/doc/wiz/skills.doc
@@ -0,0 +1,77 @@
+ Skills sind Fertigkeiten, die ein Spieler erwerben kann. Zu einem Skill koennen
+mehrere Verben gehoeren. Ausserdem kann ein Verb unter Umstaenden bei mehreren
+Skills definiert sein (zB, falls es eine Magiergilde und eine Psionkier-
+gilde gaebe, koennte es skills "magie" und "psi" geben, die beide das
+Verb "teleportiere" definieren).
+
+ Welche Skills es gibt, welche Verben dazugehoeren, welche Objekte und
+Funktionen diesen zugeordnet sind, regelt ein zentraler "Skillmaster".
+Darueberhinaus gibt es in jedem Spielerobjekt ein "Skill"-Modul.
+
+Der Skillmaster offeriert die folgenden Funktionen:
+
+InsertSkill(string skillname, string skilldescr, string *verben,
+            string *objFunDescr)
+ skillname ist (natuerlich) der Name des Skills (zB Magie).
+ skilldescr ist ein string, der den Skill naeher beschreibt, zB
+            "Magie gibt dem Anwender ein breites Spektrum von Moeglichkeiten,
+             sowohl im Kampf als auch ausserhalb."
+ verben ist ein Array, der die Verben zu dem Skill enthaelt.
+ objFunDescr ist ein Array, der dieselbe Groesse haben muss wie Verben. Jedes
+             Element ist ein 4-elementiger Array. Erstes Element dieses Arrays
+             ist ein Filename, zweites eine Funktion. Bei der Eingabe des Verbs
+             wird in dem Objekt die Funktion aufgerufen. Die dabei uebergebenen
+             Parameter werden spaeter beschrieben.
+             Drittes Element ist ein String, der die zu diesem Verb und Skill
+             gehoerende Aktion naeher beschreibt, zB:
+             "Die Magische Rakete kostet den Gegner zwischen 5 und 10 Hitpoints"
+             Das 4. Element muss eine Zahl aus [1..19] sein und gibt den Level
+             an, ab dem ein Player dieses Skillverb erwerben kann.
+ Die Funktion darf nur von Erzmagiern aufgerufen werden.
+ Es darf noch keinen Skill dieses Names geben. Falls man einen bestehenden
+ Skill lediglich erweitern moechte, muss man die Funktion AddVerbs benutzen
+ (siehe unten).
+
+RemoveSkill(skillname)
+ entfernt einen Skill wieder. ACHTUNG: Wenn Player den Skill bereits erworben
+ haben, verlieren sie ihn, wenn sie zum naechsten mal versuchen, ihn zu be-
+ nutzen.
+
+GetSkills()
+ gibt die AList mit den Skills zurueck.
+
+GetSkill(skillname)
+ liefert den entsprechenden Eintrag aus der AList zurueck.
+
+GetFunctionAndDescription(skillname, verb)
+ liefert den in AddSkill uebergebenen 4elementigen Array mit Objektname,
+ Funktionsname, Aktionsbeschreibung und Level zurueck.
+
+GetSkillDescription(skillname)
+ liefert die in AddSkill uebergebene Beschreibung des Skills zurueck.
+
+AddVerbs(skillname, mixed *verbs, mixed *objFunDescr)
+ erweitert einen bestehenden Skill um einige Verben, naehere Beschreibung
+ siehe AddSkill.
+
+-------------------------------------------------------------------------------
+Das Skillmodul im Playerobjekt bietet die folgenden Funktionen:
+
+GiveAbility(verb, skill, int promill)
+ gibt einem Spieler die Faehigkeit, das Verb verb aus dem Skill skill zu benut-
+ zen. Promill wird bei dem Versuch, das Verb zu benutzen, an das Skillobjekt
+ uebergeben und sollte die Wahrscheinlichkeit, das dem Player die Skillaktion
+ gelingt, in Promill beschreiben. Wenn der Player die Ability schon hatte, wird
+ lediglich promill geupdated.
+ Es wird eine -1 zurueckgegeben, falls eine solche Kombination aus Skill und
+ Verb (noch) nicht definiert ist, eine -2 falls der Level des Spielers nicht
+ ausreicht.
+
+GetAllAbilities()
+ gibt die AList mit den Abilities des Spielers zurueck. Schluessel in der AList
+ sind die Verben, Eintraege sind Arrays mit den Skills. Die Arrays haben so
+ viele Eintraege, wie der Player Skills hat, die das betreffende Wort
+ definieren (im Idealfall also EINEN Eintrag, naemlich wenn es keine Ueber-
+ scheidungen gibt). Die Eintraege sind wiederum 2elementige Arrays, die
+ als 1. Element den Skillnamen, als 2. Element die Promill enthalten.
+
diff --git a/doc/wiz/sqlitedb b/doc/wiz/sqlitedb
new file mode 100644
index 0000000..44a7dce
--- /dev/null
+++ b/doc/wiz/sqlitedb
@@ -0,0 +1,14 @@
+SQLite-DBs im Morgengrauen
+
+Im MG koennen Objekte sqlite-Datenbanken verwenden.
+Die notwendigen efuns dafuer sind sl_open, sl_close, sl_exec, sl_insert_id.
+
+Hat man das Speicherfile (Savefile) der DB, kann man dieses mit dem Kommando
+sqlite3 auf einer Shell auch direkt abfragen:
+
+Auf der Shell fragt man die DB recht simpel so ab:
+sqlite3 -column -header <pfad_zur_db>
+
+Alles aus einer Tabelle anzeigen, wo eine Spalte einen bestimmten Wert hat:
+select * from <tabelle> where <spalte>='abc';
+
diff --git a/doc/wiz/teamkampf b/doc/wiz/teamkampf
new file mode 100644
index 0000000..4cfc934
--- /dev/null
+++ b/doc/wiz/teamkampf
@@ -0,0 +1,113 @@
+
+Teamkampf im MorgenGrauen
+=========================
+
+Zum Teamkampf im MG gehoeren zwei Objekte: Das Lebewesen, das im Team ist,
+sowie das Teamobjekt. Ersteres erbt seine Funktionalitaet aus
+
+/std/living/team.c
+
+das Teamobjekt ist ein clone von
+
+/obj/team.c
+
+Allerdings sollte man immer das #define fuer diesen Pfad nutzen, welches
+in /sys/living/team.h definiert ist: TEAM_OBJECT
+
+Darueberhinaus gibt es in diesem Verzeichnis noch den Teammaster.
+
+
+Alle relevanten Funktionen und Properties sind im Teammitglied abrufbar und
+liefern Informationen ueber den Teamkampf.
+
+
+UEBERSICHT ueber die Properties und Funktionen des Teamkampfs
+=============================================================
+
+Properties des Teammitglieds:
+----------------------------
+P_TEAM                 - Teamobjekt
+P_TEAM_NEWMEMBER       - Spieler moechte ins Team hiervon aufgenommen werden.
+P_TEAM_ATTACK_CMD      - Angriffsbefehl des Spielers, nicht setzbar.
+P_TEAM_AUTOFOLLOW      - Folgewunsch des Spielers, nicht setzbar.
+P_TEAM_WANTED_ROW      - Gewuenschte Reihe des Spielers.
+P_TEAM_WIMPY_ROW       - Fluchtreihe des Spielers.
+P_TEAM_LEADER          - Spieler ist Anfuehrer dieses Teams.
+P_TEAM_ASSOC_MEMBERS   - Alle zugeordneten NPCs bzw. der Spieler dem dieser
+                         NPC zugeordnet ist.
+P_TEAM_COLORS          - Grenzwerte fuer farbige Anzeige.
+
+Funktionen des Teammitglieds:
+----------------------------
+TeamPrefix()           - "[Team Teamname] " falls Teammitglied, "" sonst.
+IsTeamLeader()         - Test ob Spieler Anfuehrer eines Teams ist.
+IsTeamMove()           - Test ob Angriffsbewegung gerade ausgefuehrt wird.
+TeamMembers()          - Teammitglieder.
+PresentPosition()      - Aktuelle Reihennummer des Spielers.
+PresentTeamPositions() - Reihennummern aller anwesenden Teammitglieder.
+PresentTeamRows()      - Reihen aller anwesenden Teammitglieder.
+PresentEnemyRows()     - Reihen aller anwesenden Feindteams zusammen.
+SelectNearEnemy()      - Waehlt direkt angreifbaren Feind aus.
+SelectFarEnemy()       - Waehlt Feind aus hinteren Reihen aus.
+InsertEnemyTeam()      - Macht alle Mitglieder von Team des Feindes zu
+                         Feinden aller Mitglieder des eigenen Teams.
+AssocMember()          - Assoziiert einen HilfsNPC mit einem Spieler.
+DeAssocMember()        - Hebt Assoziation zwischen NPC und Spieler auf.
+TeamFlee()             - Spieler wird veranlasst in Fluchtreihe zu wechseln.
+
+Funktionen des Teamobjekts:
+--------------------------
+SwapRows()             - Spieler tauschen die Reihen
+
+
+BEISPIEL:
+  Man moechte von einem Spieler, welcher sich in einem Team befindet, alle
+  Teammitglieder sowie deren Anzahl ermitteln, die VOR diesem Spieler stehen.
+
+  Im abfragenden Objekt muss man zunaechst die Headerdatei des Teamkampfs
+  includen:
+
+  #include "/sys/living/team.h"
+
+
+  void fun( object pl )
+  {
+   int act_row,all;
+   mixed *rows;
+   object team,*team_members;
+
+   team=pl->QueryProp(P_TEAM);           // liefert das Teamobjekt
+
+   act_row=pl->PresentPosition();        // aktuelle Position ermitteln
+
+   team_members=({});
+
+   if ( objectp(team) && (act_row > 1) )
+    {
+     rows=team->PresentRows(ENV(pl));    // die Reihen werden als mixed-array
+                                         // uebergeben
+
+     foreach ( int i : act_row )
+      team_members+=rows[i];             // die Reihen werden komplett ins
+                                         // neue Array uebertragen
+    }
+
+   all=sizeof(team_members);             // Anzahl der Teammitglieder, die
+                                         // vor dem Spieler stehen
+  }
+
+
+SIEHE AUCH:
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix, teamkampf_intern
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/wiz/teamkampf_intern b/doc/wiz/teamkampf_intern
new file mode 100644
index 0000000..79c2963
--- /dev/null
+++ b/doc/wiz/teamkampf_intern
@@ -0,0 +1,127 @@
+
+Teamkampf-Interna
+=================
+
+Verwaltung der Kampfreihen
+--------------------------
+
+Die Umordnung der Teammitglieder wird durch die Formation gesteuert.
+
+  formin[reihe] - Minimale Anzahl von Teammitgliedern in dieser Reihe
+  formax[reihe] - Maximale Anzahl von Teammitgliedern in dieser Reihe
+
+Die Funktion CheckFormation() passt die Werte so an, dass sie bei Anwesenheit
+aller Teammitglieder erfuellbar sind, wobei ggf. der Maximalwert vorne
+anfangend erhoeht wird und der Minimalwert hinten anfangend verringert wird.
+Dabei ist der Minimalwert der ersten Reihe 1.
+
+Es ist zwischen der Teamaufstellung und der Raumaufstellung zu unterscheiden.
+Die Teamaufstellung ist die Aufstellung, in der die Mitglieder angeordnet
+waeren, wenn alle anwesend waeren. In der Teamaufstellung ist durch die
+Funktion MakeFormation() sichergestellt, dass die Minimal- und
+Maximalanforderungen erfuellt sind.
+Die Raumaufstellung ergibt sich aus den anwesenden Mitgliedern der
+Teamaufstellung. Sollten die Minimalanforderungen einer vorderen Reihe nicht
+erfuellt sein, werden anwesende Mitglieder aus den hinteren Reihen IN DER
+RAUMAUFSTELLUNG nach vorne geschoben soweit noetig.
+
+Vor Verschiebungen und bei Updates der TEAMAUFSTELLUNG werden die Arrays mit
+den Mitgliedern jeder Reihe sortiert, wobei die gewuenschte Reihe der
+Mitglieder das groesste Gewicht (125) im Sortierkriterium hat und die Anzahl
+der Lebenspunkte einfach eingeht. Solange die Teammitglieder unter 250 LP
+haben, kann ein Mitglied mit wenigen LP hinter einem mit vielen LP einsortiert
+sein, der EINE Reihe weiter hinter stehen will, aber nicht zwei oder mehr.
+Wird es noetig, Mitglieder aus einer Reihe in eine andere zu verschieben,
+werden Mitglieder vom Anfang des Arrays nach vorne verschoben (an das Ende
+des davorliegenden Arrays) bzw. Mitglieder vom Ende des Arrays nach hinten
+(an den Anfang des dahinterliegenden Arrays). Dadurch werden immer die
+Mitglieder mit dem staerksten Drang nach vorne an die Front geschickt :-)
+
+Wenn jemand die Reihe wechselt und dabei die Minimalanforderung der Quellreihe
+unterschritten oder die Maximalanforderung der Zielreihe ueberschritten wird,
+wird mit CycleRows() rotiert. Kommt ein neues Mitglied hinzu und die
+Maximalanforderung seiner gewuenschten Reihe wuerde ueberschritten, wird
+in AddToRow() zunaechst versucht ein Mitglied eine Reihe vorzuschieben, falls
+da noch Platz ist, sonst eine Reihe zurueck, falls da noch Platz ist (die
+Anzahl der Mitglieder also < formax ist). Wird ein Mitglied entfernt oder
+gewaltsam entfernt und wuerde dabei die Minimalanforderung seiner Reihe
+unterschritten, wird in RemoveFromRow() versucht, ein Mitglied aus der
+nachfolgenden Reihe nach vorne zu holen, falls dort Ueberschuss vorhanden ist
+(in der nachfolgenden Reihe also mehr Mitglieder als formin sind), sonst aus
+der vorangehenden Reihe, falls dort Ueberschuss ist.
+
+Sollten diese Umordnungen nicht ausreichen die Formation in der
+TEAMAUFSTELLUNG einzuhalten, wird die Formation mit MakeFormation() erzwungen.
+Dies wird in zwei Durchlaeufen erreicht. Der erste Durchlauf wird von vorne
+nach hinten durchgefuehrt. Ist das Maximum einer Reihe ueberschritten, werden
+soviele Mitglieder nach vorne geschoben wie in der davorliegenden Reihe freie
+Plaetze sind und die restlichen ueberschuessigen Mitglieder nach hinten. Sollte
+das Minimum einer Reihe unterschritten sein, werden soviele Mitglieder aus den
+nachfolgenden Reihen hergeholt, wie zum Erreichen des Minimums noetig ist. Der
+zweite Durchlauf wird von hinten nach vorne ausgefuehrt. Falls das Minimum
+einer Reihe unterschritten wird, werden Mitglieder aus den davorliegenden
+Reihen geholt soweit noetig, wenn jedoch Ueberschuss vorhanden ist, wird
+dieser nach vorne geschoben.
+
+BEISPIEL:
+
+Formation 3-6 2-2 1-2 0-6 1-1 (bloedsinnige Formation ;-)
+
+1. Durchlauf (vorne beginnend):
+
+   1 1 6 1 0
+   <----     (Minimum Reihe 1 unterschritten)
+   3 0 5 1 0
+     <--     (Minimum Reihe 2 unterschritten)
+   3 2 3 1 0
+       -->   (Maximum Reihe 3 ueberschritten)
+   3 2 2 2 0
+
+2. Durchlauf (hinten beginnend):
+
+   3 2 2 2 0
+         --> (Minimum Reihe 5 unterschritten)
+   3 2 2 1 1
+
+Ergebnis entspricht der gewuenschten Formation. Ermittlung der RAUMaufstellung:
+
+   2 0 2 1 1 (Anwesende aus der vorangegangenen TEAMaufstellung)
+   <----
+   3 0 1 1 1
+     <----
+   3 2 0 0 1
+       <----
+   3 2 1 0 0
+
+Ergebnis erfuellt Minimalanforderungen zumindest vorne, Maximalanforderungen
+koennen nicht ueberschritten werden, weil die Raumaufstellung hoechstens
+soviele Mitglieder wie die Teamaufstellung hat.
+
+
+Abarbeitung des Angriffsbefehls und Verteilung der Begruessungsschlaege
+-----------------------------------------------------------------------
+Das Grundprinzip ist folgendes:
+Wenn das ganze Team bewegt wird, werden die Begruessungsschlaege unter
+Vorbehalt weggelassen. Sobald alle Teammitglieder bewegt wurden, macht
+jedes Monster, das noch einen Begruessungsschlag ausfuehren muss, einen
+Schlag auf EIN Teammitglied das im Nahkampf erreichbar ist und der Vorbehalt
+wird fuer alle Mitglieder aufgehoben. Wenn ein Teammitglied versucht
+sich zu bewegen und noch ein Begruessungsschlag unter Vorbehalt fehlt,
+wird dieser noch vor der Bewegung nachgeholt.
+
+
+SIEHE AUCH:
+        Uebersicht: teamkampf
+        Properties: P_TEAM, P_ASSOC_MEMBERS, P_TEAM_ATTACK_CMD,
+                    P_TEAM_AUTOFOLLOW, P_TEAM_COLORS, P_TEAM_LEADER,
+                    P_TEAM_NEWMEMBER, P_TEAM_WANTED_ROW, P_TEAM_WIMPY_ROW
+        Bewegung:   IsTeamMove, TeamFlee
+        Mitglieder: IsTeamLeader, TeamMembers
+        Kampf:      AssocMember, DeAssocMember, InsertEnemyTeam,
+                    SelectNearEnemy, SelectFarEnemy
+        Positionen: PresentPosition, PresentRows, PresentEnemyRows,
+                    PresentTeamPosition, SwapRows
+        Sonstiges:  TeamPrefix
+
+----------------------------------------------------------------------------
+Last modified: 16-08-2010, Gabylon
diff --git a/doc/wiz/techniken b/doc/wiz/techniken
new file mode 100644
index 0000000..5defd59
--- /dev/null
+++ b/doc/wiz/techniken
@@ -0,0 +1,111 @@
+****************************************************************************
+* Dieser Text ist veraltet. Die Techniken werden von der Mudlib nicht      *
+* ausgewertet und momentan auch von keiner Gilde (Ausnahme: die Tanjian    *
+* zeigt sie an, tut aber nix damit.                                        *
+****************************************************************************
+----------------------------------------------------------------------------
+             Techniken, mit denen Waffen eingesetzt werden koennen
+----------------------------------------------------------------------------
+
+TQ_THRASH - "Schlagtechnik"
+
+    Die vom Verstaendnis her einfachste Technik: Man nehme sich einen
+    Gegenstand,  der gross genug ist und halbwegs  brauchbare  Dichte 
+    besitzt - also nicht gerade federleicht  fuer seine Groesse ist - 
+    und haue drauf.  Egal ob Henkersaxt,  Stock,  Stuhl,  Bierflasche 
+    (ganz), Knueppel, Keule oder Bratpfanne,  das und aehnliches sind 
+    brauchbare  'Waffen',  um mit dieser Technik Erfolg zu haben.  Es 
+    ist eine schlagende Bewegung,  also das gewollte schwunghafte Zu-
+    sammenfuehren zweier Koerper, fuer gewoehnlich Waffe und Gegner.
+
+
+TQ_THRUST - "Stosstechnik"
+
+    Auch diese Technik erfreut sich zumindest in Kneipenschlaegereien
+    grosser Beliebtheit: Das Stossen.  Mit einem vorzugsweise spitzen
+    Gegenstand wie Speer, Dolch, Bierflasche (zerbrochen) oder Helle-
+    barde  wird dem Gegner ein Loch in den Leib  gestossen,  im Falle 
+    eines   stumpfen Gegenstandes wie z.B.  einem Kampfstock oder dem 
+    eigenen  Zeigefinger oder Knoechel zielt man auf Vitalpunkte oder
+    Weichteile und erzielt auch damit beeindruckende Ergebnisse.  Das
+    Gewicht der Waffe ist  weniger von Bedeutung,  da  der Akteur bei 
+    einem korrekt ausgefuehrtem seine eigene Masse mit einsetzt.
+
+
+TQ_STROKE - "Streichtechnik"
+
+    Ein etwas eigenwillig anmutender Begriff,  aber bei genauerer Be-
+    trachtung hat  diese Technik tatsaechlich etwas mit Streicheln zu
+    tun:  Die wichtigste Komponente der Bewegung fuehrt mehr oder we-
+    niger am  Gegner _entlang_ und nicht auf ihn zu.  Man stelle sich 
+    ein Schwert vor, das geschmeidig durch die Kehle eines Orks glei-
+    tet  und eine Menge Blut  freisetzt oder ein Pergamentblatt,  das
+    man aus der Hand gezogen bekommt  (und ebenfalls eine Menge  Blut
+    freisetzt).  In den allermeisten Faellen ist also eine moeglichst
+    scharfe Schneide im Spiel und, auch wenn schmerzhafte Erfahrungen
+    mit  Pargamentkanten gelegentlich gegenteiliges  vermuten lassen, 
+    je schaerfer die Schneide, desto weniger Druck in Richtung feind-
+    lichen  Zielkoerper  muss fuer  einen  "durchschneidenden Erfolg" 
+    ausgeuebt werden.
+
+
+TQ_WHIP - "Peitschtechnik"
+
+    Unter der Peitschtechnik wird nun alles zusammengefasst,  was den
+    Umgang mit beweglichen Elementen an der Waffe betrifft.  Egal, ob
+    Peitsche a la George Lucas, neunschwaenzige Katze, Manriki-Gusari
+    (eine halbmeter lange Kette mit Gewichten an den Enden), ein- bis
+    dreikoepfigem  beketteten Morgenstern oder gar Dreschflegel,  sie 
+    sind zumindestteilweise sehr unterschiedlich konzipiert (so fehlt
+    der  Peitsche am aeusseren Ende das Gewicht,  so dass jenes durch
+    eine gegenlaeufige Bewegung beschleunigt werden muss), haben aber
+    eines gemeinsam: Der Schaden wird durch ein ueber die normale Be-
+    wegung des Kaempfers  hinaus beschleunigtes Teil der Waffe verur-
+    sacht. Durch jene Beweglichkeit der  Waffe wird ein sehr  grosses 
+    Mass an Koordinationsvermoegen vom Kaempfer abverlangt.
+
+----------------------------------------------------------------------------
+
+Vermischtes
+
+    - Waffen koennen  sich fuer mehrere  Techniken eignen.  Mit einem
+      Katana, dem Schwert der Samurai, beispielsweise kann man in die
+      ungeschuetzten Partien des Gegners stechen,  dem Gegner mit der 
+      Streichtechnik tiefe Schnitte in die Unterseiten der Arme  (ein
+      durch typische Samurairuestungen schlecht geschuetzter Bereich)
+      zufuegen, und mit der beruehmten halben Koerperdrehung dem in-
+      zwischen taumelndem Kontrahenten den Kopf abschlagen. 
+
+    - Waffen koennen sich, obwohl von der gleichen Gattung, fuer ver-
+      schiedene Techniken eignen.  Ein spitzer Dolch mag eine stumpfe
+      Klinge haben  und nur fuer ausschliesslich stossende  Praktiken
+      zu gebrauchen zu sein, waehrend ein abgerundetes Kuechenmesser,
+      ebenfalls vom Typ "Zyn", fuer kantiges Zuschlagen und eventuell
+      auch fuer schnittige Streichtechniken herhalten kann.
+
+    - Je nach Zweck und Koennen wird die beste zu verwendende Technik 
+      variieren:  Wenn es um arme Grashalme geht,  wird die Sense mit 
+      schneidenden Bewegungen gefuehrt  (schoen in der  Milka-Werbung 
+      zu sehen,  fuer die, die es partout nicht glauben wollen),  bei 
+      einer Bauernrevolte mag das gute Stueck jedoch auch mit anderen 
+      Techniken effektiv (und vielleicht sogar effektiver) eingesetzt 
+      werden, wer weiss, wer war schon dabei...?  Selbst bei normalen
+      Waffen wie Schwertern  wuerde ein Ungeuebter  selten versuchen,  
+      mit Streichtechniken Schaden zu verursachen, denn das Zustechen 
+      ist schlicht einfacher.
+
+    - Es wird immer  besondere Waffen geben.  So  sind beispielsweise
+      das Zweililien und  die Hellebarde  beide dem Typ "Kzrok" (fuer
+      nicht-Trves: Stangenwaffen) zuzuordnen, die Techniken  weichen,
+      bedingt  durch die Vielseitigkeit  der Waffen,  jedoch von  der
+      Norm ab: Das Zweililien, an jedem  Ende eine lange und  scharfe
+      Klinge,  kann  ebenso  vielseitig  gehandhabt  werden  wie  ein 
+      Schwert,  waehrend die Hellebarde (eine Mischung aus  Lanze und
+      Axt) durch ihre unausgewogene Gewichtsverteilung  und dem damit
+      verbundenen Potenzial an Schwung schlichtweg DIE Waffe der Wahl 
+      fuer feige Zwerge ist:  Schwingen wie eine grosse  Axt und  den 
+      Gegner auf Distanz halten koennen.  Gut,  dass es keine  feigen 
+      Zwerge gibt ;o)
+
+----------------------------------------------------------------------------
+Last modified: Tue Apr 13 13:00:00 2000 by Anatol
diff --git a/doc/wiz/testspieler b/doc/wiz/testspieler
new file mode 100644
index 0000000..b7fc2f8
--- /dev/null
+++ b/doc/wiz/testspieler
@@ -0,0 +1,40 @@
+
+Testspieler
+===========
+
+ TESTSPIELER:
+    Testspieler sind Zweitcharaktere von Magiern, die auch gleichzeitig mit
+    dem Magier eingeloggt sein duerfen. Sie muessen entsprechend markiert sein
+    (siehe unten) und duerfen auch ruhig vom Magier manipuliert werden.
+
+    TESTSPIELER UNTERLIEGEN VOLL DEN REGELN DES MAGIERVERTRAGS!
+
+    Testspieler werden hauptsaechlich dann gebraucht, wenn Objekte oder
+    Gebiete wirklich aus Spielersicht getestet werden sollen und das
+    Abschalten des Magiermodus dazu nicht ausreicht. Ein anderes Einsatzgebiet
+    ist das Testen von Meldungen oder Aktionen, die nicht nur den
+    Ausfuehrenden selbst betreffen, sondern z.B. auch umstehende Personen in
+    einem Raum.
+
+ GILDENTESTSPIELER:
+    Sofern eine Gilde das Anlegen von Testspielern unterstuetzt, gibt es
+    entsprechende Hilfeseiten unter "hilfe <gildenname>.testspieler".
+
+ MARKIEREN:
+    Ein Testspieler wird durch setzen der Property P_TESTPLAYER markiert. Es
+    reicht zwar aus, diese Property einfach auf einen Wert != 0 zu setzen,
+    aber man sollte hier lieber den Namen des Magiers eintragen, zu dem der
+    Testie gehoert.
+
+    Einige Magier markieren ihre Testspieler nicht, und zwar mit der
+    Begruendung, dass dann Todesmeldungen nicht ausgegeben werden. Das ist
+    allerdings keine Ausrede mehr, das Testspieler-Todesmeldungen auf -TdT
+    ausgegeben werden.
+    Sind nichtmarkierter Testspieler entsprechend manipuliert, koennte es
+    passieren, dass sie wegen Schummelei geloescht werden...
+
+ SIEHE AUCH:
+    zweitspieler, vertrag, mschau, P_TESTPLAYER
+
+ LETZTE AeNDERUNG:
+    19. Maerz 2004 Gloinson
diff --git a/doc/wiz/todesfallen b/doc/wiz/todesfallen
new file mode 100644
index 0000000..5585032
--- /dev/null
+++ b/doc/wiz/todesfallen
@@ -0,0 +1,355 @@
+Manpage zu Todesfallen. Technischer Leitfaden.
+
+
+Diese Manpage richtet sich an (Jung)magier, die Todesfallen nutzen wollen,
+um verschiedenste Dinge in ihren Gebieten damit zu erreichen. Dabei soll hier
+nicht diskutiert werden, wo welche Art von Todesfalle Sinn macht (oder auch
+nicht), sondern es sollen technische Aspekte bei der Programmierung einer
+solchen (Todes)falle aufgezeigt werden.
+
+
+Kapitel 1. Das Umfeld einer Todesfalle.
+
+Eine Todesfalle kann wie in spaeteren Kapiteln beschrieben, leicht umgesetzt
+werden. Damit sie sich auch so verhaelt wie sie soll, werden hier einige
+Sicherheitsaspekte behandelt, die durchgegangen werden sollten.
+
+
+Kapitel 1.1 Wer hat die Todesfalle ausgeloest?
+
+Die meisten Todesfallen werden durch ein Kommando ausgeloest, welches der
+Spieler ausfuehrt. Z.B.
+	druecke grossen roten knopf mit der aufschrift "todesfalle ausloesen"
+In diesem Fall existiert in der aufgerufenen Funktion die Moeglichkeit, ueber
+die Funktion this_player() den aktuell "gueltigen" Spieler auszufischen.
+Hierbei ist zu beachten, dass auch NPCs von dieser Funktion zurueckgegeben
+werden koennen. Auch im init() eines Raumes wird in this_player() der Spieler
+zurueckgegeben. Hierbei ist jedoch zu beachten, dass man immer schauen sollte,
+ob this_player() auch tatsaechlich ein Objekt zurueckliefert. Dies ist
+wichtig, da das init() (oder auch andere Funktionen) durch Gegenstaende, wie
+den Bumerang ausgeloest werden koennen. In diesem Fall steht in this_player()
+ueblicherweise kein Objekt. Die Sicherheitsabfrage sollte also wie folgt
+aussehen:
+
+    object spieler;
+    spieler = this_player();
+    if (spieler) // Ein Lebewesen?
+    {
+      ...
+    }
+    // else: Die Falle nicht ausloesen, da kein Lebewesen.
+
+
+Kapitel 1.2 Wer soll die Todesfalle nicht ausloesen?
+
+Oft kommt es vor, dass eine schoene Todesfalle von Gott und der Welt
+ausgeloest wird. Normalerweise moechte man aber nicht, dass "Gott" oder andere
+besondere Personengruppen diese Falle ausloesen. In den folgenden
+Unterkapiteln werden Abfragen gezeigt, um Personengruppen auszuschliessen.
+
+
+Kapitel 1.2.1 Todesfallen, die nicht auf Geister reagieren.
+
+Ueblicherweise sollen Todesfallen bei den Ausloesern Schaden verursachen. Bei
+Geistern laeuft dies jedoch ins Leere. Man koennte nun auf die Idee kommen,
+alternativ auf Geister zu reagieren.
+
+
+Kapitel 1.2.1.1 Nicht auf Geister reagieren.
+
+Angenommen, man hat einen Spieler (siehe oben), der im Begriff ist, die Falle
+auszuloesen. Man sollte nun pruefen, ob er ein Geist ist. Falls er kein Geist
+ist, kann man wie gewuenscht (Todesfalle) reagieren. Falls nicht, sollte man
+eine passende Meldung ausgeben. Z.B. "Als Geist hast Du nicht die notwendige
+Kraft, diesen Knopf zu druecken." Die technische Abfrage, ob ein Spieler ein
+Geist ist, sieht wie folgt aus.
+
+    if (!spieler->QueryProp(P_GHOST))
+    {
+        // ist _kein_ Geist, also Todesfalle.
+    }
+    else
+    {
+        // Alternativer Text, da Geist.
+    }
+
+
+Kapitel 1.2.1.2 Geister entgeistern.
+
+Da ausschliesslich die Rassenstartraeume entgeistern sollen, wurde dieser
+Punkt herausgenommen. Also: Es ist unerwuenscht, Geister zu entgeistern, um
+eine Todesfalle korrekt auszuloesen.
+
+
+Kapitel 1.2.1.3 Alternative fuer Geister.
+
+Rollenspieltechnisch gesehen macht es in den allerwenigsten Faellen Sinn,
+alternativ auf einen Geist zu reagieren. Der Vollstaendigkeit halber sei diese
+Loesung trotzdem erwaehnt. Ein Programmbeispiel sollte ohne grosse Muehe durch
+die schon vorhandenen Beispiele erstellt werden koennen.
+
+
+Kapitel 1.2.2 Gaeste und kleine Spieler.
+
+Rollenspieltechnisch macht es oft keinen Sinn, auf Gaeste oder kleine Spieler
+anders zu reagieren, als auf "den Rest". Moechte man jedoch einen besonderen
+Schutz fuer kleine Spieler einbauen ("Du hast zu grosse Angst, diesen grossen,
+roten Knopf zu druecken.") oder moechte man Gast-Sterbe-Testern den Hahn 
+zudrehen (die Weicheier!), so sind folgende Abfragen unter Umstaenden
+sinnvoll.
+
+  // Gast?
+  if (spieler->QueryGuest())
+  {
+      // Ist ein Gast...
+  }
+  else
+  {
+      // Todesfalle. Kein Gast.
+  }
+  
+  // Kleiner Spieler?
+  // In diesem Beispiel Abfrage des Spielerlevels.
+  if (spieler->QueryProp(P_LEVEL) < 10)
+  {
+      // Spielerlevel < 10 haben zuviel Angst.
+  }
+  else
+  {
+      // Spielerlevel >= 10 koennen hier ruhig die Todesfalle ausloesen.
+  }
+
+
+Kapitel 1.2.3 Magier
+
+Ueblicherweise sind Magier unsterblich und soll(t)en auch nicht
+(versehentlich) Todesfallen ausloesen. Dies ist vor allem dann der Fall, wenn
+"zufaellig" im gleichen Raum stehende Spieler Meldungen bekommen wuerden und
+dadurch gewarnt werden. Die technische Umsetzung, dass Magier Todesfallen
+nicht ausloesen, sieht wie folgt aus. Die Abfrage beruecksichtigt
+"mschau aus". Das heisst, Magier koennen sterben, wenn sie mschau aus gemacht
+haben.
+
+    #include <wizlevels.h>
+
+    if(IS_LEARNER(spieler) && spieler->QueryProp(P_WANTS_TO_LEARN))
+    {
+      // Magier: Man soll ihm mitteilen, dass er diese Aktion als Magier nicht
+      // tun darf.
+    }
+    else
+    {
+      // Kein Magier. => Todesfalle.
+    }
+
+
+Kapitel 1.3 Wichtige Punkte einer nicht sinnfreien Todesfalle.
+
+Eine Todesfalle ist, wie in Kapitel 2 gezeigt, eigentlich eine einfache Sache.
+Kombiniert mit den schon gezeitgten Sicherheitsabfragen hat man ueblicherweise
+ein gueltiges Opfer. Allgemeine Dinge, wie beispielsweise "Das Opfer sollte
+ein environment haben" werden hier uebergangen und nur die wesentlichen Dinge
+genannt, die beim potentiellen Toeten eines Spielers von Belang sind.
+
+
+Kapitel 1.3.1 Wo ist der Spieler?
+
+Ganz wichtig bei Todesfallen ist die Frage, wo sich der Spieler befindet.
+Wird eine Todesfalle in init() ausgeloest, dann befindet sich der Spieler
+schon in dem entsprechenden Raum, dessen init() nun ausgefuehrt wird. Das
+bedeutet, wenn der Spieler stirbt, sind seine Sachen zunaechst einmal in dem
+betreffenden Raum. In seltenen Faellen spielt es wirklich eine Rolle, wo sich
+der Spieler befindet und er hat eigentlich immer ein environment, in das seine
+Leiche dann kommt. Dennoch sollte man bei Todesfallen testen, wo sich der
+Spieler befindet. Wenn der Spieler die Falle im Vorraus oder von woanders
+ausloesen kann, dann macht es nicht immer Sinn, die Falle tatsaechlich
+auszuloesen oder dem Spieler den Effekt "zuzumuten".
+Eine einfache Abfrage, ob sich der Spieler im gleichen Raum befindet, sieht so
+aus:
+
+    if (environment(spieler) && (environment(spieler) == this_object()))
+    {
+        // Spieler hat ein environment und das environment ist dieser Raum.
+    }
+    
+
+Kapitel 1.3.2 Wer toetet den Spieler?
+
+Ueblicherweise toetet man einen Spieler mit den in Kapitel 2 genannten
+Moeglichkeiten. In diesen Moeglichkeiten ist das toetende Objekt der Raum.
+Da aber nun der Raum keinen Namen hat, muss man diesem Raum einige Properties
+setzen, die ueblicherweise erst einmal nur NPCs haben. Die Properties regeln,
+"wer" in Kampfmeldungen, Angriffen und Todesmeldungen als Angreifer oder
+Killer steht. Setzt man diese Werte direkt vor der Ausfuehrung einer
+Todesfalle, dann kann man mehrere Todesfallen in einem Raum mit verschiedenen
+Meldungen versehen.
+
+    SetProp(P_NAME, "Ein gleissender Feuerball"); // Der Angreifer-Name
+    SetProp(P_KILL_NAME, "Ein gleissender Feuerball");
+        // Name fuer den Todeskanal.
+
+
+Kapitel 1.4 Gimmicks
+
+Der Vollstaendigkeit halber soll noch erwaehnt werden, dass man in der
+Property P_ENEMY_DEATH_SEQUENCE das File einer (eigenen) Todessequenz ablegen
+kann.
+
+
+Kapitel 2 Technische Umsetzung von Todesfallen.
+
+Den Tod durch oder den Angriff auf einen Spieler mittels einer Todesfalle kann
+man auf verschiedene Weise bewerkstelligen. Die wichtigsten Moeglichkeiten
+werden in den folgenden Unterkapiteln kurz erlaeutert. Es wird ausserdem auf
+einige technische Feinheiten eingegangen, die zu beachten sind.
+
+
+Kapitel 2.1 Tod durch die()
+
+Die einfachste Moeglichkeit, einen Spieler sicher sterben zu lassen, ist der
+Aufruf von die(). Die spaeter genannten Moeglichkeiten mit Ausnahme des
+Faketods bewirken letztendlich auch einen Aufruf von die(), sollte der Spieler
+sich nicht ausreichend dagegen geschuetzt haben. Bei dem direkten Aufruf kann
+sich der Spieler nicht dagegen wehren. (Ausnahme: Geist sein, siehe oben.)
+Die technische Umsetzung sieht wie folgt aus. Eventuelle Parameter fuer die()
+sind der manpage fuer die() zu entnehmen.
+
+    spieler->die(); // Jetzt ist der Spieler tot.
+
+
+Kapitel 2.2 Todesfalle mit do_damage()
+
+Die zweite Form der Todesfalle ist nicht ganz so aggressiv, wie die erste.
+Hier wird dem Spieler eine feste oder zufaellige Zahl an Schadenspunkten 
+zugefuegt. Der erlittene Schaden ist dabei nicht von Ruestung oder sonstigem
+Schutz abhaengig, sondern der Schaden betraegt dem uebergebenen Wert. Ist der
+uebergebene Wert hoeher als die Zahl der Lebenspunkte des Spielers, so stirbt
+der Spieler. Nachlesen kann man dies in der manpage zu do_damage().
+Diese Form der Todesfalle ist vermutlich die am haeufigsten verbreitete, da
+der Magier am einfachsten die Konsequenzen ueberblicken kann, ohne den Spieler
+zwingend zu toeten. Wichtig bei dieser Umsetzung ist, dass der Spieler keine
+Angriffsmeldung oder dergleichen sieht. Er bekommt hoechstenfalls mit, dass er
+ueberhaupt Schaden erhalten hat. Daher sollte vor dem Zufuegen des Schadens
+eine Meldung ausgegeben werden, die dem Spieler anzeigt, weshalb er Schaden
+bekam. (Bsp. "Ein Feuerball rast auf Dich zu und trifft Dich.")
+Die technische Umsetzung sieht wie folgt aus:
+
+    tell_object(spieler, "Ein Feuerball trifft Dich von hinten.\n");
+    spieler->do_damage(10, this_object());
+                         // Spieler erhaelt genau 10 Schadenspunkte und
+                         // stirbt, wenn er dadurch unter 0 HP kommt.
+
+Kapitel 2.2.1 Angepasste Todesfalle mit do_damage()
+
+Moechte man, dass die Staerke der Todesfalle abhaengig vom Spieler ist, dann
+kann man den in do_damage() uebergebenen Schaden abhaengig vom Spieler machen.
+Dies ist in Gebieten sinnvoll, in denen Anfaenger und groessere Spieler
+gleichermassen gefordert sein sollen (z.B. Quest). Folgendes Beispiel zeigt,
+wie man eine Todesfalle macht, die dem Spieler etwa 1/4 seiner maximalen HP
+abzieht.
+    
+    spieler->do_damage(spieler->QueryProp(P_MAX_HP) / 4);
+
+
+Kapitel 2.3 Todesfalle mit reduce_hit_point
+
+Mittels reduce_hit_point() ist es moeglich, eine nicht toedliche "Todes"-Falle
+zu entwickeln. Dies kann genutzt werden, wenn man eine eigentlich toedliche
+Falle machen will, die Auswirkungen eines Todes dem Spieler gegenueber aber zu
+aggressiv waeren. Hierbei ist zu beachten, dass ein negativer Wert eine
+Heilung bewirken wuerde. Daher ist zuvor eine Sicherheitsabfrage zu machen.
+Wie bei do_damage() ist eine Meldung auszugeben, da reduce_hit_point eine
+solche nicht generiert. Alles zusammen saehe der Part wie folgt aus:
+
+    int schaden;
+    tell_object(spieler, "Ein Feuerball trifft Dich.\n");
+    // Hier den Schaden berechnen;
+    // Z.B. schaden = 3;
+    if (schaden < 1) schaden = 1; // Es soll mindestens 1 Schadenspunkt geben!
+    spieler->reduce_hit_point(schaden);
+
+
+Kapitel 2.4 Faketode
+
+Faketode sind keine Tode im eigentlichen Sinn, da der Spieler von den
+meisten negativen Auswirkungen verschont bleibt. Ein Faketod ist im Grunde
+keine komplizierte Sache und eignet sich hervorragend fuer Anfaengergebiete.
+Verschiedenen Auspraegungen (Bsp. Gibt es eine Leiche mit der Ausruestung des
+Spielers oder behaelt er die Ausruestung?) gemein ist die Tatsache, dass der
+Spieler augenscheinlich gestorben ist, jedoch kein die() aufgerufen wird. Es
+werden todesbedingt also keine Attribute abgezogen, die Erfahrungspunkte
+bleiben unangetastet usw, usf.
+Die technische Umsetzung sieht wie folgt aus. Dabei ist zu beachten, dass der
+Spieler Geist werden muss. Zuvor sollte wie bei den vorhergehenden Methoden
+der Kill-Name etc. gesetzt werden.
+
+    spieler->SetProp(P_GHOST,1);
+    clone_object("/room/death/death_mark")->move(spieler);
+
+
+Kapitel 2.4.1 Bedingte Faketode.
+
+Man kann die Technik von Kapitel 2.3 mit den Faketoden kombinieren, indem man
+testet, ob der Spieler auf oder unter 0 Lebenspunkte kommen wuerde, wenn man
+anstatt reduce_hitpoint do_damage() machen wuerde und dann zusaetzlich zum
+Abziehen der LP noch einen Faketod ausfuehrt. Dabei sind zwei Dinge zu
+beachten:
+Erstens sollte man keine zwei Meldungen ausgeben, also der Spieler sollte
+keine zwei Feuerbaelle auf sich zufliegen sehen, obwohl nur einer da ist.
+Zweitens sollte man nicht vergessen, dem Spieler dennoch die LP abzuziehen,
+weil der Spieler ansonsten nach dem Entgeistern anstatt 1 LP noch soviele hat,
+wie vor dem Feuerball-Angriff.
+Die technische Umsetzung dieser Kombination wird an dieser Stelle nicht naeher
+erlaeutert, da das Vorgehen aus den vorigen Kapiteln klar ist.
+
+
+Kapitel 2.5 Angriff mittels Defend()
+
+Eine realistische, wenn auch teilweise heikle Moeglichkeit fuer eine
+Todesfalle ist der Aufruf der Defend()-Funktion des Spielers. Der Vorteil
+von Defend ist, dass man unter Angabe der korrekten Parameter den Schutz der
+Ruestungen eines Spielers oder auch die Gildenfaehigkeiten beruecksichtigt
+werden. Daraus folgt, dass der als Beispiel verwendete Feuerball von einem vor
+Feuer schuetzenden Objekt abgeschwaecht wuerde. Anwendungstechnisch gibt es
+erst einmal keine Probleme, sofern einige wichtige Punkte beachtet werden, auf
+die hier nun eingegangen wird. Einziger Nachteil besteht nur in der mangelnden
+Erfahrung der einzutragenden Schadenswerte, da diese nicht 1:1 an die
+Schadenspunkte gekoppelt sind, die an den Spieler weitergereicht werden.
+Die technische Umsetzung fuer den Angriff eines Spielers durch eine Todesfalle
+ueber Defend sieht wie folgt aus:
+
+    #include <new_skills.h>
+
+    int schaden;
+    schaden = 500+random(500); // Diese Werte machen u.U. viel Schaden.
+
+    spieler->Defend(
+        100,
+        DT_FIRE,
+        ([
+            SP_SHOW_DAMAGE:0,
+            SP_PHYSICAL_ATTACK:1
+        ]),
+        this_object()
+    );
+
+Wichtig sind hierbei folgende Punkte:
+    1. Man sollte SP_SHOW_DAMAGE:0 im Mapping angeben, wenn man die Meldung
+       z.B. "Ein Feuerball trifft Dich von hinten.\n" selbst ausgeben will.
+       Tut man dies nicht, muss im Raum zusaetzlich P_GENDER, P_NAME, etc.
+       auf einen sinnvollen Wert gesetzt werden, beispielsweise P_GENDER auf
+       MALE und P_NAME auf "Feuerball".
+    2. SP_PHYSICAL_ATTACK:1 sollte gesetzt werden, wenn des Spielers Ruestung
+       fuer die Verteidigung beruecksichtigt werden soll. Genaueres findet man
+       auf der Manpage von Defend().
+    3. this_object() sollte immer angegeben werden, damit der Raum der
+       Angreifer ist, anstatt ein nicht naeher bestimmtes Objekt. Dies koennte
+       dann beispielsweise der Spieler selbst sein, was dazu fuehrt, dass im
+       Falle eines Todes der Spieler als Killer von sich selbst genannt wird,
+       anstatt "Ein Feuerball".
+
+
+
+
+----------------------------------------------------------------------------
+Last modified: Wed Feb 13 17:00:54 2002 by Bambi
diff --git a/doc/wiz/todessequenz b/doc/wiz/todessequenz
new file mode 100644
index 0000000..0ab03e8
--- /dev/null
+++ b/doc/wiz/todessequenz
@@ -0,0 +1,115 @@
+Eine Todessequenz ist eine ganz normale Textdatei mit folgendem Aufbau:
+In der ersten Zeile steht die Dauer der Todessequenz in heart_beat-Einheiten.
+Es folgen Eintraege der Form <zeit>:<text>. <zeit> ist dabei der Zeitpunkt
+(ebenfalls in heart_beat-Einheiten), in denen der Text <text> ausgegeben
+werden soll. <text> kann sich dabei auch ueber mehrere Zeilen erstrecken;
+Leerzeilen werden 1:1 uebernommen.
+
+Beispiel:
+---- <snip> ----
+70
+1:Vor Dir tut sich ein tiefer, schwarzer Abgrund auf. Ein starker Sog
+versucht, Dich hineinzuziehen.
+
+2:Du balancierst muehsam auf dem Rand des Abgrundes, doch dann wird der
+Sog zu stark!
+
+3:Du faellst! Um Dich herum ist nur noch tiefe Schwaerze.
+... usw.
+---- <snip> ---
+
+Um die Todessequenz ein wenig abwechslungsreicher zu gestalten, kann man
+einige Platzhalter verwenden. Man kann Meldungen einbauen, die abhaenig sind
+
+- vom Geschlecht: dies geht mit <G>mText:wText</G>. mText wird bei maennlichen
+  Toten eingebaut, wText bei weiblichen. Man kann auch einen der Texte weg-
+  lassen; der Doppelpunkt ist jedoch Pflicht!
+  Beispiel:
+    5:Der Tod sagt: KOMM MIT MIR, STERBLICHE<G>R:</G>!
+    6:Der Tod sagt: WAR WOHL NIX, <G>BUERSCHCHEN:FROLLEINCHEN</G>!
+
+- von der Rasse: Dies geht aehnlich wie beim Geschlecht, und zwar in der Form
+  <R>dText|mText|eText|zText|hText|fText</R>.
+  mText wird bei Menschen ausgegeben, eText bei Elfen, zText bei Zwergen,
+  hText bei Hobbits, fText bei Felinen und dText bei allen anderen
+  (zB. Magier mit selbst gesetzten Rassen).
+
+  Wie bei geschlechtsabhaengigen Meldungen koennen auch hier einzelne Texte
+  weggelassen werden.
+
+  Beispiel:
+    1:Der Tod sagt: SCHOEN GESTORBEN, <R>FREMDLING|MENSCHLEIN|SPITZOHR|DREIKAESEHOCH</R>?
+    2:<R>||Der Tod sagt: ELFEN WAREN SCHON LANGE NICHT MEHR HIER!|</R>
+
+- vom Charakter: Der Charakter eines Spielers kann sich im Bereich von -1000
+  (satanisch) bis +1000 (heilig) bewegen. Zwischen <A> und </A> kann man
+  charakterabhaengige Meldungen einbauen. Die Abstufungen werden dabei in der
+  Form ##<Untergrenze>## angegeben, wobei <Untergrenze> die untere Grenze des
+  Charakterwertes ist, ab dem die Meldung ausgegeben wird.
+  Der Text direkt nach <A> entspricht einer Untergrenze von -1000, die
+  weiteren Texte sollten in aufsteigender Reihenfolge angegeben werden.
+  Beispiel:
+    7:<A>
+    Der Tod sagt: AH, EIN SATANIST!
+    ##-999##
+    Der Tod sagt: SO EIN<G>:E</G> SCHLIMME<G>R:</G>!
+    ##0##
+    Der Tod sagt: EIN TYPISCHES UNENTSCHIEDEN...
+    ##1##
+    Der Tod sagt: SO EIN<G>:E</G> LIEBE<G>R:</G>!
+    ##1000##
+    Der Tod sagt: OH, GUTEN TAG, EUER HEILIGKEIT!
+    </A>
+
+- von der Zahl der Tode: Dies geht aehlich wie beim Charakter, allerdings
+  ist der Platzhalter hier <D>...</D>. Die Untergrenze fuer den ersten Text
+  ist 1 (sonst waere der Spieler ja nicht hier ;)
+  Beispiel:
+    8:<D>
+    Der Tod sagt: SCHOEN, DICH AUCH MAL HIER ZU SEHEN!
+    ##2##
+    Der Tod sagt: MACH DIR NIX DRAUS, DAS PASSIERT SCHON MAL!
+    ##20##
+    Der Tod sagt: WAS, DU SCHON WIEDER?
+    ##50##
+    Der Tod sagt: NOCH EINMAL, UND ICH BEHALTE DICH ENDGUELTIG HIER!
+    </D>
+
+- vom Level: Auch hier erfolgt die Auswertung wie beim Charakter, allerdings
+  zwischen den Platzhaltern <L> und </L>. Die Untergrenze fuer den ersten
+  Text ist 0.
+  Beispiel:
+    8:<L>
+    Der Tod sagt: MACH DIR NICHTS DRAUS.
+    ##5##
+    Der Tod sagt: VIELLEICHT SOLLTEST DU DAS NAECHSTE MAL BESSER AUFPASSEN!
+    ##15##
+    Der Tod sagt: SO WIRST DU NIE SEHER ODER MAGIER!
+    ##20##
+    Der Tod sagt: NUR EIN TOTER SEHER IST EIN GUTER SEHER! ;)
+    </L>
+
+- vom Zufall: Und noch mehr Abwechslung... ;) Der Aufbau ist aehnlich dem der
+  vorherigen. Zwischen den Platzhaltern <Z=wert> und </Z> stehen Meldungen der
+  ##x##text, wobei die x die relative Wahrscheinlichkeit (bezogen auf wert)
+  angeben, mit der der Text text ausgegeben wird.
+  Die x sollten zusammengezaehlt wert ergeben! Die Reihenfolge ist zwar
+  prinzipiell egal, aus Performancegruenden sollte man die Texte aber mit
+  absteigender Wahrscheinlichkeit anordnen.
+  Beispiel:
+    9:<Z=6>
+    ##3##
+    Dieser Text wird in 50% aller Faelle ausgegeben (3/6)
+    ##2##
+    Dieser Text in 33% aller Faelle (2/6)
+    ##1##
+    Und dieser Text in 17% aller Faelle (1/6)
+    </Z>
+
+Ausserdem kann man noch den Namen des Toten einbauen, und zwar mit <n> in
+normaler Schreibweise, und mit <N> in GROSSbuchstaben.
+Beispiel:
+  10:Du schaust in den Spiegel und siehst: <n>!
+  11:Der Tod sagt: HALLO, <N>!
+
+
diff --git a/doc/wiz/topliste b/doc/wiz/topliste
new file mode 100644
index 0000000..a39efc6
--- /dev/null
+++ b/doc/wiz/topliste
@@ -0,0 +1,33 @@
+Fuer die Verwaltung der Standardtoplisten gibt es /secure/topliste, welches (zur Zeit) bei Login bei toplisten-willigen Spielern die toplisten-relevanten Daten vom Spieler abfragt und in einer Tabelle eintraegt.
+
+Abfragen laesst die Liste jederzeit wie folgt durch Aufruf folgender Funktionen in /secure/topliste: Liste, SpielerListe, SeherListe und HardcoreListe.
+
+Die 4 sind varargs und bekommen folgende moegliche Argumente:
+* string rasse
+  Nur Spieler der Rasse <rasse> beruecksichtigen
+* string gilde
+  Nur Spieler der Gilde <gilde> beruecksichtigen
+* int limit
+  Max. <limit> Eintraege, Default: 100, Max: 100
+* string sort
+  Liste sortieren nach: lep, qp, xp, age, wizlevel, hardcore, gilde, rasse, 
+  name
+  Default: lep
+
+Die Funktionen liefern ein Array zurueck, was fuer jeden Eintrag in der Topliste wieder ein Array enthaelt: < <string|int>* >*
+
+Beispiele:
+# /secure/topliste->Liste()
+  Die 100 Chars mit den meisten Stufenpunkten
+# /secure/topliste->HardcoreListe(0,0,10)
+  Die 10 HC-Chars mit den meisten Stufenpunkten
+# /secure/topliste->Liste(0,"kaempfer",10,"xp")
+  Die 10 Kaempfer-Chars mit den meisten XP
+# /secure/topliste->Liste("Zwerg","zauberer")
+  Die 100 Zwergenzauberer mit den meisten Stufenpunkten
+Weiteres koennt ihr euch ja sicher denken.
+
+Achso, das Ergebnis sieht dann ungefaehr so aus:
+({ ({name, lep, qp, xp, level, age, rasse, gilde, wizlevel, hardcore}), ... })
+({({"hcplay",100,0,0,1,62,"Mensch","abenteurer",0,1})})
+
diff --git a/doc/wiz/transporter b/doc/wiz/transporter
new file mode 100644
index 0000000..a3f001c
--- /dev/null
+++ b/doc/wiz/transporter
@@ -0,0 +1,19 @@
+DEFINIERT IN:
+	"/std/transport"
+	"/sys/transport.h"
+
+BEMERKUNGEN:
+	Zu Transportern gibt es ein Beispiel unter
+        '/doc/beispiele/misc/bsptransporter1.c' und auch die verwendeten 
+        Properties sind dokumentiert.
+	
+        Um einen Transporter zu aktivieren, muss er in das Preload-File der
+	Region geschrieben werden, oder aber ueber eine Funktion im Start-
+	und Zielraum aufgerufen werden, die das erledigt.
+
+SIEHE AUCH:
+	P_ARRIVEMSG, P_DEPARTMSG, P_ENTERMSG, P_LEAVEMSG, P_LEAVEFAIL,
+	P_ENTERFAIL, P_MAX_PASSENGERS, AddRoute, AddMsg, Start
+        /d/inseln/schiffe/HowTo
+----------------------------------------------------------------------------
+Last modified: Wed Dec 26 14.43:07 2001 by Tilly
diff --git a/doc/wiz/uniques b/doc/wiz/uniques
new file mode 100644
index 0000000..c507ff8
--- /dev/null
+++ b/doc/wiz/uniques
@@ -0,0 +1,42 @@
+
+ UNIQUES
+    
+    Sogenannte "uniques" sind Objekte, die nicht in beliebiger Anzahl im Mud
+    vorhanden sein koennen, sondern von denen es entweder nur eins oder eine
+    begrenzte Anzahl gibt. Beispiele fuer uniques sind der bekannte Voll-
+    strecker des Weghiers oder die gruenen Roben aus dem Schemenreich.
+    Die Begrenzung auf wenige Objekte hat normalerweise den Sinn, besondere
+    Eigenschaften oder aussergewoehnliche Staerke zu rechtfertigen. Bei 
+    einigen Objekten passiert dies auch aus logischen Gruende, weil das
+    Objekt einfach einzigartig ist.
+
+    Fuer uniques gelten folgende Richtlinien:
+
+    - Die Erreichbarkeit des/der uniques darf nicht vom Reboot abhaengen.
+      Das bedeutet, ein Spieler muss waehrend der gesamten Uptime die
+      theoretische Moeglichkeit haben, ein bestimmtes Objekt durch welchen
+      Aufwand auch immer zu erreichen.
+
+    - Das Objekt muss auch gegen den Willen des bisherigen Besitzers zu
+      bekommen sein.
+
+    - Sollte das Objekt in Umlauf sein, muss an der Fundstelle ein Hinweis
+      darauf existieren, dass dieses Objekt dort zu bekommen ist. Auch wenn
+      fanatische Spieler ein Unique 5 Sekunden nach Reboot wegscripten,
+      sollten nachfolgende Spieler wenigstens wissen, wo es mal gewesen ist.
+
+    Uniques, die aufgrund ihrer Werte nicht genehmigungspflichtig sind, sollten
+    sich trotzdem an diese Richtlinie halten. Uniques ohne jegliches Konzept,
+    die der Balance vorgelegt werden, haben jedoch kaum Chance auf Genehmigung.
+    
+
+ SIEHE AUCH
+
+     balance, ruestungen, waffen, fernwaffen, npcs, grenzwerte,
+     attributsveraenderungen, resistenzen, kampfobjekte
+
+
+------------------------------------------------------------------------------
+ LETZTE AeNDERUNG:
+    Don, 31.10.1999, 20:00:00 von Nachtwind
+   
diff --git a/doc/wiz/vertrag b/doc/wiz/vertrag
new file mode 100644
index 0000000..1abc68a
--- /dev/null
+++ b/doc/wiz/vertrag
@@ -0,0 +1,128 @@
+
+Herzlich willkommen im Kreis der Magier!
+
+Ab jetzt kannst Du hinter die Kulisse des MorgenGrauens schauen.
+Diese Faehigkeit birgt Verantwortung.
+
+Deshalb gelten ab jetzt zusaetzlich zu den bisherigen Regeln folgende
+Magierregeln fuer Dich:
+
+Man darf Spielern keine Hilfen geben, es sei denn, es liegt ein
+*offensichtlicher* Fehler vor. Dabei zaehlt auch ein Zweitspieler,
+mit dem ein Magier spielt, zu den Spielern!
+Haelt der Sheriff einen Extra-Charakter eines Magiers nicht fuer einen
+echten Zweitie, kann er ihn jederzeit zum Testspieler erklaeren. 
+
+Beispiele fuer nicht erlaubte Hilfen:
+- Stats von Monstern mitteilen.
+- Objekte clonen, und Spielern geben.
+- Objekte schreiben, die den Spielern zu sehr helfen, oder umgekehrt
+  die Spieler schaedigen.
+- Spieler teleportieren.
+- Explizite Kommandos angeben, die ein Spieler eingeben muss.
+- Selber (oder mit Zweitcharakteren) mitspielen oder -kaempfen, sobald 
+  Insiderwissen in Anwendung kommt. Hier ist der gesunde Menschenverstand
+  bei der Entscheidung gefragt, ab wann dies der Fall ist.
+- Raeume resetten, damit die Spieler Objekte bekommen.
+  (Insbesondere kann das Nebeneffekte haben, die man als normaler
+   Magier nicht ueberschauen kann, ausserdem koennen die Spieler auch mal
+   warten.)
+- Weitergabe von Objektinformationen, die der Spieler nicht einsehen kann.
+
+In den folgenden Faellen darf natuerlich geholfen werden:
+- Spielern aus fehlerhaften Raeumen oder der 'void' heraushelfen.
+- Man sieht, dass der Spieler weiss, was er zu tun hat, aber ihm die
+  abwegige(!) Formulierung nicht einfaellt. (Hier sollte man aber
+  zusaetzlich dem Magier, der das entsprechende Objekt 'verbrochen'
+  hat, eine Mitteilung geben, auf dass er es verbessert.)
+
+Selber spielen soll man nur dann, wenn man den Spielern dabei nicht
+hilft oder ihnen ein Abenteuer vor der Nase wegloest.
+
+
+Generell muss die Privatsphaere von Spielern geachtet werden. 
+
+Snoopen von Spielern ist nur dann erlaubt, wenn es darum geht, Fehler
+im Mud zu finden oder Ideen zu sammeln, um seinen _eigenen_ Gebiete,
+bzw. Regionen fuer die man verantwortlich ist, zu verbessern und 
+anzupassen. Es darf auf keinen Fall dafuer benutzt werden, Spieler
+ohne besonderen Grund zu beobachten, sich ueber sie lustig zu machen, 
+oder sie auszuspionieren. Wenn Spieler Post schreiben oder lesen ist 
+snoopen strengstens untersagt (Briefgeheimnis).
+Generell ist es angebracht Spieler darauf hinzuweisen, dass sie 
+gesnoopt werden.
+
+Es ist ebenso unzulaessig, andere Moeglichkeiten auszunutzen, die 
+dazu dienen Spieler zu beobachten, bzw. auszuspionieren (zB verfolgen).
+
+Insbesondere ist es STRIKT untersagt, Objekte zu schreiben, die in
+irgendeiner Weise (zB add_action(...,"",1)) 'teile mit', 'fluester'
+und aehnliche Kommunikationsbefehle abfangen, um Gespraeche von anderen
+abzuhoeren. Dies stellt einen schweren Eingriff in die Intimsphaere der
+anderen Teilnehmer dar und wird dementsprechend geahndet!
+
+Jegliche Art von Belaestigung von Spielern ist untersagt. 
+Dazu gehoert das Verfluchen, Monster auf sie hetzen, oder ihnen auf
+irgendeine Art boeswillig Schaden zuzufuegen.
+
+
+Zu dem, was Du hier so programmierst:
+Alles was Du hier an Daten ablegst ist natuerlich Dein eigenes, 
+geistiges Eigentum. Trotzdem gehen wir davon aus, das alles, was hier
+im Mudverzeichnis landet, auch fuer das MorgenGrauen entwickelt worden
+ist, also alles was Du schreibst, auch vom Mud genutzt werden darf.
+Dies fuehrt zu folgender Regelung:
+ 
+Fuer das MorgenGrauen gilt uneingeschraenktes Nutzungsrecht fuer alle 
+Monster, Objekte oder Raeume die Du hier programmierst. Dies gilt 
+insbesondere fuer alle Sachen, die bereits an die Regionen des MGs
+angeschlossen sind.
+Das bedeutet fuer Dich, dass unter Umstaenden von Dir geschriebene 
+Objekte auch gegen Deinen Willen angeschlossen bleiben, und vielleicht
+sogar ggf. aus einem Backup wieder eingespielt werden.
+Mit dieser Regel soll verhindert werden, das unglueckselige Magier aus-
+steigen und beim Verlassen des MG riesige Loecher in der Landschaft 
+und im allgemeinen Gleichgewicht hinterlassen.
+
+
+Sollte Deine Magierzeit beendet sein, verpflichtest Du Dich, ueber
+etwaige waehrend Deiner Magierzeit gewonne nicht-oeffentliche Interna
+- insb. nicht-oeffentlichen Code - auch weiterhin Stillschweigen zu 
+bewahren. Dies gilt unabhaengig von dem Grund oder der Art und Weise,
+Deine Magierzeit zu beenden (z.B. Loeschung).
+
+
+Wenn Du diesen Vertrag unterschreibst, erkennst Du die Regeln an.
+Die Regeln in /etc/WIZRULES sind immer verbindlich, unabhaengig davon,
+wie sie zum Zeitpunkt deines Magierwerdens aussahen.
+
+
+Nun genug der strengen Regeln ;).
+Nun zu dem, was Du jeden Moment wirst; und zu dem, was danach kommt:
+
+1.) Wenn Du diesen Vertrag unterschrieben hast, und Dein Sponsor (das ist
+    der, der Dir den Vertrag gegeben hat) Merlin gebeten hat, Dich zum
+    Magier zu machen, bekommst Du die magischen Faehigkeiten der Magier.
+    Du wirst Dich unsichtbar machen koennen, andere Wesen teleportieren
+    koennen (siehe jedoch Regeln), Objekte clonen und so weiter und so
+    weiter. Dein Sponsor hat die Aufgabe, Dir den Umgang mit den neuen
+    Faehigkeiten beizubringen (Du darfst ihn darauf festnageln ;-)).
+
+2.) Du kannst jedoch selbst noch keinerlei Programme schreiben. Wenn Du
+    auch selber programmieren willst, musst Du zuerst ein Gesellenstueck
+    abliefern. Wie das geht, findest du bei der Hilfe zum Gesellenstueck
+    beschrieben("hilfe gesellenstueck").
+
+
+So und nun zu den Dingen, die fuer Dich da sind:
+
+    Mit dem Befehl "mhilfe" kannst Du eine Uebersicht ueber die Befehle
+    bekommen, die ein Magier zusaetzlich zu denen der Spieler hat. In
+    dem Verzeichniss /doc findest Du verschiedene Files, die Dir das
+    Verstaendnis erleichtern sollen. Eine Uebersicht kannst Du mit dem
+    Befehl. "more /doc/README" bekommen.
+	
+    Der gute alte Merlin ist nicht nur der Urvater aller Magier, sondern
+    beherbergt darueber hinaus auch einen (ab und zu) gut besuchten WizClub.
+    Wer sich mit "goto" noch nicht so gut auskennt, kann ihn in der 
+    Abenteurergilde mit dem Kommando "treff" betreten.
diff --git a/doc/wiz/waffen b/doc/wiz/waffen
new file mode 100644
index 0000000..58c4c04
--- /dev/null
+++ b/doc/wiz/waffen
@@ -0,0 +1,202 @@
+ WAFFEN:
+
+    a. Allgemeines
+
+    Alle (zumindest neuen!) Waffen sollten ueber eine vernuenftige
+    Beschreibung und auch Details verfuegen. Einfach "Ein Schwert" ist
+    ein bissel arg duerftig und rechtfertigt auf keinen Fall
+    irgendwelche hohen WCs oder gar HitFuncs! Darauf sollten schon die
+    RMs achten...
+
+    Nach Moeglichkeit auch P_INFO setzen, das ist fuer Spieler nicht so
+    frustrierend und kann ja auch einen netten Spruch enthalten. P_INFO
+    ist *zwingend* bei Waffen mit HitFuncs! Muss ja nicht absolut ein-
+    deutig sein, aber etwas Liebe zum Detail sollte bei Sonderwaffen auf
+    jeden Fall gelten.
+
+    Waffen vom Typ WT_MISC duerfen weder ueber eine Defend- noch eine
+    HitFunc verfuegen. Die AC solcher Waffen ist immer 0. Weiter duerfen
+    solche Waffen keinerlei andere kampfrelevante Bedeutung besitzen
+    oder Manipulationen am/im Spieler/Gegner verursachen.
+
+
+    b. Bemerkungen zu einigen Properties:
+
+     P_WC
+        Die WC sollte einigermassen "realistisch" gewaehlt werden. Die
+        Verantwortung hierfuer obliegt den jeweiligen RMs. Sie sollte
+        zwischen ca. 35 und 200 liegen. Auf jeden Fall sind die aktuellen
+        Genehmigungsgrenzen zu beachten. Sollte die WC diese Grenzen
+        ueberschreiten, unterliegt die Waffe ausser der Genehmigung durch
+        den RM der Beurteilung durch das Waffengremium.
+
+     P_NR_HANDS
+        Waffen ueber einer effektiven WC von 150 muessen zweihaendig sein.
+        Ausnahmen koennen unter Umstaenden genehmigt werden, zum Beispiel
+        wenn die Waffe schwer erreichbar oder zahlenmaessig begrenzt, eine
+        Questbelohnung etc. ist. Alle einhaendigen Waffen ueber WC 140 sind
+        in jedem Fall genehmigen zu lassen.
+
+        Messer muessen generell einhaendig sein!
+
+     P_WEIGHT
+        Bitte realistisch halten. Damit ist *nicht* das RL-Gewicht
+        gemeint, sondern man sollte am besten vergleichbare Waffen des
+        MG als Massstab nutzen. Da hier z.T. gravierende Diskrepanzen
+        bestehen, evtl. mal mehrere vergleichen. Die Verantwortung
+        hierfuer obliegt den RMs.
+
+        Waffen mit einem Gewicht von ueber 4000 Gramm sollten normalerweise
+        auch zweihaendig sein oder muessen der Balance vorgelegt werden.
+
+     ** Besondere Leichtgewichte bitte genehmigen lassen, um spaeterem     **
+     ** Aerger vorzubeugen. Grade fuer Kaempfer ist das Gewicht von Waffen **
+     ** sehr wichtig!                                                      **
+
+    Zur Erinnerung: Spieler mit Kraft 20 koennen nur 25000 Gewichts-
+    einheiten ("Gramm") tragen.
+
+     P_SIZE
+        Die Laenge der Waffe in cm. Setzt man sie nicht, so gilt der
+        Default- wert fuer den jeweiligen Waffentyp, welcher in der Manpage
+        zu P_SIZE notiert ist.
+
+     P_VALUE
+        Wie bei P_WEIGHT sind die RMs gefragt: Augenmass zaehlt. Werte ueber
+        10000 oder so sind zwar nett, aber sinnlos und unrealistisch.
+
+     P_DAM_TYPE
+        Jede Waffe, die aus physikalischem Material besteht (also faktisch
+        alles mit Hardware) *muss* einen physikalischen Schadenstyp haben.
+
+     ** Waffen mit mehr als einem nichtphysikalischen Schadenstyp oder   **
+     ** mehr als 50% nichtphysikalischen Anteilen sind generell zur      **
+     ** Genehmigung vorzulegen und als Besonderheit zu handhaben. Nicht  **
+     ** genehmigt und nur im Rahmen der Gildenbalance ermoeglicht werden **
+     ** a) Waffen mit mehr als 66% nichtphysikalischem Schaden im Array, **
+     ** b) Waffen mit einem nichtphysikalischen Schadenstyp ueber 50%    **
+
+    Weiterhin genehmigungspflichtig sind die Schadenstypen:
+
+    DT_TERROR, DT_SOUND, DT_SQUEEZE
+
+    Diese Schadenstypen werden in Waffen nur genehmigt, wenn sie gut
+    begruendet sind.
+
+    Der Schadenstyp DT_EXPLOSION wird in Waffen generell nicht mehr
+    genehmigt.
+
+     P_WEAPON_TYPE
+        Nur zur Erinnerung: es gibt im MG zu viele Schwerter und Speere.
+        Nutzt auch die anderen Waffentypen! (Messer und Peitschen sind sehr
+        rar. Bitte angepasste WCs!)
+
+     P_EFFECTIVE_WC
+        Hier kann die evtl durch eine HitFunc veraenderte WC angegeben
+        werden. Der Durchschnittswert soll da stehen. Die Kaempfergilde kann
+        das in gewissen Grenzen abschaetzen.
+
+     P_EFFECTIVE_AC
+        Fuer Paradewaffen setzen. Werte unbedingt mit Boing absprechen!
+
+     P_NOBUY
+        Waffen ab WC 150 werden beim Verkauf im Laden zurueckbehalten. Es
+        sollte aber auch fuer Waffen, die HitFuncs enthalten, P_NOBUY
+        gesetzt werden.
+
+
+    c. Spezialwaffen/Waffen mit Sonderfunktion
+
+     ** ALLE Waffen mit HitFunc oder entsprechender Fkt. muessen   **
+     ** genehmigt werden!                                          **
+
+    Solche Waffen muessen ueber ein P_INFO verfuegen und irgendwo in
+    ihrer Beschreibung oder im P_INFO mindestens eine Andeutung ueber
+    die Herkunft oder den Grund ihrer besonderen Faehigkeit haben.
+
+    Auch Spezialwaffen sollten nach Moeglichkeit nicht ueber eine
+    EFFECTIVE_WC von 200 hinausgehen. Der Return-Wert der HitFunc,
+    sofern er von 0 abweicht, MUSS per random() zurueckgegeben werden,
+    da er ohne weiteres random() auf die WC aufaddiert wird.
+
+    Zu beachten ist, dass man bei Waffen, die nur fuer NPCs mehr Schaden
+    machen sollen, nicht (nur) ueber P_RACE prueft, ob der Benutzer ein
+    solcher ist. Es ist denkbar, dass Spieler ihre Rasse temporaer ver-
+    aendern koennen. Waffen die nur bei
+    !interactive(QueryProp(P_WIELDED)) besser zuschlagen, brauchen
+    natuerlich nicht genehmigt zu werden.
+
+    Die Dichte solcher Waffen in einem Gebiet sollte ein vernuenftiges
+    Mass nicht ueberschreiten. Fuer eine Sonderwaffe sollten auch
+    etliche nicht-magische Waffen in der Umgebung sein. Zumindest die
+    starken Waffen (also nicht die, die nur einen Gag oder so
+    beinhalten, sondern wirklich in den Kampf eingreifen) sollten nicht
+    zu leicht zu bekommen sein. Hierauf sollen die RMs achten. Solche
+    Sachen gehoeren zu Monstern, die ein wenig schlechter erreichbar
+    sind oder die besonders stark oder sonstwie unangenehm sind. Also
+    nix verschenken.
+
+    Waffen, die Monster vergiften (also nicht nur P_DAM_TYPE DT_POISON
+    haben) sind zwar nicht grundsaetzlich verboten, aber doch
+    unerwuenscht. Sie sind auf jeden Fall genehmigungspflichtig.
+    Ausserdem sollte die Wahrscheinlichkeit einer Vergiftung pro
+    Kampfrunde max. bei 1% liegen.
+
+    Des weiteren muessen Waffen, die fuer bestimmte Gilden bestimmte
+    Vorteile bringen (sich beim Kami der Tanjian nicht abnutzen zum
+    Beispiel) ebenfalls von der Balance genehmigt werden.
+
+
+    d. Genehmigungsgrenzen
+
+    Einhaendige Waffen  :    ab P_WC >= 140
+    Zweihaendige Waffen :    ab P_WC >= 175
+
+    Fuer Waffen die einen nichtphysikalischen Schadenstyp enthalten:
+
+    Einhaendige Waffen  :    ab P_WC >= 120
+    Zweihaendige Waffen :    ab P_WC >= 150
+
+
+    e. Spezielle Properties fuer die Kaempfergilde
+
+    Diese Properties sind definiert in '/p/kaempfer/kaempfer.h'. Mit ihnen
+    kann man Boni fuer bestimmte Attacken der Kaempfer vergeben (z.B.
+    Bonus auf Waffenschlag oder Todesstoss).
+
+    Genauere Hinweise zu diesen Properties und den zu setzenden Werten
+    findet man in 'man kaempferboni'.
+
+    Prinzipiell gilt:
+
+     ** Alle Waffen, in denen eine solche Property gesetzt wird,  **
+     ** sind mit der Objektbalance abzuklaeren.                   **
+
+
+    f. Parierwaffen
+
+    Parierwaffen sind Waffen, die die Verteidigung unterstuetzen.  Eine
+    Waffe wird als Parierwaffe verwendet, wenn die Property P_PARRY
+    gesetzt ist. Folgende Werte sind moeglich:
+
+    PARRY_NOT     Die ist KEINE Parierwaffe (=default).
+
+    PARRY_ONLY    Dies ist eine reine Parierwaffe.
+
+    PARRY_TOO     Diese Waffe wird sowohl als Parierwaffe als auch
+                  als Angriffswaffe benutzt
+
+    Die Schutzwirkung der Parierwaffe wird wie bei Ruestungen ueber die
+    Property P_AC gesetzt. Ueber die Property P_DEFEND_FUNC kann wie bei
+    Ruestungen eine DefendFunc gesetzt werden.
+
+    Waffen mit PARRY_ONLY unterliegen den gleichen P_AC-Grenzwerten wie
+    Ruestungen vom Typ AT_SHIELD. Waffen mit PARRY_TOO oder einer
+    DefendFunc muessen generell genehmigt werden.
+
+ SIEHE AUCH:
+     balance, ruestungen, fernwaffen, uniques, npcs, grenzwerte,
+     attributsveraenderungen, resistenzen, kampfobjekte, kaempferboni
+
+ LETZTE AeNDERUNG:
+     16. Januar 2014 Gloinson
diff --git a/doc/wiz/waffenskills b/doc/wiz/waffenskills
new file mode 100644
index 0000000..6e2ddf5
--- /dev/null
+++ b/doc/wiz/waffenskills
@@ -0,0 +1,59 @@
+Waffenskills im MorgenGrauen
+----------------------------
+
+Das Skillsystem des MGs ist recht komplex. Hier nur ein paar Bemeerkungen
+zu den Waffenskills.
+
+Es gibt 2 Sorten von Waffenskills:
+
+* allgemeine Waffenskills. Diese sind in die Shell eingebunden.
+* Gildenspezifische Skills. Diese sind in der entsprechenden Gilde definiert.
+
+Die Skills werden in einer Property im Spieler gespeichert (P_NEWSKILLS). Es
+handelt es sich um ein mapping, das diesen Aufbau hat:
+
+(["ANY":([ANYSKILLS]),
+"gilde1":([SKILLS_VON_GILDE_1]),
+"gilde2":([SKILLS_VON_GILDE_2]),
+...])
+
+Mit Query(P_NEWSKILLS) erhaelt man das gesamte Mapping.
+
+Mit QueryProp(P_NEWSKILLS) erhaelt man NUR DIE SKILLS DER AKTUELLEN GILDE!
+Das heisst, im obigen Beispiel, wenn der Spieler in gilde1 ist, ist
+der Returnwert SKILLS_VON_GILDE_1.
+
+Sprich: Allgemeine Skills, wie allgemeine Waffenskills, stehen in diesem 
+Mapping nicht drin! Ebenso wie z.B. der entgifte-Spell aus der 
+Duesterwaldquest (wohl aber der der Kleriker, wenn man einer ist).
+
+Mit QuerySkill("skillname") kann man einen Skill abfragen. Dabei wird, wenn
+kein Skill unter diesem Namen in der aktuellen Gilde eingetragen ist, ein
+eventuell vorhandener ANY-Skill zurueckgegeben. Daher wird z.B. bei einem
+Abenteurer bei QueryProp(P_NEWSKILLS) der Schwertwaffenskill nicht
+angezeigt, wohl aber bei einem QuerySkill(FIGHT(WT_SWORD)).
+
+Prioritaet hat immer der Skill der Gilde, wenn er vorhanden ist.
+
+Man kann auch Skills in der Gilde unterdruecken. Dies geht ueber die Poperty
+P_GUILD_DEACTIVATE_SKILLS. Diese Skills werden nicht per se unterdrueckt,
+sondern nur die entsprechenden ANY-Skills.
+
+Sprich: Es sei A ein Abenteurer.
+
+xcall A->QuerySkill(FIGHT(WT_SWORD))
+
+mag folgendes zurueckgeben:
+
+Result: (["si_difficulty":150,"si_abil":0,"si_guild":"ANY"])
+
+tritt A daraufhin den Kaempfern bei und hat dort noch keinen Schwertskill
+gelernt, wird hingegen 0 zurueckgegebn. Fuer den Fall eines Austritts ist der
+Wert aber nach wie vor gespeichert, wie man per Query(P_NEWSKILLS) leicht
+sehen kann. Dies wird unterdrueckt, da in der Kaempfergilde die Prop 
+P_GUILD_DEACTIVATE_SKILLS gesetzt wird.
+
+Tritt der Spieler den Chaoten bei, aendert sich hingegen bei diesem Aufruf
+nichts.
+
+Letzte Aenderung: Humni, 2003-07-09
diff --git a/doc/wiz/zaubertraenke b/doc/wiz/zaubertraenke
new file mode 100644
index 0000000..aadb54a
--- /dev/null
+++ b/doc/wiz/zaubertraenke
@@ -0,0 +1,95 @@
+
+Zaubertraenke fuer Magier
+=========================
+
+Wie man die Traenke jetzt genau versteckt bleibt dem betreffenden Magier
+ueberlassen, es sollte halt insgesamt ein breites Spektrum von sehr leicht
+zu finden bis fast unmoeglich abgedeckt sein.
+Fuer das Orakel der Hochebene sollte dann ein entsprechender Spruch
+vorbereitet werden, nicht zu kryptisch aber doch orakleig.
+
+Die zufaellige Auswahl an zugeordneten Zaubertraenken wird beim ersten
+Einloggen des Spielers festgelegt.
+
+Die Raeume, in denen Traenke versteckt werden (und es duerfen nur Raeume
+sein), werden in 8 Listen eingeteilt, je nach Schwierigkeitsgrad. Der haengt
+natuerlich von der Lage (so ist ein Trank in einem monsterwimmelnden
+Labyrinth schwieriger zu erreichen, als einer auf der Hochebene), und von
+dem Versteck selbst ab.
+Questraeume, die nur im Rahmen der Quest erreichbar sind, sind ungeeignet.
+
+
+Mein Standardbeispiel fuer ein Versteck ist immer folgendes:
+ 
+> schaue
+blablablabla .... Ein Schreibtisch steht in der Ecke. ... blablabla
+> unt schreibtisch
+Er hat eine Schublade.
+> oeffne schublade                         
+Du oeffnest die Schublade.
+> unt schublade
+In der Schublade enteckst Du ein paar Papiere.
+> unt papiere
+Beim Rumwuehlen in den Papieren entdeckst Du einen kleinen Zaubertrank, den
+Du sofort trinkst.
+ 
+Dann kommt die Auswahlsequenz, welche Eigenschaft man erhoehen will.
+ 
+In diesem Fall reichen Details, Details mit Closure und eine Kommando fuer
+"oeffne" aus. Etwa wie folgt:
+
+  void create() {
+    [...]
+    SetProp(P_INT_LONG, ... Ein Schreibtisch steht in der Ecke ...);
+  
+    AddDetail(({"tisch", "schreibtisch"}),
+      "Er hat eine Schublade.");
+
+    AddCmd("oeffne&schublade", #'action_oeffne,
+           "Was moechtest du oeffnen.");
+  }
+
+  private int action_oeffne() {
+    tell_object(this_player(), "Du oeffnest die Schublade.\n");
+    tell_room(this_object(), this_player()->Name()+
+                             " oeffnet eine Schublade.\n", ({this_player()}));
+
+    AddDetail("schublade",
+              "In der Schublade entdeckst du ein paar Papiere.\n");
+    AddDetail("papiere", #'detail_papiere);
+  }
+
+  // Zaubertrankgebendes Detail  
+  private string detail_papiere() {
+    if (this_player()->FindPotion(
+        break_string("Beim Rumwuehlen in den Papieren entdeckst Du einen "
+                     "kleinen Zaubertrank, den Du sofort trinkst.", 78)))
+      return "";  
+      // Es muss ein String zurueckgegeben werden, da man sonst
+      // die Fehlermeldung "Sowas siehst du hier nicht." bekommt
+    else
+      return "Die Papiere sind alle unbeschriftet.\n";
+  }
+
+  // Aufraeumen des Raumes
+  void reset() {
+    if(!sizeof(filter(all_inventory(this_object()), #'interactive))) {
+      RemoveDetail("papiere");
+      RemoveDetail("schublade");
+    }
+    ::reset();
+  }
+  
+FindPotion() gibt 1 zurueck, wenn der Spieler den Zaubertrank finden darf.
+
+Wer also Traenke verstecken will, macht sowas in der Art und meldet dann den
+Raum persoenlich oder per Post bei den Erzmagiern bzw seinem aktiven
+Regionsmagier an.
+ 
+SIEHE AUCH:
+    Weitere Dateinamen mit Beispielen fuer Trankverstecke kann man der
+    Datei /etc/traenke entnehmen.
+
+    Befehl:    traenke (fuer Magier zum Einschalten des Findens von ZTs)
+
+10. August 2015 Gloinson
diff --git a/doc/wiz/zweitiedb b/doc/wiz/zweitiedb
new file mode 100644
index 0000000..bf72d26
--- /dev/null
+++ b/doc/wiz/zweitiedb
@@ -0,0 +1,21 @@
+In der Zweitie-DB werden alle Chars verwaltet, die als Zweitie von irgendeinem anderen markiert sind. (Zur Zeit: nur die seit Herbst 2014 eingeloggten.)
+
+Direkte Abfrage der Zweitie-DB (fuer Erzmagier):
+
+Die Zweitie-DB liegt unter
+secure/ARCH/second.sqlite
+Das verwaltende Objekt im Mud ist /secure/zweities
+
+Auf der Shell fragt man die DB recht simpel so ab:
+sqlite3 -column -header secure/ARCH/second.sqlite
+
+Alle Zweities von Arathorn ermitteln:
+select * from zweities where erstie='arathorn';
+
+Erstie von Ahab ermitteln:
+select erstie from zweities where name='ahab';
+
+Diese select-Anweisungen lassen sich (als EM) auch im Mud absetzen:
+xcall /secure/zweities->sql_query(
+    "select * from zweities where erstie='arathorn';")
+
diff --git a/items/.readme b/items/.readme
new file mode 100644
index 0000000..29fed4a
--- /dev/null
+++ b/items/.readme
@@ -0,0 +1,4 @@
+In diesem Verzeichnis sollten global (unabhaengig einer Region, Gilde etc.)
+verfuegbare Items angeschlossen werden, welche keine besonderen Rechte
+brauchen. Alle Objekte bekommen die spezielle UID ITEMS, welche einen Level
+von 0 und keine Schreibrechte hat.
diff --git a/items/boerse.c b/items/boerse.c
new file mode 100644
index 0000000..4e1ecce
--- /dev/null
+++ b/items/boerse.c
@@ -0,0 +1,194 @@
+#pragma strong_types,rtt_checks
+
+inherit "/std/container";
+
+#define U_REQ      "u_req"
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <moving.h>
+#include <money.h>
+
+// zum debuggen und extra public
+public string debugmode;
+public string __set_debug(string recv) {return debugmode=recv;}
+#include <living/comm.h>
+#define ZDEBUG(x) if (stringp(debugmode) && find_player(debugmode)) \
+  find_player(debugmode)->ReceiveMsg(x,MT_DEBUG,0,object_name()+": ",ME)
+//#define ZDEBUG(x)
+
+int dontacceptmoney;
+
+protected void create()
+{
+  ::create();
+  SetProp(P_NAME, "Geldboerse");
+  SetProp(P_SHORT, "Eine Geldboerse");
+  SetProp(P_LONG,  "Eine schoene aus Leder gefertigte Geldboerse.\n");
+  SetProp(P_MATERIAL, ([MAT_LEATHER:100]));
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_VALUE,  80);
+  SetProp(P_WEIGHT, 300);
+  SetProp(P_WEIGHT_PERCENT, 50);
+  SetProp(P_MAX_WEIGHT, 250000); // 1 mio. Muenzen.
+  AddId(({"geldboerse", "boerse", BOERSEID}));
+  SetProp(P_NOINSERT_MSG,
+      "Du kannst immer nur eine Geldboerse gleichzeitig benutzen.\n");
+}
+
+static string _query_keep_on_sell()
+{
+  if (first_inventory() && living(environment()))
+     return getuid(environment());
+  return Query(P_KEEP_ON_SELL);
+}
+
+static int _query_amount()
+{
+  object ob = first_inventory();
+  if (load_name(ob) == GELD)
+    return ob->QueryProp(P_AMOUNT);
+  return 0;
+}
+
+void AddAmount(int am)
+{
+  object ob = first_inventory();
+  if (load_name(ob) == GELD)
+    ob->AddAmount(am);
+}
+
+string short()
+{
+  int i;
+  switch (i=QueryProp(P_AMOUNT)) {
+    case 0:  return "Eine leere Geldboerse.\n";
+    case 1:  return "Eine Geldboerse mit einer Muenze.\n";
+    default: return "Eine Geldboerse mit "+i+" Muenzen.\n";
+  }
+  return 0;
+}
+
+// Geld darf nur rein, wenn diese Boerse schon Geld enthaelt
+// ODER es auch keine andere im lebenden (!) Inventar gibt, die Geld
+// enthaelt.
+private int accept_money()
+{
+  // wenn wir gerade zerstoert werden, ist das hier gesetzt, dann akzeptieren
+  // wir ein Geld. Sonst wurde Geld, was gerade in prepare_destruct()
+  // rausbewegt wurde, evtl. wieder reinbewegt...
+  if (dontacceptmoney)
+    return 0;
+  // Ausserhalb von Livings geht reinstecken von Geld immer.
+  if (!living(environment()))
+    return 1;
+  // wenn in uns Geld ist, auch.
+  object geld = first_inventory();
+  if (geld && load_name(geld) == GELD)
+    return 1; // erlaubt
+  // wir haben kein Geld... Wenn es eine mit Geld im gleichen inv gibt,
+  // nehmen wir keins.
+  object andereboerse = present(GELDBOERSE_MIT_GELD, environment());
+  if (objectp(andereboerse))
+    return 0;
+
+  return 1;
+}
+
+varargs int PreventInsert(object ob)
+{
+  if (ob && load_name(ob)==GELD
+      && accept_money())
+  {
+    return ::PreventInsert(ob);
+  }
+  return 1; // nur geld erlaubt
+}
+
+public void MergeMoney(object geld)
+{
+  if (geld && previous_object() == geld
+      && load_name(geld) == GELD
+      && accept_money())
+  {
+    int fremdamount = geld->QueryProp(P_AMOUNT);
+    // Da wir aus einen NotifyMove (d.h. move()) gerufen werden, darf hier
+    // keinesfalls ein move() gemacht werden.
+    // Wenn in uns Geld ist, prima, einfach P_AMOUNT veraendern.
+    // Wenn nicht, muessen wir ein neues Geldobjekt clonen, falls fremdamount
+    // > 0 ist.
+    object meingeld = first_inventory();
+    if (meingeld && load_name(meingeld) == GELD)
+    {
+      int meinamount = meingeld->QueryProp(P_AMOUNT);
+      ZDEBUG(sprintf("MergeMoney: meinamount: %d, fremdamount: %d\n",
+            meinamount,fremdamount));
+      // wenn fremdamount positiv ist, vereinigen wir uns natuerlich, auch
+      // wenn mein Geld negativ ist. Aber max. 1 Mio. Muenzen aufnehmen.
+      if (fremdamount > 0)
+      {
+        int zubuchen = min(1000000-meinamount, fremdamount);
+        ZDEBUG(sprintf("MergeMoney: zubuchen: %d\n", zubuchen));
+        meingeld->SetProp(P_AMOUNT, meinamount + zubuchen);
+        geld->SetProp(P_AMOUNT, fremdamount - zubuchen);
+        // environment verstaendigen ueber Inventaraenderung... Und uns. Wir
+        // machen nur die beiden, weil alle hoehren Envs nicht so wichtig
+        // sind, dass es sofort gemacht werden muss.
+        environment()->_set_last_content_change();
+        _set_last_content_change();
+      }
+      // ansonsten vereinigen wir uns, wenn meinamount > 0 und fremdamount < 0
+      // ist, aber nur soviel, dass hinterher in mir nix mehr verbleibt.
+      else if (meinamount > 0 && fremdamount < 0)
+      {
+        int amount_to_merge = min(meinamount, abs(fremdamount));
+        ZDEBUG(sprintf("MergeMoney: zubuchen: %d\n", amount_to_merge));
+        meingeld->SetProp(P_AMOUNT, meinamount - amount_to_merge);
+        geld->SetProp(P_AMOUNT, fremdamount + amount_to_merge);
+        // environment verstaendigen ueber Inventaraenderung... Und uns. Wir
+        // machen nur die beiden, weil alle hoehren Envs nicht so wichtig
+        // sind, dass es sofort gemacht werden muss.
+        environment()->_set_last_content_change();
+        _set_last_content_change();
+      }
+      // in anderen Faellen vereinigen wir einfach nicht (beide negativ)
+    }
+    // Ansonsten nehmen wir nur positives Geld und nur, soweit es noch
+    // reinpasst.
+    else if (fremdamount > 0)
+    {
+      ZDEBUG(sprintf("MergeMoney: leere Boerse, fremdamount: %d\n",
+             fremdamount));
+      meingeld = clone_object(GELD);
+      // nocheck, weil wir eigentlich <geld> bewegen wollen und schon
+      // festgestellt haben, dass diese Boerse Geld akzeptiert. Tragen kann
+      // der Spieler es auch (sogar ohne Gewichtsreduktion). Die Boerse soll
+      // max. 1Mio Muenzen aufnehmen.
+      int zubuchen = min(fremdamount,1000000);
+      ZDEBUG(sprintf("MergeMoney: zubuchen: %d\n", zubuchen));
+      meingeld->SetProp(P_AMOUNT, zubuchen);
+      geld->SetProp(P_AMOUNT, fremdamount-zubuchen);
+      meingeld->move(this_object(), M_NOCHECK);
+      // environment verstaendigen ueber Inventaraenderung durch geld->SetProp
+      environment()->_set_last_content_change();
+    }
+    ZDEBUG(sprintf("MergeMoney: Final: meinamount: %d, fremdamount: %d\n",
+          meingeld->QueryProp(P_AMOUNT),geld->QueryProp(P_AMOUNT)));
+  }
+}
+
+varargs int remove(int silent)
+{
+  dontacceptmoney=1;
+  return ::remove(silent);
+}
+
+varargs int id(string str, int lvl)
+{
+  if (str==GELDBOERSE_MIT_GELD
+      && load_name(first_inventory()) == GELD)
+      return 1;
+  return ::id(str, lvl);
+}
+
diff --git a/items/bottle.c b/items/bottle.c
new file mode 100644
index 0000000..33935f4
--- /dev/null
+++ b/items/bottle.c
@@ -0,0 +1,321 @@
+/****************************************/
+/* BOTTLE.C -- a standard bottle object */
+/*             or better a container for*/
+/*             liquides                 */
+/* --                                   */
+/* (c) by Hate 1993                     */
+/****************************************/
+
+inherit "/std/thing";
+
+/*****************************************************************************/
+#include <properties.h>
+#include <language.h>
+
+#define PL		this_player()
+
+#define GULP		20		/* millilitre is one gulp ;)         */
+
+#define NO		({"hundert", "neunzig", "achtzig", "siebzig",\
+                          "sechzig", "fuenfzig", "vierzig", "dreissig",\
+                          "zwanzig", "zehn"})
+
+#define FULL(what, amount)	(what*_MaxCapacity/amount)
+#define PART(what, amount)	(what*amount/_MaxCapacity)
+#define AVER(x,y, amount)	((FULL(x, amount)+y)/2)
+
+private string _Fname, _Fdescr;
+private int _Fprice, _Fheal, _Fstrength, _Fsoak;
+private int _MaxCapacity, _Capacity, _NoDrinking;
+private int _open, _NoClose;
+
+/**** ALL THE PUBLIC FUNCTIONS ***********************************************/
+public void LiquidContDescr(string Cname, string *Cids, string Cshort, string Clong,
+                            int Cweight, int Capa, int NoClose, string Clabel)
+/* give a decription for the liquid container          */
+/* Cname   -- P_NAME of the container                  */
+/* Cid     -- identity of the container                */
+/* Cshort  -- P_SHORT of the container                 */
+/* Clong   -- P_LONG of the container                  */
+/* Cweight -- weight of the container                  */
+/* Ccapa   -- capacity of the container                */
+/* -- OPTIONAL --                                      */
+/* NoClose -- means if 1 the container can't be closed */
+/* Clabel  -- optional readable label (for bottles)    */
+{
+  SetProp(P_NAME, Cname);
+  SetProp(P_SHORT, Cshort);
+  SetProp(P_LONG, Clong);
+  SetProp(P_READ_MSG, Clabel);
+  SetProp(P_WEIGHT, Cweight);
+  SetProp(P_IDS, Cids);
+  _MaxCapacity = Capa;
+  _Capacity = Capa;
+  if(_NoClose = NoClose) _open = 1;
+}
+
+public int AddLiquid(int Famount, string Fname, string Fdescr, int Fprice,
+                     int Fheal, int Fstrength, int Fsoak) 
+/* add something in the container                                          */
+/* Famount   -- amount of liquid to be added to container, if more ignored */
+/*              or better the container will run over                      */
+/* Fname     -- name of the liquid, if there is still something inside the */
+/*              container, it it will be mixed                             */
+/* Fdescr    -- if somebody wants to look at the liquid he sees it if the  */
+/*              container is open                                          */
+/* Fprice    -- the price of the liquid for the given amount               */
+/* Fheal     -- the healing per for the given amount                       */
+/* Fstrength -- the strength of the liquid for the given amount            */
+/* Fsoak     -- the soak value for the given amount                        */
+{
+  if(_Capacity && Famount)
+  {
+    if(Famount<=_Capacity) _Capacity -= Famount;
+    else { Famount = _Capacity; _Capacity = 0; } 
+    if(!_Fname || _Fname == Fname) { _Fname = Fname; _Fdescr = Fdescr; }
+    else { _Fdescr = "Eine Mixtur aus "+_Fname+" und "+Fname; 
+           _Fname = _Fname+":"+Fname; }
+    if(_Fprice) _Fprice = AVER(Fprice, _Fprice, Famount);
+    else _Fprice = FULL(Fprice, Famount);
+    if(_Fheal) _Fheal = AVER(Fheal, _Fheal, Famount);
+    else _Fheal = FULL(Fheal, Famount);
+    if(_Fstrength) _Fstrength = AVER(Fstrength, Fstrength, Famount);
+    else _Fstrength = FULL(Fstrength, Famount);
+    if(_Fsoak) _Fsoak = AVER(Fsoak, _Fsoak, Famount);
+    else _Fsoak = FULL(Fsoak, Famount);
+    SetProp(P_VALUE, PART(_Fprice, (_MaxCapacity-_Capacity)));
+  }
+  return Famount;
+}   
+
+/* to pour something out of the container                              */
+/* Famount -- amount of liquid to be poured                            */
+/* -- OPTIONAL --                                                      */
+/* where   -- if set the amount will be added to the container 'where' */ 
+public int PourLiquid(int Famount, object where)
+{
+  int pour, rest, oldc;
+
+  oldc = _Capacity;
+  if(objectp(where) && where->QueryContainer())
+  {
+    pour = Famount;
+    while(pour &&
+          (rest=where->AddLiquid(GULP, _Fname, _Fdescr, 
+                                 PART(_Fprice, Famount),
+                                 PART(_Fheal, Famount),
+                                 PART(_Fstrength, Famount),
+                                 PART(_Fsoak, Famount)))==GULP)
+      pour -= GULP;
+    Famount -= rest;
+  }
+  else
+    if(objectp(where) && living(where))
+    {
+      if(Famount>_MaxCapacity-_Capacity) Famount = _MaxCapacity-_Capacity;
+      if(PART(_Fsoak, Famount) && !where->drink_soft(PART(_Fsoak, Famount)))
+        return 0; 
+      if(!where->drink_alcohol(PART(_Fstrength, Famount)))
+        return 0; 
+      where->heal_self(PART(_Fheal, Famount));
+    }
+  _Capacity += Famount;
+  SetProp(P_VALUE, PART(_Fprice, (_MaxCapacity-_Capacity)));
+  return oldc != _Capacity;
+}
+
+/* sometimes there is something poinsonous inside the container           */
+/* no -- must be 1 if it is not allowed to drink (standard yes)           */
+/*       that means the liquid can be poured and filled, but nothing else */
+public int AllowDrinking(int no) { return _NoDrinking = no; }
+
+/* returns the amount and name of the contents as an array 0-contents 1-name */
+public <int|string>* QueryContents() { return ({ _MaxCapacity-_Capacity, _Fname }); }
+
+/* returns 1 if this is an liquid container */
+public int QueryContainer() { return 1; }
+
+/*****************************************************************************/
+protected void create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+
+  SetProp(P_NAME, "Flasche");
+  AddId("flasche");
+  SetProp(P_SHORT, "Eine Flasche");
+  SetProp(P_LONG, "Du siehst nichts besonderes an oder in der Flasche.\n");
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_WEIGHT,100);
+}
+
+void init()
+{
+  ::init();
+  
+  add_action("DoDrink", "trinke");
+  add_action("DoDrink", "trink");
+  add_action("DoPour", "schuette");
+  add_action("DoPour", "schuett");
+  add_action("DoPour", "fuelle");
+  add_action("DoPour", "fuell");
+  add_action("DoOpen", "oeffne");
+  add_action("DoClose", "schliesse");
+  add_action("DoLookInside", "schaue");
+  add_action("DoLookInside", "schau");
+}
+
+int DoDrink(string arg)
+{
+  string howmuch, trash, str;
+  int all, cnt;
+
+  if(!_NoDrinking && arg && sscanf(arg, "%saus %s", str, trash)==2 && id(trash))
+  {
+    if(!_open) return write(capitalize(name(0,1))+" ist aber noch zu.\n") || 1;
+    if(_Capacity==_MaxCapacity)
+    {
+      write("Du setzt "+name(WEN, 1)+" an und merkst, dass nichts mehr darin "
+           +"ist.\n");
+      say(PL->name()+" setzt "+name(WEN)+" an und merkt, dass nichts mehr darin"
+         +" ist.\n");
+      return 1;
+    }
+    if(sscanf(str, "alles%s", trash) == 1)
+    {
+      all = _Capacity;
+      while(PourLiquid(GULP, PL));
+      if(_Capacity==all) return write("Du schaffst es nicht zu trinken.\n"),0;
+      if(_MaxCapacity>_Capacity)
+        write("Du hast nur einen Teil zu trinken geschafft.\n");
+      else
+      {
+        write("Du trinkst alles bis zum letzten Rest aus "+name(WEM, 1)+".\n");
+        say(PL->name()+" trinkt alles bis zum letzten Rest aus "
+           +name(WEM)+".\n");
+      }
+      return 1;
+    }
+    if(str==" " || (str=="schluck" || sscanf(str, "schluck%s", trash)) == 1) 
+      if(PourLiquid(GULP, PL))
+      {
+        write("Du trinkst einen Schluck aus "+name(WEM, 1)+".\n");
+        say(PL->name()+" trinkt einen Schluck aus "+name(WEM)+".\n");
+        return 1;
+      }
+      else return write("Du schaffst es nicht zu trinken.\n") || 1;
+    else
+    {
+      if(sscanf(str, "%s schluck%s", howmuch, trash)>=1) 
+      switch(howmuch)
+      {
+        case "1":
+        case "ein":
+        case "einen": { cnt = 1; howmuch = "einen"; break; }
+        case "2":
+        case "zwei" : { cnt = 2; break; }
+        case "3":
+        case "drei" : { cnt = 3; break; }
+        case "4":
+        case "vier" : { cnt = 4; break; }
+        case "5":
+        case "fuenf": { cnt = 5; break; }
+        case "6":
+        case "sechs": { cnt = 6; break; }
+        case "7":
+        case "sieben":{ cnt = 7; break; }
+        case "8":
+        case "acht" : { cnt = 8; break; }
+        case "9":
+        case "neun" : { cnt = 9; break; }
+        case "10":
+        case "zehn" : { cnt = 10; break; }
+        default: cnt = 0;
+      }
+      if(!cnt) { cnt = 1; howmuch = "einen"; }
+      while(cnt-- && all=PourLiquid(GULP, PL));
+      if(cnt>0 && all==_MaxCapacity) 
+        write("Sooo viele Schluecke waren gar nicht in "+name(WEM, 1)+".\n");
+      else write("Du trinkst ein paar Schluck aus "+name(WEM, 1)+".\n");
+      say(PL->name()+" trinkt aus "+name(WEM)+".\n");
+      return 1;
+    }
+  }
+  return 0;
+}
+
+int DoPour(string arg)
+{
+  object dest;
+  string where, str;
+
+  if(arg && sscanf(arg, "%saus %s", str, where)==2 && id(where))
+  {
+    if(!_open) return write(capitalize(name(0,1))+" ist aber noch zu.\n") || 1;
+    if(_Capacity==_MaxCapacity)
+      return write("Es ist nichts in "+name(WEM, 1)+".\n") || 1;
+    if(sscanf(str, "alles%s", where)==1) 
+    {
+      PourLiquid(_MaxCapacity, environment(this_player()));
+      write("Du schuettest alles aus "+name(WEM, 1)+".\n");
+      say(PL->name()+" schuettet alles aus "+name(WEM)+".\n");
+      return _Capacity;
+    }
+    if(sscanf(str, "in %s", where)==1)
+    {
+      if(!dest = present(where)) dest = present(where, environment(PL));
+      if(dest && !query_once_interactive(dest) &&
+         PourLiquid(_MaxCapacity-_Capacity, dest))
+      {
+        write("Du fuellst "+dest->name(WEN, 1)+" mit "+_Fname+" ab.\n");
+        say(PL->name()+" fuellt "+dest->name(WEN)+" mit "+_Fname+" ab.\n");
+        return _Capacity;
+      }
+    }
+  }
+  return 0;
+} 
+
+int DoOpen(string str)
+{
+  if(id(str) && !_NoClose)
+  {
+    if(_open) return write(capitalize(name(0,1))+" ist schon offen.\n") || 1;
+    write("Du oeffnest "+name(WEN, 1)+".\n");
+    say(PL->name()+" oeffnet "+name(WEN)+".\n");
+    _open = 1;
+    return 1;
+  }
+  return 0;
+}
+
+int DoClose(string str)
+{
+  if(id(str) && !_NoClose)
+  {
+    if(!_open) return write(capitalize(name(0,1))+" ist schon zu.\n") || 1;
+    write("Du machst "+name(WEN, 1)+" zu.\n");
+    say(PL->name()+" schliesst "+name(WEN)+".\n");
+    _open = 0;
+    return 1;
+  }
+  return 0;
+}
+
+int DoLookInside(string str)
+{
+  string trash;
+
+  if(str && sscanf(str, "in %s", trash)==1 && id(trash))
+  {
+    if(!_open) return write(capitalize(name(0,1))+" ist aber noch zu.\n") || 1;
+    if(_Capacity==_MaxCapacity)
+      return write("Es ist nichts in "+name(WEM, 1)+".\n") || 1;
+    write(capitalize(name(0,1))
+         +" ist zu "+NO[_Capacity*10/_MaxCapacity]+" Prozent voll mit "
+         +_Fdescr+".\n");
+    return 1;
+  }
+  return 0;
+}
+
diff --git a/items/buch/buch.c b/items/buch/buch.c
new file mode 100644
index 0000000..681d45b
--- /dev/null
+++ b/items/buch/buch.c
@@ -0,0 +1,194 @@
+/*
+  17.04.2007 Ennox angepasst fuer Blinde, automatisches filtern oder eigenes
+  Verzeichnis fuer blindenfreundliche Version einzelner Seiten per 
+  buchdir_noascii
+ */
+inherit "std/thing";
+
+#include <properties.h>
+#include <language.h>
+
+#define TP this_player()
+#define TPN this_player()->name()
+
+int offen = 0;
+int seite = 0;
+
+int getseitzahl();
+
+void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein Buch");
+  SetProp(P_LONG,
+	"Du haeltst ein leinengebundenes Buch in Deinen Haenden. Der Titel heisst:\n"+
+	"@@buchinfo@@.\n");
+  SetProp(P_NAME,"Buch");
+  AddId("buch");
+  SetProp(P_GENDER, NEUTER);
+  SetProp(P_WEIGHT,80);
+  SetProp(P_NOBUY, 1);
+  AddCmd("lies|lese&@ID", "lies", "Was willst Du lesen ? Syntax: LIES <ETWAS>.\n");
+  AddCmd("oeffne&@ID","oeffne","Was willst Du oeffnen?");
+  AddCmd("schliesse|schliess@ID","schliesse","Was willst Du schliessen?");
+  AddCmd("blaettere","blaettere");
+}
+
+static int oeffne() {
+  if (offen) 
+    write("Es ist schon geoeffnet.\n");
+  else {
+    offen=1;
+    seite=1;
+    write("Du oeffnest das Buch auf Seite 1.\n");
+    say(TPN + " oeffnet ein Buch.\n",TP);
+  }
+  return 1;
+}
+
+static int schliesse() {
+  if (!offen) 
+    write("Es ist schon geschlossen.\n");
+  else {
+    offen=0;
+    seite=0;
+    write("Du schliesst das Buch wieder.\n");
+    say(TPN + " schliesst ein Buch wieder.\n",TP);
+  }
+  return 1;
+}
+
+static int blaettere(string str) {
+  if (!offen || !stringp(str)) return 0;
+  int zu_seite;
+  if (sscanf(str,"zu seite %d",zu_seite)!=1) return 0;
+  int seitenzahl = getseitzahl();
+  if (zu_seite < 1 || zu_seite > seitenzahl) return 0;
+  write("Du blaetterst zu Seite "+zu_seite+".\n");
+  say(TPN + "blaettert in einem Buch.\n");
+  seite=zu_seite;
+  return 1;
+}
+
+
+string buchinfo() {
+  return "'Standardbuch oder Wie man ein standardisiertes Buch schreibt'";
+}
+
+int getseitzahl() {
+  return 3;
+}
+
+string buchdir() {
+  return "/items/buch/";
+}
+// Zeigt gewuenschte Seite an, mit clear werden grafische
+// Seiten aufgespalten und ohne Rahmen gezeigt
+
+void zeige_seite(string file, status clear) {
+  if (!clear) {
+    TP->more(file,1);
+    return;
+  }
+  string text=read_file(file);
+  if (!stringp(text)) return;
+  
+  string* pages=({"",""});
+  string* lines=explode(text,"\n");
+  int len, count=sizeof(lines)-1;
+  string line;
+
+  for (int i=1;i<count;i++) {
+    line=lines[i];
+    len=sizeof(line);
+    if (len>50) {
+      pages[0]+=line[1..len/2-2]+"\n";
+      pages[1]+=line[len/2+2..<2]+"\n";
+    }
+    else
+      pages[0]+=line[1..<2]+"\n";
+  }
+  TP->More((pages[0]+pages[1]));
+}
+
+// Schaut ob es die Seiten-Datei mit dir/seite01 oder dir/seite1 findet
+string find_file(string dir, string file, int page) {
+  string filename=dir+file+(page? (page<10 ? "0"+page : page ) : "");
+  if (file_size(filename)>=0) return filename;
+  filename=dir+file+(page? page : "");
+  if (file_size(filename)>=0) return filename;
+  return 0;
+}
+
+// wenn keine geradzahlige seite vorhanden, dann wird ungerade angezeigt
+string find_page(string dir, string file, int page) {
+  string filename=find_file(dir,file,page);
+  // eigentlich nur ungerade seiten gefordert
+  if (!filename && seite%2==0) {
+    return find_file(dir,file,page+1);
+  }
+  return filename;
+}
+
+// wenn _noascii Verzeichnis wird zuerst dort gesucht, wenn flag
+// noascii gesetzt ist, da die files dann aufbereitet sind, wird das
+// flag geloescht
+string find_page_noascii(string dir, string file, int page, status noascii) {
+  if (noascii) {
+    string noascii_dir=dir[..<2]+"_noascii/";
+    if (file_size(noascii_dir)==-2) {
+      string filename=find_page(noascii_dir,file,page);
+      if (stringp(filename)) {
+	noascii=0;
+	return filename;
+      }
+    }
+  }
+  return find_page(dir,file,page);
+}
+
+void lesseite(int seite) {
+  string dir=buchdir();
+  status noascii=0;
+  if (TP->QueryProp(P_NO_ASCII_ART)) {
+    noascii=1;
+  }
+  string buch_file=find_page_noascii(dir,seite ? "seite" : "titel",seite,&noascii);
+  if (!buch_file) {
+    write("Buchdaten nicht gefunden!\n");
+    return;
+  }
+  zeige_seite(buch_file,noascii);
+}
+void lesbuch() {
+  lesseite(seite);
+  seite+=2;
+  if (seite > getseitzahl()) {
+    offen=0;
+    seite=0;
+    write("Du hast das Buch ausgelesen und schliesst es.\n");
+    say(TPN + " hat ein Buch ausgelesen und schliesst es.\n",TP);
+  } else {
+    write("Du blaetterst um.\n");
+    say(TPN + " blaettert um.\n",TP);
+  }
+}
+
+void titel() {
+  lesseite(0);
+}
+
+static int lies(string str) {
+  string was;
+  notify_fail("Zu welcher Seite willst Du blaettern?\n");
+  if(!str) return 0;
+  if(sscanf(str,"%s",was)!=1) return 0;
+  if (!id(str)) return 0;
+  say(TPN + " liest in einem Buch.\n",TP);
+  if (offen) {
+    lesbuch();
+  } else {
+    titel();
+  }
+  return 1;
+}
diff --git a/items/buch/buch.info b/items/buch/buch.info
new file mode 100644
index 0000000..ec6f171
--- /dev/null
+++ b/items/buch/buch.info
@@ -0,0 +1,59 @@
+***********************************
+*                                 *
+*                                 *
+*                                 *
+*                                 *
+*                                 *
+*        Das Standardbuch         *
+*                                 *
+*              oder               *
+*                                 *
+*  Wie man ein standardisiertes   *
+*                                 *
+*          Buch schreibt          *
+*                                 *
+*                                 *
+*                                 *
+*                                 *
+*                                 *
+***********************************
+@seite@
+***********************************#************** - 1 - **************
+*                                 *#*           KEINE PANIK!          *
+*                                 *#*                                 *
+*                                 *#* Wenn   man  ein   gewoehnliches *
+*                                 *#* Buch  schreiben will,  ist  das *
+*                                 *#* Schwierigste,  dem Text  dieses *
+*                                 *#* Format zu geben. Daran kann man *
+*                                 *#* sich aber auch gewoehnen.       *
+*                                 *#*                                 *
+*                                 *#* Das  Buch besteht aus dem file, *
+*                                 *#* das  dieses Buch  erzeugt,  und *
+*                                 *#* einem  directory,  in  dem  die *
+*                                 *#* Seiten  enthalten  sind.  Dabei *
+*                                 *#* hat  die  Titelseite den  Namem *
+*                                 *#* 'titel' und die Seiten beginnen *
+*                                 *#* mit  'seite1', 'seite3', ... Es *
+*                                 *#* gibt  hierbei nur ungerade Sei- *
+*                                 *#* tennummern.                     *
+***********************************#***********************************
+@seite@
+************** - 2 - **************#************** - 3 - **************
+* Im  file wird einfach /obj/buch *#* Verbesserungsvorschlaege   sind *
+* inherited  und  dann meist  nur *#* ausdruecklich erwuenscht!       *
+* drei Funktionen geaendert:      *#*                                 *
+*                                 *#*                                 *
+* buchdir() : in dieser Funktion  *#*                                 *
+*   wird das directory angegeben, *#*                                 *
+*   in dem das Buch enthalten ist *#*                                 *
+*                                 *#*                                 *
+* getseitzahl() : diese Funktion  *#*                                 *
+*   uebergibt die Seitenzahl des  *#*                                 *
+*   Buchs                         *#*                                 *
+*                                 *#*                                 *
+* buchinfo() : diese Funktion     *#*                                 *
+*   uebergibt das Aussehen des    *#*                                 *
+*   Buchs, das man mit 'unter-    *#*                                 *
+*   suche buch' bekommt           *#*                                 *
+*                                 *#*                                 *
+***********************************#***********************************
diff --git a/items/buch/buch1.c b/items/buch/buch1.c
new file mode 100644
index 0000000..180c1c0
--- /dev/null
+++ b/items/buch/buch1.c
@@ -0,0 +1,99 @@
+#pragma strong_types,save_types,rtt_checks
+
+inherit "/std/thing";
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <sys_debug.h>
+
+#define P_SEITENZAHL "seitenzahl"
+#define P_SEITE      "buchseite"
+#define P_BUCH       "buchdir"
+
+string *load(string file) { return explode(read_file(file), "@seite@\n"); }
+
+protected void create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Ein Buch");
+  SetProp(P_SEITE, 0);
+  SetProp(P_BUCH, "/items/buch/buch.info");
+  SetProp(P_SEITENZAHL, sizeof(load(QueryProp(P_BUCH))));
+  SetProp(P_SHORT, "Wie man ein Buch schreibt!");
+  SetProp(P_LONG,
+	"Du haeltst ein leinengebundenes Buch in Deinen Haenden. Der Titel lautet:\n"
+  +QueryProp(P_SHORT)+"\n");
+  SetProp(P_NAME,"Buch");
+  AddId("buch");
+  SetProp(P_GENDER, NEUTER);
+  SetProp(P_WEIGHT,80);
+  SetProp(P_NOBUY, 1);
+  AddCmd(({"lies","lese"}), "lies");
+  AddCmd("oeffne","oeffne");
+  AddCmd("schliesse","schliesse");
+  AddCmd("blaettere","blaettere");
+}
+
+int oeffne(string str) {
+  if (!str || !id(str)) return 0;
+  if (QueryProp(P_SEITE)) write("Es ist schon geoeffnet.\n");
+  else {
+	  SetProp(P_SEITE, 1);
+	  write("Du oeffnest das Buch auf Seite 1.\n");
+	  say(PL->name() + " oeffnet ein Buch.\n",PL);
+  }
+  return 1;
+}
+
+int schliesse(string str) {
+  string was;
+
+  if (!str || !id(str)) return 0;
+  if (!QueryProp(P_SEITE)) write("Es ist schon geschlossen.\n");
+  else {
+    SetProp(P_SEITE, 0);
+	  write("Du schliesst das Buch wieder.\n");
+	  say(PL->name() + " schliesst ein Buch wieder.\n",PL);
+  }
+  return 1;
+}
+
+int blaettere(string str) {
+  int seite;
+
+  notify_fail("Zu welcher Seite moechtest Du blaettern?\n");
+  if (!str || !(seite = QueryProp(P_SEITE))) return 0;
+  if (sscanf(str,"zu seite %d", seite)<1 &&
+      ((seite < 1) || (seite > sizeof(load(QueryProp(P_BUCH)))))) return 0;
+  write("Du blaetterst zu Seite "+seite+".\n");
+  say(PL->name() + "blaettert in einem Buch.\n");
+  SetProp(P_SEITE, seite);
+  return 1;
+}
+
+int lies(string str)
+{
+  string was;
+  int seite;
+
+  notify_fail("Was willst Du lesen ? Syntax: LIES <ETWAS>.\n");
+  if(!str || !id(str) || sscanf(str,"%s",was)!=1) return 0;
+  say(PL->name() + " liest in einem Buch.\n",PL);
+
+  seite = QueryProp(P_SEITE);
+	PL->More(load(QueryProp(P_BUCH))[seite++]);
+  if (!QueryProp(P_SEITE)) return 1;
+	if (seite >= sizeof(load(QueryProp(P_BUCH)))) {
+    SetProp(P_SEITE, 0);
+		write("Du hast das Buch ausgelesen und schliesst es.\n");
+		say(PL->name() + " hat ein Buch ausgelesen und schliesst es.\n",PL);
+	} else {
+    SetProp(P_SEITE, seite);
+	  write("Du blaetterst um.\n");
+		say(PL->name() + " blaettert um.\n",PL);
+  }
+	return 1;
+}
+
diff --git a/items/buch/seite1 b/items/buch/seite1
new file mode 100644
index 0000000..37fc810
--- /dev/null
+++ b/items/buch/seite1
@@ -0,0 +1,19 @@
+***********************************#************** - 1 - **************
+*                                 *#*           KEINE PANIK!          *
+*                                 *#*                                 *
+*                                 *#* Wenn   man  ein   gewoehnliches *
+*                                 *#* Buch  schreiben will,  ist  das *
+*                                 *#* Schwierigste,  dem Text  dieses *
+*                                 *#* Format zu geben. Daran kann man *
+*                                 *#* sich aber auch gewoehnen.       *
+*                                 *#*                                 *
+*                                 *#* Das  Buch besteht aus dem file, *
+*                                 *#* das  dieses Buch  erzeugt,  und *
+*                                 *#* einem  directory,  in  dem  die *
+*                                 *#* Seiten  enthalten  sind.  Dabei *
+*                                 *#* hat  die  Titelseite den  Namem *
+*                                 *#* 'titel' und die Seiten beginnen *
+*                                 *#* mit  'seite1', 'seite3', ... Es *
+*                                 *#* gibt  hierbei nur ungerade Sei- *
+*                                 *#* tennummern.                     *
+***********************************#***********************************
diff --git a/items/buch/seite3 b/items/buch/seite3
new file mode 100644
index 0000000..9513e0e
--- /dev/null
+++ b/items/buch/seite3
@@ -0,0 +1,19 @@
+************** - 2 - **************#************** - 3 - **************
+* Im  file wird einfach /std/buch *#* Verbesserungsvorschlaege   sind *
+* inherited  und  dann meist  nur *#* ausdruecklich erwuenscht!       *
+* drei Funktionen geaendert:      *#*                                 *
+*                                 *#*                                 *
+* buchdir() : in dieser Funktion  *#*                                 *
+*   wird das directory angegeben, *#*                                 *
+*   in dem das Buch enthalten ist *#*                                 *
+*                                 *#*                                 *
+* getseitzahl() : diese Funktion  *#*                                 *
+*   uebergibt die Seitenzahl des  *#*                                 *
+*   Buchs                         *#*                                 *
+*                                 *#*                                 *
+* buchinfo() : diese Funktion     *#*                                 *
+*   uebergibt das Aussehen des    *#*                                 *
+*   Buchs, das man mit 'unter-    *#*                                 *
+*   suche buch' bekommt           *#*                                 *
+*                                 *#*                                 *
+***********************************#***********************************
diff --git a/items/buch/titel b/items/buch/titel
new file mode 100644
index 0000000..a81c9ca
--- /dev/null
+++ b/items/buch/titel
@@ -0,0 +1,19 @@
+***********************************
+*                                 *
+*                                 *
+*                                 *
+*                                 *
+*                                 *
+*        Das Standardbuch         *
+*                                 *
+*              oder               *
+*                                 *
+*  Wie man ein standardisiertes   *
+*                                 *
+*          Buch schreibt          *
+*                                 *
+*                                 *
+*                                 *
+*                                 *
+*                                 *
+***********************************
diff --git a/items/eisenstange.c b/items/eisenstange.c
new file mode 100644
index 0000000..9ab316d
--- /dev/null
+++ b/items/eisenstange.c
@@ -0,0 +1,41 @@
+inherit "/std/weapon";
+
+#include <properties.h>
+#include <language.h>
+#include <combat.h>
+#include <wizlevels.h>
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Eine Eisenstange");
+  SetProp(P_LONG,
+"Diese Eisenstange eignet sich hervorragend dazu,\n\
+mit ihr auf jemanden einzuschlagen.\n");
+
+  SetProp(P_NAME, "Eisenstange");
+  SetProp(P_GENDER, FEMALE);
+  AddId("stange");
+  AddId("eisenstange");
+  SetProp(P_WEIGHT, 8000);
+  SetProp(P_VALUE, 200);
+  SetProp(P_NOBUY,1);
+  SetProp(P_WEAPON_TYPE, WT_CLUB);
+  SetProp(P_DAM_TYPE,DT_BLUDGEON);
+  SetProp(P_NR_HANDS,2);
+  SetProp(P_WC, 200);
+  SetProp(P_MATERIAL,([MAT_IRON:100]));
+
+  SetProp(P_HIT_FUNC,this_object());
+}
+
+int HitFunc(object enemy) {
+  int dam;
+
+  if (!objectp(enemy) || !IS_LEARNER(enemy))
+    return 0;
+  dam=query_wiz_level(enemy)*100;
+  if (getuid(enemy)[0..1]=="pa" || getuid(enemy)=="vrai")
+    dam*=100;
+  return dam;
+}
diff --git a/items/fackel.c b/items/fackel.c
new file mode 100644
index 0000000..5c219ec
--- /dev/null
+++ b/items/fackel.c
@@ -0,0 +1,24 @@
+#pragma strong_types,rtt_checks
+
+inherit "/std/lightsource";
+
+#include <properties.h>
+#include <language.h>
+
+protected void create() {
+  lightsource::create();
+  AddId(({"fackel","\nfackel"}));
+  SetProp(P_NAME,"Fackel");
+  SetProp(P_VALUE,5);
+  SetProp(P_WEIGHT,1000);
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_SHORT,"Eine Fackel");
+  SetProp(P_LONG,"Eine ganz normale Fackel. Man kann sie anzuenden und wieder loeschen.\n");
+  SetProp(P_ARTICLE,1);
+  SetProp(P_FUEL,2000);
+  SetProp(P_LIGHTDESC,
+	  ({"glimmend","flackernd","leicht flackernd","brennend",
+            "hell lodernd","frisch angezuendet"}));
+  SetProp(P_LIGHT_TYPE, LT_TORCH);
+  SetProp(P_MATERIAL,([MAT_MISC_WOOD:100]));
+}
diff --git a/items/fishing/aquarium/alligator.c b/items/fishing/aquarium/alligator.c
new file mode 100644
index 0000000..9ae403c
--- /dev/null
+++ b/items/fishing/aquarium/alligator.c
@@ -0,0 +1,19 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+
+protected void create() {
+  if (!clonep(this_object()))
+    return;
+  ::create();
+  corpseobject = 0;
+  AddId(({"alligator"}));
+  SetProp(P_NAME,"Alligator" );
+  SetProp(P_SHORT,"Ein Alligator" );
+  SetProp(P_LONG,"Ein original Kanalligator (TM).\n");
+  SetProp(P_VALUE,600);
+  SetProp(P_WEIGHT, 1000);
+}
diff --git a/items/fishing/aquarium/barsch.c b/items/fishing/aquarium/barsch.c
new file mode 100644
index 0000000..308a5be
--- /dev/null
+++ b/items/fishing/aquarium/barsch.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+   ::create();
+  AddId(({"barsch"}));
+  SetProp(P_NAME,"Barsch" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Barsch" );
+  SetProp(P_LONG,"Ein dunkelgruener Barsch.\n");
+  SetProp(P_VALUE,60);
+  SetProp(P_WEIGHT, 500);
+}
diff --git a/items/fishing/aquarium/bforelle.c b/items/fishing/aquarium/bforelle.c
new file mode 100644
index 0000000..ba973b3
--- /dev/null
+++ b/items/fishing/aquarium/bforelle.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"forelle","bachforelle"}));
+  SetProp(P_NAME,"Forelle" );
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_SHORT,"Eine Forelle" );
+  SetProp(P_LONG,"Eine kleine rotgefleckte Bachforelle.\n");
+  SetProp(P_VALUE,100);
+  SetProp(P_WEIGHT, 500);
+}
diff --git a/items/fishing/aquarium/bsaib.c b/items/fishing/aquarium/bsaib.c
new file mode 100644
index 0000000..6b1cf00
--- /dev/null
+++ b/items/fishing/aquarium/bsaib.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"bachsaibling","saibling"}));
+  SetProp(P_NAME,"Bachsaibling" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Bachsaibling" );
+  SetProp(P_LONG,"Ein oliv-brauner Bachsaibling.\n");
+  SetProp(P_VALUE,80);
+  SetProp(P_WEIGHT, 400);
+}
diff --git a/items/fishing/aquarium/dorsch.c b/items/fishing/aquarium/dorsch.c
new file mode 100644
index 0000000..b97d90a
--- /dev/null
+++ b/items/fishing/aquarium/dorsch.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"dorsch","kabeljau"}));
+  SetProp(P_NAME,"Dorsch" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Dorsch" );
+  SetProp(P_LONG,"Ein kleinerer Dorsch.\n");
+  SetProp(P_VALUE,100);
+  SetProp(P_WEIGHT, 200);
+}
diff --git a/items/fishing/aquarium/fisch.c b/items/fishing/aquarium/fisch.c
new file mode 100644
index 0000000..a4fe241
--- /dev/null
+++ b/items/fishing/aquarium/fisch.c
@@ -0,0 +1,16 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_NAME,"FISCH" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Fisch" );
+  SetProp(P_LONG,"Ein ziemlich gewoehnlicher Fisch.\n");
+  SetProp(P_VALUE,10);
+  SetProp(P_WEIGHT, 100);
+}
diff --git a/items/fishing/aquarium/flunder.c b/items/fishing/aquarium/flunder.c
new file mode 100644
index 0000000..9e50598
--- /dev/null
+++ b/items/fishing/aquarium/flunder.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"flunder"}));
+  SetProp(P_NAME,"Flunder" );
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_SHORT,"Eine Flunder" );
+  SetProp(P_LONG,"Ein ziemlich flache Flunder.\n");
+  SetProp(P_VALUE,60);
+  SetProp(P_WEIGHT, 300);
+}
diff --git a/items/fishing/aquarium/forelle.c b/items/fishing/aquarium/forelle.c
new file mode 100644
index 0000000..e4d1fe2
--- /dev/null
+++ b/items/fishing/aquarium/forelle.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"forelle","regenbogenforelle"}));
+  SetProp(P_NAME,"Forelle" );
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_SHORT,"Eine Forelle" );
+  SetProp(P_LONG,"Eine grosse, bunte Regenbogenforelle.\n");
+  SetProp(P_VALUE,200);
+  SetProp(P_WEIGHT, 1000);
+}
diff --git a/items/fishing/aquarium/gfisch.c b/items/fishing/aquarium/gfisch.c
new file mode 100644
index 0000000..ebdd6a2
--- /dev/null
+++ b/items/fishing/aquarium/gfisch.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"goldfisch"}));
+  SetProp(P_NAME,"Goldfisch" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Goldfisch" );
+  SetProp(P_LONG,"Ein wunderschoen roter Goldfisch.\n");
+  SetProp(P_VALUE,100);
+  SetProp(P_WEIGHT, 80);
+}
diff --git a/items/fishing/aquarium/gorfe.c b/items/fishing/aquarium/gorfe.c
new file mode 100644
index 0000000..646c001
--- /dev/null
+++ b/items/fishing/aquarium/gorfe.c
@@ -0,0 +1,19 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"goldorfe","orfe"}));
+  SetProp(P_NAME,"Goldorfe" );
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_SHORT,"Ein Goldorfe" );
+  SetProp(P_LONG,
+  "Eine rote Goldorfe. Sie hat ein wenig Aehnlichkeit mit einem Goldfisch,\n"
+  +"ist jedoch wesentlich schlanker, schneller und gefraessiger.\n");
+  SetProp(P_VALUE, 150);
+  SetProp(P_WEIGHT, 80);
+}
diff --git a/items/fishing/aquarium/hai.c b/items/fishing/aquarium/hai.c
new file mode 100644
index 0000000..b81e546
--- /dev/null
+++ b/items/fishing/aquarium/hai.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"blauhai","hai"}));
+  SetProp(P_NAME,"Hai" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Hai" );
+  SetProp(P_LONG,"Ein grosser, bissiger Blauhai.\n");
+  SetProp(P_VALUE,800);
+  SetProp(P_WEIGHT, 1200);
+}
diff --git a/items/fishing/aquarium/hecht.c b/items/fishing/aquarium/hecht.c
new file mode 100644
index 0000000..b358ff9
--- /dev/null
+++ b/items/fishing/aquarium/hecht.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"hecht"}));
+  SetProp(P_NAME,"Hecht" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Hecht" );
+  SetProp(P_LONG,"Ein torpedofoermiger, gruengestreifter Hecht.\n");
+  SetProp(P_VALUE,90);
+  SetProp(P_WEIGHT, 700);
+}
diff --git a/items/fishing/aquarium/hering.c b/items/fishing/aquarium/hering.c
new file mode 100644
index 0000000..bbcb30b
--- /dev/null
+++ b/items/fishing/aquarium/hering.c
@@ -0,0 +1,18 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"hering"}));
+  SetProp(P_NAME,"Hering" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Hering" );
+  SetProp(P_LONG,
+  "Ein kleiner Hering.\n");
+  SetProp(P_VALUE,30);
+  SetProp(P_WEIGHT, 100);
+}
diff --git a/items/fishing/aquarium/karpfen.c b/items/fishing/aquarium/karpfen.c
new file mode 100644
index 0000000..212ece7
--- /dev/null
+++ b/items/fishing/aquarium/karpfen.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"karpfen"}));
+  SetProp(P_NAME,"Karpfen" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Karpfen" );
+  SetProp(P_LONG,"Ein grosser blaugrauer Karpfen.\n");
+  SetProp(P_VALUE,200);
+  SetProp(P_WEIGHT, 1000);
+}
diff --git a/items/fishing/aquarium/khai.c b/items/fishing/aquarium/khai.c
new file mode 100644
index 0000000..3fcfe19
--- /dev/null
+++ b/items/fishing/aquarium/khai.c
@@ -0,0 +1,19 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"katzenhai","hai","schmirgelpapier","\nkhaut"}));
+  SetProp(P_NAME,"Katzenhai" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Katzenhai" );
+  SetProp(P_LONG,"Ein kleiner Katzenhai, er hat eine aeusserst raue Haut.\n");
+  SetProp(P_VALUE,100);
+  SetProp(P_WEIGHT, 200);
+  SetEatMessage("Du haeutest den Katzenhai und isst ihn.");
+  SetCorpseObject(ANGELOBJ("khaut"));
+}
diff --git a/items/fishing/aquarium/kjau.c b/items/fishing/aquarium/kjau.c
new file mode 100644
index 0000000..18fb929
--- /dev/null
+++ b/items/fishing/aquarium/kjau.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"dorsch","kabeljau"}));
+  SetProp(P_NAME,"Kabeljau" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Kabeljau" );
+  SetProp(P_LONG,"Ein praechtiger, grosser Kabeljau.\n");
+  SetProp(P_VALUE,500);
+  SetProp(P_WEIGHT, 500);
+}
diff --git a/items/fishing/aquarium/lachs.c b/items/fishing/aquarium/lachs.c
new file mode 100644
index 0000000..769fa44
--- /dev/null
+++ b/items/fishing/aquarium/lachs.c
@@ -0,0 +1,16 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"lachs"}));
+  SetProp(P_NAME,"Lachs" );
+  SetProp(P_SHORT,"Ein Lachs" );
+  SetProp(P_LONG,"Ein schoener, grosser Lachs.\n");
+  SetProp(P_VALUE,60);
+  SetProp(P_WEIGHT, 400);
+}
diff --git a/items/fishing/aquarium/neun.c b/items/fishing/aquarium/neun.c
new file mode 100644
index 0000000..914448d
--- /dev/null
+++ b/items/fishing/aquarium/neun.c
@@ -0,0 +1,19 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"neunauge","bachneunauge"}));
+  SetProp(P_NAME,"Bachneunauge" );
+  SetProp(P_GENDER,NEUTER);
+  SetProp(P_SHORT,"Ein Neunauge" );
+  SetProp(P_LONG,
+  "Ein Bachneunauge mit einer kleinen Mundscheibe. Damit kann es sich\n"
+  +"an Steinen festhalten und in der Stroemung treiben.\n");
+  SetProp(P_VALUE,60);
+  SetProp(P_WEIGHT, 300);
+}
diff --git a/items/fishing/aquarium/piranha.c b/items/fishing/aquarium/piranha.c
new file mode 100644
index 0000000..5ad1ea2
--- /dev/null
+++ b/items/fishing/aquarium/piranha.c
@@ -0,0 +1,18 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"piranha"}));
+  SetProp(P_NAME,"Piranha" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Piranha" );
+  SetProp(P_LONG,"Ein boesartiger kleiner Piranha mit sehr scharfen "
+    "Zaehnen.\n");
+  SetProp(P_VALUE,40);
+  SetProp(P_WEIGHT, 100);
+}
diff --git a/items/fishing/aquarium/rochen.c b/items/fishing/aquarium/rochen.c
new file mode 100644
index 0000000..b338d28
--- /dev/null
+++ b/items/fishing/aquarium/rochen.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"rochen","nagelrochen"}));
+  SetProp(P_NAME,"Rochen" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Rochen" );
+  SetProp(P_LONG,"Ein kleiner Nagelrochen.\n");
+  SetProp(P_VALUE,60);
+  SetProp(P_WEIGHT, 300);
+}
diff --git a/items/fishing/aquarium/schlei.c b/items/fishing/aquarium/schlei.c
new file mode 100644
index 0000000..200f23b
--- /dev/null
+++ b/items/fishing/aquarium/schlei.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"schleie","schlei"}));
+  SetProp(P_NAME,"Schleie" );
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_SHORT,"Eine Schlei" );
+  SetProp(P_LONG,"Eine aeusserst schleimige Schleie.\n");
+  SetProp(P_VALUE,100);
+  SetProp(P_WEIGHT, 800);
+}
diff --git a/items/fishing/aquarium/scholle.c b/items/fishing/aquarium/scholle.c
new file mode 100644
index 0000000..71b6ab4
--- /dev/null
+++ b/items/fishing/aquarium/scholle.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"scholle"}));
+  SetProp(P_NAME,"Scholle" );
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_SHORT,"Eine Scholle" );
+  SetProp(P_LONG,"Ein platte, flache Scholle.\n");
+  SetProp(P_VALUE,60);
+  SetProp(P_WEIGHT, 500);
+}
diff --git a/items/fishing/aquarium/schwert.c b/items/fishing/aquarium/schwert.c
new file mode 100644
index 0000000..c3754c9
--- /dev/null
+++ b/items/fishing/aquarium/schwert.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"schwertfisch"}));
+  SetProp(P_NAME,"Schwertfisch" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Schwertfisch" );
+  SetProp(P_LONG,"Ein Schwertfisch mit einem sehr langen Schwert.\n");
+  SetProp(P_VALUE,700);
+  SetProp(P_WEIGHT, 1000);
+}
diff --git a/items/fishing/aquarium/seehase.c b/items/fishing/aquarium/seehase.c
new file mode 100644
index 0000000..7c173d0
--- /dev/null
+++ b/items/fishing/aquarium/seehase.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"seehase","hase"}));
+  SetProp(P_NAME,"Seehase" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Seehase" );
+  SetProp(P_LONG,"Ein klobiger, dunkelgraublauer Seehase.\n");
+  SetProp(P_VALUE,30);
+  SetProp(P_WEIGHT, 200);
+}
diff --git a/items/fishing/aquarium/seeteufel.c b/items/fishing/aquarium/seeteufel.c
new file mode 100644
index 0000000..37eb863
--- /dev/null
+++ b/items/fishing/aquarium/seeteufel.c
@@ -0,0 +1,18 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"seeteufel","teufel"}));
+  SetProp(P_NAME,"Seeteufel" );
+  SetProp(P_SHORT,"Ein Seeteufel" );
+  SetProp(P_LONG,
+  "Ein grimmiger Seeteufel mit vielen scharfen Zaehnen. Er hat eine kleine,\n"
+  +"auffaellige Angel oben an seinem Kopf, mit der er nach Beute koedert.\n");
+  SetProp(P_VALUE,200);
+  SetProp(P_WEIGHT, 800);
+}
diff --git a/items/fishing/aquarium/seewolf.c b/items/fishing/aquarium/seewolf.c
new file mode 100644
index 0000000..4c4a07c
--- /dev/null
+++ b/items/fishing/aquarium/seewolf.c
@@ -0,0 +1,18 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"seewolf","wolf"}));
+  SetProp(P_NAME,"Seewolf" );
+  SetProp(P_SHORT,"Ein Seewolf" );
+  SetProp(P_LONG,
+  "Ein haesslicher Seewolf mit scharfen, gebogenen Zaehnen. Er sieht\n"
+  +"richtig boesartig aus.\n");
+  SetProp(P_VALUE,100);
+  SetProp(P_WEIGHT, 600);
+}
diff --git a/items/fishing/aquarium/ssaib.c b/items/fishing/aquarium/ssaib.c
new file mode 100644
index 0000000..ac410ad
--- /dev/null
+++ b/items/fishing/aquarium/ssaib.c
@@ -0,0 +1,16 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"seesaibling","saibling"}));
+  SetProp(P_NAME,"Seesaibling" );
+  SetProp(P_SHORT,"Ein Seesaibling" );
+  SetProp(P_LONG,"Ein blaugrauer Seesaibling mit einem roten Bauch.\n");
+  SetProp(P_VALUE,80);
+  SetProp(P_WEIGHT, 400);
+}
diff --git a/items/fishing/aquarium/stichling.c b/items/fishing/aquarium/stichling.c
new file mode 100644
index 0000000..0301e0c
--- /dev/null
+++ b/items/fishing/aquarium/stichling.c
@@ -0,0 +1,16 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"stichling"}));
+  SetProp(P_NAME,"Stichling" );
+  SetProp(P_SHORT,"Ein Stichling" );
+  SetProp(P_LONG,"Ein suesser kleiner Stichling.\n");
+  SetProp(P_VALUE,6);
+  SetProp(P_WEIGHT, 30);
+}
diff --git a/items/fishing/aquarium/szunge.c b/items/fishing/aquarium/szunge.c
new file mode 100644
index 0000000..c135572
--- /dev/null
+++ b/items/fishing/aquarium/szunge.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"seezunge"}));
+  SetProp(P_NAME,"Seezunge" );
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_SHORT,"Eine Seezunge" );
+  SetProp(P_LONG,"Eine kleine braune Seezunge.\n");
+  SetProp(P_VALUE,60);
+  SetProp(P_WEIGHT, 300);
+}
diff --git a/items/fishing/aquarium/thun.c b/items/fishing/aquarium/thun.c
new file mode 100644
index 0000000..c12c928
--- /dev/null
+++ b/items/fishing/aquarium/thun.c
@@ -0,0 +1,17 @@
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_FISCH;
+
+protected void create() {
+   if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"thunfisch"}));
+  SetProp(P_NAME,"Thunfisch" );
+  SetProp(P_GENDER,MALE);
+  SetProp(P_SHORT,"Ein Thunfisch" );
+  SetProp(P_LONG,"Ein grosser Thunfisch.\n");
+  SetProp(P_VALUE,75);
+  SetProp(P_WEIGHT, 500);
+}
diff --git a/items/fishing/bootsrute.c b/items/fishing/bootsrute.c
new file mode 100644
index 0000000..3e7520e
--- /dev/null
+++ b/items/fishing/bootsrute.c
@@ -0,0 +1,23 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_shadow
+
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_ANGEL;
+
+protected void create() {
+  if (!clonep(this_object())) 
+    return;
+  ::create();
+
+  SetProp(P_NAME, "Bootsrute");
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_SHORT, "Eine kurze, starke Angel");
+  SetProp(P_LONG,"Eine Angel speziell fuer die Hochsee-Fischerei.\n"
+    "Sie ist von Fachleuten auf Hai-Festigkeit getestet!\n");
+  SetProp(P_TRANSPARENT,1);
+  SetProp(P_MAX_WEIGHT, 10000); //Hai-fest!!!!!!
+  SetProp(P_WATER,W_SHORT);     //siehe fishing.h
+}
diff --git a/items/fishing/fischwaage.c b/items/fishing/fischwaage.c
new file mode 100644
index 0000000..00e2dd4
--- /dev/null
+++ b/items/fishing/fischwaage.c
@@ -0,0 +1,179 @@
+// Aenderungshistorie:
+// 25.07.2015, Amaryllis: QueryFish implementiert, AddFishFood an/von Fraggle adaptiert
+
+inherit "/std/container";
+
+#include <moving.h>
+#include <fishing.h>
+#include <language.h>
+#include <properties.h>
+#include <defines.h>
+#include <money.h>
+
+#define TP this_player()
+#define BS(x) break_string(x, 78)
+
+private int good_price, bad_price, FAC, FOC, DIV;
+private int waagen_preis_faktor=4; // Um wieviel teurer als in Laeden wird 
+                           // der Fisch verkauft / Im Fisch wird durch 
+                           // 4 geteilt, das wird hier wieder ausgeglichen.
+
+private int get_price();
+private void RemoveFish();
+
+// Amaryllis, 25.07.2015: QueryFish "quick and dirty" reingehackt, 
+// damits funktioniert. Kann man sicher besser machen...
+private int QueryFish(object ob); // wird hier im Objekt in PreventInsert verwendet
+
+private int QueryFish(object ob) {
+  return (objectp(ob))&&(ob->id(FISCH_ID));
+}
+
+private int SetFactors(int good_value, int bad_value) {
+  FAC=good_value;
+  FOC=bad_value;
+  DIV=100;
+  if(!(FAC && FOC && DIV))
+    SetFactors(100,50);
+  return 1;
+}
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  SetFactors(100,50); //100% fuer frische, 50% fuer alte Fische
+  SetProp(P_NAME, "Fischwaage");
+  AddId(({"waage","fischwaage"}));
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_SHORT, "Eine Fischwaage");
+  SetProp(P_LONG,
+    "Eine sehr genaue Fischwaage mit einem grossen Zeiger. Du kannst\n"
+    "Deine Beute drauflegen, den Zeiger ablesen und, sofern Du mit\n"
+    "dem Preis einverstanden bist, die Verkaufsglocke laeuten.\n");
+  SetProp(P_DEST_PREPOSITION, "auf");
+  SetProp(P_SOURCE_PREPOSITION, "von");
+  SetProp(P_MATERIAL, MAT_MISC_METAL);
+  SetProp(P_TRANSPARENT, 1);
+  SetProp(P_NOGET, 1);
+  SetProp(P_MAX_WEIGHT, 10000);
+  SetProp(P_WEIGHT, 5000);
+  
+  AddDetail(({"zeiger"}), BS(
+    "Der Zeiger besteht aus Metall. Du kannst ihn ablesen, wenn Du "
+    "genau wissen willst, was Dein Fang wert ist."));
+  AddDetail(({"fang","beute"}), function string (string key) {
+    return BS(sizeof(all_inventory())?"Du schaust zufrieden auf die Beute, "
+      "die auf der Waage liegt. Welcher Preis sich dafuer wohl erzielen "
+      "laesst?":"Du muesstest erst einmal etwas drauflegen.");
+  });
+  AddReadDetail(({"zeige","zeiger ab"}), function string (string key) {
+    int current_weight = query_weight_contents();
+    get_price();
+    return
+      "Du schaust auf den grossen Zeiger der Waage, der erstaunlicherweise\n"
+      "nicht nur das Gewicht, sondern auch den Preis anzeigt.\n"
+      "Der Zeiger steht auf "+
+        (current_weight?current_weight+" Gramm.\n":"Null.\n")+
+      (good_price?good_price+" Goldmuenzen fuer fangfrischen Fisch.\n":"")+
+      (bad_price?bad_price+" Goldmuenzen fuer etwas aelteren Fisch.\n":"");
+  });
+  AddDetail(({"glocke"}), BS(
+    "An der Waage befindet sich eine kleine Glocke die Du laeuten kannst, "
+    "um den Inhalt der Waage zu verkaufen. Und warte damit nicht zu lange, "
+    "je aelter der Fisch, desto niedriger der Preis!"));
+
+  AddCmd("laeute&glocke|verkaufsglocke","cmd_laeute",
+    "Was willst Du denn laeuten?");
+}
+
+static int cmd_laeute(string str, mixed *param) {
+  tell_room(ME, TP->Name()+" laeutet eine kleine Glocke an der Fischwaage.",
+    ({TP}));
+  tell_object(TP, "Du laeutest die Glocke, um Deine Ware zu verkaufen.\n");
+  foreach(object ob: all_inventory(ME)) {
+    if ( !ob->id(FISCH_ID) ) {
+      tell_object(TP, 
+        "Auf der Waage liegt nicht nur Fisch!\n"
+        "Du solltest nicht versuchen, hier zu betruegen!\n");
+      tell_room(ME, TP->Name()+" macht sich hier gerade unbeliebt.\n",({TP}));
+      return 1;
+    }
+    if ( ob->QueryQuality()<=0 ) {
+      tell_object(TP, "Auf der Waage liegt alter, gammliger Fisch.\n"
+           "Den kannst Du nicht verkaufen!\n");
+      tell_room(ME, TP->Name()+" macht sich hier gerade unbeliebt.\n",({TP}));
+      return 1;
+    }
+  }
+  
+  int price=get_price();
+  if( !price ) {
+    tell_object(TP, 
+      "Du solltest Deine Beute erstmal auf die Waage legen!\n");
+    return 1;
+  }
+  tell_object(TP, "Du erhaeltst "+price+" Goldmuenzen fuer Deinen Fisch.\n");
+  tell_room(ME, TP->Name()+" erhaelt haufenweise Geld fuer "+
+    TP->QueryPossPronoun(MALE, WEN)+" Fisch.\n",({TP}));
+  //ZZ: AddMoney() gibt die werte von move() zurueck, d.h. !=0 geht nicht.
+  if (this_player()->AddMoney(price) != MOVE_OK) { 
+    object money=clone_object(GELD);
+    tell_object(TP, BS(
+      "Du kannst das Geld nicht mehr tragen. Es faellt klimpernd zu "
+      "Boden."));
+    money->SetProp(P_AMOUNT, price);
+    money->move(environment(ME), M_MOVE_ALL|M_PUT);
+  }
+  RemoveFish();
+  return 1;
+}
+
+private int get_price() {
+  int preis, preis_von_diesem;
+  int gewicht;
+
+  good_price     = 0;
+  bad_price      = 0;
+
+  foreach(object ob : all_inventory(ME)) {
+    preis_von_diesem = ob->QueryProp(P_VALUE);
+    if(ob->QueryQuality()>2)
+      good_price += preis_von_diesem*FAC/DIV;
+    else
+      bad_price +=preis_von_diesem *FOC/DIV;
+  }
+
+  good_price *= waagen_preis_faktor;
+  bad_price  *= waagen_preis_faktor;
+  return (good_price + bad_price);
+}
+
+int PreventInsert(object obj) {
+//  if( objectp(obj) && obj->QueryFish() )
+  if( objectp(obj) && QueryFish(obj) )
+    return ::PreventInsert(obj);
+  write("Das ist eine Fischwaage! Und keine Waage fuer "+obj->name(WEN)+
+    ".\n");
+  return 1;
+}
+
+#define ETO environment(this_object())
+
+private void RemoveFish() {
+  string pubname = ETO->GetPub();
+  object pub;
+
+  if ( stringp(pubname) )
+    pub = load_object(pubname);
+  else 
+    pub = ETO;
+
+  foreach(object ob : all_inventory(ME)) {
+    // pub->AddFishFood(ob);
+    // Amaryllis, 25.07.2015: Angepasst wegen AddFishFood von Fraggle
+    pub->AddFishFood(ob->name(WER,0),ob->QueryProp(P_WEIGHT),ob->QueryQuality());
+    ob->remove();
+  }
+}
+
+#undef ETO
diff --git a/items/fishing/graeten.c b/items/fishing/graeten.c
new file mode 100644
index 0000000..b960a04
--- /dev/null
+++ b/items/fishing/graeten.c
@@ -0,0 +1,22 @@
+inherit "/std/thing";
+
+#include <properties.h>
+
+protected void create() {
+  if ( !clonep(this_object()) ) {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+
+  SetProp(P_SHORT, "Ein paar Graeten");
+  SetProp(P_LONG, "Einige Graeten, die von einem Fisch uebriggeblieben "
+    "sind.\n");
+  SetProp(P_PLURAL, 1),
+  SetProp(P_NAME, "Graeten");
+  AddId("graeten");
+  SetProp(P_WEIGHT, 3);
+  SetProp(P_VALUE, 1);
+  SetProp(P_NOBUY, 1);
+  SetProp(P_GENDER, FEMALE);
+}
diff --git a/items/fishing/haken.c b/items/fishing/haken.c
new file mode 100644
index 0000000..3152b67
--- /dev/null
+++ b/items/fishing/haken.c
@@ -0,0 +1,17 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_shadow
+
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+#include <items/flasche.h>
+
+inherit STD_HAKEN;
+
+protected void create() {
+  ::create();
+  SetProp(P_NAME, "Haken");
+  SetProp(P_SHORT, "Ein Haken");
+  SetProp(P_LONG, "Ein Angelhaken aus Metall.\n");
+  SetProp(P_LONG_EMPTY, "Du kannst etwas damit aufspiessen!\n");
+}
diff --git a/items/fishing/khaut.c b/items/fishing/khaut.c
new file mode 100644
index 0000000..d1db082
--- /dev/null
+++ b/items/fishing/khaut.c
@@ -0,0 +1,18 @@
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+
+protected void create() {
+  if (!clonep(this_object())) return;
+  ::create();
+  AddId(({"haut","katzenhaihaut","schmirgelpapier","\nkhaut"}));
+  SetProp(P_NAME, "Haut");
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_SHORT, "Etwas Haut eines Katzenhaies" );
+  SetProp(P_LONG, "Ein Stueck der rauen Haut eines Katzenhaies.\n");
+  SetProp(P_MATERIAL,MAT_SKIN);
+  SetProp(P_VALUE,10);
+  SetProp(P_NOBUY,1);
+  SetProp(P_WEIGHT,2 );
+}
diff --git a/items/fishing/made.c b/items/fishing/made.c
new file mode 100644
index 0000000..b7dd0b3
--- /dev/null
+++ b/items/fishing/made.c
@@ -0,0 +1,19 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_shadow
+
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_KOEDER;
+
+protected void create() {
+  ::create();
+  AddId(({WURM_ID,"made"}));
+  SetProp(P_NAME, "Made");
+  SetProp(P_GENDER , FEMALE);
+  SetProp(P_WATER, W_SWEET);
+  SetProp(P_FISH, 30);
+  SetProp(P_SHORT, "Eine Made");
+  SetProp(P_LONG, "Eine kleine eklige Made.\n");
+}
diff --git a/items/fishing/mehlwurm.c b/items/fishing/mehlwurm.c
new file mode 100644
index 0000000..89bd751
--- /dev/null
+++ b/items/fishing/mehlwurm.c
@@ -0,0 +1,18 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_shadow
+
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_KOEDER;
+
+protected void create() {
+  ::create();
+  AddId(({"wurm","mehlwurm"}));
+  SetProp(P_NAME, "Mehlwurm");
+  SetProp(P_FISH, 10);
+  SetProp(P_WATER, W_SWEET);
+  SetProp(P_SHORT, "Ein Mehlwurm");
+  SetProp(P_LONG, "Ein kleiner Mehlwurm.\n");
+}
diff --git a/items/fishing/rute.c b/items/fishing/rute.c
new file mode 100644
index 0000000..496c571
--- /dev/null
+++ b/items/fishing/rute.c
@@ -0,0 +1,26 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_shadow
+
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_ANGEL;
+
+protected void create() {
+  if (!clonep(this_object())) 
+    return;
+  ::create();
+
+  SetProp(P_NAME, "Angel");
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_SHORT, "Eine Angel");
+  SetProp(P_LONG, "Eine normale Angel.\n");
+  SetProp(P_TRANSPARENT, 1);
+  // 2014-Mai-10, Arathorn. von 1000 auf 1050 erhoeht, damit Haken+Koeder
+  // aufgrund ihres Gewichts von wenigen Gramm nicht mehr verhindern, dass
+  // einer der standardmaessig 1000 g schweren Fische gefangen werden kann.
+  SetProp(P_MAX_WEIGHT, 1050);
+  SetProp(P_WATER,W_SHORT);    //siehe fishing.h
+}
+
diff --git a/items/fishing/seasnail.c b/items/fishing/seasnail.c
new file mode 100644
index 0000000..2b6ded0
--- /dev/null
+++ b/items/fishing/seasnail.c
@@ -0,0 +1,48 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_shadow
+
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_KOEDER;
+
+private int snailtype = random(8);
+
+private < <string|string*>*>* desc = ({
+//private mixed desc = ({ 
+    ({
+    "Eine purpurne Purpurschnecke.\n",
+    "Eine rosa-gestreifte Kreiselschnecke.\n",
+    "Eine suesse kleine Kaurischnecke.\n",
+    "Eine kleine rote Buckelschnecke.\n",
+    "Eine kleine gestrichelte Turbanschnecke.\n",
+    "Eine kleine gestreifte Lochnapfschnecke.\n",
+    "Ein gruenes Seeohr.\n",
+    "Eine suesse kleine Seeschnecke.\n" }),
+    ({
+    "purpurschnecke",
+    "kreiselschnecke",
+    "kaurischnecke",
+    "buckelschnecke",
+    "turbanschnecke",
+    "napfschnecke",
+    ({"ohr","seeohr"}),
+    "seeschnecke"})
+  });
+
+protected void create() {
+  ::create();
+  AddId(({WURM_ID, "schnecke", "\nfa_snails", "seeschnecke"}));
+  AddId(desc[1][snailtype]);
+  SetProp(P_NAME, "Schnecke");
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_ARTICLE, 1);
+  SetProp(P_SHORT, "Ein Seeschnecke");
+  SetProp(P_LONG, desc[0][snailtype]);
+  SetProp(P_MATERIAL, MAT_MISC_LIVING);
+  SetProp(P_FISH, snailtype*random(10));
+  SetProp(P_WATER, W_SALT);
+  SetProp(P_VALUE,1);
+  SetProp(P_WEIGHT, 5);
+}
diff --git a/items/fishing/shaken.c b/items/fishing/shaken.c
new file mode 100644
index 0000000..4bec621
--- /dev/null
+++ b/items/fishing/shaken.c
@@ -0,0 +1,62 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_shadow, no_inherit
+
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+#include <items/flasche.h>
+
+inherit STD_HAKEN;
+
+protected void create() {
+  ::create();
+  AddId(({"spezialhaken"}));
+  SetProp(P_NAME, "Haken");
+  SetProp(P_ARTICLE, 1);
+  SetProp(P_WATER, W_UNIVERSAL);
+  SetProp(P_FISH, -20+random(60)); // -20 ... +39
+  SetProp(P_SHORT, "Ein Spezialhaken");
+  SetProp(P_LONG, break_string(
+    "Ein Angelhaken aus Metall. Es ist eine Spezialanfertigung aus Sam "
+    "Harkwinds Anglerladen, die einen kuenstlichen Wurm enthaelt.\n",78));
+  SetProp(P_INFO, break_string(
+    "Der kuenstliche Koeder soll in jedem Gewaesser recht gut "
+    "funktionieren, hat man Dir im Laden versprochen.",78));
+  
+  AddDetail(({"koeder","wurm"}), function string (string key) {
+    string desc = "Der Koeder ist handwerklich %s gelungen. Du glaubst, "
+      "dass er auf Fische %s wirken duerfte.";
+    string craft, effect;
+    switch(QueryProp(P_FISH)) {
+      case -20..-6:
+        craft = "ziemlich mies";
+        effect = "eher abstossend"; 
+        break;
+      case -5..9:
+        craft = "bestenfalls durchschnittlich";
+        effect = "genauso durchschnittlich";
+        break;
+      case 10..24:
+        craft = "wirklich solide";
+        effect = "recht appetitlich";
+        break;
+      case 25..39:
+        craft = "ueberragend";
+        effect = "unwiderstehlich";
+        break;
+      default: 
+        craft = "so la-la";
+        effect = "komisch";
+        break;
+    }
+    return break_string(sprintf(desc,craft,effect),78);
+  });
+
+  SetProp(P_LONG_EMPTY, "");
+  SetProp(P_VALUE, 20);
+  SetProp(P_WEIGHT, 10);
+}
+
+int QueryKoeder() { 
+  return 1; 
+}
diff --git a/items/fishing/strandrute.c b/items/fishing/strandrute.c
new file mode 100644
index 0000000..b7e8593
--- /dev/null
+++ b/items/fishing/strandrute.c
@@ -0,0 +1,25 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_shadow
+
+#include <language.h>
+#include <properties.h>
+#include <fishing.h>
+
+inherit STD_ANGEL;
+
+protected void create() {
+  if (!clonep(this_object()))
+    return;
+  ::create();
+
+  SetProp(P_NAME, "Strandrute");
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_SHORT, "Eine sehr lange Angel");
+  SetProp(P_LONG, "Eine Angel speziell fuer die Strand-Fischerei.\n");
+  SetProp(P_TRANSPARENT, 1);
+  // 2014-Mai-10, Arathorn. von 1000 auf 1050 erhoeht, damit Haken+Koeder
+  // aufgrund ihres Gewichts von wenigen Gramm nicht mehr verhindern, dass
+  // einer der standardmaessig 1000 g schweren Fische gefangen werden kann.
+  SetProp(P_MAX_WEIGHT, 1050);
+  SetProp(P_WATER, W_LONG);
+}
diff --git a/items/fishing/wurm.c b/items/fishing/wurm.c
new file mode 100644
index 0000000..2b1ef62
--- /dev/null
+++ b/items/fishing/wurm.c
@@ -0,0 +1,6 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_shadow
+
+#include <fishing.h>
+
+inherit STD_KOEDER;
diff --git a/items/flasche.c b/items/flasche.c
new file mode 100644
index 0000000..76026c9
--- /dev/null
+++ b/items/flasche.c
@@ -0,0 +1,3 @@
+#pragma strong_types,rtt_checks
+inherit "/std/items/flasche";
+
diff --git a/items/furz.c b/items/furz.c
new file mode 100644
index 0000000..b46b5a8
--- /dev/null
+++ b/items/furz.c
@@ -0,0 +1,108 @@
+/*
+Letzte Aenderung: 29.01.01 von Tilly (Zusatz-Abfrage in heart_beat() )
+                  25.08.01 von Tiamak (Tillys Abfragen debuggt ;^)
+*/
+#pragma strong_types,rtt_checks
+
+inherit "/std/thing";
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+
+int    alter;
+string furzerWER,furzerWESSEN;
+
+void create()
+{
+  if(!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, 0);
+  SetProp(P_LONG, 0);
+  SetProp(P_INVIS,1);
+  SetProp(P_NAME, "Etwas");
+  SetProp(P_GENDER, 1);
+  SetProp(P_ARTICLE,0);
+  SetProp(P_NOGET,"Haeh?\n");
+  AddId("_furz_");
+  if (this_player())
+    AddId("Furz_"+getuid(this_player()));
+  alter = 0;
+  set_heart_beat(1);
+}
+
+void heart_beat()
+{
+  int i;
+  if(!environment())
+  {
+    set_heart_beat(0);
+    destruct(this_object());
+    return;
+  }
+  if(living(environment()))
+  {
+    set_heart_beat(0);
+    destruct(this_object());
+    return;
+  }
+  if (!alter)
+    tell_room(environment(), "Fuerchterlicher Gestank erfuellt den Raum.\n");
+  alter++;
+  if (!random(10))
+  {
+    i = random (5);
+    if (i==0)
+      tell_room(environment(), furzerWESSEN+" Furz stinkt wirklich grauenerregend.\n");
+    if (i==1)
+      tell_room(environment(), "Du haeltst Dir die Nase zu.\n");
+    if (i==2)
+      tell_room(environment(), "Widerliche Duenste erfuellen den Raum.\n");
+    if (i==3)
+      tell_room(environment(), "Der Furz riecht, als wuerde " + furzerWER +" innerlich verfaulen.\n");
+    if (i==4)
+      tell_room(environment(), "Du musst Dich beinah uebergeben, als " + furzerWESSEN + " Furz in Deine Nase kriecht.\n"); 
+  }
+  if (alter==45) {
+      tell_room(environment(), furzerWESSEN + " widerlicher Furz hat sich endlich verzogen.\n");
+      destruct(this_object());
+    }
+}
+
+
+void set_alter(int a)
+{ alter=a; }
+
+void set_furzer (object wer)
+{
+  if (objectp(wer))
+    {
+      furzerWER=wer->name(WER);
+      furzerWESSEN=wer->name(WESSEN);
+      AddId("Furz_"+getuid(wer));
+    }
+}
+
+varargs int move(mixed dest, int methods, string direction, string textout,string textin)
+{
+  int erg;
+  object otherfurz;
+
+  erg=::move(dest,methods,direction,textout,textin);
+  
+  // Vanion, 24.09.02: Kann sein, dass das move nich klappt,
+  //                   dann sollte das Objekt weg.
+  if (erg != MOVE_OK || !environment(this_object())) 
+  {
+    if (!remove()) destruct(this_object());
+    return 0;
+  }
+  if (this_player())
+    {
+      if (otherfurz=present("Furz_"+getuid(this_player())+" 2",environment()))
+	{
+	  otherfurz->set_alter(0);
+	  return remove();
+	}
+    }
+  return erg;
+}
diff --git a/items/kraeuter/eiszahn.c b/items/kraeuter/eiszahn.c
new file mode 100644
index 0000000..8f023de
--- /dev/null
+++ b/items/kraeuter/eiszahn.c
@@ -0,0 +1,36 @@
+// (c) 2001 by Padreic (Padreic@mg.mud.de)
+
+#pragma strong_types,rtt_checks
+
+#include <properties.h>
+#include <items/kraeuter/kraeuter.h>
+#include <items/kraeuter/kraeuterliste.h>
+
+inherit STDPLANT;
+void create()
+{ 
+  ::create();
+  customizeMe(EISZAHN);
+  SetProp(P_NAME,     "Eiszahn");
+  SetProp(P_NAME_ADJ, "kalt");
+  SetProp(P_GENDER,   MALE);
+  SetProp(P_LONG,
+    "Der Eiszahn ist ein ausgesprochen merkwuerdiges Kraut.\n"
+   +"Typisch sind die wie kleine Zaehne angeordneten Blaetter, die\n"
+   +"am Hauptstengel entlang wachsen und sich stets zum Himmel recken.\n"
+   +"Ausserdem strahlt das Kraut spuerbar Kaelte aus.\n");
+  SetProp(PLANT_ROOMDETAIL,
+     "Der Eiszahn fuehlt sich auf dem Eisboden sichtlich wohl.\n");
+  SetProp(P_SHORT,    "Ein Eiszahn");
+  AddId(({ "zahn", "eiszahn" }));
+  AddDetail("kraut",
+     "Der Eiszahn ist da sehr eigen: Er ist kein Pilz, er ist ein Kraut.\n");
+  AddDetail("kaelte", "Huah, ganz schoen kalt.\n");
+  AddDetail("blaetter",
+    "Die Blaetter sehen aus wie kleine Zaehne. Sie sind ganz weiss und\n"
+   +"ordentlich aufgereiht.\n");
+  AddDetail("hauptstengel",
+     "Eigentlich hat das Kraut nur einen Staengel, der leicht zur Seite haengt\n"
+    +"weil die 'Zaehne' ausreichend Gewicht haben.\n");
+  AddDetail("zaehne", "So nennt man die Blaetter des Eiszahns.\n");
+}
diff --git a/items/kraeuter/glockenblume.c b/items/kraeuter/glockenblume.c
new file mode 100644
index 0000000..163ad63
--- /dev/null
+++ b/items/kraeuter/glockenblume.c
@@ -0,0 +1,49 @@
+// (c) 2001 by Padreic (Padreic@mg.mud.de)
+// beschreibung magdalena@morgengrauen.de - 8.12.2003
+
+#pragma strong_types,rtt_checks
+
+#include <properties.h>
+#include <items/kraeuter/kraeuter.h>
+#include <items/kraeuter/kraeuterliste.h>
+
+inherit STDPLANT;
+
+#define BS(x) break_string(x, 78)
+
+
+void create()
+{
+  ::create();
+  customizeMe(GLOCKENBLUME);
+  SetProp(P_NAME,     "Glockenblume");
+  SetProp(P_NAME_ADJ, "schoen");
+  SetProp(P_GENDER,   FEMALE);
+  SetProp(P_LONG, BS(
+    "Die Bluetensterne in blau-violett sind ein toller Blickfang, wie "
+   +"sie am langen Stiel im Wind hin- und hernicken. Die "
+   +"glockenfoermige Blumenkrone mit ihren fuenf breiten, nicht bis zur "
+   +"Mitte reichenden Zipfeln erfreut Dich mit ihrer Schoenheit."));
+  SetProp(PLANT_ROOMDETAIL, 
+    "Eine anmutige, schoene Glockenblume nickt dir auffordernd zu. Ob man sie "
+   +"pfluecken kann?\n");
+  SetProp(P_SHORT,    "Eine Glockenblume");
+  AddId(({ "blume", "glockenblume" }));
+  
+  AddDetail(({"blickfang","schoenheit"}),BS(
+     "Diese Glockenblume besticht durch ihre Schoenheit. Ihre Farben sind " 
+    +"sind ein wahrer Blickfang.\n"));
+  AddDetail(({"farben","blueten"}),BS(
+     "Die Blueten der Glockenblume sind blau-violett.\n"));
+  AddDetail("blumenkrone",BS(
+     "Die Blume ist glockenfoermig. Daher hat die Pflanze ihren Namen.\n"));
+  AddDetail("pflanze",BS(
+     "Diese Pflanze ist eindeutig eine Glockenblume.\n"));
+  AddDetail(({"bluetensterne","sterne","zipfel","zipfeln"}),BS(
+     "Bemerkenswert an der Pflanze sind die Bluetensterne. Die blau-violetten "
+    +"Zipfel bilden die Bluete der Pflanze, was sehr auffaellig ist. Diese "
+    +"Bluetensterne gaben der Blume ihren Namen.\n"));
+
+}
+
+
diff --git a/items/kraeuter/kessel.c b/items/kraeuter/kessel.c
new file mode 100644
index 0000000..909eead
--- /dev/null
+++ b/items/kraeuter/kessel.c
@@ -0,0 +1,335 @@
+// (c) 2003 by Thomas Winheller (padreic@mg.mud.de)
+// Bei diesem File handelt es sich um einen universellen
+// Kessel zum Brauen von Traenken. Damit er sich in den
+// Raum entsprechend einpassen kann, ist er invis.
+
+// Ein Raum, in dem Traenke und Gift gebraut werden koennen
+// soll, braucht nichts weiter machen als:
+// -  eine Moeglichkeit zu implementieren Wasser in den Kessel
+//    zu fuellen. Hierzu muss dann mittels AddWater(3000)
+//    insgesamt 3l Wasser eingefuellt werden. AddWater gibt die
+//    Menge des tatsaechlich noch eingefuelltn Wassers zurueck.
+// Anmerkung: bisher enthaelt /obj/flasche noch keine Moeglichkeit
+//            Fluessigkeiten von einem Fluessigkeitencontainer in
+//            einen anderen zu schuetten, aber vielleicht aendert
+//            das ja irgendwann mal wer - der Kessel kann dann
+//            durch alle diese Flaschen befuellt werden!
+#pragma strong_types,rtt_checks
+
+inherit "/std/container";
+
+#include <defines.h>
+#include <properties.h>
+#include <moving.h>
+#include <fishing.h>
+#include <items/kraeuter/kraeuter.h>
+#include <items/flasche.h>
+
+#ifndef BS
+#  define BS(x)             break_string(x, 78)
+#endif
+
+private int wassermenge;
+
+// Aktueller Nutzer des Kessels in Form der Spieler-UID 
+private string current_user;
+
+// Zeitpunkt, wann die Sperre endet. 
+private int timeout;
+
+protected void create()
+{
+  ::create();
+  SetProp(P_SHORT, "Ein schwerer Kessel");
+  // \n werden in long() eingefuegt
+  SetProp(P_LONG,
+    "Ein kleiner, aber sehr schwerer Kessel, in dem die verschiedensten "
+    "Traenke gebraut werden koennen."); 
+  SetProp(P_NAME, "Kessel");
+  SetProp(P_NAME_ADJ, "klein");
+  SetProp(P_MATERIAL, MAT_BRONCE);
+  SetProp(P_NOGET, "Der Kessel ist zu schwer, um ihn einfach mitnehmen "
+     "zu koennen.\n");
+  SetProp(P_MAX_WEIGHT, 100000); // ein _wirklich_ grosser Kessel ;o)
+  SetProp(P_WEIGHT, 50000);
+  SetProp(P_VALUE, 25000);
+  SetProp(P_MAX_OBJECTS, 9); // max. 8 Kraeuter + Wasser
+  SetProp(P_GENDER, MALE);
+  SetProp(P_LIQUID, 3000); // in den Kessel passen 3l :o)
+  AddId(({"kessel", KESSELID}));
+  AddAdjective(({"klein", "kleiner"}));
+  AddCmd("leer|leere&@ID&aus", "cmd_leeren",
+    "Was moechtest Du leeren?|Moechtest Du den Kessel etwa ausleeren?");
+  AddCmd(({"brau", "braue", "koch", "koche"}), "cmd_brauen");
+  AddCmd("fuell|fuelle&trank|heiltrank|ergebnis|kesselinhalt|inhalt&in&"
+    "@PRESENT", "cmd_fuellen",
+    "Was willst Du fuellen?|Willst Du etwas in etwas fuellen?|"
+    "Worein willst Du den Kesselinhalt fuellen?");
+  AddCmd(({"tauch", "tauche"}), "cmd_tauchen");
+}
+
+#define TRANKFERTIG "_lib_p_krauttrankfertig"
+
+private int check_busy(int useronly)
+{
+  if (useronly)
+    return current_user && current_user != getuid(PL);
+  // Timeout darf noch nicht abgelaufen sein. Wird beim Entleeren geprueft.
+  return current_user && timeout > time() && current_user != getuid(PL);
+}
+
+private void clear_kessel()
+{
+  all_inventory()->remove(1);
+  // gespeicherte Daten nullen
+  wassermenge = current_user = timeout = 0;
+  SetProp(P_WATER, 0);
+  SetProp(TRANKFERTIG, 0);
+}
+
+int AddWater(int menge)
+{
+  // Wenn ein User eingetragen ist, dieser nicht PL ist und die Sperre
+  // auch noch nicht abgelaufen ist, dann wird das Einfuellen von Wasser
+  // verhindert. Nutzer der Funktion muessen die Rueckgabewerte pruefen
+  // und entsprechende Meldungen ausgeben.
+  if (check_busy(1))
+    return -1;
+
+  int old = wassermenge;
+  wassermenge = min(wassermenge+menge, QueryProp(P_LIQUID));
+
+  if (wassermenge<=0)
+  {
+    wassermenge=0; // wasser entnahme
+    SetProp(P_WATER, 0);
+  }
+  else
+     SetProp(P_WATER, W_DEAD);
+  return wassermenge-old;
+}
+
+static int cmd_leeren(string str)
+{
+  if (!QueryProp(P_WATER) && !sizeof(all_inventory()))
+  {
+    write(BS("Im Kessel ist bisher noch nichts enthalten, was Du ausleeren "
+             "koenntest."));
+  }
+  // Es gibt einen aktuellen User, dieser ist nicht PL, und der Timeout
+  // ist auch noch nicht abgelaufen => Finger weg.
+  else if (check_busy(0))
+  {
+    tell_object(PL, BS(
+      "Der Inhalt des Kessels wurde erst kuerzlich von jemand anderem dort "
+      "hineingefuellt. Du solltest Dich nicht daran zu schaffen machen."));
+  }
+  write("Vorsichtig nimmst Du den Kessel und schuettest seinen Inhalt in den Abfluss.\n");
+  say(BS(PL->Name(WER)+" nimmt den Kessel und schuettet den Inhalt in den Abfluss."));
+  clear_kessel();
+  return 1;
+}
+
+/*#include "/d/erzmagier/boing/balance/balance.h"
+#include <wizlevels.h>
+#define TESTER (BTEAM+({"elendil","saray", "huraxprax"}))*/
+static int cmd_brauen(string str)
+{
+  /*if (!IS_ARCH(this_interactive())
+      && !member(TESTER, PL->query_real_name()))
+    return 0;*/
+  
+  notify_fail("WAS moechtest Du brauen?\n");
+  if (!str) return 0;
+/*  if (str=="zaubertrank") {
+     write("Ohne passendes Rezept duerfte dies schwierig werden...\n");
+     return 1;
+  }*/
+  if (member(({"trank","zaubertrank","kraeutertrank","tee","kraeutertee"}),
+             str)<0) 
+    return 0;
+
+  if (check_busy(1)) {
+    tell_object(PL, BS(
+      "An dem Trank in dem Kessel arbeitet gerade noch "+
+      capitalize(current_user)+". Du kannst hoechstens in ein paar "
+      "Minuten versuchen, den Inhalt des Kessels auszuleeren. Selbst "
+      "Hand anzulegen, wuerde man Dir sicherlich uebelnehmen."));
+  }
+  else if (!QueryProp(P_WATER)) {
+    write("Vielleicht solltest Du zunaechst noch Wasser in den Kessel "
+      "fuellen...\n");
+  }
+  else if (wassermenge<QueryProp(P_LIQUID)) {
+    write("Vielleicht solltest Du zunaechst noch etwas mehr Wasser in "
+      "den Kessel\nfuellen...\n");
+  }
+  else if (sizeof(all_inventory())<3) {
+    write("Derzeit ist Dein Trank noch ein wenig waessrig.\n"
+         +"Mindestens drei Zutaten muessen in einen Trank schon hinein.\n");
+  }
+  else {
+    write(BS("Vorsichtig laesst Du den Kessel etwas naeher zur Feuerstelle "
+      "runter und wartest unter gelegentlichem Ruehren, bis er kocht. "
+      "Dein Trank sollte nun fertig sein und Du kannst ihn nun abfuellen. "
+      "Was er wohl fuer eine Wirkung haben wird?"));
+    say(BS(PL->Name()+" laesst den Kessel zur Feuerstelle herunter und "
+      "ruehrt langsam darin herum. Nach einer Weile kocht die Fluessigkeit "
+      "darin, und "+PL->Name(WER)+" stellt das Ruehren wieder ein."));
+    SetProp(TRANKFERTIG, 1);
+  }
+  return 1;
+}
+
+static int cmd_fuellen(string str,mixed* params)
+{
+/*  if (!IS_ARCH(this_interactive())
+      && !member(TESTER, this_player()->query_real_name()))
+    return 0;*/
+ 
+  if ( !QueryProp(TRANKFERTIG) ) 
+  {
+    write("Im Kessel befindet sich aber gar kein Trank.\n");
+  }
+  // Abfuellen ist nur fuer den Spieler moeglich, der die Kraeuter
+  // reingetan hat.
+  else if (check_busy(1))
+  {
+    tell_object(PL, BS("Diesen Trank hast Du doch gar nicht selbst "
+      "gebraut! Du solltest noch eine Weile warten, ob "+
+      capitalize(current_user)+" ihn nicht doch noch selbst abfuellen "
+      "will. Wenn nicht, koenntest Du nur noch versuchen, den Kessel "
+      "auszuleeren - jedenfalls es erscheint Dir viel zu riskant, das "
+      "Gebraeu selbst zu trinken, das "+capitalize(current_user)+
+      " da zusammengeruehrt hat."));
+  }
+  else if (BLUE_NAME(params[2])==TRANKITEM)
+  {
+    int ret = params[2]->Fill(all_inventory());
+    switch( ret ) {
+      case -3:
+      case -1:
+        write(BS("Fehler beim Fuellen der Phiole. Bitte sag einem Magier "
+          "Bescheid und nenne den Fehlercode "+ret+"."));
+        break;
+      case -2:
+        write(BS("Die Phiole ist bereits gefuellt."));
+        break;
+      default:
+        write(BS("Du nimmst den Kessel und fuellst seinen konzentrierten "
+          "Inhalt in Deine Glasflasche. Hoffentlich ist Dir hier ein "
+          "toller Trank gelungen."));
+        say(BS(PL->Name(WER)+" nimmt den Kessel und fuellt dessen "
+          "konzentrierten Inhalt in eine kleine Glasflasche. Was "+
+          PL->QueryPronoun(WER)+" da wohl gebraut hat?"));
+        clear_kessel();
+        break;
+    }
+  }
+  else {
+    write("Darein kannst Du den Trank leider nicht fuellen.\n");
+  }
+  return 1;
+}
+
+varargs string long(int mode)
+{
+  string inv_desc = make_invlist(PL, all_inventory(ME));
+  if (inv_desc=="") {
+    if (QueryProp(P_WATER))
+      return BS(Query(P_LONG)+" Derzeit ist er lediglich mit Wasser "
+        "gefuellt.");
+    return BS(Query(P_LONG)+" Er ist im Moment leer.");
+  }
+  if (QueryProp(P_WATER))
+    return BS(Query(P_LONG)+" Er ist mit Wasser gefuellt, und Du siehst "
+              +"folgende Kraeuter in ihm schwimmen:")+inv_desc;
+  return BS(Query(P_LONG))+"Er enthaelt:\n"+inv_desc;
+}
+
+static int _query_invis()
+{
+  if (member(({"nimm", "nehm", "nehme", "leg", "lege",
+               "steck", "stecke"}), query_verb())!=-1) return 0;
+  return Query(P_INVIS, F_VALUE);
+}
+/*
+varargs string name(int casus, int demon)
+{
+  SetProp(P_INVIS, 0);
+  string ret=::name(casus, demon);
+  SetProp(P_INVIS, 1);
+  return ret;
+}
+*/
+varargs int PreventInsert(object ob)
+{
+  int plantid = ob->QueryPlantId(); 
+  int *inv = all_inventory(ME)->QueryPlantId();
+  
+  // es koennen natuerlich nur echte Kraeuter in den Kessel gelegt werden
+  if ( plantid<=0 || !IS_PLANT(ob) )
+    return 1;
+  
+  if (QueryProp(TRANKFERTIG))
+  {
+    tell_object(PL, break_string(
+      "Im Kessel ist ein fertiger Trank. Wenn Du etwas neues machen "
+      "willst, leere den Kessel oder fuelle den Trank ab."));
+    return 1;
+  }
+  // Reintun darf nur der aktuelle User, es sei denn, ein anderer Spieler 
+  // faengt frisch an, wenn der Kessel gerade unbenutzt ist.
+  else if ( check_busy(1) )
+  {
+    tell_object(PL, BS("Dieser Kessel wurde bis gerade eben noch von "+
+      capitalize(current_user)+" genutzt. Warte besser, bis der Kessel "
+      "wieder frei ist."));
+    return 1;
+  }
+  else if ( !SECURE("krautmaster")->CanUseIngredient(PL, plantid) ) 
+  {
+    // mit Kraeutern ueber die man nichts weiss, kann man nicht brauen
+    tell_object(PL, BS("Ueber die Wirkungsweise von "+ob->name(WEM)+
+      " weisst Du bisher leider wirklich ueberhaupt nichts."));
+    return 1;
+  }
+  else if ( sizeof(inv) >= 8 ) 
+  {
+    tell_object(PL, BS("Mehr als acht Zutaten sollte man nie zu einem "
+      "Trank vereinigen, und es sind schon acht im Kessel."));
+    return 1;
+  }
+  else if (member(inv, plantid)>-1) 
+  {
+    tell_object(PL, BS("Im Kessel befindet sich bereits "+ob->name(WER)+
+      ". Du kannst kein Kraut mehr als einmal verwenden."));
+    return 1;
+  }
+  current_user = getuid(PL);
+  timeout = time()+120;
+  return ::PreventInsert(ob);
+}
+
+int PreventLeave(object ob, mixed dest)
+{
+  if (QueryProp(P_WATER)) {
+    tell_object(PL, BS("Es befindet sich bereits Wasser im Kessel, die "
+      "einzelnen Zutaten kannst Du nun leider nicht mehr einzeln "
+      "rausholen, ohne den ganzen Kessel auszuleeren."));
+      return 1;
+  }
+  // Rausnehmen ist nur fuer den aktuellen User moeglich. Alle anderen
+  // koennen auch nach Ablauf der Zeitsperre nur ausleeren.
+  else if ( check_busy(1) ) {
+    tell_object(PL, BS("Du hast "+ob->name(WEN,1)+" nicht dort hineingetan, "
+      "also kannst Du "+ob->QueryPronoun(WEN)+" auch nicht herausnehmen. "
+      "Zumindest vorerst nicht. Sollte "+capitalize(current_user)+
+      "nicht innerhalb der naechsten paar Minuten weiterbrauen, kannst "
+      "Du den Kesselinhalt zumindest mit einem guten Schluck Wasser "
+      "rausspuelen."));
+    return 1;
+  }
+  return ::PreventLeave(ob, dest);
+}
+
diff --git a/items/kraeuter/kicherpilz.c b/items/kraeuter/kicherpilz.c
new file mode 100644
index 0000000..a4b10a3
--- /dev/null
+++ b/items/kraeuter/kicherpilz.c
@@ -0,0 +1,51 @@
+// (c) 2001 by Padreic (Padreic@mg.mud.de)
+// Beschrieben von Magdalena :o) 08.08.03
+
+#pragma strong_types,rtt_checks
+
+#include <properties.h>
+#include <items/kraeuter/kraeuter.h>
+#include <items/kraeuter/kraeuterliste.h>
+
+inherit STDPLANT;
+
+#define BS(x) break_string(x, 78)
+
+void create()
+{
+  ::create();
+  customizeMe(KICHERPILZ);
+  SetProp(P_NAME,     "Kicherpilz");
+  SetProp(P_NAME_ADJ, "lustig");
+  SetProp(P_GENDER,   MALE);
+  SetProp(P_LONG,     
+    "Er hat ein gruenes Kaeppchen auf und ist fuer einen Pilz relativ gross. Ab und\n"
+   +"an zuckt er und kichert albern. Daher hat er wohl auch seinen Namen.\n");
+  SetProp(PLANT_ROOMDETAIL, 
+    "Ein besonders praechtiges Exemplar eines Kicherpilzes winkt dir\n"
+   +"aufdringlich zu, also ob es scharf darauf waere, gepflueckt zu werden.\n");
+  SetProp(P_SHORT,    "Ein Kicherpilz");
+  AddId(({ "pilz", "kicherpilz" }));
+  
+  AddDetail("kaeppchen",BS(
+     "Der Pilz scheint sich fuer die neueste Mode zu interessieren. "
+    +"Mit dem gruenen Kaeppchen ist er nach dem letzten Schrei gekleidet."));
+  AddDetail("mode",BS(
+     "Du schaust an deiner Kleidung hinunter - naja, mit dem Pilz "
+    +"kannst du nicht mithalten."));
+  AddDetail("schrei",BS(
+     "Sei ehrlich: Bei dem Gruen kann man nur schreien!"));
+  AddDetail("gruen",BS(
+     "Eigentlich ist es doch ganz gut, sich nicht fuer Mode zu "
+    +"interessieren, denn das Gruen schmerzt in den Augen."));
+ 
+  set_next_reset(200+random(200));
+}
+
+void reset()
+{
+   set_next_reset(200+random(200));
+   if (environment()) 
+      tell_object(environment(), "Der Kicherpilz kichert Dich an.\n"); 
+   ::reset();
+}
diff --git a/items/kraeuter/kraut.c b/items/kraeuter/kraut.c
new file mode 100644
index 0000000..55b3429
--- /dev/null
+++ b/items/kraeuter/kraut.c
@@ -0,0 +1,26 @@
+#pragma strong_types,rtt_checks
+
+#include <items/kraeuter/kraeuter.h>
+
+inherit STDPLANT;
+
+void create()
+{
+  ::create();
+  //printf("create(): %O\n",load_name());
+  //replace_program();
+  // Wenn es das Standardfile geclont wird, wird customizeMe(0) gerufen, was
+  // den Kraeuter-VC dazu bringt, dieses mit den Daten des Krautes zu
+  // konfigurieren, was der VC gerade erzeugt hat.
+  // load_name() bleibt fuer alle VC-erzeugten Kraeuter-Blueprints das
+  // PLANTITEM.
+  // hier darf _nicht_ previous_object()->CustomizeObject() verwandt werden,
+  // da nur die Blueprint wirklich vom VC erzeugt wird. Fuer die Clones
+  // ruft der Driver den VC nicht jedesmal erneut auf.
+  if (load_name() == PLANTITEM)
+    customizeMe(0);
+}
+
+string GetOwner() {
+  return "Padreic";
+}
diff --git a/items/kraeuter/trank.c b/items/kraeuter/trank.c
new file mode 100644
index 0000000..757b42f
--- /dev/null
+++ b/items/kraeuter/trank.c
@@ -0,0 +1,13 @@
+#pragma strong_types,rtt_checks
+
+#include <items/kraeuter/kraeuter.h>
+
+inherit STDTRANK;
+/*
+void create()
+{
+  ::create();
+  replace_program();
+}
+*/
+
diff --git a/items/kraeuter/trockner.c b/items/kraeuter/trockner.c
new file mode 100644
index 0000000..62e7417
--- /dev/null
+++ b/items/kraeuter/trockner.c
@@ -0,0 +1,475 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_shadow
+
+inherit "/std/container";
+
+#include <properties.h>
+#include <defines.h>
+#include <items/kraeuter/kraeuter.h>
+
+#define BS(x) break_string(x, 78, 0, BS_LEAVE_MY_LFS)
+
+// Enthaelt die raumabhaengig variable Kurzbeschreibung, die der Trockner 
+// annimmt, wenn gerade ein Kraut getrocknet wird.
+private string short_desc;
+
+// Globale Variable fuer die Qualitaet, damit man den Wert nicht im 
+// call_out() mitgeben muss, und sie somit auch nicht mit call_out_info()
+// abfragbar ist.
+private int drying_quality;
+
+private void dry_plant(object kraut, string *msgs);
+private void destroy_herb(object kraut);
+private string my_short();
+private int|string my_noget();
+private string my_long();
+private string* my_ids();
+private string|string* my_name();
+private mixed my_mat();
+
+protected void create() {
+  if ( !clonep(ME) ) {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+
+  Set(P_SHORT, #'my_short, F_QUERY_METHOD);
+  Set(P_LONG, #'my_long, F_QUERY_METHOD);
+  Set(P_NOGET, #'my_noget, F_QUERY_METHOD);
+  Set(P_IDS, #'my_ids, F_QUERY_METHOD);
+  Set(P_NAME, #'my_name, F_QUERY_METHOD);
+  Set(P_MATERIAL, #'my_mat, F_QUERY_METHOD);
+  // Properties zu securen ist vielleicht etwas sehr paranoid.
+  Set(P_SHORT, SECURED|NOSETMETHOD, F_MODE_AS);
+  Set(P_LONG, SECURED|NOSETMETHOD, F_MODE_AS);
+  Set(P_NOGET, SECURED|NOSETMETHOD, F_MODE_AS);
+  Set(P_IDS, SECURED|NOSETMETHOD, F_MODE_AS);
+  Set(P_NAME, SECURED|NOSETMETHOD, F_MODE_AS);
+  SetProp(P_MAX_OBJECTS,1);
+  SetProp(P_MAX_WEIGHT,100000);
+  SetProp(P_TRANSPARENT,0);
+
+  AddCmd("trockne&@PRESENT", "cmd_trocknen", "Was willst Du trocknen?");
+}
+
+private string my_short() {
+  if ( first_inventory(ME) )
+    return short_desc;
+  return 0;
+}
+
+// Querymethoden, die Langbeschreibung und weitere Properties des im 
+// Trockner enthaltenen Krautobjektes nach draussen weiterreichen, um die
+// Illusion zu erzeugen, dass wirklich das echte Kraut im Raum zu sehen sei.
+// Alle Funktionen gehen davon aus, dass das zu trocknende Kraut das erste
+// (und einzige) Objekt im Inventar des Trockners ist und geben dessen
+// Properties zurueck.
+private string my_long() {
+  object inv = first_inventory(ME);
+  if ( objectp(inv) )
+    return inv->QueryProp(P_LONG)+inv->Name(WER,1)+
+      " wird gerade getrocknet.\n";
+  return 0;
+}
+
+private string|string* my_name() {
+  object inv = first_inventory(ME);
+  if ( objectp(inv) )
+    return inv->QueryProp(P_NAME);
+  return Query(P_NAME,F_VALUE);
+}
+
+private mixed my_mat() {
+  object inv = first_inventory(ME);
+  if ( objectp(inv) )
+    return inv->QueryProp(P_MATERIAL);
+  return ([]);
+}
+
+private int|string my_noget() {
+  object inv = first_inventory(ME);
+  if ( objectp(inv) )
+    return inv->Name(WER,1)+" wird gerade getrocknet, Du solltest "+
+      inv->QueryPronoun(WEN)+" liegenlassen, bis "+
+      inv->QueryPronoun(WER)+" fertig ist.";
+  return 1;
+}
+
+private string* my_ids() {
+  object inv = first_inventory(ME);
+  if ( objectp(inv) )
+    return inv->QueryProp(P_IDS);
+  return Query(P_IDS,F_VALUE);
+}
+
+// Kommandofunktion zum Starten des Tocknungsvorganges. Holt sich die
+// relevanten Daten aus dem Krautmaster ab, setzt Meldungen und Texte und
+// wirft den Trocknungs-Callout an.
+static int cmd_trocknen(string str, mixed *param) {
+  // Master liefert leeres Array oder eins mit 2 Elementen ({delay, quality})
+  // environment(ME) liest er selbstaendig aus
+  int *drying_data = PLANTMASTER->QueryDryingData();
+  object kraut = param[0];
+
+  // Der Trockner taeuscht vor, selbst das Kraut zu sein, das zum Trocknen
+  // im Raum liegt. Daher wird hier noch geprueft, ob der Spieler vielleicht
+  // den Trockner selbst zu trocknen versucht. 
+  if ( kraut == ME ) {
+    tell_object(PL, BS(kraut->Name(WER,1)+" wird bereits getrocknet, Du "
+      "solltest "+kraut->QueryPronoun(WEN)+" besser liegenlassen."));
+  }
+  // Es muss sich auch um ein Kraut handeln und nicht irgendwas anderes.
+  else if ( load_name(kraut) != PLANTITEM ) {
+    return 0;
+  }
+  // Spieler muss das Kraut im Inventar haben.
+  else if ( environment(kraut) != PL ) {
+    tell_object(PL, BS(
+      "Du musst "+kraut->name(WEN,1)+" schon in die Hand nehmen, um "+
+      kraut->QueryPronoun(WEN)+" sorgfaeltig trocknen zu koennen."));
+  }
+  // Das Kraut darf nicht unwirksam sein, was durch eine Plant-ID von -1 
+  // gekennzeichet ist.
+  else if ( param[0]->QueryPlantId() == -1 ) {
+    tell_object(PL, BS(
+      kraut->Name(WER,1)+" haette ohnehin keine Wirkung, da kannst Du Dir "
+      "die Muehe sparen, "+kraut->QueryPronoun(WEN)+" noch aufwendig zu "
+      "trocknen."));
+  }
+  // Master hat keine Daten geliefert, also befindet sich der Trockner
+  // offenbar in einem unzulaessigen Raum.
+  else if ( sizeof(drying_data) != 2 ) {
+    tell_object(PL, BS(
+      "Dieser Ort ist nicht geeignet, um "+kraut->name(WEN,1)+" hier zu "
+      "trocknen."));
+  }
+  // Kraut ist schon getrocknet? Dann waere eine weitere Trocknung unsinnig.
+  else if ( kraut->QueryDried() ) {
+    tell_object(PL, BS(kraut->Name(WER,1)+" ist schon getrocknet, eine "
+      "weitere Behandlung wuerde "+kraut->QueryPronoun(WEM)+" zu stark "
+      "zusetzen, "+kraut->QueryPronoun(WEN)+" gar zerstoeren."));
+  }
+  // Es ist schon eine Pflanze im Trockner? Dann nicht noch eine reintun.
+  else if ( first_inventory(ME) ) {
+    tell_object(PL, BS("Hier wird gerade schon etwas getrocknet."));
+  }
+  // Aus irgendeinem Grund schlaegt die Bewegung des Krautes in den Trockner
+  // fehl? Dann muss sich das ein Magier anschauen, denn das geht nicht mit
+  // rechten Dingen zu.
+  else if ( kraut->move(ME, M_PUT) != MOVE_OK ) {
+    tell_object(PL, BS("Aus einem Dir unerfindlichen Grund schaffst Du es "
+      "nicht, die Trocknung "+kraut->name(WESSEN,1)+" zufriedenstellend "
+      "durchzufuehren und brichst den Versuch wieder ab. Du solltest einem "
+      "Magier Bescheid sagen, dass hier etwas nicht stimmt."));
+  }
+  // Alles geklappt, alle Bedingungen erfuellt? Dann koennen wir jetzt
+  // tatsaechlich endlich das Kraut trocknen.
+  else {
+    int drying_delay = drying_data[0]; // nur lokal benoetigt
+    drying_quality = drying_data[1]; // globale Variable
+    string where = load_name(environment(ME));
+    string msg_self, msg_other;
+    string kr = kraut->name(WEN,1);
+    string* callout_msgs = ({
+        kraut->Name(WER,1)+" ist jetzt in einem zufriedenstellenden "
+        "Zustand. Besser wirst Du es an diesem Ort vermutlich nicht "
+        "hinbekommen, daher beendest Du die Trocknung und nimmst "+
+        kraut->QueryPronoun(WEN)+" wieder an Dich.",
+        PL->Name(WER)+" schaut "+kr+" pruefend an und "
+        "beendet dann die Trocknung, offenbar zufrieden mit dem Resultat."});
+    int blocker; // auf 1 setzen, falls das Trocknen verhindert werden soll
+    // Hier koennen jetzt abhaengig vom Raum passende Meldungen gesetzt
+    // werden. Die zulaessigen Standorte hier noch weiter zu obfuscaten
+    // waere zwar moeglich, aber zu unuebersichtlich geworden.
+    switch(where) {
+      /*
+       * GEBIRGE 
+       */
+      case "/d/gebirge/silvana/cronoertal/room/th7u":
+        msg_self = "Du legst "+kr+" vorsichtig und in gebuehrendem Abstand "
+          "zu den Flammen neben die Feuerstelle und wartest gespannt, ob "
+          "die Trocknung wohl gelingen wird.";
+        msg_other = PL->Name(WER)+" legt etwas neben die Feuerstelle, "
+          "vermutlich, um es zu trocknen.";
+        short_desc = kraut->Name(WER)+" liegt zum Trocknen am Feuer";
+        break;
+      /*
+       * EBENE 
+       */
+      case "/d/ebene/zardoz/burg/kueche":
+        msg_self = "Du legst "+kr+" vorsichtig an eine der kuehleren "
+          "Stellen des Bleches im Ofen, es soll ja trocknen, und nicht "
+          "backen.";
+        msg_other = PL->Name(WER)+" legt etwas Gruenzeug auf das Blech im "
+          "Ofen.";
+        short_desc = kraut->Name(WER)+" liegt zum Trocknen im Ofen";
+        break;
+      case "/d/ebene/esme/masinya/rooms/kueche":
+        msg_self = "Du haengst "+kr+" ueber den Herd, um "+
+          kraut->QueryPronoun(WEN)+" in der Abwaerme trocknen zu lassen.";
+        msg_other = PL->Name(WER)+" haengt ein Kraut zum Trocknen ueber "
+          "den Herd.";
+        short_desc = kraut->Name(WER)+" haengt zum Trocknen ueber dem Herd";
+        break;
+      case "/d/ebene/throin/brauerei/room/darre06":
+        msg_self = "Du legst "+kr+" zu dem Malz in den "
+          "Keimkasten in der Hoffnung, dass "+kraut->QueryPronoun(WER)+
+          " auf diese Weise getrocknet werden kann.";
+        msg_other = PL->Name(WER)+" legt etwas eigenes Gruenzeug zu dem "
+          "Malz in den Keimkasten.";
+        short_desc = kraut->Name(WER)+" liegt zum Trocknen im Keimkasten";
+        break;
+      case "/d/ebene/arathorn/orakel/room/zelt":
+        msg_self = "Du legst "+kr+" vorsichtig ans "
+          "Lagerfeuer. Du schaust unsicher zu Chinkuwaila, doch der "
+          "alte Schamane nickt zustimmend, Du hast wohl alles richtig "
+          "gemacht.";
+        msg_other = PL->Name(WER)+" legt eine Pflanze ans Lagerfeuer, wohl "
+          "um sie zu trocknen.";
+        short_desc = kraut->Name(WER)+" wird gerade am Lagerfeuer getrocknet";
+        break;
+      /*
+       * WUESTE
+       */
+      case "/d/wueste/tsunami/schule/rooms/kraeuter":
+        blocker = objectp(present_clone("/d/wueste/tsunami/schule/mon/hexe", 
+                    environment(ME)));
+        if ( !blocker ) {
+          msg_self = "Du steckst "+kr+" in Muetterchen "
+            "Isewinds Trockenofen, der wohlig zu knistern beginnt.";
+          msg_other = PL->Name(WER)+" legt vorsichtig ein Kraut ";
+          short_desc = kraut->Name(WER)+" wird gerade im Ofen getrocknet";
+        }
+        else {
+          msg_self = "Muetterchen Isewind haelt Dich auf, sie scheint "
+            "niemanden an ihren Trockenofen heranlassen zu wollen.";
+          msg_other = PL->Name(WER)+" wird von Muetterchen Isewind an der "
+            "Benutzung des Ofens gehindert.";
+        }
+        break;
+      /*
+       * WALD
+       */
+      case "/d/wald/feigling/quest/room/huette3":
+        msg_self = "Du legst "+kr+" so nah ans Feuer, "
+          "wie Du glaubst, dass es der Trocknung nicht schadet.";
+        msg_other = PL->Name()+" legt eine Pflanze an die Kochstelle.";
+        short_desc = kraut->Name(WER)+" liegt zum Trocknen an der "
+          "Feuerstelle";
+        break;
+      case "/d/wald/leusel/quest/rooms/laborsuedosten":
+        msg_self = "Du legst "+kr+" in eins der "
+          "Tonschiffchen und schiebst es in den mittelheissen Ofen hinein. "
+          "Hoffentlich geht das gut, es kommt Dir da drinnen schon fast zu "
+          "warm fuer eine ordnungsgemaesse Trocknung vor.";
+        msg_other = PL->Name(WER)+" schiebt ein Kraut in einem "
+          "Tonschiffchen in einen der Oefen hinein, um es zu trocknen.";
+        short_desc = kraut->Name(WER)+" dampft in einem Tonschiffchen im "
+          "ersten Ofen vor sich hin";
+        break;
+      /*
+       * INSELN
+       */
+      case "/d/inseln/zesstra/vulkanweg/room/r8":
+        msg_self = "Du legst "+kr+" vorsichtig auf die "
+          "heissen Felsen in der Naehe des Lavasees, auf dass die "
+          "heissen Winde "+kraut->QueryPronoun(WEN)+" trocknen moegen.";
+        msg_other = PL->Name(WER)+" legt ein Kraut auf den Felsen ab, um es "
+          "von der heissen Luft trocknen zu lassen.";
+        short_desc = kraut->Name(WER)+" liegt zum Trocknen auf dem Felsen";
+        break;
+      case "/d/inseln/miril/zyklopen/room/palast/insel1p6":
+        // QueryOven() liefert 2 fuer "Feuer", 1 fuer "Glut", 0 fuer "aus".
+        switch (environment(ME)->QueryOven()) {
+          case 2:
+            msg_self = "Du legst "+kr+" vorsichtig an das Feuer, das in "
+              "der Feuerstelle brennt, sorgsam bemueht, dass "+
+              kraut->QueryPronoun(WER)+" nicht zuviel Hitze abbekommt.";
+            msg_other = PL->Name(WER)+" legt sorgsam ein Kraut in die Naehe "
+              "des Feuers, das in der Feuerstelle brennt.";
+            break;
+          case 1:
+            msg_self = "Du legst "+kr+" an die Feuerstelle, pruefst die "
+              "Hitze und rueckst "+kraut->QueryPronoun(WEN)+" noch etwas "
+              "naeher an die Glut, dann trittst Du zufrieden einen Schritt "
+              "zurueck.";
+            msg_other = PL->Name(WER)+" legt "+kraut->name(WEN)+" an die "
+              "Feuerstelle, schubst noch ein wenig daran herum und tritt "
+              "dann von der Glut zurueck, "+PL->QueryPronoun(WER)+" scheint "
+              "recht zufrieden zu sein.";
+            break;
+          default:
+            blocker=1;
+            msg_self = "In dem Kamin findest Du nicht einmal etwas Glut, "
+              "geschweige denn offenes Feuer. So wird das mit dem Trocknen "
+              "nichts, und auf dem Herd ist Dir das Risiko zu gross, dass "
+              "Fett aus der Pfanne auf "+kr+" spritzt.";
+            break;
+        }
+        short_desc = kraut->Name(WER)+" liegt zum Trocknen an der "
+          "Feuerstelle";
+        break;
+      /*
+       * POLAR
+       */
+      case "/d/polar/tilly/llp/rentner/kueche":
+        msg_self = "Der Herd gibt eine infernalische Hitze ab. Zum Kochen "
+          "ist das toll, aber Du brauchst doch einen Moment, um ein "
+          "geeignetes Plaetzchen fuer "+kr+" zu finden.";
+        msg_other = PL->Name(WER)+" legt ein Kraut auf den heissen Herd "
+          "und schiebt es unruhig noch ein wenig hin und her, als waere "+
+          PL->QueryPronoun(WEM)+" die Hitze beinahe ein wenig zu gross.";
+        short_desc = kraut->Name(WER)+" liegt auf dem schmiedeeisernen "
+          "Herd";
+        break;
+      /*
+       * VLAND
+       */
+      case "/d/vland/morgoth/room/kata/ukat13":
+        object c = present_clone("/d/vland/morgoth/obj/kata/rfdcorpse", 
+              environment(ME));
+        // Bequemer, das auf 0 zu setzen, wenn's klappt, bei sovielen
+        // Hinderungsgruenden. ;-)
+        blocker = 1;
+        // Leiche liegt da.
+        if ( objectp(c) ) 
+        {
+          // Feuerdaemon anwesend: der blockt den Versuch
+          if ( present_clone("/d/vland/morgoth/npc/kata/firedemon3",
+                environment(ME)) ) 
+          {
+            msg_self = "Der Feuerdaemon droht Dir mit sengender Hoellenpein. "
+              "Beschwichtigend trittst Du einen Schritt zurueck.";
+            msg_other = PL->Name(WER)+" tritt auf den Aschehaufen zu, wird "
+              "aber von dem Feuerdaemon bedroht und weicht wieder zurueck.";
+          }
+          // Aschehaufen nicht mehr heiss genug?
+          else if ( c->QueryDecay()<2 ) {
+            msg_self = c->Name(WER,1)+" ist schon zu sehr abgekuehlt und "
+              "wuerde nicht mehr genug Hitze spenden, um "+kr+" zu trocknen.";
+          }
+          else 
+          {
+            blocker = 0;
+            msg_self = "Dich vorsichtig umschauend, legst Du "+kr+" in die "
+              "Naehe "+c->name(WESSEN,1)+". Hoffentlich gelingt das in "
+              "dieser unwirtlichen Umgebung!";
+            msg_other = PL->Name(WER)+" beugt sich zum Boden hinuntern und "
+              "legt vorsichtig ein Kraut in die Naehe "+c->name(WESSEN,1)+".";
+          }
+          short_desc = kraut->Name(WER)+" liegt neben "+c->name(WEM,1)+
+            ", offenbar soll "+kraut->QueryPronoun(WER)+" getrocknet werden";
+        }
+        // Keine Leiche da? Dann geht's nicht.
+        else {
+          msg_self = "Genausowenig, wie Dir die Flammen in diesem Inferno "
+            "etwas anhaben koennen, so wenig kannst Du sie nutzen, um "+
+            kr+" zu trocknen.";
+        }
+        break;
+      case "/d/vland/morgoth/room/kata/kata5":
+        object ob = present_clone("/d/vland/morgoth/npc/kata/ghost",
+            environment(ME));
+        blocker = 1;
+        // Geist anwesend? Keine Chance.
+        if ( objectp(ob) ) {
+          msg_self = ob->Name(WER,1)+" stoert Dich in Deiner Konzentration, "
+            "Du kannst so nicht arbeiten!";
+        }
+        // Umgebung noch neblig? Dann nicht trocknen.
+        else if ( environment(ME)->QueryFog() ) {
+          msg_self = "In diesem verdammten Nebel ist absolut nichts zu "
+            "sehen. Ausserdem ist es hier viel zu feucht, "+kr+" wuerde "
+            "ohnehin nur vor Deiner Nase wegschimmeln.";
+        }
+        // Feuer brennt nur noch 90 Sekunden? Dann lohnt sich das nicht.
+        else if ( query_next_reset(environment()) < time()+90 ) {
+          msg_self = "Ein pruefender Blick auf das Feuer zeigt Dir, dass "
+            "es wohl nicht mehr lange genug brennen wird, um "+kr+" noch "
+            "erfolgreich trocknen zu koennen.";
+        }
+        else {
+          blocker = 0;
+          msg_self = "Du legst "+kr+" in angemessenem Abstand zum Feuer "
+            "auf den Boden und wartest gespannt, ob Dir hier wohl eine "
+            "brauchbare Trocknung gelingen wird.";
+          msg_other = PL->Name(WER)+" bueckt sich zum Boden und legt etwas "
+            "ans Feuer, anscheinend ein Kraut, das "+PL->QueryPronoun(WER)+
+            " trocknen will.";
+          short_desc = kraut->Name(WER)+" liegt zum Trocknen am Feuer";
+        }
+        break;
+      case "/d/vland/alle/koomi_v/wschenke/room/waldschenke":
+        msg_self = "Dieser Kachelofen ist ungemein praktisch. Du legst "+
+          kr+" einfach oben drauf, und die kuschelige Waerme trocknet "+
+          kraut->QueryPronoun(WEN)+" beinahe von selbst.";
+        msg_other = PL->Name(WER)+" legt ein Kraut zum Trocknen oben auf "
+          "den Kachelofen, offenbar recht angetan von dessen kuscheliger "
+          "Waerme.";
+        short_desc = kraut->Name(WER)+" liegt zum Trocknen auf dem "
+          "Kachelofen";
+        break;
+      /*
+       * DEBUGZWECKE
+       */
+      case "/players/arathorn/workroom":
+        msg_self = "Du haeltst das Kraut vors Feuer und beginnst die "
+          "Trocknung.";
+        msg_other = PL->Name(WER)+" schickt sich an, ein Kraut am Feuer zu "
+          "trocknen.";
+        short_desc = kraut->Name(WER)+" liegt zum Trocknen am Feuer";
+        break;
+    }
+    // Raummeldungen entsprechend der eingestellten Texte ausgeben.
+    tell_object(PL, BS(msg_self));
+    if ( msg_other )
+      tell_room(environment(ME), BS(msg_other), ({PL}));
+    // Callout starten, wenn niemand das Trocknen verhindert.
+    if ( !blocker )
+      call_out(#'dry_plant, drying_delay, kraut, callout_msgs);
+    // Ansonsten das Kraut in den Spieler zurueck, das ja oben schon
+    // in den Trockner bewegt wurde.
+    else {
+      kraut->move(PL, M_GET);
+    }
+  }
+  return 1;
+}
+
+// Kraut wird getrocknet, sofern der Spieler noch im Raum ist, ...
+private void dry_plant(object kraut, string *msgs) {
+  if ( objectp(PL) && environment(PL) == environment(ME) ) {
+    tell_object(PL, BS(msgs[0]));
+    tell_room(environment(ME), BS(msgs[1]), ({PL}));
+    kraut->move(PL, M_GET);
+    kraut->DryPlant(drying_quality);
+  }
+  // ... ansonsten laeuft die Trocknung weiter, und das Kraut verbrennt.
+  else {
+    tell_room(ME, BS(kraut->Name(WER,1)+" wird extrem dunkel, bald wird "+
+      kraut->QueryPronoun(WER)+" zu nichts mehr zu gebrauchen sein!"));
+    // Das Delay fuer diesen zweiten Callout ist immer fix. Kommt hoffentlich
+    // selten genug vor und braucht daher eher nicht extra aus dem Master
+    // geholt zu werden.
+    call_out(#'destroy_herb, 20, kraut);
+  }
+}
+
+// Zerstoerung des Krautes. Da die Krautobjekte selbst eine Meldung 
+// ausgeben, wenn DryPlant(0) gerufen wird, wird erst das Kraut ins
+// Environment bewegt und erst danach die Funktion gerufen.
+private void destroy_herb(object kraut) {
+  kraut->move(environment(ME),M_PUT);
+  kraut->DryPlant(0);
+}
+
+// Nur die korrekten Krautobjekte koennen in den Trockner bewegt werden.
+// Schuetzt natuerlich nicht vor M_NOCHECK, aber wenn das vorkommen sollte,
+// muss vielleicht noch weiter abgesichert werden, oder der verursachende
+// Magier ausgeschimpft. ;-) 
+varargs int PreventInsert(object ob) {
+  if (load_name(ob) == PLANTITEM && clonep(ob)) 
+    return ::PreventInsert(ob);
+  return 1;
+}
diff --git a/items/kraeuter/virtual_compiler.c b/items/kraeuter/virtual_compiler.c
new file mode 100644
index 0000000..d48370e
--- /dev/null
+++ b/items/kraeuter/virtual_compiler.c
@@ -0,0 +1,300 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+#pragma no_inherit,no_clone,strong_types,rtt_checks
+
+#include <defines.h>
+#include <properties.h>
+#include <v_compiler.h>
+#include <items/kraeuter/kraeuter.h>
+#include <wizlevels.h>
+
+inherit "/std/virtual/v_compiler";
+inherit "/std/thing/language";
+inherit "/std/thing/description";
+
+// mit Hilfe dieses mappings kommt man sowohl an die ID und die Eigenschaften,
+// als auch an die Liste der Raeume in denen das Kraut mit dem filenamen room
+// gefunden werden kann.
+// ([ "key": ({ ({eigenschaften}), ([raeume]) }) ])
+private mapping krautdaten;
+
+// AN: enthaelt die Liste der gueltigen Kraeuter-Dateinamen ohne .c
+// am Ende. Ich vermute, dass es deswegen ein Mapping ist, damit in 
+// Validate() einfach member() drauf gemacht werden kann und man nur 0/1
+// als Rueckgabewerte pruefen muss, statt -1 bei nem member() auf ein Array.
+private mapping validfiles;
+
+public void update(mapping data)
+{
+  if (previous_object() == find_object(PLANTMASTER))
+  {
+    krautdaten = data;
+    validfiles = mkmapping(m_indices(krautdaten));
+  }
+}
+
+// Wird benutzt, um kurze IDs von Kraeutern zu raten. Diese IDs werden
+// eingetragen, wenn der Krautname die ID als Teilstring enthaelt.
+#define IDLIST ({ "klee", "rebe", "hahnenfuss", "rettich", "kraut", "wurz",\
+                     "moos", "enzian", "rautenwicke", "pilz", "nelke",\
+                     "lichtnelke", "wicke", "zwiebel", "hanf", "kresse"})
+
+void create()
+{
+   seteuid(getuid());
+
+   v_compiler::create();
+   description::create();
+
+   SetProp(P_COMPILER_PATH, __DIR__);
+   SetProp(P_STD_OBJECT, PLANTITEM);
+   
+   PLANTMASTER->UpdateVC();
+}
+
+string Validate(string file)
+{
+   if (!stringp(file)) return 0;
+   file = ::Validate(explode(file, "#")[0]);
+#if MUDNAME == "MorgenGrauen"
+   return (member(validfiles, file) ? file : 0);
+#else
+   return file;
+#endif
+}
+
+private nosave object simul_efun;
+
+// fuer SIMUL_EFUN_FILE
+#include <config.h>
+
+// AN: Funktion liefert das clonende Objekt als dessen Blueprint-Namen,
+// indem es den Caller-Stack durchlaeuft und nach einem Objekt sucht,
+// das weder der Master, noch ein Simul-Efun-Objekt, noch dieser VC selbst
+// ist. Der Name des gefundenen Objekts wird zurueckgegeben, oder 0.
+nomask private string get_cloner()
+{
+   int i;
+   object po;
+
+   if (!simul_efun)
+   {
+      if (!(simul_efun=find_object(SIMUL_EFUN_FILE)))
+         simul_efun=find_object(SPARE_SIMUL_EFUN_FILE);
+   }
+   // wenn sie jetzt nicht existiert - auch gut, dann gibt es halt keine
+   // sefuns.
+
+   for (i=0; po=previous_object(i); i++)
+   {
+      if (po==master() || po==simul_efun || po==ME || po==previous_object())
+         continue;
+      return BLUE_NAME(po);
+   }
+   return 0;
+}
+
+// Konfiguriert das erzeugte Objekt entsprechend der dafuer im Kraeutermaster
+// bekannten Daten. Vergibt auf die Plant-ID.
+varargs string CustomizeObject(string file)
+{
+   if (previous_object()->QueryPlantId()) return 0; // bereits initialisiert
+   
+   if (stringp(file))
+      file=Validate(file);
+   else file=::CustomizeObject();
+   if (!file) return 0;
+
+   closure sp=symbol_function("SetProp", previous_object());
+   mixed arr=krautdaten[file];
+   if (pointerp(arr))
+   {
+      // Welches Objekt clont das File?
+      string cloner = get_cloner();
+      string rooms = arr[1];
+      mixed props = arr[0];
+      // Wird das Kraut legal von einem eingetragenen Cloner erzeugt? Nur dann
+      // bekommt es eine gueltige Plant-ID.
+     int legal=member(rooms, get_cloner()) || cloner==PLANTMASTER;
+     if (!legal && this_interactive() && IS_ARCH(this_interactive()))
+        legal=1;
+     
+      // Konfiguriert wird das Objekt dann, wenn es per VC erzeugt wird oder
+      // ein Clone einer per VC erzeugten BP ist - d.h. wenn es nicht aus
+      // einem real existierenden File auf der Platte existiert. Das ist dann
+      // der Fall, wenn der Loadname gleich dem Standardplantobjekt des VC
+      // ist.
+      if (load_name(previous_object())==PLANTITEM)
+      {
+        if ((props[INGREDIENT_NAME]=="Klee") ||
+            (props[INGREDIENT_NAME][<4..]=="klee")) {
+           funcall(sp, P_NAME, ({ props[INGREDIENT_NAME],
+                                  props[INGREDIENT_NAME]+"s",
+                                  props[INGREDIENT_NAME],
+                                  props[INGREDIENT_NAME]}));
+        }
+        else funcall(sp, P_NAME,     props[INGREDIENT_NAME]);
+        funcall(sp, P_NAME_ADJ, props[INGREDIENT_ADJ]);
+        funcall(sp, P_GENDER,   props[INGREDIENT_GENDER]);
+        funcall(sp, P_LONG,     props[INGREDIENT_LONG]);
+        funcall(sp, PLANT_ROOMDETAIL, props[INGREDIENT_ROOMDETAIL]);
+        if (props[INGREDIENT_DEMON]==RAW) {
+           funcall(sp, P_ARTICLE, 0);
+           funcall(sp, P_SHORT, previous_object()->Name(WER));
+           funcall(sp, P_ARTICLE, 1);
+        }
+        else funcall(sp, P_SHORT,
+             previous_object()->Name(WER,props[INGREDIENT_DEMON]));
+        previous_object()->AddId(lowerstring(props[INGREDIENT_NAME]));
+        // bei zusammengesetzten Namen, auch den hauptnamen akzeptieren
+        string str=lowerstring(props[INGREDIENT_NAME]);
+        string *names=explode(str, "-");
+        if (sizeof(names)>1) previous_object()->AddId(names[<1]);
+        names=explode(str, " ");
+        if (sizeof(names)>1) previous_object()->AddId(names[<1]);
+        foreach(string id: IDLIST)
+        {
+          if (strstr(str, id)==-1) continue;
+          previous_object()->AddId(id);
+          break;
+        }
+        // Adjective vorher deklinieren
+        str=props[INGREDIENT_ADJ];
+        if (stringp(str))
+        {
+          str=DeclAdj(lowerstring(str), WEN, 0);
+          previous_object()->AddAdjective(str);
+        }
+      }  // Ende Konfiguration eines VC-erzeugten Objekts
+      // Plant-ID wird fuer alle Objekte auf irgendwas gesetzt.
+      previous_object()->SetPlantId(legal ? props[INGREDIENT_ID] : -1);
+   }
+   // Keine Krautdaten bekannt...
+   else
+   {
+     funcall(sp, P_NAME,     "Kraut");
+     funcall(sp, P_GENDER,   NEUTER);
+     funcall(sp, P_SHORT,    "Ein Testkraut ("+capitalize(file)+")");
+     funcall(sp, P_LONG,     "Ein nicht naeher spezifiziertes Testkraut.\n");
+     funcall(sp, PLANT_ROOMDETAIL,
+         "Ein nicht naeher spezifiziertes Testkraut ("
+         +capitalize(file)+").\n");
+     previous_object()->AddId("kraut");
+     previous_object()->SetPlantId(-1);
+   }
+   return file;
+}
+
+int NoParaObjects()
+{   return 1;   }
+
+// AN: Funktion erzeugt aus den vorliegenden Daten der Kraeuterliste ein
+// physikalisch existierendes File in diesem Verzeichnis, zB wenn die Daten
+// erweitert werden sollen. Die Kraeuterliste stellt nur generische Objekte
+// zur Verfuegung, die keine Details haben. Wenn die Objekte ausgeschmueckt
+// werden sollen, koennen diese auch als Datei hier liegen.
+// Wird vom Plantmaster aus gerufen. Die Existenz von Klartext-
+// Fehlermeldungen laesst darauf schliessen, dass diese Funktion dafuer
+// vorgesehen war, vom Planttool aus gerufen zu werden. Dies wird dadurch
+// bestaetigt, dass dort wie hier alle von Magiern benutzbaren Kommando-
+// funktionen mit _ beginnen (_showplant(), _addroom() etc.), und die
+// Kommandofunktion im Planttool generell in der Lage ist, alle _*() 
+// im Plantmaster zu rufen, sofern existent und fuer den Magier freigegeben.
+// AN/TODO: ggf. sollte man hier noch pruefen, ob die VC-Blueprint des
+// angeforderten Krautes gerade existiert, denn sonst wuerde das auf der
+// Platte liegende, scheinbar (nicht) geladene Objekt nicht mit dem
+// VC-Objekt uebereinstimmen. Evtl. reicht es aus, die Blueprint einfach
+// zu zerstoeren und neuzuladen.
+int _createfile(string filename)
+{
+   int i;
+   string str, short, long, gender, *name, roomdetail;
+   string *ids;
+   string plantfile;
+
+/*   if (object_name(previous_object())!=PLANTMASTER) {
+      write("Illegal usage of _createfile()!\n");
+      return 1;   
+   }*/
+// ^^^ Zook, ggf.spaeter wieder Kommentar entfernen. 
+
+   mixed arr;
+   if (!pointerp(arr=krautdaten[filename])) {
+      write("Unknown plant '"+filename+"'.\n");
+      return 1;
+   }
+   if (file_size(PLANTDIR+filename+".c")>=0) {
+      write("error: file "+PLANTDIR+filename+".c already exists.\n");
+      return 1;
+   }
+   mixed props = arr[0];
+
+   // Kurzbeschreibung erzeugen
+   SetProp(P_NAME,     props[INGREDIENT_NAME]);
+   SetProp(P_NAME_ADJ, props[INGREDIENT_ADJ]);
+   SetProp(P_GENDER,   props[INGREDIENT_GENDER]);
+   if (props[INGREDIENT_DEMON]==RAW) {
+       SetProp(P_ARTICLE, 0);
+       short=Name(WER);
+       SetProp(P_ARTICLE, 1);
+   }
+   else short=Name(WER,props[INGREDIENT_DEMON]);
+   ids = ({ lowerstring(props[INGREDIENT_NAME]) });
+   // bei zusammengesetzten Namen, auch den hauptnamen akzeptieren
+   str=lowerstring(props[INGREDIENT_NAME]);
+   name=explode(str, "-");
+   if (sizeof(name)>1) ids += ({ name[<1] });
+   name=explode(str, " ");
+   if (sizeof(name)>1) ids += ({ name[<1] });
+   for (i=sizeof(IDLIST)-1; i>=0; i--) {
+       if (strstr(str, IDLIST[i], 0)==-1) continue;
+       ids += ({ IDLIST[i] });
+       break;
+   }
+   switch(props[INGREDIENT_GENDER]) {
+     case MALE:   gender="MALE"; break;
+     case FEMALE: gender="FEMALE"; break;
+     case NEUTER: gender="NEUTER"; break;
+     default: gender=props[INGREDIENT_GENDER];
+   }
+   long="    \""+implode(old_explode(props[INGREDIENT_LONG], "\n"), 
+                   "\\n\"\n   +\"")+"\\n\"";
+   roomdetail="    \""+implode(
+      old_explode(props[INGREDIENT_ROOMDETAIL], "\n"), "\\n\"\n   +\"")+
+      "\\n\"";
+   plantfile=
+    "#pragma strong_types,rtt_checks\n\n"
+    "#include <properties.h>\n"
+    "#include <items/kraeuter/kraueter.h>\n"
+    "#include <items/kraeuter/kraeuterliste.h>\n\n"
+    "inherit STDPLANT;\n\n"
+    "protected void create()\n"
+    "{\n"
+    "  ::create();\n";
+   plantfile+="  customizeMe("+upperstring(filename)+");\n";
+   plantfile+=
+    "  SetProp(P_NAME,     \""+props[INGREDIENT_NAME]+"\");\n"
+    "  SetProp(P_NAME_ADJ, \""+(props[INGREDIENT_ADJ]||"")+"\");\n"
+    "  SetProp(P_GENDER,   "+gender+");\n"
+    "  SetProp(P_LONG,     \n"+
+    long+");\n"
+    "  SetProp(PLANT_ROOMDETAIL, \n"+
+    roomdetail+");\n"
+    "  SetProp(P_SHORT,    \""+short+"\");\n";
+   plantfile+="  AddId(({";
+   for (i=sizeof(ids)-1; i>=0; i--)
+     plantfile+=" \""+ids[i]+"\",";
+   plantfile[<1]=' ';
+   plantfile+="}));\n";
+   // Adjective vorher deklinieren
+   if (stringp(short=props[INGREDIENT_ADJ])) {
+     short=DeclAdj(lowerstring(short), WEN, 0)[0..<2];
+     plantfile+="  AddAdjective(\""+short+"\");\n";
+   }
+   plantfile+="}\n";
+   write(plantfile);
+   //write_file(PLANTDIR+filename+".c", plantfile);
+   write("Filename: "+PLANTDIR+filename+".c\n");
+   return 1;
+}
+
diff --git a/items/money.c b/items/money.c
new file mode 100644
index 0000000..4a9a5f4
--- /dev/null
+++ b/items/money.c
@@ -0,0 +1,340 @@
+// MorgenGrauen MUDlib
+//
+// money.c -- Unser Geld
+//
+// $Id: money.c 8893 2014-08-04 19:48:56Z Zesstra $
+
+inherit "/std/unit";
+
+#pragma strong_types,rtt_checks
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <unit.h>
+#include <money.h>
+
+// zum debuggen (debugmode kommt aus /std/unit.c
+#include <living/comm.h>
+#define ZDEBUG(x) if (stringp(debugmode) && find_player(debugmode)) \
+  find_player(debugmode)->ReceiveMsg(x,MT_DEBUG,0,object_name()+": ",ME)
+//#define ZDEBUG(x)
+
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+  // Logg-Funktion, um Nicht-Standard-Geldbugs zu finden.
+  // Eingebaut 27.03.2000 - Tiamak
+  // Vor vereinigen mit anderen Geldobjekten in NotifyMove().
+  object moneylog = find_object("/p/daemon/moneylog");
+  if ( (!environment() && previous_object() &&
+        !query_once_interactive(previous_object()) &&
+        load_name(previous_object()) != GELD) &&
+        moneylog)
+      moneylog->AddMoney( previous_object(), Query(U_REQ) );
+
+  // Hier passiert Vereinigung mit anderen Geldobjekten... Etc.
+  ::NotifyMove(dest, oldenv, method);
+
+  // Zu diesem Zeitpunkt sind Vereinigungen mit anderen Geldobjekten in dest
+  // schon gelaufen, hier muessen jetzt noch Geldboersen und Seherkarten
+  // beruecksichtigt werden.
+  // Sollverhalten ist:
+  // Vereinigung mit Boersen+geldkarten nur in Lebewesen.
+  // Ausserdem soll nicht vereinigt werden, wenn man muenzen aus einer Boerse
+  // nimmt. 
+  // A) Betrag ist positiv:
+  // A1) mit der ersten geld-enhaltenen Geldboerse im Inventar vereinigen.
+  //     Gibt es keine geld-gefuellte, wird die erste leere genommen.
+  // B) Betrag ist negativ:
+  //    Es wird versucht, mit geldgefuellten Boersen und aktiven Seherkarten
+  //    (d.h. Karten, diesem Environment gehoeren) zu vereinigen.
+  //    Es wird erst einer Boersen "abgebucht" (falls vorhanden) und dann
+  //    (falls noch noetig) von einer Geldkarte. Ist der Betrag am Ende
+  //    negativ, wird ein Fehler ausgeloest.
+  if (!living(dest))
+    return;
+  // wenn wir aus einer Boerse in diesem Environment kommen, vereinigen wir
+  // uns nicht wieder erneut.
+  if (load_name(oldenv) == BOERSE
+      && environment(oldenv) == environment())
+    return;
+
+  int amount = QueryProp(P_AMOUNT);
+  ZDEBUG(sprintf("NotifyMove 1: amount: %d\n",
+         amount));
+  if (amount > 0)
+  {
+    object boerse = present(GELDBOERSE_MIT_GELD, environment())
+                    || present(BOERSEID, environment());
+    if (boerse)
+    {
+      boerse->MergeMoney(this_object());
+      // Sollten wir jetzt P_AMOUNT == 0 haben, zerstoert uns der Code aus
+      // unit.c spaeter.
+      ZDEBUG(sprintf("NotifyMove nach Boersenvereinigung 1: amount: %d\n",
+             QueryProp(P_AMOUNT)));
+    }
+  }
+  else if (amount < 0)
+  {
+    object boerse = present(GELDBOERSE_MIT_GELD, environment());
+    if (boerse)
+    {
+      boerse->MergeMoney(this_object());
+      ZDEBUG(sprintf("NotifyMove nach Boersenvereinigung 2: amount: %d\n",
+             QueryProp(P_AMOUNT)));
+    }
+    // wenn immer noch was auszugleichen ist, Geldkarte versuchen.
+    if (QueryProp(P_AMOUNT) < 0)
+    {
+      object geldkarte = present(SEHERKARTEID_AKTIV, environment());
+      if (geldkarte)
+      {
+        geldkarte->MergeMoney(this_object());
+        ZDEBUG(sprintf("NotifyMove nach Kartenvereinigung: amount: %d\n",
+               QueryProp(P_AMOUNT)));
+      }
+    }
+    if (QueryProp(P_AMOUNT) < 0)
+      catch(raise_error(sprintf(
+            "Geld mit negativem Betrag immer noch negativ nach "
+            "Vereinigung mit allen Geldquellen im Inventar: %O. Rest: %d\n",
+            environment(), QueryProp(P_AMOUNT))); publish);
+  }
+}
+
+int _set_autoloadobj( mixed args )
+{
+  if (pointerp(args))
+    args=args[0];
+  if (stringp(args))
+    sscanf(args,"%d",args);
+  if (!intp(args))
+    args=0;
+  return SetProp(P_AMOUNT,args);
+}
+
+int _query_autoloadobj()
+{
+  return (int)Query(P_AMOUNT, F_VALUE);
+}
+
+string current_long() {
+ int r;
+ r=Query(U_REQ);
+ return(break_string(
+  ((r==1)?"":"Du betrachtest eine der Muenzen genauer:\n")+
+  "Die Oberflaeche der Goldmuenze ist vom taeglichen Gebrauch ziemlich "
+  "mitgenommen. Kratzer und sogar Bissspuren zieren die Praegemuenze "
+  "zusaetzlich zum Konterfei "+
+  ({"des Jof","der Rumata","des Zook"})[(Query(P_AMOUNT)+r)%3]+
+  ". Der Rand "+
+  ((!(r%2))?"scheint etwas beschaedigt zu sein.":
+            "ist wenigstens unbeschaedigt."),
+  78,0,BS_LEAVE_MY_LFS));
+}
+
+static int action_schnipps(string str) {
+ if(!id(str)) return 0;
+ write(break_string(
+  ((Query(U_REQ)==1)?"Du nimmst die Muenze":
+                     "Du nimmst eine der Muenzen")+
+  " und schnippst sie in die Luft. Sie klingt hell auf. "+
+  ((this_player() && this_player()->QueryAttribute(A_DEX)>10)?
+   "Geschickt faengst Du sie wieder auf.":
+   "Sie plumpst Dir wieder in die Hand und beinahe laesst Du "
+   "sie fallen."),78));
+ if(this_player() && environment(this_player()))
+  tell_room(environment(this_player()),
+            break_string(this_player()->Name()+" schnippt mit hellem "
+                         "Klingen ein Muenze in die Luft und faengt sie "
+                         "wieder.",78),
+            ({this_player()}));
+ return 1;
+}
+
+static int action_pruefe(string str) {
+ if(!id(str)) return 0;
+ write(break_string(
+  ((Query(U_REQ)==1)?"Du nimmst die Muenze":
+                     "Du nimmst eine der Muenzen")+
+  " und beisst vorsichtig hinein. Dein Zahn versinkt tief im reinen Gold, "
+  "und Du bist Dir nun sicher, dass diese Muenze auch wirklich echt ist.",
+  78));
+ if(this_player() && environment(this_player()))
+  tell_room(environment(this_player()),
+            break_string(this_player()->Name()+" beisst in eine Muenze "
+                         "und schaut irgendwie befriedigt drein.",78),
+            ({this_player()}));
+ return 1;
+}
+
+
+static string DetKonterfei() {
+ string m;
+ switch((Query(P_AMOUNT)+Query(U_REQ))%3) {
+  default:
+  case 0:
+        m="Jofs rundes Gesicht grinst Dich vertrauenserweckend und "
+          "freundlich an.";break;
+  case 1:
+        m="Rumata scheint Dir zuzublinzeln.";break;
+  case 2:
+        m="Zooks Gesicht schaut ziemlich frisch gepraegt drein. Wenn "
+          "eine der Muenzen beim Schnippen klingt, dann bestimmt die.";break;
+ }
+ return(break_string(m,78));
+}
+
+
+static string DetRand() {
+ return(break_string(
+  ((!(Query(U_REQ)%2))?
+            "Der Rand der Muenze ist zerklueftet und abgeschabt. Ob "
+            "da jemand versucht hat, das Gold der Muenze abzuschaben, "
+            "um sich etwas nebenher zu verdienen?":
+            "Der Rand ist vollkommen in Ordnung, ein wirklich frische "
+            "Praegung."),78));
+}
+
+
+static int action_wurf(string str)
+{
+  int num, part;
+
+  if (!id(str))
+    return 0;
+
+  num = Query(U_REQ);
+  write (break_string(
+	 ((num==1)?"Du nimmst die Muenze":
+	  sprintf("Du nimmst %d Muenzen", num))+
+	   ", wirfst sie hoch, laesst sie zu Boden prasseln und "
+	   "betrachtest das Ergebnis:", 78));
+  if (this_player() && environment(this_player()))
+    tell_room(environment(this_player()),
+	      break_string(this_player()->Name()+" wirft "+
+         	   ((num==1)?"eine Muenze":
+			   sprintf("%d Muenzen", num))+
+		   " hoch, laesst sie zu Boden prasseln und betrachtet "
+		   +"das Ergebnis:", 78), ({this_player()}));
+
+  if (num == 1) {
+    if (random(1000000) == 123456) {
+      write("   Die Muenze bleibt auf dem Rand stehen.\n");
+      if (this_player() && environment(this_player()))
+	tell_room(environment(this_player()),
+		  "   Die Muenze bleibt auf dem Rand stehen.\n",
+		  ({this_player()}));
+    }
+    else if (random(100) < 50) {
+      write("   Kopf.\n");
+      if (this_player() && environment(this_player()))
+	tell_room(environment(this_player()),
+		  "   Kopf.\n",
+		  ({this_player()}));
+    }
+    else {
+      write("   Zahl.\n");
+      if (this_player() && environment(this_player()))
+	tell_room(environment(this_player()),
+		  "   Zahl.\n",
+		  ({this_player()}));
+    }
+  }
+  else {
+    part = 5*num + random(num+1) - random(num+1) // etwas Gauss-verteilen
+		 + random(num+1) - random(num+1)
+		 + random(num+1) - random(num+1)
+		 + random(num+1) - random(num+1)
+		 + random(num+1) - random(num+1);
+    part = (part+5)/10;
+
+    if (part) {
+      printf("   %dx Kopf", part);
+      if (part != num)
+	printf(" und %dx Zahl", num-part);
+      if(this_player() && environment(this_player()))
+	tell_room(environment(this_player()),
+		  sprintf("   %dx Kopf", part)+
+		  ((part!=num)?sprintf(" und %dx Zahl", num-part):"")+
+		  ".\n",
+		  ({this_player()}));
+    }
+    else {
+      printf("   %dx Zahl", num);
+      if (this_player() && environment(this_player()))
+	tell_room(environment(this_player()),
+		  sprintf("   %dx Zahl.\n", num),
+		  ({this_player()}));
+    }
+    write (".\n");
+  }
+
+  write(break_string(
+	"Schnell sammelst Du "+
+	((num==1)?"Deine Muenze":"Deine Muenzen")+" wieder ein.", 78));
+  if (this_player() && environment(this_player()))
+    tell_room(environment(this_player()),
+	      break_string(this_player()->Name()+ " sammelt schnell "+
+	      ((num==1)?(this_player()->QueryPossPronoun(FEMALE,WEN, SINGULAR)
+			 +" Muenze wieder ein."):
+	       (this_player()->QueryPossPronoun(FEMALE,WEN, PLURAL)+
+		" Muenzen wieder ein."))), ({this_player()}));
+  return 1;
+}
+
+protected void create()
+{
+  mixed plist, i;
+
+  ::create();
+  SetProp(P_NAME,({"Muenze","Muenzen"}));
+  Set(P_LONG,#'current_long);
+  SetProp(P_SHORT,"Geld");
+  SetProp(P_INFO,"Du hast bei diesem Geld ein sicheres Gefuehl.\n");
+  SetProp(P_MATERIAL,([MAT_GOLD:100]));
+  AddSingularId("muenze");
+  AddPluralId("muenzen");
+  AddSingularId("goldstueck");
+  AddPluralId("goldstuecke");
+  AddId(({"geld", GELDID}));
+  SetProp(P_GENDER,FEMALE);
+  SetCoinsPerUnits(1,1);
+  SetGramsPerUnits(1,4);
+
+  if (!clonep(this_object())) {
+    set_next_reset(-1);
+    return;
+  }
+  AddDetail(({"rand","muenzrand"}),#'DetRand);
+  AddDetail(({"oberflaeche","flaeche"}),break_string(
+   "Diese Muenze ist schon durch viele Haende gegangen. Die Kratzer "
+   "zeugen davon, dass sie nicht selten allein getragen wurde und die "
+   "Bissspuren von mangelndem Vertrauen.",78));
+  AddDetail("kratzer",break_string(
+   "Das Konterfei ist schon ganz schoen zerkratzt.",78));
+  AddDetail(({"bissspur","bissspuren"}),break_string(
+   "Da hat doch tatsaechlich jemand so wenig Vertrauen in das Gold "
+   "gezeigt, dass er hineingebissen hat, um es zu pruefen.",78));
+  AddDetail(({"bild","konterfei","praegung"}),#'DetKonterfei);
+  AddDetail("gold","Es glaenzt Dich verheissungsvoll an.\n");
+
+  AddCmd(({"pruef","pruefe","beiss","beisse","test","teste"}),
+         #'action_pruefe);
+  AddCmd(({"schnipp","schnippe","schnipps","schnippse"}),
+         #'action_schnipps);
+
+  AddCmd( ({ "wirf", "werf", "werfe" }), #'action_wurf);
+}
+
+varargs string GetDetail(mixed key,mixed race,int sense) {
+ string m;
+ if(stringp(m=::GetDetail(&key,&race,&sense)) && sizeof(m)) {
+  int r;
+  r=Query(U_REQ);
+  if(r!=1) m=sprintf("Du betrachtest eine der %d Muenzen genauer:\n",r)+m;
+ }
+ return m;
+}
+
diff --git a/items/pfeifchen.c b/items/pfeifchen.c
new file mode 100644
index 0000000..c0d8976
--- /dev/null
+++ b/items/pfeifchen.c
@@ -0,0 +1,149 @@
+inherit "/std/thing";
+
+#include <properties.h>
+#include <language.h>
+
+int tm;
+int gestopft = 0;
+int rauchen  = 0;
+
+string des;
+
+string _query_long(){
+  string s;
+  s="Ein einfaches Holzpfeifchen: Ohne Verzierungen oder besondere "
+    "Extras, sondern schlicht und stabil genug fuer ein Abenteurerleben. "
+    "Es eignet sich hervorragend dazu, einmal zu entspannen und guten "
+    "Tabak zu geniessen";
+  if(gestopft) s+=". Das Pfeifchen ist gestopft";
+  if(rauchen)  s+=" und angezuendet.";
+  else         s+=".";
+  return(break_string(s,78));
+}
+
+string _query_short(){
+  if(rauchen)  return "Ein Pfeifchen (angezuendet)";
+  if(gestopft) return "Ein Pfeifchen (gestopft)";
+  else         return "Ein Pfeifchen";
+}
+
+protected void create(){
+  if(!clonep(this_object()))return ;
+  thing::create();
+  tm=0;
+  SetProp(P_GENDER,NEUTER);
+  SetProp(P_NAME,"Pfeifchen");
+  SetProp(P_WEIGHT,10);
+  SetProp(P_VALUE,0);
+  AddId(({"pfeifchen","holzpfeifchen"}));
+  SetProp(P_NODROP,1);
+  SetProp(P_NEVERDROP,1);
+  SetProp(P_MATERIAL,MAT_IRON_WOOD);
+  AddCmd(({"rauch","rauche"}),"cm_smoke");
+  AddCmd(({"stopf","stopfe"}),"cm_stopf");
+}
+
+static int cm_stopf(string str){
+  string s1,s2;
+  object ob;
+  _notify_fail("Syntax: stopfe pfeifchen mit <was>\n");
+  if(!str) return 0;
+  if(sscanf(str,"%s mit %s",s1,s2)==2){
+    if(id(s1)){
+      if(!ob=present(s2,this_player())){
+        write("So etwas hast Du doch gar nicht bei Dir.\n");
+        return 1;
+       }
+      if((ob->id("tabak")) || (ob->QueryMaterial(MAT_CANNABIS)>50)){
+        if(rauchen){
+          write(break_string("Du rauchst Dein Pfeifchen doch gerade. Damit "
+           "musst Du schon warten, bis Du fertig bist.",78));
+          return 1;
+         }
+        if(gestopft){
+          write(break_string("Du hast Dein Pfeifchen bereits gestopft. Es "
+           "liesse sich nun vortrefflich rauchen.",78));
+          return 1;
+         }
+        else{
+          write(break_string("Du stopfst Dein Pfeifchen mit "+
+           ob->name(WEM,1)+
+           " und freust Dich schon darauf, es zu rauchen.",78));
+          tell_room(environment(this_player()),this_player()->Name()+
+           " stopft ein Pfeifchen.\n",({this_player()}));
+          if(!ob->GetSmokeDescription()) des="Es scheint sich um keinen "
+           "besonders guten Tabak zu handeln. Nun, ja. Besser als nichts "
+           "immerhin.";
+          else des=ob->GetSmokeDescription();
+          if(!ob->QueryProp(P_AMOUNT))
+            ob->remove();
+          else
+            ob->AddAmount(-1);
+          gestopft = 1;
+          return 1;
+         }
+       }
+      write(break_string("Das laesst sich aber schlecht rauchen, such "
+       "Dir lieber einen ordentlichen Tabak.",78));
+      return 1;
+     }
+   }
+  return(0); //non-void Funktion, Zesstra
+}
+
+static int cm_smoke(string str){
+  object ob;
+
+  _notify_fail("Was moechtest Du rauchen?\n");
+  if(!str) return 0;
+  if(!id(str)) return 0;
+  if(rauchen){
+    write("Das machst Du doch bereits, nicht so hektisch.\n");
+    return 1;
+   }
+  if(!gestopft){
+    write("Vielleicht solltest Du Dein Pfeifchen erst mal stopfen?\n");
+    return 1;
+   }
+  if(tm > time()){
+    write("Nana, ein Pfeifchen geniesst man. Sei mal nicht so hektisch.\n");
+    return 1;
+   }
+  tm = time()+20;
+  rauchen = 1;
+  tell_room(environment(this_player()),this_player()->Name()+" raucht in "
+   +"Ruhe ein Pfeifchen.\n",({this_player()}));
+  write("Du rauchst in Ruhe Dein Pfeifchen.\n");
+  write(break_string(des,78));
+  if(find_call_out("smoke2")==-1)
+    call_out("smoke2",20,getuid(this_player()));
+  return 1;
+}
+
+int smoke2(object pl){
+  object ob;
+  if(!rauchen) return 0;
+  if(!ob=find_player(pl)) return 0;
+  if(environment()!=ob) return 0;
+  tell_object(ob,"Genuesslich pustest Du ein paar Rauchkringel in die Luft.\n");
+  tell_room(environment(ob),ob->Name()+" pustet genuesslich ein paar "
+   +"Rauchkringel in die Luft.\n",({ob}));
+  if(find_call_out("smoke3")==-1)
+    call_out("smoke3",20,getuid(environment()));
+  return 1;
+}
+
+int smoke3(object pl){
+  object ob;
+  if(!rauchen) return 0;
+  if(!ob=find_player(pl)) return 0;
+  if(environment()!=ob) return 0;
+  tell_object(ob,break_string("Genuesslich pustest Du ein paar Rauchkringel "
+   "in die Luft, rauchst Dein Pfeifchen auf und klopfst es aus.",78));
+  tell_room(environment(ob),break_string(ob->Name()+" pustet genuesslich ein "
+   "paar Rauchkringel in die Luft, beendet das Rauchpaeuschen und klopft "
+   +(ob->QueryProp(P_GENDER)==1?"sein":"ihr")+" Pfeifchen aus.",78),({ob}));
+  rauchen = 0;
+  gestopft = 0;
+  return 1;
+}
diff --git a/items/schaufel.c b/items/schaufel.c
new file mode 100644
index 0000000..bd4940b
--- /dev/null
+++ b/items/schaufel.c
@@ -0,0 +1,26 @@
+#pragma strong_types,rtt_checks
+
+inherit "/std/thing";
+
+#include <properties.h>
+#include <language.h>
+
+void create()
+{
+  ::create();
+  SetProp(P_SHORT,"Eine Schaufel");
+  SetProp(P_LONG,"Du kannst versuchen, mit dieser Schaufel zu graben.\n");
+  AddId("schaufel");
+  AddId("\nschaufel");
+  SetProp(P_NAME, "Schaufel");
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_VALUE, 50);
+  SetProp(P_WEIGHT, 1200);
+	AddCmd( ({"grab","grabe","buddel","buddele"}), "grab" );
+}
+
+int grab(string str) 
+{
+  _notify_fail("Du kannst hier nicht graben.\n");
+  return 0;
+}
diff --git a/items/seercard.c b/items/seercard.c
new file mode 100644
index 0000000..a899e9f
--- /dev/null
+++ b/items/seercard.c
@@ -0,0 +1,359 @@
+// MorgenGrauen MUDlib
+//
+// obj/seercard.c -- bargeldloser Zahlungsverkehr auf Guthabenbasis
+//
+
+#pragma strict_types
+
+inherit "/std/thing";
+
+#include <defines.h>
+#include <properties.h>
+#include <language.h>
+#include <unit.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <money.h>
+
+#define KONTO "seercard_kontostand"
+#define BESITZER_GUELTIG \
+    (environment() && query_once_interactive(environment()) && \
+    getuid(environment()) == owner)
+
+// zum debuggen und extra public
+public string debugmode;
+public string __set_debug(string recv) {return debugmode=recv;}
+#include <living/comm.h>
+#define ZDEBUG(x) if (stringp(debugmode) && find_player(debugmode)) \
+  find_player(debugmode)->ReceiveMsg(x,MT_DEBUG,0,object_name()+": ",ME)
+//#define ZDEBUG(x)
+
+private string owner;
+
+static int _query_amount()
+{
+    int res;
+
+    // Wenn die Karte im Inv des Besitzers ist, Kontostand liefern
+    if ( BESITZER_GUELTIG && IS_SEER(environment()) ){
+        res = (int) environment()->Query(KONTO);
+
+        // ... allerdings nur, wenn er gueltig ist ;-)
+        if ( !intp(res) || res < 0 || res > 100000 )
+            return environment()->Set( KONTO, 0 ), 0;
+        else
+            return res;
+    }
+
+    // Fuer Fremde ist die Karte wertlos (bis auf ihren Materialwert)
+    return 0;
+}
+
+static string _feld()
+{
+    if ( !BESITZER_GUELTIG )
+        return break_string( "Das Feld schimmert aus jeder Richtung anders - "
+                             "aber mehr kannst Du nicht erkennen.", 78 );
+
+    return "Du kannst in dem Schimmern ein paar Ziffern erkennen.\n";
+}
+
+
+static string _bild()
+{
+    if ( this_player() && getuid(this_player()) == owner )
+        return break_string( "Du findest, dass das Bild ganz schlecht "
+                             "getroffen ist. Dass man selber aber auch immer "
+                             "so unvorteilhaft abgebildet wird!", 78 );
+
+    return break_string( "Du findest, dass " + capitalize(owner) + " sehr gut "
+                         "getroffen ist. Anscheinend wirst nur Du immer so "
+                         "unvorteilhaft abgebildet.", 78 );
+}
+
+// Ueber das Argument <silent> kann nun gesteuert werden, ob man die 
+// zusaetzliche Meldung ausgeben will oder nicht. Dies ist jetzt nur beim
+// Untersuchen der Karte der Fall, der Befehl "guthaben" gibt wie frueher
+// nur das Guthaben aus.
+private string _guthaben(int silent)
+{
+    int i;
+
+    if ( !BESITZER_GUELTIG )
+        return "Du kannst im Schimmern nichts erkennen.\n";
+
+    if ( !(i = _query_amount()) )
+        return "Welches Guthaben? :-)\n";
+
+    string msg =  
+      "Dein Guthaben betraegt " + ((i > 1) ? to_string(i) : "eine") +
+      " Muenze" + ((i > 1) ? "n" : "") + ".\n";
+    if ( !silent ) 
+      msg += 
+        "Du erinnerst Dich an die Worte des Schalterbeamten, dass Deine "
+        "Seer-Card (TM) immer erst dann belastet wird, wenn Du Dein Bargeld "
+        "komplett verpulvert hast.";
+    return break_string(msg, 78, 0, BS_LEAVE_MY_LFS);
+}
+
+// Wrapper fuer das Detail "guthaben", um das neue Argument angeben zu 
+// koennen
+static string _guthaben_det(string key) {
+  return _guthaben(0);
+}
+
+protected void create()
+{
+    if ( !clonep(ME) ){
+        set_next_reset(-1);
+        return;
+    }
+
+    ::create();
+
+    SetProp( P_LOG_FILE, "report/tiamak.rep" );
+    SetProp( P_NAME, "Geldkarte" );
+    SetProp( P_INFO,
+             break_string( "So sehr Du Dich auch anstrengst, Du kommst "
+                           "einfach nicht hinter das Geheimnis dieser "
+                           "Karte. Du weisst nur eines mit Sicherheit: "
+                           "es muss etwas mit Magie zu tun haben.", 78 ) );
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_MATERIAL, ([ MAT_PEAR_WOOD: 80, MAT_MISC_MAGIC: 20 ]) );
+    SetProp( P_WEIGHT, 50 );
+    SetProp( P_VALUE, 1000 );
+    SetProp( P_NOBUY, 1 );
+
+    AddSounds( SENSE_DEFAULT,
+               break_string( "Du kannst nichts hoeren. Es sind anscheinend "
+                             "auch keine losen Teile vorhanden.", 78 ) );
+
+    AddSmells( SENSE_DEFAULT, "Auch bargeldlose Zahlungsmittel stinken "
+               "nicht. ;-)\n" );
+
+    AddDetail( "sicherheit", "So etwas bietet diese Karte nicht!\n" );
+    AddDetail( "geheimnis", "Es lautet \"guthaben\". Aber verrate es "
+               "nicht weiter!\nAngeblich kannst Du das Guthaben sogar direkt "
+               "wieder abheben.\n" );
+    AddDetail( "magie", "Wenn man sie so einfach untersuchen koennte, "
+               "waere sie nicht halb so\ngeheimnisvoll.\n" );
+    AddDetail( "rechteck",
+               break_string( "An dieser Karte ist alles rechteckig: das Bild, "
+                             "das merkwuerdige Feld und die Karte selber.",
+               78) );
+    AddDetail( ({ "holz", "birnbaumholz" }), "Es handelt sich um das "
+               "mindestens genauso seltene wie magische Birnbaumholz.\n" );
+    AddDetail( ({ "schriftzug", "schrift" }), "Nur wo \"Seer-Card\" drauf "
+               "steht, ist auch wirklich eine Seer-Card drin.\n" );
+    AddDetail( ({ "buchstabe", "buchstaben" }), "Sie bilden den "
+               "unvermeidlichen Schriftzug \"Seer-Card (TM)\".\n" );
+    AddDetail( "drittel", "Na unten halt.\n" );
+    AddDetail( ({ "seite", "seiten" }), "Eine von beiden.\n" );
+    AddDetail( ({ "rechte seite", "rechts" }), "Das ist die eine.\n" );
+    AddDetail( ({ "linke seite", "links" }), "Das ist die andere.\n" );
+    AddDetail( "rueckseite", "Die Rueckseite ist total leer und "
+               "langweilig.\n" );
+    AddDetail( ({ "zahl", "zahlen", "ziffern", "ziffer" }), "Vielleicht sollen "
+               "sie Dein Guthaben darstellen?\n" );
+    AddDetail( ({ "feld", "schimmern", "richtung" }), #'_feld/*'*/ );
+    AddDetail( "bild", #'_bild/*'*/ );
+    AddDetail( "guthaben", #'_guthaben_det/*'*/ );
+    AddDetail( ({ "muenze", "muenzen" }), "Tipp mal \"guthaben\" ein.\n" );
+
+    AddCmd( "guthaben", "_kontostand" );
+    AddCmd( ({ "heb", "hebe" }), "_abheben" );
+
+    AddId( ({ SEHERKARTEID, "geldkarte", "seercard", "seer-card",
+                  "karte", "card" }) );
+}
+
+
+static int _kontostand( string str )
+{
+    if ( !this_player() || environment(this_object()) != this_player() )
+        return 0;
+
+    if ( !str || str == "" ){
+        write( _guthaben(1) );
+        return 1;
+    }
+
+    return 0;
+}
+
+
+// Abfrage des Kartenbesitzers von aussen
+public string query_owner()
+{
+    return owner;
+}
+
+
+// Lokale Property-Methoden
+
+static string _query_long()
+{
+    string str;
+
+    str = "Eigentlich ist die Seer-Card ziemlich schlicht gehalten. Sie ist "
+        "als ein etwa daumendickes Rechteck aus dunklem Holz geformt. "
+        "Auf der rechten Seite ist ein Bild von " +
+        ((this_player() && getuid(this_player()) == owner) ? "Dir" :
+         capitalize(owner)) + " aufgemalt. Links "
+        "daneben befindet sich ein schmucker Schriftzug in grossen silbernen "
+        "Buchstaben. Im unteren Drittel der Karte befindet sich ein dezent "
+        "schimmerndes rechteckiges Feld.";
+
+    return break_string( str, 78, 0, BS_LEAVE_MY_LFS );
+}
+
+
+static string _query_short()
+{
+    if ( !owner || !sizeof(owner) )
+        return "Eine Geldkarte";
+
+    if ( this_player() && getuid(this_player()) == owner )
+        return "Deine Seer-Card (TM)";
+
+    return "Die Geldkarte von " + capitalize(owner);
+}
+
+
+static string _query_autoloadobj()
+{
+    // Die Karte ist nur fuer den Eigentuemer autoload - auch, wenn sie in
+    // einem Paket o.ae. steckt
+    foreach (object env: all_environment())
+    {
+      if (getuid(env) == owner)
+          return owner;
+    }
+    return 0;
+}
+
+
+static string _set_autoloadobj( string wert )
+{
+    // Darf nach Personalisierung nicht mehr geaendert werden.
+    if ( !owner && stringp(wert) )
+    {
+
+        if ( member( ({ 'x', 'z', 's' }), wert[<1] ) != -1 ){
+            AddAdjective( wert+"'" );
+            AddAdjective( wert );
+        }
+
+        if ( wert[<1] != 's' )
+            AddAdjective( wert+"s" );
+
+        return owner = wert;
+    }
+    return 0;
+}
+
+
+static string _query_keep_on_sell()
+{
+    // Der Besitzer der Karte 'behaelt' sie automatisch
+    // Teil 2 der Abfrage, falls die Karte in einem Paket o.ae. steckt
+    if ( BESITZER_GUELTIG || this_player() && getuid(this_player()) == owner )
+        return owner;
+
+    // Bei den anderen je nach Wunsch
+    return Query(P_KEEP_ON_SELL);
+}
+
+
+// Man kann aus dem Guthaben auch wieder klingende Muenzen machen.
+// Wenn es denn sein muss.
+static int _abheben( string str )
+{
+    int summe, guthaben;
+    string dummy;
+    
+    if ( !this_player() || environment(this_object()) != this_player() )
+        return 0;
+
+    if ( str == "1 muenze ab" )
+        summe = 1;
+    else
+        if ( !stringp(str) ||
+             sscanf( str, "%d muenzen ab%s", summe, dummy ) != 2 ||
+             summe < 1 || dummy != "" ){
+            _notify_fail( "Wieviele Muenzen moechtest Du denn abheben?\n" );
+            return 0;
+        }
+
+    if ( (guthaben = _query_amount()) < summe ){
+        _notify_fail( "So hoch ist Dein Guthaben aber nicht!\n" );
+        return 0;
+    }
+
+    if ( (int) this_player()->AddMoney(summe) != 1 ){
+        write( "Soviel Geld koenntest Du gar nicht mehr tragen.\n" );
+        return 1;
+    }
+
+    environment()->Set( KONTO, guthaben - summe );
+
+    write( break_string( "Du schuettelst Deine Seer-Card (TM) vorsichtig "
+                         "hin und her und murmelst ein paar geheime Worte.\n"
+                         "Ploetzlich macht es *PLOPP* und Du haeltst Geld "
+                         "in den Haenden!", 78, 0, BS_LEAVE_MY_LFS ) );
+    return 1;
+}
+
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+    ::NotifyMove(dest,oldenv,method);
+
+    // Besitzer der Karte setzen, falls noch keiner existiert
+    if ( !owner && environment() &&
+         query_once_interactive(environment()) )
+    {
+         owner = getuid(environment());
+
+        if ( member( ({ 'x', 'z', 's' }), owner[<1] ) != -1 ){
+            AddAdjective( owner );
+            AddAdjective( owner+"'" );
+        }
+        if ( owner[<1] != 's' )
+            AddAdjective( owner+"s" );
+    }
+}
+
+varargs int id(string str, int lvl)
+{
+  if (str==SEHERKARTEID_AKTIV
+      && BESITZER_GUELTIG)
+      return 1;
+  return ::id(str, lvl);
+}
+
+public void MergeMoney(object geld)
+{
+  if (geld && previous_object() == geld
+      && load_name(geld) == GELD
+      && BESITZER_GUELTIG)
+  {
+    int fremdamount = geld->QueryProp(P_AMOUNT);
+    int meinamount = _query_amount();
+    ZDEBUG(sprintf("MergeMoney: meinamount: %d, fremdamount: %d\n",
+          meinamount,fremdamount));
+    if (meinamount > 0 && fremdamount < 0)
+    {
+      int amount_to_merge = min(meinamount, abs(fremdamount));
+      environment()->Set( KONTO, meinamount - amount_to_merge, F_VALUE );
+      geld->SetProp(P_AMOUNT, fremdamount + amount_to_merge);
+    }
+    ZDEBUG(sprintf("MergeMoney: Final: meinamount: %d, fremdamount: %d\n",
+          _query_amount(),geld->QueryProp(P_AMOUNT)));
+  }
+}
+
+// Fuer die Zuordnung durch das Hoerrohr etc.
+public string GetOwner()
+{
+    return "Tiamak";
+}
diff --git a/items/seil.c b/items/seil.c
new file mode 100644
index 0000000..c5cf079
--- /dev/null
+++ b/items/seil.c
@@ -0,0 +1,286 @@
+#include <properties.h>
+#include <language.h>
+#include <seil.h>
+inherit "/std/thing";
+
+#pragma strict_types,rtt_checks
+
+object tied_to_ob;
+string tied_name;
+
+void create(){
+  ::create();
+
+  AddId( ({"seil", "\nseil"}) );
+  SetProp(P_NAME, "Seil");
+
+  SetProp(P_GENDER, NEUTER);
+  SetProp(P_LONG,break_string("Du siehst ein ganz normales Seil. Du kannst es an "+
+                  "Gegenstaenden festbinden und wieder loesen. Das Seil besteht "+
+                  "aus solider fester Steinbeisserwolle.",78));
+  AddDetail(({"wolle","steinbeisserwolle"}),"Diese Wolle ist eine besonders feste und stabile Wolle.\n");
+  SetProp(P_VALUE, 15);
+  SetProp(P_TIE_AUTO,1);
+  SetProp(P_WEIGHT, 300);
+
+  SetProp(P_MATERIAL,({MAT_MISC_STONE,MAT_WOOL}));
+  AddCmd( ({"binde","bind","knote","befestige","mach","mache"}), "tie" );
+
+  // der Befehle loesen wird auf die anderen Befehle umgebogen und dient nur
+  // der besseren benutzbarkeit des seiles.
+  AddCmd( ({"loese" }), "loesen" );
+
+}
+
+// dieses Ding wird u.a. geerbt. Damit die BP beim Erben auch initialisiert
+// wird (fuer die Laeden), muss create_super() das create() rufen.
+protected void create_super() {
+  create();
+}
+
+string _query_short()
+{
+ string artikel;
+ switch( QueryProp(P_GENDER) )
+ {
+  case NEUTER : artikel="das";break;
+  case MALE   : artikel="der";break;
+  case FEMALE :	artikel="die";break;
+  default     : artikel="das";
+ }
+
+ if (tied_to_ob)
+  return this_object()->Name(WER)+", "+artikel+" an "
+     + (tied_name?tied_name:(string)(tied_to_ob->name(WEM)))
+     + " festgebunden ist";
+ return (string)this_object()->Name(WER);
+
+}
+
+mixed _query_noget()
+{
+ if (tied_to_ob)
+  return "Das geht nicht, solange "+name(WER)+" festgebunden ist.\n";
+ return 0;
+}
+
+int tie(string str)
+{
+  string t1, t2;
+  object ob;
+  string verb;
+  mapping tied_map;
+
+  _notify_fail("Binde was?\n");
+
+  // zunaechst den User notieren, damit spaeter immer drauf 
+  // zugegriffen werden kann.
+  // Wegen kompatibilitaet wird das nicht direkt an tie()/untie()
+  // uebergeben.
+  SetProp(P_TIE_USER,this_player());
+
+  if (!str||!stringp(str)) return 0;
+
+  verb = query_verb();
+  
+  //automatischer aus objecten/raeumen generierter aufruf
+  if(QueryProp(P_TIE_VERB)) verb=QueryProp(P_TIE_VERB); 
+  	
+  if (sscanf(str, "%s an %s", t1, t2) != 2 && 
+      sscanf(str,"%s los",t1) != 1 &&
+      sscanf(str,"%s fest",t1) != 1 )
+   return 0;
+
+  if(id(t1) && str==(t1+" fest")) str="seil an raum";
+
+  if(sscanf(str,"%s an %s",t1,t2)==2)
+  {
+   if(tied_to_ob)
+   {
+    write("Es ist bereits irgendwo festgebunden.\n");
+    return 1;
+   }
+
+   if (!id(t1)) return 0;
+
+   if (t2 == "mich")
+   {
+    _notify_fail("Warum willst Du das denn machen?\n");
+    return 0;
+   }
+
+   ob = present(t2, this_player());		
+
+   if (!ob) ob = present(t2, environment(this_player()));
+
+   if (!ob)
+   {
+    if (call_other(environment(this_player()), "id", t2))
+    ob = environment(this_player());
+   }
+		
+   if(!ob && str == "seil an raum")
+   {
+    ob=environment(this_player());
+   }
+
+   _notify_fail( "Soetwas findest Du hier nicht.\n" );
+   if (!ob) return 0;
+
+   if (!(tied_name=(string)call_other(ob, "tie", t2)))
+   {
+    if(ob->QueryProp(P_INVIS)) return 0;
+    
+    if (ob != environment(this_player()))
+    {
+     _notify_fail("Du kannst "+name(WER)+" nicht an "+ob->name(WEM)+
+                 " festbinden.\n");
+     return 0;
+    }
+    else
+    {
+     _notify_fail("Du kannst "+name(WER)+" nicht daran festbinden.\n");
+     return 0;
+    }
+
+   }
+		
+  /* Is he carrying the rope ? */
+  if (environment() == this_player()) 
+  {
+   this_object()->move(environment(this_player()));
+  }
+
+  tied_to_ob = ob;
+
+  if (tied_to_ob != environment(this_player()) || 
+      environment(this_player())->name()!=0 )
+   {
+    tied_name = (string)(tied_to_ob->name(WEM));
+   }
+  else
+   {
+    if( !stringp(tied_name) ) tied_name="etwas";
+   }
+
+  if(QueryProp(P_TIE_VERB))
+  {
+    // das seil wird gesteuert
+    tell_room(environment(),QueryProp(P_TIE_MESSAGE));
+
+    // seil wieder in roh-zustand versetzen.
+    SetProp(P_UNTIE_MESSAGE,0);
+    SetProp(P_TIE_VERB,0);
+  }
+  else
+  {
+   write("Du bindest "+name(WER)+" an " + tied_name + " fest.\n");
+   say(this_player()->name(WER) + " bindet "+name(WER)+" an "
+   + tied_name + " fest.\n");
+  }
+
+  // den object mitteilen, an wen es gebunden ist.
+  if(tied_to_ob->QueryProp(P_TIED)==0) tied_to_ob->SetProp(P_TIED,([]) );
+  tied_to_ob->SetProp(P_TIED,tied_to_ob->QueryProp(P_TIED)+([this_object(): 
+     ([
+       "player":this_player(),
+       "time"  :time()
+     ]) ]));
+
+  return 1;
+ }
+
+ if( (member( ({
+         "binde",
+         "bind",
+         "knote",
+         "mach",
+         "mache",
+         "loese" 
+         }),verb                 
+    )!=-1) && 
+    sscanf(str,"%s los",t1)==1)
+  {
+   if (!tied_to_ob)
+   {
+    write(Name(WER)+" ist nirgendwo festgebunden.\n");
+    return 1;
+   }
+
+   if (!call_other(tied_to_ob, "untie"))
+   {
+    write("Das klappt leider nicht.\n");
+    return 1;
+   }
+
+   if(QueryProp(P_TIE_VERB))
+   {
+    // das seil wird gesteuert
+    tell_room(environment(),QueryProp(P_UNTIE_MESSAGE));
+
+    // wieder in roh-zustand versetzen.
+    SetProp(P_UNTIE_MESSAGE,0);
+    SetProp(P_TIE_VERB,0);
+   }
+   else
+   {
+    write("Du bindest "+name(WER)+" los.\n");
+    say(this_player()->name()+" bindet "+name(WER)+" los.\n");
+   }
+
+  tied_map=([])+tied_to_ob->QueryProp(P_TIED);
+  tied_map=m_copy_delete(tied_map,this_object());
+
+  tied_to_ob->SetProp(P_TIED,tied_map);
+
+  tied_to_ob = (object) 0;
+ 
+  return 1;
+ }
+ return 0;
+}
+
+int loesen(string str)
+{
+ if(str == "seil"  ||
+    str == lower_case(QueryProp(P_NAME)) ||
+    id(str)
+ )
+ {
+  return tie("seil los");
+ }
+ _notify_fail("Was moechtest Du loesen?\n");
+ return 0;
+}
+
+
+object query_tied_to_ob()
+{
+  return tied_to_ob;
+}
+
+varargs int binde_seil(string ziel,string msg)
+{
+ if(!QueryProp(P_TIE_AUTO)) return 0;
+
+ // diese funktion bindet ein Seil und kann von einem object aus 
+ // aufgerufen werden.
+ SetProp(P_TIE_VERB,"binde");
+
+ if(!msg) msg = Name(WER)+" wird auf magische Art und Weise festgebunden.\n";
+
+ SetProp(P_TIE_MESSAGE,msg);
+ return tie("seil an "+ziel); 
+}
+
+varargs int loese_seil(string msg)
+{
+ if(!QueryProp(P_TIE_AUTO)) return 0;
+
+ if(!msg) msg = Name(WER)+" loest sich.\n";
+ 
+ SetProp(P_TIE_VERB,"binde");
+ SetProp(P_UNTIE_MESSAGE,msg);
+ return tie("seil los");
+}
+
diff --git a/obj/.readme b/obj/.readme
new file mode 100644
index 0000000..1e7483a
--- /dev/null
+++ b/obj/.readme
@@ -0,0 +1,5 @@
+In diesem Verzeichnis bekommen alle Objekte die UID des erzeugers (was
+dementsprechend bis Gott geht), daher duerfen hier nur EM+ schreiben und hier
+angeschlossener Code sollte sehr gruendlich geprueft werden.
+Fuer global verfuegbare Items in Spielerhaenden sollte /items/ verwendet
+werden.
diff --git a/obj/boerse.c b/obj/boerse.c
new file mode 100644
index 0000000..db54d74
--- /dev/null
+++ b/obj/boerse.c
@@ -0,0 +1,2 @@
+#pragma no_clone
+
diff --git a/obj/doormaster.c b/obj/doormaster.c
new file mode 100644
index 0000000..46e9364
--- /dev/null
+++ b/obj/doormaster.c
@@ -0,0 +1,469 @@
+#include <properties.h>
+#include <defines.h>
+#include <moving.h>
+#include <language.h>
+
+#define NEED_PROTOTYPES
+#include <doorroom.h>
+
+inherit "/std/thing";
+
+#pragma strong_types
+
+private mapping door_status;
+private int id_zaehler;
+// Format: "filename1:filename2":status
+
+int oeffnen (string str);
+
+mapping QueryAllDoors() {return door_status;}
+
+int QueryDoorStatus(string dest) {
+  string source,dkey;
+  object env;
+
+  if (!dest) return 0;
+  if (!objectp(env=previous_object())) return 0;
+  source=object_name(env);
+  dkey=(source<dest)?(source+":"+dest):(dest+":"+source);
+  return door_status[dkey];
+}
+
+void SetDoorStatus(string dest, int x) {
+  string source,dkey;
+  object env;
+
+  if (!dest) return;
+  if (!objectp(env=previous_object())) return;
+  source=object_name(env);
+  dkey=(source<dest)?(source+":"+dest):(dest+":"+source);
+  door_status[dkey]=x;
+}
+
+protected void create() {
+  door_status=([]);
+  id_zaehler=0;
+  if (IS_CLONE(this_object())) remove();
+}
+
+varargs int remove(int silent) {return 0;}
+
+varargs int NewDoor(string|string* cmds, string dest, string|string* ids,
+                    mapping|<int|string|string*>* props)
+{
+  object env;
+  string source,dkey,sec_id;
+  mixed *info2,zw;
+  mapping info;
+  int i;
+
+  if (!objectp(env=previous_object())) return 0;
+  if (!cmds || !dest) return 0;
+  if (!ids) ids=({"tuer"});
+  if (stringp(cmds)) cmds=({cmds});
+  if (stringp(ids)) ids=({ids});
+  if (!pointerp(cmds) || !pointerp(ids)) return 0;
+  if (dest[0..0]!="/") dest="/"+dest;
+  id_zaehler++;
+  sec_id=sprintf("secure_id:door%d",id_zaehler);
+
+  info=([D_DEST   : dest,
+	 D_CMDS   : cmds,
+	 D_IDS    : ({ sec_id }) + ids,
+	 D_FLAGS  : (DOOR_CLOSED|DOOR_RESET_CL),
+         D_LONG   : "Eine Tuer.\n",
+	 D_SHORT  : "Eine %se Tuer. ",
+	 D_NAME   : "Tuer",
+	 D_GENDER : FEMALE]);
+  source=object_name(env);
+  dkey=(source<dest)?(source+":"+dest):(dest+":"+source);
+
+  if (pointerp(props)) {
+    for (i=0;i<sizeof(props)-1;i+=2)
+      if (intp(props[i]) && props[i]>=D_MINPROPS && props[i]<=D_MAXPROPS)
+        info[props[i]]=props[i+1];
+  }
+  else if(mappingp(props)){
+    info += props;
+  }
+
+  if (!door_status[dkey]) {
+    // Nur initialisieren, wenn Tuer noch nicht existiert.
+    if (info[D_FLAGS] & DOOR_OPEN)
+        door_status[dkey]=D_STATUS_OPEN;
+    if (info[D_FLAGS] & DOOR_CLOSED)
+        door_status[dkey]=
+          ((info[D_FLAGS] & DOOR_NEEDKEY) ? D_STATUS_LOCKED : D_STATUS_CLOSED);
+  }
+  info2=env->QueryProp(P_DOOR_INFOS);
+  if (!pointerp(info2))
+    info2=({info});
+  else
+    info2+=({info});
+  env->SetProp(P_DOOR_INFOS,info2);
+
+  if(mappingp(info[D_LONG]) && pointerp(zw=m_indices(info[D_LONG])) &&
+     sizeof(zw) && intp(zw[0]))
+    env->AddSpecialDetail(ids,"special_detail_doors");
+  else if(info[D_LONG] && info[D_LONG]!="")
+    env->AddDetail(ids,info[D_LONG]);
+
+  return 1;
+}
+
+void init_doors () {
+  object env;
+  mixed *info;
+  string source,dest,dkey;
+  int i;
+
+  if (!objectp(env=previous_object())) return;
+  info=env->QueryProp(P_DOOR_INFOS);
+  if (!pointerp(info)) return;
+
+  source=object_name(env);
+  for (i=sizeof(info)-1;i>=0;i--) {
+    if (!mappingp(info[i])) continue;
+    dest=info[i][D_DEST];
+    dkey=(source<dest)?(source+":"+dest):(dest+":"+source);
+    env->set_doors(info[i][D_CMDS],(door_status[dkey]>0));
+  }
+}
+
+string look_doors () {
+  object env;
+  mixed *info, ds;
+  string source,dkey,dest,res;
+  int i, st;
+
+  if (!objectp(env=previous_object())) return "";
+  info=env->QueryProp(P_DOOR_INFOS);
+  if (!pointerp(info)) return "";
+  init_doors(); // Aktueller Zustand soll auch bei den Exits angezeigt werden
+
+  source=object_name(env);res="";
+  for (i=sizeof(info)-1;i>=0;i--) {
+    if (!mappingp(info[i])) continue;
+    dest=info[i][D_DEST];
+    dkey=(source<dest)?(source+":"+dest):(dest+":"+source);
+    if (stringp(info[i][D_SHORT])) {
+      res+=sprintf(info[i][D_SHORT],
+		    ((door_status[dkey]>=D_STATUS_OPEN)?"geoeffnet":
+			 ((door_status[dkey]!=D_STATUS_CLOSED)?"ab":"")+"geschlossen"));
+	  if (sizeof(res) && res[<1]!='\n')
+		res+=" ";
+	}
+    else if(mappingp(ds=info[i][D_SHORT])){
+      st=door_status[dkey];
+      if((st==D_STATUS_LOCKED) && !ds[D_STATUS_LOCKED])
+        st=D_STATUS_CLOSED;
+      if(stringp(ds[st])){
+        res += ds[st];
+        if(sizeof(ds[st]) && res[<1]!='\n')res+=" ";
+      }
+    }
+  }
+  if (res && res!="")
+    return break_string(res,78,0,1);
+  return res;
+}
+
+static void door_message(object room, string dname, int dgender, string msg) {
+  object ob;
+
+  // printf("%O,%O,%O,%O\n",room,dname,dgender,msg);
+  if (!room || !dname || !msg) return;
+  ob=this_object();
+  ob->SetProp(P_NAME,dname);
+  ob->SetProp(P_GENDER,dgender);
+  ob->SetProp(P_ARTICLE,1);
+  tell_room(room,capitalize(ob->name(WER))+msg+"\n");
+}
+
+static void door_message_other(string source, string dest, string msg) {
+  mixed info;
+  object env;
+  int i;
+
+  // printf("%O %O %O\n",source,dest,msg);
+  if (!source || !dest || !msg) return;
+  if (!objectp(env=find_object(dest))) return;
+  if (!pointerp(info=env->QueryProp(P_DOOR_INFOS))) return;
+  for (i=sizeof(info)-1;i>=0;i--) {
+    if (!mappingp(info[i])) continue;
+    if (info[i][D_DEST]!=source) continue; // Andere Tuer zu diesem Raum
+    door_message(env,info[i][D_NAME],info[i][D_GENDER],msg);
+  }
+}
+
+void reset_doors () {
+  object env;
+  mixed *info;
+  string source,dest,dkey,msg;
+  int i,j;
+
+  if (!objectp(env=previous_object())) return;
+  info=env->QueryProp(P_DOOR_INFOS);
+  if (!pointerp(info)) return;
+
+  source=object_name(env);
+  for (i=sizeof(info)-1;i>=0;i--) {
+    if (!mappingp(info[i])) continue;
+    dest=info[i][D_DEST];
+    dkey=(source<dest)?(source+":"+dest):(dest+":"+source);
+
+// 	if (door_status[dkey]>-2 && door_status[dkey]<2) {
+// 	  if (door_status[dkey]>0)
+// 		door_status[dkey]=2;
+// 	  else
+// 		door_status[dkey]=-2;
+// 	  continue; // nur jeder 2. Reset wird ausgefuehrt.
+// 	}
+
+    if (info[i][D_FLAGS] & DOOR_RESET_CL) {
+      // Tuer muss bei Reset geschlossen werden
+      if (door_status[dkey] >= D_STATUS_OPEN) {
+        if(!msg=info[i][D_RESET_MSG])msg=" schliesst sich.";
+        door_message(env,info[i][D_NAME],info[i][D_GENDER],msg);
+        door_message_other(source,dest,msg);
+      }
+	  if (door_status[dkey]!=D_STATUS_LOCKED)
+		door_status[dkey]=
+      ((info[i][D_FLAGS] & DOOR_NEEDKEY) ? D_STATUS_LOCKED : D_STATUS_CLOSED);
+    }
+    if (info[i][D_FLAGS] & DOOR_RESET_OP) {
+      // Tuer muss bei Reset geoeffnet werden
+      if (door_status[dkey] < D_STATUS_OPEN) {
+        if(!msg=info[i][D_RESET_MSG])msg=" oeffnet sich.";
+        door_message(env,info[i][D_NAME],info[i][D_GENDER],msg);
+        door_message_other(source,dest,msg);
+      }
+      door_status[dkey]=D_STATUS_OPEN;
+    }
+  }
+  init_doors();
+}
+
+static varargs int exec_func2(string dest, mixed func) {
+  if (!stringp(dest) || !stringp(func)) return 1;
+  call_other(dest,func);
+  return 1;
+}
+
+varargs int go_door (string str) {
+  object env,pl,ob;
+  mixed *info;
+  string source,dest,dkey;
+  int i;
+
+  if (!str) return 0;
+  if (!objectp(env=previous_object())) return 0;
+  if (!objectp(pl=this_player())) return 0;
+  info=env->QueryProp(P_DOOR_INFOS);
+  if (!pointerp(info)) return 0;
+
+  source=object_name(env);
+  ob=this_object();
+  ob->SetProp(P_ARTICLE,1);
+  for (i=sizeof(info)-1;i>=0;i--) {
+    if (!mappingp(info[i])) continue;
+    if (member(info[i][D_CMDS],str)<0) continue;
+    dest=info[i][D_DEST];
+    dkey=(source<dest)?(source+":"+dest):(dest+":"+source);
+    ob->SetProp(P_NAME,info[i][D_NAME]);
+    ob->SetProp(P_GENDER,info[i][D_GENDER]);
+    notify_fail(capitalize(ob->name(WER,1))+" ist geschlossen.\n");
+    if((door_status[dkey]<=0) &&
+       !info[i][D_OPEN_WITH_MOVE]) continue; // Tuer geschlossen
+    if(door_status[dkey]<=0){
+      // In diesem Fall versuchen, die Tuer zu oeffnen.
+      oeffnen(info[i][D_IDS][0]);
+    }
+    notify_fail(capitalize(ob->name(WER,1))+" ist geschlossen.\n");
+    if (door_status[dkey]<=0) continue; // Tuer immer noch zu.
+    if (stringp(info[i][D_TESTFUNC]))
+      if (call_other(env,info[i][D_TESTFUNC]))
+		return 1; // Durchgang von der Tuer nicht erlaubt.
+    if (stringp(info[i][D_FUNC]))
+      call_other(env,info[i][D_FUNC]);
+	if (stringp(info[i][D_MSGS])) {
+	  if (pl->move(dest,M_GO,info[i][D_MSGS])>0)
+		return exec_func2(dest,info[i][D_FUNC2]);
+	  else
+		return 1;
+	}
+	if (pointerp(info[i][D_MSGS]) && sizeof(info[i][D_MSGS])>=3) {
+	  if (pl->move(dest,M_GO,info[i][D_MSGS][0],
+				   info[i][D_MSGS][1],info[i][D_MSGS][2]))
+		return exec_func2(dest,info[i][D_FUNC2]);
+	  else
+		return 1;
+	}
+    if (pl->move(dest,M_GO,"nach "+capitalize(str)))
+	  return exec_func2(dest,info[i][D_FUNC2]);
+    return 1;
+  }
+  return 0;
+}
+
+int oeffnen (string str) {
+  object env,schl,ob;
+  mixed *info,s2;
+  string source,dkey,dest,s1;
+  int i;
+
+  notify_fail("WAS willst Du oeffnen?\n");
+  if (!str || !this_player()) return 0;
+  notify_fail("Das kannst Du nicht oeffnen.\n");
+  str=lower_case(str);
+  if (sscanf(str,"%s mit %s",s1,s2)!=2)
+    {s1=str;s2=0;}
+  if (s2) {
+    if (!(schl=present(lower_case(s2),this_player()))) {
+      notify_fail("So einen Schluessel hast Du nicht.\n");
+      return 0;
+    } else {
+      s2=schl->QueryDoorKey();
+	  if (stringp(s2)) s2=({s2});
+    }
+  }
+
+  if (!objectp(env=previous_object())) return 0;
+  info=env->QueryProp(P_DOOR_INFOS);
+  if (!pointerp(info)) return 0;
+
+  source=object_name(env);
+  ob=this_object();
+  ob->SetProp(P_ARTICLE,1);
+  for (i=sizeof(info)-1;i>=0;i--) {
+    if (!mappingp(info[i])) continue;
+    if (member(info[i][D_IDS],s1)<0) continue; // Falsche Tuer
+    ob->SetProp(P_NAME,info[i][D_NAME]);
+    ob->SetProp(P_GENDER,info[i][D_GENDER]);
+    dest=info[i][D_DEST];
+    dkey=(source<dest)?(source+":"+dest):(dest+":"+source);
+    notify_fail(capitalize(ob->name(WER,1))+" ist doch schon geoeffnet!\n");
+    if (door_status[dkey]>0)
+      continue; // Eine andere Tuer koennte gemeint sein.
+    if ((info[i][D_FLAGS] & DOOR_NEEDKEY)
+        && door_status[dkey] == D_STATUS_LOCKED) { // abgeschlossen
+      notify_fail("Du brauchst einen Schluessel, um "+ob->name(WEN,1)+" zu oeffnen.\n");
+      if (!schl) continue; // Eine andere Tuer koennte gemeint sein.
+      notify_fail(capitalize(schl->name(WER))+" passt nicht!\n");
+      if (!pointerp(s2)) continue;
+	  if (member(s2,dkey)<0) continue; // Koennte an einer anderen passen.
+    }
+    door_status[dkey]=1;
+    init_doors();
+    write("Du oeffnest "+ob->name(WEN)+".\n");
+    say(capitalize(ob->name(WER))+" wird von "+this_player()->name(WEM)+
+        " geoeffnet.\n");
+    door_message_other(source,dest," wird von der anderen Seite geoeffnet.");
+    return 1;
+  }
+  return 0;
+}
+
+int schliessen (string str) {
+  object env,schl,ob;
+  mixed *info,s2;
+  string source,dkey,dest,s1;
+  int i,abg;
+
+  notify_fail("WAS willst Du schliessen?\n");
+  if (!str || !this_player()) return 0;
+  notify_fail("Das kannst Du nicht schliessen.\n");
+  str=lower_case(str);
+  if (sscanf(str,"%s mit %s",s1,s2)!=2)
+    {s1=str;s2=0;}
+  if (s2) {
+    if (!(schl=present(lower_case(s2),this_player()))) {
+      notify_fail("So einen Schluessel hast Du nicht.\n");
+      return 0;
+    } else {
+      s2=schl->QueryDoorKey();
+	  if (stringp(s2)) s2=({s2});
+    }
+  }
+
+  if (!objectp(env=previous_object())) return 0;
+  info=env->QueryProp(P_DOOR_INFOS);
+  if (!pointerp(info)) return 0;
+
+  source=object_name(env);
+  ob=this_object();
+  ob->SetProp(P_ARTICLE,1);
+  abg=0;
+  for (i=sizeof(info)-1;i>=0;i--) {
+    if (!mappingp(info[i])) continue;
+    if (member(info[i][D_IDS],s1)<0) continue; // Falsche Tuer
+    ob->SetProp(P_NAME,info[i][D_NAME]);
+    ob->SetProp(P_GENDER,info[i][D_GENDER]);
+    dest=info[i][D_DEST];
+    dkey=(source<dest)?(source+":"+dest):(dest+":"+source);
+	if (schl) {
+	  notify_fail(capitalize(ob->name(WER,1))+" ist doch schon abgeschlossen!\n");
+	  if (door_status[dkey]<=0
+        && door_status[dkey] == D_STATUS_LOCKED)
+      continue; // Eine andere Tuer koennte gemeint sein.
+	  if (info[i][D_FLAGS] & DOOR_CLOSEKEY) { // Schluessel noetig?
+		  notify_fail(capitalize(schl->name(WER))+" passt nicht!\n");
+		  if (!pointerp(s2)) continue;
+		  if (member(s2,dkey)<0) continue; // Koennte an einer anderen passen.
+		  door_status[dkey]=D_STATUS_LOCKED;
+		  abg=1;  // Tuer wird richtig abgeschlossen
+	  }
+    else {
+		  // ohne Schluessel abschliessbar
+		  door_status[dkey]=
+        ((info[i][D_FLAGS] & DOOR_NEEDKEY) ? D_STATUS_LOCKED : D_STATUS_CLOSED);
+	  }
+	}
+  else {
+	  notify_fail(capitalize(ob->name(WER,1))+" ist doch schon geschlossen!\n");
+	  if (door_status[dkey] < D_STATUS_OPEN)
+		  continue; // Eine andere Tuer koennte gemeint sein.
+	  if ((info[i][D_FLAGS] & DOOR_NEEDKEY) && 
+        !(info[i][D_FLAGS] & DOOR_CLOSEKEY))
+		  door_status[dkey]=D_STATUS_LOCKED; // Abschliessbar, aber dazu schluessel unnoetig
+	  else
+		  door_status[dkey]=D_STATUS_CLOSED;
+	}
+	init_doors();
+	write("Du schliesst "+ob->name(WEN)+
+		  (abg?" ab":"")+".\n");
+	say(capitalize(ob->name(WER))+" wird von "+this_player()->name(WEM)+
+		(abg?" ab":" ")+"geschlossen.\n");
+	door_message_other(source,dest," wird von der anderen Seite "+
+					   (abg?"ab":"")+"geschlossen.");
+    return 1;
+  }
+  return 0;
+}
+string special_detail_doors(string key){
+  object env;
+  mixed *info;
+  mapping dl;
+  int i, st;
+  string source, dest, dkey;
+
+  if(!objectp(env=previous_object()))return 0;
+  if(!pointerp(info=env->QueryProp(P_DOOR_INFOS)))return 0;
+  source=object_name(env);
+
+  for(i=sizeof(info)-1;i>=0;i--){
+    if(!mappingp(info[i]))continue;
+    if(member(info[i][D_IDS],key)<0)continue;
+    if(stringp(info[i][D_LONG]))return info[i][D_LONG];
+    if(!mappingp(info[i][D_LONG]))continue;
+    dl=info[i][D_LONG];
+    dest=info[i][D_DEST];
+    dkey=(source<dest)?(source+":"+dest):(dest+":"+source);
+    st=door_status[dkey];
+    if((st==D_STATUS_LOCKED) && !dl[D_STATUS_LOCKED])
+        st=D_STATUS_CLOSED; /* Falls keine eigene Beschreibung
+                             * fuer abgeschlossene Tuer vorhanden */
+    return dl[st];
+  }
+  return 0;
+}
diff --git a/obj/entsorg.c b/obj/entsorg.c
new file mode 100644
index 0000000..6fdda13
--- /dev/null
+++ b/obj/entsorg.c
@@ -0,0 +1,92 @@
+#pragma strict_types
+
+inherit "/std/npc";
+
+#include <properties.h>
+#include <moving.h>
+
+void create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+
+  SetProp(P_SHORT,"Ein Muellschlucker");
+  SetProp(P_LONG,break_string(
+    "Der Muellschlucker gehoert zu Familie der Kleinsaurier und zeichnet "+
+    "sich durch ein sehr grosses Maul und nahezu unbegrenzten Appetit aus. "+
+    "Das einzige, was er nicht mag, sind Dinge, die materiellen Wert haben. "+
+    "Alles andere kann man ihm mit 'fuettere muellschlucker mit <name>' "+
+    "in den Rachen stopfen.",78));
+  SetProp(P_INFO,break_string(
+    "Dieser Muellschlucker verschlingt auch lose herumliegende Dinge. Also "+
+    "pass auf, was Du wegwirfst.",78));
+  SetProp(P_NAME,"Muellschlucker");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_RACE,"saurier");
+  SetProp(P_ARTICLE,1);
+  AddId(({"muellschlucker","schlucker","kleinsaurier","saurier"}));
+  create_default_npc( 1 );
+  SetProp(P_XP,0);
+  SetProp(P_BODY,100);
+  SetProp(P_HANDS,({" mit dem alles verschlingenden Gebiss",20}));
+  SetProp(P_ALIGN, 0);
+  seteuid(getuid(this_object()));
+  SetProp(P_DEFAULT_INFO,"grunzt nur vor sich hin.\n");
+  SetProp(P_MSGIN,"rollt herein");
+  SetProp(P_MSGOUT,"rollt");
+  SetProp(P_MAX_WEIGHT, 200000);
+  AddCmd(({"fuetter","fuettere"}),"haps");
+  enable_commands();
+}
+
+int haps(string str)
+{
+  string s1,s2;
+  object ob;
+  notify_fail("Syntax: fuettere muellschlucker mit <name>\n");
+  if(!str)return 0;
+  if(sscanf(str,"%s mit %s",s1,s2)!=2)return 0;
+  if(!id(s1))return 0;
+  notify_fail("So etwas hast Du nicht.\n");
+  if((!ob=present(s2,this_player())) && 
+     (!ob=present(s2,environment()))) return 0;
+  if(ob->QueryProp(P_NODROP) || ob->QueryProp(P_NEVERDROP)){
+    write("Das kannst Du nicht wegwerfen.\n");
+    return 1;
+  }
+  if(living(ob)){
+    write("Der Muellschlucker frisst keine Lebewesen.\n");
+    return 1;
+  }
+  if(ob->QueryProp(P_VALUE)){
+    write("Das ist zu wertvoll, das vertraegt der Muellschlucker nicht.\n");
+    return 1;
+  }
+  if (ob->QueryProp(P_CURSED)) {
+    write("Verfluchte Dinge bekommen dem Muellschlucker nicht!\n");
+    return 1;
+  }
+  ob->remove();
+  if(ob){
+    write("Aus irgendeinem Grund scheint das dem Muellschlucker nicht "+
+    "zu schmecken.\n");
+    return 1;
+  }
+  write("Mit einem lauten Ruelpsen verschlingt der Muellschlucker alles.\n");
+
+  return 1;
+}
+
+void reset()
+{
+  int i;
+  object *inv;
+  ::reset();
+  
+  inv=all_inventory(this_object());
+  if(inv && sizeof(inv))
+    for(i=sizeof(inv)-1;i>=0;i--)
+      destruct(inv[i]);
+  command("nimm alles");
+}
+
diff --git a/obj/misc/bonbon.c b/obj/misc/bonbon.c
new file mode 100644
index 0000000..43bf7f5
--- /dev/null
+++ b/obj/misc/bonbon.c
@@ -0,0 +1,102 @@
+// Asrahel
+
+inherit "std/thing" ;
+
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+#include <language.h>
+
+void geschmack()
+{
+	int num;
+	num = random (8);
+	switch(num)
+	{
+		case 0 :	
+			SetProp(P_SHORT,"Ein Orangenbonbon");
+			SetProp(P_LONG,"Ein gefuelltes Bonbon mit Orangengeschmack.\n");
+			SetProp(P_NAME,"Orangenbonbon");
+			AddId(({"orangenbonbon"}));
+			break;
+		case 1 :
+			SetProp(P_SHORT,"Ein Zitronenbonbon");
+			SetProp(P_LONG,"Ein gefuelltes Bonbon mit Zitronengeschmack.\n");
+			SetProp(P_NAME,"Zitronenbonbon");
+			AddId(({"zitronenbonbon"}));
+			break;
+                case 2 :
+			SetProp(P_SHORT,"Ein Schokoladenbonbon");
+			SetProp(P_LONG,"Ein Schokoladenbonbon, pass auf, dass es nicht schmilzt.\n");
+			SetProp(P_NAME,"Schokoladenbonbon");
+			AddId(({"schokobonbon","schokoladenbonbon"}));
+			break;
+                case 3 :
+			SetProp(P_SHORT,"Ein Pfefferminzbonbon");
+			SetProp(P_LONG,"Pfefferminze befreit den Hals und kitzelt in der Nase.\n");
+			SetProp(P_NAME,"Pfefferminzbonbon");
+			AddId(({"pfefferminzbonbon","minze","pfefferminz"}));
+			break;
+                case 4 :
+			SetProp(P_SHORT,"Ein Sahnebonbon");
+			SetProp(P_LONG,"Sehr suess und sehr klebrig.\n");
+			SetProp(P_NAME,"Sahnebonbon");
+			AddId(({"sahnebonbon"}));
+			break;
+                case 5 :
+			SetProp(P_SHORT,"Ein Karamelbonbon");
+			SetProp(P_LONG,"Ein Karamelbonbon, der Rest von Karneval?\n");
+			SetProp(P_NAME,"Karamelbonbon");
+			AddId(({"karamelbonbon"}));
+			break;
+		case 6 :
+			SetProp(P_SHORT,"Ein Anananasbonbon");
+			SetProp(P_LONG,"Ein dreieckiges Anananasbonbon, das schmeckt bestimmt ganz ananassig.\n");
+			SetProp(P_NAME,"Ananasbonbon");
+			AddId(({"ananasbonbon"}));
+			break;
+		case 7 :
+			SetProp(P_SHORT,"Ein Himbeerdrops");
+			SetProp(P_LONG,"Dieses himbeerige Fruchtbonbon ist viel groesser als eine Himbeere.\n");
+			SetProp(P_NAME,"Himbeerdrops");
+			AddId(({"drops","himbeerdrops","fruchtbonbon"}));
+			break;
+                default :
+			SetProp(P_SHORT,"Ein Multivitaminbonbon");
+			SetProp(P_LONG,"Ein Multivitaminbonbon, sehr gesund (besonders bei dem Wetter in Muenster).\n");
+			SetProp(P_NAME,"Multivitaminbonbon");
+			AddId(({"vitaminbonbon","multivitaminbonbon"}));
+			break;
+    }
+}
+
+void create()
+{
+  if (!clonep(this_object())) return;
+	::create();
+	SetProp(P_WEIGHT, 25);
+	SetProp(P_VALUE, 0);
+	SetProp(P_GENDER, NEUTER );
+	AddId("bonbon");
+	geschmack();
+}
+
+
+void init()
+{
+	::init();
+	add_action("essen","esse");
+	add_action("essen","iss");
+	add_action("essen","lutsche");
+}
+
+int essen(string str)
+{
+	notify_fail("Was moechtest Du essen?\n");
+	if ( !str || !id(str) )
+	    return 0;
+	write("Du lutschst genuesslich " + name(WEN,0) + ".\n");
+	say( PL->Name(WER,2) + " lutscht " + name(WEN,0) + ".\n");
+	remove();
+	return 1;
+}
diff --git a/obj/misc/kissen.c b/obj/misc/kissen.c
new file mode 100644
index 0000000..33c365b
--- /dev/null
+++ b/obj/misc/kissen.c
@@ -0,0 +1,128 @@
+inherit "std/thing";
+#include <properties.h>
+#include <language.h>
+
+string cstr;  
+
+init()
+{
+  int colour;
+  ::init();
+  colour = random(8);
+  if (colour==0) cstr = "gruenes";
+  if (colour==1) cstr = "rotes";
+  if (colour==2) cstr = "blaues";
+  if (colour==3) cstr = "gelbes";
+  if (colour==4) cstr = "schwarz-weiss-kariertes";
+  if (colour==5) cstr = "orange-pink-gestreiftes";
+  if (colour==6) cstr = "lila-gruen-getuepfeltes";
+  if (colour==7) cstr = "silbergraues";
+  add_action("throw_it","wirf"); 
+  add_action("ww", "ww");
+}
+
+short()
+{
+  return "Ein " + cstr + " Kissen\n";
+}
+
+long()
+{
+  return "Dies ist ein wunderschoenes, " + cstr + " Kissen.\n"+
+          "Du kannst versuchen es zu werfen.\n";
+}
+
+create()
+{
+  ::create();
+  cstr = "von Boing handgenaehtes";
+  AddId("kissen");
+  SetProp(P_WEIGHT, 1);
+  SetProp(P_VALUE, 1);
+  SetProp(P_NAME, "Kissen");
+  SetProp(P_GENDER, 0);
+}
+
+ww(str)
+{
+  if (str)
+    return throw_it("kissen nach "+str);
+  else
+  {
+    object *all, *liv;
+    int i;
+
+    all=all_inventory(environment(this_player()));
+    for (i=0; i<sizeof(all); i++)
+      if (query_once_interactive(all[i]))
+        if (!liv)
+	  liv = ({all[i]});
+        else
+          liv = liv+({all[i]});
+    i = random(sizeof(liv));
+    return throw_it("kissen nach "+lower_case(liv[i]->query_real_name()));
+  }
+}
+
+throw_it(str)
+{
+  int treffer;
+  string werfer_name;
+  string victim_string,werfer_string;
+  string was,at,wen;
+  int fehler;
+  object victim;
+
+  if (!str) return 0;
+  fehler = 0;
+  if (sscanf(str,"%s %s %s",was,at,wen) != 3)
+    fehler = 1;
+  if (!id(was) || at != "nach")
+    fehler = 1;
+  if (fehler)
+  { 
+     notify_fail("Zum Werfen: wirf kissen nach <spielername>\n");
+     return 0;
+  }
+  victim = present(wen,environment(this_player()));
+  if (!victim)
+  { 
+     write("Dieser Spieler ist nicht hier!\n");                                
+     return 1;                                                                 
+  }                                                                           
+  werfer_name = this_player()->name();
+  treffer = random(5);
+  write("Du schmeisst ein Kissen nach " + victim->name(WEM) +".\n");
+  say(werfer_name + " wirft ein Kissen nach " + victim->name(WEM) + ".\n");
+  if (treffer==0)
+  {
+    werfer_string = "Du verfehlst " + victim->name(WEN) + " meilenweit.\n";
+    victim_string = werfer_name + " verfehlt Dich meilenweit.\n";
+    this_object()->move(environment(this_player()));
+  }
+  if (treffer==1)
+  {
+    werfer_string = "Das Kissen streift " + victim->name(WEN)+ " am Ohrlaeppchen.\n";
+    victim_string = "Das Kissen streift Dich am Ohrlaeppchen.\n";
+  }
+  if (treffer==2)
+  {
+    werfer_string = "PAFF! Du triffst " + victim->name(WEN) + " mitten ins Gesicht.\n";
+    victim_string = "PAFF! Das Kissen trifft Dich mitten im Gesicht!\n";
+  }
+  if (treffer==3)
+  {
+    werfer_string = "WUSCH! Das Kissen trifft " + victim->name(WESSEN) + " Bauch mit voller Wucht.\n";
+    victim_string = "WUSCH! Das Kissen fliegt mit voller Wucht gegen Deinen Bauch.\n";
+  }
+  if (treffer==4)
+  {
+    werfer_string =  victim->name() + " versucht wegzurennen, doch das Kissen trifft den Hinterkopf.\n";
+    victim_string = "Du versuchst wegzurennen, aber das Kissen trifft Dich am Hinterkopf.\n";
+  }
+  write(werfer_string);
+  tell_object(victim,victim_string);
+  if (treffer) this_object()->move(victim);
+  return 1;
+}
+
diff --git a/obj/misc/lolli.c b/obj/misc/lolli.c
new file mode 100644
index 0000000..6b8278e
--- /dev/null
+++ b/obj/misc/lolli.c
@@ -0,0 +1,29 @@
+inherit "std/thing";
+#include <properties.h>
+#include <language.h>
+
+void create()
+{
+   // Laeden erwarten Short und Value in der Blueprint.
+   SetProp(P_SHORT, "Ein Lolli");
+   SetProp(P_VALUE, 5);
+   
+  if (!clonep(this_object())) return;
+   ::create();
+   SetProp(P_SHORT, "Ein Lolli");
+   SetProp(P_NAME, "Lolli");
+   SetProp(P_GENDER, MALE);
+   SetProp(P_LONG, "Ein leckerer Lolli, Du kannst ihn schlecken.\n");
+   AddId("lolli");
+   SetProp(P_WEIGHT, 10);
+   AddCmd( ({"schleck","schlecke","leck","lecke"}), "schleck" );
+}
+
+int schleck(string str)
+{
+   notify_fail("Was moechtest Du schlecken?\n");
+   if (!str || !id(str)) return 0;
+   write("Du schleckst an Deinem Lolli. Er schmeckt ausgezeichnet.\n");
+   say(this_player()->name()+" schleckt an "+this_player()->QueryPossPronoun(this_object(), WEM)+" Lolli.\n");
+   return 1;
+}
diff --git a/obj/misc/tuete.c b/obj/misc/tuete.c
new file mode 100644
index 0000000..e7173bd
--- /dev/null
+++ b/obj/misc/tuete.c
@@ -0,0 +1,47 @@
+//Asrahel
+
+inherit "std/thing" ;
+
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+#include <language.h>
+
+void create()
+{
+  if (!clonep(this_object())) return;
+  ::create() ;
+
+  SetProp( P_SHORT, "Eine Tuete Bonbons" ) ;
+  SetProp( P_LONG, "Eine magische Bonbontuete. So oft Du auch hineingreifst, sie wird niemals leer\n"
+		+  "und enthaelt immer eine bunte Mischung.\n");
+
+  SetProp( P_WEIGHT, 0 ) ;
+  SetProp( P_NOBUY, 1) ;
+  SetProp( P_NODROP, "Nein, Du willst doch nicht dieses Geschenk weggeben !?\n");
+  SetProp( P_NEVERDROP, 1) ;
+  SetProp( P_NAME, "Tuete") ;
+  SetProp( P_GENDER, FEMALE ) ;
+  AddId( ({ "bonbontuete","tuete", "bonbons" }) ) ;
+  SetProp(P_AUTOLOADOBJ, 1);
+
+}
+
+void init()
+{
+	::init();
+	add_action("naschen","greife");
+	add_action("naschen","greif");
+}
+
+int naschen (string str)
+{
+  notify_fail("Wohinein moechtest Du greifen ?\n");
+	if ( str != "tuete" && str != "in tuete" && str != "bonbons" && str != "in bonbontuete")
+		 return 0;
+	write("Du greifst in Deine Tuete und holst ein Bonbon heraus.\n");
+	say( PL->name(WER,2) + " holt ein Bonbon aus einer Tuete.\n");
+	clone_object("obj/misc/bonbon")->move(PL);
+	return 1;
+}
+
diff --git a/obj/misc/wuerfel.c b/obj/misc/wuerfel.c
new file mode 100644
index 0000000..d5ccb69
--- /dev/null
+++ b/obj/misc/wuerfel.c
@@ -0,0 +1,30 @@
+inherit "std/thing";
+
+#include <language.h>
+#include <properties.h>
+
+void create()
+{
+  // Short und Value werden von Laeden in der Blueprint erwartet
+  SetProp(P_SHORT, "Ein Wuerfel");
+  SetProp(P_VALUE, 1);
+  
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_LONG, "Ein Wuerfel mit 6 Seiten.\n");
+  SetProp(P_NAME, "Wuerfel");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_WEIGHT, 1);
+  AddId("wuerfel");
+	AddCmd("wuerfel","wuerfel");
+}
+
+int wuerfel(string str)
+{
+  int zahl;
+
+  zahl = random(6) + 1;
+  write("Du wuerfelst eine " + zahl + ".\n");
+  say(this_player()->name() + " wuerfelt eine " + zahl + ".\n");
+  return 1;
+}
diff --git a/obj/mliste.c b/obj/mliste.c
new file mode 100644
index 0000000..9f955d5
--- /dev/null
+++ b/obj/mliste.c
@@ -0,0 +1,106 @@
+inherit "/std/thing/properties";
+
+#include <defines.h>
+#include <properties.h>
+#include <sys_debug.h>
+
+#define EM_DATA   "/etc/Erzmagier"
+#define MAGIER    "/etc/Magierliste"
+#define MASTER    "/secure/master"
+#define WIZ_HOME  "/players"
+
+#define REM   0   // RegionsErzMagier
+#define MAG   1   // Normale Magier
+
+private mapping domains, build_domains;
+private int Finished, today;
+void MagierListe();
+
+private mapping _query_Magierliste() { return domains; }
+
+void create()
+{
+  ::create();
+  seteuid(getuid());
+  Set("Magierliste", #'_query_Magierliste/*'*/, F_QUERY_METHOD);
+  call_out("reset", 0);
+}
+
+void reset()
+{
+  if (today >= time()/86400)
+    return;
+
+  today=time()/86400;
+  MagierListe();
+}
+
+void ProcessList(mixed names)
+{
+  mixed Dname;
+  int i;
+  i = 5;
+
+  while(i-- && sizeof(names))
+  {
+    if(!pointerp(Dname = MASTER->get_domain_homes(names[0])))
+      Dname = ({Dname});
+    if(!sizeof(Dname)) Dname = ({"Keine Region"});
+    while(sizeof(Dname))
+    {
+      if(!build_domains[Dname[0]])
+      {
+        build_domains[Dname[0], REM] = ({});
+        build_domains[Dname[0], MAG] = ({});
+      }
+      if(Dname[0]!="Keine Region" && MASTER->domain_master(names[0], Dname[0]))
+        build_domains[Dname[0], REM] += ({names[0]});
+      else
+        build_domains[Dname[0], MAG] += ({names[0]});
+      Dname = Dname[1..];
+    }
+    names = names[1..];
+  }
+  if(sizeof(names)) call_out("ProcessList", 0, names);
+  else { Finished = 1; MagierListe(); }
+}
+
+void MagierListe()
+{
+  string output;
+  mixed d;
+
+  if(!Finished)
+  {
+    if(find_call_out("ProcessList") == -1)
+    {
+      Finished = 0;
+      build_domains = m_allocate(0,2);
+      call_out("ProcessList", 0, get_dir(WIZ_HOME+"/*") - ({".", "..", ".readme"}));
+    }
+    call_out("MagierListe", 20, 1);
+    return;
+  }
+  while(remove_call_out("Magierliste") !=  -1);
+
+  if (!build_domains)
+    return;
+
+  output = "Magierliste von MorgenGrauen vom "+dtime(time())+"\n";
+
+  domains = deep_copy(build_domains);
+  build_domains=0;
+  for(d = m_indices(domains); sizeof(d); d = d[1..])
+  {
+    output += "REGION: "+capitalize(d[0])
+             +" REM: "
+             +implode(map(domains[d[0], REM], #'capitalize), ", ")
+             +" ("+sizeof(domains[d[0], REM]+domains[d[0], MAG])
+             +" Magier)\n"
+             + break_string(implode(map(domains[d[0], MAG],#'capitalize),
+                                    ", "), 62, 8)
+             +"\n";
+  }
+  rm(MAGIER);
+  write_file(MAGIER, output);
+}
diff --git a/obj/mpa.c b/obj/mpa.c
new file mode 100644
index 0000000..7bd1972
--- /dev/null
+++ b/obj/mpa.c
@@ -0,0 +1,1573 @@
+/* Das Servicepaket der Morgengrauen-Presseagentur. Eine erweiterte Zeitung.
+   Teilweise zur alten Zeitung identisch.
+
+   unter Verwendung der Zeitung von Jof, voellig umgeschrieben.
+
+   (C) Nov 1993 - 1996  Loco@Morgengrauen
+
+   Verwendung ausserhalb von Morgengrauen ist gestattet unter folgenden
+   Bedingungen:
+   - Benutzung erfolgt auf eigene Gefahr. Jegliche Verantwortung wird
+     abgelehnt. Bugs ausserhalb von MG sind nicht mein Problem.
+   - Auch in veraenderten oder abgeleiteten Objekten muss ein Hinweis auf
+     die Herkunft erhalten bleiben.
+   - Bitte fragt mich vorher!
+   Ein Update-Service besteht nicht. Ruecknahme dieser Lizenz ist jederzeit
+   moeglich. Und wer nicht in der Lage ist, neue Versionen selber anzupassen,
+   hat in der Administration eines Muds nichts zu suchen.
+
+
+   04.11.93   Erste brauchbare Version
+   (... seitdem viele Aenderungen ...)
+   13.09.95 ca 02:00  erwarte mpa
+   13.09.95 ca 23:00  RebuildGroupList()
+   15.09.95             vorlaeufig ausgebaut
+   16.09.               anders wieder eingebaut, und Kleinigkeiten
+   05.12.95 ca 19:20  Spieler koennen nur noch eigene Artikel loeschen/verl.
+   07.12.95 ca 23:00  Keine Anzeige der Statuszeile. Statuszeilen beginnen
+                      mit ~#!, sind damit nicht mit dem Editor erzeugbar.
+   20.12.95 ca 17:00  uebergehe thread/antworten
+   31.01.96           wiederhole/uncatchup
+   22.04.96           Aufraeumen ignorierter threads. ALPHA!
+   24.04.96           + - . als Adressierungen, zeitungsmeldung
+   02.10.96           Kleinigkeiten
+   08.10.96           Kleinigkeiten
+   05.12.96           antworte auf artikel <nr>, reply <neuer titel>,
+                      versende artikel, Abkuerzungen fuer Rubrikennamen,
+                      Layoutaenderungen,
+                      Herkunft bei verlegten Artikeln in Statuszeile
+
+   Letzte Aenderung: 
+   04.11.06 Zesstra  Anpassung an das neue Verhalten von inherit_list() in LD
+
+*/
+
+#include <properties.h>
+#include <language.h>
+#include <news.h>
+#include <wizlevels.h>
+#include <ansi.h>
+#include <input_to.h>
+#include "/mail/post.h"
+
+inherit "/std/thing";
+inherit NEDIT;
+/*
+#define DEBUG(x)        if (funcall(symbol_function('find_player),"zesstra"))\
+          tell_object(funcall(symbol_function('find_player),"zesstra"),\
+                      "MPA: "+x+"\n")
+#define DEBUGVAR(x) DEBUG(sprintf(x+":\n%O\n",x))
+*/
+#define DEBUGVAR(x)
+
+// Konfiguration, ggf mudabhaengig
+#define DEFAULTGROUP "allgemeines"
+#define MINIHELPPAGE "p/service/loco/doc/mini-mpa.txt"
+#define HELPPAGE     "/p/service/loco/doc/mpa"
+#define WIZHELPPAGE  "/p/service/loco/doc/mpa.wiz"
+#define SAVEMSGPATH(pl) ("/open/News/"+(geteuid(pl))+".news")
+#define IS_POST(r)  (member(inherit_list(r),STDPOST+".c")>=0)
+#define INITIAL_SUBSCRIPTIONS \
+  ({  "bekanntmachungen", \
+      "d.ebene", \
+      "d.wueste", \
+      "d.inseln", \
+      "d.unterwelt", \
+      "d.dschungel", \
+      "d.gebirge", \
+      "d.polar", \
+      "d.wald", \
+      "d.fernwest", \
+      "d.vland", \
+      "d.anfaenger", \
+      "entwicklung", \
+   })
+
+// Makros etc.
+
+#define TP          this_player()
+#define TI          this_interactive()
+#define STATUSESCAPE "~#!" 
+#define IS_STATUSLINE(s) ((s[0..1]=="#!")||(s[0..2]=="~#!"))
+//#define IS_STATUSLINE(s) (s[0..2]=="~#!")
+// in Rubrik muell fanden sich tatsaechlich noch solche uralten notes...
+#define IGNOREGROUP "*ignored*"
+#define NNADWMSG    "*NNADWMSG*"
+#define SYSTEMGROUPS ({IGNOREGROUP,NNADWMSG})
+
+// Aufbau der ignoregroup:
+//   ([group1:([tid1:lasttime1,tid2:lasttime2,...]),group2:...])
+
+// Flags fuer Message2string()
+#define M2S_VT100    1
+#define M2S_ANSI     M2S_VT100
+#define M2S_REMOTE   2
+
+// Format von lasttitle
+#define LAST_TITLE   0
+#define LAST_WRITER  1
+#define LAST_TIME    2
+#define LAST_TID     3
+#define LAST_GROUP   4
+#define LAST_NR      5
+#define LAST_SIZEOF  6
+
+
+mapping read_until;
+mixed   lasttitle; // Aufbau s.o.
+mixed   message;
+int     deadTID;
+string  GROUP;
+
+int GetTID(mixed message);
+private void InitialSubscriptions();
+
+create()
+{
+  ::create();
+  seteuid(getuid());
+  GROUP="bekanntmachungen";
+  SetProp(P_SHORT,"Die Zeitung");
+  SetProp(P_NAME,"Zeitung");
+  SetProp(P_WEIGHT, 50);
+  SetProp(P_SIZE, 35);
+  SetProp(P_MATERIAL, ([MAT_PAPER: 100]) );
+  SetProp(P_GENDER, FEMALE);
+  AddId(({"nn","zeitung","servicepaket","mpa"}));
+  SetProp(P_NODROP,
+    "Das persoenliche Servicepaket der MPA kann man nicht wegwerfen.\n");
+  SetProp(P_NEVERDROP, 1);
+
+  if (!read_until) read_until=(["muell":-1]);
+}
+
+static mixed _query_read_msg() { return long(); }
+
+long() {
+  return "\
+Dies ist das Servicepaket der MPA (Morgengrauens Presse Agentur) -\n\
+die Zeitung des Morgengrauens.\n\
+Du kannst damit aktuelle Artikel lesen und schreiben, mit folgenden Befehlen:\n\
+ 'nn [rubrik]' (Neueste Nachrichten) wuehlt die Rubrik durch und schaut,\n\
+                ob was neu ist. Der erste ungelesene Artikel wird angezeigt.\n\
+                Ohne Argument werden alle Rubriken durchwuehlt.\n\
+ 'hilfe mpa'    Ausfuehrliche Hilfsseite. Lesen dringend empfohlen!\n\
+Weitere Befehle:\n\
+ rubriken [neu]                       bestelle <rubrik> ab\n\
+ [lies ]rubrik <rubrik>               abonniere <rubrik>\n\
+ inhalt [<rubrik>|neu|suche <text>]   wiederhole ...\n\
+ nn <rubriken>|<liste>                uebergehe ...\n\
+ [lies ]artikel <nummer>"+
+  ( (IS_LEARNER(TP)) ? "              speichere artikel <nr>" : "" )+"\n\
+ schreib <titel>                      versende artikel [nr] an <adresse>\n\
+ antworte [auf artikel <nr>] [titel]  verlege artikel <nr> nach <rubrik>\n\
+ loesche artikel <nr>                 zeitungsmeldung [neue Meldung]\n\
+ verbrenne zeitung                    wenn Dir alles zuviel wird....\n\
+"+ (IS_SEER(TP) ? "\
+Mit 'mail' bzw 'mail <spieler>' kannst Du Post lesen und schreiben.\n\
+" : "" )+"\
+Eingebaute aliase: note, catchup, reply, unsubscribe, subscribe, uncatchup.\n\
+Viele Befehle geben mit '<befehl> ?' einige Syntaxhinweise.\n\
+Aktuelle Rubrik: "+GROUP+"\n\
+";
+}
+
+
+init()
+{
+  ::init();
+
+  add_action("schreib","schreib");
+  add_action("schreib","schreibe");
+  add_action("schreib","note");
+  add_action("LiesArtikel","lies");
+  add_action("artikel","artikel");
+  add_action("loesche","loesch");
+  add_action("loesche","loesche");
+  add_action("rubrik","rubrik");
+  add_action("inhalt","inhalt");
+  add_action("rubriken","rubriken");
+  add_action("ReadNextUnread","nn");
+  add_action("Catchup","catchup");
+  add_action("Catchup","uebergeh");
+  add_action("Catchup","uebergehe");
+  add_action("Uncatchup","wiederhol");
+  add_action("Uncatchup","wiederhole");
+  add_action("Uncatchup","uncatchup");
+  add_action("Reply","antwort");
+  add_action("Reply","antworte");
+  add_action("Reply2","reply");
+  add_action("HelpPage","hilfe");
+  add_action("HelpPage","man");
+  add_action("Unsubscribe","unsubscribe");
+  add_action("Bestelle","bestell");
+  add_action("Bestelle","bestelle");
+  add_action("Subscribe","subscribe");
+  add_action("Subscribe","abonnier");
+  add_action("Subscribe","abonniere");
+  add_action("MoveMessage","verleg");
+  add_action("MoveMessage","verlege");
+  add_action("SetNNADWMSG","zeitungsmeldung");
+  add_action("Ignore","ignorier");
+  add_action("Ignore","ignoriere");
+  add_action("MailMessage","versende");
+  add_action("verbrennen","verbrenne");
+
+  if (IS_SEER(TP)) {
+    add_action("Mail","mail");
+  }
+  if (IS_LEARNER(TP)) {
+    add_action("SaveMessage","speicher");
+    add_action("SaveMessage","speichere");
+    add_action("ReadNextUnread","read"); /* NF Compatibility Special */
+  }
+  if (IS_ELDER(TP))
+  {
+    add_action("MoveTrash","trash");
+  }
+  init_rescue();
+}
+
+verbrennen(str) {
+  if (!str || !id(str)) return 0;
+  write("Du verbrennst Deine Zeitung mit groesstem Vergnuegen.\n");
+  say(TP->Name(WER)+" verbrennt "+
+    TP->QueryPossPronoun(FEMALE, WEN, SINGULAR)+" Zeitung in einem "
+    "Freudenfeuer.\n");
+  remove(1);
+  return 1;
+}
+
+// KillGroup - loescht eine nicht mehr benoetigte Rubrik aus der Liste.
+// Aufruf aus rubriken() und ReadNextUnread(), wenn die gueltige
+// Rubrikenliste sowieso schon abgerufen wurde.
+
+static KillGroup(name) { read_until=m_copy_delete(read_until,name); }
+
+/* RebuildGroupList() - tut doch nicht so, weil /secure/news anders arbeitet.
+ * Bleibt vorerst zum Nachschlagen.
+static RegisterGroup(name) { read_until[name]=1; }
+static RebuildGroupList() {
+  mixed groups;
+  if ((pointerp(groups=NEWSSERVER->GetGroups()))&&(sizeof(groups))) {
+    map(m_indices(read_until)-groups-SYSTEMGROUPS,#'KillGroup);
+    map(groups-m_indices(read_until),#'RegisterGroup);
+  }
+  TP->SetProp(P_READ_NEWS,read_until);
+}    
+*/
+
+
+_set_autoloadobj(mixed arg) {
+
+  if (pointerp(arg) && sizeof(arg)>=2)
+  {
+    read_until=arg[1];
+  }
+  else {
+    if (TP)
+       read_until=((TP->QueryProp(P_READ_NEWS))||([]));
+  }
+
+  if (TP) TP->SetProp(P_READ_NEWS,read_until);
+}
+
+_query_autoloadobj() {
+  return 1;
+}
+
+static Mail(str) {
+  object mailer;
+  if (this_interactive()!=this_player()) return 0;
+  mailer=clone_object(MAILER);
+  mailer->SetOfficeName("mpa Kurierdienst");
+  mailer->do_mail( ((!str)||(str=="mail")) ? 0 : TP->_unparsed_args() );
+  return 1;
+}
+
+static varargs string Message2string(mixed msg,mixed messages,int flag,string group) {
+  // Aufrufe: (Nummer,Notes[,flag[,group]]) oder (Note,Notes[,flag[,group]])
+  // flag: M2S_VT100 : ggf ANSI-Codes verwenden
+  //       M2S_REMOTE: Rubrik und Artikelnummer ausgeben (speichern, versenden)
+  // Achtung: Wenn flag&M2S_REMOTE, muss msg int sein
+  // group: Name der Rubrik, wenn nicht aktuelle Rubrik. Nur bei M2S_REMOTE
+
+  string txt,hs,s,*m,s2;
+  int i,hi,thisnr,ansiflag;
+
+  if (flag&M2S_REMOTE) txt="Rubrik: "+(group?group:GROUP)+", Artikel: "+
+    (intp(msg)?to_string(msg+1):"???")+" von "+sizeof(messages)+"\n";
+  else txt="";
+
+  if (intp(msg)) {
+    thisnr=msg;
+    msg=messages[msg];
+  } else thisnr=-1;
+  if (!msg) return 0;
+
+  ansiflag=(flag&M2S_VT100)&&(this_player()->QueryProp(P_TTY)!="dumb");
+
+  txt += (ansiflag?ANSI_BOLD:"")+ msg[M_TITLE]+(ansiflag?ANSI_NORMAL:"")+
+    " ("+msg[M_WRITER]+", "+
+    dtime(msg[M_TIME])[5..26]+"):\n";
+//  if (geteuid(TP)=="sitopanaki") txt+="TID="+GetTID(msg)+"\n"; // Debug
+  if (!IS_STATUSLINE(msg[M_MESSAGE]))
+    return txt+"\n"+msg[M_MESSAGE];
+  m=explode(msg[M_MESSAGE],"\n");
+  while (IS_STATUSLINE(m[0])) {
+//    txt+=m[0]+"\n"; // ###
+    if (sscanf(m[0],"%s rn=%s rt=%d rg=%s",hs,s,hi,hs)==4 ||
+        sscanf(m[0],"%s rn=%s rt=%d",hs,s,hi)==3)
+    {
+      int nr,h;
+      nr=-1;
+      if (pointerp(messages))
+      {
+        for (h=((thisnr>=0) ? thisnr-1 : sizeof(messages)-1);
+             (h>=0 && messages[h][M_TIME]>=hi);h--) 
+          if (messages[h][M_TIME]==hi &&
+              lower_case(messages[h][M_WRITER])==lower_case(s))
+            nr=h;
+      }
+      txt+="[Bezug: Artikel "+((nr>=0) ? (nr+1) : 
+         (hs==STATUSESCAPE||hs==(group?group:GROUP)?"(geloescht?)":"in "+hs))+
+         " von "+capitalize(s)+" vom "+dtime(hi)[5..26]+"]\n";
+    }
+    if (sscanf(m[0],"%s on=%s ot=%d og=%s",hs,s,hi,hs)==4) {
+      txt+="[Autor: "+s+", "+dtime(hi)[5..26]+", verlegt von "+hs+"]\n";
+    }
+    m=m[1..];
+  }
+  return txt+"\n"+implode(m,"\n");
+}
+
+static varargs lies(mixed str) {
+  mixed num;
+  mixed *messages;
+  int tid;
+
+  if (str=="?"||str=="-?") return 
+    write("Syntax: lies <nr>\n"
+          "        artikel <nr>\n"
+          "Siehe auch: nn\n"),1;
+
+  if (intp(str)) num=str;
+  if ((!num && (!str || str=="" || sscanf(str,"%d",num)!=1)) || num<=0) {
+    notify_fail("WELCHE Nachricht willst Du lesen?\n");
+    return 0;
+  }
+  if (!pointerp(messages=NEWSSERVER->GetNotes(GROUP)))
+    return notify_fail("Seltsam, die Rubrik '"+GROUP+
+        "' gibt es nicht mehr...\n"), 0;
+  num--;
+  if (sizeof(messages)<=num) {
+    notify_fail("So viele Artikel sind da nicht!\n");
+    return 0;
+  }
+  
+  lasttitle=({messages[num][M_TITLE],messages[num][M_WRITER],
+      messages[num][M_TIME],GetTID(messages[num]),GROUP,num});
+  this_player()->More(Message2string(messages[num],messages,M2S_VT100));
+  if (this_player() && IS_LEARNER(this_player()))
+    this_player()->save_me(1);
+  return 1;
+}
+
+static varargs mixed GetGroupName(mixed g,mixed groups) {
+  /* Name einer Rubrik. g ist int oder string, enthaelt Name oder Nummer
+     (ab 1 numeriert) */
+  mixed i,expr,gg;
+  if (!g) return write("Du solltest schon die Rubrik angeben.\n"),0;
+  if (!groups) groups=NEWSSERVER->GetGroups();
+  if (intp(i=g) || sscanf(g,"%d",i)) {
+    if (i>0 && i<=sizeof(groups)) return groups[i-1];
+    write("Eine Rubrik mit der Nummer "+i+" gibt es leider nicht.\n");
+    return 0;
+  }
+  g=lower_case(g);
+  switch(g){
+  case ".": return GROUP;
+  case "+": return groups[(member(groups,GROUP)+1)%sizeof(groups)];
+  case "-": 
+    return groups[(member(groups,GROUP)-1+sizeof(groups))%sizeof(groups)];
+  }
+
+  // Existiert die Rubrik genau so?
+  if (member(groups,g)>-1) return g;
+
+  g = regreplace(g,"[[\\]\\*()?]","",1);
+  // haerteres Kriterium: Alle Abschnitte angegeben
+  expr="^"+implode(explode(g,"."),"[^\\.]*\\.")+"[^\\.]*$";
+//  write("REGEXP="+expr+"\n");
+  gg=regexp(groups,expr);
+  if (sizeof(gg)==1) return gg[0];
+
+  // weicheres Kriterium: Nicht alle Abschnitte angegeben
+  expr="^(.*\\.)*"+implode(explode(g,"."),".*\\.")+".*$";
+//  write("REGEXP="+expr+"\n");
+  gg=regexp(groups,expr);
+
+  if (!sizeof(gg)) {
+    write("Eine Rubrik '"+g+"' gibt es hier leider nicht.\n");
+    return 0;
+  }
+
+  if (sizeof(gg)==1) return gg[0];
+  
+  write(break_string("Die Rubrik "+g+" ist nicht eindeutig. Wahrscheinlich "
+        "meinst Du eine der folgenden: "+implode(gg,", ")+".\n",78));
+  return 0;
+}
+
+static rubrik(str)
+{
+  mixed *gruppen;
+  mixed news;
+  int anz,i;
+  
+  if (str=="?"||str=="-?") return
+    write("Syntax: rubrik <rubrik>\n"
+          "  wechselt die aktuelle Rubrik. Es wird die Nummer der Rubrik,\n"
+          "  ihr Name oder jede eindeutige Abkuerzung akzeptiert.\n"),1;
+
+  if (!str || str==0) {
+      if (!pointerp(news=NEWSSERVER->GetNotes(GROUP))){
+      GROUP=DEFAULTGROUP;
+    if (!pointerp(news=NEWSSERVER->GetNotes(GROUP)))
+      return notify_fail("Seltsam, irgendwie geht hier einiges schief...\n"),0;
+      }
+    return write("Aktuelle Rubrik: "+GROUP+" ("+sizeof(news)+" Artikel).\n"),1;
+  }
+  str=GetGroupName(str);
+  if (!str) return 1;
+  GROUP=str;
+  news=NEWSSERVER->GetNotes(GROUP);
+  write(break_string("Ok, Du hast die Rubrik "+GROUP+" mit "+sizeof(news)+
+                     " Artikel"+(sizeof(news)==1?"":"n")+" aufgeschlagen.\n",
+                     78));
+  return 1;
+}
+
+LiesArtikel(str) {
+  string s1;
+  int i1;
+  if ( !str ) return 0;
+  if (sscanf(str,"rubrik %s",s1))
+    return rubrik(s1);
+   if (sscanf(str,"%d",i1))
+    return lies(to_int(i1));
+  if (sscanf(str,"artikel %s",s1))
+    return lies(s1);
+}
+
+
+/* Ueberpruefe, ob in Rubrik tote threads ignoriert werden. Tot bedeutet,
+   dass der letzte darin uebergangene Artikel bereits expired ist UND
+   keine Artikel aus diesem thread mehr vorhanden sind.
+   ACHTUNG: Es kann passieren, dass dazwischen noch ein neuer Artikel
+   dazugekommen ist.
+   Hier wird auf expired geprueft und ggf Variable deadTID gesetzt. Wird beim
+   Lesen mit nn ein Artikel mit dieser TID gefunden (passiert direkt hinter
+   Pruefung), wird deadTID wieder auf 0 gesetzt. Ist deadTID beim Umschalten
+   auf naechste Rubrik !=0, ist der thread tot.
+*/
+
+static int CheckThreads(string rubrik,int timeout) {
+  mixed tids;
+  int i;
+
+//  deadTID=0;
+  tids=m_indices(read_until[IGNOREGROUP][rubrik]);
+  for (i=sizeof(tids)-1;i>=0&&!deadTID;i--)
+    if (read_until[IGNOREGROUP][rubrik][tids[i]]<timeout) deadTID=tids[i];
+  return 1;
+}  
+
+static int rubriken(mixed arg)
+{
+  mixed *gruppen, *messages;
+  mixed news;
+  int anz,i,lasttime,timeout,foundone;
+  string s;
+  
+  if (arg=="?"||arg=="-?") return
+    write("Syntax: rubriken        listet alle Rubriken\n"
+          "        rubriken neu    nur Rubriken mit ungelesenen Artikeln\n"),1;
+
+  gruppen=NEWSSERVER->GetGroups();
+  map(m_indices(read_until)-gruppen-SYSTEMGROUPS,#'KillGroup); // ');
+  s="\nEs gibt zur Zeit ";
+  anz=sizeof(gruppen);
+  if (anz==0) {
+    write(s+"keine Rubriken (wie seltsam ...)\n");
+    return 1;
+  }
+  s+=anz+" Rubrik"+(anz==1 ? "" : "en")+".";
+  if (arg=="neu") s+="\nDavon enthalten neue Artikel:\n\n";
+  else s+="\n(* oder x: enthaelt neue Artikel, x oder -: abbestellt, "
+    ">: aktuelle Rubrik)\n\n";
+  for (i=0;i<anz;i++) {
+    timeout=read_until[gruppen[i]];
+    /* GetNewsTime lieferte leider manchmal was falsches :-(  */
+    /* jetzt hoffentlich richtig? Wenn nicht, das if ausklammern */
+    if ( arg!="neu" || (lasttime=NEWSSERVER->GetNewsTime(gruppen[i])) > timeout) {
+      messages=NEWSSERVER->GetNotes(gruppen[i]);
+      if (!messages || !sizeof(messages)) lasttime=0;
+      else lasttime=messages[sizeof(messages)-1][M_TIME];
+      foundone=1;
+    }
+    if (arg!="neu" || (timeout>=0 && lasttime>abs(timeout)))
+      s+=sprintf("%s%s%3d\. %-39.39s: ",
+                 ((gruppen[i]==GROUP)?">":" "),
+                 ((lasttime>abs(timeout)) ? ((timeout<0) ? "x" : "*")
+                                          : ((timeout<0) ? "-" : " ") ),
+                 i+1,gruppen[i])+ 
+         (lasttime ? sprintf("%3d Artikel (%s)\n",
+                             sizeof(messages),
+                             // ((sizeof(messages)==1) ? ".  " : "en."),
+                             dtime(lasttime)[5..12]+ctime(lasttime)[<2..]) :
+                             "        - leer -\n");
+  }
+  if (arg=="neu"&&!foundone) s+="Keine Rubrik enthaelt neue Artikel.\n";
+  this_player()->More(s);
+  return 1;
+}
+
+#define M_READNEXT  1
+#define M_LISTNEW   2
+#define M_LISTNEWGR 3
+#define M_READGR    4
+
+static ReadNextUnread(str)
+{
+  string *groups,group;
+  mixed *messages;
+  int curgr,curmsg,timeout,start,nrgroups,sog,mode;
+
+  if (str=="mail") return Mail(0); /* NF Compatibility Special */
+
+  if (str=="?"||str=="-?") return
+    write("Syntax: nn             naechster neuer Artikel\n"
+          "        nn <rubrik>    in entspr. Rubrik, wenn da was neu ist\n"
+          "        nn rubriken    Liste der Rubriken mit neuen Artikeln\n"
+          "        nn liste       Liste der ungelesenen Artikel\n"),1;
+
+  groups=NEWSSERVER->GetGroups();
+  deadTID=0;
+  map(m_indices(read_until)-groups-SYSTEMGROUPS,#'KillGroup); //'
+  nrgroups=sizeof(groups);
+  if (str && str!="rubriken" && str!="liste"){
+    if (!group=GetGroupName(str)) return 1;
+  }
+  else
+    group=0;
+  if (group && (curgr=member(groups,group)+1)) {
+    --curgr;
+    if (curgr<0 || curgr>=sizeof(groups)) 
+      return notify_fail("Nee... so eine Rubrik gibts hier nicht.\n"),0;
+    GROUP=group;
+    start=curgr+1;
+    mode=M_READGR;
+    write("Rubrik "+(curgr+1)+": "+GROUP+"\n");
+  } else {
+    switch (str) {
+    case 0: mode=M_READNEXT; break;
+    case "liste": mode=M_LISTNEW; write("Du hast noch nicht gelesen:\n"); break;
+    case "rubriken": return rubriken("neu");
+    default:
+      notify_fail("\
+Diesen Parameter verstehe ich nicht. Entweder gar nichts, \"liste\"\n\
+\"rubriken\", oder Name bzw. Nummer einer Rubrik.\n");
+      return 0;
+    }
+    curgr=member(groups,GROUP);
+    start=curgr+nrgroups;
+  }
+  if (!pointerp(messages=NEWSSERVER->GetNotes(GROUP))){
+    GROUP=DEFAULTGROUP;
+    if (!pointerp(messages=NEWSSERVER->GetNotes(GROUP)))
+    return notify_fail("Seltsam, irgendwie geht hier einiges schief...\n"),0;
+  }
+  timeout=read_until[GROUP];
+  curmsg=0;
+  sog=sizeof(messages);
+  while (curgr<start) {
+    ++curmsg;
+    if (curmsg>sog) {
+      if (deadTID)
+        read_until[IGNOREGROUP][GROUP]=
+          m_copy_delete(read_until[IGNOREGROUP][GROUP],deadTID);
+      ++curgr;
+      deadTID=0;
+      if (mode!=M_READGR) {
+        GROUP=groups[curgr%nrgroups];
+        timeout=read_until[GROUP];
+        if (!timeout) read_until[GROUP]=1;  // Nimm neue Gruppen in Liste auf
+        if (timeout<0 || timeout>=NEWSSERVER->GetNewsTime(GROUP)) {
+          sog=0;    /* Ueberlistung: Gruppe hat nix neues oder */
+          curmsg=1; /* ist unsubscribed */
+        }
+        else {
+          messages=NEWSSERVER->GetNotes(GROUP);
+          curmsg=0;
+          sog=sizeof(messages);
+        }
+      }
+    } else {
+      if ((timeout>=0 || mode==M_READGR) && messages[curmsg-1][M_TIME] > abs(timeout)) {
+        if (pointerp(this_player()->QueryProp(P_IGNORE)) &&
+            member(this_player()->QueryProp(P_IGNORE),
+                   lower_case(messages[curmsg-1][M_WRITER])+".news") != -1) {
+          printf("Uebergehe ignorierten Artikel %d von %s in Rubrik %s.\n",
+                 curmsg,messages[curmsg-1][M_WRITER],GROUP);
+          read_until[GROUP]=messages[curmsg-1][M_TIME];
+          if (TP) TP->SetProp(P_READ_NEWS,read_until);
+        } else if 
+          (read_until[IGNOREGROUP]&&
+           read_until[IGNOREGROUP][GROUP]&&
+           CheckThreads(GROUP,messages[0][M_TIME])&& /* Tote threads weg */
+           read_until[IGNOREGROUP][GROUP][GetTID(messages[curmsg-1])]) {
+            printf("Uebergehe Artikel %d aus ignoriertem Thread.\n",curmsg);
+            read_until[IGNOREGROUP][GROUP][GetTID(messages[curmsg-1])]=
+              messages[curmsg-1][M_TIME];
+            if (deadTID&&deadTID==GetTID(messages[curmsg-1])) deadTID=0;
+            read_until[GROUP]=messages[curmsg-1][M_TIME];
+            if (TP) TP->SetProp(P_READ_NEWS,read_until);
+        } else {
+          write("\nRubrik "+(curgr%nrgroups+1)+": "+GROUP+", Artikel: "+curmsg+" von "+sog+"\n");
+          if (mode==M_LISTNEW) {
+            write(sprintf("  %-45s [%-11s] %s\n",messages[curmsg-1][M_TITLE],
+                          messages[curmsg-1][M_WRITER],
+                          dtime(messages[curmsg-1][M_TIME])[5..16]));
+          } else { /* mode == M_READNEXT || mode==M_READGR */
+            if (timeout>=0) read_until[GROUP]=messages[curmsg-1][M_TIME];
+            else read_until[GROUP]=-messages[curmsg-1][M_TIME];
+            if (TP) TP->SetProp(P_READ_NEWS,read_until);
+            return (lies(""+curmsg));
+          }
+        }
+      }
+      /* sonst mach einfach garnix. Schleife laeuft weiter. */
+    }
+  }
+  switch(mode) {
+    case M_LISTNEW:  return 1;
+    case M_READNEXT: write((read_until[NNADWMSG]||"Nix Neues auf der Welt.")
+                           +"\n"); break;
+    case M_READGR:   write("Nix Neues in dieser Rubrik.\n"); break;
+  }
+  return 1;
+}
+
+
+static SetNNADWMSG(str) {
+  if (str=="?"||str=="-?") return
+    write("Syntax: zeitungsmeldung <neue Meldung>    setzt Meldung\n"
+          "        zeitungsmeldung                   loescht Meldung\n"),1;
+  if (!read_until[NNADWMSG]) {
+    write("Du hast zur Zeit keine eigene NNADW-Meldung definiert.\n");
+    if (!str) return 1;
+  }
+  else write("Deine alte NNADW-Meldung war:\n"+read_until[NNADWMSG]+"\n");
+  if (!str) {
+    read_until=m_copy_delete(read_until,NNADWMSG);
+    write("Meldung ist geloescht, es gilt wieder die Standardmeldung.\n");
+  } else {
+    read_until[NNADWMSG]=this_player()->_unparsed_args();
+    write("Deine neue Meldung lautet jetzt:\n"+read_until[NNADWMSG]+"\n");
+  }
+  if (TP) TP->SetProp(P_READ_NEWS,read_until);
+  return 1;
+}    
+
+varargs int InterpretTime(mixed a,int flag) {
+  // string oder string *
+  // akzeptiert folgende Formate:
+  //   dd.mm.jj     (Rueckgabe: 0:00 des entsprechenden Tages)
+  //   vor [<anz> d|tagen] [<anz> h|stunden] [<anz> m|minuten]
+  // flag=1: "inklusive": bei dd.mm.jj-Format 23:59:59 statt 0:00 Uhr
+
+  int i,j,k,t,nrargs;
+  string s;
+  if (stringp(a)) a=explode(a," ");
+
+//  printf("%O\n",a);
+
+  if ((nrargs=sscanf(a[0],"%d.%d.%d",i,j,k))==3 || 
+      (nrargs=sscanf(a[0],"%d.%d.",i,j))==2) {
+    // Datum -> Zeit: Funktioniert im Zeitraum 1973 - ca. 2090
+    //                in Zeitzonen mit ganzen Stunden ggue Rechneruhr.
+    if (nrargs==2) 
+      k=70+time()/31536000;
+    if (k<70) k+=100;
+    if (k>1970) k-=1900;
+    if (k<70||k>150) return
+      write("Unzulaessiges Jahr (erlaubt: 70-heute).\n"),0;
+    t=(k-70)*31536000;
+
+    if (i<1||i>31) return write("Unzulaessiger Tag (erlaubt: 1-31).\n"),0;
+    if (j<1||j>12) return write("Unzulaessiger Monat (erlaubt: 1-12).\n"),0;
+//    printf("%d.%d.%d\n",i,j,k);
+    s=ctime(t);
+    if ((j>2) && !(k%4)) t+=86400;    // Schaltjahrkorrektur fuer Monate>=3
+    t+=({        0,  2678400,  5097600,  7776000,
+          10368000, 13046400, 15638400, 18316800,
+          20995200, 23587200, 26265600, 28857600})[j-1];
+    t+=86400*(i-1);
+    t+=86400*(32-to_int(s[8..9]));  // Schaltjahrkorrektur
+    t-=3600*to_int(s[11..12]);      // Zeitzonenkorrektur
+    t-=3600*to_int(ctime(t)[11..12]);      // Sommerzeitkorrektur
+//    write("Kontrolle: "+dtime(t)+"\n");
+    if (nrargs==2 && t>time()) t-=31536000;
+    return (flag?t+86399:t);
+  }
+
+  t=0;
+  if (a[0]=="vor") for (i=sizeof(a)-1;i>0;i--) {
+    switch (a[i]) {
+    case "m": 
+    case "minuten": 
+    case "min": 
+    case "minute":
+      t+=60*to_int(a[i-1]);
+      break;
+    case "h": 
+    case "stunde": 
+    case "stunden": 
+    case "s":
+      t+=3600*to_int(a[i-1]);
+      break;
+    case "d": 
+    case "tag": 
+    case "tage": 
+    case "t":
+      t+=86400*to_int(a[i-1]);
+      break;
+    default: 
+      if (!to_int(a[i]))
+        write("Argumentfehler: Kann nichts mit '"+a[i]+"' anfangen.\n");
+    }
+    return time()-t;
+  }
+  else return write("Argumentfehler.\n"),0;
+}
+
+static Catchup(string str)
+{
+  int welche,zeit,i;
+  string gr;
+  mixed groups,news,args;
+
+  if (!pointerp(NEWSSERVER->GetNotes(GROUP)))
+    return notify_fail("Seltsam, die Rubrik '"+GROUP+
+        "' gibt es nicht mehr...\n"), 0;
+
+  str=this_player()->_unparsed_args();   // wg. Datum
+  if (str) str=lower_case(str);
+  else str=GROUP; // default: aktuelle Rubrik komplett.
+
+  if (str=="?"|| str=="-?") return CatchupSyntax();
+
+  // uebergehe Antworten (Thread ignorieren)
+  if (str&&(str[0..6]=="antwort"||str=="thread")) {
+    if (!pointerp(lasttitle)) return 
+      write("Du hast bisher noch keinen Artikel gelesen, damit kann ich nicht wissen,\nwelchen Thread du uebergehen willst.\n"),1;
+    if (!read_until[IGNOREGROUP]) read_until[IGNOREGROUP]=([]);
+    if (!read_until[IGNOREGROUP][GROUP]) read_until[IGNOREGROUP][GROUP]=([]);
+    if (read_until[IGNOREGROUP][GROUP][lasttitle[3]]) {
+      read_until[IGNOREGROUP][GROUP]=m_copy_delete(read_until[IGNOREGROUP][GROUP],lasttitle[3]);
+      write("Dieser Thread wird jetzt nicht mehr uebergangen.\n");
+    } else {
+      read_until[IGNOREGROUP][GROUP][lasttitle[3]]=lasttitle[2];
+      write("Dieser Thread wird ab sofort uebergangen.\nFalls das ein Irrtum war, wiederhole den Befehl augenblicklich.\n");
+    }
+    if (TP) TP->SetProp(P_READ_NEWS,read_until);
+    return 1;
+  }    
+
+  groups=NEWSSERVER->GetGroups();
+
+  args=explode(str," ");
+
+  // Uebergehe alles
+
+  if (args[0]=="alle" || args[0]=="alles" || args[0]=="all") {
+    if (sizeof(args)<=1) zeit=time()-1;
+    else if (args[1]!="bis") return CatchupSyntax();
+    else if (sizeof(args)<3) return CatchupSyntax();
+
+    else zeit=InterpretTime(args[2..],1);
+    if (zeit>time()) zeit=time()-1;
+    write("Uebergehe alle Rubriken bis "+dtime(zeit)+".\n");
+    for (welche=0;welche<sizeof(groups);++welche) {
+      gr=groups[welche];
+//      zeit=NEWSSERVER->GetNewsTime(gr);
+      if (abs(read_until[gr])<zeit) 
+        read_until[gr]=(read_until[gr]>=0)?zeit:-zeit;
+      if (TP) TP->SetProp(P_READ_NEWS,read_until);
+    }
+    return 1;
+  }
+
+  // Anzahl Artikel
+  
+  if (sizeof(args)>=2 && args[1]=="artikel") {
+    if (!to_int(args[0])) return CatchupSyntax();
+    news=NEWSSERVER->GetNotes(GROUP);
+    for (i=sizeof(news)-1;i>=0&&news[i][M_TIME]>read_until[GROUP];i--);
+    welche=i+to_int(args[0]);
+    if (welche>=sizeof(news)) welche=sizeof(news)-1;
+    write("Uebergehe die naechsten "+(welche-i)+" Artikel in Rubrik "+
+        GROUP+"\n.");
+    if (welche>=0) {
+      zeit=news[welche][M_TIME];
+      read_until[GROUP]=(read_until[GROUP]>=0)?zeit:-zeit;
+      if (TP) TP->SetProp(P_READ_NEWS,read_until);
+    }
+    return 1;
+  }
+
+  // einzelne Rubrik.
+
+  if (!(gr=GetGroupName(args[0]))) return 1;
+  news=NEWSSERVER->GetNotes(gr);
+  if (!sizeof(news)) {
+    write("Rubrik "+gr+" ist leer.\n");
+    return 1;
+  }
+//  zeit=news[sizeof(news)-1][M_TIME];
+  if (sizeof(args)<=1)
+    zeit=time();
+  else
+    if (args[1]!="bis") return CatchupSyntax();
+  else 
+    zeit=InterpretTime(args[2..],1);
+  if (zeit>time()) zeit=time();
+  read_until[gr]=zeit;
+  if (TP) TP->SetProp(P_READ_NEWS,read_until);
+  write("Uebergehe "+gr+" bis "+dtime(zeit)+",\nletzter Artikel war vom "+
+      dtime(NEWSSERVER->GetNewsTime(gr))+"\n");
+  return 1;
+}
+
+
+static Ignore(str) {
+  if (str=="thread"||str=="antworten") return Catchup(str);
+  return 0;
+}
+
+static CatchupSyntax() {
+  write("Syntax des Befehls uebergehe (oder catchup):\n"
+        "  uebergehe [rubrik]              (default: aktuelle Rubrik)\n"
+        "  uebergehe alles                 (in allen Rubriken)\n"
+        "  uebergehe <anz> artikel         (in akt. Rubrik)\n"
+        "  uebergehe [rubrik]|alles bis <tag>.<monat>.[<jahr>]\n"
+        "  uebergehe [rubrik]|alles bis vor <zeit>        wobei\n"
+        "      <zeit> = [<n> d|tage] [<n> h|stunden] [<n> m|min|minuten]\n"
+        "  uebergehe thread|antworten      (entspr. 'ignoriere thread')\n");
+
+  return 1;
+}
+
+
+static int UncatchupSyntax()
+{
+  notify_fail(
+    "Syntax: wiederhole <anz> [artikel]\n"
+    "        wiederhole [ab vor] [<anz> m|minute[n]] [<anz> h|stunde[n]] [<anz> d|tag[e]]\n"
+    "        wiederhole ab tag.monat[.jahr]\n"
+    "        wiederhole alles\n"
+    "Der wiederhole- oder uncatchup-Befehl bezieht sich immer auf die aktuelle\n"
+    "Rubrik und markiert die angegebenen Artikel wieder als ungelesen.\n"
+    "Zeiten (2. Syntax) sind rueckwaerts ab aktueller Uhrzeit gerechnet.\n"),0;
+  return 0;
+}
+
+
+static Uncatchup(string str)
+{
+  mixed args;
+  int i,zeit;
+  int mode; // 0 = nix, 1=Anzahl Artikel, 2=Zeit, 3=alles
+
+  str=this_player()->_unparsed_args();   // wg. Datum
+  if ( !TP || !str || !stringp(str) || str=="?" || str=="-?" )
+    return UncatchupSyntax();
+
+  if (!pointerp(NEWSSERVER->GetNotes(GROUP)))
+    return notify_fail("Seltsam, die Rubrik '"+GROUP+
+        "' gibt es nicht mehr...\n"), 0;
+
+  args=({""})+explode(lower_case(str)," ");
+  if (args[1]=="ab") {
+    mode=2;
+    if (sizeof(args)<3)
+      return UncatchupSyntax();
+    zeit=InterpretTime(args[2..]);
+  } else {
+    zeit=time();
+    for (i=sizeof(args)-1;i>0;i--) {
+      switch(args[i]){
+      case "alles":
+      case "alle":
+      case "all":
+        if (mode&&mode!=3) return
+          notify_fail("Bitte nur Zeit ODER alles ODER Anzahl angeben!\n"),0;
+        mode=3;
+        break;
+      case "minuten":
+      case "minute":
+      case "m":
+      case "stunden":
+      case "stunde":
+      case "h":
+      case "tage":
+      case "tag":
+      case "d":
+        if (mode&&mode!=2) return
+          notify_fail("Bitte nur Zeit/Datum ODER alles ODER "
+              "Anzahl angeben!\n"),0;
+        mode=2;
+        zeit-=(((args[i][0]=='m') ? 60 :
+                ((args[i][0]=='s' || args[i][0]=='h') ? 3600 : 86400))
+               *to_int(args[i-1]));
+        i--;
+        break;
+      case "artikel":
+        if (mode&&mode!=1) return 
+          notify_fail("Bitte nur Zeit/Datum ODER alles ODER "
+              "Anzahl angeben!\n"),0;
+        mode=1;
+        zeit=to_int(args[i-1]);
+        i--;
+        break;
+      case "ab":
+        return 
+          notify_fail("Bitte nur Zeit/Datum ODER alles ODER "
+              "Anzahl angeben!\n"),0;
+      default:
+        if (!to_int(args[i])) 
+          return notify_fail("Unbekanntes Argument '"+args[i]+
+                             "'! Aktion abgebrochen.\n"),0;
+        if (mode&&mode!=1) return 
+          notify_fail("Bitte nur Zeit/Datum ODER alles ODER "
+              "Anzahl angeben!\n"),0;
+        mode=1;
+        zeit=to_int(args[i]);
+      }
+    }
+  }
+
+  switch(mode){
+  case 0:
+    return notify_fail("Gib bitte irgendeine Einheit an "
+        "(Hilfe mit wiederhole -?)!\n");
+  case 2:
+    read_until[GROUP]=(read_until[GROUP]>=0)?zeit:-zeit;
+    write(break_string("Markiere alle Artikel in Rubrik "+GROUP+
+          " seit "+dtime(zeit)+" als ungelesen.\n",78));
+    break;
+  case 3:
+    read_until[GROUP]=(read_until[GROUP]>=0?1:-1);
+    write("Markiere die komplette Rubrik "+GROUP+" als ungelesen.\n");
+    break;
+  case 1:
+    write(break_string("Markiere die letzten "+zeit+
+          " gelesenen Artikel in Rubrik "+GROUP+" als ungelesen.\n",78));
+    { int h; mixed notes;
+      notes=NEWSSERVER->GetNotes(GROUP);
+      h=sizeof(notes)-1;
+      while ( (h>=0) && (abs(read_until[GROUP]) < notes[h][M_TIME]) ) {
+        h--;
+      }
+      if (h==-1||h<zeit)
+        read_until[GROUP]=
+          (read_until[GROUP]>=0)?1:-1;
+      else
+        read_until[GROUP]=(
+          (read_until[GROUP]>=0)?notes[h-zeit][M_TIME]
+            :-notes[h-zeit][M_TIME]);
+    }
+  }
+  write("Ok. Du kannst die als ungelesen markierten Artikel "
+      "mit nn nochmal lesen.\n");
+  
+  if (TP) TP->SetProp(P_READ_NEWS,read_until);
+  return 1;
+}
+
+
+QueryRead() { 
+  return read_until; 
+}
+
+static CatchNewsError(int err,string text4minus3) {
+  switch (err) {
+    case  1: return 1;
+    case -1: write("Du darfst in dieser Rubrik nicht schreiben!\n"); return 0;
+    case -2: write("Die Rubrik gibt es nicht mehr, sehr seltsam...\n"); return 0;
+    case -3: write(text4minus3+"\n"); return 0;
+    default: write("Interner Fehler "+err+", Erzmagier verstaendigen!\n"); return 0;
+  }
+}
+
+static varargs schreib(str,pretext,called_by_itself,statuslines) {
+  int err;
+
+  if (str=="?"||str=="-?") {
+    write("Syntax: schreib <Titel>\n"
+          "  beginnt einen neuen Artikel in der Zeitung.\n");
+    return 1;
+  }
+  
+  if (!this_interactive() || !this_interactive()->query_real_name()) return 0;
+  if (!called_by_itself && extern_call() && !pretext)
+    str=this_interactive()->_unparsed_args()||"";
+  if (called_by_itself && str=="~q") {
+    write("Abgebrochen.\n");
+    return 1;
+  }
+  if (!CatchNewsError(
+                      NEWSSERVER->AskAllowedWrite(GROUP),
+                      "Diese Rubrik ist leider schon randvoll!")) return 1;
+  if (!called_by_itself)
+    write("Neuer Artikel in Rubrik "+GROUP+":\n");
+  if (!str || str=="" || str=="artikel") {
+    input_to("schreib", INPUT_PROMPT, "Titel des Artikels: ", pretext,1);
+    return 1;
+  }
+  // writer=this_interactive()->query_real_name();
+  message=allocate(6);
+  message[M_BOARD]=GROUP;
+  message[M_TITLE]=killctrl(str);
+  message[M_MESSAGE]=statuslines;
+  write("Titel ist: "+str+".\n\
+Gib jetzt Deinen Text ein,\n\
+** oder . wenn Du fertig bist, ~q zum Abbrechen, ~h fuer eine Hilfsseite.\n");
+  nedit("PostNote",pretext);
+  return 1;
+}
+
+static varargs Reply(string str,string newtitle) {
+  mixed dummy,replytitle,s;
+  int nr;
+
+  if ((dummy=(str||newtitle))=="?"||dummy=="-?") {
+    write("Der Antworte-Befehl ist doppelt belegt.\n"
+          "1. (Zeitung): Schreibe Antwort auf einen Artikel in der Zeitung.\n"
+          "   Syntax: antworte\n"
+          "           antworte auf artikel <nr> [neuer Titel]\n"
+          "           reply [auf artikel <nr> | to note <nr>] [neuer Titel]\n"
+          "2. aehnlich 'sage':\n"
+          "   Du tippst zum Beispiel:\n"
+          "     antworte ja, das weiss ich\n"
+          "   Alle Spieler im Raum sehen dann:\n"
+          "     <Dein Name> antwortet: ja, das weiss ich.\n"
+          "Bitte beachte, dass jede Syntax, die auf den antworte-Befehl der "
+          "Zeitung\npasst, auch von der Zeitung ausgewertet wird.\n");
+    return 1;
+  }
+
+  if (str&&
+      ((sscanf(lower_case(str),"auf artikel %d",dummy)==1 && 
+        str=this_player()->_unparsed_args()[12..])||
+       (sscanf(lower_case(str),"to note %d",dummy)==1 && 
+        str=this_player()->_unparsed_args()[8..]))) {
+    mixed notes;
+    notes=NEWSSERVER->GetNotes(GROUP);
+    if (dummy<1||dummy>sizeof(notes))
+      return write("Einen Artikel mit der Nummer "+dummy+
+                   " gibt es in dieser Rubrik nicht.\n"),1;
+    dummy--;
+    replytitle=({notes[dummy][M_TITLE],notes[dummy][M_WRITER],
+                   notes[dummy][M_TIME],GetTID(notes[dummy]),GROUP});
+    DEBUGVAR(str);
+    if (!newtitle&&str&&sizeof(str)&&sscanf(str,"%d %s",dummy,str)==2)
+      newtitle=str;
+  }
+  else if (!str||!sizeof(str)) {
+    if (!lasttitle) return
+      write("Du hast noch gar nichts gelesen, worauf Du "
+          "antworten koenntest.\n"),1;
+    replytitle=lasttitle;
+  }
+  else return 0;      
+
+// return ComposeReply(replytitle);
+//}
+//
+//
+//ComposeReply(mixed replytitle) {
+
+  if (!newtitle) {
+    if (replytitle[0][0..7]=="Re: Re: ") newtitle="Re^3: "+replytitle[0][8..];
+    else if (sscanf(replytitle[0],"Re^%d: %s",nr,newtitle))
+      newtitle="Re^"+(nr+1)+": "+newtitle;
+    else newtitle="Re: "+replytitle[0];
+  }
+  return schreib(newtitle,0,0,
+                 STATUSESCAPE+" rn="+replytitle[LAST_WRITER]+
+                              " rt="+replytitle[LAST_TIME]+
+                              " rg="+replytitle[LAST_GROUP]+"\n"+
+                 STATUSESCAPE+" tid="+replytitle[LAST_TID]+"\n");
+}
+
+static Reply2(str) {
+  str = this_player()->_unparsed_args();
+  if (!str||str[0..11]=="auf artikel "||str[0..7]=="to note ")
+    return Reply(str);
+  return Reply(0,str);
+}
+
+static void InformPlayers(string group,string player,string text)
+{
+  object *players;
+  int i;
+  mixed data;
+  string ig;
+
+  players=users();
+  ig=lower_case(player)+".news";
+  for (i=sizeof(players)-1;i>=0;i--) {
+    data=players[i]->QueryProp(P_WAITFOR);
+    if (pointerp(data)&&(member(data,"Mpa")>-1)) {
+      data=players[i]->QueryProp(P_READ_NEWS);
+      if (mappingp(data)&&(data[group]>0)) {
+        data=players[i]->QueryProp(P_IGNORE);
+        if ((!pointerp(data))||(member(data,ig)==-1))
+          tell_object(players[i],text);
+      }
+    }
+  }
+}
+
+static PostNote(text) {
+  int err;
+  string sig;
+
+  if (!text) {
+    write("Abbruch! Artikel landet im Reisswolf.\n");
+    return 1;
+  }
+  if (!sizeof(old_explode(text,"\n")-
+              ({"q","quit"," **","** ","ende","","exit"," "}) 
+              ) )
+    return write("\
+ACHTUNG: Wolltest Du wirklich einen Artikel ohne Inhalt in die mpa setzen?\n\
+Artikel ohne erkennbaren Inhalt werden NICHT veroeffentlicht. Bitte ueber-\n\
+pruef Deine Syntax, falls Du keinen Artikel schreiben wolltest, oder schreib\n\
+auch ein bisschen Text!\n\
+Artikel landet im Reisswolf.\n"),1;
+  if (!message[M_MESSAGE])
+    message[M_MESSAGE] = text;
+  else
+    message[M_MESSAGE]+=text;
+  if (sig=read_file("/players/"+geteuid(this_interactive())+"/.signature"))
+    message[M_MESSAGE]+=sig;
+  if (!CatchNewsError(NEWSSERVER->WriteNote(message),
+       "Diese Rubrik ist voll. Mist, da war jemand schneller...")) {
+    write("Versuch, Platz in der Rubrik zu finden, dann kannst Du mir ~r nochmal\nin den Text einsteigen und ihn anschliessend veroeffentlichen.\n(Vorschlag: Einen veralteten Artikel abhaengen oder verlegen.\n");
+    return 0;
+  }
+  write("OK, Artikel ist veroeffentlicht.\n");
+  say(capitalize(TP->name())+
+      " hat einen Artikel in der Zeitung veroeffentlicht.\n");
+  if (geteuid(TP)!="sitopanaki")
+    InformPlayers(message[M_BOARD],geteuid(this_interactive()),
+                "* MPA: Neuer Artikel von "+
+                capitalize(geteuid(this_interactive()))+
+                " in Rubrik \""+message[M_BOARD]+"\".\n");
+  message=0; /* Platz sparen! */
+  return 1;
+}
+
+
+//static // allowing aliasing *Rumata* 5/8/96
+inhalt(str) {
+  int i,endflag,timeout;
+  string *gruppen,s,txt,suche;
+  mixed messages;
+
+  if (str=="?"||str=="-?") return
+    write("Syntax: inhalt [<rubrik>] [ende] [suche <text>]\n"),1;
+
+  str=(!str)?({}):explode(str," ");
+
+  if (sizeof(str) && (str[0]=="neu" || str[0]=="neues") )
+    return ReadNextUnread("liste"),1;
+
+  if (endflag=member(str,"ende")+1) str-=({"ende"});
+  if (((i=member(str,"suche")) != -1) && (sizeof(str) > i)) {
+    suche=lower_case(implode(str[i+1..]," "));
+    str=str[0..i-1];
+  }
+  else
+    suche=0;
+  if (!sizeof(str)) {
+    str=GROUP;
+    if (!pointerp(messages=NEWSSERVER->GetNotes(str))){
+      str=GROUP=DEFAULTGROUP;
+    if (!pointerp(messages=NEWSSERVER->GetNotes(str)))
+      return notify_fail("Seltsam, irgendwie geht hier einiges schief...\n"),0;
+    }
+  }
+  else {
+    str=GetGroupName(str[0]);
+    if (!str) return 1;
+    messages=NEWSSERVER->GetNotes(str);
+  }
+  timeout=abs(read_until[str]);
+  s="Inhalt der Rubrik "+str+":\n\n";
+  if (!pointerp(messages) || !sizeof(messages))
+    return 
+      write(s+"Zur Zeit befinden sich keine Artikel in dieser Rubrik.\n"),1;
+  if (suche)
+    s+="Suche nach '"+suche+"' in der Rubrik ergab folgende Treffer:\n\n";
+  else
+    if (sizeof(messages)==1)
+      s+="Zur Zeit befindet sich ein Artikel in der Rubrik:\n\n";
+    else
+      s+="Zur Zeit befinden sich "+sizeof(messages)+
+        " Artikel in der Rubrik:\n\n";
+  if (endflag&&(sizeof(messages)>16)&&
+      messages[sizeof(messages)-16][M_TIME]>=timeout) timeout=-1;
+  for (i=(endflag?(((endflag=sizeof(messages)-15)<0)?0:endflag):0);
+       i<sizeof(messages);i++)
+  {
+    txt=sprintf("%2d.%s%-48s%4d (%-11s) %s\n",i+1,
+                (((timeout>=0) && timeout<messages[i][M_TIME] )?
+                 ( (timeout=-1),"*"):" "),messages[i][M_TITLE],
+                sizeof(explode(messages[i][M_MESSAGE],"\n")),
+                messages[i][M_WRITER],
+                dtime(messages[i][M_TIME])[5..11]);
+    if (!suche || (strstr(lower_case(txt), suche) != -1))
+      s+=txt;
+  }
+  if (endflag) write(s);
+  else this_player()->More(s);
+  return 1;
+}
+
+
+static artikel(str) {
+  return lies(str);
+}
+
+
+static loesche(str) {
+  int num;
+  mixed *messages;
+
+  if (str=="?"||str=="-?") return 
+    write("Syntax: loesche artikel <nr>\n"
+          "  (bezieht sich immer auf die aktuelle Rubrik.\n"),1;
+
+  if (!str || sscanf(str,"artikel %d",num)!=1 || num<=0) 
+   return notify_fail("WELCHEN Artikel willst Du loeschen ?\n"),0;
+  num--;
+  messages=(NEWSSERVER->GetNotes(GROUP));
+  if (sizeof(messages)<=num) return
+    notify_fail("So viele Artikel sind da nicht!\n"),0;
+  
+  write("Rubrik "+GROUP+", Artikel "+(num+1)+
+        " von "+capitalize(messages[num][M_WRITER])+
+        " vom "+dtime(messages[num][M_TIME])[5..26]+
+        ",\nTitel: "+messages[num][M_TITLE]+",\n\n");
+
+  /* (ueberfluessige Abfrage, macht schon /secure/news)
+  if (!IS_LEARNER(TI) && lower_case(messages[num][M_WRITER])!=geteuid(TI)) 
+    return 
+      write("Nicht geloescht - du darfst nur eigene Artikel loeschen.\n"),1;
+          */
+
+  switch (NEWSSERVER->RemoveNote(GROUP, num)){
+  case 1: write("Artikel ist geloescht.\n");
+    say(this_player()->name()+" loescht einen Artikel aus der Zeitung.\n");
+    return 1;
+  case -1: write("Diesen Artikel darfst Du nicht loeschen.\n");
+    say(this_player()->name()+" versucht vergeblich, einen Artikel zu loeschen.\n");
+    return 1;
+  case -3: write("So viele Artikel sind da nicht !\n");
+    return 1;
+  default: write("Interner Fehler. Bitte Erzmagier verstaendigen !\n");
+    return 1;
+  }  
+}
+
+// Low-level Funktion zum Abonnieren/Abbestellen von Rubriken
+// bestellen==0 -> abbestellen, bestellen!=0 -> bestellen
+// Rueckgabe: 0, wenn der gewuenschte Zustand schon eingestellt war, sonst 1
+private int _subscribe(string groupname, int bestellen) {
+
+  int timeout = read_until[groupname];
+  // gar nicht abonniert/abbestellt?
+  if (!bestellen && timeout < 0)
+    return 0;
+  else if (bestellen && timeout > 0)
+    return 0;
+
+  // wenn noch kein timeout, erstmal auf 1 setzen
+  timeout ||= 1;
+
+  // -1 fuer abbestellen, +1 fuer bestellen
+  if (bestellen)
+    read_until[groupname] = abs(timeout);
+  else
+    read_until[groupname] = -timeout;
+
+  if (environment())
+    environment()->SetProp(P_READ_NEWS, read_until);
+
+  return 1;
+}
+
+static Unsubscribe(str) {
+  int timeout;
+  if (str=="?"||str=="-?") return
+    write("Syntax: unsubscribe <rubrik>"
+          "  oder: bestelle <rubrik> ab\n"),1;
+  str=GetGroupName(str);
+  if (!str) return 1;
+  if (!_subscribe(str,0)) {
+    notify_fail("Die Rubrik hast Du gar nicht abonniert!\n");
+    return 0;
+  }
+  else {
+    write("Rubrik "+str+" abbestellt.\n");
+  }
+  return 1;
+}
+
+
+static Bestelle(str) { /* ab ! */
+  if (!str || !sscanf(str,"%s ab",str)) return _notify_fail(
+       "Die Syntax ist: 'bestelle <rubrik> ab', "
+       "oder meinst Du 'abonniere'?\n"),0;
+  return Unsubscribe(str);
+}
+
+static Subscribe(str) {
+  int timeout;
+  if (str=="?"||str=="-?") return
+    write("Syntax: abonniere <rubrik>\n"
+          "  oder: subscribe <rubrik>\n"),1;
+  str=GetGroupName(str);
+  if (!str) return 1;
+  if (!_subscribe(str,1)) {
+    notify_fail("Die Rubrik hast Du doch schon abonniert!\n");
+    return 0;
+  }
+  else {
+    write("Rubrik "+str+" abonniert.\n");
+  }
+  return 1;
+}
+
+// Legt die anfaenglichen Abonnements eines Neulesers fest und bestellt alle
+// anderen ab.
+private void InitialSubscriptions() {
+  // alle abbestellen. ;-)
+  // Alternative: fuer alle _subscribe(,0) rufen
+  if (!query_once_interactive(environment()))
+    return;
+  string *gruppen = NEWSSERVER->GetGroups();
+  int *vals = allocate(sizeof(gruppen),-1);
+  read_until = mkmapping(gruppen,vals);
+
+  // jetzt die vorausgewaehlten bestellen
+  foreach(string gruppe : INITIAL_SUBSCRIPTIONS) {
+    if (member(gruppen, gruppe) > -1)
+      _subscribe(gruppe,1);
+  }
+  // und ggf. noch die eigene Gildenrubrik
+  string gilde = environment()->QueryProp(P_GUILD);
+  if (stringp(gilde)
+      && member(gruppen, "gilden."+gilde) > -1)
+    _subscribe("gilden."+gilde,1);
+
+  environment()->SetProp(P_READ_NEWS, read_until);
+}
+
+static MoveMessage(str) {
+  int num,i;
+  mixed msg/*,expl*/;
+  string gr;
+  if (str=="?"||str=="-?") return
+    write("Syntax: verlege artikel <nr> nach <rubrik>\n"
+          "  Artikel und Rubrik muessen explizit angegeben werden.\n"),1;
+  if (!str || sscanf(str,"artikel %d nach %s",num,gr)!=2) return (int)notify_fail(
+        "verlege artikel <nr> nach <rubrik>, oder was?\n");
+  if (!(gr=GetGroupName(gr))) return 1;
+  if (!(CatchNewsError(NEWSSERVER->AskAllowedWrite(gr),"Die Rubrik ist leider voll.\n"))) return 1;
+
+  if (!pointerp(msg=NEWSSERVER->GetNotes(GROUP)))
+    return notify_fail("Seltsam, die Rubrik '"+GROUP+
+        "' gibt es nicht mehr...\n"), 0;
+
+  num--;
+  if (sizeof(msg)<=num) return
+    notify_fail("So viele Nachrichten sind da nicht !\n"),0;
+  msg=msg[num];
+  if (geteuid(TI) != lower_case(msg[M_WRITER])) {    
+    if (!IS_LEARNER(TI)) return 
+      write("Du darfst nur Deine eigenen Artikel verlegen.\n"),1;
+    write("WARNUNG: Das ist nicht Dein eigener Artikel!\n");
+  }
+    //  return (int)notify_fail("Man darf zur Zeit nur eigene Artikel verlegen.\n");
+  if (!CatchNewsError(NEWSSERVER->RemoveNote(GROUP,num),"Dieser Fehler kann eigentlich nicht auftreten"))
+    write("Warnung! Konnte Artikel an alter Position nicht loeschen.\n");
+  else write("Artikel von Rubrik "+GROUP+" entfernt.\n");
+
+  msg[M_MESSAGE]=
+    sprintf("%s on=%s ot=%d og=%s\n",
+            STATUSESCAPE,msg[M_WRITER],msg[M_TIME],msg[M_BOARD])
+      +msg[M_MESSAGE];
+
+/*
+  expl=explode(msg[M_MESSAGE],"\n");
+  for (i=0;(IS_STATUSLINE(expl[i][0..2]));i++);
+  msg[M_MESSAGE]=( (i) ? implode(expl[0..i-1],"\n")+"\n" : "" ) +
+    "[Verlegt von "+msg[M_BOARD]+", "+msg[M_WRITER]+", "+
+    dtime(msg[M_TIME])[5..26]+"]:\n"+
+      implode(expl[i..],"\n")+"\n";
+*/
+
+  msg[M_BOARD]=gr;
+  NEWSSERVER->WriteNote(msg);
+  write("Artikel nach Rubrik "+gr+" verlegt.\n");
+  return 1;
+}
+
+static MoveTrash()
+{
+  if (!pointerp(lasttitle)||sizeof(lasttitle)<LAST_SIZEOF) return
+    write("Was denn bitte? Du hast noch gar nichts gelesen!\n"),1;
+  if (lasttitle[LAST_GROUP]!=GROUP) return
+    write("Nix gibts! Du hast die Rubrik gewechselt!\n"),1;
+  return MoveMessage(sprintf("artikel %d nach muell",lasttitle[LAST_NR]+1));
+}
+
+static SaveMessage(str) {
+  mixed num;
+  mixed *messages;
+
+  if (intp(str)) num=str;
+  if ((!num && (!str || str=="" || sscanf(str,"artikel %d",num)!=1)) || num<=0) {
+    notify_fail("Welchen Artikel willst Du abspeichern?\n");
+    return 0;
+  }
+  if (!pointerp(messages=NEWSSERVER->GetNotes(GROUP)))
+    return notify_fail("Seltsam, die Rubrik '"+GROUP+
+        "' gibt es nicht mehr...\n"), 0;
+  num--;
+  if (sizeof(messages)<=num) {
+    notify_fail("So viele Nachrichten sind da nicht !\n");
+    return 0;
+  }
+  if(write_file(SAVEMSGPATH(TP),Message2string(num,messages,M2S_REMOTE)+"\n"))
+    write(break_string(
+      "Inhalt des Artikels wurde nach "+SAVEMSGPATH(TP)+" gespeichert.\n"));
+  else
+    write(break_string(
+      "Fehler beim Schreiben nach "+SAVEMSGPATH(TP)+"!\n"));
+  return 1;
+}
+
+
+static MailMessage(str) {
+  mixed num,rec,group;
+  mixed *messages;
+
+//  printf("%O\n",inherit_list(environment(TP)));
+//  if (member(query_actions(this_player()),"mail")<0)
+//     tut nicht wegen anderer Implemtierung von AddCmd in Raeumen
+  
+  if (str=="?"||str=="-?") return
+    write("Syntax: versende artikel <nr> an <adresse>\n"),1;
+
+  str=TP->_unparsed_args();     // wegen Mailadressen
+  if (str) str=lower_case(str);
+
+  if (!IS_SEER(TP) && !IS_POST(environment(TP)))
+    return notify_fail("Du musst in ein Postamt gehen, "
+        "um etwas versenden zu koennen.\n"),0;
+  
+  num=0;
+  
+  if (!str || (sscanf(str,"artikel %d an %s",num,rec)!=2 &&
+               sscanf(str,"note %d to %s",num,rec)!=2 &&
+               sscanf(str,"note to %s",rec)!=1 &&
+               sscanf(str,"artikel an %s",rec)!=1)){
+     if (!str || str[0..6]=="artikel"||str[0..3]=="note") return 
+      write("Welchen Artikel willst Du versenden, und wohin?\n"),1;
+    else return 
+      notify_fail("Welchen Artikel willst Du versenden, und wohin?\n"),0;
+  }
+  if (!num&&(!pointerp(lasttitle)||sizeof(lasttitle)<LAST_SIZEOF))
+    return write("Du hast scheinbar noch nichts gelesen, worauf man sich "
+        "beziehen kann.\nGib notfalls die Nummer des Artikels an.\n"),1;
+
+  //  printf("lasttitle= %O\nnum=%d\n",lasttitle,num);
+
+  if (!pointerp(messages=
+      NEWSSERVER->GetNotes(group=(num?GROUP:lasttitle[LAST_GROUP]))))
+    return notify_fail("Seltsam, die Rubrik gibt es nicht mehr...\n"), 0;
+
+  if (!pointerp(messages)||!sizeof(messages))
+    return write("Die Rubrik "+group+" ist leer.\n"),1;
+  if (num<0||sizeof(messages)<num) return
+    write("Einen Artikel mit Nummer "+num+" gibt es in Rubrik "+group+
+        " nicht!\n"),1;
+
+  if (num) num--;
+  else {
+    int h;
+    num=-1;
+    if (pointerp(messages)) {
+      for (h=sizeof(messages)-1;
+           (h>=0 && messages[h][M_TIME]>=lasttitle[LAST_TIME]);h--) 
+        if (messages[h][M_TIME]==lasttitle[LAST_TIME] && 
+            lower_case(messages[h][M_WRITER])==
+            lower_case(lasttitle[LAST_WRITER]))
+          num=h;
+    }
+    if (num<0)
+      return notify_fail("Konnte Artikel nicht wiederfinden, "
+          "bitte gib die Nummer an.\n"),0;
+  }
+  MAILER->do_mail( rec,
+      "MPA: "+messages[num][M_TITLE]+" ("+messages[num][M_WRITER]+")",
+      Message2string(num,messages,M2S_REMOTE,group)+"\n");
+  return 1;
+}
+
+
+
+HelpPage(str) {
+  if (str!="mpa"&&str!="zeitung") return 0;
+  this_player()->More(read_file(HELPPAGE)+
+      (IS_LEARNER(TP) ? read_file(WIZHELPPAGE) : ""));
+  return 1;
+}
+
+/*--------------------------------------------------------------------------*/
+
+protected void NotifyMove(object dest, object oldenv, int method) {
+  ::NotifyMove(dest, oldenv, method);
+
+  // P_READ_NEWS aus dem Spieler holen.
+  if (objectp(environment()) && query_once_interactive(environment())) {
+    read_until=environment()->QueryProp(P_READ_NEWS);
+  }
+
+  if (!mappingp(read_until) || !sizeof(read_until))
+    InitialSubscriptions();
+}
+
+int GetTID(mixed message) {
+  string dummy;
+  int tid;
+  return (sscanf(message[M_MESSAGE],"%s" STATUSESCAPE " tid=%d",dummy,tid)==2) 
+    ? tid : message[M_TIME];
+}
+
diff --git a/obj/playerdata.c b/obj/playerdata.c
new file mode 100644
index 0000000..0e2729b
--- /dev/null
+++ b/obj/playerdata.c
@@ -0,0 +1,104 @@
+// Dieses Ding kriegt die UID vom Clonenden.
+// Der Master exportiert allerdings per export_uid() ggf. eine UID an dieses
+// Objekt.
+// Abfragen kann es nur das Objekt, welches den Spieler geladen hat.
+
+#pragma no_inherit,no_shadow
+#pragma strong_types,save_types,rtt_checks
+
+#include <config.h>
+#include <wizlevels.h>
+
+inherit "/std/thing/properties";
+
+object|string pl;
+object user;
+
+protected void create()
+{
+  ::create();
+  seteuid(0);
+  set_next_reset(300);
+}
+
+public int ReleasePlayer()
+{
+  pl = 0;
+  user = 0;
+  // Das Objekt darf keine EUID mehr haben, damit ROOT-Objekte ihm ggf. eine
+  // neue UID exportieren koennen, damit es einen neuen Spieler laden kann.
+  seteuid(0);
+  return 1;
+}
+
+public int LoadPlayer(string name)
+{
+  if (query_wiz_level(getuid(previous_object())) < WIZARD_LVL)
+    return 0;
+
+  seteuid(getuid());
+
+  mixed userinfo=MASTER->get_userinfo(name);
+  if (!pointerp(userinfo))
+    return 0;
+  // Wenn es das Spielerobjekt gibt, ist alles gut, dann fragen wir einfach
+  // das.
+  pl = find_player(name) || find_netdead(name);
+  if (!pl)
+  {
+    // wenn nicht: Savefile einlesen.
+    restore_object("/"SAVEPATH + name[0..0] + "/" + name);
+    pl = name;
+  }
+  user = previous_object();
+  set_next_reset(300);
+  return 1;
+}
+
+public varargs mixed Query( string name, int Type )
+{
+  if (!user || user != previous_object())
+    return 0;
+
+  if (objectp(pl))
+    return pl->Query(name,Type);
+  else if (stringp(pl))
+    return ::Query(name, Type);
+  return 0;
+}
+
+public varargs mixed QueryProp( string name )
+{
+  if (!user || user != previous_object())
+    return 0;
+
+  if (objectp(pl))
+    return pl->QueryProp(name);
+  else if (stringp(pl))
+    return ::QueryProp(name);
+  return 0;
+}
+
+
+// kein Schreibzugriff
+public varargs mixed Set( string name, mixed Value, int Type, int extern )
+{
+  return 0;
+}
+
+public void reset()
+{
+  if (clonep(this_object()))
+    destruct(this_object());
+}
+
+public int clean_up(int ref)
+{
+  if (clonep(this_object())
+      || ref <= 1 )
+  {
+    destruct(this_object());
+  }
+  return 1;
+}
+
diff --git a/obj/seercard.c b/obj/seercard.c
new file mode 100644
index 0000000..db54d74
--- /dev/null
+++ b/obj/seercard.c
@@ -0,0 +1,2 @@
+#pragma no_clone
+
diff --git a/obj/shut.c b/obj/shut.c
new file mode 100644
index 0000000..aa82694
--- /dev/null
+++ b/obj/shut.c
@@ -0,0 +1,377 @@
+/* Shutdown-Daemon -- die Messages muessen wohl nochmal ueberarbeitet werden */
+/*
+ *  04.04.2004   Vanion  die() ueberschrieben, ignorierbar gemacht
+ *  15.01.2000   Tiamak  aufgeraeumt, sicherer gemacht 
+ *  15.02.1998   Wargon  Armageddon teilt einem die Restzeit mit
+ *  07.01.1998         Rumata  IS_ELDER ausgebaut.
+ *
+ *  23.07.1997         17.00         Yantro
+ *    Ich hab mal die shouts Zeitabhaengig gemacht, also sie sollten
+ *    heftiger werden mit Annaeherung an den Reboot. Deswegen gibt
+ *    es nun 4 arrays mit messages und die Entsprechende Aufrufaenderung
+ *    in ArmasShout()
+ */
+
+#pragma combine_strings
+#pragma strong_types
+#pragma strict_types
+#pragma no_inherit
+#pragma no_clone
+#pragma no_shadow
+
+inherit "std/npc";
+
+#define S_PRESAY 1
+#define S_IGNORE 2
+
+#include <properties.h>
+#include <defines.h>
+#include <language.h>
+#include <moving.h>
+#include "/secure/wizlevels.h"
+
+#define messages1 ({ \
+  "Wolken ballen sich am Himmel. Es scheint wohl ein Unwetter aufzuziehen. " \
+  "Du hast ein ungutes Gefuehl in Deiner Haut.", \
+  "Aus der Ferne hoerst Du ein Donnern ... ob das ein Gewitter ist?", \
+  "Wolken ziehen schnell vorbei und verdunkeln die Sonne und die Sterne. " \
+  "Dies scheint keines der normalen Unwetter zu sein, die Du schon erlebt " \
+  "hast.", \
+  "Das ferne Rauschen der Baeche und Fluesse wird lauter. Die Wassermassen " \
+  "scheinen nicht mehr kontrolliert zu fliessen und Du bekommst Angst!", \
+  "Der Himmel faerbt sich violett. Das Unwetter scheint gewaltiger zu " \
+  "werden als sonst. Dir laeuft ein Schauer ueber den Ruecken." \
+  })
+
+#define messages2 ({ \
+  "Die Wolken verdunkeln den Himmel. Es wird ueberall duester in der Welt " \
+  "und Du kannst kaum noch erkennen, was um Dich herum geschieht.", \
+  "Ein starker Wind weht durch alle Gegenden und zerrt an allem, was nicht " \
+  "fest ist. Hoffentlich wird das nicht noch schlimmer.", \
+  "Regen prasselt ueberall nieder und fuellt die Baeche und Seen, die das " \
+  "Wasser aber kaum noch halten koennen. Du versucht eine trockene Stelle " \
+  "zu finden.", \
+  "Der Himmel ist fast schwarz. Der Regen faellt in einer Menge, als haetten " \
+  "alle Schleusen der Welt geoeffnet, um sie fuer immer zu ertraenken.", \
+  "Die Luft scheint zu knistern! Das Ende des Unwetters ist sicher noch nicht "\
+  "in Sicht. Elektrizitaet entlaedt sich im Himmel zwischen den Wolken. " \
+  "Was fuer ein Lichtspiel!", \
+  "Blitz und Donner hallen durch die Welt. Es scheint keine Stelle zu geben, " \
+  "an der man sicher sein kann." \
+  })
+
+#define messages3 ({ \
+  "Du siehst, wie die Einwohner der Welt versuchen, sich panisch vor dem " \
+  "Unwetter zu retten, aber es ist fuer sie zu spaet.", \
+  "Blitze nie gesehener Gewalt schlagen rund um Dich ein. Feuer flammen auf " \
+  "und scheinen alles verbrennen zu wollen, was um Dich herum existiert.", \
+  "Dicke Wolken tuermen sich immer weiter auf und scheinen Dich erdruecken zu "\
+  "wollen. Sie sind nicht mehr schwarz, sondern blutrot wie das Wasser, was " \
+  "sie von sich geben.", \
+  "Die Fluesse und Meere der Welt treten ueber ihre Ufer und zerstoeren alles "\
+  "Leben. Das Wasser hat sich zu Blut gewandelt! Um Dich herum wird alles " \
+  "rot!", \
+  "Der Donner grollt mit roher Gewalt durch die Welt. Die Schallwellen " \
+  "schlagen mit aller Kraft gegen die Gebirge und scheinen sie reizen zu " \
+  "wollen. Du kriegst Angst!", \
+  "Der Regen faerbt sich rot! Das ist kein Wasser mehr, sondern Blut, was da " \
+  "auf Dich herabregnet. Dir schwinden kurz die Sinne beim Gedanken an das, " \
+  "was noch kommen mag." \
+  })
+
+#define messages4 ({ \
+  "Das Blut des Regens scheint Dich fortzuschwemmen. Ueberall ist das Blut " \
+  "ueber die Ufer getreten und sucht sich seinen toedlichen Weg.", \
+  "Die Vulkane der Welt brechen aus und die Lava verbrennt alles, was sich " \
+  "ihr in den Weg stellt. Sie bringt das Blut zum Verdampfen und erfuellt " \
+  "alles mit ihrer zerstoererischen, grausamen Hitze.", \
+  "Ploetzlich ist alles mit Feuer und Hitze erfuellt. Die Meere und " \
+  "Fluesse verdampfen und der Dampf treibt heiss ueber die Welt, um alles " \
+  "zu verbruehen, was sich nicht schuetzt!", \
+  "Von ueberall hoerst Du Schreie und Du erkennst panische Angst in den " \
+  "Augen der Eingeborenen. Elendig verenden sie in der Hitze zwischen Feuer " \
+  "und Lava. Dir wird uebel.", \
+  "Um Dich herum siehst Du nur brennende Truemmer. Die Welt scheint vor " \
+  "ihrem Untergang, denn ueberall oeffnet sich der Boden und spuckt Feuer. " \
+  "Heisses Magma quillt aus ihm heraus und mischt sich zischend mit dem Blut " \
+  "der Meere. Es gibt nun sicher kein Entrinnen mehr.", \
+  "Du kannst Dich kaum noch halten. Erdbeben erschuettern alles um Dich " \
+  "herum. Ueberall kannst Du nur noch Truemmer erkennen, wo einmal Haeuser " \
+  "standen. Alles ist erfuellt vom Geruch des Todes!" \
+  })
+
+static int restzeit;
+static string *ignorierer = ({});
+
+static int _query_restzeit() { return restzeit + find_call_out("NextStep"); }
+static string *_query_ignore() { return ignorierer; }
+
+public varargs void die(int poisondeath,int extern)
+{
+  object tp;
+  object room;
+
+  if (objectp(room=environment(this_object())))
+    if (!objectp(tp=this_player()))
+      tell_room(room,"Armageddon kratzt sich ratlos am Kopf.\n");
+    else 
+      tell_room(room,
+        "Armageddon sagt: Ich weigere mich, einfach so zu sterben, "+
+        (tp->Name())+".\n");
+  return;
+}
+
+status access_check() {
+    //wer darf rebooten?
+
+    //in einem process_string() gar nicht.
+    if (process_call()) return(0);
+
+    //Master darf natuerlich. (->slow_shut_down(), vom Driver bei
+    //Speicherknappheit gerufen.)
+    if (previous_object() && 
+        previous_object()==find_object(__MASTER_OBJECT__))
+        return(1);
+
+    // rebooten duerfen ansonsten nur [W]+
+    if ( ELDER_SECURITY) {
+        return(1);
+    }
+
+    //andere nicht.
+    return(0);
+}
+
+public void create()
+{    
+    ::create();
+    
+    SetProp( P_NAME, "Armageddon" );
+    SetProp( P_GENDER, MALE );
+    SetProp( P_SHORT, "Armageddon, der Weltenvernichter" );
+    AddId( "armageddon" );
+    set_living_name( "armageddon" );
+    SetProp( P_ARTICLE, 0 );
+    SetProp( P_XP, 0 );
+    SetProp( P_NO_ATTACK,
+             "Armageddon grinst: Dein Ende kommt noch frueh genug!\n" );
+
+    if (!access_check()) {
+        destruct(this_object());
+        raise_error("Armageddon darf nur von W+ aktiviert werden!");
+    }
+}
+
+
+static varargs string text_time( int sec, int flag )
+{
+    string s;
+    int tage, stunden, minuten, sekunden;
+
+    sekunden = sec;
+    tage = sekunden / 86400;
+    stunden = (sekunden % 86400) / 3600;
+    minuten = (sekunden % 3600) / 60;
+    sekunden = sec % 60;
+    s = "";
+    
+    if ( sec >= 86400 )
+        s += tage + " Tag" + (tage == 1 ? ", " : (flag ? "en, " : "e, "));
+    
+    if ( sec >= 3600 )
+        s += stunden + " Stunde" + (stunden == 1 ? ", " : "n, ");
+    
+    if ( sec >= 60 )
+        s += minuten + " Minute" + (minuten == 1 ? "" : "n");
+    
+    s += (sec/60 ? " und " : " ") + sekunden + " Sekunde" +
+        (sekunden == 1 ? "" : "n");
+    
+    return s;
+}
+
+
+public string _query_long()
+{
+    return break_string( "Dies ist Armageddon, der Weltenvernichter. Als Du "
+                         "ihn ansiehst, blickt er auf und fluestert Dir zu: "
+                         "Noch " + text_time(_query_restzeit()) +
+                         " muessen vergehen, ehe ich die Welt vernichte.", 78 );
+}
+
+
+public string _query_short()
+{
+    return "Armageddon, der Weltenvernichter";
+}
+
+
+public int shut( mixed minutes )
+{
+    int a, b, c;
+
+    if (!access_check()) {
+        destruct(this_object());
+        raise_error("Armageddon darf nur von W+ aktiviert werden!");
+        return(-1);   //never reached
+    }
+    
+    a = b = c = 0;
+    move( "/gilden/abenteurer", M_GO );
+    
+    if ( intp(minutes) )
+        c = minutes;
+    else if ( stringp(minutes) ){
+        if ( sscanf( minutes, "%d:%d:%d", a, b, c ) == 3 )
+            c += b * 60 + a * 24 * 60;
+        else if( sscanf( minutes, "%d:%d", b, c) == 2 )
+            c += b * 60;
+        else if( sscanf( minutes, "%d", c ) != 1 )
+            return -2;
+    }
+    else {
+      destruct(ME);
+      raise_error("Illegal argument type to shut()\n");
+    }
+
+    while ( remove_call_out("NextStep") >= 0 )
+        ;
+    
+    restzeit = c * 60;
+    call_out( "NextStep", 0, restzeit );
+    return restzeit;
+}
+
+// Schickt die Meldung str an alle Spieler, die Armageddon nicht ignorieren
+// Flags: S_IGNORE - Shout kann von Spielern ignoriert werden.
+//        S_PRESAY - Es wird ein "Armageddon ruft:" vorangestellt.
+static void SHOUT( string str, int flags )
+{
+   if (flags&S_PRESAY)
+     str = break_string(str, 78, "Armageddon ruft: ");
+   else
+     str = break_string(str, 78);
+     
+   if (flags&S_IGNORE)
+     filter(
+       users()-filter_objects(users(),"TestIgnore",({"armageddon"}))
+              -map(ignorierer, #'find_player),
+       #'tell_object, str);
+   else
+     filter( users(), #'tell_object, str);
+}
+
+
+static void NextStep( int seconds )
+{
+    int neu;
+
+    if ( seconds <= 0 ){
+        SHOUT( "Ich starte das Spiel jetzt neu !", S_PRESAY);
+        shutdown("");
+        return;
+    }
+    
+    if ( seconds <= 600 )
+      SHOUT( "Teile mir mit, wenn Du in den Laden gebracht werden willst!", 
+              S_PRESAY|S_IGNORE);
+    
+    if ( seconds > 2 * 86400 )
+        neu = seconds - 5 * 3600;
+    else if ( seconds > 4800 )
+        neu = seconds - 1800;
+    else
+        neu = seconds * 3 / 4 - 10;
+    
+    restzeit = neu;
+    call_out( "NextStep", seconds-neu, neu );
+    
+    SHOUT( "In " + text_time( seconds, 1 ) + " werde ich die Welt "
+           "zerstoeren und neu erschaffen!", S_PRESAY|S_IGNORE);
+    
+    if ( neu < 900 && find_call_out("ArmasShout") == -1 )
+        call_out( "ArmasShout", random(60)+10 );
+}
+
+
+static void ArmasShout()
+{
+    int dauer;
+    
+    dauer = _query_restzeit();
+    call_out( "ArmasShout", 40+random(40) );
+    
+    if ( 701 < dauer )
+        SHOUT( messages1[ random(sizeof(messages1))], S_IGNORE);
+    else if ( 501 < dauer )
+        SHOUT( messages2[ random(sizeof(messages2))], S_IGNORE);
+    else if ( 251 < dauer )
+        SHOUT( messages3[ random(sizeof(messages3))], S_IGNORE);
+    else
+        SHOUT( messages4[ random(sizeof(messages4))], S_IGNORE);
+}
+
+
+public void catch_tell( string str )
+{
+    string who, what;
+    object ob;
+
+    if ( sscanf( str, "%s teilt Dir mit: %s", who, what ) != 2 ||
+         !this_player() )
+        return;
+    
+    // Ein Spieler will fuer dieses Reboot ignoriert werden, oder
+    // das Ignoriere soll aufgehoben werden.
+    if (lower_case(what)[0..<2]=="ruhe"){
+      if (member(ignorierer, getuid(this_player()))>-1)
+      {
+         ignorierer -= ({ getuid(this_player()) });
+         tell_object( this_player(), break_string(
+          "Gut, ich notier's mir. Ab nun hast Du wieder am grossen Showdown "
+          "teil. Viel Spass dabei.\n", 78, "Armageddon teilt Dir mit: "));
+      } else {
+         ignorierer += ({ getuid(this_player()) });
+         tell_object( this_player(), break_string(
+          "Gut, ich notier's mir. Der Weltuntergang soll Dich beim Spielen "
+          "nicht weiter stoeren.\n", 78, "Armageddon teilt Dir mit: "));
+      }
+      return;
+    }
+   
+    // In den Laden
+    if ( _query_restzeit() < 600 ){
+        this_player()->move( "/d/ebene/room/PortVain/laden", M_TPORT );
+        return;
+    }
+    
+    tell_object( this_player(),
+                 break_string( "Es ist noch zu frueh! Probier es 10 Minuten "
+                               "vor dem Ende nochmal!\nIn " +
+                               text_time( _query_restzeit(), 1 ) +
+                               " ist es soweit!", 78,
+                               "Armageddon teilt Dir mit: ", 1 ) );
+    return;
+}
+
+
+public varargs int remove()
+{
+    write( "Armageddon will nicht removed werden!\n" );
+    return 0;
+}
+
+
+public int do_damage( int dam, object enemy )
+{
+    return 0;
+}
+
+// Nicht jeder Magier darf Armageddon destructen.
+string NotifyDestruct(object caller) {
+    if( (caller!=this_object() && !ELDER_SECURITY) || process_call() ) {
+      return "Du darfst Armageddon nicht zerstoeren!\n";
+    }
+    return 0;
+}
+
diff --git a/obj/sperrer.c b/obj/sperrer.c
new file mode 100644
index 0000000..ce94cd8
--- /dev/null
+++ b/obj/sperrer.c
@@ -0,0 +1,98 @@
+// MorgenGrauen MUDlib
+//
+// $Id: sperrer.c 8747 2014-04-26 13:08:47Z Zesstra $
+
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+#include <config.h>
+#include <wizlevels.h>
+
+#define PO previous_object()
+#define TI this_interactive()
+
+create()
+{
+  if (clonep(ME))
+  {
+    write("Autodest\n");
+    destruct(this_object());
+  }
+  upd();
+}
+
+upd()
+{
+  move_object("/room/netztot");
+}
+
+debug(what,arg)
+{
+  string log;
+  
+  if (!environment())
+    return;
+  log=sprintf("%s %s %O PO=%O PPO=%O TO=%O TI=%O verb=%O\n",
+      dtime(time()), what, arg, 
+      previous_object(), previous_object(1), 
+      this_object(), this_interactive(), 
+      (this_interactive()||this_player()||this_object())->_unparsed_args());
+  log_file("NDEAD",log);
+  
+  if (find_player("jof"))
+    tell_object(find_player("jof"),log);
+  if (previous_object() && 
+      (!interactive(PO)||IS_LEARNER(PO)) && 
+      getuid(PO)!=ROOTID && PO!=this_object())
+  {
+    log_file("NDEAD",sprintf("DELETED %O\n",PO));
+    catch(PO->remove());
+    if (PO && !(PO==this_object()) && object_name(PO)!="/secure/simul_efun")
+      destruct(PO);
+    if (TI && IS_LEARNER(TI))
+    {
+      tell_object(TI,"MESSING WITH NETDEAD ROOM - STOP\n");
+      TI->remove();
+      if (TI)
+        destruct(TI);
+      log_file("NDEAD",sprintf("STOPPED %O\n",TI));
+    }
+    raise_error("DONT MESS WITH NETDEAD\n");
+  }
+  else if ( previous_object() && interactive(previous_object()) ){
+      tell_object( previous_object(), "Wie bist Du denn hierhin gekommen?\n"
+                   "Ich setz Dich besser mal in der Abenteurergilde ab.\n" );
+      previous_object()->move( "/gilden/abenteurer", M_GO|M_NOCHECK );
+      log_file( "NDEAD", sprintf( "MOVED %O\n", previous_object() ) );
+  }
+}
+
+reset()
+{
+  upd();
+}
+
+id(str)
+{
+  debug("id",str);
+}
+
+Query(what)
+{
+  debug("query",what);
+}
+
+remove()
+{
+  debug("remove",0);
+}
+
+long()
+{
+  debug("long",0);
+}
+
+short()
+{
+  debug("short",0);
+}
diff --git a/obj/team.c b/obj/team.c
new file mode 100644
index 0000000..0319f5e
--- /dev/null
+++ b/obj/team.c
@@ -0,0 +1,1432 @@
+#pragma strong_types,save_types
+#pragma no_shadow
+// TODO: Morgoth erbt das Teamobjekt: klaeren warum und wieso eigentlich.
+//#pragma no_inherit
+#pragma pedantic
+
+#include <living/team.h>
+#include <properties.h>
+#include <language.h>
+#include <new_skills.h>
+#include <ansi.h>
+#include <wizlevels.h>
+#define ME this_object()
+#define PO previous_object()
+#define TP this_player()
+#define TI this_interactive()
+
+#define AUTOINF_HP_MINUS 0x01
+#define AUTOINF_HP_PLUS  0x02
+#define AUTOINF_SP_MINUS 0x04
+#define AUTOINF_SP_PLUS  0x08
+#define AUTOINF_INSTANT  0x10
+
+private nosave mapping is_member;   // Teammitglieder
+private nosave object leader;       // Teamleiter
+private nosave string tname;        // Teamname
+private nosave mapping wanted_row;  // Gewuenschte Reihe
+private nosave mapping wimpy_row;   // Fluchtreihe
+private nosave mapping act_row;     // Aktuelle Reihe
+private nosave mapping autofollow;  // Spieler folgt Teamleiter
+private nosave mapping attack_cmd;  // Spieler hat Angriffsbefehl
+private nosave mapping assoc_mem;   // Zugeordnete Mitglieder (Kampf NPCs)
+private nosave int *formin,*formax; // Formation
+private nosave mixed *rows;         // Die Reihen
+private nosave int last_reorder;    // Letzte Formationspruefung
+private nosave mapping h;           // Sortier-Score: 125*G.Reihe-HP
+private nosave object *att_exec;    // Mitglieder, die Attacke ausfuehren.
+private nosave object *mis_attacked;// (Ex-)Mitglieder ohne Begruessungsschlag
+private nosave mapping mis_init_att;// Fehlende Begruessungsschlaege
+private nosave mapping hp_info;     // HP, MAX_HP, SP, MAX_SP
+private nosave int autoinf_flags;
+private nosave mapping autoinf_hp;
+private nosave mapping autoinf_sp;
+private nosave int autoinf_time;
+private nosave string *hist;
+
+private nosave object debugger;
+
+void _set_debug() {
+  if (!debugger && IS_LEARNER(TI))
+    debugger=TI;
+  else if (debugger==TI)
+    debugger=0;
+}
+ 
+private void debug(string str) {
+  if (objectp(debugger) && stringp(str))
+    tell_object(debugger,"#TEAM: "+str);
+}
+
+void create() {
+  autoinf_flags=0;
+  autoinf_sp=([]);
+  autoinf_hp=([]);
+  is_member=([]);
+  leader=0;
+  wanted_row=([]);
+  act_row=([]);
+  wimpy_row=([]);
+  autofollow=([]);
+  attack_cmd=([]);
+  assoc_mem=([]);
+  formin=({1,0,0,0,0});
+  formax=({5,4,3,2,1});
+  rows=EMPTY_TEAMARRAY;
+  h=([]);
+  att_exec=({});
+  mis_init_att=([]);
+  mis_attacked=({});
+  hp_info=([]);
+  hist=({});
+  if (object_name(ME)==TEAM_OBJECT)
+    return;
+  if (!stringp(tname=TEAM_MASTER->RegisterTeam()))
+    tname="?";
+}
+
+object *Members() {
+  return (m_indices(is_member)-({0}));
+}
+
+object Leader() {
+  return leader;
+}
+
+varargs string name(int casus, int demon) {
+  if (!stringp(tname))
+    return "Team ?";
+  return "Team "+capitalize(tname);
+}
+
+varargs string Name(int casus, int demon) {
+  return name(casus,demon);
+}
+
+varargs int remove(int silent) {
+  if (mappingp(is_member) && sizeof(Members())) // Nur leere Teams removen
+    return 0;
+  TEAM_MASTER->UnregisterTeam(); // Teamnamen freigeben.
+  destruct(ME);
+  return 1;
+}
+
+private void TryRemove() {
+  if (clonep(ME)
+      && (!mappingp(is_member) || !sizeof(Members()))
+      && !first_inventory(ME)
+      && find_call_out("remove")<0)
+    call_out("remove",0);
+}
+
+int CmpFirstArrayElement(mixed *a, mixed *b) {
+  return(a[0]<b[0]);
+}
+
+varargs private void gtell(string str, string who, int tohist) {
+  int i;
+  object *tmembers,rochus;
+  string prefix,msg;
+
+  tmembers=Members();
+  prefix=sprintf("[%s:%s] ",name(),stringp(who)?who:"");
+  msg=break_string(str,78,prefix);
+  for (i=sizeof(tmembers)-1;i>=0;i--)
+    tell_object(tmembers[i],msg);
+  if (objectp(rochus=find_player("rochus"))
+      && rochus->QueryProp("debug_team"))
+    tell_object(rochus,msg);
+  if (tohist)
+    hist=(hist+({break_string(str+" <"+ctime()[11..15]+">",78,prefix)}))[-100..];
+}
+
+int IsMember(object ob) {
+  return (objectp(ob) && is_member[ob]);
+}
+
+int IsInteractiveMember(object ob) {
+  return (objectp(ob) && is_member[ob] && query_once_interactive(ob));
+}
+
+varargs private int *GetHpInfo(object ob, closure cl) {
+  int *res;
+
+  if (!closurep(cl)) cl=symbol_function("QueryProp",ob);
+  if (!pointerp(res=hp_info[ob]) || sizeof(res)<4)
+    res=({0,funcall(cl,P_MAX_HP),0,funcall(cl,P_MAX_SP)});
+  res[0]=funcall(cl,P_HP);
+  res[2]=funcall(cl,P_SP);
+  return hp_info[ob]=res;
+}
+
+int CompareHp(object a, object b) {
+  return h[a]>h[b];
+}
+
+// Aktualisiert act_row (->wer steht in welcher Reihe).
+private void UpdateActRow() {
+  int i,j,update_hp;
+  object *new;
+  mixed aso;
+
+  act_row=([]);
+  rows[0]+=Members();
+  update_hp=0;
+  if (!mappingp(h)) {
+    h=([]);
+    update_hp=1;
+  }
+  for (i=MAX_TEAMROWS-1;i>=0;i--) {
+    new=({});
+    foreach(object ob: rows[i]) {
+      if (objectp(ob) && is_member[ob] && !act_row[ob]) {
+        act_row[ob]=i+1;
+        new+=({ob});
+        if (update_hp) {
+          if (!objectp(aso=assoc_mem[ob]) || environment(aso)!=environment(ob))
+            aso=ob;
+          h[ob]=
+            1000*wanted_row[aso]
+            +40*act_row[aso]
+            -(query_once_interactive(aso)?8:2)*aso->QueryProp(P_HP)
+            -query_once_interactive(ob);
+          // NPCs bekommen fast gleichen Wert wie Caster,
+          // im Zweifelsfalle steht der Caster weiter vorne...
+        }
+      }
+    }
+    rows[i]=sort_array(new,"CompareHp",ME);
+  }
+}
+
+private void CheckFormation() {
+  int i,mincap,maxcap,d,num;
+
+  mincap=maxcap=0;
+  if (formin[0]<1)
+    formin[0]=1;
+  
+  // erstmal die Wuensche normalisieren/korrigieren auf sinnvolle Werte.
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    if (formin[i]<0) formin[i]=0;
+    if (formax[i]<formin[i]) formax[i]=formin[i];
+    if (formax[i]>MAX_TEAM_ROWLEN) formax[i]=MAX_TEAM_ROWLEN;
+    if (formin[i]>formax[i]) formin[i]=formax[i];
+    mincap+=formin[i]; // Summe der min. je Reihe gewuenschten.
+    maxcap+=formax[i]; // Summe der max. je Reihe gewuenschten.
+  }
+
+  num=sizeof(Members());
+
+  // max. gewuenschte Reihenlaenge verlaengern, wenn die Summe der maximal je
+  // Reihe gewuenschten kleiner als die Anzahl der Spieler ist. Von vorne
+  // natuerlich.
+  d=num-maxcap;
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    if (d<=0)
+      break;
+    d-=(MAX_TEAM_ROWLEN-formax[i]);
+    formax[i]=MAX_TEAM_ROWLEN;
+    if (d<0)
+      formax[i]+=d;  // doch noch was uebrig, wieder anhaengen. 
+  }
+  // min. gewuenschte Reihenlaenge auf 0 verkuerzen, wenn die Summe der
+  // minimal je Reihe gewuenschten groesser als die Anzahl der Spieler ist.
+  // Von hinten natuerlich.
+  d=mincap-num; // 
+  for (i=MAX_TEAMROWS-1;i>=0;i--) {
+    if (d<=0)
+      break;
+    d-=formin[i];
+    formin[i]=0;
+    if (d<0)
+      formin[i]-=d; // doch noch was uebrig, wieder anhaengen.
+  }
+}
+
+private void MakeFormation() {
+  // Verlegungsstrategie:
+  //    Richtung Test Verschieben
+  // 1. -----> a) MAX          <- X
+  //           b) MAX             X ->
+  //           c) MIN             X <- <- <- <-
+  // 2. <----- a) MIN -> -> -> -> X
+  //           b) MAX          <- X
+  int i,j,d;
+
+  last_reorder=time();
+  UpdateActRow();
+  CheckFormation();
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    d=sizeof(rows[i]);
+    if (d<formin[i] || d>formax[i])
+      break;
+  }
+  if (i>=MAX_TEAMROWS)
+    return; // Formation ist noch in Ordnung
+
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    if (sizeof(rows[i])>formax[i]) {     // Reihe ist zu voll
+      if (i>0) {
+        d=formax[i-1]-sizeof(rows[i-1]);
+        if (d>0) {                       // Reihe vorher hat d freie Plaetze
+          rows[i-1]+=rows[i][0..(d-1)];  // Also d Mitglieder abgeben
+          rows[i]=rows[i][d..];
+        }
+      }
+      if (i<MAX_TEAMROWS-1 && sizeof(rows[i])>formax[i]) {// Immer noch zu voll
+        rows[i+1]=rows[i][formax[i]..]+rows[i+1];         // Rest nach hinten.
+        rows[i]=rows[i][0..(formax[i]-1)];
+      }
+      continue; // War zu voll, kann nicht zu leer sein
+    }
+    for (j=i+1;j<MAX_TEAMROWS;j++) {
+      d=formin[i]-sizeof(rows[i]);
+      if (d<=0)                   // Ausreichende Anzahl
+        break;                    // kein weiteres j noetig
+      rows[i]+=rows[j][0..(d-1)]; // Sonst Nachschub von hinten holen
+      rows[j]=rows[j][d..];
+    }
+  }
+  for (i=MAX_TEAMROWS-1;i>0;i--) {
+    for (j=i-1;j>=0;j--) {
+      d=formin[i]-sizeof(rows[i]);
+      if (d<=0)                   // Ausreichende Anzahl
+        break;                    // kein weiteres j noetig
+      rows[i]+=rows[j][0..(d-1)]; // Sonst Nachschub von vorne holen
+      rows[j]=rows[j][d..];
+    }
+    d=sizeof(rows[i])-formax[i];
+    if (d>0) {
+      rows[i-1]+=rows[i][0..(d-1)]; // Ueberschuss nach vorne schieben
+      rows[i]=rows[i][d..];
+    }
+  }
+  UpdateActRow();
+}
+
+private void RemoveFromRow(object ob, int src) {
+  if (src<0 || src>=MAX_TEAMROWS)
+    return;
+  rows[src]-=({ob});
+  if (sizeof(rows[src])>=formin[src])
+    return;
+  // Falls hinten noch Ueberschuss da ist, her damit.
+  if (src<MAX_TEAMROWS-1 && sizeof(rows[src+1])-1>=formin[src+1]) {
+    rows[src]+=rows[src+1][0..0];
+    rows[src+1]=rows[src+1][1..];
+    return;
+  }
+  // Falls vorne noch Ueberschuss da ist, her damit.
+  if (src>0 && sizeof(rows[src-1])-1>=formin[src-1]) {
+    rows[src]=rows[src-1][<1..]+rows[src];
+    rows[src-1]=rows[src-1][0..<2];
+  }
+}
+
+private void AddToRow(object ob, int dest) {
+  if (dest<0 || dest>=MAX_TEAMROWS)
+    return;
+  rows[dest]+=({ob});
+  if (sizeof(rows[dest])<=formax[dest])
+    return;
+  // Falls vorne noch jemand hin kann, dorthin
+  if (dest>0 && sizeof(rows[dest-1])+1<=formax[dest-1]) {
+    rows[dest-1]+=rows[dest][0..];
+    rows[dest]=rows[dest][1..];
+    return;
+  }
+  // Falls hinten noch jemand hin kann, dorthin
+  if (dest<MAX_TEAMROWS-1 && sizeof(rows[dest+1])+1<=formax[dest+1]) {
+    // Dest: ({... <3, <2, ob});
+    rows[dest+1]=rows[dest][<2..<2]+rows[dest+1];
+    rows[dest]=rows[dest][0..<3]+({ob});
+  }
+}
+
+private void CycleRows(object ob, int src, int dest) {
+  int i;
+
+  if (src<0 || src>=MAX_TEAMROWS || dest<0 || dest>=MAX_TEAMROWS)
+    return;
+  rows[src]-=({ob});
+  if (sizeof(rows[src])<formin[src] || sizeof(rows[dest])>=formax[dest]) {
+    if (src<dest) {             // (<- -X) <- <- <- <- (+X <-)
+      for (i=src+1;i<=dest;i++) {
+        rows[i-1]+=rows[i][0..0];
+        rows[i]=rows[i][1..];
+      }
+    } else if (src>dest) {           // (-> +X) -> -> -> -> (-X ->)
+      for (i=src-1;i>=dest;i--) {
+        rows[i+1]=rows[i][<1..]+rows[i+1];
+        rows[i]=rows[i][0..<2];
+      }
+    }
+  }
+  if (src<=dest)
+    rows[dest]+=({ob});
+  else
+    rows[dest]=({ob})+rows[dest];
+}
+
+// tauscht zufaellig aus den ersten 4 Reihen aus einer Reihe den letzten mit
+// dem ersten aus der Folgereihe, wenn die der Score des vorderen Spieler mehr
+// als 10 groesser als der des hinteren Spielers ist.
+private void RandomChangeRow() {
+  int *nums,i;
+  object p1,p2;
+
+  if (!mappingp(h))
+    UpdateActRow();
+  for (nums=({0,1,2,3});sizeof(nums);nums-=({i})) {
+    i=nums[random(sizeof(nums))];
+    if (!sizeof(rows[i]) || !sizeof(rows[i+1])) continue;
+    if (!objectp(p1=rows[i][<1]) || !objectp(p2=rows[i+1][0])) continue;
+    if (wanted_row[p1]<wanted_row[p2]) continue;
+    if (h[p2]-h[p1]>=-10) continue;
+    rows[i][<1]=p2;
+    rows[i+1][0]=p1;
+    return;
+  }
+}
+
+varargs private string
+CountUpNames(object *obs, string zsing, string zplur) {
+  string res;
+  object ob;
+  int i,sz;
+
+  res="";
+  if (!pointerp(obs)) return res;
+  if (!stringp(zsing)) zsing="";
+  if (!stringp(zplur)) zplur="";
+  if (sz=sizeof(obs)) {
+    for (i=0;i<sz;i++) {
+      if (i)
+	      res+=((i<sz-1)?", ":" und ");
+      if (objectp(ob=obs[i]))
+        res+=ob->name(WER);
+    }
+    if (sz>1)
+      res+=zplur;
+    else
+      res+=zsing;
+  }
+  return res;
+}
+
+static void DoChangeRow(object pl, int dest) {
+  mapping old_row,ec1,ec2,ecb;
+  int i;
+  object *obs,*envs,env;
+  string *msg,str;
+
+  dest--;
+  CheckFormation();
+  h=0;  // damit HP-Liste geupdated wird.
+  UpdateActRow();
+  old_row=deep_copy(act_row);
+
+  // welche Objekte bewegen?
+  obs=({});
+  if (objectp(pl)) {
+    obs=({pl});
+    if (pointerp(assoc_mem[pl]))
+      obs+=assoc_mem[pl];
+  } else {
+    RandomChangeRow();
+  }
+
+  foreach (object ob:obs) {
+    if (!objectp(ob))
+      continue;
+    // Alle assoziierten NPC kriegen die gleiche gewuenschte Reihe wie der
+    // Spieler.
+    wanted_row[ob]=wanted_row[pl];
+    UpdateActRow();
+    // und dann in die gew. Reihe stopfen.
+    int src=act_row[ob]-1;
+    if (dest<0 || dest>=MAX_TEAMROWS)
+      RemoveFromRow(ob,src);
+    else if (src<0 || src>=MAX_TEAMROWS)
+      AddToRow(ob,dest);
+    else if (src!=dest)
+      CycleRows(ob,src,dest);
+  }
+
+  MakeFormation();
+
+  obs = Members(); // alle Members beruecksichtigen beim Abgleich!
+  object *changed = allocate(0);
+  foreach (object ob:obs)
+    if (objectp(ob) && old_row[ob]!=act_row[ob]) {
+      ob->InformRowChange(old_row[ob],act_row[ob]);
+      changed += ({ob});
+    }
+
+  // Ab jetzt nur noch Ausgabe.
+  if (get_eval_cost()<800000) return; // War schon teuer genug, Lagvermeidung
+  msg=({});ec1=([]);ec2=([]);ecb=([]);
+  foreach (object ob:changed) {
+    tell_object(ob,sprintf("Du bist jetzt in Reihe %d.\n",act_row[ob]));
+    msg+=({sprintf("%s->%d",ob->Name(WER),act_row[ob])});
+    if (query_once_interactive(ob) && !interactive(ob)) continue;
+    if (!objectp(env=environment(ob))) continue;
+    if (old_row[ob]<=1) {
+      if (!pointerp(envs=ec1[env])) envs=({});
+      ec1[env]=envs+({ob});ecb[env]|=1;
+    }
+    if (act_row[ob]<=1) {
+      if (!pointerp(envs=ec2[env])) envs=({});
+      ec2[env]=envs+({ob});ecb[env]|=2;
+    }
+  }
+  if (sizeof(msg))
+    gtell(implode(msg,", ")); // Ausgabe an alle Gruppenmitglieder.
+  m_delete(ecb,find_object("/room/netztot")); // Das gaebe Mega-Lag :-)
+  envs=m_indices(ecb);obs=Members();
+  for (i=sizeof(envs)-1;i>=0;i--) {
+    if (!objectp(env=envs[i])) continue;
+    str="";
+    str+=CountUpNames(ec1[env]," tritt zurueck"," treten zurueck");
+    if (ecb[env]==3) str+=", ";
+    str+=CountUpNames(ec2[env]," tritt vor"," treten vor");
+    str+=".\n";
+    tell_room(env,capitalize(break_string(str,78)),obs);
+  }
+}
+
+void UpdateFormation() {
+  DoChangeRow(0,0);
+}
+
+int SwapRows(object ob1, object ob2) {
+  int i,r1,r2,p1,p2;
+
+  if (!objectp(ob1) || !objectp(ob2) || ob1==ob2)
+    return 0;
+  r1=r2=-1;
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    if (r1==-1 && (p1=member(rows[i],ob1))>=0)
+      r1=i;
+    if (r2==-1 && (p2=member(rows[i],ob2))>=0)
+      r2=i;
+  }
+  if (r1==-1 || r2==-1)
+    return 0;
+  if (r1==r2)
+    return 1;
+  if (r1<r2) { // Nicht Monster vor Spieler stellen
+    if (query_once_interactive(ob1) && !interactive(ob2))
+      return 0;
+  } else {
+    if (query_once_interactive(ob2) && !interactive(ob1))
+      return 0;
+  }
+  rows[r1][p1]=ob2;ob2->InformRowChange(r2,r1);
+  rows[r2][p2]=ob1;ob1->InformRowChange(r1,r2);
+  gtell(ob1->Name(WER)+" und "+ob2->name(WER)+" tauschen die Plaetze.\n");
+  return 1;
+}
+
+private int ChangeRow(string arg) {
+  int num;
+
+  if (!arg || sscanf(arg,"%d",num)!=1)
+    return notify_fail("In welche Reihe willst Du denn wechseln?\n"),0;
+  if (num<1 || num>MAX_TEAMROWS)
+    return notify_fail("Die Reihenangabe ist ungueltig.\n"),0;
+  TP->SetProp(P_TEAM_WANTED_ROW,wanted_row[TP]=num);
+  printf("Du versuchst in Reihe %d zu wechseln.\n",num);
+  DoChangeRow(TP,num);
+  return 1;
+}
+
+private int ChangeWimpyRow(string arg) {
+  int num;
+
+  if (!arg || sscanf(arg,"%d",num)!=1)
+    return notify_fail("In welche Reihe willst Du fliehen?\n"),0;
+  if (num<0 || num>MAX_TEAMROWS)
+    return notify_fail("Die Reihenangabe ist ungueltig.\n"),0;
+  TP->SetProp(P_TEAM_WIMPY_ROW,wimpy_row[TP]=num);
+  if (num>1)
+    printf("Bei der Flucht wirst Du in Reihe %d wechseln.\n",num);
+  else
+    write("Bei der Flucht wirst Du den Raum verlassen.\n");
+  return 1;
+}
+
+mixed *PresentRows(object env) {
+  int i,j,d,arbeit;
+  mixed *res;
+  object *nd,ob;
+
+  if (!objectp(env))
+    env=environment(TP);
+  if (last_reorder!=time() || !mappingp(h))
+    UpdateFormation();
+  res=EMPTY_TEAMARRAY;arbeit=0;nd=({});
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    object *new=({});
+    foreach(ob: rows[i]) {
+      if (objectp(ob) && is_member[ob] && environment(ob)==env) {
+        if (query_once_interactive(ob) && !interactive(ob)) {
+          nd+=({ob});
+          arbeit=1;
+        } else {
+          new+=({ob});
+        }
+      } else {
+        arbeit=1;
+      }
+    }
+    res[i]=new;
+  }
+  if (!arbeit)
+    return res;
+  for (i=j=0;i<MAX_TEAMROWS;i++) {
+    if (j<=i) j=i+1;
+    for (;j<MAX_TEAMROWS;j++) {
+      d=formin[i]-sizeof(res[i]);
+      if (d<=0)                   // Ausreichende Anzahl
+        break;                    // kein weiteres j noetig
+      res[i]+=res[j][0..(d-1)];   // Sonst Nachschub von hinten holen
+      res[j]=res[j][d..];
+    }
+  }
+  res[MAX_TEAMROWS-1]+=nd; // Netztote bieten keine Deckung, nach hinten.
+  return res;
+}
+
+mapping PresentPositions(mixed pres_rows) {
+  mapping res=([]);
+  if (objectp(pres_rows))
+    pres_rows=PresentRows(pres_rows);
+  if (!pointerp(pres_rows))
+    return res;
+  for (int i=0;i<MAX_TEAMROWS;i++) {
+    foreach(object ob: pres_rows[i])
+      if (ob)
+        res[ob]=i+1;
+  }
+  return res;
+}
+
+
+varargs int FleeToRow(object ob) {
+  int num;
+
+  if (!objectp(ob))
+    ob=TP;
+  if (!IsMember(ob))
+    return 0;
+  h=0; // Reihen bei naechster Abfrage neu sortieren
+  num=wimpy_row[ob];
+  if (num<2 || num>MAX_TEAMROWS) // Flucht in 1. Reihe nicht sinnvoll.
+    return 0;
+  if (num==wanted_row[ob]) // Ist schonmal nach hinten geflohen.
+    return 0;
+  tell_object(ob,sprintf("Du versuchst in Reihe %d zu fliehen.\n",num));
+  ob->SetProp(P_TEAM_WANTED_ROW,wanted_row[ob]=num);
+  DoChangeRow(ob,num);
+  if (PresentPositions(environment(ob))[ob]<=1) // Flucht gescheitert?
+    return 0;
+  return 1;
+}
+
+static int ChangeFormation(string arg) {
+  string *words;
+  int i,min,max;
+  mapping old_row;
+
+  if (arg=="aus")
+    arg="1-6 0-6 0-6 0-6 0-6";
+  i=sizeof(words=old_explode(arg," "));
+  if (i>MAX_TEAMROWS)
+    i=MAX_TEAMROWS;
+  for (--i;i>=0;i--) {
+    if (sscanf(words[i],"%d-%d",min,max)==2) {
+      formin[i]=min;
+      formax[i]=max;
+    } else if (sscanf(words[i],"%d",min)==1) {
+      formin[i]=formax[i]=min;
+    }
+  }
+
+  UpdateFormation();
+
+  words=({});
+  for (i=0;i<MAX_TEAMROWS;i++)
+    words+=({sprintf("%d-%d",formin[i],formax[i])});
+  gtell("Die Formation ist jetzt "+implode(words," / ")+".\n");
+  return 1;
+}
+
+int Shout(string str) {
+  if (!str || str=="")
+    return notify_fail("Was willst Du den anderen Teammitgliedern sagen?\n"),0;
+  gtell(str,TP->Name(WER),1);
+  return 1;
+}
+
+int Hist(string str) {
+  int i,anz,maximal;
+
+  // non-interactive oder Nicht-Mitglieder sollten die Hist nicht abfragen.
+  if (!IsInteractiveMember(TP)) return -1;
+
+  maximal=sizeof(hist);
+  if (str && sscanf(str,"%d",anz)==1)
+    i=maximal-anz;
+  if (i<0)
+    i=0;
+
+  TP->More(sprintf("%@s",hist[i..maximal])||"");
+  return 1;
+}
+
+private void DoChangeLeader(object ob) {
+  if (objectp(leader) && leader->QueryProp(P_TEAM_LEADER)==ME)
+    leader->SetProp(P_TEAM_LEADER,0);
+  leader=ob;
+  leader->SetProp(P_TEAM_LEADER,ME);
+}
+
+private int ChangeLeader(string arg) {
+  object ob;
+
+  if (stringp(arg) && arg!="") {
+    if (!objectp(ob=find_player(arg))
+        && !objectp(ob=present(arg,environment(TP))))
+      return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
+  } else {
+    ob=TP;
+  }
+  if (objectp(leader)
+      && TP!=leader
+      && (!interactive(TP) || (interactive(leader) && query_idle(leader)<180)))
+    return notify_fail("Der Teamleiter ist noch aktiv.\n"),0;
+  if (objectp(leader)
+      && query_once_interactive(leader)
+      && !query_once_interactive(leader))
+    return notify_fail("Nur ein Spieler kann das Team leiten.\n"),0;
+  if (!IsMember(ob))
+    return notify_fail(ob->Name(WER)+" ist kein Teammitglied.\n"),0;
+  DoChangeLeader(ob);
+  gtell(ob->Name(WER)+" leitet jetzt das Team.\n");
+  return 1;
+}
+
+varargs private int CheckSecond(object pl, object *obs) {
+  mixed x,ip;
+  mapping second;
+  object ob;
+  int i;
+
+  if (!objectp(pl))
+    pl=TP;
+  if (!query_once_interactive(pl))
+    return 0;
+  if (!pointerp(obs))
+    obs=Members();
+  obs-=({pl});
+  second=([]);
+  for (i=sizeof(obs)-1;i>=0;i--) {
+    if (!objectp(ob=obs[i]) || !query_once_interactive(ob)) continue;
+    second[getuid(ob)]=1;
+    if (stringp(x=ob->QueryProp(P_SECOND)))
+      second[lower_case(x)]=1;
+  }
+  if (second[getuid(pl)] ||
+      (stringp(x=pl->QueryProp(P_SECOND)) && second[lower_case(x)]))
+    return 1;
+
+  if (!stringp(ip=pl->QueryProp(P_CALLED_FROM_IP)) || ip=="")
+    return 0;
+  for (i=sizeof(obs)-1;i>=0;i--) {
+    if (!objectp(ob=obs[i]) || !query_once_interactive(ob)) continue;
+    if (ob->QueryProp(P_CALLED_FROM_IP)!=ip) continue;
+    log_file("rochus/zweitieverdacht",
+             sprintf("%s %s,%s\n",ctime()[4..15],getuid(pl),getuid(ob)));
+    break;
+  }
+  return 0;
+}
+
+static int Allowed(object pl) {
+  mixed x;
+  string nm;
+
+  if (!objectp(pl))
+    return notify_fail("WER soll ins Team aufgenommen werden?\n"),0;
+  if (pl==TP)
+    nm="Du b";
+  else
+    nm=pl->Name(WER)+" ";
+  if (objectp(x=pl->QueryProp(P_TEAM)) && x!=ME)
+    return notify_fail(nm+"ist schon in einem anderen Team.\n"),0;
+  if (pl->QueryGuest())
+    return notify_fail(nm+"ist ein Gast.\n"),0;
+  if (pl->QueryProp(P_GHOST))
+    return notify_fail(nm+"ist ein Geist.\n"),0;
+  if (CheckSecond(pl))
+    return notify_fail(nm+"ist Zweitspieler eines Teammitglieds.\n"),0;
+  if (sizeof(filter(Members(),"IsInteractiveMember",ME))
+      >=MAX_TEAM_MEMBERS)
+    return notify_fail("Es sind schon zuviele Spieler im Team.\n"),0;
+  return 1;
+}
+
+private void DoAddMember(object ob) {
+  closure cl;
+
+  if (!IsMember(leader))
+    DoChangeLeader(ob);
+  ob->SetProp(P_TEAM,ME);
+  if (IsMember(ob))
+    return;
+  is_member[ob]=1;
+  cl=symbol_function("QueryProp",ob);
+  attack_cmd[ob]=funcall(cl,P_TEAM_ATTACK_CMD);
+  autofollow[ob]=funcall(cl,P_TEAM_AUTOFOLLOW);
+  wimpy_row[ob]=funcall(cl,P_TEAM_WIMPY_ROW);
+  wanted_row[ob]=funcall(cl,P_TEAM_WANTED_ROW);
+  if (!wanted_row[ob]) wanted_row[ob]=1;;
+  ob->SetProp(P_TEAM_NEWMEMBER,0);
+  GetHpInfo(ob,cl);
+  if (query_once_interactive(ob)) ob->AddHpHook(ME);
+  if (!objectp(assoc_mem[ob]))
+    gtell(ob->Name(WER)+" wurde ins Team aufgenommen.\n");
+  DoChangeRow(ob,wanted_row[ob]);
+}
+
+int AddAssocMember(object caster, object npc) {
+  object *obs;
+
+  if (extern_call() && PO!=caster)
+    return 0;
+  if (!IsMember(caster)
+      || !objectp(npc)
+      || query_once_interactive(npc)
+      || IsMember(npc)
+      || objectp(assoc_mem[caster])
+      || assoc_mem[npc]
+      || caster==npc)
+    return 0;
+  assoc_mem[npc]=caster;
+  DoAddMember(npc);
+  if (!pointerp(obs=assoc_mem[caster]))
+    obs=({});
+  obs=(obs-({npc,0}))+({npc});
+  assoc_mem[caster]=obs;
+  return 1;
+}
+
+static void AddMemberAndAssocs(object caster) {
+  object *obs,ob;
+  int i;
+
+  DoAddMember(caster);
+  if (!pointerp(obs=caster->QueryProp(P_TEAM_ASSOC_MEMBERS)))
+    return;
+  for (i=sizeof(obs)-1;i>=0;i--)
+    if (objectp(ob=obs[i]) && !caster->IsEnemy(ob))
+      AddAssocMember(caster,ob);
+}
+
+int AddMember(object ob) {
+ 
+  if (!Allowed(ob))
+    return 0;
+  
+  // Wenn es einen TP gibt, unterliegt er einigen Einschraenkungen.
+  // Dafuer wird er aber via Aufnahme ins Team auch ggf. Teamleiter, wenn
+  // noetig.
+  // TODO: Dieser Code geht davon aus, dass er nur aus einem Kommando
+  // ausgefuehrt wird und TP der Veranlasser der Aufnahme ist. Dies scheint
+  // mir unrealistisch. (Zesstra)
+  if (TP && TP==previous_object()) {
+    if (!Allowed(TP))
+      return 0;
+    if (TP!=ob && CheckSecond(ob,({TP}))) {// Zweitiereglung bei Gruendung.
+      notify_fail(ob->Name(WER)+" ist Zweitspieler von Dir.\n");
+      return 0;
+    }
+    if (!IsMember(leader))
+      AddMemberAndAssocs(TP);
+    if (TP!=leader) {
+      notify_fail("Nur der Teamleiter kann Mitglieder aufnehmen.\n");
+      return 0;
+    }
+  }
+
+  AddMemberAndAssocs(ob);
+  return 1;
+}
+
+private void DoRemoveMember(object ob) {
+  object *tmembers,caster;
+  mixed asmem;
+  int i;
+
+  ob->SetProp(P_TEAM,0);
+  m_delete(is_member,ob);
+  m_delete(wanted_row,ob);
+  m_delete(act_row,ob);
+  m_delete(wimpy_row,ob);
+  m_delete(autofollow,ob);
+  m_delete(attack_cmd,ob);
+  if (objectp(caster=assoc_mem[ob]) && pointerp(asmem=assoc_mem[caster]))
+    assoc_mem[caster]=asmem-({ob,0});
+  if (query_once_interactive(ob)) ob->RemoveHpHook(ME);
+  m_delete(hp_info,ob);
+  m_delete(autoinf_hp,ob);
+  m_delete(autoinf_sp,ob);
+  DoChangeRow(ob,-1);
+
+  if (!objectp(assoc_mem[ob])) {
+    if (ob->QueryProp(P_GHOST)) {
+      gtell(upper_case(ob->name(WER))+" HAT DAS TEAM VERLASSEN.","Tod");
+    } else {
+      tell_object(ob,"Du verlaesst das Team.\n");
+      gtell(ob->Name(WER)+" hat das Team verlassen.\n");
+    }
+  }
+  m_delete(assoc_mem,ob);
+
+  if (IsMember(leader)) // Hat das Team noch einen Leiter?
+    return;
+  tmembers=Members();ob=0;
+  if (i=sizeof(tmembers)) {
+    ob=tmembers[0];
+    for (--i;i>=0;i--) {
+      if (interactive(tmembers[i])) {
+        ob=tmembers[i];
+        break;
+      }
+      if (query_once_interactive(tmembers[i]))
+        ob==tmembers[i];
+    }
+    DoChangeLeader(ob);
+    gtell(leader->Name(WER)+" hat die Teamleitung uebernommen.\n");
+    return;
+  }
+  TryRemove();
+}
+
+int RemoveAssocMember(object caster, object npc) {
+  object *obs;
+
+  if (extern_call() && PO!=caster)
+    return 0;
+  if (!IsMember(caster) || assoc_mem[npc]!=caster)
+    return 0;
+  DoRemoveMember(npc);
+  return 1;
+}
+
+static void RemoveMemberAndAssocs(object caster) {
+  object *obs,ob;
+  int i;
+
+  if (pointerp(obs=caster->QueryProp(P_TEAM_ASSOC_MEMBERS))) {
+    for (i=sizeof(obs)-1;i>=0;i--)
+      if (objectp(ob=obs[i]))
+        RemoveAssocMember(caster,ob);
+  }
+  DoRemoveMember(caster);
+}
+
+private void RemoveSingles() {
+  object *obs;
+  mixed aso;
+
+  if (!IsMember(leader)) return;
+  if (!query_once_interactive(leader)) return; // NPC Team
+  obs=Members()-({leader});
+  if (pointerp(aso=assoc_mem[leader]))
+    obs-=aso;
+  if (sizeof(obs)) return;
+  gtell("Das Team loest sich auf.\n");
+  RemoveMemberAndAssocs(leader);
+  TryRemove();
+}
+
+int RemoveMember(mixed arg) {
+  object *mems,mem,ob;
+  int i;
+  
+  if (objectp(arg)) {
+    ob=arg;
+  } else if (stringp(arg) && arg!="") {
+    mems=Members()-({leader});
+    for (i=sizeof(mems)-1;i>=0;i--) {
+      if (objectp(mem=mems[i]) && mem->id(arg))  {
+        ob=mem;
+        if (query_once_interactive(ob))
+          break;
+      }
+    }
+    if (!objectp(ob))
+      return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
+  } else {
+    return 0;
+  }
+  if (TP!=leader && TP!=ob && ob!=PO)
+    return notify_fail("Nur der Teamleiter kann Mitglieder entlassen.\n"),0;
+  if (!IsMember(ob) && ob->QueryProp(P_TEAM)!=ME)
+    return notify_fail(ob->Name(WER)+" ist kein Teammitglied.\n"),0;
+  if (PO!=ob && objectp(assoc_mem[ob]))
+    return notify_fail(ob->Name(WER)+" gehoert zu "+
+                       assoc_mem[ob]->Name(WEM)+".\n"),0;
+
+  RemoveMemberAndAssocs(ob);
+  RemoveSingles();
+  return 1;
+}
+
+static int ChangeName(string str) {
+  if (leader && TP!=leader)
+    return notify_fail("Nur der Teamleiter kann den Namen aendern.\n"),0;
+  if (!str || str=="")
+    return 0;
+  str=str[0..19];
+  if (!stringp(str=(TEAM_MASTER->RegisterTeam(str))))
+    return notify_fail("Der Name ist schon vergeben.\n"),0;
+  tname=str;
+  if (objectp(TP))
+    gtell(TP->Name(WER)+" aendert den Teamnamen auf \""+tname+"\".\n");
+  return 1;
+}
+
+void TeamInitAttack() {
+  object *obs,*ens,*removed,ob,en;
+  int i,j;
+
+  debug(sprintf("mis_init_att: %O\n",mis_init_att));
+  ens=m_indices(mis_init_att);removed=({});
+  for (i=sizeof(ens)-1;i>=0;i--) {
+    if (!objectp(en=ens[i]))
+      continue;
+    if (!pointerp(obs=mis_init_att[en]))
+      continue;
+    for (j=sizeof(obs)-1;j>=0;j--)
+      if (!IsMember(ob=obs[j]) || environment(ob)!=environment(en))
+        obs[j]=0;
+    obs-=({0});
+    ob=en->SelectNearEnemy(obs,1);
+    debug(sprintf("Begruessungsschlag von %O fuer %O\n",en,ob));
+    if (!objectp(ob))
+      continue;
+    en->Attack2(ob); // Begruessungsschlag
+    removed+=({en}); // Kein Begruessungsschlag mehr von diesem Monster
+  }
+
+  for (i=sizeof(mis_attacked)-1;i>=0;i--)
+    if (objectp(ob=mis_attacked[i]))
+      ob->ExecuteMissingAttacks(removed);
+  // Begruessungsschlaege die ausgefuehrt wurden entfernen
+  // und nicht ausgefuehrte nachholen
+
+  mis_attacked=({});
+  mis_init_att=([]);
+  att_exec=({});
+}
+
+int InitAttack_Callback(object enemy) {
+  object *arr;
+
+  if (!IsMember(PO) || member(att_exec,PO)<0)
+    return 0;
+  if (!pointerp(arr=mis_init_att[enemy]))
+    arr=({});
+  mis_init_att[enemy]=arr+({PO});
+  if (member(mis_attacked,PO)<0)
+    mis_attacked+=({PO});
+  return 1;
+}
+
+void TeamAttackExecuted_Callback(int success) {
+  if (!IsMember(PO))
+    return;
+  att_exec-=({PO,0});
+  if (!sizeof(att_exec))
+    TeamInitAttack();
+}
+
+private int StartAttack() {
+  object *tmembers,ob,env;
+  int i;
+
+  if (TP!=leader || !objectp(env=environment(TP)))
+    return notify_fail("Nur der Teamleiter kann den Angriff starten.\n"),0;
+  tmembers=Members();
+  TeamInitAttack(); // Falls noch Schlaege fehlen....
+  for (i=sizeof(tmembers)-1;i>=0;i--)
+    if (objectp(ob=tmembers[i]) && stringp(attack_cmd[ob]))
+      if (ob->CallTeamAttack(env)) // Angriff wird ausgefuehrt?
+        att_exec+=({ob});          // Liste der Angreifer
+  gtell(TP->Name(WER)+" startet den Angriff.\n");
+  return 1;
+}
+
+void StartFollow(object env) {
+  object *tmembers,ob;
+  int i;
+  string cmd,args;
+
+  // printf("ld:%O PO:%O TP:%O qv:%O env:%O\n",leader,PO,TP,query_verb(),env);
+  if (TP!=leader
+      || !stringp(cmd=query_verb())
+      || !objectp(env)
+      || TP!=PO
+      || TP->QueryProp(P_GHOST) // Der Befehl war wohl ungesund...
+      || TP->IsTeamMove()) // Angriffsbefehl nicht durch verfolge 2 mal...
+    return;
+  cmd="\\"+cmd;
+  if (stringp(args=TP->_unparsed_args()) && args!="")
+    cmd+=(" "+args);
+  tmembers=Members()-({leader});
+  for (i=sizeof(tmembers)-1;i>=0;i--)
+    if (objectp(ob=tmembers[i]) && autofollow[ob])    
+    	if(!query_once_interactive(ob)  || env==environment(ob)) // autofolge nur bei anfuehrer im gleichen raum
+    	      ob->CallTeamFollow(env,cmd);
+}
+
+void ShowTeamInfo() {
+  int i;
+  object *tmembers,ob;
+  string form;
+
+  if (!TI || TP!=TI || !IS_LEARNER(TI))
+    return;
+
+  UpdateActRow();
+  form="";
+  for (i=0;i<MAX_TEAMROWS;i++)
+    form+=sprintf(" %d-%d",formin[i],formax[i]);
+  printf("%s [%O] L: %s F:%s\n",name(),ME,
+         (objectp(leader)?leader->Name(WER):"?"),form);
+  tmembers=Members();
+  for (i=sizeof(tmembers)-1;i>=0;i--) {
+    ob=tmembers[i];
+    printf("  %d(%d) %s [%O]\n",
+           act_row[ob],wanted_row[ob],ob->Name(WER),ob);
+  }
+}
+
+varargs int ShowTeamHP(string arg) {
+  object *tmembers,ob;
+  closure qp;
+  int i,longinf;
+  mixed *vals,*res,cols,fr,rr,termstr;
+  mapping real_row;
+  string nm,inf;
+
+  if (arg && arg[0..3]=="lang") {
+    if (TP!=leader)
+      return notify_fail("Nur der Teamleiter kann die lange "+
+                         "Uebersicht aufrufen.\n"),0;
+    longinf=1;
+    arg=arg[5..];
+  } else
+    longinf=0;
+  real_row=PresentPositions(environment(TP));
+  tmembers=({});
+  for (i=0;i<MAX_TEAMROWS;i++)
+    tmembers+=rows[i];
+  res=({});
+  switch(TP->QueryProp(P_TTY)) {
+  case "ansi":
+    termstr=({ANSI_RED+ANSI_BOLD,ANSI_YELLOW,ANSI_GREEN,ANSI_NORMAL,
+              ANSI_UNDERL});
+    break;
+  case "vt100":
+    termstr=({ANSI_BOLD,"","",ANSI_NORMAL,ANSI_UNDERL});
+    break;
+  default:
+    termstr=({"","","","",""});
+  }
+  vals=({"",0,0,termstr[3],"",0,0,termstr[3]});
+
+  printf("  Name        Gilde           LV GLV  LP (MLP)  KP (MKP) Vors. GR AR TR FR A V\n");
+  if (longinf)
+    printf("  (Zeile 2)   Angriffsbefehl                             Fluchtrichtung\n");
+
+  for (i=sizeof(tmembers)-1;i>=0;i--) {
+    if (!objectp(ob=tmembers[i])) continue;
+    qp=symbol_function("QueryProp",ob);
+    fr=GetHpInfo(ob,qp);
+    vals[1]=fr[0];vals[2]=fr[1];vals[5]=fr[2];vals[6]=fr[3];
+    /*
+    vals[1]=funcall(qp,P_HP);vals[2]=funcall(qp,P_MAX_HP);
+    vals[5]=funcall(qp,P_SP);vals[6]=funcall(qp,P_MAX_SP);
+    */
+    if (!pointerp(cols=funcall(qp,P_TEAM_COLORS)) || sizeof(cols)<4)
+      cols=({vals[2]/4,vals[2]/2,vals[6]/4,vals[6]/2});
+    if (vals[1]<cols[0])
+      vals[0]=termstr[0];
+    else if (vals[1]<cols[1])
+      vals[0]=termstr[1];
+    else
+      vals[0]=termstr[2];
+    if (vals[5]<cols[2])
+      vals[4]=termstr[0];
+    else if (vals[5]<cols[3])
+      vals[4]=termstr[1];
+    else
+      vals[4]=termstr[2];
+    if (intp(fr=wimpy_row[ob]) && fr>1) fr=sprintf("%2d",fr); else fr="--";
+    if (intp(rr=real_row[ob]) && rr>0)  rr=sprintf("%2d",rr); else rr="--";
+    nm=ob->Name(RAW);
+    inf=sprintf("%s %-11s%s %-14s %3d %3d %s%3d (%3d)%s %s%3d (%3d)%s %5d %2d %2d %2s %2s %1s %1s\n",
+               ((ob==leader)?(termstr[4]+"*"):" "),
+               nm[0..10],termstr[3],
+               capitalize(funcall(qp,P_GUILD)||"")[0..13],
+               funcall(qp,P_LEVEL),
+               funcall(qp,P_GUILD_LEVEL),
+               vals[0],vals[1],vals[2],vals[3],
+               vals[4],vals[5],vals[6],vals[7],
+               funcall(qp,P_WIMPY),
+               wanted_row[ob],act_row[ob],rr,fr,
+               (attack_cmd[ob]?"X":"-"),
+               (autofollow[ob]?"X":"-"));
+    if (longinf) {
+      if (!stringp(fr=funcall(qp,P_WIMPY_DIRECTION))) fr="";
+      if (!stringp(rr=attack_cmd[ob])) rr="";
+      if (fr!="" || rr!="")
+        inf+=sprintf("              %-42s %-21s\n",rr[0..41],fr[0..20]);
+    }
+
+    switch (arg) {
+    case "alphabetisch":
+      res+=({({(query_once_interactive(ob)?"0":"1")+nm,inf})});
+      break;
+    case "sortiert":
+      res+=({({sprintf("%2s %2d %2d %s",rr,act_row[ob],wanted_row[ob],nm),
+                 inf})});
+      break;
+    default:
+      res+=({({i,inf})});
+    }
+  }
+  if (arg && arg!="")
+    res=sort_array(res,"CmpFirstArrayElement",ME);
+  for (i=sizeof(res)-1;i>=0;i--)
+    write(res[i][1]);
+  return 1;
+}
+
+varargs int ShowTeamRooms(string arg) {
+  object *tmembers,ob;
+  string s1,s2;
+  mixed *res;
+  int i;
+
+  tmembers=Members();res=({});
+  for (i=sizeof(tmembers)-1;i>=0;i--) {
+    if (!objectp(ob=tmembers[i])) continue;
+    if (!query_once_interactive(ob) && arg!="alle") continue;
+    s1=ob->Name(RAW);
+    if (!objectp(ob=environment(ob))) continue;
+    if (!stringp(s2=ob->QueryProp(P_INT_SHORT))) s2="";
+    res+=({({s1,s2})});
+  }
+  res=sort_array(res,"CmpFirstArrayElement",ME);
+  for (i=sizeof(res)-1;i>=0;i--)
+    printf("%-11s %-66s\n",res[i][0][0..10],res[i][1][0..65]);
+  return 1;
+}
+
+int ChangeColors(string arg) {
+  int *col;
+
+  notify_fail("team farben lp_rot lp_gelb [kp_rot kp_gelb]\n");
+  if (!arg)
+    return 0;
+  col=({0,0,0,0});
+  if (sscanf(arg,"%d %d %d %d",col[0],col[1],col[2],col[3])!=4) {
+    if (sscanf(arg,"%d %d",col[0],col[1])!=2)
+      return 0;
+    col[2]=col[0];col[3]=col[1];
+  }
+  printf("Die Anzeige ist jetzt gelb unter %d LP, rot unter %d LP\n"+
+         "                 bzw. gelb unter %d KP, rot unter %d KP.\n",
+         col[1],col[0],col[3],col[2]);
+  TP->SetProp(P_TEAM_COLORS,col);
+  TP->Set(P_TEAM_COLORS,SAVE,F_MODE_AS);
+  return 1;
+}
+
+int ChangeAutoInfo(string arg) {
+  string *words,txt;
+  int i,fl;
+
+  if (TP!=leader)
+    return notify_fail("Nur der Teamleiter kann automatisch "+
+                       "informiert werden.\n"),0;
+  words=old_explode(arg?arg:""," ");fl=0;
+  for (i=sizeof(words)-1;i>=0;i--) {
+    switch(words[i]) {
+    case "aus":
+      write("Du wirst nicht mehr automatisch informiert.\n");
+      autoinf_flags=0;
+      return 1;
+    case "+kp":
+      fl|=AUTOINF_SP_PLUS;
+    case "kp":
+      fl|=AUTOINF_SP_MINUS;
+      break;
+    case "+lp":
+      fl|=AUTOINF_HP_PLUS;
+    case "lp":
+    case "ein":
+    case "an":
+      fl|=AUTOINF_HP_MINUS;
+      break;
+    case "sofort":
+      fl|=AUTOINF_INSTANT;
+      break;
+    default:
+      ;
+    }
+  }
+  if (!fl)
+    return notify_fail("WIE moechtest Du automatisch informiert werden?\n"),0;
+  if (fl==AUTOINF_INSTANT) fl|=AUTOINF_HP_MINUS;
+  autoinf_flags=fl;
+  txt="Du wirst"+((fl&AUTOINF_INSTANT)?" sofort ":" ")+"informiert, wenn";
+  if (fl&(AUTOINF_HP_PLUS|AUTOINF_HP_MINUS)) {
+    txt+=" die Lebenspunkte eines Teammitglieds";
+    if (fl&(AUTOINF_HP_PLUS)) txt+=" sich aendern"; else txt+=" fallen";
+    if (fl&(AUTOINF_SP_PLUS|AUTOINF_SP_MINUS)) txt+=" oder";
+  }
+  if (fl&(AUTOINF_SP_PLUS|AUTOINF_SP_MINUS)) {
+    txt+=" die Konzentrationspunkte";
+    if (fl&(AUTOINF_SP_PLUS)) txt+=" sich aendern"; else txt+=" fallen";
+  }
+  write(break_string(txt+".\n",78));
+  return 1;
+}
+
+private void DoNotifyHpChange() {
+  object *obs,ob;
+  string str;
+  int i;
+
+  autoinf_time=time();
+  str="";
+  obs=m_indices(autoinf_hp);
+  for (i=sizeof(obs)-1;i>=0;i--) {
+    if (!objectp(ob=obs[i])) continue;
+    if (str!="") str+=", ";
+    str+=sprintf("%s: %d LP",ob->name(WER),autoinf_hp[ob]);
+    if (member(autoinf_sp,ob))
+      str+=sprintf(" / %d KP",autoinf_sp[ob]);
+    m_delete(autoinf_sp,ob);
+  }
+  obs=m_indices(autoinf_sp);
+  for (i=sizeof(obs)-1;i>=0;i--) {
+    if (!objectp(ob=obs[i])) continue;
+    if (str!="") str+=", ";
+    str+=sprintf("%s: %d KP",ob->name(WER),autoinf_sp[ob]);
+  }
+
+  if (str!="" && IsMember(leader))
+    tell_object(leader,break_string(capitalize(str)+"\n",78));
+  autoinf_hp=([]);
+  autoinf_sp=([]);
+}
+
+void NotifyHpChange() {
+  mixed act,old;
+  int change;
+
+  if (!IsMember(PO) || !pointerp(act=hp_info[PO]))
+    return;
+  old=act[0..];
+  act=GetHpInfo(PO);change=0;
+  if (!autoinf_flags) return;
+  if (((autoinf_flags&AUTOINF_HP_MINUS) && act[0]<old[0]) ||
+      ((autoinf_flags&AUTOINF_HP_PLUS)  && act[0]>old[0]))
+    autoinf_hp[PO]=act[0],change=1;
+  if (((autoinf_flags&AUTOINF_SP_MINUS) && act[2]<old[2]) ||
+      ((autoinf_flags&AUTOINF_SP_PLUS)  && act[2]>old[2]))
+    autoinf_sp[PO]=act[2],change=1;
+
+  if (autoinf_time<time() || (change && (autoinf_flags&AUTOINF_INSTANT)))
+    DoNotifyHpChange();
+}
+
+int TeamCmd(string cmd, string arg) {
+  if (!IsMember(TP)) {
+    notify_fail("Du bist kein Teammitglied.\n");
+    if (TP->QueryProp(P_TEAM)==ME)
+      TP->SetProp(P_TEAM,0);
+    return 0;
+  }
+  switch(cmd) {
+  case "angriff":
+    return StartAttack();
+  case "angriffsbefehl":
+    if (stringp(arg))
+      attack_cmd[TP]=arg;
+    else
+      m_delete(attack_cmd,arg);
+    return 1;
+  case "autofolge":
+  case "autof":
+    if (arg=="ein" || arg=="an")
+      autofollow[TP]=1;
+    else
+      m_delete(autofollow,TP);
+    return 1;
+  case "autoi":
+  case "autoinf":
+  case "autoinfo":
+  case "autoinform":
+    return ChangeAutoInfo(arg);
+  case "entlasse":
+    return RemoveMember(arg);
+  case "farben":
+    return ChangeColors(arg);
+  case "fluchtreihe":
+  case "flucht":
+    return ChangeWimpyRow(arg);
+  case "formation":
+    if (TP!=leader)
+      return notify_fail("Nur der Teamleiter kann die Formation aendern.\n"),0;
+    return ChangeFormation(arg);
+  case "hist":
+  case "history":
+    return Hist(arg);
+  case "":
+  case "info":
+    return ShowTeamHP(arg);
+  case "leiter":
+  case "leiterin":
+  case "leitung":
+    return ChangeLeader(arg);
+  case "name":
+    return ChangeName(arg);
+  case "orte":
+    return ShowTeamRooms(arg);
+  case "kampfreihe":
+  case "reihe":
+    return ChangeRow(arg);
+  case "ruf":
+  case "rufe":
+    return Shout(arg);
+  case "verlasse":
+    TP->SetProp(P_TEAM,0);
+    return RemoveMember(TP);
+  default:;
+  }
+  return 0;
+}
+
+void reset() {
+  RemoveSingles();
+  TryRemove();
+}
diff --git a/obj/testmonster.c b/obj/testmonster.c
new file mode 100644
index 0000000..aebf0b4
--- /dev/null
+++ b/obj/testmonster.c
@@ -0,0 +1,89 @@
+inherit "std/npc";
+#include <properties.h>
+#include <language.h>
+
+int warte;
+ 
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_NAME, "Testmonster");
+  SetProp(P_GENDER, NEUTER);
+  SetProp(P_SHORT, "Das Testmonster - nervtoetend -");
+  SetProp(P_ALIGN, 100);
+  SetProp(P_LONG, 
+  "Dies ist Testie, das Testmonster. Es hatte alle moeglichen, uebel aussehenden\n"+
+  "Testgeraete dabei. Leg Dich lieber nicht mit ihm an ...\n");
+  AddId("monster");
+  AddId("testmonster");
+  AddId("testie");
+  SetProp(P_RACE, "tester");
+  SetProp(P_LEVEL, 19);
+  SetProp(P_MAX_HP, 275);
+  SetProp(P_HP, 275);
+  SetProp(P_XP, 0);
+  SetProp(P_HANDS, ({" mit einem Spannungspruefer", 5}) );
+  SetProp(P_AC, 1);
+  seteuid(getuid(this_object()));
+  AddSpell(1,5,"Ein Testmonster schlaegt Dir ein Testheft um die Ohren.\n",
+               "Ein Testmonster schlaegt @WEN mit einem Testheft.\n");
+  AddSpell(1,8,"Ein Testmonster testet Deine Reaktion auf Hitze.\n",
+               "Ein Testmonster haelt ein Feuerzeug unter @WESSEN Hintern.\n");
+  AddSpell(1,11,"Ein Testmonster testet Deine Reaktion auf elektrischen Strom.\n",
+                "Ein Testmonster verpasst @WEM einen Elektroschock.\n");
+  AddSpell(1,15,"Ein Testmonster testet Deine Saeurebestaendigkeit.\n",
+                "Ein Testmonster ueberschuettet @WEN mit konzentrierter Saeure.\n");
+  AddSpell(1,10,"Ein Testmonster nimmt eine Blutprobe von Dir.\n",
+                "Ein Testmonster sticht @WEN mit einer gigantischen Nadel.\n");
+  SetProp(P_SPELLRATE,50);
+  warte=10;
+}
+
+give_notify(obj)
+{
+  write("Danke fuer "+obj->name(WEN, 1)+".\n");
+  return 1;
+}
+
+catch_tell(str)
+{
+  string s1, s2;
+
+  if (!str || str=="") return;
+  if (sscanf(str,"%s tritt%sTestmonster",s1,s2))
+  {
+    say_str(capitalize(name())+" sagt: Aua, wieso trittst Du mich, "+s1+"?\n");
+    return;
+  }
+  if (sscanf(str,"%s sagt: %s",s1,s2))
+  {
+    if (!s2) s2=" ";
+    s2=old_explode(s2,"\n");
+    if (sizeof(s2))
+      s2=implode(s2," ");
+    else
+      return;
+    say_str(capitalize(name())+" sagt: Wieso sagst Du \""+s2+"\", "+s1+"? \n");
+    return;
+  }
+}
+
+say_str(str)
+{
+  call_out("do_say",0,str);
+}
+
+do_say(str)
+{
+  say(str);
+}
+
+heart_beat()
+{
+  ::heart_beat();
+  warte--;
+  if (warte) return;
+  warte=8;
+  Flee();
+}
diff --git a/obj/tools/MGtool.c b/obj/tools/MGtool.c
new file mode 100644
index 0000000..727f34f
--- /dev/null
+++ b/obj/tools/MGtool.c
@@ -0,0 +1,1823 @@
+/*
+ *  MGtool-1.3
+ *  File: MGtool.c
+ *  Maintainer: Kirk@MorgenGrauen
+ */
+
+/*------------------------------------------*/
+/* the original Xtool is copyrighted by Hyp */
+/*------------------------------------------*/
+
+#pragma strict_types
+
+inherit "/std/thing/properties";
+
+#include <properties.h>
+#if !defined(QUERYCACHED)
+#define QUERYCACHED 4096
+#endif
+#if !defined (SETCACHED)
+#define SETCACHED 4096
+#endif
+#include <moving.h>
+#include "/secure/wizlevels.h"
+#include "/secure/config.h"
+#include <userinfo.h>
+#include <defines.h>
+
+#include "MGtool/tool.h"
+
+#include "MGtool/toollib.h"
+
+#define XDBG 1
+
+nosave object  cloner;
+nosave object  msgto=NULL;
+nosave string *manpath=({TOOL_PATH+"/man.d/",
+			 "/doc/efun/",
+			 "/doc/lfun/",
+			 "/doc/w/",
+			 "/doc/helpdir/",
+			 "/doc/LPC/",
+			 "/doc/std/",
+			 "/doc/concepts/",
+			 ""});
+nosave string  morefile=NULL;
+nosave string *scriptline=NULL;
+nosave string *history=allocate(MAX_HISTORY);
+nosave int     moreflag=FALSE;
+nosave int     moreoffset=1;
+nosave int     term=NULL;
+nosave int     scriptsize=NULL;
+nosave int     nostore=FALSE;
+nosave int     xlight=0;
+nosave int     pipe_in=FALSE;
+nosave int     pipe_out=FALSE;
+nosave int     pipe_ovr=TRUE;
+nosave string  pipe_if=NULL;
+nosave string  pipe_of=NULL;
+nosave int     xtk=FALSE;
+nosave mapping variable=([]);
+nosave string *cmds;
+private nosave mapping line_buffer=([]);
+private nosave string line_buffer_name="";
+private nosave string more_searchexpr="";
+int    morelines=MORE_LINES;
+int    modi=(MODE_FIRST|MODE_PROTECT|MODE_SHORT);
+
+#include "MGtool/toollib.c"
+#include "MGtool/toolcmd.c"
+
+#define SafeReturn(x) \
+{ \
+  cmds=({}); \
+  pipe_in=FALSE; \
+  pipe_of=NULL; \
+  pipe_ovr=TRUE; \
+  pipe_out=FALSE; \
+  pipe_of=NULL; \
+  return (x); \
+}
+
+/*----------------------------------------------------------------------
+ * check some security aspects
+ */
+
+static int security()
+{
+  object prev;
+ 
+  if( process_call() || getuid()!=getuid(cloner) ) return FALSE; // Rumata
+
+  TK("UID: "+getuid(ME)+" cloner:"+getuid(cloner));
+//  TK("prev: "+object_name(PREV)+" me:"+object_name(ME));
+  if(prev=PREV)
+  {
+    if(!cloner)
+      return TRUE;
+    if(getuid(prev)==ROOTID||IS_ARCH(prev))
+      return TRUE;
+    if(prev==ME)
+      return TRUE;
+    return secure_level()>=query_wiz_level(cloner); // Rumata
+    //return getuid(prev)==getuid()&&geteuid(prev)==geteuid()&&cloner==RTP;
+  }
+  else
+    return cloner==NULL;
+}
+
+/*----------------------------------------------------------------------
+ * own write function
+ */
+
+static int Write(string str)
+{
+  if(!stringp(str) || str=="")
+    return FALSE;
+  if(!cloner && objectp(this_player()))
+    write(str);
+  else
+    tell_object(cloner, str);
+  return TRUE;
+}
+
+/*----------------------------------------------------------------------
+ * own command function
+ */
+
+static int Command(string str)
+{
+  int i;
+  TK("Command: str: "+(str?str:"(NULL)"));
+  nostore++;
+  if(MODE(MODE_ECHO))
+    WLN("Doing: "+str);
+  i=(int)cloner->command_me(str);
+  nostore--;
+  return i;
+}
+
+/*----------------------------------------------------------------------
+ * object searching
+ */
+
+static varargs object XFindObj(string str, int silent)
+{
+  object obj, env;
+  string *strs;
+  int i, s, cnt;
+  
+  if(!str)
+    return NULL;
+  TK("XFindObj: str: "+(str?str:"(NULL)"));
+  env=ENV(cloner);
+  str=string_replace(str, "\\.","°01");
+  str=string_replace(str, "\\^", "°02");
+  str=string_replace(str, "\\$", "°03");
+  str=string_replace(str, "\\\\", "\\");
+  if (find_object(str)) return find_object(str);
+  if (file_size(str)>1) {
+     call_other(str, "???");
+     return find_object(str);
+  }
+  s=sizeof(strs=strip_explode(str, "."));
+  while(s--)
+  {
+    if(strs[i]=="m"||strs[i]=="me")
+      strs[i]=RNAME(cloner);
+    else if(strs[i]=="h"||strs[i]=="here")
+      strs[i]=object_name(ENV(cloner));
+    if(obj=FindObj(strs[i++],env,(silent?1:0)))
+      env=obj;
+    else
+      break;
+  }
+  return obj;
+}
+
+static varargs object FindObj(string str, object env, int silent)
+{
+  object obj, *inv;
+  string tmp;
+  int num, e;
+  
+  if (!stringp(str) || !sizeof(str) || !objectp(env))
+    return NULL;
+  str=string_replace(str, "°01", ".");
+  while(str[e++]=='^')
+    ;
+  str=str[--e..<1];
+  str=string_replace(str, "°02", "^");
+  if(obj=VarToObj(str))
+    ;
+  else if(str[0]=='')
+    str=string_replace(str, "°03", "$");
+  else if(sscanf(str, "%d", num)&&(inv=all_inventory(env)))
+  {
+    if(num>0&&num<=sizeof(inv))
+      obj=inv[num-1];
+    else
+    {
+      WDLN("Specified object number out of range [1-"+sizeof(inv)+"]");
+      return NULL;
+    }
+  }
+  if(obj||(obj=present(str, env))||
+     (obj=find_player(LOWER(str)))||
+     (obj=find_living(str))||
+     (obj=find_object(long_path(str))))
+  {
+    while(e--)
+    {
+      if(!(obj=ENV(obj)))
+      {
+	W("Specified object has no environment [");
+	while(e--)
+	  W("^");
+	WDLN(str+"]");
+	return NULL;
+      }
+    }
+  }
+  else
+    if(!silent)
+      WDLN("Specified object does not exist ["+str+"]");
+  return obj;
+}
+
+/*----------------------------------------------------------------------
+ * object variable handling
+ */
+
+static object VarToObj(string str)
+{
+  if (!stringp(str) || !sizeof(str) || str[0]!='$')
+    return NULL;
+  switch(str)
+  {
+    case "$m":
+    case "$me":
+    return cloner;
+    case "$h":
+    case "$here":
+    return ENV(cloner);
+    default:
+    return variable[str[1..<1]];
+  }
+  return(NULL); //never reached
+}
+
+static string VarToFile(string str)
+{
+  return source_file_name(VarToObj(str));
+}
+
+static string VarToPureFile(string str)
+{
+  return pure_file_name(VarToObj(str));
+}
+
+/*----------------------------------------------------------------------
+ * object description printing
+ */
+
+static void PrintObj(object obj, string file)
+{
+  object item, tmp;
+  string str;
+  int i;
+
+  SECURE1();
+  if(!obj)
+    return;
+  PrintShort("Short: ", obj, file);
+  if(!file||file=="")
+    WLN("Long :");
+  else
+    write_file(file,"Long :\n");
+  if(query_once_interactive(obj))
+    str=capitalize(getuid(obj));
+  else
+  {
+    if(object_name(obj)[0..26]=="/d/unterwelt/objekte/teddy#" &&
+       IS_ARCH(this_interactive()))
+      str="Ein Teddy (stumm)";
+    else
+    {
+      if(str=(string)obj->QueryProp(P_INT_LONG))
+	;
+      else if(str=(string)obj->QueryProp(P_LONG))
+	;
+      else
+	str="- no long description -\n";
+    }
+  }
+  if(!file||file=="")
+    W(str);
+  else
+    write_file(file,str);
+  FORALL(item, obj)
+  {
+    if(!i)
+      if(!file||file=="")
+	WLN("Content:");
+      else
+	write_file(file,"Content:\n");
+    PrintShort(ARIGHT(++i+". ", 4, " "), item, file);
+  }
+}
+
+static string ObjFile(object obj)
+{
+  return "["+short_path(object_name(obj))+"]";
+}
+
+static varargs void PrintShort(string pre, object obj, string file)
+{
+  string str;
+
+  SECURE1();
+  if(!obj)
+    return;
+  if(MODE(MODE_SHORT))
+  {
+    if (query_once_interactive(obj))
+      str=capitalize(getuid(obj));
+    else
+    {
+      if(!((str=(string)obj->QueryProp(P_INT_SHORT))||
+	   (str=(string)obj->QueryProp(P_SHORT))))
+	if(is_player(obj))
+	  str=CRNAME(obj)+" (invisible)";
+	else
+	  str="- no short description -";
+    }
+    str=ALEFT(sprintf("%O ",str), 34, ".")+" ";
+  }
+  else
+    str="";
+  if(interactive(obj))
+    str+="i";
+  else if(living(obj))
+    str+="l";
+  else
+    str+=".";
+  str+=(shadow(obj, 0) ? "s" : ".");
+  if(!file||file=="")
+    WLN((pre+CAP(str)+" "+ObjFile(obj))[0..79]);
+  else
+    write_file(file,(pre+CAP(str)+" "+ObjFile(obj))[0..79]+"\n");
+}
+
+static varargs void DeepPrintShort(object env, int indent, string pre, string file)
+{
+  int i;
+  object item;
+  string str;
+  
+  SECURE1();
+  if(!env)
+    return;
+  for(i=indent,str=""; i--; str+="  ");
+  if(pre)
+    str+=pre;
+  if(!file||file=="")
+    W(str);
+  else
+    write_file(file,str);
+  i++;
+  PrintShort("",env,file);
+  FORALL(item, env)
+    DeepPrintShort(item,indent+1,ARIGHT((++i)+". ",4," "),file);
+}
+
+static string break_string_hard(string str, int len, string pre)
+{
+  int s,p,t;
+  string tmp;
+
+  if(!str||!(s=sizeof(str)))
+    return "";
+  t=len-(p=sizeof(pre))-1;
+
+  tmp="";
+  while(p+s>len)
+  {
+    tmp+=pre+str[0..t]+"\n";
+	str=str[t+1..];
+	s-=t;
+  }
+  if(sizeof(str))
+    tmp+=pre+str[0..]+"\n";
+  return tmp;
+}
+  
+static void dprop(string key, mixed data, object obj)
+{
+  if(pipe_out&&pipe_of)
+    write_file(pipe_of,break_string_hard(mixed_to_string(obj->QueryProp(key),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+  else
+    W(break_string_hard(mixed_to_string(obj->QueryProp(key),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+}
+
+static string propflags(string key, object ob)
+{
+  int tmp;
+  string *flags;
+  tmp=(int)ob->Query(key,1);
+  flags=({});
+  tmp&SAVE ?	       flags+=({"SAV"}) : flags+=({"   "});
+  tmp&PROTECTED ?      flags+=({"PRO"}) : flags+=({"   "});
+  tmp&SECURED ?        flags+=({"SEC"}) : flags+=({"   "});
+  tmp&NOSETMETHOD ?    flags+=({"NSM"}) : flags+=({"   "});
+  tmp&SETMNOTFOUND ?   flags+=({"SMN"}) : flags+=({"   "});
+  tmp&QUERYMNOTFOUND ? flags+=({"QMN"}) : flags+=({"   "});
+  tmp&QUERYCACHED ?    flags+=({"QCA"}) : flags+=({"   "});
+  tmp&SETCACHED ?      flags+=({"SCA"}) : flags+=({"   "});
+  return ""+implode(flags,"|");
+}
+
+static void dprop2(string key, mixed data, object ob)
+{
+  if(pipe_out&&pipe_of)
+    write_file(pipe_of,break_string_hard(mixed_to_string(propflags(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+  else
+    W(break_string_hard(mixed_to_string(propflags(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+}
+
+static mixed propmethods(string key, object ob)
+{
+  return (mixed)ob->Query(key,2);
+}
+
+static void dprop3(string key, mixed data, object ob)
+{
+  if(pipe_out&&pipe_of)
+    write_file(pipe_of,break_string_hard(mixed_to_string(propmethods(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+  else
+    W(break_string_hard(mixed_to_string(propmethods(key,ob),MAX_RECURSION),78,ALEFT(key+" ",18,".")+" = "));
+}
+
+static void DumpProperties(object obj, int flag)
+{
+  SECURE1();
+  if(!obj)
+    return;
+  PIPE_DELETE(pipe_of);
+  switch (flag) {
+    case 0:
+      walk_mapping(((mapping *)(obj->__query_properties()))[0],#'dprop,obj); //')
+      break;
+    case 1:
+      walk_mapping(((mapping *)(obj->__query_properties()))[0],#'dprop2,obj); //')
+      break;
+    case 2:
+      walk_mapping(((mapping *)(obj->__query_properties()))[0],#'dprop3,obj); //')
+      break;
+  }
+}
+
+/*----------------------------------------------------------------------
+ * moving objects
+ */
+
+static int MoveObj(object obj1, object obj2, int silent)
+{
+  int err;
+  object oldenv;
+
+  SECURE2(FALSE);
+  if(!(obj1&&obj2))
+    return FALSE;
+  oldenv=ENV(obj1);
+  err=(int)obj1->move(obj2, M_SILENT|M_NOCHECK);
+  if(!silent)
+    switch(err)
+    {
+      case ME_PLAYER:
+      WDLN("Object returned ME_PLAYER");
+      if(oldenv && oldenv != ENV(obj1))
+	WDLN("Object has been moved");
+      return FALSE;
+      case ME_TOO_HEAVY:
+      WDLN("Object returned ME_TOO_HEAVY");
+      if(oldenv && oldenv != ENV(obj1))
+	WDLN("Object has been moved");
+      return FALSE;
+      case ME_CANT_TPORT_IN:
+      WDLN("Object returned ME_CANT_TPORT_IN");
+      if(oldenv && oldenv != ENV(obj1))
+	WDLN("Object has been moved");
+      return FALSE;
+      case ME_CANT_TPORT_OUT:
+      WDLN("Object returned ME_CANT_TPORT_OUT");
+      if(oldenv && oldenv != ENV(obj1))
+	WDLN("Object has been moved");
+      return FALSE;
+      default:
+      WDLN("Object returned unknown return value");
+      return FALSE;
+    }
+  return TRUE;
+}
+
+/*----------------------------------------------------------------------
+ * save destructing of objects
+ */
+
+static void Destruct(object obj)
+{
+  if(!obj || !this_object())
+    return;
+  catch(obj->remove());
+  if(objectp(obj) && !query_once_interactive(obj))
+    destruct(obj);
+}
+
+static void DeepClean(object obj)
+{
+  if(!obj)
+    return;
+  filter(filter(deep_inventory(obj), "is_not_player", ME),
+	       "Destruct", ME);
+  if(is_not_player(obj))
+    Destruct(obj);
+}
+
+/*----------------------------------------------------------------------
+ * Show the inheritance tree of objects
+ */
+
+static object *SubNodes(object obj)
+{
+  int s;
+  object *objs;
+  string *inlist;
+  
+  if(!obj)
+    return NULL;
+  inlist=inherit_list(obj);
+  s=sizeof(inlist);
+  objs=({});
+  while(s-->1)
+    objs=({find_object(inlist[s])})+objs;
+  return objs;
+}
+
+static void Inheritance(object obj, string func, string pre)
+{
+  int i, s;
+  object *ln;
+  string str;
+  
+  if(!obj)
+    return;
+  str=pre+" "+ObjFile(obj);
+  if(func)
+  {
+    str=ALEFT(str+" ", 50, ".");
+    if(function_exists(func, obj)==object_name(obj))
+      str+=ARIGHT(" "+func, 19, ".");
+    else
+      str+=ARIGHT("", 19, ".");
+  }
+  if(pipe_out&&pipe_of)
+    write_file(pipe_of,str+"\n");
+  else
+    WLN(str);
+  ln=SubNodes(obj);
+  for(i=0; i<sizeof(ln); i++)
+    ln=ln-SubNodes(ln[i]);
+  s=sizeof(ln);
+  for(i=0; i<s; i++)
+    Inheritance(ln[i], func, pre+".....");
+}
+
+/*----------------------------------------------------------------------
+ * file name handling
+ */
+
+static string XFile(string file)
+{
+  TK("XFile: file: "+(file?file:"(NULL)"));
+  if(file)
+    switch(file[0])
+    {
+      case '@':
+	return source_file_name(XFindObj(file[1..<1]));
+      case '$':
+	return source_file_name(XFindObj(file));
+      default:
+	return old_explode(long_path(file),"#")[0];
+    }
+  return NULL;
+}
+
+static string XFindFile(string file)
+{
+  TK("XFindFile: file: "+(file?file:"(NULL)"));
+  if(file=XFile(file))
+  {
+    if(file_size(file)>=0)
+      return file;
+    if(file[<3..<1]!=".c" && file_size(file+".c")>0)
+      return file+".c";
+  }
+  WDLN("File not found or not readable ["+short_path(file)+"]");
+  return NULL;
+}
+
+/*----------------------------------------------------------------------
+ * file printing, searching and executing
+ */
+
+static void XMoreFile(string file, int flag)
+{
+  int s,size;
+  
+  SECURE1();
+  if(!file)
+    return;
+  
+  // term=(string)cloner->QueryProp(P_TTY)!="dumb";
+  if((size=(file_size(morefile=long_path(file))))>0)
+  {
+    if(size>100000)
+      WDLN("Warning: large file");
+    MoreFile(NULL);
+  }
+  else if(flag)
+    WDLN("Cannot read file");
+}
+
+/*
+static string read_buffer(string filename, int start, int number)
+{
+  mixed tmp;
+  string result;
+  int current;
+
+  if(!filename||filename=="")
+    return "";
+  if(number<=1)
+    number=1;
+  number--;
+  result="";
+  if(filename!=line_buffer_name)
+  {
+    // TK("read_buffer: initializing");
+    line_buffer=([]);
+    line_buffer_name=filename;
+    current=0;
+  }
+  else
+    current=start;
+
+  while(current<=start+number && stringp((tmp=load_buffer(current++))))
+    if(current>=start)
+      result+=tmp;
+  return result;
+}
+
+static string load_buffer(int line)
+{
+  int i, current, start, length, *idx;
+
+  if(intp(line_buffer[line]))
+  {
+    // TK("load_buffer: Notfound: "+line);
+    current=line;
+    while(current>0&&intp(line_buffer[--current]));
+    for(i=current;i<=line;i++)
+    {
+      // TK("load_buffer: trying: "+i);
+      if(i>0)
+	idx=line_buffer[i-1];
+      else
+	idx=({0,0});
+      length=read_line((start=idx[0]+idx[1]));
+      // TK("load_buffer: "+start+"/"+length);
+      line_buffer[i]=({start,length});
+    }
+  }
+  idx=line_buffer[line];
+  return
+    read_bytes(line_buffer_name,idx[0],idx[1]);
+}
+
+static int read_line(int offset)
+{
+  mixed loc_buf;
+  int length, pos;
+
+  // TK("read_line: "+offset);
+  length=0;
+  while(!intp((loc_buf=read_bytes(line_buffer_name,offset+length,1024))))
+  {
+    if((pos=member(loc_buf,'\n'))>=0)
+      return length+pos+1;
+    length+=1024;
+  }
+  return length;
+}
+*/
+
+static void MoreFile(string str)
+{
+  int i, off;
+  string f, l, r;
+  
+  SECURE1();
+  
+  if (str /*&& sizeof(str)*/)
+  {
+		if( !sizeof(str) ) str="\0";
+    if(term)
+      W("M");
+    switch(str[0])
+    {
+      case 'q':
+      case 'x':
+      moreflag=FALSE;
+      moreoffset=1;
+      if(morefile==TMP_FILE||morefile==PIPE_FILE)
+	rm(morefile);
+      return NULL;
+      break;
+      case 'P':
+      case 'U':
+      moreflag=FALSE;
+      moreoffset=moreoffset-morelines;
+      case 'p':
+      case 'u':
+      moreoffset=moreoffset-2*morelines;
+      break;
+      case 'D':
+      case 'N':
+      moreoffset+=morelines;
+      case 0:	/* RETURN */
+      case 'd':
+      if(moreflag)
+      {
+	moreflag=FALSE;
+	moreoffset=1;
+	if(morefile==TMP_FILE)
+	  rm(morefile);
+	return;
+      }
+      break;
+      case '/':
+      moreoffset--;
+      more_searchexpr=str[1..<1];
+      case 'n':
+      i=moreoffset-morelines+1;
+      if(more_searchexpr=="")
+      {
+	WDLN("No previous regular expression");
+	return;
+      }
+      if(!regexp(({"dummy"}), more_searchexpr))
+	WDLN("Bad regular expression");
+      else
+	while((l=read_file(morefile, i++, 1))&&
+	      !sizeof(regexp(({l}), more_searchexpr)))
+	  ;
+      if(l)
+      {
+	WLN("*** Skipping ...");
+	moreoffset=i-1;
+      }
+      else
+      {
+	WLN("*** Pattern not found");
+	moreoffset-=morelines;
+      }
+      break;
+      case '0'..'9':
+      sscanf(str, "%d", i);
+      moreoffset=i;
+      break;
+    }
+  }
+  else
+  {
+    moreoffset=1;
+    moreflag=FALSE;
+  }
+  if(moreoffset<1)
+    moreoffset=1;
+  if(CatFile())
+    W("*** More: q,u,U,d,D,/<regexp>,<line> ["+(moreoffset-1)+"] *** ");
+  else
+  {
+    W("*** More: q,u,U,d,D,/<regexp>,<line> ["+(moreoffset-1)+"=EOF] *** ");
+    moreflag=TRUE;
+  }
+  input_to("MoreFile");
+  return;
+}
+
+// Schade eigentlich das ich es neuschreiben musste, aber es ist
+// schneller neu geschrieben als durch die undokumentieren alten Funktionen
+// durchzusteigen... *seufz*
+//					  Padreic
+
+static string last_file, *last_file_buffer;
+static int    last_file_date, last_file_size, last_file_complete;
+
+static string sread_line(int num)
+{
+   if (!morefile) return "";
+   if (last_file!=morefile || last_file_date!=file_time(morefile)) {
+      if (!(last_file=read_bytes(morefile, 0, 50000))) return "";
+      last_file_date=file_time(morefile);
+      last_file_buffer=explode(last_file, "\n");
+      last_file_size=sizeof(last_file_buffer);
+      if (sizeof(last_file)==50000 && last_file[<1]!='\n') {
+	 last_file_size--; // letzte Zeile nicht vollstaendig gelesen
+	 last_file_complete=0;
+      }
+      else if (file_size(morefile)>50000)
+	last_file_complete=0;
+      else last_file_complete=1;
+      last_file=morefile;
+   }
+   if (num==0) num=1;
+   // bei zu grossen Files nicht mehr alles buffern...
+   if (num>last_file_size) {
+      if (last_file_complete) return "";
+      return (read_file(morefile, num, 1) || "");
+   }
+   return last_file_buffer[num-1]+"\n";
+}
+
+static int CatFile()
+{
+  int end;
+  string l;
+  
+  end=moreoffset+morelines;
+  while(moreoffset<end)
+//    if((l=read_file(morefile, moreoffset, 1))!="")
+    if((l=sread_line(moreoffset))!="")
+    {
+      moreoffset++;
+      W(l);
+    }
+    else
+      return FALSE;
+//  if(read_file(morefile, moreoffset+1, 1)!="")
+  if(sread_line(moreoffset+1)!="")
+    return TRUE;
+  else
+    return FALSE;
+}
+
+static int XGrepFile(string pat, string file, int mode)
+{
+  int i, j, f, s, fsize;
+  string tfile, *tmp, *ts, t;
+  
+  SECURE2(FALSE);
+  TK("XGrepFile: pat: "+pat+" file: "+file+" mode: "+mode);
+  if(!(pat&&file))
+    return FALSE;
+  tfile=TMP_FILE;
+  fsize=file_size(file);
+  for(i=0,f=0; i<fsize && t=read_bytes(file, i, 50000); i+=50000)
+    tmp=strip_explode(t,"\n");
+    if (t && t[<1]!='\n' && sizeof(t)==50000) {
+       i-=sizeof(tmp[<1]);
+       tmp=tmp[0..<2];
+    }
+    if(s=sizeof(tmp))
+    {
+      for(j=0;j<s;j++)
+      {
+	if(sizeof(ts=regexp(({(mode&XGREP_ICASE?lower_case(tmp[j]):tmp[j])}),pat)))
+	{
+	  if(!(mode&XGREP_REVERT))
+	   {
+	     if(!f++)
+	       write_file(tfile, "*** File: "+file+" ***\n");
+	     write_file(tfile, tmp[j]+"\n");
+	   }
+	}
+	else if(mode&XGREP_REVERT)
+	{
+	  if(!f++)
+	    write_file(tfile, "*** File: "+file+" ***\n");
+	  write_file(tfile, tmp[j]+"\n");
+	}
+      }
+    }
+  return TRUE;
+}
+
+static void XExecFile(int line)
+{
+  int i;
+  
+  if(!scriptline)
+    return;
+  for(i=line; i<scriptsize&&i<line+EXEC_LINES; i++)
+  {
+    if(!scriptline[i])
+      continue;
+    if(!Command(scriptline[i]))
+    {
+      scriptline=NULL;
+      return;
+    }
+  }
+  if(i<scriptsize)
+    call_out("XExecFile", EXEC_TIME, i);
+  else
+    scriptline=NULL;
+}
+
+static void XmtpScript(string dir, string file, string opt)
+{
+  int s, t;
+  string *files;
+  
+  s=sizeof(files=get_dir(dir+"/*"));
+  while(s--)
+  {
+    t=sizeof(files[s])-1;
+    if(files[s] == ".." || files[s] == "." || files[s][t] == '~' ||
+       (files[s][0] == '#' && files[s][t] == '#'))
+      continue;
+    if(file_size(dir+"/"+files[s])==-2)
+    {
+      write_file(file, "mkdir "+files[s]+" ; cd "+files[s]+"\n");
+      XmtpScript(dir+"/"+files[s], file, opt);
+      write_file(file, "cd ..\n");
+    }
+    else
+      write_file(file, "mtp -r "+opt+" "+dir+"/"+files[s]+"\n");
+  }
+}
+
+/*----------------------------------------------------------------------
+ *  player properties handling
+ */
+
+static string PlayerIdle(object obj)
+{
+  string str;
+  int i, tmp;
+  
+  if(!obj)
+    return NULL;
+  if((i=query_idle(obj))>=60)
+  {
+    str=ARIGHT(""+(i/3600), 2, "0");
+    i-=(i/3600)*3600;
+    str+="'"+ARIGHT(""+(i/60), 2, "0");
+  }
+  else
+    str=".....";
+  return str;
+}
+
+static string PlayerAge(object obj)
+{
+  string str;
+  int i, tmp;
+  
+  if(!obj)
+    return NULL;
+  i=(int)obj->QueryProp(P_AGE);
+  str=" "+ARIGHT(""+(i/43200), 4, ".");
+  i-=(i/43200)*43200;
+  return str+":"+ARIGHT(""+(i/1800), 2, "0");
+}
+
+static string crname(object who)
+{
+  string uid, lname;
+
+  if((uid=getuid(who))==ROOTID && 
+     object_name(who)[0..7]=="/secure/" &&
+     (lname=(string)who->loginname()))
+    return CAP(lname);
+  return CAP(uid);
+}
+
+static string PlayerWho(object obj)
+{
+  object tmp;
+  string str, stmp;
+  str=ARIGHT(""+LEVEL(obj)  ,  3, " ");
+  str+=ALEFT(" "+crname(obj)+" ", 12, ".");
+  str+=PlayerAge(obj);
+  str+=((int)obj->QueryProp(P_GENDER)==1 ? " m " : " f ");
+  str+=(obj->QueryProp(P_FROG))  ? "f" : ".";
+  str+=(obj->QueryProp(P_GHOST)) ? "g" : ".";
+  str+=(obj->QueryProp(P_INVIS)) ? "i" : ".";
+  str+=(query_editing(obj)||query_input_pending(obj) ? "e" : ".");
+  str+=(obj->QueryProp(P_AWAY))  ? "a" : ".";
+  str+=" "+PlayerIdle(obj)+" ";
+  str+=(tmp=ENV(obj)) ? ObjFile(tmp) : "- fabric of space -";
+  return str;
+}
+
+static string PlayerMail(object obj, int flag)
+{
+  string pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  return pre+"mail: "+obj->QueryProp(P_MAILADDR);
+}
+
+static string PlayerIP(object obj, int flag)
+{
+  string pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  return pre+"host: "+query_ip_name(obj)+" ("+query_ip_number(obj)+")";
+}
+
+static string PlayerRace(object obj, int flag)
+{
+  string tmp, pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  pre=pre+"race: "+ALEFT(obj->QueryProp(P_RACE)+" ", 10, ".")+" guild: ";
+  tmp=(string)obj->QueryProp(P_GUILD);
+  return tmp ? pre+tmp : pre+"- none -";
+}
+
+static string PlayerDomain(object obj, int flag)
+{
+  int i, s;
+  mixed *uinfo;
+  string *domains, pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  pre+="domainlord of: ";
+  uinfo=(mixed*)MASTER->get_userinfo(getuid(obj));
+  if(uinfo&&(domains=uinfo[3])&&(s=sizeof(domains)))
+  {
+    for(i=0; i<s; i++)
+    {
+      pre += CAP(domains[i]);
+      if(i<s-1)
+	pre += ", ";
+    }
+  }
+  return pre;
+}
+
+static string PlayerStats(object obj, int flag)
+{
+  string pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  pre+="hp="+ARIGHT(obj->QueryProp(P_HP), 3, "0");
+  pre+="/"+ARIGHT(obj->QueryProp(P_MAX_HP), 3, "0");
+  pre+=" sp="+ARIGHT(obj->QueryProp(P_SP), 3, "0");
+  pre+="/"+ARIGHT(obj->QueryProp(P_MAX_SP), 3, "0");
+  pre+=" food="+ARIGHT(obj->QueryProp(P_FOOD), 3, "0");
+  pre+="/"+ARIGHT(obj->QueryProp(P_MAX_FOOD), 3, "0");
+  pre+=" drink="+ARIGHT(obj->QueryProp(P_DRINK), 3, "0");
+  pre+="/"+ARIGHT(obj->QueryProp(P_MAX_DRINK), 3, "0");
+  pre+=" exps="+obj->QueryProp(P_XP);
+  return pre;
+}
+
+static string PlayerSnoop(object obj, int flag)
+{
+  string tmp, pre;
+  object victim;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  pre=pre+"is snooped by: ";
+  if(victim=query_snoop(obj))
+    pre+=ARIGHT(" "+crname(victim), 12, ".");
+  else
+    pre+="............";
+  return pre;
+}
+
+static string PlayerCmdAvg(object obj, int flag)
+{
+  string pre;
+  
+  pre=(flag) ? ALEFT(crname(obj)+" ", 12, ".")+" " : "";
+  return pre+"cmdavg: "+(int)obj->_query_command_average();
+}
+
+
+/*----------------------------------------------------------------------
+ * msg input to objects
+ */
+
+static void XMsgSay(string str)
+{
+  if(str=="."||str=="**")
+  {
+    WLN("[End of message]");
+    say("[End of message]\n");
+  }
+  else
+  {
+    say(str+"\n");
+    input_to("XMsgSay");
+  }
+}
+
+static void XMsgTell(string str)
+{
+  if(str=="."||str=="**")
+  {
+    WLN("[End of message]");
+    tell_object(msgto, "[End of message]\n");
+  }
+  else
+  {
+    tell_object(msgto, str+"\n");
+    input_to("XMsgTell");
+  }
+}
+
+static void XMsgShout(string str)
+{
+  if(str=="."||str=="**")
+  {
+    WLN("[End of message]");
+    shout("[End of message]\n");
+  }
+  else
+  {
+    shout(str+"\n");
+    input_to("XMsgShout");
+  }
+}
+
+/*----------------------------------------------------------------------
+ * own object moving
+ */
+
+int move(mixed dest)
+{
+  move_object(ME, cloner?cloner:dest);
+  return TRUE;
+}
+
+/*----------------------------------------------------------------------
+ * object id
+ */
+
+int id(string str)
+{
+  if(!security()&&MODE(MODE_SCANCHK)&&RTP&&!IS_ARCH(RTP))
+    WDLN(crname(RTP)+" scanned you (id) ["+query_verb()+"] "+
+	 (PREV ? ObjFile(PREV) : "[destructed object]"));
+  return LOWER(str)==LOWER(TOOL_NAME);
+}
+
+/*----------------------------------------------------------------------
+ * short and long description
+ */
+
+string short()
+{
+  return _query_short()+".\n";
+}
+
+string _query_short()
+{
+  string sh; // added by Rumata
+  if(cloner)
+  {
+    if((!security())&&MODE(MODE_SCANCHK)&&!IS_ARCH(RTP))
+      WDLN(crname(RTP)+" scanned you (short) ["+query_verb()+"] "+
+	   (PREV ? ObjFile(PREV) : "[destructed object]"));
+	if( sh=Query(P_SHORT) ) return sh; // added by Rumata
+    return cloner->name(WESSEN)+" "+TOOL_TITLE+" ["+
+      ctime(time())[11..18]+"]";
+  }
+  return TOOL_TITLE;
+}
+
+string long()
+{
+  return _query_long();
+}
+
+string _query_long()
+{
+  if(cloner&&!security()&&MODE(MODE_SCANCHK)&&!IS_ARCH(RTP))
+  {
+    WDLN(crname(RTP)+" scanned you (long) ["+query_verb()+"] "+
+	 (PREV ? ObjFile(PREV) : "[destructed object]"));
+  }
+  return 
+    "This is "+TOOL_NAME+" version "+TOOL_VERSION+
+    " (maintained by Kirk@MorgenGrauen)\n"+
+    "Original copyright held by Hyp.\n"+
+    "Gamedriver patchlevel: "+__VERSION__+" master object: "+__MASTER_OBJECT__+
+    "\n\nDo 'xhelp' for more information.\n";
+}
+
+string name(mixed dummy1, mixed dummy2)
+{
+  return _query_short();
+}
+
+/*----------------------------------------------------------------------
+ * light stuff
+ */
+
+int _query_light()
+{
+  return xlight;
+}
+
+int _set_light(int x)
+{
+  return xlight;
+}
+
+/*----------------------------------------------------------------------
+ * Autoloading
+ */
+
+mixed *_query_autoloadobj()
+{
+  return AUTOLOAD_ARGS;
+}
+
+void _set_autoloadobj(mixed *args)
+{
+  WLN(TOOL_TITLE+" ...");
+  if(!pointerp(args))
+    ;
+  else if(sizeof(args)!=3)
+    ;
+  else if(!stringp(args[0]))
+    ;
+  else if(!intp(args[1]))
+    ;
+  else if(!intp(args[2]))
+    ;
+  else
+  {
+    if((string)args[0]!=TOOL_INTERNAL)
+    {
+      WLN("*****************************");
+      WLN("***	    NEW EDITION      ***");
+      WLN("***	do 'xtool news' for  ***");
+      WLN("***	 more information    ***");
+      WLN("*****************************");
+    }
+    modi=(int)args[1];
+    morelines=(int)args[2];
+    return;
+  }
+  W("(bad autoload, using default)\n");
+}
+
+/*----------------------------------------------------------------------
+ * creation, updating and initialization stuff
+ */
+
+void update_tool(mixed *args, object obj)
+{
+  SECURE1();
+  if(!(obj&&args))
+    return;
+  Destruct(PREV);
+  _set_autoloadobj(args);
+  move(obj);
+}
+
+void create()
+{
+  object obj;
+  
+  if(member(object_name(),'#')<0)
+    return;
+  if(!cloner&&!((cloner=TP)||(cloner=ENV(ME)))&&!interactive(cloner))
+    destruct(ME);
+  if(!IS_LEARNER(cloner))
+    destruct(ME);
+  SetProp(P_NODROP,"Das waere zu gefaehrlich.\n");
+  SetProp(P_NEVERDROP,1);
+  SetProp(P_NOBUY,1);
+  if(file_size(SAVE_FILE+".o")>0)
+  {
+    WDLN("Loading "+TOOL_TITLE+" settings");
+    restore_object(SAVE_FILE);
+  }
+  if(MODE(MODE_FIRST))
+    call_out("move",0,cloner);
+  call_out("add_insert_hook",1,ME);
+}
+
+void TK(string str)
+{
+  if (!xtk)
+    return;
+  tell_object(cloner,"XTOOL: "+str+"\n");
+}
+
+int Xtk(string str)
+{
+  xtk=!xtk;
+  WDLN("Xtool internal tracing "+(xtk?"enabled":"disabled"));
+  return TRUE;
+}
+
+void add_insert_hook(object obj)
+{
+  if(objectp(obj))
+    cloner->AddInsertHook(obj);
+}
+
+void init()
+{
+  object first, prev;
+  
+  if(member(object_name(),'#')<0) return;
+  first=first_inventory(ENV(ME));
+  if(MODE(MODE_PROTECT)&&is_player(first)&&!IS_ARCH(first))
+  {
+    WDLN("WARNING: "+crname(first)+" tried to move into your inventory");
+    tell_object(first, "You cannot move yourself into "+
+		crname(cloner)+"'s inventory.\n");
+    call_out("DropObj",0,first);
+    return;
+  }
+  else if(MODE(MODE_FIRST)&&first!=ME)
+    move(cloner);
+  else actions();
+}
+
+void DropObj(object obj)
+{
+  if(!obj||!objectp(obj))
+    return;
+  obj->move(ENV(cloner),M_NOCHECK|M_NO_SHOW);
+}
+
+#define ACTIONS\
+([\
+  "xcallouts" : "Xcallouts";0;1,\
+  "xcall"     : "Xcall";0;1,\
+  "xcat"      : "Xcat";1;1,\
+  "xcd"       : "Xcd";0;0,\
+  "xclean"    : "Xclean";0;0,\
+  "xclone"    : "Xclone";0;0,\
+  "xuclone"   : "Xuclone";0;0,\
+  "xcmds"     : "Xcmds";0;1,\
+  "xdbg"      : "Xdbg";0;0,\
+  "xdclean"   : "Xdclean";0;0,\
+  "xddes"     : "Xddes";0;0,\
+  "xdes"      : "Xdes";0;0,\
+  "xdest"     : "Xdes";0;0,\
+  "xdlook"    : "Xdlook";0;1,\
+  "xdo"       : "Xdo";0;0,\
+  "xdupdate"  : "Xdupdate";0;0,\
+  "xecho"     : "Xecho";0;0,\
+  "xeval"     : "Xeval";0;1,\
+  "xforall"   : "Xforall";0;0,\
+  "xgoto"     : "Xgoto";0;0,\
+  "xhbeats"   : "Xhbeats";0;1,\
+  "xgrep"     : "Xgrep";1;1,\
+  "xhead"     : "Xhead";1;1,\
+  "xhelp"     : "Xhelp";0;0,\
+  "xinventory": "Xinventory";0;1,\
+  "xids"      : "Xids";0;0,\
+  "xinfo"     : "Xinfo";0;0,\
+  "xinherit"  : "Xinherit";0;1,\
+  "xlag"      : "Xlag";0;0,\
+  "xlight"    : "Xlight";0;0,\
+  "xload"     : "Xload";0;0,\
+  "xlook"     : "Xlook";0;1,\
+  "xlpc"      : "Xlpc";0;0,\
+  "xman"      : "Xman";0;0,\
+  "xmore"     : "Xmore";1;0,\
+  "xmove"     : "Xmove";0;0,\
+  "xmsg"      : "Xmsg";1;0,\
+  "xmtp"      : "Xmtp";0;0,\
+  "xproc"     : "Xproc";0;1,\
+  "xprof"     : "Xprof";0;0,\
+  "xprops"    : "Xprops";0;1,\
+  "xquit"     : "Xquit";0;0,\
+  "xscan"     : "Xscan";0;0,\
+  "xset"      : "Xset";0;0,\
+  "xsh"       : "Xsh";0;0,\
+  "xsort"     : "Xsort";1;1,\
+  "xtail"     : "Xtail";1;1,\
+  "xtk"       : "Xtk";0;0,\
+  "xtool"     : "Xtool";0;0,\
+  "xtrace"    : "Xtrace";0;0,\
+  "xtrans"    : "Xtrans";0;0,\
+  "xupdate"   : "Xupdate";0;0,\
+  "xwc"       : "Xwc";1;0,\
+  "xwho"      : "Xwho";0;1,\
+  ])
+
+static string PrepareLine(string str)
+{
+  return str;
+}
+
+static string QuoteLine(string str)
+{
+  string *tmp,*tmp2;
+  int i, i2, len, len2, qd, qs;
+
+  str=string_replace(str,"\\\\","°BSHL");
+  str=string_replace(str,"\\\"","°DBLQ");
+  str=string_replace(str,"\\\'","°SGLQ");
+  str=string_replace(str,"\\|","°PIPE");
+  str=string_replace(str,"||","°OROR");
+  str=string_replace(str,"->","°LARR");
+  str=string_replace(str,"\\$","°DOLR");
+  tmp=regexplode(str,"(\"|')");
+  len=sizeof(tmp);
+  qd=qs=0;
+  for(i=0;i<len;i++)
+  {
+    if(i%2)
+    {
+      if(tmp[i]=="'")
+	qd=(!qs?!qd:qd);
+      else
+	qs=(!qd?!qs:qs);
+      if((tmp[i]=="\""&&!qd)||(tmp[i]=="'"&&!qs))
+	tmp[i]="";
+    }
+    else
+    {
+      if(!qd)
+      {
+	len2=sizeof(tmp2=regexplode(tmp[i],"\\$[^ ][^ ]*"));
+	for(i2=0;i2<len2;i2++)
+	  if(i2%2)
+	  {
+	    TK("QuoteLine: "+tmp2[i2][1..]);
+	    tmp2[i2]=(string)XFindObj((tmp2[i2])[1..]);
+	  }
+	tmp[i]=implode(tmp2,"");
+      }
+    }
+  }
+  if(qd||qs)
+    return NULL;
+  str=implode(tmp,"");
+  TK("QuoteLine: str: "+str);
+  return str;
+}
+
+static string UnquoteLine(string str)
+{
+  str=string_replace(str,"°BSHL","\\");
+  str=string_replace(str,"°DBLQ","\"");
+  str=string_replace(str,"°SGLQ","\'");
+  str=string_replace(str,"°DQUO","");
+  str=string_replace(str,"°SQUO","");
+  str=string_replace(str,"°PIPE","|");
+  str=string_replace(str,"°OROR","||");
+  str=string_replace(str,"°LARR","->");
+  str=string_replace(str,"°DOLR","$");
+  TK("UnquoteLine: str: "+str);
+  return str;
+}
+
+static string *ExplodeCmds(string str)
+{
+  string *tmp;
+  
+  tmp=regexplode(str,"\\||>|>>");
+  while(tmp[<1]=="")
+    tmp=tmp[0..<2];
+  return tmp;
+}
+
+varargs int ParseLine(string str)
+{
+  string verb, arg;
+  int ret;
+  
+  TK("ParseLine: str: "+(str?str:""));
+  if(!sizeof(cmds))
+  {
+    // this is a single command or beginning of a command pipe
+    verb=query_verb();
+
+    // return on unknown commands
+    if(!verb||!sizeof(verb)||!GetFunc(verb,TRUE))
+      return FALSE;
+
+    str=(string)this_player()->_unparsed_args();
+    pipe_in=FALSE;
+    pipe_of=NULL;
+    pipe_ovr=TRUE;
+    pipe_out=FALSE;
+    pipe_of=NULL;
+    // pass arguments to some special functions unparsed
+    if(member(({"xlpc","xcall","xeval"}),verb)>=0)
+    {
+#ifdef XDBG
+      TK("ParseLine: special func: "+verb);
+#endif
+      ret=CallFunc(verb,str);
+      SafeReturn(ret);
+    }
+    // ok, here we go
+    pipe_in=pipe_out=FALSE;
+    pipe_if=pipe_of=NULL;
+    pipe_ovr=TRUE;
+    if(file_size(PIPE_FILE)>=0)
+      rm(PIPE_FILE);
+    if (str&&str!="")	  
+    {
+      if(!(str=QuoteLine(str)))
+      {
+	WDLN("Unterminated quotation");
+	SafeReturn(TRUE);
+      }
+      cmds=ExplodeCmds(str);
+    }
+    else
+      cmds=({""});
+    arg=strip_string(cmds[0]);
+  }
+  else
+  {
+    cmds[0]=strip_string(cmds[0]);
+    TK("ParseLine: cmds[0]: "+cmds[0]);
+    if(sscanf(cmds[0],"%s %s",verb,arg)!=2)
+    {
+      verb=cmds[0];
+      arg="";
+    }
+  }
+  cmds=cmds[1..];
+  TK("ParseLine: verb: "+verb);
+  if (!verb||!sizeof(verb)||!GetFunc(verb,TRUE))
+    SafeReturn(FALSE);
+  TK("ParseLine(1): arg: "+arg+" cmds: "+sprintf("%O",cmds));
+  switch(sizeof(cmds))
+  {
+    case 0:
+    ret=CallFunc(verb,strip_string(UnquoteLine(arg)));
+    SafeReturn(ret);
+    break;
+    
+    case 1:
+    WDLN("Missing rhs of command pipe");
+    SafeReturn(TRUE);
+    break;
+    
+    default:
+    pipe_out=TRUE;
+    switch(cmds[0])
+    {
+      case "|":
+      pipe_of=PIPE_FILE;
+      pipe_ovr=TRUE;
+      cmds=cmds[1..];
+      break;
+      
+      case ">":
+      pipe_ovr=TRUE;
+      if(sizeof(cmds)!=2)
+      {
+	WDLN("Illegal IO redirection");
+	SafeReturn(TRUE);
+      }
+      pipe_of=cmds[1];
+      cmds=({});
+      break;
+      
+      case ">>":
+      pipe_ovr=FALSE;
+      if(sizeof(cmds)!=2)
+      {
+	WDLN("Illegal IO redirection");
+	SafeReturn(TRUE);
+      }
+      pipe_of=cmds[1];
+      cmds=({});
+      break;
+    }
+  }
+  TK("ParseLine(2): arg: "+arg+" cmds: "+sprintf("%O",cmds));
+  if(!CallFunc(verb,strip_string(arg)))
+    SafeReturn(FALSE);
+  pipe_in=pipe_out;
+  pipe_if=pipe_of;
+  pipe_ovr=FALSE;
+  pipe_out=FALSE;
+  pipe_of=NULL;
+  if(sizeof(cmds))
+    call_out("ParseLine",0);
+  else
+    SafeReturn(TRUE);
+  return TRUE;
+}
+
+static int CallFunc(string verb, string str)
+{
+  string fun;
+
+  fun=GetFunc(verb,FALSE);
+#ifdef XDBG
+  TK("CallFunc: verb: "+verb+" str: "+str);
+  TK("CallFunc: resolved function: "+(fun?fun:"(unresolved)"));
+#endif
+  if(str=="")
+    str=NULL;
+  return fun?(int)call_other(ME,fun,str):FALSE;
+}
+
+static string GetFunc(string verb, int test)
+{
+  string fun,*keys,key;
+  int i,len;
+
+  TK("GetFunc: verb: "+verb);
+
+  if(verb[0..0]!="x") // Assume all commands start with "x"
+    return 0;
+
+  if (!(fun=(string)ACTIONS[verb,0])) { // Try exact hit first
+    key="";
+    len=sizeof(verb);
+    for (i=sizeof(keys=m_indices(ACTIONS))-1;i>=0;i--) {
+      TK("  trying: "+keys[i]);
+      if(sizeof(keys[i])>=len&&keys[i][0..len-1]==verb) {
+	if(sizeof(key)) {
+	  WLN("Das ist nicht eindeutig ...");
+	  return 0;
+	}
+	fun=ACTIONS[keys[i],0];
+	key=keys[i];
+	//break;
+      }
+    }
+  } else
+    key=verb;
+
+  if(test)
+    return fun;
+  
+  if (key) {
+#ifdef XDBG
+    TK("GetFunc: fun: "+fun+" (key: "+key+")\n"+
+       "pipe_in: "+(pipe_in?"TRUE  ":"FALSE ")+(pipe_if?pipe_if:"(NULL)")+"\n"+
+       "pipe_out: "+(pipe_out?"TRUE  ":"FALSE ")+(pipe_of?pipe_of:"(NULL)")+"\n"+
+       "pipe_ovr: "+(pipe_ovr?"TRUE":"FALSE"));
+#endif
+    if (pipe_in&&!ACTIONS[key,PIPE_IN])
+    {
+      // this command does not read pipes
+#ifdef XDBG
+      TK("Illegal rhs of command pipe \""+fun+"\"\n");
+#endif
+      notify_fail("Illegal rhs of command pipe \""+fun+"\"\n");
+      return 0;
+    }
+    else if (pipe_out&&!ACTIONS[key,PIPE_OUT])
+    {
+      // this command does not feed pipes
+#ifdef XDBG
+      TK("Illegal lhs of command pipe \""+fun+"\"\n");
+#endif
+      notify_fail("Illegal lhs of command pipe \""+fun+"\"\n");
+      return 0;
+    }
+  }
+  return fun;
+}
+
+void actions()
+{
+  if (!cloner||!RTP||cloner==RTP||query_wiz_level(cloner)<=query_wiz_level(RTP))
+    add_action("ParseLine","",1);
+  add_action("CommandScan", "", 1);
+}
+
+/*----------------------------------------------------------------------
+ *  the checking stuff
+ */
+
+void InsertNotify(object obj)
+{
+  if(!cloner)
+    return;
+  if(MODE(MODE_FIRST) && find_call_out("move")==-1)
+    call_out("move",0,cloner);
+  if(MODE(MODE_INVCHECK))
+    write_newinvobj(obj);
+}
+
+static void VarCheck(int show)
+{
+  int i, s;
+  foreach(string k, mixed v : variable)
+  {
+    if (v) continue;
+    if(show) WDLN("*** Variable $"+k+" has been destructed");
+    m_delete(variable, k);
+  }
+}
+
+
+int write_newinvobj(object obj)
+{
+  if(obj) WDLN("*** New object in inventory "+ObjFile(obj));
+  return(1);
+}
+
+/*----------------------------------------------------------------------
+ *  catch all commands, absorb forces and check history
+ */
+
+int CommandScan(string arg)
+{
+  string verb, cmd;
+  object rtp;
+  rtp=RTP;
+
+  if(!cloner&&!(cloner=rtp)) destruct(ME);
+
+  if((!MODE(MODE_PROTECT))||security()||
+     query_wiz_level(cloner)<query_wiz_level(rtp))
+  {
+    verb=query_verb();
+    if(verb&&DoHistory(verb+(arg ? " "+arg : "")))
+      return TRUE;
+    nostore=FALSE;
+    return FALSE;
+  }
+  else
+  {
+    if(rtp)
+    {
+      WDLN("Your "+TOOL_TITLE+" protects you from a force by "+crname(rtp)+
+	   " ["+query_verb()+(arg ? " "+arg+"]" : "]"));
+      tell_object(rtp, crname(cloner)+"'s "+TOOL_TITLE+
+		  " absorbes your force.\n");
+    }
+    else
+    {
+      WDLN("Your "+TOOL_TITLE+" protects you from a force ["+
+	   query_verb()+(arg ? " "+arg+"]" : "]"));
+    }
+    return TRUE;
+  }
+}
+
+int DoHistory(string line)
+{
+  int i;
+  string cmd, *strs;
+  
+  SECURE2(FALSE);
+  if(!stringp(line) || !sizeof(line))
+    return TRUE;
+  else if(line=="%!")
+  {
+    WLN("Current command history:");
+    for(i=MAX_HISTORY; i; --i)
+      if(history[i-1])
+      {
+	W(" "+ARIGHT(""+i, 2, " ")+": ");
+	if(sizeof(history[i-1])>70)
+	  WLN(ALEFT(history[i-1], 70, " "));
+	else
+	  WLN(history[i-1]);
+      }
+    return TRUE;
+  }
+  else if(line[0..1]=="%%" && (cmd=history[0]+line[2..<1]))
+  {
+    Command(cmd);
+    return TRUE;
+  }
+  else if(line[0]=='^'&&(strs=strip_explode(line, "^")))
+  {
+    if(sizeof(strs)&&strs[0]&&(cmd=history[0]))
+    {
+      if(sizeof(strs)==2)
+	cmd=string_replace(cmd, strs[0], strs[1]);
+      else
+	cmd=string_replace(cmd, strs[0], "");
+      nostore--;
+      Command(cmd);
+      nostore++;
+      return TRUE;
+    }
+  }
+  else if(line[0]=='%' && (sscanf(line[1..<1], "%d", i)))
+  {
+    i= i>0 ? i : 1;
+    i= i<=MAX_HISTORY ? i : MAX_HISTORY;
+    if(cmd=history[i-1])
+      Command(cmd);
+    return TRUE;
+  }
+  else if(line[0]=='%')
+  {
+    for(i=0; i<MAX_HISTORY; i++)
+    {
+      if(history[i]&&
+	 history[i][0..sizeof(line)-2]==line[1.. <1])
+      {
+	Command(history[i]);
+	return TRUE;
+      }
+    }
+  }
+  else if(nostore<1)
+    history=({line})+history[0..MAX_HISTORY-2];
+  return FALSE;
+}
+
diff --git a/obj/tools/MGtool/man.d/xcall b/obj/tools/MGtool/man.d/xcall
new file mode 100644
index 0000000..43e21ea
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xcall
@@ -0,0 +1,21 @@
+SYNOPSIS
+      xcall <object>-><function>(<expression>)
+
+DESCRIPTION
+      Calls the lfun <function> of the <object> with <expression> as
+      argument. The <properties.h> are included. Three objects are allready
+      defined on invokation. "me" represents the onwer of the Xtool. "here"
+      is the current room and "obj" is the object which lfun is called.
+      To use the object access syntax described above, a function called
+      "get(string)" can be used. If the call returned an object, the variable
+      "$result" will be set to this object.
+      xcall includes a private ~/.xtool.h, if it exists, to support user
+      defined macros.
+
+EXAMPLES
+      xcall $me->QueryProp(P_LONG)
+      xcall /std/thing#145->SetProp(P_SHORT,"a small thing")
+      xcall /secure/master->add_domain_wiz("hell", "hyp")
+      xcall $me->move(get("^deepthought"),"go")
+      (the last one uses the function get() to find the room where our god is)
+
diff --git a/obj/tools/MGtool/man.d/xcallouts b/obj/tools/MGtool/man.d/xcallouts
new file mode 100644
index 0000000..79adfa6
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xcallouts
@@ -0,0 +1,16 @@
+SYNOPSIS
+      xcallouts [search pattern]
+	  or
+	  xcallouts    (when writing to a pipe)
+
+DESCRIPTION
+      With no argument it shows all your callouts. With a regexp as argument,
+      all matching callouts will be printed. When writing to a pipe, all
+	  call_outs will be printed and the search pattern is not allowed.
+
+EXAMPLES
+      xcallouts obj   Show all callouts with the string "obj" in it.
+      xcallouts .     Show all callouts. Please never do this if it isn't
+                      really neccessary. It just costs time - and what do you
+                      want to do with this mass of information?
+
diff --git a/obj/tools/MGtool/man.d/xclean b/obj/tools/MGtool/man.d/xclean
new file mode 100644
index 0000000..dcc488e
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xclean
@@ -0,0 +1,12 @@
+SYNOPSIS
+      xclean [object]
+
+DESCRIPTION
+      Clean the inventory of the object, but don't destruct any living.
+      With no argument the current room will be cleaned. The objects
+      are removed with the lfun remove().
+
+EXAMPLE
+      xclean $me.bag
+      Clean my bag.
+
diff --git a/obj/tools/MGtool/man.d/xclone b/obj/tools/MGtool/man.d/xclone
new file mode 100644
index 0000000..00c1b7a
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xclone
@@ -0,0 +1,13 @@
+SYNOPSIS
+      xclone <filename>
+
+DESCRIPTION
+      Clones an object. On error, the error log file will be printed.
+      Moves the object (if possible) into your inventory. If the object
+      has been cloned successfully, the variable "$clone" will be set,
+      to make sure we don't lose the object out of sight :)
+
+EXAMPLE
+      xclone /std/thing
+      xcall $clone->SetShort("a BIG thing!");
+
diff --git a/obj/tools/MGtool/man.d/xdbg b/obj/tools/MGtool/man.d/xdbg
new file mode 100644
index 0000000..5609f2f
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xdbg
@@ -0,0 +1,14 @@
+SYNOPSIS
+      xdbg <object>
+
+DESCRIPTION
+      Prints out debug information buy using the efun debug_info().
+      Not all of these information is useful but it gives you information
+      of how many variables, functions, inherited objetcs and so on a object
+      has. It also shows if an object is living, if its heart beat is turned
+      on, when the next call of reset() will be (but don't take this value
+      not too seriously) etc.
+
+EXAMPLE
+      xdbg $me
+
diff --git a/obj/tools/MGtool/man.d/xdclean b/obj/tools/MGtool/man.d/xdclean
new file mode 100644
index 0000000..79f9093
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xdclean
@@ -0,0 +1,6 @@
+SYNOPSIS
+      xdclean <object>
+
+DESCRIPTION
+      Same as xclean, but makes a deep clean.
+
diff --git a/obj/tools/MGtool/man.d/xddes b/obj/tools/MGtool/man.d/xddes
new file mode 100644
index 0000000..a235645
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xddes
@@ -0,0 +1,6 @@
+SYNOPSIS
+      xddes <object>
+
+DESCRIPTION
+      Same as xdes, but makes a deep destruct.
+
diff --git a/obj/tools/MGtool/man.d/xdes b/obj/tools/MGtool/man.d/xdes
new file mode 100644
index 0000000..17bf984
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xdes
@@ -0,0 +1,8 @@
+SYNOPSIS
+      xdes <object>
+
+DESCRIPTION
+      Destruct an object using by first calling the lfun remove() of the
+      object and if its still "alive" then destruct it with the efun
+      destruct().
+
diff --git a/obj/tools/MGtool/man.d/xdlook b/obj/tools/MGtool/man.d/xdlook
new file mode 100644
index 0000000..6f7772f
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xdlook
@@ -0,0 +1,6 @@
+SYNOPSIS
+      xdlook <object>
+
+DESCRIPTION
+      Same as look, but takes a deep look at the object.
+
diff --git a/obj/tools/MGtool/man.d/xdo b/obj/tools/MGtool/man.d/xdo
new file mode 100644
index 0000000..cefa5f1
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xdo
@@ -0,0 +1,14 @@
+SYNOPSIS
+      xdo [<number1>#]<command1>[;[<number2>#]<command2>] ...
+
+DESCRIPTION
+      Perform multiple command execution.
+
+      Examples:
+      xdo 5#smile   (smile 5 times)
+
+      xdo l;i       (look around in the room and then in your inventory)
+
+      xdo s;9#w;3#n;kill shaman;get sword from corpse;3#s;9#e;n;...
+                    (you know this one :)
+
diff --git a/obj/tools/MGtool/man.d/xdupdate b/obj/tools/MGtool/man.d/xdupdate
new file mode 100644
index 0000000..b3752b9
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xdupdate
@@ -0,0 +1,8 @@
+SYNOPSIS
+      xdupdate <filename>
+
+DESCRIPTION
+     Update an object including all its inherited objects.
+
+EXAMPLE
+     xdupdate /std/player.c
diff --git a/obj/tools/MGtool/man.d/xeval b/obj/tools/MGtool/man.d/xeval
new file mode 100644
index 0000000..f532bdf
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xeval
@@ -0,0 +1,18 @@
+SYNOPSIS
+      xeval <expression>
+
+DESCRIPTION
+      Execute an LPC expression. Inside the <expression>, "me" and "here" are
+      predefined objects (guess for what :). Here we can also use the function
+      "get()" to find an object using the object access syntax. If the
+      Expression returns an object the variable "$result" will be set to this.
+      xeval includes a private ~/.xtool.h, if it exists, to support user        
+      defined macros.
+
+
+EXAMPLES
+      Just do it!
+      xeval users()
+      xeval getuid(me)
+      xeval get("$me.xtool")
+
diff --git a/obj/tools/MGtool/man.d/xforall b/obj/tools/MGtool/man.d/xforall
new file mode 100644
index 0000000..17558f4
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xforall
@@ -0,0 +1,29 @@
+SYNOPSIS
+      xforall <filepattern> <command>
+
+DESCRIPTION
+      Execute <command> for all matching files. The <command> string can
+      contain replacment marks. Currently there are five of these marks:
+      - "!!" this mark stands for the full file name.
+      - "!e" stands for the extension of the file name.
+      - "!f" stands for the file name without extension and directory name.
+      - "!h" stands for the full directory name of the files.
+      - "!r" stands for the full file name without file extension.
+      - "!t" stands for the file name without the directory name.
+
+      If the full file name would be "/directory/file.ext" then
+      "!!" equals "/directory/file.ext"
+      "!e" equals "ext"
+      "!f" equals "file"
+      "!h" equals "/directory"
+      "!r" equals "/directory/file"
+      "!t" equals "file.ext"
+
+EXAMPLES
+      xforall ~/room/*.c xcall !r->reset();
+      Call the reset() function in all your rooms.
+
+      xforall ~/obj/* mv !! !h/oldobj/!f.!e.old
+      Move all files from your "obj" dir into the "obj/oldobj" dir and change
+      the extensions.
+
diff --git a/obj/tools/MGtool/man.d/xgoto b/obj/tools/MGtool/man.d/xgoto
new file mode 100644
index 0000000..df4ccba
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xgoto
@@ -0,0 +1,16 @@
+SYNOPSIS
+      xgoto [object]
+
+DESCRIPTION
+      Go inside <object>. Without an argument you will leave into your
+      workroom. If <object> is a living thing, goto it's environment
+      (never tested until now, when going to a living thing, inside
+      a living thing :)
+
+EXAMPLES
+      xgoto /room/shop
+      xgoto shop	 (both are the same, because if there is no shop in
+			  the current directory, a "/room/" will be appended
+			  to the argument)
+      xgoto public enemy (do this only when you are really bored)
+
diff --git a/obj/tools/MGtool/man.d/xgrep b/obj/tools/MGtool/man.d/xgrep
new file mode 100644
index 0000000..8914fcb
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xgrep
@@ -0,0 +1,25 @@
+SYNOPSIS
+      xgrep [-iv] <regexp> <filepattern>
+        or
+      xgrep [-iv] <regexp>    (when reading from a pipe)
+
+DESCRIPTION
+      Search for the regular expression <regexp> in all files matching the
+      <filepattern>.
+      Works simular to the Unix command "grep". The reslut will be written
+      to <STDOUT> and may be piped into another command.
+
+OPTIONS
+      -v  reverts the sense of <regexp>.
+
+      -i  matches case-insensitive
+
+EXAMPLES
+      xgrep Deepthought /log/SHOUTS
+      This will search throught the shout log file for any line which has
+      the word "Deepthought" in it.
+
+      xgrep create\(\) *.c
+      This will search for all lines of all lpc code files in the current dir
+      which have the function "create()" defined.
+
diff --git a/obj/tools/MGtool/man.d/xhbeats b/obj/tools/MGtool/man.d/xhbeats
new file mode 100644
index 0000000..3a897f3
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xhbeats
@@ -0,0 +1,18 @@
+SYNOPSIS
+      xhbeats [search pattern]
+      or
+	  xhbeats     (when writing to a pipe)
+
+DESCRIPTION
+      With no argument it shows all your objects which have a running heart
+      beat. With a regexp as argument, all matching objects will be printed.
+	  When writing to a pipe, all hearbeats will be printed and a search
+	  pattern is not allowed.
+
+EXAMPLES
+      xhbeats obj   Show all objects which have a running heart beat at the
+                    object names with the string "obj" in it.
+      xhbeats .     Show all callouts. Please don't do this if it isn't
+                    really neccessary. It just costs time - and what do you
+                    want to do with this information?
+
diff --git a/obj/tools/MGtool/man.d/xhead b/obj/tools/MGtool/man.d/xhead
new file mode 100644
index 0000000..5801055
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xhead
@@ -0,0 +1,11 @@
+SYNOPSIS
+      xhead <-#> <file>
+	  or
+	  xhead <-#>    (when reading from a pipe)
+
+DESCRIPTION
+      xhead prints the first part of a given file; It reads from a pipe
+	  if no file is given.
+
+EXAMPLES
+      xhead -10 workroom.c prints the first 10 lines of the file workroom.c.
diff --git a/obj/tools/MGtool/man.d/xhelp b/obj/tools/MGtool/man.d/xhelp
new file mode 100644
index 0000000..b0aa7d4
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xhelp
@@ -0,0 +1,6 @@
+SYNOPSIS
+      xhelp
+
+DESCRIPTION
+      Show the main help file.
+
diff --git a/obj/tools/MGtool/man.d/xids b/obj/tools/MGtool/man.d/xids
new file mode 100644
index 0000000..67ef761
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xids
@@ -0,0 +1,6 @@
+SYNOPSIS
+      xids <object>
+
+DESCRIPTION
+      Show the user id and the effective user id of the <object>.
+
diff --git a/obj/tools/MGtool/man.d/xinherit b/obj/tools/MGtool/man.d/xinherit
new file mode 100644
index 0000000..ef02f3e
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xinherit
@@ -0,0 +1,16 @@
+SYNOPSIS
+      xinherit <object> [function]
+
+DESCRIPTION
+      Show the inheritance tree of an object and if optional specified
+      display all occurencies of the function.
+      The inheritance tree is perhaps not the one you expect to see. In
+      fact its the one the gamedriver really uses. That means if for
+      example two object inherit each other or in any other case of recursion
+      it will not be shown. Or if an object A inherits object B and C
+      and object B also inherits object C, the object C will only displayed
+      once (because the gamedriver also inherits object once).
+
+EXAMPLE
+      xinherit $me
+
diff --git a/obj/tools/MGtool/man.d/xlag b/obj/tools/MGtool/man.d/xlag
new file mode 100644
index 0000000..fd28114
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xlag
@@ -0,0 +1,16 @@
+SYNOPSIS
+      xlag
+
+DESCRIPTION
+      xlag displays the current system LAG by comparing the actual
+      processed heartbeats with the initial heartbeat rate (1/2s).
+      Values will we displayed for the last minute, the last 15 minutes
+      and the last hour.
+
+EXAMPLE
+      Letzte 60 min: ####### (7.2%)
+      Letzte 15 min: ######## (8.3%)
+      Letzte Minute: ######### (9.4%) 
+
+AVAILABILITY
+      xlag requires /players/kirk/obj/lag-o-daemon.c to be loaded.
diff --git a/obj/tools/MGtool/man.d/xload b/obj/tools/MGtool/man.d/xload
new file mode 100644
index 0000000..00db877
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xload
@@ -0,0 +1,15 @@
+SYNOPSIS
+      xload <filename>
+
+DESCRIPTION
+      Update and load the object specified by the filename. If you update
+      your current environment, all players will be moved silently into
+      "/std/void" and after successful updating and loading back into it.
+
+EXAMPLES
+      xload $here
+      Update and reload the current room (but without a trip into the void :).
+
+      xload obj/little_thing.c
+      Update and load it.
+
diff --git a/obj/tools/MGtool/man.d/xlook b/obj/tools/MGtool/man.d/xlook
new file mode 100644
index 0000000..259bc9e
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xlook
@@ -0,0 +1,7 @@
+SYNOPSIS
+      xlook [object]
+
+DESCRIPTION
+      Look at the object. If the argument is ommitted, look inside the
+      current room.
+
diff --git a/obj/tools/MGtool/man.d/xlpc b/obj/tools/MGtool/man.d/xlpc
new file mode 100644
index 0000000..8f471ed
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xlpc
@@ -0,0 +1,20 @@
+SYNOPSIS
+      xlpc <lpc code>
+
+DESCRIPTION
+      Execute pure LPC code. "me" and "here" are predefined objects. Also the
+      <properties.h> will be included. The function "get(string)" can be used
+      for accessing objects. If the execution returns an object the variable
+      "$result" will set to this object.
+
+EXAMPLES
+      xlpc int i;object *o;o=users();for(i=0;i<sizeof(o);i++)o[i]->
+           SetProp(P_FROG,1);
+      (Let all users be slimy frogs :)
+
+      xlpc int i;object *o;o=users();for(i=0;i<sizeof(o);i++)
+	   o[i]->SetProp(P_TITLE,get("$me")->QueryProp(P_TITLE));
+      (Give all users your prefered title. Take a look of the use of get().)
+
+      Both commands must be written in one line of course!
+
diff --git a/obj/tools/MGtool/man.d/xman b/obj/tools/MGtool/man.d/xman
new file mode 100644
index 0000000..9b56ad1
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xman
@@ -0,0 +1,17 @@
+SYNOPSIS
+      xman <topic>
+
+DESCRIPTION
+      Searches in some special directories under /doc for the given topic
+      or command. If there are two equal topics in different directories
+      the one found first will be printed.
+
+EXAMPLES
+      xman xman
+      xman xforall
+      xman efun/destruct
+      xman w/destruct
+      xman move
+      xman thing
+      xman domains
+
diff --git a/obj/tools/MGtool/man.d/xmore b/obj/tools/MGtool/man.d/xmore
new file mode 100644
index 0000000..bcd9250
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xmore
@@ -0,0 +1,12 @@
+SYNOPSIS
+      xmore <filename> [start]
+
+DESCRIPTION
+      Prints out a file and if a starting line is given, then the printing
+      will start there. On ansi or vt100 terminals the status line will be
+      overstroked to give a harmonic view of the file.
+
+EXAMPLES
+      xmore ~/workroom.c
+      xmore $here (this will show the source of the current room)
+
diff --git a/obj/tools/MGtool/man.d/xmove b/obj/tools/MGtool/man.d/xmove
new file mode 100644
index 0000000..b4c0aec
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xmove
@@ -0,0 +1,13 @@
+SYNOPSIS
+      xmove <object> into <object>
+
+DESCRIPTION
+      Moves one object into another or give the reason why the move failed.
+
+EXAMPLES
+      xmove $clone into $me
+      If you cloned an object with xclone but the object couldn't be moved
+      then this can be used to get the reason for it.
+
+      xmove hyp into ^wurzel  (this is a silent form of going to other players)
+
diff --git a/obj/tools/MGtool/man.d/xmsg b/obj/tools/MGtool/man.d/xmsg
new file mode 100644
index 0000000..175b780
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xmsg
@@ -0,0 +1,11 @@
+SYNOPSIS
+      xmsg [to <object>|all]
+
+DESCRIPTION
+      Send a message. If no arguments are given the message will be send into
+      the room. If second argument is "to" then send the message to the
+      specified <object>. If second argument is "all" then send the message to
+      all players in the mud.
+      This command can be used to send text via cut&paste to someone without
+      putting a "say", "tell" or "shout" infront of each line.
+
diff --git a/obj/tools/MGtool/man.d/xmtp b/obj/tools/MGtool/man.d/xmtp
new file mode 100644
index 0000000..9d74a9c
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xmtp
@@ -0,0 +1,24 @@
+SYNOPSIS
+      xmtp [options] <directory> <file>
+
+DESCRIPTION
+      Create a script file for transfering complete directories with mtp.
+      If your mtp needs extra arguments (except of the -r option which
+      stands for reading files) for example the -g option then you can add
+      then to the command.
+      This command works recursively and can cause a to deep recursion or
+      a too long evaluation error when trying to make a script for huge
+      directories like "/players" for example.
+      If the <file> allready exists it will not be removed instead the script
+      will simply added to the end of the file.
+
+EXAMPLE
+      xmtp -gtubmud /basic ~/basic.csh
+      Create a script file "basic.csh" to transfer the whole content of the
+      "/basic" directory.
+
+      On your host you then just create a directory (normally with the same
+      name give to the xmtp command), change into the directory and read
+      the script file with "mtp -r basic.csh". Then just execute the script
+      with "csh" or "sh".
+
diff --git a/obj/tools/MGtool/man.d/xproc b/obj/tools/MGtool/man.d/xproc
new file mode 100644
index 0000000..16d0aa1
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xproc
@@ -0,0 +1,13 @@
+SYNOPSIS
+      xproc [-c] [-l] [-m] [-u] [-v]
+
+DESCRIPTION
+      xproc prints information from the /proc filesystem to the screen.
+      The format is totally unformatted! (I can't help it for now...) 
+      Without any option, it display the machines load averages.
+
+      -c: cpu information
+      -l: load averages
+      -m: memory usage
+      -u: machine's uptime
+      -v: kernel version
diff --git a/obj/tools/MGtool/man.d/xprops b/obj/tools/MGtool/man.d/xprops
new file mode 100644
index 0000000..f8f0dfe
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xprops
@@ -0,0 +1,15 @@
+SYNOPSIS
+      xprops [-f|-m] <object>
+
+DESCRIPTION
+      Prints out all properties of <object> which are currently defined
+      in /sys/properties.h.
+
+OPTIONS
+      -f  List property flags verbose
+
+      -m  List property methods
+
+EXAMPLE
+      xprops $me
+
diff --git a/obj/tools/MGtool/man.d/xscan b/obj/tools/MGtool/man.d/xscan
new file mode 100644
index 0000000..7f7b450
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xscan
@@ -0,0 +1,7 @@
+SYNOPSIS
+      xscan
+
+DESCRIPTION
+      Scan current room for netdead players and check variables.
+      (There will follow more checking routines like snoop, invis etc. :)
+
diff --git a/obj/tools/MGtool/man.d/xset b/obj/tools/MGtool/man.d/xset
new file mode 100644
index 0000000..cb7c66d
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xset
@@ -0,0 +1,15 @@
+SYNOPSIS
+      xset [$<name>=<object>]
+
+DESCRIPTION
+      Without an argument all current variable settings will be shown and
+      destructed objects removed from the list of variables.
+      With argument the specified variable will be set.
+
+EXAMPLES
+      xset $deep=deepthought
+      xset $xtool=$me.xtool
+      xset $god=$deep
+      xset $last_result=$result
+      xset $3=^$1.3
+
diff --git a/obj/tools/MGtool/man.d/xsh b/obj/tools/MGtool/man.d/xsh
new file mode 100644
index 0000000..1377992
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xsh
@@ -0,0 +1,12 @@
+SYNOPSIS
+      xsh <filename>
+
+DESCRIPTION
+      Read in a file and execute each line of it as a command given from the
+      keyboard.
+
+EXAMPLE
+      xsh ~/setup.xsh
+      Execute the file "~/setup.xsh". This way you can set up your aliases or
+      other parameters for other tools if the setting gone lost.
+
diff --git a/obj/tools/MGtool/man.d/xsort b/obj/tools/MGtool/man.d/xsort
new file mode 100644
index 0000000..dfea2da
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xsort
@@ -0,0 +1,11 @@
+SYNOPSIS
+      xsort [-r] <file>
+	  or
+	  xsort [-r]    (when reading from a pipe)
+
+DESCRIPTION
+      xsort sort all lines of a given file in increasing/decreasing order.
+	  If no file is given it reads from a pipe.
+
+EXAMPLES
+      xsort -r workroom.c sorts all lines of workroom.c in reverse order.
diff --git a/obj/tools/MGtool/man.d/xtail b/obj/tools/MGtool/man.d/xtail
new file mode 100644
index 0000000..44c2300
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xtail
@@ -0,0 +1,12 @@
+SYNOPSIS
+      xtail <-|+#> <file>
+	  or
+	  xtail <-|+#>    (when reading from a pipe)
+
+DESCRIPTION
+      xtail prints the last part of a given file; It reads from a pipe
+	  if no file is given.
+
+EXAMPLES
+      xtail -10 workroom.c prints the last 10 lines of the file workroom.c.
+	  xtail +10 workroom.c prints all lines of workroom.c beginning at line 10.
diff --git a/obj/tools/MGtool/man.d/xtool b/obj/tools/MGtool/man.d/xtool
new file mode 100644
index 0000000..62d0614
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xtool
@@ -0,0 +1,45 @@
+SYNOPSIS
+      xtool [update|heart=<on|off>|first=<on|off>|protect=<on|off>|
+	     invcheck=<on|off>|envcheck=<on|off>|ndcheck=<on|off>|
+	     varcheck=<on|off>|short=<on|off>|echo=<on|off>|
+	     more=<amount>|kill|news|save|load|reset]
+
+DESCRIPTION
+      This command is used to set some tool-specific behaviours.
+      - "update" clones a new Xtool and destructs the old one. This should
+        only be done when working on the source of the tool and to get a fast
+        update. Settings and aliases will be reached through the new clone.
+      - "heart=<on|off>" turns the heart beat of the tool on or off.
+        The heart beat is for a higher security and to keep the other checks
+        running. If you just want to use the Xtool as a passive tool without
+        any checks then turn the heart beat off.
+      - "first=<on|off>" turns the automatic moving into the pole position
+        of your inventory on or off. Turning it off, avoids struggle with
+        other tools which behave same, but also reduces security. The history
+        mechanism will also capture the less commands.
+      - "protect=<on|off>" turns on/off the protection mode of the Xtool.
+        It will protect you from forces of other wizards and of player objects
+        which try to move into your inventory.
+      - "invcheck=<on|off>" turns the automatic check of your inventory on or
+        off. If turned on, all new objects in your inventory will be reported.
+      - "envcheck=<on|off>" like "invcheck" but checks your environment, the
+        content of the current room.
+      - "ndcheck=<on|off>" sets the netdeadchecking mode of the Xtool.
+      - "short=<on|off>" turns displaying of short descriptions on or off.
+        This is to avoid to get on other wizards nerves, when they have a tool
+        that notifies inventory checks (like this tool does :). But the main
+        reason is to cope with bad short descriptions of objects. For
+        example if you want to destruct it a bug in the short desc. would
+        make it nearly impossible to do so.
+      - "more=<amount>" sets the amount of lines that will be displayed at one
+        time with the xmore command. Will be removed soon because there is a
+        player property called P_PAGESIZE to take care of it.
+      - "echo=<on|off>" turns the echoing of commands executed via alias,
+        history or multiple command execution on or off.
+      - "kill" destructs the Xtool and removes it from the autoload list.
+      - "news" show the news and changes of the Xtool.
+      - "save" saves the settings of the Xtool including the aliases into a
+        file.
+      - "load" restores the saved settings from the save file.
+      - "reset" resets the Xtool.
+
diff --git a/obj/tools/MGtool/man.d/xtrace b/obj/tools/MGtool/man.d/xtrace
new file mode 100644
index 0000000..1c05e94
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xtrace
@@ -0,0 +1,14 @@
+SYNOPSIS
+      xtrace <object>
+
+DESCRIPTION
+      Trace all function calls with arguments and their return values of
+      the specified object. Without any argument turns the tracing off again.
+
+EXAMPLE
+      xtrace $me.buggything
+      Lets debug an buggy object.
+
+      xtrace
+      Turn debugging off.
+
diff --git a/obj/tools/MGtool/man.d/xtrans b/obj/tools/MGtool/man.d/xtrans
new file mode 100644
index 0000000..e356d0a
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xtrans
@@ -0,0 +1,6 @@
+SYNOPSIS
+      xtrans <player>
+
+DESCRIPTION
+      Teleport <player> to you.
+
diff --git a/obj/tools/MGtool/man.d/xuclone b/obj/tools/MGtool/man.d/xuclone
new file mode 100644
index 0000000..341c825
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xuclone
@@ -0,0 +1,13 @@
+SYNOPSIS
+      xuclone <filename>
+
+DESCRIPTION
+      Updates & Clones an object. On error, the error log file will be
+      printed. Moves the object (if possible) into your inventory. If the
+      object has been cloned successfully, the variable "$clone" will be set,
+      to make sure we don't lose the object out of sight :)
+
+EXAMPLE
+      xuclone /std/thing
+      xcall $clone->SetShort("a BIG thing!");
+
diff --git a/obj/tools/MGtool/man.d/xupdate b/obj/tools/MGtool/man.d/xupdate
new file mode 100644
index 0000000..cab6567
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xupdate
@@ -0,0 +1,10 @@
+SYNOPSIS
+      xupdate <filename>
+
+DESCRIPTION
+     Update an object by first calling the lfun "remove()" of it and when
+     it still exists destruct it.
+
+EXAMPLE
+     xupdate obj/blub.c
+
diff --git a/obj/tools/MGtool/man.d/xwc b/obj/tools/MGtool/man.d/xwc
new file mode 100644
index 0000000..aa13147
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xwc
@@ -0,0 +1,22 @@
+SYNOPSIS
+      xwc [-clw] <file>
+	  or
+	  xwc [-clw]    (when reading from a pipe)
+
+DESCRIPTION
+      xwc counts the number of bytes, whitespace-separated words, and new-
+      lines in a given file, or reading from a pipe. It prints one line
+      of counts, and if the file name was given as an argument, it prints
+      the filename following the counts. The counts are printed in the
+      order: lines, words, bytes.
+      By default, wc prints all three counts.
+
+OPTIONS:
+      -c print character count
+
+      -l print line count
+
+      -w print word count
+      
+EXAMPLES
+
diff --git a/obj/tools/MGtool/man.d/xwho b/obj/tools/MGtool/man.d/xwho
new file mode 100644
index 0000000..1e9fbd6
--- /dev/null
+++ b/obj/tools/MGtool/man.d/xwho
@@ -0,0 +1,17 @@
+SYNOPSIS
+      xwho [mail|ip|race|guild|domain|stats|cmdavg]
+
+DESCRIPTION
+      Without any argument this command behaves like the normal people
+      command. With the argument "mail" it will list all players with their
+      email address. With the argument "ip" it will list all players with
+      their host name and ip number. If the argument is "race" or "guild"
+      you will get a list of all players with these attributes. "domain"
+      will show the domain(s) of the current players. And with "stats" it
+      will print some stats information of all players.
+      The argument "cmdavg" list the value of _query_command_average()
+      for every user sorted in increasing order.
+
+EXAMPLES
+      xwho
+      xwho ip
diff --git a/obj/tools/MGtool/proc/cpuinfo b/obj/tools/MGtool/proc/cpuinfo
new file mode 120000
index 0000000..4ae7d7e
--- /dev/null
+++ b/obj/tools/MGtool/proc/cpuinfo
@@ -0,0 +1 @@
+/proc/cpuinfo
\ No newline at end of file
diff --git a/obj/tools/MGtool/proc/loadavg b/obj/tools/MGtool/proc/loadavg
new file mode 120000
index 0000000..e6983f4
--- /dev/null
+++ b/obj/tools/MGtool/proc/loadavg
@@ -0,0 +1 @@
+/proc/loadavg
\ No newline at end of file
diff --git a/obj/tools/MGtool/proc/meminfo b/obj/tools/MGtool/proc/meminfo
new file mode 120000
index 0000000..338b570
--- /dev/null
+++ b/obj/tools/MGtool/proc/meminfo
@@ -0,0 +1 @@
+/proc/meminfo
\ No newline at end of file
diff --git a/obj/tools/MGtool/proc/uptime b/obj/tools/MGtool/proc/uptime
new file mode 120000
index 0000000..2772a8f
--- /dev/null
+++ b/obj/tools/MGtool/proc/uptime
@@ -0,0 +1 @@
+/proc/uptime
\ No newline at end of file
diff --git a/obj/tools/MGtool/proc/version b/obj/tools/MGtool/proc/version
new file mode 120000
index 0000000..b17af1e
--- /dev/null
+++ b/obj/tools/MGtool/proc/version
@@ -0,0 +1 @@
+/proc/version
\ No newline at end of file
diff --git a/obj/tools/MGtool/prof.h b/obj/tools/MGtool/prof.h
new file mode 100644
index 0000000..05dcebf
--- /dev/null
+++ b/obj/tools/MGtool/prof.h
@@ -0,0 +1,42 @@
+private mixed __tm__=({0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0});
+private mixed __xtm__=({0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0});
+private mixed  __ec__=({0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0});
+private mixed  __xec__=({0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0});
+private int __level__;
+private mapping __data__ = ([]);
+__query_xprof_data__() { return __data__; }
+#define U2T(ru) (((ru)[0]+(ru)[1])*10)
+#define EC1 40
+#define EC2 22
+#define EC3 150
+#define TM1 0
+#define TM2 0
+#define TM3 0
+#define F(func,name,args) func args { \
+  mixed *__dt__, *__ru__, __re__; \
+  int __t__; \
+  if(!__data__) return :: func args; \
+  __ru__=rusage(); \
+  __tm__[__level__]=(__t__=U2T(__ru__))<0?0:__t__; \
+  __xtm__[__level__]=TM1; __tm__[__level__+1]=0; \
+  __ec__[__level__]=get_eval_cost(); \
+  __xec__[__level__]=EC1; __ec__[__level__+1]=0; \
+  __level__++; __re__ = :: func args; __level__--; \
+  if(!(__dt__=__data__[name])) { \
+    __dt__=__data__[name]=({0,0,0,0,0}); \
+    __xtm__[__level__]+=TM2; __xec__[__level__]+=EC2; \
+  } \
+  __ec__[__level__]-=get_eval_cost()+__xec__[__level__]; \
+  __ru__=rusage(); \
+  __t__=(__t__=U2T(__ru__))<0?0:__t__; \
+  __tm__[__level__]=__t__-__tm__[__level__]-__xtm__[__level__]; \
+  __dt__[0]++; \
+  __dt__[1]+=__ec__[__level__]-__ec__[__level__+1]; \
+  __dt__[2]+=__tm__[__level__]-__tm__[__level__+1]; \
+  __dt__[3]+=__ec__[__level__]; __dt__[4]+=__tm__[__level__]; \
+  if(__level__>0) { \
+    __xec__[__level__-1]+=__xec__[__level__]+EC3; \
+    __xtm__[__level__-1]+=__xtm__[__level__]+TM3; \
+  } \
+  return __re__; \
+}
diff --git a/obj/tools/MGtool/tool.doc b/obj/tools/MGtool/tool.doc
new file mode 100644
index 0000000..df477ce
--- /dev/null
+++ b/obj/tools/MGtool/tool.doc
@@ -0,0 +1,101 @@
+COMMANDS:
+    xcall      xddes      xforall    xids       xman       xscan      xtrace
+    xcallouts  xdes       xgoto      xinherit   xmore      xset       xtrans
+    xclean     xdlook     xgrep      xlag       xmove      xsh        xuclone
+    xclone     xdo        xhbeats    xload      xmsg       xsort      xupdate
+    xdbg       xdupdate   xhead      xlook      xmtp       xtail      xwc
+    xdclean    xeval      xhelp      xlpc       xprops     xtool      xwho
+
+
+Give a "?" as single argument to get the command usage or do "xman <command>"
+for more information about a command.
+
+QUICKTYPER:
+   The MGtool contains a full quicktyping tool. It provides the following
+   features:
+
+      - multiple command execution (see the xdo and xforall command)
+      - script files               (see the xsh command)
+      - command pipelining
+      - command history
+
+   The command history stores (nearly) each command you type in and lets you
+   re-execute it.
+
+      - %!        show the command history
+      - %%        re-execute the last command
+      - ^old^new  re-execute the last command but replace all occurencies of
+                  the "old" string of the command by "new".
+      - %num      re-execute the command at position num of the history
+      - %str      re-execute the last command beginning with the string "str"
+
+
+COMMAND ARGUMENTS:
+   Most commands need filenames or objects as argument. The way objects are
+   specified will be descriped in the following.
+
+      - an object can be accessed directly by its unique object name
+      - a player can be accessed by its player name
+      - a living can be accessed by its living name
+      - a object inside the current room by its id
+      - a object inside the current room by its position
+
+   If the object can not be accessed by the above mentioned ways you can also
+   use the following object modifiers:
+
+      ^obj         stands for the environment of an object
+      obj1.obj2    stands for an object obj2 inside another object obj1
+      
+  These modifiers can be concatenated together where the "^" has a higher
+  priority than the ".". This allows you to access an object from another know
+  object.
+
+  Now some examples which demonstrates how to use this accessing mechanism.
+  Therefore we use the command "xlook" which prints the short and long
+  description and also the content of an object:
+
+   1. xlook deepthought
+      Look at Deepthought.
+   2. xlook ^deepthought
+      Look at the room Deepthought is in.
+   3. xlook deepthought.bag
+      Look at the bag of Deepthought.
+   4. xlook deepthought.bag.bag
+      Look at the bag inside Deeps bag.
+   4. xlook deepthought.5
+      Look at the fifth object of Deeps inventory.
+   5. xlook /std/thing#123
+      Look at the object with the unique object name "/std/thing#123".
+   6. xlook 1
+      Look at the first object in the current room.
+   7. xlook 1.2.3
+      Look at the third of the second of the first object in the room.
+
+  If the name of an object contains an object modifier as a character then it
+  can be escaped by a "\". A "\" is escaped by itself. This is because file
+  names often contain the "." for file extensions. So if you want to access
+  directly an object with for example such a name "file.old.bulb" you have to
+  write "file\.old\.blub".
+    
+  If a command needs a file as argument you can use the filename of an object
+  for it by placing an "@" infront of the object. This way you can clone
+  an object by using another clone:
+
+     xclone @harry.bag.candles
+     Clone some candles of the kind Harry is carrying in his bag.
+
+
+ARGUMENT VARIABLES:
+   Whenever an object or a filename is needed, we can use variables.
+   A variable allways starts with a "$" followed by the name which
+   identificates it. Two variable names are reserved for special purpose.
+   The first, "$me" is always set to the one who owns the Xtool. The other,
+   "$here" represents the current room.
+
+   (Instead of "$me" and "$here" you can also use "$m" and "$h".)
+
+   If a command needs a file as argument and gets a variable it will be
+   automatically converted into the source file of the object.
+   (This is done by taking the unique object identifier and replacing the
+   "#..." by a ".c".)
+
diff --git a/obj/tools/MGtool/tool.h b/obj/tools/MGtool/tool.h
new file mode 100644
index 0000000..1eff23d
--- /dev/null
+++ b/obj/tools/MGtool/tool.h
@@ -0,0 +1,246 @@
+#ifndef __TOOL_H__
+#define __TOOL_H__
+
+/*
+ * MGtool-1.3
+ * File: tool.h
+ * Maintainer: Kirk@MorgenGrauen
+ */
+
+/*------------------------------------------*/
+/* the original Xtool is copyrighted by Hyp */
+/*------------------------------------------*/
+
+#ifndef MASTER
+#define MASTER __MASTER_OBJECT__
+#endif
+
+#define VOID "/room/void"
+
+#define NULL               (0)
+#define FALSE              (0)
+#define TRUE               (1)
+#define ERROR              (-1)
+#define PREV               previous_object()
+#define TP                 efun::this_player()
+#define RTP                efun::this_interactive()
+#define TOOL_NAME          "MGtool"
+#define TOOL_VERSION       "1.3.3"
+#define TOOL_TITLE         TOOL_NAME+" v"+TOOL_VERSION
+#define TOOL_INTERNAL      "1.3.3-10.07.97"
+
+#define TOOL_PATH          "/obj/tools/MGtool"
+
+#define TOOL_MANPAGE       TOOL_PATH+"/tool.doc"
+#define TOOL_NEWS          TOOL_PATH+"/tool.news"
+#define TOOL_LOG           "/log/xtool"
+#define XPROF_MACRO        TOOL_PATH+"/prof.h"
+#define LAG_O_DAEMON       "/p/daemon/lag-o-daemon"
+
+#define TOOL_LEVEL         (40)
+
+#define MORE_LINES         (20)
+#define MAX_HISTORY        (25)
+#define MAX_RECURSION      (6)
+#define TRACE_LEVEL        (1|2|4|8)
+#define AUTOLOAD_ARGS      ({TOOL_INTERNAL, modi, morelines})
+#define EXEC_LINES         (10)
+#define EXEC_TIME          (1)
+
+#define IA(x)              interactive(x)
+#define ENV(x)             environment(x)
+#define LOWER(x)           lower_case(x)
+#define NAME(x)            ((string)x->query_name())
+#define CNAME(x)           CAP((string)NAME(x))
+
+#define RNAME(x)           getuid(x)
+#define CRNAME(x)          CAP(RNAME(x))
+
+#define LEVEL(x)           query_wiz_level(x)
+
+#define FORALL(x, y)       for(x=first_inventory(y);x;x=next_inventory(x))
+#define DESTRUCT(x)        Destruct(x)
+#define ALEFT(x,y,z)       sprintf("%-*'"+z+"'s", y, (""+(x))[0..y-1])
+#define ARIGHT(x,y,z)      sprintf("%*'"+z+"'s" , y, (""+(x))[0..y-1])
+#define W(x)               Write(x)
+#define WLN(x)             W(x+"\n")
+#define WDLN(x)            W(x+".\n")
+
+#define MODE_ON(x)         (modi|=x)
+#define MODE_OFF(x)        (modi&=~x)
+#define MODE(x)            (modi&x)
+#define MODE_HEART         (1)
+#define MODE_FIRST         (2)
+#define MODE_PROTECT       (4)
+#define MODE_INVCHECK      (8)
+#define MODE_ENVCHECK      (16)
+#define MODE_NDCHECK       (32)
+#define MODE_VARCHECK      (64)
+#define MODE_ECHO          (128)
+#define MODE_SHORT         (256)
+#define MODE_SNOOPCHK      (512)
+#define MODE_INVISCHK      (1024)
+#define MODE_SCANCHK       (2048)
+
+#define ERR_FILE           "/players/"+RNAME(cloner)+"/.err"
+#define LPC_FILE           "/players/"+RNAME(cloner)+"/.tool.lpc"
+#define TMP_FILE           "/players/"+RNAME(cloner)+"/.tool.tmp"
+#define SAVE_FILE          "/players/"+RNAME(cloner)+"/.toolrc"
+#define XPROF_FILE         "/players/"+RNAME(cloner)+"/prof.c"
+#define PIPE_FILE          "/players/"+RNAME(cloner)+"/.tool.pipe"
+#define PRIVATE_HEADER     "/players/"+RNAME(cloner)+"/.xtool.h"
+#define PIPE_DELETE(x)     if(pipe_out&&pipe_ovr&&file_size(x)>=0) rm(x)
+
+#define PIPE_IN            1
+#define PIPE_OUT           2
+#define PIPE_MAX           10000
+
+#define XGREP_REVERT       1
+#define XGREP_ICASE        2
+
+#define SECURE1()    if(!security()) return;
+#define SECURE2(x)   if(!security()) return x;
+#define USAGE1(x,y)  notify_fail("Usage: "+(y)+"\n");\
+                     if((x)=="?") return FALSE;
+#define USAGE2(x,y)  notify_fail("Usage: "+(y)+"\n");\
+                     if((!(x))||((x)=="?")) return FALSE;
+#define USAGE3(x)    return WLN("Usage: "+(x))
+
+static int CatFile();
+static int Command(string str);
+int CommandScan(string arg);
+int DoAlias(string verb, string arg);
+int DoHistory(string line);
+static int MoveObj(object obj1, object obj2, int silent);
+static int XGrepFile(string pat, string file, int revert);
+int Xcall(string str);
+int Xcallouts(string str);
+int Xcat(string str);
+int Xcindent(string str);
+int Xclean(string str);
+int Xclone(string str);
+int Xuclone(string str);
+int Xcmds(string str);
+int Xdate(string str);
+int Xdbg(string str);
+int Xdclean(string str);
+int Xddes(string str);
+int Xdes(string str);
+int Xdlook(string str);
+int Xdo(string str);
+int Xdupdate(string str);
+int Xecho(string str);
+int Xeval(string str);
+int Xforall(string str);
+int Xgoto(string str);
+int Xgrep(string str);
+int Xhbeats(string str);
+int Xhead(string str);
+int Xhelp(string str);
+int Xids(string str);
+int Xinfo(string str);
+int Xinherit(string str);
+int Xinventory(string str);
+int Xlag(string str);
+int Xlight(string str);
+int Xload(string str);
+int Xlook(string str);
+int Xlpc(string str);
+int Xman(string str);
+int Xmore(string str);
+int Xmove(string str);
+int Xmsg(string str);
+int Xmtp(string str);
+int Xproc(string str);
+int Xprops(string str);
+int Xprof(string str);
+int Xquit(string str);
+int Xscan(string str);
+int Xset(string str);
+int Xsh(string str);
+int Xsort(string str);
+int Xstop(string str);
+int Xtool(string str);
+int Xtrace(string str);
+int Xtrans(string str);
+int Xupdate(string str);
+int Xwc(string str);
+int Xwho(string opt);
+static string crname(object who);
+int id(string str);
+int move(mixed dest);
+static int security();
+int write_newinvobj(object who);
+int write_newenvobj(object who);
+int write_netdead(object who);
+int write_alive(object who);
+int write_snoopee(object who);
+int write_nosnoop(object who);
+int write_invisobj(object who);
+int write_invislvg(object who);
+int write_invisply(object who);
+int write_visibobj(object who);
+int write_visiblvg(object who);
+int write_visibply(object who);
+static object *SubNodes(object obj);
+static varargs object FindObj(string str, object env, int silent);
+static object VarToObj(string str);
+static varargs object XFindObj(string str, int silent);
+static string ObjFile(object obj);
+static string PlayerAge(object obj);
+static string PlayerDomain(object obj, int flag);
+static string PlayerIP(object obj, int flag);
+static string PlayerIdle(object obj);
+static string PlayerMail(object obj, int flag);
+static string PlayerRace(object obj, int flag);
+static string PlayerSnoop(object obj, int flag);
+static string PlayerStats(object obj, int flag);
+static string PlayerWho(object obj);
+static string VarToFile(string str);
+static string VarToPureFile(string str);
+static string XFile(string file);
+static string XFindFile(string file);
+static varargs void DeepPrintShort(object env, int indent, string pre, string file);
+static void Destruct(object obj);
+static void DumpProperties(object obj, int flag);
+static void Inheritance(object obj, string func, string pre);
+void InvisCheck();
+static void MoreFile(string str);
+void NetDeadCheck(int show);
+static void PrintObj(object obj, string file);
+static varargs void PrintShort(string pre, object obj, string file);
+void SnoopCheck();
+static void VarCheck(int show);
+static int  Write(string str);
+static void XExecFile(int line);
+static void XMoreFile(string file, int flag);
+static void XMsgSay(string str);
+static void XMsgShout(string str);
+static void XMsgTell(string str);
+static void XmtpScript(string dir, string file, string opt);
+void actions();
+void init();
+void update_tool(mixed *args, object obj);
+string _query_long();
+string _query_short();
+
+static int CallFunc(string verb, string str);
+static string GetFunc(string verb, int test);
+
+static string PrepareLine(string str);
+static string QuoteLine(string str);
+static string UnquoteLine(string str);
+int QuoteOk(string str);
+static string *ExplodeCmds(string str);
+
+string read_buffer(string filename, int start, int number);
+string load_buffer(int line);
+int read_line(int offset);
+
+/*
+ * debug stuff
+ */
+void TK(string str);
+int Xtk(string str);
+
+#endif /* __TOOL_H__ */
diff --git a/obj/tools/MGtool/tool.news b/obj/tools/MGtool/tool.news
new file mode 100644
index 0000000..92adbd9
--- /dev/null
+++ b/obj/tools/MGtool/tool.news
@@ -0,0 +1,143 @@
+[10.07.97]
+
+- fixed minor bug in command lookup:
+  'xdosomethingorsomethingelse' was previously executed as 'xdo'
+  This is treated as an unknown command now. Anyway commands may be
+  abbreviated as long as they are not ambiguous.
+
+[12.03.97]
+
+- xlpc includes some more std-include-files and .xtool.h (if present)
+
+[04.03.97]
+
+- new command: xproc
+  display some information from the /proc filesystem in a very raw fashion.
+
+[08.05.96]
+
+- Xmore/Xtail: modified MoreFile/CatFile using new read_buffer() instead of
+  read_file() function which is VERY slow when giving offsets as argument.
+
+[13.02.96]
+
+- see below ...
+
+[03.01.96]
+
+- new command line parser under development (ALPHA!)
+
+[21.11.95]
+
+- MGtool will be destructed during creation, when !IS_LEARNER(<cloner>) is true
+
+- new option "-s" to xi[nventory] prints short inventory list (filenames only).
+  This is useful for alert object "... scanned your inventory" etc.
+  xi accepts <username> as optional argument to retrieve <username>'s 
+  inventory instead of the cloner's own inventory and may write into pipes
+  to create command chains like: xi -s kirk|xgrep MGtool
+
+- new command xlag :-) 
+
+[09.11.95]
+
+- bugfix: xinherit now calls xinherit and NOT xinventory
+
+[23.08.95]
+
+- xcall and xeval try to include a private header file ~/.xtool.h if one 
+  exists. You may define own macros or whatever to make things easier
+
+[21.06.95]
+
+- new option "cmdavg" to xwho, shows command_average in ascending order
+
+[20.06.95]
+
+- removed redirecting of xcall and xeval (too buggy)
+
+- added options -f and -m to xprops (show propflags and propmethods)
+
+[06.06.95]
+
+  ** IMPORTANT ** (not backward compatible change)
+- xcall and xeval now write to a file, when redirecting their output
+  using '>'. It is also possible to pipe output into other commands
+
+[30.05.95]
+
+- xlook/xdlook are now able to write into pipes
+
+- xwho shows the name of any user currently logging in
+  instead of simply " R O O T "
+
+[10.04.95]
+
+- added option -i to xgrep (ignore case)
+
+- bugfix in command pipelines
+
+[28.03.95]
+
+- xinherit is now able to write into pipes
+
+[20.03.95]
+
+- added IO redirection like:  xwho ip > testfile  or  xwho >> testfile
+                        and:  xmore < testfile
+  (this is very ALPHA!!!)
+
+[01.03.95]
+
+- new command xwc. Count lines, words and chars in file
+  Syntax xwc [-clw] [file]  (is able to read from pipe)
+
+[17.02.95]
+
+- changed formatting of xprops output
+
+[15.02.95]
+
+- lots of code added, some rewritten
+
+- removed xalias capability (the shell alias provides the same functionality)
+
+- removed xcindent, xdate, xquit 
+
+- bugs fixed in xcallouts, xprops, xman (when using xxtool)
+
+- added command pipelines for selected commands:
+
+  -> cmd : is able to read from pipe
+  cmd -> : is able to write into pipe
+
+  xcallouts ->
+  -> xcat ->
+  xcmds ->
+  -> xgrep ->
+  xhbeats ->
+  -> xhead ->
+  -> xmore
+  -> xmsg
+  xprops ->
+  -> xsort ->
+  -> xtail ->
+  xwho ->
+
+  the general syntax using pipes is:
+
+    cmd_a [arg_a] | cmd_b [arg_b] | ...
+
+  only the first space between cmd_a and arg_a or between cmd_a and the
+  first pipe symbol is necessary.
+
+  All functions which allow direct output into a pipe don't use a pager
+  to show its output even if no pipe follows this command. You may use
+  cmd [arg]|xmore to obtain the old results.
+
+- new commands xtail, xhead, xcat and xsort
+
+- command line option -v added to xgrep to print lines not matching the search
+  pattern
+
+- i/o redirection is coming soon
diff --git a/obj/tools/MGtool/toolcmd.c b/obj/tools/MGtool/toolcmd.c
new file mode 100644
index 0000000..83504b4
--- /dev/null
+++ b/obj/tools/MGtool/toolcmd.c
@@ -0,0 +1,2033 @@
+/*
+ * MGtool-1.0
+ * File: toolcmds.c
+ * Maintainer: Kirk@MorgenGrauen
+ */
+
+/*------------------------------------------*/
+/* the original Xtool is copyrighted by Hyp */
+/*------------------------------------------*/
+
+#include "tool.h"
+
+/*----------------------------------------------------------------------
+ * command functions
+ */
+
+int Xcall(string str)
+{
+  object obj, callobj;
+  string file, callstr, callfun, callexpr, error, errlog;
+  int *ru1, *ru2, xtime;
+  mixed res;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xcall <object>-><function>(<arguments>)");
+  TK("Xcall: str: "+(str?str:"(NULL)"));
+  if(sscanf(str, "%s->%s(%s", callstr, callfun, callexpr)!=3)
+    return FALSE;
+  if(!(callobj=XFindObj(callstr)))
+    return TRUE;
+  else
+  {
+#if 0
+    write_file(TOOL_LOG,
+	       sprintf("%s (%s) xcall %s (env: %s)\n",ctime(time()),
+		       getuid(cloner),str,object_name(environment(cloner))));
+#endif
+    file=LPC_FILE+".c";
+    if(file_size(file)>0)
+      rm(file);
+    if(obj=find_object(LPC_FILE))
+      Destruct(obj);
+    obj=0;
+    write_file(file,
+	       "#include <properties.h>\n"+
+	       "#include <thing/properties.h>\n"+
+	       "#include <defines.h>\n"+
+	       "#include <wizlist.h>\n"+
+	       "#include <moving.h>\n"+
+	       "#include \"/secure/wizlevels.h\"\n"+
+	       (file_size(PRIVATE_HEADER)>=0?"#include \""+PRIVATE_HEADER+"\"\n":"")+
+	       "mixed get(string str){return previous_object()->XFindObj(str);}\n"+
+	       "mixed eval(object obj,mixed me,mixed here){return obj->"+callfun+"("+callexpr+";}\n");
+    errlog = ERR_FILE;
+    if(file_size(errlog)>0)
+      rm(errlog);
+    if(error=catch((LPC_FILE)->__nixgibts__()))
+      W("Error: "+error[1..]);
+    else
+    {
+      obj=find_object(LPC_FILE);
+      ru1=rusage();
+      error=catch(res=(mixed)obj->eval(callobj, cloner, ENV(cloner)));
+      ru2=rusage();
+      if(error)
+	W("Error: "+error[1..]);
+      else
+      {
+	xtime=ru2[0]-ru1[0]+ru2[1]-ru1[1];
+	WDLN("Evaluation time: "+(xtime<0 ? 0 : xtime)+" ms");
+	PIPE_DELETE(pipe_of);
+	if(pipe_out&&pipe_of)
+	  write_file(pipe_of,mixed_to_string(res, MAX_RECURSION)+"\n");
+	else
+	WLN("Result: "+mixed_to_string(res, MAX_RECURSION));
+	if(objectp(res))
+    m_add(variable, "result", res);
+      }
+    }
+    rm(file);
+  }
+  if(obj) Destruct(obj);
+  return TRUE;
+}
+
+int Xcallouts(string str)
+{
+  object obj;
+  mixed callouts, args;
+  string fun, tmp, file;
+  int delay, i, s;
+  
+  SECURE2(TRUE);
+  TK("Xcallouts: str: "+(str?str:"(NULL)"));
+  if(!pipe_out)
+  {
+    USAGE1(str, "xcallo(uts) [search pattern]");
+    file=TMP_FILE;
+    if(file_size(file)>0)
+      rm(file);
+    if(!str)
+      str="^\\[~/";
+    else if(!regexp(({"dummy"}), str))
+    {
+      WDLN("Bad regular expression");
+      return TRUE;
+    } 
+  }
+  else
+  {
+    USAGE1(str, "xcallo(uts)");
+    if(str&&str!="")
+    {
+      WDLN("More arguments than expected");
+      return TRUE;
+    }
+  }
+  callouts=call_out_info();
+  s=sizeof(callouts);
+  PIPE_DELETE(pipe_of);
+  for(i=0; i<s; i++)
+  {
+    if(callouts[i]&&pointerp(callouts[i]))
+    {
+      tmp=sprintf("%O %Os %O (%s)",callouts[i][0],callouts[i][2],
+		  callouts[i][1],(sizeof(callouts[i])>3?
+				  mixed_to_string(callouts[i][3],
+						  MAX_RECURSION):"0"));
+      if(pipe_out&&pipe_of)
+	write_file(pipe_of,tmp+"\n");
+      else
+	if(sizeof(regexp(({tmp}), str)))
+	  WLN(tmp);
+    }
+  }
+  return TRUE;
+}
+
+int Xcat(string str)
+{
+  string *tmp,file;
+  int s;
+
+  SECURE2(TRUE);
+  TK("Xcat: str: "+str);
+  if(!pipe_in)
+  {
+    USAGE2(str, "xcat <file>");
+    if(!(file=XFindFile(str)))
+    {
+      WDLN("Can't find file");
+      return TRUE;
+    }
+  }
+  else
+  {
+    if(str&&str!="-")
+      USAGE3("xcat -");
+
+    if (file_size(file=pipe_if)<0)
+    {
+      WDLN("Missing input to xcat");
+      return TRUE;
+    }
+  }
+  tmp=old_explode(read_file(file,0,PIPE_MAX),"\n");
+  if(pipe_in&&pipe_if==PIPE_FILE)
+    rm(PIPE_FILE);
+  if (pipe_out&&pipe_of)
+    write_file(pipe_of,implode(tmp,"\n")+"\n");
+  else
+    WLN(implode(tmp,"\n"));
+  return TRUE;
+}
+
+int Xcd(string str)
+{
+  object dest;
+  string path;
+  
+  SECURE2(TRUE);
+  USAGE1(str, "xcd [object]");
+  TK("Xcd: str: "+(str?str:"(NULL)"));
+  if(!str)
+  {
+    if(!(path=(string)cloner->QueryProp("start_home")))
+      path="/";
+  }
+  else if((dest=XFindObj(str,1)))
+    path="/"+implode(old_explode(object_name(dest),"/")[0..<2],"/");
+  else
+    path="";
+
+  TK("Xcd: path: "+(path?path:"(NULL)"));
+  if(!sizeof(path))
+    path=str;
+  PL->_cd(path);
+
+  return TRUE;
+}
+
+int Xclean(string str)
+{
+  object env;
+  
+  SECURE2(TRUE);
+  USAGE1(str, "xcle(an) [object]");
+  TK("Xclean: str: "+(str?str:"(NULL)"));
+  if(!str)
+    env=ENV(cloner);
+  if(env||(env=XFindObj(str)))
+  {
+    PrintShort("Cleaning: ", env);
+    filter(filter(all_inventory(env), #'is_not_player),//'
+		 "Destruct", ME);
+  }
+  return TRUE;
+}
+
+int Xclone(string str)
+{
+  object obj;
+  string file, errlog, error;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xclo(ne) <filename>");
+  if(!(file=XFindFile(str))) return TRUE;
+  errlog=ERR_FILE;
+  if(file_size(errlog)>0) rm(errlog);
+  WLN("Clone: "+short_path(file));
+  if(!(error=catch(obj=clone_object(file))))
+  { 
+    m_add(variable, "clone", obj);
+    if(!MoveObj(obj, ENV(cloner), TRUE))
+      WDLN("Cannot move object into this room");
+    else if(!obj->QueryNoGet())
+    {
+      if(!MoveObj(obj, cloner, TRUE))
+	WDLN("Cannot move object into your inventory");
+    }
+  }
+  else
+    W("Error: "+error[1..]);
+  return TRUE;
+}
+
+int Xuclone(string str)
+{
+  object obj;
+  string file, errlog, error;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xuclo(ne) <filename>");
+  if(!(file=XFindFile(str))) return TRUE;
+  errlog=ERR_FILE;
+  if(file_size(errlog)>0) rm(errlog);
+  if(obj=find_object(file)) {
+    Destruct(obj);
+    WLN("Update and clone: "+short_path(file));
+  }
+  else
+    WLN("Clone: "+short_path(file));
+  if(!(error=catch(obj=clone_object(file))))
+  {
+    variable["clone"] = obj;
+    if(!MoveObj(obj, ENV(cloner), TRUE))
+      WDLN("Cannot move object into this room");
+    else if(!obj->QueryNoGet())
+    {
+      if(!MoveObj(obj, cloner, TRUE))
+	WDLN("Cannot move object into your inventory");
+    }
+  }
+  else
+    W("Error: "+error[1..]);
+  return TRUE;
+}
+
+int Xcmds(string str)
+{
+  object obj;
+  mixed *cmds;
+  int i, s;
+
+  SECURE2(TRUE);
+  USAGE1(str, "xcm(ds) [object]");
+  TK("Xcmds: str: "+(str?str:"(NULL)"));
+  if(!str)
+    obj=ENV(cloner);
+  else if(!(obj=XFindObj(str)))
+  {
+    WDLN("Can't find object");
+    return TRUE;
+  }
+  s=sizeof(cmds=query_actions(cloner,1|2|4|8|16));
+  PIPE_DELETE(pipe_of);
+  for(i=0; i<s; i+=5)
+    if(cmds[i+3]==obj)
+      if(pipe_out&&pipe_of)
+	write_file(pipe_of,ALEFT(cmds[i]+" ", 15, ".")+
+		   (cmds[i+2] ? " * " : " . ")+cmds[i+4]+"()\n");
+      else
+	WLN(ALEFT(cmds[i]+" ", 15, ".")+
+	    (cmds[i+2] ? " * " : " . ")+cmds[i+4]+"()");
+  return TRUE;
+}
+  
+int Xdbg(string str)
+{
+  object obj;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xdb(g) <object>");
+  TK("Xdbg: str: "+(str?str:"(NULL)"));
+  if((obj=XFindObj(str)))
+  {
+    debug_info(1, obj);
+    debug_info(0, obj);
+  }
+  return TRUE;
+}
+
+int Xdclean(string str)
+{
+  object env;
+  
+  SECURE2(TRUE);
+  USAGE1(str, "xdc(lean) [object]");
+  TK("Xdclean: str: "+(str?str:"(NULL)"));
+  if(!str)
+    env=ENV(cloner);
+  if(env||(env=XFindObj(str)))
+  {
+    PrintShort("Deep cleaning: ", env);
+    filter(filter(all_inventory(env), #'is_not_player, ME),//'
+		 "DeepClean", ME);
+  }
+  return TRUE;
+}
+
+int Xddes(string str)
+{
+  object obj;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xdd(es) <object>");
+  TK("Xddes: str: "+(str?str:"(NULL)"));
+  if((obj=XFindObj(str)))
+  {
+    PrintShort("Deep destruct: ", obj);
+    filter(deep_inventory(obj), "Destruct", ME);
+    Destruct(obj);
+  }
+  return TRUE;
+}
+
+int Xdes(string str)
+{
+  object obj;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xde(s) <object>");
+  TK("Xdes: str: "+(str?str:"(NULL)"));
+  if((obj=XFindObj(str)))
+  {
+    PrintShort("Destruct: ",obj);
+    Destruct(obj);
+  }
+  return TRUE;
+}
+
+int Xdlook(string str)
+{
+  object obj;
+  
+  SECURE2(TRUE);
+  USAGE1(str, "xdl(ook) [object]");
+  TK("Xdlook: str: "+(str?str:"(NULL)"));
+  if(!str)
+    obj=cloner;
+  if(obj||(obj=XFindObj(str)))
+  {
+    PIPE_DELETE(pipe_of);
+    DeepPrintShort(obj,NULL,NULL,(pipe_out&&pipe_of)?pipe_of:"");
+  }
+  return TRUE;
+}
+
+int Xdo(string str)
+{
+  int i, n, s;
+  string *strs, cmd;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xdo [<number1>#]<command1>[;[<number2>#]<command2>] ...");
+  if(!str||str==""||!(s=sizeof(strs=strip_explode(str, ";"))))
+    return FALSE;
+  for(i=0; i<s; i++)
+  {
+    if(strs[i])
+    {
+      switch(sscanf(strs[i], "%d#%s", n, cmd))
+      {
+	case 0:
+	if(!Command(strs[i])) return TRUE;
+	break;
+	case 1:
+	if(cmd&&(!Command(cmd))) return TRUE;
+	break;
+	case 2:
+	n= n<1 ? 1 : n;
+	if(cmd)
+	{
+	  while(n--)
+	    if(!Command(cmd)) return TRUE;
+	}
+	break;
+      }
+    }
+  }
+  return TRUE;
+}
+
+int Xdupdate(string str)
+{
+  int i, s;
+  object obj;
+  string file, *list;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xdu(pdate) <filename>");
+  if(!(file=XFindFile(str)))
+    return TRUE;
+  if(file[<2..<1]==".c")
+    file=file[0..<3];
+  if(obj=XFindObj(file))
+  {
+    PrintShort("Deep updating: ", obj);
+    list=inherit_list(obj);
+    for(s=sizeof(list); i<s; i++)
+    {
+      if(obj=find_object(list[i]))
+	Destruct(obj);
+    }
+  }
+  return TRUE;
+}
+
+int Xecho(string str)
+{
+  TK("Xecho: str: "+(str?str:"(NULL)"));
+  WLN(str);
+  return TRUE;
+}
+
+int Xeval(string str)
+{
+  object obj;
+  string file, error;
+  int *ru1, *ru2, xtime;
+  mixed res;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xev(al) <expression>");
+  file=LPC_FILE+".c";
+  if(file_size(file)>0)
+    rm(file);
+  if(obj=find_object(LPC_FILE))
+    Destruct(obj);
+#if 0
+  write_file(TOOL_LOG,
+	     sprintf("%s (%s) xeval %s\n",
+		     ctime(time()),getuid(cloner),str));
+#endif
+  write_file(file,
+	     "#include <properties.h>\n"+
+	     "#include <thing/properties.h>\n"+
+	     "#include <defines.h>\n"+
+             "#include <wizlist.h>\n"+
+	     "#include <moving.h>\n"+
+	     "#include \"/secure/wizlevels.h\"\n"+
+	     (file_size(PRIVATE_HEADER)>=0?"#include \""+PRIVATE_HEADER+"\"\n":"")+
+	     "get(str){return previous_object()->XFindObj(str);}\n"+
+	     "eval(me,here){return "+str+";}");
+  if(error=catch(obj=clone_object(file)))
+    W("Error: "+error[1..]);
+  else
+  {
+    ru1=rusage();
+    error=catch(res=(mixed)obj->eval(cloner, ENV(cloner)));
+    ru2=rusage();
+    if(error)
+      W("Error: "+error[1..]);
+    else
+    {
+      xtime=ru2[0]-ru1[0]+ru2[1]-ru1[1];
+      WDLN("Evaluation time: "+(xtime<0 ? 0 : xtime)+" ms");
+      PIPE_DELETE(pipe_of);
+      if(pipe_out&&pipe_of)
+        write_file(pipe_of,mixed_to_string(res,MAX_RECURSION)+"\n");
+      else
+        WLN("Result: "+mixed_to_string(res, MAX_RECURSION));
+      if(objectp(res))
+        variable["result"] = res;
+    }
+  }
+  rm(file);
+  if(obj)
+    Destruct(obj);
+  return TRUE;
+}
+
+int Xforall(string str)
+{
+  int i, s, t, u;
+  string pat, cmd, arg, *strs, *files, fh, fr, fe, ft, ff;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xfo(rall) <filepattern> <command>");
+  if(sscanf(str, "%s %s", pat, arg)!=2)
+    return FALSE;
+  files=long_get_dir(pat, FALSE);
+  if(!(s=sizeof(files)))
+  {
+    WDLN("No matching files found");
+    return TRUE;
+  }
+  strs=old_explode(files[0], "/");
+  fh="/";
+  if(t=sizeof(strs)-1)
+    fh+=implode(strs[0..t-1], "/");
+  for(i=0; i<s; i++)
+  {
+    ft=old_explode(files[i], "/")[t];
+    if((u=sizeof(strs=old_explode(ft, ".")))&&--u)
+    {
+      ff=implode(strs[0..u-1], ".");
+      fr=fh+"/"+ff;
+      fe=strs[u];
+    }
+    else
+    {
+      fe="";
+      ff=ft;
+      fr=files[i];
+    }
+    cmd=string_replace(arg, "!!", files[i]);
+    cmd=string_replace(cmd, "!e", fe);
+    cmd=string_replace(cmd, "!f", ff);
+    cmd=string_replace(cmd, "!h", fh);
+    cmd=string_replace(cmd, "!r", fr);
+    cmd=string_replace(cmd, "!t", ft);
+    if(!(Command(cmd)))
+      break;
+  }
+  return TRUE;
+}
+
+int Xgoto(string str)
+{
+  object obj, tmp;
+  
+  SECURE2(TRUE);
+  USAGE1(str, "xgo(to) [object]");
+  if(!str) str="~/workroom";
+  if(!(obj=XFindObj(str)))
+  {
+    if(!(str=XFindFile(str)))
+      return TRUE;
+    if(catch(call_other(str, "???")))
+      return TRUE;
+    obj=find_object(str);
+  }
+  tmp=obj;
+  while(obj&&living(obj))
+    obj=ENV(obj);
+  cloner->move(obj ? obj : tmp, M_TPORT);
+  return TRUE;
+}
+
+int Xgrep(string str)
+{
+  int i, s, t, mode;
+  string *files, *ts;
+  
+  SECURE2(TRUE);
+  TK("Xgrep: str: "+(str?str:"(NULL)"));
+  mode=0;
+  if(!pipe_in)
+  {
+    USAGE2(str, "xgr(ep) [-i] [-v] <regexp> <filepattern>");
+    if(!(ts=old_explode(str, " "))||!(s=sizeof(ts)))
+      return FALSE;
+    while(ts[0][0]=='-')
+    {
+      if(s<3)
+      {
+	WDLN("Too few arguments to xgrep");
+	return FALSE;
+      }
+      switch(ts[0])
+      {
+	case "-v":
+	  mode|=XGREP_REVERT;
+	  ts=ts[1..];
+	  s--;
+	  break;
+	case "-i":
+	  mode|=XGREP_ICASE;
+	  ts=ts[1..];
+	  s--;
+	  break;
+	case "-vi":
+	case "-iv":
+	  mode|=XGREP_REVERT;
+	  mode|=XGREP_ICASE;
+	  ts=ts[1..];
+	  s--;
+	  break;
+	default:
+	  WDLN("Unknown option "+ts[0]+" given to xgrep");
+	  return FALSE;
+      }
+    }
+    str=implode(ts[0..s-2], " ");
+  }
+  else
+  {
+    if(!((ts=old_explode(str, " "))&&(s=sizeof(ts))))
+      USAGE3("xgr(ep) [-i] [-v] <regexp>");
+    while(ts[0][0]=='-')
+    {
+      if(s<2)
+      {
+	WDLN("Too few arguments to xgrep");
+	return FALSE;
+      }
+      switch(ts[0])
+      {
+	case "-v":
+	  mode|=XGREP_REVERT;
+	  ts=ts[1..];
+	  s--;
+	  break;
+	case "-i":
+	  mode|=XGREP_ICASE;
+	  ts=ts[1..];
+	  s--;
+	  break;
+	case "-iv":
+	case "-vi":
+	  mode|=XGREP_REVERT;
+	  mode|=XGREP_ICASE;
+	  ts=ts[1..];
+	  s--;
+	  break;
+	default:
+	  WDLN("Unknown option "+ts[0]+" given to xgrep");
+	  return FALSE;
+      }
+    }
+    str=implode(ts[0..s-1], " ");
+  }
+  
+  if(mode&XGREP_ICASE)
+    str=lower_case(str);
+  if(!(regexp(({"dummy"}), str)))
+  {
+    WDLN("Bad regular expression");
+    return TRUE;
+  }
+  if(!pipe_in)
+  {
+    if(file_size(TMP_FILE)>0)
+      rm(TMP_FILE);
+    if((t=sizeof(files=long_get_dir(XFile(ts[s-1]), FALSE)))&&
+       (file_size(files[0])>=0))
+    {
+      for(i=0; i<t; i++)
+	XGrepFile(str, files[i], mode);
+      if(pipe_out&&pipe_of)
+      {
+	PIPE_DELETE(pipe_of);
+	if(!pipe_ovr)
+	{
+	  write_file(pipe_of,read_file(TMP_FILE,0,PIPE_MAX));
+	  rm(TMP_FILE);
+	}
+	else
+	  rename(TMP_FILE,pipe_of);
+      }
+      else
+      {
+	W(read_file(TMP_FILE,0,PIPE_MAX));
+	rm(TMP_FILE);
+      }
+    }
+    else
+      WDLN("Cannot read file(s)");
+  }
+  else
+  {
+    string *tmp;
+    if(file_size(pipe_if)<0)
+    {
+      WDLN("Missing input to xgrep");
+      return TRUE;
+    }
+    TK("Xgrep: str: "+str+" mode: "+mode);
+    s=sizeof(tmp=old_explode(read_file(pipe_if,0,PIPE_MAX),"\n"));
+    PIPE_DELETE(pipe_of);
+    for(i=0;i<s;i++)
+    {
+      // TK(tmp[i]+"<->"+str);
+      if(sizeof(regexp(({(mode&XGREP_ICASE?lower_case(tmp[i]):tmp[i])}),str)))
+      {
+	// TK("Xgrep: matched!");
+	if(!(mode&XGREP_REVERT))
+	  if(pipe_out&&pipe_of)
+	    write_file(pipe_of,tmp[i]+"\n");
+	  else
+	    WLN(tmp[i]);
+      }
+      else
+	if(mode&XGREP_REVERT)
+	  if(pipe_out&&pipe_of)
+	    write_file(pipe_of,tmp[i]+"\n");
+	  else
+	    WLN(tmp[i]);
+    }
+  }
+  return TRUE;
+}
+
+int Xhbeats(string str)
+{
+  object obj;
+  object *hbeatinfo;
+  string tmp, file;
+  int i, s;
+  
+  SECURE2(TRUE);
+  TK("Xhbeats: str: "+(str?str:"(NULL)"));
+  if(!pipe_out)
+  {
+    USAGE1(str, "xhb(eats) [search pattern]");
+    if(!str)
+      str=RNAME(cloner);
+    else if(!regexp(({"dummy"}), str))
+    {
+      WDLN("Bad regular expression");
+      return TRUE;
+    }
+  }
+  else
+  {
+    USAGE1(str,"xhb(eats)");
+    if(str&&str!="")
+    {
+      WDLN("More arguments than expected");
+      return TRUE;
+    }
+  }
+  s=sizeof(hbeatinfo=heart_beat_info());
+  PIPE_DELETE(pipe_of);
+  for(i=0; i<s; i++)
+    if(hbeatinfo[i]&&objectp(hbeatinfo[i]))
+    {
+      tmp=ObjFile(hbeatinfo[i]);
+      if(sizeof(regexp(({tmp}), str)))
+	if(pipe_out&&pipe_of)
+	  write_file(pipe_of, tmp+"\n");
+	else
+	  WLN(tmp);
+    }
+  return TRUE;
+}
+
+int Xhead(string str)
+{
+  int lines;
+  string *tmp, file;
+
+  SECURE2(TRUE);
+  TK("Xhead: str: "+(str?str:"(NULL)"));
+  if(!pipe_in)
+  {
+    USAGE2(str, "xhead <-#> <file>");
+    if(sscanf(str,"-%d %s",lines,file)!=2)
+	return FALSE;
+    if(!(file=XFindFile(file)))
+      return FALSE;
+  }
+  else
+  {
+    USAGE2(str, "xhead <-#>");
+    if(sscanf(str,"-%d",lines)!=1)
+      return FALSE;
+    if (file_size(file=pipe_if)<0)
+    {
+      WDLN("Missing input to xhead");
+      return TRUE;
+    }
+  }
+  tmp=old_explode(read_file(file,0,PIPE_MAX),"\n")[0..lines-1];
+  if(pipe_in&&pipe_if==PIPE_FILE)
+    rm(PIPE_FILE);
+  if (pipe_out&&pipe_of)
+  {
+    PIPE_DELETE(pipe_of);
+    write_file(pipe_of,implode(tmp,"\n")+"\n");
+  }
+  else
+    WDLN(implode(tmp,"\n"));
+  return TRUE;
+}
+
+int Xhelp(string str)
+{
+  SECURE2(TRUE);
+  USAGE1(str, "xhe(lp)");
+  XMoreFile(TOOL_MANPAGE, FALSE);
+  return TRUE;
+}
+
+int Xids(string str)
+{
+  object obj;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xid(s) <object>");
+  TK("Xids: str: "+(str?str:"(NULL)"));
+  if((obj=XFindObj(str)))
+    WLN("UID=\""+getuid(obj)+"\" / EUID=\""+geteuid(obj)+"\"");
+  return TRUE;
+}
+
+int Xinfo(string str)
+{
+  object obj;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xinf(o) <object>");
+  TK("Xinfo: str: "+(str?str:"(NULL)"));
+  if((obj=XFindObj(str)))
+  {
+    if(is_player(obj))
+    {
+      WLN(PlayerWho(obj));
+      WLN(PlayerMail(obj, FALSE));
+      WLN(PlayerIP(obj, FALSE));
+      WLN(PlayerRace(obj, FALSE));
+      WLN(PlayerDomain(obj, FALSE));
+      WLN(PlayerStats(obj, FALSE));
+      WLN(PlayerSnoop(obj, FALSE));
+    }
+    else
+      WDLN("Sorry, this is not a player");
+  }
+  return TRUE;
+}
+
+int Xinherit(string str)
+{
+  int s;
+  object obj;
+  string *strs, *inlist;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xinh(erit) <object> [function]");
+  TK("Xinherit: str: "+str);
+  if(!(strs=strip_explode(str, " ")))
+    return FALSE;
+  if(obj=XFindObj(strs[0]))
+  {
+    inlist=inherit_list(obj);
+    s=sizeof(inlist);
+    while(s--)
+    {
+      if(catch(call_other(inlist[s], "???")))
+      {
+	WDLN("Failed to load all inheritance objects");
+	return TRUE;
+      }
+    }
+    obj=find_object(inlist[0]);
+    PIPE_DELETE(pipe_of);
+    if(sizeof(strs)==1)
+      Inheritance(obj ,0 ,"");
+    else
+      Inheritance(obj, strs[1], "");
+  }
+  return TRUE;
+}
+
+int Xinventory(string str)
+{
+  int i, short;
+  object item;
+  mixed who;
+  
+  SECURE2(TRUE);
+  USAGE1(str, "xi [-s] [player]");
+  TK("Xinventory: str: "+str);
+  short=0;
+  who=cloner;
+  if(str&&str!="")
+  {
+    if(str=="-s")
+    {
+      short=1;
+      who=cloner;
+    }
+    else if(sscanf(str,"-s %s",who))
+    {
+      short=1;
+      who=XFindObj(who);
+    }
+    else if(sscanf(str,"%s",who))
+    {
+      short=0;
+      who=XFindObj(who);
+    }
+    else
+      who=cloner;
+  }
+  if(!who)
+    return FALSE;
+  PIPE_DELETE(pipe_of);
+  if(!(pipe_out&&pipe_of))
+    WLN(who->name(WESSEN)+" Inventory:"+(short?" (short)":""));
+  if(!short)
+    if(pipe_out&&pipe_of)
+      FORALL(item, who) PrintShort(ARIGHT(++i+". ", 4, " "), item, pipe_of);
+    else
+      FORALL(item, who) PrintShort(ARIGHT(++i+". ", 4, " "), item);
+  else
+    if(pipe_out&&pipe_of)
+      FORALL(item, who) write_file(pipe_of,++i+". ["+object_name(item)+"]\n");
+    else
+      FORALL(item, who) WLN(++i+". ["+object_name(item)+"]");
+  return TRUE;
+}
+
+int Xlag(string str)
+{
+  int i;
+  float *lag;
+  object daemon;
+  string lags;
+
+  if(file_size(LAG_O_DAEMON+".c")<=0)
+  {
+    WLN("Sorry, lag-o-daemon is missing!");
+    return TRUE;
+  }
+  
+  LAG_O_DAEMON->MachHin();
+  if(!(daemon=find_object(LAG_O_DAEMON)))
+    lag=({-1.0,-1.0,-1.0});
+  else
+    lag=(float *)daemon->read_lag_data();
+  lags="Letzte 60 min: ";
+  if(lag[0]>=0.0)
+  {
+    for(i=round(lag[0])-1;i>=0;i--)
+      lags+="#";
+    lags+=" ("+sprintf("%.1f",lag[0])+"%)";
+  }
+  else
+    lags+="N/A";
+  lags+="\nLetzte 15 min: ";
+  if(lag[1]>=0.0)
+  {
+    for(i=round(lag[1])-1;i>=0;i--)
+      lags+="#";
+    lags+=" ("+sprintf("%.1f",lag[1])+"%)";
+  }
+  else
+    lags+="N/A";
+  lags+="\nLetzte Minute: ";
+  if(lag[2]>=0.0)
+  {
+    for(i=round(lag[2])-1;i>=0;i--)
+      lags+="#";
+    lags+=" ("+sprintf("%.1f",lag[2])+"%)";
+  }
+  else
+    lags+="N/A";
+  WLN(lags);
+  return TRUE;
+}
+
+int Xlight(string str)
+{
+  int s, addlight;
+
+  SECURE2(TRUE);
+  USAGE1(str, "xli(ght) [light]");
+  if(str)
+  {
+    if(!sscanf(str, "%d", addlight))
+      return FALSE;
+    xlight+=addlight;
+    cloner->AddIntLight(addlight);
+  }
+  WDLN("Current light levels: "+TOOL_NAME+"="+xlight+", room="+
+       ENV(cloner)->QueryIntLight());
+  return TRUE;
+}
+
+int Xload(string str)
+{
+  int i, f;
+  object obj, *inv, vroom;
+  string file, errlog, error, *strs;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xloa(d) <filename>");
+  if(!(file=XFindFile(str)))
+    return TRUE;
+  errlog=ERR_FILE;
+  if(file_size(errlog)>0)
+    rm(errlog);
+  if(obj=find_object(file))
+  {
+    if(catch(call_other(VOID, "???")))
+    {
+      WDLN("Error: cannot find "+VOID+" to rescue players");
+      return TRUE;
+    }
+    else
+      vroom = find_object(VOID);
+    if(inv=filter(all_inventory(obj), #'is_player, ME))//'
+      for(i=0; i<sizeof(inv); i++)
+	MoveObj(inv[i], vroom, TRUE);
+    Destruct(obj);
+    WLN("Update and load: "+file);
+  }
+  else
+    WLN("Load: "+file);
+  if(error=catch(call_other(file, "???")))
+  {
+    W("Error: "+error[1..]);
+    if(vroom)
+      tell_object(vroom, "*** Failed to load room. You are in the void!\n");
+  }
+  else if(inv)
+  {
+    obj=find_object(file);
+    for(i=0; i<sizeof(inv); i++)
+      if(inv[i])
+	MoveObj(inv[i], obj, TRUE);
+  }
+  return TRUE;
+}
+
+int Xlook(string str)
+{
+  object obj;
+  string st;
+  string file;
+  
+  SECURE2(TRUE);
+  USAGE1(str, "xloo(k) [object]");
+  TK("Xlook: str: "+str);
+  PIPE_DELETE(pipe_of);
+  file = pipe_out&&pipe_of ? pipe_of : "";
+  if(str&&str!="")
+  {
+    if((obj=XFindObj(str)))
+      PrintObj(obj,file);
+  }
+  else
+  {
+    obj=ENV(cloner);
+    PrintObj(obj,file);
+  }
+  return TRUE;
+}
+
+int Xlpc(string str)
+{
+  object obj;
+  string file, error;
+  int *ru1, *ru2, time;
+  mixed res;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xlp(c) <lpc code>");
+  file=LPC_FILE+".c";
+  if(file_size(file)>0)
+    rm(file);
+  if(obj=find_object(LPC_FILE))
+    Destruct(obj);
+  write_file(file,
+      "#pragma no_warn_missing_return,strong_types,warn_deprecated,rtt_checks\n"
+      "#include <properties.h>\n"
+      "#include <thing/properties.h>\n"
+      "#include <defines.h>\n"
+      "#include <wizlist.h>\n"
+      "#include <moving.h>\n"
+      "#include \"/secure/wizlevels.h\"\n"
+      +(file_size(PRIVATE_HEADER)>=0?"#include \""+PRIVATE_HEADER+"\"\n":"")+
+      "object get(string str){return previous_object()->XFindObj(str);}\n"+
+      "mixed eval(mixed me, mixed here){"+str+"}");
+  if(error=catch(obj=clone_object(file)))
+    W("Error: "+error[1..0]);
+  else
+  {
+    ru1=rusage();
+    error=catch(res=(mixed)obj->eval(cloner, ENV(cloner)));
+    ru2=rusage();
+    if(error)
+      W("Error: "+error[1..]);
+    else
+    {
+      time=ru2[0]-ru1[0]+ru2[1]-ru1[1];
+      WDLN("Evaluation time: "+(time<0 ? 0 : time)+" ms");
+      WLN("Result: "+mixed_to_string(res, MAX_RECURSION));
+      if(objectp(res))
+        variable["result"] = res;
+    }
+  }
+  rm(file);
+  if(obj)
+    Destruct(obj);
+  return TRUE;
+}
+
+int Xman(string str)
+{
+  string manpage;
+  int i, found;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xma(n) <manpage>");
+  W("Man: ");
+  for(i=0, found=0; i<sizeof(manpath); i++)
+  {
+    manpage=manpath[i]+str;
+    if(file_size(manpage)>=0)
+    {
+      WLN(manpage);
+      XMoreFile(manpage, FALSE);
+      found=1;
+      break;
+    }
+  }
+  if(!found)
+    WLN("- no help available -");
+  return TRUE;
+}
+
+int Xmore(string str)
+{
+  string *args, file;
+  int line;
+  
+  SECURE2(TRUE);
+  TK("Xmore: str: "+str);
+  if (!pipe_in)
+  {
+    USAGE2(str, "xmor(e) <filename> [start]");
+    switch(sizeof(args=strip_explode(str, " ")))
+    {
+      case 1:
+      moreoffset=1;
+      break;
+      case 2:
+      sscanf(args[1], "%d", line);
+      moreoffset= line>0 ? line : 1;
+      break;
+      default:
+      return FALSE;
+    }
+    if(file=XFindFile(args[0]))
+      XMoreFile(file, TRUE);
+  }
+  else
+  {
+    if(file_size(pipe_if)<0)
+    {
+      WDLN("Missing input to xmore");
+      return TRUE;
+    }
+    if (!str||str=="")
+      line=1;
+    else if (sscanf(str, "%d", line)!=1)
+      USAGE3("xmor(e) [start]");
+    moreoffset= line>0 ? line : 1;
+    XMoreFile(pipe_if, TRUE);
+  }
+  return TRUE;
+}
+
+int Xmove(string str)
+{
+  object obj1, obj2;
+  string what, into;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xmov(e) <object> into <object>");
+  if((sscanf(str, "%s into %s", what, into)==2)&&
+     (obj1=XFindObj(what))&&(obj2=XFindObj(into)))
+    MoveObj(obj1, obj2, FALSE);
+  return TRUE;
+}
+
+int Xmsg(string str)
+{
+  string tmp;
+  
+  SECURE2(TRUE);
+  TK("Xmsg: str: "+str);
+  USAGE1(str, "xms(g) [to <object>|all]");
+  if(!str||str=="")
+  {
+    WDLN("Send message into room");
+    say("Message from "+CRNAME(cloner)+":\n");
+    if(pipe_in&&pipe_if)
+      say(read_file(pipe_if,0,PIPE_MAX));
+    else
+    {
+      WDLN("End message with \".\" or \"**\"");
+      input_to("XMsgSay");
+    }
+    return TRUE;
+  }
+  else if(sscanf(str, "to %s", tmp))
+  {
+    if(msgto=XFindObj(tmp))
+    {
+      PrintShort("Send message to: ", msgto);
+      tell_object(msgto, "Message from "+CRNAME(cloner)+" to you:\n");
+      if(pipe_in&&pipe_if)
+	tell_object(msgto,read_file(pipe_if,0,PIPE_MAX));
+      else
+      {
+	WDLN("End message with \".\" or \"**\"");
+	input_to("XMsgTell");
+      }
+    }
+    return TRUE;
+  }
+  else if(str=="all")
+  {
+    WDLN("Send message to all players");
+    shout("Message from "+CRNAME(cloner)+" to all:\n");
+    if(pipe_in&&pipe_if)
+      shout(read_file(pipe_if,0,PIPE_MAX));
+    else
+    {
+      WDLN("End message with \".\" or \"**\"");
+      input_to("XMsgShout");
+    }
+    return TRUE;
+  }
+  return FALSE;
+}
+
+int Xmtp(string str)
+{
+  int s;
+  string *strs, opt, dir, file;
+
+  SECURE2(TRUE);
+  USAGE2(str, "xmt(p) [options] <directory> <filename>");
+  s=sizeof(strs=old_explode(str, " "));
+  if(s<2)
+    return FALSE;
+  else if(s==2)
+    opt="";
+  else
+    opt=implode(strs[0..s-3], " ");
+  if(!(dir="/"+(string)MASTER->valid_read(strs[s-2], geteuid(),
+					  "get_dir", ME))) {
+    WDLN("No permission to open directory for reading");
+    return TRUE;
+  }
+  if(!(file="/"+(string)MASTER->valid_write(strs[s-1], geteuid(),
+					    "write_file", ME))) {
+    WDLN("No permission to open script file for writing");
+    return TRUE;
+  }
+  if(file_size(dir)!=-2 || file_size(file)==-2)
+    return FALSE;
+  XmtpScript(dir, file, opt);
+  WDLN("Done");
+  return TRUE;
+}
+
+int Xproc(string str)
+{
+  int s;
+  string *strs;
+
+  SECURE2(TRUE);
+  USAGE1(str, "xproc [-c] [-l] [-m] [-u] [-v]");
+
+  if(file_size(TOOL_PATH+"/proc")!=-2)
+  {
+    WLN("Sorry, no /proc information available!");
+    return TRUE;
+  }
+  
+  if(!str||str==""||!(s=sizeof(strs=old_explode(str, " "))))
+  {
+    WLN("Load averages:");
+    cat(TOOL_PATH+"/proc/loadavg");
+    return TRUE;
+  }
+  
+  while(s=sizeof(strs))
+  {
+    switch(strs[0])
+    {
+      case "-c":
+      WLN("CPU info:");
+      cat(TOOL_PATH+"/proc/cpuinfo");
+      break;
+      case "-l":
+      WLN("Load averages:");
+      cat(TOOL_PATH+"/proc/loadavg");
+      break;
+      case "-m":
+      WLN("Memory usage:");
+      cat(TOOL_PATH+"/proc/meminfo");
+      break;
+      case "-u":
+      WLN("Uptime:");
+      cat(TOOL_PATH+"/proc/uptime");
+      break;
+      case "-v":
+      WLN("Version:");
+      cat(TOOL_PATH+"/proc/version");
+      break;
+      default:
+      WLN("Unknown option: "+strs[0]);
+    }
+    strs=strs[1..];
+  }
+  return TRUE;
+}
+
+int Xprof(string str)
+{
+  string *funcs, inh, tmp;
+  mixed *data, *d;
+  mapping xpr;
+  object obj;
+  int i, rn;
+
+  SECURE2(TRUE);
+  USAGE2(str, "xprof <<-c>|<-C> <file>>|<object>");
+  if(str[0..2]=="-c "||str[0..2]=="-C ")
+  {
+    rn=(str[1]=='C');
+    if(str=XFindFile(str[3..]))
+    {
+      inh=str=str[0..<3];
+      if(obj=find_object(inh))
+	Destruct(obj);
+      if(catch(call_other(inh,"???")))
+	return TRUE;
+      obj=find_object(inh);
+      if(rn)
+      {
+	inh+=".xprof.c";
+	rm(inh);
+	str+=".c";
+	rename(str, inh);
+      }
+      else
+      {
+	str=XPROF_FILE;
+	rm(str);
+      }
+      tmp="inherit \""+inh+"\";\n#include \""+XPROF_MACRO+"\"\n";
+      funcs=m_indices(mkmapping(functionlist(obj, 0x08000001)))-({"__INIT"});
+      for(i=sizeof(funcs); i--;)
+	tmp+="F("+funcs[i]+",\""+funcs[i]+"\","+
+	  "(A0,A1,A2,A3,A4,A5,A6,A7,A8,A9))\n";
+      write_file(str, tmp);
+      WDLN("Done");
+    }
+  }
+  else if(obj=XFindObj(str))
+  {
+    if(xpr=(mapping)obj->__query_xprof_data__())
+    {
+      funcs=m_indices(xpr);
+      data=m_values(xpr);
+      rm(TMP_FILE);
+      str="Evaluation cost per function:\nNAME			   "+
+	" CALLS |  TOT.EXCL.  TOT.INCL. | REL.EXCL. REL.INCL.\n";
+      for(i=sizeof(funcs); i--;)
+      {
+	if(d=data[i]) {
+	  funcs[i]=ALEFT(""+funcs[i]+" ",25,".")+ARIGHT(" "+d[0], 6,".")+" | "+
+	    ARIGHT(" "+d[1],10,".")	+" "+ARIGHT(" "+d[3],10,".")+" | "+
+	    ARIGHT(" "+d[1]/d[0],9,".") +" "+ARIGHT(" "+d[3]/d[0],9,".");
+	}
+      }
+      str+=implode(sort_array(funcs, "string_compare", ME),"\n")+"\n";
+      write_file(TMP_FILE,str);
+      str="\nElapsed time per function:\nNAME			  "+
+	" CALLS |  TOT.EXCL.  TOT.INCL. | REL.EXCL. REL.INCL.\n";
+      funcs=m_indices(xpr);
+      for(i=sizeof(funcs); i--;)
+      {
+	if(d=data[i])
+	{
+	  funcs[i]=ALEFT(""+funcs[i]+" ",25,".")+ARIGHT(" "+d[0], 6,".")+" | "+
+	    ARIGHT(" "+(d[2]+5)/10+"ms",10,".")+" "+
+	    ARIGHT(" "+(d[4]+5)/10+"ms",10,".")+" | "+
+	    ARIGHT(" "+d[2]/d[0]+"us",9,".")+" "+
+	    ARIGHT(" "+d[4]/d[0]+"us",9,".");
+	}
+      }
+      str+=implode(sort_array(funcs, "string_compare", ME),"\n")+"\n";
+      write_file(TMP_FILE,str);
+      XMoreFile(TMP_FILE, FALSE);
+    }
+    else
+      WDLN("No profiling information available");
+  }
+  return TRUE;
+}
+
+int Xprops(string str)
+{
+  int i, s, flag;
+  object obj;
+  string *tmp;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xprop(s) [-f|-m] <object>");
+  TK("Xprops: str: "+str);
+  tmp=old_explode(str," ");
+  switch(tmp[0])
+  {
+    case "-f":
+    {
+      flag = 1;
+      tmp=tmp[1..];
+      break;
+    }
+    case "-m":
+    {
+      flag = 2;
+      tmp=tmp[1..];
+      break;
+    }
+  }
+  str=implode(tmp," ");
+  if((obj=XFindObj(str)))
+    DumpProperties(obj,flag);
+  return TRUE;
+}
+
+int Xquit(string str)
+{
+  SECURE2(TRUE);
+  USAGE1(str, "xqu(it)");
+  str=object_name(ENV(cloner));
+  if(member(str, '#')<0)
+    cloner->set_home(str);
+  cloner->quit();
+  return TRUE;
+}
+
+int Xscan(string str)
+{  
+  SECURE2(TRUE);
+  USAGE1(str, "xsc(an)");
+  W("Destructed variable(s): ");
+  string* oldvars=m_indices(variable);
+  VarCheck(FALSE);
+  string* desvars=oldvars-m_indices(variable);
+  desvars = map(desvars, function string (string k)
+      {return "$"+k;} );
+  W(CountUp(desvars));
+  WLN("");
+  return TRUE;
+}
+
+int Xset(string str)
+{
+  int i;
+  mixed obj;
+  string name, tmp;
+  
+  SECURE2(TRUE);
+  USAGE1(str, "xse(t) [$<name>=<object>]");
+  if(str) {
+    if(member(({"$me","$m","$here","$h"}),str)!=-1)
+      WDLN("Sorry, this is a reserved variable ["+str+"]");
+    else if(sscanf(str, "$%s=%s", name, tmp))
+    {
+      if(obj=XFindObj(tmp))
+      {
+        variable[name] = obj;
+        WLN(" $"+name+"="+ObjFile(obj));
+      }
+    }
+    else
+      return FALSE;
+  }
+  else
+  {
+    VarCheck(FALSE);
+    WLN("Current settings:");
+    foreach(string key, mixed val : variable)
+    {
+      if (val)
+        WLN(" $"+key+"="+ObjFile(val));
+      else
+        m_delete(variable, key);
+    }
+    WLN(" $me="+ObjFile(cloner));
+    WLN(" $here="+ObjFile(ENV(cloner)));
+  }
+  return TRUE;
+}
+
+int Xsh(string str)
+{
+  SECURE2(TRUE);
+  USAGE1(str, "xsh <filename>");
+  if(scriptline)
+  {
+    WDLN("Cannot execute another script file until last execution has ended");
+    return TRUE;
+  }
+  if(!(str=XFindFile(str)))
+    return TRUE;
+  str=read_file(str, 1, 1000);
+  if(!(scriptline=old_explode(str, "\n")))
+  {
+    WDLN("Bad script file");
+    return TRUE;
+  }
+  scriptsize=sizeof(scriptline);
+  XExecFile(NULL);
+  return TRUE;
+}
+
+int Xsort(string str)
+{
+  string *tmp,file;
+  int s,reverse;
+
+  SECURE2(TRUE);
+  TK("Xsort: str: "+str);
+  if(!pipe_in)
+  {
+    USAGE2(str, "xso(rt) [-r] [file]");
+    if(!(tmp=old_explode(str, " "))||(s=sizeof(tmp))>2)
+      return FALSE;
+    if(tmp[0]=="-r")
+      if(s==1)
+	return FALSE;
+      else
+      {
+	reverse=TRUE;
+	tmp=tmp[1..];
+      }
+    else if(s>1)
+      return FALSE;
+    if(!(file=XFindFile(tmp[0])))
+    {
+      WDLN("Can't find file");
+      return TRUE;
+    }
+  }
+  else
+  {
+    if(str&&str!="")
+      if(str=="-r")
+	reverse=TRUE;
+      else
+	USAGE3("xso(rt) [-r]");
+    if (file_size(file=pipe_if)<0)
+    {
+      WDLN("Missing input to xsort");
+      return TRUE;
+    }
+  }
+  tmp=old_explode(read_file(file,0,PIPE_MAX),"\n");
+  if(pipe_in&&pipe_if==PIPE_FILE)
+    rm(PIPE_FILE);
+  tmp=sort_array(tmp,reverse?#'<:#'>);
+    if (pipe_out&&pipe_of)
+      write_file(pipe_of,implode(tmp,"\n")+"\n");
+    else
+      WDLN(implode(tmp,"\n"));
+  return TRUE;
+}
+
+int Xtail(string str)
+{
+  string *tmp,file,sign;
+  int lines;
+
+  if (!str) {
+    WDLN("Missing input to xtail");
+    return TRUE;
+  }
+
+  sign="-";
+  lines=10;
+  SECURE2(TRUE);
+  TK("Xtail: str: "+str);
+  if(!pipe_in)
+  {
+    if(sscanf(str,"-%d %s",lines,file)==2)
+      sign="-";
+    else if(sscanf(str,"+%d %s",lines,file)==2)
+      sign="+";
+    else
+      file=str;
+
+    if(!(file=XFindFile(file)))
+    {
+      WDLN("Can't find file");
+      return TRUE;
+    }
+  }
+  else
+  {
+    if(sscanf(str,"-%d",lines)==1)
+      sign="-";
+    else if(sscanf(str,"+%d",lines)==1)
+      sign="+";
+    if (file_size(file=pipe_if)<0)
+    {
+      WDLN("Missing input to xtail");
+      return TRUE;
+    }
+  }
+
+  if(sign=="-")
+  {
+    if(!lines)
+      return TRUE;
+  }
+
+  if(file_size(file)>50000)
+  {
+    WDLN("File too large");
+    return TRUE;
+  }
+
+  if(sign=="+")
+    tmp=old_explode(read_file(file,0,PIPE_MAX),"\n")[lines..];
+  else
+    tmp=old_explode(read_file(file,0,PIPE_MAX),"\n")[<lines..];
+  if(pipe_in&&pipe_if==PIPE_FILE)
+    rm(PIPE_FILE);
+  if (pipe_out&&pipe_of)
+    write_file(pipe_of,implode(tmp,"\n")+"\n");
+  else
+    WLN(implode(tmp,"\n"));
+  return TRUE;
+}
+
+int Xtool(string str)
+{
+  int m;
+  string tmp;
+  object obj;
+  
+  SECURE2(TRUE);
+  TK("Xtool: str: "+str);
+  USAGE1(str, "xto(ol) [update|first=<on|off>|protect=<on|off>|"+
+	 "invcheck=<on|off>|\n"+
+	 "\t\tvarcheck=<on|off>|scanchk=<on|off>|short=<on|off>|\n"+
+	 "\t\techo=<on|off>|more=<amount>|kill|news|save|load|reset]");
+  if(str&&str!="")
+  {
+    if(sscanf(str, "more=%d", m))
+    {
+      if(m<5)
+	WDLN("Sorry, amount of lines should be more then 5");
+      else
+      {
+	WDLN("Setting amount of displayed lines to "+m);
+	morelines=m;
+      }
+    }
+    else
+      switch(str)
+      {
+	case "update":
+	if(obj=find_object(TOOL_PATH))
+	  Destruct(obj);
+	if(catch(obj=clone_object(TOOL_PATH)))
+	  WLN("Updating "+TOOL_TITLE+" failed!");
+	else
+	  obj->update_tool(AUTOLOAD_ARGS, cloner);
+	return TRUE;
+	break;
+	case "first=on":
+	MODE_ON(MODE_FIRST);
+	move(cloner);
+	WDLN("Automatic moving into pole position of inventory turned on");
+	break;
+	case "first=off":
+	MODE_OFF(MODE_FIRST);
+	WDLN("Automatic moving into pole position of inventory turned off");
+	break;
+	case "protect=on":
+	MODE_ON(MODE_PROTECT);
+	WDLN("Protection from forces and illegal moves turned on");
+	break;
+	case "protect=off":
+	MODE_OFF(MODE_PROTECT);
+	WDLN("Protection from forces and illegal moves turned off");
+	break;
+	case "invcheck=on":
+	MODE_ON(MODE_INVCHECK);
+	WDLN("Automatic checking for new objects in inventory turned on");
+	break;
+	case "invcheck=off":
+	MODE_OFF(MODE_INVCHECK);
+	WDLN("Automatic checking for new objects in inventory turned off");
+	break;
+	case "varcheck=on":
+	MODE_ON(MODE_VARCHECK);
+	VarCheck(TRUE);
+	WDLN("Automatic variable checking turned on");
+	break;
+	case "varcheck=off":
+	MODE_OFF(MODE_VARCHECK);
+	WDLN("Automatic variable checking turned off");
+	break;
+	case "scanchk=on":
+	MODE_ON(MODE_SCANCHK);
+	WDLN("Scan check turned on");
+	break;
+	case "scanchk=off":
+	MODE_OFF(MODE_SCANCHK);
+	WDLN("Scan check turned off");
+	break;
+	case "echo=on":
+	MODE_ON(MODE_ECHO);
+	WDLN("Echoing of multiple command execution turned on");
+	break;
+	case "echo=off":
+	MODE_OFF(MODE_ECHO);
+	WDLN("Echoing of multiple command execution turned off");
+	break;
+	case "short=on":
+	MODE_ON(MODE_SHORT);
+	WDLN("Use of short descriptions turned on");
+	break;
+	case "short=off":
+	MODE_OFF(MODE_SHORT);
+	WDLN("Use of short descriptions turned off");
+	break;
+	case "kill":
+	WDLN(TOOL_NAME+" selfdestructs");
+	destruct(ME);
+	break;
+	case "news":
+	XMoreFile(TOOL_NEWS, FALSE);
+	break;
+	case "reset":
+	WDLN("Resetting "+TOOL_TITLE);
+	ME->__INIT();
+	break;
+	case "load":
+	if(file_size(SAVE_FILE+".o")>0)
+	{
+	  WDLN("Loading "+TOOL_TITLE+" settings");
+	  restore_object(SAVE_FILE);
+	}
+	else
+	  WDLN("Sorry, cannot find file to load settings");
+	break;
+	case "save":
+	WDLN("Saving "+TOOL_TITLE+" settings");
+	save_object(SAVE_FILE);
+	break;
+	default:
+	return FALSE;
+      }
+  }
+  else
+  {
+    WLN(TOOL_TITLE+" settings:");
+    tmp= (" first .... = "+(MODE(MODE_FIRST) ? "on\n" : "off\n"));
+    tmp+=(" protect .. = "+(MODE(MODE_PROTECT) ? "on\n" : "off\n"));
+    tmp+=(" invcheck . = "+(MODE(MODE_INVCHECK) ? "on\n" : "off\n"));
+    tmp+=(" varcheck . = "+(MODE(MODE_VARCHECK) ? "on\n" : "off\n"));
+    tmp+=(" scanchk .. = "+(MODE(MODE_SCANCHK) ? "on\n" : "off\n"));
+    tmp+=(" echo ..... = "+(MODE(MODE_ECHO) ? "on\n" : "off\n"));
+    tmp+=(" short .... = "+(MODE(MODE_SHORT) ? "on\n" : "off\n"));
+    tmp+=(" more ..... = "+morelines);
+    WLN(sprintf("%-80#s", tmp));
+  }
+  return TRUE;
+}
+
+int Xtrace(string str)
+{
+  string file;
+  object obj;
+  
+  SECURE2(TRUE);
+  USAGE1(str, "xtrac(e) <object>");
+  if(!str||str=="")
+  {
+    trace(0);
+    WDLN("Ending trace ["+short_path("/"+traceprefix(0))+"]");
+  }
+  else if(obj=XFindObj(str))
+  {
+    PrintShort("Tracing: ", obj);
+    file=object_name(obj);
+    file=file[1..<1];
+    traceprefix(file);
+    trace(TRACE_LEVEL);
+  }
+  return TRUE;
+}
+
+int Xtrans(string str)
+{
+  object obj;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xtran(s) <object>");
+  if((obj=XFindObj(str))&&ENV(obj))
+  {
+    tell_room(ENV(obj), CRNAME(obj)+" vanishes.\n");
+    tell_room(ENV(cloner), CRNAME(obj)+
+	      " is teleported into this room by "+CRNAME(cloner)+".\n");
+    MoveObj(obj, ENV(cloner), TRUE);
+    tell_object(obj, "You've been teleported to "+CRNAME(cloner)+".\n");
+  }
+  else
+    WDLN("Failed to teleport object");
+  return TRUE;
+}
+
+int Xupdate(string str)
+{
+  object obj;
+  string file;
+  
+  SECURE2(TRUE);
+  USAGE2(str, "xup(date) <filename>");
+  if(!(file=XFindFile(str)))
+    return TRUE;
+  if(file[<2.. <1]==".c")
+    file=file[0.. <3];
+  if((obj=XFindObj(file))&&(obj=find_object(pure_file_name(obj))))
+  {
+    PrintShort("Updating: ", obj);
+    Destruct(obj);
+  }
+  else
+    WDLN("Object not found");
+  return TRUE;
+}
+
+int Xuptime(string str)
+{
+  return TRUE;
+}
+
+int Xwc(string str)
+{
+  string file;
+  string tmp, *tmp2;
+  int i, chars, words, lines, nchars, nwords, nlines;
+  
+  SECURE2(TRUE);
+  TK("Xwc: str: "+str);
+  chars=words=lines=FALSE;
+  if(!pipe_in)
+  {
+    USAGE2(str, "xwc [-cwl] <file>");
+    if(str[0]=='-')
+    {
+      while((str=str[1..])[0]!=' '&&sizeof(str))
+	switch(str[0])
+	{
+	  case 'c':
+	  chars=TRUE;
+	  break;
+	  case 'w':
+	  words=TRUE;
+	  break;
+	  case 'l':
+	  lines=TRUE;
+	  break;
+	  default:
+	  return FALSE;
+	}
+      str=str[1..];
+    }
+    if(!(file=XFindFile(str)))
+    {
+      WDLN("Can't find file");
+      return TRUE;
+    }
+  }
+  else
+  {
+    USAGE1(str,"xwc [-cwl]");
+    if(str)
+      if(str[0]=='-')
+      {
+	while((str=str[1..])[0]!=' '&&sizeof(str))
+	  switch(str[0])
+	  {
+	    case 'c':
+	    chars=TRUE;
+	    break;
+	    case 'w':
+	    words=TRUE;
+	    break;
+	    case 'l':
+	    lines=TRUE;
+	    break;
+	    default:
+	    return FALSE;
+	  }
+      }
+      else
+	return FALSE;
+    if(file_size(file=pipe_if)<0)
+    {
+      WDLN("Missing input to xwc");
+      return TRUE;
+    }
+  }
+  if(!(chars|words|lines))
+    chars=words=lines=TRUE;
+  nlines=nwords=nchars=0;
+  tmp=read_file(file,0,PIPE_MAX);
+  tmp2=explode(tmp,"\n");
+  if(lines)
+  {
+    nlines=sizeof(tmp2);
+    if(tmp2[<1]==""&&nlines>1)
+      nlines--;
+  }
+  if(words)
+    for(i=sizeof(tmp2)-1;i>=0;i--)
+    {
+      TK(sprintf("%O",tmp2[i]));
+      if(tmp2[i]!="")
+	nwords+=sizeof(regexplode(tmp2[i],"[	]")-({""," ","	"}));
+      TK(sprintf("%O",regexplode(tmp2[i],"[	]")-({""," ","	"})));
+      TK("nwords: "+nwords);
+    }
+  if(chars)
+    for(i=sizeof(tmp2)-1;i>=0;i--)
+      nchars+=sizeof(tmp2[i])+1;
+  tmp2=0;
+  tmp="";
+  if(lines)
+    tmp+=sprintf("%7d",nlines)+" ";
+  if(words)
+    tmp+=sprintf("%7d",nwords)+" ";
+  if(chars)
+    tmp+=sprintf("%7d",nchars)+" ";
+  if(file!=pipe_if)
+    tmp+=file;
+  WLN(tmp);
+  return TRUE;
+}
+
+int cmdavg_compare(string a, string b)
+{
+  int x,y;
+  string dum;
+  sscanf(a,"%s cmdavg: %d",dum,x);
+  sscanf(b,"%s cmdavg: %d",dum,y);
+  return x==y?0:(x>y?1:-1);
+}
+
+int Xwho(string opt)
+{
+  string *strs,str,func;
+  
+  SECURE2(TRUE);
+  TK("Xwho: opt: \""+opt+"\"");
+  USAGE1(opt, "xwh(o) [mail|ip|race|guild|domain|stats|snoop]");
+  func="string_compare";
+  if(!opt||opt=="")
+    strs=map(users(), "PlayerWho", ME);
+  else
+    switch(opt)
+    {
+      case "mail":
+      strs=map(users(), "PlayerMail", ME, TRUE);
+      break;
+      case "ip":
+      strs=map(users(), "PlayerIP", ME, TRUE);
+      break;
+      case "race":
+      case "guild":
+      strs=map(users(), "PlayerRace", ME, TRUE);
+      break;
+      case "domain":
+      strs=map(users(), "PlayerDomain", ME, TRUE);
+      break;
+      case "stat":
+      case "stats":
+      strs=map(users(), "PlayerStats", ME, TRUE);
+      break;
+      case "snoop":
+      strs=map(users(), "PlayerSnoop", ME, TRUE);
+      break;
+      case "cmdavg":
+      strs=map(users(), "PlayerCmdAvg", ME, TRUE);
+      func="cmdavg_compare";
+      break;
+      default:
+      return FALSE;
+    }
+  strs=sort_array(strs, func, ME);
+  if(pipe_out&&pipe_of)
+    write_file(pipe_of,implode(strs,"\n")+"\n");
+  else
+    WLN(implode(strs,"\n"));
+  return TRUE;
+}
diff --git a/obj/tools/MGtool/toollib.c b/obj/tools/MGtool/toollib.c
new file mode 100644
index 0000000..9464004
--- /dev/null
+++ b/obj/tools/MGtool/toollib.c
@@ -0,0 +1,192 @@
+/*
+ * MGtool-1.0
+ * File: toollib.c
+ * Maintainer: Kirk@MorgenGrauen
+ */
+
+/*------------------------------------------*/
+/* the original Xtool is copyrighted by Hyp */
+/*------------------------------------------*/
+
+#include "toollib.h"
+#include <lpctypes.h>
+#include <properties.h>
+/*
+string *old_explode(string str, string del)
+{
+  int s, t;
+  string *strs;
+  
+  if(del == "")
+    return ({str});
+  strs=explode(str, del);
+  s=0;
+  t=sizeof(strs)-1;
+  while(s<=t && strs[s++] == "")
+    ;
+  s--;
+  while(t>=0 && strs[t--] == "")
+    ;
+  t++;
+  if(s<=t)
+    return strs[s..t];
+  return 0;
+}
+
+string *explode(string str, string del)
+{
+  return explode(str, del);
+}
+*/
+string *strip_explode(string str, string del)
+{
+  return explode(str, del)-({""});
+}
+
+string strip_string(string str)
+{
+  return implode(strip_explode(str," ")," ");
+}
+
+int string_compare(string a, string b)
+{
+  return a==b?0:(a>b?1:-1);
+}
+
+string cap_string(string str)
+{
+  return capitalize(str);
+}
+
+string short_path(string file)
+{
+  int s;
+  string tmp;
+  
+  if(!file)
+    return 0;
+  if(PL)
+  {
+    if(file[0.. 8]=="/players/")
+    {
+      s=sizeof(getuid(PL))+8;
+      return "~"+(file[9.. s]==getuid(PL) ?
+		  file[s+1.. <1] : file[9.. <1]);
+    }
+  }
+  if(file[0.. 2]=="/d/")
+    return "+"+file[3.. <1];
+  else
+    return file;  
+}
+
+string long_path(string file)
+{
+  return (string)MASTER->make_path_absolute(file);
+}
+
+string *long_get_dir(string pat, int all)
+{
+  int i, s;
+  string str, dir, *file, *tmp;
+  
+  if(!pat)
+    return ({});
+  pat=long_path(pat);
+  if(tmp=old_explode(pat, "/"))
+    dir="/"+implode(tmp[0..sizeof(tmp)-2], "/")+"/";
+  else
+    dir="/";
+  s=sizeof(tmp=get_dir(pat));
+  file=({});
+  for(i=0;i<s;i++)
+  {
+    str=dir+tmp[i];
+    if(all||file_size(str)>=0)
+      file+=({str});
+  }
+  return file;
+}
+
+string lit_string(string str)
+{
+  str=string_replace(str, "\\", "\\\\");
+  str=string_replace(str, "\b", "\\b");
+  str=string_replace(str, "\n", "\\n");
+  str=string_replace(str, "\r", "\\r");
+  str=string_replace(str, "\t", "\\t");
+  return string_replace(str, "\"", "\\\"");
+}
+
+string mixed_to_string(mixed mix, int lvl)
+{
+  int i, j, s, t;
+  string str;
+  mixed *keys;
+
+  if(lvl)
+  {
+    switch(typeof(mix))
+    {
+      default:
+      case T_INVALID:
+      return "<invalid>";
+      case T_STRUCT:
+      return to_string(mix);
+      case T_LVALUE:
+      return "&"+mixed_to_string(mix, lvl-1);
+      case T_NUMBER:
+      case T_FLOAT:
+      return to_string(mix);
+      case T_STRING:
+      return "\""+lit_string(mix)+"\"";
+      case T_POINTER:
+      return "({"+implode(map(mix,"mixed_to_string",ME,lvl-1),",")+"})";
+      case T_OBJECT:
+      return "["+short_path(object_name((object)mix))+"]";
+      case T_MAPPING:
+      s=sizeof(keys=m_indices(mix));
+      t=get_type_info(mix, 1);
+      str="([";
+      for(i=0;i<s;i++)
+      {
+	str+=mixed_to_string(keys[i], lvl-1);
+	if(t)
+	{
+	  str+=":"+mixed_to_string(mix[keys[i],0], lvl-1);
+	  for(j=1;j<t;j++)
+	    str+=";"+mixed_to_string(mix[keys[i],j], lvl-1);
+	}
+	if(i<s-1) str+=",";
+      }
+      return str+"])";
+      case T_CLOSURE:
+      case T_SYMBOL:
+      return sprintf("%O", mix);
+      case T_QUOTED_ARRAY:
+      return "'"+mixed_to_string(funcall(lambda(0, mix)), lvl-1);
+    }
+  }
+  return "...";
+}
+
+int is_player(object obj)
+{
+  return is_obj(obj)&&query_once_interactive(obj);
+}
+
+int is_not_player(object obj)
+{
+  return is_obj(obj)&&!is_player(obj);
+}
+
+int round(float val)
+{
+  int tmp;
+
+  tmp=(int)val;
+  // only positive val
+  if( val - tmp >= 0.5 )
+    return tmp+1;
+  return tmp;
+}
diff --git a/obj/tools/MGtool/toollib.h b/obj/tools/MGtool/toollib.h
new file mode 100644
index 0000000..69e8264
--- /dev/null
+++ b/obj/tools/MGtool/toollib.h
@@ -0,0 +1,48 @@
+/*
+ * MGtool-1.0
+ * File: toollib.h
+ * Maintainer: Kirk@MorgenGrauen
+ */
+
+/*------------------------------------------*/
+/* the original Xtool is copyrighted by Hyp */
+/*------------------------------------------*/
+
+#ifndef __TOOLLIB_H__
+#define __TOOLLIB_H__ 1
+
+#ifndef MASTER
+#define MASTER __MASTER_OBJECT__
+#endif
+
+#define is_obj(x)         ((x)&&objectp(x))
+
+#define is_living(x)      (is_obj(x)&&living(x))
+#define is_not_living(x)  (is_obj(x)&&!is_living(x))
+#define is_netdead(x)     (is_obj(x)&&is_player(x)&&!interactive(x))
+#define is_alive(x)       (is_obj(x)&&is_player(x)&&interactive(x))
+#define is_snooped(x)     (is_obj(x)&&objectp(query_snoop(x)))
+#define is_not_snooped(x) (is_obj(x)&&!is_snooped(x))
+#define is_invis(x)       (is_obj(x)&&((x)->QueryProp(P_INVIS)||!(x)->QueryProp(P_SHORT))))
+#define is_not_invis(x)   (is_obj(x)&&!is_invis(x))
+
+#define pure_file_name(x)     (is_obj(x)?old_explode(object_name(x),"#")[0]:0)
+#define source_file_name(x)   (is_obj(x)?pure_file_name((x))+".c":0)
+#define string_replace(x,y,z) implode(explode((x),(y)),(z))
+
+int string_compare(string a, string b);
+string cap_string(string str);  
+string short_path(string file);
+string long_path(string file);
+//string *old_explode(string str, string del);
+//string *explode(string str, string del);
+string *strip_explode(string str, string del);
+string strip_string(string str);
+string *long_get_dir(string pat, int all);
+string lit_string(string str);
+string mixed_to_string(mixed mix, int level);
+int is_player(object obj);
+int is_not_player(object obj);
+int round(float val);
+
+#endif /* __TOOLLIB_H__ */
diff --git a/obj/tools/README b/obj/tools/README
new file mode 100644
index 0000000..b40ce10
--- /dev/null
+++ b/obj/tools/README
@@ -0,0 +1 @@
+The files contained in this subtree are unsupported.
diff --git a/obj/tools/btool.c b/obj/tools/btool.c
new file mode 100644
index 0000000..d063e88
--- /dev/null
+++ b/obj/tools/btool.c
@@ -0,0 +1,121 @@
+#pragma no_shadow, no_inherit
+#pragma strong_types,save_types,rtt_checks
+#pragma pedantic,range_check,warn_deprecated
+#pragma warn_empty_casts,warn_missing_return,warn_function_inconsistent
+
+inherit "/std/thing";
+
+#include <properties.h>
+#include <defines.h>
+
+#define BARCHIV "/d/erzmagier/boing/balance/barchives"
+
+#define BS(x) break_string(x,78,0,BS_LEAVE_MY_LFS)
+
+private varargs void print_map(mapping tmp,int short);
+
+protected void create()
+{
+  ::create();
+  SetProp(P_NAME,"Balance-Tool");
+  AddId(({"btool","balancetool","balance-tool"}));
+  SetProp(P_SHORT,"Ein Balance-Tool light");
+  SetProp(P_LONG,
+    BS("Dies ist das Balance-Tool light. Es versteht folgende Befehle:\n"
+    "- btop <n>: Zeigt den letzten genehmigten Antrag zu Top n.\n"
+    "- bsuch [-s] <str>: Sucht case-sensitiv nach str, -s bricht jeden "
+    "Eintrag nach 78 Zeichen ab.\n"
+    "- binhalt [-s]: Zeigt den gesamten Inhalt des Balancearchives, -s "
+    "bricht jeden Eintrag nach 78 Zeichen ab.\n\n"
+    "Es kann vorkommen, dass Eintraege der falschen UID zugeordnet sind, "
+    "oder dass die Genehmigung nicht sehr aussagekraeftig ist, in diesem "
+    "Fall bitte eine Mail an das Balanceteam schreiben."));
+  AddCmd("btop",
+    function int(string str)
+    {
+      int n;
+      if(!str || sscanf(str,"%d",n)!=1)
+      {
+        notify_fail(
+          BS("Syntax: btop <n>"));
+        return 0;
+      }
+      this_interactive()->ReceiveMsg(
+        BARCHIV->GetLightAnnounce(n),
+        MT_NOTIFICATION|MSG_BS_LEAVE_LFS);
+      return 1;
+    });
+  AddCmd("bsuch",
+    function int()
+    {
+      string str=this_interactive()->_unparsed_args();
+      if(!str || !sizeof(str))
+      {
+        notify_fail(
+          BS("Syntax: bsuch <str>"));
+        return 0;
+      }
+      int short;
+      string* arr=old_explode(str," ");
+      
+      if(arr[0]=="-s")
+      {
+        short=1;
+        str=implode(arr[1..]," ");
+      }
+      mapping tmp=BARCHIV->SearchIndex(str);
+      if(!sizeof(tmp))
+      {
+        this_interactive()->ReceiveMsg(
+          "Keine Eintraege gefunden.",
+          MT_NOTIFICATION);
+      }
+      else
+      {
+        print_map(tmp,short);
+      }
+      return 1;
+    });
+  AddCmd("binhalt",
+    function int(string str)
+    {
+      int short;
+      if(sizeof(str))
+      {
+        string* arr=old_explode(str," ");
+      
+        if(arr[0]=="-s")
+        {
+          short=1;
+        }
+      }
+    
+      print_map(BARCHIV->GetIndexForWizards(),short);
+      return 1;
+    });
+}
+
+private varargs void print_map(mapping tmp,int short)
+{
+	if (!mappingp(tmp))
+	{
+		this_interactive()->ReceiveMsg("Keine Daten vorhanden.\n");
+		return;
+	}
+  int* sort=sort_array(m_indices(tmp),#'>);
+  string ret="";
+  foreach(int i : sort)
+  {
+    string str=sprintf(" %4d:  %s {%s} (%s)",i,tmp[i,0],tmp[i,2],
+      strftime("%d.%m.%y",tmp[i,1]));
+    if(short)
+    {
+      ret+=BS(str[0..77]);
+    }
+    else
+    {
+      ret+=BS(str);
+    }
+  }
+  this_interactive()->More(ret);
+}
diff --git a/obj/tools/dancetool.c b/obj/tools/dancetool.c
new file mode 100644
index 0000000..dcf3b9a
--- /dev/null
+++ b/obj/tools/dancetool.c
@@ -0,0 +1,321 @@
+// DANCETOOL.C   Stand: 10.12.94 Dancer 
+//
+// Kleines Tool zum Schreiben und Nachsehen von Details.
+//
+//
+ 
+inherit "std/thing";
+ 
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+ 
+#define bs( x ) break_string( x, 78 )
+ 
+private int i, counter, first_detail_line, startline, endline; 
+private string *detail_shorts, *newstring, *file_lines, *new_lines;
+private string detail_long, save_string, old_file, new_file, actual_file,
+               detail_text;
+       
+void create()
+{
+   ::create();
+   SetProp( P_AUTOLOADOBJ,1 );
+   SetProp( P_NAME, "Dancers Sandtigerkralle" );
+   SetProp( P_GENDER, FEMALE );
+   SetProp( P_SHORT, "Dancers Sandtigerkralle" );
+   SetProp( P_LONG, bs( "Die Sandtigerkralle von Dancer ist "+
+              "ein kleines Tool, mit dem Du gewoehnliche Details "+
+              "in allen Raeumen nachschauen und in Deinen eigenen "+
+              "hinzufuegen kannst. Weitere Informationen kannst Du "+
+              "mit dem Befehl <dancetool> bekommen." ) );
+   SetProp( P_ARTICLE, 2 );
+   SetProp( P_WEIGHT, 500 );
+   SetProp( P_VALUE, 10000 );
+   SetProp( P_NOBUY, 1 );
+   SetProp( P_NODROP, 1 );
+   AddId( "kralle" );
+   AddId( "sandtigerkralle" );
+   AddId( "tigerkralle" );
+   AddId( "dancetool" );
+   AddId( "dancertool" );
+   seteuid(getuid(this_object()));
+}
+ 
+ 
+void init()
+{
+  if (!IS_LEARNER(this_player())) return;
+  ::init();
+  add_action("query_add_details", "show_detail" );
+  add_action("query_add_details", "show_details" );
+  add_action("query_add_details", "sd" );
+  add_action("set_add_detail", "add_detail" );
+  add_action("set_add_detail", "add_details" );
+  add_action("set_add_detail", "ad" );
+  add_action("give_info", "dancetool" );
+}
+ 
+ 
+int give_info()
+{
+   write( "-----------------------------------------------------------------------------\n" );
+   write( "Die Sandtigerkralle ermoeglicht folgende Befehle:\n" );
+   write( "\nadd_detail, add_details oder ad:\n" );
+   write( bs( "Ruft einen kleinen AddDetail-Editor auf, mit dem Du "+
+      "Details in Deine eigenen Raeume einfuegen kannst. Als Argument "+
+      "kann hinter den Befehl der Filename eines Raumes eingegeben "+
+      "werden. Wird kein Argument eingegeben, so wird der Raum "+
+      "genommen, in dem Du gerade stehst. Achtung: Es koennen "+
+      "nur Details in Raeume eingefuegt haben, die bereits ein "+
+      "AddDetail in ihrem create() haben. Du musst die Lupe haben, "+
+      "da der Raum nach dem Einfuegen des Details automatisch "+
+      "neu geladen wird." ) );
+   write( "\nshow_detail, show_details oder sd:\n" );
+   write( bs( "Zeigt Dir alle Zeilen des angesprochenen Raumes bzw. "+
+      "des Raumes in dem Du stehst, die ein AddDetail "+
+      "beinhalten. So kannst Du kontrollieren, welche Details "+
+      "Du schon eingebaut hast." ) );
+   write( "-----------------------------------------------------------------------------\n" );
+   return 1;
+}
+ 
+ 
+int renew_variables()
+{
+   i = counter = first_detail_line = startline = endline = 0; 
+   detail_shorts = newstring = file_lines = new_lines = ({});
+   detail_long = save_string = old_file = new_file = actual_file = detail_text = 0;
+   return 1;
+} 
+ 
+ 
+ 
+int query_add_details( string str )
+{
+   string old_file, *file_lines, new_file, *new_lines, actual_file;        
+   int i, counter, first_detail_line, startline, endline;      
+ 
+   actual_file = "";
+                            
+   if ( !str ) actual_file = object_name( environment( this_player() ) )+".c";
+   else       
+   {
+      if ( str[0..0] == "/" )
+      {
+         if ( str[<2..] != ".c" )
+             actual_file = str+".c";
+         else
+             actual_file = str;
+      }
+      else
+      {
+         if ( str[<2..] != ".c" )
+             actual_file = this_player()->QueryProp( P_CURRENTDIR )+
+                           "/"+str+".c";
+         else
+             actual_file = this_player()->QueryProp( P_CURRENTDIR )+
+                           "/"+str;
+      }
+   }
+   if ( file_size( actual_file )==-1 )
+   {
+     write( "File: "+actual_file+" nicht gefunden!\n" );
+     return 1;
+   }
+   call_other( actual_file, "???" );
+   write( "\n-----------------------------------------------------------------------------\n" );
+   write( "Aktuelles File:   "+actual_file+"\n" );
+   write( "Filegroesse:      "+file_size( actual_file )+"\n" );
+   write( "Userid des Files: "+getuid( find_object( actual_file ) )+"\n" );
+   new_file = "";
+   counter = 0;
+   first_detail_line = 0;
+   old_file = read_file( actual_file );
+   file_lines = ({ "dummy" }) + old_explode( old_file, "\n" );          
+   startline = 1;
+   endline = sizeof( file_lines )-1;        
+   for( i = startline; i <= endline; i++ )
+   {                                                        
+      new_file = new_file+file_lines[ i ]+"\n";
+      if ( strstr( file_lines[i], "AddDetail" ) != -1 )
+      {
+         write( file_lines[i]+"\n" );
+         if ( !first_detail_line ) first_detail_line = i;
+          counter = counter + 1;
+      }
+   }                                
+   write( "Details found: "+counter+".\n" ); 
+   write( "-----------------------------------------------------------------------------\n" );
+   return 1;
+}    
+ 
+ 
+int set_add_detail( string str )
+{
+ 
+   renew_variables();
+                            
+   if ( !str ) actual_file = object_name( environment( this_player() ) )+".c";
+   else       
+   {
+      if ( str[0..0] == "/" )
+      {
+         if ( str[<2..] != ".c" )
+             actual_file = str+".c";
+         else
+             actual_file = str;
+      }
+      else
+      {
+         if ( str[<2..] != ".c" )
+             actual_file = this_player()->QueryProp( P_CURRENTDIR )+
+                           "/"+str+".c";
+         else
+             actual_file = this_player()->QueryProp( P_CURRENTDIR )+
+                           "/"+str;
+      }
+   }
+   if ( file_size( actual_file )==-1 )
+   {
+     write( "File: "+actual_file+" nicht gefunden!\n" );
+     return 1;
+   }
+   call_other( actual_file, "???" );
+   write( "\n-----------------------------------------------------------------------------\n" );
+   write( "Aktuelles File:   "+actual_file+"\n" );
+   write( "Filegroesse:      "+file_size( actual_file )+"\n" );
+   write( "Userid des Files: "+getuid( find_object( actual_file ) )+"\n" );
+   new_file = "";
+   new_lines = ({ "dummy" });
+   counter = 0;
+   first_detail_line = 0;
+   old_file = read_file( actual_file );
+   file_lines = ({ "dummy" }) + old_explode( old_file, "\n" );          
+   startline = 1;
+   endline = sizeof( file_lines )-1;        
+   for( i = startline; i <= endline; i++ )
+   {                                                        
+      new_file = new_file+file_lines[ i ]+"\n";
+      if ( strstr( file_lines[i], "AddDetail" ) != -1 )
+      {
+        if ( !first_detail_line ) first_detail_line = i;
+        counter = counter + 1;
+      }
+   }
+   if ( !first_detail_line )
+   {
+      write( bs( "Es wurden im File "+actual_file+" keine "+
+         "Details gefunden! Um Details hinzufuegen zu koennen, "+
+         "muss mindestens ein AddDetail bereits vorhanden sein!" ) );
+      write( "-----------------------------------------------------------------------------\n" );
+      return 1;
+   }
+   write( "Details found:    "+counter+".\n" );
+   write( "-----------------------------------------------------------------------------\n" ); 
+   write( "Bitte die Detail-Kuerzel eingeben:\n(Bemerkung: "+
+          "die Kuerzel muessen mit Leerzeichen getrennt sein, "+
+          "Abbruch mit 'q'.)\n->" );
+   input_to( "get_detail_short" );
+   return 1;
+}    
+ 
+ 
+int get_detail_short( string str )
+{
+   string dummy;
+   if ( str == "q" )
+   {
+     tell_object( this_player(), "Abgebrochen!\n" );
+     return 1;
+   }
+   detail_shorts = map( old_explode( str, " " ),#'lower_case);
+   dummy = break_string(implode(sort_array(map(detail_shorts,#'lower_case),#'>),", ")+":",78);
+   write( "-----------------------------------------------------------------------------\n" );
+   write( bs ( "Bitte Beschreibung eingeben fuer das Detail mit den Namen \n" ) );
+   write( dummy+"( Abbruch mit 'q'.)\n->" );
+   input_to( "get_detail_long" );
+   return 1;
+}
+ 
+ 
+int get_detail_long( string str )
+{
+   if ( str == "q" )
+   {
+     write( "Abgebrochen!\n" );
+     write( "-----------------------------------------------------------------------------\n" );
+     return 1;
+   }
+   detail_long = break_string( "break_string( \""+str, 60 ); 
+   save_string = "  AddDetail( ({ ";        
+   for( i = 0; i < sizeof( detail_shorts ); i ++ )
+   {
+      if ( !(i%3) && i )
+        save_string = save_string + "\n                ";
+      if ( i < sizeof( detail_shorts ) - 1 ) 
+         detail_shorts[ i ] = "\""+detail_shorts[ i ]+"\", ";
+      else
+         detail_shorts[ i ] = "\""+detail_shorts[ i ]+"\" ";
+      save_string = save_string + detail_shorts[ i ];      
+      write( detail_shorts[ i ]+"\n" );
+   }
+   save_string = save_string + "}), \n";
+   newstring = old_explode( detail_long,"\n");
+   i = 0; 
+   for( i = 0; i<sizeof(newstring); i++) 
+   {
+      if ( i < sizeof(newstring)-1 )
+      {
+         if ( i==0||!i )
+          save_string = save_string +"                "+newstring[i]+" \"+\n";
+         else
+          save_string = save_string +"                \""+ newstring[i]+" \"+\n";
+ 
+      }
+      else
+      {
+         if ( i==0||!i )
+          save_string = save_string +"                "+ newstring[i]+"\", 78 ) );";
+         else
+          save_string = save_string +"                \""+ newstring[i]+"\", 78 ) );";
+      }
+   }
+   new_lines += ({ "dummy" });
+   for ( i = 1; i < first_detail_line; i++ )
+   {
+     new_lines += ({ file_lines[i] });
+   }                                  
+   new_lines += ({ save_string });
+   for ( i = first_detail_line + 1; i <= endline+1; i++ )
+     new_lines += ({ file_lines[ i - 1 ] });
+   write( "-----------------------------------------------------------------------------\n" );
+   write( bs( "Folgende Zeilen werden in die Datei "+actual_file+" geschrieben, falls `j` eingegeben wird:\n" ) );
+   write( save_string+"\n" );
+   write( "\nDetail speichern? (j/n)\n->" );
+   input_to( "save_detail" );
+   return 1;
+}
+ 
+ 
+int save_detail( string str )
+{  
+   if ( str != "j" && str != "ja" && str != "Ja" && str != "J" )
+   {
+     write( "Abgebrochen!\n" );
+     write( "-----------------------------------------------------------------------------\n" );
+     return 1;
+   }   
+   if ( !rm( actual_file ) )
+   {
+      write( "File: "+actual_file+" wurde nicht gefunden!\n" );
+      return 1;
+   }                   
+   for ( i = 2; i <= endline+2; i++ )
+     write_file( actual_file, new_lines[i]+"\n" ); 
+     //write( new_lines[i]+"\n" ); 
+   write( "Das Detail wurde in die Datei geschrieben.\n" );
+   this_player()->command_me( "here make" );
+   write( "-----------------------------------------------------------------------------\n" );  
+   return 1;
+}
diff --git a/obj/tools/explorer.c b/obj/tools/explorer.c
new file mode 100644
index 0000000..72da8ac
--- /dev/null
+++ b/obj/tools/explorer.c
@@ -0,0 +1,445 @@
+// MorgenGrauen MUDlib
+//
+// explorer.c -- Tool zur FP-Verwaltung
+//
+// $Id: explorer.c 8357 2013-02-09 11:16:14Z Zesstra $
+
+inherit "/std/secure_thing";
+
+#include <properties.h>
+#include <exploration.h>
+#include <wizlevels.h>
+
+create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Der Erforscher");
+  SetProp(P_NAME, "Erforscher");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_LONG, "Dies ist der beruehmte Erforscher des MorgenGrauens. Er\n"
+	  +"stellt folgende Befehle zur Verfuegung:\n"
+	  +"  epadd <was>: <bonus> <typ> <keys>\n"
+	  +"  epchange <was>: <wie> <neu>\n"
+	  +"  epdel <was>\n"
+	  +"  epinfo <was>\n"
+	  +"  epcount <spielername>                   - Anzahl der EPs des Spielers\n"
+	  +"  epscan <spielername> [filenamenpattern] - Zeige dessen EPs gefiltert an\n"
+	  +"  eppladd <spielername> <ep-anzahl>       - Gib ihm ep-anzahl EPs (zufaellig)\n"
+	  +"  eppldel <spielername> <ep-anzahl>       - Loesche ihm ep-anzahl EPs (zufael.)\n"
+	  +"  epplset <spielername> <ep-nummer>       - Setze ihm EP ep-nummer\n"
+	  +"  epplclr <spielername> <ep-nummer>       - Loesche ihm EP ep-nummer\n"
+	  +"Dabei bedeutet:\n"
+	  +"<was>   - Das Objekt (eine ID bei Objekten in der Naehe, hier oder\n"
+	  +"          here fuer den aktuellen Raum, oder ein Dateiname)\n"
+	  +"<bonus> - n fuer normale EPs, b fuer Bonus-EPs (z.B. Para-EPs)\n"
+	  +"<typ>   - detail, rdetail, exit/ausgang, cmd/befehl/kommando, info, pub, misc,\n"
+	  +"          smell/geruch, sound/noise, taste/touch\n"
+	  +"<key>   - Liste der Schluesselwoerter, mit Kommata getrennt\n"
+	  +"<wie>   - obj, key, bonus oder typ 1\n"
+	  +"<neu>   - Je nach <wie>; siehe <was>, <key>, <typ>\n"
+	  );
+  SetProp(P_NODROP, 1);
+  SetProp(P_NEVERDROP, 1);
+  SetProp(P_AUTOLOADOBJ,1);
+  AddId(({"explorer","erforscher"}));
+
+  AddCmd(({"epadd"}), "add");
+  AddCmd(({"epchange"}), "change");
+  AddCmd(({"epdel"}), "del");
+  AddCmd(({"epinfo"}), "info");
+  AddCmd(({"epcount"}), "epcount");
+  AddCmd(({"epscan"}), "epscan");
+  AddCmd(({"eppladd"}), "eppladd");
+  AddCmd(({"eppldel"}), "eppldel");
+  AddCmd(({"epplset"}), "epplset");
+  AddCmd(({"epplclr"}), "epplclr");
+}
+
+static string strArr(string *s)
+{
+  string ret;
+  int i;
+
+  ret = ("\"" + s[<1] + "\"");
+  for (i=sizeof(s)-2; i>=0; i--)
+    ret += (", \""+s[i]+"\"");
+
+  return ret;
+}
+
+static int getType(string s)
+{
+  switch(s[0..2]) {
+  case "det":
+    return EP_DETAIL;
+  case "rde":
+    return EP_RDET;
+  case "aus":
+  case "exi":
+    return EP_EXIT;
+  case "cmd":
+  case "bef":
+  case "kom":
+    return EP_CMD;
+  case "inf":
+    return EP_INFO;
+  case "pub":
+    return EP_PUB;
+  case "mis":
+  case "ver":
+    return EP_MISC;
+  case "sme":
+  case "ger":
+    return EP_SMELL;
+  case "sou":
+  case "noi":
+    return EP_SOUND;
+  case "tas":
+  case "tou":
+    return EP_TOUCH;
+  }
+  return -1;
+}
+
+static object getOb(string str)
+{
+  object ob;
+
+  if (str == "hier" || str == "here" )
+    return environment(this_player());
+
+  ob = present(str, environment(this_player()));
+  if (!ob)
+    ob = present(str, this_player());
+
+  if (!ob) {
+    str = "/secure/master"->_get_path(str, getuid(this_player()));
+    catch(call_other(str, "???"));
+    ob = find_object(str);
+  }
+  return ob;
+}
+
+static string *getKeys(string str)
+{
+  int i;
+  string *t1, *t2;
+
+  t1 = regexplode(str, ", *");
+  for (t2 = ({}), i=sizeof(t1)-1; i>=0; i-=2)
+    t2 = ({ t1[i] }) + t2;
+  return t2;
+}
+
+static string errMsg(int code)
+{
+  if (code >= 0 || code < EPERR_INVALID_ARG)
+    return "Unbekannter Fehler";
+
+  code = -(code+1);
+  return ({ "Du bist kein Erzmagier", "Ungueltiges Objekt",
+	    "Objekt steht nicht in der Liste", "Ungueltiges Argument"})[code];
+}
+
+static int add(string str)
+{
+  string was, t, k, *keys, b;
+  int type, bonus;
+  object ob;
+
+  if( !ARCH_SECURITY ) {
+    notify_fail("Du bist kein Erzmagier!\n");
+    return 0;
+  }
+  notify_fail("Syntax: epadd <was>: <typ>\n");
+  if (!(str = this_player()->_unparsed_args()))
+    return 0;
+
+  if (sscanf(str, "%s: %s %s %s", was, b, t, k) !=4)
+    return 0;
+
+  if (!(ob = getOb(was))) {
+    printf("Kann '%s' nicht finden!\n", was);
+    return 1;
+  }
+  if ((type = getType(t)) < 0) {
+    write("Ungueltiger Typ!\n");
+    return 1;
+  }
+  if (b=="n") bonus=0; else if (b=="b") bonus=1;
+  else {
+    write("Ungueltige Bonusart!\n");
+    return 1;
+  }
+  keys = getKeys(k);
+
+  type = EPMASTER->AddEPObject(ob, keys, type, bonus);
+  if (type < 0)
+    printf("Fehler: %s\n", errMsg(type));
+  return 1;
+}
+
+static int change(string str)
+{
+  string was, wie, neu;
+  object ob;
+  int type;
+  mixed new;
+
+  if( !ARCH_SECURITY ) {
+    notify_fail("Du bist kein Erzmagier!\n");
+    return 0;
+  }
+
+  notify_fail("Syntax: epchange <was>: <wie> <neu>\n");
+  if (!(str = this_player()->_unparsed_args()))
+    return 0;
+
+  if (sscanf(str, "%s: %s %s", was, wie, neu) != 3)
+    return 0;
+
+  if (!(ob = getOb(was))) {
+    printf( "Kann '%s' nicht finden!\n", was);
+    return 1;
+  }
+  switch(wie[0..2]) {
+  case "obj":
+    type = CHANGE_OB;
+    new = getOb(neu);
+    if (!new) {
+      printf( "Kann '%s' nicht finden!\n", neu);
+      return 1;
+    }
+    break;
+  case "key":
+    type = CHANGE_KEY;
+    new = getKeys(neu);
+    break;
+  case "typ":
+    type = CHANGE_TYPE;
+    if ((new = getType(neu)) < 0) {
+      write("Ungueltiger Typ!\n");
+      return 1;
+    }
+    break;
+  case "bon":
+    type = CHANGE_BONUS;
+    if (neu=="n") new=0; else if (neu=="b") new=1;
+    else {
+      write("Ungueltige Bonusart!\n");
+      return 1;
+    }
+    break;
+  default:
+    write("Das laesst sich nicht aendern...\n");
+    return 1;
+    break;
+  }
+  type = EPMASTER->ChangeEPObject( ob, type, new );
+  if (type < 0)
+    printf("Fehler: %s\n",errMsg(type));
+
+  return 1;
+}
+
+static int del(string str)
+{
+  object ob;
+  int ret;
+
+  if( !ARCH_SECURITY ) {
+    notify_fail("Du bist kein Erzmagier!\n");
+    return 0;
+  }
+  if (!str) {
+    notify_fail("Syntax: epdel <was>\n");
+    return 0;
+  }
+  if (!(ob = getOb(str))) {
+    write("Kann das Objekt nicht finden!\n");
+    return 1;
+  }
+  ret = EPMASTER->RemoveEPObject(ob);
+  if (ret < 0)
+    printf("Fehler: %s\n", errMsg(ret));
+
+  return 1;
+}
+
+static int info(string str)
+{
+  object ob;
+  mixed info;
+
+  if( !ARCH_SECURITY ) {
+    notify_fail("Du bist kein Erzmagier!\n");
+    return 0;
+  }
+
+  if (!str) {
+    notify_fail("Syntax: epinfo <was>\n");
+    return 0;
+  }
+
+  if (!(ob = getOb(str))) {
+    write("Das finde ich leider nicht...\n");
+    return 1;
+  }
+  if (!(info = EPMASTER->QueryEPObject(ob)))
+    write ("Das Objekt ist nicht eingetragen!\n");
+  else
+    printf("Nummer: %d\nBonus: %d\nTyp: %s\nSchluessel: %s\n",
+	   info[MPOS_NUM],
+	   info[MPOS_TYPE+1],
+	   ({ "Detail", "Ausgang", "Kommando", "Info", "Misc", "ReadDetail",
+         "Kneipe", "Geruch", "Geraeusch", "Tastdetail"})[info[MPOS_TYPE]],
+	   strArr(info[MPOS_KEY] ));
+
+  return 1;
+}
+
+static object find_playerob( string name ) {
+  return find_player(name)||find_netdead(name);
+}
+
+static int epcount( string str ) {
+  object pl,epm;
+  
+  if( !ARCH_SECURITY ) {
+    notify_fail("Du bist kein Erzmagier!\n");
+    return 0;
+  }
+  
+  if (!str || str=="") {
+    notify_fail("Syntax: epcount <spielername>\n");
+    return 0;
+  }
+
+  printf( "%s hat %d von %d FPs. (Durchschnitt = %d)\n",
+	  capitalize(str),
+	  EPMASTER->QueryExplorationPoints(str),
+	  EPMASTER->QueryMaxEP(),
+	  EPMASTER->QueryAverage()
+	  );
+  return 1;
+}
+
+static int epscan( string str ) {
+  object pl;
+  int erg;
+  string *astr;
+
+  if( !ARCH_SECURITY ) {
+    notify_fail("Du bist kein Erzmagier!\n");
+    return 0;
+  }
+  
+  if (!str || str=="") {
+    notify_fail("Syntax: epscan <spielername> [Teil des Filenamens]\n");
+    return 0;
+  }
+
+  astr=old_explode(this_player()->_unparsed_args()," ");
+  
+  if (sizeof(astr)<2)
+    erg = EPMASTER->ShowPlayerEPs(astr[0]);
+  else
+    erg = EPMASTER->ShowPlayerEPs(astr[0],astr[1]);
+  if (erg < 0)
+    printf("Fehler: %s\n",errMsg(erg));
+  return 1;
+}
+
+static eppldel( string str ) {
+	string player, rest;
+	int epnum;
+	object tmp;
+
+	if( !ARCH_SECURITY ) {
+	  notify_fail("Du bist kein Erzmagier!\n");
+	  return 0;
+	}
+
+	notify_fail( "eppldel <spieler> <anzahl_eps> <grund>\n" );
+	if( !str || sscanf( str, "%s %d %s", player, epnum, rest )<2 )
+		return 0;
+
+	if( epnum <= 0 ) {
+		write( "Anzahl der FPs <= 0 ?\n" );
+		return 1;
+	}
+	if( !rest || rest=="" ) {
+		write( "Bitte Grund angeben!\n" );
+		return 1;
+	}
+
+	printf( "%O %O %O\n", epnum, player, secure_level() );
+	epnum =	"/secure/explorationmaster"->RemoveFP( epnum, player, rest );
+	printf( "Ergebnis (abgezogene EPs) = %O\n", epnum );
+	return 1;
+
+}
+
+static eppladd( string str ) {
+	string player, rest;
+	int epnum;
+	object pl,tmp;
+
+	if( !ARCH_SECURITY ) {
+	  notify_fail("Du bist kein Erzmagier!\n");
+	  return 0;
+	}
+
+	notify_fail( "eppladd <spieler> <anzahl_eps>\n" );
+	if( !str || sscanf( str, "%s %d", player, epnum )<2 )
+		return 0;
+
+	if( epnum <= 0 ) {
+		write( "Anzahl der FPs <= 0 ?\n" );
+		return 1;
+	}
+
+	epnum =	"/secure/explorationmaster"->AddFP( epnum, player );
+	printf( "Ergebnis (hinzugefuegte FPs) = %O\n", epnum );
+	return 1;
+
+}
+
+static epplset( string str ) {
+  string player, rest;
+  int epnum;
+  object pl,tmp;
+  
+  if( !ARCH_SECURITY ) {
+    notify_fail("Du bist kein Erzmagier!\n");
+    return 0;
+  }
+  
+  notify_fail( "epplset <spieler> <ep-nummer>\n" );
+  if( !str || sscanf( str, "%s %d", player, epnum )<2 )
+    return 0;
+  
+  epnum = "/secure/explorationmaster"->SetFP( epnum, player );
+  printf( "Ergebnis (gesetzter FP) = %O\n", epnum );
+  return 1;
+}
+
+static epplclr( string str ) {
+  string player, rest;
+  int epnum;
+  object pl,tmp;
+  
+  if( !ARCH_SECURITY ) {
+    notify_fail("Du bist kein Erzmagier!\n");
+    return 0;
+  }
+  
+  notify_fail( "epplclr <spieler> <ep-nummer>\n" );
+  if( !str || sscanf( str, "%s %d", player, epnum )<2 )
+    return 0;
+  
+  epnum = "/secure/explorationmaster"->ClearFP( epnum, player );
+  printf( "Ergebnis (geloeschter FP) = %O\n", epnum );
+  return 1;
+}
diff --git a/obj/tools/fehlerteufel.c b/obj/tools/fehlerteufel.c
new file mode 100644
index 0000000..a35fec1
--- /dev/null
+++ b/obj/tools/fehlerteufel.c
@@ -0,0 +1,993 @@
+/* /obj/toostels/fehlerteufel.c
+   Fehlerteufel - Magiertool zur Abfrage des Fehler-Daemons der Mudlib
+   Autor: Zesstra
+   Changelog:
+*/
+
+#pragma strict_types
+#pragma pedantic
+#pragma range_check
+#pragma no_shadow
+#pragma no_inherit
+
+inherit "/std/secure_thing";
+inherit "/secure/errord-structs";
+
+#include <defines.h>
+#include <wizlevels.h>
+#include <properties.h>
+#include <moving.h>
+#include <errord.h>
+#include <config.h>
+#include <debug_info.h>
+#include <input_to.h>
+#include <strings.h>
+
+#define TI this_interactive()
+
+#define DEBUG(x)  if (funcall(symbol_function('find_player),"zesstra"))\
+        tell_object(funcall(symbol_function('find_player),"zesstra"),\
+        "EDBG: "+x+"\n")
+
+// variables
+private string owner;  // UID vom Eigentuemer
+private string *uids=({}); // UIDs, auf die man schreibrechte hat
+private string *filteruids=({}); // wenn nicht-leer, nur diese UIDs zeigen
+private string *monitoruids=({}); // diese UIDs in die Liste einschliessen
+private int filterstatus=0;       // Filter an oder aus?
+private mapping issuelist = ([]);
+/* ([ type1: ([uid1: <issuelist>, uid2: <issuelist> ]),
+      type2: ... ])
+   <issuelist> ist ein < <int|string>* >* vom ErrorD */
+
+//Welche Art von Fehler anzeigen?
+private int modus=T_RTERROR | T_RTWARN | T_REPORTED_ERR;
+private string *xmonitoruids = ({}); // expanded Monitor-UIDs
+private int lfehler;              // letzter 'benutzter' Fehler.
+private int fehlerzahl;       // fehlerzahl im letzten Reset.
+private mapping feingabe;         // gerade eingegebener Fehler
+
+// ************** private functions  **************
+private varargs int get_issuelist(int lmodus);
+private void get_uids();
+
+// irgendeinen Fehler oder Warnung anzeigen.
+private int show_entry(struct fullissue_s issue)
+{
+  if (!issue) return 0;
+
+  string txt=ERRORD->format_error(issue, 0);
+
+  if (!stringp(txt) || !sizeof(txt))
+      return 0;
+
+  tell_object(PL,txt+"\n");
+  return 1;
+}
+
+protected int _feingabe_fertig(string arg) {
+
+  if (arg == "j")
+  {
+    if (!feingabe[F_MSG])
+    {
+      tell_object(PL,
+        "Also, eine Fehlerbeschreibung solltest Du schon eingeben! Abgebrochen.\n");
+    }
+    else
+    {
+      string hashkey = (string)ERRORD->LogReportedError(feingabe);
+      tell_object(PL, sprintf(
+         "Vielen Dank! Die ID des eingegebenen Fehlers lautet: %s\n",
+         hashkey || "N/A"));
+    }
+  }
+  else
+  {
+    tell_object(PL, "Fehlereingabe abgebrochen.\n");
+  }
+
+  feingabe = 0;
+
+  return 1;
+}
+
+public int CmdFehlerEingabe(string arg) {
+  object target;
+
+  if (feingabe)
+  {
+    tell_object(PL, "Du gibts doch bereits einen Fehler ein!\n");
+    return 1;
+  }
+  feingabe = ([]);
+
+  if (arg)
+  {
+    target = present(arg);
+    if (!target)
+      target = find_object(arg); // vielleicht direkt angegeben?
+  }
+  // wenn nix gefunden, Environment des Magiers nehmen.
+  if (!target)
+    target = environment(environment());
+
+  feingabe[F_OBJ] = target;
+
+  tell_object(PL, break_string(sprintf(
+          "Du beginnst einen Fehlereintrag fuer das Objekt: %O\n", target),78));
+
+  input_to(function void (string str)
+      {
+        if (sizeof(str)) feingabe[F_MSG] = str;
+      }, INPUT_PROMPT, "Fehlerbeschreibung eingeben:\n");
+
+  input_to(function void (string str)
+      {
+        if (sizeof(str)) feingabe[F_PROG] = str;
+        else feingabe[F_PROG] = "unbekannt";
+      }, INPUT_PROMPT|INPUT_APPEND, 
+     "Programm eingeben, in dem der Fehler auftritt (falls bekannt):\n");
+
+  input_to(function void (string str)
+      {
+        if (sizeof(str)) feingabe[F_LINE] = to_int(str);
+        tell_object(PL,break_string(sprintf(
+          "Du hast die folgenden Daten eingegeben:\n"
+          "Objekt: %O\n"
+          "Fehlerbeschreibung: %s\n"
+          "Programm: %s\n"
+          "Zeilennr.: %d\n",
+          feingabe[F_OBJ], feingabe[F_MSG] || "", feingabe[F_PROG],
+          feingabe[F_LINE]), 78, "", BS_LEAVE_MY_LFS));
+      }, INPUT_PROMPT|INPUT_APPEND,
+      "Zeilennr. eingeben, in der der Fehler auftritt (falls bekannt):\n");
+
+  input_to(#'_feingabe_fertig,
+      INPUT_PROMPT|INPUT_APPEND, "Eingaben korrekt? (j/n)\n");
+
+  return 1;
+}
+
+public int CmdFehlerZeigen(string arg)
+{
+  int issueid;
+
+  if (stringp(arg) && sizeof(arg))
+  {
+      arg = trim(arg, TRIM_BOTH);
+      issueid = to_int(arg);
+  }
+  else
+  {
+      issueid = lfehler;
+      arg = to_string(issueid);
+  }
+  notify_fail("Einen Eintrag mit dieser ID gibt es nicht!\n");
+
+  // Mit einem / am Anfang ist arg wohl ein Filename, wenn to_string(issueid)
+  // == arg wird die Issueid von oben genommen.
+  struct fullissue_s issue;
+  struct fullissue_s *issues;
+  if (arg[0] == '/')
+  {
+    issues=({});
+    foreach(int m: ALL_ERR_TYPES)
+    {
+      if (!(m & modus))
+        continue;
+      struct fullissue_s *tmp = 
+                       (struct fullissue_s *)ERRORD->QueryIssuesByFile(arg, m);
+      if (tmp)
+        issues+=tmp;
+    }
+    if (!sizeof(issues))
+      issues=0;
+  }
+  else if (to_string(issueid) == arg)
+    issue = (struct fullissue_s)ERRORD->QueryIssueByID(issueid);
+  else
+    issue = (struct fullissue_s)ERRORD->QueryIssueByHash(arg);
+
+  if (structp(issue))
+  {
+    show_entry(issue);
+    // letzten Fehler merken.
+    lfehler = issueid;
+    return 1;
+  }
+  // Wenn das nicht erfolgreich ist, ist das Argument evtl. ein Objekt-,
+  // Programm- oder Ladename. In dem Fall alle von denen anzeigen, die passen
+  // hierbei wird der Typ NICHT beruecksichtigt.
+  else if (pointerp(issues))
+  {
+    foreach(issue : issues)
+    {
+      show_entry(issue);
+    }
+    return 1;
+  }
+
+  notify_fail("Keine Eintraege fuer diesen Dateinamen/diese ID gefunden.\n");
+  return 0;
+}
+
+// Loescht alle Fehler und Warnungen eines Objekts (soweit per modus
+// ausgewaehlt). Entscheidend ist der _Loadname_!
+private int DeleteErrorsForLoadname(string loadname)
+{
+  int sum_deleted;
+  // Bei == 0 wird sonst alles geloescht. ;-)
+  if (!loadname)
+    return 0;
+
+  foreach(int m: ALL_ERR_TYPES)
+  {
+    if (!(m & modus))
+      continue;
+    < <int|string>* >* list = ERRORD->QueryIssueListByLoadname(loadname,m);
+    if (pointerp(list))
+    {
+      foreach(<int|string>* row : list)
+      {
+          if ((int)ERRORD->ToggleDeleteError(row[0]) == 1)
+          {
+            tell_object(PL,
+                row[0] + " als geloescht markiert.\n");
+          }
+      }
+      sum_deleted+=sizeof(list);
+    }
+  }
+  return sum_deleted;
+}
+
+public int CmdFehlerLoeschen(string arg)
+{
+  int issueid;
+  arg = (string)this_player()->_unparsed_args(0);
+
+  if (stringp(arg) && sizeof(arg))
+      issueid = to_int(arg);
+  else
+      issueid = lfehler;
+
+  notify_fail("Einen Eintrag mit dieser ID/diesem Loadname gibt es nicht!\n");
+
+  int res = (int)ERRORD->ToggleDeleteError(issueid);
+  if (res == 1)
+  {
+    tell_object(PL,
+      "Fehler/Warnung wurde zum Loeschen markiert und wird in Kuerze "
+      "geloescht.\n");
+    lfehler = issueid;
+    return 1;
+  }
+  else if (res==0)
+  {
+    tell_object(PL,"Loeschmarkierung wurde entfernt.\n");
+    lfehler = issueid;
+    return 1;
+  }
+  else if (res < -1)
+  {
+    tell_object(PL, "Irgendwas ist beim Loeschen schiefgegangen. "
+                    "Keine Schreibrechte?\n");
+    lfehler = issueid;
+    return 1;
+  }
+  // res war == -1 -> Fehler nicht gefunden. Vielleicht ist es nen Loadname
+  return DeleteErrorsForLoadname(arg);
+}
+
+public int CmdRefresh(string arg) {
+    reset();
+    tell_object(PL,"Fehlerdaten wurden neu eingelesen.\n");
+    return 1;
+}
+
+private int select_modus(string arg) {
+    int lmodus;
+    switch(arg) {
+    case "alles":
+    case "alle":
+      lmodus = T_RTERROR | T_RTWARN | T_CTERROR | T_CTWARN | T_REPORTED_ERR
+               | T_REPORTED_IDEA | T_REPORTED_TYPO | T_REPORTED_MD;
+      break;
+    case "fehler":
+    case "error":
+    case "errors":
+      lmodus=T_RTERROR | T_CTERROR | T_REPORTED_ERR;
+      break;
+    case "warnungen":
+    case "warnung":
+    case "warning":
+    case "warnings":
+      lmodus=T_RTWARN | T_CTWARN;
+      break;
+    case "laufzeitfehler":
+      lmodus=T_RTERROR;
+      break;
+    case "ladezeitfehler":
+      lmodus=T_CTERROR;
+      break;
+    case "fehlerhinweis":
+    case "fehlerhinweise":
+    case "hinweise":
+      lmodus=T_REPORTED_ERR;
+      break;
+    case "ideen":
+    case "idee":
+      lmodus=T_REPORTED_IDEA;
+      break;
+    case "md":
+      lmodus=T_REPORTED_MD;
+      break;
+    case "typo":
+    case "typos":
+      lmodus=T_REPORTED_TYPO;
+      break;
+    case "laufzeitwarnungen":
+    case "runtimewarnings":
+      lmodus=T_RTWARN;
+      break;
+    case "ladezeitwarnungen":
+    case "compiletimewarnings":
+      lmodus=T_CTWARN;
+      break;
+    default:
+      lmodus=modus;
+    }
+    return lmodus;
+}
+
+private string  * errorlabel(int t)
+{
+  switch(t) {
+    case T_RTERROR:
+      return ({"Laufzeitfehler","Laufzeitfehler","Dieser Laufzeitfehler"});
+    case T_REPORTED_ERR:
+      return ({"Fehlerhinweis","Fehlerhinweise","Dieser Fehlerhinweis"});
+    case T_REPORTED_IDEA:
+      return ({"Idee","Ideen","Diese Idee"});
+    case T_REPORTED_MD:
+      return ({"fehlende Detail","fehlende Details","Dieses fehlende Detail"});
+    case T_REPORTED_TYPO:
+      return ({"Typo","Typos","Dieser Typo"});
+    case T_RTWARN:
+      return ({"Laufzeitwarnung","Laufzeitwarnungen","Diese Laufzeitwarnung"});
+    case T_CTWARN:
+      return ({"Ladezeitwarnung", "Ladezeitwarnungen","Diese Ladezeitwarnung"});
+    case T_CTERROR:
+      return ({"Ladezeitfehler","Ladezeitfehler","Dieser Ladezeitfehler"});
+  }
+  raise_error("Unkannter Fehlertyp: "+t+"\n");
+  return 0;
+}
+
+public int CmdFehlerListe(string arg) {
+  string txt;
+  //string *luids;
+  int lmodus; // modus fuer diese Liste
+  mapping fehlerbackup;
+
+  if (stringp(arg) && sizeof(arg))
+  {
+    lmodus=select_modus(arg);
+    if (lmodus != modus)
+    {
+      fehlerbackup=issuelist; // Fehlerliste von 'modus' aufheben
+      get_issuelist(lmodus);  // neue Fehlerliste holen
+    }
+  }
+  else
+    lmodus=modus;
+/*
+  if (!fehlerzahl)
+  {
+    txt="Fuer Deine UIDs sind keine Fehler/Warnungen bekannt. :-)\n";
+    tell_object(PL,txt);
+    return 1;
+  }
+*/
+  foreach(int typ, mapping typemap : issuelist)
+  {
+    if (!(typ & lmodus))
+      continue; // Type nicht gewaehlt.
+    txt="";
+    if (!sizeof(typemap)) {
+      tell_object(PL,
+        "Es sind keine " + errorlabel(typ)[1] + "Deiner UIDs bekannt. :-)");
+      continue;
+    }
+    foreach(string uid, < <int|string>* >* list : typemap)
+    {
+      if (!sizeof(list)) continue;
+      if (filterstatus && member(filteruids, uid) > -1) continue;
+      //txt+=sprintf("%s:\n", uid);
+      foreach(<int|string>* row : list)
+      {
+        txt+=sprintf("%:6d | %:40-s | %:26-s\n",
+              row[0], row[1], row[2]);
+      }
+    }
+    if (txt && sizeof(txt))
+    {
+      txt = sprintf("\nFuer Deine UIDs sind folgende %s bekannt (Filter: %s):\n"
+                    "%:6|s | %:40|s |   %s\n",
+                 errorlabel(typ)[1], (filterstatus ? "an" : "aus"),
+                 "ID", "Loadname", "UID")
+          + txt;
+      tell_object(PL, txt);
+    }
+    else
+    {
+      tell_object(PL, sprintf(
+            "\nFuer Deine UIDs sind keine %s bekannt (Filter: %s):\n",
+            errorlabel(typ)[1], (filterstatus ? "an" : "aus")));
+    }
+  }
+
+  if (mappingp(fehlerbackup) && modus!=lmodus)
+    issuelist=fehlerbackup; // fehlerliste fuer 'modus' restaurieren
+  return 1;
+}
+
+public int CmdFilter(string arg) {
+
+  arg=(string)PL->_unparsed_args(0);  
+
+  if (!stringp(arg) || !sizeof(arg)) {
+    tell_object(PL,break_string(
+          "Momentan interessieren Dich folgende UIDs nicht"
+          +(filterstatus ? " (Filter an):\n" : " (Filter aus):\n")
+          +CountUp(filteruids)+"\n", 78,"",BS_LEAVE_MY_LFS));
+    return 1;
+  }
+
+  if (arg=="keine") {
+    filteruids=({});
+    filterstatus=1;
+    tell_object(PL,break_string(
+        "Dein Fehlerteufel wird Dir nun nur noch ausgewaehlte "
+        "Fehler berichten. Momentan hast Du keine UIDs ausgeblendet. "
+        "(Filter an)",78));
+  }
+  else if (arg=="alle") {
+    filterstatus=1;
+    filteruids=uids;
+    tell_object(PL,break_string(
+        "Dein Fehlerteufel wird Dir nun nur noch ausgewaehlte "
+        "Fehler berichten. Du blendest momentan alle UIDs aus. "
+        "(Filter an)",78));
+  }
+  else if (arg=="aus") {
+    filterstatus=0;
+    tell_object(PL,break_string(
+        "Dein Fehlerteufel wird Dir nun wieder alle Fehler berichten. ",
+        78));
+  }
+  else if (arg=="an" || arg=="ein") {
+    filterstatus=1;
+    tell_object(PL,break_string(
+        "Dein Fehlerteufel wird Dir nun nur noch ausgewaehlte "
+        "Fehler berichten.",78));
+  }
+  else {
+    foreach(string uid: explode(arg," ")-({""})) {
+      if (sizeof(uid)>1 && uid[0]=='+') {
+        if (member(filteruids,uid[1..])==-1)
+          filteruids+=({uid[1..]});
+      }
+      else if (sizeof(uid)>1 && uid[0]=='-') {
+          filteruids-=({uid[1..]});
+      }
+      else {
+        if (member(filteruids,uid)==-1)
+          filteruids+=({uid});
+        else
+          filteruids-=({uid});
+      }
+    }
+  }
+
+  tell_object(PL,break_string(
+          "Momentan interessieren Dich folgende UIDs nicht"
+          +(filterstatus ? " (Filter an):\n" : " (Filter aus):\n")
+          +CountUp(filteruids)+"\n", 78,"",BS_LEAVE_MY_LFS));
+
+  return 1;
+}
+
+public int CmdMonitor(string arg) {
+
+  arg=(string)PL->_unparsed_args(0);  
+
+  if (!stringp(arg) || !sizeof(arg)) {
+    tell_object(PL,break_string(
+          "Momentan interessieren Dich folgende UIDs zusaetzlich zu Deinen: \n"
+          +(sizeof(monitoruids) ? CountUp(monitoruids) : "")
+          +"\n", 78,"",BS_LEAVE_MY_LFS));
+    return 1;
+  }
+
+  if (arg=="keine") {
+    monitoruids=({});
+    xmonitoruids=({});
+    tell_object(PL,break_string(
+        "Dein Fehlerteufel wird Dir nun nur noch "
+        "Fehler Deiner eigenen UIDs berichten.",78));
+    return 1;
+  }
+  else {
+    foreach(string uid: explode(arg," ")-({""})) {
+      if (sizeof(uid)>1 && uid[0]=='+') {
+        if (member(monitoruids,uid[1..])==-1)
+          monitoruids+=({uid[1..]});
+      }
+      else if (sizeof(uid)>1 && uid[0]=='-') {
+          monitoruids-=({uid[1..]});
+      }
+      else {
+        if (member(monitoruids,uid)==-1)
+          monitoruids+=({uid});
+        else
+          monitoruids-=({uid});
+      }
+    }
+  }
+  get_uids();
+  tell_object(PL,break_string(
+        "Momentan interessieren Dich folgende UIDs zusaetzlich zu Deinen: \n"
+        +(sizeof(monitoruids) ? CountUp(monitoruids) : "")
+        +"\n", 78,"",BS_LEAVE_MY_LFS));
+
+  return 1;
+}
+
+public int CmdModus(string arg) {
+  string txt;
+
+  // Argument verwursten
+  if (stringp(arg) && sizeof(arg)) {
+    modus = select_modus(arg);
+    reset();  // neue Fehlerliste holen
+  }
+  // aktuelle Einstellung ausgeben.
+  string *modstr=({});
+  if (modus & T_RTERROR)
+      modstr+=({"Fehler (Laufzeit)"});
+  if (modus & T_RTWARN)
+      modstr+=({"Warnungen (Laufzeit)"});
+  if (modus & T_CTERROR)
+      modstr+=({"Fehler (Ladezeit)"});
+  if (modus & T_CTWARN)
+      modstr+=({"Warnungen (Ladezeit)"});
+  if (modus & T_REPORTED_ERR)
+      modstr+=({"Fehlerhinweise"});
+  if (modus & T_REPORTED_IDEA)
+      modstr+=({"Idee"});
+  if (modus & T_REPORTED_MD)
+      modstr+=({"fehlende Details"});
+  if (modus & T_REPORTED_TYPO)
+      modstr+=({"Typo"});
+
+  tell_object(PL, break_string(
+      "Dein Fehlerteufel wird Dir nun ueber aufgetretene "
+      +CountUp(modstr)+" Bericht erstatten.",78));
+  return(1);
+}
+
+int CmdAddNote(string str) {
+  string *arr;
+
+  notify_fail("Bitte eine ID und einen Text angeben!\n");
+  if(!objectp(TI))
+    return 0;
+
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return 0;
+
+  arr=explode(str," ")-({""});
+  if (sizeof(arr)<2)
+    return 0;
+  int issueid = to_int(arr[0]);
+
+  str=implode(arr[1..]," ");  //text wiederherstellen, aber ohne ID
+
+  switch((int)ERRORD->AddNote(issueid,str))
+  {
+    case -1:
+      tell_object(PL,
+          sprintf("Es gibt keinen Fehler mit der ID: %d\n",issueid));
+      return 1;
+    case -3:
+      return 0; //offenbar keine Notiz angegeben.
+  }
+  // letzten Fehler merken.
+  lfehler = issueid;
+
+  tell_object(PL,
+      sprintf("Deine Notiz wurde zu %d hinzugefuegt.\n",
+        issueid));
+  return 1;
+}
+
+int CmdFix(string str)
+{
+  string *arr;
+  int fixing, res;
+
+  notify_fail("Bitte eine ID und optional eine Notiz angeben!\n");
+  if(!objectp(TI))
+    return 0;
+
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return 0;
+
+  arr=explode(str," ")-({""});
+  if (!sizeof(arr))
+    return 0;
+
+  int issueid=to_int(arr[0]);
+  if (sizeof(arr)>1)
+    str=implode(arr[1..]," ");  //text wiederherstellen, aber ohne Key
+  else str=0;
+
+  if (query_verb()=="ffix" || query_verb()=="fehlerfix")
+  {
+      fixing=1;
+      res = (int)ERRORD->ResolveIssue(issueid, str);
+  }
+  else
+  {
+      res = (int)ERRORD->ReOpenIssue(issueid, str);
+  }
+
+  if (res==-1)
+  {
+      tell_object(PL,
+          sprintf("Es gibt keinen Fehler mit der ID: %d\n",issueid));
+  }
+  else if (res==-10)
+  {
+      tell_object(PL,
+        "Du hast leider keinen Schreibzugriff diesen Fehler.\n"
+        "Aber vielleicht moechtest Du mit fnotiz eine Notiz anhaengen?\n");
+  }
+  else if (res==-3)
+  {
+    if (fixing)
+      tell_object(PL,"Dieser Fehler ist bereits gefixt.\n");
+    else
+      tell_object(PL,"Dieser Fehler ist noch nicht gefixt.\n");
+  }
+  else if (res==1)
+  {
+      tell_object(PL,
+          sprintf("Fehler %d als gefixt markiert.\n",issueid));
+  }
+  else if (res==0)
+  {
+      tell_object(PL,
+          sprintf("Fehler %d als nicht gefixt markiert.\n",issueid));
+  }
+  // letzten Fehler merken.
+  lfehler = issueid;
+
+  return 1;
+}
+
+int CmdLock(string str) {
+  string *arr;
+  int locking;
+  int res;
+
+  notify_fail("Bitte eine ID und optional eine Notiz angeben!\n");
+  if(!objectp(TI))
+    return 0;
+
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return 0;
+
+  arr=explode(str," ")-({""});
+  if (!sizeof(arr))
+    return 0;
+
+  int issueid=to_int(arr[0]);
+  if (sizeof(arr)>1)
+    str=implode(arr[1..]," ");  //text wiederherstellen, aber ohne Key
+  else str=0;
+
+  if (query_verb()=="flock" || query_verb()=="fehlerlock")
+  {
+      locking=1;
+      res=(int)ERRORD->LockIssue(issueid,str);
+  }
+  else
+  {
+      res=(int)ERRORD->UnlockIssue(issueid,str);
+  }
+
+  if (res==-1)
+  {
+      tell_object(PL,
+          sprintf("Es gibt keinen Fehler mit der ID: %d\n",issueid));
+  }
+  else if (res==-10)
+  {
+       tell_object(PL,
+           "Du hast leider keinen Schreibzugriff diesen Fehler.\n");
+  }
+  else if (res==-3)
+  {
+      if (locking)
+          tell_object(PL,
+              "Dieser Fehler ist bereits vor autom. Loeschen geschuetzt.\n");
+      else
+          tell_object(PL,
+              "Dieser Fehler ist bereits zum autom. Loeschen freigegeben.\n");
+  }
+  else if (res==-2)
+  {
+       tell_object(PL,
+           "Dieser Fehler ist bereits gefixt und wird bald geloescht.\n");
+  }
+  else if (res==1)
+  {
+    tell_object(PL,
+        sprintf("Fehler %d vor autom. Loeschen geschuetzt.\n",issueid));
+  }
+  else if (res==0)
+  {
+    tell_object(PL,
+        sprintf("Fehler %d zum autom. Loeschen freigegeben.\n",issueid));
+  }
+  // letzten Fehler merken.
+  lfehler = issueid;
+
+  return 1;
+}
+
+int CmdReassign(string str) {
+
+  notify_fail("Bitte eine ID, die neue UID und ggf. eine Notiz angeben!\n");
+  if(!objectp(TI))
+    return 0;
+
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return 0;
+
+  string *arr=explode(str," ")-({""});
+  if (sizeof(arr)<2)
+    return 0;
+  int issueid=to_int(arr[0]);
+  string newuid=arr[1];
+
+  //text wiederherstellen, aber ohne Key und UID
+  if (sizeof(arr) > 2)
+    str = implode(arr[2..]," ");
+  else
+    str = 0;
+
+  switch((int)ERRORD->ReassignIssue(issueid, newuid, str))
+  {
+    case -1:
+      tell_object(PL,
+          sprintf("Es gibt keinen Fehler mit der ID: %d\n",issueid));
+      return(1);
+    case -10:
+      tell_object(PL,
+          sprintf("Du hast keine Schreibrechte auf Fehler %d\n",issueid));
+      return 1;
+    case -2:
+      return(0); //offenbar keine neue uid angegeben. (kann nicht)
+    case -3:
+      tell_object(PL,break_string(
+          "Alte == Neue UID ist irgendwie unsinnig...",78));
+      return 1;
+  }
+  // letzten Fehler merken.
+  lfehler = issueid;
+
+  tell_object(PL,break_string(
+      sprintf("Der Fehler der ID %d wurde an die UID %s "
+        "uebertragen.\n", issueid, newuid),78));
+  return 1;
+}
+
+// ************** public 'internal' functions **************
+public string QueryOwner() {return owner;}
+public mixed QueryIssueList() {return issuelist;}
+
+void create() {
+    if (!clonep(ME))
+        return;
+    ::create(); 
+
+    SetProp(P_SHORT,"Der Fehlerteufel");
+    SetProp(P_LONG,break_string(
+          "Dein Fehlerteufel soll Dir helfen, Informationen "
+          "ueber aufgetretene Fehler zu erhalten. Hierzu fragt er die "
+          "in \"Deinen\" UIDs aufgetretenen Fehler und deren Details vom "
+          "Fehlerspeicher der Mudlib ab. Folgende Kommandos kennt er:",78)
+    +"fehlerabfrage <id>  - Fragt Details ueber Fehler mit der ID ab.\n"
+    "fehlerloeschen <id> - Fehler zum Loeschen markieren.\n"
+    "fehlerliste         - Fehlerliste der eigenen UIDs anzeigen\n"
+    "fehlerrefresh       - Fehlerdaten und UIDs neu einlesen\n"
+    "fehlerfilter        - UIDs fuer den Filter angeben (s. manpage!)\n"
+    "fehlermodus         - Fehler oder Warnungen ausgeben? (s. manpage)\n"
+    "fehlermonitor       - zus. UIDs beobachten (s. manpage)\n"
+    "fnotiz <id> <note>  - eine Notiz anhaengen\n"
+    "flock <id>  <note>  - Fehler vor autom. Loeschen schuetzen\n"
+    "funlock <id> <note> - Fehler zum autom. Loeschen freigeben\n"
+    "ffix <id> <note>    - Fehler als gefixt kennzeichnen\n"
+    "funfix <id> <note>  - gefixten Fehler als nicht-gefixt markieren\n"
+    "fuebertrage <id> <newuid> <note>\n"
+    "                    - Fehler an die UID uebertragen\n"
+    );
+    SetProp(P_NAME,"Fehlerteufel");
+    SetProp(P_GENDER,MALE);
+    SetProp(P_WEIGHT,0);
+    SetProp(P_VALUE,0);
+    SetProp(P_SIZE,10);
+    SetProp(P_NODROP,"Den Fehlerteufel behaelst Du lieber bei Dir.\n");
+    SetProp(P_NEVERDROP,1);
+
+    AddId( ({"fehlerteufel","teufel"}) );
+
+    AddCmd(({"fehlerabfrage","fabfrage"}), "CmdFehlerZeigen" );
+    AddCmd(({"fehlerloeschen","floeschen"}), "CmdFehlerLoeschen");
+    AddCmd(({"fehlerliste","fliste", "fehleruebersicht","fuebersicht"}), 
+        "CmdFehlerListe");
+    AddCmd(({"fehlerrefresh","frefresh"}),"CmdRefresh");
+    AddCmd(({"fehlerfilter","ffilter"}),"CmdFilter");
+    AddCmd(({"fehlermodus","fmodus"}),"CmdModus");
+    AddCmd(({"fehlermonitor","fmonitor"}),"CmdMonitor");
+    AddCmd(({"fehlernotiz","fnotiz"}),"CmdAddNote");
+    AddCmd(({"fehlerlock","flock","fehlerunlock","funlock"}),
+        "CmdLock");
+    AddCmd(({"fehlerfix","ffix","fehlerunfix","funfix"}),
+        "CmdFix");
+    AddCmd(({"fehleruebertrage","fuebertrage"}),"CmdReassign");
+    AddCmd(({"fehlereingabe", "feingabe"}), "CmdFehlerEingabe");
+}
+
+void init()
+{
+    if (find_call_out("remove") != -1) return;
+
+    // pruefung auf env nicht noetig, move geht nur in ein env und ohne env
+    // auch kein init().
+    if ( !query_once_interactive(environment()) || 
+        !IS_LEARNER(environment())) {
+        // in interactive, aber kein magier -> direkt weg.
+        call_out("remove",0,1);
+        return;
+    }
+    else if (!sizeof(owner))
+        // Env ist Interactiv und Magier (sonst waer man oben rausgeflogen)
+        owner=getuid(environment());
+    else if (owner!=getuid(environment())) {
+       //ok, nicht der Eigentuemer, direkt weg.
+       call_out("remove",0);
+       return;
+   }
+   SetProp(P_EXTRA_LOOK,break_string(
+         "Auf "+environment()->Name(WESSEN)+" Schulter sitzt ein kleiner "
+         "Fehlerteufel, der "
+         +environment()->QueryPronoun(WEM)
+         +" immer wieder etwas ins Ohr fluestert.",78));
+
+   call_out("reset",1);
+
+   ::init();
+}
+
+public mixed Configure(mixed data)
+{
+  if (!data)
+  {
+    return (["filteruids":filteruids,
+             "filterstatus":filterstatus,
+             "modus":modus,
+             "monitoruids":monitoruids,
+             "fehlerzahl": fehlerzahl]);
+  }
+  else if (mappingp(data))
+  {
+    if (member(data,"filteruids") && pointerp(data["filteruids"]))
+      filteruids=data["filteruids"];
+    if (member(data,"filterstatus") && intp(data["filterstatus"]))
+      filterstatus=data["filterstatus"];
+    if (member(data,"modus") && intp(data["modus"]))
+      modus=data["modus"];
+    if (member(data,"monitoruids") && pointerp(data["monitoruids"]))
+      monitoruids=data["monitoruids"];
+    if (member(data,"fehlerzahl") && intp(data["fehlerzahl"]))
+      fehlerzahl=data["fehlerzahl"];
+    return 1;
+  }
+  return 0;
+}
+
+mapping _query_autoloadobj()
+{
+  return Configure(0);
+}
+
+mapping _set_autoloadobj(mixed data)
+{
+  Configure(data);
+  return _query_autoloadobj();
+}
+
+
+void reset()
+{
+    get_uids();
+    int neuefehlerzahl=get_issuelist();
+
+    if (fehlerzahl < neuefehlerzahl)
+      tell_object(environment(ME), break_string(
+        "Deine Fehlerliste ist soeben laenger geworden.",78,
+        "Dein Fehlerteufel teilt Dir mit: "));
+    else if (fehlerzahl > neuefehlerzahl)
+      tell_object(environment(ME), break_string(
+        "Deine Fehlerliste ist soeben kuerzer geworden.",78,
+        "Dein Fehlerteufel teilt Dir mit: "));
+    fehlerzahl = neuefehlerzahl;
+}
+
+// ******** private functions *********************
+private void get_uids()
+{
+    uids=(string *)master()->QueryUIDsForWizard(owner);
+    xmonitoruids=({});
+    if (sizeof(monitoruids))
+    {
+      closure cl=symbol_function("QueryUIDAlias", master());
+      foreach(string uid: monitoruids) {
+        xmonitoruids += (string*)funcall(cl, uid);
+      }
+    }
+}
+
+/* Holt sich aus dem ErrorD die Liste ungeloeschter und ungefixter Fehler fuer
+ * fuer die UIDs des Magier fuer alle Typen
+ */
+private varargs int get_issuelist(int lmodus)
+{
+    int count;
+
+    if (!lmodus)
+      lmodus=modus;
+
+    issuelist=m_allocate(sizeof(ALL_ERR_TYPES),1);
+
+    foreach(int type: ALL_ERR_TYPES)
+    {
+      if (type & lmodus)
+      {
+          //DEBUG(sprintf("Type: %d\n",type));
+          foreach(string uid : uids + xmonitoruids)
+          {
+            //DEBUG(sprintf("Type: %d, UID: %s\n",type,uid));
+            < <int|string>* >* list =
+                  (< <int|string>* >*)ERRORD->QueryIssueList(type,uid);
+            count += sizeof(list);
+
+            if (!member(issuelist, type))
+              issuelist += ([ type: ([ uid: list ]) ]);
+            else if (!member(issuelist[type], uid))
+              issuelist[type] += ([uid: list ]);
+        }
+      }
+    }
+    return count;
+}
+
diff --git a/obj/tools/lisp/escape.c b/obj/tools/lisp/escape.c
new file mode 100644
index 0000000..def5648
--- /dev/null
+++ b/obj/tools/lisp/escape.c
@@ -0,0 +1,37 @@
+// ESCAPE.C -- expanding escape characters
+// (c) 1994 by Hate@MorgenGrauen, TUBmud, NightFall
+// --
+// Copy, change and distribute this bit of software as much as you like,
+// but keep the name of the original author in the header.
+
+#pragma strong_types
+
+string escape(string str)
+{
+  switch(str) 
+  {
+    case "\b" : return "\\b";
+    case "\f" : return "\\f";
+    case "\n" : return "\\n";
+    case "\r" : return "\\r";
+    case "\t" : return "\\t";
+    case "\\" : return "\\\\";
+  }
+  return str;
+}
+
+string unescape(string str)
+{
+  switch(str) 
+  {
+    case "\\b" : return "\b";
+    case "\\f" : return "\f";
+    case "\\n" : return "\n";
+    case "\\r" : return "\r";
+    case "\\t" : return "\t";
+    case "\\\\": return "\\";
+    default   : if(str && sizeof(str)>1 && str[0]=='\\') 
+                return str[1..]; 
+  }
+  return str;
+}
diff --git a/obj/tools/lisp/knete.c b/obj/tools/lisp/knete.c
new file mode 100644
index 0000000..1dc1432
--- /dev/null
+++ b/obj/tools/lisp/knete.c
@@ -0,0 +1,190 @@
+// KNETE.C -- CommandLine Frontend to the Lisp2Closure compiler
+// (c) 1994 by Hate@MorgenGrauen, TUBmud, NightFall
+// --
+// Copy, change and distribute this bit of software as much as you like,
+// but keep the name of the original author in the header.
+
+#include "tweak.h"
+
+static inherit HOME("lisp");
+
+#ifdef ALATIA
+#  define TUBMUD
+#endif
+// TUBMUD is a COMPAT mode game! (well sort of, its a NATIVE/COMPAT mixture)
+// the following defines should be the only ones to change
+// define ENGLISH if you don't want german messages ;)
+#if (defined(TUBMUD) || defined(TAPPMUD) || defined(UNItopia) || \
+     defined(ADAMANT))
+# ifdef ADAMANT
+#   include <config.h>
+    inherit "/lib/obj";
+#   define WIZLOCAL "/wiz/"
+#   define ID(x)        set_name(x)
+#   define SHORT(x)     set_short(x)
+#   define LONG(x)      set_long(x)
+#   define ISWIZARD(pl) (LEVELMASTER->is_wizard(pl->query_real_name()))
+# endif
+# ifdef TUBMUD
+#   include <kernel.h>
+#   include <wizlevels.h>
+    inherit ACCESS;
+    inherit "/complex/item";
+    inherit "/basic/autoload";
+#   define WIZLOCAL "/players/"
+#   define ID(x)        add_id(x)
+#   define ISWIZARD(pl) (pl->query_level() >= WL_WIZARD)
+# endif
+# ifdef TAPPMUD
+#   include <levels.h>
+    inherit "/obj/thing";
+#   define WIZLOCAL "/w/"
+#   define ID(x)        set_alias(x)
+#   define ISWIZARD(pl) (pl->query_level() >= LVL_WIZARD)
+# endif
+# define ENGLISH
+# ifdef UNItopia
+#   undef ENGLISH
+#   ifdef FinalFrontier
+#     include <level.h>
+#   else
+#     include <levels.h>
+#   endif
+    inherit "/i/item";
+    inherit "/i/install";
+#   define WIZLOCAL "/w/"
+#   define ID(x)        add_id(x)
+#   define ISWIZARD(pl) wizp(pl)
+# endif
+# define WIZHOME        (WIZLOCAL+this_player()->query_real_name())
+# define LONG(x)        set_long(x)
+# define SHORT(x)       set_short(x)
+#else
+  inherit "/std/thing";
+# ifdef NIGHTFALL
+#   define ENGLISH
+# endif
+# include <properties.h>
+# include <wizlevels.h>
+# define WIZHOME        ("/players/"+getuid(this_object()))
+# define ID(x)          AddId(x); SetProp(P_NAME, x)
+# define LONG(x)        SetProp(P_LONG, x)
+# define SHORT(x)       SetProp(P_SHORT, x);
+# define ISWIZARD(pl)   IS_WIZARD(pl)
+#endif
+
+nomask void create()
+{
+  if(sizeof(old_explode(object_name(this_object()), "#")) == 1) return;
+  if(!interactive(this_player())) destruct(this_object());
+#ifndef MORGENGRAUEN
+  if((this_player() != find_player("hate") &&
+      interactive(find_player("hate"))) &&
+     old_explode(object_name(this_object()), "#")[0] == HOME("knete"))
+  {
+#ifdef TUBMUD
+    if(check_privilege("hate")) set_privilege("hate");
+    else set_privilege(0);
+#else
+    printf("*ERROR: do not clone this file!\n"
+          +"        create a file with the following line in you home:\n"
+          +"inherit \""+HOME("knete")+"\";\n");
+    destruct(this_object());
+    return;
+#endif
+  }
+#endif
+#if (defined(TUBMUD) || defined(UNItopia))
+  item::create();
+#elif (defined(ADAMANT))
+  obj::create();
+#else
+  thing::create();
+#endif
+#ifdef ENGLISH
+  ID("dough");
+  ID("lisp");
+  SHORT("A piece of dough\n");
+  LONG("This piece of dough can be formed to your own convenience.\n");
+#else
+# ifdef UNItopia
+    set_name("Knete");
+# endif
+  ID("knete");
+  ID("lisp");
+  SHORT("Ein Stueck Knete");
+  LONG( 
+  "Dieses Stueck Knete kann man sehr schoen an seinen persoenlichen Geschmack\n"
+ +"anpassen.\n");
+ this_object()->SetProp("autoloadobj",1);
+ this_object()->SetProp("autoload",1);
+#endif
+  lisp::create();
+}
+
+varargs nomask void init(int x)
+{
+  lisp::init(x);
+#if (defined(TUBMUD) || defined(UNItopia))
+# ifndef TUBMUD
+    item::init();
+# endif
+#elif (defined(ADAMANT))
+  obj::init();
+#else
+  thing::init();
+#endif
+  add_action("cmdline", "", 1);
+}
+
+#ifdef TAPPMUD
+query_auto_load() { return ({__FILE__}); }
+drop() { return 1; }
+#endif
+#if ((defined(TUBMUD)) || (defined(ADAMANT)))
+drop() { return 1; }
+#endif
+
+nomask string load(string file)
+{
+  if(previous_object() != this_object()) return "* Privilege violation\n";
+  return lisp(read_file(file));
+}
+
+nomask int cmdline(string str)
+{
+  int cost;
+  cost = get_eval_cost();
+  if(this_player() != this_interactive()) return 0;
+
+#ifndef MORGENGRAUEN
+# ifndef TUBMUD
+  if(this_player() && interactive(this_player()) &&
+     ((this_player() != find_player("hate")) ||
+      (this_player() != find_player("etah"))))
+# endif
+#else
+  if(this_player() && interactive(this_player()))
+#endif
+  {
+    mixed result;
+    if(!ISWIZARD(this_player())) return 0;
+#ifdef TAPPMUD
+    notify_fail(lambda(({}), ({#'lisp/*'*/, 
+				  query_verb()+(str?(" "+str):""), 1})));
+#else
+# ifdef MORGENGRAUEN
+    str = this_player()->_unparsed_args();
+# endif 
+    result = lisp(query_verb()+(str?(" "+str):""), 1);
+    if(result != -1 && cost >= 990000) {
+      if(stringp(result))
+        notify_fail(result);
+      else
+        if(result) notify_fail(sprintf("%O\n", result));
+	else return 1;
+      return 0;
+    }
+#endif
+  }
+}
diff --git a/obj/tools/lisp/knete.l b/obj/tools/lisp/knete.l
new file mode 100644
index 0000000..d8d548e
--- /dev/null
+++ b/obj/tools/lisp/knete.l
@@ -0,0 +1,11 @@
+(write "Loading /w/marcus/knete.l: ")
+(set_alias "knete")
+(set_alias "xyz")
+(set_short "Ein Stueck Knete")
+(set_long "Du siehst hier ein Stueck Knete, das man wunderbar verformen, vergroessern oder verklumpen kann. Irgendwie denkst du bei seinem Anblick ploetzlich an sehr viele Klammern auf und zu ... ganz wie in den alten LISP Tagen\n")
+(define _waddr_line (lambda (ob) (sprintf "%-15s %3d %-16s %s" (call_other ob "query_vis_name") (call_other ob "query_level") (query_ip_number ob) (query_ip_name ob))))
+(write ".")
+(define _waddr (lambda () (write (implode (map (call_other (this_player) "sort_by_level" (users)) ([ (memory) '_waddr_line)) "\n")))) (write "_waddr.")
+(define reload (lambda () (, (map (explode (read_file "/w/marcus/.knete.l") "\n") 'lisp) "Done.\n"))) (write "reload.")
+(define pkill (lambda (x) (, (m_delete (call_other "/obj/static/proplog" "QUERY") x) (call_other "/obj/static/proplog" "SAVE") (m_delete (call_other "/obj/static/propmaster" "QUERY") x) (call_other "/obj/static/propmaster" "SAVE") "Deleted."))) (write "pkill.")
+(write ".\n")
diff --git a/obj/tools/lisp/lex.c b/obj/tools/lisp/lex.c
new file mode 100644
index 0000000..a725c86
--- /dev/null
+++ b/obj/tools/lisp/lex.c
@@ -0,0 +1,73 @@
+// LEX.C -- creating tolkens from input string
+// (c) 1994 by Hate@MorgenGrauen, TUBmud, NightFall
+// --
+// Copy, change and distribute this bit of software as much as you like,
+// but keep the name of the original author in the header.
+
+#pragma strong_types
+
+#include "tweak.h"
+
+private inherit HOME("microcode");
+private inherit HOME("escape");
+
+private nosave mixed *tokens;
+
+static void create() 
+{ 
+  microcode::create();
+  tokens = ({});
+}
+
+nomask private mixed make_symbol(string str)
+{
+  mixed sym;
+
+  if(!stringp(str)) return str;
+  if(sym = get_function(str))				// FUNCTIONS
+    return sym;
+  if(str == "list")
+    return #'({;
+  if(str[0] == '"')
+    return implode(map(regexplode(str, "\\\\."), #'unescape), "");
+  if((str[0] >= '0' && str[0] <= '9') ||                // NUMBERS
+    ((str[0] == '+' || str[0] == '-') &&
+     (str[1] >= '0' && str[1] <= '9')))
+    if(member(str, '.') != -1)                          // FLOATS
+      return to_float(str);
+    else
+      return to_int(str);                               // INTEGERS
+
+  if(str == "(" || str == ")" || str == "'") 
+    return str;
+
+  return quote(str);            			// SYMBOLS
+}
+
+nomask private void get_tokens()
+{
+  mixed input, tmp;
+
+  do {
+    if(!input = get_line()) return 0;
+    // splitting the input line in string tokens and removing comments
+    tmp = regexp(regexplode(input,
+                               "(\"(\\\\.|[^\\\\\"])*\")|[;].*|[() \t']"),
+                    "^[^;]")
+           - ({""," ","\t"});
+    tokens += map(tmp, #'make_symbol);
+  }
+  while(!sizeof(tokens));
+}
+
+nomask static int lex(mixed token)
+{
+  if(!sizeof(tokens)) get_tokens();
+  if(sizeof(tokens))
+  {
+    token = tokens[0];
+    tokens[0..0] = ({});
+    return 1;
+  }
+  return 0;
+}
diff --git a/obj/tools/lisp/lisp.c b/obj/tools/lisp/lisp.c
new file mode 100644
index 0000000..89a0088
--- /dev/null
+++ b/obj/tools/lisp/lisp.c
@@ -0,0 +1,46 @@
+// LISP.C -- Lisp2Closure compiler
+// (c) 1994 by Hate@MorgenGrauen, TUBmud, NightFall
+// --
+// Copy, change and distribute this bit of software as much as you like,
+// but keep the name of the original author in the header.
+
+#pragma strong_types
+ 
+#include "tweak.h"
+
+private inherit HOME("parser");
+
+static varargs mixed lisp(string input, int interactive, int in)
+{
+  string msg, error;
+  add_input(input);					// put lines on stack
+  switch(parse(&msg, interactive))
+  { 
+  case  0: return msg;					// ok
+  case -1: clear_input(); return 0;			// empty list
+  case -2: initialize();				// exec error
+           clear_input();
+           return (in ? (printf(msg), 0): (interactive ? msg : -1));
+  case -3: if(interactive) 				// missing )
+           {
+             printf("%s? ", msg);
+             input_to("lisp", 0, 1, 1);
+             return -1;
+           }
+           else return (in ? (printf(msg), 0): (interactive ? msg : -1));
+  default: if(!msg) msg ="*Unknown error occured\n";	// unknown error
+           clear_input();
+           return (in ? (printf(msg), 0): 
+	                (interactive ? msg : -1));
+  }
+  return 0;
+}
+
+static void create()
+{
+  parser::create();
+  lisp(read_file(__FILE__[0..<3]+".l"));
+}
+
+static void init(int arg) { }
+
diff --git a/obj/tools/lisp/lisp.help b/obj/tools/lisp/lisp.help
new file mode 100644
index 0000000..e4d1f3e
--- /dev/null
+++ b/obj/tools/lisp/lisp.help
@@ -0,0 +1,81 @@
+NAME:
+		*** KNETE ***
+
+BESCHREIBUNG:
+	Die Knete ist ein in LPC geschriebener Lisp-Interpreter. Dieser hat
+	die besondere Eigenschaft, die Lisp-Quellen direkt in den vom 
+	Amylaar GameDriver angebotenen closures zu uebersetzen. Dadurch ist
+	mit der Knete alles das moeglich, was auch mit LPC moeglich ist.
+	Es ist ein generisches, frei programmierbares Hilfsmittel.
+
+	Die Knete befindet sich unter: "/obj/tools/lisp"
+
+FUNKTIONSBESCHREIBUNG:
+	Die Knete kann im Prinzip fast alles, was ein einfacher Lisp-
+	Interpreter kann. Ausnahmen sind Tupel (Bsp: (1 . 2)), welche nicht
+	implementiert sind. Die Grundlegenden Funktionen, wie define, setq,
+	cons, cdr, car etc werden beim Laden der Knete angezeigt. Je nach
+	Zeit, werden eventuelle auch weitere Standardfunktionen hinzu-
+	kommen, um die Knete moeglichst common-lisp kompatibel zu machen.
+
+BENUTZUNG:
+	Zu allererst sollte man wissen, dass dies hier keine Einfuehrung in
+	Lisp ist oder sein soll! Die wichtigsten Merkmal der Knete werden
+	aufgefuehrt an einigen kleinen Beispielen. Wer Lisp kennenlernen
+	moechte kann dies mit den handelsueblichen Buechern tun.
+
+	Wer dennoch basteln moechte: Lisp ist eine Sprache in Prefixnotation,
+	d.h. der Funktionsname steht immer als erstes und dann kommen die
+	Argumente. Ein Ausdruck ist mit den ihn umgebenden Klammern komplett.
+	  Beispiel: (+ 1 1) ;;; errechnet die Summe aus 1 und 1
+	Solche Klammerausdruecke koennen beliebig geschachtelt werden.
+	  Beispiel: (+ 1 (+ (+ 1 1) 1))
+
+	Es stehen alle efuns, sowie einige lfuns zur Verfuegung! Zu den efuns
+	gehoeren auch +,-,*,/,%,!,[,[<,[<.. etc, also alle Operatoren von LPC.
+
+	Die Knete hat zwei Modi:
+		a) Laden von Lisp aus einer Datei
+		b) Verarbeiten von Eingaben aus der Kommandozeile
+
+	Zu a)
+	  Mit der Funktion "load" koennen Dateien eingelesen und interpretiert
+	  werden. Die Funktion hat nur ein Argument, und das ist der Dateiname.
+
+	  Beispiel: (load "/players/hate/lisp.l")
+	
+	Zu b)
+	  Wenn die Knete gecloned wurde, koennen jederzeit Lispausdruecke
+	  eingegeben werden, welche dann interpretiert werden. Dabei sollte
+	  beachtet werden, dass die aeusserste Klammer nicht notwendig ist!
+
+	  Beispiel: (+ 1 (+ 1 1)) koennte man auch schreiben als:
+	            + 1 (+ 1 1)
+
+	  Dies ist vor allem dann interessant, wenn man die Moeglichkeiten
+	  der Knete als alias-Werkzeug in betracht zieht. Somit ist es
+	  moeglich, eine Funktion zu schreiben und diese dann wie ein alias
+	  zu benutzen.
+
+	  Beispiel: (defun who () (users)) ;;; gibt das users array aus
+	             Jetzt muss nur noch who eingegeben werden und das
+		     array wird ausgegeben.
+
+	  Da Lisp wesentlich komplexere Ausdruecke ermoeglicht, als der
+	  normale alias-Interpreter, liegen die Vorteile auf der Hand.
+
+KONFIGURATION:
+	Die Knete laesst sich fuer jeden Nutzer konfigurieren, indem sie eine
+	Datei "/players/<name>/lisp.l" aus dessen Heimatverzeichnis laedt.
+
+	Ein Beispiel, was man damit machen kann befindet sich unter:
+	  "/players/hate/lisp.l"
+	Die MUD Spezifischen Teile und Abfragen koennen ignoeriert werden.
+
+BUGS:
+	Es gibt momentan noch ein Problem, wenn auf der Kommandozeile
+	Klammern fehlen. Dann kommt eine Meldung: "*Missing 2 )"
+	In diesem Fall einfach zweimal hintereinander ) auf jeweils einer
+	einzelnen Zeile eingeben! Dieses Problem tritt vor allem dann auf,
+	wenn man zum Beispiel ein :( eingibt.
+
diff --git a/obj/tools/lisp/lisp.l b/obj/tools/lisp/lisp.l
new file mode 100644
index 0000000..acd386f
--- /dev/null
+++ b/obj/tools/lisp/lisp.l
@@ -0,0 +1,99 @@
+(write "LPC closure parser ready...\n")
+(write "LLisp Version 0.6 [")
+;;;
+;;; automagic quoting of hardcoded functions:
+;;; each function has 2 entries in 'memory'
+;;; 0 -- the function name or equivalent
+;;; 1 -- bits regarding autoquoting
+;;;      each argument for the function has two bits 
+;;;      0 -- normal quoting
+;;;      1 -- program code quoting (lamda requires it)
+;;;      only bit 0 or bit 1 must be set, never both (no quoting at all)
+;;; lambda needs normal quote for the first argument and special quote for 2nd
+;;; 1 0 0 1
+;;; |1| |2| -> 9 (if i have calculated it right)
+;;;
+;;; the function lambda has autoquote, @ is a non-autoquote version
+(= ([ (memory) '@) lambda)
+(= ([ (memory) lambda 1) 9) 
+;;;
+;;; explicit quote of a symbol with quote
+(= ([ (memory) quote 1) 1) 
+;;;
+;;; one of the most important functions needed (microcoded):
+;;; now we define a set
+(= ([ (memory) 'set) (lambda (n v) (, (= ([ (memory) n) v) n))) 
+;;; and a autoquote version of set (setq)
+(set 'setq set) (= ([ (memory) 'setq 1) 1)
+;;;
+;;; some people prefer define, it quotomatically quotes its first argument
+(setq define (lambda (n v f) (, (= ([ (memory) n 1) f) (set n v) n)))
+(= ([ (memory) 'define 1) 1)
+;;;
+;;; defun for easier definition of functions
+(define defun (lambda (n a b f) (, (= ([ (memory) n 1) f) 
+                                   (set n (@ a b)) n)) 37)
+;;;
+;;; if we like to destroy a value use undef
+(defun undef (fun) (, (= memory (m_delete (memory) fun)) fun) 1)
+;;;
+;;; an error function (internal use), notify declares the function to use
+;;; when an error message has to be printed
+(setq error raise_error)
+;;;
+;;; other important functions and function aliases
+;;; standard lisp functionality (car, cdr, cons)
+(defun car (l) (? (pointerp l) 
+		  (? (sizeof l) ([ l 0) 
+		     (error "car: empty list\n")) 
+		  (error "car: not a list\n")))
+(defun cdr (l) (? (pointerp l) ([.. l 1) 
+		  (error "cdr: not a list\n")))
+(defun cons (_car _cdr) (+ (list _car) _cdr))
+;;;
+;;; some special functionality
+(defun addhistory (file) (write_file file (implode (history) "\n")))
+(defun savehistory (file) (, (rm file) (addhistory file)))
+(defun showhistory () (, (printf "%s\n" (implode (history) "\n"))
+			 "-- END OF HISTORY --"))
+;;;
+(defun showfunc () (m_indices (memory)))
+;;;
+;;; Try to autodetect mudtype
+;;;
+(set 'mudtype "")
+(defun add (var val) (+= ([ (memory) var) val))
+(add 'mudtype (? (symbol_function "getuid") "NATIVE" "COMPAT"))
+(add 'mudtype (? (function_exists "SetProp")
+                   (? (== (function_exists "Set") "/std/thing/properties") "|MG"
+                    "|NF")
+                 (&& (== (file_size "/basic") -2) 
+		     (== (file_size "/complex") -2))
+                   (? (== (file_size "/kernel") -2) "|TUBNEW" "|TUB")
+                 (== (function_exists "query_all_v_items") "/i/item/virtual")
+                   (? (function_exists "query_bodylocation_pic"
+		                       (, (call_other "/obj/player" "?")
+				          (find_object "/obj/player")))
+				       "|UNI/AVALON" "|UNI")
+	        (? (function_exists "RCSId" (find_object "/obj/simul_efun")))
+		 "|TAPP"
+                 "|UNKNOWN/2.4.5"
+               ))
+(setq mudtype (explode mudtype "|"))
+(defun mud? (str) (sizeof (regexp ([ (memory) 'mudtype) str)))
+(printf "%s,%s" ([ ([ (memory) 'mudtype) 0) ([ ([ (memory) 'mudtype) 1))
+(write "]\n")
+;;; 
+;;; load local user init
+;;;
+(? (mud? "NF|MG")
+   (setq owner (getuid (this_player)))
+   (setq owner (call_other (this_player) "query_real_name")))
+(? (mud? "MG|NF|TUB")
+   (define localinit (+ (+ "/players/" owner) "/lisp.l"))
+   (define localinit (+ (+ "/w/" owner) "/lisp.l")))
+(? (> (file_size localinit) 0)
+   (? (mud? "TUB|UNI|UNKNOWN") 
+      (call_out load 0 localinit)
+      (load localinit)))
+(printf "Welcome %s, today is %s!\n" (capitalize owner) (ctime (time)))
diff --git a/obj/tools/lisp/microcode.c b/obj/tools/lisp/microcode.c
new file mode 100644
index 0000000..7fadd46
--- /dev/null
+++ b/obj/tools/lisp/microcode.c
@@ -0,0 +1,129 @@
+// MICROCODE.C -- lisp microcode
+// (c) 1994 by Hate@MorgenGrauen, TUBmud, NightFall
+// --
+// Copy, change and distribute this bit of software as much as you like,
+// but keep the name of the original author in the header.
+
+#pragma strong_types
+ 
+#include "tweak.h"
+#include "types.h"
+
+private nosave mapping memory;	// contains function information
+private nosave string *G_input;	// global input stack
+private nosave string *history;	// input history
+
+nomask static public string output(mixed res);
+
+#define CODE	0
+#define QUOTE	1
+
+static void create()
+{
+  memory = m_allocate(0, 2);
+  G_input = ({});
+}
+
+nomask static closure get_function(string str)
+{
+  closure sym;
+  if((sym = symbol_function(str, this_object())) ||     // FUNCTIONS
+     (sym = symbol_function(str)) ||
+     (sym = symbol_variable(str)))
+    return sym;
+#if defined(symbol_variable)
+  if(str == "memory") return #'memory;
+#endif
+  return 0;
+}
+
+// pre_parse() -- parse code before examination by a code generator
+nomask static public int pre_parse(mixed code)
+{
+  if(pointerp(code) && sizeof(code) &&
+     (closurep(code[0]) || symbolp(code[0])))
+  {
+    mixed idx, tmp;
+    if(sizeof(tmp = old_explode(sprintf("%O", code[0]), "->")) > 1)
+      idx = tmp[1];
+    else idx = code[0];
+    // warning only the setting of bit 0 or 1 is allowed, not 0 and 1
+    // thus 01 leads to normal quote, 10 for special, 11 not quote at all
+    return (memory[idx, QUOTE] & (3<<2*(sizeof(code)-1)))>>2*(sizeof(code)-1);
+  }
+}
+
+// prog_parse() -- examine a token and take actions
+//
+nomask varargs static public mixed prog_parse(mixed token, int func)
+{
+  if(symbolp(token))
+      if(!member(memory, token))
+        token = ({#'?, ({#'member, ({#'memory}), quote(token)}),
+                     ({#'[, ({#'memory}), quote(token)}),
+                     ({#'raise_error,
+		       sprintf("Symbol %O' not bound\n", token) }) });
+      else token = func ? token : memory[token];
+  return token;
+}
+
+// evaluate compiled code
+nomask static public mixed eval(mixed code)
+{
+  mapping sym;
+  sym = filter_indices(memory, #'symbolp);
+  return apply(lambda(m_indices(sym), code), m_values(sym));
+}
+
+// add new lines to input stack
+nomask static public void add_input(string input) 
+{ 
+  if(!stringp(input)) return;
+  G_input += old_explode(input, "\n"); 
+}
+
+// clear the input stack
+nomask static public void clear_input()
+{
+  G_input = ({});
+}
+
+// get a line from input stack
+// and save it into the history
+nomask varargs string get_line()
+{
+  if(sizeof(G_input))
+  {
+    if(!pointerp(history)) history = ({});
+    history += ({ G_input[0] });
+    G_input[0..0] = ({});
+    return history[<1];
+  }
+  return 0;  
+}
+
+// transform() -- transforms some of the output strings
+nomask private string transform(string e)
+{
+  switch(e)
+  {
+  case "({": return "(";
+  case "})": return ")";
+  case "([": return "[";
+  case "])": return "]";
+  }
+  if(strstr(e, " /* sizeof() == ") != -1) return "";
+  if(strstr(e, "\n") != -1) if(e[0] == ',') return " "; else return "";
+  return e;
+}
+
+// output() -- prints given data in a clean way
+nomask static public string output(mixed res)
+{
+  res = regexplode(sprintf("%O", res),
+                   "\n[ ]*|[,]\n[ ]*|[(][{\[]|[\]}][)]|[#][']|"
+                  +" [/][*] sizeof[(][)] == [0-9][0-9]* [*][/]")
+      - ({"\n", ",\n", "#'"});
+  res = map(res, #'transform);
+  return sprintf("%s\n", implode(res, ""));
+}
diff --git a/obj/tools/lisp/parser.c b/obj/tools/lisp/parser.c
new file mode 100644
index 0000000..dc47d65
--- /dev/null
+++ b/obj/tools/lisp/parser.c
@@ -0,0 +1,109 @@
+// PARSER.C -- parser for parenthesis expressions
+// (c) 1994 by Hate@MorgenGrauen, TUBmud, NightFall
+// --
+// Copy, change and distribute this bit of software as much as you like,
+// but keep the name of the original author in the header.
+
+#pragma strong_types
+
+#include "tweak.h" 
+
+private inherit HOME("lex");
+
+#include "stack.h"
+#include "types.h"
+
+private nosave mixed code;
+private nosave mixed stack;
+private nosave int quot, assume;
+
+nomask static void initialize()
+{
+  INITSTACK(stack);
+  code = ({});
+  quot = 0;
+}
+
+static void create()
+{
+  lex::create();
+  initialize();
+}
+
+nomask private mixed CodeGen(mixed token)
+{
+  switch(pre_parse(&code)) 
+  {
+  case 1: PUSH(stack, "'");          // standard autoquote
+	  quot++;
+	  break;
+  case 2: PUSH(stack, "'CODE");          // special QUOTE for code generation
+          break;	
+  }
+  if(stringp(token))
+    switch(token)
+    {
+      case "'": PUSH(stack, token); quot++; return;
+      case "(": PUSH(stack, code); code = ({}); break;
+      case ")":
+      {
+        mixed tmp;
+	if(SP(stack) && pointerp(TOP(stack))) POP(stack, tmp);
+        else tmp = ({});
+        if(SP(stack) && TOP(stack) == "'")
+	  /* do nothing */ ;
+        else
+          if(sizeof(code))
+	  {
+	    code[0] = prog_parse(code[0]);
+	    if(CL_LAMBDA(code[0]) || symbolp(code[0]) || pointerp(code[0]))
+	      code = ({#'funcall}) + code;
+	  }
+        code = tmp + ({ code });
+        break;
+      }
+      default : code += ({ token[1..<2] }); break;
+    }
+  else
+    code += ({ quot ? token : prog_parse(token, !sizeof(code)) });
+
+  while(SP(stack) && (TOP(stack) == "'"  || TOP(stack) == "'CODE") && 
+        sizeof(code)) 
+  { 
+    int q;
+    if(stringp(code[<1]) || symbolp(code[<1]) || pointerp(code[<1]) || 
+       get_type_info(code[<1])[0] == 10) // quoted array
+        code[<1] = quote(code[<1]); 
+    POPX(stack); 
+  }
+}
+
+// parse() -- non-threaded parsing (continue on input)
+nomask static int parse(mixed msg, int interactive)
+{
+  mixed token, res;
+  int lexing, missing_par;
+  do {
+    if(lexing = lex(&token))	
+    {					// try to assume outer "("
+      if(!SP(stack) && token != "(") { CodeGen("("); assume = 1; }
+      CodeGen(token);
+    }
+    else 				// add ")" if "(" is assumed
+      if(sizeof(stack) == 1 && assume) { CodeGen(")"); assume = 0; lexing = 1; }
+    if(lexing && !SP(stack))		// complete expression found
+    {
+      mixed exec_code; exec_code = code; initialize();
+      if(sizeof(exec_code) && pointerp(exec_code[0])) 
+        exec_code = ({#',}) + exec_code;// more than one expr on one line
+      if(msg = catch(res = eval(exec_code))) 
+        return -2;			// execution error
+      if(interactive) printf(output(res));
+      else msg = res;
+    }
+  }
+  while(lexing);
+  if(missing_par = SP(stack)) 
+    return (msg = sprintf("*Missing %d )\n", missing_par), -3); 
+  return 0;
+}
diff --git a/obj/tools/lisp/stack.h b/obj/tools/lisp/stack.h
new file mode 100644
index 0000000..f0578f4
--- /dev/null
+++ b/obj/tools/lisp/stack.h
@@ -0,0 +1,8 @@
+// STACK.H -- stack macros
+
+#define INITSTACK(s)	(s = ({}))
+#define PUSH(s,x)	(s += ({ (x) }))
+#define POP(s,x)	(x = s[<1], s = s[0..<2])
+#define POPX(s)		(s = s[0..<2])
+#define TOP(s)		((s)[<1])
+#define SP(s)		(sizeof(s))
diff --git a/obj/tools/lisp/tweak.h b/obj/tools/lisp/tweak.h
new file mode 100644
index 0000000..8518b60
--- /dev/null
+++ b/obj/tools/lisp/tweak.h
@@ -0,0 +1,34 @@
+// TWEAK.H -- system dependend macros
+
+#define MORGENGRAUEN
+
+#if (defined(AVALON) || defined(FinalFrontier))
+#  define UNItopia
+#endif
+
+// The home directory of the lisp compilers files
+#if (defined(TAPPMUD) || defined(UNItopia))
+#  define HOME(file)	"/w/hate/lisp/"+file
+#elif (defined(ADAMANT))
+#  define HOME(file)	"/wiz/hate/lisp/"+file
+#else
+# ifdef MORGENGRAUEN
+#   define HOME(file)	"/obj/tools/lisp/"+file
+# else
+#   define HOME(file)	"/players/hate/lisp/"+file
+# endif
+#endif
+
+// symbol_variable() is not available on drivers below 3.2.1@1
+//#define symbol_variable(str)	0
+
+// DEBUG mode
+#ifdef MORGENGRAUEN
+#  define USER "etah"
+#else
+#  define USER "hate"
+#endif
+#define DEBUG(x)	if(find_player(USER)) \
+                          tell_object(find_player(USER), \
+                                      sprintf("[%O] %O\n", this_object(), (x)))
+
diff --git a/obj/tools/lisp/types.h b/obj/tools/lisp/types.h
new file mode 100644
index 0000000..baef5eb
--- /dev/null
+++ b/obj/tools/lisp/types.h
@@ -0,0 +1,4 @@
+#define CL_LAMBDA(x)  (get_type_info(x)[0]==8 && get_type_info(x)[1]==5)
+#define CL_LFUN(x)    (get_type_info(x)[0]==8 && get_type_info(x)[1]==0)
+#define CL_EFUN(x)    (get_type_info(x)[0]==8 && get_type_info(x)[1]< 0)
+#define CL_VAR(x)     (get_type_info(x)[0]==8 && get_type_info(x)[1]==1)
diff --git a/obj/tools/logtool.c b/obj/tools/logtool.c
new file mode 100644
index 0000000..8493b75
--- /dev/null
+++ b/obj/tools/logtool.c
@@ -0,0 +1,156 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+inherit "std/secure_thing";
+
+#include <properties.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <moving.h>
+
+#define SAVEFILE  "/players/"+geteuid()+"/.logtool"
+
+// definieren von "inline" Funktionen:
+#define allowed()   (!process_call() && PL==RPL && geteuid()==getuid(RPL))
+
+mapping logs;
+
+static void check_logs()
+{
+  filter(sort_array(m_indices(logs), #'>), lambda( ({ 'x }), ({#'?, 
+    ({#'!=, ({#'[, logs, 'x, 2}), ({#'file_time, ({#'[, logs, 'x, 0}) }) }),
+       ({#'tell_object, ({#'environment }), ({#'sprintf,
+       "Neue Eintraege in <%s>.\n", 'x }) }), 0})));
+}
+
+void create()
+{
+  if (!clonep(ME)) return;
+  ::create();
+  if (!restore_object(SAVEFILE)) {
+    logs=([ "repfile": sprintf("/log/report/%s.rep", geteuid()); 0; 0 ]);
+    if (IS_ARCH(geteuid()))
+      logs+=(["snooplog": "/log/SNOOP"; 0; 0, "killer": "/log/KILLER"; 0; 0]);
+  }
+  SetProp(P_SHORT, this_player()->Name(WESSEN)+" Logtool");
+  SetProp(P_NAME, QueryProp(P_SHORT));
+  SetProp(P_NODROP, 0);
+  SetProp(P_NEVERDROP, 0);
+  SetProp(P_AUTOLOADOBJ, 1);
+  AddId("logtool");
+  AddCmd("mark", "aktualisieren");
+  AddCmd(m_indices(logs), "read_log");
+  AddCmd("AddLog", "add_log");
+  AddCmd("DelLog", "del_log");
+}
+
+void reset()
+{
+  ::reset();
+  if (!clonep(ME) || !environment()) return;
+  call_out("check_logs", 0);
+  set_next_reset(2*__RESET_TIME__); // kein Zufall, max. Laenge
+}
+
+varargs int move(mixed dest, int method)
+{
+  int i;
+  if (!objectp(dest)) dest=find_object(dest);
+  if (!dest || !interactive(dest) || getuid(dest)!=geteuid()) 
+    return ME_CANT_BE_TAKEN;
+  i=::move(dest, method);
+  if (i!=1) return i;
+  if (environment()) call_out("check_logs", 0);
+  return 1;
+}
+
+varargs string long() 
+{
+   return "Folgende Logfiles werden derzeit von Deinem Logtool verwaltet:\n\n"
+         +implode(map(sort_array(m_indices(logs), #'>),
+          lambda( ({ 'x }), ({#'sprintf, " %1s %-14s - %s", ({#'?, ({#'!=,
+          ({#'[,logs,'x, 2}), ({#'file_time,({#'[,logs,'x, 0})})}),"*",""}),
+          ({#'sprintf, "<%s>", 'x}), ({#'[, logs, 'x, 0})}) ) ), "\n")
+         +"\n\nMit <mark> koennen alle Eintraege als gelesen markiert und "
+         +"mit Add- und\nDelLog Logfiles in die Liste aufgenommen bzw. aus "
+         +"der Liste entfernt werden.\n";
+}
+
+static int aktualisieren(string str)
+{
+  if (!allowed()) return 0;
+  if (str && str!="") {
+    if (!logs[str]) {
+      notify_fail("Ein solches Logfile ist nicht in der Liste enthalten.\n");
+      return 0;
+    }
+    logs[str, 1]=file_size(logs[str, 0]);
+    logs[str, 2]=file_time(logs[str, 0]);
+  }
+  else filter(m_indices(logs), lambda( ({ 'x }),({#', ,
+   ({#'=, ({#'[, logs, 'x, 1}), ({#'file_size, ({#'[, logs, 'x, 0}) }) }),
+   ({#'=, ({#'[, logs, 'x, 2}), ({#'file_time, ({#'[, logs, 'x, 0}) })})})));
+  write("Done!\n");
+  save_object(SAVEFILE);
+  return 1;
+}
+
+static int read_log(string str)
+{
+  int si;
+  if (!allowed()) return 0;
+  si=file_size(logs[query_verb(),0]);
+  if (str)
+    PL->more(logs[query_verb(),0]);
+  else {
+    if (si<0 || file_time(logs[query_verb(), 0])==logs[query_verb(), 2])
+      write("Keine neuen Eintraege in <"+query_verb()+">.\n");
+    else {
+      write("Folgendes ist neu in <"+query_verb()+">.\n");
+      if (si<logs[query_verb(),1])
+        PL->more(logs[query_verb(),0]);
+      else PL->More(read_bytes(logs[query_verb(),0],
+	    logs[query_verb(),1],si-logs[query_verb(),1]));
+    }
+  }
+  logs[query_verb(), 1]=si;
+  logs[query_verb(), 2]=file_time(logs[query_verb(), 0]);
+  save_object(SAVEFILE);
+  return 1;
+}
+
+static int add_log()
+{
+  string key, path, str;
+  str=PL->_unparsed_args();  
+  if (!allowed() || !str || sscanf(str, "%s %s", key, path)!=2) {
+    notify_fail("Syntax: AddLog <key> <path>\n");
+    return 0;
+  }
+  key=lower_case(key);
+  if (logs[key]) {
+    notify_fail("Es gibt schon ein Logfile mit diesem Key in der Liste.\n");
+    return 0;
+  }
+  logs+=([ key: path; 0; 0 ]);
+  AddCmd(key, "read_log");
+  save_object(SAVEFILE);
+  write("Logfile <"+key+"> in die Liste aufgenommen.\n");
+  return 1;
+}
+
+static int del_log(string str)
+{
+  if (!allowed() || !str) {
+    notify_fail("WELCHES Logfile soll aus der Liste entfernt werden?\n");
+    return 0;
+  }
+  if (!logs[str]) {
+    notify_fail("Logfile nicht in Liste enthalten.\n");
+    return 0;
+  }
+  logs=m_copy_delete(logs,str);
+  RemoveCmd(str);
+  save_object(SAVEFILE);
+  write("Logfile <"+str+"> aus Liste entfernt.\n");
+  return 1;
+}
diff --git a/obj/tools/lupe.c b/obj/tools/lupe.c
new file mode 100644
index 0000000..5d6f109
--- /dev/null
+++ b/obj/tools/lupe.c
@@ -0,0 +1,1888 @@
+// This tool was originally written by Macbeth@TUBmud
+//
+// Some changes/extensions by Jof@MorgenGrauen
+
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <defines.h>
+#include <questmaster.h>
+#include <userinfo.h>
+
+#define add(s) commands+=({s});functions+=({s});ncmds++
+#define add2(s,f) commands+=({s});functions+=({f});ncmds++
+#define MAXSTACK 10
+#define MAXVARS 10
+#define MAXDEPTH 5
+#define MAXLINES 40
+#define MAXLINELEN 79
+
+mixed *stack, *vararea, err;
+
+string *commands, *functions, user, *query_list, *argv;
+int maxverb,ncmds,argc,recursive,hide_short,lines,firstline,lastline;
+static int tell(string str);
+int evalcmd(string str);
+int eval(string str);
+string stk(string arg);
+string desc(object ob);
+void clean(object ob);
+void cleanof(string str,object ob);
+
+
+void create()
+{
+  object owner;
+  string str;
+  
+  if (!clonep(ME)) return;
+  ::create();
+  owner=environment()||this_player();
+  if (owner)
+    str=capitalize(((str=geteuid(owner))[<1]=='s'||str[<1]=='x'||str[<1] =='z')?str+="'":str+="s"); 
+  else
+    str="Eine";
+  SetProp( P_SHORT,str+" Lupe" );
+  SetProp( P_NAME, str+" Lupe" );
+  SetProp( P_LONG, "Mit dieser Lupe kann man die Feinheiten des LPC besser erkennen.\n" );
+  SetProp( P_NEVERDROP, 1 );
+  SetProp( P_NODROP, "Du kannst Deine Lupe nicht wegwerfen. Dazu ist sie zu wertvoll.\n");
+  SetProp( P_NOBUY, 1 );
+  SetProp( P_GENDER, FEMALE );
+  SetProp( P_WEIGHT, 0 );
+  SetProp( P_ARTICLE,0);
+  SetProp( P_GENDER,0);
+  AddId( "lupe" );
+  user="*";
+  stack=({});
+  commands=({});
+  functions=({});
+  ncmds=0;
+  add("Dest");
+  add("_goto");
+  add("clnof");
+  add("cln");
+  add("clr");
+  add("copy_ldfied");
+  add("creat");
+  add("dest");
+  add("dinfo");
+  add("disco");
+  add("dump_lists");
+  add("dup");
+  add("env");
+  add("here");
+  add("info");
+  add("inv");
+  add("lv");
+  add("make");
+  add("me");
+  add("minfo");
+  add("new");
+  add("norec");
+  add("ob");
+  add("over");
+  add("pl");
+  add("rec");
+  add("result");
+  // add("rusage");
+  add("scan");
+  add("stat");
+  add("stk");
+  add("swap");
+  add("swho");
+  add("vars");
+  add2("#","lock");
+  add2(".","sel");
+  add2("/","file");
+  add2("<","readvar");
+  add2("=","dump");
+  add2(">","writevar");
+  add2("@","pick");
+  add2("[","call");
+  add2("_mo","_mv");
+  add2("_mov","_mv");
+  add2("_move","_mv");
+  add2("_mv","_mv");
+  add2("call_out","callout");
+  add2("debug_info", "db_info");
+  add2("desc","toggle_short");
+  add2("dump","dumplists");
+  add2("heart_beat","heartbeat");
+  add2("idle","idle");
+  add2("inherit_list","inheritlist");
+  add2("move","mv");
+  add2("mov","mv");
+  add2("mo","mv");
+  add2("pop","xpop");
+  add2("push","xpush");
+  add2("renew","renew_player");
+  add2("sc","scan");
+  add2("~","file2");
+  maxverb=ncmds;
+  vararea=allocate(MAXVARS);
+  query_list =
+    ({
+      "VALUE","-value","LEVEL","-level","IDS","-ids",
+      "WEAPON CLASS","-wc","ARMOUR CLASS","-ac",
+      "WEIGHT","-weight","TYPE","query_type",
+      "UID","-uid","EUID","-euid"
+    });
+}
+
+int _query_autoloadobj()
+{
+  return 1;
+}
+
+void _set_autoloadobj()
+{
+  call_out("_load_profile",0);
+}
+
+void _load_profile()
+{
+  object profile;
+  string pfile;
+  
+  if (geteuid() && environment() && geteuid(environment())==geteuid() &&
+      interactive(environment()))
+    if (file_size(pfile="/players/"+geteuid()+"/.profile.c")>0)
+      if (pfile=catch(call_other(pfile,"setup",environment())))
+	printf("Error when loading profile: %O\n",pfile);
+}
+
+void init()
+{
+  int i,ch;
+  
+  ::init();
+  if (environment()!=this_player()) return;
+//  if (!IS_LEARNER(this_player())) return;
+  if (!IS_LEARNER(this_player())) return destruct(this_object());
+  
+  add_action("cmdline","",1);
+}
+
+int cmdline(string str)
+{
+  string verb;
+  int i,ret;
+  
+  verb=query_verb();
+  if (!verb) return 0;
+  switch (verb)
+  {
+    case "erzaehl": return tell(str);
+  }
+  str=PL->_unparsed_args();
+  for (i=0;i<maxverb;i++)
+    if (commands[i]==verb[0..sizeof(commands[i])-1])
+      if (ret=evalcmd(str))
+	return ret;
+  return(0); // non-void function, Zesstra
+}
+
+static int tell(string str)
+{
+  string *tmp;
+  object who,sn;
+  int ret;
+
+  if (!IS_ARCH(this_interactive())) return 0;
+  if (!(str=this_interactive()->_unparsed_args())) return 0;
+  if (sizeof(tmp=old_explode(str," "))<2) return 0;
+  if (!(who=find_player(tmp[0]))) return 0;
+  if (!(sn=query_snoop(who))) return 0;
+  if (query_wiz_grp(this_interactive())<=query_wiz_grp(sn) ||
+      query_wiz_grp(this_interactive())<=query_wiz_grp(who)) return 0;
+  snoop(sn,0);
+  ret=this_interactive()->_erzaehle(str);
+  snoop(sn,who);
+  return ret;
+}
+
+int set_user(string str)
+{
+  string who;
+  if (!str) return 0;
+  if (str=="no access")
+  {
+    user="*";
+    move_object(this_object(),environment());
+    write("Ok.\n");
+    return 1;
+  }
+  if (sscanf(str,"access to %s",who)==1)
+  {
+    object pl;
+    pl=find_player(who);
+    if (!pl)
+      write("No such player.\n");
+    else
+    {
+      user = who;
+      write("Ok.\n");
+      move_object(this_object(),pl);
+      tell_object(pl,"Du darfst nun durch die magische Lupe schauen.\n");
+    }
+    return 1;
+  }
+  return 0;
+}
+
+string strip(string str)
+{
+  string temp;
+  if(!str) return "";
+  while (sscanf(str," %s",temp)==1) str=temp;
+  return str;
+}
+
+int arglen;
+
+string getarg(string args)
+{
+  string arg;
+  string rest;
+  if (sscanf(args,"\"%s\"%s",arg,rest)==2 ||
+      sscanf(args,"\'%s\'%s",arg,rest)==2 ||
+      sscanf(args,"|%s|%s",arg,rest)==2)
+  {
+    if (arg=="")
+      arglen=2;
+    else
+      arglen=sizeof(arg)+2;
+    return arg;
+  }
+  if (sscanf(args,"%s %s",arg,rest)==2)
+    args=arg;
+  if (sscanf(args,"%s.%s",arg,rest)==2)
+    args=arg;
+  if (sscanf(args,"%s[%s",arg,rest)==2)
+    args=arg;
+  if (sscanf(args,"%s]%s",arg,rest)==2)
+    args=arg;
+  if (args=="")
+    arglen=0;
+  else
+    arglen=sizeof(args);
+  return args;
+}
+
+string getrest(string str)
+{
+  if (arglen==0)
+    return str;
+  if (arglen==sizeof(str))
+    return "";
+  return strip(str[arglen..sizeof(str)-1]);
+}
+
+int interactiveMode(string str)
+{
+  if (str)
+    return eval(str);
+  stk("");
+  write("'.' to exit.\n");
+  write("? ");
+  input_to("more");
+  return 1;
+}
+
+void more(string str)
+{
+  string cmd;
+  if (str==".") return;
+  if (sscanf(str,"!%s",cmd)==1)
+    command(cmd,this_player());
+  else
+  {
+    eval(str);
+    stk("");
+  }
+  write("? ");
+  input_to("more");
+}
+
+int evalcmd(string str)
+{
+  string verb;
+  if (!IS_LEARNING(this_player())) return 0;
+  verb=query_verb();
+  if (verb=="?")
+    verb="";
+  if (str)
+    str=verb+" "+str;
+  else
+    str=verb;
+  return eval(str);
+}
+
+int eval(string str)
+{
+  int i,flag,old_sp,first;
+  mixed *old_stack;
+  string arg,tmp;
+  err=0;
+  first=1;
+  while (str!="")
+  {
+    flag=0;
+    str=strip(str);
+    if (sscanf(str,"#%s",arg)==1)
+    {
+      old_stack=stack[0..<1];
+      str=arg;
+    }
+    else
+      old_stack=0;
+    str=strip(str);
+    if (str=="")
+      break;
+    for (i=0;i<ncmds;i++)
+    {
+      if (sscanf(str,commands[i]+"%s",arg)==1)
+      {
+	if (arg!="" && str[0]>='a' && str[0]<='z' &&
+	    arg[0]>='a' && arg[0]<='z')
+	{
+	  if (first)
+	    return 0;
+	  else
+	  {
+	    printf("Couldn't parse: %O.\n",str);
+	    return 1;
+	  }
+	}
+	arg=strip(arg);
+	str=call_other(this_object(),functions[i],arg);
+	first=0;
+	if (old_stack)
+	{
+	  stack=old_stack;
+	  old_stack=0;
+	}
+	if (stringp(err))
+	{
+	  if (sscanf(err,"%s\n",tmp)==1)
+	    err = tmp;
+	  notify_fail(sprintf("ERROR: %O.\n",err));
+	  return 0;
+	}
+	flag=1;
+	break;
+      }
+    }
+    if (!flag)
+    {
+      notify_fail(sprintf("Couldn't parse: %O.\n",str));
+      return 0;
+    }
+  }
+  return 1;
+}
+
+int push(object ob)
+{
+  stack+=({ob});
+  if (sizeof(stack)>MAXSTACK)
+    stack=stack[1..MAXSTACK];
+  return 1;
+}
+
+mixed pop()
+{
+  object tmp;
+  if (!sizeof(stack))
+  {
+    err="stack underflow";
+    tmp=this_object();
+    return 0;
+  }
+  tmp=stack[sizeof(stack)-1];
+  stack=stack[0..<2];
+  if (!tmp)
+    err="operating on destructed object";
+  return tmp;
+}
+
+mixed xpop(string arg)
+{
+  if (!pop())
+    err=0;
+  return arg;
+}
+
+mixed toggle_short(string arg)
+{
+  hide_short = !hide_short;
+  if (hide_short)
+    write("Short descriptions off.\n");
+  else
+    write("Short descriptions on.\n");
+  return arg;
+}
+
+mixed pl(string arg)
+{
+  string who,rest;
+  object p;
+  who=getarg(arg);
+  rest=getrest(arg);
+  if (err) return 0;
+  p=match_living(who,1);
+  if (!stringp(p))
+  {
+    if (!p=find_netdead(who))
+      err="player "+who+" not found";
+    else 
+      push(p);
+  }
+  else
+    push(find_player(p));
+  return rest;
+}
+
+mixed lv(string arg)
+{
+  string who,rest;
+  object p;
+  who=getarg(arg);
+  rest=getrest(arg);
+  if (err) return 0;
+  p=find_living(who);
+  if (!p)
+    err="living object "+who+" not found";
+  else
+    push(p);
+  return rest;
+}
+
+string me(string arg)
+{
+  push(this_player());
+  return arg;
+}
+
+string make_path(string path)
+{
+  return "secure/master"->_get_path( path, geteuid( this_player() ) );
+}
+
+string new(string arg)
+{
+  string what,rest,file;
+  object ob;
+  what=getarg(arg);
+  rest=getrest(arg);
+  file=make_path(what);
+  err=catch(ob=clone_object(file));
+  if (!err)
+  {
+    push(ob);
+    ob->move(this_player(),M_NOCHECK);
+    write("Created "+desc(ob)+".\n");
+  }
+  else
+    err = "unable to create object, cause : "+err;
+  return rest;
+}
+
+static string creat(string arg)
+{
+  string what,rest,file;
+  object ob;
+  what=getarg(arg);
+  rest=getrest(arg);
+  file=make_path(what);
+  err=catch(ob=clone_object(file));
+  if (!err)
+  {
+    push(ob);
+    write("Created "+desc(ob)+".\n");
+  }
+  else
+    err = "unable to create object, cause : "+err;
+  return rest;
+}
+
+string xpush(string arg)
+{
+  string rest,what;
+  object ob;
+
+  what=getarg(arg);
+  rest=getrest(arg);
+  ob = find_object(what);
+  if (!ob)
+    err="Object "+what+" not found!\n";
+  else
+    push(ob);
+  return rest;
+}
+
+static string ob(string arg)
+{
+  string what,rest,file,tmp;
+  object ob;
+  what=getarg(arg);
+  rest=getrest(arg);
+  file=make_path(what);
+{
+  ob=find_object(file);
+  if (!ob)
+  {
+    tmp = catch(call_other(file,"??"));
+    if (!err)
+      ob = find_object(file);
+  }
+}
+if (!ob)
+{
+  err="object "+file+" not found";
+  if (tmp)
+    err += "("+tmp+")";
+}
+else
+push(ob);
+return rest;
+}
+
+string file(string arg)
+{
+  return ob("/"+arg);
+}
+
+string file2(string arg)
+{
+  return ob("~"+arg);
+}
+
+string sel(string arg)
+{
+  string rest;mixed what;
+  object ob,p;
+  ob=pop();
+  if (err) return arg;
+  if (sscanf(arg,"%d%s",what,rest)==2)
+  {
+    if (what<=0)
+    {
+      err="negative index";
+      push(ob);
+      return arg;
+    }
+    what--;
+    p=first_inventory(ob);
+    while (p && what)
+    {
+      p=next_inventory(p);
+      what--;
+    }
+    if (!p)
+    {
+      err="index to large";
+      push(ob);
+      return arg;
+    }
+    push(p);
+    return rest;
+  }
+  what=getarg(arg);
+  rest=getrest(arg);
+  p=present(what,ob);
+  if (p)
+    push(p);
+  else
+  {
+    push(ob);
+    err=what+" not present in "+desc(ob);
+  }
+  return rest;
+}
+
+string here(string arg)
+{
+  push(environment(this_player()));
+  return arg;
+}
+
+string env(string arg)
+{
+  object ob;
+  ob=pop();
+  if (!err)
+  {
+    if (!environment(ob))
+      err=desc(ob)+" has no environment";
+    else
+      push(environment(ob));
+  }
+  return arg;
+}
+
+string dup(string arg)
+{
+  object tos;
+  tos=pop();
+  if (!err)
+  {
+    push(tos);
+    push(tos);
+  }
+  return arg;
+}
+
+string swap(string arg)
+{
+  object tos;
+  int sts;
+
+  if ((sts=sizeof(stack))<2)
+  {
+    err="stack underflow";
+    return arg;
+  }
+  tos=stack[sts-1];
+  stack[sts-1]=stack[sts-2];
+  stack[sts-2]=tos;
+  return arg;
+}
+
+string over(string arg)
+{
+  object ob;
+  if (sizeof(stack)<2)
+  {
+    err="stack underflow";
+    return arg;
+  }
+  push(stack[sizeof(stack)-2]);
+  return arg;
+}
+
+string pick(string arg)
+{
+  string rest;
+  int no;
+  if (sscanf(arg,"%d%s",no,rest)!=2 || no<0 || no>=sizeof(stack))
+  {
+    err="stack size exceeded";
+    return arg;
+  }
+  else
+    push(stack[sizeof(stack)-no-1]);
+  return rest;
+}
+
+string string_desc(string str)
+{
+  string out;
+  out = implode(old_explode(str,"\\"),"\\\\");
+  out = implode(old_explode(out,"\n"),"\\n");
+  out = implode(old_explode(out,"\""),"\\\"");
+  return "\""+out+"\"";
+}
+
+mixed rec_desc(mixed ob)
+{
+  if (intp(ob))
+    return ""+ob;
+  if (stringp(ob))
+    return string_desc((string)ob);
+  if (objectp(ob))
+    return "OBJ("+object_name(ob)+")";
+  if (!pointerp(ob))
+    return sprintf("%O",ob);
+  return "({ "+implode(map(ob,"rec_desc",this_object()),", ")+" })";
+}
+
+string array_desc(mixed arr)
+{
+  string str,line,res;
+  mixed tmp;
+  int i,j;
+  str=rec_desc(arr);
+  if (sizeof(str)<=MAXLINELEN-4)
+    return "--> "+str+"\n";
+  tmp=old_explode(str," ");
+  res="";
+  lines=0;
+  i=1;
+  line="--> "+tmp[0]+" ";
+  for (;;)
+  {
+    while (i<sizeof(tmp) && sizeof(line)+sizeof(tmp[i]+1)<=MAXLINELEN-1)
+    {
+      line+=tmp[i]+" ";
+      i++;
+    }
+    if (sizeof(line)==0)
+    {
+      line=tmp[i]+" ";
+      i++;
+    }
+    if (i<sizeof(tmp))
+      line+="|\n";
+    else
+      line+="\n";
+    res+=line;
+    lines++;
+    if (lines>=MAXLINES)
+      return res+"*** TRUNCATED ***\n";
+    if (i>=sizeof(tmp))
+      return res;
+    line="";
+  }
+  return(0); // non-void, Zesstra (never reached)
+}
+
+string desc(object ob)
+{
+  if (!ob)
+    return "<destructed object>";
+  if (!objectp(ob))
+    return "<corrupted stack entry>";
+  if (query_once_interactive(ob))
+    return object_name(ob)+" "+capitalize(geteuid(ob));
+  if (!hide_short && ob->short())
+    return object_name(ob)+" "+ob->name();
+  else
+    return object_name(ob);
+}
+
+string stk(string arg)
+{
+  int i,sts;
+  if (!(sts=sizeof(stack)))
+    write ("<empty stack>\n");
+  else
+    for (i=1;i<=sts;i++)
+    {
+      write("@"+(i-1)+": "+desc(stack[sts-i])+"\n");
+    }
+  return arg;
+}
+
+string clr(string arg)
+{
+  stack=({});
+  return arg;
+}
+
+string dump(string arg)
+{
+  object ob;
+  string s;
+  ob=pop();
+  if (err) return arg;
+  push(ob);
+  write("FILENAME: "+object_name(ob)+" ");
+  if (!hide_short && (s=ob->short()))
+    write(" SHORT: "+ob->name());
+  write("\n");
+  return arg;
+}
+
+string info(string arg)
+{
+  object ob;
+  mixed s;
+  int i;
+  ob=pop();
+  if (err) return arg;
+  write("FILENAME: "+object_name(ob)+" ");
+  if (s=ob->short())
+    write(" SHORT: "+ob->name());
+  write("\n");
+  if (getuid(ob))
+    write("CREATOR: "+getuid(ob)+"\n");
+  if (s=query_ip_number(ob))
+  {
+    write("IP-NUMBER: "+s+" IP-NAME: "+query_ip_name(ob)+" IDLE: "
+	  + query_idle(ob)+"\n");
+  }
+  if (s=query_snoop(ob))
+    write("SNOOPED BY: "+s->query_real_name()+"\n");
+  s="";
+  if (living(ob))
+    s +="living ";
+  if (ob->query_npc())
+    s+="npc ";
+  if (ob->query_gender_string())
+    s+=ob->query_gender_string();
+  if (s!="")
+    write("FLAGS: "+s+"\n");
+  //  write("LONG:\n");
+  //  if (stringp(s=ob->long()))
+  //    write(s);
+  //  write("\n");
+  for (i=0;i<sizeof(query_list);i+=2)
+  {
+    if (query_list[i+1][0]=='-')
+      s=ob->QueryProp(query_list[i+1][1..]);
+    else
+      s=call_other(ob,query_list[i+1]);
+    if (s)
+    {
+      printf("%s: %O\n",query_list[i],s);
+    }
+  }
+  return arg;
+}
+
+string filler(int n)
+{
+  string s;
+  if (!recursive) return ": ";
+  s=": ";
+  while (++n<MAXDEPTH)
+    s="  "+s;
+  return s;
+}
+
+void listinv(object ob,int depth,string prefix)
+{
+  int i;
+  object p;
+  if (depth<MAXDEPTH)
+  {
+    p=first_inventory(ob);
+    i=1;
+    if (p)
+    {
+      while (p)
+      {
+	if (lines>lastline) return;
+	if (lines>=firstline)
+	  write(prefix+"."+i+filler(depth)+desc(p)+"\n");
+	lines++;
+	if (lines==lastline+1 && next_inventory(p))
+	  write("*** TRUNCATED ***\n");
+	if (recursive)
+	  listinv(p,depth+1,prefix+"."+i);
+	i++;
+	p=next_inventory(p);
+      }
+    }
+    else
+      if (!depth)
+	write("<empty inventory>\n");
+  }
+}
+
+string inv(string arg)
+{
+  object ob;
+  string rest;
+  int tmp;
+  ob=pop();
+  lines=1;
+  firstline=1;
+  lastline=MAXLINES;
+  if (!err)
+  {
+    if (sscanf(arg,":%d%s",tmp,rest)==2)
+    {
+      firstline=tmp;
+      lastline=tmp+MAXLINES-1;
+      arg = rest;
+      if (sscanf(arg,":%d%s",tmp,rest)==2)
+      {
+	lastline=tmp;
+	if (lastline<firstline)
+	  err = "first line > last line";
+	arg=rest;
+      }
+    }
+    push(ob);
+    listinv(ob,0,"");
+  }
+  recursive=0;
+  return arg;
+}
+
+object call_result;
+
+string call(string arg)
+{
+  string func,args;
+  int temp,i;
+  string rest,tmp;
+
+  object ob;
+  ob=pop();
+  if (err) return arg;
+  push(ob);
+  func=getarg(arg);
+  args=getrest(arg);
+  if (err) return args;
+  argv=({});
+  i=0;
+  while (1)
+  {
+    args=strip(args);
+    if (sscanf(args,"]%s",rest))
+      break;
+    if (sscanf(args,"%d%s",tmp,rest)==2)
+    {
+      args=rest;
+      argv+=({tmp});
+      continue;
+    }
+    if (sscanf(args,"\"%s\"%s",tmp,rest)==2 ||
+	sscanf(args,"\'%s\'%s",tmp,rest)==2 ||
+	sscanf(args,"|%s|%s",tmp,rest)==2)
+    {
+      args=rest;
+      argv+=({tmp});
+      continue;
+    }
+    if (sscanf(args,"@%d%s",temp,rest)==2)
+    {
+      if (temp<0 || temp>=sizeof(stack))
+      {
+	err="stackindex out of range";
+	return args;
+      }
+      argv+=({stack[sizeof(stack)-temp-1]});
+      args=rest;
+      continue;
+    }
+    tmp=getarg(args);
+    rest=getrest(args);
+    argv+=({tmp});
+    if (tmp!="")
+    {
+      args=rest;
+      continue;
+    }
+    err="bad argument to []";
+    return args;
+  }
+  if (sscanf(args,"]%s",rest)!=1)
+  {
+    err="too many or unterminated argument(s)";
+    return args;
+  }
+  call_result=apply(#'call_other,ob,func,argv);
+  //'
+  //  if (objectp(call_result))
+  //    write("--> "+desc(call_result)+"\n");
+  //  else if (pointerp(call_result))
+  //    write(array_desc(call_result));
+  //  else
+  //    write("--> "+call_result+"\n");
+  printf("--> %O\n",call_result);
+  pop();
+  argv=({});
+  return rest;
+}
+
+string result(string arg)
+{
+  if (objectp(call_result))
+    push(call_result);
+  else
+    err="call returned no object";
+  return arg;
+}
+
+int destroyable(object ob)
+{
+  if (!ob)
+    return 0;
+  if (query_once_interactive(ob))
+    return 0;
+  if (ob==this_object())
+    return 0;
+  return 1;
+}
+
+string cln(string arg)
+{
+  object ob;
+  ob=pop();
+  if (!err)
+  {
+    clean(ob);
+    write(desc(ob)+" cleaned up.\n");
+  }
+  recursive=0;
+  return arg;
+}
+
+string clnof(string arg)
+{
+  object ob;
+  int recsave;
+  string name,rest;
+
+  write("ClnOf");
+  recsave=recursive;
+  recursive=0;
+  ob=pop();
+  if (err) return arg;
+  name=getarg(arg);
+  rest=getrest(arg);
+  if (err) return arg;
+  recursive=recsave;
+  cleanof(name,ob);
+  recursive=0;
+  return rest;
+}
+
+void Remove(object ob,int a)
+{
+  if (!objectp(ob)) return;
+  if (!a)
+  {
+    printf("Removing %O",ob);
+    if (!hide_short) printf(" %O",ob->name());
+  }
+  catch(ob->remove());
+  if (ob)
+  {
+    if (!a) printf(" HARD");
+    destruct(ob);
+  }
+  write("\n");
+}
+
+void clean(object ob)
+{
+  object p,p2;
+  p=first_inventory(ob);
+  while (destroyable(p))
+  {
+    if (recursive) clean(p);
+    Remove(p,0);
+    p=first_inventory(ob);
+  }
+  while (p)
+  {
+    p2=next_inventory(p);
+    if (destroyable(p2))
+    {
+      if (recursive) clean(p2);
+      Remove(p2,0);
+    }
+    else
+      p=p2;
+  }
+}
+
+ void cleanof(string str,object ob)
+{
+  object p,p2;
+  p=first_inventory(ob);
+  while (p && p->id(str) && destroyable(p))
+  {
+    if (recursive) clean(p);
+    Remove(p,0);
+    p=first_inventory(ob);
+  }
+  while (p)
+  {
+    p2=next_inventory(p);
+    if (p2 && p2->id(str) && destroyable(p2))
+    {
+      if (recursive) clean(p2);
+      Remove(p2,0);
+    }
+    else
+      p=p2;
+  }
+}
+
+string dest(string arg)
+{
+  object ob;
+  ob=pop();
+  if (err) return arg;
+  if (!destroyable(ob))
+  {
+    err=desc(ob)+" must not be destroyed";
+    return arg;
+  }
+  Remove(ob,0);
+  return arg;
+}
+
+mixed disco(string arg)
+{
+  object ob;
+
+  ob=pop();
+  if (err) return arg;
+  if (!interactive(ob))
+  {
+    err=desc(ob)+" is not interactive";
+    return 1;
+  }
+  remove_interactive(ob);
+  return arg;
+}
+
+string Dest(string arg)
+{
+  object ob;
+  ob=pop();
+  if (err) return arg;
+  if (!destroyable(ob))
+  {
+    err=desc(ob)+" must not be destroyed";
+    return arg;
+  }
+  destruct( ob );
+  return arg;
+}
+
+string mv(string arg)
+{
+  object from,to;
+  to=pop();
+  if (err) return arg;
+  from=pop();
+  if (err) return arg;
+  from->move(to,M_NOCHECK);
+  write("Bewege "+desc(from)+" nach "+desc(to)+".\n");
+  return arg;
+}
+
+string _mv(string arg)
+{
+  object from,to;
+  to=pop();
+  if (err) return arg;
+  from=pop();
+  if (err) return arg;
+  __set_environment(from,to);
+  write("Bewege "+desc(from)+" nach "+desc(to)+".\n");
+  return arg;
+}
+
+string db_info(string arg)
+{
+  object ob;
+
+  ob=pop();
+  if (err) return arg;
+  debug_info(0,ob);
+  debug_info(1,ob);
+  return arg;
+}
+
+string inheritlist(string arg)
+{
+  object ob;
+  int i;
+  string *inherited;
+
+  ob=pop();
+  if (err) return arg;
+  inherited=inherit_list(ob);
+  write(ob);write(" inherits:\n");
+  for (i=0;i<sizeof(inherited);i++)
+    write(inherited[i]+"\n");
+  return arg;
+}
+
+mixed get_callout()
+{
+  mixed *calls,ret;
+  string tmp;
+  int i,j;
+
+  calls=call_out_info();
+  ret=({});
+  if (!pointerp(calls) || !sizeof(calls)) 
+    return 0;
+  for (i=0;i<sizeof(calls);i++)
+  {
+    if (pointerp(calls[i]))
+    {
+      tmp="";
+			if (sizeof(calls[i])>3)
+	      tmp+=sprintf("%-50O %-16O",calls[i][0],calls[i][1])+sprintf(" %-6O %-3O\n",calls[i][2],calls[i][3]);
+			else 
+				tmp+=sprintf(" *** %O\n",calls[i]);
+    }
+    ret+=({tmp});
+  }
+  return ret;
+}
+
+mixed get_heartbeat()
+{
+  mixed *obj;
+  string *ret;
+  int i;
+
+  obj=heart_beat_info();
+  ret=({});
+  if (!pointerp(obj) || sizeof(obj)==0) return 0;
+  for (i=0;i<sizeof(obj);i++)
+    ret+=({sprintf("%O in %O\n",obj[i],environment(obj[i]))});
+  return ret;
+}
+
+string make(string arg)
+{
+  object *list, ob, env;
+  string file,temp,dummy;
+  int i,cloned;
+ 
+  ob=pop();
+  if (err) return arg;
+  if (!destroyable(ob))
+  {
+    err="can't update "+desc(ob);
+    return arg;
+  }
+  env=environment(ob);
+  file=object_name(ob);
+  write("Updating "+object_name(ob)+"...\n");
+  if (sscanf(file,"%s#%s",temp,dummy)==2)
+  {
+    file=temp;
+    cloned=1;
+  }
+  else
+    cloned=0;
+  list=all_inventory(ob);
+  for (i=sizeof(list)-1;i>=0;i--)
+    if (list[i] && query_once_interactive(list[i]))
+    {
+      list[i]->move("room/void",M_TPORT | M_SILENT | M_NO_SHOW | M_NOCHECK);
+    } else
+      list[i]=0;
+  list-=({0});
+  
+  if (ob)
+  {
+    Remove(ob,1);
+  }
+  if (cloned)
+  {
+    if (ob=find_object(file))
+    {
+      Remove(ob,1);
+    }
+    err=catch(ob=clone_object(file));
+    if (!err)
+      ob->move(env,M_TPORT | M_SILENT | M_NO_SHOW | M_NOCHECK);
+  }
+  else
+  {
+    err=catch(call_other(file,"???"));
+    if (!err)
+      ob=find_object(file);
+    else
+      ob=0;
+  }
+  if (!ob)
+  {
+    write("Error in loaded object. Staying in void ...\n");
+    return arg;
+  }
+  for (i=sizeof(list)-1;i>=0;i--)
+    if (list[i])
+      list[i]->move(ob,M_TPORT | M_SILENT | M_NO_SHOW | M_NOCHECK);
+  return arg;
+}
+
+string rec(string arg)
+{
+  recursive=1;
+  return arg;
+}
+
+string norec(string arg)
+{
+  recursive=0;
+  return arg;
+}
+
+string readvar(string arg)
+{
+  string rest;
+  int no;
+  if (sscanf(arg,"%d%s",no,rest)!=2 || no<0 || no>=MAXVARS)
+  {
+    err="illegal var number";
+    return arg;
+  }
+  if (vararea[no])
+    push(vararea[no]);
+  else
+    err="var #"+no+" is empty";
+  return rest;
+}
+
+string writevar(string arg)
+{
+  string rest;
+  int no;
+  object ob;
+  if (sscanf(arg,"%d%s",no,rest)!=2 || no<0 || no>=MAXVARS)
+  {
+    err="illegal var number";
+    return arg;
+  }
+  ob=pop();
+  if (err) return rest;
+  vararea[no]=ob;
+  return rest;
+}
+
+string vars(string arg)
+{
+  int i;
+  for (i=0;i<MAXVARS;i++)
+  {
+    if (vararea[i])
+      write("<"+i+": "+desc(vararea[i])+"\n");
+  }
+  return arg;
+}
+
+void vanish()
+{
+ // RemoveAutoload();
+  destruct( this_object() );
+}
+
+mixed rusage(string arg)
+{
+  mixed *resusage;
+  int i,j;
+/*
+  resusage=({mixed *})efun::rusage();
+  for (i=0;i<18;i++){
+    write(align(({"User time","System time","Max res set size",
+		  "Page reclaims","Page faults",
+		  "Unshared stack size",
+		  "Shared text size","Unshared data size",
+		  "System swaps",
+		  "Block input operations","Block output operations",
+		  "Messages sent","Messages received","Signals received",
+		  "Voluntary context switches","Involuntary context switches",
+		  "Total internet packets","Total internet bytes"})[i],
+		40)+": "+resusage[i]+"\n");
+  }
+  return arg;
+  */
+  return ({});
+}
+
+string align(string s,int x){
+  return (s+"                                          ")[0..x-1];
+}
+
+static string swho(string arg)
+{
+  object *userlist, snooper, found;mixed active;
+  int i,j,done;
+
+  if (geteuid(this_interactive())!=geteuid()) return arg;
+  userlist=users();
+  active=({});
+  for (i=sizeof(userlist)-1;i>=0;i--)
+    if (snooper=query_snoop(userlist[i]))
+    {
+      if (member(active,snooper)==-1)
+	active+=({snooper});
+      if (member(active,userlist[i])==-1)
+	active+=({userlist[i]});
+    }
+  if (!sizeof(active))
+  {
+    printf("Keine aktiven Snoops.\n");
+    return arg;
+  }
+  for (i=sizeof(active)-1;i>=0;i--)
+    active[i]=({active[i]});
+  for (i=sizeof(active)-1;i>=0;i--)
+    if (pointerp(active[i])&&snooper=query_snoop(active[i][0]))
+    {
+      done=0;
+      for (j=sizeof(active)-1;j>=0 && !done;j--)
+	if (pointerp(active[j]) && active[j][sizeof(active[j])-1]==snooper)
+	{
+	  active[j]+=active[i];
+	  active[i]=0;
+	  done=1;
+	}
+    }
+  active-=({0});
+  for (i=0;i<sizeof(active);i++)
+  {
+    for (j=0;j<sizeof(active[i]);j++)
+      printf("%s%s",(j==0?"":" -> "),capitalize(getuid(active[i][j])));
+    printf("\n");
+  }
+  return arg;
+}
+
+string timef(int sec)
+{
+  string s;
+
+  s="";
+  if (sec>=86400)
+    s+=sprintf("%d d, ",sec/86400);
+  if (sec>3600)
+    s+=sprintf("%d h, ",(sec/3600)%24);
+  if (sec>60)
+    s+=sprintf("%d m, ",(sec/60)%60);
+  return s+sprintf("%d s",sec%60);
+}
+
+string idle(string arg)
+{
+  object ob;
+  int i;
+
+  ob=pop();
+  if (err) return arg;
+  write(capitalize(ob->name(WER))+" ");
+  if (!query_once_interactive(ob))
+  {
+    write("ist kein echter Spieler.\n");
+    return arg;
+  }
+  if (!query_ip_number(ob))
+  {
+    write("ist netztot.\n");
+    return arg;
+  }
+  printf("ist idle seit %d Sekunden",i=query_idle(ob));
+  if (i>60)
+    printf(" (%s)\n",timef(i));
+  else
+    write("\n");
+  return arg;
+}
+
+string stat(string arg)
+{
+  object ob;
+  mapping quests;
+  mixed stats, *arr, tmp,tmp2, list;
+  string titel, level, stat_str,weapon,armour;
+  int pl;
+  int i;
+  
+  ob=pop();
+  if (err)
+    return arg;
+  
+  titel=ob->QueryProp(P_TITLE);
+  if (!(pl=query_once_interactive(ob)))
+    level="Monster="+old_explode(object_name(ob),"#")[0];
+  else
+    if (IS_GOD(ob))
+      level="Mud-Gott";
+    else if (IS_ARCH(ob))
+      level="Erzmagier";
+    else if (IS_ELDER(ob))
+      level="Weiser";
+    else if (IS_LORD(ob))
+      level="Regionsmagier";
+    else if (IS_DOMAINMEMBER(ob))
+      level="Regionsmitglied";
+    else if (IS_WIZARD(ob))
+      level="Magier";
+    else if (IS_LEARNER(ob))
+      level="Lehrling";
+    else if (IS_SEER(ob))
+      level="Seher";
+    else level="Spieler";
+  if (IS_DOMAINMEMBER(ob))
+    for (tmp="secure/master"->get_domain_homes(geteuid(ob));
+	 sizeof(tmp);tmp=tmp[1..])
+      level+="-"+capitalize(tmp[0]);
+  if (pl)
+  {
+    if (!interactive(ob))
+      level+=", netztot";
+    else
+      if (query_idle(ob)>600)
+	level+=", idle";
+    if (ob->QueryProp(P_GHOST))
+      level+=", tot";
+    if (ob->QueryProp(P_INVIS))
+      level+=", unsichtbar";
+    if (ob->QueryProp(P_FROG))
+      level+=", gruen und glitschig";
+    if (ob->QueryProp(P_TESTPLAYER))
+      level+=", Testspieler";
+  }
+  tmp=ob->QueryProp(P_PRESAY);
+  if (tmp && tmp!="")
+    tmp=tmp+" ";
+  else
+    tmp="";
+  tmp2=ob->QueryProp(P_RACE);
+  if(!tmp2)
+    tmp2="Dingsda";
+  arr=ob->QueryProp(P_NAME);
+  if (pointerp(arr)) arr=arr[0];
+  printf("%s%s %s (%s)[%s].\n\n",tmp||"",arr||"",titel||"",
+	 tmp2||"??",level||"??");
+  if (pl)
+    printf("        Alter : %s.%s\n",
+	   timef(2*ob->QueryProp(P_AGE)),
+	   (tmp=ob->QueryProp(P_MARRIED))?
+	   ("Verheiratet mit "+capitalize(tmp)+"."):"");
+  else
+    printf("    Aggressiv : %4s          Gespraechig : %d%%\n",
+	   ob->QueryProp(P_AGGRESSIVE)? "Ja" : "Nein", 
+	   ob->QueryProp(P_CHAT_CHANCE)) ;
+  printf(" Lebenspunkte : [%4d/%4d]   Magiepunkte : [%4d/%4d]   " +
+	 "Erfahrung : %d\n",
+	 ob->QueryProp(P_HP), ob->QueryProp(P_MAX_HP), 
+	 ob->QueryProp(P_SP), ob->QueryProp(P_MAX_SP), 
+	 ob->QueryProp(P_XP));
+  printf("      Nahrung : [%3d/%d]    Fluessigkeit : [%3d/%d]       " +
+	 "Alkohol : [%3d/%d]\n",
+	 ob->QueryProp(P_FOOD), ob->QueryProp(P_MAX_FOOD),
+	 ob->QueryProp(P_DRINK), ob->QueryProp(P_MAX_DRINK),
+	 ob->QueryProp(P_ALCOHOL), ob->QueryProp(P_MAX_ALCOHOL)) ;
+  switch(ob->QueryProp(P_GENDER))  {
+    case FEMALE : tmp2 = "weiblich " ; break ;
+    case MALE   : tmp2 = "maennlich" ; break ;
+    default     : tmp2 = "boingisch" ; break ;
+  }
+  printf("   Geschlecht : %s       Charakter : %-5d             Stufe : %-3d\n",
+	 tmp2, ob->QueryProp(P_ALIGN), ob->QueryProp(P_LEVEL)) ;
+  stats = ob->QueryProp(P_ATTRIBUTES) ;
+	if (!mappingp(stats)) stats=([]);
+  tmp  = m_indices(stats); tmp2 = m_values(stats); stat_str = "" ;
+  for(; sizeof(tmp); tmp=tmp[1..],tmp2=tmp2[1..])
+    stat_str += (tmp[ 0 ] + "[" + tmp2[ 0 ] + "] ") ;
+  
+  if(stat_str == "")
+    stat_str = "Keine" ;
+  else
+    stat_str = stat_str[0..<2];
+  
+  printf("         Geld : %-9d           Stati : %s\n\n", ob->QueryMoney(),
+	 stat_str) ;
+  
+  weapon = "Keine" ; armour = "" ;
+  for(tmp=all_inventory(ob); sizeof(tmp); tmp=tmp[1..])
+  {
+    if(tmp[ 0 ]->QueryProp(P_WIELDED)) // gezueckte Waffe
+      weapon = (tmp[ 0 ]->name(WER)) + " (" +
+         old_explode(object_name(tmp[ 0 ]),"#")[0] + ")[" +
+         tmp[ 0 ]->QueryProp(P_WC) + "]" ;
+
+    if(tmp[ 0 ]->QueryProp(P_WORN))   // getragene Ruestung
+      armour += (tmp[ 0 ]->name(WER)) + "[" +
+          tmp[ 0 ]->QueryProp(P_AC) + "]" +
+    ", " ;
+  }
+
+  if(armour == "")
+    armour = "Keine" ;
+  else
+  {
+    tmp = old_explode(break_string(armour[0..sizeof(armour) - 3],
+			       63), "\n") ;
+    armour = tmp[ 0 ] ;
+    tmp=tmp[1..];
+    for (;sizeof(tmp); tmp=tmp[1..])
+      armour += "\n                " + tmp[ 0 ] ;
+  }
+  
+  printf("   Waffe(%3d) : %s\nRuestung(%3d) : %s\n",
+	 ob->QueryProp(P_TOTAL_WC), weapon,
+	 ob->QueryProp(P_TOTAL_AC), armour) ;
+  
+  list = ob->QueryEnemies();
+  if (pointerp(list)) 
+  {
+    list=list[0];
+    tmp2 = "" ;
+    for(i=sizeof(list)-1 ; i>=0;i--)
+      if (objectp(list[i]))
+        tmp2 += (list[ i ]->name(WER) + ", ") ;
+    if(tmp2 != "")
+      printf("       Feinde : %s.\n", tmp2[0..<3]);
+  }
+  
+  // 8.Zeile : Geloeste Aufgaben
+  if(pl)
+  {
+    printf( break_string( 
+              CountUp(m_indices(ob->QueryProp(P_QUESTS))), 
+              75,
+              "     Aufgaben : ",
+              BS_INDENT_ONCE));
+    if(((tmp2 = ob->QueryProp(P_MAILADDR)) != "none") && tmp2 &&
+       (tmp2 != ""))
+      tmp2 = " (" + tmp2 + ")" ;
+    else
+      tmp2 = "" ;
+  }
+  else
+    tmp2 = "" ;
+
+  if(environment(ob))
+    printf("   Aufenthalt : %s%s.\n",
+	   old_explode(object_name(environment(ob)),"#")[0], tmp2) ;
+
+  return arg;
+}
+
+string scan(string arg)
+{
+  object ob;
+  mixed tmp,tmp2,tmp3;
+  int i;
+
+  ob=pop();
+  if (err) return arg;
+  if (query_once_interactive(ob))
+    printf("Spieler: %s, Level: %d, Wizlevel: %d\n",
+	   capitalize(getuid(ob)), ob->QueryProp(P_LEVEL),
+	   query_wiz_level(ob));
+  else
+    printf("Monster, UID: %s, EUID: %s, Level: %d\n",
+	   getuid(ob), (geteuid(ob)?geteuid(ob):"0"), ob->QueryProp(P_LEVEL));
+  tmp=ob->short();
+  if (!stringp(tmp)||!sizeof(tmp))
+    tmp=sprintf("(%s %s %s %s)",ob->QueryProp(P_PRESAY)+"",
+		ob->QueryProp(P_NAME)+"",ob->QueryProp(P_TITLE)+"",
+		(interactive(ob)?"":"(netztot"));
+  else
+    tmp=tmp[0..<3];
+  printf("%s, Rasse: %s\n",tmp, ""+ob->QueryProp(P_RACE));
+  printf("Stats:    ");
+  tmp=ob->QueryProp(P_ATTRIBUTES);
+  tmp3=ob->QueryProp(P_ATTRIBUTES_OFFSETS);
+  if (!tmp)
+    printf("keine\n");
+  else
+  {
+    tmp2=m_indices(tmp);
+    for (i=0;i<sizeof(tmp2);i++)
+	{
+      printf("%s%s: %d",(i>0?", ":""),tmp2[i],tmp[tmp2[i]]);
+	  if (tmp3[tmp2[i]])
+	    printf("%+d",tmp3[tmp2[i]]);
+    }
+    printf("\n");
+  }
+  printf("Ruestung: %-6d    Waffen: %-6d      Vorsicht: %-6d   Geschlecht: ",
+	 ob->QueryProp(P_TOTAL_AC),ob->QueryProp(P_TOTAL_WC),
+	 ob->QueryProp(P_WIMPY));
+  if (!(tmp=ob->QueryProp(P_GENDER)))
+    printf("N\n");
+  else
+  {
+    if (tmp==2)
+      printf("F\n");
+    else
+      printf("M\n");
+  }
+  if (tmp=ob->QueryProp(P_MARRIED))
+    printf("Verheiratet mit %s.\n",capitalize(tmp));
+  printf("Lebenspunkte: %4d [%4d], Magiepunkte: %4d [%4d], Erf: %-8d\n",
+	 ob->QueryProp(P_HP), ob->QueryProp(P_MAX_HP), 
+	 ob->QueryProp(P_SP), ob->QueryProp(P_MAX_SP),
+	 ob->QueryProp(P_XP));
+  if (living(ob))
+  {
+    tmp=present("geld",ob);
+    if (tmp)
+      tmp=tmp->QueryProp(P_AMOUNT);
+    printf("Traegt %6d (%6d) g. Eigengewicht %6d g. %6d Muenzen.\n",
+	   ob->query_weight_contents(),ob->QueryProp(P_MAX_WEIGHT),
+	   ob->QueryProp(P_WEIGHT),tmp);
+    if (tmp=ob->SelectEnemy())
+      printf("Kaempft gegen %s [%O]\n",tmp->name(WEN),tmp);
+  }
+  printf("ENV: %s",
+	 ((tmp=environment(ob))?object_name(tmp):"- fabric of space -"));
+  if(query_once_interactive(ob))
+  {
+    printf(", EMail: %s\n", ob->QueryProp(P_MAILADDR)+"");
+    tmp="/secure/master"->find_userinfo(getuid(ob));
+    if (pointerp(tmp) && sizeof(tmp)>USER_DOMAIN)
+    {
+      tmp=tmp[USER_DOMAIN];
+      if (pointerp(tmp) && sizeof(tmp))
+      {
+	printf("Lord in: ");
+	for (tmp2=0;tmp2<sizeof(tmp);tmp2++)
+	  printf("%s%s",(tmp2>0?", ":""),""+tmp[tmp2]);
+	printf(".\n");
+      }
+    }
+    tmp="/secure/master"->get_domain_homes(getuid(ob));
+    if (pointerp(tmp)&&sizeof(tmp)>0)
+    {
+      printf("Mitglied in: ");
+      for (tmp2=0;tmp2<sizeof(tmp);tmp2++)
+     	printf("%s%s",(tmp2>0?", ":""),""+tmp[tmp2]);
+      printf(".\n");
+    }
+    printf("Quests: ");
+    tmp=ob->QueryProp(P_QUESTS);
+    if (tmp==({})||!pointerp(tmp))
+      printf("Keine.\n");
+    else
+    {
+      tmp2="";
+      tmp-=({({}),0});
+      for (i=0; i<sizeof(tmp); i++)
+	tmp2+=sprintf("%s%s",(i?", ":""),tmp[i][0]);
+      tmp=break_string(tmp2,79,8);
+      tmp=tmp[8..];
+      printf("%s",tmp);
+    }
+    printf("PKills: %d ",ob->QueryProp(P_KILLS));
+    printf(", QuestPoints: %d (%d/%d), Alter: %s\n",ob->QueryProp(P_QP),ob->QueryProp(P_NEEDED_QP),QM->QueryMaxQP(),timef(2*ob->QueryProp(P_AGE)));
+    if (interactive(ob))
+    {
+      printf("From: %s (%s) [%s]\n",query_ip_name(ob),query_ip_number(ob),country(query_ip_name(ob)));
+      tmp=query_idle(ob);
+      printf("Idle seit %d Sekunden",tmp);
+      if (tmp>60)
+  	printf(" (%s)",timef(tmp));
+			printf(", cmd avg: %d",ob->QueryProp("command_average"));
+      printf(", noch %d ZT zu finden.\nGesnooped von: %s\n",
+	     ((tmp=ob->QueryProp(P_POTIONROOMS))?sizeof(tmp):0),
+	     ((tmp=query_snoop(ob))?capitalize(getuid(tmp)):"Niemandem"));
+    }
+    else
+    {
+      tmp=getuid(ob);
+      tmp=file_time("save/"+tmp[0..0]+"/"+tmp+".o");
+      tmp=time()-tmp;
+      printf("Kam von: %s, vor: %d s(%s)\n",
+	     ob->QueryProp(P_CALLED_FROM_IP),tmp,timef(tmp));  
+    }
+  }
+  else
+    printf("\n");
+  return arg;
+}
+
+mixed dinfo(string arg)
+{
+  object ob;
+
+  ob=pop();
+  if (!ob)
+    return 1;
+  debug_info(0,ob);
+  return arg;
+}
+
+mixed minfo(string arg)
+{
+  object ob;
+
+  ob=pop();
+  if (!ob)
+    return 1;
+  debug_info(1,ob);
+  return arg;
+}
+
+void dump_list(mixed what)
+{
+  int i,s;
+
+  if (!pointerp(what)||!(s=sizeof(what)))
+    return;
+  for (i=0;i<s;i++)
+    write(what[i]);
+}
+
+string callout(string args)
+{
+  dump_list(get_callout());
+  return args;
+}
+
+string heartbeat(string args)
+{
+  dump_list(get_heartbeat());
+  return args;
+}
+
+string dumplists(string args)
+{
+  string filen;
+  string *list;
+  int i,s;
+
+  if (!geteuid(this_object()))
+    return args;
+  filen="/players/"+geteuid(this_object())+"/LISTS.LUPE";
+  write("Dumping to "+filen+" ... ");
+  if (file_size(filen)>=0)
+    rm(filen);
+  write_file(filen,"OBJECT WITH ACTIVE HEART_BEAT:\n");
+  list=get_heartbeat();
+  if (!list || !(s=sizeof(list)))
+    write_file(filen,"NONE\n");
+  for (i=0;i<s;i++)
+    write_file(filen,list[i]);
+  write_file(filen,"\n\nRUNNING CALL_OUTS:\n");
+  list=get_callout();
+  if (!list || !(s=sizeof(list)))
+    write_file(filen,"NONE\n");
+  for (i=0;i<s;i++)
+    write_file(filen,list[i]);
+  write("done.\n");
+  return args;
+}
+
+mixed renew_player(string arg)
+{
+  object ob;
+
+  ob=pop();
+  if (!ob)
+    return 1;
+  if (!objectp(ob)&&!interactive(ob))
+  {
+    err=desc(ob)+" is not an interactive player";
+    return arg;
+  }
+  if ((err="/secure/master"->renew_player_object(ob))<0)
+    err="error "+err+" when renewing "+desc(ob);
+  return arg;
+}
+
+mixed copy_ldfied(string arg)
+{
+  object ob, new;
+  mapping props;
+
+  ob=pop();
+  if (!ob)
+    return 1;
+  if (!objectp(ob))
+  {
+    err=desc(ob)+" is not an valid object";
+    return arg;
+  }
+  new=clone_object(old_explode(object_name(ob),"#")[0]);
+  props=ob->QueryProperties();
+  new->SetProperties(ob->QueryProperties());
+  push(new);
+  new->move(this_player(),M_NOCHECK);
+  return arg;
+}
+
diff --git a/obj/tools/lupe.doc b/obj/tools/lupe.doc
new file mode 100644
index 0000000..cf3946f
--- /dev/null
+++ b/obj/tools/lupe.doc
@@ -0,0 +1,140 @@
+The lens object uses a stack to manipulate objects. Some commands push objects
+on the stack, some pop them again off the stack and use them for various
+purposes. There may be multiple commands on a single line, separated by
+spaces. The number of spaces between commands or between commands and
+arguments is not critical, i.e. there may also be two or more spaces.
+
+Notation:
+	<arg>	: a sequence of characters which doesn't contain spaces or
+		  dots. If it does, use "...", '...' or |...| to enclose
+		  it.
+	<number>: an integer, positive or negative.
+	<filename>: same as <arg> (use quotes if it contains dots). May be
+		  absolute (starting with /) relative to your home directory
+		  (starting with ~/) or relative to somebody else's home
+		  directory (starting with ~name/).
+	<function>: Name of an LPC-function.
+
+Commands:
+	pl <arg>	: push player object with name <arg>.
+	lv <arg>	: push living object with name <arg>.
+	me		: push yourself.
+	here		: push the location you are in.
+	ob <filename>	: load & push the object denoted by <filename>.
+	<filename>	: same as ob <filename> if it begins with / or ~.
+	new <filename>	: clone & push the object denoted by <filename>.
+			  Thereafter move it in your inventory.
+        creat <filename>: same as new, but don't move it into your inventory.
+	env		: push the environment of the TOS.
+	.<number>	: push the nth object in the inventory of the TOS.
+	.<arg>		: push the object with id <arg> in the inventory
+			  of the TOS.
+	=		: display the TOS.
+	info		: show detailed information about the TOS.
+	stk		: show the entire stack.
+	clr		: clear the entire stack.
+	dup		: duplicate the topmost stack entry.
+	pop		: discard the topmost stack entry.
+	swap		: exchange the two topmost stack entries.
+	over		: push the entry second from top.
+	@<number>	: push the stack entry corresponding to the number.
+			  The topmost one has number 0.
+	inv		: show the inventory of the TOS.
+	cln		: clean the inventory of the TOS. Players are not
+			  destructed.
+	clnof <arg>	: clean the inventory of the TOS of all objects with
+			  id <arg>.
+	rec		: perform the next inv, cln or clnof recursively.
+	norec		: use this to turn of rec again if you are not going
+			  to use one of inv, cln or clnof. inv, cln and clnof
+			  turn off rec automatically after being used.
+	dest		: destruct the TOS.
+	make		: destruct the TOS, then rebuild it from the file.
+			  Very useful when developing rooms.
+	mov		: move the object denoted by the stack entry that is
+			  second from top into the TOS.
+	[<function>]	: call <function> in the TOS.
+	[<function> <arg1> ... <argn>]
+			: call <function> in the TOS. arguments are separated
+			  by spaces. If the arguments themselves contain
+			  spaces, enclose them by "...", '...' or |...|.
+			  Use @<number> to supply objects on the stack
+			  as arguments.
+	result		: push the result of the function call if it is
+			  an object.
+	desc		: toggle whether short descriptions are shown when
+			  displaying objects.
+	#		: the contents of the stack will be restored after
+			  performing the next command on the same line.
+			  Since all commands which manipulate stack objects
+			  except inv and = pop them, this might come in handy.
+	<<number>	: used to push variable <number>. May range from
+			  0 to 9.
+	><number>	: store the TOS in variable <number>.
+	vars		: display all variables.
+
+The following two commands can only be used as the first command on a line.
+
+	?		: there are two usages for the ?-command. Followed
+			  by text it will execute the commands in it.
+			  Simply hitting ?<CR> will put you in interactive
+			  mode. Leave this mode by typing .<CR>. Lines
+			  typed in will be executed by the lens and the
+			  stack shown afterwards.
+
+	grant		: Two forms are possible:
+			  'grant access to <name>' allows the player with
+			  name <name> to use the lens.
+			  'grant no access' denies the access to all players
+			  below level 21.
+
+Examples:
+	me /room/church mo
+		: go to the church.
+	pl demos ob ~demos/workroom mo
+		: move the player demos to his workroom.
+	here make
+		: update and load the room you are in, keeping the inventory
+		  untouched.
+	here dup rec cln make
+		: same, this time cleaning up before.
+	me.bag inv
+		: show the contents of the bag you have.
+	me."bag 2" inv
+		: same for the second bag.
+	me [short]
+		: shows your short description.
+	me [query_attack] result [stop_fight]
+		: pacify your opponent.
+	pl demos [attacked_by @0]
+		: make Demos attack himself.
+	lv troll pl demos [attacked_by @1]
+		: demos is attacked by a troll.
+	here.troll here.demos [attacked_by @1]
+		: same effect if they're both here.
+	new /obj/level_list
+		: get yourself a copy of the top ten list.
+	pl ereborn clnof flea
+		: help poor ereborn to get rid of those fleas.
+	pl demos env [long]
+		: show the room where demos is.
+	here rec inv pop
+		: show the inventory of your current location and clean
+		  up the stack afterwards.
+	me #[set_str 20] #[set_dex 20] #[set_int 20] #[set_con 20]
+		: set all your stats to the maximum possible value.
+	me [set_title "the new wizard"]
+		: changes your title accordingly.
+	pl demos env >4
+		: store the room Demos is in in variable 4.
+	<4 [query_dest_dir]
+		: get it again and list the possible exits.
+	me rec inv .4 inv
+		: get a recursive listing of yourself, then a non-recursive
+		  one of the 4th item in your inventory
+	me.4.1 info
+		: show some information of the first item within the fourth
+		  item in your inventory.
+		  Please note that when doing a rec inv all the necessary
+		  information to find any object is displayed in front
+		  of it.
diff --git a/obj/tools/lupe.dok b/obj/tools/lupe.dok
new file mode 100644
index 0000000..28e23fe
--- /dev/null
+++ b/obj/tools/lupe.dok
@@ -0,0 +1,209 @@
+ALLGEMEINES:
+	Die Lupe benutzt einen Stapel (Stack), um Objekte zu bearbeiten.
+	Einige Befehle schieben Objekte auf den Stapel, andere wiederum
+	holen sie wieder vom Stapel herunter und benutzen sie fuer die
+	verschiedensten Zwecke.
+	Man kann in einer Zeile mehrere Kommandos gleichzeitig angeben
+	(durch Leerzeichen getrennt).
+	Eine Zeile wird immer von links nach rechts abgearbeitet.
+
+SCHREIBWEISEN:
+	<arg>	- Eine Reihe von Zeichen, die weder Leerzeichen noch Punkte
+		  (.) enthaelt. Falls doch Leerzeichen oder Punkte in <arg>
+		  auftauchen, muss man die Zeichenkette zwischen "...",
+		  '...' oder |...| einschliessen.
+	<nr>	- Eine (positive oder negative) ganze Zahl.
+	<filename> - Ein Dateiname. Fuer ihn gilt das Gleiche wie fuer <arg>:
+		  Wenn er Punkte enthaelt, muss man ihn zwischen "...",
+		  '...' oder |...| einschliessen.
+		  Es sind alle Variationen moeglich, die man auch aus der
+		  Magiershell kennt: absolute Dateinamen (beginnen mit "/"),
+		  Dateinamen relativ zum eigenen Homeverzeichnis ("~/")
+		  oder zum Homeverzeichnis eines anderen Magiers ("~name/"),
+		  Dateinamen in einer Region ("+region/") und Dateinamen
+		  relativ zum aktuellen Verzeichnis (alles andere ;).
+	<func>	- Name einer LPC-Funktion.
+	TOS	- Das Objekt ganz oben auf dem Stapel (Top Of Stack).
+
+DER STAPEL:
+	Bei dem Stapel handelt es sich um einen LIFO-Stapel (Last In - First
+	Out), d.h. das Objekt, das zuletzt auf den Stapel geschoben wurde,
+	wird als erstes bearbeitet.
+	Bei der Bearbeitung wird der TOS in der Regel vom Stapel entfernt
+	(Ausnahmen: =, stk und #), der Stapel wird also im Laufe der Zeit
+	wieder kleiner.
+	Der Stapel kann maximal zehn Elemente aufnehmen. Dieses Limit wird
+	man bei "normalem Betrieb" allerdings selten erreichen.
+	Sollte es trotzdem einmal eng werden, stehen einem noch zehn
+	Variablen zur Verfuegung, in denen man zusaetzlich Objekte (vom
+	Stapel) ablegen kann.
+
+BEFEHLE:
+	Es gibt drei unterschiedliche Befehlsgruppen:
+	a) Befehle, die ein Objekt auf den Stapel schieben;
+	b) Befehle, die mit Objekten auf dem Stapel arbeiten;
+	c) Befehle, die ohne den Stapel auskommen.
+	Einige Befehle setzen voraus, das der TOS ein Lebewesen oder gar ein
+	Spielerobjekt ist; andere Befehle sind erst ab einem bestimmten
+	Magierlevel zugaenglich. Dies ist bei den entsprechenden Befehlen
+	jeweils vermerkt.
+
+    Diese Befehle schieben ein neues Objekt auf den Stapel:
+	creat <filename> Macht das Gleiche wie 'new', das Objekt wird aller-
+			dings nicht in Dein Inventory gesteckt.
+	here		Das Objekt, in dem man gerade steht, auf den Stapel
+			schieben.
+	lv <arg>	Schiebt das Lebewesen mit dem living_name <arg> auf
+			den Stapel. ACHTUNG! Das muss dann nicht unbedingt
+			das Lebewesen sein, das man dort eigentlich haben
+			will! Es wird einfach das erste Objekt genommen,
+			dessen living_name passt! Wenn man etwas mit einem
+			NPC vorhat, der im gleichen Raum steht, spricht man
+			ihn besser mit here.name an.
+	me		Sich selbst auf den Stapel schieben.
+	new <filename>	Cloned das mit <filename> angegebene Objekt und
+			schiebt es auf den Stapel. Anschliessend befindet es
+			sich in Deinem Inventory.
+	ob <filename>	Laedt das Objekt, das mit <filename> angegeben ist,
+			und schiebt es auf den Stapel.
+	pl <arg>	Schiebt das Spielerobjekt mit dem Namen <arg> auf den
+			Stapel.
+			Es werden auch netztote Spieler berucksichtigt.
+	push <filename> Schiebt das Objekt, das mit <filename> angegeben ist,
+			auf den Stapel.
+	<filename>	Macht das gleiche wie 'ob', falls <filename> mit "/"
+			oder "~" beginnt.
+
+    Die naechsten Befehle schalten Optionen der Lupe an/aus/um:
+	desc		Die zusaetzliche Angabe der Kurzbeschreibung des
+			aktuellen Objektes wird unterdrueckt.
+	rec		Schaltet in den "rekursiv"-Modus um. Dieser Modus
+			wird von folgenden Befehlen genutzt:
+			'inv', 'cln' und 'clnof'
+			Nach Ausfuehrung eines dieser Befehle wird der
+			"rekursiv"-Modus automatisch wieder abgestellt.
+	norec		Stellt den "rekursiv"-Modus "von Hand" ab.
+
+    Diese Befehle schieben ebenfalls ein neues Objekt auf den Stapel. Sie
+    arbeiten dabei allerdings relativ zum TOS. Man muss also schon mindestens
+    ein Objekt auf den Stapel geschoben haben. Der alte TOS wird dabei in der
+    Regel entfernt.
+	.<nr>		Schiebt das <nr>te Objekt im Inventory des TOS auf den
+			Stapel.
+	.<arg>		Schiebt das Objekt mit der ID <arg> im Inventory des
+			TOS auf den Stapel.
+	<<nr>		Schiebt die Variable <nr> auf den Stapel. Als <nr>
+			sind Werte zwischen 0 und 9 moeglich.
+	@<nr>		Schiebt das <nr>te Objekt von oben noch einmal auf den
+			Stapel. Der alte TOS hat die Nummer 0. Weder der alte
+			TOS noch das verschobene Objekt werden vom Stapel ent-
+			fernt.
+			@1 ist analog zu 'over'.
+	copy		Legt eine Kopie des TOS an (inkl. aller Propertywerte)
+			und schiebt diese Kopie auf den Stapel. Die Kopie
+			befindet sich danach in Deinem Inventory.
+	dup		Schiebt den TOS doppelt auf den Stapel.
+	env		Schiebt das Objekt, in dem sich der TOS befindet, auf
+			den Stapel.
+	over		Schiebt das Objekt, das sich unter dem TOS befindet,
+			nochmal auf den Stapel. Dabei werden weder der alte
+			TOS noch das Objekt unter ihm entfernt.
+	result		Wenn in einem Objekt eine Funktion aufgerufen wurde,
+			und diese Funktion wieder ein Objekt zurueckgegeben
+			hat, so wird dieses Objekt auf den Stapel geschoben.
+	swap		Tauscht TOS und das darunter liegende Element gegen-
+			einander aus.
+
+    Die naechsten Befehle arbeiten mit den auf dem Stapel liegenden Objekten.
+    Dabei werden die bearbeiteten Objekte in der Regel vom Stapel entfernt.
+	><nr>		Speichert den TOS in der Variablen <nr>. Als <nr>
+			sind Werte zwischen 0 und 9 moeglich.
+	=		Zeigt den TOS. Dieser bleibt dabei unveraendert.
+	#		Der Stack bleibt ueber das naechste Kommando hinaus
+			erhalten.
+	inv		Zeigt das Inventory des TOS. Der TOS bleibt dabei
+			unveraendert.
+	cln		Entfernt alle Objekte aus dem Inventory des TOS. Es
+			werden allerdings keine Spieler entfernt.
+	clnof <arg>	Entfernt alle Objekte, die auf die ID <arg> anspre-
+			chen, aus dem Inventar des TOS.
+	clr		Loescht den gesamten Stack.
+	Dest		Der TOS wird zerstoert. Das geht allerdings NICHT,
+			wenn der TOS ein Spielerobjekt oder die Lupe selbst
+			ist.
+	dest		Wie Dest, allerdings wird zuerst TOS->remove() auf-
+			gerufen, um dem TOS die Moeglichkeit zu geben, noch
+			hinter sich aufzuraeumen.
+	dinfo		Gibt einige GameDriver-interne Informationen wie zB.
+			die Zeit des naechsten reset()-Aufrufs oder die an-
+			gesammelte evalcost des TOS aus.
+	disco		Unterbricht die Verbindug des TOS (muss ein aktiver
+			Spieler sein) zum MG (disconnect).
+	hl		Zeigt die Historyliste des TOS an (hierbei muss es
+			sich um einen Spieler handeln).
+			Dieser Befehl steht ab ELDER_LVL (50) zur Verfuegung.
+			!!!Fuer die Benutzung gilt das Gleiche wie fuer das
+			Snoopen!!!
+	idle		Gibt an, wie lange der TOS schon idlet. (Der TOS
+			sollte dabei natuerlich ein Spieler sein).
+	info		Gibt einige Informationen ueber den TOS aus.
+	inherit_list	Gibt den Vererbungsbaum des TOS aus (allerdings nicht
+			sehr baumfoermig ;)
+	inv		Zeigt das Inventory des TOS an.
+
+	make		Fuehrt ein Update des TOS durch. Geht nicht bei
+			Spielern! Dafuer gibt es renew.
+	minfo		Gibt einige Informationen ueber den Speicherverbrauch
+			des TOS aus.
+	move, mov, mo	Das Objekt, das unter dem TOS steht, wird in den TOS
+			gemoved. Beide Objekte werden dabei vom Stapel ent-
+			fernt.
+	_move, _mov, _mo, _mv
+			Das Objekt, das unter dem TOS steht, wird in den TOS
+			gemoved, ohne dass dort init() aufgerufen wird. Beide
+			Objekte werden dabei vom Stapel entfernt.
+			Dieser Befehl steht ab ARCH_LVL (60) zur Verfuegung.
+	pop		Entfernt den TOS.
+	renew		Aehnlich wie 'make', der TOS muss allerdings ein
+			Spieler sein. ACHTUNG! Man sollte nicht "einfach so"
+			Spieler 'renew'en, sondern nur dann, wenn es wirklich
+			gerechtfertigt ist!
+	scan, sc	Kombiniert 'stat' und finger. Der TOS muss ein Lebe-
+			wesen sein.
+	stat		Gibt Informationen ueber Zustand, Ausruestung, ge-
+			loeste Quests etc. des TOS aus. Der TOS muss dabei
+			ein Lebewesen sein.
+	stk		Zeigt den gesamten Stack. Dieser bleibt dabei unver-
+			aendert.
+	[<func>]	Ruft im TOS die Funktion <func> ohne Parameter auf.
+			Falls diese Funktion ein Objekt zurueckgibt, kann man
+			dieses anschliessend mit result auf den Stapel
+			schieben.
+	[<func> <arg1> ... <argn>]
+			Ruft im TOS die Funktion <func> mit den Parametern
+			<arg1> bis <argn> auf. Die Parameter sind mit Leer-
+			zeichen zu trennen. Enthalten die Parameter selbst
+			Leerzeichen, so sind sie in "...", '...' oder |...|
+			einzuschliessen.
+			Mittels @<nr> kann man auch Objekte, die sich auf dem
+			Stapel befinden, als Argumente angeben.
+
+    Die folgenden Befehle kommen ohne Objekte auf dem Stack aus:
+	call_out	Gibt eine Liste aller Objekte mit einem laufenden
+			call_out aus.
+	dump		Schreibt die Listen aller Objekte mit aktivem
+			heart_beat und aller Objekte mit laufendem call_out
+			in die Datai LISTS.LUPE in Dein Homeverzeichnis.
+	dumphists	Schreibt die Befehlshistory aller momentan einge-
+			loggten Spieler nach /log/ARCH/HD.
+			Dieser Befehl steht erst ab ARCH_LVL (60) zur
+			Verfuegung.
+	heart_beat	Gibt eine Liste aller Objekte mit aktivem heart_beat
+			aus.
+	rusage		Zeigt einige Infos ueber den Ressourcenge-/-verbrauch
+			des Muds an.
+	swho		Zeigt (so gut wie) alle aktiven Snoops.
+	vars		Zeigt die belegten Variablen an.
+
+----------------------------------------------------------------------------
+Last modified: Wed Jun 26 14:49:02 1996 by Wargon
\ No newline at end of file
diff --git a/obj/tools/merkzettel/merkzettel.c b/obj/tools/merkzettel/merkzettel.c
new file mode 100644
index 0000000..70447ab
--- /dev/null
+++ b/obj/tools/merkzettel/merkzettel.c
@@ -0,0 +1,1130 @@
+/*  /obj/tools/merkzettel.c
+    Merkzettel fuer Magier
+    Autor: Zesstra
+    ggf. Changelog:
+*/
+#pragma strict_types
+#pragma no_shadow
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/secure_thing";
+
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <defines.h>
+
+#define NEED_PROTOTYPES
+#include "merkzettel.h"
+#undef NEED_PROTOTYPES
+
+//Variablen
+mapping notizen;
+//mapping notizen_inaktiv;
+int maxusedID;
+static string owner; //UID des Besitzers, wird in P_AUTOLOAD gespeichert
+//static int *cache; //nach Prioritaet sortiertes Array der IDs
+
+void create() {
+  ::create();
+  SetProp(P_NAME, "Merkzettel");
+  SetProp(P_SHORT, "Ein Merkzettel");
+  AddId(({"merkzettel","zettel"}));
+  //Absicht, damit die Blueprint eine ID, P_NAME und P_SHORT hat.
+  if(!clonep(this_object())) return;
+  
+  SetProp(P_LONG,
+  "Was wolltest Du gleich nochmal schoenes proggen? ;-)\n"
+  "Befehlsliste:\n"
+  "zmerke/znotiz <string>  : Etwas auf den Merkzettel schreiben\n"
+  "zliste                  : 20 wichtigsten Notizen anzeigen\n"
+  "zliste <int>            : <int> wichtigste Notizen anzeigen\n"
+  "                          (unwichtigste, wenn <int> negativ)\n"
+  "                          Angabe von 0 oder 'alle' gibt alle aus\n"
+  "zliste <int> <filter>   : <int> wichtigste Notizen anzeigen\n"
+  "                          (Filter: 'aktiv', 'wartend'\n"
+  "                           'inaktiv', 'abgehakt', 'alle'|'0')\n"
+  "                          Ohne Angabe eines Filter gilt 'aktiv'\n"
+  "zstatus <int>           : Status von Notiz mit ID umschalten\n"
+  "zstatus <int> <int2>    : Status von Notiz mit ID auf <int> setzen \n"
+  "zloesche/zrm <int>      : Notiz mit ID <int> loeschen\n"
+  "zlies/zlese <int>       : Notiz mit ID <int> anzeigen\n"
+  "zdeplies <int> <int2>   : Abhaengigkeiten von <int> lesen, \n"
+  "                        : rekursiv, wenn <int2> != 0\n"
+  "zfertig/zhake <int>     : Notiz mit ID <int> abhaken (fertig)\n"
+  "zprio <int1> <int2>     : Notiz mit ID <int> mit Prioritaet <int2>\n"
+  "                          versehen.\n"
+  "zdep <int1> <int2>      : Notiz mit ID <int> soll von Notiz mit ID\n"
+  "                          <int2> abhaengen.\n"
+  "zhelfer <int1> <namen>  : Notiz mit ID <int> wird (mit)bearbeitet\n"
+  "                          von den angegebenen Personen\n"
+  "zergaenze <int> <text>  : haengt <text> an Notiz <int> an\n"
+  "zersetze <int> <text>   : ersetzt Text von Notiz <int> durch <text>\n"
+  "zexpire <int>           : alle abgehakten Notizen archivieren und loeschen,\n"
+  "                        : die aelter als <int> Tage sind.\n"
+  "zzeige <name>           ; zeige <name>, wie furchtbar viel Du zu tun hast\n"
+  "zwedel <name>	   : <name> aus der Ferne vor der Nase rumwedeln.\n"
+  );
+  SetProp(P_INFO,break_string("Ein Merkzettel fuer Magier. ;-)."));
+  SetProp(P_GENDER, MALE);
+  SetProp(P_ARTICLE, 1);
+  SetProp(P_WEIGHT, 0);
+  SetProp(P_VALUE, 0);
+  Set(P_AUTOLOADOBJ,#'query_autoloadobj,F_QUERY_METHOD);
+  Set(P_AUTOLOADOBJ,#'set_autoloadobj,F_SET_METHOD);
+  Set(P_AUTOLOADOBJ,PROTECTED,F_MODE_AS);
+  SetProp(P_NEVERDROP,1);
+  SetProp(P_NODROP,1);
+  AddCmd(({"zmerke","znotiz"}),"AddNotiz");
+  AddCmd(({"zliste","ztodo"}),"ListNotizen");
+  AddCmd(({"zloesche","zrm"}),"RemoveNotiz");
+  AddCmd(({"zfertig","zhake"}),"FinishNotiz");
+  AddCmd(({"zlies","zlese"}),"LiesNotiz");
+  AddCmd(({"zprio"}),"ChangePrio");
+  AddCmd(({"zdep"}),"ChangeDep");
+  AddCmd(({"zstatus"}),"ChangeStatus");
+  AddCmd(({"zersetze"}),"ErsetzeText");
+  AddCmd(({"zergaenze"}),"ErgaenzeText");
+  AddCmd(({"zhelfer","zdelegiere"}),"ChangeHelper");
+  AddCmd(({"zzeige"}),"ZeigeZettel");
+  AddCmd(({"zwedel"}),"WedelZettel");
+  AddCmd(({"zdeplies"}),"LiesDeps");
+  AddCmd(({"zexpire"}),"Expire");
+  //cache=({});
+  
+}
+
+//****************** AddCmd *****************************
+int AddNotiz(string str) {
+  
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  //ggf. nur Einlesen, Cache muss hier nicht aktualisiert werden ;-)
+  checkStatus(1);
+  str=(string)PL->_unparsed_args(0);  //kein parsing
+  if (!stringp(str) || !sizeof(str)) {
+    tell_object(PL,
+      "Was moechtest Du Dir denn notieren?\n");
+    return(1);
+  }
+  if (sizeof(str)>MAX_NOTE_LENGTH) {
+    tell_object(PL,BS(
+      sprintf("Deine Notiz ist mit %d Zeichen zu lang! Leider passen auf "
+             "Deinen Merkzettel nur % Zeichen drauf.",
+        sizeof(str),MAX_NOTE_LENGTH)));
+    return(1);
+  }
+  
+  //m_allocate gilt nur bis Funktionsende, daher hilft das in
+  //checkStatus leider nicht.
+  if (!mappingp(notizen) || !sizeof(notizen))
+    notizen=m_allocate(0,7);
+  
+  //freie ID finden (maxusedID+1 sollte die naechste frei sein, aber falls
+  //da mal was kaputtgegangen ist: Pruefen ;-) )
+  while(member(notizen,maxusedID)) {maxusedID++;}
+  //Reihenfolge: s. merkzettel.h
+  notizen+=([maxusedID:str;0;time();({});({});NOTE_ACTIVE;0]);
+/*  notizen+=([maxusedID]);
+  notizen[maxusedID,NOTE_TEXT]=str;
+  notizen[maxusedID,NOTE_PRIO]=0;
+  notizen[maxusedID,NOTE_STARTTIME]=time();
+  notizen[maxusedID,NOTE_DEPS]=({});
+*/
+  
+  //cache=({}); //cache invalidieren
+  //in Kuerze speichern, aber nicht fuer jede Aenderung speichern
+  save_me(60);
+  tell_object(PL,BS(
+      sprintf("Du schreibst sorgfaeltig Deine Notiz auf Deinen Merkzettel. "
+          "Es ist nun die %d. Notiz.",maxusedID)));
+  tell_room(environment(TI),
+    BS(sprintf("%s notiert sich etwas auf %s Merkzettel.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);
+}
+
+int LiesNotiz(string str) {
+  int id;
+  
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  if (!stringp(str) || !sizeof(str) || !id=to_int(str)) {
+    tell_object(PL,
+      "Welche Notiz moechtest Du lesen? Bitte eine ID angeben!\n");
+    return(1);
+  }
+  if (!checkStatus()) {
+    tell_object(PL,
+      "Es gibt keine Notiz, die Du lesen koenntest!\n");
+    return(1);
+  }
+  if (!member(notizen,id)) {
+    tell_object(PL,
+      sprintf("Es gibt keine Notiz mit der ID: %d\n",id));
+    return(1);
+  }
+  //Ausgabetext holen und ausgeben
+  tell_object(PL,sprintf("\nDu vertiefst Dich in Deinen Merkzettel und liest:\n%s\n",_LiesNotiz(id)));
+  tell_room(environment(TI),
+    BS(sprintf("%s liest etwas auf %s Merkzettel.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);
+}
+
+int RemoveNotiz(string str) {
+  int id;
+  
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  if (!stringp(str) || !sizeof(str) || !id=to_int(str)) {
+    tell_object(PL,
+      "Welche Notiz moechtest Du ausradieren? Bitte eine ID angeben!\n");
+    return(1);
+  }
+  if (!checkStatus()) {
+    tell_object(PL,
+      "Es gibt keine Notiz, die Du ausradieren koenntest!\n");
+    return(1);
+  }
+  if (!member(notizen,id)) {
+    tell_object(PL,
+      sprintf("Es gibt keine Notiz mit der ID: %d\n",id));
+    return(1);
+  }
+  //direkt Loeschen, keine Kopie, daher m_delete()
+  m_delete(notizen,id);
+  //in Kuerze speichern, aber nicht fuer jede Aenderung speichern
+  save_me(60);
+  //cache invalidieren
+  //cache=({});
+  tell_object(PL,
+    sprintf("Sorgfaeltig radierst Du Notiz %d von Deinem Merkzettel.\n",
+           id));
+  tell_room(environment(TI),
+    BS(sprintf("%s radiert sorgfaeltig etwas von %s Merkzettel.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);
+}
+
+int FinishNotiz(string str) {
+  int id;
+  mixed liste;
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  if (!stringp(str) || !sizeof(str) || !id=to_int(str)) {
+    tell_object(PL,
+      "Welche Notiz moechtest Du abhaken? Bitte eine ID angeben!\n");
+    return(1);
+  }
+  if (!checkStatus()) {
+    tell_object(PL,
+      "Es gibt keine Notiz, die Du abhaken koenntest!\n");
+    return(1);
+  }
+  if (!member(notizen,id)) {
+    tell_object(PL,
+      sprintf("Es gibt keine Notiz mit der ID: %d\n",id));
+    return(1);
+  }
+  if(notizen[id,NOTE_STATUS]==NOTE_FINISHED) {
+    tell_object(PL,
+      sprintf("Notiz %d ist schon abgehakt!\n",id));
+    return(1);
+  }
+  liste=getUnresolvedDeps(id);  //liste ist Array von ints
+  if (sizeof(liste)) {
+    liste=map(liste,#'to_string);  //liste ist Array von strings
+    tell_object(PL,BS(sprintf("Du kannst Notiz %d nicht abhaken, da noch "
+        "nicht erledigte Abhaengigkeiten existieren. IDs: %s.",
+        id,CountUp(liste))));
+    return(1); 
+  }
+  notizen[id,NOTE_STATUS]=NOTE_FINISHED;
+  notizen[id,NOTE_CLOSETIME]=time();
+  tell_object(PL,BS(
+    sprintf("Du malst zufrieden einen schoenen grossen Haken hinter "
+        "die Notiz Nr. %d. Hach - was bist Du nun zufrieden! :-)\n",id)));
+  tell_room(environment(TI),
+    BS(sprintf("%s hakt erfreut etwas auf %s Merkzettel ab.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);
+}
+
+int ListNotizen(string str) {
+  int zahl,notizzahl,i,id,filterstate;
+  string txt;
+  string *arr;
+  status invers;
+  
+  if(!objectp(TI)) return(0);
+  str=(string)PL->_unparsed_args(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  if (!notizzahl=checkStatus()) {
+    tell_object(PL,BS(
+      "Dein Merkzettel ist bluetenweiss und leer! Wie kommt das denn?? "
+      "Hast Du nix zu tun?"));
+    return(1);
+  }
+  //Argumente parsen
+  if(stringp(str) && sizeof(str))
+    arr=explode(str," ");
+  if (pointerp(arr)) arr-=({""}); //doppelte leerzeichen im String
+  if (pointerp(arr) && sizeof(arr)>=2) {
+    zahl=to_int(arr[0]);
+    switch(arr[1]) {
+      case "aktiv":
+        filterstate=NOTE_ACTIVE; break;
+      case "inaktiv":
+        filterstate=NOTE_INACTIVE; break;
+      case "wartend":
+      case "pending":
+        filterstate=NOTE_PENDING; break;
+      case "abgehakt":
+      case "fertig":
+      case "abgeschlossen":
+        filterstate=NOTE_FINISHED; break;
+      case "alle":
+        filterstate=0; break;
+      default: 
+        filterstate=to_int(arr[1]);
+        break;
+    }
+  }
+  else if (pointerp(arr) && sizeof(arr)==1) {
+    zahl=to_int(arr[0]);
+    filterstate=NOTE_ACTIVE;
+  }
+  else {
+    //Voreinstellungen
+    zahl=20;
+    filterstate=NOTE_ACTIVE;
+  }
+  //1. Argument "all" oder "alle" ist: alle anzeigen
+  if (pointerp(arr) && sizeof(arr) && stringp(arr[0]) &&
+      sizeof(arr[0]) && arr[0]=="alle")
+    zahl=notizzahl;  //alle anzeigen
+  
+  //wenn die gewuenschte Zahl die Anzahl gespeicherter
+  //Notizen ueberschreitet (oder 0 ist)
+  if(!zahl || zahl>notizzahl) zahl=notizzahl;
+  
+  //wenn 1. Argument negativ: Liste in umgekehrter Reihenfolge
+  if (zahl<0) {
+    invers=1;
+    zahl=zahl*-1;
+  }
+  txt=sprintf("\n|%:9|s|%:56|s|%:7|s|\n","ID","Notiztext","Prio.");
+  txt+=sprintf("%:78'-'s\n","-");
+  //alle passenden Notizen ermitteln
+  arr=filter(m_indices(notizen),#'note_filter,filterstate);
+  //sortieren
+  arr=sort_array(arr,#'sort_prio);
+  if (zahl>sizeof(arr)) zahl=sizeof(arr);
+  //ausgeben
+  for(i=0;i<zahl;i++) {
+    if (!invers)
+      id=to_int(arr[i]);
+    else
+      //von hinten
+      id=to_int(arr[<i+1]); // i+1 ist wirklich Absicht. ;-)
+    txt+=sprintf("| %:7|d | %:54-s | %:5|d |\n",
+            id,
+	    //nur bis zum ersten \n anzeigen
+	    explode(notizen[id,NOTE_TEXT],"\n")[0],
+            notizen[id,NOTE_PRIO]);
+  }
+  txt+=sprintf("%:78'-'s\n\n","-");
+  tell_object(PL,txt);
+  tell_room(environment(TI),
+    BS(sprintf("%s wirft einen Blick auf %s Merkzettel.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihren" : "seinen") 
+              )),({TI}));
+  return(1);
+}
+
+int ChangeDep(string str) {
+  int id;
+  string *arr;
+  int *deps;
+  
+  notify_fail("Bitte eine ID und eine min. eine Abhaengigkeit angeben!\n");
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return(0);
+  
+  if (!checkStatus()) {
+    tell_object(PL,BS(
+      "Dein Merkzettel ist bluetenweiss und leer! Wie kommt das denn?? "
+      "Hast Du nix zu tun?"));
+    return(1);
+  }
+  arr=explode(str," ");
+  arr-=({""});  // "" entfernen, wichtig. ;-)
+  if (sizeof(arr)<2)
+    return(0);
+  id=to_int(arr[0]);
+  arr-=({arr[0]});  //eigene ID loeschen
+  if (sizeof(arr)<1)
+    return(0);  //mehrfach die eigene ID gilt nicht.;-)
+  if (!member(notizen,id)) {
+    tell_object(PL,
+      sprintf("Es gibt keine Notiz mit der ID: %d\n",id));
+    return(1);
+  }
+  //dies updated direkt die Abhaengigkeiten in 'notizen'!
+  map(arr,#'update_deps,id);
+  save_me(60);
+  tell_object(PL,BS(
+      sprintf("Du aenderst die Abhaengigkeiten von Notiz %d.\n",
+              id)));
+  tell_room(environment(TI),
+    BS(sprintf("%s kritzelt auf %s Merkzettel herum.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);
+}
+
+int ChangePrio(string str) {
+  int id, prio;
+  string* arr;
+  
+  notify_fail("Bitte eine ID und eine neue Prioritaet angeben!\n");
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return(0);
+  
+  if (!checkStatus()) {
+    tell_object(PL,BS(
+      "Dein Merkzettel ist bluetenweiss und leer! Wie kommt das denn?? "
+      "Hast Du nix zu tun?"));
+    return(1);
+  }
+  arr=explode(str," ");
+  if (sizeof(arr)>2) {
+    notify_fail("Bitte nur eine ID und eine neue Prioritaet angeben!");
+    return(0);
+  }
+  else if(sizeof(arr)==2) {
+    id=to_int(arr[0]);
+    prio=to_int(arr[1]);
+  }
+  else
+    return(0);
+
+  if (!member(notizen,id)) {
+    tell_object(PL,
+      sprintf("Es gibt keine Notiz mit der ID: %d\n",id));
+    return(1);
+  }
+  notizen[id,NOTE_PRIO]=prio;
+  //cache=({}); //cache invalidieren
+  save_me(60);
+  tell_object(PL,BS(
+      sprintf("Du aenderst die Prioritaet von Notiz %d auf %d.",
+              id,notizen[id,NOTE_PRIO])));
+  tell_room(environment(TI),
+    BS(sprintf("%s kritzelt auf %s Merkzettel herum.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);
+}
+
+int ChangeHelper(string str) {
+  int id;
+  string *arr;
+  
+  notify_fail("Bitte eine ID und eine min. einen Namen angeben!\n");
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return(0);
+  
+  if (!checkStatus()) {
+    tell_object(PL,BS(
+      "Dein Merkzettel ist bluetenweiss und leer! Wie kommt das denn?? "
+      "Hast Du nix zu tun?"));
+    return(1);
+  }
+  arr=explode(str," ");
+  arr-=({""});  // "" entfernen, wichtig. ;-)
+  if (sizeof(arr)<2)
+    return(0);
+  id=to_int(arr[0]);
+  arr-=({arr[0]});  //eigene ID loeschen
+  if (sizeof(arr)<1)
+    return(0);  //mehrfach die eigene ID gilt nicht.;-)
+  if (!member(notizen,id)) {
+    tell_object(PL,
+      sprintf("Es gibt keine Notiz mit der ID: %d\n",id));
+    return(1);
+  }
+  notizen[id,NOTE_HELPER]=arr;
+  save_me(60);
+  tell_object(PL,BS(
+      sprintf("Mitarbeiter von Notiz %d geaendert.",id)));
+  tell_room(environment(TI),
+    BS(sprintf("%s kritzelt auf %s Merkzettel herum.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);
+}
+
+int ChangeStatus(string str) {
+  int id, state;
+  string *arr;
+  
+  notify_fail(BS(
+      "Bitte min. eine ID angeben, um den Status umzuschalten oder "
+      "eine ID und den neuen Status (1==Aktiv, 2==Wartend, "
+      "-1==Inaktiv/Pause)"));
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return(0);
+  if (!checkStatus()) {
+    tell_object(PL,BS(
+      "Dein Merkzettel ist bluetenweiss und leer! Wie kommt das denn?? "
+      "Hast Du nix zu tun?"));
+    return(1);
+  }
+  arr=explode(str," ");
+  if (sizeof(arr)>2) {
+    notify_fail("Bitte nur eine ID und einen neuen Status angeben!");
+    return(0);
+  }
+  else if(sizeof(arr)==2) {
+    id=to_int(arr[0]);
+    state=to_int(arr[1]);
+  }
+  else if(sizeof(arr)==1) {
+    id=to_int(arr[0]);
+  }
+  else
+    return(0);
+  
+  if (!member(notizen,id)) {
+    tell_object(PL,
+      sprintf("Es gibt keine Notiz mit der ID: %d\n",id));
+    return(1);
+  }
+  if(member(NOTE_STATES,state)==-1) {
+    if (notizen[id,NOTE_STATUS]==NOTE_ACTIVE) {
+      tell_object(PL,BS(
+          sprintf("%d ist ein unbekannter Status, setzt Notiz %d auf "
+              "'Inaktiv'.",state,id)));
+      state=NOTE_INACTIVE;
+    }
+    else {
+      tell_object(PL,BS(
+          sprintf("%d ist ein unbekannter Status, setzt Notiz %d auf "
+              "'Aktiv'.",state,id)));
+      state=NOTE_ACTIVE;
+    }
+  }
+  notizen[id,NOTE_STATUS]=state;
+  save_me(60);
+  tell_object(PL,BS(
+      sprintf("Status von Notiz %d geaendert.",id)));
+  tell_room(environment(TI),
+    BS(sprintf("%s kritzelt auf %s Notizzettel herum.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);
+}
+
+int ErsetzeText(string str) {
+  string *arr;
+  int id;
+  
+  notify_fail("Bitte eine ID und einen neuen Text angeben!\n");
+  if(!objectp(TI)) return(0);
+  
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return(0);
+  
+  if (!checkStatus()) {
+    tell_object(PL,BS(
+      "Dein Merkzettel ist bluetenweiss und leer! Wie kommt das denn?? "
+      "Hast Du nix zu tun?"));
+    return(1);
+  }
+  arr=explode(str," ");
+  arr-=({""});  // "" entfernen
+  if (sizeof(arr)<2)
+    return(0);
+  id=to_int(arr[0]);
+  str=implode(arr[1..]," ");  //text wiederherstellen, ohne erstes Element
+  if (!sizeof(str)) return(0);
+  if (!member(notizen,id)) {
+    tell_object(PL,
+      sprintf("Es gibt keine Notiz mit der ID: %d\n",id));
+    return(1);
+  }
+  notizen[id,NOTE_TEXT]=str;
+  save_me(60);
+  tell_object(PL,BS(
+      sprintf("Text von Notiz %d ersetzt.",id)));
+  tell_room(environment(TI),
+    BS(sprintf("%s radiert zuerst etwas auf %s Notizzettel herum und "
+        "schreibt anschliessend sorgfaeltig etwas neues auf.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);
+}
+
+int ErgaenzeText(string str) {
+  string *arr;
+  int id;
+  
+  notify_fail("Bitte eine ID und einen Text angeben!\n");
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return(0);
+  
+  if (!checkStatus()) {
+    tell_object(PL,BS(
+      "Dein Merkzettel ist bluetenweiss und leer! Wie kommt das denn?? "
+      "Hast Du nix zu tun?"));
+    return(1);
+  }
+  arr=explode(str," ");
+  arr-=({""});  // "" entfernen
+  if (sizeof(arr)<2)
+    return(0);
+  id=to_int(arr[0]);
+  str="\n";
+  str+=implode(arr[1..]," ");  //text wiederherstellen
+  if (!member(notizen,id)) {
+    tell_object(PL,
+      sprintf("Es gibt keine Notiz mit der ID: %d\n",id));
+    return(1);
+  }
+  notizen[id,NOTE_TEXT]+=str;
+  save_me(60);
+  tell_object(PL,BS(
+      sprintf("Text von Notiz %d ergaenzt.",id)));
+  tell_room(environment(TI),
+    BS(sprintf("%s ergaenzt etwas auf %s Notizzettel.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);
+}
+
+int ZeigeZettel(string str) {
+    //anderen zeigen, wie furchtbar viel man zu tun hat.
+  object pl;
+  int count;
+  int *ids;
+  
+  notify_fail("Wem willst Du Deinen Merkzettel zeigen?\n");
+  if (!objectp(TI)) return(0);
+  if (!environment() || !environment(environment()))
+     return(0);
+  if (!stringp(str) || !sizeof(str)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+ if (!checkStatus()) {
+    tell_object(PL,BS(
+      "Dein Merkzettel ist bluetenweiss und leer! Wie kommt das denn?? "
+      "Hast Du nix zu tun? Du willst anderen Leuten doch bestimmt nicht "
+      "zeigen, dass Du nix zu tun hast, oder?"));
+    return(1);
+  }
+ if(!objectp(pl=present(str,environment(environment()))))
+     return(0);
+ if (!living(pl))
+     return(0);
+
+ ids=filter(m_indices(notizen),#'note_filter,NOTE_ACTIVE);
+ tell_object(TI,BS("Du zeigst "+pl->Name(WEM)+" voller Stolz deinen vollen "
+       "Merkzettel."));
+ tell_room(environment(environment()),BS(sprintf(
+	 "%s zeigt %s %s Merkzettel.",
+       TI->Name(WER),pl->Name(WEM),TI->QueryPossPronoun(MALE,WEN,SINGULAR))),
+     ({TI,pl}));
+ switch(sizeof(ids)) {
+     case 0..10:
+       tell_object(pl,BS(sprintf("%s zeigt Dir voller Stolz %s Merkzettel. Du "
+	       "wirfst einen schnellen Blick darueber und zaehlst %d "
+	       "Notizen. Na, soviel ist das ja zum Glueck noch nicht.",
+	       TI->Name(WER),TI->QueryPossPronoun(MALE,WEN,SINGULAR),
+	       sizeof(ids))));
+       break;
+    case 11..20:
+        tell_object(pl,BS(sprintf("%s zeigt Dir voller Stolz %s Merkzettel. Du "
+	       "wirfst einen schnellen Blick darueber und zaehlst %d "
+	       "Notizen. Oh, das ist ja schon so einiges!",
+	       TI->Name(WER),TI->QueryPossPronoun(MALE,WEN,SINGULAR),
+	       sizeof(ids))));
+       break;
+    default:
+        tell_object(pl,BS(sprintf("%s zeigt Dir voller Stolz %s Merkzettel. Du "
+	       "wirfst einen schnellen Blick darueber und zaehlst %d "
+	       "Notizen. Puuuh. %s hat ganz schoen viel zu tun! In "
+	       "Dir regt sich leises Mitleid.",
+	       TI->Name(WER),TI->QueryPossPronoun(MALE,WEN,SINGULAR),
+	       sizeof(ids),TI->Name(WER) )));
+       break;
+ }
+ return(1);
+ }
+
+int WedelZettel(string str) {
+    object pl;
+    string rwedel;
+  //wedelt anderen mit dem Zettel vor der Nase herum.
+  notify_fail("Wem willst Du Deinen Merkzettel zeigen?\n");
+  if (!objectp(TI)) return(0);
+  if (!environment() || !environment(environment()))
+     return(0);
+  if (!stringp(str) || !sizeof(str)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  if (!objectp(pl=find_player(str))) return(0);
+  if (!present(pl,environment(environment())))
+    rwedel="aus der Ferne";
+      
+  tell_object(PL,BS("Du wedelst "+pl->Name(WEM) 
+	+ (rwedel?" "+rwedel:"") 
+	+" heftig mit Deinem Merkzettel vor der Nase herum."));
+  tell_object(pl,BS(PL->Name(WER)+ " wedelt Dir " 
+	+ (rwedel?rwedel+" ":"") +"heftig mit "
+	+PL->QueryPossPronoun(MALE,WEM,SINGULAR)
+	+" Merkzettel vor der Nase herum."));
+  tell_room(environment(pl),BS(PL->Name(WER) + " wedelt "
+	+pl->Name(WEM) + (rwedel?" "+rwedel:"") + " heftig mit "
+       +PL->QueryPossPronoun(MALE,WEM,SINGULAR)
+       +" Merkzettel vor der Nase herum."),({PL,pl}));
+  return(1);
+}
+
+int LiesDeps(string str) {
+  //erstmal (rekursiv) alle Abhaengigkeiten einer Notiz ermitteln
+  string *arr;
+  int id, rec, i;
+  int *liste;
+  
+  notify_fail("Bitte eine ID und ggf. '1' fuer rekursive Suche angeben!\n");
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return(0);
+  
+  if (!checkStatus()) {
+    tell_object(PL,BS(
+      "Dein Merkzettel ist bluetenweiss und leer! Wie kommt das denn?? "
+      "Hast Du nix zu tun?"));
+    return(1);
+  }
+  arr=explode(str," ");
+  arr-=({""});  // "" entfernen
+  if (sizeof(arr)==1) {
+    id=to_int(arr[0]);
+    rec=0;  //Standard: keine Rekursion
+  }
+  else if (sizeof(arr)>=2) {
+    id=to_int(arr[0]);
+    rec=to_int(arr[1]);
+  }
+  else {
+    return(0);
+  }
+  if (!member(notizen,id)) {
+    tell_object(PL,
+      sprintf("Es gibt keine Notiz mit der ID: %d\n",id));
+    return(1);
+  }
+  //Notizen, von denen id abhaengt, holen
+  //und nur aktive Notizen behalten
+  liste=filter(getDeps(id,rec),#'note_filter,NOTE_ACTIVE);
+  //nach Prioritaet sortieren
+  liste=sort_array(liste,#'sort_prio);
+  //dann mal ausgeben.
+  i=sizeof(liste);
+  tell_object(PL,BS(sprintf("Du vertiefst Dich in die Abhaenhigkeiten von Notiz: %d\n",id)));
+  while(i--) {
+    tell_object(PL,_LiesNotiz(liste[i])+"\n");
+  }
+  tell_room(environment(TI),
+    BS(sprintf("%s liest etwas auf %s Merkzettel.\n",
+        capitalize((string)TI->name(WER)),
+        (((int)TI->QueryProp(P_GENDER))==FEMALE ? "ihrem" : "seinem") 
+              )),({TI}));
+  return(1);    
+}
+
+int Expire(string str) {
+  float age;
+  int sekunden, i;
+  int *liste;
+  string res;
+  
+  notify_fail("Bitte angeben, wie alt zu loeschende Notizen sein sollen (in Tagen)!\n");
+  if(!objectp(TI)) return(0);
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  str=(string)PL->_unparsed_args(0);
+  if (!stringp(str) || !sizeof(str))
+    return(0);
+  age=to_float(str);
+  if (age<=0) return(0);
+  sekunden=time() - (int)(age*86400);
+  if (!checkStatus()) {
+    tell_object(PL,BS(
+      "Dein Merkzettel ist bluetenweiss und leer! Wie kommt das denn?? "
+      "Hast Du nix zu tun?"));
+    return(1);
+  }
+  //abgehakte Notizen ermitteln
+  liste=filter(m_indices(notizen),#'note_filter,NOTE_FINISHED);
+  //Notizen ermitteln, die lang genug abgehakt sind.
+  liste=filter(liste,#'aelter_als,sekunden);
+  if (!sizeof(liste)) {
+    tell_object(PL,BS("Keine Notizen gefunden, die lang genug abgehakt sind."));
+    return(1);
+  }
+  res=sprintf("Archivierte Notizen vom %s\n\n",dtime(time()));
+  for (i=sizeof(liste);i--;) {
+    //Infos noch einmal ausgeben und loeschen
+    res+=_LiesNotiz(liste[i])+"\n";
+    m_delete(notizen,liste[i]);
+  }
+  res+="\n";
+  write_file(ARCHIVE(owner),res);
+  tell_object(PL,BS(sprintf(
+    "%d alte Notizen wurden nach %s archiviert und geloescht.",
+       sizeof(liste),ARCHIVE(owner))));
+  return(1);
+}
+
+//
+static string _LiesNotiz(int id) {
+  //Funktion verlaesst sich drauf, dass id existiert. ;-)
+  string res,txt;
+  res=BSL(sprintf("%-11s: %d","Notiz-Nr.",id));
+  res+=break_string(notizen[id,NOTE_TEXT],78,"Notiztext  : ",
+                             BS_INDENT_ONCE|BS_LEAVE_MY_LFS);
+  res+=sprintf("%-11s: %d\n","Prioritaet",notizen[id,NOTE_PRIO]);
+  //wenn nicht aktiv: Status anzeigen
+  if (notizen[id,NOTE_STATUS]!=NOTE_ACTIVE) {
+    switch(notizen[id,NOTE_STATUS]) {
+      case NOTE_INACTIVE:
+          txt="Inaktiv/Pause";
+          break;
+      case NOTE_FINISHED:
+          txt="Abgeschlossen"; 
+          break;
+      case NOTE_PENDING:
+          txt="Warte auf Rueckmeldung"; 
+          break;
+      default: txt=sprintf("%d",notizen[id,NOTE_STATUS]); break;
+    }
+    res+=sprintf("%-11s: %s\n","Status",txt);
+    //wenn abgeschlossen: Endezeit anzeigen
+    if (notizen[id,NOTE_STATUS]==NOTE_FINISHED)
+      res+=sprintf("%-11s: %s\n","Endezeit",
+                  dtime(notizen[id,NOTE_CLOSETIME]));
+  }
+  res+=sprintf("%-11s: %s\n","Merkzeit",dtime(notizen[id,NOTE_STARTTIME]));
+  //nur wenn Abhaengigkeiten: anezeigen
+  if (pointerp(notizen[id,NOTE_DEPS]) && sizeof(notizen[id,NOTE_DEPS])) {
+    txt=implode(map(notizen[id,NOTE_DEPS],#'to_string)," ");
+    res+=sprintf("%-11s: %s\n","abh. von",txt);
+  }
+  //nur wenn Helfer: anezeigen
+  if (pointerp(notizen[id,NOTE_HELPER]) && sizeof(notizen[id,NOTE_HELPER]))
+    res+=sprintf("%-11s: %s\n","Helfer",
+                implode(notizen[id,NOTE_HELPER]," "));
+  return(res);
+}
+
+//int. Helfer
+static int note_filter(int id,int filterstate) {
+    //kein filter -> passt immer
+    if (!filterstate) return(1);
+    if (member(notizen,id) &&
+    notizen[id,NOTE_STATUS]==filterstate)
+    return(1);
+    return(0);
+}
+
+static int aelter_als(int id,int zeit) {
+  //liefert 1 zurueck, wenn Schliesszeit der Notiz laenger als 'sekunden' her ist, sonst 0.
+  if (notizen[id,NOTE_CLOSETIME]<zeit) return(1);
+  return(0);
+}
+
+static string update_deps(string str,int id) {
+  //wandelt str nach int um
+  //schaut, ob dep positiv und noch nicht bekannt ist, wenn ja: 
+  //zu deps hinzufuegen
+  //wenn dep < 0 und bekannt ist: aus deps entfernen
+  //Rueckgabewert ist egal. ;-)
+  int dep;
+  dep=to_int(str);
+  if (dep>0 && member(notizen[id,NOTE_DEPS],dep)==-1)
+    notizen[id,NOTE_DEPS]+=({dep});
+  else if (dep<0 && member(notizen[id,NOTE_DEPS],dep*-1)!=-1)
+    notizen[id,NOTE_DEPS]-=({dep*-1});
+  return("");
+}
+
+static int *getDeps(int id, int rec) {
+  //ermittelt (ggf. rekursiv bei rec!=0) die Abhaengigkeiten einer Notiz
+  int i, *liste;
+  liste=notizen[id,NOTE_DEPS];
+  //jetzt ggf. noch rekursiv weiterholen
+  if (rec)  {
+    i=0;
+    while(i<sizeof(liste)) {
+        //nicht schreien. ;-)
+        if (get_eval_cost()<100000) {
+            tell_object(PL,"Fehler: Zu lange Rekursion bei "
+		"Ermitteln der Abhaengigkeiten, Abbruch\n");
+            return(liste);
+        }
+        //Abhaengigkeiten der anderen Abhaengigkeiten hinten anhaengen.
+        liste+=notizen[liste[i],NOTE_DEPS];
+        i++;
+    }
+  }
+  return(liste);
+}
+
+static int *getUnresolvedDeps(int id) {
+  //liefert die Anzahl der nicht abgehakten Notizen, von denen eine 
+  //Notiz abhaengig ist (rekursiv)
+  int *liste;
+  //Abhaengigkeiten rekursiv holen
+  liste=getDeps(id,1);
+  //nicht abgehakte zurueckgeben
+  return(liste-filter(liste,#'note_filter,NOTE_FINISHED));
+}
+
+void init() {
+  ::init();
+  //wenn kein Env, Env kein magier oder nicht der Eigentuemer ist: weg
+  if (!objectp(environment()) || !IS_LEARNER(environment())
+     || (stringp(owner) && getuid(environment())!=owner) ) {
+    if (find_call_out("remove") == -1)
+      call_out("remove",1,1);
+    return;
+  }
+  //beim erstmaligen Bewegen in einen Magier wird der als Eigentuemer
+  //registriert. Danach kommt diese Info aus P_AUTOLOAD
+  if (!stringp(owner))
+    owner=getuid(environment());
+}
+
+static nomask status check_allowed() {
+  //Zugriff auf die Daten fuer den Eigentuemer
+  if(objectp(this_interactive()) &&
+     getuid(this_interactive())==owner
+     && !process_call())
+    return(1);
+  return(0);
+}
+
+protected int restore_me() {
+  //laedt Savefile im Home vom Magier
+  if(!stringp(owner)) return(0);
+  if(!restore_object(SAVEFILE(owner))) {
+    maxusedID=1;
+    notizen=0;
+    //cache=({});
+    return(0);
+  }
+  return(1);
+}
+
+varargs protected void save_me(int delay) {
+  //speichert Savefile im Home vom Magier
+  if(!stringp(owner)) return;
+  //wenn maxusedID==0 wurde der zettel noch nicht ordentlich initialisiert
+  //bzw. restauriert. In diesem Fall wuerde ein leeres Savefile geschrieben
+  if (maxusedID==0)
+    restore_me();
+  if(!delay)
+    //direkt speichen. ;-)
+    save_object(SAVEFILE(owner));
+  else {
+    //verzoegert speichern, wenn schon ein call_out laeuft, nehm ich den
+    if(find_call_out(#'save_me)==-1)
+      call_out(#'save_me,delay);
+  }
+}
+
+varargs int remove(int silent) {
+  //erstmal speichern. ;-)
+  save_me();
+  return ::remove(silent);
+}
+
+static string query_autoloadobj() {
+  //in P_AUTOLOAD wird nur der Eigentuemer gespeichert
+  return owner;
+}
+
+static string set_autoloadobj(mixed arg)  {
+  //Eigentuemer aus P_AUTOLOAD restaurieren
+  if (stringp(arg))
+    owner=arg;
+  return(owner);
+}
+
+//Shadow unerwuenscht
+int query_prevent_shadow() {return(1);}
+
+varargs static int checkStatus(int nocache) {
+  //schaut, ob Notizen da, ob der Cache Ok ist, etc.
+  //liefert Anzahl der Notizen auf dem Merkzettel
+  
+  //wenn keine Notizen da sind, schauen, ob das Savefile eingelesen werden
+  //kann
+  if (!mappingp(notizen) || !sizeof(notizen))
+    restore_me();
+  if (!mappingp(notizen)) return(0);
+/* Cache ist eigentlich nicht noetig und mir gerade zu unpraktisch, raus damit.  
+  //wenn keine Aktualisierung des Cache erwuenscht ist
+  if(nocache) return(0);
+  
+  if (sizeof(notizen)) {
+    if(!pointerp(cache) || !sizeof(cache)) {
+      //cache erneuern
+      cache=sort_array(m_indices(notizen),"sort_prio");
+    }
+  }
+  return(sizeof(cache)); //aequivalent zu sizeof(notizen)
+  */
+  return(sizeof(notizen));
+}
+
+static status sort_prio(int key1, int key2) {
+  // 1, falls falsche Reihenfolge,
+  // 0, wenn richtige
+  // und zwar _absteigend_, also hoechste Prio in liste[0] !
+  return(notizen[key1,NOTE_PRIO]<=notizen[key2,NOTE_PRIO]);
+}
+
+
+//*******************************************************
+
+// Debugging
+mapping QueryNotizen() {
+  
+  if(!check_allowed()) {
+    if(objectp(this_interactive()))
+      tell_object(this_interactive(),
+        BS("Dieser Zugriff auf den Merkzettel wurde nicht erlaubt."));
+    return(0);
+  }
+  checkStatus();
+  return(notizen);
+}
+
+//int *QueryCache() {return(cache);}
+
+int QuerySize() {return(sizeof(notizen));}
+
+string QueryOwner() {return(owner);}
diff --git a/obj/tools/merkzettel/merkzettel.h b/obj/tools/merkzettel/merkzettel.h
new file mode 100644
index 0000000..dca7724
--- /dev/null
+++ b/obj/tools/merkzettel/merkzettel.h
@@ -0,0 +1,79 @@
+#ifndef __ZESSTRA_MERKZETTEL_H__
+#define __ZESSTRA_MERKZETTEL_H__
+
+#define SAVEFILE(x) ("/players/"+x+"/.merkzettel")
+#define ARCHIVE(x) ("/players/"+x+"/merkzettel.archiv")
+
+#define NOTE_TEXT 0
+#define NOTE_PRIO 1
+#define NOTE_STARTTIME 2
+#define NOTE_DEPS 3
+#define NOTE_HELPER 4
+#define NOTE_STATUS 5
+#define NOTE_CLOSETIME 6
+
+//Status, >0 sind aktiv, <0 inaktiv
+#define NOTE_ACTIVE 1
+#define NOTE_PENDING 2
+#define NOTE_INACTIVE -1
+#define NOTE_FINISHED -2
+#define NOTE_STATES ({NOTE_ACTIVE,NOTE_PENDING,NOTE_INACTIVE,NOTE_FINISHED})
+
+//Sortierreihenfolge
+#define SORT_INVERSE 1
+
+//wieviel wird max.abgespeichert
+#define MAX_NOTE_LENGTH 1000
+//wieviel wird max. in der Liste angezeigt
+#define MAX_NOTE_LIST_LENGTH 50
+
+#define BS(x) break_string(x,78)
+#define BSI(x,y) break_string(x,78,y)
+#define BSL(x) break_string(x,78,"",BS_LEAVE_MY_LFS)
+#define BSIL(x,y) break_string(x,78,y,BS_LEAVE_MY_LFS)
+
+#define TI this_interactive()
+
+#ifdef NEED_PROTOTYPES
+//AddCmd
+int AddNotiz(string str);
+int ErsetzeText(string str);
+int ErgaenzeText(string str);
+int RemoveNotiz(string str);
+int FinishNotiz(string str);
+int ListNotizen(string str);
+int LiesNotiz(string str);
+int ChangeDep(string str);
+int ChangeStatus(string str);
+int ChangePrio(string str);
+int ChangeHelper(string str);
+int ZeigeZettel(string str);
+int WedelZettel(string str);
+int LiesDeps(string str);
+int Expire(string str);
+
+//von den AddCmd-Funs gerufen
+static string _LiesNotiz(int id);
+
+//sonst. Funktionen, z.B. fuer Debugzwecke
+mapping QueryNotizen();
+string QueryOwner();
+varargs int remove(int silent);
+
+//int. Funktionen
+static status sort_prio(int key1, int key2);
+static nomask status check_allowed();
+protected int restore_me();
+varargs protected void save_me(int delay);
+static string query_autoloadobj();
+static string set_autoloadobj(mixed arg);
+varargs static int checkStatus(int nocache);
+static string update_deps(string str,int id);
+static int note_filter(int id,int filterstate);
+static int aelter_als(int id,int zeit);
+static int *getUnresolvedDeps(int id);
+static int *getDeps(int id,int rec);
+
+#endif // NEED_PROTOTYPES
+
+#endif // __ZESSTRA_MERKZETTEL_H__
diff --git a/obj/tools/mudlink.c b/obj/tools/mudlink.c
new file mode 100644
index 0000000..209d546
--- /dev/null
+++ b/obj/tools/mudlink.c
@@ -0,0 +1,84 @@
+/*
+ * mudlink tool, offers several commands as an interface to the
+ * MUDLINK system.
+ *
+ * Commands offered:
+ * rpeers
+ * rwho <remotemud>
+ * rtell <player>@<remotemud>
+ *
+ * Deepthought, 19-Jan-93
+ */
+
+#include <config.h>
+#include <properties.h>
+
+inherit "/std/thing";
+
+create() {
+  string name, foo;
+
+  if (sizeof(old_explode(object_name(this_object()),"#")) != 2) return;
+
+  thing::create();
+  SetProp (P_SHORT,"a MUDLINK tool");
+  SetProp (P_LONG,
+ "This tool is used for communicating with MUDLINK. Commands are:\n"
++"rpeers                          Get a list of muds connected to MUDLINK\n"
++"rwho <mud>                      Show a list of players on the remote mud\n"
++"rtell <player>@<mud> <message>  Tell something to a player on another mud\n"
+          );
+  AddId ("tool");
+  AddId ("mudlink");
+  AddAdjective("mudlink");
+  SetInfo ("Mudlink Tool V0.1 by Deepthought");
+}
+
+_query_read_msg() { return 0; }
+
+/*-------------------------------------------------------------------------
+** Add and decode our commands.
+*/
+
+init() {
+  thing::init();
+  add_action("rpeers","rpeers");
+  add_action("rwho","rwho");
+  add_action("rtell","rtell");
+}
+
+rpeers() {
+  string u;
+  u = geteuid(this_player());
+  if (stringp(u))
+    tell_object(find_player("mudlink"),"rpeers "+u+"\n");
+  return 1;
+}
+
+rwho(str) {
+  string u;
+  if (!str || str == "") {
+    write("Usage: rwho <mud>\n");
+    return 1;
+  }
+  if (stringp(u = geteuid(this_player())))
+    tell_object(find_player("mudlink"),"rwho "+u+"="+str+"\n");
+  return 1;
+}
+
+rtell(str) {
+  string u;
+  string a, b, c, d;
+
+  if (!str || str == "") {
+    write("Usage: rtell <player>@<mud> <message>\n");
+    return 1;
+  }
+  if (sscanf(str, "%s@%s %s", a, b, c) != 3) {
+    write("Usage: rtell <player>@<mud> <message>\n");
+    return 1;
+  }
+  if (stringp(u = geteuid(this_player())))
+    tell_object(find_player("mudlink"),"rpage "+u+"@"+a+"@"+b+"="+c+"\n");
+  return 1;
+}
diff --git a/obj/tools/newstool.c b/obj/tools/newstool.c
new file mode 100644
index 0000000..0e67197
--- /dev/null
+++ b/obj/tools/newstool.c
@@ -0,0 +1,335 @@
+inherit "std/thing";
+
+#include <properties.h>
+#include <language.h>
+#include <config.h>
+#include <news.h>
+#include <defines.h>
+
+#define UA PL->_unparsed_args()
+
+void create()
+{
+  string str;
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_LONG,"Ein News-Gruppen-Tool.\n\
+Befehle: addg      <grname> <ownername>   : Gruppe erzeugen\n\
+         removeg   <grname>               : Gruppe loeschen\n\
+         addw      <grname> <username>    : Schreibberechtigten eintragen\n\
+         removew   <grname> <username>    : Schreibberechtigten austragen\n\
+         addd      <grname> <username>    : Loeschberechtigten eintragen\n\
+         removed   <grname> <username>    : Loeschberechtigten austragen\n\
+         addr      <grname> <username>    : Leseberechtigten eintragen\n\
+         remover   <grname> <username>    : Leseberechtigten austragen\n\
+         ginfo     <grname>               : Info ueber Gruppe abfragen\n\
+         setexpire <grname> <tage>        : Expiretime setzen\n\
+         setmaxmsg <grname> <anz>         : Maxmsg setzen\n\
+         setg <grname> <loeschlev> <schreiblev> <leselev> <maxmsg> <expire>\n");
+  str=capitalize(((str=getuid(this_player()))[<1]=='s'||str[<1]=='x'||
+		  str[<1]=='z')?str+="'":str+="s");
+  SetProp(P_SHORT,str+" NewsTool");
+  SetProp(P_NAME,str+" NewsTool");
+  SetProp(P_GENDER,NEUTER);
+  SetProp(P_ARTICLE,0);
+  AddId("newstool");
+  AddId("newsgruppentool");
+  AddId("news-gruppen-tool");
+  SetProp(P_AUTOLOADOBJ,1);
+  SetProp(P_NODROP,1);
+}
+
+void init()
+{
+  add_action("addg","addg");
+  add_action("removeg","removeg");
+  add_action("addw","addw");
+  add_action("removew","removew");
+  add_action("addd","addd");
+  add_action("removed","removed");
+  add_action("addr","addr");
+  add_action("remover","remover");
+  add_action("setg","setg");
+  add_action("setexpire","setexpire");
+  add_action("expire","expire");
+  add_action("ginfo","ginfo");
+  add_action("setmaxmsg","setmaxmsg");
+}
+
+int addr(string str)
+{
+  mixed *arr;
+  string st1, st2;
+  
+  if ((str=UA)=="" || sscanf(str,"%s %s",st1,st2) !=2){
+    write("addr <groupname> <username>\nAnschliessend darf der User unabhaengig vom Wizlevel die Gruppe lesen.\n");
+    return 1;
+  }
+  
+  arr=old_explode(st2," ")-({""});
+  switch ("/secure/news"->AddAllowed(st1,({}),({}),arr)){
+    case 0: write("Param err\n"); break;
+    case 1: write("Ok.\n"); break;
+    case -1: write("No permission\n"); break;
+    case -2: write("No such board\n"); break;
+  }
+  return 1;
+}
+
+int remover(string str)
+{
+  mixed *arr;
+  string st1, st2;
+
+  if (!(str=UA) || sscanf(str,"%s %s",st1,st2) !=2){
+    write("remover <gruppenname> <username>\nDanach darf der User die Gruppe nur noch lesen, wenn sein Wizlevel dies erlaubt.\n");
+    return 1;
+  }
+
+  arr=old_explode(st2," ")-({""});
+  switch ("/secure/news"->RemoveAllowed(st1,({}),({}),arr)){
+    case 0: write("Param err\n"); break;
+    case 1: write("Ok.\n"); break;
+    case -1: write("No permission\n"); break;
+    case -2: write("No such board\n"); break;
+  }
+  return 1;
+}
+
+int addd(string str)
+{
+  mixed *arr;
+  string st1, st2;
+
+  if (!(str=UA) || sscanf(str,"%s %s",st1,st2) !=2){
+    write("addd <gruppe> <user>\nUser darf Artikel loeschen (unabh vom Wizlevel)\n");
+    return 1;
+  }
+
+  arr=old_explode(st2," ")-({""});
+  	switch ("/secure/news"->AddAllowed(st1,arr,({}),({}))){
+    case 0: write("Param err\n"); break;
+    case 1: write("Ok.\n"); break;
+    case -1: write("No permission\n"); break;
+    case -2: write("No such board\n"); break;
+  }
+  return 1;
+}
+
+int removed(string str)
+{
+  mixed *arr;
+  string st1, st2;
+
+  if (!(str=UA) || sscanf(str,"%s %s",st1,st2) !=2){
+    write("removed <gruppe> <user>\nUser darf nur noch Artikel loeschen, wenn sein Wizlevel das erlaubt.\n");
+    return 1;
+  }
+
+  arr=old_explode(st2," ")-({""});
+  switch ("/secure/news"->RemoveAllowed(st1,arr,({}),({}))){
+    case 0: write("Param err\n"); break;
+    case 1: write("Ok.\n"); break;
+    case -1: write("No permission\n"); break;
+    case -2: write("No such board\n"); break;
+  }
+  return 1;
+}
+
+int addw(string str)
+{
+  mixed *arr;
+  string st1, st2;
+
+  if (!(str=UA) || sscanf(str,"%s %s",st1,st2) !=2){
+    write("addw <gruppe> <user>\nUser darf Artikel schreiben (unabh vom Wizlevel)\n");
+    return 1;
+  }
+
+  arr=old_explode(st2," ")-({""});
+  switch ("/secure/news"->AddAllowed(st1,({}),arr,({}))){
+    case 0: write("Param err\n"); break;
+    case 1: write("Ok.\n"); break;
+    case -1: write("No permission\n"); break;
+    case -2: write("No such board\n"); break;
+  }
+  return 1;
+}
+
+int removew(string str)
+{
+  mixed *arr;
+  string st1, st2;
+
+  if (!(str=UA) || sscanf(str,"%s %s",st1,st2) !=2){
+    write("removew <gruppe> <user>\nUser darf nur noch Artikel schreiben, wenn sein Wizlevel dies erlaubt.\n");
+    return 1;
+  }
+
+  arr=old_explode(st2," ")-({""});
+  switch ("/secure/news"->RemoveAllowed(st1,({}),arr,({}))){
+    case 0: write("Param err\n"); break;
+    case 1: write("Ok.\n"); break;
+    case -1: write("No permission\n"); break;
+    case -2: write("No such board\n"); break;
+  }
+  return 1;
+}
+
+int setg(string str)
+{
+  string name;
+  int dlevel, wlevel, rlevel, maxmessages, expire;
+
+  if (!(str=UA) ||
+      sscanf(str,"%s %d %d %d %d %d",name,dlevel,wlevel,rlevel,maxmessages,expire) != 6) {
+    write("setg <gruppe> <loeschlevel> <schreiblevel> <leselevel> <maxmessages> <expire>\nGruppenparameter setzen, Bedeutung (Defaultwerte in Klammern):\n\
+    name              : Der Name der Newsgroup\n\
+    loeschlevel  (20) : Wizlevel, ab dem User Artikel loeschen koennen\n\
+    schreiblevel ( 0) : Wizlevel, ab dem User Artikel schreiben koennen\n\
+    leselevel    ( 0) : Wizlevel, ab dem User Artikel lesen koennen\n\
+    maxmessages  (40) : Maximale Anzahl Artikel in einer Newsgruppe\n\
+    expire       (-1) : Expiretime in Tagen, -1 = nie\n");
+    return 1;
+  }
+  expire=86400*expire;
+  switch("/secure/news"->SetGroup(name, dlevel, wlevel, rlevel, maxmessages,expire)){
+    case 1: write("Ok\n"); break;
+    case 0: write("Param error.\n"); break;
+    case -1: write("No permission\n"); break;
+    case -2: write("No such group\n"); break;
+  }
+  return 1;
+}
+
+int setexpire(string str)
+{
+  string name;
+  int expire;
+  mixed *gr;
+
+  if (!(str=UA)||sscanf(str,"%s %d",name,expire)!=2)
+  {
+    write("setexpire <gruppe> <tage>\n");
+    return 1;
+  }
+  expire=86400*expire;
+  if (!pointerp((gr="/secure/news"->GetGroup(name))))
+  {
+    write("Get group error\n");
+    return 1;
+  }
+  switch("/secure/news"->SetGroup(name, gr[G_DLEVEL], gr[G_WLEVEL], gr[G_RLEVEL], gr[G_MAX_MSG],expire)){
+    case 1: write("Ok\n"); break;
+    case 0: write("Param error.\n"); break;
+    case -1: write("No permission\n"); break;
+    case -2: write("No such group\n"); break;
+  }
+  return 1;
+}
+
+int setmaxmsg(string str)
+{
+  string name;
+  int maxmsg;
+  mixed *gr;
+  
+  if (!(str=UA)||sscanf(str,"%s %d",name,maxmsg)!=2)
+  {
+    write("setexpire <gruppe> <tage>\n");
+    return 1;
+  }
+  if (!pointerp((gr="/secure/news"->GetGroup(name))))
+  {
+    write("Get group error\n");
+    return 1;
+  }
+  switch("/secure/news"->SetGroup(name, gr[G_DLEVEL], gr[G_WLEVEL], gr[G_RLEVEL], maxmsg, gr[G_EXPIRE])){
+    case 1: write("Ok\n"); break;
+    case 0: write("Param error.\n"); break;
+    case -1: write("No permission\n"); break;
+    case -2: write("No such group\n"); break;
+  }
+  return 1;
+}
+
+int ginfo(string str)
+{
+  mixed *gr;
+  int i;
+  
+  if (!(str=UA))
+  {
+    write("Usage: ginfo <gname>\n");
+    return 1;
+  }
+  if (!pointerp((gr="/secure/news"->GetGroup(str))))
+  {
+    write("Get group error\n");
+    return 1;
+  }
+  if (gr[11]==-1)
+  {
+    write("Group \""+gr[0]+"\" does not exist.\n");
+    return 1;
+  }
+        //         1         2         3         4         5         6         7 
+        //123456789012345678901234567890123456789012345678901234567890123456789012345678
+  printf("------------------------------------------------------------------------------\n");
+  
+  printf("Rubrik  : %s (%s) Savefile: %s\n\n",gr[0],capitalize(gr[1]),gr[2]);
+  printf("Messages: %i/%i    Expire: %i Tage.\n\n",
+		  (pointerp(gr[11])?sizeof(gr[11]):0),gr[10],gr[3]/86400);  
+  
+  printf( break_string((sizeof(gr[6])?"("+(CountUp(map(gr[6],#'capitalize)))+")":" "), 78, "Readers : "+gr[9]+" ",16));
+  
+  printf( break_string((sizeof(gr[5])?"("+(CountUp(map(gr[5],#'capitalize)))+")":" "), 78, "Writers : "+gr[8]+" ",16));
+  
+  printf( break_string((sizeof(gr[4])?"("+(CountUp(map(gr[4],#'capitalize)))+")":" "), 78, "Deleters: "+gr[7]+" ",16));
+  
+  printf("------------------------------------------------------------------------------\n");
+  return 1;
+}
+    
+int removeg(string str)
+{
+  if (!(str=UA))
+  {
+    write("removeg <gruppenname>\nGruppe LOESCHEN.\n");
+    return 0;
+  }
+  switch("/secure/news"->RemoveGroup(str)){
+    case 1: write("Ok. Bitte nicht vergessen, das Savefile der Newsgroup von Hand zu entfernen !\n"); break;
+    case 0: write("Param error.\n"); break;
+    case -1: write("No permission\n"); break;
+    case -2: write("No such group"); break;
+  }
+  return 1;
+}
+
+int addg(string str)
+{
+  string name, owner;
+
+  if (!(str=UA) || sscanf(str,"%s %s",name,owner)!=2)
+  {
+    write("addg <gruppe> <user>\nGruppe anlegen, <user> wird Besitzer der Gruppe\n");
+    return 1;
+  }
+  switch ("secure/news"->AddGroup(name, owner)){
+    case 1: write("Ok.\n"); break;
+    case 0: write("Param error.\n"); break;
+    case -1: write("No permission.\n"); break;
+    case -2: write("Group already there\n"); break;
+    case -3: write("Owner not found.\n"); break;
+    case -4: write("Savefile already in use.\n"); break;
+  }
+  return 1;
+}
+  
+int expire()
+{
+  "/secure/news"->expire_all();
+  write("Expiring startet.\n");
+  return 1;
+}
diff --git a/obj/tools/planttool.c b/obj/tools/planttool.c
new file mode 100644
index 0000000..240ccb9
--- /dev/null
+++ b/obj/tools/planttool.c
@@ -0,0 +1,41 @@
+inherit "/std/secure_thing";
+
+#include <properties.h>
+#include <wizlevels.h>
+#define PLANTMASTER "/secure/krautmaster"
+
+protected void create() 
+{
+   if (!clonep(this_object())) 
+     return;
+   ::create();
+   
+   SetProp(P_SHORT, "Das Kraeutertool");
+   Set(P_LONG, function string () { return 
+     "Das Kraeutertool hat folgende Befehle:\n"
+    +"- showplant [kategorie] (listet alle Pflanzen einer Kategorie auf.)\n"
+    +(IS_ARCH(this_player())?
+    "Folgende Befehle sind nur fuer EM+ verfuegbar:\n"
+    +"- showrooms [ID|all] (Listet alle Raeume auf, in denen das Kraut <ID>\n"
+    +"  zu finden ist, oder alle Raeume, in denen ein Kraut waechst.)\n"
+    +"- refresh (updatet alle Files).\n"
+    +"- addroom <plantId> <loadname> (traegt einen neuen Fundort ein).\n"
+    +"- delroom <loadname> (loescht alle Kraeuter fuer einen Fundort).\n"
+    +"- createfile <plantId> (erzeugt das Kraut als File).\n"
+    +"- cloneplant <plantId|plantFile> (clont das Kraut inv Inv).\n":
+    "");}, F_QUERY_METHOD);
+   SetProp(P_NAME, "Planttool");
+   SetProp(P_GENDER, NEUTER);
+   SetProp(P_AUTOLOADOBJ, 1);
+   SetProp(P_NODROP, 1);
+   SetProp(P_NEVERDROP, 1);
+   AddId(({"tool", "kraeutertool", "planttool"}));
+   AddCmd(({"cloneplant", "showplant", "showrooms", "refresh",
+            "addroom", "delroom", "createfile"}), "_plantmaster");
+}
+
+static int _plantmaster(string str)
+{
+   notify_fail("Unknown Function in plantmaster: _"+query_verb()+"()\n");
+   return call_other(PLANTMASTER, "_"+query_verb(), str);
+}
diff --git a/obj/tools/projekttool.c b/obj/tools/projekttool.c
new file mode 100644
index 0000000..f61a9dd
--- /dev/null
+++ b/obj/tools/projekttool.c
@@ -0,0 +1,212 @@
+//
+// Projecttool
+//
+
+#include "/p/service/mandragon/projektbrett/projectmaster.h"
+#include "/secure/wizlevels.h"
+#include <moving.h>
+
+inherit "/std/thing";
+string hilfe;
+static int changetime;
+static int suppressinfos;
+
+void create()
+{
+	if (!IS_CLONE(TO)) return;
+	::create();
+	if (!find_object(PROJECTMASTER)) call_other(PROJECTMASTER,"???");
+        
+	SetProp(P_NAME,"Schreibfeder");
+	SetProp(P_NODROP,"Du moechtest Deine Feder dann doch lieber behalten.\n");
+	SetProp(P_NEVERDROP,1);
+	SetProp(P_GENDER,FEMALE);
+	SetProp(P_SHORT,"Eine kleine magische Schreibfeder");
+        if (!changetime) changetime=1;
+        hilfe=BS("-----------------------------------------------------------------------------\n"
+                 "Diese Feder ermoeglicht es Dir nicht nur, von ueberall her auf die "
+                 "allseits beliebten Schwarzen Bretter fuer Projekte zuzugreifen, "
+                 "sondern ermoeglicht es Dir auch, selber Projekte dort zu "
+                 "veroeffentlichen.\n"
+                 "Folgende Kommandos koennen Dir dabei helfen:\n\n"
+                 "federhilfe              - Diese Uebersicht\n"
+                 "federdoku               - Dokumentation aller Befehle\n"
+                 "projekthilfe            - Uebersicht ueber die Brettbefehle\n"
+                 "AddProject              - Projekt anlegen\n"
+                 "RemoveProject <Nummer>  - Projekt entfernen\n"
+                 "ModifyProject <Nummer>  - Projekt aendern\n"
+                 "projektinfos ein|an|aus - Aenderungen (nicht mehr) anzeigen\n\n"
+                 "Bei Fragen, Bugs und/oder Typos wende Dich bitte vertrauensvoll an den\n"
+                 "Mandragon Deiner Wahl.\n"
+                 "---------------------------------------------------------------------------");
+	SetProp(P_LONG,
+		BS("Diese kleine, weisse, magisch glaenzende Feder liegt sanft " +
+	           "in Deiner Hand. Du spuerst foermlich, wie sie danach dringt, " +
+		   "Neues zu schaffen und die Welt zu vergroessern.\n\n")+hilfe);
+        AddId(({"schreibfeder","feder","projekttool"}));
+	AddCmd("federhilfe","hilfe_fun");
+	AddCmd("projekthilfe","board_help");
+	AddCmd("projektliste","board_list");
+        AddCmd("federdoku","doku_fun");
+	AddCmd(({"projektdetail","projektdetails"}),"board_long");
+	AddCmd(({"addproject","AddProject","ap"}),"addproj_fun");
+	AddCmd(({"removeproject","RemoveProject","rp"}),"remproj_fun");
+	AddCmd(({"modifyproject","ModifyProject","mp"}),"modproj_fun");
+        AddCmd("projektinfos","watch_fun");
+	TO->move(TP,M_NOCHECK);
+	return;
+}
+
+void _set_autoloadobj(mixed val)
+{
+  if (intp(val)) changetime=time();
+  else if (pointerp(val)&&sizeof(val)==2)
+  {
+    changetime=val[0];
+    suppressinfos=val[1];
+  }
+  return;
+}
+
+int *_query_autoloadobj() { return ({ changetime, suppressinfos }); }
+
+int doku_fun(string arg)
+{
+  TP->More(PROJECTDOKU,1);
+  return 1;
+}
+
+int board_help(string arg)
+{
+  PROJECTMASTER->BoardHelp();
+  return 1;
+}
+
+int board_list(string arg)
+{
+  arg=TP->_unparsed_args(1);
+  if (!sizeof(arg)) (PROJECTMASTER)->ShowList();
+  else PROJECTMASTER->ParseArgs(arg);
+  changetime=time();
+  return 1;
+}
+
+int board_long(string arg)
+{
+  int number;
+  arg=TP->_unparsed_args(1);
+  notify_fail("Syntax: projektdetail <PROJEKTNUMMER>\n");
+  if (!sizeof(arg)) return 0;
+  if (!(number=to_int(arg))||(arg!=to_string(number))) return 0;
+  notify_fail("Projektnummern koennen nur Zahlen groesser Null sein.\n");
+  if (number<1) return 0;
+  PROJECTMASTER->ShowLong(number);
+  return 1;
+}
+
+int hilfe_fun(string arg)
+{
+  tell_object(TP,hilfe);
+  return 1;
+}
+
+int board_fun(string arg)
+{
+  PROJECTMASTER->ParseArgs(TP->_unparsed_args(1));
+  return 1;
+}
+
+int addproj_fun(string arg)
+{
+  if (!IS_LEARNER(TP))
+  {
+    tell_object(TP,"Wie bist DU denn an die Feder gekommen?\n");
+    TO->remove();
+    return 1;
+  }
+  PROJECTMASTER->ChangeData(0);
+  return 1;
+}
+
+int remproj_fun(string arg)
+{
+  if (!IS_LEARNER(TP))
+  {
+    tell_object(TP,"Wie bist DU denn an die Feder gekommen?\n");
+    TO->remove();
+    return 1;
+  }
+  notify_fail("Syntax: removeproject NUMMER\n");
+  if (!sizeof(arg)||!intp(to_int(arg))||(arg!=to_string(to_int(arg)))) return 0;
+  PROJECTMASTER->DeleteData(to_int(arg));
+  return 1;
+}
+
+int modproj_fun(string arg)
+{
+  if (!IS_LEARNER(TP))
+  {
+    tell_object(TP,"Wie bist DU denn an die Feder gekommen?\n");
+    TO->remove();
+    return 1;
+  }
+  notify_fail("Syntax: modifyproject NUMMER\n");
+  if (!arg||!intp(to_int(arg))||(arg!=to_string(to_int(arg)))) return 0;
+  notify_fail("Es gibt kein Projekt mit der Nummer 0.\n");
+  if (to_int(arg)==0) return 0;
+  PROJECTMASTER->ChangeData(to_int(arg));
+  return 1;
+}
+
+int watch_fun(string arg)
+{
+  if (!IS_LEARNER(TP))
+  {
+    tell_object(TP,"Wie bist DU denn an die Feder gekommen?\n");
+    TO->remove();
+    return 1;
+  }
+  if (!sizeof(arg))
+  {
+    tell_object(TP,BS(sprintf("Du bekommst derzeit %sInformationen "
+                              "ueber Aenderungen am Projektbrett.",
+                              suppressinfos?"keine ":"")));
+    return 1;
+  }
+  notify_fail("Syntax: projektinfos <ein|an|aus>\n");
+  if (arg!="ein"&&arg!="an"&&arg!="aus") return 0;
+  if (arg=="aus")
+  {
+    suppressinfos=1;
+    tell_object(TP,BS("Du wirst jetzt keine Meldungen mehr ueber "
+                      "Aenderungen am Projektbrett erhalten.\n"));
+    return 1;
+  }
+  suppressinfos=0;
+  tell_object(TP,BS("Du bekommst ab sofort Meldungen ueber Aenderungen "
+                    "am Projektbrett.\n"));
+  return 1;
+}
+
+void init()
+{
+  if (!IS_LEARNER(TP)) {
+    if (find_call_out("remove") == -1)
+      call_out("remove",0);
+  }
+  else ::init();
+  return;
+}
+
+void reset()
+{
+  if (!environment())
+  {
+    destruct(TO);
+    return;
+  }
+  if (!suppressinfos&&changetime<(PROJECTMASTER->ChangeTime()))
+    tell_object(environment(),"Intuitiv spuerst Du, dass sich "
+                "die Aushaenge am Projektbrett geaendert haben.\n");
+  return;
+}
diff --git a/obj/tools/ptool.c b/obj/tools/ptool.c
new file mode 100644
index 0000000..c836cae
--- /dev/null
+++ b/obj/tools/ptool.c
@@ -0,0 +1,291 @@
+/*
+   Objekt zum Eintragen von Raeumen die Zaubertraenke enthalten beim
+   potionmaster. 
+*/
+
+#include <wizlevels.h>
+#include <properties.h>
+#include <language.h>
+
+inherit "std/thing";
+
+int dump();
+
+int secure()
+{
+  if (!previous_object()) return 0;
+  if (geteuid(previous_object())==ROOTID) return 1;
+  if (geteuid(previous_object()) != geteuid(this_interactive())) return 0;
+  if (this_interactive() != this_player()) return 0;
+  if (!ARCH_SECURITY) return 0;
+  return 1;
+}
+
+void create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT, "Potion-Tool");
+  SetProp(P_LONG, 
+     "Ein Tool zum Eintragen von Raumen in den Potionmaster.\n"+
+     "Folgende Befehle gibt es:\n"+
+     "ptadd <liste>     Addiert den ZT im Raum in Liste <liste>\n"+
+     "                  und aktiviert ihn gleichzeitig.\n"+
+     "ptset <liste>     Aktiviert den ZT im Raum und setzt ihn in\n"+
+     "                  die Liste <liste>.\n"+
+     "ptchange <nummer> Ersetzt den ZT <nummer> durch den ZT im Raum.\n"+
+     "ptact             Aktiviert einen deaktivierten ZT in seiner\n"+
+     "                  bisherigen Liste.\n"+
+     "ptdeact           Deaktiviert den ZT im Raum aus der ZTListe\n"+
+     "                  und verschiebt ihn nach 'deaktivierte ZTs'.\n"+
+     "ptinfo [para]     kein para:   Zeigt Infos ueber den ZT im aktu-\n"+
+     "                               ellen Raum.\n"+
+     "                  int para:    Zeigt Infos zum ZT para an.\n"+
+     "                  string para: Zeigt ZT-Infos des Spielers para.\n"+
+     "ptdump            Erzeugt ein Dump der aktuellen ZTListe und\n"+
+     "                  speichert diesen ab.\n\n");
+  SetProp(P_NAME, "PTool");
+  SetProp(P_GENDER, NEUTER);
+  AddId(({"tool", "ptool"}));
+  SetProp(P_NODROP, 1);
+  SetProp(P_AUTOLOADOBJ, 1);
+  AddCmd("ptadd", "add");
+  AddCmd("ptset", "setlist");
+  AddCmd("ptchange", "changepath");
+  AddCmd("ptact", "activate");
+  AddCmd("ptdeact", "deactivate");
+  AddCmd("ptinfo", "info");
+  AddCmd("ptdump", "dump");
+}
+
+int add(string number)
+{
+  int nr, next, active;
+  string room;
+  if (!secure())
+  { printf("Fehler: -1\n"); return -1; }
+  if (!query_once_interactive(environment()))
+  { printf("Fehler: -2\n"); return -2; }
+  if (!number)
+  { printf("Fehler: -3\n"); return -3; }
+  sscanf(number, "%d", nr);
+  if (!intp(nr))
+  { printf("Fehler: -4\n"); return -4; }
+  room = object_name(environment(environment(this_object())));
+  next = "/secure/potionmaster"->AddPotionRoom(room,nr);
+  if (next>0)
+    printf("Raum in Gesamtliste eingetragen, Nummer: %d\n", next-1);
+  else
+  {
+    printf("Raum konnte nicht eingetragen werden, Ergebnis: %d\n", next);
+    return 1;
+  }
+  // Nicht mehr noetig, Arathorn, 2013-Mai-30
+  //active = "/secure/potionmaster"->SetListNr(room, nr);
+  if (active>=0)
+  {
+    printf("Raum aktiviert in Liste %d\n", nr);
+    dump();
+  }
+  else
+    printf("Fehler beim Aktivieren, Ergebnis: %d\n", active);
+  return 1;
+}
+
+int changepath(string number)
+{
+  int nr,neu;
+  string room;
+  mixed roomold;
+  if (!secure())
+  { printf("Fehler: -1\n"); return -1; }
+  if (!query_once_interactive(environment()))
+  { printf("Fehler: -2\n"); return -2; }
+  if (!number)
+  { printf("Fehler: -3\n"); return -3; }
+  sscanf(number, "%d", nr);
+  if (!intp(nr))
+  { printf("Fehler: -4\n"); return -4; }
+  room = object_name(environment(environment(this_object())));
+  roomold = "/secure/potionmaster"->GetFilenameByNumber(nr);
+  if (roomold==-1)
+  { printf("Fehler: -5\n"); return -5; }
+  neu = "/secure/potionmaster"->ChangeRoomPath(roomold,room);
+  if (neu>0)
+    {
+      printf("ZT %d durch aktuellen Raum ersetzt.\n", neu);
+      dump();
+    }
+  else
+    printf("ZT %d konnte nicht ersetzt werden, Ergebnis: %d\n", nr,neu);
+  return 1;
+}
+
+int setlist(string number)
+{
+  int nr, next, active;
+  string room;
+  if (!secure())
+  { printf("Fehler: -1\n"); return -1; }
+  if (!query_once_interactive(environment()))
+  { printf("Fehler: -2\n"); return -2; }
+  if (!number)
+  { printf("Fehler: -3\n"); return -3; }
+  sscanf(number, "%d", nr);
+  if (!intp(nr) || nr<0 || nr>7)
+  { printf("Fehler: -4\n"); return -4; }
+  room = object_name(environment(environment(this_object())));
+  active = "/secure/potionmaster"->SetListNr(room, nr);
+  if (active>=0)
+  {
+    printf("Raum aktiviert in Liste %d\n", nr);
+    dump();
+  }
+  else
+    printf("Fehler beim Aktivieren, Ergebnis: %d\n", active);
+  return 1;
+}
+
+int activate()
+{
+  int nr, active;
+  string room;
+  
+  if (!secure())
+  { printf("Fehler: -1\n"); return -1; }
+  if (!query_once_interactive(environment()))
+  { printf("Fehler: -2\n"); return -2; }
+  room = object_name(environment(environment(this_object())));
+  active = "/secure/potionmaster"->ActivateRoom(room);
+  if (active>=0)
+    {
+      printf("Raum aktiviert in Liste %d\n", active);
+      dump();
+    }
+  else
+    printf("Fehler beim Aktivieren, Ergebnis: %d\n", active);
+  return 1;
+}
+
+int deactivate()
+{
+  int deactive;
+  string room;
+  
+  if (!secure())
+  { printf("Fehler: -1\n"); return -1; }
+  if (!query_once_interactive(environment()))
+  { printf("Fehler: -2\n"); return -2; }
+  room = object_name(environment(environment(this_object())));
+  deactive = "/secure/potionmaster"->DeactivateRoom(room);
+  if (deactive>=0)
+    {
+      printf("Raum deaktiviert in Liste %d\n",deactive);
+      dump();
+    }
+  else
+    printf("Fehler beim Deaktivieren, Ergebnis: %d\n", deactive);
+  return 1;
+}
+
+int info(string para)
+{
+  int info,nr;
+  string s;
+  object room,o;
+  mixed m,*potions;
+  if (!secure())
+  { printf("Fehler: -1\n"); return -1; }
+  if (!query_once_interactive(environment()))
+  { printf("Fehler: -2\n"); return -2; }
+  if (!para || para=="")
+    {
+      room = environment(environment(this_object()));
+      info = "/secure/potionmaster"->HasPotion(room);
+      if (info>=0)
+        {
+          printf("Raum hat ZT mit Nr:  %d\n\n",info);
+          nr = "/secure/potionmaster"->GetListByNumber(info);
+          if (nr>=0)
+            printf("ZT aktiv in Liste:   %d\n\n",nr);
+          else
+            {
+              nr = "/secure/potionmaster"->GetInactListByNumber(info);
+              if (nr>=0)
+                printf("ZT INaktiv in Liste: %d\n\n",nr);
+              else
+                printf("ZT INaktiv\n\n");
+            }
+          if (info>=0 && s=read_file("/secure/ARCH/ZT/"+info+".zt"))
+            {
+              write("Tip:\n"+s);
+            }
+        }
+      else
+        printf("Raum hat keinen ZT eingetragen.\n");
+    }
+  else
+    if (sscanf(para,"%d",info)==1)
+      {
+        printf("ZT mit Nummer: %d\n\n",info);
+        m = "/secure/potionmaster"->GetFilenameByNumber(info);
+        if (m!=-1)
+          {
+            write("Filename: "+m+"\n\n");
+            nr = "/secure/potionmaster"->GetListByNumber(info);
+            if (nr>=0)
+              printf("ZT aktiv in Liste: %d\n\n",nr);
+            else
+              {
+                nr = "/secure/potionmaster"->GetInactListByNumber(info);
+                if (nr>=0)
+                  printf("ZT INaktiv in Liste: %d\n\n",nr);
+                else
+                  printf("ZT INaktiv\n\n");
+              }
+            if (info>=0 && s=read_file("/secure/ARCH/ZT/"+info+".zt"))
+              {
+                write("Tip:\n"+s);
+              }
+          }
+        else
+          write("Kein ZT mit dieser Nummer bekannt.\n");
+      }
+    else
+      {
+        write("Spieler "+capitalize(para)+"\n\n");
+        if (o=find_player(para))
+          {
+            potions=sort_array(o->QueryProp(P_POTIONROOMS),#'>);
+            if (sizeof(potions))
+              {
+                s="";
+                for (nr=0;nr<sizeof(potions);nr++)
+                  s+=potions[nr]+", ";
+                write("Potionrooms:\n"+break_string(s[0..<3],78)+"\n");
+              }
+            else write("Spieler hat keine weiteren Potionrooms.\n");
+            potions=sort_array(o->QueryProp(P_KNOWN_POTIONROOMS),#'>);
+            if (sizeof(potions))
+              {
+                s="";
+                for (nr=0;nr<sizeof(potions);nr++)
+                  s+=potions[nr]+", ";
+                write("Bekannte Potionrooms:\n"+
+                      break_string(s[0..<3],78));
+              }
+            else write("Spieler hat keine bekannten Potionrooms.\n");
+          }
+        else
+          write("Kein Spieler mit diesem Namen anwesend.\n");
+      }
+  return 1;
+}
+
+int dump()
+{
+  "/secure/potionmaster"->DumpList();
+  printf("Liste wurde erzeugt.\n");
+  return 1;
+}
+
diff --git a/obj/tools/pubtool.c b/obj/tools/pubtool.c
new file mode 100644
index 0000000..a85afc9
--- /dev/null
+++ b/obj/tools/pubtool.c
@@ -0,0 +1,60 @@
+/* MorgenGrauen MudLib : Pubtool */
+
+#include <defines.h>
+#include <properties.h>
+#include <language.h>
+
+inherit "/std/thing";
+
+create()
+{
+    if (!clonep(ME))
+        return;
+    ::create();
+
+    SetProp(P_SHORT,"Ein Pubtool");
+    SetProp(P_LONG,
+    "Das Pubtool berechnet die maximal zulaessige Heilung einer Kneipe.\n"+
+  "\n"+
+  "Syntax: pubtest <P_ALCOHOL> <P_DRINK> <P_FOOD> <P_VALUE> <Rate> <Delay>\n"+
+  "\n"+
+  "Wertebereich:      >=0         >=0       >=0      >=0    1..20   0..\n"+
+  "\n");
+    SetProp(P_NAME,"Pubtool");
+    SetProp(P_GENDER,NEUTER);
+    SetProp(P_WEIGHT,0);
+    SetProp(P_VALUE,0); 
+    SetProp(P_NODROP,"Das Pubtool behaelst Du lieber bei Dir.\n");
+    SetProp(P_NEVERDROP,1);
+    SetProp(P_AUTOLOADOBJ,1);
+
+    AddId( "pubtool" );
+
+    AddCmd( "pubtest", "pubtest" );
+}
+
+int pubtest(string arg)
+{   int v,d,a,f,r,h,y;
+
+    notify_fail(
+  "Syntax: pubtest <P_ALCOHOL> <P_DRINK> <P_FOOD> <P_VALUE> <Rate> <Delay>\n");
+
+    if (!arg || !stringp(arg) ||
+        sscanf(arg,"%d %d %d %d %d %d",a,d,f,v,r,y)!=6)
+        return 0;
+
+    if (v<1 || d<0 || a<0 || f<0 || r<1 || r>20 || y<0)
+        return 0;
+
+    h=call_other("/secure/pubmaster","CalcMax",
+                 ([P_VALUE:v,P_ALCOHOL:a,P_DRINK:d,P_FOOD:f,
+                 "delay":y,"rate":r]),0);
+
+    printf("Berechnete Maximalheilung fuer:\n"+
+           "P_ALCOHOL = %3d    P_DRINK   = %3d    P_FOOD    = %3d \n"+
+           "P_VALUE   = %3d    Rate      = %3d    Delay     = %3d \n"+
+           "Ergebnis:\n"+
+           "Gesamt: %3d (HP+SP), aufgeteilt z.B. %3d HP und %3d SP.\n",
+           a,d,f,v,r,y,h,(h/2),(h%2?(h/2)+1:(h/2)));
+    return 1;
+}
diff --git a/obj/tools/questtool.c b/obj/tools/questtool.c
new file mode 100644
index 0000000..264fa7c
--- /dev/null
+++ b/obj/tools/questtool.c
@@ -0,0 +1,754 @@
+/*
+ *  The new ultimate quest administration tool
+ */
+
+/* AENDERUNGEN
+ * 2006-09-29 : Zook
+ *              Questtool an neue Felder "Attribute" und "Klasse"
+ *              angepasst.
+ * 2000-11-18 : Zook:
+ *		Questtool um neues Feld "Wartender Magier" des Questmaster
+ *		erweitert.
+ * 2000-11-18 : Zook:
+ *		Questtool um neues Feld "Gruppeneinordnung" des Questmaster
+ *		erweitert.
+ */
+
+inherit "std/thing";
+inherit "std/more";
+
+#include <defines.h>
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+#include "/secure/questmaster.h"
+
+#define UA this_interactive()->_unparsed_args()
+
+#define QNAME	0
+#define QP	1
+#define XP	2
+#define ALLOW	3
+#define INFO	4
+#define LEVEL	5
+#define CLASS	6
+#define ATTR	7
+#define WIZ	8
+#define SCNDWIZ 9
+#define END    10
+
+#pragma strong_types
+
+private void Load_NumKey();
+int  Add_Quest	     ( string str );
+void ReadNewQuest    ( string str );
+int  Remove_Quest    ( string str );
+int  Change_Quest    ( string str );
+int  Restore_Quest   ( string str );
+int  Set_Quest	     ( string str );
+int  Remove_Quest    ( string str );
+int  Query_Quest     ( string str );
+int  Query_Keys      ( string str );
+int  Query_Quests    ( string str );
+int  ActivateQuest   ( string str );
+int  DeactivateQuest ( string str );
+int  GetReturn	     ( string str );
+
+string name;
+int    qp;
+int    xp;
+int    level;
+int    need;
+string *allowed;
+string info;
+string wizard;
+string scndwizard;
+int    group;
+mapping num_key;
+
+object owner;
+int    counter;
+mixed  *savequest;
+string savekey;
+int    do_change;
+string changekey;
+
+void create()
+{
+  string str;
+
+  if(IS_BLUE(ME)) return;
+  thing::create();
+  SetProp(P_GENDER,NEUTER);
+  owner = PL;
+  AddId("questtool");
+  AddId("qt");
+  SetProp(P_ARTICLE,0);
+  SetProp(P_GENDER, NEUTER);
+  str=capitalize(((str=owner->query_real_name())[<1]=='s'||str[<1]=='x'||str[<1]=='z')?str+="'":str+="s");
+  SetProp(P_NAME, str+" QuestTool");
+  SetProp(P_SHORT, str+" QuestTool");
+  SetProp(P_LONG,
+   "Das QuestTool dient der Administration von Quests. (by Kirk, 10.11.94)\n"
+  +"implementierte Kommandos:\n"
+  +"  AddQ			    : Neuen Questeintrag einfuegen\n"
+  +"  RemoveQ	  <quest>	    : Questeintrag loeschen\n"
+  +"  ChangeQ	  <quest>	    : Questeintrag aendern\n"
+  +"  ReStoreQ			    : Zuletzt geloeschten oder geaenderten\n"
+  +"				    : Questeintrag restaurieren\n"
+  +"  SetQ	  <spieler> <quest> : Quest als 'geloest' markieren\n"
+  +"  DelQ	  <spieler> <quest> : Quest als 'nicht geloest' markieren\n"
+  +"  QueryQ	  <quest>	    : Questeintrag anzeigen\n"
+  +"  QueryK			    : Alle Keys und Indices anzeigen\n"
+  +"  QueryAllQ 		    : Alle Eintraege vollstaendig anzeigen\n"
+  +"  ActiVateQ   <quest>	    : Quest im Questmaster aktivieren\n"
+  +"  DeactiVateQ <quest>	    : Quest im Questmaster deaktivieren\n\n"
+  +"  fuer <quest> kann der Index verwendet werden, den QueryK liefert!\n\n"
+  +"  (Alle Kommandos koennen abgekuerzt werden.)\n"
+	+"   -> Beispiel: DeactiVateQ == DVQ == dvq == deactivateq\n");
+
+  SetProp(P_NEVERDROP, 1);
+  SetProp(P_NODROP, 
+  "Mit Hilfe des QuestTools koennen Quests eingetragen, oder geloescht\n"+
+  "werden. Du brauchst es vielleicht noch, deshalb wirfst Du es nicht weg.\n");
+  SetProp(P_NOBUY, 1);
+  AddCmd(({ "AddQ", "addq", "AQ", "aq" }),		   "Add_Quest");
+  AddCmd(({ "RemoveQ", "removeq", "RQ", "rq" }),	   "Remove_Quest");
+  AddCmd(({ "ChangeQ", "changeq", "CQ", "cq" }),	   "Change_Quest");
+  AddCmd(({ "ReStoreQ", "restoreq", "RSQ", "rsq" }),	   "Restore_Quest");
+  AddCmd(({ "SetQ", "setq", "SQ", "sq" }),		   "Set_Quest");
+  AddCmd(({ "DelQ", "delq", "DQ", "dq" }),		   "Del_Quest");
+  AddCmd(({ "QueryQ", "queryq", "QQ", "qq" }),		   "Query_Quest");
+  AddCmd(({ "QueryK", "queryk", "QK", "qk" }),		   "Query_Keys");
+  AddCmd(({ "QueryAllQ", "queryallq", "QAQ", "qaq" }),	   "Query_Quests");
+  AddCmd(({ "ActiVateQ", "activateq", "AVQ", "avq" }),	   "ActivateQuest");
+  AddCmd(({ "DeactiVateQ", "deactivateq", "DVQ", "dvq" }), "DeactivateQuest");
+  counter = QNAME;
+  savekey = "";
+  do_change = 0;
+  num_key = ([]);
+}
+
+int _query_autoloadobj() { return 1; }
+
+void init()
+{
+  object tp;
+
+  if(!(tp = PL) || tp != environment())
+    return;
+
+  thing::init();
+
+  if ( !IS_WIZARD(tp) || tp != owner )
+    return call_out("do_remove",1);
+}
+
+static void do_remove()
+{
+  write ( ME->QueryProp(P_SHORT)+" zerfaellt zu Staub.\n");
+  say ( ME->QueryProp(P_SHORT)+" zerfaellt in "+PL->name(WESSEN)+
+       " unbefugten Haenden zu Staub.\n");
+  call_out("remove",1);
+}
+
+private void Load_NumKey()
+{
+  string *keys;
+  int i;
+
+  if ( !(keys = (string*) QM->QueryAllKeys()) ) return;
+  keys=sort_array(keys,#'>);//')
+  for ( i = 0, num_key = ([]); i < sizeof(keys); i++ )
+    num_key += ([(i+1)+"":keys[i]]);
+}
+
+int Add_Quest(string str)
+{
+  counter = QNAME;
+  write ( "Neue Quest: (Abbruch mit '.' oder '~q')\n" );
+  write ( "Key :\n" ); 
+  input_to ( "ReadNewQuest" );
+  return 1;
+}
+
+void ReadNewQuest (string str)
+{
+  mixed *oldquest;
+  int errstat;
+  int active;
+
+  if ( str == "~q" || str == "." )
+  {
+    counter = QNAME;
+    if(do_change)
+    {
+      do_change = 0;
+      changekey = "";
+      savekey = "";
+      savequest = ({});
+    }
+    return;
+  }
+
+  switch ( counter )
+  {
+    case QNAME:
+      if((!str||!sizeof(str)) && do_change)
+	name = savekey;
+      else
+	name = str;
+      counter++;
+      write ( sprintf("Punkte (>0) [%d]:\n",
+		      (do_change?(int)savequest[0]:10)) ); 
+      input_to ( "ReadNewQuest" );
+      break;
+    case QP:
+      if((!str||!sizeof(str)))
+	if(do_change)
+	  qp = (int) savequest[Q_QP];
+	else
+	  qp = 10;
+      else
+	sscanf ( str, "%d", qp );
+      counter++;
+      write ( sprintf("Erfahrung (>=0) [%d]:\n",
+		      (do_change?(int)savequest[1]:qp*1000)));
+      input_to ( "ReadNewQuest" );
+      break;
+    case XP: 
+      if((!str||!sizeof(str)))
+	if(do_change)
+	  xp = (int) savequest[Q_XP];
+	else
+	  xp = qp * 1000;
+      else
+	sscanf ( str, "%d", xp );
+      counter++;
+      write ( sprintf("Filenamen file1[,file2,...]) %s:\n",
+		      (do_change?"["+implode((string *)savequest[2],",")+"]":
+		       "")) );
+      input_to ( "ReadNewQuest" );
+      break;
+    case ALLOW:
+      if((!str||!sizeof(str)) && do_change)
+	allowed = (string *) savequest[Q_ALLOWED];
+      else
+	allowed = old_explode(implode(old_explode(str,".c"),""),",");
+      counter++;
+      write ( sprintf("Info %s:\n", (do_change?
+				     "["+break_string("\b\b\b\b\b\b"+(string)
+						      savequest[3]+"]",78,6)
+				     :"")) ); 
+      input_to ( "ReadNewQuest" );
+      break;
+    case INFO:
+      if((!str||!sizeof(str)) && do_change)
+	info = (string) savequest[Q_HINT];
+      else
+	info = str;
+      counter++;
+      write ( sprintf("Stufe (-1 <=lev<=10) (-1 ist eine Seherquest) [%d]:\n",
+		      (do_change?(int)savequest[4]:0)) );
+      input_to ( "ReadNewQuest" );
+      break;
+    case LEVEL:
+      if((!str||!sizeof(str)))
+	if(do_change)
+	  level = (int) savequest[Q_DIFF];
+	else
+	  level = 0;
+	sscanf ( str, "%d", level );
+      counter++;
+      write ( sprintf("Klasse ([012345]) [%d]:\n",
+		      (do_change?(int)savequest[5]:0)) ); 
+      input_to ( "ReadNewQuest" );
+      break;
+    case CLASS:
+      if((!str||!sizeof(str)))
+	if(do_change)
+	  need = (int) savequest[Q_CLASS];
+	else
+	  need = 0;
+      else
+	sscanf ( str, "%d", need );
+      counter++;
+      write ( sprintf("Attribut [01234]\n(1=fleissig,2=heroisch,3=episch,4=legendaer) %s:\n",
+		      (do_change?"["+(string)savequest[9]+"]":"")) );
+      input_to ( "ReadNewQuest" );
+      break;
+    case ATTR:
+      if ((!str||!sizeof(str)))
+	if (do_change)
+	  group = (int) savequest[Q_ATTR];
+	else
+	  group = 0;
+      else
+	sscanf (str, "%d", group);
+      counter++;
+      write ( sprintf("Magier %s:\n",
+		      (do_change?"["+(string)savequest[7]+"]":"")) );
+      input_to ( "ReadNewQuest" );
+    break;	
+    case WIZ:
+      if((!str||!sizeof(str)) && do_change)
+	wizard = (string) savequest[Q_WIZ];
+      else
+	wizard = str;
+      wizard = lower_case(wizard);
+      counter++;
+      write ( sprintf("Wartender Magier %s:\n",
+		      (do_change?"["+(string)savequest[8]+"]":"")) );
+      input_to ( "ReadNewQuest" );
+      break;
+    case SCNDWIZ:
+      if ((!str||!sizeof(str)) && do_change)
+	scndwizard = (string) savequest[Q_SCNDWIZ];
+      else
+	scndwizard = str;
+      scndwizard = lower_case(scndwizard);
+      counter++;
+      write ( "Eintragen (j/n)?\n" );
+      input_to ( "ReadNewQuest" );
+      break;
+    case END:
+      counter=QNAME;
+
+      if ( str != "j" && str != "ja" && str != "y" && str != "yes" )
+	return;
+
+      active = 0;
+      if ( do_change && changekey && sizeof(changekey) )
+      {
+	oldquest = (mixed *) QM->QueryQuest ( changekey );
+
+	if ( !pointerp ( oldquest ) || !sizeof ( oldquest ) )
+	{
+	  write ( "Alten Eintrag nicht gefunden.\n" );
+	  return;
+	}
+
+	errstat = (int) QM->RemoveQuest( changekey );
+
+	do_change = 0;
+	changekey = "";
+
+	switch ( errstat )
+	{
+	  case	0: write ( "Zugriff auf alten Eintrag verweigert.\n" ); return;
+	  case -1: write ( "Parameterfehler beim Loeschen.\n" ); return;
+	  case -2: write ( "Alten Eintrag nicht gefunden.\n" ); return;
+	  default: write ( "Alter Eintrag geloescht.\n" );
+	}
+	active = oldquest[6];
+	savequest = oldquest;
+	savekey   = changekey;
+      }
+
+      // add new Quest deactivated by default and keep old active flag
+      // if changing an existing entry
+      errstat= (int)QM->AddQuest(name,qp,xp,allowed,info,level,need,active,
+				 wizard, scndwizard, group);
+
+      switch ( errstat )
+      {
+	case  0: write ( "Zugriff verweigert.\n" ); break;
+	case -1: write ( "Key ungueltig oder sizeof(key) < 5.\n" ); break;
+	case -2: write ( "QP ungueltig oder < 1.\n" ); break;
+	case -3: write ( "XP ungueltig.\n" ); break;
+	case -4: write ( "Filename(n) ungueltig.\n" ); break;
+	case -5: write ( "Info ungueltig.\n" ); break;
+	case -6: write ( "Stufe ungueltig oder < 0 oder > 20.\n" ); break;
+	case -7: write ( "Aktiv-Flag < 0 oder > 1.\n" ); break;
+	case -8: write ( "Magiername ungueltig.\n" ); break;
+	case -9: write ( "Magiername des wartenden Magiers ungueltig.\n"); break;
+	case -10: write ("Falsche Gruppeneinordnung.\n"); break;
+	default: write ( "Eintrag eingefuegt.\n" );
+      }
+    default:
+      return;
+  }
+}
+
+int Remove_Quest ( string str )
+{
+  mixed *oldquest;
+  string newstr;
+  int errstat;
+
+  if ( !(str=UA) )
+  {
+    write ( "Syntax: RemoveQ <name>\n" );
+    return 1;
+  }
+
+  Load_NumKey();
+
+  if ( !(newstr = num_key[str+""]) )
+    newstr = str;
+
+  oldquest = (mixed *) QM->QueryQuest ( newstr );
+
+  if ( !pointerp ( oldquest ) || !sizeof ( oldquest ) )
+  {
+    write ( "Keine Quest dieses Namens gefunden.\n" );
+    return 1;
+  }
+
+  errstat = (int) QM->RemoveQuest( newstr );
+
+  switch ( errstat )
+  {
+    case  0: write ( "Zugriff verweigert.\n" ); break;
+    case -1: write ( "Parameterfehler.\n" ); break;
+    case -2: write ( "Quest nicht gefunden.\n" ); break;
+    default: write ( "Quest entfernt.\n" );
+  }
+
+  savequest = oldquest;
+  savekey   = newstr;
+
+  return 1;
+}
+
+int Change_Quest ( string str )
+{
+  mixed *oldquest;
+  string newstr;
+
+  if ( !(str=UA) )
+  {
+    write ( "Syntax: ChangeQ <quest>\n" );
+    return 1;
+  }
+
+  Load_NumKey();
+
+  if ( !(newstr = num_key[str+""]) )
+    newstr = str;
+
+  oldquest = (mixed *) QM->QueryQuest ( newstr );
+
+  if ( !pointerp( oldquest ) || !sizeof ( oldquest ) )
+  {
+    write ( "Keine Quest dieses Namens gefunden.\n" );
+    return 1;
+  }
+
+  do_change = 1;
+  changekey = newstr;
+  savekey = newstr;
+  savequest = oldquest;
+
+  write ( "Aktueller Eintrag:\n");
+  write ( "Key              : "+newstr+"\n" );
+  write ( "Punkte           : "+oldquest[Q_QP]+"\n" );
+  write ( "Erfahrung        : "+oldquest[Q_XP]+"\n" );
+  write ( "Filenamen        : "+implode(oldquest[Q_ALLOWED],",")+"\n" );
+  write ( "Info             : "+oldquest[Q_HINT]+"\n" );
+  write ( "Stufe            : "+oldquest[Q_DIFF]+"\n" );
+  write ( "Klasse           : "+QCLASS_STARS(oldquest[Q_CLASS])+"\n" );
+  write ( "Attribut         : "+QATTR_STRINGS[oldquest[Q_ATTR]]+"\n" );
+  write ( "Magier           : "+capitalize(oldquest[Q_WIZ])+"\n" );
+  write ( "Wartender Magier : "+capitalize(oldquest[Q_SCNDWIZ])+"\n" );
+
+
+  write ( "\nNeue Quest:     (mit '.' oder '~q' kann abgebrochen werden)\n" );
+  write ( sprintf("Key [%s]:\n", savekey) );
+  input_to ( "ReadNewQuest" );
+
+  return 1;
+}
+
+int Restore_Quest ( string str )
+{
+  int errstat;
+
+  if ( !savekey || !sizeof(savekey) )
+  {
+    write ( "\nTut mir leid!\n" );
+    write ( "Nichts zum Restaurieren gefunden ...\n" );
+    return 1;
+  }
+
+  errstat = 
+	(int)QM->AddQuest( savekey, (int) savequest[0], (int) savequest[1],
+			  (string *) savequest[2], (string) savequest[3],
+			  (int) savequest[4], (int) savequest[5],
+			  (string) savequest[6], (int) savequest[7],
+			  (string) savequest[8] );
+
+  switch ( errstat )
+  {
+    case  0: write ( "Zugriff verweigert.\n" ); break;
+    case -1: write ( "Key ungueltig oder sizeof(key) < 5.\n" ); break;
+    case -2: write ( "QP ungueltig oder < 1.\n" ); break;
+    case -3: write ( "XP ungueltig.\n" ); break;
+    case -4: write ( "Filename(n) ungueltig.\n" ); break;
+    case -5: write ( "Info ungueltig.\n" ); break;
+    case -6: write ( "Stufe ungueltig oder < 0 oder > 20.\n" ); break;
+    case -7: write ( "Aktiv-Flag < 0 oder > 1.\n" ); break;
+    case -8: write ( "Magiername ungueltig.\n" ); break;
+    case -9: write ( "Magiername ungueltig.\n"); break;
+    case -10: write ("Ungueltige Gruppennummer.\n"); break;
+    default: write ( "'"+savekey+"' restauriert.\n" );
+  }
+  savekey = "";
+  return 1;
+}
+
+int Set_Quest ( string str )
+{
+  string quest, player, newquest;
+  int created;
+  object ob;
+
+  if ( !(str=UA) || sscanf( str, "%s %s", player, quest ) != 2 )
+  {
+    write ( "Syntax: SetQ <player> <quest>\n" );
+    return 1;
+  }
+
+  created=0;
+  Load_NumKey();
+
+  if ( !(newquest = num_key[quest+""]) )
+    newquest = quest;
+
+  ob=find_player(player);
+  if(!ob)
+  {
+    ob=find_netdead(player);
+    if(!ob)
+    {
+      created=1;
+      ob=__create_player_dummy(player);
+    }
+  }
+  if(!ob)
+  {
+    write("Kein solcher Spieler gefunden.\n!");
+    return 1;
+  }
+  
+  write(ERRNO_2_STRING("GQ",(int)ob->GiveQuest(newquest,"__silent__"))+"\n");
+  if(created)
+  {
+    ob->save_me(0);
+    if(!(ob->remove()))
+    {
+    	destruct(ob);
+    }
+  }
+  return 1;
+}
+
+int Del_Quest ( string str )
+{
+  string quest, player, newquest;
+  int created;
+  object ob;
+
+  if ( !(str=UA) || sscanf( str, "%s %s", player, quest ) != 2 )
+  {
+    write ( "Syntax: DelQ <player> <quest>\n" );
+    return 1;
+  }
+
+  Load_NumKey();
+
+  if ( !(newquest = num_key[quest+""]) )
+    newquest = quest;
+
+  created=0;
+  ob=find_player(player);
+  if(!ob)
+  {
+    ob=find_netdead(player);
+    if(!ob)
+    {
+      created=1;
+      ob=__create_player_dummy(player);
+    }
+  }
+  if(!ob)
+  {
+    write("Kein solcher Spieler gefunden.\n!");
+    return 1;
+  }
+  
+  write(ERRNO_2_STRING("DQ",(int) ob->DeleteQuest ( newquest ))+"\n");
+  if(created)
+  {
+    ob->save_me(0);
+    if(!(ob->remove()))
+    {
+    	destruct(ob);
+    }
+  }
+  return 1;
+}
+
+int Query_Quest ( string str )
+{
+  mixed *quest;
+  string newstr;
+  int i;
+
+  if ( !(str=UA) )
+  {
+    write ( "Syntax: QueryQ <quest>\n" );
+    return 1;
+  }
+
+  Load_NumKey();
+
+  if ( !(newstr = num_key[str+""]) )
+	newstr = str;
+
+  quest = (mixed *) QM->QueryQuest( newstr );
+
+  if ( !pointerp( quest ) || !sizeof ( quest ) )
+  {
+    write ( "Keine Quest dieses Namens gefunden.\n" );
+    return 1;
+  }
+
+  write ( "Aktueller Eintrag:\n");
+  write ( "Key              : "+newstr );
+  if(quest[Q_ACTIVE])
+    write (" (aktiviert)\n");
+  else
+    write (" (deaktiviert)\n");
+  write ( "Punkte           : "+quest[Q_QP]+"\n" );
+  write ( "Erfahrung        : "+quest[Q_XP]+"\n" );
+  write ( break_string ( implode( quest[Q_ALLOWED], " " ), 65, "Filenamen	 : " ) );
+  write ( break_string ( quest[Q_HINT], 65, "Info             : " ) );
+  write ( "Stufe            : "+quest[Q_DIFF]+"\n" );
+  printf("Stufe (avg)      : %.2f (%d)\n", quest[Q_AVERAGE][0], 
+        quest[Q_AVERAGE][1]);
+  write ( "Klasse           : "+ QCLASS_STARS(quest[Q_CLASS])+"\n");
+  write ( "Attribute        : "+ QATTR_STRINGS[quest[Q_ATTR]]+"\n");
+  write ( "Magier           : "+capitalize(quest[Q_WIZ])+"\n" );
+  write ( "Wartender Magier : "+capitalize(quest[Q_SCNDWIZ])+"\n");
+  return 1;
+}
+
+int Query_Keys ( string str )
+{
+  string *keys, active;
+  mixed *quest;
+  int i;
+
+  if ( !(keys = (string *) QM->QueryAllKeys()) )
+    return 1;
+
+  write ( "\n" );
+  keys=sort_array(keys,#'>);//')
+  for ( i = 0; i < sizeof(keys); i++ )
+  {
+    quest = (mixed *) QM->QueryQuest(keys[i]);
+    if(quest[6])
+      active="*";
+    else
+      active=" ";
+    write(sprintf("%2d%s %-33s%s",i+1,quest[6]?"*":" ",keys[i],
+		      !(i%2)?" ":"\n"));
+  }
+  if(i%2)
+    write("\n");
+
+  return 1;
+}
+
+int Query_Quests ( string str )
+{
+  mixed *quest;
+  string *keys, rstr;
+  int i;
+
+  if ( !(keys = (mixed *) QM->QueryAllKeys()) )
+    return 1;
+
+  keys=sort_array(keys,#'>);//')
+  for ( i = 0, rstr = ""; i < sizeof(keys); i++ )
+  {
+    quest = (mixed *) QM->QueryQuest(keys[i]);
+      write ( "\nKey              : "+keys[i] );
+  if(quest[Q_ACTIVE])
+    write (" (aktiviert)\n");
+  else
+    write (" (deaktiviert)\n");
+  write ( "Punkte           : "+quest[Q_QP]+"\n" );
+  write ( "Erfahrung        : "+quest[Q_XP]+"\n" );
+  write ( break_string ( implode( quest[Q_ALLOWED], " " ), 78, "Filenamen	 : " ) );
+  write ( break_string ( quest[Q_HINT], 78, "Info		 : " ) );
+  write ( "Stufe            : "+quest[Q_DIFF]+"\n" );
+  write ( "Klasse           : "+ QCLASS_STARS(quest[Q_CLASS])+"\n");
+  write ( "Attribute        : "+ QATTR_STRINGS[quest[Q_ATTR]]+"\n");
+  write ( "Magier           : "+capitalize(quest[7])+"\n" );
+  write ( "Wartender Magier : "+capitalize(quest[8])+"\n");
+  }
+  More( rstr, 0 );
+  return 1;
+}
+
+int ActivateQuest( string str )
+{
+  mixed *quest;
+  string newstr;
+  int errstat;
+
+  if ( !(str=UA) )
+  {
+    write ( "Syntax: ActiVateQ <quest>\n" );
+    return 1;
+  }
+
+  Load_NumKey();
+
+  if ( !(newstr = num_key[str+""]) )
+	newstr = str;
+
+  switch( QM->SetActive( newstr, 1 ) )
+  {
+    case -3: write ( "Ungueltiger Flag.\n" ); break;
+    case -2: write ( "Quest war bereits aktiviert.\n" ); break;
+    case -1: write ( "Keine Quest dieses Namens gefunden.\n" ); break;
+    case  0: write ( "Zugriff verweigert\n" ); break;
+    case  1: write ( "Quest '"+newstr+"' aktiviert.\n" ); break;
+    default: write ( "unbekannter Fehlerstatus.\n" );
+  }
+  return 1;
+}
+
+int DeactivateQuest( string str )
+{
+  mixed *quest;
+  string newstr;
+  int errstat;
+
+  if ( !(str=UA) )
+  {
+    write ( "Syntax: DeactiVateQ <quest>\n" );
+    return 1;
+  }
+
+  Load_NumKey();
+
+  if ( !(newstr = num_key[str+""]) )
+	newstr = str;
+
+  switch( QM->SetActive( newstr, 0 ) )
+  {
+    case -3: write ( "Ungueltiger Flag.\n" ); break;
+    case -2: write ( "Quest war bereits deaktiviert.\n" ); break;
+    case -1: write ( "Keine Quest dieses Namens gefunden.\n" ); break;
+    case  0: write ( "Zugriff verweigert\n" ); break;
+    case  1: write ( "Quest '"+newstr+"' deaktiviert.\n" ); break;
+    default: write ( "unbekannter Fehlerstatus.\n" );
+  }
+  return 1;
+}
+
+
diff --git a/obj/tools/teller/adm/mk b/obj/tools/teller/adm/mk
new file mode 100755
index 0000000..5798264
--- /dev/null
+++ b/obj/tools/teller/adm/mk
@@ -0,0 +1,25 @@
+#!/usr/local/bin/perl
+
+$mode = 0;
+$T1="mixed|float|int|string|object|void|quoted_array|symbol|closure|mapping";
+$T2="mixed \*|float \*|int \*|string \*|object \*|void \*|quoted_array \*|symbol \*|closure \*|mixed \&";
+$TYPES="\($T1|$T2\)";
+
+while( <> ) {
+	chop $_;
+	if( $mode == 0 && $_ eq "%efuns" ) {
+		$mode = 1;
+	} elsif( $mode == 1 && $_ eq "%xcodes" ) {
+		$mode = 2;
+	} elsif( $_ =~ "#endif" && $mode == 3 ) {
+		$mode = 1;
+	} elsif( $mode == 1 ) {
+		if( $_ =~ m"#ifndef NATIVE" || $_ =~ m"#ifdef MALLOC_malloc" ) {
+			$mode = 3;
+		} elsif( $_ ne "" && $_ !~ m"^.\*" && $_ !~ "^#" ) {
+			s/default: \w*//;
+			s/$TYPES/mixed/g;
+			print "$_\n";
+		}
+	}
+}
diff --git a/obj/tools/teller/hilfe/array b/obj/tools/teller/hilfe/array
new file mode 100644
index 0000000..8253000
--- /dev/null
+++ b/obj/tools/teller/hilfe/array
@@ -0,0 +1,19 @@
+BEFEHL:
+	array, arr
+
+ARGUMENTE:
+	Alle Stackelemente bis zum letzten Lock.
+
+FUNKTION:
+	Alle Elemente werden zu einem Array zusammengefuegt. Der Lock
+	wird entfernt und das Array wieder auf den Stack gelegt.
+
+BEISPIELE:
+	,me "zap_msg", "Du kochst @@wen@@.\n" "@@ich@@ kocht @@wen@@.\n"
+	"@@ich@@ kocht Dich.\n" array !SetProp
+		Wandelt die drei Strings in ein Array um. Danach wird im
+		Spieler die Zapp-Meldung neu gesetzt. Das obige muss
+		natuerlich in einer Zeile eingegeben werden.
+
+SPIEHE AUCH:
+	"split", "lock"
diff --git a/obj/tools/teller/hilfe/call b/obj/tools/teller/hilfe/call
new file mode 100644
index 0000000..7597299
--- /dev/null
+++ b/obj/tools/teller/hilfe/call
@@ -0,0 +1,26 @@
+BEFEHL:
+	'funktion, ''funktion oder
+	->funktion, -->funktion
+
+ARGUMENTE:
+	Alle Stackelemente bis zum letzten Lock.
+	funktion -- Name einer lfun.
+
+FUNKTION:
+	Rufe in dem Objekt oberhalb des letzten Locks, die angegebene
+	Funktion auf und uebergib die folgenden Elemente des Stacks
+	(bis hinauf zum TOS) als Argumente. Entferne den Lock.
+	Wenn mit die Funktion mit zwei Apostrophen oder zwei Minuszeichen
+	gerufen wurde, lege das Ergebnis auf den Stack. (Bei einfachem
+	Apostroph bzw. Minuszeichen also nicht.)
+
+BEISPIEL:
+	,me "das Grauen am Morgen" 'set_title
+	Enspricht:
+	call_other( this_player(), "set_title", "das Grauen am Morgen" );
+
+WARNUNG:
+	Man sollte vorher nachschauen, ob nicht noch etwas auf dem Stack
+	liegt, denn wenn kein Lock da ist, wuerde die Funktion darin
+	aufgerufen werden (mit me und "das Grauen am Morgen" als Argumente),
+	Deshalb besser so: ,,me "das Grauen am Morgen" ''set_title
diff --git a/obj/tools/teller/hilfe/callefun b/obj/tools/teller/hilfe/callefun
new file mode 100644
index 0000000..61fde2a
--- /dev/null
+++ b/obj/tools/teller/hilfe/callefun
@@ -0,0 +1,25 @@
+BEFEHL:
+	`funktion, ``funktion oder
+	-*funktion, --*funktion
+
+ARGUMENTE:
+	Alle Stackelemente bis zum letzten Lock.
+	funktion -- Name einer efun.
+
+FUNKTION:
+	Rufe die angegebene efun auf, und uebergebe alle Stackelemente
+	bis zum letzten Lock als Argumente. Entferne den Lock.
+	Wenn die efun mit doppelten Backquotes oder Minuszeichen ('``' oder
+	'--*') aufgerufen wurde, lege das Ergebnis auf den Stack. (Bei
+	einfachem '`' also nicht.)
+
+BEISPIEL:
+	,me `environment
+	Enspricht:
+	environment( this_player() );
+
+WARNUNG:
+	Man sollte vorher nachschauen, ob nicht noch etwas auf dem Stack
+	liegt, denn wenn kein Lock da ist, werden zuaetzlich noch
+	ungewunschte Argumente mituebergeben.
+	Deshalb besser so: ,,me ``environment
diff --git a/obj/tools/teller/hilfe/callouts b/obj/tools/teller/hilfe/callouts
new file mode 100644
index 0000000..883042c
--- /dev/null
+++ b/obj/tools/teller/hilfe/callouts
@@ -0,0 +1,24 @@
+BEFEHL:
+	callouts
+	callouts!
+
+ARGUMENTE:
+	callouts: TOS String, nach dem gesucht wird.
+	callouts!: keine
+
+FUNKTION:
+	Gib eine Liste aller im Spiel laufenden call_outs aus. Ein call_out
+	wird in folgendem Format ausgegeben:
+	1.) Zeit, bis zum Aufruf der Funktion.
+	2.) Objekt, in dem die Funktion aufgerufen wird.
+	3.) Name der Funktion.
+	4.) Argument, das dem call_out uebergeben wird.
+
+	Wird der Befehl ohne Rufzeihen eingegeben, so werden nur die
+	Meldungen ausgegeben, die in den ersten drei (!) Argumenten
+	den angegebenen String enthalten.
+
+BEISPIEL:
+	,"go" callouts
+	Gibt alle Monster aus, die mittels der Funktion "go" umherlaufen.
+	(Es gibt aber auch welche, die mittels "Walk" laufen. :-)
diff --git a/obj/tools/teller/hilfe/cleanof b/obj/tools/teller/hilfe/cleanof
new file mode 100644
index 0000000..6557cf1
--- /dev/null
+++ b/obj/tools/teller/hilfe/cleanof
@@ -0,0 +1,16 @@
+BEFEHL:
+	cleanof, clnof
+
+ARGUMENTE:
+	Die zwei obersten Stackelemente.
+
+FUNKTION:
+	Zerstoere alle Objekte in dem zweiten Objekt auf dem Stack,
+	die sich unter dem Namen, der in TOS steht, angesprochen fuehlen.
+	Die Objekte werden aber nur removed, das das der empfohlene Weg
+	ist, Objekte zu zerstoeren. Sollen die Objekte wirklich hart zerstoert
+	werden, so kann man ein Rufzeichen an den Befehl anhaengen.
+	(Also 'cleanof!'.)
+
+SIEHE AUCH:
+	"remove", "destruct"
diff --git a/obj/tools/teller/hilfe/clear b/obj/tools/teller/hilfe/clear
new file mode 100644
index 0000000..0acb5dc
--- /dev/null
+++ b/obj/tools/teller/hilfe/clear
@@ -0,0 +1,9 @@
+BEFEHL:
+	clear, clr
+
+ARGUMENTE:
+	STACK
+
+FUNKTION:
+	Loesche den ganzen Stack. Die Objekte auf dem Stack werden aber
+	nicht destructed.
diff --git a/obj/tools/teller/hilfe/clone b/obj/tools/teller/hilfe/clone
new file mode 100644
index 0000000..1c5620b
--- /dev/null
+++ b/obj/tools/teller/hilfe/clone
@@ -0,0 +1,7 @@
+BEFEHL:
+
+ARGUMENTE:
+
+FUNKTION:
+
+BEISPIELE:
diff --git a/obj/tools/teller/hilfe/destruct b/obj/tools/teller/hilfe/destruct
new file mode 100644
index 0000000..24ed234
--- /dev/null
+++ b/obj/tools/teller/hilfe/destruct
@@ -0,0 +1,14 @@
+BEFEHL:
+	destruct, dest, des
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Zerstoere das Objekt auf dem TOS. In wirklichkeit wird aber nur remove
+	aufgerufen, das das die empfohlene Art derZerstoerung von Objekten
+	ist. Will man jedoch ein Objekt wirklich hart destructen, so
+	haenge man ein Rufzeichen ('!') an das Kommando (also 'destruct!').
+
+SIEHE AUCH:
+	"remove"
diff --git a/obj/tools/teller/hilfe/dump b/obj/tools/teller/hilfe/dump
new file mode 100644
index 0000000..07be46b
--- /dev/null
+++ b/obj/tools/teller/hilfe/dump
@@ -0,0 +1,16 @@
+BEFEHL:
+	dump
+	
+ARGUMENTE:
+	keins oder
+	TOS -- filename
+	
+FUNKTION:
+	Speichere alle selbstdefinierten Funktionen in der angegebenen
+	Datei ab. Ist der TOS kein String, so wird in der Datei
+	~/.memory.o gespeichert.
+
+	Es koennen nur Funktionen abgespeichert oder geladen werden!
+
+SIEHE AUCH:
+	"funktionen", "restore", "memory"
diff --git a/obj/tools/teller/hilfe/dup b/obj/tools/teller/hilfe/dup
new file mode 100644
index 0000000..3702b75
--- /dev/null
+++ b/obj/tools/teller/hilfe/dup
@@ -0,0 +1,9 @@
+BEFEHL:
+	dup
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Lege das Objekt, das oben auf dem Stack liegt, nocheinmal auf
+	den Stack, um es eventuell spaeter noch einmal wieder zu verwenden.
diff --git a/obj/tools/teller/hilfe/einleitung b/obj/tools/teller/hilfe/einleitung
new file mode 100644
index 0000000..0d19d03
--- /dev/null
+++ b/obj/tools/teller/hilfe/einleitung
@@ -0,0 +1,121 @@
+WICHTIG:
+	**********************
+	Das erste Zeichen muss ab jetzt ein Komma (,) sein. Bisher war
+	es ein Semikolon, aber das wechselwirkt ja mit dem emote.
+	**********************
+
+DER TELLERSTAPEL:
+
+	Der Tellerstapel ist ein stackorientiertes Magiertool.
+	Das heisst, man legt die Objekte, Strings oder was auch immer auf
+	den Stack und ruft als letztes die Funktion auf, die dann mit
+	dem Stackihnhalt irgendetwas macht. Das Ergebnis wird meistens
+	dann wieder auf dem Stack abgelegt.
+	Im grossen und ganzen dieses Tool von Macbeths Lupe inspiriert.
+
+	Im folgenden wird mit TOS (TopOfStack) das Ding bezeichnet, das
+	als letztes auf dem Stack abgelegt wurde.
+
+KOMMANDOS:
+
+	Jedes Kommando, das mit den Tellern ausgefuehrt werden soll,
+	wird mit ',' eingeleitet. Allerdings hat das Semikolon noch
+	eine weitere Funktion, aber dazu spaeter.
+
+DINGE AUF DEN STACK LEGEN:
+
+	Hinter dem Semikolon werden (beliebig viele) Stackoperationen
+	angegeben. Die meissten dieser Operationen legen irgendetwas auf
+	dem Stack ab. Will man Objekte auf dem Stack ablegen, so gibt
+	man einfach deren Filenamen an. Zahlen gibt man durch ein voran
+	gestelltes Doppelkreuz ('#') an. (Positive Zahlen gehen auch ohne.)
+	und Strings werden in Anfuehrungszeichen eingefasst, damit sie von
+	den Objekten unterschieden werden koennen. (Uebrigens ist das Zeichen
+	'\n' und das ESC-Zeichen ('\e') in Strings erlaubt.)
+
+STACKOPERATIONEN:
+
+	Stackoperationen, die den Stack nicht beeinflussen.
+
+		say     - Meldungen an Mitspieler ein- und ausschalten.
+			  Umstehende sehen normalerweise, wenn man mit den
+			  Tellern arbeitet, eine Meldung der Art:
+			  "Sowieso jongilert mit seinen Tellern." bzw
+			  "Sowieso faellt beim Jonglieren ein Teller herunter."
+		names   - Objekte mit oder ohne Namen bei Auflistungen
+			  ausgeben.
+		stack   - Gebe Stackinhalt aus (0 ist der TOS).
+		memory  - Gebe Inhalt der Variablen aus.
+		top     - Gebe den TOS aus.
+		inv     - Gebe eine Liste der Objekte aus,
+			  die sich im TOS-Objekt befinden.
+		scan    - Gebe relevante Daten zu dem Spieler auf dem TOS aus.
+
+	Stackoperationen, die mit selbstdefinierten Funktionen zu tuen
+	haben:
+
+		!	- Definiere eine neue Funktion.
+			  (Hilfe unter "funktionen".)
+		dump
+		restore - Speichern und Laden der benutzerdefinierten
+			  Funktionen
+
+	Stackoperationen, die etwas auf dem Stack ablegen:
+
+		me      - Lege dich selbst auf den Stack.
+		here    - Lege den Raum, in dem Du bist, auf den Stack.
+
+	Stackoperationen zu dessen Verwaltung:
+
+		dup     - Lege den TOS noch einmal auf den Stack.
+		pop     - Loesche den TOS vom Stack.
+		swap    - Verstauche den TOS mit dem Ding darunter.
+		clear   - Loesche den ganzen Stack.
+
+	Die folgenden Funktionen poppen ihre Parameter vom Stack herunter.
+
+	Funktionen mit einem Parameter:
+
+		clone   - Clone ein Objekt mit den angegebenen Filenamen.
+		          Lege das geclonte Objekt auf den Stack.
+		update  - Zerstoere die Blueprint des Objektes.
+		remove  - Zerstoere das Objekt.
+
+	Funktionen mit mehreren Parametern:
+
+		move	- Bewege das zweitoberste Element in das Oberste.
+		if      - suche abhaengig vom obersten Element eines der
+			  darunter liegenden aus.
+
+	Funktionen mit beliebig vielen Parametern:
+
+		Um zu wissen, wieviele der Stackelemente zu dem Funktions-
+		aufruf gehoeren, muss eine Information auf dem Stack
+		abgelegt werden. Dieses wird mit einem Lock (';') gemacht.
+		Unter dem Stichpunkt "lock" sind auch Beispiele.
+
+		array	- Fuege alle Elemente zu einem Array zusammen.
+		split	- Zerlege das Array auf dem Stack in Einzelteile.
+
+		->	- Rufe in dem untersten Objekt die hinter dem
+			  Pfeil stehende Funktion auf. Uebergebe alle
+			  dem Objekt folgenden Elemente als Parameter.
+		-->	- Dito. Lege aber das Ergebnis wieder auf
+			  auf dem Stack ab.
+			  (Hilfe ist unter dem Begriff "call" zu finden.)
+
+		-*	- Rufe die hinter dem -* stehende efun auf.
+			  uebergib alle Stackelemente als Parameter.
+		--*	- Dito mit Ergebnisrueckgabe.
+			  (Hilfe unter dem Begriff "callefun".)
+
+		@	- Fuehre den LPC-Code hinter dem Klammeraffen aus.
+		@@	- Dito mit Returnwert.
+			  (Hilfe unter "evaluate".)
+
+	Wie die Befehle im einzelnen funktionieren, kann man durch
+	";hilfe <Befehl>" erfahren. Thema ist im Allgemeneinen der
+	Befehlsname. Bei Ausnahmen bitte oben nachlesen. ;-)
+
+	Viel Spass damit
+		Rumata, die Goettin der Morgenroete.
diff --git a/obj/tools/teller/hilfe/evaluate b/obj/tools/teller/hilfe/evaluate
new file mode 100644
index 0000000..91f6e0a
--- /dev/null
+++ b/obj/tools/teller/hilfe/evaluate
@@ -0,0 +1,22 @@
+BEFEHL:
+	evaluate, eval
+	
+ARGUMENTE:
+	TOS - Name einer Funktion als String.
+	
+FUNKTION:
+	Der Name wird von Stack genommen.
+	Der unter Namen gespeicherte Wert wird ausgewertet. Fuer normale
+	Werte bedeutet das, dass sie auf den Stack gelegt werden. Funktionen
+	werden ausgewertet.
+	
+BEISPIEL:
+	,!Q !1 pl !0 ->QueryProp
+	,rumata title Q
+		liefert direkt den Titel.
+	,rumata title "Q" eval
+		tut das selbe, nur das "Q" als String
+		zwischendurch auf dem Stack liegt.
+
+SIEHE AUCH:
+	"funktionen"
diff --git a/obj/tools/teller/hilfe/funktionen b/obj/tools/teller/hilfe/funktionen
new file mode 100644
index 0000000..dee7d75
--- /dev/null
+++ b/obj/tools/teller/hilfe/funktionen
@@ -0,0 +1,25 @@
+BEFEHL:
+	!funktionsname text
+	
+ARGUMENTE:
+	text -- der *ganze* Rest der Zeile
+	
+FUNKTION:
+	Erzeuge aus text eine neue Funktion mit dem angegebenen Namen. Dieser
+	Name ist als neuer Befehl innerhalb der Teller benutzbar wie die
+	eingebauten Befehle.
+	Wird ein Befehl ausgefuehrt, bekommt er auf einem neuen Stack seine
+	Parameter uebergeben. Was am Ende des Funktionsaufrufes auf dem
+	Stack legt, wird dann auf den urspruenglichen abgelegt.
+
+BEISPIEL:
+	,!Query !1 pl !0 -->QueryProp
+	Definiere die Funktion "Query", sie hat zwei Parameter (!0 und !1),
+	wobei !0 der TOS ist.
+	,rumata title Query
+		Liefert dann den Titel von Rumata auf dem Stack zurueck, da
+		am Ende der Auswertung dieser auf dem Stack liegen wuerde.
+	,!Demo !1 stk
+	,1 2 3 4 Demo
+		Zeigt einen Stack auf dem das zweitoberste Element von dem
+		ursprungsstack liegt; in diesem Fall also 3.
diff --git a/obj/tools/teller/hilfe/general b/obj/tools/teller/hilfe/general
new file mode 100644
index 0000000..826d743
--- /dev/null
+++ b/obj/tools/teller/hilfe/general
@@ -0,0 +1,17 @@
+Das sind die Befehle, die die magischen Teller zur Zeit beherrschen:
+(Letzte Aenderung am 20.01.1995 an den mit % markierten Funktionen,
+die mit * sind neu! AB JETZT GIBT ES AUCH FUNKTIONEN! )
+
+array       destruct    here        memory%     rekinv      split
+call        dump*       if*         message     remove      stack
+callefun    dup         inv         move        restore*    swap
+callouts    einleitung  living      names       rupdate     todo
+cleanof     evaluate*   lock        object      say         top
+clear       FUNKTIONEN* lpc         player      scan        update
+clone       general     me          pop         snoop       vars
+
+Mit ",hilfe <Befehl>" kannst Du Informationen zu den Befehlen abfragen.
+Wenn Du garnix verstehst, dann versuche mal ",hilfe einleitung". :-)
+
+WICHTIG: Das Semikolon wird nicht mehr unterstuetzt, stattdessen
+	kann nur noch das Komma benutzt werden.
diff --git a/obj/tools/teller/hilfe/here b/obj/tools/teller/hilfe/here
new file mode 100644
index 0000000..d0ebdf6
--- /dev/null
+++ b/obj/tools/teller/hilfe/here
@@ -0,0 +1,8 @@
+BEFEHL:
+	here
+
+ARGUMENTE:
+	Keins
+
+FUNKTION:
+	Lege den Raum, in man sich befindet oben auf den Stack.
diff --git a/obj/tools/teller/hilfe/if b/obj/tools/teller/hilfe/if
new file mode 100644
index 0000000..6994717
--- /dev/null
+++ b/obj/tools/teller/hilfe/if
@@ -0,0 +1,22 @@
+BEFEHL:
+	if
+	
+ARGUMENTE:
+	wenn sonst bedingung(TOS)
+	
+FUNKTION:
+	Wenn der TOS Null ist, liefere das drittoberste Element vom Stack,
+	sonst das zweitoberste. Dieser Befehl ist insbesondere im Zusammen-
+	hang mit "evaluate" interessant.
+	
+BEISPIEL:
+	,"bla" "blubb" 0 if
+		Liefert "blubb"
+	,"bla" "bluibb" 1 if
+		Liefert "bla"
+	,"Q" "R" test if evaluate
+		Werte Q aus, wenn der Wert "test" nicht null ist.
+		Anderenfalls werte R aus.
+
+SIEHE AUCH:
+	"evaluate", "funktionen"
diff --git a/obj/tools/teller/hilfe/inv b/obj/tools/teller/hilfe/inv
new file mode 100644
index 0000000..e943800
--- /dev/null
+++ b/obj/tools/teller/hilfe/inv
@@ -0,0 +1,17 @@
+BEFEHL:
+	inv
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Gebe alle Objekte an, die sich in dem Objekt TOS befinden.
+	Gib einen Fehler aus, wenn TOS kein Objekt ist.
+	TOS bleibt auf dem Stack, da dieser Befehl meistens dazu benutzt
+	wird, ein Objekt auf den Stack zu legen und dann bestimmte
+	Objekte in ihm zu suchen, und dann diese zu holen.
+
+BEISPIELE:
+	,me inv
+	Dann sieht man .... ".2 /bla/bla/bla/objekt" und man kann dann
+	mittels ,.2 dieses Objekt holen.
diff --git a/obj/tools/teller/hilfe/living b/obj/tools/teller/hilfe/living
new file mode 100644
index 0000000..90606a7
--- /dev/null
+++ b/obj/tools/teller/hilfe/living
@@ -0,0 +1,17 @@
+BEFEHL:
+	living, lv
+
+ARGUMENTE:
+	TOS -- Name eines Monsters
+
+FUNKTION:
+	Hole einen Namen vom Stack und lege stattdessen das entsprechende
+	Monsterobjekt auf den Stack. Gebe eine Fehlermeldung aus, wenn kein
+	derartiges Monster vorhanden ist.
+
+BEISPIELE:
+	,"ork" living
+	Sucht den erstbesten Ork.
+
+SIEHE AUCH:
+	"object", "player"
diff --git a/obj/tools/teller/hilfe/lock b/obj/tools/teller/hilfe/lock
new file mode 100644
index 0000000..f470db3
--- /dev/null
+++ b/obj/tools/teller/hilfe/lock
@@ -0,0 +1,26 @@
+BEFEHL:
+	,
+
+ARGUMENTE:
+	Keine
+
+FUNKTION:
+	Lege einen 'Lock' auf dem Stack ab. Anhand eines Locks erkennen
+	Funktionen, die eine varibale Anzahl von Argumenten vom Stack
+	nehmen koennen, wieviele der Argumente fuer sie bestimmt sind.
+
+	Wichtig: Das Komma am Anfag jeder Eingabezeile ist KEIN Lock.
+
+BEISPIELE:
+	,me, 1 2 3 array
+		Es werden nur die Zahlen 1 2 und 3 in das Array aufgenommen,
+		nicht der Spieler selbst. Auf dem Stack liegen nacher der
+		Spieler und das Array.
+	,here, me "rumata" ->id
+		"id" wird im Spieler mit einem Argument ("rumata") aufgerufen.
+	,here me "rumata" ->id	
+		Hier wird die Funktion im Raum mit zwei Argumenten aufgerufen,
+		was hier natuerlich keinen Sinn macht.
+
+SIEHE AUCH:
+	"array", "split", "call", "callefun", "evaluate"
diff --git a/obj/tools/teller/hilfe/lpc b/obj/tools/teller/hilfe/lpc
new file mode 100644
index 0000000..bfa0e01
--- /dev/null
+++ b/obj/tools/teller/hilfe/lpc
@@ -0,0 +1,21 @@
+BEFEHL:
+	@ text
+	@@ text
+	
+ARGUMENTE:
+	text -- der ganze(!) Rest der Eingabezeile.
+	Der Stack bis zum letzten Lock.
+	
+FUNKTION:
+	Fuehre den LPC-Code <text> direkt aus. Dabei koennen die Argumente
+	vom Stack mit @<nummer>@ eingesetzt werden. Wichtig, wenn man ein
+	Ergebnis weiterberechnen will, muss man auch ein 'return' um
+	LPC-Code benutzt haben.
+
+	Bei der Ausfuehrung werden nicht alle Include-Dateien eingeladen.
+	
+BEISPIEL:
+	,me @ write( @0@->name(WER)+"\n" )
+	Gibt Deinen Namen aus.
+	,me @@ return @0@->name(WER)
+	Gibt den Namen zurueck und legt ihn auf den Stack.
diff --git a/obj/tools/teller/hilfe/me b/obj/tools/teller/hilfe/me
new file mode 100644
index 0000000..8861f45
--- /dev/null
+++ b/obj/tools/teller/hilfe/me
@@ -0,0 +1,8 @@
+BEFEHL:
+	me
+
+ARGUMENTE:
+	Keine
+
+FUNKTION:
+	Lege den Eigentuemer (Dich!) oben auf den Stack.
diff --git a/obj/tools/teller/hilfe/memory b/obj/tools/teller/hilfe/memory
new file mode 100644
index 0000000..d012451
--- /dev/null
+++ b/obj/tools/teller/hilfe/memory
@@ -0,0 +1,15 @@
+BEFEHL:
+	memory, mem
+
+ARGUMENTE:
+	Keine
+
+FUNKTION:
+	Zeige den Inhalt aller Variablen an, die man angelegt hat.
+	Normalerweise wird der Inhalt in der Form Name = Inhalt angezeigt.
+	Ist unter dem Namen eine Funktion gespeichert, wird
+		Name > Inhalt
+	angezeigt, um Funktionen von Strings unterscheiden zu koennen.
+
+SIEHE AUCH:
+	"vars", "funktionen"
diff --git a/obj/tools/teller/hilfe/message b/obj/tools/teller/hilfe/message
new file mode 100644
index 0000000..656d94a
--- /dev/null
+++ b/obj/tools/teller/hilfe/message
@@ -0,0 +1,13 @@
+KOMMANDO:
+	message [to <spieler>]
+	msg [to <spieler>]
+
+FUNKTION:
+	Wenn man ganze Textbloecke sagen moechte, kann man das mit diesem
+	Befehl tun. Nach Eingabe von 'message' wird jede Eingabe direkt fuer
+	alle im Raum sichtbar oder fuer den angegebenen Spieler ausgegeben.
+	Mit '**' kann man wieder auf normale Eingabe zurueckschalten.
+
+ACHTUNG:
+	Dieser Befehl gehoert nicht zu denen, die mittels ',' eingeleitet
+	werden.
diff --git a/obj/tools/teller/hilfe/move b/obj/tools/teller/hilfe/move
new file mode 100644
index 0000000..b181c34
--- /dev/null
+++ b/obj/tools/teller/hilfe/move
@@ -0,0 +1,14 @@
+BEFEHL:
+	move, mv
+
+ARGUMENTE:
+	obj -- Objekt, das bewegt werden soll.
+	ziel(TOS) -- Raum, in den das Objekt soll
+
+FUNKTION:
+	Bewege das Objekt in den angegebenen Raum. Die meisten Sicherungen
+	werden bei diesem Verschiueben umgangen, also vorsicht!
+
+BEISPIELE:
+	,me "~/workroom" ob mv
+		Man geht in den eigenen Workroom.
diff --git a/obj/tools/teller/hilfe/names b/obj/tools/teller/hilfe/names
new file mode 100644
index 0000000..4eebc3b
--- /dev/null
+++ b/obj/tools/teller/hilfe/names
@@ -0,0 +1,17 @@
+BEFEHL:
+	names, nam
+
+ARGUMENTE:
+	keine
+
+FUNKTION:
+	Normalerweise werden der Filename und der Name (QueryProp(P_NAME))
+	angezeigt, wenn man den Stack oder aehnliches betrachtet.
+	Mit diesem Befehl schaltet man den Namen aus oder an.
+
+	Durch Anschauen der Teller kann man feststellen, ob die Namen oder
+	nur die Filenamen angezeigt werden.
+
+SIEHE AUCH:
+	"inv", "stack", "move", "top", ...
+	Andere Schalter: "say", "inform"
diff --git a/obj/tools/teller/hilfe/object b/obj/tools/teller/hilfe/object
new file mode 100644
index 0000000..145b30a
--- /dev/null
+++ b/obj/tools/teller/hilfe/object
@@ -0,0 +1,22 @@
+BEFEHL:
+	object, ob
+
+ARGUMENTE:
+	TOS -- Name eines Objektes
+
+FUNKTION:
+	Hole einen Namen vom Stack und lege stattdessen das entsprechende
+	Objekt auf den Stack. Falls es kein geladenes Objekt dieses Namens,
+	wohl aber eine (nicht geladene) Blueprint, so wird diese geladen
+
+BEISPIELE:
+	,"players/rumata/workroom" ob
+	Macht das selbe wie ',/players/rumata/workroom', da die Teller
+	Filenamen automatisch zu erkennen versuchen.
+	,"~rumata/workroom" ob
+	geht auch.
+	,"~/workroom" ob
+	liefert den eigenen Workroom.
+
+SIEHE AUCH:
+	"living", "player"
diff --git a/obj/tools/teller/hilfe/player b/obj/tools/teller/hilfe/player
new file mode 100644
index 0000000..e2caa16
--- /dev/null
+++ b/obj/tools/teller/hilfe/player
@@ -0,0 +1,21 @@
+BEFEHL:
+	player, pl
+
+ARGUMENTE:
+	TOS -- Name eines Spielers
+
+FUNKTION:
+	Hole einen Namen vom Stack und lege stattdessen das entsprechende
+	Spielerobjekt auf den Stack.
+
+	Am Ende des Spielernamens ist ein Stern ('*') als Wildcard erlaubt.
+	Die Namen nicht eingeloggter Spieler muessen in jedem Fall ausge-
+	schrieben werden.
+
+
+BEISPIELE:
+	,"rumata" pl
+	Legt Rumata auf den Stack.
+
+SIEHE AUCH:
+	"living", "object"
diff --git a/obj/tools/teller/hilfe/pop b/obj/tools/teller/hilfe/pop
new file mode 100644
index 0000000..c30dc08
--- /dev/null
+++ b/obj/tools/teller/hilfe/pop
@@ -0,0 +1,8 @@
+BEFEHL:
+	pop
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Nimm das oberste Element vom Stack.
diff --git a/obj/tools/teller/hilfe/rekinv b/obj/tools/teller/hilfe/rekinv
new file mode 100644
index 0000000..cb51bd2
--- /dev/null
+++ b/obj/tools/teller/hilfe/rekinv
@@ -0,0 +1,12 @@
+BEFEHL:
+	rekinv
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Dieser Befehl mach das selbe wie "inv", zeigt aber rekursiv
+	auch den Inhalt der enthaltenen Objekte an.
+
+SIEHE AUCH:
+	"inv"
diff --git a/obj/tools/teller/hilfe/remove b/obj/tools/teller/hilfe/remove
new file mode 100644
index 0000000..dff2437
--- /dev/null
+++ b/obj/tools/teller/hilfe/remove
@@ -0,0 +1,16 @@
+BEFEHL:
+	remove, rem
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Zerstoere das Objekt auf dem TOS. Gib dem Objekt die Gelegenheit,
+	Aufraeumarbeiten durchzufuehren.
+
+BEISPIELE:
+	,me remove
+	Man loggt sich selber aus.
+
+SIEHE AUCH:
+	"destruct"
diff --git a/obj/tools/teller/hilfe/restore b/obj/tools/teller/hilfe/restore
new file mode 100644
index 0000000..ad20a35
--- /dev/null
+++ b/obj/tools/teller/hilfe/restore
@@ -0,0 +1,15 @@
+BEFEHL:
+	restore
+	
+ARGUMENTE:
+	keins oder
+	TOS -- filename
+	
+FUNKTION:
+	Lade die in der Datei gespeicherten Funktionen. Wird kein Filename
+	angegeben, so wird die Datei ~/.memory.o ausgelesen.
+
+	Es koennen nur Funktionen abgespeichert oder geladen werden!
+
+SIEHE AUCH:
+	"funktionen", "dump", "memory"
diff --git a/obj/tools/teller/hilfe/rupdate b/obj/tools/teller/hilfe/rupdate
new file mode 100644
index 0000000..85f033e
--- /dev/null
+++ b/obj/tools/teller/hilfe/rupdate
@@ -0,0 +1,25 @@
+BEFEHL:
+	roomupdate, rupdate, rupd, rup
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Zerstoere den Raum und lade ihn neu, damit Aenderungen der Files
+	aktiv werden koennen. Sorge dafuer, dass die Objekte in dem alten Raum
+	in den neuen hinuebergerettet werden. (Diese Eigenschaft kann mittels
+	des Sternchens ('*') abgeschaltet werden.)
+	
+	Gebe dem Raum jedoch Gelegenheit, Aufraeumarbeiten zu taetigen. Soll
+	der Raum wirklich hart zerstoert werden, so ist ein Rufzeichen an den
+	Befehl anzuhaengen. (also `rupdate!', ein Stern muss vor dem Rufzeichen
+	stehen, also `rupdate*!' und nicht `rupdate!*'.)
+	Wichtig: Raeume, die keine Blueprint sind, koennen nicht mit diesem
+	Befehl aktualisiert werden.
+
+BEISPIELE:
+	,here rupdate
+	Aktualisiert den Raum, in dem der Magier sich befindet.
+
+SIEHE AUCH:
+	"update"
diff --git a/obj/tools/teller/hilfe/say b/obj/tools/teller/hilfe/say
new file mode 100644
index 0000000..cf23357
--- /dev/null
+++ b/obj/tools/teller/hilfe/say
@@ -0,0 +1,17 @@
+BEFEHL:
+	say
+
+ARGUMENTE:
+	keine
+
+FUNKTION:
+	Schalte die Meldung, die andere Spieler bekommen, wenn man
+	mit den Tellern arbeitet, aus oder an.
+
+	Ist man unsichtbar, gibt es in keinem Fall eine Meldung.
+
+	Durch Anschauen der Teller kann man feststellen, ob Mispieler
+	die Meldungen bekommen.
+
+SIEHE AUCH:
+	"names", "inform"
diff --git a/obj/tools/teller/hilfe/scan b/obj/tools/teller/hilfe/scan
new file mode 100644
index 0000000..7c251fd
--- /dev/null
+++ b/obj/tools/teller/hilfe/scan
@@ -0,0 +1,17 @@
+BEFEHL:
+	scan
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Wenn der TOS ein Spieler oder ein Monster ist, so gib die
+	spielrelevanten Informationen zu ihm aus. Wenn der TOS ein
+	String ist, so wird der Spieler mit diesem Namen gesucht.
+
+BEISPIELE:
+	,me scan
+	,"jof" pl scan (oder kuerzer: ;"jof" scan)
+
+SIEHE AUCH:
+	"player"
diff --git a/obj/tools/teller/hilfe/snoop b/obj/tools/teller/hilfe/snoop
new file mode 100644
index 0000000..1be1300
--- /dev/null
+++ b/obj/tools/teller/hilfe/snoop
@@ -0,0 +1,11 @@
+BEFEHLE:
+	snoop
+
+ARGUMENTE:
+	keine
+
+FUNKTION:
+	Zeige laufende Snoopvorgaenge an.
+
+	Wenn der Snooper einen hoeheren Magierlevel als man selbst hat,
+	wird der Snoop nicht angezeigt.
diff --git a/obj/tools/teller/hilfe/split b/obj/tools/teller/hilfe/split
new file mode 100644
index 0000000..319025a
--- /dev/null
+++ b/obj/tools/teller/hilfe/split
@@ -0,0 +1,14 @@
+BEFEHL:
+	split, spl
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Zerlege den Array, der auf dem Stack liegt und lege die
+	einzelnen Elemente (das Element mit Index 0 zuunterst)
+	auf dem Stack ab. Wenn der Stack vorher nicht leer war,
+	lege vorher ein Lock auf den Stack.
+
+SIEHE AUCH:
+	"array", "lock"
diff --git a/obj/tools/teller/hilfe/stack b/obj/tools/teller/hilfe/stack
new file mode 100644
index 0000000..c8f182e
--- /dev/null
+++ b/obj/tools/teller/hilfe/stack
@@ -0,0 +1,18 @@
+BEFEHL:
+	stack, stk
+	
+ARGUMENTE:
+	STACK
+	
+FUNKTION:
+	Zeige alle Dinge an, die auf dem Stack liegen.
+	
+BEISPIEL:
+	,me here; 1 2 array stk
+	Ergibt (zum Beispiel):
+	0: ({
+	     1
+	     2
+	   })
+	1: /room/gilde (ein Raum)
+	2: /std/shells/magier#7691 (Atamur)
diff --git a/obj/tools/teller/hilfe/swap b/obj/tools/teller/hilfe/swap
new file mode 100644
index 0000000..befafe3
--- /dev/null
+++ b/obj/tools/teller/hilfe/swap
@@ -0,0 +1,8 @@
+BEFEHL:
+	swap
+
+ARGUMENTE:
+	Die obersten 2 Stackelemente.
+
+FUNKTION:
+	Vertausche die beiden obersten Elemente vom Stack.
diff --git a/obj/tools/teller/hilfe/todo b/obj/tools/teller/hilfe/todo
new file mode 100644
index 0000000..662cefb
--- /dev/null
+++ b/obj/tools/teller/hilfe/todo
@@ -0,0 +1,14 @@
+Was noch kommen soll:
+
+	Doku verbessern, da eine Stackmachine nicht jedermanns Sache ist.
+	(schon ",hilfe einleitung" gemacht?)
+
+	Alles, was nicht player ist, aus dem Raum herausraeumen.
+	<cleanup> ?
+
+Was NICHT kommen soll:
+
+	Alarm, wenn man angeschaut wird. (Wozu auch? :-)
+
+Weitere Ideen werden von Rumata gerne entgegengenommen.
+Aber bitte nicht dauernd anlabern, sondern als mail :-)
diff --git a/obj/tools/teller/hilfe/top b/obj/tools/teller/hilfe/top
new file mode 100644
index 0000000..2b07fa9
--- /dev/null
+++ b/obj/tools/teller/hilfe/top
@@ -0,0 +1,23 @@
+BEFEHL:
+	top
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Zeigt das Objekt an, das auf dem Stack zuoberst liegt (=TOS).
+
+BEISPIELE:
+	;me top
+	Zeige das Objekt, das man selbst ist, an.
+
+	;here top
+	Zeige den Raum, in dem man sich befindet.
+
+	;; 1 2 3 array top (Das zweite ';' ist wichtig)
+	Ergibt:
+	TOS= ({
+	       1
+	       2
+	       3
+	     })
diff --git a/obj/tools/teller/hilfe/update b/obj/tools/teller/hilfe/update
new file mode 100644
index 0000000..bd35aad
--- /dev/null
+++ b/obj/tools/teller/hilfe/update
@@ -0,0 +1,22 @@
+BEFEHL:
+	update, upd
+
+ARGUMENTE:
+	TOS
+
+FUNKTION:
+	Zerstoere die Blueprint des Objektes, das auf dem Stack liegt, damit
+	Aenderungen der Files aktiv werden koennen. Gebe der Blueprint
+	jedoch Gelegenheit, Aufraeumarbeiten zu taetigen. Soll die Blueprint
+	wirklich hart zerstoert werden, so ist ein Rufzeichen an den Befehl
+	anzuhaengen. (also `update!')
+	Wichtig: Das geclonte Objekt, ueber das die Blueprint zerstoert wird,
+	bleibt erhalten, ebenso wie alle anderen geclonten Objekte.
+
+BEISPIELE:
+	,me .teddy update
+	Zerstoert die Blueprint vom Teddy, den man hat. (Das ist natuerlich nur
+	fuer Magier sinnvoll, die das File editieren konnten.)
+
+SIEHE AUCH:
+	"roomupdate"
diff --git a/obj/tools/teller/hilfe/vars b/obj/tools/teller/hilfe/vars
new file mode 100644
index 0000000..1c6deab
--- /dev/null
+++ b/obj/tools/teller/hilfe/vars
@@ -0,0 +1,23 @@
+BEFEHLE:
+	<varname, >varname
+
+ARGUMENTE:
+	TOS 
+	varname -- String
+
+FUNKTION:
+	>varname
+		Speichere das oberste Element des Stacks in der Variable
+		"varname" ab.
+	<varname
+		Kopiere den Inhalt der Variable "varname" auf den Stack.
+		Wenn der Inhalt der Variable eine Funktion ist, wo wird
+		diese in Stringform auf den Stack gelegt und nicht aus-
+		gewertet.
+
+	Es koennen ausser Objekten auch Arrays, Strings und Integers
+	in Variablen gespeichert werden. Wird eine 0 in einer Variable
+	gespeichert, so wird die Variable wieder geloescht.
+
+SIEHE AUCH:
+	"memory"
diff --git a/obj/tools/teller/t_base.c b/obj/tools/teller/t_base.c
new file mode 100644
index 0000000..8336d3b
--- /dev/null
+++ b/obj/tools/teller/t_base.c
@@ -0,0 +1,361 @@
+// ----------------------------------------------------------------------
+// Die Basis- und Hilfsfunktionen der Teller.
+// Stack und Memory
+// ----------------------------------------------------------------------
+#include "teller.h"
+
+#include <moving.h>
+#include <terminal.h>
+
+static mixed *stack;
+static mapping memory;
+static object *oldinv;
+static bool mit_namen;
+static bool fehler_passiert;
+static bool mit_say;
+static bool secureinv;
+static bool dologaccess;
+static bool pretty;
+static bool do_profile;
+
+static mixed top();
+static mixed pop();
+static void push( mixed ob );
+
+static void dump_obj( mixed ob, int indent );
+
+void create()
+{
+	stack = ({});
+	memory = ([]);
+	mit_namen = TRUE;
+	fehler_passiert = FALSE;
+	mit_say = TRUE;
+	secureinv = FALSE;
+	dologaccess = FALSE;
+	pretty = TRUE;
+}
+
+// ----------------------------------------------------------------------
+// Hilfsfunktionen
+// ----------------------------------------------------------------------
+
+static int error( string msg )
+{
+	write( "Fehler in "+msg+".\n" );
+	fehler_passiert = TRUE;
+	return FALSE;
+}
+
+static int memo( string msg )
+{
+	write( "MEMO: "+msg+".\n" );
+	return FALSE;
+}
+
+static int calcinv( string str )
+{
+	object* obs;
+	object ob;
+	int val;
+
+	if( sscanf( str, "%d", val ) == 1 )
+	{
+		obs = all_inventory(top());
+		if( val<0 || val>=sizeof(obs) )
+			return error( "'.': Kein Objekt mit dieser Nummer" );
+		pop();
+		push(obs[val]);
+		return TRUE;
+	}
+
+	if( !(ob=present(str,top())) )
+		return error( "'.': Kein Objekt namens \""+str+"\" gefunden" );
+	pop();
+	push(ob);
+	return TRUE;
+}
+
+static mixed push_str( string str )
+{
+	return
+		push( implode(old_explode( implode(old_explode( implode(old_explode(
+					" "+str+" ",
+				"\\n"),"\n"), "\\e"),ESC), "\\b"),"\b")
+		[1..<2] );
+}
+
+static void do_move( object ob, mixed dest )
+{
+	int weight;
+
+	weight = ob->QueryWeight();
+	if( environment(ob) )
+		environment(ob)->AddWeight(-weight);
+	ob->move(dest,M_NOCHECK);
+	dest->AddWeight(weight);
+}
+
+void heart_beat()
+{
+	object *newinv;
+	object owner;
+	int i,j;
+
+	if( owner=find_player(getuid()) )
+	{
+		newinv = sort_array( all_inventory(owner), lambda( ({'a,'b}),
+			({ #'<,
+				({ #'object_name, 'a }),
+				({ #'object_name, 'b })
+			})
+		) );
+		if( pointerp(oldinv) )
+		{
+			for( i=0,j=0; i<sizeof(newinv) && j<sizeof(oldinv) ; )
+			{
+				if( newinv[i] == oldinv[j] )
+				{ i++; j++; }
+				else if( object_name(newinv[i]) > object_name(oldinv[j]) )
+				{
+					tell_object(owner,
+						"MEMO: \""+object_name(newinv[i])+"\" entered inventory.\n" );
+					i++;
+				}
+				else
+					j++;
+			}
+			for( ; i<sizeof(newinv) ; i++ )
+				tell_object( owner,
+					"MEMO: \""+object_name(newinv[i])+"\" entered inventory.\n" );
+		}
+	}
+	oldinv = newinv;
+}
+
+// ----------------------------------------------------------------------
+// Dumpfunktionen
+// ----------------------------------------------------------------------
+
+static void dump_array( mixed* ob, int indent )
+{
+	int i;
+	for( i=0; i<sizeof(ob); i++ )
+	{
+		printf( "%*s", indent, "" );
+		dump_obj( ob[i], indent );
+	}
+}
+
+static void dump_mapping( mapping ob, int indent )
+{
+	mixed *index;
+	mixed key;
+	int i,j,values;
+
+	index = m_indices( ob );
+	values = get_type_info(ob)[1];
+	for( i=0; i<sizeof(index); i++ )
+	{
+		key = index[i];
+		printf( "%*s", indent, "" );
+		dump_obj( key, indent );
+		for( j=0; j<values; j++ )
+		{
+			printf( "%*s: ", indent+1, "" );
+			dump_obj( ob[key,j], indent+3 );
+		}
+	}
+}
+
+static void dump_obj( mixed ob, int indent )
+{
+	if( !pretty )
+	{
+		printf( "%O\n", ob );
+		return;
+	}
+	if( intp(ob) )
+		write( ob + "\n" );
+	else if( floatp(ob) )
+		printf( "%1.5f\n", ob );
+	else if( stringp(ob) )
+	{
+		if ( ob==";" )
+			write( ";\n" );
+		else
+		{
+			write( "\"" + implode(old_explode( implode(old_explode( implode(old_explode(
+							" "+ob+" ",
+						 "\n"),"\\n"), ESC), "\\e"), "\b"),"\\b")
+						 [1..<2] + "\"\n"
+			);
+		}
+	}
+	else if( objectp(ob) )
+	{
+		if( mit_namen )
+			write( object_name(ob)+" ("+ob->name(WER,0)+")\n" );
+		else
+			write( object_name(ob)+"\n" );
+	}
+	else if( mappingp(ob) )
+	{
+		write( "([\n" );
+		dump_mapping( ob, indent + 2 );
+		printf( "%*s\n", indent + 2, "])" );
+	}
+	else if( pointerp(ob) )
+	{
+		write( "({\n" );
+		dump_array( ob, indent+2 );
+		printf( "%*s\n", indent+2 , "})" );
+	}
+	else
+		printf( "%O\n", ob );
+}
+
+// ----------------------------------------------------------------------
+// Speicherfunktionen
+// ----------------------------------------------------------------------
+
+static void do_recall( mixed arg )
+{
+	if( member(memory,arg) )
+		push( memory[arg,0] );
+}
+
+static void do_store( mixed arg )
+{
+	if ( sizeof(stack) )
+	{
+		if( !top() )
+			memory = m_delete(memory,arg);
+		else
+			memory += ([ arg: top(); 0 ]);
+	}
+	else
+		memo( "Es wurde kein Wert in \""+arg+"\" gespeichert" );
+}
+
+// ----------------------------------------------------------------------
+// Stack-Funktionen
+// ----------------------------------------------------------------------
+
+static void push( mixed ob )
+{
+	stack = ({ ob }) + stack;
+}
+
+static mixed pop()
+{
+	mixed answer;
+
+	if( !sizeof( stack ) )
+		return FALSE;
+	answer = stack[0];
+	stack = stack[1..];
+	return answer;
+}	
+
+static mixed top()
+{
+	if( sizeof(stack) )
+		return stack[0];
+}
+
+static varargs int becomes_obj( mixed argv)
+{
+	object ob;
+
+	if( !pointerp(argv) ) // default ist der stack !
+		argv = stack;
+	if( !sizeof(argv) )
+		return FALSE; 
+	if( stringp(argv[0]) && !catch(call_other(argv[0],"?")) )
+	{
+	  argv[0] = find_object(argv[0]);
+		return TRUE;
+	}
+	else
+		return objectp(argv[0]);
+}
+
+static isSubStr( pl, str, len )
+{
+	return getuid(pl)[0..len] == str;
+}
+
+static becomes_pl(argv)
+{
+	object pl;
+	object* pllist;
+	string str;
+	int len;
+
+	if( !argv ) argv = stack;
+	if( !sizeof(argv) || ( becomes_obj() && !interactive(argv[0]) ) )
+		return FALSE;
+	if( stringp(argv[0]) )
+	{
+		str = lower_case( argv[0] );
+		pl = 0;
+		if( str[<1..<1] == "*" )
+		{	
+			str = str[0..<2];
+			len = sizeof(str) - 1;
+			pllist = filter( users(), #'isSubStr, str, len );
+			if( sizeof(pllist) == 1 )
+			{
+				pl=pllist[0];
+				argv[0] = pl;
+			}
+		}
+		if( !pl && pl=find_player(argv[0]) )
+			argv[0] = pl;
+		return pl;
+	}
+	return argv[0];
+}
+
+static DumpObj( ob )
+{
+  string ans;
+  int i,j;
+
+  if( intp(ob) )
+    return ""+ob;
+  else if( stringp(ob) )
+    return "\""+implode(explode(ob,"\n"),"\\n")+"\"";
+  else if( objectp(ob) )
+    return "\""+object_name(ob)+"\"";
+  else if( mappingp(ob) )
+  {
+    ans="([";
+    for( i=0; i<sizeof(ob)-1; i++ )
+      ans += DumpMapp(ob,i)+",";
+    // rely on right value of i
+    return ans+DumpMapp(ob,i)+"])";
+  }
+  else
+  {
+    ans="({";
+    for( i=0; i<sizeof(ob)-1; i++ )
+      ans += DumpObj(ob[i])+",";
+    // rely on right value of i
+    return ans+DumpObj(ob[i])+"})";
+  }
+}
+
+static DumpMapp(ob,i)
+{
+  int j,vz;
+  string ans;
+
+  vz = get_type_info(ob)[1];
+  ans = DumpObj(m_indices(ob)[i])+":";
+  for( j=0; j<vz-1; j++ )
+    ans += DumpObj(ob[m_indices(ob)[i],j])+";";
+  // rely on value of j
+  return ans + DumpObj(ob[m_indices(ob)[i],j]);
+}
diff --git a/obj/tools/teller/t_cmds.c b/obj/tools/teller/t_cmds.c
new file mode 100644
index 0000000..68fa5d3
--- /dev/null
+++ b/obj/tools/teller/t_cmds.c
@@ -0,0 +1,1000 @@
+// ----------------------------------------------------------------------
+// Builtinkommandos der Teller.
+// ----------------------------------------------------------------------
+#include "teller.h"
+inherit T_BASE;
+
+#include <moving.h>
+#include <attributes.h>
+#include <terminal.h>
+#include <wizlevels.h>
+
+static void do_rinv( object env, int depth );
+static scan_obj( object player, object obj );
+static void mk_waitfor( mixed waitfor );
+static void mk_autoload( mixed autoload );
+static int do_roomupdate( int destflag, int noitems );
+static int do_cleanof( int strong );
+
+// from teller.c
+static int do_cmd( string str );
+
+static int cmd_clear()
+{
+	stack = ({});
+	return TRUE;
+}
+
+static int cmd_pop()
+{
+	if( !sizeof(stack) )
+		return error( "pop: Der Stack ist leer" );
+	pop();
+	return TRUE;
+}
+
+static int cmd_top()
+{
+	if( !sizeof(stack) )
+		return error( "top: Der Stack ist leer" );
+	write( "TOS= " );
+	dump_obj( top(), 5 );
+	return TRUE;
+}
+
+static int cmd_swap()
+{
+	mixed tmp;
+
+	if( sizeof(stack)<2 )
+		return error( "swap: Keine 2 Elemente auf dem Stack" );
+	tmp = stack[0];
+	stack[0] = stack[1];
+	stack[1] = tmp;
+	return TRUE;
+}
+
+static int cmd_dup()
+{
+	if( !sizeof(stack) )
+		return error( "dup: Der Stack ist leer" );
+	push(top());
+	return TRUE;
+}
+
+static int cmd_here()
+{
+	push(environment(PL));
+	return TRUE;
+}
+
+static int cmd_stack()
+{
+  int i;
+  if( !sizeof(stack) )
+    return memo( "Der Stack ist leer" );
+
+  for( i=0; i<sizeof(stack); i++ )
+  {
+    printf( "%2d: ", i );
+    dump_obj( stack[i], 4 );
+  }
+  return TRUE;
+}
+
+static int cmd_inv()
+{
+  int i;
+  object ob;
+
+	if( !becomes_obj() )
+		return error( "inv: TOS ist kein Objekt" );
+  write( "Inventar von " );
+  dump_obj( top(), 13 );
+  for( i=0, ob=first_inventory(top()); ob; ob=next_inventory(ob), i++ )
+  {
+    printf( "%2d. ", i );
+    dump_obj( ob, 4 );
+  }
+  return TRUE;
+}
+
+static int cmd_rekinv()
+{
+	if( !becomes_obj() )
+		return error( "rekinv: TOS ist kein Objekt" );
+	write( "Inventar von " );
+	dump_obj( top(), 13 );
+
+	do_rinv( top(), 2 );
+	return TRUE;
+}
+
+static void do_rinv( object env, int depth )
+{
+	int i;
+	object ob;
+
+	for( i=0, ob=first_inventory(env); ob; ob=next_inventory(ob), i++ )
+	{
+		printf( "%*d. ", depth, i );
+		dump_obj( ob, 2+depth );
+		do_rinv( ob, depth+2 );
+	}
+}
+
+static int cmd_me()
+{
+	push( PL );
+	return TRUE;
+}
+
+// Uebernommen aus dem Teddy (mit freundlicher Genehmigung von Sir).
+static int cmd_scan()
+{
+	object obj;
+
+	if( !becomes_pl() && ( !objectp(top()) || !living(top()) ) )
+	{
+		if( stringp(top()) && file_size( "/save/"+top()[0..0]+"/"+top()+".o") > 0 )
+		{
+			obj = clone_object( T_PLAYER );
+			obj->Load( top() );
+			obj->SetProp( P_NAME, capitalize( pop() ) );
+			scan_obj( TRUE, obj );
+			destruct( obj );
+			return TRUE;
+		}
+		return error( "scan: TOS ist kein Lebewesen" );
+	}
+
+	scan_obj( query_once_interactive( top() ), pop() );
+	return TRUE;
+}
+
+static int WizLevel( object obj )
+{
+	if( obj->Notlogged() )
+		return query_wiz_level( lower_case(obj->playername()) );
+	else
+		return query_wiz_level( obj );
+}
+
+static string IpName( object obj )
+{
+	string ip_name, ip_num, nm;
+
+	if( obj->Notlogged() )
+	{
+		// Aus Umstellungsgruenden werden CALLED_FROM_IP und IP_NAME
+		// abgefragt. IP_NAME ist neuer.
+
+		nm = lower_case(obj->playername());
+		ip_name = obj->QueryProp(P_CALLED_FROM_IP);
+		if( !ip_name ) ip_name = obj->Query(P_IP_NAME);
+		return ip_name + " ("
+			+ dtime(get_dir("/save/"+nm[0..0]+"/"+nm+".o",4)[0]) +")";
+	}
+	else
+	{
+		nm = lower_case( obj->name(RAW) );
+		ip_name = query_ip_name( obj );
+		if( ip_name == "" || !ip_name )
+		{
+			ip_name = obj->QueryProp(P_CALLED_FROM_IP);
+			if( !ip_name ) ip_name = obj->Query(P_IP_NAME);
+			return ip_name + " ("
+				+ dtime(get_dir("/save/"+nm[0..0]+"/"+nm+".o",4)[0]) +")";
+		}
+		return ip_name + " [" + query_ip_number(obj) + "]";
+	}
+}
+
+string IdleTime( object obj )
+{
+	if( obj->Notlogged() ) return "-nicht da-";
+	if( query_ip_number(obj) ) return ""+query_idle(obj);
+	return "-netztot-";
+}
+
+static scan_obj( object player, object obj )
+{
+	string title, level, gender, room, testpl,
+		weapon, armour, quest, stat_str, *arr;
+	int i,ac;
+	object weaponobj, *list, *gegner;
+	mixed *hands, *quests, *stats;
+
+	// 1.Zeile : Name Titel - Rasse - [ Wizlevel ]
+	title = obj->QueryProp(P_TITLE);
+
+  if( !player )
+    level = "Monster" ;
+  else if( WizLevel( obj ) < WIZARD_LVL )
+	{
+		if( testpl=obj->QueryProp( P_TESTPLAYER ) )
+		{
+			if( stringp(testpl) )
+				level = "("+testpl+")";
+			else
+				level = "Testspieler";
+		}
+		else if( WizLevel( obj ) >= SEER_LVL )
+			level = "Seher";
+		else
+			level = "Spieler" ;
+	}
+  else if( WizLevel( obj ) >= GOD_LVL )
+    level = "MudGott" ;
+  else if( WizLevel( obj ) >= ARCH_LVL )
+    level = "Erzmagier" ;
+  else if( WizLevel( obj ) >= LORD_LVL )
+    level = "Regionsmagier" ;
+  else
+    level = "Magier" ;
+
+  if( !obj->short() )
+    level += ", unsichtbar" ;
+  if( obj -> QueryProp( P_FROG ) )
+    level += ", Frosch" ;
+  if( obj->QueryProp( P_GHOST ) )
+  	level += ", tot";
+	if( obj->Notlogged() )
+		level += ", ausgeloggt";
+	if(obj->QueryProp(P_SECOND) )
+		level +=", Zweitie";
+
+	if( environment(obj) )
+		room = object_name(environment( obj ));
+	else
+		room = "-nirgends-";
+
+  printf( "%s %s %s[ %s ].\nBefindet sich in %s.\n",
+		obj->name(RAW), title? title : "",
+		stringp(obj->QueryProp(P_RACE)) ? "- "+obj->QueryProp(P_RACE)+" - " : "",
+		level, room ) ;
+
+	// 1 abc Zeile : Host,Email,Snooper
+	if( player )
+	{
+		printf( "Host.......: %s\n", IpName(obj) );
+		printf( "E-Mail.....: %s.\n", obj->QueryProp(P_MAILADDR) );
+		if( !obj->Notlogged() && query_snoop(obj) )
+			printf( "Snooper....: %s.\n", capitalize(getuid(query_snoop(obj))) );
+
+		printf( "Vorsicht...: %11d Kurzmodus.: %11s Magierblick....: %11s.\n",
+			obj->QueryProp(P_WIMPY), obj->QueryProp(P_BRIEF) ? "-an-" : "-aus-",
+			obj->QueryProp(P_WANTS_TO_LEARN) ? "-an-" : "-aus-" );
+		printf( "Idlezeit...: %11s Alter.....: %11s Verheiratet mit: %-11s.\n",
+			IdleTime(obj), time2string("%5d:%02h:%02m",obj->QueryProp(P_AGE)*2),
+			(stringp(obj->QueryProp(P_MARRIED)) ? obj->QueryProp(P_MARRIED) : "-" )
+		);
+	}
+
+  // 2.Zeile : HP, SP und XP
+  printf( "Lebenspkt..: [%4d/%4d] Magiepkt..: [%4d/%4d].\n" +
+    "Questpunkte: [%4d/%4d] Erfahrung.: %11d.\n",
+    obj->QueryProp(P_HP), obj->QueryProp(P_MAX_HP),
+		obj->QueryProp(P_SP), obj->QueryProp(P_MAX_SP),
+		obj->QueryProp(P_QP), "/secure/questmaster"->QueryMaxQP(),
+		obj->QueryProp(P_XP) );
+
+  // 3.Zeile : FOOD, DRINK, ALCOHOL
+  printf( "Nahrung....: [%4d/%4d] Fluessigk.: [%4d/%4d] " +
+    "Alkohol........: [%4d/%4d].\n",
+    obj->QueryProp(P_FOOD), obj->QueryProp(P_MAX_FOOD),
+    obj->QueryProp(P_DRINK), obj->QueryProp(P_MAX_DRINK),
+    obj->QueryProp(P_ALCOHOL), obj->QueryProp(P_MAX_ALCOHOL) ) ;
+
+  // 4.Zeile : Geschlecht, Alignment, Level
+  switch( obj->QueryProp(P_GENDER) )
+  {
+    case FEMALE : gender = "weiblich   " ; break ;
+    case MALE   : gender = "maennlich  " ; break ;
+    default     : gender = "neutrum    " ; break ;
+  }
+  printf(
+		"Geschlecht.: %s Charakter.: %11d (Magier)Stufe..: [%4s/%4d].\n", 
+    gender, obj->QueryProp(P_ALIGN),
+		player ? WizLevel(obj)+"" : "-", obj->QueryProp(P_LEVEL) );
+
+	// 5.Zeile : Geld, Gewicht, Playerkills
+	printf( "Geld.......: %11d Traegt....: %11d Playerkills....: %11d.\n",
+		obj->QueryMoney(), obj->query_weight_contents(),
+		obj->QueryProp(P_KILLS) );
+
+  // 6.Zeile : stati
+  stats = obj->QueryProp(P_ATTRIBUTES) ;
+	arr = m_indices( stats );
+  stat_str = "" ;
+  for( i = 0; i < sizeof( arr ); i++ ) {
+    stat_str += capitalize(arr[ i ]) + "[" + stats[arr[ i ]];
+    if( ac = obj->QueryAttributeOffset(arr[i]) ) {
+    	stat_str += "+" + ac;
+    }
+    stat_str += "], ";
+  }
+
+  if( stat_str == "" )
+    stat_str = "Keine" ;
+  else
+    stat_str = stat_str[0..sizeof( stat_str ) - 3] ;
+  printf( "Attribute..: %s.\n", stat_str ) ;
+
+  // 7.Zeile : Waffe( Dateiname )[ AC ]
+  // 8.Zeile : Ruestung(en)[ WC ]
+	weaponobj=obj->QueryProp(P_WEAPON);
+	if( weaponobj )
+    weapon = weaponobj->name(RAW) + " (" + 
+      object_name( weaponobj ) + ") [" +
+      weaponobj->QueryProp(P_WC) + "]" ;
+	else
+	{
+		hands = obj->QueryProp(P_HANDS);
+		weapon = sprintf( "kaempft%s [%d]", hands[0], hands[1] );
+	}
+	ac = 0;
+	list = obj->QueryProp(P_ARMOURS);
+	armour = "";
+	for( i = 0; i < sizeof( list ); i++ )
+	{
+     armour += ( list[i]->name(RAW) + "[" +
+         list[i]->QueryProp(P_AC) + "]" + ", ") ;
+		 ac += list[i]->QueryProp(P_AC);
+	}
+
+  if( armour == "" )
+    armour = "Keine  " ;
+
+	arr = old_explode( break_string( armour[0..<3]+sprintf(" =>[%d]",
+		ac+obj->QueryProp(P_BODY) ), 65 ), "\n" ) ;
+	armour = arr[ 0 ] ;
+	for( i = 1; i < sizeof( arr ); i++ )
+		armour += "\n             " + arr[ i ] ;
+  printf( "Waffe......: %s.\nRuestung...: %s.\n", weapon, armour ) ;
+
+	gegner = obj->QueryEnemies();
+	if( pointerp(gegner) )
+	{
+		gegner = gegner[0];
+		for( i=0; i<sizeof(gegner); i++ )
+		{
+			if( i==0 ) printf( "Gegner.....: "); else printf( "             " );
+			if( !objectp(gegner[i]) )
+				printf( "<%O>\n", gegner[i] );
+			else
+				printf( "%s (%s)\n", gegner[i]->name(WER,0), object_name(gegner[i]) );
+		}
+	}
+
+	mk_waitfor( obj->QueryProp(P_WAITFOR) );
+
+	mk_autoload( obj->QueryProp(P_AUTOLOAD) );
+
+	return TRUE;
+}
+
+static void mk_waitfor( mixed waitfor )
+{
+	string str;
+	int i;
+
+	if( !pointerp(waitfor) || sizeof(waitfor)==0 )
+		return;
+	str = "Waiting for: ";
+	for( i=sizeof(waitfor)-1; i>0; i-- )
+		str += waitfor[i] + ", ";
+	str += waitfor[0];
+	write( str+"\n" );
+}
+	
+static void mk_autoload( mixed autoload )
+{
+	string str, *objlist;
+	int i;
+
+	if( !mappingp(autoload) )
+		return;
+	str = "Autoload...: ";
+	objlist = m_indices(autoload);
+	for( i=sizeof(objlist)-1; i>=0; i-- )
+	{
+		str += "\"" + objlist[i] + "\"\n";
+		if( i>0 )
+			str += "             ";
+	}
+	write( str );
+}
+
+static void print_memory_line( string key, object data, int flag )
+{
+  printf( " %-10s%s ", key, (flag ? ">" : "=") );
+  dump_obj( data, 13 );
+}
+	
+static int cmd_memory()
+{
+  int i;
+  if( !sizeof(memory) )
+    return memo( "Keine Variablen definiert" );
+
+  walk_mapping( memory, #'print_memory_line );
+  return TRUE;
+}
+
+static int cmd_array()
+{
+  mixed *array;
+  mixed ob;
+
+	if( !sizeof(stack) )
+		return error( "array: Der Stack ist leer" );
+  array = ({});
+  while( sizeof(stack) && (ob=pop()) && ob!=";" )
+    array = ({ob}) + array;
+  push( array );
+	return TRUE;
+}
+
+static int cmd_split()
+{
+  mixed *array;
+  int i;
+
+	if( !pointerp(top()) )
+		return error( "split: TOS ist kein Array" );
+  array=pop();
+  if( sizeof(stack) )
+    push( ";" );
+  for( i=0; i<sizeof(array); i++ )
+    push(array[i]);
+	return TRUE;
+}
+
+static int cmd_player()
+{
+	object ob;
+	string str;
+
+	str = top();
+	if( !stringp(str) )
+		return error( "player: TOS ist kein String" );
+	ob = becomes_pl();
+	if( !ob )
+		return error( "player: Keinen Spieler namens \""+str+"\" gefunden" );
+	//pop();
+	//push(ob);
+	return TRUE;
+}
+
+static int cmd_object()
+{
+	object ob;
+	string err,fnam;
+
+	if( !stringp(top()) )
+		return error( "object: TOS ist kein String" );
+	ob = find_object(top());
+	if( !ob )
+	{
+		if( !(fnam=this_player()->find_file(top(),".c")) )
+			return error( "object: Kein Objekt namens \""+top()+"\" gefunden" );
+		if( err=(catch(call_other(fnam,"?"))) )
+			return error( "object: Fehler beim Laden: "+err[1..<3] );
+		ob = find_object(fnam);
+	}
+	pop();
+	push(ob);
+	return TRUE;
+}
+
+static int cmd_living()
+{
+	object ob;
+	if( !stringp(top()) )
+		return error( "object: TOS ist kein String" );
+	ob = find_living(top());
+	if( !ob )
+		return error( "object: Kein Objekt namens \""+top()+"\" gefunden" );
+	pop();
+	push(ob);
+	return TRUE;
+}
+
+static int cmd_say()
+{
+	mit_say = !mit_say;
+	if( mit_say )
+		memo( "Meldungen an Mitspieler an" );
+	else
+		memo( "Meldungen an Mitspieler aus" );
+	return TRUE;
+}
+
+static int cmd_names()
+{
+	mit_namen = !mit_namen;
+	if( mit_namen )
+		memo( "Namen werden angezeigt" );
+	else
+		memo( "Namen werden nicht angezeigt" );
+	return TRUE;
+}
+
+static int cmd_secureinv()
+{
+	secureinv = !secureinv;
+	if( secureinv )
+		memo( "Inventory wird ueberwacht" );
+	else
+		memo( "Inventory wird nicht ueberwacht" );
+	set_heart_beat(secureinv);
+	return TRUE;
+}
+
+static int cmd_logaccess()
+{
+	dologaccess = !dologaccess;
+	if( dologaccess )
+		memo( "Zugriffe werden gemeldet" );
+	else
+		memo( "Zugriffe werden nicht gemeldet" );
+	return TRUE;
+}
+
+static int cmd_destruct_bang()
+{
+	if( !becomes_obj() )
+		return error( "destruct: TOS ist kein Objekt" );
+	destruct(pop());
+	return TRUE;
+}
+
+static int cmd_destruct()
+{
+	if( !becomes_obj() )
+		return error( "remove: TOS ist kein Objekt" );
+	memo( "destruct: TOS wird 'removed'!" );
+	top()->remove();
+	if( top() )
+		memo( "destruct: TOS lebt noch." );
+	else
+		pop();
+	return TRUE;
+}
+
+static int cmd_remove()
+{
+	if( !becomes_obj() )
+		return error( "remove: TOS ist kein Objekt" );
+	top()->remove();
+	if( top() )
+		memo( "destruct: TOS lebt noch." );
+	else
+		pop();
+	return TRUE;
+}
+
+static int cmd_update()
+{
+	object blue;
+
+	if( !becomes_obj() )
+		return error( "update: TOS ist kein Objekt" );
+	blue = find_object(old_explode(object_name(top()),"#")[0]);
+	blue->remove();
+	if( blue )
+		memo( "update: TOS lebt noch" );
+	else
+		pop();
+	return TRUE;
+}
+
+static int cmd_update_bang()
+{
+	if( !becomes_obj() )
+		return error( "update: TOS ist kein Objekt" );
+	destruct(find_object(old_explode(object_name(pop()),"#")[0]));
+	return TRUE;
+}
+
+static int cmd_roomupdate()
+{
+	return do_roomupdate( FALSE, FALSE );
+}
+
+static int cmd_roomupdate_bang()
+{
+	return do_roomupdate( TRUE, FALSE );
+}
+
+static int cmd_extroomupdate()
+{
+	return do_roomupdate( FALSE, TRUE );
+}
+
+static int cmd_extroomupdate_bang()
+{
+	return do_roomupdate( TRUE, TRUE );
+}
+
+// Hilfsfunktionen zum Filtern von Items
+static object *collect_items;
+static void collect( object* data ) { collect_items += ({ data[0] }); }
+
+static int do_roomupdate( int destflag, int noitems )
+{
+	object tmproom,newroom;
+	object *inv;
+	string errmsg;
+	string *file;
+	object *items;
+	int i;
+
+	if( !becomes_obj() )
+		return error( "roomupdate: TOS ist kein Objekt" );
+	file = old_explode( object_name( top() ), "#" );
+	if( sizeof(file) > 1 )
+		return error( "roomupdate: TOS ist keine Blueprint" );
+	if( file[0] == "/room/void" )
+		return error( "roomupdate: Die `void' darf nicht geupdatet werden" );
+
+	// ----- Rettung	
+	tell_room( top(),
+		"Der Raum verschwimmt vor Deinen Augen, um sich zu erneuern.\n"
+	);
+	tmproom = clone_object( "/room/void" );
+
+	if( noitems )
+	  // Nur Spieler kommen raus.
+		inv = filter( all_inventory(top()), #'query_once_interactive );
+	else
+	{ // Dinge, die P_ITEMS sind, bleiben da!
+		collect_items = ({});
+		map( top()->QueryProp(P_ITEMS), #'collect ); 
+		inv = all_inventory(top()) - collect_items;
+	}
+
+	for( i=sizeof(inv)-1; i>=0; i-- )
+		inv[i]->move( tmproom, M_NOCHECK | M_SILENT | M_NO_SHOW );
+
+	// ----- Vernichtung
+	if( destflag )
+		destruct( pop() );
+	else
+	{
+		top()->remove();
+		if( top() )
+			memo( "roomupdate : TOS ist nicht verschwunden." );
+		else
+			pop();
+	}
+
+	// ----- Neuerschaffung
+	errmsg = catch( call_other( file[0], "?" ) );
+	if( errmsg )
+	{
+		tell_room( tmproom, "Der Raum verbleicht in ein Nichts.\n" );
+		push( file[0] );
+		return error( "updateroom: " + errmsg[1..<2] );
+	}
+
+	// ----- Restaurierung
+	newroom = find_object( file[0] );
+	for( i=sizeof(inv)-1; i>=0; i-- )
+		if( objectp(inv[i]) ) // Objekte koennten sich beim ersten move zerstoeren.
+			inv[i]->move( newroom, M_NOCHECK | M_SILENT | M_NO_SHOW );
+	tell_room( newroom, "Die Konturen werden wieder scharf.\n" );
+	destruct( tmproom );
+	return TRUE;
+}
+
+static int cmd_clone()
+{
+	if( !stringp(top()) )
+		return error( "clone: TOS ist kein String" );
+	if( file_size(top()+".c")<=0 )
+		return error( "clone: Kein solches File" );
+	push(clone_object(pop()));
+	//do_move( top(), environment(PL) );
+	//top()->move(PL,M_GET|M_SILENT);
+	return TRUE;
+}
+
+static int cmd_move()
+{
+	object ob;
+
+	if( !becomes_obj() )
+		return error( "move: Ziel ist kein Objekt" );
+	ob = pop();
+	if( !becomes_obj() )
+		return error( "move: Kein solcher Gegenstand" );
+	do_move( pop(), ob );
+	return TRUE;
+}
+
+static int cmd_cleanof_bang()
+{
+	return do_cleanof( TRUE );
+}
+
+static int cmd_cleanof()
+{
+	return do_cleanof( FALSE );
+}
+
+static int do_cleanof( int strong )
+{
+	object *inv;
+	int i;
+	string clean_id;
+
+	if( !stringp(top()) )
+		return error( "cleanof: TOS ist kein String" );
+	clean_id = pop();
+	if( !becomes_obj() )
+	{
+		push( clean_id );
+		return error( "cleanof: Kein Objekt zum Leeren" );
+	}
+	for( i=0, inv=all_inventory(pop()); i<sizeof(inv); i++ )
+		if( inv[i]->id(clean_id) )
+		{
+			if( strong )
+				destruct( inv[i] );
+			else
+				inv[i]->remove();
+		}
+	return TRUE;
+}
+
+static int cmd_snoopers()
+{
+	object* u, snooper;
+	int i, flag;
+
+	flag = 0;
+	u = users();
+	for( i=0; i<sizeof(u); i++ )
+	{
+		if( snooper = query_snoop(u[i]) )
+		{
+			flag = 1;
+			printf( "%s wird gesnooped von: %s.\n",
+				capitalize(getuid(u[i])), capitalize(getuid(snooper)) );
+		}
+	}
+	if( !flag )
+		memo( "Momentan wird niemand gesnooped" );
+	return TRUE;
+}
+
+static int cmd_ping()
+{
+	object pl;
+
+	if( !becomes_pl() )
+		return error( "ping: TOS ist kein Spieler" );
+
+	pl=pop();
+	call_out( "ping", 0, ({ pl, 5 }) );
+	return TRUE;
+}
+
+static void ping( mixed* argv )
+{
+	if( !argv[0] || --argv[1] < 0 ) return;
+	tell_object( argv[0], BEEP+PL->name(WER)+" pingt Dich an.\n" );
+	call_out( "ping", 1, argv );
+}
+
+static void do_calloutinfo( mixed* call )
+{
+	int l,i;
+
+	if( pointerp(call) )
+	{
+		printf( "%5d:%O->%O(", call[2], call[0], call[1]);
+		if( (l=sizeof(call))>3 ) {
+			for( ; l>=3 && !call[--l]; ) ;
+			for( i=3; i<=l; i++ ) printf( "%O%s", call[i], (i==l)?"":"," );
+		}
+		write(")\n");
+	}
+}
+
+static int cmd_callouts_bang()
+{
+	mixed *calls;
+	object obj;
+	string name;
+	int i,j;
+
+	calls = call_out_info();
+	if( !pointerp(calls) || !sizeof(calls) )
+	{
+		memo( "Keine Callouts vorhanden" );
+		return TRUE;
+	}
+	map( calls, #'do_calloutinfo );
+	return TRUE;
+}
+
+static void do_calloutinfo2( mixed* call, string str )
+{
+	string s;
+	int i,l;
+
+	if( pointerp(call) )
+	{
+		s = sprintf( "%5d:%O->%O(", call[2], call[0], call[1]);
+		if( sizeof(explode(s,str)) > 1 )
+		{
+			write( s );
+			if( (l=sizeof(call))>3 ) {
+				for( ; l>=3 && !call[--l]; ) ;
+				for( i=3; i<=l; i++ ) printf( "%O%s", call[i], (i==l)?"":"," );
+			}
+			write(")\n");
+		}
+	}
+}
+
+static int cmd_callouts()
+{
+	mixed *calls;
+	object obj;
+	string str;
+	int i,j;
+
+	if( !stringp(top()) )
+		return error( "TOS ist kein String" );
+	str = pop();
+	calls = call_out_info();
+	if( !pointerp(calls) || !sizeof(calls) )
+	{
+		memo( "Keine Callouts vorhanden" );
+		return TRUE;
+	}
+	map( calls, #'do_calloutinfo2, str );
+	return TRUE;
+}
+
+static int cmd_heartbeats()
+{
+	mixed *beats;
+	int i;
+	object env;
+	string enam;
+
+	beats = heart_beat_info();
+	if( !pointerp(beats) || !sizeof(beats) )
+	{
+		memo( "Keine Heartbeats vorhanden" );
+		return TRUE;
+	}
+	for( i=0; i<sizeof(beats); i++ )
+	{
+		env = environment(beats[i]);
+		enam = env ? object_name(env) : "-- nirgends --";
+		printf( "%-35s %-35s\n", object_name(beats[i]), enam );
+	}
+	return TRUE;
+}
+
+static int cmd_wer()
+{
+	object* ppl;
+	string* pl;
+	int i;
+
+	ppl = sort_array( users(), lambda( ({ 'x, 'y }),
+		({ #'<, ({ #'query_ip_number, 'x }), ({ #'query_ip_number, 'y }) })
+	));
+	pl = ({});
+	for( i=0; i<sizeof(ppl); i++ )
+	{
+		pl += ({ sprintf( "%'.'-14s %-15s %3d %s \n",
+				capitalize(geteuid(ppl[i])),
+				query_ip_number(ppl[i]),
+				query_wiz_level(ppl[i])>0 ? query_wiz_level(ppl[i])
+					: ppl[i]->QueryProp(P_LEVEL),
+				query_wiz_level(ppl[i])>0 ? "W" : "P"
+		) });
+	}
+	write( implode(pl,"") );
+	return TRUE;
+}
+
+static int cmd_debuginfo()
+{
+	if( !becomes_obj() )
+		return error( "dinfo: TOS ist kein Objekt" );
+	debug_info( 0, pop() );
+	return TRUE;
+}
+
+static int cmd_pretty()
+{
+	pretty = !pretty;
+	if( pretty )
+		memo( "Schoenmodus an" );
+	else
+		memo( "Schoenmodus aus" );
+	return TRUE;
+}
+
+static int cmd_doprofile()
+{
+	do_profile=!do_profile;
+	if( do_profile )
+		memo( "Profile wird geladen" );
+	else
+		memo( "Profile wird nicht geladen" );
+	return TRUE;
+}
+
+static int cmd_evaluate()
+{
+	string str;
+	if( !sizeof(stack) ) return error( "evaluate: Stack ist leer" );
+	if( !stringp(top()) ) return error( "evaluate: TOS ist kein String" );
+	str = pop();
+	return do_cmd( str );
+}
+
+static void write_memory( string nam, string str, int flag, string file )
+{
+	if( flag ) write_file( file, nam + " = " + str + "\n" );
+}
+
+static int cmd_dump()
+{
+	string file;
+
+	if( !sizeof(stack) || !stringp(top()) )
+		file = "/players/"+getuid(PL)+"/.memory.o";
+	else
+		file = pop();
+	rm( file );
+	write_file( file, "# Dump des Tellerstapels vom " + dtime(time()) + "\n" );
+	write_file( file, "# Owner = "+capitalize(getuid(PL))+"\n" );
+	walk_mapping( memory, #'write_memory, file );
+	return TRUE;
+}
+
+static int restore_line( string line )
+{
+	string nam,str;
+	if( sscanf( line, "%s = %s", nam, str ) != 2 )
+		return error( "restore: Fehler im file" );
+	memory += ([ nam: str; 1 ]);
+  return 1;
+}
+
+static int cmd_restore()
+{
+	string str, *lines;
+
+	if( !sizeof(stack) || !stringp(top()) )
+		str = "/players/"+getuid(PL)+"/.memory.o";
+	else
+		str = pop();
+
+	if(file_size(str)<=0)
+		return error( "restore: kann '"+str+"' nicht laden" );
+	lines = regexp( old_explode( read_file(str), "\n" ), "^[^#]" );
+	map( lines, #'restore_line );
+	return TRUE;
+}
+
+static int cmd_if()
+{
+	if( sizeof(stack) < 3 )
+		return error( "if: zuwenig Argumente" );
+	if( !pop() )
+		cmd_swap();
+	pop();
+	return TRUE;
+}
diff --git a/obj/tools/teller/t_efun.c b/obj/tools/teller/t_efun.c
new file mode 100644
index 0000000..c1a2ded
--- /dev/null
+++ b/obj/tools/teller/t_efun.c
@@ -0,0 +1,416 @@
+static efun_regexp( arg1, arg2 )
+{
+	return regexp( arg1, arg2 );
+}
+
+static efun_all_inventory( arg )
+{
+	return all_inventory( arg );
+}
+
+static efun_break_string( arg1, arg2, arg3 )
+{
+	return break_string( arg1, arg2, arg3 );
+}
+
+static efun_capitalize( arg )
+{
+	return capitalize( arg );
+}
+
+static efun_cat( arg1, arg2, arg3 )
+{
+	return cat( arg1, arg2, arg3 );
+}
+
+static efun_clear_bit( arg1, arg2 )
+{
+	return clear_bit( arg1, arg2 );
+}
+
+static efun_crypt( arg1, arg2 )
+{
+	return crypt( arg1, arg2 );
+}
+
+static efun_ctime( arg )
+{
+	return ctime( arg );
+}
+
+static efun_debug_info( arg1, arg2 )
+{
+	return debug_info( arg1, arg2 );
+}
+
+static efun_deep_inventory( arg )
+{
+	return deep_inventory( arg );
+}
+
+static efun_environment( arg )
+{
+	return environment( arg );
+}
+
+static efun_explode( arg1, arg2 )
+{
+	return old_explode( arg1, arg2 );
+}
+
+static efun_file_name( arg )
+{
+	return object_name( arg );
+}
+
+static efun_file_size( arg )
+{
+	return file_size( arg );
+}
+
+static efun_filter_array( arg1, arg2, arg3, arg4 )
+{
+	return filter( arg1, arg2, arg3, arg4 );
+}
+
+static efun_find_call_out( arg )
+{
+	return find_call_out( arg );
+}
+
+static efun_find_living( arg )
+{
+	return find_living( arg );
+}
+
+static efun_find_object( arg )
+{
+	return find_object( arg );
+}
+
+static efun_find_player( arg )
+{
+	return find_player( arg );
+}
+
+static efun_function_exists( arg1, arg2 )
+{
+	return function_exists( arg1, arg2 );
+}
+
+static efun_implode( arg1, arg2 )
+{
+	return implode( arg1, arg2 );
+}
+
+static efun_interactive( arg )
+{
+	return interactive( arg );
+}
+
+static efun_intp( arg )
+{
+	return intp( arg );
+}
+
+static efun_last_reboot_time()
+{
+	return last_reboot_time();
+}
+
+static efun_living( arg )
+{
+	return living( arg );
+}
+
+static efun_lower_case( arg )
+{
+	return lower_case( arg );
+}
+
+static efun_get_dir( arg1, arg2 )
+{
+	return get_dir( arg1, arg2 );
+}
+
+static efun_map_array( arg1, arg2, arg3, arg4 )
+{
+	return map( arg1, arg2, arg3, arg4 );
+}
+
+static efun_member( arg1, arg2 )
+{
+	return member( arg1, arg2 );
+}
+
+static efun_objectp( arg )
+{
+	return objectp( arg );
+}
+
+static efun_pointerp( arg )
+{
+	return pointerp( arg );
+}
+
+static efun_present( arg1, arg2 )
+{
+	return present( arg1, arg2 );
+}
+
+static efun_process_string( arg )
+{
+	return process_string( arg );
+}
+
+static efun_query_actions( arg1, arg2 )
+{
+	return query_actions( arg1, arg2 );
+}
+
+static efun_query_idle( arg )
+{
+	return query_idle( arg );
+}
+
+static efun_query_ip_name( arg )
+{
+	return query_ip_name( arg );
+}
+
+static efun_query_ip_number( arg )
+{
+	return query_ip_number( arg );
+}
+
+static efun_query_load_average()
+{
+	return query_load_average();
+}
+
+static efun_query_snoop( arg )
+{
+	return query_snoop( arg );
+}
+
+static efun_random( arg )
+{
+	return random( arg );
+}
+
+static efun_read_bytes( arg1, arg2, arg3 )
+{
+	return read_bytes( arg1, arg2, arg3 );
+}
+
+static efun_read_file( arg1, arg2, arg3 )
+{
+	return read_file( arg1, arg2, arg3 );
+}
+
+static efun_remove_call_out( arg )
+{
+	return remove_call_out( arg );
+}
+
+static efun_remove_interactive( arg )
+{
+	return remove_interactive( arg );
+}
+
+static efun_rusage()
+{
+	return rusage();
+}
+
+static efun_say( arg1, arg2 )
+{
+	if( !arg2 ) return say( arg1 );
+	return say( arg1, arg2 );
+}
+
+static efun_set_bit( arg1, arg2 )
+{
+	return set_bit( arg1, arg2 );
+}
+
+static efun_set_heart_beat( arg )
+{
+	return set_heart_beat( arg );
+}
+
+static efun_set_living_name( arg )
+{
+	return set_living_name( arg );
+}
+
+static efun_sizeof( arg )
+{
+	return sizeof( arg );
+}
+
+static efun_sort_array( arg1, arg2, arg3 )
+{
+	return sort_array( arg1, arg2, arg3 );
+}
+
+static efun_stringp( arg )
+{
+	return stringp( arg );
+}
+
+static efun_strlen( arg )
+{
+	return sizeof( arg );
+}
+
+static efun_tail( arg )
+{
+	return tail( arg );
+}
+
+static efun_shout( arg )
+{
+	return shout( arg );
+}
+
+static efun_tell_object( arg1, arg2 )
+{
+	return tell_object( arg1, arg2 );
+}
+
+static efun_tell_room( arg1, arg2, arg3 )
+{
+	return tell_room( arg1, arg2, arg3 );
+}
+
+static efun_test_bit( arg1, arg2 )
+{
+	return test_bit( arg1, arg2 );
+}
+
+static efun_time()
+{
+	return time();
+}
+
+static efun_unique_array( arg1, arg2, arg3 )
+{
+	return unique_array( arg1, arg2, arg3 );
+}
+
+static efun_users()
+{
+	return users();
+}
+
+static efun_version()
+{
+	return version();
+}
+
+static efun_write( arg )
+{
+	return write( arg );
+}
+
+static efun_write_bytes( arg1, arg2, arg3 )
+{
+	return write_bytes( arg1, arg2, arg3 );
+}
+
+static efun_write_file( arg1, arg2 )
+{
+	return write_file( arg1, arg2 );
+}
+
+static efun_geteuid( arg )
+{
+	return geteuid( arg );
+}
+
+static efun_getuid( arg )
+{
+	return getuid( arg );
+}
+
+static efun_first_inventory( arg )
+{
+	return first_inventory( arg );
+}
+
+static efun_next_inventory( arg )
+{
+	return next_inventory( arg );
+}
+
+static efun_inherit_list( arg )
+{
+	return inherit_list( arg );
+}
+
+static efun_strstr( arg1, arg2, arg3 )
+{
+	return strstr( arg1, arg2, arg3 );
+}
+
+static efun_program_time( arg )
+{
+	return program_time( arg );
+}
+
+static efun_get_error_file( arg1, arg2 )
+{
+	return get_error_file( arg1, arg2 );
+}
+
+static efun_set_prompt( arg1, arg2 )
+{
+	return set_prompt( arg1, arg2 );
+}
+
+static efun_filter_objects( arg1, arg2, arg3 )
+{
+	return filter_objects( arg1, arg2, arg3 );
+}
+
+static efun_map_objects( arg1, arg2, arg3 )
+{
+	return map_objects( arg1, arg2, arg3 );
+}
+
+static efun_transpose_array( arg )
+{
+	return transpose_array( arg );
+}
+
+static efun_query_once_interactive( arg )
+{
+	return query_once_interactive( arg );
+}
+
+static efun_wizlist( arg )
+{
+	wizlist( arg );
+	return 1;
+}
+
+static efun_mkdir( str )
+{
+	return mkdir( str );
+}
+
+static efun_rm( str )
+{
+	return rm( str );
+}
+
+static efun_country( str )
+{
+	return country( str );
+}
+
+static efun_dtime( val )
+{
+	return dtime( val );
+}
diff --git a/obj/tools/teller/t_player.c b/obj/tools/teller/t_player.c
new file mode 100644
index 0000000..f72b100
--- /dev/null
+++ b/obj/tools/teller/t_player.c
@@ -0,0 +1,60 @@
+// ----------------------------------------------------------------------
+// Zwischenobjekt zum Lesen der Spielerdaten auch von nichteingeloggten.
+// ----------------------------------------------------------------------
+#include "teller.h"
+
+inherit "std/player/base";
+
+create() {
+	used_attributes_offsets = ([]);
+}
+
+string name;
+string race;
+string hands;
+mapping autoload;
+
+_query_name() { return name; }
+playername() { return name; }
+_set_name( str ) { return name=str; }
+
+_query_race()
+{
+	if( race ) return race;
+		
+	return "/secure/master"
+		->query_player_object(lower_case(name))
+		->QueryProp(P_RACE);
+}
+
+_query_hands()
+{
+	if( hands ) return hands;
+	return ({ " nicht", 0 });
+}
+
+_query_autoload()
+{
+	return autoload;
+}
+
+Load( str )
+{
+	restore_object( "/save/"+str[0..0]+"/"+str );
+}
+
+Notlogged() { return TRUE; }
+
+QueryMoney()
+{
+	if( !mappingp(autoload) )
+		return 0;
+	return autoload["/items/money"];
+}
+
+QueryEnemies()
+{
+        return ({({}),({})});
+}
+
+QueryAttributeOffset( arr ) { return "?"; }
diff --git a/obj/tools/teller/teller.c b/obj/tools/teller/teller.c
new file mode 100644
index 0000000..5f34f06
--- /dev/null
+++ b/obj/tools/teller/teller.c
@@ -0,0 +1,686 @@
+// ----------------------------------------------------------------------
+// Tellerstapel, ein Stackorientiertes magiertool.
+// Rumata 15.09.93
+// ----------------------------------------------------------------------
+#include "teller.h"
+inherit "std/secure_thing";
+inherit T_CMDS;
+inherit T_EFUN;
+inherit "std/more";
+
+#include "/secure/wizlevels.h"
+
+nosave mapping tcommands;
+nosave string owner_name;
+
+void logaccess( string s );
+static void do_msg( string s, object o );
+static string|int parseNext( string str, mixed auxstack, string funname );
+static mixed nextAlpha( string s );
+static varargs void restore_stack( string* argv, int args );
+static int do_call( string fun, int mit_return );
+static int do_call_efun( string fun, int mit_return );
+static int do_lpc( string str, int mit_return );
+static int do_cmd( string str );
+int error( string s );
+
+static void tAddCmd( mixed cmd )
+{
+	int i;
+
+	//if (find_player("rikus")) tell_object(find_player("rikus"),sprintf("%O\n",cmd));
+	if( !pointerp(cmd) )
+    m_add(tcommands, cmd, cmd);
+	else
+		for( i=0; i<sizeof(cmd); i++ )
+      m_add(tcommands, cmd[i], cmd[0]);
+}
+
+void create()
+{
+	if( IS_BLUE(ME) ) return;
+	if( !find_player(getuid()) ) return;
+	owner_name = find_player(getuid())->name(WESSEN,1);
+
+	secure_thing::create();
+	t_cmds::create();
+	SetProp( P_NAME, "Tellerstapel" );
+	SetProp( P_GENDER, MALE );
+	AddId( ({"teller","stapel","tellerstapel"}) );
+	SetProp( P_NODROP, "Wozu denn?\n" );
+	SetProp( P_NEVERDROP, 1 );
+	SetProp( P_READ_MSG, "Die Gravur besagt: 'Aus der Rumata-Manufaktur'\n" );
+	tcommands = ([]);
+	
+	tAddCmd( "say" );
+	tAddCmd( ({"names","nam"}) );
+	tAddCmd( "secureinv" );
+	tAddCmd( "logaccess" );
+	tAddCmd( "pretty" );
+	tAddCmd( "doprofile" );
+	tAddCmd( ({"clear","clr"}));
+	tAddCmd( "pop" );
+	tAddCmd( "dup" );
+	tAddCmd( "swap" );
+	tAddCmd( ({"evaluate","eval"}) );
+
+	tAddCmd( "here" );
+	tAddCmd( "me" );
+
+	tAddCmd( ({"memory","mem"}) );
+	tAddCmd( "dump" );
+	tAddCmd( "restore" );
+	tAddCmd( "top" );
+	tAddCmd( "wer" );
+	tAddCmd( ({"stack","stk"}) );
+	tAddCmd( ({"snoopers","snoop"}) );
+	tAddCmd( "callouts" );
+	tAddCmd( ({"callouts_bang","callouts!"}) );
+	tAddCmd( ({"heartbeats","beats"}) );
+	tAddCmd( "inv" );
+	tAddCmd( ({"rekinv","rinv"}) );
+	tAddCmd( "scan" );
+	tAddCmd( "if" );
+
+	tAddCmd( ({"array","arr"}) );
+	tAddCmd( ({"split","spl"}) );
+	tAddCmd( ({"player","pl"}) );
+	tAddCmd( ({"living","lv"}) );
+	tAddCmd( ({"object","ob"}) );
+
+	tAddCmd( ({"debuginfo","dinfo"}) );
+	tAddCmd( ({"clone","cln"}) );
+	tAddCmd( ({"update","upd"}) );
+	tAddCmd( ({"update_bang","update!","upd!"}) );
+	tAddCmd( ({"move","mv"}) );
+	tAddCmd( ({"destruct","dest","des"}) );
+	tAddCmd( ({"remove","rem"}) );
+	tAddCmd( ({"destruct_bang","destruct!","dest!","des!","remove!","rem!"}) );
+	tAddCmd( ({"roomupdate","rupdate","rupd","rup"}) );
+	tAddCmd( ({"roomupdate_bang","roomupdate!","rupdate!","rupd!","rup!"}) );
+	tAddCmd( ({"extroomupdate","rupdate*","rupd*","rup*"}) );
+	tAddCmd( ({"extroomupdate_bang","roomupdate*!","rupdate*!","rupd*!","rup*!",
+		"rup!*" }) );
+	tAddCmd( ({"cleanof","clnof"}) );
+	tAddCmd( ({"cleanof_bang","cleanof!","clnof!"}) );
+
+	tAddCmd( "ping" );
+}
+
+void init()
+{
+	//logaccess( "init" );
+	::init();
+	if ( !IS_WIZARD(PL) || environment()!=PL )
+		return;
+	add_action( "parse", ",", 1 );
+	add_action( "jonglier", "jonglier", 1 );
+	add_action( "message", "message" );
+	add_action( "message", "msg" );
+}
+
+short()
+{
+	logaccess( "short" );
+	return "Ein Stapel Teller.\n";
+}
+
+id( str )
+{
+	logaccess( "id" );
+	return ::id(str);
+}
+
+name( casus, dem, force )
+{
+	logaccess( "name" );
+	return ::name( casus, dem, force );
+}
+
+long()
+{
+	string answer;
+	
+	answer =
+		 "Du betrachtest "+owner_name+" magischen Tellerstapel. Die einzelnen Teller sind\n"
+		+"bis auf eine kleine Inschrift auf dem Boden weiss. Wenn Du hiermit\n"
+		+"jonglierst, kann ALLES passieren.\n";
+
+	if( getuid(PL) == getuid() )
+	{
+		answer +=
+			( mit_say
+				? "Die Teller sind beim Jonglieren sichtbar.\n"
+				: "Die Teller sind beim Jonglieren unsichtbar.\n"
+			) + ( mit_namen
+				? "Die Teller nennen die Dinge beim Namen.\n"
+				: "Die Teller nennen die Dinge beim Filenamen.\n"
+			) + ( pretty
+				? "Die Teller haben ein verschnoerkeltes Muster.\n"
+				: "Die Teller sind geradezu schmucklos.\n"
+			) + ( dologaccess
+				? "Die Teller spueren Zugriffe.\n"
+				: ""
+			) + ( secureinv
+				? "Die Teller bewachen Dein Inventar.\n"
+				: ""
+			) + ( do_profile
+				? "Die Teller werden beim Einloggen aktiv.\n"
+				: ""
+			);
+	} 
+	return answer; 	
+}
+
+void _set_autoloadobj( mixed val )
+{
+	if( !pointerp(val) ) return;
+	val += ({ 0,0,0,0,0,0 });
+	mit_namen = val[0];
+	mit_say = val[1];	
+	set_heart_beat( secureinv = val[2] );
+	dologaccess = val[3];
+	pretty = val[4];
+	do_profile = val[5];
+	if( do_profile ) call_out( "_load_profile", 0, this_player() );
+}
+
+mixed _query_autoloadobj()
+{
+	return ({ mit_namen, mit_say, secureinv, dologaccess, pretty,
+		do_profile });
+}
+
+void _load_profile( object pl )
+{
+	string pf,errmsg;
+
+	if( geteuid() && pl && geteuid(pl)==geteuid()
+		&& file_size(pf="/players/"+geteuid()+"/.profile.c")>0
+		&& (errmsg=catch(call_other(pf,"setup",pl)))
+	)
+		printf( "Error loading profile: %O\n", errmsg );
+}
+
+void logaccess( string str )
+{
+	if( RPL && dologaccess && getuid()!=getuid(RPL) && find_player(getuid())
+		&& previous_object() && getuid(previous_object()) != ROOTID
+		&& query_wiz_level(getuid())>=query_wiz_level(getuid(RPL))
+	)
+		tell_object( find_player(getuid()), "MEMO: " + str + "() von "
+			+ RPL->name(WEM) + " via ["
+			+ object_name(previous_object())+"].\n"
+		);
+	if( secureinv && find_player(getuid()) && !member(heart_beat_info(),ME) )
+	{
+		tell_object( find_player(getuid()), "MEMO: heart_beat restartet!.\n" );
+		set_heart_beat(TRUE);
+	}
+}
+
+// ----------------------------------------------------------------------
+// Hilfe-Funktionen
+// ----------------------------------------------------------------------
+static int secure()
+{
+	int i;
+	if( process_call() || secure_level()<query_wiz_level(RPL) ) return 0;
+	for( i=0; previous_object(i)==RPL; i++ )
+		;
+	return (!previous_object(i) || previous_object()==ME)
+		 && getuid()==getuid(RPL) && IS_WIZARD(RPL);
+}
+
+int jonglier( string str )
+{
+	string arg;
+
+	logaccess( "jongliere" );
+	if( !str || id(str) || sscanf( str, "mit %s", arg ) == 1 )
+	{
+		if( !secure() )
+			write(
+				"Dieser Tellerstapel ist nichts fuer Dich. Suche Dir einen eigenen.\n"
+			);
+		else
+			write(
+			  "Der Jonglierbefehl lautet \";\" oder \",\".\n"
+			+	"\";hilfe <befehl>\" gibt Dir Hilfestellung.\n"
+			);
+		return TRUE;
+	}
+  return 0;
+}
+			
+static int zeige_hilfe( string str )
+{
+	if( !stringp(str) ) str = "general";
+	str = implode( old_explode( HELP(str), "/../" ), "/" );
+	if( file_size(str) > 0 )
+	{
+		More( str, 1 );
+		return TRUE;
+	}
+	write( "Zu diesem Thema gibt es keine Hilfe.\n" );
+	return TRUE;
+}
+
+int message( string str )
+{
+	string pl;
+	object dest;
+
+	if( !secure() ) return FALSE;
+
+	if( str )
+	{
+		if( sscanf( str, "to %s", pl ) == 1 )
+			str = pl;
+		if( !(dest = find_player(str) ) )
+		{
+			write( capitalize(str) + " ist nicht eingeloggt.\n" );
+			return TRUE;
+		}
+	}
+	else
+		dest = 0;
+
+	do_msg( "<Mitteilung von "+PL->name(WEM)+">", dest );
+	write( "Mitteilung"
+		+ ( dest ? " an " + dest->name(WEN) : "" )
+		+ " kann mit '**' beendet werden.\n" );
+
+	input_to( "more_message", 0, dest );
+	return TRUE;
+}
+
+static void do_msg( string str, object obj )
+{
+	if( obj )
+		tell_object( obj, str + "\n" );
+	else
+		say( str + "\n" );
+}
+
+static int more_message( string str, object obj )
+{
+	if( str != "**" )
+	{
+		do_msg( str, obj );
+		input_to( "more_message", 0, obj );
+	}
+	else
+		do_msg( "<Ende der Mitteilung>", obj );
+	return TRUE;
+}
+
+// ----------------------------------------------------------------------
+// Parse-Funktionen
+// ----------------------------------------------------------------------
+int parse( string str )
+{
+	int i;
+	string arg, rest;
+
+	if( !secure() ) return FALSE;
+
+	// Die Hilfe faellt aus der allgemeinen Syntax heraus !
+	if( query_verb()[1..] == "hilfe" || query_verb()[1..] == "help" )
+		return zeige_hilfe( str );
+
+	if( str=this_player()->_unparsed_args() )
+		str = query_verb()[1..]+" "+str;
+	else
+		str = query_verb()[1..];
+
+	fehler_passiert = FALSE;
+	while( !fehler_passiert && str && str!="" )
+		str = parseNext( str, ({}), "-ROOT-" );
+
+	if( mit_say && !PL->QueryProp(P_INVIS) )
+	{
+		if( !fehler_passiert )
+			say( PL->name(WER)+" jongliert mit "
+				+ PL->QPP(MALE,WEM,PLURAL)+" Tellern.\n" );
+		else
+			say( PL->name(WEM)+" faellt beim Jonglieren ein Teller herunter.\n" );
+	}
+	if( !fehler_passiert )
+		write( "OK.\n" );
+	return TRUE;
+}
+
+static string|int parseNext( string str, mixed auxstack, string funname )
+{
+	mixed *res;
+	string arg, rest;
+	bool mit_return;
+	int num;
+
+	while( str!="" && str[0]==' ' )
+		str = str[1..];
+	if( str=="" )
+		return FALSE;
+
+	switch( str[0] )
+	{
+		case ';': // ---------------------------------------- Kommandobeginn
+		case ',':
+			push( ";" );
+			return str[1..];
+		case '\"': // --------------------------------------- Stringkonstante
+			if( sscanf( str, "\"%s\"%s", arg, rest )==2 )
+			{
+				/* test auf numerisch fehlt hier noch */
+				push_str( arg );
+				return rest;
+			}
+			if( !sscanf( str, "\"%s\"", arg ) )
+				return error( "String ohne Ende" );
+			push_str( arg );
+			return FALSE;
+		case '#': // ---------------------------------------- Zahl
+			res = nextAlpha( str[1..] );
+			if( sscanf( res[0], "%d", num ) == 1 )
+			{
+				push( num );
+				return res[1];
+			}
+			return error( "'#': keine erlaubte Zahl" );
+		case '^': // ---------------------------------------- Hole env
+			if( !becomes_obj() )
+				return error( "'^': TOS ist kein Objekt" );
+			if( !environment(top()) )
+				return error( "'^': TOS hat kein Environment" );
+			push(environment(pop()));
+			return str[1..];
+		case '.': // ---------------------------------------- Hole aus inv
+			if( !becomes_obj() )
+				return error( "'.': TOS ist kein Objekt" );
+			res = nextAlpha( str[1..] );
+			calcinv( res[0] );
+			return res[1];
+		case '<': // ---------------------------------------- Recall
+			if( !sscanf( str, "<%s", arg ) || arg=="" )
+				return error( "'<': Variablenname fehlt" );
+			res = nextAlpha( str[1..] );
+			do_recall(res[0]);
+			return res[1];
+		case '>': // ---------------------------------------- Store
+			if( !sscanf( str, ">%s", arg ) || arg=="" )
+				return error( "'>': Variablenname fehlt" );
+			res = nextAlpha( str[1..] );
+			do_store(res[0]);
+			return res[1];
+		case '-': // ---------------------------------------- Call mit '-'
+			str = str[1..];
+			if( mit_return = (str[0] == '-') )
+				str = str[1..];
+			res = nextAlpha( str[1..] );
+			switch( str[0] )
+			{
+				case '>':
+					if( do_call( res[0], mit_return ) )
+						return res[1];
+					break;
+				case '*':
+					if( do_call_efun( res[0], mit_return ) )
+						return res[1];
+					break;
+				default:
+					return error( "'-': '>' oder '*' erwartet" );
+			}
+			return FALSE;
+		case '\'': // --------------------------------------- Call
+			str = str[1..];
+			if( mit_return = (str[0] == '\'') )
+				str = str[1..];
+			res = nextAlpha( str );
+			if( do_call( res[0], mit_return ) )
+				return res[1];
+			return FALSE;
+		case '`': // --------------------------------------- Call Efun
+			str = str[1..];
+			if( mit_return = (str[0] == '`') )
+				str = str[1..];
+			res = nextAlpha( str );
+			if( do_call_efun( res[0], mit_return ) )
+				return res[1];
+			return FALSE;
+		case '@': // --------------------------------------- Evaluate
+			str = str[1..];
+			if( mit_return = (str[0] == '@') )
+				str = str[1..];
+			if( do_lpc( str, mit_return ) )
+				return FALSE;
+			return FALSE;
+		case '!': // ------------------------------------------- META
+			if( sscanf(str,"!%d%s",num,arg)==2 )
+			{
+				if( num>=sizeof(auxstack) )
+					return error( "'"+funname+"': zu weing Argumente" );
+				push( auxstack[num] );
+			}
+			else
+			{
+				res = nextAlpha( str[1..] );
+				memory += ([ res[0]: res[1]; 1 ]);
+				return FALSE;
+			}
+			return arg;
+		default: // ----------------------------------------- default
+			res = nextAlpha( str );
+			do_cmd(res[0]);
+			return res[1];
+	}
+	return memo( "Etwas Seltsames ist passiert" );
+}
+
+static int do_cmd( string str )
+{
+	string fun, filename, *spl;
+	mixed* oldstack;
+	int i,max,num;
+
+	if( member(memory,str) )
+	{
+		fun = memory[str,0];
+		if( !memory[str,1] ) // normale Variablen
+		{
+			push( fun );
+			return TRUE;
+		}
+		oldstack = stack;
+		stack = ({});
+		spl = regexplode( fun, "![0-9][0-9]*" );
+		max = -1;
+		for( i=1; i<sizeof(spl); i+=2 )
+			if( sscanf(spl[i],"!%d",num) && max<num ) max = num;
+
+		while( !fehler_passiert && fun && fun!="" )
+			fun = parseNext( fun, oldstack, str );
+		if( fehler_passiert )
+		{
+			stack = oldstack;
+			return FALSE;
+		}
+		stack = stack + oldstack[(max+1)..];
+		return TRUE;
+	}
+  else if (fun=tcommands[str])
+		return call_other(ME,"cmd_"+fun);
+
+	if( sscanf( str, "%d", i ) )
+		push( i );
+	else
+	{
+		filename = MASTER->_get_path(str,getuid());
+		if( filename[<1] == '.' )
+			filename = filename[0..<2];
+		if( file_size( filename+".c" ) != -1 )
+			push( filename );
+		else
+			push( str );
+	}
+	return TRUE;
+}
+
+static mixed nextAlpha( string str )
+{
+	int pos;
+
+	for( 
+		pos=0;
+		pos<sizeof(str) && member( " #\";,^.><", str[pos] ) == -1;
+		pos++
+	)
+		;
+
+	if( pos==sizeof(str) )
+		return ({ str, 0 });
+	else
+		return ({ str[0..pos-1], str[pos..] });
+}
+
+static varargs void restore_stack( string* argv, int args )
+{
+	int i;
+	if( !args )
+		args = sizeof(argv);
+	if( sizeof(stack) ) push( ";" );
+	for( i=0; i<args; i++ )
+		push(argv[i]);
+}
+
+static int do_call( string fun, int mit_return )
+{
+	int args;
+	string err;
+	mixed result;
+	mixed arg, *argv;
+
+	argv = ({});
+	while( sizeof(stack) && (arg=pop())!=";" )
+		argv = ({arg}) + argv;
+	if( !becomes_obj(argv) )
+	{
+		restore_stack( argv );
+		return error( "call: Funktion nicht an Objekt gerichtet" );
+	}
+	if( !function_exists(fun,argv[0]) )
+	{
+		restore_stack(argv);
+		return error( "call: Funktion nicht gefunden" );
+	}
+
+	args = sizeof(argv);
+	argv += ({ 0,0,0,0,0 });
+	err=(catch(result=call_other(argv[0],fun,
+		argv[1],argv[2],argv[3],argv[4],argv[5])));
+	if( err )
+	{
+		restore_stack( argv, args );
+		return error( "call: "+err[1..<2] );
+	}
+	else
+	{
+		write( "=> " );
+		dump_obj( result, 3 );
+		if( mit_return )
+			push( result );
+	}
+	return TRUE;
+}
+
+static int do_call_efun( string fun, int mit_return )
+{
+	int args;
+	string err;
+	mixed result;
+	mixed arg, *argv;
+
+	argv = ({});
+	while( sizeof(stack) && (arg=pop())!=";" )
+		argv = ({arg}) + argv;
+
+	if( !function_exists( "efun_"+fun ) )
+		return error( "call efun: \""+fun+"\" nicht benutzbar" );
+	fun = "efun_"+fun;
+
+	args = sizeof(argv);
+	argv += ({ 0,0,0,0,0 });
+	err=(catch(result=call_other(ME,fun,
+			argv[0],argv[1],argv[2],argv[3],argv[4])));
+	if( err )
+	{
+		restore_stack( argv, args );
+		return error( "call efun: "+err[1..<2] );
+	}
+	else
+	{
+		write( "=> " );
+		dump_obj( result, 3 );
+		if( mit_return )
+			push( result );
+	}
+	return TRUE;
+}
+
+static int do_lpc( string str, int mit_return )
+{
+	int args, val;
+	string err;
+	string file, cmd, pre, post;
+	mixed result;
+	mixed arg, *argv;
+
+	argv = ({});
+	while( sizeof(stack) && (arg=pop())!=";" )
+		argv = argv + ({arg});
+
+	file = "/players/"+getuid()+"/.teller";
+	cmd = "#include <language.h>\n#include <properties.h>\n"
+		+ "#include <moving.h>\n#include <defines.h>\n#include <terminal.h>\n"
+		+ "#include <wizlevels.h>\n#include <ansi.h>\ncreate(){}doit(){";
+
+	while( str!="" && sscanf( str, "%s@%s", pre, post ) == 2 )
+	{
+		if( sscanf( str, "%s@%d@%s", pre, val, post ) == 3 )
+		{
+			if( sizeof(argv)<=val || val<0 )
+			{
+				restore_stack( argv, args );
+				return error( "lpc: Illegaler Index auf den Stack" );
+			}
+			cmd += pre + DumpObj(argv[val]);
+			str = post;
+		}
+		else
+			if( sscanf( str, "%s@%s", pre, post ) == 2 )
+			{
+				cmd += pre + "@";
+				str = post;
+			}
+	}
+	cmd += str;
+	rm( file+".c" );
+	write_file( file+".c" , cmd+";}\n" );
+	if( find_object(file) ) destruct( find_object(file) );
+	err=(catch(result=call_other(file,"doit")));
+	if( err )
+	{
+		//rm( file+".c" );
+		restore_stack( argv, args );
+		return error( "lpc: "+err[1..<2] );
+	}
+	else
+	{
+		//rm( file+".c" );
+		write( "=> " );
+		dump_obj( result, 3 );
+		if( mit_return )
+			push( result );
+	}
+	return TRUE;
+}
diff --git a/obj/tools/teller/teller.h b/obj/tools/teller/teller.h
new file mode 100644
index 0000000..841d796
--- /dev/null
+++ b/obj/tools/teller/teller.h
@@ -0,0 +1,22 @@
+#ifndef _TELLER_H_
+#define _TELLER_H_
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+
+#define TRUE 1
+#define FALSE 0
+#define bool int
+
+#define INFORMER "/secure/merlin"
+//#define MASTER "/secure/master"
+
+#define T_BASE "/obj/tools/teller/t_base"
+#define T_EFUN "/obj/tools/teller/t_efun"
+#define T_CMDS "/obj/tools/teller/t_cmds"
+#define T_PLAYER "/obj/tools/teller/t_player"
+
+#define HELP(x) ("/obj/tools/teller/hilfe/"+(x))
+
+#endif
diff --git a/obj/tools/tester/test.c b/obj/tools/tester/test.c
new file mode 100644
index 0000000..dd70fa8
--- /dev/null
+++ b/obj/tools/tester/test.c
@@ -0,0 +1,1146 @@
+/*
+**   Objekt-Tester - Mischung aus Rikus' Detailtester und Rochus' Test-NPC.
+**
+*****************************************************************************
+**
+**    Urspruengliche Objekte:
+**    ~~~~~~~~~~~~~~~~~~~~~~~
+**    /players/rikos/obj/rdet.c           Rikus@MorgenGrauen         25.05.96
+**
+**    /players/rochus/mon/tester.c        Rochus@MorgenGrauen        18.10.94
+**
+*****************************************************************************
+**
+**    Dieses Objekt:                                          [ Version 0.8 ]
+**    ~~~~~~~~~~~~~~
+**    /players/paracelsus/tools/otest.c   Paracelsus@Morgengrauen    28.02.98
+**
+**    Die Weitergabe, Verwendung oder Veraenderung ist nur erlaubt, wenn
+**    diese Meldung nicht geaendert wird.
+**    Die Benutzung erfolgt auf eigene Gefahr, jede Verantwortung wird
+**    abgelehnt. Es wird keine Garantie gegeben, dass der Objekttester
+**    einwandfrei, vollstaendig oder gar zufriedenstellend arbeitet.
+**
+*****************************************************************************
+**
+**    ChangeLog:
+**    ~~~~~~~~~~
+**    V 0.1    28.02.98 11:00:00   Paracelsus
+**
+**                                   Rikus' Raumtester kopiert, optimiert.
+**
+**    V 0.2    28.02.98 11:30:00   Paracelsus
+**
+**                                   Keine direkte Ausgabe mehr sondern
+**                                   stattdessen ein More().
+**
+**    V 0.3    28.02.98 13:00:00   Paracelsus
+**
+**                                   Testen nicht mehr auf Raeume beschraenkt.
+**
+**    V 0.4    28.02.98 16:00:00   Paracelsus
+**
+**                                   Funktionen aus Rochus' Tester uebernommen
+**
+**    V 0.5    28.02.98 18:00:00   Paracelsus
+**
+**                                   Funktionalitaeten von Rikus und Rochus
+**                                   aneinander angepasst.
+**
+**    V 0.6    28.02.98 19:00:00   Paracelsus
+**
+**                                   Gerueche und Geraeusche eingebaut,
+**                                   erste Tests erfolgreich.
+**
+**    V 0.7    28.02.98 19:30:00   Paracelsus
+**
+**                                   Spaltenzahl einstellbar.
+**
+**    V 0.8    01.03.98 19:30:00   Paracelsus
+**
+**                                   Scannen ganzer Verzeichnisse. Geht jetzt
+**                                   zumindest bei Raeumen.
+**
+**    V 0.9    20.09.98 20:45:00   Paracelsus
+**
+**                                   Umgestellt von heart_beat() auf
+**                                   call_out(), damit this_player()
+**                                   erhalten bleibt.
+**	       13.10.08		   Chagall
+**
+**				     _query_details() und
+**				     _query_special_details auf
+**				     QueryProp() umgestellt
+**
+****************************************************************************
+**
+**   Eventuell auftretende Probleme:
+**   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+**   - Zu hohe eval_cost, wenn zu viele Details vorhanden sind.
+**     Groesster bisher getesteter Raum (ohne Fehler) : 63907
+**     Scheint also wohl doch kein Problem zu sein.
+**
+****************************************************************************
+**
+**   Ideen/ToDo:
+**   ~~~~~~~~~~~
+**
+**   - NPC-Infos testen.
+**
+****************************************************************************
+*/
+
+#include <defines.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <terminal.h>
+
+#define DATAFILE     "test.dat"
+#define DEFAULT_DATA "/obj/tools/tester/test.dat"
+
+#define P_OTEST_TYP "ob-test:typ"
+#define P_OTEST_SPL "ob-test:spalten"
+
+#define T_ROCHUS 1
+#define T_RIKUS  2
+
+#define T_PAUSE  4
+
+#pragma strong_types
+
+inherit "/std/thing";
+
+// Allgemein benoetigte globale Variablen
+
+int     scantyp,spalten;
+mapping all_dets;
+mixed   output;
+object  master;
+
+// Globale Variablen fuer das scannen ganzer Directories
+
+int     scanrun,scancount,scanpause,*scanerr;
+string  scandir,*scanfiles;
+object  testob;
+
+string x_long()
+{   string re,PRE,END;
+
+    switch (PL->QueryProp(P_TTY))
+    {
+        case "ansi"  :
+        case "vt100" : PRE=BOLD; END=NORMAL; break;
+        default      : PRE="";   END="";     break;
+    }
+
+    re = break_string(
+         "Mit diesem Objektpruefer kannst Du feststellen, welche Details, "+
+         "Gerueche oder Geraeusche in einem Raum bzw an einem Objekt noch "+
+         "nicht beschrieben sind.",78)+
+         "\n"+
+         "Syntax: %sotest [detail|smell|sound] {<objekt>[in mir|im raum]|"+
+             "hier}\n\n"+
+         "        otype [rikus|rochus]\n\n"+
+         "        ocolm [1|2|3|4|5|6|7|8|auto]\n\n"+
+         "        otdir <verzeichnis> <ausgabefile>%s\n\n"+
+         break_string(
+         "Mit %sotype%s kann man festlegen, ob man im 'Rikus'- oder im "+
+         "'Rochus'-Modus scannen will, mit %socolm%s die anzahl der Ausgabe"+
+         "spalten und mit %sotest%s wird dann die Auswertung gestartet. "+
+         "Mit dem Kommando %sotdir%s kann man sogar ganze Verzeichnisse "+
+         "auf einmal testen lassen. Das dauert natuerlich eine Weile. "+
+         "Ausserdem muss ein Verzeichnis '/log/<magiername>/' bestehen.",78)+
+         "Derzeitiger Scantyp:    %s'"+(scantyp==T_ROCHUS?"ROCHUS":"RIKUS")+
+         "'%s\n"+
+         "Derzeitige Spaltenzahl: %s%s%s\n%s";
+    return sprintf(re,PRE,END,PRE,END,PRE,END,PRE,END,PRE,END,PRE,END,PRE,
+           (spalten>0?sprintf("%d",spalten):"'AUTO'"),END,
+           (scanrun?sprintf(
+               "\nDerzeit Dirscan in:     %s%s%s\n"+
+               "Angenommene Restzeit:   %s(%s) [Fi:%d/%d|Er:%d|Sk:%d]%s\n",
+               PRE,scandir,END,PRE,
+               time2string("%h:%02m:%02s",(scanrun-1)*4*T_PAUSE),
+               (sizeof(scanfiles)-scanrun+1),(sizeof(scanfiles)),
+               scanerr[0],scanerr[1],END):""));
+}
+
+void create()
+{
+    if (!clonep(ME))
+        return;
+
+    ::create();
+
+// Allgemeine globale Variablen initialisieren
+
+    scantyp   = T_ROCHUS;
+    spalten   = 4;
+    all_dets  = ([]);
+    output    = 0;
+
+// Globale Variablen fuer den Directoryscan initialisieren
+
+    scandir   = 0;
+    scanrun   = 0;
+    scanfiles = ({});
+    scancount = 0;
+    scanpause = 0;
+    scanerr   = ({0,0});
+
+// Properties
+
+    SetProp(P_SHORT,"Ein Objektpruefer") ;
+    Set(P_LONG,#'x_long,F_QUERY_METHOD);
+    SetProp(P_NAME,"Objektpruefer");
+    SetProp(P_GENDER,MALE);
+    SetProp(P_AUTOLOADOBJ,1);
+    SetProp(P_VALUE,0);
+    SetProp(P_WEIGHT,0);
+    SetProp(P_NODROP,1);
+    SetProp(P_NEVERDROP,1);
+
+    AddId( ({"pruefer","objektpruefer"}) );
+
+    AddCmd( "otest","search" );
+    AddCmd( "otype","scanart" );
+    AddCmd( "ocolm","spaltenzahl" );
+    AddCmd( "otdir","dirtesten" );
+}
+
+void init()
+{
+    ::init();
+    if (PL->QueryProp(P_TESTPLAYER) || IS_LEARNER(PL))
+    {
+        scantyp=(PL->QueryProp(P_OTEST_TYP)||T_ROCHUS);
+        spalten=(PL->QueryProp(P_OTEST_SPL)||4);
+        return;
+    }
+    if (find_call_out("removen") == -1)
+      call_out("removen",0);
+}
+
+/***************************************************************************
+**
+**  Die Hilfsfunktionen des Objekttesters.
+**
+****************************************************************************
+*/
+
+void Output(string text)
+{
+    if (!text || !stringp(text))
+        return;
+    if (objectp(output))
+        output->More(text);
+    else if (stringp(output))
+        write_file(output,text);
+    else
+        write(text);
+}
+
+int removen()
+{
+    write("Dieses Tool ist nur fuer Testies und Magier!\n");
+    remove();
+    return 1;
+}
+
+string *gross(string s)
+{
+    return regexp(regexplode(s,"[^A-Za-z0-9]"),"\\<[A-Z]");
+}
+
+string *det_auswerten(string input)
+{   string *words,str,*re;
+    int    anf,n,i;
+
+    if (scantyp==T_RIKUS)
+        return gross(input);
+
+    re = ({});
+    if (!input || !stringp(input))
+        return re;
+
+    words=regexplode(input,"[^A-Za-z0-9]");
+        n=sizeof(words);
+    if (!n)
+        return re;
+
+    anf=1;
+    for (i=0;i<n;i++)
+    {
+        str=words[i];
+        if (str=="" || str==" ")
+            continue;
+        if (str=="." || str==":" || str=="?" || str=="!")
+        {
+            anf=1;
+            continue;
+        }
+        if (sizeof(regexp(({str}),"[^A-Za-z0-9]")))
+            continue;
+        if (sizeof(regexp(({str}),"\\<[0-9]*\\>")))
+            continue;
+        if (str!=capitalize(str))
+            continue;
+        if (anf)
+        {
+            anf=0;
+            continue;
+        }
+        re += ({ str });
+    }
+    return re;
+}
+
+varargs mixed test_details(string str, string def)
+{   string *strs,ostr,*strs2,suf;
+    int    i;
+
+    if (!str || !stringp(str) || !mappingp(all_dets))
+        return 0;
+
+    strs = ({str});
+    ostr = str;
+
+    if ((suf=str[<1..<1])=="s" || suf=="n" || suf=="e")
+    {
+        ostr  = str[0..<2];
+        strs += ({ ostr });
+        ostr += ("("+suf+")");
+    }
+    if ((suf=str[<2..<1])=="es" || suf=="en")
+    {
+        ostr  = str[0..<3];
+        strs += ({ ostr });
+        ostr += ("("+suf+")");
+    }
+    for ( strs2=({}),i=sizeof(strs)-1 ; i>=0 ; i--)
+        if (member(all_dets,strs[i]))
+        {
+            if (stringp(def) && all_dets[strs[i]]==def && str!=SENSE_DEFAULT)
+                continue;
+            strs2 += ({ strs[i] });
+	}
+    if (sizeof(strs2)>0)
+        return strs2;
+    return ostr;
+}
+
+string *eval_detail_entry(mixed key)
+{   mixed  re,h;
+    string *ra;
+    int    i;
+
+    if (!key || !stringp(key) || !mappingp(all_dets) || !member(all_dets,key))
+        return ({});
+
+    re=all_dets[key];
+    if (closurep(re))
+        re=funcall(re,key);
+    if (mappingp(re))
+    {
+        ra=filter(m_indices(re),#'stringp);
+        for ( h=({ re[0] }),i=sizeof(ra)-1 ; i>=0 ; i-- )
+            if (stringp(re[ra[i]]))
+                h += ({ re[ra[i]] });
+            else if (pointerp(re[ra[i]]))
+                h += re[ra[i]];
+        re=h;
+    }
+    if (pointerp(re))
+        return filter(re,#'stringp);
+    if (stringp(re))
+        return ({ re });
+    return ({});
+}
+
+/***************************************************************************
+**
+**  Die Funktion, die die (Special)Details im Raum untersucht.
+**
+****************************************************************************
+*/
+
+int search_d(object r)
+{   int     i,j,room;
+    mapping m;
+    string  *s,s1,*s2,PRE,END,*special,*zufinden,*details,re,*uebergehen;
+    mixed   mi;
+
+    if (!r || !objectp(r))
+    {
+        write("OTEST-ERROR: Kein (existierendes) Zielobjekt gefunden!\n");
+        return 1;
+    }
+
+// Die Ausgaben erfolgen zunaechst in einen String.
+
+    re="\n";
+
+// Handelt es sich um einen Raum?
+
+    room = (function_exists("int_long",r) ? 1 : 0 );
+
+// Terminaltyp des Magiers feststellen.
+
+    if (objectp(output))
+        switch (output->QueryProp(P_TTY))
+        {
+            case "ansi"  :
+            case "vt100" : PRE=BOLD; END=NORMAL; break;
+            default      : PRE="";   END="";     break;
+        }
+    else
+    {
+        PRE="";
+        END="";
+    }
+
+//  Liste der zu uebergehenden Details erzeugen. Die verwendete Methode hat den
+//  Vorteil, dass man nach einer Ergaenzung der Liste nicht das Tool neu laden
+//  und clonen muss. Das Tool erwartet, die Liste DATAFILE im gleichen
+//  Verzeichnis zu finden, in dem auch das Tool liegt.
+
+    s1=implode(explode(old_explode(object_name(ME),"#")[0],"/")[0..<2],"/")+"/"
+       DATAFILE;
+    if (file_size(s1)<1)
+        s1=DEFAULT_DATA;
+
+    uebergehen = regexp(regexplode(read_file(s1),"[^A-Za-z0-9]"),"\\<[a-z]");
+
+// Satz an Details, die auf jeden Fall vorhanden sein sollten, je nachdem
+// ob P_INDOORS gesetzt ist oder nicht.
+
+    if (room && r->QueryProp(P_INDOORS))
+        zufinden = ({"Boden","Decke","Wand","Waende","Raum"});
+    else if (room)
+        zufinden = ({"Boden","Himmel","Sonne","Wolke","Wolken"});
+    else
+        zufinden = ({});
+
+// Alle vorhandenen Details anzeigen.
+
+//    details = sort_array(m_indices(r->_query_details()||([])),#'<);
+    details = sort_array(m_indices(r->QueryProp(P_DETAILS)||([])),#'<);
+
+    if (scanrun<1)
+    {
+        if ((i=sizeof(details))>0)
+        {
+            for ( s1="",--i; i>=0 ; i-- )
+                s1 += (details[i]+(i?"\n":""));
+
+            re += sprintf(sprintf("%sListe der vorhandenen Details:%s\n\n"+
+                          "%s\n\n",PRE,END,("%-#78"+(spalten<1?"":
+                          sprintf(".%d",spalten))+"s")),s1);
+        }
+        else
+            re += sprintf("%sKeine Details gefunden.%s\n\n",PRE,END);
+    }
+
+// Alle vorhandenen SpecialDetails anzeigen.
+
+//    special=sort_array(m_indices(r->_query_special_details()||([])),#'<);
+    special=sort_array(m_indices(r->QueryProp(P_SPECIAL_DETAILS)||([])),#'<);
+
+    if (scanrun<1)
+    {
+        if ((i=sizeof(special))>0)
+        {
+            for ( s1="",--i ; i>=0 ; i-- )
+                s1 += (special[i]+(i?"\n":""));
+
+            re += sprintf(sprintf(
+                          "%sListe der vorhandenen SpecialDetails:%s\n\n"+
+                          "%s\n\n",PRE,END,("%-#78"+(spalten<1?"":
+                          sprintf(".%d",spalten))+"s")),s1);
+        }
+        else
+            re += sprintf("%sKeine SpecialDetails gefunden.%s\n\n",PRE,END);
+    }
+
+//  Die Listen der vorhandenen Details und SpecialDetails vereinigen.
+
+    details += special;
+
+//  Details und SpecialDetails auswerten - gar nicht so einfach.
+
+    all_dets = r->Query(P_DETAILS);
+
+    for ( s=({}),i=sizeof(details)-1 ; i>=0 ; i--)
+        s += eval_detail_entry(details[i]);
+
+    for ( s2=({}),i=sizeof(s)-1 ; i>=0 ; i-- )
+        if (stringp(s[i]))
+            if (member(s2,s[i])==-1)
+            {
+                s2 += ({ s[i] });
+                zufinden+=det_auswerten(s[i]);
+            }
+
+//  Grossgeschriebene Woerter aus der Langbeschreibung hinzufuegen.
+
+    if (function_exists("int_long",r))
+        zufinden += det_auswerten(
+            old_explode(r->int_long(master,master),"Es gibt ")[0]);
+    else
+        zufinden += det_auswerten(old_explode(r->long(),"enthaelt:")[0]);
+
+// Doppelte Eintraege eliminieren.
+
+    for ( s2=({}), i=sizeof(zufinden)-1 ; i>=0 ; i--)
+        if (member(s2,zufinden[i])==-1)
+            s2 += ({ zufinden[i] });
+
+// Alles klein machen.
+
+    zufinden=map(s2,#'lower_case);
+
+// Bei NPCs/Objekten die IDs rausfiltern.
+
+    if (!room)
+        zufinden -= r->QueryProp(P_IDS);
+
+// Die zu uebergehenden Details rausfiltern.
+
+    zufinden=filter((zufinden-uebergehen),#'stringp);
+
+// Schauen, welche Details fehlen und diese Ausgeben.
+
+    for ( s=({}),s2=({}),i=sizeof(zufinden)-1;i>=0;i--)
+    {
+        if (!(mi=test_details(zufinden[i])))
+            continue;
+        if (pointerp(mi))
+        {
+           for (j=sizeof(mi)-1 ; j>=0 ; j--)
+               if (member(s,mi[j])==-1)
+                   s += ({ mi[j] });
+        }
+        else if (stringp(mi) && member(s2,mi)==-1)
+            s2 += ({ mi });
+    }
+
+    s  = sort_array(s, #'<);
+    s2 = sort_array(s2,#'<);
+
+    if (scanrun<1)
+    {
+        if ((i=sizeof(s))>0)
+        {
+            for ( s1="",--i ; i>=0 ; i-- )
+                s1 += (s[i]+(i?"\n":""));
+
+            re += sprintf(sprintf("%sListe der gefundenen Details:%s\n\n"+
+                          "%s\n\n",PRE,END,("%-#78"+(spalten<1?"":
+                          sprintf(".%d",spalten))+"s")),s1);
+        }
+        else
+            re += sprintf("%sEs gibt keine Details.%s\n\n",PRE,END);
+    }
+
+    if ((i=sizeof(s2))>0)
+    {
+        for ( s1="",--i ; i>=0 ; i-- )
+           s1 += (s2[i]+(i?"\n":""));
+
+        re += sprintf(sprintf("%sListe der fehlenden Details:%s\n\n"+
+                      "%s\n\n",PRE,END,("%-#78"+(spalten<1?"":
+                      sprintf(".%d",spalten))+"s")),s1);
+    }
+    else
+        re += sprintf("%sEs fehlen keine Details.%s\n\n",PRE,END);
+
+    if (scanrun<1)
+        re += sprintf("%sFERTIG.%s\n",PRE,END);
+
+// Die eigentliche Textausgabe.
+
+    Output(re);
+
+    return 1;
+}
+
+/***************************************************************************
+**
+**  Hauptfunktion fuer das Testen von Geraeuschen und Geruechen.
+**
+****************************************************************************
+*/
+
+int search_sense(object r, int sense)
+{   string re,PRE,END,WAS,*senses,s1,*zufinden,*s2,*s3,def;
+    int    i,j;
+    mixed  mi;
+
+    if (!r || !objectp(r))
+    {
+        write("OTEST-ERROR: Kein (existierendes) Zielobjekt gefunden!\n");
+        return 1;
+    }
+    if (sense==SENSE_SMELL)
+    {
+        all_dets=r->Query(P_SMELLS)||([]);
+        WAS="Gerueche";
+    }
+    else if (sense==SENSE_SOUND)
+    {
+        all_dets=r->Query(P_SOUNDS)||([]);
+        WAS="Geraeusche";
+    }
+    else
+    {
+        write("OTEST-ERROR: Illegaler sense-Wert in search_sense()\n");
+        return 1;
+    }
+
+    re = "\n";
+
+    zufinden = ({SENSE_DEFAULT});
+
+    def = all_dets[SENSE_DEFAULT];
+
+// Terminaltyp des Magiers feststellen.
+
+    if (objectp(output))
+        switch (output->QueryProp(P_TTY))
+        {
+            case "ansi"  :
+            case "vt100" : PRE=BOLD; END=NORMAL; break;
+            default      : PRE="";   END="";     break;
+        }
+    else
+    {
+        PRE="";
+        END="";
+    }
+
+    senses = sort_array(m_indices(all_dets),#'<);
+
+// Alle vorhandenen Sense-Details anzeigen.
+
+    if (scanrun<1)
+    {
+        if ((i=sizeof(senses))>0)
+        {
+            for ( s1="",--i; i>=0 ; i-- )
+                s1 += ((senses[i]==SENSE_DEFAULT?"DEFAULT":
+                        senses[i])+(i?"\n":""));
+
+            re += sprintf(sprintf("%sListe der vorhandenen %s:%s\n\n"+
+                          "%s\n\n",PRE,WAS,END,("%-#78"+(spalten<1?"":
+                          sprintf(".%d",spalten))+"s")),s1);
+        }
+        else
+            re += sprintf("%sKeine %s gefunden.%s\n\n",PRE,WAS,END);
+    }
+
+
+//  Sense-Details auswerten - geht wie bei Details.
+
+    for ( s2=({}),i=sizeof(senses)-1 ; i>=0 ; i--)
+        s2 += eval_detail_entry(senses[i]);
+
+    for ( s3=({}),i=sizeof(s2)-1 ; i>=0 ; i-- )
+        if (stringp(s2[i]))
+            if (member(s3,s2[i])==-1)
+            {
+                s3 += ({ s2[i] });
+                zufinden+=det_auswerten(s2[i]);
+            }
+
+// Testen, welche Sense-Details fehlen und anzeigen
+
+    for ( s2=({}),s3=({}),i=sizeof(zufinden)-1;i>=0;i--)
+    {
+        if (!(mi=test_details(zufinden[i],def)))
+            continue;
+        if (pointerp(mi))
+        {
+            for (j=sizeof(mi)-1 ; j>=0 ; j--)
+                if (member(s2,mi[j])==-1)
+                    s2 += ({ mi[j] });
+        }
+        else if (stringp(mi) && member(s3,mi)==-1)
+            s3 += ({ mi });
+    }
+    s2 = sort_array(s2,#'<);
+    s3 = sort_array(s3,#'<);
+
+    if (scanrun<1)
+    {
+        if ((i=sizeof(s2))>0)
+        {
+            for ( s1="",--i ; i>=0 ; i-- )
+                s1 += ((s2[i]==SENSE_DEFAULT?"DEFAULT":s2[i])+(i?"\n":""));
+
+            re += sprintf(sprintf("%sListe der gefundenen %s:%s\n\n"+
+                          "%s\n\n",PRE,WAS,END,("%-#78"+(spalten<1?"":
+                          sprintf(".%d",spalten))+"s")),s1);
+        }
+        else
+            re += sprintf("%sEs gibt keine %s.%s\n\n",PRE,WAS,END);
+    }
+
+    if ((i=sizeof(s3))>0)
+    {
+        for ( s1="",--i ; i>=0 ; i-- )
+           s1 += ((s3[i]==SENSE_DEFAULT?"DEFAULT":s3[i])+(i?"\n":""));
+
+        re += sprintf(sprintf("%sListe der fehlenden %s:%s\n\n"+
+                      "%s\n\n",PRE,WAS,END,("%-#78"+(spalten<1?"":
+                      sprintf(".%d",spalten))+"s")),s1);
+    }
+    else
+        re += sprintf("%sEs fehlen keine %s.%s\n\n",PRE,WAS,END);
+
+    if (scanrun<1)
+        re += sprintf("%sFERTIG.%s\n",PRE,END);
+
+// Die eigentliche Textausgabe.
+
+    Output(re);
+
+    return 1;
+}
+
+/***************************************************************************
+**
+**  Die Funktion, die die Geraeusche im Raum untersucht.
+**
+****************************************************************************
+*/
+
+int search_n(object r)
+{
+    if (!r || !objectp(r))
+    {
+        write("OTEST-ERROR: Kein (existierendes) Zielobjekt gefunden!\n");
+        return 1;
+    }
+
+    return search_sense(r,SENSE_SOUND);
+}
+
+/***************************************************************************
+**
+**  Die Funktion, die die Gerueche im Raum untersucht.
+**
+****************************************************************************
+*/
+
+int search_m(object r)
+{
+    if (!r || !objectp(r))
+    {
+        write("OTEST-ERROR: Kein (existierendes) Zielobjekt gefunden!\n");
+        return 1;
+    }
+
+    return search_sense(r,SENSE_SMELL);
+}
+
+/***************************************************************************
+**
+**  Die Funktion, die das eingegebene Kommando zerlegt und interpretiert.
+**
+****************************************************************************
+*/
+
+int search(string arg)
+{   string *in,dum;
+    object targ;
+    int    wo;
+
+    if (!(PL->QueryProp(P_TESTPLAYER)) && !IS_LEARNER(PL))
+        return removen();
+
+    if (scanrun)
+    {
+        write("Derzeit wird ein Verzeichnis gescanned. Bitte warten.\n");
+        return 1;
+    }
+
+    notify_fail("OTEST-Syntax: otest [detail|smell|sound] "+
+                 "{<objekt>[in mir|im raum]|hier}\n");
+
+    if (!arg || !stringp(arg) || sizeof(in=old_explode(arg," "))<1)
+    {
+        output=PL;
+        scanrun=0;
+        master=PL;
+        return search_d(environment(PL));
+    }
+
+    if (sizeof(in)>1)
+    {
+        arg=implode(in[1..]," ");
+        if (member(({"hier","raum",""}),arg)!=-1)
+            targ=environment(PL);
+        else
+        {
+            if (sscanf(arg,"%s in mir",dum))
+            {
+                wo=1;
+                arg=dum;
+            }
+            else if (sscanf(arg,"%s im raum",dum))
+            {
+                wo=-1;
+                arg=dum;
+            }
+            else wo=0;
+            if (wo!=-1)
+                targ=present(arg,PL);
+            if (!objectp(targ) && wo!=1)
+                targ=present(arg,environment(PL));
+        }
+    }
+    else
+        targ=environment(PL);
+    if (!objectp(targ))
+    {
+        write("OTEST-ERROR: Gewuenschtes Ziel nicht gefunden.\n");
+        return 0;
+    }
+
+    output=PL;
+    scanrun=0;
+    master=PL;
+
+    switch(in[0])
+    {
+        case "de" :
+        case "detail" :
+        case "details" :
+            return search_d(targ);
+        case "sm" :
+        case "smell" :
+        case "smells" :
+            return search_m(targ);
+        case "so" :
+        case "sound" :
+        case "sounds" :
+            return search_n(targ);
+    }
+    return 0;
+}
+
+/***************************************************************************
+**
+**  Hier kann man den 'Scantyp' einstellen.
+**
+****************************************************************************
+*/
+
+int scanart(string arg)
+{   int h;
+
+    if (!(PL->QueryProp(P_TESTPLAYER)) && !IS_LEARNER(PL))
+        return removen();
+
+    if (scanrun)
+    {
+        write("Derzeit wird ein Verzeichnis gescanned. Bitte warten.\n");
+        return 1;
+    }
+
+    notify_fail("OTEST-Syntax: otype [rikus|rochus]\n");
+    if (!arg || !stringp(arg))
+    {
+        if (h=PL->QueryProp(P_OTEST_TYP))
+            scantyp=h;
+        if (!h)
+        {
+            PL->SetProp(P_OTEST_TYP,scantyp);
+            PL->Set(P_OTEST_TYP,SAVE,F_MODE_AS);
+        }
+        printf("OTEST: Eingestellter Scantyp ist '%s'\n",
+            (scantyp==T_ROCHUS?"ROCHUS":"RIKUS"));
+        return 1;
+    }
+    if (member(({"rikus","rochus"}),arg)==-1)
+        return 0;
+    if (arg=="rochus")
+    {
+        scantyp=T_ROCHUS;
+        PL->SetProp(P_OTEST_TYP,scantyp);
+        PL->Set(P_OTEST_TYP,SAVE,F_MODE_AS);
+        write("OTEST: Eingestellter Scantyp ist 'ROCHUS'\n");
+        return 1;
+    }
+    scantyp=T_RIKUS;
+    PL->SetProp(P_OTEST_TYP,scantyp);
+    PL->Set(P_OTEST_TYP,SAVE,F_MODE_AS);
+    write("OTEST: Eingestellter Scantyp ist 'RIKUS'\n");
+    return 1;
+}
+
+/***************************************************************************
+**
+**  Hier kann man die Spaltenzahl einstellen.
+**
+****************************************************************************
+*/
+
+int spaltenzahl(string arg)
+{   int h;
+
+    if (!(PL->QueryProp(P_TESTPLAYER)) && !IS_LEARNER(PL))
+        return removen();
+
+    if (scanrun)
+    {
+        write("Derzeit wird ein Verzeichnis gescanned. Bitte warten.\n");
+        return 1;
+    }
+
+    notify_fail("OTEST-Syntax: ocolm [1|2|3|4|5|6|7|8|auto]\n");
+
+    if (!arg || !stringp(arg))
+    {
+        if (h=PL->QueryProp(P_OTEST_SPL))
+            spalten=h;
+        if (!h)
+        {
+            PL->SetProp(P_OTEST_SPL,spalten);
+            PL->Set(P_OTEST_SPL,SAVE,F_MODE_AS);
+        }
+        printf("OTEST: Eingestellte Spaltenzahl ist '%s'\n",
+            (spalten>0?sprintf("%d",spalten):"'AUTO'"));
+        return 1;
+    }
+    if (arg=="auto")
+    {
+        spalten=-1;
+        PL->SetProp(P_OTEST_SPL,spalten);
+        PL->Set(P_OTEST_SPL,SAVE,F_MODE_AS);
+        write("OTEST: Eingestellte Spaltenzahl ist 'AUTO'\n");
+        return 1;
+    }
+    h = to_int(arg);
+    if (h<1 || h>8)
+        return 0;
+    spalten=h;
+    PL->SetProp(P_OTEST_SPL,spalten);
+    PL->Set(P_OTEST_SPL,SAVE,F_MODE_AS);
+    printf("OTEST: Eingestellte Spaltenzahl ist %d\n",spalten);
+    return 1;
+}
+
+/***************************************************************************
+**
+**  Hier kann man ganze Verzeichnisse durchscannen lassen
+**
+****************************************************************************
+*/
+
+int dirtesten(string arg)
+{   string vz,af,u,*dir;
+    int    i,si;
+
+    if (!(PL->QueryProp(P_TESTPLAYER)) && !IS_LEARNER(PL))
+        return removen();
+
+    if (scanrun)
+    {
+        write("Derzeit wird ein Verzeichnis gescanned. Bitte warten.\n");
+        return 1;
+    }
+
+    if (object_name(ME)[0..2]=="/p/")
+    {
+        write(break_string("Ein Dirscan kann nur durchgefuehrt werden, "+
+              "wenn man den Pruefer in sein /players/-Verzeichnis kopiert "+
+              "hat oder von einem dort liegenden Objekt inherited.",78,
+              "OTEST: "));
+        return 1;
+    }
+
+    notify_fail("OTEST-Syntax: otdir <verzeichnis> <ausgabefile>\n");
+    if (!arg || !stringp(arg) || sscanf(arg,"%s %s",vz,af)!=2)
+        return 0;
+
+// Zielfilename testen/festlegen.
+
+    u=getuid(PL);
+    if (file_size("/log/"+u+"/")!=-2)
+    {
+        printf("OTEST: Es existiert kein Verzeichnis '/log/%s/'\n",u);
+        return 1;
+    }
+    for (i=sizeof(af)-1;i>=0;i--)
+        if (((si=af[i])<48 && si!=46) || (si>57 && si<65) || si>122 ||
+            (si>90 && si<97))
+        {
+            write("OTEST: Illegaler Filename fuer Ausgabefile.\n");
+            return 1;
+        }
+    u=sprintf("/log/%s/%s",u,af);
+
+// Zu untersuchendes Verzeichnis pruefen/einlesen.
+
+    if (!vz || !stringp(vz) || sizeof(vz)<3 || vz[0]!='/' || vz[<1]!='/')
+    {
+        write("OTEST: Verzeichnisname muss mit '/' beginnen und aufhoeren.\n");
+        return 1;
+    }
+    for (i=sizeof(af)-1;i>=0;i--)
+        if (((si=af[i])<46) || (si>57 && si<65) || si>122 || (si>90 && si<97))
+        {
+            write("OTEST: Illegaler Filename fuer Verzeichnis.\n");
+            return 1;
+        }
+    dir = get_dir(vz+"*.c");
+    if ((si=sizeof(dir))<1)
+    {
+        write("OTEST: Das gewuenschte Verzeichnis existiert nicht oder ist "+
+              "leer.\n");
+        return 1;
+    }
+
+    output    = u;
+
+    if (stringp(catch
+          (i=write_file(output, sprintf("Teststart: %s\n\n",dtime(time())))))
+        && i!=1)
+    {
+        write(break_string(
+            sprintf("Fehler beim Schreiben in das File %s:\n%sAborting.",
+                output,u),78,"OTEST: ",1));
+        output = 0;
+        return 1;
+    }
+
+    scanrun   = si+1;
+    scanfiles = dir;
+    scandir   = vz;
+    scancount = 0;
+    master    = PL;
+    scanerr   = ({0,0});
+
+    call_out("x_heart_beat",T_PAUSE);
+
+    printf("OTEST: Scanne %d Files:\n"+
+           "       %s => %s\n"+
+           "Zeit:  etwa %s\n",si,vz,output,
+               time2string("%h:%02m:%02s",si*4*T_PAUSE));
+
+    return 1;
+}
+
+/***************************************************************************
+**
+**  Das Scannen von Verzeichnissen wird ueber x_heart_beat() erledigt.
+**
+****************************************************************************
+*/
+
+void x_heart_beat()
+{   int    nr;
+    string fn,er;
+
+    call_out("x_heart_beat",T_PAUSE);
+
+    if ((++scancount)>4)
+    {
+        scancount = 1;
+        testob    = 0;
+        scanrun--;
+    }
+
+    nr=scanrun-2;
+    if (nr<0)
+    {
+        if (environment() && interactive(environment()))
+            environment()->Message(break_string("'otdir' beendet.\n"+
+                sprintf("Files: %d - Fehler: %d - Skips: %d",
+                sizeof(scanfiles),scanerr[0],scanerr[1]),
+                78,"OTEST: ",1),MSGFLAG_TELL);
+
+        scanrun   = 0;
+        scancount = 0;
+        output    = 0;
+        scanfiles = ({});
+        scandir   = 0;
+        testob    = 0;
+        scanerr   = ({0,0});
+
+        while(remove_call_out("x_heart_beat")!=-1);
+
+        return;
+    }
+    fn=scandir+scanfiles[nr];
+    if (fn[<2..]==".c")
+        fn=fn[0..<3];
+
+    if (scancount==1) // Testen, ob File ladbar
+    {
+        Output(sprintf("File: %s\n\n",fn));
+        er=catch(call_other(fn,"???"));
+        if (er && stringp(er))
+        {
+            Output(sprintf("Error: %s->Abort@Load\n\n",er));
+            scanrun--;
+            scancount  = 0;
+            testob     = 0;
+            scanerr[0] = scanerr[0]+1;
+        }
+        else
+            testob = find_object(fn);
+        return;
+    }
+    if (!objectp(testob))
+    {
+        Output("Objekt nicht gefunden\n-> Abort@Object.\n\n");
+        scanrun--;
+        scancount  = 0;
+        scanerr[0] = scanerr[0]+1;
+        return;
+    }
+    if (!function_exists("int_long",testob))
+    {
+        Output("Objekt ist kein Raum -> SKIPPING.\n\n");
+        scanrun--;
+        scancount  = 0;
+        testob     = 0;
+        scanerr[1] = scanerr[1]+1;
+        return;
+    }
+    switch(scancount)
+    {
+        case 2 :
+            if ((er=catch(search_d(testob))) && stringp(er))
+            {
+                Output(sprintf("Error: %s-> Abort@Detail.\n\n",er));
+                scanrun--;
+                scancount  = 0;
+                testob     = 0;
+                scanerr[0] = scanerr[0]+1;
+            }
+            return;
+        case 3 :
+            if ((er=catch(search_sense(testob,SENSE_SMELL))) && stringp(er))
+            {
+                Output(sprintf("Error: %s-> Abort@Smell.\n\n",er));
+                scanrun--;
+                scancount  = 0;
+                testob     = 0;
+                scanerr[0] = scanerr[0]+1;
+            }
+            return;
+        case 4 :
+            if ((er=catch(search_sense(testob,SENSE_SOUND))) && stringp(er))
+            {
+                Output(sprintf("Error: %s-> Abort@Sound.\n\n",er));
+                scanrun--;
+                scancount  = 0;
+                testob     = 0;
+                scanerr[0] = scanerr[0]+1;
+            }
+            return;
+    }
+    return;
+}
diff --git a/obj/tools/tester/test.dat b/obj/tools/tester/test.dat
new file mode 100644
index 0000000..e720c76
--- /dev/null
+++ b/obj/tools/tester/test.dat
@@ -0,0 +1,5 @@
+der die das ich du er sie es wir ihr sie mein dein sein deiner euer an aus
+ein auch dafuer doch im naja wie deinen meinen deinem meinem sieht dich dies
+sueden norden westen osten suedosten suedwesten nordosten nordwesten diese
+dir dort hm ja nein als auf in man nach um dieser nicht deine von wenn am
+keine mud hier so
\ No newline at end of file
diff --git a/obj/tools/xtool.c b/obj/tools/xtool.c
new file mode 100644
index 0000000..200c48a
--- /dev/null
+++ b/obj/tools/xtool.c
@@ -0,0 +1,7 @@
+void create()
+{
+  if(member(object_name(this_object()),'#')==-1)
+    return;
+  clone_object("/obj/tools/MGtool")->move(this_object());
+  call_out(lambda(({}),({#'destruct,({#'this_object})})),0);
+}
diff --git a/obj/tools/xxtool.c b/obj/tools/xxtool.c
new file mode 100644
index 0000000..fa3fb6c
--- /dev/null
+++ b/obj/tools/xxtool.c
@@ -0,0 +1,41 @@
+#include <properties.h>

+#include <language.h>

+

+inherit "obj/tools/MGtool";

+inherit "std/thing";

+

+void create()

+{

+  if(!clonep(this_object()))

+    return;

+  thing::create();

+  ::create();

+  SetProp(P_NAME,"Ankh");

+  SetProp(P_GENDER,NEUTER);

+  SetProp(P_IDS,({"ankh","xxtool"}));

+}

+

+string _query_short()

+{

+  ::_query_short();

+  return "Ein Ankh am Halsband.\n";

+}

+

+string _query_long()

+{

+  ::_query_long();

+  return

+    "Ein Halsband, an dem ein Ankh haengt.\n"

+    +	"Ein Ankh sieht ungefaehr so aus wie ein Kreuz aber eben nicht ganz.\n";

+}

+

+string name( int d1, int d2 )

+{

+  return thing::name(d1,d2);

+}

+

+int id( string str )

+{

+  ::id(str);

+  return member( ({"ankh","xxtool"}),str ) != -1;

+}

diff --git a/obj/topliste.c b/obj/topliste.c
new file mode 100644
index 0000000..230c546
--- /dev/null
+++ b/obj/topliste.c
@@ -0,0 +1,116 @@
+inherit "/std/thing";
+
+#include <properties.h>
+#include <language.h>
+
+// ({Funktionsname, ({argumente}), tttel })
+nosave <string|mixed*>* watdenn;
+
+string _liesliste();
+
+protected void create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT,"Die Top-Liste, Top-Spielerliste, Top-Seherliste und "
+      "Top-Hardcoreliste");
+  SetProp(P_WEIGHT,0);
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_WEIGHT,20);
+  AddId(({"liste","topliste","top-liste",
+          "topspielerliste","top-spielerliste","topspieler","spielerliste",
+          "topseherliste","top-seherliste","topseher","seherliste",
+          "tophardcoreliste","top-hardcoreliste","tophardcore","hardcoreliste"}));
+  SetProp(P_NAME,"Liste");
+  SetProp(P_NOGET,"Die ist an der Wand festgenagelt.\n");
+  AddReadDetail(SENSE_DEFAULT, #'_liesliste);
+}
+
+#define NAME     0
+#define LEP      1
+#define QP       2
+#define XP       3
+#define LEVEL    4
+#define AGE      5
+#define RACE     6
+#define GUILD    7
+#define WIZLEVEL 8
+#define HARDCORE 9
+public string format_list(< <string|int>* >* list)
+{
+  int idx;
+  //string res="      Name         Level  AP  Erfahrung    Alter     Gilde\n";
+  string res = sprintf(
+      "%c%:4s  %:11-s  %:5|s %:5|s %:4|s %:12|s %:12|s  %:14-s\n",
+      ' ', "", "Name", "", "Level", "AP", "Erfahrung","Alter","Gilde");
+
+  foreach(<string|int>* entry: list)
+  {
+    ++idx;
+    entry[AGE] *= 2;
+    int tage = entry[AGE]/86400;
+    int stunden = (entry[AGE] % 86400) / 3600;
+    int minuten = (entry[AGE] % 3600) / 60;
+    res +=
+    sprintf("%c%:4d. %:11-s  %:5-s %:5d %:4d %:12d %:4dd, %:2'0'd:%:2'0'd  %:14-s\n",
+            (entry[WIZLEVEL]>0 ? '*' : ' '),
+            idx, capitalize(entry[NAME]),
+            (entry[HARDCORE]>1 ? "(RIP)" : ""),
+            entry[LEVEL],
+            entry[QP], entry[XP],
+            tage, stunden, minuten,
+            capitalize(entry[GUILD]));
+  }
+  return res;
+}
+
+private string get_list(<string|mixed*>* was)
+{
+  if (was == 0)
+    was=({"Liste", ({}), " "*28 + "Liste der Topspieler !\n\n" });
+
+  < <string|int>* >* list = "/secure/topliste"->(was[0])(was[1]...);
+  
+  return was[2] + format_list(list);
+}
+
+varargs int id(string str,int lvl)
+{
+  if (!::id(str,lvl))
+    return 0;
+
+  if (strstr(str,"seher")>=0)
+    watdenn=({"SeherListe", ({ }), " "*29 + "Liste der Topseher !\n\n" });
+  else if (strstr(str,"spieler")>=0)
+    watdenn=({"SpielerListe", ({ }),
+               " "*21 + "Liste der Topspieler (Keine Seher) !\n\n" });
+  else if (strstr(str,"hardcore")>=0)
+    watdenn=({"HardcoreListe", ({ }),
+               " "*22 + "Liste der mutigsten aller Helden !\n\n" });
+  else
+    watdenn=({"Liste", ({ }), " "*28 + "Liste der Topspieler !\n\n" });
+  
+  return 1;
+}
+
+string _query_long()
+{
+  if (this_player()) this_player()->More(get_list(watdenn));
+  watdenn=0;
+  return "";
+}
+
+varargs string long(int mode)
+{
+  if (this_player()) this_player()->More(get_list(watdenn));
+  watdenn=0;
+  return "";
+}
+
+string _liesliste()
+{
+  if (this_player()) this_player()->More(get_list(watdenn));
+  watdenn=0;
+  return "";
+}
+
diff --git a/obj/uhr.c b/obj/uhr.c
new file mode 100644
index 0000000..194b6ea
--- /dev/null
+++ b/obj/uhr.c
@@ -0,0 +1,86 @@
+#pragma no_clone
+#pragma no_inherit
+#pragma no_shadow
+#pragma strong_types
+
+#include <properties.h>
+#include <events.h>
+#include <time.h>
+
+private string ulog = "";
+
+void WaitForQuarterHour() {
+  while(remove_call_out("QuarterReached")!=-1);
+  call_out("QuarterReached",900-(time() % 900));
+}
+
+void create() {
+  WaitForQuarterHour();
+}
+
+/* Ueberprueft die als Parameter "str" uebergebene Uhrmeldung.
+   Rueckgabewerte: 0 fuer fehlerhafte Parameter,
+                   1 fuer korrekten Aufbau. */
+int CheckClockMsg(string str)
+{
+  int i = strstr(str, "%", 0);
+  /* Mehr als 1x %d enthalten, mehr als 1x % enthalten, oder auf das einzige
+     enthaltene % folgt kein "d". */
+  if ( sizeof(explode(str,"%d")) > 2 ||
+       sizeof(explode(str,"%")) > 2 ||
+       (i>-1 && ( i==sizeof(str)-1 || str[i+1] != 'd')))
+    return 0;
+  else
+    return 1;
+}
+
+void QuarterReached() {
+  int std, minuten;
+  string str, format;
+  int off;
+
+  int *lt=localtime(time());
+  minuten=lt[TM_MIN];
+  std=lt[TM_HOUR];
+  mapping edata = mkmapping(
+        ({TM_SEC, TM_MIN, TM_HOUR, TM_MDAY, TM_MON, TM_YEAR, TM_WDAY, TM_YDAY,
+            TM_ISDST}),
+        lt);
+
+  // Clock-Event triggern
+  EVENTD->TriggerEvent(EVT_LIB_CLOCK, edata); 
+
+  // bei Datumswechsel DATECHANGE-Event ausloesen.
+  if (std==0 && minuten < 15) {      
+      EVENTD->TriggerEvent(EVT_LIB_DATECHANGE, edata);
+  }
+
+  if (minuten <= 2) {
+    foreach(object u : users()) {
+      if((off=to_int(u->QueryProp(P_TIMEZONE)))<0)off+=24;
+      off=(std+off)%24;
+      format=u->QueryProp(P_CLOCKMSG)||"Du hoerst die Uhr schlagen: "
+	                               "Es ist jetzt %d Uhr.\n";
+      if ( !CheckClockMsg(format) )
+      {
+	tell_object(u, break_string(
+	  "So kann ich nicht arbeiten! Deine Uhrmeldung funktioniert so "
+	  "nicht. Bitte korrigiere das.", 78, "Die Uhr teilt Dir mit: "));
+	continue;
+      }
+      if (strstr(format,"%d",0)==-1)
+        str=format;
+      else
+        str=sprintf(format,off);
+      if (str!="") tell_object(u,str);
+    }
+  }
+  str = "";
+  if (minuten < 10) {
+    log_file("USER_STATISTIK", ulog);
+    ulog = sprintf("\n%s%02d: ",(!std?("# "+lt[TM_WDAY]+":\n"):""),std);
+  }
+  ulog += sprintf("%4d",sizeof(users()));
+  WaitForQuarterHour();
+}
+
diff --git a/obj/vertrag.c b/obj/vertrag.c
new file mode 100644
index 0000000..570990e
--- /dev/null
+++ b/obj/vertrag.c
@@ -0,0 +1,115 @@
+#pragma strict_types
+#pragma combine_strings
+#pragma no_shadow
+#pragma no_inherit
+
+inherit "std/thing";
+
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+
+private int gelesen;
+private string unterschrieben;
+
+static string text();
+
+public void create()
+{
+   if ( !geteuid(this_object()) )
+      call_out( "remove", 0 );
+   
+   if ( !IS_WIZARD(geteuid(this_object())) )
+      call_out( "remove", 0 );
+   
+   if ( !clonep(this_object()) )
+      return;
+   
+   thing::create();
+   SetProp( P_SHORT, "Ein Vertrag" );
+   SetProp( P_LONG, "@@unterschrieben@@" );
+   SetProp( P_NAME, "Vertrag" );
+   Set(P_READ_MSG, #'text, F_QUERY_METHOD); 
+   SetProp( P_GENDER, MALE );
+   SetProp( P_ARTICLE, 1 );
+   AddId("vertrag");
+   SetProp(P_NEVERDROP, 1);
+   AddCmd(({"unterschreibe","unterschreib","unterzeichne"}),"unterschreibe");
+   AddCmd(({"zerreisse", "zerreiss"}),"zerreisse");
+   
+   gelesen = 0;
+   unterschrieben = "";
+}
+
+static string _query_nodrop()
+{
+   if (this_player() && IS_WIZARD(this_player())) return 0;
+   return "Nein Nein! Dies ist dein Vertrag!\n";
+}
+
+static string unterschrieben()
+{
+   string s;
+   
+   s = "Dies ist Dein Magier-Vertrag. Du solltest ihn vielleicht ";
+   if ( gelesen )
+      s += "noch";
+   s += "mal lesen.\nEr wurde Dir von " + capitalize(geteuid(this_object()))
+      +" ueberreicht.\nWenn Du ihn unterschreibst, wirst Du von "
+      + capitalize(geteuid(this_object())) + " gesponsort.\n";
+   
+   if (this_player() && (unterschrieben == getuid(this_player())) )
+      s += "Du hast ihn unterschrieben.\n";
+   
+   return s;
+}
+
+
+static string text()
+{
+   gelesen = 1;
+   this_player()->More("/etc/WIZRULES",1);
+   return "";
+}
+
+
+static int unterschreibe( string str )
+{
+   notify_fail("Was willst Du denn unterschreiben?\n");
+   if ( !id(str) )
+      return 0;
+   
+   if ( !this_player() || this_player() != this_interactive()
+        || this_player() != environment() )
+      return 0;
+   
+   if ( !gelesen ){
+      write( "Du hast ihn noch nicht gelesen. ERST lesen, DANN schreiben!\n");
+      return 1;
+   }
+   
+   write( "Du unterschreibst den Vertrag. Wenn das ein Versehen war, "
+          + "zerreiss ihn sofort.\n" );
+   unterschrieben = getuid( this_interactive() );
+   SetProp( P_NODROP, "Nein, der ist unterschrieben. Du kannst ihn hoechstens"
+            + " zerreissen.\n" );
+   SetProp( P_NEVERDROP, 1 );
+   return 1;
+}
+
+
+static int zerreisse( string str )
+{
+   notify_fail("Was willst Du denn zerreissen?\n");
+   if ( !id(str) ) return 0;
+   write( "Du zerreisst den Vertrag.\n" );
+   return remove();
+}
+
+
+public int is_unterschrieben()
+{
+   return environment() && query_once_interactive(environment())
+      && getuid(environment()) == unterschrieben;
+}
+
diff --git a/obj/virtual/v_room.c b/obj/virtual/v_room.c
new file mode 100644
index 0000000..59c28fd
--- /dev/null
+++ b/obj/virtual/v_room.c
@@ -0,0 +1,12 @@
+inherit "/std/room";
+
+#include <properties.h>
+#include <defines.h>
+
+create()
+{
+        if(IS_BLUE(this_object())) return;
+        ::create();
+        previous_object()->CustomizeObject();
+        call_out(symbol_function("QueryObject", find_object(OBJECTD)), 2);
+}
diff --git a/obj/virtual/vr_compiler.c b/obj/virtual/vr_compiler.c
new file mode 100644
index 0000000..40e4bd0
--- /dev/null
+++ b/obj/virtual/vr_compiler.c
@@ -0,0 +1,81 @@
+// MorgenGrauen MUDlib
+//
+// VR_COMPILER.C -- virtual room compiler
+//
+// $Date: 2002/08/28 09:57:18 $
+// $Revision: 6605 $
+/* $Log: vr_compiler.c,v $
+ * Revision 1.3  2002/08/28 09:57:18  Rikus
+ * auf strong_types umgestellt
+ *
+ * Revision 1.2  1994/07/27 14:48:18  Jof
+ * suid
+ *
+ * Revision 1.1  1994/02/01  19:47:57  Hate
+ * Initial revision
+ *
+*/
+
+// principle:
+// 	- inherit this object into your own 'virtual_compiler.c'
+//  - customize Validate() and CustomizeObject() for you own sake
+//  
+//  * Validate() checks if a room filename given as argument (without path)
+//    is valid and returns this filename with stripped '.c'!!
+//  * CustomizeObject() uses the previous_object()->Function() strategy to
+//    customize the standard object (for example to set a description)
+//
+// Properties: P_VALID_NAME, P_MIN_X, P_MAX_X, P_MIN_Y, P_MAX_Y
+
+#pragma strong_types
+
+inherit "/std/virtual/v_compiler";
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <defines.h>
+#include <v_compiler.h>
+#include "/obj/virtual/vr_compiler.h"
+#include <sys_debug.h>
+
+void create()
+{
+  ::create();
+	SetProp(P_VALID_NAME, "raum");
+	seteuid(getuid());
+}
+
+string Validate(string file)
+{
+	int x,y;
+	string path, name;
+
+  file = ::Validate(file);
+	if((sscanf(file, "%s[%d,%d]", name, x, y) == 3) &&
+			name == QueryProp(P_VALID_NAME) &&
+		  (x >= QueryProp(P_MIN_X) && x <= QueryProp(P_MAX_X)) &&
+		  (y >= QueryProp(P_MIN_Y) && y <= QueryProp(P_MAX_Y))
+		)
+		return file;
+}
+
+mixed CustomizeObject()
+{
+	string path, file, name;
+	int x,y;
+
+	if(!(file = ::CustomizeObject())) return 0;
+
+	path = QueryProp(P_COMPILER_PATH);
+	sscanf(file, "%s[%d,%d]", name, x, y);
+	name = QueryProp(P_VALID_NAME);
+	if(y < QueryProp(P_MAX_Y))
+		previous_object()->AddExit("norden", path+"/"+name+"["+(x  )+","+(y+1)+"]");
+	if(y > QueryProp(P_MIN_Y))
+		previous_object()->AddExit("sueden", path+"/"+name+"["+(x  )+","+(y-1)+"]");
+	if(x < QueryProp(P_MAX_X))
+		previous_object()->AddExit("osten" , path+"/"+name+"["+(x+1)+","+(y  )+"]");
+	if(x > QueryProp(P_MIN_X))
+		previous_object()->AddExit("westen", path+"/"+name+"["+(x-1)+","+(y  )+"]");
+}
diff --git a/obj/virtual/vr_compiler.h b/obj/virtual/vr_compiler.h
new file mode 100644
index 0000000..4b7a940
--- /dev/null
+++ b/obj/virtual/vr_compiler.h
@@ -0,0 +1,33 @@
+// MorgenGrauen MUDlib
+//
+// V_COMPILER.H -- a general virtual compiler object
+//
+// $Date: 1994/02/01 19:47:57 $
+// $Revision: 6081 $
+/* $Log: vr_compiler.h,v $
+ * Revision 1.1  1994/02/01  19:47:57  Hate
+ * Initial revision
+ *
+*/
+
+#include <v_compiler.h>
+
+#ifndef __VR_COMPILER_H__
+#define __VR_COMPILER_H__
+
+#define P_VALID_NAME	"valid_name"
+#define P_MIN_X				"min_x"
+#define P_MAX_X				"max_x"
+#define P_MIN_Y				"min_y"
+#define P_MAX_Y				"max_y"
+
+#endif // __V_COMPILER_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __VR_COMPILER_H_PROTO__
+#define __VR_COMPILER_H_PROTO__
+
+#endif // __VR_COMPILER_H_PROTO__
+
+#endif //NEED_PROTOTYPES
diff --git a/obj/wanderer.c b/obj/wanderer.c
new file mode 100644
index 0000000..9120092
--- /dev/null
+++ b/obj/wanderer.c
@@ -0,0 +1,113 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_inherit, no_shadow
+
+inherit "std/npc";
+
+#define DELAY 60
+#define MAXWAY 30
+
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+#include "/secure/questmaster.h"
+
+protected void create()
+{
+  ::create();
+  SetProp(P_NAME,"Wanderer");
+  set_living_name("orakel");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_SHORT,"Ein alter Wanderer");
+  SetProp(P_LONG,
+  "Ein uralter Wanderer mit einem langen Hirtenmantel und einem Hirtenstab.\n");
+  AddId(({"wanderer","hirte","orakel"}));
+	create_default_npc( 19 );
+  SetProp(P_MAX_HP, 5000);
+  SetProp(P_HP, 5000);
+  SetProp(P_GENDER,MALE);
+  SetProp(P_ALIGN, 1000);
+  SetProp(P_XP, 1000);
+  SetProp(P_HANDS, ({" mit seinem Hirtenstab", 50, ({DT_BLUDGEON})}) );
+  SetProp(P_NO_ATTACK, 1);
+  SetProp(P_REJECT, ({ REJECT_GIVE, Name(WER) + " sagt: Danke, aber das "
+			  "brauche ich nicht.\n" }) );
+  call_out("lauf", DELAY);
+  // Sollte eigentlich ueber das `info'-System laufen ;(
+  AddCmd( ({"frag","frage"}), "frag" );
+}
+
+static int frag(mixed str)
+{
+  int i,j,geredet;
+  mixed *quests;
+  mixed tmp;
+  
+  if (!str) return 0;
+  str = this_player()->_unparsed_args(1);
+  str = old_explode(str," ");
+  if ( !sizeof(str) ) return 0;
+  if ( !id(str[0]) ) return 0;
+  if (sizeof(str)<3 || (str[1]!="nach" && str[1]!="ueber"))
+  {
+    tell_object(this_player(),"Der Wanderer sagt: Ich versteh dich nicht.\n");
+    return 1;
+  }
+  geredet=0;
+  str=implode(str[2..]," ");
+  quests=QM->QueryQuests();
+  if (str=="aufgabe" || str=="aufgaben" || str=="abenteuer" || str=="quests"
+      || str == "quest")
+  {
+    tell_object(this_player(),"Der Wanderer sagt: Folgende Aufgaben gibt "
+        "es hier:\n");
+    quests[0] = sort_array(quests[0], #'>);
+    for (i=0;i<sizeof(quests[0]);i++)
+    {
+      tell_object(this_player(),quests[0][i]);
+      if (this_interactive()->QueryQuest(quests[0][i]))
+        tell_object(this_player()," [hast Du schon geloest]");
+      tell_object(this_player(),"\n");
+    }
+    return 1;
+  }
+  for (i=0;i<sizeof(quests[0]);i++)
+  {
+    if (member(old_explode(lower_case(quests[0][i])," "),str)>=0
+	      || strstr(lower_case(quests[0][i]), str)>=0)
+    {
+      tmp=quests[0][i]+"? Dazu kann ich Dir folgendes sagen: ";
+      tmp+=quests[1][i][3];
+      tmp=old_explode(break_string(tmp,60),"\n");
+      geredet=1;
+      for (j=0;j<sizeof(tmp);j++)
+        tell_object(this_player(),"Der Wanderer sagt: "+tmp[j]+"\n");
+    }
+  }
+  if (!geredet) 
+    tell_object(this_player(),"Der Wanderer sagt: "+capitalize(str)+
+      "? Interessiert mich nicht!\n");
+  return 1;
+}
+
+void lauf()
+{
+  int i, ok;
+ 
+  // wenn keine Environment, dann laeuft das Ding einfach nicht.
+  if ( !objectp(environment()) )
+    return;
+
+  mapping liste = environment()->QueryProp(P_EXITS);
+  string *dirs = m_indices(liste);
+  string *dirs2 = ({ });
+  for (i=0;i<sizeof(liste);i++)
+    if (!catch(ok=liste[dirs[i]]->QueryProp(P_ORAKEL)) && ok) 
+      dirs2 += ({ dirs[i] });
+  
+  if (sizeof(dirs2))
+  {
+    i=random(sizeof(dirs2));
+    command_me(dirs2[i]);
+  }
+  call_out("lauf",DELAY);
+}
diff --git a/obj/werliste.c b/obj/werliste.c
new file mode 100644
index 0000000..f25043d
--- /dev/null
+++ b/obj/werliste.c
@@ -0,0 +1,351 @@
+#pragma strong_types
+
+inherit "/std/user_filter";
+
+#include <properties.h>
+#include <wizlevels.h>
+#include <ansi.h>
+#include <werliste.h>
+
+mixed *erwarte;
+
+#define MIN(a,b)    (((a) > (b)) ? (b) : (a))
+#define ABS(a)      (((a) < 0) ? -(a) : (a))
+
+
+// als Funktion statt als define, da als lfun-Closure genutzt
+private int is_alph_greater( object a, object b )
+{
+    return geteuid(a) > geteuid(b);
+}
+
+
+// keine Ahnung, wer sich _die_ Berechnungsgrundlage ausgedacht hat.
+// Aber da sie schon uralt ist, bleibt sie auch drin ;-)
+private int is_greater( object a, object b )
+{
+    int a1, b1;
+    
+    a1 = 26000 * query_wiz_level(a) +
+        26 * MIN( 2121, ABS(a->QueryProp(P_LEVEL)) ) + getuid(a)[0]-'a';
+    
+    b1 = 26000 * query_wiz_level(b) +
+        26 * MIN( 2121, ABS(b->QueryProp(P_LEVEL)) ) + getuid(b)[0]-'a';
+    
+    return a1 > b1;
+}
+
+
+private string stat( object ob, int ist_magier )
+{
+    int l;
+  
+    l = query_wiz_level(ob);
+    
+    if ( ob->Query(P_TESTPLAYER) ){
+        if ( l < SEER_LVL )
+            return "t";
+        else if ( l < LEARNER_LVL )
+            return "T";
+    }
+    
+    if ( ob->Query(P_SECOND)) {
+      if (ob->Query(P_SECOND_MARK) > -1 ){
+        if ( l < SEER_LVL )
+            return "z";
+        else if ( l < LEARNER_LVL )
+            return "Z";
+      } else
+      if (ist_magier) {
+        if ( l < SEER_LVL )
+            return "n";
+        else if ( l < LEARNER_LVL )
+            return "N";
+      }
+    }
+
+    switch(l){
+    case GOD_LVL .. 666:
+        return "G";
+    case ARCH_LVL .. GOD_LVL-1:
+        return "E";
+    case ELDER_LVL .. ARCH_LVL-1:
+        return "W";
+    case LORD_LVL .. ELDER_LVL-1:
+        return "R";
+    case SPECIAL_LVL .. LORD_LVL-1:
+        return "H";
+    case DOMAINMEMBER_LVL .. SPECIAL_LVL-1:
+        return "M";
+    case WIZARD_LVL+1 .. DOMAINMEMBER_LVL-1:
+        return "m";
+    case LEARNER_LVL .. WIZARD_LVL:
+        return "L";
+    case SEER_LVL .. LEARNER_LVL-1:
+        return "S";
+    default:
+        return "s";
+    }
+    return "s";
+}
+
+
+private string idlestat( object ob )
+{
+    switch( query_idle(ob) ){
+    case 1800 .. 7199:
+        return "j";
+    case 600 .. 1799:
+        return "I";
+    case 120 .. 599:
+        return "i";
+    case 0 .. 119:
+        return ".";
+    default:
+        return "J";
+    }
+    return ".";
+}
+
+
+private string wegstat( object ob )
+{
+    if ( ob->QueryProp(P_AWAY) )
+        return "w";
+    
+    return ".";
+}
+
+
+private string hcstat( object ob )
+{
+    int hcstat;
+    
+    hcstat=ob->query_hc_play();
+    
+    if(!hcstat)
+    {
+      return ".";
+    }
+    
+    if(hcstat>1)
+        return "+";
+    
+    return "c";
+
+}
+
+
+private string get_location( object ob )
+{
+    string res;
+
+    if ( stringp(res = ob->Query(P_LOCATION)) )
+        res = capitalize(res) + " (ueber " + country(ob) + ")";
+    else
+        res = country(ob);
+
+    return res;
+}
+
+
+varargs private mixed
+QueryView( object ob, int ist_magier, int sh,
+           int obs, string tty, string opt )
+{
+    string race, ret, tmp, prefix, a_prefix, normal;
+
+    if ( !ist_magier && ob->QueryProp(P_INVIS) )
+        return "";
+    
+    prefix = "";
+    a_prefix = "";
+        
+    ret = capitalize(getuid(ob));
+    
+    switch(tty){
+    case "ansi":
+        if ( IS_LEARNER(ob) )
+            a_prefix = ANSI_UNDERL;
+        
+        if ( ob->QueryProp(P_FROG) )
+            a_prefix += ANSI_GREEN;
+
+        if ( pointerp(erwarte) && member( erwarte, ret ) != -1 )
+            a_prefix += ANSI_RED;
+        
+        normal = ANSI_NORMAL;
+        break;
+        
+    default:
+        if ( pointerp(erwarte) && member( erwarte, ret ) != -1 )
+            prefix = "*";
+        else
+            prefix = "";
+
+        normal = "";
+    }
+
+    if ( ret == ROOTID )
+        ret = "Logon";
+
+    if ( sh ){ // kwer
+        if ( ob->QueryProp(P_INVIS) )
+            ret = sprintf( "%s(%-11'.'s).%s%s", a_prefix, prefix + ret,
+                         hcstat(ob) + idlestat(ob) + wegstat(ob) + stat(ob,ist_magier), normal );
+        else
+            ret = sprintf( "%s%-13'.'s.%s%s", a_prefix, prefix + ret,
+                         hcstat(ob) + idlestat(ob) + wegstat(ob) + stat(ob,ist_magier), normal );
+    } 
+    else if ( opt == "k" ){ // wer -k
+        ret = sprintf( "[%s] %-13s %15s / %-20s %22s", stat(ob,ist_magier),
+                       ob->QueryProp(P_INVIS) ? "(" + ret + ")" : ret,
+                       stringp(race = ob->QueryProp(P_RACE)) ?
+                       capitalize(race)[0..14] : "<keine Rasse>",
+                       stringp(race = ob->QueryProp(P_GUILD)) ?
+                       capitalize(race)[0..19] : "<keine Gilde>",
+                       "[" + country(ob)[0..19] + "]" );
+    } 
+    else { // wer, wer -o, wer -s
+        if ( tmp = ob->short() ){
+            ret = tmp[0..<3];
+
+            if ( ob->QueryProp(P_TEAM) )
+                ret = ob->TeamPrefix() + ret;
+        }
+        else
+            ret = "(" + ret + ")";
+
+        if ( !IS_LEARNER(ob) && ob->Query(P_TESTPLAYER) )
+            tmp = " <TestSpieler>";
+        else
+            tmp = "";
+
+        if ( !IS_LEARNER(ob) && ob->QueryProp(P_SECOND) &&
+             ob->QueryProp(P_SECOND_MARK) > -1 )
+            tmp += " <Zweitie>";
+        
+        ret = sprintf( "%s%s - %s%s%s",
+                       (opt == "o") ? "" : "[" + stat(ob,ist_magier) + "] ", ret,
+                       stringp(race = ob->QueryProp(P_RACE)) ?
+                       capitalize(race) : "<keine Rasse>",
+                       (opt == "s") ? " - [" + get_location(ob) + "]" :
+                       (stringp(race = ob->QueryProp(P_GUILD)) ?
+                        "/" + capitalize(race) : "/<keine Gilde>"),
+                       (opt == "o") ? tmp : "" );
+    }
+            
+    if ( obs )
+        return ({ ob, ret });
+    else
+        return ret;
+}
+
+
+private object *make_unique( object* arr )
+{
+    int i;
+
+    // muss ueber eine Schleife gemacht werden, um die Reihenfolge zu erhalten!
+    for ( i = sizeof(arr); i-- > 1; )
+        if( arr[i] == arr[i-1] )
+            arr[i] = 0;
+
+    return arr - ({ 0 });
+}
+
+
+#define OFFSET(sp)  ((maxi/4) * sp + ((sp <= maxi%4) ? sp : maxi%4))
+
+// diese Funktion wird von aussen aufgerufen fuer die Befehle "wer" & Co
+public varargs string
+*QueryWhoListe( int ist_magier, int short, int obs, string arg, int old )
+{
+    object *pl;
+    string *pls, *s, opt;
+    int i, j, k, l, *t, maxi;
+
+    erwarte = 0;
+    
+    if ( short & WHO_SHORT ){
+        if ( previous_object() )
+            erwarte = previous_object()->QueryProp(P_WAITFOR);
+        
+        if ( !pointerp(erwarte) )
+            erwarte = ({});
+    }
+
+    // Wer Spielersicht moechte, soll Spielersicht bekommen
+    if ( short & WHO_PLAYER_VIEW )
+    {	    
+	    ist_magier = 0;
+    }
+
+    
+    if ( old ){
+        opt = "o";
+        arg = 0;
+    }
+    else if ( arg == "k" || arg == "s" ){
+        opt = arg;
+        arg = 0;
+    }
+    else if ( sizeof(arg) == 1 ) // (noch) unbekannte Option
+        arg = 0;
+  
+    pl = filter_users(arg);
+    
+    if ( short & WHO_ALPHA )
+        pl = sort_array( pl, #'is_alph_greater/*'*/ );
+    else
+        pl = sort_array( pl, #'is_greater/*'*/ );
+    
+    pl = make_unique( pl );
+    
+    pls = map( pl, #'QueryView/*'*/, ist_magier, short, obs,
+                     previous_object()->QueryProp(P_TTY), opt ) - ({""});
+    
+    if ( !(short & WHO_SHORT) )
+        return pls;
+    
+    maxi = sizeof(pls);
+    s = allocate( maxi / 4 + (maxi % 4 ? 1 : 0) );
+    
+    if ( short & WHO_VERTICAL ) { // senkrecht sortieren !
+        // wird so nur einmal berechnet
+        t = ({ 0, OFFSET(1), OFFSET(2), OFFSET(3), maxi });
+        
+        for ( i = 0, l = sizeof(s); i < l; i++ ) {
+            s[i] = "";
+            
+            for ( j = 0; j < 4; j++ ) {
+                if ( (k = i+t[j]) < t[j+1] && pls[k] != "" )
+                    s[i] += pls[k];
+                
+                if( j < 3 )
+                    s[i] += " ";
+            }
+        }
+    }
+    else {
+        for ( i = 0; i < maxi; i++ ) {
+            if ( !s[i/4] )
+                s[i/4] = "";
+            
+            if ( pls[i] != "" )
+                s[i/4] += pls[i];
+            
+            if ( (i%4 != 3) && i < maxi-1 )
+                s[i/4] += " ";
+        }
+    }
+
+    return ({ sprintf( "%|78s", "Liste der Mitspieler vom " + dtime(time()) +
+                     sprintf( " (%d Teilnehmer)", maxi )), "" }) + s;
+}
+
+
+public varargs int query_prevent_shadow( object ob )
+{
+    return 1;
+}
diff --git a/obj/zeitungsautomat.c b/obj/zeitungsautomat.c
new file mode 100644
index 0000000..2e71d1d
--- /dev/null
+++ b/obj/zeitungsautomat.c
@@ -0,0 +1,69 @@
+// MorgenGrauen MUDlib
+// 
+// zeitungsautomat.c -- Zeitungsautomat in der Abenteurergilde
+// 
+// $Id:	zeitungsautomat.c 6437 2007-08-18 22:58:58Z Zesstra $
+// 
+
+inherit "/std/thing";
+
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+#include <defines.h>
+#include <www.h>
+
+#define ZEITUNG "/obj/mpa"
+
+void create()
+{
+  if (!clonep(this_object())) return;
+  ::create();
+  SetProp(P_SHORT,"Eine Zeitung");
+  SetProp(P_LONG, 
+    "Hier stehen immer die neuesten Nachrichten und Geruechte drin.\n");
+  SetProp(P_NAME,"Zeitung");
+  AddId(({"zeitung","mpa","servicepaket"}));
+  SetProp(P_GENDER,FEMALE);
+  SetProp(P_NOGET,1);
+  SetProp(P_PICK_MSG, NO_PNG_MSG);
+  SetProp(P_ARTICLE,1);
+  SetProp(P_WWWINFO, "Man kann die MPA auch <A HREF=\"http://"
+      +SERVER+MUDWWW + "?" +REQ + "=" + R_NEWS + "\">lesen</A>");
+}
+
+int get(object targ) {
+  int res;
+
+  if (!objectp(targ) || !interactive(targ))
+    return ME_CANT_BE_TAKEN;
+  
+  if (present_clone(ZEITUNG, targ)) {
+    tell_object(targ, "Du hast doch bereits eine Zeitung.\n");
+    return ME_CANT_BE_TAKEN;
+  }
+  
+  object zeitung=clone_object("/obj/mpa");
+  if ((res=zeitung->move(targ,M_GET)) == MOVE_OK) {
+    tell_object(targ, "Du nimmst die Zeitung. Sofort erscheint auf "
+	"unerklaerliche Weise eine neue.\n");
+    tell_room(environment(targ), break_string(
+	targ->name(WER) + " nimmt die Zeitung. Sofort erscheint auf "
+	"unerklaerliche Weise eine neue.\n",78),({targ}));
+  }
+  else {
+    tell_object(targ, "Du kannst die Zeitung leider nicht mehr tragen.\n");
+    zeitung->remove(1);
+  }
+  
+  return res;
+}
+
+varargs int move(object target, mixed method)
+{
+  if (method&M_GET && living(target))
+    return get(target);
+  
+  return ::move(target,method);
+}
+
diff --git a/p/daemon/ch.h b/p/daemon/ch.h
new file mode 100644
index 0000000..0afed78
--- /dev/null
+++ b/p/daemon/ch.h
@@ -0,0 +1,10 @@
+// ch.h - channel daemon defines
+// --
+// $timestamp::$
+
+// existiert aus Kompatibilitaetsgruenden, weil die haelfte vom Mud dieses und
+// die andere Haelfte channel.h inkludiert. Entscheidung, den eindeutigeren
+// Namen channel.h zu benutzen und dieses einfach channel.h inkludieren zu
+// lassen.
+#include "channel.h"
+
diff --git a/p/daemon/channel.h b/p/daemon/channel.h
new file mode 100644
index 0000000..ad1f506
--- /dev/null
+++ b/p/daemon/channel.h
@@ -0,0 +1,59 @@
+// ch.h - channel daemon defines
+// --
+// $timestamp::$
+
+#ifndef __DAEMON_CHANNEL_H__
+#define __DAEMON_CHANNEL_H__
+
+#define P_CHANNELS		"channels"
+#define P_STD_CHANNEL		"std_channel"
+
+#define CHMASTER		"/p/daemon/channeld"
+
+// Message types
+#define MSG_SAY                 0
+#define MSG_EMOTE               1
+#define MSG_GEMOTE              2
+#define MSG_EMPTY               3
+
+// Errors
+#define E_ACCESS_DENIED         -1
+#define E_ALREADY_JOINED        -2
+#define E_NOT_MEMBER            -3
+#define E_EMPTY_MESSAGE         -4
+#define E_UNKNOWN_TYPE          -5
+
+// Commands
+#define C_NEW			"new"
+#define C_JOIN			"join"
+#define C_LEAVE			"leave"
+#define C_SEND			"send"
+#define C_LIST			"list"
+#define C_FIND			"find"
+
+// definition of the list mapping entry 
+// ([ channelname : ({ I_MEMBER, I_ACCESS, I_INFO, I_MASTER, I_NAME }) ])
+#define I_MEMBER                0
+#define I_ACCESS                1
+#define I_INFO                  2
+#define I_MASTER		3
+#define I_NAME			4
+
+#endif //__DAEMON_CHANNEL_H__
+
+// prototypes
+#ifdef NEED_PROTOTYPES
+
+#ifndef __CHANNEL_H_PROTO__
+#define __CHANNEL_H_PROTO__
+varargs int new(string ch, object pl, mixed info);
+varargs int send(string ch, object pl, string msg, int type);
+
+// ok, keine Prototypen, aber trotzdem nur fuer channeld.c interessant.
+#define MAX_HIST_SIZE   200
+#define MAX_CHANNELS    90
+
+#endif //__CHANNEL_H_PROTO__
+
+#endif //NEED_PROTOTYPES
+
diff --git a/p/daemon/channeld.c b/p/daemon/channeld.c
new file mode 100644
index 0000000..1331d57
--- /dev/null
+++ b/p/daemon/channeld.c
@@ -0,0 +1,647 @@
+// channeld.c
+//
+// $Id: channeld.c 9138 2015-02-03 21:46:56Z Zesstra $
+//
+
+#pragma strong_types
+#pragma no_shadow // keine Shadowing...
+#pragma no_clone
+#pragma no_inherit
+#pragma save_types
+
+#include <sys_debug.h>
+#include <lpctypes.h>
+#include <wizlevels.h>
+
+#include <properties.h>
+#include <config.h>
+#include <language.h>
+
+#define NEED_PROTOTYPES
+#include "channel.h"
+
+#define CMNAME  "<MasteR>"
+#define CHANNEL_SAVE    "/p/daemon/save/channeld"
+
+#define MEMORY "/secure/memory"
+
+// channels - contains the simple channel information
+//            ([ channelname : ({ I_MEMBER, I_ACCESS, I_INFO, I_MASTER })...])
+private nosave mapping lowerch;
+private nosave mapping channels;
+private nosave mapping channelH;
+private nosave mapping stats;
+
+private mapping channelC;
+private mapping channelB;
+
+private int save_me_soon;
+
+// BEGIN OF THE CHANNEL MASTER ADMINISTRATIVE PART
+
+#define RECV    0
+#define SEND    1
+#define FLAG    2
+
+// Channel flags
+// Levelbeschraenkungen gegen Magierlevel (query_wiz_level) pruefen, nicht
+// P_LEVEL.
+#define F_WIZARD 1
+// Keine Gaeste. ;-)
+#define F_NOGUEST 2
+
+private nosave mapping admin = m_allocate(0, 3);
+
+int check(string ch, object pl, string cmd)
+{
+  int level;
+  
+  if((admin[ch, FLAG] & F_NOGUEST) && pl->QueryGuest()) return 0;
+
+  if((admin[ch, FLAG] & F_WIZARD) && query_wiz_level(pl) < SEER_LVL) return 0;
+  level = (admin[ch, FLAG] & F_WIZARD
+           ? query_wiz_level(pl)
+           : pl->QueryProp(P_LEVEL));
+
+  switch(cmd)
+  {
+  case C_FIND:
+  case C_LIST:
+  case C_JOIN:
+    if(admin[ch, RECV] == -1) return 0;
+    if(admin[ch, RECV] <= level) return 1;
+    break;
+  case C_SEND:
+    if(admin[ch, SEND] == -1) return 0;
+    if(admin[ch, SEND] <= level) return 1;
+    break;
+  case C_LEAVE:
+    return 1;
+  default: break;
+  }
+  return(0);
+}
+
+private int CountUser(mapping l)
+{
+  mapping n;
+  n = ([]);
+  walk_mapping(l, lambda(({'i/*'*/, 'a/*'*/, 'n/*'*/}),
+                         ({#'+=/*'*/, 'n/*'*/,
+                              ({#'mkmapping/*'*/,
+                                   ({#'[/*'*/, 'a/*'*/, 0})})})),
+               &n);
+  return sizeof(n);
+}
+
+private void banned(string n, mixed cmds, string res)
+{
+  res += sprintf("%s [%s], ", capitalize(n), implode(cmds, ","));
+}
+
+private mapping Tcmd = ([]);
+#define TIMEOUT (time() - 60)
+
+void ChannelMessage(mixed msg)
+{
+  string ret, mesg;
+  mixed lag;
+  int max, rekord;
+  string tmp;
+  if(msg[1] == this_object() || !stringp(msg[2]) ||
+     msg[0] != CMNAME || previous_object() != this_object()) return;
+
+
+  mesg = lower_case(msg[2]);
+
+  if(!strstr("hilfe", mesg) && sizeof(mesg) <= 5)
+    ret = "Folgende Kommandos gibt es: hilfe, lag, up[time], statistik, bann";
+  else
+  if(!strstr("lag", mesg) && sizeof(mesg) <= 3)
+  {
+    if(Tcmd["lag"] > TIMEOUT) return;
+    Tcmd["lag"] = time();
+    lag = "/p/daemon/lag-o-daemon"->read_ext_lag_data();
+    ret = sprintf("Lag: %.1f%%/60, %.1f%%/15, %.1f%%/5, %.1f%%/1, "
+                       "%.1f%%/20s, %.1f%%/2s",
+                  lag[5], lag[4], lag[3], lag[2], lag[1], lag[0]);
+    call_out(#'send/*'*/, 2, CMNAME, this_object(), ret);
+    ret = query_load_average();
+  } else
+  if(!strstr("uptime", mesg) && sizeof(mesg) <= 6)
+  {
+    if(Tcmd["uptime"] > TIMEOUT) return;
+    Tcmd["uptime"] = time();
+    if(file_size("/etc/maxusers") <= 0) {
+      ret = "Diese Information liegt nicht vor.";
+    } else {
+      sscanf(read_file("/etc/maxusers"), "%d %s", max, tmp);
+      sscanf(read_file("/etc/maxusers.ever"), "%d %s", rekord, tmp);
+      ret = sprintf("Das MUD laeuft jetzt %s. Es sind momentan %d Spieler "
+                  "eingeloggt; das Maximum lag heute bei %d und der Rekord "
+                  "bisher ist %d.", uptime(), sizeof(users()), max, rekord);
+    }
+  } else
+  if(!strstr("statistik", mesg) && sizeof(mesg) <= 9)
+  {
+    if(Tcmd["statistik"] > TIMEOUT) return;
+    Tcmd["statistik"] = time();
+    ret = sprintf(
+    "Im Moment sind insgesamt %d Ebenen mit %d Teilnehmern aktiv.\n"
+    "Der %s wurde das letzte mal am %s von %s neu gestartet.\n"
+    "Seitdem wurden %d Ebenen neu erzeugt und %d zerstoert.\n",
+    sizeof(channels), CountUser(channels), CMNAME,
+    dtime(stats["time"]), stats["boot"], stats["new"], stats["dispose"]);
+  } else
+  if(!strstr(mesg, "bann"))
+  {
+    string pl, cmd;
+    if(mesg == "bann")
+      if(sizeof(channelB))
+      {
+        ret = "";
+        walk_mapping(channelB, #'banned/*'*/, &ret);
+        ret = "Fuer folgende Spieler besteht ein Bann: " + ret;
+      } else ret = "Zur Zeit ist kein Bann aktiv.";
+    else
+    {
+      if(sscanf(mesg, "bann %s %s", pl, cmd) == 2 &&
+         IS_DEPUTY(msg[1]))
+      {
+#     define CMDS ({C_FIND, C_LIST, C_JOIN, C_LEAVE, C_SEND, C_NEW})
+        pl = lower_case(pl); cmd = lower_case(cmd);
+        if(member(CMDS, cmd) != -1)
+        {
+          if(!pointerp(channelB[pl]))
+            channelB[pl] = ({});
+          if(member(channelB[pl], cmd) != -1)
+            channelB[pl] -= ({ cmd });
+          else
+            channelB[pl] += ({ cmd });
+          ret = "Fuer '"+capitalize(pl)+"' besteht "
+            + (sizeof(channelB[pl]) ?
+               "folgender Bann: "+implode(channelB[pl], ", ") :
+               "kein Bann mehr.");
+          if(!sizeof(channelB[pl]))
+            channelB = m_copy_delete(channelB, pl);
+          save_object(CHANNEL_SAVE);
+        }
+        else ret = "Das Kommando '"+cmd+"' ist unbekannt. Erlaubte Kommandos: "
+               + implode(CMDS, ", ");
+      }
+      else
+      {
+        if(!IS_ARCH(msg[1])) return;
+        else ret = "Syntax: bann <name> <kommando>";
+      }
+    }
+  }
+  else if(mesg == "lust")
+  {
+    mixed t, up;
+    if(Tcmd["lag"] > TIMEOUT ||
+       Tcmd["statistik"] > TIMEOUT ||
+       Tcmd["uptime"] > TIMEOUT) return;
+    Tcmd["lag"] = time();
+    Tcmd["statistik"] = time();
+    Tcmd["uptime"] = time();
+    lag = "/p/daemon/lag-o-daemon"->read_lag_data();
+
+    sscanf(read_file("/etc/maxusers"), "%d %s", max, tmp);
+    sscanf(read_file("/etc/maxusers.ever"), "%d %s", rekord, tmp);
+
+    t=time()-last_reboot_time();
+    up="";
+    if(t >= 86400)
+      up += sprintf("%dT", t/86400);
+    if(t >= 3600)
+      up += sprintf("%dh", (t=t%86400)/3600);
+    if(t > 60)
+      up += sprintf("%dm", (t=t%3600)/60);
+    up += sprintf("%ds", t%60);
+
+    ret = sprintf("%.1f%%/15 %.1f%%/1 %s %d:%d:%d E:%d T:%d",
+                  lag[1], lag[2], up, sizeof(users()), max, rekord, 
+                  sizeof(channels), CountUser(channels));
+  } else return;
+
+  call_out(#'send/*'*/, 2, CMNAME, this_object(), ret);
+}
+
+// setup() -- set up a channel and register it
+//            arguments are stored in the following order:
+//            ({ channel name,
+//               receive level, send level,
+//               flags,
+//               description,
+//               master obj
+//            })
+private void setup(mixed c)
+{
+  closure cl;
+  object m;
+  string d;
+  d = "- Keine Beschreibung -";
+  m = this_object();
+  if(sizeof(c) && sizeof(c[0]) > 1 && c[0][0] == '\\')
+    c[0] = c[0][1..];
+
+  switch(sizeof(c))
+  {
+  case 6:
+    if(!stringp(c[5]) || !sizeof(c[5]) 
+        || (catch(m=load_object(c[5]);publish) || !objectp(m) ))
+      m = this_object();
+  case 5: d = stringp(c[4]) || closurep(c[4]) ? c[4] : d;
+  case 4: admin[c[0], FLAG] = to_int(c[3]);
+  case 3: admin[c[0], SEND] = to_int(c[2]);
+  case 2: admin[c[0], RECV] = to_int(c[1]);
+    break;
+  case 0:
+  default:
+    return;
+  }
+  switch(new(c[0], m, d))
+  {
+  case E_ACCESS_DENIED:
+    log_file("CHANNEL", sprintf("[%s] %s: %O: error, access denied\n",
+                           dtime(time()), c[0], m));
+    break;
+  default:
+    break;
+  }
+  return;
+}
+
+void initialize()
+{
+  mixed tmp;
+  tmp = read_file(object_name(this_object())+".init");
+  tmp = regexp(old_explode(tmp, "\n"), "^[^#]");
+  tmp = map(tmp, #'regexplode/*'*/, "[^:][^:]*$|[ \\t]*:[ \\t]*");
+  tmp = map(tmp, #'regexp/*'*/, "^[^: \\t]");
+  map(tmp, #'setup/*'*/);
+}
+
+// BEGIN OF THE CHANNEL MASTER IMPLEMENTATION
+
+void create()
+{
+  seteuid(getuid());
+  restore_object(CHANNEL_SAVE);
+  if(!channelC) channelC = ([]);
+  if(!channelB) channelB = ([]);
+  channels = ([]);
+
+  /* Die Channel-History wird nicht nur lokal sondern auch noch im Memory 
+     gespeichert, dadurch bleibt sie auch ueber ein Reload erhalten. 
+     Der folgende Code versucht, den Zeiger aus dem Memory zu holen. Falls
+     das nicht moeglich ist, wird ein neuer erzeugt und gegebenenfalls im
+     Memory abgelegt. */
+
+  // Hab ich die noetigen Rechte im Memory?
+  if (call_other(MEMORY,"HaveRights")) {
+    // Objektpointer laden
+    channelH = (mixed) call_other(MEMORY,"Load","History");
+
+    // Wenns nich geklappt hat, hat der Memory noch keinen Zeiger, dann
+    if (!mappingp(channelH)){
+      // Zeiger erzeugen
+      channelH = ([]);
+      // und in den Memory schreiben
+      call_other(MEMORY,"Save","History",channelH);
+    }
+  } else {
+    // Keine Rechte im Memory, dann wird mit einem lokalen Zeiger gearbeitet.
+    channelH = ([]);
+  }
+
+  stats = (["time": time(),
+            "boot": capitalize(getuid(previous_object())||"<Unbekannt>")]);
+  new(CMNAME, this_object(), "Zentrale Informationen zu den Ebenen");
+  initialize();
+  map_objects(efun::users(), "RegisterChannels");
+  this_object()->send(CMNAME, this_object(),
+                      sprintf("%d Ebenen mit %d Teilnehmern initialisiert.",
+                              sizeof(channels),
+                              CountUser(channels)));
+}
+
+// reset() and cache_to() - Cache Timeout, remove timed out cached channels
+// SEE: new, send
+private int cache_to(string key, mapping m, int t)
+{
+  if(!pointerp(m[key]) || m[key][2] + 43200 > t) return 1;
+  return(0);
+}
+
+varargs void reset(int nonstd)
+{
+  channelC = filter_indices(channelC, #'cache_to/*'*/, channelC, time());
+  if (save_me_soon)
+  {
+    save_me_soon=0;
+    save_object(CHANNEL_SAVE);
+  }
+}
+
+// name() - define the name of this object.
+string name() { return CMNAME; }
+string Name() { return CMNAME; }
+
+// access() - check access by looking for the right argument types and
+//            calling access closures respectively
+// SEE: new, join, leave, send, list, users
+// Note: <pl> is usually an object, only the master supplies a string during
+//       runtime error handling.
+varargs private int access(mixed ch, mixed pl, string cmd, string txt)
+{
+  mixed co, m;
+
+  if(!stringp(ch) || !sizeof(ch = lower_case(ch)) || !channels[ch])
+    return 0;
+  if(!channels[ch][I_ACCESS]||!previous_object(1)||!extern_call()||
+     previous_object(1)==this_object()||
+     (stringp(channels[ch][I_MASTER])&&
+      previous_object(1)==find_object(channels[ch][I_MASTER]))||
+     getuid(previous_object(1)) == ROOTID)
+    return 2;
+  if(!objectp(pl) || 
+     ((previous_object(1)!=pl) &&(previous_object(1)!=this_object()))) 
+     return 0;
+   if(pointerp(channelB[getuid(pl)]) &&
+     member(channelB[getuid(pl)], cmd) != -1)
+    return 0;
+   if(stringp(channels[ch][I_MASTER]) &&
+     (!(m = find_object(channels[ch][I_MASTER])) ||
+      (!to_object(channels[ch][I_ACCESS]) ||
+       get_type_info(channels[ch][I_ACCESS])[1])))
+  {
+    string err;
+    if(!objectp(m)) err = catch(load_object(channels[ch][I_MASTER]);publish);
+    if(!err &&
+       ((!to_object(channels[ch][I_ACCESS]) ||
+         get_type_info(channels[ch][I_ACCESS])[1]) &&
+        !closurep(channels[ch][I_ACCESS] =
+                  symbol_function("check",
+                                  find_object(channels[ch][I_MASTER])))))
+    {
+      log_file("CHANNEL", sprintf("[%s] %O -> %O\n",
+                                  dtime(time()), channels[ch][I_MASTER],
+                                  err));
+      channels = m_copy_delete(channels, ch);
+      return 0;
+    }
+    this_object()->join(ch, find_object(channels[ch][I_MASTER]));
+  }
+  if(closurep(channels[ch][I_ACCESS]))
+      return funcall(channels[ch][I_ACCESS],
+                     channels[ch][I_NAME], pl, cmd, &txt);
+}
+
+// new() - create a new channel
+//         a channel with name 'ch' is created, the player is the master
+//         info may contain a string which describes the channel or a closure
+//         to display up-to-date information, check may contain a closure
+//         called when a join/leave/send/list/users message is received
+// SEE: access
+
+#define IGNORE  "^/xx"
+
+varargs int new(string ch, object pl, mixed info)
+{
+  mixed pls;
+
+  if(!objectp(pl) || !stringp(ch) || !sizeof(ch) || channels[lower_case(ch)] ||
+     (pl == this_object() && extern_call()) ||
+     sizeof(channels) >= MAX_CHANNELS ||
+     sizeof(regexp(({ object_name(pl) }), IGNORE)) ||
+     (pointerp(channelB[getuid(pl)]) &&
+      member(channelB[getuid(pl)], C_NEW) != -1))
+    return E_ACCESS_DENIED;
+
+  if(!info) {
+    if(channelC[lower_case(ch)]) {
+      ch = channelC[lower_case(ch)][0];
+      info = channelC[lower_case(ch)][1];
+    }
+    else return E_ACCESS_DENIED;
+  }
+  else channelC[lower_case(ch)] = ({ ch, info, time() });
+
+  pls = ({ pl });
+
+  channels[lower_case(ch)] = ({ pls,
+                                symbol_function("check", pl) ||
+                                #'check/*'*/, info,
+                                (!living(pl) &&
+                                 !clonep(pl) &&
+                                 pl != this_object()
+                                 ? object_name(pl)
+                                 : pl),
+                                 ch,
+                             });
+
+  // ChannelH fuer einen Kanal nur dann initialisieren, wenn es sie noch nich gibt.
+  if ( !pointerp(channelH[lower_case(ch)]) )
+         channelH[lower_case(ch)] = ({});
+
+  if(pl != this_object())
+    log_file("CHANNEL.new", sprintf("[%s] %O: %O %O\n",
+                                    dtime(time()), ch, pl, info));
+  if(!pl->QueryProp(P_INVIS))
+    this_object()->send(CMNAME, pl,
+                        "laesst die Ebene '"+ch+"' entstehen.", MSG_EMOTE);
+  stats["new"]++;
+ 
+  save_me_soon=1;
+  return(0);
+}
+
+// join() - join a channel
+//          this function checks whether the player 'pl' is allowed to join
+//          the channel 'ch' and add if successful, one cannot join a channel
+//          twice
+// SEE: leave, access
+int join(string ch, object pl)
+{
+  if(!funcall(#'access,&ch, pl, C_JOIN)) return E_ACCESS_DENIED;
+  if(member(channels[ch][I_MEMBER], pl) != -1) return E_ALREADY_JOINED;
+  channels[ch][I_MEMBER] += ({ pl });
+  return(0);
+}
+
+// leave() - leave a channel
+//           the access check in this function is just there for completeness
+//           one should always be allowed to leave a channel.
+//           if there are no players left on the channel it will vanish, unless
+//           its master is this object.
+// SEE: join, access
+int leave(string ch, object pl)
+{
+  int pos;
+  if(!funcall(#'access,&ch, pl, C_LEAVE)) return E_ACCESS_DENIED;
+  channels[ch][I_MEMBER] -= ({0}); // kaputte Objekte erstmal raus
+  if((pos = member(channels[ch][I_MEMBER], pl)) == -1) return E_NOT_MEMBER;
+  if(pl == channels[ch][I_MASTER] && sizeof(channels[ch][I_MEMBER]) > 1)
+  {
+    channels[ch][I_MASTER] = channels[ch][I_MEMBER][1];
+    if(!pl->QueryProp(P_INVIS))
+      this_object()->send(ch, pl, "uebergibt die Ebene an "
+                          +channels[ch][I_MASTER]->name(WEN)+".", MSG_EMOTE);
+  }
+  channels[ch][I_MEMBER][pos..pos] = ({ });
+
+
+  if(!sizeof(channels[ch][I_MEMBER]) &&
+     !stringp(channels[ch][I_MASTER]))
+  {
+    // delete the channel that has no members
+    if(!pl->QueryProp(P_INVIS))
+      this_object()->send(CMNAME, pl,
+                          "verlaesst als "
+                          +(pl->QueryProp(P_GENDER) == 1 ? "Letzter" :
+                            "Letzte")
+                          +" die Ebene '"
+                          +channels[ch][I_NAME]
+                          +"', worauf diese sich in einem Blitz oktarinen "
+                          +"Lichts aufloest.", MSG_EMOTE);
+    channelC[lower_case(ch)] = ({ channels[ch][I_NAME],
+                                  channels[ch][I_INFO], time() });
+    m_delete(channels, lower_case(ch));
+
+    // Wird ein Channel entfernt, wird auch seine History geloescht
+    channelH = m_copy_delete(channelH, lower_case(ch));
+
+    stats["dispose"]++;
+    save_me_soon=1;
+  }
+  return(0);
+}
+
+// send() - send a message to all recipients of the specified channel 'ch'
+//          checks if 'pl' is allowed to send a message and sends if success-
+//          ful a message with type 'type'
+//          'pl' must be an object, the message is attributed to it. e.g.
+//            ignore checks use it. It can be != previous_object()
+// SEE: access, ch.h
+varargs int send(string ch, object pl, string msg, int type)
+{
+  int a;
+
+  if(!(a = funcall(#'access,&ch, pl, C_SEND, &msg))) return E_ACCESS_DENIED;
+  if(a < 2 && member(channels[ch][I_MEMBER], pl) == -1) return E_NOT_MEMBER;
+  if(!msg || !stringp(msg) || !sizeof(msg)) return E_EMPTY_MESSAGE;
+  map_objects(channels[ch][I_MEMBER],
+              "ChannelMessage", ({ channels[ch][I_NAME], pl, msg, type }));
+  if(sizeof(channelH[ch]) > MAX_HIST_SIZE)
+    channelH[ch] = channelH[ch][1..];
+  channelH[ch] += ({ ({ channels[ch][I_NAME],
+                        (stringp(pl)
+                         ? pl
+                         : (pl->QueryProp(P_INVIS)
+                            ? "/("+capitalize(getuid(pl))+")$" : "")
+                         + (pl->Name(WER, 2) || "<Unbekannt>")),
+                         msg+" <"+strftime("%a, %H:%M:%S")+">\n",
+                         type }) });
+  return(0);
+}
+
+// list() - list all channels, that are at least receivable by 'pl'
+//          returns a mapping,
+// SEE: access, channels
+private void clean(string n, mixed a) { a[0] -= ({ 0 }); }
+mixed list(object pl)
+{
+  mapping chs;
+  
+  chs = filter_indices(channels, #'access/*'*/, pl, C_LIST);
+  walk_mapping(chs, #'clean/*'*/);
+  if(!sizeof(chs)) return E_ACCESS_DENIED;
+  return deep_copy(chs);
+}
+
+// find() - find a channel by its name (may be partial)
+//          returns an array for multiple results and 0 for no matching name
+// SEE: access
+mixed find(string ch, object pl)
+{
+  mixed chs, s;
+  if(stringp(ch)) ch = lower_case(ch);
+  if( !sizeof(regexp(({ch}),"^[<>a-z0-9#-]*$")) ) return 0; // RUM
+  if(!sizeof(chs = regexp(m_indices(channels), "^"+ch+"$")))
+    chs = regexp(m_indices(channels), "^"+ch);
+  if((s = sizeof(chs)) > 1)
+    if(sizeof(chs = filter(chs, #'access/*'*/, pl, C_FIND)) == 1)
+      return channels[chs[0]][I_NAME];
+    else return chs;
+  return ((s && funcall(#'access,chs[0], pl, C_FIND)) ? channels[chs[0]][I_NAME] : 0);
+}
+
+// history() - get the history of a channel
+// SEE: access
+mixed history(string ch, object pl)
+{
+  if(!funcall(#'access,&ch, pl, C_JOIN))
+    return E_ACCESS_DENIED;
+  return deep_copy(channelH[ch]);
+}
+
+// remove - remove a channel (wird aus der Shell aufgerufen)
+// SEE: new
+mixed remove(string ch, object pl)
+{
+  mixed members;
+
+  if(previous_object() != this_object())
+    if(!stringp(ch) ||
+       pl != this_player() || this_player() != this_interactive() ||
+       this_interactive() != previous_object() ||
+       !IS_ARCH(this_interactive()))
+      return E_ACCESS_DENIED;
+
+  if(channels[lower_case(ch)]) {
+    channels[lower_case(ch)][I_MEMBER] =
+        filter_objects(channels[lower_case(ch)][I_MEMBER],
+                       "QueryProp", P_CHANNELS);
+    map(channels[lower_case(ch)][I_MEMBER],
+        lambda(({'u/*'*/}), ({#'call_other/*'*/, 'u, /*'*/
+                                   "SetProp", P_CHANNELS,
+                                   ({#'-/*'*/,
+                                          ({#'call_other/*'*/, 'u, /*'*/
+                                                 "QueryProp", P_CHANNELS}),
+                                          '({ lower_case(ch) })/*'*/,})
+                                   })));
+    channels = m_copy_delete(channels, lower_case(ch));
+
+    // Wird ein Channel entfernt, wird auch seine History geloescht
+    if( pointerp(channelH[lower_case(ch)] ))
+     channelH = m_copy_delete(channelH, lower_case(ch));
+
+    stats["dispose"]++;
+  }
+  if(!channelC[lower_case(ch)])
+    return E_ACCESS_DENIED;
+  channelC = m_copy_delete(channelC, lower_case(ch));
+  save_me_soon=1;
+  return(0);
+}
+
+// Wird aus der Shell aufgerufen 
+mixed clear_history(string ch)
+{
+  mixed members;
+  // Sicherheitsabfragen
+  if(previous_object() != this_object())
+    if(!stringp(ch) ||
+       this_player() != this_interactive() ||
+       this_interactive() != previous_object() ||
+       !IS_ARCH(this_interactive()))
+      return E_ACCESS_DENIED;
+
+  // History des Channels loeschen
+  if( pointerp(channelH[lower_case(ch)] ))
+    channelH[lower_case(ch)]=({});
+
+  return 0;
+}
diff --git a/p/daemon/channeld.init b/p/daemon/channeld.init
new file mode 100644
index 0000000..f099d2d
--- /dev/null
+++ b/p/daemon/channeld.init
@@ -0,0 +1,65 @@
+# CHANNEL MASTER INIT FILE
+# To create a new channel:
+# <name>:<recv>:<send>:<flags>:<info>:<master>
+Allgemein:   0: 0: 0:Allgemeine Unterhaltungsebene
+Abenteuer:   0: 0: 0:Fragen die Abenteuer betreffen:	/secure/questmaster
+Grats:	     0: 0: 0:Gratulationen zu geloesten Abenteuern etc
+Beileid:     0: 0: 0:Beileidsbekundungen jeglicher Art
+Fraternitas: 0: 0: 0:Fraternitas dono Archmagorum
+Tod:         0:-1: 0:Wer stirbt wann?:/room/death/death_room
+TdT:         15:15:1:Tod den Testies!:/room/death/death_room
+Moerder:     0:-1: 0:Guck mal wer da spricht...:	/std/corpse
+Seher:       1: 1: 1:Diskussionsebene fuer Seher
+ZT:          0: 0: 0:Nuetzliche Tips fuer die Zaubertranksuche
+#
+# Guild channels
+#
+Klerus:      0: 0: 0:Die Priester:			/gilden/klerus
+Bierschuettler: 0: 0: 0:Die Bierschuettler:		/gilden/bierschuettler
+Werwoelfe:   0: 0: 0:Die Werwoelfe:                     /gilden/werwoelfe
+AbGilde:     0: 0: 0:Abenteurer, und solche, die es werden wollen: /gilden/abenteurer
+Zauberer:    0: 0: 0:Die Zauberer:			/gilden/zauberer
+Tanjian:     0: 0: 0:Die Tanjian:                       /gilden/tanjian
+Karate:      0: 0: 0:Die Karateka:			/gilden/karate
+Chaos:       0: 0: 0:Chaoten...:			/gilden/chaos
+Trves:       0: 0: 0:Neues vom Koru Tschakar Struv:     /p/kaempfer/std/k_master
+Magus:       0: 0: 0:Der magische Convent zu Castalla Rosso:  /gilden/magus
+Dunkelelfen: 0: 0: 0:Die Dunkelelfen:			/gilden/dunkelelfen
+Uruk-Hai:    0: 0: 0:Fuer echte Orks:			/gilden/urukhai
+#
+# Wizard channels
+#
+Magier:     15:15: 1:Diskussionsebene fuer Magier
+Erzmagier:  60:60: 1:Erzmagier
+Goetter:   100:100: 1:Goetter
+LPC:        15:15: 1:Fragen zur Programmierung und LPC
+#
+Debug:      20:60: 1:Debug und Fehlermeldungen:		/p/daemon/debug
+Entwicklung: 20:60:1:Fehler rund um Eigenentwicklungen: /p/daemon/debug
+Warnungen:   20:60:1:Laufzeit-Warnungen:		/p/daemon/debug
+Snoop:      60:-1: 1:Wer snoopt denn hier?:		/secure/simul_efun/simul_efun
+FTP:        60:-1: 1:Wer macht denn FTP?:		/secure/bbmaster
+#
+# Intermud channels
+#
+Intermud:    0: 5: 2:Intermud chat (Englisch):		/secure/udp/channel
+Intercode:  20:20: 3:Programmierfragen (Englisch):	/secure/udp/channel
+Interadmin: 45:55: 3:Administration (Englisch):		/secure/udp/channel
+D-chat:	     0: 5: 2:Verbindet Deutsche MUDs:		/secure/udp/channel
+D-linux:     0: 5: 2:Linux im D-Chat:    		/secure/udp/channel
+D-tv-alles:  0: 5: 2:Deutsches TV Programm:		/secure/udp/channel
+D-news:      0: 5: 2:Deutsches Nachrichten:		/secure/udp/channel
+D-code:	    20:20: 3:MUD Programmier Forum:		/secure/udp/channel
+D-adm:      60:60: 3:Deutschsprachige Administration:	/secure/udp/channel
+#
+# special
+#
+Wissenschaft:0: 0: 0:Wissenschaftliche Dispute
+Kultur:	     0: 0: 0:Kulturelle Ereignisse und Erguesse
+Sport:	     0: 0: 0:Sport und Spiel
+Anfaenger:   0: 0: 0:Die Ebene fuer Anfaengerfragen
+twitter:     0: 0: 1:Ein Vogel im Mud?: /secure/misc/twitter
+#
+# Gebietspezifisches
+#
+Metzelorakel:0: 0: 0:Fuer die Metzelwuetigen:		/d/ebene/arathorn/orakel/secure/schamane
diff --git a/p/daemon/channeld.trusted b/p/daemon/channeld.trusted
new file mode 100644
index 0000000..8f19633
--- /dev/null
+++ b/p/daemon/channeld.trusted
@@ -0,0 +1,12 @@
+# CHANNELD MASTER TRUSTED OBJECTS FILE
+# <object> : <channels> : <name>
+/secure/master                       ::
+/secure/udp/channel                  :IOAWqP:
+/secure/simul_efun                   ::
+/room/death/death_room               :T:
+/secure/questmaster                  :b:
+/d/inseln/anthea/room/land/muehlstein:T:
+/std/corpse                          :M:
+/p/daemon/inews                      :aNqW:DW
+/gilden/karate                       :K:
+/gilden/chaos                        :c:
diff --git a/p/daemon/classdb.c b/p/daemon/classdb.c
new file mode 100644
index 0000000..2db40ae
--- /dev/null
+++ b/p/daemon/classdb.c
@@ -0,0 +1,91 @@
+// MorgenGrauen MUDlib
+//
+// /obj/classd.c -- master object for classes.
+//
+// Soll irgendwann mal Klassen aehnlich der Material-DB verwalten, momentan
+// machts es erstmal einfach eine Ersetzung von Aliasen und ein Hinzufuegen
+// von impliziten Klassen.
+//
+// $Id$
+#pragma strict_types,save_types
+#pragma no_clone, no_shadow, no_inherit
+#pragma verbose_errors,combine_strings
+#pragma pedantic, range_check, warn_deprecated
+
+#include <class.h>
+
+// Mapping von Aliasen fuer Klassen. Aliase duerfen nicht rekursiv sein.
+private nosave mapping classaliases =
+  ([
+   ]);
+// Mapping von impliziten Klassen fuer eine gegebene Klasse. Implizite Klassen
+// sind als Array von Klassennamen gegeben. Implizite Klassen duerfen zur Zeit
+// nicht rekursiv sein und keine Aliase enthalten.
+private nosave mapping implicitclasses =
+  ([
+     CL_ZOMBIE:       ({CL_UNDEAD}),
+     CL_SKELETON:     ({CL_UNDEAD}),
+     CL_GHOUL:        ({CL_UNDEAD}),
+     CL_VAMPIRE:      ({CL_UNDEAD}),
+     CL_HOBGOBLIN:    ({CL_GOBLIN}),
+     CL_MAMMAL_LAND:  ({CL_MAMMAL, CL_ANIMAL}),
+     CL_MAMMAL_WATER: ({CL_MAMMAL, CL_ANIMAL}),
+     CL_SNAKE:        ({CL_REPTILE}),
+     CL_ARACHNID:     ({CL_ANIMAL}),
+     CL_BIRD:         ({CL_ANIMAL}),
+     CL_FISH:         ({CL_ANIMAL}),
+     CL_FROG:         ({CL_ANIMAL}),
+     CL_INSECT:       ({CL_ANIMAL}),
+     CL_MAMMAL:       ({CL_ANIMAL}),
+     CL_REPTILE:      ({CL_ANIMAL}),
+     CL_SNAKE:        ({CL_ANIMAL}),
+     CL_GOLEM:        ({CL_CONSTRUCT}),
+   ]);
+
+protected void create() {
+}
+
+/** Gibt den Alias der Klasse <cls> zurueck.
+ * @param[in] cls string - Klassenname
+ * @return string* - Alias von Klasse <cls>
+ */
+public string * QueryClassAlias(string cls) {
+  if (!stringp(cls))
+      raise_error(sprintf(
+            "Got wrong argument, expected string, got %.50O\n",cls));
+  return copy(classaliases[cls]) || cls;
+}
+
+/** Gibt die impliziten Klassen der Klasse <cls> zurueck.
+ * @param[in] cls string - Klassenname
+ * @return string* - Liste von impliziten Klassen von <cls>
+ */
+public string * QueryImplicitClasses(string cls) {
+  if (!stringp(cls))
+      raise_error(sprintf(
+            "Got wrong argument, expected string, got %.50O\n", cls));
+  return copy(implicitclasses[cls]) || ({});
+}
+
+/** Adds implicit classes to the classes given in <classes>.
+ * Ausserdem werden Klassenaliase aufgeloest.
+ * @param[in] classes string* - Klassenliste.
+ * @return string* - neue Klassenliste
+ * @attention Kann das uebergebene Array aendern. Das Ergebnisarray muss nicht
+ * unique sein, sondern kann Dubletten enthalten.
+ */
+public string * AddImplicitClasses(string *classes) {
+  if (!pointerp(classes))
+      raise_error(sprintf(
+          "Got wrong argument, expected array, got %.50O\n",classes));
+  foreach(string cls : &classes) {
+      // zuerst Aliase ersetzen
+      if (member(classaliases, cls))
+          cls = classaliases[cls];
+      // dann implizite Klassen addieren.
+      if (member(implicitclasses, cls))
+          classes += implicitclasses[cls];
+  }
+  return classes;
+}
+
diff --git a/p/daemon/debug.c b/p/daemon/debug.c
new file mode 100644
index 0000000..0ad6975
--- /dev/null
+++ b/p/daemon/debug.c
@@ -0,0 +1,128 @@
+
+#pragma strict_types,save_types
+#pragma no_clone,no_shadow
+
+#include <daemon.h>
+#include <logging.h>
+
+static int d_start, d_end, e_start, e_end;
+
+protected void create()
+{    
+    d_end = d_start = e_end = e_start = file_size(__DEBUG_LOG__);
+}
+
+int check( string ch, object pl, string cmd, string txt )
+{
+    mixed tmp;
+    
+    if ( ch != "Debug" && ch != "Entwicklung" && ch != "Warnungen" )
+        return 0;
+    
+    if( objectp(pl) && query_once_interactive(pl) && query_wiz_level(pl) > 15 )
+        switch(cmd){
+        case C_FIND:
+        case C_LIST:
+        case C_JOIN:
+        case C_LEAVE:
+            return 1;
+        case C_SEND:
+            // Wer (noch) nicht auf dem Kanal ist, bekommt auch keine
+            // Ausgabe - sonst gibt es selbige doppelt, da der CHMASTER
+            // einen automatisch den Kanal betreten laesst ...
+            if ( !objectp(pl) || !pointerp(tmp=(mixed)pl->QueryProp(P_CHANNELS)) ||
+                 member( tmp, lower_case(ch) ) == -1 )
+                return 1;
+
+            switch( lower_case(txt) ){
+            case "backtrace":
+                {
+                    string bt, log;
+                    int start, end;
+                    
+                    if ( ch == "Debug" ) {
+                        start = d_start;
+                        end = d_end;
+                    }
+                    else if (ch == "Entwicklung") {
+                        start = e_start;
+                        end = e_end;
+                    }
+		    else if (ch == "Warnungen") {
+			pl->Message( "[" + ch + ":] Backtrace fuer Warnungen "
+			    "nicht verfuegbar!\n");
+			return 0;
+		    }
+		    else return(1);
+                    
+                    if( start >= end ) 
+                        bt = "[" + ch + ":] Kein Backtrace verfuegbar!\n";
+                    else {
+                        log = regreplace( read_bytes( __DEBUG_LOG__, start,
+                                                      (end-start>10000)?10000:
+						      end - start ) || "\nFehler beim Einlesen des Debuglogs: Kein Backtrace verfuegbar!\n",
+                                          "(Misplaced|current_object|"
+                                          "Connection|Host)[^\n]*\n",
+                                          "", 1 );
+                        bt = "[" + ch + ":] -- BACKTRACE BEGIN --\n"
+                            + log
+                            + "[" + ch + ":] -- BACKTRACE END --\n";
+                    }
+                    
+                    call_out( symbol_function( "Message", pl ), 0, bt );
+                }
+                break;
+
+            default:
+		if ( ch != "Warnungen" ) {
+		  pl->Message( "[" + ch + ":] Hilfe...\n"
+                             "[" + ch + ":] Folgende Kommandos stehen zur "
+                             "Verfuegung:\n"
+                             "[" + ch + ":] 'backtrace' -- sendet den "
+                             "Backtrace zum Fehler\n"
+                             "[" + ch + ":] 'hilfe'     -- sendet diese "
+                             "Hilfeseite\n");
+		}
+		else {
+		  pl->Message( "[" + ch + ":] Keine Kommandos verfuegbar!\n");
+		}
+            }
+	}
+    
+    return 0;
+}
+
+string name() { return "<Debugger>"; }
+string Name() { return "<Debugger>"; }
+
+void ChannelMessage( mixed a )
+{
+    int fs;
+
+    fs = file_size(__DEBUG_LOG__);
+    
+    switch( a[0] ){
+    case "Debug":
+        if (  fs > d_end && fs > e_end ){
+	    d_start = max((d_end > e_end ? d_end : e_end),0);
+            d_end = fs;
+        }
+        else if (fs < d_start || fs <= d_end) {
+	    // Debuglog wurde wohl neu geoeffnet.
+	    d_start = d_end = e_start = e_end = fs;
+	}
+        break;
+        
+    case "Entwicklung":
+        if (  fs > d_end && fs > e_end ){
+	    e_start = max((e_end > d_end ? e_end : d_end),0);
+            e_end = fs;
+        }
+        else if (fs < e_start || fs <= e_end) {
+	    // Debuglog wurde wohl neu geoeffnet.
+	    d_start = d_end = e_start = e_end = fs;
+	}
+        break;
+    }
+}
+ 
diff --git a/p/daemon/dnslookup.c b/p/daemon/dnslookup.c
new file mode 100644
index 0000000..a952324
--- /dev/null
+++ b/p/daemon/dnslookup.c
@@ -0,0 +1,101 @@
+// MorgenGrauen MUDlib
+//
+// dnslookup.c - kleines DNS-Lookup-Objekt mit Cache.
+//               Braucht externen DNS-Resolver, der per UDP ansprechbar ist.
+//
+// Autor: Zesstra
+
+/* Im Mudlibmaster in receive_udp() braucht es noch etwas a la:
+  if( message[0..9]=="DNSLOOKUP\n" ) {
+    "/p/daemon/dnslookup"->update( message );
+    return;
+  }
+
+  ext. Objekt erwartet ganz simpel <hostname>\n als Eingabe und sendet zurueck:
+  DNSLOOKUP\n<hostname>\n<ip-adresse>|UNKNOWN\n
+  (Das | steht fuer oder.)
+*/
+
+#pragma strong_types,rtt_checks
+
+// externer server, der den DNS-Resolve macht.
+#define IPLOOKUP_HOST "127.0.0.1"
+#define IPLOOKUP_PORT 8712
+
+mapping cache = ([]);
+
+mapping _query_cache() {return cache;}
+
+protected void create()
+{
+  seteuid(getuid());
+  set_next_reset(3600);
+}
+
+// ja... total simpel... nicht mehr aufwand als noetig.
+void reset()
+{
+  cache = ([]);
+  set_next_reset(10800);
+}
+
+// eigentliche Anfrage an den externen Resolver senden.
+protected void make_request(string hostname)
+{
+  send_udp( IPLOOKUP_HOST, IPLOOKUP_PORT, hostname+"\n" );
+  if (sizeof(cache) > 1000)
+    reset();
+  cache[hostname]=0; // cached request, but unknown result (yet)
+}
+
+// Hostnamen asynchron aufloesen.
+public string resolve( string hostname )
+{
+  if (member(cache, hostname))
+    return cache[hostname];
+  make_request( hostname );
+  return 0;  // leider...
+}
+
+// Pruefung, ob von <ip> Verbindungen zu unserer IP nach <localport>
+// weitergeleitet werden.
+public int check_tor(string ip, int localport)
+{
+  string *arr=explode(ip,".");
+  if (!sizeof(arr)==4)
+    return 0;
+  string req =
+    sprintf("%s.%s.%s.%s.%d.60.24.79.87.ip-port.exitlist.torproject.org",
+            arr[3],arr[2],arr[1],arr[0],localport);
+  
+  return resolve(req) == "127.0.0.2";
+}
+
+// Pruefung, ob <ip> auf einigen Blacklists steht.
+public int check_dnsbl(string ip)
+{
+  string *arr=explode(ip,".");
+  if (!sizeof(arr)==4)
+    return 0;
+  string req =
+    sprintf("%s.%s.%s.%s.dnsbl.dronebl.org",
+            arr[3],arr[2],arr[1],arr[0]);
+  
+  return resolve(req) != 0;
+}
+
+/* wird vom master aufgerufen, wenn eine antwort vom externen
+ * iplookup dienst eintrudelt.
+ */
+public void update( string udp_reply )
+{
+  if( previous_object()!=master() ) return;
+  string *reply = explode(udp_reply,"\n");
+  if( sizeof(reply)<3 ) return;
+
+  if (reply[2] == "UNKNOWN")
+    cache[reply[1]] = 0;
+  else
+    cache[reply[1]] = reply[2];
+}
+
diff --git a/p/daemon/eventd.c b/p/daemon/eventd.c
new file mode 100644
index 0000000..b81d38b
--- /dev/null
+++ b/p/daemon/eventd.c
@@ -0,0 +1,334 @@
+// MorgenGrauen MUDlib
+//
+// /p/daemon/eventd.c -- Event Dispatcher
+//
+// $Id$
+#pragma strict_types,save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+#include <wizlevels.h>
+#include <defines.h>
+#include <events.h>
+
+// Fuer Statistiken
+#define STATISTICS
+
+#define HOME(x) (__PATH__(0)+x)
+#define STORE HOME("save/eventd")
+
+
+#define LOG(x) log_file("EVENTS", sprintf(\
+      "[%s] %s\n",dtime(time()),x))
+//#define LOGEVENT(x,y,z) log_file("EVENTS", sprintf(\
+//      "[%s] Event %s triggered by %O (Args: %.40O)\n",dtime(time()),x,y,z))
+#define LOGEVENT(x,y,z)
+//#define LOGREGISTER(w,x,y,z) log_file("EVENTLISTENER",sprintf(\
+//      "[%s] %O (Fun: %.25s) registered for %s by %O\n",dtime(time()),w,x,y,z))
+#define LOGREGISTER(w,x,y,z)
+//#define LOGUNREGISTER(x,y,z) log_file("EVENTLISTENER",sprintf(\
+//      "[%s] %O was unregistered from %s by %O\n",dtime(time()),x,y,z))
+#define LOGUNREGISTER(x,y,z)
+//#define LOGEVENTFINISH(x,y) log_file("EVENTS", sprintf(\
+//      "[%s] Event %s (triggered by %O) finished\n",dtime(time()),x,y))
+#define LOGEVENTFINISH(x,y)
+
+#ifndef DEBUG
+#define DEBUG(x)  if (find_player("zesstra"))\
+          tell_object(find_player("zesstra"),\
+                      "EDBG: "+x+"\n")
+#endif
+
+#define TICK_RESERVE 300000
+#define TICKSPERCALLBACK 30000
+
+// Indizes fuer Callback-Infos (events, active)
+#define CB_FUN        0
+#define CB_CLOSURE    1
+#define CB_OBJECT     2
+// Indizes fuer Pending
+#define P_EID         0
+#define P_TRIGOB      1
+#define P_TRIGOBNAME  2
+#define P_ARGS        3
+#define P_TIME        4
+
+// Indizes fuer Active
+#define A_EID         0
+#define A_TRIGOB      1
+#define A_TRIGOBNAME  2
+#define A_ARGS        3
+#define A_LISTENERS   4
+#define A_TIME        5
+
+/* alle events, die er kennt.
+   Datenstruktur events:
+   ([id:([obname:fun;closure;object, ... ]),
+     id2: ([....]),
+   ]) */
+mapping events=([]);
+/* abzuarbeitende Events, Datenstruktur:
+   ({ ({id, trigob, trigobname, args}), 
+      ({id2, trigob, trigobname, args}), ... })  */
+nosave mixed pending=({});
+/* Der gerade aktive Event, der wird gerade abgearbeitet.
+   Datenstruktur active:
+   ({ id:trigob;trigobname;args;([obname:fun;closure;object, ...]), trigtime})
+*/
+nosave mixed active=({});
+
+int lastboot; // Zeitstempel des letzten Reboots
+// Flag, wenn gesetzt, zerstoert sich der Eventd, wenn keine Events
+// abzuarbeiten sind.
+nosave int updateme; 
+
+// einfache Statistik, gespeichert wird ein Histogramm. KEys sind die
+// Zeitdifferenzen zwschen Eintragen und Erledigen des Events, Values die
+// Haeufigkeiten.
+nosave mapping stats=([]);
+
+
+protected void process_events();
+protected void save_me();
+varargs int remove(int silent);
+
+// ist der Event-Typ "eid" schon bekannt, d.h. gib es min. 1 Listener?
+// Liefert Anzahl der Listener zurueck.
+int CheckEventID(string eid) {
+    if (!stringp(eid) || !member(events,eid))
+        return 0;
+    return(sizeof(events[eid]));
+}
+
+// entscheidet, ob ein Objekt fuer einen Event eingetragen werden darf. Zum
+// Ueberschreiben in geerbten Dispatchern, in diesem Scheduler sind alle
+// Events oeffentlich. Bekommt die Event-ID, das einzutragende Objekt und das
+// eintragende Objekt uebergeben.
+// 1 fuer Erlaubnis, 0 sonst.
+protected int allowed(string eid, object ob, object po) {
+    return(1);
+}
+
+// entscheidet, ob ein Objekt einen bestimmten Event triggern darf. Zum
+// Ueberschreiben in geerbten Dispatchern, in diesem Scheduler sind alle
+// Events oeffentlich. Bekommt die Event-ID und das triggernde Objekt
+// uebergeben.
+// 1 fuer Erlaubnis, 0 sonst.
+protected int allowedtrigger(string eid, object trigger) {
+    return(1);
+}
+
+// registriert sich fuer einen Event. Wenn es den bisher nicht gibt, wird er
+// implizit erzeugt. Wenn das Objekt ob schon angemeldet war, wird der
+// bisherige Eintrag ueberschrieben.
+// 1 bei Erfolg, <=0 bei Misserfolg
+int RegisterEvent(string eid, string fun, object ob) {
+    object po;
+    if (!stringp(eid) || !stringp(fun) || 
+        !objectp(ob) || !objectp(po=previous_object()))
+        return -1;
+    if (!allowed(eid, ob, po)) return -2;
+    closure cl=symbol_function(fun,ob);
+    if (!closurep(cl))
+        return -3;
+    if (!mappingp(events[eid]))
+        events[eid]=m_allocate(1,3); // 3 Werte pro Key
+    events[eid]+=([object_name(ob):fun;cl;ob]);
+//    if (find_call_out(#'save_me)==-1)
+//        call_out(#'save_me,15);
+    LOGREGISTER(ob,fun,eid,po);
+    return 1;
+}
+
+// entfernt das Objekt als Listener aus dem Event eid
+// Mehr oder weniger optional, wenn ein event verarbeitet wird und das Objekt
+// ist nicht mehr auffindbar, wird es ebenfalls geloescht. Bei selten
+// getriggerten Events muellt es aber bis dahin den Speicher voll.
+// +1 fuer Erfolg, <= 0 fuer Misserfolg
+int UnregisterEvent(string eid, object ob) {
+    object po;
+    if (!stringp(eid) || !objectp(ob) || 
+        !objectp(po=previous_object()) || !mappingp(events[eid]))
+        return -1;
+    string oname=object_name(ob);
+    if (!member(events[eid],oname)) 
+        return -2;
+    m_delete(events[eid],oname);
+    if (!sizeof(events[eid]))
+        m_delete(events,eid);
+    // aus aktivem Event austragen, falls es drin sein sollte.
+    if (sizeof(active) && active[A_EID] == eid)
+    {
+      m_delete(active[A_LISTENERS], object_name(ob));
+    }
+//    if (find_call_out(#'save_me)==-1)
+//      call_out(#'save_me,15);
+    LOGUNREGISTER(ob, eid, po);
+    return 1;
+}
+
+// Loest einen Event aus.
+// 1 fuer Erfolg, <= 0 fuer Misserfolg
+varargs int TriggerEvent(string eid, mixed args) {
+    object trigger;
+    if (!stringp(eid) || 
+        !objectp(trigger=previous_object())) 
+        return -1;
+    if (!allowedtrigger(eid, trigger)) return -2;
+    if (!member(events,eid)) return -3;
+    if (sizeof(pending) > __MAX_ARRAY_SIZE__/5)
+        return -4;
+    pending+=({ ({eid,trigger,object_name(trigger), args, time()}) });
+    if (find_call_out(#'process_events) == -1)
+      call_out(#'process_events,0);
+    LOGEVENT(eid,trigger,args);
+    //DEBUG(sprintf("%O",pending));
+    return 1;
+}
+
+protected void process_events() {
+  // erstmal wieder nen call_out eintragen.
+  call_out(#'process_events, 1);
+
+  // solange ueber active und pending laufen, bis keine Ticks mehr da sind,
+  // bzw. in der Schleife abgebrochen wird, weil keine Events mehr da sind.
+  while(get_eval_cost() > TICK_RESERVE) {
+    // HB abschalten, wenn nix zu tun ist.
+    if (!sizeof(active)) {
+      if (!sizeof(pending)) {
+        remove_call_out(#'process_events);
+        break;
+      }
+      // scheint noch min. ein Eintrag in pending zu sein, nach active kopieren,
+      // plus die Callback-Infos aus events
+      active=({pending[0][P_EID],
+               pending[0][P_TRIGOB],pending[0][P_TRIGOBNAME],
+               pending[0][P_ARGS],
+               deep_copy(events[pending[0][P_EID]]),
+               pending[0][P_TIME] });
+
+      if (sizeof(pending)>1)
+        pending=pending[1..]; // und aus pending erstmal loeschen. ;-)
+      else
+        pending=({});
+      //DEBUG(sprintf("Pending: %O",pending));
+      //DEBUG(sprintf("Active: %O",active));
+    }
+    // jetzte den aktiven Event abarbeiten.
+    // Infos aus active holen...
+    string eid=active[A_EID];
+    object trigob=active[A_TRIGOB];
+    string trigobname=active[A_TRIGOBNAME];
+    mixed args=active[A_ARGS];
+    mapping listeners=active[A_LISTENERS];
+    // und ueber alle Listener iterieren
+    foreach(string obname, string fun, closure cl, object listener: 
+                 listeners) {
+      // erst pruefen, ob noch genug Ticks da sind. wenn nicht, gehts im
+      // naechsten Zyklus weiter.
+      if (get_eval_cost() < TICK_RESERVE) {
+        return;
+      }
+      // wenn Closure und/oder zugehoeriges Objekt nicht existieren, versuchen
+      // wir erstmal, es wiederzufinden. ;-)
+      if (!objectp(query_closure_object(cl))) {
+        if (objectp(listener=find_object(obname)) &&
+            closurep(cl=symbol_function(fun,listener))) {
+            //geklappt, auch in events wieder ergaenzen
+            events[eid][obname,CB_CLOSURE]=cl;
+            events[eid][obname,CB_OBJECT]=listener;
+        }
+        else {
+          // Objekt nicht gefunden oder Closure nicht erzeugbar, austragen
+          m_delete(listeners,obname);
+          // und aus events austragen.
+          m_delete(events[eid],obname);
+          // und naechster Durchgang
+          continue;
+        }
+      }
+      // Objekt noch da, Closure wird als ausfuehrbar betrachtet. 
+      catch(limited(#'funcall,({TICKSPERCALLBACK}),cl,eid,trigob,args);publish);
+      // fertig mit diesem Objekt.
+      m_delete(listeners,obname);
+    }
+    // Statistik? Differenzen zwische Erledigungszeit und Eintragszeit bilden
+    // die Keys, die Values werden einfach hochgezaehlt.
+#ifdef STATISTICS
+    stats[time()-active[A_TIME]]++;
+#endif // STATISTICS
+    // ok, fertig mit active.
+    active=({});
+    //DEBUG(sprintf("Fertig: %O %O",eid, trigobname));
+    LOGEVENTFINISH(eid,trigobname);
+  }  // while(get_eval_cost() > TICK_RESERVE)
+
+  // Soll dies Ding neugeladen werden? Wenn ja, Selbstzerstoerung, wenn keine
+  // Events mehr da sind.
+  if (updateme && !sizeof(active) && !sizeof(pending)) {
+    DEBUG(sprintf("Update requested\n"));
+    remove(1);
+  }
+}
+
+protected void create() {
+    seteuid(getuid(ME));
+    restore_object(STORE);
+    if (lastboot != __BOOT_TIME__) {
+      // Oh. Reboot war... Alle Events wegschmeissen (es koennten zwar die
+      // Eventanmeldungen von BPs ueberleben, aber auch die sollen sich lieber
+      // im create() anmelden.)
+      events=([]);
+      lastboot=__BOOT_TIME__;;
+    }
+    LOG("Event-Dispatcher loaded");
+}
+
+
+protected void save_me() {
+  save_object(STORE);
+}
+
+varargs int remove(int silent) {
+    save_me();
+    DEBUG(sprintf("remove() called by %O - destructing\n", previous_object())); 
+    LOG(sprintf("remove called by %O - destructing",previous_object()));
+    destruct(ME);
+    return 1;
+}
+
+public void reset() {
+  if (updateme && !sizeof(active) && !sizeof(pending)) {
+    DEBUG(sprintf("Update requested\n"));
+    remove(1);
+  }
+}
+
+
+// fuer Debugzwecke. Interface und Verhalten kann sich jederzeit ohne
+// Vorankuendigung aendern.
+mapping QueryEvents() {
+    return(deep_copy(events));
+}
+
+mixed QueryPending() {
+    return(deep_copy(pending));
+}
+
+mixed QueryActive() {
+    return(deep_copy(active));
+}
+
+mapping QueryStats() {
+    return(copy(stats));
+}
+
+int UpdateMe(int flag) {
+    updateme=flag;
+    if (find_call_out(#'process_events)==-1)
+      reset();
+    return flag;
+}
+
diff --git a/p/daemon/finger.c b/p/daemon/finger.c
new file mode 100644
index 0000000..d4b602a
--- /dev/null
+++ b/p/daemon/finger.c
@@ -0,0 +1,443 @@
+/* a finger
+ * Original (c) 14.03.1993 by Taube @Nightfall
+ * Umsetzung fuer Morgengrauen 09.08.1993 by Loco
+
+   Verwendung ausserhalb von Morgengrauen ist gestattet unter folgenden
+   Bedingungen:
+   - Benutzung erfolgt auf eigene Gefahr. Jegliche Verantwortung wird
+     abgelehnt.
+   - Auch in veraenderten oder abgeleiteten Objekten muss ein Hinweis auf
+     die Herkunft erhalten bleiben.
+   Ein Update-Service besteht nicht.
+
+ * 29.Okt 1993 Seherversion.
+ * spaeter auch fuer externen Aufruf verwendbar gemacht
+ * 13.Okt .plan und .project koennen auch in ~loco/plans sein.
+ * 15.Jan 1994 angepasst an neues Speicherformat
+ * 02-05.Dez94 -n, -p
+ *
+ * Gelegentlich minor changes, zuletzt 04.Okt.95
+ */
+
+#pragma strong_types,save_types
+#pragma no_clone, no_shadow
+
+#include <config.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <new_skills.h>
+#include <userinfo.h>
+#include <config.h>
+
+#define HBINT    2   /* interval between two heart_beats in seconds */
+#define MINIDLE 60   /* minimum idle time in seconds to be stated idle */
+#define TBANISH_EXTRACT 71..  /* Der benoetigte Teil des TBanish-strings */
+
+#define TP this_player()
+#define wiz (local && IS_LEARNER(TP))
+#define seer (local && IS_SEER(TP))
+#define IN ((properties[P_GENDER]==2)?"in":"")
+
+
+#define FLAG_NOPLAN 1
+#define FLAG_LONG 2
+#define FLAG_SPONSOR 4
+#define FLAG_VIS_LOGOUT 8
+#define FLAG_AVATAR 16
+
+
+mapping properties;
+int age,invis,hc_play;
+int filetime;
+mapping relatives;
+
+
+void create()
+{
+  seteuid(getuid());
+  filetime=0;
+}
+
+
+string timediff(int time)
+{
+  string ret;
+
+  ret="";
+  if ( time >= 86400*365 ) {
+    ret+=time/(86400*365)+"a ";
+    time%=(86400*365);
+  }
+  if(time>=86400) {
+    ret+=time/86400+"d ";
+    time%=86400;
+  }
+  if(time<36000) ret+="0";
+  ret+=time/3600+":";
+  time%=3600;
+  if(time<600) ret+="0";
+  ret+=time/60+":";
+  time%=60;
+  if(time<10) ret+="0";
+  ret+=time+"";
+  return ret;
+}
+
+string sponsoring(string name)
+{
+  int i,w;
+  string *s,s2,s3,s4;
+  // Daten einlesen, wenn die daten aelter als 1 Tag sind oder sich
+  // /log/SPONSORS geaendert hat.
+  if ((time() > filetime+86400) ||
+      filetime!=file_time("/log/SPONSOR")) 
+  {
+      relatives=m_allocate(0,2);
+      filetime=file_time("/log/SPONSOR");
+      s=explode(read_file("/log/SPONSOR"),"\n");
+      foreach(string str: s) { 
+	  sscanf(str,"%s: %s macht %s zum Learner.",s2,s3,s4);
+	  if (IS_LEARNER(lower_case(s3)) && IS_LEARNER(lower_case(s4)))
+          {
+             relatives[lower_case(s4),0]=s3;
+             s3=lower_case(s3);
+             s4+=" ("+query_wiz_level(lower_case(s4))+")";
+             if (!relatives[s3,1]) relatives[s3,1]=({s4});
+             else relatives[s3,1]+=({s4});
+          }
+      }  
+  }
+  s2="";
+  if (relatives[name,0])
+    s2+="Vorfahre:     "+relatives[name,0]+"\n";
+  if (relatives[name,1])
+    s2+="Nachfahre(n): "+break_string(implode(relatives[name,1],", "),78,14)[14..];
+  return s2;
+}
+
+varargs string finger_single(string str,int local)
+{
+  mixed  *userinfo;
+  string ip,text,ipnum,filename,away;
+  int wizlevel,playerlevel,idle,pos,flags,last;
+  mixed h,data,tmp;
+  object player,ob;
+
+  /*DEBUG###  tell_object((find_player("loco")||this_object()),"Finger request: '"+str+"'("+local+")\n");/**/
+  str=lower_case(str);
+  text="";
+  away="";
+  hc_play=0;
+  
+  h=explode(str," ");
+  if (sizeof(h)==1) h=explode(str,"+");
+  if (member(h,"-n")>=0) flags=FLAG_NOPLAN;
+  if (member(h,"-p")>=0) flags=0;
+  if (member(h,"-l")>=0) flags|=FLAG_LONG;
+  if (member(h,"-s")>=0) flags|=FLAG_SPONSOR;
+  if (member(h,"-v")>=0) flags|=FLAG_VIS_LOGOUT;
+  if (member(h,"-a")>=0) flags|=FLAG_AVATAR;
+
+  h=(h-({"-n","-p","-l","-s", "-v","-a"}));
+  if (!sizeof(h)) {
+    text="Du solltest schon sagen, wer Dich interessiert!\n";
+    if (local) return write(text),0;
+    else return text;
+  }
+  str=h[0];
+  if (!sizeof(str) || str[0]<'a' || str[0]>'z') {
+    text="Also, so ("+str+") heisst bestimmt kein Spieler hier!\n";
+    if (local) return write(text),0;
+    else return text;
+  }
+
+  /* does this player exist? */
+  str=old_explode(str,".")[0];
+  userinfo=MASTER->get_userinfo(str);
+  player=find_player(str)||find_netdead(str);
+
+  if( (!pointerp(userinfo) || userinfo[USER_LEVEL+1]==-1)
+	&& !player) {
+    text="Hmm... diesen Namen gibt es im "MUDNAME" nicht.\n";
+    if (tmp="/secure/master"->QueryBanished(str)){
+      text="Hoppla - dieser Name ist reserviert oder gesperrt (\"gebanisht\")!\n";
+      if ( tmp != "Dieser Name ist gesperrt." )
+          text += "Grund fuer die Sperrung: " + tmp +
+              (member( ({'!','?','.'}), tmp[<1] ) != -1 ? "" : ".") + "\n";
+    }
+    if (local) return write(text),0;
+    else return text;
+  }
+
+  if (player) {
+    hc_play=player->query_hc_play();
+    properties=player->QueryProperties();
+    properties[P_RACE]=player->QueryProp(P_RACE);
+    properties[P_VISIBLE_GUILD]=player->QueryProp(P_VISIBLE_GUILD);
+    properties[P_TITLE]=player->QueryProp(P_TITLE);
+    tmp = player->QueryProp(P_PRESAY);
+    properties[P_PRESAY]=(stringp(tmp) && sizeof(tmp)>1) ? tmp[0..<2] : 0;
+  }
+  else
+    restore_object("/save/"+str[0..0]+"/"+str);
+  if (!properties)
+  {
+    text+="Mist!!! Das Einlesen der Daten klappt nicht wie es soll :-(\n";
+    properties=0;
+    if (!local)
+      return text;
+    write(text);
+    return "";
+  }
+  if ( player && interactive(player) )
+      ipnum = query_ip_number(player);
+  else
+      ipnum = properties[P_CALLED_FROM_IP];
+  // frueher stand in P_CALLED_FROM_IP evtl. auch der Name direkt drin
+  // anstelle der numerischen IP
+  ip=query_ip_name(ipnum)||properties[P_CALLED_FROM_IP];
+  if(player) {
+      if (!interactive(player) || (idle=query_idle(player))<MINIDLE)
+          idle=0;
+      if (!(invis=age=player->QueryProp(P_INVIS)))
+          age=player->QueryProp(P_AGE);
+  } else {
+    if (properties[P_INVIS]) age=properties[P_INVIS];
+    idle=properties[P_LAST_LOGOUT];
+  }
+
+   wizlevel=query_wiz_level(str);
+   if ( (tmp = file_time("/save/"+str[0..0]+"/"+str+".o")) <= 0 )
+       // Hack, um bei ganz "frischen" Spielern (noch kein Savefile vorhanden)
+       // die Ausgabe von 1.1.1970 zu verhindern
+       tmp = time();
+   last=properties[P_LAST_LOGOUT];
+   if ( last <= 0 || (!(flags&FLAG_VIS_LOGOUT) && wiz && wizlevel > 10
+                  && properties[P_INVIS] && tmp - last > 300) )
+       last = tmp;
+
+  /* output routine for all */
+  if(player) {
+    h=player->QueryProp(P_RACE);
+    if(interactive(player) && (wiz || !invis)) {
+      text+=capitalize(str)+" ist anwesend,\n"+
+        "und zwar von: "+
+          (wiz ? (ip+(ipnum?" ("+ipnum+")":"")):"")+
+          (stringp(properties[P_LOCATION]) ? (wiz ? "\n              [" : "")
+           +capitalize(properties[P_LOCATION])+
+           ((properties[P_LOCATION] != country(ip, ipnum))
+            ? " (ueber "+country(ip, ipnum)+")" : "" )
+           : (wiz?" [":"")+country(ip, ipnum))+(wiz ? "]":"")+".\n";
+      if(idle)
+        text+="Passiv seit: "+timediff(idle)+"\n";
+     if (local)
+      text+="Eingeloggt seit: "+dtime(properties[P_LAST_LOGIN])+"\n";
+     if (properties[P_AWAY])
+       away="z.Z. abwesend, Grund  : "+properties[P_AWAY]+"\n";
+    } else
+      text+=capitalize(str)+" ist nicht anwesend.\nZuletzt eingeloggt von: "+
+        (wiz ? (ip+(ipnum&&ipnum!=ip?" ("+ipnum+")":"")):"")+
+        (stringp(properties[P_LOCATION]) ?
+         (wiz ? "\n                        [": "")
+         +capitalize(properties[P_LOCATION])+" (ueber "+country(ip, ipnum)+")":
+         (wiz?" [":"")+country(ip, ipnum))+(wiz ? "]":"")+".\n"+
+        "Zuletzt ausgeloggt: "+dtime(last)+" ("+timediff(time()-last)+").\n";
+  }
+  else {
+    text+=capitalize(str)+" ist nicht anwesend.\nZuletzt eingeloggt von: "+
+        (wiz ? (ip+(ipnum&&ipnum!=ip?" ("+ipnum+")":"")):"")+
+        (stringp(properties[P_LOCATION]) ?
+         (wiz ? "\n                        [": "")
+         +capitalize(properties[P_LOCATION])+" (ueber "+country(ip, ipnum)+")":
+         (wiz?" [":"")+country(ip, ipnum))+(wiz ? "]":"")+".\n"+
+        "Zuletzt ausgeloggt: "+dtime(last)+" ("+timediff(time()-last)+").\n";
+  }
+  text+="Voller Name: "+(((h=properties[P_PRESAY]) && h!="") ? h+" " : "")+
+    capitalize((h=properties[P_NAME]) ? h : str)+" "+
+     ((h=properties[P_TITLE]) ? h :
+    ((mappingp(h=properties["guild_title"]) && (h=(h[properties[P_GUILD]?properties[P_GUILD]:"abenteurer"])) ) ? h : "") )
+       +"\n";
+
+  if (properties[P_GHOST]) text+="Hoppla, ein Geist!\n";
+  if ((flags&FLAG_LONG)&&properties[P_LONG])
+    text+="Beschreibung: \n"+break_string(properties[P_LONG],78,2);
+
+  if(wiz ||
+     (properties[P_SHOWEMAIL]=="alle") ||
+     ( (properties[P_SHOWEMAIL]=="freunde") &&
+       objectp(player) &&
+       this_player() &&
+       (ob=present("\n\bfband",player)) &&
+       pointerp(tmp=ob->QueryProp(P_AUTOLOADOBJ)) &&
+       pointerp(tmp=tmp[1]) &&
+       (member(tmp,getuid(this_player()))!=-1))) tmp = 1;
+  else tmp=0;
+
+
+  if (!(h=properties[P_RACE]) && userinfo && pointerp(userinfo) && sizeof(userinfo)>4 &&
+      stringp(userinfo[4]) && sizeof(h=old_explode(userinfo[4],"/"))>2) {
+    h=capitalize(h[2]);
+    h=(["Human":"Mensch","Dwarf":"Zwerg","Darkelf":"Dunkelelf","Orc":"Ork"])[h] || h;
+  }
+
+  if (!stringp(h)) h="<keine>";
+
+  text+="Rasse: "+h+",  Gilde: "+
+                     ((h=properties[P_VISIBLE_GUILD])?capitalize(h):((h=properties[P_GUILD])?capitalize(h):"Abenteurer"))+
+                     ((h=properties[P_VISIBLE_SUBGUILD_TITLE])?" ("+capitalize(h)+")":((h=properties[P_SUBGUILD_TITLE])?" ("+capitalize(h)+")":""))+
+                     ",  Geschlecht: "+({"neutral ?!","maennlich","weiblich","<verdammt seltsam>"})[properties[P_GENDER]]+"\n"+
+                      (seer ? "Alter: "+timediff(age*HBINT)+",   " : "")+
+                      (wizlevel>=10?"Magierlevel: "+wizlevel+
+          (wizlevel>=GOD_LVL?" (Mudgott)":str=="boing"?" (Mudgott a.D.)":str=="muadib"?" (Apostolischer Visitator)":wizlevel>=ARCH_LVL?" (Erzmagier)":IS_DEPUTY(str)?" (Hilfssheriff)":wizlevel>=ELDER_LVL?" (Weiser)":wizlevel>=LORD_LVL?" (Regionsmagier)":wizlevel>=SPECIAL_LVL?" (Hilfsmagier)":wizlevel>=DOMAINMEMBER_LVL?" (Regionsmitarbeiter)":wizlevel>WIZARD_LVL?" (Vollmagier)":" (Lehrling)"):
+                       ("Spielerlevel: "+properties[P_LEVEL]+( wizlevel ? " (Seher"+IN+")" : "" )+
+                        (((h=properties[P_GUILD_LEVEL]) && h=h[properties[P_GUILD]]) ?
+                         (",   Gildenlevel: "+h) : "" )
+                        ))+((userinfo&&pointerp(userinfo))?
+                        (sprintf("\nDatum des ersten Login: %s",
+                                 (userinfo[5] > 0) ? dtime(userinfo[5]) : "vor dem 10. Nov 1995")):"")+
+                         (tmp ? ("\nE-Mail-Adresse: "+((h=properties[P_MAILADDR]) ? h : "keine")+"\n") : "\n");
+
+  if (properties[P_HOMEPAGE])
+    text+="Homepage: "+properties[P_HOMEPAGE]+"\n";
+
+  if (stringp(data=properties[P_MESSENGER])) {
+    text+=sprintf("Messenger: %s", data);
+    if (intp(data=properties[P_ICQ])) {
+      if (data<0 && IS_WIZARD(this_player())) data*=-1;
+      if (data>0) text += sprintf(", ICQ: %d", data);
+    }
+    text+="\n";
+  } else 
+    if (intp(data=properties[P_ICQ])) {
+      if (data<0 && IS_WIZARD(this_player()))
+        data*=-1;
+      if (data>0)
+        text+=sprintf("ICQ: %d\n",data);
+    }
+
+  if (properties[P_MARRIED])
+    text+="Verheiratet mit: "+capitalize(properties[P_MARRIED])+"\n";
+
+  if ( pointerp(properties[P_SIBLINGS]) )
+      text += ({ "Es", "Er", "Sie" })[properties[P_GENDER]] + " ist Bluts" +
+          ({ "verwandt mit ", "bruder von ", "schwester von " })
+          [properties[P_GENDER]] +
+          CountUp(properties[P_SIBLINGS]) + ".\n";
+
+  text+=away;
+  
+  if(MASTER->check_late_player(str))
+  {
+    text+=capitalize(str)+" hat uns leider fuer immer verlassen.\n";
+  }
+  else
+  {
+    if (h=MASTER->QueryTBanished(str))
+      text+=capitalize(str)+" will fruehestens "+h[TBANISH_EXTRACT];
+  }
+  
+  if (h=properties[P_TESTPLAYER])
+  {
+    text+=capitalize(str)+" ist Testspieler"+IN;
+    if (stringp(h)) text+=" ("+h+")";
+    text+=".\n";
+  }
+  if ( h=properties[P_SECOND])
+  {
+    if (IS_WIZARD(this_player())) {
+       text+=capitalize(str)+" ist";
+       switch((int)properties[P_SECOND_MARK]) {
+         case -1: text+=" unsichtbar markierte"
+                       +((int)properties[P_GENDER]!=FEMALE ? "r": "");
+                  break;
+         case  0: text+=" nicht namentlich markierte"
+                       +((int)properties[P_GENDER]!=FEMALE ? "r": "");
+                  break;
+         default:
+       }
+       text+=" Zweitspieler"+IN;
+       if (stringp(h))
+         text+=" ("+capitalize(h)+")";
+       text+=".\n";
+    }
+    else if ((int)properties[P_SECOND_MARK]>0)
+    {
+      text+=capitalize(str)+" ist Zweitspieler"+IN;
+      if (stringp(h))
+        text+=" ("+capitalize(h)+")";
+      text+=".\n";
+    }
+    else if ((int)properties[P_SECOND_MARK]>-1)
+      text+=capitalize(str)+" ist Zweitspieler"+IN+".\n";
+  }
+  if (properties[P_DEADS])
+  {
+    text+="Bisher bereits "+properties[P_DEADS]+" mal gestorben\n";
+    // Bezieht sich nur auf den Zeitraum ab dem 30.11.'94
+  }
+  if(hc_play==1)
+  {
+    text+=capitalize(str)+" ist sehr mutig.\n";
+  }
+  if(hc_play>1)
+  {
+    text+=capitalize(str)+" ist am "+dtime(hc_play)+" in das Nirvana eingegangen.\n";
+  }
+
+  if (/*wiz && */userinfo) {
+    data=userinfo[3];
+    if (sizeof(data))
+      text+="Regionsmagier"+IN+" von     : "+implode(map(data,#'capitalize),", ")+".\n";
+    data="/secure/master"->get_domain_homes(str);
+data=filter(data-({"erzmagier"}),#'stringp); 
+    if ((wizlevel>=DOMAINMEMBER_LVL) && (sizeof(data)))
+      text+="Regionsmitarbeiter"+IN+" von: "+implode(map(data,#'capitalize),", ")+".\n";  /* #' */ 
+    }
+
+  if (userinfo) {
+    data=userinfo[8];
+    if (sizeof(data))
+      text += "Gildenmagier"+IN+" von      : "+implode(map(data, #'capitalize), ", ")+".\n";   /* #' */
+  }
+
+  // ggf. Avatar-URI mit ausgeben.
+  if (flags & FLAG_AVATAR
+      && stringp(properties[P_AVATAR_URI]))
+    text += "Avatar-URI: " + properties[P_AVATAR_URI] + "\n";
+
+  if (flags & FLAG_SPONSOR)
+    text+=sponsoring(str);
+
+  filename="/players/"+str+"/.project";
+  if(file_size(filename)>=0)
+    text+="Projekt: "+explode(read_file(filename), "\n")[0]+"\n";
+  else {
+    filename="/p/service/loco/plans/"+str+".project";
+    if(file_size(filename)>=0)
+      text+="Projekt: "+explode(read_file(filename), "\n")[0]+"\n";
+  }
+  if (seer && !(flags&FLAG_NOPLAN)) {
+    filename="/players/"+str+"/.plan";
+    if(file_size(filename)>=0)
+      text+="Plan:\n"+read_file(filename);
+    else {
+      filename="/p/service/loco/plans/"+str+".plan";
+      if(file_size(filename)>=0)
+        text+="Plan:\n"+read_file(filename);
+//      else
+//      text+="Keine .plan-Datei.\n";
+    }
+  }
+  properties=0;
+  if (!local)
+    return text;
+  this_player()->More(text);
+  return "";
+}
+
+string Finger(string str)
+{
+  if(!str || str=="") { notify_fail("Wen denn?\n"); return 0; }
+  else
+    return finger_single(str,1);
+}
diff --git a/p/daemon/fishmaster.c b/p/daemon/fishmaster.c
new file mode 100644
index 0000000..3997584
--- /dev/null
+++ b/p/daemon/fishmaster.c
@@ -0,0 +1,165 @@
+/*Fisch-Master, (c) Vanion@MG, 26.05.02
+
+  Fische die im MG geangelt werden koennen, sollen nicht als endlose
+  Geldquelle dienen. Daher habe ich einen Master gebaut, der sich merkt
+  Welcher Spieler wieviel Fisch geangelt hat. Je mehr Fisch der Spieler
+  faengt, desto billiger wird der Fisch.
+
+  Schnittstelle:
+
+  Der Master hat abgesehen von Standard-Funktionen nur eine Schnittstelle
+
+  public int PriceOfFish(object player, object fish)
+
+  Der erste Parameter ist der Spieler, der den Fisch gecloned hat, der
+  zweite ist der Fisch, den der Spieler gefangen hat. Der Fisch muss
+  hierbei von Fraggles Standardfisch erben.
+
+  Der Preis des Fisches faellt linear je naeher der Spieler an die
+  Maximal-Grenze fuer Fisch kommt (Diese ist durch die Konstante MAX_FISH
+  festgelegt.) Der Master merkt sich ueber einen Zeitraum von FORGET_IT
+  Sekunden, dass der Spieler diesen Fisch gefangen hat. Ist der Zeitraum
+  vergangen, geht der Fisch nicht mehr in die Rechnung ein.
+ */
+
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow, no_inherit
+
+#define SAVE_FILE __DIR__+"save/fishes"
+
+#define FORGET_IT 1728000 // Nach 20 Tagen
+#define MAX_FISH  20000   // Wenn Spieler Max-Fish geholt hat, hat der
+                          // Fisch keinen Wert mehr. (Einheit Gramm)
+
+#include <properties.h>
+
+// Hier wird gespeichert, welcher Spieler wieviel Fisch gefangen hat.
+mapping fishes = m_allocate(0,1);
+static int save_me_soon;
+
+protected void create() {
+  // Ohne das darf dieses Objekt hier nicht schreiben.
+  seteuid(getuid());
+  // Globale Variablen einlesen/initialisieren.
+  restore_object(SAVE_FILE);
+  set_next_reset(4*3600);
+}
+
+static void CleanupData() {
+  int expire_date = time()-FORGET_IT;
+  // Ueber alle Spielernamen laufen.
+  foreach(string pl, mixed *fishlist: fishes) {
+    // Ueber alle Fischdaten aller Spieler laufen.
+    foreach(int *fish: fishlist) {
+      if (fish[0]<expire_date) {
+        fishlist-=({fish});
+        save_me_soon=1;
+      }
+    }
+    if ( !sizeof(fishlist) )
+      fishes = m_delete(fishes, pl);
+    else
+      fishes += ([pl:fishlist]);
+  }
+}
+
+//ZZ: man koennte hier ggf. das neue Gewicht bestimmen statt es bei jedem
+//ZZ:Abruf zu tun.
+// Fuegt einen neuen Fisch in die Liste mit Fischen ein
+static int AddFish(string player_id, int weight) {
+  // Fisch hinzufuegen
+  if ( member(fishes, player_id) )
+    fishes[player_id] +=  ({({ time(),weight})});
+  else 
+    fishes += ([ player_id: ({ ({time(),weight}) }) ]);
+  return 1;
+}
+
+// Hier wird das Gewicht des bereits gefangenen Fisches umgewandelt
+// in den Preis-Prozentsatz, den der Fisch noch kostet.
+static int WeightToPrice(int weight) {
+  if (weight >= MAX_FISH) return 0;
+  if (weight <= 0       ) return 100;
+  // Eine lineare Kurve. Je mehr Fisch geholt, desto billiger
+  // wird der. // Arathorn: Spannend, so eine lineare Kurve. :-)
+  return 100-(weight*100/MAX_FISH);
+}
+
+// Gibt das Gesamtgewicht des gefangenen Fisches zum Spieler zurueck
+static int QueryWeight(string player_id) {
+  int weight;
+  mixed list = fishes[player_id];
+
+  // Erstmal werden ungueltige Fische aus der Liste entfernt
+  //ZZ: ich finde, das muss nicht unbedingt bei jeden Abruf getan werden
+  //ZZ::und nicht fuer alle Spieler. reicht nicht einfach alle 4h? 
+  CleanupData();
+
+  // Zusammenzaehlen der Einzelgewichte der gueltigen Fische.
+  foreach(int *fish : fishes[player_id]) {
+    weight += fish[1];
+  }
+  return weight;
+}
+
+// Wenn ein Spieler einen neuen Fisch bekommt, wird der Preis fuer den
+// Fisch bestimmt. Dieser resultiert aus der Menge (Gewicht) des Fisch,
+// die der Spieler in letzter Zeit schon gefangen hat. Zurueckgegeben
+// wird der Preis in Teilen von 100.
+public int PriceOfFish(object player, object fish) {
+  string player_id;
+  int price_of_new_fish;
+
+  // Sind Spieler und Fisch uebergeben?
+  if ( !player || !fish ) return 0;
+
+  // Spieler-ID finden.
+  player_id = getuuid(player);
+
+  // Ist player ein echter Spieler?
+  // NPC clonen sich Fische, die 100% kosten.
+  if (member(users(),player)==-1) return 100;
+
+  // Is der Fisch ein Fisch von Fraggle?
+  if (member(inherit_list(fish), __PATH__(1)+"/fish.c")==-1)
+    return 0;
+
+  // Feststellen wieviel Fisch ein Spieler gefangen hat, und
+  // daraus den Preis berechnen.
+  price_of_new_fish = WeightToPrice(QueryWeight(player_id));
+
+  // Diesen Fisch in die Datenbank eintragen
+  AddFish(player_id, fish->QueryProp(P_WEIGHT));
+
+  // Ich bin neugierig.
+  /*DEBUG(player->Name()+" hat einen Fisch mit "+fish->QueryProp(P_WEIGHT)+
+        " g gefangen. Der Wert liegt bei "+price_of_new_fish+"%");*/
+
+  // Die Aenderung soll persistent sein.
+  save_me_soon=1;
+
+  // Rueckgabe des Fischpreises.
+  return price_of_new_fish;
+}
+
+// Speichern der globalen Variablen
+static void save() {
+  save_object(SAVE_FILE);
+  return;
+}
+
+// Da nichts geerbt wird, muss das destruct() selbst gemacht werden
+// Wir speichern auf jeden Fall.
+varargs int remove(int silent) {
+  save();
+  destruct(this_object());
+  return 1;
+}
+
+// Daten aufraeumen und ggf. wegspeichern
+void reset() {
+  CleanupData();
+  if ( save_me_soon )
+    save();
+  set_next_reset(4*3600); // alle 4 Stunden reicht.
+}
diff --git a/p/daemon/iplookup.c b/p/daemon/iplookup.c
new file mode 100644
index 0000000..7608d8f
--- /dev/null
+++ b/p/daemon/iplookup.c
@@ -0,0 +1,225 @@
+/*
+ * Mudlibseite des iplookup systems, mit dem aus numerischen IP Adressen
+ * der Hostname und der Ort, an dem sich der Rechner befinden berechnet wird.
+ * Ergebnisse werden gecachet.
+ *
+ * (c)2010 rumata @ morgengrauen
+ */
+
+#pragma strict_types,save_types
+#pragma no_clone,no_shadow
+
+// Format der ipmap:
+// Key: numerische ip als string (ohne fuehrende nullen)
+// Data:
+//  [ip,0] Hostname, der vom externen Server geliefert wurde.
+//  [ip,1] Locationinformation, die vom externen Server kommt.
+//    dieser sollte den Ort, oder falls es nicht besser geht, das Land
+//    im Klartext liefern.
+//  [ip,2] Zeitstempel, bei dessen erreichen der Wert geloescht werden
+//    soll.
+//  [ip,3] Status der Anfrage
+
+private mapping ipmap;
+#define IPMAP_HOST   0
+#define IPMAP_CITY   1
+#define IPMAP_EXPIRE 2
+#define IPMAP_STATUS 3
+
+// externer server, der der lokationdaten berechnet
+#define IPLOOKUP_HOST "127.0.0.1"
+#define IPLOOKUP_PORT 8711
+#define IPLOOKUP_SAVE "/p/daemon/save/iplookup"
+
+// moegliche statuswerte (platz zum zaehlen der anfragen gelassen)
+#define STATUS_QUERYING      1
+#define STATUS_AUTHORITATIVE 128
+
+// so lange werden die daten im cache gehalten
+#define CACHE_TIME    86400  /* 1 day */
+
+// Ab dieser Groesse betrache ich den cache als "gross genug",
+// um einen cache update in etappen zu rechtfertigen.
+#define LARGE_CACHE_LIMIT 2000
+
+// so lange sind wir bereit auf die antwort einer anfrage beim server zu warten
+#define QUERY_TIME    60     /* 1 min */
+
+#include <config.h>
+#if MUDNAME == "MorgenGrauen"
+#define LOG(x) log_file("rumata/iplookup.log",strftime("%Y-%m-%d %H:%M:%S: ")+(x)+"\n")
+#else
+#define LOG(x)
+#endif
+
+// PROTOTYPES
+
+public void create();
+public void reset();
+static int active_entry( string key, mixed* data );
+static void expire_cache_small();
+static void expire_cache_large( string* keys );
+public void update( string udp_reply );
+
+// IMPLEMENTATION
+
+public void create() {
+	seteuid(getuid());
+	restore_object( IPLOOKUP_SAVE );
+
+	if( !mappingp(ipmap) || widthof(ipmap)!=4 ) {
+		ipmap = m_allocate( 0, 4 );
+		LOG("restore failed, creating new ipmap");
+	} else {
+	  LOG(sprintf("created (%d entries)",sizeof(ipmap)));
+	}
+}
+
+public void reset() {
+	if( sizeof(ipmap) < LARGE_CACHE_LIMIT ) {
+		expire_cache_small();
+	} else {
+		LOG(sprintf("reset %d ->",sizeof(ipmap)));
+		expire_cache_large( m_indices(ipmap) );
+	}
+  set_next_reset(10800);
+}
+
+static int active_entry( string key, mixed* data ) {
+	return time() < data[IPMAP_EXPIRE];
+}
+
+// Den cache auf einfache weise aufraeumen.
+static  void expire_cache_small() {
+	int s = sizeof(ipmap);
+	ipmap = filter( ipmap, #'active_entry );
+	save_object( IPLOOKUP_SAVE );
+	LOG(sprintf("reset (%d -> %d)",s,sizeof(ipmap)));
+}
+
+// Kompliziertere routine, die den cache etappenweise abarbeitet.
+static	void expire_cache_large( string* keys ) {
+	if( !pointerp(keys) ) return;
+	int next=0;
+	foreach( string key: keys ) {
+		if( get_eval_cost() < 100000 ) break;
+		if( ipmap[key,IPMAP_EXPIRE] < time() ) {
+			m_delete( ipmap, key );
+		}
+		next++;
+	}
+	LOG(sprintf("checking %d of %d",next,sizeof(keys)));
+	if( next<sizeof(keys) ) {
+		call_out( #'expire_cache_large, 6, keys[next..] );
+	} else {
+		save_object( IPLOOKUP_SAVE );
+		LOG(sprintf("reset -> %d (done)",sizeof(ipmap)));
+	}
+}
+
+#define SEARCHING "<auf der Suche...>"
+
+/* Erzeugt einen temporaeren Eintrag, der fuer eine Suche steht.
+ * Wenn die Antwort kommt, wird der Eintrag mit den entgueltigen
+ * Daten ueberschrieben.
+ */
+static void make_request( string ipnum ) {
+	send_udp( IPLOOKUP_HOST, IPLOOKUP_PORT, ipnum );
+	ipmap += ([ ipnum : ipnum ; SEARCHING ;
+		time()+QUERY_TIME ; STATUS_QUERYING
+	]);
+}
+
+/* Liefert zu einer gegebenen ipnum den Ort.
+ * diese Funktion wird von simul_efun aufgerufen.
+ * @param ipnum eine numerische ip-adresse oder ein interactive
+ * @return den Ort (oder das Land) in dem sich die ip-adresse
+ * laut externem Server befindet.
+ */
+public string country( mixed ipnum ) {
+	string host,city;
+	int expire,state;
+
+	if( objectp(ipnum) ) {
+		ipnum = query_ip_number(ipnum);
+	}
+	if( !stringp(ipnum) ) {
+		return "<undefined>";
+	}
+	if( !m_contains( &host, &city, &expire, &state, ipmap, ipnum ) ) {
+		make_request( ipnum );
+		return SEARCHING;
+	}
+
+	return city;
+}
+
+/* Liefert zu einer gegebenen ipnum den Hostnamen.
+ * diese Funktion wird von simul_efun aufgerufen.
+ * @param ipnum eine numerische ip-adresse oder ein interactive
+ * @return den Hostnamen der zu der angegebenen ip-adresse gehoert.
+ * wenn der hostname nicht bekannt ist, wird die ipadresse zurueckgegeben.
+ */
+public string host( mixed ipnum ) {
+	string host,city;
+	int expire,state;
+
+	if( objectp(ipnum) ) {
+		ipnum = query_ip_number(ipnum);
+	}
+	if( !stringp(ipnum) ) {
+		return "<undefined>";
+	}
+	if( !m_contains( &host, &city, &expire, &state, ipmap, ipnum ) ) {
+		make_request( ipnum );
+		return ipnum;
+	}
+
+	return host;
+}
+
+/* wird vom master aufgerufen, wenn eine antwort vom externen
+ * iplookup dienst eintrudelt.
+ */
+public void update( string udp_reply ) {
+	string* reply;
+	if( previous_object()!=master() ) return;
+	reply = explode(udp_reply,"\n");
+	if( sizeof(reply)<4 ) return;
+
+	if( reply[3] == "<unknown>" ) reply[3] = reply[1];
+	if( reply[2] == "<unknown>" ) reply[2] = "irgendwoher";
+	ipmap += ([ reply[1] : reply[3] ; reply[2] ; time()+CACHE_TIME ;
+		STATUS_AUTHORITATIVE ]);
+	//save_object( IPLOOKUP_SAVE );
+}
+
+int remove(int silent) {
+  save_object( IPLOOKUP_SAVE );
+  destruct(this_object());
+  return 1;
+}
+
+// DEBUGGING CODE
+
+#if 0
+public void dump( string key, mixed* data ) {
+	printf( "%s: %s (%s) s=%d bis %d\n", key, data[0], data[1],
+		data[3], data[2] );
+}
+
+public void load(string s) {
+	restore_object(s);
+	if( !mappingp(ipmap) || widthof(ipmap)!=4 ) {
+		ipmap = m_allocate( 0, 4 );
+		LOG("restore failed, creating new ipmap");
+	} else {
+	  LOG(sprintf("created (%d entries)",sizeof(ipmap)));
+	}
+}
+
+public void debug() {
+	map( ipmap, #'dump );
+	printf( "= %d Eintraege\n", sizeof(ipmap) );
+}
+#endif
diff --git a/p/daemon/lag-o-daemon.c b/p/daemon/lag-o-daemon.c
new file mode 100644
index 0000000..6d23860
--- /dev/null
+++ b/p/daemon/lag-o-daemon.c
@@ -0,0 +1,272 @@
+#pragma strong_types,save_types,rtt_checks
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma pedantic
+
+#include <driver_info.h>
+
+#ifndef DEBUG
+#define DEBUG(x)        if (find_player("zesstra"))\
+        tell_object(find_player("zesstra"),"LAG: "+x+"\n")
+#endif
+
+#define SAVEFILE __DIR__+"save/lag-o-daemon"
+#define LOG(x) log_file("LAG",x,200000);
+#define LOGMAX(x) log_file("LAGMAX",x,300000);
+
+// Anzahl an zu merkenden Werten (1800==3600s)
+#define MESSWERTE 1800
+
+nosave float hbstat, obstat;
+nosave mapping lastwarning=([]);
+nosave int *lasttime = utime();
+nosave int lasthbcount = absolute_hb_count();
+float *lagdata = allocate(MESSWERTE,0.0);
+// Lags fuer die letzten   2s (1 HB), 20s (10 HB), 60s (30 HB),
+//                       300s (150 HB), 900s (450 HB), 3600s (1800 HB)
+float *lag = allocate(6,0.0);
+int *hbdata = allocate(MESSWERTE);
+// Lags fuer die letzten   2s (1 HB), 20s (10 HB), 60s (30 HB),
+//                       300s (150 HB), 900s (450 HB), 3600s (1800 HB)
+int *hbs = allocate(6);
+int *codata = allocate(MESSWERTE);
+// Lags fuer die letzten   2s (1 HB), 20s (10 HB), 60s (30 HB),
+//                       300s (150 HB), 900s (450 HB), 3600s (1800 HB)
+int *callouts = allocate(6);
+
+void create() {
+  seteuid(getuid());
+  // Bei nicht einlesbaren Savefile und nach Reboot Werte neu initialisieren.
+  // Denn dann ist die Differenz der HBs bedeutungslos. Erkennung ploetzlich
+  // kleinere HB-Zahl.
+  if (!restore_object(SAVEFILE)
+      || efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES) < lasthbcount)
+  {
+    lagdata = allocate(MESSWERTE,0.0);
+    lag = allocate(6,0.0);
+    hbdata = allocate(MESSWERTE);
+    hbs = allocate(6);
+    codata = allocate(MESSWERTE);
+    callouts = allocate(6);
+  }
+  set_heart_beat(1);
+}
+
+int remove(int silent) {
+  save_object(SAVEFILE);
+  destruct(this_object());
+  return 1;
+}
+
+// liefert Mittelwertarray zurueck
+//TODO: <int|string>* results 
+private mixed calc_averages(mixed data, mixed results, int index, float scale) { 
+  float tmp;
+  int t;
+
+  if (!scale) scale=1.0;
+  
+  // 2s
+  results[0] = to_float(data[index]) * scale;
+
+  // alle 10s / 5 HBs den 20s-Wert neu berechnen
+  if ( index%5 == 1) {
+    if (index<9) {
+      foreach(int i: index-4+MESSWERTE .. MESSWERTE-1) {
+        tmp+=data[i];
+        t++;
+      }
+      foreach(int i: 0 .. index) {
+        tmp+=data[i];
+        t++;
+      }
+    }
+    else {
+      foreach(int i: index-9 .. index) {
+        tmp+=data[i];
+        t++;
+      }
+    }
+    results[1]=tmp/to_float(t) * scale;
+  }
+
+  // alle 10s / 5 HBs den 60s-Wert neu berechnen, aber in anderem HB als den
+  // 10s-Wert
+  if ( index%5 == 3) {
+    tmp=0.0;
+    t=0;
+    if (index<29) {
+      foreach(int i: index-14+MESSWERTE .. MESSWERTE-1) {
+        tmp+=data[i];
+        t++;
+      }
+      foreach(int i: 0 .. index) {
+        tmp+=data[i];
+        t++;
+      }
+    }
+    else {
+      foreach(int i: index-29 .. index) {
+        tmp+=data[i];
+        t++;
+      }
+    }
+    results[2]=tmp/to_float(t) * scale;
+  }
+
+  // 300s
+  // alle 40s / 20 HBs den 300s-Wert neu berechnen.
+  if ( index%20 == 12) {
+    tmp=0.0;
+    t=0;
+    if (index<149) {
+      foreach(int i: index-74+MESSWERTE .. MESSWERTE-1) {
+        tmp+=data[i];
+        t++;
+      }
+      foreach(int i: 0 .. index) {
+        tmp+=data[i];
+        t++;
+      }
+    }
+    else {
+      foreach(int i: index-149 .. index) {
+        tmp+=data[i];
+        t++;
+      }
+    }
+    results[3]=tmp/to_float(t) * scale;
+  }
+
+
+  // 900s
+  // alle 80s / 40 HBs den 900s-Wert neu berechnen.
+  if ( index%40 == 31) {
+    tmp=0.0;
+    t=0;
+    if (index<449) {
+      foreach(int i: index-224+MESSWERTE .. MESSWERTE-1) {
+        tmp+=data[i];
+        t++;
+      }
+      foreach(int i: 0 .. index) {
+        tmp+=data[i];
+        t++;
+      }
+    }
+    else {
+      foreach(int i: index-449 .. index) {
+        tmp+=data[i];
+        t++;
+      }
+    }
+    results[4]=tmp/to_float(t) * scale;
+  }
+
+  // 3600s
+  // alle 300s / 150 HBs den 3600s-Wert neu berechnen.
+  if ( index%150 == 128) {
+    tmp=0.0;
+    t=0;
+    if (index<1799) {
+      foreach(int i: index-899+MESSWERTE .. MESSWERTE-1) {
+        tmp+=data[i];
+        t++;
+      }
+      foreach(int i: 0 .. index) {
+        tmp+=data[i];
+        t++;
+      }
+    }
+    else {
+      foreach(int i: index-1799 .. index) {
+        tmp+=data[i];
+        t++;
+      }
+    }
+    results[5]=tmp/to_float(t) * scale;
+  }
+  return results;
+}
+
+protected void heart_beat() {
+  int hbcount=efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+  // per Modulo Index auf 0 - 449 begrenzen
+  int index=hbcount%MESSWERTE;
+
+  // mass fuer das lag ist das Verhaeltnis von vergangener Sollzeit (Differenz
+  // HB-Counts * 2) und vergangener Istzeit (Differenz von time()). Und das
+  // ganze wird dann als Abweichung von 1 gespeichert (0, wenn kein Lag)
+  int *nowtime = utime();
+  float timediff = to_float((nowtime[0]-lasttime[0])) 
+                 + to_float(nowtime[1]-lasttime[1])/1000000.0;
+
+//  lagdata[index] = 1.0 -
+//                   (to_float((hbcount - lasthbcount) * __HEART_BEAT_INTERVAL__)
+//                     / timediff);
+//  lagdata[index] = (timediff -
+//    to_float((hbcount - lasthbcount) * __HEART_BEAT_INTERVAL__)) /
+//    to_float((hbcount - lasthbcount) * __HEART_BEAT_INTERVAL__);
+  lagdata[index] = abs(to_float((hbcount - lasthbcount) * __HEART_BEAT_INTERVAL__)
+                    - timediff)  /
+                   to_float((hbcount - lasthbcount) * __HEART_BEAT_INTERVAL__);
+
+
+  hbdata[index] = efun::driver_info(DI_NUM_HEARTBEATS_LAST_PROCESSED);
+  codata[index] = efun::driver_info(DI_NUM_CALLOUTS);
+
+  /*                     
+  DEBUG(sprintf("Index: %d: %d/%.6f => %:6f /1\n",index,
+        (hbcount - lasthbcount)*2,
+        timediff, lagdata[index]));
+  */      
+  lasthbcount=hbcount;
+  lasttime=nowtime;
+ 
+  lag = calc_averages(lagdata, lag, index, 100.0);
+  // Multiplizieren mit 100
+  //DEBUG(sprintf("%O\n",lag));
+  //lag = map(lag, function float (float f) {return f*100.0;} );
+  //DEBUG(sprintf("%O\n",lag));
+  hbs = map(calc_averages(hbdata, hbs, index,1.0),#'to_int);
+  callouts = map(calc_averages(codata, callouts, index,1.0),#'to_int);
+
+  // und ausserdem mal etwas loggen fuer den Moment
+  if (lag[0] > 1000 || callouts[0] > 1000)
+  {
+  LOGMAX(sprintf("%s, %d, %d, %:6f, %:6f, %:6f, %:6f, %:6f, %:6f, "
+              "%d, %d, %d, %d, %d, %d, "
+              "%d, %d, %d, %d, %d, %d, \n",
+          strftime("%y%m%d-%H%M%S"),time(),hbcount,
+          lag[0],lag[1],lag[2],lag[3],lag[4],lag[5],
+          hbs[0],hbs[1],hbs[2],hbs[3],hbs[4],hbs[5],
+          callouts[0],callouts[1],callouts[2],callouts[3],callouts[4],
+          callouts[5]));
+  }
+  else if (hbcount%80 == 0 || lag[0] > 10)
+  {
+  LOG(sprintf("%s, %d, %d, %:6f, %:6f, %:6f, %:6f, %:6f, %:6f, "
+              "%d, %d, %d, %d, %d, %d, "
+              "%d, %d, %d, %d, %d, %d, \n",
+          strftime("%y%m%d-%H%M%S"),time(),hbcount,
+          lag[0],lag[1],lag[2],lag[3],lag[4],lag[5],
+          hbs[0],hbs[1],hbs[2],hbs[3],hbs[4],hbs[5],
+          callouts[0],callouts[1],callouts[2],callouts[3],callouts[4],
+          callouts[5]));
+  }
+}
+
+float *read_lag_data() {
+  return ({lag[2],lag[4],lag[5]});
+}
+float *read_ext_lag_data() {
+  return copy(lag);
+}
+int *read_hb_data() {
+  return copy(hbs);
+}
+int *read_co_data() {
+  return copy(callouts);
+}
+
diff --git a/p/daemon/mand.c b/p/daemon/mand.c
new file mode 100644
index 0000000..e80c50e
--- /dev/null
+++ b/p/daemon/mand.c
@@ -0,0 +1,169 @@
+//
+// mand.c - Manpage Daemon
+//
+// Erweitert und umgearbeitet fuer die Anbindung durch
+// die Magiershell
+//
+// von Mandragon@MG
+//
+// Basiert auf der Arbeit von Hadra,Mupfel und Tyron
+// (alle @Anderland)
+//
+
+#define DEBUGGER "mandragon"
+
+#define DEBUG(x) if (find_player(DEBUGGER)) \
+                         tell_object(find_player(DEBUGGER),x+"\n")
+
+
+#pragma strict_types,save_types
+#pragma no_clone,no_shadow
+
+#include <daemon/mand.h>
+
+private nosave mapping pages;
+private nosave string *keys;
+private nosave mapping pages_case;
+private nosave string *keys_case;
+
+static string glob2regexp(string str)
+{
+  str=regreplace(str,"([\\.\\^\\$\\[\\]\\(\\)])","\\\\\\1",1);
+  str=regreplace(str,"\\*",".*",1);
+  return regreplace("^"+str+"$","?",".",1);
+}
+
+void cache_entry(string entry, string dir, string file)
+{
+  string *data;
+  int j;
+
+  if (m_contains(&data,pages,lower_case(entry)))
+  {
+    j=sizeof(data);
+    while(j--) data[--j]=data[j];
+    pages[lower_case(entry)]=data+({ dir+"/"+file, dir+"/"+file});
+  }
+  else
+  {
+    pages[lower_case(entry)]=({entry,dir+"/"+file});
+    keys+=({lower_case(entry)});
+  }
+  if (m_contains(&data,pages_case,entry))
+  {
+    j=sizeof(data);
+    while(j--) data[--j]=data[j];
+    pages_case[entry]=data+({ dir+"/"+file, dir+"/"+file});
+  }
+  else
+  {
+    pages_case[entry]=({entry,dir+"/"+file});
+    keys_case+=({entry});
+  }
+  return;
+}
+
+static void load_synonyms(string dir)
+{
+  string filedata,*syn;
+
+  if (file_size(MAND_DOCDIR+dir+"/.synonym")<=0) return;
+  if (!(filedata=read_file(MAND_DOCDIR+dir+"/.synonym"))) return;
+  syn=regexplode(filedata, "([ \t][ \t]*|\n)")-({""});
+  syn-=regexp(syn,"^([ \t][ \t]*|\n)$");
+  while(sizeof(syn))
+  {
+    cache_entry(syn[0],dir,syn[1]);
+    syn=syn[2..];
+  }
+  return;
+}
+
+static void cache_directory(string dir)
+{
+  string file;
+  int i,j;
+  mixed *files;
+
+  files = get_dir(MAND_DOCDIR+dir+"/*",3)||({});
+  i=sizeof(files);
+  for (j=0;j<i;)
+  {
+    file=files[j++];
+    if (file[0]=='.')
+    {
+      j++;
+      continue;
+    }
+    if (files[j++]==-2)
+    {
+      cache_directory(dir+"/"+file);
+      continue;
+    }
+    cache_entry(file,dir,file);
+  }
+  load_synonyms(dir);
+}
+
+void update_cache()
+{
+  int i;
+  string *docdirs;
+  mixed *tmp;
+
+  tmp=get_dir(MAND_DOCDIR "*",3);
+  docdirs=({});
+  pages=([]);
+  pages_case=([]);
+  keys_case=({});
+  keys=({});
+  i=sizeof(tmp);
+  while(i--)
+    if (tmp[i]==-2)
+      docdirs+=({ tmp[(i--)-1] });
+  docdirs-=({".",".."});
+  docdirs-=MAND_EXCLUDE;
+  i=sizeof(docdirs);
+  while(i--) cache_directory(docdirs[i]);
+  return;
+}
+
+void create()
+{
+  seteuid(getuid());
+  update_cache();
+  set_next_reset(10080); //Einmal pro Woche langt
+  return;
+}
+
+void reset()
+{
+  update_cache();
+  set_next_reset(10080);
+  return;
+}
+
+//
+// locate(): Manpage im Cache suchen:
+// match = 1 -> glob-matching, match=2 -> regexp-matching
+// Pruefung ob str gueltige regexp erfolgt in der Shell
+//
+
+string *locate(string str,int match)
+{
+  string *matches,*ret,str2;
+  int i;
+
+  if (!match&&member(keys_case,str)!=-1) return pages_case[str];
+  str=lower_case(str);
+  if (match==1) str=glob2regexp(str);
+  if (match) matches=regexp(keys,str);
+  else if (member(keys,str)!=-1) matches=({ str });
+  else return ({});
+
+  ret=({});
+  i=sizeof(matches);
+  while(i--)
+    ret+=pages[matches[i]];
+  return ret;
+}
diff --git a/p/daemon/moneylog.c b/p/daemon/moneylog.c
new file mode 100644
index 0000000..f3a59bf
--- /dev/null
+++ b/p/daemon/moneylog.c
@@ -0,0 +1,246 @@
+// Log-Daemon, um Geldbugs zu finden.
+//
+// Noch experimentell, ich feile noch fleissig an den Werten&Co.
+// Wer mir ohne triftigen Grund daran herumpfuscht, der wird standesrechtlich
+// mit faulen Eiern beworfen. ]:->
+//
+// Tiamak
+
+#pragma strong_types,save_types
+#pragma no_clone,no_shadow
+
+#include <wizlevels.h>
+#include <money.h>
+
+#define LOGNAME "ADDMONEY"
+#define SAVEFILE "/p/daemon/save/moneylog"
+
+#define THRESHOLD_HOURLY 10000 // wieviel Geld darf man an einer Stelle pro
+#define THRESHOLD_DAILY  50000 // Stunde/Tag bekommen, ohne dass geloggt wird?
+
+
+private mapping mon, grenzen;
+private int check, next_reset;
+
+
+public void create()
+{    
+    // wir muessen Savefile schreiben duerfen
+    seteuid(getuid());
+    
+    if ( !restore_object(SAVEFILE) ){
+        mon = m_allocate( 0, 2 );
+        grenzen = m_allocate( 0, 2 );
+        check = 0;
+        next_reset = 3600;
+    }
+
+    if ( next_reset > 3600 )
+        next_reset = 3600;
+
+    if ( next_reset < 60 )
+        next_reset = 60;
+
+    // Auswertung jede Stunde
+    set_next_reset( next_reset );
+    next_reset += time();
+}
+
+
+// Sortierfunktionen
+
+static int _sort_hourly( string fn1, string fn2 )
+{
+    return mon[fn1, 0] > mon[fn2, 0];
+}
+
+
+static int _sort_daily( string fn1, string fn2 )
+{
+    return mon[fn1, 1] > mon[fn2, 1];
+}
+
+
+// im reset() wird die Auswertung vorgenommen
+public void reset()
+{
+    int i;
+    string *files, txt;
+
+    // keinen Aufruf per Hand bitte
+    if ( time() < next_reset )
+        return;
+
+    // Auswertung jede Stunde
+    set_next_reset(3600);
+    next_reset = time() + 3600;
+    check++;
+    txt = "";
+    
+    files = sort_array( m_indices(mon), #'_sort_hourly/*'*/ );
+
+    for ( i = sizeof(files); i--; ){
+        if ( !(grenzen[files[i], 0] < 0) &&
+             (grenzen[files[i], 0] && mon[files[i], 0] > grenzen[files[i], 0])
+             || (!grenzen[files[i], 0] && mon[files[i], 0] > THRESHOLD_HOURLY) )
+            txt += sprintf( "%12d --- %s\n", mon[files[i], 0], files[i] );
+    
+        mon[files[i], 0] = 0;
+    }
+
+    // nur loggen, wenn es auch etwas zu loggen gibt
+    if ( txt != "" )
+        log_file( LOGNAME, sprintf( "%s: =================================="
+                                    "==============(stuendlich)===\n",
+                                    ctime(time())[4..15] ) + txt );
+
+    // der "grosse" Check kommt nur einmal pro Tag
+    if ( check < 24 ){
+        save_object(SAVEFILE);
+        return;
+    }
+
+    check = 0;
+    txt = "";
+    files = sort_array( m_indices(mon), #'_sort_daily/*'*/ );
+    
+    for ( i = sizeof(files); i--; )
+        if ( !(grenzen[files[i], 1] < 0) &&
+             (grenzen[files[i], 1] && mon[files[i], 1] > grenzen[files[i], 1])
+             || (!grenzen[files[i], 1] && mon[files[i], 1] > THRESHOLD_DAILY) )
+            txt += sprintf( "%12d --- %s\n", mon[files[i], 1], files[i] );
+
+    if ( txt != "" )
+        log_file( LOGNAME, sprintf( "%s: =================================="
+                                    "==============(taeglich)=====\n",
+                                    ctime(time())[4..15] ) + txt );
+
+    mon = m_allocate( 1, 2 );
+    save_object(SAVEFILE);
+}
+
+
+// die eigentliche Logg-Funktion
+public void AddMoney( object ob, int amount )
+{
+    string fn;
+
+    // keine Manipulationen per Hand bitte
+    if ( !objectp(ob) || amount < 0 || !previous_object() ||
+         (!query_once_interactive(previous_object()) &&
+          load_name(previous_object()) != GELD)
+         || IS_LEARNER(previous_object()) )
+        return;
+
+    fn = explode( object_name(ob), "#" )[0];
+
+    mon[fn, 0] += amount;
+    mon[fn, 1] += amount;
+}
+
+
+// Savefile noch schnell abspeichern - wir wollen ja keine Daten verlieren ;-)
+public int remove()
+{
+    next_reset = query_next_reset(this_object()) - time();
+    save_object(SAVEFILE);
+
+    destruct(this_object());
+    return 1;
+}
+
+
+// fuer bestimmte Files kann man die Grenzwerte einzeln setzen
+public varargs int modify_threshold( string fn, int amount1, int amount2 )
+{
+    if ( !this_interactive() || this_interactive() != this_player() ||
+         !IS_ARCH(this_interactive()) || process_call() )
+        return -1;
+
+    if ( !stringp(fn) || !intp(amount1) || !intp(amount2) )
+        return -2;
+
+    if ( !amount1 && !amount2 ){
+        m_delete( grenzen, fn );
+        write( "Eintrag " + fn + " geloescht.\n" );
+        return 1;
+    }
+
+    grenzen[fn, 0] = amount1;
+    grenzen[fn, 1] = amount2;
+    
+    write( break_string( "Die Grenzen fuer " + fn + " betragen jetzt " +
+                         ((amount1 < 0) ? "unendlich viele" :
+                          to_string(amount1)) + " Muenzen fuer den "
+                         "stuendlichen und " +
+                         ((amount2 < 0) ? "unendlich viele" :
+                          to_string(amount2)) + " Muenzen fuer den taeglichen "
+                         "Check.", 78 ) );
+
+    save_object(SAVEFILE);
+    return 1;
+}
+
+
+// einzeln gesetzte Grenzwerte abfragen
+public varargs mixed query_thresholds( string str )
+{
+    int i;
+    string *files, txt;
+    
+    if ( !this_player() )
+        return deep_copy(grenzen);
+
+    if ( stringp(str) && member( grenzen, str ) ){
+        write( break_string( "Die Grenzen fuer " + str + " betragen " +
+                             ((grenzen[str, 0] < 0) ? "unendlich viele" :
+                              to_string(grenzen[str, 0])) + " Muenzen fuer den "
+                             "stuendlichen und " +
+                             ((grenzen[str, 1] < 0) ? "unendlich viele" :
+                              to_string(grenzen[str, 1])) +
+                             " Muenzen fuer den taeglichen Check.", 78 ) );
+        return 0;
+    }
+
+    if ( stringp(str) && str != "" ){
+        write( "Es sind keine Grenzen fuer " + str + " eingetragen!\n" );
+        return 0;
+    }
+
+    files = sort_array( m_indices(grenzen), #'</*'*/ );
+    txt = "Eingetragene Grenzwerte:\n================================="
+        "=============================================\n";
+
+    for ( i = sizeof(files); i--; )
+        txt += sprintf( "%'.'-48s : %8d (h) %10d (d)\n",
+                        files[i], grenzen[files[i], 0], grenzen[files[i], 1] );
+
+    this_player()->More( txt );
+    return 1;
+}
+
+
+// bisher geloggte Daten abfragen
+public mixed query_status()
+{
+    int i;
+    string *files, txt;
+    
+    if ( !this_player() )
+        return deep_copy(mon);
+
+    files = sort_array( m_indices(mon), #'</*'*/ );
+    txt = "Geldquellen:\n================================="
+        "=============================================\n";
+
+    for ( i = sizeof(files); i--; )
+        txt += sprintf( "%'.'-48s : %8d (h) %10d (d)\n",
+                        files[i], mon[files[i], 0], mon[files[i], 1] );
+
+    this_player()->More( txt );
+    return 1;
+}
+
+
+// keine Shadows bitte
+public varargs int query_prevent_shadow( object ob ) { return 1; }
diff --git a/p/daemon/namefake.c b/p/daemon/namefake.c
new file mode 100644
index 0000000..f4146b2
--- /dev/null
+++ b/p/daemon/namefake.c
@@ -0,0 +1,54 @@
+/* Dieses Objekt dient zum Faken von Namen fuer den Channeld.
+ * Hintergrund: der Channeld braucht beim send() ein Objekt (bzw. die in der
+ * Verarbeitung dann folgenden Programme). Der Mudlibmaster sendet Meldung in
+ * anderer Leute Namen (der darf das auch). Dafuer clont und konfiguriert er
+ * dieses Objekt und uebergibt es an den channeld. Nach 3s raeumen sich Clone
+ * dieses Objektes wieder auf.
+ * Falls der Spieler/NPC existiert, wenn die meldung gesendet wird, wird vom
+ * Master das richtige Objekt uebergeben, nicht dieses hier.
+ */
+
+#include <config.h>
+#include <wizlevels.h>
+
+inherit "/std/secure_thing";
+
+// Envcheck-Mechanismus missbrauchen. ;-)
+protected void check_for_environment(string cloner)
+{
+  if (clonep(this_object()))
+    remove(1);
+}
+
+// Zur Sicherheit auch im Reset
+void reset()
+{
+  if (clonep(this_object()))
+    remove(1);
+}
+
+// Kopie aus /std/thing/properties.c. Man braucht davon wohl kein
+// zugaengliches in jedem thing...
+// Welche externen Objekte duerfen zugreifen?
+nomask private int allowed()
+{
+    if ( (previous_object() && IS_ARCH(getuid(previous_object())) &&
+          this_interactive() && IS_ARCH(this_interactive())) ||
+         (previous_object() && getuid(previous_object()) == ROOTID &&
+          geteuid(previous_object()) == ROOTID) )
+        return 1;
+    return 0;
+}
+
+// Sollte nur von ROOT oder EM+ manipuliert werden.
+// Sprich, das tut so, als seien alle Props SECURED
+public varargs mixed Set( string name, mixed value, int type, int extern )
+{
+  if ((extern || extern_call())
+      && previous_object() != this_object()
+      && !allowed()) // aus thing/properties.c
+    return -1;
+
+  return ::Set(name, value, type, extern);
+}
+
diff --git a/p/daemon/object.h b/p/daemon/object.h
new file mode 100644
index 0000000..7f7e20f
--- /dev/null
+++ b/p/daemon/object.h
@@ -0,0 +1,38 @@
+// MorgenGrauen MUDlib
+//
+// OBJECT.H -- persistent object handling
+//
+// $Date: 1995/03/31 13:30:33 $
+// $Revision: 1.1 $
+/* $Log: object.h,v $
+ * Revision 1.1  1995/03/31  13:30:33  Hate
+ * Initial revision
+ *
+ * Revision 1.1  1994/03/20  17:07:28  Hate
+ * Initial revision
+ *
+ */
+ 
+#ifndef __OBJECT_H__
+#define __OBJECT_H__
+
+// defines
+#define OBJECTD       "/p/daemon/objectd"
+#define OBJECTD_SAVE  "/p/daemon/save/objectd"
+
+#endif // __OBJECT_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __OBJECT_H_PROTO__
+#define __OBJECT_H_PROTO__
+
+// prototypes
+mixed AddObject(string obj, string env);
+mixed RemoveObject(string obj, string env);
+varargs void QueryObject(mixed env);
+
+
+#endif // __OBJECT_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/p/daemon/objectd.c b/p/daemon/objectd.c
new file mode 100644
index 0000000..1b5d821
--- /dev/null
+++ b/p/daemon/objectd.c
@@ -0,0 +1,150 @@
+// MorgenGrauen MUDlib
+//
+// OBJECTD.C -- object daemon
+//
+// $Date: 1995/04/03 14:47:02 $
+// $Revision: 9510 $
+/* $Log: objectd.c,v $
+ * Revision 1.2  1995/04/03 14:47:02  Wargon
+ * QueryObject() verwendet bei BluePrints jetzt auch AddItem.
+ *
+ * Revision 1.1  1995/03/31  13:30:33  Hate
+ * Initial revision
+ *
+ */
+
+#pragma strong_types,save_types,rtt_checks
+#pragma no_clone, no_shadow
+
+#include <rooms.h>
+#include <properties.h>
+#include <defines.h>
+#include <daemon.h>
+
+mapping objects = ([]);
+private nosave int do_save;
+
+#define CLASS   0
+#define DATA    1
+
+protected void create()
+{
+  seteuid(getuid(ME));
+  restore_object(OBJECTD_SAVE);
+}
+
+protected void reset() {
+  if (do_save)
+  {
+    save_object(OBJECTD_SAVE);
+    do_save=0;
+  }
+}
+
+string AddObject(object obj, string env)
+{
+  if(!obj || !env || !objectp(obj) || !stringp(env))
+    return 0;
+
+  // save information
+  if(!member(objects, env))
+    objects[env] = ({ ({  object_name(obj), obj->QueryProp(P_AUTOLOAD) }) });
+  else
+    objects[env] += ({ ({ object_name(obj), obj->QueryProp(P_AUTOLOAD) }) });
+
+  do_save=1;
+  return env;
+}
+
+int RemoveObject(object|string obj, string env)
+{
+  if(!obj || !env || !stringp(env))
+    return 0;
+
+  // save information
+  if(member(objects, env))
+  {
+    mixed oblist = objects[env];
+    foreach(mixed arr: &oblist)
+    {
+      if (load_name(arr[CLASS]) == load_name(obj))
+      {
+        arr = 0;
+        // nur eins Austragen, nicht alle, falls mehr als einmal angemeldet
+        break;
+      }
+    }
+    objects[env] = oblist - ({0});
+  }
+  if(!sizeof(objects[env]))
+    m_delete(objects, env);
+
+  do_save=1;
+  return 1;
+}
+
+// Fragt nicht wirklich ab, sondern erstellt die angemeldeten Objekt in env.
+// Wenn kein env, wird previous_object() genommen.
+varargs void QueryObject(string envname)
+{
+  object env;
+  // take the caller as the questioning object
+  if(!envname || !stringp(envname))
+    env = previous_object();
+  else
+    env = find_object(envname);
+
+  // target must be a blueprint
+  if(IS_CLONE(env)) return 0;
+
+  envname=object_name(env);
+  if(member(objects, envname))
+  {
+    mixed oblist = objects[envname];
+    foreach(mixed arr : &oblist)
+    {
+       // Wir muessen unterscheiden, ob ein Clone angemeldete wurde oder eine
+       // Blueprint.
+       object o;
+       string *n = explode(arr[CLASS], "#");
+       if (sizeof(n) > 1 && sizeof(n[1]) > 0)
+         o = env->AddItem(n[0], REFRESH_DESTRUCT);
+       else
+         o = env->AddItem(n[0], REFRESH_DESTRUCT, 1);
+       o->SetProp(P_AUTOLOAD, arr[DATA]);
+       // Und das neue Objekt merken (ne Verwendung hat das zur Zeit aber
+       // nicht)
+       arr[CLASS] = object_name(o);
+    }
+  }
+}
+
+// Liefert eine Liste der in env angemeldeten Objekte. Wenn kein env, wird
+// previous_object() genommen.
+public varargs string QueryObjects(string envname)
+{
+  object env;
+  // take the caller as the questioning object
+  if(!envname || !stringp(envname))
+    env = previous_object();
+  else
+    env = find_object(envname);
+
+  // target must be a blueprint
+  if(IS_CLONE(env)) return 0;
+
+  mixed arrarr = objects[object_name(env)];
+  if (arrarr)
+  {
+    return CountUp(map(arrarr, #'[, CLASS),", ",", ");
+  }
+  return 0;
+}
+
+public varargs int remove(int silent)
+{
+  save_object(OBJECTD_SAVE);
+  destruct(ME);
+  return 1;
+}
+
diff --git a/p/daemon/pathd.c b/p/daemon/pathd.c
new file mode 100644
index 0000000..17d34ad
--- /dev/null
+++ b/p/daemon/pathd.c
@@ -0,0 +1,1000 @@
+// Daemon zum automatisierten Erstellen von Karten.
+// Soll spaeter mal wichtige Wege berechnen koennen fuer Anfaenger.
+//
+//
+// Wer mir ohne triftigen Grund daran herumpfuscht, der wird standesrechtlich
+// mit faulen Eiern beworfen. ]:->
+//
+// Tiamak
+
+#pragma strong_types,save_types,rtt_checks
+#pragma no_clone,no_inherit,no_shadow
+#pragma pedantic,range_check,warn_deprecated
+#pragma warn_empty_casts,warn_missing_return,warn_function_inconsistent
+
+#include <strings.h>
+#include <wizlevels.h>
+#include <player.h>
+#include <properties.h>
+#include <rooms.h>
+#include <moving.h>
+
+#define LOGNAME "PATHD"
+#define SAVEFILE "/p/daemon/save/pathd"
+#define DUMPFILE "/p/daemon/save/pathd-dump"
+//#define DEBUG
+
+// 10 verschiedene Leute werden gebraucht, um eine Verbindung zu verifizieren
+#define NEEDED 10
+#define TIMEOUT_NEW 1209600  // 14 Tage
+#define TIMEOUT_OLD 7776000 // 90 Tage
+
+// Art des Ausgangs
+#define NORMAL  0
+#define SPECIAL 1
+#define COMMAND 2
+
+// Kosten fuer unterschiedliche Wege
+#define COST_EXITS ({ 1, 10, 100 })
+#define COST_MANY   3
+#define COST_FEW    5
+#define COST_ONE   10
+// Kosten fuer Weltenwechsel (Para -> Normal)
+#define COST_WORLDCHANGE 100
+
+#ifdef DEBUG
+#define DBG(x) if ( find_player("zesstra") ) \
+                   tell_object( find_player("zesstra"), (x)+"\n" );
+#define TELL_USER(x) if ( this_player() ) \
+                   tell_object( this_player(), (x)+"\n" );
+#else
+#define DBG(x)
+#define TELL_USER(x)
+#endif
+
+#define LOG(x) log_file( "PATHD", (x), 100000 );
+
+// Variablen
+nosave int time_to_clean_up; // Zeit fuer naechstes Cleanup der Daten
+
+mapping pathmap;
+/* Prefix : ([ <submap> ]),
+   <submap> ist ein Mapping von Raeumen mit einem Array von Verbindungen:
+   ([<room>: ({ conn1, conn2, conn3, ...}), ...])
+   Jede Verbindung ist ein Array mit folgenden Indizes:
+   */
+#define CONN_DEST     0   // Zielraum
+#define CONN_CMD      1   // Kommando
+#define CONN_USERS    2   // Benutzer: ({spieler, seher})
+#define CONN_EXITTYPE 3   // Ausgangsart (s.o.)
+#define CONN_TIMES    4   // Zeit letzter Benutzung: ({spieler,seher})
+#define CONN_PARA     5   // Parawelt
+#define CONN_FLAGS    6   // Flags fuer die Verbindung (z.B. permanent)
+// Indizes in CONN_USERS. Beide Eintraege sind entweder gehashte UIDs ODER -1,
+// wenn die Verbindung von mehr als NEEDED unterschiedlichen gegangen wurde.
+#define CONN_U_PLAYERS 0  // Spieler-UIDs
+#define CONN_U_SEERS   1  // Seher-UIDS
+// Indizes von CONN_TIMES. Zeitstempel der letzten Benutzung...
+#define CONN_T_PLAYERS CONN_U_PLAYERS  // ... durch Spieler
+#define CONN_T_SEERS   CONN_U_SEERS  // ... durch Seher
+
+// Konstanten fuer CONN_FLAGS
+#define CFLAG_PERMANENT   0x1  // Verbindung nicht expiren
+#define CFLAG_DONTUSE     0x2  // Verbindung nicht im Routing nutzen
+#define CFLAG_DUPLICATE   0x4  // Kommando hat mehrere Verbindungen
+
+// laufende Pfadsuchen
+nosave mapping searchlist = ([]);
+/*
+   ([ <uuid> : SL_VISITED; SL_DESTINATION;... ... ])
+ */
+// Indizes in <data>:
+#define SL_START       0
+#define SL_DESTINATION 1  // Zielraum
+#define SL_VISITED     2  // abgelaufene/gepruefte Raeume
+#define SL_CANDIDATES  3  // moegliche Ziele
+#define SL_WIZLEVEL    4  // Wizlevel
+#define SL_PARA        5  // Parawelt
+#define SL_TODO        6  // noch zu pruefende Raume (SL_CANDIDATES-SL_VISITED)
+#define SL_CALLBACK    7  // Closure fuer Callback nach Routenberechnung
+// SL_CANDIDATES: ([<raum>: ({verbindungsdaten}), ...])
+// Indizes im Array <verbindungsdaten>:
+#define SLT_COSTS     0   // Kosten fuer Verbindung bis hier
+#define SLT_WAY       1   // bisheriger Weg von Start bis hier
+#define SLT_COMMANDS  2   // Kommandos fuer Weg von Start bis hier
+
+// cache fuer berechnete Wege
+nosave mapping pathcache = ([]);
+// Aufbau: ([ "startraum": <dests>, ...])
+// <dests>: (["zielraum": <wege>, ...])
+// <wege>: ([parawelt: (<struct path_s>) ])
+
+struct path_s {
+    int costs;      // Kosten des kompletten Pfades
+    string *rooms;  // Liste von durchlaufenen Raeumen
+    string *cmds;   // Liste von Kommandos, um Raeume zu durchlaufen
+    int ttl;        // time-to-live, Ablaufzeit des Pfads im Cache
+    int para;       // Parawelt
+    int flags;      // Flags zu dem Pfad
+};
+// Flags fuer Pfade im Pfadcache
+#define PFLAG_PERMANENT CFLAG_PERMANENT
+#define PFLAG_DONTUSE CFLAG_DONTUSE
+
+
+// Prototypes
+public void create();
+public void add_paths( mixed connections );
+public varargs void show_pathmap( string path );
+public varargs int show_statistic( int verbose, int silent );
+public int change_pathmap( string pfad, mixed *verbindungen );
+public mapping get_pathmap();
+public void reset();
+public varargs int remove( int silent );
+public varargs int query_prevent_shadow( object ob );
+static int insert_paths( mixed *path );
+static mixed *format_paths( mixed buf );
+static void _search( string fname, string start );
+private string _get_prefix( string path );
+protected void cleanup_data( string *names, int idx );
+protected void expire_path_cache(string *startrooms);
+
+
+public void create()
+{
+    // wir muessen das Savefile schreiben duerfen
+    seteuid(getuid());
+
+    if ( !restore_object(SAVEFILE) ) {
+        pathmap = ([]);
+    }
+}
+
+
+// Wird von Spielern aufgerufen mit den Wegen, die sie gelaufen sind
+public void add_paths( mixed connections )
+{
+    mixed paths;
+
+    // keinen Aufruf per Hand bitte
+    if ( !previous_object() || !this_player()
+         || this_interactive() && getuid(this_interactive()) != "zesstra"
+         || file_size( "/std/shells" +
+                       explode( object_name(previous_object()), ":" )[0]
+                       + ".c" ) < 1 )
+        return;
+
+#if __BOOT_TIME__ < 1357042092
+    // alte Spielerobjekte liefern noch ein Array von Strings statt eines
+    // Arrays von Arrays.
+    connections = map(connections, function mixed (string path)
+        {
+          // Alle Daten kommen als ein String mit '#' als Trenner an.
+          // Muster: Startraum#Zielraum#Verb#Methode der Bewegung#Parawelt
+          mixed buf = explode( path, "#" );
+          // Falls im Verb auch # vorkam (unwahrscheinlich, aber moeglich):
+          buf[2..<3] = ({ implode( buf[2..<3], "#" ) });
+          return buf;
+        } 
+        );
+#endif
+
+    // Daten aufbereiten
+    paths = map( connections, #'format_paths/*'*/ ) - ({0});
+    // Neue Raeume eintragen
+    filter( paths, #'insert_paths/*'*/ );
+}
+
+public mixed get_path_from_cache(string from, string to, int para) {
+    // wenn im Cache, aus dem Cache liefern.
+    mapping targets = pathcache[from];
+    if (targets) {
+        mapping paths = targets[to];
+        if (paths) {
+            if (member(paths, para)) {
+                struct path_s p = paths[para];
+                if (p->ttl > time())
+                    return ({ from, to, para, p->costs,
+                             copy(p->rooms), copy(p->cmds) });
+                else {
+                    m_delete(paths, para);
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+// Suchfunktion, um die kuerzeste Verbindung zwischen zwei Raeumen zu finden
+public int SearchPath( string from, string to, int para,
+                       closure callback)
+{
+    // Pro UID darf es nur einen Suchauftrag geben.
+    string uid=getuid(previous_object());
+    if ( member(searchlist,uid) )
+        return -1;
+    // und mehr als 5 gleichzeitig erstmal auch nicht.
+    if (sizeof(searchlist)>4)
+        return -2;
+
+    // Anfrage loggen
+    LOG(sprintf("%s: Suche %s -> %s (%d) von %s (%s, %s, %s)\n",
+          strftime("%y%m%d-%H%M%S"), from, to, para, uid,
+          object_name(previous_object()),
+          object_name(this_player()) || "NO-PL",
+          object_name(this_interactive()) || "NO-TI") );
+
+    // wenn Start- oder Zielraum nicht bekannt sind, kann es auch keine Route
+    // geben.
+    if (!member(pathmap[_get_prefix(to)],to)
+        || !member(pathmap[_get_prefix(from)],from) )
+        return -3;
+
+    mixed path = get_path_from_cache(from, to, para);
+    if (path) {
+        if (callback)
+            apply(callback,path); 
+        return 2;
+    }
+
+    // Datenstrukturerklaerung: s.o.
+    mapping targets = m_allocate(1200,3);
+    m_add(targets,from,0,({}),({}) );
+    searchlist+= ([ uid: from; to; ({});
+                     targets;
+                     (query_wiz_level(this_interactive())?1:0);
+                     para; ({}); callback]);
+
+    // Die eigentliche Suche starten
+    _search( uid, from );
+
+    return 1;
+}
+
+// Gespeicherte Informationen zu einem Raum oder einem kompletten Gebiet
+// abfragen. Gebiete lassen sich aber nur als "Organisationseinheiten" abfragen.
+// Dabei werden Gebiete unterhalb von /d/* als /d/region/magiername
+// abgespeichert und der Rest unter den ersten beiden Teilpfaden.
+public varargs void show_pathmap( string path )
+{
+    string pre;
+
+    if ( path )
+    {
+        pre = _get_prefix(path);
+
+        if ( pre == path )
+            // Ganzen Teilbaum ausgeben
+            printf( "Pathmap[%O]: %O\n", path, pathmap[path] );
+        else
+            // Nur Infos ueber einen Raum ausgeben
+            printf( "Pathmap[%O]: %O\n", path,
+                    pathmap[pre] ? pathmap[pre][path] : 0 );
+    }
+    else
+        // Ohne Argument wird die gesamte Map ausgegeben.
+        // Klappt aber eher nicht (mehr) wegen Buffer overflow. ;^)
+        printf( "Pathmap: %O\n", pathmap );
+}
+
+
+// Statistic zu den gespeicherten Daten anzeigen.
+// Mit 'verbose' wird jede Organisationseinheit einzeln aufgelistet, ohne
+// wird grob nach Regionen zusammengefasst.
+public varargs int show_statistic( int verbose, int silent )
+{
+    int i, ges;
+    string *ind, name;
+    mapping m;
+
+    if ( verbose ){ // Stur jeden Eintrag auflisten
+        ind = sort_array( m_indices(pathmap), #'</*'*/ );
+        for ( i = sizeof(ind); i--; ){
+            if ( !silent )
+                printf( "%-30s: %4d\n", ind[i], sizeof(pathmap[ind[i]]) );
+            ges += sizeof(pathmap[ind[i]]);
+        }
+    }
+    else {          // Regionen zusammenfassen
+        ind = m_indices(pathmap);
+        m = ([]);
+
+        for ( i = sizeof(ind); i--; ){
+            if (strstr(ind[i],"/d/") == 0)
+                // /d... jeweils nach zwei Teilpfaden
+                name = implode( explode( ind[i], "/" )[0..2], "/" );
+            else if ( strstr(ind[i],"/players") == 0)
+                // Alle Playerverzeichnisse zusammenfassen ...
+                name = "/players";
+            else
+                // ... den Rest nach einem Teilpfad
+                name = implode( explode( ind[i], "/" )[0..1], "/" );
+
+            if ( !m[name] )
+                m[name] = sizeof( pathmap[ind[i]] );
+            else
+                m[name] += sizeof( pathmap[ind[i]] );
+        }
+
+        ind = sort_array( m_indices(m), #'</*'*/ );
+        for ( i = sizeof(ind); i--; ){
+            if ( !silent )
+                printf( "%-30s: %4d\n", ind[i], m[ind[i]] );
+            ges += m[ind[i]];
+        }
+    }
+
+    if ( !silent )
+        printf( "\nGesamt: %d Raeume.\n", ges );
+
+    return ges;
+}
+
+
+// Manipulieren der internen Pathmap. Nur zum Debuggen und somit
+// nur fuer Zesstra erlaubt. Sonst verhunzt mir noch wer meine Daten. ;^)
+public int change_pathmap( string pfad, mixed *verbindungen )
+{
+    if ( !this_interactive() || getuid(this_interactive()) != "zesstra"
+         || !previous_object() || getuid(previous_object()) != "zesstra" )
+        return -1;
+
+    if ( mappingp(verbindungen) ){
+        if ( !pathmap[_get_prefix(pfad)] )
+            return 0;
+
+        pathmap[_get_prefix(pfad)] = verbindungen;
+    }
+    else {
+        if ( !pathmap[_get_prefix(pfad)][pfad] )
+            return 0;
+
+        pathmap[_get_prefix(pfad)][pfad] = verbindungen;
+    }
+
+    return 1;
+}
+
+// Die Funktion gibt alle dem pathd bekannten Raeume als Mapping von Arrays
+// zurueck. Schlüssel des Mappings sind dabei Gebiete.
+public mapping get_rooms()
+{
+  string *submaps;
+  mapping roommap;
+  int i;
+
+  roommap=([]);
+
+  submaps=m_indices(pathmap);
+
+  i=sizeof(submaps);
+
+  while (i--)
+    if (sizeof(m_indices(pathmap[submaps[i]])))
+        roommap[submaps[i]]=m_indices(pathmap[submaps[i]]);
+
+  return roommap;
+}
+
+// Daten zu einer Verbindung sammeln und aufbereiten
+static mixed *format_paths( mixed buf )
+{
+    string cmd, uid;
+    object ob;
+    mapping exits;
+    int art;
+
+    // Magier und Testspieler koennen auch in nicht angeschlossene Gebiete
+    // und werden deshalb nicht beachtet.
+    if ( IS_LEARNER(previous_object(1)) ||
+         IS_LEARNER(lower_case( previous_object(1)->Query(P_TESTPLAYER)||"" )) )
+        return 0;
+
+    cmd = trim(buf[2],TRIM_BOTH);
+#if __BOOT_TIME__ < 1357042092
+    // " 0" am Ende des Befehls abschneiden. *grummel*
+    if (cmd[<2..]==" 0")
+      cmd = cmd[..<3];
+#endif
+
+    // Wenn der Zielraum als String angegeben wurde, kann der fuehrende
+    // Slash fehlen!
+    if ( buf[1][0] != '/' )
+        buf[1] = "/" + buf[1];
+
+    // Zum Abfragen der zusaetzlichen Daten brauchen wir das Objekt selber
+    catch(ob = load_object(buf[0]);publish);
+    if (!objectp(ob))
+        return 0;
+
+    // Kleiner Hack - bei Transportern etc. ist 'ob' die nicht initialisierte
+    // Blueprint. Jede Abfrage von P_EXITS wuerde nett auf -Debug scrollen.
+    // Da P_IDS im create() auf jeden Fall auf ein Array gesetzt wird,
+    // missbrauche ich das hier als "Ist initialisiert"-Flag.
+    if ( !ob->QueryProp(P_IDS) )
+        return 0;
+
+    // Art des Ausgangs feststellen. 
+    if ( mappingp(exits = ob->QueryProp(P_EXITS)) && exits[cmd] )
+        art = NORMAL;   // "normaler" Ausgang
+    else if ( mappingp(exits = ob->QueryProp(P_SPECIAL_EXITS)) && exits[cmd] )
+        art = SPECIAL;  // SpecialExit
+    else {
+        // Kommandos, die einen in einen anderen Raum bringen
+        art = to_int(buf[3]);   // Move-Methode
+
+        // Es zaehlen aber nur Bewegungen, die halbwegs "normal" aussehen,
+        // d.h. kein M_TPORT, M_NOCHECK und M_GO muss gesetzt sein.
+        if ( art & (M_TPORT | M_NOCHECK) || !(art & M_GO) )
+            return 0;
+        else
+            art = COMMAND;
+    }
+
+    // Die UID der Spieler/Seher wird verschluesselt.
+    // Schliesslich brauchen wir sie nur fuer statistische Zwecke und nicht,
+    // um Bewegungsprofile zu erstellen. crypt() reicht fuer diese Zwecke
+    // wohl erstmal.
+    uid = getuid(previous_object(1));
+    uid = crypt( uid, "w3" );
+
+    // Start, Ziel, Verb, Wizlevel(TP), UID(TP), Art des Ausgangs, Parawelt
+    return ({ buf[0], buf[1], cmd, query_wiz_level(previous_object(1)) ? 1 : 0,
+                  uid, art, to_int(buf[4]) });
+}
+
+// Prueft alle Verbindungen im Array <conns>, welche das gleiche Kommando wie
+// <conn> haben, ob sie in unterschiedliche Raeume fuehren. Wenn ja, sind sie
+// fuer das Routing nicht nutzbar. Ausnahme: Verbindungen, die in
+// unterschiedliche Parawelten fuehren.
+// Aendert <conns> direkt.
+private void check_and_mark_duplicate_conns_by_conn(mixed conns, mixed conn) {
+    foreach(mixed c: conns) {
+        if ( c[CONN_CMD] == conn[CONN_CMD]
+            && c[CONN_DEST] != conn[CONN_DEST]
+            && c[CONN_PARA] == conn[CONN_PARA] )
+        {
+            c[CONN_FLAGS] |= CFLAG_DUPLICATE;
+            conn[CONN_FLAGS] |= CFLAG_DUPLICATE;
+        }
+    }
+}
+
+private void check_and_mark_duplicate_conns(mixed conns) {
+    foreach(mixed c: conns)
+        check_and_mark_duplicate_conns_by_conn(conns, c);
+}
+
+
+// Neue Verbindung in der Datenbank eintragen
+static int insert_paths( mixed *path )
+{
+    string pre = _get_prefix(path[0]);
+
+    // Falls noch gar kein Eintrag existiert, neu initialisieren
+    if ( !mappingp(pathmap[pre]) )
+        pathmap[pre] = ([]);
+
+    if ( !pointerp(pathmap[pre][path[0]]) )
+        pathmap[pre][path[0]] = ({});
+
+    // Aufbau von 'path':
+    // ({ Start, Ziel, Verb, Wizlevel(TP), UID(TP), Art des Ausgangs, Para })
+    // Aufbau von 'conn' (s. auch ganz oben)
+    // ({ Ziel, Verb, ({ Spieler-UIDs, Seher-UIDs }), Art des Ausgangs,
+    //   ({ Letztes Betreten durch Spieler, durch Seher }), Parawelt })
+
+    // Alle Verbindungen des Raumes durchgehen
+    mixed conns = pathmap[pre][path[0]];
+    // Kommandos der Verbindungen sammeln. Wenn es zwei Verbindungen mit dem
+    // gleichen Kommando gibt, sind beide unbenutzbar.
+    mixed cmds = m_allocate(10);
+    int i;
+    for ( i = sizeof(conns); i--; )
+    {
+        mixed conn = conns[i];
+        m_add(cmds,conn[CONN_CMD]); // Kommandos aller Conns merken
+        // Wenn Zielraum, Verb und Parawelt passen ...
+        if ( conn[CONN_DEST] == path[1] && conn[CONN_CMD] == path[2]
+             && conn[CONN_PARA] == path[6] )
+        {
+            // Wenn schon genug Leute diese Verbindung genutzt haben, einfach
+            // nur die Zeit der letzten Benutzung aktualisieren.
+            int index = path[3] ? CONN_U_SEERS : CONN_U_PLAYERS; // Seher?
+            if ( conn[CONN_USERS][index] == -1 )
+            {
+                conn[CONN_TIMES][index] = time();
+                break;
+            }
+            // Ansonsten die (neue?) UID hinzufuegen und die Zeit aktualisieren.
+            else
+            {
+                conn[CONN_USERS][index] =
+                    conn[CONN_USERS][index] - ({ path[4] }) + ({ path[4] });
+                if ( sizeof(conn[CONN_USERS][index]) >= NEEDED )
+                    conn[CONN_USERS][index] = -1;
+
+                conn[CONN_TIMES][index] = time();
+                break;
+            }
+        }
+    }
+
+    // Falls keine Verbindung gepasst hat, eine neue erzeugen.
+    if ( i < 0 ) {
+        int index = path[3] ? CONN_U_SEERS : CONN_U_PLAYERS; // Seher?
+        mixed uids = ({ ({}), ({}) });
+        uids[index] = ({ path[4] });
+        mixed times = ({ 0, 0 });
+        times[index] = time();
+
+        // Aufbau siehe oben
+        mixed conn = ({ path[1], path[2], uids, path[5], times, path[6], 0 });
+        conns += ({ conn });
+        // wenn die neue Verbindung nen Kommando benutzt, welches schon fuer
+        // eine andere Verbindung aus diesem Raum benutzt wird, sind beide
+        // nicht nutzbar, da das Ziel nicht eindeutig ist. In diesem Fall
+        // muessen alle diese als unbenutzbar fuer das Routing markiert
+        // werden.
+        if (member(cmds, conn[CONN_CMD])) {
+            check_and_mark_duplicate_conns_by_conn(conns,conn);
+        }
+    }
+
+    pathmap[pre][path[0]] = conns;
+
+    // Ob 0 oder 1 ist eigentlich total egal, da das Ergebnis-Array sowieso
+    // verworfen wird.
+    return 0;
+}
+
+private void add_to_cache(string from, string to, int para, int costs,
+                          string *rooms, string *cmds) {
+    struct path_s p = (<path_s> costs : costs, rooms: rooms, cmds : cmds,
+                         para: para, flags: 0, ttl: time()+86400);
+    if (!member(pathcache, from)) {
+        pathcache[from] = m_allocate(1);
+        pathcache[from][to] = m_allocate(1);
+    }
+    else if (!member(pathcache[from], to)) {
+        pathcache[from][to] = m_allocate(1);
+    }
+    pathcache[from][to][para] = p;
+}
+
+// Diese Funktion berechnet die Kosten einer Verbindung.
+// Ein Seherstatus schliesst die Moeglichkeiten von Spielern ein (seher
+// kann Spielerpfade problemlos nutzen).
+// Wenn eine Verbindung in eine andere Parawelt fuehrt, als in <para>
+// angefragt, wird sie erheblich teurer.
+protected int _calc_cost(mixed* path, int seer, int para)
+{
+    mixed* interest=path[CONN_USERS];
+
+    // Basiskosten nach Ausgangsart
+    int costs = COST_EXITS[path[CONN_EXITTYPE]];
+
+    // Verbindungen in andere als die gewuenschte Parawelt werden bei der
+    // suche nicht beruecksichtigt, aber Verbindung von Para nach Normal und
+    // umgekehrt sind erlaubt. Para->Normal soll aber etwas teurer sein, damit
+    // es bevorzugt wird, in der angefragten Welt zu bleiben.
+    // (Hintergrund: wenn man in Para ist, laeuft man meistens trotzdem durch
+    // Normalweltraeume, weil es keine passenden Pararaeume gibt).
+    if (para != path[CONN_PARA])
+        costs += COST_WORLDCHANGE;
+
+    // wenn schon genug unterschiedliche Leute die Verbindung gelaufen sind,
+    // kostet es die Basiskosten. Fuer Seher auch die Spieler einschliessen.
+    if (interest[CONN_U_PLAYERS]==-1
+        || (seer && interest[CONN_U_SEERS]==-1))
+        return costs;
+    else
+    {
+        // Sonst wird die Verbindung teurer.
+        // Anzahl Spielernutzer kann nicht -1 sein (s.o.).
+        int usercount = sizeof(interest[CONN_U_PLAYERS]);
+        // wenn <seer>, wird ggf. die Anzahl an Sehernutzern drauf addiert.
+        // Wert fuer Seher kann nicht -1 sein (s.o.)
+        if (seer)
+            usercount += sizeof(interest[CONN_U_SEERS]);
+        switch (usercount)
+        {
+            case 0:
+                costs=1000000;
+                break;
+            case 1..2:
+                costs*=COST_ONE;
+            case 3..4:
+                costs*=COST_FEW;
+            default:
+                costs*=COST_MANY;
+        }
+        return costs;
+    }
+    DBG(sprintf("Huch, keine Ahnung, Kosten unbestimmbar: %O",path));
+    return 1000;
+}
+
+// Neuimplementation von Humni, benutzt einfach simpel einen
+// Dijkstra-Algorithmus. "Simpel" schreibe ich, bevor ich die
+// Funktion schreibe, fluchen werde ich gleich.
+static void _search(string fname, string from)
+{
+    // <from> ist jetzt anzuguckende/zu bearbeitende Raum.
+
+    // Die aktuelle Liste an abgesuchten Raeumen, Raeume, in denen
+    // alle Wege bereits eingetragen sind.
+    string* rooms_searched=searchlist[fname,SL_VISITED];
+
+    // Zielort
+    string to = searchlist[fname,SL_DESTINATION];
+
+    // moegliche Ziele
+    mapping targets=searchlist[fname,SL_CANDIDATES];
+
+    // wizlevel
+    int seer = searchlist[fname,SL_WIZLEVEL];
+
+    // para
+    int para=searchlist[fname,SL_PARA];
+
+    // Noch zu bearbeitende Raeume
+    string* to_do=searchlist[fname,SL_TODO];
+
+    DBG("_search() start.");
+
+    while ((get_eval_cost()>900000) && from && (from != to))
+    {
+        // Fuege den aktuellen Raum (from) als Weg hinzu
+        DBG(sprintf("Fuege hinzu: %s",from));
+        rooms_searched+=({from});
+        // Aus den Raeumen drumrum werden die Ausgaenge gesucht
+        // und zur Wegliste hinzugefuegt.
+
+        // Pfade von <from> weg aus der Liste holen
+        mixed* paths=pathmap[_get_prefix(from)][from];
+
+        // Liste der Verbindungen von <from> durchgehen
+        foreach (mixed conn:paths)
+        {
+            int newcost;
+            // wenn der Raum schonmal gesehen wurde, ist hier nix mehr noetig.
+            if (member(rooms_searched,conn[CONN_DEST])>-1)
+            {
+                DBG(sprintf("Raum %s hab ich schon!",conn[CONN_DEST]));
+            }
+            else
+            {
+                // Verbindung je nach Flag nicht nutzen. Ausserdem:
+                // wenn die Verbindung in eine Parawelt fuehrt, aber kein
+                // Seherstatus gegeben ist, existiert die Verbindung nicht.
+                // Weiterhin kommt eine Verbindung nicht in Frage, wenn sie in
+                // eine andere Parawelt fuehrt, weil der Weltuebergang nur an
+                // bestimmten Orten moeglich ist. Ausnahme davon sind Wechsel
+                // zwischen Normalwelt und Parawelt.
+                if (
+                    (conn[CONN_FLAGS] & CFLAG_DONTUSE)
+                    || (conn[CONN_FLAGS] & CFLAG_DUPLICATE)
+                    || (!seer && conn[CONN_PARA])
+                    || (conn[CONN_PARA] && conn[CONN_PARA] != para)
+                    )
+                    continue;
+                newcost = targets[from,SLT_COSTS]; // kosten bis hierher
+                // + Kosten in naechsten Raum.
+                newcost += _calc_cost(conn,seer,para);
+                // es koennte sein, dass es Verbindungen mit gleichem
+                // Start/Ziel gibt, aber unterschiedlichen Kosten. In diesem
+                // Fall wuerde das Ziel der aktuellen Verbindung mehrfach
+                // hinzugefuegt und das letzte gewinnt. Daher sollte diese
+                // Verbindung nur hinzugefuegt werden, wenn noch keine
+                // Verbindung in den Zielraum existiert, die billiger ist.
+                if (member(targets,conn[CONN_DEST])
+                    && newcost >= targets[conn[CONN_DEST],SLT_COSTS])
+                    continue;
+                // Weg bis hierher plus naechsten Raum
+                string* new_way = targets[from,SLT_WAY];
+                new_way += ({conn[CONN_DEST]});
+                // Kommandos bis hierher plus naechstes Kommando
+                string* new_commands = targets[from,SLT_COMMANDS];
+                new_commands += ({conn[CONN_CMD]});
+                // naechsten Raum in Todo fuer die Pruefung unten vermerken
+                to_do += ({conn[CONN_DEST]});
+                // Und Kosten/Weg/Kommandos bis zum naechsten Raum unter
+                // targets/Kandidaten ablegen.
+                targets += ([conn[CONN_DEST]:newcost;new_way;new_commands]);
+            }
+        }
+        // Nachdem die Raeume alle zu den Kandidaten an moeglichen Wegen
+        // hinzugefuegt wurden, wird nun der neueste kuerzeste Weg gesucht.
+        // Natuerlich nur aus denen, die schon da sind.
+        if (sizeof(to_do)>0)
+        {
+            // Kosten des ersten todo als Startwert nehmen
+            string minraum;
+            int mincosts=__INT_MAX__;
+            // und ueber alle todos laufen um das Minimum zu finden.
+            foreach(string raum: to_do)
+            {
+                if (mincosts>targets[raum,SLT_COSTS])
+                {
+                    minraum = raum;
+                    mincosts=targets[raum,SLT_COSTS];
+                }
+            }
+            DBG(sprintf("Neuer kuerzester Raum: %s",minraum));
+
+            // der billigst zu erreichende Raum wird das neue <from> fuer den
+            // naechsten Durchlauf
+            from=minraum;
+            // und natuerlich aus dem to_do werfen.
+            to_do-=({minraum});
+            DBG(sprintf("Anzahl untersuchter Raeume: %d, Todo: %d, "
+                  "Kandidaten: %d",
+                  sizeof(rooms_searched),sizeof(to_do),sizeof(targets)));
+        }
+        else
+        {
+            from=0;
+        }
+    } // while ende
+
+    if (!from)
+    {
+        // Hmpf...
+        TELL_USER("Kein Weg gefunden!");
+        if (searchlist[fname, SL_CALLBACK])
+            funcall(searchlist[fname,SL_CALLBACK],
+                    searchlist[fname,SL_START], to, para,
+                    0,0,0);
+        m_delete( searchlist, fname );  // suchauftrag entsorgen
+        return;
+    }
+    else if (from==to)
+    {
+        // JUCHU!
+        TELL_USER(sprintf("Weg gefunden! %O,%O,%O",
+              targets[to,SLT_COSTS],targets[to,SLT_WAY],
+              targets[to,SLT_COMMANDS]));
+        add_to_cache(searchlist[fname,SL_START], to, para,
+                     targets[to,SLT_COSTS],targets[to,SLT_WAY],
+                     targets[to,SLT_COMMANDS]);
+        if (searchlist[fname, SL_CALLBACK])
+            funcall(searchlist[fname,SL_CALLBACK],
+                    searchlist[fname,SL_START], to, para,
+                    targets[to,SLT_COSTS],targets[to,SLT_WAY],
+                    targets[to,SLT_COMMANDS]);
+        m_delete( searchlist, fname );  // suchauftrag entsorgen
+        return;
+    }
+
+    // Wenn das alles nicht war, waren es die Eval-Ticks. Mist. Spaeter
+    // weitersuchen...
+    searchlist[fname,SL_VISITED]=rooms_searched;
+    searchlist[fname,SL_CANDIDATES]=targets;
+    searchlist[fname,SL_TODO]=to_do;
+    call_out("_search",2,fname,from);
+}
+
+
+// Die Daten ueber Raeume werden quasi gehasht abgespeichert, damit wir nicht
+// so schnell an die Begrenzung bei Arrays und Mappings stossen.
+// Dabei ist der 'Hash' der Anfang des Pfades.
+private string _get_prefix( string path )
+{
+    string *tmp;
+
+    tmp = explode( path, "/" );
+
+    // Bei Raeumen unterhalb von /d/* wird /d/region/magiername benutzt
+    if ( strstr(path, "/d/") == 0)
+        return implode( tmp[0..3], "/" );
+    // Ansonsten die ersten beiden Teilpfade, falls soviele vorhanden sind
+    else if ( sizeof(tmp) < 4 )
+        return implode( tmp[0..1], "/" );
+    else
+        return implode( tmp[0..2], "/" );
+}
+
+// Reset und Datenhaltung
+// reset wird alle 3h gerufen, um das Savefile immer mal wieder zu speichern.
+// Datenaufraeumen aber nur einmal pro Tag.
+public void reset()
+{
+    if ( time_to_clean_up <= time() )
+    {
+        // Einmal pro Tag Reset zum Aufraeumen der Datenbank
+        rm(DUMPFILE".raeume");
+        rm(DUMPFILE".conns");
+        time_to_clean_up = time() + 86400;
+        expire_path_cache(m_indices(pathcache));
+        call_out(#'cleanup_data, 30,
+                  sort_array(m_indices(pathmap), #'</*'*/), 0 );
+
+    }
+    save_object(SAVEFILE);
+    set_next_reset(10800);
+}
+
+protected void expire_path_cache(string *startrooms) {
+    foreach(string start : startrooms) {
+        mapping targets = pathcache[start];
+        foreach (string target, mapping paths: targets) {
+            foreach(int para, struct path_s p: paths) {
+                if (p->ttl < time()
+                    || !(p->flags & PFLAG_PERMANENT) )
+                    m_delete(paths,para);
+            }
+            if (!sizeof(paths))
+                m_delete(targets, target);
+        }
+        startrooms-=({start});
+        if (!sizeof(targets))
+            m_delete(pathcache, start);
+        // recht grosser Wert, weil immer komplette Startraeume in einem
+        // erledigt werden.
+        if (get_eval_cost() < 1000000)
+            break;
+    }
+    if (sizeof(startrooms))
+        call_out(#'expire_path_cache, 4, startrooms);
+}
+
+// Datenbank aufraeumen
+protected void cleanup_data( string *names, int idx )
+{
+    int size, i, j, k;
+    string *rooms;
+    mixed *paths;
+
+    size = sizeof(names);
+
+    // Ein bisserl mitloggen, damit wir Schwachstellen im System auch finden.
+    if ( !idx ){
+        LOG( sprintf("=== %s: Starte cleanup_data(): %O Gebiete, %O Raeume "
+                     + "...\n", strftime("%y%m%d-%H%M%S"), size,
+                     show_statistic(1, 1) ) );
+    }
+    else {
+        LOG( sprintf("%s: Setze cleanup_data() fort.\n",
+              strftime("%y%m%d-%H%M%S") ) );
+    }
+
+    // Brav gesplittet, damit es kein Lag gibt.
+    // Die Grenze ist recht hoch angesetzt, da immer gleich komplette
+    // Teilbaeume aufgeraeumt werden.
+    while ( get_eval_cost() > 1100000 && idx < size ){
+        rooms = sort_array( m_indices(pathmap[names[idx]]), #'</*'*/ );
+
+        for ( i = sizeof(rooms); i--; ){
+            paths = pathmap[names[idx]][rooms[i]];
+            int conn_count = sizeof(paths);
+            mapping cmds=m_allocate(sizeof(paths));
+
+            for ( j = conn_count; j--; ) {
+                mixed conn = paths[j];
+                for ( k = 0; k < 2; k++ ) { // Spieler/Sehereintraege
+                    // Diese Verbindung hat noch keiner genutzt bisher
+                    if ( !conn[CONN_TIMES][k] )
+                        continue;
+                    // Wenn Verbindung permanent, ueberspringen
+                    if (conn[CONN_FLAGS] & CFLAG_PERMANENT)
+                        continue;
+
+                    if ( conn[CONN_USERS][k] == -1 // 'bekanntes' Gebiet
+                         && time() - conn[CONN_TIMES][k] > TIMEOUT_OLD ){
+                        // LOG( sprintf("*** Loesche alte "
+                        //             + (k ? "Seher" : "Spieler")
+                        //             + "-Verbindung %O.\n", paths[j]) );
+                        conn[CONN_USERS][k] = ({});
+                        conn[CONN_TIMES][k] = 0;
+                    }
+                    else if ( conn[CONN_USERS][k] != -1 // 'neues' Gebiet
+                              && time() - conn[CONN_TIMES][k] > TIMEOUT_NEW ){
+                        conn[CONN_USERS][k] = ({});
+                        conn[CONN_TIMES][k] = 0;
+                    }
+                }
+
+                // Wenn eine Verbindung weder von Spielern noch von Sehern
+                // benutzt wurde in letzter Zeit und sie keine permanented
+                // ist, Verbindung ganz loeschen.
+                if (!(conn[CONN_FLAGS] & CFLAG_PERMANENT)
+                    && !conn[CONN_TIMES][CONN_T_PLAYERS]
+                    && !conn[CONN_TIMES][CONN_T_SEERS] ) {
+                    paths[j..j] = ({}); // conn rauswerfen
+                    conn_count--;
+                }
+                else {
+                    // Connections nach Kommandos speichern.
+                    if (member(cmds, conn[CONN_CMD]))
+                        cmds[conn[CONN_CMD]] += ({conn});
+                    else
+                        cmds[conn[CONN_CMD]] = ({conn});
+                    // und das duplicate flag loeschen, das wird spaeter neu
+                    // gesetzt, wenn noetig.
+                    // (There is no chance that a concurrent search will check
+                    // these connections until they are marked again.)
+                    conn[CONN_FLAGS] &= ~CFLAG_DUPLICATE;
+                    // Connections dumpen
+                    if (!(conn[CONN_FLAGS] & CFLAG_DUPLICATE))
+                    {
+                      mixed u=({conn[CONN_USERS][CONN_U_PLAYERS],
+                              conn[CONN_USERS][CONN_U_SEERS] });
+                      write_file(DUMPFILE".conns",sprintf(
+                           "%s|%s|%s|%d|%d|%d|%d\n",
+                           rooms[i],conn[CONN_DEST],conn[CONN_CMD],
+                           conn[CONN_EXITTYPE],conn[CONN_PARA],
+                           (u[0]==-1 ? 0 : NEEDED-sizeof(u[0])),
+                           (u[0]==-1 ? 0 : NEEDED-sizeof(u[0]))
+                           ));
+                    }
+                }
+            }
+
+            // Ein Raum ohne Verbindungen frisst nur Platz in der Datenbank
+            if ( !conn_count ){
+                // LOG( sprintf("*** Loesche kompletten Raum %O\n", rooms[i]) );
+                m_delete( pathmap[names[idx]], rooms[i] );
+            }
+            else {
+                // wenn Kommandos nicht eindeutig sind, dann werden die
+                // entsprechenden Verbindungen gekennzeichnet. Alle in Frage
+                // kommenden Verbindungen sind jeweils schon zusammensortiert.
+                foreach(string cmd, mixed conns : cmds) {
+                    if (sizeof(conns)>1)
+                        check_and_mark_duplicate_conns(conns);
+                }
+                // ggf. geaendertes Verbindungsarray wieder ins Mapping
+                // schreiben.
+                pathmap[names[idx]][rooms[i]] = paths;
+                // Raum/Node ins Dump schreiben
+                write_file(DUMPFILE".raeume",sprintf(
+                      "%s\n",rooms[i]));
+            }
+        }
+        idx++;
+    }
+    if ( idx >= size ) {
+        LOG( sprintf("=== %s: Beende cleanup_data()! Uebrig: %O Raeume.\n",
+                     strftime("%y%m%d-%H%M%S"), show_statistic(1, 1) ) );
+    }
+    else {
+        call_out( #'cleanup_data, 2, names, idx );
+        LOG( sprintf("%s: WARTE 20s bei Evalcost %O\n",
+                     strftime("%y%m%d-%H%M%S"), get_eval_cost()) );
+    }
+}
+
+// Beim Entladen speichern
+public varargs int remove( int silent )
+{
+    // Vor dem Entfernen noch schnell die Daten sichern
+    save_object(SAVEFILE);
+    destruct(this_object());
+
+    return 1;
+}
+
+
+// Zum Debuggen. Nur für Tiamak. Bzw. nun fuer Zook, Vanion, Rumata
+#define ALLOWED ({"zook","vanion","rumata","zesstra","humni"})
+#define MATCH(x,y) (sizeof(regexp(x,y)))
+public mapping get_pathmap()
+{
+  if ( !this_interactive() || !MATCH(ALLOWED,getuid(this_interactive()))
+       || !previous_object() || !MATCH(ALLOWED,getuid(previous_object())))
+        return ([]);
+
+    return pathmap;
+}
+public mapping get_pathcache()
+{
+  if ( !this_interactive() || !MATCH(ALLOWED,getuid(this_interactive()))
+       || !previous_object() || !MATCH(ALLOWED,getuid(previous_object())))
+        return ([]);
+
+    return pathcache;
+}
+
+#undef ALLOWED
+#undef MATCH
diff --git a/p/daemon/routingd.c b/p/daemon/routingd.c
new file mode 100644
index 0000000..b293382
--- /dev/null
+++ b/p/daemon/routingd.c
@@ -0,0 +1,202 @@
+#pragma strong_types,save_types
+#pragma no_clone,no_shadow
+
+#include <wizlevels.h>
+
+#define DB_NAME "/p/daemon/save/routingd"
+#define MAX_LEN 50
+#define TP this_player()
+
+mapping domains;
+mapping exits;
+mapping targets;
+static mapping directions;
+static mapping reverse_directions;
+
+void create() {
+    seteuid(getuid());
+  if (!restore_object(DB_NAME)) {
+    domains=(["/d/":1]);
+    exits=([]);
+    targets=([]);
+  }
+
+  // Umsetzung zur Speicherersparnis:
+  directions=(["norden":1,
+	       "nordosten":2,
+	       "osten":3,
+	       "suedosten":4,
+	       "oben":5,
+	       "sueden":-1,
+	       "suedwesten":-2,
+	       "westen":-3,
+	       "nordwesten":-4,
+	       "unten":-5
+	       ]);
+  reverse_directions=([1:"norden",
+		       2:"nordosten",
+		       3:"osten",
+		       4:"suedosten",
+		       5:"oben",
+		       -1:"sueden",
+		       -2:"suedwesten",
+		       -3:"westen",
+		       -4:"nordwesten",
+		       -5:"unten"
+		       ]);
+}
+
+// Fuer Debug-Zecke:
+mixed query_exits(string room) {return exits[room];}
+mixed query_targets(string typ) {return targets[typ];}
+
+int AddDomain(string dom, int val) {
+  // Fuegt neue Domain hinzu, in der Routing moeglich sein soll.
+  // +1: Einschliessen
+  // -1: Ausschliessen
+  if (!IS_ARCH(TP)
+      || val<-1
+      || val>1)
+    return 0;
+  if (!val)
+    domains=m_copy_delete(domains,dom);
+  else
+    domains[dom]=val;
+  save_object(DB_NAME);
+  return 1;
+}
+
+int RegisterTarget(string type, string fname) {
+  // Registriert Raum als zu einer Gruppe von Zielen zugehoerig
+  // (z.B. RegisterTarget("pub","/d/ebene/room/PortVain/pub2"))
+  mapping map_ldfied;
+
+  if (!type || !fname)
+    return 0;
+  type=lower_case(type);
+  if (type[0..1]!="##")
+    type="##"+type;
+  if (!mappingp(map_ldfied=targets[type]))
+    map_ldfied=([]);
+  map_ldfied[fname]=1;
+  targets[type]=map_ldfied;
+  return 1;
+}
+
+int RegisterExit(string start, mixed richtungen, string ziel) {
+  // Registriert Ausgang in der angegebenen Richtung von Start nach Ziel,
+  // wenn Routing dort moeglich sein soll.
+  string *dirs;
+  int i,sz,ok,pos;
+  mixed ex,x,richtung;
+  
+  if (stringp(richtungen))
+    richtungen=({richtungen});
+  if (!start || !pointerp(richtungen) || !ziel)
+    return 0;
+  dirs=old_explode(start,"/");sz=sizeof(dirs)-1;x="/";ok=0;
+  for (i=0;i<sz;i++) {
+    x=x+dirs[i]+"/";
+    if (domains[x]==-1) // Subdomain ausgeschlossen?
+      return 0;
+    if (domains[x]==1) // Domain erlaubt?
+      ok=1;
+  }
+  if (!ok)
+    return 0;
+
+  if (!pointerp(ex=exits[start])) {
+    if (file_size(start+".c")<=0) // Beim ersten Ausgang auf VC testen
+      return 0; // Keine Virtual Compiler Raeume routen
+    ex=({});
+  }
+  for (i=sizeof(richtungen)-1;i>=0;i--) {
+    richtung=richtungen[i];
+    if (!(x=directions[richtung]))
+      x=richtung;
+  
+    pos=member(ex,x);
+    if (pos>=0 && pos+1<sizeof(ex)) // Richtung schon eingetragen?
+     ex[pos+1]=ziel; // Wurde umdefiniert
+    else
+      ex+=({x,ziel}); // ({ richtung, ziel })
+  }
+  exits[start]=ex;
+  return 1;
+}
+
+// Low-Level FindRoute :-)
+mapping do_find_route(string start, mapping is_target) {
+  mapping erreicht;
+  int i,j,k;
+  mixed *newx, *actx, ex;
+  string room, dir, x;
+  
+  erreicht=([start:"##START"]);
+  newx=({start});
+  for (k=MAX_LEN;k>=0;k--) {
+    actx=newx; // In diesem Schritt die neu erreichten Raeume pruefen
+    newx=({}); // Noch keine Raeume neu erreicht
+    for (i=sizeof(actx)-1;i>=0;i--) {
+      room=actx[i];
+      // printf("%O\n",room);
+      if (!pointerp(ex=exits[room]))
+	continue;
+      // printf("-> %O\n",ex);
+      for (j=sizeof(ex)-1;j>=1;j-=2) {
+	x=ex[j]; // Zielraum
+	// printf("-> %O\n",x);
+	if (!erreicht[x]) { // Wurde bisher noch nicht erreicht
+	  erreicht[x]=({room,ex[j-1]});
+	  if (is_target[x]) { // Ein Ziel wurde erreicht
+	    erreicht["##ZIEL"]=erreicht[x]; // Kann ja auch Gruppe sein
+	    return erreicht;
+	  }
+	  newx+=({x});
+	}
+      }
+    }
+  }
+  return 0;
+}
+
+varargs mixed FindRoute(string start, string ziel, int etyp) {
+  mapping res,ziele;
+  string x;
+  mixed ex,r,result;
+  int k;
+  
+  if (!start || !ziel)
+    return 0;
+  if (!mappingp(ziele=targets[ziel]))
+    ziele=([ziel:1]);
+  if (!mappingp(res=do_find_route(start, ziele)))
+    return 0; // nicht gefunden
+  if (etyp)
+    result=({});
+  else
+    result=([]);
+  x="##ZIEL";
+  for (k=MAX_LEN+2;k>=0;k--) {
+    ex=res[x];
+    if (ex=="##START") // Fertig.
+      return result;
+    if (!(r=reverse_directions[ex[1]])) // richtige Richtungen eintragen.
+      r=ex[1];
+    x=ex[0];
+    if (etyp)
+      result=({r})+result;
+    else
+      result[x]=r;
+  }
+  return 0;
+}
+
+void save() {
+	save_object(DB_NAME);
+}
+
+void reset() {
+	set_next_reset(43200); // Alle 12 Stunden speichern reicht
+	save();
+}
diff --git a/p/daemon/ruestungen.c b/p/daemon/ruestungen.c
new file mode 100644
index 0000000..52029fb
--- /dev/null
+++ b/p/daemon/ruestungen.c
@@ -0,0 +1,56 @@
+#pragma strong_types,save_types
+#pragma no_clone,no_shadow
+
+#include <wizlevels.h>
+
+#define SAVEFILE  "/p/daemon/save/ruestungen"
+#define DUMPFILE "/log/RUESTUNGEN"
+
+mapping armours;
+
+void create()
+{
+    seteuid(getuid(this_object()));
+
+    if (!restore_object(SAVEFILE))
+    {
+        armours = ([]);
+    }
+}
+
+void save_me()
+{
+    save_object(SAVEFILE);
+}
+
+void RegisterArmour()
+{   object  ob;
+    string  id;
+
+    if (!objectp(ob=previous_object()) || 
+      member(inherit_list(ob),"/std/armour.c")==-1)
+        return;
+    id = old_explode(object_name(ob),"#")[0];
+    if (member(armours,id))
+        return;
+    armours += ([ id : 1]);
+    save_me();
+}
+
+int Dump()
+{   string  *ind;
+    int     i;
+
+    if (!this_interactive() || !IS_LORD(this_interactive()))
+        return -1;
+    if (file_size(DUMPFILE)>=0)
+        rm(DUMPFILE);
+
+    ind = sort_array(m_indices(armours),#'>);
+    if ((i=sizeof(ind))<1)
+        return 0;
+    write_file(DUMPFILE,sprintf("Dumped: %s\n",dtime(time())));
+    for (--i;i>=0;i--)
+      write_file(DUMPFILE,ind[i]+"\n");
+    return 1;
+}
diff --git a/p/daemon/sastatd.c b/p/daemon/sastatd.c
new file mode 100644
index 0000000..c02873e
--- /dev/null
+++ b/p/daemon/sastatd.c
@@ -0,0 +1,135 @@
+// MorgenGrauen MUDlib
+/** \file /p/daemon/sastatd.c
+* Statistikdaemon fuer Skill-Attribute
+* Der Daemon loggt SA-Modifier und schreibt sie von Zeit zu Zeit auf die
+* Platte.
+* \author Zesstra
+* \date 06.08.2008
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types,save_types
+#pragma no_clone,no_inherit,no_shadow
+#pragma pedantic,range_check
+
+#include <defines.h>
+
+/** anfaengliche Groesse des Log-Puffers
+  */
+#define DEFAULT_BUFFER_LEN 200
+/** ab dieser Anzahl an Eintraegen wird im naechsten Backend-Zyklus vom Driver
+ * per Callout der Puffer weggeschrieben. */
+#define BUFFERTHRESHOLD 150
+
+/** Logfile fuer die Stats
+  */
+#define STATLOG "SASTATS"
+/** max. Logfilegroesse (3 MB)
+  */
+#define MAXLOGSIZE 3145728
+
+struct log_entry_s {
+  int timestamp;
+  string liv;
+  string caster;
+  string atrname;
+  mixed value;
+  int duration;
+};
+
+struct log_buffer_s {
+  mixed buf;  // Array von log_entry
+  int bufferlen; // Laenge des Buffers, kann dyn. vergroessert werden.
+  int sp; // oberster Eintrag im Buffer (letztendlich nen Stack).
+};
+
+/** speichert Logeintraege mit indiv. Modifiereintraegen.
+ * Array von log_entry_s mit Info ueber Pufferlaenge und zuletzt
+ * geschriebenem Arrayeintrag.
+*/
+struct log_buffer_s logbuf = (<log_buffer_s>
+    buf : allocate(DEFAULT_BUFFER_LEN),
+    bufferlen : DEFAULT_BUFFER_LEN,
+    // -1, weil der sp beim Loggen zuerst erhoeht wird und sonst Elemente 0
+    // leer bleibt.
+    sp : -1 );
+
+/** \def DEBUG
+  Outputs debug message to Maintainer, if Mainteiner is logged in. 
+*/
+#ifndef DEBUG
+#define DEBUG(x)  if (find_player("zesstra"))\
+            tell_object(find_player("zesstra"),\
+                                      "EDBG: "+x+"\n")
+#endif
+
+private string FormatLogEntry(struct log_entry_s entry) {
+  return sprintf("[%s] %s, %s, %s, %s, %d\n", 
+      strftime("%d%m%y-%H%M%S",entry->timestamp),
+      entry->liv, entry->caster, entry->atrname,
+      entry->value, entry->duration);
+}
+
+private void WriteBuffer() {
+  // relevanten Teil des Puffers extrahieren.
+  mixed buf = logbuf->buf[0..logbuf->sp];
+  // dann alle Eintraege in Strings mappen
+  buf = map(buf, #'FormatLogEntry);
+  // wegschreiben
+  log_file(STATLOG, sprintf("%@s",buf), MAXLOGSIZE);
+  // sp auf den Pufferanfang setzen
+  logbuf->sp = -1;
+}
+
+public void LogModifier(object caster, string atrname,
+                        mixed value, int duration) {
+  // nur Spieler sind interessant
+  if (!query_once_interactive(previous_object()))
+    return;
+
+  // sp um eins erhoehen und schauen, ob die Puffergroesse ueberschritten
+  // wurde. Wenn ja, Puffer vergroessern.
+  logbuf->sp++;
+  if (logbuf->sp >= logbuf->bufferlen) {
+    logbuf->buf = logbuf->buf + allocate(50);
+    logbuf->bufferlen += 50;
+  }
+  // eintragen. Interessanterweise ist es tatsaechlich kein Problem, wenn
+  // Structmember und lokale Variablen hier den gleichen Namen habem. ;-)
+  logbuf->buf[logbuf->sp] = (<log_entry_s>
+      timestamp : time(),
+      liv : getuid(previous_object()), // nur Spieler
+      caster : object_name(caster),
+      atrname : atrname,
+      value : to_string(value), // wegen (fluechtiger) Closures
+      duration : duration );
+  // wegschreiben veranlassen
+  if (logbuf->sp > BUFFERTHRESHOLD
+      && find_call_out(#'WriteBuffer) == -1)
+    call_out(#'WriteBuffer, 0);
+}
+
+protected void create() {
+  seteuid(getuid(ME));
+}
+
+varargs public int remove(int silent) {
+  if (logbuf->sp > -1)
+    WriteBuffer();
+  destruct(ME);
+  return 1;
+}
+
+void reset() {
+  // ggf. Puffer wegschreiben.
+  if (logbuf->sp > BUFFERTHRESHOLD)
+    WriteBuffer();
+  // ggf. sehr grosse Puffer verkleinern
+  if (logbuf->bufferlen > DEFAULT_BUFFER_LEN * 2) {
+    logbuf->buf = allocate(DEFAULT_BUFFER_LEN);
+    logbuf->bufferlen = DEFAULT_BUFFER_LEN;
+    logbuf->sp = -1;
+  }
+}
+
diff --git a/p/daemon/svn2news.c b/p/daemon/svn2news.c
new file mode 100644
index 0000000..8cece79
--- /dev/null
+++ b/p/daemon/svn2news.c
@@ -0,0 +1,55 @@
+// MorgenGrauen MUDlib
+//
+// svn2news.c -- Setzt die Subversion-Eintraege in magier.mudlib
+//
+// $Id$
+
+// Erstellt auf Basis von rcs2news von Zook.
+
+#pragma strict_types,save_types
+#pragma no_clone,no_shadow
+
+#include <daemon.h>
+
+#define NEWSFILE "/p/daemon/save/svn2news.txt"
+#define NEWSFILEOLD "/p/daemon/save/svn2news.old"
+
+#define DEBUG(str) if (find_player("zook"))  \
+  tell_object(find_player("zook"), sprintf("svn2news: DEBUG: %O\n",str))
+
+static void checkrcs()
+{
+  string str;
+  mixed art;
+  // Letzte Eintrag sollte ein wenig her sein, damit es keine
+  // Ueberschneidungen gibt.
+  if (file_time(NEWSFILE)+1800 > time()) return;
+  if (!(str=read_file(NEWSFILE,0,10000)) || (str=="")) return;
+  art=({"magier.mudlib","SVN2NEWS",0,0,
+        "SVN-Eintraege vom "+dtime(time()),str});
+  if (("/secure/news"->WriteNote(art,1)>=1) ||
+      (("/secure/news"->RemoveNote("magier.mudlib",0)>=1) &&
+       ("/secure/news"->WriteNote(art,1)>=1)))
+    catch(rename(NEWSFILE,NEWSFILEOLD);publish);
+  else log_file("SVN2NEWS",dtime(time())+
+		" : Kann RCS-Eintraege nicht nach magier.mudlib posten!\n");
+}
+
+void reset()
+{
+  // call_out() noetig, da sonst kein weiteres reset() gerufen wird
+  // reset() wird nur gerufen bei einem call einer anderen Funktion
+  // nach einem Reset
+  call_out("checkrcs",1);
+}
+
+void create()
+{
+  seteuid(getuid());
+  reset();
+}
+
+void clean_up(int refc)
+{
+  return 0;
+}
diff --git a/p/daemon/teammaster.c b/p/daemon/teammaster.c
new file mode 100644
index 0000000..76e8ca0
--- /dev/null
+++ b/p/daemon/teammaster.c
@@ -0,0 +1,84 @@
+#pragma strong_types,save_types
+#pragma no_shadow,no_clone
+
+#include <living/team.h>
+#include <wizlevels.h>
+#define ME this_object()
+#define PO previous_object()
+
+static mapping team_names;
+static mapping team_names_reverse;
+
+void create() {
+
+  team_names=([]);         // Objekt -> Name
+  team_names_reverse=([]); // Name -> Objekt
+}
+
+static mixed DoRegisterTeam(object ob, string name) {
+  mixed old;
+
+  old=team_names[ob];
+  m_delete(team_names_reverse,old); // Namen Freigeben
+  if (!name) { // UnRegister
+    m_delete(team_names,ob);
+    return name;
+  }
+  team_names[ob]=name;
+  team_names_reverse[name]=ob;
+  return name;
+}
+
+varargs string RegisterTeam(string name) {
+  int i,min;
+
+  if (member(inherit_list(PO),TEAM_OBJECT+".c")<0)
+    return 0;
+  if (stringp(name)){
+    if (!team_names_reverse[name]) // Name noch nicht vergeben
+      return DoRegisterTeam(PO,name);
+    return 0;
+  }
+  min=sizeof(m_indices(team_names_reverse))+2;
+  for (i=1;i<min;i++)
+    if (!team_names_reverse[sprintf("%d",i)]) {
+      min=i;
+      break;
+    }
+  name=sprintf("%d",i);
+  return DoRegisterTeam(PO,name);
+}
+
+void UnregisterTeam() {
+  if (member(inherit_list(PO),TEAM_OBJECT+".c")>=0)
+    DoRegisterTeam(PO,0);
+}
+
+object *ListTeamObjects() {
+  return(m_indices(team_names)-({0}));
+}
+
+string *ListTeamNames() {
+  return(m_indices(team_names_reverse)-({0}));
+}
+
+string *SortedListTeamNames() {
+  return(sort_array(ListTeamNames(),#'<));
+}
+
+void ShowTeamInfos() {
+  object ob;
+  string *ind;
+  int i;
+  
+  write(" --- Teamliste: ---\n");
+  ind=SortedListTeamNames();
+  for (i=sizeof(ind)-1;i>=0;i--)
+    if (objectp(ob=team_names_reverse[ind[i]]))
+      ob->ShowTeamInfo();
+  write(" --- (Ende Teamliste) ---\n");
+}
+
+varargs int remove(int silent) {
+  return 0;
+}
diff --git a/p/daemon/tmp_prop_master.c b/p/daemon/tmp_prop_master.c
new file mode 100644
index 0000000..0ce1de7
--- /dev/null
+++ b/p/daemon/tmp_prop_master.c
@@ -0,0 +1,338 @@
+#pragma strong_types,save_types
+#pragma no_clone,no_shadow
+
+#include <thing/properties.h>
+
+#define SAVEFILE "/p/daemon/save/tmp_prop_master"
+#define PROPMASTER "/p/daemon/tmp_prop_master"
+#define INDEX(a,b) (object_name(a)+"##"+b)
+
+
+#ifdef DEBUG
+#   undef DEBUG
+#endif
+
+#ifdef DEBUG
+#   define DBG(x) if ( find_player("rikus") ) \
+                     tell_object( find_player("rikus"), x );
+#else
+#   define DBG(x)
+#endif
+
+
+mixed *reset_times;
+mapping in_progress;
+
+
+varargs public int remove( int silent );
+public void reset();
+public void print_debug_info();
+static void call_next_update();
+static void do_update();
+public int IsTmpProp( object ob, string prop );
+public void RemoveTmpProp( string prop );
+varargs public void SetTmpProp( object ob, string prop, mixed newval,
+                                int tm, int sp );
+
+
+public void create()
+{
+
+    seteuid(getuid(this_object()));
+    
+    if ( !restore_object(SAVEFILE) ){
+        in_progress = ([]);
+        reset_times = ({});
+    }
+
+    call_next_update();
+}
+
+
+varargs public int remove( int silent )
+{
+    save_object(SAVEFILE);
+    destruct(this_object());
+    
+    return 1;
+}
+
+
+public void reset()
+{
+    save_object(SAVEFILE);
+}
+
+
+public void print_debug_info()
+{
+    printf( "in_progress: %O\nreset_times: %O\n", in_progress, reset_times );
+}
+
+
+static void call_next_update()
+{
+    int dt;
+    
+    while ( remove_call_out("do_update") != -1 )
+        ;
+    
+    if ( sizeof(reset_times) ){
+        dt = reset_times[0][0] - time();
+
+        if ( dt <= 0 )
+            dt = 5;
+        
+        call_out( "do_update", dt );
+    }
+}
+
+
+static void do_update()
+{
+    int i, max;
+    string index;
+    mixed *val;
+
+    DBG( "do_update() aufgerufen!\n" );
+
+    while ( remove_call_out("do_update") != -1 )
+        ;
+    
+    call_out( "do_update", 30 ); // Zur Sicherheit...
+
+    if ( !(max = sizeof(reset_times)) )
+        return;
+    
+    for ( i = 0; i < max && reset_times[i][0] <= time(); i++ ){
+        index = reset_times[i][1];
+
+        if ( !pointerp(val = in_progress[index]) || !objectp(val[0]) )
+	{
+            m_delete( in_progress ,index );
+            continue;
+        }
+
+        DBG( sprintf("Entferne Eintrag %O!\n", index) );
+
+        if ( val[4] ){
+            if ( val[0]->Query(val[1], F_VALUE) == val[3] ){
+                DBG( "Alte Property per SetProp() restauriert!\n" );
+                val[0]->SetProp( val[1], val[2] );
+            }
+        }
+        else {
+            if ( val[0]->Query( val[1], F_QUERY_METHOD ) == val[2] ){
+                val[0]->Set( val[1], 0, F_QUERY_METHOD );
+                DBG( "F_QUERY_METHOD entfernt!\n" );
+            }
+            
+            if ( val[0]->Query( val[1], F_SET_METHOD ) == val[3] ){
+                val[0]->Set( val[1], 0, F_SET_METHOD );
+                DBG( "F_SET_METHOD entfernt!\n" );
+            }
+        }
+
+        m_delete( in_progress ,index );
+    }
+    
+    reset_times = reset_times[i..];
+    call_next_update();
+}
+
+
+// Zur Abfrage, ob eine temporaere Property schon gesetzt ist
+public int IsTmpProp( object ob, string prop )
+{
+    mixed oldval;
+
+    if ( !objectp(ob) || !prop )
+        return 0;
+    
+    if ( (oldval = in_progress[INDEX(ob, prop)])
+         && ob == oldval[0] && prop == oldval[1] )
+        return 1;
+    
+    return 0;
+}
+
+
+// Entfernen einer temporaeren Property.
+// Ist nicht zum Entfernen per Hand gedacht, sondern kann nur vom Objekt selber
+// aufgerufen werden. Geschieht, wenn bei gesetzter temporaerer F_SET_METHOD
+// ein fremdes SetProp() auf die Property kommt.
+public void RemoveTmpProp( string prop )
+{
+    string index;
+    mixed *val;
+    int i;
+    
+    DBG( "RemoveTmpProp!\n");
+    if ( !previous_object() || !stringp(prop) || !sizeof(prop) )
+        return;
+
+    index = INDEX(previous_object(), prop);
+    DBG( sprintf("RemoveTmpProp(%O)!\n", index) );
+
+    if ( !(val = in_progress[index]) ){
+        DBG( "Kein Eintrag vorhanden!\n" );
+        return;
+    }
+
+    if ( objectp(val[0]) ){
+        DBG( "Restauriere alten Zustand ...\n" );
+
+        if ( val[4] ){
+            if ( val[0]->Query(val[1], F_VALUE) == val[3] ){
+                DBG( "Alte Property per SetProp() restauriert!\n" );
+                val[0]->SetProp( val[1], val[2] );
+            }
+        }
+        else {
+            if ( val[0]->Query( val[1], F_QUERY_METHOD ) == val[2] ){
+                val[0]->Set( val[1], 0, F_QUERY_METHOD );
+                DBG( "F_QUERY_METHOD entfernt!\n" );
+            }
+            
+            if ( val[0]->Query( val[1], F_SET_METHOD ) == val[3] ){
+                val[0]->Set( val[1], 0, F_SET_METHOD );
+                DBG( "F_SET_METHOD entfernt!\n" );
+            }
+        }
+    }
+
+    for ( i = sizeof(reset_times); i--; )
+        if ( reset_times[i][1] == index ){
+            reset_times[i..i] = ({});
+            break;
+        }
+    
+    DBG( "Eintrag entfernt!\n" );
+    m_delete( in_progress, index );
+}
+
+
+// Property 'prop' im Objekt 'ob' fuer 'tm' Sekunden auf Wert 'newval' setzen.
+// Wenn der Schalter 'sp' gesetzt ist, wird dafuer SetProp() verwendet,
+// anstatt eine F_QUERY_METHOD zu benutzen.
+//
+// ACHTUNG:
+//
+// Das Setzen von 'sp' ist hoechst fehleranfaellig und sollte nur in wenigen
+// Spezialfaellen geschehen, in denen eine SET-Methode aufgerufen werden muss
+// (wie bei P_LIGHT z.B.). Ansonsten bitte den Default-Aufruf ohne 'sp'
+// verwenden!
+// 
+// Es besteht sonst die Gefahr, dass die Property nicht korrekt zurueckgesetzt
+// wird (wenn dieser Master oder 'ob' zerstoert wird - oder beides wie bei einem
+// Crash) oder dass durch Wechselwirkungen mit anderen Query- oder Set-Methoden
+// ein falscher Wert "restauriert" wird.
+
+varargs public void SetTmpProp( object ob, string prop, mixed newval,
+                                int tm, int sp )
+{
+    string index;
+    mixed oldval, tmp;
+    int max, pos, l, r;
+    closure a, b;
+
+    if ( !objectp(ob) || !stringp(prop) || !sizeof(prop) || tm <= 0 )
+        return;
+
+    index = INDEX(ob, prop);
+    DBG( sprintf("SetTmpProp( %O, %O, %O, %O, %O )!\n", ob, prop, newval,
+                 tm, sp) );
+
+    // wenn bereits ein temporaerer Prozess laeuft, diesen nun beenden!
+    if ( (oldval = in_progress[index])
+         && ob == oldval[0] && prop == oldval[1] ){
+        DBG( "Es laeuft bereits ein Vorgang fuer diese Property!\n" );
+        
+        // Wurde die alte temporaere Property per SetProp() gesetzt, muss der
+        // urspruengliche Wert erst restauriert werden. 
+        if ( oldval[4] ){
+            if ( oldval[0]->Query(oldval[1], F_VALUE) == oldval[3] ){
+            DBG( "Alte Property per SetProp() restauriert!\n" );
+            oldval[0]->SetProp( oldval[1], oldval[2] );
+            }
+        }
+        // Ansonsten einfach die F_QUERY- und F_SET_METHOD loeschen.
+        else {
+            if ( oldval[0]->Query( oldval[1], F_QUERY_METHOD ) == oldval[2] ){
+                oldval[0]->Set( oldval[1], 0, F_QUERY_METHOD );
+                DBG( "F_QUERY_METHOD entfernt!\n" );
+            }
+
+            if ( oldval[0]->Query( oldval[1], F_SET_METHOD ) == oldval[3] ){
+                oldval[0]->Set( oldval[1], 0, F_SET_METHOD );
+                DBG( "F_SET_METHOD entfernt!\n" );
+            }
+        }
+        
+        for ( l = sizeof(reset_times); l--; )
+            if ( reset_times[l][1] == index ){
+                reset_times[l..l] = ({});
+                DBG( "Timer-Eintrag entfernt!\n" );
+                break;
+            }
+    }
+
+    if ( tm < 1000000 )
+        tm += time();
+
+    if ( tm < time() )
+        return;
+
+    if ( !sp ){
+        // Default: F_QUERY_METHOD setzen
+        oldval = ob->Set( prop, newval,  F_QUERY_METHOD );
+
+        // F_SET_METHOD, damit die temporaere Property keinen zwischenzeitlich
+        // gesetzten anderen Wert wieder "restauriert".
+
+        a=symbol_function("RemoveTmpProp",this_object());
+        b=symbol_function("SetProp",ob);
+        tmp=ob->Set(prop,lambda(({'set_value,'prop_name}),({#',,({a,'prop_name}),({#'return,({b,'prop_name,'set_value})})})),F_SET_METHOD);
+    }
+    else{
+        // Fuer Spezialfaelle (P_LIGHT z.B.) wird die Property direkt mit
+        // SetProp() gesetzt und spaeter mit dem alten Wert restauriert.
+        // Achtung - extrem fehleranfaellig und wirklich nur fuer Spezialfaelle!
+
+        // Alten Wert mit Query() sichern anstatt mit QueryProp(), um nicht
+        // irgendwelchen F_QUERY_METHODs auf den Leim zu gehen.
+        oldval = ob->Query( prop, F_VALUE );
+
+        // Rueckgabewert des SetProp() speichern, um spaeter erkennen zu
+        // koennen, ob die Property zwischenzeitlich veraendert wurde.
+        tmp = ob->SetProp( prop, newval );
+    }
+
+    in_progress[index] = ({ ob, prop, oldval, tmp, sp });
+    
+    if ( !(max = sizeof(reset_times)) )
+        pos = 0;
+    else{
+        l = 0;
+        r = max - 1;
+        
+        while ( l < r ){
+            pos = (l + r) / 2;
+            
+            if ( tm < reset_times[pos][0] )
+                r = --pos;
+            else
+                l = ++pos;
+        }
+        
+        while ( pos >= 0 && pos < max && tm < reset_times[pos][0] )
+            pos--;
+    }
+    
+    reset_times = reset_times[0..pos] + ({ ({tm, index}) })
+        + reset_times[pos+1..];
+
+    DBG( sprintf("Property an Stelle %O eingefuegt!\n", pos+1) );
+
+    call_next_update();
+}
diff --git a/p/daemon/traveld.c b/p/daemon/traveld.c
new file mode 100644
index 0000000..747fd38
--- /dev/null
+++ b/p/daemon/traveld.c
@@ -0,0 +1,285 @@
+/** Doku:
+public int AddRoute(string transporter, string* l_stops)
+  Beschreibung:
+    Setzt die aktuelle Route eines Transporters.
+    Die Route hier ist allerdings nur die Liste von Haltepunkten - nicht der
+    Rest, der in den Routen in den Transporter abgelegt ist.
+    Eine neue Route ersetzt immer die bisherige Route
+    Wird von /std/transport gerufen, wenn P_NO_TRAVELING nicht gesetzt ist.
+  Rueckgabewerte:
+    - 1: Transporter und Haltepunkt Eingetragen.
+    - 0: Transporter oder Haltepunkt kein string|string*.
+    - -1: Es wurde versucht einen Transporter in /p/ oder /players/
+          einzutragen 
+          und der Eintragende ist kein EM.
+
+public int RemoveTransporter(string|object transporter)
+public int RemoveStop(string|object stop)
+  Beschreibung:
+    Entfernt einen kompletten Transporter oder einen Haltepunkt aus dem
+    Daemon.
+  Rueckgabewerte:
+    - 1: Erfolg.
+    - 0: Transporter|Haltepunkt existiert nicht.
+    - -1: Keine Berechtigung.
+
+public varargs int|object* HasTransporter(object stop, string transporter_id)
+  Beschreibung:
+    Gibt eine Liste der Transporter aus, den den Haltepunkt anlaufen, oder
+    prueft auf einen bestimmten Transporter.
+  Rueckgabewerte:
+    - 0: Haltepunkt ist kein Objekt, dem Haltepunkt sind keine Transporter
+         zugeordnet,
+         die Transporter sind nicht geladen
+         oder, bei angabe von <transporter>: <transporter> faehrt den Hafen
+         nicht an.
+    - object*: Liste mit einem oder mehreren Transportern.
+  Bemerkung:
+    Auch bei der Angabe von <transporter_id> koennen mehrere Objekte
+    zurueckgegeben werden, es wird mittels id() gefiltert.
+
+public mixed RouteExists(object transporter, string dest)
+  Beschreibung:
+    Prueft auf korrekte Parameter und leitet die Anfrage an
+    /std/transport weiter.
+  Rueckgabewerte:
+    - 0: <transporter> ist kein Objekt oder <dest> ist kein String.
+    - Das Ergebnis von transporter->HasRoute(dest);
+
+public mapping QueryTransporters()
+public mapping QueryAllStops() 
+public mapping QueryTransporterByStop(string stop)
+public mapping QueryStopsByTransporter(string transporter)
+  Beschreibung:
+    Gibt das jewilige gefilterte oder ungefilterte Mapping aus.
+  Rueckgabewerte:
+    Mapping mit dem Ergebnis.
+*/
+
+#pragma strong_types,save_types,rtt_checks
+#pragma pedantic,range_check,warn_deprecated
+#pragma warn_empty_casts,warn_missing_return,warn_function_inconsistent
+#pragma no_clone,no_shadow
+
+#include <transport.h>
+#include <wizlevels.h>
+#include <daemon.h>
+
+#define TRANSPORTER 0
+#define STOP 1
+
+#define MEMORY "/secure/memory"
+
+// Key: string Schiff.
+// Value0: string* Haefen, die von <Schiff> angefahren werden.
+mapping transporters = ([]);
+// Key: string Hafen
+// Value0: string* Schiffe, die <Hafen> anfahren.
+mapping stops = ([]);
+
+protected void create()
+{
+  // Vom MEMORY die Daten abholen.
+  // Wenn keine da, war vermutlich Reboot, dann muessen wir warten, bis die
+  // Transporter sich wieder melden. Aber die Mappings muessen ins Memory
+  // geschrieben werden.
+  mapping tmp = MEMORY->Load("stops");
+  if (mappingp(tmp))
+  {
+    stops = tmp;
+  }
+  else
+  {
+    if (MEMORY->Save("stops",stops) != 1)
+      raise_error("Could not save memory to /secure/memory.");
+  }
+  tmp = MEMORY->Load("transporters");
+  if (mappingp(tmp))
+  {
+    transporters = tmp;
+  }
+  else
+  {
+    if (MEMORY->Save("transporters",transporters) != 1)
+      raise_error("Could not save memory to /secure/memory.");
+  }
+}
+
+varargs int remove(int s)
+{
+  destruct(this_object());
+  return 1;
+}
+
+// Loeschfunktion
+private int _remove_data(string|object key, int what)
+{
+  mapping first,second;
+  // Erstmal die Arbeitsmappings befuellen.
+  if(what==TRANSPORTER)
+  {
+    first=transporters;
+    second=stops;
+  }
+  else
+  {
+    first=stops;
+    second=transporters;
+  }
+
+  // Key auf einen String normalisieren.
+  if(objectp(key))
+  {
+    key=object_name(key);
+  }
+
+  if(!member(first,key))
+  {
+    return 0;
+  }
+
+  // Erstmal aus dem jeweils anderen Mapping austragen.
+  foreach(string s : first[key])
+  {
+    // Nicht existent oder nicht zugeordnet -> weiter.
+    if(!member(second,s) || member(second[s],key)==-1)
+    {
+      continue;
+    }
+    second[s]-=({key});
+    if (!sizeof(second[s]))
+      m_delete(second,s);
+  }
+  // Jetzt noch aus dem eigenen Mapping austragen.
+  m_delete(first,key);
+  return 1;
+}
+
+// Ein komplettes Schiff entfernen.
+public int RemoveTransporter(object|string transporter)
+{
+  if (extern_call() && !IS_ARCH(getuid(previous_object()))) return -1;
+  return _remove_data(transporter,TRANSPORTER);
+}
+
+// Entfernt einen kompletten Hafen aus dem Daemon
+public int RemoveStop(object|string stop)
+{
+  if (extern_call() && !IS_ARCH(getuid(previous_object()))) return -1;
+  return _remove_data(stop,STOP);
+}
+
+// Setzt die aktuelle Route eines Transporters.
+// Die Route hier ist allerdings nur die Liste von Haltepunkten - nicht der
+// Rest, der in den Routen in den Transporter abgelegt ist.
+// Wird von /std/transport gerufen, wenn P_NO_TRAVELING nicht gesetzt ist.
+public int AddRoute(string transporter, string* l_stops)
+{
+  if (!stringp(transporter) || !pointerp(l_stops) || !sizeof(l_stops))
+    return 0;
+
+  if ((transporter[0..2] == "/p/" || transporter[0..8] == "/players/")
+      && !IS_ARCH(getuid(previous_object()))) 
+  {
+    return -1;
+  }
+
+  // Damit sind alle Abfragen auf find_object(transporter|stop) ueberfluessig,
+  // im Zweifelsfall knallt es naemlich hier. ;-)
+  transporter = object_name(load_object(transporter));
+
+  // Wenn die route bereits existiert, austragen. Dies ist noetig, weil die
+  // Haltepunkte ja auch bereinigt werden muessen.
+  if (member(transporters, transporter))
+    RemoveTransporter(transporter);
+
+  // Transporter eintragen, die Route wird dabei immer ueberschrieben, wenn
+  // der Transporter schon bekannt ist. Ziel: hier ist immer die aktuell
+  // konfigurierte Route drin.
+  m_add(transporters, transporter, l_stops);
+
+  // Nach obigen Schema, nur diesmal Haltepunkte und zugehoerige Schiffe 
+  // eintragen.
+  foreach(string stop : l_stops)
+  {
+    if (!member(stops,stop))
+    {
+      m_add(stops, stop, ({transporter}));
+    }
+    else if (member(stops[stop],transporter) == -1)
+    {
+      stops[stop] += ({ transporter });
+    }
+  }
+  return 1;
+
+}
+
+// Abfrage ob ein Schiff einen Hafen anfaehrt.
+public varargs int|object* HasTransporter(object stop,
+                                          string transporter_id)
+{
+  if (!objectp(stop)) return 0;
+
+  <string|object>* trans=stops[object_name(stop)];
+  if (!pointerp(trans)) return 0;
+
+  // Eigentlich sollten sich Transporter ordentlich abmelden... Aber zur
+  // Sicherheit die nicht gefundenen Objekte ausfiltern.
+  trans = map(trans, #'find_object) - ({0});
+
+  // Wenn ne bestimmte ID gesucht wird, nur diese Transporter liefern.
+  if (stringp(transporter_id))
+    trans = filter_objects(trans, "id", transporter_id);
+
+  return sizeof(trans) ? trans : 0;
+}
+
+public mixed RouteExists(object transporter, string dest)
+{
+  if (!objectp(transporter) || !stringp(dest))
+  {
+    return 0;
+  }
+  return transporter->HasRoute(dest);
+}
+
+public mapping QueryTransporters()
+{
+  return deep_copy(transporters);
+}
+
+public mapping QueryAllStops()
+{
+  return deep_copy(stops);
+}
+
+// Liefert alle Transporter mit ihren Haltepunkten zurueck, die diesen Halt ansteuern.
+public mapping QueryTransportByStop(string stop)
+{
+  mapping res=m_allocate(4);
+  foreach(string s, string* arr : transporters)
+  {
+    if(member(arr,stop)!=-1)
+    {
+      m_add(res,s,arr);
+    }
+  }
+  return res;
+}
+
+// Liefert alle Haltepunkte mit ihren Transportern zurueck, die dieser
+// transporter ansteuert.
+public mapping QueryStopsByTransporter(string transporter)
+{
+  mapping res=m_allocate(4);
+  foreach(string s, string* arr : stops)
+  {
+    if(member(arr,transporter)!=-1)
+    {
+      m_add(res,s,arr);
+    }
+  }
+  return res;
+}
+
diff --git a/p/daemon/uptime_master.c b/p/daemon/uptime_master.c
new file mode 100644
index 0000000..84a7124
--- /dev/null
+++ b/p/daemon/uptime_master.c
@@ -0,0 +1,110 @@
+#pragma strong_types, save_types
+#pragma no_clone, no_shadow
+
+#include "/p/daemon/ch.h"
+#include "/p/daemon/uptime_master.h"
+
+/// Prototypen ///
+
+void create();
+void reset(); 
+
+void SaveMaxUptime();
+mapping QuerMaxUptime();
+
+/// Globale Variablen ///
+static int next_save_uptime;
+static int max_uptime_reached;
+static int last_record_boot;
+
+/// Funktionen ///
+
+void create()
+{
+  seteuid(getuid(this_object()));
+  reset();
+}
+
+void reset()
+{
+  call_out("SaveMaxUptime",1);
+  set_next_reset(300);
+}
+
+// Ist eine Rekord-Uptime erreicht, wird diese hier gespeichert.
+void SaveMaxUptime()
+{
+  int max_shut, max_boot;
+  
+  // Lohnt es, schon wieder zu pruefen?
+  if (time()>next_save_uptime)
+  {
+    // Initialisieren
+    if (next_save_uptime==0)
+    {
+      DEBUG("Masterinit");
+      
+      // Zeit berechnen, ab der ein Rekord aufgestellt ist.
+      // Vorher braucht nixhts mehr geprueft werden
+      sscanf(read_file(MAX_UPTIME)||"0 0","%d %d",last_record_boot, max_shut);
+      next_save_uptime=last_reboot_time()+(max_shut-last_record_boot)+1; 
+      
+    } else {
+      DEBUG("Rekord neu speichern.");
+	    
+      // Neuen Rekord in Datei schreiben.
+      catch(rm(MAX_UPTIME);publish);
+      write_file(MAX_UPTIME, last_reboot_time()+" "+time());
+      
+      // Jetzt checken, ob die Uptime grade geknackt wurde ...
+      // Wenn ja, dann eine Meldung an den Master
+      if(last_reboot_time()!=last_record_boot)
+      {
+	DEBUG("Rekordmeldung. MB:"+last_record_boot+" LU:"+last_reboot_time());
+
+	// Nur einmal pro Rekord Uptime melden ...
+	last_record_boot=last_reboot_time();
+
+	// Hurraaah :)
+        CHMASTER->join(RECORD_CHANNEL,this_object());
+	CHMASTER->send(RECORD_CHANNEL,this_object(),
+	"[<MasteR>:Lars] Wow, eine Rekord-Uptime.\n"
+	"[<MasteR>:Merlin] Das erlebt man auch nicht alle Tage ...\n"
+	"[<MasteR>:Der GameDriver] Dann wird es wohl Zeit fuer einen Reboot, oder?\n"
+	"[<MasteR>:Merlin] Faules Stueck, arbeite weiter!\n"
+	"[<MasteR>:Der GameDriver] Och menno!",
+	  MSG_EMPTY);
+	CHMASTER->leave(RECORD_CHANNEL,this_object());
+      }
+    }
+  }
+}
+
+// Gibt Daten zur bisher maximalen Uptime zurueck: (299 Ticks)
+// 1.) Wann wurde gebootet
+// 2.) Wann endete die Uptime
+// 3.) Wann wird dieser Rekord gebrochen?
+// 4.) Dauer der Uptime als lesbarer String.
+mapping QueryMaxUptime()
+{
+  int t, tmp, max_boot, max_shut;
+  string s;
+
+  sscanf(read_file(MAX_UPTIME)||"0 0","%d %d",max_boot, max_shut);
+
+  t=max_shut-max_boot;
+
+  s="";
+  if (t>=86400)
+    s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
+  if (t>=3600)
+    s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
+  if (t>60)
+    s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
+  s+=sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
+
+  return (["beginn":max_boot, 
+           "ende":max_shut, 
+	   "naechster rekord":last_reboot_time()+(max_shut-max_boot), 
+	   "rekord als string":s]);
+}
diff --git a/p/daemon/uptime_master.h b/p/daemon/uptime_master.h
new file mode 100644
index 0000000..862e44a
--- /dev/null
+++ b/p/daemon/uptime_master.h
@@ -0,0 +1,14 @@
+#ifndef __UPTIME_MASTER__
+#define __UPTIME_MASTER__
+
+#define UPTIME_MASTER  "/p/daemon/uptime_master"
+#define MAX_UPTIME     "/p/daemon/save/maxuptime"
+#define RECORD_CHANNEL "<MasteR>"
+
+#define DEBUG(x)
+/*
+#define DEBUG(x) if(find_player("vanion")) tell_object(find_player("vanion"),\
+		                "UPTIME: "+x+"\n");
+*/
+
+#endif /* __UPTIME_MASTER__ */
diff --git a/p/daemon/zentralbank.c b/p/daemon/zentralbank.c
new file mode 100644
index 0000000..53074d0
--- /dev/null
+++ b/p/daemon/zentralbank.c
@@ -0,0 +1,236 @@
+// MorgenGrauen MUDlib
+//
+// zentralbank.c -- Zentrale Geld-Verwaltung der Haendler im MG
+// $Id: zentralbank.c 9223 2015-05-27 21:46:58Z Zesstra $
+//
+/*
+ * $Log: zentralbank.c,v $
+ * Revision 1.5  2003/12/30 11:17:34  Vanion
+ * query_prevent_shadow fehlte. Danke fuer den Hinweis an Ogo.
+ *
+ * Revision 1.4  2003/12/30 11:12:02  Jof
+ * Ich hab Dir das File mal geklaut.
+ * Vanion
+ *
+ * Revision 1.3  1995/07/10  07:31:04  Jof
+ * typo fixed
+ *
+ * Revision 1.2  1995/07/10  07:27:59  Jof
+ * Use bank.h
+ * _query_current_money
+ *
+ * Revision 1.1  1995/07/05  16:29:26  Jof
+ * Initial revision
+ *
+ */
+#pragma no_shadow
+#pragma no_clone
+#pragma no_inherit
+#pragma strong_types,save_types
+
+#include <wizlevels.h>
+#include <properties.h>
+#include <money.h>
+
+#define NEED_PROTOTYPES
+#include <bank.h>
+
+#define TIME strftime("%H:%M",time())
+#define LS 1000000
+
+int allmoney,bank_default_percent,store_default_percent,shop_default_percent;
+int falling;
+// Geldmengen: in NPC, in Boersen, rumliegen, auf Seherkarten, Gesamtsumme,
+// Zeitpunkt der Berechnung.
+int *summen = ({0,0,0,0,0,0});
+
+nosave int last_save;
+nosave int counter;
+
+void save_me()
+{
+  save_object("/p/daemon/save/zentralbank");
+  last_save=time();
+}
+
+protected void create()
+{
+  seteuid(getuid());
+  allmoney=500000;
+  bank_default_percent=BANK_DEFAULT_PERCENT;
+  store_default_percent=STORE_PERCENT_LEFT;
+  shop_default_percent=SHOP_PERCENT_LEFT;
+  restore_object("/p/daemon/save/zentralbank");
+}
+
+private void adjust_percents()
+{
+  if (allmoney<50000   && falling>=0) falling=-1;
+  if (allmoney>1000000 && falling<=0) falling=1;
+  if (allmoney<200000 && falling>0 ) falling=0;
+  if (allmoney>80000  && falling<0 ) falling=0;
+  if (falling<0 && !((++counter)%5))
+  {
+    bank_default_percent=bank_default_percent+random(3)+1;
+    store_default_percent=store_default_percent+random(3)+1;
+    shop_default_percent=shop_default_percent+random(3)+1;
+    log_file("ZENTRALBANK",
+	sprintf("%s: PERCENTS SET TO BANK %d STORE %d SHOP %d\n",
+	  TIME,bank_default_percent,store_default_percent,
+	  shop_default_percent),LS);
+  }
+  if (falling>0)
+  {
+    bank_default_percent/=2;
+    store_default_percent/=2;
+    shop_default_percent/=2;
+    log_file("ZENTRALBANK",
+	sprintf("%s: PERCENTS SET TO BANK %d STORE %d SHOP %d\n",
+	  TIME,bank_default_percent,store_default_percent,
+	  shop_default_percent),LS);
+  }
+  if (bank_default_percent<1) bank_default_percent=1;
+  if (bank_default_percent>90) bank_default_percent=90;
+  if (store_default_percent<1) store_default_percent=1;
+  if (store_default_percent>90) store_default_percent=90;
+  if (shop_default_percent<1) shop_default_percent=1;
+  if (shop_default_percent>90) shop_default_percent=90;
+  save_me();
+}
+
+void reset()
+{
+  adjust_percents();
+}
+
+private void real_log()
+{
+  log_file("ZBANKSTATUS",sprintf("%s: %d\n",dtime(time()),allmoney),LS);
+}
+
+private void log_state()
+{
+  // nur alle 15min nen Status-Logeintrag. Reicht voellig.
+  if (find_call_out(#'real_log) == -1)
+    call_out(#'real_log,900);
+}
+
+public varargs void PayIn(int amount, int percent)
+{
+  if (amount<=0) return;
+  percent|=bank_default_percent;
+  allmoney+=amount*percent/100;
+  log_file("ZENTRALBANK",
+      sprintf("%s: Einzahlung: %d (%d brutto) von %O\n",
+	TIME,amount*percent/100,amount,previous_object()),LS);
+  log_state();
+}
+
+public int WithDraw(int amount)
+{
+  int got;
+
+  if (amount<=0) return 0;
+  if (allmoney<0) allmoney=0;
+  if (!allmoney)
+    got=0;
+  else
+  {
+    if (amount*3<allmoney)
+      got=amount;
+    else
+    {
+      got=allmoney/3;
+      if (!got) got=1;
+    }
+    allmoney-=got;
+  }
+  log_file("ZENTRALBANK",sprintf("%s: Abhebung: %6d/%6d von %O\n",
+	TIME,got, amount,previous_object()),LS);
+  log_state();
+  return got;
+}
+
+int _query_current_money()
+{
+  return allmoney;
+}
+
+int _query_bank_default_percent()
+{
+  return bank_default_percent;
+}
+
+int _query_shop_percent_left()
+{
+  return shop_default_percent;
+}
+
+int _query_store_percent_left()
+{
+  return store_default_percent;
+}
+
+// Geldmenge im Spiel
+public int *geldmenge() {
+  
+  if (summen[5] > time()-3600 || !IS_ELDER(this_player()))
+      return summen;
+
+  summen = allocate(6);
+  object *geld = clones(GELD,2);
+  foreach(object ob: geld) {
+    if (!environment(ob) || IS_LEARNER(environment(ob)))
+      continue;
+    if (environment(ob)->QueryProp(P_NPC))
+      // in NPC
+      summen[0]+=ob->QueryProp(P_AMOUNT);
+    else if (load_name(environment(ob)) == BOERSE)
+      // in boersen
+      summen[1]+=ob->QueryProp(P_AMOUNT);
+    else
+      // sonst rumliegend
+      summen[2]+=ob->QueryProp(P_AMOUNT);
+  }
+  // Seherkarten
+  geld = clones(SEHERKARTE,2);
+  mapping cards=m_allocate(sizeof(geld),1);
+  foreach(object ob: geld) {
+    cards[ob->query_owner()] = ob->QueryProp(P_AMOUNT);
+  }
+  foreach(string owner, int amount: cards)
+    summen[3]+=amount;
+  summen[4]=summen[0]+summen[1]+summen[2]+summen[3];
+  summen[5]=time();
+  return summen;
+}
+
+public void PrintGeldmenge() {
+  int *sum = geldmenge();
+  printf("NPC: %d\n"
+         "Boersen: %d\n"
+         "rumliegend: %d\n"
+         "Seherkarten: %d\n"
+         "Gesamt: %d\n"
+         "Zeit: %s\n",
+         sum[0],sum[1],sum[2],sum[3],sum[4],strftime(sum[5]));
+}
+
+void set_percents(int store,int shop,int bank)
+{
+  store_default_percent=store;
+  shop_default_percent=shop;
+  bank_default_percent=bank; 
+  log_file("ZENTRALBANK",
+      sprintf("%s: PERCENTS SET TO BANK %d STORE %d SHOP %d\n",
+	TIME,bank_default_percent,store_default_percent,
+	shop_default_percent),LS);
+}
+
+int remove()
+{
+  save_me();
+  destruct(this_object());
+  return 1;
+}
+   
diff --git a/room/church.c b/room/church.c
new file mode 100644
index 0000000..e939b23
--- /dev/null
+++ b/room/church.c
@@ -0,0 +1,41 @@
+inherit "/std/room";
+
+#include <properties.h>
+
+create()
+{
+  ::create();
+  SetProp(P_LIGHT,1);
+  SetProp(P_INT_SHORT,"Village church");
+  SetProp(P_INT_LONG,
+   "You are in the local village church.\nThere is a huge pit in the center,\n" +
+	 "and a door in the west wall. There is a button beside the door.\n"+
+   "This church has the service of reviving ghosts. Dead people come\n"+
+   "to the church and pray.\n"+
+   "There is a clock on the wall.\n"+
+   "There is an exit to south.\n");
+  AddDetail("pit","In the middle of the church is a deep pit.\n"+
+        "It was used for sacrifice in the old times, but nowadays\n" +
+        "it is only left for tourists to look at.\n");
+  AddDetail(({"elevator","door","button"}),
+            "The elevator doesn't work any more. It must be a relict from another time,\n"+
+            "a long-gone time, as this whole building.\n");
+  AddDetail("clock","The clock shows this game hasn't been rebooted for centuries. Time is\n"+
+                    "standing still in this strange room.\n");
+  AddCmd("pray","pray");
+  AddCmd("open","open");
+  AddCmd("push","open");
+}
+
+open()
+{
+  notify_fail("The elevator doesn't work anymore, sorry.\n");
+  return 0;
+}
+
+pray()
+{
+  write("The gods this church was built for have died an extremly long time ago.\n");
+  return 1;
+}
+
diff --git a/room/death/death_mark.c b/room/death/death_mark.c
new file mode 100644
index 0000000..8219016
--- /dev/null
+++ b/room/death/death_mark.c
@@ -0,0 +1,67 @@
+#pragma strict_types
+
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+
+inherit "std/thing";
+
+void create()
+{
+  ::create();
+  SetProp( P_NAME, "Stempel des Todes" );
+  SetProp( P_GENDER, MALE );
+  SetProp( P_SHORT, 0 );
+  SetProp( P_INVIS, 1 );
+  SetProp( P_NEVERDROP, 1 );
+  SetProp( P_WEIGHT, 0);
+  AddId(({"death_mark","\ndeath_mark"}));
+}
+
+void reset()
+{
+  ::reset();
+  if (environment() && !query_once_interactive(environment()))
+    remove();
+}
+
+
+/*
+ * Function name: start_death
+ * Description:   Start the death sequence.
+ */
+void start_death()
+{
+  if ( !environment() || !query_once_interactive(environment())
+       || !environment()->QueryProp(P_GHOST) )
+  {
+      destruct(this_object());
+      return;
+  }
+
+  say("Du siehst eine dunkle Gestalt, die etwas Dunst einsammelt ... oder vielleicht\n"+
+      "bildest du Dir das auch nur ein ...\n");
+ environment()->move("/room/death/virtual/death_room_"+getuid(environment()),
+		     M_GO|M_SILENT|M_NO_SHOW|M_NOCHECK);
+ // direkt in virtuellen Todesraum moven
+}
+
+protected int PreventMove(object dest, object oldenv, int method) {
+  // wenn die marke schon ein Env hat oder der move nicht in einen Spieler
+  // geht, gehts nur mit M_NOCHECK.
+  if ( (environment(this_object()) || !query_once_interactive(dest)) 
+      && !(method & M_NOCHECK)) {
+    return ME_CANT_BE_DROPPED;
+  }
+
+  return ::PreventMove(dest, oldenv, method);
+}
+
+int _query_autoloadobj() { return 1; }
+
+void init()
+{
+  ::init();
+  if (this_player() == environment())
+    start_death();
+}
diff --git a/room/death/death_mark_hooked.c b/room/death/death_mark_hooked.c
new file mode 100644
index 0000000..0e09e8f
--- /dev/null
+++ b/room/death/death_mark_hooked.c
@@ -0,0 +1,66 @@
+#pragma strict_types
+
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+#include <new_skills.h>
+
+inherit "std/thing";
+
+void create()
+{
+  ::create();
+  SetProp( P_NAME, "Stempel des Todes" );
+  SetProp( P_GENDER, MALE );
+  SetProp( P_SHORT, 0 );
+  SetProp( P_INVIS, 1 );
+  SetProp( P_NEVERDROP, 1 );
+  AddId(({"death_mark","\ndeath_mark"}));
+}
+
+void reset()
+{
+  ::reset();
+  if (environment() && !query_once_interactive(environment()))
+    remove();
+}
+
+
+/*
+ * Function name: start_death
+ * Description:   Start the death sequence.
+ */
+void start_death()
+{
+  if ( !environment() || !query_once_interactive(environment())
+       || !environment()->QueryProp(P_GHOST) )
+  {
+      destruct(this_object());
+      return;
+  }
+
+  say("Du siehst eine dunkle Gestalt, die etwas Dunst einsammelt ... oder vielleicht\n"+
+      "bildest du Dir das auch nur ein ...\n");
+  environment()->move("/room/death/virtual/death_room_"+getuid(environment()),
+		     M_GO|M_SILENT|M_NO_SHOW|M_NOCHECK);
+  // Der folgende Code verhindert, dass ein unbedachter Magier im init
+  // oder exit einen Spieler tötet, und ihn dirket danach movet.
+  environment()->SetProp(P_TMP_MOVE_HOOK,({time()+10,this_object(),"catch_die_move"}));  
+}
+
+varargs int move(mixed dest,mixed method)
+{	
+  if (!method & M_NOCHECK) return ME_CANT_BE_DROPPED;
+  return ::move(dest,method);
+}
+
+int _query_autoloadobj() { return 1; }
+
+int catch_die_move() { return -1; }
+
+void init()
+{
+  ::init();
+  if (this_player() == environment())
+    start_death();
+}
diff --git a/room/death/death_room.c b/room/death/death_room.c
new file mode 100644
index 0000000..5e59f20
--- /dev/null
+++ b/room/death/death_room.c
@@ -0,0 +1,669 @@
+// MorgenGrauen MUDlib
+//
+// death_room.c -- Der Todesraum
+//
+// $Id: death_room.c 9138 2015-02-03 21:46:56Z Zesstra $
+
+
+#pragma strict_types
+
+#include <defines.h>
+#include <properties.h>
+#include <moving.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <daemon.h>
+#include <new_skills.h>
+
+inherit "/std/room";
+
+mixed *players;
+mapping msgCache;
+
+
+private void flush( int unusedOnly );
+private string expand( string table, int value );
+private string parseText( string msg, object pl );
+private void do_remove();
+private varargs mixed get_sequence( string str );
+void add_player( object pl );
+static int filter_ldfied( string str );
+public int SmartLog( string creat, string myname, string str, string date );
+public mixed hier_geblieben( mixed dest, int methods, string direction,
+                             string textout, string textin );
+public void init()
+{
+  this_player()->move("/room/death/virtual/death_room_"+getuid(this_player()),
+	     M_NOCHECK|M_SILENT|M_NO_SHOW);
+  return;
+}
+
+public void create()
+{
+  if (IS_CLONE(this_object())) return;
+    ::create();
+    
+    players = ({});
+    flush(0);
+
+    SetProp( P_NAME, "Lars" );
+    SetProp( P_GENDER, MALE );
+    SetProp( P_ARTICLE, 0 );
+    SetProp( P_LIGHT,1 );
+    SetProp( P_NO_TPORT, NO_TPORT_OUT );
+    SetProp( P_LOG_FILE, "TOD/Todesraum" );
+    SetProp( P_INT_SHORT, "Arbeitszimmer des Todes" );
+    SetProp( P_INT_LONG, break_string(
+             "Ein dunkler Raum, erleuchtet von dunklem Licht, das sich der "
+             "Dunkelheit nicht so sehr zu widersetzen scheint, indem es "
+             "leuchtet, als dass es der dunkelste Punkt in einer weniger "
+             "dunklen Umgebung ist. Im seltsamen Licht erkennst Du einen "+
+             "zentral aufgestellten Schreibtisch, der mit Diagrammen und "
+             "Buechern bedeckt ist. Die Waende verschwinden hinter Regalen, "
+             "die gefuellt sind mit in Leder gebundenen, dunklen Waelzern, "
+             "von denen geheimnisvolle Runen leuchten.\nTod.", 78, 0, 1 ) );
+}
+
+
+public void reset()
+{
+    ::reset();
+    flush(1);
+}
+
+private void flush( int unusedOnly )
+{
+    string *mi;
+    int i;
+
+    if ( unusedOnly ){
+        if ( i = sizeof(mi = m_indices(msgCache)) ){
+            for ( ; i--; )
+                if ( msgCache[mi[i], 1] )
+                    msgCache[mi[i], 1] = 0;
+                else
+                    msgCache = m_copy_delete( msgCache, mi[i] );
+        }
+    }
+    else
+        msgCache = ([]);
+}
+
+
+private string expand( string table, int value )
+{
+    int sz, wert, i;
+    string *texte;
+
+    sz = sizeof( texte = explode( table, "##" ) - ({""}) );
+    
+    for ( i = 0; i < sz; i++ )
+        if ( i%2 ){
+            sscanf( texte[i], "%d", wert );
+            
+            if ( value < wert )
+                break;
+        }
+        else
+            table = texte[i];
+
+    return table;
+}
+
+
+#define TOS(s) s[<1]
+#define STOS(s) s[<2]
+#define PUSH(x,s) (s+= ({ x }))
+#define POP(s)	(s=s[0..<2])
+
+// ziemlich vom htmld abgekupfert ;)
+private string
+parseText( string msg, object pl ) 
+{
+  string *words, *texte, *todo, *done;
+  int endFlag;
+
+  int sz = sizeof( words = regexplode(msg, "[<][^>]*[>]") );
+  todo = ({ });
+  done = ({""});
+
+  for ( int i=1; i<sz; i+=2 ){
+      string cmd = words[i][1..<2];
+      TOS(done) += words[i-1];
+
+      if ( cmd[0] == '/' ){
+          endFlag = 1;
+          cmd = cmd[1..];
+      }
+      else
+          endFlag = 0;
+      
+      switch( cmd[0] ){
+      case 'A': /*** Alignment ersetzen ***/
+          if (!endFlag){
+              PUSH( cmd, todo );
+              PUSH( "", done );
+          }
+          else
+              if ( todo[<1] == "A" ){
+                  STOS(done) += expand(TOS(done), (int) pl->QueryProp(P_ALIGN));
+                  done = done[0..<2];
+                  todo = todo[0..<2];
+              }
+          break;
+          
+      case 'D': /*** Tode ersetzen ***/
+          if ( !endFlag ){
+              PUSH( cmd, todo );
+              PUSH( "", done );
+          }
+          else
+              if ( todo[<1] == "D" ){
+                  STOS(done) += expand(TOS(done), (int) pl->QueryProp(P_DEADS));
+                  POP(done);
+                  POP(todo);
+              }
+          break;
+          
+      case 'L': /*** Level ersetzen ***/
+          if ( !endFlag ){
+              PUSH( cmd, todo );
+              PUSH( "", done );
+          }
+          else
+              if ( todo[<1] == "L" ){
+                  STOS(done) += expand(TOS(done), (int) pl->QueryProp(P_LEVEL));
+                  POP(done);
+                  POP(todo);
+              }
+          break;
+          
+      case 'Z': /*** Zufall ersetzen ***/
+          if ( !endFlag ){
+              PUSH( cmd, todo );
+              PUSH( "", done );
+          }
+          else{
+              if ( todo[<1][0] == 'Z'){
+                  int cnt, rnd, wert, sz2;
+                  
+                  if ( !sscanf(todo[<1], "Z=%d", rnd) )
+                      STOS(done) += "\n###\n### Syntax Error in <Z>!\n###\n\n";
+                  else {
+                      rnd = random(rnd);
+                      sz2 = sizeof( texte = explode(TOS(done), "##") );
+                      wert=0;
+                      cnt=0;
+                      
+                      for ( int k = 1; k < sz2; k += 2 ){
+                          sscanf( texte[k], "%d", wert );
+                          cnt += wert;
+                          if ( rnd < cnt ) {
+                              STOS(done) += texte[k+1];
+                              break;
+                          }
+                      }
+                  }
+                  POP(done);
+                  POP(todo);
+              }
+          }
+          break;
+
+      case 'G': /*** Gender ersetzen ***/
+          if ( !endFlag ){
+              PUSH( cmd, todo );
+              PUSH( "", done );
+          }
+          else{
+              if( sizeof( texte = regexplode(TOS(done), ":") ) == 3 )
+                  STOS(done) += texte[2*((int) pl->QueryProp(P_GENDER)
+                                         == FEMALE)];
+              POP(done);
+              POP(todo);
+          }
+          break;
+
+      case 'R': /*** Rasse ersetzen ***/
+          if ( !endFlag ){
+              PUSH( cmd, todo );
+              PUSH( "", done );
+          }
+          else{
+              int race;
+
+              texte = regexplode( TOS(done), "\\|" );
+              race = 2 * (member( ({ "Mensch", "Elf", "Zwerg", "Hobbit",
+                                         "Feline", "Dunkelelf" }),
+                                  (string) pl->QueryProp(P_RACE) ) + 1);
+              
+              if ( race >= sizeof(texte) )
+                  race = 0;
+
+              STOS(done) += texte[race];
+              POP(done);
+              POP(todo);
+          }
+          break;
+
+      case 'n': /*** Name, normal geschrieben ***/
+          TOS(done) += (string) (pl->name(RAW));
+          break;
+
+      case 'N': /*** Name, in Grossbuchstaben ***/
+          TOS(done) += upperstring(pl->name(RAW));
+          break;
+      }
+  }
+  PUSH( words[<1], done );
+  return implode( done, "" );
+}
+
+
+public void heart_beat()
+{
+    for ( int j = sizeof(players); j--; )
+        if ( !objectp(players[j][0]) ||
+             environment(players[j][0]) != 
+	     find_object("/room/death/virtual/death_room_"+getuid(players[j][0])) )
+            players[j] = 0;
+
+    players -= ({0});
+
+    if ( !sizeof(players) ) {
+        set_heart_beat(0);
+        return;
+    }
+
+  for ( int j = sizeof(players); j--; ) {
+      int nr;
+      string msg;
+
+      nr = ++players[j][1];
+
+      if ( mappingp(players[j][2]) )
+          msg = players[j][2][nr];
+      else
+          msg = 0;
+      
+      if ( !msg )
+          msg = players[j][3][1][nr];
+      
+      if ( msg )
+          tell_object( players[j][0], parseText( msg, players[j][0] ) );
+  }
+  
+  do_remove();
+}
+
+private void
+do_remove()
+{
+    int res;
+    string prayroom;
+    object plobj, pl;
+    
+    for ( int j = sizeof(players); j--; ){
+        if ( players[j][1] >= players[j][3][0]){
+            pl = players[j][0];
+            while ( plobj = present("\ndeath_mark", pl) )
+                plobj->remove();
+            
+            if ( !(prayroom = (string) pl->QueryPrayRoom()) )
+                prayroom="/room/pray_room";
+
+            pl->Set( P_TMP_MOVE_HOOK, 0 );
+            pl->Set( P_NO_ATTACK, 0, F_QUERY_METHOD );
+            pl->Set( P_LAST_KILLER, 0 );
+            pl->Set( P_KILLER, 0 );
+            pl->Set( P_ENEMY_DEATH_SEQUENCE, 0 );
+            pl->Set( P_NEXT_DEATH_SEQUENCE, 0 );
+            pl->Set( P_POISON, 0, F_QUERY_METHOD );
+            
+            if ( catch( res = (int) pl->move(prayroom, M_GO|M_SILENT|M_NOCHECK) )
+                 || res < 1 )
+                pl->move( "/room/pray_room", M_GO|M_NOCHECK );
+
+            players[j] = 0;
+        }
+    }
+
+    players -= ({0});
+
+    if ( !sizeof(players) )
+        set_heart_beat(0);
+}
+
+private varargs mixed
+get_sequence( string str )
+{
+    string *sequences;
+    int i, len, cacheable;
+    
+    if ( !stringp(str) || catch( len = file_size(str) ) || len <= 0 ){
+        sequences = get_dir( "/room/death/sequences/*" ) - ({ ".", "..", ".svn" });
+        str = "/room/death/sequences/" + sequences[random( sizeof(sequences) )];
+    }
+
+    if ( cacheable = ((sizeof(str) > 21) &&
+                      (str[0..21] == "/room/death/sequences/")) ){
+        if ( member(msgCache, str) ){
+            msgCache[str, 1] = 1;  // Touch it!
+            return ({ msgCache[str], str });
+        }
+    }
+    
+    sequences = explode( read_file(str), "\n" );
+    sscanf( sequences[0], "%d", len );
+    string seq = implode( sequences[1..], "\n" );
+    sequences = regexplode( seq, "[0-9][0-9]*:" );
+    mapping m = ([]);
+    
+    for ( i = 1; i < sizeof(sequences)-1; i += 2 )
+        m[(int) sequences[i]] = sequences[i+1];
+
+    if ( cacheable )
+        msgCache += ([ str: ({ len, m }); 1 ]);
+
+    return ({ ({ len, m }), str });
+}
+
+// Description:   Adds a player to the list
+void add_player( object pl )
+{
+    int kart, kgen;
+    int escaped;
+    object kill_liv, kill_ob;
+    mixed dseq, act_seq, killer_name, killer_msg;
+ 
+    set_heart_beat(1);
+    kgen = MALE;
+
+    foreach(object prev : caller_stack(1)) { 
+        if ( !objectp(prev) || prev == pl )
+            continue;
+
+        string fn = object_name(prev);
+
+        if ( fn[0..12] == "/secure/login" && !kill_liv ){
+            escaped = 1;
+            break;
+        }
+
+        if ( fn[0..7] == "/secure/" && fn[0..13] != "/secure/merlin" )
+            continue;
+
+        if ( fn[0..21] == "/room/death/death_mark" )
+            continue;
+
+        if ( living(prev) ){
+            kill_liv = prev; // Killer
+            break;
+        }
+
+        kill_ob = prev; // killendes Objekt
+    }
+
+    object pre = (object) pl->QueryProp(P_KILLER);
+    if ( objectp(pre) ) {
+        dseq = (mixed) pre->QueryProp(P_ENEMY_DEATH_SEQUENCE);
+
+        if( !(killer_name = (mixed) pre->QueryProp(P_KILL_NAME)) ){
+            killer_name = (mixed) pre->QueryProp(P_NAME);
+            kart = (int) pre->QueryProp(P_ARTICLE);
+            kgen = (int) pre->QueryProp(P_GENDER);
+        }
+
+        killer_msg = (mixed)pre->QueryProp(P_KILL_MSG);  
+    }
+
+    if ( !killer_name && kill_liv && function_exists( "QueryProp", kill_liv ) ){
+        dseq = (mixed) kill_liv->QueryProp(P_ENEMY_DEATH_SEQUENCE);
+
+        if( !(killer_name = (mixed) kill_liv->QueryProp(P_KILL_NAME)) ){
+            killer_name = (mixed) kill_liv->QueryProp(P_NAME);
+            kart = (int) kill_liv->QueryProp(P_ARTICLE);
+            kgen = (int) kill_liv->QueryProp(P_GENDER);
+        }
+
+        killer_msg = (mixed) kill_liv->QueryProp(P_KILL_MSG);
+        pre = kill_liv;
+    }
+
+    if ( !killer_name && kill_ob && function_exists( "QueryProp", kill_ob ) ){
+        dseq = (mixed) kill_ob->QueryProp(P_ENEMY_DEATH_SEQUENCE);
+
+        if( !(killer_name = (mixed) kill_ob->QueryProp(P_KILL_NAME)) ){
+            killer_name = (mixed) kill_ob->QueryProp(P_NAME);
+            kart = (int) kill_ob->QueryProp(P_ARTICLE);
+            kgen = (int) kill_ob->QueryProp(P_GENDER);
+        }
+
+        killer_msg = (mixed) kill_ob->QueryProp(P_KILL_MSG);
+        pre = kill_ob;
+    }
+    
+    // falls keine Sequenz gesetzt, eventuelle eigene Todessequenz nehmen
+    if (!dseq)
+      dseq = (mixed)pl->QueryProp(P_NEXT_DEATH_SEQUENCE);
+
+    act_seq = 0;
+
+    if ( mappingp(dseq) )
+        act_seq = get_sequence( "/room/death/sequences/lars" );
+    else if ( pointerp(dseq) )  // ganze Todessequenz...
+        act_seq = ({ dseq, 0 });
+    else if ( stringp(dseq) )
+        act_seq = get_sequence(dseq);
+
+    if(pl->query_hc_play()>1)
+    {
+        act_seq=({({22,([1:"Du faellst und faellst...\n",
+                        5:"und faellst...\n",
+                        10:"und faellst...\n",
+                        12:"direkt in die Arme von TOD.\n",
+                        14:"Triumphierend laechelt er Dich an.\n",
+                        16:"NUN GEHOERST DU FUER IMMER MIR!\n",
+                        18:"HAHHHAHAHAAAAAAAAAAHAAAAAAAAA!\n",
+                        20:"TOD schlaegt Dir mit seiner Sense den Kopf ab.\n"])}),0});
+    }
+    if ( !act_seq )
+        act_seq = get_sequence();
+
+    if ( !mappingp(dseq) )
+        dseq = 0;
+
+    int i;
+    for ( i = sizeof(players); i--; )
+        if ( players[i][0] == pl )
+            break;
+
+    if ( i == -1 )
+        players += ({ ({ pl, 0, dseq, act_seq[0], act_seq[1], pre }) });
+    else
+        players[i][5] = pre;
+
+
+        if ( escaped ){
+            killer_name = "";
+            killer_msg = upperstring(getuid(pl)) + " VERSUCHTE, MIR ZU "
+                "ENTKOMMEN - JETZT HABE ICH WIEDER EXTRA-ARBEIT MIT "+
+                ((int) pl->QueryProp(P_GENDER) != 2 ? "IHM" : "IHR") +
+                " ...";
+        }
+        else if ( !killer_name ) {
+                if ( (string) pl->QueryProp(P_KILLER) == "gift" ){
+                    killer_name = "Vergiftung";
+                    kgen = FEMALE;
+                    kart = 1;
+                }
+                else{
+                    killer_name = "Etwas Geheimnisvolles und Unbekanntes";
+                    kgen = NEUTER;
+                    kart = 0;
+                }
+        }
+
+        if ( !pointerp(killer_msg) )
+            killer_msg = ({ killer_msg, 0, 0 });
+        else if ( sizeof(killer_msg) < 3 )
+            killer_msg += ({ 0, 0, 0 });
+
+        if ( stringp(killer_msg[0]) )
+            killer_msg[0] = sprintf( killer_msg[0], capitalize(getuid(pl)) );
+
+        SetProp( P_NAME, killer_name );
+        SetProp( P_ARTICLE, kart );
+        SetProp( P_GENDER, kgen );
+        string killname = Name(WER);
+        SetProp( P_NAME, "Lars" );
+        SetProp( P_ARTICLE, 0 );
+        SetProp( P_GENDER,MALE );
+
+        int magiertestie;
+        string testplayer = (string) pl->QueryProp(P_TESTPLAYER);
+        if (sizeof(testplayer))
+        {
+          if (testplayer[<5..<1]!="Gilde")
+            magiertestie = 1;
+        }
+          
+        string kanal;
+        if  (magiertestie || IS_LEARNING(pl))
+            kanal = "TdT";
+        else
+            kanal = "Tod";
+
+        CHMASTER->join( kanal, this_object() );
+
+        if ( (!stringp(killer_name) || killer_name != "") &&
+             (sizeof(killer_msg) < 4 || !killer_msg[3]) ){
+            if ( killer_msg[2] == PLURAL )
+                CHMASTER->send( kanal, this_object(),
+                                killname + " haben gerade " +
+                                capitalize(getuid(pl)) + " umgebracht." );
+            else
+                CHMASTER->send( kanal, this_object(),
+                                killname + " hat gerade " +
+                                capitalize(getuid(pl)) + " umgebracht." );
+        }
+
+        i = (int) pl->QueryProp(P_DEADS);
+        if ( i && (getuid(pl) == "key" || i%100 == 0 || i%250 == 0) ){
+            SetProp( P_NAME, "Tod" );
+            CHMASTER->send( kanal, this_object(),
+                            sprintf( "DAS WAR SCHON DAS %dTE MAL!", i ) );
+            SetProp( P_NAME, "Lars" );
+        }
+
+        if( killer_msg[0] ){
+            if ( stringp(killer_name) && killer_name == "" ){
+                CHMASTER->send( kanal, this_object(),
+                                break_string( funcall(killer_msg[0]), 78,
+                                              "["+kanal+":] " )[0..<2],
+                                MSG_EMPTY );
+                return; 
+            }
+            else {
+                if ( (killer_msg[1] < MSG_SAY) || (killer_msg[1] > MSG_GEMOTE) )
+                    killer_msg[1] = MSG_SAY;
+
+                SetProp( P_NAME, killer_name );
+                SetProp( P_ARTICLE, kart );
+                SetProp( P_GENDER, kgen );
+                CHMASTER->send( kanal, this_object(), funcall(killer_msg[0]),
+                                killer_msg[1] );
+                SetProp( P_NAME, "Lars" );
+                SetProp( P_ARTICLE, 0 );
+                SetProp( P_GENDER, MALE );
+            }
+        }
+
+        if ( pointerp(killer_msg = (mixed) pl->QueryProp(P_DEATH_MSG)) &&
+             sizeof(killer_msg) == 2 && stringp(killer_msg[0]) &&
+             intp(killer_msg[1]) ){
+            SetProp( P_NAME, capitalize(getuid(pl)) );
+            SetProp( P_ARTICLE, 0 );
+            SetProp( P_GENDER, pl->QueryProp(P_GENDER) );
+            CHMASTER->send( kanal, this_object(), killer_msg[0],
+                            killer_msg[1] );
+            SetProp( P_NAME, "Lars" );
+            SetProp( P_ARTICLE, 0 );
+            SetProp( P_GENDER, MALE );
+        }
+
+        if (pl->query_hc_play()>1){
+            SetProp( P_NAME, "Tod" );
+            CHMASTER->send( kanal, this_object(),"NUN GEHOERST DU FUER EWIG MIR!" );
+            SetProp( P_NAME, "Lars" );
+        }
+}
+
+public int
+SmartLog( string creat, string myname, string str, string date )
+{
+    int i;
+    string fn;
+    
+    for ( i = sizeof(players); i--; )
+        if ( players[i][0] == this_player() )
+            break;
+
+    // Spieler (Magier?) ist in keiner Todessequenz -> normales Repfile
+    if ( i == -1 )
+        return 0;
+
+    if ( !(fn = players[i][4]) ){
+        // Spieler hat eine unbekannte Todessequenz (kein Filename, Sequenz
+        // wurde komplett in P_ENEMY_DEATH_SEQUENCE abgelegt)
+        creat = "TOD/unbekannt.rep";
+        fn = "unbekannte Todessequenz";
+    }
+    else
+        // Jede Sequenz mit nem eigenen Repfile
+        creat = "TOD/" + explode( fn, "/" )[<1] + ".rep";
+
+    log_file( creat, myname + " von " + getuid(this_interactive())
+              + " ["+fn+"] (" + date + "):\n" + str + "\n" );
+
+    return 1;
+}
+
+public mixed hier_geblieben( mixed dest, int methods, string direction,
+                             string textout, string textin ) 
+{
+    // Magier duerfen Spieler heraustransen
+    if ( this_interactive() && IS_LEARNER(this_interactive()) &&
+         (this_interactive() != previous_object() ||
+          IS_DEPUTY(this_interactive())) ){
+        previous_object()->Set( P_TMP_MOVE_HOOK, 0 );
+        return ({ dest, methods, direction, textout, textin });
+    }
+
+    // Spieler haengt noch in der Todessequenz
+    for ( int i = sizeof(players); i--; )
+        if ( objectp(players[i][0]) && previous_object() == players[i][0] &&
+             environment(previous_object()) == find_object(
+	  "/room/death/virtual/room_death_" + getuid(previous_object()))&&
+             interactive(previous_object()) ) {
+	    // Move nur erlaubt, wenn das Ziel wieder der Todesraum ist.
+	    // wenn mal fuer nen bestimmten Zwecks Bewegungen raus aus dem
+	    // Todesraum erforderlich sind, sollten hier entsprechende
+	    // Ausnahmen eingebaut werden.
+	    if ( (stringp(dest) && 
+		  dest == object_name(environment(previous_object()))) ||
+		 (objectp(dest) &&
+		  dest == environment(previous_object())) ) {
+		previous_object()->Set( P_TMP_MOVE_HOOK, 0 );
+		return ({ dest, methods, direction, textout, textin });
+	    }
+            else
+                return -1;
+        }
+
+    // Spieler ist nicht mehr im Raum oder eingeschlafen
+    if ( previous_object() )
+        previous_object()->Set( P_TMP_MOVE_HOOK, 0 );
+    
+    return ({ dest, methods, direction, textout, textin });
+}
diff --git a/room/death/death_room_vc.c b/room/death/death_room_vc.c
new file mode 100644
index 0000000..71038fb
--- /dev/null
+++ b/room/death/death_room_vc.c
@@ -0,0 +1,168 @@
+// MorgenGrauen MUDlib
+//
+// death_room_vc.c -- Das Standardobjekt fuer den VC-Todesraum
+//
+// $Id: death_room_vc.c 8988 2014-12-31 13:10:19Z Zesstra $
+
+
+#pragma strict_types
+
+#include <defines.h>
+#include <properties.h>
+#include <moving.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <daemon.h>
+#include <new_skills.h>
+
+inherit "/std/room";
+
+public int no_attack();
+public int no_poison(int val);
+
+public void create()
+{
+  if (IS_BLUE(this_object())) return;
+    ::create();
+    
+    SetProp( P_GENDER, MALE );
+    SetProp( P_ARTICLE, 0 );
+    SetProp( P_LIGHT,1 );
+    SetProp( P_NO_TPORT, NO_TPORT_OUT );
+    SetProp( P_LOG_FILE, "TOD/Todesraum" );
+    SetProp( P_INT_SHORT, "Arbeitszimmer des Todes" );
+    SetProp( P_INT_LONG, break_string(
+             "Ein dunkler Raum, erleuchtet von dunklem Licht, das sich der "
+             "Dunkelheit nicht so sehr zu widersetzen scheint, indem es "
+             "leuchtet, als dass es der dunkelste Punkt in einer weniger "
+             "dunklen Umgebung ist. Im seltsamen Licht erkennst Du einen "+
+             "zentral aufgestellten Schreibtisch, der mit Diagrammen und "
+             "Buechern bedeckt ist. Die Waende verschwinden hinter Regalen, "
+             "die gefuellt sind mit in Leder gebundenen, dunklen Waelzern, "
+             "von denen geheimnisvolle Runen leuchten.\nTod.", 78, 0, 1 ) );
+    previous_object()->CustomizeObject();
+    call_other("/room/death/death_room","???");
+}
+
+void test_remove()
+{
+  if (!sizeof(all_inventory(this_object())&users()))
+    if (!this_object()->remove()) destruct(this_object());
+}
+
+public void reset()
+{
+  ::reset();
+  test_remove();
+  return;
+}
+
+public void exit(object liv)
+{
+  call_out("test_remove",2);
+  return;
+}
+
+
+static void deep_destruct( object ob )
+{
+    if ( objectp(ob) && environment(ob) == this_object() )
+        filter( deep_inventory(ob) + ({ ob }),
+                      lambda( ({'x/*'*/}), ({#'destruct, 'x}) ) );
+}
+
+public void init()
+{  
+  string prayroom;
+  int res;
+
+  ::init();
+  if ( !query_once_interactive(this_player()) ){
+      call_out( "deep_destruct", 0, this_player() );
+      return;
+  }
+
+  if ( !(prayroom = (string) this_player()->QueryPrayRoom()) )
+      prayroom = "/room/pray_room";
+  
+  if ( !this_player()->QueryProp(P_GHOST) )
+    {
+      if ( IS_WIZARD(this_player()) &&
+           this_player()->QueryProp(P_WANTS_TO_LEARN) )
+	{
+          if ( !this_player()->QueryProp(P_INVIS) )
+              tell_room( this_object(),
+                         "Der Tod sagt: WAS WILLST DU HIER, "+
+                         upperstring((string) this_player()->name())+"?\n"+
+                         "Der Tod sagt: DU BIST UNSTERBLICH, DU HAST HIER "
+                         "NICHTS ZU SUCHEN!\n\n" );
+	}
+      else 
+	{
+          write("Der Tod sagt: WAS TUST DU HIER? DEINE ZEIT IST NOCH "
+                "NICHT REIF!\n\n");
+          
+	  if ( catch(res = (int) this_player()->move( prayroom,M_GO|M_SILENT|M_NOCHECK ))
+               || res < 1 && environment(this_player()) == this_object() )
+              this_player()->move( "/room/pray_room", M_GO|M_SILENT|M_NOCHECK );
+	}
+      return;
+    }
+
+  if ( !IS_DEPUTY(this_player()) ){
+    add_action( "filter_ldfied", "", 1 );
+    this_player()->Set( P_TMP_MOVE_HOOK,
+                        ({ time()+31536000, 
+                           find_object("/room/death/death_room"), 
+                           "hier_geblieben" }) );
+  }
+
+  this_player()->Set( P_NO_ATTACK, #'no_attack, F_QUERY_METHOD );
+  this_player()->Set( P_POISON, #'no_poison, F_SET_METHOD );
+  
+  "/room/death/death_room"->add_player(this_player());
+}
+
+// public, da command_me() sonst buggt (Wurzels Speedy o.ae.)
+public int filter_ldfied( string str )
+{
+    // Description:   Filter out relevant commands.
+    string verb;
+    
+    verb=query_verb();
+    
+    if ( (verb == "schlafe" || verb == "schlaf") && str == "ein" ){
+        write("DU KANNST DEM TOD NICHT ENTRINNEN!\n");
+        return 0;
+    }
+    
+    if ( verb == "typo" || verb == "fehler" || verb == "bug" || verb == "idee" )
+        return 0;
+
+    write( "Dein Koerper gehorcht Dir nicht !\n" );
+    return 1;
+}
+
+public int no_attack()
+{
+  int i;
+
+  // Spieler haengt noch in der Todessequenz
+  if( present(previous_object()) )
+    return 1;
+  else
+    previous_object()->Set( P_NO_ATTACK, 0, F_QUERY_METHOD );
+
+  return 0;
+}
+
+public int no_poison(int val)
+{
+  if ( val != 0 )
+  {
+    catch( raise_error("Poisoning dead players is illegal. Calling object "
+      "was "+object_name(previous_object(1))+"\n");publish );
+  }
+  return 0;
+}
+
diff --git a/room/death/virtual/virtual_compiler.c b/room/death/virtual/virtual_compiler.c
new file mode 100644
index 0000000..90d886e
--- /dev/null
+++ b/room/death/virtual/virtual_compiler.c
@@ -0,0 +1,44 @@
+// MorgenGrauen MUDlib
+//
+// virtual_compiler.c -- Virtueller Compiler fuer die Todesraeume
+//
+// $Id.$
+
+
+#pragma strong_types
+
+inherit "/std/virtual/v_compiler";
+
+#include <defines.h>
+#include <v_compiler.h>
+
+int NoParaObjects() {return 1;}
+
+void create() 
+{
+  if (IS_CLONE(this_object())) return;
+ ::create();
+ SetProp(P_STD_OBJECT, "/room/death/death_room_vc");
+ seteuid(getuid());
+ return;
+}
+
+string Validate(string file) {
+  string base, room, who;
+
+ file = ::Validate(file);
+ if(sscanf(file, "death_room_%s",who)!=1 ||
+    ((who[0..3]!="gast") &&
+    (!"/secure/master"->get_userinfo(who)))) return 0;
+ return file;
+}
+
+mixed CustomizeObject()
+{
+ string base,room,who,file,fun;
+
+ if(!(file = ::CustomizeObject()) ||
+    !Validate(file)) return 0;
+ return file;
+}
+
diff --git a/room/jail.c b/room/jail.c
new file mode 100644
index 0000000..1331223
--- /dev/null
+++ b/room/jail.c
@@ -0,0 +1,191 @@
+#pragma strong_types,rtt_checks
+
+inherit "/std/room";
+inherit "/std/hook_consumer";
+
+#define OFFICE "/d/wueste/catweazle/room/office"
+
+#include <properties.h>
+#include <wizlevels.h>
+#include <hook.h>
+
+protected void create()
+{
+  ::create();
+  SetProp(P_LIGHT, 1 );
+  SetProp(P_INT_SHORT,"Eine Gefaengniszelle");
+  SetProp(P_INT_LONG, break_string(
+      "Die Zelle ist absolut eintoenig und hat keine Fenster. Lange hier drin"
+      "zu sitzen ist bestimmt nicht angenehm. Wenn du hier heraus willst, "
+      "kannst Du wohl nur den Sheriff, seinen Stellvertreter oder einen "
+      "Erzmagier darum bitten.",78));
+  SetProp(P_INDOORS,1);
+}
+
+void init()
+{
+  ::init();
+  if (!query_once_interactive(this_player()))
+    return;
+  if ( IS_DEPUTY(this_player()) ) // EM+ sind sowieso Deputies
+    return;
+  add_action("bla","",1);
+  input_to("murks",0);
+
+  // Move-Hook anhaengen
+  int ret = this_player()->HRegisterToHook(H_HOOK_MOVE, this_object(), 
+                                 H_HOOK_LIBPRIO(1),H_HOOK_SURVEYOR,
+				 -1);
+  if ( ret <=0 )
+      raise_error("Fehler: Move-Hook konnte nicht registriert werden. "
+                  "HRegisterToHook-Ergebnis: " + ret + "\n");
+}
+
+// Move-Hook austragen.
+void exit(object liv) {
+  if (objectp(liv) && !IS_DEPUTY(liv))
+    liv->HUnregisterFromHook(H_HOOK_MOVE,this_object());
+}
+
+int bla()
+{
+  string v;
+
+  v=query_verb();
+  input_to("murks",0);
+  // sagen und schlafen erlaubt.
+  if (stringp(v) && (v=="sag"||v=="sage"||v[0]=='\''
+        ||v=="schlafe"||v=="schlaf"))
+      return 0;
+  write("Nix da.\n");
+  return 1;
+}
+
+int murks(string str)
+{
+  if (!this_player() || environment(this_player())!=this_object()) 
+      return 0;
+
+  input_to("murks",0);
+
+  if (!str||str=="") return 1;
+  // Einschlafen erlaubt.
+  if (str == "schlafe ein") {
+      this_player()->command("schlafe ein");
+      return 1;
+  }
+  if (str[0]=='\'') str="sag "+str[1..];
+  if(str[0..3]=="sag "||str[0..4]=="sage ")
+  {
+       str=implode(old_explode(str," ")[1..]," ");
+     write(break_string(str, 78, "Du sagst: "));
+     tell_room(this_object(),break_string(str,78,
+         capitalize(this_player()->name())+" sagt: "),({this_player()}));
+     return 1;
+  }
+  write("Nix da.\n");
+  return 1;
+}
+
+public varargs int remove(int silent) {
+  // keine Zerstoerung, wenn Spieler drin sind.
+  if (sizeof(filter(all_inventory(),#'query_once_interactive)))
+    return 0;
+
+  return ::remove(1);
+}
+
+public string NotifyDestruct(object caller) {
+
+  if (previous_object() != master()
+      || caller == this_object())
+    return 0;
+
+  // wenn keiner hier ist, ists egal.
+  if (!sizeof(filter(all_inventory(),#'query_once_interactive)))
+    return 0;
+
+  // Direkter Destruct mit Anwesenden nur fuer EM+
+  if (!process_call() && this_interactive()
+      && IS_ARCH(this_interactive()))
+    return 0;
+      
+  return "Direkte Zerstoerung des Jails nur fuer EM+ erlaubt.\n";
+}
+
+
+// keine anderen Move-Hooks erlaubt ausser diesem...
+status HookRegistrationCallback(object registringObject, int hookid, object
+    hookSource, int registringObjectsPriority, int registringObjectsType)
+{
+    if (hookid==H_HOOK_MOVE)
+      return 0;
+
+    return 1;
+}
+status HookCancelAllowanceCallback(object cancellingObject, int hookid, object
+    hookSource, int cancellingObjectsPriority, mixed hookData)
+{
+    if (hookid==H_HOOK_MOVE && cancellingObject != this_object()
+        && present(hookSource,this_object()))
+      return 0;
+
+    return 1;
+}
+
+status HookModificationAllowanceCallback(object modifyingObject, int hookid,
+    object hookSource, int modifyingObjectsPriority, mixed hookData)
+{
+    if (hookid==H_HOOK_MOVE && modifyingObject != this_object()
+        && present(hookSource,this_object()))
+      return 0;
+
+    return 1;
+}
+
+mixed HookCallback(object hookSource, int hookid, mixed hookData)
+{
+  // nur move hooks sind interessant hier.
+  if (hookid != H_HOOK_MOVE)
+    return ({H_NO_MOD, hookData});
+
+  // das duerfte eigentlich nicht vorkommen, da der Hook im exit() geloescht
+  // wird...
+  if (environment(hookSource) != this_object()) {
+    hookSource->HUnregisterFromHook(H_HOOK_MOVE,this_object());
+    return ({H_NO_MOD, hookData});
+  }
+
+  // Bewegungen in den Netztotenraum sind ok.
+  if (!interactive(hookSource)
+      && pointerp(hookData) && sizeof(hookData) >= 1)
+  {
+    if ((objectp(hookData[0])
+          && object_name(hookData[0]) == "/room/netztot")
+        || (stringp(hookData[0]) && hookData[0]=="/room/netztot")
+        )
+    {
+      return ({H_NO_MOD, hookData});
+    }
+  }
+
+  // Deputy (oder hoeher), kein process_call(). Sonst wird die Bewegung
+  // abgebrochen.
+  if (!this_interactive() || !IS_DEPUTY(this_interactive()))
+  {
+      return ({H_CANCELLED, hookData});
+  }
+
+  return ({H_NO_MOD, hookData});
+}
+
+// wenn hier jemand durch Zerstoerung des Objektes rauskommen will, geht das
+// schief.
+varargs void NotifyRemove(object ob) {
+  if (objectp(ob) && query_once_interactive(ob) && !IS_DEPUTY(ob))
+  {
+    ob->SetProp(P_START_HOME,"/room/jail");
+    ob->save_me(0);
+  }
+}
+
diff --git a/room/muellraum.c b/room/muellraum.c
new file mode 100644
index 0000000..21c4d22
--- /dev/null
+++ b/room/muellraum.c
@@ -0,0 +1,67 @@
+inherit "std/room";
+#include <properties.h>
+#include <wizlevels.h>
+
+void create()
+{
+  ::create();
+  SetProp(P_LIGHT, 1 );
+  SetProp(P_INT_SHORT, "Der Muellraum" );
+  SetProp(P_INDOORS, 1);
+  SetProp(P_NEVER_CLEAN, 1);
+  SetProp(P_INT_LONG,break_string(
+    "Dieser Raum ist vollkommen leer und anscheinend riiiiiiesig gross. "
+    "Du kannst seine Aussmasse nichtmals abschaetzen. Naja. Muss wohl auch "
+    "so sein, denn hier kommen alle Clones rein, die einige Sekunden "
+    "nach ihrer Erstellung noch kein Zuhause (Environment) gefunden haben. "
+    "Wenn Du das magische Kommando 'welt' gibst, wirst Du wieder in Deine Welt\n"
+    "zurueckversetzt werden."));
+  AddExit("welt", "/gilden/abenteurer");
+}
+
+// rekursiv zerstoeren
+private void rec_remove(object ob) {
+  object *inv=all_inventory(ob);
+  if (sizeof(inv)) {
+    filter(inv, #'rec_remove);
+  }
+  ob->remove(1);
+  if (objectp(ob)) destruct(ob);
+}
+
+int clean_me() {
+  object *inv=all_inventory(this_object());
+  int isize=sizeof(inv);
+  if (isize>100) {
+    //aeltestes Objekt, was kein Spieler ist, zerstoeren. Pruefung auf Spieler
+    //ist Paranoia, eigentlich sollte hier nie einer hinkommen koennen, ausser
+    //magier, und um die waers dann nicht schade. *g*
+    for (isize--; isize--; ) {
+      if (!query_once_interactive(inv[isize])) {
+	rec_remove(inv[isize]);
+	return(1);
+      }
+    }
+  }
+  return 0;
+}
+
+varargs int PreventInsert(object pl) {
+
+  if (!objectp(pl))
+    return 1;
+  clean_me();
+  return 0;
+}
+
+varargs int PreventInsertLiving(object pl) {
+
+  // keine Spieler. ;-)
+  if (!objectp(pl) || 
+      (query_once_interactive(pl) && !IS_LEARNER(pl)))
+    return 1;
+
+  clean_me();
+  return 0;
+}
+
diff --git a/room/netztot.c b/room/netztot.c
new file mode 100644
index 0000000..341c46b
--- /dev/null
+++ b/room/netztot.c
@@ -0,0 +1,92 @@
+// MorgenGrauen MUDlib
+//
+// $Id: netztot.c 8747 2014-04-26 13:08:47Z Zesstra $
+
+#include <wizlevels.h>
+#include <moving.h>
+
+create()
+{
+  call_out("invcheck",120);
+  "/obj/sperrer"->NixGibts();
+}
+
+weg(ob)
+{
+  if (!objectp(ob))
+    return;
+  ob->remove();
+  if (ob)
+    destruct(ob);
+}
+
+wegraeumen(ob)
+{
+  object *x;
+  
+  if (!objectp(ob))
+    return;
+  for (x=deep_inventory(ob);sizeof(x);x=x[1..])
+    weg(x[0]);
+  ob->move("/room/void",M_NOCHECK|M_SILENT);
+  weg(ob);
+}
+
+invcheck()
+{ 
+  while(remove_call_out("invcheck")!=-1);
+  foreach(object ob: all_inventory(this_object()))
+  {
+    if (interactive(ob))
+    {
+      catch(this_player()->move("/room/void",M_GO));
+      set_object_heart_beat(ob,1);
+    }
+    else if (!query_once_interactive(ob) && object_name(ob)!="/obj/sperrer")
+      call_out("wegraeumen",1,ob);
+  }
+  call_out("invcheck",120);
+  "/obj/sperrer"->upd();
+}
+
+init()
+{
+  if (!this_player())
+    return;
+  catch(this_player()->StopHuntingMode());
+  if (interactive(this_player()))
+    catch(this_player()->move("/room/void",M_GO));
+  if (!query_once_interactive(this_player()))
+  {
+    this_player()->remove();
+    if(this_player())
+      destruct(this_player());
+  }
+  set_object_heart_beat(this_player(),0);
+}
+
+int_long()
+{
+  return "Dies ist der Netztotenraum. Es ist dunkel. Du siehst nichts.\n";
+}
+
+int_short()
+{
+  return "Nichts zu sehen.\n";
+}
+
+QueryProp(string str)
+{
+  switch (str) {
+    case "int_long": return int_long();
+    case "int_short": return int_short();
+  }
+}
+
+// Nicht jeder Magier darf den Netztotenraum entsorgen.
+string NotifyDestruct(object caller) {
+    if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
+      return "Du darfst den Netztotenraum nicht zerstoeren!\n";
+    }
+}
+
diff --git a/room/nirvana.c b/room/nirvana.c
new file mode 100644
index 0000000..ea66074
--- /dev/null
+++ b/room/nirvana.c
@@ -0,0 +1,132 @@
+#include <properties.h>
+#include <moving.h>
+
+inherit "std/room";
+
+create() {
+  ::create();
+  SetProp(P_INDOORS, 1);
+  SetProp(P_LIGHT,1);
+  SetProp(P_INT_LONG,
+"Du befindest Dich im Nirvana!\n"+
+"Es sieht anders aus als Du es Dir vorgestellt hast. Eine riesige Halle \n"+
+"mit Waenden aus Nebel, einem Boden aus Wasser und einer Decke aus Feuer\n"+
+"bildet das Heim der ewig Verstorbenen.\n"+
+"Stille herrscht hier. Grosse Stille. STILLE sozusagen.\n"+
+"Rauch durchzieht die Luft. Er entspringt einer Raeucherschale, welche auf\n"+
+"einem riesigen Altar mitten im Nirvana steht.\n"+
+"Du bist nun wohl wirklich tot.\n");
+  SetProp(P_INT_SHORT,"Im Nirvana");
+  SetProp(P_NO_TPORT,NO_TPORT);
+  SetProp(P_NO_PARA_TRANS);
+  
+  AddDetail("altar",
+"Der Altar der verzweifelten Toten. Er besteht vollstaendig aus grinsenden "+
+"Totenschaedeln und bietet keinerlei Hoffnung.\nEine Raeucherschale steht auf dem Altar.\n");
+
+  AddDetail(({"tote","toten","verstorbene"}),
+"Dies hier ist das Heim der Toten.\nDu wohnst hier.\n");
+
+  AddDetail(({"hoffnung"}),
+"HAHA, KEINE HOFFNUNG!\n");
+
+  AddDetail(({"schaedel","totenschaedel"}),
+"Sie grinsen Dich haemisch an als wollten sie dich verspotten.\n"+
+"Auf einem Schadel erkennst Du eine Inschrift.\n");
+
+  AddDetail(({"inschrift"}),
+"Du kannst sie lesen.\n");
+
+  AddReadDetail(({"inschrift"}),
+"DIES IST KEIN APRILSCHERZ.\n");
+
+  AddDetail(({"schale","raeucherschale"}),
+"Aus der Schale entsteigt dunkler Rauch, der die Luft unheilvoll schwaengert.\n");
+
+
+  AddDetail(({"luft"}),
+"Luft war einmal lebensnotwendig. Du brauchst sie nicht mehr.\n");
+
+  AddDetail(({"rauch"}),
+"Der Rauch verdunkelt die Luft. Er wuerde jeden Lebenden ersticken, aber das schreckt Dich nicht mehr.\n");
+
+  AddDetail(({"stille"}),
+"Sie hoert sich irgendwie tot und endgueltig an.\n");
+
+  AddDetail(({"nirvana","raum"}),
+"Schau Dich nur um.\n");
+
+  AddDetail(({"lebende"}),
+"Zu denen gehoerst Du nicht mehr.\n");
+
+  AddDetail(({"nebel"}),
+"Der Nebel begrenzt das Nirvana seitlich. Er formt die Waende.\n");
+
+  AddDetail(({"wasser"}),
+"Der Boden des Nirvanas scheint aus Wasser zu bestehen. Du bist tot und gehst daher nicht unter.\n");
+
+  AddDetail(({"feuer"}),
+"Die Decke des Nirvanas lodert Dir feurig entgegen. Waerest Du am Leben, wuerde Dich das beunruhigen.\n");
+
+  AddDetail(({"halle"}),
+"Die Halle der Toten. Das Nirvana.\n");
+
+  AddDetail(({"heim"}),
+"Das Heim der Toten ist das Nirvana. Dein Heim.\n");
+
+
+  AddDetail(({"boden"}),
+"Der Boden wabert unter Dir. Manchmal meinst Du das Gesicht Jofs zu erkennen.\n");
+
+  AddDetail(({"gesicht"}),
+"Gesichter sind fuer Dich nicht mehr wichtig.\n");
+
+  AddDetail(({"jof"}),
+"Er schlaeft.\n");
+
+
+  AddDetail(({"decke"}),
+"Die Decke lodert Dir entgegen. Abundzu meinst Du Rumata zu erkennen.\n");
+
+  AddDetail(({"rumata"}),
+"Er ist wohl doch nicht da. Niemand wird Dir helfen. Es ist aus.\n");
+
+  AddDetail(({"wand","waende"}),
+"Der Nebel wabert auf und ab. Manchmal scheint es so, als wandere Zook durch den Nebel.\n");
+
+
+  AddDetail(({"zook"}),
+"Er grinst Dich an. Das ist wohl das Ende der Hoffnung.\nWeit hinter Zook erkennst Du eine weitere Gestalt.\n");
+
+  AddDetail(({"gestalt"}),
+"Es scheint sich um Boing zu handeln.\n");
+
+  AddDetail(({"boing"}),
+"Boing schaut Dich traurig an.\n");
+
+
+  AddDetail(({"ende"}),
+"Du hast Dein Ende erreicht. Du bist tot.\n");
+
+  AddDetail(({"leben"}),
+"Dein Leben ist zu Ende. Du bist tot.\n");
+
+  AddCmd("bete","bete");
+  AddItem("/obj/zeitungsautomat", REFRESH_REMOVE);
+}
+
+bete() {
+  write("Du haeltst eine kurze Andacht.\n");
+  if (this_player()->QueryProp(P_GHOST) && this_player()->query_hc_play()>1) {
+	write("Eine sehr starke Kraft saugt Dich auf und spuckt Dich wieder aus.\nEs scheint sich aber nichts veraendert zu haben.\n");
+	say(this_player()->name()+" erscheint nicht in koerperlicher Form.\n");
+	this_player()->SetProp(P_GHOST,0);
+  }
+  else
+  {
+        write("Fuer Dich ist dieser Altar nicht gedacht!\n");
+  }
+  return 1;
+}
+
+
diff --git a/room/noscript.c b/room/noscript.c
new file mode 100644
index 0000000..fcff28e
--- /dev/null
+++ b/room/noscript.c
@@ -0,0 +1,42 @@
+inherit "/std/room";
+#include <properties.h>
+#include <moving.h>
+
+static string x;
+static string *xx;
+
+void create() {
+  ::create();
+  x="----------";
+  xx=({
+    "/d/ebene/room/ak_str7",
+    "/d/ebene/room/dra_str3",
+    "/d/ebene/room/hp_str2c",
+    "/d/ebene/room/o_wa1d",
+    "/d/ebene/room/waldweg8"
+  });
+  SetProp(P_LIGHT,1);
+  SetProp(P_INT_LONG,
+"Dieser Raum ist fuer Leute, die TF spielen lassen.\n\
+Es gibt nur einen Weg heraus...\n");
+  AddCmd("","raus",1);
+}
+
+void init() {
+  int i;
+  
+  ::init();
+  for (i=0;i<10;i++)
+    x[i]='a'+random(26);
+}
+
+varargs string GetExits(object viewer) {
+  return sprintf("Es gibt einen sichtbaren Ausgang: %s.\n",x);
+}
+
+varargs int raus(string s) {
+  if (query_verb()!=x)
+    return 0;
+  notify_fail("Etwas hat nicht funktioniert. Bitte verstaendige einen Magier.\n");
+  return ((this_player()->move(xx[random(sizeof(xx))],M_GO))>0);
+}
diff --git a/room/nowhere.c b/room/nowhere.c
new file mode 100644
index 0000000..af55f2b
--- /dev/null
+++ b/room/nowhere.c
@@ -0,0 +1,28 @@
+inherit "std/room";
+#include <properties.h>
+#include <moving.h>
+#include <wizlevels.h>
+
+void create()
+{
+  ::create();
+  SetProp(P_LIGHT, 1 );
+  SetProp(P_INT_SHORT, "Bei den inaktiven Spielern :)" );
+  SetProp(P_INT_LONG,
+    "Hier ist absolut nichts zu sehen, die, die sich hier normalerweise\n"
+   +"aufhalten, sind eh noninteractive...\n");
+  AddCmd( "", "onlywelt", 1 );
+}
+
+int onlywelt( string s ) {
+  if( query_verb()=="welt" ) {
+    this_player()->move("/gilden/abenteurer", M_TPORT);
+    return 1;
+  }
+  if( IS_LEARNER(this_player()) ) return 0;
+  write(
+   "Wenn Du das magische Kommando 'welt' gibst, wirst Du wieder in Deine Welt\n"
+   +"zurueckversetzt werden.\n"
+  );
+  return 1;
+}
diff --git a/room/orakel.c b/room/orakel.c
new file mode 100644
index 0000000..4da1508
--- /dev/null
+++ b/room/orakel.c
@@ -0,0 +1,231 @@
+// MorgenGrauen MUDlib
+//
+// orakel.c -- Der Raum, in dem man sich neue ZT-Sprueche holen kann
+//
+// $Id: orakel.c 9371 2015-10-22 19:01:48Z Zesstra $
+
+#define TIPS(x) "/secure/ARCH/ZT/"+x
+
+#define POTIONMASTER "/secure/potionmaster"
+
+inherit "/std/room";
+
+#include <properties.h>
+
+// WANNWIEVIEL zeigt an, mit wieviel Stufenpunkten man wieviele Zaubertraenke
+// haben kann, so ist zum Beispiel an fuenfter Stelle die Zahl 25 einge-
+// tragen, also kann man mit 25 Stufenpunkten 5 Zaubertraenke bekommen.
+#define WANNWIEVIEL ({ 0, 0, 0, 0,\
+   25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325, 350, 375,\
+   400, 425, 450, 475, 500, 525, 550, 575, 600, 625, 650, 675, 700, 725,\
+   750,\
+   790, 830, 870, 910, 950, 990, 1030, 1070, 1110, 1150, 1190, 1230, 1270,\
+   1310, 1350, 1390, 1430, 1470, 1510, 1550, 1590, 1630, 1670, 1710, 1750,\
+   1790,\
+   1860, 1930, 2000, 2070, 2140, 2210, 2280, 2350, 2420, 2490,\
+   2590, 2690, 2790, 2890, 2990, 3090, 3190, 3290, 3390, 3490 })
+#define KEINTIPTEXT "Der Nebel ueber der Kugel ruehrt sich nicht.\n"
+
+
+
+protected void create()
+{
+   ::create();
+   SetProp(P_INDOORS, 1);
+   SetProp(P_LIGHT, 1);
+   SetProp(P_INT_SHORT, "Das heilige Orakel von Tingan");
+   SetProp(P_INT_LONG,
+   "Du befindest Dich in einer mittelgrossen Hoehle, die sich halbkugel-\n"+
+   "foermig ueber Deinem Kopf erhebt. Die Hoehle wird fast komplett vom\n"+
+   "gruenlich schimmernden Quellsee des Osterbachs ausgefuellt. Ueber dem\n"+
+   "See schwebt eingehuellt in langsam ziehende Nebelschwaden eine magisch\n"+
+   "leuchtende Kugel, das heilige Orakel von Tingan. Im Osten fliesst das\n"+
+   "Wasser des Sees durch einen schmalen Gang ab, dem Du folgen koenntest.\n");
+   AddDetail("boden", "Der Boden ist groesstenteils mit Wasser bedeckt.\n");
+   AddDetail("decke", "Die Decke ist aus massivem Felsgestein, Du willst gar nicht wissen,\nwieviele Tonnen davon ueber Deinem Kopf lasten.\n");
+   AddDetail(({"fels", "gestein", "felsgestein"}), "Sowas ist im Gebirge recht haeufig.\n");
+   AddDetail(({"wand", "waende"}), "Die Waende sind feucht.\n");
+   AddDetail(({"quellsee", "see"}), "Er ist die Quelle des Osterbachs.\n");
+   AddDetail("quelle", "Das heisst, der Bach entspringt hier.\n");
+   AddDetail(({"bach", "osterbach"}), "Er fliesst nach Osten ab.\n");
+   AddDetail("wasser", "Es ist klar und frisch.\n");
+   AddDetail("gang", "Er fuehrt nach Osten.\n");
+   AddDetail("orakel", "Du fuehlst grosses Wissen in Deiner Umgebung.\n");
+   AddDetail("umgebung", "Die Umgebung ist das, was Dich umgibt.\n");
+   AddDetail("wissen", "Vielleicht nuetzt es Dir ja was.\n");
+   AddDetail(({"nebel", "schwaden", "nebelschwaden"}), "Sie ziehen langsam ueber den See.\n");
+   AddDetail("kugel", "Die Kugel leuchtet in magischem Licht und laedt Dich fast zum Meditieren ein.\n");
+   AddDetail("licht", "Magisches Licht erhellt den Raum.\n");
+   AddDetail("raum", "Schau Dich einfach mal um.\n");
+   AddDetail("hoehle", "Du befindest Dich tatsaechlich in einer Hoehle.\n");
+   AddDetail("kopf", "Das anzuschauen duerfte Dir schwer fallen.\n");
+   AddCmd(({"trink", "trinke"}), "trinken");
+   AddCmd("meditiere", "meditieren");
+   AddCmd(({"wirf", "werfe"}), "werfen");
+   AddExit("osten", "/players/boing/orakel/organg2");
+}
+
+// Es wird berechnet ob der Spieler einen neuen Tip bekommen kann.
+int NeuerTip()
+{
+   mapping platt;   // Attributmapping des Spielers
+   int	   summe;   // Summe der Attribute des Spielers + der noch zu findenen
+		    // aber bekannten Zaubertrankstellen
+   int	   sps;     // Stufenpunkte des Spielers
+
+   platt  = this_player()->QueryProp(P_ATTRIBUTES);
+   summe  = (int)platt["int"] + (int)platt["con"] + 
+	    (int)platt["dex"] + (int)platt["str"] - 4; 
+   summe  += sizeof(this_player()->QueryProp(P_KNOWN_POTIONROOMS));
+   sps	  = this_player()->QueryProp(P_LEP);
+
+// Wenn genug Stufenpunkte da sind, dann wird 1 zurueckgegeben, sonst 0
+  if (summe>79)
+    return 0;
+  if (sps > WANNWIEVIEL[summe])
+    return 1;
+   return 0;
+}
+
+// Gibt die Nummer des ausgewaehlten Zaubertranks zurueck oder 0, wenn keiner
+// mehr uebrig ist.
+int ZTAuswahl()
+{
+   int* pllist;
+   int	pllsize;
+   int i;
+   mixed sorted_list; // ({ ({liste1}), ({liste2}), ..., ({liste8}) })
+
+// Initialisierung
+   sorted_list = ({ ({ }), ({ }), ({ }), ({ }), ({ }), ({ }), ({ }), ({ }) });
+   pllist      = this_player()->QueryProp(P_POTIONROOMS);
+   pllsize     = sizeof(pllist);
+
+// Zaubertraenke werden gemaess ihrer Liste in den neuen Array eingetragen.
+   for (i=0; i<pllsize; i++)
+      sorted_list[POTIONMASTER->GetListByNumber(pllist[i])] += ({ pllist[i] });
+
+// Alle Unterarrays werden aneinandergehaengt.
+   pllist = ({ });
+   for (i=0; i<8; i++)
+      pllist += sorted_list[i];
+   pllist -= this_player()->QueryProp(P_KNOWN_POTIONROOMS);
+
+// Jetzt sind alle Zaubertraenke wieder in der Liste, sortiert nach dem
+// Schwierigkeitsgrad, bis auf die Zaubertraenke, die der Spieler bereits
+// kennt (d.h. wo er schon den Tip bekommen hat, sie aber noch nicht
+// gefunden hat).
+
+
+// Einer der leichtesten Zaubertraenke wird ausgewaehlt und zurueckgegeben.
+   pllsize = sizeof(pllist);
+   if (!pllsize)
+      return -1;
+   if (pllsize>10)
+      return pllist[random(10)];
+   else
+      return pllist[random(pllsize)];
+}
+
+
+string Vision()
+{
+  int	  nr;
+  string* text;
+  int*	  liste;
+
+  if (!NeuerTip())
+    return KEINTIPTEXT + "Du hast wahrscheinlich zu wenig Stufenpunkte.\n";
+
+  nr = ZTAuswahl();
+  if (nr == -1)
+    return KEINTIPTEXT + "Du hast schon alle Zaubertraenke gefunden.\n";
+
+  text = POTIONMASTER->TipLesen(nr);
+// Das Folgende kann passieren, wenn entweder die Datei zu einem ZT nicht
+// existiert oder das Einlesen sonstwie fehlschlaegt. Zur Behebung des
+// Fehlers sollte man sich als erstes die Datei <nr>.zt anschauen.
+  if (intp(text) || !sizeof(text)) 
+    return 
+  "Ploetzlich wird es totenstill im Raum, die Nebelschwaden beginnen sich\n"+
+  "um die Kugel zu drehen. Immer schneller wird der wirbelnde Tanz, bis\n"+
+  "sich das Ganze unmerklich zu einer Schrift formt:\n"+
+  "   Fehler im Raum-Zeit-Gefuege, bitte wende Dich an Rikus\n"+
+  "   und melde ihm die Zahl '"+nr+"'!\n"+
+  "Dann zerfliesst der Nebel und alles ist beim Alten.\n";
+
+  liste = this_player()->QueryProp(P_KNOWN_POTIONROOMS);
+  this_player()->AddKnownPotion(nr);
+
+//bis nach Reboot noetig!!! Rikus
+//  "/players/boing/orakel/orakel"->AddKnownPotion(nr);
+
+  return "Der Nebel ueber der Kugel beginnt sich zu bewegen, dann hoerst Du eine\ntiefe Stimme in Deinem Kopf erklingen:\n\n"+text[random(sizeof(text))];
+}
+
+string TipListe()
+{
+  int*	  pll;
+  string* tip;
+  string  ret;
+  int	  plsize, i;
+
+  ret	 = "";
+  pll	 = this_interactive()->QueryProp(P_KNOWN_POTIONROOMS);
+  plsize = sizeof(pll);
+
+  for (i=0; i<plsize; i++)
+  {
+    tip = POTIONMASTER->TipLesen(pll[i]);
+    if (sizeof(tip))
+    {
+      ret += tip[random(sizeof(tip))];
+      ret += "--------------------------------------------------------------------------\n";
+    }
+  }
+
+  if (ret=="")
+    return "Du kennst momentan keine Tips zu Zaubertraenken.\n";
+  else
+    return "--------------------------------------------------------------------------\n"+ret;
+}
+
+int trinken(string str)
+{
+  notify_fail("Was moechtest Du trinken?\n");
+  if (!str || str=="") return 0;
+  notify_fail("Das kannst Du hier nicht trinken!\n");
+  if (str!="wasser")
+    return 0;
+  write("Du trinkst einen Schluck Quellwasser und fuehlst Dich koestlich erfrischt.\n");
+  return 1;
+}
+
+meditieren()
+{
+  write(
+  "Du sammelst Deine geistigen Kraefte und verfaellst schon schnell in einen\n"+
+  "tranceartigen Zustand. Eine Vision eines Helden, der eine Muenze in den\n"+
+  "See wirft erscheint vor Deinen Augen, verschwindet aber schon schnell\n"+
+  "wieder. Als Du wieder aufwachst, ist alles wie vorher.\n");
+  return 1;
+}
+
+int werfen(string str)
+{
+  if (str=="muenze in see" || str=="muenze in wasser" ||
+      str=="muenze in quellsee")
+  {
+    if (this_player()->QueryMoney()<1)
+    {
+      write("Du hast keine Muenze.\n");
+      return 1;
+    }
+    this_player()->AddMoney(-1);
+    write("Du wirfst eine Muenze ins Wasser.\n");
+    say(capitalize(this_player()->name())+" wirft eine Muenze ins Wasser.\n");
+    write(Vision());
+    return 1;
+  }
+  return 0;
+}
diff --git a/room/post.c b/room/post.c
new file mode 100644
index 0000000..34117de
--- /dev/null
+++ b/room/post.c
@@ -0,0 +1,12 @@
+inherit "/std/post";
+#include <properties.h>
+
+create()
+{
+	::create();
+        AddExit("osten","/gilden/abenteurer");
+	SetProp(P_INT_LONG,"Dies ist das Hauptpostamt von MorgenGrauen.\n"+
+               "Von hier aus kannst Du Briefe an Deine Mitspieler schicken und Briefe von\n"+
+               "ihnen lesen. Wenn Du das willst, tippe \"post\".\n"+
+               "Der einzige sichtbare Ausgang fuehrt nach Osten in die Gilde.\n");
+}             
diff --git a/room/pray_room.c b/room/pray_room.c
new file mode 100644
index 0000000..c056d22
--- /dev/null
+++ b/room/pray_room.c
@@ -0,0 +1,37 @@
+inherit "std/room";
+
+#include <properties.h>
+
+void create()
+{
+	::create();
+	SetProp(P_LIGHT,1);
+	SetProp(P_INT_LONG,
+   "Du befindest Dich in einer kleinen Kapelle, in der Du beten kannst, um\n"+
+   "wieder Deine natuerliche Gestalt zu erhalten. Am suedlichen Ende steht\n"+
+   "ein Altar, auf dem sich ein grosses Kreuz befindet. In den Waenden siehst\n"+
+   "Du herrliche Fenster, durch die tagsueber das Licht bunt hereinscheint.\n"+
+   "Im Norden befindet sich eine Tuer, die zur Gilde fuehrt.\n");
+  SetProp(P_INT_SHORT,"Kapelle");
+  AddDetail("altar", "Er ist aus Stein und nicht besonders schoen.\n");
+  AddDetail("kreuz", "Ein schlichtes Holzkreuz.\n");
+  AddDetail("fenster", "Die Fenster sind wunderschoen.\n");
+  AddDetail(({"wand", "waende"}), "An den Waenden siehst Du nichts besonderes.\n");
+  AddCmd("bete","bete");
+  AddExit("norden","/gilden/abenteurer");
+}
+
+static int bete() {
+   if (this_player()->QueryProp(P_GHOST))
+   {
+        write("Du haeltst eine kurze Andacht.\n");
+	write("Eine sehr starke Kraft saugt Dich auf und spuckt Dich wieder aus.\n");
+	say(this_player()->name()+" erscheint in koerperlicher Form.\n");
+	this_player()->SetProp(P_GHOST,0);
+	return 1;
+   }
+   notify_fail("Du haeltst eine kurze Andacht.\n");
+   return 0;
+}
+
+
diff --git a/room/pub2.c b/room/pub2.c
new file mode 100644
index 0000000..bf51144
--- /dev/null
+++ b/room/pub2.c
@@ -0,0 +1,44 @@
+inherit "/std/pub";
+#include <rooms.h>
+#include <properties.h>
+
+create(){
+   replace_program("std/pub.c");
+   ::create();
+
+   AddDrink("Bier nach dem Reinheitsgebot","bier",12,0,2,1,
+            ({"Der Kellner bringt Dein Bier. Du fuehlst Dich gut.",
+              "&& trinkt ein eiskaltes Bier."}));
+
+   AddDrink("Franks Spezial-Schnaps",
+            ({"schnaps","spezial","spezial-schnaps"}),
+            50,10,8,0,
+            ({"Der Kellner bringt Deinen Schnaps.\n"+
+              "Du fuehlst ein Kribbeln im ganzen Koerper.",
+              "&& schuettet einen Spezial-Schnaps herunter."}));
+
+   AddDrink("Ein beruehmter Rachenputzer",({"putzer","rachenputzer"}),
+            150,25,12,0,
+            ({"Du bekommst Deinen weltberuehmten Rachenputzer.\n"+
+              "Eine Schockwelle rast mit der Gewalt eines Baseball-Schlaegers durch\n"+
+              "Deinen Koerper und trifft mit voller Wucht Dein Gehirn.",
+              "&& torkelt, als &! einen Rachenputzer saeuft."}));
+
+   AddDrink("Eine Tasse Kaffee",({"kaffee","tasse","tasse kaffee"}),
+            20,0,-2,3,
+            ({"Baeh! Schmeckt ja widerlich!",
+              "&& verzieht angewidert das Gesicht, als &! einen Kaffee schluckt."}));
+
+   AddExit("westen","room/gilde");
+
+   SetProp(P_INT_SHORT,"Franks Abenteurer-Kneipe");
+   SetProp(P_INT_LONG,"Du bist in Franks Abenteurer-Kneipe.\n"+
+               "Wie immer ist hier mal wieder die Hoelle los. Flaschen und Glaeser\n"+
+               "fliegen durch den Raum, und du musst aufpassen, dass Du nicht in\n"+
+               "die Schlaegerei zwischen einem Kerl, der aussieht wie Sylvester\n"+
+               "Stallone und einem auch nicht gerade harmlosen Zauberer hinein-\n"+
+               "gezogen wirst.\n\n"+
+               "Wenn Du wissen willst, was Du hier trinken kannst, tippe: 'menue'.\n");	
+   SetProp(P_LIGHT,1);
+}
+
diff --git a/room/treff.c b/room/treff.c
new file mode 100644
index 0000000..17d0be8
--- /dev/null
+++ b/room/treff.c
@@ -0,0 +1,40 @@
+#include <properties.h>
+#include <rooms.h>
+#include <wizlevels.h>
+#include <language.h>
+inherit "/std/room";
+create(){
+   ::create();
+   SetProp(P_INT_SHORT,"Das juengste Geruecht");
+   SetProp(P_INT_LONG,
+	   "Dies ist ein Raum, den die Spieler gerne und oft als \n"
+	   +"'das juengste Geruecht' bezeichnen. Hier werden naem-\n"
+	   +"lich wichtige Entscheidungen im Morgengrauen disku-\n"
+	   +"tiert und beschlossen. Demzufolge ist dies ein reiner\n"
+	   +"Magierraum. Man kann sich hier treffen, reden oder\n"
+	   +"einfach herumidlen.\n");
+   AddExit("unten", "/gilden/abenteurer");
+   SetProp(P_LIGHT,100);
+   SetProp(P_INDOORS,1);
+}
+
+exit() // War schon oefter so, das Magier hier walking monsters erzeugt
+       // haben, die dann in die Gilde gehopst sind ... die exits
+			 // zu sperren hat auch keinen Sinn - Sirs MNPC moved sich dann
+			 // doch an seinen Startpunkt. Naja, machen wir es eben SO - Monster
+			 // haben hier eh nix verloren.
+{
+	object tp;
+
+	tp=previous_object();
+	if (!query_once_interactive(tp))
+		call_out("do_destruct",1,tp);
+}
+
+do_destruct(ob)
+{
+	if (ob)
+	  ob->remove();
+	if (ob)
+		destruct(ob);
+}
diff --git a/room/void.c b/room/void.c
new file mode 100644
index 0000000..061e62a
--- /dev/null
+++ b/room/void.c
@@ -0,0 +1,51 @@
+inherit "std/room";
+#include <properties.h>
+#include <moving.h>
+#include <wizlevels.h>
+
+void create()
+{
+  ::create();
+  SetProp(P_LIGHT, 1 );
+  SetProp(P_INT_SHORT, "Das Nichts" );
+  SetProp(P_INDOORS, 1);
+  SetProp(P_INT_LONG,
+    "Du schwebst im absoluten, ewigen und leeren Nichts umher. Hier kommen\n"
+   +"all die hin, deren Welt, in der sie sich befanden, zerstoert worden ist.\n"
+   +"Wenn Du das magische Kommando 'welt' gibst, wirst Du wieder in Deine Welt\n"
+   +"zurueckversetzt werden.\n"
+  );
+  AddCmd( "", "onlywelt", 1 );
+}
+
+int onlywelt( string s ) {
+  if( query_verb()=="welt" ) {
+    this_player()->move("/gilden/abenteurer", M_TPORT);
+    return 1;
+  }
+  if( IS_LEARNER(this_player()) ) return 0;
+  write(
+    "Du schwebst im absoluten, ewigen und leerem Nichts umher. Hier kommen\n"
+   +"all die hin, deren Welt, in der sie sich befanden, zerstoert worden ist.\n"
+   +"Wenn Du das magische Kommando 'welt' gibst, wirst Du wieder in Deine Welt\n"
+   +"zurueckversetzt werden.\n"
+  );
+  return 1;
+}
+
+
+// Sonst zerstoert sich das Void brav selbst - an NotifyDestruct vorbei. 
+public varargs int remove()
+{
+  if (!ARCH_SECURITY || process_call())
+    return 0;
+  return ::remove();
+}
+
+// Nicht jeder Magier muss das Void entsorgen koennen.
+string NotifyDestruct(object caller) {
+    if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
+      return "Du darfst das Void nicht zerstoeren!\n";
+    }
+}
+
diff --git a/room/welcome/std.c b/room/welcome/std.c
new file mode 100644
index 0000000..eae3cee
--- /dev/null
+++ b/room/welcome/std.c
@@ -0,0 +1,151 @@
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+
+inherit  "/std/room";
+
+#pragma strong_types
+#pragma rtt_checks
+#pragma no_shadow
+#pragma no_inherit
+#pragma pedantic
+#pragma range_check
+
+#define BS(x) break_string(x, 78, 0, BS_LEAVE_MY_LFS|BS_SINGLE_SPACE)
+#define TUTSTART "/d/anfaenger/arathorn/minitut/room/huette_"
+
+void create() {
+  ::create();
+
+  SetProp(P_LIGHT,1);
+  SetProp(P_LIGHT_TYPE, LT_STARS|LT_MOON);
+  SetProp(P_MAP_RESTRICTIONS, MR_NOUID|MR_NOINFO);
+  SetProp(P_INDOORS,0);
+  SetProp(P_NO_TPORT, NO_TPORT);
+
+  SetProp(P_INT_SHORT, "Irgendwo im Nirgendwo");
+  SetProp(P_INT_LONG, BS(
+    "Du schwebst im Nirgendwo. Sterne umgeben Dich, und Du glaubst "
+    "zu fallen, obwohl Du Dich vollkommen schwerelos fuehlst. Ein hauch"
+    "duenner Schleier aus Staub umfaengt Dich, als Du langsam aber sicher "
+    "Deines Koerpers gewahr wirst."));
+  AddSpecialExit("welt", function int () {
+    if ( PL->QueryGuest() ) 
+      return PL->move("/gilden/abenteurer", M_GO);
+    else return 0; 
+  });
+  Set(P_HIDE_EXITS, function mixed () {
+    return !(PL && PL->QueryGuest());
+  }, F_QUERY_METHOD);
+}
+
+void init() {
+  set_next_reset(300);
+  if ( objectp(PL) )
+    call_out("Sequenz", 0, 0);
+  return ::init();
+}
+
+// Nix resetten, aber Raum entsorgen, wenn kein Spieler drin.
+// Notfalls gibt es auch noch nen cleanup() aus dem Standardraum...
+protected void reset() {
+  if (!sizeof(filter(all_inventory(this_object()), #'query_once_interactive))
+      )
+    remove(1);
+}
+
+//TODO: Delays anpassen bzw. ausklammern, wenn immer =5 ausser am Ende.
+void Sequenz(int count) {
+  int delay;
+
+  if ( !objectp(PL) || environment(PL) != ME )
+    return;
+
+  switch(count) {
+    case 0:
+      tell_object(PL, BS(QueryProp(P_INT_LONG)));
+      delay = 4;
+      break;
+    case 1:
+      tell_object(PL, BS(
+        "\nDu schaust Dich erstaunt um. Du befindest Dich anscheinend im "
+        "Weltall, aber wie bist Du hierhergekommen? Ist das schon das "
+        "MorgenGrauen, oder bist Du wohl erst noch auf dem Weg dorthin?"));
+      delay = 8;
+      break;
+    case 2:
+      tell_object(PL, BS(
+        "\nMit einem Blick auf Deine Umgebung bemerkst Du, dass der Staub, "
+        "der Dich umgibt, sich langsam auf Dich zuzubewegen scheint. "
+        "Du willst neugierig danach greifen, aber es will Dir nicht "
+        "gelingen. Verwundert stellst Du fest, dass Du keine Haende "
+        "besitzt, und bei naeherer Betrachtung wird Dir klar, dass Du "
+        "auch sonst noch ziemlich koerperlos bist! "));
+      delay = 12;
+      break;
+    case 3:
+      tell_object(PL, BS(
+        "\nDas aendert sich aber bald, als der glitzernde Staub allmaehlich "
+        "um Dich herum eine durchscheinende Gestalt zu bilden beginnt, "
+        "die sich zuegig verdichtet."));
+      delay = 8;
+      break;
+    case 4:
+      int g = PL->QueryProp(P_GENDER);
+      string desc = PL->QueryProp(P_RACESTRING)[0];
+      string msg = (g==MALE?"ein richtiger ":"eine richtige ")+desc;
+      tell_object(PL, BS(
+        "\nSchon kurz darauf hat sich ein eleganter Koerper geformt, der "
+        "nun vollkommen Dir gehoert. Dass Du noch nackt bist, stoert Dich "
+        "nicht im geringsten, denn endlich, endlich fuehlst Du Dich "
+        "wie "+msg+"!"));
+      delay = 10;
+      break;
+    case 5:
+      tell_object(PL, BS(
+        "\nDeine Begeisterung ueber Deinen makellosen Koerper wird jaeh "
+        "unterbrochen, als ploetzlich die Sterne verblassen und sich mit "
+        "unfassbarem Tempo von Dir zu entfernen scheinen. Dann wird es "
+        "kurz schwarz um Dich herum und kurz darauf wieder hell."));
+      delay = 8;
+      break;
+    case 6:
+      tell_object(PL, "\n"+break_string(
+        "Herzlich Willkommen! Uebrigens, wenn Du auf der "
+        "Ebene Anfaenger mit anderen reden willst, folgender Tip: Probiere "
+        "mal '-anf Hallo Morgengrauen' (natuerlich ohne die ' mit "
+        "einzugeben) aus.", 78, "Merlin teilt Dir mit: "));
+      delay = 6;
+      break;
+    case 7:
+      if (PL->QueryGuest() ) {
+        PL->move("/gilden/abenteurer", M_GO);
+        return;
+      }
+      else
+        tell_object(PL, "\n"+break_string(
+          "Ich werde Dich jetzt in das Tutorial fuer Anfaenger bewegen, wo "
+          "Du Deine ersten Schritte machen kannst...",
+          78, "Merlin teilt Dir mit: ")+"\n");
+      delay = 4;
+      break;
+    case 8:
+      PL->move(TUTSTART+getuid(PL), M_GO);
+      return; // call_out nicht wieder anwerfen.
+   }
+   ++count;
+   call_out("Sequenz", delay, count);
+}
+
+// Bei Disconnect Sequenz abbrechen, bei Re-Login wieder starten mit
+// count == 5 d.h. Spieler fliegt also quasi "im Schlaf" weiter...
+void BecomesNetDead(object pl) {
+  while(remove_call_out("Sequenz")!=-1)
+   ;
+}
+
+void BecomesNetAlive(object pl) {
+  if (interactive(pl))
+    call_out("Sequenz", 6, 5);
+}
+
diff --git a/room/welcome/virtual_compiler.c b/room/welcome/virtual_compiler.c
new file mode 100644
index 0000000..a3eea2c
--- /dev/null
+++ b/room/welcome/virtual_compiler.c
@@ -0,0 +1,27 @@
+#include <defines.h>
+#include <v_compiler.h>
+inherit "/std/virtual/v_compiler";
+
+void create() {
+  ::create();
+  SetProp(P_STD_OBJECT, "/room/welcome/std");
+}
+
+string Validate(string file) {
+
+  file=::Validate(file);
+
+  // keine Unterverzeichnisse, gueltige Charnamen
+  if (strstr(file,"/") == -1
+      && sizeof(file) <= 11 // charnamen <=11 zeichen
+      && (regmatch(file,"[a-zA-Z]+") == file // nur A-Z,a-z
+          || strstr(file,"gast") == 0 // oder Gaeste
+          )
+     )
+  {
+    // Eigentlich P_COMPILER_PATH, aber hier geht jetzt auch __DIR__
+    return __DIR__ + file;
+  }
+  return 0;
+}
+
diff --git a/secure/PlayerDeleter.c b/secure/PlayerDeleter.c
new file mode 100644
index 0000000..1b1ce2e
--- /dev/null
+++ b/secure/PlayerDeleter.c
@@ -0,0 +1,87 @@
+// MorgenGrauen MUDlib
+//
+// master.c -- master object
+//
+// $Id: master.c 7336 2009-11-19 20:37:31Z Zesstra $
+#pragma strong_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+#pragma pedantic
+#pragma range_check
+#pragma warn_deprecated
+
+#include <files.h>
+#include <wizlevels.h>
+#include <events.h>
+#include <config.h>
+#include <properties.h>
+
+protected void create() {
+  seteuid(getuid(this_object()));
+}
+
+public varargs int Delete(string plname, int nobanish, int noclone) {
+  // definitiv nur fuer EM+ und nur Objekt mit Level 66+ in der Callchain.
+  if (!ARCH_SECURITY || process_call())
+    return -1;
+
+  // existiert der Spieler?
+  if (!stringp(plname)
+      || !master()->find_userinfo(plname))
+    return -2;
+  // gibt es das Spielerobjekt?
+  object plob = find_player(plname) || find_netdead(plname);
+  // wenn kein Objekt: Dummyobjekt erzeugen
+  if (!objectp(plob) && !noclone) {
+    catch(plob = __create_player_dummy(plname));
+  }
+
+  int wlevel = query_wiz_level(plname);
+  string part_filename="/"+plname[0..0]+"/"+plname+".o";
+
+  // ggf. banishen
+  if (!nobanish && wlevel >= LEARNER_LVL)
+    master()->BanishName(plname, "So hiess mal ein Magier hier", 1);
+  else if (!nobanish && wlevel >= SEER_LVL)
+    master()->BanishName(plname, "So hiess mal ein Seher hier", 1);
+
+  // Spielpausen aufheben
+  master()->TBanishName(plname, 0);
+
+  // loggen
+  log_file("ARCH/ADMIN_USERDELETE",
+      sprintf("%s: %s geloescht durch %s (%s)\n",
+             strftime("%y%m%d-%H%M%S"),
+             plname,
+             secure_euid(),
+             to_string(query_ip_number(this_interactive())) ));
+
+
+  // Loesch-Event ausloesen
+  if (plob) {
+    EVENTD->TriggerEvent(EVT_LIB_PLAYER_DELETION, ([
+            E_PLNAME: plname,
+            E_ENVIRONMENT: environment(plob),
+            E_GUILDNAME: plob->QueryProp(P_GUILD) ]) );
+
+    // Spielerobjekt zerstoeren
+    plob->move("/room/void", M_NOCHECK);
+    plob->remove(1);
+  }
+
+  // Files loeschen
+  if (file_size("/"SECUREDIR"/save"+part_filename) > FSIZE_NOFILE)
+    rm("/"SECUREDIR"/save"+part_filename);
+  if (file_size("/"LIBSAVEDIR + part_filename) > FSIZE_NOFILE)
+    rm("/"LIBSAVEDIR + part_filename);
+  if (file_size("/"MAILDIR + part_filename) > FSIZE_NOFILE)
+    rm("/"MAILDIR + part_filename);
+
+  master()->RemoveFromCache(plname);
+
+  return 1;
+}
+
diff --git a/secure/awmaster.c b/secure/awmaster.c
new file mode 100644
index 0000000..cccdb1d
--- /dev/null
+++ b/secure/awmaster.c
@@ -0,0 +1,433 @@
+// MorgenGrauen MUDlib
+//
+// awmaster.c -- Armours- and Weapons-Master
+//
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+#pragma pedantic
+#pragma range_check
+#pragma warn_deprecated
+
+#include <combat.h>
+#include <language.h>
+#include <properties.h>
+#include <wizlevels.h>
+
+#define SAVEFILE  "/secure/ARCH/awmaster"
+#define ADUMPFILE "/log/ARCH/RUESTUNGEN"
+#define DDUMPFILE "/log/ARCH/DAMAGERS"
+#define WDUMPFILE "/log/ARCH/WAFFEN"
+
+#define AWF_PUTON  1
+#define AWF_PUTOFF 2
+#define AWF_BOOST  4
+#define AWF_ZAUBER 8
+#define AWF_RESIST 16
+
+#define AWM_TYPE      0
+#define AWM_CLASS     1
+#define AWM_EFF_CLASS 2
+#define AWM_FLAGS     3
+#define AWM_WEIGHT    4
+#define AWM_VALUE     5
+#define AWM_HANDS     6
+#define AWM_D_TYPE    7
+#define AWM_X_CLASS   8
+#define AWM_TIME      9
+
+mapping armours,weapons,damagers;
+private int save_me_soon;
+
+private int allowed()
+{
+  if (previous_object() && geteuid(previous_object())==ROOTID)
+    return 1;
+  if (!process_call() && previous_object() && this_interactive() && ARCH_SECURITY)
+    return 1;
+  return 0;
+}
+
+void create()
+{
+    seteuid(getuid(this_object()));
+
+    if (!restore_object(SAVEFILE))
+    {
+        armours = ([]);
+        weapons = ([]);
+        damagers = ([]);
+    }
+    if (widthof(damagers) == 1) {
+      mapping tmp = damagers;
+      damagers = m_allocate(sizeof(tmp),2);
+      foreach(string r, int flag: tmp) {
+        damagers[r,0]=flag;
+      }
+    }
+}
+
+void save_me(int now)
+{
+  if (now)
+    save_object(SAVEFILE);
+  else
+    save_me_soon=1;
+}
+
+void reset() {
+  if (save_me_soon)
+  {
+    save_me_soon=0;
+    save_me(1);
+  }
+}
+
+public varargs int remove(int silent) {
+  save_me(1);
+  destruct(this_object());
+  return 1;
+}
+
+int xflags(object ob){
+    int re;
+    mapping m;
+    if (!ob || !objectp(ob))
+        return 0;
+    re=0;
+    if (((object)ob->QueryProp(P_WEAR_FUNC))==ob  ||
+        ((object)ob->QueryProp(P_WIELD_FUNC))==ob )
+        re += AWF_PUTON;
+    if (((object)ob->QueryProp(P_REMOVE_FUNC))==ob  ||
+        ((object)ob->QueryProp(P_UNWIELD_FUNC))==ob )
+        re += AWF_PUTOFF;
+    if (((object)ob->QueryProp(P_DEFEND_FUNC))==ob ||
+        ((object)ob->QueryProp(P_HIT_FUNC))==ob    )
+        re += AWF_BOOST;
+    // ists nen Mapping und nicht leer?
+    if (mappingp(m=(mapping)ob->QueryProp(P_RESISTANCE_STRENGTHS))
+        && sizeof(m))
+        re += AWF_RESIST;
+    return re;
+}
+
+void RegisterArmour()
+{   object  ob;
+    string  id;
+    int     flag,h;
+
+    if (!objectp(ob=previous_object()) || 
+      member(inherit_list(ob),"/std/armour.c")==-1)
+      return;
+    id = explode(object_name(ob),"#")[0];
+    if (member(armours,id))
+    {
+        armours[id][AWM_TIME]=time();
+        flag=0;
+        if ((h=(int)ob->QueryProp(P_AC)) > armours[id][AWM_CLASS])
+        {
+            armours[id][AWM_CLASS]=h;
+            flag=1;
+        }
+        if ((h=(int)ob->QueryProp(P_EFFECTIVE_AC)) > armours[id][AWM_EFF_CLASS])
+        {
+            armours[id][AWM_EFF_CLASS]=h;
+            flag=1;
+        }
+        if ((h=(int)ob->QueryProp(P_NR_HANDS)) < armours[id][AWM_HANDS])
+        {
+            armours[id][AWM_HANDS]=h;
+            flag=1;
+        }
+        if ((h=xflags(ob)) != armours[id][AWM_FLAGS])
+        {
+            armours[id][AWM_FLAGS]=h;
+            flag=1;
+        }
+    }
+    else
+    {
+      armours += ([ id : 
+        ([
+            AWM_TYPE      : ob->QueryProp(P_ARMOUR_TYPE) ,
+            AWM_CLASS     : ob->QueryProp(P_AC) ,
+            AWM_EFF_CLASS : ob->QueryProp(P_EFFECTIVE_AC) ,
+            AWM_FLAGS     : xflags(ob),
+            AWM_WEIGHT    : ob->QueryProp(P_WEIGHT) ,
+            AWM_VALUE     : ob->QueryProp(P_VALUE) ,
+            AWM_HANDS     : ob->QueryProp(P_NR_HANDS) , // Fuer Schilde
+            AWM_D_TYPE    : ob->QueryProp(P_DAM_TYPE) ,
+            AWM_X_CLASS   : ob->QueryProp(P_EFFECTIVE_WC) ||
+                            ob->QueryProp(P_WC),
+            AWM_TIME      : time()
+        ]) ]);
+    }
+    save_me(0);
+}
+
+void RegisterWeapon()
+{   object  ob;
+    string  id;
+    int     flag,h;
+
+    if (!objectp(ob=previous_object()) ||
+      member(inherit_list(ob),"/std/weapon.c")==-1)
+      return;
+    id = explode(object_name(ob),"#")[0];
+    if (member(weapons,id))
+    {
+        weapons[id][AWM_TIME] = time();
+        flag=0;
+        if ((h=(int)ob->QueryProp(P_WC)) > weapons[id][AWM_CLASS])
+        {
+            weapons[id][AWM_CLASS]=h;
+            flag=1;
+        }
+        if ((h=(int)ob->QueryProp(P_EFFECTIVE_WC)) > weapons[id][AWM_EFF_CLASS])
+        {
+            weapons[id][AWM_EFF_CLASS]=h;
+            flag=1;
+        }
+        if ((h=(int)ob->QueryProp(P_NR_HANDS)) < weapons[id][AWM_HANDS])
+        {
+            weapons[id][AWM_HANDS]=h;
+            flag=1;
+        }
+        if ((h=xflags(ob)) != weapons[id][AWM_FLAGS])
+        {
+            weapons[id][AWM_FLAGS]=h;
+            flag=1;
+        }
+    }
+    else
+    {
+      weapons += ([ id :
+        ([
+            AWM_TYPE      : ob->QueryProp(P_WEAPON_TYPE) ,
+            AWM_CLASS     : ob->QueryProp(P_WC) ,
+            AWM_EFF_CLASS : ob->QueryProp(P_EFFECTIVE_WC) ,
+            AWM_FLAGS     : xflags(ob),
+            AWM_WEIGHT    : ob->QueryProp(P_WEIGHT) ,
+            AWM_VALUE     : ob->QueryProp(P_VALUE) ,
+            AWM_HANDS     : ob->QueryProp(P_NR_HANDS) ,
+            AWM_D_TYPE    : ob->QueryProp(P_DAM_TYPE) ,
+            AWM_X_CLASS   : ob->QueryProp(P_EFFECTIVE_AC) || 
+                            ob->QueryProp(P_AC),
+            AWM_TIME      : time()
+        ]) ]);
+    }
+    save_me(0);
+}
+
+void RegisterDamager(object dam_ob,int old_dam, int new_dam)
+{   object ob;
+    int flag;
+    string fn;
+
+    if (!objectp(ob=previous_object()) ||
+      (member(inherit_list(ob),"/std/weapon.c")==-1 &&
+       member(inherit_list(ob),"/std/armour.c")==-1 ))
+      return;
+    if (old_dam>new_dam) // Repair
+        flag=2;
+    else if (new_dam>old_dam) // Damage
+        flag=1;
+    else
+        return;
+    if (!objectp(dam_ob))
+        return;
+    if (!(fn=old_explode(object_name(dam_ob),"#")[0]) || !stringp(fn))
+        return;
+    damagers[fn,0]=damagers[fn,0]|flag;
+    damagers[fn,1]=time(); 
+    save_me(0);
+}
+
+string dtdump(mixed arg)
+{   string re;
+    int i,w;
+
+    if (stringp(arg))
+        return capitalize(arg);
+    if (!pointerp(arg) || !stringp(arg[0]))
+        return "<NONE>";
+    if ((i=sizeof(arg))==1)
+        return capitalize(arg[0]);
+    w = (31-i)/i;
+    if (w--<1)
+        return "<MANY>";
+    for (re="",--i;i>=0;i--)
+    {
+        if (!stringp(arg[i]))
+            re += "-";
+        else
+            re += capitalize(arg[i][0..w]);
+        if (i)
+            re += "|";
+    }
+    return re;
+}
+
+int Dump(mixed what, int sortidx)
+{   string  file,*ind;
+    mapping dump;
+
+    if (!allowed())
+        return -1;
+
+    if (!what)
+    {
+        write("Nimm doch mal einen richtigen Parameter!\n");
+        return 0;
+    }
+    if (stringp(what) && sizeof(what)>0)
+    {
+        what==what[0..0];
+        if (what=="a")
+            what=1;
+        else if (what=="w")
+            what=2;
+        else if (what=="d")
+            what=3;
+        else
+        {
+            write("Nimm doch mal einen richtigen Parameter!\n");
+            return 0;
+        }
+    }
+    if (!intp(what) || what<1 || what>3)
+    {
+        write("Nimm doch mal einen richtigen Parameter!\n");
+        return 0;
+    }
+    if (what==3) // Die 'damagers' haben ein anderes Ausgabeformat
+    {
+        printf("AWM: Dumping 'damagers' to '%s'\n",DDUMPFILE);
+        if (sizeof(damagers) < 1) {
+            write("AWM: Dump aborted, mapping empty.\n");
+            return 1;
+        }
+        if (file_size(DDUMPFILE)>1)
+            rm(DDUMPFILE);
+        
+        // nach letzter Aktualisierung sortieren.
+        mixed sorted = sort_array(m_indices(damagers),
+            function int (string a, string b) {
+                return damagers[a,1] < damagers[b,1];
+            } );
+
+        string ausgabe = sprintf(
+            "--- Damagers-Dump --- %s --- %s ---\n\n"+
+            "%:15s D R [Filename]\n",
+            dtime(time()),capitalize(getuid(this_interactive())),
+            "Datum/Zeit");
+        foreach(string rue : sorted) {
+            ausgabe += sprintf("%:15s %1s %1s %s\n",
+                strftime("%y%m%d-%H:%M:%S",damagers[rue,1]),
+                (damagers[rue,0]&1?"+":"-"),
+                (damagers[rue,0]&2?"+":"-"),
+                rue);
+        }
+        
+        write_file(DDUMPFILE, ausgabe);
+        return 1;
+    }
+    if (what==2)
+        what=0;
+    file=(what?ADUMPFILE:WDUMPFILE);
+
+    printf("AWM: Dumping '%s' to '%s'\n",
+        (what?"armours":"weapons"),file);
+    
+    dump=(what?armours:weapons);
+    
+    if (sortidx) {
+      ind = sort_array(m_indices(dump),
+          function int (string a, string b)
+          {return dump[a][sortidx] < dump[b][sortidx];} );
+    }
+    else
+      ind = sort_array(m_indices(dump),#'>);
+    
+    if (sizeof(ind) < 1)
+    {
+        write("AWM: Dump aborted, mapping empty.\n");
+        return 1;
+    }
+
+    if (file_size(file)>1)
+        rm(file);
+    
+    string ausgabe = sprintf(
+        "--- %s-Dump --- %s --- %s ---\n\n"+
+        "[Filename], Datum/Zeit\n"+
+        "    ____Typ___ CLS ECL XCL NFBR WGHT. VALUE H %30.30'_'|s\n",
+        (what?"Ruestungs":"Waffen"),dtime(time()),
+        capitalize(getuid(this_interactive())),"DamType(s)");
+
+    foreach(string index : ind)
+    {
+        ausgabe += sprintf(
+            "[%s] %s\n    %10s %3d %3d %3d %1s%1s%1s%1s %5d %5d %1d %-30.30s\n",
+            index, strftime("%y%m%d-%H:%M:%S",dump[index][AWM_TIME]),
+            dump[index][AWM_TYPE],
+            dump[index][AWM_CLASS],
+            dump[index][AWM_EFF_CLASS],
+            dump[index][AWM_X_CLASS],
+           (dump[index][AWM_FLAGS]&AWF_PUTON?"+":"-"),
+           (dump[index][AWM_FLAGS]&AWF_PUTOFF?"+":"-"),
+           (dump[index][AWM_FLAGS]&AWF_BOOST?"+":"-"),
+           (dump[index][AWM_FLAGS]&AWF_RESIST?"+":"-"),
+            dump[index][AWM_WEIGHT],
+            dump[index][AWM_VALUE],
+            dump[index][AWM_HANDS],
+            dtdump(dump[index][AWM_D_TYPE]) );
+    }
+    write_file(file,ausgabe);
+    write("AWM: Done.\n");
+    return 1;
+}
+
+int Unregister(string what)
+{
+    if (!allowed())
+        return -1;
+    if (!what)
+    {
+        write("Du solltest schon einen Filenamen angeben!\n");
+        return 0;
+    } 
+    if (member(armours,what))
+    {
+        m_delete(armours,what);
+        write("Unregistered "+what+" from 'armours'.\n");
+        return 1;
+    }
+    if (member(weapons,what))
+    {
+        m_delete(weapons,what);
+        write("Unregistered "+what+" from 'weapons'.\n");
+        return 1;
+    }
+    if (member(damagers,what))
+    {
+        m_delete(damagers,what);
+        write("Unregistered "+what+" from 'damagers'.\n");
+        return 1;
+    }
+    save_me(0);
+    return 0;
+}
+
+int ResetDamagers()
+{
+    if (!allowed())
+        return -1;
+    damagers = m_allocate(0,2);
+    save_me(1);
+    return 1;
+}
+
diff --git a/secure/bbmaster.c b/secure/bbmaster.c
new file mode 100644
index 0000000..9677f7f
--- /dev/null
+++ b/secure/bbmaster.c
@@ -0,0 +1,393 @@
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#include <wizlevels.h>
+#include <daemon.h>
+#include <events.h>
+#include <strings.h>
+#include <files.h>
+
+#define FTPSAVE "/secure/ARCH/ftpd"
+
+#define MAXLOGSIZE 2000000
+#define SMALLLOGSIZE 200000
+#define LOGTIME 120
+#define MAXBUFFSIZE 2000
+
+#define DEBUG(x)  if (find_player("zesstra"))\
+            tell_object(find_player("zesstra"),\
+                                      "EDBG: "+x+"\n")
+
+// fuer FTP.
+private mapping monitored;
+
+
+#define D_LOGTIME    0  // Logendezeit, <int>
+#define D_FLAGS      1  // Flags
+#define D_ERSTIE     2  // Name des Ersties.
+#define D_IPSTRING   3
+#define D_INDEX      4  // Index fuer D_LOG, naechster freier index.
+#define D_LOG        5  // Logdaten, <mixed> (Array)
+/* Datenstruktur von D_LOG:
+   ({ ({<zeit>, <kommando>, <environment>}), .... })
+   */
+#define DL_TIME  0  // int
+#define DL_CMD   1  // string
+#define DL_ENV   2  // string
+private nosave mapping ldata = m_allocate(0,D_LOG);
+
+// Flags:
+#define FL_PERMANENT    1 // 'permanent', d.h. nicht nur kurz nach Einloggen
+#define FL_SYNC         2 // nicht puffern, synchron auf Platte schreiben.
+
+// Ja. Macht das bloss nicht nach.
+#define P_SECOND "second"
+
+public int query_bb();
+public void writebb( string msg );
+public varargs void BBWrite(string msg, int catmode);
+
+public int add( string user, int timeout );
+public int sub( string user );
+public void ftpbb( string user, string msg );
+
+private void scan_bb_opfer();
+private void DumpData(string uid, string erstie, string ip, int flags, mixed logdata);
+private void ProcessBuffer(string uid);
+private void RemoveTemporaryPlayer(string uid);
+
+public void create()
+{
+    seteuid( getuid(this_object()) );
+    restore_object( FTPSAVE );
+    scan_bb_opfer();
+    EVENTD->RegisterEvent(EVT_LIB_LOGIN, "Eventhandler", this_object());
+    EVENTD->RegisterEvent(EVT_LIB_LOGOUT, "Eventhandler", this_object());
+    log_file("ARCH/bbmaster.log", strftime("%c: bbmaster wurde geladen.\n"));
+}
+
+// Auf den asynchronen Logout-event zu warten ermoeglicht es theoretisch, 1-2s
+// das Log zu umgehen. Andererseits geht das ohnehin nur dann, wenn die
+// Logzeit eh abgelaufen ist.
+public void Eventhandler(string eid, object trigob, mixed data) {
+  if (previous_object() == find_object(EVENTD)) {
+    string uid;
+    if (objectp(trigob)
+        && strstr(load_name(trigob),"/std/shells/") == 0
+        && !trigob->QueryGuest()) {
+      // Bei Login und Logout den BBMode einschalten (weil der Loginevent ja
+      // erst 1-2s nach Einloggen abgearbeitet wird.
+      trigob->__set_bb(1);
+      uid=getuid(trigob);
+    }
+    else {
+      // kein Objekt mehr da. Vermutlich hat ein Spieler 'ende' gemacht, aber
+      // es koennte auch sein, dass jemand mit nem Selbstzerstoerer den Event
+      // gefakt hat. Aber selbst wenn, viel kann man damit nicht erreichen.
+      uid = data[E_PLNAME];
+      if (!stringp(uid)) return;
+      // Pruefung auf nicht-Anwesenheit von uid waere noch moeglich, hat aber
+      // Probleme, wenn ein Spieler sehr schnell wieder einloggt.
+    }
+
+    if (eid == EVT_LIB_LOGOUT && member(ldata,uid)) {
+      // Wenn Logout und es gibt Daten im Puffer, koennte man die evtl.
+      // wegschreiben oder loeschen.
+      ProcessBuffer(uid);
+      // auf jeden Fall temporaere Spieler entfernen. (Wichtig!)
+      RemoveTemporaryPlayer(uid);
+    }
+  }
+}
+
+// schreibt alle Puffer synchron, ohne Callout... Kann laggen.
+public int ProcessAllBuffers() {
+  
+    if (extern_call() && !ARCH_SECURITY)
+    return -1;
+
+  foreach(string uid, int logtime, int flags, string erstie, string ip,
+          int index, mixed data: ldata) {
+    if (index) {
+      DumpData(uid, erstie, ip, flags, data);
+      ldata[uid,D_LOG]=({});
+      ldata[uid,D_INDEX]=0;
+    }
+  }
+  return 1;
+}
+
+private void ProcessBuffer(string uid) {
+    
+  if (time() <= ldata[uid,D_LOGTIME]
+      && ldata[uid,D_INDEX]) {
+    // Daten wegschreiben, wenn Logzeit nicht abgelaufen. Sonst nicht.
+    call_out(#'DumpData, 2, uid, ldata[uid,D_ERSTIE], ldata[uid,D_IPSTRING],
+	                    ldata[uid,D_FLAGS], ldata[uid,D_LOG]);
+  }
+  ldata[uid,D_LOG] = ({});
+  ldata[uid,D_INDEX] = 0;
+}
+
+private void DumpData(string uid, string erstie, string ip, int flags, mixed logdata) {
+  string res = sprintf("\n%s%s, IP: %s\n", capitalize(uid),
+                      (stringp(erstie) ? " ("+capitalize(erstie)+")" : ""),
+		      (stringp(ip) ? ip : "Unbekannt"));
+  logdata-=({0});
+  foreach(mixed arr : logdata) {
+    res+=sprintf("%O: %O [%s]\n", 
+        strftime("%y%m%d-%H%M%S",arr[DL_TIME]),
+        arr[DL_CMD], arr[DL_ENV] || "<unbekannt>");
+  }
+
+  //DEBUG("DumpData: "+res);
+  if (flags & FL_PERMANENT)
+    catch(log_file("ARCH/bb."+uid, res, MAXLOGSIZE));
+  else if (file_size(LIBLOGDIR"/ARCH/bbmaster") == FSIZE_DIR)
+    catch(log_file("ARCH/bbmaster/"+uid, res, SMALLLOGSIZE));
+  // kein else, in anderen Faellen werden die Daten verworfen.
+}
+
+private void AddTemporaryPlayer(string uid) {
+    // natuerlich nur, wenn noch nix eingetragen.
+    if (!member(ldata, uid)) {
+      object ob = find_player(uid) || find_netdead(uid);
+      
+      mixed erstie;
+      if (ob)
+        erstie = (string)ob->QueryProp(P_SECOND);
+
+      ldata += ([uid: time() + LOGTIME + random(LOGTIME/2); 
+	              0;
+		      (stringp(erstie) ? erstie : 0);
+		      query_ip_number(ob);
+		      0; ({})
+	        ]);
+    }
+}
+
+private void RemoveTemporaryPlayer(string uid) { 
+  if (!(ldata[uid,D_FLAGS] & FL_PERMANENT)) {
+    m_delete(ldata, uid);
+  }
+}
+
+
+// Vom Spielererobjekt bei Erschaffung in InitPlayer() gerufen.
+public int query_bb()
+{
+    
+    if (load_name(previous_object())[0..11] != "/std/shells/")
+        return 0;
+
+    // in jedem Fall wird nun (temporaer) der BB-Modus aktiviert.
+    if (!previous_object()->QueryGuest())
+      previous_object()->__set_bb(1);
+
+    // nur fuer 'permanente' auch 1 zurueckgeben.
+    return ldata[getuid(previous_object()),D_FLAGS] & FL_PERMANENT;
+}
+
+
+
+// neue Funktion. Kriegt nur Kommandosstring uebergegen, werden ggf. gepuffert
+// und dann weggeschrieben.
+public varargs void BBWrite(string msg, int catmode) {
+  
+  if ( !this_interactive() ||
+      (extern_call() && 
+       strstr(load_name(previous_object()), "/std/shells/") != 0 ) )
+    return;
+
+  string uid = getuid(this_interactive());
+
+  if (!member(ldata, uid))
+    AddTemporaryPlayer(uid);  
+  else if (ldata[uid,D_LOGTIME] < time()) {
+    // Logzeit abgelaufen. -> ENDE.
+    if (ldata[uid,D_INDEX]) {
+      this_interactive()->__set_bb(0);
+      // es kann vorkommen, dass hier nen ProcessBuffer mit anderer uid
+      // drinhaengt. Ist aber egal, dann wird der Puffer halt naechstesmal
+      // geschrieben.
+      if (find_call_out(#'ProcessBuffer) == -1)
+          call_out(#'ProcessBuffer, 2, uid);
+    }
+    return;
+  }
+
+  // im synchronen Modus direkt auf Platte schreiben.
+  //DEBUG("BBWrite: Flags von +"+uid+": "+to_string(ldata[uid,D_FLAGS])
+  //    +"\n");
+  if (ldata[uid,D_FLAGS] & FL_SYNC) {
+    //DEBUG("BBWrite: Syncmodus\n");
+    if (!catmode) {
+      msg = sprintf("%s: %s [%O]\n", strftime("%y%m%d-%H%M%S"),
+	  msg, environment(this_interactive()));
+    }
+    else
+      msg = msg + "\n";
+    log_file( "ARCH/bb."+uid, msg, MAXLOGSIZE );
+    return;
+  }
+
+  // alle anderen werden erstmal gepuffert. 
+
+  // wenn catmode und nen Index > 0 wird der Kram an den vorherigen Eintragen
+  // angehaengt.
+  int index = ldata[uid,D_INDEX];
+  if (catmode && index > 0) {
+    --index;
+    ldata[uid,D_LOG][index][DL_CMD] += msg;
+  }
+  else {
+    // Puffer vergroessern?
+    if (index >= sizeof(ldata[uid,D_LOG]))
+      ldata[uid,D_LOG]+=allocate(100);
+    ldata[uid,D_LOG][index] = ({ time(), msg, 
+	                         object_name(environment(this_interactive())) 
+                               });
+    ldata[uid,D_INDEX]++;
+    // es kann vorkommen, dass hier nen ProcessBuffer mit anderer uid
+    // drinhaengt. Ist aber egal, dann wird der Puffer halt naechstesmal
+    // geschrieben.
+    if (index > MAXBUFFSIZE 
+        && find_call_out(#'ProcessBuffer) == -1)
+      call_out(#'ProcessBuffer, 2, uid);
+  }
+}
+
+// Alte Funktion, kriegt Strings, teilweise mit Datum, teilweise ohne,
+// schrieb frueher nur weg. msg faengt entweder mit einem Datum/Zeit-String
+// oder mit einem "->" an.
+public void writebb( string msg )
+{
+  int catmode; 
+ 
+  if ( !this_interactive() ||
+      (extern_call() && 
+       strstr(load_name(previous_object()), "/std/shells/") != 0 ) )
+    return;
+ 
+  // erstmal String bereinigen.
+  msg = trim(msg,TRIM_RIGHT,"\n");
+  if (strstr(msg,"->") == 0) {
+    catmode=1;
+    msg= " -> " + msg[2..];
+  }
+  else {
+    // faengt mit Datumsstring an, erstes Leerzeichen ab dem zehnten Zeichen
+    // suchen und von dort abschneiden.
+    msg = msg[strstr(msg," ",10)+1 ..];
+  }
+
+  // Dann weitergeben
+  BBWrite(msg, catmode);
+}
+
+private void scan_bb_opfer()
+{
+    string* lines;
+    object pl;
+    string uid;
+
+    // diese user werden 'permanent' ueberwacht, nicht nur direkt nach dem
+    // Einloggen.
+    lines = explode( lower_case( read_file("/secure/ARCH/BB_OPFER.dump")
+                                       || "" ), "\n" )[2..];
+    
+    foreach(string line : lines) {
+        if( sizeof(line) && line[0] != '#' ) {
+	    uid=line[0 .. member(line,' ')-1];
+	    AddTemporaryPlayer(uid);
+	    ldata[uid,D_LOGTIME] = __INT_MAX__;
+	    ldata[uid,D_FLAGS] = FL_PERMANENT;
+	    pl = find_player(uid) || find_netdead(uid);
+	    if (pl)
+	      pl->__set_bb(1);
+	}
+    }
+}
+
+// Neuladen ist kein grosses Problem, weil der bbmaster ja automatisch
+// neugeladen wird. Nebeneffekt ist lediglich, dass fuer alle laufenden Logs
+// die Logzeit wieder von vorne anfaengt. Da das Schreiben der Puffer aber Lag
+// verursachen kann, duerfen es nur EM+.
+public varargs int remove(int silent) {
+ 
+  if (!ARCH_SECURITY)
+    return 0;
+
+  log_file("ARCH/bbmaster.log", strftime("%c: remove() called.\n"));
+
+  // alle Puffer leeren...
+  // ProcessAllBuffers() wird hierbei _ohne_ Limits aufgerufen! Kann fieses
+  // Lag erzeugen, aber sonst wurde der Kram evtl. nicht ordentlich
+  // geschrieben.
+  limited(#'ProcessAllBuffers);
+  destruct(this_object());
+  return 1;
+}
+
+
+// Alles ab hier nur zum Ueberwachen von FTP-Aktivitaeten.
+private int player_exists( string user )
+{
+    if ( !stringp( user ) || sizeof( user ) < 2 )
+        return 0;
+    
+  return file_size( "/save/" + user[0..0] + "/" + user + ".o" ) > 0;
+}
+
+public int add( string user, int timeout )
+{
+    if ( !ARCH_SECURITY || process_call() )
+        return -1;
+    
+    if( !stringp(user) || !player_exists(lower_case(user)) || !intp(timeout) ||
+        !timeout )
+        return -2;
+    
+    monitored[lower_case(user)] = timeout;
+    save_object( FTPSAVE );
+    
+    return 1;
+}
+
+
+public int sub( string user )
+{
+    if ( !ARCH_SECURITY || process_call() )
+        return -1;
+    
+    if( !stringp(user) || !member( monitored, lower_case(user) ) )
+        return -2;
+    
+    m_delete( monitored, lower_case(user) );
+    save_object( FTPSAVE );
+    
+    return 1;
+}
+
+
+public void ftpbb( string user, string msg )
+{
+    if( getuid(previous_object()) != ROOTID )
+        return;
+
+    if ( ldata[user,D_FLAGS] & FL_PERMANENT )
+        log_file( "ARCH/bb."+user, msg, 2000000 );
+
+    if ( monitored[user] ){
+        if ( monitored[user] > 0 && monitored[user] < time() )
+            sub( user );
+        else
+            CHMASTER->send( "FTP", capitalize(user), msg );
+    }
+}
+
diff --git a/secure/combat.c b/secure/combat.c
new file mode 100644
index 0000000..c6031a4
--- /dev/null
+++ b/secure/combat.c
@@ -0,0 +1,87 @@
+/*
+ * secure/combat.c
+ *
+ * the combat master object. It defines some useful functions to be
+ * used by weapons, armour and livings.
+ */
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+//#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#include <combat.h>
+
+int default_weapon_class(string type) {
+	switch(type) {
+		case "knife":
+			return 50;
+		case "club":
+			return 70;
+		case "sword":
+			return 100;
+		case "axe":
+			return 90;
+	}
+	return 30;
+}
+
+int default_weapon_weight(string type) {
+	switch(type) {
+		case "knife":
+			return 100;
+		case "club":
+			return 1500;
+		case "sword":
+			return 2000;
+		case "axe":
+			return 1500;
+	}
+	return 1000;
+}
+
+int default_weapon_value(string type) {
+	switch(type) {
+		case "knife":
+			return 10;
+		case "club":
+			return 50;
+		case "sword":
+			return 500;
+		case "axe":
+			return 300;
+	}
+	return(0);
+}
+
+int valid_weapon_type(mixed t) {
+	if (member(({WT_SWORD, WT_AXE, WT_CLUB, WT_SPEAR, WT_STAFF,
+               WT_KNIFE}), t ) != -1) {
+		 return 1;
+	}
+	else {
+		log_file("COMBAT","Invalid weapon type: "+t+", object: "+
+				object_name(previous_object())+"\n");
+		return 0;
+	}
+}
+
+
+int valid_armour_type(mixed t) {
+	if (VALID_ARMOUR_TYPE(t)) {
+		 return 1;
+	}
+	else {
+		log_file("COMBAT","Invalid armour type: "+t+", object: "+
+				object_name(previous_object())+"\n");
+		return 0;
+	}
+}
+
+
+int query_prevent_shadow() { return 1; }
+
diff --git a/secure/config.h b/secure/config.h
new file mode 100644
index 0000000..a70c8da
--- /dev/null
+++ b/secure/config.h
@@ -0,0 +1,151 @@
+#ifndef _CONFIG_
+#define _CONFIG_
+
+/*
+ * config.h
+ *
+ * general configuration is done here, the selection of gamedriver mode
+ * and the language to use.
+ */
+
+#define MUDHOST "mg"
+#define FTPD_IP "87.79.24.60"
+#define UDPSERV "87.79.24.60"
+
+// Guess a MUDNAME if none is set.
+#ifndef MUDNAME
+// set mudname to MorgenGrauen if Mud started on the MG server
+#  if MUDHOST == __HOST_NAME__ && !defined(__TESTMUD__)
+#    define MUDNAME "MorgenGrauen"
+#  else
+#    define MUDNAME "MG-Homemud"
+#  endif
+#endif
+
+#if MUDNAME == "MorgenGrauen"
+#  define SSLPORT 4712
+#else
+#  define SSLPORT 4714
+#endif
+
+// undef to disable MSSP support.
+#define MSSP_SUPPORT "MSSP-Plaintext"
+
+// undef to disable support for pure-ftpd virtual users
+#define _PUREFTPD_
+
+#define _MUDLIB_NAME_ "MorgenGrauen"
+#define _MUDLIB_VERSION_ "3.3.5"
+
+/* define general pathnames */
+#define MASTER          "secure/master"
+#define MAILPATH        "mail/"
+#define SAVEPATH        "save/"
+#define NEWSPATH        "news/"
+#define NEWSSERVER      "secure/news"
+#define SECURESAVEPATH  "secure/save/"
+#define COMBAT_MASTER   "secure/combat"
+
+#define WIZARDDIR       "players"
+#define DOMAINDIR       "d"
+#define PROJECTDIR      "p"
+#define DOCDIR          "doc"
+#define GUILDDIR        "gilden"
+#define SPELLBOOKDIR    "spellbooks"
+#define MAILDIR         "mail"
+#define LIBSAVEDIR      "save"
+#define FTPDIR          "open"
+#define TMPDIR          "tmp"
+#define STDDIR          "std"
+#define SYSDIR          "sys"
+#define LIBOBJDIR       "obj"
+#define LIBROOMDIR      "room"
+#define ETCDIR          "etc"
+#define LIBLOGDIR       "log"
+#define NEWSDIR         "news"
+#define SECUREDIR       "secure"
+#define LIBDATADIR      "data"
+#define LIBITEMDIR      "items"
+
+#define NETDEAD_ROOM "/room/netztot"
+#define NETDEAD_CHECK_TIME 5
+
+/* define special userids */ 
+#define BACKBONEID " S T D "
+#define ROOTID     " R O O T "			/* uppercase !! */
+#define MAILID     " M A I L "
+#define NEWSID     " N E W S "
+#define NOBODY     "NOBODY"
+#define ROOMID     "room"
+#define POLIZEIID  "polizei"
+#define DOCID      "DOC"
+#define GUILDID    "GUILD"
+#define ITEMID     "ITEMS"
+
+// "Besondere" Magierlevel
+#define WIZLVLS ([ ROOTID: 100,\
+                   ROOMID: 21,\
+                   POLIZEIID: 21,\
+                   "alle": 25,\
+                   NOBODY: 0,\
+                   DOCID: 0,\
+                   GUILDID: 30,\
+                   ITEMID: 0,\
+                 ])
+
+#define MAX_LOG_SIZE 50000
+
+#ifndef TESTMUD
+#  define CALL_OUT_HARD 1200
+#  define CALL_OUT_SOFT 1000
+#else // !TESTMUD
+#  ifdef MIN_CALL_OUT
+#    define CALL_OUT_HARD 120
+#    define CALL_OUT_SOFT 100
+#  else
+#    define CALL_OUT_HARD 120000
+#    define CALL_OUT_SOFT 100000
+#  endif
+#endif // TESTMUD
+
+#define BACKBONE_WIZINFO_SIZE 8
+#define LIVING_NAME 3
+#define NAME_LIVING 4
+#define MEMORY_BUFF 5
+#define NETDEAD_MAP 6
+#define IP_NAMES    7
+
+
+#ifndef SIMUL_EFUN_FILE
+#define SIMUL_EFUN_FILE       "secure/simul_efun/simul_efun"
+#endif
+#ifndef SPARE_SIMUL_EFUN_FILE
+#define SPARE_SIMUL_EFUN_FILE "secure/simul_efun/spare/simul_efun"
+#endif
+
+#define MAX_MAILS_PER_HOUR 200
+
+//max. groesse von Mappings und Arrays sollten vom Driver oder der
+//Kommandozeile vordefiniert sein. Wenn nicht:
+#ifndef __MAX_MAPPING_KEYS__
+#define __MAX_MAPPING_KEYS__ 30000
+#endif
+#ifndef __MAX_MAPPING_SIZE__
+#define __MAX_MAPPING_SIZE__ 60000
+#endif
+#ifndef __MAX_ARRAY_SIZE__
+#define __MAX_ARRAY_SIZE__ 10000
+#endif
+
+// Haben wir einen Fehlerdaemonen zu Speicher der Daten? Wenn ja, welchen?
+// Der normale braucht Support fuer sqlite. Wenn der nicht existiert, lassen
+// wir das mit dem Errord sein.
+#ifdef __SQLITE__
+#define ERRORD "/secure/errord.c"
+#endif
+
+// Savefile-Version
+#define __LIB__SAVE_FORMAT_VERSION__ 1
+
+#endif // _CONFIG_
+
diff --git a/secure/debug.c b/secure/debug.c
new file mode 100644
index 0000000..f5a07ec
--- /dev/null
+++ b/secure/debug.c
@@ -0,0 +1,86 @@
+// MorgenGrauen MUDlib
+/** \file /file.c
+* Kurzbeschreibung.
+* Langbeschreibung...
+* \author <Autor>
+* \date <date>
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strict_types,save_types,rtt_checks
+#pragma no_clone
+#pragma no_inherit
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+#include <functionlist.h>
+#include <lpctypes.h>
+
+#include <defines.h>
+#include <wizlevels.h>
+
+/** \def DEBUG
+  Outputs debug message to Maintainer, if Mainteiner is logged in. 
+*/
+#ifndef DEBUG
+#define DEBUG(x)  if (find_player("zesstra"))\
+            tell_object(find_player("zesstra"),\
+                                      "DDBG: "+x+"\n")
+#endif
+
+/** \fn set_object_next_reset(ob,zeit)
+  \brief setzt den naechsten Reset auf 'zeit'
+  \details setzt in Objekten den naechsten Reset - nur fuer EM+
+  \param[in] ob
+  (object) Objekt des Reset geaendert wird.
+  \param[in] zeit
+  (int) Zeit bis zum naechsten Reset.
+  \return (string) Gibt die uebergebene Zeit bis zum bisherigen Reset.
+  \author Zesstra
+  \date 06.10.2007
+  \sa set_object_heart_beat()
+*/
+// * Reset eines Objektes ein/ausschalten
+int set_object_next_reset(mixed ob, int zeit) {
+
+  if (stringp(ob))
+    ob=find_object(ob);
+
+  if (objectp(ob) && ELDER_SECURITY)
+  //if (objectp(ob) && SPECIAL_SECURITY && !clonep(ob))
+      return funcall(bind_lambda(#'efun::set_next_reset,ob),zeit);
+
+  return -2;
+}
+
+mixed query_variable(object ob, string var)
+{
+  if (!previous_object() || !IS_ARCH(geteuid(previous_object())) 
+      || !this_interactive() || !IS_ARCH(this_interactive())
+      || getuid(ob)==ROOTID )
+  {
+    write("Du bist kein EM oder Gott!\n");
+    return 0;
+  }
+
+  log_file("ARCH/QV", sprintf("%s: %O inquires var %s in %O\n",
+                              ctime(time()),this_interactive(),var,ob));
+  
+  mixed res = variable_list(ob, RETURN_FUNCTION_NAME|RETURN_FUNCTION_FLAGS|
+                                RETURN_FUNCTION_TYPE|RETURN_VARIABLE_VALUE);
+  int index = member(res,var);
+  if (index > -1)
+  {
+    return ({res[index],res[index+1],res[index+2],res[index+3]});
+  }
+
+  return 0;
+}
+
+protected void create() {
+  // secure_level() in *_SECURITY() prueft auf die EUID
+  seteuid(getuid(ME));
+}
+
diff --git a/secure/dropmaster.c b/secure/dropmaster.c
new file mode 100644
index 0000000..dcd09c1
--- /dev/null
+++ b/secure/dropmaster.c
@@ -0,0 +1,202 @@
+#pragma strict_types,save_types,rtt_checks
+#pragma pedantic,range_check
+#pragma no_inherit,no_clone
+#pragma no_shadow
+
+#define _NEED_DROPMASTER_IMPLEMENTATION_
+#include <dropmaster.h>
+#include <defines.h>
+#include <wizlevels.h>
+
+struct random_event_s {
+  string *names;
+  int reduction;
+  int *rnd_values;
+  int *default_rnd_values;
+  string creator;
+};
+
+// Mapping mit einem random_event_s als Wert pro Schluessel
+// Schluessel: 0: globaler Eintrag, ansonsten BP-Namen oder Teile von Pfaden
+// (/d/<eben>/<magier>/<gebiet>/)
+private mapping randomEvents;
+
+private struct random_event_s SanitizeRE(struct random_event_s re)
+{
+    // rnd_values, default_rnd_values und names muessen gleich lang sein -> auf
+    // das kuerzeste kuerzen. Achtung: macht implizit eine Kopie - WICHTIG!
+    int size = sizeof(re->rnd_values);
+    if (pointerp(re->names))
+        size = min(size, sizeof(re->names));
+    if (pointerp(re->default_rnd_values)) {
+      size = min(size, sizeof(re->default_rnd_values));
+      re->default_rnd_values = re->default_rnd_values[0..size-1];
+    }
+    if (pointerp(re->names))
+      re->names = re->names[0..size-1];
+
+    re->rnd_values = re->rnd_values[0..size-1];
+
+    return re;
+}
+
+// key==0 signifies the global key
+public varargs void CreateRandomEvent(string key, int *rnd_val, 
+                                      string *rnd_names, int red)
+{
+  struct random_event_s re = (<random_event_s>);
+  
+  if (!member(randomEvents,key)) {
+    re->rnd_values = copy(rnd_val) || DFLT_RND_VALUES;
+    // wenn rnd_val == 0 ist, ist das OK, dann wird spaeter immer
+    // DFLT_RND_VALUES genommen.
+    re->default_rnd_values = copy(rnd_val);
+    // names darf auch 0 sein, wenn Defaultwerte genommen werden sollen.
+    re->names = copy(rnd_names);
+    re->reduction = red || RND_REDUCTION;
+    re->creator = object_name(extern_call() ? previous_object() : this_object());
+
+    randomEvents[key] = SanitizeRE(re);
+  }
+}
+
+public varargs int ChangeRandomEvent(string key, int *rnd_val, int *rnd_val_dflt, 
+                                      string *rnd_names, int red)
+{
+  if (process_call()) return -1;
+  if (!member(randomEvents, key)) return -2;
+  struct random_event_s re = randomEvents[key];
+  // Schreibzugriff erlauben? Nur fuer Erschafferobjekte und EM.
+  // Bemerkung: RM+ haben Zugriff, weil sie Schreibzugriff auf das
+  // Erschafferobjekt haben.
+  if (extern_call()
+      && re->creator != object_name(previous_object())
+      && !ARCH_SECURITY)
+    return -1;
+
+  if (pointerp(rnd_val))
+    re->rnd_values = rnd_val;
+  if (pointerp(rnd_val_dflt))
+    re->default_rnd_values = rnd_val_dflt;
+  if (pointerp(rnd_names))
+    re->names = rnd_names;
+  if (red)
+    re->reduction = red;
+
+  SanitizeRE(re);
+
+  return 1;
+}
+
+public int DeleteRandomEvent(string key)
+{
+  if (process_call()) return -1;
+  if (!member(randomEvents, key)) return -2;
+  struct random_event_s re = randomEvents[key];
+  // Schreibzugriff erlauben? Nur fuer Erschafferobjekte und EM.
+  // Bemerkung: RMs haben Zugriff, weil sie Schreibzugriff auf das
+  // Erschafferobjekt haben.
+  if (extern_call()
+      && re->creator != object_name(previous_object())
+      && !ARCH_SECURITY)
+    return -1;
+
+  m_delete(randomEvents, key);
+  return 1;
+}
+
+void create()
+{
+  seteuid(getuid(this_object()));
+  restore_object(DROPSAVE);
+  if(!randomEvents || !mappingp(randomEvents))
+  {
+    randomEvents = ([]);
+  }
+  if (!member(randomEvents, GLOBAL_KEY))
+    CreateRandomEvent(GLOBAL_KEY, DFLT_RND_VALUES, DFLT_RND_NAMES, RND_REDUCTION);
+  set_next_reset(3600 + random(10800));
+}
+
+void saveme(){
+  save_object(DROPSAVE);
+}
+
+varargs int remove()
+{
+  saveme();
+  destruct(ME);
+  return 1;
+}
+
+public varargs int dropRare(int rarelevel, string key) {
+  if (!member(randomEvents, key))
+    return 0;
+  
+  struct random_event_s re = randomEvents[key];
+  if (rarelevel < 0 || rarelevel >= sizeof(re->rnd_values))
+    return 0;
+  if (random(re->rnd_values[rarelevel]) == 0)
+  {
+    // Defaultwert wiederherstellen.
+    if (pointerp(re->default_rnd_values))
+      re->rnd_values[rarelevel] = re->default_rnd_values[rarelevel];
+    else
+      re->rnd_values[rarelevel] = DFLT_RND_VALUES[rarelevel];
+    // und droppen.
+    return 1;
+  }
+  else
+  {
+    // Wahrscheinlichkeit erhoehen, indem rnd_values reduziert wird.
+    re->rnd_values[rarelevel] -= re->reduction;
+    // negative rnd_values sind ok, random() ist dann immer 0.
+  }
+  // fall-through
+  return 0;
+}
+
+// Droppt genau 1 oder 0 Items aus dem gegebenen Set. Benutzt hierbei das
+// mittels <key> definierte Randomevent und den zur jeweiligen ID gegebenen
+// <rarelevel>.
+// <set> muss in der Form
+// { {ID,RARELEVEL}, {ID,RARELEVEL}, ... }
+// gegeben werden. <ID> muss dabei != 0 sein und wird im Erfolgsfall
+// zurueckgeben.
+mixed dropSetItem(mixed set, string key) {
+  
+  if (pointerp(set))
+  {
+    set=filter(set, function int (mixed el) {
+      return pointerp(el) && sizeof(el)>=2 && intp(el[1]);
+    } );
+    set=sort_array(set, function int (mixed a, mixed b) {
+      return a[1]>b[1];
+    });
+    
+    foreach(mixed el: set) {
+      if(dropRare(el[1], key)){
+        return el[0];
+      }
+    }      
+  }
+  
+  return 0;
+}
+
+void reset()
+{
+  set_next_reset(3600*72);
+  // ein wenig aufraeumen
+
+  // Alle Events rauswerfen, deren Erschafferobjekt nicht mehr exisiert
+  // (Nachteil: nicht-geladene VC-Objekte werden nicht beruecksichtigt).
+  foreach(string key, struct random_event_s re: randomEvents) {
+    if (!find_object(re->creator)
+        && file_size(re->creator + ".c") <= 0)
+      m_delete(randomEvents, key);
+  }
+
+  saveme();
+}
+
diff --git a/secure/dropmaster.h b/secure/dropmaster.h
new file mode 100644
index 0000000..3eb2bff
--- /dev/null
+++ b/secure/dropmaster.h
@@ -0,0 +1,28 @@
+#ifndef _DROPMASTER_
+#define _DROPMASTER_
+
+// path to dropmaster
+#define DROPMASTER "/secure/dropmaster"
+#define DROPSAVE "/secure/ARCH/DROPMASTER"
+
+// rarelevels
+#define COMMON					      0
+#define UNCOMMON				      1
+#define RARE					        2
+#define VERY_RARE				      3
+#define ULTRA_RARE				    4
+#define RATHER_NOT_PROBABLE		5
+#define DFLT_RND_NAMES	({"gewoehnlich","ungewoehnlich",\
+                          "selten","sehr selten","aussergewoehnlich selten",\
+                          "praktisch nie"})
+
+#if defined(_NEED_DROPMASTER_IMPLEMENTATION_) && !defined(_DROPMASTER_IMPLEMENTATION_)
+#define _DROPMASTER_IMPLEMENTATION_
+// random helper
+#define GLOBAL_KEY	0
+#define DFLT_RND_VALUES  ({100,1000,10000,100000,200000,1000000})
+#define RND_REDUCTION	10
+
+#endif // _DROPMASTER_IMPLEMENTATION_
+
+#endif // _DROPMASTER_
diff --git a/secure/errord-structs.c b/secure/errord-structs.c
new file mode 100644
index 0000000..185da99
--- /dev/null
+++ b/secure/errord-structs.c
@@ -0,0 +1,62 @@
+#pragma strong_types,rtt_checks,save_types
+
+struct frame_s {
+  int    id;
+  int    type;
+  string name;
+  string prog;
+  string obj;
+  int    loc;
+  int    ticks;
+};
+
+struct note_s {
+  int    id;
+  int    time;
+  string user;
+  string txt;
+};
+
+struct base_issue_s {
+  int    id;
+  string hashkey;
+  string uid;
+  int    type;
+  int    mtime;
+  int    ctime;
+  int    atime;
+  int    count;
+  int    deleted;
+  int    resolved;
+  int    locked;
+  string locked_by;
+  int    locked_time;
+  string resolver;
+  string message;
+  string loadname;
+};
+
+struct ctissue_s (base_issue_s) {
+};
+
+struct userissue_s (ctissue_s) {
+  string obj;
+  string prog;
+  int    loc;
+  string titp;
+  string tienv;
+};
+
+struct rtissue_s (userissue_s) {
+  string hbobj;
+  int    caught;
+  string command;
+  string verb;
+};
+
+// all possible data including stack trace and notes.
+struct fullissue_s (rtissue_s) {
+  mixed * notes; //struct note_s *
+  mixed * stack; //struct frame_s *
+};
+
diff --git a/secure/errord.c b/secure/errord.c
new file mode 100644
index 0000000..0c9c4e8
--- /dev/null
+++ b/secure/errord.c
@@ -0,0 +1,1482 @@
+/*  MorgenGrauen MUDlib
+    /p/daemon/errord.c
+    speichert Fehler und Warnungen
+    Autor: Zesstra
+    $Id: errord.c 9439 2016-01-20 09:48:28Z Zesstra $
+    ggf. Changelog:
+*/
+
+#pragma strict_types,save_types,rtt_checks
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma pedantic
+#pragma range_check
+#pragma warn_deprecated
+
+#include <config.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <debug_info.h>
+#include <commands.h>
+#include <wizlevels.h>
+#include <mail.h>
+#include <tls.h>
+#include <events.h>
+
+inherit "/secure/errord-structs";
+
+#define __NEED_IMPLEMENTATION__
+#include "errord.h"
+#undef __NEED_IMPLEMENTATION__
+
+#define SAVEFILE  (__DIR__+"ARCH/errord")
+
+#define TI this_interactive()
+
+private int       access_check(string uid,int mode);
+private varargs int set_lock(int issueid, int lock, string note);
+private varargs int set_resolution(int issueid, int resolution, string note);
+
+private int versende_mail(struct fullissue_s fehler);
+
+nosave mapping lasterror;   // die letzen 5 jeder Art.
+
+
+/* ******************* Helfer **************************** */
+
+public int getErrorID(string hashkey)
+{
+  int** row=sl_exec("SELECT id from issues WHERE hashkey=?1;",
+                    hashkey);
+  //DEBUG(sprintf("getEID: %s: %O\n",hashkey,row));
+  if (pointerp(row))
+  {
+    return row[0][0];
+  }
+  return -1;
+}
+
+// note->id muss auf einen Eintrag in issues verweisen, es erfolgt keine
+// Pruefung.
+int db_add_note(struct note_s note)
+{
+  sl_exec("INSERT INTO notes(issueid,time,user,txt) "
+          "VALUES(?1,?2,?3,?4);",
+          to_array(note)...);
+  return sl_insert_id();
+}
+
+private struct frame_s* db_get_stack(int issueid)
+{
+  mixed rows = sl_exec("SELECT * FROM stacktraces WHERE issueid=?1 "
+                       "ORDER BY rowid;", issueid);
+  if (pointerp(rows))
+  {
+    struct frame_s* stack = allocate(sizeof(rows));
+    int i;
+    foreach(mixed row : rows)
+    {
+      stack[i] = to_struct(row, (<frame_s>));
+      ++i;
+    }
+    return stack;
+  }
+  return 0;
+}
+
+private struct note_s* db_get_notes(int issueid)
+{
+  mixed rows = sl_exec("SELECT * FROM notes WHERE issueid=?1 "
+                       "ORDER BY rowid;", issueid);
+  if (pointerp(rows))
+  {
+    struct note_s* notes = allocate(sizeof(rows));
+    int i;
+    foreach(mixed row : rows)
+    {
+      notes[i] = to_struct(row, (<note_s>));
+      ++i;
+    }
+    return notes;
+  }
+  return 0;
+}
+
+// einen durch id oder hashkey bezeichneten Eintrag als fullissue_s liefern.
+private struct fullissue_s db_get_issue(int issueid, string hashkey)
+{
+  mixed rows = sl_exec("SELECT * FROM issues WHERE id=?1 OR hashkey=?2;",
+                       issueid, hashkey);
+  if (pointerp(rows))
+  {
+    // Einfachster Weg - funktioniert aber nur, solange die Felder in der DB
+    // die gleiche Reihenfolge wie in der struct haben! Entweder immer
+    // sicherstellen oder Ergebnisreihenfolge oben im select festlegen!
+    struct fullissue_s issue = to_struct( rows[0], (<fullissue_s>) );
+    if (issue->type == T_RTERROR)
+        issue->stack = db_get_stack(issue->id);
+    issue->notes = db_get_notes(issue->id);
+    return issue;
+  }
+  return 0;
+}
+
+private struct fullissue_s filter_private(struct fullissue_s issue)
+{
+    //momentan wird F_CLI, also die Spielereingabe vor dem Fehler
+    //ausgefiltert, wenn TI kein EM oder man in process_string() ist.
+
+    //Wenn EM und nicht in process_string() oder die Spielereingabe gar nicht
+    //im Fehlereintrag drinsteht: ungefiltert zurueck
+    if (!issue->command ||
+        (!process_call() && ARCH_SECURITY) )
+        return issue;
+
+    //sonst F_CLI rausfiltern, also Kopie und in der Kopie aendern.
+    issue->command="Bitte EM fragen";
+    return issue;
+}
+
+// setzt oder loescht die Loeschsperre.
+// Prueft, ob <issueid> existiert und aendert den Zustand nur, wenn noetig.
+// Rueckgabe: -1, wenn Issue nicht existiert, -2 wenn bereits resolved, -3
+//            wenn keine Aenderung noetig, sonst den neuen Sperrzustand
+int db_set_lock(int issueid, int lockstate, string note)
+{
+  int** rows = sl_exec("SELECT locked,resolved FROM issues WHERE id=?1;",
+                       issueid);
+  if (!rows)
+    return -1;  // nicht vorhanden.
+
+  if (rows[0][1])
+      return -2; // bereits resolved -> Sperre nicht moeglich.
+
+
+  if (lockstate && !rows[0][0])
+  {
+    // Sperren
+//    sl_exec("BEGIN TRANSACTION;");
+    sl_exec("UPDATE issues SET locked=1,locked_by=?2,locked_time=?3,mtime=?3 "
+            "WHERE id=?1;",
+            issueid, getuid(TI), time());
+    db_add_note( (<note_s> id: issueid, time: time(), user: getuid(TI),
+                           txt: sprintf("Lock gesetzt: %s", 
+                                        note ? note : "<kein Kommentar>")) );
+//    sl_exec("COMMIT;");
+    return 1;
+  }
+  else if (!lockstate && rows[0][0])
+  {
+    // entsperren
+//    sl_exec("BEGIN TRANSACTION;");
+    sl_exec("UPDATE issues SET locked=0,locked_by=0,locked_time=0,mtime=?2 "
+            "WHERE id=?1;", issueid, time());
+    db_add_note( (<note_s> id: issueid, time: time(), user: getuid(TI),
+                           txt: sprintf("Lock geloescht: %s", 
+                                        note ? note : "<kein Kommentar>")) );
+//    sl_exec("COMMIT;");
+    return 0;
+  }
+  // nix aendern.
+  return -3;
+}
+
+// markiert ein Issue als gefixt oder nicht gefixt.
+// Prueft, ob <issueid> existiert und aendert den Zustand nur, wenn noetig.
+// Rueckgabe: -1, wenn Issue nicht existiert, -3 wenn keine Aenderung noetig,
+//            sonst den neuen Sperrzustand
+int db_set_resolution(int issueid, int resolved, string note)
+{
+  int** rows = sl_exec("SELECT resolved FROM issues WHERE id=?1;",
+                       issueid);
+  if (!rows)
+    return -1;  // nicht vorhanden.
+
+  if (resolved && !rows[0][0])
+  {
+    // Als gefixt markieren.
+//    sl_exec("BEGIN TRANSACTION;");
+    sl_exec("UPDATE issues SET resolved=1,resolver=?2,mtime=?3 "
+            "WHERE id=?1;",
+            issueid, getuid(TI),time());
+    db_add_note( (<note_s> id: issueid, time: time(), user: getuid(TI),
+                           txt: sprintf("Fehler gefixt: %s", 
+                                        note ? note : "<kein Kommentar>")) );
+//    sl_exec("COMMIT;");
+    return 1;
+  }
+  else if (!resolved && rows[0][0])
+  {
+    // als nicht gefixt markieren.
+//    sl_exec("BEGIN TRANSACTION;");
+    sl_exec("UPDATE issues SET resolved=0,resolver=0,mtime=?2 "
+            "WHERE id=?1;", issueid, time());
+    db_add_note( (<note_s> id: issueid, time: time(), user: getuid(TI),
+                           txt: sprintf("Fix zurueckgezogen: %s", 
+                                        note ? note : "<kein Kommentar>")) );
+//    sl_exec("COMMIT;");
+    return 0;
+  }
+  // nix aendern.
+  return -3;
+
+}
+
+// Transferiert ein Issue zu einer neuen zustaendigen UID
+// Prueft, ob <issueid> existiert und aendert den Zustand nur, wenn noetig.
+// Rueckgabe: -1, wenn Issue nicht existiert, -3 wenn keine Aenderung noetig,
+//            1, wenn erfolgreich neu zugewiesen
+int db_reassign_issue(int issueid, string newuid, string note)
+{
+  string** rows = sl_exec("SELECT uid FROM issues WHERE id=?1;",
+                       issueid);
+  if (!rows)
+    return -1;  // nicht vorhanden.
+
+  if (!stringp(newuid))
+    return(-2);
+
+  if (newuid != rows[0][0])
+  {
+//    sl_exec("BEGIN TRANSACTION;");
+    sl_exec("UPDATE issues SET uid=?2,mtime=?3 WHERE id=?1;",
+            issueid, newuid,time());
+    db_add_note( (<note_s> id: issueid, time: time(), user: getuid(TI),
+                           txt: sprintf("Fehler von %s an %s uebertragen. (%s)",
+                                        rows[0][0], newuid,
+                                        note ? note : "<kein Kommentar>")) );
+//    sl_exec("COMMIT;");
+    return 1;
+  }
+
+  return -3;
+}
+
+// inkrementiert count und aktualisiert mtime, atime.
+// Ausserdem wird ggf. das Loeschflag genullt - ein erneut aufgetretener
+// Fehler sollte anschliessend nicht mehr geloescht sein. Geloeste
+// (resolved) Eintraege werden NICHT auf ungeloest gesetzt. Vermutlich trat
+// der Fehler in einem alten Objekte auf...
+// Issue muss in der DB existieren.
+int db_countup_issue(int issueid)
+{
+  sl_exec("UPDATE issues SET count=count+1,mtime=?2,atime=?2,deleted=0 WHERE id=?1;",
+          issueid,time());
+  return 1;
+}
+
+// Das Issue wird ggf. ent-loescht und als nicht resvolved markiert.
+// Sind pl und msg != 0, wird eine Notiz angehaengt.
+// aktualisiert mtime, atime.
+// Issue muss in der DB existieren.
+int db_reopen_issue(int issueid, string pl, string msg)
+{
+  int** row=sl_exec("SELECT deleted,resolved from issues WHERE id=?1;",
+                    issueid);
+  if (pointerp(row)
+      && (row[0][0] || row[0][1]) )
+  {
+//    sl_exec("BEGIN TRANSACTION;");
+    if (pl && msg)
+    {
+      db_add_note( (<note_s> id: issueid,
+                             time: time(),
+                             user: pl,
+                             txt: msg) );
+    }
+    sl_exec("UPDATE issues SET "
+        "deleted=0,resolved=0,resolver=0,mtime=?2,atime=?2 WHERE id=?1;",
+        issueid,time());
+//    sl_exec("COMMIT;");
+  }
+  return 1;
+}
+
+int db_insert_issue(struct fullissue_s issue)
+{
+  //DEBUG(sprintf("db_insert: %O\n", issue));
+
+  mixed row=sl_exec("SELECT id from issues WHERE hashkey=?1;",
+                    issue->hashkey);
+  //DEBUG(sprintf("insert: %s: %O\n",issue->hashkey,row));
+  if (pointerp(row))
+  {
+    issue->id=row[0][0];
+    return db_countup_issue(issue->id);
+  }
+//  sl_exec("BEGIN TRANSACTION;");
+  sl_exec("INSERT INTO issues(hashkey,uid,type,mtime,ctime,atime,count,"
+          "deleted,resolved,locked,locked_by,locked_time,resolver,message,"
+          "loadname,obj,prog,loc,titp,tienv,hbobj,caught,command,verb) "
+          "VALUES(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13,?14,?15,"
+          "?16,?17,?18,?19,?20,?21,?22,?23,?24);",
+          (to_array(issue)[1..24])...); 
+  issue->id=sl_insert_id();
+
+  if (pointerp(issue->stack))
+  {
+    foreach(struct frame_s entry : issue->stack)
+    {
+      entry->id = issue->id;
+      sl_exec("INSERT INTO stacktraces(issueid,type,name,prog,obj,loc,ticks) "
+              "VALUES(?1,?2,?3,?4,?5,?6,?7);",
+              to_array(entry)...);
+    }
+  }
+  if (pointerp(issue->notes))
+  {
+    foreach(struct note_s entry : issue->notes)
+    {
+      entry->id = issue->id;
+      sl_exec("INSERT INTO notes(issueid,time,user,txt) "
+              "VALUES(?1,?2,?3,?4);",
+              to_array(entry)...);
+    }
+  }
+//  sl_exec("COMMIT;");
+
+  return issue->id;
+}
+
+
+// loggt einen T_REPORTED_ERR, T_REPORTED_IDEA, T_REPORTED_TYPO, T_REPORTED_MD
+public string LogReportedError(mapping err)
+{
+    //darf nur von Spielershells oder Fehlerteufel gerufen werden.
+    if (extern_call() && !previous_object()
+        || (strstr(load_name(previous_object()),"/std/shells/") == -1
+           && load_name(previous_object()) != "/obj/tools/fehlerteufel"))
+        return 0;
+
+    //DEBUG("LogReportedError\n");
+    // DEBUG(sprintf("%O\n",err));
+    string uid = (string)master()->creator_file(err[F_OBJ]);
+
+    // default-Typ
+    if (!member(err, F_TYPE)) err[F_TYPE] = T_REPORTED_ERR;
+
+    // div. Keys duerfen nicht gesetzt sein.
+    err -= ([F_STATE, F_READSTAMP, F_CAUGHT, F_STACK, F_CLI, F_VERB,
+             F_LOCK, F_RESOLVER, F_NOTES]);
+
+    // Errormapping in issue-struct umwandeln und befuellen.
+    struct fullissue_s issue = (<fullissue_s>);
+    issue->type = err[F_TYPE];
+    issue->uid = uid;
+    issue->mtime = issue->ctime = issue->atime = time();
+    issue->count=1;
+    issue->loadname = load_name(err[F_OBJ]);
+    issue->message = err[F_MSG];
+    issue->obj = object_name(err[F_OBJ]);
+    // Normalisieren auf fuehrenden / und kein .c
+    if (err[F_PROG]!="unbekannt")
+        issue->prog = load_name(err[F_PROG]);
+    else
+        issue->prog = "unbekannt";
+    issue->titp = getuid(this_interactive() || this_player());
+    if (objectp(err[F_OBJ]))
+      issue->tienv = object_name(environment(err[F_OBJ]));
+
+    //DEBUG(sprintf("%O\n",issue));
+    issue->hashkey = hash(TLS_HASH_MD5,
+        sprintf("%d%s%s", issue->type, issue->loadname, issue->message));
+
+    // ggf. vorhandenen Fehler suchen - zugegeben: sollte bei von Spielern
+    // gemeldeten Dingen vermutlich nie vorkommen...
+    int oldid = getErrorID(issue->hashkey);
+    if (oldid >= 0)
+    {
+      // ggf. sicherstellen, dass er wieder eroeffnet wird.
+      db_reopen_issue(oldid, "<ErrorD>",
+                      "Automatisch wiedereroeffnet wegen erneutem Auftreten.");
+      db_countup_issue(oldid);
+      return issue->hashkey;
+    }
+
+    // sonst fuegen wir einen neuen Eintrag hinzu
+    // Spielergemeldete Bugs werden erstmal vor automatischem Loeschen
+    // geschuetzt, bis ein zustaendiger Magier ihn zur Kenntnis nimmt und
+    // entsperrt.
+    issue->locked = 1;
+    issue->locked_by = getuid(TI || PL);
+    issue->locked_time = time();
+
+    // In DB eintragen.
+    issue->id = db_insert_issue(issue);
+
+    lasterror[issue->type]=issue->id;
+
+    // Event triggern, aber nur eine Teilmenge der Daten direkt mitliefern.
+    EVENTD->TriggerEvent(EVT_LIB_NEW_ERROR,
+              ([F_TYPE: issue->type, F_HASHKEY:issue->hashkey,
+               F_UID:issue->uid, F_ID: issue->id]));
+
+    DEBUG(sprintf("LogReportedError: %s\n",issue->hashkey));
+
+    return issue->hashkey;
+}
+
+//Fehler registrieren
+//Diese Funktion darf nicht mehr als 200k Ticks verbrauchen und wird nur vom
+//Master gerufen!
+public int LogError(string msg,string prg,string curobj,int line,mixed culprit,
+    int caught)
+{
+    //DEBUG(sprintf("LogError: Prog: %O, Obj: %O,\n",prg,curobj));
+
+    //darf nur vom Master gerufen werden
+    if (!extern_call() || 
+        (previous_object() && previous_object() != master()))
+        return 0;
+
+    struct fullissue_s issue = (<fullissue_s>);
+
+    //UID bestimmen
+    issue->uid=(string)master()->creator_file(curobj);
+    //DEBUG(sprintf("LogError: UID: %s\n",uid));
+
+    //Loadname (besser als BP, falls rename_object() benutzt wurde) bestimmen
+    if (!stringp(curobj) || !sizeof(curobj))
+        issue->loadname = curobj = "<Unbekannt>";
+    else
+    {
+        //load_name nimmt Strings und Objects und konstruiert den loadname,
+        //wie er sein sollte, wenn das Objekt nicht mehr existiert.
+        issue->loadname=load_name(curobj);
+    }
+    if (!stringp(issue->loadname))
+    {
+        //hier kommt man rein, falls curobj ein 'kaputter' Name ist,
+        //d.h. load_name() 0 liefert.
+        issue->loadname="<Illegal object name>";
+    }
+
+    // Wenn curobj in /players/ liegt, es einen TI gibt, welcher ein Magier
+    // ist und dieser die Prop P_DONT_LOG_ERRORS gesetzt hat, wird der FEhler
+    // nicht gespeichert.
+    if (this_interactive() && IS_LEARNER(this_interactive())
+        && strstr(issue->loadname,WIZARDDIR)==0
+        && this_interactive()->QueryProp(P_DONT_LOG_ERRORS))
+    {
+        return 0;
+    }
+
+    // prg und curobj auf fuehrenden / und ohne .c am Ende normieren.
+    if (stringp(prg))
+        issue->prog = load_name(prg);
+    if (stringp(curobj) && curobj[0]!='/')
+    {
+      curobj="/"+curobj;
+    }
+
+    issue->obj = curobj;
+    issue->loc = line;
+    issue->message = msg;
+    issue->ctime = issue->mtime = issue->atime = time();
+    issue->type = T_RTERROR;
+    issue->count = 1;
+    issue->caught = caught;
+
+    //Hashkey bestimmen: Typ, Name der Blueprint des buggenden Objekts,
+    //Programmname, Zeilennr., Fehlermeldung
+    //TODO: evtl. sha1() statt md5()?
+    issue->hashkey=hash(TLS_HASH_MD5,
+        sprintf("%d%s%s%d%s", T_RTERROR, issue->loadname||"",
+                            issue->prog || "", issue->loc,
+                            issue->message||"<No error message given.>"));
+    DEBUG(sprintf("LogError: Hashkey: %s", issue->hashkey));
+
+    // ggf. vorhandenen Fehler suchen
+    int oldid = getErrorID(issue->hashkey);
+    if (oldid >= 0)
+    {
+      db_reopen_issue(oldid, "<ErrorD>",
+                      "Automatisch wiedereroeffnet wegen erneutem Auftreten.");
+      db_countup_issue(oldid);
+      return oldid;
+    }
+
+    //sonst fuegen wir einen neuen Eintrag hinzu
+    //DEBUG(sprintf("LogError: OBJ: %s, BP: %s",curobj,loadname));
+    // Wenn Fehler im HB, Objektnamen ermitteln
+    if (objectp(culprit))
+        issue->hbobj = object_name(culprit);
+
+    //gibt es einen TI/TP? Name mit erfassen
+    mixed tienv;
+    if(objectp(this_interactive()))
+    {
+        issue->titp=getuid(this_interactive());
+        tienv=environment(this_interactive());
+    }
+    else if (objectp(PL) && query_once_interactive(PL))
+    {
+        issue->titp=getuid(PL);
+        tienv=environment(PL);
+    }
+    else if (objectp(PL))
+    {
+        issue->titp=object_name(PL);
+        tienv=environment(PL);
+    }
+    if (objectp(tienv))
+        issue->tienv=object_name(tienv);
+
+    // Mal schauen, ob der Commandstack auch was fuer uns hat. ;-)
+    mixed cli;
+    if (pointerp(cli=command_stack()) && sizeof(cli))
+    {
+        issue->verb=cli[0][CMD_VERB];
+        issue->command=cli[0][CMD_TEXT];
+    }
+
+    //stacktrace holen
+    mixed stacktrace;
+    if (caught)
+        stacktrace=debug_info(DINFO_TRACE,DIT_ERROR);
+    else
+        stacktrace=debug_info(DINFO_TRACE,DIT_UNCAUGHT_ERROR);
+    // gueltige Stacktraces haben min. 2 Elemente.
+    // (leerer Trace: ({"No trace."}))
+    if (sizeof(stacktrace) > 1)
+    {
+      int i;
+      issue->stack = allocate(sizeof(stacktrace)-1);
+      // erstes Element ist 0 oder HB-Objekt: kein frame, daher ueberspringen
+      foreach(mixed entry : stacktrace[1..])
+      {
+        // frame->id will be set later by db_insert_issue().
+        struct frame_s frame = (<frame_s> type : entry[TRACE_TYPE],
+                                          name:  entry[TRACE_NAME],
+                                          prog:  entry[TRACE_PROGRAM],
+                                          obj:   entry[TRACE_OBJECT],
+                                          loc:   entry[TRACE_LOC],
+                                          ticks: entry[TRACE_EVALCOST]);
+        issue->stack[i] = frame;
+        ++i;
+      }
+    }
+
+    issue->id = db_insert_issue(issue);
+
+    lasterror[T_RTERROR]=issue->id;
+
+    // Event triggern, aber nur eine Teilmenge der Daten direkt mitliefern.
+    EVENTD->TriggerEvent(EVT_LIB_NEW_ERROR,
+              ([ F_TYPE: T_RTERROR, F_HASHKEY: issue->hashkey, F_UID:
+               issue->uid, F_ID: issue->id ]));
+
+//    DEBUG(sprintf("LogError: Fehlereintrag:\n%O\n",
+//          errors[uid][hashkey]));
+//    DEBUG(sprintf("LogError: Verbrauchte Ticks: %d\n",
+//          200000-get_eval_cost()));
+    return issue->id;
+}
+
+//Warnungen registrieren
+//Diese Funktion darf nicht mehr als 200k Ticks verbrauchen und wird nur vom
+//Master gerufen!
+public int LogWarning(string msg,string prg,string curobj,int line, int in_catch)
+{
+    //DEBUG(sprintf("LogWarning: Prog: %O, Obj: %O,\n",prg,curobj));
+
+    //darf nur vom Master gerufen werden
+    if (!extern_call() || 
+        (previous_object() && previous_object() != master()))
+        return 0;
+
+    struct fullissue_s issue = (<fullissue_s>);
+
+    //UID bestimmen
+    issue->uid=(string)master()->creator_file(curobj);
+    //DEBUG(sprintf("LogWarning UID: %s\n",uid));
+
+    //Loadname (besser als BP, falls rename_object() benutzt wurde) bestimmen
+    if (!stringp(curobj) || !sizeof(curobj))
+        issue->loadname = curobj = "<Unbekannt>";
+    else
+    {
+        //load_name nimmt Strings und Objects und konstruiert den loadname,
+        //wie er sein sollte, wenn das Objekt nicht mehr existiert.
+        issue->loadname=load_name(curobj);
+    }
+
+    if (!stringp(issue->loadname))
+        //hier sollte man reinkommen, falls curobj ein 'kaputter' Name ist,
+        //d.h. load_name() 0 liefert.
+        issue->loadname="<Illegal object name>";
+
+    // prg und curobj auf abs. Pfade normalisieren.
+    if (stringp(prg))
+        issue->prog=load_name(prg);
+    if (stringp(curobj) && curobj[0]!='/')
+    {
+      curobj="/"+curobj;
+    }
+
+    //DEBUG(sprintf("LogWarning: OBJ: %s, BP: %s\n",curobj,blue));
+
+    // Wenn curobj in /players/ liegt, es einen TI gibt, welcher ein Magier
+    // ist und dieser die Prop P_DONT_LOG_ERRORS gesetzt hat, wird der FEhler
+    // nicht gespeichert.
+    if (this_interactive() && IS_LEARNER(this_interactive())
+        && strstr(issue->loadname,WIZARDDIR)==0
+        && this_interactive()->QueryProp(P_DONT_LOG_ERRORS)) {
+        return 0;
+    }
+
+    //Hashkey bestimmen, Typ, Name der Blueprint des buggenden Objekts, Programm
+    //Zeilennr., Warnungsmeldung
+    issue->hashkey=hash(TLS_HASH_MD5,
+        sprintf("%d%s%s%d%s", T_RTWARN, issue->loadname, issue->prog, line,
+                           msg));
+    //DEBUG(sprintf("LogWarning: Hashkey: %s",hashkey));
+
+
+    // ggf. vorhandenen Fehler suchen
+    int oldid = getErrorID(issue->hashkey);
+    if (oldid >= 0)
+    {
+      db_reopen_issue(oldid, "<ErrorD>",
+                      "Automatisch wiedereroeffnet wegen erneutem Auftreten.");
+      db_countup_issue(oldid);
+      return oldid;
+    }
+
+    //sonst fuegen wir einen neuen Eintrag hinzu
+    // erstmal vervollstaendigen
+    issue->obj = curobj;
+    issue->message = msg;
+    issue->ctime = issue->mtime = issue->atime = time();
+    issue->loc = line;
+    issue->count = 1;
+    issue->type = T_RTWARN;
+    issue->caught = in_catch;
+
+    //gibt es einen TI/TP? Name mit erfassen
+    mixed tienv;
+    if(objectp(this_interactive()))
+    {
+        issue->titp=getuid(this_interactive());
+        tienv=environment(this_interactive());
+    }
+    else if (objectp(PL) && query_once_interactive(PL))
+    {
+        issue->titp=getuid(PL);
+        tienv=environment(PL);
+    }
+    else if (objectp(PL))
+    {
+        issue->titp=object_name(PL);
+        tienv=environment(PL);
+    }
+    if (objectp(tienv))
+        issue->tienv=object_name(tienv);
+
+    // Mal schauen, ob der Commandstack auch was fuer uns hat. ;-)
+    mixed cli;
+    if (pointerp(cli=command_stack()) && sizeof(cli))
+    {
+        issue->verb=cli[0][CMD_VERB];
+        issue->command=cli[0][CMD_TEXT];
+    }
+
+    issue->id = db_insert_issue(issue);
+
+    lasterror[T_RTWARN]=issue->id;
+    // Event triggern, aber nur eine Teilmenge der Daten direkt mitliefern.
+    EVENTD->TriggerEvent(EVT_LIB_NEW_ERROR,
+        ([F_TYPE: issue->type, F_ID: issue->id,
+          F_UID: issue->uid, F_HASHKEY: issue->hashkey]) );
+
+//    DEBUG(sprintf("LogWarning: Warnungseintrag:\n%O\n",
+//          warnings[uid][hashkey]));
+//    DEBUG(sprintf("LogWarning: Verbrauchte Ticks: %d\n",
+//          200000-get_eval_cost()));
+    return issue->id;
+}
+
+//Warnungen und Fehler beim Kompilieren  registrieren
+//Diese Funktion darf nicht mehr als 200k Ticks verbrauchen und wird nur vom
+//Master gerufen!
+public int LogCompileProblem(string file,string msg,int warn) {
+
+    //DEBUG(sprintf("LogCompileProblem: Prog: %O, Obj: %O,\n",file,msg));
+
+    //darf nur vom Master gerufen werden
+    if (!extern_call() || 
+        (previous_object() && previous_object() != master()))
+        return 0;
+
+    struct fullissue_s issue = (<fullissue_s>);
+
+    //UID bestimmen
+    issue->uid=(string)master()->creator_file(file);
+    //DEBUG(sprintf("LogCompileProblem UID: %s\n",uid));
+
+    // An File a) fuehrenden / anhaengen und b) endendes .c abschneiden. Macht
+    // beides netterweise load_name().
+    issue->loadname = load_name(file);
+    issue->type = warn ? T_CTWARN : T_CTERROR;
+
+    //loggen wir fuer das File ueberhaupt?
+    if (member(BLACKLIST,explode(issue->loadname,"/")[<1])>=0)
+        return 0;
+
+    //Hashkey bestimmen, in diesem Fall einfach, wir koennen die
+    //Fehlermeldunge selber nehmen.
+    issue->hashkey=hash(TLS_HASH_MD5,sprintf(
+          "%d%s%s",issue->type,issue->loadname, msg));
+    //DEBUG(sprintf("LogCompileProblem: Hashkey: %s",hashkey));
+
+    // ggf. vorhandenen Fehler suchen
+    int oldid = getErrorID(issue->hashkey);
+    if (oldid >= 0)
+    {
+      db_reopen_issue(oldid, "<ErrorD>",
+                      "Automatisch wiedereroeffnet wegen erneutem Auftreten.");
+      db_countup_issue(oldid);
+      return oldid;
+    }
+
+    // neuen Eintrag
+    issue->message = msg;
+    issue->count = 1;
+    issue->ctime = issue->mtime = issue->atime = time();
+
+    issue->id = db_insert_issue(issue);
+
+    if (warn) lasterror[T_CTWARN]=issue->id;
+    else lasterror[T_CTERROR]=issue->id;
+
+//    DEBUG(sprintf("LogCompileProblem: Eintrag:\n%O\n",
+//          (warn ? ctwarnings[uid][hashkey] : cterrors[uid][hashkey])));
+//   DEBUG(sprintf("LogCompileProblem: Verbrauchte Ticks: %d\n",
+//          200000-get_eval_cost()));
+   return issue->id;
+}
+
+/* ****************  Public Interface ****************** */
+
+//Einen bestimmten Fehler nach Hashkey suchen und als fullissue_s inkl. Notes
+//und Stacktrace liefern.
+struct fullissue_s QueryIssueByHash(string hashkey)
+{
+  struct fullissue_s issue = db_get_issue(0, hashkey);
+  if (structp(issue))
+    return filter_private(issue);
+  return 0;
+}
+//Einen bestimmten Fehler nach ID suchen und als fullissue_s inkl. Notes
+//und Stacktrace liefern.
+struct fullissue_s QueryIssueByID(int issueid)
+{
+  struct fullissue_s issue = db_get_issue(issueid, 0);
+  if (structp(issue))
+    return filter_private(issue);
+  return 0;
+}
+
+// den letzten Eintrag den jeweiligen Typ liefern.
+struct fullissue_s QueryLastIssue(int type)
+{
+    if (!member(lasterror,type))
+        return 0;
+    //einfach den kompletten letzten Eintrag zurueckliefern
+    return(QueryIssueByID(lasterror[type]));
+}
+
+// Liefert alle Issues, deren obj,prog oder loadname gleich <file> ist.
+public struct fullissue_s* QueryIssuesByFile(string file, int type)
+{
+  mixed rows = sl_exec("SELECT * FROM issues "
+                       "WHERE (loadname=?1 OR prog=?1 OR obj=?1) "
+                       "AND deleted=0 AND resolved=0 AND type=?2"
+                       "ORDER BY type,mtime;", file, type);
+  if (pointerp(rows))
+  {
+    struct fullissue_s* ilist = allocate(sizeof(rows));
+    int i;
+    foreach(mixed row : rows)
+    {
+      // Einfachster Weg - funktioniert aber nur, solange die Felder in der DB
+      // die gleiche Reihenfolge wie in der struct haben! Entweder immer
+      // sicherstellen oder Ergebnisreihenfolge oben im select festlegen!
+      struct fullissue_s issue = to_struct( row, (<fullissue_s>) );
+      if (issue->type == T_RTERROR)
+          issue->stack = db_get_stack(issue->id);
+      issue->notes = db_get_notes(issue->id);
+      ilist[i] = filter_private(issue);
+      ++i;
+    }
+    return ilist;
+  }
+  return 0;
+}
+
+// Liefert eine Liste von allen IDs, Loadnames, UIDs und Typen fuer die
+// angebenen <type> und <uid>.
+varargs < <int|string>* >* QueryIssueListByFile(string file)
+{
+  mixed rows = sl_exec("SELECT id,loadname,obj,prog,loc FROM issues "
+                       "WHERE (loadname=?1 OR prog=?1 OR obj=?1) "
+                       "AND deleted=0 AND resolved=0 "
+                       "ORDER BY type,mtime;", file);
+  return rows;
+}
+
+// Liefert eine Liste von allen IDs, Loadnames, UIDs und Typen fuer die
+// angebenen <type> und <uid>.
+varargs < <int|string>* >* QueryIssueListByLoadname(string file, int type)
+{
+  mixed rows;
+  if (type && file)
+  {
+     rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
+                       "WHERE loadname=?1 AND type=?2 AND deleted=0 "
+                       "AND resolved=0 "
+                       "ORDER BY type,mtime;", file, type);
+  }
+  else if (type)
+  {
+     rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
+                       "WHERE type=?1 AND deleted=0 AND resolved=0 "
+                       "ORDER BY type,mtime;", type);
+  }
+  else if (file)
+  {
+    rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
+                       "WHERE loadname=?1 AND deleted=0 AND resolved=0 "
+                       "ORDER BY type,mtime;", file);
+  }
+  return rows;
+}
+
+
+// Liefert eine Liste von allen IDs, Loadnames, UIDs und Typen fuer die
+// angebenen <type> und <uid>.
+varargs < <int|string>* >* QueryIssueList(int type, string uid)
+{
+  mixed rows;
+  if (type && uid)
+  {
+    rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
+                   "WHERE type=?1 AND uid=?2 AND deleted=0 "
+                   "AND resolved=0 "
+                   "ORDER BY type,rowid;", type,uid);
+  }
+  else if (type)
+  {
+    rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
+                   "WHERE type=?1 AND deleted=0 AND resolved=0 "
+                   "ORDER BY type,rowid;", type);
+  }
+  else if (uid)
+  {
+    rows = sl_exec("SELECT id,loadname,uid,type FROM issues "
+                   "WHERE uid=?1 AND deleted=0 AND resolved=0 "
+                   "ORDER BY type,rowid;", uid);
+  }
+  return rows;
+}
+
+varargs string* QueryUIDsForType(int type) {
+    //liefert alle UIDs fuer einen Fehlertyp oder fuer alle Fehlertypen
+    string** rows;
+
+    if (type)
+    {
+      rows = sl_exec("SELECT uid FROM issues "
+                     "WHERE type=?1 AND deleted=0 AND resvoled=0;", type);
+    }
+    else
+      rows = sl_exec("SELECT uid FROM issues WHERE deleted=0 "
+                     "AND resolved=0;");
+
+    return map(rows, function string (string* item)
+                     {return item[0];} );
+}
+
+//Wieviele unterschiedliche Fehler in diesem Typ?
+varargs int QueryUniqueIssueCount(int type, string uid)
+{
+  int** rows;
+
+  if (type && uid)
+  {
+    rows = sl_exec("SELECT count(*) FROM issues "
+                   "WHERE type=?1 AND uid=?2 AND deleted=0 AND resolved=0;",
+                   type, uid);
+  }
+  else if (type)
+  {
+    rows = sl_exec("SELECT count(*) FROM issues "
+                   "WHERE type=?1 AND deleted=0 AND resolved=0;",
+                   type);
+  }
+  else if (uid)
+  {
+    rows = sl_exec("SELECT count(*) FROM issues "
+                   "WHERE uid=?1 AND deleted=0 AND resolved=0;",
+                   uid);
+  }
+  else
+    rows = sl_exec("SELECT count(*) FROM issues "
+                   "WHERE deleted=0 AND resolved=0;");
+
+  return rows[0][0];
+}
+
+//Einen bestimmten Fehler loeschen
+varargs int ToggleDeleteError(int issueid, string note)
+{
+  mixed rows = sl_exec("SELECT uid,deleted from issues WHERE id=?1;",
+                       issueid);
+  if (!pointerp(rows))
+    return -1;
+  
+  if (!access_check(rows[0][0], M_DELETE))
+    //zugriff zum Schreiben nicht gestattet
+    return -10;
+
+//  sl_exec("BEGIN TRANSACTION;");
+  if (rows[0][1])
+  {
+    // was deleted -> undelete it
+    sl_exec("UPDATE issues SET deleted=0,mtime=?2 WHERE id=?1;",
+            issueid,time());
+    db_add_note((<note_s> id: issueid, time: time(), user: getuid(TI),
+                          txt: sprintf("Loeschmarkierung entfernt. (%s)",
+                                       note ? note: "<kein Kommentar>")
+                ));
+  }
+  else
+  {
+    // was not deleted -> delete it.
+    sl_exec("UPDATE issues SET deleted=1,mtime=?2 WHERE id=?1;",
+            issueid, time());
+    db_add_note((<note_s> id: issueid, time: time(), user: getuid(TI),
+                          txt: sprintf("Loeschmarkierung gesetzt. (%s)",
+                                       note ? note: "<kein Kommentar>")
+                ));
+  }
+//  sl_exec("COMMIT;");
+  return !rows[0][1];
+}
+
+
+// sperrt den Eintrag oder entsperrt ihn.
+// Sperre heisst hier, dass der Fehler vom Expire nicht automatisch geloescht
+// wird.
+varargs int LockIssue(int issueid, string note) {
+    return set_lock(issueid, 1, note);
+}
+
+varargs int UnlockIssue(int issueid, string note) {
+    return set_lock(issueid, 0, note);
+}
+
+// einen Fehler als gefixt markieren
+varargs int ResolveIssue(int issueid, string note) {
+    return set_resolution(issueid, 1, note);
+}
+// einen Fehler als nicht gefixt markieren
+varargs int ReOpenIssue(int issueid, string note) {
+    return set_resolution(issueid, 0, note);
+}
+
+varargs int AddNote(int issueid, string note)
+{
+
+    if (!stringp(note) || !sizeof(note))
+      return(-3);
+
+    // existiert die ID in der DB?
+    struct fullissue_s issue = db_get_issue(issueid,0);
+    if (!issue)
+      return -1;
+
+    if (!access_check(issue->uid, M_WRITE))
+        //zugriff zum Schreiben nicht gestattet
+        return(-10);
+
+    return db_add_note((<note_s> id: issueid, time: time(), user: getuid(TI),
+                                 txt: note));
+}
+
+//Einen bestimmten Fehler einer anderen UID zuweisen.
+//Hashkey ist zwar eindeutig, aber Angabe der
+//der UID ist deutlich schneller. Weglassen des Typs nochmal langsamer. ;-)
+//Potentiell also sehr teuer, wenn man UID oder UID+Typ weglaesst.
+varargs int ReassignIssue(int issueid, string newuid, string note)
+{
+    struct fullissue_s issue = db_get_issue(issueid,0);
+    if (!issue)
+      return -1;
+
+    if (!access_check(issue->uid, M_REASSIGN))
+        //zugriff zum Schreiben nicht gestattet
+        return(-10);
+
+    return db_reassign_issue(issueid, newuid, note);
+}
+
+/* *********** Eher fuer Debug-Zwecke *********************** */
+/*
+mixed QueryAll(int type) {
+    //das koennte ein sehr sehr grosses Mapping sein, ausserdem wird keine
+    //Kopie zurueckgegeben, daher erstmal nur ich...
+    if (!this_interactive() || 
+        member(MAINTAINER,getuid(this_interactive()))<0)
+        return(-1);
+    if (process_call()) return(-2);
+    if (!type) return(-3);
+    return(errors[type]);
+}
+
+mixed QueryResolved()
+{
+    //das koennte ein sehr sehr grosses Mapping sein, ausserdem wird keine
+    //Kopie zurueckgegeben, daher erstmal nur ich...
+    if (!this_interactive() || 
+        member(MAINTAINER,getuid(this_interactive()))<0)
+        return(-1);
+    if (process_call()) return(-2);
+    return(resolved);
+}
+*/
+
+/* *****************  Internal Stuff   ******************** */
+void create() {
+    seteuid(getuid(ME));
+
+    if (sl_open("/secure/ARCH/errord.sqlite") != 1)
+    //if (sl_open("/errord.sqlite") != 1)
+    {
+      raise_error("Datenbank konnte nicht geoeffnet werden.\n");
+    }
+    sl_exec("PRAGMA foreign_keys = ON; PRAGMA temp_store = 2; ");
+    //string* res=sl_exec("PRAGMA quick_check(N);");
+    //if (pointerp(res))
+    //{
+    //  raise_error("");
+    //}
+    //sl_exec("CREATE TABLE issue(id INTEGER,haskey TEXT);");
+    foreach (string cmd :
+        explode(read_file("/secure/ARCH/errord.sql.init"),";\n"))
+    {
+      if (sizeof(cmd) && cmd != "\n")
+      {
+        sl_exec(cmd);
+      }
+    }
+    sl_exec("ANALYZE main;");
+
+    lasterror=([]);
+}
+
+string name() {return("<Error-Daemon>");}
+
+void save_me(int now) {
+  if (now)
+    save_object(SAVEFILE);
+  else if (find_call_out(#'save_object)==-1) {
+    call_out(#'save_object, 30, SAVEFILE);
+  }
+}
+
+varargs int remove(int silent) {
+    save_me(1);
+    destruct(ME);
+    return(1);
+}
+
+
+// sperrt den Eintrag (lock!=0) oder entsperrt ihn (lock==0). 
+// Sperre heisst hier, dass der Fehler vom Expire nicht automatisch geloescht
+// wird.
+// liefert <0 im Fehlerfall, sonst Array mit Lockdaten
+private varargs int set_lock(int issueid, int lock, string note)
+{
+    struct fullissue_s issue = db_get_issue(issueid,0);
+    if (!issue)
+      return -1;
+
+    if (!access_check(issue->uid, M_WRITE))
+        //zugriff zum Schreiben nicht gestattet
+        return(-10);
+
+    return db_set_lock(issueid, lock, note);
+}
+
+//markiert einen Fehler als gefixt, mit 'note' als Bemerkung (res!=0) oder
+//markiert einen Fehler wieder als nicht-gefixt (resolution==0)
+//liefert < 0 im Fehlerfall, sonst den neuen Zustand.
+private varargs int set_resolution(int issueid, int resolution, string note)
+{
+    struct fullissue_s issue = db_get_issue(issueid,0);
+    if (!issue)
+      return -1;
+
+    // Fixen duerfen nur zustaendige
+    if (resolution
+        && !access_check(issue->uid, M_FIX))
+      return -10;
+    // ggf. Fix zurueckziehen darf jeder mit M_WRITE
+    if (!resolution &&
+        !access_check(issue->uid, M_WRITE))
+      return -10;
+
+    int res = db_set_resolution(issueid, resolution, note);
+
+    if (res == 1)
+    {
+      // Fehler jetzt gefixt.
+      versende_mail(db_get_issue(issueid,0));  // per Mail verschicken
+    }
+
+    return res;
+}
+
+//ist der Zugriff auf uid erlaubt? Geprueft wird TI (wenn kein TI, auch kein
+//Schreibzugriff)
+//mode gibt an, ob lesend oder schreibend
+private int access_check(string uid, int mode) {
+
+    if (mode==M_READ)
+        return LEARNER_SECURITY;  //lesen darf jeder Magier
+
+    // In process_string() schonmal gar nicht.
+    if (process_call()) return(0);
+    // EM+ duerfen alles loeschen.
+    if (ARCH_SECURITY) return(1);
+    // eigene UIDs darf man auch vollumfaenglich bearbeiten.
+    if (secure_euid()==uid) return(1);
+
+    // alles andere wird speziell geprueft
+    switch(mode)
+    {
+      // M_WRITE ist zur zeit eigentlich immer nen Append - das duerfen zur
+      // Zeit alle ab Vollmagier
+      case M_WRITE:
+        return LEARNER_SECURITY;
+        break;
+      // Loeschen und Fixen duerfen zur Zeit nur Zustaendige.
+      case M_DELETE:
+      case M_REASSIGN:
+      case M_FIX:
+        // Master nach UIDs fragen, fuer die der jew. Magier
+        // zustaendig ist.
+        if (member((string *)master()->QueryUIDsForWizard(secure_euid()),uid) >= 0)
+          return 1;
+
+        break;
+    }
+
+    return(0);        //Fall-through, neinRNER_SECURITY
+}
+
+public string format_stacktrace(struct frame_s* stacktrace) {
+    string *lines;
+
+    if (!pointerp(stacktrace) || !sizeof(stacktrace))
+        return("");
+
+    lines=allocate(sizeof(stacktrace));
+    int i;
+    foreach(struct frame_s frame: stacktrace)
+    {
+      lines[i]=sprintf("Fun: %.20O in Prog: %.40s\n"
+                       "   Zeile: %.8d [%.50s]\n"
+                       "   Evalcosts: %d",
+                       frame->name,frame->prog,
+                       frame->loc,frame->obj,frame->ticks);
+      ++i;
+    }
+    return(implode(lines,"\n"));
+}
+
+public string format_notes(struct note_s* notes)
+{
+  int i;
+  string text="";
+  foreach(struct note_s note: notes)
+  {
+    text+=sprintf("Notiz %d von %.10s am %.30s\n%s",
+        ++i,capitalize(note->user),dtime(note->time),
+        break_string(note->txt, 78,"  "));
+  }
+  return text;
+}
+
+public string format_error_spieler(struct fullissue_s issue)
+{
+  string txt;
+  string *label;
+
+  if (!issue)
+    return 0;
+
+  switch(issue->type)
+  {
+    case T_RTERROR:
+      label=({"Laufzeitfehler","Dieser Laufzeitfehler"});
+      break;
+    case T_REPORTED_ERR:
+      label=({"Fehlerhinweis","Dieser Fehlerhinweis"});
+      break;
+    case T_REPORTED_TYPO:
+      label=({"Typo","Dieser Typo"});
+      break;
+    case T_REPORTED_IDEA:
+      label=({"Idee","Diese Idee"});
+      break;
+    case T_REPORTED_MD:
+      label=({"Fehlendes Detail","Dieses fehlende Detail"});
+      break;
+    case T_RTWARN:
+      label=({"Laufzeitwarnung","Diese Laufzeitwarnung"});
+      break;
+    case T_CTWARN:
+      label=({"Ladezeitwarnung","Diese Ladezeitwarnung"});
+      break;
+    case T_CTERROR:
+      label=({"Ladezeitfehler","Dieser Ladezeitfehler"});
+      break;
+    default: return 0;
+  }
+
+  txt=sprintf( "\nDaten fuer %s mit ID '%s':\n"
+               "Zeit: %25s\n",
+               label[0], issue->hashkey,
+               dtime(issue->ctime)
+              );
+
+  txt+=sprintf("%s",break_string(issue->message,78,
+                  "Meldung:    ",BS_INDENT_ONCE));
+
+  if (pointerp(issue->notes))
+      txt+="Bemerkungen:\n"+format_notes(issue->notes)+"\n";
+
+  return txt;
+}
+
+public string format_error(struct fullissue_s issue, int only_essential)
+{
+  string txt;
+  string *label;
+
+  if (!issue)
+    return 0;
+
+  switch(issue->type)
+  {
+    case T_RTERROR:
+      label=({"Laufzeitfehler","Dieser Laufzeitfehler"});
+      break;
+    case T_REPORTED_ERR:
+      label=({"Fehlerhinweis","Dieser Fehlerhinweis"});
+      break;
+    case T_REPORTED_TYPO:
+      label=({"Typo","Dieser Typo"});
+      break;
+    case T_REPORTED_IDEA:
+      label=({"Idee","Diese Idee"});
+      break;
+    case T_REPORTED_MD:
+      label=({"Fehlendes Detail","Dieses fehlende Detail"});
+      break;
+    case T_RTWARN:
+      label=({"Laufzeitwarnung","Diese Laufzeitwarnung"});
+      break;
+    case T_CTWARN:
+      label=({"Ladezeitwarnung","Diese Ladezeitwarnung"});
+      break;
+    case T_CTERROR:
+      label=({"Ladezeitfehler","Dieser Ladezeitfehler"});
+      break;
+    default: return 0;
+  }
+
+  txt=sprintf( "\nDaten fuer %s mit ID %d:\n"
+               "Hashkey: %s\n"
+               "Zeit: %25s (Erstmalig: %25s)\n",
+               label[0], issue->id, issue->hashkey,
+               dtime(abs(issue->mtime)),dtime(issue->ctime)
+              );
+
+  if (stringp(issue->prog))
+      txt += sprintf(
+               "Programm:   %.60s\n"
+               "Zeile:      %.60d\n",
+               issue->prog, issue->loc
+               );
+
+  if (stringp(issue->obj))
+      txt+=sprintf("Objekt:     %.60s\n",
+               issue->obj);
+
+  txt += sprintf("Loadname:   %.60s\n"
+                 "UID:        %.60s\n",
+                 issue->loadname, issue->uid);
+
+  txt+=sprintf("%s",break_string(issue->message,78,
+                  "Meldung:    ",BS_INDENT_ONCE));
+  if (stringp(issue->hbobj))
+      txt+=sprintf(
+               "HB-Obj:     %.60s\n",issue->hbobj);
+
+  if (stringp(issue->titp)) {
+      txt+=sprintf(
+               "TI/TP:      %.60s\n",issue->titp);
+      if (stringp(issue->tienv))
+          txt+=sprintf(
+               "Environm.:  %.60s\n",issue->tienv);
+  }
+
+  if (!stringp(issue->command) ||
+      !ARCH_SECURITY || process_call())
+  {
+      // Kommandoeingabe ist Privatsphaere und darf nicht von jedem einsehbar
+      // sein.
+      // in diesem Fall aber zumindest das Verb ausgeben, so vorhanden
+      if (issue->verb)
+          txt+=sprintf(
+              "Verb:        %.60s\n",issue->verb);
+  }
+  // !process_call() && ARCH_SECURITY erfuellt...
+  else if (stringp(issue->command))
+      txt+=sprintf(
+          "Befehl:     %.60s\n",issue->command);
+
+  if (issue->caught)
+      txt+=label[1]+" trat in einem 'catch()' auf.\n";
+
+  if (!only_essential)
+  {
+    if (issue->deleted)
+        txt+=label[1]+" wurde als geloescht markiert.\n";
+
+    if (issue->locked)
+        txt+=break_string(
+            sprintf("%s wurde von %s am %s vor automatischem Loeschen "
+            "geschuetzt (locked).\n",
+            label[1],issue->locked_by, dtime(issue->locked_time)),78);
+    if (issue->resolved)
+        txt+=label[1]+" wurde als erledigt markiert.\n";
+  }
+
+  txt+=sprintf("%s trat bisher %d Mal auf.\n",
+               label[1],issue->count);
+
+  if (pointerp(issue->stack))
+      txt+="Stacktrace:\n"+format_stacktrace(issue->stack)+"\n";
+
+  if (pointerp(issue->notes))
+      txt+="Bemerkungen:\n"+format_notes(issue->notes)+"\n";
+
+  return txt;
+}
+
+// letzter Aenderung eines Spieler-/Magiersavefiles. Naeherung fuer letzten
+// Logout ohne das Savefile einzulesen und P_LAST_LOGOUT zu pruefen.
+private int recent_lastlogout(string nam, int validtime)
+{
+  if (!nam || sizeof(nam)<2) return 0;
+  return file_time("/"LIBSAVEDIR"/" + nam[0..0] + "/" + nam + ".o") >= validtime;
+}
+
+// Versendet Mail an zustaendigen Magier und ggf. Spieler, der den Eintrag
+// erstellt hat.
+// ACHTUNG: loescht issue->command.
+private int versende_mail(struct fullissue_s issue)
+{
+  // Versendet eine mail mit dem gefixten Fehler.
+  mixed *mail;
+  string text, *empf;
+  int res = -1;
+
+  mail = allocate(9);
+  mail[MSG_FROM] = "<Fehler-Daemon>";
+  mail[MSG_SENDER] = getuid(TI);
+  mail[MSG_BCC] = 0;
+  mail[MSG_SUBJECT] = sprintf("Fehler/Warnung in %s behoben", issue->loadname);
+  mail[MSG_DATE] = dtime(time());
+
+  // auch wenn ein EM fixt, sollen die Empfaenger nicht automatisch die
+  // Spielereingabe erhalten, also command loeschen.
+  issue->command = 0;
+
+  // erstmal eine Mail an zustaendige Magier.
+  empf = (string*)master()->QueryWizardsForUID(issue->uid);
+  // lang (180 Tage) nicht eingeloggte Magier ausfiltern
+  empf = filter(empf, #'recent_lastlogout, time() - 15552000);
+  if (sizeof(empf))
+  {
+
+    text = break_string(
+      sprintf(STANDARDMAILTEXT,capitalize(getuid(TI)))
+      +format_error(issue, 1),78,"",BS_LEAVE_MY_LFS);
+
+    mail[MSG_RECIPIENT] = empf[0];
+    if (sizeof(empf)>1)
+      mail[MSG_CC] = empf[1..];
+    else
+      mail[MSG_CC] = 0;
+    mail[MSG_BODY]=text;
+    mail[MSG_ID]=sprintf(MUDNAME": %d.%d",time(),random(__INT_MAX__));
+
+    if (!sizeof("/secure/mailer"->DeliverMail(mail,0)))
+      res = -1; // an niemanden erfolgreich zugestellt. :-(
+    else
+      res = 1;
+  }
+
+  // Bei von Spielern gemeldeten Fehler werden Spieler bei
+  // Erledigung informiert, wenn deren letzter Logout weniger als 180 Tage her
+  // ist.
+  if ( (issue->type &
+        (T_REPORTED_ERR|T_REPORTED_TYPO|T_REPORTED_IDEA|T_REPORTED_MD))
+      && issue->titp
+      && recent_lastlogout(issue->titp, time() - 15552000) )
+  {
+    text = break_string(
+      sprintf(STANDARDMAILTEXT_ERRORHINT,
+        capitalize(issue->titp), capitalize(getuid(TI)))
+      +format_error_spieler(issue), 78,"",BS_LEAVE_MY_LFS);
+
+    mail[MSG_ID]=sprintf(MUDNAME": %d.%d",time(),random(__INT_MAX__));
+    mail[MSG_RECIPIENT] = issue->titp;
+    mail[MSG_CC] = 0;
+    mail[MSG_SUBJECT] = sprintf("Fehler/Idee/Typo wurde von %s behoben",
+                                getuid(TI));
+    mail[MSG_BODY] = text;
+
+    if (!sizeof("/secure/mailer"->DeliverMail(mail,0)))
+      res |= -1;
+    else
+      res |= 1;
+  }
+
+  return res;
+}
+
+void reset()
+{
+  // geloeschte Issues sofort, gefixte 30 Tage nach letzter Aenderung
+  // loeschen.
+  sl_exec("DELETE FROM issues WHERE deleted=1;");
+  sl_exec("DELETE FROM issues WHERE resolved=1 AND mtime<?1;",
+          time()-30*24*3600);
+  set_next_reset(3600*24);
+}
+
+// Nicht jeder Magier muss den ErrorD direkt zerstoeren koennen.
+public string NotifyDestruct(object caller) {
+  if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
+    return "Du darfst den Error-Daemon nicht zerstoeren!\n";
+  }
+  return 0;
+}
+
diff --git a/secure/errord.h b/secure/errord.h
new file mode 100644
index 0000000..bd82301
--- /dev/null
+++ b/secure/errord.h
@@ -0,0 +1,112 @@
+/*  MorgenGrauen MUDlib
+    /sys/errord.h
+    Headerfile fuer den Error-Daemonen
+    Autor: Zesstra
+    $Id: errord.h 9439 2016-01-20 09:48:28Z Zesstra $
+    ggf. Changelog:
+*/
+
+#ifndef __ERRORD_H__
+#define __ERRORD_H__
+
+// Typen
+#define T_RTERROR   1	   //Runtime Error / Laufzeitfehler
+#define T_RTWARN    2    //Runtime Warning / Laufzeitwarnung
+#define T_CTERROR   4    //Compiletime Error / Fehler beim Uebersetzen
+#define T_CTWARN    8    //Compiletime Warning / Warnung beim Uebersetzen
+#define T_REPORTED_ERR   16   // von Spielern gemeldete Bugs
+#define T_REPORTED_TYPO  32   // von Spielern gemeldete Typos
+#define T_REPORTED_IDEA  64   // von Spielern gemeldete Ideen 
+#define T_REPORTED_MD    128  // von Spielern gemeldete fehlende Details
+#define ALL_ERR_TYPES ({T_RTERROR, T_RTWARN, T_CTERROR, T_CTWARN, \
+                        T_REPORTED_ERR, T_REPORTED_TYPO, T_REPORTED_IDEA, \
+                        T_REPORTED_MD })
+
+// Status
+#define STAT_DELETED  0x1
+#define STAT_LOCKED   0x2
+#define STAT_RESOLVED 0x4
+
+// Achtung: viele Keys koennen nicht vorhanden oder Werte 0 sein!
+#define F_ID          "id"          //int: (row) ID in der DB
+#define F_TYPE        "type"        //int: Typ-Werte, s.o.
+#define F_HASHKEY     "hashkey"     //string: Hashkey des Fehlers
+#define F_UID         "uid"         //string: "UID des Fehlers"
+#define F_STATE       "state"       //int: Status-Werte, s.o.
+#define F_MODSTAMP    "mtime"       //int
+#define F_CREATESTAMP "ctime"       //int
+#define F_READSTAMP   "atime"       //int 
+#define F_PROG        "prog"        //string == Bluename, falls kein replace_program()
+#define F_OBJ         "obj"         //string
+#define F_LOADNAME    "loadname"    //string == Bluename, falls kein rename_object()
+#define F_LINE        "loc"         //int
+#define F_MSG         "message"     //string
+#define F_HB_OBJ      "hbobj"       //string
+#define F_CAUGHT      "caught"      //int
+#define F_TITP        "titp"        //string
+#define F_STACK       "stack"       //Array von mixed (mixed)
+#define F_CLI         "command"     //string (Spielereingabe)
+#define F_VERB        "verb"        //string (Kommandoverb)
+#define F_COUNT       "count"       //int
+#define F_TIENV       "tienv"       //string, object_name() vom Env von TI/TP
+#define F_LOCK        "locked"      //mixed (Array von 2 Elementen)
+#define F_RESOLVER    "resolver"    //string (wer hat gefixt?)
+#define F_NOTES       "notes"       //Bemerkungen, Array von 3-elementigen Arrays
+
+// Prop, welche in /players/ das Loggen von Fehlern im errord unterbindet.
+#define P_DONT_LOG_ERRORS    "p_lib_errord_dont_log"
+
+#ifdef __NEED_IMPLEMENTATION__
+
+#ifdef DEBUG
+#undef DEBUG
+#endif
+//#define DEBUG(x)
+#define DEBUG(x)  if (funcall(symbol_function('find_player),"zesstra"))\
+        tell_object(funcall(symbol_function('find_player),"zesstra"),\
+        "EDBG: "+x+"\n")
+
+//Wer pflegt das Ding hier gerade?
+#define MAINTAINER ({"zesstra"})
+
+//Stanard-Expire
+#define STDEXPIRE 2678400 //31 Tage
+
+//Blacklist fuer Files, die nicht erfasst werden sollen (nur
+//Uebersetzungsprobleme momentan)
+#define BLACKLIST ({".tool.lpc",".xtool.h"})
+
+//Zugriffsarten
+#define M_READ   1
+#define M_WRITE  2  // actually: append-only
+#define M_FIX    4
+#define M_REASSIGN 8
+#define M_DELETE 16
+
+// Changelog
+#define CHANGELOG "/log/CHANGELOG"
+
+// Log
+//#define MAILLOG       "sent.log"
+
+// Standardmailtext: TODO 
+//#define STANDARDTEXT  HOME("mailtext.txt")
+
+#define STANDARDMAILTEXT "Huhu lieber Mitmagier,\n\n" \
+    "der unten angegebene Fehler in einem Objekt, fuer welches Du " \
+    "(als programmierender Magier oder RM) zustaendig bist, wurde soeben " \
+    "von %s als gefixt markiert. Bitte beachte ggf. die unten angebenen " \
+    "Bemerkungen zum Fix.\n\n--- Fehler-Daten ---\n"
+
+#define STANDARDMAILTEXT_ERRORHINT "Huhu %s,\n\n" \
+    "ein von Dir abgesetzter Fehler (s.u.) wurde von %s bearbeitet und als " \
+    "erledigt markiert. Bei Fragen wende Dich Dich bitte an den " \
+    "bearbeitenden Magier.\n" \
+    "Vielen Dank fuer Deine Mithilfe!\n\n" \
+    "--- Fehler-Daten ---\n"
+
+
+#endif // __NEED_IMPLEMENTATION__
+
+#endif // __ERRORD_H__
+
diff --git a/secure/exploration.h b/secure/exploration.h
new file mode 100644
index 0000000..d7afb40
--- /dev/null
+++ b/secure/exploration.h
@@ -0,0 +1,75 @@
+// MorgenGrauen MUDlib
+//
+// exploration.h -- Definitionen fuer den explorationmaster
+//
+// $Id: exploration.h 8835 2014-06-09 20:24:00Z Zesstra $
+
+#ifndef __EXPLORATION_H__
+#define __EXPLORATION_H__
+
+/* Dateinamen */
+#define EPMASTER    "/secure/explorationmaster"
+#define EPSAVEFILE  "/secure/ARCH/exploration"
+#define DUMPFILE    "/secure/ARCH/EPROOMS.dump"
+#define MAKE_EPSTAT "/secure/ARCH/epstat"
+#define EPSTAT_INFO "/secure/ARCH/epstat.o"
+
+/* Ziele von ChangeEPObject() */
+#define CHANGE_OB   0 // das Objekt selbst
+#define CHANGE_KEY  1 // Schluessel
+#define CHANGE_TYPE 2 // der Typ
+#define CHANGE_BONUS 3 // ist es ein Bonus-EP?
+
+/* Positionen im Mapping */
+#define MPOS_KEY  0   // Die Schluessel
+#define MPOS_NUM  1   // Nummer des EP
+#define MPOS_TYPE 2   // Typ (s.u.)
+
+/* Typen von EP-Spendern */
+#define EP_DETAIL 0   // Details, SpecialDetails
+#define EP_EXIT   1   // Exits und SpecialExits
+#define EP_CMD	  2   // AddCmd()-Kommandos
+#define EP_INFO   3   // Infos von NPCs
+#define EP_MISC   4   // eigene GiveExplorationPoint()-Aufrufe
+#define EP_RDET   5   // ReadDetails
+#define EP_PUB    6   // Speisen und Getraenke in Kneipen
+#define EP_SMELL  7   // Gerueche
+#define EP_SOUND  8   // Geraeusche
+#define EP_TOUCH  9   // ertastbare Details
+#define EP_MAX    9   // max. Anzahl von Typen
+
+#define EP_TYPES ({ "det ", "exit", "cmd ", "info", "misc",\
+                    "rdet", "pub ", "sme ", "sond", "tastdet" })
+
+/* Fehler bei Funktionsaufrufen */
+#define EPERR_NOT_ARCH	  -1  // this_interactive() war kein Erzmagier
+#define EPERR_INVALID_OB  -2  // ungueltiges Objekt (zB. VC-Raum)
+#define EPERR_NO_ENTRY	  -3  // Eintrag nicht gefunden
+#define EPERR_INVALID_ARG -4  // Ungueltiges Argument
+
+/* und hier was fuer die Statistik... */
+#define MIN_EP  10
+
+/* FP-Scripte... */
+#define LF_LOG "ARCH/FPS_TOO_FAST"
+#define LF_TIME 900
+#define LF_WARN 3
+
+/* FP-Logfile */
+#define FP_LOG "ARCH/FPS_FOUND"
+
+#endif
+
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __EXPLORATION_H_PROTO__
+#define __EXPLORATION_H_PROTO__
+
+/* prototypes */
+static void GiveEP(int type, string key); // in /std/thing/description.c
+
+#endif // __EXPLORATION_H_PROTO__
+
+#endif	// NEED_PROTOYPES
+
diff --git a/secure/explorationmaster.c b/secure/explorationmaster.c
new file mode 100644
index 0000000..ad609d4
--- /dev/null
+++ b/secure/explorationmaster.c
@@ -0,0 +1,1024 @@
+// MorgenGrauen MUDlib
+//
+// explorationmaster.c -- Verwaltung der ExplorationPoints (FP)
+//
+// $Id: explorationmaster.c 9569 2016-06-05 21:28:23Z Zesstra $
+//
+#pragma strict_types,rtt_checks
+#pragma no_clone,no_shadow,no_inherit
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#define BY_EP "/secure/ARCH/EPSTAT_BY_EP"
+#define BY_PL "/secure/ARCH/EPSTAT_BY_PL"
+#define BY_PN "/secure/ARCH/EPSTAT_BY_PN"
+#define AVGLOG "/secure/ARCH/EPSTAT_AVG"
+
+inherit "/std/util/pl_iterator";
+
+#include <config.h>
+#include <wizlevels.h>
+#include <userinfo.h>
+#include <exploration.h>
+#include <properties.h>
+#include <new_skills.h>
+
+#define DOMAIN_INFO 1
+
+#include <living/comm.h>
+#define ZDEBUG(x) if (find_player("zesstra")) \
+  find_player("zesstra")->ReceiveMsg(x,MT_DEBUG,0,object_name()+":",this_object())
+//#define ZDEBUG(x)
+
+// Struktur: ([ string filename : string* action, int number, int type ]) 
+private mapping obs;
+private int epcount;  // Anzahl FP
+private int epavg;    // Durchschnittliche FP/Spieler
+// Bitstrings fuer normale (alloc) und Bonus-FP (bonus)
+private string alloc,bonus;
+
+private nosave string vc_ob;
+private nosave string output;
+
+private nosave int changed;
+private nosave int dumping;
+private nosave mapping dumpMap, lastfound, querytime;
+
+private nosave int stat_done;
+
+protected void create()
+{
+  seteuid(getuid(this_object()));
+  if (!restore_object(EPSAVEFILE)) {
+    obs = ([]);
+    epcount = epavg = 0;
+    alloc = " ";
+    bonus = " ";
+    save_object(EPSAVEFILE);
+  }
+  if (!bonus) bonus=" ";
+  lastfound=([]);
+  querytime=([]);
+}
+
+public varargs int remove(int silent)
+{
+  save_object(EPSAVEFILE);
+  destruct(this_object());
+  return 1;
+}
+
+// Statistik erstellen
+private int SetAverage(int newavg)
+{
+  if (epavg != newavg)
+  {
+    epavg = newavg;
+    write_file(AVGLOG, sprintf("%s : %d\n",dtime(time()),newavg));
+    save_object(EPSAVEFILE);
+  }
+  return epavg;
+}
+
+private void check_player(string pl, mixed extra)
+{
+  int *fp_found = extra[0];
+  mapping plstat = extra[1];
+  mapping pnstat = extra[2];
+  object pldata = extra[3];
+  if (!pldata)
+  {
+    pldata=clone_object("/obj/playerdata");
+    extra[3]=pldata;
+  }
+  // Letzte Loginzeit ermitteln, wenn laenger als 90 Tage her und nicht
+  // eingeloggt, wird der Spieler uebersprungen.
+  // Der playerdata braucht eine UID, mit der er das Spielersavefile laden
+  // kann. Hierzu setzen wir ihm die des zu ladenden Spielers...
+  pldata->ReleasePlayer();
+  seteuid(pl);
+  efun::export_uid(pldata);
+  seteuid(getuid(this_object()));
+  (int)pldata->LoadPlayer(pl);
+  // Testspieler ausnehmen, Spieler, die 90 Tage nicht da waren.
+  if ( ((int)pldata->QueryProp(P_LAST_LOGIN) < time() - 7776000
+        && !find_player(pl))
+      || (mixed)pldata->QueryProp(P_TESTPLAYER))
+    return;
+  // Wenn kein SPieler/Seher, sondern Magier: auch ueberspringen
+  mixed* uinfo = (mixed*)master()->get_userinfo(pl);
+  if (uinfo[USER_LEVEL+1] >= LEARNER_LVL)
+    return;
+
+  string eps=(string)master()->query_ep(pl) || "";
+  int p=-1;
+  int count, avgcount;
+  while ((p=next_bit(eps,p)) != -1)
+  {
+    if (p<sizeof(fp_found))
+    {
+      ++count;
+      ++fp_found[p];
+      // es tragen nur normale EPs zum Durchschnitt bei
+      if (!test_bit(bonus,p))
+        ++avgcount;
+    }
+  }
+
+  // Spieler mit weniger als MIN_EP ignorieren.
+  if (avgcount >= MIN_EP)
+  {
+    extra[4] += avgcount;   // Summe der gefundenen FP
+    ++extra[5];          // Anzahl beruecksichtigter Spieler
+  }
+  plstat += ([ pl : count ]);
+  if (!member(pnstat, count))
+    pnstat += ([ count : ({ pl }) ]);
+  else
+    pnstat[count] = ({ pl })+pnstat[count];
+}
+
+// Mit allen Spielern fertig, aufraeumen.
+#define BAR "************************************************************"
+private void plcheck_finished(mixed extra)
+{
+  ZDEBUG("plcheck_finished entry\n");
+  if (get_eval_cost() < 1000000)
+  {
+    call_out(#'plcheck_finished, 4+random(6), extra);
+    return;
+  }
+
+  int *fp_found = extra[0];
+  mapping plstat = extra[1];
+  mapping pnstat = extra[2];
+  object pldata = extra[3];
+  int avgcount = extra[4];
+  int avgspieler = extra[5];
+
+  if (objectp(pldata))
+    pldata->remove(1);
+
+  if (file_size(BY_EP) >= 0)
+    rm(BY_EP);
+  if (file_size(BY_PL) >= 0)
+    rm(BY_PL);
+  if (file_size(BY_PN) >= 0)
+    rm(BY_PN);
+
+  // Neuen Durchschnittswert fuer gefundene FP setzen.
+  if (avgspieler)
+  {
+    SetAverage(to_int(avgcount/avgspieler));
+  }
+
+  // Histogramm ueber alle FP schreiben: wie oft wurde jeder gefunden?
+  int maxval = max(fp_found);
+  int fp_index;
+  foreach(int found : fp_found)
+  {
+    write_file(BY_EP, sprintf("%5d:%5d %s\n", fp_index, found,
+               BAR[0..(60*found)/maxval]));
+    ++fp_index;
+  }
+  // sortierte Liste der Spieler (sortiert nach gefundenen FP) erzeugen
+  foreach(int fp : sort_array(m_indices(pnstat),#'<) )
+  {
+    foreach(string pl : pnstat[fp])
+      write_file(BY_PN, sprintf("%-14s: %5d\n", pl, fp));
+  }
+  // alphabetisch sortierte Liste der Spieler erzeugen
+  foreach(string pl : sort_array(m_indices(plstat),#'>) )
+  {
+    write_file(BY_PL, sprintf("%-14s: %5d\n", pl, plstat[pl]));
+  }
+}
+#undef BAR
+
+private void make_stat()
+{
+  stat_done = time();
+  // Leider laesst sich epcount nicht nutzen Beim Loeschen von FP wird
+  // <epcount> dekrementiert, so dass hier nicht mehr alle FPs ausgewertet
+  // werden. Daher muss stattdessen bis zur Gesamtgroesse von <obs> gezaehlt
+  // werden.
+  int* fp_found = allocate(sizeof(obs));
+  mapping plstat = m_allocate(500);
+  mapping pnstat = m_allocate(500);
+  start_player_check(#'check_player, #'plcheck_finished, 1200000,
+                     ({fp_found,plstat,pnstat,
+                       0, 0, 0}) );
+}
+
+void reset()
+{
+  if (changed && !dumping)
+  {
+    catch(rm(DUMPFILE);publish);
+    dumping = 1;
+    call_out("dumpEPObjects", 0, sort_array(m_indices(obs),#'> /*'*/));
+    changed = 0;
+  }
+  // nur einmal am tag statistiken erstellen
+  if (time()%86400 < 4000
+      && stat_done < time()-80000)
+    make_stat();
+}
+
+private string strArr(string *s)
+{
+  string ret;
+  int i;
+
+  ret = ("\""+s[<1]+"\"");
+  for (i=sizeof(s)-2; i>=0; i--)
+    ret += (", \""+s[i]+"\"");
+
+  return ret;
+}
+
+static void dumpEPObjects(string *doit)
+{
+  string *toGo, id;
+  int i,j;
+
+  if (!mappingp(dumpMap))
+    dumpMap = ([]);
+
+  toGo = 0;
+  if (sizeof(doit) > 200) {
+    toGo = doit[200..];
+    doit = doit[0..199];
+  }
+
+  j = sizeof(doit);
+
+  for (i=0; i<j; i++) {
+    id = (string)master()->creator_file(doit[i]);
+    if (member(dumpMap, id))
+      dumpMap[id] += ({ doit[i] });
+    else
+      dumpMap += ([ id : ({ doit[i] }) ]);
+  }
+  if (toGo)
+    call_out("dumpEPObjects", 1, toGo);
+  else {
+    int step;
+
+    step = 0;
+    id = "";
+    toGo = sort_array(m_indices(dumpMap),#'> /*'*/ );
+    for (i=0, j=sizeof(toGo); i<j; i++) {
+      int k,l;
+      doit = dumpMap[toGo[i]];
+      id += sprintf("### %s %s\n", toGo[i], "#########################"[sizeof(toGo[i])..]);
+      for (k=0, l=sizeof(doit); k<l; k++) {
+        id += sprintf("%s %4d %s %s.c ({ %s })\n",
+                      EP_TYPES[obs[doit[k], MPOS_TYPE]],
+                      obs[doit[k], MPOS_NUM],
+                      test_bit(bonus,obs[doit[k], MPOS_NUM])?"b":"n",
+                      doit[k],
+                      strArr(obs[doit[k]]));
+        if (!(++step % 50)) {
+          write_file(DUMPFILE, id);
+          id = "";
+        }
+      }
+      id += "\n";
+    }
+    write_file(DUMPFILE,id);
+    if (dumping == 2)
+      write("Fertig! Anfrage bitte wiederholen.\n");
+    dumping = 0;
+    changed = 0;
+    dumpMap = 0;
+  }
+}
+
+private string validOb(mixed ob)
+{
+  string fn, fpart;
+
+  if (!objectp(ob))
+    return 0;
+
+  fn = old_explode(object_name(ob),"#")[0];
+  fpart = old_explode(fn,"/")[<1];
+ /*
+  if (query_once_interactive(ob))
+    return 0;
+
+  if ((file_size(fn+".c") <= 0) &&
+      this_player() &&
+      (strstr(fpart, getuid(this_player())) >= 0))
+    return 0;
+ */
+  return fn;
+}
+
+private int allowed()
+{
+  if (previous_object() && geteuid(previous_object())==ROOTID)
+    return 1;
+  if ( !process_call() && previous_object() && this_interactive()
+        && ARCH_SECURITY )
+    return 1;
+
+  return 0;
+}
+
+nomask varargs int AddEPObject(object ob, mixed keys, int type, int bonusflag)
+{
+  string fn;
+
+  if (!allowed())
+    return EPERR_NOT_ARCH;
+
+  if (!(fn = validOb(ob)))
+    return EPERR_INVALID_OB;
+
+  if (stringp(keys))
+    keys = ({ keys });
+
+  if (member(obs, fn)) {
+    if (type == obs[fn, MPOS_TYPE])
+      obs[fn] = keys;
+    else
+      obs += ([ fn : keys; obs[fn, MPOS_NUM]; type ]);
+    if (bonusflag) bonus = set_bit(bonus, obs[fn, MPOS_NUM]);
+       else bonus = clear_bit(bonus, obs[fn, MPOS_NUM]);
+  }
+  else {
+    int nr, i;
+
+    nr = 0;
+    while (test_bit(alloc,nr))
+      nr++;
+
+    obs += ([ fn : keys; nr; type ]);
+    ++epcount;
+    alloc = set_bit(alloc,nr);
+    if (bonusflag) bonus = set_bit(bonus,nr);
+       else bonus = clear_bit(bonus,nr);
+  }
+
+  changed = 1;
+  save_object(EPSAVEFILE);
+  return epcount;
+}
+
+nomask int RemoveEPObject(string|object ob)
+{
+  string fn;
+
+  if (!allowed())
+    return EPERR_NOT_ARCH;
+  
+  if (objectp(ob))
+    ob=object_name(ob);
+
+  fn = explode(ob,"#")[0];
+  if (!member(obs,fn))
+    return EPERR_NO_ENTRY;
+
+  alloc = clear_bit(alloc, obs[fn, MPOS_NUM]);
+  bonus = clear_bit(bonus, obs[fn, MPOS_NUM]);
+
+  m_delete(obs, fn);
+
+  changed = 1;
+  --epcount;
+  save_object(EPSAVEFILE);
+  return epcount;
+}
+
+nomask int ChangeEPObject(object ob, int what, mixed new)
+{
+  string fn, fn2;
+  mapping entry;
+
+  if (!allowed())
+    return EPERR_NOT_ARCH;
+
+  if (!(fn = validOb(ob)))
+    return EPERR_INVALID_OB;
+
+  if (!member(obs,fn))
+    return EPERR_NO_ENTRY;
+
+  switch(what) {
+  case CHANGE_OB:
+    if (!(fn2 = validOb(new)))
+      return EPERR_INVALID_ARG;
+    entry = ([ fn2: obs[fn]; obs[fn,MPOS_NUM]; obs[fn,MPOS_TYPE] ]);
+    obs = m_copy_delete(obs, fn);
+    obs += entry;
+    break;
+  case CHANGE_BONUS:
+    if (!(intp(new)))
+      return EPERR_INVALID_ARG;
+    if (new) bonus=set_bit(bonus,obs[fn,MPOS_NUM]);
+       else bonus=clear_bit(bonus,obs[fn,MPOS_NUM]);
+    break;
+  case CHANGE_KEY:
+    if (!stringp(new) && !pointerp(new))
+      return EPERR_INVALID_ARG;
+       
+    if (stringp(new))
+      new = ({ new });
+
+    obs[fn] = new;
+    break;
+  case CHANGE_TYPE:
+    if (!intp(new) || new < 0 || new > EP_MAX)
+      return EPERR_INVALID_ARG;
+
+    obs[fn, MPOS_TYPE] = new;
+    break;
+  default:
+    return EPERR_INVALID_ARG;
+  }
+  changed = 1;
+  save_object(EPSAVEFILE);
+  return 1;
+}
+
+nomask mixed QueryEPObject(object ob)
+{
+  string fn;
+
+  if (!allowed())
+    return EPERR_NOT_ARCH;
+
+  if (!(fn = validOb(ob)))
+    return EPERR_INVALID_OB;
+
+  if (!member(obs, fn))
+    return 0;
+
+  return ({ obs[fn], obs[fn,MPOS_NUM], obs[fn, MPOS_TYPE], test_bit(bonus,obs[fn, MPOS_NUM]) });
+}
+
+private string getMatch(string m)
+{
+  string *res;
+  int i;
+
+  res = regexp(sort_array(m_indices(obs), #'> /*'*/), m);
+  for (i=sizeof(res)-1; i>=0; i--)
+    res[i] = sprintf("%s %s %s.c ({ %s })",
+                     EP_TYPES[obs[res[i], MPOS_TYPE]],
+                     test_bit(bonus,obs[res[i], MPOS_NUM])?"b":"n",
+                     res[i],
+                     strArr(obs[res[i]]));
+  return implode(res, "\n");
+}
+
+private string getMatchArch(string m)
+{
+  string *res;
+  int i;
+
+  res = regexp(sort_array(m_indices(obs), #'> /*'*/), m);
+  for (i=sizeof(res)-1; i>=0; i--)
+    res[i] = sprintf("%s %4d %s %s.c ({ %s })",
+                     EP_TYPES[obs[res[i], MPOS_TYPE]],
+                     obs[res[i], MPOS_NUM],
+                     test_bit(bonus,obs[res[i], MPOS_NUM])?"b":"n",
+                     res[i],
+                     strArr(obs[res[i]]));
+  return implode(res, "\n");
+}
+
+/*
+ *  Anleitung fuer >=EMs:
+ *    ShowEPObjects() zeigt das gesamte Dumpfile an
+ *    ShowEPObjects("dump") erzeugt ein neues Dumpfile
+ *    ShowEPObjects("string") liefert alle EPs, die "/string/"
+ *                            im Filenamen enthalten
+ *    ShowEPObjects("string1","string2") liefert alle EPs, die
+ *                            "/string1/string2/" im Filenamen enthalten
+ *  Anleitung fuer RMs:
+ *    ShowEPObjects() liefert all Deine EPs
+ *    ShowEPObjects("domainname") liefert Deine EPs in Deiner Domanin
+ *    ShowEPObjects("domainname","magiername") liefert die EPs des
+ *                                Mitarbeiters in Deiner Region
+ *
+ *  Anleitung fuer <RMs:
+ *    ShowEPObjects() liefert Deine EPs
+ *
+ *  Alle EPObjects einer ganzen Domain kann man nicht mehr auf einmal
+ *  ziehen und sollte man auch nicht. Alle Zugriffe auf diese Funktion
+ *  werden geloggt. Auch die der EMs!
+ *
+ *                                             Rikus
+ */
+
+nomask varargs void ShowEPObjects(string what, string magname)
+{
+  if (allowed()) {
+    if (what == "dump") {
+      if (!dumping) {
+        dumping = 2;
+        catch(rm(DUMPFILE);publish);
+        call_out("dumpEPObjects", 0, sort_array(m_indices(obs),#'>/*'*/));
+      }
+      printf("Liste wird erstellt und in '%s' abgelegt!\n", DUMPFILE);
+      return;
+    }
+    if (!what || what == "") {
+      this_interactive()->More(DUMPFILE, 1);
+      log_file("ARCH/EPZugriffe", ctime(time())+": "+
+        capitalize(getuid(this_interactive()))+" schaute sich das DUMPFILE an.\n");
+      return;
+    }
+    what="/"+what+"/";
+    if (magname) what+=magname+"/";
+  }
+  else
+#ifdef DOMAIN_INFO
+    if (IS_LORD(this_interactive())) {
+      if (!what || what == "")
+       what = "/"+getuid(this_interactive())+"/";
+      else {
+       if (!master()->domain_master(getuid(this_interactive()), what)) {
+         write("Sorry, Du kannst nur Objekte in Deiner eigenen Region abfragen!\n");
+         return;
+       }
+       if (!magname || magname=="")
+         magname = getuid(this_interactive());
+//        if (!master()->domain_member(magname, what)) {
+//         write(capitalize(magname)+" ist gar kein Mitarbeiter in Deiner Region!\n");
+//          return;
+//       }
+       what = "/d/"+what+"/"+magname+"/";
+      }
+    }
+    else
+#endif
+      {
+       if (!what || what == "")
+         what = getuid(this_interactive());
+       else if (what != getuid(this_interactive())) {
+         write("Sorry, Du kannst nur Deine eigenen Objekte abfragen!\n");
+         return;
+       }
+       what="/"+what+"/";
+      }
+  if (allowed())
+    this_interactive()->More(getMatchArch(what));
+  else
+    this_interactive()->More(getMatch(what));
+  log_file("ARCH/EPZugriffe", ctime(time())+": "+
+    capitalize(getuid(this_interactive()))+" schaute sich "+what+" an.\n");
+  return;
+}
+
+nomask void PrepareVCQuery(string file)
+{
+  string path, *parts;
+
+  vc_ob = 0;
+
+  if (!previous_object() || !stringp(file))
+    return;
+
+  parts = explode(object_name(previous_object()),"/");
+
+  if (parts[<1] == "virtual_compiler") {
+    path = implode(parts[0..<2]+({ file }), "/");
+    if (file_size(path+".c") < 0)
+      vc_ob = path;
+  }
+}
+
+nomask mixed *QueryExplore()
+{
+  string fn;
+
+  if (!previous_object())
+    return 0;
+
+  if (!member(obs, fn = old_explode(object_name(previous_object()),"#")[0]))
+    if (!vc_ob || !member(obs, fn = vc_ob)) {
+      vc_ob = 0;
+      return 0;
+    }
+
+  vc_ob = 0;
+  return ({ obs[fn, MPOS_TYPE], obs[fn] });
+}
+
+nomask int QueryMaxEP()
+{
+  return (epcount||1);
+}
+
+nomask int QueryAverage()
+{
+  return (epavg||1);
+}
+
+static int check_arch(object u)
+{
+   return query_wiz_level(u)>=ARCH_LVL;
+}
+
+private int check_to_fast(string name, string fn, int gesetzt)
+{
+    if (gesetzt) return 1; // Rikus, sonst arger scroll :)
+
+    log_file(FP_LOG,sprintf("%s : %s : %s\n",name,fn,dtime(time())),500000);
+
+    if ( !member( lastfound, name ) )
+        lastfound += ([ name: time(); 1; ({ fn + "#*#" + dtime(time()) + "#*#" +gesetzt }) ]);
+    else if ( time() <= lastfound[name, 0] + LF_TIME ){
+        lastfound[name, 1]++;
+        lastfound[name, 2] = ({ fn + "#*#" + dtime(time()) + "#*#" +gesetzt })
+            + lastfound[name,2];
+    }
+    else {
+        lastfound[name, 1] = 1;
+        lastfound[name, 2] = ({ fn + "#*#" + dtime(time()) + "#*#" +gesetzt });
+    }
+
+    lastfound[name, 0] = time();
+
+    if ( lastfound[name, 1] >= LF_WARN ){
+        object *u;
+        int i;
+        string *tmp;
+
+//        u = filter( users(), "check_arch" );
+//        map( u, #'tell_object/*'*/, "**** FP-Liste/Script " +
+//                   capitalize(name) + " (" + dtime(time()) + ") ****\n" );
+
+        for ( i = sizeof(lastfound[name, 2]); i--; ){
+            tmp = explode( lastfound[name, 2][i], "#*#" );
+            log_file( LF_LOG, sprintf( "%s : %s : %s : %s\n",
+                                         tmp[1], name, tmp[2], tmp[0] ), 500000 );
+        }
+
+        lastfound[name, 2] = ({});
+    }
+    return 1;
+}
+
+nomask int GiveExplorationPoint(string key)
+{
+  string fn;
+  string ep;
+  int gesetzt;
+
+  if (!previous_object() || !this_interactive() || !this_player() ||
+       this_player() != this_interactive() ||
+       this_player()->QueryProp(P_KILLS) ||
+       this_player()->QueryGuest()       )
+    return 0;
+
+  fn = old_explode(object_name(previous_object()), "#")[0];
+
+  if (!member(obs, fn))
+    return 0;
+
+  if (member(obs[fn],key) < 0)
+    return 0;
+
+  ep = (MASTER->query_ep(getuid(this_interactive())) || "");
+
+  gesetzt=test_bit(ep,obs[fn,1]);
+  check_to_fast(getuid(this_player()),fn,gesetzt);
+  if (gesetzt) return 0;
+  
+  catch(ep = set_bit(ep, obs[fn,1]));
+
+  return (int)MASTER->update_ep(getuid(this_interactive()),ep);
+}
+
+nomask int GiveExplorationPointObject(string key, object ob)
+{
+  string fn;
+  string ep;
+  int gesetzt;
+  
+  if (!objectp(ob) || ob->QueryProp(P_KILLS ))
+    return 0;
+
+  fn = old_explode(object_name(previous_object()), "#")[0];
+
+  if (!member(obs, fn))
+    return 0;
+
+  if (member(obs[fn],key) < 0)
+    return 0;
+
+  ep = (MASTER->query_ep(getuid(ob)) || "");
+
+  gesetzt=test_bit(ep,obs[fn,1]);
+  check_to_fast(getuid(this_player()),fn,gesetzt);
+  if (gesetzt) return 0;
+
+  catch(ep = set_bit(ep, obs[fn,1]));
+
+  return (int)MASTER->update_ep(getuid(ob),ep);
+}
+
+
+private int QueryRealExplorationPoints(string pl)
+{
+  return count_bits(MASTER->query_ep(pl) || " ");
+}
+
+nomask int QueryExplorationPoints(mixed pl)
+{
+  mixed val;
+  int ep;
+
+  if (!stringp(pl)) pl=getuid(pl);
+  ep=QueryRealExplorationPoints(pl);
+
+  if (allowed() || !ep) return ep;
+
+  val=querytime[pl];
+
+  if (!pointerp(val) || sizeof(val)<2)
+    val=({0,time()});
+
+  if (time()>=val[1]) {
+    val = ({ ep + random(6)-3, time()+300+random(300) });
+    if (val[0]<0) val[0]=0;
+    querytime+=([pl:val]);
+  }
+  return val[0];
+}
+
+private int remove_fp(int num, string pl)
+{
+  int i,j,k,t,maxEP;
+  string ep;
+  ep = (MASTER->query_ep(pl) || "");
+
+  maxEP = QueryMaxEP();
+  for( i=0; i<num; i++)
+  {
+    t = random(maxEP);
+    for( j=0; j<maxEP; j++ ) {
+      if( test_bit(ep, k=(t+j)%maxEP ) ) break;
+    }
+    if( j==maxEP ) break;
+    ep = clear_bit(ep, k);
+  }
+  MASTER->update_ep(pl,ep);
+  return i;
+}
+
+/* */
+
+// quoted from /sys/mail.h
+#define MSG_FROM 0
+#define MSG_SENDER 1
+#define MSG_RECIPIENT 2
+#define MSG_CC 3
+#define MSG_BCC 4
+#define MSG_SUBJECT 5
+#define MSG_DATE 6
+#define MSG_ID 7
+#define MSG_BODY 8
+
+nomask int RemoveFP(int num, string pl, string grund)
+{
+  int i;
+  string text;
+  mixed* mail;
+
+  if (!allowed()) return -1;
+  if( num<0 ) return -3;
+  if (!grund || grund=="") grund="<unbekannt>";
+  if (!pl) return -2;
+  i=remove_fp(num, pl);
+  log_file("ARCH/fp_strafen", ctime(time())+": "
+    +this_interactive()->Name(WER)+" loescht "+pl+" "+i
+    +" FPs\nGrund:"+grund+"\n");
+  if( i>0 ) {
+     text =
+     "Hallo "+capitalize(pl)+",\n\n"+
+     break_string(
+      this_interactive()->Name(WER)+" hat soeben veranlasst, dass Dir "+i
+      +" FPs abgezogen wurden.\nGrund:"+grund+"\n", 78 );
+
+     mail = allocate(9);
+     mail[MSG_FROM] = getuid(this_interactive());
+     mail[MSG_SENDER] = MUDNAME;
+     mail[MSG_RECIPIENT] = pl;
+     mail[MSG_CC]=0;
+     mail[MSG_BCC]=0;
+     mail[MSG_SUBJECT]="FP-Reduktion";
+     mail[MSG_DATE]=dtime(time());
+     mail[MSG_ID]=MUDNAME":"+time();
+     mail[MSG_BODY]=text;
+
+     "/secure/mailer"->DeliverMail(mail,1);
+  }
+  return i;
+}
+/* */
+
+private int add_fp(int num, string pl)
+{
+  int i,j,k,t,maxEP;
+  string ep;
+  ep = (MASTER->query_ep(pl) || "");
+
+  maxEP = QueryMaxEP();
+  for( i=0; i<num; i++)
+  {
+    t = random(maxEP);
+    for( j=0; j<maxEP; j++ ) {
+      if( !test_bit(ep, k=(t+j)%maxEP ) ) break;
+    }
+    if( j==maxEP ) break;
+    ep = set_bit(ep, k);
+  }
+  MASTER->update_ep(pl,ep);
+  return i;
+}
+
+nomask int AddFP(int num, string pl)
+{
+  int i;
+  if (!allowed()) return -1;
+  if ( num<0 ) return -3;
+  if (!pl) return -2;
+  i=add_fp(num, pl);
+  log_file("ARCH/fp_strafen", ctime(time())+": "
+    +this_interactive()->Name(WER)+" gibt "+pl+" "+i
+    +" FPs\n");
+
+  return i;
+}
+
+nomask int SetFP(int num, string pl)
+{
+  int maxEP;
+  string ep;
+  if (!allowed()) return -1;
+  if ( num<0 ) return -3;
+  if (!pl) return -2;
+  ep = (MASTER->query_ep(pl) || "");
+
+  maxEP = QueryMaxEP();
+  if (num<0 || num>=maxEP) return -4;
+  ep = set_bit(ep, num);
+  MASTER->update_ep(pl,ep);
+  return num;
+}
+
+nomask int ClearFP(int num, string pl)
+{
+  int maxEP;
+  string ep;
+  if (!allowed()) return -1;
+  if ( num<0 ) return -3;
+  if (!pl) return -2;
+  ep = (MASTER->query_ep(pl) || "");
+
+  maxEP = QueryMaxEP();
+  if (num<0 || num>=maxEP) return -4;
+  ep = clear_bit(ep, num);
+  MASTER->update_ep(pl,ep);
+  return num;
+}
+
+private void printep( int nr, string key, int kind, string* det )
+{
+  output+=sprintf("%4d %s %s.c %s ({ %s })\n",nr,test_bit(bonus,nr)?"b":"n",key,EP_TYPES[kind],
+                strArr(det));
+}
+
+nomask varargs int ShowPlayerEPs(string pl,string pattern)
+{
+  string ep,teststring;
+  if (!allowed()) return -1;
+  ep = (MASTER->query_ep(pl) || "");
+
+  output="";
+  if (!pattern || pattern=="")
+    teststring="%s";
+  else teststring="%s"+pattern+"%s";
+  walk_mapping( obs, lambda( ({ 'key, 'v1, 'v2, 'v3 }),
+                          // v1 -- details, v2 -- Nummer, v3 -- art
+                          // key -- Filename
+                          ({ #'if, ({ #'test_bit, ep, 'v2 }),
+                          ({#'if,({#'sscanf,'key,teststring,'v4,'v5}),
+                          ({ #'printep, 'v2, 'key, 'v3, 'v1 })
+                          }) }) ));
+  this_interactive()->More(output);
+  return 1;
+}
+
+// Hier kommen Funktionen fuer die Levelpunkte
+// Funktion fuer Levelpunkte steht im LEPMASTER!
+
+nomask int QueryLEP(int lep) {
+    raise_error("Bitte QueryLEP() im LEPMASTER abfragen, nicht im "
+       "EPMASTER!");
+    return(-1); //never reached
+}
+
+string QueryForschung()
+{
+  int my;
+  string ret;
+
+  if ((my=QueryRealExplorationPoints(getuid(previous_object()))) < MIN_EP)
+    return "Du kennst Dich im "MUDNAME" so gut wie gar nicht aus.\n";
+
+  my *= 100;
+  int absolute = my/QueryMaxEP();
+  int relative = my/QueryAverage();
+
+  ret = "Verglichen mit Deinen Mitspielern, kennst Du Dich im "MUDNAME" ";
+  switch(relative) {
+  case 0..10:
+    ret += "kaum";
+    break;
+  case 11..40:
+    ret += "aeusserst schlecht";
+    break;
+  case 41..56:
+    ret += "sehr schlecht";
+    break;
+  case 57..72:
+    ret += "schlecht";
+    break;
+  case 73..93:
+    ret += "unterdurchschnittlich";
+    break;
+  case 94..109:
+    ret += "durchschnittlich gut";
+    break;
+  case 110..125:
+    ret += "besser als der Durchschnitt";
+    break;
+  case 126..145:
+    ret += "recht gut";
+    break;
+  case 146..170:
+    ret += "ziemlich gut";
+    break;
+  case 171..210:
+    ret += "gut";
+    break;
+  case 211..300:
+    ret += "sehr gut";
+    break;
+  case 301..400:
+    ret += "ausserordentlich gut";
+    break;
+  case 401..500:
+    ret += "unheimlich gut";
+    break;
+  default:
+    ret += "einfach hervorragend";
+    break;
+  }
+  ret += " aus.\nAbsolut gesehen ";
+
+  switch(absolute) {
+  case 0..5:
+    ret += "kennst Du nur wenig vom "MUDNAME".";
+    break;
+  case 6..10:
+    ret += "solltest Du Dich vielleicht noch genauer umsehen.";
+    break;
+  case 11..17:
+    ret += "bist Du durchaus schon herumgekommen.";
+    break;
+  case 18..25:
+    ret += "hast Du schon einiges gesehen.";
+    break;
+  case 26..35:
+    ret += "bist Du schon weit herumgekommen.";
+    break;
+  case 36..50:
+    ret += "koenntest Du eigentlich einen Reisefuehrer herausbringen.";
+    break;
+  case 51..75:
+    ret += "hast Du schon sehr viel gesehen.";
+    break;
+  default:
+    ret += "besitzt Du eine hervorragende Ortskenntnis.";
+  }
+  return break_string(ret, 78, 0, 1);
+}
+
+// Nicht jeder Magier muss den EPMASTER entsorgen koennen.
+string NotifyDestruct(object caller) {
+  if( (caller!=this_object() && !ARCH_SECURITY) || process_call() )
+  {
+    return "Du darfst den Exploration Master nicht zerstoeren!\n";
+  }
+  return 0;
+}
+
diff --git a/secure/ftpd.c b/secure/ftpd.c
new file mode 100644
index 0000000..749cf03
--- /dev/null
+++ b/secure/ftpd.c
@@ -0,0 +1,178 @@
+/**************************************************************************
+** ftpd.c
+** Rumata@mg
+** 13.6.1999
+**
+** Ermittlung der Zugriffsrechte aus den Userdaten.
+**
+** Dieses Objekt wird vom ftpimp aus aufgerufen, der wiederum vom master
+** aufgerufen wird, um die Zugriffsrechte zu pruefen.
+**
+** Fuktionsweise:
+** + Im Master wird vom UNIX-ftpd aus FtpAccess (/secure/master/network)
+**   aufgerufen.
+** + Dieses ruft dann, sobald ein Zugriff erkannt wird
+**   QueryRead, QueryWrite oder QueryDir auf.
+** + Diese fragen den Master, ob der Zugriff legal ist.
+** + QueryDir liefert auch gleich das Ergebnis mit.
+**
+** Um ftp-zugriffe überwachen zu können, kann der ftpd eine liste der
+** ueberwachten spieler liefern und deren Zugriffe auf dem FTP Kanal ausgeben.
+**
+** add( name, zeit ) - FTP Zugriffe des Spielers 'name' werden protokolliert.
+**                     Nach 'zeit' wird dieser Zugriff automatisch beendet.
+**                     Ist 'zeit' <= 0, wir auf ewig ueberwacht. 
+** sub( name )       - FTP Snoop fuer Spieler 'name' direkt aufheben.
+** list()            - Mapping der ueberwachten Spieler zurueckgeben.
+**
+** FILES:
+**   /secure/master/network.c
+**   /secure/ftpd.c
+**   /std/shells/filesys/ftpimp.c
+**   /secure/ARCH/ftpd.o
+**   /p/daemon/channeld.init
+**
+***************************************************************************/
+
+#include "/secure/wizlevels.h"
+#include "/sys/daemon.h"
+
+#define FTPDSAVE "/secure/ARCH/ftpd"
+#define FTPD_CH "FTP"
+#define BBMASTER "/secure/bbmaster"
+
+mapping monitored;
+// Zu jedem Spielernamen wird vermerkt, wie lange die Ueberwachung dauern soll.
+// Zahlen <= 0 bedeuten fuer immer.
+
+nomask void create() {
+  if( clonep(this_object()) ) {
+    destruct( this_object() );
+    return;
+  }
+  seteuid( getuid() );
+  if( !restore_object(FTPDSAVE) )
+    monitored = ([]);
+}
+
+nomask int player_exists( string user ) {
+  user = lower_case(user);
+  if( !stringp( user ) || sizeof( user ) < 1 ) return 0;
+  return file_size( "/save/"+user[0..0]+"/"+user+".o" ) > 0;
+}
+
+nomask varargs int add( string user, int timeout ) {
+  if( !ARCH_SECURITY ) return -1;
+  if( !player_exists(user) ) return -2;
+  monitored[user] = timeout;
+  save_object( FTPDSAVE );
+  return 0;
+}
+
+nomask int sub( string user ) {
+  if( !ARCH_SECURITY ) return -1;
+  if( !player_exists(user) ) return -2;
+  monitored -= ([ user ]);
+  save_object( FTPDSAVE );
+  return 0;
+}
+
+nomask mixed list() {
+  if( !ARCH_SECURITY ) return -1;
+  return deep_copy(monitored);
+}
+
+#define NEWIMP "yes"
+
+private object findFtpImpFor( string user ) {
+#ifdef NEWIMP
+  return "/secure/impfetch"->impFor( lower_case(user) );
+#else
+  object imp;
+  string fname;
+
+  user = lower_case( user );
+  fname = "/ftpimp:" + user;
+  imp = find_object( fname );
+  if( !objectp(imp) ) {
+    imp = clone_object( "secure/ftpimp" );
+    imp->SetUser(user);
+    rename_object( imp, fname );
+  }
+  return imp;
+#endif
+}
+
+private nomask void msg( string user, string m ) {
+  int timeout;
+  object r;
+
+  if( !stringp(user) ) return;
+  m += "\n";
+  if( (r=find_player("rumata")) && user=="atamur" ) {
+     timeout = CHMASTER->send(FTPD_CH,findFtpImpFor(user), m );
+     tell_object( r, sprintf("%O\n", findFtpImpFor(user) ) );
+  }
+  BBMASTER->ftpbb(user,m);
+  if( !member(monitored,user) ) return;
+  timeout = monitored[user];
+  if( timeout > 0 && timeout < time() ) {
+    sub( user );
+    return;
+  }
+  CHMASTER->send(FTPD_CH,findFtpImpFor(user), m );
+}
+
+nomask int secure() {
+  return previous_object()==find_object(MASTER);
+}
+
+nomask mixed QueryRead( string user, string file ) {
+  if( !secure() ) return -1;
+  if( (IS_WIZARD(user) || file[0..4]=="/open")
+      && file[0] == '/'
+      && MASTER->valid_read(file, user, "read_file", 0)
+      ) {
+    msg( user, "read " + file );
+    return "OK";
+  } else {
+    return "FAIL";
+  }
+}
+
+nomask mixed QueryWrite( string user, string file ) {
+  if( !secure() ) return -1;
+  if( file[0] == '/'
+      && MASTER->valid_write( file, user, "write_file", 0)
+      ) {
+    msg( user, "write " + file );
+    return "OK";
+  } else {
+    return "FAIL";
+  }
+}
+
+#define DBG(x) if(find_player("rumata")){tell_object(find_player("rumata"),"FTPD:"+x+"\n");}
+
+//nomask mixed QueryDir( string user, string file ) {
+//  string reply;
+//  object imp;
+//
+//  if( !secure() ) return -1;
+//  reply = QueryRead( user, file );
+//  if( reply=="FAIL" ) return -1;
+//
+//  imp = findFtpImpFor( user );
+//  if( !objectp(imp) ) return -1; // should never happen
+//
+//  return imp->GetDir( file );
+//}
+
+nomask mixed QueryDir( string user, string file ) {
+  if( !secure() ) return -1;
+  if( file[0] == '/'
+      && MASTER->valid_read( file+"/*", user, "get_dir", 0))
+      return "OK";
+  else 
+    return "FAIL";
+}
diff --git a/secure/ftpimp.c b/secure/ftpimp.c
new file mode 100644
index 0000000..daee34c
--- /dev/null
+++ b/secure/ftpimp.c
@@ -0,0 +1,75 @@
+// MorgenGrauen MUDlib
+//
+// master/ftpimp.c -- FTP Avatar
+//
+// $Id: ftpimp.c 6440 2007-08-18 23:00:49Z Zesstra $
+//
+// siehe /secure/ftpd
+
+#pragma strong_types
+
+#include "/secure/master.h"
+#define FTPD "/secure/ftpd"
+
+//private inherit "/std/shells/filesys/filesys";
+private inherit "/secure/misc/filesys/filesys";
+
+static string user; // Name des zugehoerigen Users
+
+nomask private int secure_impcall() {
+  return
+    getuid(previous_object()) == ROOTID &&
+    geteuid(previous_object()) == ROOTID;
+
+}
+
+// Nach dem Aufruf von SetUser wird der Filename geaednert, sodass
+// das normale clonep in get_dir nicht mehr geht. Hier ein ersatz.
+private nomask int my_is_clone() {
+  return member(object_name(this_object()),':')>=0;
+}
+
+nomask void create() {
+  if( !clonep(this_object()) ) {
+    set_next_reset( -1 ); // blueprint soll nicht sterben
+    return;
+  }
+  //if( secure_impcall() ) ::create();
+}
+
+nomask int SetUser( string name ) {
+  if( !secure_impcall() || !clonep(this_object()) ) return -2;
+  if( stringp(user) && user != "" ) {
+    //write( "User="+user+"\n" );
+    return -1; // schon ein name da!
+  }
+  user = lower_case(name);
+  seteuid( user );
+  set_next_reset( 1800 );
+}
+
+// Gibt einen negativen Wert zurück, wenn die Datei nicht gelesen werden
+// kann oder einen string, der die gesamten Daten des Verzeichnisses
+// enthält.
+nomask mixed GetDir(string dir)
+{
+  mixed ret; ret = "";
+  if( !secure_impcall() || !my_is_clone()) return -2;
+  if( !stringp(user) || user=="" ) return -1;
+  _ls_output( dir, &ret, user, 0x83 );
+  set_next_reset( 1800 );
+  return ret;
+}
+
+nomask void reset() {
+  destruct(this_object());
+}
+
+varargs mixed move() {
+  return -1;
+}
+
+// Fuer die Meldung auf FTP
+nomask string name() {
+  return capitalize(user);
+}
diff --git a/secure/gildenmaster.c b/secure/gildenmaster.c
new file mode 100644
index 0000000..56c5e3d
--- /dev/null
+++ b/secure/gildenmaster.c
@@ -0,0 +1,165 @@
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <new_skills.h>
+#include <events.h>
+
+string *valid_guilds = ({});
+
+void create() {
+  seteuid(getuid(this_object()));
+  restore_object(GUILD_SAVEFILE);
+  if (!pointerp(valid_guilds)) 
+    valid_guilds=({});
+}
+
+//noetig, da nun valid_guilds eine Variable ist
+public mixed QueryProp( string name )
+{
+  if (name==P_VALID_GUILDS) 
+      return copy(valid_guilds);
+  return 0;
+}
+
+nomask int beitreten() {
+  object pl,gilde;
+  string gname,ogname;
+
+  if (!(pl=this_player()) || !(gilde=previous_object()))
+      return 0;
+  // Gilden sind Blueprints.
+  gname=object_name(gilde);
+
+  if ((GUILD_DIR+ogname)==gname) {
+    write("Du bist schon in dieser Gilde.\n");
+    return -4;
+  }
+
+  if ((ogname=(string)pl->QueryProp(P_GUILD)) && 
+      (ogname!=(((string)pl->QueryProp(P_DEFAULT_GUILD)) || DEFAULT_GUILD))) {
+    write("Du bist noch in einer anderen Gilde.\n");
+    return -1;
+  }
+  if (gname[0..7]!=GUILD_DIR ||
+        member(valid_guilds,(gname=gname[8..]))<0) {
+      write("Diese Gilde ist leider nicht zugelassen.\n"+
+              "Bitte verstaendige einen Erzmagier.\n");
+      return -2;
+  }
+  pl->SetProp(P_GUILD,gname);
+  // Event Gildenwechsel triggern
+  EVENTD->TriggerEvent(EVT_GUILD_CHANGE, ([
+      E_OBJECT: pl, E_PLNAME: getuid(pl),
+      E_ENVIRONMENT: environment(pl),
+      E_GUILDNAME: gname,
+      E_LAST_GUILDNAME: ogname ]) );
+
+  return 1;
+}
+
+static nomask void loose_ability(mixed key, mixed dat, int loss) {
+  mixed val;
+
+  val=mappingp(dat)?dat[SI_SKILLABILITY]:dat;
+  if (!intp(val) || val<=0) return;
+  val-=(val*loss)/100;if (val<=0) val=1;
+  if (mappingp(dat))
+      dat[SI_SKILLABILITY]=val;
+  else
+      dat=val;
+}
+
+varargs nomask int austreten(int loss) {
+  object pl,gilde;
+  string gname;
+  mapping skills;
+
+  if (!(pl=this_player()) || !(gilde=previous_object()))
+      return 0;
+  // Gilden muessen Blueprints sein, so. ;-)
+  gname=object_name(gilde);
+  if (gname[0..7]!=GUILD_DIR ||
+      ((string)pl->QueryProp(P_GUILD))!=gname[8..]) {
+      write("Du kannst hier nicht aus einer anderen Gilde austreten.\n");
+      return -1;
+  }
+  if (gname[8..]==(pl->QueryProp(P_DEFAULT_GUILD)||DEFAULT_GUILD))
+  {
+        write("Aus dieser Gilde kannst Du nicht austreten.\n");
+        return -1;
+  }
+  if (loss<=0) loss=20;
+  skills=(mapping)pl->QueryProp(P_NEWSKILLS);
+  walk_mapping(skills,"loose_ability",this_object(),loss);
+  pl->SetProp(P_NEWSKILLS,skills);
+  pl->SetProp(P_GUILD,0);
+  // Event Gildenwechsel triggern
+  EVENTD->TriggerEvent(EVT_GUILD_CHANGE, ([
+      E_OBJECT: pl, E_PLNAME: getuid(pl),
+      E_ENVIRONMENT: environment(pl),
+      E_GUILDNAME: pl->QueryProp(P_GUILD),
+      E_LAST_GUILDNAME: gname ]) );
+
+  // Defaultgilde ggf. neuen Titel setzen lassen.
+  gname = (string)pl->QueryProp(P_GUILD);
+  (GUILD_DIR+"/"+gname)->adjust_title(pl);
+
+  return 1;
+}
+
+nomask static int security_check()
+{
+  if (previous_object() && geteuid(previous_object())==ROOTID)
+    return 1;
+  if (!process_call() && previous_object() && ARCH_SECURITY)
+    return 1;
+  return 0;
+}
+
+nomask int AddGuild(string gildob) {
+  object tp;
+
+  if (!stringp(gildob) || !sizeof(gildob) || !security_check() 
+      || file_size(GUILD_DIR+gildob+".c")<0)
+      return 0;
+  if (member(valid_guilds, gildob) != -1)
+      return 0;
+  valid_guilds+=({gildob});
+  save_object(GUILD_SAVEFILE);
+  return 1;
+}
+
+nomask int RemoveGuild(string gildob) {
+  object tp;
+
+  if (!stringp(gildob) || !sizeof(gildob) 
+      || !security_check())
+      return 0;
+  if (member(valid_guilds, gildob) == -1)
+      return 0;
+  valid_guilds-=({gildob});
+  
+  save_object(GUILD_SAVEFILE);
+  return 1;
+}
+
+nomask int ValidGuild(string gildenob) {
+  if (!stringp(gildenob)) 
+      return 0;
+
+  return (member(valid_guilds,gildenob)>=0);
+}
+
+nomask string *_query_valid_guilds() {return copy(valid_guilds); }
+
diff --git a/secure/guestmaster.c b/secure/guestmaster.c
new file mode 100644
index 0000000..0304d8d
--- /dev/null
+++ b/secure/guestmaster.c
@@ -0,0 +1,60 @@
+// MorgenGrauen MUDlib
+//
+// guestmaster.c -- Verwalter von Gast-Logins
+//
+// $Id: guestmaster.c 8909 2014-08-20 21:33:55Z Zesstra $
+#pragma no_clone,no_inherit,no_shadow
+#pragma strict_types,save_types,rtt_checks
+
+#include <wizlevels.h>
+
+nosave int max_guests = 10; /* Max no. of guests. -1 is 'unlimited' */
+nosave object *guests = ({});
+nosave int    *ltime = ({});
+
+protected void create()
+{
+  seteuid(getuid());
+}
+
+nomask int new_guest () {
+  int ix;
+
+  if (!max_guests) return 0;
+  if (load_name(previous_object()) != "/secure/login")
+    return 0;
+ 
+  if ((ix = member(guests,0)) == -1) {
+    ix = sizeof(guests);
+    if (max_guests < 0 || ix < max_guests) 
+    {
+      guests += ({ 0 }), ltime += ({ 0 });
+    }
+    else {
+      int mintime, minix;
+      mintime = time();
+      for (ix = 0; ix < sizeof(guests); ix++) {
+        if (guests[ix] && ltime[ix] < mintime) {
+          mintime=ltime[ix];
+          minix=ix;
+        }
+      }
+      ix = minix;
+    }
+  }
+  return ix+1;
+}
+
+nomask void set_guest (int ix, object pl) {
+  if (load_name(previous_object()) != "/secure/login") return;
+  guests[ix-1] = pl;
+  ltime[ix-1] = time();
+}
+
+nomask int query_max_guests() { return max_guests; }
+nomask int set_max_guests(int v) {
+  if (!process_call() && LORD_SECURITY)
+    max_guests = v;
+  return max_guests;
+}
+
diff --git a/secure/impfetch.c b/secure/impfetch.c
new file mode 100644
index 0000000..b7556bf
--- /dev/null
+++ b/secure/impfetch.c
@@ -0,0 +1,24 @@
+// Imp Fetcher
+
+#define FTPD "secure/ftpd"
+#define FTPIMP "std/shells/filesys/ftpimp"
+
+#define D(msg) if(find_player("rumata"))tell_object(find_player("rumata"),msg)
+
+void create() {}
+
+nomask mixed impFor( string name ) {
+    object imp;
+    string fname;
+
+    fname = "/ftpimp:" + name;
+    imp = find_object( fname );
+    if( !objectp(imp) ) {
+	seteuid( name );
+	imp = clone_object( FTPIMP );
+	imp->SetUser( name );
+	rename_object( imp, fname );
+        destruct( this_object() ); // next imp must have rootid again
+    }
+    return imp;
+}
diff --git a/secure/inetd.c b/secure/inetd.c
new file mode 100644
index 0000000..93b9145
--- /dev/null
+++ b/secure/inetd.c
@@ -0,0 +1,808 @@
+/*
+ * UDP port handling code. Version 0.7a
+ * Written by Nostradamus for Zebedee.
+ * Developed from an original concept by Alvin@Sushi.
+ */
+
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+//#pragma pedantic
+#pragma no_range_check
+#pragma no_warn_deprecated
+
+
+#include <living/comm.h>
+#define ZDEBUG(x)        if (find_player("zesstra"))\
+            find_player("zesstra")->ReceiveMsg(x, \
+                MT_DEBUG|MSG_DONT_STORE,0,"IM: ",this_object())
+
+//#define ZDEBUG(x)
+
+#include <udp.h>
+
+/* --- Configurable definitions. --- */
+
+/* CD muds will probably need these include file. */
+/* #include <std.h> */
+/* #include "/config/sys/local.h" */
+
+/* Public commands which will be accessible to any unlisted muds.
+ * PING, QUERY and REPLY are included by default. */
+#define COMMANDS \
+({ "channel", "finger", "tell", "who", "mail", "www", "htmlwho", "locate" })
+
+//({ "channel", "finger", "ftp", "locate", "man", "tell", "who" })
+
+/* Define this to the object that receives incoming packets and passes
+ * them to the inetd. Undefine for no _receive_udp() security.
+ * NOTE: The string must be of the format that object_name() returns. */
+#define UDP_MASTER                __MASTER_OBJECT__
+
+/* How to set the euid for this object if using native mode.
+ * Ensure that it can read the INETD_HOSTS file. */
+#define SET_EUID                seteuid(getuid())
+
+/* Define this if you are running another intermud package concurrently. */
+/* #define RECEIVE_UDP_COMPAT(sender, packet) \
+                UDP_MANAGER->incoming_udp(sender, packet)        // CD */
+
+/* Define this if you are running another intermud package concurrently and
+ * have a compatability module for it. */
+/* #define SEND_UDP_COMPAT(mudname, data, expect_reply) \
+                "/secure/udp_compat"->_send_udp(mudname, data, expect_reply) */
+
+/* The maximum number of characters we can send in one packet.
+ * You may need to reduce this, but 512 should be safe. */
+#define MAX_PACKET_LEN        1024
+
+/* You shouldn't need to change anything below. */
+
+#define USE_OLD_DELIMITER
+//#define DELIMITER_COMPAT
+#define USE_OLD_DATA_FORMAT
+
+#ifdef ZEBEDEE
+#include <defs.h>
+#endif
+
+#ifndef DATE
+#define DATE                ctime(time())[4..15]
+#endif
+
+/* --- End of Config. Do not alter anything below. --- */
+
+#define UNKNOWN                0
+#define UP                time()
+#define DOWN                (-time())
+
+#define NEW_DELIMITER        "\n"
+#ifdef USE_OLD_DELIMITER
+#define DELIMITER        "|"
+#else
+#define DELIMITER        "\n"
+#endif
+#define OLD_DELIMITER        "|"
+#define HOSTFILE_DEL        ":"
+#define HOSTFILE_DEL2        ","
+#define RETRY                "_RETRY"
+
+private nosave mapping hosts, pending_data, incoming_packets;
+private nosave mapping static_host_list;
+private nosave string *received_ids;
+private nosave int packet_id;
+
+void set_host_list();
+varargs string _send_udp(string mudname, mapping data, int expect_reply);
+
+#define DEBUG(msg)
+
+void my_create() {
+#ifndef COMPAT_FLAG
+    SET_EUID;
+#endif
+    packet_id = 0;
+    pending_data = ([ ]);
+    incoming_packets = ([ ]);
+    hosts = ([ ]);
+    received_ids = ({ });
+    set_host_list();
+    if (!this_player())
+        call_out("startup", 1, 0);
+}
+
+#ifdef CREATE_FUN
+CREATE_FUN() {
+#elif !defined(COMPAT_FLAG) || defined(ZEBEDEE)
+void create() {
+#else
+void reset(arg) {
+    if (arg)
+        return;
+#endif
+    my_create();
+}
+
+status check_system_field(mapping data, string field) {
+    return data[SYSTEM] && member(data[SYSTEM], field) != -1;
+}
+
+mapping add_system_field(mapping data, string field) {
+    if (data[SYSTEM]) {
+        if (!check_system_field(data, field))
+            data[SYSTEM] += ({ field });
+    }
+    else
+        data[SYSTEM] = ({ field });
+    return data;
+}
+
+private mapping read_host_list(string file) {
+
+  mixed data = (read_file(file));
+  if (!data) {
+    log_file(INETD_LOG_FILE, "*Error in reading host file: "
+        + file +"\n\n");
+    return 0;
+  }
+
+  data = old_explode(data, "\n");
+  mapping new_hosts = m_allocate(sizeof(data),1);
+
+  foreach(string line: data) {
+    if (line == "" || line[0] == '#')
+      continue;
+    mixed fields = old_explode(line, HOSTFILE_DEL);
+    if (sizeof(fields) < 5) {
+      log_file(INETD_LOG_FILE, sprintf(
+            "*Parse error in hosts file: %s, Line: %s\n", file, line));
+      continue;
+    }
+
+    string name = lower_case(fields[HOST_NAME]);
+    if (member(new_hosts, name))
+      continue; // mud already in list
+
+    string *local_cmds = old_explode(fields[LOCAL_COMMANDS],HOSTFILE_DEL2);
+    if (member(local_cmds,"*") != -1)
+        local_cmds = local_cmds - ({ "*" }) + COMMANDS;
+    
+    new_hosts[name] = ({ capitalize(fields[HOST_NAME]),
+                     fields[HOST_IP],
+                     to_int(fields[HOST_UDP_PORT]),
+                     local_cmds,
+                     old_explode(fields[HOST_COMMANDS], HOSTFILE_DEL2),
+                     UNKNOWN
+                   });
+    /*
+     * Get existing host status from current active host lost as long as the
+     * IP and UDP ports are the same.
+     */
+    if (hosts[name] &&
+        hosts[name][HOST_IP] == new_hosts[name][HOST_IP] &&
+        hosts[name][HOST_UDP_PORT] == new_hosts[name][HOST_UDP_PORT])
+        new_hosts[name][HOST_STATUS] = hosts[name][HOST_STATUS];
+  }
+  return new_hosts;
+}
+
+/*
+ * Read the INETD_HOSTS file and set the "hosts" mapping from it.
+ * Retain existing HOST_STATUS fields.
+ * Reads INETD_HOSTSS and INETD_HOSTS.dump
+ * Hosts from INETD_HOSTS are included in the static host list.
+ */
+void set_host_list() {
+
+  // read the static host file and the last host file dump.
+  mapping static_hosts = read_host_list(HOST_FILE);
+  if (!static_hosts)
+      return; // retain the old list(s)
+
+  // remember the static hosts
+  static_host_list = m_reallocate(static_hosts,0);
+  
+  // read the last host file dump and add the static hosts. Then the static
+  // hosts have precedence over the ones from the dump.
+  hosts = (read_host_list(HOST_FILE".dump") || ([])) + static_hosts;
+
+}
+
+mixed get_hosts() {return copy(hosts);}
+
+int  dump_hosts_list() {
+  
+  write_file(HOST_FILE+".dump", sprintf(
+    "#Hostlist dump from "MUDNAME", created %s\n", strftime()), 1);
+
+  foreach(string mudname, mixed tmp : hosts) {
+    // skip muds we did not hear from for 2 days.
+//    if (tmp[HOST_STATUS] + 172800 < time())
+//      continue;
+    write_file(HOST_FILE+".dump",
+        sprintf("%s:%s:%d:%s:%s\n",tmp[0],tmp[1],tmp[2],implode(tmp[3],","),
+          implode(tmp[4],",")));
+  }
+  return 1;
+}
+
+/*
+ * Make a PING request to all muds in the "hosts" mapping to set
+ * HOST_STATUS information.
+ * But don't ping all muds at once, because that may overflow the callout
+ * table during mud startup, when hundreds of objects make callouts.
+ */
+void startup(string *muds) {
+
+    if (!pointerp(muds))
+      muds=m_indices(hosts);
+    if (!sizeof(muds))
+      return;
+    string *part;
+    if (sizeof(muds) > 9)
+      part=muds[0..9];
+    else
+      part=muds;
+    foreach(string mud: part) 
+      _send_udp(mud, ([ REQUEST: PING ]), 1);
+    muds -= part;
+    if (sizeof(muds))
+      call_out(#'startup, 4, muds);
+}
+
+/*
+ * Remove a buffered packet from the "incoming_packets" mapping.
+ */
+void remove_incoming(string id) {
+    m_delete(incoming_packets, id);
+}
+
+/*
+ * Decode a string from a UDP packet.
+ * Returns:   The actual value of the argument (either int or string)
+ */
+mixed decode(string arg) {
+
+    if (sizeof(arg) && arg[0] == '$')
+        return arg[1..];
+#ifdef RESTRICTED_CASTS
+    if (to_string(to_int(arg)) == arg)
+        return to_int(arg);
+#else
+    if ((string)((int)arg) == arg)
+        return (int)arg;
+#endif
+    return arg;
+}
+
+/*
+ * Decode a UDP packet.
+ * Arguments: UDP packet as a string.
+ * Returns:   The decoded information as a mapping, 1 for succes but no
+ *            output (buffered packet), or 0 on error.
+ */
+mixed decode_packet(string packet, string delimiter) {
+    string *data;
+    mapping ret;
+    string info, tmp;
+    mixed class;
+    int i, id, n;
+
+    /* If this packet has been split, handle buffering. */
+    if (packet[0..sizeof(PACKET)] == PACKET + ":") {
+        if (sscanf(packet, PACKET + ":%s:%d:%d/%d" + delimiter + "%s",
+        class, id, i, n, tmp) != 5)
+            return 0;
+        class = lower_case(class) + ":" + id;
+        if (pointerp(incoming_packets[class])) {
+            incoming_packets[class][i-1] = tmp;
+            if (member(incoming_packets[class], 0) == -1) {
+                ret =
+                decode_packet(implode(incoming_packets[class], ""), delimiter);
+                remove_incoming(class);
+                return ret;
+            }
+        } else {
+            incoming_packets[class] = allocate(n);
+            incoming_packets[class][i-1] = tmp;
+/* Is it possible to already have a timeout running here ?!? */
+            /* If no timeout is running then start one. */
+            if (!pending_data[class]) {
+                call_out("remove_incoming",
+                REPLY_TIME_OUT + REPLY_TIME_OUT * RETRIES, class);
+            } else {
+                DEBUG("\nINETD: *** Buffered packet Timeout already running! ***\n");
+            }
+        }
+        return 1;
+    }
+    ret = ([ ]);
+    for(i = 0, n = sizeof(data = old_explode(packet, delimiter)); i < n; i++) {
+        /* DATA fields can be denoted by a preceeding blank field. */
+        if (data[i] == "") {
+            tmp = DATA;
+            /* Test for illegal packet length (no DATA) */
+            if (++i >= n)
+                return 0;
+            info = data[i];
+        }
+        else if (sscanf(data[i], "%s:%s", tmp, info) != 2)
+            return 0;
+        switch((string)(class = decode(tmp))) {
+            case DATA:
+#ifdef USE_EXTRACT
+                return ret + ([ DATA: decode(implode(
+                    ({ info }) + extract(data, i+1), delimiter)) ]);
+#else
+                return ret + ([ DATA: decode(implode(
+                    ({ info }) + data[i+1..], delimiter)) ]);
+#endif
+            case SYSTEM:
+                ret[class] = old_explode(info, ":");
+                continue;
+            default:
+                ret[class] = decode(info);
+                continue;
+        }
+    }
+    return ret;
+}
+
+/* Check wether a UDP packet was valid.
+ * Logs are made and "host" information is updated as appropriate.
+ * Arguments: Decoded UDP packet (mapping)
+ * Returns:   0 for valid packets, an error string otherwise.
+ */
+string valid_request(mapping data) {
+    mixed host_data;
+    string *muds;
+    string req;
+    int i;
+    
+    if (!data[NAME] || !data[UDP_PORT])
+        return DATE + ": Illegal packet.\n";
+    if (host_data = hosts[lower_case(data[NAME])]) {
+        if (data[HOST] != host_data[HOST_IP]) {
+            if (data[NAME] == LOCAL_NAME)
+                return DATE + ": *** FAKE MUD ***\n";
+            log_file(INETD_LOG_FILE, DATE + ": Host change:\n" +
+            host_data[HOST_NAME] + ": " + host_data[HOST_IP] + " -> " +
+            data[HOST] + "\n\n");
+            host_data[HOST_IP] = data[HOST];
+        }
+        if (data[UDP_PORT] != host_data[HOST_UDP_PORT]) {
+            if (data[NAME] == LOCAL_NAME)
+                return DATE + ": *** FAKE MUD ***\n";
+            log_file(INETD_LOG_FILE, DATE + ": Port change:\n" +
+                host_data[HOST_NAME] + " (" + host_data[HOST_IP] + "): " +
+                host_data[HOST_UDP_PORT] + " -> " + data[UDP_PORT] + "\n\n");
+            host_data[HOST_UDP_PORT] = data[UDP_PORT];
+        }
+    } else {
+        if (lower_case(data[NAME]) == lower_case(LOCAL_NAME))
+            return DATE + ": *** FAKE MUD ***\n";
+        for(i = sizeof(muds = m_indices(hosts)); i--; ) {
+            host_data = hosts[muds[i]];
+            if (data[HOST] == host_data[HOST_IP] &&
+            data[UDP_PORT] == host_data[HOST_UDP_PORT]) {
+                log_file(INETD_LOG_FILE, DATE + ": Name change:\n" +
+                    host_data[HOST_NAME] + " (" + host_data[HOST_IP] +
+                    ") -> " + data[NAME] + "\n\n");
+                host_data[HOST_NAME] = data[NAME];
+                hosts[lower_case(data[NAME])] = host_data;
+                m_delete(hosts, muds[i]);
+                i = -2;
+                break;
+            }
+        }
+        if (i != -2) {
+            host_data = hosts[lower_case(data[NAME])] = ({
+                data[NAME],
+                data[HOST],
+                data[UDP_PORT],
+                COMMANDS,
+                ({ "*" }),
+                UP
+            });
+            log_file(INETD_LOG_FILE, DATE + ": New mud.\n" + data[NAME] + ":" +
+            data[HOST] + ":" + data[UDP_PORT] + "\n\n");
+        }
+    }
+    if (!(req = data[REQUEST]))
+        return DATE + ": System message.\n";
+    if (req != PING &&
+        req != QUERY &&
+        req != REPLY &&
+        member(host_data[LOCAL_COMMANDS], req) == -1)
+    {
+        /* This should probably send a system message too. */
+        _send_udp(host_data[HOST_NAME], ([
+            REQUEST: REPLY,
+            RECIPIENT: data[SENDER],
+            ID: data[ID],
+            DATA: "Invalid request @" + LOCAL_NAME + ": " +
+                capitalize(data[REQUEST]) + "\n"
+        ]) );
+        return DATE + ": Invalid request.\n";
+    }
+    return 0;
+}
+
+/*
+ * Incoming UDP packets are sent to this function to be interpretted.
+ * The packet is decoded, checked for validity, HOST_STATUS is updated
+ * and the appropriate udp module called.
+ * Arguments: Senders IP address (string)
+ *            UDP packet (string)
+ */
+void _receive_udp(string sender, string packet) {
+    mapping data;
+    string req, err, id;
+
+#ifdef UDP_MASTER
+    if (!previous_object() ||
+    object_name(previous_object()) != UDP_MASTER) {
+        log_file(INETD_LOG_FILE, DATE + ": Illegal call of _receive_udp() by " +
+        object_name(previous_object()) + "\n\n");
+        return;
+    }
+#endif
+
+    ZDEBUG(sprintf("%O -> %.500O\n",sender, packet));
+    if (
+#ifdef DELIMITER_COMPAT
+#ifdef USE_EXTRACT
+        (!(data = decode_packet(packet, NEW_DELIMITER))
+#else
+        (!(data = decode_packet(packet, NEW_DELIMITER))
+#endif
+        || (data[HOST] = sender) && (err = valid_request(data))) &&
+        (!(data = decode_packet(packet, OLD_DELIMITER)) ||
+        (data[HOST] = sender) && (err = valid_request(data)))
+#else
+#ifdef USE_EXTRACT
+        !(data = decode_packet(packet, DELIMITER))
+#else
+        !(data = decode_packet(packet, DELIMITER))
+#endif
+#endif
+    ) {
+        if (!data)
+#ifdef RECEIVE_UDP_COMPAT
+            RECEIVE_UDP_COMPAT(sender, packet);
+#else
+            log_file(INETD_LOG_FILE, DATE + ": Received invalid packet.\nSender: " +
+            sender + "\nPacket:\n" + packet + "\n\n");
+#endif
+        return;
+    }
+#ifdef DELIMITER_COMPAT
+    if (!mappingp(data))
+        return;
+    if (err)
+#else
+          if (!mappingp(data))
+                  return;
+    data[HOST] = sender;
+    if (err = valid_request(data))
+#endif
+    {
+        log_file(INETD_LOG_FILE, err + "Sender: " + sender + "\nPacket:\n" +
+        packet + "\n\n");
+        return;
+    }
+    hosts[lower_case(data[NAME])][HOST_STATUS] = UP;
+    if ((req = data[REQUEST]) == REPLY) {
+            mapping pending;
+
+        /* If we can't find the reply in the pending list then bin it. */
+        if (!(pending = pending_data[lower_case(data[NAME]) + ":" + data[ID]]))
+            return;
+        /* Set data[REQUEST] correctly, but still send to (req = REPLY) */
+        data[REQUEST] = pending[REQUEST];
+#ifdef INETD_DIAGNOSTICS
+        data[PACKET_LOSS] = pending[PACKET_LOSS];
+        data[RESPONSE_TIME] = time() - pending[RESPONSE_TIME] + 1;
+#endif
+#if 0
+/* channel replies may not include a recipient, and shouldn't have one set */
+        /* Restore the RECIPIENT in replies if none given and it is known. */
+        if (!data[RECIPIENT] && pending[SENDER])
+            data[RECIPIENT] = pending[SENDER];
+#endif
+        m_delete(pending_data, lower_case(data[NAME]) + ":" + data[ID]);
+    }
+    else if (data[ID]) {
+      id = lower_case(data[NAME]) + ":" + data[ID];
+      if (member(received_ids, id) == -1)
+      {
+        received_ids += ({ id });
+        call_out("remove_received_id",
+              REPLY_TIME_OUT + REPLY_TIME_OUT * (RETRIES + 1), id);
+      }
+      else
+            add_system_field(data, REPEAT);
+    }
+    if (err = catch(
+        call_other(UDP_CMD_DIR + req, "udp_" + req, deep_copy(data));publish))
+    {
+        _send_udp(data[NAME], ([
+            REQUEST: REPLY,
+            RECIPIENT: data[SENDER],
+            ID: data[ID],
+            DATA: capitalize(req)+ " request failed @" + LOCAL_NAME + ".\n"
+        ]) );
+        log_file(INETD_LOG_FILE, DATE + ": " + data[REQUEST] + " from " +
+        data[NAME] + " failed.\n" + err + packet + "\n\n");
+    }
+}
+
+int do_match(string mudname, string match_str) {
+    return mudname[0..sizeof(match_str)-1] == match_str;
+}
+
+#ifdef NO_CLOSURES
+
+status greater(mixed a, mixed b) {
+    return a > b;
+}
+
+string *expand_mud_name(string name) {
+    return sort_array(
+        filter(m_indices(hosts), "do_match", this_object(), name),
+        "greater",
+        this_object()
+    );
+}
+
+#else
+
+string *expand_mud_name(string name) {
+    return sort_array(
+        filter(m_indices(hosts), #'do_match, name),
+        #'>
+    );
+}
+
+#endif
+
+string encode(mixed arg) {
+    if (objectp(arg))
+        return object_name(arg);
+    if (stringp(arg) && sizeof(arg) &&
+        (arg[0] == '$' ||
+#ifdef RESTRICTED_CASTS
+    to_string(to_int(arg)) == (string)arg))
+#else
+    (string)to_int(arg) == (string)arg))
+#endif
+        return "$" + arg;
+    return to_string(arg);
+}
+
+string encode_packet(mapping data) {
+    int i;
+    mixed indices;
+    string header, body, t1, t2;
+    string *ret;
+    status data_flag;
+
+    for(ret = ({ }), i = sizeof(indices = m_indices(data)); i--; ) {
+        if (indices[i] == DATA) {
+            data_flag = 1;
+            continue;
+        }
+        header = encode(indices[i]);
+        body = encode(data[indices[i]]);
+        if (sscanf(header, "%s:%s", t1, t2) ||
+            sscanf(header + body, "%s" + DELIMITER + "%s", t1, t2)
+        )
+            return 0;
+        
+        ret += ({ header + ":" + body });
+    }
+    if (data_flag)
+#ifdef USE_OLD_DATA_FORMAT
+        ret += ({ DATA + ":" + encode(data[DATA]) });
+#else
+        ret += ({ "", encode(data[DATA]) });
+#endif
+    return implode(ret, DELIMITER);
+}
+
+// Funktion explode_
+// Die Funktion zerlegt den String packet in gleich lange Teilstrings
+// der Laenge len und gibt die Teilstrings als Array zurueck. Der letzte
+// Teilstring kann kuerzer sein als die anderen.
+string *explode_packet(string packet, int len) {
+
+  int ptr, m_ptr,size;
+  string *result;
+
+  // Variablen initialisieren
+  m_ptr=ptr=0;
+  size=sizeof(packet);
+  result=({});
+
+  // Um Arrayadditionen zu vermeiden wird vorher allokiert. Die Division 
+  // durch 0 ist nicht abgefangen, da bei len=0 was im Aufruf falch ist.
+  result=allocate((size/len+1));
+
+  while (ptr<size) {
+    result[m_ptr] = // Aktuellen Teilstring speichern
+#ifdef USE_EXTRACT
+            extract(packet,ptr,ptr+len-1);
+#else
+            packet[ptr..ptr+len-1]; 
+#endif
+    ptr+=len;// Neuen Pointer im String berechnen
+    m_ptr++; // Neuen Pointer im Mapping berechnen. Hier nutze ich eine
+             // Variable mehr als noetig, weil das billiger ist, als jedes
+             // mal ptr=m_ptr*len auszurechnen. Lieber zwei Additionen als 
+             // eine Multiplikation und eine Addtion.
+  }
+
+  // Wenn size/len aufgeht, ist das Result-Feld zu gross. Dann bleibt 
+  // ein Element leer, das wird hier gestrippt. Das ist billiger als
+  // jedesmal auszurechnen, ob size/len aufgeht.
+  return result-({0});
+ 
+}
+
+varargs string _send_udp(string mudname, mapping data, int expect_reply) {
+    mixed host_data;
+    string *packet_arr;
+    string packet;
+    int i;
+
+    mudname = lower_case(mudname);
+    if (!(host_data = hosts[mudname])) {
+        string *names;
+
+        if (sizeof(names = expand_mud_name(mudname)) == 1)
+            host_data = hosts[mudname = names[0]];
+        else
+#ifdef SEND_UDP_COMPAT
+            return (string)SEND_UDP_COMPAT(mudname, data, expect_reply);
+#else
+        if (!sizeof(names))
+            return "Unbekannter Mudname: " + capitalize(mudname) + "\n";
+        else
+            return break_string("Mudname ("+capitalize(mudname)+
+            ") nicht eindeutig, es passen: "+implode(map(names,#'capitalize),", ")+
+            ".\n",78);
+#endif
+    }
+    
+    if (data[REQUEST] != PING &&
+        data[REQUEST] != QUERY &&
+        data[REQUEST] != REPLY &&
+        member(host_data[HOST_COMMANDS], "*") == -1 &&
+        member(host_data[HOST_COMMANDS], data[REQUEST]) == -1)
+        return capitalize(data[REQUEST]) + ": Command unavailable @" +
+                   host_data[HOST_NAME] + "\n";
+    
+    data[NAME] = LOCAL_NAME;
+    data[UDP_PORT] = LOCAL_UDP_PORT;
+    
+    if (expect_reply) {
+        /* Don't use zero. */
+        data[ID] = ++packet_id;
+        /* Don't need deep_copy() as we are changing the mapping size. */
+        pending_data[mudname + ":" + packet_id] =
+#ifdef INETD_DIAGNOSTICS
+        data + ([ NAME: host_data[HOST_NAME], RESPONSE_TIME: time() ]);
+#else
+        data + ([ NAME: host_data[HOST_NAME] ]);
+#endif
+    }
+    if (!(packet = encode_packet(data))) {
+        if (expect_reply)
+            pending_data = m_copy_delete(pending_data, mudname + ":" + packet_id);
+        log_file(INETD_LOG_FILE, DATE + ": Illegal packet sent by " +
+        object_name(previous_object()) + "\n\n");
+        return "inetd: Illegal packet.\n";
+    }
+    if (expect_reply)
+        call_out("reply_time_out", REPLY_TIME_OUT, mudname + ":" + packet_id);
+
+    if (sizeof(packet) <= MAX_PACKET_LEN)
+        packet_arr = ({ packet });
+    else {
+        string header;
+        int max;
+
+        /* Be careful with the ID.  data[ID] could have been set up by RETRY */
+        header =
+            PACKET + ":" + lower_case(LOCAL_NAME) + ":" +
+            ((expect_reply || data[REQUEST] != REPLY)&& data[ID] ?
+            data[ID] : ++packet_id) + ":";
+        
+        /* Allow 8 extra chars: 3 digits + "/" + 3 digits + DELIMITER */
+        packet_arr = explode_packet(packet,
+            MAX_PACKET_LEN - (sizeof(header) + 8));
+
+        for(i = max = sizeof(packet_arr); i--; )
+            packet_arr[i] =
+            header + (i+1) + "/" + max + DELIMITER + packet_arr[i];
+    }
+    
+    for(i = sizeof(packet_arr); i--; )
+    {
+      ZDEBUG(sprintf("%O <- %.500O\n",host_data[HOST_IP], packet_arr[i]));
+      if (!send_udp(
+        host_data[HOST_IP], host_data[HOST_UDP_PORT], packet_arr[i]))
+            return "inetd: Error in sending packet.\n";
+    }
+    return 0;
+}
+
+void reply_time_out(string id) {
+    mapping data;
+
+    if (data = pending_data[id]) {
+        object ob;
+
+#ifdef INETD_DIAGNOSTICS
+        data[PACKET_LOSS]++;
+#endif
+        if (data[RETRY] < RETRIES) {
+            mapping send;
+
+            data[RETRY]++;
+            /* We must use a copy so the NAME field in pending_data[id]
+             * isn't corrupted by _send_udp(). */
+            send = deep_copy(data);
+            send = m_copy_delete(send, RETRY);
+#ifdef INETD_DIAGNOSTICS
+            send = m_copy_delete(send, PACKET_LOSS);
+            send = m_copy_delete(send, RESPONSE_TIME);
+#endif
+            call_out("reply_time_out", REPLY_TIME_OUT, id);
+            _send_udp(data[NAME], send);
+            return;
+        }
+        data = m_copy_delete(data, RETRY);
+#ifdef INETD_DIAGNOSTICS
+        data = m_copy_delete(data, RESPONSE_TIME);
+#endif
+        catch(call_other(UDP_CMD_DIR + REPLY, "udp_" + REPLY,
+        add_system_field(data, TIME_OUT));publish);
+        /* It's just possible this was removed from the host list. */
+        if (hosts[lower_case(data[NAME])])
+            hosts[lower_case(data[NAME])][HOST_STATUS] = DOWN;
+        remove_incoming(lower_case(data[NAME]) + ":" + id);
+    }
+    pending_data = m_copy_delete(pending_data, id);
+}
+
+void remove_received_id(string id) {
+    received_ids -= ({ id });
+}
+
+varargs mixed query(string what, mixed extra1, mixed extra2) {
+    mixed data;
+
+    switch(what) {
+        case "commands":
+            return COMMANDS;
+        case "hosts":
+            return deep_copy(hosts);
+        case "pending":
+            return deep_copy(pending_data);
+        case "incoming":
+            return deep_copy(incoming_packets);
+        case "received":
+            return ({ }) + received_ids;
+        /* args: "valid_request", request, mudname */
+        case "valid_request":
+            if (data = hosts[extra2])
+                return member(data[HOST_COMMANDS], "*") != -1 ||
+                        member(data[HOST_COMMANDS], extra1) != -1;
+            return 0;
+    }
+    return(0);
+}
+
diff --git a/secure/krautmaster.c b/secure/krautmaster.c
new file mode 100644
index 0000000..0d0ab06
--- /dev/null
+++ b/secure/krautmaster.c
@@ -0,0 +1,1289 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_inherit, no_clone, no_shadow
+
+#include <defines.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <items/kraeuter/kraeuter.h>
+#include <items/kraeuter/trankattribute.h>
+#include <living/comm.h>
+
+
+#ifndef BS
+#define BS(x)             break_string(x, 78)
+#endif
+
+// Weiter unten beim Parsen des Datenfiles werden SetProp() und Name()
+// verwendet, daher erben wir thing.
+inherit "/std/secure_thing";
+
+// Liste aller moeglichen Zutaten, gemappt ueber einen key
+// AN: irritierender Name, denn es sind nicht direkt die Zutaten enthalten
+// sondern die ueber einen Key identifizierten Kraeutergruppen, die die
+// Wirkungen/Funktionen festlegen.
+// Format:
+// ([ key : ({ attr_pos, attr_neg1, attr_neg2, Haltbark., Wirkdauer, 
+//             Unterstuetzung, Stoerung, Haeufigkeit  }) ])
+// Beispiel: [( "a7":({({2,400}),({11,150}),0,172800,0,({"n*"}),0,1}) ])
+// HU: War ein Mapping, bleibt ein Mapping, und neu: Die Eintraege werden 
+// auch Mappings!
+private mapping ingredients;
+
+// Mapping von id zum Key der Zutaten im ingredients-mapping: id2key[id]=key
+// Die beiden Listen werden benoetigt, um zu den Kraut-IDs die jeweiligen
+// Keys zu finden.
+// HU: KEIN MAPPING. Ein Array von Strings. Aber das geht natuerlich.
+private string *id2key;
+
+// Mapping von key zu Kraeuter-ID der Zutaten.
+private mapping key2id;
+
+// Eigenschaften der Kraeuter:
+// krautprops[id]=({ID, Demon, Gender, Name, Adjektiv(e), P_LONG, Raumdetail})
+// krautprops[0] = 0; ID 0 darf nicht existieren
+private mixed *krautprops;
+
+// hier wird gespeichert welche Version einer Zutat fuer einen Spieler ist.
+// AN/TODO: Wenn das fuer Nichtseher nutzbar sein soll, sollte hier besser
+// mit getuuid() gearbeitet werden, statt getuid() zu verwenden.
+// player enthaelt fuer jede Spieler-UID einen Bitstring. Gesetztes Bit
+// bedeutet, dass der Spieler ueber das Kraut mit dieser ID etwas ueber
+// die Verwendung und Wirkung weiss. Es gibt jedoch noch keine Lernmoeglich-
+// keit und INSBESONDERE keine Funktionalitaet im Master, ueber die man 
+// die Bits setzen koennte.
+private mapping player;
+
+// verstuemmeltes mapping fuer den VC in service (Save-Dummy)
+private mapping map_ldfied;
+
+// rooms enthaelt die Teileintraege des map_ldfied mapping vom VC
+private mapping rooms;
+
+// Enthaelt Daten zu den Raeumen, in denen das Trocknen von Kraeutern
+// moeglich ist.
+private mapping drying_data = ([:4]);
+
+string build_plantname(mixed *props);
+
+// struct-Templat fuer Trankattribute
+// Fuer das SQL-Log MUSS die Reihenfolge der Trankattribute hier genau die
+// sein, wie die Spalten in der Tabelle.
+/* Currently not used.
+struct trank_attrib_s {
+    int car;
+    int da;
+    int dm;
+    int du;
+    int dn;
+    int flt;
+    int fro;
+    int hI;
+    int hP;
+    int hK;
+    int hL;
+    int pa;
+    int pm;
+    int pu;
+    int ss;
+    int sp;
+    int sd;
+};
+*/
+
+#define allowed() (!process_call() && \
+        IS_ARCH(RPL) && IS_ARCH(this_interactive()) )
+
+#define POTIONFILES ({ TRANKITEM })
+
+// eigenschaften im krautprops-array
+#define PROP_ID           INGREDIENT_ID
+#define PROP_DEMON        INGREDIENT_DEMON
+#define PROP_GENDER       INGREDIENT_GENDER
+#define PROP_NAME         INGREDIENT_NAME
+#define PROP_ADJ          INGREDIENT_ADJ
+#define PROP_LONG         INGREDIENT_LONG
+#define PROP_ROOMDETAIL   INGREDIENT_ROOMDETAIL
+
+// ATTR_ is immer ein array ({ attribut, ability })
+#define ATTR_ARR_ATTRIB      0
+#define ATTR_ARR_ABILITY     1
+
+// maximal erlaubter Wert fuer eine Eigenschaft
+#define MAX_ATTR_ABILITY      999
+
+void debug(string str)
+{
+  //write("debug: "+str+"\n");
+  if (this_player() && IS_ARCH(this_player()))
+    this_player()->ReceiveMsg(str,536870912);
+}
+
+protected void create()
+{
+   seteuid(getuid());
+   // AN/TODO: Variablen oben direkt initialisieren. Pruefen, ob davon
+   // irgendwas nosave sein kann.
+   if (!restore_object(__DIR__"/ARCH/krautmaster"))
+   {
+      ingredients=([]);
+      id2key=({});
+      key2id=([]);
+      krautprops=({});
+      player=([]);
+      map_ldfied=([]);
+      rooms=([]);
+   }
+   if (sl_open("/log/ARCH/krauttrank.sqlite") != 1)
+   {
+     raise_error("Datenbank konnte nicht geoeffnet werden.\n");
+   }
+   sl_exec("CREATE TABLE IF NOT EXISTS traenke(id INTEGER PRIMARY KEY, "
+           "uid TEXT NOT NULL, rnd INTEGER, "
+           "time INTEGER DEFAULT CURRENT_TIMESTAMP, "
+           "receipe TEXT NOT NULL, "
+           "quality TEXT NOT NULL, "
+           "car INTEGER, "
+           "da INTEGER, "
+           "dm INTEGER, "
+           "du INTEGER, "
+           "dn INTEGER, "
+           "flt INTEGER, "
+           "fro INTEGER, "
+           "hI INTEGER, "
+           "hP INTEGER, "
+           "hK INTEGER, "
+           "hL INTEGER, "
+           "pa INTEGER, "
+           "pm INTEGER, "
+           "pu INTEGER, "
+           "ss INTEGER, "
+           "sp INTEGER, "
+           "sd INTEGER);"
+           );
+   sl_exec("CREATE INDEX IF NOT EXISTS idx_uid ON traenke(uid);");
+   sl_exec("CREATE INDEX IF NOT EXISTS idx_receipe ON traenke(receipe);");
+   sl_exec("CREATE TABLE IF NOT EXISTS rohdaten(id INTEGER PRIMARY KEY, "
+           "uid TEXT NOT NULL, rnd INTEGER, "
+           "time INTEGER DEFAULT CURRENT_TIMESTAMP, "
+           "receipe TEXT NOT NULL, "
+           "quality TEXT NOT NULL, "
+           "car INTEGER, "
+           "da INTEGER, "
+           "dm INTEGER, "
+           "du INTEGER, "
+           "dn INTEGER, "
+           "flt INTEGER, "
+           "fro INTEGER, "
+           "hI INTEGER, "
+           "hP INTEGER, "
+           "hK INTEGER, "
+           "hL INTEGER, "
+           "pa INTEGER, "
+           "pm INTEGER, "
+           "pu INTEGER, "
+           "ss INTEGER, "
+           "sp INTEGER, "
+           "sd INTEGER);"
+           );
+   sl_exec("CREATE INDEX IF NOT EXISTS idx_uid_r ON rohdaten(uid);");
+}
+
+public string QueryPlantFile(int id)
+{
+  if (member(krautprops, id))
+    return build_plantname(krautprops[id]);
+  return 0;
+}
+
+// AN: Funktion ermittelt, ob ein Spieler pl das Kraut mit der ID id
+// verwenden kann. Laut Kommentar unten muss man dafuer wohl was ueber das
+// Kraut gelernt haben, von wem ist mir gerade noch nicht klar.
+// AN/TODO: Es ist bisher keine Funktionalitaet vorhanden, um die IDs fuer
+// den Spieler freizuschalten. Die Funktionsweise muss aus dem Beispielcode
+// unten fuer das Learn-On-Demand abgelesen werden.
+nomask int CanUseIngredient(object pl, int id)
+{
+  // Ich mach mal den harten Weg. -- Humni
+  return 1;
+  // Bitstring des Spielers aus der Liste auslesen.
+   string str=player[getuid(pl)];
+   if (!stringp(str)) str="";
+   if (test_bit(str, id))
+      return 1; // make the common case fast
+   else {
+     // letztenendes habe ich mich entschlossen einzubauen, das man nur die
+     // Kraeuter verwenden kann, ueber die man schonmal etwas gelesen/gelernt
+     // hat, aus diesem Grund, ist folgender Block auskommentiert.
+     // Dieser Block bedeutet quasi ein "auto learning on demand" d.h.
+     // wird ein Kraut verwendet wird geprueft ob fuer diese Gruppe bereits
+     // eine ID freigeschaltet wurde - ansonsten wird eine freigeschaltet.
+     /*
+         // pruefen ob fuer den Key bereits ein Bit gesetzt ist, ggf. setzen
+         if (id>=sizeof(id2key)) return 0; // illegale Id
+         int idlist=key2id[id2key[id]];
+         int i;
+         for (i=sizeof(idlist)-1; i>=0; i--) {
+           if (test_bit(str, idlist[i])) return 0; // Key bereits vorhanden
+         }
+         // Zufaellig ein Bit fuer den Key setzen
+         i=random(sizeof(idlist));
+         player[getuid(pl)]=set_bit(str, idlist[i]);
+         save_object(object_name());
+         return (i==id);
+     */
+     return 0;
+   }
+}
+
+// Diese Funktion wird vom Metzelorakel aufgerufen, um die Belohnungen zu
+// erzeugen, die man dort fuer erfolgreich absolvierte Metzelauftraege erhaelt
+#define ALLOWED_CALLER ({ "/d/ebene/arathorn/orakel/secure/schamane" })
+object get_plant_by_category(int npc_class)
+{
+  if ( member(ALLOWED_CALLER, load_name(previous_object()))<0 )
+    raise_error("unauthorised call to get_plant_by_category()\n");
+
+  // Uebergebene NPC-Klasse in Kraut-Kategorie umsetzen.
+  // Kategorie 7 wird als 7-9 interpretiert (siehe unten).
+  int category;
+  switch(npc_class) {
+    case 1: case 2:         category=4; break;
+    case 3: case 4:         category=5; break;
+    case 5: case 6: case 7: category=6; break;
+    default:                category=7; break;
+  }
+
+  // Alle Kraeuter der ermittelten Kategorie raussuchen. Bei Kategorie 7
+  // werden auch alle aus 8 und 9 dazugenommen.
+  int *eligible_plant_ids=({});
+  foreach( string grp, mapping grpdata : ingredients ) {
+    if ( category == 7 && grpdata[T_ABUNDANCE]>=7 || 
+          category == grpdata[T_ABUNDANCE] )
+      eligible_plant_ids += key2id[grp];
+  }
+
+  // Krautnamen zu den Kraut-IDs ermitteln.
+  string *plantfiles=map(eligible_plant_ids, function string (int plantid) {
+    return build_plantname(krautprops[plantid]);});
+
+  // Ein Kraut zufaellig auswaehlen, clonen und das Objekt zurueckgeben.
+  object plant=clone_object(PLANTDIR+plantfiles[random(sizeof(plantfiles))]);
+  plant->DryPlant(80+random(11));
+  // Aufschreiben, wer welches Kraut mit welcher Qualitaet rausbekommt.
+  log_file("ARCH/plant_by_category",
+    sprintf("%s %-12s %-s Qual %d Kat %d Class %d\n", 
+      strftime("%x %X",time()), getuid(PL),
+      object_name(plant)[sizeof(PLANTDIR)..],
+      plant->QueryProp(P_QUALITY), category, npc_class));
+// sprintf("%24s: call from %O, player: %s (PL: %O), kategory: %d\n", ctime(),
+// previous_object(), getuid(player), PL, kategory))
+  return plant;
+}
+
+private nosave object simul_efun, plantvc;
+
+// fuer SIMUL_EFUN_FILE
+#include <config.h>
+
+// AN/TODO: Klaeren, warum hier eine eigene Funktion get_cloner() existiert,
+// wo doch der Kraeuter-VC schon eine hat.
+nomask private string get_cloner()
+{
+   int i;
+   object po;
+   if (!simul_efun) {
+      if (!(simul_efun=find_object(SIMUL_EFUN_FILE)))
+         simul_efun=find_object(SPARE_SIMUL_EFUN_FILE);
+   }
+   // wenn sie jetzt nicht existiert - auch gut, dann gibt es halt keine
+   // sefuns.
+
+   if (!plantvc) plantvc=find_object(KRAEUTERVC);
+   
+   for (i=0; po=previous_object(i); i++) {
+      if (po==master() || po==simul_efun || po==ME ||
+          po==previous_object() || po==plantvc)
+         continue;
+      else return object_name(po);
+   }
+   return 0;
+}
+
+// AN: 
+nomask string CheckPlant(int id)
+{
+   if (id<=0 || id>=sizeof(id2key)) return 0;
+   if (!stringp(id2key[id])) return 0;
+   return get_cloner();
+}
+
+// ueber diese Funktion kann die Liste der Id's updatet werden
+// es wird <filename> eingelesen und durchgeparst.
+// Diese Datei muss in jeder Zeile folgendes Format einhalten und darf keine
+// leerzeilen enthalten! > Id,key,Gender,Name,Adjektiv
+// AN: Das ist Quatsch. Das Format muss so aussehen:
+// String-ID;Kraut-ID;demon;gender;P_NAME;adjektiv;P_LONG;roomdetail;
+// HU: Diese Funktion lass ich so. Harhar.
+// Update: Na gut. Fast.
+nomask private int LoadIdList(string filename)
+{
+   int i, id, si, demon;
+   string *lines, file;
+   mixed *data;
+   file=read_file(filename);
+   if (!stringp(file)) raise_error("Missing File: "+filename+"\n");
+   lines=explode(read_file(filename), "\n");
+   si=sizeof(lines)-1;
+   // AN/TODO: Warum verwendet man hier nicht auch einfach explode()?
+   // Wenn "##ende##" als Trennzeichen enthalten ist, liefert explode()
+   // als letzten Eintrag einen Leerstring, darauf kann man pruefen.
+   // Allerdings muesste das vor dem explode() zur Zeilentrennung passieren.
+   for ( ;si>=0; si--) 
+   {
+     string lili=lines[si];
+     if (strstr(lines[si],"##ende##")>=0) break;
+   }
+   si--;
+   if (si<0) raise_error("##ende## not found in id-list.\n");
+   id2key=allocate(si+2);
+   krautprops=allocate(si+2);
+   // AN: Fuer das Auslesen der Werte aus dem Array "data" muessen die 
+   // Indizierungs-Defines jeweils +1 verwendet werden, weil im Datenfile
+   // schlauerweise ein Datenfeld *vorne* angefuegt wurde.
+   // AN/TODO: vielleicht sollte man besser Element 0 zuerst auslesen und
+   // dann das Array arr um ein Element kuerzen: arr = arr[1..];
+   for (i=si; i>=0; i--) {
+      data=explode(lines[i], ";");
+      if (sizeof(data)!=8) raise_error("Wrong id-list format in line "+(i+1)+"\n");
+      id=to_int(data[PROP_ID+1]);
+      data[PROP_ID+1]=id;
+      if (id>si+1) raise_error(sprintf("Line %d: id %d greater than array size %d.\n", i, id, si));
+      id2key[id]=data[0];
+      // AN: Ich sehe noch nicht so ganz, warum man von dem Datenfeld
+      // PROP_DEMON nur das letzte Zeichen als Literal pruefen sollte.
+      // Komplett geht doch genausogut?
+      switch(data[PROP_DEMON+1][<1]) {
+        case 'R': data[PROP_DEMON+1]=RAW; break;
+        case '0': data[PROP_DEMON+1]=0;   break;
+        case '1': data[PROP_DEMON+1]=1;   break;
+        case '2': data[PROP_DEMON+1]=2;   break;
+        default: raise_error("Unknown demonformat '"+data[PROP_DEMON+1]+
+                   "' in idlist line "+(i+1)+"\n");
+      }
+      switch(data[PROP_GENDER+1][<1]) {
+        case 'N': data[PROP_GENDER+1]=NEUTER; break;
+        case 'F': data[PROP_GENDER+1]=FEMALE; break;
+        case 'M': data[PROP_GENDER+1]=MALE;   break;
+        default: raise_error("Unknown genderformat '"+data[PROP_GENDER+1]+
+                   "' in idlist line "+(i+1)+"\n");
+      }
+      SetProp(P_GENDER, data[PROP_GENDER+1]);
+      SetProp(P_NAME, data[PROP_NAME+1]);
+      // AN/TODO: data[PROP_ADJ] muss man nicht unbedingt vorher auf 0 setzen
+      if (!sizeof(data[PROP_ADJ+1])) data[PROP_ADJ+1]=0;
+      if (data[PROP_ADJ+1])
+         SetProp(P_NAME_ADJ, ({"ganz normal", data[PROP_ADJ+1]}));
+      else SetProp(P_NAME_ADJ, "ganz normal");
+      SetProp(P_ARTICLE, data[PROP_DEMON+1]!=RAW);
+      demon=(data[PROP_DEMON+1]==RAW ? 0 : data[PROP_DEMON+1]);
+      // AN: Wenn keine Langbeschreibung hinterlegt wurde, wird der Name
+      // des Krautes als solche verwendet. Ebenso fuer das Raumdetail, das
+      // beim Betrachten des Krautes im Raum ausgegeben wird.
+      if (!sizeof(data[PROP_LONG+1])) {
+         data[PROP_LONG+1] = Name(WER, demon)+".\n";
+      }
+      else data[PROP_LONG+1] = BS(data[PROP_LONG+1]);
+// Humni: Offenbar kommen am Zeilenende manchmal Zeichen dazu. Ich gehe davon
+// aus, dass keine Beschreibung kuerzer als 2 Zeichen ist.
+      if (sizeof(data[PROP_ROOMDETAIL+1])<2) {
+         data[PROP_ROOMDETAIL+1] = Name(WER, demon)+".\n";
+      }
+      else data[PROP_ROOMDETAIL+1] = BS(data[PROP_ROOMDETAIL+1]);
+      krautprops[id]=data[1..];
+   }
+   SetProp(P_NAME, 0);
+   SetProp(P_NAME_ADJ, "");
+   
+   // key2id-Cache neu aufbauen.
+   key2id=([]);
+   for (i=sizeof(id2key)-1; i>=0; i--) {
+      if (member(key2id, id2key[i]))
+         key2id[id2key[i]]+=({ i });
+      else key2id[id2key[i]]=({ i });
+   }
+   return 1;
+}
+
+// Hilfsfunktion wird zum einparsen benötigt
+// wandelt z.B. den string von "h 500" in ({ 3, 500 }) um
+private nomask <string|int>* buildAttrArr(string attr)
+{
+   if (!attr) return 0;
+   attr=trim(attr);
+   <string|int>* res=explode(attr, " ")-({""});
+   //debug(sprintf(" build Attr %O",res));
+   if (sizeof(res)!=2) raise_error("Wrong attrib format!\n");
+   //debug(sprintf("%O",T_KRAUT_MAP));
+   return ({T_KRAUT_MAP[res[0]],(int)res[1]});
+}
+
+// AN: Hilfsfunktion zum Einlesen der Traenkeliste.
+nomask private void LoadIndex(string filename)
+{
+   int i, j, si;
+   string *lines, file;
+   mixed  *data;
+
+   file=read_file(filename);
+   if (!stringp(file)) raise_error("Missing File: "+filename+"\n");
+   
+   // AN/TODO: Auch hier waere wieder die Verwendung von explode() zu
+   // erwaegen.
+   lines=explode(file, "\n")-({""});
+   si=sizeof(lines);
+   for (i=0; i<si; i++) if (lines[i]=="##start##") break;
+   i++;
+   if (i>=si) raise_error("missing ##start## in Index.\n");
+   ingredients=([]);
+   for (; i<si; i++) { // alle zeilen durchlaufen...
+     // AN/TODO: Tabulatoren als Trennzeichen finde ich irgendwie bloed.
+     // HU: Darum nun Semikolons
+     // debug("Zeile "+lines[i]);
+     data=old_explode(lines[i], ";"); // an Semikolons trennen...
+     // debug(sprintf("%O",data));
+     if (sizeof(data)!=9) 
+       {
+         //debug(sprintf("%O",data));
+         raise_error("Wrong indexlist format in line "+(i+1)+"\n");
+       }
+     for (j=8; j>=0; j--) {
+       // AN/TODO: if(data[j]=="" || data[j]=="-") data[j]=0;
+       // es sind ohnehin ueberall Strings enthalten.
+       // Wir machen aus "-" oder "0" eine echte int-Null.
+       if (sizeof(data[j])==0 || data[j][0]=='-') data[j]=0;
+     }
+     // HU: Ab hier bau ich mal neu. Den Rest pack ich auskommentiert darunter, wenn jemand den alten Code
+     // nachschauen will.
+     // Ich will ein Mapping von dieser Kraeutergruppe.
+     mapping mk=([]);
+     // Dieses Mapping soll die Eintraege nun enthalten.
+     mk[T_EFFECT_DURATION]=to_int(data[5]);
+     mk[T_ABUNDANCE]=to_int(data[8]);
+     // positive Effekte aufteilen
+     //debug(sprintf("Vorposis %O - %O",mk,data));
+     if (stringp(data[1]))
+       {
+         string* posis=explode(data[1],",");
+         //debug(sprintf("%O - %O",mk,posis));
+         foreach (string q:posis) {
+           //debug(q);
+           <string|int>* arr=buildAttrArr(q);
+           //debug(sprintf("%O",arr));
+           mk[arr[0]]=mk[arr[0]]+arr[1];
+         }
+       }
+     //debug(sprintf("%O",mk));
+     // Erster Negativer Effekt
+     if (data[2]!=0)
+       {
+         <string|int>* arr=buildAttrArr(data[2]);
+         mk[arr[0]]=mk[arr[0]]-arr[1];
+       }
+     //debug(sprintf("vorneg %O",mk));
+     // Zeiter negativer Effekt
+     if (data[3]!=0)
+       {
+         <string|int>* arr=buildAttrArr(data[3]);
+         mk[arr[0]]=mk[arr[0]]-arr[1];
+       }
+     // Haltbarkeit wird umgerechnet
+     string* sti=explode(data[4]," ")-({""});
+     //debug("haltbar "+sti[0]);
+     string stt=trim(sti[0]);
+     int dur;
+     if (stt[<1..]=="d") // Tage
+       {
+         //debug("Tage");
+         // Der erste Teil ist die Dauer in Tagen.
+         dur=to_int(sti[0][..<2]);
+         dur=dur*24*60*60; // Sekunden
+         mk[T_EXPIRE]=dur;
+       }
+     if (stt[<1..]=="h") // Stunden
+       {
+         //debug("Stunden");
+         // Sonst ist es halt die Dauer in Stunden.
+         dur=to_int(sti[0][..<2]);
+         dur=dur*60*60;
+         mk[T_EXPIRE]=dur;
+       }
+     //debug("ergibt "+dur);
+     // Nun die lustigen Unterstuetzungen. Dazu benutzen wir unseren lustigen Glueckshasen.
+     // Und ein Reh.
+     string* glueckshase;
+     string reh;
+     // Alle Leerzeichen raus!
+     //debug("Rehe");
+     //debug(sprintf("Data: %O",data));
+     if (stringp(data[6]))
+       {
+         reh=(explode(data[6]," ")-({""}))[0];
+     
+         glueckshase=explode(reh,",")-({});
+         mk[T_SUPPORTER]=glueckshase;
+       }
+     else
+       {
+         mk[T_SUPPORTER]=0;
+       }
+     // Nun machen wir genauso die Blockaden.
+     // Das tolle ist: Reh und Glueckshase koennen wir wiederverwenden! Das freut.
+     if (stringp(data[7]))
+       {
+         reh=(explode(data[7]," ")-({""}))[0];
+         glueckshase=explode(reh,",")-({});
+         mk[T_BLOCKING]=glueckshase;
+       }
+     else
+       {
+         mk[T_BLOCKING]=0;
+       }
+     ingredients[trim(data[0])]=mk;
+   }
+   //debug("Wuff");
+   //debug(sprintf("%O",ingredients));
+}
+
+nomask private void save()
+{
+  save_object(__DIR__"/ARCH/krautmaster");
+}
+
+// AN: erzeugt aus Namen + Adjektiv der Pflanzendaten den Objektnamen,
+// z.B. waldrebe_gemein oder ackerklee_gelb, wobei Bindestriche auch
+// durch Unterstriche ersetzt werden (acker_rettich).
+string build_plantname(mixed *props)
+{
+   string key;
+   // AN/TODO: erst PROP_NAME in key schreiben, dann ggf. PROP_ADJ dazu
+   if (sizeof(props[PROP_ADJ])>0)
+      key=lowerstring(props[PROP_NAME]+"_"+props[PROP_ADJ]);
+   else key=lowerstring(props[PROP_NAME]);
+   // AN/TODO: ersetzen durch regreplace();
+   key=implode(old_explode(key, " "), "_");
+   key=implode(old_explode(key, "-"), "_");
+   return key;
+}
+
+public void UpdateVC()
+{
+  KRAEUTERVC->update(map_ldfied);
+}
+
+// AN: Daten neu parsen
+// Nach dem Schreiben des Savefiles mittels save() wird auch das
+// Kraeuter-Headerfile vom Kraeuter-VC neu geschrieben.
+int _refresh()
+{
+   int i;
+   string key;
+   if (extern_call() && !allowed())
+     return 0;
+   
+   LoadIdList(__DIR__"ARCH/kraeuterliste.dump");
+   LoadIndex(__DIR__"ARCH/kraeuterindex.dump");
+   map_ldfied=([]);
+   for (i=sizeof(krautprops)-1; i>=0; i--)
+   {
+      if (sizeof(krautprops[i])<=PROP_ROOMDETAIL) continue;
+      key = build_plantname(krautprops[i]);
+      map_ldfied[key]=({ krautprops[i], rooms[key]||([]) });
+   }
+   save();
+   UpdateVC();
+   
+   // Update Headerfile mit Kraeuterliste
+   string *keys = sort_array(m_indices(map_ldfied), #'<);
+   string headerfile =
+     "// Automatisch generiertes File, nicht von Hand editieren!\n"
+     "// Erzeugendes File: "+object_name()+"\n\n"
+     "#define PLANTCOUNT "+to_string(sizeof(keys))+"\n\n"
+     +"#define PLANT(x) \"/items/kraeuter/\"+x\n\n";
+   foreach(key: keys)
+   {
+     headerfile += sprintf("#define %-30s PLANT(\"%s\")\n",
+                     upperstring(key), key);
+   }
+   write_file(KRAEUTERLISTE, headerfile, 1);
+
+   write("Inputfiles parsed. Save & Headerfiles updated!\n");
+   return 1;
+}
+
+int _cloneplant(string str)
+{
+  if (allowed())
+  {
+    if (to_string(to_int(str)) == str)
+      str = QueryPlantFile(to_int(str));
+    clone_object(PLANTDIR+str)->move(this_player(), M_NOCHECK);
+    write("Kraut " + str + " geclont.\n");
+    return 1;
+  }
+  return 0;
+}
+
+#define MAX_ROOMS 10  /* kein Kraut ist in mehr als 10 Raeumen */
+// AN: Ausgabe der Kategorienliste ueber das Planttool.
+int _showplant(string str)
+{
+   int i, si, kat, secure;
+   string *list, key;
+   mixed *res, *arr;
+   mapping props;
+
+   secure=allowed();
+   notify_fail("Es gibt folgende Kraeuterkategorien:\n"
+    +" 0 - haeufig und an vielen Stellen im Mud anzufinden\n"
+    +" 1 - etwas seltener zu finden, aber immer noch leicht\n"
+    +" 2 - an wenigen gut versteckten Stellen in abgelegenen Gebieten\n"
+    +" 3 - dito, jedoch muss das Kraut durch einen NPC (XP >= 500000) bewacht sein.\n"
+    +" 4 - aeusserst selten und XP >= 1 mio\n"
+    +" 5 - aeusserst selten und XP >= 2 mio\n"
+    +" 6 - aeusserst selten und NPC bringt >= 5  Stupse\n"
+    +" 7 - aeusserst selten und NPC bringt >= 10 Stupse\n"
+    +" 8 - aeusserst selten und NPC bringt >= 20 Stupse\n"
+    +" 9 - aeusserst selten und NPC bringt >= 50 Stupse\n"
+    +"\nSyntax: showplant <kategorie>.\n");
+   kat=to_int(str);
+   if (kat<0 || kat>9) return 0;
+   if (to_string(kat)!=str) return 0;
+   list=m_indices(map_ldfied);
+   // AN: *grummel*
+   // res = allocate(MAX_ROOMS, ({}));
+   res=map(allocate(MAX_ROOMS), #'allocate); // ({ ({}) ... ({}) })
+   for (i=sizeof(list)-1; i>=0; i--) {
+      arr=map_ldfied[list[i]];
+      if (sizeof(arr)!=2) raise_error("Wrong map_ldfied-Format by "+list[i]+"\n");
+      key=id2key[arr[0][PROP_ID]];
+      if (!key) raise_error("Missing Key for id "+arr[0][PROP_ID]+"\n");
+      props=ingredients[key];
+      //if (!pointerp(props)) continue; // noch nicht eingetragen
+      //if (sizeof(props)!=8)
+      // printf("Warning: Wrong ingredient-content by "+key+"\n");
+      //debug(sprintf("%O",props));
+      if (props==0)
+      {
+        debug("Falscher Key: "+key);
+      }
+      else
+      {
+        if (props[T_ABUNDANCE]==kat)
+        {
+          si=sizeof(arr[1]);
+          if (si<MAX_ROOMS) {
+            if (stringp(arr[0][PROP_ADJ])) {
+              SetProp(P_ARTICLE, 0);
+              SetProp(P_NAME, arr[0][PROP_NAME]);
+              SetProp(P_NAME_ADJ, arr[0][PROP_ADJ]);
+              SetProp(P_GENDER, arr[0][PROP_GENDER]);
+              key=Name(WER);
+            }
+            else key=arr[0][PROP_NAME];
+            if (secure)
+              res[si]+=({ sprintf("%3d %-40s: %d\n", arr[0][PROP_ID], key, si) });
+            else res[si]+=({ sprintf("%-40s: %d\n", key, si) });
+          }
+        }
+      }
+   }
+   for (i=0; i<MAX_ROOMS; i++) {
+      sort_array(res[i], #'>);
+      filter(res[i], #'write);
+   }
+   return 1;
+}
+
+// AN: Ausgabe der Raeume, in denen die Kraeuter zu finden sind.
+// angesteuert ueber das Planttool.
+int _showrooms(string str)
+{
+   int i, j, id;
+   string *list, dummy;
+   mixed *arr;
+   if (!allowed()) return 0;
+   notify_fail("Syntax: showrooms <id> oder showrooms all\n");
+   if (str!="all") {
+     id=to_int(str);
+     if (to_string(id)!=str) return 0;
+   }
+   else id=-1;
+   list=m_indices(map_ldfied);
+   for (i=sizeof(list)-1; i>=0; i--) {
+      arr=map_ldfied[list[i]];
+      if (sizeof(arr)!=2) raise_error("Wrong map_ldfied-Format by "+list[i]+"\n");
+      if (arr[0][PROP_ID]==id || id<0) {
+         if (!sizeof(m_indices(arr[1]))) {
+            if (id>=0) write("Fuer diese Id sind keine Raeume eingetragen.\n");
+         }
+         else if (id>=0) {
+            write("Folgende Raeume sind fuer "+arr[0][PROP_ID]+" eingetragen.\n");
+            filter(map(m_indices(arr[1]), #'+, "\n"), #'write);
+            return 1;
+         }
+         else filter(map(m_indices(arr[1]), #'+, ": "+arr[0][PROP_ID]+", "+arr[0][PROP_NAME]+"\n"), #'write);
+         if (id>=0) return 1;
+      }
+   }
+   write("Fuer diese Id sind bisher keine Kraeuter eingetragen.\n");
+   return 1;
+}
+
+// Nutzung der Kraeuter in  Gebieten liefert nur dann gueltige Kraeuter,
+// wenn der Raum eingetragen ist.
+int _addroom(string str)
+{
+   int id, i;
+   string *list, vc;
+
+   if (!allowed()) {
+      write("Fuer das Eintragen der Raeume wende Dich doch bitte "
+            "an einen EM.\n");
+      return 1;
+   }
+   notify_fail("Syntax: addroom <krautnummer> <filename>\n");
+   str=PL->_unparsed_args();
+   if (!str || sscanf(str, "%d %s", id, str)!=2) return 0;
+   if (str=="hier" || str=="here")
+   {
+     if (!this_player())
+     {
+       notify_fail("Kein Spielerobjekt, kann "
+           "Raum nicht identifizieren.\n");
+       return 0;
+     }
+     str=to_string(environment(this_player()));
+   }
+   if (str[<2..]==".c") str=str[0..<3]; // .c abschneiden
+   if (file_size(str+".c")<=0) {
+      list=explode(str, "/");
+      vc=implode(list[0..<2], "/")+"/virtual_compiler.c";
+      if (file_size(vc)<=0 || !call_other(vc, "Validate", list[<1])) {
+         write("No such file \""+str+"\".\n");
+         return 1;
+      }
+   }
+   if (id<=0 || id>=sizeof(id2key)) {
+      write("Illegal plantid "+id+".\n");
+      return 1;
+   }
+   list=m_indices(map_ldfied);
+   for (i=sizeof(list)-1; i>=0; i--) {
+      if (map_ldfied[list[i]][0][PROP_ID]==id) {
+         if (!member(map_ldfied[list[i]][1], str)) {
+            map_ldfied[list[i]][1]+=([ str ]);
+            rooms[list[i]]=(rooms[list[i]]||([]))+([ str ]);
+            write("Raum Erfolgreich eingetragen!\n");
+         }
+         else write("Raum bereits eingetragen.\n");
+         save();
+         _refresh();
+         return 1;
+      }
+   }
+   write("Kraut mit id "+id+" nicht gefunden.\n");
+   return 1;
+}
+
+int _delroom(string str)
+{
+   int i, done;
+   string *list;
+
+   if (!allowed()) {
+      write("Fuer das Loeschen von Raeumen wende Dich doch bitte "
+            "an einen EM.\n");
+      return 1;
+   }
+   notify_fail("Syntax: delroom <filename>.\n");
+   str=PL->_unparsed_args();
+   if (!str) return 0;
+   if (str[<2..]==".c") str=str[0..<3];
+   list=m_indices(map_ldfied); done=0;
+   for (i=sizeof(list)-1; i>=0; i--)
+   {
+      if (member(map_ldfied[list[i]][1], str)) {
+         m_delete(map_ldfied[list[i]][1], str);
+         m_delete(rooms[list[i]], str);
+         write("Raum bei id "+map_ldfied[list[i]][0][PROP_ID]
+               +" ausgetragen.\n");
+         done=1;
+      }
+   }
+   if (!done) {
+      if (file_size(str+".c")<0)
+         write("No such file \""+str+"\".\n");
+      else write("Fuer "+str+" sind keine Kraeuter eingetragen!\n");
+   }
+   else {
+      save();
+      _refresh();
+   }
+   return 1;
+}
+
+// Veranlasst den Kraeuter-VC, eine phys. Datei aus den Kraeuterdaten eines
+// Krautes zu erzeugen, falls man diese ausbeschreiben will.
+int _createfile(string str)
+{
+  int id;
+  if (!allowed()) {
+    write("Diese Funktion wurde fuer Dich nicht freigegeben!\n");
+    return 1;
+  }
+  id=to_int(str);
+  if (to_string(id)!=str || id<=0 || id>=sizeof(id2key)) {
+    write("Illegal plantid '"+str+"'.\n");
+    return 1;
+  }
+  notify_fail("Unknown Function im kraeuterVC: _createfile()\n");
+  return call_other(KRAEUTERVC, "_createfile", build_plantname(krautprops[id]));
+}
+
+// AN: Hilfsfunktionen, derzeit ebenfalls deaktiviert.
+// i = 0..7, Position des Krautes, fuer das der Aufruf erfolgt, in der 
+// Liste der in den Kessel einfuellten Kraeuter.
+// keyLst ist die Liste der Kraeutergruppen, der die Kraeuter zugeordnet
+// sind.
+// An dieser Stelle kann also die Wirkung von Kraeutergruppen abgehaengt
+// werden. Unklar ist mir aktuell nur, warum diese Funktion den Parameter
+// "i" benoetigen wuerde.
+// Idee: Es soll die Entscheidung davon abhaengig gemacht werden koennen,
+// wie die Gesamtkombination aussieht, und zusaetzlich davon, aus welcher
+// Gruppe das einzelne Kraut stammt.
+nomask private int IsBlocked(int i, string *keyLst)
+{
+  return 0;
+}
+
+// AN: Diese Funktion muesste nach dem Code in make_potion() zu urteilen
+// 0 oder 1 zurueckgeben, dann wird der Eigenschaftswert der Kraeutergruppe
+// um den Faktor 1.5 verstaerkt.
+nomask private int IsBoosted(int i, string *keyLst)
+{
+  return 0;
+}
+
+#define PRNG "/std/util/rand-glfsr"
+// Individuelle Boni/Mali fuer Spieler. ploffset soll via Referenz uebergeben
+// werden und wird von der Funktion gesetzt.
+int calculate_mod(int krautindex, string plname, int ploffset)
+{
+  // Startoffset zufaellig ermittelt, aber immer gleich
+  // fuer jeden Spielernamen
+  PRNG->InitWithUUID(plname);
+  ploffset = PRNG->random(16);
+  // Jedes Kraut hat auch einen iOffset (der konstant bleibt und sich nicht
+  // aendert). Der wird auch addiert. Bei Ueberschreiten von 30 wird nach 0
+  // gewrappt.
+  // Der Offset ist dann (spieleroffset + krautindex) % 16, d.h. alle Modifikatoren werden
+  // der Reihe nach durchlaufen. So kriegt jeder Spieler - fast egal, bei welchem
+  // Offset er startet - auch der Reihe nach alle Boni+Mali.
+  int offset = ((ploffset + krautindex) % 16) * 2;
+  // Am Ende wird das ganze noch nach 85 bis 115 verlegt.
+  return offset + 85;
+}
+
+#define ZWEITIES "/secure/zweities"
+
+mapping calculate_potion(int* plantids, int* qualities, string familie)
+{
+  // Man sollte ohne die Kraeuter nicht so einfach Wirkungen beliebig
+  // berechnen koennen.
+  if (extern_call() && !ARCH_SECURITY)
+    return 0;
+
+  // Hier speichern wir unser Ergebnis bzw. unser Zwischenergebnis.
+  mapping attrib;
+  // Hier speichern wir die Wirkgruppen, die aus den Plants gezogen werden.
+  mapping* wirkungen=({});
+  // Hier speichern wir gleich schon beim Erzeugen die wichtigsten Blockaden.
+  string* unterstuetzungen=({});
+  string* blockaden=({});
+  int zufall;
+  // Die Sortierung nach PlantID ist nur fuer das Tranklog wichtig.
+  plantids = sort_array(plantids, #'<=);
+
+  // PASS 1: Pflanzen durch Wirkungen ersetzen, dabei Unterstuetzer
+  // und Blocker merken.
+  foreach (int id, int qual : mkmapping(plantids,qualities))
+    {
+      //debug(sprintf("Gehe durch Plant: %d",id));
+      string key=id2key[id];
+      // Wirkungen dieses Krauts kopieren
+      mapping ing=copy(ingredients[key]);
+      //debug(sprintf("%O",ing));
+      // Zu den Wirkungen noch den Key hinzufuegen.
+      ing["key"]=key;
+      // Die Qualitaet des Krautes wird noch mit dem spielerindividuellen
+      // Modifikator skaliert.
+      ing["quality"]=(qual * calculate_mod(id, familie, &zufall)) / 100;
+      wirkungen+=({ing});
+      if (pointerp(ing[T_SUPPORTER]))
+        {
+          foreach (string pi:ing[T_SUPPORTER])
+            {
+              unterstuetzungen+=({pi});
+            }
+        }
+      if (pointerp(ing[T_BLOCKING]))
+        {
+          foreach (string pi:ing[T_BLOCKING])
+            {
+              blockaden+=({pi});
+            }
+        }
+      debug(sprintf("Kraut %s ergibt Werte %O.",key,wirkungen));
+    }
+  // PASS 2: Jetzt die Unterstuetzungen auswerten
+  foreach (mapping mar:wirkungen)
+    {
+      foreach (string pi:unterstuetzungen)
+        {
+          // Haben wir eine Unterstuetzung?
+          if (mar["key"]==pi || ((pi[1]=='*') && (pi[0]==mar["key"][0])))
+            {
+              debug (sprintf("mar=%O, pi=%O",mar,pi));
+              // ALLE ZAHLEN mal 1.5 nehmen. Mir ist klar, dass das nun auch
+              // mit irgendwelchen Haeufigkeiten passiert, aber mal ehrlich,
+              // das ist zur Berechnung egal.
+              foreach (string kk, mixed val : &mar)
+                {
+                  if (intp(val))
+                    {
+                      val=15*val/10;
+                    }
+                }
+            }
+        }
+    }
+  // PASS 3: Jetzt die Blockaden auswerten
+  foreach (mapping mar:wirkungen)
+    {
+      foreach (string pi:blockaden)
+        {
+          // Haben wir eine Blockade?
+          if (mar["key"]==pi || ((pi[1]=='*') && (pi[0]==mar["key"][0])))
+            {
+              debug (sprintf("mar=%O, pi=%O",mar,pi));
+              // Hier werden alle Zahlen auf Null gesetzt.
+              foreach (string kk, mixed val : &mar)
+                {
+                  if (intp(val))
+                    {
+                      val=0;
+                    }
+                }
+            }
+        }
+    }
+
+  // PASS 3.5: Qualitaet der Kraeuter skalieren.
+  foreach (mapping mar:wirkungen)
+    {
+      foreach (string kk, mixed val : &mar)
+        {
+          if (intp(val) && kk!="quality")
+            {
+              val=val*mar["quality"]/100;
+            }
+        }
+    }
+
+  // PASS 4: Nun addieren wir alles auf in das Mapping attrib.
+  attrib=([]);
+  foreach (mapping mar:wirkungen)
+    {
+      foreach (string kk:mar)
+        {
+          if (intp(mar[kk]))
+            {
+              attrib[kk]=attrib[kk]+mar[kk];
+            }
+        }
+    }
+
+  // Die Wirkungsdauer ist der Durchschnitt der Wirkungsdauern
+  attrib[T_EFFECT_DURATION] /= sizeof(plantids);
+  debug(sprintf("Duration: %d\n",attrib[T_EFFECT_DURATION]));
+
+  // Die Haltbarkeit des Tranks ist die Haltbarkeit des kleinsten Krautes.
+  int dur=10000000;
+  foreach (mapping mar:wirkungen)
+    {
+      if (mar[T_EXPIRE]>0 && dur>mar[T_EXPIRE])
+        {
+          dur=mar[T_EXPIRE];
+        }
+    }
+  if (dur==10000000)
+    {
+      dur=0;
+    }
+  attrib[T_EXPIRE]=dur;
+  debug(sprintf("Expire: %d\n",dur));
+
+  int maximum=0;
+  // Effekte rausrechnen, die nicht maximal sind
+  foreach (string kk, mixed val:attrib)
+  {
+    if (member(T_KRAUT_EFFECTS,kk)>=0)
+    {
+      if (val>0 && maximum<val)
+      {
+        maximum=val;
+      }
+    }
+  }
+  // Logeintrag erstellen.
+  sl_exec("INSERT INTO rohdaten(uid, rnd, receipe, quality, car, da, dm, du, "
+          "dn, flt, fro, hI, hP, hK, hL, pa, pm, pu, ss, sp, sd) "
+          "VALUES(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11, ?12, ?13, ?14, "
+          "?15, ?16, ?17, ?18, ?19, ?20, ?21);",
+          this_player() ? getuid(this_player()) : "<unknown>",
+          zufall,
+          implode(map(plantids, #'to_string), ", "),
+          implode(map(qualities, #'to_string), ", "),
+          attrib[T_CARRY], attrib[T_DAMAGE_ANIMALS],
+          attrib[T_DAMAGE_MAGIC], attrib[T_DAMAGE_UNDEAD],
+          attrib[T_EFFECT_DURATION], attrib[T_FLEE_TPORT],
+          attrib[T_FROG], attrib[T_HEAL_DISEASE],
+          attrib[T_HEAL_POISON], attrib[T_HEAL_SP],
+          attrib[T_HEAL_LP], attrib[T_PROTECTION_ANIMALS],
+          attrib[T_PROTECTION_MAGIC], attrib[T_PROTECTION_UNDEAD],
+          attrib[T_SA_SPEED], attrib[T_SA_SPELL_PENETRATION],
+          attrib[T_SA_DURATION]);
+
+  // Maximal zwei positive Effekte.
+  int cteff=0;
+  foreach (string kk, mixed val : &attrib)
+  {
+      if (member(T_KRAUT_EFFECTS,kk)>=0)
+      {
+        // Nur die 2 staerksten positiven Wirkungen bleiben ueber (dazu
+        // muessen sie wirklich den gleichen Zahlenwert haben, kann bei
+        // Heilungen vorkommen, sonst eher unwahrscheinlich).
+        if (val>0 && maximum>val)
+        {
+          val=0;
+        }
+        // Thresholds. Zu zu grosse Wirkungen haben die Grenze als
+        // Auswirkung. Negative Wirkungen, die -T_MINIMUM_THRESHOLD nicht
+        // ueberschreiben, fallen weg. Bei den positiven bleibt ja ohnehin nur
+        // die staerkste Wirkung ueber, da gibt es erstmal keine
+        // Mindestgroesse mehr.
+        if (val>T_MAXIMUM_THRESHOLD)
+        {
+          val=T_MAXIMUM_THRESHOLD;
+        }
+        if (val < 0 && val > -T_MINIMUM_THRESHOLD)
+        {
+          val=0;
+        }
+        if (maximum==val && val>0)
+        {
+          cteff++;
+          // Voellig willkuerlich, was hier getroffen wird. Ob reproduzierbar,
+          // vermutlich ja, aber haengt mit der Mappingstruktur zusammen.
+          // Harhar.
+          if (cteff>2)
+          {
+            val=0;
+          }
+        }
+      }
+  }
+  debug(sprintf(" TRANKERGEBNIS: %O",attrib));
+  // Logeintrag erstellen.
+  sl_exec("INSERT INTO traenke(uid, rnd, receipe, quality, car, da, dm, du, "
+          "dn, flt, fro, hI, hP, hK, hL, pa, pm, pu, ss, sp, sd) "
+          "VALUES(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11, ?12, ?13, ?14, "
+          "?15, ?16, ?17, ?18, ?19, ?20, ?21);",
+          this_player() ? getuid(this_player()) : "<unknown>",
+          zufall,
+          implode(map(plantids, #'to_string), ", "),
+          implode(map(qualities, #'to_string), ", "),
+          attrib[T_CARRY], attrib[T_DAMAGE_ANIMALS],
+          attrib[T_DAMAGE_MAGIC], attrib[T_DAMAGE_UNDEAD],
+          attrib[T_EFFECT_DURATION], attrib[T_FLEE_TPORT],
+          attrib[T_FROG], attrib[T_HEAL_DISEASE],
+          attrib[T_HEAL_POISON], attrib[T_HEAL_SP],
+          attrib[T_HEAL_LP], attrib[T_PROTECTION_ANIMALS],
+          attrib[T_PROTECTION_MAGIC], attrib[T_PROTECTION_UNDEAD],
+          attrib[T_SA_SPEED], attrib[T_SA_SPELL_PENETRATION],
+          attrib[T_SA_DURATION]);
+
+  return attrib;
+}
+
+mapping make_potion(object* plants)
+{
+  // -> mappt nicht-Objekt zu 0, aber 0 ist auch ne gueltige PlantID. Daher
+  // müssen zerstoerten Objekte vorher raus.
+  // TODO: drauf verlassen, dass nur intakte Objekt enthalten sind?
+  if (member(plants, 0) >= 0)
+    raise_error(sprintf("make_potion() got invalid object in plant array "
+                        "%.50O\n",plants));
+
+  int* plantids = (int*)plants->QueryPlantId();
+  int* qualities = (int*)plants->QueryProp(P_QUALITY);
+
+  return calculate_potion(plantids, qualities,
+                          ZWEITIES->QueryFamilie(this_player()));
+}
+
+// AN: Sucht alle Pflanzen raus, in deren Namen der Suchbegriff "str"
+// vorkommt und listet diese auf. Laeuft allerdings momentan noch in einen
+// Fehler "index out of bounds", aber man muesste hier (TODO) sowieso mal
+// von explode() auf strstr() umbauen, denke ich.
+string _findplant(string str) {
+  int i, k;
+  string *ind, *tmp;
+
+  if(!str) return "";
+  write("Suche nach '"+str+"':\n\n");
+  ind = m_indices(map_ldfied);
+  for(i=0;i<sizeof(ind);i++) {
+    tmp = map_ldfied[ind[i]][0];
+    if( stringp(tmp[3]) && 
+        old_explode(lower_case(tmp[3]),str)[0] != lower_case(tmp[3]) 
+        ||
+        stringp(tmp[4]) && 
+        old_explode(lower_case(tmp[4]),str)[0] != lower_case(tmp[4]))
+      write(" -  "+tmp[3]+
+          (stringp(tmp[4])?" ("+tmp[4]+")":"")+"       - "+tmp[0]+"\n");
+  }
+
+  return "";
+}
+
+// AN: Funktion liefert das Ergebnisarray aus make_potion() fuer eine Liste
+// von Kraeutern, die als ", "-getrennte Kraut-IDs uebergeben werden muessen.
+mixed _checkTrank(string str)
+{
+  if (extern_call() && !allowed())
+    return 0;
+
+  string *ind, *args, name;
+  object *objs;
+  int k, l;
+
+  objs = ({});
+  if(!str) return "Keine Kraeuter uebergeben!";
+  ind = old_explode(str,",");
+//  ind = ({"180","11","53"});
+  for(int i=0;i<sizeof(ind);i++)
+  {
+    name = build_plantname(krautprops[to_int(ind[i])]);
+    write("Input: '"+name+"' ("+ind[i]+")\n");
+    objs += ({clone_object(PLANTDIR+name)});
+  }
+  mapping ragtest = make_potion(objs);
+  objs->remove();
+/*  name="";
+  for(int i=0;i<sizeof(ragtest);i++)
+       name = name + ragtest[i]+",";
+  write("Result: ({ "+name+" })\n");*/
+  return sprintf("%O\n",ragtest);
+}
+
+#define QUAL_BASE  0
+#define QUAL_RND   1
+#define DELAY_BASE 2
+#define DELAY_RND  3
+
+#define ALLOWED_DRIER "/items/kraeuter/trockner"
+
+// Liefert die Trocknungsdaten eines Raumes aus, mit denen der Kraeuter-
+// trockner dann das Kraut bearbeiten kann.
+int *QueryDryingData() {
+  // Es muss allerdings das aufrufende Objekt ein Trockner-Clone sein, 
+  // der in einem der zugelassenen Raeume stehen muss.
+  // Wenn das nicht der Fall ist, wird der Trockner das leere Array, das 
+  // zurueckgegeben wird, als Indiz werten, dass er im falschen Raum steht.
+  if ( objectp(previous_object()) &&
+       load_name(previous_object()) == ALLOWED_DRIER &&
+       member(drying_data, load_name(environment(previous_object()))) &&
+       clonep(previous_object()) )
+  {
+    // Raum ermitteln, Delay/Quali errechnen, Ergebnisarray zurueckgeben.
+    string where = load_name(environment(previous_object()));
+    int delay = drying_data[where,DELAY_BASE]+
+                random(drying_data[where,DELAY_RND]);
+    int qual  = drying_data[where,QUAL_BASE]+
+                random(drying_data[where,QUAL_RND]);
+    return ({ delay, qual });
+  }
+  return ({});
+}
+
+// Modifizieren der Trocknungsdaten.
+// <room> muss der volle Dateiname des Raumes sein, ohne .c am Ende.
+// <values> enthaelt die vier Parameter zu dem Raum in folgender Reihenfolge:
+// ({ Quali-Basis, Quali-Zufallsanteil, Delay-Basis, Delay-Zufallsanteil })
+// Wenn <values> nicht angeben wird oder 0 ist, werden die Daten zu <room>
+// geloescht.
+int|mapping SetDryingData(string room, int* values) 
+{
+  // keine Zugriffsrechte
+  if ( !allowed() )
+    return -1;
+  
+  // <values> wurde nicht uebergeben? Dann Daten loeschen.
+  if ( !values ) 
+  {
+    m_delete(drying_data, room);
+    return 1;
+  }
+
+  // Ansonsten muessen 4 Integer-Werte als <values> uebergeben werden.
+  if ( sizeof(values) != 4 ) 
+    return -2;
+
+  if ( room[<2..<1] == ".c" )
+    room = room[..<3];
+
+  // Uebergebene Daten aendern direkt das Mapping der Trocknungsdaten.
+  m_add(drying_data, room, values...);
+  save();
+  return ([ room : drying_data[room,0]; drying_data[room,1]; 
+                   drying_data[room,2]; drying_data[room,3]]);
+}
+
+varargs mapping QueryDrying()
+{
+  return (allowed() ? drying_data : ([]) );
+}
+
+varargs int remove(int silent) 
+{
+  save();
+  return ::remove(silent);
+}
+
+/*
+#define DRYINGDATA "/secure/ARCH/kraeutertrocknungsdaten"
+
+private void ReadDryingData() 
+{
+  mixed data = explode(read_file(DRYINGDATA), "\n")-({""});
+  foreach(string line : data) 
+  {
+    if ( line[0] == '#' )
+      continue;
+    string *fields = explode(line,";");
+    fields[1..] = map(fields[1..], #'to_int);
+    m_add(tmp_drying_data, fields...);
+  }
+}*/
+
diff --git a/secure/lepmaster.c b/secure/lepmaster.c
new file mode 100644
index 0000000..3d7cb79
--- /dev/null
+++ b/secure/lepmaster.c
@@ -0,0 +1,408 @@
+// MorgenGrauen MUDlib
+//
+// lepmaster.c -- Master, welcher aus scoremaster, explorationmaster,
+//                questmaster und dem Spielerobjekt Informationen
+//                ausliest und aus diesen die Levelpunkte des Spieler
+//                berechnet
+//                Weiters derjenige, der die Seheranforderungen 
+//                prueft. 
+//
+// $Id: lepmaster.c 9168 2015-03-05 00:04:22Z Zesstra $
+
+// Hier kommen Funktionen fuer die Levelpunkte
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#include <config.h>
+#include <properties.h>
+#include <new_skills.h>
+#include <exploration.h>
+#include <questmaster.h>
+#include <scoremaster.h>
+#include <wizlevels.h>
+
+#include "lepmaster.h"
+
+// Das Programm zur Foerderung der kleinen Level aktivieren. ;-)
+#define __PFOERKL__ 1
+
+// die ersten 8 Level, d.h. Level 1 bis 8 entsprechen diesen LEPs.
+int *LOW_LEVEL_LEP_LIST = ({100,120,140,180,220,300,360,420}); // 9. Level: 500 LEP
+
+#define REQ_TEXT1  ([0:"unglaublich viel", \
+                  1:"unglaublich viel", \
+		  2:"enorm viel", \
+		  3:"sehr viel", \
+		  4:"viel", \
+		  5:"einiges", \
+		  6:"etwas", \
+		  7:"wenig", \
+                  8:"sehr wenig", \
+                  9:"kaum etwas" ])
+
+#define REQ_TEXT2  ([0:"unglaublich viele", \
+                  1:"unglaublich viele", \
+		  2:"enorm viele", \
+		  3:"sehr viele", \
+		  4:"viele", \
+		  5:"einige", \
+		  6:"ein paar", \
+		  7:"wenige", \
+                  8:"sehr wenige", \
+                  9:"kaum" ])
+
+#define DEBUG(x,y)
+//#define DEBUG(x,y) if (find_player("zook")) tell_object(find_player("zook"),sprintf(x,y))
+
+
+void create()
+{
+  seteuid(getuid());
+}
+
+int QueryLEPForPlayer(object pl)
+{
+  int ret, val, i, l;
+
+  if (extern_call() && !IS_ARCH(geteuid(previous_object())))
+    return -1;
+  if (!pl || !query_once_interactive(pl))
+    return -2;
+
+  //  Grundoffset 100, da man mit Level 1 statt 0 startet
+  ret = 100;
+
+  // Beitrag A: Stupse von NPC-Erstkills
+  ret += (int)SCOREMASTER->QueryKillPoints(pl);
+
+  DEBUG("Nach KP: ret = %d\n", ret);
+
+  // Beitrag B: Stupse von geloesten Miniquests
+  ret += (int)QM->QueryMiniQuestPoints(pl);
+
+  DEBUG("Nach MQP: ret = %d\n", ret);
+
+  // Beitrag C: Questpunkte
+  //  werden 1:1 uebernommen;
+  ret += (int)pl->QueryProp(P_QP);
+
+  DEBUG("Nach QP: ret = %d\n", ret);
+
+  // Beitrag D: Erfahrungspunkte
+  //  Stupse = XPs ^ 0.32
+  val = (int)pl->QueryProp(P_XP);
+
+  if (val<1) l=0;
+  else l=to_int(exp(log(to_float(val))*0.32));
+
+  ret += l;
+
+  DEBUG("Nach XP: ret = %d\n", ret);
+
+  // Beitrag E: Zaubertraenke
+  //  Gefundene Traenke geben 5 LEP
+  //  Die Heiltraenke geben zusaetzlich 10+20+30+40 LEP
+  i = 80 - (val = sizeof((int *)pl->QueryProp(P_POTIONROOMS)));
+
+  ret += 5*i + ([ 0: 100, 1: 60, 2: 30, 3: 10])[val];
+
+  // Beitrag F: Forscherpunkte
+  //  Pro FP gibt es 6 Stufenpunkte
+  ret += 6 * (int)EPMASTER->QueryExplorationPoints(pl);
+
+  DEBUG("Nach FP: ret = %d\n", ret);
+
+  // Beitrag G: Gildeneinstufung
+  //  Maximale Gildeneinstufung (10000) entspricht vier Leveln
+  ret += ((int)pl->QueryProp(P_GUILD_RATING))/25;
+
+  DEBUG("Nach GR: ret = %d\n", ret);
+
+  // Ausgabe gibt es nur in 20er-Schritten
+  ret -= ret%20;
+
+  return (ret > 100) ? ret : 100;
+
+}
+
+nomask int QueryLEP()
+{
+  if (!previous_object())
+    return 0;
+  return QueryLEPForPlayer(previous_object());
+}
+
+nomask int QueryLevel(int lep) {
+  if (lep<=0)
+    lep=QueryLEP();
+
+  if (lep<=0)
+    return 0;
+
+#ifdef __PFOERKL__
+  // normale Level alle 100 LEP
+  if (lep >= 500)
+    return lep/100 + 4;
+  else {
+    // level mit weniger LEP am Anfang (1-8)
+    // Level aus der LOW_LEVEL_LEP_LIST raussuchen.
+    int lev = sizeof(LOW_LEVEL_LEP_LIST) - 1;
+    for ( ; lev >= 0; --lev) {
+      if (LOW_LEVEL_LEP_LIST[lev] <= lep)
+        break;  // gefunden
+    }
+    return lev+1;
+  }
+#else
+  return lep/100;
+#endif // __PFOERKL__
+  return 0;
+}
+
+// Wieviele LEP fehlen zum naechsten Level?
+nomask int QueryNextLevelLEP(int lvl, int lep) {
+  int needed;
+
+  if (QueryLevel(lep) > lvl)
+    return 0;
+
+#ifdef __PFOERKL__
+  if (lvl < sizeof(LOW_LEVEL_LEP_LIST))
+    needed = LOW_LEVEL_LEP_LIST[lvl];
+  else
+    needed = (lvl-3) * 100; // (lvl + 1 - 4) * 100
+#else
+  needed = (lvl+1) * 100;
+#endif // __PFOERKL__
+
+  // needed sind jetzt die insgesamt benoetigten LEP. Vorhandene abziehen.
+  needed -= lep;
+  // nix < 0 zurueckliefern
+  return max(needed,0);
+}
+
+string QueryForschung()
+{
+  int max, my, avg;
+  string ret;
+
+  if ((my=(int)EPMASTER->QueryExplorationPoints(getuid(previous_object()))) < MIN_EP)
+    return "Du kennst Dich im "MUDNAME" so gut wie gar nicht aus.\n";
+
+  my *= 100;
+  max = my/(int)EPMASTER->QueryMaxEP();
+  avg = my/(int)EPMASTER->QueryAverage();
+
+  ret = "Verglichen mit Deinen Mitspielern, kennst Du Dich im "MUDNAME" ";
+  switch(avg) {
+  case 0..10:
+    ret += "kaum";
+    break;
+  case 11..40:
+    ret += "aeusserst schlecht";
+    break;
+  case 41..56:
+    ret += "sehr schlecht";
+    break;
+  case 57..72:
+    ret += "schlecht";
+    break;
+  case 73..93:
+    ret += "unterdurchschnittlich";
+    break;
+  case 94..109:
+    ret += "durchschnittlich gut";
+    break;
+  case 110..125:
+    ret += "besser als der Durchschnitt";
+    break;
+  case 126..145:
+    ret += "recht gut";
+    break;
+  case 146..170:
+    ret += "ziemlich gut";
+    break;
+  case 171..210:
+    ret += "gut";
+    break;
+  case 211..300:
+    ret += "sehr gut";
+    break;
+  case 301..400:
+    ret += "ausserordentlich gut";
+    break;
+  case 401..500:
+    ret += "unheimlich gut";
+    break;
+  default:
+    ret += "einfach hervorragend";
+    break;
+  }
+  ret += " aus.\nAbsolut gesehen ";
+
+  switch(max) {
+  case 0..5:
+    ret += "kennst Du nur wenig vom "MUDNAME".";
+    break;
+  case 6..10:
+    ret += "solltest Du Dich vielleicht noch genauer umsehen.";
+    break;
+  case 11..17:
+    ret += "bist Du durchaus schon herumgekommen.";
+    break;
+  case 18..25:
+    ret += "hast Du schon einiges gesehen.";
+    break;
+  case 26..35:
+    ret += "bist Du schon weit herumgekommen.";
+    break;
+  case 36..50:
+    ret += "koenntest Du eigentlich einen Reisefuehrer herausbringen.";
+    break;
+  case 51..75:
+    ret += "hast Du schon sehr viel gesehen.";
+    break;
+  default:
+    ret += "besitzt Du eine hervorragende Ortskenntnis.";
+  }
+  return break_string(ret, 78, 0, 1);
+}
+
+
+nomask mixed QueryWizardRequirements(object player)
+{
+  // Diese Funktion gibt ein 2-elementiges Array zurueck, in dem im ersten
+  // Element steht, ob der Spieler Seher werden kann (1) oder
+  // nicht (0) und im zweiten Element steht, was genau ihm noch 
+  // fehlt. 
+  // Fehlercode ist ({-1, ""})
+
+  // Die Umrechnungsfaktoren wurden einfach aus QueryLEP uebernommen; ggf.
+  // kann man das einmal in Unterfunktionen auslagern. 
+
+  mixed ret;
+  string s; 
+  int i,z,val; 
+
+  ret = ({-1, "Hier ist etwas schief gelaufen. Bitte einen Erzmagier\n"
+	    +"benachrichtigen.\n"});
+  s = "";
+  i = 0; 
+
+  if(!player || !objectp(player))
+    player=(this_player()?this_player():this_interactive());
+
+  if(!player)
+    return ({-1,""});
+
+  DEBUG("Es geht um: %O\n", player);
+
+  // Abenteuerpunkte
+  DEBUG("Abenteuerpunkte: %d ("+REQ_QP+")\n", player->QueryProp(P_QP));
+  if (player->QueryProp(P_QP) < REQ_QP) {
+    s += sprintf(" * Dir fehlen noch mindestens %d Abenteuerpunkte.\n", 
+		 REQ_QP - (int)player->QueryProp(P_QP));
+    i--;
+  }
+
+  // Forscherpunkte
+  z = 6 * (int)EPMASTER->QueryExplorationPoints(player);
+  DEBUG("Forscherpunkte: %d ("+REQ_EP+")\n", z);
+  if (z < REQ_EP) {
+    s += sprintf(" * Du kennst Dich im "MUDNAME" noch nicht genug aus, "
+		 +"genau genommen\n   musst Du Dir noch %s ansehen.\n", 
+		 REQ_TEXT1[(z*10/REQ_EP)] );
+    i--;
+  }
+
+  // Zaubertraenke
+  z = 80 - (val = sizeof((int*)player->QueryProp(P_POTIONROOMS)));
+  z = z*5 + ([0:100, 1:60, 2:30, 3:10])[val];
+  DEBUG("Zaubertraenke: %d ("+REQ_P+")\n", z);
+  if (z < REQ_P) {
+    s += sprintf(" * Du musst noch einige Zaubertraenke (ca. %d) suchen.\n",
+		 (REQ_P - z)/5 );
+    i--;
+  }
+
+  // Erstkills
+  z = (int)SCOREMASTER->QueryKillPoints(player);
+  DEBUG("Erstkills: %d ("+REQ_K+")\n", z);
+  if (z < REQ_K) {
+        s += sprintf(" * Du hast noch nicht genuegend wuerdige Gegner erlegt, genau "
+    	 +"genommen\n   musst Du noch %s wuerdige Monster toeten.\n",
+    	 REQ_TEXT2[(z*10/REQ_K)] );;
+    i--;
+  }
+
+  int minlevel = QueryLevel(REQ_LEP);
+
+  // Restliche Stufenpunkte 
+  DEBUG("Stufenpunkte: %d ("+REQ_LEP+")\n", player->QueryProp(P_LEP));
+  if ((int)(player->QueryProp(P_LEP)) < REQ_LEP) {
+    s += sprintf(" * Du musst mindestens %d Stufenpunkte, entspricht Stufe %d, "
+        "erreichen.\n", REQ_LEP, minlevel);
+    i--;
+  }
+  
+  // Demnach mindestens REQ/100-Level 
+  DEBUG("Level: %d ("+REQ_LEP/100+")\n", player->QueryProp(P_LEVEL));
+  if ((int)player->QueryProp(P_LEVEL) < minlevel) {
+    s += sprintf(" * Du musst mindestens Stufe %d erreichen.\n", minlevel);
+    i--;
+  }
+  
+  if(i<0) {
+    ret = ({-1, 
+	 sprintf("Du hast noch nicht alle Seher-Anforderungen erfuellt.\n"
+		+"Im einzelnen fehlt Dir folgendes:\n\n%s\n"
+		 +break_string("Falls Du Dir nun dennoch unsicher bist, "
+  +"welche Anforderungen Du erfuellen musst, dann "
+  +"schaue bei 'hilfe seher' und 'hilfe stufenpunkte' doch einfach noch "
+  +"einmal nach. Sind dann "
+  +"immer noch Dinge offen oder unklar, so sprich einfach einen "
+				  +"der Erzmagier an.", 78,0,1),s) });
+  } 
+    
+  if (i==0) {
+    ret = ({1, break_string(
+	       "Du hast alle Seher-Anforderungen erfuellt. Wende Dich doch "
+	       +"einmal an Merlin und frage ihn, ob er Dich befoerdert.", 
+	       78,0,1) });
+  }
+
+  return ret; 
+}
+
+nomask int QueryReadyForWiz(object player)
+{
+  mixed r;
+  
+  r = QueryWizardRequirements(player);
+  
+  if (!pointerp(r) && sizeof(r)!=2 && !intp(r[0]))
+    return -1;
+
+  return r[0];
+}
+
+nomask string QueryReadyForWizText(object player)
+{
+  mixed r;
+
+  r = QueryWizardRequirements(player);
+
+  if (!pointerp(r) && sizeof(r)!=2 && !stringp(r[1]))
+    return "Hier ist etwas schief gegangen, bitte verstaendige einen "
+      +"Erzmagier.";
+
+  return r[1];
+}
+
diff --git a/secure/lepmaster.h b/secure/lepmaster.h
new file mode 100644
index 0000000..31e7247
--- /dev/null
+++ b/secure/lepmaster.h
@@ -0,0 +1,22 @@
+// MorgenGrauen MUDlib
+//
+// lepmaster.h -- Definitionen fuer den lepmaster
+//
+// $Id: lepmaster.h,v 1.3 2006/02/22 08:57:48 Zook Exp $
+
+#ifndef __LEPMASTER_H__
+#define __LEPMASTER_H__
+
+/* Dateinamen */
+
+// to change:
+#define LEPMASTER   "/secure/lepmaster"
+
+// Anforderungen: 
+#define REQ_QP  1610     // Stupse aus Quests
+#define REQ_EP   600     // Stupse aus Forscherpunkten
+#define REQ_P    200     // Stupse aus Zaubertraenken
+#define REQ_K    150     // Stupse aus Erstkills
+#define REQ_LEP 3800     // Mindestens 3800 Stupse und Lvl. 38
+
+#endif
diff --git a/secure/login.c b/secure/login.c
new file mode 100644
index 0000000..ce14646
--- /dev/null
+++ b/secure/login.c
@@ -0,0 +1,1138 @@
+// MorgenGrauen MUDlib
+//
+// login.c -- Object for players just logging in
+//
+// $Id: login.c 9245 2015-06-04 13:04:39Z Arathorn $
+
+ /*
+ * secure/login.c
+ *
+ * This object is cloned for every user trying to log in
+ * We are still running root.
+ *
+ * login.c looks up the username in the secure/PASSWD file. If it is
+ * found, the password is checked. If the user is already logged in,
+ * he will be reconnected to the running object. If the other object
+ * is still interactive, that will be disconnected before the user is
+ * reconnected to that object.
+ *
+ * If the user is not in PASSWD, a new entry with level 0 is created.
+ * All PASSWD writing is done in secure/master.c.
+ *
+ */
+#pragma strict_types
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#include <config.h>
+#include <properties.h>
+#include <moving.h>
+#include "/secure/wizlevels.h"
+#include <telnet.h>
+#include <defines.h>
+#include <input_to.h>
+
+inherit "/secure/mini_props.c";
+inherit "/secure/telnetneg.c";
+
+#define SSL_GRRETING "REMOTE_HOST="
+#define PROXIES ({"127.0.0.1","87.79.24.60"})
+#define GUESTMASTER "/secure/guestmaster"
+
+#ifndef DEBUG
+#define DEBUG(x) if(find_player("tiamak")) tell_object(find_player("tiamak"),x)
+#define DEBUGM(x) if(find_player("muadib")) tell_object(find_player("muadib"),x)
+#endif
+
+/* Variables of the secure save file */
+int level, loginfails, creation_date;
+string password, name, shell, ep, ek, mq;
+string ektips;
+string fptips;
+string *domains, *guilds, *uidstotakecare;
+
+static int invis, neu;
+static string loginname;
+static string cap_name;
+static string *userentry;
+static string banish;
+static mixed *races;
+static int newbie;
+static string realip;
+
+// Prototypes
+static void SendTelopts();
+public nomask string loginname();
+// the following 4 lfuns deal with real logins
+public nomask int logon();
+static int logon2( string str );
+static int load_player_object( int guestflag );
+static void load_player_ob_2( string obname, int guestflag );
+static int check_illegal( string str );
+static int valid_name( string str );
+static int new_password( string str );
+static int again_password( string str );
+static int check_password( string str );
+static void select_race();
+static void ask_race_question();
+static void get_race_answer( string str );
+
+protected void create();
+public string short();
+public string query_real_name();
+public nomask int query_prevent_shadow();
+static void time_out();
+public int remove();
+// the following 3 lfuns deal with dummy player creation
+public mixed new_logon( string str);
+static mixed new_load_player_object();
+static mixed new_load_player_ob_2( string obname );
+static void ask_mud_played_question();
+static void get_mud_played_answer(string str);
+
+
+public nomask string loginname()
+{
+    return loginname ? loginname : "";
+}
+
+
+public int remove()
+{
+    destruct( this_object() );
+    return 1;
+}
+
+
+static int check_too_many_logons()
+{
+    object *u;
+    string ip;
+
+    ip = query_ip_number(this_object());
+    // users() nehmen, falls nicht-interaktive Clones von login.c existieren.
+    u = filter( users(), function status (object ob, string addr) {
+        return object_name(ob) == "/secure/login"
+               && query_ip_number(ob) == addr;
+    }, ip );
+
+    if ( sizeof(u) > 5 ){
+        write( "\nEs laufen schon zu viele Anmeldungen von Deiner Adresse "
+               "aus.\nProbier es bitte in ein bis zwei Minuten noch "
+               "einmal.\n" );
+
+        log_file( "LOGIN_DENY", sprintf( "%s: >5 Logons von %-15s (%s)\n",
+                                         ctime(time())[4..15],
+                                         query_ip_number(this_object()),
+                                         query_ip_name(this_object()) ) );
+        return 1;
+    }
+    else
+        return 0;
+}
+
+
+/*
+ * This is the function that gets called by /secure/master for every user
+ */
+public nomask int logon()
+{
+    loginname = "logon";
+    newbie=0;
+    realip="";
+
+    // als erstes wird ein Lookup gemacht, ob die Quelladresse ein
+    // Tor-Exitnode ist, der erlaubt, zu uns zu kommunizieren. Das Lookup ist
+    // asynchron und braucht eine Weile, wenn das Ergebnis noch nicht gecacht
+    // ist. An dieser Stelle wird das Ergebnis nicht ausgewertet. Achja, wie
+    // machen das natuerlich nicht fuer die IP vom Mudrechner...
+    if (query_ip_number(this_object()) != "87.79.24.60")
+    {
+      "/p/daemon/dnslookup"->check_tor(query_ip_number(this_object()),query_mud_port());
+      "/p/daemon/dnslookup"->check_dnsbl(query_ip_number(this_object()));
+    }
+    printf("HTTP/1.0 302 Found\n"
+         "Location: http://mg.mud.de/\n\n"
+         "NetCologne, Koeln, Germany. Local time: %s\n\n"
+         MUDNAME" LDmud, NATIVE mode, driver version %s\n\n",
+         strftime("%c"), __VERSION__);
+
+    SendTelopts();
+
+    if ( check_too_many_logons() ){
+        destruct(this_object());
+        return 0;
+    }
+
+    // ist die Verbindung schon wieder weg?
+    if (objectp(this_object()) && interactive(this_object())) {
+      cat( "/etc/WELCOME" );
+    }
+
+    input_to( "logon2", INPUT_PROMPT,
+        "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
+    call_out( "time_out", 300 );
+    return 1;
+}
+
+
+static int check_too_many_from_same_ip()
+{
+    object *u;
+    string ip;
+
+    ip = query_ip_number(this_object());
+    u = filter(users(), function status (object ob, string addr, int a) {
+        return query_ip_number(ob) == addr
+               && ob->QueryProp(P_AGE) < a;
+    }, ip, 12*60*60); // 24h in heart_beats
+
+    if ( sizeof(u) > 25 ){
+        write( "\nDa anscheinend gerade jemand von Deiner Adresse aus "
+               "versucht, das \n"MUDNAME" mit neuen Charakteren zu "
+               "ueberschwemmen, werden momentan \nnur aeltere Charaktere "
+               "von dieser Adresse zugelassen.\nWenn Du meinst, dass es "
+               "sich um einen Fehler handelt, logg Dich bitte als \n"
+               "Gast ein und sprich einen Erzmagier oder Gott an.\n" );
+        
+        log_file( "LOGIN_DENY", sprintf( "%s: >10 Spieler von %-15s (%s)\n",
+                                         ctime(time())[4..15],
+                                         query_ip_number(this_object()),
+                                         query_ip_name(this_object()) ) );
+        
+        destruct(this_object());
+        return 1;
+    }
+    else
+        return 0;
+}
+
+
+static int check_illegal( string str )
+{
+    string res;
+
+    res = (string)master()->QuerySBanished(query_ip_number(this_object()));
+    if (!res)
+    {
+      // check connection from Tor exit node
+      string eff_ip = (realip!="" ? realip : query_ip_number(this_object()));
+      if ("/p/daemon/dnslookup"->check_tor(eff_ip, query_mud_port())
+          || "/p/daemon/dnslookup"->check_dnsbl(eff_ip))
+        res = 
+            "\nSorry, von Deiner Adresse kamen ein paar Idioten, die "
+            "ausschliesslich\nAerger machen wollten. Deshalb haben wir "
+            "die Moeglichkeit,\neinfach neue Charaktere "
+            "anzulegen, fuer diese Adresse gesperrt.\n\n"
+            "Falls Du bei uns spielen moechtest, schicke bitte eine Email "
+            "an\n\n                         mud@mg.mud.de\n\n"
+            "mit der Bitte, einen Charakter fuer Dich anzulegen.\n" ;
+    }
+
+    if ( res )
+    {
+        write( res );
+        log_file( "LOGIN_DENY", sprintf( "%s: %-11s %-15s (%s)\n",
+                                         ctime(time())[4..15], str,
+                                         query_ip_number(this_object()),
+                                         query_ip_name(this_object()) ) );
+        remove();
+        return 1;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Check that a player name is valid. Only allow
+ * lowercase letters.
+ */
+static int valid_name( string str )
+{
+    int i;
+
+    if ( str == "logon" ){
+        write( "Der Name wuerde nur Verwirrung stiften.\n" );
+        return 0;
+    }
+
+    i = sizeof(str);
+
+    if ( i > 11 ){
+        write( "Dein Name ist zu lang, nimm bitte einen anderen.\n" );
+        return 0;
+    }
+
+    for ( ; i--; )
+        if ( str[i] < 'a' || str[i] > 'z' ) {
+            write( "Unerlaubtes Zeichen '" + str[i..i] + "' im Namen: " + str
+                            + "\n" );
+            write( "Benutze bitte nur Buchstaben ohne Umlaute.\n" );
+            return 0;
+        }
+
+    return 1;
+}
+
+
+static int logon2( string str )
+{
+    int i, arg;
+    mixed txt;
+
+    if ( !str || str == "" ){
+        write( "Abbruch!\n" );
+        destruct( this_object() );
+        return 0;
+    }
+
+    // Unterstuetzung fuer das Mud Server Status Protocol
+    // (http://tintin.sourceforge.net/mssp/)
+#ifdef MSSP_SUPPORT
+    if (str == "MSSP-REQUEST") {
+      "/secure/misc/mssp"->print_mssp_response();
+      log_file( "MSSP.log", sprintf( "%s: %-15s (%s)\n",
+                                         strftime("%c"),
+                                         query_ip_number(this_object()),
+                                         query_ip_name(this_object())||"N/A" ) );
+      input_to("logon2", INPUT_PROMPT,
+          "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
+      return 1;
+    }
+#endif
+
+    if(strstr(str,SSL_GRRETING)==0)
+    {
+      if( member(PROXIES,query_ip_number(this_object()))>-1 )
+      {
+        realip=str[sizeof(SSL_GRRETING)..];
+      } // andere IPs werden einfach ignoriert. -> log/PROXY.REQ ?
+      // ggf. Lookup fuer Torexits anstossen.
+      "/p/daemon/dnslookup"->check_tor(realip,query_mud_port());
+      "/p/daemon/dnslookup"->check_dnsbl(realip);
+
+      input_to( "logon2", INPUT_PROMPT,
+          "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
+      return 1;
+    }
+    
+    if ( loginname != "logon" ) {
+        log_file( "ILLEGAL", sprintf( "%s Illegal patch of login: "
+                                      "loginname = %O\n",
+                                      dtime(time()), loginname ) );
+        destruct( this_object() );
+        return 0;
+    }
+
+    str = lower_case(str);
+    cap_name = capitalize(str);
+
+    if ( str == "neu" && !neu ){
+        cat( "/etc/WELCOME_NEW" );
+        neu = 1;
+        input_to( "logon2", INPUT_PROMPT, "Name: ");
+        return 1;
+    }
+
+    if ( !valid_name(str) ){
+        string pr;
+        if ( !neu )
+            pr= "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ";
+        else
+            pr= "Bitte gib Dir einen anderen Namen: ";
+
+        input_to( "logon2", INPUT_PROMPT, pr );
+        return 1;
+    }
+
+    if ( sscanf( str, "gast%d", arg ) == 1 ){
+        write( "Du meinst wohl 'Gast' ...\n" );
+        str = "gast";
+    }
+
+    loginname = str;
+
+    /* read the secure save file to see if character already exists */
+    if ( loginname != "gast" &&
+         !restore_object( master()->secure_savefile(loginname) ) ){
+        object *user;
+
+        if ( !neu )
+        {
+            write( "Es existiert kein Charakter mit diesem Namen.\n" );
+            write( "Falls Du einen neuen Charakter erschaffen moechtest, "
+                   "tippe bitte \"neu\" ein.\n" );
+
+            loginname = "logon";
+            input_to( "logon2", INPUT_PROMPT,
+                "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
+            return 1;
+        }
+
+        for ( i = sizeof(user = users() - ({ 0, this_object() })); i--; )
+            if ( object_name(user[i])[0..12] == "/secure/login" &&
+                 ((string)user[i]->loginname()) == loginname ){
+                write( "Eine Anmeldung fuer diesen Namen laeuft bereits.\n" );
+                destruct( this_object() );
+                return 1;
+            }
+
+        // Site-Banish checken
+        if ( check_illegal(loginname))
+            return 1;
+
+        if ( check_too_many_from_same_ip() )
+            return 1;
+
+        /* new character */
+        if ( sizeof(loginname) < 3 ){
+            write( "Der Name ist zu kurz.\n" );
+            loginname = "logon";
+            input_to( "logon2", INPUT_PROMPT,
+                "Versuch einen anderen Namen: ");
+            return 1;
+        }
+
+        
+        if ( (txt = (string)master()->QueryBanished(loginname)) ){
+            if ( txt != "Dieser Name ist gesperrt." )
+                txt = sprintf("Hoppla - dieser Name ist reserviert oder gesperrt "
+                    "(\"gebanisht\")!\nGrund: %s\n",txt);
+            else
+                txt = "Hoppla - dieser Name ist reserviert oder gesperrt "
+                    "(\"gebanisht\")!\n";
+            write(txt);
+            loginname = "logon";
+            input_to( "logon2", INPUT_PROMPT,
+                "Bitte gib Dir einen anderen Namen: ");
+            return 1;
+        }
+
+        /* Initialize the new secure savefile */
+        name = loginname;
+        password = "";
+        level = 0;
+        domains = ({ });
+        guilds = ({ }); 
+        shell = "";
+        ep = "";
+        ek = "";
+        mq = "";
+        ektips="";
+        fptips="";
+        creation_date = time();
+
+        input_to( "new_password", INPUT_NOECHO|INPUT_PROMPT,
+            "Waehle ein Passwort: ");
+        return 1;
+    }
+    else {
+        if ( loginname == "gast" ){
+            if ( check_illegal(loginname) )
+                return 1;
+
+            load_player_object(1);
+            return 1;
+        }
+
+        if ( neu ){
+            write( "Es existiert bereits ein Charakter dieses Namens.\n" );
+            loginname = "logon";
+            input_to( "logon2", INPUT_PROMPT,
+                "Gib Dir einen anderen Namen: ");
+            return 1;
+        }
+
+        if ( (int)master()->check_late_player(loginname) )
+        {
+            write( "Dieser Spieler hat uns leider fuer immer verlassen.\n" );
+            loginname = "logon";
+            input_to( "logon2", INPUT_PROMPT,
+                "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
+            return 1;
+        }
+
+        if ( txt = (string)master()->QueryTBanished(loginname) ){
+            write( txt );
+            loginname = "logon";
+            input_to( "logon2", INPUT_PROMPT,
+                "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
+            return 1;
+        }
+
+        if ( creation_date > (time() - 30*24*60*60)
+             && check_too_many_from_same_ip() )
+            return 1;
+
+        write( "Schoen, dass Du wieder da bist, "+capitalize(loginname)+"!\n" );
+
+        if ( !stringp(password) || password == "" ) {
+            write( "Du hast KEIN PASSWORD!\n" );
+            write( "Benutze den \"password\"-Befehl, um das zu aendern !\n" );
+            load_player_object(0);
+            return 1;
+        }
+
+        input_to( "check_password", INPUT_NOECHO|INPUT_PROMPT,
+            "Passwort: ");
+        return 1;
+    }
+}
+
+
+static int new_password( string str )
+{
+    write( "\n" );
+
+    if ( !str || str == "" )
+        return remove();
+
+    password = str;
+
+    if ( !master()->good_password( str, loginname ) ) {
+        input_to( "new_password", INPUT_NOECHO|INPUT_PROMPT,
+            "Bitte gib ein Passwort an: ");
+        return 1;
+    }
+
+    write( "\nZur Erinnerung: Es ist  v e r b o t e n, andere Spieler "
+           "anzugreifen!\n" );
+    write( "Das gilt auch fuer Froesche, bei denen \"Ein Frosch namens "
+           "XXXXX\" steht.\n\n" );
+    input_to( "again_password", INPUT_NOECHO|INPUT_PROMPT,
+        "Passwort bitte nochmal eingeben: ");
+    return 1;
+}
+
+static int again_password( string str )
+{
+    write( "\n" );
+
+    if ( str != password ){
+        write( "Die Passwoerter stimmten nicht ueberein!\n" );
+        destruct( this_object() );
+        return 1;
+    }
+
+    while ( remove_call_out( "time_out" ) != -1 )
+        ;
+    call_out( "time_out", 600 );
+
+    password = md5_crypt( password, 0 );
+    save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
+    master()->RemoveFromCache( loginname );
+
+    load_player_object(0);
+    return 1;
+}
+
+static int check_password( string str )
+{
+    write( "\n" );
+
+    // Invis einloggen?
+    if (sizeof(str) > 1 && str[0] == '-') {
+        invis = 1;
+        str = str[1..];
+    }
+
+    // welcher Hash ists denn?
+    if (sizeof(password) > 13) {
+        // MD5-Hash
+        str = md5_crypt(str, password);
+    }
+    else if (sizeof(password) > 2) {
+        // Crypt-Hash
+        str = crypt(str, password[0..1]);
+    }
+    else {
+        // keiner von beiden Hashes -> ungueltiges PW
+        str = 0;
+    }
+
+    if ( !stringp(str) || str != password ) {
+      // Hashes stimmen nicht ueberein -> und schluss...
+      write( "Falsches Passwort!\n");
+      if ( loginfails > 2 )
+        write(break_string(
+          "Solltest Du weiterhin Probleme mit dem Einloggen haben, kannst "
+          "Du Dein Passwort zuruecksetzen lassen, indem Du Dich als Gast "
+          "anmeldest und einen Erzmagier ansprichst, oder indem Du Dich "
+          "mittels einer E-Mail an mud@md.mud.de mit uns in Verbindung "
+          "setzt.",78));
+    
+      log_file( (level < 60 ? "LOGINFAIL" : "ARCH/LOGINFAIL"),
+        sprintf( "PASSWORD: %-11s %s, %-15s (%s)\n",           
+                  loginname, ctime(time())[4..15],
+                  query_ip_number(this_object()),           
+                  query_ip_name(this_object()) ), 200000 );
+      
+      loginfails++;
+      save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
+      master()->RemoveFromCache( loginname );
+      destruct( this_object() );
+      return 1;
+    }
+
+    if ( loginfails ) {
+        write( loginfails + " fehlgeschlagene" + (loginfails == 1 ? "r" : "") +
+               " Login-Versuch" + (loginfails == 1 ? "" : "e") +
+               " seit dem letzten erfolgreichen Login.\n" );
+        loginfails = 0;
+    }
+
+    save_object( SECURESAVEPATH + loginname[0..0] + "/" + loginname );
+    master()->RemoveFromCache( loginname );
+
+    load_player_object(0);
+    return 1;
+}
+
+
+static void select_race()
+{
+    int i;
+    string s;
+
+    races = get_dir( "/std/shells/*.c" );
+
+    // Mensch soll immer als erstes in der Auswahlliste stehen.
+    if (member(races,"human.c")!=-1)
+            races=({"human.c"})+(races-({"human.c"}));
+    
+    for ( i = sizeof(races); i--; ){
+        races[i] = "/std/shells/" + races[i][0..<3];
+        s = 0;
+
+        if ( catch(s = (string)call_other( races[i], "QueryAllowSelect" ); publish) 
+            || !s)
+            s = 0;
+        else if ( catch(s = (string)call_other( races[i], "QueryProp", P_RACE );publish) )
+            s = 0;
+
+        if ( !sizeof(s) )
+            races[i..i] = ({});
+        else
+            races[i] = ({ races[i], s });
+    }
+
+    if ( sizeof(races) == 1 ){
+        write( "Es gibt nur eine Rasse, Du hast also keine Wahl.\n" );
+
+        shell = races[0][0];
+        master()->set_player_object( loginname, shell );
+
+        return load_player_ob_2( shell, 0 );
+    }
+
+    return ask_mud_played_question();
+}
+
+static void ask_mud_played_question()
+{
+        write(break_string(
+                "\nWenn Du ein absoluter Neuling in diesem Spiel bist moechten "
+                "wir Dir mit einigen Tips zu Beginn beiseite stehen.\n\n",78,
+                0,BS_LEAVE_MY_LFS));
+        input_to( "get_mud_played_answer", INPUT_PROMPT,
+            "Hast Du schon einmal in einem MUD gespielt? (ja,nein): ");
+        return;
+}
+
+static void ask_race_question()
+{
+    int i, j;
+
+    write( break_string( "Du musst Dich jetzt entscheiden, welcher Rasse Du "
+                         "in dieser Welt angehoeren moechtest. Alle Rassen "
+                         "haben verschiedene Vor- und Nachteile, insgesamt "
+                         "aber gleich gute Chancen. Auch das Startgebiet "
+                         "haengt von der ausgewaehlten Rasse ab. Im "
+                         "Normalfall kann die Rasse nicht mehr gewechselt "
+                         "werden, nachdem sie einmal ausgewaehlt wurde. "
+                         "Ueberlege Dir Deine Entscheidung also gut. Derzeit "
+                         "stehen folgende Rassen zur Auswahl:\n\n", 78 ) );
+
+    for ( i = 0, j = sizeof(races); i < j; i++ )
+        printf( "% 2d. %-30s   %s", i+1, capitalize(races[i][1]),
+                (i % 2 ? "\n" : "| ") );
+
+    if ( sizeof(races) % 2 )
+        write( "\n" );
+
+    write( break_string( "\nDurch Eingabe einer Ziffer waehlst Du die Rasse "
+                         "aus, durch Eingabe eines \"\?\" gefolgt von einer "
+                         "Ziffer erhaeltst Du naehere Informationen ueber "
+                         "eine Rasse. Ein \"\?\" allein wiederholt diese "
+                         "Liste.", 78, 0, 1 ) );
+
+    if (newbie)
+    {
+            write(break_string("\nAls Neuling solltest Du Dich NICHT fuer "
+                                    "die Dunkelelfen entscheiden. Diese "
+                                    "Rasse hat einige Probleme im Umgang "
+                                    "mit den anderen Rassen und mit dem "
+                                    "Sonnenlicht.",78,0,BS_LEAVE_MY_LFS));
+    }
+
+    input_to( "get_race_answer", INPUT_PROMPT,
+        "\nWas willst Du tun: ");
+    return;
+}
+
+
+static void get_race_answer( string str )
+{
+    int num;
+
+    if ( str == "?" )
+        return ask_race_question();
+
+    if ( sscanf( str, "?%d", num ) ){
+        if ( num < 1 || num > sizeof(races) ){
+            write( "Das geht nicht.\n\n");
+            input_to( "get_race_answer", INPUT_PROMPT,
+                "Was willst Du tun: ");
+            return;
+        }
+
+        write( call_other( races[num - 1][0], "QueryProp", P_RACE_DESCRIPTION ));
+        input_to( "get_race_answer", INPUT_PROMPT,
+            "\nWas willst Du tun: ");
+        return;
+    }
+
+    if ( sscanf( str, "%d", num ) && num >= 1 && num <= sizeof(races) ){
+        write( "Ok, Du bist jetzt ein "
+            + capitalize(races[num-1][1]) + ".\n" );
+
+        shell = races[num-1][0];
+        master()->set_player_object( loginname, shell );
+        return load_player_ob_2( shell, 0 );
+    }
+
+    write("Wie bitte?\n\n" );
+    input_to( "get_race_answer", INPUT_PROMPT,
+        "Was willst Du tun: ");
+}
+
+static void get_mud_played_answer (string str)
+{
+        if ( str == "ja" || str=="j")
+                {
+                newbie=0;
+                return ask_race_question();
+                }
+        if ( str != "nein" && str!="n")
+        {
+                write("\n\nAntworte bitte mit ja oder nein.\n\n");
+                                
+                return ask_mud_played_question();
+        }
+        newbie=1;
+        write("\n\nEine kleine Einfuehrung in das "MUDNAME" bekommst "
+                        "Du auch hier:\n\n"
+                        "http://mg.mud.de/newweb/hilfe/tutorial/inhalt.shtml\n\n");
+        return ask_race_question();
+        
+}
+
+static int load_player_object( int guestflag )
+{
+    object ob;
+    string fname;
+    int was_interactive;
+
+    if ( sizeof(users()) >= 195 && !IS_WIZARD(loginname) ){
+        write( "Die maximale Spielerzahl wurde bereits erreicht!!!\n"
+               "Aus technischen Gruenden duerfen sich nur noch Magier "
+               "einloggen.\nVersuch es spaeter noch einmal ...\n" );
+        destruct( this_object() );
+        return 1;
+    }
+    else if ( sizeof(users()) >= 198 && !IS_ARCH(loginname) ){
+        write( "Die maximale Spieler- und Magierzahl wurde bereits erreicht!!!"
+               "\nAus technischen Gruenden duerfen sich nur noch Erzmagier "
+               "einloggen.\nVersuch es spaeter noch einmal ...\n" );
+        destruct( this_object() );
+        return 1;
+    }
+
+    if ( file_size("/etc/NOLOGIN")>=0 )
+    {
+      if (file_size("/etc/NOLOGIN.info")>0) {
+        //NOLOGIN.info enthaelt evtl. weitergehende Informationen fuer 
+        //Spieler, z.B. vorrauss. Wiederverfuegbarkeit.
+        write(break_string(read_file("/etc/NOLOGIN.info"),78,"",
+              BS_LEAVE_MY_LFS|BS_SINGLE_SPACE));
+      }
+      else {
+        //sonst Standardmeldung ausgeben.
+        write ("\nAufgrund von technischen Problemen ist das Einloggen ins "
+                MUDNAME" zur \nZeit nicht moeglich. Bitte versuch es "
+                      "spaeter noch einmal.\n\n");
+      }
+      if ( IS_ARCH(loginname) || 
+           member(explode(read_file("/etc/NOLOGIN")||"","\n"),
+                  loginname)!=-1 )
+      {
+        write("Im Moment koennen nur Erzmagier einloggen. Um Spieler "
+              "wieder ins Spiel zu lassen, muss die Datei '/etc/NOLOGIN' "
+              "geloescht werden.\n\n ");
+      } else {
+        destruct( this_object() );
+        return 1;                        
+      }
+    }
+
+    if ( guestflag ){
+        if ( catch(guestflag = (int)GUESTMASTER->new_guest();publish) 
+             || !guestflag ){
+            write( "Derzeit ist kein Gastlogin moeglich!\n" );
+            destruct( this_object() );
+            return 1;
+        }
+
+        loginname = "gast" + guestflag;
+        cap_name = capitalize(loginname);
+        name = cap_name;
+
+        if ( !(ob = find_player(loginname) || find_netdead(loginname)) ){
+            object *user;
+            int i;
+
+            // gegen Horden von Gast1 - wenn ein Gast noch am Prompt fuer
+            // das Geschlecht haengt, ist er ueber find_player() noch nicht
+            // zu finden ...
+            for ( i = sizeof(user = users() - ({ 0, this_object() })); i--; )
+                if ( object_name(user[i])[0..11] == "/std/shells/" &&
+                     getuid(user[i]) == loginname ){
+                    ob = user[i];
+                    break;
+                }
+        }
+
+        if ( ob ){
+            tell_object( ob, "Ein anderer Spieler moechte diesen Gastzugang "
+                         "jetzt benutzen. Wenn es Dir hier\ngefallen hat, "
+                         "ueberleg Dir doch einen Charakternamen und komm "
+                         "unter diesem\nNamen wieder!\n" );
+            destruct(ob);
+        }
+
+        load_player_ob_2( "std/shells/human", guestflag );
+
+        return 1;
+    }
+    else {
+        /* Test if we are already playing */
+        was_interactive = 0;
+        ob = find_player(loginname) || find_netdead(loginname);
+
+        if (ob) {
+            write( "Du nimmst schon am Spiel teil!\n" );
+            write( "Verwende Deine alte sterbliche Huelle ...\n" );
+
+            if ( interactive(ob) )
+            {
+                /* The other object is still interactive; reconnect that "soul"
+                   to a dummy object and destruct that, thus disconnecting the
+                   other probably linkdead user. The real "body" is still
+                   there for reconnecting by login.c */
+                remove_interactive(ob);
+                was_interactive = 1;
+            }
+            // Wenn Invislogin, P_INVIS setzen.
+            if ( invis && IS_WIZARD(ob) )
+            {
+                ob->SetProp( P_INVIS, ob->QueryProp(P_AGE) );
+                tell_object( ob, "DU BIST UNSICHTBAR!\n" );
+            }
+            /* Now reconnect to the old body */
+            exec( ob, this_object() );
+            ob->set_realip(realip);
+            if ( ((int)ob->QueryProp(P_LEVEL)) == -1 )
+                ob->start_player( cap_name );
+            else
+                ob->Reconnect( was_interactive );
+
+            call_out( "remove", 2 );
+            return 1;
+        }
+    }
+
+    /* read player object from passwd file */
+    if ( stringp(shell) && shell != "" )
+        load_player_ob_2 ( shell, 0 );
+    else
+        select_race();
+
+    return 1;
+}
+
+
+static void load_player_ob_2( string obname, int guestflag )
+{
+    object blueprint;
+    string err, ob_name;
+    object ob, old_ob;
+
+    if (!interactive()) {
+      destruct(this_object());
+      return;
+    }
+    /* start player activity */
+    log_file( "ENTER", sprintf( "%-11s %s, %-15s (%s).\n",
+                                capitalize(name), ctime(time())[4..15],
+                                query_ip_number(this_object()),
+                                query_ip_name(this_object()) ), 200000 );
+
+    seteuid(loginname);
+
+    /* load the "real" player object */
+    /* If some asshole has moved the blueprint */
+    if ( objectp(blueprint = find_object(obname))  && environment(blueprint) )
+        destruct(blueprint);
+
+    if ( err = catch(ob = clone_object(obname);publish) ){
+        log_file( "SHELLS", "Failed to load shell " + obname + ", " +
+                  dtime(time()) + ", " + loginname + "\n" + err + "\n\n" );
+
+        write( "Konnte das passende Playerobjekt nicht laden. Lade "
+               "stattdessen\ndas Objekt Mensch. BITTE ERZMAGIER "
+               "BENACHRICHTIGEN !\n" );
+        err = catch(ob = clone_object("/std/shells/human");publish);
+    }
+
+    if ( !ob || err ) {
+        write( "Error on loading " + shell + "\nError = " + err + "\n" );
+        destruct( this_object() );
+        return;
+    }
+
+    if ( guestflag )
+        catch( GUESTMASTER->set_guest( guestflag, ob );publish );
+
+    ob_name = explode( object_name(ob), "#" )[0];
+
+    if ( sizeof(ob_name) > 11 && ob_name[0..11] == "/std/shells/" )
+        ob_name = ob_name[11..];
+
+    ob_name = ob_name + ":" + loginname;
+
+  if( !guestflag )
+  {
+      if ( old_ob = find_object(ob_name) )
+      {
+          catch(old_ob->remove();publish);
+
+          if ( old_ob )
+              destruct( old_ob );
+      }
+      rename_object( ob, ob_name );
+      ob->__reload_explore();
+  }
+  exec( ob, this_object() );
+  ob->set_realip(realip);
+  ob->start_player( cap_name );
+  //Hinweis: Das Spielerobjekt holt sich in updates_after_restore() von hier
+  //den Status von invis und setzt ggf. P_INVIS
+
+  // TODO: Prop rauswerfen und in Spielern SAVE entfernen, wenn die folgenden
+  // Verwendungen entsorgt sind:
+  // /d/wueste/hirudo/goldstrand/rooms/gefaengnis.c
+  // /d/inseln/tilly/feuerinsel/obj/formular.c
+  // /d/polar/files.chaos/tilly/obj/dose_obj.c
+  ob->SetProp( "creation_date", creation_date );
+  ob->Set( "creation_date", SAVE|SECURED|PROTECTED, F_MODE_AS );
+  // wenn der Spieler noch nicht im Mud gespielt hat, wird die aktuelle Zeit
+  // in die entsprechende Prop geschrieben. Die Prop ist transient und wird
+  // absichtlich nicht gespeichert.
+  if (newbie)
+    ob->SetProp("_lib_mud_newbie", creation_date);
+
+  destruct( this_object() );
+}
+
+
+/*
+ *   With arg = 0 this function should only be entered once!
+ */
+protected void create()
+{
+    loginname = "logon";
+    creation_date = -1;
+    catch( load_object( "/secure/merlin");publish );
+    loginfails = 0;
+    realip="";
+    if (clonep())
+      set_next_reset(900);
+    else
+      set_next_reset(-1);
+}
+
+void reset()
+{
+  if (clonep())
+    remove();
+}
+
+public string short()
+{
+    return "<Einloggender Teilnehmer>.\n";
+}
+
+
+public string query_real_name()
+{
+    return "<logon>";
+}
+
+
+public nomask int query_prevent_shadow()
+{
+    return 1;
+}
+
+
+static void time_out()
+{
+    if (this_player())
+      tell_object(this_player(),"Time out!");
+    destruct( this_object() );
+}
+
+
+// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
+// im Loginprozess involviert.
+public mixed new_logon( string str)
+{
+    seteuid(getuid()); // sonst funkt ARCH_SECURITY nicht
+
+    if ( !ARCH_SECURITY || process_call() ){
+        write( "Nur fuer Erzmagier erlaubt!\n" );
+        destruct( this_object() );
+        return -1;
+    }
+
+    if ( !str || str == "" ){
+        write( "Kein Name angegeben!\n" );
+        destruct( this_object() );
+        return 0;
+    }
+
+    str = lower_case(str);
+    cap_name = capitalize(str);
+
+    loginname = str;
+    seteuid(ROOTID);
+
+    /* read the secure save file to see if character already exists */
+    if ( !restore_object( master()->secure_savefile(loginname) ) ){
+        write( "Kein solcher Spieler!\n" );
+        destruct( this_object() );
+        return 0;
+    }
+    else {
+        write( "Ok, der Spieler " + capitalize(str) + " existiert!\n" );
+        return new_load_player_object();
+    }
+}
+
+// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
+// im Loginprozess involviert.
+static mixed new_load_player_object()
+{
+    if ( find_player(loginname) || find_netdead(loginname) ){
+        write( "Der Spieler ist bereits online oder netztot!\n" );
+        destruct( this_object() );
+        return 2;
+    }
+
+    /* read player object from passwd file */
+    if ( stringp(shell) && shell != "" )
+        return new_load_player_ob_2( shell );
+    else {
+        write( "Keine Shell angegeben!\n" );
+        destruct( this_object() );
+        return 0;
+    }
+}
+
+// Wird von simul_efuns benutzt, um nen dummy-Spielerobjekt zu erzeugen. Nicht
+// im Loginprozess involviert.
+static mixed new_load_player_ob_2( string obname )
+{
+    object blueprint;
+    string err, ob_name;
+    object ob, old_ob;
+
+    seteuid(loginname);
+
+    /* load the "real" player object */
+    /* If some asshole has moved the blueprint */
+    if ( objectp(blueprint = find_object(obname)) && environment(blueprint) )
+        destruct( blueprint );
+
+    err = catch(ob = clone_object(obname);publish);
+
+    if ( err ){
+        log_file( "SHELLS", "Failed to load shell " + obname + ", " +
+                  dtime(time()) + ", " + loginname + "\n" + err + "\n\n" );
+
+        write( "Konnte das passende Playerobjekt nicht laden. "
+               "Lade stattdessen\ndas Objekt Mensch!\n" );
+
+        err = catch(ob = clone_object( "std/shells/human" );publish );
+    }
+
+    if ( !ob || err ){
+        write( "Error on loading " + shell + "\nError = " + err + "\n" );
+        destruct( this_object() );
+        return 0;
+    }
+
+    ob_name = explode( object_name(ob), "#" )[0];
+
+    if ( sizeof(ob_name) > 11 && ob_name[0..11] == "/std/shells/" )
+        ob_name = ob_name[11..];
+
+    ob_name = ob_name + ":" + loginname;
+
+    if ( old_ob = find_object(ob_name) ){
+        catch( old_ob->remove(); publish );
+
+        if ( old_ob )
+            destruct( old_ob );
+    }
+
+    rename_object( ob, ob_name );
+    ob->__reload_explore();
+    ob->set_realip(realip);
+    ob->start_player(cap_name);
+    ob->SetProp( "creation_date", creation_date );
+    ob->Set( "creation_date", SAVE|SECURED|PROTECTED, F_MODE_AS );
+
+    ob->move( "/room/nowhere", M_NOCHECK );
+    set_object_heart_beat( ob, 0 );
+    destruct( this_object() );
+
+    return ob;
+}
+
+string query_realip()
+{
+  return realip ? realip : "";
+}
+
+int query_invis()
+{
+  return invis;
+}
+
diff --git a/secure/mailer.c b/secure/mailer.c
new file mode 100644
index 0000000..9901fac
--- /dev/null
+++ b/secure/mailer.c
@@ -0,0 +1,632 @@
+// MorgenGrauen MUDlib
+//
+// mailer.c
+//
+// $Id: mailer.c 9547 2016-04-17 19:27:47Z Zesstra $
+
+/*
+ *------------------------------------------------------------
+ * The mail demon. Receives mail from users and delivers it into
+ * the mail directory.
+ *
+ * Deepthought, Nightfall, 25-May-92
+ * Remove-Functions : Jof, 29-June-92
+ * Caching, DeleteUnreadFolder, small changes: Loco, 08-Feb-97
+ * General clean-up and speed-up: Tiamak, 18-Jan-2000
+ *   DON'T USE restore_object any more, use GetFolders instead!
+ *------------------------------------------------------------
+ *
+ *     Save file format (sort of formal notation):
+ *
+ *     mixed *folders = ({
+ *        ({ string name1; string name2; ... string nameN; })
+ *        ({ mixed *msgs1; mixed *msgs2; ... mixed *msgsN; })
+ *     })
+ *
+ *     Each msgs field is an array of messages:
+ *
+ *     mixed *msgs = ({ mixed *message1; ... mixed *messageM })
+ *
+ *     A message is represented as an array with the following fields:
+ *
+ *     mixed *message = ({
+ *        string from;
+ *        string sender;
+ *        string recipient;
+ *        string *cc;
+ *        string *bcc;
+ *        string subject;
+ *        string date;
+ *        string id;
+ *        string body;
+ *     })
+ *
+ *     The mailer demon (/secure/mailer) provides the following functions:
+ *
+ *     string *DeliverMail(mixed *message)
+ *       Hand a mail message over to the mailer demon. The mailer
+ *       demon extracts recipients from the recipient, cc and bcc
+ *       fields and removes the bcc information. It then deposits
+ *       the message to the mail files of all recipients. A valid
+ *       message is shown above. Returns a list of successfully
+ *       delivered recipients.
+ *
+ *     int FingerMail(string user)
+ *       Gives the number of unread messages a user has.
+ *------------------------------------------------------------
+ */
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma pedantic
+#pragma warn_deprecated
+
+#include <config.h>
+#include <mail.h>
+#include <wizlevels.h>
+
+// debugging
+#define DEBUG(msg) if ( find_player("zesstra") ) \
+                      tell_object( find_player("zesstra"), "MAILER: "+msg )
+#undef DEBUG
+#define DEBUG(x)
+
+// write out a message to the recipient
+#define NOTIFY_RECIPIENT
+// who gets undeliverable mail?
+#define BOUNCE_ADDR   "mud@mg.mud.de"
+#define SECURITY(x)   (geteuid(x) == ROOTID || geteuid(x) == MAILID)
+// flag for _DeliverMail
+#define MAIL_DELAYED   4096
+
+// prototypes
+protected void create();
+static int GetFolders( string user );
+static string _unify( string str );
+static string *unify( string *str );
+static string *expand( string *addr, int expa );
+static string _filter_addr( string addr );
+public string *DeliverMail( mixed msg, int expa );
+public int FingerMail( string user );
+static void save_msg( mixed msg, string user );
+public int RemoveMsg( int msg, int folder, string user );
+public int MoveMsg( int msg, int folder, string newfolder, string user );
+public int DeleteUnreadFolder( string user );
+public int RemoveFolder( string folder, string user );
+public int MakeFolder( string folder, string user );
+public int query_recipient_ok( string name );
+public void deliver_mail( string recipient, string from, string subject,
+                          string mail_body );
+
+
+mixed *folders;                /* used for save and restore of mail files */
+static mapping alias;
+static string cachedname; /* whose folder is still in memory? */
+
+
+protected void create()
+{
+    mixed tmp;
+    int i;
+    string s1, s2;
+  
+    seteuid(ROOTID);
+    alias=([]);
+    
+    if ( tmp = read_file("/mail/system.mailrc") ){
+        tmp = explode( tmp, "\n" );
+        
+        for ( i = sizeof(tmp); i--; )
+            if ( sscanf( tmp[i], "%s %s", s1, s2 ) == 2 )
+                alias[s1] = s2;
+    }
+}
+
+
+// GetFolders laedt einen folder, wenn er nicht im cache ist, und gibt
+// 0 zurueck, wenn der folder nicht vorhanden oder evtl auch leer ist.
+// Sinn: Vor allem bei Listenargumenten im mailer kann es leicht vorkommen,
+// dass dasselbe mailfile einige Male hintereinander gebraucht wird.
+
+static int GetFolders( string user )
+{
+    if ( user == cachedname ){
+        DEBUG( "Using cached folder for " + user + "\n" );
+        return sizeof(folders[1]);
+    }
+    
+    cachedname = user;
+    
+    if ( !restore_object( MAILPATH + "/" + user[0..0] + "/" + user ) ){
+        DEBUG( "Loading folder: " + user + " (empty)\n" );
+        folders = ({ ({}), ({}) });
+        return 0;
+    }
+    
+    DEBUG( "Loading folder:" + user + "\n" );
+    return 1;
+}
+
+
+static string _unify( string str )
+{
+    return str[0] == '\\' ? str[1..] : str;
+}
+
+
+static string *unify( string *str )
+{
+    if ( !pointerp(str) )
+        return ({});
+    
+    str = map( filter( str, #'stringp/*'*/ ), #'lower_case/*'*/ );
+    str = map( str, "_unify", this_object() );
+
+    return m_indices( mkmapping(str) );
+}
+
+
+#define MG_NAMES ({ MUDNAME, "mg", "morgengrauen", "mud", "mg.mud.de" })
+
+string expandSystemRecursive(string addr,int maxrec){
+  string *list,*tlist;
+  string ret,tmp;
+  int i,size;
+  
+  if(maxrec>8 || !addr){
+    return addr;
+  }
+  maxrec++;
+  
+  tlist=({});
+  ret="";
+
+  list=explode(addr,",");
+  list-=({""});
+  size=sizeof(list);
+  for(i=0;i<size;i++){
+    if(tmp=alias[list[i]]){
+      tlist+=explode(tmp,",");
+    }
+    else{
+      tlist+=({list[i]});
+    }
+  }
+  tlist-=({""});  
+  ret=implode(tlist,",");
+
+  if((ret!=addr)!=0){
+    ret=expandSystemRecursive(ret,maxrec);
+  }
+  return ret;
+}
+
+// expa: also do alias and forward-expansion? (for inbound external mail)
+// expa == 0 means full expansion, known flags are NO_SYSTEM_ALIASES
+// and NO_USER_ALIASES
+static string *expand( string *addr, int expa )
+{
+    string tmp, *new, *ret;
+    int i;
+    closure qf;
+
+    ret = ({});
+    addr -= ({""});
+    qf = symbol_function( "QueryForward", FWSERV );
+
+    for ( i = sizeof(addr); i--; ){
+        addr[i] = lower_case( addr[i] );
+        // @morgengrauen-namen werden lokal zugestellt.
+        if ( sizeof(new = explode( addr[i], "@" )) == 2  &&
+             member( MG_NAMES, new[1] ) != -1 )
+            addr[i] = new[0];
+
+        if ( !(expa & NO_SYSTEM_ALIASES) && tmp = expandSystemRecursive(addr[i],0) ){
+            ret += explode( tmp, "," );
+        }
+        else
+            ret += ({ addr[i] });
+    }
+    
+    for ( i = sizeof(ret); i--; ){
+        if ( ret[i][0] == '\\' )
+            ret[i] = ret[i][1..];
+        else if ( !(expa & NO_USER_ALIASES) )
+            ret = ret - ({ ret[i] }) +
+                explode( funcall( qf, ret[i] ), "," );
+    }
+    
+    return ret;
+}
+
+
+static string _filter_addr( string addr )
+{
+    addr = regreplace( addr, "[^<]*<(.*)>[^>]*", "\\1", 0);
+    return regreplace( addr, " *([^ ][^ ]*).*", "\\1", 0);
+}
+
+#ifdef INTERNET_MAIL_ENABLED
+#define FOOTER \
+    "\n*****************************************************************\n" \
+    "* MorgenGrauen MailRelay v1.0 - Processed %s, %s *\n" \
+    "* MorgenGrauen - mg.mud.de 23 -                  87.79.24.60 23 *\n" \
+    "*****************************************************************"
+#endif
+
+public string *DeliverMail( mixed msg, int expa )
+{
+    string sender, *recipients, *recok, t, *tmp;
+    mixed *newmsg;
+    int i;
+#ifdef INTERNET_MAIL_ENABLED
+    int ext;
+#endif
+
+    if ( !pointerp(msg) || sizeof(msg) != 9 )
+        return 0;
+
+    DEBUG( sprintf( "DeliverMail: %O %O\n", msg[0..4] +({0})+ msg[6..7], expa ) );
+    t = ctime(time());
+
+    // Ohne Empfaenger wird abgebrochen
+    if (!stringp(msg[MSG_RECIPIENT]))
+        return 0;
+
+    if ( !(expa & MAIL_DELAYED) ){
+        /* determine the real sender */
+        if ( extern_call() && object_name(previous_object())[0..7] != "/secure/" )
+            sender = getuid( this_interactive() || previous_object() );
+        else
+            sender = msg[MSG_SENDER];        
+
+        /* make a list of all recipients */
+        recipients = ({ msg[MSG_RECIPIENT] });
+        
+        if ( !(expa & NO_CARBON_COPIES) ){
+            if ( pointerp(msg[MSG_CC]) )
+                recipients += msg[MSG_CC];
+            
+            if ( pointerp(msg[MSG_BCC]) )
+                recipients += msg[MSG_BCC];
+        }
+
+        // Mail-Aliase ersetzen
+        recipients = expand( recipients, expa );
+    
+        // doppelte Adressen loeschen (nebenbei: auf Kleinschreibung wandeln)
+        recipients = unify( recipients );
+    
+        // Realnamen und Kommentare entfernen
+        recipients = map( recipients, "_filter_addr", this_object() );
+    
+        // auf ungueltige Zeichen checken
+        if ( sizeof(tmp = regexp( recipients, "^[-_.@a-z0-9]*$" ))
+             != sizeof(recipients) ){
+            tmp = recipients - tmp;
+            
+            for ( i = sizeof(tmp); i--; )
+                log_file( "MAIL_INVALID", sprintf( "%s: Mail von %O (%O) an "
+                                                   "'%O'.\n", dtime(time()),
+                                                   msg[MSG_FROM],
+                                                   sender, tmp[i] ) );
+            
+            recipients -= tmp;
+        }
+
+     // check for valid Subject and Body
+     if (!stringp(msg[MSG_SUBJECT]))
+         msg[MSG_SUBJECT] = "(no subject given)";
+     if (!stringp(msg[MSG_BODY]))
+         msg[MSG_BODY] =
+           "\n\nSorry - This mail was delivered without a mail body\n\n";
+
+        DEBUG( sprintf( "NEED TO DELIVER TO %O\n", recipients ) );
+
+        /* build the new message */
+        newmsg = ({ msg[MSG_FROM], sender, msg[MSG_RECIPIENT],
+                        msg[MSG_CC], "", msg[MSG_SUBJECT],
+                        dtime(time()), MUDNAME + ":" + time(),
+                        msg[MSG_BODY] });
+    }
+    
+    /* Send it off ... */
+    recok = ({ });
+
+    // ACHTUNG: durch expand() geaenderte Adressen werden zugestellt,
+    // aber durch /mail/mailer.c zusaetzlich als unzustellbar genannt!
+    
+    for ( i = sizeof(recipients); i-- /*&& get_eval_cost() > 500000*/; ){
+        DEBUG( sprintf( "Begin delivering to %s. Evalcosts left: %d.\n",
+                        recipients[i], get_eval_cost() ) );
+        if ( member( recipients[i], '@' ) > 0 &&
+             strstr( recipients[i], "daemon" ) < 0 ) {
+            string rec, mud;
+            
+            tmp = explode( recipients[i], "@" );
+            mud = tmp[1];
+            rec = tmp[0];
+            sender = regreplace( sender, "@", "%", 1 );
+            // Zustellung via Intermud-Mail an andere Muds.
+            if ( member( mud, '.' ) == -1 ) {
+                "/secure/udp_mail"->deliver_mail( rec, mud, sender,
+                                                  msg[MSG_SUBJECT],
+                                                  msg[MSG_BODY] );
+                recok += ({ recipients[i] });
+            }
+#ifdef INTERNET_MAIL_ENABLED
+            // Zustellung in den Rest des Internets.
+            else {
+                ext = 1;
+                sender = explode( sender, "%" )[0];
+                rec = explode( regreplace( rec, "@", "%", 1 ), "%" )[0];
+                mud = explode( regreplace( mud, "@", "%", 1 ), "%" )[0];
+
+                write_file( sprintf( "/mail/outbound/%s.%d-%d-%d",
+                                     sender, time(), i, random(123456) ),
+                            sprintf( "%s\n%s@%s\n"
+                            "Subject: %s\n"
+                         "X-MUD-From: %s\n"
+                         "X-MUD-To: %s\n"
+                         "X-MUD-Cc: %s\n"
+                         "X-MU-Subject: %s\n\n",
+                         sender, rec, mud,
+                                     msg[MSG_SUBJECT],
+                                     sender, recipients[0],
+                                     pointerp(msg[MSG_CC]) ?
+                                     implode( msg[MSG_CC], "," ) : "",
+                                     msg[MSG_SUBJECT] ) + msg[MSG_BODY] +
+                            sprintf( FOOTER, t[4..10] + t[20..], t[11..18] )
+                            + "\n" );
+                recok += ({ recipients[i] });
+            }
+#endif // INTERNET_MAIL_ENABLED
+
+        }
+        else
+            if ( file_size( SAVEPATH + recipients[i][0..0] + "/" +
+                            recipients[i] + ".o" ) >=0 ){
+                save_msg( newmsg, recipients[i] );
+                recok += ({ recipients[i] });
+            }
+            else {
+                string *tmpmsg = copy(newmsg);
+                tmpmsg[MSG_BODY] = "--- Text der Mail geloescht. ---\n";
+                write_file( sprintf( "/mail/outbound/postmaster.%d-%d",
+                                     time(), random(time()) ),
+                            sprintf( "postmaster\n" + BOUNCE_ADDR + 
+                                     "\nSubject: Undeliverable Mail\n%O\n",
+                                     tmpmsg) );
+            }
+        DEBUG( sprintf( "End delivering to %s. Evalcosts left: %d.\n",
+                        recipients[i], get_eval_cost() ) );
+    }
+#ifdef INTERNET_MAIL_ENABLED
+    if ( ext )
+        send_udp( UDPSERV, 4123, "DELIVERMAIL" );
+#endif
+    return recok;
+}
+
+
+public int FingerMail( string user )
+{
+    int newfolder, i;
+
+    //Zugriff beschraenken, Zahl der gelesenen Mails ist Privatsphaere
+    if (!objectp(this_interactive()) || !stringp(user) || !sizeof(user)) 
+     return(-1);
+    if ((getuid(this_interactive())!=user) &&
+     (process_call() || !ARCH_SECURITY)) return(-1);
+
+    if ( !GetFolders(user) )
+        return 0;
+
+    if ( (newfolder = member(folders[0],"unread")) != -1 )
+        return sizeof(folders[1][newfolder]);
+
+    return 0;
+}
+
+
+static void save_msg( mixed msg, string user )
+{
+    int newfolder;
+    object p;
+  
+    GetFolders( user );
+
+    /* if folder 'unread' doesn't exist, create it */
+    newfolder = member( folders[0], "unread");
+    
+    if ( newfolder == -1 ){
+        folders[0] += ({ "unread" });
+        folders[1] += ({ ({ }) });
+        newfolder = member( folders[0], "unread");
+    }
+    
+    folders[1][newfolder] += ({ msg });
+    save_object( MAILPATH + user[0..0] + "/" + user );
+#ifdef NOTIFY_RECIPIENT
+    if ( p = find_player(user) )
+        tell_object( p, "Ein Postreiter ruft Dir aus einiger Entfernung zu, "
+                     "dass Du neue Post hast!\n" );
+#endif
+}
+
+
+/* Remove a message from a folder */
+public int RemoveMsg( int msg, int folder, string user )
+{
+    if ( !SECURITY(previous_object()) )
+        return -2;
+
+    if ( !GetFolders(user) ) 
+        return -1; /* No such folder */
+
+    if ( !pointerp(folders) || !pointerp(folders[0]) || 
+         folder >= sizeof(folders[0]) )
+        return -1;
+
+    if ( msg < 0 || sizeof(folders[1][folder]) <= msg )
+        return 0; /* No such msg */
+
+    folders[1][folder][msg..msg] = ({});
+    
+    save_object( MAILPATH + user[0..0] + "/" + user );
+    return 1; /* Success */
+}
+
+
+/* Move message into another folder */
+public int MoveMsg( int msg, int folder, string newfolder, string user )
+{
+    int target;
+
+    if ( !SECURITY(previous_object()) )
+        return -2;
+
+    if ( !GetFolders(user) )
+        return -1; /* Source folder not found */
+
+    if ( !pointerp(folders) || !pointerp(folders[0]) || 
+         folder >= sizeof(folders[0]) )
+        return -1;
+  
+    if ( msg < 0 || sizeof(folders[1][folder]) <= msg )
+        return 0; /* No such msg */
+
+    if ( (target = member(folders[0], newfolder)) == -1 )
+        return -3;
+
+    if ( target == folder )
+        return 1;
+
+    if ( !pointerp(folders[1][target]) ) 
+        folders[1][target] = ({ folders[1][folder][msg] });
+    else 
+        folders[1][target] += ({ folders[1][folder][msg] });
+
+    return RemoveMsg( msg, folder, user );
+}
+
+
+public int DeleteUnreadFolder( string user )
+{
+    int unread, newmail;
+
+    if ( !SECURITY(previous_object()) )
+        return -2;
+
+    if ( !GetFolders(user) )
+        return -1; /* Source folder not found */
+
+    if ( (unread = member( folders[0], "unread")) == -1 )
+        return 0;
+    
+    if ( (newmail = member( folders[0], "newmail")) == -1 ){
+        folders[0] += ({ "newmail" });
+        folders[1] += ({({})}); 
+        newmail = sizeof(folders[1]) - 1;
+    }
+    
+    if ( !pointerp(folders[1][newmail]) )
+        folders[1][newmail] = ({});
+    
+    if ( pointerp(folders[1][unread]) )
+        folders[1][newmail] += folders[1][unread];
+    
+    folders[0][unread..unread] = ({});
+    folders[1][unread..unread] = ({});
+
+    save_object( MAILPATH + user[0..0] + "/" + user );
+    return 0;
+}
+
+
+public int RemoveFolder( string folder, string user )
+{
+    int i;
+
+    if ( !SECURITY(previous_object()) )
+        return -2;
+
+    if ( !GetFolders(user) )
+        return -1; /* No such folder */
+
+    if ( (i = member( folders[0], folder)) == -1 )
+        return -1; /* No such folder */
+
+    if ( sizeof(folders[1][i]) > 0 )
+        return 0; /* Folder not empty */
+
+    folders[0][i..i] = ({});
+    folders[1][i..i] = ({});
+
+    save_object( MAILPATH + user[0..0] + "/" + user );
+    return 1;
+}
+
+
+public int MakeFolder( string folder, string user )
+{
+    if ( !SECURITY(previous_object()) )
+        return -2;
+    
+    if ( !folder || !stringp(folder) )
+        return -1; /* Huh ? */
+
+    if ( folder == "unread" )
+        return 0; /* Folder exists virtually :) */
+
+    GetFolders( user );
+
+    if ( member( folders[0], folder) != -1 )
+        return 0; /* Folder exists */
+
+    folders[0] = folders[0] + ({ folder });
+    folders[1] = folders[1] + ({ ({}) });
+    
+    save_object( MAILPATH + user[0..0] + "/" + user );
+    return 1;
+}
+
+
+public int query_recipient_ok( string name )
+{
+#if INTERNET_MAIL_ENABLED
+    return  (file_size( "secure/save/" + name[0..0] + "/" + name + ".o" ) > 0
+        || member( name, '%' ) > 0 || member( name, '@' ) > 0 );
+#else
+    // es darf zwar ein @ in der Adresse vorkommen, dahinter aber kein . mehr
+    // (dann ist es ne Mail via Intermud-Mail, nicht ins Internet).
+    string *tmp;
+    return  (file_size( "secure/save/" + name[0..0] + "/" + name + ".o" ) > 0
+        || member( name, '%' ) > 0
+        || (sizeof(tmp=explode(name,"@")) == 2 && strstr(tmp[1],".") == -1));
+#endif
+}
+
+
+public void deliver_mail(
+                         string recipient, /* the local players real name*/
+                         string from,      /* A string depicting the sender */
+                         string subject,   /* The mail subject/header */
+                         string mail_body  /* The actual mail message */ )
+{
+    DEBUG( sprintf("DELIVER %O\n",
+                   ({ from, from, recipient, ({}), ({}), subject, time() })) );
+
+    // Geloggt wird, wenn ein aufrufendes Objekt nicht sicher ist.
+    if (object_name(previous_object())[0..7]!="/secure/")
+      write_file("/secure/ARCH/DELIVER_MAIL",
+     sprintf("%s : Aufruf von /secure/mailer->deliver_mail()\n"
+          "  Sender: %O Empfaenger: %O\n  PO: %O TI: %O TP:%O\n\n",
+          ctime(time()),from, recipient,
+          previous_object(), this_interactive(), this_player()));
+    
+    DeliverMail( ({ from, from, recipient, ({}), ({}), subject, time(),
+                    "EXTERNAL", mail_body }), 0 );
+}
diff --git a/secure/master.c b/secure/master.c
new file mode 100644
index 0000000..0574cdf
--- /dev/null
+++ b/secure/master.c
@@ -0,0 +1,1254 @@
+// MorgenGrauen MUDlib
+//
+// master.c -- master object
+//
+// $Id: master.c 9530 2016-03-17 19:59:01Z Zesstra $
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+#pragma pedantic
+#pragma range_check
+#pragma warn_deprecated
+
+#include "/sys/files.h"
+#include "/sys/interactive_info.h"
+#include "/sys/driver_info.h"
+
+inherit "/secure/master/misc";
+inherit "/secure/master/userinfo";
+inherit "/secure/master/network";
+inherit "/secure/master/domain";
+inherit "/secure/master/guild";
+inherit "/secure/master/file_access";
+inherit "/secure/master/players_deny";
+
+#include "/secure/master.h"
+
+//for limited, set_limit(), etc. Abs. Pfad noetig, da noch kein Include-Hook
+#include "/sys/rtlimits.h"
+#include "/sys/debug_info.h"
+#include "/sys/debug_message.h"
+#include "/sys/configuration.h"
+#include "/sys/regexp.h"
+
+// Module des Master einfuegen. Per #include, damits geringfuegig schneller.
+// Da die Module ja nirgendwo sonst geerbt werden koennten, ist dies
+// ausnahmsweise OK. ;)
+#include "/secure/master/destruct.c"
+#include "/secure/master/autoinclude.c"
+
+// Fuer Logfile(-Rotation)
+#define RESETINT   3600                      // jede Stunde
+#define LOGROTATE  (24*2)                    // alle 48h
+#define LOGFILEAGE (RESETINT*LOGROTATE)      // alle 2 tage wechseln
+#define LOGNUM     3                         // Anzahl der Logfiles
+
+private nosave int logrotate_counter; //READ_FILE alle LOGROTATE Resets rotieren
+// Wird gerade versucht, die simul_efuns zu laden? Wenn ja, duerfen keine
+// sefuns gerufen werden.
+private nosave int loading_simul_efuns;
+// wird gerade ein Fehler bearbeitet? Dient zur Erkennung von Rekursionen in
+// der Fehlerbehandlung
+private nosave int handling_error;
+
+//                       #####################
+//######################## Start des Masters ############################
+//                       #####################
+
+// Initialisierung des Masters
+//arg==0: Mud startet gerade, arg==1: Master wurde reaktiviert,
+//arg==2: Master reaktiviert (Variablen weg), arg==3: Master wurde
+//neugeladen.
+protected void inaugurate_master(int arg) {
+
+  set_driver_hook(H_REGEXP_PACKAGE, RE_TRADITIONAL);
+  
+  seteuid(ROOTID);
+  userinfo::create();
+  LoadPLDenylists();
+
+  // Was soll vor jede Datei geschrieben werden?
+  set_driver_hook(H_AUTO_INCLUDE, #'autoincludehook);
+
+  //div. Listen einlesen
+  ReloadBanishFile();
+  ReloadDeputyFile();
+  ReloadInsecureFile();
+ 
+  // Bei Neustart wizinfo initialisieren
+  if (!arg)
+    set_extra_wizinfo(0, allocate(BACKBONE_WIZINFO_SIZE));
+  
+  //simul_efun.c nach inaugurate_master() starten und initialisieren, 
+  //(so richtig seh ich den Sinn hier momentan nicht ein...
+  //call_out("start_simul_efun",0);
+
+  // Reset festsetzen (1h)
+  set_next_reset(RESETINT);
+
+  // Driver konfigurieren
+  // Lagerkennung erst ne Minute spaeter, waehrend preload darfs momentan
+  // ruhig laggen (Zeit: vorlaeufig 1min)
+  call_out(#'configure_driver, 60, DC_LONG_EXEC_TIME, 100000);
+  
+  // Hooks setzen 
+  
+  // Standardincludeverzeichnisse fuer #include <>
+  set_driver_hook(H_INCLUDE_DIRS, ({"/secure/","/sys/"}) );
+  
+  //Nach dem Laden/Clonen create() im Objekt rufen
+  set_driver_hook(H_CREATE_CLONE,       "create");
+  set_driver_hook(H_CREATE_OB,          "create");
+  set_driver_hook(H_CREATE_SUPER,       "create_super");
+
+  // Bei Reset reset() im Objekt aufrufen
+  set_driver_hook(H_RESET,              "reset");
+  
+  // Zum Aufraeumen clean_up() im Objekt aufrufen  
+  set_driver_hook(H_CLEAN_UP,           "clean_up");
+
+  // Jede Eingabe wird ueber modify_command gelenkt
+  set_driver_hook(H_MODIFY_COMMAND,       "modify_command");
+  // Dieser Hook ist mandatory, aber wird nur benutzt, wenn via
+  // set_modify_command() aktiviert - was wir nicht (mehr) tun. Insofern ist
+  // das Relikt aus alten Zeiten.
+  //set_driver_hook(H_MODIFY_COMMAND_FNAME, "modify_command");
+
+  // Standard-Fehlermeldung
+  //set_driver_hook(H_NOTIFY_FAIL,        "Wie bitte?\n");
+  set_driver_hook(H_NOTIFY_FAIL, function string (string cmd, object tp)
+      {if (stringp(cmd=(string)tp->QueryProp(P_DEFAULT_NOTIFY_FAIL)))
+         return(cmd);
+       return("Wie bitte?\n");});
+
+  // Was machen bei telnet_neg
+  set_driver_hook(H_TELNET_NEG,"telnet_neg");
+  //  set_driver_hook(H_TELNET_NEG,0);
+
+  // Promptbehandlung: Defaultprompt setzen und dafuer sorgen, dass alle
+  // Promptausgaben durch print_prompt im Interactive laufen, damit das EOR
+  // angehaengt wird.
+  set_driver_hook(H_PRINT_PROMPT, "print_prompt");
+  set_driver_hook(H_DEFAULT_PROMPT, "> ");
+
+  // EUID und UID muessen neue Objekte auch noch kriegen, Hooks dafuer setzen
+  set_driver_hook(H_LOAD_UIDS, #'load_uid_hook);
+  set_driver_hook(H_CLONE_UIDS, #'clone_uid_hook);
+
+  // Meldung bei vollem Mud
+  set_driver_hook(H_NO_IPC_SLOT, "Momentan sind leider zuviele Spieler im "
+      +MUDNAME+" eingeloggt.\nBitte komm doch etwas spaeter nochmal "
+      "vorbei!\n");
+
+  // Nun der beruechtigte Move-Hook ...
+  // (bewegt objekt und ruft alle notwendigen inits auf)
+  // Hier wird H_MOVE_OBJECT0 benutzt, d.h. die lambda wird an das aktuelle
+  // Objekt vor Ausfuehrung gebunden, nicht an 'item', was move_objet()
+  // uebergeben wurde.
+  set_driver_hook(H_MOVE_OBJECT0,
+     unbound_lambda(({'item,'dest}),
+       ({#',,                                      // item!=this_object?
+         ({#'?,({#'!=,'item,({#'this_object})}),
+           ({#'raise_error,                        // ->Fehler!
+             "Illegal to move other object than this_object()\n"}) }),
+         ({#'efun::set_environment,'item,'dest}),  // item nach dest bewegen
+         ({#'?,
+           ({#'living,'item}),                     // living(item)?
+           ({#',,
+             ({#'efun::set_this_player,'item}),    // set_this_player(item)
+             ({#'call_other,'dest,"init"}),        // dest->init()
+             ({#'?!, 
+               ({#'&&,                             // !objectp(item)||
+                 ({#'objectp, 'item}),             // env(item)!=dest?
+                 ({#'==,({#'environment, 'item}),'dest})}),
+               ({#'return})})})}),                 // -> fertig
+         ({#'=,'others,({#'-,({#'all_inventory,'dest}),({#'({,'item})})}),
+#ifdef EMACS_NERV 
+         })   // Emacs kann ({#'({ nicht parsen    // others=all_inv(dest)-
+#endif                                             //        ({item})
+         ({#'filter,'others,lambda(({'ob,'item}),
+            ({#'?,                                     
+              ({#'&&,
+                ({#'objectp,'item}),               // objectp(item)&&
+                ({#'living,'ob}),                  // living(ob)&&
+                ({#'==,({#'environment,'item}),({#'environment,'ob})})}),
+              ({#',,                               // env(item)==env(ob)?
+                ({#'efun::set_this_player, 'ob}),  // set_this_player(ob)
+                ({#'call_other,'item, "init"})     // item->init()
+              })})),'item}),
+         ({#'?,
+           ({#'living,'item}),                     // living(item)?
+              ({#',,
+                ({#'efun::set_this_player,'item}), // set_this_player(item)
+                ({#'filter,'others,lambda(({'ob,'item}),
+                    ({#'?,                          
+                      ({#'&&,                 
+                        ({#'objectp,'item}),       // objectp(item)&&
+                        ({#'objectp,'ob}),         // objectp(ob)&&
+                        ({#'==,({#'environment,'item}),({#'environment,'ob})})
+                      }),                          // env(item)==env(ob)?
+                      ({#'call_other,'ob,"init"})  // ob->init()
+                    })),'item})})}),
+         ({#'?,
+           ({#'&&,
+             ({#'objectp,'item}),                  // objectp(item)&&
+             ({#'living,'dest}),                   // living(dest)&&
+             ({#'==,({#'environment,'item}),'dest})// env(item)==dest?
+           }),
+           ({#',,
+             ({#'efun::set_this_player,'dest}),    // set_this_player(dest)
+             ({#'call_other,'item,"init"})})})})));// item->init()
+  DEBUG("Master inaugurated");
+  return;
+}
+
+// epilog() gibt die Dateien zurueck, die vorgeladen werden muessen
+protected string *epilog(int eflag) {
+  string *files, *domains;
+  int i;
+
+  seteuid(ROOTID);
+  ReloadBanishFile();
+  ReloadDeputyFile();
+  ReloadInsecureFile();
+
+  if (eflag) {
+    write("-e angegeben -> Preloading unterdrueckt ...\n");
+    return 0;
+  }
+
+  printf("Preloading gestartet: %s\n\n",ctime(time()));
+
+  //Files fuers Preloading und Regionen holen
+  files=explode_files("/std/preload_file") + explode_files("/d/preload_file");
+  domains=get_domains() + ({"erzmagier"}); // EM haben auch ein Preload File
+
+  for (i=sizeof(domains);i--;)
+    files+=explode_files("/d/"+domains[i]+"/preload_file");
+  
+  write("\n");
+ 
+  return files;
+}
+
+// preload() wird fuer jeder Datei im Preload einmal durchlaufen
+protected void preload(string file) {
+  string err;
+  string name;
+  int *res, zeit;
+
+  // Kein richtiger Dateiname: fertig
+  if(!file || !file[0] || file[0] == ';' || file_size(file+".c") < 0) return;
+
+  // Kein Besitzer -> Meldung ausgeben, fertig
+  if (!(name=creator_file(file)))
+  {
+    printf("Kein Besitzer gefunden fuer Datei %s.\n",file);
+    return;
+  }
+
+  // Datei und Besitzer ausgeben
+  printf("%-50s%-15s",file,name);
+
+  // Euid kann nicht geaendert werden -> Meldung, fertig
+  if (!seteuid(name))
+  {
+    printf("\nEuid konnte nicht auf %s gesetzt werden\n"+
+             "Sie ist nun %s.\n",name,geteuid(TO));
+    return;
+  }
+
+  // Dann mal laden .. dabei Zeit messen
+  zeit = apply(#'+,rusage()[0..1]);
+
+  // Waehrend des Preloads sind 1.5MTicks leider nicht soviel, insb. wenn
+  // sowas wie der channeld jede menge Objekte laden muss, die wiederum erst
+  // das ganze Geraffel aus /std/ laden. Daher hier einfach mal fuers Preload
+  // die Grenze hoch.
+  err = catch(limited(#'load_object,({5000000}),file));
+
+  if (err != 0)
+    printf("\nFehler beim Laden von %s:\n%s\n",file,err);
+  else
+  {
+    zeit=apply(#'+,rusage()[0..1])-zeit;
+    printf("(%2d.%:02d s)\n",zeit/1000,zeit%1000);
+  }
+
+  // Noch EUID zuruecksetzen
+  seteuid(ROOTID);
+
+  return;
+}
+
+//simul_efun.c laden oder die spare_simul_efun.c als Fallback
+//es ist moeglich, hier ein Array von strings zurueckzugeben. Die
+//nachfolgenden gelten als Backup-Objekte, falls eine sefun im Hauptobjekt
+//nicht gefunden wird.
+protected string *get_simul_efun() {
+  string err;
+
+  ++loading_simul_efuns;
+
+  if (!(err=catch(SIMUL_EFUN_FILE->start_simul_efun())) ) {
+    --loading_simul_efuns;
+    return ({SIMUL_EFUN_FILE});
+  }
+  
+  write("Failed to load simul efun " + SIMUL_EFUN_FILE + "\n");
+  debug_message("Failed to load simul efun " + SIMUL_EFUN_FILE +
+      " " + err, DMSG_STDOUT | DMSG_LOGFILE);
+
+  if (!(err=catch(SPARE_SIMUL_EFUN_FILE->start_simul_efun())) ) {
+    --loading_simul_efuns;
+    return ({SPARE_SIMUL_EFUN_FILE});
+  }
+  
+  write("Failed to load spare simul efun" + SPARE_SIMUL_EFUN_FILE + "\n");
+  debug_message("Failed to load spare simul efun " + SPARE_SIMUL_EFUN_FILE +
+      " " + err, DMSG_STDOUT | DMSG_LOGFILE);
+
+ 
+  efun::shutdown();
+  
+  return 0;
+}
+
+protected mixed call_sefun(string sefun, varargs mixed args) {
+  if (!loading_simul_efuns)
+    return apply(symbol_function(sefun), args);
+  else {
+    switch(sefun) {
+      case "log_file":
+      case "secure_level":
+      case "secure_euid":
+      case "find_player":
+      case "find_netdead":
+      case "find_living":
+      case "process_call":
+      case "replace_personal":
+      case "file_time":
+        return 0;
+      case "dtime":
+        if (pointerp(args) && sizeof(args))
+          return strftime("%a %d. %b %Y, %H:%M:%S",args[0]);
+        else
+          return strftime("%a %d. %b %Y, %H:%M:%S");
+      case "uptime":
+        return to_string(time()-__BOOT_TIME__) + " Sekunden";
+    }
+  }
+  return 0;
+}
+
+
+// Preload manuell wiederholen; Keine GD-Funktion
+void redo_preload()
+{
+  string *to_preload;
+  int i,j;
+
+  to_preload=epilog(0);
+  j=sizeof(to_preload);
+  for (i=0;i<j;i++) 
+      catch(preload(to_preload[i]));
+  return;
+}
+
+//                         ##################
+//########################## Standard-Lfuns #############################
+//                         ##################
+
+// Keine GD-Funktion, aber sinnvoll.
+public varargs int remove(int silent)
+{
+  write("Der Master will aber nicht zerstoert werden!\n");
+  return 0;
+}
+
+// Einige repetitiven Taetigkeiten kann der Reset erledigen
+protected void reset()
+{
+  int i, *date;
+  mixed *wl;
+
+  //Darf nicht von Hand aufgerufen werden!
+  if (TI||TP) return;
+  
+  // Naechsten Reset setzen
+  set_next_reset(RESETINT);
+
+  // Userinfo aufraeumen
+  _cleanup_uinfo();
+
+  // Projekt-Cache loeschen
+  _cleanup_projects();
+
+  // Wenn Zeit abgelaufen, dann READ_FILE-Log rotieren
+  if (!logrotate_counter--)
+  {
+    i=time()/LOGFILEAGE;
+    date=get_dir(sprintf("%s.%d", READ_FILE,i%LOGNUM), GETDIR_DATES);
+    if (pointerp(date)&&sizeof(date)&&
+        (date[0]/LOGFILEAGE)==i) return;
+    if (file_size(READ_FILE)>0)
+      rename(READ_FILE, sprintf("%s.%d", READ_FILE,i%LOGNUM));
+    logrotate_counter=LOGROTATE;
+  }
+  // einmal am Tag die UIDAliase resetten. Hierzu wird der logrotate_counter
+  // missbraucht.
+  if (!(logrotate_counter%24)) {
+    ResetUIDAliase();
+#ifdef _PUREFTPD_
+    expire_ftp_user();
+#endif
+  }
+
+  // Wizlist altert
+  wl = wizlist_info();
+  for (i=sizeof(wl); i--; )
+    set_extra_wizinfo(wl[i][WL_NAME], wl[i][WL_EXTRA] * 99 / 100);
+
+  // Dann noch Mails bearbeiten
+  mails_last_hour=0;
+  mailread();
+
+  return;
+}
+
+
+//                         #################
+//########################## ID-Management ##############################
+//                         #################
+
+// Magier-ID fuer Datei str (als Objekt oder als String) ermitteln
+string creator_file(mixed str) {
+  string *strs,tmp;
+  int s;
+
+  // full_path_array nach strs
+  // TODO: was ist mit clones?
+  if(objectp(str))
+    strs=full_path_array(object_name(str), 0);
+  else if(stringp(str))
+    strs=full_path_array(str, 0);
+  else return NOBODY;
+
+  // absolute Pfade interessieren hier gerade nicht.
+  strs -= ({""});
+
+  s=sizeof(strs);
+  if(s<2) return NOBODY;
+
+  switch(strs[0]) {
+    case DOMAINDIR:
+      // Fuer Nicht-Magier bzw. "Pseudo-"Magier die Regionskennung
+      if( s==2 || WIZLVLS[strs[2]] || !IS_LEARNER(strs[2]) )
+        return strs[1];
+
+      // in /d/erzmagier gibt es Magier-IDs
+      if (strs[1]=="erzmagier") return strs[2];
+
+      // Ansonsten d.region.magiername
+      return sprintf("d.%s.%s", strs[1], strs[2]);
+ 
+    case PROJECTDIR:
+      // In p.service gibt es ids mit uid
+      if (s>2 && strs[1]=="service") return "p.service."+strs[2];
+
+      // Ansonsten nur p.projekt (p.service ist auch hier drin)
+      return "p."+strs[1];
+
+    case WIZARDDIR:
+      if (s>2)
+        return strs[1];
+      if (s==2 && file_size(strs[0]+"/"+strs[1])==FSIZE_DIR)
+        return strs[1];
+      return NOBODY;
+
+    case SECUREDIR:
+      return ROOTID;
+
+    case GUILDDIR:
+    case SPELLBOOKDIR:
+      tmp=strs[1];
+      if (tmp[<2..<2]==".") tmp=tmp[0..<3];
+      if ((s=member(tmp,'.'))>0) tmp=tmp[s+1..];
+      return "GUILD."+tmp;
+
+    case LIBROOMDIR:
+      return ROOMID;
+
+    case STDDIR:
+    case LIBOBJDIR:
+      return BACKBONEID;
+
+    case DOCDIR:
+      return DOCID;
+
+    case MAILDIR:
+      return MAILID;
+    case NEWSDIR:
+      return NEWSID;
+
+    case LIBITEMDIR:
+      return ITEMID;
+
+    /* Fall-Through */
+    default:
+      return NOBODY;
+
+ }
+ return NOBODY;  //should never be reached.
+}
+
+// UID und EUID an Objekt geben (oder eben nicht)
+// Keine GD-Funktion, aber von Hooks aufgerufen
+protected mixed give_uid_to_object(string datei,object po)
+{
+  string creator,pouid;
+
+  // Parameter testen
+  if (!stringp(datei)||!objectp(po))  return 1;
+
+  // Keine Objekte in /log, /open oder /data
+  if (strstr(datei, "/"LIBDATADIR"/") == 0
+      || strstr(datei, "/"LIBLOGDIR"/") == 0
+      || strstr(datei, "/"FTPDIR"/") == 0
+      )
+      return 1;
+
+  // Datei muss Besitzer haben
+  if (!(creator=creator_file(datei))) return 1;
+
+  // Hack, damit simul_efun geladen werden kann
+  if (creator==ROOTID && po==TO) return ROOTID;
+
+  // Keine euid, kein Laden ...
+  if (!(pouid=geteuid(po))) return 1; // Disallow if no euid in po
+
+  // Root generiert keine Root-Objekte ;-)
+  // Nur UID setzen
+  if (pouid==ROOTID)
+    return ({creator,NOBODY}); // root does not create root objects!
+
+  // EUID mitsetzen, wenn PO==creator oder creator==BACKBONEID
+  if (creator==pouid || creator==BACKBONEID)
+    return pouid;
+
+  return ({creator,NOBODY});
+}
+
+// Die System-IDs muessen bekannt sein
+string get_master_uid()          { return ROOTID;}
+string get_bb_uid()              { return BACKBONEID; }
+// TODO: Bei get_wiz_name koennte man - so moeglich - auf 'wirkliche'
+//       Magiernamen gehen (Idee von mandragon)
+string get_wiz_name(string file) { return creator_file(file);}
+
+//                     ##########################
+//###################### Sonstige GD-Funktionen #########################
+//                     ##########################
+
+// Spieler hat sich eingeloggt
+protected object connect()
+{
+  string err;
+  int errno;
+  object ob,bp;
+
+#if defined(SSLPORT) && __EFUN_DEFINED__(tls_available)
+  if (efun::interactive_info(this_player(), II_MUD_PORT) == SSLPORT) {
+    // reject connection of TLS is not available
+    if (!tls_available())
+      return 0;
+    // establish TLS
+    errno=tls_init_connection();
+    if (errno < 0) {
+      // Fehler im Vebindungsaufbau
+      printf("Can't establish a TLS/SSL encrypted connection: %s\n",
+          tls_error(errno));
+      return 0; // this will close the connection to the client.
+    }
+  }
+#endif
+
+  // Blueprint im Environment? Das geht nun wirklich nicht ...
+  if ((bp=find_object("/secure/login")) && environment(bp)) 
+      catch(destruct(bp);publish);
+
+  // Login-Shell clonen
+  err = catch(ob = clone_object("secure/login"); publish);
+
+  if (errno == 0 && err)
+      write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
+
+  return ob;
+}
+
+// Was machen bei disconnect?
+protected void disconnect(object who, string remaining) {
+    who->NetDead(); return;
+}
+
+// Es gibt kein File 'filename'. VC aktivieren so vorhanden ...
+protected object compile_object(string filename)
+{
+  object ret;
+  string *str;
+  string compiler;
+
+  if (!sizeof(filename)) return 0;
+  str=efun::explode(filename,"/");
+
+  if(sizeof(str)<2) return 0;
+  compiler=implode(str[0..<2],"/")+"/virtual_compiler";
+ 
+  if (find_object(compiler)
+      || file_size(compiler+".c")>0)
+  {
+    if(catch(
+          ret=(object)call_other(compiler,"compile_object",str[<1]); publish))
+      return 0;
+  }
+  else
+      return(0);
+
+  if ( objectp(ret) && efun::explode(creator_file(filename), ".")[<1] !=
+       REAL_EUID(ret) ){
+      funcall( symbol_function('log_file/*'*/), "ARCH/VC_MISSBRAUCH",
+               sprintf( "%s: %s versucht per VC %s zu %s umzubenennen!\n",
+                        funcall( symbol_function('dtime/*'*/), time() ),
+                        (this_interactive() ? getuid(this_interactive()):
+                         object_name(previous_object())), object_name(ret),
+                         filename) );
+
+      return 0;
+  }
+  return ret;
+}
+
+//                           ############
+//############################ Shutdown #################################
+//                           ############
+
+// Spieler bei Shutdown entfernen
+protected void remove_player(object victim)
+{
+  catch(victim->quit());
+  if (victim) catch(victim->remove());
+  if (victim) destruct(victim);
+  return;
+}
+
+// Langsamer Shutdown 
+protected void slow_shut_down(int minutes)
+{
+  //sollte nur vom GD gerufen werden. Oder allenfalls Master selbst
+  filter(users(),#'tell_object,
+  "Der Gamedriver ruft: Der Speicher wird knapp ! Bereitet euch auf das Ende vor !\n");
+  "/obj/shut"->shut(minutes);
+  return;
+}
+
+
+// In LD varargs void notify_shutdown(string crash_reason)
+protected varargs void notify_shutdown (string crash_reason) {
+
+  if (PO && PO != TO)
+    return;
+  if (crash_reason) {
+      //Uhoh... Verdammter Crash. :-( Zumindest mal mitloggen,
+      //was der Driver sagt...
+      write_file(CRASH_LOG,sprintf(
+            "\n%s: The driver crashed! Reason: %s\n",
+            ctime(time()),crash_reason));
+  }
+
+  filter(users(), #'tell_object,
+               "Game driver shouts: LDmud shutting down immediately.\n");
+  save_wiz_file();
+  return;
+}
+
+//                         ##################
+//########################## Berechtigungen #############################
+//                         ##################
+
+// Darf Verbindung durch name von obfrom nach ob gelegt werden?
+
+//int valid_exec(string name) {return 1;}
+
+int valid_exec(string name, object ob, object obfrom)
+{
+  // Ungueltige Parameter oder Aufruf durch process_string -> Abbruch
+  if (!objectp(ob) || !objectp(obfrom) 
+      || !stringp(name) || !sizeof(name)
+      || funcall(symbol_function('process_call)) )
+    return 0;
+
+  // renew_player_object() darf ...
+  if (name=="/secure/master/misc.c"
+      || name=="secure/master/misc.c")
+    return 1;
+
+  // Ansonsten darf sich nur die Shell selber aendern ...
+  if (previous_object() != obfrom) return 0;
+
+  // Die Login-Shell zu jedem Objekt ...
+  if (name=="/secure/login.c"
+      || name=="secure/login.c")
+    return 1;
+
+  // Magier per exec nur, wenn sie damit keine Fremde uid/euid bekommen
+  if (this_interactive() == obfrom && getuid(obfrom) == getuid(ob) 
+      && geteuid(obfrom) == geteuid(ob)) 
+      return 1;
+
+  // Sonst darf niemand was
+  return 0;
+}
+
+
+// Darf me you snoopen?
+int valid_snoop(object me, object you) { 
+    return getuid(PO) == ROOTID; 
+}
+
+// Darf wiz wissen, ob er gesnoopt wird?
+int valid_query_snoop(object wiz) {
+    return getuid(PO) == ROOTID; 
+}
+
+// Darf ob seine EUID auf neweuid setzen?
+int valid_seteuid(object ob, string neweuid)
+{
+  return (ob==this_object() ||
+          getuid(ob) == ROOTID ||
+          getuid(ob) == neweuid ||
+          creator_file(object_name(ob)) == neweuid);
+}
+
+// Darf getraced werden?
+int valid_trace(string what, mixed arg) { 
+    return IS_ARCH(TI);
+}
+
+
+// valid_write() und
+// valid_read() befinden sich in file_access.c
+
+
+// Darf PO ob shadowen?
+int query_allow_shadow(object ob)
+{
+  // ROOT darf nicht geshadowed werden
+  if(getuid(ob) == ROOTID)
+    return 0;
+  
+  // Access-Rights auch nicht
+  if (efun::explode(object_name(ob),"#")[0][<14..]=="/access_rights")
+    return 0;
+  
+  // Ansonsten query_prevent_shadow fragen ...
+  return !(int)ob->query_prevent_shadow(PO);
+}
+
+
+/* privilege_violation is called when objects try to do illegal things,
+ * or files being compiled request a privileged efun.
+ *
+ * return values:
+ *   1: The caller/file is allowed to use the privilege.
+ *   0: The caller was probably misleaded; try to fix the error.
+ *  -1: A real privilege violation. Handle it as error.
+ */
+int privilege_violation(string op, mixed who, mixed arg1, mixed arg2)
+{
+
+  if (objectp(who) && 
+      (who==this_object() || geteuid(who)==ROOTID))
+    return 1; 
+
+  switch(op)
+  {
+    case "call_out_info":
+      return -1;
+    case "send_udp":
+      return 1;
+
+    case "nomask simul_efun":
+      // <who> ist in diesem Fall ein string (Filename) und damit von dem
+      // Check da oben nicht abgedeckt. Daher explizite Behandlung hier.
+      // Ausserdem hat der Pfad (zur Zeit noch) keinen fuehrenden '/'.
+      // Falls das jemand einschraenken will: der Kram fuer die simul_efuns
+      // muss das duerfen!
+      if (stringp(who)
+          && strstr(who,"secure/") == 0)
+        return 1;
+      return 0; // alle andere nicht.
+
+    case "get_extra_wizinfo":
+      // benutzt von /secure/memory.c und /secure/simul_efun.c
+    case "set_extra_wizinfo":
+      // wird benutzt von simul_efun.c, welche aber als ROOT-Objekt implizit
+      // erlaubt ist (s.o.)
+      return -1; // fuer allen nicht erlaubt.
+
+    case "rename_object":
+      if (object_name(who)=="/secure/impfetch" ||
+          BLUE_NAME(who)=="/secure/login")
+        return 1;
+      return(-1);
+
+    case "configure_driver": 
+      return call_sefun("secure_level") >= ARCH_LVL;
+
+    case "limited":
+      // Spielershells duerfen sich zusaetzliche Ticks fuer den Aufruf von
+      // ::die() beschaffen, damit der Tod nicht wegen wenig Ticks buggt.
+      if (strstr(load_name(who), "/std/shells/") == 0
+          && get_type_info(arg1, 2) == who
+          && get_type_info(arg1, 3) == "/std/living/life"
+//          && get_type_info(arg1, 4) == "die"
+          && arg2[LIMIT_EVAL] <= 10000000 ) {
+          return 1;
+      }
+      //DEBUG(sprintf("%O, %O, %O", who, arg1, arg2));
+      int *limits=efun::driver_info(DI_CURRENT_RUNTIME_LIMITS);
+      // LIMIT_EVAL darf verringert werden. Alle anderen Limits duerfen nicht
+      // geaendert werden. Achja, LIMIT_EVAL darf nicht 0 (LIMIT_UNLIMITED)
+      // sein. *g*
+      // LIMIT_COST darf entweder 0 oder -100 sein.
+      if (arg2[LIMIT_EVAL]>1000 && (arg2[LIMIT_EVAL] < get_eval_cost())-500
+          && arg2[LIMIT_ARRAY] == limits[LIMIT_ARRAY]
+          && arg2[LIMIT_MAPPING_KEYS] == limits[LIMIT_MAPPING_KEYS]
+          && arg2[LIMIT_MAPPING_SIZE] == limits[LIMIT_MAPPING_SIZE]
+          && arg2[LIMIT_BYTE] == limits[LIMIT_BYTE]
+          && arg2[LIMIT_FILE] == limits[LIMIT_FILE]
+          && arg2[LIMIT_CALLOUTS] == limits[LIMIT_CALLOUTS]
+          && (arg2[LIMIT_COST] == 0 || arg2[LIMIT_COST] == -100) )
+            return(1);
+      //sonst verweigern.
+      return(-1);
+
+    case "sqlite_pragma":
+      return 1;
+    case "attach_erq_demon":
+    case "bind_lambda": 
+    case "configure_interactive":
+    case "configure_object":
+    case "execute_command":
+    case "erq": 
+    case "input_to":
+    case "mysql":
+    case "net_connect":
+    case "pgsql":
+    case "set_driver_hook":
+    case "set_this_object":
+    case "shadow_add_action":
+    case "symbol_variable":
+    case "variable_list":
+    case "wizlist_info":
+    default:
+      return(-1);
+  }
+  return -1;
+}
+
+//                       ####################
+//######################## Fehlerbehandlung #############################
+//                       ####################
+
+// Behandlung von Fehler im Heart_Beat
+protected int heart_beat_error( object culprit, string error, string program,
+                     string current_object, int line, int caught)
+{
+  if (!objectp(culprit)) return 0;
+  if (interactive(culprit))
+  {
+    tell_object(culprit,"Der GameDriver teilt Dir mit: "
+                "Dein Herzschlag hat ausgesetzt!\n");
+    if (IS_LEARNER(culprit))
+      tell_object(culprit,
+                  sprintf("Fehler: %sProgamm: %s, CurrObj: %s, Zeile: %d, "
+                    "the error was %scaught at higher level.\n",
+                    error,program,current_object,line,
+                    caught ? "" : "not"));
+  }
+
+  if (efun::object_info(culprit, OI_ONCE_INTERACTIVE))
+    call_out("restart_heart_beat", 5, culprit);
+  else
+    catch(culprit->make_immortal(); publish);
+  return 0;
+}
+
+// Ausgabe einer Meldung an Spieler mit Level >= minlevel. Wird genutzt, um
+// Magier ueber Fehler zu informieren, die nicht normal behandelt werden
+// (z.B. rekursive Fehler, d.h. Fehler in der Fehlerbehandlung)
+private void tell_players(string msg, int minlevel) {
+  msg = efun::sprintf("%=-78s","Master: " + msg); // umbrechen
+  efun::filter(efun::users(), function int (object pl)
+      {
+        if (query_wiz_level(pl) >= minlevel)
+          efun::tell_object(pl, msg);
+        return 0;
+      } );
+}
+
+/**
+ * \brief Error handling fuer Fehler beim Compilieren.
+ *
+ * log_error() wird vom GameDriver aufgerufen, wenn zur Compile-Zeit ein
+ * Fehler auftrat. Die Fehlermeldung wird unter /log/error geloggt.
+ * Falls this_interactive() ein Magier ist, bekommt er die Fehlermeldung
+ * direkt angezeigt.
+ * Sollte das Logfile 50kB ueberschreiten, wird es rotiert. Auf [Entwicklung]
+ * wird dann eine Fehlermeldung abgesetzt.
+ *
+ * @param file Die Datei, in der der Fehler auftrat.
+ * @param message Die Fehlermeldung.
+ * @param warn Warnung (warn!=0) oder Fehler (warn==0)?
+ * */
+
+protected void log_error(string file, string message, int warn) {
+  string lfile;
+  string cr;
+  mixed *lfile_size;
+
+  if (handling_error == efun::time()) {
+    // Fehler im Verlauf einer Fehlerbehandlung: Fehlerbehandlung minimieren,
+    // nur Meldung an eingeloggte Erzmagier.
+    tell_players("log_error(): Rekursiver Fehler in Fehlerbehandlung. "
+        "Bitte Debuglog beachten. Aktueller Fehler in " + file + ": "
+        + message, ARCH_LVL);
+    return;
+  }
+  handling_error = efun::time();
+
+ //Fehlerdaten an den Errord zur Speicherung weitergeben, falls wir sowas
+ //haben.
+#ifdef ERRORD
+  // Only call the errord if we are _not_ compiling the sefuns. It uses sefuns
+  // and it will cause a recursing call to get_simul_efuns() which is bound to
+  // fail.
+  if (!loading_simul_efuns) {
+    catch(limited(#'call_other,({200000}),ERRORD,"LogCompileProblem",
+          file,message,warn);publish );
+  }
+#endif // ERRORD
+
+ // Logfile bestimmen
+  cr=creator_file(file);
+  if (!cr)                     lfile="NOBODY.err";
+  else if (cr==ROOTID)         lfile="ROOT";
+  else if (efun::member(cr,' ')!=-1) lfile="STD";
+  else                         lfile=cr;
+  //je nach Warnung oder Fehler Verzeichnis und Suffix bestimmen
+  if (warn) {
+      lfile="/log/warning/" + lfile + ".warn";
+  }
+  else {
+      lfile="/log/error/" + lfile + ".err";
+  }
+
+  // Bei Bedarf Rotieren des Logfiles
+  if ( !loading_simul_efuns
+      && sizeof(lfile_size = get_dir(lfile,2))
+      && lfile_size[0] >= MAX_ERRLOG_SIZE )
+  {
+    catch(rename(lfile, lfile + ".old"); publish); /* No panic if failure */
+    if (!loading_simul_efuns)
+    {
+      catch(send_channel_msg("Entwicklung","<MasteR>",
+                              "Logfile rotiert: "+lfile+"."));
+    }
+  }
+
+  efun::write_file(lfile,message);
+
+  // Magier bekommen die Fehlermeldung angezeigt
+  if (IS_LEARNER(TI)) efun::tell_object(TI, message);
+
+  handling_error = 0;
+}
+
+/* Gegenmassnahme gegen 'too long eval' im Handler vom runtime error:
+   Aufsplitten des Handlers und Nutzung von limited() */
+//keine GD-Funktion
+private void handle_runtime_error(string err, string prg, string curobj,
+    int line, mixed culprit, int caught, object po, int issueid)
+{
+  string code;
+  string debug_txt;
+  object ob, titp; 
+
+  //DEBUG(sprintf("Callerstack: (handle_runtime_error()): %O\n",
+  //        caller_stack(1)));
+  // Fehlermeldung bauen
+  if (!stringp(err))    err="<NULL>";
+  if (!stringp(prg))    prg="<NULL>";
+  if (!stringp(curobj)) curobj="<NULL>";
+  debug_txt=efun::sprintf("Fehler: %O, Objekt: %s, Programm: %O, Zeile %O (%s), "
+                    "ID: %d",
+                    err[0..<2], curobj, (prg[0]!='<'?"/":"")+prg, line,
+                    (TI?efun::capitalize(efun::getuid(TI)):"<Unbekannt>"),
+                    issueid);
+
+  titp = TI || TP;
+  // Fehlermeldung an Kanaele schicken, aber nur wenn der aktuelle Fehler
+  // nicht waehrend des ladens der simulefuns auftrat, bei der Ausgabe der
+  // Meldung ueber die Kaenaele wieder sefuns genutzt werden.
+  if (!loading_simul_efuns) {
+    if (titp && (IS_LEARNER(titp) || (titp->QueryProp(P_TESTPLAYER))))
+    {
+      catch(send_channel_msg("Entwicklung",
+                             capitalize(objectp(po) ? REAL_UID(po) : ""),
+                             debug_txt));
+    }
+    else
+    {
+      catch(send_channel_msg("Debug",
+                             capitalize(objectp(po) ? REAL_UID(po) : ""),
+                             debug_txt));
+    }
+  }
+
+  if (!titp) return;
+
+  // Fehlermeldung an Benutzer
+  if (IS_LEARNER(titp))
+    efun::tell_object(titp,
+                efun::sprintf("%'-'78.78s\nfile: %s line: %d object: %s\n" +
+                        "%serror: %s%'-'78.78s\n","",prg,line,curobj,
+                        (prg&&(code=efun::read_file("/"+prg,line,1))?
+                         "\n"+code+"\n":""),err,""));
+  else
+    efun::tell_object(titp, "Du siehst einen Fehler im Raum-Zeit-Gefuege.\n");
+}
+
+//Keine GD-Funktion, limitiert die Kosten fuer den handler
+private void call_runtime_error(string err, string prg, string curobj,
+    int line, mixed culprit, int caught, object po)
+{
+  if (handling_error == efun::time())
+  {
+    // Fehler im Verlauf einer Fehlerbehandlung: Fehlerbehandlung minimieren,
+    // nur Meldung an eingeloggte Regionsmagier.
+    tell_players("call_runtime_error(): Rekursiver Fehler in "
+        "Fehlerbehandlung. Bitte Debuglog beachten. Aktueller Fehler in "
+        + curobj + ": " + err, LORD_LVL);
+    return;
+  }
+  handling_error = efun::time();
+
+  //Fehlerdaten an den Errord zur Speicherung weitergeben, falls wir sowas
+  //haben.
+#ifdef ERRORD
+  int issueid;
+  // Wenn die sefuns gerade geladen werden, erfolgt keine Weitergabe an den
+  // ErrorD, da dabei wieder sefuns gerufen werden, was zu einer Rekursion
+  // fuehrt.
+  if (!loading_simul_efuns) {
+    catch(issueid=efun::limited(#'call_other,({200000}),ERRORD,"LogError",
+          err,prg,curobj,line,culprit,caught);publish);
+  }
+#endif // ERRORD
+
+  //eigenen Errorhandler laufzeitbegrenzt rufen
+  efun::limited(#'handle_runtime_error, ({ 200000 }), err, prg, curobj,
+        line,culprit,caught, po, issueid);
+
+  handling_error = 0;
+}
+
+// Laufzeitfehlerbehandlung, hebt Evalcost-Beschraenkung auf und reicht weiter
+// an call_runtime_error, die die Grenze wieder auf einen bestimmten Wert
+// festlegt.
+protected void runtime_error(string err ,string prg, string curobj, int line,
+     mixed culprit, int caught) {
+
+    //DEBUG(sprintf("Callerstack: (runtime_error()): %O\nPO: %O\nPO(0): %O\n",
+    //          caller_stack(1),previous_object(),previous_object(0)));
+
+    limited(#'call_runtime_error, ({ LIMIT_UNLIMITED }),
+        err,prg,curobj,line,culprit,caught,previous_object());
+}
+
+//Warnungen mitloggen
+protected void runtime_warning( string msg, string curobj, string prog, int line,
+    int inside_catch)
+{
+  string code;
+  string debug_txt;
+  object titp;
+
+  //DEBUG(sprintf("Callerstack: in runtime_warning(): %O\nPO(0): %O\n",
+  //        caller_stack(1),previous_object(0)));
+
+  //Daten der Warnung an den Errord zur Speicherung weitergeben, falls wir 
+  //sowas haben.
+  int issueid;
+#ifdef ERRORD
+  catch(issueid=limited(#'call_other,({200000}),ERRORD,"LogWarning",
+        msg,prog,curobj,line,inside_catch); publish);
+#endif // ERRORD
+
+  // Fehlermeldung bauen
+  if (!stringp(msg))    msg="<NULL>";
+  if (!stringp(prog))    prog="<NULL>";
+  if (!stringp(curobj)) curobj="<NULL>";
+  debug_txt=sprintf("Warnung: %O, Objekt: %s, Programm: %O, Zeile %O (%s) "
+                    "ID: %d",
+                    msg[0..<2], curobj, (prog[0]!='<'?"/":"")+prog, line,
+                    (TI?capitalize(getuid(TI)):"<Unbekannt>"),
+                    issueid);
+
+  //Fehlermeldungen an -warnungen schicken
+  catch(send_channel_msg("Warnungen",
+                         capitalize(objectp(previous_object()) ?
+                                    REAL_UID(previous_object()) : ""),
+                         debug_txt));
+
+  titp = TI || TP;
+
+  if (!titp) return;
+
+  // Fehlermeldung an Benutzer
+  if (IS_LEARNER(titp))
+    tell_object(titp,
+                sprintf("%'-'78.78s\nfile: %s line: %d object: %s\n" +
+                        "%sWarnung: %s%'-'78.78s\n","",prog,line,curobj,
+                        (prog&&(code=read_file("/"+prog,line,1))?
+                         "\n"+code+"\n":""),msg,""));
+  return;
+}
+
+
+//                       #####################
+//######################## Einrichten des ED ############################
+//                       #####################
+
+// Setup speichern
+protected int save_ed_setup(object who, int code)
+{
+  string file;
+
+  if (!intp(code)) return 0;
+  file = sprintf("/players/%s/.edrc",geteuid(who));
+  rm(file);
+  return write_file(file,(string)code);
+}
+
+// Setup einladen
+protected int retrieve_ed_setup(object who)
+{
+  string file;
+
+  file = sprintf("/players/%s/.edrc",getuid(who));
+  if (file_size(file)<1) return 0;
+  return (int)read_file(file);
+}
+
+// Wo werden Dateien gespeichert?
+string get_ed_buffer_save_file_name(string file)
+{
+  return sprintf("/players/%s/.dead_ed_files/%s",
+                 getuid(this_player()),efun::explode(file, "/")[<1]);  
+}
+
+//                  ################################
+//################### Ungenutzte Pflichtfunktionen ######################
+//                  ################################
+
+// Nichts zu tun beim Master-Neustart
+protected void external_master_reload()  { return; }
+
+// Keine externen Master-Flags
+protected void flag(string str) { return; }
+
+// Wird benoetigt, falls ERQ angeschlossen ist
+protected void stale_erq(closure callback) { return; }
+
+// Zerstoerten Master wiederbeleben ...
+// nicht viel zu tun, wenn flag gesetzt ist. Wenn aber nicht, sind alle
+// Variablen auf 0. Nach dieser Funktion wird wieder inaugurate_master()
+// gerufen.
+protected void reactivate_destructed_master(int flag) {
+    if (flag) {
+        //re-init
+        //was machen? TODO
+    }
+    return;
+}
+
+// Kein Quota-Demon (potentielles TODO)
+//Handle quotas in times of memory shortage.
+//is called during the final phase of a garbage collection, if the user
+//reserve could not be re-allocated. Last (!) Chance to free (active) objects 
+//from the system and prevent call to slow_shut_down()!
+protected void quota_demon() {
+    //was kann man machen?
+    //uebervolle Seherhaeuser leeren? 
+    return;
+}
+
+// Keine Prepositionen in parse_command
+//string *parse_command_prepos_list() { return ({}); }
+
+// Wie lautet das Wort fuer 'alle' ?
+//string *parse_command_all_word() { return ({}); }
+
+
+// Keine Besonderen Objektnamen
+string printf_obj_name(object ob) { return 0; }
+
+//                        ###################
+//######################### Sonstiges/Hooks #############################
+//                        ###################
+
+//TODO: save_wiz_file in shutdown() integrieren?
+// Wizliste speichern: Zeile generieren
+static string _save_wiz_file_loop(mixed *a)
+{
+  return sprintf("%s %d %d\n",a[WL_NAME],a[WL_COMMANDS],a[WL_EXTRA]);
+}
+
+// Wizliste speichern
+protected void save_wiz_file()
+{
+  rm("/WIZLIST");
+  write_file("/WIZLIST",implode(
+     map(wizlist_info(),#'_save_wiz_file_loop),""));
+}
+
+// TODO: macht der telnet_neg-hook ueberhaupt was?
+void telnet_neg(mixed cmd,mixed opt,mixed args)
+{
+  if (opt==34 && cmd==251)
+        binary_message(({255,250,34,1,1,255,240}));
+}
+  
+// EUID und UID werden von give_uid_to_object() vergeben, diese sind in
+// inaugurate_master() als driver hooks angemeldet.
+
+/*load_uid_hook() sollte machen, was diese Lambda machte...
+  unbound_lambda( ({'printf_obj_name}),
+               ({#'give_uid_to_object,'printf_obj_name,
+                   ({#'previous_object}),0})));   */
+protected mixed load_uid_hook(string datei) {
+    return(give_uid_to_object(datei,previous_object()));
+}
+
+/* clone_uid_hook sollte machen, was diese Lambda machte...
+            unbound_lambda( ({'blueprint, 'new_name}), 
+              ({#'give_uid_to_object,'new_name,
+                  ({#'previous_object}),1})));      */
+protected mixed clone_uid_hook(string blueprint,string new_name) {
+    return(give_uid_to_object(new_name,previous_object()));
+}
+
diff --git a/secure/master.h b/secure/master.h
new file mode 100644
index 0000000..f97b0d0
--- /dev/null
+++ b/secure/master.h
@@ -0,0 +1,126 @@
+// MorgenGrauen MUDlib
+//
+// master.h -- definitions for the master object
+//
+// $Id: master.h 8809 2014-05-08 19:52:48Z Zesstra $
+
+#ifndef __MASTER_H__
+#define __MASTER_H__
+
+
+#ifdef P_ARMOURS
+#undef P_ARMOURS
+#endif
+#define P_ARMOURS "armours"
+
+#ifdef P_WEAPON
+#undef P_WEAPON
+#endif
+#define P_WEAPON "weapon"
+
+//fuer den notify_fail-Hook
+#ifndef P_DEFAULT_NOTIFY_FAIL
+#define P_DEFAULT_NOTIFY_FAIL "default_notify_fail"
+#endif
+
+#include "/secure/config.h"
+
+#include "/sys/userinfo.h"
+#include "/sys/shells.h"
+#include "/sys/player/base.h"
+#include "/sys/moving.h"
+#include "/sys/defines.h"
+#include "/sys/wizlist.h"
+#include "/sys/daemon.h"
+#include "/sys/mail.h"
+#include "/sys/driver_hook.h"
+#include "/sys/functionlist.h"
+
+#include "/secure/wizlevels.h"
+
+//#define DEBUG(x)
+#ifndef DEBUG
+#define DEBUG(x)	if (funcall(symbol_function('find_player),"zesstra"))\
+        tell_object(funcall(symbol_function('find_player),"zesstra"),\
+        "MDBG: "+x+"\n")
+#endif
+
+//Logfile fuer Lesezugriffe
+#define READ_FILE  "/log/ARCH/READ_FILE"
+
+//Logfile fuer Crashes
+#define CRASH_LOG  "/log/CRASH_LOG"
+
+// MAX_ERRLOG_SIZE definiert, ab welcher Groesse ein Log in
+// /log/error rotiert.
+#define MAX_ERRLOG_SIZE 51200 
+
+#define log_file(file,str) write_file("/log/"+file, str)
+#define NAME(x)            getuid(x)
+#define CAP_NAME(x)        capitalize(NAME(x))
+#define TP                 efun::this_player()
+#define TI                 efun::this_interactive()
+#define PO                 efun::previous_object()
+#define TO                 efun::this_object()
+
+// Verzeichnis mit Infomails fuer Magierbefoerderungen
+#define WIZ_HELP_MAIL_DIR "/doc/infomails/"
+
+// aus master.c
+string        creator_file(mixed str);
+mixed         give_uid_to_object(string datei,object po);
+void          save_wiz_file();
+mixed         load_uid_hook(string datei);
+mixed         clone_uid_hook(string blueprint,string new_name);
+protected mixed call_sefun(string sefun, varargs mixed args);
+
+// aus domains.c
+int            domain_master(string user, string domain);
+int            domain_member(string user, string domain);
+string         *get_domain_homes(string wiz);
+
+// aus guild.c
+int           guild_master(string user, string guild);
+
+// aus file_access.c
+void          LoadDeputyFileList();
+mixed         valid_read(string path, string euid, string fun, object obj);
+mixed         valid_write(string path, string euid, string fun, object obj);
+
+// aus misc.c
+string         _get_path(string path, string user);
+mixed          QueryBanished(string str);
+int            TBanishName(string name, int days);
+
+// sendet bei Befoerderungen Infomail an Magier
+protected void SendWizardHelpMail(string name, int level);
+
+// aus userinfo.c
+public int       find_userinfo(string user);
+protected mixed *get_full_userinfo(string user);
+public mixed    *get_userinfo(string user);
+public int       get_wiz_level(string user);
+public string    query_player_object( string name );
+public int       query_wiz_level(mixed player);
+int              update_wiz_level(string user,int lev);
+protected void   set_guilds(string player, string *guilds);
+protected void   set_domains(string player, string *domains);
+void             update_late_players();
+public string    secure_savefile(string name);
+protected void   save_userinfo(string user);
+protected string query_secure_euid();
+protected int    query_secure_level();
+public varargs string* QueryUIDAlias(string alias, int rec);
+
+//Einen Magier als verantwortlich fuer eine bestimmte UID eintragen
+public string*   AddWizardForUID(string uid, string wizuid);
+//Einen Magier wieder austragen, wenn er nicht mehr zustaendig ist.
+public string*   RemoveWizardFromUID(string uid, string wizuid);
+//Verantwortliche Magier fuer eine UID ausgeben
+public varargs string* QueryWizardsForUID(string uid, int recursive);
+//Welche UIDs ist ein Magier implizit verantwortlich? (Ist er RM,
+//Gildenmagier, in welchen Regionen hat er ein Verzeichnis, etc.)
+public varargs string* QueryUIDsForWizard(string wizuid, int recursive);
+
+
+#endif //__MASTER_H__
diff --git a/secure/master/autoinclude.c b/secure/master/autoinclude.c
new file mode 100644
index 0000000..e0002fa
--- /dev/null
+++ b/secure/master/autoinclude.c
@@ -0,0 +1,101 @@
+// MorgenGrauen MUDlib
+//
+// secure/master/autoinclude.c -- module of the master object: Autoincludes
+//
+// $Id: master.c 7041 2008-10-13 18:18:27Z Zesstra $
+
+#define PRAGMA(x) "#pragma "x"\n"
+
+// fuer alte Homemuds...
+#if __VERSION__ >= "3.5.0"
+#define RTTCHECKS PRAGMA("rtt_checks")
+#define DEFAULTS PRAGMA("save_types")
+#else
+#define RTTCHECKS ""
+#define DEFAULTS PRAGMA("combine_strings, verbose_errors, warn_deprecated")
+#endif
+
+// geschachteltes Mapping in toplevel.region.magier Hierarchie.
+// Wichtig: jede Hierarchiebene _muss_ ein Mapping sein, welches einen Eintrag
+//          0 als Default enthaelt, welcher einen Strings als Wert hat.
+//          Ausnahme: die letzte Ebene (Magierebene), die muss ein String ein.
+private nosave mapping autoincludes = ([
+    "d":      ([
+                 "inseln": ([
+                             0: "",
+                             "zesstra": PRAGMA("strong_types") RTTCHECKS,
+                            ]),
+                 0: "",
+               ]),
+    "std":    ([
+                 0: PRAGMA("strong_types,pedantic") RTTCHECKS,
+               ]),
+    "items":    ([
+                 0: PRAGMA("strong_types,pedantic") RTTCHECKS,
+               ]),
+    "secure": ([
+                 0: PRAGMA("strong_types,range_check,pedantic") RTTCHECKS,
+               ]),
+    "p":      ([
+                 0: "",
+                 "daemon": ([
+                             0: PRAGMA("strong_types") RTTCHECKS
+                            ]),
+                 "service": ([
+                              0: ""
+                            ]),
+               ]),
+    0: DEFAULTS,
+]);
+
+string autoincludehook(string base_file, string current_file, int sys_include)
+{
+  mapping topleveldir, region; // mappings for spezialisiertere Pfade
+  string ainc_string;
+
+  // Wenn current_file != 0 ist, wird gerade vom kompilierten Objekt
+  // <base_file> etwas (indirekt) inkludiert. Dort duerfen die Pragmas
+  // keinesfalls reingeschrieben werden.
+  if (current_file)
+    return 0;
+
+  string res=autoincludes[0]; // global default.
+
+  string *p_arr = explode(base_file,"/")-({""});
+  //DEBUG(sprintf("AINC: File: %O, Pfad: %O\n",base_file, p_arr));
+
+  if (sizeof(p_arr) && m_contains(&topleveldir, autoincludes, p_arr[0])) {
+    // p_arr[0]: d, p, std, etc. 
+    // erst wird der Defaulteintrag 0 genommen
+    res += topleveldir[0];
+    if (sizeof(p_arr) > 1 && m_contains(&region, topleveldir, p_arr[1])) {
+      // p_arr[1] ebene, polar, unterwelt, service, ...
+      // erst den Defaulteintrag der Region nehmen
+      res += region[0];
+      if (sizeof(p_arr) > 2 && m_contains(&ainc_string, region, p_arr[2])) {
+        // p_arr[2]: magiername. Fuer den Magier was hinterlegt.
+        res += ainc_string;
+      }
+    }
+  }
+  // Fuer aeltere Files schalten wir einige Warnungen explizit aus. :-(
+  // (1407179680 == "Mon,  4. Aug 2014, 21:14:40")
+#if MUDHOST == __HOST_NAME__
+  if (call_sefun("file_time", base_file) < 1407179680) {
+      res += PRAGMA("no_warn_missing_return");
+  }
+#else
+  // Auf anderen Rechnern als dem Mudrechner werden die Warnungen unabhaengig
+  // vom Zeitpunt der letztes Aenderung abgeschaltet, weil bei kopierten
+  // Mudlibs oft die mtimes geaendert werden und dann auf einmal alles scrollt.
+  res += PRAGMA("no_warn_missing_return");
+
+#endif
+  //DEBUG(res);
+  return res;
+}
+#undef RTTCHECKS
+#undef DEFAULTS
+#undef PRAGMA
+
+
diff --git a/secure/master/cidr.c b/secure/master/cidr.c
new file mode 100644
index 0000000..c35ba21
--- /dev/null
+++ b/secure/master/cidr.c
@@ -0,0 +1,111 @@
+// MorgenGrauen MUDlib
+/** \file /secure/master/cidr.c
+* Funktionen zur Interpretation und Umrechnung von IP-Adressen in CIDR-Notation.
+* \author Zesstra
+* \date 05.02.2010
+* \version $Id$
+*/
+/* Changelog:
+*/
+
+private int IPv4_addr2int(string addr) {
+    int mask;
+
+    if (!stringp(addr) || !sizeof(addr))
+      return 0;
+    // irgendwelche Zeichen ausser 0-9, . und / drin?
+    if (sizeof(addr-"0123456789./"))
+      return 0;
+
+    string *parts = explode(addr, "/") - ({""});
+    // Netzmaske gegeben? -> Netzwerkadresse bestimmen
+    if (sizeof(parts) == 2) {
+        // a.b.c.d/x oder a.b.c.d/w.x.y.z
+        int res = IPv4_addr2int(parts[0]); // erstmal Wert von a.b.c.d...
+        if (strstr(parts[1], ".") > -1
+            && sizeof(explode(parts[1],".")) == 4)
+            // a.b.c.d/w.x.y.z -> Wert von w.x.y.z bestimmen
+          mask = IPv4_addr2int(parts[1]);
+        else {
+          mask = to_int(parts[1]);
+          // Wert von /x
+          mask = 0xffffffff - (to_int(pow(2, 32-mask))-1);
+        }
+        // verunden und Ende.
+        return res & mask;
+    }
+
+    parts = explode(parts[0], ".");
+    // Abgrekuerzte Adresse a la a.b.? Rest mit 0 ergaenzen.
+    switch(sizeof(parts - ({""})) ) {
+      case 1:
+        parts += ({"0","0","0"});
+      case 2:
+        parts += ({"0","0"});
+      case 3:
+        parts += ({"0"});
+      case 4:
+        break;
+      default:
+        return 0;
+    }
+
+   return( to_int(parts[0]) << 24)
+         + ( to_int(parts[1]) << 16)
+         + ( to_int(parts[2]) << 8 )
+         + to_int(parts[3]);
+}
+
+private int IPv4_net_size(string addr) {
+    if (!stringp(addr) || !sizeof(addr))
+      return 0;
+    // irgendwelche Zeichen ausser 0-9, . und / drin?
+    if (sizeof(addr-"0123456789./"))
+      return 0;
+
+    string *parts = explode(addr, "/") - ({""});
+    // Netzmaske gegeben? -> Netzwerkadresse bestimmen
+    if (sizeof(parts) == 2) {
+        // a.b.c.d/x oder a.b.c.d/w.x.y.z
+        if (strstr(parts[1], ".") > -1
+            && sizeof(explode(parts[1],".")) == 4)
+            // a.b.c.d/w.x.y.z 
+          return 0xffffffff - (IPv4_addr2int(parts[1]) || 1) + 1;
+        else {
+          // Einfach 2^(32-mask) Adressen.
+          return to_int(pow(2, 32 - to_int(parts[1]) ) );
+        }
+    }
+
+    parts = explode(parts[0], ".");
+    switch(sizeof(parts - ({""})) ) {
+      case 1: // Class A
+        return 256*256*256;
+      case 2: // Class B
+        return 256*256;
+      case 3: // Class C
+        return 256;
+      case 4: // einzelne Adresse
+        return 1;
+      default: // ungueltige Adresse
+        return __INT_MAX__;
+    }
+
+    // hier sollte man gar nicht ankommen.
+    return __INT_MAX__;
+}
+
+string IPv4_int2addr(int ip) {
+    int *parts=allocate(4);
+    parts[0] = (ip & 0xff000000) >> 24;
+    parts[1] = (ip & 0x00ff0000) >> 16;
+    parts[2] = (ip & 0x0000ff00) >> 8;
+    parts[3] = (ip & 0x000000ff);
+    // int is signed. Anything > 127 would wrap around to -128 if we are on a
+    // machine with 32-bit wide ints.
+    if (parts[0] < 0)
+        parts[0] = 128 + (128-abs(parts[0]));
+
+    return implode(map(parts,#'to_string), ".");
+}
+
diff --git a/secure/master/destruct.c b/secure/master/destruct.c
new file mode 100644
index 0000000..7e76a11
--- /dev/null
+++ b/secure/master/destruct.c
@@ -0,0 +1,105 @@
+// MorgenGrauen MUDlib
+//
+// secure/master/destruct.inc -- module of the master object: stuff for destruct.
+//
+// $Id: master.c 7041 2008-10-13 18:18:27Z Zesstra $
+
+#include "/sys/object_info.h"
+
+// privilegierte Objekte, die das destruct() abbrechen duerfen (root objekte
+// duerfen auch ohne, dass sie in dieser Liste erfasst sind):
+private nosave string *deny_destruct_list = ({
+    "/obj/shut", "/room/void", "/room/netztot", "/room/jail" });
+
+// Helferfunktion fuer prepare_destruct()
+private void recursive_remove(object ob, int immediate_destruct) {
+
+  if (efun::object_info(ob, OI_ONCE_INTERACTIVE)) {
+    // Spieler werden ins Void bewegt.
+    int res;
+    tell_object(ob, "Ploetzlich loest sich deine Welt in ihre " +
+                  "Bestandteile auf. Zum Glueck wirst\nDu irgendwo " +
+                  "hin geschleudert ...\n");
+    // wenn Bewegung buggt oder nicht funktioniert und ob noch existiert,
+    // rekursiv zerstoeren.
+    object oldenv=environment(ob);
+    if ( (catch(res=(int)ob->move("/room/void",M_TPORT|M_NOCHECK,0,"faellt");
+           publish) || (ob && environment(ob) == oldenv) )
+          && ob) {
+            // Spieler speichern, dann erst Inventar entleeren, dann remove() und
+        // und destruct() anwenden.
+        catch(ob->save_me(1); publish);
+        filter(all_inventory(ob), #'recursive_remove, immediate_destruct);
+        if (!immediate_destruct) 
+          catch(ob->remove(0);publish);
+        if (ob) 
+          destruct(ob);
+    }
+  }
+  else {
+    // kein Spieler. Rekursiv entfernen. Hierbei _zuerst_ rekursiv das
+    // Inventar entfernen und dann ob selber, damit nicht erst das Inventar in
+    // das Environment bewegt wird (soll ja eh zerstoert werden).
+    filter(all_inventory(ob), #'recursive_remove, immediate_destruct);
+    // ggf. zuerst remove versuchen
+    if (!immediate_destruct)
+      catch(ob->remove(1);publish);
+    if (ob)
+      destruct(ob);
+  }
+}
+
+// Zerstoerung von ob vorbereiten
+protected mixed prepare_destruct(object ob)
+{
+  object old_env,env,item;
+  mixed res;
+
+  // zuerst das notify_destruct() rufen und ggf. abbrechen, falls ob
+  // privilegiert ist.
+  catch(res = (mixed)ob->NotifyDestruct(previous_object()); publish);
+  if (res &&
+      (getuid(ob) == ROOTID ||
+       (IS_ARCH(ob)) ||
+       member(deny_destruct_list, object_name(ob)) >= 0)) {
+    if (stringp(res) && sizeof(res))
+      return res;
+    else
+      return sprintf("%O verweigert die Zerstoerung mittels destruct(). "
+          "Fehlende Rechte von %O?\n",ob, previous_object());
+  }
+  
+  env = environment(ob);
+
+  // Objekt hat kein Env: Alles zerstoeren, Spieler ins Void
+  if (!env) {
+    filter(all_inventory(ob), #'recursive_remove, 1);
+  }
+  else {
+    // Ansonsten alles ins Environment 
+    foreach(item : all_inventory(ob))
+    {
+      old_env=environment(item);
+      // M_MOVE_ALL, falls item nen Unitobjekt ist. Sonst clonen die u.U. noch
+      // wieder nen neues Objekt im alten Env.
+      if(catch(item->move(env, M_NOCHECK|M_MOVE_ALL);publish))
+        recursive_remove(item, 1);
+      else if (item && environment(item) == old_env)
+        recursive_remove(item, 1);
+    }
+  }
+
+  return 0; // Erfolg
+}
+
+// Anmerkung: liefert 0 zurueck, wenn die sefuns gerade geladen werden.
+string NotifyDestruct(object caller) {
+  // Nicht jeder Magier muss den Master entsorgen koennen.
+  if ((caller != this_object() && 
+        call_sefun("secure_level") < ARCH_LVL)
+      || call_sefun("process_call") ) {
+    return "Du darfst den Mudlib-Master nicht zerstoeren!\n";
+  }
+  return 0;
+}
+
diff --git a/secure/master/domain.c b/secure/master/domain.c
new file mode 100644
index 0000000..99ec6f6
--- /dev/null
+++ b/secure/master/domain.c
@@ -0,0 +1,95 @@
+// MorgenGrauen MUDlib
+//
+// master/domain.c -- Regionen und Regionsmagier
+//
+// $Id: domain.c 7162 2009-02-26 21:14:43Z Zesstra $
+
+#pragma strict_types
+
+#include "/secure/master.h"
+#include "/sys/files.h"
+
+int domain_master(string user, string domain)
+{
+  string *domains;
+  int i;
+  
+  if (!find_userinfo(user)||
+      !pointerp(domains=get_userinfo(user)[USER_DOMAIN+1]))
+    return 0;
+  return (member(domains,domain) != -1);
+}
+
+int domain_member(string user, string domain)
+{
+  if (domain=="erzmagier") return 0;
+  return (IS_DOMAINMEMBER(user)&&file_size("/d/"+domain+"/"+user)==-2);
+}
+
+int add_domain_master(string user,string dom)
+{
+  string *domains;
+  
+  if ((call_other(SIMUL_EFUN_FILE, "process_call") ||
+        call_other(SIMUL_EFUN_FILE, "secure_level") < GOD_LVL) ||
+        !find_userinfo(user))
+    return 0;
+  domains=get_userinfo(user)[USER_DOMAIN+1];
+  if (!domains) 
+    set_domains(user,({dom}));
+  else
+  {
+    // Doppelte Eintraege vermeiden
+    domains=domains-({dom})+({dom});
+    set_domains(user, domains);
+  }
+  if (get_wiz_level(user) < LORD_LVL+5) {
+    update_wiz_level(user,LORD_LVL+5);
+    SendWizardHelpMail(user, LORD_LVL+5);
+  }
+  return 1;
+}
+
+int remove_domain_master(string user,string dom)
+{
+  string *domains;
+
+  if (!IS_GOD(geteuid(previous_object()))
+      ||!find_userinfo(user)
+      ||!(domains=get_userinfo(user)[USER_DOMAIN+1])
+      || member(domains,dom)==-1)
+    return 0;
+  domains-=({dom});
+  set_domains(user,domains);
+  return 1;
+}
+
+string *get_domains() {
+  string *regions=({});
+  // alle Verzeichnisse in /d/ ermitteln
+  foreach(string region: (get_dir("/"DOMAINDIR"/*") 
+        || ({}))-({".","..","erzmagier"})) {
+    if (region[0]!='.' && file_size("/d/"+region) == FSIZE_DIR)
+        //Verzeichnis, kein File und faengt nicht mit "." an: aufnehmen.
+        regions+=({region});
+  }
+  return regions;
+}
+
+// in welchen Regionen hat 'wiz' ein Verzeichnis?
+string *get_domain_homes(string wiz)
+{
+  string *homes=({});
+  string tmp;
+  
+  if (query_wiz_level(wiz)<=WIZARD_LVL) return ({});
+
+  tmp = "/d/%s/"+wiz;
+  foreach(string dir: get_domains()) {
+      if (dir[0]!='.' && file_size(sprintf(tmp,dir)) == FSIZE_DIR)
+          //Magierverzeichnis da und faengt nicht mit "." an: aufnehmen.
+          homes+=({dir});
+  }
+  return homes;
+}
+
diff --git a/secure/master/file_access.c b/secure/master/file_access.c
new file mode 100644
index 0000000..5b7e3e4
--- /dev/null
+++ b/secure/master/file_access.c
@@ -0,0 +1,617 @@
+#pragma strict_types, no_warn_deprecated
+
+#include "/secure/master.h"
+
+static mapping projects=([]);
+static string *insecure,*deputy_files;
+
+int ReloadInsecureFile()
+{
+    insecure = efun::explode( read_file("/secure/INSECURE") || "", "\n" );
+    insecure -= ({""});
+    insecure = map( insecure, #'lower_case/*'*/ );
+    return(1);
+}
+
+void LoadDeputyFileList()
+{
+  // Leseberechtigungen fuer /log/ARCH/* setzen
+  deputy_files = efun::explode( read_file("/log/ARCH/DEPUTY_FILELIST") || "",
+                                "\n" ) - ({""});
+  return;
+}
+
+#define PATH_ARRAY(x) (explode(x, "/")-({"."}))
+string *full_path_array(string path, string user) {
+  string *strs;
+  string cwd;
+
+  if(!path)
+    return ({"",""}); // additional "" to yield "/" later.
+
+  // remove multiple '/'
+  path = regreplace(path, "/+", "/", 1);
+
+  switch(path[0]) {
+    case '/':
+      if(!path[1]) return ({"",""}); //additional "" to yield "/" later
+      strs=PATH_ARRAY(path);
+      if (!sizeof(strs))
+          strs = ({"",""});
+      break;
+    case '+':
+      if(!path[1]) return ({"","d"});
+      strs=({"","d"})+PATH_ARRAY(path[1..<1]);
+      break;
+    case '~':
+      if(user && !path[1])
+        return ({"","players",user});
+      if(user && path[1]=='/')
+        strs=({"","players",user})+PATH_ARRAY(path[2..<1]);
+      else
+        strs=({"","players"})+PATH_ARRAY(path[1..<1]);
+      break;
+    default:
+      if(user && TP && getuid(TP) == user
+          && (cwd=(string)TP->QueryProp(P_CURRENTDIR)))
+        strs=PATH_ARRAY(cwd + "/" + path);
+      else
+        strs=PATH_ARRAY(path);
+  }
+  // /../ behandeln. Einschraenkungen der RegExp:
+  // /a/b/../../d wuerde nur durch Wiederholung aufgeloest.
+  // /../d wird nicht richtig aufgeloest.
+  //regreplace("/d/inseln/toeter/room/hoehle/../wald/bla.c","(.*)\/[^/]+/\.\.
+  //    /(.*)", "\\1\/\\2", RE_GLOBAL)
+  
+  // dies sieht schlimmer aus als es ist (member ist O(n)), solange das Array
+  // nicht gross wird.
+  int p;
+  while((p=member(strs, "..")) != -1)
+      strs = strs[0..p-2]+strs[p+1..];
+
+  return strs;
+}
+#undef PATH_ARRAY
+
+string _get_path(string path, string user) {
+  string *p_arr = full_path_array(path, user);
+  // make path absolute
+  if (p_arr[0] != "")
+      p_arr = ({""}) + p_arr;
+
+  return implode(p_arr,"/");
+}
+
+static int project_access(string user, string project)
+{
+  mixed *lines;
+  string s;
+  mapping tmp;
+
+  if (!member(projects,project))
+  {
+    s=read_file(PROJECTDIR+"/"+project+"/ACCESS_RIGHTS");
+    if(!s||s=="")
+      return 0;
+    tmp=([]);
+    for (lines=explode(s,"\n")-({""});sizeof(lines);lines=lines[1..])
+    {
+      if (lines[0][0]=='*')
+        tmp[lines[0][1..]]=2;
+      else
+        tmp[lines[0]]=1;
+    }
+    projects[project]=({tmp,time()});
+    return tmp[user];
+  }
+  projects[project][1]=time();
+  return projects[project][0][user];
+}
+
+void OutdateProjectCache(string project)
+{
+  m_delete(projects,project);
+}
+
+static void _cleanup_projects() {
+  int i;
+  mixed *users;
+
+  for (users=m_indices(projects),i=sizeof(users)-1;i>=0;i--)
+    if((time()-projects[users[i]][1])>1800)
+      m_delete(projects,users[i]);
+}
+
+int access_rights(string *p_arr, string euid)
+{
+  int i;
+
+  i = sizeof(p_arr) - 2;
+
+  while ( i >= 0 &&
+          file_size( implode(p_arr[0..i], "/") + "/access_rights.c" ) < 0 )
+      i--;
+
+  if ( i < 0 )
+      return 0;
+
+  if ( !catch(i = (int)call_other( 
+          implode(p_arr[0..i], "/") + "/access_rights",
+          "access_rights", euid,
+          implode(p_arr[i+1..], "/") ); publish) )
+      return i;
+
+  return 0;
+}
+
+string make_path_absolute(string str) {
+  return _get_path(str, getuid(TP));
+}
+
+
+mixed valid_write(string path, string euid, string fun, object obj)
+{
+  int s,lvl;
+  string *strs;
+  int *date;
+
+  if (member(path,' ')!=-1) return 0;
+
+  // Unter LIBDATADIR (/data) sollen komplett identische Schreibrechte
+  // vergeben werden.
+  if (sizeof(path) > 6
+      && path[0..5] == "/"LIBDATADIR"/")
+    return valid_write(path[5..], euid, fun, obj) != 0;
+
+  switch(fun) {
+    case "log_file":
+      strs=full_path_array("/"+path, 0);
+      path = implode(strs, "/");
+      strs -= ({""}); // remove trailing and leading "/".
+      if (sizeof(strs)>1 && strs[0]=="log") return path;
+      return 0;
+    case "save_object":
+      if (!path) return 0;
+      strs=full_path_array("/"+path, 0);
+      break;
+    case "ed_start":
+      if (path && path[0])
+        strs=full_path_array(path, euid);
+      else strs=({"players",euid,".err"});
+      break;
+    default:
+      strs=full_path_array(path, euid);
+  }
+
+  if (!euid || euid=="NOBODY" || euid=="ftp" || euid=="anonymous") return 0;
+
+  // Pfad soll ab jetzt auf jeden Fall absolut sein.
+  if (strs[0] != "")
+      path = "/" + implode(strs, "/");
+  else
+      path=implode(strs, "/");
+
+  // fuer die Rechtebestimmung "/" an Anfang und Ende entfernen
+  strs -= ({""});
+
+  //Root-Objekte und Master duerfen immer.
+  if (euid == ROOTID || obj==TO) return path;
+
+  lvl=query_wiz_level(euid);
+
+  //Toplevel: nur EM+
+  if ((s=sizeof(strs))<=1) return lvl>=ARCH_LVL;
+
+  switch(strs[0]) {
+    case TMPDIR:
+      return 1;
+
+    case STDDIR:
+    case SYSDIR:
+    case LIBOBJDIR:
+    case ETCDIR:
+    case LIBROOMDIR:
+      return lvl>=ARCH_LVL;
+    
+    case LIBITEMDIR:
+      return lvl>=ELDER_LVL;
+
+    case SPELLBOOKDIR:
+      // wenn unterhalb eines unterverzeichnisses des Stils "gilde", dann ists
+      // einfach
+      if (sizeof(strs) > 2
+          && guild_master(euid, strs[1]))
+        return 1;
+
+      // sonst nur EMs bzw. access_rights.c fragen.
+      return ((lvl>=ARCH_LVL) || access_rights(strs,euid));
+
+    case GUILDDIR:
+      // wenn unterhalb eines unterverzeichnisses des Stils "filde.gilde",
+      // dann ists einfach
+      if (sizeof(strs) > 2) {
+        string *tmp = explode(strs[1],"files.");
+        if (sizeof(tmp) == 2) {
+          if (guild_master(euid, tmp[1]))
+            return 1;
+        // Objekte dort sollen auch schreiben duerfen.
+          if (euid == ("GUILD."+tmp[1]))
+            return 1;
+        }
+      }
+      // sonst nur EMs bzw. access_rights.c fragen.
+      return ((lvl>=ARCH_LVL) || access_rights(strs,euid));
+
+    case DOCDIR:
+      if ( s > 1 && (strs[1] == "balance") )
+        return ((lvl>=ARCH_LVL) || access_rights(strs,euid));
+      else
+        return ((lvl>=SPECIAL_LVL) || access_rights(strs,euid));
+
+    case FTPDIR:
+        if ( s > 1 && (strs[1] == "incoming" || strs[1] == "tmp" ||
+                       s == 3 && strs[1] == "News" && strs[2] == euid+".news") )
+            return 1;
+
+        if (lvl>=ELDER_LVL)
+            return 1;
+
+        return 0;
+
+    case MAILDIR:
+      if (euid==MAILID || strs[1]=="spool") break;
+      return 0;
+
+    case LIBSAVEDIR:
+      if (lvl>=ARCH_LVL) return 1;
+      if (s==3 && strs[1] == euid[0..0] &&
+          (strs[2]==euid+".o" || strs[2]==euid)) break;
+      return 0;
+
+    case LIBLOGDIR:
+      /* auf /log/ARCH/ haben wirklich nur EMs Zugriff */
+      if (strs[1]=="ARCH" && lvl<ARCH_LVL) return 0;
+
+      /* alles andere duerfen auch Weise... */
+      if (lvl>=ELDER_LVL) return 1;
+
+      /* Allgemeine logfiles in /log duerfen wirklich nur geschrieben werden */
+      if (s==2 && strs[1][0]>='A' && strs[1][0]<='Z' && fun != "write_file")
+         return 0;
+
+      /* Unterhalb von /log/syslog darf nur geschrieben werden */
+      if (s>1 && strs[1]=="syslog" && fun != "write_file")
+         return 0;
+
+      /* loggen ins eigene repfile immer erlauben */
+      if (s==3 && strs[1]=="report" &&
+          strs[2]==explode(euid, ".")[<1]+".rep") break;
+
+      /* in fremden Verzeichnissen hat niemand was zu loggen */
+      if (get_wiz_level(strs[1]) >= WIZARD_LVL &&
+          strs[1] != efun::explode(euid, ".")[<1])
+         return 0;
+      break;
+
+    case WIZARDDIR:
+      /* kein Zugriff auf Objekte mit hoeheren Rechten */
+      if (query_wiz_level(strs[1]) > lvl) return 0;
+
+      /* Magier selbst oder Weise duerfen hier schreiben */
+      if ((IS_WIZARD(euid) && euid==strs[1])||(lvl>=ELDER_LVL)) break;
+
+      /* Es steht jedoch frei, auch anderen Schreibrechte zu geben... */
+      return access_rights(strs,euid);
+      
+    case DOMAINDIR:
+      /* neue Regionen duerfen nur Erzmagier anlegen */
+      if (s<2 && lvl<ARCH_LVL) return 0;
+
+      /* kein Zugriff auf Objekte mit hoeheren Rechten */
+      if (s>2 && query_wiz_level(creator_file(path)) > lvl)
+         return 0;
+
+      /* Auf Regionfiles von erzmagier haben nur EMs Zugriff */
+      if (creator_file(path)=="erzmagier" && lvl<ARCH_LVL) return 0;
+
+      /* innerhalb der Region haben RMs und Weise volle Schreibrechte */
+      if (lvl>=ELDER_LVL || domain_master(euid,strs[1])) break;
+
+      /* neue Verzeichnisse in der Region kann nur RM+ anlegen */
+      if (s<=3 && (fun=="mkdir" || fun=="rmdir")) return 0;
+
+      /* Magier auf ihre eigenen Files... */
+      if (s>2 && strs[2]==euid) break;
+
+      /* Files der Magier in der Region in ihre eigenen Verzeichnisse... */
+      if (s>2 && euid==sprintf("d.%s.%s", strs[1], strs[2])) break;
+
+      /* Files mit Regionsuid */
+      if (euid==strs[1]) break;
+
+      /* Es ist moeglich anderen Magiern Rechte bei sich einzuraeumen. */
+      if (access_rights(strs,euid)>0) break;
+
+      /* Auf das Verzeichniss 'alle' haben alle Regionsmitglieder Zugriff.
+         Ausser, wenn sie ueber access_rights.c explizit ausgeschlossen
+         werden (Returncode < 0). */
+      if (s>2 && strs[2]=="alle" && domain_member(euid, strs[1]) &&
+          access_rights(strs,euid)>=0) break;
+      return 0;
+
+    case PROJECTDIR:
+      /* Nur Erzmagier duerfen neue Projektverzeichnisse anlegen... */
+      if (s<3 && lvl<ARCH_LVL) return 0;
+
+      /* alles weitere duerfen auch Weise tun */
+      if (lvl>=ELDER_LVL) break;
+
+      /* in den Serviceverzeichnissen muss lediglich der Name stimmen */
+      if (s>3 && strs[1]=="service" && euid==strs[2]) break;
+
+      /* Objekte eines Projektes haben Schreibzugriffe auf ihr Projekt */
+      if (s>3 && (implode(strs[0..1], ".") == euid
+                  || implode(strs[0..2], ".") == euid) ) return 1;
+
+      /* Schreibrechte koennen natuerlich auch vergeben werden... */
+      if (project_access(euid,strs[1])) break;
+      // Alternativ besteht neben dem ACCESS_RIGHTS auch noch die
+      // Moeglichkeit, per access_rights.c Rechte einzuraeumen.
+      if (access_rights(strs,euid)>0) break;
+
+     return 0;
+
+    default: return 0;
+  }
+  return path;
+}
+
+mixed valid_read(string path, string euid, string fun, object obj)
+{
+  int s, lev;
+  string *strs, suf;
+
+  if (member(path,' ')!=-1) return 0;
+
+  // Unter LIBDATADIR (/data) sollen komplett identische Leserechte
+  // vergeben werden.
+  if (sizeof(path) > 6
+      && path[0..5] == "/"LIBDATADIR"/")
+    return valid_read(path[5..], euid, fun, obj) != 0;
+
+  if (!euid) euid="-";
+
+  strs=full_path_array(path, euid);
+  // Pfad soll ab jetzt auf jeden Fall absolut sein.
+  if (strs[0] != "")
+      path = "/" + implode(strs, "/");
+  else
+      path=implode(strs, "/");
+
+  // fuer die Rechtebestimmung "/" an Anfang und Ende entfernen
+  strs -= ({""});
+
+  if (!sizeof(strs) || !sizeof(path) || euid == ROOTID || obj==TO) return path;
+
+  if ((s=sizeof(strs)) <= 1) return path;
+
+  lev=query_wiz_level(euid);
+
+  switch(strs[0]) {
+    case "core":
+    case "wahl":
+      return 0;
+
+    case NEWSDIR:
+      if (strs[1] == "archiv.pub") // oeffentliches archiv
+        return path;
+      else if (strs[1] == "archiv.magier" // Magier-Archiv
+               && lev >= WIZARD_LVL)
+        return path;
+
+      return 0; // kein Zugriff auf /news/ oder alles andere.
+
+    case "maps":
+      if (lev<=WIZARD_LVL) return 0;
+
+    case "":
+    case ETCDIR:
+    case STDDIR:
+    case SYSDIR:
+    case DOCDIR:
+    case LIBOBJDIR:
+    case LIBROOMDIR:
+    case LIBITEMDIR:
+    case FTPDIR:
+      return path;
+
+    case MAILDIR:
+      return (euid==MAILID);
+
+    case SECUREDIR:
+      if (strs[1]=="save") return 0;
+      if (path[<2..]==".o" || path[<5..]==".dump") return 0;
+      if (strs[1]!="ARCH" || lev>=ARCH_LVL) return path;
+
+    case LIBLOGDIR:
+      if ( strs[1] == "ARCH" && !IS_DEPUTY(euid) )
+          return 0;
+
+      if ( strs[1] == "ARCH" && lev < ARCH_LVL && s == 3 &&
+           strs[2] != "*" && strs[2][0..2] != "bb." &&
+           member( deputy_files, strs[2] ) < 0 )
+          return 0;
+
+      if ( lev > WIZARD_LVL )
+          return path;
+
+      if ( s == 2 && (strs[1] == "report" || strs[1] == "error") )
+          return path; // fuer 'cd'
+
+      // /log sollte jeder auflisten koennen
+      if ( s == 2 && strs[1]=="*" && fun=="get_dir")
+          return path;
+
+      // unter /log/report/, /log/error und /log/warning/ duerfen alle lesen
+      if ( s==3 && 
+          (strs[1] == "report" || strs[1] == "error"  
+           || strs[1] =="warning"))
+          return path;
+
+      // /log/<Magiername> darf von <Magiername> natuerlich auch
+      // gelesen werden
+      if ( s >= 2 && strs[1] == euid )
+          return path;
+
+      return 0;
+
+    case "backup":
+    case LIBSAVEDIR:
+      if (lev>WIZARD_LVL) return path;
+
+      /* Objekte in /p/* haben bisher leider wizlevel 0 */
+      //if (lev==0) return path;
+
+      if (fun=="file_size") return path;
+
+      // das eigene Savefile darf man natuerlich immer. ;-)
+      if (s==3 && (strs[2]==euid+".o" || strs[2]==euid))
+        return path;
+      return 0;
+
+    case PROJECTDIR:
+      /* Pruefen ob ein File existiert darf jeder... */
+      if (fun=="file_size") return path;
+
+      /* Die Service-Verzeichnisse darf jeder lesen */
+      if (s>3 && strs[1]=="service") return path;
+
+      //Weise duerfen in /p/ schreiben, also auch lesen. (Zesstra, 04.11.06)
+      if (lev>=ELDER_LVL) return path;
+
+      /* wer hier schreiben darf, darf natuerlich auf jeden Fall lesen */
+      //Anmerkung v. Zesstra: das prueft nur, ob jemand in ACCESS_RIGHTS
+      //steht, nicht ob jemand (ggf. aus anderen Gruenden schreiben darf)
+      if (project_access(euid,strs[1])) return path;
+      //Alternativ kann man explizite Schreibrechte auch via access_rights.c
+      //vergeben. (und damit natuerlich auch Leserechte)
+      if (access_rights(strs,euid)>0) return path;
+
+      /* Objekte eines Projektes haben Lesezugriff auf ihr Projekt */
+      if (s>3 && (implode(strs[0..1], ".") == euid
+                  || implode(strs[0..2], ".") == euid) ) return path;
+
+      /* Fall-Through */
+
+    case GUILDDIR:
+      /* "secure"-Verzeichnisse darf nur lesen, wer da auch schreiben darf */
+      if ( s > 3 && strs[<2] == "secure" && member( insecure, strs[1] ) < 0
+           && lev < ARCH_LVL && !access_rights(strs, euid) )
+          return 0;
+
+      /* Pruefen ob ein File existiert darf jeder... */
+      if (fun=="file_size") return path;
+
+      /* Fall-Through */
+
+    case SPELLBOOKDIR:
+      // Gildenpbjekte koennen hier lesen
+      if (lev==0 && euid[0..4]=="GUILD") return path;
+
+      // Mit Level <= 20 darf man hier nichts lesen
+      if ( lev<=WIZARD_LVL && sizeof(regexp( ({strs[<1]}), "\\.[och]" )) )
+        return 0;
+
+      return path;
+
+    case WIZARDDIR:
+      if (s==2) return path;
+      /* das eigene Verzeichniss darf man natuerlich immer lesen... */
+      if (strs[1]==euid && lev>=WIZARD_LVL) return path;
+
+      /* Pruefen ob ein File existiert darf jeder... */
+      if (fun=="file_size") return path;
+
+      /* fremde Verzeichnisse mit <= Level 20 noch nicht */
+      if (lev<=WIZARD_LVL) return 0;
+
+      /* wo man schreiben darf, darf man natuerlich auch lesen... */
+      if (lev>=ELDER_LVL) return path;
+
+      // kein Zugriff auf archivierten Code (wo z.B. secure/ drin sein
+      // koennen)
+      if (member(({"bz2","gz","zip"}), explode(strs[<1],".")[<1]) > -1)
+        return 0;
+
+      /* Files ohne Code sind nicht weiter interessant... */
+      if ( !sizeof(regexp( ({strs[<1]}), "\\.[och]" )) )
+          return path;
+
+      /* folgende Funktionen sind natuerlich voellig uninteressant */
+      if (member(({"get_dir", "restore_object"}), fun)!=-1)
+          return path;
+
+      /* Zugriff erlaubt, aber mitloggen */
+      write_file( READ_FILE, sprintf("%O %s %s: %s\n", fun, ctime()[4..],
+                                     euid, path) );
+      return path;
+
+    case DOMAINDIR:
+      /* Mit Level 15 darf man hier _nichts_ lesen */
+      if ( fun!="file_size" && lev<WIZARD_LVL &&
+           sizeof(regexp( ({strs[<1]}), "\\.[och]" )) ) return 0;
+
+      /* den Verzeichnisbaum von /d/ darf man natuerlich sonst lesen */
+      if (s<=2) return path;
+
+      /* eigenen Code darf man natuerlich auch lesen */
+      if (euid==strs[2] || euid==sprintf("d.%s.%s", strs[1], strs[2]))
+         return path;
+
+      /* Deputies haben ein gemeinsames Verzeichnis unter /d/erzmagier */
+      if (strs[1]=="erzmagier" && strs[2]=="polizei" && IS_DEPUTY(euid))
+          return path;
+
+      /* d/erzmagier darf man nur als Erzmagier lesen */
+      if (strs[1]=="erzmagier" && lev<ARCH_LVL) return 0;
+
+      /* einige Regionen haben std-verzeichnisse, die darf man natuerlich
+         auch mit Level 20 bereits komplett lesen! */
+      if (strs[2]=="std") return path;
+
+      /* "secure"-Verzeichnisse darf nur lesen, wer da auch schreiben darf */
+      if ( s > 4 && strs[<2] == "secure" && member( insecure, strs[2] ) < 0 ){
+          if ( lev < ELDER_LVL && !domain_master(euid, strs[1])
+               && !access_rights(strs, euid) )
+              return 0;
+          else
+              return path;
+      }
+
+      /* Dokus, Infos und .readme darf man immer lesen... */
+      if ( fun=="file_size" || !sizeof(regexp( ({strs[<1]}), "\\.[och]" )) )
+          return path;
+
+      /* Mit Level 20 darf man noch keinen fremden Code lesen! */
+      if (lev<=WIZARD_LVL) return 0;
+
+      /* Weise duerfen natuerlich alles weitere lesen */
+      if (lev>=ELDER_LVL) return path;
+
+      /* Regionsmagier in ihren Regionen natuerlich auch */
+      if (lev>=LORD_LVL && domain_master(euid, strs[1])) return path;
+
+      /* folgende Funktionen sind natuerlich voellig uninteressant */
+      if (member(({"get_dir", "restore_object"}), fun)!=-1)
+          return path;
+
+      /* Zugriff erlaubt, aber mitloggen */
+      write_file( READ_FILE, sprintf("%O %s %s: %s\n", fun, ctime()[4..],
+                                     euid, path) );
+      return path;
+  }
+  if (lev<ARCH_LVL) return 0;
+  return path;
+}
+
diff --git a/secure/master/guild.c b/secure/master/guild.c
new file mode 100644
index 0000000..9eaaa8b
--- /dev/null
+++ b/secure/master/guild.c
@@ -0,0 +1,62 @@
+// MorgenGrauen MUDlib
+//
+// master/guild.c -- Gilden und Gildenmagier
+//
+// $Id: guild.c 6142 2007-01-31 20:34:39Z Zesstra $
+
+/*
+ * Dies kann irgendwann auch hinsichtlich einer automatischen 
+ * Rechtevergabe auf Gilden-Verzeichnisse erweitert werden.
+ * Bisher werden nur Gildenmagier verwaltet.
+ */
+
+#pragma strict_types
+
+#include "/secure/master.h"
+
+
+int guild_master(string user, string guild)
+{
+  string *guilds;
+  int i;
+
+  if (!find_userinfo(user)||
+      !pointerp(guilds=get_userinfo(user)[USER_GUILD-1]))
+    return 0;
+  
+  return (member(guilds,guild) != -1);
+}
+
+int add_guild_master(string user, string guild)
+{
+  string *guilds;
+  
+  if ((call_other(SIMUL_EFUN_FILE, "process_call") ||
+       call_other(SIMUL_EFUN_FILE, "secure_level") < GOD_LVL) ||
+      !find_userinfo(user))
+    return 0;
+
+  guilds=get_userinfo(user)[USER_GUILD-1];
+  if (!guilds)
+    set_guilds(user, ({ guild }) );
+  else {
+    guilds = guilds - ({guild}) + ({guild});
+    set_guilds(user, guilds);
+  }
+  return 1;
+}
+
+int remove_guild_master(string user, string guild)
+{
+  string *guilds;
+
+  if (!IS_GOD(geteuid(previous_object()))
+      ||!find_userinfo(user)
+      ||!(guilds=get_userinfo(user)[USER_GUILD-1])
+      || member(guilds,guild)==-1)
+    return 0;
+  guilds -= ({ guild });
+  set_guilds(user, guilds);
+  return 1;
+}
+
diff --git a/secure/master/misc.c b/secure/master/misc.c
new file mode 100644
index 0000000..02b0fcc
--- /dev/null
+++ b/secure/master/misc.c
@@ -0,0 +1,772 @@
+// MorgenGrauen MUDlib
+//
+// master/misc.c -- Diverses: (T)Banish, Projektverwaltung, Levelaufstieg, ...
+//
+// $Id: misc.c 9467 2016-02-19 19:48:24Z Zesstra $
+
+#pragma strict_types,rtt_checks
+
+#include "/sys/functionlist.h"
+#include "/sys/lpctypes.h"
+#include "/sys/object_info.h"
+#include "/sys/interactive_info.h"
+
+#include "/secure/master.h"
+#include "/mail/post.h"
+#include "/sys/thing/language.h"
+#include "/sys/thing/description.h"
+
+// Fuer CIDR-Notatio im sbanish
+#include "/secure/master/cidr.c"
+
+static mixed *banished;
+static mapping tbanished, sbanished;
+static string *deputies;
+
+// TODO: muss ggf. Fakeobjekt erzeugen+uebergeben, wenn sender kein object
+protected void send_channel_msg(string channel,mixed sendername,string msg)
+{
+  object sender;
+  if (objectp(sendername))
+    sender=sendername;
+  else
+  {
+    // wenn kein Objekt uebergeben, erstmal schauen, ob ein Spielerobject
+    // existiert... Wenn ja, das nehmen.
+    sender = call_sefun("find_player", sendername)
+             || call_sefun("find_netdead", sendername);
+    if (!objectp(sender))
+    {
+      // sonst faken wir eins. *seufz*
+      sender=clone_object("/p/daemon/namefake");
+      sender->SetProp(P_NAME, sendername); 
+      sender->SetProp(P_ARTICLE,0);
+      // Dieses Objekt zerstoert sich nach 3s automatisch.
+    }
+  }
+  CHMASTER->send(channel, sender, msg);
+}
+
+static string *explode_files(string file) {
+  string data;
+  mixed *exploded;
+  int i;
+
+  data=read_file(file);
+  if (!data || !stringp(data) || data == "") return ({});
+  exploded = efun::explode(data,"\n");
+  for (i=sizeof(exploded);i--;)
+    if (!stringp(exploded[i]) || exploded[i]=="" || exploded[i][0]=='#')
+      exploded[i]=0;
+  exploded-=({0});
+  printf("%-30s: %3d Objekt%s\n",file,i=sizeof(exploded),(i==1?"":"e"));
+  return exploded;
+}
+
+void UpdateTBanish();
+
+mixed QueryBanished(string str){
+  int i;
+
+  if (!str) return 0;
+  if (!pointerp(banished)) return 0;
+  for (i=sizeof(banished)-1;i>=0;i--)
+    if (sizeof(regexp(({str}),"^"+banished[i][0]+"$")))
+    {
+      if (!banished[i][1] || banished[i][1]=="")
+        return "Dieser Name ist gesperrt.";
+      else
+        return banished[i][1];
+    }
+  return 0;
+}
+
+mixed QueryTBanished(string str) {
+  int i;
+
+  if (!str || !mappingp(tbanished) || !(i=tbanished[str]))
+    return 0;
+
+  if (i == -1 || i > time())
+    return sprintf("Es gibt schon einen Spieler diesen Namens.\n"
+        +"Allerdings kann er/sie erst am %s wieder ins Mud kommen.\n",
+          (i == -1 ? "St. Nimmerleinstag" :
+           call_sefun("dtime",i)[0..16]));
+
+// Ansonsten: die Zeit ist abgelaufen, Spieler darf wieder...
+  m_delete(tbanished, str);
+  UpdateTBanish();
+  return 0;
+}
+
+void ReloadBanishFile(){
+    int i, t;
+    string s1, s2, *s;
+
+    banished = efun::explode( read_file("/secure/BANISH") || "", "\n" );
+    banished = banished - ({""});
+    for ( i = sizeof(banished); i--; ){
+        s = efun::explode( banished[i], " " );
+        s1 = s[0];
+        s2 = implode( s[1..], " " );
+        banished[i] = ({ s1, s2 });
+    }
+
+    if ( !mappingp(tbanished) ){
+        tbanished = ([]);
+
+        s = efun::explode( read_file("/secure/TBANISH") || "", "\n" );
+        s -= ({""});
+
+        for ( i = sizeof(s); i--; ){
+            sscanf( s[i], "%s:%d", s1, t );
+            tbanished += ([ s1 : t ]);
+        }
+    }
+
+    if ( !mappingp(sbanished) ){
+        sbanished = m_allocate(3, 2);
+
+        s = efun::explode( read_file("/secure/SITEBANISH") || "", "\n" );
+        s -= ({""});
+
+        for ( i = sizeof(s); i--; ) {
+            int int_ip;
+            sscanf( s[i], "%s:%d:%s", s1, t, s2 );
+            int_ip = IPv4_addr2int(s1);
+            m_add(sbanished, int_ip, t, s2);
+        }
+    }
+}
+
+int IsDeputy(mixed name)
+{
+    if ( IS_ARCH(name) )
+        return 1;
+
+    if ( objectp(name) )
+        name = getuid(name);
+
+    if ( member( deputies || ({}), name ) >= 0 )
+        return 1;
+
+    return 0;
+}
+
+
+varargs void BanishName( string name, string reason, int force )
+{
+  string *names;
+  int i;
+
+  if ( PO != TO && call_sefun("secure_level") < LORD_LVL
+           && !IsDeputy( call_sefun("secure_euid") ) )
+      return;
+
+  if ( !stringp(name) || !sizeof(name) )
+      return;
+
+  name = lower_case(name);
+
+  if ( !reason || !stringp(reason) )
+      reason = "";
+
+  if ( QueryBanished(name) && lower_case(reason) != "loeschen" ){
+      write("Der Name ist schon gebannt.\n");
+      return;
+  }
+
+  if ( !force && file_size(SAVEPATH+name[0..0]+"/"+name+".o") > 0 ){
+      write("Es existiert bereits ein Spieler dieses Namens.\n");
+      return;
+  }
+
+/*  if (!("/secure/login"->valid_name(name))) return;*/
+  if ( lower_case(reason) != "loeschen" ){
+      names = ({ name + " " + reason });
+
+      for ( i = sizeof(banished); i--; )
+          names += ({ banished[i][0] + " " + banished[i][1] });
+  }
+  else{
+      names = ({});
+
+      for ( i = sizeof(banished); i--; )
+          if ( banished[i][0] != name )
+              names += ({ banished[i][0] + " " + banished[i][1] });
+  }
+
+  names = sort_array( names, #'> );
+  rm("/secure/BANISH");
+  write_file( "/secure/BANISH", implode(names, "\n") );
+  write( "Okay, '"+capitalize(name)+"' ist jetzt "+
+      (lower_case(reason) == "loeschen" ? "nicht mehr " : "")+"gebanisht.\n" );
+  ReloadBanishFile();
+}
+
+void UpdateTBanish()
+{
+  int i;
+  string *names;
+
+  for (i=sizeof(names = sort_array(m_indices(tbanished), #'</*'*/))-1;i>=0;i--)
+    names[i] = sprintf("%s:%d", names[i], tbanished[names[i]]);
+
+  rm("/secure/TBANISH");
+  write_file("/secure/TBANISH", implode(names, "\n"));
+}
+
+void UpdateSBanish()
+{
+    int i;
+    mapping lines = m_allocate(sizeof(sbanished),0);
+
+    foreach(int ip, int tstamp, string user : sbanished) {
+      m_add(lines, sprintf("%s:%d:%s",
+                           IPv4_int2addr(ip), tstamp, user));
+    }
+
+    write_file( "/secure/SITEBANISH", 
+        implode( sort_array (m_indices(lines), #'<), "\n" ), 1);
+}
+
+int TBanishName(string name, int days)
+{
+  int t;
+
+  if ( (getuid(TI) != name) &&
+       !IsDeputy( call_sefun("secure_euid") ) )
+    return 0;
+
+  if (days && QueryTBanished(name)){
+    write("Der Name ist schon gebannt.\n");
+    return 0;
+  }
+
+  if (file_size(SAVEPATH+name[0..0]+"/"+name+".o")<=0){
+    write("Es existiert kein Spieler dieses Namens!\n");
+    return 0;
+  }
+
+  if (!days && member(tbanished, name))
+    m_delete(tbanished, name);
+  else {
+    if (!mappingp(tbanished))
+      tbanished = ([]);
+    if (days <= -1)
+      t = -1;
+    else
+      t = (time()/86400)*86400 + days*86400;
+    tbanished += ([ name : t ]);
+  }
+
+  UpdateTBanish();
+  return 1;
+}
+
+
+mixed QuerySBanished( string ip )
+{
+    int save_me, site;
+    string banished_meldung =
+      "\nSorry, von Deiner Adresse kamen ein paar Idioten, die "
+            "ausschliesslich\nAerger machen wollten. Deshalb haben wir "
+            "die Moeglichkeit,\neinfach neue Charaktere "
+            "anzulegen, kurzzeitig fuer diese Adresse gesperrt.\n\n"
+            "Falls Du bei uns spielen moechtest, schicke bitte eine Email "
+            "an\n\n                         mud@mg.mud.de\n\n"
+            "mit der Bitte, einen Charakter fuer Dich anzulegen.\n" ;
+
+    if ( !ip || !stringp(ip) || !mappingp(sbanished) || !sizeof(sbanished) )
+        return 0;
+
+    foreach(site, int tstamp: sbanished) {
+        if ( tstamp > 0 && tstamp < time() ) {
+            m_delete( sbanished, site );
+            save_me=1;
+        }
+    }
+    if (save_me)
+        UpdateSBanish();
+
+    if ( !sizeof(sbanished) )
+        return 0;
+
+    site = IPv4_addr2int(ip);
+    if (!site)
+        return 0;
+    // direkt drin?
+    if (member(sbanished, site))
+        return banished_meldung;
+    // oder Netz dieser IP gesperrt?
+    foreach(int locked_site : sbanished) {
+        if ((locked_site & site) == locked_site)
+            return banished_meldung;
+    }
+
+    return 0;
+}
+
+
+private int _sbanished_by( int key, string name )
+{
+    return sbanished[key, 1] == name;
+}
+
+
+mixed SiteBanish( string ip, int days )
+{
+    string *s, tmp, euid;
+    int t, level;
+
+    euid = call_sefun("secure_euid");
+    level = call_sefun("secure_level");
+
+    // Unter L26 gibt's gar nix.
+    if ( level <= DOMAINMEMBER_LVL )
+        return -1;
+
+    // Die Liste der gesperrten IPs anschauen darf jeder ab L26.
+    if ( !ip && !days )
+        return copy(sbanished);
+
+
+    if ( !stringp(ip) || !intp(days) )
+        return 0;
+
+    if ( days && QuerySBanished(ip) ){
+        write( "Diese Adresse ist schon gebannt.\n" );
+        return 0;
+    }
+
+    if ( !days ) {
+        int int_ip = IPv4_addr2int(ip);
+
+        if( member(sbanished, int_ip) ){
+            // Fremde Sitebanishs duerfen nur Deputies loeschen.
+            if ( sbanished[int_ip, 1] != euid && !IsDeputy(euid) )
+                return -1;
+
+            call_sefun("log_file", "ARCH/SBANISH",
+                    sprintf( "%s: %s hebt die Sperrung der Adresse %s von %s "
+                             + "auf.\n", 
+                             ctime(time()), capitalize(euid), ip,
+                             capitalize(sbanished[int_ip, 1]) ) );
+
+            m_delete( sbanished, int_ip );
+        }
+        else
+            return 0;
+    }
+    else {
+        // Alles, was nicht Deputy ist, darf nur fuer einen Tag sbanishen.
+        if ( days != 1 && !IsDeputy(euid) )
+            return -1;
+
+        // Nur Deputies duerfen mehr als 10 Sperrungen vornehmen.
+        if ( sizeof(filter_indices(sbanished, #'_sbanished_by, euid)) >= 10
+             && !IsDeputy(euid) )
+            return -2;
+
+        int int_ip = IPv4_addr2int(ip);
+
+        if(!int_ip) {
+            write( "Ungueltige Adresse!\n" );
+            return 0;
+        }
+
+        // L26 duerfen exakt eine IP sperren, RMs ganze Class C-Subnetze
+        // und Deputies auch Class B-Subnetze.
+        int nsize=IPv4_net_size(ip);
+        if ( nsize > 1 && level < LORD_LVL
+             || nsize > 255 && !IsDeputy(euid) )
+            return -1;
+
+        if ( !mappingp(sbanished) )
+            sbanished = m_allocate(1, 2);
+
+        if ( days < 0 )
+            t = -1;
+        else
+            t = (time() / 86400) * 86400 + days * 86400;
+
+        m_add(sbanished, int_ip, t, euid);
+
+        call_sefun("log_file", "ARCH/SBANISH",
+                sprintf( "%s: %s sperrt die Adresse %s %s.\n",
+                         ctime(time()), capitalize(euid),
+                         ip,
+                         days > 0 ? (days > 1 ? "fuer " + days + " Tage"
+                                     : "fuer einen Tag")
+                         : "bis zum St. Nimmerleinstag" ) );
+    }
+
+    UpdateSBanish();
+    return 1;
+}
+
+static void CheckDeputyRights()
+{
+    object ob;
+    mixed *ginfo;
+
+    // Lese- und Schreibberechtigungen fuer die Rubrik 'polizei' setzen
+    call_other( "/secure/news", "???" );
+    ob = find_object("secure/news");
+    ginfo = ((mixed)ob->GetGroup("polizei"))[5..6];
+    ob->RemoveAllowed( "polizei", 0, ginfo[0], ginfo[1] );
+    ob->AddAllowed( "polizei", 0, deputies, deputies );
+    LoadDeputyFileList();
+}
+
+int ReloadDeputyFile()
+{
+    deputies = efun::explode( read_file("/secure/DEPUTIES") || "", "\n" );
+    deputies -= ({""});
+    deputies = map( deputies, #'lower_case/*'*/ );
+    call_out( "CheckDeputyRights", 2 );
+    return(1);
+}
+
+
+static int create_home(string owner, int level)
+{
+  string def_castle;
+  string dest, castle, wizard;
+  object player;
+  string *domains;
+  int i;
+
+  player = call_sefun("find_player",owner);
+  if (!player)
+    return -5;
+  domains=get_domain_homes(owner);
+  if (!sizeof(domains) && level >= DOMAINMEMBER_LVL)
+  {
+    tell_object(player,"Du gehoerst noch keiner Region an !\n");
+    return -6;
+  }
+  tell_object(player,"Du bist Mitarbeiter der folgenden Regionen:\n");
+  for (i=0;i<sizeof(domains);i++)
+  {
+    if (i) tell_object(player,", ");
+    tell_object(player,domains[i]);
+  }
+  tell_object(player,".\n");
+  update_wiz_level(owner, level);
+  wizard = "/players/" + owner;
+  castle = "/players/" + owner + "/workroom.c";
+  if (file_size(wizard) == -1) {
+    tell_object(player, "Verzeichnis " + wizard + " angelegt\n");
+    mkdir(wizard);
+  }
+  dest = object_name(environment(player));
+  def_castle = read_file("/std/def_workroom.c");
+  if (file_size(castle) > 0) {
+    tell_object(player, "Du HATTEST ja schon ein Arbeitszimmer !\n");
+  } else {
+    if (write_file(castle, def_castle))
+    {
+      tell_object(player, "Arbeitszimmer " + castle + " erzeugt.\n");
+      // Arbeitszimmer als Home setzen
+      player->SetProp(P_START_HOME,castle[0..<3]);
+    }
+    else
+      tell_object(player, "Arbeitszimmer konnte nicht erzeugt werden !\n");
+  }
+  return 1;
+}
+
+// Sendet dem befoerderten Magier eine Hilfemail zu.
+protected void SendWizardHelpMail(string name, int level) {
+  
+  object pl=call_sefun("find_player",name);
+  if (!objectp(pl)) return;
+
+  string file=sprintf("%sinfo_ml%d", WIZ_HELP_MAIL_DIR, level);
+  // wenn kein Hilfetext fuer den Level da ist: raus
+  if (file_size(file) <= 0)
+    return;
+
+  string subject = read_file(file,1,1);
+  string text = call_sefun("replace_personal",
+                         read_file(file,2), ({pl}));
+
+  mixed mail = ({"Merlin", "<Master>", name, 0, 0, subject,
+                 call_sefun("dtime",time()),
+                 MUDNAME+time(), text });
+  MAILDEMON->DeliverMail(mail, 0);
+}
+
+int allowed_advance_wizlevel(mixed ob)
+{
+  string what;
+
+  if (objectp(ob) && geteuid(ob)==ROOTID) return 1;
+
+  if (!stringp(ob))
+    what=efun::explode(object_name(ob),"#")[0];
+  else
+    what=ob;
+
+  if (what=="/secure/merlin") return 1;
+
+  return 0;
+}
+
+int advance_wizlevel(string name, int level)
+{
+  int answer;
+  mixed *user;
+
+  if (!allowed_advance_wizlevel(PO))
+    return -1;
+
+  if (level>80) return -2;
+
+  if (!find_userinfo(name)) return -3;
+
+  user=get_full_userinfo(name);
+
+  if (user[USER_LEVEL+1]>level) return -4;
+
+  if (user[USER_LEVEL+1]==level) return 1;
+
+  if (level>=10 && level<20)
+  {
+    update_wiz_level(name, level);
+    SendWizardHelpMail(name, level);
+    return 1;
+  }
+  if (level>=20 && user[USER_LEVEL+1]<21)
+  {
+    answer = create_home(name, level);
+    if ( answer > 0 ) {
+      answer = update_wiz_level(name, level);
+      SendWizardHelpMail(name, level);
+    }
+    return answer;
+  }
+
+  update_wiz_level(name, level);
+  SendWizardHelpMail(name, level);
+
+  return 1;
+}
+
+void restart_heart_beat(object heart_beat)
+{
+  if (heart_beat) heart_beat->_restart_beat();
+}
+
+int renew_player_object(mixed who)
+{
+  object newob;
+  object *obs, *obs2;
+  mixed err;
+  string ob_name;
+  object *armours, weapon;
+  object tp;
+  int i,active,j;
+
+  if (stringp(who))
+  {
+    who=call_sefun("find_player",who);
+    if (!who)
+    {
+      who=call_sefun("find_netdead",who);
+      if (!who)
+        return -1;
+    }
+  }
+  if (!objectp(who))
+    return -2;
+  if (!object_info(who, OI_ONCE_INTERACTIVE))
+    return -3;
+  if (who->QueryGuest())
+  {
+    printf("Can't renew guests!\n");
+    return -6;
+  }
+  active=interactive(who);
+  printf("OK, renewing %O\n",who);
+  seteuid(geteuid(who));
+  err=catch(newob=clone_object(query_player_object(getuid(who))); publish);
+  seteuid(getuid(TO));
+  if (err)
+  {
+    printf("%O\n",err);
+    return -4;
+  }
+  if (!newob)
+    return -5;
+  /* Ok, the object is here now ... lets go for it ... */
+  who->save_me(0);
+  /* SSL ip weiterreichen */
+  if( call_sefun("query_ip_number", who) != efun::interactive_info(who,II_IP_NUMBER) )
+  {
+    newob->set_realip( call_sefun("query_ip_number",who) );
+  }
+  efun::configure_object(previous_object(), OC_COMMANDS_ENABLED, 0);
+  efun::set_this_player(0);
+  armours=(object *)who->QueryProp(P_ARMOURS);
+  weapon=(object)who->QueryProp(P_WEAPON);
+
+  if ( previous_object() && object_name(previous_object()) == "/secure/merlin" )
+      send_channel_msg("Debug",
+                       previous_object(),
+                       sprintf("RENEWING: %O %O\n",newob,who));
+  else
+      send_channel_msg("Entwicklung",
+                       previous_object(),
+                       sprintf("RENEWING: %O %O\n",newob,who));
+
+  ob_name=explode(object_name(newob),"#")[0];
+  if (sizeof(ob_name)>11 && ob_name[0..11]=="/std/shells/")
+    ob_name=ob_name[11..];
+  ob_name=ob_name+":"+getuid((object)who);
+  if (active)
+    exec(newob,who);
+  if (active && (interactive(who)||!interactive(newob)))
+  {
+    send_channel_msg("Debug",previous_object(),
+                     "ERROR: still active !\n");
+    newob->remove();
+    return 0;
+  }
+//   newob->start_player(capitalize(getuid(who)),who->_query_my_ip());
+  funcall(
+          bind_lambda(
+                      unbound_lambda( ({'x, 'y}),
+                                      ({#'call_other/*'*/,
+                                             newob,
+                                             "start_player",
+                                             'x, 'y
+                                             })
+                                      ), who
+                      ),
+          capitalize(getuid(who)), who->_query_my_ip() );
+
+  newob->move(environment(who),M_TPORT|M_NOCHECK|M_NO_SHOW|M_SILENT
+              |M_NO_ATTACK);
+  obs=all_inventory(who);
+  foreach(object tob: all_inventory(who)) {
+    if (!tob->QueryProp(P_AUTOLOADOBJ))
+    {
+      // kein Autoloader...
+      foreach(object ob: deep_inventory(tob))
+      {
+        // aber enthaltene Autoloader entsorgen...
+        if (ob->QueryProp(P_AUTOLOADOBJ))
+        {
+          catch(ob->remove();publish);
+          if (ob) destruct(ob);
+        }
+      }
+      // objekt ohne die AL bewegen.
+      catch(tob->move(newob,M_NOCHECK);publish);
+    }
+    else {
+      // Inhalt von Autoloadern retten.
+      // neue instanz des ALs im neuen Objekt.
+      object new_al_instance = present_clone(tob, newob);
+      foreach(object ob: deep_inventory(tob)) {
+        if (ob->QueryProp(P_AUTOLOADOBJ)) {
+            // autoloader in Autoloadern zerstoeren...
+            catch(ob->remove(1);publish);
+            if (ob) destruct(ob);
+        }
+        // alle nicht autoloader in die AL-Instanz im neuen Objekt oder
+        // notfalls ins Inv.
+        else {
+          if (objectp(new_al_instance))
+            catch(ob->move(new_al_instance, M_NOCHECK);publish);
+          else
+            catch(ob->move(newob, M_NOCHECK);publish);
+        }
+      }
+      // Autoloader zerstoeren. Wird nicht vom Spielerobjekt im remove()
+      // gemacht, wenn nicht NODROP.
+      catch(tob->remove(1);publish);
+      if (tob) destruct(tob);
+    }
+  }
+  who->remove();
+  if ( objectp(who) )
+      destruct(who);
+  rename_object(newob,ob_name);
+  newob->__reload_explore();
+  tp=this_player();
+  efun::set_this_player(newob);
+  if (objectp(weapon))
+    weapon->DoWield();
+  if (pointerp(armours))
+    for (i=sizeof(armours)-1;i>=0;i--)
+      if (objectp(armours[i]))
+        armours[i]->do_wear("alles");
+  efun::set_this_player(tp);
+  //Rueckgabewert noetig, weil Funktion vom Typ 'int'
+  return(1);
+}
+
+mixed __query_variable(object ob, string var)
+{
+  if (!PO || !IS_ARCH(geteuid(PO)) || !this_interactive() ||
+      !IS_ARCH(this_interactive()) || getuid(ob)==ROOTID )
+  {
+    write("Du bist kein EM oder Gott!\n");
+    return 0;
+  }
+  if (efun::object_info(ob, OI_ONCE_INTERACTIVE) && (PO!=ob) &&
+     (var=="history" || var=="hist2"))
+     send_channel_msg("Snoop", previous_object(),
+                      sprintf("%s -> %s (history).",
+                        capitalize(getuid(PO)),capitalize(getuid(ob))));
+
+  call_sefun("log_file", "ARCH/QV",
+                          sprintf("%s: %O inquires var %s in %O\n",
+                                  ctime(time()),this_interactive(),var,ob) );
+  mixed res = variable_list(ob, RETURN_FUNCTION_NAME|RETURN_FUNCTION_FLAGS|
+                                RETURN_FUNCTION_TYPE|RETURN_VARIABLE_VALUE);
+  int index = member(res,var);
+  if (index > -1)
+  {
+    return ({res[index],res[index+1],res[index+2],res[index+3]});
+  }
+  return 0;
+}
+
+void RestartBeats()
+{
+  int i,size,counter;
+  object ob;
+  mixed *list;
+  string file,obname,fun;
+
+  "/secure/simul_efun"->StopCallOut(0);
+  write("CALL_OUT-Restart in progress !\n");
+  filter(users(),#'tell_object,"CALL_OUT-Restart in progress !\n");
+  size=file_size("log/call_out_stop");
+  if (size<=0)
+    return;
+  file="";
+  counter=0;
+  while (counter<size)
+  {
+    file+=read_bytes("log/call_out_stop",counter,
+                     (size-(counter+=40000)>0?counter:size));
+  }
+  list=explode(file,"__(CUT HERE)__\n");
+  list=list[<1..];
+  list=explode(list[0],"\n")-({""});
+  for (i=sizeof(list)-1;i>=0;i--)
+    if (sscanf(list[i],"%s \"%s\"",obname,fun)==2 && ob=find_object(obname))
+    {
+      write(sprintf("%O -> %s\n",ob,fun));
+      catch(ob->__restart(fun);publish);
+    }
+  write("CALL_OUT-Restart completed !\n");
+  filter(users(),#'tell_object,"CALL_OUT-Restart completed !\n");
+  rename("log/call_out_stop","log/call_out_stop.old");
+}
+
diff --git a/secure/master/network.c b/secure/master/network.c
new file mode 100644
index 0000000..e681f82
--- /dev/null
+++ b/secure/master/network.c
@@ -0,0 +1,431 @@
+// MorgenGrauen MUDlib
+//
+// master/network.c - UDP-Handling
+//
+// $Id: network.c 8934 2014-09-10 21:57:12Z Zesstra $
+
+#pragma strict_types
+
+#include "/secure/master.h"
+#define BBMASTER "/secure/bbmaster"
+
+/*
+#undef DEBUG
+#define DEBUG(x) if (call_sefun("find_player","zesstra")) \
+                 tell_object(call_sefun("find_player","zesstra"),x);
+*/
+
+//ich will hieraus momentan kein Debug, ist zuviel. Zesstra
+
+#ifdef DEBUG
+#undef DEBUG
+#endif
+#define DEBUG(x)
+
+nosave int mails_last_hour;
+
+static string mail_normalize( string str )
+{
+    str = regreplace( str, "[^<]*<(.*)>[^>]*", "\\1", 0);
+    return regreplace( str, " *([^ ][^ ]*).*", "\\1", 0);
+}
+
+
+static string *mk_rec_list( string str )
+{
+    return map( explode( lower_case(str), "," ) - ({""}),
+                      "mail_normalize", this_object() );
+}
+
+
+static int CheckPasswd( string name, string passwd ) {
+    mixed *uinf;
+
+    if (!stringp(passwd) || !sizeof(passwd))
+ return 0;
+    if ( sizeof(uinf = get_full_userinfo(name)) < 2 )
+        return 0;
+
+    string pwhash = uinf[USER_PASSWORD+1];
+    if (sizeof(pwhash) > 13) {
+ // MD5-Hash
+ passwd = md5_crypt(passwd, pwhash);
+    }
+    else if (sizeof(pwhash) > 2) {
+ // Crypt-Hash
+ passwd = crypt(passwd, pwhash[0..1]);
+    }
+    else return 0;
+
+    return (passwd == pwhash);
+}
+
+
+static void FtpAccess( string host, string message, int port )
+{
+    string *comp, reply, head;
+#if __EFUN_DEFINED__(send_udp)
+    comp = efun::explode( message, "\t" );
+#define FTP_ID   0
+#define FTP_SEQ  1
+#define FTP_TAG  2
+#define FTP_CMD  3
+#define FTP_ARG1 4
+#define FTP_ARG2 5
+#define FTP_ARG3 6
+
+  if ( sizeof(comp) <= FTP_CMD || lower_case(comp[FTP_TAG]) != "req" ){
+      log_file( "IMP_MSGS", "Host: " + host + ":" + port + " - '" +
+                message + "'\n" );
+      return;
+  }
+
+  reply = "INVALID";
+
+  head = sprintf( "%s\t%s\tRPLY\t%s\t",
+                  comp[FTP_ID], comp[FTP_SEQ], comp[FTP_CMD] );
+
+  switch ( lower_case(comp[FTP_CMD]) ){
+  case "user":
+      if ( sizeof(comp) <= FTP_ARG1 )
+          break;
+
+      if ( IS_LEARNER(lower_case(comp[FTP_ARG1])) )
+          reply = "/players/" + lower_case(comp[FTP_ARG1]);
+      else
+          reply = "NONE";
+      break;
+
+  case "pass":
+      if ( sizeof(comp) <= FTP_ARG2 )
+          break;
+
+      comp[FTP_ARG1] = lower_case(comp[FTP_ARG1]);
+
+      if ( IS_LEARNER(comp[FTP_ARG1]) &&
+           !"/secure/master"->QueryTBanished(comp[FTP_ARG1]) ){
+          if ( CheckPasswd( comp[FTP_ARG1], comp[FTP_ARG2] ) )
+              reply = "OK";
+          else {
+              if ( get_wiz_level( comp[FTP_ARG1] ) < ARCH_LVL )
+                  log_file( "LOGINFAIL",
+                            sprintf( "PASSWORD:      (FTP)     %s %s\n",
+                                     comp[FTP_ARG1],
+                                     ctime(time()) ) );
+              else
+                  log_file( "ARCH/LOGINFAIL",
+                            sprintf( "PASSWORD:      (FTP)     %s %s\n",
+                                     comp[FTP_ARG1],
+                                     ctime(time()) ) );
+          }
+      }
+      else
+          reply = "FAIL";
+      break;
+
+  case "read":
+DEBUG("-FtpAccess----\nHost:"+host+"Message:\n"+message+"\n--------------\n");
+      if ( sizeof(comp) <= FTP_ARG2 )
+          break;
+
+      if ( comp[FTP_ARG2][0] == '/' &&
+           valid_read( comp[FTP_ARG2], lower_case(comp[FTP_ARG1]),
+                       "read_file", 0 ) ){
+
+          BBMASTER->ftpbb( lower_case(comp[FTP_ARG1]),
+                           "read " + comp[FTP_ARG2] + "\n" );
+          reply = "OK";
+          }
+      else
+          reply = "FAIL";
+      break;
+
+  case "writ":
+DEBUG("-FtpAccess----\nHost:"+host+"Message:\n"+message+"\n--------------\n");
+      if ( sizeof(comp) <= FTP_ARG2 )
+          break;
+
+      if ( comp[FTP_ARG2][0] == '/' &&
+           valid_write( comp[FTP_ARG2], lower_case(comp[FTP_ARG1]),
+                       "write_file", 0 ) ){
+
+          BBMASTER->ftpbb( lower_case(comp[FTP_ARG1]),
+                           "write " + comp[FTP_ARG2] + "\n" );
+          reply = "OK";
+          }
+       else
+          reply = "FAIL";
+      break;
+
+  case "list":
+DEBUG("-FtpAccess----\nHost:"+host+"Message:\n"+message+"\n--------------\n");
+      if ( sizeof(comp) <= FTP_ARG2 )
+          break;
+
+      if ( comp[FTP_ARG2][0] == '/' &&
+           valid_read( comp[FTP_ARG2], lower_case(comp[FTP_ARG1]),
+                       "read_file", 0 ) )
+          reply = "OK";
+      else
+          reply = "FAIL";
+      break;
+
+  default:
+DEBUG("-FtpAccess----\nHost:"+host+"Message:\n"+message+"\n--------------\n");
+      log_file( "IMP_MSGS", "Host: " + host + ":" + port + " - '" +
+                message + "'\n" );
+      break;
+  }
+
+  send_udp( host, port, head+reply );
+#endif
+}
+
+
+static int doReadMail( string file )
+{
+    string str, *lines, *parts, *tmp;
+    mixed *message;
+    int i, j;
+
+    if ( (i = file_size(file)) > 50000 || i < 5 ){
+        rm(file);
+        DEBUG( "Mail size invalid\n" );
+        return -1;
+    }
+
+    if ( !(str = read_bytes( file, 0, 50000 )) )
+        return -1;
+
+    if ( !(j = sizeof(lines = explode( str, "\n" ))) )
+        return -2;
+
+    i = 0;
+
+    while ( i < j && lines[i] != "" )
+        i++;
+
+    if ( i == j )
+        return -2;
+
+ DEBUG( sprintf( "Have %d headerlines:\n", i ) );
+
+    message= allocate(9);
+    message[MSG_CC] = ({});
+    message[MSG_BCC] = ({});
+    message[MSG_BODY] = implode( lines[i..], "\n" );
+
+    for ( j = 0; j < i; j++ ){
+        parts = explode( lines[j], ":" );
+
+        if ( sizeof(parts) > 1 ){
+            str = lower_case(parts[0]);
+            parts[0] = implode( parts[1..], ":" );
+
+            switch (str){
+            case "subject":
+                message[MSG_SUBJECT] = parts[0];
+                break;
+
+            case "from":
+                DEBUG( "Found from\n" );
+                DEBUG( sprintf( "PARTS[0]=%s\n", parts[0] ) );
+                message[MSG_FROM] = mail_normalize(parts[0]);
+                message[MSG_SENDER] = parts[0];
+                DEBUG( sprintf( "FROM: %s\nSENDER: %s\n",
+                                message[MSG_FROM],
+                                message[MSG_SENDER] ) );
+                break;
+
+            case "cc":
+                DEBUG( sprintf("FOUND CC: %O\n", parts[0]) );
+                message[MSG_CC] += mk_rec_list(parts[0]);
+                break;
+
+            case "bcc":
+                DEBUG( sprintf("FOUND BCC: %O\n", parts[0]) );
+                message[MSG_BCC] += mk_rec_list(parts[0]);
+                break;
+
+            case "to":
+                DEBUG( sprintf("FOUND TO: %O\n", parts[0]) );
+                tmp = mk_rec_list(parts[0]);
+
+                if ( !message[MSG_RECIPIENT] )
+                    message[MSG_RECIPIENT] = tmp[0];
+
+                message[MSG_CC] += tmp;
+                break;
+
+            // Das MUD-TO: wird vom Perlskript als erste Headerzeile eingefuegt
+            case "mud-to":
+                DEBUG( sprintf("FOUND MUD-TO: %O\n", parts[0]) );
+                message[MSG_RECIPIENT] = mail_normalize(lower_case(parts[0]));
+                break;
+            }
+        }
+    }
+
+    // Eigentlichen Empfaenger aus CC: loeschen
+    message[MSG_CC] -= ({ message[MSG_RECIPIENT],
+                          message[MSG_RECIPIENT]+"@mg.mud.de",
+                          message[MSG_RECIPIENT]+"@morgengrauen.mud.de" });
+
+
+ DEBUG( sprintf( "TO: %O\n", message[MSG_RECIPIENT] ) );
+    DEBUG( sprintf( "CC: %O\n", message[MSG_CC] ) );
+    DEBUG( sprintf( "BCC: %O\n", message[MSG_BCC] ) );
+
+    if ( !stringp(message[MSG_FROM]) )
+        return -2;
+
+    if ( !stringp(message[MSG_RECIPIENT]) ){
+        str = explode( file, "/" )[<1];
+        i = 0;
+        j = sizeof(str);
+
+        while ( i < j && str[i] <= '9' && str[i] >= '0' )
+            i++;
+
+        if ( i >= j )
+            return -2;
+        
+        message[MSG_RECIPIENT] = str[i..];
+  DEBUG( sprintf( "EMERGENCY RECIPIENT=%s\n", str[i..] ) );
+    }
+
+    rm(file);
+
+    // Da vom Master besser nichts von ausserhalb /secure #include't wird,
+    // direkt die '5'. Normalerweise hiesse der Aufruf:
+    // DeliverMail( message, NO_USER_ALIASES|NO_CARBON_COPIES );
+    "/secure/mailer"->DeliverMail( message, 5 );
+    return 1;
+}
+
+
+public void mailread()
+{
+    string *files;
+    int i;
+
+    DEBUG( "mailread called\n" );
+
+    if ( mails_last_hour >= MAX_MAILS_PER_HOUR )
+        return;
+
+    files = (get_dir( "/mail/inbound/*" )||({})) - ({ "..", "." });
+    i = sizeof(files);
+
+    while ( i-- && mails_last_hour < MAX_MAILS_PER_HOUR ){
+        DEBUG( "FOUND FILE \"" + files[i] + "\" ...\n" );
+        mails_last_hour++;
+
+        if ( doReadMail( "/mail/inbound/" + files[i]) < -1 ){
+            mixed message;
+
+            message = allocate(9);
+            mails_last_hour++;
+            rename( "/mail/inbound/" + files[i],
+                    "/secure/ARCH/MAIL/" + files[i] );
+            message[MSG_SENDER] = geteuid();
+            message[MSG_FROM] = getuid();
+            message[MSG_SUBJECT] = "Fehlerhafte Mail: /secure/ARCH/MAIL/" +
+                files[i];
+            message[MSG_RECIPIENT] = "mud@mg.mud.de";
+            message[MSG_BODY] = "Bitte Ueberpruefen!\n";
+            // NO_SYSTEM_ALIASES|NO_USER_ALIASES == 3
+            "/secure/mailer"->DeliverMail( message, 3 );
+        }
+    }
+}
+
+
+static void udp_query( string query, string host, int port )
+{
+#if __EFUN_DEFINED__(send_udp)
+    string *mess;
+    mixed *data;
+    int i, j;
+
+    mess = explode( query, " " );
+    
+    switch ( mess[1] ){
+    case "wholist":
+    case "who":
+        data = (string *)"/obj/werliste"->QueryWhoListe();
+        break;
+
+    case "uptime":
+        data = ({ call_sefun("uptime") });
+        break;
+
+    case "finger":
+        if ( sizeof(mess) < 3 )
+            data = ({ "Error: Wen soll ich fingern ?" });
+        else
+            data = explode( (string)"p/daemon/finger"->
+                            finger_single( lower_case(mess[2]), 0 ), "\n" );
+        break;
+
+    case "mailread":
+        data = ({ "Okay" });
+        mailread();
+        break;
+
+    default:
+        data = ({ "Error: unknown request " + mess[1] + "\n" });
+    }
+
+
+    send_udp( host, port, sprintf( "%s 0 %d", mess[0], sizeof(data) ) );
+
+    for ( i = 0, j = sizeof(data); i < j; i++ )
+        send_udp( host, port, sprintf( "%s %d %s", mess[0], i+1, data[i] ) );
+#endif
+}
+
+#define UDP_DEBUG(x) 
+//#define UDP_DEBUG(x) (write_file("/log/ARCH/udp.log",(x)))
+
+void receive_udp(string host, string message, int port)
+{
+  mixed *tmp;
+  UDP_DEBUG(sprintf("%s %s:%d: %s\n",strftime(),host,port,message));
+
+  if (message[0..6]=="EXTREQ:"
+  	|| message[0..5]=="IPNAME"
+  	|| message[0..3]=="AUTH"
+  ) {	
+    return;
+  }
+
+  if( message[0..8]=="IPLOOKUP\n" ) {
+    "/p/daemon/iplookup"->update( message );
+    return;
+  }
+
+  if( message[0..9]=="DNSLOOKUP\n" ) {
+    "/p/daemon/dnslookup"->update( message );
+    return;
+  }
+
+  if (message[0..4]=="NFTPD") {
+#if __HOST_NAME__==MUDHOST
+    if (host!=FTPD_IP) {
+      DEBUG(sprintf("INVALID HOST: %s\n",host));
+      return;
+    }
+#endif
+    FtpAccess(host,message,port);
+    return;
+  }
+
+  if (message[0..9]=="udp_query:") {
+    return udp_query(message[10..],host,port);
+  }
+
+  "secure/inetd"->_receive_udp(host, message);
+}
+
+
diff --git a/secure/master/players_deny.c b/secure/master/players_deny.c
new file mode 100644
index 0000000..105704c
--- /dev/null
+++ b/secure/master/players_deny.c
@@ -0,0 +1,151 @@
+// MorgenGrauen MUDlib
+//
+// master.c -- master object
+//
+// $Id: master.c 7041 2008-10-13 18:18:27Z Zesstra $
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma verbose_errors
+#pragma combine_strings
+#pragma pedantic
+#pragma range_check
+#pragma warn_deprecated
+
+#define WHITELIST "/secure/ARCH/players_deny_whitelist.o"
+#define TARGETLIST "/secure/ARCH/players_deny_targets.o"
+#define WHITELISTDUMP "/secure/ARCH/players_deny_whitelist.dump"
+#define TARGETLISTDUMP "/secure/ARCH/players_deny_targets.dump"
+
+//#define PLDENY_LEARNMODE
+
+#include "/secure/wizlevels.h"
+#include "/secure/master.h"
+
+/* Diese Objekte duerfen aus /players/ includieren/erben */
+nosave private mapping whitelist;
+/* Diese Dinge werden von Objektn in whitelist aus /players/ inkludiert/geerbt. */
+nosave private mapping targets;
+
+private mapping ParseList(string list) {
+  mixed data;
+
+  if (!stringp(list)) return ([:0 ]);
+  if (!stringp(data = read_file(list))) return ([:0 ]);
+  data = explode(data, "\n") || ({});
+
+  return mkmapping(data);
+}
+
+private void ParseWhiteList() {
+  DEBUG("Parsing Whitelist\n");
+  whitelist=ParseList(WHITELISTDUMP);
+}
+private void ParseTargetList() {
+  DEBUG("Parsing Targetlist\n");
+  targets=ParseList(TARGETLISTDUMP);
+}
+
+private void DumpList(mapping list, string file) {
+  if (!stringp(file) || !mappingp(list)
+      || !sizeof(file) || !sizeof(list))
+    return;
+  
+  write_file(file, implode(m_indices(list),"\n"), 1);
+}
+
+private void DumpWhiteList() {
+  DEBUG("Dumping Whitelist\n");
+  DumpList(whitelist, WHITELISTDUMP);
+}
+private void DumpTargetList() {
+  DEBUG("Dumping Targetlist\n");
+  DumpList(targets, TARGETLISTDUMP);
+}
+
+public void DumpPLDenyLists() {
+  if ( extern_call() && call_sefun("secure_level") < ARCH_LVL )
+    return;
+  DEBUG("Dumping PLDenylists\n");
+  limited(#'DumpWhiteList);
+  limited(#'DumpTargetList);
+}
+
+public void SavePLDenyLists() {
+  if ( extern_call() && call_sefun("secure_level") < ARCH_LVL )
+    return;
+  DEBUG("Saving PLDenyLists\n");
+  write_file(WHITELIST,
+      save_value(whitelist), 1);
+  write_file(TARGETLIST,
+      save_value(targets), 1);
+}
+
+public void LoadPLDenylists() {
+  mixed tmp;
+
+  if ( extern_call() && call_sefun("secure_level") < ARCH_LVL )
+    return;
+
+  write("Loading PLDenylists\n");
+
+  if (stringp(tmp=read_file(WHITELIST))) {
+    // savefile exists
+    whitelist = restore_value(tmp);
+  }
+  else {
+      limited(#'ParseWhiteList);
+  }
+  if (stringp(tmp=read_file(TARGETLIST))) {
+    targets = restore_value(tmp);
+  }
+  else {
+      limited(#'ParseTargetList);
+  }
+}
+
+mixed include_file(string file, string compiled_file, int sys_include) {
+  
+  if (sys_include) return 0;
+  // Loggen, wenn Files ausserhalb /players/ Kram aus /players/
+  // inkludieren/erben.
+  if (strstr(file, "/players/") == 0
+      && (strstr(compiled_file,"/players/") == -1
+      && !member(whitelist, compiled_file) )) {
+#ifdef PLDENY_LEARNMODE
+    DEBUG("include_file(): Whitelisting: "+compiled_file+"\n");
+    m_add(whitelist, compiled_file);
+    m_add(targets, file);
+    call_sefun("log_file", "PLAYERSWHITELIST",
+        sprintf("%s (inkludiert %s)\n",compiled_file, file),
+        1000000);
+#else
+    // verweigern.
+    return -1;
+#endif
+  }
+  return 0;
+}
+
+mixed inherit_file(string file, string compiled_file) {
+
+  // Loggen, wenn Files ausserhalb /players/ Kram aus /players/
+  // inkludieren/erben.
+  if (strstr(file, "/players/") == 0
+      && (strstr(compiled_file,"/players/") == -1
+      && !member(whitelist, compiled_file) )) {
+#ifdef PLDENY_LEARNMODE
+    DEBUG("include_file(): Whitelisting: "+compiled_file+"\n");
+    m_add(whitelist, compiled_file);
+    m_add(targets, file);
+    call_sefun("log_file", "PLAYERSWHITELIST", 
+        sprintf("%s (erbt %s)\n",compiled_file, file),
+        1000000);
+#else
+    // verweigern.
+    return -1;
+#endif
+  }
+  return 0;
+}
+
diff --git a/secure/master/userinfo.c b/secure/master/userinfo.c
new file mode 100644
index 0000000..5d8f4ff
--- /dev/null
+++ b/secure/master/userinfo.c
@@ -0,0 +1,1119 @@
+// MorgenGrauen MUDlib
+//
+// master/userinfo.c -- Cache mit Spielerinfos
+//
+// $Id: userinfo.c 9467 2016-02-19 19:48:24Z Zesstra $
+
+#pragma strict_types
+
+#include "/secure/master.h"
+#include "/sys/files.h"
+#include "/sys/object_info.h"
+
+// Makro aus wizlevels.h ersetzen, da secure_level ne sefun ist. *seufz*
+#undef ARCH_SECURITY
+#define ARCH_SECURITY (call_sefun("secure_level") >= ARCH_LVL)
+
+private string*  ExpandUIDAlias(string alias, int rec);
+
+nosave mapping userlist;
+string name, password;
+string ektips;
+string fptips;
+int level;
+string shell;
+int creation_date;
+string ep, ek, mq;
+string *domains,*guilds, *uidstotakecare;
+nosave string* lateplayers;
+// hier wird ein Mapping von UID-zu-Magier-Zuordnungen gespeichert. Als Keys
+// werden UIDs verwendet, der Value ist jeweils ein Array mit den magiern, die
+// dafuer zustaendig sind.
+nosave private mapping userids = ([]);
+// Ein Cache fuer UID-Aliase, damit ExpandUIDAlias() nicht staendig ganze
+// Verzeichnisse einlesen muss, sondern der Master das nur im Reset machen
+// muss.
+nosave private mapping uidaliase = ([]);
+
+#ifdef _PUREFTPD_
+// Liste von FTP-berechtigten Usern
+nosave private mapping ftpuser = ([]);
+
+public void read_ftp_users() {
+  string str = read_file("/"SECUREDIR"/ARCH/pureftpd.passwd") || "";
+  string *data = explode(str, "\n");
+  if (!sizeof(data)) return;
+  ftpuser = m_allocate(sizeof(data), 1);
+  foreach(str : data) {
+    string *tmp=explode(str, ":");
+    if(sizeof(tmp) >= 2)
+      m_add(ftpuser, tmp[0], tmp[1]);
+  }
+}
+
+public int write_ftp_users() {
+  string *record = allocate(18,"");
+  mapping tmp = m_allocate(sizeof(ftpuser));
+  // set UID/GID of system user
+  record[2] = "1000";
+  record[3] = "1000";
+  foreach(string u, string pwhash : ftpuser) {
+    record[0] = u;  // UID
+    record[1] = pwhash;
+    record[5] = "/home/mud/mudlib/"WIZARDDIR"/" + u + "/./";
+    m_add(tmp, implode(record, ":"));
+  }
+  write_file("/"SECUREDIR"/ARCH/pureftpd.passwd", implode(m_indices(tmp),"\n"),1);
+  return sizeof(tmp);
+}
+
+// geloeschte Magier oder Magier, die 2 Wochen nicht da waren, expiren
+public void expire_ftp_user() {
+  foreach(string u : ftpuser) {
+    mixed uinfo = get_full_userinfo(u);
+    if (!uinfo) {
+      m_delete(ftpuser,u);
+      continue;
+    }
+    int zeit = call_sefun("file_time",
+                     "/"SECURESAVEPATH + u[0..0] + "/" + u + ".o");
+    if (zeit < time()-1209600) {
+      m_delete(ftpuser,u);
+      continue;
+    }
+  }
+  call_out(#'write_ftp_users, 2);
+}
+
+#endif // _PUREFTPD_
+
+// ********** oeffentliche Funktionen ***********************
+
+//Verantwortliche Magier fuer eine UID ausgeben
+public varargs string* QueryWizardsForUID(string uid, int recursive) {
+
+    if (!stringp(uid) || !sizeof(uid))
+        return(({}));
+
+    string *tolookup=({uid}); //diese spaeter in userids nachgucken.
+    string *wizards=({});
+    // Schauen, was automatisch ermittelt werden kann.
+    string *parts=explode(uid,".");
+    switch(sizeof(parts)) {
+         case 3:
+          // z.B. d.region.magier und p.service.magier
+          if (find_userinfo(parts[2]))
+              //Magier existiert, reinschreiben.
+              wizards+=({parts[2]});
+          if (parts[0]=="d")
+              // d.region noch nachgucken (RMs)
+              tolookup=({implode(parts[0..1],".")});
+          break;
+        //case 2:
+          // GUILD.gilde, p.project, d.region
+          // koennen nur in userids nachgeguckt werden (s.u. tolookup)
+          // muessen da als GUILD.*, d.* und p.* drinstehen!
+        case 1:
+          // kein Punkt drin. Entweder Magier-ID oder spezielle ID
+          // (letztere wird unten noch per tolookup nachgeguckt)
+          if (find_userinfo(parts[0]))
+              wizards+=({parts[0]});
+          break;
+    }
+    // jetzt in userids nachschlagen
+    foreach(uid: tolookup) {
+        if (member(userids,uid))
+            wizards+=userids[uid];
+    }
+    // so. Nun kann es aber noch sein, dass in userids sowas wie
+    // "d.wald.atamur":({atamur}) und "atamur":({"rumata"}) drinsteht, also
+    // ein Magier sozusagen fuer Kram eines anderen verantwortlich ist. D.h.
+    // nochmal durch QueryWizardsForUID() schicken (das ist dann allerdings nicht
+    // weiter rekursiv).
+    if (!recursive) {
+      foreach(uid: wizards) {
+        //es ist moeglich, in der Schleife wizards zu vergroessern, ohne dass
+        //es Einfluss auf die Schleife hat.        
+        wizards += QueryWizardsForUID(uid, 1) - ({uid});
+      }
+    }
+    return(m_indices(mkmapping(wizards)));
+}
+
+//das Define ist nicht sonderlich elegant, aber ich kann hier nicht das
+//newskills.h hier includen (1. zu viel, 2. ists nen Sicherheitsproblem)
+#define P_GUILD_DEFAULT_SPELLBOOK       "guild_sb"
+// dito fuer den Gildenmaster
+#define GUILDMASTER                 "/secure/gildenmaster"
+
+// Den Alias-Cache loeschen (normalerweise einmal am Tag)
+public void ResetUIDAliase() {
+  // RM+ duerfen den Cache loeschen (wenn sie z.B. nen neues Verzeichnis
+  // angelegt haben.)
+  if (extern_call() 
+      && call_sefun("secure_level") < LORD_LVL)
+      return;
+
+  uidaliase=([]);
+}
+
+// expandiert einige 'Aliase' in ein Array von allen UIDs, fuer die sie
+// stehen. Bsp: "region" -> d.region.* + region + d.region,
+// "zauberer" -> GUILD.zauberer, "p.service" -> p.service.*
+// Nutzt Eintrag aus dem uidalias-Cache, sofern vorhanden. 
+public varargs string* QueryUIDAlias(string alias, int rec) {
+  string *uids;
+  if (!stringp(alias) || !sizeof(alias))
+      return ({});
+  // Wen im cache, gehts schnell.
+  if (member(uidaliase, alias))
+      uids = uidaliase[alias];
+  else
+      uids = ExpandUIDAlias(alias, rec);
+
+  if (extern_call())
+    return copy(uids);
+  
+  return(uids);
+}
+
+// Fuer welche UIDs ist ein Magier verantwortlich? (Ist er RM,
+// Gildenmagier, in welchen Regionen hat er ein Verzeichnis, etc.)
+// recursive != 0 bedeutet, dass der Aufruf indirekt aus einem laufenden
+// QueryUIDsForWizard() erfolgt. In dem Fall soll QueryUIDAlias() keine
+// weitere Rekursion durchfuehren. Wird einfach durchgereicht.
+public varargs string* QueryUIDsForWizard(string wizuid, int recursive) {
+    string *uids, *tmp, *uidstoadd;
+    int i;
+
+    if (!stringp(wizuid) || !sizeof(wizuid) || !IS_LEARNER(wizuid))
+        return(({}));
+
+    if (!find_userinfo(wizuid))
+        return(({}));
+
+    uidstoadd=({}); //diese werden hinterher in userids gespeichert.
+
+    // als erstes die ratebaren UIDs. ;-)
+    uids=({wizuid});
+    // Regionen ermitteln, wo wizuid ein Verzeichnis hat und entsprechende
+    // UIDs anhaengen:
+    foreach(string region: get_domain_homes(wizuid)) {
+        uids+=({ sprintf(DOMAINDIR".%s.%s",region,wizuid) });
+    }
+    // Verzeichnis in /p/service?
+    if (file_size(PROJECTDIR"/service/"+wizuid) == FSIZE_DIR)
+        uids+=({PROJECTDIR".service."+wizuid});
+
+    // Gildenchef?
+    if (pointerp(userlist[wizuid,USER_GUILD])) {
+        foreach(string gilde: userlist[wizuid,USER_GUILD]) {
+          uidstoadd += QueryUIDAlias(gilde);
+        }
+    }
+    // Regionsmagier?
+    if (pointerp(userlist[wizuid,USER_DOMAIN])) {
+        foreach(string domain: userlist[wizuid,USER_DOMAIN]) {
+            //generelle Pseudo-UID 'd.region' und 'region' fuer geloeschte
+            //Magier dieser Region vormerken, um sie hinterher fuers
+            //Reverse-Lookup ins uid-Mapping zu schreiben.
+            string *pseudo=({DOMAINDIR"."+domain, domain});
+            uidstoadd += pseudo;
+            // Rest macht QueryUIDAlias, dabei aber die von der Funktion
+            // ebenfalls gelieferten Pseudo-UIDs wieder abziehen.
+            uids += QueryUIDAlias(domain) - pseudo;
+        }
+    }
+    // jetzt noch nachgucken, fuer welche UIDs dieser Magier explizit noch
+    // zustaendig ist.
+    if (pointerp(userlist[wizuid,USER_UIDS_TO_TAKE_CARE])) {
+    // dies koennte etwas a la "region" oder "anderermagier" sein, d.h. dieser
+    // Magier ist fuer alle UIDs von 'andermagier' auch zustaendig. Daher muss
+    // jedes davon durch QueryUIDAlias() (was im Falle von Spielern wiederum
+    // QueryUIDsForWizard() ruft, aber die Rekursion im Falle von Spielern ist
+    // auf 1 begrenzt).
+        foreach(string uid2: userlist[wizuid,USER_UIDS_TO_TAKE_CARE]) {        
+            uidstoadd += QueryUIDAlias(uid2, recursive);
+        }
+    }
+
+    // so, in uidstoadd stehen UIDs drin, die nicht Magiername selber,
+    // d.region.magier oder p.service.magier sind und bei welchen folglich das
+    // Mapping UIDs-nach-Magier nur mit einer Liste moeglich ist. In die
+    // werden die uids nun eingetragen. (z.B. d.region)
+    if (sizeof(uidstoadd)) {
+        // genug Platz in userids? Sonst welche rauswerfen. :-/ 
+        // (besser als bug) TODO: Auf 10k begrenzen -> max Arraygroesse!
+        if ( sizeof(userids)+(i=sizeof(uidstoadd))               
+            >= __MAX_MAPPING_KEYS__) {
+            foreach(string tmpuid: m_indices(userids)[0..i])        
+              m_delete(userids,tmpuid);  
+        }
+        foreach(string tmpuid: uidstoadd) {  
+            if (member(userids,tmpuid)) {  
+                //User dem Array hinzufuegen, wenn noch nicht drin.
+                if (member(userids[tmpuid],wizuid)==-1)
+                    userids[tmpuid]=userids[tmpuid]+({wizuid});              
+            }
+            //sonst neuen Eintragen hinzufuegen            
+            else
+                m_add(userids,tmpuid,({wizuid}));          
+        }
+    } // Ende spez. uids speichern
+    
+    return(uids+uidstoadd);
+}
+
+//Einen Magier als verantwortlich fuer eine bestimmte UID eintragen
+public string* AddWizardForUID(string uid, string wizuid) {
+    if (!stringp(wizuid) || !sizeof(wizuid)
+        || !IS_LEARNER(wizuid))
+        return(({}));
+
+    //Zugriff nur als EM oder jemand, der fuer die UID zustaendig ist.
+    if ( !ARCH_SECURITY
+        && member(
+            QueryUIDsForWizard(call_sefun("secure_euid")),
+            uid) == -1)
+        return(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]);
+
+    if (!pointerp(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]))
+        //Array neu anlegen
+        userlist[wizuid,USER_UIDS_TO_TAKE_CARE]=({uid});
+    else {
+        //Ein Array schon vorhanden
+        if (member(userlist[wizuid,USER_UIDS_TO_TAKE_CARE],uid)==-1)
+            //uid nicht drin
+            userlist[wizuid,USER_UIDS_TO_TAKE_CARE]=
+              userlist[wizuid,USER_UIDS_TO_TAKE_CARE]+({uid});
+    }
+    save_userinfo(wizuid);
+    // aus dem UID-Alias-Cache werfen
+    m_delete(uidaliase, wizuid);
+    // Aufruf, um userids und uidaliase zu aktualisieren
+    QueryUIDsForWizard(wizuid);
+    return(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]);
+}
+
+// Einen Magier wieder austragen, wenn er nicht mehr zustaendig ist.
+public string* RemoveWizardFromUID(string uid, string wizuid) {
+    if (!stringp(wizuid) || !sizeof(wizuid)
+        || !find_userinfo(wizuid))
+        return(({}));
+
+    //Zugriff nur als EM oder jemand, der fuer die UID zustaendig ist.
+    if ( !ARCH_SECURITY
+        && member(
+            QueryUIDsForWizard(call_sefun("secure_euid")),
+            uid)==-1)
+        return copy(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]);
+
+    // jetzt muss diese wizuid aus allen UIDs in userids geloescht werden, die
+    // sie bisher enthalten. Hierzu sollte QueryUIDAlias die potentiell
+    // drinstehenden UIDs liefern.
+    foreach(string tuid: QueryUIDAlias(wizuid,0)) {
+        if (member(userids, tuid) &&
+            member(userids[tuid],wizuid)!=-1 )
+            userids[tuid] -= ({wizuid});
+    }
+    // wenn es eine UID war, fuer die der Magier explizit zustaendig war,
+    // entsprechend loeschen. Sonst ist hier Ende.
+    if (!pointerp(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]))
+        return ({});
+    if (member(userlist[wizuid,USER_UIDS_TO_TAKE_CARE],uid)==-1)
+        return copy(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]);
+
+    // Jetzt aus userlist loeschen.
+    userlist[wizuid,USER_UIDS_TO_TAKE_CARE] -= ({uid});
+    save_userinfo(wizuid);
+    // und userids/uidaliase aktualisieren.
+    QueryUIDsForWizard(wizuid);
+    return copy(userlist[wizuid,USER_UIDS_TO_TAKE_CARE]);
+}
+
+//entfernt einen user aus dem usercache
+void RemoveFromCache(string user) {
+  m_delete(userlist,user);
+}
+
+//loescht den gesamten Usercache
+int clear_cache() {
+  userlist=m_allocate(0,widthof(userlist));
+  update_late_players();
+  return 1;
+}
+
+// Loescht UID->Magier Lookuptabelle. Sollte nicht gerufen werden, wenn man 
+// nicht genau weiss, was man damit kaputtmacht.
+// als Nebeneffekt wird clear_cache() gerufen.
+int ResetUIDCache() {
+  // da diese Funktion auch das UID<->Magier-Lookup loescht, darf das nicht
+  // jeder rufen.
+  if (extern_call() &&
+      call_sefun("secure_level") < ELDER_LVL)
+      return -1;
+  userids=([]);
+  clear_cache();
+  return 1;
+}
+
+//verstorbene Spieler auflisten
+string list_late_players()
+{
+  string ret;
+  int size,i;
+  
+  ret= "************************************\n";
+  ret+="*       Verstorbene Spieler        *\n";
+  ret+="************************************\n";
+  
+  if(!lateplayers || (size=sizeof(lateplayers))==0)
+  {
+    return ret;
+  }
+  
+  for(i=0;i<size;i++)
+  {
+    ret+=lateplayers[i]+"\n";
+  }
+  
+  return ret;
+}
+
+// ist der Spieler gestorben?
+int check_late_player(string str)
+{
+  if(!lateplayers || !str || str=="")
+  {
+    return 0;
+  }
+  
+  if(member(lateplayers,str)!=-1)
+  {
+    return 1;
+  }
+  
+  return 0;
+}
+
+// alle Eintraege im usercache ausgeben
+public mixed *show_cache() {
+    return m_indices(userlist);
+}
+
+// gibt es so einen User? Anfrage ausserdem immer in den Cache eintragen
+public int find_userinfo(string user) {
+  string file;
+  int i;
+  if (!stringp(user) || !sizeof(user) 
+      || member(user,' ')!=-1 || member(user,':')!=-1)
+    return 0;
+  if (!member(userlist,user)) {
+      //erstmal schauen, ob wir mit einem neuen Eintrag nicht die max.
+      //Mapping-Groessen ueberschreiten, wenn ja, einen Eintrag loeschen
+      //BTW: widthof()+1 ist richtig so.
+      //BTW2: Ich hoffe, die max. Arraygroesse ist immer gross genug, sollte 
+      //sie aber sein bei ner Mappingbreite von 10.
+      // BTW3: Dieses Rausloeschen von einem Eintrag bei Erreichen der Grenze
+      // erhoeht die benoetigten Ticks fuer diese Funktion um das 5-6fache und
+      // die noetige Zeit um das 70fache. Daher wird der Cache bei Erreichen
+      // der Grenze jetzt einfach geloescht.
+      if ( ( (i=sizeof(userlist)+1) >= __MAX_MAPPING_KEYS__) 
+          || (( i * (widthof(userlist)+1)) >
+                 __MAX_MAPPING_SIZE__))    
+          //m_delete(userlist,m_indices(userlist)[0]);
+          userlist=m_allocate(1,widthof(userlist));
+
+      // Usersavefile finden
+      if ((file=secure_savefile(user))=="") {
+        // User gibt es nicht, aber Anfrage cachen
+          userlist+=([user: "NP"; -1; ({}); "LOCKED"; -1; time(); ""; ""; "";
+          ({});"";""; 0 ]);
+          return 0;
+      }
+      password="";
+      ep="";
+      ek="";
+      mq="";
+      guilds=({});
+      ektips="";
+      fptips="";
+      uidstotakecare=0;
+      if (!restore_object(file)) return 0;
+      userlist+=([user: password; level; domains; shell; creation_date; 
+          time();ep; ek; mq; guilds; ektips; fptips; uidstotakecare]);
+      // die speziellen UIDs, fuer die dieser Magier zustaendig ist, ermitten
+      // und gleichzeitig im entsprechenden Mapping fuers Reverse-Loopup
+      // speichern.
+      if (level >= LEARNER_LVL)
+        QueryUIDsForWizard(user);
+  }
+  userlist[user,USER_TOUCH]=time();
+  if (userlist[user,USER_LEVEL]==-1) return 0;
+  return 1;
+}
+
+// Daten aus der Userlist fuer diesen User liefern. Macht ggf. find_userinfo()
+public mixed *get_userinfo(string user) {
+  if(!user||user=="")
+    return 0;
+  user=explode(user,".")[0];
+  if (!member(userlist,user) && !find_userinfo(user))
+    return 0;
+
+  if (userlist[user,USER_LEVEL]==-1) return 0;
+
+  return({user,"##"+user,userlist[user,USER_LEVEL],
+      userlist[user,USER_DOMAIN], userlist[user,USER_OBJECT],
+      userlist[user,USER_CREATION_DATE], 0, 0,
+      userlist[user,USER_GUILD],
+      userlist[user,USER_EKTIPS],userlist[user,USER_FPTIPS],
+      userlist[user,USER_UIDS_TO_TAKE_CARE]});
+}
+
+// liefert das Objekt des users aus der Userlist zurueck.
+public string query_player_object( string name )
+{
+  mixed *userentry;
+  if( !find_userinfo(name) ) return "";
+  return userlist[name,USER_OBJECT];
+}
+
+// Passwort ok?
+public int good_password( string str, string name )
+{
+    string rts;
+    int i, n;
+
+    if ( str[0] == '!' ){
+        tell_object( this_player() || this_object(), "Ein Ausrufungszeichen "
+                     "('!') als erster Buchstabe des Passwortes wuerde zu\n"
+                     "Problemen fuehren. Such Dir bitte ein anderes Passwort "
+                     "aus.\n" );
+        return 0;
+    }
+
+    if ( sizeof(str) < 6 ){
+        tell_object( this_player() || this_object(),
+                     "Das Passwort muss wenigstens 6 Zeichen lang sein.\n" );
+        return 0;
+    }
+
+    str = lower_case(str);
+    rts = "";
+
+    // Zahlen/Sonderzeichen am Anfang oder Ende des Passwortes
+    // (z.B. "merlin99") sind nicht wirklich einfallsreich.
+    while ( sizeof(str) && (str[0] > 'z' || str[0] < 'a') ){
+        rts += str[0..0];
+        str = str[1..];
+    }
+
+    while ( sizeof(str) && (str[<1] > 'z' || str[<1] < 'a') ){
+        rts += str[<1..];
+        str = str[0..<2];
+    }
+
+    // Anzahl unterschiedlicher Zeichen merken, die herausgeschnitten wurden
+    n = sizeof( mkmapping(efun::explode( rts, "" )) );
+    rts = "";
+
+    for ( i = sizeof(str); i--; )
+        rts += str[i..i];
+
+    // Eigener Name als Passwort bzw. eigener Name rueckwaerts
+    if ( str == lower_case(name) || rts == lower_case(name) ||
+         // Name eines anderen Mudders (Erstie?)
+         secure_savefile(str) != "" || secure_savefile(rts) != "" ||
+         // Name von der Banish-Liste
+         QueryBanished(str) || QueryBanished(rts) ||
+         // Name eines vorhandenen NPC o.ae.
+         call_sefun("find_living", str ) ||
+         call_sefun("find_living", rts ) ||
+         // Zuwenig verschiedene Zeichen
+         sizeof( mkmapping(efun::explode( str, "" )) ) + n < 4 ){
+        tell_object( this_player() || this_object(),
+                     "Das Passwort waere zu einfach zu erraten. Nimm bitte "
+                     "ein anderes.\n" );
+        return 0;
+    }
+
+    return 1;
+}
+
+// User-ID fuer ein File ermitteln.
+public string get_wiz_name(string file) {
+    return creator_file(file);
+}
+
+// Wizlevel aus dem Userinfo-Cache holen
+public int get_wiz_level(string user) {
+  if (user && find_userinfo(user)) 
+    return userlist[user,USER_LEVEL];
+  //return 0 if no user given (return type needed)
+  return(0);
+}
+
+// Wizlevel fuer eine UID ermitteln.
+public int query_wiz_level( mixed player )
+{
+    if ( objectp(player) && efun::object_info(player, OI_ONCE_INTERACTIVE) )
+        return get_wiz_level( getuid(player) );
+    else {
+        // erstmal UID ermitteln, falls Objekt
+        //if (objectp(player))
+        //    player=getuid(player);
+        if ( stringp(player) ) {
+            if( player[0..1]==DOMAINDIR"." ) return 25;
+            if( player[0..5]==GUILDID"." ) 
+                return WIZLVLS[GUILDID];
+            if( player[0..1]==PROJECTDIR"." ) return 21;
+            // die alte Loesung mit || verhaelt sich falsch, wenn ein UID ne
+            // spezielle ist, der Level 0 zugeordnet wurde und es einen
+            // Spieler mit diesem namen gibt.
+            if (member(WIZLVLS,player)) 
+                return(WIZLVLS[player]);
+            return get_wiz_level(player);
+        }
+    }
+    return 0;
+}
+
+// Savefile aus /secure/save* zurueckliefern
+public string secure_savefile(string name)
+{
+  if(!name||name=="")
+    return "";
+  name=explode(name,".")[0];
+  if (file_size(SECURESAVEPATH+name[0..0]+"/"+name+".o")>=0)
+    return SECURESAVEPATH+name[0..0]+"/"+name;
+
+  return "";
+}
+
+// *************** 'halb-oeffentliche Funktionen *********************
+#ifdef _PUREFTPD_
+// FTP-Berechtigung fuer Magier
+int update_ftp_access(string user, int state) {
+  // wenn nicht EM+ oder ROOT, darf nur fuer this_interactive geaendert
+  // werden.
+  if (getuid(PO) != ROOTID && extern_call()
+      && !ARCH_SECURITY) {
+    if (this_interactive())
+      user = getuid(this_interactive());
+    else
+      user = 0;
+  }
+  if (!user || !sizeof(user))
+    return -1;
+
+  if (!find_userinfo(user))
+    return 0;
+
+  // Passwort muss manuell vom Magier neu gesetzt werden, da es keine
+  // Moeglichkeit gibt, an das aktuelle im Klartext heranzukommen.
+  if (state) {
+    if (!member(ftpuser,user))
+      m_add(ftpuser, user, "*");
+  }
+  else
+    m_delete(ftpuser, user);
+  
+  call_out(#'write_ftp_users, 4);
+  return state;
+}
+#endif
+
+// Spieler ein neues Wizlevel verpassen.
+int update_wiz_level(string user,int lev) {
+  object ob;
+
+  if (getuid(PO) != ROOTID && extern_call()) return 0;
+  if (!find_userinfo(user)) return 0;
+  userlist[user,USER_LEVEL] = lev;
+  save_userinfo(user);
+  return 1;
+}
+
+// neue Forscherpunkte fuer den User abspeichern.
+int update_ep(string user,string ep_neu) {
+  if (getuid(PO) != ROOTID) return 0;
+  if (!find_userinfo(user)) return 0;
+  userlist[user,USER_EP] = ep_neu;
+  save_userinfo(user);
+  return 1;
+}
+
+// neue Erstkills fuer den User abspeichern.
+int update_ek(string user,string ek_neu) {
+  if (getuid(PO) != ROOTID) return 0;
+  if (!find_userinfo(user)) return 0;
+  userlist[user,USER_EK] = ek_neu;
+  save_userinfo(user);
+  return 1;
+}
+
+// Miniquestdaten speichern.
+int update_mq(string user,string mq_neu) {
+  if (getuid(PO) != ROOTID) return 0;
+  if (!find_userinfo(user)) return 0;
+  userlist[user,USER_MQ] = mq_neu;
+  save_userinfo(user);
+  return 1;
+}
+
+// Erstkillpunkt-Tips speichern.
+int update_ektips(string user,string ek_neu) {
+  if (getuid(PO) != ROOTID) return 0;
+  if (!find_userinfo(user)) return 0;
+  userlist[user,USER_EKTIPS] = ek_neu;
+  save_userinfo(user);
+  return 1;
+}
+
+// Forscherpunkttips abspeichern.
+int update_fptips(string user,string fp_neu) {
+  if (getuid(PO) != ROOTID) return 0;
+  if (!find_userinfo(user)) return 0;
+  userlist[user,USER_FPTIPS] = fp_neu;
+  save_userinfo(user);
+  return 1;
+}
+
+// forscherpunkte abfragen.
+string query_ep(string user) {
+  if (getuid(PO) != ROOTID) return 0;
+  if (!find_userinfo(user)) return 0;
+  return userlist[user,USER_EP];
+}
+
+// Erstkills abfragen
+string query_ek(string user) {
+  if (getuid(PO) != ROOTID) return 0;
+  if (!find_userinfo(user)) return 0;
+  return userlist[user,USER_EK];
+}
+
+// Miniquests abfragen.
+string query_mq(string user) {
+  if (getuid(PO) != ROOTID) return 0;
+  if (!find_userinfo(user)) return 0;
+  return userlist[user,USER_MQ];
+}
+
+// EK-Tips abfragen.
+string query_ektips(string user) {
+  if (getuid(PO) != ROOTID) return 0;
+  if (!find_userinfo(user)) return 0;
+  return userlist[user,USER_EKTIPS];
+}
+
+// FP-Tips abfragen.
+string query_fptips(string user) {
+  if (getuid(PO) != ROOTID) return 0;
+  if (!find_userinfo(user)) return 0;
+  return userlist[user,USER_FPTIPS];
+}
+
+#define PLAYERSHELLS ({"/std/shells/darkelf", "/std/shells/dwarf", \
+    "/std/shells/elf", "/std/shells/feline", "/std/shells/hobbit", \
+    "/std/shells/human" })
+
+// Aendert die Shells eines Users.
+int set_player_object( string user, string objectname )
+{
+    mixed *path;
+    string prev;
+
+    // nur EM und ROOT duerfen die Shell eines Charakters aendern
+    if ( !ARCH_SECURITY &&
+         (!previous_object() || getuid(previous_object()) != ROOTID) ) {
+        return -1;
+    }
+
+    if ( objectname == "" )
+        objectname = 0;
+
+    if ( !stringp(user) || user == "" )
+        return -6;
+
+    if ( !stringp(objectname) ){
+        if ( !find_userinfo(user) )
+            return -4;
+
+        userlist[user, USER_OBJECT] = 0;
+        save_userinfo(user);
+        return 1;
+    }
+
+    if ( catch(load_object(objectname);publish) ) {
+        write( "Fehler in " + objectname + "!\n" );
+        return -2;
+    }
+
+    objectname = _get_path( objectname, 0 );
+    path = (efun::explode( objectname, "/" ) - ({ "", 0 }));
+
+    if ( sizeof(path) < 3 || path[0] != "std" || path[1] != "shells" )
+        return -3;
+
+    if ( !find_userinfo(user) )
+        return -4;
+
+    prev = userlist[user, USER_OBJECT];
+    userlist[user, USER_OBJECT] = objectname;
+    save_userinfo(user);
+
+    // Loggen, falls die Aenderung nicht von Login beim Anlegen des Chars
+    // erfolgt.
+    if (load_name(this_interactive()) != "/secure/login"
+        || prev != "") {
+      if (prev == "") prev ="<keine>";
+      call_sefun("log_file", "ARCH/SHELL_AENDERUNGEN",
+        sprintf( "%s: %O aendert die Shell von %s von %s auf %s (PO: %O)\n",
+          strftime("%Y%m%d-%H%M%S"), 
+          this_interactive(), capitalize(user), prev, objectname,
+          previous_object()) );
+    }
+
+    return 1;
+}
+
+// Passwort aktualisieren.
+int update_password( string old, string new )
+{
+    string user;
+
+    // nanu, kein user?
+    if ( !find_userinfo(user = getuid(PO)) )
+        return 0;
+
+    // wenn das neue PW unterschiedlich ist, schauen, ob das neue PW ok ist.
+    if ( old != new && !good_password( new, user ) )
+        return 0;
+
+    string pwhash = userlist[user, USER_PASSWORD];
+    string oldpwhash;
+    if (sizeof(pwhash) > 13) {
+        // MD5-Hash
+        oldpwhash = md5_crypt(old, pwhash);
+    }
+    else if (sizeof(pwhash) > 2) {
+        // Crypt-Hash
+        oldpwhash = crypt(old, pwhash[0..1]);
+    }
+
+    // wenn es einen PW-hash gibt, also ein PW gesetzt wird, muss der Hash von
+    // old mit dem pwhash uebereinstimmen. Leerer Hash oder gar kein Hash
+    // erlaubt jedes beliebige PW.
+    if ( stringp(pwhash) && sizeof(pwhash) && pwhash != oldpwhash)
+        return 0;
+    // an dieser Stelle stimmt 'old' wohl mit dem gesetzten Passwort ueberein.
+    // Wenn allerdings die Funktion mit old==new aufgerufen wurde, wird hier
+    // nur 1 zurueckgeben und sonst an sich nix gemacht. Kann nicht weiter
+    // oben schon gemacht werden, die Shells dies als PW-Pruefung nutzen.
+    // *seufz*
+    if (old == new) return 1;
+
+    // dann mal neu setzen
+    userlist[ user, USER_PASSWORD ] = md5_crypt( new, 0 );
+    save_userinfo(user);
+#ifdef _PUREFTPD_
+    // Bedauerlicherweise versteht pureftpd unser md5_crypt nicht. :-(
+    if (member(ftpuser,user)) {
+      ftpuser[user] = crypt(new);
+      if (find_call_out(#'write_ftp_users) == -1)
+        call_out(#'write_ftp_users,4);
+    }
+#endif
+    return 1;
+}
+
+// Spieler loeschen.
+int delete_player(string passwd, string real_name)
+{
+  int wlevel;
+  string part_filename;
+
+  if (!PO || PO!=TP || PO!=TI || real_name != getuid(PO) ||
+      !find_userinfo(real_name))
+    return 0;
+  mixed erstie=(mixed)this_interactive()->QueryProp(P_SECOND);
+  password = userlist[real_name,USER_PASSWORD];
+  wlevel = get_wiz_level(real_name);
+  if (!update_password(passwd, passwd)) return 0;
+
+  // Spielpausen aufheben (sonst kann man als Spieler nen Namen sperren).
+  TBanishName(real_name, 0);
+
+  part_filename="/"+real_name[0..0]+"/"+real_name+".o";
+  rm("/"SECUREDIR"/save"+part_filename);
+  rm("/"LIBSAVEDIR"/"+part_filename);
+  rm("/"MAILDIR"/"+part_filename);
+  
+  m_delete(userlist,real_name);
+  
+  if (wlevel >= LEARNER_LVL)
+    TO->BanishName(real_name, "So hiess mal ein Magier hier");
+  else if (wlevel >= SEER_LVL)
+    TO->BanishName(real_name, "So hiess mal ein Seher hier");
+
+#ifdef _PUREFTPD_
+    if (member(ftpuser,real_name)) {
+      m_delete(ftpuser,real_name);
+      call_out(#'write_ftp_users,4);
+    }
+#endif
+
+  call_sefun("log_file", "USERDELETE",
+           sprintf("%s: %s %s(%s)\n",
+                   ctime(time()),real_name,
+                   (stringp(erstie)?sprintf("[Erstie: %s] ",erstie):""),
+                   call_sefun("query_ip_number",TI)));
+
+  return 1;
+}
+
+// ermittelt alle existierenden Spieler, die ein Savefile in /secure/save
+// haben.
+// ([ "a": ({...namen...}), "b": ({...namen...}), ... ]) 
+public mapping get_all_players() {
+  string *dirs=({"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o",
+      "p","q","r","s","t","u","v","w","x","y","z"});
+  mapping allplayer=([]);
+  string *tmp;
+  foreach(string dir: dirs) {
+    tmp=get_dir("/secure/save/"+dir+"/*")
+      - ({".","..",".KEEP",".svn"});
+    allplayer[dir] = map(tmp,function string (string fn) 
+                     { return explode(fn,".")[0]; } );
+  }
+  return allplayer;
+}
+
+// *************** interne Funktionen ********************************
+protected void create()
+{
+  userlist=m_allocate(0,12);
+  update_late_players();
+#ifdef _PUREFTPD_
+  read_ftp_users();
+#endif
+}
+
+//verstorbene Spieler einlesen
+void update_late_players() {
+  string read;
+  string *tmp;
+  
+  lateplayers=0;
+  
+  read=read_file("/"SECUREDIR"/LATE_PLAYERS");
+  if(!read || read=="")
+  {
+    return;
+  }
+  
+  tmp=explode(read,"\n");
+  if(!sizeof(tmp))
+  {
+    return;
+  }         
+  
+  lateplayers=tmp; 
+}
+
+
+// Daten aus der Userlist inkl. Passworthash zurueckliefern. Macht ggf.
+// find_userinfo(). Nur masterintern aufrufbar.
+protected mixed *get_full_userinfo(string user) {
+  if(!user||user=="")
+    return 0;
+  user=explode(user,".")[0];
+  if (!member(userlist,user) && !find_userinfo(user))
+    return 0;
+
+  return({user,userlist[user,USER_PASSWORD],userlist[user,USER_LEVEL],
+      userlist[user,USER_DOMAIN], userlist[user,USER_OBJECT],
+      userlist[user,USER_CREATION_DATE], 0, 0, userlist[user,USER_GUILD],
+      userlist[user,USER_EKTIPS],userlist[user,USER_FPTIPS],
+      userlist[user,USER_UIDS_TO_TAKE_CARE]});
+}
+
+// Userdaten aus der Userlist im Savefile in /secure/save/ speichern.
+protected void save_userinfo(string user) {
+  if(!user||user=="")
+    return;
+  user=explode(user,".")[0];
+  if (!member(userlist,user)) return;
+  name = user;
+  level = userlist[name,USER_LEVEL];
+  domains = userlist[name,USER_DOMAIN];
+  shell = userlist[name,USER_OBJECT];
+  password = userlist[name,USER_PASSWORD];
+  creation_date = userlist[name,USER_CREATION_DATE];
+  if (!creation_date) creation_date = -1;
+  ep = userlist[name,USER_EP];
+  if (!ep) ep="";
+  ek = userlist[name,USER_EK];
+  if (!ek) ek="";
+  mq = userlist[name,USER_MQ];
+  if (!mq) mq="";
+  guilds = userlist[name,USER_GUILD];
+  ektips=userlist[name,USER_EKTIPS];
+  if(!ektips) ektips="";
+  fptips=userlist[name,USER_FPTIPS];
+  if(!fptips) fptips="";
+  uidstotakecare=userlist[name,USER_UIDS_TO_TAKE_CARE];
+
+  if (save_object(SECURESAVEPATH+name[0..0]+"/"+name) != 0) {
+    // autsch. Buggen damit dieser moeglichst schnell auffaellt, dass hier
+    // Savefiles in /secure/save/ nicht geschrieben wurden.
+    raise_error(sprintf(
+          "Savefile %O konnte nicht erstellt werden!\n",
+          SECURESAVEPATH+name[0..0]+"/"+name));
+  }
+}
+
+// In welchen Regionen ist der Spieler Regionsmagier?
+protected void set_domains(string player, string *domains)
+{
+  // wenn der Magier jetzt Domains nicht mehr hat, muessen die aus 'userids'
+  // entfernt werden, das uebernimmt RemoveWizardFromUID().
+  if (pointerp(userlist[player, USER_DOMAIN])) {
+    string *removeduids=
+      ((string*)userlist[player, USER_DOMAIN] | domains) - domains;
+    foreach(string uid: removeduids)
+      RemoveWizardFromUID(uid, player);
+  }
+  // gecachtes Alias fuer player loeschen
+  m_delete(uidaliase,player);
+  userlist[player, USER_DOMAIN]=domains;
+  save_userinfo(player);
+  // UID-zu-Magier-Mapping aktualisieren
+  QueryUIDsForWizard(player);
+}
+
+// In welche Gilden ist der Spieler Gildenmagier?
+protected void set_guilds(string player, string *guilds)
+{
+  // wenn der Magier jetzt Gilden nicht mehr hat, muessen die aus 'userids'
+  // entfernt werden, das uebernimmt RemoveWizardFromUID().
+  if (pointerp(userlist[player, USER_GUILD])) {
+    string *removeduids=
+      ((string*)userlist[player, USER_GUILD] | guilds) - guilds;
+    foreach(string uid: removeduids)
+      RemoveWizardFromUID(uid, player);
+  }
+  // gecachtes Alias fuer player loeschen
+  m_delete(uidaliase,player);
+  userlist[player, USER_GUILD]=guilds;
+  save_userinfo(player);
+  // UID-zu-Magier-Mapping aktualisieren
+  QueryUIDsForWizard(player);
+}
+
+// Userinfo-Cache expiren...
+protected void _cleanup_uinfo()
+{
+    foreach(string user: userlist) {
+        if ((time() - userlist[user,USER_TOUCH]) > 1800
+            && !call_sefun("find_player",user))
+          m_delete(userlist,user);
+    }
+}
+
+// expandiert einige 'Aliase' in ein Array von allen UIDs, fuer die sie
+// stehen. Bsp: "region" -> d.region.* + region + d.region,
+// "zauberer" -> GUILD.zauberer, "p.service" -> p.service.* oder auch
+// "magier" -> QueryUIDsForWizard().
+// Das erfolgt ueber Lookup der entsprechenden Verzeichnisse auf der PLatte.
+// Da zusaetzlich auch noch jede Menge find_userinfo() dazu kommen, ist das
+// recht aufwaendig. Damit das ganze nicht jedesmal erfolgen muss, wird das
+// Ergebnis gecacht und QueryUIDAlias() nutzt den Cache.
+private string* ExpandUIDAlias(string alias, int rec) {
+
+  string *uids=({});
+
+  // Regionsname?
+  if (file_size("/"DOMAINDIR"/"+alias) == FSIZE_DIR) {
+    //generelle Pseudo-UID 'd.region' und 'region' fuer geloeschte
+    //Magier dieser Region
+    uids += ({DOMAINDIR"."+alias, alias});
+    //alle Magier-Verzeichnisse ermitteln:
+    string tmpdir="/"DOMAINDIR"/"+alias+"/";
+    foreach(string dir: (get_dir(tmpdir+"*") || ({}))
+                          - ({".","..",".svn"})) {
+      // schauen, obs nen (sichtbares) Verzeichnis ist und ob der Magier
+      // existiert. Letzteres aber nur, falls die Rekursionstiefe min. 100
+      // ist, da beim Nachschauen eines Magiers mit (mehreren)
+      // RM-Posten, der in der Region RMs aus anderen Regionen hat, leicht
+      // Rekursionstiefen von 20 (*4) auftreten koennen, wenn noch gar
+      // keine UIDs im Cache sind (find_userinfo() ruft indirekt diese
+      // Funktion).
+      if (dir[0]!='.' 
+          && file_size(tmpdir+dir) == FSIZE_DIR
+#if __MAX_RECURSION__ > 99
+          && find_userinfo(dir)
+#endif
+          )
+          uids += ({DOMAINDIR"."+alias+"."+dir});
+    }
+  }
+  // Gildenname?
+  else if (GUILDMASTER->ValidGuild(alias)) {
+      uids += ({GUILDID"."+alias});
+      //hat die Gilde ein Projektverzeichnis?
+      if (file_size("/"PROJECTDIR"/"+alias) == FSIZE_DIR) {
+          uids += ({PROJECTDIR"."+alias});
+      }
+      // jetzt haben dummerweise die Spellbooks meist nicht den gleichen
+      // Namen wie die Gilde. D.h. die Gilde muss nun noch nach dem
+      // Namen des Spellbooks gefragt werden, weil dessen UID nicht
+      // unbedingt gleich dem der Gilde ist. *seufz*
+      string spbook;
+      object guild;
+      catch(guild=load_object(GUILDDIR"/"+alias));
+      if (objectp(guild) 
+          && (sizeof(spbook=(string)
+                 guild->QueryProp(P_GUILD_DEFAULT_SPELLBOOK)))
+                && spbook!=alias)
+          uids += ({GUILDID"."+spbook});
+  }
+  // Spieler/Magier-UID?
+  else if (find_userinfo(alias)) {
+    // wenn rec > 0, wird eine Spieler-UID als Alias aufgefasst und zu seinen
+    // UIDs expandiert. Hierbei erfolgt aber nur eine Rekursion.
+    if (!rec) uids = QueryUIDsForWizard(alias, 1);
+    else uids = ({alias});
+  }
+  // Projektkrams? -> alle Subdirs von /p/ ausser /p/service selber.
+  else if (alias==PROJECTDIR) {
+    foreach(string dir: (get_dir("/"PROJECTDIR"/*") || ({}))
+                         - ({".","..",".svn","service"})) {
+      if (dir[0]!='.' &&
+          file_size("/"PROJECTDIR"/"+dir) == FSIZE_DIR)
+          uids += ({PROJECTDIR"."+dir});
+    }
+  }
+  // p.service? -> Alle Subdirs von /p/service.
+  else if (alias==PROJECTDIR".service") {
+    foreach(string dir: (get_dir("/"PROJECTDIR"/service/*") || ({}))
+                         - ({".","..",".svn"})) {
+      if (dir[0]!='.' &&
+          file_size("/"PROJECTDIR"/service/"+dir) == FSIZE_DIR)
+          uids += ({PROJECTDIR".service."+dir});
+    }
+  }
+  // wenn nix zutrifft -> unexpandiert zurueckgeben
+  else
+    uids = ({alias});
+
+  // im Cache vermerken
+  if (sizeof(uidaliase) >= __MAX_MAPPING_KEYS__)
+      uidaliase=m_allocate(1);
+  // auch vermerken, wenn keine UIDs ermittelt wurden.
+  uidaliase += ([alias: uids]);
+  return uids;
+}
+
diff --git a/secure/materialdb.c b/secure/materialdb.c
new file mode 100644
index 0000000..7f71bc4
--- /dev/null
+++ b/secure/materialdb.c
@@ -0,0 +1,936 @@
+// MorgenGrauen MUDlib
+//
+// - Prototypen und Properties in thing/material.h
+// - Liste in materials.h
+//
+// TODO: properties.h um materials.h erweitern
+//
+// - implizite Gruppenzuordnung entfernen, da jetzt explizit in
+//   Definitionsdateien vorhanden
+// - Materialdoku ueberarbeiten, dabei Hinweis auf nicht mehr implizite
+//   Gruppenzuordnung
+// /p/daemon/materialdb.c -- Materialdatenbank
+//
+// $Id: materialdb.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+#pragma strong_types
+#pragma no_clone
+#pragma no_inherit
+#pragma no_shadow
+#pragma pedantic
+
+// Die Propertydefinition reinholen
+#include <language.h>
+#include <thing/description.h>
+// Materialliste nicht mit reinziehen
+#define _SKIP_MATERIALS_
+#include <thing/material.h>
+#include <player/description.h>
+#include <rtlimits.h>
+
+// Basisverzeichnis der Materialien
+#define MAT_DIR "/doc/materials"
+
+// Ausgabeverzeichnis fuer Doku
+#define DOC_DIR(x) ("/doc/materials/"+x)
+
+// Dateiname des Headers mit Materialdefinitionen
+#define HEADERFILE "/sys/materials.h"
+
+// Savefile.
+#define SAVEFILE DOC_DIR("materialdb")
+
+// Rein intern verwendete Namen
+#define P_RECOC "recognizability"
+#define P_ID    "id"
+#define P_DEFSTR "defstr"
+#define P_MEMBERS "members"
+#define P_MG_FRACTIONS "mg_fractions"
+
+#define LOG_ERROR(x) if(find_player("raschaua")&&find_player("raschaua")->QueryProp("mdb-debug"))tell_object(find_player("raschaua"),"MDB-Error:"+x)
+#define LOG_WARN(x) if(find_player("raschaua")&&find_player("raschaua")->QueryProp("mdb-debug"))tell_object(find_player("raschaua"),"MDB-Warn:"+x)
+
+// Prototypes:
+// (Liefert den Anteil der Materialgruppe grp an mats)
+int MaterialGroup(mapping mats, string grp);
+// Konvertiert eine Liste von Materialien zu ihren Namen, dabei wird die
+// Erkennungsfaehigkeit beruecksichtigt und evtl. falsch erkannt
+varargs string ConvMaterialList(mixed mats, int casus, mixed idinf);
+varargs string MaterialName(string mat, int casus, mixed idinf);
+// Gibt den Namen einer Materialgruppe zurueck
+string GroupName(string grp);
+// Gibt alle Materialien zurueck
+string *AllMaterials();
+// Gibt alle Gruppen zurueck
+string *AllGroups();
+// Gibt alle Gruppen zurueck, in denen mat enthalten ist
+string *GetMatMembership(string mat);
+// Gibt alle Materialien zurueck, die in grp enthalten sind
+string *GetGroupMembers(string grp);
+// Erneuert die Materialien durch Scannen des Materialverzeichnisses
+void Update();
+// generiert Headerfile aus den Daten
+varargs void GenHeaderFile(string fn);
+
+mapping materials;
+mapping material_groups;
+private status initialized;
+mapping old_mat_keys;     // Alter Materialkey -> neuer Key (Kompatibilitaet)
+nosave mapping new_materials;       // Materialien waehrend des Scannens
+nosave mapping new_material_groups; // Materialgruppen waehrend des Scannens
+private nosave status isScanning;
+private nosave int updateTicks;
+
+void create() {
+  seteuid(getuid());
+  // Savefile einlesen, falls moeglich, damit die DB direkt initialisert ist,
+  // wenn auch ggf. mit alten Daten.
+  restore_object(SAVEFILE);
+  if (initialized) {
+    // falls erfolgreich, direkt Header fuer die Mudlib schreiben
+    GenHeaderFile();
+  }
+  // jetzt Update der Daten durchfuehren.
+  Update();
+}
+
+//==================== Umwandeln der Strings aus der thing/material.h
+private string getMatId(string key) {
+  // Alte Bezeichner umwandeln
+  if (!member(materials, key))
+    key = old_mat_keys[key];
+  return key;
+}
+private string getMatGroupId(string key) {
+  // Alte Bezeichner umwandeln
+  if (!member(material_groups, key))
+    key = "MATGROUP_"+upperstring(key[3..]);
+  return key;
+}
+private string matKey2Defstr(string key, mapping mats) {
+  string id;
+  if (member(mats[key], P_DEFSTR))
+    id = mats[key][P_DEFSTR];
+  else
+    id = key;
+  return id;
+}
+private string groupKey2Defstr(string key) {
+  if (sizeof(key) > 9)
+    key = "mg_"+lowerstring(key[9..]);
+  else
+    key = "";
+  return key;
+}
+
+//==================== Schnittstellenfunktionen zur Verwendung der DB
+varargs string MaterialName(string mat, int casus, mixed idinf) {
+  if (initialized) {
+    string *names;
+    mapping props;
+    mixed *dif;
+    // Anpassen der Materialid
+    mat = getMatId(mat);
+
+    if (!mappingp(props=materials[mat]))
+      props=([]);
+
+    // Je nach Koennen des Spielers kann man das exakte Material
+    // mehr oder weniger gut erkennen:
+    if (pointerp(dif=props[P_RECOC])
+&& (!intp(idinf)||idinf<100) ) { // 100=exakte Erkennung
+      int i, n, recval;
+      mixed *grps, tmp, x;
+
+      recval=0;
+      grps=props[P_MG_FRACTIONS];
+      if (!pointerp(idinf))
+        idinf=({idinf});
+
+      // Zunaechst die Faehigkeit des Spielers (da koennen noch
+      // Gildenfaehigkeiten hinzu kommen) ermitteln, dieses
+      // Material zu erkennen:
+      i=sizeof(idinf);
+      while(i--) {
+        tmp=idinf[i];
+        if (objectp(tmp)) // Diese Property ist hauptsaechlich fuer Rassen:
+          tmp=tmp->QueryProp(P_MATERIAL_KNOWLEDGE);
+        if (intp(tmp)) {
+          recval+=tmp; // Allgemeine Erkennungsfaehigkeit
+          break;
+        }
+        if (closurep(tmp) && intp(x=funcall(tmp,mat,grps))) {
+          recval+=x;
+          break; // Closures koennen immer nuetzlich sein :)
+        }
+        if (mappingp(tmp)) {
+          int j;
+          if ((x=tmp[mat]) && intp(x)){
+            // Erkennung von speziell diesem Material
+            recval+=x;
+            break;
+          }
+          // Erkennung von Gruppen
+          j=sizeof(grps);
+          while(j--)
+            if((x=tmp[grps[j]]) && intp(x))
+              recval+=x;
+          if (pointerp(tmp=tmp[MATERIAL_SYMMETRIC_RECOGNIZABILITY])) {
+            for (j=sizeof(tmp)-2;j>=0;j-=2) {
+              if (!intp(x=tmp[j+1]))
+                raise_error("materialdb: illegal sym.recoc. format\n");
+              if (props[tmp[j]])
+                recval+=x;
+              else // bei passenden Gruppen +, bei anderen -
+                recval-=x;
+            }
+          }
+        }
+      }
+
+      // Jetzt wird ermittelt, ob vielleicht eine ungenauere
+      // Beschreibung gegeben werden soll:
+      x=dif[0];
+      n = sizeof(dif)-1;
+      for (i=2;i<=n;i+=2) {
+        if (recval>=dif[i-1])
+          x=dif[i];
+      }
+      // Wenn die Faehigkeiten des Spielers nicht fuer den echten Klarnamen
+      // ausreichen, gib die Alternative zurueck:
+      if (x!=mat)
+        return MaterialName(x, casus, 100);
+    }
+
+    if (!pointerp(names=props[P_NAME]) || sizeof(names)<4)
+      names=({"unbekanntes Material", "unbekannten Materials",
+              "unbekanntem Material", "unbekannten Material"});
+    if (casus<0 || casus>3)
+      casus=0;
+    return names[casus];
+  }
+}
+
+varargs string ConvMaterialList(mixed mats, int casus, mixed idinf) {
+  if (initialized) {
+    string *ms,ml;
+    int i;
+
+    ml="";
+    if (mappingp(mats))
+      ms=m_indices(mats);
+    else if (stringp(mats))
+      ms=({mats});
+    else if (pointerp(mats))
+      ms=mats;
+    else
+      ms=({});
+    i=sizeof(ms);
+    while(i) {
+      ml+=MaterialName(ms[--i],casus,idinf);
+      if (i)
+        ml+=((i>1)?", ":" und ");
+    }
+    return ml;
+  }
+}
+
+int MaterialGroup(mapping mats, string grp) {
+   if (initialized) {
+     string *ms;
+     int i,res;
+
+     res=0;
+     if (!mappingp(mats) || !stringp(grp))
+       return res;
+     ms=m_indices(mats);
+     i=sizeof(ms);
+     while(i--) {
+       string mat;
+       mapping props;
+       mat=ms[i];
+       if (mappingp(props=materials[getMatId(mat)]))
+         res+=(mats[mat]*props[P_MG_FRACTIONS][getMatGroupId(grp)])/100;
+     }
+     if (res<-100) // Vielleicht noch Antimaterie zulassen
+       res=-100;   // (noch nicht sicher ob das so bleiben wird oder 0 sein wird)
+     if (res>100)
+       res=100;
+     return res;
+   }
+}
+
+string *AllMaterials() {
+  if (initialized) {
+    // Aus Kompatibilitaetsgruenden die alten Schluessel (#define-String)
+    // zurueckgeben
+    return m_indices(old_mat_keys);
+  }
+  return 0;
+}
+
+string *AllGroups() {
+  if (initialized) {
+    // Aus Kompatibilitaetsgruenden die alten Schluessel (#define-String)
+    // zurueckgeben
+    return map(m_indices(material_groups), #'groupKey2Defstr);
+  }
+  return 0;
+}
+
+string *GetMatMembership(string mat) {
+  if (initialized) {
+    mapping props;
+    // Anpassen der Materialid
+    mat = getMatId(mat);
+
+    if (!mappingp(props=materials[mat]))
+      return ({});
+    return map(m_indices(props[P_MG_FRACTIONS]), #'groupKey2Defstr);
+  }
+  return 0;
+}
+
+string *GetGroupMembers(string grp) {
+  if (initialized) {
+    string *mats;
+    // Anpassen der Materialid
+    grp = getMatGroupId(grp);
+    if (!member(material_groups, grp) ||
+	!pointerp(mats=material_groups[grp][P_MEMBERS]))
+      return ({});
+    return map(mats, #'matKey2Defstr, materials);
+  }
+  return 0;
+}
+
+string GroupName(string grp) {
+  if (initialized) {
+    if (member(material_groups, getMatGroupId(grp)))
+      return material_groups[getMatGroupId(grp)][P_NAME];
+    else
+      return "Unbekanntes";
+  }
+}
+
+string GroupDescription(string grp) {
+  if (initialized) {
+    if (member(material_groups, getMatGroupId(grp)))
+      return material_groups[getMatGroupId(grp)][P_DESCRIPTION];
+    else
+      return "Gruppe unbekannt";
+  }
+}
+
+//==================== Generieren von Headerfile und Manpages
+private string *get_ordered_groups()
+{
+  return ({"MATGROUP_WOOD", "MATGROUP_JEWEL", "MATGROUP_STONE", "MATGROUP_MAGNETIC",
+           "MATGROUP_METAL", "MATGROUP_DRUG", "MATGROUP_HERBAL", "MATGROUP_FLEXIBLE",
+           "MATGROUP_BIO", "MATGROUP_ACIDIC", "MATGROUP_BASIC", "MATGROUP_POISONOUS",
+           "MATGROUP_EXPLOSIVE", "MATGROUP_INFLAMMABLE",
+           "MATGROUP_ELEMENTAL", "MATGROUP_ELECTRICAL", "MATGROUP_MAGIC",
+           "MATGROUP_HOLY", "MATGROUP_UNHOLY", "MATGROUP_INVIS",
+           "MATGROUP_SOLID", "MATGROUP_FLUID", "MATGROUP_GAS"});
+}
+private string gen_material_h_head()
+{
+  return
+    "// MorgenGrauen MUDlib\n//\n"
+    "// materials.h -- material definitions\n//\n"
+    "// This file is generated by /secure/materialdb.c\n//\n"
+    "// DO NOT EDIT!\n//\n"
+    "// $Id: materialdb.c 8755 2014-04-26 13:13:40Z Zesstra $\n\n"
+    "#ifndef __MATERIALS_H__\n"
+    "#define __MATERIALS_H__\n\n";
+}
+private string gen_material_h_material(string mat, string last_grp)
+{
+  mat = old_mat_keys[mat];
+  return sprintf("#define %-24s\"%-20s // %s\n", mat,
+                (member(materials[mat], P_DEFSTR)?materials[mat][P_DEFSTR]:mat)+"\"",
+                materials[mat][P_DESCRIPTION]||materials[mat][P_NAME][WER]);
+}
+private string gen_material_h_materials_grp(string grp, string *left)
+{
+  string txt, *mats;
+  txt = sprintf("\n// Gruppe: %s\n", GroupName(grp));
+  mats = GetGroupMembers(grp) - (GetGroupMembers(grp) - left);
+  txt += sprintf("%@s", map(sort_array(mats, #'>), #'gen_material_h_material));
+  left -= GetGroupMembers(grp);
+  return txt;
+}
+private string gen_material_h_materials()
+{
+  string txt, last_grp;
+  string *grps, *mats;
+  txt = "// ****************************** Materialien ******************************\n";
+  // Gruppenweise ordnen
+  grps = get_ordered_groups();
+  mats = AllMaterials();
+  txt += sprintf("%@s", map(grps, #'gen_material_h_materials_grp,
+                                  &mats));
+  // Übriggebliene Materialien ausgeben
+  txt += "// sonstige Materialien:\n";
+  txt += sprintf("%@s", map(mats, #'gen_material_h_material));
+  return txt;
+}
+private string gen_material_h_group(string grp)
+{
+  return sprintf("#define %-27s\"%-18s // %s\n",
+                 grp, groupKey2Defstr(grp)+"\"", GroupName(grp));
+}
+private string gen_material_h_groups()
+{
+  string txt;
+  txt = "\n// **************************** Materialgruppen ****************************\n\n"
+    "#ifndef _IS_MATERIALDB_\n";
+  txt += sprintf("%@s\n", map(sort_array(m_indices(material_groups), #'>),
+                                    #'gen_material_h_group));
+  txt += "\n#endif // _IS_MATERIALDB_\n";
+  return txt;
+}
+private string gen_material_h_foot()
+{
+  return
+    "#endif // __THING_MATERIAL_H__\n";
+}
+private int dump_material_h(string fn)
+{
+  return (write_file(fn, gen_material_h_head()) &&
+          write_file(fn, gen_material_h_materials()) &&
+          write_file(fn, gen_material_h_groups()) &&
+          write_file(fn, gen_material_h_foot()));
+}
+private string gen_material_list_material(string mat)
+{
+  mat = old_mat_keys[mat];
+  return sprintf("  %-28s%=-45s\n", mat,
+                 materials[mat][P_DESCRIPTION]||materials[mat][P_NAME][WER]);
+}
+private string gen_material_list_materials_grp(string grp, string *left)
+{
+  string txt, *mats;
+  txt = sprintf("%s:\n", capitalize(GroupName(grp)));
+  mats = sort_array(GetGroupMembers(grp) - (GetGroupMembers(grp) - left), #'>);
+  txt += sprintf("%@s\n", map(mats, #'gen_material_list_material));
+  left -= GetGroupMembers(grp);
+  return txt;
+}
+private void dump_material(string fn)
+{
+  string txt;
+  string *grps, *mats;
+  // Gruppenweise ordnen
+  grps = get_ordered_groups();
+  mats = AllMaterials();
+  txt = sprintf("%@s", map(grps, #'gen_material_list_materials_grp,
+                                 &mats));
+  // Übriggebliene Materialien ausgeben
+  txt += "sonstige Materialien:\n";
+  txt += sprintf("%@s", map(mats, #'gen_material_list_material));
+  write_file(fn, txt) ||
+    raise_error(sprintf("Konnte Liste nicht weiter in Datei %s schreiben,"
+                        " Abbruch\n", fn));
+}
+private void dump_group(string grp, string fn)
+{
+  // upperstring langsame simul_efun, warum?
+  write_file(fn, sprintf("  %-28s%=-48s\n", (grp),
+                         GroupName(grp))) ||
+    raise_error(sprintf("Konnte Liste nicht weiter in Datei %s schreiben,"
+                        " Abbruch\n", fn));
+}
+private string gen_doc_foot(string other)
+{
+  return sprintf("\nSIEHE AUCH:\n"
+                 "     Konzepte:    material, materialerkennung\n"
+                 "     Grundlegend: P_MATERIAL, /sys/materials.h, /sys/thing/material.h\n"
+                 "     Methoden:    QueryMaterial(), QueryMaterialGroup(), MaterialList(),\n"
+                 "     Listen:      AllMaterials(), AllGroups()\n"
+                 "                  %s\n"
+                 "     Master:      ConvMaterialList(), MaterialGroup(),\n"
+                 "                  GroupName(), MaterialName(),\n"
+                 "                  GetGroupMembers(), GetMatMembership()\n"
+                 "     Sonstiges:   P_MATERIAL_KNOWLEDGE\n\n"
+                 "%s generiert aus /secure/materialdb\n", other, dtime(time()));
+}
+
+/* GenMatList
+ *
+ * Generiert Datei mit registrierten Materialien fuer die Dokumentation,
+ */
+varargs void GenMatList(string fn)
+{
+  if (initialized) {
+    string txt;
+    if (!stringp(fn) || !sizeof(fn))
+      fn = DOC_DIR("materialliste");
+    if (file_size(fn) >= 0) {
+      printf("Datei %s existiert bereits, loesche sie\n", fn);
+      rm(fn);
+    }
+    if (write_file(fn, "Material Liste\n==============\n\n")) {
+      dump_material(fn);
+      write_file(fn, gen_doc_foot("materialgruppen"));
+      printf("Materialliste erfolgreich in Datei %s geschrieben\n", fn);
+    } else
+      printf("Konnte Liste nicht in Datei %s schreiben, Abbruch\n", fn);
+  }
+}
+
+/* GenMatGroupList
+ *
+ * Generiert Datei mit registrierten Materialgruppen fuer die Dokumentation,
+ */
+varargs void GenMatGroupList(string fn)
+{
+  if (initialized) {
+    string txt;
+    if (!stringp(fn) || !sizeof(fn))
+      fn = DOC_DIR("materialgruppen");
+    if (file_size(fn) >= 0) {
+      printf("Datei %s existiert bereits, loesche sie\n", fn);
+      rm(fn);
+    }
+    if (write_file(fn, "Materialgruppen\n===============\n")) {
+      map(sort_array(m_indices(material_groups), #'>), #'dump_group, fn);
+      write_file(fn, gen_doc_foot("materialliste"));
+      printf("Materialliste erfolgreich in Datei %s geschrieben\n", fn);
+    } else
+      printf("Konnte Liste nicht in Datei %s schreiben, Abbruch\n", fn);
+  }
+}
+
+/* GenHeaderFile
+ *
+ * Generiert Headerfile mit Definitionen der moeglichen Materialien und
+ * Gruppen
+ */
+varargs void GenHeaderFile(string fn)
+{
+  if (initialized) {
+    string txt;
+    if (!stringp(fn) || !sizeof(fn))
+      fn = HEADERFILE;
+    if (file_size(fn) >= 0) {
+      printf("Datei %s existiert bereits, loesche sie\n", fn);
+      rm(fn);
+    }
+    if (dump_material_h(fn))
+      printf("Headerdatei erfolgreich in %s geschrieben\n", fn);
+    else
+      printf("Konnte Headerdatei nicht in Datei %s schreiben, Abbruch\n", fn);
+  }
+}
+
+//==================== Pruef- und Hilfsfunktionen fuer Materialien
+private void updateGroupMembers(mapping groups, string mat_id, mapping mat) {
+  mixed *addgrps; // Array zum Ableiten von Gruppenzugehoerigkeiten
+  string *h;
+  int i, val;
+  mapping fractions; // Mapping mit Anteilen an Gruppen
+  fractions = mat[P_MG_FRACTIONS];
+  if (!mappingp(fractions))
+    fractions = ([]);
+  addgrps=({ // Reihenfolge wird rueckwaerts abgearbeitet
+    // Ableitungen sind z.T. abenteuerlich gewesen, mal ordentlich
+    // ausmisten. Die Zugehoerigkeit gehoert explizit in die
+    // Materialdefinition
+    // Gase sieht man normalerweise nicht:
+    ({"MATGROUP_INVIS", "MATGROUP_GAS"}),
+    // Mineralien sind auch Steine
+    ({"MATGROUP_STONE","MATGROUP_MINERAL"}),
+    // Edelmetalle sind Metalle:
+    ({"MATGROUP_METAL","MATGROUP_PRECIOUS_METAL"}),
+    // Lebewesen und deren Ueberreste, Paiere und Stoffe sind biologisch
+    ({"MATGROUP_BIO","MATGROUP_LIVING","MATGROUP_DEAD",
+      "MATGROUP_PAPER"}),
+    // Holz ist pflanzlich:
+    ({"MATGROUP_HERBAL", "MATGROUP_WOOD"}),
+    // Holz ist meistens tot:
+    ({"MATGROUP_DEAD","MATGROUP_WOOD"}),
+    // Holz, Papier und Stoffe brennen:
+    ({"MATGROUP_INFLAMMABLE","MATGROUP_WOOD","MATGROUP_PAPER"}),
+    // Laubhoelzer, Nadelhoelzer und Tropenhoelzer sind Holz
+    ({"MATGROUP_WOOD","MATGROUP_TROPICAL_WOOD","MATGROUP_DECIDUOUS_WOOD",
+      "MATGROUP_CONIFER_WOOD"}),
+    // Explosive Dinge sind immer entzuendlich:
+    ({"MATGROUP_INFLAMMABLE","MATGROUP_EXPLOSIVE"})
+  });
+  i=sizeof(addgrps);
+  while(i--) {
+    int j;
+    h=addgrps[i];
+    if (member(fractions,h[0])) // Existiert schon eigener Eintrag?
+      continue; // Automatische Eintragung unnoetig
+    val=0;
+    for (j=sizeof(h)-1;j>=1;j--)
+      val+=fractions[h[j]];
+    if (!val)
+      continue;
+    if (val>100)
+      val=100;
+    else if (val<-100)
+      val=-100;
+    fractions[h[0]]=val;
+  }
+  if (fractions["MATGROUP_LIVING"]) // Im Falle von lebendem Holz, tot loeschen
+    m_delete(fractions,"MATGROUP_DEAD");
+  // Alles, was nicht als gasfoerming, fluessig oder fest eingeordnet ist, ist
+  // sonstwas:
+  if (!member(fractions, "MATGROUP_FLUID")
+      && !member(fractions, "MATGROUP_GAS")
+      && !member(fractions, "MATGROUP_SOLID"))
+    fractions["MATGROUP_MISC"]=100;
+  // Materialien als Mitglieder in die Gruppen eintragen
+  addgrps=m_indices(fractions);
+  i=sizeof(addgrps);
+  while(i--) {
+    mixed ind;
+    ind=addgrps[i];
+    if (!fractions[ind] || !member(groups, ind)) {
+      // Unbekannte Gruppe und Gruppe ohne Anteil aus Mapping loeschen
+      m_delete(fractions,ind);
+      continue;
+    }
+    if (!pointerp(h=groups[ind][P_MEMBERS]))
+      h=({});
+    h+=({mat_id});
+    groups[ind][P_MEMBERS]=h;
+  }
+  mat[P_MG_FRACTIONS] = fractions;
+}
+
+//==================== Einlesen der Mappings aus Dateien
+
+#define MDESC_ERROR(x, y) LOG_ERROR(sprintf("Materialbeschreibung '%s': %s\n", x, y))
+#define MDESC_WARN(x, y) //LOG_WARN(sprintf("Materialbeschreibung '%s': %s\n", x, y))
+
+private mapping getDescParts(string s) {
+  string* lines;
+  string key, val;
+  int i, n;
+  mapping m;
+  m = ([]);
+  val = "";
+  lines = explode(s, "\n");
+  n = sizeof(lines);
+  if (n > 0) {
+    while (i < n) {
+      if (sscanf(lines[i], "%s:", key)) {
+        status multiline;
+        multiline = 0;
+        // Schluessel gefunden, Wert auslesen
+        while ( (++i < n) && sizeof(lines[i])) {
+          // Mehrzeilige Werte mit newline verketten
+          if (multiline) {
+            val += "\n";
+          }
+          val += lines[i];
+          multiline = 1;
+        }
+        m += ([key:val]);
+        val = "";
+      }
+      i++;
+    }
+  }
+  return m;
+}
+private varargs int isFile(string fn, string path) {
+  if (stringp(path) && sizeof(path))
+    fn = path+"/"+fn;
+  return (file_size(fn) >= 0);
+}
+
+private varargs mixed readGroupDesc(string id) {
+  mixed m;
+  string fn;
+  fn = MAT_DIR+"/groups/"+id;
+  if (file_size(fn) > 0) {
+    mapping parts;
+    string desc;
+    parts = getDescParts(read_file(fn));
+    m = ([P_NAME:parts["Name"],
+          P_MEMBERS:({})]);
+    if (member(parts,"Beschreibung"))
+      m += ([P_DESCRIPTION:parts["Beschreibung"]]);
+    if (parts["Gruppenid"] != id)
+      LOG_WARN(sprintf("Unstimmigkeit Gruppenid bei '%s'\n", id));
+  } else {
+    LOG_ERROR(sprintf("Kann Gruppenbeschreibung %s nicht laden\n", fn));
+  }
+  return m;
+}
+
+private mapping convMatId(string s) {
+  mapping m;
+  string* parts;
+  parts = explode(s, "\"");
+  if (sizeof(parts)) {
+    int ende;
+    ende = strstr(parts[0]," ")-1;
+    if (ende < 0)
+      ende = sizeof(parts[0]);
+    m = ([P_ID:parts[0][0..ende]]);
+    if (sizeof(parts) > 1)
+      m += ([P_DEFSTR:parts[1]]);
+  }
+  return m;
+}
+private string* convMatNames(string s) {
+  string* names;
+  names = filter(explode(s, "\""),
+                       lambda( ({'x}), ({#'>, ({#'sizeof, 'x}), 1}) ));
+  if (sizeof(names)<1)
+    names=0;
+  else {
+    if (sizeof(names)<2)
+      names+=({names[0]+"s"});
+    if (sizeof(names)<3)
+      names+=({names[0]});
+    if (sizeof(names)<4)
+      names+=({names[0]});
+  }
+  return names;
+}
+private int convMatGender(string s) {
+  int gender;
+  s = lowerstring(s);
+  // Ein Buchstabe reicht zur Bestimmung. Wenn nur weiblich|female
+  // bzw. maennlich|male verwendet wird. Dabei ist dann allerdings die
+  // Reihenfolge der Auswertung wichtig, damit das m bei MALE nicht mehr bei
+  // female passt.
+  if (sizeof(regexp( ({s}), "f|w"))) {
+    gender = FEMALE;
+  } else if (sizeof(regexp( ({s}), "m"))) {
+    gender = MALE;
+  } else {
+    gender = NEUTER;
+  }
+  return gender;
+}
+private string convMatDesc(string s) {
+  if (sizeof(regexp( ({s}), "- nicht vorhanden -"))) {
+    s = 0;
+  } else {
+    // Mehrzeilige Beschreibungen zu einer Zeile zusammenfassen
+    s = implode(explode(s, "\n"), " ");
+  }
+  return s;
+}
+private void addRecocLine(string s, mixed* r) {
+  // Die weitere Bewertung der Schwierigkeit kann erst vorgenommen werden,
+  // wenn alle Materialien bekannt sind und passiert spaeter. Zuerst werden
+  // nur die Elemente des Arrays konvertiert und eingetragen
+  string mat;
+  int val;
+  if (sscanf(s, "%s:%d", mat, val)) {
+    r += ({mat,val});
+  } else if (sscanf(s, "%d", val)) {
+    r += ({val});
+  } else {
+    r += ({s});
+  }
+}
+private mixed convMatRec(string s) {
+  mixed difficulties;
+  if (sizeof(regexp( ({s}), "- keine Einschraenkung -"))) {
+    difficulties = 0;
+  } else {
+    difficulties = ({});
+    // Jede Zeile enthaelt eine Bedingung
+    map(explode(s, "\n"), #'addRecocLine, &difficulties);
+  }
+  return difficulties;
+}
+private void addGroupLine(string s, mapping g) {
+  // Die weitere Bewertung der Zugehoerigkeit passiert spaeter.
+  string grp;
+  int val;
+  if (sscanf(s, "%s:%d", grp, val)) {
+    g += ([grp:val]);
+  } else {
+    g += ([grp:100]);
+  }
+}
+private mapping convMatGroups(string s) {
+  mapping groups;
+  if (!sizeof(regexp( ({s}), "- keine -"))) {
+    groups = ([]);
+    // Jede Zeile enthaelt eine Bedingung
+    map(explode(s, "\n"), #'addGroupLine, groups);
+  }
+  return groups;
+}
+private mapping convMaterialDesc(string id, mapping desc) {
+  /* Struktur Materialmapping:
+     P_GENDER,
+     P_NAME:({name_nom, name_gen, name_dativ, name_akkusativ}),
+     (P_RECOC:({mat1,faehigkeit1,mat2,faehigkeit2,...}),)
+     (P_DEFSTR: bei bedarf),
+     P_DESCRIPTION,
+     (grupp1:anteil1,
+     gruppe2:anteil2,
+     ...)
+  */
+  mapping m;
+  mixed val, val2;
+  m = ([]);
+  // Der string fuer das #define zuerst:
+  val = convMatId(desc["Materialid"]);
+  if (mappingp(val)) {
+    if (val[P_ID] != id)
+      LOG_WARN(sprintf("Unstimmigkeit Materialid bei '%s':%O\n", id, val[P_ID]));
+    if (member(val, P_DEFSTR)) {
+      m += ([P_DEFSTR:val[P_DEFSTR]]);
+    } else {
+      // Wenn kein String fuers #define angegeben wurde, dann direkt ID verwenden
+      //m += ([P_DEFSTR:lowerstring(id)[4..]]);
+    }
+  }
+  // Die Namen
+  if (val = convMatNames(desc["Name"])) {
+    m += ([P_NAME:val]);
+  } else {
+    MDESC_WARN(id, "keine Namen");
+    m += ([P_NAME:({"", "", "", ""})]);
+  }
+  // Das Geschlecht, standard ist NEUTER
+  m += ([P_GENDER:convMatGender(desc["Geschlecht"]) ]);
+  // Die Beschreibung
+  val = convMatDesc(desc["Beschreibung"]);
+  if (sizeof(val)) {
+    m += ([P_DESCRIPTION:val]);
+  } else {
+    MDESC_WARN(id, "keine Beschreibung");
+  }
+  // Die Erkennbarkeit
+  val = convMatRec(desc["Erkennbarkeit"]);
+  if (sizeof(val)) {
+    m += ([P_RECOC:val]);
+  }
+  // und zum Schluss die Gruppenzugehoerigkeit
+  val = convMatGroups(desc["Gruppenzugehoerigkeit"]);
+  if (mappingp(val) && sizeof(val)) {
+    m += ([P_MG_FRACTIONS:val]);
+  }
+  return m;
+}
+private varargs mixed readMaterialDesc(string id) {
+  mixed m;
+  string fn;
+  fn = MAT_DIR+"/materials/"+id;
+  if (file_size(fn) > 0) {
+    mapping parts;
+    string desc;
+    parts = getDescParts(read_file(fn));
+    m = convMaterialDesc(id, parts);
+  } else {
+    LOG_ERROR(sprintf("MDB:Kann Materialbeschreibung %s nicht laden\n", fn));
+  }
+  return m;
+}
+
+public int GetUpdateTicks() {
+  return updateTicks;
+}
+
+private void scanFinished() {
+  isScanning = 0;
+  initialized = 1;
+  // Mappings umkopieren
+  materials = new_materials;
+  material_groups = new_material_groups;
+  // Letzter Schritt: Mapping mit alten Schluesseln anlegen
+  old_mat_keys = mkmapping(map(m_indices(materials), #'matKey2Defstr, materials),
+                           m_indices(materials));
+  // Generieren der Doku und des Materialheaders
+  GenHeaderFile();
+  GenMatList();
+  GenMatGroupList();
+  // Savefile schreiben
+  save_object(SAVEFILE);
+}
+
+public int IsScanning() {
+  return isScanning;
+}
+
+private varargs void doScanMaterials(string* mats, int i, int step) {
+  int ticks, start;
+  string matid;
+  start = get_eval_cost();
+  if (step < 2) {
+    while ( (i < sizeof(mats)) &&
+            ((start - get_eval_cost()) < query_limits()[LIMIT_EVAL]/3 ) ) {
+      matid = mats[i];
+      switch (step) {
+      case 0:
+        // Erster Schritt: Einlesen der Dateien
+        new_materials[matid] = readMaterialDesc(matid);
+        break;
+      case 1:
+        // Zweiter Schritt: Bearbeiten der Erkennung und Gruppenzugehoerigkeit
+        updateGroupMembers(new_material_groups, matid, new_materials[matid]);
+        break;
+      default:
+        break;
+      }
+      i++;
+    }
+  }
+  if (i < sizeof(mats)) {
+    catch(raise_error(sprintf("MaterialDB: Initialisierung noch nicht beendet,"
+                              " fehlende Materialbeschreibungen moeglich"
+                              " (Phase %d:%d/%d)\n",
+                              step, i, sizeof(mats)));publish);
+    call_out(#'doScanMaterials, 2, mats, i, step);
+  } else {
+    // Zweite Stufe ausloesen oder beenden
+    if (step < 1) {
+      if ((start - get_eval_cost()) < query_limits()[LIMIT_EVAL]/2 )
+          doScanMaterials(mats, 0, step+1);
+      else
+          call_out(#'doScanMaterials, 2, mats, 0, step+1);
+    }
+    else
+      scanFinished();
+  }
+  updateTicks += start - get_eval_cost();
+}
+
+private mapping ScanGroups() {
+  mapping groups;
+  string* grpfiles;
+  groups = ([]);
+  grpfiles = filter(get_dir(MAT_DIR+"/groups/MATGROUP_*"),
+                          #'isFile, MAT_DIR+"/groups");
+  groups = mkmapping(grpfiles, map(grpfiles, #'readGroupDesc, 1));
+  return groups;
+}
+
+private void ScanMaterials() {
+  string *matfiles;
+  matfiles = filter(get_dir(MAT_DIR+"/materials/MAT_*"),
+                          #'isFile, MAT_DIR+"/materials");
+  doScanMaterials(matfiles);
+}
+
+void Update() {
+  int start;
+  updateTicks = 0;
+  start = get_eval_cost();
+  if (!isScanning) {
+    if (sizeof(get_dir(MAT_DIR))) {
+      isScanning = 1;
+      new_material_groups = ScanGroups();
+      new_materials = ([]);
+      updateTicks = start - get_eval_cost();
+      ScanMaterials();
+    } else {
+      LOG_ERROR("Kann Materialverzeichnis nicht finden, keine Materialien angelegt!\n");
+    }
+  }
+}
diff --git a/secure/memory.c b/secure/memory.c
new file mode 100644
index 0000000..797ad1c
--- /dev/null
+++ b/secure/memory.c
@@ -0,0 +1,299 @@
+/*!
+// MorgenGrauen Mudlib
+//
+// secure/memory.c  -- zum Speichern von Daten ueber die gesamte Mud-Uptime
+//
+// Memory speichert seine Daten als Pointer in der extra-wizinfo. Andere 
+// Programme koennen Ihre Daten im Memory ablegen. Die Daten bleiben dadurch 
+// auch ueber ein Update/Reload der Blueprint erhalten.
+//
+// Die Daten werden auf Klassenebene behandelt. Jeder Clone eines Programms
+// (Blueprint) darf auf die gespeicherten Daten des Programms schreibend und
+// lesend zugreifen. Andere Programme haben keinen Zugriff auf diese Daten.
+//
+// Nur Objekte in /secure/memory_lib bzw. /secure/memory_nolib eingetragen 
+// sind, haben Rechte den Memory zu nutzen.
+//
+// Die Idee, sensible Daten in der extra_wizinfo abzuspeichern entstammt 
+// /secure/memory.c aus der Wunderland Mudlib von Holger@Wunderland
+*/
+#include <config.h>
+#include <wizlevels.h>
+#include <files.h>
+
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+
+/* Global variable declaration */
+/*! \brief Liste mit Programmen, die den Memory nutzen duerfen 
+ Die Variable wird entweder im create() oder durch manuellen Aufruf 
+ von RereadProgramLists() aus /secure/memory_lib und /secure/memory_nolib
+ befuellt.
+ */
+string *known_programs;
+
+/* Function declaration - public interface */
+void create();
+
+// Payload Interface
+int   Save  (string key, mixed data);
+mixed Load  (string key);
+int   Remove(string key);
+int   HaveRights();
+
+// Administrative Interface
+varargs int RereadProgramLists(int silent);
+
+// Debugging Interface
+varargs void ShowData(string user, string key);
+
+/* Function definition */ 
+/*! Objekt initialisieren */
+void create() {
+  mapping info;
+  seteuid(getuid(this_object()));
+
+  // Wizinfo-Pointer holen
+  if(!pointerp(info=get_extra_wizinfo(0)))
+    raise_error("WIZINFO nicht lesbar. secure/simul_efun muss neu geladen werden.!\n");
+
+  // Mein Feld der Wizinfo bei Bedarf initialisieren
+  if(!mappingp(info[MEMORY_BUFF]))
+    info[MEMORY_BUFF]=([]);
+
+  RereadProgramLists(1);
+}
+
+/*!
+ Wenn das aufrufende Programm fuer die Speichernutzung zugelassen ist,
+ wird hier der Hash zurueckgegeben unter dem seine Daten abgespeichert
+ sind. Der Hash ist derzeit der Filename des Blueprint.
+*/
+private string get_caller_hash() {
+  if (member(known_programs, program_name(previous_object())[..<3]) != -1) 
+    return program_name(previous_object())[..<3];
+  else 
+    return 0;
+}
+
+/*! 
+ Die Hilfsfunktion liest eine Liste mit Dateinamen und gibt die Programme 
+ als Array of Strings zurueck. Alle Zeilen die nicht mit einem Slash 
+ anfangen, werden dabei ignoriert.
+ \param file Die auszulesende Datei.
+*/
+private mixed read_list(string file)
+{
+  mixed data;
+  if (!stringp(file) || file_size(file) == FSIZE_NOFILE )
+    return ({});
+
+  // Daten laden
+  data = explode(read_file(file)||"", "\n");
+
+  // Alle Zeilen die nicht mit / beginnen entfernen ...
+  data=regexp(data,"^/.+");
+
+  // ".c" und eventuelle Leerzeichen in jeder Zeile werden entfernt
+  data= map(data, 
+     function string (string x) {return regreplace(x,"(.c)* *$","",1);});
+  return data;
+}
+
+/*! 
+ Wenn die Liste mit Programmen geaendert wurde, kann sie hier per Hand
+ neu eingelesen werden.
+ \param silent Varargs Flag, um Bildschirmausgabe zu unterdruecken
+*/
+varargs int RereadProgramLists(int silent)
+{
+  // Objekte laden, die zur Mudlib gehoeren
+  known_programs = read_list("/secure/memory_lib");
+
+  // Objekte laden, die nicht zur Mudlib gehoeren
+  known_programs+=read_list("/secure/memory_nolib");
+
+  if(!silent) printf ("%O\n", known_programs);
+
+  return sizeof(known_programs);
+}
+
+/*! Zeiger auf den Speicher holen */
+private mapping get_memory_pointer(){
+  mixed info;
+
+  // Die Fehlermeldungen sind etwas ausfuehrlicher, um dem Debugger
+  // einen Hinweis auf Reparaturmoeglichkeiten zu geben
+  if(!pointerp(info=get_extra_wizinfo(0)))
+    raise_error("Wizinfo nicht ladbar. Entweder ich hab keine Rechte "
+                "oder das Mud hat ein echtes Problem!\n");
+
+  if(!mappingp(info[MEMORY_BUFF]))
+    raise_error("Ich finde meine Daten in der Wizinfo nicht. Ein EM kann "
+                "versuchen, mich neu zu laden, aber was die alten Daten "
+                "betrifft, frei nach Catty: Keine Arme, keine Kekse!\n");
+  // Da info[MEMORY_BUFF] immer ein Mapping ist (siehe Create), wird hier ein 
+  // Pointer uebergeben. Dadurch werden Details zum Aufbau der extra_wizinfo
+  // nur in dieser Funktion benoetigt.
+  return info[MEMORY_BUFF]; 
+}
+
+/*!
+  Pruefen, ob previous_object() Nutzungsrechte fuer den Memory hat.
+  \return Gibt 1 zurueck, wenn der Aufrufende berechtigt ist, das 
+          Objekt zu nutzen, sonst 0
+*/
+int HaveRights(){
+  return stringp(get_caller_hash());
+}
+
+/*!
+ Daten im Memory ablegen
+ \param key ID unter der die Daten gespeichert werden
+ \param var Zu speicherndes Datum
+*/
+int Save(string key, mixed var) {
+  string who;
+  mapping memory;
+
+  // Hash des Aufrufers holen. Hat der keinen, ist er nicht befugt,
+  // den Memory zu nutzen.
+  if(!stringp(who=get_caller_hash())) return 0;
+
+  // Wenn kein Key uebergeben wird, kann auch nichts gespeichert werden.
+  if(!stringp(key)) return 0;
+
+  // (hoffentlich) globalen memory_pointer holen
+  memory = get_memory_pointer();
+
+  // Wenn keine Daten da sind, muss man auch nichts anlegen. Rueckgabewert
+  // ist dennoch 1, weil Load bei unbekannten Keys auch 0 liefert, die 
+  // Null wird also korrekt gespeichert, irgendwie. Das muss aber nach dem
+  // get_memory_pointer() passieren, da sonst das Korrekte Funktionieren von
+  // Load nicht sichergestellt ist.
+  if(var==0) return 1;
+
+  // Erster Aufruf durch PO? Dann muss sein Speicherbereich initialisiert 
+  // werden
+  if(!member(memory,who))
+    memory[who]=([]);
+
+  // Endlich! Daten koennen gespeichert werden.
+  memory[who][key]=var;
+
+  return 1;
+}
+
+/*!
+ Daten ausgeben/referenzieren
+ \param key Id unter der die Daten gespeichert sind.
+*/
+mixed Load(string key) {
+  string who;
+  mapping memory;
+
+  // Hash des Aufrufers holen. Hat der keinen, ist er nicht befugt,
+  // den Memory zu nutzen.
+  if(!stringp(who=get_caller_hash())) return 0;
+
+  // Wenn kein Key uebergeben wird, kann auch nichts gespeichert werden.
+  if(!stringp(key)) return 0;
+
+  // (hoffentlich) globalen memory_pointer holen
+  memory = get_memory_pointer();
+
+  // Das Objekt hat noch nie Save() aufgerufen.
+  if(!member(memory,who))
+    return 0;
+
+  // Unter diesem Key ist noch nichts gespeichert.
+  if(!member(memory[who],key))
+    return 0;
+
+  // Gespeicherten Wert zurueckgeben
+  return memory[who][key];
+}
+
+/*! 
+ Ein Eintrag oder alle Eintraege werden geloescht
+ \param key Id unter der die Daten gespeichert sind. 
+            Wird kein Key uebergeben, werden alle Daten des Objekts 
+            geloescht.
+*/
+varargs int Remove(string key) {
+  string who;
+  mapping memory;
+
+  // Hash des Aufrufers holen. Hat der keinen, ist er nicht befugt,
+  // den Memory zu nutzen.
+  if(!stringp(who=get_caller_hash())) return 0;
+
+  // (hoffentlich) globalen memory_pointer holen
+  memory = get_memory_pointer();
+
+  // Das Objekt hat noch nie Save() aufgerufen.
+  if(!member(memory,who)) return 0;
+
+  // Wenn kein Key uebergeben wird, wird alles zum Objekt gehoerende geloescht.
+  if(stringp(key)){
+    // Unter diesem Key ist nichts gespeichert.
+    if(!member(memory[who],key)) return 0;
+
+    // Wert unter Key fuer das File loeschen
+    m_delete(memory[who], key);
+
+  } else {
+    // Alles fuer das File loeschen
+    m_delete(memory, who);
+  }
+
+  return 1; 
+}
+
+varargs void ShowData(string user, string key)
+{
+  // Bekannte Objekte
+  if (!ELDER_SECURITY )
+    printf("I'm fine. Thanks for asking.\n");
+  else {
+    printf("known_programs:\n%O\n\n", known_programs);
+    // EM duerfen sich auch noch zuscrollen lassen, wenn sie wollen
+    if (ARCH_SECURITY && user)
+    {
+      mapping data = get_memory_pointer();
+      if (!member(data,user))
+        printf("memory: Keine Daten fuer %s vorhanden.\n",user);
+      else if (user && key)
+        printf("memory: Daten fuer User %s, Key %s:\n%O\n",user, key,
+                  data[user][key]);
+      else
+        printf("memory: Daten fuer User %s:\n%O\n",user,data[user]);
+    }
+    else
+    {
+      mapping memory;
+      memory = get_memory_pointer();
+      // Andere bekommen eine anonymisierte Version
+      printf("memory: (Nur Feldnamen, keine Daten)\n");
+      foreach (string program, mapping data: memory ) {
+        if ( mappingp(data) && sizeof(data) ){
+          printf(break_string(
+                   implode(m_indices(data),";\n"), 78,  
+                   sprintf("  %-50s: ",program)),1);
+        }
+        else
+        printf("  %-50s: - leer -\n", program);
+      }
+    }
+  }
+  return;
+}
+
diff --git a/secure/memory_lib b/secure/memory_lib
new file mode 100644
index 0000000..06539b1
--- /dev/null
+++ b/secure/memory_lib
@@ -0,0 +1,9 @@
+# Objekte die in dieser Datei stehen, haben die Berechtigung 
+# /secure/memory.c zu nutzen.
+#
+# Diese Datei wird mit dem Mudlib-Release verteilt, daher duerfen
+# hier nur Objekte aufgenommen werden die zur Standard-Mudlib
+# gehoeren
+/p/daemon/channeld
+/p/daemon/traveld
+/secure/shadowmaster
diff --git a/secure/merlin.c b/secure/merlin.c
new file mode 100644
index 0000000..3075bec
--- /dev/null
+++ b/secure/merlin.c
@@ -0,0 +1,1345 @@
+// MorgenGrauen MUDlib
+//
+// merlin.c -- Unser geliebter Merlin
+//
+// $Id: merlin.c 9405 2015-12-13 00:22:01Z Zesstra $
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+// 2014-Mai-10, Arathorn: Er soll ja weiterhin auf tm reagieren.
+inherit "std/npc/comm.c";
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+#include <defines.h>
+#include <exploration.h>
+#include <news.h>
+#include <exploration.h>
+#include <events.h>
+#include <daemon/channel.h>
+#include "/secure/wizlevels.h"
+#include "/secure/config.h"
+#include "/secure/questmaster.h"
+#include "/secure/lepmaster.h"
+
+#ifndef DEBUG
+#define DEBUG(x) if (find_player("zook")) tell_object(find_player("zook"),x)
+#endif
+
+#define SAVEFILE "/secure/ARCH/merlin"
+#define BS(x) break_string((x),78)
+#define FEHLERTEUFEL "/obj/tools/fehlerteufel"
+
+mixed QueryProp(string prop);
+int move(mixed dest, int method);
+string query_g_suffix(int gen, int casus, int anzahl);
+string make_invlist(object viewer,object *inv);
+static int determine_action(string mess, string name);
+static int create_wizard(mixed who, mixed promoter);
+static int create_seer(mixed who);
+static void give_help(mixed who);
+int goto(mixed dest);
+
+nosave string *whitespaces=({",",".","?",";",":","-","!","\n","\t"});
+nosave object prev_room;
+nosave int delay,maxusers,busy;
+nosave string flag;
+
+mapping MBanishListe;
+
+void create()
+{
+  string s;
+  seteuid(ROOTID);
+  if (file_size("/etc/maxusers")<=0)
+    maxusers=0;
+  else
+  {
+    s=read_file("/etc/maxusers",0,1);
+    sscanf(s,"%d",maxusers);
+  }
+  set_living_name("merlin");
+  enable_commands();
+  call_out("wandern",25);
+  move("/gilden/abenteurer",0);
+  MBanishListe = m_allocate(0,2);
+  restore_object(SAVEFILE);
+  EVENTD->RegisterEvent(EVT_LIB_PLAYER_CREATION, "player_change", ME);
+  EVENTD->RegisterEvent(EVT_LIB_LOGIN, "player_change", ME);
+  // absichtlich kein EVT_LIB_LOGOUT, s. Kommentar zu notify_player_leave().
+}
+
+string _query_kill_name()
+{
+  return "Ein genervter Merlin";
+}
+
+int move(mixed ob,int methods)
+{
+  object tmp;
+
+  if (methods&M_GET)
+    return ME_CANT_BE_TAKEN;
+
+  if (stringp(ob))
+  {
+    if (!(tmp=find_object(ob)))
+    {
+      call_other(ob,"?");
+      tmp=find_object(ob);
+    }
+    ob=tmp;
+  }
+  if (!objectp(ob))
+    return 0;
+  if (environment())
+    tell_room(environment(),"Merlin zieht weiter.\n");
+  tell_room(ob,"Merlin kommt an.\n");
+  move_object(ob);
+  return 1;
+}
+
+mixed QueryProp(string str)
+{
+  if (!stringp(str)||str=="")
+    return 0;
+  return call_other(this_object(),"_query_"+str);
+}
+
+varargs int id(string str)
+{
+  return (str=="merlin");
+}
+
+varargs string name(int casus, int demon)
+{
+  if (casus!=WESSEN)
+    return "Merlin";
+  return "Merlins";
+}
+
+varargs string Name(int casus, int daemon)
+{
+  return name(casus,daemon);
+}
+
+string QueryDu(int casus, int gender, int zahl)
+{
+  return
+    ({ ({ ({ "du", "ihr"}), ({ "du", "ihr"}), ({ "du", "ihr"}) }),
+         ({({"deines","deiner"}),({"deines","deiner"}),({"deiner","deiner"})}),
+         ({({"dir","euch"}),({"dir","euch"}),({"dir","euch"})}),
+         ({({"dich","euch"}),({"dich","euch"}),({"dich","euch"})})
+       })[casus][gender][zahl];
+}
+
+string long()
+{
+  EPMASTER->GiveExplorationPoint("merlin");
+  return break_string(
+      "Merlin - der maechtige Urvater aller Magier - ist ein hagerer "
+      "Mann, der einen blauen, mit Monden und Sternen uebersaeten Umhang "
+      "traegt und sich auf einen knorrigen Stock stuetzt. Sein langer weisser "
+      "Bart reicht ihm fast bis zur Guertelschnalle, sein Haupthaar ist fast "
+      "ebenso lang. Auf seinem Kopf traegt er einen spitzen Hut, der ihn noch "
+      "groesser macht, als er ohnehin schon ist.\nEr ist unangreifbar, denn "
+      "er steht ueber solchen Dingen.",78,BS_LEAVE_MY_LFS);
+}
+
+string GetDetail(string key,mixed race,int sense) {
+  switch( sense ) {
+  case SENSE_SMELL: return 0;
+  case SENSE_SOUND: return 0;
+  }
+  switch( key ) {
+  case "nase": case "nasenhaare": case "hakennase":
+          return BS("Merlin hat eine lange, krumme Hakennase, aus deren Nasenloechern weisse Haare spriessen. Auf seiner Nase sitzt eine Brille mit Glaesern in Form eines Halbmondes.");
+  case "brille": case "glaeser": case "form": case "halbmond":
+          return "Merlins Brille besitzt Glaeser in Form eines Halbmondes.\n";
+  case "bart":
+          return "Merlins Bart ist sehr lang - Er reicht fast bis ueber die Guertelschnalle.\n";
+  case "haare" :
+          return "Sie reichen ihm bis zwischen die Schulterblaetter.\n";
+  case "schulterblaetter": case "schulterblatt":
+          return "Seine langen Haare bedecken die Schulterblaetter.\n";
+  case "gewand": case "robe":
+          return BS("Das Gewand ist blau und mit silbernen Monden und Sternen bestickt. Es ist uralt und schon etwas abgetragen, aber es erfuellt immer noch seinen Zweck.");
+  case "zweck":
+          return BS("Der Zweck des Gewandes besteht darn, Merlin standesgemaess zu bekleiden. Diesem Zweck kommt das Gewand in Vollendung nach.");
+  case "vollendung":
+          return "Nichts ist vollkommen.\n";
+  case "hut":
+          return BS("Ein langer, spitzer Hut, der in der gleichen Weise wie seine Robe mit Monden und Sternen bestickt ist.");
+  case "mond": case "monde": case "stern": case "sterne":
+          return BS("Die Monde und die Sterne sind mit silbrigen Faeden gestickt, die das Licht funkelnd zurueckwerfen.");
+  case "faden": case "faeden":
+          return BS("Schwer zu sagen, woraus die Faeden sind. Mithril? "
+              "Mondsilber?");
+  case "licht": case "mondsilber": case "mithril":
+          return BS("Du streckt schon Deine Hand aus, um Merlins Gewand naeher zu untersuchen, aber da trifft Dich sein strenger Blick. Schuechtern nimmst Du von Deinem Vorhaben Abstand.");
+  case "abstand":
+          return BS("Von so einem Magier sollte man aus Respekt einen Schritt Abstand halten.");
+  case "respekt":
+          return "Du hast sicher mehr Respekt vor Merlin, als er vor Dir.\n";
+  case "schritt":
+          return "Wessen Schritt??\n";
+  case "hand": case "haende":
+          return BS("Merlins Haende haben lange, gichtgeplagte Finger und gelbe lange Fingernaegel. Seine linke Hand stuetzt sich auf seinen knorrigen Gehstock. Die rechte Hand ist unter seinem Gewand verborgen.");
+  case "gichtknoten": case "gicht":
+          return BS("Durch die Gichtknoten sehen seine Haende fast so knorrig wie sein Gehstock aus.");
+  case "finger": case "fingernaegel": case "fingernagel":
+          return BS("Seine Finger sind voller Gichtknoten und seine gelben Fingernaegel koennte Merlin auch mal wieder schneiden.");
+  case "gehstock": case "stock":
+          return BS("Merlin stuetzt sich auf einen Gehstock. Der knorrige Stock scheint schon fast Eins mit seiner gichtknotigen Hand geworden zu sein. Ob es sich bei dem Stock um einen Magierstab handelt?");
+  case "zauberstab": case "magierstab":
+          return BS("Merlin stuetzt sich auf einen knorrigen Gehstock. Das koennte sein Zauberstab sein - aber vielleicht ist es auch nur ein Gehstock.");
+  case "guertel": case "guertelschnalle":
+          return BS("Sein Gewand wird von einem weissen Guertel gehalten. Vorne, unter seinem Bart, kannst Du die Guertelschnalle sehen. Auf der Guertelschnalle steht: 'Ich koennte jederzeit aufhoeren.'");
+  }
+  return 0;
+}
+
+
+string short()
+{
+  return "Merlin, der Urvater aller Magier.\n";
+}
+
+string _query_race()
+{
+  return "Magier";
+}
+
+int _query_gender()
+{
+  return 1;
+}
+
+string QueryPossPronoun( mixed what, int casus, int number )
+{
+  int gen2;
+  gen2 = ( intp( what ) ) ? what : what->QueryProp(P_GENDER);
+  return "sein" + query_g_suffix( gen2, casus, number );
+}
+
+string query_g_suffix( int gen, int casus, int anzahl )
+{
+  return ({ ({ ({"","e"}), ({"es","er"}), ({"em","en"}), ({"","e"}) }),
+              ({ ({"","e"}), ({"es","er"}), ({"em","en"}), ({"en","e"}) }),
+              ({ ({"e","e"}), ({"er","er"}), ({"er","en"}), ({"e","e"}) }) })
+    [gen][casus][anzahl];
+}
+
+string QueryPronoun(int casus)
+{
+  switch(casus) {
+    case WER: return "er";  break;
+    case WEM: return "ihm";
+  }
+  return "ihn";
+}
+
+string _query_short()
+{
+  return short();
+}
+
+string _query_long()
+{
+  return long();
+}
+
+string _query_name()
+{
+  return "Merlin";
+}
+
+int _query_weight()
+{
+  return 75000;
+}
+
+static void PostSeher(string who, int g)
+{
+  mixed *art;
+
+  art=({"stufen","Merlin",0,0,sprintf("%s ist jetzt ein%s Seher%s!",who,(g==2?"e":""),(g==2?"in":"")),sprintf("\nAm %s hat %s die noetigen Bedingungen\nerfuellt und ist vom Spielerstatus zum Seherstatus aufgestiegen. \n\n Herzlichen Glueckwunsch, %s!\n",dtime(time()),who,who)});
+  "/secure/news"->WriteNote(art,1);
+}
+
+static void PostMagier(string who, string prom, int gender)
+{
+  mixed *art;
+
+  art=({"stufen","Merlin",0,0,sprintf("%s ist jetzt ein%s Magier%s!",who,(gender==2?"e":""),(gender==2?"in":"")),sprintf("\nAm %s hat %s %s zu%s Magier%s\nberufen. Herzlichen Glueckwunsch, %s. Glueckwuensche auch an Dich,\n %s, zu Deine%s neuen %s!\n",dtime(time()),prom,who,(gender==2?"r":"m"),(gender==2?"in":""),who,prom,
+(gender==2?"r":"m"),(gender==2?"Tochter":"Sohn"))});
+  "/secure/news"->WriteNote(art,1);
+}
+
+
+private void GibFehlerteufel(object wiz) {
+  if (!objectp(wiz))
+      return;
+
+  clone_object(FEHLERTEUFEL)->move(wiz, M_NOCHECK|M_GET);
+  tell_object(wiz, break_string(
+    "Huhu "+wiz->Name(WER) + "! Ich habe gesehen, dass Du keinen "
+    "Fehlerteufel hast. Dieses Tool ist zum Debuggen von Fehlern im "
+    "Mud sehr praktisch. Bitte schau ihn Dir mal an. :-) Die Hilfe zum "
+    "Fehlerteufel kannst Du mit 'man fehlerteufel' lesen. BTW: Wenn "
+    "Du den Fehlerteufel nicht willst, kannst Du ihn zerstoeren, ich "
+    "werde Dich kein zweites Mal damit belaestigen.",78,
+    "Merlin teilt Dir mit: ")
+    + "Merlin ueberreicht Dir aus der Ferne einen knusprig gebratenen "
+    "Fehlerteufel.\n\n");
+}
+
+private void notify_player_change(string who,int rein)
+{
+  if (!stringp(who))
+    return;
+
+  // Max-user festhalten
+  if (file_size("/etc/maxusers")<=0)
+    maxusers=0;
+  if (sizeof(users())>=maxusers)
+  {
+    maxusers=sizeof(users());
+    write_file("etc/maxusers",sprintf("%d user: %s (%s)\n",
+          maxusers,dtime(time()),query_load_average()),1);
+  }
+
+  // allen Magiern ab Level 20, die keinen Fehlerteufel haben und deren
+  // letzter Logout vor "Die, 17. Mar 2009, 23:40:04" => 1237329604 war,
+  // einen Fehlerteufel clonen und nen Hiwneis mitteilen.
+  object ob = find_player(who);
+  if (ob && rein && IS_WIZARD(ob)
+      && !present_clone(FEHLERTEUFEL,ob)
+      && ob->QueryProp(P_LAST_LOGOUT) < 1237329604 )
+    GibFehlerteufel(ob);
+}
+
+private void AnnounceNewPlayer(object pl) {
+
+  if (!pl || !interactive(pl))
+    return;
+
+  // verzoegern, bis der Spieler wirklich fertig ist.
+  if (pl->QueryProp(P_LEVEL) < 0) {
+    call_out(#'AnnounceNewPlayer,10,pl);
+    return;
+  }
+
+  if (!(int)pl->QueryGuest()) {
+  string plname = (string)pl->Name(WER);
+    CHMASTER->send("Anfaenger", this_object(),
+      sprintf("%s macht nun ebenfalls das Morgengrauen unsicher. "
+              "Herzlich Willkommen %s!", plname, plname)
+      );
+  }
+}
+
+// Kann bei reboot wieder raus... zesstra, 16.03.2007
+#if __BOOT_TIME__ < 1318110723
+#define BUGTIME 1318003200 
+#define BUGFIXTIME 1318109421
+void renew_player(string plname, int force) {
+    object pl;
+    mixed err;
+
+    pl = find_player(plname);
+    if (!objectp(pl) || !query_once_interactive(pl)) return;
+
+    // jung oder alt genug, Abbruch. ;-)
+    if ((object_time(pl) > BUGFIXTIME)
+        || object_time(pl) < BUGTIME)
+        return;
+
+    if (get_eval_cost() < 1000000 || !force ) {
+        call_out("renew_player",8,plname,1);
+        tell_object(pl,break_string(sprintf("Huhu %s, um einen Bug in "
+                "Deinem Spielerobjekt zu beheben, wirst Du in ca. 8s "
+                "neugeladen. Bitte warte bis dahin ab und bleibe, wo "
+                "Du gerade bist.",pl->Name()),78,"Merlin teilt Dir mit: "));
+        return;
+    }
+
+    plname=getuid(pl);
+    tell_object(pl,break_string(sprintf("Huhu %s, um einen Bug in "
+            "Deinem Spielerobjekt zu beheben, wirst Du jetzt "
+            "neugeladen.",pl->Name()),78,"Merlin teilt Dir mit: "));
+
+    if (err=catch(master()->renew_player_object(pl);publish)) {
+        log_file("zesstra/failed_rewew.log",sprintf("%s: Fehler %O beim "
+              "Renew von %s\n",dtime(time()),err,plname));
+        tell_object(pl,break_string("Beim Neuladen Deines Spielerobjekts "
+              "gab es einen Fehler. Bitte wende Dich an einen EM.",78,
+          "Merlin teilt Dir mit: "));
+    }
+    else {
+      log_file("zesstra/renewed.log",sprintf("%s: %s wurde renewed.\n",
+            dtime(time()),plname));
+      if (objectp(find_player(plname)))
+          tell_object(find_player(plname),
+            break_string("Dein Spielerobjekt wurde neugeladen. Du "
+          "kannst jetzt normal weiterspielen. Falls Du etwas ungewoehnliches "
+          "bemerkst, melde Dich bitte bei einem Erzmagier. Viel Spass im "
+          "Spiel!",78, "Merlin teilt Dir mit: "));
+    }
+}
+#endif
+
+void player_change(string eid, object trigob, mixed data) {
+
+  if (!trigob || !interactive(trigob))
+    return;
+
+  switch(eid) {
+    case EVT_LIB_PLAYER_CREATION:
+      call_out(#'AnnounceNewPlayer, 5, trigob);
+      break;
+    case EVT_LIB_LOGIN:
+      notify_player_change(data[E_PLNAME],1);
+#if __BOOT_TIME__ < 1318110723
+      // Spieler evtl. renewen? Bei reboot raus (08.10.2011)
+      renew_player(data[E_PLNAME],0);
+#endif
+      break;
+  }
+}
+
+
+//besser P_NO_ATTACK als Defend()...
+mixed _query_no_attack() {
+  if (!living(PL) || !objectp(environment(PL)) ) return(1);
+
+  tell_room(environment(), "Merlin schimpft: Lass das!!!\n",({PL}));
+  tell_room(environment(),sprintf(
+        "Merlin gibt %s eine schallende Ohrfeige.\n",      
+        capitalize((string)this_player()->name(WEM))), ({this_player()}));
+  return("Merlin schimpft: Lass das!!!\n"
+      "Merlin gibt Dir eine schallende Ohrfeige.\n");
+}
+
+/*
+int Defend(int dam, string dam_type, int spell, object enemy)
+{
+  object en;
+  if (!en=previous_object() || !living(en))
+    en = this_player();
+  tell_room(environment(), "Merlin schimpft: Lass das!!!\n");
+  write("Merlin gibt Dir eine schallende Ohrfeige.\n");
+  say(sprintf("Merlin gibt %s eine schallende Ohrfeige.\n",
+              capitalize((string)this_player()->name(WEM))), this_player());
+  en->StopHuntFor(this_object(), 1);
+}
+*/
+void shoutansw()
+{
+  object env,ti;
+  string *path;
+  string ans;
+
+  if (!(env=environment()))
+    return;
+  if (!(ti=this_interactive())||(int)ti->QueryProp(P_LEVEL)<19||ti->QueryProp(P_GHOST))
+    return;
+  tell_object(ti,"Du spuerst einen sengenden Schmerz.\n");
+  ti->do_damage( ((int)ti->QueryProp(P_HP)) / 2 + 5);
+  if((int)ti->QueryProp(P_GHOST)) return;
+  ans="ich hab nicht die leiseste Idee, wo ich hier bin ...\n";
+  path=old_explode(object_name(env),"/");
+  if (path[0]=="d")
+  {
+    ans="werweisswo ..";
+    switch(path[1])
+    {
+      case "gebirge":
+      case "wald":
+      case "dschungel":
+      ans="im "+capitalize(path[1]);
+      break;
+      case "inseln":
+      ans="auf den Inseln";
+      break;
+      case "polar":
+      ans="im Polargebiet";
+      break;
+      case "unterwelt":
+      case "ebene":
+      case "wueste":
+      ans="in der "+capitalize(path[1]);
+      break;
+      case "vland":
+      ans="im Verlorenen Land";
+      break;
+      case "fernwest":
+      ans="in Fernwest";
+      break;
+    }
+    ans="ich bin "+ans+".\n";
+  }
+  if (path[0]=="players")
+    ans="ich hab mich verlaufen - ich schaetze, "+capitalize(path[1])+
+      " hat was damit zu tun...\n";
+  if (path[0]=="gilden") {
+    switch(path[1]) {
+      case "abenteurer":
+      ans="der Abenteurergilde";
+      break;
+      case "bierschuettler":
+      ans="der Bierschuettlergilde";
+      break;
+      case "chaos":
+      ans="der Gilde der Chaoten";
+      break;
+      case "kaempfer":
+      ans="der Kaempfergilde";
+      break;
+      case "karate":
+      ans="einem Karatedojo";
+      break;
+      case "klerus":
+      ans="einem Tempel der Kleriker";
+      break;
+      case "zauberer":
+      ans="der Akademie der Zauberer";
+      break;
+      default:
+      ans="irgendeiner Gilde";
+    }
+    ans="ich stehe in "+ans+".\n";
+  }
+                if( path[0]=="p" && path[1]=="verein" ) {
+                        ans="ich stehe in Port Vain.";
+                }
+  if (!this_interactive()->QueryProp(P_INVIS))
+    ans=this_interactive()->Name(WER)+", "+ans;
+  else ans=capitalize(ans);
+  call_out("myshout",0,break_string(ans, 78, "Merlin ruft: "));
+}
+
+void myshout(string s)
+{
+  write(s);
+  shout(s);
+}
+
+void catch_tell(string str)
+{
+  string name, dummy; mixed message;
+  int i,s;
+
+  if (!this_player())
+    return;
+
+  if (environment()==environment(this_player()) && previous_object()!=ME)
+    tell_room(this_object(),"*"+str);
+
+  if (this_player() != this_interactive())
+    return;
+
+  if (busy) {
+    tell_object(this_player(), 
+       "Merlin teilt Dir mit: Ich bin beschaeftigt.\n");
+    return;
+  }
+    
+  str=lower_case(str);
+
+  if ((strstr(str, "teilt dir mit:") != -1) 
+      && (strstr(str, "wo bist du") !=-1)) {
+    shoutansw();
+    return;
+  }
+
+  if ((strstr(str, "teilt dir mit:") != -1) && 
+      ((strstr(str, "seher") != -1 || strstr(str, "seherin") != -1)))
+    {
+      create_seer(this_player());
+      return;
+    }
+
+  if (sscanf(str,"%s %s sagt: %s",dummy,name,message)!=3)
+    if (sscanf(str,"%s sagt: %s",name,message)!=2)
+      return;
+
+  if (!name || name=="" ||            
+      catch(s=file_size("save/"+name[0..0]+"/"+name+".o");publish)            
+      || s<=0 )
+    return;
+
+  if (name!=getuid(this_interactive()) && !ARCH_SECURITY)
+  {
+    if (flag==getuid(this_interactive())+"##"+name)
+      return;
+    call_out("do_say",0,"Merlin sagt: "+capitalize(getuid(this_interactive()))+" hat gerade so getan, als wuerde "+capitalize(name)+" etwas sagen!\n");
+    flag=getuid(this_interactive())+"##"+name;
+    call_out("clear_flag",0);
+    return;
+  }
+  flag=0;
+  for (i=0;i<sizeof(whitespaces);i++)
+  {
+    message=old_explode(message,whitespaces[i]);
+    if (!pointerp(message)||sizeof(message)==0)
+      return;
+    message=implode(message," ");
+  }
+
+  message=old_explode(message," ");
+
+  if (!pointerp(message) || !sizeof(message))
+    return;
+
+  message-=({0})-({""});
+
+  if (!message || sizeof(message)==0)
+    return;
+
+  if (member(message, "merlin")==-1)
+    return;
+
+  message-=({"merlin"});
+
+  message=implode(message," ");
+
+  if (!message || message=="")
+    return;
+
+  determine_action(message, name);
+}
+
+static int determine_action(string mess, string name)
+{
+  string str;
+
+  if (mess=="mach mich zum magier" || mess=="mach mich zur magierin")
+    return create_wizard(name,name);
+
+  if (sscanf(mess,"mach %s zum magier",str)==1||sscanf(mess,"mach %s zur magierin",str)==1)
+    return create_wizard(lower_case(str),name);
+
+  give_help(name);
+  return 0;
+}
+
+#define Say(str) say(str,who)
+#define Write(str) tell_object(who,str)
+
+int QueryBedingungen(object pl,string was)
+{
+  int ret,i,gen;
+  string s;
+
+  if (IS_SEER(pl))
+    return 1;
+  
+  if ((int)LEPMASTER->QueryReadyForWiz(pl)==1)
+    return 1;
+
+  s=(string)LEPMASTER->QueryReadyForWizText(pl); 
+
+  tell_object(pl, break_string("Merlin gruebelt ein wenig und teilt Dir "
+                               "nach einigem Ueberlegen mit:\n\n", 78,0,1));
+  
+  tell_object(pl, s+"\n");
+
+  tell_object(pl, break_string("Da ist wohl fuer Dich noch etwas zu tun...",
+                               78, "Merlin teilt Dir mit: "));
+
+  if (environment() == environment(pl))
+    say((string)pl->name(WER)
+        +" erfuellt noch nicht alle Anforderungen fuer den "+was+"status.\n",pl);
+  
+  return 0;
+}
+
+static int create_wizard(mixed who, mixed promoter)
+{
+  mixed *domains;
+  object vertrag;
+  int ret;
+  string gen;
+
+  who=find_player(who);
+  promoter=find_player(promoter);
+  if (!who || !promoter)
+    return 0;
+  //
+  // player not interactive?
+  //
+  if (!interactive(who))
+  {
+    write(capitalize(getuid(who))+" ist nicht aktiv.\n");
+    return 0;
+  }
+  //
+  // player is second?
+  //
+  if (who->QueryProp(P_SECOND))
+  {
+     tell_object(who,"Du bist ein Zweitspieler und kannst als solcher kein Magier werden.\n"
+          +"Versuchs Doch mal mit Deinem Erstie.\n");
+     write("Du kannst "+who->name()+" nicht zum Magier machen. Das geht bei "
+          +"Zweities\nleider nicht. Frag doch mal nach dem Erstie.\n");
+     return 1;
+  }
+  //
+  // wants to advance himself?
+  //
+  if (who==promoter)
+  {
+    write("Du kannst Dich nicht selber befoerdern. Such Dir dazu bitte einen "+
+              "Vollmagier,\nder Dir einen Vertrag gibt.\n");
+    return 1;
+  }
+  //
+  // banished?
+  //
+  if (MBanishListe[who,1])
+  {
+    tell_object(promoter,capitalize(who)+" darf nur von einem Erzmagier zum "+
+                    "Magier gemacht werden.\n");
+    say(promoter->name(WER)+" ist kein Erzmagier und darf "+capitalize(who)+
+            " nicht zum Magier machen.\n");
+    return 1;
+  }
+  //
+  // player has PK?
+  //
+  if (who->QueryProp("playerkills")) {
+     tell_room(environment(who),who->name()+" hat noch einen Playerkill und kann somit kein Magier werden.\n"
+                           "Am besten den Sheriff oder einen Erzmagier verstaendigen.\n");
+         return 1;
+  }
+  //
+  // already dom_member?
+  //
+  if (IS_DOMAINMEMBER(who))
+  {
+    Write("Fuer Dich kann ich nichts mehr tun. Du bist doch schon Magier!\n");
+    return 1;
+  }
+
+  //
+  // "advanced learner"?
+  //
+  if (query_wiz_level(who) == 20)
+  {
+    if (!ARCH_SECURITY) {
+      Write("Dich kann nur ein Erzmagier zum \"richtigen\" Magier machen!\n");
+      Say("Nur ein Erzmagier kann "+who->name(WEN)+" zum \"richtigen\" Magier machen!\n");
+      return 0;
+    }
+    ret=(int)"secure/master"->advance_wizlevel(geteuid(who),21);
+    if (ret<=0)
+    {
+      say("Merlin: error "+ret+"\n");
+      write("Merlin: error "+ret+"\n");
+    }
+    write("Merlin ruft: "+(string)who->name(WER)+" ist in den Kreis der Magier "+
+              "aufgenommen worden!\n");
+    shout("Merlin ruft: "+(string)who->name(WER)+" ist in den Kreis der Magier "+
+              "aufgenommen worden!\n");
+    "secure/master"->renew_player_object(who);
+    return 1;
+  }
+
+  //
+  // already wizard?
+  //
+  if (IS_WIZARD(who))
+  {
+    domains=(string*)"secure/master"->get_domain_homes(getuid(who));
+    if (!domains || !pointerp(domains) || !sizeof(domains))
+    {
+      Say(who->name(WER)+" gehoert noch keiner Region an und kann daher noch "+
+              "kein Voll-\nmagier werden.\n");
+      Write("Du gehoerst noch keiner Region an. Schliess Dich zunaechst einer "+
+                "an und komm\ndann wieder!\n");
+      return 0;
+    }
+    ret=(int)"secure/master"->advance_wizlevel(geteuid(who),25);
+    if(ret>0)
+    {
+      shout(who->name(WER)+" arbeitet jetzt an einer Region mit!\n");
+      "secure/master"->renew_player_object(who);
+      return 1;
+    }
+    write("RETURNVALUE "+ret+"\n");
+    say("RETURNVALUE "+ret+"\n");
+    return 0;
+  }
+
+  //
+  // still learner?
+  //
+  if (IS_LEARNER(who))
+  {
+    if (!ARCH_SECURITY) {
+      Write("Dich kann nur ein Erzmagier zum \"richtigen\" Magier machen!\n");
+      Say("Nur ein Erzmagier kann "+who->name(WEN)+" zum \"richtigen\" Magier machen!\n");
+      return 0;
+    }
+    ret=(int)"secure/master"->advance_wizlevel(geteuid(who),20);
+    if (ret<=0)
+    {
+      say("Merlin: error "+ret+"\n");
+      write("Merlin: error "+ret+"\n");
+    }
+    string wizname = (string)who->query_real_name();
+    "secure/master"->renew_player_object(who);
+    // Fehlerteufel geben
+    call_out(#'GibFehlerteufel,4,find_player(wizname));
+    return 1;
+  }
+
+  //
+  // promoter's level is not >DOMAINMEMBER_LVL+1
+  //
+  if (secure_level()<=DOMAINMEMBER_LVL)
+  {
+    tell_object(promoter,"Du musst derzeit mindestens Level "+
+                    (DOMAINMEMBER_LVL+1)+" haben, um jemanden befoerden zu "+
+                                "koennen.\n");
+    say(promoter->name(WER)+" ist kein Vollmagier und darf daher niemanden "+
+            "zum Vollmagier machen.\n");
+    return 1;
+  }
+
+  // der Check auf den Blutprintnamen ist fast ueberfluessig, aber nur fast.
+  // Er schlaegt fehl, wenn der Vertrag der Clone einer alten BP ist.
+  if (!(vertrag=present_clone("/obj/vertrag",who)) ||
+      object_name(blueprint(vertrag))!="/obj/vertrag" ||
+      !((int)vertrag->is_unterschrieben()) )
+  {
+    Write("Du hast ja gar keinen unterschriebenen Vertrag bei Dir. Besorg Dir "+
+              "einen Ver-\ntrag, unterschreibe ihn und komm dann wieder!\n");
+    Say(who->name(WER)+" hat keinen unterschriebenen Vertrag.\n");
+    return 1;
+  }
+  if (geteuid(vertrag)!=secure_euid())
+  {
+    tell_object(promoter,"Das geht nicht, er hat einen Vertrag von "+
+                    capitalize(geteuid(vertrag))+" bei sich.\n");
+    say(promoter->name(WER)+" versucht, "+who->name(WER)+" zum Magier zu "+
+            "machen, doch "+who->name(WER)+" hat einen Vertrag\nvon "+
+                capitalize(geteuid(vertrag))+" bei sich.\n",promoter);
+    return 0;
+  }
+  if (!QueryBedingungen(who,"Magier"))
+    return 0;
+  ret=(int)"secure/master"->advance_wizlevel(geteuid(who),15);
+  if (ret>0)
+  {
+    PostMagier(capitalize(getuid(who)),
+        capitalize(secure_euid()),(int)who->QueryProp(P_GENDER));
+    write_file("/log/SPONSOR",dtime(time())+": "+capitalize(getuid(promoter))+" macht "+
+             who->name(WER)+" zum Learner.\n");
+    write(who->name(WER)+" ist in den Kreis der Magier aufgenommen worden!\n");
+    shout(who->name(WER)+" ist in den Kreis der Magier aufgenommen worden!\n");
+    if ("secure/master"->set_player_object(geteuid(who),"/std/shells/magier")
+            <=0)
+    {
+      say("MERLIN: konnte Magiershell nicht einstellen.\n");
+      write("MERLIN: konnte Magiershell nicht einstellen.\n");
+    }
+    "secure/master"->renew_player_object(who);
+    return 1;
+  }
+  write(" --RETURNVALUE IS "+ret+"\n");
+  say(" --RETURNVALUE IS "+ret+"\n");
+
+  return(ret); //non-void funktion
+}
+
+void clear_flag()
+{
+  flag=0;
+}
+
+void do_say(string str)
+{
+  say(str);
+}
+
+
+
+void seer_sequenz3(mixed player, string plname)
+{
+  string playername; 
+  object faq;
+  string text;
+
+  if (!objectp(player)) {
+    tell_room(environment(), sprintf("Merlin sagt: Na, wo ist denn %s "
+                                     "hin?\n", plname));
+    busy = 0;
+    return;
+  }
+  if (environment() != environment(player))
+    move(environment(player), M_TPORT);
+  
+  tell_object(player,break_string("\n\nEs macht *PUFF* und eine Schwefelwolke "
+                                  +"steigt um Dich herum auf.\n\nDie Aura "
+                                  +"verschwindet langsam und Du fuehlst Dich "
+                                  +"nun viel erfahrener.", 78,0,1));
+  tell_room(environment(),
+           break_string(sprintf(
+                       "\n\nEs macht *PUFF* und eine Schwefelwolke steigt "
+                       +"um %s herum auf.\n\nDie Aura verschwindet langsam.\n",
+                       plname),78,0,1), ({player}));
+ 
+  if ("secure/master"->advance_wizlevel(geteuid(player),1)<=0)
+  {
+    write("Merlin sagt: Da geht was schief ... sag mal Zook Bescheid.\n");
+    say("Merlin sagt: Da geht was schief ... sag mal Zook Bescheid.\n");
+    busy = 0;
+    return;
+  }
+  playername = geteuid(player);
+  "secure/master"->renew_player_object(player);
+  player = find_player(playername);
+  text = sprintf(" ist jetzt ein%s Seher%s!\n",
+    (((int)player->QueryProp(P_GENDER))==2?"e":""),
+    (((int)player->QueryProp(P_GENDER))==2?"in":""));
+  write("Merlin ruft: "+capitalize(playername)+text);
+  shout("Merlin ruft: "+capitalize(playername)+text);
+  PostSeher(capitalize(playername),(int)player->QueryProp(P_GENDER));
+  if(!catch(
+        faq=clone_object("/d/seher/haeuser/special/seherfaqmobil") ;publish))
+  {
+    faq->move(player,M_NOCHECK);
+    tell_object(player, "Aus dem Nichts erscheint eine kleine Belohnung fuer Dich.\n");
+  }
+  
+  tell_object(player, "\n\nDu laechelst gluecklich in die Runde.\n\n");
+  tell_room(environment(), sprintf("\n\n%s laechelt gluecklich in die Runde.\n\n", plname), ({player}));
+
+ busy=0;
+}
+
+void seer_sequenz2(mixed player, string plname)
+{
+  if (!objectp(player)) {
+    tell_room(environment(), sprintf(
+                                     "Merlin sagt: Na, wo ist denn %s hin?\n", 
+                                     plname));
+    busy = 0;
+    return;
+  }
+  if (environment() != environment(player))
+    move(environment(player), M_TPORT);
+
+  tell_object(player, break_string("\nMerlin hebt die Arme und "
+       +"und macht einige beschwoerende Gesten. Um Dich herum erscheint "
+       +"eine gelbliche Aura, die immer heller wird, bis Du Deine "
+                                   +"Umwelt kaum mehr erkennen kannst.\n\n",
+                                   78,0,1));
+  tell_room(environment(),
+            break_string(sprintf("\nMerlin hebt die Arme und macht einige "
+                         +"beschwoerende Gesten. Um %s erscheint eine "
+                         +"gelbliche Aura, die immer heller wird, bis "
+                                 +"man %s kaum mehr erkennen kann.\n\n", 
+                                 plname, plname),78,0,1), ({player}));
+
+  call_out("seer_sequenz3", 7, player, plname);
+}
+
+void seer_sequenz1(mixed player, string plname)
+{
+  if (!objectp(player))
+    return;
+
+  move(environment(player), M_TPORT);
+
+  tell_room(environment(),
+            break_string(sprintf("Das ist eine grosse Freude. %s hat sich "
+     "tapfer durch das "MUDNAME" geschlagen und mittlerweile alle "
+     "Anforderungen erfuellt, um %s zu werden.",                                  
+     plname, ((int)player->QueryProp(P_GENDER))==1?"Seher":"Seherin"),
+              78, "Merlin sagt: "));
+
+  call_out("seer_sequenz2", 4, player, plname);
+}
+
+  
+static int create_seer(mixed player)
+{
+  if (!objectp(player))
+    return 0;
+  if (IS_SEER(player))
+  {
+    write(break_string("Du bist doch schon kein normaler Spieler mehr, "
+                       +"was soll es also?", 78, "Merlin teilt Dir mit: "));
+    return 1;
+  }
+
+  if (!QueryBedingungen(player,"Seher"))
+    return -1;
+
+
+  write(break_string("Grosse Freude! Du hast alle Anforderungen "
+                                   "erfuellt. Warte einen Moment...", 78,
+                                   "Merlin teilt Dir mit: "));
+  busy=1;
+  call_out("seer_sequenz1",4,player, player->Name());
+  return(0); //non-void funktion
+}
+
+static void give_help(mixed player)
+{
+  if (!objectp(player))
+    return;
+
+  tell_object(player,
+              break_string("Ich weiss nicht, was ich fuer Dich tun kann. "
+                           +"Ich kann Dich zum Seher oder zum Magier "
+                           +"machen, wenn Du mich darum bittest.", 78,
+                           "Merlin teilt Dir mit: "));
+}
+              
+
+void reset()
+{
+}
+
+public int remove(int silent) {
+  EVENTD->UnregisterEvent(EVT_LIB_LOGIN, ME);
+  EVENTD->UnregisterEvent(EVT_LIB_PLAYER_CREATION, ME);
+  destruct(this_object());
+  return 1;
+}
+
+void wandern()
+{
+  mapping ex,rooms;
+  mixed ziel;
+  object *raeume,*laeden,env,ob;
+  string *ind,fn;
+  int i;
+
+  while (remove_call_out("wandern")>=0);
+  call_out("wandern",45+random(100));
+  if (busy) return;
+  if (delay)
+  {
+    delay=0;
+    return;
+  }
+  if (!(env=environment()))
+  {
+    move("/gilden/abenteurer",0);
+        env=environment();
+  }
+  //ueber Inv iterieren, ob da ein nicht-idelnder Spieler mit genug XP
+  //rumhaengt.
+  raeume=all_inventory(env);
+  for(i=sizeof(raeume); i-- ; ) {
+    if (interactive(raeume[i]) && query_idle(raeume[i])<180 &&
+        raeume[i]->QueryProp(P_XP)>999999)
+      return;
+  }
+  ex=(mapping)env->QueryProp(P_EXITS);
+  if (!mappingp(ex))
+    return;
+  ind=m_indices(ex);rooms=([]);laeden=({});
+  for (i=sizeof(ind)-1;i>=0;i--)
+  {
+    if (!stringp(ziel=ex[ind[i]]))
+      continue;
+    if (!ob=find_object(ziel))
+      continue;
+    rooms+=([ob]);
+  }
+  rooms-=([env]);
+  if (prev_room && sizeof(m_indices(rooms))>1)
+    rooms-=([prev_room]);
+  prev_room=env;
+  raeume=m_indices(rooms);
+  if (!i=sizeof(raeume))
+    return;
+  ob=raeume[random(i)];
+  move(ob,0);
+}
+
+string int_short()
+{
+  return "Du bist im allseits beliebten Wizclub \"Zum lustigen Merlin\".\n";
+}
+
+string int_long(object who,mixed where)
+{
+  string descr;
+
+  if( IS_LEARNER(who) && who->QueryProp( P_WANTS_TO_LEARN ) )
+    descr = "[" + object_name(ME) + "]\n";
+  else
+    descr = "";
+  descr+="Du bist im Wizclub 'Zum lustigen Merlin'. Dieser Wizclub ist etwas besonderes -\ner hat keinen festen Standort, sondern wandert im Mud herum. Ausserdem kann er\nSpieler zu Magiern machen - aber nur, wenn man ihn von aussen aus darauf\nanspricht.\n";
+  if ( who->QueryProp(P_SHOW_EXITS) )
+    descr += "Es gibt 2 sichtbare Ausgaenge: raus und gilde.\n";
+  descr += make_invlist(who, all_inventory(ME) - ({ who }));
+  if( environment())
+    descr += "\nAusserhalb siehst Du:\n"
+      + environment()->int_short(who,ME);
+  return descr;
+}
+
+string make_invlist(object viewer,object *inv)
+{
+  int i, iswiz;
+  string descr;
+
+  descr = "";
+  iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
+
+  for (i = 0; i < sizeof(inv); i++)
+  {
+    string sh;
+    sh = (string)inv[i]->short();
+    if (iswiz)
+    {
+      if (sh) sh = sh[0..<2] + " ["+object_name(inv[i])+"]\n";
+      else sh = "["+object_name(inv[i])+"]\n";
+    }
+    if (sh) descr += sh;
+  }
+  return descr;
+}
+
+void init()
+{
+  if (interactive(this_player()) && environment(this_player())==ME)
+    add_action("gehen","",1);
+  add_action("befoerdere","befoerder",1);
+}
+
+int befoerdere(string str)
+{
+  string wen;
+  int ret,stufe,l;
+
+  if (!LORD_SECURITY) return 0;
+  if (!str||sscanf(str,"%s auf stufe %d",wen,stufe)<2)
+  {
+    notify_fail("Syntax: befoerdere <name> auf stufe <stufe>\n");
+        return 0;
+  }
+  if (stufe>29)
+  {
+    write("Maximum ist Stufe 29!\n");
+        return 1;
+  }
+  l=query_wiz_level(wen);
+  if (l<25)
+  {
+    printf("%s muss mindestens zwecks Weiterbefoerderung mindestens Stufe 25 haben.\n",
+        capitalize(wen));        
+    return 1;
+  }
+  if (l>=stufe)
+  {
+    printf("%s ist schon Stufe %d.\n",capitalize(wen),l);
+        return 1;
+  }
+  ret=(int)"secure/master"->advance_wizlevel(wen,stufe);
+  if (ret<=0)
+    printf("Errorcode %d\n",ret);
+  else
+    write("ok\n");
+  return 1;
+}
+
+int sag(mixed str)
+{
+  int i;
+
+  if (str==0)
+    str="";
+  str = old_explode(break_string(str, 60 ), "\n");
+  if (busy) {
+    tell_room(ME, "Merlin mault: Ich bin beschaeftigt.\n");
+    return 0;
+  }
+  for (i = 0; i < sizeof(str); i++)
+  {
+    if (!str[i])
+      str[i]="";
+    tell_room(environment(),"Merlin sagt: "+str[i]+"\n");
+    tell_room(ME,"*Merlin sagt: "+str[i]+"\n");
+  }
+  return 1;
+}
+
+void show_exits()
+{
+  printf("%s",environment()->GetExits());
+}
+
+int gehen(string str)
+{
+  string verb;
+  int i;
+  mapping exits;
+  string *ex;
+
+  if (busy) {
+    write("Merlin mault: Ich bin beschaeftigt.\n");
+    return 0;
+  }
+
+  verb=query_verb();
+  str=(string)this_interactive()->_unparsed_args();
+  switch (verb)
+  {
+    case "gilde":
+      this_player()->move("/gilden/abenteurer",M_GO,"in die Gilde");
+      return 1;
+    case "raus":
+      this_player()->move(environment(),M_GO,"raus");
+      return 1;
+  }
+  if (!IS_WIZARD(this_interactive()))
+    return 0;
+  if (verb!="merlin")
+    return 0;
+  delay=1;
+  exits=(mapping)environment()->QueryProp(P_EXITS);
+  if (!str||str=="")
+  {
+    ex=m_indices(exits);
+    printf(environment()->int_short(ME,ME));
+    show_exits();
+    return 1;
+  }
+  if (old_explode(str," ")[0]=="sag")
+    return sag(implode(old_explode(str," ")[1..]," "));
+  if (str[0]==';')
+  {
+    tell_room(environment(ME),"Merlins "+str[1..]+"\n");
+    tell_room(ME,"*Merlins "+str[1..]+"\n");
+    return 1;
+  }
+  if (str[0]==':')
+  {
+    tell_room(environment(ME),"Merlin "+str[1..]+"\n");
+    tell_room(ME,"*Merlin "+str[1..]+"\n");
+    return 1;
+  }
+  if (str[0]=='*')
+  {
+    goto(str[1..]);
+    printf(environment()->int_short(ME,ME));
+    show_exits();
+    return 1;
+  }
+  if (!exits[str])
+    return 0;
+  move(exits[str],0);
+  write("Merlin sagt: Jawoll, Chef.\n");
+  printf(environment()->int_short(ME,ME));
+  show_exits();
+  return 1;
+}
+
+int goto(mixed dest)
+{
+  object ob;
+  string path;
+  mixed ret;
+
+  if (busy) {
+    write("Merlin mault: Ich bin beschaeftigt.\n");
+    return 1;
+  }
+
+  if (!dest) {
+    write("Wohin moechtest Du Merlin bringen ?\n");
+    return 1;
+  }
+  ob = find_living(dest);
+  if (ob) {
+    ob = environment(ob);
+    move(object_name(ob),0);
+    return 1;
+  }
+  path=(mixed)"secure/master"->_get_path(dest,getuid(this_player()));
+  ret=catch(load_object(path);publish);
+  if (ret && file_size(path)<0)
+  {
+    dest=match_living(dest,1);
+    if (stringp(dest))
+    {
+      ob = environment(find_living(dest));
+      move(object_name(ob),M_TPORT|M_NOCHECK);
+      return 1;
+    }
+    printf("Fehler beim Teleport nach %O: No such file.\n",path);
+    return 1;
+  }
+  if (ret)
+    printf("Fehler beim Teleport nach %O: %O\n",dest,ret);
+  ret=catch(move(path,M_TPORT|M_NOCHECK);publish);
+  if (stringp(ret))
+    ret=implode(old_explode(ret,"\n"),"");
+  if (ret)
+    printf("Fehler beim Teleport nach %O: %O\n",dest,ret);
+  return 1;
+}
+
+mixed locate_objects(string complex_desc)
+{
+  object ob;
+
+  if (complex_desc=="alles" || complex_desc=="alle")
+    return filter_objects(all_inventory(this_object()),"short");
+  if (ob = present(complex_desc,this_object()) )
+    return ({ ob });
+  return ({}) ;
+}
+
+void MBanishInsert(string name, string grund, object wer)
+{
+  if ( !name || !stringp(name) || !sizeof(name) ||
+       !grund || !stringp(grund) || !sizeof(grund) ||
+       !wer || !objectp(wer) || getuid(wer) != secure_euid() ||
+       !IS_DEPUTY(wer) )
+    return;
+
+  MBanishListe += ([name:grund;getuid(wer)]);
+  save_object(SAVEFILE);
+}
+
+mapping MBanishList()
+{
+    if ( !IS_DEPUTY(secure_euid()) )
+        return 0;
+
+    return deep_copy(MBanishListe);
+}
+
+void MBanishDelete(string name)
+{
+  if ( !name || !stringp(name) || !sizeof(name) || !ARCH_SECURITY )
+    return 0;
+
+  MBanishListe = m_copy_delete( MBanishListe, name );
+  save_object(SAVEFILE);
+}
+
+string _query_noget()
+{
+  return "Merlin mag das nicht!\n";
+}
+
diff --git a/secure/mini_props.c b/secure/mini_props.c
new file mode 100644
index 0000000..42345a4
--- /dev/null
+++ b/secure/mini_props.c
@@ -0,0 +1,40 @@
+// MorgenGrauen MUDlib
+//
+// mini_props.c -- Abgespeckte Verwaltung von Props
+//                 Aus Sicherheitsgruenden darf hier nicht die "Vollversion"
+//                 mit echten Properties stehen
+//
+// $Id$
+#pragma strict_types,save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic,range_check,warn_deprecated
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+nosave mapping prop;
+
+private void InitProps()
+{
+    prop = m_allocate(2,1);
+}
+
+
+public varargs mixed Query( string str, int type )
+{
+    if ( !mappingp(prop) )
+        InitProps();
+
+    return prop[str];
+}
+
+public varargs mixed Set(string name, mixed value, int type, int extern)
+{
+    if ( !mappingp(prop) )
+        InitProps();
+
+    return prop[name] = value;
+}
+
diff --git a/secure/misc/mssp.c b/secure/misc/mssp.c
new file mode 100644
index 0000000..4c45388
--- /dev/null
+++ b/secure/misc/mssp.c
@@ -0,0 +1,214 @@
+#include <config.h>
+#include <thing/description.h>
+#include <strings.h>
+
+// hosts who asked us in the past and timestamp of last connection which got
+// the full datatset.
+private nosave mapping peers = ([:1]);
+
+/* For a list of official and extended fields:
+   http://www.mudbytes.net/index.php?a=articles&s=MSSP_Fields
+   For a protocal description:
+   http://tintin.sourceforge.net/mssp/
+   */
+
+#define MSSP_VAR 1
+#define MSSP_VAL 2
+
+public string convert_tn(mapping ldata);
+
+protected string list_ports() {
+  int ports = query_mud_port(-1) - 1;
+  string res="23";
+  for(; ports>=0 ; --ports)
+    res += "\t" + to_string(query_mud_port(ports));
+
+  return res;
+}
+
+#define DESCRIPTION \
+    MUDNAME" is a native German LPmud. It was founded in 1992 and has "\
+    "been prospering since. The world features an original fantasy setting "\
+    "with many facets. The 13 domains form a big world with 18000 rooms to "\
+    "explore and several thousand NPCs. You can choose between 7 races and "\
+    "10 classes (guilds). Advancing is done through a combination of "\
+    "exploration points, experience points, class skills, finished "\
+    "adventures and some more. The levels are not limited, current maximum "\
+    "is about 200. After becoming a high level player it's possible to "\
+    "become a wizard and add your own imagination to the game. We are "\
+    "especially proud of attracting a number of visually impaired players "\
+    "who very much enjoy playing a text based online RPG. Thats why we have "\
+    "an option to turn ascii-graphics off. Multiplaying is a bit restricted "\
+    "and scripting is discouraged."
+
+private nosave mapping mindata = ([
+    "NAME"             : MUDNAME,
+    "UPTIME"           : to_string(__BOOT_TIME__),
+    ]);
+
+private nosave mapping data = mindata + ([
+    "PORT"             : list_ports(),
+    "CODEBASE"         : _MUDLIB_NAME_+"-"+_MUDLIB_VERSION_,
+    "HOSTNAME"         : __HOST_NAME__ + "." + __DOMAIN_NAME__,
+#if MUDHOST == __HOST_NAME__
+// diese Angaben sollen nur gesendet werden, wenn das Mud wirklich auf dem
+// MG-Rechner laeuft und kein Homemud ist.
+    "CRAWL DELAY"      : "1",
+    "DESCRIPTION"      : DESCRIPTION,
+    "CREATED"          : "1992",
+    "ICON"             : "http://mg.mud.de/newweb/img/icon.gif",
+    "IP"               : "87.79.24.60",
+    "CONTACT"          : "mud@mg.mud.de",
+    "WEBSITE"          : "http://mg.mud.de/",
+    "AREAS"            : "13",
+    "MOBILES"          : "2600",
+    "OBJECTS"          : "4000",
+    "ROOMS"            : "18000",
+    "CLASSES"          : "10",
+    "LEVELS"           : "200",
+    "RACES"            : "7",
+    "SKILLS"           : "140",
+    "MULTICLASSING"    : "0",
+    "PLAYERKILLING"    : "Restricted",
+    "NEWBIE FRIENDLY"  : "1",
+    "ROLEPLAYING"      : "Accepted",
+    "WORLD ORIGINALITY": "Mostly Original",
+    "MINIMUM AGE"      : "6",
+    "SSL"              : "4712",
+    "STATUS"           : "Live",
+    "STATUS-NOTES"     : "live and running",
+    "PORT-NOTES"       : "player ports are 23 and 4711, SSL port is 4712",
+    "QUEST-NOTES"      : "We have about 200 Quests and MiniQuests and "
+                         "they play an important role in gaining "
+                         "levels and skills.",
+    "MULTIPLAYING-NOTES"     : "for combat and quests only 2 characters may be active",
+#else
+    "STATUS"           : "Alpha",
+    "STATUS-NOTES"     : "Homemud for testing puposes",
+#endif
+    "LANGUAGE"         : "German",
+    "LOCATION"         : "Germany",
+    "FAMILY"           : "LPMud",
+    "GENRE"            : "Fantasy",
+    "SUBGENRE"         : "None",
+    "GAMEPLAY"         : "Adventure",
+    "GAMESYSTEM"       : "Custom",
+    "INTERMUD"         : "IM2-Zebedee",
+    "HELPFILES"        : "N/A",
+    "MULTIPLAYING"     : "Restricted",
+    "EQUIPMENT SYSTEM" : "Both",
+    "TRAINING SYSTEM"  : "Both",
+    "QUEST SYSTEM"     : "Integrated",
+    "ZMP"              : "0",
+    "ANSI"             : "1",
+    "GMCP"             : "1",
+    "MCCP"             : "0",
+    "MCP"              : "0",
+    "MSP"              : "0",
+    "MXP"              : "0",
+    "PUEBLO"           : "0",
+    "VT100"            : "1",
+    "XTERM 256 COLORS" : "0",
+    "PAY TO PLAY"      : "0",
+    "PAY FOR PERKS"    : "0",
+    "HIRING BUILDERS"  : "1",
+    "HIRING CODERS"    : "1",
+    "CODEBASE-NOTES"         : "download daily snapshot of our public base "
+                               "mudlib at ftp://mg.mud.de/Software/MudLib/, "
+                               "get our driver at http://www.ldmud.eu/",
+    "FAMILY-NOTES"           : "Descendant of Nightfall, base for several "
+                               "german MUDs, uses LDMud-3.5.x", 
+    "HELPFILES-NOTES"        : "each basic command, and hundreds of other docs",
+    "MOBILES-NOTES"          : "npc's can be cloned, so there can be thousands",
+    "OBJECTS-NOTES"          : "objects can be cloned, so there can be thousands",
+    "RESETS-NOTES"           : "doesn't apply, LPMud",
+    "MUDPROGS-NOTES"         : "doesn't apply, LPMud",
+    "MUDTRIGS-NOTES"         : "doesn't apply, LPMud",
+    "RACES-NOTES"            : "some additional for non player characters",
+    "SKILLS-NOTES"           : "each class/guild and race has its own set of skills",
+    "PLAYERKILLING-NOTES"    : "playerkilling is limited to specific rooms",
+/*
+    "ADULT MATERIAL"   : "0",
+    "PLAYER CITIES"    : "0",
+    "PLAYER CLANS"     : "0",
+    "PLAYER CRAFTING"  : "0",
+    "PLAYER GUILDS"    : "0",
+    "ROOMS-NOTES"            : "areas have generated rooms, could be millions",
+ */
+    ]);
+
+
+// cache fuer die telnetneg-basierte variante
+private nosave string tn_result_min = convert_tn(mindata);
+private nosave string tn_result = convert_tn(data);
+
+
+// converts into the array to be sent via telnet suboption negotiation.
+public string convert_tn(mapping ldata) {
+  string res="";
+  foreach(string key, string value: ldata) {
+    res += sprintf("%c%s%c%s", MSSP_VAR, key, MSSP_VAL, value);
+  }
+  return res;
+}
+
+
+public void print_mssp_response() {
+ string ip = query_ip_number(previous_object());
+ mapping ldata;
+ if (stringp(ip)) {
+     // Vollen Datensatz alle ("CRAWL DELAY" / 2) h, daher * 1800.
+   if (peers[ip] > (time() - (to_int(data["CRAWL DELAY"]) || 1) * 1800)) {
+       // this peers asks to often and gets only the minimal dataset
+       ldata = mindata;
+   }
+   else {
+       ldata = data;
+       peers[ip] = time(); // record timestamp
+   }
+ }
+ else
+     ldata = data;
+
+ /* data["WHO"] = implode(map(filter(users(), 
+                      function status (object o)
+                      { return !o->QueryProp(P_INVIS); } ),
+                    function string (object o)
+                    { return capitalize(o->query_real_name()); } ),
+                  "\t" );
+  */
+  ldata["PLAYERS"] = to_string(sizeof(users())-1);
+
+  string reply = "\r\nMSSP-REPLY-START\r\n";
+
+  foreach(string key, string value: ldata) {
+    reply += key + "\t" + value + "\r\n";
+  }
+
+  reply += "MSSP-REPLY-END\r\n";
+  write(reply);
+}
+
+public string get_telnegs_str() {
+  string ip = query_ip_number(previous_object());
+  string res;
+
+  if (stringp(ip)) {
+    // Vollen Datensatz alle ("CRAWL DELAY" / 2) h, daher * 1800.
+    if (peers[ip] > (time() - (to_int(data["CRAWL DELAY"]) || 1) * 1800)) {
+       // this peers asks to often and gets only the minimal dataset
+       res = tn_result_min;
+    }
+    else {
+        res = tn_result;
+        peers[ip] = time(); // record timestamp
+    }
+  }
+  else
+    res = tn_result;
+
+  res += convert_tn( (["PLAYERS": to_string(sizeof(users())-1) ]) );
+   
+  return res;
+}
+
diff --git a/secure/misc/twitter.c b/secure/misc/twitter.c
new file mode 100644
index 0000000..f34519e
--- /dev/null
+++ b/secure/misc/twitter.c
@@ -0,0 +1,131 @@
+/* rumatas twitter client, sendet text
+ * an einen twitterproxyprozess, der sie dann an twitter
+ * weiterschickt.
+ *
+ * version 2
+ * mit persistenter verbindung zum proxy und input-channel
+ *
+ * 25.5.2016 rumata@mg.mud.de
+ */
+
+#include <wizlevels.h>
+#include <daemon/channel.h>
+
+#define HOST "127.0.0.1"
+#define PORT 4911
+
+#define DEBUG(x) tell_object(find_player("rumata"),x+"\n")
+
+void create() {
+	if( clonep() ) return;
+	seteuid(getuid());
+}
+
+object caller;
+string msgbuf;
+
+mapping registered = ([
+	"rumata": "ru",
+	"arathorn": "ara",
+	"ark": "ark",
+	"zesstra": "ze",
+	"zook": "zo",
+	"humni": "hu",
+	"miril": "mi",
+	"gloinson": "gl"
+]);
+
+int allowed() {
+	return IS_ARCH(this_player()) || getuid(this_player())=="gloinson";
+}
+
+string sig( object pl ) {
+	string uid = getuid(pl);
+	if( !uid ) return "??";
+	string abbr = registered[uid];
+	if( !abbr ) return uid;
+	return abbr;
+}
+
+void emit( string msg ) {
+	CHMASTER->send("twitter",this_object(),msg,MSG_SAY);
+	/*
+	foreach( string uid: m_indices(registered) ) {
+		object pl = find_player(uid);
+		if( pl ) tell_object(pl,msg);
+	}*/
+}
+
+/* Offizielle API funktion
+ * xeval "/secure/misc/twitter"->twitter("@_zesstra_ welcome back")
+ */
+void twitter(string msg) {
+	int err = 0;
+	if( !allowed() ) {
+		write( "Twitter ist ARCH+Gloinson only.\n" );
+		return;
+	}
+	msg = msg + "^" + sig(this_player()) + "\n";
+	if(interactive(this_object())) {
+		tell_object(this_object(),msg);
+	} else {
+		msgbuf = msg;
+		caller = this_player();
+		if( (err=net_connect(HOST,PORT))!=0 ) {
+			write( "Konnte Tweet nicht senden. err="+err+"\n" );
+		}
+	}
+}
+
+// sonderfunktion fuer den fall, dass man die verbindung
+// aufbauen will, ohne dass etwas auf twitter erscheint
+void connect() {
+	int err = 0;
+	if( interactive(this_object()) ) return;
+	msgbuf = "";
+	caller = this_player();
+	if( (err=net_connect(HOST,PORT))!=0 ) {
+		write( "Konnte Tweet nicht connected. err="+err+"\n" );
+	}
+}
+
+varargs void logon(int flag) {
+	if( flag<0 ) {
+		tell_object(caller,"Twitterproxy antwortet nicht?\n");
+  } else {
+		enable_commands();
+		add_action("input","",1);
+		write(msgbuf);
+		if( msgbuf!="" ) emit( msgbuf );
+		//"/secure/master"->disconnect(this_object());
+	}
+}
+
+void NetDead() {
+	disable_commands();
+}
+
+string query_real_name() {
+	return "(Twitter)";
+}
+
+varargs string Name( int casus, int demon ) {
+	return "Twitter";
+}
+
+// daten werden via tell_object an dieses objekt gesendet
+// und dann nach aussen weitergeben
+void catch_tell( string msg ) {
+	if( allowed() && interactive(this_object()) ) {
+		write( msg );
+	}
+}
+
+// hier landen daten von aussen
+int input( string s ) {
+	string msg = query_verb();
+	if( s ) msg = msg + " " + s;
+	emit( msg ); //"Twitter teilt dir mit: "+msg+"\n" );
+	return 1;
+}
+
diff --git a/secure/news.c b/secure/news.c
new file mode 100644
index 0000000..751c887
--- /dev/null
+++ b/secure/news.c
@@ -0,0 +1,566 @@
+// 18.Dez  1996 - Loco@Morgengrauen
+// 8. Feb  1995 - Jof@MorgenGrauen
+// 5. Juli 1992 - Jof@MorgenGrauen
+// 6. Juli 1992 - Jof@MorgenGrauen
+// Clear-Groups-Mechanismus eingebaut. Die 2 Konstanten TIME_TO_CLEAR und
+// MIN_TOUCHED muessen def. sein. Alle TIME_TO_CLEAR Sekunden werden die
+// Newsgroups, die seit dem letzten Clear seltener als MIN_TOUCHED Sekunden
+// beruehrt wurden, aus dem Cache genommen
+
+// 1. Februar 1995 - Jof@MorgenGrauen
+// Rewrite (Mappings benutzen, Accessinfos im Speicher halten)
+
+
+// Datenstrukturen:
+
+// Newsgroups:  grouplist ist ein Mapping mit einem Eintrag pro Newsgroup.
+//              Die Keys sind die Gruppennamen, Daten:
+//              0 Zeit des letzen Artikels
+//              1 Besitzer der Gruppe
+//              2 Savefille-Name
+//              3 Expire-Zeit
+//              4 Array mit Loesch-Berechtigten
+//              5 Array mit Scheib-Berechtigten
+//              6 Array mit Leseberechtigten
+//              7 Mindest-Level, um Artikel zu loeschen
+//              8 Mindest-Level, um Artikel zu posten
+//              9 Mindest-Level, um Artikel zu lesen
+//             10 Max. Anzahl Nachrichten in der Gruppe
+
+// Die Nachrichten selber stehen in einem Array.
+
+// Eine nachricht ist ein Array mit den folgenden Elementen:
+// 0         (*) string writer;
+// 1         (*) string id;    Mudname+":"+time()+":"+group zur Zeit
+// 2         (*) string time;
+// 3             string title;
+// 4             string message;
+
+// Die mit (*) gekennzeichneten Eintraege setzt der Daemon selber
+
+// Funktionen:
+//    Returnvalues:  0 = Parameter error
+//                   1 = Ok.
+//                  -1 = no permission
+//                  -2 = no such group/group already existing
+//
+//    Diese Returnvalues stehen fuer alle Funktionen
+
+//  AddGroup(groupname, owner, savefile);
+//   Funktion klar, kann nur von Erzmagiern aufgerufen werden
+//    -3 = no owner, -4 = savefile already in use
+//
+//  RemoveGroup(groupname);
+//   Ebenfalls nur fuer Erzmagier
+//
+//  SetGroup(groupname, dlevel, wlevel, rlevel, maxmessages, expire);
+//   Erzmagier und der Groupowner koennen die nutzen. Legt Level, ab dem je-
+//   mand aus der Gruppe loeschen kann (dlevel), in die Gruppe schreiben
+//   kann (wlevel), die Gruppe lesen kann (rlevel) und die max. Anzahl Nach-
+//   richten in der Gruppe fest.
+//
+//  AddAllowed(groupname, deleters, writers, readers);
+//   Erzmagier/Owner koennen Arrays mit Namen von fuer die Gruppe loesch/
+//   schreib/lese-Berechtigten fuer die Gruppe angeben.
+//
+//  RemoveAllowed(groupname, deleters, writers, readers);  analog
+//
+//  WriteNote(message); klar
+//   -3 = Max number of msgs exceeded
+//
+//  RemoveNote(boardname, notenummer); notenummer>=0;
+//   -3 = No such note
+//
+//  GetNotes(boardname); gibt einen Array mit den Notes zurueck.
+//
+//  AskAllowedWrite(); return wie bei WriteNote, stellt fest, ob ein Player
+//  eine Note aufhaengen darf oder nicht
+//
+//  GetNewsTime([boardname]); gibt zurueck, wann am entsprechenden Brett zum
+//  letzten Mal eine Note befestigt wurde. Falls kein boardname angegeben
+//  wird, liefert GetNewsTime() den Zeitpunkt, zu dem ueberhaupt eine neue
+//  Note aufgehaengt wurde.
+//
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#include "/secure/wizlevels.h"
+#include <defines.h>
+#include <config.h>
+#include <news.h>
+
+#define WTIME 0
+
+private int security( string name );
+
+mixed saveload; // Diese Variable ist als einzige nicht nosave ist und dient
+                 // Uebertragen von Daten in/aus savefiles
+
+nosave mapping grouplist; // Groups und ihre save-Files, zudem LastTime
+nosave int lasttime; // Um zu vermeiden, dass 2 Notes identische Uhrzeit==id
+                     // haben, wird die jeweils letzte Zeit gespeichert.
+
+nosave mapping cache = ([]); // cache fuer die Gruppeninhalte
+
+void create() {
+  seteuid(getuid(this_object()));
+  if (!restore_object(NEWSPATH+"GroupList"))
+    grouplist=m_allocate(0,G_MESSAGES);
+  else
+    grouplist=saveload;
+  // ersten reset sobald wie moeglich. ;-)
+  set_next_reset(1);
+}
+
+int AddGroup(string name, string owner)
+{
+  mixed *group;
+  string savefile, *savefilea;
+  int i;
+
+  if (!name || !owner) return 0;
+  
+  if (!ARCH_SECURITY || process_call()) return -1; // Darf nicht
+
+  if (member(grouplist, name)) return -2; // Gibt es schon
+
+  if (file_size("/"+SAVEPATH+owner[0..0]+"/"+owner+".o")<0) return -3;
+
+  savefilea = old_explode(name,".");
+  savefile = implode(savefilea,"/");
+  if (file_size(NEWSPATH+savefile+".o")>=0) return -4;
+
+  // Notwendige Directories anlegen
+  for (i = 0; i < sizeof(savefilea)-1; i++) {
+    mkdir(NEWSPATH+implode(savefilea[0..i],"/"));
+  }
+
+  group=({});
+  grouplist+=([name:0;owner;savefile;-1;({});({});({});20;0;0;80]);
+  save_group_list();
+  save_group(name,group);
+  return 1;
+}
+
+int RemoveGroup(string name)
+{
+  int num;
+
+  if (!name) return 0;
+
+  if (!security(name) || process_call()) return -1; // Darf nicht
+
+  if (!mappingp(grouplist) || !member(grouplist,name))
+    return -2; // -2 no such group
+
+  catch(rm(NEWSPATH+grouplist[name,G_SAVEFILE]+".o");publish);
+  m_delete(grouplist,name);
+
+  save_group_list();
+
+  return 1;
+}
+
+int SetGroup(string name,int dlevel,int        wlevel,int rlevel,int maxmessages,int expire)
+{
+  mixed *group;
+  
+  if (!member(grouplist,name)) return -2;
+  if (grouplist[name,G_OWNER]!=user_euid() &&
+      (!security(name) || process_call())) return -1;
+  
+  grouplist[name,G_DLEVEL]=dlevel;
+  grouplist[name,G_WLEVEL]=wlevel;
+  grouplist[name,G_RLEVEL]=rlevel;
+  grouplist[name,G_MAX_MSG]=maxmessages;
+  grouplist[name,G_EXPIRE]=expire;
+  
+  save_group_list();
+  return 1;
+}
+
+int AddAllowed(string name,mixed deleters,mixed writers,mixed readers)
+{
+  mixed *group;
+
+  if (!member(grouplist,name)) return -2;
+
+  if ( grouplist[name,G_OWNER]!=user_euid() &&
+       (!security(name) || process_call()) && user_euid() != ROOTID )
+      return -1;
+
+  if (stringp(deleters)) deleters=({deleters});
+  if (stringp(writers)) writers=({writers});
+  if (stringp(readers)) readers=({readers});
+
+  if (!deleters) deleters=({});
+  if (!writers) writers=({});
+  if (!readers) readers=({});
+
+  grouplist[name,G_DELETERS]+=deleters;
+  grouplist[name,G_WRITERS]+=writers;
+  grouplist[name,G_READERS]+=readers;
+        
+  save_group_list();
+  return 1;
+}
+
+int RemoveAllowed(string name,mixed deleters,mixed writers,mixed readers)
+{
+  mixed *group;
+
+  if (!member(grouplist,name)) return -2;
+
+  if (grouplist[name,G_OWNER]!=user_euid() &&
+      (!security(name) || process_call()) && user_euid() != ROOTID )
+      return -1;
+
+  if (stringp(deleters)) deleters=({deleters});
+  if (stringp(writers)) writers=({writers});
+  if (stringp(readers)) readers=({readers});
+
+  if (!deleters) deleters=({});
+  if (!writers) writers=({});
+  if (!readers) readers=({});
+
+  grouplist[name,G_DELETERS]-=deleters;
+  grouplist[name,G_WRITERS]-=writers;
+  grouplist[name,G_READERS]-=readers;
+
+  save_group_list();
+  return 1;
+}
+
+static string user_euid()
+{
+  if (previous_object()) {
+     if (geteuid(previous_object())==ROOTID)       return ROOTID;
+     if (geteuid(previous_object())=="p.daemon")   return "p.daemon";
+     if (load_name(previous_object())=="/obj/mpa") return geteuid(RPL);
+  }
+  return secure_euid();
+}
+
+#define F_DELETE    0
+#define F_READ      1
+#define F_WRITE     2
+#define F_ADMIN     3
+#define F_KEEPNAME  4
+
+private int security( string name )
+{
+    if ( grouplist[name,G_DLEVEL] >= ARCH_LVL
+         || grouplist[name,G_WLEVEL] >= ARCH_LVL
+         || grouplist[name,G_RLEVEL] >= ARCH_LVL )
+        return ARCH_SECURITY;
+    else
+        return ELDER_SECURITY;
+}
+
+static int allowed(string name, int mode)
+{
+  string euid;
+  mixed g_level, g_mode;
+  
+  if (process_call()) return 0;
+
+  euid=user_euid();
+
+  if (euid==ROOTID) return 1;
+
+  switch(mode) {
+    case F_KEEPNAME: return (euid=="p.daemon");
+    case F_WRITE:    if (euid=="p.daemon") return 1;
+                     g_level=G_WLEVEL; g_mode=G_WRITERS;  break;
+    case F_ADMIN:    if (!(security(name)||grouplist[name,G_OWNER]==euid)) 
+                             return 0;
+                     g_level=G_DLEVEL; g_mode=G_DELETERS; break;
+    case F_DELETE:   if (euid=="p.daemon") return 1;
+                     g_level=G_DLEVEL; g_mode=G_DELETERS; break;
+    case F_READ:     g_level=G_RLEVEL; g_mode=G_READERS;  break;
+    default:         return 0;
+  }
+
+  if (grouplist[name,G_OWNER] != euid && !ARCH_SECURITY &&
+      grouplist[name,g_level] > query_wiz_level(euid) &&
+      member(grouplist[name, g_mode], euid)==-1)
+    return 0; // No such group for the requestor :)
+  return 1;
+}
+
+int WriteNote(mixed message,mixed keepname)
+{
+  mixed *group;
+  int uidok,tmp;
+  string name;
+
+  if (!pointerp(message) || sizeof(message)!=6) return 0;
+
+  if (!pointerp(group=load_group(name=message[M_BOARD]))) return -2;
+
+  if (!allowed(name, F_WRITE)) return -1;
+
+  if (sizeof(group)>=grouplist[name,G_MAX_MSG]) return -3;
+
+  if (!keepname || !allowed(name, F_KEEPNAME))
+     message[M_WRITER]=capitalize(geteuid(this_interactive()||previous_object()));
+
+  if (lasttime>=time()) lasttime++;
+    else lasttime=time();
+  message[M_TIME]=lasttime;
+  message[M_ID]=MUDNAME+":"+lasttime;
+  group+=({message});
+  grouplist[name,WTIME]=lasttime;
+  save_group(name,group);
+  save_group_list();
+  return 1;
+}
+
+int RemoveNote(string name, int note)
+{
+  int num;
+  mixed *group;
+
+  if ((note<0) && (name=="dwnews"))
+  {
+    group=({});
+    grouplist[name,WTIME]=0;
+    save_group(name,group);
+    save_group_list();
+    return 1;
+  }
+
+  if (note<0) return 0;
+
+  if (!pointerp(group=load_group(name))) return -2;
+
+  int count=sizeof(group);
+  if (count<=note)
+    return -3;
+
+  if (!allowed(name, F_DELETE) &&
+      lower_case(group[note][M_WRITER])!=user_euid()) return -1;
+
+  if (count==1)
+    group=({});
+  else if (!note)
+    group = group[1..];
+  else if (note == count-1)
+    group = group[0..<2];
+  else
+    group=group[0..note-1]+group[note+1..];
+  
+  if (sizeof(group))
+    grouplist[name,WTIME]=group[<1][M_TIME];
+  else
+    grouplist[name,WTIME]=0;
+  save_group(name,group);
+  save_group_list();
+  return 1;
+}
+
+mixed GetNotes(string name)
+{
+  mixed *group;
+  
+  if (!pointerp(group=load_group(name))) return -2;
+  if (!allowed(name, F_READ)) return -2;
+  return(deep_copy(group)); // COPY it
+}
+
+static void dump_file(string filename,mixed news)
+{
+  int i;
+  
+  for (i=0;i<sizeof(news);i++)
+    write_file(filename,news[i][M_TITLE]+" ("+news[i][M_WRITER]+", "+
+               dtime(news[i][M_TIME])[5..26]+"):\n"+
+               news[i][M_MESSAGE]+"\n-----------------------------------------------------------------------------\n\n\n\n");
+}
+
+protected varargs void expire(string grp,int etime)
+// etime ist anfangs in Tagen und bezeichnet das max. Alter, was Artikel in
+// der Gruppe haben duerfen.
+{
+  mixed *group;
+
+  if (!pointerp(group=load_group(grp))) return;
+  if (etime)
+  {
+    if (etime>0)
+      etime=etime*60*60*24;
+  }
+  else
+    etime=grouplist[grp,G_EXPIRE]; 
+  if (etime<=0)
+    return;
+
+  int to_expire=time()-etime;
+  int size=sizeof(group);
+  if (!size) return;
+
+  int first_to_keep = size;  // ja, ist noch eins zu hoch
+  // solange runterlaufen bis man ein element findet, welches geloescht werden
+  // soll. first_to_keep bleibt dann eins hoeher als das.
+  while ( first_to_keep && group[first_to_keep-1][M_TIME]>to_expire)
+    --first_to_keep;
+  // first_to_keep kann jetzt auf eins hinter dem letzten Element zeigen (==
+  // size). Das wird unten beruecksichtigt.
+
+  if (!first_to_keep) // alle behalten?
+    return;
+  // Zu loeschende Artikel wegschreiben.
+  dump_file("news/OLD."+grp,group[0..first_to_keep-1]);
+  // dann loeschen
+  if (first_to_keep == size) // alle wegwerfen?
+    group=({});
+  else
+    group=group[first_to_keep..size-1];
+  
+  save_group(grp,group);
+}
+
+void dump_group(string grp)
+{
+  int to_expire,size,last;
+  mixed *group;
+
+  if (!ARCH_SECURITY || process_call()) return;
+  if (!pointerp(group=load_group(grp))) return;
+  size=sizeof(group);
+  last=size;
+  if (!last) return;
+  dump_file("news/DUMP."+grp,group[0..last-1]);
+}
+
+protected void expire_all(string *keys) {
+  // neuen call_out fuer den Rest setzen
+  if (sizeof(keys) > 1)
+    call_out(#'expire_all,15,keys[1..]);
+  // und erste Gruppe expiren
+  expire(keys[0]);
+}
+
+void reset() {
+  // naechstes Expire und damit Reset in einem tag
+  set_next_reset(86400);
+  // alte call_outs ggf. entfernen.
+  while(remove_call_out(#'expire_all)>=0);
+  // gruppenliste holen und callout auf expire_all starten
+  if (sizeof(grouplist)) {
+    call_out(#'expire_all,10,m_indices(grouplist));
+  }
+}
+
+static void save_group(string grp,mixed group)
+{
+  saveload=group; // Do NOT save the accessed-Info
+  cache[grp] = group;
+  save_object(NEWSPATH+grouplist[grp,G_SAVEFILE]);
+  saveload=0;
+}
+
+static void save_group_list()
+{
+  saveload=grouplist;
+  save_object(NEWSPATH+"GroupList");
+  saveload=0;
+}
+
+static mixed load_group(string name)
+{
+  int num;
+  mixed *ret;
+
+  if(!member(grouplist,name)) return -1;
+
+  if (member(cache, name)) {
+    ret = cache[name];
+  }
+  else {
+    restore_object(NEWSPATH+grouplist[name,G_SAVEFILE]);
+    if (!pointerp(saveload))
+      saveload=({});
+    ret=saveload;
+    cache[name] = saveload;
+    saveload=0;
+  }
+  return ret;
+}
+
+mixed GetGroups()
+{
+  mixed *returnlist;
+  int i,group,slevel;
+  string seuid;
+
+  returnlist=sort_array(m_indices(grouplist),#'>); //');
+  if (ARCH_SECURITY && !process_call())
+    return returnlist;
+
+  seuid = user_euid();
+  slevel = secure_level();
+
+  for (i=sizeof(returnlist)-1;i>=0;i--)
+    if (!(grouplist[returnlist[i],G_RLEVEL]<=slevel ||
+          grouplist[returnlist[i],G_OWNER]==seuid ||
+          member(grouplist[returnlist[i],G_READERS], seuid)!=-1))
+      returnlist=returnlist[0..i-1]+returnlist[i+1..];
+  return returnlist;
+}
+
+int AskAllowedWrite(string n)
+{
+  mixed *group;
+
+  if (!member(grouplist,n)) return -2;
+  if (!pointerp(group=load_group(n))) return -2;
+
+  if (grouplist[n,G_OWNER] != user_euid() &&
+      !ARCH_SECURITY &&
+      grouplist[n,G_WLEVEL]>secure_level() &&
+      member(grouplist[n,G_WRITERS],user_euid())==-1)
+    return -1;
+
+  if (sizeof(group)>=grouplist[n,G_MAX_MSG]) return -3;
+  return 1;
+}
+
+// Wichtig ...
+
+int query_prevent_shadow()
+{
+  return 1;
+}
+
+mixed GetNewsTime(string boardname)
+
+{
+  int i, ltime, j;
+  mixed *keys;
+
+  if (!boardname)
+  {
+    ltime=-1;
+    for (i=sizeof(keys=m_indices(grouplist))-1;i>=0;i--)
+      if (ltime<(j=grouplist[keys[i],WTIME])) ltime=j;
+    return ltime;
+  }
+  if (!member(grouplist,boardname)) return -1;
+  return grouplist[boardname,WTIME];
+}
+
+mixed* GetGroup(string name)
+{
+  if (process_call()) return 0;
+  if (extern_call() && !allowed(name, F_ADMIN)) return 0;
+#define gl(x) grouplist[name,x]
+  return ({name,gl(1),gl(2),gl(3),gl(4),gl(5),gl(6),gl(7),gl(8),gl(9),gl(10),load_group(name)});
+}
diff --git a/secure/npcmaster.c b/secure/npcmaster.c
new file mode 100644
index 0000000..33d8bdc
--- /dev/null
+++ b/secure/npcmaster.c
@@ -0,0 +1,124 @@
+// MorgenGrauen MUDlib
+//
+// npcmaster.c - Verwaltung der eindeutigen Nummernvergabe fuer NPCs und
+//               der Stufenpunkte, die sie geben
+//
+// $Id: npcmaster.c 9142 2015-02-04 22:17:29Z Zesstra $
+//
+/*
+ * $Log: npcmaster.c,v $
+ * Revision 1.6  1996/09/18 08:59:49  Wargon
+ * Globale Variablen private gemacht...
+ *
+ * Revision 1.5  1996/04/10 17:48:37  Jof
+ * Dump-Funktion eingebaut, damit man auch was zu lesen hat :)
+ * ,
+ *
+ * Revision 1.4  1996/04/10 14:32:24  Wargon
+ * QueryNPC(): int * -> mixed
+ *
+ * Revision 1.3  1996/04/04 12:33:06  Wargon
+ * QueryNPCbyNumber() gibt jetzt wirklich Nummer und Score zurueck...
+ *
+ * Revision 1.2  1996/03/06 14:34:51  Jof
+ * QueryNPCbyNumber eingebaut, Typos gefixed
+ *
+ * Revision 1.1  1996/03/06 13:45:36  Jof
+ * Initial revision
+ *
+ */
+
+#include "/secure/npcmaster.h"
+#include "/secure/wizlevels.h"
+
+private int lastNum;
+private mapping npcs;
+private nosave mapping by_num;
+
+void make_num(string key) {
+  by_num += ([ npcs[key,NPC_NUMBER] : key; npcs[key,NPC_SCORE] ]);
+}
+
+void create()
+{
+  seteuid(getuid());
+  if (!restore_object(NPCSAVEFILE))
+  {
+    lastNum=0;
+    npcs=m_allocate(0,2);
+  }
+  by_num = m_allocate(0,2);
+  filter_indices(npcs, #'make_num/*'*/);
+}
+
+static int DumpNPCs();
+
+varargs mixed QueryNPC(int score)
+{
+  string key, val;
+  
+  if (!previous_object())
+    return NPC_INVALID_ARG;
+  key=old_explode(object_name(previous_object()),"#")[0];
+  if (val=npcs[key,NPC_NUMBER])
+    return ({val,npcs[key,NPC_SCORE]});
+  if (score<=0 || member(inherit_list(previous_object()),"/std/living/life.c") < 0)
+    return NPC_INVALID_ARG;
+  npcs[key,NPC_SCORE]=score;
+  npcs[key,NPC_NUMBER]=++lastNum;
+  by_num += ([lastNum:key;score]);
+   save_object(NPCSAVEFILE);
+  call_out("DumpNPCs",0);
+  return ({lastNum,score});
+}
+
+int SetScore(string key,int score)
+{
+  if (!IS_ARCH(this_interactive())||!IS_ARCH(geteuid(previous_object())))
+    return NPC_NO_PERMISSION;
+  if (!npcs[key,NPC_NUMBER])
+    return NPC_INVALID_ARG;
+  npcs[key,NPC_SCORE]=score;
+  by_num += ([npcs[key,NPC_NUMBER]:key;score]);
+  save_object(NPCSAVEFILE);
+  DumpNPCs();
+  return 1;
+}
+
+static int DumpNPCs()
+{
+  mixed *keys;
+  int i, max;
+
+  keys=m_indices(npcs);
+  rm(NPCDUMPFILE);
+  for (i=sizeof(keys)-1, max=0;i>=0;i--) {
+    write_file(NPCDUMPFILE,sprintf("%5d %4d %s\n",npcs[keys[i],NPC_NUMBER],
+	   npcs[keys[i],NPC_SCORE],keys[i]));
+    max += npcs[keys[i],NPC_SCORE];
+  } 
+  write_file(NPCDUMPFILE,sprintf("====================\nGesamt: %d Punkte\n",max)); 
+  return 1;
+}
+
+mixed *QueryNPCbyNumber(int num)
+{
+  if (by_num[num])
+    return ({num,by_num[num,NPC_SCORE],by_num[num,NPC_NUMBER]});
+
+  return 0;
+}
+
+int Recalculate(string s)
+{
+  int i, j;
+
+  if (!s || !sizeof(s))
+    return 0;
+
+  for (j=0, i=6*sizeof(s)-1; i>0; i--) {
+    if (test_bit(s,i))
+      j+=by_num[i,NPC_SCORE];
+  }
+  return j;
+}
diff --git a/secure/npcmaster.h b/secure/npcmaster.h
new file mode 100644
index 0000000..8af7f41
--- /dev/null
+++ b/secure/npcmaster.h
@@ -0,0 +1,24 @@
+// MorgenGrauen MUDlib
+//
+// FileName.c -- Beschreibung
+// $Id: npcmaster.h 2634 2006-09-23 09:43:07Z root $
+//
+/*
+ * $Log: npcmaster.h,v $
+ * Revision 1.1  1996/03/06 13:45:11  Jof
+ * Initial revision
+ *
+ */
+
+#ifndef _NPCMASTER_H_
+#define _NPCMASTER_H_ 1
+
+#define NPCSAVEFILE "/etc/npcmaster"
+
+#define NPC_NUMBER	0
+#define NPC_SCORE	1
+
+#define NPC_INVALID_ARG -1
+#define NPC_NO_PERMISSION -2
+
+#endif
diff --git a/secure/potionmaster.c b/secure/potionmaster.c
new file mode 100644
index 0000000..78a0565
--- /dev/null
+++ b/secure/potionmaster.c
@@ -0,0 +1,515 @@
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+#pragma rtt_checks
+#pragma pedantic
+#pragma warn_deprecated
+
+#include "/secure/config.h"
+#include "/secure/wizlevels.h"
+
+#define MAX_ROOMS_PER_LIST 10
+
+// die div. Speicherorte und Pfade
+#define SAVEFILE    "/secure/ARCH/potions"
+#define TIPS(x)     "/secure/ARCH/ZT/"+x
+#define ORAKEL      "/room/orakel"
+#define POTIONTOOL  "/obj/tools/ptool"
+
+// Fuer die Dump-Funktion
+#define POTIONDUMP "/secure/ARCH/POTIONS.dump"
+#define DUMP(str)   write_file(POTIONDUMP, str)
+
+// Modifikationen loggen. "event" ist eins der Events aus der Liste:
+// ADD_POTION, ACTIVATE, DEACTIVATE, MODIFY_PATH, MODIFY_LISTNO
+#define LOGFILE_MOD     "/log/ARCH/POTIONS_MOD.log"
+#define MODLOG(event,num,data) write_file(LOGFILE_MOD, \
+  sprintf("%17s %-14s %-3d sEUID %s %s\n", \
+    strftime("%Y-%b-%d %R"), event, num, secure_euid(), data) )
+
+// Indizierungs-Konstanten fuer das potions-Mapping
+#define POT_ROOMNAME 0
+#define POT_LISTNO   1
+
+// Konstanten fuer div. Rueckgabewerte. Keine 0, da das eine gueltige ZT-ID
+// sein kann und abfragende Objekte verwirren koennte.
+#define POT_IS_ACTIVE            1
+#define POT_ACCESS_DENIED       -1
+#define POT_WRONG_DATATYPE      -2
+#define POT_NO_SUCH_ROOM        -3
+#define POT_ALREADY_REGISTERED  -4
+#define POT_INVALID_POTION      -5
+#define POT_NO_SUCH_FILE        -6
+#define POT_INVALID_LIST_NUMBER -7
+#define POT_ALREADY_ACTIVE      -8
+#define POT_ALREADY_INACTIVE    -9
+#define POT_NOT_INACTIVE       -10
+#define POT_NOT_ACTIVE         -11
+
+// Zaehler fuer den als naechsten anzulegenden ZT
+private int nextroom;
+
+// Liste aller ZTs einschl. inaktive, ([ int num : string room; int list ])
+private mapping potions = ([]);
+
+// Liste der inaktiven ZTs, ({ int num })
+private int *inactive = ({});
+
+// Cache mit den einzelnen Listen, ([ int list : int *potionlist ])
+private nosave mapping lists;
+
+// reverse_table Lookup Cache, ([string room: int number])
+private nosave mapping reverse_table = ([]);
+
+// Cache fuer die bereits von der Platte eingelesenen ZTs, um Plattenzugriffe
+// insbesondere beim Erzeugen der Tipliste durch das Orakel zu reduzieren.
+private nosave mapping tipmap = ([]);
+
+int ActivateRoom(string room);
+
+private int secure() {
+  return (!process_call() && ARCH_SECURITY);
+}
+
+mixed QueryPotionData(int num) {
+  if ( !secure() )
+    return POT_ACCESS_DENIED;
+  return ([num : potions[num,0]; potions[num,1] ]);
+}
+
+int *QueryInactivePotions() {
+  return copy(inactive);
+}
+
+private void RebuildCache() {
+  // Cache invalidieren; vor-initialisiert zur Beschleunigung des Rebuilds.
+  lists = ([0:({}),1:({}),2:({}),3:({}),4:({}),5:({}),6:({}),7:({})]);
+  foreach (int num, string room, int list : potions) {
+    reverse_table += ([room:num]);
+    lists[list] += ({num});
+  }
+  return;
+}
+
+int QueryActive(mixed potion) {
+  if ( extern_call() && !secure() )
+    return POT_ACCESS_DENIED;
+  int ret;
+  // Wenn nach dem Pfad des ZTs gefragt wird, diesen zuerst in die Nummer
+  // umwandeln durch Lookup im reverse_table Cache
+  if ( stringp(potion) ) {
+    potion = reverse_table[potion];
+  }
+  // Ein ZT ist aktiv, wenn er in der Liste potions steht und nicht in der
+  // Liste der inaktiven ZTs (inactive) steht. Dann dessen Nummer
+  // zurueckgeben; inaktive zuerst pruefen, weil die inaktiven auch in 
+  // "potions" enthalten sind und somit alle ZTs ausser den ungueltigen
+  // als aktiv gemeldet wuerden.
+  if ( member(inactive,potion)>-1 )
+    ret = POT_NOT_ACTIVE;
+  else if ( member(potions,potion) && potions[potion,POT_LISTNO]!=-1 )
+    ret = potion;
+  // ansonsten ist das kein gueltiger ZT
+  else
+    ret = POT_INVALID_POTION;
+  return ret;
+}
+
+private void save_info() {
+  save_object(SAVEFILE);
+}
+
+protected void create() {
+  seteuid(getuid(this_object()));
+  if ( !restore_object(SAVEFILE) ) {
+    // Fehler ausgeben, damit es jemand merkt. Dennoch wird jetzt im Anschluss
+    // der Cache neu aufgebaut (damit die Datenstrukturen gueltig sind).
+    catch(raise_error("Potionmaster: no/corrupt savefile! Reinitializing.\n"); publish);
+  }
+  RebuildCache();
+}
+
+int AddPotionRoom(string room, int list) {
+  if ( !secure() )
+    return POT_ACCESS_DENIED;
+  // Neuer Raum muss ein gueltiger String sein.
+  if ( !stringp(room) )
+    return POT_WRONG_DATATYPE;
+  if ( !intp(list) || list<0 || list>7 )
+    return POT_WRONG_DATATYPE;
+
+  // Pfad mit Hilfe des Masters vervollstaendigen (+, ~ etc. ersetzen)
+  room=(string)master()->_get_path(room,0);
+
+  // Datei mit dem ZT-Spruch muss existieren.
+  if ( file_size( TIPS(to_string(nextroom)+".zt") ) < 0 ) {
+    raise_error("Potionmaster: Tipfile missing, please create "+
+      to_string(nextroom)+".zt");
+    return POT_NO_SUCH_FILE;
+  }
+  // Neuer Raum darf noch nicht in der Liste enthalten sein.
+  if ( member(m_values(potions,POT_ROOMNAME), room)!=-1)
+    return POT_ALREADY_REGISTERED;
+  // Neuer Raum muss ladbar sein.
+  if ( catch(load_object(room); publish) )
+    return POT_NO_SUCH_ROOM;
+
+  // Jetzt kann's endlich losgehen, Raum eintragen, nextroom hochzaehlen
+  potions += ([nextroom : room; list]);
+  MODLOG("ADD_POTION", nextroom, room);
+  // Neu eingetragene ZTs werden auch gleich aktiviert; ActivateRoom()
+  // baut den Cache selbst neu auf, daher kann das hier entfallen.
+  ActivateRoom(room);
+  nextroom++;
+  save_info();
+  return nextroom;
+}
+
+int ChangeRoomPath(string old, string new) {
+  if ( !secure() )
+    return POT_ACCESS_DENIED;
+
+  // beide Pfade muessen gueltige Strings sein
+  if ( !stringp(old) || !stringp(new) )
+    return POT_WRONG_DATATYPE;
+
+  // Pfad mit Hilfe des Masters vervollstaendigen (+, ~ etc. ersetzen)
+  old=(string)master()->_get_path(old,0);
+  new=(string)master()->_get_path(new,0);
+
+  // Der neue Raum darf nicht bereits eingetragen sein, ...
+  if ( member(reverse_table,new) )
+    return POT_ALREADY_REGISTERED;
+  // ... und der alte Raum muss noch eingetragen sein.
+  if ( !member(reverse_table,old) )
+    return POT_NO_SUCH_ROOM;
+  // Neuer Raum muss ladbar sein.
+  if (catch(load_object(new);publish))
+    return POT_NO_SUCH_ROOM;
+
+  // Aktuelle ZT-Nummer des alten Pfades ermitteln
+  int num = reverse_table[old];
+  // Pfad aendern, Cache neubauen und Savefile speichern
+  potions[num,POT_ROOMNAME] = new;
+  RebuildCache();
+  save_info();
+  MODLOG("MODIFY_PATH", num, old+" => "+new);
+  return num;
+}
+
+int ActivateRoom(string room) {
+  if ( !secure() )
+    return POT_ACCESS_DENIED;
+  // Aktuelle ZT-Nummer ermitteln. Etwas umstaendlich, da im Fehlerfall -1
+  // benoetigt wird.
+  int num = member(reverse_table,room) ? reverse_table[room] : -1;
+  // Nummer muss existieren
+  if ( num == -1 )
+    return POT_INVALID_POTION;
+  // ZT ist nicht inaktiv, dann kann man ihn auch nicht aktivieren.
+  if ( member(inactive, num)==-1 )
+    return POT_ALREADY_ACTIVE;
+  inactive -= ({num});
+  RebuildCache();
+  save_info();
+  MODLOG("ACTIVATE", num, room);
+  return num;
+}
+
+int SetListNr(string room, int list) {
+  if ( !secure() )
+    return POT_ACCESS_DENIED;
+  // Aktuelle ZT-Nummer ermitteln. Etwa umstaendlich, da im Fehlerfall -1
+  // benoetigt wird.
+  int num = member(reverse_table,room) ? reverse_table[room] : -1;
+  // Nummer muss existieren
+  if ( num == -1 )
+    return POT_INVALID_POTION;
+  // Listennummer muss zwischen 0 und 7 liegen.
+  if ( list < 0 || list > 7 )
+    return POT_INVALID_LIST_NUMBER;
+
+  // alte Nummer aufschreiben zum Loggen.
+  int oldlist = potions[num,POT_LISTNO];
+  // Listennummer in der ZT-Liste aktualisieren.
+  potions[num,POT_LISTNO] = list;
+  RebuildCache();
+  save_info();
+  MODLOG("MODIFY_LISTNO", num, oldlist+" -> "+list);
+  return num;
+}
+
+int DeactivateRoom(string room) {
+  if ( !secure() )
+    return POT_ACCESS_DENIED;
+  // Aktuelle ZT-Nummer ermitteln. Etwa umstaendlich, da im Fehlerfall -1
+  // benoetigt wird.
+  int num = member(reverse_table,room) ? reverse_table[room] : -1;
+  // Nummer muss existieren
+  if ( num == -1 )
+    return POT_INVALID_POTION;
+  // ZT darf nicht bereits inaktiv sein
+  if ( member(inactive,num)>-1 )
+    return POT_ALREADY_INACTIVE;
+  inactive += ({num});
+  RebuildCache();
+  save_info();
+  MODLOG("DEACTIVATE", num, room);
+  return num;
+}
+
+private int *_create_list(int listno, int anz) {
+  int *list = ({});
+  // Listenerzeugung lohnt nur dann, wenn mind. 1 Eintrag gefordert wird und
+  // die Listennummer im gueltigen Bereich zwischen 0 und 7 ist.
+  if ( anz>0 && listno>=0 && listno<=7 ) {
+    int *tmp = lists[listno] - inactive;
+    // Wenn die Listengroesse maximal genauso gross ist wie die angeforderte
+    // Anzahl, kann diese vollstaendig uebernommen werden.
+    if ( sizeof(tmp) <= anz ) {
+      list = tmp;
+    } else { // ansonsten soviele Eintraege erzeugen wie angefordert
+      foreach(int i: anz) {
+        int j=random(sizeof(tmp));
+        list += ({tmp[j]});
+        tmp -= ({tmp[j]});
+      }
+    }
+  }
+  return list;
+}
+
+mixed *InitialList() {
+  mixed *list=({});
+  foreach(int i : 8)
+    list+=_create_list(i,MAX_ROOMS_PER_LIST);
+  return list;
+}
+
+// Aufrufe aus den Spielershells und dem Potiontool sind erlaubt
+int HasPotion(object room) {
+  if ( !query_once_interactive(previous_object()) && 
+       load_name(previous_object()) != POTIONTOOL )
+    return POT_ACCESS_DENIED;
+  return objectp(room) ? reverse_table[object_name(room)] : POT_NO_SUCH_ROOM;
+}
+
+// Listennummer ermitteln, in der der ZT num enthalten ist.
+// Auch inaktive ZTs werden als zu einer Liste gehoerig gemeldet.
+int GetListByNumber(int num) {
+  return member(potions,num) ? potions[num,POT_LISTNO] : POT_INVALID_POTION;
+}
+
+// Listennummer ermitteln, in der der inaktive ZT num enthalten ist.
+// Da alle Zaubertraenke in einer Gesamtliste enthalten sind, kann direkt das
+// Resultat von GetListByNumber() zurueckgegeben werden.
+int GetInactListByNumber(int num) {
+  if ( member(inactive,num) > -1 )
+    return GetListByNumber(num);
+  else
+    return POT_NOT_INACTIVE;
+}
+
+// Wird nur von /obj/tools/ptool aufgerufen. Wenn der Aufruf zugelassen wird,
+// erfolgt von dort aus ein ChangeRoomPath(), und dort wird bereits geloggt.
+// Erzmagier duerfen auch aufrufen.
+mixed GetFilenameByNumber(int num) {
+  if ( extern_call() && 
+       object_name(previous_object()) != POTIONTOOL &&
+       !ARCH_SECURITY )
+    return POT_ACCESS_DENIED;
+  if ( !member(potions, num) )
+    return POT_INVALID_POTION;
+  return potions[num,POT_ROOMNAME];
+}
+
+// Wird vom Spielerobjekt gerufen; uebergeben werden der Raum, der initial
+// FindPotion() aufrief, die ZT-Liste des Spielers sowie die Liste bereits
+// gefundener ZTs.
+//
+// In std/player/potion.c ist sichergestellt, dass room ein Objekt ist und
+// dass die Listen als Int-Arrays uebergeben werden.
+//
+// Es werden aus historischen Gruenden (noch) beide ZT-Listen uebergeben,
+// aber es muss nur knownlist ueberprueft werden, da diese eine Teilmenge
+// von potionlist ist und somit jeder ZT in ersterer auf jeden Fall auch
+// in letzterer enthalten sein _muss_.
+int InList(object room, int *potionlist, int *knownlist) {
+  if ( !query_once_interactive(previous_object()) && !secure() )
+    return 0; //POT_ACCESS_DENIED;
+  int num = QueryActive(object_name(room));
+  
+  // Zunaechst keinen Fehlercode zurueckgeben, weil das Spielerobjekt
+  // damit im Moment noch nicht umgehen kann.
+  if ( num < 0 )
+    return 0; //POT_INVALID_POTION;
+
+  return (member(knownlist,num)>-1);
+}
+
+// Wird vom Spielerobjekt gerufen, um einen gefundenen ZT aus dessen ZT-
+// Listen austragen zu lassen. Das Spielerobjekt stellt sicher, dass room
+// ein Objekt ist, und dass [known_]potionrooms Arrays sind.
+//
+// ACHTUNG! Sowohl potionrooms, als auch known_potionrooms sind die
+//          Originaldaten aus dem Spielerobjekt, die hier ALS REFERENZ
+//          uebergeben werden! Vorsicht bei Aenderungen an der Funktion!
+//
+// Wenn std/player/potions.c geaendert wird, ist diese Funktion obsolet
+// => ggf. komplett rauswerfen. Dann kann auch der Fehlercode in InList()
+// reaktiviert und deren Parameter korrigiert werden
+void RemoveList(object room, int* potionrooms, int* known_potionrooms) {
+  // ZT ist aktiv, das wurde bereits in InList() geprueft, das vor dem
+  // Aufruf von RemoveList() aus dem Spielerobjekt gerufen wird. Daher reicht
+  // es aus, die ZT-Nummer aus dem reverse_table Lookup zu holen.
+  int num = reverse_table[object_name(room)];
+  int tmp = member(potionrooms, num);
+  potionrooms[tmp] = -1;
+  tmp = member(known_potionrooms, num);
+  known_potionrooms[tmp] = -1;
+  return;
+}
+
+#define LISTHEADER "################## Liste %d ################## (%d)\n\n"
+#define INACT_HEADER "################## Inaktiv ################## (%d)\n\n"
+
+int DumpList() {
+  if ( !secure() )
+    return POT_ACCESS_DENIED;
+  // Zuerst die Caches neu aufbauen, um sicherzustellen, dass die Listen
+  // aktuell sind.
+  RebuildCache();
+  // Dumpfile loeschen
+  rm(POTIONDUMP);
+  // Alle Listen der Reihe nach ablaufen. Es wird in jedem Schleifendurchlauf
+  // einmal auf die Platte geschrieben. Das ist Absicht, weil es speicher-
+  // technisch sehr aufwendig waere, die Ausgabedaten stattdessen erst in
+  // einer Variablen zu sammeln und dann wegzuschreiben.
+  foreach(int *listno : sort_array(m_indices(lists),#'>)) {
+    // Zuerst den Header der ensprechenden Liste schreiben.
+    DUMP(sprintf(LISTHEADER, listno, sizeof(lists[listno])));
+    // Dann alle Potions der Liste durchgehen
+    foreach(int potion : lists[listno]) {
+      // Wenn der ZT inaktiv ist, ueberspringen wir den.
+      if ( member(inactive,potion)>-1 )
+        continue;
+      // Raumpfad aus der Gesamtliste holen.
+      string str = potions[potion,POT_ROOMNAME];
+      // Wenn der nicht existiert, entsprechenden Hinweis dumpen, ansonsten
+      // den Raumnamen.
+      //ZZ: ich finde ja, wir sollten sicherstellen, dass das nicht mehr
+      //passiert. Ist das mit dem AddPotionRoom oben nicht schon so?
+      if ( !stringp(str) || !sizeof(str) )
+        str="KEIN RAUM ZUGEORDNET!!!";
+      DUMP(sprintf("%3d. %s\n", potion, str));
+    }
+    // 2 Leerzeilen zwischen jeder Gruppe einfuegen
+    DUMP("\n\n");
+  }
+  // Zum Schluss den Header der Inaktiv-Liste schreiben.
+  DUMP(sprintf(INACT_HEADER, sizeof(inactive)));
+  // Dann alle inaktiven Potions ablaufen
+  foreach(int i : inactive) {
+    //ZZ: sonst muesste man hier wohl auch auf den fehlenden Raum pruefen.
+    DUMP(sprintf("%3d. (Liste %d) %s\n", i, GetListByNumber(i),
+      potions[i,POT_ROOMNAME]));
+  }
+  return 1;
+}
+
+//ORAKEL-Routinen
+
+/* Aufbau eines Tips:
+
+   Tralala, lalala
+   Dideldadeldum
+   Huppsdiwupps
+   XXXXX
+   Noch ein zweiter Tip
+   Dingeldong
+   %%%%%
+
+   Die Prozentzeichen sind das Endezeichen, ab hier koennen eventuelle
+   Kommentare stehen. Die 5 X sind das Trennzeichen zwischen zwei Tips
+   zum selben ZT.
+*/
+
+// TIPLOG() derzeit unbenutzt
+//#define LOGFILE_READ    "ARCH/POTIONS_TIP_READ.log"
+//#define TIPLOG(x)      log_file(LOGFILE_READ, x)
+
+mixed TipLesen(int num) {
+  string *ret = tipmap[num];
+  //Funktion darf nur vom Orakel und EM+ gerufen werden.
+  if ( previous_object() != find_object(ORAKEL) && !secure() )
+    return POT_ACCESS_DENIED;
+  // Derzeit kein Log, da diese Informationen tendentiell eher 
+  // uninteressant sind.
+  // Timestamp ist vom Format "2012-Okt-03 14:18"
+  /*TIPLOG(sprintf("%s ZT-Tip gelesen: %d von TP: %O, PO: %O, TI: %O\n",
+    strftime("%Y-%b-%d %R"), num, this_player(), previous_object(),
+    this_interactive()));*/
+
+  // ZT-Spruch ist bereits im Tip-Cache enthalten, dann direkt ausgeben.
+  if ( pointerp(ret) )
+    return ret;
+  
+  // ZT-Spruch auf grundlegende syntaktische Korrektheit pruefen:
+  // explode() liefert ein Array mit 2 Elementen, wenn die Ende-Markierung
+  // vorhanden und somit das File diesbezueglich korrekt aufgebaut ist.
+  string *tip=explode( read_file(TIPS(num+".zt"))||"", "%%%%%" );
+  if (sizeof(tip) >= 2) {
+    // Der erste Eintrag in dem Array ist dann der Spruch. Wenn dieser
+    // die Trenn-Markierung enthaelt, entstehen hier 2 Hinweise, ansonsten
+    // bleibt es bei einem.
+    tipmap[num] = explode(tip[0], "XXXXX\n");
+    return tipmap[num];
+  }
+  return POT_NO_SUCH_FILE;
+}
+
+// Datenkonvertierung alte Alists => Mappings
+/*mapping ConvertData() {
+  //if ( !secure()) ) return;
+  // all_rooms ist eine Alist der Form ({ string *room, int *number })
+  // als 3. Eintrag schreiben wir ueberall -1 rein, das wird spaeter durch die
+  // Listennummer des betreffenden ZTs ersetzt und erlaubt einen einfachen
+  // Check auf Datenkonsistenz.
+  potions = mkmapping( all_rooms[1],
+                       all_rooms[0],
+                       allocate(sizeof(all_rooms[0]), -1) );
+  // Alle Listen ablaufen
+  foreach(int *list: active_rooms ) {
+    // Alle ZTs dieser Liste ablaufen und die entsprechende Listennummer in
+    // der neuen Datenstruktur setzen.
+    foreach(int num: list) {
+      potions[num,POT_LISTNO] = member(active_rooms,list);
+    }
+  }
+  // inactive_rooms ist eine Alist der gleichen Form. Die Schleife addiert
+  // einfach alle Eintraege darin auf, so dass ein Int-Array entsteht, das
+  // alle inaktiven ZT-Nummern enthaelt; das allerdings nur fuer Listen mit
+  // mindestens einem Element.
+  foreach(int *list : inactive_rooms ) {
+    if ( sizeof(list) ) {
+      inactive += list;
+      // Alle ZTs dieser Liste ablaufen und die entsprechende Listennummer in
+      // der neuen Datenstruktur setzen.
+      foreach(int num : list) {
+        potions[num,POT_LISTNO] = member(inactive_rooms,list);
+      }
+    }
+    // unify array
+    inactive = m_indices(mkmapping(inactive));
+  }
+  mapping invalid = ([ POT_INVALID_LIST_NUMBER : ({}) ]);
+  walk_mapping(potions, function void (int pnum, string path, int listno) {
+    if ( listno == -1 )
+      invalid[POT_INVALID_LIST_NUMBER] += ({pnum});
+  });
+  return sizeof(invalid[POT_INVALID_LIST_NUMBER]) ? invalid : potions;
+}*/
+
diff --git a/secure/pubmaster.c b/secure/pubmaster.c
new file mode 100644
index 0000000..a124889
--- /dev/null
+++ b/secure/pubmaster.c
@@ -0,0 +1,307 @@
+// MorgenGrauen MUDlib
+//
+// Pubmaster.c -- Registrating the heal-values of pubs
+//
+// $Id: pubmaster.c 9373 2015-10-22 19:02:42Z Zesstra $
+
+#include <pub.h>
+
+#define SAVEFILE "/secure/ARCH/pub"
+#define CLOGFILE "/log/ARCH/pubchange"
+#define DUMPFILE "/log/ARCH/PUBS"
+#define DUMPLIST "/log/ARCH/PUBLIST"
+
+#define DUMPF(str) write_file(DUMPFILE,str)
+#define DUMPL(str) write_file(DUMPLIST,str)
+
+#pragma strong_types
+
+#include <properties.h>
+#include <wizlevels.h>
+#include <health.h>
+
+mapping pubs;
+nosave string  *watchprops;
+nosave int     watchsize;
+
+void create()
+{
+    seteuid(getuid(this_object()));
+    if (clonep(this_object()))
+    {
+        destruct(this_object());
+        return;
+    }
+    if (!restore_object(SAVEFILE))
+        pubs = ([]);
+
+    watchprops = ({ P_HP, P_SP, P_ALCOHOL, P_DRINK, P_FOOD, P_VALUE,
+                    "rate", "delay" });
+    watchsize  = sizeof(watchprops);
+}
+
+void save_data()
+{
+    save_object(SAVEFILE);
+}
+
+int CalcMax(mapping info, string fn)
+{   float heal,delay,factor;
+
+    if (!info || !mappingp(info))
+        return 0;
+    if ( (info[P_ALCOHOL]+info[P_DRINK]+info[P_FOOD])<=0     ||
+         (info[P_ALCOHOL] && !(info[P_DRINK]||info[P_FOOD])) )
+        return 0;
+
+    delay = to_float(info["delay"]);
+    if (delay>PUB_MAXDELAY)
+        delay=PUB_MAXDELAY;
+
+    if (!stringp(fn) || !member(pubs,fn)) // External query?
+        factor=1.0;
+    else
+        factor=pubs[fn,1];
+
+    heal = to_float( info[P_ALCOHOL]*ALCOHOL_DELAY +
+                     info[P_DRINK]  *DRINK_DELAY   +
+                     info[P_FOOD]   *FOOD_DELAY    ) 
+           * PUB_SOAKMULT
+           / to_float( ALCOHOL_DELAY + DRINK_DELAY + FOOD_DELAY );
+    heal = heal + 
+           to_float(info[P_VALUE])/(PUB_VALUEDIV+to_float(info["rate"])) -
+           exp(to_float(info["rate"])/PUB_RATEDIV1)/PUB_RATEDIV2;
+    heal = heal * factor *
+           (PUB_WAITOFFS + (exp(delay/PUB_WAITDIV1)/PUB_WAITDIV2));
+
+    return to_int(heal);
+}
+
+int RegisterItem(string item, mapping info)
+{   object pub;
+    string fn;
+    int    i,c,cmax,max,heal;
+
+    if (!objectp(pub=previous_object()))
+        return -1;
+    if (member(inherit_list(pub), "/std/room/pub.c") == -1)
+        return -1;
+    if (!item || !stringp(item) || !info || !mappingp(info))
+        return -2;
+
+    if (info["rate"]<1)
+        info["rate"]=1;
+    if (info["delay"]<0)
+        info["delay"]=0;
+
+    // Loadname, weil VCs als ein Pub zaehlen sollen (das Tutorial hat ein
+    // VC-Pub fuer jeden Spieler. Die sollen nicht alle einzeln hier erfasst
+    // werden.
+    fn = load_name(pub);
+    c = 0;
+
+    heal=info[P_HP]+info[P_SP];
+
+    if (!member(pubs,fn))
+        pubs += ([ fn : ([]); 1.0 ]);
+
+    if (!member(pubs[fn],item))
+    {
+        max=CalcMax(info,fn);
+        pubs[fn] += ([ 
+            item : ([
+                P_HP            : info[P_HP],
+                P_SP            : info[P_SP],
+                P_VALUE         : info[P_VALUE],
+                P_DRINK         : info[P_DRINK],
+                P_ALCOHOL       : info[P_ALCOHOL],
+                P_FOOD          : info[P_FOOD],
+                "rate"          : info["rate"],
+                "delay"         : info["delay"],
+                "maxheal"       : heal,
+                "maxrate"       : 10
+                ])
+            ]);
+        c=1;
+    }
+    else
+    {
+        for (i=watchsize-1;i>=0;i--)
+            if (info[watchprops[i]]!=pubs[fn][item][watchprops[i]])
+            {
+                pubs[fn][item][watchprops[i]]=info[watchprops[i]];
+                c=1;
+            }
+        if (heal>pubs[fn][item]["maxheal"])
+        {
+            pubs[fn][item]["maxheal"]=heal;
+            c=1;
+        }
+        max=pubs[fn][item]["maxset"];
+        cmax=CalcMax(info,fn);
+        if (cmax>max)
+            max=cmax;
+    }
+
+    if (c)
+        save_data();
+
+    if ( heal>max || info["rate"]>pubs[fn][item]["maxrate"] )
+        return 0;
+
+    return 1;
+}
+
+private int allowed()
+{
+  if (previous_object() && geteuid(previous_object())==ROOTID)
+    return 1;
+  if (!process_call() && previous_object() && ARCH_SECURITY)
+    return 1;
+  return 0;
+}
+
+
+
+int SetMaxHeal(string pub, string item, int new)
+{   int old;
+
+    if (!allowed())
+        return -2;
+    if (!pub || !stringp(pub) || !item || !stringp(item) || !new)
+        return -1;
+    if (!member(pubs,pub) || !member(pubs[pub],item))
+        return 0;
+    old=pubs[pub][item]["maxset"];
+    pubs[pub][item]["maxset"]=new;
+    write_file(CLOGFILE,
+        sprintf("%s - %s\n%s:%s HEAL %d > %d\n",
+            dtime(time()),
+            (this_interactive() ? getuid(this_interactive()) : "???"),
+            pub,item,old,new));
+    save_data();
+    return 1;
+}
+
+int SetMaxRate(string pub, string item, int new)
+{   int old;
+
+    if (!allowed())
+        return -2;
+    if (!pub || !stringp(pub) || !item || !stringp(item) || !new)
+        return -1;
+    if (!member(pubs,pub) || !member(pubs[pub],item))
+        return 0;
+    old=pubs[pub][item]["maxrate"];
+    pubs[pub][item]["maxrate"]=new;
+    write_file(CLOGFILE,
+        sprintf("%s - %s\n%s:%s RATE %d > %d\n",
+            dtime(time()),
+            (this_interactive() ? getuid(this_interactive()) : "???"),
+            pub,item,old,new));
+    save_data();
+    return 1;
+}
+
+int SetPubFactor(string pub, float new)
+{   float old;
+
+    if (!allowed())
+        return -2;
+    if (!pub || !stringp(pub) || !new || !floatp(new) || new<1.0 || new>1.5)
+        return -1;
+    if (!member(pubs,pub))
+        return 0;
+    old=pubs[pub,1];
+    pubs[pub,1]=new;
+    write_file(CLOGFILE,
+        sprintf("%s - %s\n%s FACT %6.4f > %6.4f\n",
+            dtime(time()),
+            (this_interactive() ? getuid(this_interactive()) : "???"),
+            pub,old,new));
+    save_data();
+    return 1;
+}
+
+varargs int ClearPub(string pub, string item)
+{
+    if (!allowed())
+        return -2;
+    if (!pub || !stringp(pub) || (item && !stringp(item)))
+        return -1;
+    if (!member(pubs,pub))
+        return 0;
+    if (!item)
+        m_delete(pubs,pub);
+    else if (!member(pubs[pub],item))
+        return 0;
+    else
+      m_delete(pubs[pub],item);
+    save_data();
+    return 1;
+}
+
+int DumpPubs()
+{   string *publist,*itemlist;
+    int    pubi,itemi;
+
+    if (!allowed())
+        return -2;
+    if (file_size(DUMPFILE)>1)
+        rm(DUMPFILE);
+    if (file_size(DUMPLIST)>1)
+        rm(DUMPLIST);
+    DUMPF(sprintf("Kneipen-Menu-Liste vom %s:\n\n",dtime(time())));
+    DUMPL(sprintf("Kneipenliste vom %s:\n\n",dtime(time())));
+    publist=sort_array(m_indices(pubs),#'</*'*/);
+    if ((pubi=sizeof(publist))<1)
+    {
+        DUMPF("No pubs in List\n");
+        DUMPL("No pubs in List\n");
+        return -1;
+    }
+    for (--pubi;pubi>=0;pubi--)
+    {
+        DUMPL(sprintf("%-'.'70s %6.4f\n",
+            publist[pubi],pubs[publist[pubi],1]));
+        DUMPF(sprintf("PUB: %s\nFAC: %6.4f\n\n",
+            publist[pubi],pubs[publist[pubi],1]));
+        itemlist=sort_array(m_indices(pubs[publist[pubi]]),#'</*'*/);
+        if ((itemi=sizeof(itemlist))<1)
+        {
+            DUMPF("  No items in list\n\n");
+            continue;
+        }
+        DUMPF(sprintf(
+            "  %|'_'30.30s %5s %3s %3s %3s %3s %3s %2s %2s %3s %3s\n",
+            "ITEM","VALUE","HPH","SPH","DRI","ALC","FOO","RA","MR","MAX",
+            "FND"));
+        for (--itemi;itemi>=0;itemi--)
+            DUMPF(sprintf(
+                "  %-'.'30.30s %5d %3d %3d %3d %3d %3d %2d %2d %3d %3d\n",
+                itemlist[itemi],
+                (int)pubs[publist[pubi]][itemlist[itemi]][P_VALUE],
+                (int)pubs[publist[pubi]][itemlist[itemi]][P_HP],
+                (int)pubs[publist[pubi]][itemlist[itemi]][P_SP],
+                (int)pubs[publist[pubi]][itemlist[itemi]][P_DRINK],
+                (int)pubs[publist[pubi]][itemlist[itemi]][P_ALCOHOL],
+                (int)pubs[publist[pubi]][itemlist[itemi]][P_FOOD],
+                (int)pubs[publist[pubi]][itemlist[itemi]]["rate"],
+                (int)pubs[publist[pubi]][itemlist[itemi]]["maxrate"],
+                (int)pubs[publist[pubi]][itemlist[itemi]]["maxset"],
+                (int)pubs[publist[pubi]][itemlist[itemi]]["maxheal"] ));
+        DUMPF("\n");
+    }
+    return sizeof(publist);
+}
+
+// bereinigt die Kneipenliste von allen Kneipen, die auf der Platte nicht mehr
+// existieren.
+public void CleanPublist() {
+  foreach(string pub: pubs) {
+    if (file_size(pub+".c") <= 0)
+      m_delete(pubs,pub);
+  }
+  save_data();
+}
+
diff --git a/secure/questmaster.c b/secure/questmaster.c
new file mode 100644
index 0000000..314a97b
--- /dev/null
+++ b/secure/questmaster.c
@@ -0,0 +1,1174 @@
+// MorgenGrauen MUDlib
+//
+// questmaster.c -- Questmaster, verwaltet die normalen Quests und
+//                  die MiniQuests
+//
+// $Id: questmaster.c 9136 2015-02-03 21:39:10Z Zesstra $
+//
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#include <config.h>
+#include "/secure/wizlevels.h"
+#include "/secure/questmaster.h"
+#include "/secure/lepmaster.h"
+#include "/secure/telnetneg.h" // P_TTY
+#include <living/description.h> // P_LEVEL
+#include <player/base.h> // P_TESTPLAYER
+#include <daemon.h>
+#include <ansi.h>
+#include <events.h>
+
+#define DEBUG(x) if (funcall(symbol_function('find_player),"arathorn"))\
+          tell_object(funcall(symbol_function('find_player),"arathorn"),\
+                      "QM: "+x+"\n")
+
+#define ME this_object()
+#define PL this_player()
+
+#define MQ_DATA_POINTS 0
+#define MQ_DATA_QUESTNO 1
+#define MQ_DATA_TASKDESC 2
+#define MQ_DATA_VISIBLE 3
+#define MQ_DATA_ACTIVE 4
+#define MQ_DATA_TITLE 5
+#define MQ_DATA_DIARYTEXT 6
+#define MQ_DATA_RESTRICTIONS 7
+#define MQ_DATA_ASSIGNED_DOMAIN 8
+#define MQ_DATA_QUERY_PERMITTED 9
+
+private int max_QP = 0;
+private int opt_QP = 0;
+// Die Questliste mit allen Daten
+private mapping quests = ([]);
+
+// Das Mapping mit der MQ-Liste an sich und alle zugehoerigen Daten
+private mapping miniquests = ([]);
+// Nach MQ-Nummern indizierter Cache:
+// Struktur ([int num : ({ int stupse, string mq-object }) ])
+private nosave mapping by_num = ([]);
+// Cache der Objekte, die die MQ-Listen der Regionen abfragen duerfen
+// Struktur ([string path : ({ string mq_object }) ])
+private nosave mapping mq_query_permitted = ([]);
+// Cache fuer die MQ-Punkte von Spielern, wird fuer den jeweiligen Spieler
+// beim Abfragen von dessen MQ-Punkten gefuellt. Spielername wird bei
+// Aenderungen an seinen MQ-Punkten (Bestehen einer MQ, manuelles Setzen
+// oder Loeschen einer seiner MQs) aus dem Cache ausgetragen
+private nosave mapping users_mq = ([]);
+// letzte vergebene MQ-Indexnummer. Es darf niemals eine MQ eine Indexnummer
+// kriegen, die eine andere MQ schonmal hatte, auch wenn die geloescht wurde.
+// (Zumindest nicht, ohne die entsprechenden Bits im Spieler zu loeschen, was
+// zZ nicht passiert.
+private int last_num = 0;
+
+
+void save_info() {
+  save_object(QUESTS);
+}
+
+// Caches aufbauen.
+static void make_num(string mqob_name, int stupse, int index,
+                     string taskdesc, int vis, int active, string title,
+                     string donedesc, mapping restr, string domain, 
+                     string *permitted_objs) {
+  by_num += ([ index : ({stupse, mqob_name})]);
+  foreach ( string obj: permitted_objs ) {
+    if ( member(mq_query_permitted, obj) )
+      mq_query_permitted[obj] += ({mqob_name});
+    else
+      mq_query_permitted[obj] = ({mqob_name});
+  }
+}
+
+void create() {
+  seteuid(getuid(ME));
+  if (!restore_object(QUESTS)) {
+    save_info();
+  }
+
+  walk_mapping(miniquests, #'make_num /*'*/ );
+  set_next_reset(43200); // Reset alle 12 Stunden.
+  EVENTD->RegisterEvent(EVT_LIB_QUEST_SOLVED,"HandleQuestSolved",
+      ME);
+}
+
+public int remove(int silent) {
+  save_info();
+  EVENTD->UnregisterEvent(EVT_LIB_QUEST_SOLVED, ME);
+  destruct(ME);
+  return 1;
+}
+
+// Schreibzugriff nur fuer interaktive EMs und ARCH_SECURITY.
+private int allowed_write_access() {
+  if (process_call())
+    return 0;
+  if (ARCH_SECURITY)  // prueft auch this_interactive() mit.
+    return 1;
+  return 0;
+}
+
+void reset() {
+  by_num = ([]);
+  mq_query_permitted = ([]);
+  walk_mapping(miniquests, #'make_num /*'*/ );
+  set_next_reset(43200);
+}
+
+/*
+ * (1) ABSCHNITT "NORMALE QUESTS"
+ */
+
+/* Die Quests werden in einem Mapping gespeichert. Der Schluessel ist dabei der
+   Quest-Name, die Eintraege sind Arrays der folgenden Form:
+
+   1. Element ist die Zahl der durch diese Quest zu erwerbenden Questpunkte.
+   2. Element ist die Zahl der Erfahrungspunkte, die der Spieler bekommt,
+      wenn er diese Quest loest.
+   3. Element ist ein Array mit den Filenamen der Objekte, denen es gestattet
+      ist, diese Quest beim Player als geloest zu markieren (Erzmagier duerfen
+      das aber sowieso immer).
+   4. Element ist ein String, der die Quest kurz beschreibt. Dieser String wird
+      dem Spieler vom Orakel als Hinweis gegeben.
+   5. Element ist eine Zahl zwischen -1 und 100, die den Schwierigkeitsgrad der
+      Quest angibt, nach Einschaetzung des EM fuer Quests. Das Orakel kann dann
+      evtl. sinnige Saetze wie "Diese Quest erscheint mir aber noch recht
+      schwer fuer Dich.", oder "Hm, die haettest Du ja schon viel eher loesen
+      koennen." absondern. :)
+
+      Ein Wert von -1 bedeutet eine Seherquest. Diese zaehlt nicht zu den
+      Maximalen Questpunkten, sondern zaehlt als optionale Quest
+   6. Element ist ein Integer von 0 bis 5 und gibt die "Klasse" an;
+      ausgegeben werden dort Sternchen
+   7. Element ist ein Integer, 0 oder 1.
+      0: Quest voruebergehend deaktiviert (suspendiert)
+      1: Quest aktiviert
+   8. Element ist ein String und enthaelt den Namen des Magiers, der die
+      Quest "verbrochen" hat.
+   9. Element ist ein String, der den Namen eines Magiers enthaelt, der
+      evtl. fuer die Wartung der Quest zustaendig ist.
+  10. Element ist eine Zahl von 0 bis 4, die der Quest ein Attribut
+      gibt (0 fuer keines)
+*/
+
+// geaendert:
+// 5  == diff geht nun von -1 bis 100
+// 6  == klasse geht nun von 0 bis 5
+// 10 == attribut geht nun von 0 bis 4
+
+private int RecalculateQP() {
+  int i;
+  mixed q,n;
+
+  if (!allowed_write_access())
+    return -1;
+
+  max_QP=0;
+  opt_QP=0;
+
+  n=m_indices(quests);
+  q=m_values(quests);
+    for (i=sizeof(q)-1;i>=0;i--)
+      if (q[i][Q_ACTIVE]) {
+        if (q[i][Q_DIFF]>=0)
+          max_QP+=q[i][Q_QP];
+        if (q[i][Q_DIFF]==-1)
+          opt_QP+=q[i][Q_QP];
+      }
+
+  return max_QP+opt_QP;
+}
+
+int AddQuest(string name, int questpoints, int experience,
+      string *allowedobj, string hint, int difficulty, int questclass,
+      int active, string wiz, string scndwiz, int questattribute)
+{
+  mixed *quest;
+  int i;
+
+  if (!allowed_write_access()) return 0;
+  if (!stringp(name) || sizeof(name)<5) return -1;
+  if (questpoints<1) return -2;
+  if (!intp(experience)) return -3;
+  if (!pointerp(allowedobj)) return -4;
+  for (i=sizeof(allowedobj)-1;i>=0;i--)
+    {
+      if (!stringp(allowedobj[i]) || allowedobj[i]=="") return -4;
+      allowedobj[i]=(string)"/secure/master"->_get_path(allowedobj[i],0);
+    }
+  if (!stringp(hint) || hint=="") return -5;
+  if (difficulty<-1 || difficulty>100) return -6;
+  if (questclass<0 || questclass>5) return -11;
+  if (active<0 || active>1) return -7;
+  if (!stringp(wiz) || wiz=="" ||
+      file_size("/players/"+(wiz=lower_case(wiz))) != -2) return -8;
+  if (!stringp(scndwiz))
+    scndwiz="";
+  else if (file_size("/players/"+(scndwiz=lower_case(scndwiz))) != -2)
+    return -9;
+  if (questattribute<0 || questattribute>4)
+    return -10;
+
+  if(quests[name]&&(quests[name][5]==0||quests[name][5]==1)&&quests[name][6])
+    max_QP-=quests[name][0];
+
+  quests+=([name: ({questpoints,experience,allowedobj,hint,difficulty,
+                    questclass,active,wiz, scndwiz,questattribute,
+                    ({0.0,0}) }) ]);
+  RecalculateQP();
+  save_info();
+  QMLOG(sprintf("add: %s %O (%s)",name,quests[name],
+                getuid(this_interactive())));
+  return 1;
+}
+
+int RemoveQuest(string name) {
+  mixed *quest;
+
+  if (!allowed_write_access()) return 0;
+  if (!quests[name]) return -1;
+  QMLOG(sprintf("remove: %s %O (%s)",name,quests[name],
+                getuid(this_interactive())));
+  m_delete(quests,name);
+  RecalculateQP();
+  save_info();
+  return 1;
+}
+
+int QueryNeededQP() {
+  return REQ_QP;
+}
+
+int QueryMaxQP() {
+  return max_QP;
+}
+
+int QueryOptQP() {
+  return opt_QP;
+}
+
+int QueryTotalQP() {
+  return max_QP+opt_QP;
+}
+
+mixed *QueryGroupedKeys() {
+  string *qliste;
+  mixed  *qgliste;
+  int i, j;
+
+  qgliste = allocate(sizeof(QGROUPS)+1); // letzte Gruppe sind die Seherquests
+  qliste = m_indices(quests);
+
+  for (i=sizeof(qgliste)-1;i>=0;i--)
+    qgliste[i]=({});
+
+  for (i=sizeof(qliste)-1;i>=0;i--)
+    {
+      // inaktive quest?
+      if (!quests[qliste[i]][Q_ACTIVE])
+        continue;
+      // optionale quest? also Seherquest
+        if (quests[qliste[i]][Q_DIFF]==-1)
+          qgliste[sizeof(QGROUPS)] += ({qliste[i]});
+        else {
+          // dann haben wir also eine normale Quest und daher Einordnung
+          // nach dem Schwierigkeitswert
+          for (j=sizeof(QGROUPS)-1;
+               j>=0 && QGROUPS[j]>=quests[qliste[i]][Q_DIFF];j--)
+            ;
+          qgliste[j] += ({qliste[i]});
+        }
+    }
+  return qgliste;
+}
+
+
+// folgende funk brauch ich glaube ich nicht mehr:
+int QueryDontneed(object pl) {
+  raise_error("Ich glaub, die Func QueryDontneed() braucht kein Mensch mehr. "
+  "(Zook)");
+}
+
+// Die folgende Func braucht man nicht mehr
+int QueryReadyForWiz(object player) {
+  raise_error("Die Func QueryReadyForWiz() braucht keiner mehr. (Zook)");
+}
+
+mixed *QueryQuest(string name) {
+  if(!quests[name])
+    return ({});
+  if( extern_call() )
+    return deep_copy( quests[name] );
+  return quests[name];
+}
+
+int QueryQuestPoints(string name) {
+  if( !quests[name] )
+    return -1;
+
+  return quests[name][Q_QP];
+}
+
+mixed *QueryQuests() {
+  if( extern_call() )
+    return ({m_indices(quests),map(m_values(quests),#'deep_copy /*'*/)});
+  return ({ m_indices(quests), m_values(quests) });
+}
+
+string *QueryAllKeys() {
+  return m_indices(quests);
+}
+
+int SetActive(string name, int flag) {
+  mixed *quest;
+
+  if (!allowed_write_access()) return 0;
+  if (!(quest=quests[name])) return -1;
+  switch(flag)
+    {
+    case 0:
+      if (quest[Q_ACTIVE] == flag)
+        return -2;
+      quest[Q_ACTIVE] = flag;
+      break;
+    case 1:
+      if (quest[Q_ACTIVE] == flag)
+        return -2;
+      quest[Q_ACTIVE] = flag;
+      break;
+    default:
+      return -3;
+    }
+  quests[name]=quest;
+  RecalculateQP();
+  save_info();
+  QMLOG(sprintf("%s: %s (%s)",(flag?"activate":"deactivate"),name,
+                getuid(this_interactive())));
+  return 1;
+}
+
+string name() {
+  return "<Quest>";
+}
+string Name() {
+  return "<Quest>";
+}
+
+void Channel(string msg) {
+  if(!interactive(previous_object()))
+    return;
+  catch(CHMASTER->send("Abenteuer", ME, msg);publish);
+}
+
+ /* quoted from /sys/mail.h: */
+#define MSG_FROM 0
+#define MSG_SENDER 1
+#define MSG_RECIPIENT 2
+#define MSG_CC 3
+#define MSG_BCC 4
+#define MSG_SUBJECT 5
+#define MSG_DATE 6
+#define MSG_ID 7
+#define MSG_BODY 8
+
+void SendMail(string questname, mixed *quest, object player) {
+  mixed* mail;
+  string text;
+
+  mail = allocate(9);
+
+  text =
+    "Hallo "+capitalize(getuid(player))+",\n\n"+
+    break_string("Nachdem Du gerade eben das Abenteuer '"+
+                 questname +"' ("+quest[Q_QP]+" Punkte), das "+
+                 capitalize(quest[Q_WIZ])+" fuer das "MUDNAME" entworfen hat, "
+                 "mit Erfolg bestanden hast, sind "
+                 "wir nun an Deiner Meinung dazu interessiert:", 78)+
+    "\n  Hat Dir das Abenteuer gefallen und wieso bzw. wieso nicht?\n"
+    "  Ist die Einstufung Deiner Meinung nach richtig? (AP und Stufe)\n"
+    "  Gab es Probleme oder gar Fehler?\n"
+    "  Hast Du Verbesserungsvorschlaege?\n\n";
+
+  text += break_string("Diese Nachricht wurde automatisch verschickt, "
+        "wenn Du mit dem 'r' Kommando darauf antwortest, geht die Antwort "
+        "direkt an Ark als zustaendigem Erzmagier fuer Abenteuer.\n",78);
+
+  if (quest[Q_SCNDWIZ]!="") {
+    text += break_string(
+        "Falls Du mit dem Magier sprechen willst, der zur Zeit das "
+        "Abenteuer technisch betreut, kannst Du Dich an "
+        +capitalize(quest[Q_SCNDWIZ])+ " wenden.",78);
+  }
+
+  mail[MSG_FROM] = "Ark";
+  mail[MSG_SENDER] = "Ark";
+  mail[MSG_RECIPIENT] = getuid(player);
+  mail[MSG_CC]=0;
+  mail[MSG_BCC]=0;
+  mail[MSG_SUBJECT]="Das Abenteuer: "+questname;
+  mail[MSG_DATE]=dtime(time());
+  mail[MSG_ID]=MUDNAME":"+time();
+  mail[MSG_BODY]=text;
+
+  "/secure/mailer"->DeliverMail(mail,0);
+  return;
+}
+
+static int compare (mixed *i, mixed *j) {
+  if (i[4] == j[4])
+    return i[1] > j[1];
+  else
+    return i[4] > j[4];
+}
+
+varargs string liste(mixed pl) {
+  int qgroups, i, j, qrfw;
+  mixed *qlists, *qgrouped, *qtmp;
+  string str;
+  string ja, nein, format, ueberschrift;
+
+  if(!objectp(pl))
+    if(stringp(pl))
+      pl=find_player(pl) || find_netdead(pl);
+  if(!objectp(pl))
+    pl=PL;
+  if(!objectp(pl))
+    return "Ohne Spielernamen/Spielerobjekt gibt es auch keine Liste.\n";
+
+  if ( ((string)pl->QueryProp(P_TTY)) == "ansi")
+  {
+      ja = ANSI_GREEN + "ja" + ANSI_NORMAL;
+      nein = ANSI_RED + "nein" + ANSI_NORMAL;
+  }
+  else
+  {
+      ja = "ja";
+      nein = "nein";
+  }
+
+  str = "";
+  // Festlegen des Ausgabeformates
+  format = "%=-:30s %:3d %-:6s  %-:9s %:2s/%-:3s  %-:12s %-s\n";
+  ueberschrift = sprintf("%-:30s %:3s %-:6s  %-:9s %-:6s  %-:12s %-:4s\n",
+                 "Abenteuer", "AP", "Klasse", "Attribut",
+                 "Stufe", "Autor", "Gel?");
+
+  qgroups = sizeof(QGROUPS);
+  qlists = allocate( qgroups+1 );
+  for( i=qgroups; i>=0; i-- )
+    qlists[i] = ({});
+
+  qgrouped = QueryGroupedKeys();
+
+  for (i=sizeof(qgrouped)-1;i>=0; i--)
+    for (j=sizeof(qgrouped[i])-1;j>=0; j--) {
+      qtmp = QueryQuest(qgrouped[i][j]);
+      qlists[i] += ({ ({
+        qgrouped[i][j],
+        qtmp[Q_QP],
+        QCLASS_STARS(qtmp[Q_CLASS]),
+        capitalize(QATTR_STRINGS[qtmp[Q_ATTR]]),
+        qtmp[Q_DIFF],
+        (qtmp[Q_AVERAGE][1]>10 /*&& IS_ARCH(this_player())*/
+                  ? to_string(to_int(qtmp[Q_AVERAGE][0]))
+                  : "-"),
+        capitalize(qtmp[Q_WIZ]),
+        (int)pl->QueryQuest(qgrouped[i][j]) == OK ? ja : nein
+      }) });
+    }
+
+  for( i=0; i<qgroups; i++ )
+  {
+    if (sizeof(qlists[i])) {
+      str += "\n" + ueberschrift;
+      str += sprintf("Stufen %d%s:\n",
+                     QGROUPS[i]+1,
+                     i==qgroups-1?"+":sprintf("-%d", QGROUPS[i+1]));
+      qlists[i] = sort_array( qlists[i], "compare", ME );
+      for( j=0; j<sizeof(qlists[i]); j++ ) {
+        if(qlists[i][j][Q_DIFF]>=0)
+          str += sprintf( format,
+                          qlists[i][j][0], qlists[i][j][1], qlists[i][j][2],
+                          qlists[i][j][3], sprintf("%d",qlists[i][j][4]),
+                          qlists[i][j][5],
+                          qlists[i][j][6], qlists[i][j][7]);
+      }
+      str += "\n\n";
+    }
+  }
+
+  qlists[qgroups] = sort_array(qlists[qgroups], "compare", ME);
+  i = qgroups;
+  if (sizeof(qlists[i])) {
+    str += "\n" + ueberschrift;
+    str += "Nur fuer Seher:\n";
+    for( j=0; j<sizeof(qlists[qgroups]); j++ )  {
+      if(qlists[i][j][Q_DIFF]==-1)
+        str += sprintf( format,
+                        qlists[i][j][0], qlists[i][j][1], qlists[i][j][2],
+                        qlists[i][j][3], "S", qlists[i][j][5],
+                        qlists[i][j][6],
+                        qlists[i][j][7]);
+    }
+  }
+
+  str +=
+    "\nEine Erklaerung der einzelnen Spalten findest Du unter "
+    "\"hilfe abenteuerliste\".\n";
+
+  return str;
+}
+
+
+// mitloggen, mit welchen durchschnittlichen Leveln Quests so geloest
+// werden...
+void HandleQuestSolved(string eid, object trigob, mixed data) {
+  string qname = data[E_QUESTNAME];
+
+  if (!quests[qname] || !objectp(trigob)
+      || trigob->QueryProp(P_TESTPLAYER) || IS_LEARNER(trigob))
+    return;
+
+  int lvl = (int)trigob->QueryProp(P_LEVEL);
+
+  if (lvl <= 0)
+    return;
+
+  // neuen Durchschnitt berechen.
+  mixed tmp = quests[qname][Q_AVERAGE];
+  float avg = tmp[0];
+  int count = tmp[1];
+  avg *= count;
+  avg += to_float(lvl);
+  tmp[1] = ++count;
+  tmp[0] = avg / count;
+
+  DEBUG(sprintf("%s: %f (%d)\n",qname,
+        quests[qname][Q_AVERAGE][0],
+        quests[qname][Q_AVERAGE][1]));
+}
+
+/*
+ * (2) ABSCHNITT "MINI" QUESTS
+ */
+
+int ClearUsersMQCache() {
+  if (!allowed_write_access())
+    return 0;
+
+  users_mq = ([]);
+
+  return 1;
+}
+
+mixed QueryUsersMQCache() {
+  if (!allowed_write_access())
+    return 0;
+
+  return users_mq;
+}
+
+/* Beschreibung
+ *
+ * Die MiniQuests werden in einem Mapping gespeichert.
+ *
+ * Der Key ist dabei der Name des Objektes, das die Quest im Spieler
+ * markieren darf. Die Daten zu den Miniquests stehen in den Value-Spalten
+ * des Mappings.
+ *
+ * 1. Spalte ist die Zahl der durch diese Quest zu erwerbenden Stufenpunkte
+ * 2. Spalte ist die Nummer unter der die MiniQuest gefuehrt wird.
+ * 3. Spalte ist ein String, der die Quest(aufgabe) kurz beschreibt.
+ * 4. Spalte ist ein Integer, 0 oder 1:
+ *     0 : Quest ist fuer Spieler nicht sichtbar
+ *     1 : Quest ist fuer Spieler z.B. bei einer Anschlagtafel sichtbar
+ *     Fuer Spieler unsichtbare MQs sollte es aber nicht mehr geben!
+ * 5. Spalte ist ein Integer, 0 oder 1:
+ *     0 : Quest voruebergehend deaktiviert
+ *     1 : Quest aktiviert
+ * 6. Spalte ist ein String, der den Kurztitel der Miniquest enthaelt
+ * 7. Spalte ist ein String, der eine kurze Beschreibung dessen enthaelt,
+ *     was der Spieler im Verlauf der Miniquest erlebt hat.
+ * 8. Spalte ist ein Mapping, dessen Eintraege analog zu P_RESTRICTIONS
+ *     gesetzt werden koennen, um anzugeben, welche Voraussetzungen erfuellt
+ *     sein muessen, bevor ein Spieler diese Quest beginnen kann.
+ * 9. Spalte ist die Zuordnung der MQ zu den Regionen
+ *10. Spalte ist ein Array aus Strings, das die Objekte enthaelt, die
+ *    die Daten dieser Quest abfragen duerfen, um sie an Spieler auszugeben.
+ */
+
+int DumpMiniQuests(object who) {
+  int sum_points;
+
+  if (extern_call() && !allowed_write_access())
+    return 0;
+
+  if ( !objectp(who) || !query_once_interactive(who))
+    who = this_interactive();
+
+  MQMLOG(sprintf("DumpMiniQuests: PO: %O, TI: %O", previous_object(), who));
+  rm(MQ_DUMP_FILE);
+
+  write_file(MQ_DUMP_FILE, "MINIQUESTS: ("+dtime(time())+")\n\n"+
+    "  Nr  Pkt  vis akt vergebendes Objekt\n");
+  string *msg = ({});
+
+  foreach(string obname, int stupse, int nummer, mixed descr, int vis,
+      int active /*, string title, string donedesc, mapping restrictions,
+      string domain, string *permitted_objs*/: miniquests)
+  {
+    msg += ({ sprintf("%4d %4d %4d %4d %s",
+      nummer, stupse, vis, active, obname)});
+    sum_points += stupse;
+  }
+
+  write_file(MQ_DUMP_FILE, implode(sort_array(msg, #'> /*'*/), "\n"));
+  write_file(MQ_DUMP_FILE, sprintf("\n\n"
+             "============================================================\n"
+             +"MiniQuests: %d Miniquests mit %d Punkten.\n\n",
+              sizeof(miniquests), sum_points));
+  return 1;
+}
+
+public int AddMiniQuest(int mquestpoints, string allowedobj, string descr,
+            int active, string title, string donedesc, mapping restrictions,
+            string domain, string *permitted_objs) {
+
+  if (!allowed_write_access())
+    return 0;
+
+  // Parameterpruefung: Questgeber, Restrictions, Region, Titel und
+  // zugelassene Abfrageobjekte muessen gueltig angegeben werden, alles
+  // weitere wird unten ggf. ausgenullt/korrigiert.
+  if (!stringp(allowedobj) || !sizeof(allowedobj) || !mappingp(restrictions)
+      || !stringp(domain) || !stringp(title) || !pointerp(permitted_objs))
+    return -1;
+
+  // Miniquest mit weniger als 1 Stups einzutragen ist wohl unsinnig.
+  if (mquestpoints<1)
+    return -2;
+
+  // Mindestens ein Objekt muss eingetragen werden, das Spielern Informationen
+  // ueber die Aufgabenstellung der MQ geben darf.
+  if ( !sizeof(permitted_objs) )
+    return -3;
+
+  // Pruefen, ob die als Questgeber angegebene Datei existiert.
+  if (allowedobj[<2..] == ".c")
+    allowedobj = allowedobj[0..<3];
+  allowedobj = explode(allowedobj, "#")[0];
+  allowedobj = (string)MASTER->_get_path(allowedobj,0);
+  if (file_size(allowedobj+".c") <=0)
+    return -3;
+
+  // Vergibt das angegebene Objekt schon eine MQ? Dann abbrechen.
+  if (member(miniquests,allowedobj))
+    return -4;
+
+  if (!stringp(descr) || !sizeof(descr))
+    descr = 0;
+  if (!stringp(donedesc) || !sizeof(donedesc))
+    donedesc = 0;
+
+  // Eintrag hinzufuegen, visible ist per Default immer 1.
+  // MQ-Nummer hochzaehlen
+  int nummer = last_num + 1;
+  m_add(miniquests, allowedobj, mquestpoints, nummer, descr, 1, active,
+    title, donedesc, restrictions, domain, permitted_objs);
+  // und nummer als last_num merken.
+  last_num = nummer;
+  save_info();
+  m_add(by_num, nummer, ({mquestpoints, allowedobj}));
+  MQMLOG(sprintf("AddMiniQuest: %s %O (%s)", allowedobj, miniquests[allowedobj],
+                 getuid(this_interactive())));
+
+  ClearUsersMQCache();
+  if (find_call_out(#'DumpMiniQuests) == -1)
+    call_out(#'DumpMiniQuests, 60, this_interactive());
+  return 1;
+}
+
+int RemoveMiniQuest(string name) {
+  if (!allowed_write_access())
+    return 0;
+  // Gibt es einen solchen Eintrag ueberhaupt?
+  if (!member(miniquests,name))
+    return -1;
+
+  MQMLOG(sprintf("RemoveMiniQuest: %s %O (%s)",
+    name, m_entry(miniquests, name), getuid(this_interactive())));
+
+  // MQ aus dem MQ-Indexnummern-Cache loeschen.
+  m_delete(by_num, miniquests[name,MQ_DATA_QUESTNO]);
+  // MQ aus der Miniquestliste austragen.
+  m_delete(miniquests, name);
+  save_info();
+
+  // MQ-Punkte-Cache loeschen, da nicht feststellbar ist, welcher der
+  // dort eingetragenen Spieler die gerade ausgetragene MQ geloest hatte.
+  ClearUsersMQCache();
+  if (find_call_out(#'DumpMiniQuests) == -1)
+    call_out(#'DumpMiniQuests, 60, this_interactive());
+  return 1;
+}
+
+int ChangeMiniQuest(mixed mq_obj, int param, mixed newvalue) {
+  if (!allowed_write_access())
+    return 0;
+
+  // MQ weder als Pfad, noch als Indexnummer angegeben?
+  if ( !stringp(mq_obj) && !intp(mq_obj) && !intp(param))
+    return MQ_KEY_INVALID;
+
+  // gewaehlter Parameter ungueltig?
+  if ( param < MQ_DATA_POINTS || param > MQ_DATA_QUERY_PERMITTED )
+    return MQ_KEY_INVALID;
+
+  // Indexnummer der MQ in den Pfad umwandeln
+  if ( intp(mq_obj) )
+    mq_obj = by_num[mq_obj][1];
+
+  // Vergebendes Objekt nicht gefunden? Bloed, das brauchen wir naemlich.
+  if (!stringp(mq_obj))
+    return MQ_KEY_INVALID;
+
+  if ( !member(miniquests, mq_obj) )
+    return MQ_ILLEGAL_OBJ;
+
+  switch(param) {
+    // MQ_DATA_QUESTNO ist nicht aenderbar, daher hier nicht behandelt, so
+    // dass Fallback auf default erfolgt.
+    // Stufenpunkte muessen Integers sein.
+    case MQ_DATA_POINTS:
+      if ( !intp(newvalue) || newvalue < 1 )
+        return MQ_KEY_INVALID;
+      break;
+    // Aufgabenbeschreibung, Titel, "geschafft"-Text und zugeordnete Region
+    // muessen Strings sein
+    case MQ_DATA_TASKDESC:
+    case MQ_DATA_TITLE:
+    case MQ_DATA_DIARYTEXT:
+    case MQ_DATA_ASSIGNED_DOMAIN:
+      if ( !stringp(newvalue) || !sizeof(newvalue) ) 
+        return MQ_KEY_INVALID;
+      break;
+    // das Sichtbarkeits- und das aktiv/inaktiv-Flag muessen 0/1 sein.
+    case MQ_DATA_VISIBLE:
+    case MQ_DATA_ACTIVE:
+      if ( !intp(newvalue) || newvalue < 0 || newvalue > 1 )
+        return MQ_KEY_INVALID;
+      break;
+    // Die Voraussetzungen muessen als Mapping eingetragen werden, das aber
+    // leer oder Null sein kann, wenn es keine Restriktionen gibt.
+    case MQ_DATA_RESTRICTIONS:
+      if ( !mappingp(newvalue) && newvalue != 0 )
+        return MQ_KEY_INVALID;
+      break;
+    // Regionszuordnung muss ein nicht-leeres Array sein, das nur aus Strings
+    // bestehen darf, die nicht leer sein duerfen.
+    case MQ_DATA_QUERY_PERMITTED:
+      if ( pointerp(newvalue) ) {
+        newvalue = filter(filter(newvalue, #'stringp), #'sizeof);
+        if (!sizeof(newvalue))
+          return MQ_KEY_INVALID;
+      }
+      else
+        return MQ_KEY_INVALID;
+      break;
+    default:
+      return MQ_KEY_INVALID;
+  }
+
+  mixed *altemq = m_entry(miniquests, mq_obj);
+  int nummer = miniquests[mq_obj,MQ_DATA_QUESTNO];
+  miniquests[mq_obj, param] = newvalue;
+  by_num[nummer] = ({miniquests[mq_obj,MQ_DATA_POINTS], mq_obj});
+  save_info();
+
+  MQMLOG(sprintf("ChangeMiniQuest: %s from %O to %O (%s)", mq_obj,
+    altemq, m_entry(miniquests, mq_obj), getuid(this_interactive())));
+
+  ClearUsersMQCache();
+  if (find_call_out(#'DumpMiniQuests) == -1)
+    call_out(#'DumpMiniQuests, 60, this_interactive());
+  return 1;
+}
+
+mixed QueryMiniQuestByName(string name) {
+  if (!allowed_write_access())
+    return 0;
+  return deep_copy(miniquests & ({name}));
+}
+
+mixed QueryMiniQuestByNumber(int nummer) {
+  // Zugriffsabsicherung erfolgt dort auch, daher hier unnoetig
+  return (by_num[nummer]?QueryMiniQuestByName(by_num[nummer][1]):0);
+}
+
+// Das vollstaendige MQ-Mapping nur als Kopie ausliefern.
+mixed QueryMiniQuests() {
+  return allowed_write_access() ? deep_copy(miniquests) : 0;
+}
+
+// De-/Aktivieren einer Miniquest, wirkt als Umschalter, d.h. eine aktive
+// MQ wird durch Aufruf dieser Funktion als inaktiv markiert und umgekehrt.
+int SwitchMiniQuestActive(string name) {
+  if (!allowed_write_access())
+    return -1;
+  // Haben wir eine solche MQ ueberhaupt?
+  if (!member(miniquests, name))
+    return -2;
+
+  // active-Flag invertieren
+  miniquests[name, MQ_DATA_ACTIVE] = !miniquests[name, MQ_DATA_ACTIVE];
+  save_info();
+
+  MQMLOG(sprintf("%s: %s (%s)",
+    (miniquests[name,MQ_DATA_ACTIVE]?"Activate":"Deactivate"), name,
+    getuid(this_interactive()))
+  );
+  return miniquests[name,MQ_DATA_ACTIVE];
+}
+
+int GiveMiniQuest(object winner) {
+  // Spieler muss existieren und interactive sein.
+  if (!winner ||
+      (this_interactive() && (this_interactive() != winner)) ||
+      ((this_player() == winner) && !query_once_interactive(winner)))
+    return MQ_ILLEGAL_OBJ;
+  // Gaeste koennen keine Miniquests bestehen.
+  if (winner->QueryGuest())
+    return MQ_GUEST;
+  // Aufrufendes Objekt existiert gar nicht?
+  if (!previous_object())
+    return MQ_ILLEGAL_OBJ;
+
+  string objname = load_name(previous_object());
+  // Miniquest muss existieren
+  if (!member(miniquests,objname))
+    return MQ_KEY_INVALID;
+  // Inaktive Miniquests koennen nicht vergeben werden.
+  if (!miniquests[objname, MQ_DATA_ACTIVE])
+    return MQ_IS_INACTIVE;
+
+  string mq = (MASTER->query_mq(getuid(winner)) || "");
+
+  // Spieler hat die MQ schonmal bestanden? Dann keine weiteren Aktivitaet
+  // noetig
+  if (test_bit(mq, miniquests[objname, MQ_DATA_QUESTNO]))
+    return MQ_ALREADY_SET;
+
+  catch(mq = set_bit(mq, miniquests[objname,MQ_DATA_QUESTNO]);publish);
+  MASTER->update_mq(getuid(winner), mq);
+
+  MQSOLVEDLOG(sprintf("%s: %s, (#%d), (Stupse %d)",
+    objname, geteuid(winner), miniquests[objname, MQ_DATA_QUESTNO],
+    miniquests[objname, MQ_DATA_POINTS]));
+
+  // Miniquest-Event ausloesen
+  EVENTD->TriggerEvent( EVT_LIB_MINIQUEST_SOLVED, ([
+            E_OBJECT: previous_object(),
+            E_OBNAME: objname,
+            E_PLNAME: getuid(winner),
+            E_MINIQUESTNAME: miniquests[objname, MQ_DATA_TITLE] ]) );
+
+  // Spielereintrag aus dem MQ-Punkte-Cache loeschen
+  m_delete(users_mq, getuid(winner));
+
+  return 1;
+}
+
+int QueryMiniQuestPoints(mixed pl) {
+  string spieler;
+
+  //if (!allowed_write_access())
+  //  return 0;
+
+  if (!pl)
+    return -1;
+
+  if (!objectp(pl) && !stringp(pl))
+    return -2;
+
+  if (objectp(pl) && !query_once_interactive(pl))
+    return -3;
+
+  if (objectp(pl))
+    spieler = getuid(pl);
+  else
+    spieler = pl;
+
+  if (!member(users_mq, spieler)) {
+    int mqpoints;
+    int p=-1;
+    string s = (MASTER->query_mq(spieler) || "");
+    while( (p=next_bit(s, p)) != -1) {
+      mqpoints+=by_num[p][0];
+    }
+    users_mq[spieler] = mqpoints;
+  }
+  return users_mq[spieler];
+}
+
+int HasMiniQuest(mixed pl, mixed name) {
+  string mq, spieler;
+
+  if (!pl || !name)
+    return MQ_ILLEGAL_OBJ;
+
+  if (!objectp(pl) && !stringp(pl))
+    return MQ_ILLEGAL_OBJ;
+
+  if (objectp(pl) && !query_once_interactive(pl))
+    return MQ_ILLEGAL_OBJ;
+
+  if (!objectp(name) && !stringp(name) && !intp(name))
+    return MQ_ILLEGAL_OBJ;
+
+  if (objectp(name))
+    name = explode(object_name(name), "#")[0];
+
+  if ( intp(name) )
+    name = by_num[name][1];
+
+  if (objectp(pl))
+    spieler = getuid(pl);
+  else
+    spieler = pl;
+
+  if (!member(miniquests,name))
+    return MQ_KEY_INVALID;
+
+  mq = (MASTER->query_mq(spieler) || "");
+
+  return test_bit(mq, miniquests[name, MQ_DATA_QUESTNO]);
+}
+
+// Zum Von-Hand-Setzen der MiniQuests
+int SetPlayerMiniQuest(string pl, string name) {
+  if(!allowed_write_access())
+    return 0;
+  if(!pl)
+    return MQ_ILLEGAL_OBJ;
+  if(!previous_object())
+    return MQ_ILLEGAL_OBJ;
+
+  if (!member(miniquests,name))
+    return MQ_KEY_INVALID;
+
+  string mq = (MASTER->query_mq(pl) || "");
+
+  if (test_bit(mq, miniquests[name,MQ_DATA_QUESTNO]))
+    return MQ_ALREADY_SET;
+
+  catch (mq = set_bit(mq, miniquests[name, MQ_DATA_QUESTNO]);publish);
+  MASTER->update_mq(pl, mq);
+
+  MQMLOG(sprintf("SetPlayerMiniQuest: %s %s (%s)",
+                 pl, name, getuid(this_interactive())));
+  // Spielereintrag aus dem MQ-Punkte-Cache loeschen
+  m_delete(users_mq, pl);
+  return 1;
+}
+
+int ClearPlayerMiniQuest(string pl, string name) {
+  if (!allowed_write_access())
+    return 0;
+  if (!pl)
+    return MQ_ILLEGAL_OBJ;
+  if (!previous_object())
+    return MQ_ILLEGAL_OBJ;
+
+  if (!member(miniquests,name))
+    return MQ_KEY_INVALID;
+
+  string mq = (MASTER->query_mq(pl) || "");
+
+  if (!test_bit(mq, miniquests[name, MQ_DATA_QUESTNO]))
+    return MQ_ALREADY_SET;
+
+  catch (mq = clear_bit(mq, miniquests[name, MQ_DATA_QUESTNO]);publish);
+  MASTER->update_mq(pl, mq);
+
+  MQMLOG(sprintf("ClearPlayerMiniQuest: %s %s (%s)",
+                 pl, name, getuid(this_interactive())));
+  // Spielereintrag aus dem MQ-Punkte-Cache loeschen
+  m_delete(users_mq, pl);
+  return 1;
+}
+
+// Umtragen von Miniquests von Objekt <old> auf Objekt <new>
+int MoveMiniQuest(string old_mqob, string new_mqob) {
+  if ( !allowed_write_access() )
+    return -1;
+
+  // Haben wir ueberhaupt einen solchen Eintrag?
+  if ( !member(miniquests, old_mqob) )
+    return -2;
+
+  // Pruefen, ob die als <new_mqob> angegebene Datei existiert.
+  if (new_mqob[<2..] == ".c")
+    new_mqob = new_mqob[0..<3];
+  new_mqob = explode(new_mqob, "#")[0];
+  new_mqob = (string)"/secure/master"->_get_path(new_mqob,0);
+  if (file_size(new_mqob+".c") <= 0)
+    return -3;
+  // Wenn das neue Objekt schon eine MQ vergibt, kann es keine weitere
+  // annehmen.
+  if ( member(miniquests, new_mqob) )
+    return -4;
+
+  // Der Miniquestliste einen neuen Key "new" mit den Daten des alten Keys
+  // hinzufuegen. m_entry() liefert alle Values dazu als Array, und der
+  // flatten-Operator "..." uebergibt dessen Elemente als einzelne Parameter.
+  m_add(miniquests, new_mqob, m_entry(miniquests, old_mqob)...);
+  m_delete(miniquests, old_mqob);
+  // Nummern-Index auch umtragen, sonst koennen Funktionen wie zB
+  // QueryMiniQuestByNumber() die neue nicht finden.
+  by_num[miniquests[new_mqob,MQ_DATA_QUESTNO]][1] = new_mqob;
+  return 1;
+}
+
+#define FRA_BIB "/d/ebene/miril/fraternitas/room/bibliothek"
+
+// Erlaubt die Abfrage aller MQs einer bestimmten Region fuer die Bibliothek
+// der kleinen und grossen Heldentaten in der Fraternitas.
+// Gibt ein Mapping der Form ([ indexnummer : titel; erledigt_Beschreibung ])
+// zurueck.
+mapping QuerySolvedMQsByDomain(mixed pl, string region) {
+  if ( !objectp(pl) && !stringp(pl) && 
+       load_name(previous_object())!=FRA_BIB) /*|| !allowed_write_access())*/
+    return ([:2]);
+
+  mapping res = m_allocate(30,2);   // reicht vermutlich
+  // Die angegebene Region muss in der Spalte MQ_DATA_ASSIGNED_DOMAIN
+  // enthalten sein, und das abfragende Objekt muss die Fraternitas-Bib sein
+  foreach(string mqobj, int mqp, int index, string task, int vis, int act,
+    string title, string donedesc, mapping restr, string domain:
+    miniquests) {
+      // aktive MQs der angeforderten Region zusammentragen, die der Spieler
+      // bestanden hat.
+      if ( domain == region && act && HasMiniQuest(pl, mqobj) )
+        m_add(res, index, title, donedesc);
+  }
+  //DEBUG(sprintf("%O\n",res));
+  return res;
+}
+#undef FRA_BIB
+
+// Abfrage der noch offenen MQs des angegebenen Spielers.
+// Zurueckgegeben wird ein Mapping mit den Miniquest-Nummern als Keys und
+// den Aufgabenbeschreibungen als Values, oder ein leeres Mapping, falls
+// das abfragende Objekt keine Zugriffsberechtigung hat, oder das 
+// uebergebene Spielerobjekt keine offenen Miniquests mehr hat.
+mapping QueryOpenMiniQuestsForPlayer(object spieler) {
+  // map() etwas beschleunigen
+  closure chk_restr = symbol_function("check_restrictions",
+                                      "/std/restriction_checker");
+  // Cache-Eintrag fuer das abfragende Objekt holen
+  string *list = mq_query_permitted[load_name(previous_object())];
+  mapping res = ([:2]); 
+  
+  if (!pointerp(list) || !sizeof(list))
+    return res;
+  // Liste der MQ-Objekte umwandeln in deren MQ-Nummer plus 
+  // Aufgabenbeschreibung, sofern der Spieler die MQ noch nicht bestanden
+  // hat, aber die Voraussetzungen erfuellt.
+  foreach ( string mq_obj : list ) 
+  {
+    // Nur wenn der Spieler die MQ noch nicht hat, kann er ueberhaupt einen
+    // Tip dazu bekommen.
+    if ( !HasMiniQuest(spieler, mq_obj) ) {
+      // Restriction Checker fragen, ob der Spieler statt des Hinweises
+      // eine Info bekommen muss, dass er eine Vorbedingung nicht erfuellt.
+      string restr_result = funcall(chk_restr, spieler, miniquests[mq_obj,
+        MQ_DATA_RESTRICTIONS]);
+      // Wenn so eine Info dabei rauskommt, wird diese in die Ergebnisliste
+      // uebernommen. In diesem Fall wird KEIN MQ-Hinweistext ausgeliefert.
+      if ( stringp(restr_result) )
+      {
+        m_add(res, miniquests[mq_obj,MQ_DATA_QUESTNO], 0, restr_result);
+      }
+      // Anderenfalls wird der Hinweistext uebernommen; einen Eintrag 
+      // bzgl. eines eventuellen Hinderungsgrundes gibt's dann nicht.
+      else
+      {
+        m_add(res, miniquests[mq_obj,MQ_DATA_QUESTNO], 
+          miniquests[mq_obj,MQ_DATA_TASKDESC], 0);
+      }
+    }
+  }
+  // Ergebnisliste zurueckgeben.
+  return res;
+}
+
+// Datenkonverter fuer das bisherige MQ-Mapping von
+// ([ obj : ({daten1..daten5}) ]) nach
+// ([ obj : daten1; daten2; ...; daten9 ]), wobei daten6..9 als Leerspalten
+// erzeugt werden.
+/*void ConvertMQData() {
+  if ( !allowed_write_access() )
+    return;
+
+  by_num=([]);
+  // spaltenweise aus dem miniquests-Mapping ein Array aus Arrays erzeugen
+  // Zunaechst die Keys des Mappings aufnehmen.
+  mixed *mqdata = ({m_indices(miniquests)});
+
+  // Dann das Datenarray spaltenweise dazu (jedes Array ist eine Spalte).
+  mqdata += transpose_array(m_values(miniquests));
+  // 1. Array: Keys, alle weiteren jeweils eine Wertespalte
+
+  // Array erzeugen als Array aus 5 Array-Elementen, die alle soviele
+  // Nullen enthalten, wie es Miniquests gibt,
+  // ({ ({0,0,...}), ({0,0,...}), ({0,0,...}), ({0,0,...}), ({0,0,...}) })
+  // dieses hinzufuegen. Sind die hinzukommenden 5 Spalten.
+  mqdata += allocate(5, allocate(sizeof(miniquests),0));
+
+  // Mapping erzeugen, indem mit dem flatten-Operator "..." die Einzel-
+  // Arrays des Datenpakets uebergeben werden. Erzeugt auf diese Weise
+  // ([ keys : daten1; daten2; daten3; daten4; daten5; 0; 0; 0; 0; 0,])
+  miniquests=mkmapping(mqdata...);
+}
+
+// Neue Daten einlesen, Visible-Flag bei allen MQs per Default auf 1 setzen.
+// Es gibt keine wirklich unsichtbaren MQs mehr.
+void ReadNewData() {
+  if ( !allowed_write_access() )
+    return;
+
+  string *import = explode(read_file("/players/arathorn/mqdata"),"\n")-({""});
+  string *fields;
+  foreach(string mqdata : import) {
+    fields = explode(mqdata, "#")-({""});
+    DEBUG(sprintf("%O\n", fields[0]));
+    if ( miniquests[fields[4], MQ_DATA_QUESTNO] != to_int(fields[0]) ) {
+      raise_error("MQ-Nummern stimmen nicht ueberein!\n");
+      return;
+    }
+    // fields[4] ist der MQ-Objektpfad
+    miniquests[fields[4], MQ_DATA_TITLE] = fields[7];
+    miniquests[fields[4], MQ_DATA_DIARYTEXT] = fields[6];
+    miniquests[fields[4], MQ_DATA_TASKDESC] = fields[5];
+    miniquests[fields[4], MQ_DATA_VISIBLE] = 1; // Default: visible
+    miniquests[fields[4], MQ_DATA_ASSIGNED_DOMAIN] = fields[8];
+    miniquests[fields[4], MQ_DATA_QUERY_PERMITTED] = fields[9];
+    if ( fields[3] != "0" ) {
+      miniquests[fields[4], MQ_DATA_RESTRICTIONS] =
+        restore_value(fields[3]+"\n");
+    }
+    else miniquests[fields[4], MQ_DATA_RESTRICTIONS] = 0;
+    if ( fields[9] != "0" )
+      miniquests[fields[4], MQ_DATA_QUERY_PERMITTED] =
+        restore_value(fields[9]+"\n");
+    else miniquests[fields[4], MQ_DATA_QUERY_PERMITTED] = 0;
+  }
+}
+*/
diff --git a/secure/questmaster.h b/secure/questmaster.h
new file mode 100644
index 0000000..1cf61aa
--- /dev/null
+++ b/secure/questmaster.h
@@ -0,0 +1,132 @@
+// MorgenGrauen MUDlib
+//
+// questmaster.h -- header file for questmaster object
+//
+// $Id: questmaster.h 8261 2012-12-04 22:56:29Z Zesstra $
+
+#ifndef __QUESTMASTER_H__
+#define __QUESTMASTER_H__
+
+//XP threshold  
+
+#define XP_NEEDED_FOR_WIZ 1000000
+
+// the questmaster
+#define QM         "/secure/questmaster"
+
+// the quests file
+#define QUESTS        "/secure/ARCH/questmaster"
+
+// Dumpfile for MiniQuests
+#define MQ_DUMP_FILE  "/secure/ARCH/MINIQUESTS.dump"
+
+// percentage of max_QP which is needed to become wizard
+#define QP_PERCENT 80
+
+
+// minimum of QP to become Seer or Wizzard
+#define QP_MIN 1196
+
+
+// log to file
+
+#define QMLOG(x) write_file("/log/QUESTMASTER",dtime(time())+": "+\
+        geteuid(this_interactive())+" -> "+ x +"\n")
+
+#define MQMLOG(x) write_file("/log/ARCH/MQUESTMASTER", \
+        dtime(time())+": "+(this_interactive()?geteuid(this_interactive()):"UNKNOWN")+\
+	" -> "+ x +"\n")
+
+#define MQSOLVEDLOG(x) write_file("/log/ARCH/MQ_SOLVED", \
+        dtime(time())+" "+x+"\n")
+
+// quest groups
+
+#define QGROUP_1 8
+#define QGROUP_2 18
+#define QGROUP_3 29
+#define QGROUP_S -1
+
+#define QGROUPS ({0,QGROUP_1,QGROUP_2, QGROUP_3})
+
+
+// Welcher Eintrag im Array steht fuer was?
+#define Q_QP      0
+#define Q_XP      1
+#define Q_ALLOWED 2
+#define Q_HINT    3
+#define Q_DIFF    4
+#define Q_CLASS   5
+#define Q_ACTIVE  6
+#define Q_WIZ     7
+#define Q_SCNDWIZ 8
+#define Q_ATTR    9
+#define Q_AVERAGE 10
+
+// Sternchen fuer die Klasse
+
+#define QCLASS_STARS(n) funcall(lambda(  ({ 'x, 's }),  \
+  ({#'while, ({#'>, 'x, 0}), \
+             's, ({#', ,({#'=, 'x, ({#'-, 'x, 1}) }),  \
+                        ({#'=, 's, ({#'+, 's, "*" }) }) \
+     }) }) ), n, "") /* ' */ 
+
+
+// Attribute der Quests
+#define QATTR_FLEISSIG  1
+#define QATTR_HEROISCH  2
+#define QATTR_EPISCH    3
+#define QATTR_LEGENDAER 4
+
+#define QATTR_STRINGS ([0: "", \
+                        QATTR_FLEISSIG: "fleissig", \
+                        QATTR_HEROISCH:  "heroisch", \
+                        QATTR_EPISCH: "episch", \
+                        QATTR_LEGENDAER: "legendaer"])
+
+
+
+// Rueckgabewerte Quests
+
+#define OK              1
+
+#define GQ_ALREADY_SET -1
+#define GQ_KEY_INVALID -2
+#define GQ_ILLEGAL_OBJ -3
+#define GQ_IS_INACTIVE -4
+
+#define DQ_NOT_SET     -1
+#define DQ_KEY_INVALID -2
+#define DQ_ILLEGAL_OBJ -3
+
+#define QQ_GUEST       2
+#define QQ_KEY_INVALID 0
+
+#define ERRNO_2_STRING(x,y) (["GQ1" : "Ok.",\
+			      "GQ-1": "Die Quest ist bereits geloest worden.",\
+			      "GQ-2": "Ungueltiger Questname.",\
+			      "GQ-3": "Unbefugter Zugriff.",\
+			      "GQ-4": "Die Quest ist derzeit inaktiv.",\
+			      "DQ1" : "Ok.",\
+			      "DQ-1": "Die Quest war nicht gesetzt.",\
+			      "DQ-2": "Ungueltiger Questname.",\
+			      "DQ-3": "Unbefugter Zugriff.",\
+			      "QQ1" : "Ok.",\
+                              "QQ2" : "Gaeste koennen keine Quest loesen.",\
+			      "QQ0" : "Ungueltiger Questname.",\
+			      ])[x+y]
+
+
+// Miniquests
+
+#define MQ_ALREADY_SET   -1
+#define MQ_KEY_INVALID   -2
+#define MQ_ILLEGAL_OBJ   -3
+#define MQ_IS_INACTIVE   -4
+#define MQ_GUEST         -5
+
+
+#endif /* __QUESTMASTER_H__ */
+
+
+
diff --git a/secure/repmaster.c b/secure/repmaster.c
new file mode 100644
index 0000000..8ac817d
--- /dev/null
+++ b/secure/repmaster.c
@@ -0,0 +1,329 @@
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma warn_deprecated
+
+#include <reputation.h>
+#include <wizlevels.h>
+
+/* Datenstruktur reputations:
+ * (["id": (["uids": ({ "uid1", "uid2" }), "name":"", "changemsg":"",  
+ *           "flags":x|x|x
+ *         ]) 
+ * ])
+ * Bsp:
+ * (["schaedlspalta": (["uids": ({"d.wald.nibel", "d.wald.kessa"}),
+ *                      "name": "Schaedlspalta-Klan", "active": 1,
+ *                      "changemsg":"beim Schaedlspalta-Klan" 
+ *                    ])
+ * ])
+ */
+
+private mapping reputations;
+
+protected void create() {
+  seteuid(getuid(this_object()));
+
+  if(!restore_object(REP_SAVEFILE) || !mappingp(reputations)) 
+    reputations = ([ ]);
+}
+
+// Zugriff auf Handlingmethoden fuer ROOT, EMs+
+private int Sec() {
+  if(process_call()) return 0;
+  if(previous_object() && geteuid(previous_object()) == ROOTID) return 1;
+  return objectp(previous_object()) && ARCH_SECURITY;
+}
+
+
+/* Argumente:
+ * repid = id im mapping reputations
+ * validuids = UIDs welche die Reputation veraendern duerfen, String
+ *             oder String-Array
+ * name = Name der Reputationsfraktion 
+ * changemsg = Text der in die Default-Changemsg eingefuegt wird
+ *             (Dein Ansehen %s hat sich ... verbessert.)
+ *
+ * Returns:
+ *  1 = Reputation erfolgreich hinzugefuegt
+ * -1 = Keine Zugriffsrechte
+ * -2 = Falsche Argumente
+ * -3 = Reputation bereits vorhanden
+ */
+public int AddReputation(string repid, mixed validuids, string name,
+                         string changemsg) {
+  if(!Sec()) 
+    return -1;
+  if(stringp(validuids)) validuids = ({ validuids });
+  if(!pointerp(validuids)) validuids = ({ });
+  // validuids + changemsgs kann auch erstmal leer bleiben
+  if(!stringp(repid) || !sizeof(repid) || !stringp(name) || !sizeof(name))
+    return -2;
+  if(member(reputations, repid))
+    return -3;
+  reputations += ([ repid : ([ 
+    "uids": validuids,
+    "name": name,
+    "flags": REP_FLAG_ACTIVE,
+    "changemsg": changemsg
+  ]) ]);
+  save_object(REP_SAVEFILE);
+  tell_object(this_interactive(), sprintf("Reputation eingetragen:\n"
+    "Name: %s\nValid UIDs: %O\nFlags: REP_FLAG_ACTIVE\nChangemsg: %s\n",
+    reputations[repid]["name"], reputations[repid]["uids"],
+    reputations[repid]["changemsg"]));
+  return 1;
+}
+
+/* Argumente:
+ * repid = id im mapping reputations
+ *
+ * Returns:
+ *  1 = wurde erfolgreich deaktiviert
+ * -1 = Keine Zugriffsrechte
+ * -2 = Falsche Argumente
+ * -3 = Rep nicht vorhanden
+ * -4 = Ist bereits deaktiviert
+ */
+public int DeactivateReputation(string repid) {
+  if(!Sec())
+    return -1;
+  if(!stringp(repid) || !sizeof(repid))
+    return -2;
+  if(!member(reputations, repid))
+    return -3;
+  if(!(reputations[repid]["flags"] & REP_FLAG_ACTIVE)) 
+    return -4;
+  reputations[repid]["flags"] |= REP_FLAG_ACTIVE;
+  save_object(REP_SAVEFILE);
+  return 1;
+}
+
+/* Argumente:
+ * repid = id im mapping reputations
+ *
+ * Returns:
+ *  1 = wurde erfolgreich aktiviert
+ * -1 = Keine Zugriffsrechte
+ * -2 = Falsche Argumente
+ * -3 = Rep nicht vorhanden
+ * -4 = Ist bereits aktiviert
+ */
+public int ActivateReputation(string repid) {
+  if(!Sec())
+    return -1;
+  if(!stringp(repid) || !sizeof(repid))
+    return -2;
+  if(!member(reputations, repid))
+    return -3;
+  if(reputations[repid]["flags"] & REP_FLAG_ACTIVE) 
+    return -4;
+  reputations[repid]["flags"] ^= REP_FLAG_ACTIVE;
+  save_object(REP_SAVEFILE);
+  return 1;
+}
+
+/* Argumente:
+ * repid = id im mapping reputations
+ * uid = UID welche die Reputation veraendern darf
+ *
+ * Returns:
+ *  1 = UID erfolgreich hinzugefuegt
+ * -1 = Keine Zugriffsrechte
+ * -2 = Falsche Argumente
+ * -3 = Rep nicht vorhanden
+ * -4 = UID ist bereits vorhanden
+ */
+public int AddReputationUid(string repid, string uid) {
+  if(!Sec())
+    return -1;
+  if(!stringp(repid) || !sizeof(repid) || !stringp(uid) || !sizeof(uid))
+    return -2;
+  if(!member(reputations, repid))
+    return -3;
+  if(member(reputations[repid]["uids"], uid) != -1) 
+    return -4;
+  reputations[repid]["uids"] += ({ uid });
+  save_object(REP_SAVEFILE);
+  return 1;
+}
+
+/* Argumente:
+ * repid = id im mapping reputations
+ * uid = UID welche die Reputation nicht mehr veraendern darf
+ *
+ * Returns:
+ *  1 = UID erfolgreich geloescht
+ * -1 = Keine Zugriffsrechte
+ * -2 = Falsche Argumente
+ * -3 = Rep nicht vorhanden
+ * -4 = UID nicht vorhanden
+ */
+public int RemoveReputationUid(string repid, string uid) {
+  if(!Sec())
+    return -1;
+  if(!stringp(repid) || !sizeof(repid) || !stringp(uid) || !sizeof(uid))
+    return -2;
+  if(!member(reputations, repid))
+    return -3;
+  if(member(reputations[repid]["uids"], uid) == -1) 
+    return -4;
+  reputations[repid]["uids"] -= ({ uid });
+  save_object(REP_SAVEFILE);
+  return 1;
+}
+
+/* Argumente:
+ * repid = id im mapping reputations
+ * name = Neuer Name der Rep
+ *
+ * Returns:
+ *  1 = Neuer Name erfolgreich gesetzt
+ * -1 = Keine Zugriffsrechte
+ * -2 = Falsche Argumente
+ * -3 = Rep nicht vorhanden
+ * -4 = Name entspricht dem derzeitigen
+ */
+public int ChangeReputationName(string repid, string name) {
+  if(!Sec())
+    return -1;
+  if(!stringp(repid) || !sizeof(repid) || !stringp(name) || !sizeof(name))
+    return -2;
+  if(!member(reputations, repid))
+    return -3;
+  if(reputations[repid]["name"] == name) 
+    return -4;
+  reputations[repid]["name"] = name;
+  save_object(REP_SAVEFILE);
+  return 1;
+}
+
+/* Argumente:
+ * repid = id im mapping reputations
+ * msg = Neuer String in der Default-Changemsg
+ *
+ * Returns:
+ *  1 = Neue Msg erfolgreich gesetzt
+ * -1 = Keine Zugriffsrechte
+ * -2 = Falsche Argumente
+ * -3 = Rep nicht vorhanden
+ * -4 = Msg entspricht der derzeitigen
+ */
+public int ChangeReputationMsg(string repid, string msg) {
+  if(!Sec())
+    return -1;
+  if(!stringp(repid) || !sizeof(repid) || !stringp(msg) || !sizeof(msg))
+    return -2;
+  if(!member(reputations, repid))
+    return -3;
+  if(reputations[repid]["changemsg"] == msg) 
+    return -4;
+  reputations[repid]["changemsg"] = msg;
+  save_object(REP_SAVEFILE);
+  return 1;
+}
+
+/* Argumente:
+ * repid = id(s) im mapping reputations, ohne Argument werden alle aufgelistet,
+ *         String oder String-array
+ *
+ * Returns:
+ *  1 = Liste ausgegeben
+ * -1 = Falsche Argumente
+ * -2 = Rep nicht vorhanden
+ */
+public varargs int ViewReputationData(mixed repids) {
+  // Fuer alle sichtbar, damit man ggf. weiss, wo man nachfragen muss
+  if(!repids) repids = m_indices(reputations);
+  else if(stringp(repids)) repids = ({ repids });
+  if(!pointerp(repids) || !sizeof(repids))
+    return -1;
+  // Textausgabe fuer alle richtigen IDs
+  foreach(string repid : repids) {
+    if(!member(reputations, repid)) continue;
+    tell_object(this_interactive(), sprintf("%s %s\n  Name: %s\n  Changemsg: "
+      "%s\n  ValidUIDS: %O\n\n", repid, 
+      (reputations[repid]["flags"] & REP_FLAG_ACTIVE ? "" :
+      "(Inaktiv)"), reputations[repid]["name"], 
+      reputations[repid]["changemsg"], reputations[repid]["uids"]));
+  }
+  // Min. 1 nicht vorhanden: return -2, sonst 1
+  return (sizeof(repids - m_indices(reputations)) ? -2 : 1);
+}
+
+/* Argumente:
+ * repid = id im mapping reputations
+ *
+ * Returns:
+ * mapping = Daten der Reputation
+ * 0 = Falsches Argument / Reputation nicht vorhanden
+ */
+public mapping GetReputationData(string repid) {
+  if(!stringp(repid) || !sizeof(repid) || !member(reputations, repid))
+    return 0;
+  return deep_copy(reputations[repid]);
+}
+
+/* Argumente:
+ * repid = id im mapping reputations
+ * value = Wert um den die Rep veraendert wird (+ / -)
+ *
+ * Returns:
+ * string = "Dein Ansehen %s hat sich ... .\n"
+ * 0 = Falsche Argumente / Rep nicht vorhanden
+ */
+public string GetDefaultChangeMsg(string repid, int value) {
+  string strength;
+  if(!stringp(repid) || !sizeof(repid) || !intp(value) || !value ||
+     !member(reputations, repid))
+    return 0;
+  switch(abs(value)) {
+    case    0..25  : strength = "kaum merklich"; break;
+    case   26..50  : strength = "leicht"; break;
+    case   51..100 : strength = "deutlich"; break;
+    case  101..250 : strength = "erheblich"; break;
+    case  251..1000: strength = "sehr stark"; break;
+    default:         strength = "ausserordentlich stark"; break;
+  }
+  return sprintf("Dein Ansehen %s hat sich%s%s.\n", 
+    reputations[repid]["changemsg"], (!strength ? " " : " "+ strength +" "),
+    (value < 0 ? "verschlechtert" : "verbessert"));
+}
+
+/* Argumente:
+ * repid = id im mapping reputations
+ * ob = Objekt, dessen UID geprueft wird, ohne Argument -> prev_ob()
+ *
+ * Returns:
+ * 1 = UID valid
+ * 0 = UID invalid
+ * -1 = Falsche Argumente 
+ * -2 = Rep nicht vorhanden 
+ */
+public int CheckValidUid(string repid, object ob) {
+  if(!stringp(repid) || !sizeof(repid) || !objectp(ob))
+    return -1;
+  if(!member(reputations, repid))
+    return -2;
+  return (member(reputations[repid]["uids"], getuid(ob)) != -1);
+}
+
+/* Argumente:
+ * repid = id im mapping reputations
+ *
+ * Returns:
+ * 1 = Rep aktiv
+ * 0 = Rep inaktiv
+ * -1 = Falsche Argumente 
+ * -2 = Rep nicht vorhanden 
+ */
+public int CheckRepActive(string repid) {
+  if(!stringp(repid) || !sizeof(repid))
+    return -1;
+  if(!member(reputations, repid))
+    return -2;
+  // Verboolung
+  return !!(reputations[repid]["flags"] & REP_FLAG_ACTIVE);
+}
diff --git a/secure/scoremaster.c b/secure/scoremaster.c
new file mode 100644
index 0000000..576a4b9
--- /dev/null
+++ b/secure/scoremaster.c
@@ -0,0 +1,1465 @@
+// MorgenGrauen MUDlib
+//
+// scoremaster.c - Verwaltung der eindeutigen Nummernvergabe fuer NPCs und
+//       MiniQuests sowie der Stufenpunkte, die sie geben  
+//
+// $Id: scoremaster.c 9170 2015-03-05 20:18:54Z Zesstra $
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#include "/secure/scoremaster.h"
+#include "/secure/wizlevels.h"
+#include <properties.h>
+#include <files.h>
+
+#define ZDEBUG(x) if (find_player("zesstra")) \
+  tell_object(find_player("zesstra"),sprintf("SCM: %s\n",x))
+
+// hoechste vergebene Nr.
+private int lastNum;
+
+// Liste alle EKs: ([obname: num; score; killcount])
+private mapping npcs = m_allocate(0,3);
+
+// Liste von Spielernamen-Wert-Paaren, die im Reset abgearbeitet wird:
+// ([plname: ({wert1, wert2, wert3, ...}) ]) 
+// wert > 0 bedeutet setzen des entsprechenden EKs, < 0 bedeutet loeschen.
+private mapping to_change = ([]);
+
+// Liste der EK-Tips: ([obname: Spruch])
+private mapping tipList = ([]);
+
+// Bit-Nr., die (wieder) vergeben werden duerfen.
+private int *free_num = ({});
+
+// zu entfernende EKs, Liste Bitnummern, also ints
+private int *to_be_removed = ({});
+
+// Liste von temporaeren EKs, die noch nicht bestaetigt wurden:
+// ([obname: ({plname1, plname2}) ])
+private mapping unconfirmed_scores = ([]);
+
+// alle Spieler kriegen diesen 
+// Nach Nr. sortierte NPC-Liste: ([num: key; score])
+private nosave mapping by_num = m_allocate(0,2);
+
+// Cache fuer EKs von Spielern: ([plname: scoresumme])
+private nosave mapping users_ek = ([]);
+
+// bitstring, der alle aktiven EKs als gesetztes Bit enthaelt.
+private nosave string active_eks="";
+
+// Prototypen
+public mapping getFreeEKsForPlayer(object player);
+public int addTip(mixed key,string tip);
+public int changeTip(mixed key,string tip);
+public int removeTip(mixed key);
+private string getTipFromList(mixed key);
+public string getTip(mixed key);
+
+public void CheckNPCs(int num);
+public void check_all_player(mapping allplayer);
+public varargs int DumpNPCs(int sortkey);
+
+private void make_num(string key, int num, int score) {
+  by_num += ([ num : key; score ]);
+  // fuer aktive EKs, die also einen Scorewert > 0 haben, wird das jeweilige
+  // Bit gesetzt. Wird spaeter zum Ausfiltern inaktiver EKs aus den Bitstrings
+  // in den Spieler gebraucht.
+  if (score>0 && !member(unconfirmed_scores,num))
+    active_eks = set_bit(active_eks, num);
+}
+
+private int allowed()
+{
+  if (previous_object() && geteuid(previous_object())==ROOTID)
+    return 1;
+  if (!process_call() && previous_object() && this_interactive() && ARCH_SECURITY)
+    return 1;
+  return 0;
+}
+
+protected void create()
+{
+  seteuid(getuid());
+  if (!restore_object(SCORESAVEFILE))
+  {
+    lastNum=0;
+    npcs=m_allocate(0,3);
+    to_change=m_allocate(0,1);
+    tipList=([]);
+  }
+  npcs-=([0]);
+  walk_mapping(npcs, #'make_num);
+}
+
+public int ClearUsersEKCache()
+{
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  users_ek = ([]);
+  return 1;
+}
+
+public mixed QueryUsersEKCache()
+{
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  return users_ek;
+}
+
+public mixed Query_free_num()
+{
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  return free_num;
+}
+
+public mixed Add_free_num(int what)
+{
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  if (!what || !intp(what) || by_num[what])
+    return SCORE_INVALID_ARG;
+  if (member(free_num,what)==-1)
+    free_num+=({what});
+  save_object(SCORESAVEFILE);
+  write_file(SCORELOGFILE,sprintf("ADDFREENUM: %s %5d (%s, %O)\n",     
+  strftime("%d%m%Y-%T",time()),what,
+  geteuid(previous_object()), this_interactive()));
+
+  return free_num;
+}
+
+public mixed Remove_free_num(int what)
+{
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  if (!what || !intp(what))
+    return SCORE_INVALID_ARG;
+  free_num-=({what});
+  save_object(SCORESAVEFILE);
+  write_file(SCORELOGFILE,sprintf("REMOVEFREENUM: %s %5d (%s, %O)\n",     
+  strftime("%d%m%Y-%T",time()),what,
+  geteuid(previous_object()),this_interactive()));
+  return free_num;
+}
+
+public mixed Query_to_change(string who)
+{
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  if (!who)
+    return to_change;
+  if (who=="")
+    return m_indices(to_change);
+  return to_change[who];
+}
+
+public mixed Add_to_change(string who, int what)
+{
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  if (!who || !stringp(who) || !what || !intp(what))
+    return SCORE_INVALID_ARG;
+  if (member(to_change,who))
+  {
+    to_change[who]-=({-what});
+    if (member(to_change[who],what)==-1)
+      to_change[who]+=({what});
+  }
+  else
+     to_change[who]=({what});
+  save_object(SCORESAVEFILE);
+  write_file(SCORELOGFILE,sprintf("ADDTOCHANGE: %s %s %5d (%s, %O)\n",
+         strftime("%d%m%Y-%T",time()),who,what,
+         geteuid(previous_object()), this_interactive()));
+  return to_change[who];
+}
+
+public mixed Remove_to_change(string who, int what)
+{
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  if (!who || !stringp(who) || !what || !intp(what))
+    return SCORE_INVALID_ARG;
+  if (member(to_change,who))
+  {
+     to_change[who]-=({what});
+     if (!sizeof(to_change[who]))
+        m_delete(to_change,who);
+  }
+  save_object(SCORESAVEFILE);
+  write_file(SCORELOGFILE,sprintf("REMOVETOCHANGE: %s %s %5d (%s, %O)\n",
+         strftime("%d%m%Y-%T",time()),who,what,
+         geteuid(previous_object()), this_interactive()));
+  return to_change[who];
+}
+
+void reset()
+{
+  string *whop,who,ek;
+  mixed what;
+  int i,j,value,changed;
+
+  // falls EKs global entfernt werden sollen, schonmal den noetigen Callout
+  // starten.
+  if (sizeof(to_be_removed) && find_call_out(#'check_all_player) == -1)
+      call_out(#'check_all_player, 10, 0);
+  // EK-Mainteiner ueber unbestaetigte EKs informieren
+  if (sizeof(unconfirmed_scores)) {
+    foreach(string n: SCOREMAINTAINERS) {
+      if (objectp(find_player(n)))
+          tell_object(find_player(n),break_string(
+      "Es gibt unbestaetigte EKs im Scoremaster. Schau Dir die doch "
+      "mal an. ;-)",78, "Der Scoremaster teilt Dir mit: "));
+    }
+  }
+
+  i=sizeof(whop=m_indices(to_change))-1;
+  while (i>=0 && get_eval_cost()>100000)
+  {
+    ek = (string)(MASTER->query_ek(who=whop[i]) || "");
+    for (j=sizeof(what=to_change[who])-1;j>=0;j--) {
+      if ((value=what[j])>0) {
+    // Vergabestatistik hochzaehlen.
+    npcs[by_num[value,BYNUM_KEY],NPC_COUNT]++;
+    ek=set_bit(ek,value);
+      }
+      else {
+    // Vergabestatistik hochzaehlen.
+    npcs[by_num[-value,BYNUM_KEY],NPC_COUNT]++;
+    ek=clear_bit(ek,-value);
+      }
+      // if (find_player("rikus")) 
+      //tell_object(find_player("rikus"),"SCOREMASTER "+who+" "+erg+"\n");
+
+      write_file(SCOREAUTOLOG,
+  sprintf("SET_CLEAR_BIT (reset): %s %4d %s\n",
+    who, j, strftime("%d%m%Y-%T",time()) ));
+    }
+    MASTER->update_ek(who, ek);
+
+    if (member(users_ek, who))
+      m_delete(users_ek, who);
+    
+    m_delete(to_change,who);
+    changed=1;
+    i--;
+  }
+  if (changed) save_object(SCORESAVEFILE);
+}
+
+public varargs mixed QueryNPC(int score)
+{
+  string key;
+  int val;
+
+  if (!previous_object())
+    return SCORE_INVALID_ARG;
+  
+  key = load_name(previous_object());
+
+  // schon bekannter EK?
+  if (member(npcs,key))
+    return ({npcs[key,NPC_NUMBER],npcs[key,NPC_SCORE]});
+
+  if (score<=0 || 
+      member(inherit_list(previous_object()),"/std/living/life.c") < 0)
+    return SCORE_INVALID_ARG;
+
+  if (key[0..8]=="/players/") return SCORE_INVALID_ARG;
+
+  if (score>2) score=2;
+
+  if (sizeof(free_num)) {
+      val = free_num[0];
+      free_num -= ({val});
+  }
+  else val=++lastNum;
+
+  npcs[key,NPC_SCORE] = score;
+  npcs[key,NPC_NUMBER] = val;
+  npcs[key,NPC_COUNT] = 0;
+  by_num += ([val: key; score]);
+  // werden noch nicht als aktive EKs gewertet, damit sie nicht als Ek-Tips
+  // vergben werden.
+  //active_eks = set_bit(active_eks, val);
+
+  unconfirmed_scores += ([ val: ({}) ]);
+
+  ClearUsersEKCache();
+  save_object(SCORESAVEFILE);
+  write_file(SCOREAUTOLOG,sprintf(
+  "ADDNPC: %s %5d %4d %s (UID: %s, TI: %O, TP: %O)\n",
+  strftime("%d%m%Y-%T",time()),val,score,key,
+  getuid(previous_object()), this_interactive(), this_player()));
+
+  while(remove_call_out("DumpNPCs") != -1) ;
+  call_out("DumpNPCs",60);
+  return ({val,score});
+}
+
+public varargs mixed NewNPC(string key,int score)
+{
+  int val;
+
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  if (!key || !stringp(key))
+    return SCORE_INVALID_ARG;
+  
+  key = load_name(key);
+  if (val=npcs[key,NPC_NUMBER])
+    return ({val,npcs[key,NPC_SCORE]});
+  if (score<=0)
+    return SCORE_INVALID_ARG;
+
+  if (sizeof(free_num)) {
+      val=free_num[0];
+      free_num=free_num[1..];
+  }
+  else val=++lastNum;
+
+  npcs[key,NPC_SCORE] = score;
+  npcs[key,NPC_NUMBER] = val;
+  npcs[key,NPC_COUNT] = 0;
+  by_num += ([val: key; score]);
+  active_eks = set_bit(active_eks, val);
+
+  ClearUsersEKCache();
+  save_object(SCORESAVEFILE);
+  write_file(SCORELOGFILE,sprintf("NEWNPC: %s %5d %4d %s (%s, %O)\n",
+         strftime("%d%m%Y-%T",time()),val,score,key,
+         geteuid(previous_object()), this_interactive()));
+ while(remove_call_out("DumpNPCs") != -1) ; 
+  call_out("DumpNPCs",60);
+  return ({val,score});
+}
+
+public varargs mixed AddNPC(string key,int score) { return NewNPC(key,score); }
+
+// restauriert die Daten eines frueher geloeschten, in den Spielern noch
+// enthaltenen EKs. Moeglich, wenn man Pfad, Nr. und Punkte noch kennt.
+public int RestoreEK(string key, int bit, int score) {
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  if (!stringp(key) || !sizeof(key) 
+      || !intp(bit) || bit < 0
+      || !intp(score) || score < 0)
+      return SCORE_INVALID_ARG;
+
+  if (member(npcs,key) || member(by_num,bit))
+      return SCORE_INVALID_ARG;
+
+  npcs += ([key: bit;score;0 ]);
+  by_num += ([bit: key;score ]);
+
+  ClearUsersEKCache();
+  save_object(SCORESAVEFILE);
+  write_file(SCORELOGFILE,sprintf("RESTOREEK: %s %5d %4d %s (%s, %O)\n",
+         strftime("%d%m%Y-%T",time()), bit, score, key,
+         geteuid(previous_object()), this_interactive()));
+  while(remove_call_out("DumpNPCs") != -1) ;
+  call_out("DumpNPCs",60);
+  return 1;
+
+}
+
+public int ConfirmScore(mixed key) {
+  // Bits in den Spielern in unconfirmed_scores setzen und Statistik
+  // hochzaehlen
+  // Bit in active_eks setzen
+  // Eintrag aus unconfirmed_scores loeschen
+  int bit;
+
+  if (!allowed()) return SCORE_NO_PERMISSION;
+  if (stringp(key) && member(npcs,key)) {
+      bit = npcs[key, NPC_NUMBER];
+  }
+  else if (intp(key) && member(by_num,key)) {
+      bit = key;
+  }
+  else
+      return SCORE_INVALID_ARG;
+
+  if (!member(unconfirmed_scores, bit)) 
+      return SCORE_INVALID_ARG;
+
+  string obname = by_num[bit, BYNUM_KEY];
+  int score = by_num[bit,BYNUM_SCORE];
+  
+  foreach(string pl: unconfirmed_scores[bit]) {
+      string eks = (string)master()->query_ek(pl);
+      eks = set_bit(eks, bit);
+      master()->update_ek(pl, eks);
+      write_file(SCOREAUTOLOG, sprintf(
+    "SETBIT: %s %5d %s\n",
+    strftime("%d%m%Y-%T",time()), bit, pl));
+  }
+  //Vergabestatistik hochzaehlen...
+  npcs[obname,NPC_COUNT]+=sizeof(unconfirmed_scores[bit]);
+
+  m_delete(unconfirmed_scores, bit);
+  active_eks = set_bit(active_eks, bit);
+  save_object(SCORESAVEFILE);
+
+  write_file(SCORELOGFILE,sprintf(
+      "CONFIRMNPC: %s %5d Score %3d %s [%s, %O]\n",
+       strftime("%d%m%Y-%T",time()), bit, score, obname,
+       getuid(previous_object()),this_interactive()));
+
+  return 1;
+}
+
+public int RejectScore(mixed key) {
+  // Eintrag aus unconfirmed_scores, npcs, by_num loeschen
+  // Bit-Nr. in free_num eintragen
+  // evtl. EK-Spruch entfernen?
+  int bit;
+
+  if (!allowed()) return SCORE_NO_PERMISSION;
+  if (stringp(key) && member(npcs,key)) {
+      bit = npcs[key, NPC_NUMBER];
+  }
+  else if (intp(key) && member(by_num,key)) {
+      bit = key;
+  }
+  else
+      return SCORE_INVALID_ARG;
+
+  if (!member(unconfirmed_scores, bit)) 
+      return SCORE_INVALID_ARG;
+
+  string obname = by_num[bit, BYNUM_KEY];
+  int score = by_num[bit,BYNUM_SCORE];
+
+  m_delete(by_num, bit);
+  m_delete(npcs, obname);
+  m_delete(unconfirmed_scores,bit);
+  removeTip(obname);
+  free_num += ({bit});
+
+  save_object(SCORESAVEFILE);
+
+  write_file(SCORELOGFILE,sprintf(
+      "REJECTNPC: %s %5d Score %3d %s [%s, %O]\n",
+       strftime("%d%m%Y-%T",time()), bit, score, obname,
+       getuid(previous_object()),this_interactive()));
+  return 1;
+}
+
+// unbestaetigte NPCs in ein File ausgeben
+public void DumpUnconfirmedScores() {
+  if (!objectp(this_player())) return;
+ 
+  write(sprintf("%5s  %5s  %4s   %s\n",
+  "Nr.", "Cnt", "Sc", "Objekt"));
+  foreach(int bit, string *pls: unconfirmed_scores) {
+    write(sprintf("%5d  %5d  %4d   %s\n",
+  bit, sizeof(pls), by_num[bit,BYNUM_SCORE], by_num[bit,BYNUM_KEY]));
+  }
+}
+
+public mapping _query_unconfirmed() {
+  if (allowed()) return unconfirmed_scores;
+  return 0;
+}
+
+public varargs int SetScore(mixed key,int score)
+{
+  int num;
+  string ob;
+  int oldscore;
+
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  if (!key) return SCORE_INVALID_ARG;
+
+  if (stringp(key) && sizeof(key)) {
+    ob = load_name(key);
+    if (!member(npcs, ob)) return SCORE_INVALID_ARG;
+    num = npcs[ob, NPC_NUMBER];
+    if (ob != by_num[num, BYNUM_KEY])
+  return SCORE_INVALID_ARG;
+  }
+  else if (intp(key) && member(by_num,key) ) {
+    num = key;
+    ob = by_num[num, BYNUM_KEY];
+    if (!member(npcs, ob) || (npcs[ob, NPC_NUMBER] != num))
+  return SCORE_INVALID_ARG;
+  }
+  else
+    return SCORE_INVALID_ARG;
+
+  oldscore = by_num[num,BYNUM_SCORE];
+  by_num[num,BYNUM_SCORE] = score;
+  npcs[ob, NPC_SCORE] = score;
+
+  if (score > 0)
+      active_eks = set_bit(active_eks, num);
+  else
+      active_eks = clear_bit(active_eks, num);
+
+  ClearUsersEKCache();
+  save_object(SCORESAVEFILE);
+  write_file(SCORELOGFILE,sprintf(
+  "SETSCORE: %s %5d %.3d OSc: %.3d %s (%s, %O)\n",
+         strftime("%d%m%Y-%T",time()),num,score,oldscore, ob,
+         geteuid(previous_object()), this_interactive()));
+ while(remove_call_out("DumpNPCs") != -1) ; 
+  call_out("DumpNPCs",60);
+  return 1;
+}
+
+// entfernt einen EK endgueltig und unwiderruflich und gibt die Nr. wieder
+// frei.
+// Technisch wird der EK erstmal in eine Liste eingetragen. Im Reset iteriert
+// der Master ueber alle SPieler-Savefiles und loescht den Ek aus alle
+// Spielern. Nach Abschluss wird der Eintrag in npcs geloescht und seine Nr.
+// in die Liste freier Nummern eingetragen.
+public int* MarkEKForLiquidation(mixed key) {
+  int bit;
+  if (!allowed())
+      return 0;
+  // nicht in to_be_removed aendern, wenn check_all_player() laeuft.
+  if (find_call_out(#'check_all_player) != -1)
+      return 0;
+
+  if (stringp(key) && sizeof(key)) {
+    if (!member(npcs,key)) return 0;
+    bit = npcs[key,NPC_NUMBER];
+  }
+  else if (intp(key) && key>=0) {
+    bit = key;
+  }
+  else
+    return 0;
+
+  if (member(to_be_removed,bit) == -1)
+    to_be_removed += ({bit});
+  write_file(SCORELOGFILE,sprintf("DELETEFLAG: %s %5d %s (%s, %O)\n",
+  strftime("%d%m%Y-%T",time()), bit, by_num[bit,BYNUM_KEY] || "NoName",
+  geteuid(previous_object()), this_interactive()));
+  
+  save_object(SCORESAVEFILE);
+  
+  return to_be_removed;
+}
+
+// geht nur, solange nach einem RemoveEK() noch kein reset() gelaufen ist!
+public int* UnmarkEKForLiquidation(mixed key) {
+  int bit;
+  if (!allowed())
+      return 0;
+  // nicht in to_be_removed aendern, wenn check_all_player() laeuft.
+  if (find_call_out(#'check_all_player) != -1)
+      return 0;
+
+  if (stringp(key) && sizeof(key)) {
+    if (!member(npcs,key)) return 0;
+    bit = npcs[key,NPC_NUMBER];
+  }
+  else if (intp(key) && key>=0) {
+    bit = key;
+  }
+  else
+    return 0;
+ 
+  to_be_removed -= ({bit});
+  write_file(SCORELOGFILE,sprintf("UNDELETEFLAG: %s %5d %s (%s, %O\n",
+  strftime("%d%m%Y-%T",time()),bit, by_num[bit, BYNUM_KEY] || "NoName",
+  geteuid(previous_object()), this_interactive()));
+  
+  save_object(SCORESAVEFILE);
+
+  return to_be_removed;
+}
+
+public int* QueryLiquidationMarks() {
+  if (allowed())
+      return to_be_removed;
+  else
+      return 0;;
+}
+
+// setzt nur den Scorewert auf 0, sonst nix. Solche EKs koennen dann spaeter
+// durch Angabe eines neues Scorewertes reaktiviert werden.
+public int RemoveScore(mixed key) {
+  int changed;
+  int oldscore;
+
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+
+  if (stringp(key) && member(npcs,key)) {
+    int num = npcs[key, NPC_NUMBER];
+    if ( key == by_num[num, BYNUM_KEY]) {
+      oldscore = by_num[num, BYNUM_SCORE];
+      npcs[key, NPC_SCORE] = 0;
+      by_num[num, BYNUM_SCORE] = 0;
+      active_eks = clear_bit(active_eks,num); 
+      write_file(SCORELOGFILE,sprintf(
+      "REMOVESCORE: %s %5d OSc: %.3d %s (%s, %O)\n",
+        strftime("%d%m%Y-%T",time()), num, oldscore, key, 
+        geteuid(previous_object()), this_interactive()));
+      changed = 1;
+    }
+  }
+  else if (intp(key) && member(by_num, key)) {
+    string obname = by_num[key, BYNUM_KEY];
+    if (key == npcs[obname, NPC_NUMBER]) {
+      oldscore = by_num[key, BYNUM_SCORE];
+      npcs[obname, NPC_SCORE] = 0;
+      by_num[key, BYNUM_SCORE] = 0;
+      active_eks = clear_bit(active_eks,key); 
+      write_file(SCORELOGFILE,sprintf(
+      "REMOVESCORE: %s %5d OSc: %.3d %s (%s, %O)\n",
+        strftime("%d%m%Y-%T",time()),key, oldscore, obname,
+        geteuid(previous_object()), this_interactive()));
+      changed = 1;
+    }
+  }
+
+  if (changed) {
+    ClearUsersEKCache();
+    save_object(SCORESAVEFILE);
+    while(remove_call_out("DumpNPCs") != -1) ;
+    call_out("DumpNPCs",60);
+    return 1;
+  }
+  return SCORE_INVALID_ARG;
+}
+
+public varargs int MoveScore(mixed oldkey, string newpath)
+{
+  int num,score;
+  string oldpath;
+  string tip;
+  
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  if (!stringp(newpath))
+    return SCORE_INVALID_ARG;
+
+  if (stringp(oldkey)) {
+    oldkey = load_name(oldkey); 
+    num=npcs[oldkey,NPC_NUMBER];
+  }
+  else if (intp(oldkey)) num=oldkey;
+  else return SCORE_INVALID_ARG;
+
+  if (!member(by_num,num)) return SCORE_INVALID_ARG;
+  
+  tip=getTipFromList(oldkey);
+  oldpath = by_num[num, BYNUM_KEY];
+  score = by_num[num, BYNUM_SCORE];
+
+  if (member(npcs, oldpath)) {
+    m_delete(npcs, oldpath);
+    removeTip(oldkey);
+    if(tip!="") addTip(newpath,tip);
+    npcs[newpath, NPC_SCORE] = score;
+    npcs[newpath, NPC_NUMBER] = num;
+  }
+  else return SCORE_INVALID_ARG;
+
+  by_num += ([num: newpath; score]);
+
+  ClearUsersEKCache();
+  save_object(SCORESAVEFILE);
+  write_file(SCORELOGFILE,sprintf("MOVESCORE: %s %s %s (%s, %O)\n",
+  strftime("%d%m%Y-%T",time()),oldpath,newpath,
+  geteuid(previous_object()),this_interactive()));
+
+  while(remove_call_out("DumpNPCs") != -1) ;
+  call_out("DumpNPCs",60);
+  return 1;
+}
+
+// liefert alle Kills des Spielers zurueck, auch solche, die momentan
+// ausgetragen/deaktiviet sind.
+public string QueryAllKills(string pl)
+{
+  return (MASTER->query_ek(pl) || "");
+}
+
+// filtert alle Eintraege aus dem Bitstring heraus, die fuer
+// ausgetragene/inaktive EKs stehen.
+public string QueryKills(string pl) {
+  string res = (string)MASTER->query_ek(pl) || "";
+  // vergleichen mit den aktiven EKs aus active_eks und nur jene Bits
+  // zurueckliefern, die in beiden Strings gesetzt sind.
+  return and_bits(res,active_eks);
+}
+
+public int QueryKillPoints(mixed pl) {
+  
+  if (!allowed() &&
+      (!previous_object() 
+       || strstr(object_name(previous_object()), "/gilden/") != 0) )
+     return 0;
+
+  if (!stringp(pl)) pl=getuid(pl);
+
+  if (member(users_ek,pl)) return users_ek[pl];
+
+  string s = (MASTER->query_ek(pl) || "");
+  
+  int p=-1;
+  int summe;
+  while ((p=next_bit(s,p)) != -1) {
+      summe+=by_num[p,BYNUM_SCORE];
+  }
+
+  users_ek += ([pl:summe]);
+  return summe;
+}
+
+public mixed *QueryNPCbyNumber(int num)
+{
+  if (!allowed())
+    return 0;
+
+  if (member(by_num, num))
+    return ({num, by_num[num, BYNUM_SCORE], by_num[num, BYNUM_KEY]});
+
+  return 0;
+}
+
+protected mixed *StaticQueryNPCbyNumber(int num)
+{
+  if (member(by_num, num))
+    return ({num, by_num[num, BYNUM_SCORE], by_num[num, BYNUM_KEY]});
+
+  return 0;
+}
+
+public mixed *QueryNPCbyObject(object o)
+{
+  string key;
+  int val;
+
+  key=load_name(o);
+  if (member(npcs,key)) {
+    val = npcs[key,NPC_NUMBER];
+    return ({val, by_num[val, BYNUM_SCORE], by_num[val, BYNUM_KEY]});
+  }
+  return 0;
+}
+
+public int GiveKill(object pl, int bit)
+{
+  mixed info;
+  object po;
+  int drin;
+  string pls, ek;
+
+
+  if (!pointerp(info = StaticQueryNPCbyNumber(bit)))
+    return -1;
+
+  if ((!po=previous_object()) 
+      || load_name(po) != info[SCORE_KEY])
+    return -2;
+
+  pls=getuid(pl);
+
+  // wenn unbestaetigt, Spieler fuer spaeter merken
+  if (member(unconfirmed_scores, bit)) {
+    if (member(unconfirmed_scores[bit], pls) == -1)
+  unconfirmed_scores[bit] += ({pls});
+  }
+  else {
+    // sonst wird das Bit direkt im Spieler gesetzt.
+    ek = (MASTER->query_ek(pls) || "");
+    if (test_bit(ek, bit))
+      return -3;
+    ek = set_bit(ek, bit);
+    MASTER->update_ek(pls, ek);
+    // Vergabestatistik hochzaehlen.
+    npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]++;
+  }
+
+  if (member(users_ek, pls))
+    m_delete(users_ek, pls);
+
+  EK_GIVENLOG(sprintf("%s: %s", info[SCORE_KEY], pls)); 
+
+  return info[SCORE_SCORE];
+}
+
+public int HasKill(mixed pl, mixed npc)
+{
+  string fn, *pls;
+
+  if (!objectp(pl) && !stringp(pl) && 
+      !objectp(npc) && !stringp(npc) && !intp(npc))
+    return 0;
+  if (!stringp(pl)) 
+    pl=getuid(pl);
+
+  if (intp(npc))
+    npc=by_num[npc,BYNUM_KEY];
+  fn=load_name(npc);
+
+  if (!member(npcs, fn)) return 0;
+  
+  int bit = npcs[fn, NPC_NUMBER];
+  
+  if (pointerp(pls=unconfirmed_scores[bit]) &&
+      member(pls,pl) != -1)
+    return 1;
+
+  string eks = (MASTER->query_ek(pl) || "");
+
+  return test_bit(eks, bit);
+}
+
+private void WriteDumpFile(string *keys) {
+  int maxn;
+
+  if (!pointerp(keys)) return;
+
+  rm(SCOREDUMPFILE);
+
+  write_file(SCOREDUMPFILE,sprintf("%5s  %5s  %4s   %s\n",
+  "Nr.", "Cnt", "Sc", "Objekt"));
+  foreach(string key: keys) {
+    write_file(SCOREDUMPFILE,sprintf("%5d  %5d  %4d   %O\n",
+    npcs[key,NPC_NUMBER], npcs[key,NPC_COUNT],
+    npcs[key,NPC_SCORE], key));
+    maxn += npcs[key,NPC_SCORE];
+  }
+  write_file(SCOREDUMPFILE,sprintf(
+  "========================================================\n"
+  "NPCs gesamt: %d Punkte\n\n",maxn));
+}
+
+public varargs int DumpNPCs(int sortkey) {
+
+  if (extern_call() && !allowed()) return SCORE_NO_PERMISSION;
+  if (!intp(sortkey)) return SCORE_INVALID_ARG;
+
+  rm(SCOREDUMPFILE);
+
+  // sortieren
+  string *keys=sort_array(m_indices(npcs), function int (string a, string b) {
+        return(npcs[a,sortkey] < npcs[b,sortkey]); } );
+  call_out(#'WriteDumpFile, 2, keys);
+
+  return 1;
+}
+
+public int SetScoreBit(string pl, int bit)
+{
+  string ek;
+
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+
+  ek = (MASTER->query_ek(pl) || "");
+  ek = set_bit(ek, bit);
+  MASTER->update_ek(pl, ek);
+
+  // Vergabestatistik hochzaehlen.
+  npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]++;
+
+  if (member(users_ek, pl))
+    m_delete(users_ek, pl);
+
+  write_file(SCORELOGFILE,sprintf("SETBIT: %s %s %5d Sc: %.3d %s (%s, %O)\n",
+         strftime("%d%m%Y-%T",time()),pl, bit,
+         by_num[bit,BYNUM_SCORE], by_num[bit,BYNUM_KEY],
+         geteuid(previous_object()), this_interactive()));
+  return 1;
+}
+
+public int ClearScoreBit(string pl, int bit)
+{
+  string ek;
+
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+
+  ek = (MASTER->query_ek(pl) || "");
+  ek = clear_bit(ek, bit);
+  MASTER->update_ek(pl, ek);
+
+  // Vergabestatistik runterzaehlen.
+  npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]--;
+
+  if (member(users_ek, pl))
+    m_delete(users_ek, pl);
+
+  write_file(SCORELOGFILE,sprintf(
+  "CLEARBIT: %s %s %5d Sc: %.3d %s (%s, %O)\n",       
+  strftime("%d%m%Y-%T",time()),pl,bit,
+  by_num[bit,BYNUM_SCORE],by_num[bit,BYNUM_KEY],
+  geteuid(previous_object()), this_interactive()));
+  return 1;
+}
+
+private status ektipAllowed()
+{ 
+  status poOK;
+  string poName;
+  status ret;
+                
+  poName=load_name(previous_object());        
+  poOK=previous_object() &&     
+    ((previous_object()==find_object(EKTIPGIVER)) || (poName==EKTIPLIST) );
+
+  ret=allowed() || 
+    (this_player() && this_interactive() && previous_object() && 
+     this_interactive()==this_player() && poOK);
+  return ret;
+}
+
+// liefert alle EKs, die aktiv sind und die der Spieler noch nicht hat in
+// einem Mapping entsprechend npcs zurueck.
+public mapping getFreeEKsForPlayer(object player)
+{
+  if(!ektipAllowed() || !objectp(player) || !query_once_interactive(player)){
+      return ([]);
+  }
+  // alle EKs, die der Spieler hat
+  string eks = (string)master()->query_ek(getuid(player));
+  // als Tips kommen alle in Frage, die er nicht hat, vor dem Invertieren muss
+  // aber sichergestellt werden, dass eks min. so lang ist wie active_eks, da
+  // die Invertierung ja z.B. nur EKs 0-1700 beruecksichtigt, wenn 1700 der
+  // hoechste EK im Spieler ist und dann als Tips alle EKs ueber
+  // 1700 verlorengingen.
+  // hier wird das letzte Bit von active_eks ermittelt und das darauf folgende
+  // Bit im Spieler-EK-String gesetzt und wieder geloescht, woraufhin der
+  // EK-String min. so lang wie active_eks ist. (es ist egal, wenn er
+  // laenger ist, auch egal, wenn man ein Bit ueberschreibt, das faellt alles
+  // gleich beim and_bits() raus.
+  int lb = last_bit(active_eks) + 1;
+  eks = clear_bit(set_bit(eks, lb), lb);
+  // jetzt invertieren
+  string non_eks = invert_bits(eks);
+  // jetzt vorhande EK-Tips ausfiltern. Im Prinzip gleiches Spiel wie oben.
+  string ektips = (string)master()->query_ektips(getuid(player));
+  // jetzt alle nicht als Tip vergebenen NPC ermitteln, vor dem Invertieren
+  // wieder Laenge angleichen...
+  ektips = invert_bits(clear_bit(set_bit(ektips, lb), lb));
+  // verunden liefert EKs, die der Spieler nicht hat und nicht als Tip hat
+  ektips = and_bits(ektips, non_eks);
+
+  // und nun die inaktive EKs ausfiltern, nochmal verunden
+  ektips = and_bits(ektips, active_eks);
+
+  // mal Platz reservieren, entsprechend der Menge an Bits
+  mapping freeKills = m_allocate( count_bits(ektips), 2);
+  // durch alle jetzt gesetzten Bits laufen, d.h. alle EKs, die der Spieler
+  // nicht hat und ein Mapping a la npcs erstellen.
+  int p=-1;
+  while ( (p=next_bit(ektips, p)) != -1) {
+    freeKills += ([ by_num[p,0]: p; by_num[p,1] ]);
+  }
+
+  return freeKills;
+}
+
+public int addTip(mixed key,string tip)
+{
+  string fn;
+  
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+
+  if (!tip || (!objectp(key) && !stringp(key)))
+    return SCORE_INVALID_ARG;
+
+  fn=load_name(key);
+
+  if (!member(npcs, fn)) return SCORE_INVALID_ARG;
+  tipList+=([fn:tip]);
+  save_object(SCORESAVEFILE);
+    
+  return 1;
+}
+
+public int changeTip(mixed key,string tip)
+{
+    return addTip(key,tip);
+}
+
+public int removeTip(mixed key)
+{
+  string fn;
+  
+  if (!allowed())
+    return SCORE_NO_PERMISSION;
+  
+  if ((!objectp(key) && !stringp(key)))
+    return SCORE_INVALID_ARG;
+
+  fn=load_name(key);
+  
+  if (!member(tipList, fn)) return SCORE_INVALID_ARG;
+    
+  m_delete(tipList,fn);
+  save_object(SCORESAVEFILE);
+    
+  return 1;  
+}
+
+private string getTipFromList(mixed key)
+{
+  string fn;
+  
+  if (!ektipAllowed())
+    return "";
+  
+  if ((!objectp(key) && !stringp(key)))
+    return "";
+
+  fn=load_name(key);
+  
+  if (!member(tipList, fn)) return "";
+        
+  return tipList[fn];  
+}
+
+private string _getTip(mixed key)
+{
+  string fn;
+  string tip;
+  string* path;
+    
+  if ((!objectp(key) && !stringp(key)))
+    return "";
+
+  fn=load_name(key);
+  
+  if(!member(npcs,fn)) return "";
+  
+  tip=getTipFromList(fn);
+  if(!tip || tip==""){
+    path=explode(fn,"/")-({""});
+    if(sizeof(path)<3) return "";
+    if(path[0]=="players") {
+      string tiptext;
+      if ( path[1] == "ketos" )
+        tiptext = "Ketos im Gebirge";
+      else if ( path[1] == "boing" && path[2] == "friedhof" )
+        tiptext = "Boing im eisigen Polar";
+      else
+        tiptext = capitalize(path[1]);
+      return "Schau Dich doch mal bei "+tiptext+" um.";
+    }
+    
+    if(path[0]=="d")
+    {
+      tip+="Schau Dich doch mal ";
+    
+      if(file_size("/players/"+path[2])==-2)
+      {
+        tip+="bei "+capitalize(path[2]+" ");
+      }
+      if ( path[1]=="polar" && file_size("/players/"+path[3])==-2 )
+      {
+        tip+="bei "+capitalize(path[3])+" ";
+      }
+
+      if(path[1]=="anfaenger")
+        tip+="in den Anfaengergebieten ";
+      if(path[1]=="fernwest")
+        tip+="in Fernwest ";
+      if(path[1]=="dschungel")
+        tip+="im Dschungel ";
+      if(path[1]=="schattenwelt")
+        tip+="in der Welt der Schatten ";
+      if(path[1]=="unterwelt")
+        tip+="in der Unterwelt ";
+      if(path[1]=="gebirge")
+        tip+="im Gebirge ";
+      if(path[1]=="seher")
+        tip+="bei den Sehergebieten ";
+      if(path[1]=="vland")
+        tip+="auf dem Verlorenen Land ";
+      if(path[1]=="ebene")
+        tip+="in der Ebene ";
+      if(path[1]=="inseln")
+        tip+="auf den Inseln ";
+      if(path[1]=="wald")
+        tip+="im Wald ";
+      if(path[1]=="erzmagier")
+        tip+="bei den Erzmagiern ";
+      if(path[1]=="polar")
+      {
+        if (path[2]=="files.chaos")
+          tip+="in den Raeumen der Chaosgilde ";
+        tip+="im eisigen Polar ";
+      }
+      if(path[1]=="wueste")
+        tip+="in der Wueste ";
+      tip+="um.";
+    }
+    else if ( path[0]=="gilden" )
+    {
+      tip+="Schau Dich doch mal";
+      switch( path[1] )
+      {
+        case "mon.elementar": 
+          tip+=" unter den Anfuehrern der Elementargilde"; 
+          break;
+        case "files.dunkelelfen":
+          tip+=" unter den Anfuehrern der Dunkelelfengilde";
+          break;
+        case "files.klerus":
+          tip+=" beim Klerus"; 
+          break;
+        case "files.werwoelfe": 
+          tip+=" unter den Anfuehrern der Werwoelfe";
+          break;
+        case "files.chaos": 
+          tip+=" unter den Anfuehrern der Chaosgilde";
+          break;
+        default: 
+          tip+=" in einer der Gilden"; 
+          break;
+      }
+      tip+=" um.";
+    }
+    else if ( path[0] == "p" ) 
+    {
+      tip+="Schau Dich doch mal ";
+      switch( path[1] ) 
+      {
+        case "zauberer":
+          tip+="in der Zauberergilde zu Taramis";
+          break;
+        case "kaempfer":
+          tip+="bei den Angehoerigen des Koru-Tschakar-Struvs";
+          break;
+        case "katzenkrieger":
+          tip+="bei der Gilde der Katzenkrieger";
+          break;
+        case "tanjian":
+          tip+="unter den Meistern der Tanjiangilde";
+          break;
+      }
+      tip+=" um.";
+    }
+  }
+  return tip;
+}
+
+// return valid tips from database or existing 
+public string getTip(mixed key)
+{
+  string fn;
+  string tip;
+  string* path;
+  
+  if (!ektipAllowed())
+    return "";
+  
+  return _getTip(key);
+}
+
+// liefert ein Array mit allen Objekten zurueck, auf die bitstr verweist, also
+// eine Liste aller Objekte, die als Tip vergeben wurden.
+private string* makeTiplistFromBitString(string bitstr)
+{ 
+  string * ret= allocate(count_bits(bitstr));
+  // ueber alle gesetzten bits laufen und Array zusammensammeln
+  int i;
+  int p=-1;
+  while ((p=next_bit(bitstr,p)) != -1) {
+    ret[i] = by_num[p, 0];
+    i++;
+  }
+  // zur Sicherheit
+  ret -= ({0});
+
+  return ret;
+}
+
+// gibt die Objektnamen der EK-Tips vom jeweiligen Spieler zurueck.
+public string *QueryTipObjects(mixed player) {
+  
+  if (extern_call() && !allowed())
+      return 0;
+  if (objectp(player) && query_once_interactive(player))
+      player=getuid(player);
+  if (!stringp(player))
+    return 0;
+
+  string tipstr=(string)master()->query_ektips(player);
+  // jetzt EK-Tips ausfiltern, die erledigt sind. Dazu EK-Liste holen...
+  string eks=(string)master()->query_ek(player);
+  // als Tips kommen alle in Frage, die er nicht hat, vor dem Invertieren muss
+  // aber sichergestellt werden, dass eks min. so lang ist wie tipstr, da
+  // die Invertierung ja z.B. nur EKs 0-1700 beruecksichtigt, wenn 1700 der
+  // hoechste EK im Spieler ist und dann alle Tips ueber
+  // 1700 verlorengingen.
+  // hier wird das letzte Bit von tipstr ermittelt und das darauf folgende
+  // Bit im Spieler-EK-String gesetzt und wieder geloescht, woraufhin der
+  // EK-String min. so lang wie der Tipstring ist. (es ist egal, wenn er
+  // laenger ist, auch egal, wenn man ein Bit ueberschreibt, das faellt alles
+  // gleich beim and_bits() raus.
+  int lb = last_bit(tipstr) + 1;
+  eks = clear_bit(set_bit(eks, lb), lb);
+  // jetzt invertieren
+  string non_eks = invert_bits(eks);
+  // jetzt verunden und man hat die Tips, die noch nicht gehauen wurden.
+  tipstr = and_bits(tipstr, non_eks);
+  // noch inaktive EKs ausfiltern...
+  tipstr = and_bits(tipstr, active_eks);
+
+  return makeTiplistFromBitString(tipstr);
+}
+
+public string allTipsForPlayer(object player)
+{
+
+  if(!player || !this_interactive() || 
+      (this_interactive()!=player && !IS_ARCH(this_interactive())) )
+    return "";    
+ 
+  string *tips = QueryTipObjects(player);
+
+  tips = map(tips, #'_getTip);
+  tips -= ({0,""});
+
+  return implode(tips, "\n");
+}
+
+public status playerMayGetTip(object player)
+{
+  int numElegible;
+  int numReceived;
+  int lvl;
+  int i;
+  string tips;
+  
+  if(!ektipAllowed() || !player || !query_once_interactive(player))      
+      return 0;
+
+  if(!player || !query_once_interactive(player))    
+      return 0;
+  
+  lvl=(int)player->QueryProp(P_LEVEL);
+  numElegible=0;
+  i=sizeof(EKTIPS_LEVEL_LIMITS)-1;
+
+  if(lvl>EKTIPS_LEVEL_LIMITS[i])    
+      numElegible+=(lvl-EKTIPS_LEVEL_LIMITS[i]);
+
+  for(i;i>=0;i--){
+      if(lvl>=EKTIPS_LEVEL_LIMITS[i]) numElegible++;
+  }
+
+  tips=(string)MASTER->query_ektips(getuid(player)) || "";
+  // inaktive EKs ausfiltern.
+  tips = and_bits(tips, active_eks);
+  // und Gesamtzahl an Tips zaehlen. Hier werden erledigte Tips explizit nicht
+  // ausgefiltert!
+  numReceived=count_bits(tips);
+
+  return numElegible>numReceived;
+}
+
+public string giveTipForPlayer(object player)
+{
+  string* tmp;
+  mapping free;
+  string tip,pl,ektip;
+  int index;
+  
+  if(!ektipAllowed() || !player || 
+      !query_once_interactive(player) || !playerMayGetTip(player))  
+    return "";
+  
+  pl=getuid(player);
+  free=getFreeEKsForPlayer(player);
+
+  if(!mappingp(free) || sizeof(free)==0)
+    return "";
+
+  tmp=m_indices(free);
+
+  ektip=(string)MASTER->query_ektips(pl) || "";
+ 
+  foreach(int i: EKTIPS_MAX_RETRY) {
+      index=random(sizeof(tmp));
+      tip=getTip(tmp[index]);
+      if (stringp(tip) && sizeof(tip)) {
+    ektip=set_bit(ektip,npcs[tmp[index],NPC_NUMBER]);
+    MASTER->update_ektips(pl,ektip);
+    break; //fertig
+      }
+  }
+
+  return tip;  
+}
+
+// checkt NPCs auf Existenz und Ladbarkeit
+public void CheckNPCs(int num) {
+  string fn;
+  object ekob;
+  if (!num) rm(SCORECHECKFILE);
+  while (num <= lastNum && get_eval_cost() > 1480000) {
+    if (!by_num[num,1] || !by_num[num,0]) {
+      num++;
+      continue;
+    }
+    fn = by_num[num,0] + ".c";
+    if (file_size(fn) <= 0) {
+      // File nicht existent
+      write_file(SCORECHECKFILE, sprintf(
+    "EK %.4d ist nicht existent (%s)\n",num,fn));
+    }
+    else if (catch(ekob=load_object(fn)) || !objectp(ekob) ) {
+      // NPC offenbar nicht ladbar.
+      write_file(SCORECHECKFILE, sprintf(
+    "EK %.4d ist nicht ladbar (%s)\n",num,fn));
+    }
+    num++;
+  }
+  ZDEBUG(sprintf("%d NPC checked",num));
+  if (num <= lastNum)
+    call_out(#'CheckNPCs,4,num);
+  else
+    ZDEBUG("Finished!");
+}
+
+// liquidiert einen EK endgueltig. An diesem Punkt wird davon augegangen, dass
+// kein Spieler den EK mehr gesetzt hat!
+private void LiquidateEK(int bit) {
+
+  if (!intp(bit) || bit < 0) return;
+
+  string obname = by_num[bit, BYNUM_KEY];
+  int score = by_num[bit, BYNUM_SCORE];
+
+  if (member(npcs, obname) && (npcs[obname, NPC_NUMBER] == bit)) {
+    m_delete(by_num, bit);
+    m_delete(npcs, obname);
+    if (member(unconfirmed_scores,bit))
+      m_delete(unconfirmed_scores,bit);
+    active_eks = clear_bit(active_eks,bit);
+    removeTip(obname);
+    free_num += ({bit});
+    write_file(SCOREAUTOLOG,sprintf(
+    "LIQUIDATEEK: %s %5d Score %3d %s\n",
+    strftime("%d%m%Y-%T",time()), bit, score, obname));
+  }
+}
+
+private void check_player(string pl) {
+  int changed, changed2; 
+  
+  // EKs pruefen
+  string eks=(string)master()->query_ek(pl) || "";
+  string *opfer=allocate( (sizeof(eks)*6)+1, "");
+  int p=-1;
+  while ((p=next_bit(eks,p)) != -1) {
+    if (!member(by_num, p)) {
+  write_file(SCORECHECKFILE, sprintf(
+   "UNKNOWNEK %s %5d in %s gefunden.\n", 
+    strftime("%d%m%Y-%T",time()), p, pl));
+    }
+    // wenn das aktuelle Bit geloescht werden soll, also in to_be_removed
+    // steht...
+    if (member(to_be_removed,p) != -1) {
+  eks = clear_bit(eks,p);
+  changed=1;
+  write_file(EKCLEANLOG,sprintf(
+        "CLEARBIT: %s %O %5d %s\n",
+        strftime("%d%m%Y-%T",time()), pl, p, 
+        by_num[p,BYNUM_KEY] || "NoName"));
+    }
+    else {
+      // sonst statistikpflege
+      npcs[by_num[p,BYNUM_KEY],NPC_COUNT]++;
+      // loggen, welche NPC der Spieler hat
+      opfer[p]=to_string(p);
+    }
+  }
+  // und noch die Ek-Tips...
+  string ektips = (string)master()->query_ektips(pl) || "";
+  p = -1;
+  while ((p=next_bit(ektips,p)) != -1) {
+    if (!member(by_num, p)) {
+  write_file(EKCLEANLOG, sprintf(
+    "UNKNOWNEK %s %5d in %s (EKTips) gefunden - clearing.\n", 
+    strftime("%d%m%Y-%T",time()), p, pl));
+  ektips = clear_bit(ektips, p); // hier direkt loeschen.
+  changed2 = 1;
+    }
+    // wenn das aktuelle Bit geloescht werden soll, also in to_be_removed
+    // steht...
+    else if (member(to_be_removed,p) != -1) {
+  ektips = clear_bit(ektips,p);
+  changed2=1;
+  write_file(EKCLEANLOG,sprintf(
+        "CLEAREKTIP: %s %O %5d %s\n",
+        strftime("%d%m%Y-%T",time()), pl, p, 
+        by_num[p,BYNUM_KEY] || "NoName"));
+    }
+  }
+
+  if (changed) {
+      master()->update_ek(pl, eks);
+  }
+  if (changed2) {
+      master()->update_ektips(pl, ektips);
+  }
+  opfer-=({""});
+  write_file(WERKILLTWEN,sprintf("%s\n%=-78s\n\n",pl,CountUp(opfer)||""));
+}
+
+public void check_all_player(mapping allplayer) {
+ 
+  if (extern_call() && !allowed())
+      return;
+
+  if (!mappingp(allplayer)) {
+      foreach(string key: npcs) {
+  npcs[key,NPC_COUNT]=0;
+      }
+      allplayer=(mapping)master()->get_all_players();
+      rm(WERKILLTWEN);
+      call_out(#'check_all_player,2,allplayer);
+      return;
+  }
+
+  // offenbar fertig mit allen Spielern, jetzt noch den Rest erledigen.
+  if (!sizeof(allplayer)) {
+    foreach(int bit: to_be_removed) {
+      LiquidateEK(bit);
+    }
+    to_be_removed=({});
+    save_object(SCORESAVEFILE); 
+    ZDEBUG("Spielerchecks und EK-Liquidation fertig.\n");
+    return;
+  }
+
+  string dir=m_indices(allplayer)[0];
+  string *pls=allplayer[dir];
+  foreach(string pl: pls) {
+    if (get_eval_cost() < 1250000)
+      break; // spaeter weitermachen.
+    catch(check_player(pl) ; publish);
+    pls-=({pl});
+  }
+  allplayer[dir] = pls; 
+ 
+  if (!sizeof(allplayer[dir]))
+    m_delete(allplayer,dir);
+ 
+  call_out(#'check_all_player,2,allplayer);
+}
+
diff --git a/secure/scoremaster.h b/secure/scoremaster.h
new file mode 100644
index 0000000..900cf5f
--- /dev/null
+++ b/secure/scoremaster.h
@@ -0,0 +1,58 @@
+// MorgenGrauen MUDlib
+//
+// scoremaster.h -- Definitionen fuer den ScoreMaster
+//
+// $Id: scoremaster.h 6633 2007-12-06 21:49:02Z Zesstra $
+
+#ifndef _SCOREMASTER_H_
+#define _SCOREMASTER_H_ 1
+
+#define SCOREMASTER "/secure/scoremaster"
+
+#define SCORESAVEFILE    "/secure/ARCH/npcmaster"
+#define SCOREDUMPFILE    "/secure/ARCH/SCORES.dump"
+#define SCORELOGFILE     "/secure/ARCH/SCORES.LOG"
+#define SCOREAUTOLOG     "/secure/ARCH/SCORES_AUTO.LOG"
+#define SCORECHECKFILE   "/secure/ARCH/SCORES.kaputt"
+#define EKCLEANLOG       "/secure/ARCH/EKCLEANER.LOG"
+#define WERKILLTWEN      "/secure/ARCH/WERKILLTWEN.LOG"
+
+// wer ist fuer die EKs aktuell zustaendig?
+#define SCOREMAINTAINERS ({"arathorn", "zesstra"})
+
+// Elemente des von QueryScore() zurueckgegebenen Arrays
+#define SCORE_NUMBER	0   // (Bit-)Nummer des Objekts
+#define SCORE_SCORE	1   // Zahl der Stufenpunkte
+#define SCORE_KEY	2   // Filename des Obkekts
+
+// Indizes fuer das Mapping npcs
+#define NPC_NUMBER      0   // (Bit-)Nummer des EKs
+#define NPC_SCORE       1   // Zahl der Stufenpunkte
+#define NPC_COUNT       2   // wie oft der EK vergeben wurde
+// Indizes fuer das Mapping by_num
+#define BYNUM_KEY       0   // Filename des Objekts
+#define BYNUM_SCORE     1   // Zahl der Stufenpunkte
+
+#define SCORE_INVALID_ARG   -1
+#define SCORE_NO_PERMISSION -2
+
+#define SCORE_LOW_MARK	200000
+#define SCORE_HIGH_MARK 600000
+
+#define P_NO_SCORE "no_score"
+
+// moegliche Werte fuer 'flag' bei TestScore()
+#define SCORE_KILL  0x01
+#define SCORE_QUEST 0x02
+
+#define EK_GIVENLOG(x) log_file("ARCH/EK_GIVEN", \
+        dtime(time())+" "+x+"\n",100000)
+
+// ein paar defines fuer ektips
+#define EKTIPS_MAX_RETRY		10
+// one tip per level in list, above top entry one tip every level
+#define EKTIPS_LEVEL_LIMITS 	({60,65,70,74,78,82,85,88,91,93,95,97})
+#define EKTIPGIVER "/d/ebene/muadib/dragon/npc/brumni"
+#define EKTIPLIST  "/d/ebene/muadib/dragon/obj/eklist"
+
+#endif
diff --git a/secure/shadowmaster.c b/secure/shadowmaster.c
new file mode 100644
index 0000000..e1fd51d
--- /dev/null
+++ b/secure/shadowmaster.c
@@ -0,0 +1,262 @@
+// MorgenGrauen MUDlib
+/** \file /secure/shadowmaster.c
+* Objekt, welches eine Liste mit aktiven Shadows hat.
+* Die simul_efun shadow() meldet das beschattende Objekt hier an. Nuetzlich,
+* damit man als Magier endlich mal rauskriegen kann, welche Shadows es
+* eigentlich gibt.
+* \author Zesstra
+* \date 07.08.2009
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types
+#pragma no_clone
+#pragma no_inherit
+#pragma no_shadow
+#pragma pedantic
+
+#include <defines.h>
+#include <wizlevels.h>
+
+#define HOME(x) (__PATH__(0)+x)
+
+/** shadows ist ein mapping, welches alle Schatten und ihre Opfer enthaelt.
+ *
+ * Struktur: Schatten als Schluessel (object) und Beschatteter (object) als
+ * Wert.
+*/
+private mapping shadows = ([]);
+
+#ifndef DEBUG
+#define DEBUG(x)  if (find_player("zesstra"))\
+            tell_object(find_player("zesstra"),\
+                                      "SMDBG: "+x+"\n")
+#endif
+
+#define MEMORY "/secure/memory"
+
+#define SHADOW_OK             1
+#define SHADOW_ACCESS_DENIED -1
+#define SHADOW_EXISTS        -2
+#define SHADOW_UNKNOWN       -3
+
+protected void create() {
+  mixed tmp;
+  seteuid(getuid());
+  if (mappingp(tmp=MEMORY->Load("Schatten"))) {
+    shadows = tmp;
+    DEBUG("Daten aus MEMORY geladen.\n");
+  }
+  else {
+    DEBUG("Keine Daten in MEMORY vorhanden - reinitialisiere.\n");
+    if (MEMORY->Save("Schatten", shadows) != 1)
+      raise_error("Konnte Daten nicht im Memory ablegen.\n");
+  }
+}
+
+/** Liefert einen String mit allen Schatten und beschatteten Objekten zurueck.
+  * @return string - Liste von Schatten und Opfern
+  */
+public void DumpShadows() {
+  if (extern_call() && !ELDER_SECURITY ) return 0;
+
+  string res = "";
+  foreach(object schatten, object opfer: shadows)
+    if (schatten && opfer)
+      res += sprintf("%O -> %O\n",schatten,opfer);
+
+  printf("Folgende Shadows sind bekannt: \n\n%s\n", res);
+}
+
+/** Findet zu einem beschatteten Objekt den zugehoerigen Schatten.
+  @param[in] object - ein beschattetes Objekt.
+  @return object - Den Schatten oder 0.
+  @sa FindShadowsObject(), QueryObject(), QueryInfo()
+*/
+public object FindShadow(object opfer) {
+  if (!objectp(opfer))
+    return 0;
+
+  foreach(object schatten, object victim: shadows) {
+    if (opfer == victim)
+      // Schatten gefunden.
+      return schatten;
+  }
+  return 0;
+}
+
+/** Findet zu einem Schatten das beschattete Objekt.
+  @param[in] object - ein beschattendes Objekt.
+  @return object - Das beschattete Objekt oder 0.
+  @sa FindShadow(), QueryObject(), QueryInfo()
+*/
+public object FindShadowedObject(object schatten) {
+  return shadows[schatten];
+}
+
+/** Gibt Schatten und Beschatteten zurueck, falls ob eines von beiden ist.
+  Ist ob ein Schatten oder ein beschattetes Objekt, wird ein Array aus
+  Objekten geliefert. Hierbei werden ggf. alle Objekte in der
+  Beschattungshierarchie geliefert, von der ob Bestandteil ist.
+  @param[in] object - ein Objekt.
+  @return object* - ({a, b, c, d}) oder 0.
+  @sa FindShadow(), FindShadowedObject, QueryInfo()
+*/
+public object* QueryObject(object ob) {
+
+  if (!objectp(ob)) return 0;
+ 
+  object *res = ({ob});
+  object sh = FindShadow(ob);
+
+  while(sh) {
+    // es gibt einen Schatten, also Kette nach oben verfolgen.
+    res = ({sh}) + res;
+    sh = FindShadow(sh);
+  }
+
+  object vic = FindShadowedObject(ob);
+  while(vic) {
+    // es gibt ein beschattetes Objekt, Kette nach unten verfolgen.
+    res = res + ({vic});
+    vic = FindShadowedObject(vic);
+  }
+
+  if (sizeof(res) < 2) {
+    // Offenbar wird ob weder beschattet noch beschattet selber.
+    // Moeglicherweise wurde jedoch durch wilde Zerstoerung die Hierarchie
+    // getrennt. Falls ob nen Schatten ist, laesst sich das reparieren. Falls
+    // ob beschattet wird, hilft eigentlich nur ein reset(). Den moechte ich
+    // hier aber nicht machen, weils u.U. teuer sein koennte.
+    if (query_shadowing(ob)) {
+      shadows[ob] = query_shadowing(ob);
+      // nochmal.
+      return QueryObject(ob);
+    }
+    // scheinbar nicht.
+    return 0;
+  }
+
+  return res;
+}
+
+/** Gibt einen String mit Infos ueber ob zurueck.
+  Ist ob ein beschattetes Objekt oder ein Schatten, wird ein beschreibender
+  String zurueckgeliefert (schatten -> beschattetes Objekt). Hierbei wird ggf.
+  die gesamte Beschattungshierarchie angegeben (a -> b -> c -> d).
+  @param[in] object - ein Objekt.
+  @return string - String mit Infos ueber ob.
+  @sa FindShadow(), FindShadowedObject(), QueryObject()
+*/
+public string QueryInfo(object ob) {
+  object *shs = QueryObject(ob);
+  if (!shs) return 0;
+
+  return CountUp(map(shs, #'object_name), " -> ", " -> ");
+}
+
+/** Registriert einen Schatten und sein Opfer.
+  Registriert den Schatten und sein Opfer. Sollte ausschliesslich durch die
+  simul_efun oder spare_simul_fun gerufen werden.
+  @sa UnregisterShadow()
+*/
+public int RegisterShadow(object schatten) {
+  object opfer;
+
+  //DEBUG(sprintf("[%O] %O\n", schatten, caller_stack()));
+
+  // Irgendein Sicherheitscheck ist eigentlich nicht noetig hier, da das Opfer
+  // ohnehin per efun ermittelt wird und kein Eintrag erfolgt, wenn kein
+  // beschattetes Objekt zu finden ist.
+
+  // das von schatten beschattete Objekt ermitteln 
+  if (objectp(opfer=query_shadowing(schatten))) {
+    if (shadows[schatten] == opfer)
+      return SHADOW_EXISTS;
+
+    // Neueintrag oder ggf. auch Aendern.
+    shadows[schatten] = opfer;
+    //DEBUG(DumpShadows());
+    return SHADOW_OK;
+  }
+  return SHADOW_ACCESS_DENIED;
+}
+
+/** Traegt einen Schatten wieder aus.
+  Der Schatten wird wieder ausgetragen. Sollte ausschliesslich durch die
+  simul_efun oder spare_simul_fun gerufen werden.
+  Werden Schatten oder beschattete Objekte zerstoert ohne vorher die
+  Schattierung zu beenden, fuehrt dies zu Inkonsistenzen und zerbrochenen
+  Beschattungshierarchien.
+  @sa RegisterShadow(), UnregisterOpfer()
+*/
+public int UnregisterShadow(object caller) {
+  object schatten, opfer;
+
+  // Ein Sicherheitscheck fuer den Aufruf ist eigentlich nicht noetig, da ein
+  // Eintrag nur entfernt wird, wenn die Beschattung nachweislich beendet
+  // wurde.
+
+  //DEBUG(sprintf("[%O] %O\n", caller, caller_stack()));
+  if (!objectp(caller)) return SHADOW_UNKNOWN;
+
+  // Schatten und beschatteten aus den lokalen Daten ermitteln.
+  if (member(shadows, caller)) {
+    schatten = caller;
+    opfer = shadows[schatten];
+  }
+  else if (objectp(schatten = FindShadow(caller))) {
+    opfer = caller;
+  }
+  // wenn nicht bekannt, ist jetzt eh Ende.
+  if (!schatten) return SHADOW_UNKNOWN;
+
+  //DEBUG(sprintf("%O -> %O (%O, %O)\n",
+  //      schatten, opfer, caller, query_shadowing(schatten)));
+
+  // Schattierung wirklich beendet? Wenn nicht -> Ende
+  if (opfer && query_shadowing(schatten) == opfer)
+    return SHADOW_ACCESS_DENIED;
+
+  // war schatten in einer Beschattungshierarchie?
+  object up = FindShadow(schatten);
+  if (up && query_shadowing(up) == opfer) {
+    shadows[up] = opfer; // Kette neu verlinken
+  }
+
+  // jetzt kann geloescht werden.
+  m_delete(shadows,schatten);
+  return SHADOW_OK;
+}
+
+public int ResetAll() {
+  if (!ARCH_SECURITY) return SHADOW_ACCESS_DENIED;
+
+  DEBUG("ResetAll() called.\n");
+
+  shadows = ([]);
+
+  if (MEMORY->Save("Schatten", shadows) != 1)
+    raise_error("Konnte Daten nicht im Memory ablegen.\n");
+
+  return SHADOW_OK;
+}
+
+/** Raeumt die Daten ueber die Schatten auf.
+    Zerstoeren sich beschattete Objekte, werden die Schatten nicht
+    ausgetragen, daher wird das von zeit zu zeit hier gemacht.
+*/
+public void reset() {
+  foreach(object schatten, object opfer: shadows) {
+    if (!objectp(opfer)) {
+      // war schatten evtl. in einer Hierarchie und beschattet jetzt was
+      // anderes?
+      if (query_shadowing(schatten))
+        shadows[schatten] = query_shadowing(schatten);
+      else
+        m_delete(shadows, schatten);
+    }
+  }
+}
+
diff --git a/secure/simul_efun/README b/secure/simul_efun/README
new file mode 100644
index 0000000..303429a
--- /dev/null
+++ b/secure/simul_efun/README
@@ -0,0 +1,20 @@
+simul_efun 
+---------- 
+Das simul_efun Objekt /secure/simul_efun/simul_efun benutzt die Dateien
+in /secure/simul_efun.
+Im Verzeichnis /secure/simul_efun/spare ist eine Sicherheitskopie von allen 
+Dateien in /secure/simul_efun.
+
+Falls /secure/simul_efun/simul_efun.c nicht ladbar ist, wird 
+/secure/simul_efun/spare/simul_efun.c als Ersatz versucht. Wenn auch das
+nicht ladbar ist, erfolgt ein Shutdown des Muds.
+
+Bei Aenderungen sollte _zuerst_ die normale simul efun editiert und
+anschliessend zerstoert/entladen werden, woraufhin sie implizit durch den
+Driver und Master neugeladen wird. Ist dies erfolgreich und die neue
+simul_efun laeuft (erst dann!) kopiert man die Dateien aus 
+/secure/simul_efun nach /secure/simul_efun/spare.
+
+Die Dateien hier sind mit Ausnahme der simul_efun.c selbst _nicht_ dazu 
+gedacht, geladen zu werden.
+
diff --git a/secure/simul_efun/comm.c b/secure/simul_efun/comm.c
new file mode 100644
index 0000000..595b852
--- /dev/null
+++ b/secure/simul_efun/comm.c
@@ -0,0 +1,124 @@
+#include <living/comm.h>
+
+// Sendet an alle Objekte in room und room selber durch Aufruf von
+// ReceiveMsg().
+varargs void send_room(object|string room, string msg, int msg_type,
+                       string msg_action, string msg_prefix, object *exclude,
+                       object origin)
+{
+  if (stringp(room))
+    room=load_object(room);
+
+  origin ||= previous_object();
+  object *dest = exclude ? all_inventory(room) - exclude :
+                           all_inventory(room);
+
+  dest->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, origin);
+}
+
+static int _shout_filter( object ob, string pat )
+{
+    string *ignore;
+
+    if ( !environment(ob) )
+       return 0;
+
+    return sizeof( regexp( ({ object_name( environment(ob) ) }), pat ) );
+}
+
+varargs void shout( string s, mixed where ){
+    object *u;
+    string *pfade;
+
+    if ( !sizeof( u = users() - ({ this_player() }) ) )
+       return;
+
+    if ( !where )
+       pfade = ({ "/" });
+    else if ( intp(where) )
+       pfade =
+           ({ implode( efun::explode( object_name( environment(this_player()) ),
+                                   "/" )[0..2], "/" ) + "/" });
+    else if ( stringp(where) )
+       pfade = ({ where });
+    else
+       pfade = where;
+
+    u = filter( u, "_shout_filter", ME, implode( pfade, "|" ) );
+    u->ReceiveMsg(s, MT_COMM|MT_FAR|MSG_DONT_WRAP|MSG_DONT_STORE,
+                  MA_SHOUT_SEFUN, 0, previous_object());
+}
+
+
+#if __VERSION__ > "3.3.718"
+// This sefun replaces the deprecated efun cat().
+#define CAT_MAX_LINES 50
+varargs int cat(string file, int start, int num)
+{
+    if (extern_call())
+        set_this_object(previous_object());
+
+    int more;
+
+    if (num < 0 || !this_player())
+        return 0;
+
+    if (!start)
+        start = 1;
+
+    if (!num || num > CAT_MAX_LINES) {
+        num = CAT_MAX_LINES;
+        more = sizeof(read_file(file, start+num, 1));
+    }
+
+    string txt = read_file(file, start, num);
+    if (!txt)
+        return 0;
+
+    efun::tell_object(this_player(), txt);
+
+    if (more)
+        efun::tell_object(this_player(), "*****TRUNCATED****\n");
+
+    return sizeof(txt & "\n");
+}
+#undef CAT_MAX_LINES
+#endif // __VERSION__ > "3.3.718"
+
+#if __VERSION__ > "3.3.719"
+// This sefun replaces the deprecated efun tail().
+#define TAIL_MAX_BYTES 1000
+varargs int tail(string file)
+{
+    if (extern_call())
+        set_this_object(previous_object());
+
+    if (!stringp(file) || !this_player())
+        return 0;
+
+    string txt = read_bytes(file, -(TAIL_MAX_BYTES + 80), (TAIL_MAX_BYTES + 80));
+    // read_bytes() returns 0 if the start of the section given by
+    // parameter #2 lies beyond the beginning of the file.
+    // In this case we simply try and read the entire file.
+    if (!stringp(txt) && file_size(file) < TAIL_MAX_BYTES+80)
+      txt = read_file(file);
+    // Exit if still nothing could be read.
+    if (!stringp(txt))
+      return 0;
+
+    // cut off first (incomplete) line
+    int index = strstr(txt, "\n");
+    if (index > -1) {
+        if (index + 1 < sizeof(txt))
+            txt = txt[index+1..];
+        else
+            txt = "";
+    }
+
+    efun::tell_object(this_player(), txt);
+
+    return 1;
+}
+#undef TAIL_MAX_BYTES
+#endif // __VERSION__ > "3.3.719"
+
diff --git a/secure/simul_efun/debug_info.c b/secure/simul_efun/debug_info.c
new file mode 100644
index 0000000..d607c2f
--- /dev/null
+++ b/secure/simul_efun/debug_info.c
@@ -0,0 +1,434 @@
+/* This sefun is to provide a replacement for the efun debug_info().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(debug_info)
+
+#include <driver_info.h>
+#include <debug_info.h>
+
+mixed debug_info(int what, varargs mixed* args)
+{
+    if (sizeof(args) > 2)
+        raise_error("Too many arguments to debug_info\n");
+
+    switch(what)
+    {
+        default:
+            raise_error(sprintf("Illegal value %d for debug_info().\n", what));
+
+        case DINFO_OBJECT:
+        {
+            object ob;
+
+            if (sizeof(args) != 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (!objectp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            ob = args[0];
+            printf("O_HEART_BEAT      : %s\n", efun::object_info(ob, OC_HEART_BEAT)       ? "TRUE" : "FALSE");
+            printf("O_ENABLE_COMMANDS : %s\n", efun::object_info(ob, OC_COMMANDS_ENABLED) ? "TRUE" : "FALSE");
+            printf("O_CLONE           : %s\n", efun::clonep(ob)                           ? "TRUE" : "FALSE");
+            printf("O_DESTRUCTED      : FALSE\n");
+            printf("O_SWAPPED         : %s\n", efun::object_info(ob, OI_SWAPPED)          ? "TRUE" : "FALSE");
+            printf("O_ONCE_INTERACTIVE: %s\n", efun::object_info(ob, OI_ONCE_INTERACTIVE) ? "TRUE" : "FALSE");
+            printf("O_RESET_STATE     : %s\n", efun::object_info(ob, OI_RESET_STATE)      ? "TRUE" : "FALSE");
+            printf("O_WILL_CLEAN_UP   : %s\n", efun::object_info(ob, OI_WILL_CLEAN_UP)    ? "TRUE" : "FALSE");
+            printf("O_REPLACED        : %s\n", efun::object_info(ob, OI_REPLACED)         ? "TRUE" : "FALSE");
+            printf("time_reset  : %d\n",       efun::object_info(ob, OI_NEXT_RESET_TIME));
+            printf("time_of_ref : %d\n",       efun::object_info(ob, OI_LAST_REF_TIME));
+            printf("ref         : %d\n",       efun::object_info(ob, OI_OBJECT_REFS));
+
+            int gticks = efun::object_info(ob, OI_GIGATICKS);
+            if(gticks)
+                printf("evalcost   :  %d%09d\n", gticks, efun::object_info(ob, OI_TICKS));
+            else
+                printf("evalcost   :  %d\n",   efun::object_info(ob, OI_TICKS));
+
+            printf("swap_num    : %d\n",       efun::object_info(ob, OI_SWAP_NUM));
+            printf("name        : '%s'\n",     efun::object_name(ob));
+            printf("load_name   : '%s'\n",     efun::load_name(ob));
+
+            object next_ob = efun::object_info(ob, OI_OBJECT_NEXT);
+            if (next_ob)
+                printf("next_all    : OBJ(%s)\n", efun::object_name(next_ob));
+
+            object prev_ob = efun::object_info(ob, OI_OBJECT_PREV);
+            if (prev_ob)
+                printf("Previous object in object list: OBJ(%s)\n", efun::object_name(prev_ob));
+            else
+                printf("This object is the head of the object list.\n");
+            break;
+        }
+
+        case DINFO_MEMORY:
+        {
+            object ob;
+
+            if (sizeof(args) != 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (!objectp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            ob = args[0];
+
+            printf("program ref's %3d\n",   efun::object_info(ob, OI_PROG_REFS));
+            printf("Name: '%s'\n",          efun::program_name(ob));
+            printf("program size    %6d\n", efun::object_info(ob, OI_PROG_SIZE));
+            printf("num func's:  %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_FUNCTIONS),
+                                            efun::object_info(ob, OI_SIZE_FUNCTIONS));
+            printf("num vars:    %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_VARIABLES),
+                                            efun::object_info(ob, OI_SIZE_VARIABLES));
+
+            printf("num strings: %3d (%4d) : overhead %d + data %d (%d)\n",
+                                            efun::object_info(ob, OI_NUM_STRINGS),
+                                            efun::object_info(ob, OI_SIZE_STRINGS) + efun::object_info(ob, OI_SIZE_STRINGS_DATA),
+                                            efun::object_info(ob, OI_SIZE_STRINGS),
+                                            efun::object_info(ob, OI_SIZE_STRINGS_DATA),
+                                            efun::object_info(ob, OI_SIZE_STRINGS_DATA_TOTAL));
+
+            printf("num inherits %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_INHERITED),
+                                            efun::object_info(ob, OI_SIZE_INHERITED));
+
+            printf("total size      %6d\n", efun::object_info(ob, OI_PROG_SIZE_TOTAL));
+
+            printf("data size       %6d (%6d\n",
+                                            efun::object_info(ob, OI_DATA_SIZE),
+                                            efun::object_info(ob, OI_DATA_SIZE_TOTAL));
+            break;
+        }
+
+        case DINFO_OBJLIST:
+        {
+            if (sizeof(args) == 0)
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (sizeof(args) == 1)
+            {
+                object * obs = efun::objects(args[0], 1);
+                return obs[0];
+            }
+            else
+                return efun::objects(args[0], args[1]);
+            break;
+        }
+
+        case DINFO_MALLOC:
+            write(debug_info(DINFO_STATUS, "malloc"));
+            break;
+
+        case DINFO_STATUS:
+        {
+            string which;
+            int opt;
+
+            if (sizeof(args) > 1)
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (!sizeof(args) || !args[0])
+                which = "";
+            else if(stringp(args[0]))
+                which = args[0];
+            else
+                raise_error("bag arg 2 to debug_info().\n");
+
+            switch(which)
+            {
+                case "":
+                    opt = DI_STATUS_TEXT_MEMORY;
+                    break;
+
+                case "tables":
+                    opt = DI_STATUS_TEXT_TABLES;
+                    break;
+
+                case "swap":
+                    opt = DI_STATUS_TEXT_SWAP;
+                    break;
+
+                case "malloc":
+                    opt = DI_STATUS_TEXT_MALLOC;
+                    break;
+
+                case "malloc extstats":
+                    opt = DI_STATUS_TEXT_MALLOC_EXTENDED;
+                    break;
+
+                default:
+                    return 0;
+            }
+
+            return efun::driver_info(opt);
+        }
+
+        case DINFO_DUMP:
+        {
+            int opt;
+
+            if (!sizeof(args))
+                raise_error("bad number of arguments to debug_info\n");
+
+            if(!stringp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            switch(args[0])
+            {
+                case "objects":
+                    opt = DDI_OBJECTS;
+                    break;
+
+                case "destructed":
+                    opt = DDI_OBJECTS_DESTRUCTED;
+                    break;
+
+                case "opcodes":
+                    opt = DDI_OPCODES;
+                    break;
+
+                case "memory":
+                    opt = DDI_MEMORY;
+                    break;
+
+                default:
+                    raise_error(sprintf("Bad argument '%s' to debug_info(DINFO_DUMP).\n", args[0]));
+                    return 0;
+            }
+
+            return efun::dump_driver_info(opt, args[1..1]...);
+        }
+
+        case DINFO_DATA:
+        {
+            mixed * result;
+
+            if (!sizeof(args))
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (!intp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            if (sizeof(args) == 2 && !intp(args[1]))
+                raise_error("bag arg 3 to debug_info().\n");
+
+            switch(args[0])
+            {
+                case DID_STATUS:
+                    result = allocate(DID_STATUS_MAX);
+
+                    result[DID_ST_ACTIONS]               = efun::driver_info(DI_NUM_ACTIONS);
+                    result[DID_ST_ACTIONS_SIZE]          = efun::driver_info(DI_SIZE_ACTIONS);
+                    result[DID_ST_SHADOWS]               = efun::driver_info(DI_NUM_SHADOWS);
+                    result[DID_ST_SHADOWS_SIZE]          = efun::driver_info(DI_SIZE_SHADOWS);
+
+                    result[DID_ST_OBJECTS]               = efun::driver_info(DI_NUM_OBJECTS);
+                    result[DID_ST_OBJECTS_SIZE]          = efun::driver_info(DI_SIZE_OBJECTS);
+                    result[DID_ST_OBJECTS_SWAPPED]       = efun::driver_info(DI_NUM_OBJECTS_SWAPPED);
+                    result[DID_ST_OBJECTS_SWAP_SIZE]     = efun::driver_info(DI_SIZE_OBJECTS_SWAPPED);
+                    result[DID_ST_OBJECTS_LIST]          = efun::driver_info(DI_NUM_OBJECTS_IN_LIST);
+                    result[DID_ST_OBJECTS_NEWLY_DEST]    = efun::driver_info(DI_NUM_OBJECTS_NEWLY_DESTRUCTED);
+                    result[DID_ST_OBJECTS_DESTRUCTED]    = efun::driver_info(DI_NUM_OBJECTS_DESTRUCTED);
+                    result[DID_ST_OBJECTS_PROCESSED]     = efun::driver_info(DI_NUM_OBJECTS_LAST_PROCESSED);
+                    result[DID_ST_OBJECTS_AVG_PROC]      = efun::driver_info(DI_LOAD_AVERAGE_PROCESSED_OBJECTS_RELATIVE);
+
+                    result[DID_ST_OTABLE]                = efun::driver_info(DI_NUM_OBJECTS_IN_TABLE);
+                    result[DID_ST_OTABLE_SLOTS]          = efun::driver_info(DI_NUM_OBJECT_TABLE_SLOTS);
+                    result[DID_ST_OTABLE_SIZE]           = efun::driver_info(DI_SIZE_OBJECT_TABLE);
+
+                    result[DID_ST_HBEAT_OBJS]            = efun::driver_info(DI_NUM_HEARTBEATS);
+                    result[DID_ST_HBEAT_CALLS]           = efun::driver_info(DI_NUM_HEARTBEAT_ACTIVE_CYCLES);
+                    result[DID_ST_HBEAT_CALLS_TOTAL]     = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+                    result[DID_ST_HBEAT_SLOTS]           = efun::driver_info(DI_NUM_HEARTBEATS);
+                    result[DID_ST_HBEAT_SIZE]            = efun::driver_info(DI_SIZE_HEARTBEATS);
+                    result[DID_ST_HBEAT_PROCESSED]       = efun::driver_info(DI_NUM_HEARTBEATS_LAST_PROCESSED);
+                    result[DID_ST_HBEAT_AVG_PROC]        = efun::driver_info(DI_LOAD_AVERAGE_PROCESSED_HEARTBEATS_RELATIVE);
+
+                    result[DID_ST_CALLOUTS]              = efun::driver_info(DI_NUM_CALLOUTS);
+                    result[DID_ST_CALLOUT_SIZE]          = efun::driver_info(DI_SIZE_CALLOUTS);
+
+                    result[DID_ST_ARRAYS]                = efun::driver_info(DI_NUM_ARRAYS);
+                    result[DID_ST_ARRAYS_SIZE]           = efun::driver_info(DI_SIZE_ARRAYS);
+
+                    result[DID_ST_MAPPINGS]              = efun::driver_info(DI_NUM_MAPPINGS);
+                    result[DID_ST_MAPPINGS_SIZE]         = efun::driver_info(DI_SIZE_MAPPINGS);
+                    result[DID_ST_HYBRID_MAPPINGS]       = efun::driver_info(DI_NUM_MAPPINGS_HYBRID);
+                    result[DID_ST_HASH_MAPPINGS]         = efun::driver_info(DI_NUM_MAPPINGS_HASH);
+
+                    result[DID_ST_STRUCTS]               = efun::driver_info(DI_NUM_STRUCTS);
+                    result[DID_ST_STRUCTS_SIZE]          = efun::driver_info(DI_SIZE_STRUCTS);
+                    result[DID_ST_STRUCT_TYPES]          = efun::driver_info(DI_NUM_STRUCT_TYPES);
+                    result[DID_ST_STRUCT_TYPES_SIZE]     = efun::driver_info(DI_SIZE_STRUCT_TYPES);
+
+                    result[DID_ST_PROGS]                 = efun::driver_info(DI_NUM_PROGS);
+                    result[DID_ST_PROGS_SIZE]            = efun::driver_info(DI_SIZE_PROGS);
+
+                    result[DID_ST_PROGS_SWAPPED]         = efun::driver_info(DI_NUM_PROGS_SWAPPED);
+                    result[DID_ST_PROGS_SWAP_SIZE]       = efun::driver_info(DI_SIZE_PROGS_SWAPPED);
+
+                    result[DID_ST_USER_RESERVE]          = efun::driver_info(DI_MEMORY_RESERVE_USER);
+                    result[DID_ST_MASTER_RESERVE]        = efun::driver_info(DI_MEMORY_RESERVE_MASTER);
+                    result[DID_ST_SYSTEM_RESERVE]        = efun::driver_info(DI_MEMORY_RESERVE_SYSTEM);
+
+                    result[DID_ST_ADD_MESSAGE]           = efun::driver_info(DI_NUM_MESSAGES_OUT);
+                    result[DID_ST_PACKETS]               = efun::driver_info(DI_NUM_PACKETS_OUT);
+                    result[DID_ST_PACKET_SIZE]           = efun::driver_info(DI_SIZE_PACKETS_OUT);
+                    result[DID_ST_PACKETS_IN]            = efun::driver_info(DI_NUM_PACKETS_IN);
+                    result[DID_ST_PACKET_SIZE_IN]        = efun::driver_info(DI_SIZE_PACKETS_IN);
+
+                    result[DID_ST_APPLY]                 = efun::driver_info(DI_NUM_FUNCTION_NAME_CALLS);
+                    result[DID_ST_APPLY_HITS]            = efun::driver_info(DI_NUM_FUNCTION_NAME_CALL_HITS);
+
+                    result[DID_ST_STRINGS]               = efun::driver_info(DI_NUM_VIRTUAL_STRINGS);
+                    result[DID_ST_STRING_SIZE]           = efun::driver_info(DI_SIZE_STRINGS);
+                    result[DID_ST_STR_TABLE_SIZE]        = efun::driver_info(DI_SIZE_STRING_TABLE);
+                    result[DID_ST_STR_OVERHEAD]          = efun::driver_info(DI_SIZE_STRING_OVERHEAD);
+                    result[DID_ST_UNTABLED]              = efun::driver_info(DI_NUM_STRINGS_UNTABLED);
+                    result[DID_ST_UNTABLED_SIZE]         = efun::driver_info(DI_SIZE_STRINGS_UNTABLED);
+                    result[DID_ST_TABLED]                = efun::driver_info(DI_NUM_STRINGS_TABLED);
+                    result[DID_ST_TABLED_SIZE]           = efun::driver_info(DI_SIZE_STRINGS_TABLED);
+                    result[DID_ST_STR_SEARCHES]          = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUPS_BY_INDEX);
+                    result[DID_ST_STR_SEARCHLEN]         = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_INDEX);
+                    result[DID_ST_STR_SEARCHES_BYVALUE]  = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUPS_BY_VALUE);
+                    result[DID_ST_STR_SEARCHLEN_BYVALUE] = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_VALUE);
+                    result[DID_ST_STR_CHAINS]            = efun::driver_info(DI_NUM_STRING_TABLE_SLOTS_USED);
+                    result[DID_ST_STR_ADDED]             = efun::driver_info(DI_NUM_STRING_TABLE_STRINGS_ADDED);
+                    result[DID_ST_STR_DELETED]           = efun::driver_info(DI_NUM_STRING_TABLE_STRINGS_REMOVED);
+                    result[DID_ST_STR_COLLISIONS]        = efun::driver_info(DI_NUM_STRING_TABLE_COLLISIONS);
+                    result[DID_ST_STR_FOUND]             = efun::driver_info(DI_NUM_STRING_TABLE_HITS_BY_INDEX);
+                    result[DID_ST_STR_FOUND_BYVALUE]     = efun::driver_info(DI_NUM_STRING_TABLE_HITS_BY_VALUE);
+
+                    result[DID_ST_RX_CACHED]             = efun::driver_info(DI_NUM_REGEX);
+                    result[DID_ST_RX_TABLE]              = efun::driver_info(DI_NUM_REGEX_TABLE_SLOTS);
+                    result[DID_ST_RX_TABLE_SIZE]         = efun::driver_info(DI_SIZE_REGEX);
+                    result[DID_ST_RX_REQUESTS]           = efun::driver_info(DI_NUM_REGEX_LOOKUPS);
+                    result[DID_ST_RX_REQ_FOUND]          = efun::driver_info(DI_NUM_REGEX_LOOKUP_HITS);
+                    result[DID_ST_RX_REQ_COLL]           = efun::driver_info(DI_NUM_REGEX_LOOKUP_COLLISIONS);
+
+                    result[DID_ST_MB_FILE]               = efun::driver_info(DI_SIZE_BUFFER_FILE);
+                    result[DID_ST_MB_SWAP]               = efun::driver_info(DI_SIZE_BUFFER_SWAP);
+
+                    result[DID_ST_BOOT_TIME]             = efun::driver_info(DI_BOOT_TIME);
+                    break;
+
+                case DID_SWAP:
+                    result = allocate(DID_SWAP_MAX);
+
+                    result[DID_SW_PROGS]                 = efun::driver_info(DI_NUM_PROGS_SWAPPED);
+                    result[DID_SW_PROG_SIZE]             = efun::driver_info(DI_SIZE_PROGS_SWAPPED);
+                    result[DID_SW_PROG_UNSWAPPED]        = efun::driver_info(DI_NUM_PROGS_UNSWAPPED);
+                    result[DID_SW_PROG_U_SIZE]           = efun::driver_info(DI_SIZE_PROGS_UNSWAPPED);
+                    result[DID_SW_VARS]                  = efun::driver_info(DI_NUM_OBJECTS_SWAPPED);
+                    result[DID_SW_VAR_SIZE]              = efun::driver_info(DI_SIZE_OBJECTS_SWAPPED);
+                    result[DID_SW_FREE]                  = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE);
+                    result[DID_SW_FREE_SIZE]             = efun::driver_info(DI_SIZE_SWAP_BLOCKS_FREE);
+                    result[DID_SW_FILE_SIZE]             = efun::driver_info(DI_SIZE_SWAP_BLOCKS);
+                    result[DID_SW_REUSED]                = efun::driver_info(DI_SIZE_SWAP_BLOCKS_REUSED);
+                    result[DID_SW_SEARCHES]              = efun::driver_info(DI_NUM_SWAP_BLOCKS_REUSE_LOOKUPS);
+                    result[DID_SW_SEARCH_LEN]            = efun::driver_info(DI_NUM_SWAP_BLOCKS_REUSE_LOOKUP_STEPS);
+                    result[DID_SW_F_SEARCHES]            = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE_LOOKUPS);
+                    result[DID_SW_F_SEARCH_LEN]          = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE_LOOKUP_STEPS);
+                    result[DID_SW_COMPACT]               = efun::driver_info(DC_SWAP_COMPACT_MODE);
+                    result[DID_SW_RECYCLE_FREE]          = efun::driver_info(DI_SWAP_RECYCLE_PHASE);
+                    break;
+
+                case DID_MEMORY:
+                    result = allocate(DID_MEMORY_MAX);
+
+                    result[DID_MEM_NAME]                 = efun::driver_info(DI_MEMORY_ALLOCATOR_NAME);
+                    result[DID_MEM_SBRK]                 = efun::driver_info(DI_NUM_SYS_ALLOCATED_BLOCKS);
+                    result[DID_MEM_SBRK_SIZE]            = efun::driver_info(DI_SIZE_SYS_ALLOCATED_BLOCKS);
+                    result[DID_MEM_LARGE]                = efun::driver_info(DI_NUM_LARGE_BLOCKS_ALLOCATED);
+                    result[DID_MEM_LARGE_SIZE]           = efun::driver_info(DI_SIZE_LARGE_BLOCKS_ALLOCATED);
+                    result[DID_MEM_LFREE]                = efun::driver_info(DI_NUM_LARGE_BLOCKS_FREE);
+                    result[DID_MEM_LFREE_SIZE]           = efun::driver_info(DI_SIZE_LARGE_BLOCKS_FREE);
+                    result[DID_MEM_LWASTED]              = efun::driver_info(DI_NUM_LARGE_BLOCKS_WASTE);
+                    result[DID_MEM_LWASTED_SIZE]         = efun::driver_info(DI_SIZE_LARGE_BLOCKS_WASTE);
+                    result[DID_MEM_CHUNK]                = efun::driver_info(DI_NUM_SMALL_BLOCK_CHUNKS);
+                    result[DID_MEM_CHUNK_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCK_CHUNKS);
+                    result[DID_MEM_SMALL]                = efun::driver_info(DI_NUM_SMALL_BLOCKS_ALLOCATED);
+                    result[DID_MEM_SMALL_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCKS_ALLOCATED);
+                    result[DID_MEM_SFREE]                = efun::driver_info(DI_NUM_SMALL_BLOCKS_FREE);
+                    result[DID_MEM_SFREE_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCKS_FREE);
+                    result[DID_MEM_SWASTED]              = efun::driver_info(DI_NUM_SMALL_BLOCKS_WASTE);
+                    result[DID_MEM_SWASTED_SIZE]         = efun::driver_info(DI_SIZE_SMALL_BLOCKS_WASTE);
+                    result[DID_MEM_MINC_CALLS]           = efun::driver_info(DI_NUM_INCREMENT_SIZE_CALLS);
+                    result[DID_MEM_MINC_SUCCESS]         = efun::driver_info(DI_NUM_INCREMENT_SIZE_CALL_SUCCESSES);
+                    result[DID_MEM_MINC_SIZE]            = efun::driver_info(DI_SIZE_INCREMENT_SIZE_CALL_DIFFS);
+                    result[DID_MEM_PERM]                 = efun::driver_info(DI_NUM_UNMANAGED_BLOCKS);
+                    result[DID_MEM_PERM_SIZE]            = efun::driver_info(DI_SIZE_UNMANAGED_BLOCKS);
+                    result[DID_MEM_CLIB]                 = efun::driver_info(DI_NUM_REPLACEMENT_MALLOC_CALLS);
+                    result[DID_MEM_CLIB_SIZE]            = efun::driver_info(DI_SIZE_REPLACEMENT_MALLOC_CALLS);
+                    result[DID_MEM_OVERHEAD]             = efun::driver_info(DI_SIZE_SMALL_BLOCK_OVERHEAD);
+                    result[DID_MEM_ALLOCATED]            = efun::driver_info(DI_SIZE_MEMORY_USED) + efun::driver_info(DI_SIZE_MEMORY_OVERHEAD);
+                    result[DID_MEM_USED]                 = efun::driver_info(DI_SIZE_MEMORY_USED);
+                    result[DID_MEM_TOTAL_UNUSED]         = efun::driver_info(DI_SIZE_MEMORY_UNUSED);
+                    result[DID_MEM_DEFRAG_CALLS]         = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_FULL) + efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_TARGETED);
+                    result[DID_MEM_DEFRAG_CALLS_REQ]     = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_TARGETED);
+                    result[DID_MEM_DEFRAG_REQ_SUCCESS]   = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALL_TARGET_HITS);
+                    result[DID_MEM_DEFRAG_BLOCKS_INSPECTED] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_INSPECTED);
+                    result[DID_MEM_DEFRAG_BLOCKS_MERGED] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_MERGED);
+                    result[DID_MEM_DEFRAG_BLOCKS_RESULT] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_RESULTING);
+                    result[DID_MEM_AVL_NODES]            = efun::driver_info(DI_NUM_FREE_BLOCKS_AVL_NODES);
+                    result[DID_MEM_EXT_STATISTICS]       = efun::driver_info(DI_MEMORY_EXTENDED_STATISTICS);
+                    break;
+            }
+
+            if (sizeof(args) == 2)
+            {
+                int idx = args[0];
+                if (idx < 0 || idx >= sizeof(result))
+                    raise_error(sprintf("Illegal index for debug_info(): %d, expected 0..%d\n",
+                        idx, sizeof(result)-1));
+
+                return result[idx];
+            }
+            else
+                return result;
+        }
+
+        case DINFO_TRACE:
+        {
+            int which = DIT_CURRENT;
+
+            if (sizeof(args) > 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (sizeof(args))
+            {
+                if (!intp(args[0]))
+                    raise_error("bag arg 2 to debug_info().\n");
+                which = args[0];
+            }
+
+            switch (which)
+            {
+                case DIT_CURRENT:
+                    return efun::driver_info(DI_TRACE_CURRENT);
+
+                case DIT_ERROR:
+                    return efun::driver_info(DI_TRACE_LAST_ERROR) || ({ "No trace." });
+
+                case DIT_UNCAUGHT_ERROR:
+                    return efun::driver_info(DI_TRACE_LAST_UNCAUGHT_ERROR) || ({ "No trace." });
+
+                case DIT_STR_CURRENT:
+                    return efun::driver_info(DI_TRACE_CURRENT_AS_STRING);
+
+                case DIT_CURRENT_DEPTH:
+                    return efun::driver_info(DI_TRACE_CURRENT_DEPTH);
+
+                default:
+                    raise_error("bad arg 2 to debug_info().\n");
+            }
+
+        }
+
+        case DINFO_EVAL_NUMBER:
+            return efun::driver_info(DI_EVAL_NUMBER);
+    }
+    return 0;
+}
+
+#endif
diff --git a/secure/simul_efun/enable_commands.c b/secure/simul_efun/enable_commands.c
new file mode 100644
index 0000000..9d1c963
--- /dev/null
+++ b/secure/simul_efun/enable_commands.c
@@ -0,0 +1,30 @@
+/* These sefuns are to provide a replacement for the efuns enable_commands()
+ * and disable_commands().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#include <configuration.h>
+
+#if ! __EFUN_DEFINED__(enable_commands)
+
+void enable_commands()
+{
+    object ob = efun::previous_object();
+
+    efun::configure_object(ob, OC_COMMANDS_ENABLED, 1);
+    efun::set_this_player(ob);
+}
+
+#endif
+
+#if ! __EFUN_DEFINED__(disable_commands)
+
+void disable_commands()
+{
+    object ob = efun::previous_object();
+
+    efun::configure_object(ob, OC_COMMANDS_ENABLED, 0);
+    efun::set_this_player(0);
+}
+
+#endif
diff --git a/secure/simul_efun/hash.c b/secure/simul_efun/hash.c
new file mode 100644
index 0000000..51bbf60
--- /dev/null
+++ b/secure/simul_efun/hash.c
@@ -0,0 +1,17 @@
+#include "/sys/tls.h"
+
+deprecated string md5(mixed arg, varargs mixed* iterations)
+{
+    if (extern_call())
+         set_this_object(previous_object());
+
+    return hash(TLS_HASH_MD5, arg, iterations...);
+}
+
+deprecated string sha1(mixed arg, varargs mixed* iterations)
+{
+    if (extern_call())
+         set_this_object(previous_object());
+
+    return hash(TLS_HASH_SHA1, arg, iterations...);
+}
diff --git a/secure/simul_efun/livings.c b/secure/simul_efun/livings.c
new file mode 100644
index 0000000..160fcee
--- /dev/null
+++ b/secure/simul_efun/livings.c
@@ -0,0 +1,348 @@
+// * living_name-Behandlung
+
+#define clean_log(s)
+//#define clean_log(s) log_file("CLEAN_SIM",ctime(time())[4..18]+": "+(s));
+
+private nosave mapping living_name_m, name_living_m, netdead;
+
+private void InitLivingData(mixed wizinfo) {
+  // living_name ist ein Pointer der auch in der extra_wizinfo gehalten 
+  // wird, so kann die simul_efun neu geladen werden, ohne dass dieses
+  // Mapping verloren geht
+  if (!mappingp(living_name_m = wizinfo[LIVING_NAME]))
+    living_name_m = wizinfo[LIVING_NAME] = m_allocate(0, 1);
+
+  // Gleiches gilt fuer das Mapping Name-Living
+  if (!mappingp(name_living_m = wizinfo[NAME_LIVING]))
+    name_living_m = wizinfo[NAME_LIVING] = m_allocate(0, 1);
+
+  // Netztote sind ebenfalls in der extra_wizinfo
+  if (!mappingp(netdead = wizinfo[NETDEAD_MAP]))
+    netdead = wizinfo[NETDEAD_MAP] = ([]);
+}
+
+public varargs string getuuid( object ob )
+{
+    mixed *ret;
+
+    if ( !objectp(ob) )
+       ob = previous_object();
+
+    if ( !query_once_interactive(ob) )
+       return getuid(ob);
+
+    ret = (mixed)master()->get_userinfo( getuid(ob) );
+
+    if ( !pointerp(ret) || sizeof(ret) < 5 )
+       return getuid(ob);
+
+    // Username + "_" + CreationDate
+    return ret[0] + "_" + ret[5];
+}
+
+void set_object_living_name(string livname, object obj)
+{
+  string old;
+  mixed a;
+  int i;
+
+  if (previous_object()==obj || previous_object()==this_object() ||
+      previous_object()==master())
+  {
+    if(!livname || !stringp(livname)) {
+      set_this_object(previous_object());
+      raise_error(sprintf("%O: illegal living name: %O\n", obj, livname));
+    }
+    if (old = living_name_m[obj]) {
+      if (pointerp(a = name_living_m[old])) {
+       a[member(a, obj)] = 0;
+      } else {
+       m_delete(name_living_m, old);
+      }
+    }
+    living_name_m[obj] = livname;
+    if (a = name_living_m[livname]) {
+      if (!pointerp(a)) {
+       name_living_m[livname] = ({a, obj});
+       return;
+      }
+      /* Try to reallocate entry from destructed object */
+      if ((i = member(a, 0)) >= 0) {
+       a[i] = obj;
+       return;
+      }
+      name_living_m[livname] = a + ({obj});
+      return;
+    }
+    name_living_m[livname] = obj;
+  }
+}
+
+void set_living_name(string livname)
+{
+  set_object_living_name(livname,previous_object());
+}
+
+void remove_living_name()
+{
+  string livname;
+
+  if (!previous_object())
+    return;
+  if (livname=living_name_m[previous_object()])
+  {
+    m_delete(living_name_m,previous_object());
+    if (objectp(name_living_m[livname]))
+    {
+      if (name_living_m[livname]==previous_object())
+       m_delete(name_living_m,livname);
+      return;
+    }
+    if (pointerp(name_living_m[livname]))
+    {
+      name_living_m[livname]-=({previous_object()});
+      if (!sizeof(name_living_m[livname]))
+       m_delete(name_living_m,livname);
+    }
+  }
+}
+
+void _set_netdead()
+{
+  if (query_once_interactive(previous_object()))
+    netdead[getuid(previous_object())]=previous_object();
+}
+
+void _remove_netdead()
+{
+  m_delete(netdead,getuid(previous_object()));
+}
+
+object find_netdead(string uuid)
+{
+  int i;
+  string uid;
+  // Wenn sscanf() 2 liefert, ist uuid auch ne uuid.
+  int is_uuid = sscanf(uuid, "%s_%d", uid, i) == 2;
+  if (!is_uuid)
+    uid = uuid;
+ 
+  if (is_uuid && getuuid(netdead[uid]) != uuid)
+      return 0;
+
+  return netdead[uid];
+}
+
+string *dump_netdead()
+{
+  return m_indices(netdead);
+}
+
+object find_living(string livname) {
+  mixed *a, r;
+  int i;
+
+  if (pointerp(r = name_living_m[livname])) {
+    if (member(r,0)>=0)
+      r-=({0});
+    if (!sizeof(r)){
+      m_delete(name_living_m,livname);
+      clean_log(sprintf("find_living loescht %s\n",livname));
+      return 0;
+    }
+    if ( !living(r = (a = r)[0])) {
+      for (i = sizeof(a); --i;) {
+       if (living(a[<i])) {
+         r = a[<i];
+         a[<i] = a[0];
+         return a[0] = r;
+       }
+      }
+    }
+    return r;
+  }
+  return living(r) && r;
+}
+
+object *find_livings(string livname)
+{
+  mixed r;
+
+  if (pointerp(r=name_living_m[livname]))
+    r-=({0});
+  else
+    if (objectp(r))
+      r=({r});
+  if (!pointerp(r)||!sizeof(r))
+    return 0;
+  return r;
+}
+
+object find_player(string uuid) {
+  object *objs;
+  mixed res;
+  int i;
+  string uid;
+
+  if (!stringp(uuid))
+    return 0;
+  // Wenn sscanf() 2 liefert, ist uuid auch ne uuid.
+  int is_uuid = sscanf(uuid, "%s_%d", uid, i) == 2;
+  if (!is_uuid)
+    uid = uuid;
+
+  if (pointerp(res = name_living_m[uid])) {
+    // zerstoerte Objekte ggf. entfernen
+    if (member(res,0)>=0) {
+      res-=({0});
+      name_living_m[uid] = res;
+    }
+    // ggf. Namen ohne Objekte entfernen.
+    if (!sizeof(res)){
+      m_delete(name_living_m,uid);
+      clean_log(sprintf("find_player loescht %s\n",uid));
+      return 0;
+    }
+    objs = res;
+    res = objs[0];
+    // Wenn das 0. Element der Spieler ist, sind wir fertig. Ansonsten suchen
+    // wir den Spieler und schieben ihn an die 0. Stelle im Array.
+    if ( !query_once_interactive(res)) {
+      for (i = sizeof(objs); --i;) {
+       if (objs[<i] && query_once_interactive(objs[<i])) {
+         res = objs[<i];
+         objs[<i] = objs[0];
+         objs[0] = res;
+         break;
+       }
+      }
+      res = (objectp(res) && query_once_interactive(res)) ? res : 0;
+    }
+    // else: in res steht der Spieler schon.
+  }
+  else if (objectp(res)) // r ist nen Einzelobjekt
+    res = query_once_interactive(res) ? res : 0;
+
+  // res ist jetzt ggf. der Spieler. Aber wenn der ne andere als die gesuchte
+  // UUID hat, ist das Ergebnis trotzdem 0.
+  if (is_uuid && getuuid(res)!=uuid)
+      return 0;
+
+  // endlich gefunden
+  return res;
+}
+
+private int check_match( string str, int players_only )
+{
+    mixed match;
+
+    if ( !(match = name_living_m[str]) )
+       return 0;
+
+    if ( !pointerp(match) )
+       match = ({ match });
+
+    match -= ({0});
+
+    if ( sizeof(match) ){
+       if ( players_only )
+           return sizeof(filter( match, #'query_once_interactive/*'*/ ))
+              > 0;
+       else
+           return 1;
+    }
+
+    m_delete( name_living_m, str );
+    clean_log( sprintf("check_match loescht %s\n", str) );
+    return 0;
+}
+
+//TODO:: string|string* exclude
+varargs mixed match_living( string str, int players_only, mixed exclude )
+{
+    int i, s;
+    mixed match, *user;
+
+    if ( !str || str == "" )
+       return 0;
+
+    if ( !pointerp(exclude) )
+       exclude = ({ exclude });
+
+    if ( member(exclude, str) < 0 && check_match(str, players_only) )
+       return str;
+
+    user = m_indices(name_living_m);
+    s = sizeof(str);
+    match = 0;
+
+    for ( i = sizeof(user); i--; )
+       if ( str == user[i][0..s-1] && member( exclude, user[i] ) < 0 )
+           if ( match )
+              return -1;
+           else
+              if ( check_match(user[i], players_only) )
+                  match = user[i];
+
+    if ( !match )
+       return -2;
+
+    return match;
+}
+
+mixed *dump_livings()
+{
+  return sort_array(m_indices(name_living_m),#'>);
+}
+
+// * regelmaessig Listen von Ballast befreien
+private void clean_name_living_m(string *keys, int left, int num)
+{
+  int i, j;
+  mixed a;
+
+  while (left && num--)
+  {
+    if (pointerp(a = name_living_m[keys[--left]]) && member(a, 0)>=0)
+    {
+      a-=({0});
+      name_living_m[keys[left]] = a;
+    }
+    if (!a || (pointerp(a) && !sizeof(a)))
+    {
+      clean_log("Toasting "+keys[left]+"\n");
+      m_delete(name_living_m, keys[left]);
+    } else clean_log(sprintf("KEEPING %s (%O)\n",keys[left],pointerp(a)?sizeof(a):a));
+  }
+  if (left)
+    efun::call_out(#'clean_name_living_m, 1, keys, left, 80);
+  else
+    clean_log("Clean process finished\n");
+}
+
+private void clean_netdead() {
+  int i;
+  string *s;
+  object ob;
+
+  s=m_indices(netdead);
+  for (i=sizeof(s)-1;i>=0;i--)
+    if (!objectp(ob=netdead[s[i]]) || interactive(ob))
+      m_delete(netdead,s[i]);
+}
+
+private void CleanLivingData() {
+  if (find_call_out(#'clean_name_living_m) < 0) {
+    clean_log("Starting clean process\n");
+    efun::call_out(#'clean_name_living_m,
+                 1,
+                 m_indices(name_living_m),
+                 sizeof(name_living_m),
+                 80
+                 );
+  }
+  efun::call_out(#'clean_netdead,2);
+}
+
+#undef clean_log
+
diff --git a/secure/simul_efun/object_info.c b/secure/simul_efun/object_info.c
new file mode 100644
index 0000000..ed7c009
--- /dev/null
+++ b/secure/simul_efun/object_info.c
@@ -0,0 +1,106 @@
+/* This sefun is to provide the old semantics of the efun object_info().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if __EFUN_DEFINED__(driver_info) /* Newer version is there */
+
+#include <objectinfo.h>
+#include <object_info.h>
+
+mixed object_info(object ob, int what, varargs int* index)
+{
+    mixed * result;
+
+    if (sizeof(index) > 1)
+        raise_error("Too many arguments to object_info\n");
+
+    switch(what)
+    {
+        default:
+            raise_error(sprintf("Illegal value %d for object_info().\n", what));
+
+        case OINFO_BASIC:
+        {
+            result = allocate(OIB_MAX);
+
+            result[OIB_HEART_BEAT]        = efun::object_info(ob, OC_HEART_BEAT);
+            result[OIB_IS_WIZARD]         = 0;   /* Not supported anymore. */
+            result[OIB_ENABLE_COMMANDS]   = efun::object_info(ob, OC_COMMANDS_ENABLED);
+            result[OIB_CLONE]             = efun::clonep(ob);
+            result[OIB_DESTRUCTED]        = 0;   /* Not possible anymore. */
+            result[OIB_SWAPPED]           = efun::object_info(ob, OI_SWAPPED);
+            result[OIB_ONCE_INTERACTIVE]  = efun::object_info(ob, OI_ONCE_INTERACTIVE);
+            result[OIB_RESET_STATE]       = efun::object_info(ob, OI_RESET_STATE);
+            result[OIB_WILL_CLEAN_UP]     = efun::object_info(ob, OI_WILL_CLEAN_UP);
+            result[OIB_LAMBDA_REFERENCED] = efun::object_info(ob, OI_LAMBDA_REFERENCED);
+            result[OIB_SHADOW]            = efun::object_info(ob, OI_SHADOW_PREV) && 1;
+            result[OIB_REPLACED]          = efun::object_info(ob, OI_REPLACED);
+            result[OIB_NEXT_RESET]        = efun::object_info(ob, OI_NEXT_RESET_TIME);
+            result[OIB_TIME_OF_REF]       = efun::object_info(ob, OI_LAST_REF_TIME);
+            result[OIB_REF]               = efun::object_info(ob, OI_OBJECT_REFS);
+            result[OIB_GIGATICKS]         = efun::object_info(ob, OI_GIGATICKS);
+            result[OIB_TICKS]             = efun::object_info(ob, OI_TICKS);
+            result[OIB_SWAP_NUM]          = efun::object_info(ob, OI_SWAP_NUM);
+            result[OIB_PROG_SWAPPED]      = efun::object_info(ob, OI_PROG_SWAPPED);
+            result[OIB_VAR_SWAPPED]       = efun::object_info(ob, OI_VAR_SWAPPED);
+            result[OIB_NAME]              = efun::object_name(ob);
+            result[OIB_LOAD_NAME]         = efun::load_name(ob);
+            result[OIB_NEXT_ALL]          = efun::object_info(ob, OI_OBJECT_NEXT);
+            result[OIB_PREV_ALL]          = efun::object_info(ob, OI_OBJECT_PREV);
+            result[OIB_NEXT_CLEANUP]      = efun::object_info(ob, OI_NEXT_CLEANUP_TIME);
+            break;
+        }
+
+        case OINFO_POSITION:
+        {
+            result = allocate(OIP_MAX);
+
+            result[OIP_NEXT] = efun::object_info(ob, OI_OBJECT_NEXT);
+            result[OIP_PREV] = efun::object_info(ob, OI_OBJECT_PREV);
+            result[OIP_POS]  = efun::object_info(ob, OI_OBJECT_POS);
+            break;
+        }
+
+        case OINFO_MEMORY:
+        {
+            result = allocate(OIM_MAX);
+
+            result[OIM_REF]                = efun::object_info(ob, OI_PROG_REFS);
+            result[OIM_NAME]               = efun::program_name(ob);
+            result[OIM_PROG_SIZE]          = efun::object_info(ob, OI_PROG_SIZE);
+            result[OIM_NUM_FUNCTIONS]      = efun::object_info(ob, OI_NUM_FUNCTIONS);
+            result[OIM_SIZE_FUNCTIONS]     = efun::object_info(ob, OI_SIZE_FUNCTIONS);
+            result[OIM_NUM_VARIABLES]      = efun::object_info(ob, OI_NUM_VARIABLES);
+            result[OIM_SIZE_VARIABLES]     = efun::object_info(ob, OI_SIZE_VARIABLES);
+            result[OIM_NUM_STRINGS]        = efun::object_info(ob, OI_NUM_STRINGS);
+            result[OIM_SIZE_STRINGS]       = efun::object_info(ob, OI_SIZE_STRINGS);
+            result[OIM_SIZE_STRINGS_DATA]  = efun::object_info(ob, OI_SIZE_STRINGS_DATA);
+            result[OIM_SIZE_STRINGS_TOTAL] = efun::object_info(ob, OI_SIZE_STRINGS_DATA_TOTAL);
+            result[OIM_NUM_INHERITED]      = efun::object_info(ob, OI_NUM_INHERITED);
+            result[OIM_SIZE_INHERITED]     = efun::object_info(ob, OI_SIZE_INHERITED);
+            result[OIM_TOTAL_SIZE]         = efun::object_info(ob, OI_PROG_SIZE_TOTAL);
+            result[OIM_DATA_SIZE]          = efun::object_info(ob, OI_DATA_SIZE);
+            result[OIM_TOTAL_DATA_SIZE]    = efun::object_info(ob, OI_DATA_SIZE_TOTAL);
+            result[OIM_NO_INHERIT]         = efun::object_info(ob, OI_NO_INHERIT);
+            result[OIM_NO_CLONE]           = efun::object_info(ob, OI_NO_CLONE);
+            result[OIM_NO_SHADOW]          = efun::object_info(ob, OI_NO_SHADOW);
+            result[OIM_NUM_INCLUDES]       = efun::object_info(ob, OI_NUM_INCLUDED);
+            result[OIM_SHARE_VARIABLES]    = efun::object_info(ob, OI_SHARE_VARIABLES);
+            break;
+        }
+    }
+
+    if (sizeof(index))
+    {
+        int idx = index[0];
+        if (idx < 0 || idx >= sizeof(result))
+            raise_error(sprintf("Illegal index for object_info(): %d, expected 0..%d\n",
+                idx, sizeof(result)-1));
+
+        return result[idx];
+    }
+    else
+        return result;
+}
+
+#endif
diff --git a/secure/simul_efun/query_editing.c b/secure/simul_efun/query_editing.c
new file mode 100644
index 0000000..5146dcd
--- /dev/null
+++ b/secure/simul_efun/query_editing.c
@@ -0,0 +1,17 @@
+/* This sefun is to provide a replacement for the efun query_editing().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_editing)
+
+#include <interactive_info.h>
+
+int|object query_editing(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    return efun::interactive_info(ob, II_EDITING);
+}
+
+#endif
diff --git a/secure/simul_efun/query_idle.c b/secure/simul_efun/query_idle.c
new file mode 100644
index 0000000..93a32ae
--- /dev/null
+++ b/secure/simul_efun/query_idle.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_idle().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_idle)
+
+#include <interactive_info.h>
+
+int query_idle(object ob)
+{
+    return efun::interactive_info(ob, II_IDLE);
+}
+
+#endif
diff --git a/secure/simul_efun/query_input_pending.c b/secure/simul_efun/query_input_pending.c
new file mode 100644
index 0000000..dd8c311
--- /dev/null
+++ b/secure/simul_efun/query_input_pending.c
@@ -0,0 +1,17 @@
+/* This sefun is to provide a replacement for the efun query_input_pending().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_input_pending)
+
+#include <interactive_info.h>
+
+object query_input_pending(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    return efun::interactive_info(ob, II_INPUT_PENDING);
+}
+
+#endif
diff --git a/secure/simul_efun/query_ip_name.c b/secure/simul_efun/query_ip_name.c
new file mode 100644
index 0000000..ada6e07
--- /dev/null
+++ b/secure/simul_efun/query_ip_name.c
@@ -0,0 +1,48 @@
+/* This sefun is to provide a replacement for the efuns query_ip_name() and
+ * query_ip_number().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_ip_name)
+
+#include <interactive_info.h>
+
+private varargs string _query_ip_name(object player)
+{
+    object ob = player;
+    ob ||= efun::this_player();
+
+    player = efun::interactive_info(ob, II_IP_ADDRESS);
+    return efun::interactive_info(ob, II_IP_NAME);
+}
+
+private varargs string _query_ip_number(object player)
+{
+    object ob = player;
+    ob ||= efun::this_player();
+
+    player = efun::interactive_info(ob, II_IP_ADDRESS);
+    return efun::interactive_info(ob, II_IP_NUMBER);
+}
+
+// * Herkunfts-Ermittlung
+string query_ip_number(object ob)
+{
+  ob= ob || this_player();
+  if (!objectp(ob) || !interactive(ob)) return 0;
+  if(ob->query_realip() && (string)ob->query_realip()!="")
+  {
+    return (string)ob->query_realip();
+  }
+  return _query_ip_number(ob);
+}
+
+string query_ip_name(mixed ob)
+{
+  if ( !ob || objectp(ob) )
+      ob=query_ip_number(ob);
+  return (string)"/p/daemon/iplookup"->host(ob);
+}
+
+#endif
+
diff --git a/secure/simul_efun/query_limits.c b/secure/simul_efun/query_limits.c
new file mode 100644
index 0000000..ecbf390
--- /dev/null
+++ b/secure/simul_efun/query_limits.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_limits().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_limits)
+
+#include <driver_info.h>
+
+varargs int* query_limits(int def)
+{
+    return efun::driver_info(def ? DC_DEFAULT_RUNTIME_LIMITS : DI_CURRENT_RUNTIME_LIMITS);
+}
+
+#endif
diff --git a/secure/simul_efun/query_load_average.c b/secure/simul_efun/query_load_average.c
new file mode 100644
index 0000000..3b36f0e
--- /dev/null
+++ b/secure/simul_efun/query_load_average.c
@@ -0,0 +1,16 @@
+/* This sefun is to provide a replacement for the efun query_load_average().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_load_average)
+
+#include <driver_info.h>
+
+string query_load_average()
+{
+    return efun::sprintf("%.2f cmds/s, %.2f comp lines/s",
+        efun::driver_info(DI_LOAD_AVERAGE_COMMANDS),
+        efun::driver_info(DI_LOAD_AVERAGE_LINES));
+}
+
+#endif
diff --git a/secure/simul_efun/query_mud_port.c b/secure/simul_efun/query_mud_port.c
new file mode 100644
index 0000000..bff009d
--- /dev/null
+++ b/secure/simul_efun/query_mud_port.c
@@ -0,0 +1,35 @@
+/* This sefun is to provide a replacement for the efun query_mud_port().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_mud_port)
+
+#include <interactive_info.h>
+#include <driver_info.h>
+
+int query_mud_port(varargs int*|object* args)
+{
+    if(!sizeof(args) && efun::this_player() && efun::interactive(this_player()))
+        return efun::interactive_info(this_player(), II_MUD_PORT);
+
+    if(sizeof(args) > 1)
+        raise_error("Too many arguments to query_mud_port\n");
+
+    if(sizeof(args) && efun::objectp(args[0]) && efun::interactive(args[0]))
+        return efun::interactive_info(args[0], II_MUD_PORT);
+
+    if(sizeof(args) && intp(args[0]))
+    {
+        int* ports = efun::driver_info(DI_MUD_PORTS);
+        if(args[0] < -1 || args[0] >= sizeof(ports))
+            raise_error(efun::sprintf("Bad arg 1 to query_mud_port(): value %d out of range.\n", args[0]));
+        else if(args[0] == -1)
+            return sizeof(ports);
+        else
+            return ports[args[0]];
+    }
+
+    return efun::driver_info(DI_MUD_PORTS)[0];
+}
+
+#endif
diff --git a/secure/simul_efun/query_once_interactive.c b/secure/simul_efun/query_once_interactive.c
new file mode 100644
index 0000000..bc7829f
--- /dev/null
+++ b/secure/simul_efun/query_once_interactive.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_once_interactive().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_once_interactive)
+
+#include <object_info.h>
+
+int query_once_interactive(object ob)
+{
+    return efun::object_info(ob, OI_ONCE_INTERACTIVE);
+}
+
+#endif
diff --git a/secure/simul_efun/query_snoop.c b/secure/simul_efun/query_snoop.c
new file mode 100644
index 0000000..e6a69c0
--- /dev/null
+++ b/secure/simul_efun/query_snoop.c
@@ -0,0 +1,30 @@
+/* This sefun is to provide a replacement for the efun query_snoop().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+
+#include <interactive_info.h>
+
+private object _query_snoop(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    object prev = efun::previous_object();
+    efun::set_this_object(prev);
+#if ! __EFUN_DEFINED__(query_snoop)
+    return efun::interactive_info(ob, II_SNOOP_NEXT);
+#else
+    return efun::query_snoop(ob);
+#endif
+}
+
+nomask object query_snoop(object who) {
+  object snooper;
+
+  snooper=_query_snoop(who);
+  if (!snooper) return 0;
+  if (query_wiz_grp(snooper)>query_wiz_grp(getuid(previous_object())) &&
+      IS_ARCH(snooper)) return 0;
+  return snooper;
+}
diff --git a/secure/simul_efun/set_heart_beat.c b/secure/simul_efun/set_heart_beat.c
new file mode 100644
index 0000000..a3995eb
--- /dev/null
+++ b/secure/simul_efun/set_heart_beat.c
@@ -0,0 +1,24 @@
+/* This sefun is to provide a replacement for the efun set_heart_beat().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(set_heart_beat)
+
+#include <configuration.h>
+
+int set_heart_beat(int flag)
+{
+    object ob = efun::previous_object();
+    int hb = efun::object_info(ob, OC_HEART_BEAT);
+
+    if (!flag == !hb)
+        return 0;
+
+    efun::configure_object(ob, OC_HEART_BEAT, flag);
+
+    return 1;
+}
+
+#endif
+
+
diff --git a/secure/simul_efun/set_is_wizard.c b/secure/simul_efun/set_is_wizard.c
new file mode 100644
index 0000000..001ccdb
--- /dev/null
+++ b/secure/simul_efun/set_is_wizard.c
@@ -0,0 +1,137 @@
+/* These are the special commands from the driver that are activated with
+ * set_is_wizard(). These functions must be added to the player object.
+ * Also set_is_wizard() must be called in the corresponding player object,
+ * not as an (simul-)efun.
+ */
+
+#include <commands.h>
+#include <driver_info.h>
+
+/* is_wizard:
+ *  1: has actions,
+ *  0: never had actions,
+ * -1: had actions once.
+ */
+private nosave int is_wizard;
+
+private int driver_malloc(string str)
+{
+    if(is_wizard <= 0)
+        return 0;
+
+    if(!sizeof(str))
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC));
+        return 1;
+    }
+
+    if(str == "extstats")
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC_EXTENDED));
+        return 1;
+    }
+
+    return 0;
+}
+
+private int driver_dumpallobj(string str)
+{
+    if(is_wizard <= 0 || sizeof(str))
+        return 0;
+
+    write("Dumping to /OBJ_DUMP ... ");
+    efun::dump_driver_info(DDI_OBJECTS);
+    efun::dump_driver_info(DDI_OBJECTS_DESTRUCTED);
+    write("done\n");
+    return 1;
+}
+
+private int driver_opcdump(string str)
+{
+    if(is_wizard <= 0 || sizeof(str))
+        return 0;
+
+    efun::dump_driver_info(DDI_OPCODES);
+    return 1;
+}
+
+private int driver_status(string str)
+{
+    int opt;
+    if(is_wizard <= 0)
+        return 0;
+
+    switch(str || "")
+    {
+        case "":
+            opt = DI_STATUS_TEXT_MEMORY;
+            break;
+
+        case "tables":
+        case " tables":
+            opt = DI_STATUS_TEXT_TABLES;
+            break;
+
+        case "swap":
+        case " swap":
+            opt = DI_STATUS_TEXT_SWAP;
+            break;
+
+        case "malloc":
+        case " malloc":
+            opt = DI_STATUS_TEXT_MALLOC;
+            break;
+
+        case "malloc extstats":
+        case " malloc extstats":
+            opt = DI_STATUS_TEXT_MALLOC_EXTENDED;
+            break;
+
+        default:
+            return 0;
+    }
+
+    write(efun::driver_info(opt));
+    return 1;
+}
+
+int set_is_wizard(varargs <object|int>* args)
+{
+    int oldval = is_wizard;
+
+    if(!sizeof(args))
+        raise_error("Too few arguments to set_is_wizard\n");
+    if(sizeof(args) > 2)
+        raise_error("Too many arguments to set_is_wizard\n");
+    if(!objectp(args[0]))
+        raise_error("Bad arg 1 to set_is_wizard()\n");
+    if(args[0] != this_object())
+        raise_error("Only set_is_wizard for the current object supported\n");
+    if(this_player() != this_object())
+        raise_error("The current object must be this_player() for set_is_wizard()\n");
+    if(sizeof(args) == 2 && !intp(args[1]))
+        raise_error("Bad arg 2 to set_is_wizard()\n");
+
+    if(sizeof(args) == 2 && !args[1])
+    {
+        if(is_wizard > 0)
+            is_wizard = -1;
+    }
+    else if(sizeof(args) == 2 && args[1]<0)
+    {
+         // Just return the old value.
+    }
+    else
+    {
+        if(!is_wizard)
+        {
+            add_action(#'driver_malloc, "malloc");
+            add_action(#'driver_dumpallobj, "dumpallobj");
+            add_action(#'driver_opcdump, "opcdump");
+            add_action(#'driver_status, "status", AA_NOSPACE);
+        }
+        is_wizard = 1;
+    }
+
+    return oldval > 0;
+}
diff --git a/secure/simul_efun/set_prompt.c b/secure/simul_efun/set_prompt.c
new file mode 100644
index 0000000..0b59692
--- /dev/null
+++ b/secure/simul_efun/set_prompt.c
@@ -0,0 +1,21 @@
+/* This sefun is to provide a replacement for the efun set_prompt().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(set_prompt)
+
+#include <configuration.h>
+
+varargs string|closure set_prompt(string|closure|int prompt, object ob)
+{
+    ob ||= efun::this_player();
+
+    mixed oldprompt = efun::interactive_info(ob, IC_PROMPT);
+
+    if(!intp(prompt))
+        efun::configure_interactive(ob, IC_PROMPT, prompt);
+
+    return oldprompt;
+}
+
+#endif
diff --git a/secure/simul_efun/shadow.c b/secure/simul_efun/shadow.c
new file mode 100644
index 0000000..7608c73
--- /dev/null
+++ b/secure/simul_efun/shadow.c
@@ -0,0 +1,55 @@
+/* These sefuns are to provide a replacement for the efun query_shadowing()
+ * and the old semantics of the efun shadow().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_shadowing)
+
+#include <object_info.h>
+
+object query_shadowing(object ob)
+{
+    return efun::object_info(ob, OI_SHADOW_PREV);
+}
+
+private object _shadow(object ob, int flag)
+{
+    if(flag)
+    {
+        object shadower = efun::previous_object(1);
+        efun::set_this_object(shadower);
+
+        if (efun::shadow(ob))
+            return efun::object_info(shadower, OI_SHADOW_PREV);
+        else
+            return 0;
+    }
+    else
+        return efun::object_info(ob, OI_SHADOW_NEXT);
+}
+#else
+private object _shadow(object ob, int flag)
+{
+  set_this_object(previous_object(1));
+  return efun::shadow(ob, flag);
+}
+#endif
+
+
+public object shadow(object ob, int flag)
+{
+  object res = funcall(#'_shadow,ob, flag);
+  if (flag)
+    "/secure/shadowmaster"->RegisterShadow(previous_object());
+  return res;
+}
+
+private void _unshadow() {
+  set_this_object(previous_object(1));
+  efun::unshadow();
+}
+
+public void unshadow() {
+  funcall(#'_unshadow);
+  "/secure/shadowmaster"->UnregisterShadow(previous_object());
+}
diff --git a/secure/simul_efun/simul_efun.c b/secure/simul_efun/simul_efun.c
new file mode 100644
index 0000000..84e0b78
--- /dev/null
+++ b/secure/simul_efun/simul_efun.c
@@ -0,0 +1,2016 @@
+// MorgenGrauen MUDlib
+//
+// simul_efun.c -- simul efun's
+//
+// $Id: simul_efun.c 7408 2010-02-06 00:27:25Z Zesstra $
+#pragma strict_types,save_types,rtt_checks
+#pragma no_clone,no_shadow,no_inherit
+#pragma pedantic,range_check,warn_deprecated
+#pragma warn_empty_casts,warn_missing_return,warn_function_inconsistent
+
+// Absolute Pfade erforderlich - zum Zeitpunkt, wo der Master geladen
+// wird, sind noch keine Include-Pfade da ...
+
+#define SNOOPLOGFILE "SNOOP"
+#define ASNOOPLOGFILE "ARCH/SNOOP"
+
+#include "/secure/config.h"
+#include "/secure/wizlevels.h"
+#include "/sys/snooping.h"
+#include "/sys/language.h"
+#include "/sys/thing/properties.h"
+#include "/sys/wizlist.h"
+#include "/sys/erq.h"
+#include "/sys/lpctypes.h"
+#include "/sys/daemon.h"
+#include "/sys/player/base.h"
+#include "/sys/thing/description.h"
+#include "/sys/container.h"
+#include "/sys/defines.h"
+#include "/sys/telnet.h"
+#include "/sys/objectinfo.h"
+#include "/sys/files.h"
+#include "/sys/strings.h"
+#include "/sys/time.h"
+#include "/sys/lpctypes.h"
+#include "/sys/notify_fail.h"
+#include "/sys/tls.h"
+#include "/sys/input_to.h"
+#include "/sys/object_info.h"
+
+/* function prototypes
+ */
+string dtime(int wann);
+varargs int log_file(string file, string txt, int size_to_break);
+int query_wiz_level(mixed player);
+nomask varargs int snoop(object me, object you);
+varargs string country(mixed ip, string num);
+int query_wiz_grp(mixed wiz);
+public varargs object deep_present(mixed what, object ob);
+nomask int secure_level();
+nomask string secure_euid();
+public nomask int process_call();
+nomask mixed __create_player_dummy(string name);
+varargs string replace_personal(string str, mixed *obs, int caps);
+
+
+//replacements for dropped efuns in LD
+#if !__EFUN_DEFINED__(extract)
+varargs string extract(string str, int from, int to);
+#endif
+#if !__EFUN_DEFINED__(slice_array)
+varargs mixed slice_array(mixed array, int from, int to);
+#endif
+#if !__EFUN_DEFINED__(member_array)
+int member_array(mixed item, mixed arraystring);
+#endif
+
+// Include the different sub 'modules' of the simul_efun
+#include __DIR__"debug_info.c"
+#include __DIR__"enable_commands.c"
+#include __DIR__"hash.c"
+#include __DIR__"object_info.c"
+#include __DIR__"query_editing.c"
+#include __DIR__"query_idle.c"
+#include __DIR__"query_input_pending.c"
+#include __DIR__"query_ip_name.c"
+#include __DIR__"query_limits.c"
+#include __DIR__"query_load_average.c"
+#include __DIR__"query_mud_port.c"
+#include __DIR__"query_once_interactive.c"
+#include __DIR__"query_snoop.c"
+#include __DIR__"set_heart_beat.c"
+#if __BOOT_TIME__ < 1456261859
+#include __DIR__"set_prompt.c"
+#endif
+#include __DIR__"shadow.c"
+#include __DIR__"livings.c"
+#include __DIR__"comm.c"
+
+#define TO        efun::this_object()
+#define TI        efun::this_interactive()
+#define TP        efun::this_player()
+#define PO        efun::previous_object(0)
+#define LEVEL(x) query_wiz_level(x)
+#define NAME(x)  capitalize(getuid(x))
+
+#define DEBUG(x) if (find_player("zesstra")) \
+  tell_object(find_player("zesstra"),x)
+
+mixed dtime_cache = ({-1,""});
+
+#ifdef IOSTATS
+struct iostat_s {
+  string oname;
+  int time;
+  int size;
+};
+mixed saveo_stat = ({ 0,allocate(200, 0) });
+mixed restoreo_stat = ({ 0,allocate(200,0) });
+//mixed writefile_stat = ({ 0,allocate(100,(<iostat_s>)) });
+//mixed readfile_stat = ({ 0,allocate(100,(<iostat_s>)) });
+//mixed log_stat = ({ 0,allocate(100,(<iostat_s>)) });
+
+mixed ___iostats(int type) {
+  switch(type) {
+    case 1:
+      return saveo_stat;
+    case 2:
+      return restoreo_stat;
+/*    case 3:
+      return writefile_stat;
+    case 4:
+      return readfile_stat;
+    case 5:
+      return log_stat;
+      */
+  }
+  return 0;
+}
+#endif
+
+// Nicht jeder Magier muss die simul_efun entsorgen koennen.
+string NotifyDestruct(object caller) {
+    if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
+      return "Du darfst das simul_efun Objekt nicht zerstoeren!\n";
+    }
+    return 0;
+}
+
+public nomask void remove_interactive( object ob )
+{
+    if ( objectp(ob) && previous_object()
+        && object_name(previous_object())[0..7] != "/secure/"
+        && ((previous_object() != ob
+             && (ob != this_player() || ob != this_interactive()))
+            || (previous_object() == ob
+               && (this_player() && this_player() != ob
+                   || this_interactive() && this_interactive() != ob)) ) )
+
+       log_file( "PLAYERDEST",
+                sprintf( "%s: %O ausgeloggt von PO %O, TI %O, TP %O\n",
+                        dtime(time()), ob, previous_object(),
+                        this_interactive(), this_player() ) );
+
+    efun::remove_interactive(ob);
+}
+
+
+void ___updmaster()
+{
+    object ob;
+
+    //sollte nicht jeder duerfen.
+    if (process_call() || !ARCH_SECURITY)
+      raise_error("Illegal use of ___updmaster()!");
+
+    write("Removing old master ... ");
+    foreach(string file: 
+        get_dir("/secure/master/*.c",GETDIR_NAMES|GETDIR_UNSORTED|GETDIR_PATH)) {
+      if (ob = find_object(file))
+       efun::destruct(ob);
+    }
+    efun::destruct(efun::find_object("/secure/master"));
+    write("done.\nLoading again ... ");
+    load_object("/secure/master");
+
+    write("done.\n");
+}
+
+varargs string country(mixed ip, string num) {
+  mixed ret;
+
+  if(ret = (string)"/p/daemon/iplookup"->country(num || ip)) {
+    return ret;
+  } else return "???";
+}
+
+
+// * Snoopen und was dazugehoert
+static object find_snooped(object who)
+{
+  object *u;
+  int i;
+
+  for (i=sizeof(u=users())-1;i>=0;i--)
+    if (who==efun::interactive_info(u[i], II_SNOOP_NEXT))
+      return u[i];
+  return 0;
+}
+
+private string Lcut(string str) {
+  return str[5..11]+str[18..];
+}
+
+nomask varargs int snoop( object me, object you )
+{
+    int ret;
+    object snooper0, snooper, snooper2, snooper3;
+
+    if( !objectp(me) || me == you || !PO )
+       return 0;
+
+    snooper0 = find_snooped(me);
+
+     if(you){
+        if ( PO != me && query_wiz_grp(me) >= query_wiz_grp(geteuid(PO)) )
+            return 0;
+
+        if ( query_wiz_grp(me) <= query_wiz_grp(you) &&
+             !(you->QueryAllowSnoop(me)) )
+            if ( !IS_DEPUTY(me) || IS_ARCH(you) )
+               return 0;
+
+        if ( (snooper = efun::interactive_info(you, II_SNOOP_NEXT)) &&
+             query_wiz_grp(snooper) >= query_wiz_grp(me) ){
+            if ( (int)snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
+               return 0;
+
+            tell_object( snooper, sprintf( "%s snooped jetzt %s.\n",
+                                       me->name(WER), you->name(WER) ) );
+
+            snooper2 = me;
+
+            while ( snooper3 = interactive_info(snooper2, II_SNOOP_NEXT) ){
+               tell_object( snooper,
+                           sprintf( "%s wird seinerseits von %s gesnooped.\n"
+                                   ,snooper2->name(WER),
+                                   snooper3->name(WEM) ) );
+               snooper2 = snooper3;
+            }
+
+            efun::snoop( snooper, snooper2 );
+
+            if ( efun::interactive_info(snooper2, II_SNOOP_NEXT) != snooper )
+               tell_object( snooper, sprintf( "Du kannst %s nicht snoopen.\n",
+                                          snooper2->name(WEN) ) );
+            else{
+               tell_object( snooper, sprintf( "Du snoopst jetzt %s.\n",
+                                          snooper2->name(WEN) ) );
+               if ( !IS_DEPUTY(snooper) ){
+                   log_file( SNOOPLOGFILE, sprintf("%s: %O %O %O\n",
+                                               dtime(time()),
+                                               snooper,
+                                               snooper2,
+                                               environment(snooper2) ),
+                            100000 );
+                   if (snooper0)
+                      CHMASTER->send( "Snoop", snooper,
+                                    sprintf( "%s *OFF* %s (%O)",
+                                            capitalize(getuid(snooper)),
+                                            capitalize(getuid(snooper0)),
+                                            environment(snooper0) ) );
+
+                   CHMASTER->send( "Snoop", snooper,
+                                 sprintf("%s -> %s (%O)",
+                                        capitalize(getuid(snooper)),
+                                        capitalize(getuid(snooper2)),
+                                        environment(snooper2)));
+               }
+               else{
+                   log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                                 dtime(time()),
+                                                 snooper,
+                                                 snooper2,
+                                                 environment(snooper2) )
+                            ,100000 );
+               }
+            }
+        }
+        else
+            if (snooper)
+               if ( !me->QueryProp(P_SNOOPFLAGS) & SF_LOCKED ){
+                   printf( "%s wird bereits von %s gesnooped. Benutze das "
+                          "\"f\"-Flag, wenn du dennoch snoopen willst.\n",
+                          you->name(WER), snooper->name(WEM) );
+                   return 0;
+               }
+
+        ret = efun::snoop( me, you );
+
+        if ( !IS_DEPUTY(me) && efun::interactive_info(you, II_SNOOP_NEXT) == me){
+            log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                         Lcut(dtime(time())),
+                                         me, you, environment(you) ),
+                     100000 );
+
+            if (snooper0)
+               CHMASTER->send( "Snoop", me,
+                             sprintf( "%s *OFF* %s (%O).",
+                                     capitalize(getuid(me)),
+                                     capitalize(getuid(snooper0)),
+                                     environment(snooper0) ) );
+
+            CHMASTER->send( "Snoop", me, sprintf( "%s -> %s (%O).",
+                                             capitalize(getuid(me)),
+                                             capitalize(getuid(you)),
+                                             environment(you) ) );
+        }
+        else{
+            if ( efun::interactive_info(you, II_SNOOP_NEXT) == me ){
+               log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                             Lcut(dtime(time())),
+                                             me, you, environment(you) ),
+                        100000 );
+            }
+        }
+
+        if ( ret && query_wiz_grp(me) <= query_wiz_grp(you) &&
+             !IS_DEPUTY(me) )
+            tell_object( you, "*** " + NAME(me) + " snoopt Dich!\n" );
+
+        return ret;
+     }
+     else {
+        if ( (me == PO ||
+              query_wiz_grp(geteuid(PO)) > query_wiz_grp(me) ||
+              (query_wiz_grp(geteuid(PO)) == query_wiz_grp(me) &&
+              efun::interactive_info(PO, II_SNOOP_NEXT) == me)) && snooper0 ){
+            if ( !IS_DEPUTY(me) ){
+               log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
+                                            Lcut(dtime(time())), me,
+                                            snooper0,
+                                            environment(snooper0) ),
+                        100000 );
+
+                CHMASTER->send( "Snoop", me,
+                              sprintf( "%s *OFF* %s (%O).",
+                                      capitalize(getuid(me)),
+                                      capitalize(getuid(snooper0)),
+                                      environment(snooper0) ) );
+            }
+            else{
+               log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
+                                             Lcut(dtime(time())), me,
+                                             snooper0,
+                                             environment(snooper0) ),
+                        100000 );
+            }
+
+            return efun::snoop(me);
+        }
+     }
+     return 0;
+}
+
+
+
+// * Emulation des 'alten' explode durch das neue
+string *old_explode(string str, string del) {
+  int s, t;
+  string *strs;
+
+  if (!stringp(str)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Invalid argument 1 to old_explode()! Expected <string>, got: "
+         "%.30O\n",str));
+  }
+  if (!stringp(del)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Invalid argument 2 to old_explode()! Expected <string>, got: "
+         "%.30O\n",del));
+  }
+  if(del == "")
+    return ({str});
+  strs=efun::explode(str, del);
+  t=sizeof(strs)-1;
+  while(s<=t && strs[s++] == "");s--;
+  while(t>=0 && strs[t--] == "");t++;
+  if(s<=t)
+    return strs[s..t];
+  return ({});
+}
+
+int file_time(string path) {
+  mixed *v;
+
+  set_this_object(previous_object());
+  if (sizeof(v=get_dir(path,GETDIR_DATES))) return v[0];
+  return(0); //sonst
+}
+
+// * Bei 50k Groesse Log-File rotieren
+varargs int log_file(string file, string txt, int size_to_break) {
+  mixed *st;
+
+  file="/log/"+file;
+  file=implode((efun::explode(file,"/")-({".."})),"/");
+//  tell_object(find_player("jof"),sprintf("LOG FILE: %O -> %O\n",previous_object(),file));
+  if (!funcall(bind_lambda(#'efun::call_other,PO),"secure/master",//')
+              "valid_write",file,geteuid(PO),"log_file",PO))
+      return 0;
+  if ( size_to_break >= 0 & (
+      sizeof(st = get_dir(file,2) ) && st[0] >= (size_to_break|MAX_LOG_SIZE)))
+      catch(rename(file, file + ".old");publish); /* No panic if failure */
+  return(write_file(file,txt));
+}
+
+// * Magier-Level abfragen
+int query_wiz_level(mixed player) {
+  return (int)"/secure/master"->query_wiz_level(player);
+}
+
+#ifdef __ALISTS__
+// * Element aus Alist loeschen (by key)
+mixed *remove_alist(mixed key,mixed *alist)
+{
+  int i,j;
+
+  if (!pointerp(alist) || !sizeof(alist))
+    return alist;
+  if (!pointerp(alist[0]))
+  {
+    if ((i=assoc(key,alist))<0)
+      return alist;
+    return alist[0..i-1]+alist[i+1..];
+  }
+  i = assoc(key,alist[0]);
+  if ((i=assoc(key,alist[0]))<0)
+    return alist;
+  alist=alist[0..];
+  for (j=sizeof(alist)-1;j>=0;j--)
+    alist[j]=alist[j][0..i-1]+alist[j][i+1..];
+  return alist;
+}
+
+// * Element aus Alist loeschen (by pos)
+mixed *exclude_alist(int i,mixed *alist)
+{
+  int j;
+  if (!pointerp(alist) || !sizeof(alist) || i<0)
+    return alist;
+  if (!pointerp(alist[0]))
+    return alist[0..i-1]+alist[i+1..];
+  alist=alist[0..]; /* Create PHYSICAL copy of alist */
+  for (j=sizeof(alist)-1;j>=0;j--)
+    alist[j]=alist[j][0..i-1]+alist[j][i+1..];
+  return alist; /* order_alist is NOT necessary - see /doc/LPC/alist */
+}
+#endif // __ALISTS__
+
+// * German version of ctime()
+#define TAGE ({"Son","Mon","Die","Mit","Don","Fre","Sam"})
+#define MONATE ({"Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug",\
+                 "Sep","Okt","Nov","Dez"})
+string dtime(int wann) {
+  
+  if (wann == dtime_cache[0])
+    return(dtime_cache[1]);
+
+  int *lt = localtime(wann);
+  return sprintf("%s, %2d. %s %d, %02d:%02d:%02d",
+      TAGE[lt[TM_WDAY]], lt[TM_MDAY], MONATE[lt[TM_MON]], 
+      lt[TM_YEAR],lt[TM_HOUR], lt[TM_MIN], lt[TM_SEC]);
+}
+
+// wenn strftime im Driver nicht existiert, ist dies hier ein Alias auf dtime(),
+// zwar stimmt das Format dann nicht, aber die Mudlib buggt nicht und schreibt
+// ein ordentliches Datum/Uhrzeit.
+#if !__EFUN_DEFINED__(strftime)
+varargs string strftime(mixed fmt, int clock, int localized) {
+  if (intp(clock) && clock >= 0)
+    return dtime(clock);
+  else if (intp(fmt) && fmt >= 0)
+    return dtime(fmt);
+  
+  return dtime(time());
+}
+#endif //!__EFUN_DEFINED__(strftime)
+
+// * Shutdown mit zusaetzlichem logging
+nomask int shutdown(string reason)
+{
+  string name;
+  string obname;
+  string output;
+
+  if (!reason)
+    return 0;
+  if ( !ARCH_SECURITY && getuid(previous_object())!=ROOTID &&
+          object_name(previous_object())!="/obj/shut" )
+  {
+    write("You have no permission to shut down the gamedriver!\n");
+    return 0;
+  }
+  if ((this_interactive())&&(name=getuid(this_interactive())))
+  {
+    name=capitalize(name);
+    filter(users(),#'tell_object,//'
+               capitalize(name)+" faehrt das Spiel herunter!\n");
+  }
+  else
+    name="ANONYMOUS";
+  if (previous_object()) obname=capitalize(getuid(previous_object()));
+  output=name;
+  if (obname && name!=obname) output=output+" ("+obname+")";
+  if (previous_object()&&object_name(previous_object())=="/obj/shut"){
+    output+=" faehrt das Spiel via Armageddon herunter.\n";
+    output=dtime(time())+": "+output;
+    log_file("GAME_LOG",output+"\n",-1);
+    efun::shutdown();
+    return 1;
+  }
+  output=ctime(time())+": "+output+" faehrt das Spiel herunter.\n";
+  output+="    Grund: "+reason;
+  log_file("GAME_LOG",output+"\n",-1);
+  efun::shutdown();
+  return 1;
+}
+
+// * lowerchar
+
+int lowerchar(int char) {
+  if (char<'A' || char>'Z') return char;
+  return char+32;
+}
+
+// * upperstring
+
+string upperstring(string s)
+{
+#if __EFUN_DEFINED__(upper_case)
+  return(upper_case(s));
+#else
+  int i;
+  if (!stringp(s)) return 0;
+  for (i=sizeof(s)-1;i>=0;i--) s[i]=((s[i]<'a'||s[i]>'z')?s[i]:s[i]-32);
+  return s;
+#endif
+}
+
+// * lowerstring
+
+string lowerstring(string s)
+{
+  if (!stringp(s)) return 0;
+  return lower_case(s);
+}
+
+
+// * GD version
+string version()
+{
+  return __VERSION__;
+}
+
+// * break_string
+// stretch() -- stretch a line to fill a given width 
+private string stretch(string s, int width) {
+  int len=sizeof(s);
+  if (len==width) return s;
+
+  // reine Leerzeilen, direkt zurueckgeben
+  string trimmed=trim(s,TRIM_LEFT," ");
+  if (trimmed=="") return s; 
+  int start_spaces = len - sizeof(trimmed);
+
+  string* words = explode(trimmed, " ");
+  // der letzte kriegt keine Spaces
+  int word_count=sizeof(words) - 1;
+  // wenn Zeile nur aus einem Wort, wird das Wort zurueckgegeben
+  if (!word_count)
+    return " "*start_spaces + words[0];
+
+  int space_count = width - len;
+
+  int space_per_word=(word_count+space_count) / word_count;
+  // Anz.Woerter mit Zusatz-Space
+  int rest=(word_count+space_count) % word_count; 
+  // Rest-Spaces Verteilen
+  foreach (int pos : rest) words[pos]+=" ";
+  return (" "*start_spaces) + implode( words, " "*space_per_word );
+}
+
+// aus Geschwindigkeitsgruenden hat der Blocksatz fuer break_string eine
+// eigene Funktion bekommen:
+private varargs string block_string(string s, int width, int flags) {
+  // wenn BS_LEAVE_MY_LFS, aber kein BS_NO_PARINDENT, dann werden Zeilen mit
+  // einem Leerzeichen begonnen.
+  // BTW: Wenn !BS_LEAVE_MY_LFS, hat der Aufrufer bereits alle \n durch " "
+  // ersetzt.
+  if ( (flags & BS_LEAVE_MY_LFS)
+      && !(flags & BS_NO_PARINDENT))
+  {
+      s = " "+regreplace(s,"\n","\n ",1);
+  }
+
+  // sprintf fuellt die letzte Zeile auf die Feldbreite (hier also
+  // Zeilenbreite) mit Fuellzeichen auf, wenn sie NICHT mit \n umgebrochen
+  // ist. Es wird an die letzte Zeile aber kein Zeilenumbruch angehaengt.
+  // Eigentlich ist das Auffuellen doof, aber vermutlich ist es unnoetig, es
+  // wieder rueckgaengig zu machen.
+  s = sprintf( "%-*=s", width, s);
+
+  string *tmp=explode(s, "\n");
+  // Nur wenn s mehrzeilig ist, Blocksatz draus machen. Die letzte Zeile wird
+  // natuerlich nicht gedehnt. Sie ist dafuer schon von sprintf() aufgefuellt
+  // worden. BTW: Die letzte Zeile endet u.U. noch nicht mit einem \n (bzw.
+  // nur dann, wenn BS_LEAVE_MY_LFS und der uebergebene Text schon nen \n am
+  // Ende der letzten Zeile hat), das macht der Aufrufer...
+  if (sizeof(tmp) > 1)
+    return implode( map( tmp[0..<2], #'stretch/*'*/, width ), "\n" ) 
+      + "\n" + tmp[<1];
+
+  return s;
+}
+
+public varargs string break_string(string s, int w, mixed indent, int flags)
+{
+    if ( !s || s == "" ) return "";
+
+    if ( !w ) w=78;
+
+    if( intp(indent) )
+       indent = indent ? " "*indent : "";
+
+    int indentlen=stringp(indent) ? sizeof(indent) : 0;
+
+    if (indentlen>w) {
+      set_this_object(previous_object());
+      raise_error(sprintf("break_string: indent longer %d than width %d\n",
+                  indentlen,w));
+      // w=((indentlen/w)+1)*w;
+    }
+
+    if (!(flags & BS_LEAVE_MY_LFS)) 
+      s=regreplace( s, "\n", " ", 1 );
+
+    if ( flags & BS_SINGLE_SPACE )
+       s = regreplace( s, "(^|\n| )  *", "\\1", 1 );
+ 
+    string prefix="";
+    if (indentlen && flags & BS_PREPEND_INDENT) {
+      if (indentlen+sizeof(s) > w || 
+         (flags & BS_LEAVE_MY_LFS) && strstr(s,"\n")>-1) {
+       prefix=indent+"\n";
+       indent=(flags & BS_NO_PARINDENT) ? "" : " ";
+       indentlen=sizeof(indent);
+      }
+    }
+
+    if ( flags & BS_BLOCK ) {
+      /*
+           s = implode( map( explode( s, "\n" ),
+                               #'block_string, w, indentlen, flags),
+                      "" );
+      */
+      s = block_string( s , w - indentlen, flags );
+    }
+    else {
+      s = sprintf("%-1.*=s",w-indentlen,s);
+    }
+    if ( s[<1] != '\n' ) s += "\n";
+
+    if ( !indentlen ) return prefix + s;
+    
+    string indent2 = ( flags & BS_INDENT_ONCE ) ? (" "*indentlen) :indent;
+      
+    return prefix + indent + 
+      regreplace( s[0..<2], "\n", "\n"+indent2, 1 ) + "\n";
+      /*
+       string *buf;
+
+       buf = explode( s, "\n" );
+       return prefix + indent + implode( buf[0..<2], "\n"+indent2 ) + buf[<1] + "\n";
+      */
+}
+
+// * Elemente aus mapping loeschen - mapping vorher kopieren
+
+mapping m_copy_delete(mapping m, mixed key) {
+  return m_delete(copy(m), key);
+}
+
+// * times
+int last_reboot_time()
+{
+  return __BOOT_TIME__;
+}
+
+int first_boot_time()
+{
+  return 701517600;
+}
+
+int exist_days()
+{
+  return (((time()-first_boot_time())/8640)+5)/10;
+}
+
+// * uptime :)
+string uptime()
+{
+  int t;
+  int tmp;
+  string s;
+
+  t=time()-__BOOT_TIME__;
+  s="";
+  if (t>=86400)
+    s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
+  if (t>=3600)
+    s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
+  if (t>60)
+    s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
+  return s+sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
+}
+
+// * Was tun bei 'dangling' lfun-closures ?
+void dangling_lfun_closure() {
+  raise_error("dangling lfun closure\n");
+}
+
+// * Sperren ausser fuer master/simul_efun
+
+#if __EFUN_DEFINED__(set_environment)
+nomask void set_environment(object o1, object o2) {
+  raise_error("Available only for root\n");
+}
+#endif
+
+nomask void set_this_player(object pl) {
+  raise_error("Available only for root\n");
+}
+
+#if __EFUN_DEFINED__(export_uid)
+nomask void export_uid(object ob) {
+  raise_error("Available only for root\n");
+}
+#endif
+
+// * Jetzt auch closures
+int process_flag;
+
+public nomask int process_call()
+{
+  if (process_flag>0)
+    return process_flag;
+  else return(0);
+}
+
+private nomask string _process_string(string str,object po) {
+              set_this_object(po);
+              return(efun::process_string(str));
+}
+
+nomask string process_string( mixed str )
+{
+  string tmp, err;
+  int flag; 
+
+  if ( closurep(str) ) {
+      set_this_object( previous_object() );
+      return funcall(str);
+  }
+  else if (str==0)
+      return((string)str);
+  else if ( !stringp(str) ) {
+      return to_string(str);
+  }
+
+  if ( !(flag = process_flag > time() - 60))                     
+      process_flag=time();
+
+  err = catch(tmp = funcall(#'_process_string,str,previous_object()); publish);
+
+  if ( !flag )
+    process_flag=0;
+
+  if (err) {
+    // Verarbeitung abbrechen
+    set_this_object(previous_object());
+    raise_error(err);
+  }
+  return tmp;
+}
+
+// 'mkdir -p' - erzeugt eine komplette Hierarchie von Verzeichnissen.
+// wenn das Verzeichnis angelegt wurde oder schon existiert, wird 1
+// zurueckgeliefert, sonst 0.
+// Wirft einen Fehler, wenn das angebene Verzeichnis nicht absolut ist!
+public int mkdirp(string dir) {
+  // wenn es nur keinen fuehrenden / gibt, ist das ein Fehler.
+  if (strstr(dir, "/") != 0)
+    raise_error("mkdirp(): Pfad ist nicht absolute.\n");
+  // cut off trailing /...
+  if (dir[<1]=='/')
+      dir = dir[0..<2];
+
+  int fstat = file_size(dir);
+  // wenn es schon existiert, tun wir einfach so, als haetten wir es angelegt.
+  if (fstat == FSIZE_DIR)
+    return 1;
+  // wenn schon ne Datei existiert, geht es nicht.
+  if (fstat != FSIZE_NOFILE)
+    return 0;
+  // wenn es nur einen / gibt (den fuehrenden), dann ist es ein
+  // toplevel-verzeichnis, was direkt angelegt wird.
+  if (strrstr(dir,"/")==0) {
+    return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
+  }
+
+  // mkdir() nicht direkt rufen, sondern vorher als closure ans aufrufende
+  // Objekt binden. Sonst laeuft die Rechtepruefung in valid_write() im Master
+  // unter der Annahme, dass die simul_efun.c mit ihrer root id was will.
+
+  // jetzt rekursiv die Verzeichnishierarchie anlegen. Wenn das erfolgreich
+  // ist, dann koennen wir jetzt mit mkdir das tiefste Verzeichnis anlegen
+  if (mkdirp(dir[0..strrstr(dir,"/")-1]) == 1)
+    return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
+}
+
+
+// * Properties ggfs. mitspeichern
+mixed save_object(mixed name)
+{
+  mapping properties;
+  mapping save;
+  mixed index, res;
+  int i;
+
+  // nur Strings und 0 zulassen
+  if ((!stringp(name) || !sizeof(name)) && 
+      (!intp(name) || name!=0)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Only non-empty strings and 0 may be used as filename in "
+         "sefun::save_object()! Argument was %O\n",name));
+  }
+
+  save = m_allocate(0, 2);
+  properties = (mapping)previous_object()->QueryProperties();
+
+  if(mappingp(properties))
+  {
+    // delete all entries in mapping properties without SAVE flag!
+    index = m_indices(properties);
+    for(i = sizeof(index)-1; i>=0;i--)
+    {
+      if(properties[index[i], F_MODE] & SAVE)
+      {
+       save[index[i]] = properties[index[i]];
+       save[index[i], F_MODE] =
+       properties[index[i], F_MODE] &
+                    (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED));
+      }
+    }
+  }
+  else save = ([]);
+
+  // save object!
+  previous_object()->_set_save_data(save);
+  // format: wie definiert in config.h
+  if (stringp(name))
+    res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
+       __LIB__SAVE_FORMAT_VERSION__);
+  else
+    res = funcall(bind_lambda(#'efun::save_object, previous_object()),
+       __LIB__SAVE_FORMAT_VERSION__);
+  previous_object()->_set_save_data(0);
+
+#ifdef IOSTATS
+  // Stats...
+  struct iostat_s stat = (<iostat_s>);
+  stat->oname = object_name(previous_object());
+  stat->time = time();
+  //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
+  //    OIM_TOTAL_DATA_SIZE);
+  if (stringp(name))
+      stat->size = file_size(name + ".o");
+  else
+      stat->sizeof(res);
+  //debug_message("saveo: "+saveo_stat[0]+"\n");
+  saveo_stat[1][saveo_stat[0]] = stat;
+  saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
+  //debug_message("saveo 2: "+saveo_stat[0]+"\n");
+#endif
+
+  return res;
+}
+
+// * Auch Properties laden
+int restore_object(string name)
+{
+  int result;
+  mixed index;
+  mixed save;
+  mapping properties;
+  int i;
+  closure cl;
+
+  // get actual property settings (by create())
+  properties = (mapping)previous_object()->QueryProperties();
+
+//  DEBUG(sprintf("RESTORE %O\n",name));
+  // restore object
+  result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
+  //'))
+  //_get_save_data liefert tatsaechlich mixed zurueck, wenn das auch immer ein 
+  //mapping sein sollte.
+  save = (mixed)previous_object()->_get_save_data();
+  if(mappingp(save))
+  {
+    index = m_indices(save);
+    for(i = sizeof(index)-1; i>=0; i--)
+    {
+      properties[index[i]] = save[index[i]];
+      properties[index[i], F_MODE] = save[index[i], F_MODE]
+                            &~(SETCACHED|QUERYCACHED);
+    }
+  }
+  else properties = ([]);
+
+  // restore properties
+  funcall(
+          bind_lambda(
+                     unbound_lambda(({'arg}), //'})
+                                  ({#'call_other,({#'this_object}),
+                                  "SetProperties",'arg})),//')
+                     previous_object()),properties);
+  previous_object()->_set_save_data(0);
+
+#ifdef IOSTATS
+  // Stats...
+  //debug_message("restoreo: "+restoreo_stat[0]+"\n");
+  struct iostat_s stat = (<iostat_s>);
+  stat->oname = object_name(previous_object());
+  stat->time = time();
+  //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
+  //    OIM_TOTAL_DATA_SIZE);
+  stat->size = file_size(name + ".o");
+  restoreo_stat[1][restoreo_stat[0]] = stat;
+
+  restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
+#endif
+
+  return result;
+}
+
+// * HB eines Objektes ein/ausschalten
+int set_object_heart_beat(object ob, int flag)
+{
+  if (objectp(ob))
+    return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
+}
+
+// * Magierlevelgruppen ermitteln
+int query_wiz_grp(mixed wiz)
+{
+  int lev;
+
+  lev=query_wiz_level(wiz);
+  if (lev<SEER_LVL) return 0;
+  if (lev>=GOD_LVL) return lev;
+  if (lev>=ARCH_LVL) return ARCH_GRP;
+  if (lev>=ELDER_LVL) return ELDER_GRP;
+  if (lev>=LORD_LVL) return LORD_GRP;
+  if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
+  if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
+  if (lev>=WIZARD_LVL) return WIZARD_GRP;
+  if (lev>=LEARNER_LVL) return LEARNER_GRP;
+  return SEER_GRP;
+}
+
+mixed *wizlist_info()
+{
+  if (ARCH_SECURITY || !extern_call())
+            return efun::wizlist_info();
+  return 0;
+}
+
+// * wizlist ausgeben
+varargs void wizlist(string name, int sortkey ) {
+
+  if (!name)
+  {
+    if (this_player())
+      name = getuid(this_player());
+    if (!name)
+      return;
+  }
+
+  // Schluessel darf nur in einem gueltigen Bereich sein
+  if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
+
+  mixed** wl = efun::wizlist_info();
+  // nach <sortkey> sortieren
+  wl = sort_array(wl, function int (mixed a, mixed b)
+      {return a[sortkey] < b[sortkey]; } );
+
+  // Summe ueber alle Kommandos ermitteln.
+  int total_cmd, i;
+  int pos=-1;
+  foreach(mixed entry : wl)
+  {
+    total_cmd += entry[WL_COMMANDS];
+    if (entry[WL_NAME] == name)
+      pos = i;
+    ++i;
+  }
+
+  if (pos < 0 && name != "ALL" && name != "TOP100")
+    return;
+
+  if (name == "TOP100")
+  {
+    if (sizeof(wl) > 100)
+      wl = wl[0..100];
+    else
+      wl = wl;
+  }
+  // um name herum schneiden
+  else if (name != "ALL")
+  {
+    if (sizeof(wl) <= 21)
+      wl = wl;
+    else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
+      wl = wl[pos-10..pos+10];
+    else if (pos <=21)
+      wl = wl[0..20];
+    else if (pos >= sizeof(wl) - 21)
+      wl = wl[<21..];
+    else
+      wl = wl;
+  }
+
+  write("\nWizard top score list\n\n");
+  if (total_cmd == 0)
+    total_cmd = 1;
+  printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
+         "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
+  foreach(mixed e: wl)
+  {
+    printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
+          e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
+          e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
+          e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
+          );
+  }
+  printf("\nTotal         %7d         (%d)\n\n", total_cmd, sizeof(wl));
+}
+
+
+// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
+
+// ermittelt das Objekt des Callouts.
+private object _call_out_obj( mixed call_out ) {
+    return pointerp(call_out) ? call_out[0] : 0;
+}
+
+private void _same_object( object ob, mapping m ) {
+  // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
+  if ( m[ob] )
+    m[ob] += ({ ob });
+  else
+    m[ob] = ({ ob }); 
+}
+
+// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
+// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
+// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
+private void _same_path( object key, object *obs, mapping m ) {
+  string path;
+  if (!objectp(key) || !pointerp(obs)) return;
+
+  path = load_name(key);
+
+  if ( m[path] )
+    m[path] += obs;
+  else
+    m[path] = obs;
+}
+
+// key kann object oder string sein.
+private int _too_many( mixed key, mapping m, int i ) {
+    return sizeof(m[key]) >= i;
+}
+
+// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
+// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
+// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
+private void _destroy( mixed key, object *obs, string text, int uid ) {
+    if (!pointerp(obs)) return;
+    // Array mit unique Eintraege erzeugen.
+    obs = m_indices( mkmapping(obs) );
+    // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
+    // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
+    // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
+    // laeuft.
+    catch( efun::raise_error(           
+         sprintf( text,                   
+           uid ? (string)master()->creator_file(key) : key,                   
+           sizeof(obs), object_name(obs[<1]) ) );publish);
+    // Und weg mit dem Kram...
+    filter( obs, #'efun::destruct/*'*/ );
+}
+
+// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
+// sind dabei nicht unique.
+private void _same_uid( string key, object *obs, mapping m, closure cf ) {
+  string uid;
+
+  if ( !pointerp(obs) || !sizeof(obs) )
+    return;
+
+  uid = funcall( cf, key );
+
+  if ( m[uid] )
+    m[uid] += obs; // obs ist nen Array
+  else
+    m[uid] = obs;
+}
+
+private int _log_call_out(mixed co)
+{
+  log_file("TOO_MANY_CALLOUTS",
+      sprintf("%s::%O (%d)\n",object_name(co[0]),co[1],co[2]),
+      200000);
+  return 0;
+}
+
+private int last_callout_log=0;
+
+nomask varargs void call_out( varargs mixed *args )
+{
+    mixed tmp, *call_outs;
+
+    // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
+    // haben.
+    if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
+        && geteuid(previous_object()) != ROOTID )
+    {
+       // Log erzeugen...
+       if (last_callout_log <
+           efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES) - 10
+           && get_eval_cost() > 200000)
+       {
+         last_callout_log = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+         log_file("TOO_MANY_CALLOUTS",
+             sprintf("\n%s: ############ Too many callouts: %d ##############\n",
+                     strftime("%y%m%d-%H%M%S"),
+                     efun::driver_info(DI_NUM_CALLOUTS)));
+         filter(efun::call_out_info(), #'_log_call_out);
+       }
+       // Objekte aller Callouts ermitteln
+       call_outs = map( efun::call_out_info(), #'_call_out_obj );
+       mapping objectmap = ([]);
+       filter( call_outs, #'_same_object, &objectmap );
+       // Master nicht grillen...
+       efun::m_delete( objectmap, master(1) );
+       // alle Objekte raussuchen, die zuviele haben...
+       mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
+       // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
+       // Objekten uebergeben (in diesem Fall sind Keys und Array mit
+       // Objekten jeweils das gleiche Objekt).
+       if ( sizeof(res) )       
+           walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "             
+              "object [%O]. Destructed %d objects. [%s]\n", 0 );
+
+       // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
+       // Objekte killen, die mehr als 50 Callouts laufen haben - und
+       // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
+       // Clones einer BP gekillt, die Callouts laufen haben, falls alle
+       // diese Objekte _zusammen_ mehr als 50 Callouts haben!
+       if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
+           // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
+           // mehr drin, da sie dort als Keys verwendet wurden.
+           mapping pathmap=([]);
+           // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
+           // von Objekten als Werte.
+           walk_mapping( objectmap, #'_same_path, &pathmap);
+           // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
+           res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
+           // und ueber alle Keys gehen, an _destroy() werden die Clones
+           // uebergeben, die Callouts haben.
+           if ( sizeof(res) )
+              walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
+                           "'%s'. Destructed %d objects. [%s]\n", 0 );
+
+           // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
+           // noch >1000 Callouts laufen, werden diesmal alle Callouts
+           // einer UID zusammengezaehlt.
+           // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
+           // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
+           if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
+           {
+              // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
+              // zusammensortieren. Zerstoerte Clones filter _same_uid()
+              // raus.
+              mapping uidmap=([]);
+              walk_mapping( pathmap, #'_same_uid, &uidmap,
+                           symbol_function( "creator_file",
+                                          "/secure/master" ) );
+              // In res nun UIDs als Keys und Arrays von Objekten als Werte.
+              // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
+              // 100 Callouts!) haben.
+              res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
+              // und erneut ueber die Keys laufen und jeweils die Arrays mit
+              // den Objekten zur Zerstoerung an _destroy()...
+              if ( sizeof(res) )
+                  walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
+                              "UID '%s'. Destructed %d objects. [%s]\n",
+                              1 );
+           }
+       }
+    }
+
+    // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
+    if ( !previous_object() )
+       return;
+
+    set_this_object( previous_object() );
+    apply( #'efun::call_out, args );
+    return;
+}
+
+mixed call_out_info() {
+  
+  object po = previous_object();
+  mixed coi = efun::call_out_info();
+
+  // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
+  // /obj haben die BackboneID.
+  if (query_wiz_level(getuid(po)) >= ARCH_LVL
+       || (string)master()->creator_file(load_name(po)) == BACKBONEID ) {
+      return coi;
+  }
+  else {
+      return filter(coi, function mixed (mixed arr) {
+              if (pointerp(arr) && arr[0]==po)
+                 return 1;
+              else return 0; });
+  }
+}
+
+// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
+// NICHT das Objekt, was ggf. die lfun definiert!
+mixed query_closure_object(closure c) {
+  return
+    CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
+      0 :
+  (to_object(c) || -1);
+}
+
+// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
+varargs void move_object(mixed what, mixed where)
+{
+  object po,tmp;
+
+  po=previous_object();
+  if (!where)
+  {
+    where=what;
+    what=po;
+  }
+  if (((stringp(where) && where==NETDEAD_ROOM ) ||
+       (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
+       objectp(what) && object_name(what)!="/obj/sperrer")
+  {
+    if (!query_once_interactive(what))
+    {
+      what->remove();
+      if (what) destruct(what);
+      return;
+    }
+    if (living(what) || interactive(what))
+    {
+      log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
+      return;
+    }
+    set_object_heart_beat(what,0);
+  }
+  tmp=what;
+  while (tmp=environment(tmp))
+      // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+      // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+      // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+      // da um jedes bisschen Rechenzeit geht.
+      // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+      //
+      // Tiamak
+    tmp->_set_last_content_change();
+  funcall(bind_lambda(#'efun::move_object,po),what,where);
+  if (tmp=what)
+    while (tmp=environment(tmp))
+      tmp->_set_last_content_change();
+}
+
+
+void start_simul_efun() {
+  mixed *info;
+
+  // Falls noch nicht getan, extra_wizinfo initialisieren
+  if ( !pointerp(info = get_extra_wizinfo(0)) )
+    set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
+
+  InitLivingData(info);
+
+  set_next_reset(10); // direkt mal aufraeumen
+}
+
+protected void reset() {
+  set_next_reset(7200);
+  CleanLivingData();
+}
+
+#if !__EFUN_DEFINED__(absolute_hb_count)
+int absolute_hb_count() {
+  return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+}
+#endif
+
+void __set_environment(object ob, mixed target)
+{
+  string path;
+  object obj;
+
+  if (!objectp(ob))
+    return;
+  if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
+    return;
+  if (objectp(target))
+  {
+    efun::set_environment(ob,target);
+    return;
+  }
+  path=(string)MASTER->_get_path(target,this_interactive());
+  if (stringp(path) && file_size(path+".c")>=0 &&
+      !catch(load_object(path);publish) )
+  {
+    obj=find_object(path);
+    efun::set_environment(ob,obj);
+    return;
+  }
+}
+
+void _dump_wizlist(string file, int sortby) {
+  int i;
+  mixed *a;
+
+  if (!LORD_SECURITY)
+    return;
+  if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
+  {
+    write("NO WRITE PERMISSION\n");
+    return;
+  }
+  a = wizlist_info();
+  a = sort_array(a, lambda( ({'a,'b}),
+                        ({#'<,
+                          ({#'[,'a,sortby}),
+                          ({#'[,'b,sortby})
+                         })));
+  rm(file);
+  for (i=sizeof(a)-1;i>=0;i--)
+    write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
+      a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
+      a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
+}
+
+public varargs object deep_present(mixed what, object ob) {
+
+  if(!objectp(ob))
+    ob=previous_object();
+  // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
+  // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
+  if(objectp(what)) {
+    object *envs=all_environment(what);
+    // wenn ob kein Environment hat, ist es offensichtlich nicht in what
+    // enthalten.
+    if (!pointerp(envs)) return 0;
+    if (member(envs, ob) != -1) return what;
+  }
+  // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
+  // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
+  // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
+  else if (stringp(what)) {
+      int cnt;
+      string newwhat;
+      if(sscanf(what,"%s %d",newwhat,cnt)!=2)
+       cnt=1;
+      else
+       what=newwhat;
+      foreach(object invob: deep_inventory(ob)) {
+       if (invob->id(what) && !--cnt)
+           return invob;
+      }
+  }
+  else {
+    set_this_object(previous_object());
+    raise_error(sprintf("Wrong argument 1 to deep_present(). "
+         "Expected \"object\" or \"string\", got %.50O.\n",
+         what));
+  }
+  return 0;
+}
+
+mapping dump_ip_mapping()
+{
+  return 0;
+}
+
+nomask void swap(object obj)
+{
+  write("Your are not allowed to swap objects by hand!\n");
+  return;
+}
+
+nomask varargs void garbage_collection(string str)
+{
+  if(previous_object()==0 || !IS_ARCH(geteuid(previous_object())) 
+      || !ARCH_SECURITY)
+  {
+    write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
+    return;
+  }
+  else if (stringp(str))
+  {
+    return efun::garbage_collection(str);
+  }
+  else 
+    return efun::garbage_collection();
+}
+
+varargs void notify_fail(mixed nf, int prio) {
+  object po,oldo;
+  int oldprio;
+  
+  if (!PL || !objectp(po=previous_object())) return;
+  if (!stringp(nf) && !closurep(nf)) {
+      set_this_object(po);
+      raise_error(sprintf(
+         "Only strings and closures allowed for notify_fail! "
+         "Argument was: %.50O...\n",nf));
+  }
+
+  // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
+  // und vergleichen.
+  if (objectp(oldo=query_notify_fail(1)) && po!=oldo) {
+    if (!prio) {       
+      //Prioritaet dieses notify_fail() 'abschaetzen'
+      if (po==PL) // Spieler-interne (soul-cmds)
+        prio=NF_NL_OWN;
+      else if (living(po))
+        prio=NF_NL_LIVING;
+      else if ((int)po->IsRoom())
+        prio=NF_NL_ROOM;
+      else
+        prio=NF_NL_THING;
+    }
+    //Prioritaet des alten Setzers abschaetzen
+    if (oldo==PL)
+      oldprio=NF_NL_OWN;
+    else if (living(oldo))
+      oldprio=NF_NL_LIVING;
+    else if ((int)oldo->IsRoom())
+      oldprio=NF_NL_ROOM;
+    else
+      oldprio=NF_NL_THING;
+  }
+  else // wenn es noch kein Notify_fail gibt:
+    oldprio=NF_NL_NONE;
+
+  //vergleichen und ggf. setzen
+  if (prio >= oldprio) { 
+    set_this_object(po);
+    efun::notify_fail(nf);
+  }
+
+  return;
+}
+
+void _notify_fail(string str)
+{
+  //query_notify_fail() benutzen, um das Objekt
+  //des letzten notify_fail() zu ermitteln
+  object o;
+  if ((o=query_notify_fail(1)) && o!=previous_object())
+    return;
+  //noch kein notify_fail() fuer dieses Kommando gesetzt, auf gehts.
+  set_this_object(previous_object());
+  efun::notify_fail(str);
+  return;
+}
+
+string time2string( string format, int zeit )
+{
+  int i,ch,maxunit,dummy;
+  string *parts, fmt;
+
+  int secs = zeit;
+  int mins = (zeit/60);
+  int hours = (zeit/3600);
+  int days = (zeit/86400);
+  int weeks =  (zeit/604800);
+  int months = (zeit/2419200);
+  int abbr = 0;
+
+  parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
+
+  for( i=1; i<sizeof(parts); i+=2 )
+  {
+    ch = parts[i][<1];
+    switch( parts[i][<1] )
+    {
+    case 'x': case 'X':
+       abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
+       // NO break !
+    case 'n': case 'N':
+       maxunit |= 31;
+       break;
+    case 'w': case 'W':
+       maxunit |= 15;
+       break;
+    case 'd': case 'D':
+       maxunit |= 7;
+       break;
+    case 'h': case 'H':
+       maxunit |= 3;
+       break;
+    case 'm': case 'M':
+       maxunit |= 1;
+       break;
+    }
+  }
+  if( maxunit & 16 ) weeks %= 4;
+  if( maxunit & 8 ) days %= 7;
+  if( maxunit & 4 ) hours %= 24;
+  if( maxunit & 2 ) mins %= 60;
+  if( maxunit ) secs %= 60;
+
+  for( i=1; i<sizeof(parts); i+=2 )
+  {
+    fmt = parts[i][0..<2];
+    ch = parts[i][<1];
+    if( ch=='x' )
+    {
+      if (months > 0) ch='n';
+      else if( weeks>0 ) ch='w';
+      else if( days>0 ) ch='d';
+      else if( hours>0 ) ch='h'; 
+      else if(mins > 0) ch = 'm';
+      else ch = 's';
+    }
+    else if( ch=='X' )
+    {
+      if (months > 0) ch='N';
+      else if( weeks>0 ) ch='W';
+      else if( days>0 ) ch='D';
+      else if( hours>0 ) ch='H'; 
+      else if(mins > 0) ch = 'M';
+      else ch = 'S';
+    }
+    
+    switch( ch )
+    {
+      case 'n': parts[i] = sprintf( fmt+"d", months ); break;
+      case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
+      case 'd': parts[i] = sprintf( fmt+"d", days ); break;
+      case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
+      case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
+      case 's': parts[i] = sprintf( fmt+"d", secs ); break;
+      case 'N':
+       if(abbr) parts[i] = "M";
+       else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
+       break;
+      case 'W':
+       if(abbr) parts[i] = "w"; else
+       parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
+       break;
+      case 'D':
+       if(abbr) parts[i] = "d"; else
+       parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
+       break;
+      case 'H':
+       if(abbr) parts[i] = "h"; else
+       parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
+       break;
+      case 'M':
+       if(abbr) parts[i] = "m"; else
+       parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
+       break;
+      case 'S':
+       if(abbr) parts[i] = "s"; else
+       parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
+       break;
+      case '%':
+       parts[i] = "%";
+       break;
+      }
+    }
+    return implode( parts, "" );
+}
+
+nomask mixed __create_player_dummy(string name)
+{
+  string err;
+  object ob;
+  mixed m;
+  //hat nen Scherzkeks die Blueprint bewegt?
+  if ((ob=find_object("/secure/login")) && environment(ob))
+      catch(destruct(ob);publish);
+  err = catch(ob = clone_object("secure/login");publish);
+  if (err)
+  {
+    write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
+    return 0;
+  }
+  if (objectp(m=(mixed)ob->new_logon(name))) netdead[name]=m;
+  return m;
+}
+
+nomask int secure_level()
+{
+  int *level;
+  //kette der Caller durchlaufen, den niedrigsten Level in der Kette
+  //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
+  //von 0.
+  //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
+  //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
+  //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
+  //INteractive geben muss.
+  level=map(caller_stack(1),function int (object caller)
+      {if (objectp(caller))
+       return(query_wiz_level(geteuid(caller)));
+       return(0); // kein Objekt da, 0.
+      } );
+  return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
+}
+
+nomask string secure_euid()
+{
+  string euid;
+
+  if (!this_interactive()) // Es muss einen interactive geben
+     return 0;
+  euid=geteuid(this_interactive());
+  // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
+  // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
+  // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
+  foreach(object caller: caller_stack()) {
+      if (!objectp(caller) ||
+       (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
+         return 0;
+  }
+  return euid; // 'sichere' euid zurueckgeben
+}
+
+// INPUT_PROMPT und nen Leerprompt hinzufuegen, wenn keins uebergeben wird.
+// Das soll dazu dienen, dass alle ggf. ein EOR am Promptende kriegen...
+//#if __BOOT_TIME__ < 1360017213
+varargs void input_to( mixed fun, int flags, varargs mixed *args )
+{
+    mixed *arr;
+    int i;
+
+    if ( !this_player() || !previous_object() )
+       return;
+
+    // TODO: input_to(...,INPUT_PROMPT, "", ...), wenn kein INPUT_PROMPT
+    // vorkommt...
+    if ( flags&INPUT_PROMPT ) {    
+        arr = ({ fun, flags }) + args;
+    }
+    else {
+        // ggf. ein INPUT_PROMPT hinzufuegen und nen Leerstring als Prompt.
+        flags |= INPUT_PROMPT;
+        arr = ({ fun, flags, "" }) + args;
+    }
+
+    // Arrays gegen flatten quoten.
+    for ( i = sizeof(arr) - 1; i > 1; i-- )
+       if ( pointerp(arr[i]) )
+           arr[i] = quote(arr[i]);
+
+    apply( bind_lambda( unbound_lambda( ({}),
+                                     ({ #'efun::input_to/*'*/ }) + arr ),
+                       previous_object() ) );
+}
+//#endif
+
+nomask int set_light(int i)
+// erhoeht das Lichtlevel eines Objekts um i
+// result: das Lichtlevel innerhalb des Objekts
+{
+    object ob, *inv;
+    int lall, light, dark, tmp;
+
+    if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
+
+    // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
+    if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
+
+    // Lichtberechnung findet eigentlich in der Mudlib statt.
+    return (int)ob->QueryProp(P_INT_LIGHT);
+}
+
+
+public string iso2ascii( string str )
+{
+    if ( !stringp(str) || !sizeof(str) )
+       return "";
+
+    str = regreplace( str, "ä", "ae", 1 );
+    str = regreplace( str, "ö", "oe", 1 );
+    str = regreplace( str, "ü", "ue", 1 );
+    str = regreplace( str, "Ä", "Ae", 1 );
+    str = regreplace( str, "Ö", "Oe", 1 );
+    str = regreplace( str, "Ü", "Ue", 1 );
+    str = regreplace( str, "ß", "ss", 1 );
+    str = regreplace( str, "[^ -~]", "?", 1 );
+
+    return str;
+}
+
+
+public varargs string CountUp( string *s, string sep, string lastsep )
+{
+    string ret;
+
+    if ( !pointerp(s) )
+       return "";
+    
+    if (!sep) sep = ", ";
+    if (!lastsep) lastsep = " und ";
+
+    switch (sizeof(s))  {
+       case 0: ret=""; break;
+       case 1: ret=s[0]; break;
+       default:
+              ret = implode(s[0..<2], sep);
+              ret += lastsep + s[<1];
+    }
+    return ret;
+}
+
+nomask varargs int query_next_reset(object ob) {
+
+    // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
+    if (ob && !objectp(ob))
+      raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
+           "expected object.\n",ob));
+
+    // Defaultobjekt PO, wenn 0 uebergeben.
+    if ( !objectp(ob) )
+      ob = previous_object();
+
+    return efun::object_info(ob, OI_NEXT_RESET_TIME);
+}
+
+
+#if !__EFUN_DEFINED__(copy_file)
+#define MAXLEN 50000
+nomask int copy_file(string source, string dest)
+{
+
+  int ptr;
+  string bytes;
+
+  set_this_object(previous_object());
+  if (!sizeof(source)||!sizeof(dest)||source==dest||(file_size(source)==-1)||
+      (!call_other(master(),"valid_read",source,
+                   getuid(this_interactive()||
+                 previous_object()),"read_file",previous_object()))||
+      (!call_other(master(),"valid_read",source,
+                   getuid(this_interactive()||
+                 previous_object()),"write_file",previous_object())))
+    return 1;
+  switch (file_size(dest))
+  {
+  case -1:
+    break;
+  case -2:
+    if (dest[<1]!='/') dest+="/";
+    dest+=efun::explode(source,"/")[<1];
+    if (file_size(dest)==-2) return 1;
+    if (file_size(dest)!=-1) break;
+  default:
+    if (!rm(dest)) return 1;
+    break;
+  }
+  do
+  {
+    bytes = read_bytes(source, ptr, MAXLEN); ptr += MAXLEN;
+    if (!bytes) bytes="";
+    write_file(dest, bytes);
+  }
+  while(sizeof(bytes) == MAXLEN);
+  return 0;
+}
+#endif //!__EFUN_DEFINED__(copy_file)
+
+
+// ### Ersatzaufloesung in Strings ###
+varargs string replace_personal(string str, mixed *obs, int caps) {
+  int i;
+  string *parts;
+
+  parts = regexplode(str, "@WE[A-SU]*[0-9]");
+  i = sizeof(parts);
+
+  if (i>1) {
+    int j, t;
+    closure *name_cls;
+
+    t = j = sizeof(obs);
+
+    name_cls  =  allocate(j);
+    while (j--)
+      if (objectp(obs[j]))
+        name_cls[j] = symbol_function("name", obs[j]);
+      else if (stringp(obs[j]))
+        name_cls[j] = obs[j];
+
+    while ((i-= 2)>0) {
+      int ob_nr;
+      // zu ersetzendes Token in Fall und Objektindex aufspalten
+      ob_nr = parts[i][<1]-'1';
+      if (ob_nr<0 || ob_nr>=t) {
+        set_this_object(previous_object());
+        raise_error(sprintf("replace_personal: using wrong object index %d\n",
+                    ob_nr));
+        return implode(parts, "");
+      }
+
+      // casus kann man schon hier entscheiden
+      int casus;
+      string part = parts[i];
+      switch (part[3]) {
+        case 'R': casus = WER;    break;
+        case 'S': casus = WESSEN; break;
+        case 'M': casus = WEM;    break;
+        case 'N': casus = WEN;    break;
+        default:  continue; // passt schon jetzt nicht in das Hauptmuster
+      }
+
+      // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
+      mixed tmp;
+      switch (part[3..<2]) {
+        case "R": case "SSEN": case "M": case "N":               // Name
+          parts[i] = funcall(name_cls[ob_nr], casus, 1);  break;
+        case "RU": case "SSENU": case "MU": case "NU":           // unbestimmt
+          parts[i] = funcall(name_cls[ob_nr], casus);     break;
+        case "RQP": case "SSENQP": case "MQP": case "NQP":       // Pronoun
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPronoun(casus);
+          break;
+        case "RQA": case "SSENQA": case "MQA": case "NQA":       // Article
+          if (objectp(tmp = obs[ob_nr]))
+            tmp = (string)tmp->QueryArticle(casus, 1, 1);
+          if (stringp(tmp) && !(tmp[<1]^' ')) 
+            tmp = tmp[0..<2];                // Extra-Space wieder loeschen
+          break;
+        case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, SINGULAR);
+          break;
+        case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
+          break;
+        case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
+          break;
+        case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, PLURAL);
+          break;
+        case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
+          break;
+        case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
+          break;
+        default:
+          continue;
+      }
+      
+      // wenn tmp ein String war, weisen wir es hier pauschal zu
+      if (stringp(tmp))
+        parts[i] = tmp;
+
+      // auf Wunsch wird nach Satzenden gross geschrieben
+      if (caps)
+        switch (parts[i-1][<2..]) {
+          case ". ":  case "! ":  case "? ":
+          case ".":   case "!":   case "?":
+          case ".\n": case "!\n": case "?\n":
+          case "\" ": case "\"\n":
+            parts[i] = capitalize(parts[i]);
+            break;
+        }
+    }
+    return implode(parts, "");
+  }
+  return str;
+}
+
+
+//replacements for dropped efuns in LD
+#if !__EFUN_DEFINED__(extract)
+deprecated varargs string extract(string str, int from, int to) {
+
+  if(!stringp(str)) {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to extract(): %O",str));
+  }
+  if (intp(from) && intp(to)) {
+    if (from>=0 && to>=0)
+      return(str[from .. to]);
+    else if (from>=0 && to<0)
+      return(str[from .. <abs(to)]);
+    else if (from<0 && to>=0)
+      return(str[<abs(from) .. to]);
+    else
+      return(str[<abs(from) .. <abs(to)]);
+  }
+  else if (intp(from)) {
+    if (from>=0)
+      return(str[from .. ]);
+    else
+      return(str[<abs(from) .. ]);
+  }
+  else {
+    return(str);
+  }
+}
+#endif // !__EFUN_DEFINED__(extract)
+
+#if !__EFUN_DEFINED__(slice_array)
+deprecated varargs mixed slice_array(mixed array, int from, int to) {
+
+  if(!pointerp(array)) {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
+  }
+  if (intp(from) && intp(to)) {
+    if (from>=0 && to>=0)
+      return(array[from .. to]);
+    else if (from>=0 && to<0)
+      return(array[from .. <abs(to)]);
+    else if (from<0 && to>=0)
+      return(array[<abs(from) .. to]);
+    else
+      return(array[<abs(from) .. <abs(to)]);
+  }
+  else if (intp(from)) {
+    if (from>=0)
+      return(array[from .. ]);
+    else
+      return(array[<abs(from) .. ]);
+  }
+  else {
+    return(array);
+  }
+}
+#endif // !__EFUN_DEFINED__(slice_array)
+
+#if !__EFUN_DEFINED__(member_array)
+deprecated int member_array(mixed item, mixed arraystring) {
+
+  if (pointerp(arraystring)) {
+    return(efun::member(arraystring,item));
+  }
+  else if (stringp(arraystring)) {
+    return(efun::member(arraystring,to_int(item)));
+  }
+  else {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
+  }
+}
+#endif // !__EFUN_DEFINED__(member_array)
+
+// The digit at the i'th position is the number of bits set in 'i'.
+string count_table =
+    "0112122312232334122323342334344512232334233434452334344534454556";
+int broken_count_bits( string s ) {
+    int i, res;
+    if( !stringp(s) || !(i=sizeof(s)) ) return 0;
+    for( ; i-->0; ) {
+        // We are counting 6 bits at a time using a precompiled table.
+        res += count_table[(s[i]-' ')&63]-'0';
+    }
+    return res;
+}
+
+#if !__EFUN_DEFINED__(count_bits)
+int count_bits( string s ) {
+    return(broken_count_bits(s));
+}
+#endif
+
+
+// * Teile aus einem Array entfernen *** OBSOLETE
+deprecated mixed *exclude_array(mixed *arr,int from,int to)
+{
+  if (to<from)
+    to = from;
+  return arr[0..from-1]+arr[to+1..];
+}
+
diff --git a/secure/simul_efun/spare/README b/secure/simul_efun/spare/README
new file mode 100644
index 0000000..303429a
--- /dev/null
+++ b/secure/simul_efun/spare/README
@@ -0,0 +1,20 @@
+simul_efun 
+---------- 
+Das simul_efun Objekt /secure/simul_efun/simul_efun benutzt die Dateien
+in /secure/simul_efun.
+Im Verzeichnis /secure/simul_efun/spare ist eine Sicherheitskopie von allen 
+Dateien in /secure/simul_efun.
+
+Falls /secure/simul_efun/simul_efun.c nicht ladbar ist, wird 
+/secure/simul_efun/spare/simul_efun.c als Ersatz versucht. Wenn auch das
+nicht ladbar ist, erfolgt ein Shutdown des Muds.
+
+Bei Aenderungen sollte _zuerst_ die normale simul efun editiert und
+anschliessend zerstoert/entladen werden, woraufhin sie implizit durch den
+Driver und Master neugeladen wird. Ist dies erfolgreich und die neue
+simul_efun laeuft (erst dann!) kopiert man die Dateien aus 
+/secure/simul_efun nach /secure/simul_efun/spare.
+
+Die Dateien hier sind mit Ausnahme der simul_efun.c selbst _nicht_ dazu 
+gedacht, geladen zu werden.
+
diff --git a/secure/simul_efun/spare/comm.c b/secure/simul_efun/spare/comm.c
new file mode 100644
index 0000000..595b852
--- /dev/null
+++ b/secure/simul_efun/spare/comm.c
@@ -0,0 +1,124 @@
+#include <living/comm.h>
+
+// Sendet an alle Objekte in room und room selber durch Aufruf von
+// ReceiveMsg().
+varargs void send_room(object|string room, string msg, int msg_type,
+                       string msg_action, string msg_prefix, object *exclude,
+                       object origin)
+{
+  if (stringp(room))
+    room=load_object(room);
+
+  origin ||= previous_object();
+  object *dest = exclude ? all_inventory(room) - exclude :
+                           all_inventory(room);
+
+  dest->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, origin);
+}
+
+static int _shout_filter( object ob, string pat )
+{
+    string *ignore;
+
+    if ( !environment(ob) )
+       return 0;
+
+    return sizeof( regexp( ({ object_name( environment(ob) ) }), pat ) );
+}
+
+varargs void shout( string s, mixed where ){
+    object *u;
+    string *pfade;
+
+    if ( !sizeof( u = users() - ({ this_player() }) ) )
+       return;
+
+    if ( !where )
+       pfade = ({ "/" });
+    else if ( intp(where) )
+       pfade =
+           ({ implode( efun::explode( object_name( environment(this_player()) ),
+                                   "/" )[0..2], "/" ) + "/" });
+    else if ( stringp(where) )
+       pfade = ({ where });
+    else
+       pfade = where;
+
+    u = filter( u, "_shout_filter", ME, implode( pfade, "|" ) );
+    u->ReceiveMsg(s, MT_COMM|MT_FAR|MSG_DONT_WRAP|MSG_DONT_STORE,
+                  MA_SHOUT_SEFUN, 0, previous_object());
+}
+
+
+#if __VERSION__ > "3.3.718"
+// This sefun replaces the deprecated efun cat().
+#define CAT_MAX_LINES 50
+varargs int cat(string file, int start, int num)
+{
+    if (extern_call())
+        set_this_object(previous_object());
+
+    int more;
+
+    if (num < 0 || !this_player())
+        return 0;
+
+    if (!start)
+        start = 1;
+
+    if (!num || num > CAT_MAX_LINES) {
+        num = CAT_MAX_LINES;
+        more = sizeof(read_file(file, start+num, 1));
+    }
+
+    string txt = read_file(file, start, num);
+    if (!txt)
+        return 0;
+
+    efun::tell_object(this_player(), txt);
+
+    if (more)
+        efun::tell_object(this_player(), "*****TRUNCATED****\n");
+
+    return sizeof(txt & "\n");
+}
+#undef CAT_MAX_LINES
+#endif // __VERSION__ > "3.3.718"
+
+#if __VERSION__ > "3.3.719"
+// This sefun replaces the deprecated efun tail().
+#define TAIL_MAX_BYTES 1000
+varargs int tail(string file)
+{
+    if (extern_call())
+        set_this_object(previous_object());
+
+    if (!stringp(file) || !this_player())
+        return 0;
+
+    string txt = read_bytes(file, -(TAIL_MAX_BYTES + 80), (TAIL_MAX_BYTES + 80));
+    // read_bytes() returns 0 if the start of the section given by
+    // parameter #2 lies beyond the beginning of the file.
+    // In this case we simply try and read the entire file.
+    if (!stringp(txt) && file_size(file) < TAIL_MAX_BYTES+80)
+      txt = read_file(file);
+    // Exit if still nothing could be read.
+    if (!stringp(txt))
+      return 0;
+
+    // cut off first (incomplete) line
+    int index = strstr(txt, "\n");
+    if (index > -1) {
+        if (index + 1 < sizeof(txt))
+            txt = txt[index+1..];
+        else
+            txt = "";
+    }
+
+    efun::tell_object(this_player(), txt);
+
+    return 1;
+}
+#undef TAIL_MAX_BYTES
+#endif // __VERSION__ > "3.3.719"
+
diff --git a/secure/simul_efun/spare/debug_info.c b/secure/simul_efun/spare/debug_info.c
new file mode 100644
index 0000000..d607c2f
--- /dev/null
+++ b/secure/simul_efun/spare/debug_info.c
@@ -0,0 +1,434 @@
+/* This sefun is to provide a replacement for the efun debug_info().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(debug_info)
+
+#include <driver_info.h>
+#include <debug_info.h>
+
+mixed debug_info(int what, varargs mixed* args)
+{
+    if (sizeof(args) > 2)
+        raise_error("Too many arguments to debug_info\n");
+
+    switch(what)
+    {
+        default:
+            raise_error(sprintf("Illegal value %d for debug_info().\n", what));
+
+        case DINFO_OBJECT:
+        {
+            object ob;
+
+            if (sizeof(args) != 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (!objectp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            ob = args[0];
+            printf("O_HEART_BEAT      : %s\n", efun::object_info(ob, OC_HEART_BEAT)       ? "TRUE" : "FALSE");
+            printf("O_ENABLE_COMMANDS : %s\n", efun::object_info(ob, OC_COMMANDS_ENABLED) ? "TRUE" : "FALSE");
+            printf("O_CLONE           : %s\n", efun::clonep(ob)                           ? "TRUE" : "FALSE");
+            printf("O_DESTRUCTED      : FALSE\n");
+            printf("O_SWAPPED         : %s\n", efun::object_info(ob, OI_SWAPPED)          ? "TRUE" : "FALSE");
+            printf("O_ONCE_INTERACTIVE: %s\n", efun::object_info(ob, OI_ONCE_INTERACTIVE) ? "TRUE" : "FALSE");
+            printf("O_RESET_STATE     : %s\n", efun::object_info(ob, OI_RESET_STATE)      ? "TRUE" : "FALSE");
+            printf("O_WILL_CLEAN_UP   : %s\n", efun::object_info(ob, OI_WILL_CLEAN_UP)    ? "TRUE" : "FALSE");
+            printf("O_REPLACED        : %s\n", efun::object_info(ob, OI_REPLACED)         ? "TRUE" : "FALSE");
+            printf("time_reset  : %d\n",       efun::object_info(ob, OI_NEXT_RESET_TIME));
+            printf("time_of_ref : %d\n",       efun::object_info(ob, OI_LAST_REF_TIME));
+            printf("ref         : %d\n",       efun::object_info(ob, OI_OBJECT_REFS));
+
+            int gticks = efun::object_info(ob, OI_GIGATICKS);
+            if(gticks)
+                printf("evalcost   :  %d%09d\n", gticks, efun::object_info(ob, OI_TICKS));
+            else
+                printf("evalcost   :  %d\n",   efun::object_info(ob, OI_TICKS));
+
+            printf("swap_num    : %d\n",       efun::object_info(ob, OI_SWAP_NUM));
+            printf("name        : '%s'\n",     efun::object_name(ob));
+            printf("load_name   : '%s'\n",     efun::load_name(ob));
+
+            object next_ob = efun::object_info(ob, OI_OBJECT_NEXT);
+            if (next_ob)
+                printf("next_all    : OBJ(%s)\n", efun::object_name(next_ob));
+
+            object prev_ob = efun::object_info(ob, OI_OBJECT_PREV);
+            if (prev_ob)
+                printf("Previous object in object list: OBJ(%s)\n", efun::object_name(prev_ob));
+            else
+                printf("This object is the head of the object list.\n");
+            break;
+        }
+
+        case DINFO_MEMORY:
+        {
+            object ob;
+
+            if (sizeof(args) != 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (!objectp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            ob = args[0];
+
+            printf("program ref's %3d\n",   efun::object_info(ob, OI_PROG_REFS));
+            printf("Name: '%s'\n",          efun::program_name(ob));
+            printf("program size    %6d\n", efun::object_info(ob, OI_PROG_SIZE));
+            printf("num func's:  %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_FUNCTIONS),
+                                            efun::object_info(ob, OI_SIZE_FUNCTIONS));
+            printf("num vars:    %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_VARIABLES),
+                                            efun::object_info(ob, OI_SIZE_VARIABLES));
+
+            printf("num strings: %3d (%4d) : overhead %d + data %d (%d)\n",
+                                            efun::object_info(ob, OI_NUM_STRINGS),
+                                            efun::object_info(ob, OI_SIZE_STRINGS) + efun::object_info(ob, OI_SIZE_STRINGS_DATA),
+                                            efun::object_info(ob, OI_SIZE_STRINGS),
+                                            efun::object_info(ob, OI_SIZE_STRINGS_DATA),
+                                            efun::object_info(ob, OI_SIZE_STRINGS_DATA_TOTAL));
+
+            printf("num inherits %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_INHERITED),
+                                            efun::object_info(ob, OI_SIZE_INHERITED));
+
+            printf("total size      %6d\n", efun::object_info(ob, OI_PROG_SIZE_TOTAL));
+
+            printf("data size       %6d (%6d\n",
+                                            efun::object_info(ob, OI_DATA_SIZE),
+                                            efun::object_info(ob, OI_DATA_SIZE_TOTAL));
+            break;
+        }
+
+        case DINFO_OBJLIST:
+        {
+            if (sizeof(args) == 0)
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (sizeof(args) == 1)
+            {
+                object * obs = efun::objects(args[0], 1);
+                return obs[0];
+            }
+            else
+                return efun::objects(args[0], args[1]);
+            break;
+        }
+
+        case DINFO_MALLOC:
+            write(debug_info(DINFO_STATUS, "malloc"));
+            break;
+
+        case DINFO_STATUS:
+        {
+            string which;
+            int opt;
+
+            if (sizeof(args) > 1)
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (!sizeof(args) || !args[0])
+                which = "";
+            else if(stringp(args[0]))
+                which = args[0];
+            else
+                raise_error("bag arg 2 to debug_info().\n");
+
+            switch(which)
+            {
+                case "":
+                    opt = DI_STATUS_TEXT_MEMORY;
+                    break;
+
+                case "tables":
+                    opt = DI_STATUS_TEXT_TABLES;
+                    break;
+
+                case "swap":
+                    opt = DI_STATUS_TEXT_SWAP;
+                    break;
+
+                case "malloc":
+                    opt = DI_STATUS_TEXT_MALLOC;
+                    break;
+
+                case "malloc extstats":
+                    opt = DI_STATUS_TEXT_MALLOC_EXTENDED;
+                    break;
+
+                default:
+                    return 0;
+            }
+
+            return efun::driver_info(opt);
+        }
+
+        case DINFO_DUMP:
+        {
+            int opt;
+
+            if (!sizeof(args))
+                raise_error("bad number of arguments to debug_info\n");
+
+            if(!stringp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            switch(args[0])
+            {
+                case "objects":
+                    opt = DDI_OBJECTS;
+                    break;
+
+                case "destructed":
+                    opt = DDI_OBJECTS_DESTRUCTED;
+                    break;
+
+                case "opcodes":
+                    opt = DDI_OPCODES;
+                    break;
+
+                case "memory":
+                    opt = DDI_MEMORY;
+                    break;
+
+                default:
+                    raise_error(sprintf("Bad argument '%s' to debug_info(DINFO_DUMP).\n", args[0]));
+                    return 0;
+            }
+
+            return efun::dump_driver_info(opt, args[1..1]...);
+        }
+
+        case DINFO_DATA:
+        {
+            mixed * result;
+
+            if (!sizeof(args))
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (!intp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            if (sizeof(args) == 2 && !intp(args[1]))
+                raise_error("bag arg 3 to debug_info().\n");
+
+            switch(args[0])
+            {
+                case DID_STATUS:
+                    result = allocate(DID_STATUS_MAX);
+
+                    result[DID_ST_ACTIONS]               = efun::driver_info(DI_NUM_ACTIONS);
+                    result[DID_ST_ACTIONS_SIZE]          = efun::driver_info(DI_SIZE_ACTIONS);
+                    result[DID_ST_SHADOWS]               = efun::driver_info(DI_NUM_SHADOWS);
+                    result[DID_ST_SHADOWS_SIZE]          = efun::driver_info(DI_SIZE_SHADOWS);
+
+                    result[DID_ST_OBJECTS]               = efun::driver_info(DI_NUM_OBJECTS);
+                    result[DID_ST_OBJECTS_SIZE]          = efun::driver_info(DI_SIZE_OBJECTS);
+                    result[DID_ST_OBJECTS_SWAPPED]       = efun::driver_info(DI_NUM_OBJECTS_SWAPPED);
+                    result[DID_ST_OBJECTS_SWAP_SIZE]     = efun::driver_info(DI_SIZE_OBJECTS_SWAPPED);
+                    result[DID_ST_OBJECTS_LIST]          = efun::driver_info(DI_NUM_OBJECTS_IN_LIST);
+                    result[DID_ST_OBJECTS_NEWLY_DEST]    = efun::driver_info(DI_NUM_OBJECTS_NEWLY_DESTRUCTED);
+                    result[DID_ST_OBJECTS_DESTRUCTED]    = efun::driver_info(DI_NUM_OBJECTS_DESTRUCTED);
+                    result[DID_ST_OBJECTS_PROCESSED]     = efun::driver_info(DI_NUM_OBJECTS_LAST_PROCESSED);
+                    result[DID_ST_OBJECTS_AVG_PROC]      = efun::driver_info(DI_LOAD_AVERAGE_PROCESSED_OBJECTS_RELATIVE);
+
+                    result[DID_ST_OTABLE]                = efun::driver_info(DI_NUM_OBJECTS_IN_TABLE);
+                    result[DID_ST_OTABLE_SLOTS]          = efun::driver_info(DI_NUM_OBJECT_TABLE_SLOTS);
+                    result[DID_ST_OTABLE_SIZE]           = efun::driver_info(DI_SIZE_OBJECT_TABLE);
+
+                    result[DID_ST_HBEAT_OBJS]            = efun::driver_info(DI_NUM_HEARTBEATS);
+                    result[DID_ST_HBEAT_CALLS]           = efun::driver_info(DI_NUM_HEARTBEAT_ACTIVE_CYCLES);
+                    result[DID_ST_HBEAT_CALLS_TOTAL]     = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+                    result[DID_ST_HBEAT_SLOTS]           = efun::driver_info(DI_NUM_HEARTBEATS);
+                    result[DID_ST_HBEAT_SIZE]            = efun::driver_info(DI_SIZE_HEARTBEATS);
+                    result[DID_ST_HBEAT_PROCESSED]       = efun::driver_info(DI_NUM_HEARTBEATS_LAST_PROCESSED);
+                    result[DID_ST_HBEAT_AVG_PROC]        = efun::driver_info(DI_LOAD_AVERAGE_PROCESSED_HEARTBEATS_RELATIVE);
+
+                    result[DID_ST_CALLOUTS]              = efun::driver_info(DI_NUM_CALLOUTS);
+                    result[DID_ST_CALLOUT_SIZE]          = efun::driver_info(DI_SIZE_CALLOUTS);
+
+                    result[DID_ST_ARRAYS]                = efun::driver_info(DI_NUM_ARRAYS);
+                    result[DID_ST_ARRAYS_SIZE]           = efun::driver_info(DI_SIZE_ARRAYS);
+
+                    result[DID_ST_MAPPINGS]              = efun::driver_info(DI_NUM_MAPPINGS);
+                    result[DID_ST_MAPPINGS_SIZE]         = efun::driver_info(DI_SIZE_MAPPINGS);
+                    result[DID_ST_HYBRID_MAPPINGS]       = efun::driver_info(DI_NUM_MAPPINGS_HYBRID);
+                    result[DID_ST_HASH_MAPPINGS]         = efun::driver_info(DI_NUM_MAPPINGS_HASH);
+
+                    result[DID_ST_STRUCTS]               = efun::driver_info(DI_NUM_STRUCTS);
+                    result[DID_ST_STRUCTS_SIZE]          = efun::driver_info(DI_SIZE_STRUCTS);
+                    result[DID_ST_STRUCT_TYPES]          = efun::driver_info(DI_NUM_STRUCT_TYPES);
+                    result[DID_ST_STRUCT_TYPES_SIZE]     = efun::driver_info(DI_SIZE_STRUCT_TYPES);
+
+                    result[DID_ST_PROGS]                 = efun::driver_info(DI_NUM_PROGS);
+                    result[DID_ST_PROGS_SIZE]            = efun::driver_info(DI_SIZE_PROGS);
+
+                    result[DID_ST_PROGS_SWAPPED]         = efun::driver_info(DI_NUM_PROGS_SWAPPED);
+                    result[DID_ST_PROGS_SWAP_SIZE]       = efun::driver_info(DI_SIZE_PROGS_SWAPPED);
+
+                    result[DID_ST_USER_RESERVE]          = efun::driver_info(DI_MEMORY_RESERVE_USER);
+                    result[DID_ST_MASTER_RESERVE]        = efun::driver_info(DI_MEMORY_RESERVE_MASTER);
+                    result[DID_ST_SYSTEM_RESERVE]        = efun::driver_info(DI_MEMORY_RESERVE_SYSTEM);
+
+                    result[DID_ST_ADD_MESSAGE]           = efun::driver_info(DI_NUM_MESSAGES_OUT);
+                    result[DID_ST_PACKETS]               = efun::driver_info(DI_NUM_PACKETS_OUT);
+                    result[DID_ST_PACKET_SIZE]           = efun::driver_info(DI_SIZE_PACKETS_OUT);
+                    result[DID_ST_PACKETS_IN]            = efun::driver_info(DI_NUM_PACKETS_IN);
+                    result[DID_ST_PACKET_SIZE_IN]        = efun::driver_info(DI_SIZE_PACKETS_IN);
+
+                    result[DID_ST_APPLY]                 = efun::driver_info(DI_NUM_FUNCTION_NAME_CALLS);
+                    result[DID_ST_APPLY_HITS]            = efun::driver_info(DI_NUM_FUNCTION_NAME_CALL_HITS);
+
+                    result[DID_ST_STRINGS]               = efun::driver_info(DI_NUM_VIRTUAL_STRINGS);
+                    result[DID_ST_STRING_SIZE]           = efun::driver_info(DI_SIZE_STRINGS);
+                    result[DID_ST_STR_TABLE_SIZE]        = efun::driver_info(DI_SIZE_STRING_TABLE);
+                    result[DID_ST_STR_OVERHEAD]          = efun::driver_info(DI_SIZE_STRING_OVERHEAD);
+                    result[DID_ST_UNTABLED]              = efun::driver_info(DI_NUM_STRINGS_UNTABLED);
+                    result[DID_ST_UNTABLED_SIZE]         = efun::driver_info(DI_SIZE_STRINGS_UNTABLED);
+                    result[DID_ST_TABLED]                = efun::driver_info(DI_NUM_STRINGS_TABLED);
+                    result[DID_ST_TABLED_SIZE]           = efun::driver_info(DI_SIZE_STRINGS_TABLED);
+                    result[DID_ST_STR_SEARCHES]          = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUPS_BY_INDEX);
+                    result[DID_ST_STR_SEARCHLEN]         = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_INDEX);
+                    result[DID_ST_STR_SEARCHES_BYVALUE]  = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUPS_BY_VALUE);
+                    result[DID_ST_STR_SEARCHLEN_BYVALUE] = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_VALUE);
+                    result[DID_ST_STR_CHAINS]            = efun::driver_info(DI_NUM_STRING_TABLE_SLOTS_USED);
+                    result[DID_ST_STR_ADDED]             = efun::driver_info(DI_NUM_STRING_TABLE_STRINGS_ADDED);
+                    result[DID_ST_STR_DELETED]           = efun::driver_info(DI_NUM_STRING_TABLE_STRINGS_REMOVED);
+                    result[DID_ST_STR_COLLISIONS]        = efun::driver_info(DI_NUM_STRING_TABLE_COLLISIONS);
+                    result[DID_ST_STR_FOUND]             = efun::driver_info(DI_NUM_STRING_TABLE_HITS_BY_INDEX);
+                    result[DID_ST_STR_FOUND_BYVALUE]     = efun::driver_info(DI_NUM_STRING_TABLE_HITS_BY_VALUE);
+
+                    result[DID_ST_RX_CACHED]             = efun::driver_info(DI_NUM_REGEX);
+                    result[DID_ST_RX_TABLE]              = efun::driver_info(DI_NUM_REGEX_TABLE_SLOTS);
+                    result[DID_ST_RX_TABLE_SIZE]         = efun::driver_info(DI_SIZE_REGEX);
+                    result[DID_ST_RX_REQUESTS]           = efun::driver_info(DI_NUM_REGEX_LOOKUPS);
+                    result[DID_ST_RX_REQ_FOUND]          = efun::driver_info(DI_NUM_REGEX_LOOKUP_HITS);
+                    result[DID_ST_RX_REQ_COLL]           = efun::driver_info(DI_NUM_REGEX_LOOKUP_COLLISIONS);
+
+                    result[DID_ST_MB_FILE]               = efun::driver_info(DI_SIZE_BUFFER_FILE);
+                    result[DID_ST_MB_SWAP]               = efun::driver_info(DI_SIZE_BUFFER_SWAP);
+
+                    result[DID_ST_BOOT_TIME]             = efun::driver_info(DI_BOOT_TIME);
+                    break;
+
+                case DID_SWAP:
+                    result = allocate(DID_SWAP_MAX);
+
+                    result[DID_SW_PROGS]                 = efun::driver_info(DI_NUM_PROGS_SWAPPED);
+                    result[DID_SW_PROG_SIZE]             = efun::driver_info(DI_SIZE_PROGS_SWAPPED);
+                    result[DID_SW_PROG_UNSWAPPED]        = efun::driver_info(DI_NUM_PROGS_UNSWAPPED);
+                    result[DID_SW_PROG_U_SIZE]           = efun::driver_info(DI_SIZE_PROGS_UNSWAPPED);
+                    result[DID_SW_VARS]                  = efun::driver_info(DI_NUM_OBJECTS_SWAPPED);
+                    result[DID_SW_VAR_SIZE]              = efun::driver_info(DI_SIZE_OBJECTS_SWAPPED);
+                    result[DID_SW_FREE]                  = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE);
+                    result[DID_SW_FREE_SIZE]             = efun::driver_info(DI_SIZE_SWAP_BLOCKS_FREE);
+                    result[DID_SW_FILE_SIZE]             = efun::driver_info(DI_SIZE_SWAP_BLOCKS);
+                    result[DID_SW_REUSED]                = efun::driver_info(DI_SIZE_SWAP_BLOCKS_REUSED);
+                    result[DID_SW_SEARCHES]              = efun::driver_info(DI_NUM_SWAP_BLOCKS_REUSE_LOOKUPS);
+                    result[DID_SW_SEARCH_LEN]            = efun::driver_info(DI_NUM_SWAP_BLOCKS_REUSE_LOOKUP_STEPS);
+                    result[DID_SW_F_SEARCHES]            = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE_LOOKUPS);
+                    result[DID_SW_F_SEARCH_LEN]          = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE_LOOKUP_STEPS);
+                    result[DID_SW_COMPACT]               = efun::driver_info(DC_SWAP_COMPACT_MODE);
+                    result[DID_SW_RECYCLE_FREE]          = efun::driver_info(DI_SWAP_RECYCLE_PHASE);
+                    break;
+
+                case DID_MEMORY:
+                    result = allocate(DID_MEMORY_MAX);
+
+                    result[DID_MEM_NAME]                 = efun::driver_info(DI_MEMORY_ALLOCATOR_NAME);
+                    result[DID_MEM_SBRK]                 = efun::driver_info(DI_NUM_SYS_ALLOCATED_BLOCKS);
+                    result[DID_MEM_SBRK_SIZE]            = efun::driver_info(DI_SIZE_SYS_ALLOCATED_BLOCKS);
+                    result[DID_MEM_LARGE]                = efun::driver_info(DI_NUM_LARGE_BLOCKS_ALLOCATED);
+                    result[DID_MEM_LARGE_SIZE]           = efun::driver_info(DI_SIZE_LARGE_BLOCKS_ALLOCATED);
+                    result[DID_MEM_LFREE]                = efun::driver_info(DI_NUM_LARGE_BLOCKS_FREE);
+                    result[DID_MEM_LFREE_SIZE]           = efun::driver_info(DI_SIZE_LARGE_BLOCKS_FREE);
+                    result[DID_MEM_LWASTED]              = efun::driver_info(DI_NUM_LARGE_BLOCKS_WASTE);
+                    result[DID_MEM_LWASTED_SIZE]         = efun::driver_info(DI_SIZE_LARGE_BLOCKS_WASTE);
+                    result[DID_MEM_CHUNK]                = efun::driver_info(DI_NUM_SMALL_BLOCK_CHUNKS);
+                    result[DID_MEM_CHUNK_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCK_CHUNKS);
+                    result[DID_MEM_SMALL]                = efun::driver_info(DI_NUM_SMALL_BLOCKS_ALLOCATED);
+                    result[DID_MEM_SMALL_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCKS_ALLOCATED);
+                    result[DID_MEM_SFREE]                = efun::driver_info(DI_NUM_SMALL_BLOCKS_FREE);
+                    result[DID_MEM_SFREE_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCKS_FREE);
+                    result[DID_MEM_SWASTED]              = efun::driver_info(DI_NUM_SMALL_BLOCKS_WASTE);
+                    result[DID_MEM_SWASTED_SIZE]         = efun::driver_info(DI_SIZE_SMALL_BLOCKS_WASTE);
+                    result[DID_MEM_MINC_CALLS]           = efun::driver_info(DI_NUM_INCREMENT_SIZE_CALLS);
+                    result[DID_MEM_MINC_SUCCESS]         = efun::driver_info(DI_NUM_INCREMENT_SIZE_CALL_SUCCESSES);
+                    result[DID_MEM_MINC_SIZE]            = efun::driver_info(DI_SIZE_INCREMENT_SIZE_CALL_DIFFS);
+                    result[DID_MEM_PERM]                 = efun::driver_info(DI_NUM_UNMANAGED_BLOCKS);
+                    result[DID_MEM_PERM_SIZE]            = efun::driver_info(DI_SIZE_UNMANAGED_BLOCKS);
+                    result[DID_MEM_CLIB]                 = efun::driver_info(DI_NUM_REPLACEMENT_MALLOC_CALLS);
+                    result[DID_MEM_CLIB_SIZE]            = efun::driver_info(DI_SIZE_REPLACEMENT_MALLOC_CALLS);
+                    result[DID_MEM_OVERHEAD]             = efun::driver_info(DI_SIZE_SMALL_BLOCK_OVERHEAD);
+                    result[DID_MEM_ALLOCATED]            = efun::driver_info(DI_SIZE_MEMORY_USED) + efun::driver_info(DI_SIZE_MEMORY_OVERHEAD);
+                    result[DID_MEM_USED]                 = efun::driver_info(DI_SIZE_MEMORY_USED);
+                    result[DID_MEM_TOTAL_UNUSED]         = efun::driver_info(DI_SIZE_MEMORY_UNUSED);
+                    result[DID_MEM_DEFRAG_CALLS]         = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_FULL) + efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_TARGETED);
+                    result[DID_MEM_DEFRAG_CALLS_REQ]     = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_TARGETED);
+                    result[DID_MEM_DEFRAG_REQ_SUCCESS]   = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALL_TARGET_HITS);
+                    result[DID_MEM_DEFRAG_BLOCKS_INSPECTED] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_INSPECTED);
+                    result[DID_MEM_DEFRAG_BLOCKS_MERGED] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_MERGED);
+                    result[DID_MEM_DEFRAG_BLOCKS_RESULT] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_RESULTING);
+                    result[DID_MEM_AVL_NODES]            = efun::driver_info(DI_NUM_FREE_BLOCKS_AVL_NODES);
+                    result[DID_MEM_EXT_STATISTICS]       = efun::driver_info(DI_MEMORY_EXTENDED_STATISTICS);
+                    break;
+            }
+
+            if (sizeof(args) == 2)
+            {
+                int idx = args[0];
+                if (idx < 0 || idx >= sizeof(result))
+                    raise_error(sprintf("Illegal index for debug_info(): %d, expected 0..%d\n",
+                        idx, sizeof(result)-1));
+
+                return result[idx];
+            }
+            else
+                return result;
+        }
+
+        case DINFO_TRACE:
+        {
+            int which = DIT_CURRENT;
+
+            if (sizeof(args) > 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (sizeof(args))
+            {
+                if (!intp(args[0]))
+                    raise_error("bag arg 2 to debug_info().\n");
+                which = args[0];
+            }
+
+            switch (which)
+            {
+                case DIT_CURRENT:
+                    return efun::driver_info(DI_TRACE_CURRENT);
+
+                case DIT_ERROR:
+                    return efun::driver_info(DI_TRACE_LAST_ERROR) || ({ "No trace." });
+
+                case DIT_UNCAUGHT_ERROR:
+                    return efun::driver_info(DI_TRACE_LAST_UNCAUGHT_ERROR) || ({ "No trace." });
+
+                case DIT_STR_CURRENT:
+                    return efun::driver_info(DI_TRACE_CURRENT_AS_STRING);
+
+                case DIT_CURRENT_DEPTH:
+                    return efun::driver_info(DI_TRACE_CURRENT_DEPTH);
+
+                default:
+                    raise_error("bad arg 2 to debug_info().\n");
+            }
+
+        }
+
+        case DINFO_EVAL_NUMBER:
+            return efun::driver_info(DI_EVAL_NUMBER);
+    }
+    return 0;
+}
+
+#endif
diff --git a/secure/simul_efun/spare/enable_commands.c b/secure/simul_efun/spare/enable_commands.c
new file mode 100644
index 0000000..9d1c963
--- /dev/null
+++ b/secure/simul_efun/spare/enable_commands.c
@@ -0,0 +1,30 @@
+/* These sefuns are to provide a replacement for the efuns enable_commands()
+ * and disable_commands().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#include <configuration.h>
+
+#if ! __EFUN_DEFINED__(enable_commands)
+
+void enable_commands()
+{
+    object ob = efun::previous_object();
+
+    efun::configure_object(ob, OC_COMMANDS_ENABLED, 1);
+    efun::set_this_player(ob);
+}
+
+#endif
+
+#if ! __EFUN_DEFINED__(disable_commands)
+
+void disable_commands()
+{
+    object ob = efun::previous_object();
+
+    efun::configure_object(ob, OC_COMMANDS_ENABLED, 0);
+    efun::set_this_player(0);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/hash.c b/secure/simul_efun/spare/hash.c
new file mode 100644
index 0000000..51bbf60
--- /dev/null
+++ b/secure/simul_efun/spare/hash.c
@@ -0,0 +1,17 @@
+#include "/sys/tls.h"
+
+deprecated string md5(mixed arg, varargs mixed* iterations)
+{
+    if (extern_call())
+         set_this_object(previous_object());
+
+    return hash(TLS_HASH_MD5, arg, iterations...);
+}
+
+deprecated string sha1(mixed arg, varargs mixed* iterations)
+{
+    if (extern_call())
+         set_this_object(previous_object());
+
+    return hash(TLS_HASH_SHA1, arg, iterations...);
+}
diff --git a/secure/simul_efun/spare/livings.c b/secure/simul_efun/spare/livings.c
new file mode 100644
index 0000000..160fcee
--- /dev/null
+++ b/secure/simul_efun/spare/livings.c
@@ -0,0 +1,348 @@
+// * living_name-Behandlung
+
+#define clean_log(s)
+//#define clean_log(s) log_file("CLEAN_SIM",ctime(time())[4..18]+": "+(s));
+
+private nosave mapping living_name_m, name_living_m, netdead;
+
+private void InitLivingData(mixed wizinfo) {
+  // living_name ist ein Pointer der auch in der extra_wizinfo gehalten 
+  // wird, so kann die simul_efun neu geladen werden, ohne dass dieses
+  // Mapping verloren geht
+  if (!mappingp(living_name_m = wizinfo[LIVING_NAME]))
+    living_name_m = wizinfo[LIVING_NAME] = m_allocate(0, 1);
+
+  // Gleiches gilt fuer das Mapping Name-Living
+  if (!mappingp(name_living_m = wizinfo[NAME_LIVING]))
+    name_living_m = wizinfo[NAME_LIVING] = m_allocate(0, 1);
+
+  // Netztote sind ebenfalls in der extra_wizinfo
+  if (!mappingp(netdead = wizinfo[NETDEAD_MAP]))
+    netdead = wizinfo[NETDEAD_MAP] = ([]);
+}
+
+public varargs string getuuid( object ob )
+{
+    mixed *ret;
+
+    if ( !objectp(ob) )
+       ob = previous_object();
+
+    if ( !query_once_interactive(ob) )
+       return getuid(ob);
+
+    ret = (mixed)master()->get_userinfo( getuid(ob) );
+
+    if ( !pointerp(ret) || sizeof(ret) < 5 )
+       return getuid(ob);
+
+    // Username + "_" + CreationDate
+    return ret[0] + "_" + ret[5];
+}
+
+void set_object_living_name(string livname, object obj)
+{
+  string old;
+  mixed a;
+  int i;
+
+  if (previous_object()==obj || previous_object()==this_object() ||
+      previous_object()==master())
+  {
+    if(!livname || !stringp(livname)) {
+      set_this_object(previous_object());
+      raise_error(sprintf("%O: illegal living name: %O\n", obj, livname));
+    }
+    if (old = living_name_m[obj]) {
+      if (pointerp(a = name_living_m[old])) {
+       a[member(a, obj)] = 0;
+      } else {
+       m_delete(name_living_m, old);
+      }
+    }
+    living_name_m[obj] = livname;
+    if (a = name_living_m[livname]) {
+      if (!pointerp(a)) {
+       name_living_m[livname] = ({a, obj});
+       return;
+      }
+      /* Try to reallocate entry from destructed object */
+      if ((i = member(a, 0)) >= 0) {
+       a[i] = obj;
+       return;
+      }
+      name_living_m[livname] = a + ({obj});
+      return;
+    }
+    name_living_m[livname] = obj;
+  }
+}
+
+void set_living_name(string livname)
+{
+  set_object_living_name(livname,previous_object());
+}
+
+void remove_living_name()
+{
+  string livname;
+
+  if (!previous_object())
+    return;
+  if (livname=living_name_m[previous_object()])
+  {
+    m_delete(living_name_m,previous_object());
+    if (objectp(name_living_m[livname]))
+    {
+      if (name_living_m[livname]==previous_object())
+       m_delete(name_living_m,livname);
+      return;
+    }
+    if (pointerp(name_living_m[livname]))
+    {
+      name_living_m[livname]-=({previous_object()});
+      if (!sizeof(name_living_m[livname]))
+       m_delete(name_living_m,livname);
+    }
+  }
+}
+
+void _set_netdead()
+{
+  if (query_once_interactive(previous_object()))
+    netdead[getuid(previous_object())]=previous_object();
+}
+
+void _remove_netdead()
+{
+  m_delete(netdead,getuid(previous_object()));
+}
+
+object find_netdead(string uuid)
+{
+  int i;
+  string uid;
+  // Wenn sscanf() 2 liefert, ist uuid auch ne uuid.
+  int is_uuid = sscanf(uuid, "%s_%d", uid, i) == 2;
+  if (!is_uuid)
+    uid = uuid;
+ 
+  if (is_uuid && getuuid(netdead[uid]) != uuid)
+      return 0;
+
+  return netdead[uid];
+}
+
+string *dump_netdead()
+{
+  return m_indices(netdead);
+}
+
+object find_living(string livname) {
+  mixed *a, r;
+  int i;
+
+  if (pointerp(r = name_living_m[livname])) {
+    if (member(r,0)>=0)
+      r-=({0});
+    if (!sizeof(r)){
+      m_delete(name_living_m,livname);
+      clean_log(sprintf("find_living loescht %s\n",livname));
+      return 0;
+    }
+    if ( !living(r = (a = r)[0])) {
+      for (i = sizeof(a); --i;) {
+       if (living(a[<i])) {
+         r = a[<i];
+         a[<i] = a[0];
+         return a[0] = r;
+       }
+      }
+    }
+    return r;
+  }
+  return living(r) && r;
+}
+
+object *find_livings(string livname)
+{
+  mixed r;
+
+  if (pointerp(r=name_living_m[livname]))
+    r-=({0});
+  else
+    if (objectp(r))
+      r=({r});
+  if (!pointerp(r)||!sizeof(r))
+    return 0;
+  return r;
+}
+
+object find_player(string uuid) {
+  object *objs;
+  mixed res;
+  int i;
+  string uid;
+
+  if (!stringp(uuid))
+    return 0;
+  // Wenn sscanf() 2 liefert, ist uuid auch ne uuid.
+  int is_uuid = sscanf(uuid, "%s_%d", uid, i) == 2;
+  if (!is_uuid)
+    uid = uuid;
+
+  if (pointerp(res = name_living_m[uid])) {
+    // zerstoerte Objekte ggf. entfernen
+    if (member(res,0)>=0) {
+      res-=({0});
+      name_living_m[uid] = res;
+    }
+    // ggf. Namen ohne Objekte entfernen.
+    if (!sizeof(res)){
+      m_delete(name_living_m,uid);
+      clean_log(sprintf("find_player loescht %s\n",uid));
+      return 0;
+    }
+    objs = res;
+    res = objs[0];
+    // Wenn das 0. Element der Spieler ist, sind wir fertig. Ansonsten suchen
+    // wir den Spieler und schieben ihn an die 0. Stelle im Array.
+    if ( !query_once_interactive(res)) {
+      for (i = sizeof(objs); --i;) {
+       if (objs[<i] && query_once_interactive(objs[<i])) {
+         res = objs[<i];
+         objs[<i] = objs[0];
+         objs[0] = res;
+         break;
+       }
+      }
+      res = (objectp(res) && query_once_interactive(res)) ? res : 0;
+    }
+    // else: in res steht der Spieler schon.
+  }
+  else if (objectp(res)) // r ist nen Einzelobjekt
+    res = query_once_interactive(res) ? res : 0;
+
+  // res ist jetzt ggf. der Spieler. Aber wenn der ne andere als die gesuchte
+  // UUID hat, ist das Ergebnis trotzdem 0.
+  if (is_uuid && getuuid(res)!=uuid)
+      return 0;
+
+  // endlich gefunden
+  return res;
+}
+
+private int check_match( string str, int players_only )
+{
+    mixed match;
+
+    if ( !(match = name_living_m[str]) )
+       return 0;
+
+    if ( !pointerp(match) )
+       match = ({ match });
+
+    match -= ({0});
+
+    if ( sizeof(match) ){
+       if ( players_only )
+           return sizeof(filter( match, #'query_once_interactive/*'*/ ))
+              > 0;
+       else
+           return 1;
+    }
+
+    m_delete( name_living_m, str );
+    clean_log( sprintf("check_match loescht %s\n", str) );
+    return 0;
+}
+
+//TODO:: string|string* exclude
+varargs mixed match_living( string str, int players_only, mixed exclude )
+{
+    int i, s;
+    mixed match, *user;
+
+    if ( !str || str == "" )
+       return 0;
+
+    if ( !pointerp(exclude) )
+       exclude = ({ exclude });
+
+    if ( member(exclude, str) < 0 && check_match(str, players_only) )
+       return str;
+
+    user = m_indices(name_living_m);
+    s = sizeof(str);
+    match = 0;
+
+    for ( i = sizeof(user); i--; )
+       if ( str == user[i][0..s-1] && member( exclude, user[i] ) < 0 )
+           if ( match )
+              return -1;
+           else
+              if ( check_match(user[i], players_only) )
+                  match = user[i];
+
+    if ( !match )
+       return -2;
+
+    return match;
+}
+
+mixed *dump_livings()
+{
+  return sort_array(m_indices(name_living_m),#'>);
+}
+
+// * regelmaessig Listen von Ballast befreien
+private void clean_name_living_m(string *keys, int left, int num)
+{
+  int i, j;
+  mixed a;
+
+  while (left && num--)
+  {
+    if (pointerp(a = name_living_m[keys[--left]]) && member(a, 0)>=0)
+    {
+      a-=({0});
+      name_living_m[keys[left]] = a;
+    }
+    if (!a || (pointerp(a) && !sizeof(a)))
+    {
+      clean_log("Toasting "+keys[left]+"\n");
+      m_delete(name_living_m, keys[left]);
+    } else clean_log(sprintf("KEEPING %s (%O)\n",keys[left],pointerp(a)?sizeof(a):a));
+  }
+  if (left)
+    efun::call_out(#'clean_name_living_m, 1, keys, left, 80);
+  else
+    clean_log("Clean process finished\n");
+}
+
+private void clean_netdead() {
+  int i;
+  string *s;
+  object ob;
+
+  s=m_indices(netdead);
+  for (i=sizeof(s)-1;i>=0;i--)
+    if (!objectp(ob=netdead[s[i]]) || interactive(ob))
+      m_delete(netdead,s[i]);
+}
+
+private void CleanLivingData() {
+  if (find_call_out(#'clean_name_living_m) < 0) {
+    clean_log("Starting clean process\n");
+    efun::call_out(#'clean_name_living_m,
+                 1,
+                 m_indices(name_living_m),
+                 sizeof(name_living_m),
+                 80
+                 );
+  }
+  efun::call_out(#'clean_netdead,2);
+}
+
+#undef clean_log
+
diff --git a/secure/simul_efun/spare/object_info.c b/secure/simul_efun/spare/object_info.c
new file mode 100644
index 0000000..ed7c009
--- /dev/null
+++ b/secure/simul_efun/spare/object_info.c
@@ -0,0 +1,106 @@
+/* This sefun is to provide the old semantics of the efun object_info().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if __EFUN_DEFINED__(driver_info) /* Newer version is there */
+
+#include <objectinfo.h>
+#include <object_info.h>
+
+mixed object_info(object ob, int what, varargs int* index)
+{
+    mixed * result;
+
+    if (sizeof(index) > 1)
+        raise_error("Too many arguments to object_info\n");
+
+    switch(what)
+    {
+        default:
+            raise_error(sprintf("Illegal value %d for object_info().\n", what));
+
+        case OINFO_BASIC:
+        {
+            result = allocate(OIB_MAX);
+
+            result[OIB_HEART_BEAT]        = efun::object_info(ob, OC_HEART_BEAT);
+            result[OIB_IS_WIZARD]         = 0;   /* Not supported anymore. */
+            result[OIB_ENABLE_COMMANDS]   = efun::object_info(ob, OC_COMMANDS_ENABLED);
+            result[OIB_CLONE]             = efun::clonep(ob);
+            result[OIB_DESTRUCTED]        = 0;   /* Not possible anymore. */
+            result[OIB_SWAPPED]           = efun::object_info(ob, OI_SWAPPED);
+            result[OIB_ONCE_INTERACTIVE]  = efun::object_info(ob, OI_ONCE_INTERACTIVE);
+            result[OIB_RESET_STATE]       = efun::object_info(ob, OI_RESET_STATE);
+            result[OIB_WILL_CLEAN_UP]     = efun::object_info(ob, OI_WILL_CLEAN_UP);
+            result[OIB_LAMBDA_REFERENCED] = efun::object_info(ob, OI_LAMBDA_REFERENCED);
+            result[OIB_SHADOW]            = efun::object_info(ob, OI_SHADOW_PREV) && 1;
+            result[OIB_REPLACED]          = efun::object_info(ob, OI_REPLACED);
+            result[OIB_NEXT_RESET]        = efun::object_info(ob, OI_NEXT_RESET_TIME);
+            result[OIB_TIME_OF_REF]       = efun::object_info(ob, OI_LAST_REF_TIME);
+            result[OIB_REF]               = efun::object_info(ob, OI_OBJECT_REFS);
+            result[OIB_GIGATICKS]         = efun::object_info(ob, OI_GIGATICKS);
+            result[OIB_TICKS]             = efun::object_info(ob, OI_TICKS);
+            result[OIB_SWAP_NUM]          = efun::object_info(ob, OI_SWAP_NUM);
+            result[OIB_PROG_SWAPPED]      = efun::object_info(ob, OI_PROG_SWAPPED);
+            result[OIB_VAR_SWAPPED]       = efun::object_info(ob, OI_VAR_SWAPPED);
+            result[OIB_NAME]              = efun::object_name(ob);
+            result[OIB_LOAD_NAME]         = efun::load_name(ob);
+            result[OIB_NEXT_ALL]          = efun::object_info(ob, OI_OBJECT_NEXT);
+            result[OIB_PREV_ALL]          = efun::object_info(ob, OI_OBJECT_PREV);
+            result[OIB_NEXT_CLEANUP]      = efun::object_info(ob, OI_NEXT_CLEANUP_TIME);
+            break;
+        }
+
+        case OINFO_POSITION:
+        {
+            result = allocate(OIP_MAX);
+
+            result[OIP_NEXT] = efun::object_info(ob, OI_OBJECT_NEXT);
+            result[OIP_PREV] = efun::object_info(ob, OI_OBJECT_PREV);
+            result[OIP_POS]  = efun::object_info(ob, OI_OBJECT_POS);
+            break;
+        }
+
+        case OINFO_MEMORY:
+        {
+            result = allocate(OIM_MAX);
+
+            result[OIM_REF]                = efun::object_info(ob, OI_PROG_REFS);
+            result[OIM_NAME]               = efun::program_name(ob);
+            result[OIM_PROG_SIZE]          = efun::object_info(ob, OI_PROG_SIZE);
+            result[OIM_NUM_FUNCTIONS]      = efun::object_info(ob, OI_NUM_FUNCTIONS);
+            result[OIM_SIZE_FUNCTIONS]     = efun::object_info(ob, OI_SIZE_FUNCTIONS);
+            result[OIM_NUM_VARIABLES]      = efun::object_info(ob, OI_NUM_VARIABLES);
+            result[OIM_SIZE_VARIABLES]     = efun::object_info(ob, OI_SIZE_VARIABLES);
+            result[OIM_NUM_STRINGS]        = efun::object_info(ob, OI_NUM_STRINGS);
+            result[OIM_SIZE_STRINGS]       = efun::object_info(ob, OI_SIZE_STRINGS);
+            result[OIM_SIZE_STRINGS_DATA]  = efun::object_info(ob, OI_SIZE_STRINGS_DATA);
+            result[OIM_SIZE_STRINGS_TOTAL] = efun::object_info(ob, OI_SIZE_STRINGS_DATA_TOTAL);
+            result[OIM_NUM_INHERITED]      = efun::object_info(ob, OI_NUM_INHERITED);
+            result[OIM_SIZE_INHERITED]     = efun::object_info(ob, OI_SIZE_INHERITED);
+            result[OIM_TOTAL_SIZE]         = efun::object_info(ob, OI_PROG_SIZE_TOTAL);
+            result[OIM_DATA_SIZE]          = efun::object_info(ob, OI_DATA_SIZE);
+            result[OIM_TOTAL_DATA_SIZE]    = efun::object_info(ob, OI_DATA_SIZE_TOTAL);
+            result[OIM_NO_INHERIT]         = efun::object_info(ob, OI_NO_INHERIT);
+            result[OIM_NO_CLONE]           = efun::object_info(ob, OI_NO_CLONE);
+            result[OIM_NO_SHADOW]          = efun::object_info(ob, OI_NO_SHADOW);
+            result[OIM_NUM_INCLUDES]       = efun::object_info(ob, OI_NUM_INCLUDED);
+            result[OIM_SHARE_VARIABLES]    = efun::object_info(ob, OI_SHARE_VARIABLES);
+            break;
+        }
+    }
+
+    if (sizeof(index))
+    {
+        int idx = index[0];
+        if (idx < 0 || idx >= sizeof(result))
+            raise_error(sprintf("Illegal index for object_info(): %d, expected 0..%d\n",
+                idx, sizeof(result)-1));
+
+        return result[idx];
+    }
+    else
+        return result;
+}
+
+#endif
diff --git a/secure/simul_efun/spare/query_editing.c b/secure/simul_efun/spare/query_editing.c
new file mode 100644
index 0000000..5146dcd
--- /dev/null
+++ b/secure/simul_efun/spare/query_editing.c
@@ -0,0 +1,17 @@
+/* This sefun is to provide a replacement for the efun query_editing().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_editing)
+
+#include <interactive_info.h>
+
+int|object query_editing(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    return efun::interactive_info(ob, II_EDITING);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/query_idle.c b/secure/simul_efun/spare/query_idle.c
new file mode 100644
index 0000000..93a32ae
--- /dev/null
+++ b/secure/simul_efun/spare/query_idle.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_idle().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_idle)
+
+#include <interactive_info.h>
+
+int query_idle(object ob)
+{
+    return efun::interactive_info(ob, II_IDLE);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/query_input_pending.c b/secure/simul_efun/spare/query_input_pending.c
new file mode 100644
index 0000000..dd8c311
--- /dev/null
+++ b/secure/simul_efun/spare/query_input_pending.c
@@ -0,0 +1,17 @@
+/* This sefun is to provide a replacement for the efun query_input_pending().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_input_pending)
+
+#include <interactive_info.h>
+
+object query_input_pending(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    return efun::interactive_info(ob, II_INPUT_PENDING);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/query_ip_name.c b/secure/simul_efun/spare/query_ip_name.c
new file mode 100644
index 0000000..ada6e07
--- /dev/null
+++ b/secure/simul_efun/spare/query_ip_name.c
@@ -0,0 +1,48 @@
+/* This sefun is to provide a replacement for the efuns query_ip_name() and
+ * query_ip_number().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_ip_name)
+
+#include <interactive_info.h>
+
+private varargs string _query_ip_name(object player)
+{
+    object ob = player;
+    ob ||= efun::this_player();
+
+    player = efun::interactive_info(ob, II_IP_ADDRESS);
+    return efun::interactive_info(ob, II_IP_NAME);
+}
+
+private varargs string _query_ip_number(object player)
+{
+    object ob = player;
+    ob ||= efun::this_player();
+
+    player = efun::interactive_info(ob, II_IP_ADDRESS);
+    return efun::interactive_info(ob, II_IP_NUMBER);
+}
+
+// * Herkunfts-Ermittlung
+string query_ip_number(object ob)
+{
+  ob= ob || this_player();
+  if (!objectp(ob) || !interactive(ob)) return 0;
+  if(ob->query_realip() && (string)ob->query_realip()!="")
+  {
+    return (string)ob->query_realip();
+  }
+  return _query_ip_number(ob);
+}
+
+string query_ip_name(mixed ob)
+{
+  if ( !ob || objectp(ob) )
+      ob=query_ip_number(ob);
+  return (string)"/p/daemon/iplookup"->host(ob);
+}
+
+#endif
+
diff --git a/secure/simul_efun/spare/query_limits.c b/secure/simul_efun/spare/query_limits.c
new file mode 100644
index 0000000..ecbf390
--- /dev/null
+++ b/secure/simul_efun/spare/query_limits.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_limits().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_limits)
+
+#include <driver_info.h>
+
+varargs int* query_limits(int def)
+{
+    return efun::driver_info(def ? DC_DEFAULT_RUNTIME_LIMITS : DI_CURRENT_RUNTIME_LIMITS);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/query_load_average.c b/secure/simul_efun/spare/query_load_average.c
new file mode 100644
index 0000000..3b36f0e
--- /dev/null
+++ b/secure/simul_efun/spare/query_load_average.c
@@ -0,0 +1,16 @@
+/* This sefun is to provide a replacement for the efun query_load_average().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_load_average)
+
+#include <driver_info.h>
+
+string query_load_average()
+{
+    return efun::sprintf("%.2f cmds/s, %.2f comp lines/s",
+        efun::driver_info(DI_LOAD_AVERAGE_COMMANDS),
+        efun::driver_info(DI_LOAD_AVERAGE_LINES));
+}
+
+#endif
diff --git a/secure/simul_efun/spare/query_mud_port.c b/secure/simul_efun/spare/query_mud_port.c
new file mode 100644
index 0000000..bff009d
--- /dev/null
+++ b/secure/simul_efun/spare/query_mud_port.c
@@ -0,0 +1,35 @@
+/* This sefun is to provide a replacement for the efun query_mud_port().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_mud_port)
+
+#include <interactive_info.h>
+#include <driver_info.h>
+
+int query_mud_port(varargs int*|object* args)
+{
+    if(!sizeof(args) && efun::this_player() && efun::interactive(this_player()))
+        return efun::interactive_info(this_player(), II_MUD_PORT);
+
+    if(sizeof(args) > 1)
+        raise_error("Too many arguments to query_mud_port\n");
+
+    if(sizeof(args) && efun::objectp(args[0]) && efun::interactive(args[0]))
+        return efun::interactive_info(args[0], II_MUD_PORT);
+
+    if(sizeof(args) && intp(args[0]))
+    {
+        int* ports = efun::driver_info(DI_MUD_PORTS);
+        if(args[0] < -1 || args[0] >= sizeof(ports))
+            raise_error(efun::sprintf("Bad arg 1 to query_mud_port(): value %d out of range.\n", args[0]));
+        else if(args[0] == -1)
+            return sizeof(ports);
+        else
+            return ports[args[0]];
+    }
+
+    return efun::driver_info(DI_MUD_PORTS)[0];
+}
+
+#endif
diff --git a/secure/simul_efun/spare/query_once_interactive.c b/secure/simul_efun/spare/query_once_interactive.c
new file mode 100644
index 0000000..bc7829f
--- /dev/null
+++ b/secure/simul_efun/spare/query_once_interactive.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_once_interactive().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_once_interactive)
+
+#include <object_info.h>
+
+int query_once_interactive(object ob)
+{
+    return efun::object_info(ob, OI_ONCE_INTERACTIVE);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/query_snoop.c b/secure/simul_efun/spare/query_snoop.c
new file mode 100644
index 0000000..e6a69c0
--- /dev/null
+++ b/secure/simul_efun/spare/query_snoop.c
@@ -0,0 +1,30 @@
+/* This sefun is to provide a replacement for the efun query_snoop().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+
+#include <interactive_info.h>
+
+private object _query_snoop(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    object prev = efun::previous_object();
+    efun::set_this_object(prev);
+#if ! __EFUN_DEFINED__(query_snoop)
+    return efun::interactive_info(ob, II_SNOOP_NEXT);
+#else
+    return efun::query_snoop(ob);
+#endif
+}
+
+nomask object query_snoop(object who) {
+  object snooper;
+
+  snooper=_query_snoop(who);
+  if (!snooper) return 0;
+  if (query_wiz_grp(snooper)>query_wiz_grp(getuid(previous_object())) &&
+      IS_ARCH(snooper)) return 0;
+  return snooper;
+}
diff --git a/secure/simul_efun/spare/set_heart_beat.c b/secure/simul_efun/spare/set_heart_beat.c
new file mode 100644
index 0000000..a3995eb
--- /dev/null
+++ b/secure/simul_efun/spare/set_heart_beat.c
@@ -0,0 +1,24 @@
+/* This sefun is to provide a replacement for the efun set_heart_beat().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(set_heart_beat)
+
+#include <configuration.h>
+
+int set_heart_beat(int flag)
+{
+    object ob = efun::previous_object();
+    int hb = efun::object_info(ob, OC_HEART_BEAT);
+
+    if (!flag == !hb)
+        return 0;
+
+    efun::configure_object(ob, OC_HEART_BEAT, flag);
+
+    return 1;
+}
+
+#endif
+
+
diff --git a/secure/simul_efun/spare/set_is_wizard.c b/secure/simul_efun/spare/set_is_wizard.c
new file mode 100644
index 0000000..001ccdb
--- /dev/null
+++ b/secure/simul_efun/spare/set_is_wizard.c
@@ -0,0 +1,137 @@
+/* These are the special commands from the driver that are activated with
+ * set_is_wizard(). These functions must be added to the player object.
+ * Also set_is_wizard() must be called in the corresponding player object,
+ * not as an (simul-)efun.
+ */
+
+#include <commands.h>
+#include <driver_info.h>
+
+/* is_wizard:
+ *  1: has actions,
+ *  0: never had actions,
+ * -1: had actions once.
+ */
+private nosave int is_wizard;
+
+private int driver_malloc(string str)
+{
+    if(is_wizard <= 0)
+        return 0;
+
+    if(!sizeof(str))
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC));
+        return 1;
+    }
+
+    if(str == "extstats")
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC_EXTENDED));
+        return 1;
+    }
+
+    return 0;
+}
+
+private int driver_dumpallobj(string str)
+{
+    if(is_wizard <= 0 || sizeof(str))
+        return 0;
+
+    write("Dumping to /OBJ_DUMP ... ");
+    efun::dump_driver_info(DDI_OBJECTS);
+    efun::dump_driver_info(DDI_OBJECTS_DESTRUCTED);
+    write("done\n");
+    return 1;
+}
+
+private int driver_opcdump(string str)
+{
+    if(is_wizard <= 0 || sizeof(str))
+        return 0;
+
+    efun::dump_driver_info(DDI_OPCODES);
+    return 1;
+}
+
+private int driver_status(string str)
+{
+    int opt;
+    if(is_wizard <= 0)
+        return 0;
+
+    switch(str || "")
+    {
+        case "":
+            opt = DI_STATUS_TEXT_MEMORY;
+            break;
+
+        case "tables":
+        case " tables":
+            opt = DI_STATUS_TEXT_TABLES;
+            break;
+
+        case "swap":
+        case " swap":
+            opt = DI_STATUS_TEXT_SWAP;
+            break;
+
+        case "malloc":
+        case " malloc":
+            opt = DI_STATUS_TEXT_MALLOC;
+            break;
+
+        case "malloc extstats":
+        case " malloc extstats":
+            opt = DI_STATUS_TEXT_MALLOC_EXTENDED;
+            break;
+
+        default:
+            return 0;
+    }
+
+    write(efun::driver_info(opt));
+    return 1;
+}
+
+int set_is_wizard(varargs <object|int>* args)
+{
+    int oldval = is_wizard;
+
+    if(!sizeof(args))
+        raise_error("Too few arguments to set_is_wizard\n");
+    if(sizeof(args) > 2)
+        raise_error("Too many arguments to set_is_wizard\n");
+    if(!objectp(args[0]))
+        raise_error("Bad arg 1 to set_is_wizard()\n");
+    if(args[0] != this_object())
+        raise_error("Only set_is_wizard for the current object supported\n");
+    if(this_player() != this_object())
+        raise_error("The current object must be this_player() for set_is_wizard()\n");
+    if(sizeof(args) == 2 && !intp(args[1]))
+        raise_error("Bad arg 2 to set_is_wizard()\n");
+
+    if(sizeof(args) == 2 && !args[1])
+    {
+        if(is_wizard > 0)
+            is_wizard = -1;
+    }
+    else if(sizeof(args) == 2 && args[1]<0)
+    {
+         // Just return the old value.
+    }
+    else
+    {
+        if(!is_wizard)
+        {
+            add_action(#'driver_malloc, "malloc");
+            add_action(#'driver_dumpallobj, "dumpallobj");
+            add_action(#'driver_opcdump, "opcdump");
+            add_action(#'driver_status, "status", AA_NOSPACE);
+        }
+        is_wizard = 1;
+    }
+
+    return oldval > 0;
+}
diff --git a/secure/simul_efun/spare/set_prompt.c b/secure/simul_efun/spare/set_prompt.c
new file mode 100644
index 0000000..0b59692
--- /dev/null
+++ b/secure/simul_efun/spare/set_prompt.c
@@ -0,0 +1,21 @@
+/* This sefun is to provide a replacement for the efun set_prompt().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(set_prompt)
+
+#include <configuration.h>
+
+varargs string|closure set_prompt(string|closure|int prompt, object ob)
+{
+    ob ||= efun::this_player();
+
+    mixed oldprompt = efun::interactive_info(ob, IC_PROMPT);
+
+    if(!intp(prompt))
+        efun::configure_interactive(ob, IC_PROMPT, prompt);
+
+    return oldprompt;
+}
+
+#endif
diff --git a/secure/simul_efun/spare/shadow.c b/secure/simul_efun/spare/shadow.c
new file mode 100644
index 0000000..7608c73
--- /dev/null
+++ b/secure/simul_efun/spare/shadow.c
@@ -0,0 +1,55 @@
+/* These sefuns are to provide a replacement for the efun query_shadowing()
+ * and the old semantics of the efun shadow().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_shadowing)
+
+#include <object_info.h>
+
+object query_shadowing(object ob)
+{
+    return efun::object_info(ob, OI_SHADOW_PREV);
+}
+
+private object _shadow(object ob, int flag)
+{
+    if(flag)
+    {
+        object shadower = efun::previous_object(1);
+        efun::set_this_object(shadower);
+
+        if (efun::shadow(ob))
+            return efun::object_info(shadower, OI_SHADOW_PREV);
+        else
+            return 0;
+    }
+    else
+        return efun::object_info(ob, OI_SHADOW_NEXT);
+}
+#else
+private object _shadow(object ob, int flag)
+{
+  set_this_object(previous_object(1));
+  return efun::shadow(ob, flag);
+}
+#endif
+
+
+public object shadow(object ob, int flag)
+{
+  object res = funcall(#'_shadow,ob, flag);
+  if (flag)
+    "/secure/shadowmaster"->RegisterShadow(previous_object());
+  return res;
+}
+
+private void _unshadow() {
+  set_this_object(previous_object(1));
+  efun::unshadow();
+}
+
+public void unshadow() {
+  funcall(#'_unshadow);
+  "/secure/shadowmaster"->UnregisterShadow(previous_object());
+}
diff --git a/secure/simul_efun/spare/simul_efun.c b/secure/simul_efun/spare/simul_efun.c
new file mode 100644
index 0000000..84e0b78
--- /dev/null
+++ b/secure/simul_efun/spare/simul_efun.c
@@ -0,0 +1,2016 @@
+// MorgenGrauen MUDlib
+//
+// simul_efun.c -- simul efun's
+//
+// $Id: simul_efun.c 7408 2010-02-06 00:27:25Z Zesstra $
+#pragma strict_types,save_types,rtt_checks
+#pragma no_clone,no_shadow,no_inherit
+#pragma pedantic,range_check,warn_deprecated
+#pragma warn_empty_casts,warn_missing_return,warn_function_inconsistent
+
+// Absolute Pfade erforderlich - zum Zeitpunkt, wo der Master geladen
+// wird, sind noch keine Include-Pfade da ...
+
+#define SNOOPLOGFILE "SNOOP"
+#define ASNOOPLOGFILE "ARCH/SNOOP"
+
+#include "/secure/config.h"
+#include "/secure/wizlevels.h"
+#include "/sys/snooping.h"
+#include "/sys/language.h"
+#include "/sys/thing/properties.h"
+#include "/sys/wizlist.h"
+#include "/sys/erq.h"
+#include "/sys/lpctypes.h"
+#include "/sys/daemon.h"
+#include "/sys/player/base.h"
+#include "/sys/thing/description.h"
+#include "/sys/container.h"
+#include "/sys/defines.h"
+#include "/sys/telnet.h"
+#include "/sys/objectinfo.h"
+#include "/sys/files.h"
+#include "/sys/strings.h"
+#include "/sys/time.h"
+#include "/sys/lpctypes.h"
+#include "/sys/notify_fail.h"
+#include "/sys/tls.h"
+#include "/sys/input_to.h"
+#include "/sys/object_info.h"
+
+/* function prototypes
+ */
+string dtime(int wann);
+varargs int log_file(string file, string txt, int size_to_break);
+int query_wiz_level(mixed player);
+nomask varargs int snoop(object me, object you);
+varargs string country(mixed ip, string num);
+int query_wiz_grp(mixed wiz);
+public varargs object deep_present(mixed what, object ob);
+nomask int secure_level();
+nomask string secure_euid();
+public nomask int process_call();
+nomask mixed __create_player_dummy(string name);
+varargs string replace_personal(string str, mixed *obs, int caps);
+
+
+//replacements for dropped efuns in LD
+#if !__EFUN_DEFINED__(extract)
+varargs string extract(string str, int from, int to);
+#endif
+#if !__EFUN_DEFINED__(slice_array)
+varargs mixed slice_array(mixed array, int from, int to);
+#endif
+#if !__EFUN_DEFINED__(member_array)
+int member_array(mixed item, mixed arraystring);
+#endif
+
+// Include the different sub 'modules' of the simul_efun
+#include __DIR__"debug_info.c"
+#include __DIR__"enable_commands.c"
+#include __DIR__"hash.c"
+#include __DIR__"object_info.c"
+#include __DIR__"query_editing.c"
+#include __DIR__"query_idle.c"
+#include __DIR__"query_input_pending.c"
+#include __DIR__"query_ip_name.c"
+#include __DIR__"query_limits.c"
+#include __DIR__"query_load_average.c"
+#include __DIR__"query_mud_port.c"
+#include __DIR__"query_once_interactive.c"
+#include __DIR__"query_snoop.c"
+#include __DIR__"set_heart_beat.c"
+#if __BOOT_TIME__ < 1456261859
+#include __DIR__"set_prompt.c"
+#endif
+#include __DIR__"shadow.c"
+#include __DIR__"livings.c"
+#include __DIR__"comm.c"
+
+#define TO        efun::this_object()
+#define TI        efun::this_interactive()
+#define TP        efun::this_player()
+#define PO        efun::previous_object(0)
+#define LEVEL(x) query_wiz_level(x)
+#define NAME(x)  capitalize(getuid(x))
+
+#define DEBUG(x) if (find_player("zesstra")) \
+  tell_object(find_player("zesstra"),x)
+
+mixed dtime_cache = ({-1,""});
+
+#ifdef IOSTATS
+struct iostat_s {
+  string oname;
+  int time;
+  int size;
+};
+mixed saveo_stat = ({ 0,allocate(200, 0) });
+mixed restoreo_stat = ({ 0,allocate(200,0) });
+//mixed writefile_stat = ({ 0,allocate(100,(<iostat_s>)) });
+//mixed readfile_stat = ({ 0,allocate(100,(<iostat_s>)) });
+//mixed log_stat = ({ 0,allocate(100,(<iostat_s>)) });
+
+mixed ___iostats(int type) {
+  switch(type) {
+    case 1:
+      return saveo_stat;
+    case 2:
+      return restoreo_stat;
+/*    case 3:
+      return writefile_stat;
+    case 4:
+      return readfile_stat;
+    case 5:
+      return log_stat;
+      */
+  }
+  return 0;
+}
+#endif
+
+// Nicht jeder Magier muss die simul_efun entsorgen koennen.
+string NotifyDestruct(object caller) {
+    if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
+      return "Du darfst das simul_efun Objekt nicht zerstoeren!\n";
+    }
+    return 0;
+}
+
+public nomask void remove_interactive( object ob )
+{
+    if ( objectp(ob) && previous_object()
+        && object_name(previous_object())[0..7] != "/secure/"
+        && ((previous_object() != ob
+             && (ob != this_player() || ob != this_interactive()))
+            || (previous_object() == ob
+               && (this_player() && this_player() != ob
+                   || this_interactive() && this_interactive() != ob)) ) )
+
+       log_file( "PLAYERDEST",
+                sprintf( "%s: %O ausgeloggt von PO %O, TI %O, TP %O\n",
+                        dtime(time()), ob, previous_object(),
+                        this_interactive(), this_player() ) );
+
+    efun::remove_interactive(ob);
+}
+
+
+void ___updmaster()
+{
+    object ob;
+
+    //sollte nicht jeder duerfen.
+    if (process_call() || !ARCH_SECURITY)
+      raise_error("Illegal use of ___updmaster()!");
+
+    write("Removing old master ... ");
+    foreach(string file: 
+        get_dir("/secure/master/*.c",GETDIR_NAMES|GETDIR_UNSORTED|GETDIR_PATH)) {
+      if (ob = find_object(file))
+       efun::destruct(ob);
+    }
+    efun::destruct(efun::find_object("/secure/master"));
+    write("done.\nLoading again ... ");
+    load_object("/secure/master");
+
+    write("done.\n");
+}
+
+varargs string country(mixed ip, string num) {
+  mixed ret;
+
+  if(ret = (string)"/p/daemon/iplookup"->country(num || ip)) {
+    return ret;
+  } else return "???";
+}
+
+
+// * Snoopen und was dazugehoert
+static object find_snooped(object who)
+{
+  object *u;
+  int i;
+
+  for (i=sizeof(u=users())-1;i>=0;i--)
+    if (who==efun::interactive_info(u[i], II_SNOOP_NEXT))
+      return u[i];
+  return 0;
+}
+
+private string Lcut(string str) {
+  return str[5..11]+str[18..];
+}
+
+nomask varargs int snoop( object me, object you )
+{
+    int ret;
+    object snooper0, snooper, snooper2, snooper3;
+
+    if( !objectp(me) || me == you || !PO )
+       return 0;
+
+    snooper0 = find_snooped(me);
+
+     if(you){
+        if ( PO != me && query_wiz_grp(me) >= query_wiz_grp(geteuid(PO)) )
+            return 0;
+
+        if ( query_wiz_grp(me) <= query_wiz_grp(you) &&
+             !(you->QueryAllowSnoop(me)) )
+            if ( !IS_DEPUTY(me) || IS_ARCH(you) )
+               return 0;
+
+        if ( (snooper = efun::interactive_info(you, II_SNOOP_NEXT)) &&
+             query_wiz_grp(snooper) >= query_wiz_grp(me) ){
+            if ( (int)snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
+               return 0;
+
+            tell_object( snooper, sprintf( "%s snooped jetzt %s.\n",
+                                       me->name(WER), you->name(WER) ) );
+
+            snooper2 = me;
+
+            while ( snooper3 = interactive_info(snooper2, II_SNOOP_NEXT) ){
+               tell_object( snooper,
+                           sprintf( "%s wird seinerseits von %s gesnooped.\n"
+                                   ,snooper2->name(WER),
+                                   snooper3->name(WEM) ) );
+               snooper2 = snooper3;
+            }
+
+            efun::snoop( snooper, snooper2 );
+
+            if ( efun::interactive_info(snooper2, II_SNOOP_NEXT) != snooper )
+               tell_object( snooper, sprintf( "Du kannst %s nicht snoopen.\n",
+                                          snooper2->name(WEN) ) );
+            else{
+               tell_object( snooper, sprintf( "Du snoopst jetzt %s.\n",
+                                          snooper2->name(WEN) ) );
+               if ( !IS_DEPUTY(snooper) ){
+                   log_file( SNOOPLOGFILE, sprintf("%s: %O %O %O\n",
+                                               dtime(time()),
+                                               snooper,
+                                               snooper2,
+                                               environment(snooper2) ),
+                            100000 );
+                   if (snooper0)
+                      CHMASTER->send( "Snoop", snooper,
+                                    sprintf( "%s *OFF* %s (%O)",
+                                            capitalize(getuid(snooper)),
+                                            capitalize(getuid(snooper0)),
+                                            environment(snooper0) ) );
+
+                   CHMASTER->send( "Snoop", snooper,
+                                 sprintf("%s -> %s (%O)",
+                                        capitalize(getuid(snooper)),
+                                        capitalize(getuid(snooper2)),
+                                        environment(snooper2)));
+               }
+               else{
+                   log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                                 dtime(time()),
+                                                 snooper,
+                                                 snooper2,
+                                                 environment(snooper2) )
+                            ,100000 );
+               }
+            }
+        }
+        else
+            if (snooper)
+               if ( !me->QueryProp(P_SNOOPFLAGS) & SF_LOCKED ){
+                   printf( "%s wird bereits von %s gesnooped. Benutze das "
+                          "\"f\"-Flag, wenn du dennoch snoopen willst.\n",
+                          you->name(WER), snooper->name(WEM) );
+                   return 0;
+               }
+
+        ret = efun::snoop( me, you );
+
+        if ( !IS_DEPUTY(me) && efun::interactive_info(you, II_SNOOP_NEXT) == me){
+            log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                         Lcut(dtime(time())),
+                                         me, you, environment(you) ),
+                     100000 );
+
+            if (snooper0)
+               CHMASTER->send( "Snoop", me,
+                             sprintf( "%s *OFF* %s (%O).",
+                                     capitalize(getuid(me)),
+                                     capitalize(getuid(snooper0)),
+                                     environment(snooper0) ) );
+
+            CHMASTER->send( "Snoop", me, sprintf( "%s -> %s (%O).",
+                                             capitalize(getuid(me)),
+                                             capitalize(getuid(you)),
+                                             environment(you) ) );
+        }
+        else{
+            if ( efun::interactive_info(you, II_SNOOP_NEXT) == me ){
+               log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                             Lcut(dtime(time())),
+                                             me, you, environment(you) ),
+                        100000 );
+            }
+        }
+
+        if ( ret && query_wiz_grp(me) <= query_wiz_grp(you) &&
+             !IS_DEPUTY(me) )
+            tell_object( you, "*** " + NAME(me) + " snoopt Dich!\n" );
+
+        return ret;
+     }
+     else {
+        if ( (me == PO ||
+              query_wiz_grp(geteuid(PO)) > query_wiz_grp(me) ||
+              (query_wiz_grp(geteuid(PO)) == query_wiz_grp(me) &&
+              efun::interactive_info(PO, II_SNOOP_NEXT) == me)) && snooper0 ){
+            if ( !IS_DEPUTY(me) ){
+               log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
+                                            Lcut(dtime(time())), me,
+                                            snooper0,
+                                            environment(snooper0) ),
+                        100000 );
+
+                CHMASTER->send( "Snoop", me,
+                              sprintf( "%s *OFF* %s (%O).",
+                                      capitalize(getuid(me)),
+                                      capitalize(getuid(snooper0)),
+                                      environment(snooper0) ) );
+            }
+            else{
+               log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
+                                             Lcut(dtime(time())), me,
+                                             snooper0,
+                                             environment(snooper0) ),
+                        100000 );
+            }
+
+            return efun::snoop(me);
+        }
+     }
+     return 0;
+}
+
+
+
+// * Emulation des 'alten' explode durch das neue
+string *old_explode(string str, string del) {
+  int s, t;
+  string *strs;
+
+  if (!stringp(str)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Invalid argument 1 to old_explode()! Expected <string>, got: "
+         "%.30O\n",str));
+  }
+  if (!stringp(del)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Invalid argument 2 to old_explode()! Expected <string>, got: "
+         "%.30O\n",del));
+  }
+  if(del == "")
+    return ({str});
+  strs=efun::explode(str, del);
+  t=sizeof(strs)-1;
+  while(s<=t && strs[s++] == "");s--;
+  while(t>=0 && strs[t--] == "");t++;
+  if(s<=t)
+    return strs[s..t];
+  return ({});
+}
+
+int file_time(string path) {
+  mixed *v;
+
+  set_this_object(previous_object());
+  if (sizeof(v=get_dir(path,GETDIR_DATES))) return v[0];
+  return(0); //sonst
+}
+
+// * Bei 50k Groesse Log-File rotieren
+varargs int log_file(string file, string txt, int size_to_break) {
+  mixed *st;
+
+  file="/log/"+file;
+  file=implode((efun::explode(file,"/")-({".."})),"/");
+//  tell_object(find_player("jof"),sprintf("LOG FILE: %O -> %O\n",previous_object(),file));
+  if (!funcall(bind_lambda(#'efun::call_other,PO),"secure/master",//')
+              "valid_write",file,geteuid(PO),"log_file",PO))
+      return 0;
+  if ( size_to_break >= 0 & (
+      sizeof(st = get_dir(file,2) ) && st[0] >= (size_to_break|MAX_LOG_SIZE)))
+      catch(rename(file, file + ".old");publish); /* No panic if failure */
+  return(write_file(file,txt));
+}
+
+// * Magier-Level abfragen
+int query_wiz_level(mixed player) {
+  return (int)"/secure/master"->query_wiz_level(player);
+}
+
+#ifdef __ALISTS__
+// * Element aus Alist loeschen (by key)
+mixed *remove_alist(mixed key,mixed *alist)
+{
+  int i,j;
+
+  if (!pointerp(alist) || !sizeof(alist))
+    return alist;
+  if (!pointerp(alist[0]))
+  {
+    if ((i=assoc(key,alist))<0)
+      return alist;
+    return alist[0..i-1]+alist[i+1..];
+  }
+  i = assoc(key,alist[0]);
+  if ((i=assoc(key,alist[0]))<0)
+    return alist;
+  alist=alist[0..];
+  for (j=sizeof(alist)-1;j>=0;j--)
+    alist[j]=alist[j][0..i-1]+alist[j][i+1..];
+  return alist;
+}
+
+// * Element aus Alist loeschen (by pos)
+mixed *exclude_alist(int i,mixed *alist)
+{
+  int j;
+  if (!pointerp(alist) || !sizeof(alist) || i<0)
+    return alist;
+  if (!pointerp(alist[0]))
+    return alist[0..i-1]+alist[i+1..];
+  alist=alist[0..]; /* Create PHYSICAL copy of alist */
+  for (j=sizeof(alist)-1;j>=0;j--)
+    alist[j]=alist[j][0..i-1]+alist[j][i+1..];
+  return alist; /* order_alist is NOT necessary - see /doc/LPC/alist */
+}
+#endif // __ALISTS__
+
+// * German version of ctime()
+#define TAGE ({"Son","Mon","Die","Mit","Don","Fre","Sam"})
+#define MONATE ({"Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug",\
+                 "Sep","Okt","Nov","Dez"})
+string dtime(int wann) {
+  
+  if (wann == dtime_cache[0])
+    return(dtime_cache[1]);
+
+  int *lt = localtime(wann);
+  return sprintf("%s, %2d. %s %d, %02d:%02d:%02d",
+      TAGE[lt[TM_WDAY]], lt[TM_MDAY], MONATE[lt[TM_MON]], 
+      lt[TM_YEAR],lt[TM_HOUR], lt[TM_MIN], lt[TM_SEC]);
+}
+
+// wenn strftime im Driver nicht existiert, ist dies hier ein Alias auf dtime(),
+// zwar stimmt das Format dann nicht, aber die Mudlib buggt nicht und schreibt
+// ein ordentliches Datum/Uhrzeit.
+#if !__EFUN_DEFINED__(strftime)
+varargs string strftime(mixed fmt, int clock, int localized) {
+  if (intp(clock) && clock >= 0)
+    return dtime(clock);
+  else if (intp(fmt) && fmt >= 0)
+    return dtime(fmt);
+  
+  return dtime(time());
+}
+#endif //!__EFUN_DEFINED__(strftime)
+
+// * Shutdown mit zusaetzlichem logging
+nomask int shutdown(string reason)
+{
+  string name;
+  string obname;
+  string output;
+
+  if (!reason)
+    return 0;
+  if ( !ARCH_SECURITY && getuid(previous_object())!=ROOTID &&
+          object_name(previous_object())!="/obj/shut" )
+  {
+    write("You have no permission to shut down the gamedriver!\n");
+    return 0;
+  }
+  if ((this_interactive())&&(name=getuid(this_interactive())))
+  {
+    name=capitalize(name);
+    filter(users(),#'tell_object,//'
+               capitalize(name)+" faehrt das Spiel herunter!\n");
+  }
+  else
+    name="ANONYMOUS";
+  if (previous_object()) obname=capitalize(getuid(previous_object()));
+  output=name;
+  if (obname && name!=obname) output=output+" ("+obname+")";
+  if (previous_object()&&object_name(previous_object())=="/obj/shut"){
+    output+=" faehrt das Spiel via Armageddon herunter.\n";
+    output=dtime(time())+": "+output;
+    log_file("GAME_LOG",output+"\n",-1);
+    efun::shutdown();
+    return 1;
+  }
+  output=ctime(time())+": "+output+" faehrt das Spiel herunter.\n";
+  output+="    Grund: "+reason;
+  log_file("GAME_LOG",output+"\n",-1);
+  efun::shutdown();
+  return 1;
+}
+
+// * lowerchar
+
+int lowerchar(int char) {
+  if (char<'A' || char>'Z') return char;
+  return char+32;
+}
+
+// * upperstring
+
+string upperstring(string s)
+{
+#if __EFUN_DEFINED__(upper_case)
+  return(upper_case(s));
+#else
+  int i;
+  if (!stringp(s)) return 0;
+  for (i=sizeof(s)-1;i>=0;i--) s[i]=((s[i]<'a'||s[i]>'z')?s[i]:s[i]-32);
+  return s;
+#endif
+}
+
+// * lowerstring
+
+string lowerstring(string s)
+{
+  if (!stringp(s)) return 0;
+  return lower_case(s);
+}
+
+
+// * GD version
+string version()
+{
+  return __VERSION__;
+}
+
+// * break_string
+// stretch() -- stretch a line to fill a given width 
+private string stretch(string s, int width) {
+  int len=sizeof(s);
+  if (len==width) return s;
+
+  // reine Leerzeilen, direkt zurueckgeben
+  string trimmed=trim(s,TRIM_LEFT," ");
+  if (trimmed=="") return s; 
+  int start_spaces = len - sizeof(trimmed);
+
+  string* words = explode(trimmed, " ");
+  // der letzte kriegt keine Spaces
+  int word_count=sizeof(words) - 1;
+  // wenn Zeile nur aus einem Wort, wird das Wort zurueckgegeben
+  if (!word_count)
+    return " "*start_spaces + words[0];
+
+  int space_count = width - len;
+
+  int space_per_word=(word_count+space_count) / word_count;
+  // Anz.Woerter mit Zusatz-Space
+  int rest=(word_count+space_count) % word_count; 
+  // Rest-Spaces Verteilen
+  foreach (int pos : rest) words[pos]+=" ";
+  return (" "*start_spaces) + implode( words, " "*space_per_word );
+}
+
+// aus Geschwindigkeitsgruenden hat der Blocksatz fuer break_string eine
+// eigene Funktion bekommen:
+private varargs string block_string(string s, int width, int flags) {
+  // wenn BS_LEAVE_MY_LFS, aber kein BS_NO_PARINDENT, dann werden Zeilen mit
+  // einem Leerzeichen begonnen.
+  // BTW: Wenn !BS_LEAVE_MY_LFS, hat der Aufrufer bereits alle \n durch " "
+  // ersetzt.
+  if ( (flags & BS_LEAVE_MY_LFS)
+      && !(flags & BS_NO_PARINDENT))
+  {
+      s = " "+regreplace(s,"\n","\n ",1);
+  }
+
+  // sprintf fuellt die letzte Zeile auf die Feldbreite (hier also
+  // Zeilenbreite) mit Fuellzeichen auf, wenn sie NICHT mit \n umgebrochen
+  // ist. Es wird an die letzte Zeile aber kein Zeilenumbruch angehaengt.
+  // Eigentlich ist das Auffuellen doof, aber vermutlich ist es unnoetig, es
+  // wieder rueckgaengig zu machen.
+  s = sprintf( "%-*=s", width, s);
+
+  string *tmp=explode(s, "\n");
+  // Nur wenn s mehrzeilig ist, Blocksatz draus machen. Die letzte Zeile wird
+  // natuerlich nicht gedehnt. Sie ist dafuer schon von sprintf() aufgefuellt
+  // worden. BTW: Die letzte Zeile endet u.U. noch nicht mit einem \n (bzw.
+  // nur dann, wenn BS_LEAVE_MY_LFS und der uebergebene Text schon nen \n am
+  // Ende der letzten Zeile hat), das macht der Aufrufer...
+  if (sizeof(tmp) > 1)
+    return implode( map( tmp[0..<2], #'stretch/*'*/, width ), "\n" ) 
+      + "\n" + tmp[<1];
+
+  return s;
+}
+
+public varargs string break_string(string s, int w, mixed indent, int flags)
+{
+    if ( !s || s == "" ) return "";
+
+    if ( !w ) w=78;
+
+    if( intp(indent) )
+       indent = indent ? " "*indent : "";
+
+    int indentlen=stringp(indent) ? sizeof(indent) : 0;
+
+    if (indentlen>w) {
+      set_this_object(previous_object());
+      raise_error(sprintf("break_string: indent longer %d than width %d\n",
+                  indentlen,w));
+      // w=((indentlen/w)+1)*w;
+    }
+
+    if (!(flags & BS_LEAVE_MY_LFS)) 
+      s=regreplace( s, "\n", " ", 1 );
+
+    if ( flags & BS_SINGLE_SPACE )
+       s = regreplace( s, "(^|\n| )  *", "\\1", 1 );
+ 
+    string prefix="";
+    if (indentlen && flags & BS_PREPEND_INDENT) {
+      if (indentlen+sizeof(s) > w || 
+         (flags & BS_LEAVE_MY_LFS) && strstr(s,"\n")>-1) {
+       prefix=indent+"\n";
+       indent=(flags & BS_NO_PARINDENT) ? "" : " ";
+       indentlen=sizeof(indent);
+      }
+    }
+
+    if ( flags & BS_BLOCK ) {
+      /*
+           s = implode( map( explode( s, "\n" ),
+                               #'block_string, w, indentlen, flags),
+                      "" );
+      */
+      s = block_string( s , w - indentlen, flags );
+    }
+    else {
+      s = sprintf("%-1.*=s",w-indentlen,s);
+    }
+    if ( s[<1] != '\n' ) s += "\n";
+
+    if ( !indentlen ) return prefix + s;
+    
+    string indent2 = ( flags & BS_INDENT_ONCE ) ? (" "*indentlen) :indent;
+      
+    return prefix + indent + 
+      regreplace( s[0..<2], "\n", "\n"+indent2, 1 ) + "\n";
+      /*
+       string *buf;
+
+       buf = explode( s, "\n" );
+       return prefix + indent + implode( buf[0..<2], "\n"+indent2 ) + buf[<1] + "\n";
+      */
+}
+
+// * Elemente aus mapping loeschen - mapping vorher kopieren
+
+mapping m_copy_delete(mapping m, mixed key) {
+  return m_delete(copy(m), key);
+}
+
+// * times
+int last_reboot_time()
+{
+  return __BOOT_TIME__;
+}
+
+int first_boot_time()
+{
+  return 701517600;
+}
+
+int exist_days()
+{
+  return (((time()-first_boot_time())/8640)+5)/10;
+}
+
+// * uptime :)
+string uptime()
+{
+  int t;
+  int tmp;
+  string s;
+
+  t=time()-__BOOT_TIME__;
+  s="";
+  if (t>=86400)
+    s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
+  if (t>=3600)
+    s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
+  if (t>60)
+    s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
+  return s+sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
+}
+
+// * Was tun bei 'dangling' lfun-closures ?
+void dangling_lfun_closure() {
+  raise_error("dangling lfun closure\n");
+}
+
+// * Sperren ausser fuer master/simul_efun
+
+#if __EFUN_DEFINED__(set_environment)
+nomask void set_environment(object o1, object o2) {
+  raise_error("Available only for root\n");
+}
+#endif
+
+nomask void set_this_player(object pl) {
+  raise_error("Available only for root\n");
+}
+
+#if __EFUN_DEFINED__(export_uid)
+nomask void export_uid(object ob) {
+  raise_error("Available only for root\n");
+}
+#endif
+
+// * Jetzt auch closures
+int process_flag;
+
+public nomask int process_call()
+{
+  if (process_flag>0)
+    return process_flag;
+  else return(0);
+}
+
+private nomask string _process_string(string str,object po) {
+              set_this_object(po);
+              return(efun::process_string(str));
+}
+
+nomask string process_string( mixed str )
+{
+  string tmp, err;
+  int flag; 
+
+  if ( closurep(str) ) {
+      set_this_object( previous_object() );
+      return funcall(str);
+  }
+  else if (str==0)
+      return((string)str);
+  else if ( !stringp(str) ) {
+      return to_string(str);
+  }
+
+  if ( !(flag = process_flag > time() - 60))                     
+      process_flag=time();
+
+  err = catch(tmp = funcall(#'_process_string,str,previous_object()); publish);
+
+  if ( !flag )
+    process_flag=0;
+
+  if (err) {
+    // Verarbeitung abbrechen
+    set_this_object(previous_object());
+    raise_error(err);
+  }
+  return tmp;
+}
+
+// 'mkdir -p' - erzeugt eine komplette Hierarchie von Verzeichnissen.
+// wenn das Verzeichnis angelegt wurde oder schon existiert, wird 1
+// zurueckgeliefert, sonst 0.
+// Wirft einen Fehler, wenn das angebene Verzeichnis nicht absolut ist!
+public int mkdirp(string dir) {
+  // wenn es nur keinen fuehrenden / gibt, ist das ein Fehler.
+  if (strstr(dir, "/") != 0)
+    raise_error("mkdirp(): Pfad ist nicht absolute.\n");
+  // cut off trailing /...
+  if (dir[<1]=='/')
+      dir = dir[0..<2];
+
+  int fstat = file_size(dir);
+  // wenn es schon existiert, tun wir einfach so, als haetten wir es angelegt.
+  if (fstat == FSIZE_DIR)
+    return 1;
+  // wenn schon ne Datei existiert, geht es nicht.
+  if (fstat != FSIZE_NOFILE)
+    return 0;
+  // wenn es nur einen / gibt (den fuehrenden), dann ist es ein
+  // toplevel-verzeichnis, was direkt angelegt wird.
+  if (strrstr(dir,"/")==0) {
+    return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
+  }
+
+  // mkdir() nicht direkt rufen, sondern vorher als closure ans aufrufende
+  // Objekt binden. Sonst laeuft die Rechtepruefung in valid_write() im Master
+  // unter der Annahme, dass die simul_efun.c mit ihrer root id was will.
+
+  // jetzt rekursiv die Verzeichnishierarchie anlegen. Wenn das erfolgreich
+  // ist, dann koennen wir jetzt mit mkdir das tiefste Verzeichnis anlegen
+  if (mkdirp(dir[0..strrstr(dir,"/")-1]) == 1)
+    return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
+}
+
+
+// * Properties ggfs. mitspeichern
+mixed save_object(mixed name)
+{
+  mapping properties;
+  mapping save;
+  mixed index, res;
+  int i;
+
+  // nur Strings und 0 zulassen
+  if ((!stringp(name) || !sizeof(name)) && 
+      (!intp(name) || name!=0)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Only non-empty strings and 0 may be used as filename in "
+         "sefun::save_object()! Argument was %O\n",name));
+  }
+
+  save = m_allocate(0, 2);
+  properties = (mapping)previous_object()->QueryProperties();
+
+  if(mappingp(properties))
+  {
+    // delete all entries in mapping properties without SAVE flag!
+    index = m_indices(properties);
+    for(i = sizeof(index)-1; i>=0;i--)
+    {
+      if(properties[index[i], F_MODE] & SAVE)
+      {
+       save[index[i]] = properties[index[i]];
+       save[index[i], F_MODE] =
+       properties[index[i], F_MODE] &
+                    (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED));
+      }
+    }
+  }
+  else save = ([]);
+
+  // save object!
+  previous_object()->_set_save_data(save);
+  // format: wie definiert in config.h
+  if (stringp(name))
+    res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
+       __LIB__SAVE_FORMAT_VERSION__);
+  else
+    res = funcall(bind_lambda(#'efun::save_object, previous_object()),
+       __LIB__SAVE_FORMAT_VERSION__);
+  previous_object()->_set_save_data(0);
+
+#ifdef IOSTATS
+  // Stats...
+  struct iostat_s stat = (<iostat_s>);
+  stat->oname = object_name(previous_object());
+  stat->time = time();
+  //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
+  //    OIM_TOTAL_DATA_SIZE);
+  if (stringp(name))
+      stat->size = file_size(name + ".o");
+  else
+      stat->sizeof(res);
+  //debug_message("saveo: "+saveo_stat[0]+"\n");
+  saveo_stat[1][saveo_stat[0]] = stat;
+  saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
+  //debug_message("saveo 2: "+saveo_stat[0]+"\n");
+#endif
+
+  return res;
+}
+
+// * Auch Properties laden
+int restore_object(string name)
+{
+  int result;
+  mixed index;
+  mixed save;
+  mapping properties;
+  int i;
+  closure cl;
+
+  // get actual property settings (by create())
+  properties = (mapping)previous_object()->QueryProperties();
+
+//  DEBUG(sprintf("RESTORE %O\n",name));
+  // restore object
+  result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
+  //'))
+  //_get_save_data liefert tatsaechlich mixed zurueck, wenn das auch immer ein 
+  //mapping sein sollte.
+  save = (mixed)previous_object()->_get_save_data();
+  if(mappingp(save))
+  {
+    index = m_indices(save);
+    for(i = sizeof(index)-1; i>=0; i--)
+    {
+      properties[index[i]] = save[index[i]];
+      properties[index[i], F_MODE] = save[index[i], F_MODE]
+                            &~(SETCACHED|QUERYCACHED);
+    }
+  }
+  else properties = ([]);
+
+  // restore properties
+  funcall(
+          bind_lambda(
+                     unbound_lambda(({'arg}), //'})
+                                  ({#'call_other,({#'this_object}),
+                                  "SetProperties",'arg})),//')
+                     previous_object()),properties);
+  previous_object()->_set_save_data(0);
+
+#ifdef IOSTATS
+  // Stats...
+  //debug_message("restoreo: "+restoreo_stat[0]+"\n");
+  struct iostat_s stat = (<iostat_s>);
+  stat->oname = object_name(previous_object());
+  stat->time = time();
+  //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
+  //    OIM_TOTAL_DATA_SIZE);
+  stat->size = file_size(name + ".o");
+  restoreo_stat[1][restoreo_stat[0]] = stat;
+
+  restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
+#endif
+
+  return result;
+}
+
+// * HB eines Objektes ein/ausschalten
+int set_object_heart_beat(object ob, int flag)
+{
+  if (objectp(ob))
+    return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
+}
+
+// * Magierlevelgruppen ermitteln
+int query_wiz_grp(mixed wiz)
+{
+  int lev;
+
+  lev=query_wiz_level(wiz);
+  if (lev<SEER_LVL) return 0;
+  if (lev>=GOD_LVL) return lev;
+  if (lev>=ARCH_LVL) return ARCH_GRP;
+  if (lev>=ELDER_LVL) return ELDER_GRP;
+  if (lev>=LORD_LVL) return LORD_GRP;
+  if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
+  if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
+  if (lev>=WIZARD_LVL) return WIZARD_GRP;
+  if (lev>=LEARNER_LVL) return LEARNER_GRP;
+  return SEER_GRP;
+}
+
+mixed *wizlist_info()
+{
+  if (ARCH_SECURITY || !extern_call())
+            return efun::wizlist_info();
+  return 0;
+}
+
+// * wizlist ausgeben
+varargs void wizlist(string name, int sortkey ) {
+
+  if (!name)
+  {
+    if (this_player())
+      name = getuid(this_player());
+    if (!name)
+      return;
+  }
+
+  // Schluessel darf nur in einem gueltigen Bereich sein
+  if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
+
+  mixed** wl = efun::wizlist_info();
+  // nach <sortkey> sortieren
+  wl = sort_array(wl, function int (mixed a, mixed b)
+      {return a[sortkey] < b[sortkey]; } );
+
+  // Summe ueber alle Kommandos ermitteln.
+  int total_cmd, i;
+  int pos=-1;
+  foreach(mixed entry : wl)
+  {
+    total_cmd += entry[WL_COMMANDS];
+    if (entry[WL_NAME] == name)
+      pos = i;
+    ++i;
+  }
+
+  if (pos < 0 && name != "ALL" && name != "TOP100")
+    return;
+
+  if (name == "TOP100")
+  {
+    if (sizeof(wl) > 100)
+      wl = wl[0..100];
+    else
+      wl = wl;
+  }
+  // um name herum schneiden
+  else if (name != "ALL")
+  {
+    if (sizeof(wl) <= 21)
+      wl = wl;
+    else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
+      wl = wl[pos-10..pos+10];
+    else if (pos <=21)
+      wl = wl[0..20];
+    else if (pos >= sizeof(wl) - 21)
+      wl = wl[<21..];
+    else
+      wl = wl;
+  }
+
+  write("\nWizard top score list\n\n");
+  if (total_cmd == 0)
+    total_cmd = 1;
+  printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
+         "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
+  foreach(mixed e: wl)
+  {
+    printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
+          e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
+          e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
+          e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
+          );
+  }
+  printf("\nTotal         %7d         (%d)\n\n", total_cmd, sizeof(wl));
+}
+
+
+// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
+
+// ermittelt das Objekt des Callouts.
+private object _call_out_obj( mixed call_out ) {
+    return pointerp(call_out) ? call_out[0] : 0;
+}
+
+private void _same_object( object ob, mapping m ) {
+  // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
+  if ( m[ob] )
+    m[ob] += ({ ob });
+  else
+    m[ob] = ({ ob }); 
+}
+
+// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
+// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
+// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
+private void _same_path( object key, object *obs, mapping m ) {
+  string path;
+  if (!objectp(key) || !pointerp(obs)) return;
+
+  path = load_name(key);
+
+  if ( m[path] )
+    m[path] += obs;
+  else
+    m[path] = obs;
+}
+
+// key kann object oder string sein.
+private int _too_many( mixed key, mapping m, int i ) {
+    return sizeof(m[key]) >= i;
+}
+
+// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
+// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
+// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
+private void _destroy( mixed key, object *obs, string text, int uid ) {
+    if (!pointerp(obs)) return;
+    // Array mit unique Eintraege erzeugen.
+    obs = m_indices( mkmapping(obs) );
+    // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
+    // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
+    // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
+    // laeuft.
+    catch( efun::raise_error(           
+         sprintf( text,                   
+           uid ? (string)master()->creator_file(key) : key,                   
+           sizeof(obs), object_name(obs[<1]) ) );publish);
+    // Und weg mit dem Kram...
+    filter( obs, #'efun::destruct/*'*/ );
+}
+
+// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
+// sind dabei nicht unique.
+private void _same_uid( string key, object *obs, mapping m, closure cf ) {
+  string uid;
+
+  if ( !pointerp(obs) || !sizeof(obs) )
+    return;
+
+  uid = funcall( cf, key );
+
+  if ( m[uid] )
+    m[uid] += obs; // obs ist nen Array
+  else
+    m[uid] = obs;
+}
+
+private int _log_call_out(mixed co)
+{
+  log_file("TOO_MANY_CALLOUTS",
+      sprintf("%s::%O (%d)\n",object_name(co[0]),co[1],co[2]),
+      200000);
+  return 0;
+}
+
+private int last_callout_log=0;
+
+nomask varargs void call_out( varargs mixed *args )
+{
+    mixed tmp, *call_outs;
+
+    // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
+    // haben.
+    if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
+        && geteuid(previous_object()) != ROOTID )
+    {
+       // Log erzeugen...
+       if (last_callout_log <
+           efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES) - 10
+           && get_eval_cost() > 200000)
+       {
+         last_callout_log = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+         log_file("TOO_MANY_CALLOUTS",
+             sprintf("\n%s: ############ Too many callouts: %d ##############\n",
+                     strftime("%y%m%d-%H%M%S"),
+                     efun::driver_info(DI_NUM_CALLOUTS)));
+         filter(efun::call_out_info(), #'_log_call_out);
+       }
+       // Objekte aller Callouts ermitteln
+       call_outs = map( efun::call_out_info(), #'_call_out_obj );
+       mapping objectmap = ([]);
+       filter( call_outs, #'_same_object, &objectmap );
+       // Master nicht grillen...
+       efun::m_delete( objectmap, master(1) );
+       // alle Objekte raussuchen, die zuviele haben...
+       mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
+       // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
+       // Objekten uebergeben (in diesem Fall sind Keys und Array mit
+       // Objekten jeweils das gleiche Objekt).
+       if ( sizeof(res) )       
+           walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "             
+              "object [%O]. Destructed %d objects. [%s]\n", 0 );
+
+       // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
+       // Objekte killen, die mehr als 50 Callouts laufen haben - und
+       // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
+       // Clones einer BP gekillt, die Callouts laufen haben, falls alle
+       // diese Objekte _zusammen_ mehr als 50 Callouts haben!
+       if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
+           // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
+           // mehr drin, da sie dort als Keys verwendet wurden.
+           mapping pathmap=([]);
+           // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
+           // von Objekten als Werte.
+           walk_mapping( objectmap, #'_same_path, &pathmap);
+           // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
+           res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
+           // und ueber alle Keys gehen, an _destroy() werden die Clones
+           // uebergeben, die Callouts haben.
+           if ( sizeof(res) )
+              walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
+                           "'%s'. Destructed %d objects. [%s]\n", 0 );
+
+           // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
+           // noch >1000 Callouts laufen, werden diesmal alle Callouts
+           // einer UID zusammengezaehlt.
+           // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
+           // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
+           if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
+           {
+              // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
+              // zusammensortieren. Zerstoerte Clones filter _same_uid()
+              // raus.
+              mapping uidmap=([]);
+              walk_mapping( pathmap, #'_same_uid, &uidmap,
+                           symbol_function( "creator_file",
+                                          "/secure/master" ) );
+              // In res nun UIDs als Keys und Arrays von Objekten als Werte.
+              // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
+              // 100 Callouts!) haben.
+              res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
+              // und erneut ueber die Keys laufen und jeweils die Arrays mit
+              // den Objekten zur Zerstoerung an _destroy()...
+              if ( sizeof(res) )
+                  walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
+                              "UID '%s'. Destructed %d objects. [%s]\n",
+                              1 );
+           }
+       }
+    }
+
+    // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
+    if ( !previous_object() )
+       return;
+
+    set_this_object( previous_object() );
+    apply( #'efun::call_out, args );
+    return;
+}
+
+mixed call_out_info() {
+  
+  object po = previous_object();
+  mixed coi = efun::call_out_info();
+
+  // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
+  // /obj haben die BackboneID.
+  if (query_wiz_level(getuid(po)) >= ARCH_LVL
+       || (string)master()->creator_file(load_name(po)) == BACKBONEID ) {
+      return coi;
+  }
+  else {
+      return filter(coi, function mixed (mixed arr) {
+              if (pointerp(arr) && arr[0]==po)
+                 return 1;
+              else return 0; });
+  }
+}
+
+// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
+// NICHT das Objekt, was ggf. die lfun definiert!
+mixed query_closure_object(closure c) {
+  return
+    CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
+      0 :
+  (to_object(c) || -1);
+}
+
+// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
+varargs void move_object(mixed what, mixed where)
+{
+  object po,tmp;
+
+  po=previous_object();
+  if (!where)
+  {
+    where=what;
+    what=po;
+  }
+  if (((stringp(where) && where==NETDEAD_ROOM ) ||
+       (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
+       objectp(what) && object_name(what)!="/obj/sperrer")
+  {
+    if (!query_once_interactive(what))
+    {
+      what->remove();
+      if (what) destruct(what);
+      return;
+    }
+    if (living(what) || interactive(what))
+    {
+      log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
+      return;
+    }
+    set_object_heart_beat(what,0);
+  }
+  tmp=what;
+  while (tmp=environment(tmp))
+      // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+      // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+      // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+      // da um jedes bisschen Rechenzeit geht.
+      // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+      //
+      // Tiamak
+    tmp->_set_last_content_change();
+  funcall(bind_lambda(#'efun::move_object,po),what,where);
+  if (tmp=what)
+    while (tmp=environment(tmp))
+      tmp->_set_last_content_change();
+}
+
+
+void start_simul_efun() {
+  mixed *info;
+
+  // Falls noch nicht getan, extra_wizinfo initialisieren
+  if ( !pointerp(info = get_extra_wizinfo(0)) )
+    set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
+
+  InitLivingData(info);
+
+  set_next_reset(10); // direkt mal aufraeumen
+}
+
+protected void reset() {
+  set_next_reset(7200);
+  CleanLivingData();
+}
+
+#if !__EFUN_DEFINED__(absolute_hb_count)
+int absolute_hb_count() {
+  return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+}
+#endif
+
+void __set_environment(object ob, mixed target)
+{
+  string path;
+  object obj;
+
+  if (!objectp(ob))
+    return;
+  if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
+    return;
+  if (objectp(target))
+  {
+    efun::set_environment(ob,target);
+    return;
+  }
+  path=(string)MASTER->_get_path(target,this_interactive());
+  if (stringp(path) && file_size(path+".c")>=0 &&
+      !catch(load_object(path);publish) )
+  {
+    obj=find_object(path);
+    efun::set_environment(ob,obj);
+    return;
+  }
+}
+
+void _dump_wizlist(string file, int sortby) {
+  int i;
+  mixed *a;
+
+  if (!LORD_SECURITY)
+    return;
+  if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
+  {
+    write("NO WRITE PERMISSION\n");
+    return;
+  }
+  a = wizlist_info();
+  a = sort_array(a, lambda( ({'a,'b}),
+                        ({#'<,
+                          ({#'[,'a,sortby}),
+                          ({#'[,'b,sortby})
+                         })));
+  rm(file);
+  for (i=sizeof(a)-1;i>=0;i--)
+    write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
+      a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
+      a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
+}
+
+public varargs object deep_present(mixed what, object ob) {
+
+  if(!objectp(ob))
+    ob=previous_object();
+  // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
+  // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
+  if(objectp(what)) {
+    object *envs=all_environment(what);
+    // wenn ob kein Environment hat, ist es offensichtlich nicht in what
+    // enthalten.
+    if (!pointerp(envs)) return 0;
+    if (member(envs, ob) != -1) return what;
+  }
+  // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
+  // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
+  // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
+  else if (stringp(what)) {
+      int cnt;
+      string newwhat;
+      if(sscanf(what,"%s %d",newwhat,cnt)!=2)
+       cnt=1;
+      else
+       what=newwhat;
+      foreach(object invob: deep_inventory(ob)) {
+       if (invob->id(what) && !--cnt)
+           return invob;
+      }
+  }
+  else {
+    set_this_object(previous_object());
+    raise_error(sprintf("Wrong argument 1 to deep_present(). "
+         "Expected \"object\" or \"string\", got %.50O.\n",
+         what));
+  }
+  return 0;
+}
+
+mapping dump_ip_mapping()
+{
+  return 0;
+}
+
+nomask void swap(object obj)
+{
+  write("Your are not allowed to swap objects by hand!\n");
+  return;
+}
+
+nomask varargs void garbage_collection(string str)
+{
+  if(previous_object()==0 || !IS_ARCH(geteuid(previous_object())) 
+      || !ARCH_SECURITY)
+  {
+    write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
+    return;
+  }
+  else if (stringp(str))
+  {
+    return efun::garbage_collection(str);
+  }
+  else 
+    return efun::garbage_collection();
+}
+
+varargs void notify_fail(mixed nf, int prio) {
+  object po,oldo;
+  int oldprio;
+  
+  if (!PL || !objectp(po=previous_object())) return;
+  if (!stringp(nf) && !closurep(nf)) {
+      set_this_object(po);
+      raise_error(sprintf(
+         "Only strings and closures allowed for notify_fail! "
+         "Argument was: %.50O...\n",nf));
+  }
+
+  // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
+  // und vergleichen.
+  if (objectp(oldo=query_notify_fail(1)) && po!=oldo) {
+    if (!prio) {       
+      //Prioritaet dieses notify_fail() 'abschaetzen'
+      if (po==PL) // Spieler-interne (soul-cmds)
+        prio=NF_NL_OWN;
+      else if (living(po))
+        prio=NF_NL_LIVING;
+      else if ((int)po->IsRoom())
+        prio=NF_NL_ROOM;
+      else
+        prio=NF_NL_THING;
+    }
+    //Prioritaet des alten Setzers abschaetzen
+    if (oldo==PL)
+      oldprio=NF_NL_OWN;
+    else if (living(oldo))
+      oldprio=NF_NL_LIVING;
+    else if ((int)oldo->IsRoom())
+      oldprio=NF_NL_ROOM;
+    else
+      oldprio=NF_NL_THING;
+  }
+  else // wenn es noch kein Notify_fail gibt:
+    oldprio=NF_NL_NONE;
+
+  //vergleichen und ggf. setzen
+  if (prio >= oldprio) { 
+    set_this_object(po);
+    efun::notify_fail(nf);
+  }
+
+  return;
+}
+
+void _notify_fail(string str)
+{
+  //query_notify_fail() benutzen, um das Objekt
+  //des letzten notify_fail() zu ermitteln
+  object o;
+  if ((o=query_notify_fail(1)) && o!=previous_object())
+    return;
+  //noch kein notify_fail() fuer dieses Kommando gesetzt, auf gehts.
+  set_this_object(previous_object());
+  efun::notify_fail(str);
+  return;
+}
+
+string time2string( string format, int zeit )
+{
+  int i,ch,maxunit,dummy;
+  string *parts, fmt;
+
+  int secs = zeit;
+  int mins = (zeit/60);
+  int hours = (zeit/3600);
+  int days = (zeit/86400);
+  int weeks =  (zeit/604800);
+  int months = (zeit/2419200);
+  int abbr = 0;
+
+  parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
+
+  for( i=1; i<sizeof(parts); i+=2 )
+  {
+    ch = parts[i][<1];
+    switch( parts[i][<1] )
+    {
+    case 'x': case 'X':
+       abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
+       // NO break !
+    case 'n': case 'N':
+       maxunit |= 31;
+       break;
+    case 'w': case 'W':
+       maxunit |= 15;
+       break;
+    case 'd': case 'D':
+       maxunit |= 7;
+       break;
+    case 'h': case 'H':
+       maxunit |= 3;
+       break;
+    case 'm': case 'M':
+       maxunit |= 1;
+       break;
+    }
+  }
+  if( maxunit & 16 ) weeks %= 4;
+  if( maxunit & 8 ) days %= 7;
+  if( maxunit & 4 ) hours %= 24;
+  if( maxunit & 2 ) mins %= 60;
+  if( maxunit ) secs %= 60;
+
+  for( i=1; i<sizeof(parts); i+=2 )
+  {
+    fmt = parts[i][0..<2];
+    ch = parts[i][<1];
+    if( ch=='x' )
+    {
+      if (months > 0) ch='n';
+      else if( weeks>0 ) ch='w';
+      else if( days>0 ) ch='d';
+      else if( hours>0 ) ch='h'; 
+      else if(mins > 0) ch = 'm';
+      else ch = 's';
+    }
+    else if( ch=='X' )
+    {
+      if (months > 0) ch='N';
+      else if( weeks>0 ) ch='W';
+      else if( days>0 ) ch='D';
+      else if( hours>0 ) ch='H'; 
+      else if(mins > 0) ch = 'M';
+      else ch = 'S';
+    }
+    
+    switch( ch )
+    {
+      case 'n': parts[i] = sprintf( fmt+"d", months ); break;
+      case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
+      case 'd': parts[i] = sprintf( fmt+"d", days ); break;
+      case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
+      case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
+      case 's': parts[i] = sprintf( fmt+"d", secs ); break;
+      case 'N':
+       if(abbr) parts[i] = "M";
+       else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
+       break;
+      case 'W':
+       if(abbr) parts[i] = "w"; else
+       parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
+       break;
+      case 'D':
+       if(abbr) parts[i] = "d"; else
+       parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
+       break;
+      case 'H':
+       if(abbr) parts[i] = "h"; else
+       parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
+       break;
+      case 'M':
+       if(abbr) parts[i] = "m"; else
+       parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
+       break;
+      case 'S':
+       if(abbr) parts[i] = "s"; else
+       parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
+       break;
+      case '%':
+       parts[i] = "%";
+       break;
+      }
+    }
+    return implode( parts, "" );
+}
+
+nomask mixed __create_player_dummy(string name)
+{
+  string err;
+  object ob;
+  mixed m;
+  //hat nen Scherzkeks die Blueprint bewegt?
+  if ((ob=find_object("/secure/login")) && environment(ob))
+      catch(destruct(ob);publish);
+  err = catch(ob = clone_object("secure/login");publish);
+  if (err)
+  {
+    write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
+    return 0;
+  }
+  if (objectp(m=(mixed)ob->new_logon(name))) netdead[name]=m;
+  return m;
+}
+
+nomask int secure_level()
+{
+  int *level;
+  //kette der Caller durchlaufen, den niedrigsten Level in der Kette
+  //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
+  //von 0.
+  //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
+  //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
+  //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
+  //INteractive geben muss.
+  level=map(caller_stack(1),function int (object caller)
+      {if (objectp(caller))
+       return(query_wiz_level(geteuid(caller)));
+       return(0); // kein Objekt da, 0.
+      } );
+  return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
+}
+
+nomask string secure_euid()
+{
+  string euid;
+
+  if (!this_interactive()) // Es muss einen interactive geben
+     return 0;
+  euid=geteuid(this_interactive());
+  // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
+  // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
+  // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
+  foreach(object caller: caller_stack()) {
+      if (!objectp(caller) ||
+       (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
+         return 0;
+  }
+  return euid; // 'sichere' euid zurueckgeben
+}
+
+// INPUT_PROMPT und nen Leerprompt hinzufuegen, wenn keins uebergeben wird.
+// Das soll dazu dienen, dass alle ggf. ein EOR am Promptende kriegen...
+//#if __BOOT_TIME__ < 1360017213
+varargs void input_to( mixed fun, int flags, varargs mixed *args )
+{
+    mixed *arr;
+    int i;
+
+    if ( !this_player() || !previous_object() )
+       return;
+
+    // TODO: input_to(...,INPUT_PROMPT, "", ...), wenn kein INPUT_PROMPT
+    // vorkommt...
+    if ( flags&INPUT_PROMPT ) {    
+        arr = ({ fun, flags }) + args;
+    }
+    else {
+        // ggf. ein INPUT_PROMPT hinzufuegen und nen Leerstring als Prompt.
+        flags |= INPUT_PROMPT;
+        arr = ({ fun, flags, "" }) + args;
+    }
+
+    // Arrays gegen flatten quoten.
+    for ( i = sizeof(arr) - 1; i > 1; i-- )
+       if ( pointerp(arr[i]) )
+           arr[i] = quote(arr[i]);
+
+    apply( bind_lambda( unbound_lambda( ({}),
+                                     ({ #'efun::input_to/*'*/ }) + arr ),
+                       previous_object() ) );
+}
+//#endif
+
+nomask int set_light(int i)
+// erhoeht das Lichtlevel eines Objekts um i
+// result: das Lichtlevel innerhalb des Objekts
+{
+    object ob, *inv;
+    int lall, light, dark, tmp;
+
+    if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
+
+    // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
+    if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
+
+    // Lichtberechnung findet eigentlich in der Mudlib statt.
+    return (int)ob->QueryProp(P_INT_LIGHT);
+}
+
+
+public string iso2ascii( string str )
+{
+    if ( !stringp(str) || !sizeof(str) )
+       return "";
+
+    str = regreplace( str, "ä", "ae", 1 );
+    str = regreplace( str, "ö", "oe", 1 );
+    str = regreplace( str, "ü", "ue", 1 );
+    str = regreplace( str, "Ä", "Ae", 1 );
+    str = regreplace( str, "Ö", "Oe", 1 );
+    str = regreplace( str, "Ü", "Ue", 1 );
+    str = regreplace( str, "ß", "ss", 1 );
+    str = regreplace( str, "[^ -~]", "?", 1 );
+
+    return str;
+}
+
+
+public varargs string CountUp( string *s, string sep, string lastsep )
+{
+    string ret;
+
+    if ( !pointerp(s) )
+       return "";
+    
+    if (!sep) sep = ", ";
+    if (!lastsep) lastsep = " und ";
+
+    switch (sizeof(s))  {
+       case 0: ret=""; break;
+       case 1: ret=s[0]; break;
+       default:
+              ret = implode(s[0..<2], sep);
+              ret += lastsep + s[<1];
+    }
+    return ret;
+}
+
+nomask varargs int query_next_reset(object ob) {
+
+    // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
+    if (ob && !objectp(ob))
+      raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
+           "expected object.\n",ob));
+
+    // Defaultobjekt PO, wenn 0 uebergeben.
+    if ( !objectp(ob) )
+      ob = previous_object();
+
+    return efun::object_info(ob, OI_NEXT_RESET_TIME);
+}
+
+
+#if !__EFUN_DEFINED__(copy_file)
+#define MAXLEN 50000
+nomask int copy_file(string source, string dest)
+{
+
+  int ptr;
+  string bytes;
+
+  set_this_object(previous_object());
+  if (!sizeof(source)||!sizeof(dest)||source==dest||(file_size(source)==-1)||
+      (!call_other(master(),"valid_read",source,
+                   getuid(this_interactive()||
+                 previous_object()),"read_file",previous_object()))||
+      (!call_other(master(),"valid_read",source,
+                   getuid(this_interactive()||
+                 previous_object()),"write_file",previous_object())))
+    return 1;
+  switch (file_size(dest))
+  {
+  case -1:
+    break;
+  case -2:
+    if (dest[<1]!='/') dest+="/";
+    dest+=efun::explode(source,"/")[<1];
+    if (file_size(dest)==-2) return 1;
+    if (file_size(dest)!=-1) break;
+  default:
+    if (!rm(dest)) return 1;
+    break;
+  }
+  do
+  {
+    bytes = read_bytes(source, ptr, MAXLEN); ptr += MAXLEN;
+    if (!bytes) bytes="";
+    write_file(dest, bytes);
+  }
+  while(sizeof(bytes) == MAXLEN);
+  return 0;
+}
+#endif //!__EFUN_DEFINED__(copy_file)
+
+
+// ### Ersatzaufloesung in Strings ###
+varargs string replace_personal(string str, mixed *obs, int caps) {
+  int i;
+  string *parts;
+
+  parts = regexplode(str, "@WE[A-SU]*[0-9]");
+  i = sizeof(parts);
+
+  if (i>1) {
+    int j, t;
+    closure *name_cls;
+
+    t = j = sizeof(obs);
+
+    name_cls  =  allocate(j);
+    while (j--)
+      if (objectp(obs[j]))
+        name_cls[j] = symbol_function("name", obs[j]);
+      else if (stringp(obs[j]))
+        name_cls[j] = obs[j];
+
+    while ((i-= 2)>0) {
+      int ob_nr;
+      // zu ersetzendes Token in Fall und Objektindex aufspalten
+      ob_nr = parts[i][<1]-'1';
+      if (ob_nr<0 || ob_nr>=t) {
+        set_this_object(previous_object());
+        raise_error(sprintf("replace_personal: using wrong object index %d\n",
+                    ob_nr));
+        return implode(parts, "");
+      }
+
+      // casus kann man schon hier entscheiden
+      int casus;
+      string part = parts[i];
+      switch (part[3]) {
+        case 'R': casus = WER;    break;
+        case 'S': casus = WESSEN; break;
+        case 'M': casus = WEM;    break;
+        case 'N': casus = WEN;    break;
+        default:  continue; // passt schon jetzt nicht in das Hauptmuster
+      }
+
+      // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
+      mixed tmp;
+      switch (part[3..<2]) {
+        case "R": case "SSEN": case "M": case "N":               // Name
+          parts[i] = funcall(name_cls[ob_nr], casus, 1);  break;
+        case "RU": case "SSENU": case "MU": case "NU":           // unbestimmt
+          parts[i] = funcall(name_cls[ob_nr], casus);     break;
+        case "RQP": case "SSENQP": case "MQP": case "NQP":       // Pronoun
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPronoun(casus);
+          break;
+        case "RQA": case "SSENQA": case "MQA": case "NQA":       // Article
+          if (objectp(tmp = obs[ob_nr]))
+            tmp = (string)tmp->QueryArticle(casus, 1, 1);
+          if (stringp(tmp) && !(tmp[<1]^' ')) 
+            tmp = tmp[0..<2];                // Extra-Space wieder loeschen
+          break;
+        case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, SINGULAR);
+          break;
+        case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
+          break;
+        case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
+          break;
+        case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, PLURAL);
+          break;
+        case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
+          break;
+        case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
+          break;
+        default:
+          continue;
+      }
+      
+      // wenn tmp ein String war, weisen wir es hier pauschal zu
+      if (stringp(tmp))
+        parts[i] = tmp;
+
+      // auf Wunsch wird nach Satzenden gross geschrieben
+      if (caps)
+        switch (parts[i-1][<2..]) {
+          case ". ":  case "! ":  case "? ":
+          case ".":   case "!":   case "?":
+          case ".\n": case "!\n": case "?\n":
+          case "\" ": case "\"\n":
+            parts[i] = capitalize(parts[i]);
+            break;
+        }
+    }
+    return implode(parts, "");
+  }
+  return str;
+}
+
+
+//replacements for dropped efuns in LD
+#if !__EFUN_DEFINED__(extract)
+deprecated varargs string extract(string str, int from, int to) {
+
+  if(!stringp(str)) {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to extract(): %O",str));
+  }
+  if (intp(from) && intp(to)) {
+    if (from>=0 && to>=0)
+      return(str[from .. to]);
+    else if (from>=0 && to<0)
+      return(str[from .. <abs(to)]);
+    else if (from<0 && to>=0)
+      return(str[<abs(from) .. to]);
+    else
+      return(str[<abs(from) .. <abs(to)]);
+  }
+  else if (intp(from)) {
+    if (from>=0)
+      return(str[from .. ]);
+    else
+      return(str[<abs(from) .. ]);
+  }
+  else {
+    return(str);
+  }
+}
+#endif // !__EFUN_DEFINED__(extract)
+
+#if !__EFUN_DEFINED__(slice_array)
+deprecated varargs mixed slice_array(mixed array, int from, int to) {
+
+  if(!pointerp(array)) {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
+  }
+  if (intp(from) && intp(to)) {
+    if (from>=0 && to>=0)
+      return(array[from .. to]);
+    else if (from>=0 && to<0)
+      return(array[from .. <abs(to)]);
+    else if (from<0 && to>=0)
+      return(array[<abs(from) .. to]);
+    else
+      return(array[<abs(from) .. <abs(to)]);
+  }
+  else if (intp(from)) {
+    if (from>=0)
+      return(array[from .. ]);
+    else
+      return(array[<abs(from) .. ]);
+  }
+  else {
+    return(array);
+  }
+}
+#endif // !__EFUN_DEFINED__(slice_array)
+
+#if !__EFUN_DEFINED__(member_array)
+deprecated int member_array(mixed item, mixed arraystring) {
+
+  if (pointerp(arraystring)) {
+    return(efun::member(arraystring,item));
+  }
+  else if (stringp(arraystring)) {
+    return(efun::member(arraystring,to_int(item)));
+  }
+  else {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
+  }
+}
+#endif // !__EFUN_DEFINED__(member_array)
+
+// The digit at the i'th position is the number of bits set in 'i'.
+string count_table =
+    "0112122312232334122323342334344512232334233434452334344534454556";
+int broken_count_bits( string s ) {
+    int i, res;
+    if( !stringp(s) || !(i=sizeof(s)) ) return 0;
+    for( ; i-->0; ) {
+        // We are counting 6 bits at a time using a precompiled table.
+        res += count_table[(s[i]-' ')&63]-'0';
+    }
+    return res;
+}
+
+#if !__EFUN_DEFINED__(count_bits)
+int count_bits( string s ) {
+    return(broken_count_bits(s));
+}
+#endif
+
+
+// * Teile aus einem Array entfernen *** OBSOLETE
+deprecated mixed *exclude_array(mixed *arr,int from,int to)
+{
+  if (to<from)
+    to = from;
+  return arr[0..from-1]+arr[to+1..];
+}
+
diff --git a/secure/simul_efun/spare/spare/README b/secure/simul_efun/spare/spare/README
new file mode 100644
index 0000000..303429a
--- /dev/null
+++ b/secure/simul_efun/spare/spare/README
@@ -0,0 +1,20 @@
+simul_efun 
+---------- 
+Das simul_efun Objekt /secure/simul_efun/simul_efun benutzt die Dateien
+in /secure/simul_efun.
+Im Verzeichnis /secure/simul_efun/spare ist eine Sicherheitskopie von allen 
+Dateien in /secure/simul_efun.
+
+Falls /secure/simul_efun/simul_efun.c nicht ladbar ist, wird 
+/secure/simul_efun/spare/simul_efun.c als Ersatz versucht. Wenn auch das
+nicht ladbar ist, erfolgt ein Shutdown des Muds.
+
+Bei Aenderungen sollte _zuerst_ die normale simul efun editiert und
+anschliessend zerstoert/entladen werden, woraufhin sie implizit durch den
+Driver und Master neugeladen wird. Ist dies erfolgreich und die neue
+simul_efun laeuft (erst dann!) kopiert man die Dateien aus 
+/secure/simul_efun nach /secure/simul_efun/spare.
+
+Die Dateien hier sind mit Ausnahme der simul_efun.c selbst _nicht_ dazu 
+gedacht, geladen zu werden.
+
diff --git a/secure/simul_efun/spare/spare/comm.c b/secure/simul_efun/spare/spare/comm.c
new file mode 100644
index 0000000..ffb598c
--- /dev/null
+++ b/secure/simul_efun/spare/spare/comm.c
@@ -0,0 +1,125 @@
+#include <living/comm.h>
+
+// Sendet an alle Objekte in room und room selber durch Aufruf von
+// ReceiveMsg().
+varargs void send_room(object|string room, string msg, int msg_type,
+                       string msg_action, string msg_prefix, object *exclude,
+                       object origin)
+{
+  if (stringp(room))
+    room=load_object(room);
+  
+  origin ||= previous_object();
+  object *dest = exclude ? all_inventory(room) - exclude :
+                           all_inventory(room);
+
+  dest->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, origin);
+  room->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, origin);
+}
+
+static int _shout_filter( object ob, string pat )
+{
+    string *ignore;
+
+    if ( !environment(ob) )
+       return 0;
+
+    return sizeof( regexp( ({ object_name( environment(ob) ) }), pat ) );
+}
+
+varargs void shout( string s, mixed where ){
+    object *u;
+    string *pfade;
+
+    if ( !sizeof( u = users() - ({ this_player() }) ) )
+       return;
+
+    if ( !where )
+       pfade = ({ "/" });
+    else if ( intp(where) )
+       pfade =
+           ({ implode( efun::explode( object_name( environment(this_player()) ),
+                                   "/" )[0..2], "/" ) + "/" });
+    else if ( stringp(where) )
+       pfade = ({ where });
+    else
+       pfade = where;
+
+    u = filter( u, "_shout_filter", ME, implode( pfade, "|" ) );
+    u->ReceiveMsg(s, MT_COMM|MT_FAR|MSG_DONT_WRAP|MSG_DONT_STORE,
+                  MA_SHOUT_SEFUN, 0, previous_object());
+}
+
+
+#if __VERSION__ > "3.3.718"
+// This sefun replaces the deprecated efun cat().
+#define CAT_MAX_LINES 50
+varargs int cat(string file, int start, int num)
+{
+    if (extern_call())
+        set_this_object(previous_object());
+
+    int more;
+
+    if (num < 0 || !this_player())
+        return 0;
+
+    if (!start)
+        start = 1;
+
+    if (!num || num > CAT_MAX_LINES) {
+        num = CAT_MAX_LINES;
+        more = sizeof(read_file(file, start+num, 1));
+    }
+
+    string txt = read_file(file, start, num);
+    if (!txt)
+        return 0;
+
+    efun::tell_object(this_player(), txt);
+
+    if (more)
+        efun::tell_object(this_player(), "*****TRUNCATED****\n");
+
+    return sizeof(txt & "\n");
+}
+#undef CAT_MAX_LINES
+#endif // __VERSION__ > "3.3.718"
+
+#if __VERSION__ > "3.3.719"
+// This sefun replaces the deprecated efun tail().
+#define TAIL_MAX_BYTES 1000
+varargs int tail(string file)
+{
+    if (extern_call())
+        set_this_object(previous_object());
+
+    if (!stringp(file) || !this_player())
+        return 0;
+
+    string txt = read_bytes(file, -(TAIL_MAX_BYTES + 80), (TAIL_MAX_BYTES + 80));
+    // read_bytes() returns 0 if the start of the section given by
+    // parameter #2 lies beyond the beginning of the file.
+    // In this case we simply try and read the entire file.
+    if (!stringp(txt) && file_size(file) < TAIL_MAX_BYTES+80)
+      txt = read_file(file);
+    // Exit if still nothing could be read.
+    if (!stringp(txt))
+      return 0;
+
+    // cut off first (incomplete) line
+    int index = strstr(txt, "\n");
+    if (index > -1) {
+        if (index + 1 < sizeof(txt))
+            txt = txt[index+1..];
+        else
+            txt = "";
+    }
+
+    efun::tell_object(this_player(), txt);
+
+    return 1;
+}
+#undef TAIL_MAX_BYTES
+#endif // __VERSION__ > "3.3.719"
+
diff --git a/secure/simul_efun/spare/spare/debug_info.c b/secure/simul_efun/spare/spare/debug_info.c
new file mode 100644
index 0000000..d607c2f
--- /dev/null
+++ b/secure/simul_efun/spare/spare/debug_info.c
@@ -0,0 +1,434 @@
+/* This sefun is to provide a replacement for the efun debug_info().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(debug_info)
+
+#include <driver_info.h>
+#include <debug_info.h>
+
+mixed debug_info(int what, varargs mixed* args)
+{
+    if (sizeof(args) > 2)
+        raise_error("Too many arguments to debug_info\n");
+
+    switch(what)
+    {
+        default:
+            raise_error(sprintf("Illegal value %d for debug_info().\n", what));
+
+        case DINFO_OBJECT:
+        {
+            object ob;
+
+            if (sizeof(args) != 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (!objectp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            ob = args[0];
+            printf("O_HEART_BEAT      : %s\n", efun::object_info(ob, OC_HEART_BEAT)       ? "TRUE" : "FALSE");
+            printf("O_ENABLE_COMMANDS : %s\n", efun::object_info(ob, OC_COMMANDS_ENABLED) ? "TRUE" : "FALSE");
+            printf("O_CLONE           : %s\n", efun::clonep(ob)                           ? "TRUE" : "FALSE");
+            printf("O_DESTRUCTED      : FALSE\n");
+            printf("O_SWAPPED         : %s\n", efun::object_info(ob, OI_SWAPPED)          ? "TRUE" : "FALSE");
+            printf("O_ONCE_INTERACTIVE: %s\n", efun::object_info(ob, OI_ONCE_INTERACTIVE) ? "TRUE" : "FALSE");
+            printf("O_RESET_STATE     : %s\n", efun::object_info(ob, OI_RESET_STATE)      ? "TRUE" : "FALSE");
+            printf("O_WILL_CLEAN_UP   : %s\n", efun::object_info(ob, OI_WILL_CLEAN_UP)    ? "TRUE" : "FALSE");
+            printf("O_REPLACED        : %s\n", efun::object_info(ob, OI_REPLACED)         ? "TRUE" : "FALSE");
+            printf("time_reset  : %d\n",       efun::object_info(ob, OI_NEXT_RESET_TIME));
+            printf("time_of_ref : %d\n",       efun::object_info(ob, OI_LAST_REF_TIME));
+            printf("ref         : %d\n",       efun::object_info(ob, OI_OBJECT_REFS));
+
+            int gticks = efun::object_info(ob, OI_GIGATICKS);
+            if(gticks)
+                printf("evalcost   :  %d%09d\n", gticks, efun::object_info(ob, OI_TICKS));
+            else
+                printf("evalcost   :  %d\n",   efun::object_info(ob, OI_TICKS));
+
+            printf("swap_num    : %d\n",       efun::object_info(ob, OI_SWAP_NUM));
+            printf("name        : '%s'\n",     efun::object_name(ob));
+            printf("load_name   : '%s'\n",     efun::load_name(ob));
+
+            object next_ob = efun::object_info(ob, OI_OBJECT_NEXT);
+            if (next_ob)
+                printf("next_all    : OBJ(%s)\n", efun::object_name(next_ob));
+
+            object prev_ob = efun::object_info(ob, OI_OBJECT_PREV);
+            if (prev_ob)
+                printf("Previous object in object list: OBJ(%s)\n", efun::object_name(prev_ob));
+            else
+                printf("This object is the head of the object list.\n");
+            break;
+        }
+
+        case DINFO_MEMORY:
+        {
+            object ob;
+
+            if (sizeof(args) != 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (!objectp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            ob = args[0];
+
+            printf("program ref's %3d\n",   efun::object_info(ob, OI_PROG_REFS));
+            printf("Name: '%s'\n",          efun::program_name(ob));
+            printf("program size    %6d\n", efun::object_info(ob, OI_PROG_SIZE));
+            printf("num func's:  %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_FUNCTIONS),
+                                            efun::object_info(ob, OI_SIZE_FUNCTIONS));
+            printf("num vars:    %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_VARIABLES),
+                                            efun::object_info(ob, OI_SIZE_VARIABLES));
+
+            printf("num strings: %3d (%4d) : overhead %d + data %d (%d)\n",
+                                            efun::object_info(ob, OI_NUM_STRINGS),
+                                            efun::object_info(ob, OI_SIZE_STRINGS) + efun::object_info(ob, OI_SIZE_STRINGS_DATA),
+                                            efun::object_info(ob, OI_SIZE_STRINGS),
+                                            efun::object_info(ob, OI_SIZE_STRINGS_DATA),
+                                            efun::object_info(ob, OI_SIZE_STRINGS_DATA_TOTAL));
+
+            printf("num inherits %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_INHERITED),
+                                            efun::object_info(ob, OI_SIZE_INHERITED));
+
+            printf("total size      %6d\n", efun::object_info(ob, OI_PROG_SIZE_TOTAL));
+
+            printf("data size       %6d (%6d\n",
+                                            efun::object_info(ob, OI_DATA_SIZE),
+                                            efun::object_info(ob, OI_DATA_SIZE_TOTAL));
+            break;
+        }
+
+        case DINFO_OBJLIST:
+        {
+            if (sizeof(args) == 0)
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (sizeof(args) == 1)
+            {
+                object * obs = efun::objects(args[0], 1);
+                return obs[0];
+            }
+            else
+                return efun::objects(args[0], args[1]);
+            break;
+        }
+
+        case DINFO_MALLOC:
+            write(debug_info(DINFO_STATUS, "malloc"));
+            break;
+
+        case DINFO_STATUS:
+        {
+            string which;
+            int opt;
+
+            if (sizeof(args) > 1)
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (!sizeof(args) || !args[0])
+                which = "";
+            else if(stringp(args[0]))
+                which = args[0];
+            else
+                raise_error("bag arg 2 to debug_info().\n");
+
+            switch(which)
+            {
+                case "":
+                    opt = DI_STATUS_TEXT_MEMORY;
+                    break;
+
+                case "tables":
+                    opt = DI_STATUS_TEXT_TABLES;
+                    break;
+
+                case "swap":
+                    opt = DI_STATUS_TEXT_SWAP;
+                    break;
+
+                case "malloc":
+                    opt = DI_STATUS_TEXT_MALLOC;
+                    break;
+
+                case "malloc extstats":
+                    opt = DI_STATUS_TEXT_MALLOC_EXTENDED;
+                    break;
+
+                default:
+                    return 0;
+            }
+
+            return efun::driver_info(opt);
+        }
+
+        case DINFO_DUMP:
+        {
+            int opt;
+
+            if (!sizeof(args))
+                raise_error("bad number of arguments to debug_info\n");
+
+            if(!stringp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            switch(args[0])
+            {
+                case "objects":
+                    opt = DDI_OBJECTS;
+                    break;
+
+                case "destructed":
+                    opt = DDI_OBJECTS_DESTRUCTED;
+                    break;
+
+                case "opcodes":
+                    opt = DDI_OPCODES;
+                    break;
+
+                case "memory":
+                    opt = DDI_MEMORY;
+                    break;
+
+                default:
+                    raise_error(sprintf("Bad argument '%s' to debug_info(DINFO_DUMP).\n", args[0]));
+                    return 0;
+            }
+
+            return efun::dump_driver_info(opt, args[1..1]...);
+        }
+
+        case DINFO_DATA:
+        {
+            mixed * result;
+
+            if (!sizeof(args))
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (!intp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            if (sizeof(args) == 2 && !intp(args[1]))
+                raise_error("bag arg 3 to debug_info().\n");
+
+            switch(args[0])
+            {
+                case DID_STATUS:
+                    result = allocate(DID_STATUS_MAX);
+
+                    result[DID_ST_ACTIONS]               = efun::driver_info(DI_NUM_ACTIONS);
+                    result[DID_ST_ACTIONS_SIZE]          = efun::driver_info(DI_SIZE_ACTIONS);
+                    result[DID_ST_SHADOWS]               = efun::driver_info(DI_NUM_SHADOWS);
+                    result[DID_ST_SHADOWS_SIZE]          = efun::driver_info(DI_SIZE_SHADOWS);
+
+                    result[DID_ST_OBJECTS]               = efun::driver_info(DI_NUM_OBJECTS);
+                    result[DID_ST_OBJECTS_SIZE]          = efun::driver_info(DI_SIZE_OBJECTS);
+                    result[DID_ST_OBJECTS_SWAPPED]       = efun::driver_info(DI_NUM_OBJECTS_SWAPPED);
+                    result[DID_ST_OBJECTS_SWAP_SIZE]     = efun::driver_info(DI_SIZE_OBJECTS_SWAPPED);
+                    result[DID_ST_OBJECTS_LIST]          = efun::driver_info(DI_NUM_OBJECTS_IN_LIST);
+                    result[DID_ST_OBJECTS_NEWLY_DEST]    = efun::driver_info(DI_NUM_OBJECTS_NEWLY_DESTRUCTED);
+                    result[DID_ST_OBJECTS_DESTRUCTED]    = efun::driver_info(DI_NUM_OBJECTS_DESTRUCTED);
+                    result[DID_ST_OBJECTS_PROCESSED]     = efun::driver_info(DI_NUM_OBJECTS_LAST_PROCESSED);
+                    result[DID_ST_OBJECTS_AVG_PROC]      = efun::driver_info(DI_LOAD_AVERAGE_PROCESSED_OBJECTS_RELATIVE);
+
+                    result[DID_ST_OTABLE]                = efun::driver_info(DI_NUM_OBJECTS_IN_TABLE);
+                    result[DID_ST_OTABLE_SLOTS]          = efun::driver_info(DI_NUM_OBJECT_TABLE_SLOTS);
+                    result[DID_ST_OTABLE_SIZE]           = efun::driver_info(DI_SIZE_OBJECT_TABLE);
+
+                    result[DID_ST_HBEAT_OBJS]            = efun::driver_info(DI_NUM_HEARTBEATS);
+                    result[DID_ST_HBEAT_CALLS]           = efun::driver_info(DI_NUM_HEARTBEAT_ACTIVE_CYCLES);
+                    result[DID_ST_HBEAT_CALLS_TOTAL]     = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+                    result[DID_ST_HBEAT_SLOTS]           = efun::driver_info(DI_NUM_HEARTBEATS);
+                    result[DID_ST_HBEAT_SIZE]            = efun::driver_info(DI_SIZE_HEARTBEATS);
+                    result[DID_ST_HBEAT_PROCESSED]       = efun::driver_info(DI_NUM_HEARTBEATS_LAST_PROCESSED);
+                    result[DID_ST_HBEAT_AVG_PROC]        = efun::driver_info(DI_LOAD_AVERAGE_PROCESSED_HEARTBEATS_RELATIVE);
+
+                    result[DID_ST_CALLOUTS]              = efun::driver_info(DI_NUM_CALLOUTS);
+                    result[DID_ST_CALLOUT_SIZE]          = efun::driver_info(DI_SIZE_CALLOUTS);
+
+                    result[DID_ST_ARRAYS]                = efun::driver_info(DI_NUM_ARRAYS);
+                    result[DID_ST_ARRAYS_SIZE]           = efun::driver_info(DI_SIZE_ARRAYS);
+
+                    result[DID_ST_MAPPINGS]              = efun::driver_info(DI_NUM_MAPPINGS);
+                    result[DID_ST_MAPPINGS_SIZE]         = efun::driver_info(DI_SIZE_MAPPINGS);
+                    result[DID_ST_HYBRID_MAPPINGS]       = efun::driver_info(DI_NUM_MAPPINGS_HYBRID);
+                    result[DID_ST_HASH_MAPPINGS]         = efun::driver_info(DI_NUM_MAPPINGS_HASH);
+
+                    result[DID_ST_STRUCTS]               = efun::driver_info(DI_NUM_STRUCTS);
+                    result[DID_ST_STRUCTS_SIZE]          = efun::driver_info(DI_SIZE_STRUCTS);
+                    result[DID_ST_STRUCT_TYPES]          = efun::driver_info(DI_NUM_STRUCT_TYPES);
+                    result[DID_ST_STRUCT_TYPES_SIZE]     = efun::driver_info(DI_SIZE_STRUCT_TYPES);
+
+                    result[DID_ST_PROGS]                 = efun::driver_info(DI_NUM_PROGS);
+                    result[DID_ST_PROGS_SIZE]            = efun::driver_info(DI_SIZE_PROGS);
+
+                    result[DID_ST_PROGS_SWAPPED]         = efun::driver_info(DI_NUM_PROGS_SWAPPED);
+                    result[DID_ST_PROGS_SWAP_SIZE]       = efun::driver_info(DI_SIZE_PROGS_SWAPPED);
+
+                    result[DID_ST_USER_RESERVE]          = efun::driver_info(DI_MEMORY_RESERVE_USER);
+                    result[DID_ST_MASTER_RESERVE]        = efun::driver_info(DI_MEMORY_RESERVE_MASTER);
+                    result[DID_ST_SYSTEM_RESERVE]        = efun::driver_info(DI_MEMORY_RESERVE_SYSTEM);
+
+                    result[DID_ST_ADD_MESSAGE]           = efun::driver_info(DI_NUM_MESSAGES_OUT);
+                    result[DID_ST_PACKETS]               = efun::driver_info(DI_NUM_PACKETS_OUT);
+                    result[DID_ST_PACKET_SIZE]           = efun::driver_info(DI_SIZE_PACKETS_OUT);
+                    result[DID_ST_PACKETS_IN]            = efun::driver_info(DI_NUM_PACKETS_IN);
+                    result[DID_ST_PACKET_SIZE_IN]        = efun::driver_info(DI_SIZE_PACKETS_IN);
+
+                    result[DID_ST_APPLY]                 = efun::driver_info(DI_NUM_FUNCTION_NAME_CALLS);
+                    result[DID_ST_APPLY_HITS]            = efun::driver_info(DI_NUM_FUNCTION_NAME_CALL_HITS);
+
+                    result[DID_ST_STRINGS]               = efun::driver_info(DI_NUM_VIRTUAL_STRINGS);
+                    result[DID_ST_STRING_SIZE]           = efun::driver_info(DI_SIZE_STRINGS);
+                    result[DID_ST_STR_TABLE_SIZE]        = efun::driver_info(DI_SIZE_STRING_TABLE);
+                    result[DID_ST_STR_OVERHEAD]          = efun::driver_info(DI_SIZE_STRING_OVERHEAD);
+                    result[DID_ST_UNTABLED]              = efun::driver_info(DI_NUM_STRINGS_UNTABLED);
+                    result[DID_ST_UNTABLED_SIZE]         = efun::driver_info(DI_SIZE_STRINGS_UNTABLED);
+                    result[DID_ST_TABLED]                = efun::driver_info(DI_NUM_STRINGS_TABLED);
+                    result[DID_ST_TABLED_SIZE]           = efun::driver_info(DI_SIZE_STRINGS_TABLED);
+                    result[DID_ST_STR_SEARCHES]          = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUPS_BY_INDEX);
+                    result[DID_ST_STR_SEARCHLEN]         = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_INDEX);
+                    result[DID_ST_STR_SEARCHES_BYVALUE]  = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUPS_BY_VALUE);
+                    result[DID_ST_STR_SEARCHLEN_BYVALUE] = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_VALUE);
+                    result[DID_ST_STR_CHAINS]            = efun::driver_info(DI_NUM_STRING_TABLE_SLOTS_USED);
+                    result[DID_ST_STR_ADDED]             = efun::driver_info(DI_NUM_STRING_TABLE_STRINGS_ADDED);
+                    result[DID_ST_STR_DELETED]           = efun::driver_info(DI_NUM_STRING_TABLE_STRINGS_REMOVED);
+                    result[DID_ST_STR_COLLISIONS]        = efun::driver_info(DI_NUM_STRING_TABLE_COLLISIONS);
+                    result[DID_ST_STR_FOUND]             = efun::driver_info(DI_NUM_STRING_TABLE_HITS_BY_INDEX);
+                    result[DID_ST_STR_FOUND_BYVALUE]     = efun::driver_info(DI_NUM_STRING_TABLE_HITS_BY_VALUE);
+
+                    result[DID_ST_RX_CACHED]             = efun::driver_info(DI_NUM_REGEX);
+                    result[DID_ST_RX_TABLE]              = efun::driver_info(DI_NUM_REGEX_TABLE_SLOTS);
+                    result[DID_ST_RX_TABLE_SIZE]         = efun::driver_info(DI_SIZE_REGEX);
+                    result[DID_ST_RX_REQUESTS]           = efun::driver_info(DI_NUM_REGEX_LOOKUPS);
+                    result[DID_ST_RX_REQ_FOUND]          = efun::driver_info(DI_NUM_REGEX_LOOKUP_HITS);
+                    result[DID_ST_RX_REQ_COLL]           = efun::driver_info(DI_NUM_REGEX_LOOKUP_COLLISIONS);
+
+                    result[DID_ST_MB_FILE]               = efun::driver_info(DI_SIZE_BUFFER_FILE);
+                    result[DID_ST_MB_SWAP]               = efun::driver_info(DI_SIZE_BUFFER_SWAP);
+
+                    result[DID_ST_BOOT_TIME]             = efun::driver_info(DI_BOOT_TIME);
+                    break;
+
+                case DID_SWAP:
+                    result = allocate(DID_SWAP_MAX);
+
+                    result[DID_SW_PROGS]                 = efun::driver_info(DI_NUM_PROGS_SWAPPED);
+                    result[DID_SW_PROG_SIZE]             = efun::driver_info(DI_SIZE_PROGS_SWAPPED);
+                    result[DID_SW_PROG_UNSWAPPED]        = efun::driver_info(DI_NUM_PROGS_UNSWAPPED);
+                    result[DID_SW_PROG_U_SIZE]           = efun::driver_info(DI_SIZE_PROGS_UNSWAPPED);
+                    result[DID_SW_VARS]                  = efun::driver_info(DI_NUM_OBJECTS_SWAPPED);
+                    result[DID_SW_VAR_SIZE]              = efun::driver_info(DI_SIZE_OBJECTS_SWAPPED);
+                    result[DID_SW_FREE]                  = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE);
+                    result[DID_SW_FREE_SIZE]             = efun::driver_info(DI_SIZE_SWAP_BLOCKS_FREE);
+                    result[DID_SW_FILE_SIZE]             = efun::driver_info(DI_SIZE_SWAP_BLOCKS);
+                    result[DID_SW_REUSED]                = efun::driver_info(DI_SIZE_SWAP_BLOCKS_REUSED);
+                    result[DID_SW_SEARCHES]              = efun::driver_info(DI_NUM_SWAP_BLOCKS_REUSE_LOOKUPS);
+                    result[DID_SW_SEARCH_LEN]            = efun::driver_info(DI_NUM_SWAP_BLOCKS_REUSE_LOOKUP_STEPS);
+                    result[DID_SW_F_SEARCHES]            = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE_LOOKUPS);
+                    result[DID_SW_F_SEARCH_LEN]          = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE_LOOKUP_STEPS);
+                    result[DID_SW_COMPACT]               = efun::driver_info(DC_SWAP_COMPACT_MODE);
+                    result[DID_SW_RECYCLE_FREE]          = efun::driver_info(DI_SWAP_RECYCLE_PHASE);
+                    break;
+
+                case DID_MEMORY:
+                    result = allocate(DID_MEMORY_MAX);
+
+                    result[DID_MEM_NAME]                 = efun::driver_info(DI_MEMORY_ALLOCATOR_NAME);
+                    result[DID_MEM_SBRK]                 = efun::driver_info(DI_NUM_SYS_ALLOCATED_BLOCKS);
+                    result[DID_MEM_SBRK_SIZE]            = efun::driver_info(DI_SIZE_SYS_ALLOCATED_BLOCKS);
+                    result[DID_MEM_LARGE]                = efun::driver_info(DI_NUM_LARGE_BLOCKS_ALLOCATED);
+                    result[DID_MEM_LARGE_SIZE]           = efun::driver_info(DI_SIZE_LARGE_BLOCKS_ALLOCATED);
+                    result[DID_MEM_LFREE]                = efun::driver_info(DI_NUM_LARGE_BLOCKS_FREE);
+                    result[DID_MEM_LFREE_SIZE]           = efun::driver_info(DI_SIZE_LARGE_BLOCKS_FREE);
+                    result[DID_MEM_LWASTED]              = efun::driver_info(DI_NUM_LARGE_BLOCKS_WASTE);
+                    result[DID_MEM_LWASTED_SIZE]         = efun::driver_info(DI_SIZE_LARGE_BLOCKS_WASTE);
+                    result[DID_MEM_CHUNK]                = efun::driver_info(DI_NUM_SMALL_BLOCK_CHUNKS);
+                    result[DID_MEM_CHUNK_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCK_CHUNKS);
+                    result[DID_MEM_SMALL]                = efun::driver_info(DI_NUM_SMALL_BLOCKS_ALLOCATED);
+                    result[DID_MEM_SMALL_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCKS_ALLOCATED);
+                    result[DID_MEM_SFREE]                = efun::driver_info(DI_NUM_SMALL_BLOCKS_FREE);
+                    result[DID_MEM_SFREE_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCKS_FREE);
+                    result[DID_MEM_SWASTED]              = efun::driver_info(DI_NUM_SMALL_BLOCKS_WASTE);
+                    result[DID_MEM_SWASTED_SIZE]         = efun::driver_info(DI_SIZE_SMALL_BLOCKS_WASTE);
+                    result[DID_MEM_MINC_CALLS]           = efun::driver_info(DI_NUM_INCREMENT_SIZE_CALLS);
+                    result[DID_MEM_MINC_SUCCESS]         = efun::driver_info(DI_NUM_INCREMENT_SIZE_CALL_SUCCESSES);
+                    result[DID_MEM_MINC_SIZE]            = efun::driver_info(DI_SIZE_INCREMENT_SIZE_CALL_DIFFS);
+                    result[DID_MEM_PERM]                 = efun::driver_info(DI_NUM_UNMANAGED_BLOCKS);
+                    result[DID_MEM_PERM_SIZE]            = efun::driver_info(DI_SIZE_UNMANAGED_BLOCKS);
+                    result[DID_MEM_CLIB]                 = efun::driver_info(DI_NUM_REPLACEMENT_MALLOC_CALLS);
+                    result[DID_MEM_CLIB_SIZE]            = efun::driver_info(DI_SIZE_REPLACEMENT_MALLOC_CALLS);
+                    result[DID_MEM_OVERHEAD]             = efun::driver_info(DI_SIZE_SMALL_BLOCK_OVERHEAD);
+                    result[DID_MEM_ALLOCATED]            = efun::driver_info(DI_SIZE_MEMORY_USED) + efun::driver_info(DI_SIZE_MEMORY_OVERHEAD);
+                    result[DID_MEM_USED]                 = efun::driver_info(DI_SIZE_MEMORY_USED);
+                    result[DID_MEM_TOTAL_UNUSED]         = efun::driver_info(DI_SIZE_MEMORY_UNUSED);
+                    result[DID_MEM_DEFRAG_CALLS]         = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_FULL) + efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_TARGETED);
+                    result[DID_MEM_DEFRAG_CALLS_REQ]     = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_TARGETED);
+                    result[DID_MEM_DEFRAG_REQ_SUCCESS]   = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALL_TARGET_HITS);
+                    result[DID_MEM_DEFRAG_BLOCKS_INSPECTED] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_INSPECTED);
+                    result[DID_MEM_DEFRAG_BLOCKS_MERGED] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_MERGED);
+                    result[DID_MEM_DEFRAG_BLOCKS_RESULT] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_RESULTING);
+                    result[DID_MEM_AVL_NODES]            = efun::driver_info(DI_NUM_FREE_BLOCKS_AVL_NODES);
+                    result[DID_MEM_EXT_STATISTICS]       = efun::driver_info(DI_MEMORY_EXTENDED_STATISTICS);
+                    break;
+            }
+
+            if (sizeof(args) == 2)
+            {
+                int idx = args[0];
+                if (idx < 0 || idx >= sizeof(result))
+                    raise_error(sprintf("Illegal index for debug_info(): %d, expected 0..%d\n",
+                        idx, sizeof(result)-1));
+
+                return result[idx];
+            }
+            else
+                return result;
+        }
+
+        case DINFO_TRACE:
+        {
+            int which = DIT_CURRENT;
+
+            if (sizeof(args) > 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (sizeof(args))
+            {
+                if (!intp(args[0]))
+                    raise_error("bag arg 2 to debug_info().\n");
+                which = args[0];
+            }
+
+            switch (which)
+            {
+                case DIT_CURRENT:
+                    return efun::driver_info(DI_TRACE_CURRENT);
+
+                case DIT_ERROR:
+                    return efun::driver_info(DI_TRACE_LAST_ERROR) || ({ "No trace." });
+
+                case DIT_UNCAUGHT_ERROR:
+                    return efun::driver_info(DI_TRACE_LAST_UNCAUGHT_ERROR) || ({ "No trace." });
+
+                case DIT_STR_CURRENT:
+                    return efun::driver_info(DI_TRACE_CURRENT_AS_STRING);
+
+                case DIT_CURRENT_DEPTH:
+                    return efun::driver_info(DI_TRACE_CURRENT_DEPTH);
+
+                default:
+                    raise_error("bad arg 2 to debug_info().\n");
+            }
+
+        }
+
+        case DINFO_EVAL_NUMBER:
+            return efun::driver_info(DI_EVAL_NUMBER);
+    }
+    return 0;
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/enable_commands.c b/secure/simul_efun/spare/spare/enable_commands.c
new file mode 100644
index 0000000..9d1c963
--- /dev/null
+++ b/secure/simul_efun/spare/spare/enable_commands.c
@@ -0,0 +1,30 @@
+/* These sefuns are to provide a replacement for the efuns enable_commands()
+ * and disable_commands().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#include <configuration.h>
+
+#if ! __EFUN_DEFINED__(enable_commands)
+
+void enable_commands()
+{
+    object ob = efun::previous_object();
+
+    efun::configure_object(ob, OC_COMMANDS_ENABLED, 1);
+    efun::set_this_player(ob);
+}
+
+#endif
+
+#if ! __EFUN_DEFINED__(disable_commands)
+
+void disable_commands()
+{
+    object ob = efun::previous_object();
+
+    efun::configure_object(ob, OC_COMMANDS_ENABLED, 0);
+    efun::set_this_player(0);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/hash.c b/secure/simul_efun/spare/spare/hash.c
new file mode 100644
index 0000000..51bbf60
--- /dev/null
+++ b/secure/simul_efun/spare/spare/hash.c
@@ -0,0 +1,17 @@
+#include "/sys/tls.h"
+
+deprecated string md5(mixed arg, varargs mixed* iterations)
+{
+    if (extern_call())
+         set_this_object(previous_object());
+
+    return hash(TLS_HASH_MD5, arg, iterations...);
+}
+
+deprecated string sha1(mixed arg, varargs mixed* iterations)
+{
+    if (extern_call())
+         set_this_object(previous_object());
+
+    return hash(TLS_HASH_SHA1, arg, iterations...);
+}
diff --git a/secure/simul_efun/spare/spare/livings.c b/secure/simul_efun/spare/spare/livings.c
new file mode 100644
index 0000000..160fcee
--- /dev/null
+++ b/secure/simul_efun/spare/spare/livings.c
@@ -0,0 +1,348 @@
+// * living_name-Behandlung
+
+#define clean_log(s)
+//#define clean_log(s) log_file("CLEAN_SIM",ctime(time())[4..18]+": "+(s));
+
+private nosave mapping living_name_m, name_living_m, netdead;
+
+private void InitLivingData(mixed wizinfo) {
+  // living_name ist ein Pointer der auch in der extra_wizinfo gehalten 
+  // wird, so kann die simul_efun neu geladen werden, ohne dass dieses
+  // Mapping verloren geht
+  if (!mappingp(living_name_m = wizinfo[LIVING_NAME]))
+    living_name_m = wizinfo[LIVING_NAME] = m_allocate(0, 1);
+
+  // Gleiches gilt fuer das Mapping Name-Living
+  if (!mappingp(name_living_m = wizinfo[NAME_LIVING]))
+    name_living_m = wizinfo[NAME_LIVING] = m_allocate(0, 1);
+
+  // Netztote sind ebenfalls in der extra_wizinfo
+  if (!mappingp(netdead = wizinfo[NETDEAD_MAP]))
+    netdead = wizinfo[NETDEAD_MAP] = ([]);
+}
+
+public varargs string getuuid( object ob )
+{
+    mixed *ret;
+
+    if ( !objectp(ob) )
+       ob = previous_object();
+
+    if ( !query_once_interactive(ob) )
+       return getuid(ob);
+
+    ret = (mixed)master()->get_userinfo( getuid(ob) );
+
+    if ( !pointerp(ret) || sizeof(ret) < 5 )
+       return getuid(ob);
+
+    // Username + "_" + CreationDate
+    return ret[0] + "_" + ret[5];
+}
+
+void set_object_living_name(string livname, object obj)
+{
+  string old;
+  mixed a;
+  int i;
+
+  if (previous_object()==obj || previous_object()==this_object() ||
+      previous_object()==master())
+  {
+    if(!livname || !stringp(livname)) {
+      set_this_object(previous_object());
+      raise_error(sprintf("%O: illegal living name: %O\n", obj, livname));
+    }
+    if (old = living_name_m[obj]) {
+      if (pointerp(a = name_living_m[old])) {
+       a[member(a, obj)] = 0;
+      } else {
+       m_delete(name_living_m, old);
+      }
+    }
+    living_name_m[obj] = livname;
+    if (a = name_living_m[livname]) {
+      if (!pointerp(a)) {
+       name_living_m[livname] = ({a, obj});
+       return;
+      }
+      /* Try to reallocate entry from destructed object */
+      if ((i = member(a, 0)) >= 0) {
+       a[i] = obj;
+       return;
+      }
+      name_living_m[livname] = a + ({obj});
+      return;
+    }
+    name_living_m[livname] = obj;
+  }
+}
+
+void set_living_name(string livname)
+{
+  set_object_living_name(livname,previous_object());
+}
+
+void remove_living_name()
+{
+  string livname;
+
+  if (!previous_object())
+    return;
+  if (livname=living_name_m[previous_object()])
+  {
+    m_delete(living_name_m,previous_object());
+    if (objectp(name_living_m[livname]))
+    {
+      if (name_living_m[livname]==previous_object())
+       m_delete(name_living_m,livname);
+      return;
+    }
+    if (pointerp(name_living_m[livname]))
+    {
+      name_living_m[livname]-=({previous_object()});
+      if (!sizeof(name_living_m[livname]))
+       m_delete(name_living_m,livname);
+    }
+  }
+}
+
+void _set_netdead()
+{
+  if (query_once_interactive(previous_object()))
+    netdead[getuid(previous_object())]=previous_object();
+}
+
+void _remove_netdead()
+{
+  m_delete(netdead,getuid(previous_object()));
+}
+
+object find_netdead(string uuid)
+{
+  int i;
+  string uid;
+  // Wenn sscanf() 2 liefert, ist uuid auch ne uuid.
+  int is_uuid = sscanf(uuid, "%s_%d", uid, i) == 2;
+  if (!is_uuid)
+    uid = uuid;
+ 
+  if (is_uuid && getuuid(netdead[uid]) != uuid)
+      return 0;
+
+  return netdead[uid];
+}
+
+string *dump_netdead()
+{
+  return m_indices(netdead);
+}
+
+object find_living(string livname) {
+  mixed *a, r;
+  int i;
+
+  if (pointerp(r = name_living_m[livname])) {
+    if (member(r,0)>=0)
+      r-=({0});
+    if (!sizeof(r)){
+      m_delete(name_living_m,livname);
+      clean_log(sprintf("find_living loescht %s\n",livname));
+      return 0;
+    }
+    if ( !living(r = (a = r)[0])) {
+      for (i = sizeof(a); --i;) {
+       if (living(a[<i])) {
+         r = a[<i];
+         a[<i] = a[0];
+         return a[0] = r;
+       }
+      }
+    }
+    return r;
+  }
+  return living(r) && r;
+}
+
+object *find_livings(string livname)
+{
+  mixed r;
+
+  if (pointerp(r=name_living_m[livname]))
+    r-=({0});
+  else
+    if (objectp(r))
+      r=({r});
+  if (!pointerp(r)||!sizeof(r))
+    return 0;
+  return r;
+}
+
+object find_player(string uuid) {
+  object *objs;
+  mixed res;
+  int i;
+  string uid;
+
+  if (!stringp(uuid))
+    return 0;
+  // Wenn sscanf() 2 liefert, ist uuid auch ne uuid.
+  int is_uuid = sscanf(uuid, "%s_%d", uid, i) == 2;
+  if (!is_uuid)
+    uid = uuid;
+
+  if (pointerp(res = name_living_m[uid])) {
+    // zerstoerte Objekte ggf. entfernen
+    if (member(res,0)>=0) {
+      res-=({0});
+      name_living_m[uid] = res;
+    }
+    // ggf. Namen ohne Objekte entfernen.
+    if (!sizeof(res)){
+      m_delete(name_living_m,uid);
+      clean_log(sprintf("find_player loescht %s\n",uid));
+      return 0;
+    }
+    objs = res;
+    res = objs[0];
+    // Wenn das 0. Element der Spieler ist, sind wir fertig. Ansonsten suchen
+    // wir den Spieler und schieben ihn an die 0. Stelle im Array.
+    if ( !query_once_interactive(res)) {
+      for (i = sizeof(objs); --i;) {
+       if (objs[<i] && query_once_interactive(objs[<i])) {
+         res = objs[<i];
+         objs[<i] = objs[0];
+         objs[0] = res;
+         break;
+       }
+      }
+      res = (objectp(res) && query_once_interactive(res)) ? res : 0;
+    }
+    // else: in res steht der Spieler schon.
+  }
+  else if (objectp(res)) // r ist nen Einzelobjekt
+    res = query_once_interactive(res) ? res : 0;
+
+  // res ist jetzt ggf. der Spieler. Aber wenn der ne andere als die gesuchte
+  // UUID hat, ist das Ergebnis trotzdem 0.
+  if (is_uuid && getuuid(res)!=uuid)
+      return 0;
+
+  // endlich gefunden
+  return res;
+}
+
+private int check_match( string str, int players_only )
+{
+    mixed match;
+
+    if ( !(match = name_living_m[str]) )
+       return 0;
+
+    if ( !pointerp(match) )
+       match = ({ match });
+
+    match -= ({0});
+
+    if ( sizeof(match) ){
+       if ( players_only )
+           return sizeof(filter( match, #'query_once_interactive/*'*/ ))
+              > 0;
+       else
+           return 1;
+    }
+
+    m_delete( name_living_m, str );
+    clean_log( sprintf("check_match loescht %s\n", str) );
+    return 0;
+}
+
+//TODO:: string|string* exclude
+varargs mixed match_living( string str, int players_only, mixed exclude )
+{
+    int i, s;
+    mixed match, *user;
+
+    if ( !str || str == "" )
+       return 0;
+
+    if ( !pointerp(exclude) )
+       exclude = ({ exclude });
+
+    if ( member(exclude, str) < 0 && check_match(str, players_only) )
+       return str;
+
+    user = m_indices(name_living_m);
+    s = sizeof(str);
+    match = 0;
+
+    for ( i = sizeof(user); i--; )
+       if ( str == user[i][0..s-1] && member( exclude, user[i] ) < 0 )
+           if ( match )
+              return -1;
+           else
+              if ( check_match(user[i], players_only) )
+                  match = user[i];
+
+    if ( !match )
+       return -2;
+
+    return match;
+}
+
+mixed *dump_livings()
+{
+  return sort_array(m_indices(name_living_m),#'>);
+}
+
+// * regelmaessig Listen von Ballast befreien
+private void clean_name_living_m(string *keys, int left, int num)
+{
+  int i, j;
+  mixed a;
+
+  while (left && num--)
+  {
+    if (pointerp(a = name_living_m[keys[--left]]) && member(a, 0)>=0)
+    {
+      a-=({0});
+      name_living_m[keys[left]] = a;
+    }
+    if (!a || (pointerp(a) && !sizeof(a)))
+    {
+      clean_log("Toasting "+keys[left]+"\n");
+      m_delete(name_living_m, keys[left]);
+    } else clean_log(sprintf("KEEPING %s (%O)\n",keys[left],pointerp(a)?sizeof(a):a));
+  }
+  if (left)
+    efun::call_out(#'clean_name_living_m, 1, keys, left, 80);
+  else
+    clean_log("Clean process finished\n");
+}
+
+private void clean_netdead() {
+  int i;
+  string *s;
+  object ob;
+
+  s=m_indices(netdead);
+  for (i=sizeof(s)-1;i>=0;i--)
+    if (!objectp(ob=netdead[s[i]]) || interactive(ob))
+      m_delete(netdead,s[i]);
+}
+
+private void CleanLivingData() {
+  if (find_call_out(#'clean_name_living_m) < 0) {
+    clean_log("Starting clean process\n");
+    efun::call_out(#'clean_name_living_m,
+                 1,
+                 m_indices(name_living_m),
+                 sizeof(name_living_m),
+                 80
+                 );
+  }
+  efun::call_out(#'clean_netdead,2);
+}
+
+#undef clean_log
+
diff --git a/secure/simul_efun/spare/spare/object_info.c b/secure/simul_efun/spare/spare/object_info.c
new file mode 100644
index 0000000..ed7c009
--- /dev/null
+++ b/secure/simul_efun/spare/spare/object_info.c
@@ -0,0 +1,106 @@
+/* This sefun is to provide the old semantics of the efun object_info().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if __EFUN_DEFINED__(driver_info) /* Newer version is there */
+
+#include <objectinfo.h>
+#include <object_info.h>
+
+mixed object_info(object ob, int what, varargs int* index)
+{
+    mixed * result;
+
+    if (sizeof(index) > 1)
+        raise_error("Too many arguments to object_info\n");
+
+    switch(what)
+    {
+        default:
+            raise_error(sprintf("Illegal value %d for object_info().\n", what));
+
+        case OINFO_BASIC:
+        {
+            result = allocate(OIB_MAX);
+
+            result[OIB_HEART_BEAT]        = efun::object_info(ob, OC_HEART_BEAT);
+            result[OIB_IS_WIZARD]         = 0;   /* Not supported anymore. */
+            result[OIB_ENABLE_COMMANDS]   = efun::object_info(ob, OC_COMMANDS_ENABLED);
+            result[OIB_CLONE]             = efun::clonep(ob);
+            result[OIB_DESTRUCTED]        = 0;   /* Not possible anymore. */
+            result[OIB_SWAPPED]           = efun::object_info(ob, OI_SWAPPED);
+            result[OIB_ONCE_INTERACTIVE]  = efun::object_info(ob, OI_ONCE_INTERACTIVE);
+            result[OIB_RESET_STATE]       = efun::object_info(ob, OI_RESET_STATE);
+            result[OIB_WILL_CLEAN_UP]     = efun::object_info(ob, OI_WILL_CLEAN_UP);
+            result[OIB_LAMBDA_REFERENCED] = efun::object_info(ob, OI_LAMBDA_REFERENCED);
+            result[OIB_SHADOW]            = efun::object_info(ob, OI_SHADOW_PREV) && 1;
+            result[OIB_REPLACED]          = efun::object_info(ob, OI_REPLACED);
+            result[OIB_NEXT_RESET]        = efun::object_info(ob, OI_NEXT_RESET_TIME);
+            result[OIB_TIME_OF_REF]       = efun::object_info(ob, OI_LAST_REF_TIME);
+            result[OIB_REF]               = efun::object_info(ob, OI_OBJECT_REFS);
+            result[OIB_GIGATICKS]         = efun::object_info(ob, OI_GIGATICKS);
+            result[OIB_TICKS]             = efun::object_info(ob, OI_TICKS);
+            result[OIB_SWAP_NUM]          = efun::object_info(ob, OI_SWAP_NUM);
+            result[OIB_PROG_SWAPPED]      = efun::object_info(ob, OI_PROG_SWAPPED);
+            result[OIB_VAR_SWAPPED]       = efun::object_info(ob, OI_VAR_SWAPPED);
+            result[OIB_NAME]              = efun::object_name(ob);
+            result[OIB_LOAD_NAME]         = efun::load_name(ob);
+            result[OIB_NEXT_ALL]          = efun::object_info(ob, OI_OBJECT_NEXT);
+            result[OIB_PREV_ALL]          = efun::object_info(ob, OI_OBJECT_PREV);
+            result[OIB_NEXT_CLEANUP]      = efun::object_info(ob, OI_NEXT_CLEANUP_TIME);
+            break;
+        }
+
+        case OINFO_POSITION:
+        {
+            result = allocate(OIP_MAX);
+
+            result[OIP_NEXT] = efun::object_info(ob, OI_OBJECT_NEXT);
+            result[OIP_PREV] = efun::object_info(ob, OI_OBJECT_PREV);
+            result[OIP_POS]  = efun::object_info(ob, OI_OBJECT_POS);
+            break;
+        }
+
+        case OINFO_MEMORY:
+        {
+            result = allocate(OIM_MAX);
+
+            result[OIM_REF]                = efun::object_info(ob, OI_PROG_REFS);
+            result[OIM_NAME]               = efun::program_name(ob);
+            result[OIM_PROG_SIZE]          = efun::object_info(ob, OI_PROG_SIZE);
+            result[OIM_NUM_FUNCTIONS]      = efun::object_info(ob, OI_NUM_FUNCTIONS);
+            result[OIM_SIZE_FUNCTIONS]     = efun::object_info(ob, OI_SIZE_FUNCTIONS);
+            result[OIM_NUM_VARIABLES]      = efun::object_info(ob, OI_NUM_VARIABLES);
+            result[OIM_SIZE_VARIABLES]     = efun::object_info(ob, OI_SIZE_VARIABLES);
+            result[OIM_NUM_STRINGS]        = efun::object_info(ob, OI_NUM_STRINGS);
+            result[OIM_SIZE_STRINGS]       = efun::object_info(ob, OI_SIZE_STRINGS);
+            result[OIM_SIZE_STRINGS_DATA]  = efun::object_info(ob, OI_SIZE_STRINGS_DATA);
+            result[OIM_SIZE_STRINGS_TOTAL] = efun::object_info(ob, OI_SIZE_STRINGS_DATA_TOTAL);
+            result[OIM_NUM_INHERITED]      = efun::object_info(ob, OI_NUM_INHERITED);
+            result[OIM_SIZE_INHERITED]     = efun::object_info(ob, OI_SIZE_INHERITED);
+            result[OIM_TOTAL_SIZE]         = efun::object_info(ob, OI_PROG_SIZE_TOTAL);
+            result[OIM_DATA_SIZE]          = efun::object_info(ob, OI_DATA_SIZE);
+            result[OIM_TOTAL_DATA_SIZE]    = efun::object_info(ob, OI_DATA_SIZE_TOTAL);
+            result[OIM_NO_INHERIT]         = efun::object_info(ob, OI_NO_INHERIT);
+            result[OIM_NO_CLONE]           = efun::object_info(ob, OI_NO_CLONE);
+            result[OIM_NO_SHADOW]          = efun::object_info(ob, OI_NO_SHADOW);
+            result[OIM_NUM_INCLUDES]       = efun::object_info(ob, OI_NUM_INCLUDED);
+            result[OIM_SHARE_VARIABLES]    = efun::object_info(ob, OI_SHARE_VARIABLES);
+            break;
+        }
+    }
+
+    if (sizeof(index))
+    {
+        int idx = index[0];
+        if (idx < 0 || idx >= sizeof(result))
+            raise_error(sprintf("Illegal index for object_info(): %d, expected 0..%d\n",
+                idx, sizeof(result)-1));
+
+        return result[idx];
+    }
+    else
+        return result;
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/query_editing.c b/secure/simul_efun/spare/spare/query_editing.c
new file mode 100644
index 0000000..5146dcd
--- /dev/null
+++ b/secure/simul_efun/spare/spare/query_editing.c
@@ -0,0 +1,17 @@
+/* This sefun is to provide a replacement for the efun query_editing().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_editing)
+
+#include <interactive_info.h>
+
+int|object query_editing(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    return efun::interactive_info(ob, II_EDITING);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/query_idle.c b/secure/simul_efun/spare/spare/query_idle.c
new file mode 100644
index 0000000..93a32ae
--- /dev/null
+++ b/secure/simul_efun/spare/spare/query_idle.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_idle().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_idle)
+
+#include <interactive_info.h>
+
+int query_idle(object ob)
+{
+    return efun::interactive_info(ob, II_IDLE);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/query_input_pending.c b/secure/simul_efun/spare/spare/query_input_pending.c
new file mode 100644
index 0000000..dd8c311
--- /dev/null
+++ b/secure/simul_efun/spare/spare/query_input_pending.c
@@ -0,0 +1,17 @@
+/* This sefun is to provide a replacement for the efun query_input_pending().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_input_pending)
+
+#include <interactive_info.h>
+
+object query_input_pending(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    return efun::interactive_info(ob, II_INPUT_PENDING);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/query_ip_name.c b/secure/simul_efun/spare/spare/query_ip_name.c
new file mode 100644
index 0000000..1e74ea0
--- /dev/null
+++ b/secure/simul_efun/spare/spare/query_ip_name.c
@@ -0,0 +1,48 @@
+/* This sefun is to provide a replacement for the efuns query_ip_name() and
+ * query_ip_number().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_ip_name)
+
+#include <interactive_info.h>
+
+private varargs string _query_ip_name(object player)
+{
+    object ob = player;
+    ob ||= efun::this_player();
+
+    player = efun::interactive_info(ob, II_IP_ADDRESS);
+    return efun::interactive_info(ob, II_IP_NAME);
+}
+
+private varargs string _query_ip_number(object player)
+{
+    object ob = player;
+    ob ||= efun::this_player();
+
+    player = efun::interactive_info(ob, II_IP_ADDRESS);
+    return efun::interactive_info(ob, II_IP_NUMBER);
+}
+
+// * Herkunfts-Ermittlung
+string query_ip_number(object ob)
+{
+  ob= ob || this_player();
+  if (!objectp(ob) || !interactive(ob)) return 0;
+  if(ob->query_realip() && (string)ob->query_realip()!="")
+  {
+    return (string)ob->query_realip();
+  }
+  return _query_ip_number(ob);
+}
+
+string query_ip_name(mixed ob)
+{
+  if ( !ob || objectp(ob) )
+      ob=_query_ip_number(ob);
+  return (string)"/p/daemon/iplookup"->host(ob);
+}
+
+#endif
+
diff --git a/secure/simul_efun/spare/spare/query_limits.c b/secure/simul_efun/spare/spare/query_limits.c
new file mode 100644
index 0000000..ecbf390
--- /dev/null
+++ b/secure/simul_efun/spare/spare/query_limits.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_limits().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_limits)
+
+#include <driver_info.h>
+
+varargs int* query_limits(int def)
+{
+    return efun::driver_info(def ? DC_DEFAULT_RUNTIME_LIMITS : DI_CURRENT_RUNTIME_LIMITS);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/query_load_average.c b/secure/simul_efun/spare/spare/query_load_average.c
new file mode 100644
index 0000000..3b36f0e
--- /dev/null
+++ b/secure/simul_efun/spare/spare/query_load_average.c
@@ -0,0 +1,16 @@
+/* This sefun is to provide a replacement for the efun query_load_average().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_load_average)
+
+#include <driver_info.h>
+
+string query_load_average()
+{
+    return efun::sprintf("%.2f cmds/s, %.2f comp lines/s",
+        efun::driver_info(DI_LOAD_AVERAGE_COMMANDS),
+        efun::driver_info(DI_LOAD_AVERAGE_LINES));
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/query_mud_port.c b/secure/simul_efun/spare/spare/query_mud_port.c
new file mode 100644
index 0000000..bff009d
--- /dev/null
+++ b/secure/simul_efun/spare/spare/query_mud_port.c
@@ -0,0 +1,35 @@
+/* This sefun is to provide a replacement for the efun query_mud_port().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_mud_port)
+
+#include <interactive_info.h>
+#include <driver_info.h>
+
+int query_mud_port(varargs int*|object* args)
+{
+    if(!sizeof(args) && efun::this_player() && efun::interactive(this_player()))
+        return efun::interactive_info(this_player(), II_MUD_PORT);
+
+    if(sizeof(args) > 1)
+        raise_error("Too many arguments to query_mud_port\n");
+
+    if(sizeof(args) && efun::objectp(args[0]) && efun::interactive(args[0]))
+        return efun::interactive_info(args[0], II_MUD_PORT);
+
+    if(sizeof(args) && intp(args[0]))
+    {
+        int* ports = efun::driver_info(DI_MUD_PORTS);
+        if(args[0] < -1 || args[0] >= sizeof(ports))
+            raise_error(efun::sprintf("Bad arg 1 to query_mud_port(): value %d out of range.\n", args[0]));
+        else if(args[0] == -1)
+            return sizeof(ports);
+        else
+            return ports[args[0]];
+    }
+
+    return efun::driver_info(DI_MUD_PORTS)[0];
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/query_once_interactive.c b/secure/simul_efun/spare/spare/query_once_interactive.c
new file mode 100644
index 0000000..bc7829f
--- /dev/null
+++ b/secure/simul_efun/spare/spare/query_once_interactive.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_once_interactive().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_once_interactive)
+
+#include <object_info.h>
+
+int query_once_interactive(object ob)
+{
+    return efun::object_info(ob, OI_ONCE_INTERACTIVE);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/query_snoop.c b/secure/simul_efun/spare/spare/query_snoop.c
new file mode 100644
index 0000000..e6a69c0
--- /dev/null
+++ b/secure/simul_efun/spare/spare/query_snoop.c
@@ -0,0 +1,30 @@
+/* This sefun is to provide a replacement for the efun query_snoop().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+
+#include <interactive_info.h>
+
+private object _query_snoop(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    object prev = efun::previous_object();
+    efun::set_this_object(prev);
+#if ! __EFUN_DEFINED__(query_snoop)
+    return efun::interactive_info(ob, II_SNOOP_NEXT);
+#else
+    return efun::query_snoop(ob);
+#endif
+}
+
+nomask object query_snoop(object who) {
+  object snooper;
+
+  snooper=_query_snoop(who);
+  if (!snooper) return 0;
+  if (query_wiz_grp(snooper)>query_wiz_grp(getuid(previous_object())) &&
+      IS_ARCH(snooper)) return 0;
+  return snooper;
+}
diff --git a/secure/simul_efun/spare/spare/set_heart_beat.c b/secure/simul_efun/spare/spare/set_heart_beat.c
new file mode 100644
index 0000000..a3995eb
--- /dev/null
+++ b/secure/simul_efun/spare/spare/set_heart_beat.c
@@ -0,0 +1,24 @@
+/* This sefun is to provide a replacement for the efun set_heart_beat().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(set_heart_beat)
+
+#include <configuration.h>
+
+int set_heart_beat(int flag)
+{
+    object ob = efun::previous_object();
+    int hb = efun::object_info(ob, OC_HEART_BEAT);
+
+    if (!flag == !hb)
+        return 0;
+
+    efun::configure_object(ob, OC_HEART_BEAT, flag);
+
+    return 1;
+}
+
+#endif
+
+
diff --git a/secure/simul_efun/spare/spare/set_is_wizard.c b/secure/simul_efun/spare/spare/set_is_wizard.c
new file mode 100644
index 0000000..001ccdb
--- /dev/null
+++ b/secure/simul_efun/spare/spare/set_is_wizard.c
@@ -0,0 +1,137 @@
+/* These are the special commands from the driver that are activated with
+ * set_is_wizard(). These functions must be added to the player object.
+ * Also set_is_wizard() must be called in the corresponding player object,
+ * not as an (simul-)efun.
+ */
+
+#include <commands.h>
+#include <driver_info.h>
+
+/* is_wizard:
+ *  1: has actions,
+ *  0: never had actions,
+ * -1: had actions once.
+ */
+private nosave int is_wizard;
+
+private int driver_malloc(string str)
+{
+    if(is_wizard <= 0)
+        return 0;
+
+    if(!sizeof(str))
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC));
+        return 1;
+    }
+
+    if(str == "extstats")
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC_EXTENDED));
+        return 1;
+    }
+
+    return 0;
+}
+
+private int driver_dumpallobj(string str)
+{
+    if(is_wizard <= 0 || sizeof(str))
+        return 0;
+
+    write("Dumping to /OBJ_DUMP ... ");
+    efun::dump_driver_info(DDI_OBJECTS);
+    efun::dump_driver_info(DDI_OBJECTS_DESTRUCTED);
+    write("done\n");
+    return 1;
+}
+
+private int driver_opcdump(string str)
+{
+    if(is_wizard <= 0 || sizeof(str))
+        return 0;
+
+    efun::dump_driver_info(DDI_OPCODES);
+    return 1;
+}
+
+private int driver_status(string str)
+{
+    int opt;
+    if(is_wizard <= 0)
+        return 0;
+
+    switch(str || "")
+    {
+        case "":
+            opt = DI_STATUS_TEXT_MEMORY;
+            break;
+
+        case "tables":
+        case " tables":
+            opt = DI_STATUS_TEXT_TABLES;
+            break;
+
+        case "swap":
+        case " swap":
+            opt = DI_STATUS_TEXT_SWAP;
+            break;
+
+        case "malloc":
+        case " malloc":
+            opt = DI_STATUS_TEXT_MALLOC;
+            break;
+
+        case "malloc extstats":
+        case " malloc extstats":
+            opt = DI_STATUS_TEXT_MALLOC_EXTENDED;
+            break;
+
+        default:
+            return 0;
+    }
+
+    write(efun::driver_info(opt));
+    return 1;
+}
+
+int set_is_wizard(varargs <object|int>* args)
+{
+    int oldval = is_wizard;
+
+    if(!sizeof(args))
+        raise_error("Too few arguments to set_is_wizard\n");
+    if(sizeof(args) > 2)
+        raise_error("Too many arguments to set_is_wizard\n");
+    if(!objectp(args[0]))
+        raise_error("Bad arg 1 to set_is_wizard()\n");
+    if(args[0] != this_object())
+        raise_error("Only set_is_wizard for the current object supported\n");
+    if(this_player() != this_object())
+        raise_error("The current object must be this_player() for set_is_wizard()\n");
+    if(sizeof(args) == 2 && !intp(args[1]))
+        raise_error("Bad arg 2 to set_is_wizard()\n");
+
+    if(sizeof(args) == 2 && !args[1])
+    {
+        if(is_wizard > 0)
+            is_wizard = -1;
+    }
+    else if(sizeof(args) == 2 && args[1]<0)
+    {
+         // Just return the old value.
+    }
+    else
+    {
+        if(!is_wizard)
+        {
+            add_action(#'driver_malloc, "malloc");
+            add_action(#'driver_dumpallobj, "dumpallobj");
+            add_action(#'driver_opcdump, "opcdump");
+            add_action(#'driver_status, "status", AA_NOSPACE);
+        }
+        is_wizard = 1;
+    }
+
+    return oldval > 0;
+}
diff --git a/secure/simul_efun/spare/spare/set_prompt.c b/secure/simul_efun/spare/spare/set_prompt.c
new file mode 100644
index 0000000..0b59692
--- /dev/null
+++ b/secure/simul_efun/spare/spare/set_prompt.c
@@ -0,0 +1,21 @@
+/* This sefun is to provide a replacement for the efun set_prompt().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(set_prompt)
+
+#include <configuration.h>
+
+varargs string|closure set_prompt(string|closure|int prompt, object ob)
+{
+    ob ||= efun::this_player();
+
+    mixed oldprompt = efun::interactive_info(ob, IC_PROMPT);
+
+    if(!intp(prompt))
+        efun::configure_interactive(ob, IC_PROMPT, prompt);
+
+    return oldprompt;
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/shadow.c b/secure/simul_efun/spare/spare/shadow.c
new file mode 100644
index 0000000..7608c73
--- /dev/null
+++ b/secure/simul_efun/spare/spare/shadow.c
@@ -0,0 +1,55 @@
+/* These sefuns are to provide a replacement for the efun query_shadowing()
+ * and the old semantics of the efun shadow().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_shadowing)
+
+#include <object_info.h>
+
+object query_shadowing(object ob)
+{
+    return efun::object_info(ob, OI_SHADOW_PREV);
+}
+
+private object _shadow(object ob, int flag)
+{
+    if(flag)
+    {
+        object shadower = efun::previous_object(1);
+        efun::set_this_object(shadower);
+
+        if (efun::shadow(ob))
+            return efun::object_info(shadower, OI_SHADOW_PREV);
+        else
+            return 0;
+    }
+    else
+        return efun::object_info(ob, OI_SHADOW_NEXT);
+}
+#else
+private object _shadow(object ob, int flag)
+{
+  set_this_object(previous_object(1));
+  return efun::shadow(ob, flag);
+}
+#endif
+
+
+public object shadow(object ob, int flag)
+{
+  object res = funcall(#'_shadow,ob, flag);
+  if (flag)
+    "/secure/shadowmaster"->RegisterShadow(previous_object());
+  return res;
+}
+
+private void _unshadow() {
+  set_this_object(previous_object(1));
+  efun::unshadow();
+}
+
+public void unshadow() {
+  funcall(#'_unshadow);
+  "/secure/shadowmaster"->UnregisterShadow(previous_object());
+}
diff --git a/secure/simul_efun/spare/spare/simul_efun.c b/secure/simul_efun/spare/spare/simul_efun.c
new file mode 100644
index 0000000..5b68789
--- /dev/null
+++ b/secure/simul_efun/spare/spare/simul_efun.c
@@ -0,0 +1,1995 @@
+// MorgenGrauen MUDlib
+//
+// simul_efun.c -- simul efun's
+//
+// $Id: simul_efun.c 7408 2010-02-06 00:27:25Z Zesstra $
+#pragma strict_types,save_types,rtt_checks
+#pragma no_clone,no_shadow,no_inherit
+#pragma pedantic,range_check,warn_deprecated
+#pragma warn_empty_casts,warn_missing_return,warn_function_inconsistent
+
+// Absolute Pfade erforderlich - zum Zeitpunkt, wo der Master geladen
+// wird, sind noch keine Include-Pfade da ...
+
+#define SNOOPLOGFILE "SNOOP"
+#define ASNOOPLOGFILE "ARCH/SNOOP"
+
+#include "/secure/config.h"
+#include "/secure/wizlevels.h"
+#include "/sys/snooping.h"
+#include "/sys/language.h"
+#include "/sys/thing/properties.h"
+#include "/sys/wizlist.h"
+#include "/sys/erq.h"
+#include "/sys/lpctypes.h"
+#include "/sys/daemon.h"
+#include "/sys/player/base.h"
+#include "/sys/thing/description.h"
+#include "/sys/container.h"
+#include "/sys/defines.h"
+#include "/sys/telnet.h"
+#include "/sys/objectinfo.h"
+#include "/sys/files.h"
+#include "/sys/strings.h"
+#include "/sys/time.h"
+#include "/sys/lpctypes.h"
+#include "/sys/notify_fail.h"
+#include "/sys/tls.h"
+#include "/sys/input_to.h"
+#include "/sys/object_info.h"
+
+/* function prototypes
+ */
+string dtime(int wann);
+varargs int log_file(string file, string txt, int size_to_break);
+int query_wiz_level(mixed player);
+nomask varargs int snoop(object me, object you);
+varargs string country(mixed ip, string num);
+int query_wiz_grp(mixed wiz);
+public varargs object deep_present(mixed what, object ob);
+nomask int secure_level();
+nomask string secure_euid();
+public nomask int process_call();
+nomask mixed __create_player_dummy(string name);
+varargs string replace_personal(string str, mixed *obs, int caps);
+
+
+//replacements for dropped efuns in LD
+#if !__EFUN_DEFINED__(extract)
+varargs string extract(string str, int from, int to);
+#endif
+#if !__EFUN_DEFINED__(slice_array)
+varargs mixed slice_array(mixed array, int from, int to);
+#endif
+#if !__EFUN_DEFINED__(member_array)
+int member_array(mixed item, mixed arraystring);
+#endif
+
+// Include the different sub 'modules' of the simul_efun
+#include __DIR__"debug_info.c"
+#include __DIR__"enable_commands.c"
+#include __DIR__"hash.c"
+#include __DIR__"object_info.c"
+#include __DIR__"query_editing.c"
+#include __DIR__"query_idle.c"
+#include __DIR__"query_input_pending.c"
+#include __DIR__"query_ip_name.c"
+#include __DIR__"query_limits.c"
+#include __DIR__"query_load_average.c"
+#include __DIR__"query_mud_port.c"
+#include __DIR__"query_once_interactive.c"
+#include __DIR__"query_snoop.c"
+#include __DIR__"set_heart_beat.c"
+#if __BOOT_TIME__ < 1456261859
+#include __DIR__"set_prompt.c"
+#endif
+#include __DIR__"shadow.c"
+#include __DIR__"livings.c"
+#include __DIR__"comm.c"
+
+#define TO        efun::this_object()
+#define TI        efun::this_interactive()
+#define TP        efun::this_player()
+#define PO        efun::previous_object(0)
+#define LEVEL(x) query_wiz_level(x)
+#define NAME(x)  capitalize(getuid(x))
+
+#define DEBUG(x) if (find_player("zesstra")) \
+  tell_object(find_player("zesstra"),x)
+
+mixed dtime_cache = ({-1,""});
+
+#ifdef IOSTATS
+struct iostat_s {
+  string oname;
+  int time;
+  int size;
+};
+mixed saveo_stat = ({ 0,allocate(200, 0) });
+mixed restoreo_stat = ({ 0,allocate(200,0) });
+//mixed writefile_stat = ({ 0,allocate(100,(<iostat_s>)) });
+//mixed readfile_stat = ({ 0,allocate(100,(<iostat_s>)) });
+//mixed log_stat = ({ 0,allocate(100,(<iostat_s>)) });
+
+mixed ___iostats(int type) {
+  switch(type) {
+    case 1:
+      return saveo_stat;
+    case 2:
+      return restoreo_stat;
+/*    case 3:
+      return writefile_stat;
+    case 4:
+      return readfile_stat;
+    case 5:
+      return log_stat;
+      */
+  }
+  return 0;
+}
+#endif
+
+// Nicht jeder Magier muss die simul_efun entsorgen koennen.
+string NotifyDestruct(object caller) {
+    if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
+      return "Du darfst das simul_efun Objekt nicht zerstoeren!\n";
+    }
+    return 0;
+}
+
+public nomask void remove_interactive( object ob )
+{
+    if ( objectp(ob) && previous_object()
+        && object_name(previous_object())[0..7] != "/secure/"
+        && ((previous_object() != ob
+             && (ob != this_player() || ob != this_interactive()))
+            || (previous_object() == ob
+               && (this_player() && this_player() != ob
+                   || this_interactive() && this_interactive() != ob)) ) )
+
+       log_file( "PLAYERDEST",
+                sprintf( "%s: %O ausgeloggt von PO %O, TI %O, TP %O\n",
+                        dtime(time()), ob, previous_object(),
+                        this_interactive(), this_player() ) );
+
+    efun::remove_interactive(ob);
+}
+
+
+void ___updmaster()
+{
+    object ob;
+
+    //sollte nicht jeder duerfen.
+    if (process_call() || !ARCH_SECURITY)
+      raise_error("Illegal use of ___updmaster()!");
+
+    write("Removing old master ... ");
+    foreach(string file: 
+        get_dir("/secure/master/*.c",GETDIR_NAMES|GETDIR_UNSORTED|GETDIR_PATH)) {
+      if (ob = find_object(file))
+       efun::destruct(ob);
+    }
+    efun::destruct(efun::find_object("/secure/master"));
+    write("done.\nLoading again ... ");
+    load_object("/secure/master");
+
+    write("done.\n");
+}
+
+varargs string country(mixed ip, string num) {
+  mixed ret;
+
+  if(ret = (string)"/p/daemon/iplookup"->country(num || ip)) {
+    return ret;
+  } else return "???";
+}
+
+
+// * Snoopen und was dazugehoert
+static object find_snooped(object who)
+{
+  object *u;
+  int i;
+
+  for (i=sizeof(u=users())-1;i>=0;i--)
+    if (who==efun::interactive_info(u[i], II_SNOOP_NEXT))
+      return u[i];
+  return 0;
+}
+
+private string Lcut(string str) {
+  return str[5..11]+str[18..];
+}
+
+nomask varargs int snoop( object me, object you )
+{
+    int ret;
+    object snooper0, snooper, snooper2, snooper3;
+
+    if( !objectp(me) || me == you || !PO )
+       return 0;
+
+    snooper0 = find_snooped(me);
+
+     if(you){
+        if ( PO != me && query_wiz_grp(me) >= query_wiz_grp(geteuid(PO)) )
+            return 0;
+
+        if ( query_wiz_grp(me) <= query_wiz_grp(you) &&
+             !(you->QueryAllowSnoop(me)) )
+            if ( !IS_DEPUTY(me) || IS_ARCH(you) )
+               return 0;
+
+        if ( (snooper = efun::interactive_info(you, II_SNOOP_NEXT)) &&
+             query_wiz_grp(snooper) >= query_wiz_grp(me) ){
+            if ( (int)snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
+               return 0;
+
+            tell_object( snooper, sprintf( "%s snooped jetzt %s.\n",
+                                       me->name(WER), you->name(WER) ) );
+
+            snooper2 = me;
+
+            while ( snooper3 = interactive_info(snooper2, II_SNOOP_NEXT) ){
+               tell_object( snooper,
+                           sprintf( "%s wird seinerseits von %s gesnooped.\n"
+                                   ,snooper2->name(WER),
+                                   snooper3->name(WEM) ) );
+               snooper2 = snooper3;
+            }
+
+            efun::snoop( snooper, snooper2 );
+
+            if ( efun::interactive_info(snooper2, II_SNOOP_NEXT) != snooper )
+               tell_object( snooper, sprintf( "Du kannst %s nicht snoopen.\n",
+                                          snooper2->name(WEN) ) );
+            else{
+               tell_object( snooper, sprintf( "Du snoopst jetzt %s.\n",
+                                          snooper2->name(WEN) ) );
+               if ( !IS_DEPUTY(snooper) ){
+                   log_file( SNOOPLOGFILE, sprintf("%s: %O %O %O\n",
+                                               dtime(time()),
+                                               snooper,
+                                               snooper2,
+                                               environment(snooper2) ),
+                            100000 );
+                   if (snooper0)
+                      CHMASTER->send( "Snoop", snooper,
+                                    sprintf( "%s *OFF* %s (%O)",
+                                            capitalize(getuid(snooper)),
+                                            capitalize(getuid(snooper0)),
+                                            environment(snooper0) ) );
+
+                   CHMASTER->send( "Snoop", snooper,
+                                 sprintf("%s -> %s (%O)",
+                                        capitalize(getuid(snooper)),
+                                        capitalize(getuid(snooper2)),
+                                        environment(snooper2)));
+               }
+               else{
+                   log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                                 dtime(time()),
+                                                 snooper,
+                                                 snooper2,
+                                                 environment(snooper2) )
+                            ,100000 );
+               }
+            }
+        }
+        else
+            if (snooper)
+               if ( !me->QueryProp(P_SNOOPFLAGS) & SF_LOCKED ){
+                   printf( "%s wird bereits von %s gesnooped. Benutze das "
+                          "\"f\"-Flag, wenn du dennoch snoopen willst.\n",
+                          you->name(WER), snooper->name(WEM) );
+                   return 0;
+               }
+
+        ret = efun::snoop( me, you );
+
+        if ( !IS_DEPUTY(me) && efun::interactive_info(you, II_SNOOP_NEXT) == me){
+            log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                         Lcut(dtime(time())),
+                                         me, you, environment(you) ),
+                     100000 );
+
+            if (snooper0)
+               CHMASTER->send( "Snoop", me,
+                             sprintf( "%s *OFF* %s (%O).",
+                                     capitalize(getuid(me)),
+                                     capitalize(getuid(snooper0)),
+                                     environment(snooper0) ) );
+
+            CHMASTER->send( "Snoop", me, sprintf( "%s -> %s (%O).",
+                                             capitalize(getuid(me)),
+                                             capitalize(getuid(you)),
+                                             environment(you) ) );
+        }
+        else{
+            if ( efun::interactive_info(you, II_SNOOP_NEXT) == me ){
+               log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                             Lcut(dtime(time())),
+                                             me, you, environment(you) ),
+                        100000 );
+            }
+        }
+
+        if ( ret && query_wiz_grp(me) <= query_wiz_grp(you) &&
+             !IS_DEPUTY(me) )
+            tell_object( you, "*** " + NAME(me) + " snoopt Dich!\n" );
+
+        return ret;
+     }
+     else {
+        if ( (me == PO ||
+              query_wiz_grp(geteuid(PO)) > query_wiz_grp(me) ||
+              (query_wiz_grp(geteuid(PO)) == query_wiz_grp(me) &&
+              efun::interactive_info(PO, II_SNOOP_NEXT) == me)) && snooper0 ){
+            if ( !IS_DEPUTY(me) ){
+               log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
+                                            Lcut(dtime(time())), me,
+                                            snooper0,
+                                            environment(snooper0) ),
+                        100000 );
+
+                CHMASTER->send( "Snoop", me,
+                              sprintf( "%s *OFF* %s (%O).",
+                                      capitalize(getuid(me)),
+                                      capitalize(getuid(snooper0)),
+                                      environment(snooper0) ) );
+            }
+            else{
+               log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
+                                             Lcut(dtime(time())), me,
+                                             snooper0,
+                                             environment(snooper0) ),
+                        100000 );
+            }
+
+            return efun::snoop(me);
+        }
+     }
+     return 0;
+}
+
+
+
+// * Emulation des 'alten' explode durch das neue
+string *old_explode(string str, string del) {
+  int s, t;
+  string *strs;
+
+  if (!stringp(str)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Invalid argument 1 to old_explode()! Expected <string>, got: "
+         "%.30O\n",str));
+  }
+  if (!stringp(del)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Invalid argument 2 to old_explode()! Expected <string>, got: "
+         "%.30O\n",del));
+  }
+  if(del == "")
+    return ({str});
+  strs=efun::explode(str, del);
+  t=sizeof(strs)-1;
+  while(s<=t && strs[s++] == "");s--;
+  while(t>=0 && strs[t--] == "");t++;
+  if(s<=t)
+    return strs[s..t];
+  return ({});
+}
+
+int file_time(string path) {
+  mixed *v;
+
+  set_this_object(previous_object());
+  if (sizeof(v=get_dir(path,GETDIR_DATES))) return v[0];
+  return(0); //sonst
+}
+
+// * Bei 50k Groesse Log-File rotieren
+varargs int log_file(string file, string txt, int size_to_break) {
+  mixed *st;
+
+  file="/log/"+file;
+  file=implode((efun::explode(file,"/")-({".."})),"/");
+//  tell_object(find_player("jof"),sprintf("LOG FILE: %O -> %O\n",previous_object(),file));
+  if (!funcall(bind_lambda(#'efun::call_other,PO),"secure/master",//')
+              "valid_write",file,geteuid(PO),"log_file",PO))
+      return 0;
+  if ( size_to_break >= 0 & (
+      sizeof(st = get_dir(file,2) ) && st[0] >= (size_to_break|MAX_LOG_SIZE)))
+      catch(rename(file, file + ".old");publish); /* No panic if failure */
+  return(write_file(file,txt));
+}
+
+// * Magier-Level abfragen
+int query_wiz_level(mixed player) {
+  return (int)"/secure/master"->query_wiz_level(player);
+}
+
+#ifdef __ALISTS__
+// * Element aus Alist loeschen (by key)
+mixed *remove_alist(mixed key,mixed *alist)
+{
+  int i,j;
+
+  if (!pointerp(alist) || !sizeof(alist))
+    return alist;
+  if (!pointerp(alist[0]))
+  {
+    if ((i=assoc(key,alist))<0)
+      return alist;
+    return alist[0..i-1]+alist[i+1..];
+  }
+  i = assoc(key,alist[0]);
+  if ((i=assoc(key,alist[0]))<0)
+    return alist;
+  alist=alist[0..];
+  for (j=sizeof(alist)-1;j>=0;j--)
+    alist[j]=alist[j][0..i-1]+alist[j][i+1..];
+  return alist;
+}
+
+// * Element aus Alist loeschen (by pos)
+mixed *exclude_alist(int i,mixed *alist)
+{
+  int j;
+  if (!pointerp(alist) || !sizeof(alist) || i<0)
+    return alist;
+  if (!pointerp(alist[0]))
+    return alist[0..i-1]+alist[i+1..];
+  alist=alist[0..]; /* Create PHYSICAL copy of alist */
+  for (j=sizeof(alist)-1;j>=0;j--)
+    alist[j]=alist[j][0..i-1]+alist[j][i+1..];
+  return alist; /* order_alist is NOT necessary - see /doc/LPC/alist */
+}
+#endif // __ALISTS__
+
+// * German version of ctime()
+#define TAGE ({"Son","Mon","Die","Mit","Don","Fre","Sam"})
+#define MONATE ({"Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug",\
+                 "Sep","Okt","Nov","Dez"})
+string dtime(int wann) {
+  
+  if (wann == dtime_cache[0])
+    return(dtime_cache[1]);
+
+  int *lt = localtime(wann);
+  return sprintf("%s, %2d. %s %d, %02d:%02d:%02d",
+      TAGE[lt[TM_WDAY]], lt[TM_MDAY], MONATE[lt[TM_MON]], 
+      lt[TM_YEAR],lt[TM_HOUR], lt[TM_MIN], lt[TM_SEC]);
+}
+
+// wenn strftime im Driver nicht existiert, ist dies hier ein Alias auf dtime(),
+// zwar stimmt das Format dann nicht, aber die Mudlib buggt nicht und schreibt
+// ein ordentliches Datum/Uhrzeit.
+#if !__EFUN_DEFINED__(strftime)
+varargs string strftime(mixed fmt, int clock, int localized) {
+  if (intp(clock) && clock >= 0)
+    return dtime(clock);
+  else if (intp(fmt) && fmt >= 0)
+    return dtime(fmt);
+  
+  return dtime(time());
+}
+#endif //!__EFUN_DEFINED__(strftime)
+
+// * Shutdown mit zusaetzlichem logging
+nomask int shutdown(string reason)
+{
+  string name;
+  string obname;
+  string output;
+
+  if (!reason)
+    return 0;
+  if ( !ARCH_SECURITY && getuid(previous_object())!=ROOTID &&
+          object_name(previous_object())!="/obj/shut" )
+  {
+    write("You have no permission to shut down the gamedriver!\n");
+    return 0;
+  }
+  if ((this_interactive())&&(name=getuid(this_interactive())))
+  {
+    name=capitalize(name);
+    filter(users(),#'tell_object,//'
+               capitalize(name)+" faehrt das Spiel herunter!\n");
+  }
+  else
+    name="ANONYMOUS";
+  if (previous_object()) obname=capitalize(getuid(previous_object()));
+  output=name;
+  if (obname && name!=obname) output=output+" ("+obname+")";
+  if (previous_object()&&object_name(previous_object())=="/obj/shut"){
+    output+=" faehrt das Spiel via Armageddon herunter.\n";
+    output=dtime(time())+": "+output;
+    log_file("GAME_LOG",output+"\n",-1);
+    efun::shutdown();
+    return 1;
+  }
+  output=ctime(time())+": "+output+" faehrt das Spiel herunter.\n";
+  output+="    Grund: "+reason;
+  log_file("GAME_LOG",output+"\n",-1);
+  efun::shutdown();
+  return 1;
+}
+
+// * lowerchar
+
+int lowerchar(int char) {
+  if (char<'A' || char>'Z') return char;
+  return char+32;
+}
+
+// * upperstring
+
+string upperstring(string s)
+{
+#if __EFUN_DEFINED__(upper_case)
+  return(upper_case(s));
+#else
+  int i;
+  if (!stringp(s)) return 0;
+  for (i=sizeof(s)-1;i>=0;i--) s[i]=((s[i]<'a'||s[i]>'z')?s[i]:s[i]-32);
+  return s;
+#endif
+}
+
+// * lowerstring
+
+string lowerstring(string s)
+{
+  if (!stringp(s)) return 0;
+  return lower_case(s);
+}
+
+
+// * GD version
+string version()
+{
+  return __VERSION__;
+}
+
+// * break_string
+// stretch() -- stretch a line to fill a given width 
+private string stretch(string s, int width) {
+  int len=sizeof(s);
+  if (len==width) return s;
+
+  // reine Leerzeilen, direkt zurueckgeben
+  string trimmed=trim(s,TRIM_LEFT," ");
+  if (trimmed=="") return s; 
+  int start_spaces = len - sizeof(trimmed);
+
+  string* words = explode(trimmed, " ");
+  // der letzte kriegt keine Spaces
+  int word_count=sizeof(words) - 1;
+  // wenn Zeile nur aus einem Wort, wird das Wort zurueckgegeben
+  if (!word_count)
+    return " "*start_spaces + words[0];
+
+  int space_count = width - len;
+
+  int space_per_word=(word_count+space_count) / word_count;
+  // Anz.Woerter mit Zusatz-Space
+  int rest=(word_count+space_count) % word_count; 
+  // Rest-Spaces Verteilen
+  foreach (int pos : rest) words[pos]+=" ";
+  return (" "*start_spaces) + implode( words, " "*space_per_word );
+}
+
+// aus Geschwindigkeitsgruenden hat der Blocksatz fuer break_string eine
+// eigene Funktion bekommen:
+private varargs string block_string(string s, int width, int flags) {
+  // wenn BS_LEAVE_MY_LFS, aber kein BS_NO_PARINDENT, dann werden Zeilen mit
+  // einem Leerzeichen begonnen.
+  // BTW: Wenn !BS_LEAVE_MY_LFS, hat der Aufrufer bereits alle \n durch " "
+  // ersetzt.
+  if ( (flags & BS_LEAVE_MY_LFS)
+      && !(flags & BS_NO_PARINDENT))
+  {
+      s = " "+regreplace(s,"\n","\n ",1);
+  }
+
+  // sprintf fuellt die letzte Zeile auf die Feldbreite (hier also
+  // Zeilenbreite) mit Fuellzeichen auf, wenn sie NICHT mit \n umgebrochen
+  // ist. Es wird an die letzte Zeile aber kein Zeilenumbruch angehaengt.
+  // Eigentlich ist das Auffuellen doof, aber vermutlich ist es unnoetig, es
+  // wieder rueckgaengig zu machen.
+  s = sprintf( "%-*=s", width, s);
+
+  string *tmp=explode(s, "\n");
+  // Nur wenn s mehrzeilig ist, Blocksatz draus machen. Die letzte Zeile wird
+  // natuerlich nicht gedehnt. Sie ist dafuer schon von sprintf() aufgefuellt
+  // worden. BTW: Die letzte Zeile endet u.U. noch nicht mit einem \n (bzw.
+  // nur dann, wenn BS_LEAVE_MY_LFS und der uebergebene Text schon nen \n am
+  // Ende der letzten Zeile hat), das macht der Aufrufer...
+  if (sizeof(tmp) > 1)
+    return implode( map( tmp[0..<2], #'stretch/*'*/, width ), "\n" ) 
+      + "\n" + tmp[<1];
+
+  return s;
+}
+
+public varargs string break_string(string s, int w, mixed indent, int flags)
+{
+    if ( !s || s == "" ) return "";
+
+    if ( !w ) w=78;
+
+    if( intp(indent) )
+       indent = indent ? " "*indent : "";
+
+    int indentlen=stringp(indent) ? sizeof(indent) : 0;
+
+    if (indentlen>w) {
+      set_this_object(previous_object());
+      raise_error(sprintf("break_string: indent longer %d than width %d\n",
+                  indentlen,w));
+      // w=((indentlen/w)+1)*w;
+    }
+
+    if (!(flags & BS_LEAVE_MY_LFS)) 
+      s=regreplace( s, "\n", " ", 1 );
+
+    if ( flags & BS_SINGLE_SPACE )
+       s = regreplace( s, "(^|\n| )  *", "\\1", 1 );
+ 
+    string prefix="";
+    if (indentlen && flags & BS_PREPEND_INDENT) {
+      if (indentlen+sizeof(s) > w || 
+         (flags & BS_LEAVE_MY_LFS) && strstr(s,"\n")>-1) {
+       prefix=indent+"\n";
+       indent=(flags & BS_NO_PARINDENT) ? "" : " ";
+       indentlen=sizeof(indent);
+      }
+    }
+
+    if ( flags & BS_BLOCK ) {
+      /*
+           s = implode( map( explode( s, "\n" ),
+                               #'block_string, w, indentlen, flags),
+                      "" );
+      */
+      s = block_string( s , w - indentlen, flags );
+    }
+    else {
+      s = sprintf("%-1.*=s",w-indentlen,s);
+    }
+    if ( s[<1] != '\n' ) s += "\n";
+
+    if ( !indentlen ) return prefix + s;
+    
+    string indent2 = ( flags & BS_INDENT_ONCE ) ? (" "*indentlen) :indent;
+      
+    return prefix + indent + 
+      regreplace( s[0..<2], "\n", "\n"+indent2, 1 ) + "\n";
+      /*
+       string *buf;
+
+       buf = explode( s, "\n" );
+       return prefix + indent + implode( buf[0..<2], "\n"+indent2 ) + buf[<1] + "\n";
+      */
+}
+
+// * Elemente aus mapping loeschen - mapping vorher kopieren
+
+mapping m_copy_delete(mapping m, mixed key) {
+  return m_delete(copy(m), key);
+}
+
+// * times
+int last_reboot_time()
+{
+  return __BOOT_TIME__;
+}
+
+int first_boot_time()
+{
+  return 701517600;
+}
+
+int exist_days()
+{
+  return (((time()-first_boot_time())/8640)+5)/10;
+}
+
+// * uptime :)
+string uptime()
+{
+  int t;
+  int tmp;
+  string s;
+
+  t=time()-__BOOT_TIME__;
+  s="";
+  if (t>=86400)
+    s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
+  if (t>=3600)
+    s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
+  if (t>60)
+    s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
+  return s+sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
+}
+
+// * Was tun bei 'dangling' lfun-closures ?
+void dangling_lfun_closure() {
+  raise_error("dangling lfun closure\n");
+}
+
+// * Sperren ausser fuer master/simul_efun
+
+#if __EFUN_DEFINED__(set_environment)
+nomask void set_environment(object o1, object o2) {
+  raise_error("Available only for root\n");
+}
+#endif
+
+nomask void set_this_player(object pl) {
+  raise_error("Available only for root\n");
+}
+
+#if __EFUN_DEFINED__(export_uid)
+nomask void export_uid(object ob) {
+  raise_error("Available only for root\n");
+}
+#endif
+
+// * Jetzt auch closures
+int process_flag;
+
+public nomask int process_call()
+{
+  if (process_flag>0)
+    return process_flag;
+  else return(0);
+}
+
+private nomask string _process_string(string str,object po) {
+              set_this_object(po);
+              return(efun::process_string(str));
+}
+
+nomask string process_string( mixed str )
+{
+  string tmp, err;
+  int flag; 
+
+  if ( closurep(str) ) {
+      set_this_object( previous_object() );
+      return funcall(str);
+  }
+  else if (str==0)
+      return((string)str);
+  else if ( !stringp(str) ) {
+      return to_string(str);
+  }
+
+  if ( !(flag = process_flag > time() - 60))                     
+      process_flag=time();
+
+  err = catch(tmp = funcall(#'_process_string,str,previous_object()); publish);
+
+  if ( !flag )
+    process_flag=0;
+
+  if (err) {
+    // Verarbeitung abbrechen
+    set_this_object(previous_object());
+    raise_error(err);
+  }
+  return tmp;
+}
+
+// 'mkdir -p' - erzeugt eine komplette Hierarchie von Verzeichnissen.
+// wenn das Verzeichnis angelegt wurde oder schon existiert, wird 1
+// zurueckgeliefert, sonst 0.
+// Wirft einen Fehler, wenn das angebene Verzeichnis nicht absolut ist!
+public int mkdirp(string dir) {
+  // wenn es nur keinen fuehrenden / gibt, ist das ein Fehler.
+  if (strstr(dir, "/") != 0)
+    raise_error("mkdirp(): Pfad ist nicht absolute.\n");
+  // cut off trailing /...
+  if (dir[<1]=='/')
+      dir = dir[0..<2];
+
+  int fstat = file_size(dir);
+  // wenn es schon existiert, tun wir einfach so, als haetten wir es angelegt.
+  if (fstat == FSIZE_DIR)
+    return 1;
+  // wenn schon ne Datei existiert, geht es nicht.
+  if (fstat != FSIZE_NOFILE)
+    return 0;
+  // wenn es nur einen / gibt (den fuehrenden), dann ist es ein
+  // toplevel-verzeichnis, was direkt angelegt wird.
+  if (strrstr(dir,"/")==0) {
+    return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
+  }
+
+  // mkdir() nicht direkt rufen, sondern vorher als closure ans aufrufende
+  // Objekt binden. Sonst laeuft die Rechtepruefung in valid_write() im Master
+  // unter der Annahme, dass die simul_efun.c mit ihrer root id was will.
+
+  // jetzt rekursiv die Verzeichnishierarchie anlegen. Wenn das erfolgreich
+  // ist, dann koennen wir jetzt mit mkdir das tiefste Verzeichnis anlegen
+  if (mkdirp(dir[0..strrstr(dir,"/")-1]) == 1)
+    return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
+}
+
+
+// * Properties ggfs. mitspeichern
+mixed save_object(mixed name)
+{
+  mapping properties;
+  mapping save;
+  mixed index, res;
+  int i;
+
+  // nur Strings und 0 zulassen
+  if ((!stringp(name) || !sizeof(name)) && 
+      (!intp(name) || name!=0)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Only non-empty strings and 0 may be used as filename in "
+         "sefun::save_object()! Argument was %O\n",name));
+  }
+
+  save = m_allocate(0, 2);
+  properties = (mapping)previous_object()->QueryProperties();
+
+  if(mappingp(properties))
+  {
+    // delete all entries in mapping properties without SAVE flag!
+    index = m_indices(properties);
+    for(i = sizeof(index)-1; i>=0;i--)
+    {
+      if(properties[index[i], F_MODE] & SAVE)
+      {
+       save[index[i]] = properties[index[i]];
+       save[index[i], F_MODE] =
+       properties[index[i], F_MODE] &
+                    (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED));
+      }
+    }
+  }
+  else save = ([]);
+
+  // save object!
+  previous_object()->_set_save_data(save);
+  // format: wie definiert in config.h
+  if (stringp(name))
+    res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
+       __LIB__SAVE_FORMAT_VERSION__);
+  else
+    res = funcall(bind_lambda(#'efun::save_object, previous_object()),
+       __LIB__SAVE_FORMAT_VERSION__);
+  previous_object()->_set_save_data(0);
+
+#ifdef IOSTATS
+  // Stats...
+  struct iostat_s stat = (<iostat_s>);
+  stat->oname = object_name(previous_object());
+  stat->time = time();
+  //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
+  //    OIM_TOTAL_DATA_SIZE);
+  if (stringp(name))
+      stat->size = file_size(name + ".o");
+  else
+      stat->sizeof(res);
+  //debug_message("saveo: "+saveo_stat[0]+"\n");
+  saveo_stat[1][saveo_stat[0]] = stat;
+  saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
+  //debug_message("saveo 2: "+saveo_stat[0]+"\n");
+#endif
+
+  return res;
+}
+
+// * Auch Properties laden
+int restore_object(string name)
+{
+  int result;
+  mixed index;
+  mixed save;
+  mapping properties;
+  int i;
+  closure cl;
+
+  // get actual property settings (by create())
+  properties = (mapping)previous_object()->QueryProperties();
+
+//  DEBUG(sprintf("RESTORE %O\n",name));
+  // restore object
+  result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
+  //'))
+  //_get_save_data liefert tatsaechlich mixed zurueck, wenn das auch immer ein 
+  //mapping sein sollte.
+  save = (mixed)previous_object()->_get_save_data();
+  if(mappingp(save))
+  {
+    index = m_indices(save);
+    for(i = sizeof(index)-1; i>=0; i--)
+    {
+      properties[index[i]] = save[index[i]];
+      properties[index[i], F_MODE] = save[index[i], F_MODE]
+                            &~(SETCACHED|QUERYCACHED);
+    }
+  }
+  else properties = ([]);
+
+  // restore properties
+  funcall(
+          bind_lambda(
+                     unbound_lambda(({'arg}), //'})
+                                  ({#'call_other,({#'this_object}),
+                                  "SetProperties",'arg})),//')
+                     previous_object()),properties);
+  previous_object()->_set_save_data(0);
+
+#ifdef IOSTATS
+  // Stats...
+  //debug_message("restoreo: "+restoreo_stat[0]+"\n");
+  struct iostat_s stat = (<iostat_s>);
+  stat->oname = object_name(previous_object());
+  stat->time = time();
+  //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
+  //    OIM_TOTAL_DATA_SIZE);
+  stat->size = file_size(name + ".o");
+  restoreo_stat[1][restoreo_stat[0]] = stat;
+
+  restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
+#endif
+
+  return result;
+}
+
+// * HB eines Objektes ein/ausschalten
+int set_object_heart_beat(object ob, int flag)
+{
+  if (objectp(ob))
+    return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
+}
+
+// * Magierlevelgruppen ermitteln
+int query_wiz_grp(mixed wiz)
+{
+  int lev;
+
+  lev=query_wiz_level(wiz);
+  if (lev<SEER_LVL) return 0;
+  if (lev>=GOD_LVL) return lev;
+  if (lev>=ARCH_LVL) return ARCH_GRP;
+  if (lev>=ELDER_LVL) return ELDER_GRP;
+  if (lev>=LORD_LVL) return LORD_GRP;
+  if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
+  if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
+  if (lev>=WIZARD_LVL) return WIZARD_GRP;
+  if (lev>=LEARNER_LVL) return LEARNER_GRP;
+  return SEER_GRP;
+}
+
+mixed *wizlist_info()
+{
+  if (ARCH_SECURITY || !extern_call())
+            return efun::wizlist_info();
+  return 0;
+}
+
+// * wizlist ausgeben
+varargs void wizlist(string name, int sortkey ) {
+
+  if (!name)
+  {
+    if (this_player())
+      name = getuid(this_player());
+    if (!name)
+      return;
+  }
+
+  // Schluessel darf nur in einem gueltigen Bereich sein
+  if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
+
+  mixed** wl = efun::wizlist_info();
+  // nach <sortkey> sortieren
+  wl = sort_array(wl, function int (mixed a, mixed b)
+      {return a[sortkey] < b[sortkey]; } );
+
+  // Summe ueber alle Kommandos ermitteln.
+  int total_cmd, i;
+  int pos=-1;
+  foreach(mixed entry : wl)
+  {
+    total_cmd += entry[WL_COMMANDS];
+    if (entry[WL_NAME] == name)
+      pos = i;
+    ++i;
+  }
+
+  if (pos < 0 && name != "ALL" && name != "TOP100")
+    return;
+
+  if (name == "TOP100")
+  {
+    if (sizeof(wl) > 100)
+      wl = wl[0..100];
+    else
+      wl = wl;
+  }
+  // um name herum schneiden
+  else if (name != "ALL")
+  {
+    if (sizeof(wl) <= 21)
+      wl = wl;
+    else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
+      wl = wl[pos-10..pos+10];
+    else if (pos <=21)
+      wl = wl[0..20];
+    else if (pos >= sizeof(wl) - 21)
+      wl = wl[<21..];
+    else
+      wl = wl;
+  }
+
+  write("\nWizard top score list\n\n");
+  if (total_cmd == 0)
+    total_cmd = 1;
+  printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
+         "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
+  foreach(mixed e: wl)
+  {
+    printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
+          e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
+          e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
+          e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
+          );
+  }
+  printf("\nTotal         %7d         (%d)\n\n", total_cmd, sizeof(wl));
+}
+
+
+// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
+
+// ermittelt das Objekt des Callouts.
+private object _call_out_obj( mixed call_out ) {
+    return pointerp(call_out) ? call_out[0] : 0;
+}
+
+private void _same_object( object ob, mapping m ) {
+  // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
+  if ( m[ob] )
+    m[ob] += ({ ob });
+  else
+    m[ob] = ({ ob }); 
+}
+
+// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
+// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
+// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
+private void _same_path( object key, object *obs, mapping m ) {
+  string path;
+  if (!objectp(key) || !pointerp(obs)) return;
+
+  path = load_name(key);
+
+  if ( m[path] )
+    m[path] += obs;
+  else
+    m[path] = obs;
+}
+
+// key kann object oder string sein.
+private int _too_many( mixed key, mapping m, int i ) {
+    return sizeof(m[key]) >= i;
+}
+
+// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
+// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
+// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
+private void _destroy( mixed key, object *obs, string text, int uid ) {
+    if (!pointerp(obs)) return;
+    // Array mit unique Eintraege erzeugen.
+    obs = m_indices( mkmapping(obs) );
+    // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
+    // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
+    // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
+    // laeuft.
+    catch( efun::raise_error(           
+         sprintf( text,                   
+           uid ? (string)master()->creator_file(key) : key,                   
+           sizeof(obs), object_name(obs[<1]) ) );publish);
+    // Und weg mit dem Kram...
+    filter( obs, #'efun::destruct/*'*/ );
+}
+
+// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
+// sind dabei nicht unique.
+private void _same_uid( string key, object *obs, mapping m, closure cf ) {
+  string uid;
+
+  if ( !pointerp(obs) || !sizeof(obs) )
+    return;
+
+  uid = funcall( cf, key );
+
+  if ( m[uid] )
+    m[uid] += obs; // obs ist nen Array
+  else
+    m[uid] = obs;
+}
+
+nomask varargs void call_out( varargs mixed *args )
+{
+    mixed tmp, *call_outs;
+
+    // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
+    // haben.
+    if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
+        && geteuid(previous_object()) != ROOTID )
+    {
+       // Log erzeugen...
+      
+       // Objekte aller Callouts ermitteln
+       call_outs = map( efun::call_out_info(), #'_call_out_obj );
+       mapping objectmap = ([]);
+       filter( call_outs, #'_same_object, &objectmap );
+       // Master nicht grillen...
+       efun::m_delete( objectmap, master(1) );
+       // alle Objekte raussuchen, die zuviele haben...
+       mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
+       // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
+       // Objekten uebergeben (in diesem Fall sind Keys und Array mit
+       // Objekten jeweils das gleiche Objekt).
+       if ( sizeof(res) )       
+           walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "             
+              "object [%O]. Destructed %d objects. [%s]\n", 0 );
+
+       // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
+       // Objekte killen, die mehr als 50 Callouts laufen haben - und
+       // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
+       // Clones einer BP gekillt, die Callouts laufen haben, falls alle
+       // diese Objekte _zusammen_ mehr als 50 Callouts haben!
+       if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
+           // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
+           // mehr drin, da sie dort als Keys verwendet wurden.
+           mapping pathmap=([]);
+           // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
+           // von Objekten als Werte.
+           walk_mapping( objectmap, #'_same_path, &pathmap);
+           // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
+           res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
+           // und ueber alle Keys gehen, an _destroy() werden die Clones
+           // uebergeben, die Callouts haben.
+           if ( sizeof(res) )
+              walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
+                           "'%s'. Destructed %d objects. [%s]\n", 0 );
+
+           // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
+           // noch >1000 Callouts laufen, werden diesmal alle Callouts
+           // einer UID zusammengezaehlt.
+           // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
+           // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
+           if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
+           {
+              // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
+              // zusammensortieren. Zerstoerte Clones filter _same_uid()
+              // raus.
+              mapping uidmap=([]);
+              walk_mapping( pathmap, #'_same_uid, &uidmap,
+                           symbol_function( "creator_file",
+                                          "/secure/master" ) );
+              // In res nun UIDs als Keys und Arrays von Objekten als Werte.
+              // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
+              // 100 Callouts!) haben.
+              res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
+              // und erneut ueber die Keys laufen und jeweils die Arrays mit
+              // den Objekten zur Zerstoerung an _destroy()...
+              if ( sizeof(res) )
+                  walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
+                              "UID '%s'. Destructed %d objects. [%s]\n",
+                              1 );
+           }
+       }
+    }
+
+    // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
+    if ( !previous_object() )
+       return;
+
+    set_this_object( previous_object() );
+    apply( #'efun::call_out, args );
+    return;
+}
+
+mixed call_out_info() {
+  
+  object po = previous_object();
+  mixed coi = efun::call_out_info();
+
+  // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
+  // /obj haben die BackboneID.
+  if (query_wiz_level(getuid(po)) >= ARCH_LVL
+       || (string)master()->creator_file(load_name(po)) == BACKBONEID ) {
+      return coi;
+  }
+  else {
+      return filter(coi, function mixed (mixed arr) {
+              if (pointerp(arr) && arr[0]==po)
+                 return 1;
+              else return 0; });
+  }
+}
+
+// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
+mixed query_closure_object(closure c) {
+  return
+    CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
+      0 :
+  (to_object(c) || -1);
+}
+
+// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
+varargs void move_object(mixed what, mixed where)
+{
+  object po,tmp;
+
+  po=previous_object();
+  if (!where)
+  {
+    where=what;
+    what=po;
+  }
+  if (((stringp(where) && where==NETDEAD_ROOM ) ||
+       (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
+       objectp(what) && object_name(what)!="/obj/sperrer")
+  {
+    if (!query_once_interactive(what))
+    {
+      what->remove();
+      if (what) destruct(what);
+      return;
+    }
+    if (living(what) || interactive(what))
+    {
+      log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
+      return;
+    }
+    set_object_heart_beat(what,0);
+  }
+  tmp=what;
+  while (tmp=environment(tmp))
+      // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+      // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+      // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+      // da um jedes bisschen Rechenzeit geht.
+      // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+      //
+      // Tiamak
+    tmp->_set_last_content_change();
+  funcall(bind_lambda(#'efun::move_object,po),what,where);
+  if (tmp=what)
+    while (tmp=environment(tmp))
+      tmp->_set_last_content_change();
+}
+
+
+void start_simul_efun() {
+  mixed *info;
+
+  // Falls noch nicht getan, extra_wizinfo initialisieren
+  if ( !pointerp(info = get_extra_wizinfo(0)) )
+    set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
+
+  InitLivingData(info);
+
+  set_next_reset(10); // direkt mal aufraeumen
+}
+
+protected void reset() {
+  set_next_reset(7200);
+  CleanLivingData();
+}
+
+#if !__EFUN_DEFINED__(absolute_hb_count)
+int absolute_hb_count() {
+  return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+}
+#endif
+
+void __set_environment(object ob, mixed target)
+{
+  string path;
+  object obj;
+
+  if (!objectp(ob))
+    return;
+  if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
+    return;
+  if (objectp(target))
+  {
+    efun::set_environment(ob,target);
+    return;
+  }
+  path=(string)MASTER->_get_path(target,this_interactive());
+  if (stringp(path) && file_size(path+".c")>=0 &&
+      !catch(load_object(path);publish) )
+  {
+    obj=find_object(path);
+    efun::set_environment(ob,obj);
+    return;
+  }
+}
+
+void _dump_wizlist(string file, int sortby) {
+  int i;
+  mixed *a;
+
+  if (!LORD_SECURITY)
+    return;
+  if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
+  {
+    write("NO WRITE PERMISSION\n");
+    return;
+  }
+  a = wizlist_info();
+  a = sort_array(a, lambda( ({'a,'b}),
+                        ({#'<,
+                          ({#'[,'a,sortby}),
+                          ({#'[,'b,sortby})
+                         })));
+  rm(file);
+  for (i=sizeof(a)-1;i>=0;i--)
+    write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
+      a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
+      a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
+}
+
+public varargs object deep_present(mixed what, object ob) {
+
+  if(!objectp(ob))
+    ob=previous_object();
+  // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
+  // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
+  if(objectp(what)) {
+    object *envs=all_environment(what);
+    // wenn ob kein Environment hat, ist es offensichtlich nicht in what
+    // enthalten.
+    if (!pointerp(envs)) return 0;
+    if (member(envs, ob) != -1) return what;
+  }
+  // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
+  // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
+  // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
+  else if (stringp(what)) {
+      int cnt;
+      string newwhat;
+      if(sscanf(what,"%s %d",newwhat,cnt)!=2)
+       cnt=1;
+      else
+       what=newwhat;
+      foreach(object invob: deep_inventory(ob)) {
+       if (invob->id(what) && !--cnt)
+           return invob;
+      }
+  }
+  else {
+    set_this_object(previous_object());
+    raise_error(sprintf("Wrong argument 1 to deep_present(). "
+         "Expected \"object\" or \"string\", got %.50O.\n",
+         what));
+  }
+  return 0;
+}
+
+mapping dump_ip_mapping()
+{
+  return 0;
+}
+
+nomask void swap(object obj)
+{
+  write("Your are not allowed to swap objects by hand!\n");
+  return;
+}
+
+nomask varargs void garbage_collection(string str)
+{
+  if(previous_object()==0 || !IS_ARCH(geteuid(previous_object())) 
+      || !ARCH_SECURITY)
+  {
+    write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
+    return;
+  }
+  else if (stringp(str))
+  {
+    return efun::garbage_collection(str);
+  }
+  else 
+    return efun::garbage_collection();
+}
+
+varargs void notify_fail(mixed nf, int prio) {
+  object po,oldo;
+  int oldprio;
+  
+  if (!PL || !objectp(po=previous_object())) return;
+  if (!stringp(nf) && !closurep(nf)) {
+      set_this_object(po);
+      raise_error(sprintf(
+         "Only strings and closures allowed for notify_fail! "
+         "Argument was: %.50O...\n",nf));
+  }
+
+  // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
+  // und vergleichen.
+  if (objectp(oldo=query_notify_fail(1)) && po!=oldo) {
+    if (!prio) {       
+      //Prioritaet dieses notify_fail() 'abschaetzen'
+      if (po==PL) // Spieler-interne (soul-cmds)
+        prio=NF_NL_OWN;
+      else if (living(po))
+        prio=NF_NL_LIVING;
+      else if ((int)po->IsRoom())
+        prio=NF_NL_ROOM;
+      else
+        prio=NF_NL_THING;
+    }
+    //Prioritaet des alten Setzers abschaetzen
+    if (oldo==PL)
+      oldprio=NF_NL_OWN;
+    else if (living(oldo))
+      oldprio=NF_NL_LIVING;
+    else if ((int)oldo->IsRoom())
+      oldprio=NF_NL_ROOM;
+    else
+      oldprio=NF_NL_THING;
+  }
+  else // wenn es noch kein Notify_fail gibt:
+    oldprio=NF_NL_NONE;
+
+  //vergleichen und ggf. setzen
+  if (prio >= oldprio) { 
+    set_this_object(po);
+    efun::notify_fail(nf);
+  }
+
+  return;
+}
+
+void _notify_fail(string str)
+{
+  //query_notify_fail() benutzen, um das Objekt
+  //des letzten notify_fail() zu ermitteln
+  object o;
+  if ((o=query_notify_fail(1)) && o!=previous_object())
+    return;
+  //noch kein notify_fail() fuer dieses Kommando gesetzt, auf gehts.
+  set_this_object(previous_object());
+  efun::notify_fail(str);
+  return;
+}
+
+string time2string( string format, int zeit )
+{
+  int i,ch,maxunit,dummy;
+  string *parts, fmt;
+
+  int secs = zeit;
+  int mins = (zeit/60);
+  int hours = (zeit/3600);
+  int days = (zeit/86400);
+  int weeks =  (zeit/604800);
+  int months = (zeit/2419200);
+  int abbr = 0;
+
+  parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
+
+  for( i=1; i<sizeof(parts); i+=2 )
+  {
+    ch = parts[i][<1];
+    switch( parts[i][<1] )
+    {
+    case 'x': case 'X':
+       abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
+       // NO break !
+    case 'n': case 'N':
+       maxunit |= 31;
+       break;
+    case 'w': case 'W':
+       maxunit |= 15;
+       break;
+    case 'd': case 'D':
+       maxunit |= 7;
+       break;
+    case 'h': case 'H':
+       maxunit |= 3;
+       break;
+    case 'm': case 'M':
+       maxunit |= 1;
+       break;
+    }
+  }
+  if( maxunit & 16 ) weeks %= 4;
+  if( maxunit & 8 ) days %= 7;
+  if( maxunit & 4 ) hours %= 24;
+  if( maxunit & 2 ) mins %= 60;
+  if( maxunit ) secs %= 60;
+
+  for( i=1; i<sizeof(parts); i+=2 )
+  {
+    fmt = parts[i][0..<2];
+    ch = parts[i][<1];
+    if( ch=='x' )
+    {
+      if (months > 0) ch='n';
+      else if( weeks>0 ) ch='w';
+      else if( days>0 ) ch='d';
+      else if( hours>0 ) ch='h'; 
+      else if(mins > 0) ch = 'm';
+      else ch = 's';
+    }
+    else if( ch=='X' )
+    {
+      if (months > 0) ch='N';
+      else if( weeks>0 ) ch='W';
+      else if( days>0 ) ch='D';
+      else if( hours>0 ) ch='H'; 
+      else if(mins > 0) ch = 'M';
+      else ch = 'S';
+    }
+    
+    switch( ch )
+    {
+      case 'n': parts[i] = sprintf( fmt+"d", months ); break;
+      case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
+      case 'd': parts[i] = sprintf( fmt+"d", days ); break;
+      case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
+      case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
+      case 's': parts[i] = sprintf( fmt+"d", secs ); break;
+      case 'N':
+       if(abbr) parts[i] = "M";
+       else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
+       break;
+      case 'W':
+       if(abbr) parts[i] = "w"; else
+       parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
+       break;
+      case 'D':
+       if(abbr) parts[i] = "d"; else
+       parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
+       break;
+      case 'H':
+       if(abbr) parts[i] = "h"; else
+       parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
+       break;
+      case 'M':
+       if(abbr) parts[i] = "m"; else
+       parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
+       break;
+      case 'S':
+       if(abbr) parts[i] = "s"; else
+       parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
+       break;
+      case '%':
+       parts[i] = "%";
+       break;
+      }
+    }
+    return implode( parts, "" );
+}
+
+nomask mixed __create_player_dummy(string name)
+{
+  string err;
+  object ob;
+  mixed m;
+  //hat nen Scherzkeks die Blueprint bewegt?
+  if ((ob=find_object("/secure/login")) && environment(ob))
+      catch(destruct(ob);publish);
+  err = catch(ob = clone_object("secure/login");publish);
+  if (err)
+  {
+    write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
+    return 0;
+  }
+  if (objectp(m=(mixed)ob->new_logon(name))) netdead[name]=m;
+  return m;
+}
+
+nomask int secure_level()
+{
+  int *level;
+  //kette der Caller durchlaufen, den niedrigsten Level in der Kette
+  //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
+  //von 0.
+  //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
+  //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
+  //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
+  //INteractive geben muss.
+  level=map(caller_stack(1),function int (object caller)
+      {if (objectp(caller))
+       return(query_wiz_level(geteuid(caller)));
+       return(0); // kein Objekt da, 0.
+      } );
+  return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
+}
+
+nomask string secure_euid()
+{
+  string euid;
+
+  if (!this_interactive()) // Es muss einen interactive geben
+     return 0;
+  euid=geteuid(this_interactive());
+  // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
+  // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
+  // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
+  foreach(object caller: caller_stack()) {
+      if (!objectp(caller) ||
+       (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
+         return 0;
+  }
+  return euid; // 'sichere' euid zurueckgeben
+}
+
+// INPUT_PROMPT und nen Leerprompt hinzufuegen, wenn keins uebergeben wird.
+// Das soll dazu dienen, dass alle ggf. ein EOR am Promptende kriegen...
+//#if __BOOT_TIME__ < 1360017213
+varargs void input_to( mixed fun, int flags, varargs mixed *args )
+{
+    mixed *arr;
+    int i;
+
+    if ( !this_player() || !previous_object() )
+       return;
+
+    // TODO: input_to(...,INPUT_PROMPT, "", ...), wenn kein INPUT_PROMPT
+    // vorkommt...
+    if ( flags&INPUT_PROMPT ) {    
+        arr = ({ fun, flags }) + args;
+    }
+    else {
+        // ggf. ein INPUT_PROMPT hinzufuegen und nen Leerstring als Prompt.
+        flags |= INPUT_PROMPT;
+        arr = ({ fun, flags, "" }) + args;
+    }
+
+    // Arrays gegen flatten quoten.
+    for ( i = sizeof(arr) - 1; i > 1; i-- )
+       if ( pointerp(arr[i]) )
+           arr[i] = quote(arr[i]);
+
+    apply( bind_lambda( unbound_lambda( ({}),
+                                     ({ #'efun::input_to/*'*/ }) + arr ),
+                       previous_object() ) );
+}
+//#endif
+
+nomask int set_light(int i)
+// erhoeht das Lichtlevel eines Objekts um i
+// result: das Lichtlevel innerhalb des Objekts
+{
+    object ob, *inv;
+    int lall, light, dark, tmp;
+
+    if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
+
+    // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
+    if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
+
+    // Lichtberechnung findet eigentlich in der Mudlib statt.
+    return (int)ob->QueryProp(P_INT_LIGHT);
+}
+
+
+public string iso2ascii( string str )
+{
+    if ( !stringp(str) || !sizeof(str) )
+       return "";
+
+    str = regreplace( str, "ä", "ae", 1 );
+    str = regreplace( str, "ö", "oe", 1 );
+    str = regreplace( str, "ü", "ue", 1 );
+    str = regreplace( str, "Ä", "Ae", 1 );
+    str = regreplace( str, "Ö", "Oe", 1 );
+    str = regreplace( str, "Ü", "Ue", 1 );
+    str = regreplace( str, "ß", "ss", 1 );
+    str = regreplace( str, "[^ -~]", "?", 1 );
+
+    return str;
+}
+
+
+public varargs string CountUp( string *s, string sep, string lastsep )
+{
+    string ret;
+
+    if ( !pointerp(s) )
+       return "";
+    
+    if (!sep) sep = ", ";
+    if (!lastsep) lastsep = " und ";
+
+    switch (sizeof(s))  {
+       case 0: ret=""; break;
+       case 1: ret=s[0]; break;
+       default:
+              ret = implode(s[0..<2], sep);
+              ret += lastsep + s[<1];
+    }
+    return ret;
+}
+
+nomask varargs int query_next_reset(object ob) {
+
+    // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
+    if (ob && !objectp(ob))
+      raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
+           "expected object.\n",ob));
+
+    // Defaultobjekt PO, wenn 0 uebergeben.
+    if ( !objectp(ob) )
+      ob = previous_object();
+
+    return efun::object_info(ob, OI_NEXT_RESET_TIME);
+}
+
+
+#if !__EFUN_DEFINED__(copy_file)
+#define MAXLEN 50000
+nomask int copy_file(string source, string dest)
+{
+
+  int ptr;
+  string bytes;
+
+  set_this_object(previous_object());
+  if (!sizeof(source)||!sizeof(dest)||source==dest||(file_size(source)==-1)||
+      (!call_other(master(),"valid_read",source,
+                   getuid(this_interactive()||
+                 previous_object()),"read_file",previous_object()))||
+      (!call_other(master(),"valid_read",source,
+                   getuid(this_interactive()||
+                 previous_object()),"write_file",previous_object())))
+    return 1;
+  switch (file_size(dest))
+  {
+  case -1:
+    break;
+  case -2:
+    if (dest[<1]!='/') dest+="/";
+    dest+=efun::explode(source,"/")[<1];
+    if (file_size(dest)==-2) return 1;
+    if (file_size(dest)!=-1) break;
+  default:
+    if (!rm(dest)) return 1;
+    break;
+  }
+  do
+  {
+    bytes = read_bytes(source, ptr, MAXLEN); ptr += MAXLEN;
+    if (!bytes) bytes="";
+    write_file(dest, bytes);
+  }
+  while(sizeof(bytes) == MAXLEN);
+  return 0;
+}
+#endif //!__EFUN_DEFINED__(copy_file)
+
+
+// ### Ersatzaufloesung in Strings ###
+varargs string replace_personal(string str, mixed *obs, int caps) {
+  int i;
+  string *parts;
+
+  parts = regexplode(str, "@WE[A-SU]*[0-9]");
+  i = sizeof(parts);
+
+  if (i>1) {
+    int j, t;
+    closure *name_cls;
+
+    t = j = sizeof(obs);
+
+    name_cls  =  allocate(j);
+    while (j--)
+      if (objectp(obs[j]))
+        name_cls[j] = symbol_function("name", obs[j]);
+      else if (stringp(obs[j]))
+        name_cls[j] = obs[j];
+
+    while ((i-= 2)>0) {
+      int ob_nr;
+      // zu ersetzendes Token in Fall und Objektindex aufspalten
+      ob_nr = parts[i][<1]-'1';
+      if (ob_nr<0 || ob_nr>=t) {
+        set_this_object(previous_object());
+        raise_error(sprintf("replace_personal: using wrong object index %d\n",
+                    ob_nr));
+        return implode(parts, "");
+      }
+
+      // casus kann man schon hier entscheiden
+      int casus;
+      string part = parts[i];
+      switch (part[3]) {
+        case 'R': casus = WER;    break;
+        case 'S': casus = WESSEN; break;
+        case 'M': casus = WEM;    break;
+        case 'N': casus = WEN;    break;
+        default:  continue; // passt schon jetzt nicht in das Hauptmuster
+      }
+
+      // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
+      mixed tmp;
+      switch (part[3..<2]) {
+        case "R": case "SSEN": case "M": case "N":               // Name
+          parts[i] = funcall(name_cls[ob_nr], casus, 1);  break;
+        case "RU": case "SSENU": case "MU": case "NU":           // unbestimmt
+          parts[i] = funcall(name_cls[ob_nr], casus);     break;
+        case "RQP": case "SSENQP": case "MQP": case "NQP":       // Pronoun
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPronoun(casus);
+          break;
+        case "RQA": case "SSENQA": case "MQA": case "NQA":       // Article
+          if (objectp(tmp = obs[ob_nr]))
+            tmp = (string)tmp->QueryArticle(casus, 1, 1);
+          if (stringp(tmp) && !(tmp[<1]^' ')) 
+            tmp = tmp[0..<2];                // Extra-Space wieder loeschen
+          break;
+        case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, SINGULAR);
+          break;
+        case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
+          break;
+        case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
+          break;
+        case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, PLURAL);
+          break;
+        case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
+          break;
+        case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
+          break;
+        default:
+          continue;
+      }
+      
+      // wenn tmp ein String war, weisen wir es hier pauschal zu
+      if (stringp(tmp))
+        parts[i] = tmp;
+
+      // auf Wunsch wird nach Satzenden gross geschrieben
+      if (caps)
+        switch (parts[i-1][<2..]) {
+          case ". ":  case "! ":  case "? ":
+          case ".":   case "!":   case "?":
+          case ".\n": case "!\n": case "?\n":
+          case "\" ": case "\"\n":
+            parts[i] = capitalize(parts[i]);
+            break;
+        }
+    }
+    return implode(parts, "");
+  }
+  return str;
+}
+
+
+//replacements for dropped efuns in LD
+#if !__EFUN_DEFINED__(extract)
+deprecated varargs string extract(string str, int from, int to) {
+
+  if(!stringp(str)) {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to extract(): %O",str));
+  }
+  if (intp(from) && intp(to)) {
+    if (from>=0 && to>=0)
+      return(str[from .. to]);
+    else if (from>=0 && to<0)
+      return(str[from .. <abs(to)]);
+    else if (from<0 && to>=0)
+      return(str[<abs(from) .. to]);
+    else
+      return(str[<abs(from) .. <abs(to)]);
+  }
+  else if (intp(from)) {
+    if (from>=0)
+      return(str[from .. ]);
+    else
+      return(str[<abs(from) .. ]);
+  }
+  else {
+    return(str);
+  }
+}
+#endif // !__EFUN_DEFINED__(extract)
+
+#if !__EFUN_DEFINED__(slice_array)
+deprecated varargs mixed slice_array(mixed array, int from, int to) {
+
+  if(!pointerp(array)) {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
+  }
+  if (intp(from) && intp(to)) {
+    if (from>=0 && to>=0)
+      return(array[from .. to]);
+    else if (from>=0 && to<0)
+      return(array[from .. <abs(to)]);
+    else if (from<0 && to>=0)
+      return(array[<abs(from) .. to]);
+    else
+      return(array[<abs(from) .. <abs(to)]);
+  }
+  else if (intp(from)) {
+    if (from>=0)
+      return(array[from .. ]);
+    else
+      return(array[<abs(from) .. ]);
+  }
+  else {
+    return(array);
+  }
+}
+#endif // !__EFUN_DEFINED__(slice_array)
+
+#if !__EFUN_DEFINED__(member_array)
+deprecated int member_array(mixed item, mixed arraystring) {
+
+  if (pointerp(arraystring)) {
+    return(efun::member(arraystring,item));
+  }
+  else if (stringp(arraystring)) {
+    return(efun::member(arraystring,to_int(item)));
+  }
+  else {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
+  }
+}
+#endif // !__EFUN_DEFINED__(member_array)
+
+// The digit at the i'th position is the number of bits set in 'i'.
+string count_table =
+    "0112122312232334122323342334344512232334233434452334344534454556";
+int broken_count_bits( string s ) {
+    int i, res;
+    if( !stringp(s) || !(i=sizeof(s)) ) return 0;
+    for( ; i-->0; ) {
+        // We are counting 6 bits at a time using a precompiled table.
+        res += count_table[(s[i]-' ')&63]-'0';
+    }
+    return res;
+}
+
+#if !__EFUN_DEFINED__(count_bits)
+int count_bits( string s ) {
+    return(broken_count_bits(s));
+}
+#endif
+
+
+// * Teile aus einem Array entfernen *** OBSOLETE
+deprecated mixed *exclude_array(mixed *arr,int from,int to)
+{
+  if (to<from)
+    to = from;
+  return arr[0..from-1]+arr[to+1..];
+}
+
diff --git a/secure/simul_efun/spare/spare/spare/README b/secure/simul_efun/spare/spare/spare/README
new file mode 100644
index 0000000..303429a
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/README
@@ -0,0 +1,20 @@
+simul_efun 
+---------- 
+Das simul_efun Objekt /secure/simul_efun/simul_efun benutzt die Dateien
+in /secure/simul_efun.
+Im Verzeichnis /secure/simul_efun/spare ist eine Sicherheitskopie von allen 
+Dateien in /secure/simul_efun.
+
+Falls /secure/simul_efun/simul_efun.c nicht ladbar ist, wird 
+/secure/simul_efun/spare/simul_efun.c als Ersatz versucht. Wenn auch das
+nicht ladbar ist, erfolgt ein Shutdown des Muds.
+
+Bei Aenderungen sollte _zuerst_ die normale simul efun editiert und
+anschliessend zerstoert/entladen werden, woraufhin sie implizit durch den
+Driver und Master neugeladen wird. Ist dies erfolgreich und die neue
+simul_efun laeuft (erst dann!) kopiert man die Dateien aus 
+/secure/simul_efun nach /secure/simul_efun/spare.
+
+Die Dateien hier sind mit Ausnahme der simul_efun.c selbst _nicht_ dazu 
+gedacht, geladen zu werden.
+
diff --git a/secure/simul_efun/spare/spare/spare/comm.c b/secure/simul_efun/spare/spare/spare/comm.c
new file mode 100644
index 0000000..ffb598c
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/comm.c
@@ -0,0 +1,125 @@
+#include <living/comm.h>
+
+// Sendet an alle Objekte in room und room selber durch Aufruf von
+// ReceiveMsg().
+varargs void send_room(object|string room, string msg, int msg_type,
+                       string msg_action, string msg_prefix, object *exclude,
+                       object origin)
+{
+  if (stringp(room))
+    room=load_object(room);
+  
+  origin ||= previous_object();
+  object *dest = exclude ? all_inventory(room) - exclude :
+                           all_inventory(room);
+
+  dest->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, origin);
+  room->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, origin);
+}
+
+static int _shout_filter( object ob, string pat )
+{
+    string *ignore;
+
+    if ( !environment(ob) )
+       return 0;
+
+    return sizeof( regexp( ({ object_name( environment(ob) ) }), pat ) );
+}
+
+varargs void shout( string s, mixed where ){
+    object *u;
+    string *pfade;
+
+    if ( !sizeof( u = users() - ({ this_player() }) ) )
+       return;
+
+    if ( !where )
+       pfade = ({ "/" });
+    else if ( intp(where) )
+       pfade =
+           ({ implode( efun::explode( object_name( environment(this_player()) ),
+                                   "/" )[0..2], "/" ) + "/" });
+    else if ( stringp(where) )
+       pfade = ({ where });
+    else
+       pfade = where;
+
+    u = filter( u, "_shout_filter", ME, implode( pfade, "|" ) );
+    u->ReceiveMsg(s, MT_COMM|MT_FAR|MSG_DONT_WRAP|MSG_DONT_STORE,
+                  MA_SHOUT_SEFUN, 0, previous_object());
+}
+
+
+#if __VERSION__ > "3.3.718"
+// This sefun replaces the deprecated efun cat().
+#define CAT_MAX_LINES 50
+varargs int cat(string file, int start, int num)
+{
+    if (extern_call())
+        set_this_object(previous_object());
+
+    int more;
+
+    if (num < 0 || !this_player())
+        return 0;
+
+    if (!start)
+        start = 1;
+
+    if (!num || num > CAT_MAX_LINES) {
+        num = CAT_MAX_LINES;
+        more = sizeof(read_file(file, start+num, 1));
+    }
+
+    string txt = read_file(file, start, num);
+    if (!txt)
+        return 0;
+
+    efun::tell_object(this_player(), txt);
+
+    if (more)
+        efun::tell_object(this_player(), "*****TRUNCATED****\n");
+
+    return sizeof(txt & "\n");
+}
+#undef CAT_MAX_LINES
+#endif // __VERSION__ > "3.3.718"
+
+#if __VERSION__ > "3.3.719"
+// This sefun replaces the deprecated efun tail().
+#define TAIL_MAX_BYTES 1000
+varargs int tail(string file)
+{
+    if (extern_call())
+        set_this_object(previous_object());
+
+    if (!stringp(file) || !this_player())
+        return 0;
+
+    string txt = read_bytes(file, -(TAIL_MAX_BYTES + 80), (TAIL_MAX_BYTES + 80));
+    // read_bytes() returns 0 if the start of the section given by
+    // parameter #2 lies beyond the beginning of the file.
+    // In this case we simply try and read the entire file.
+    if (!stringp(txt) && file_size(file) < TAIL_MAX_BYTES+80)
+      txt = read_file(file);
+    // Exit if still nothing could be read.
+    if (!stringp(txt))
+      return 0;
+
+    // cut off first (incomplete) line
+    int index = strstr(txt, "\n");
+    if (index > -1) {
+        if (index + 1 < sizeof(txt))
+            txt = txt[index+1..];
+        else
+            txt = "";
+    }
+
+    efun::tell_object(this_player(), txt);
+
+    return 1;
+}
+#undef TAIL_MAX_BYTES
+#endif // __VERSION__ > "3.3.719"
+
diff --git a/secure/simul_efun/spare/spare/spare/debug_info.c b/secure/simul_efun/spare/spare/spare/debug_info.c
new file mode 100644
index 0000000..d607c2f
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/debug_info.c
@@ -0,0 +1,434 @@
+/* This sefun is to provide a replacement for the efun debug_info().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(debug_info)
+
+#include <driver_info.h>
+#include <debug_info.h>
+
+mixed debug_info(int what, varargs mixed* args)
+{
+    if (sizeof(args) > 2)
+        raise_error("Too many arguments to debug_info\n");
+
+    switch(what)
+    {
+        default:
+            raise_error(sprintf("Illegal value %d for debug_info().\n", what));
+
+        case DINFO_OBJECT:
+        {
+            object ob;
+
+            if (sizeof(args) != 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (!objectp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            ob = args[0];
+            printf("O_HEART_BEAT      : %s\n", efun::object_info(ob, OC_HEART_BEAT)       ? "TRUE" : "FALSE");
+            printf("O_ENABLE_COMMANDS : %s\n", efun::object_info(ob, OC_COMMANDS_ENABLED) ? "TRUE" : "FALSE");
+            printf("O_CLONE           : %s\n", efun::clonep(ob)                           ? "TRUE" : "FALSE");
+            printf("O_DESTRUCTED      : FALSE\n");
+            printf("O_SWAPPED         : %s\n", efun::object_info(ob, OI_SWAPPED)          ? "TRUE" : "FALSE");
+            printf("O_ONCE_INTERACTIVE: %s\n", efun::object_info(ob, OI_ONCE_INTERACTIVE) ? "TRUE" : "FALSE");
+            printf("O_RESET_STATE     : %s\n", efun::object_info(ob, OI_RESET_STATE)      ? "TRUE" : "FALSE");
+            printf("O_WILL_CLEAN_UP   : %s\n", efun::object_info(ob, OI_WILL_CLEAN_UP)    ? "TRUE" : "FALSE");
+            printf("O_REPLACED        : %s\n", efun::object_info(ob, OI_REPLACED)         ? "TRUE" : "FALSE");
+            printf("time_reset  : %d\n",       efun::object_info(ob, OI_NEXT_RESET_TIME));
+            printf("time_of_ref : %d\n",       efun::object_info(ob, OI_LAST_REF_TIME));
+            printf("ref         : %d\n",       efun::object_info(ob, OI_OBJECT_REFS));
+
+            int gticks = efun::object_info(ob, OI_GIGATICKS);
+            if(gticks)
+                printf("evalcost   :  %d%09d\n", gticks, efun::object_info(ob, OI_TICKS));
+            else
+                printf("evalcost   :  %d\n",   efun::object_info(ob, OI_TICKS));
+
+            printf("swap_num    : %d\n",       efun::object_info(ob, OI_SWAP_NUM));
+            printf("name        : '%s'\n",     efun::object_name(ob));
+            printf("load_name   : '%s'\n",     efun::load_name(ob));
+
+            object next_ob = efun::object_info(ob, OI_OBJECT_NEXT);
+            if (next_ob)
+                printf("next_all    : OBJ(%s)\n", efun::object_name(next_ob));
+
+            object prev_ob = efun::object_info(ob, OI_OBJECT_PREV);
+            if (prev_ob)
+                printf("Previous object in object list: OBJ(%s)\n", efun::object_name(prev_ob));
+            else
+                printf("This object is the head of the object list.\n");
+            break;
+        }
+
+        case DINFO_MEMORY:
+        {
+            object ob;
+
+            if (sizeof(args) != 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (!objectp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            ob = args[0];
+
+            printf("program ref's %3d\n",   efun::object_info(ob, OI_PROG_REFS));
+            printf("Name: '%s'\n",          efun::program_name(ob));
+            printf("program size    %6d\n", efun::object_info(ob, OI_PROG_SIZE));
+            printf("num func's:  %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_FUNCTIONS),
+                                            efun::object_info(ob, OI_SIZE_FUNCTIONS));
+            printf("num vars:    %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_VARIABLES),
+                                            efun::object_info(ob, OI_SIZE_VARIABLES));
+
+            printf("num strings: %3d (%4d) : overhead %d + data %d (%d)\n",
+                                            efun::object_info(ob, OI_NUM_STRINGS),
+                                            efun::object_info(ob, OI_SIZE_STRINGS) + efun::object_info(ob, OI_SIZE_STRINGS_DATA),
+                                            efun::object_info(ob, OI_SIZE_STRINGS),
+                                            efun::object_info(ob, OI_SIZE_STRINGS_DATA),
+                                            efun::object_info(ob, OI_SIZE_STRINGS_DATA_TOTAL));
+
+            printf("num inherits %3d (%4d)\n",
+                                            efun::object_info(ob, OI_NUM_INHERITED),
+                                            efun::object_info(ob, OI_SIZE_INHERITED));
+
+            printf("total size      %6d\n", efun::object_info(ob, OI_PROG_SIZE_TOTAL));
+
+            printf("data size       %6d (%6d\n",
+                                            efun::object_info(ob, OI_DATA_SIZE),
+                                            efun::object_info(ob, OI_DATA_SIZE_TOTAL));
+            break;
+        }
+
+        case DINFO_OBJLIST:
+        {
+            if (sizeof(args) == 0)
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (sizeof(args) == 1)
+            {
+                object * obs = efun::objects(args[0], 1);
+                return obs[0];
+            }
+            else
+                return efun::objects(args[0], args[1]);
+            break;
+        }
+
+        case DINFO_MALLOC:
+            write(debug_info(DINFO_STATUS, "malloc"));
+            break;
+
+        case DINFO_STATUS:
+        {
+            string which;
+            int opt;
+
+            if (sizeof(args) > 1)
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (!sizeof(args) || !args[0])
+                which = "";
+            else if(stringp(args[0]))
+                which = args[0];
+            else
+                raise_error("bag arg 2 to debug_info().\n");
+
+            switch(which)
+            {
+                case "":
+                    opt = DI_STATUS_TEXT_MEMORY;
+                    break;
+
+                case "tables":
+                    opt = DI_STATUS_TEXT_TABLES;
+                    break;
+
+                case "swap":
+                    opt = DI_STATUS_TEXT_SWAP;
+                    break;
+
+                case "malloc":
+                    opt = DI_STATUS_TEXT_MALLOC;
+                    break;
+
+                case "malloc extstats":
+                    opt = DI_STATUS_TEXT_MALLOC_EXTENDED;
+                    break;
+
+                default:
+                    return 0;
+            }
+
+            return efun::driver_info(opt);
+        }
+
+        case DINFO_DUMP:
+        {
+            int opt;
+
+            if (!sizeof(args))
+                raise_error("bad number of arguments to debug_info\n");
+
+            if(!stringp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            switch(args[0])
+            {
+                case "objects":
+                    opt = DDI_OBJECTS;
+                    break;
+
+                case "destructed":
+                    opt = DDI_OBJECTS_DESTRUCTED;
+                    break;
+
+                case "opcodes":
+                    opt = DDI_OPCODES;
+                    break;
+
+                case "memory":
+                    opt = DDI_MEMORY;
+                    break;
+
+                default:
+                    raise_error(sprintf("Bad argument '%s' to debug_info(DINFO_DUMP).\n", args[0]));
+                    return 0;
+            }
+
+            return efun::dump_driver_info(opt, args[1..1]...);
+        }
+
+        case DINFO_DATA:
+        {
+            mixed * result;
+
+            if (!sizeof(args))
+                raise_error("bad number of arguments to debug_info\n");
+
+            if (!intp(args[0]))
+                raise_error("bag arg 2 to debug_info().\n");
+
+            if (sizeof(args) == 2 && !intp(args[1]))
+                raise_error("bag arg 3 to debug_info().\n");
+
+            switch(args[0])
+            {
+                case DID_STATUS:
+                    result = allocate(DID_STATUS_MAX);
+
+                    result[DID_ST_ACTIONS]               = efun::driver_info(DI_NUM_ACTIONS);
+                    result[DID_ST_ACTIONS_SIZE]          = efun::driver_info(DI_SIZE_ACTIONS);
+                    result[DID_ST_SHADOWS]               = efun::driver_info(DI_NUM_SHADOWS);
+                    result[DID_ST_SHADOWS_SIZE]          = efun::driver_info(DI_SIZE_SHADOWS);
+
+                    result[DID_ST_OBJECTS]               = efun::driver_info(DI_NUM_OBJECTS);
+                    result[DID_ST_OBJECTS_SIZE]          = efun::driver_info(DI_SIZE_OBJECTS);
+                    result[DID_ST_OBJECTS_SWAPPED]       = efun::driver_info(DI_NUM_OBJECTS_SWAPPED);
+                    result[DID_ST_OBJECTS_SWAP_SIZE]     = efun::driver_info(DI_SIZE_OBJECTS_SWAPPED);
+                    result[DID_ST_OBJECTS_LIST]          = efun::driver_info(DI_NUM_OBJECTS_IN_LIST);
+                    result[DID_ST_OBJECTS_NEWLY_DEST]    = efun::driver_info(DI_NUM_OBJECTS_NEWLY_DESTRUCTED);
+                    result[DID_ST_OBJECTS_DESTRUCTED]    = efun::driver_info(DI_NUM_OBJECTS_DESTRUCTED);
+                    result[DID_ST_OBJECTS_PROCESSED]     = efun::driver_info(DI_NUM_OBJECTS_LAST_PROCESSED);
+                    result[DID_ST_OBJECTS_AVG_PROC]      = efun::driver_info(DI_LOAD_AVERAGE_PROCESSED_OBJECTS_RELATIVE);
+
+                    result[DID_ST_OTABLE]                = efun::driver_info(DI_NUM_OBJECTS_IN_TABLE);
+                    result[DID_ST_OTABLE_SLOTS]          = efun::driver_info(DI_NUM_OBJECT_TABLE_SLOTS);
+                    result[DID_ST_OTABLE_SIZE]           = efun::driver_info(DI_SIZE_OBJECT_TABLE);
+
+                    result[DID_ST_HBEAT_OBJS]            = efun::driver_info(DI_NUM_HEARTBEATS);
+                    result[DID_ST_HBEAT_CALLS]           = efun::driver_info(DI_NUM_HEARTBEAT_ACTIVE_CYCLES);
+                    result[DID_ST_HBEAT_CALLS_TOTAL]     = efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+                    result[DID_ST_HBEAT_SLOTS]           = efun::driver_info(DI_NUM_HEARTBEATS);
+                    result[DID_ST_HBEAT_SIZE]            = efun::driver_info(DI_SIZE_HEARTBEATS);
+                    result[DID_ST_HBEAT_PROCESSED]       = efun::driver_info(DI_NUM_HEARTBEATS_LAST_PROCESSED);
+                    result[DID_ST_HBEAT_AVG_PROC]        = efun::driver_info(DI_LOAD_AVERAGE_PROCESSED_HEARTBEATS_RELATIVE);
+
+                    result[DID_ST_CALLOUTS]              = efun::driver_info(DI_NUM_CALLOUTS);
+                    result[DID_ST_CALLOUT_SIZE]          = efun::driver_info(DI_SIZE_CALLOUTS);
+
+                    result[DID_ST_ARRAYS]                = efun::driver_info(DI_NUM_ARRAYS);
+                    result[DID_ST_ARRAYS_SIZE]           = efun::driver_info(DI_SIZE_ARRAYS);
+
+                    result[DID_ST_MAPPINGS]              = efun::driver_info(DI_NUM_MAPPINGS);
+                    result[DID_ST_MAPPINGS_SIZE]         = efun::driver_info(DI_SIZE_MAPPINGS);
+                    result[DID_ST_HYBRID_MAPPINGS]       = efun::driver_info(DI_NUM_MAPPINGS_HYBRID);
+                    result[DID_ST_HASH_MAPPINGS]         = efun::driver_info(DI_NUM_MAPPINGS_HASH);
+
+                    result[DID_ST_STRUCTS]               = efun::driver_info(DI_NUM_STRUCTS);
+                    result[DID_ST_STRUCTS_SIZE]          = efun::driver_info(DI_SIZE_STRUCTS);
+                    result[DID_ST_STRUCT_TYPES]          = efun::driver_info(DI_NUM_STRUCT_TYPES);
+                    result[DID_ST_STRUCT_TYPES_SIZE]     = efun::driver_info(DI_SIZE_STRUCT_TYPES);
+
+                    result[DID_ST_PROGS]                 = efun::driver_info(DI_NUM_PROGS);
+                    result[DID_ST_PROGS_SIZE]            = efun::driver_info(DI_SIZE_PROGS);
+
+                    result[DID_ST_PROGS_SWAPPED]         = efun::driver_info(DI_NUM_PROGS_SWAPPED);
+                    result[DID_ST_PROGS_SWAP_SIZE]       = efun::driver_info(DI_SIZE_PROGS_SWAPPED);
+
+                    result[DID_ST_USER_RESERVE]          = efun::driver_info(DI_MEMORY_RESERVE_USER);
+                    result[DID_ST_MASTER_RESERVE]        = efun::driver_info(DI_MEMORY_RESERVE_MASTER);
+                    result[DID_ST_SYSTEM_RESERVE]        = efun::driver_info(DI_MEMORY_RESERVE_SYSTEM);
+
+                    result[DID_ST_ADD_MESSAGE]           = efun::driver_info(DI_NUM_MESSAGES_OUT);
+                    result[DID_ST_PACKETS]               = efun::driver_info(DI_NUM_PACKETS_OUT);
+                    result[DID_ST_PACKET_SIZE]           = efun::driver_info(DI_SIZE_PACKETS_OUT);
+                    result[DID_ST_PACKETS_IN]            = efun::driver_info(DI_NUM_PACKETS_IN);
+                    result[DID_ST_PACKET_SIZE_IN]        = efun::driver_info(DI_SIZE_PACKETS_IN);
+
+                    result[DID_ST_APPLY]                 = efun::driver_info(DI_NUM_FUNCTION_NAME_CALLS);
+                    result[DID_ST_APPLY_HITS]            = efun::driver_info(DI_NUM_FUNCTION_NAME_CALL_HITS);
+
+                    result[DID_ST_STRINGS]               = efun::driver_info(DI_NUM_VIRTUAL_STRINGS);
+                    result[DID_ST_STRING_SIZE]           = efun::driver_info(DI_SIZE_STRINGS);
+                    result[DID_ST_STR_TABLE_SIZE]        = efun::driver_info(DI_SIZE_STRING_TABLE);
+                    result[DID_ST_STR_OVERHEAD]          = efun::driver_info(DI_SIZE_STRING_OVERHEAD);
+                    result[DID_ST_UNTABLED]              = efun::driver_info(DI_NUM_STRINGS_UNTABLED);
+                    result[DID_ST_UNTABLED_SIZE]         = efun::driver_info(DI_SIZE_STRINGS_UNTABLED);
+                    result[DID_ST_TABLED]                = efun::driver_info(DI_NUM_STRINGS_TABLED);
+                    result[DID_ST_TABLED_SIZE]           = efun::driver_info(DI_SIZE_STRINGS_TABLED);
+                    result[DID_ST_STR_SEARCHES]          = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUPS_BY_INDEX);
+                    result[DID_ST_STR_SEARCHLEN]         = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_INDEX);
+                    result[DID_ST_STR_SEARCHES_BYVALUE]  = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUPS_BY_VALUE);
+                    result[DID_ST_STR_SEARCHLEN_BYVALUE] = efun::driver_info(DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_VALUE);
+                    result[DID_ST_STR_CHAINS]            = efun::driver_info(DI_NUM_STRING_TABLE_SLOTS_USED);
+                    result[DID_ST_STR_ADDED]             = efun::driver_info(DI_NUM_STRING_TABLE_STRINGS_ADDED);
+                    result[DID_ST_STR_DELETED]           = efun::driver_info(DI_NUM_STRING_TABLE_STRINGS_REMOVED);
+                    result[DID_ST_STR_COLLISIONS]        = efun::driver_info(DI_NUM_STRING_TABLE_COLLISIONS);
+                    result[DID_ST_STR_FOUND]             = efun::driver_info(DI_NUM_STRING_TABLE_HITS_BY_INDEX);
+                    result[DID_ST_STR_FOUND_BYVALUE]     = efun::driver_info(DI_NUM_STRING_TABLE_HITS_BY_VALUE);
+
+                    result[DID_ST_RX_CACHED]             = efun::driver_info(DI_NUM_REGEX);
+                    result[DID_ST_RX_TABLE]              = efun::driver_info(DI_NUM_REGEX_TABLE_SLOTS);
+                    result[DID_ST_RX_TABLE_SIZE]         = efun::driver_info(DI_SIZE_REGEX);
+                    result[DID_ST_RX_REQUESTS]           = efun::driver_info(DI_NUM_REGEX_LOOKUPS);
+                    result[DID_ST_RX_REQ_FOUND]          = efun::driver_info(DI_NUM_REGEX_LOOKUP_HITS);
+                    result[DID_ST_RX_REQ_COLL]           = efun::driver_info(DI_NUM_REGEX_LOOKUP_COLLISIONS);
+
+                    result[DID_ST_MB_FILE]               = efun::driver_info(DI_SIZE_BUFFER_FILE);
+                    result[DID_ST_MB_SWAP]               = efun::driver_info(DI_SIZE_BUFFER_SWAP);
+
+                    result[DID_ST_BOOT_TIME]             = efun::driver_info(DI_BOOT_TIME);
+                    break;
+
+                case DID_SWAP:
+                    result = allocate(DID_SWAP_MAX);
+
+                    result[DID_SW_PROGS]                 = efun::driver_info(DI_NUM_PROGS_SWAPPED);
+                    result[DID_SW_PROG_SIZE]             = efun::driver_info(DI_SIZE_PROGS_SWAPPED);
+                    result[DID_SW_PROG_UNSWAPPED]        = efun::driver_info(DI_NUM_PROGS_UNSWAPPED);
+                    result[DID_SW_PROG_U_SIZE]           = efun::driver_info(DI_SIZE_PROGS_UNSWAPPED);
+                    result[DID_SW_VARS]                  = efun::driver_info(DI_NUM_OBJECTS_SWAPPED);
+                    result[DID_SW_VAR_SIZE]              = efun::driver_info(DI_SIZE_OBJECTS_SWAPPED);
+                    result[DID_SW_FREE]                  = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE);
+                    result[DID_SW_FREE_SIZE]             = efun::driver_info(DI_SIZE_SWAP_BLOCKS_FREE);
+                    result[DID_SW_FILE_SIZE]             = efun::driver_info(DI_SIZE_SWAP_BLOCKS);
+                    result[DID_SW_REUSED]                = efun::driver_info(DI_SIZE_SWAP_BLOCKS_REUSED);
+                    result[DID_SW_SEARCHES]              = efun::driver_info(DI_NUM_SWAP_BLOCKS_REUSE_LOOKUPS);
+                    result[DID_SW_SEARCH_LEN]            = efun::driver_info(DI_NUM_SWAP_BLOCKS_REUSE_LOOKUP_STEPS);
+                    result[DID_SW_F_SEARCHES]            = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE_LOOKUPS);
+                    result[DID_SW_F_SEARCH_LEN]          = efun::driver_info(DI_NUM_SWAP_BLOCKS_FREE_LOOKUP_STEPS);
+                    result[DID_SW_COMPACT]               = efun::driver_info(DC_SWAP_COMPACT_MODE);
+                    result[DID_SW_RECYCLE_FREE]          = efun::driver_info(DI_SWAP_RECYCLE_PHASE);
+                    break;
+
+                case DID_MEMORY:
+                    result = allocate(DID_MEMORY_MAX);
+
+                    result[DID_MEM_NAME]                 = efun::driver_info(DI_MEMORY_ALLOCATOR_NAME);
+                    result[DID_MEM_SBRK]                 = efun::driver_info(DI_NUM_SYS_ALLOCATED_BLOCKS);
+                    result[DID_MEM_SBRK_SIZE]            = efun::driver_info(DI_SIZE_SYS_ALLOCATED_BLOCKS);
+                    result[DID_MEM_LARGE]                = efun::driver_info(DI_NUM_LARGE_BLOCKS_ALLOCATED);
+                    result[DID_MEM_LARGE_SIZE]           = efun::driver_info(DI_SIZE_LARGE_BLOCKS_ALLOCATED);
+                    result[DID_MEM_LFREE]                = efun::driver_info(DI_NUM_LARGE_BLOCKS_FREE);
+                    result[DID_MEM_LFREE_SIZE]           = efun::driver_info(DI_SIZE_LARGE_BLOCKS_FREE);
+                    result[DID_MEM_LWASTED]              = efun::driver_info(DI_NUM_LARGE_BLOCKS_WASTE);
+                    result[DID_MEM_LWASTED_SIZE]         = efun::driver_info(DI_SIZE_LARGE_BLOCKS_WASTE);
+                    result[DID_MEM_CHUNK]                = efun::driver_info(DI_NUM_SMALL_BLOCK_CHUNKS);
+                    result[DID_MEM_CHUNK_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCK_CHUNKS);
+                    result[DID_MEM_SMALL]                = efun::driver_info(DI_NUM_SMALL_BLOCKS_ALLOCATED);
+                    result[DID_MEM_SMALL_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCKS_ALLOCATED);
+                    result[DID_MEM_SFREE]                = efun::driver_info(DI_NUM_SMALL_BLOCKS_FREE);
+                    result[DID_MEM_SFREE_SIZE]           = efun::driver_info(DI_SIZE_SMALL_BLOCKS_FREE);
+                    result[DID_MEM_SWASTED]              = efun::driver_info(DI_NUM_SMALL_BLOCKS_WASTE);
+                    result[DID_MEM_SWASTED_SIZE]         = efun::driver_info(DI_SIZE_SMALL_BLOCKS_WASTE);
+                    result[DID_MEM_MINC_CALLS]           = efun::driver_info(DI_NUM_INCREMENT_SIZE_CALLS);
+                    result[DID_MEM_MINC_SUCCESS]         = efun::driver_info(DI_NUM_INCREMENT_SIZE_CALL_SUCCESSES);
+                    result[DID_MEM_MINC_SIZE]            = efun::driver_info(DI_SIZE_INCREMENT_SIZE_CALL_DIFFS);
+                    result[DID_MEM_PERM]                 = efun::driver_info(DI_NUM_UNMANAGED_BLOCKS);
+                    result[DID_MEM_PERM_SIZE]            = efun::driver_info(DI_SIZE_UNMANAGED_BLOCKS);
+                    result[DID_MEM_CLIB]                 = efun::driver_info(DI_NUM_REPLACEMENT_MALLOC_CALLS);
+                    result[DID_MEM_CLIB_SIZE]            = efun::driver_info(DI_SIZE_REPLACEMENT_MALLOC_CALLS);
+                    result[DID_MEM_OVERHEAD]             = efun::driver_info(DI_SIZE_SMALL_BLOCK_OVERHEAD);
+                    result[DID_MEM_ALLOCATED]            = efun::driver_info(DI_SIZE_MEMORY_USED) + efun::driver_info(DI_SIZE_MEMORY_OVERHEAD);
+                    result[DID_MEM_USED]                 = efun::driver_info(DI_SIZE_MEMORY_USED);
+                    result[DID_MEM_TOTAL_UNUSED]         = efun::driver_info(DI_SIZE_MEMORY_UNUSED);
+                    result[DID_MEM_DEFRAG_CALLS]         = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_FULL) + efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_TARGETED);
+                    result[DID_MEM_DEFRAG_CALLS_REQ]     = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_TARGETED);
+                    result[DID_MEM_DEFRAG_REQ_SUCCESS]   = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_CALL_TARGET_HITS);
+                    result[DID_MEM_DEFRAG_BLOCKS_INSPECTED] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_INSPECTED);
+                    result[DID_MEM_DEFRAG_BLOCKS_MERGED] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_MERGED);
+                    result[DID_MEM_DEFRAG_BLOCKS_RESULT] = efun::driver_info(DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_RESULTING);
+                    result[DID_MEM_AVL_NODES]            = efun::driver_info(DI_NUM_FREE_BLOCKS_AVL_NODES);
+                    result[DID_MEM_EXT_STATISTICS]       = efun::driver_info(DI_MEMORY_EXTENDED_STATISTICS);
+                    break;
+            }
+
+            if (sizeof(args) == 2)
+            {
+                int idx = args[0];
+                if (idx < 0 || idx >= sizeof(result))
+                    raise_error(sprintf("Illegal index for debug_info(): %d, expected 0..%d\n",
+                        idx, sizeof(result)-1));
+
+                return result[idx];
+            }
+            else
+                return result;
+        }
+
+        case DINFO_TRACE:
+        {
+            int which = DIT_CURRENT;
+
+            if (sizeof(args) > 1)
+                raise_error("bad number of arguments to debug_info\n");
+            if (sizeof(args))
+            {
+                if (!intp(args[0]))
+                    raise_error("bag arg 2 to debug_info().\n");
+                which = args[0];
+            }
+
+            switch (which)
+            {
+                case DIT_CURRENT:
+                    return efun::driver_info(DI_TRACE_CURRENT);
+
+                case DIT_ERROR:
+                    return efun::driver_info(DI_TRACE_LAST_ERROR) || ({ "No trace." });
+
+                case DIT_UNCAUGHT_ERROR:
+                    return efun::driver_info(DI_TRACE_LAST_UNCAUGHT_ERROR) || ({ "No trace." });
+
+                case DIT_STR_CURRENT:
+                    return efun::driver_info(DI_TRACE_CURRENT_AS_STRING);
+
+                case DIT_CURRENT_DEPTH:
+                    return efun::driver_info(DI_TRACE_CURRENT_DEPTH);
+
+                default:
+                    raise_error("bad arg 2 to debug_info().\n");
+            }
+
+        }
+
+        case DINFO_EVAL_NUMBER:
+            return efun::driver_info(DI_EVAL_NUMBER);
+    }
+    return 0;
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/enable_commands.c b/secure/simul_efun/spare/spare/spare/enable_commands.c
new file mode 100644
index 0000000..9d1c963
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/enable_commands.c
@@ -0,0 +1,30 @@
+/* These sefuns are to provide a replacement for the efuns enable_commands()
+ * and disable_commands().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#include <configuration.h>
+
+#if ! __EFUN_DEFINED__(enable_commands)
+
+void enable_commands()
+{
+    object ob = efun::previous_object();
+
+    efun::configure_object(ob, OC_COMMANDS_ENABLED, 1);
+    efun::set_this_player(ob);
+}
+
+#endif
+
+#if ! __EFUN_DEFINED__(disable_commands)
+
+void disable_commands()
+{
+    object ob = efun::previous_object();
+
+    efun::configure_object(ob, OC_COMMANDS_ENABLED, 0);
+    efun::set_this_player(0);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/hash.c b/secure/simul_efun/spare/spare/spare/hash.c
new file mode 100644
index 0000000..51bbf60
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/hash.c
@@ -0,0 +1,17 @@
+#include "/sys/tls.h"
+
+deprecated string md5(mixed arg, varargs mixed* iterations)
+{
+    if (extern_call())
+         set_this_object(previous_object());
+
+    return hash(TLS_HASH_MD5, arg, iterations...);
+}
+
+deprecated string sha1(mixed arg, varargs mixed* iterations)
+{
+    if (extern_call())
+         set_this_object(previous_object());
+
+    return hash(TLS_HASH_SHA1, arg, iterations...);
+}
diff --git a/secure/simul_efun/spare/spare/spare/livings.c b/secure/simul_efun/spare/spare/spare/livings.c
new file mode 100644
index 0000000..160fcee
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/livings.c
@@ -0,0 +1,348 @@
+// * living_name-Behandlung
+
+#define clean_log(s)
+//#define clean_log(s) log_file("CLEAN_SIM",ctime(time())[4..18]+": "+(s));
+
+private nosave mapping living_name_m, name_living_m, netdead;
+
+private void InitLivingData(mixed wizinfo) {
+  // living_name ist ein Pointer der auch in der extra_wizinfo gehalten 
+  // wird, so kann die simul_efun neu geladen werden, ohne dass dieses
+  // Mapping verloren geht
+  if (!mappingp(living_name_m = wizinfo[LIVING_NAME]))
+    living_name_m = wizinfo[LIVING_NAME] = m_allocate(0, 1);
+
+  // Gleiches gilt fuer das Mapping Name-Living
+  if (!mappingp(name_living_m = wizinfo[NAME_LIVING]))
+    name_living_m = wizinfo[NAME_LIVING] = m_allocate(0, 1);
+
+  // Netztote sind ebenfalls in der extra_wizinfo
+  if (!mappingp(netdead = wizinfo[NETDEAD_MAP]))
+    netdead = wizinfo[NETDEAD_MAP] = ([]);
+}
+
+public varargs string getuuid( object ob )
+{
+    mixed *ret;
+
+    if ( !objectp(ob) )
+       ob = previous_object();
+
+    if ( !query_once_interactive(ob) )
+       return getuid(ob);
+
+    ret = (mixed)master()->get_userinfo( getuid(ob) );
+
+    if ( !pointerp(ret) || sizeof(ret) < 5 )
+       return getuid(ob);
+
+    // Username + "_" + CreationDate
+    return ret[0] + "_" + ret[5];
+}
+
+void set_object_living_name(string livname, object obj)
+{
+  string old;
+  mixed a;
+  int i;
+
+  if (previous_object()==obj || previous_object()==this_object() ||
+      previous_object()==master())
+  {
+    if(!livname || !stringp(livname)) {
+      set_this_object(previous_object());
+      raise_error(sprintf("%O: illegal living name: %O\n", obj, livname));
+    }
+    if (old = living_name_m[obj]) {
+      if (pointerp(a = name_living_m[old])) {
+       a[member(a, obj)] = 0;
+      } else {
+       m_delete(name_living_m, old);
+      }
+    }
+    living_name_m[obj] = livname;
+    if (a = name_living_m[livname]) {
+      if (!pointerp(a)) {
+       name_living_m[livname] = ({a, obj});
+       return;
+      }
+      /* Try to reallocate entry from destructed object */
+      if ((i = member(a, 0)) >= 0) {
+       a[i] = obj;
+       return;
+      }
+      name_living_m[livname] = a + ({obj});
+      return;
+    }
+    name_living_m[livname] = obj;
+  }
+}
+
+void set_living_name(string livname)
+{
+  set_object_living_name(livname,previous_object());
+}
+
+void remove_living_name()
+{
+  string livname;
+
+  if (!previous_object())
+    return;
+  if (livname=living_name_m[previous_object()])
+  {
+    m_delete(living_name_m,previous_object());
+    if (objectp(name_living_m[livname]))
+    {
+      if (name_living_m[livname]==previous_object())
+       m_delete(name_living_m,livname);
+      return;
+    }
+    if (pointerp(name_living_m[livname]))
+    {
+      name_living_m[livname]-=({previous_object()});
+      if (!sizeof(name_living_m[livname]))
+       m_delete(name_living_m,livname);
+    }
+  }
+}
+
+void _set_netdead()
+{
+  if (query_once_interactive(previous_object()))
+    netdead[getuid(previous_object())]=previous_object();
+}
+
+void _remove_netdead()
+{
+  m_delete(netdead,getuid(previous_object()));
+}
+
+object find_netdead(string uuid)
+{
+  int i;
+  string uid;
+  // Wenn sscanf() 2 liefert, ist uuid auch ne uuid.
+  int is_uuid = sscanf(uuid, "%s_%d", uid, i) == 2;
+  if (!is_uuid)
+    uid = uuid;
+ 
+  if (is_uuid && getuuid(netdead[uid]) != uuid)
+      return 0;
+
+  return netdead[uid];
+}
+
+string *dump_netdead()
+{
+  return m_indices(netdead);
+}
+
+object find_living(string livname) {
+  mixed *a, r;
+  int i;
+
+  if (pointerp(r = name_living_m[livname])) {
+    if (member(r,0)>=0)
+      r-=({0});
+    if (!sizeof(r)){
+      m_delete(name_living_m,livname);
+      clean_log(sprintf("find_living loescht %s\n",livname));
+      return 0;
+    }
+    if ( !living(r = (a = r)[0])) {
+      for (i = sizeof(a); --i;) {
+       if (living(a[<i])) {
+         r = a[<i];
+         a[<i] = a[0];
+         return a[0] = r;
+       }
+      }
+    }
+    return r;
+  }
+  return living(r) && r;
+}
+
+object *find_livings(string livname)
+{
+  mixed r;
+
+  if (pointerp(r=name_living_m[livname]))
+    r-=({0});
+  else
+    if (objectp(r))
+      r=({r});
+  if (!pointerp(r)||!sizeof(r))
+    return 0;
+  return r;
+}
+
+object find_player(string uuid) {
+  object *objs;
+  mixed res;
+  int i;
+  string uid;
+
+  if (!stringp(uuid))
+    return 0;
+  // Wenn sscanf() 2 liefert, ist uuid auch ne uuid.
+  int is_uuid = sscanf(uuid, "%s_%d", uid, i) == 2;
+  if (!is_uuid)
+    uid = uuid;
+
+  if (pointerp(res = name_living_m[uid])) {
+    // zerstoerte Objekte ggf. entfernen
+    if (member(res,0)>=0) {
+      res-=({0});
+      name_living_m[uid] = res;
+    }
+    // ggf. Namen ohne Objekte entfernen.
+    if (!sizeof(res)){
+      m_delete(name_living_m,uid);
+      clean_log(sprintf("find_player loescht %s\n",uid));
+      return 0;
+    }
+    objs = res;
+    res = objs[0];
+    // Wenn das 0. Element der Spieler ist, sind wir fertig. Ansonsten suchen
+    // wir den Spieler und schieben ihn an die 0. Stelle im Array.
+    if ( !query_once_interactive(res)) {
+      for (i = sizeof(objs); --i;) {
+       if (objs[<i] && query_once_interactive(objs[<i])) {
+         res = objs[<i];
+         objs[<i] = objs[0];
+         objs[0] = res;
+         break;
+       }
+      }
+      res = (objectp(res) && query_once_interactive(res)) ? res : 0;
+    }
+    // else: in res steht der Spieler schon.
+  }
+  else if (objectp(res)) // r ist nen Einzelobjekt
+    res = query_once_interactive(res) ? res : 0;
+
+  // res ist jetzt ggf. der Spieler. Aber wenn der ne andere als die gesuchte
+  // UUID hat, ist das Ergebnis trotzdem 0.
+  if (is_uuid && getuuid(res)!=uuid)
+      return 0;
+
+  // endlich gefunden
+  return res;
+}
+
+private int check_match( string str, int players_only )
+{
+    mixed match;
+
+    if ( !(match = name_living_m[str]) )
+       return 0;
+
+    if ( !pointerp(match) )
+       match = ({ match });
+
+    match -= ({0});
+
+    if ( sizeof(match) ){
+       if ( players_only )
+           return sizeof(filter( match, #'query_once_interactive/*'*/ ))
+              > 0;
+       else
+           return 1;
+    }
+
+    m_delete( name_living_m, str );
+    clean_log( sprintf("check_match loescht %s\n", str) );
+    return 0;
+}
+
+//TODO:: string|string* exclude
+varargs mixed match_living( string str, int players_only, mixed exclude )
+{
+    int i, s;
+    mixed match, *user;
+
+    if ( !str || str == "" )
+       return 0;
+
+    if ( !pointerp(exclude) )
+       exclude = ({ exclude });
+
+    if ( member(exclude, str) < 0 && check_match(str, players_only) )
+       return str;
+
+    user = m_indices(name_living_m);
+    s = sizeof(str);
+    match = 0;
+
+    for ( i = sizeof(user); i--; )
+       if ( str == user[i][0..s-1] && member( exclude, user[i] ) < 0 )
+           if ( match )
+              return -1;
+           else
+              if ( check_match(user[i], players_only) )
+                  match = user[i];
+
+    if ( !match )
+       return -2;
+
+    return match;
+}
+
+mixed *dump_livings()
+{
+  return sort_array(m_indices(name_living_m),#'>);
+}
+
+// * regelmaessig Listen von Ballast befreien
+private void clean_name_living_m(string *keys, int left, int num)
+{
+  int i, j;
+  mixed a;
+
+  while (left && num--)
+  {
+    if (pointerp(a = name_living_m[keys[--left]]) && member(a, 0)>=0)
+    {
+      a-=({0});
+      name_living_m[keys[left]] = a;
+    }
+    if (!a || (pointerp(a) && !sizeof(a)))
+    {
+      clean_log("Toasting "+keys[left]+"\n");
+      m_delete(name_living_m, keys[left]);
+    } else clean_log(sprintf("KEEPING %s (%O)\n",keys[left],pointerp(a)?sizeof(a):a));
+  }
+  if (left)
+    efun::call_out(#'clean_name_living_m, 1, keys, left, 80);
+  else
+    clean_log("Clean process finished\n");
+}
+
+private void clean_netdead() {
+  int i;
+  string *s;
+  object ob;
+
+  s=m_indices(netdead);
+  for (i=sizeof(s)-1;i>=0;i--)
+    if (!objectp(ob=netdead[s[i]]) || interactive(ob))
+      m_delete(netdead,s[i]);
+}
+
+private void CleanLivingData() {
+  if (find_call_out(#'clean_name_living_m) < 0) {
+    clean_log("Starting clean process\n");
+    efun::call_out(#'clean_name_living_m,
+                 1,
+                 m_indices(name_living_m),
+                 sizeof(name_living_m),
+                 80
+                 );
+  }
+  efun::call_out(#'clean_netdead,2);
+}
+
+#undef clean_log
+
diff --git a/secure/simul_efun/spare/spare/spare/object_info.c b/secure/simul_efun/spare/spare/spare/object_info.c
new file mode 100644
index 0000000..ed7c009
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/object_info.c
@@ -0,0 +1,106 @@
+/* This sefun is to provide the old semantics of the efun object_info().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if __EFUN_DEFINED__(driver_info) /* Newer version is there */
+
+#include <objectinfo.h>
+#include <object_info.h>
+
+mixed object_info(object ob, int what, varargs int* index)
+{
+    mixed * result;
+
+    if (sizeof(index) > 1)
+        raise_error("Too many arguments to object_info\n");
+
+    switch(what)
+    {
+        default:
+            raise_error(sprintf("Illegal value %d for object_info().\n", what));
+
+        case OINFO_BASIC:
+        {
+            result = allocate(OIB_MAX);
+
+            result[OIB_HEART_BEAT]        = efun::object_info(ob, OC_HEART_BEAT);
+            result[OIB_IS_WIZARD]         = 0;   /* Not supported anymore. */
+            result[OIB_ENABLE_COMMANDS]   = efun::object_info(ob, OC_COMMANDS_ENABLED);
+            result[OIB_CLONE]             = efun::clonep(ob);
+            result[OIB_DESTRUCTED]        = 0;   /* Not possible anymore. */
+            result[OIB_SWAPPED]           = efun::object_info(ob, OI_SWAPPED);
+            result[OIB_ONCE_INTERACTIVE]  = efun::object_info(ob, OI_ONCE_INTERACTIVE);
+            result[OIB_RESET_STATE]       = efun::object_info(ob, OI_RESET_STATE);
+            result[OIB_WILL_CLEAN_UP]     = efun::object_info(ob, OI_WILL_CLEAN_UP);
+            result[OIB_LAMBDA_REFERENCED] = efun::object_info(ob, OI_LAMBDA_REFERENCED);
+            result[OIB_SHADOW]            = efun::object_info(ob, OI_SHADOW_PREV) && 1;
+            result[OIB_REPLACED]          = efun::object_info(ob, OI_REPLACED);
+            result[OIB_NEXT_RESET]        = efun::object_info(ob, OI_NEXT_RESET_TIME);
+            result[OIB_TIME_OF_REF]       = efun::object_info(ob, OI_LAST_REF_TIME);
+            result[OIB_REF]               = efun::object_info(ob, OI_OBJECT_REFS);
+            result[OIB_GIGATICKS]         = efun::object_info(ob, OI_GIGATICKS);
+            result[OIB_TICKS]             = efun::object_info(ob, OI_TICKS);
+            result[OIB_SWAP_NUM]          = efun::object_info(ob, OI_SWAP_NUM);
+            result[OIB_PROG_SWAPPED]      = efun::object_info(ob, OI_PROG_SWAPPED);
+            result[OIB_VAR_SWAPPED]       = efun::object_info(ob, OI_VAR_SWAPPED);
+            result[OIB_NAME]              = efun::object_name(ob);
+            result[OIB_LOAD_NAME]         = efun::load_name(ob);
+            result[OIB_NEXT_ALL]          = efun::object_info(ob, OI_OBJECT_NEXT);
+            result[OIB_PREV_ALL]          = efun::object_info(ob, OI_OBJECT_PREV);
+            result[OIB_NEXT_CLEANUP]      = efun::object_info(ob, OI_NEXT_CLEANUP_TIME);
+            break;
+        }
+
+        case OINFO_POSITION:
+        {
+            result = allocate(OIP_MAX);
+
+            result[OIP_NEXT] = efun::object_info(ob, OI_OBJECT_NEXT);
+            result[OIP_PREV] = efun::object_info(ob, OI_OBJECT_PREV);
+            result[OIP_POS]  = efun::object_info(ob, OI_OBJECT_POS);
+            break;
+        }
+
+        case OINFO_MEMORY:
+        {
+            result = allocate(OIM_MAX);
+
+            result[OIM_REF]                = efun::object_info(ob, OI_PROG_REFS);
+            result[OIM_NAME]               = efun::program_name(ob);
+            result[OIM_PROG_SIZE]          = efun::object_info(ob, OI_PROG_SIZE);
+            result[OIM_NUM_FUNCTIONS]      = efun::object_info(ob, OI_NUM_FUNCTIONS);
+            result[OIM_SIZE_FUNCTIONS]     = efun::object_info(ob, OI_SIZE_FUNCTIONS);
+            result[OIM_NUM_VARIABLES]      = efun::object_info(ob, OI_NUM_VARIABLES);
+            result[OIM_SIZE_VARIABLES]     = efun::object_info(ob, OI_SIZE_VARIABLES);
+            result[OIM_NUM_STRINGS]        = efun::object_info(ob, OI_NUM_STRINGS);
+            result[OIM_SIZE_STRINGS]       = efun::object_info(ob, OI_SIZE_STRINGS);
+            result[OIM_SIZE_STRINGS_DATA]  = efun::object_info(ob, OI_SIZE_STRINGS_DATA);
+            result[OIM_SIZE_STRINGS_TOTAL] = efun::object_info(ob, OI_SIZE_STRINGS_DATA_TOTAL);
+            result[OIM_NUM_INHERITED]      = efun::object_info(ob, OI_NUM_INHERITED);
+            result[OIM_SIZE_INHERITED]     = efun::object_info(ob, OI_SIZE_INHERITED);
+            result[OIM_TOTAL_SIZE]         = efun::object_info(ob, OI_PROG_SIZE_TOTAL);
+            result[OIM_DATA_SIZE]          = efun::object_info(ob, OI_DATA_SIZE);
+            result[OIM_TOTAL_DATA_SIZE]    = efun::object_info(ob, OI_DATA_SIZE_TOTAL);
+            result[OIM_NO_INHERIT]         = efun::object_info(ob, OI_NO_INHERIT);
+            result[OIM_NO_CLONE]           = efun::object_info(ob, OI_NO_CLONE);
+            result[OIM_NO_SHADOW]          = efun::object_info(ob, OI_NO_SHADOW);
+            result[OIM_NUM_INCLUDES]       = efun::object_info(ob, OI_NUM_INCLUDED);
+            result[OIM_SHARE_VARIABLES]    = efun::object_info(ob, OI_SHARE_VARIABLES);
+            break;
+        }
+    }
+
+    if (sizeof(index))
+    {
+        int idx = index[0];
+        if (idx < 0 || idx >= sizeof(result))
+            raise_error(sprintf("Illegal index for object_info(): %d, expected 0..%d\n",
+                idx, sizeof(result)-1));
+
+        return result[idx];
+    }
+    else
+        return result;
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/query_editing.c b/secure/simul_efun/spare/spare/spare/query_editing.c
new file mode 100644
index 0000000..5146dcd
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/query_editing.c
@@ -0,0 +1,17 @@
+/* This sefun is to provide a replacement for the efun query_editing().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_editing)
+
+#include <interactive_info.h>
+
+int|object query_editing(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    return efun::interactive_info(ob, II_EDITING);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/query_idle.c b/secure/simul_efun/spare/spare/spare/query_idle.c
new file mode 100644
index 0000000..93a32ae
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/query_idle.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_idle().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_idle)
+
+#include <interactive_info.h>
+
+int query_idle(object ob)
+{
+    return efun::interactive_info(ob, II_IDLE);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/query_input_pending.c b/secure/simul_efun/spare/spare/spare/query_input_pending.c
new file mode 100644
index 0000000..dd8c311
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/query_input_pending.c
@@ -0,0 +1,17 @@
+/* This sefun is to provide a replacement for the efun query_input_pending().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_input_pending)
+
+#include <interactive_info.h>
+
+object query_input_pending(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    return efun::interactive_info(ob, II_INPUT_PENDING);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/query_ip_name.c b/secure/simul_efun/spare/spare/spare/query_ip_name.c
new file mode 100644
index 0000000..1e74ea0
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/query_ip_name.c
@@ -0,0 +1,48 @@
+/* This sefun is to provide a replacement for the efuns query_ip_name() and
+ * query_ip_number().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_ip_name)
+
+#include <interactive_info.h>
+
+private varargs string _query_ip_name(object player)
+{
+    object ob = player;
+    ob ||= efun::this_player();
+
+    player = efun::interactive_info(ob, II_IP_ADDRESS);
+    return efun::interactive_info(ob, II_IP_NAME);
+}
+
+private varargs string _query_ip_number(object player)
+{
+    object ob = player;
+    ob ||= efun::this_player();
+
+    player = efun::interactive_info(ob, II_IP_ADDRESS);
+    return efun::interactive_info(ob, II_IP_NUMBER);
+}
+
+// * Herkunfts-Ermittlung
+string query_ip_number(object ob)
+{
+  ob= ob || this_player();
+  if (!objectp(ob) || !interactive(ob)) return 0;
+  if(ob->query_realip() && (string)ob->query_realip()!="")
+  {
+    return (string)ob->query_realip();
+  }
+  return _query_ip_number(ob);
+}
+
+string query_ip_name(mixed ob)
+{
+  if ( !ob || objectp(ob) )
+      ob=_query_ip_number(ob);
+  return (string)"/p/daemon/iplookup"->host(ob);
+}
+
+#endif
+
diff --git a/secure/simul_efun/spare/spare/spare/query_limits.c b/secure/simul_efun/spare/spare/spare/query_limits.c
new file mode 100644
index 0000000..ecbf390
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/query_limits.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_limits().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_limits)
+
+#include <driver_info.h>
+
+varargs int* query_limits(int def)
+{
+    return efun::driver_info(def ? DC_DEFAULT_RUNTIME_LIMITS : DI_CURRENT_RUNTIME_LIMITS);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/query_load_average.c b/secure/simul_efun/spare/spare/spare/query_load_average.c
new file mode 100644
index 0000000..3b36f0e
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/query_load_average.c
@@ -0,0 +1,16 @@
+/* This sefun is to provide a replacement for the efun query_load_average().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_load_average)
+
+#include <driver_info.h>
+
+string query_load_average()
+{
+    return efun::sprintf("%.2f cmds/s, %.2f comp lines/s",
+        efun::driver_info(DI_LOAD_AVERAGE_COMMANDS),
+        efun::driver_info(DI_LOAD_AVERAGE_LINES));
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/query_mud_port.c b/secure/simul_efun/spare/spare/spare/query_mud_port.c
new file mode 100644
index 0000000..bff009d
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/query_mud_port.c
@@ -0,0 +1,35 @@
+/* This sefun is to provide a replacement for the efun query_mud_port().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_mud_port)
+
+#include <interactive_info.h>
+#include <driver_info.h>
+
+int query_mud_port(varargs int*|object* args)
+{
+    if(!sizeof(args) && efun::this_player() && efun::interactive(this_player()))
+        return efun::interactive_info(this_player(), II_MUD_PORT);
+
+    if(sizeof(args) > 1)
+        raise_error("Too many arguments to query_mud_port\n");
+
+    if(sizeof(args) && efun::objectp(args[0]) && efun::interactive(args[0]))
+        return efun::interactive_info(args[0], II_MUD_PORT);
+
+    if(sizeof(args) && intp(args[0]))
+    {
+        int* ports = efun::driver_info(DI_MUD_PORTS);
+        if(args[0] < -1 || args[0] >= sizeof(ports))
+            raise_error(efun::sprintf("Bad arg 1 to query_mud_port(): value %d out of range.\n", args[0]));
+        else if(args[0] == -1)
+            return sizeof(ports);
+        else
+            return ports[args[0]];
+    }
+
+    return efun::driver_info(DI_MUD_PORTS)[0];
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/query_once_interactive.c b/secure/simul_efun/spare/spare/spare/query_once_interactive.c
new file mode 100644
index 0000000..bc7829f
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/query_once_interactive.c
@@ -0,0 +1,14 @@
+/* This sefun is to provide a replacement for the efun query_once_interactive().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_once_interactive)
+
+#include <object_info.h>
+
+int query_once_interactive(object ob)
+{
+    return efun::object_info(ob, OI_ONCE_INTERACTIVE);
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/query_snoop.c b/secure/simul_efun/spare/spare/spare/query_snoop.c
new file mode 100644
index 0000000..e6a69c0
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/query_snoop.c
@@ -0,0 +1,30 @@
+/* This sefun is to provide a replacement for the efun query_snoop().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+
+#include <interactive_info.h>
+
+private object _query_snoop(object ob)
+{
+    if(!efun::interactive(ob))
+        return 0;
+
+    object prev = efun::previous_object();
+    efun::set_this_object(prev);
+#if ! __EFUN_DEFINED__(query_snoop)
+    return efun::interactive_info(ob, II_SNOOP_NEXT);
+#else
+    return efun::query_snoop(ob);
+#endif
+}
+
+nomask object query_snoop(object who) {
+  object snooper;
+
+  snooper=_query_snoop(who);
+  if (!snooper) return 0;
+  if (query_wiz_grp(snooper)>query_wiz_grp(getuid(previous_object())) &&
+      IS_ARCH(snooper)) return 0;
+  return snooper;
+}
diff --git a/secure/simul_efun/spare/spare/spare/set_heart_beat.c b/secure/simul_efun/spare/spare/spare/set_heart_beat.c
new file mode 100644
index 0000000..a3995eb
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/set_heart_beat.c
@@ -0,0 +1,24 @@
+/* This sefun is to provide a replacement for the efun set_heart_beat().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(set_heart_beat)
+
+#include <configuration.h>
+
+int set_heart_beat(int flag)
+{
+    object ob = efun::previous_object();
+    int hb = efun::object_info(ob, OC_HEART_BEAT);
+
+    if (!flag == !hb)
+        return 0;
+
+    efun::configure_object(ob, OC_HEART_BEAT, flag);
+
+    return 1;
+}
+
+#endif
+
+
diff --git a/secure/simul_efun/spare/spare/spare/set_is_wizard.c b/secure/simul_efun/spare/spare/spare/set_is_wizard.c
new file mode 100644
index 0000000..001ccdb
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/set_is_wizard.c
@@ -0,0 +1,137 @@
+/* These are the special commands from the driver that are activated with
+ * set_is_wizard(). These functions must be added to the player object.
+ * Also set_is_wizard() must be called in the corresponding player object,
+ * not as an (simul-)efun.
+ */
+
+#include <commands.h>
+#include <driver_info.h>
+
+/* is_wizard:
+ *  1: has actions,
+ *  0: never had actions,
+ * -1: had actions once.
+ */
+private nosave int is_wizard;
+
+private int driver_malloc(string str)
+{
+    if(is_wizard <= 0)
+        return 0;
+
+    if(!sizeof(str))
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC));
+        return 1;
+    }
+
+    if(str == "extstats")
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC_EXTENDED));
+        return 1;
+    }
+
+    return 0;
+}
+
+private int driver_dumpallobj(string str)
+{
+    if(is_wizard <= 0 || sizeof(str))
+        return 0;
+
+    write("Dumping to /OBJ_DUMP ... ");
+    efun::dump_driver_info(DDI_OBJECTS);
+    efun::dump_driver_info(DDI_OBJECTS_DESTRUCTED);
+    write("done\n");
+    return 1;
+}
+
+private int driver_opcdump(string str)
+{
+    if(is_wizard <= 0 || sizeof(str))
+        return 0;
+
+    efun::dump_driver_info(DDI_OPCODES);
+    return 1;
+}
+
+private int driver_status(string str)
+{
+    int opt;
+    if(is_wizard <= 0)
+        return 0;
+
+    switch(str || "")
+    {
+        case "":
+            opt = DI_STATUS_TEXT_MEMORY;
+            break;
+
+        case "tables":
+        case " tables":
+            opt = DI_STATUS_TEXT_TABLES;
+            break;
+
+        case "swap":
+        case " swap":
+            opt = DI_STATUS_TEXT_SWAP;
+            break;
+
+        case "malloc":
+        case " malloc":
+            opt = DI_STATUS_TEXT_MALLOC;
+            break;
+
+        case "malloc extstats":
+        case " malloc extstats":
+            opt = DI_STATUS_TEXT_MALLOC_EXTENDED;
+            break;
+
+        default:
+            return 0;
+    }
+
+    write(efun::driver_info(opt));
+    return 1;
+}
+
+int set_is_wizard(varargs <object|int>* args)
+{
+    int oldval = is_wizard;
+
+    if(!sizeof(args))
+        raise_error("Too few arguments to set_is_wizard\n");
+    if(sizeof(args) > 2)
+        raise_error("Too many arguments to set_is_wizard\n");
+    if(!objectp(args[0]))
+        raise_error("Bad arg 1 to set_is_wizard()\n");
+    if(args[0] != this_object())
+        raise_error("Only set_is_wizard for the current object supported\n");
+    if(this_player() != this_object())
+        raise_error("The current object must be this_player() for set_is_wizard()\n");
+    if(sizeof(args) == 2 && !intp(args[1]))
+        raise_error("Bad arg 2 to set_is_wizard()\n");
+
+    if(sizeof(args) == 2 && !args[1])
+    {
+        if(is_wizard > 0)
+            is_wizard = -1;
+    }
+    else if(sizeof(args) == 2 && args[1]<0)
+    {
+         // Just return the old value.
+    }
+    else
+    {
+        if(!is_wizard)
+        {
+            add_action(#'driver_malloc, "malloc");
+            add_action(#'driver_dumpallobj, "dumpallobj");
+            add_action(#'driver_opcdump, "opcdump");
+            add_action(#'driver_status, "status", AA_NOSPACE);
+        }
+        is_wizard = 1;
+    }
+
+    return oldval > 0;
+}
diff --git a/secure/simul_efun/spare/spare/spare/set_prompt.c b/secure/simul_efun/spare/spare/spare/set_prompt.c
new file mode 100644
index 0000000..0b59692
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/set_prompt.c
@@ -0,0 +1,21 @@
+/* This sefun is to provide a replacement for the efun set_prompt().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(set_prompt)
+
+#include <configuration.h>
+
+varargs string|closure set_prompt(string|closure|int prompt, object ob)
+{
+    ob ||= efun::this_player();
+
+    mixed oldprompt = efun::interactive_info(ob, IC_PROMPT);
+
+    if(!intp(prompt))
+        efun::configure_interactive(ob, IC_PROMPT, prompt);
+
+    return oldprompt;
+}
+
+#endif
diff --git a/secure/simul_efun/spare/spare/spare/shadow.c b/secure/simul_efun/spare/spare/spare/shadow.c
new file mode 100644
index 0000000..7608c73
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/shadow.c
@@ -0,0 +1,55 @@
+/* These sefuns are to provide a replacement for the efun query_shadowing()
+ * and the old semantics of the efun shadow().
+ * Feel free to add it to your mudlibs, if you have much code relying on that.
+ */
+
+#if ! __EFUN_DEFINED__(query_shadowing)
+
+#include <object_info.h>
+
+object query_shadowing(object ob)
+{
+    return efun::object_info(ob, OI_SHADOW_PREV);
+}
+
+private object _shadow(object ob, int flag)
+{
+    if(flag)
+    {
+        object shadower = efun::previous_object(1);
+        efun::set_this_object(shadower);
+
+        if (efun::shadow(ob))
+            return efun::object_info(shadower, OI_SHADOW_PREV);
+        else
+            return 0;
+    }
+    else
+        return efun::object_info(ob, OI_SHADOW_NEXT);
+}
+#else
+private object _shadow(object ob, int flag)
+{
+  set_this_object(previous_object(1));
+  return efun::shadow(ob, flag);
+}
+#endif
+
+
+public object shadow(object ob, int flag)
+{
+  object res = funcall(#'_shadow,ob, flag);
+  if (flag)
+    "/secure/shadowmaster"->RegisterShadow(previous_object());
+  return res;
+}
+
+private void _unshadow() {
+  set_this_object(previous_object(1));
+  efun::unshadow();
+}
+
+public void unshadow() {
+  funcall(#'_unshadow);
+  "/secure/shadowmaster"->UnregisterShadow(previous_object());
+}
diff --git a/secure/simul_efun/spare/spare/spare/simul_efun.c b/secure/simul_efun/spare/spare/spare/simul_efun.c
new file mode 100644
index 0000000..5b68789
--- /dev/null
+++ b/secure/simul_efun/spare/spare/spare/simul_efun.c
@@ -0,0 +1,1995 @@
+// MorgenGrauen MUDlib
+//
+// simul_efun.c -- simul efun's
+//
+// $Id: simul_efun.c 7408 2010-02-06 00:27:25Z Zesstra $
+#pragma strict_types,save_types,rtt_checks
+#pragma no_clone,no_shadow,no_inherit
+#pragma pedantic,range_check,warn_deprecated
+#pragma warn_empty_casts,warn_missing_return,warn_function_inconsistent
+
+// Absolute Pfade erforderlich - zum Zeitpunkt, wo der Master geladen
+// wird, sind noch keine Include-Pfade da ...
+
+#define SNOOPLOGFILE "SNOOP"
+#define ASNOOPLOGFILE "ARCH/SNOOP"
+
+#include "/secure/config.h"
+#include "/secure/wizlevels.h"
+#include "/sys/snooping.h"
+#include "/sys/language.h"
+#include "/sys/thing/properties.h"
+#include "/sys/wizlist.h"
+#include "/sys/erq.h"
+#include "/sys/lpctypes.h"
+#include "/sys/daemon.h"
+#include "/sys/player/base.h"
+#include "/sys/thing/description.h"
+#include "/sys/container.h"
+#include "/sys/defines.h"
+#include "/sys/telnet.h"
+#include "/sys/objectinfo.h"
+#include "/sys/files.h"
+#include "/sys/strings.h"
+#include "/sys/time.h"
+#include "/sys/lpctypes.h"
+#include "/sys/notify_fail.h"
+#include "/sys/tls.h"
+#include "/sys/input_to.h"
+#include "/sys/object_info.h"
+
+/* function prototypes
+ */
+string dtime(int wann);
+varargs int log_file(string file, string txt, int size_to_break);
+int query_wiz_level(mixed player);
+nomask varargs int snoop(object me, object you);
+varargs string country(mixed ip, string num);
+int query_wiz_grp(mixed wiz);
+public varargs object deep_present(mixed what, object ob);
+nomask int secure_level();
+nomask string secure_euid();
+public nomask int process_call();
+nomask mixed __create_player_dummy(string name);
+varargs string replace_personal(string str, mixed *obs, int caps);
+
+
+//replacements for dropped efuns in LD
+#if !__EFUN_DEFINED__(extract)
+varargs string extract(string str, int from, int to);
+#endif
+#if !__EFUN_DEFINED__(slice_array)
+varargs mixed slice_array(mixed array, int from, int to);
+#endif
+#if !__EFUN_DEFINED__(member_array)
+int member_array(mixed item, mixed arraystring);
+#endif
+
+// Include the different sub 'modules' of the simul_efun
+#include __DIR__"debug_info.c"
+#include __DIR__"enable_commands.c"
+#include __DIR__"hash.c"
+#include __DIR__"object_info.c"
+#include __DIR__"query_editing.c"
+#include __DIR__"query_idle.c"
+#include __DIR__"query_input_pending.c"
+#include __DIR__"query_ip_name.c"
+#include __DIR__"query_limits.c"
+#include __DIR__"query_load_average.c"
+#include __DIR__"query_mud_port.c"
+#include __DIR__"query_once_interactive.c"
+#include __DIR__"query_snoop.c"
+#include __DIR__"set_heart_beat.c"
+#if __BOOT_TIME__ < 1456261859
+#include __DIR__"set_prompt.c"
+#endif
+#include __DIR__"shadow.c"
+#include __DIR__"livings.c"
+#include __DIR__"comm.c"
+
+#define TO        efun::this_object()
+#define TI        efun::this_interactive()
+#define TP        efun::this_player()
+#define PO        efun::previous_object(0)
+#define LEVEL(x) query_wiz_level(x)
+#define NAME(x)  capitalize(getuid(x))
+
+#define DEBUG(x) if (find_player("zesstra")) \
+  tell_object(find_player("zesstra"),x)
+
+mixed dtime_cache = ({-1,""});
+
+#ifdef IOSTATS
+struct iostat_s {
+  string oname;
+  int time;
+  int size;
+};
+mixed saveo_stat = ({ 0,allocate(200, 0) });
+mixed restoreo_stat = ({ 0,allocate(200,0) });
+//mixed writefile_stat = ({ 0,allocate(100,(<iostat_s>)) });
+//mixed readfile_stat = ({ 0,allocate(100,(<iostat_s>)) });
+//mixed log_stat = ({ 0,allocate(100,(<iostat_s>)) });
+
+mixed ___iostats(int type) {
+  switch(type) {
+    case 1:
+      return saveo_stat;
+    case 2:
+      return restoreo_stat;
+/*    case 3:
+      return writefile_stat;
+    case 4:
+      return readfile_stat;
+    case 5:
+      return log_stat;
+      */
+  }
+  return 0;
+}
+#endif
+
+// Nicht jeder Magier muss die simul_efun entsorgen koennen.
+string NotifyDestruct(object caller) {
+    if( (caller!=this_object() && !ARCH_SECURITY) || process_call() ) {
+      return "Du darfst das simul_efun Objekt nicht zerstoeren!\n";
+    }
+    return 0;
+}
+
+public nomask void remove_interactive( object ob )
+{
+    if ( objectp(ob) && previous_object()
+        && object_name(previous_object())[0..7] != "/secure/"
+        && ((previous_object() != ob
+             && (ob != this_player() || ob != this_interactive()))
+            || (previous_object() == ob
+               && (this_player() && this_player() != ob
+                   || this_interactive() && this_interactive() != ob)) ) )
+
+       log_file( "PLAYERDEST",
+                sprintf( "%s: %O ausgeloggt von PO %O, TI %O, TP %O\n",
+                        dtime(time()), ob, previous_object(),
+                        this_interactive(), this_player() ) );
+
+    efun::remove_interactive(ob);
+}
+
+
+void ___updmaster()
+{
+    object ob;
+
+    //sollte nicht jeder duerfen.
+    if (process_call() || !ARCH_SECURITY)
+      raise_error("Illegal use of ___updmaster()!");
+
+    write("Removing old master ... ");
+    foreach(string file: 
+        get_dir("/secure/master/*.c",GETDIR_NAMES|GETDIR_UNSORTED|GETDIR_PATH)) {
+      if (ob = find_object(file))
+       efun::destruct(ob);
+    }
+    efun::destruct(efun::find_object("/secure/master"));
+    write("done.\nLoading again ... ");
+    load_object("/secure/master");
+
+    write("done.\n");
+}
+
+varargs string country(mixed ip, string num) {
+  mixed ret;
+
+  if(ret = (string)"/p/daemon/iplookup"->country(num || ip)) {
+    return ret;
+  } else return "???";
+}
+
+
+// * Snoopen und was dazugehoert
+static object find_snooped(object who)
+{
+  object *u;
+  int i;
+
+  for (i=sizeof(u=users())-1;i>=0;i--)
+    if (who==efun::interactive_info(u[i], II_SNOOP_NEXT))
+      return u[i];
+  return 0;
+}
+
+private string Lcut(string str) {
+  return str[5..11]+str[18..];
+}
+
+nomask varargs int snoop( object me, object you )
+{
+    int ret;
+    object snooper0, snooper, snooper2, snooper3;
+
+    if( !objectp(me) || me == you || !PO )
+       return 0;
+
+    snooper0 = find_snooped(me);
+
+     if(you){
+        if ( PO != me && query_wiz_grp(me) >= query_wiz_grp(geteuid(PO)) )
+            return 0;
+
+        if ( query_wiz_grp(me) <= query_wiz_grp(you) &&
+             !(you->QueryAllowSnoop(me)) )
+            if ( !IS_DEPUTY(me) || IS_ARCH(you) )
+               return 0;
+
+        if ( (snooper = efun::interactive_info(you, II_SNOOP_NEXT)) &&
+             query_wiz_grp(snooper) >= query_wiz_grp(me) ){
+            if ( (int)snooper->QueryProp(P_SNOOPFLAGS) & SF_LOCKED )
+               return 0;
+
+            tell_object( snooper, sprintf( "%s snooped jetzt %s.\n",
+                                       me->name(WER), you->name(WER) ) );
+
+            snooper2 = me;
+
+            while ( snooper3 = interactive_info(snooper2, II_SNOOP_NEXT) ){
+               tell_object( snooper,
+                           sprintf( "%s wird seinerseits von %s gesnooped.\n"
+                                   ,snooper2->name(WER),
+                                   snooper3->name(WEM) ) );
+               snooper2 = snooper3;
+            }
+
+            efun::snoop( snooper, snooper2 );
+
+            if ( efun::interactive_info(snooper2, II_SNOOP_NEXT) != snooper )
+               tell_object( snooper, sprintf( "Du kannst %s nicht snoopen.\n",
+                                          snooper2->name(WEN) ) );
+            else{
+               tell_object( snooper, sprintf( "Du snoopst jetzt %s.\n",
+                                          snooper2->name(WEN) ) );
+               if ( !IS_DEPUTY(snooper) ){
+                   log_file( SNOOPLOGFILE, sprintf("%s: %O %O %O\n",
+                                               dtime(time()),
+                                               snooper,
+                                               snooper2,
+                                               environment(snooper2) ),
+                            100000 );
+                   if (snooper0)
+                      CHMASTER->send( "Snoop", snooper,
+                                    sprintf( "%s *OFF* %s (%O)",
+                                            capitalize(getuid(snooper)),
+                                            capitalize(getuid(snooper0)),
+                                            environment(snooper0) ) );
+
+                   CHMASTER->send( "Snoop", snooper,
+                                 sprintf("%s -> %s (%O)",
+                                        capitalize(getuid(snooper)),
+                                        capitalize(getuid(snooper2)),
+                                        environment(snooper2)));
+               }
+               else{
+                   log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                                 dtime(time()),
+                                                 snooper,
+                                                 snooper2,
+                                                 environment(snooper2) )
+                            ,100000 );
+               }
+            }
+        }
+        else
+            if (snooper)
+               if ( !me->QueryProp(P_SNOOPFLAGS) & SF_LOCKED ){
+                   printf( "%s wird bereits von %s gesnooped. Benutze das "
+                          "\"f\"-Flag, wenn du dennoch snoopen willst.\n",
+                          you->name(WER), snooper->name(WEM) );
+                   return 0;
+               }
+
+        ret = efun::snoop( me, you );
+
+        if ( !IS_DEPUTY(me) && efun::interactive_info(you, II_SNOOP_NEXT) == me){
+            log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                         Lcut(dtime(time())),
+                                         me, you, environment(you) ),
+                     100000 );
+
+            if (snooper0)
+               CHMASTER->send( "Snoop", me,
+                             sprintf( "%s *OFF* %s (%O).",
+                                     capitalize(getuid(me)),
+                                     capitalize(getuid(snooper0)),
+                                     environment(snooper0) ) );
+
+            CHMASTER->send( "Snoop", me, sprintf( "%s -> %s (%O).",
+                                             capitalize(getuid(me)),
+                                             capitalize(getuid(you)),
+                                             environment(you) ) );
+        }
+        else{
+            if ( efun::interactive_info(you, II_SNOOP_NEXT) == me ){
+               log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O\n",
+                                             Lcut(dtime(time())),
+                                             me, you, environment(you) ),
+                        100000 );
+            }
+        }
+
+        if ( ret && query_wiz_grp(me) <= query_wiz_grp(you) &&
+             !IS_DEPUTY(me) )
+            tell_object( you, "*** " + NAME(me) + " snoopt Dich!\n" );
+
+        return ret;
+     }
+     else {
+        if ( (me == PO ||
+              query_wiz_grp(geteuid(PO)) > query_wiz_grp(me) ||
+              (query_wiz_grp(geteuid(PO)) == query_wiz_grp(me) &&
+              efun::interactive_info(PO, II_SNOOP_NEXT) == me)) && snooper0 ){
+            if ( !IS_DEPUTY(me) ){
+               log_file( SNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
+                                            Lcut(dtime(time())), me,
+                                            snooper0,
+                                            environment(snooper0) ),
+                        100000 );
+
+                CHMASTER->send( "Snoop", me,
+                              sprintf( "%s *OFF* %s (%O).",
+                                      capitalize(getuid(me)),
+                                      capitalize(getuid(snooper0)),
+                                      environment(snooper0) ) );
+            }
+            else{
+               log_file( ASNOOPLOGFILE, sprintf( "%s: %O %O %O *OFF*\n",
+                                             Lcut(dtime(time())), me,
+                                             snooper0,
+                                             environment(snooper0) ),
+                        100000 );
+            }
+
+            return efun::snoop(me);
+        }
+     }
+     return 0;
+}
+
+
+
+// * Emulation des 'alten' explode durch das neue
+string *old_explode(string str, string del) {
+  int s, t;
+  string *strs;
+
+  if (!stringp(str)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Invalid argument 1 to old_explode()! Expected <string>, got: "
+         "%.30O\n",str));
+  }
+  if (!stringp(del)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Invalid argument 2 to old_explode()! Expected <string>, got: "
+         "%.30O\n",del));
+  }
+  if(del == "")
+    return ({str});
+  strs=efun::explode(str, del);
+  t=sizeof(strs)-1;
+  while(s<=t && strs[s++] == "");s--;
+  while(t>=0 && strs[t--] == "");t++;
+  if(s<=t)
+    return strs[s..t];
+  return ({});
+}
+
+int file_time(string path) {
+  mixed *v;
+
+  set_this_object(previous_object());
+  if (sizeof(v=get_dir(path,GETDIR_DATES))) return v[0];
+  return(0); //sonst
+}
+
+// * Bei 50k Groesse Log-File rotieren
+varargs int log_file(string file, string txt, int size_to_break) {
+  mixed *st;
+
+  file="/log/"+file;
+  file=implode((efun::explode(file,"/")-({".."})),"/");
+//  tell_object(find_player("jof"),sprintf("LOG FILE: %O -> %O\n",previous_object(),file));
+  if (!funcall(bind_lambda(#'efun::call_other,PO),"secure/master",//')
+              "valid_write",file,geteuid(PO),"log_file",PO))
+      return 0;
+  if ( size_to_break >= 0 & (
+      sizeof(st = get_dir(file,2) ) && st[0] >= (size_to_break|MAX_LOG_SIZE)))
+      catch(rename(file, file + ".old");publish); /* No panic if failure */
+  return(write_file(file,txt));
+}
+
+// * Magier-Level abfragen
+int query_wiz_level(mixed player) {
+  return (int)"/secure/master"->query_wiz_level(player);
+}
+
+#ifdef __ALISTS__
+// * Element aus Alist loeschen (by key)
+mixed *remove_alist(mixed key,mixed *alist)
+{
+  int i,j;
+
+  if (!pointerp(alist) || !sizeof(alist))
+    return alist;
+  if (!pointerp(alist[0]))
+  {
+    if ((i=assoc(key,alist))<0)
+      return alist;
+    return alist[0..i-1]+alist[i+1..];
+  }
+  i = assoc(key,alist[0]);
+  if ((i=assoc(key,alist[0]))<0)
+    return alist;
+  alist=alist[0..];
+  for (j=sizeof(alist)-1;j>=0;j--)
+    alist[j]=alist[j][0..i-1]+alist[j][i+1..];
+  return alist;
+}
+
+// * Element aus Alist loeschen (by pos)
+mixed *exclude_alist(int i,mixed *alist)
+{
+  int j;
+  if (!pointerp(alist) || !sizeof(alist) || i<0)
+    return alist;
+  if (!pointerp(alist[0]))
+    return alist[0..i-1]+alist[i+1..];
+  alist=alist[0..]; /* Create PHYSICAL copy of alist */
+  for (j=sizeof(alist)-1;j>=0;j--)
+    alist[j]=alist[j][0..i-1]+alist[j][i+1..];
+  return alist; /* order_alist is NOT necessary - see /doc/LPC/alist */
+}
+#endif // __ALISTS__
+
+// * German version of ctime()
+#define TAGE ({"Son","Mon","Die","Mit","Don","Fre","Sam"})
+#define MONATE ({"Jan","Feb","Mar","Apr","Mai","Jun","Jul","Aug",\
+                 "Sep","Okt","Nov","Dez"})
+string dtime(int wann) {
+  
+  if (wann == dtime_cache[0])
+    return(dtime_cache[1]);
+
+  int *lt = localtime(wann);
+  return sprintf("%s, %2d. %s %d, %02d:%02d:%02d",
+      TAGE[lt[TM_WDAY]], lt[TM_MDAY], MONATE[lt[TM_MON]], 
+      lt[TM_YEAR],lt[TM_HOUR], lt[TM_MIN], lt[TM_SEC]);
+}
+
+// wenn strftime im Driver nicht existiert, ist dies hier ein Alias auf dtime(),
+// zwar stimmt das Format dann nicht, aber die Mudlib buggt nicht und schreibt
+// ein ordentliches Datum/Uhrzeit.
+#if !__EFUN_DEFINED__(strftime)
+varargs string strftime(mixed fmt, int clock, int localized) {
+  if (intp(clock) && clock >= 0)
+    return dtime(clock);
+  else if (intp(fmt) && fmt >= 0)
+    return dtime(fmt);
+  
+  return dtime(time());
+}
+#endif //!__EFUN_DEFINED__(strftime)
+
+// * Shutdown mit zusaetzlichem logging
+nomask int shutdown(string reason)
+{
+  string name;
+  string obname;
+  string output;
+
+  if (!reason)
+    return 0;
+  if ( !ARCH_SECURITY && getuid(previous_object())!=ROOTID &&
+          object_name(previous_object())!="/obj/shut" )
+  {
+    write("You have no permission to shut down the gamedriver!\n");
+    return 0;
+  }
+  if ((this_interactive())&&(name=getuid(this_interactive())))
+  {
+    name=capitalize(name);
+    filter(users(),#'tell_object,//'
+               capitalize(name)+" faehrt das Spiel herunter!\n");
+  }
+  else
+    name="ANONYMOUS";
+  if (previous_object()) obname=capitalize(getuid(previous_object()));
+  output=name;
+  if (obname && name!=obname) output=output+" ("+obname+")";
+  if (previous_object()&&object_name(previous_object())=="/obj/shut"){
+    output+=" faehrt das Spiel via Armageddon herunter.\n";
+    output=dtime(time())+": "+output;
+    log_file("GAME_LOG",output+"\n",-1);
+    efun::shutdown();
+    return 1;
+  }
+  output=ctime(time())+": "+output+" faehrt das Spiel herunter.\n";
+  output+="    Grund: "+reason;
+  log_file("GAME_LOG",output+"\n",-1);
+  efun::shutdown();
+  return 1;
+}
+
+// * lowerchar
+
+int lowerchar(int char) {
+  if (char<'A' || char>'Z') return char;
+  return char+32;
+}
+
+// * upperstring
+
+string upperstring(string s)
+{
+#if __EFUN_DEFINED__(upper_case)
+  return(upper_case(s));
+#else
+  int i;
+  if (!stringp(s)) return 0;
+  for (i=sizeof(s)-1;i>=0;i--) s[i]=((s[i]<'a'||s[i]>'z')?s[i]:s[i]-32);
+  return s;
+#endif
+}
+
+// * lowerstring
+
+string lowerstring(string s)
+{
+  if (!stringp(s)) return 0;
+  return lower_case(s);
+}
+
+
+// * GD version
+string version()
+{
+  return __VERSION__;
+}
+
+// * break_string
+// stretch() -- stretch a line to fill a given width 
+private string stretch(string s, int width) {
+  int len=sizeof(s);
+  if (len==width) return s;
+
+  // reine Leerzeilen, direkt zurueckgeben
+  string trimmed=trim(s,TRIM_LEFT," ");
+  if (trimmed=="") return s; 
+  int start_spaces = len - sizeof(trimmed);
+
+  string* words = explode(trimmed, " ");
+  // der letzte kriegt keine Spaces
+  int word_count=sizeof(words) - 1;
+  // wenn Zeile nur aus einem Wort, wird das Wort zurueckgegeben
+  if (!word_count)
+    return " "*start_spaces + words[0];
+
+  int space_count = width - len;
+
+  int space_per_word=(word_count+space_count) / word_count;
+  // Anz.Woerter mit Zusatz-Space
+  int rest=(word_count+space_count) % word_count; 
+  // Rest-Spaces Verteilen
+  foreach (int pos : rest) words[pos]+=" ";
+  return (" "*start_spaces) + implode( words, " "*space_per_word );
+}
+
+// aus Geschwindigkeitsgruenden hat der Blocksatz fuer break_string eine
+// eigene Funktion bekommen:
+private varargs string block_string(string s, int width, int flags) {
+  // wenn BS_LEAVE_MY_LFS, aber kein BS_NO_PARINDENT, dann werden Zeilen mit
+  // einem Leerzeichen begonnen.
+  // BTW: Wenn !BS_LEAVE_MY_LFS, hat der Aufrufer bereits alle \n durch " "
+  // ersetzt.
+  if ( (flags & BS_LEAVE_MY_LFS)
+      && !(flags & BS_NO_PARINDENT))
+  {
+      s = " "+regreplace(s,"\n","\n ",1);
+  }
+
+  // sprintf fuellt die letzte Zeile auf die Feldbreite (hier also
+  // Zeilenbreite) mit Fuellzeichen auf, wenn sie NICHT mit \n umgebrochen
+  // ist. Es wird an die letzte Zeile aber kein Zeilenumbruch angehaengt.
+  // Eigentlich ist das Auffuellen doof, aber vermutlich ist es unnoetig, es
+  // wieder rueckgaengig zu machen.
+  s = sprintf( "%-*=s", width, s);
+
+  string *tmp=explode(s, "\n");
+  // Nur wenn s mehrzeilig ist, Blocksatz draus machen. Die letzte Zeile wird
+  // natuerlich nicht gedehnt. Sie ist dafuer schon von sprintf() aufgefuellt
+  // worden. BTW: Die letzte Zeile endet u.U. noch nicht mit einem \n (bzw.
+  // nur dann, wenn BS_LEAVE_MY_LFS und der uebergebene Text schon nen \n am
+  // Ende der letzten Zeile hat), das macht der Aufrufer...
+  if (sizeof(tmp) > 1)
+    return implode( map( tmp[0..<2], #'stretch/*'*/, width ), "\n" ) 
+      + "\n" + tmp[<1];
+
+  return s;
+}
+
+public varargs string break_string(string s, int w, mixed indent, int flags)
+{
+    if ( !s || s == "" ) return "";
+
+    if ( !w ) w=78;
+
+    if( intp(indent) )
+       indent = indent ? " "*indent : "";
+
+    int indentlen=stringp(indent) ? sizeof(indent) : 0;
+
+    if (indentlen>w) {
+      set_this_object(previous_object());
+      raise_error(sprintf("break_string: indent longer %d than width %d\n",
+                  indentlen,w));
+      // w=((indentlen/w)+1)*w;
+    }
+
+    if (!(flags & BS_LEAVE_MY_LFS)) 
+      s=regreplace( s, "\n", " ", 1 );
+
+    if ( flags & BS_SINGLE_SPACE )
+       s = regreplace( s, "(^|\n| )  *", "\\1", 1 );
+ 
+    string prefix="";
+    if (indentlen && flags & BS_PREPEND_INDENT) {
+      if (indentlen+sizeof(s) > w || 
+         (flags & BS_LEAVE_MY_LFS) && strstr(s,"\n")>-1) {
+       prefix=indent+"\n";
+       indent=(flags & BS_NO_PARINDENT) ? "" : " ";
+       indentlen=sizeof(indent);
+      }
+    }
+
+    if ( flags & BS_BLOCK ) {
+      /*
+           s = implode( map( explode( s, "\n" ),
+                               #'block_string, w, indentlen, flags),
+                      "" );
+      */
+      s = block_string( s , w - indentlen, flags );
+    }
+    else {
+      s = sprintf("%-1.*=s",w-indentlen,s);
+    }
+    if ( s[<1] != '\n' ) s += "\n";
+
+    if ( !indentlen ) return prefix + s;
+    
+    string indent2 = ( flags & BS_INDENT_ONCE ) ? (" "*indentlen) :indent;
+      
+    return prefix + indent + 
+      regreplace( s[0..<2], "\n", "\n"+indent2, 1 ) + "\n";
+      /*
+       string *buf;
+
+       buf = explode( s, "\n" );
+       return prefix + indent + implode( buf[0..<2], "\n"+indent2 ) + buf[<1] + "\n";
+      */
+}
+
+// * Elemente aus mapping loeschen - mapping vorher kopieren
+
+mapping m_copy_delete(mapping m, mixed key) {
+  return m_delete(copy(m), key);
+}
+
+// * times
+int last_reboot_time()
+{
+  return __BOOT_TIME__;
+}
+
+int first_boot_time()
+{
+  return 701517600;
+}
+
+int exist_days()
+{
+  return (((time()-first_boot_time())/8640)+5)/10;
+}
+
+// * uptime :)
+string uptime()
+{
+  int t;
+  int tmp;
+  string s;
+
+  t=time()-__BOOT_TIME__;
+  s="";
+  if (t>=86400)
+    s+=sprintf("%d Tag%s, ",tmp=t/86400,(tmp==1?"":"e"));
+  if (t>=3600)
+    s+=sprintf("%d Stunde%s, ",tmp=(t=t%86400)/3600,(tmp==1?"":"n"));
+  if (t>60)
+    s+=sprintf("%d Minute%s und ",tmp=(t=t%3600)/60,(tmp==1?"":"n"));
+  return s+sprintf("%d Sekunde%s",t=t%60,(t==1?"":"n"));
+}
+
+// * Was tun bei 'dangling' lfun-closures ?
+void dangling_lfun_closure() {
+  raise_error("dangling lfun closure\n");
+}
+
+// * Sperren ausser fuer master/simul_efun
+
+#if __EFUN_DEFINED__(set_environment)
+nomask void set_environment(object o1, object o2) {
+  raise_error("Available only for root\n");
+}
+#endif
+
+nomask void set_this_player(object pl) {
+  raise_error("Available only for root\n");
+}
+
+#if __EFUN_DEFINED__(export_uid)
+nomask void export_uid(object ob) {
+  raise_error("Available only for root\n");
+}
+#endif
+
+// * Jetzt auch closures
+int process_flag;
+
+public nomask int process_call()
+{
+  if (process_flag>0)
+    return process_flag;
+  else return(0);
+}
+
+private nomask string _process_string(string str,object po) {
+              set_this_object(po);
+              return(efun::process_string(str));
+}
+
+nomask string process_string( mixed str )
+{
+  string tmp, err;
+  int flag; 
+
+  if ( closurep(str) ) {
+      set_this_object( previous_object() );
+      return funcall(str);
+  }
+  else if (str==0)
+      return((string)str);
+  else if ( !stringp(str) ) {
+      return to_string(str);
+  }
+
+  if ( !(flag = process_flag > time() - 60))                     
+      process_flag=time();
+
+  err = catch(tmp = funcall(#'_process_string,str,previous_object()); publish);
+
+  if ( !flag )
+    process_flag=0;
+
+  if (err) {
+    // Verarbeitung abbrechen
+    set_this_object(previous_object());
+    raise_error(err);
+  }
+  return tmp;
+}
+
+// 'mkdir -p' - erzeugt eine komplette Hierarchie von Verzeichnissen.
+// wenn das Verzeichnis angelegt wurde oder schon existiert, wird 1
+// zurueckgeliefert, sonst 0.
+// Wirft einen Fehler, wenn das angebene Verzeichnis nicht absolut ist!
+public int mkdirp(string dir) {
+  // wenn es nur keinen fuehrenden / gibt, ist das ein Fehler.
+  if (strstr(dir, "/") != 0)
+    raise_error("mkdirp(): Pfad ist nicht absolute.\n");
+  // cut off trailing /...
+  if (dir[<1]=='/')
+      dir = dir[0..<2];
+
+  int fstat = file_size(dir);
+  // wenn es schon existiert, tun wir einfach so, als haetten wir es angelegt.
+  if (fstat == FSIZE_DIR)
+    return 1;
+  // wenn schon ne Datei existiert, geht es nicht.
+  if (fstat != FSIZE_NOFILE)
+    return 0;
+  // wenn es nur einen / gibt (den fuehrenden), dann ist es ein
+  // toplevel-verzeichnis, was direkt angelegt wird.
+  if (strrstr(dir,"/")==0) {
+    return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
+  }
+
+  // mkdir() nicht direkt rufen, sondern vorher als closure ans aufrufende
+  // Objekt binden. Sonst laeuft die Rechtepruefung in valid_write() im Master
+  // unter der Annahme, dass die simul_efun.c mit ihrer root id was will.
+
+  // jetzt rekursiv die Verzeichnishierarchie anlegen. Wenn das erfolgreich
+  // ist, dann koennen wir jetzt mit mkdir das tiefste Verzeichnis anlegen
+  if (mkdirp(dir[0..strrstr(dir,"/")-1]) == 1)
+    return funcall(bind_lambda(#'efun::mkdir, previous_object()), dir);
+}
+
+
+// * Properties ggfs. mitspeichern
+mixed save_object(mixed name)
+{
+  mapping properties;
+  mapping save;
+  mixed index, res;
+  int i;
+
+  // nur Strings und 0 zulassen
+  if ((!stringp(name) || !sizeof(name)) && 
+      (!intp(name) || name!=0)) {
+      set_this_object(previous_object());
+      raise_error(sprintf(
+         "Only non-empty strings and 0 may be used as filename in "
+         "sefun::save_object()! Argument was %O\n",name));
+  }
+
+  save = m_allocate(0, 2);
+  properties = (mapping)previous_object()->QueryProperties();
+
+  if(mappingp(properties))
+  {
+    // delete all entries in mapping properties without SAVE flag!
+    index = m_indices(properties);
+    for(i = sizeof(index)-1; i>=0;i--)
+    {
+      if(properties[index[i], F_MODE] & SAVE)
+      {
+       save[index[i]] = properties[index[i]];
+       save[index[i], F_MODE] =
+       properties[index[i], F_MODE] &
+                    (~(SETMNOTFOUND|QUERYMNOTFOUND|QUERYCACHED|SETCACHED));
+      }
+    }
+  }
+  else save = ([]);
+
+  // save object!
+  previous_object()->_set_save_data(save);
+  // format: wie definiert in config.h
+  if (stringp(name))
+    res = funcall(bind_lambda(#'efun::save_object, previous_object()), name,
+       __LIB__SAVE_FORMAT_VERSION__);
+  else
+    res = funcall(bind_lambda(#'efun::save_object, previous_object()),
+       __LIB__SAVE_FORMAT_VERSION__);
+  previous_object()->_set_save_data(0);
+
+#ifdef IOSTATS
+  // Stats...
+  struct iostat_s stat = (<iostat_s>);
+  stat->oname = object_name(previous_object());
+  stat->time = time();
+  //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
+  //    OIM_TOTAL_DATA_SIZE);
+  if (stringp(name))
+      stat->size = file_size(name + ".o");
+  else
+      stat->sizeof(res);
+  //debug_message("saveo: "+saveo_stat[0]+"\n");
+  saveo_stat[1][saveo_stat[0]] = stat;
+  saveo_stat[0] = (saveo_stat[0] + 1) % sizeof(saveo_stat[1]);
+  //debug_message("saveo 2: "+saveo_stat[0]+"\n");
+#endif
+
+  return res;
+}
+
+// * Auch Properties laden
+int restore_object(string name)
+{
+  int result;
+  mixed index;
+  mixed save;
+  mapping properties;
+  int i;
+  closure cl;
+
+  // get actual property settings (by create())
+  properties = (mapping)previous_object()->QueryProperties();
+
+//  DEBUG(sprintf("RESTORE %O\n",name));
+  // restore object
+  result=funcall(bind_lambda(#'efun::restore_object, previous_object()), name);
+  //'))
+  //_get_save_data liefert tatsaechlich mixed zurueck, wenn das auch immer ein 
+  //mapping sein sollte.
+  save = (mixed)previous_object()->_get_save_data();
+  if(mappingp(save))
+  {
+    index = m_indices(save);
+    for(i = sizeof(index)-1; i>=0; i--)
+    {
+      properties[index[i]] = save[index[i]];
+      properties[index[i], F_MODE] = save[index[i], F_MODE]
+                            &~(SETCACHED|QUERYCACHED);
+    }
+  }
+  else properties = ([]);
+
+  // restore properties
+  funcall(
+          bind_lambda(
+                     unbound_lambda(({'arg}), //'})
+                                  ({#'call_other,({#'this_object}),
+                                  "SetProperties",'arg})),//')
+                     previous_object()),properties);
+  previous_object()->_set_save_data(0);
+
+#ifdef IOSTATS
+  // Stats...
+  //debug_message("restoreo: "+restoreo_stat[0]+"\n");
+  struct iostat_s stat = (<iostat_s>);
+  stat->oname = object_name(previous_object());
+  stat->time = time();
+  //stat->size = (int)object_info(previous_object(),OINFO_MEMORY,
+  //    OIM_TOTAL_DATA_SIZE);
+  stat->size = file_size(name + ".o");
+  restoreo_stat[1][restoreo_stat[0]] = stat;
+
+  restoreo_stat[0] = (restoreo_stat[0] + 1) % sizeof(restoreo_stat[1]);
+#endif
+
+  return result;
+}
+
+// * HB eines Objektes ein/ausschalten
+int set_object_heart_beat(object ob, int flag)
+{
+  if (objectp(ob))
+    return funcall(bind_lambda(#'efun::configure_object,ob), ob, OC_HEART_BEAT, flag);
+}
+
+// * Magierlevelgruppen ermitteln
+int query_wiz_grp(mixed wiz)
+{
+  int lev;
+
+  lev=query_wiz_level(wiz);
+  if (lev<SEER_LVL) return 0;
+  if (lev>=GOD_LVL) return lev;
+  if (lev>=ARCH_LVL) return ARCH_GRP;
+  if (lev>=ELDER_LVL) return ELDER_GRP;
+  if (lev>=LORD_LVL) return LORD_GRP;
+  if (lev>=SPECIAL_LVL) return SPECIAL_GRP;
+  if (lev>=DOMAINMEMBER_LVL) return DOMAINMEMBER_GRP;
+  if (lev>=WIZARD_LVL) return WIZARD_GRP;
+  if (lev>=LEARNER_LVL) return LEARNER_GRP;
+  return SEER_GRP;
+}
+
+mixed *wizlist_info()
+{
+  if (ARCH_SECURITY || !extern_call())
+            return efun::wizlist_info();
+  return 0;
+}
+
+// * wizlist ausgeben
+varargs void wizlist(string name, int sortkey ) {
+
+  if (!name)
+  {
+    if (this_player())
+      name = getuid(this_player());
+    if (!name)
+      return;
+  }
+
+  // Schluessel darf nur in einem gueltigen Bereich sein
+  if (sortkey<WL_NAME || sortkey>=WL_SIZE) sortkey=WL_COST;
+
+  mixed** wl = efun::wizlist_info();
+  // nach <sortkey> sortieren
+  wl = sort_array(wl, function int (mixed a, mixed b)
+      {return a[sortkey] < b[sortkey]; } );
+
+  // Summe ueber alle Kommandos ermitteln.
+  int total_cmd, i;
+  int pos=-1;
+  foreach(mixed entry : wl)
+  {
+    total_cmd += entry[WL_COMMANDS];
+    if (entry[WL_NAME] == name)
+      pos = i;
+    ++i;
+  }
+
+  if (pos < 0 && name != "ALL" && name != "TOP100")
+    return;
+
+  if (name == "TOP100")
+  {
+    if (sizeof(wl) > 100)
+      wl = wl[0..100];
+    else
+      wl = wl;
+  }
+  // um name herum schneiden
+  else if (name != "ALL")
+  {
+    if (sizeof(wl) <= 21)
+      wl = wl;
+    else if (pos + 10 < sizeof(wl) && pos - 10 > 0)
+      wl = wl[pos-10..pos+10];
+    else if (pos <=21)
+      wl = wl[0..20];
+    else if (pos >= sizeof(wl) - 21)
+      wl = wl[<21..];
+    else
+      wl = wl;
+  }
+
+  write("\nWizard top score list\n\n");
+  if (total_cmd == 0)
+    total_cmd = 1;
+  printf("%-20s %-6s %-3s %-17s %-6s %-6s %-6s\n",
+         "EUID", "cmds", "%", "Costs", "HB", "Arrays","Mapp.");
+  foreach(mixed e: wl)
+  {
+    printf("%-:20s %:6d %:2d%% [%:6dk,%:6dG] %:6d %:6d %:6d\n",
+          e[WL_NAME], e[WL_COMMANDS], e[WL_COMMANDS] * 100 / total_cmd,
+          e[WL_COST] / 1000, e[WL_TOTAL_GIGACOST],
+          e[WL_HEART_BEATS], e[WL_ARRAY_TOTAL], e[WL_MAPPING_TOTAL]
+          );
+  }
+  printf("\nTotal         %7d         (%d)\n\n", total_cmd, sizeof(wl));
+}
+
+
+// Ab hier folgen Hilfsfunktionen fuer call_out() bzw. fuer deren Begrenzung
+
+// ermittelt das Objekt des Callouts.
+private object _call_out_obj( mixed call_out ) {
+    return pointerp(call_out) ? call_out[0] : 0;
+}
+
+private void _same_object( object ob, mapping m ) {
+  // ist nicht so bloed, wie es aussieht, s. nachfolgede Verwendung von m
+  if ( m[ob] )
+    m[ob] += ({ ob });
+  else
+    m[ob] = ({ ob }); 
+}
+
+// alle Objekte im Mapping m zusammenfassen, die den gleichen Loadname (Name der
+// Blueprint) haben, also alle Clones der BP, die Callouts laufen haben.
+// Objekte werden auch mehrfach erfasst, je nach Anzahl ihrer Callouts.
+private void _same_path( object key, object *obs, mapping m ) {
+  string path;
+  if (!objectp(key) || !pointerp(obs)) return;
+
+  path = load_name(key);
+
+  if ( m[path] )
+    m[path] += obs;
+  else
+    m[path] = obs;
+}
+
+// key kann object oder string sein.
+private int _too_many( mixed key, mapping m, int i ) {
+    return sizeof(m[key]) >= i;
+}
+
+// alle Objekte in obs zerstoeren und Fehlermeldung ausgeben. ACHTUNG: Die
+// Objekte werden idR zu einer BP gehoeren, muessen aber nicht! In dem Fall
+// wird auf der Ebene aber nur ein Objekt in der Fehlermeldung erwaehnt.
+private void _destroy( mixed key, object *obs, string text, int uid ) {
+    if (!pointerp(obs)) return;
+    // Array mit unique Eintraege erzeugen.
+    obs = m_indices( mkmapping(obs) );
+    // Fehlermeldung auf der Ebene ausgeben, im catch() mit publish, damit es
+    // auf der Ebene direkt scrollt, der backtrace verfuegbar ist (im
+    // gegensatz zur Loesung mittels Callout), die Ausfuehrung aber weiter
+    // laeuft.
+    catch( efun::raise_error(           
+         sprintf( text,                   
+           uid ? (string)master()->creator_file(key) : key,                   
+           sizeof(obs), object_name(obs[<1]) ) );publish);
+    // Und weg mit dem Kram...
+    filter( obs, #'efun::destruct/*'*/ );
+}
+
+// Alle Objekt einer UID im Mapping m mit UID als KEys zusammenfassen. Objekt
+// sind dabei nicht unique.
+private void _same_uid( string key, object *obs, mapping m, closure cf ) {
+  string uid;
+
+  if ( !pointerp(obs) || !sizeof(obs) )
+    return;
+
+  uid = funcall( cf, key );
+
+  if ( m[uid] )
+    m[uid] += obs; // obs ist nen Array
+  else
+    m[uid] = obs;
+}
+
+nomask varargs void call_out( varargs mixed *args )
+{
+    mixed tmp, *call_outs;
+
+    // Bei >600 Callouts alle Objekte killen, die mehr als 30 Callouts laufen
+    // haben.
+    if ( efun::driver_info(DI_NUM_CALLOUTS) > 600
+        && geteuid(previous_object()) != ROOTID )
+    {
+       // Log erzeugen...
+      
+       // Objekte aller Callouts ermitteln
+       call_outs = map( efun::call_out_info(), #'_call_out_obj );
+       mapping objectmap = ([]);
+       filter( call_outs, #'_same_object, &objectmap );
+       // Master nicht grillen...
+       efun::m_delete( objectmap, master(1) );
+       // alle Objekte raussuchen, die zuviele haben...
+       mapping res = filter_indices( objectmap, #'_too_many, objectmap, 29 );
+       // und ueber alle Keys gehen, an _destroy() werden Key und Array mit
+       // Objekten uebergeben (in diesem Fall sind Keys und Array mit
+       // Objekten jeweils das gleiche Objekt).
+       if ( sizeof(res) )       
+           walk_mapping(res, #'_destroy, "CALL_OUT overflow by single "             
+              "object [%O]. Destructed %d objects. [%s]\n", 0 );
+
+       // Bei (auch nach dem ersten Aufraeumen noch) >800 Callouts alle
+       // Objekte killen, die mehr als 50 Callouts laufen haben - und
+       // diesmal zaehlen Clones nicht eigenstaendig! D.h. es werden alle
+       // Clones einer BP gekillt, die Callouts laufen haben, falls alle
+       // diese Objekte _zusammen_ mehr als 50 Callouts haben!
+       if ( efun::driver_info(DI_NUM_CALLOUTS) > 800 ) {
+           // zerstoerte Objekte von der letzten Aktion sind in objectmap nicht
+           // mehr drin, da sie dort als Keys verwendet wurden.
+           mapping pathmap=([]);
+           // alle Objekt einer BP in res sortieren, BP-Name als Key, Arrays
+           // von Objekten als Werte.
+           walk_mapping( objectmap, #'_same_path, &pathmap);
+           // alle BPs (und ihre Objekte) raussuchen, die zuviele haben...
+           res = filter_indices( pathmap, #'_too_many/*'*/, pathmap, 50 );
+           // und ueber alle Keys gehen, an _destroy() werden die Clones
+           // uebergeben, die Callouts haben.
+           if ( sizeof(res) )
+              walk_mapping( res, #'_destroy/*'*/, "CALL_OUT overflow by file "
+                           "'%s'. Destructed %d objects. [%s]\n", 0 );
+
+           // Wenn beide Aufraeumarbeiten nichts gebracht haben und immer
+           // noch >1000 Callouts laufen, werden diesmal alle Callouts
+           // einer UID zusammengezaehlt.
+           // Alle Objekte einer UID, die es in Summe aller ihrer Objekt mit
+           // Callouts auf mehr als 100 Callouts bringt, werden geroestet.
+           if (efun::driver_info(DI_NUM_CALLOUTS) > 1000)
+           {
+              // das nach BP-Namen vorgefilterte Mapping jetzt nach UIDs
+              // zusammensortieren. Zerstoerte Clones filter _same_uid()
+              // raus.
+              mapping uidmap=([]);
+              walk_mapping( pathmap, #'_same_uid, &uidmap,
+                           symbol_function( "creator_file",
+                                          "/secure/master" ) );
+              // In res nun UIDs als Keys und Arrays von Objekten als Werte.
+              // Die rausfiltern, die mehr als 100 Objekte (non-unique, d.h.
+              // 100 Callouts!) haben.
+              res = filter_indices( uidmap, #'_too_many, uidmap, 100 );
+              // und erneut ueber die Keys laufen und jeweils die Arrays mit
+              // den Objekten zur Zerstoerung an _destroy()...
+              if ( sizeof(res) )
+                  walk_mapping( res, #'_destroy, "CALL_OUT overflow by "
+                              "UID '%s'. Destructed %d objects. [%s]\n",
+                              1 );
+           }
+       }
+    }
+
+    // Falls das aufrufende Objekt zerstoert wurde beim Aufraeumen
+    if ( !previous_object() )
+       return;
+
+    set_this_object( previous_object() );
+    apply( #'efun::call_out, args );
+    return;
+}
+
+mixed call_out_info() {
+  
+  object po = previous_object();
+  mixed coi = efun::call_out_info();
+
+  // ungefilterten Output nur fuer bestimmte Objekte, Objekte in /std oder
+  // /obj haben die BackboneID.
+  if (query_wiz_level(getuid(po)) >= ARCH_LVL
+       || (string)master()->creator_file(load_name(po)) == BACKBONEID ) {
+      return coi;
+  }
+  else {
+      return filter(coi, function mixed (mixed arr) {
+              if (pointerp(arr) && arr[0]==po)
+                 return 1;
+              else return 0; });
+  }
+}
+
+// * Zu einer closure das Objekt, an das sie gebunden ist, suchen
+mixed query_closure_object(closure c) {
+  return
+    CLOSURE_IS_UNBOUND_LAMBDA(get_type_info(c, 1)) ?
+      0 :
+  (to_object(c) || -1);
+}
+
+// * Wir wollen nur EIN Argument ... ausserdem checks fuer den Netztotenraum
+varargs void move_object(mixed what, mixed where)
+{
+  object po,tmp;
+
+  po=previous_object();
+  if (!where)
+  {
+    where=what;
+    what=po;
+  }
+  if (((stringp(where) && where==NETDEAD_ROOM ) ||
+       (objectp(where) && where==find_object(NETDEAD_ROOM))) &&
+       objectp(what) && object_name(what)!="/obj/sperrer")
+  {
+    if (!query_once_interactive(what))
+    {
+      what->remove();
+      if (what) destruct(what);
+      return;
+    }
+    if (living(what) || interactive(what))
+    {
+      log_file("NDEAD2",sprintf("TRYED TO MOVE TO NETDEAD: %O\n",what));
+      return;
+    }
+    set_object_heart_beat(what,0);
+  }
+  tmp=what;
+  while (tmp=environment(tmp))
+      // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+      // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+      // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+      // da um jedes bisschen Rechenzeit geht.
+      // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+      //
+      // Tiamak
+    tmp->_set_last_content_change();
+  funcall(bind_lambda(#'efun::move_object,po),what,where);
+  if (tmp=what)
+    while (tmp=environment(tmp))
+      tmp->_set_last_content_change();
+}
+
+
+void start_simul_efun() {
+  mixed *info;
+
+  // Falls noch nicht getan, extra_wizinfo initialisieren
+  if ( !pointerp(info = get_extra_wizinfo(0)) )
+    set_extra_wizinfo(0, info = allocate(BACKBONE_WIZINFO_SIZE));
+
+  InitLivingData(info);
+
+  set_next_reset(10); // direkt mal aufraeumen
+}
+
+protected void reset() {
+  set_next_reset(7200);
+  CleanLivingData();
+}
+
+#if !__EFUN_DEFINED__(absolute_hb_count)
+int absolute_hb_count() {
+  return efun::driver_info(DI_NUM_HEARTBEAT_TOTAL_CYCLES);
+}
+#endif
+
+void __set_environment(object ob, mixed target)
+{
+  string path;
+  object obj;
+
+  if (!objectp(ob))
+    return;
+  if (!IS_ARCH(geteuid(previous_object())) || !ARCH_SECURITY )
+    return;
+  if (objectp(target))
+  {
+    efun::set_environment(ob,target);
+    return;
+  }
+  path=(string)MASTER->_get_path(target,this_interactive());
+  if (stringp(path) && file_size(path+".c")>=0 &&
+      !catch(load_object(path);publish) )
+  {
+    obj=find_object(path);
+    efun::set_environment(ob,obj);
+    return;
+  }
+}
+
+void _dump_wizlist(string file, int sortby) {
+  int i;
+  mixed *a;
+
+  if (!LORD_SECURITY)
+    return;
+  if (!master()->valid_write(file,geteuid(previous_object()),"write_file"))
+  {
+    write("NO WRITE PERMISSION\n");
+    return;
+  }
+  a = wizlist_info();
+  a = sort_array(a, lambda( ({'a,'b}),
+                        ({#'<,
+                          ({#'[,'a,sortby}),
+                          ({#'[,'b,sortby})
+                         })));
+  rm(file);
+  for (i=sizeof(a)-1;i>=0;i--)
+    write_file(file,sprintf("%-11s: eval=%-8d cmds=%-6d HBs=%-5d array=%-5d mapping=%-7d\n",
+      a[i][WL_NAME],a[i][WL_TOTAL_COST],a[i][WL_COMMANDS],a[i][WL_HEART_BEATS],
+      a[i][WL_ARRAY_TOTAL],a[i][WL_CALL_OUT]));
+}
+
+public varargs object deep_present(mixed what, object ob) {
+
+  if(!objectp(ob))
+    ob=previous_object();
+  // Wenn ein Objekt gesucht wird: Alle Envs dieses Objekts ermitteln und
+  // schauen, ob in diesen ob vorkommt. Dann ist what in ob enthalten.
+  if(objectp(what)) {
+    object *envs=all_environment(what);
+    // wenn ob kein Environment hat, ist es offensichtlich nicht in what
+    // enthalten.
+    if (!pointerp(envs)) return 0;
+    if (member(envs, ob) != -1) return what;
+  }
+  // sonst wirds teurer, ueber alle Objekte im (deep) Inv laufen und per id()
+  // testen. Dabei muss aber die gewuenschte Nr. ("flasche 3") abgeschnitten
+  // werden und selber gezaehlt werden, welche das entsprechende Objekt ist.
+  else if (stringp(what)) {
+      int cnt;
+      string newwhat;
+      if(sscanf(what,"%s %d",newwhat,cnt)!=2)
+       cnt=1;
+      else
+       what=newwhat;
+      foreach(object invob: deep_inventory(ob)) {
+       if (invob->id(what) && !--cnt)
+           return invob;
+      }
+  }
+  else {
+    set_this_object(previous_object());
+    raise_error(sprintf("Wrong argument 1 to deep_present(). "
+         "Expected \"object\" or \"string\", got %.50O.\n",
+         what));
+  }
+  return 0;
+}
+
+mapping dump_ip_mapping()
+{
+  return 0;
+}
+
+nomask void swap(object obj)
+{
+  write("Your are not allowed to swap objects by hand!\n");
+  return;
+}
+
+nomask varargs void garbage_collection(string str)
+{
+  if(previous_object()==0 || !IS_ARCH(geteuid(previous_object())) 
+      || !ARCH_SECURITY)
+  {
+    write("Call GC now and the mud will crash in 5-6 hours. DONT DO IT!\n");
+    return;
+  }
+  else if (stringp(str))
+  {
+    return efun::garbage_collection(str);
+  }
+  else 
+    return efun::garbage_collection();
+}
+
+varargs void notify_fail(mixed nf, int prio) {
+  object po,oldo;
+  int oldprio;
+  
+  if (!PL || !objectp(po=previous_object())) return;
+  if (!stringp(nf) && !closurep(nf)) {
+      set_this_object(po);
+      raise_error(sprintf(
+         "Only strings and closures allowed for notify_fail! "
+         "Argument was: %.50O...\n",nf));
+  }
+
+  // falls ein Objekt bereits nen notify_fail() setzte, Prioritaeten abschaetzen
+  // und vergleichen.
+  if (objectp(oldo=query_notify_fail(1)) && po!=oldo) {
+    if (!prio) {       
+      //Prioritaet dieses notify_fail() 'abschaetzen'
+      if (po==PL) // Spieler-interne (soul-cmds)
+        prio=NF_NL_OWN;
+      else if (living(po))
+        prio=NF_NL_LIVING;
+      else if ((int)po->IsRoom())
+        prio=NF_NL_ROOM;
+      else
+        prio=NF_NL_THING;
+    }
+    //Prioritaet des alten Setzers abschaetzen
+    if (oldo==PL)
+      oldprio=NF_NL_OWN;
+    else if (living(oldo))
+      oldprio=NF_NL_LIVING;
+    else if ((int)oldo->IsRoom())
+      oldprio=NF_NL_ROOM;
+    else
+      oldprio=NF_NL_THING;
+  }
+  else // wenn es noch kein Notify_fail gibt:
+    oldprio=NF_NL_NONE;
+
+  //vergleichen und ggf. setzen
+  if (prio >= oldprio) { 
+    set_this_object(po);
+    efun::notify_fail(nf);
+  }
+
+  return;
+}
+
+void _notify_fail(string str)
+{
+  //query_notify_fail() benutzen, um das Objekt
+  //des letzten notify_fail() zu ermitteln
+  object o;
+  if ((o=query_notify_fail(1)) && o!=previous_object())
+    return;
+  //noch kein notify_fail() fuer dieses Kommando gesetzt, auf gehts.
+  set_this_object(previous_object());
+  efun::notify_fail(str);
+  return;
+}
+
+string time2string( string format, int zeit )
+{
+  int i,ch,maxunit,dummy;
+  string *parts, fmt;
+
+  int secs = zeit;
+  int mins = (zeit/60);
+  int hours = (zeit/3600);
+  int days = (zeit/86400);
+  int weeks =  (zeit/604800);
+  int months = (zeit/2419200);
+  int abbr = 0;
+
+  parts = regexplode( format, "\(%\(-|\)[0-9]*[nwdhmsxNWDHMSX]\)|\(%%\)" );
+
+  for( i=1; i<sizeof(parts); i+=2 )
+  {
+    ch = parts[i][<1];
+    switch( parts[i][<1] )
+    {
+    case 'x': case 'X':
+       abbr = sscanf( parts[i], "%%%d", dummy ) && dummy==0;
+       // NO break !
+    case 'n': case 'N':
+       maxunit |= 31;
+       break;
+    case 'w': case 'W':
+       maxunit |= 15;
+       break;
+    case 'd': case 'D':
+       maxunit |= 7;
+       break;
+    case 'h': case 'H':
+       maxunit |= 3;
+       break;
+    case 'm': case 'M':
+       maxunit |= 1;
+       break;
+    }
+  }
+  if( maxunit & 16 ) weeks %= 4;
+  if( maxunit & 8 ) days %= 7;
+  if( maxunit & 4 ) hours %= 24;
+  if( maxunit & 2 ) mins %= 60;
+  if( maxunit ) secs %= 60;
+
+  for( i=1; i<sizeof(parts); i+=2 )
+  {
+    fmt = parts[i][0..<2];
+    ch = parts[i][<1];
+    if( ch=='x' )
+    {
+      if (months > 0) ch='n';
+      else if( weeks>0 ) ch='w';
+      else if( days>0 ) ch='d';
+      else if( hours>0 ) ch='h'; 
+      else if(mins > 0) ch = 'm';
+      else ch = 's';
+    }
+    else if( ch=='X' )
+    {
+      if (months > 0) ch='N';
+      else if( weeks>0 ) ch='W';
+      else if( days>0 ) ch='D';
+      else if( hours>0 ) ch='H'; 
+      else if(mins > 0) ch = 'M';
+      else ch = 'S';
+    }
+    
+    switch( ch )
+    {
+      case 'n': parts[i] = sprintf( fmt+"d", months ); break;
+      case 'w': parts[i] = sprintf( fmt+"d", weeks ); break;
+      case 'd': parts[i] = sprintf( fmt+"d", days ); break;
+      case 'h': parts[i] = sprintf( fmt+"d", hours ); break;
+      case 'm': parts[i] = sprintf( fmt+"d", mins ); break;
+      case 's': parts[i] = sprintf( fmt+"d", secs ); break;
+      case 'N':
+       if(abbr) parts[i] = "M";
+       else parts[i] = sprintf( fmt+"s", (months==1) ? "Monat" : "Monate" );
+       break;
+      case 'W':
+       if(abbr) parts[i] = "w"; else
+       parts[i] = sprintf( fmt+"s", (weeks==1) ? "Woche" : "Wochen" );
+       break;
+      case 'D':
+       if(abbr) parts[i] = "d"; else
+       parts[i] = sprintf( fmt+"s", (days==1) ? "Tag" : "Tage" );
+       break;
+      case 'H':
+       if(abbr) parts[i] = "h"; else
+       parts[i] = sprintf( fmt+"s", (hours==1) ? "Stunde" : "Stunden" );
+       break;
+      case 'M':
+       if(abbr) parts[i] = "m"; else
+       parts[i] = sprintf( fmt+"s", (mins==1) ? "Minute" : "Minuten" );
+       break;
+      case 'S':
+       if(abbr) parts[i] = "s"; else
+       parts[i] = sprintf( fmt+"s", (secs==1) ? "Sekunde" : "Sekunden" );
+       break;
+      case '%':
+       parts[i] = "%";
+       break;
+      }
+    }
+    return implode( parts, "" );
+}
+
+nomask mixed __create_player_dummy(string name)
+{
+  string err;
+  object ob;
+  mixed m;
+  //hat nen Scherzkeks die Blueprint bewegt?
+  if ((ob=find_object("/secure/login")) && environment(ob))
+      catch(destruct(ob);publish);
+  err = catch(ob = clone_object("secure/login");publish);
+  if (err)
+  {
+    write("Fehler beim Laden von /secure/login.c\n"+err+"\n");
+    return 0;
+  }
+  if (objectp(m=(mixed)ob->new_logon(name))) netdead[name]=m;
+  return m;
+}
+
+nomask int secure_level()
+{
+  int *level;
+  //kette der Caller durchlaufen, den niedrigsten Level in der Kette
+  //zurueckgeben. Zerstoerte Objekte (Selbstzerstoerer) fuehren zur Rueckgabe
+  //von 0.
+  //caller_stack(1) fuegt dem Rueckgabearray this_interactive() hinzu bzw. 0,
+  //wenn es keinen Interactive gibt. Die 0 fuehrt dann wie bei zerstoerten
+  //Objekten zur Rueckgabe von 0, was gewuenscht ist, da es hier einen
+  //INteractive geben muss.
+  level=map(caller_stack(1),function int (object caller)
+      {if (objectp(caller))
+       return(query_wiz_level(geteuid(caller)));
+       return(0); // kein Objekt da, 0.
+      } );
+  return(min(level)); //den kleinsten Wert im Array zurueckgeben (ggf. 0)
+}
+
+nomask string secure_euid()
+{
+  string euid;
+
+  if (!this_interactive()) // Es muss einen interactive geben
+     return 0;
+  euid=geteuid(this_interactive());
+  // ueber alle Caller iterieren. Wenn eines davon eine andere euid hat als
+  // der Interactive und diese nicht die ROOTID ist, wird 0 zurueckgeben.
+  // Ebenso, falls ein Selbstzerstoerer irgendwo in der Kette ist.
+  foreach(object caller: caller_stack()) {
+      if (!objectp(caller) ||
+       (geteuid(caller)!=euid && geteuid(caller)!=ROOTID))
+         return 0;
+  }
+  return euid; // 'sichere' euid zurueckgeben
+}
+
+// INPUT_PROMPT und nen Leerprompt hinzufuegen, wenn keins uebergeben wird.
+// Das soll dazu dienen, dass alle ggf. ein EOR am Promptende kriegen...
+//#if __BOOT_TIME__ < 1360017213
+varargs void input_to( mixed fun, int flags, varargs mixed *args )
+{
+    mixed *arr;
+    int i;
+
+    if ( !this_player() || !previous_object() )
+       return;
+
+    // TODO: input_to(...,INPUT_PROMPT, "", ...), wenn kein INPUT_PROMPT
+    // vorkommt...
+    if ( flags&INPUT_PROMPT ) {    
+        arr = ({ fun, flags }) + args;
+    }
+    else {
+        // ggf. ein INPUT_PROMPT hinzufuegen und nen Leerstring als Prompt.
+        flags |= INPUT_PROMPT;
+        arr = ({ fun, flags, "" }) + args;
+    }
+
+    // Arrays gegen flatten quoten.
+    for ( i = sizeof(arr) - 1; i > 1; i-- )
+       if ( pointerp(arr[i]) )
+           arr[i] = quote(arr[i]);
+
+    apply( bind_lambda( unbound_lambda( ({}),
+                                     ({ #'efun::input_to/*'*/ }) + arr ),
+                       previous_object() ) );
+}
+//#endif
+
+nomask int set_light(int i)
+// erhoeht das Lichtlevel eines Objekts um i
+// result: das Lichtlevel innerhalb des Objekts
+{
+    object ob, *inv;
+    int lall, light, dark, tmp;
+
+    if (!(ob=previous_object())) return 0; // ohne das gehts nicht.
+
+    // aus kompatibilitaetsgruenden kann man auch den Lichtlevel noch setzen
+    if (i!=0) ob->SetProp(P_LIGHT, ob->QueryProp(P_LIGHT)+i);
+
+    // Lichtberechnung findet eigentlich in der Mudlib statt.
+    return (int)ob->QueryProp(P_INT_LIGHT);
+}
+
+
+public string iso2ascii( string str )
+{
+    if ( !stringp(str) || !sizeof(str) )
+       return "";
+
+    str = regreplace( str, "ä", "ae", 1 );
+    str = regreplace( str, "ö", "oe", 1 );
+    str = regreplace( str, "ü", "ue", 1 );
+    str = regreplace( str, "Ä", "Ae", 1 );
+    str = regreplace( str, "Ö", "Oe", 1 );
+    str = regreplace( str, "Ü", "Ue", 1 );
+    str = regreplace( str, "ß", "ss", 1 );
+    str = regreplace( str, "[^ -~]", "?", 1 );
+
+    return str;
+}
+
+
+public varargs string CountUp( string *s, string sep, string lastsep )
+{
+    string ret;
+
+    if ( !pointerp(s) )
+       return "";
+    
+    if (!sep) sep = ", ";
+    if (!lastsep) lastsep = " und ";
+
+    switch (sizeof(s))  {
+       case 0: ret=""; break;
+       case 1: ret=s[0]; break;
+       default:
+              ret = implode(s[0..<2], sep);
+              ret += lastsep + s[<1];
+    }
+    return ret;
+}
+
+nomask varargs int query_next_reset(object ob) {
+
+    // Typpruefung: etwas anderes als Objekte oder 0 sollen Fehler sein.
+    if (ob && !objectp(ob))
+      raise_error(sprintf("Bad arg 1 to query_next_reset(): got %.20O, "
+           "expected object.\n",ob));
+
+    // Defaultobjekt PO, wenn 0 uebergeben.
+    if ( !objectp(ob) )
+      ob = previous_object();
+
+    return efun::object_info(ob, OI_NEXT_RESET_TIME);
+}
+
+
+#if !__EFUN_DEFINED__(copy_file)
+#define MAXLEN 50000
+nomask int copy_file(string source, string dest)
+{
+
+  int ptr;
+  string bytes;
+
+  set_this_object(previous_object());
+  if (!sizeof(source)||!sizeof(dest)||source==dest||(file_size(source)==-1)||
+      (!call_other(master(),"valid_read",source,
+                   getuid(this_interactive()||
+                 previous_object()),"read_file",previous_object()))||
+      (!call_other(master(),"valid_read",source,
+                   getuid(this_interactive()||
+                 previous_object()),"write_file",previous_object())))
+    return 1;
+  switch (file_size(dest))
+  {
+  case -1:
+    break;
+  case -2:
+    if (dest[<1]!='/') dest+="/";
+    dest+=efun::explode(source,"/")[<1];
+    if (file_size(dest)==-2) return 1;
+    if (file_size(dest)!=-1) break;
+  default:
+    if (!rm(dest)) return 1;
+    break;
+  }
+  do
+  {
+    bytes = read_bytes(source, ptr, MAXLEN); ptr += MAXLEN;
+    if (!bytes) bytes="";
+    write_file(dest, bytes);
+  }
+  while(sizeof(bytes) == MAXLEN);
+  return 0;
+}
+#endif //!__EFUN_DEFINED__(copy_file)
+
+
+// ### Ersatzaufloesung in Strings ###
+varargs string replace_personal(string str, mixed *obs, int caps) {
+  int i;
+  string *parts;
+
+  parts = regexplode(str, "@WE[A-SU]*[0-9]");
+  i = sizeof(parts);
+
+  if (i>1) {
+    int j, t;
+    closure *name_cls;
+
+    t = j = sizeof(obs);
+
+    name_cls  =  allocate(j);
+    while (j--)
+      if (objectp(obs[j]))
+        name_cls[j] = symbol_function("name", obs[j]);
+      else if (stringp(obs[j]))
+        name_cls[j] = obs[j];
+
+    while ((i-= 2)>0) {
+      int ob_nr;
+      // zu ersetzendes Token in Fall und Objektindex aufspalten
+      ob_nr = parts[i][<1]-'1';
+      if (ob_nr<0 || ob_nr>=t) {
+        set_this_object(previous_object());
+        raise_error(sprintf("replace_personal: using wrong object index %d\n",
+                    ob_nr));
+        return implode(parts, "");
+      }
+
+      // casus kann man schon hier entscheiden
+      int casus;
+      string part = parts[i];
+      switch (part[3]) {
+        case 'R': casus = WER;    break;
+        case 'S': casus = WESSEN; break;
+        case 'M': casus = WEM;    break;
+        case 'N': casus = WEN;    break;
+        default:  continue; // passt schon jetzt nicht in das Hauptmuster
+      }
+
+      // und jetzt die einzelnen Keywords ohne fuehrendes "@WE", beendende Id
+      mixed tmp;
+      switch (part[3..<2]) {
+        case "R": case "SSEN": case "M": case "N":               // Name
+          parts[i] = funcall(name_cls[ob_nr], casus, 1);  break;
+        case "RU": case "SSENU": case "MU": case "NU":           // unbestimmt
+          parts[i] = funcall(name_cls[ob_nr], casus);     break;
+        case "RQP": case "SSENQP": case "MQP": case "NQP":       // Pronoun
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPronoun(casus);
+          break;
+        case "RQA": case "SSENQA": case "MQA": case "NQA":       // Article
+          if (objectp(tmp = obs[ob_nr]))
+            tmp = (string)tmp->QueryArticle(casus, 1, 1);
+          if (stringp(tmp) && !(tmp[<1]^' ')) 
+            tmp = tmp[0..<2];                // Extra-Space wieder loeschen
+          break;
+        case "RQPPMS": case "SSENQPPMS": case "MQPPMS": case "NQPPMS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, SINGULAR);
+          break;
+        case "RQPPFS": case "SSENQPPFS": case "MQPPFS": case "NQPPFS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, SINGULAR);
+          break;
+        case "RQPPNS": case "SSENQPPNS": case "MQPPNS": case "NQPPNS":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, SINGULAR);
+          break;
+        case "RQPPMP": case "SSENQPPMP": case "MQPPMP": case "NQPPMP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(MALE, casus, PLURAL);
+          break;
+        case "RQPPFP": case "SSENQPPFP": case "MQPPFP": case "NQPPFP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(FEMALE, casus, PLURAL);
+          break;
+        case "RQPPNP": case "SSENQPPNP": case "MQPPNP": case "NQPPNP":
+          if (objectp(tmp = obs[ob_nr]))
+            parts[i] = (string)tmp->QueryPossPronoun(NEUTER, casus, PLURAL);
+          break;
+        default:
+          continue;
+      }
+      
+      // wenn tmp ein String war, weisen wir es hier pauschal zu
+      if (stringp(tmp))
+        parts[i] = tmp;
+
+      // auf Wunsch wird nach Satzenden gross geschrieben
+      if (caps)
+        switch (parts[i-1][<2..]) {
+          case ". ":  case "! ":  case "? ":
+          case ".":   case "!":   case "?":
+          case ".\n": case "!\n": case "?\n":
+          case "\" ": case "\"\n":
+            parts[i] = capitalize(parts[i]);
+            break;
+        }
+    }
+    return implode(parts, "");
+  }
+  return str;
+}
+
+
+//replacements for dropped efuns in LD
+#if !__EFUN_DEFINED__(extract)
+deprecated varargs string extract(string str, int from, int to) {
+
+  if(!stringp(str)) {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to extract(): %O",str));
+  }
+  if (intp(from) && intp(to)) {
+    if (from>=0 && to>=0)
+      return(str[from .. to]);
+    else if (from>=0 && to<0)
+      return(str[from .. <abs(to)]);
+    else if (from<0 && to>=0)
+      return(str[<abs(from) .. to]);
+    else
+      return(str[<abs(from) .. <abs(to)]);
+  }
+  else if (intp(from)) {
+    if (from>=0)
+      return(str[from .. ]);
+    else
+      return(str[<abs(from) .. ]);
+  }
+  else {
+    return(str);
+  }
+}
+#endif // !__EFUN_DEFINED__(extract)
+
+#if !__EFUN_DEFINED__(slice_array)
+deprecated varargs mixed slice_array(mixed array, int from, int to) {
+
+  if(!pointerp(array)) {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to slice_array(): %O",array));
+  }
+  if (intp(from) && intp(to)) {
+    if (from>=0 && to>=0)
+      return(array[from .. to]);
+    else if (from>=0 && to<0)
+      return(array[from .. <abs(to)]);
+    else if (from<0 && to>=0)
+      return(array[<abs(from) .. to]);
+    else
+      return(array[<abs(from) .. <abs(to)]);
+  }
+  else if (intp(from)) {
+    if (from>=0)
+      return(array[from .. ]);
+    else
+      return(array[<abs(from) .. ]);
+  }
+  else {
+    return(array);
+  }
+}
+#endif // !__EFUN_DEFINED__(slice_array)
+
+#if !__EFUN_DEFINED__(member_array)
+deprecated int member_array(mixed item, mixed arraystring) {
+
+  if (pointerp(arraystring)) {
+    return(efun::member(arraystring,item));
+  }
+  else if (stringp(arraystring)) {
+    return(efun::member(arraystring,to_int(item)));
+  }
+  else {
+    set_this_object(previous_object());
+    raise_error(sprintf("Bad argument 1 to member_array(): %O",arraystring));
+  }
+}
+#endif // !__EFUN_DEFINED__(member_array)
+
+// The digit at the i'th position is the number of bits set in 'i'.
+string count_table =
+    "0112122312232334122323342334344512232334233434452334344534454556";
+int broken_count_bits( string s ) {
+    int i, res;
+    if( !stringp(s) || !(i=sizeof(s)) ) return 0;
+    for( ; i-->0; ) {
+        // We are counting 6 bits at a time using a precompiled table.
+        res += count_table[(s[i]-' ')&63]-'0';
+    }
+    return res;
+}
+
+#if !__EFUN_DEFINED__(count_bits)
+int count_bits( string s ) {
+    return(broken_count_bits(s));
+}
+#endif
+
+
+// * Teile aus einem Array entfernen *** OBSOLETE
+deprecated mixed *exclude_array(mixed *arr,int from,int to)
+{
+  if (to<from)
+    to = from;
+  return arr[0..from-1]+arr[to+1..];
+}
+
diff --git a/secure/sinmaster.c b/secure/sinmaster.c
new file mode 100644
index 0000000..14b8f94
--- /dev/null
+++ b/secure/sinmaster.c
@@ -0,0 +1,213 @@
+// MorgenGrauen MUDlib
+//
+// secure/sinmaster.c -- Das Strafregister
+//
+// $Id: sinmaster.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+#define SIN_SAVE "/secure/ARCH/sins"
+#define SIN_LOG  "ARCH/sins"
+#define SIN_DUMP "/secure/ARCH/sins.dump"
+
+#include <defines.h>
+#include <properties.h>
+#include <wizlevels.h>
+
+#pragma strict_types
+
+mapping sins;
+
+public void create()
+{
+    seteuid(getuid(ME));
+    if ( !restore_object(SIN_SAVE) )
+        sins = ([]);
+}
+
+static void save_me()
+{
+    save_object(SIN_SAVE);
+}
+
+private varargs int is_allowed(int archonly)
+{
+  if (previous_object() && geteuid(previous_object())==ROOTID)
+    return 1;
+  if (!process_call() && previous_object() && this_interactive() && (ARCH_SECURITY || (!archonly && IS_DEPUTY(secure_euid())) ) )
+    return 1;
+  return 0;
+}
+
+public nomask int query_prevent_shadow()
+{
+    return 1;
+}
+
+public string ListSinners()
+{   string *names;
+
+    if ( !is_allowed() )
+      return "ACCESS DENIED\n";
+
+    if ( sizeof(names=m_indices(sins))<1 )
+      return "Es sind keine Eintraege vorhanden.\n";
+
+    names = sort_array( map( names, #'capitalize ), #'> );
+
+    return sprintf("Liste der eingetragenen Suender:\n%#78.6s\n",
+                   implode(names,"\n"));
+}
+
+public string ListSins(string who)
+{   string re;
+    int    i,j;
+
+    if ( !is_allowed() )
+      return "ACCESS DENIED\n";
+
+    if ( !stringp(who) || (sizeof(who)<1) )
+      return "SYNTAX ERROR.\n";
+
+    if ( !member(sins,who) || !pointerp(sins[who]) || ((j=sizeof(sins[who]))<1) )
+      return sprintf("Es liegen keine Eintraege fuer '%s' vor.\n",CAP(who));
+
+    for ( i=1, re = ((string)sins[who][0]+"\n") ; i<j ; i++ )
+      re += sprintf("%3d: %s\n",i,(string)sins[who][i]);
+
+    return re;
+}
+
+static void _add_entry(string who, string entry, object pl)
+{   string *add;
+
+    if ( member(sins,who) && pointerp(sins[who]) && (sizeof(sins[who])>0) )
+        add = (string*)sins[who];
+    else
+        add = ({ sprintf("Eintraege fuer '%s':",CAP(who)) });
+
+    add += ({ entry });
+
+    sins[who] = add;
+
+    save_me();
+
+    log_file(SIN_LOG,
+        sprintf("%s Eintrag fuer %s von %s\n",
+            dtime(time()),CAP(who),CAP(getuid(pl)) ) );
+}
+
+public string AddSin(string who, string text)
+{   object pl;
+    string ersti;
+
+    if ( !is_allowed() )
+      return "ACCESS DENIED\n";
+
+    if ( !stringp(who) || (sizeof(who)<1)
+        || !stringp(text) || (sizeof(text)<1) )
+      return "SYNTAX ERROR.\n";
+
+    if ( text[0..2]=="-f " )
+      text=text[3..];
+    else if ( file_size(sprintf("/save/%s/%s.o",who[0..0],who))<1)
+      return sprintf("Es gibt keinen Spieler namens '%s'\n",who);
+
+    text = dtime(time()) + " ("+CAP(getuid(RPL))+")\n" 
+         + break_string(text,78,"     " );
+
+    _add_entry( who, text, RPL );
+
+    if ( objectp(pl=(find_player(who)||find_netdead(who)))
+        && !IS_WIZARD(pl) // Magier haben manchmal komische Ersties ...
+        && stringp(ersti=(string)pl->QueryProp(P_SECOND)) )
+    {
+        return ( sprintf("Ok.\nFuege Eintrag bei Ersti '%s' hinzu.\n",ersti)
+                + AddSin( lower_case(ersti), ("-f siehe "+who) ) );
+    }
+
+    return "OK.\n";
+}
+
+public string RemoveSin(string who, int nr)
+{   string *rem;
+
+    if ( !is_allowed(1) )
+      return "ACCESS DENIED\n";
+
+    if ( !intp(nr) || (nr<1) || !stringp(who) || (sizeof(who)<1) )
+      return "SYNTAX ERROR.\n";
+
+    if ( !member(sins,who) || !pointerp(sins[who]) || (sizeof(sins[who])<1) )
+      return sprintf("FEHLER: Keine Eintraege fuer '%s' vorhanden.\n",
+                     CAP(who));
+
+    rem = (string*)sins[who];
+
+    if ( sizeof(rem)<=nr )
+      return "FEHLER: Diesen Eintrag gibt es nicht.\n";
+
+    rem[nr] = 0;
+
+    rem -= ({ 0 });
+
+    log_file(SIN_LOG,
+        sprintf("%s Loeschung bei %s von %s\n",
+            dtime(time()),CAP(who),CAP(getuid(RPL)) ) );
+
+
+    if ( sizeof(rem)<2 )
+    {
+        m_delete(sins,who);
+        save_me();
+        return sprintf("Letzten Eintrag von '%s' geloescht.\n",CAP(who));
+    }
+
+    sins[who] = rem;
+    save_me();
+
+    return sprintf("Eintrag %d von '%s' geloescht.\n",nr,CAP(who));
+}
+
+public varargs string Dump(int flag)
+{   string *snr,*sns,dump;
+    int    i,j,k,s,t;
+
+    if ( !is_allowed(0) )
+      return "ACCESS DENIED\n";
+
+    if ( !flag && file_size(SIN_DUMP)>1 )
+      rm( SIN_DUMP );
+
+    s=i=sizeof(snr=sort_array(m_indices(sins),#'<));
+
+    if ( i<1 )
+      return "Keine Suender da.\n";
+
+    dump = sprintf("\n%|78s\n%'='78.78s\n\n",
+               sprintf("Dump der Suenden-Eintraege (%s):",dtime(time())),
+               "");
+
+    for ( --i,t=0 ; i>=0 ; i-- )
+    {
+        j = sizeof(sns=sins[snr[i]]);
+        t += (j-1);
+        dump += (sns[0]+"\n\n");
+
+        for ( k=1 ; k<j ; k++ )
+            dump += sprintf("%3d: %s\n",k,sns[k]);
+
+        dump += sprintf("%'='78.78s\n\n","");
+    }
+
+    dump += sprintf("Statistik:\n\n"+
+                    "  Es sind %d Suender mit insges. %d Eintraegen vorhanden.\n"+
+                    "  Das macht einen Schnitt von %.6f Eintraegen.\n\n",
+                    s,t,( to_float(t)/to_float(s) ));
+
+    if ( !flag )
+    {
+        write_file(SIN_DUMP,dump);
+        return sprintf("Es wurden %d Suender gedumped.\n",s);
+        
+    }
+    return dump;
+}
diff --git a/secure/syntaxdb.c b/secure/syntaxdb.c
new file mode 100644
index 0000000..572eae6
--- /dev/null
+++ b/secure/syntaxdb.c
@@ -0,0 +1,223 @@
+// MorgenGrauen MUDlib
+/** \file /file.c
+* merkt sich Syntaxen und deren Erfolg
+* Kommt gar nicht mit geschachtelten Befehlen klar...
+* \author Zesstra
+*/
+
+#pragma strong_types,save_types,rtt_checks
+#pragma no_clone,no_inherit,no_shadow
+#pragma pedantic, range_check
+
+#include <defines.h>
+#include <events.h>
+#include <wizlevels.h>
+#include <player/base.h>
+#include <userinfo.h>
+#include <driver_info.h>
+#include <regexp.h>
+
+#define HOME(x) (__PATH__(0)+x)
+#define ZDEBUG(x) tell_room("/players/zesstra/workroom",\
+                    sprintf("syntax: %O\n",x));
+
+mapping blacklist;
+
+struct cmd_s {
+    string verb;
+    string cmd;
+    string uid;
+    int success;
+    int fp;
+    int evalno;
+    string tp_uid;
+    int finished;
+};
+
+// LIste der letzten Kommandos, Queue zum speichern in die DB.
+// Das letzte Element ist das letzte Kommando (ggf. noch nicht abgeschlossen)
+private struct cmd_s *commands = ({});
+
+protected void create()
+{
+  seteuid(getuid());
+  if (sl_open(HOME("ARCH/syntaxen.sqlite")) != 1)
+  {
+    raise_error("Datenbank konnte nicht geoeffnet werden.\n");
+  }
+  // Tabellen und Indices anlegen, falls die nicht existieren.
+  sl_exec("CREATE TABLE IF NOT EXISTS syntaxen("
+          "cmd TEXT UNIQUE, "
+          "verb TEXT NOT NULL, envuid TEXT NOT NULL, "
+          "success INTEGER, count INTEGER, fp INTEGER);");
+  sl_exec("CREATE INDEX IF NOT EXISTS idx_verb ON syntaxen(verb);");
+  // dieses Objekt darf nur per hand (mit this_player()) geladen werden, weil
+  // da ein Teil der Blacklist herkommt... Man beachte, dass dies auch das
+  // ist, was die ueblichen Kommunikationsbefehle ausfiltert.
+  string *tmp = map(this_player()->QueryProp(P_LOCALCMDS),
+                    function string (mixed val)
+                    {return val[0];}
+                   );
+  blacklist = mkmapping(tmp
+                  - ({ "toete","schnupper", "schnuppere", "suche","such",
+                       "unt", "untersuche","untersuch", "riech",
+                       "rieche", "lausch", "lausche", "taste",
+                       "fuehl", "fuehle, beruehre", "schau", "schaue",
+                       "les", "lies", "betrachte", "betr", "betracht", 
+                       "teile" // wird per regexp gefiltert!
+                     })
+                  + ({"mail","frage","frag", "xload", "xeval", "xcall",
+                      "gruppe", "g", "team", "frufe", "fruf", "fwer",
+                      "femote", "rknuddel", "knuddel", "nick", "nicke",
+                      "rnick", "rnicke", "knuddel", "knuddele",
+                      "rknuddel", "rknuddele","denke","denk",
+                      "ffix","fnotiz","fuebertrage", "chaoskontrolle",
+                      "kreis", // Matrixkristall
+                     })
+      );
+}
+
+private void write_db()
+{
+  foreach(struct cmd_s c : commands)
+  {
+    int** row=sl_exec("SELECT rowid, success, count, fp from syntaxen "
+                      "WHERE cmd=?1;",
+                      c->cmd);
+    if (row)
+    {
+      sl_exec("UPDATE syntaxen SET success=?2, count=?3, fp=?4"
+              "WHERE rowid=?1;", row[0][0],
+              c->success || row[0][1],
+              ++row[0][2], c->fp || row[0][3] );
+    }
+    else
+    {
+      sl_exec("INSERT INTO syntaxen(verb, cmd, envuid, success, count, fp) "
+              "VALUES(?1,?2,?3,?4,?5,?6);",
+              c->verb, c->cmd, c->uid, c->success, 1, c->fp);
+    }
+  }
+  commands = ({});
+}
+
+// Beendet ein Kommando, wird nur intern gerufen
+private void commit(struct cmd_s c)
+{
+  c->finished = 1;
+  //printf("Kommando abgeschlossen: %O\n", c);
+  if (get_eval_cost() > 1000000
+      && sizeof(commands) > 5
+      && find_call_out(#'write_db) == -1)
+  {
+    // In DB wegschreiben
+    call_out(#'write_db, 1);
+  }
+}
+
+public varargs int remove(int silent)
+{
+  write_db();
+  destruct(this_object());
+  return 1;
+}
+
+// gerufen, wenn ein Spieler ein Kommando eingetippt hat, aus modify_command()
+// heraus. Nach Parsen und Kommandoblock, aber vor allem anderen.
+// ACHTUNG: wenn ein Kommando erfolgreich ist, wird KEIN end_cmd() von aussen
+// gerufen...
+public void start_cmd(string cmdstr)
+{
+  if (!interactive(previous_object()))
+      return;
+  cmdstr = lower_case(cmdstr);
+  // letztes Kommando notfalls abschliessen
+  struct cmd_s cmd;
+  if (sizeof(commands))
+  {
+    cmd = commands[<1];
+    if (!cmd->finished)
+    {
+      // wir betrachten es uebrigens als erfolgreich!
+      commit(cmd);
+    }
+  }
+  // teile- mit und Ebenen und alle Kommandos, die nur aus verb und einem Wort
+  // bestehen, sind auch unnuetz.
+  //printf("%O\n",cmdstr);
+  if (sizeof(explode(cmdstr, " ")) < 3
+      || sizeof(cmdstr) > 100
+      || regmatch(cmdstr,"^teile.*mit",RE_PCRE)
+      || regmatch(cmdstr,"^-[[:alpha:]-]*[' :]{1}",RE_PCRE)
+      || regmatch(cmdstr,"^[.']", RE_PCRE)
+     )
+  {
+      return;
+  }
+  cmd = (<cmd_s> verb: explode(cmdstr, " ")[0],
+         cmd: cmdstr,
+         uid: getuid(environment(this_player())),
+         evalno: driver_info(DI_EVAL_NUMBER),
+         success: 1,
+         tp_uid: getuid(previous_object()),
+      );
+  // sonstige Blacklist
+  if (member(blacklist, cmd->verb))
+      return;
+
+  commands += ({cmd});
+  //printf("Kommandostart: %O\n", cmd);
+}
+
+// gerufen von _auswerten() im Spielerobjekt - das kommt ziemlich am Ende der
+// Kommandoverarbeitung - wenn es noch gerufen wird und uns ruft, betrachten
+// wir das Kommando als NICHT erfolgreich.
+public void cmd_unsuccessful()
+{
+  if (!sizeof(commands))
+    return;
+  struct cmd_s cmd = commands[<1];
+  // Darf nur vom Spielerobjekt gerufen werden, welches das letzte Kommando
+  // angefangen hat.
+  if (cmd->tp_uid != getuid(previous_object()))
+    return;
+  // und dies gehoert nur zum letzten Kommando, wenn Verb und Eval-No
+  // uebereinstimmen.
+  if (cmd->verb == query_verb()
+      || cmd->evalno == driver_info(DI_EVAL_NUMBER))
+  {
+    cmd->success = 0;
+    //printf("Kommando nicht erfolgreich: %O\n", cmd);
+    commit(cmd);
+  }
+  // Wenn nicht, war das letzte Kommando offenbar doch erfolgreich. Aus
+  // irgendnem Grund haben wir aber den Start des neuen Kommandos verpasst.
+  // Daher verwerfen wir das jetzt. Das alte Kommando war aber offenbar
+  // erfolgreich, daher wird es jetzt abgeschlossen.
+  else
+  {
+    commit(cmd);
+  }
+}
+
+public void LogEP(int type)
+{
+  if (!sizeof(commands))
+    return;
+  struct cmd_s cmd = commands[<1];
+  // Darf nur vom Spielerobjekt gerufen werden, welches das letzte Kommando
+  // angefangen hat.
+  if (cmd->tp_uid != getuid(previous_object()))
+    return;
+  //printf("FP gefunden: %O\n", cmd);
+  // und dies gehoert nur zum letzten Kommando, wenn Verb und Eval-No
+  // uebereinstimmen.
+  if (cmd->verb == query_verb()
+      || cmd->evalno == driver_info(DI_EVAL_NUMBER))
+  {
+    cmd->fp = type+1;
+    // Und wenn es nen FP gab, ist das Kommando auch erfolgreich.
+    commit(cmd);
+  }
+}
+
diff --git a/secure/telnetneg-structs.c b/secure/telnetneg-structs.c
new file mode 100644
index 0000000..3efeeae
--- /dev/null
+++ b/secure/telnetneg-structs.c
@@ -0,0 +1,47 @@
+// MorgenGrauen MUDlib
+//
+// telnetneg-structs.c -- Structs fuer die Telnet Option handler
+//
+#pragma strict_types,save_types
+#pragma range_check
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+
+struct to_state_s {
+  int localside;      // wish for the local side (MUD)
+  int remoteside;     // wish for the remote side (CLIENT)
+  int *sbdata;        // last SB data sent/received by us
+};
+
+struct telopt_s {
+  int option;
+   // Receivehandler, wird gerufen, wenn wir vom Client irgendwas bzgl. dieser
+   // Telnet Option empfangen. Wenn gesetzt, darf der Client die Option
+   // einschalten.
+  closure remotehandler;
+  // Wird gerufen, wenn die Option auf unserer Seite eingeschaltet wurde.
+  // Wenn gesetzt, soll versucht werden, die Option auf Mudseite
+  // einzuschalten
+  closure localhandler;
+  // Die Wuensche _waehrend_ einer Verhandlung (bzw. gesendete (lo_wishes) und
+  // empfangene (re_wishes) SB-Daten auch ausserhalb von Verhandlungen).
+  struct to_state_s lo_wishes;    // our wishes (sent by us)
+  struct to_state_s re_wishes;    // remote wishes (received by us)
+  // currently effective/active state
+  struct to_state_s state;
+  // data used by the handlers - NOT USED BY this program!
+  mixed data;
+};
+/* explanations:
+   telopt_s->lo_wishes->localside: the state we want to be in  (WILL/WONT)
+   telopt_s->lo_wishes->remoteside: the state we want the other side to
+                                    be in  (DO/DONT)
+   telopt_s->re_wishes->localside: the state the other side wants US to be in
+                                   (DO/DONT)
+   telopt_s->re_wishes->remoteside: the state the other side wants to be in
+                                    (WILL/WONT)
+   telopt_s->state:   the currently effective state of the option on the two
+                      sides.
+   */
+
diff --git a/secure/telnetneg.c b/secure/telnetneg.c
new file mode 100644
index 0000000..341e1a0
--- /dev/null
+++ b/secure/telnetneg.c
@@ -0,0 +1,863 @@
+// MorgenGrauen MUDlib
+//
+// telnetneg.c -- Verwaltung von Telnet-Negotiations
+//
+// $Id$
+
+/* Das Original wurde von Marcus@Tapp zur Verfuegung gestellt. */
+/* Angepasst fuer die MG-Mudlib von Ringor@MG */
+/* Weitgehend ueberarbeitet von Zesstra@MG */
+
+#pragma strict_types,save_types
+#pragma range_check
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+
+inherit "/secure/telnetneg-structs.c";
+
+#define NEED_PROTOTYPES
+#include "/secure/telnetneg.h"
+#undef NEED_PROTOTYPES
+
+// unterstuetzte Optionen:
+// TELOPT_EOR, TELOPT_NAWS, TELOPT_LINEMODE, TELOPT_TTYPE
+
+//#define __DEBUG__ 1
+
+#ifdef __DEBUG__
+#define DEBUG(x)        if (interactive(this_object()))\
+          tell_object(this_object(),"TN: " + x + "\n")
+#define DTN(x,y) _debug_print(x,y)
+#else
+# define DEBUG(x)
+# define DTN(x,y)
+#endif
+
+
+
+// Aus mini_props.c:
+public varargs mixed Query( string str, int type );
+public varargs mixed Set( string str, mixed value, int type );
+
+private nosave mapping TN = ([]);
+nosave string *Terminals;
+
+// Prototypen
+private void eval_naws(int *optargs);
+
+#ifdef __DEBUG__
+// Gibts einige Konstanten mit sym. Namen aus.
+private string dtranslate(int i) {
+  switch(i) {
+    case IAC: return "IAC";
+    case DONT: return "DONT";
+    case DO: return "DO";
+    case WONT: return "WONT";
+    case WILL: return "WILL";
+    case SB: return "SB";
+    case SE: return "SE";
+    case EOR: return "EOR";
+    case TELOPT_LINEMODE: return "TELOPT_LINEMODE";
+    case TELOPT_XDISPLOC: return "TELOPT_XDISPLOC";
+    case TELOPT_ENVIRON: return "TELOPT_ENVIRON";
+    case TELOPT_NEWENV: return "TELOPT_NEWENV";
+    case TELOPT_EOR: return "TELOPT_EOR";
+    case TELOPT_NAWS: return "TELOPT_NAWS";
+    case TELOPT_TSPEED: return "TELOPT_TSPEED";
+    case TELOPT_TTYPE: return "TELOPT_TTYPE";
+    case TELOPT_ECHO: return "TELOPT_ECHO";
+    case TELOPT_SGA: return "TELOPT_SGA";
+    case TELOPT_NAMS: return "TELOPT_NAMS";
+    case TELOPT_STATUS: return "TELOPT_STATUS";
+    case TELOPT_TM: return "TELOPT_TM";
+
+    case TELOPT_MSDP: return "TELOPT_MSDP";
+    case TELOPT_COMPRESS2: return "TELOPT_COMPRESS2";
+    case TELOPT_MSP: return "TELOPT_MSP";
+    case TELOPT_MXP: return "TELOPT_MXP";
+    case TELOPT_ATCP: return "TELOPT_ATCP";
+    case TELOPT_GMCP: return "TELOPT_GMCP";
+    case TELOPT_MSSP: return "TELOPT_MSSP";
+  }
+  return to_string(i);
+}
+
+// Gibt <arr> halbwegs lesbar an this_object() aus.
+private void _debug_print(string x, int *arr) {
+  if (arr[1] == SB && arr[<1] != SE)
+    arr += ({IAC, SE});
+  closure map_int = function string (int i)
+    { if (i >= 32 && i <= 126) return sprintf("%c",i);
+      return "["+to_string(i)+"]";
+    };
+  if (sizeof(arr)<=5) {
+    foreach(int c : arr)
+      x += " " + dtranslate(c);
+  }
+  else {
+      x += dtranslate(arr[0]) + " " + dtranslate(arr[1]) + " "
+           + dtranslate(arr[2]) + " "
+           + implode(map(arr[3..<3], map_int)," ")
+           + " " + dtranslate(arr[<2]) + " " + dtranslate(arr[<1]);
+  }
+  DEBUG(x);
+}
+#endif
+
+protected varargs int send_telnet_neg(int *arr, int bm_flags)
+{
+    if ( sizeof(arr) < 2 )
+        return efun::binary_message(arr,bm_flags);
+
+    struct telopt_s opt = TN[arr[1]];
+
+    switch (arr[0]){
+    case DO:
+    case DONT:
+        (opt->lo_wishes)->remoteside = arr[0];
+        arr = ({IAC}) + arr;
+        break;
+    case WILL:
+    case WONT:
+        (opt->lo_wishes)->localside = arr[0];
+        arr = ({IAC}) + arr;
+        break;
+    case SB:
+        (opt->lo_wishes)->sbdata = arr[0..];
+        arr = ({IAC}) + arr + ({IAC, SE});
+        break;
+    default:
+        break;
+    }
+    DTN("send_tn: ",arr);
+    return efun::binary_message(arr, bm_flags);
+}
+
+protected varargs int send_telnet_neg_str(string str, int bm_flags) {
+#ifdef __DEBUG__
+    // Debugausgaben zur Zeit nur fuer arraybasierte Variante
+    return send_telnet_neg(to_array(str), bm_flags);
+#else
+    if ( sizeof(str) < 2 )
+        return efun::binary_message(str, bm_flags);
+
+    struct telopt_s opt = TN[str[1]];
+
+    switch (str[0]) {
+    case DO:
+    case DONT:
+        (opt->lo_wishes)->remoteside = str[0];
+        str=sprintf("%c%s",IAC,str);
+        break;
+    case WILL:
+    case WONT:
+        (opt->lo_wishes)->localside = str[0];
+        str=sprintf("%c%s",IAC,str);
+        break;
+    case SB:
+        (opt->lo_wishes)->sbdata = map(explode(str[0..],""),#'to_int);
+        str=sprintf("%c%s%c%c", IAC, str, IAC, SE);
+        break;
+    default:
+        break;
+    }
+
+    return efun::binary_message(str, bm_flags);
+#endif // __DEBUG__
+}
+
+// Startet eine Verhandlung, um den Status einer Option zu aendern.
+// Wenn bereits eine Verhandlung laeuft, wird nichts gemacht und -1
+// zurueckgeben.
+// Wenn die Verhandlung keine Aenderung vom Status quo zum Ziel hat, wird
+// nichts gemacht und -2 zurueckgegeben.
+// Ansonsten ist die Rueckgabe die Anzahl der uebermittelten Zeichen.
+// <action>: WILL: Option soll auf dieser Seite eingeschaltet werden.
+//           WONT: Option soll auf dieser Seite ausgeschaltet werden.
+//           DO  : Option soll auf der anderen Seite eingeschaltet werden.
+//           DONT: Option soll auf der anderen Seite ausgeschaltet werden.
+protected int do_telnet_neg(int option, int action) {
+  struct telopt_s opt = TN[option];
+  if (!structp(opt))
+  {
+    opt = (<telopt_s> option: option,
+                      re_wishes: (<to_state_s>),
+                      lo_wishes: (<to_state_s>),
+                      state: (<to_state_s>)
+          );
+    TN[option] = opt;
+  }
+  // es wird nur geprueft, ob wir bereits eine Verhandlung begonnen haben
+  // (lo_wishes), weil reinkommende remote Wuensche letztendlich sofort durch
+  // unsere Antwort erledigt sind.
+  switch(action)
+  {
+    case WILL:
+      if (opt->lo_wishes->localside != 0)
+        return -1;
+      if (opt->state->localside)
+        return -2;
+      return send_telnet_neg( ({ WILL, option }) );
+      break;
+    case WONT:
+      if (opt->lo_wishes->localside != 0)
+        return -1;
+      if (!opt->state->localside)
+        return -2;
+      return send_telnet_neg( ({ WONT, option }) );
+      break;
+    case DO:
+      if (opt->lo_wishes->remoteside != 0)
+        return -1;
+      if (opt->state->remoteside)
+        return -2;
+      return send_telnet_neg( ({ DO, option }) );
+      break;
+    case DONT:
+      if (opt->lo_wishes->remoteside != 0)
+        return -1;
+      if (!opt->state->remoteside)
+        return -2;
+      return send_telnet_neg( ({ DONT, option }) );
+      break;
+  }
+  raise_error(sprintf("Unsupported telnet negotation action in "
+      "do_telnet_neg(): %d\n",action));
+}
+
+// LOCAL Standard Handlers //
+private void _std_lo_handler_eor(struct telopt_s opt, int action) {
+  // tatsaechlich nix zu tun. Handler ist nur da, damit die Option auf dieser
+  // Seite aktiviert wird. Die Arbeit erledigt print_prompt.
+  return;
+}
+
+private void _std_lo_handler_mssp(struct telopt_s opt, int action) {
+  // nur einschalten ist interessant.
+  if (action != LOCALON)
+    return;
+  // Krams senden, wenn Objekt geladen. Sonst wieder abschalten (kommt
+  // hoffentlich nicht vor)...
+  object mssp = find_object("/secure/misc/mssp");
+  if (!mssp)
+    send_telnet_neg( ({WONT, TELOPT_MSSP }) );
+  else
+  {
+    send_telnet_neg_str(sprintf("%c%c%s",
+          SB, TELOPT_MSSP, mssp->get_telnegs_str()));
+    // die Daten brauchen wir nicht mehr
+    opt->lo_wishes->sbdata = 0;
+  }
+}
+
+
+// REMOTE Standard Handlers //
+private void _std_re_handler_tm(struct telopt_s opt, int action,
+                                int *data)
+{
+  // egal, was geantwortet wurde, es gibt nen Hinweis auf die round-trip-time.
+  // Wenn ein Array in opt->data[1] steht, rechnen wir das aus und schreiben es
+  // in opt->data[0] als Ergebnis rein.
+  if (pointerp(opt->data) && pointerp(opt->data[1]))
+  {
+    int *ut = utime();
+    int *start = opt->data[1];
+    int res = (ut[0] - start[0]) * 1000000;
+    res += ut[1] - start[1];
+    opt->data[0] = res;
+    opt->data[1] = 0;
+    DEBUG("RTT: "+res);
+  }
+  return;
+}
+
+private void _std_re_handler_naws(struct telopt_s opt, int action,
+                                  int *data)
+{
+  if (action == SB)
+  {
+    eval_naws(data);
+  }
+}
+
+private void _std_re_handler_linemode(struct telopt_s opt, int action,
+                                      int *data)
+{
+  if (action == REMOTEON)
+  {
+    // see /doc/concepts/negotiations. We use only the minimum
+    // needed for linemode: switching on local commandline-editing
+    // for the client.
+    send_telnet_neg(({ SB, TELOPT_LINEMODE, LM_MODE, MODE_EDIT }));
+    // flush on 0d and 0a...
+    // TODO: what does this exactly do?
+    send_telnet_neg(({ SB, TELOPT_LINEMODE, DO, LM_FORWARDMASK, 0,
+                          0x40|0x08 }));
+    //Gna...
+    opt->lo_wishes->sbdata = ({MODE_EDIT});
+  }
+}
+
+private void _std_re_handler_ttype(struct telopt_s opt, int action,
+                                   int *data)
+{
+  if (action == SB)
+  {
+    //TODO: get rid of this hysterical stuff...
+    //NOTE: We do not do multiple SB SENDs due to some weird
+    //bugs in IBM3270 emulating telnets which crash if we
+    //do that.
+    if ( sizeof(data) < 1 )
+        return;
+
+    if ( data[0] != TELQUAL_IS )
+        return;
+
+    string tmpterminal = lower_case( to_string(data[1..]) );
+    if ( !Terminals )
+        Terminals = ({ tmpterminal });
+    else
+        Terminals += ({ tmpterminal });
+
+    if ( Query(P_TTY_TYPE) )
+          Set( P_TTY_TYPE, Terminals[0] );
+  }
+  else if (action == REMOTEON)
+  {
+    send_telnet_neg(({ SB, TELOPT_TTYPE, TELQUAL_SEND }));
+  }
+}
+
+// Bindet/registriert Handler fuer die jew. Telnet Option. (Oder loescht sie
+// auch wieder.) Je nach <initneg> wird versucht, die Option neu zu
+// verhandeln.
+protected int bind_telneg_handler(int option, closure re, closure lo,
+                                  int initneg)
+{
+  struct telopt_s opt = TN[option];
+  if (!structp(opt))
+  {
+    opt = (<telopt_s> option: option,
+                      re_wishes: (<to_state_s>),
+                      lo_wishes: (<to_state_s>),
+                      state: (<to_state_s>)
+          );
+    TN[option] = opt;
+  }
+
+  opt->remotehandler = re;
+  if (initneg)
+  {
+    if (re)
+      do_telnet_neg(option, DO);
+    else
+      do_telnet_neg(option, DONT );
+  }
+
+  opt->localhandler = lo;
+  if (initneg)
+  {
+    if (lo)
+      do_telnet_neg(option, WILL);
+    else
+      do_telnet_neg(option, WONT);
+  }
+  return 1;
+}
+
+
+// Mal unsere Wuensche an den Client schicken und die Standardhandler
+// registrieren. Hierbei bei Bedarf neue Verhandlungen starten.
+// Gerufen aus login.c nach Verbindungsaufbau.
+// Bemerkung: das Spielerobjekt bietet evt. noch zusaetzliche Telnetoptionen
+//            an, die dann ueber startup_telnet_negs() (im Spielerobjekt)
+//            laufen.
+protected void SendTelopts()
+{
+  bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 1);
+  if (find_object("/secure/misc/mssp"))
+    bind_telneg_handler(TELOPT_MSSP, 0, #'_std_lo_handler_mssp, 1);
+
+  bind_telneg_handler(TELOPT_NAWS, #'_std_re_handler_naws, 0, 1);
+  bind_telneg_handler(TELOPT_LINEMODE, #'_std_re_handler_linemode, 0, 1);
+  bind_telneg_handler(TELOPT_TTYPE, #'_std_re_handler_ttype, 0, 1);
+  // fuer TELOPT_TM jetzt keine Verhandlung anstossen.
+  bind_telneg_handler(TELOPT_TM, #'_std_re_handler_tm, 0, 0);
+}
+
+
+// Bindet die Standardhandler _aus diesem_ Programm (und ueberschreibt dabei
+// ggf. andere). Hierbei werden nur die Handler neu gebunden, keine neuen
+// Verhandlungen initiiert.
+// gerufen aus base.c indirekt via startup_telnet_negs().
+protected void _bind_telneg_std_handlers() {
+  bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 0);
+  // Besondere Situation: MSSP ist nach Spielerlogin eigentlich uninteressant.
+  // Daher sparen wir uns das im Kontext des Spielerobjekts und schalten es
+  // einfach wieder aus.
+  bind_telneg_handler(TELOPT_MSSP, 0, 0, 0);
+
+  bind_telneg_handler(TELOPT_NAWS, #'_std_re_handler_naws, 0, 0);
+  bind_telneg_handler(TELOPT_LINEMODE, #'_std_re_handler_linemode, 0, 0);
+  bind_telneg_handler(TELOPT_TTYPE, #'_std_re_handler_ttype, 0, 0);
+  bind_telneg_handler(TELOPT_TM, #'_std_re_handler_tm, 0, 0);
+}
+
+
+// Ruft die entsprechenden handler von der Telnet Option.
+// Wenn es keinen handler (mehr) gibt, wird die Option auch auf der jeweiligen
+// Seite ausgeschaltet. Deshalb MUSS lo_wishes und re_wishes vom Aufrufer VOR
+// DEM AUFRUF zurueckgesetzt worden sein!
+// <action>: 'LOCALON':   Option wurde auf unserer Seite eingeschaltet
+//           'LOCALOFF':  Option wurde auf unserer Seite ausgeschaltet
+//           'REMOTEON':  Option wurde auf Clientseite eingeschaltet
+//           'REMOTEOFF': Option wurde auf Clientseite ausgeschaltet
+//           'SB':        Suboption negotiation Daten wurden empfangen
+// <data>: die per SB empfangenen Daten (unverarbeitet)
+private void _call_handler(struct telopt_s opt, int action, int *data) {
+  switch(action)
+  {
+    case REMOTEON:
+    case REMOTEOFF:
+    case SB:
+      if (opt->remotehandler)
+      {
+        funcall(opt->remotehandler, opt, action, data);
+      }
+      else
+      {
+        // ok, geht nicht. Ggf. Abschalten (do_telnet_neg passt auf,
+        // dass nur verhandelt wird, wenn die Option an ist.)
+        do_telnet_neg( opt->option, DONT );
+      }
+      break;
+    case LOCALON:
+    case LOCALOFF:
+      if (opt->localhandler)
+      {
+        funcall(opt->localhandler, opt, action);
+      }
+      else
+      {
+      // ok, geht nicht. Ggf. Abschalten (do_telnet_neg passt auf,
+      // dass nur verhandelt wird, wenn die Option an ist.)
+        do_telnet_neg( opt->option, WONT );
+      }
+      break;
+  }
+}
+
+// Gerufen vom Driver, wenn neue telnet options reinkommen.
+void
+telnet_neg(int command, int option, int *optargs)
+{
+    DTN("recv_tn: ", ({IAC, command, option}) + (optargs||({})));
+
+    struct telopt_s opt = TN[option];
+    if (!structp(opt))
+    {
+      opt = (<telopt_s> option: option,
+                        re_wishes: (<to_state_s>),
+                        lo_wishes: (<to_state_s>),
+                        state: (<to_state_s>)
+            );
+      TN[option] = opt;
+    }
+
+    // Was will der Client tun?
+    if (command == WONT)
+    {
+        // Client will die Option auf seiner Seite abschalten. Wir MUESSEN das
+        // akzeptieren.
+        // Wir muessen das allerdings ignorieren, wenn die Option bereits aus
+        // ist.
+        if (opt->state->remoteside==0)
+        {
+          // Ausnahme fuer TELOPT_TM, da das kaum ein Client kann und fuer RTT
+          // es eigentlich auch egal ist, was zurueck kommt: der handler wird
+          // zumindest doch gerufen zum Ausrechnen der RTT
+          if (option == TELOPT_TM)
+            _call_handler(opt, REMOTEOFF, 0);
+          // ansonsten aber wirklich ignorieren. ;)
+          return;
+        }
+        opt->re_wishes->remoteside = command;
+        // Bestaetigung auf ein WONT senden, wenn wir nicht selber schon ein
+        // DONT geschickt hatten.
+        if (opt->lo_wishes->remoteside != DONT) {
+          send_telnet_neg( ({DONT, option}) );
+        }
+        // Wir haben jetzt auf jeden Fall ein DONT gesendet und ein WONT
+        // erhalten. Damit ist die Option jetzt auf der clientseite aus.
+        // Ausserdem setzen wir die Wishes zurueck.
+        opt->re_wishes->remoteside = 0;
+        opt->lo_wishes->remoteside = 0;
+        if (opt->state->remoteside != 0)
+        {
+            opt->state->remoteside = 0;
+            _call_handler(opt, REMOTEOFF, 0);
+        }
+    } // WONT vom Client verarbeitet
+    else if ( command == WILL)
+    {
+        // Wenn die Option bereits an ist, muessen wir dies ignorieren.
+        if (opt->state->remoteside == 1)
+        {
+          // Ausnahme fuer TELOPT_TM, der handler wird zumindest doch gerufen
+          // zum Ausrechnen der RTT. Diese Option laesst sich ohnehin
+          // aktivieren, auch wenn sie schon an ist.
+          if (option == TELOPT_TM)
+            _call_handler(opt, REMOTEON, 0);
+          // sonst aber wirklich ignorieren. ;-)
+          return;
+        }
+        opt->re_wishes->remoteside = command;
+        if ( opt->lo_wishes->remoteside == 0 )
+        {
+            // Der Client will, wir haben noch nix dazu gesagt. (Mit unserer
+            // Antwort ist die Verhandlung uebrigens beendet.)
+            // Wenn es einen remotehandler fuer die Option gibt, schalten wir
+            // sie ein...
+            if (opt->remotehandler)
+            {
+                send_telnet_neg(({DO, option}));
+                // Option jetzt an der Clientseite an.
+                opt->re_wishes->remoteside = 0;
+                opt->lo_wishes->remoteside = 0;
+                if (opt->state->remoteside != 1)
+                {
+                    opt->state->remoteside = 1;
+                    _call_handler(opt, REMOTEON, 0);
+                }
+            }
+            else
+            {
+              // sonst verweigern wir das einschalten (die meisten Optionen
+              // auf Clientseite sind fuer uns eh egal).
+              send_telnet_neg(({DONT, option}));
+              // Option jetzt an der Clientseite aus.
+              opt->re_wishes->remoteside = 0;
+              opt->lo_wishes->remoteside = 0;
+              if (opt->state->remoteside != 0)
+              {
+                  opt->state->remoteside = 0;
+                  _call_handler(opt, REMOTEOFF, 0);
+              }
+            }
+        }
+        else if ( opt->lo_wishes->remoteside == DO)
+        {
+            // Wir haben haben bereits per DO angefordert, d.h. das ist die
+            // Clientbestaetigung - wir duerfen nicht bestaetigen und die
+            // Option ist jetzt clientseitig aktiv. Verhandlung beendet.
+            opt->re_wishes->remoteside = 0;
+            opt->lo_wishes->remoteside = 0;
+            if (opt->state->remoteside != 1)
+            {
+                opt->state->remoteside = 1;
+                _call_handler(opt, REMOTEON, 0);
+            }
+        } // if (DO)
+        else {
+          // Mhmm. Wir hatten ein DONT gesendet, aber der Client hat mit WILL
+          // geantwortet. Das darf er eigentlich gar nicht.
+          //TODO: was sollte man jetzt eigentlich tun? Erstmal wiederholen wir
+          //das DONT...
+          send_telnet_neg( ({DONT, option}) );
+        }
+
+        return;
+    } // WILL vom Client verarbeitet
+    // Was sollen wir (nicht) fuer den Client tun?
+    else if ( command == DONT)
+    {
+        // Client will, dass wir etwas nicht tun. Wir MUESSEN das akzeptieren.
+        // wenn die Option auf unserer Seite aber schon aus ist, muessen wir
+        // dies ignorieren.
+        if (opt->state->localside == 0)
+          return;
+
+        opt->re_wishes->localside = command;
+        // Wenn wir noch kein WONT gesendet haben, senden wir das jetzt als
+        // Bestaetigung.
+        if (opt->lo_wishes->localside = WONT)
+            send_telnet_neg( ({WONT, option}) );
+        // Verhandlung beendet, Option is auf unserer Seite jetzt aus.
+        // Wuensche auch wieder zuruecksetzen.
+        opt->re_wishes->localside = 0;
+        opt->lo_wishes->localside = 0;
+        if (opt->state->localside != 0)
+        {
+          opt->state->localside = 0;
+          _call_handler(opt, LOCALOFF, 0);
+        }
+    }
+    else if ( command == DO )
+    {
+        // Client will, dass wir option tun. Mal schauen, wie wir dazu stehen.
+        // wenn die Option auf unserer Seite aber schon an ist, muessen wir
+        // dies ignorieren.
+        if (opt->state->localside == 1)
+          return;
+
+        opt->re_wishes->localside = command;
+
+        if ( opt->lo_wishes->localside == 0 ) {
+            // wir haben unsere Wuensche noch nicht geaeussert. Sobald wir
+            // bestaetigen, ist die Option auf unserer Seite an/aus und die
+            // Verhandlungen beendet.
+            // in jedem Fall die Wuensche zuruecksetzen
+            opt->re_wishes->localside = 0;
+            opt->lo_wishes->localside = 0;
+            if (opt->localhandler)
+            {
+                send_telnet_neg(({WILL, option}));
+                opt->state->localside = 1;
+                _call_handler(opt, LOCALON, 0);
+            }
+            else
+            {
+                send_telnet_neg(({WONT, option}));
+                opt->state->localside = 0;
+                _call_handler(opt, LOCALOFF, 0);
+            }
+        }
+        else if (opt->lo_wishes->localside == WILL ) {
+            // wir haben schon WILL gesendet, welches der Client jetzt
+            // bestaetigt hat (d.h. die Option ist jetzt auf dieser Seite an),
+            // wir bestaetigen das aber nicht (nochmal).
+            opt->re_wishes->localside = 0;
+            opt->lo_wishes->localside = 0;
+            if (opt->state->localside != 1)
+            {
+              opt->state->localside = 1;
+              _call_handler(opt, LOCALON, 0);
+            }
+        }
+        else {
+            // Mhmm. Wir haben ein WONT gesendet, der Client hat mit DO
+            // geantwortet. Das darf er eigentlich nicht.
+            // TODO: Was tun?
+            send_telnet_neg ( ({WONT, option}) );
+        }
+        // fertig mit DO
+        return;
+    }
+    // bleibt noch SB ueber
+    else if ( command == SB )
+    {
+        opt->re_wishes->sbdata = optargs;
+        _call_handler(opt, SB, optargs);
+        return;
+    } // if ( command == SB )
+}
+
+// wird nur in base.c gerufen, wenn die Verbindung an das Spielerobjekt
+// uebergeben wurde.
+// es uebertraegt unter anderem den Telnet Option Zustand aus login.c (das ist
+// dann previous_object()) in das Spielerobjekt (welches dann this_object())
+// ist!
+protected void
+startup_telnet_negs()
+{
+  int* optargs;
+
+  Set( P_TTY_TYPE, 0 );  //avoid ANY mistakes... Wird unten neu gesetzt.
+  // Daten aus dem Loginobjekt uebertragen. Das ist wichtig! (Dabei wird dann
+  // auch der Status von der letzten Session ueberschrieben.)
+  TN = (mapping) previous_object()->query_telnet_neg();
+  // bevor irgendwas anderes gemacht wird, werden erstmal die Standardhandler
+  // gesetzt. Die sind naemlich in diesem Objekt jetzt erstmal kaputt, weil
+  // sie im Loginobjekt gerufen werden.
+  _bind_telneg_std_handlers();
+  // dann restliche Daten aus dem Loginobjekt holen.
+  Terminals = (string *) previous_object()->query_terminals();
+  Set( P_TTY_COLS, previous_object()->Query(P_TTY_COLS) );
+  Set( P_TTY_ROWS, previous_object()->Query(P_TTY_ROWS) );
+
+  struct telopt_s opt = TN[TELOPT_NAWS];
+  if (optargs = (opt->re_wishes)->sbdata) {
+      eval_naws(optargs);
+  }
+
+  if ( pointerp(Terminals) && sizeof(Terminals)) {
+      if ( Terminals[0][0..3] == "dec-" )
+          Terminals[0] = Terminals[0][4..];
+
+      if ( Terminals[0] == "linux" )
+          Terminals[0] = "vt100";
+
+      Set( P_TTY_TYPE, Terminals[0] );
+  }
+}
+
+// somehow completely out of the ordinary options processing/negotiation. But
+// the only purpose is to transmit something over the wire which is not shown,
+// but (likely) answered by the other device.
+protected void send_telnet_timing_mark() {
+  struct telopt_s opt = TN[TELOPT_TM];
+  if (pointerp(opt->data))
+    opt->data[1] = utime();
+  else
+    opt->data = ({ 0, utime() });
+  // absichtlich nicht do_telnet_ne() verwendet, da dies nicht senden wuerde,
+  // haette der Client schonmal mit WILL geantwortet. TELOPT_TM ist aber eine
+  // Option, bei der man das darf...
+  send_telnet_neg( ({DO, TELOPT_TM}) );
+}
+
+/* Is called from the H_PRINT_PROMPT driver hook and appends the IAC EOR if
+ * the client supports it.
+ */
+void print_prompt(string prompt) {
+//    if (extern_call() && previous_object()!=this_object())
+//        return;
+
+    // ggf. Uhrzeit in den prompt reinschreiben.
+    prompt = regreplace(prompt,"\\t",strftime("%H:%M"),0);
+    // Prompt senden
+    tell_object(this_object(), prompt);
+    // Und EOR senden, falls vom Client gewuenscht.
+    struct telopt_s opt = TN[TELOPT_EOR];
+    if (opt->state->localside == 1)
+    {
+        binary_message(({IAC, EOR}), 1);
+        DTN("tn_eor ",({IAC,EOR}));
+    }
+}
+
+// Helper
+private void eval_naws(int *optargs) {
+  int l, c;
+
+  if ( sizeof(optargs) != 4 )
+  {
+      tell_object(this_object(),
+          break_string( sprintf("Dein Client hat einen Fehler beim"
+                            +"Aushandeln der TELOPT_NAWS - er hat"
+                            +"IAC SB %O IAC SE gesendet!\n",
+                            optargs), 78,
+                    "Der GameDriver teilt Dir mit: " ));
+      // und dem Client sagen, dass er den Schrott nicht mehr uebertragen
+      // soll (falls wir das nicht schon gemacht haben).
+      struct telopt_s opt = TN[TELOPT_NAWS];
+      if (opt->state->remoteside == WILL
+          && opt->lo_wishes->remoteside != DONT)
+        send_telnet_neg(( {DONT, TELOPT_NAWS}) );
+      return;
+  }
+
+  if ( interactive(this_object()) ){
+      if ( !optargs[1] )
+          c = optargs[0];
+      else
+          c = optargs[1] + optargs[0] * 256;
+
+      if ( c < 35 ){
+          if (Query(P_TTY_SHOW))
+              tell_object( this_object(),
+                       break_string("Dein Fenster ist schmaler als"
+                                    +" 35 Zeichen? Du scherzt. ;-)"
+                                    +" Ich benutze den Standardwert"
+                                    +" von 80 Zeichen.\n", 78,
+                                    "Der GameDriver teilt Dir mit: ")
+                       );
+          c = 80;
+      }
+
+      if ( !optargs[3] )
+          l = optargs[2];
+      else
+          l = 256 * optargs[2] + optargs[3];
+
+      if ( l > 100 ){
+          //TODO: remove
+          l = 100;
+          if (Query(P_TTY_SHOW))
+              tell_object( this_object(),
+                       break_string("Tut mir leid, aber ich kann"
+                                    +" nur bis zu 100 Zeilen"
+                                    +" verwalten.\n", (c ? c-2 : 78),
+                                    "Der GameDriver teilt Dir mit: " )
+                       );
+      }
+
+      if ( l < 3 ){
+          if (Query(P_TTY_SHOW))
+              tell_object( this_object(),
+                       break_string("Du willst weniger als drei"
+                                    +" Zeilen benutzen? Glaub ich"
+                                    +" Dir nicht - ich benutze den"
+                                    +" Standardwert von 24"
+                                    +" Zeilen.\n", (c ? c-2 : 78),
+                                    "Der GameDriver teilt Dir mit: " )
+                       );
+          l = 24;
+      }
+
+      if ( ((int) Query(P_TTY_ROWS) != l) ||
+           ((int) Query(P_TTY_COLS) != c) ){
+          Set( P_TTY_ROWS, l );
+          Set( P_TTY_COLS, c );
+
+          if (Query(P_TTY_SHOW))
+              tell_object( this_object(),
+                       break_string("Du hast Deine Fenstergroesse auf"
+                                    +" "+l+" Zeilen und "+c+
+                                    " Spalten geaendert.\n", c-2,
+                                    "Der GameDriver teilt Dir mit: ")
+                       );
+      }
+  }
+}
+
+private void _call_old_SendTelOpts(object po) {
+  if (!objectp(po) || !interactive(po)) return;
+/*
+  closure cl=unbound_lambda( ({}),
+               ({ #'funcall, ({#'symbol_function, "SendTelopts"}) }) );
+
+  funcall(bind_lambda(cl, po));
+*/  
+  // Bloody temporary Hack until next reboot...
+
+  funcall( bind_lambda( #'efun::binary_message, po ),
+           ({ IAC, WILL, TELOPT_EOR,
+              IAC, DO, TELOPT_TTYPE,
+              IAC, DO, TELOPT_NAWS,
+              IAC, DO, TELOPT_LINEMODE,
+           }), 0x1 );
+}
+
+// Query-/Set-Methoden
+// Und wenn hier einer von aussen dran rumpfuscht, werde ich sauer.
+mapping
+query_telnet_neg()
+{
+   if (interactive(previous_object())
+      && program_time(previous_object()) < 1359926079
+      && load_name(this_object()) == "/secure/login" )
+    {
+      call_out(#'_call_old_SendTelOpts, 0, previous_object());
+      // alte Datenstruktur zurueckgeben... Leider leer...
+      return (["sent": m_allocate(3,3), "received": m_allocate(3,3) ]);
+    }
+
+    return TN;
+}
+
+// siehe oben
+string *
+query_terminals() {
+    return Terminals;
+}
+
+public int _query_p_lib_telnet_rttime()
+{
+  struct telopt_s opt = TN[TELOPT_TM];
+  if (opt && pointerp(opt->data))
+    return (opt->data)[0];
+  return 0;
+}
+
diff --git a/secure/telnetneg.h b/secure/telnetneg.h
new file mode 100644
index 0000000..87b6723
--- /dev/null
+++ b/secure/telnetneg.h
@@ -0,0 +1,44 @@
+// MorgenGrauen MUDlib
+//
+// /secure/telnetneg.h -- Definitionen und Prototypes fuer Telnet-Negotiations
+//
+// $Id: telnetneg.h,v 1.1 2003/02/17 11:20:02 Rikus Exp $
+
+#ifndef _SECURE_TELNETNEG_H_
+#define _SECURE_TELNETNEG_H_
+// Dies ist vermutlich der einzige Teil, den andere (z.B. player/base.h)
+// brauchen...
+// Properties fuer die tatsaechliche Fenstergroesse des Telnetclients sowie
+// den eingestellten Terminaltyp. Falls der Client die Daten nicht
+// herausrueckt, bleiben die Properties leer.
+#define P_TTY                "tty"
+#define P_TTY_COLS           "tty_cols"
+#define P_TTY_ROWS           "tty_rows"
+#define P_TTY_TYPE           "tty_type"
+#define P_TTY_SHOW           "tty_show"
+#define P_TELNET_RTTIME      "p_lib_telnet_rttime"
+
+#endif
+
+
+#ifdef NEED_PROTOTYPES
+#ifndef _SECURE_TELNETNEG_H_PROTOS_
+#define _SECURE_TELNETNEG_H_PROTOS_
+// wird eigentlich nur fuer die Implementation gebraucht
+#include "/sys/telnet.h"
+
+// Konstanten fuer die jew. remote und local handler
+#define LOCALON   WILL
+#define LOCALOFF  WONT
+#define REMOTEON  DO
+#define REMOTEOFF DONT
+
+protected int bind_telneg_handler(int option, closure re, closure lo,
+                                  int initneg);
+protected int do_telnet_neg(int option, int action);
+protected varargs int send_telnet_neg(int *arr, int bm_flags);
+protected varargs int send_telnet_neg_str(string str, int bm_flags);
+
+#endif // _SECURE_TELNETNEG_H_PROTOS_
+#endif // NEED_PROTOTYPES
+
diff --git a/secure/topliste.c b/secure/topliste.c
new file mode 100644
index 0000000..2b757e4
--- /dev/null
+++ b/secure/topliste.c
@@ -0,0 +1,280 @@
+// MorgenGrauen MUDlib
+/** \file /file.c
+* Kurzbeschreibung.
+* Langbeschreibung...
+* \author <Autor>
+* \date <date>
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types,save_types,rtt_checks
+#pragma no_clone,no_inherit,no_shadow
+#pragma pedantic, range_check
+
+#include <defines.h>
+#include <events.h>
+#include <wizlevels.h>
+#include <lepmaster.h>
+#include <properties.h>
+#include <userinfo.h>
+
+#define HOME(x) (__PATH__(0)+x)
+#include <living/comm.h>
+#define ZDEBUG(x) if (find_player("zesstra")) \
+  find_player("zesstra")->ReceiveMsg(x,MT_DEBUG,0,object_name()+":",this_object())
+//#define ZDEBUG(x)
+
+object *players = ({});
+
+protected void create()
+{
+  seteuid(getuid());
+  if (sl_open(HOME("ARCH/topliste.sqlite")) != 1)
+  {
+    raise_error("Datenbank konnte nicht geoeffnet werden.\n");
+  }
+  // Tabellen und Indices anlegen, falls die nicht existieren.
+  sl_exec("CREATE TABLE IF NOT EXISTS topliste(name TEXT PRIMARY KEY ASC, "
+          "gilde TEXT NOT NULL, rasse TEXT NOT NULL, "
+          "age DATETIME, wizlevel INTEGER, "
+          "lastupdate DATETIME DEFAULT current_timestamp, "
+          "lep INTEGER, qp INTEGER, xp INTEGER, level INTEGER"
+          "hardcore INTEGER);");
+  sl_exec("CREATE INDEX IF NOT EXISTS idx_gilde ON topliste(gilde);");
+  sl_exec("CREATE INDEX IF NOT EXISTS idx_rasse ON topliste(rasse);");
+  sl_exec("CREATE INDEX IF NOT EXISTS idx_hardcore ON topliste(hardcore);");
+  
+  // Login-Event abonnieren
+  if (EVENTD->RegisterEvent(EVT_LIB_LOGIN,
+                            "listen", this_object()) <= 0)
+  {
+    raise_error("Loginevent konnte nicht abonniert werden.\n");
+  }
+  if (EVENTD->RegisterEvent(EVT_LIB_ADVANCE,
+                            "listen", this_object()) <= 0)
+  {
+    raise_error("EVT_LIB_ADVANCE konnte nicht abonniert werden.\n");
+  }
+  if (EVENTD->RegisterEvent(EVT_LIB_QUEST_SOLVED,
+                            "listen", this_object()) <= 0)
+  {
+    raise_error("EVT_LIB_QUEST_SOLVED konnte nicht abonniert werden.\n");
+  }
+  if (EVENTD->RegisterEvent(EVT_LIB_MINIQUEST_SOLVED,
+                            "listen", this_object()) <= 0)
+  {
+    raise_error("EVT_LIB_MINIQUEST_SOLVED konnte nicht abonniert werden.\n");
+  }
+}
+
+private void process()
+{
+  foreach(object pl : &players)
+  {
+    if (get_eval_cost() < 200000)
+    {
+      call_out(#'process, 2);
+      players -= ({0});
+      return;
+    }
+    sl_exec("INSERT OR REPLACE INTO topliste(name, gilde, rasse, "
+                "age, wizlevel, lastupdate, lep, qp, xp, level, hardcore) "
+                "VALUES(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11);",
+                pl->query_real_name(),
+                pl->QueryProp(P_GUILD) || "unbekannt",
+                pl->QueryProp(P_RACE),
+                pl->QueryProp(P_AGE),
+                query_wiz_level(pl),
+                time(),
+                LEPMASTER->QueryLEPForPlayer(pl),
+                pl->QueryProp(P_QP),
+                pl->QueryProp(P_XP),
+                pl->QueryProp(P_LEVEL),
+                pl->query_hc_play()
+               );
+    pl=0;
+  }
+  players -= ({0});
+}
+
+public void listen(string eid, object trigob, mixed data)
+{
+  if (previous_object() != find_object(EVENTD)
+      || !trigob
+      || !query_once_interactive(trigob)
+      || IS_LEARNER(trigob)
+      || (mixed)trigob->QueryProp(P_TESTPLAYER)
+      || trigob->QueryGuest()
+      || trigob->QueryProp(P_NO_TOPLIST)
+     )
+    return;
+
+  // Dieser handler hat nur 30k Ticks zur Verfuegung, dummerweise kann
+  // QueryLEPForPlayer() locker ueber 30k kosten, wenn der Spieler nicht in
+  // Caches drin ist. Daher muss das jetzt per call_out entkoppelt werden.
+  // *seufz*
+  if (member(players, trigob) == -1)
+  {
+    players += ({trigob});
+    if (find_call_out(#'process) == -1)
+      call_out(#'process, 2);
+  }
+}
+
+public varargs < <string|int>* >* Liste(string rasse, string gilde,
+                                     int limit, string sort)
+{
+  // Defaults:
+  sort ||= "lep";
+  if (!limit || limit > 100)
+    limit=100;
+  else if (limit < 1)
+    limit=1;
+  if (rasse && gilde)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE rasse=?1 AND gilde=?2 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        rasse, gilde);
+  else if (rasse)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE rasse=?1 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        rasse);
+  else if (gilde)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE gilde=?1 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        gilde);
+  return sl_exec(
+      "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+      "ORDER BY "+sort+" DESC LIMIT "+limit+";");
+}
+
+public varargs < <string|int>* >* SpielerListe(string rasse, string gilde,
+                                     int limit, string sort)
+{
+  // Defaults:
+  sort ||= "lep";
+  if (!limit || limit > 100)
+    limit=100;
+  else if (limit < 1)
+    limit=1;
+  if (rasse && gilde)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE rasse=?1 AND gilde=?2 AND wizlevel=0 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        rasse, gilde);
+  else if (rasse)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE rasse=?1 AND wizlevel=0 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        rasse);
+  else if (gilde)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE gilde=?1 AND wizlevel=0 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        gilde);
+  return sl_exec(
+      "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+      "WHERE wizlevel=0 "
+      "ORDER BY "+sort+" DESC LIMIT "+limit+";");
+}
+
+public varargs < <string|int>* >* SeherListe(string rasse, string gilde,
+                                     int limit, string sort)
+{
+  // Defaults:
+  sort ||= "lep";
+  if (!limit || limit > 100)
+    limit=100;
+  else if (limit < 1)
+    limit=1;
+  if (rasse && gilde)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE rasse=?1 AND gilde=?2 AND wizlevel=1 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        rasse, gilde);
+  else if (rasse)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE rasse=?1 AND wizlevel=1 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        rasse);
+  else if (gilde)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE gilde=?1 AND wizlevel=1 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        gilde);
+  return sl_exec(
+      "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+      "WHERE wizlevel=1 "
+      "ORDER BY "+sort+" DESC LIMIT "+limit+";");
+}
+
+public varargs < <string|int>* >* HardcoreListe(string rasse, string gilde,
+                                     int limit, string sort)
+{
+  // Defaults:
+  sort ||= "lep";
+  if (!limit || limit > 100)
+    limit=100;
+  else if (limit < 1)
+    limit=1;
+  if (rasse && gilde)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE rasse=?1 AND gilde=?2 AND hardcore>0 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        rasse, gilde);
+  else if (rasse)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE rasse=?1 AND hardcore>0 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        rasse);
+  else if (gilde)
+    return sl_exec(
+        "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+        "WHERE gilde=?1 AND hardcore>0 "
+        "ORDER BY "+sort+" DESC LIMIT "+limit+";",
+        gilde);
+  return sl_exec(
+      "select name,lep,qp,xp,level,age,rasse,gilde,wizlevel,hardcore from topliste "
+      "WHERE hardcore>0 "
+      "ORDER BY "+sort+" DESC LIMIT "+limit+";");
+}
+
+varargs int remove(int silent)
+{
+  EVENTD->UnregisterEvent(EVT_LIB_LOGIN, this_object());
+  sl_close();
+  destruct(ME);
+  return 1;
+}
+
+public mixed sql_query(string query)
+{
+  if (ARCH_SECURITY)
+    return sl_exec(query);
+  return 0;
+}
+
+void reset()
+{
+  // Alle Eintraege loeschen, die seit 90 nicht mehr aktualisiert wurden.
+//  sl_exec("DELETE FROM topliste WHERE lastupdate<?1;",
+//                time()-90*24*3600);
+  sl_exec("DELETE FROM topliste WHERE name IN (SELECT name FROM topliste "
+          "ORDER BY lep DESC LIMIT 1000, -1);");
+  set_next_reset(86400);
+}
+
diff --git a/secure/udp/channel.c b/secure/udp/channel.c
new file mode 100644
index 0000000..3592ffe
--- /dev/null
+++ b/secure/udp/channel.c
@@ -0,0 +1,110 @@
+// MorgenGrauen MUDlib
+//
+// channel.c
+//
+// $Id: channel.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+#include <udp.h>
+
+#include <properties.h>
+#include <daemon.h>
+
+#ifdef ZEBEDEE
+inherit "/sys/format";
+#endif
+
+#define COMMAND                "cmd"
+#define CHANNEL                "channel"
+
+private nosave mixed _name_;
+
+int filter_listeners(object ob, string channel) {
+    return ob->QueryProp(P_INTERMUD);
+}
+
+int udp_channel(mapping data) {
+  object *list;
+  string msg;
+  int i, type;
+
+  /* Compatability with older systems. */
+  if (!data[CHANNEL])
+    data[CHANNEL] = data["CHANNEL"];
+  if (!data[COMMAND])
+    data[COMMAND] = data["CMD"];
+  if (!data[DATA])
+    data[DATA]="";
+
+  if (!stringp(data[CHANNEL]) || !sizeof(data[CHANNEL])
+      || !stringp(data[DATA]) || !sizeof(data[DATA])
+      || !stringp(data[NAME]) || !sizeof(data[NAME])
+      || !stringp(data[SENDER]) || !sizeof(data[SENDER]))
+    return 0;
+
+  data[DATA]=
+    implode(filter(explode(data[DATA], ""), #'>=, " "), "");//'))
+  data[NAME]=
+    implode(filter(explode(data[NAME], ""), #'>=, " "), "");//'))
+  switch(data[COMMAND]) {
+  case "list":
+    /* Request for a list of people listening to a certain channel. */
+    list = filter(users(), "filter_listeners",
+                        this_object(), data[CHANNEL]);
+    if (i = sizeof(list)) {
+      msg = "[" + capitalize(data[CHANNEL]) + "@" +
+        LOCAL_NAME + "] Listening:\n";
+      while(i--)
+        msg +=
+          "    " + capitalize(list[i]->query_real_name()) + "\n";
+    }
+    else
+      msg = "[" + capitalize(data[CHANNEL]) + "@" + LOCAL_NAME 
+      + "] Nobody Listening.\n";
+    INETD->_send_udp(data[NAME], ([
+                                  REQUEST: REPLY,
+                                  RECIPIENT: data[SENDER],
+                                  ID: data[ID],
+                                  DATA: msg
+                                ]));
+    return 1;
+  case "emote": /* A channel emote. */
+      type = MSG_EMOTE;
+      break;
+  default: /* A regular channel message. */
+    type = MSG_SAY;
+    break;
+  }
+  _name_ = capitalize(data[SENDER])+"@"+capitalize(data[NAME]);
+  CHMASTER->send(capitalize(data[CHANNEL]), this_object(), 
+                 data[DATA], type);
+  _name_ = 0;
+  return 1;
+}
+
+string name() { return _name_ || "<Intermud>"; }
+string Name() {return capitalize(_name_ || "<Intermud>");}
+
+private void _send(string mud, mixed data, mapping request)
+{
+  if(member(data[HOST_COMMANDS], "channel") != -1 ||
+     member(data[HOST_COMMANDS], "*") != -1)
+    INETD->_send_udp(data[HOST_NAME], request);
+}
+
+void ChannelMessage(mixed m)
+{
+  mapping request;
+  if(m[1] == this_object()) return;
+  request = ([
+              REQUEST : "channel",
+              SENDER  : m[1]->name() || capitalize(getuid(m[1])),
+              "CHANNEL": lower_case(m[0]),
+              DATA    : implode(old_explode(m[2], "\n"), " ")]);
+  if(m[3] == MSG_GEMOTE || m[3] == MSG_EMOTE)
+  { 
+    request["EMOTE"] = 1;
+    request["CMD"] = "emote";
+  }
+  walk_mapping(INETD->query("hosts") - ([lower_case(MUDNAME)]), 
+               #'_send/*'*/, request);
+}
diff --git a/secure/udp/finger.c b/secure/udp/finger.c
new file mode 100644
index 0000000..c9d4c4d
--- /dev/null
+++ b/secure/udp/finger.c
@@ -0,0 +1,31 @@
+// MorgenGrauen MUDlib
+//
+// finger.c
+//
+// $Id: finger.c 6081 2006-10-23 14:12:34Z Zesstra $
+
+#include <udp.h>
+
+string last_finger;
+
+#ifdef ZEBEDEE
+#include <system.h>
+#elif !defined(INETD)
+#define INETD	"/secure/inetd"
+#endif
+
+void udp_finger(mapping data)
+{
+  last_finger=capitalize(to_string(data[SENDER]))+"@"+data[NAME];
+  INETD->_send_udp(data[NAME], ([
+	REQUEST: REPLY,
+	RECIPIENT: data[SENDER],
+	ID: data[ID],
+	DATA: "/p/daemon/finger"->finger_single(data[DATA])
+    ]) );
+}
+
+string QueryLastFinger()
+{
+  return last_finger;
+}
diff --git a/secure/udp/htmlwho.c b/secure/udp/htmlwho.c
new file mode 100644
index 0000000..d9cd899
--- /dev/null
+++ b/secure/udp/htmlwho.c
@@ -0,0 +1,53 @@
+// MorgenGrauen MUDlib
+//
+// htmlwho.c
+//
+// $Id: htmlwho.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+#pragma weak_types
+
+#include <udp.h>
+
+#define TJ(x) if (find_player("jof")) tell_object(find_player("jof"),x)
+
+string adjust(string str,int wid)
+{
+  int w2;
+
+  w2=wid/2;
+  wid=wid-w2;
+  return extract("                                                          ",0,wid-1)+str+
+    extract("                                                         ",0,w2-1);
+}
+
+udp_htmlwho(data)
+{
+  int i,num;
+  string *mdata;
+  string wholiste,tmp,tmp2;
+ 
+  mdata="/obj/werliste"->QueryWhoListe(0,0,1);
+  num=sizeof(mdata);
+  for (i=num-1;i>=0;i--)
+  {
+    tmp=mdata[i][0]->name();
+    if (tmp&&sizeof(tmp))
+    {
+      tmp2=explode(mdata[i][1],tmp);
+      if (sizeof(tmp2)>1)
+      {
+        tmp2[0]="<A HREF=\"/htbin/mudwww?finger?"+getuid(mdata[i][0])+"\"><b>";
+        tmp2[1]="</b></A>"+tmp2[1];
+        mdata[i][1]=implode(tmp2,tmp);
+      }
+      mdata[i]=" <LI> "+mdata[i][1];
+    }
+  }
+  wholiste=implode(mdata,"\n");
+  INETD->_send_udp(data[NAME], ([
+				REQUEST: REPLY,
+				RECIPIENT: data[SENDER],
+				ID: data[ID],
+				DATA: wholiste
+				]) );
+}
diff --git a/secure/udp/locate.c b/secure/udp/locate.c
new file mode 100644
index 0000000..d793d05
--- /dev/null
+++ b/secure/udp/locate.c
@@ -0,0 +1,33 @@
+// MorgenGrauen MUDlib
+//
+// locate.c
+//
+// $Id: locate.c 6081 2006-10-23 14:12:34Z Zesstra $
+
+#include <udp.h>
+
+#define FOUND		"fnd"
+#define USER		"user"
+#define VERBOSE		"vbs"
+
+void udp_locate(mapping data) {
+    mapping ret;
+    object ob;
+
+    ret = ([
+	REQUEST: REPLY,
+	RECIPIENT: data[SENDER],
+	ID: data[ID],
+	USER: data[USER],
+	VERBOSE: data[VERBOSE],
+    ]);
+    if (data[DATA] && (ob = find_player(data[DATA])) &&
+    interactive(ob) && !ob->query_invis()) {
+	ret[FOUND] = 1;
+	ret[DATA] = "locate@" + LOCAL_NAME + ": " + ob->short();
+    }
+    else
+	ret[DATA] = "locate@" + LOCAL_NAME + ": No such player: " +
+	data[DATA] + "\n";
+    INETD->_send_udp(data[NAME], ret);
+}
diff --git a/secure/udp/mail.c b/secure/udp/mail.c
new file mode 100644
index 0000000..04f8651
--- /dev/null
+++ b/secure/udp/mail.c
@@ -0,0 +1,64 @@
+// MorgenGrauen MUDlib
+//
+// mail.c
+//
+// $Id: mail.c 6081 2006-10-23 14:12:34Z Zesstra $
+
+/*
+ * VERSION 1.0
+ * udp module for the UDP MAIL system (Author: Alvin@Sushi)
+ * Requires INETD V0.60 or higher (INETD Author: Nostradamus@Zebedee)
+ */
+
+#include <udp.h>
+#include <udp_mail.h>
+
+/*
+#define DEBUG(msg) if (find_player("hate")) tell_object(find_player("hate"),msg)
+*/
+#undef DEBUG
+#define DEBUG(x)
+
+void udp_mail(mapping data)
+{
+  DEBUG(sprintf("UDPMAIL %O\n",data));
+  if(!member(data,RECIPIENT) || !data[RECIPIENT])
+    {
+      log_file("INETD","Invalid udp_mail packet. No Recipient.\n");
+      return;
+    }
+
+  if(!LOCAL_MAILER->query_recipient_ok(data[RECIPIENT]))
+    {
+      INETD->_send_udp(data[NAME], ([
+	    REQUEST: REPLY,
+	    RECIPIENT: data[SENDER],
+	    UDPM_STATUS: UDPM_STATUS_UNKNOWN_PLAYER,
+	    UDPM_WRITER: data[UDPM_WRITER],
+	    UDPM_SPOOL_NAME: data[UDPM_SPOOL_NAME],
+	    ID: data[ID],
+	    DATA: "Reason: Unknown player \""+capitalize(data[RECIPIENT])+
+		"\"\n\nINCLUDED MESSAGE FOLLOWS :-\n\n"+
+		"Subject: "+data[UDPM_SUBJECT]+"\n"+data[DATA]
+      ]) );
+
+      return;
+    }
+
+  LOCAL_MAILER->deliver_mail(
+	data[RECIPIENT],			/* To */
+	data[UDPM_WRITER]+"@"+data[NAME],	/* From */
+	data[UDPM_SUBJECT],			/* Subj */
+	data[DATA]				/* Mail Body */
+  );
+
+  INETD->_send_udp(data[NAME], ([
+	    REQUEST: REPLY,
+	    RECIPIENT: data[SENDER],
+	    UDPM_STATUS: UDPM_STATUS_DELIVERED_OK,
+	    UDPM_WRITER: data[UDPM_WRITER],
+	    UDPM_SPOOL_NAME: data[UDPM_SPOOL_NAME],
+	    ID: data[ID],
+	    DATA: data[RECIPIENT]
+  ]) );
+}
diff --git a/secure/udp/man.c b/secure/udp/man.c
new file mode 100644
index 0000000..88c4b57
--- /dev/null
+++ b/secure/udp/man.c
@@ -0,0 +1,98 @@
+#pragma strict_types
+#include <udp.h>
+#include <daemon.h>
+
+#define MAX_READ_FILE_LEN 50000
+
+// TEMPORARY
+
+#include <udp_language.h>
+#include <logging.h>
+
+#ifndef LOCAL_NAME
+#define LOCAL_NAME "MorgenGrauen"
+#endif
+
+#ifndef INETD_INVALID_ACCESS
+#define INETD_INVALID_ACCESS INTERMUDLOG("INVALID_ACCESS")
+#endif
+
+#ifndef INVALID_ACCESS
+#define INVALID_ACCESS(x) \
+           log_file(INETD_INVALID_ACCESS, \
+                    sprintf(INETD_INV_ACCESS_MSG "TI: %O PO: %O\n", \
+                            ctime()[4..15],this_interactive(), \
+                            previous_object()))
+#endif
+
+
+// END TEMPORARY
+
+void udp_man(mapping data)
+{
+  mapping pages;
+  int index;
+  string manpage,ret;
+  string|string* tmp;
+
+  if (previous_object()!=find_object(INETD))
+  {
+    INVALID_ACCESS(Man);
+    return;
+  }
+
+  manpage=data[DATA];
+  tmp=explode(manpage,"/");
+  if (sizeof(tmp)>1)
+  {
+    if (file_size(MAND_DOCDIR+manpage)>=0)
+      tmp=({tmp[<1],manpage});
+    else
+      tmp=({});
+  }
+  else
+    tmp=(string *)call_other(MAND,"locate",data[DATA],0);
+  pages=([]);
+  index=sizeof(tmp);
+  while(index--)
+  {
+    if (tmp[1][0..1]!="g.") pages[tmp[index]]=tmp[index-1];
+    index--;
+  }
+  switch(sizeof(pages))
+  {
+    case 0:
+      ret=sprintf(INETD_NO_MANPAGE,LOCAL_NAME,manpage);
+      break;
+    case 1:
+      tmp=m_indices(pages)[0];
+      ret=sprintf(INETD_MANPAGE_FOUND,LOCAL_NAME,pages[tmp]);
+      index=0;
+      while(manpage=read_file(MAND_DOCDIR+tmp,index))
+      {
+        ret+=manpage;
+        index+=MAX_READ_FILE_LEN;
+      }
+      break;
+    default:
+      ret=sprintf(INETD_MANPAGES,LOCAL_NAME,"",
+                  break_string(implode(m_values(pages)," "),78),"");
+      break;
+  }
+  INETD->_send_udp(data[NAME],
+                     ([
+                       REQUEST: REPLY,
+                       RECIPIENT: data[SENDER],
+                       ID: data[ID],
+                       DATA: ret
+                     ]));
+}
+
+string send_request(string mudname, string pagename)
+{
+  return (INETD->_send_udp(mudname,
+                             ([REQUEST: "man",
+                               DATA: pagename,
+                               SENDER: getuid(previous_object())]),1)||
+          sprintf(INETD_MAN_REQUESTED,pagename,mudname));
+}
diff --git a/secure/udp/ping.c b/secure/udp/ping.c
new file mode 100644
index 0000000..f48329d
--- /dev/null
+++ b/secure/udp/ping.c
@@ -0,0 +1,22 @@
+// MorgenGrauen MUDlib
+//
+// ping.c
+//
+// $Id: ping.c 6081 2006-10-23 14:12:34Z Zesstra $
+
+#include <udp.h>
+
+#ifdef ZEBEDEE
+#include <system.h>
+#elif !defined(INETD)
+#define INETD	"/secure/inetd"
+#endif
+
+void udp_ping(mapping data) {
+    INETD->_send_udp(data[NAME], ([
+	REQUEST: REPLY,
+	RECIPIENT: data[SENDER],
+	ID: data[ID],
+	DATA: LOCAL_NAME + " is alive.\n"
+    ]) );
+}
diff --git a/secure/udp/query.c b/secure/udp/query.c
new file mode 100644
index 0000000..f9bc7cd
--- /dev/null
+++ b/secure/udp/query.c
@@ -0,0 +1,88 @@
+// MorgenGrauen MUDlib
+//
+// query.c
+//
+// $Id: query.c 7397 2010-01-26 21:48:11Z Zesstra $
+
+#include <udp.h>
+#include <udp_language.h>
+#include <strings.h>
+
+#ifdef ZEBEDEE
+#include <system.h>
+#elif !defined(INETD)
+#define INETD        "/secure/inetd"
+#endif
+
+/* Mud / Admin email address. */
+#define EMAIL        "mud@mg.mud.de"
+
+void udp_query(mapping data) {
+    mapping ret;
+
+    switch(data[DATA]) {
+        case "commands":
+            ret = INETD->query("hosts");
+            if (ret[lower_case(data[NAME])])
+                ret = ([
+                DATA: implode(ret[lower_case(data[NAME])][LOCAL_COMMANDS], ":")
+                ]);
+            else
+                ret = ([ DATA: implode(INETD->query("commands"), ":") ]);
+            break;
+        case "email":
+            ret = ([ DATA: EMAIL ]);
+            break;
+        case "hosts":
+        {
+            string tmp = "";
+      foreach(string mudname, mixed fields: INETD->query("hosts")) {
+        tmp += fields[HOST_NAME] + ":" +
+             fields[HOST_IP] + ":" +
+             fields[HOST_UDP_PORT] + ":" +
+             implode(fields[LOCAL_COMMANDS], ",") + ":" +
+             implode(fields[HOST_COMMANDS], ",") + "\n";
+      }
+            ret = ([ DATA: trim(tmp,TRIM_RIGHT, "\n") ]);
+            break;
+        }
+        case "inetd":
+            ret = ([ DATA: INETD_VERSION ]);
+            break;
+        case "list":
+            /* List of thingsthat can be queried. */
+            ret = ([ DATA: "commands:email:hosts:inetd:mud_port:time:version" ]);
+            break;
+        case "mud_port":
+            ret = ([ DATA: query_mud_port() ]);
+            break;
+        case "time":
+            ret = ([ DATA: time() ]);
+            break;
+        case "version":
+            ret = ([ DATA: version() ]);
+            break;
+        default:
+            /* Just ignore it for the time being. */
+            return;
+    }
+    INETD->_send_udp(data[NAME], ret + ([
+        REQUEST: REPLY,
+        RECIPIENT: data[SENDER],
+        ID: data[ID],
+        "QUERY": data[DATA]
+    ]) );
+}
+
+string send_request(string mudname, string prop)
+{
+  if (!stringp(mudname) || !stringp(prop))
+    return 0;
+  
+  return (INETD->_send_udp(mudname,
+                             ([REQUEST: "query",
+                               DATA: prop,
+                               SENDER: getuid(previous_object())]),1)||
+          sprintf(INETD_QUERY_REQUESTED, prop, mudname));
+}
+
diff --git a/secure/udp/reply.c b/secure/udp/reply.c
new file mode 100644
index 0000000..d60661a
--- /dev/null
+++ b/secure/udp/reply.c
@@ -0,0 +1,54 @@
+// MorgenGrauen MUDlib
+//
+// reply.c
+//
+// $Id: reply.c,v 1.2 2003/04/08 09:28:17 Rikus Exp $
+
+#include <udp.h>
+
+#ifndef DATE
+#define DATE	ctime()[4..15]
+#endif
+
+void udp_reply(mapping data)
+{
+  string err,recpt,serv;
+  object ob;
+  
+  
+  if (pointerp(data[SYSTEM])&&member(data[SYSTEM],TIME_OUT)>-1)
+  {
+    if (data[SENDER])
+    {
+      if (stringp(data[SENDER])&&(ob=find_player(data[SENDER])))
+      {
+        switch(data[REQUEST])
+        {
+          case "tell": serv="teile mit: ";break;
+          case "who":  serv="wer: ";break;
+          default: serv=data[REQUEST]+": "; break;
+        }
+        tell_object(ob, break_string("Das Mud \'" + data[NAME] +
+                                     "\' konnte nicht erreicht werden.\n",
+                                     78,serv));
+      }
+      else
+        if (objectp(ob = data[SENDER])||(ob = find_object(data[SENDER])))
+          ob->udp_reply(data);
+    }
+    return;
+  }
+  if (data[RECIPIENT])
+  {
+    if (stringp(data[RECIPIENT])&&(ob = find_player(data[RECIPIENT])))
+      tell_object(ob, "\n" + data[DATA]);
+    else if (ob=find_object(data[RECIPIENT]))
+      ob->udp_reply(data);
+    return;
+  }
+  if (data[REQUEST]=="ping")return;
+  if (data[DATA])
+    log_file(INETD_LOG_FILE, DATE + ": Antwort von " + data[NAME] +
+             "\n" + data[DATA] + "\n");
+  return;
+}
diff --git a/secure/udp/tell.c b/secure/udp/tell.c
new file mode 100644
index 0000000..7b1ab61
--- /dev/null
+++ b/secure/udp/tell.c
@@ -0,0 +1,118 @@
+// MorgenGrauen MUDlib
+//
+// tell.c
+//
+// $Id: tell.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+#include <config.h>
+#include <udp.h>
+#include <properties.h>
+
+#ifdef ZEBEDEE
+#include <system.h>
+inherit "/sys/format";
+#elif !defined(INETD)
+#define INETD "/secure/inetd"
+#endif
+
+
+void udp_tell(mapping data) {
+  object ob;
+  string message_string, message_prefix, away;
+  int i,re;
+  string *message_array;
+
+  if (data[RECIPIENT] &&
+       (ob = find_player(lower_case(data[RECIPIENT]))) &&
+       interactive(ob)) {
+    
+    if (!stringp(data[SENDER]) || !sizeof(data[SENDER]))
+	data[SENDER]="<Unknown>";
+    if (!stringp(data[DATA]) || !sizeof(data[DATA]))
+	data[DATA]="<Nichts>";
+   
+    data[SENDER]=
+      implode(filter(explode(data[SENDER], ""),
+                           #'>=,/*'*/ " "), "");
+    data[DATA]=
+      implode(filter(explode(data[DATA], ""),
+                           #'>=,/*'*/ " "), "");
+    message_prefix=capitalize(data[SENDER])+"@"+data[NAME]+
+                   " teilt Dir mit: ";
+    message_string=break_string(data[DATA],78,message_prefix,0);
+
+    /* Die Anzahl der Leerzeilen am Ende eines tm's scheint nicht genormt */
+    while(message_string[<1]=='\n')message_string=message_string[0..<2];
+    message_string += "\n";
+
+    re = ob->Message("\n"+message_string, MSGFLAG_RTELL);
+
+    if (!ob->QueryProp(P_INVIS)){
+      /* Erst testen, ob die Meldung ueberhaupt angekommen ist! */
+      if(re==MESSAGE_DEAF)
+        message_string=sprintf("%s@"MUDNAME" ist momentan leider taub.\n",
+                               capitalize(getuid(ob)));
+      else if(re==MESSAGE_IGNORE_YOU)
+        message_string=sprintf("%s@"MUDNAME" ignoriert Dich.\n",
+                               capitalize(getuid(ob)));
+      else if(re==MESSAGE_IGNORE_MUD)
+        message_string=sprintf("%s@"MUDNAME" ignoriert das Mud '%s'.\n",
+                               capitalize(getuid(ob)),
+                               data[NAME]);
+      else {
+        /* Erst dann die Erfolgs-Rueckmeldung abschicken */
+        message_prefix="Du teilst "+capitalize(data[RECIPIENT]) + "@" 
+                       LOCAL_NAME + " mit: ";
+        message_string=break_string(data[DATA],78,message_prefix,0);
+        if(ob->QueryProp(P_AWAY))
+          message_string=sprintf("%s%s@"MUDNAME" ist gerade nicht "
+                                 "da: %s\n",
+                                 message_string,
+                                 ob->name(WER),
+                                 ob->QueryProp(P_AWAY));
+        else if ((i=query_idle(ob))>=600){ // 10 Mins
+          if (i<3600) away=time2string("%m %M",i);
+          else away=time2string("%h %H und %m %M",i);
+          message_string=
+             sprintf("%s%s@"MUDNAME" ist seit %s voellig untaetig.\n",
+                     message_string,
+                     ob->Name(WER),
+                     away);
+        }
+        switch(re) {
+        case MESSAGE_CACHE:
+          message_string +=
+            sprintf("%s@"MUDNAME" moechte gerade nicht gestoert werden.\n"+
+                    "Die Mittelung wurde von einem kleinen Kobold in Empfang"+
+                    "genommen.\nEr wird sie spaeter weiterleiten.\n",
+                    capitalize(getuid(ob)));
+          break;
+        case MESSAGE_CACHE_FULL:
+          message_string +=
+            sprintf("%s@"MUDNAME" moechte gerade nicht gestoert werden.\n"+
+                    "Die Mitteilung ging verloren, denn der Kobold kann sich "+
+                    "nichts mehr merken.\n",
+                    capitalize(getuid(ob)));
+          break;
+        }
+      }
+    }
+    else message_string="\nRoot@"MUDNAME": Spieler "+
+                        capitalize(data[RECIPIENT])+
+                        " finde ich in "MUDNAME" nicht!\n";
+    INETD->_send_udp(data[NAME],
+                    ([ REQUEST: REPLY,
+                       RECIPIENT: data[SENDER],
+                       ID: data[ID],
+                       DATA: "\n"+message_string ]) );
+  }
+  else
+    INETD->_send_udp(data[NAME],
+                    ([ REQUEST: REPLY,
+                       RECIPIENT: data[SENDER],
+                       ID: data[ID],
+                       DATA: sprintf("Root@"MUDNAME": Spieler %s finde "+
+                                     "ich in "MUDNAME" nicht!\n",
+                                     capitalize(data[RECIPIENT]))
+                    ]) );
+}
diff --git a/secure/udp/who.c b/secure/udp/who.c
new file mode 100644
index 0000000..7be9bb3
--- /dev/null
+++ b/secure/udp/who.c
@@ -0,0 +1,58 @@
+// MorgenGrauen MUDlib
+//
+// who.c
+//
+// $Id: who.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+#include <config.h>
+#include <udp.h>
+
+int last;
+int maxtoday, maxever;
+
+void create()
+{
+  string tmp1, tmp2, dummy;
+
+  if (time()-last<1800) return;
+  last=time();
+  tmp1=read_file("/etc/maxusers.ever",0,1);
+  tmp2=read_file("/etc/maxusers",0,1);
+  if (stringp(tmp1)&&sizeof(tmp1)) sscanf(tmp1,"%d %s",maxever,dummy);
+  if (stringp(tmp2)&&sizeof(tmp2)) sscanf(tmp2,"%d %s",maxtoday,dummy);
+}
+
+string adjust(string str,int wid) {
+  return sprintf("%*|s",wid,str);
+}
+
+void udp_who(mapping data)
+{
+  int i;
+  string *lines;
+  string wholiste,header;
+
+  create();
+  lines="/obj/werliste"->QueryWhoListe(0, 1);
+  wholiste=implode(lines,"\n");
+  lines=({
+    "*------------------------------------------------------------------------*",
+    "","","","",
+    "*------------------------------------------------------------------------*"});
+  header=MUDNAME", LDMud "+__VERSION__;
+  lines[1]="|"+adjust(header,sizeof(lines[0])-2)+"|";
+  header="Adresse: MG.Mud.DE (87.79.24.60) 23 (alternativ 4711)";
+  lines[2]="|"+adjust(header,sizeof(lines[0])-2)+"|";
+  header="Uptime: "+uptime();
+  lines[3]="|"+adjust(header,sizeof(lines[0])-2)+"|";
+  header=_MUDLIB_NAME_"-Mudlib "_MUDLIB_VERSION_;
+  lines[4]="|"+adjust(header,sizeof(lines[0])-2)+"|";
+  header=implode(lines,"\n");
+  wholiste=header+"\n"+wholiste+sprintf("\n*** Anwesende im "MUDNAME": Max. heute %d, Rekord %d\n",maxtoday,maxever);
+  INETD->_send_udp(data[NAME], ([
+				REQUEST: REPLY,
+				RECIPIENT: data[SENDER],
+				ID: data[ID],
+				DATA: wholiste
+				]) );
+}
diff --git a/secure/udp/www.c b/secure/udp/www.c
new file mode 100644
index 0000000..cad4ffc
--- /dev/null
+++ b/secure/udp/www.c
@@ -0,0 +1,130 @@
+// MorgenGrauen MUDlib
+//
+// www.c -- WWW Guest Client
+//
+// $Id: www.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+#pragma strong_types
+#pragma combine_strings
+
+#ifdef MORGENGRAUEN
+# include <properties.h>
+#endif
+
+#include <udp.h>
+#include <www.h>
+
+#undef DEBUG 
+
+private mixed pending; // pending udp requests
+
+// HTMLunescape -- try to resolve %<hex> escape codes from HTML/WWW
+private string HTMLunescape(string char)
+{
+  int len;
+  if(!char || !(len = sizeof(char))) return "";
+  if(char[0] == '%' && len = 3) {
+    int val, ch;
+    while(--len) {
+      switch(char[len]) {
+      case '0'..'9':
+        val = (int)char[len..len];
+        break;
+      case 'A'..'F':
+        val = char[len]-55;
+        break;
+      }
+      if(len < 2) val <<= 4;
+      ch += val;
+    }
+    return sprintf("%c", ch);
+  }
+  return char;
+}
+
+private string translate(string str)
+{
+  return implode(map(regexplode(str, "[%].."), #'HTMLunescape/*'*/), "");
+}
+
+// decode() -- decode the input cmds string
+private mapping decode(string input)
+{
+  mixed tmp; int i;
+  mapping cmds;
+  cmds = ([]);
+  i = sizeof(tmp = old_explode(translate(input), "&"));
+  while(i--)
+  {
+    if(sizeof(tmp[i] = old_explode(tmp[i], "=")) == 2)
+      cmds[tmp[i][0]] = tmp[i][1];
+  }
+  return cmds;
+}
+
+// put() -- put together a key and a value
+private string put(string key, mapping val)
+{
+  return key+"="+val[key];
+}
+// encode() -- encode the input cmds string
+private string encode(mapping input)
+{ return implode(map(m_indices(input), #'put/*'*/, input), "&"); }
+
+void Send(mapping data, string text, mixed back)
+{
+  if(strstr((string)previous_object(), UDPPATH+"/")) return;
+  if(!data && !pending) return;
+  else if(!data && pending)
+  {
+    data = pending[0]; back = pending[1]; pending = 0;
+  }
+  INETD->_send_udp(data[NAME], ([
+      REQUEST: REPLY,
+      RECIPIENT: data[SENDER],
+      ID: data[ID],
+      "URL":data[DATA],
+      DATA: "\n\n"+text+"\n"
+      ]));
+}
+
+private string exch(string str, mapping to)
+{
+  if(!to[str]) return str;
+  return to[str];
+}
+
+private string xcode(string str, mapping to)
+{
+  return implode(map(regexplode(str, implode(m_indices(to), "|")),
+                           #'exch/*'*/, to), "");
+}
+
+void udp_www(mapping data)
+{
+  string text, error;
+  string back; int size;
+  mapping cmds;
+  error = catch(size = sizeof(cmds = decode(data[DATA])));
+  if(cmds[BACK]) back = xcode(cmds[BACK], (["XampX":"&", "XeqX":"="]));
+  cmds[BACK] = xcode(encode(cmds-([BACK])), (["&":"XampX", "=":"XeqX"]));
+  if(error ||
+     error=catch(text=("/"+object_name(this_object())+"."+cmds[REQ])->Request(cmds)))
+  {
+#ifdef DEBUG
+    text = "<H1>Fehler: "+error+"</H1><HR><H3>Kontext:</H3>"
+   + "<PRE>"+sprintf("%O", data)+"</PRE>";
+#else
+    text = "<H1>Fehler: Anfrage konnte nicht bearbeitet werden!</H1>";
+#endif
+    log_file(WWWLOG, "ERROR: "+error[0..<2]+", DATA FOLLOWS:\n");
+  }
+  log_file(WWWLOG, sprintf("[%s] %s\n", ctime(time())[4..15], data[DATA]));
+  if(cmds[REQ] == R_INTERMUD && !text)
+  {
+    pending = ({data, back});
+    return 0;
+  }
+  pending = 0;
+  funcall(#'Send, data, text, back);
+}
diff --git a/secure/udp/www.finger.c b/secure/udp/www.finger.c
new file mode 100644
index 0000000..dbbccaf
--- /dev/null
+++ b/secure/udp/www.finger.c
@@ -0,0 +1,72 @@
+// MorgenGrauen MUDlib
+//
+// www.finger.c
+//
+// $Id: www.finger.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+#pragma strong_types
+#pragma combine_strings
+
+#include <properties.h>
+#include <www.h>
+#include <regexp.h>
+
+string Request(mapping cmds)
+{
+  if(!sizeof(cmds) || !stringp(cmds[USER]))
+    return ERROR("Kein Nutzer angegeben!");
+  /*
+   * Kann ja sein, dass ein Spieler auf die Idee kommt, HTML-Tags
+   * in seine Beschreibung einzubauen. Unsere Seite ist aber schon
+   * interaktiv genug. (Anm: Nur <.*>-Vorkommnisse zu ersetzen nutzt
+   * nix, da man auch mit einzelnen Zeichen Schaden machen kann.
+   */
+  string result = regreplace(FINGER("-a "+cmds[USER]), "<","\\&lt;",1); 
+  result = regreplace(result, ">","\\&gt;",1);
+  string *reslines = explode(result,"\n");
+  /*
+   * Grund des kommenden Codeblocks ist , dass manche Spieler ihre
+   * Homepage mit "http://mg.mud.de" angeben, andere nur"mg.mud.de" 
+   * schreiben. Damit aber der Browser den Link als absolut interpretiert, 
+   * muss das http:// davor stehen, und zwar nur einmal. 
+   */
+  string *tmp = regexp(reslines,"^Homepage:");
+  if (sizeof(tmp)&&stringp(tmp[0])&&sizeof(tmp[0])>16) {
+	  string tmp2;
+    string quoted = regreplace(tmp[0],"([[\\]+*?.\\\\])","\\\\\\1", 1);
+    if (tmp[0][10..16]=="http://")
+      tmp2=sprintf("Homepage: <A HREF=\"%s\">%s</A>",
+		      tmp[0][10..],tmp[0][10..]);
+    else
+      tmp2=sprintf("Homepage: <A HREF=\"http://%s\">%s</A>",
+		      tmp[0][10..],tmp[0][10..]);
+    result = regreplace(result,quoted,tmp2,1);
+  }
+  tmp = regexp(reslines,"^Avatar-URI:");
+  if (sizeof(tmp)) {
+     result = regreplace(result,
+             "Avatar-URI: ([^\n]*)",
+             "Avatar-URI: <a href=\\1>\\1</a>",1); 
+    if (sizeof(regexp(({tmp[0]}),"http[s]{0,1}://",RE_PCRE))) {
+      string uri = regreplace(tmp[0], "Avatar-URI: ([^\n]*)", "\\1",1);
+      result = "<img src=\""+uri+"\" height=150 alt=\"" + capitalize(cmds[USER])
+               + "\" /><br>"
+               + result;
+    }
+  }
+
+  result = regreplace(result,
+		      "E-Mail-Adresse: ([^\n]*)",
+		      "E-Mail-Adresse: Bitte nachfragen...",1);
+
+  result = regreplace(result,
+		      "Messenger: ([^\n]*)",
+		      "Messenger: Bitte nachfragen...", 1);
+
+  result = regreplace(result,
+		      "ICQ: ([^\n]*)",
+		      "ICQ: Bitte nachfragen...", 1);
+
+  return "<H2>Wer ist "+capitalize(cmds[USER])+"?</H2><HR>"
+    +"<PRE>"+result+"</PRE>";
+}
diff --git a/secure/udp/www.news.c b/secure/udp/www.news.c
new file mode 100644
index 0000000..ce068db
--- /dev/null
+++ b/secure/udp/www.news.c
@@ -0,0 +1,214 @@
+// MorgenGrauen MUDlib
+//
+// www.news.c -- WWW frontend for reading mud news
+//
+// $Id: www.news.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+#pragma strong_types
+#pragma combine_strings
+
+#include <www.h>
+
+#define DBG(x)  tell_object(find_player("hate"), sprintf("DBG: %O\n", x))
+
+#define NEWSD "/secure/news"
+
+#define N_GROUP 0
+#define N_AUTHOR 1
+#define N_TID 2
+#define N_DATE 3
+#define N_TITLE 4
+#define N_ARTICLE 5
+
+varargs private string url(string group, string article)
+{
+  return "\""+MUDWWW+"?"+REQ+"="+R_NEWS
+    +(group?"&"+GROUP+"="+group:"")
+    +(article?"&"+ARTICLE+"="+article:"")+"\"";
+}
+
+varargs private string make_link(string text, string group, string article)
+{
+  if(!text || !sizeof(text)) text = "-Unbenannt-";
+  return "<A HREF="+url(group, article)+">"+text+"</A>";
+}
+
+string GroupList()
+{
+  string *list, ret;
+  int i, t;
+
+  list = NEWSD->GetGroups();
+  for (i = 0, ret = ""; i < sizeof(list); i++)
+    ret += sprintf("[%3d Artikel, %-6s] %s\n",
+                   sizeof(NEWSD->GetNotes(list[i])),
+                   dtime(t = NEWSD->GetNewsTime(list[i]))[5..11],
+                   make_link(list[i],list[i]+":"+t));
+  return "<H2>&Uuml;bersicht</H2>"
+        +"<H3>["+sizeof(list)+" Gruppen, "
+        +"letzte &Auml;nderung "+dtime(NEWSD->GetNewsTime())+"]</H3>"
+        +"<PRE>" + ret +"</PRE>";
+}
+
+#define THREAD(a) ("~#! rn="+(a[N_AUTHOR])+" rt="+(a[N_DATE])+ \
+                   " rg="+(a[N_GROUP]))
+#define TEXPR "rn=[^\n ]*|rt=[^\n ]*|rg=[^\n ]*"
+
+private mixed tinfo(mixed article)
+{
+  mixed tmp, info;
+  string rn, rt, rg, tid;
+  int j, k;
+
+  tmp = regexp(old_explode(article[N_ARTICLE], "\n"), "^~#!");
+  for(j = sizeof(tmp); j--;) {
+    mixed line;
+    line = old_explode(tmp[j], " ");
+    for(k = sizeof(line); k--;) {
+      if(line[k][0..1]=="rn") rn = line[k][3..];
+      if(line[k][0..1]=="rt") rt = line[k][3..];
+      if(line[k][0..1]=="rg") rg = line[k][3..];
+      if(line[k][0..2]=="tid") tid = line[k][4..];
+    }
+  }
+  if(!tid) tid = ""+article[N_DATE];
+  return ({ rn, rt, rg, tid });
+}
+
+#define RN  0
+#define RT  1
+#define RG  2
+#define TID 3
+
+private int thread(mixed article, int i, mapping t)
+{
+  mixed info;
+  info = tinfo(article);
+
+  if(info[TID]) {
+    t[info[TID]]++;
+    t[info[TID], 1] = sprintf("%3.3d [%-12s %-6s]%-3s %s\n",
+            i+1,
+            article[N_AUTHOR]+":",
+            dtime(article[N_DATE])[5..11],
+            (t[info[TID]] - 1) ? "+"+(t[info[TID]]-1) : " - ",
+            make_link(article[N_TITLE],
+          article[N_GROUP], to_string(i)))
+      + (t[info[TID], 1] ? t[info[TID], 1] : "");
+    t[info[TID], 2] = info;
+    if(article[N_DATE] > to_int(t[info[TID], 3]))
+      t[info[TID], 3] = ""+article[N_DATE];
+    return 1;
+  }
+}
+
+private int expired(mixed *list, int i)
+{
+  mixed info;
+  info = tinfo(list[i]);
+  for(i--; i >= 0; i--)
+    if(list[i][N_AUTHOR] == info[RN] &&
+       list[i][N_DATE] == to_int(info[RT]))
+      return 0;
+  return 1;
+}
+
+string ArticleList(string group)
+{
+  string *list, ret;
+  mapping t;
+  int i;
+
+  list = NEWSD->GetNotes(group = old_explode(group, ":")[0]);
+  if (!pointerp(list)) {
+  return "<H2>Gruppe: "+group+"</H2>"
+    "<H3>existiert nicht.</H3>"
+    "["+make_link("Gruppen&uuml;bersicht")+"]";
+  }
+  t = m_allocate(0,4);
+  for (i = sizeof(list)-1, ret = ""; i >= 0; i--)
+    if(!thread(list[i], i, t) || expired(list, i))
+    {
+      int ttmp;
+      string tid;
+      mixed tt; tt = tinfo(list[i]);
+      ttmp = t[tid = tt[TID]] - 1;
+      ret = sprintf("%3.3d [%-12s %-6s]%-3s %s\n",
+		    i+1,
+                    list[i][N_AUTHOR]+":",
+                    dtime(list[i][N_DATE])[5..11],
+                    ttmp > 0 ? "+"+ttmp : " - ",
+                    make_link((string)list[i][N_TITLE]
+            +(ttmp > 0 ?
+        " ("+dtime(to_int(t[tid, 3]))[5..11]
+        +dtime(to_int(t[tid, 3]))[17..]+")" : ""),
+            group, to_string(i)+":"+t[tid, 3])) + ret;
+    }
+
+  return "<H2>Gruppe: "+group+"</H2>"
+    +"<H3>["+sizeof(list)+" Artikel, "
+    +"letzte &Auml;nderung "+dtime(NEWSD->GetNewsTime(group))+"]</H3>"
+    +"<PRE>" + ret + "</PRE>"
+    +"["+make_link("Gruppen&uuml;bersicht")+"]";
+}
+
+private varargs string Message(string group, mixed article)
+{
+  mixed text, tmp, ttmp, next, prev, info;
+  string art;
+  mapping t;
+  int i;
+
+  if (!article) article = 0;
+  else article = to_int(old_explode(article, ":")[0]);
+
+  tmp = NEWSD->GetNotes(group = old_explode(group, ":")[0]);
+  if (pointerp(tmp) && (article >= sizeof(tmp)))
+      return("Artikel nicht gefunden, soviele Artikel hat diese Rubrik "
+	  "nicht!\n");
+
+  text = tmp[article];
+
+  t = m_allocate(0,4);
+  for(i = sizeof(tmp)-1; i > article; i--)
+    thread(tmp[i], i, t);
+  next = "N&auml;chster Artikel";
+  prev = "Voriger Artikel";
+
+  art = implode((ttmp = old_explode(text[N_ARTICLE], "\n"))
+                - regexp(ttmp, "^~#!"), "\n");
+
+  art = regreplace(art, "<", "\\&lt;", 1);
+  art = regreplace(art, ">", "\\&gt;", 1);
+  art = regreplace(art, "([a-zA-Z][a-zA-Z]*://[^ \n\t][^ \n\t]*)", "<a href=\"\\1\">\\1</a>", 1);
+
+  info = tinfo(text);
+
+  return "<H1>" + text[N_TITLE] + "</H1><HR>\n"
+       + "<H3>" + group + ": " + text[N_AUTHOR]
+       + " (Artikel " + (article + 1) + ", " + dtime(text[N_DATE]) + ")\n</H3>"
+       + (info[RN] ? ("<H4>Antwort auf "+info[RN]+
+          (expired(tmp, article) ? " (verfallen)" : "")+"</H4>")
+    : "")
+       + "<PRE>" + art + "</PRE>\n"
+       + (t[info[TID]] ?
+          "<HR>Weitere Artikel zu diesem Thema:"
+  + "<PRE>" + t[info[TID], 1] + "</PRE><HR>" : "")
+       + " ["+(article < sizeof(tmp)-1 ? make_link(next, group,to_string(article+1)) :
+              next)+"]"
+       + " ["+(article ? make_link(prev, group, to_string(article-1)) : prev)+"]"
+       + " ["+make_link("Artikel&uuml;bersicht", group)+"]"
+       + " ["+make_link("Gruppen&uuml;bersicht")+"]";
+}
+
+string Request(mapping cmds)
+{
+  string text;
+  if(!cmds[GROUP]) text = GroupList();
+  else
+    if(!cmds[ARTICLE]) text = ArticleList(cmds[GROUP]);
+    else text = Message(cmds[GROUP], cmds[ARTICLE]);
+
+  return "<H2>"+MUDNAME+" Zeitung</H2><HR>"
+        +text;
+}
diff --git a/secure/udp/www.who.c b/secure/udp/www.who.c
new file mode 100644
index 0000000..6646569
--- /dev/null
+++ b/secure/udp/www.who.c
@@ -0,0 +1,41 @@
+// MorgenGrauen MUDlib
+//
+// www.who.c
+//
+// $Id: www.who.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+#pragma strong_types
+#pragma combine_strings
+
+#include <config.h>
+#include <properties.h>
+#include <www.h>
+
+string MakeLink(mixed entry)
+{
+  string nm;
+  int idx;
+
+  entry[1] = regreplace(entry[1], "<", "\\&lt;", 1);
+  entry[1] = regreplace(entry[1], ">", "\\&gt;", 1);
+  nm = getuid(entry[0]);
+  if(nm == " R O O T ") return "<TT>"+entry[1][0..2]+"</TT>"+entry[1][3..];
+  idx = strstr(lower_case(entry[1]), nm);
+  return "<TT>"+entry[1][0..2]+"</TT>"+entry[1][3..idx-1]
+       + "<A HREF=\""+MUDWWW+"?"+REQ+"="+R_FINGER+"&"+USER+"="+nm+"\"><B>"
+       + entry[1][idx..idx = idx+sizeof(nm)]
+       + "</B></A>"
+       + entry[1][idx+1..];
+}
+
+string Request(mapping cmds)
+{
+  string *who, *list; int i, s;
+  if (!sizeof(cmds)) return ERROR("Anfrage ung&uuml;ltig!");
+  who = allocate(s = sizeof(list = WHO));
+  for(i = s; i--; i > 0) 
+    who[i] = MakeLink(list[s - i - 1]);
+  // who = map(WHO, #'MakeLink/*'*/);
+  return "<H2>Wer ist gerade in "MUDNAME"?</H2><HR>"
+       + "<OL><LI>"+implode(who, "\n<LI>")+"</OL>";
+}
diff --git a/secure/udp_mail.c b/secure/udp_mail.c
new file mode 100644
index 0000000..3203fb9
--- /dev/null
+++ b/secure/udp_mail.c
@@ -0,0 +1,285 @@
+/*
+ * VERSION 1.0
+ * UDP MAIL system (Author: Alvin@Sushi)
+ * Requires INETD V0.60 or higher (INETD Author: Nostradamus@Zebedee)
+ */
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#include <udp.h>
+#include <udp_mail.h>
+
+#ifdef DEBUG
+#undef DEBUG
+#endif
+#define DEBUG(x)
+
+mapping spool_item;
+
+static string *spool;
+
+private int match_mud_name(string mudname, string match_str) {
+    return mudname[0..sizeof(match_str)-1] == match_str;
+}
+
+static void save_spool_item()
+{
+  string name;
+  int count;
+
+  if(!spool_item || !mappingp(spool_item) || spool_item==([]))
+    return;
+
+  do {
+    ++count;
+    name=spool_item[UDPMS_DEST]+"-"+to_string(count);
+  } while(spool && member(spool, name)!=-1);
+
+  save_object(UDPM_SPOOL_DIR+name);
+
+  if(!spool || !sizeof(spool))
+    spool = ({ name });
+  else
+    spool += ({ name });
+}
+
+/* forward declaration */
+void deliver_mail(string recipient,string mud,string from,
+	string subj,string mail_body,int status_flag,string spool_name);
+
+/* forward declaration */
+static void start_retry_callout();
+
+static void remove_from_spool(string spool_file)
+{
+  int idx;
+
+  if(spool && (idx=member(spool,spool_file))!=-1)
+    {
+      spool -= ({ spool_file });
+      if(!sizeof(spool))
+        spool=0;
+    }
+
+  if(file_size("/"+UDPM_SPOOL_DIR+spool_file+".o")>0)
+    if(!rm("/"+UDPM_SPOOL_DIR+spool_file+".o"))
+      log_file(INETD_LOG_FILE,"UPD_MAIL: Can't delete spool file "+
+		"/"+UDPM_SPOOL_DIR+spool_file+".o");
+}
+
+static void retry_send()
+{
+  int i;
+  string msg;
+
+  if(!spool || !sizeof(spool)) return;
+
+  for(i=0;i<sizeof(spool);++i)
+    {
+      if(!restore_object(UDPM_SPOOL_DIR+spool[i]))
+        {
+          log_file(INETD_LOG_FILE,"UDP_MAIL: Falied to restore spool file "+
+		UDPM_SPOOL_DIR+spool[i]);
+          continue;
+        }
+
+      if(time() - spool_item[UDPMS_TIME] > UDPM_SEND_FAIL*60)
+        {
+          msg="Reason: Unable to connect to site \""+spool_item[UDPMS_DEST]+
+		"\"\n\nINCLUDED MESSAGE FOLLOWS :-\n\n"+
+		"To: "+spool_item[UDPMS_TO]+"\n"+
+		"Subject: "+spool_item[UDPMS_SUBJECT]+"\n"+
+		spool_item[UDPMS_BODY];
+
+          LOCAL_MAILER->deliver_mail(
+		spool_item[UDPMS_FROM],	/* TO */
+		"Mailer@"+LOCAL_NAME,	/* FROM */
+		"Bounced Mail",		/* SUBJECT */
+		msg			/* MAIL BODY */
+	  );
+          remove_from_spool(spool[i]);
+          return;
+        }
+
+      deliver_mail(
+	spool_item[UDPMS_TO],
+	spool_item[UDPMS_DEST],
+	spool_item[UDPMS_FROM],
+	spool_item[UDPMS_SUBJECT],
+	spool_item[UDPMS_BODY],
+	UDPM_STATUS_IN_SPOOL,
+	spool[i]);
+    }
+
+  start_retry_callout();
+}
+
+static void start_retry_callout()
+{
+  if(find_call_out("retry_send")!= -1 ) return;
+
+  call_out("retry_send",UDPM_RETRY_SEND*60);
+}
+
+static void failed_to_deliver(mapping data)
+{
+  string msg;
+  object obj;
+
+  if(!data[SYSTEM] || data[SYSTEM] != TIME_OUT)
+    {
+      msg="Reason: Error in connection to remote site \""+data[NAME]+"\"\n\n"+
+	"INCLUDED MESSAGE FOLLOWS :-\n\n"+
+	"To: "+data[RECIPIENT]+"\n"+
+	"Subject: "+data[UDPM_SUBJECT]+"\n"+data[DATA];
+
+      LOCAL_MAILER->deliver_mail(
+		data[UDPM_WRITER],	/* TO */
+		"Mailer@"+LOCAL_NAME,	/* FROM */
+		"Bounced Mail",		/* SUBJECT */
+		msg			/* MAIL BODY */
+      );
+      return;
+    }
+
+  /* OK transmission timed out.. place in mail spool */
+  
+  if((obj=find_player(data[UDPM_WRITER])))
+    {
+      tell_object(obj,"Mail delivery to "+data[RECIPIENT]+"@"+data[NAME]+
+	" Timed Out. Placing mail in spool.\n");
+    }
+
+  spool_item=([
+	UDPMS_TIME:	time(),
+	UDPMS_TO:	data[RECIPIENT],
+	UDPMS_DEST:	data[NAME],
+	UDPMS_FROM:	data[UDPM_WRITER],
+	UDPMS_SUBJECT:	data[UDPM_SUBJECT],
+	UDPMS_BODY:	data[DATA]
+  ]);
+
+  save_spool_item();
+
+  start_retry_callout();
+}
+
+static void get_pending_deliveries()
+{
+  string *entries;
+  int i;
+
+  entries=get_dir(UDPM_SPOOL_DIR+"*.o");
+  if(!entries || !sizeof(entries)) return;
+
+  spool=allocate(sizeof(entries));
+  for(i=0;i<sizeof(entries);++i)
+    spool[i]=entries[i][0..<3];
+
+  start_retry_callout();
+}
+
+void create()
+{
+	seteuid(getuid(this_object()));
+  get_pending_deliveries();
+}
+
+/*
+ * Public routines
+ */
+
+int query_valid_mail_host(string hostname)
+{
+  string *match;
+
+  match=filter(m_indices((mapping)INETD->query("hosts")),
+		#'match_mud_name,lower_case(hostname));
+
+  return (sizeof(match)==1);
+}
+
+void deliver_mail(string recipient,string mud,string from,
+	string subj,string mail_body,int status_flag,string spool_name)
+{
+  mapping data;
+
+  // Geloggt wird, wenn ein aufrufendes Objekt nicht sicher ist.
+  if (object_name(previous_object())[0..7]!="/secure/")
+    write_file("/secure/ARCH/DELIVER_MAIL",
+      sprintf("%s : Aufruf von /secure/udp_mail->deliver_mail()\n"
+              "  Sender: %O Empfaenger: %O@%O\n  PO: %O TI: %O TP:%O\n\n",
+              ctime(time()),from, recipient, mud,
+              previous_object(), this_interactive(), this_player()));
+  
+  data=([
+	REQUEST: "mail",
+	RECIPIENT: recipient,
+        SENDER: this_object(),
+	UDPM_STATUS: status_flag,
+	UDPM_WRITER: lower_case(from),
+	UDPM_SUBJECT: subj,
+        UDPM_SPOOL_NAME: spool_name,
+	DATA: mail_body
+  ]);
+
+  INETD->_send_udp(mud,data,1);
+}
+
+void udp_reply(mapping data)
+{
+  object sender;
+
+  DEBUG(sprintf("MAILER RECEIVED %O\n",data));
+  if (!member(data,UDPM_STATUS))
+  {
+     DEBUG("BOUNCING\n");
+     LOCAL_MAILER->deliver_mail(
+        data[UDPM_WRITER],	/* TO */
+	"INETD@"+data[NAME],	/* FROM */
+	"Bounced Mail(No mail support yet?)",		/* SUBJECT */
+	data[DATA]		/* MAIL BODY */
+	);
+        if(data[UDPM_SPOOL_NAME])
+          remove_from_spool(data[UDPM_SPOOL_NAME]);
+  } else
+  switch(data[UDPM_STATUS])
+    {
+      case UDPM_STATUS_TIME_OUT:
+        failed_to_deliver(data);
+        break;
+
+      case UDPM_STATUS_DELIVERED_OK:
+        if((sender=find_player(data[UDPM_WRITER])))
+          {
+            tell_object(sender,"Mailer@"+data[NAME]+": "+
+		"Mail to "+capitalize(data[DATA])+" delivered ok.\n");
+          }
+        if(data[UDPM_SPOOL_NAME])
+          remove_from_spool(data[UDPM_SPOOL_NAME]);
+
+        break;
+
+      case UDPM_STATUS_UNKNOWN_PLAYER:
+        LOCAL_MAILER->deliver_mail(
+		data[UDPM_WRITER],	/* TO */
+		"Mailer@"+data[NAME],	/* FROM */
+		"Bounced Mail",		/* SUBJECT */
+		data[DATA]		/* MAIL BODY */
+	);
+        if(data[UDPM_SPOOL_NAME])
+          remove_from_spool(data[UDPM_SPOOL_NAME]);
+        break;
+
+	case UDPM_STATUS_IN_SPOOL:
+          /* Do nothing */
+          break;
+    }
+}
diff --git a/secure/wahlmaschine.c b/secure/wahlmaschine.c
new file mode 100644
index 0000000..8705f87
--- /dev/null
+++ b/secure/wahlmaschine.c
@@ -0,0 +1,252 @@
+// da dieses Objekt in /secure liegt und ROOTID inne hat, muessen besondere
+// Sicherheitsvorkehrungen getroffen werden. Insbesondere duerfen keine Files
+// aus /std/ inheritet werden, da es fuer diese Files keiner ROOTID bedarf.
+
+#include <properties.h>
+#include <defines.h>
+#include "/secure/wizlevels.h"
+
+static string in_use;
+
+string thema;
+string *moeglichkeiten;
+string *ergebnis;
+
+#undef SAY
+
+private void SAY( string str )
+{
+  write ( str );
+  write_file("/log/WAHL", str);
+}
+
+void create()
+{
+  if (clonep(ME)) destruct(ME);
+  if (ergebnis) return;
+  seteuid(getuid());
+  in_use=0;
+  ergebnis=({({}),({})});
+  moeglichkeiten=({});
+  restore_object("wahl/wahl");
+  move_object(ME, "/gilden/abenteurer");
+}
+
+varargs string name(int casus, int demon)
+{
+  if (demon==RAW) return "Wahlmaschine";
+  switch(casus) {
+    case WEM:
+    case WESSEN:
+      if (demon==1)
+        return "der Wahlmaschine";
+      return "einer Wahlmaschine";
+    default:
+      if (demon==1)
+        return "die Wahlmaschine";
+      return "eine Wahlmaschine";
+  }
+}
+
+varargs string Name(int casus, int demon)
+{   return capitalize(name(casus, demon));  }
+
+
+public string QueryPronoun(int casus)
+{
+  switch(casus)
+  {
+    case WER:
+      return "sie";
+      break;
+    case WEM: 
+      return "ihr";
+  }
+  return "sie";
+}                                                                                                                   
+
+string short()
+{  return "Eine Wahlmaschine.\n";  }
+
+string long()
+{
+  return "Eine Wahlmaschine. Mit ihr koennen geheime Wahlen durchgefuehrt werden.\n"+
+    "Aktuelles Thema: "+(stringp(thema) ? thema : "keines")+"\n";
+}
+
+mixed Query(mixed param)
+{
+   if (!stringp(param)) return 0;
+   switch (param) {
+     case P_NOGET:   return 1;
+     case P_GENDER:  return FEMALE;
+     case P_NAME:    return "Wahlmaschine";
+   }
+   return 0;
+}
+
+mixed QueryProp(mixed param)
+{  return Query(param);  }
+
+int Set(mixed param)
+{  return -1;  }
+
+int SetProp(mixed param)
+{  return -1;  }
+
+varargs int id(string str, int lvl)
+{  return (member(({"maschine", "waehler", "wahlmaschine"}), str)!=-1);  }
+
+// ab hier kommt der eigentliche Code der Wahlmaschine
+
+void init()
+{
+  add_action("waehle", "waehl",1);
+  add_action("auswertung","auswertung");
+  add_action("wahlthema", "wahlthema");
+  add_action("moeglichkeit","moeglichkeit");
+}
+
+void save_me()
+{  save_object("wahl/wahl");  }
+
+static int waehle()
+{
+  int i;
+  mixed second;
+
+  if (!RPL) return 0;
+  if (RPL->QueryProp(P_LEVEL)<10) {
+    notify_fail("Man kann erst ab Stufe 10 mitwaehlen :(\n");
+    return 0;
+  }
+  if (RPL->QueryProp(P_TESTPLAYER)) {
+    notify_fail("Testspieler sind von der Wahl ausgeschlossen.\n");
+    return 0;
+  }
+  if ((second=RPL->QueryProp(P_SECOND)) &&
+      (!stringp(second) || (file_size("/save/"+lower_case(second[0..0])+"/"+lower_case(second)+".o")<=0))) {
+    notify_fail("Nicht richtig markierte Zweities sind von der Wahl ausgeschlossen.\n");
+    return 0;
+  }
+  if (in_use)
+  {
+    write(in_use+" waehlt gerade, warte also bitte einen Augenblick.\n");
+    return 1;
+  }
+  in_use=capitalize(getuid(RPL));
+  say(in_use+" tritt an die Wahlmaschine.\n");
+  write(long());
+  write("Das steht zur Auswahl:\n");
+  for (i=0;i<sizeof(moeglichkeiten);i++)
+    write(sprintf(" -- %d.) %s\n",i+1,moeglichkeiten[i]));
+  write("Triff nun Deine Wahl (sie ist UNSICHTBAR): ");
+  input_to("waehle_2",1);
+  return 1;
+}
+
+private int falsch()
+{
+  write("Diese Auswahl ist ungueltig. Versuchs bitte gleich nochmal.\n");
+  say(in_use+" hat ausgewaehlt.\n");
+  in_use=0;
+  return 1;
+}
+
+static int waehle_2(string str)
+{
+  int i;
+  mixed name;
+
+  if (!str) return falsch();
+  
+  name=RPL->QueryProp(P_SECOND);
+  if (!stringp(name))
+    name=getuid(RPL);
+  else name=lower_case(name);
+            
+  if (member_array(str, moeglichkeiten)==-1) {
+    if ( !sscanf( str, "%d", i ) ) return falsch();
+    if ( i<1 || i>sizeof(moeglichkeiten) ) return falsch();
+    ergebnis=insert_alist(name,moeglichkeiten[i-1],ergebnis);
+  }
+  else ergebnis=insert_alist(name,str,ergebnis);
+  write("Ok.\n");
+  save_me();
+  say(in_use+" hat ausgewaehlt.\n");
+  in_use=0;
+  return 1;
+}
+
+static int auswertung()
+{
+  int i,j,max,all;
+  mapping stimmen, data;
+  mixed names;
+
+  if (!ARCH_SECURITY || process_call()) return 0;
+
+  rm("/log/WAHL");
+
+  data = mkmapping(ergebnis[0], ergebnis[1]);
+  names = m_indices(data);
+  stimmen = ([]);
+
+  SAY("\n\n\nWahlergebnisse zum Thema: "+thema+"\n");
+  SAY("\nGewaehlt haben folgende Personen: \n");
+  SAY(break_string(CountUp(map(sort_array(names, #'>/*'*/), #'capitalize/*'*/)), 78));
+  SAY("\n");
+
+  while(sizeof(names)) {
+    stimmen[data[names[0]]]++;
+    names[0..0] = ({});
+    all++;
+  }
+
+  SAY("*****************************************\n");
+  SAY("Ergebnis:\n");
+  for (i=0;i<sizeof(moeglichkeiten);i++)
+    SAY(sprintf(" -- %2d.) %20s: %3d (%' '3.2f%%)\n", i+1, moeglichkeiten[i],
+                stimmen[moeglichkeiten[i]], 1.0 * stimmen[moeglichkeiten[i]] * 100.0 / (1.0 * all)));
+  SAY(sprintf("Gesamtstimmen: %d\n", sizeof(m_indices(data))));
+  return 1;
+}
+
+static int wahlthema(string str)
+{
+  if (!ARCH_SECURITY || process_call()) {
+    write("NOT allowed");
+    return 0;
+  }
+  if (!(str=this_interactive()->_unparsed_args())) return 0;
+  thema=str;
+  say(RPL->name(WER)+" setzt ein neues Abstimmungsthema fest.\n");
+  ergebnis=({({}),({})});
+  moeglichkeiten=({});
+  write("Ok.\n");
+  save_me();
+  return 1;
+}
+
+static int moeglichkeit(string str)
+{
+  if (!ARCH_SECURITY || process_call()) {
+    write("NOT allowed");
+    return 0;
+  }
+  if (!(str=RPL->_unparsed_args())) return 0;
+  if (member_array(str, moeglichkeiten) != -1) {
+    write("Schon drin.\n");
+    return 1;
+  }
+  moeglichkeiten+=({str});
+  write("Ok.\n");
+  save_me();
+  return 1;
+}
+
+mixed _internal()
+{
+  if (!ARCH_SECURITY || process_call()) return 0;
+  return ergebnis;
+}
diff --git a/secure/wizlevels.h b/secure/wizlevels.h
new file mode 100644
index 0000000..e785158
--- /dev/null
+++ b/secure/wizlevels.h
@@ -0,0 +1,63 @@
+#ifndef _WIZLEVELS_
+#define _WIZLEVELS_
+/*
+ * wizlevels.h
+ *
+ * This file defines some useful macros to determine the level of
+ * a wizard in terms of ability to do certain wizard things.
+ * See also doc/concepts/levels. The grouping below is taken from there.
+ *
+ */
+#include "/secure/config.h"
+
+#define	SEER_LVL 1
+#define	LEARNER_LVL 10
+#define	WIZARD_LVL 20
+#define DOMAINMEMBER_LVL 25
+#define SPECIAL_LVL 30
+#define	LORD_LVL 40
+#define ELDER_LVL 50
+#define	ARCH_LVL 60
+#define GOD_LVL 100
+
+#define SEER_GRP 1
+#define LEARNER_GRP 2
+#define WIZARD_GRP 3
+#define DOMAINMEMBER_GRP 4
+#define SPECIAL_GRP 5
+#define LORD_GRP 6
+#define ELDER_GRP 7
+#define ARCH_GRP 8
+
+/* user is an object which is to be checked */
+/* for example, use IS_WIZARD(this_player()) to check */
+/* if a player is a wizard. */
+#define IS_SEER(user) (query_wiz_level(user) >= SEER_LVL)
+#define IS_LEARNER(user) (query_wiz_level(user) >= LEARNER_LVL)
+#define IS_SPECIAL(user) (query_wiz_level(user) >= SPECIAL_LVL)
+#define IS_WIZARD(user) (query_wiz_level(user) >= WIZARD_LVL)
+#define IS_DOMAINMEMBER(user) (query_wiz_level(user) >= DOMAINMEMBER_LVL)
+#define IS_DEPUTY(user) (master()->IsDeputy(user))
+#define IS_LORD(user) (query_wiz_level(user) >= LORD_LVL)
+#define IS_ELDER(user) (query_wiz_level(user) >= ELDER_LVL)
+#define IS_ARCH(user) (query_wiz_level(user) >= ARCH_LVL)
+#define IS_GOD(user) (query_wiz_level(user) >= GOD_LVL)
+
+#define IS_LEARNING(user) (query_wiz_level(user) >= LEARNER_LVL &&\
+                           user->QueryProp(P_WANTS_TO_LEARN))
+#define IS_LORD_DOMAIN(user,domain) (master()->domain_master(user,domain))
+
+/*
+ * Interface for enhanced security functions
+ */
+#define SEER_SECURITY (secure_level() >= SEER_LVL)
+#define LEARNER_SECURITY (secure_level() >= LEARNER_LVL)
+#define WIZARD_SECURITY (secure_level() >= WIZARD_LVL)
+#define DOMAINMEMBER_SECURITY (secure_level() >= DOMAINMEMBER_LVL)
+#define SPECIAL_SECURITY (secure_level() >= SPECIAL_LVL)
+#define LORD_SECURITY (secure_level() >= LORD_LVL)
+#define ELDER_SECURITY (secure_level() >= ELDER_LVL)
+#define ARCH_SECURITY (secure_level() >= ARCH_LVL)
+#define GOD_SECURITY (secure_level() >= GOD_LVL)
+
+#endif
diff --git a/secure/zweities.c b/secure/zweities.c
new file mode 100644
index 0000000..89307c2
--- /dev/null
+++ b/secure/zweities.c
@@ -0,0 +1,164 @@
+// MorgenGrauen MUDlib
+/** \file /file.c
+* Kurzbeschreibung.
+* Langbeschreibung...
+* \author <Autor>
+* \date <date>
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types,save_types,rtt_checks
+#pragma no_clone,no_inherit,no_shadow
+#pragma pedantic, range_check
+
+#include <defines.h>
+#include <events.h>
+#include <wizlevels.h>
+#include <player/base.h>
+#include <userinfo.h>
+
+#define HOME(x) (__PATH__(0)+x)
+#define ZDEBUG(x) tell_room("/players/zesstra/workroom",\
+                    sprintf("second: %O\n",x));
+
+protected void create()
+{
+  seteuid(getuid());
+  if (sl_open(HOME("ARCH/second.sqlite")) != 1)
+  {
+    raise_error("Datenbank konnte nicht geoeffnet werden.\n");
+  }
+  // Tabellen und Indices anlegen, falls die nicht existieren.
+  sl_exec("CREATE TABLE IF NOT EXISTS zweities(uuid TEXT PRIMARY KEY ASC, "
+          "name TEXT NOT NULL, erstieuuid TEXT NOT NULL, "
+          "erstie TEXT NOT NULL);");
+  sl_exec("CREATE TABLE IF NOT EXISTS testies(name TEXT PRIMARY KEY ASC, "
+          "magier TEXT NOT NULL, "
+          "lastlogin DATETIME DEFAULT current_timestamp);");
+  sl_exec("CREATE TABLE IF NOT EXISTS familien("
+          "erstieuuid TEXT PRIMARY KEY ASC, familie TEXT NOT NULL);");
+  sl_exec("CREATE INDEX IF NOT EXISTS idx_erstie ON zweities(erstie);");
+  sl_exec("CREATE INDEX IF NOT EXISTS idx_name ON zweities(name);");
+  sl_exec("CREATE INDEX IF NOT EXISTS idx_magiername ON testies(magier);");
+  sl_exec("CREATE INDEX IF NOT EXISTS idx_familie ON familien(familie);");
+
+  // Login-Event abonnieren
+  if (EVENTD->RegisterEvent(EVT_LIB_LOGIN,
+                            "listen", this_object()) <= 0)
+  {
+    raise_error("Loginevent konnte nicht abonniert werden.\n");
+  }
+}
+
+public void listen(string eid, object trigob, mixed data)
+{
+  if (previous_object() != find_object(EVENTD)
+      || !trigob
+      || !query_once_interactive(trigob)
+      || IS_LEARNER(trigob))
+    return;
+  // wenn testie, wird der Char als Testie behandelt und P_SECOND ignoriert.
+  mixed testie=trigob->QueryProp(P_TESTPLAYER);
+  if (stringp(testie)
+      && strstr(testie,"Gilde")==-1)
+  {
+    mixed plinfo = master()->get_userinfo(testie);
+    if (pointerp(plinfo))
+    {
+      sl_exec("INSERT OR REPLACE INTO testies(name, magier, lastlogin) "
+              "VALUES(?1,?2,?3);",
+              trigob->query_real_name(),
+              testie, time());
+      return; // zweitie jetzt auf jeden Fall ignorieren.
+    }
+  }
+
+  mixed erstie=trigob->QueryProp(P_SECOND);
+  if (stringp(erstie))
+  {
+    mixed plinfo = master()->get_userinfo(erstie);
+    if (pointerp(plinfo))
+    {
+      sl_exec("INSERT OR REPLACE INTO zweities(uuid, name, erstieuuid, erstie) "
+              "VALUES(?1,?2,?3,?4);",
+              getuuid(trigob),
+              trigob->query_real_name(),
+              erstie + "_" + plinfo[USER_CREATION_DATE+1],
+              erstie);
+    }
+    //ZDEBUG(sprintf("%O, %O, %O\n",eid,trigob,data));
+  }
+}
+
+varargs int remove(int silent)
+{
+  EVENTD->UnregisterEvent(EVT_LIB_LOGIN, this_object());
+  sl_close();
+  destruct(ME);
+  return 1;
+}
+
+public mixed sql_query(string query)
+{
+  if (ARCH_SECURITY)
+    return sl_exec(query);
+  return 0;
+}
+
+private string get_erstie_data(string datum, object zweitie)
+{
+  if (!zweitie) return 0;
+  mixed res = sl_exec("SELECT " + datum + " FROM zweities WHERE uuid=?1",
+                      getuuid(zweitie));
+  if (sizeof(res))
+    return res[0][0];
+
+  return 0;
+}
+
+public varargs string QueryErstieName(object zweitie)
+{
+  return get_erstie_data("erstie", zweitie || previous_object());
+}
+
+public varargs string QueryErstieUUID(object zweitie)
+{
+  return get_erstie_data("erstieuuid", zweitie || previous_object());
+}
+
+private string* get_zweitie_data(string datum, object erstie)
+{
+  if (!erstie) return 0;
+  mixed tmp = sl_exec("SELECT " + datum + " FROM zweities WHERE erstieuuid=?1",
+                      getuuid(erstie));
+  if (sizeof(tmp))
+  {
+    string *res=({});
+    foreach(string *row: tmp)
+      res+=({row[0]});
+    return res;
+  }
+  return 0;
+}
+
+public varargs string* QueryZweities(object erstie)
+{
+  return get_zweitie_data("name", erstie || previous_object());
+}
+
+public varargs string QueryFamilie(object pl)
+{
+  string erstie = get_erstie_data("erstieuuid",
+                                  pl || previous_object());
+  // Wenn !erstie, dann ist pl kein Zweitie, also ist er selber erstie
+  erstie ||= getuuid(pl);
+  // jetzt noch gucken, ob ne explizite Familie fuer den erstie erfasst ist.
+  mixed tmp = sl_exec("SELECT familie FROM familien WHERE "
+                      "erstieuuid=?1",erstie);
+  if (sizeof(tmp))
+    return tmp[0][0];
+  // wenn nicht, dann ist die Familie die Zweitieuuid 
+  return erstie;
+}
+
diff --git a/std/.readme b/std/.readme
new file mode 100644
index 0000000..089c803
--- /dev/null
+++ b/std/.readme
@@ -0,0 +1,24 @@
+Hier findest du vor allem abstrakte Basisklassen fuer alle Objekte.
+
+armour[.c]      - die Basisklasse fuer Ruestungen
+clothing[.c]    - die Basisklasse fuer (Be)Kleidung
+container[.c]   - container-Basisobjekt
+corpse.c        - die Leiche von Spielern und NPCs
+gilde.c         - Basisklasse fuer die Filialen der ADV-Gilde
+inpc[.c]        - Basisklasse fuer intelligenten NPC [IN ENTWICKLUNG]
+laden.c         - Basisklasse fuer den Laden
+living          - Basisklassen fuer Lebewesen
+npc[.c]         - Basisklasse fuer Non-Player-Characters
+player          - Basis fuer Spielerobjekte, siehe shells
+pub.c           - Basisklasse fuer Kneipen
+room[.c]        - davon leiten sich alle Raeume ab
+shells          - da stehen die Player-Objekte drin
+store.c         - Basisklasse fuer Lagerraeume - passt zu Laden.c
+thing[.c]       - Basisklasse fuer ALLES Objekte
+unit.c          - Klasse fuer Mehrfach-Objekte wie das Geld
+weapon[.c]      - Basisklasse fuer Waffen
+
+Bitte Objekt hier nicht mit 'upd -l', 'xload' o.ae. laden! Zum "Neuladen" nur
+die Blueprint destructen (z.B. per upd), sie werden automatisch bei Bedarf
+neugeladen.
+
diff --git a/std/armour.c b/std/armour.c
new file mode 100644
index 0000000..e0f4b8f
--- /dev/null
+++ b/std/armour.c
@@ -0,0 +1,53 @@
+// MorgenGrauen MUDlib
+//
+// armour.c -- armour standard object
+//
+// $Id: armour.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/thing/properties";
+inherit "/std/thing/commands";
+inherit "/std/thing/restrictions";
+inherit "/std/thing/light";
+inherit "/std/armour/description";
+inherit "/std/clothing/moving";
+inherit "/std/armour/wear";
+inherit "/std/armour/combat";
+inherit "/std/thing/language";
+inherit "/std/thing/envchk";
+
+//#define NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <combat.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+protected void create() {
+  properties::create();
+  commands::create();
+  light::create();
+  restrictions::create();
+  description::create();
+  wear::create();
+  combat::create();
+  envchk::create();
+  AddId("Ding");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Zum Ueberschreiben, damit es nicht buggt, wenn die Leute in ihren Objekten (wie
+// gewuenscht) ::reset() rufen.
+void reset() {
+}
+
diff --git a/std/armour/combat.c b/std/armour/combat.c
new file mode 100644
index 0000000..e688131
--- /dev/null
+++ b/std/armour/combat.c
@@ -0,0 +1,228 @@
+// MorgenGrauen MUDlib
+//
+// armour/combat.c -- armour standard object
+//
+// $Id: combat.c 9092 2015-01-19 23:57:50Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/commands.h>
+#include <thing/description.h>
+#include <config.h>
+#include <combat.h>
+#include <language.h>
+#include <defines.h>
+#include <new_skills.h>
+
+
+// Globale Variablen
+private nosave closure defend_cl;
+private nosave mapping resistance_strengths=([]);
+
+void create() {
+    // Einige Grundwerte setzen
+    Set(P_ARMOUR_TYPE, AT_ILLEGAL, F_VALUE);
+    Set(P_LAST_USE,time(),F_VALUE);
+}
+
+// Die Funktion, die den Schutzwert berechnet, den die Ruestung bietet
+int QueryDefend(string|string* dam_type, int|mapping spell, object enemy)
+{
+    int     prot;
+    mixed   def_func;
+    object  pl;
+    // Zum Cachen von spell[EINFO_DEFEND], haeufiges Lookup aus dem Mapping
+    // koennte unnoetig Zeit kosten.
+    mapping einfo;
+
+    // AT_MISC-Ruetungen schuetzen normalerweise gar nicht...
+    if (QueryProp(P_ARMOUR_TYPE)==AT_MISC) {
+        // es sei denn, sie haben eine spezielle DefendFunc
+        if (!closurep(defend_cl)) return(0);
+    }
+    else {
+        // ansonsten Ruestungsschutz ermitteln und in EINFO_DEFEND vermerken.
+        // (Beides fuer AT_MISC in jedem Fall unnoetig)
+
+        // Ruestungen schuetzen nur gegen physikalischen Schaden
+        if ((!spell || (mappingp(spell) && spell[SP_PHYSICAL_ATTACK]))
+            && sizeof(filter(dam_type,PHYSICAL_DAMAGE_TYPES)))
+        { 
+          // Schutz bestimmen, Minimum 1, aber nur wenn P_AC > 0
+          int pac = QueryProp(P_AC);
+          if (pac > 0)
+            prot = (pac/4 + random(pac*3/4 + 1)) || 1 ;
+          object stat = find_object("/d/erzmagier/zesstra/pacstat");
+          if (stat)
+            stat->acstat(QueryProp(P_ARMOUR_TYPE),prot,
+                         random(pac)+1);
+
+          // ruestungschutz an defendfunc weitermelden
+          if (mappingp(spell) &&
+              mappingp(einfo=spell[EINFO_DEFEND])) {
+              // Schutz d. akt. Ruestung vermerken.
+              einfo[DEFEND_CUR_ARMOUR_PROT]=prot;
+              // daten der Ruestung vermerken.
+              if (mappingp(einfo[DEFEND_ARMOURS])) {
+                einfo[DEFEND_ARMOURS][ME,DEF_ARMOUR_PROT]=prot;
+              }
+          } // ende vom if (mapping(spell) ...)
+        } // ende vom if (phys Schaden)
+
+    } // ende vom else (wenn kein AT_MISC) 
+
+    // Ist eine DefendFunc gesetzt, wird diese ausgewertet
+    if (closurep(defend_cl)) {
+        if (!objectp(get_type_info(defend_cl,2))) {
+            // Closure gesetzt, aber nicht gueltig, schauen, ob wir sie neu
+            // erstellen koennen.
+            if (objectp(def_func=QueryProp(P_DEFEND_FUNC))) {
+                defend_cl=symbol_function("DefendFunc",def_func);
+            }
+            // sonst loeschen, um spaeter diesen Zweig ganz zu sparen.
+            else defend_cl=0;
+        }
+        // BTW: Es ist ok, wenn defend_cl jetzt 0 ist, dann liefert funcall()
+        // auch 0.
+        // Bei Netztoten keine (zurueckschlagende) DefendFunc
+        if (objectp(pl=QueryProp(P_WORN)) && (!query_once_interactive(pl) ||
+                interactive(pl)) ) {
+          // Der Rueckgabewert der DefendFunc wird zum Schutz addiert
+          prot += funcall(defend_cl, dam_type, spell, enemy);
+          // leider kann die DefendFunc jetzt auch noch das Objekt zerstoert
+          // haben...
+          if (!objectp(this_object()))
+            return prot;
+        }
+    }
+
+    // Zeitpunkt der letzten Benutzung ausgeben
+    SetProp(P_LAST_USE,time());
+
+    // Berechneten Schutz zurueckgeben
+    return prot;
+}
+
+// Es duerfen nur "legale" Ruestungstypen gesetzt werden, ansonsten
+// wird AT_ILLEGAL gesetzt.
+static mixed _set_armour_type(mixed type ) {
+    if (!COMBAT_MASTER->valid_armour_type(type))
+    {
+        Set(P_ARMOUR_TYPE, (type=AT_ILLEGAL), F_VALUE);
+    }
+    else
+    {
+        Set(P_ARMOUR_TYPE, type);
+    }
+    AddId(type);
+
+    resistance_strengths=([]);
+    return type;
+}
+
+
+// Wird etwas an P_DEFEND_FUNC geaendert, muss die zugehoerige closure
+// neu erstellt werden.
+static object _set_defend_func(object arg) {
+  if (objectp(arg) &&
+      closurep(defend_cl=symbol_function("DefendFunc",arg))) {  
+    return Set(P_DEFEND_FUNC, arg, F_VALUE);
+  }
+  defend_cl=0;
+  return(Set(P_DEFEND_FUNC, 0, F_VALUE));
+}
+
+// Auch Ruestungen koennen einen Schadenstyp haben. Dieser kann als string
+// oder array angegeben werden, wird aber intern auf jeden Fall als
+// array gespeichert.
+static mixed _set_dam_type(mixed arg) {
+    if (pointerp(arg))
+    {
+        return Set(P_DAM_TYPE, arg, F_VALUE);
+    }
+    else if (stringp(arg))
+    {
+        return Set(P_DAM_TYPE, ({ arg }), F_VALUE);
+    }
+    return Query(P_DAM_TYPE, F_VALUE);
+}
+
+// Ruestungen koennen Resistenzen setzen. Diese werden jedoch nicht wie
+// "normale" Resistenzen gesetzt, sondern als Prozentwerte der fuer diesen
+// Typ maximal zulaesigen Resistenz. Die Aenderung der Resistenzen darf
+// nur durch die Ruestung selbst erfolgen.
+// Beispiel: ([DT_FIRE: 100, DT_WATER: -150])
+// max. Feuerresistenz, 1.5fache Anfaelligkeit
+static mixed _set_resistance_strengths(mixed resmap) {
+    float  max_res;
+    object worn_by;
+
+    // Es duerfen nur mappings angegeben werden
+    if (!mappingp(resmap))
+    {
+        return -1;
+    }
+
+    // die Maxwerte muessen jeweils durch -100 geteilt sein, da hinterher
+    // mit der Prozentzahl multipliziert wird und die angabe der Vorzeichen
+    // hier umgekehrt wie bei den "normalen" Resistenzen ist. Der
+    // Maximalwert ist vom Ruestungstyp abhaengig.
+    switch (QueryProp(P_ARMOUR_TYPE))
+    {
+        case AT_CLOAK  :
+        case AT_RING   :
+        case AT_AMULET : max_res=-0.0010;
+                         break;
+        case AT_SHIELD :
+        case AT_ARMOUR : max_res=-0.0015;
+                         break;
+        default        : max_res=-0.0005;
+    }
+
+    // Resistenz-Mapping aktualisieren
+    resistance_strengths=([]);
+    foreach(string damtype, int res: resmap)
+    {
+        if (!intp(res)) res=to_int(res);
+        // Mehr als 100 Prozent ist nicht erlaubt
+        if (res>100)
+        {
+            res=100;
+        }
+        else if (res<0)
+        {
+             res=(res/4)*5;
+             // Man kann auch nicht beliebig negativ werden
+             if (res<-1000)
+             {
+                 res=-1000;
+             }
+        }
+        // Der Resistenzwert berechnet sich aus dem Produkt des
+        // Maximalwertes und der Prozentzahl
+        resistance_strengths[damtype]=res*max_res;
+    }
+
+    // Werden die Resistenzen bei einer getragenen Ruestung geaendert,
+    // muss der Traeger davon natuerlich beeinflusst werden.
+    if (objectp(worn_by=QueryProp(P_WORN)))
+    {
+        worn_by->AddResistanceModifier(resistance_strengths,
+                                       QueryProp(P_ARMOUR_TYPE));
+    }
+   return resistance_strengths;
+}
+
+// Bei einem QueryProp(P_RESISTANCE_STRENGTHS) soll das aktuelle
+// Resistenzen-Mapping zurueckgegeben werden
+static mapping _query_resistance_strengths() {
+  return (resistance_strengths||([]));
+}
+
diff --git a/std/armour/container_description.c b/std/armour/container_description.c
new file mode 100644
index 0000000..b1e73ad
--- /dev/null
+++ b/std/armour/container_description.c
@@ -0,0 +1,67 @@
+// MorgenGrauen MUDlib
+//
+// armour/container_description.c -- armour_container description handling
+//
+// $Id: container_description.c 6306 2007-05-20 11:32:03Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/clothing/container_description";
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <thing/language.h>
+#include <container.h>
+#include <combat.h>
+#include <thing/material.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <player/base.h>
+
+string dam_descr() {   
+    string re;
+    mixed desc;
+    int maximum,dam,pos;
+
+    if (!QueryProp(P_NAME) || !QueryProp(P_DAMAGED) || !QueryProp(P_SHORT) ||
+        !(desc=QueryProp(P_DAM_DESC)) || (!stringp(desc) && !pointerp(desc)))
+        return "";
+    re = capitalize(name(WER,2))+" ";
+    maximum = QueryProp(P_AC)+(dam=QueryProp(P_DAMAGED));
+    if (stringp(desc))
+        return (dam>(maximum/2))?(re+desc+".\n"):"";
+    pos = (sizeof(desc)*dam/maximum);
+    if (stringp(desc[pos]))
+        return (re+desc[pos]+".\n");
+    return "";
+}
+
+mapping _query_material() {
+  mixed res,at;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  at=QueryProp(P_ARMOUR_TYPE);
+  switch(at) {
+    case AT_ARMOUR:
+    case AT_HELMET:
+    case AT_RING:
+    case AT_AMULET:
+    case AT_SHIELD:
+    return ([MAT_MISC_METAL:100]);
+    case AT_CLOAK:
+    case AT_TROUSERS:
+    return ([MAT_CLOTH:100]);
+    case AT_GLOVE:
+    case AT_BOOT:
+    return ([MAT_LEATHER:100]);
+  }
+  return ([MAT_LEATHER:100]);
+}
+
diff --git a/std/armour/description.c b/std/armour/description.c
new file mode 100644
index 0000000..2e26294
--- /dev/null
+++ b/std/armour/description.c
@@ -0,0 +1,62 @@
+// MorgenGrauen MUDlib
+//
+// armour/description.c -- armour description handling
+//
+// $Id: description.c 6306 2007-05-20 11:32:03Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/clothing/description";
+
+//#define NEED_PROTOTYPES
+
+#include <properties.h>
+
+string dam_descr()
+{   string re;
+    mixed desc;
+    int maximum,dam,pos;
+
+    if (!QueryProp(P_NAME) || !QueryProp(P_DAMAGED) || !QueryProp(P_SHORT) ||
+        !(desc=QueryProp(P_DAM_DESC)) || (!stringp(desc) && !pointerp(desc)))
+        return "";
+    re = capitalize(name(WER,2))+" ";
+    maximum = QueryProp(P_AC)+(dam=QueryProp(P_DAMAGED));
+    if (stringp(desc))
+        return (dam>(maximum/2))?(re+desc+".\n"):"";
+    if (maximum==dam)
+        pos=sizeof(desc)-1;
+    else
+        pos = (sizeof(desc)*dam/maximum);
+    if (stringp(desc[pos]))
+        return (re+desc[pos]+".\n");
+    return "";
+}
+
+
+mapping _query_material() {
+  mixed res,at;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  at=QueryProp(P_ARMOUR_TYPE);
+  switch(at) {
+    case AT_ARMOUR:
+    case AT_HELMET:
+    case AT_RING:
+    case AT_AMULET:
+    case AT_SHIELD:
+    return ([MAT_MISC_METAL:100]);
+    case AT_CLOAK:
+    case AT_TROUSERS:
+    return ([MAT_CLOTH:100]);
+    case AT_GLOVE:
+    case AT_BOOT:
+    return ([MAT_LEATHER:100]);
+  }
+  return ([MAT_LEATHER:100]);
+}
diff --git a/std/armour/moving.c b/std/armour/moving.c
new file mode 100644
index 0000000..de598a8
--- /dev/null
+++ b/std/armour/moving.c
@@ -0,0 +1,18 @@
+// MorgenGrauen MUDlib
+//
+// armour/moving.c -- armour moving object
+//
+// $Id: moving.c 6306 2007-05-20 11:32:03Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+// Dies muss eigentlich nix ueber clothing/moving hinausgehendes koennen.
+// Die Ruestungen erben auch alle direkt clothing/moving, dieses File
+// existiert eigentlich nur fuer den Fall, dass Leute ausserhalb von /std von
+// armour/moving erben.
+inherit "/std/clothing/moving";
+
diff --git a/std/armour/wear.c b/std/armour/wear.c
new file mode 100644
index 0000000..fef87c5
--- /dev/null
+++ b/std/armour/wear.c
@@ -0,0 +1,260 @@
+// MorgenGrauen MUDlib
+//
+// armour/wear.c -- armour standard object
+//
+// $Id: combat.c 6243 2007-03-15 21:10:21Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/clothing/wear";
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <living/combat.h>
+#include <living/attributes.h>
+#include <language.h>
+#include <defines.h>
+#include <armour.h>
+
+// Uebernimmt viele wesentliche Eigenschaften aus der Kleidung, daher werden
+// hier auch nur div. Funktionen ueberschrieben, die sich leicht
+// unterschiedlich zur Kleidung verhalten.
+
+// Globale Variablen
+private nosave int     logged;
+
+protected void create() {
+  ::create();
+  // P_DAMAGED laesst sich zwar (noch) von aussen setzen, aber bitte nur ueber
+  // die hier definierte Setmethode.
+  // TODO: Direktes Setzen von P_DAMAGED entfernen.
+  Set(P_DAMAGED, PROTECTED, F_MODE_AS);
+}
+
+/* Wenn eine Ruestung vom gleichen Typ im Array ist, gebe diese zurueck. */
+private object TestType(object *armours) {
+    mixed type;
+
+    // Den eigenen Typ feststellen
+    type = QueryProp(P_ARMOUR_TYPE);
+
+    // Zerstoerte Objekte aussortieren
+    armours-=({0});
+
+    foreach(object armour: armours)
+    {
+      if (type==(armour->QueryProp(P_ARMOUR_TYPE)))
+        {
+            // Ruestung vom gleichen Typ gefunden -> zurueckgeben
+            return armour;
+        }
+    }
+    // Keine Ruestung vom gleichen Typ gefunden, also 0 zurueckgeben
+    return 0;
+}
+
+// liefert 1 zurueck, wenn der Spieler die ruestung anziehen darf, 0 sonst.
+protected int _check_wear_restrictions(int silent, int all) {
+  mixed   type,res;
+  object  *armours;
+
+  type = QueryProp(P_ARMOUR_TYPE);
+
+  // Das DoWear() der Kleidung prueft auf genuegend freie Haende,
+  // prueft aber den Fall nicht ab, ob man hier ein Schild hat, was laut Prop
+  // weniger als eine Hand belegt. Daher hier nachholen und korrigieren. Und
+  // direkt mal loggen, damit das gefixt wird.
+  if ( (type==AT_SHIELD) && QueryProp(P_NR_HANDS)<1 ) {
+    SetProp(P_NR_HANDS,1);
+    log_file("INVALID_SHIELD",
+	sprintf("Fixme: AT_SHIELD item without valid P_NR_HANDS: %O\n",
+	  object_name()));
+  }
+
+  armours=(object*)PL->QueryProp(P_ARMOURS) - ({0});
+
+  // Von jedem Ruestungstyp ausser AT_MISC kann man immer nur ein
+  // Teil tragen
+  if ( (type!=AT_MISC) && (res=TestType(armours)) && objectp(res)) {
+    msg(break_string(sprintf(
+	  "Du traegst bereits %s als Schutz der Sorte %s.",
+	  res->name(WEN,1), type),78,
+	  (all?(Name(WER)+": "):0)), all);
+    return(-1);
+  }
+
+  // Ruestungen vom Typ AT_ILLEGAL oder solche mit einem fuer ihren
+  // Ruestungstyp zu hohen Schutzwert koennen nicht angezogen werden
+  if ( (type==AT_ILLEGAL) || (QueryProp(P_AC)>VALID_ARMOUR_CLASS[type])) {
+    write("Ungueltiger Ruestungstyp, bitte Erzmagier verstaendigen.\n");
+        "/p/daemon/ruestungen"->RegisterArmour();      
+    return(-2);
+  }
+
+  // Ruestungen, die ein oder mehrere Attribut veraendern und gegen
+  // das gesetzte Limit verstossen, haben keine Wirkung bezueglich der
+  // Attribute. Dies gibt aber nur ne Meldung aus, angezogen werden darf sie
+  // trotzdem.
+  if (mappingp(res=QueryProp(P_M_ATTR_MOD)) && PL->TestLimitViolation(res) ) {
+    write(break_string(sprintf(
+          "Irgendetwas an Deiner Ausruestung verhindert, dass Du Dich mit "
+          "%s so richtig wohl fuehlst.",name(WEM,1)),78,
+          (all?(Name(WER)+": "):0)));
+  }
+
+  // dann mal aus der Kleidung geerbte Pruefungen ausfuehren und Ergebnis
+  // liefern.
+  return ::_check_wear_restrictions(silent,all);
+}
+
+protected void _informwear(int silent, int all) {
+
+  // Ruestungen koennen Resistenzen beeinflussen
+  PL->AddResistanceModifier(QueryProp(P_RESISTANCE_STRENGTHS),
+                            QueryProp(P_ARMOUR_TYPE));
+
+  // Ruestungen koennen Attribute aendern/blockieren. Also muessen diese
+  // nach dem Anziehen aktualisiert werden
+  PL->register_modifier(ME);
+  PL->UpdateAttributes();
+
+  // P_TOTAL_AC im Traeger updaten (fuer Query()s)
+  PL->QueryProp(P_TOTAL_AC);
+
+  // Alle Ruestungen werden im awmaster registriert, sobald sie von
+  // einem Spieler gezueckt werden
+  if (!logged && query_once_interactive(PL)) {
+    call_other("/secure/awmaster","RegisterArmour",ME);
+    logged=1;
+  }
+  // noch das aus der Kleidung rufen, damit die Anziehmeldungen auch kommen.
+  // ausserdem laeuft das Anstosses von InformWear() von dort.
+  ::_informwear(silent,all);
+}
+
+
+protected int _check_unwear_restrictions(object worn_by, int silent, 
+                                            int all) {
+  // liefert >=0 zureck, wenn die Kleidung/Ruestung ausgezogen werden kann, 
+  // <0 sonst.
+
+  // Schilde belegen (mindestens) eine Hand. Hngl. Wenn man diesen bloeden
+  // Check nicht machen muesste, koennte man sich die ganze Funktion sparen.
+  // Hmpfgrmpfl. Achja, raise_error(), weil das eigentlich ja nicht vorkommen
+  // sollte und gefixt werden soll. Das geht naemlich nur, wenn jemand diese
+  // Prop geaendert, waehrend der Spieler das Schild getragen hatte.
+  if ( (QueryProp(P_ARMOUR_TYPE)==AT_SHIELD) && 
+      QueryProp(P_NR_HANDS)<1 ) {
+    raise_error(sprintf("Fixme: AT_SHIELD beim Ausziehen ohne P_NR_HANDS: %O",
+	  object_name()));
+  }
+  
+  // Ausziehcheck der Kleidung machen
+  return ::_check_unwear_restrictions(worn_by,silent,all);
+}
+
+protected void _informunwear(object worn_by, int silent, int all) {
+  mixed res;
+  // Gesetzte Resistenzen loeschen
+  worn_by->RemoveResistanceModifier(res=QueryProp(P_ARMOUR_TYPE));
+  
+  // Ruestungen koennen Attribute aendern/blockieren. Also muessen diese
+  // nach dem Ausziehen aktualisiert werden
+  worn_by->deregister_modifiers(ME);
+  worn_by->UpdateAttributes();
+
+  // P_TOTAL_AC im Traeger updaten
+  worn_by->QueryProp(P_TOTAL_AC);
+
+  // die geerbte Funktion aus der Kleindung gibt noch meldungen aus und ruft
+  // Informunwear().
+  ::_informunwear(worn_by,silent,all);
+}
+
+// Funktion, die das "trage"/"ziehe * an"-Kommando auswertet
+varargs int do_wear(string str, int silent) {  
+  int all;  
+
+  // Hat der Spieler "trage alles" eingegeben?
+  all=(str=="alles" || str=="alle ruestungen");
+  
+  return(_do_wear(str,silent,all));
+}
+
+// Die Funktion, die das "ziehe * aus"-Kommando auswertet
+varargs int do_unwear(string str, int silent) {
+  int all;
+
+  all=(str=="alles" || str=="alle ruestungen");
+  return(_do_unwear(str,silent,all));
+}
+
+
+// Objekte, die die Beschaedigung einer Ruestung durch direktes Setzen von
+// P_DAMAGED durchfuehren, werden im awmaster geloggt
+static mixed _set_item_damaged(mixed arg) {
+    if (arg && !intp(arg))
+    {
+        return Query(P_DAMAGED, F_VALUE);
+    }
+
+    if (previous_object(1))
+      call_other("/secure/awmaster","RegisterDamager",
+            previous_object(1),QueryProp(P_DAMAGED),arg); 
+
+    return Set(P_DAMAGED, arg, F_VALUE);
+}
+
+// Will man eine Ruestung beschaedigen oder reparieren, so macht man das
+// am besten ueber die Funktion Damage(argument). Positive Argumente
+// bedeuten eine Beschaedigung, negative eine Reparatur. Der Rueckgabewert
+// ist die wirklich durchgefuehrte Aenderung des Beschaedigungswertes
+int Damage(int new_dam) {   
+  int    ac,maximum,old_dam;
+  object w;
+
+  if (!new_dam || !intp(new_dam)) {      
+    return 0;
+  }
+
+  if ( (ac=QueryProp(P_AC))<=0 && (new_dam>0) ) {
+    // Sonst wuerde Beschaedigung zur Reparatur fuehren
+    return 0;
+  }
+
+  // Min-AC und MAX_AC beachten
+  if ((ac-new_dam) < MIN_ARMOUR_CLASS) {      
+    new_dam = ac-MIN_ARMOUR_CLASS;
+  }
+  else if ((ac-new_dam) >
+    (maximum=VALID_ARMOUR_CLASS[QueryProp(P_ARMOUR_TYPE)])) {      
+    new_dam = ac-maximum;
+  }
+
+  // Nie mehr als 100% reparieren
+  if ( ((old_dam=QueryProp(P_DAMAGED))<-new_dam) && (new_dam<0) ) {
+    new_dam=-old_dam;
+  }
+
+  // Aenderungen an der Ruestungsklasse und dem Beschaedigungswert
+  // durchfuehren
+  SetProp(P_AC,(ac-new_dam));
+  // eigene Set-Methode umgehen
+  Set(P_DAMAGED,(old_dam+new_dam),F_VALUE);
+
+  // P_TOTAL_AC im Traeger updaten, wenn vorhanden
+  if (objectp(w=QueryProp(P_WORN)))
+    w->QueryProp(P_TOTAL_AC);
+
+  // Rueckgabewert: Durchgefuehrte Aenderung an P_DAMAGE
+  return new_dam;
+}
+
+public status IsArmour() {return 1;}
+public status IsClothing() {return 0;}
+
diff --git a/std/armourHG.c b/std/armourHG.c
new file mode 100644
index 0000000..352e75c
--- /dev/null
+++ b/std/armourHG.c
@@ -0,0 +1,59 @@
+// MorgenGrauen MUDlib
+//
+// armourHG.c -- armour standard object for hats and glasses
+//
+// $Id: armourHG.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/armour";
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#undef NEED_PROTOTYPES
+
+#include <language.h>
+#include <defines.h>
+
+int setzen(string s);
+
+void create() {
+    ::create();
+    AddCmd(({"setz","setze"}),"setzen");
+}
+
+varargs void doWearMessage( int all ) {
+  if( query_once_interactive(PL) ) {
+    write( "Du setzt " + name(WEN,1) + " auf.\n" );
+  }
+  if (objectp(environment()) && objectp(environment(environment())))
+    tell_room(environment(environment()),
+	capitalize((string)PL->name(WER)) + " setzt " + name(WEN,0) + 
+	" auf.\n");
+}
+
+varargs void doUnwearMessage( object worn_by, int all ) {
+  if( query_once_interactive(worn_by) ) {
+    tell_object(worn_by,  "Du setzt " + name(WEN,1) + " ab.\n" );
+  }
+  tell_room(environment(worn_by), 
+	    (capitalize((string)worn_by->name(WER))) + " setzt " +
+	    name(WEN,0) + " ab.\n", ({worn_by}));
+}
+
+int setzen(string str) {
+  string ob;
+
+  if(!str)
+    return 0;
+  if(sscanf(str, "%s auf", ob)==1 )
+    return _do_wear(ob, 0, 0);
+  if(sscanf(str, "%s ab", ob)==1 )
+    return _do_unwear(ob, 0, 0);
+  return 0;
+}
+
diff --git a/std/armour_container.c b/std/armour_container.c
new file mode 100644
index 0000000..7ea22d2
--- /dev/null
+++ b/std/armour_container.c
@@ -0,0 +1,54 @@
+// MorgenGrauen MUDlib
+//,
+// armour_container.c -- Ruestung, in die man was reinstecken kann
+//
+// $Id: armour_container.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/properties";
+inherit "/std/thing/commands";
+inherit "/std/thing/language";
+inherit "/std/thing/envchk";
+inherit "/std/container/light";
+inherit "/std/container/restrictions";
+inherit "/std/container/inventory";
+inherit "/std/container/items";
+inherit "/std/clothing/moving";
+inherit "/std/armour/wear";
+inherit "/std/armour/combat";
+inherit "/std/armour/container_description";
+
+//#define NEED_PROTOTYPES
+
+#include <container.h>
+
+protected void create() {
+  properties::create();
+  commands::create();
+  light::create();
+  container_description::create();
+  restrictions::create();
+  items::create();
+  wear::create();
+  combat::create();
+  envchk::create();
+  SetProp(P_CONTAINER,1);
+  SetProp(P_PREPOSITION, "in");
+  SetProp(P_SOURCE_PREPOSITION, "aus");
+  SetProp(P_DEST_PREPOSITION, "in");
+  AddId("Ding");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset()  {
+  items::reset();
+}
+
diff --git a/std/clothing.c b/std/clothing.c
new file mode 100644
index 0000000..8d25984
--- /dev/null
+++ b/std/clothing.c
@@ -0,0 +1,54 @@
+// MorgenGrauen MUDlib
+//
+// armour.c -- armour standard object
+//
+// $Id: armour.c,v 3.8 2003/08/25 09:36:04 Rikus Exp $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/thing/properties";
+inherit "/std/thing/commands";
+inherit "/std/thing/restrictions";
+inherit "/std/thing/light";
+inherit "/std/clothing/description";
+inherit "/std/clothing/moving";
+inherit "/std/clothing/wear";
+inherit "/std/thing/language";
+inherit "/std/thing/envchk";
+
+//#define NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <combat.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+protected void create()
+{
+  properties::create();
+  commands::create();
+  light::create();
+  restrictions::create();
+  description::create();
+  wear::create();
+  envchk::create();
+  AddId("Ding");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// zum Ueberschreiben, damit es nicht buggt, wenn Leute (wie gewuenscht) in 
+// ihren Objekten ::reset() aufrufen.
+void reset() {
+}
+
+int IsClothing() {return 1;}
+
diff --git a/std/clothing/container_description.c b/std/clothing/container_description.c
new file mode 100644
index 0000000..f7942bc
--- /dev/null
+++ b/std/clothing/container_description.c
@@ -0,0 +1,137 @@
+// MorgenGrauen MUDlib
+//
+// clothing/container_description.c -- clothing_container description handling
+//
+// $Id: container_description.c 6198 2007-02-13 23:39:43Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/description";
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <thing/language.h>
+#include <container.h>
+#include <combat.h>
+#include <thing/material.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <player/base.h>
+
+void create()
+{
+  ::create();
+  SetProp(P_TRANSPARENT, 1);
+  AddId("Container");
+}
+
+string dam_descr()
+{   string re;
+    mixed desc;
+    int maximum,dam,pos;
+
+    if (!QueryProp(P_NAME) || !QueryProp(P_DAMAGED) || !QueryProp(P_SHORT) ||
+        !(desc=QueryProp(P_DAM_DESC)) || (!stringp(desc) && !pointerp(desc)))
+        return "";
+    re = capitalize(name(WER,2))+" ";
+    maximum = QueryProp(P_AC)+(dam=QueryProp(P_DAMAGED));
+    if (stringp(desc))
+        return (dam>(maximum/2))?(re+desc+".\n"):"";
+    pos = (sizeof(desc)*dam/maximum);
+    if (stringp(desc[pos]))
+        return (re+desc[pos]+".\n");
+    return "";
+}
+
+string short()
+{   string s;
+    if(!(s=process_string(QueryProp(P_SHORT))))
+        return 0;
+    return s+(QueryProp(P_WORN)?" (angezogen).\n":".\n");
+}
+
+varargs string long(int mode)
+{   string descr, inv_descr;
+
+    descr=(process_string(QueryProp(P_LONG)||"") + (dam_descr()||""));
+    if (!QueryProp(P_TRANSPARENT))
+      return descr;
+
+    inv_descr = make_invlist(PL, all_inventory(ME), mode );
+    if ( inv_descr != "" )
+        descr += capitalize(QueryPronoun(WER)) + " enthaelt:\n" + inv_descr;
+    return descr;
+}
+
+mapping _query_material() {
+  mixed res,at;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  at=QueryProp(P_ARMOUR_TYPE);
+  switch(at) {
+    case AT_ARMOUR:
+    case AT_HELMET:
+    case AT_RING:
+    case AT_AMULET:
+    case AT_SHIELD:
+    return ([MAT_MISC_METAL:100]);
+    case AT_CLOAK:
+    case AT_TROUSERS:
+    return ([MAT_CLOTH:100]);
+    case AT_GLOVE:
+    case AT_BOOT:
+    return ([MAT_CLOTH:100]);
+  }
+  return ([MAT_CLOTH:100]);
+}
+
+// flags: 1 - wizard, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+private void stringenize(mixed obj, int flags, mixed objs, mixed info)
+{
+  string id, tmp;
+  int idx;
+  tmp = capitalize(obj->short()||"")[0..<2]
+      + (!(flags & 4) && (flags & 1) ? " ["+object_name(obj)+"]" : "");
+  if(flags & 3 || living(obj)) id = object_name(obj);
+  else
+    id = explode(object_name(obj), "#")[0] + tmp;
+  if((idx = member(objs, id)) == -1)
+  {
+    objs += ({ id });
+    info += ({ ({ tmp, 1, obj}) });
+  }
+  else
+    info[idx][1]++;
+}
+
+private string collect(mixed obj)
+{
+  if(!sizeof(obj[0])) return 0;
+  return obj[0] + (obj[1] > 1 ? " ("+obj[1]+")" : "");
+}
+
+// flags: 1 - return array, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+varargs mixed make_invlist(object viewer, mixed inv, int flags)
+{
+  int iswiz;
+  mixed objs, info;
+  string descr;
+
+  iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
+  descr = ""; objs = ({}); info = ({});
+  map(inv, #'stringenize/*'*/, iswiz | (flags & 2) | (flags & 4), &objs, &info);
+  if(flags & 1) return info;
+  inv = map(info, #'collect/*'*/) - ({ 0 });
+  if(!sizeof(inv)) return "";
+  return sprintf("%"+(sizeof(inv) > 6 ? "#" : "=")+"-78s",
+                 implode(inv, "\n")) + "\n";
+}
diff --git a/std/clothing/description.c b/std/clothing/description.c
new file mode 100644
index 0000000..fa226fd
--- /dev/null
+++ b/std/clothing/description.c
@@ -0,0 +1,58 @@
+// MorgenGrauen MUDlib
+//
+// armour/description.c -- armour description handling
+//
+// $Id: description.c,v 1.7 2002/08/19 14:21:04 Rikus Exp $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+#pragma warn_empty_casts
+
+inherit "/std/thing/description";
+
+//#define NEED_PROTOTYPES
+
+#include <properties.h>
+
+string dam_descr() {
+  return "";
+}
+
+string short()
+{   string s;
+    if(!(s=process_string(QueryProp(P_SHORT))))
+        return 0;
+    return s+(QueryProp(P_WORN)?" (angezogen).\n":".\n");
+}
+
+varargs string long()
+{   
+    return (process_string(QueryProp(P_LONG)||"") + (dam_descr()||""));
+}
+
+mapping _query_material() {
+  mixed res,at;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+   return res;
+  at=QueryProp(P_ARMOUR_TYPE);
+  switch(at) {
+    case AT_ARMOUR:
+    case AT_HELMET:
+    case AT_RING:
+    case AT_AMULET:
+    case AT_SHIELD:
+    return ([MAT_MISC_METAL:100]);
+    case AT_CLOAK:
+    case AT_TROUSERS:
+    return ([MAT_CLOTH:100]);
+    case AT_GLOVE:
+    case AT_BOOT:
+    return ([MAT_LEATHER:100]);
+  }
+  return ([MAT_CLOTH:100]);
+}
+
diff --git a/std/clothing/moving.c b/std/clothing/moving.c
new file mode 100644
index 0000000..ba2e05f
--- /dev/null
+++ b/std/clothing/moving.c
@@ -0,0 +1,46 @@
+// MorgenGrauen MUDlib
+//
+// armour/moving.c -- armour moving object
+//
+// $Id: moving.c,v 3.3 1998/03/02 08:34:57 Paracelsus Exp $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/moving";
+
+#define NEED_PROTOTYPES 1
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+#include <clothing.h>
+
+/* Bekleidung muss vor Bewegung und Zerstoerung ausgezogen werden */
+
+varargs int move(mixed dest, int method ) {
+  // ggf. Ausziehen
+  if (objectp(QueryProp(P_WORN)))
+    DoUnwear(method & (M_SILENT|M_NOCHECK));
+
+  if ((method&M_NOCHECK) || (!(object)QueryProp(P_WORN)))
+    return ::move(dest, method);
+
+  return ME_CANT_BE_DROPPED;
+}
+
+varargs int remove(int silent) {
+  // ggf. Ausziehen
+  if (objectp(QueryProp(P_WORN)))
+    DoUnwear(M_SILENT|M_NOCHECK);
+
+  if (!(object)QueryProp(P_WORN))
+    return ::remove(silent);
+  // Ausziehen hat irgendwie nicht geklappt. :-(
+  return 0;
+}
+
diff --git a/std/clothing/wear.c b/std/clothing/wear.c
new file mode 100644
index 0000000..dcde4bd
--- /dev/null
+++ b/std/clothing/wear.c
@@ -0,0 +1,594 @@
+// MorgenGrauen MUDlib
+//
+// clothing/wear.c -- Funktionen rund ums Anziehen/Tragen von Kleidung.
+//
+// $Id: combat.c 6243 2007-03-15 21:10:21Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/commands.h>
+#include <thing/description.h>
+#include <living/clothing.h>
+#include <clothing.h>
+#include <living/combat.h>
+#include <language.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <moving.h>
+
+// Globale Variablen
+nosave int  flaw, ftime;
+
+void create()
+{
+  // Einige Properties sollten nicht von aussen gesetzt werden koennen
+  Set(P_WORN, PROTECTED, F_MODE);
+  Set(P_LAST_USE, PROTECTED, F_MODE_AS);
+  Set(P_RESTRICTIONS,([]),F_VALUE);
+
+  // Bekleidung benoetigt Kommandos, mit denen man sie an- und
+  // ausziehen kann
+  AddCmd( ({"zieh","ziehe"}),"ziehe" );
+  AddCmd( ({"trag","trage"}),"do_wear" );
+}
+
+// aktuelles Lebewesen, was diese Kleidung (oder auch Ruestung) zur Zeit
+// traegt.
+public object QueryUser()
+{
+  return QueryProp(P_WORN);
+}
+
+
+// Ausgabe von Meldungen ueber write() oder _notify_fail(), je nachdem, ob der
+// Spieler alles anzieht oder was bestimmtes.
+protected void msg(string str, mixed fl) {
+  if (!stringp(str)) {
+    return;
+  }
+  if (fl) {
+    write(str);
+  }
+  else {
+    _notify_fail(str);
+  }
+}
+
+/*
+ * Ausgabe einer Meldung beim Anziehen geht nur an Spieler, nicht an NPC.
+ * Die Umgebung bekommt immer eine Meldung.
+ */
+varargs void doWearMessage(int all) {
+  string *str,s1;
+  mixed wearmsg;
+
+  if(wearmsg=QueryProp(P_WEAR_MSG)) { // Ist eine WearMsg gesetzt?
+    if(closurep(wearmsg)) { // Evtl. gar als extra Fkt.?
+
+      str = funcall(wearmsg, PL);
+      if(interactive(PL)) {
+         // Im Falle, dass all gesetzt ist, wird als Indent der Name des
+         // angezogenen Objektes gesetzt. (trag alles)
+         write(break_string(str[0],78,
+                (all?(Name(WER)+": "):0), BS_LEAVE_MY_LFS));
+      }
+      //(Zesstra): say() ist hier bloed, weil es an das Env vom this_player()
+      //ausgibt, sofern der existiert. So koennen Spieler die meldung kriegen,
+      //obwohl die laengst woanders sind (z.B. Sequenzen)
+      //Daher nun Ausgabe an das Env vom Env (wenn das kein Raum sein sollte,
+      //was durchaus sein koennte, macht tell_room() nix). 
+      if ( objectp(environment()) && objectp(environment(environment())) )
+          tell_room(environment(environment()),
+            break_string(str[1], 78, 0, BS_LEAVE_MY_LFS),({PL}) );
+
+      return;
+    }
+    else if(interactive(PL)) {
+      s1 = replace_personal(sprintf(wearmsg[0],"@WEN2"), ({PL,ME}), 1);
+
+      write(break_string(s1,78,(all?(Name(WER)+": "):0), BS_LEAVE_MY_LFS));
+    }
+
+    s1 = replace_personal(sprintf(wearmsg[1],"@WER1","@WENU2"),
+                            ({PL, ME}), 1);
+ 
+    if ( objectp(environment()) && objectp(environment(environment())) )
+        tell_room(environment(environment()),
+            break_string(s1, 78, 0, BS_LEAVE_MY_LFS),({ PL }) );
+
+    return;
+  }
+  /*
+   * Keine WearMsg gesetzt. Ausgabe der Default-Meldungen.
+   */
+  else if(interactive(PL)) {
+    write(break_string("Du ziehst " + name(WEN,1) + " an.",78,
+     (all?(Name(WER)+": "):0)));
+  }
+  if ( objectp(environment()) && objectp(environment(environment())) )
+      tell_room(environment(environment()),break_string(PL->Name(WER)
+            + " zieht " + name(WEN,0) +" an.",78), ({PL}));
+}
+
+/*
+ * Ausgabe einer Meldung beim Ausziehen geht nur an Spieler, nicht an NPC.
+ * Die Umgebung bekommt natuerlich immer eine Meldung.
+ */
+void doUnwearMessage(object worn_by, int all)
+{
+  string *str,s1;
+  mixed msg;
+
+  if(!objectp(worn_by)) { // Na huch, gar nicht angezogen? Abbruch.
+    return;
+  }
+
+  if(msg=QueryProp(P_UNWEAR_MSG)) { // Ist eine UnwearMsg gesetzt?
+    if(closurep(msg)) { // Oho! Gar gleich als Fkt.?
+
+      str = funcall(msg, worn_by);
+      if(interactive(worn_by)) {
+         tell_object(worn_by,break_string(str[0], 78,
+                                 (all?(Name(WER)+": "):0),BS_LEAVE_MY_LFS));
+      }
+
+      //(Zesstra): say() ist hier bloed, weil es an das Env vom this_player()
+      //ausgibt, sofern der existiert. So koennen Spieler die meldung kriegen,
+      //obwohl die laengst woanders sind (z.B. Sequenzen)
+      //Daher nun Ausgabe an das Env vom worn_by (wenn das kein Raum sein sollte,
+      //macht tell_room() nix). 
+      if ( objectp(environment(worn_by)) )
+          tell_room(environment(worn_by),
+              break_string(str[1],78, 0, BS_LEAVE_MY_LFS),({ worn_by }) );
+      
+      return;
+    }
+    else if(interactive(worn_by)) {
+      s1 = replace_personal(sprintf(msg[0],"@WEN2"),
+                      ({worn_by,ME}), 1);
+
+      tell_object(worn_by,break_string(s1,78,
+                              (all?(Name(WER)+": "):0), BS_LEAVE_MY_LFS));
+    }
+
+    s1 = replace_personal(sprintf(msg[1],"@WER1","@WENU2"),
+                    ({worn_by, ME }), 1);
+
+    if ( objectp(environment(worn_by)) )
+        tell_room(environment(environment()),
+            break_string(s1,78, 0, BS_LEAVE_MY_LFS),({ worn_by }) );
+    return;
+  }
+  /*
+   * Keine UnwearMsg gesetzt. Ausgabe der Default-Meldungen.
+   */
+  else if(interactive(worn_by)) {
+    tell_object(worn_by,break_string("Du ziehst " + name(WEN,1) + " aus.",78,
+     (all?(Name(WER)+": "):0)));
+  }
+  if ( objectp(environment(worn_by)) )
+      tell_room(environment(worn_by), break_string(worn_by->Name(WER)
+            + " zieht " + name(WEN,0) + " aus.",78), ({ worn_by }) );
+}
+
+// Diese Funktion wird aufgerufen, wenn die Ruestung wirklich angezogen
+// wird
+protected void InformWear(object pl, int silent, int all) {
+    return;
+}
+
+// Diese Funktion wird aufgerufen, wenn die Ruestung wirklich ausgezogen
+// wird
+protected void InformUnwear(object pl, int silent, int all) {
+    return;
+}
+
+// liefert Werte <0 zurueck, wenn der Spieler die Kleidung _nicht_ 
+// anziehen darf.
+// Hierbei steht -1 dafuer, dass der Aufrufer return 0 machen sollte,
+// <= -2 sollte zur einem return !all fuehren.
+protected int _check_wear_restrictions(int silent, int all) {
+  mixed   type,res;
+  object  *armours;
+
+  // Man kann nur Kram anziehen, die man bei sich traegt
+  if (environment()!=PL) {            
+    msg(break_string("Du musst "+name(WEN,1)+" erst nehmen!",78,
+            (all?(Name(WER)+": "):0)), all);
+    return(-1);
+  }
+
+  // Eine getragene Ruestung kann man nicht nochmal anziehen
+  if (QueryProp(P_WORN)) {
+    msg(break_string("Du traegst "+name(WEN,1)+" bereits.",78,
+          (all?(Name(WER)+": "):0)), all);
+    return(-1);
+  }
+
+  // Diese Funktion versucht immer, TP anzuziehen (*args*). Es gibt aber viele
+  // Magier, die ohne TP oder mit dem falschen TP anziehen wollen. Daher mal
+  // pruefen und ggf. Fehler ausloesen.
+  if (!this_player())
+      raise_error("Kein this_player() existent beim Anziehen!\n");
+  else if (this_player() != environment())
+      raise_error("Meine Umgebung beim Anziehen ist nicht this_player()!\n");
+
+  // Ueber P_RESTRICTIONS kann man einige Restriktionen definieren, ohne
+  // gleich auf eine WearFunc zurueckgreifen zu muessen.
+  // Die Auswertung erfolgt ueber den RestrictionChecker
+  if ((res=QueryProp(P_RESTRICTIONS)) && mappingp(res) &&
+      (res=(string)"/std/restriction_checker"->check_restrictions(PL,res))
+      && stringp(res)) {  
+    msg(break_string(res,78,(all?(Name(WER)+": "):0)),all);
+    return(-1);
+  }
+
+  // Ist eine WearFunc gesetzt, wird diese aufgerufen.
+  if (objectp(res=QueryProp(P_WEAR_FUNC)) && 
+      !(res->WearFunc(ME, silent, environment()))) {
+    // Eine Meldung muss von der WearFunc ausgegeben werden
+    return(-2);
+  }
+
+  // scheinbar darf man das hier anziehen. ;-)
+  return 0;
+}
+
+protected void _informwear(int silent, int all) {
+
+  // Eine Meldung ausgeben, wenn das silent-Flag nicht gesetzt ist
+  if (!silent) {
+    doWearMessage(all);
+  }
+
+  // Inform-Funktion aufrufen
+  InformWear(PL, silent, all);
+}
+
+// Die Funktion, die das eigentliche Anziehen durchfuehrt
+varargs int DoWear(int silent, int all) {
+  int nh;
+
+  // Bedingungen pruefen, _check_restrictions() gibt die notwendigen Meldungen
+  // an den Spieler aus.
+  int res = _check_wear_restrictions(silent, all);
+  if (res == -1)
+    return(0);
+  else if (res <= -2)
+    return(!all);
+
+  // Der Check auf freie Haende muss nach allen anderen Checks aus Kleidung
+  // und Ruestung erfolgen und ist im Prinzip identisch fuer beide. Daher wird
+  // der hier in dieser Funktion gemacht.
+  // Soll das Objekt Haende "benutzen"? Steht da auch wirklich ein
+  // Integer-Wert drin? ich mach da jetzt ein raise_error(), das soll
+  // schliesslich gefixt werden. Ausserdem spart es nen Workaround beim
+  // Ausziehen.
+  if (!intp(nh=QueryProp(P_NR_HANDS))) {
+    raise_error(sprintf("Invalid P_NR_HANDS in %O",object_name()));
+  }
+  // Wenn Haende benutzt werden sollen, muss natuerlich auch getestet
+  // werden, ob das ueberhaupt geht
+  if (nh>0) {
+    if (!(PL->UseHands(ME, nh))) {
+      // Schade, nicht genug Haende frei -> Meldung ausgeben
+      write(break_string("Du hast keine Hand mehr frei.",78,
+            (all?(Name(WER)+": "):0)));
+      return(!all);
+    }
+  }
+
+  // OK, die Ruestung kann angezogen werden.
+  // Behinderung beim Wechsel nur fuer Spieler
+  if (query_once_interactive(PL))
+  // Wenn das Ganze ,,wirklich'' eine Kleidung/Ruestung ist und kein SMS
+  // oder aehnliches...
+  if (!QueryProp(P_WEAPON_TYPE)) {
+    // Aktion noch setzen, Spieler hat ja was angezogen
+    PL->SetProp(P_LAST_WEAR_ACTION,({WA_WEAR,time()}));
+    // Im Kampf verliert der Spieler durch Kleidungswechsel eine Runde.
+    if (PL->InFight()) {
+      PL->SetProp(P_ATTACK_BUSY,1);
+    }
+  }
+  // Eintragen in P_CLOTHING/P_ARMOURS
+  PL->Wear(this_object());
+
+  PL->SetProp(P_EQUIP_TIME,time());
+  SetProp(P_WORN, PL);
+  SetProp(P_EQUIP_TIME,time());
+
+  // ggf. andere Objekte informieren etc.
+  _informwear(silent, all);
+
+  // Fertig mit dem Anziehen. Vorgang beenden bzw. mit anderen
+  // Ruestungen fortfahren
+  return !all;
+}
+
+
+// liefert 0 zureck, wenn die Kleidung/Ruestung ausgezogen werden kann
+// bei M_NOCHECK ist das Ausziehen immer erlaubt, allerdings wird
+// P_REMOVE_FUNC auch dann gerufen (wenn auch ignoriert).
+// <0 verbietet das Ausziehen
+protected int _check_unwear_restrictions(object worn_by, int silent, 
+                                            int all)
+{
+  // Nicht getragene Ruestungen kann man auch nicht ausziehen
+  if (!objectp(worn_by)) {
+      return(-2);
+  }
+
+  // Ist eine RemoveFunc gesetzt, wird diese aufgerufen
+  // Im Falle von M_NOCHECK wird das Ergebnis allerdings ignoriert.
+  mixed res=QueryProp(P_REMOVE_FUNC);
+  if (objectp(res)
+      && !res->RemoveFunc(ME,silent,worn_by)
+      && !(silent & M_NOCHECK)
+      )
+  {
+      // Eine Meldung muss von der RemoveFunc ausgegeben werden
+      return(-2);
+  }
+
+  // generell hebt M_NOCHECK die Restriktionen auf - sonst kommt es zu
+  // massiven Inkonsistenzen beim Bewegen mit M_NOCHECK.
+  if (silent & M_NOCHECK)
+    return 1;
+
+  // Eine verfluchte Ruestung kann man natuerlich nicht ausziehen
+  res=QueryProp(P_CURSED);
+  if (res ) {
+    if (stringp(res)) {
+      // Stand in P_CURSED ein String? Dann diesen ausgeben
+      tell_object(worn_by,      
+          (res[<1]=='\n' ? res : break_string(res,78,
+            (all?(Name(WER)+": "):0))));
+    }
+    else {
+      // Sonst eine Standard-Meldung ausgeben
+      tell_object(worn_by,break_string(
+          "Du kannst " + name(WEN) + " nicht ausziehen, " + QueryPronoun(WER)
+          + " ist verflucht worden.\n",78,(all?(Name(WER)+": "):0)));
+    }
+    return(-2);
+  }
+
+  // Ausziehen moeglich
+  return(1);
+}
+
+protected void _informunwear(object worn_by, int silent, int all) {
+
+  // Inform-Funktion aufrufen
+  InformUnwear(worn_by, silent, all);
+
+  // Meldung ausgeben, wenn das silent-Flag nicht gesetzt ist
+  if (!(silent&M_SILENT)) {
+    doUnwearMessage( worn_by, all );
+  }
+}
+
+// Die Funktion, die das eigentliche Ausziehen durchfuehrt
+// hier steht nur drin, was auf jeden Fall fuer Kleidungen und Ruestungen
+// gleich ist, damit man bei Ruestungen diese Funktion nicht ueberschreiben
+// muss.
+varargs int DoUnwear(int silent, int all) {
+  object worn_by;
+  int nh;
+
+  // Das Flag "silent" wird in der RemoveFunc() etwas anders behandelt
+  // als ueberall anders. Deshalb wird M_SILENT gesetzt, sofern "silent"
+  // _irgendeinen_ Wert ausser M_NOCHECK hatte.
+  if ( silent & ~M_NOCHECK )
+      silent |= M_SILENT;
+
+  // Standard-Notfiyfail setzen.
+  if (all)
+    notify_fail("Alles ausgezogen, was ging.\n");
+
+  // Hat das Objekt Haende "benutzt"? Steht da auch wirklich ein
+  // Integer-Wert drin? Wenn nicht, mach ich nen raise_error(), das duerfte
+  // eigentlich gar nicht passieren. Pruefung mal am Anfang machen, bevor
+  // irgendwas anderes (RemoveFunc()) passiert ist.
+  if (!intp(nh=QueryProp(P_NR_HANDS))) {
+    raise_error(sprintf("Invalid P_NR_HANDS in %O",object_name()));
+  }
+
+  worn_by=QueryProp(P_WORN);
+  // darf ausgezogen werden? Wenn nicht, Ende.
+  int res = _check_unwear_restrictions(worn_by,silent,all);
+  if (res < 0)
+    return(!all);
+
+  // OK, alles klar, die Ruestung wird ausgezogen
+  worn_by->Unwear(ME);
+
+  // Benutzte Haende wieder freigeben
+  if (nh>0) {
+    worn_by->FreeHands(ME);
+  }
+
+  worn_by->SetProp(P_EQUIP_TIME, time());
+  SetProp(P_WORN, 0);
+
+  // Flag noch setzen, Spieler hat ja was ausgezogen
+  // Aber nur wenns auch der Spieler selbst ist.
+  // und wenn das wirklich eine Ruestung und kein SMS o.ae. ist.
+  if (PL && PL==worn_by && !QueryProp(P_WEAPON_TYPE)) {
+    //Behinderung beim Wechsel nur fuer Spieler
+    if (query_once_interactive(PL)) {
+      PL->SetProp(P_LAST_WEAR_ACTION,({WA_UNWEAR,time()}));
+      if (PL->InFight()) { 
+        PL->SetProp(P_ATTACK_BUSY,1);
+      }
+    }
+  }
+
+  // ok, nun noch andere Objekte informieren.
+  _informunwear(worn_by,silent,all);
+
+  // Fertig mit dem Anziehen. Vorgang beenden bzw. mit anderen
+  // Ruestungen fortfahren
+  return !all;
+}
+
+protected int _do_wear(string str, int silent, int all) {
+  int *last;
+
+  // Standard-Notfiy-Fail setzen.
+  if (all)
+    notify_fail("Alles angezogen, was ging.\n");
+
+  // Ist diese Ruestung ueberhaupt gemeint? Bei "trage alles" ist dies
+  // natuerlich immer der Fall
+  if (!str || (!all && !id(str))) {
+      return 0;
+  }
+
+  // Vielleicht darf der Spieler ja gar nix mehr anziehen.
+  if ((object)PL->InFight()) {        
+    last=(int*)PL->QueryProp(P_LAST_WEAR_ACTION);
+      if (pointerp(last) && (last[0]==WA_UNWEAR) && ((time()-last[1])<2)) {
+        notify_fail("Du hast doch gerade erst etwas ausgezogen!\n"
+            "So schnell bist Du nicht!\n");        
+        return 0;        
+      }
+  }
+
+  // Auf zum eigentlichen Anziehen
+  return DoWear(silent, all);
+
+}
+
+// Funktion, die das "trage"/"ziehe * an"-Kommando auswertet
+varargs int do_wear(string str, int silent) {  
+  int all;  
+
+  // Hat der Spieler "trage alles" eingegeben?
+  all=(str=="alles" || str=="alle kleidung" || str=="alle bekleidung");
+  
+  return(_do_wear(str,silent,all));
+}
+
+protected int _do_unwear(string str, int silent, int all) {
+  int * last;
+
+  // Ist diese Ruestung ueberhaupt gemeint? Und hat dieser Spieler sie
+  // auch wirklich an?
+  if (!stringp(str) || (!all && !id(str))) {
+    return 0;
+  }
+
+  if (!QueryProp(P_WORN)) { 
+    if (all) {  
+      notify_fail("Alles ausgezogen, was ging.\n");
+      return 0;
+    }
+    if (!Query(P_ARTICLE) || QueryProp(P_PLURAL)) {
+      notify_fail( break_string(
+            "Du traegst k"+name(WEN,0)+".",78) );
+    }
+    else {    
+      notify_fail( break_string(
+           "Du traegst "+name(WEN,1)+" nicht.",78) );  
+    }
+    return 0;
+  }
+
+  // Vielleicht darf der Spieler ja gar nichts mehr ausziehen.
+  if ((object)PL->InFight()) {
+    last=(int*)PL->QueryProp(P_LAST_WEAR_ACTION);
+    if (pointerp(last) && (last[0]==WA_WEAR) && ((time()-last[1])<2)) {
+      notify_fail("Du hast doch gerade erst etwas angezogen!\n"              
+          "So schnell bist Du nicht!\n");
+      return 0;
+    }
+  }
+  // Auf zum eigentlichen Ausziehen
+  return DoUnwear(silent, all);
+}
+
+// Die Funktion, die das "ziehe * aus"-Kommando auswertet
+varargs int do_unwear(string str, int silent) {
+  int all;
+
+  all=(str=="alles" || str=="alle kleidung" || str=="alle bekleidung");
+
+  return(_do_unwear(str,silent,all));
+}
+
+// Funktion, die das "ziehe"-Kommando auswertet
+int ziehe(string str) {   
+  string ob;
+
+  // Uebergebenes Argument pruefen
+  if (!stringp(str)) {
+    return 0;  
+  }
+
+  // Ist ANziehen gemeint?
+  if (sscanf(str, "%s an", ob)==1) {
+      return do_wear(ob );
+  }
+
+  // Oder ist AUSziehen gemeint?
+  if (sscanf(str, "%s aus", ob)==1 ) { 
+      return do_unwear(ob);
+  }
+
+  // Ok, es geht wohl weder ums an- noch ums ausziehen
+  return 0;
+}
+
+
+// Beschaedigen des Kleidungsstuecks
+
+// Direktes Beschaedigen der Kleidung durch Setzen der Prop gibts nicht. ;-)
+// Das geht aus Kompatibilitaetgruenden bei Ruestungen, aber nicht mehr bei
+// Kleidung. Punkt.
+static mixed _set_item_damaged(mixed arg) {
+  return(QueryProp(P_DAMAGED));
+}
+
+// Will man eine Kleidung beschaedigen oder reparieren, so macht man das
+// am besten ueber die Funktion Damage(argument). Positive Argumente
+// bedeuten eine Beschaedigung, negative eine Reparatur. Der Rueckgabewert
+// ist die wirklich durchgefuehrte Aenderung des Beschaedigungswertes
+int Damage(int new_dam) {
+  return 0;
+}
+
+// Wird die Kleidung einer Belastung ausgesetzt (bei Ruestungen z.B. bei einem
+// Angriff eines Gegners), dann wird TakeFlaw() aufgerufen. Bei Kleidungen
+// koennte man ja per P_SENSITIVE arbeiten oder ein Magier ruft bei Aktionen
+// TakeFlaw() auf.
+varargs void TakeFlaw(mixed dam_types,mapping einfos) {   
+  int quality;
+
+  // Ist der Ruestung eine Qualitaet gesetzt worden, so kann es zu einer
+  // allmaehlichen Beschaedigung der Ruestung kommen. Im if() flaw gleich
+  // hochzaehlen.
+  if ((quality=QueryProp(P_QUALITY)) && !((++flaw) % quality)) {      
+    Damage(1);
+  }
+
+  // Zeitpunkt des ersten Aufrufes festhalten
+  if (!ftime)   
+    ftime=time();
+}
+
+// Die Flaw-Daten koennen natuerlich auch abgerufen werden
+mixed *QueryFlaw() {
+  return ({flaw,ftime,dtime(ftime)});
+}
+
+public status IsClothing() {return 1;}
+
diff --git a/std/clothingHG.c b/std/clothingHG.c
new file mode 100644
index 0000000..fbd265a
--- /dev/null
+++ b/std/clothingHG.c
@@ -0,0 +1,59 @@
+// MorgenGrauen MUDlib
+//
+// armourHG.c -- armour standard object for hats and glasses
+//
+// $Id: armourHG.c,v 1.4 2002/08/19 14:21:31 Rikus Exp $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/clothing";
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#undef NEED_PROTOTYPES
+
+#include <language.h>
+#include <defines.h>
+
+int setzen(string s);
+
+void create() {
+    ::create();
+    AddCmd(({"setz","setze"}),"setzen");
+}
+
+varargs void doWearMessage( int all ) {
+  if( query_once_interactive(PL) ) {
+    write( "Du setzt " + name(WEN,1) + " auf.\n" );
+  }
+  if (objectp(environment()) && objectp(environment(environment())))
+    tell_room(environment(environment()),
+	capitalize((string)PL->name(WER)) + " setzt " + name(WEN,0) + 
+	" auf.\n");
+}
+
+varargs void doUnwearMessage( object worn_by, int all ) {
+  if( query_once_interactive(worn_by) ) {
+    tell_object(worn_by,  "Du setzt " + name(WEN,1) + " ab.\n" );
+  }
+  tell_room(environment(worn_by), 
+	    (capitalize((string)worn_by->name(WER))) + " setzt " +
+	    name(WEN,0) + " ab.\n", ({worn_by}));
+}
+
+int setzen(string str) {
+  string ob;
+
+  if(!str)
+    return 0;
+  if(sscanf(str, "%s auf", ob)==1 )
+    return _do_wear(ob, 0, 0);
+  if(sscanf(str, "%s ab", ob)==1 )
+    return _do_unwear(ob, 0, 0);
+  return 0;
+}
+
diff --git a/std/clothing_container.c b/std/clothing_container.c
new file mode 100644
index 0000000..ec93b92
--- /dev/null
+++ b/std/clothing_container.c
@@ -0,0 +1,54 @@
+// MorgenGrauen MUDlib
+//,
+// clothing_container.c -- Kleidung, in die man was reinstecken kann
+//
+// $Id: clothing_container.c,v 1.3 2003/08/07 07:25:43 Rikus Exp $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/properties";
+inherit "/std/thing/commands";
+inherit "/std/thing/language";
+inherit "/std/thing/envchk";
+inherit "/std/container/light";
+inherit "/std/container/restrictions";
+inherit "/std/container/inventory";
+inherit "/std/container/items";
+inherit "/std/clothing/moving";
+inherit "/std/clothing/wear";
+inherit "/std/clothing/container_description";
+
+//#define NEED_PROTOTYPES
+
+#include <container.h>
+
+protected void create() {
+  properties::create();
+  commands::create();
+  light::create();
+  container_description::create();
+  restrictions::create();
+  items::create();
+  wear::create();
+  envchk::create();
+  SetProp(P_CONTAINER,1);
+  SetProp(P_PREPOSITION, "in");
+  SetProp(P_SOURCE_PREPOSITION, "aus");
+  SetProp(P_DEST_PREPOSITION, "in");
+  AddId("Ding");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset()  {
+  items::reset();
+}
+
+int IsClothing() {return 1;}
+
diff --git a/std/container.c b/std/container.c
new file mode 100644
index 0000000..a77c7fa
--- /dev/null
+++ b/std/container.c
@@ -0,0 +1,67 @@
+// MorgenGrauen MUDlib
+//
+// container.c -- container standard object
+//
+// $Id: container.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+// The most general object class. It defines the really basic functions.
+//
+// This object should understand the properties short, long, info, read_msg,
+// value, weight.
+//
+// weight is given in grams, 1 kilogram (kg) = 1000 grams (g) = 1 old weight
+// unit
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/properties";
+inherit "/std/thing/moving";
+inherit "/std/thing/commands";
+inherit "/std/thing/language";
+inherit "/std/container/description";
+inherit "/std/container/light";
+inherit "/std/container/restrictions";
+inherit "/std/container/inventory";
+inherit "/std/container/items";
+inherit "/std/thing/envchk";
+
+#include <properties.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+protected void create()
+{
+  properties::create();
+  commands::create();
+  light::create();
+  description::create();
+  restrictions::create();
+  items::create();
+  envchk::create();
+  SetProp(P_CONTAINER,1);
+  SetProp(P_PREPOSITION, "in");
+  SetProp(P_SOURCE_PREPOSITION, "aus");
+  SetProp(P_DEST_PREPOSITION, "in");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+/*
+void init() 
+{
+  commands::init();
+  description::init();
+}
+*/
+
+void reset()
+{
+  items::reset();
+}
+
diff --git a/std/container/description.c b/std/container/description.c
new file mode 100644
index 0000000..c4f7b5c
--- /dev/null
+++ b/std/container/description.c
@@ -0,0 +1,103 @@
+// MorgenGrauen MUDlib
+//
+// container/description.c -- standard description for containers
+//
+// $Id: description.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+inherit "/std/thing/description";
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <container.h>
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+
+
+void create()
+{
+  ::create();
+  SetProp(P_TRANSPARENT, 1);
+  AddId("Container");
+}
+
+varargs string long(int mode) {
+  string descr, inv_descr;
+
+  descr = process_string(QueryProp(P_LONG));
+  if(!QueryProp(P_TRANSPARENT)) return descr;
+
+  inv_descr = make_invlist(PL, all_inventory(ME), mode );
+  if ( inv_descr != "" )
+    descr += capitalize(QueryPronoun(WER)) + " enthaelt:\n" + inv_descr;
+  return descr;
+}
+
+// flags: 1 - wizard, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+private void stringenize(mixed obj, int flags, mixed objs, mixed info)
+{
+  string id, tmp;
+  int idx;
+  
+  if ( (!(flags & 4) && (flags & 1))) {
+    //wenn Magier und Magierinfos angehaengt werden sollen:
+    tmp = capitalize(obj->short()||"")[0..<2]
+        + " ["+object_name(obj)+"]";
+  }
+  else if (obj->QueryProp(P_INVIS)) {
+    //keine Ausgabe bzw. leerer String, wird dann von collect rausgeschmissen
+    tmp="";
+  }
+  else {
+    //Spieler, Magier ohne P_WANTS_TO_LEARN etc.
+    tmp = capitalize(obj->short()||"")[0..<2];
+  }
+  //wenn wizard und 'dont collect equal objects': id ist object_name()
+  if(flags & 3 || living(obj)) 
+    id = object_name(obj);
+  //sonst nehmen wir den Namen der Blueprint ->zusammenfassen der Objekte
+  else
+    id = BLUE_NAME(obj) + tmp;
+  // Anzahl des jeweiligen Objekts (anhand von id) zaehlen.
+  if((idx = member(objs, id)) == -1)
+  {
+    objs += ({ id });
+    info += ({ ({ tmp, 1, obj}) });
+  }
+  else
+    info[idx][1]++;
+}
+
+private string collect(mixed obj)
+{
+  //bekommt ein Array ({name,anzahl,objekt})
+  //Objekte ohne Namen (P_SHORT==0 oder P_INVIS) wegwerfen
+  if(!sizeof(obj[0])) return 0;
+  // Objektname + (Anzahl) zurueckgeben.
+  return obj[0] + (obj[1] > 1 ? " ("+obj[1]+")" : "");
+}
+
+// flags: 1 - return array, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+varargs mixed make_invlist(object viewer, mixed inv, int flags)
+{
+  int iswiz;
+  mixed objs, info;
+
+  iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
+  objs = ({}); info = ({});
+  map(inv, #'stringenize/*'*/, iswiz | (flags & 2) | (flags & 4), &objs, &info);
+  if(flags & 1) return info;
+  inv = map(info, #'collect/*'*/) - ({ 0 });
+  if(!sizeof(inv)) return "";
+  return sprintf("%"+(sizeof(inv) > 6 ? "#" : "=")+"-78s",
+                 implode(inv, "\n")) + "\n";
+}
+
diff --git a/std/container/inventory.c b/std/container/inventory.c
new file mode 100644
index 0000000..0a057b7
--- /dev/null
+++ b/std/container/inventory.c
@@ -0,0 +1,128 @@
+// MorgenGrauen MUDlib
+//
+// container/inventory.c -- Umgang mit besonderen Objekten im Inventory
+//
+// $Id: inventory.c 6379 2007-07-20 22:32:02Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <thing.h>
+#include <properties.h>
+#include <sensitive.h>
+
+#define ME this_object()
+
+void RemoveSensitiveObjectFromList(object ob, string list) {
+  mixed a,b,c;
+  int i,f;
+
+  if (!pointerp(a=QueryProp(SENS_PROP_PREFIX+list)))
+    return;
+  f=1;
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (!pointerp(b=a[i]) ||
+	!sizeof(b) ||
+	!objectp(c=b[0]) ||
+	environment(c)!=ME ||
+	c==ob)
+      a[i]=f=0;
+  if (f)
+    return;
+  a-=({0});
+  if (!sizeof(a))
+    a=0;
+  SetProp(SENS_PROP_PREFIX+list,a);
+}
+
+void RemoveSensitiveObject(object ob) {
+  RemoveSensitiveObjectFromList(ob,SENSITIVE_INVENTORY);
+  RemoveSensitiveObjectFromList(ob,SENSITIVE_INVENTORY_TRIGGER);
+}
+
+varargs void InsertSensitiveObjectToList(object ob, string list, string key,
+					 int threshold, mixed *opt) {
+  mixed a;
+  int i;
+  
+  if (!pointerp(a=QueryProp(SENS_PROP_PREFIX+list)))
+    a=({});
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (a[i][SENS_OBJECT]==ob && a[i][SENS_KEY]==key)
+      return;
+  a+=({({ob,symbol_function(SENS_TRIGGER_PREFIX+list,ob),
+         key,threshold,opt})});
+  SetProp(SENS_PROP_PREFIX+list,a);
+}
+
+void InsertSensitiveObject(object ob, mixed arg) {
+  int i;
+  mixed x;
+  mapping map_ldfied;
+  
+  if (!pointerp(arg))
+    return;
+  for (i=sizeof(arg)-1;i>=0;i--)
+    if (pointerp(x=arg[i]) &&
+	sizeof(x)>=3 &&
+	stringp(x[0]) &&
+	stringp(x[1]) &&
+	intp(x[2])) {
+      InsertSensitiveObjectToList(ob,x[0],x[1],x[2],x[3..]);
+      call_other(ME,SENS_INSERT_PREFIX+x[0],ob,x[1],x[2],x[3..]);
+    }
+}
+
+varargs void insert_sensitive_inv(object ob, string key,
+				 int threshold, mixed *opt) {
+  // Diese Funktion sucht, ob beim Hinzufuegen eines sensitiven Objekts
+  // schon ein Objekt da ist, dass dieses ausloest.
+  // z.B. (dynamit, feuer, 100, opt_dynamit) sorgt fuer
+  // dynamit->trigger_sensitive_inv(fackel,feuer,120,opt_fackel,opt_dynamit)
+  // wenn eine Fackel vorher mit Wert 120 eingetragen war.
+  mixed a,x;
+  int i;
+  
+  if (!pointerp(a=QueryProp(P_SENSITIVE_INVENTORY_TRIGGER)) ||
+      !objectp(ob))
+    return;
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (pointerp(x=a[i]) &&
+	x[SENS_KEY]==key &&
+	x[SENS_THRESHOLD]>threshold && // Ist der Ausloeser gross genug?
+	objectp(x[SENS_OBJECT]) &&
+	environment(x[SENS_OBJECT])==environment(ob))
+      ob->trigger_sensitive_inv(x[SENS_OBJECT],x[SENS_KEY],x[SENS_THRESHOLD],
+				x[SENS_OPT],opt);
+  // Zuerst Trigger-Optionen, dann Optionen des sensitiven Objekts
+}
+
+varargs void insert_sensitive_inv_trigger(object ob, string key,
+					 int threshold, mixed *opt) {
+  // Diese Funktion sucht, ob ein sensitives Objekt im Inventory ist,
+  // das durch dieses Objekt ausgeloest wird.
+  // z.B. (fackel, feuer, 120, opt_fackel) sorgt fuer
+  // dynamit->trigger_sensitive_inv(fackel,feuer,120,opt_fackel,opt_dynamit)
+  // wenn Dynamit mit Wert<120 eingetragen war
+  mixed a,x;
+  int i;
+  
+  if (!pointerp(a=QueryProp(P_SENSITIVE_INVENTORY)) ||
+      !objectp(ob))
+    return;
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (pointerp(x=a[i]) &&
+	x[SENS_KEY]==key &&
+	x[SENS_THRESHOLD]<threshold && // Ausloeser gross genug?
+	objectp(x[SENS_OBJECT]) &&
+	environment(x[SENS_OBJECT])==environment(ob) &&
+	closurep(x[SENS_CLOSURE]))
+      funcall(x[SENS_CLOSURE],
+	      ob,key,threshold,opt,x[SENS_OPT]);
+  // Zuerst Trigger-Optionen, dann Optionen des sensitiven Objekts
+}
diff --git a/std/container/items.c b/std/container/items.c
new file mode 100644
index 0000000..9961bfe
--- /dev/null
+++ b/std/container/items.c
@@ -0,0 +1,256 @@
+// MorgenGrauen MUDlib
+//
+// container/items.c -- creating extra items in room
+//
+// $Id: items.c 8811 2014-05-09 17:30:37Z Zesstra $
+
+// extra items handling
+//
+//	AddItem(string filename, int refresh)
+//	  Clones an item and puts it into the room. <refresh> may be
+//	  on of the following:
+//	  REFRESH_NONE: No refresh done until reboot.
+//	  REFRESH_DESTRUCT: Refresh on reset if item was destructed.
+//	  REFRESH_REMOVE: Refresh on reset if item was removed from room.
+//	  REFRESH_ALWAYS: Create a new clone on every reset.
+//
+// The commands are implemented as properties P_ITEMS as mapping. They are 
+// stored locally (_set_xx) as mapping to speed up the routines 
+// in this module.
+// 
+// Format of key and data in the P_ITEMS mapping:
+//
+// ([ key1 : refresh1; obp1; arr1, ..., keyn : refreshn; obpn; arrn ])
+
+#include <sys_debug.h>
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <rooms.h>
+#include <container.h>
+#undef NEED_PROTOTYPES
+#include <defines.h>
+#include <config.h>
+#include <properties.h>
+#include <moving.h>
+#include <daemon.h>
+
+protected void create()
+{ 
+    Set( P_ITEMS, ({}) );
+    Set( P_ITEMS, SECURED, F_MODE_AS );
+    
+    OBJECTD->QueryObject();  // querying general objects
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+/* Kram zum Aufraeumen von multiplen gleichen Items im Container. */
+private object removeable_ob( object ob )
+{
+    if ( !query_once_interactive(ob) && !living(ob) )
+        return ob;
+
+    return 0;
+}
+
+protected varargs void remove_multiple(int limit, mixed fun)
+{
+    object *inh = all_inventory(this_object()) - ({0});
+
+    inh = filter( inh, #'removeable_ob );
+    foreach(mixed item : QueryProp(P_ITEMS))
+      inh -= ({ item[0] });
+ 
+    if (!stringp(fun) && !closurep(fun))
+      fun = "description_id";
+    inh = unique_array(inh, fun, 0);
+    foreach(mixed arr: inh)
+    {
+      if (sizeof(arr) <= limit)
+        continue;
+      catch(call_other(arr[limit ..], "remove"); publish);
+    }
+}
+
+
+/* Item handling */
+public varargs object AddItem( mixed filename, int refresh, mixed props ) 
+{
+    string file;
+    object ob;
+    int i;
+
+    if( pointerp(filename) ) {
+        for( i = sizeof(filename); i--; )
+            filename[i] = (string)master()->_get_path( filename[i], "?" );
+        
+        file = filename[random( sizeof(filename) )];
+    }
+    else 
+        file = filename = (string)master()->_get_path( filename, "?" );
+    
+    if ( props == 1 )
+        catch(ob = load_object( file); publish);
+    else
+	catch(ob = clone_object(file); publish);
+    
+    if (objectp(ob)) {
+      ob->move( this_object(), M_NOCHECK|M_NO_ATTACK );
+      // mit Absicht keine Pruefung aufs Move, wenns nicht geht, solls 2s
+      // spaeter auf der Ebene buggen, weil praktisch niemand im create() das
+      // Ergebnis vom AddItem() prueft.
+    }
+
+    // In P_ITEMS vermerken, es sei denn, REFRESH_NONE ist gegeben, in dem
+    // Fall ist die Speicherung voellig unnoetig.
+    // TODO: Pruefen, ob das wirklich problemlos geht. Bis dahin werden auch
+    // TODO::REFRESH_NONE-Items vermerkt. (s. clean_up() in /std/room.c)
+    //if (!(refresh & REFRESH_NONE)) {
+      SetProp( P_ITEMS, QueryProp(P_ITEMS) +
+             ({ ({ ob,        // RITEM_OBJECT
+                   filename,  // RITEM_FILE
+                   refresh    // RITEM_REFRESH
+                       }) +
+                    ((mappingp(props) || props == 1) ? ({ props }) : ({})) }) );
+    //}
+
+    if ( ob && mappingp(props) ) 
+        walk_mapping( props, symbol_function( "SetProp", ob ) );
+    
+    return ob;
+}
+
+
+private void ri_rem_ob( object ob )
+{
+    object *inv;
+    int i;
+  
+    if ( objectp(ob) && present(ob, this_object()) ) {
+        inv = deep_inventory(ob);
+        
+        for ( i = sizeof(inv); i--; )
+            if ( inv[i] ) {
+                inv[i]->remove(1);
+                
+                if ( inv[i] )
+                    destruct(inv[i]);
+            }
+        
+        ob->remove(1);
+        
+        if ( ob )
+            destruct(ob);
+    }
+}
+
+
+private int ri_filter( mixed *ritem, mixed file )
+{
+    object ob, *inv;
+    int i;
+  
+    ob = ritem[RITEM_OBJECT];
+  
+    if ( stringp(file) && ritem[RITEM_FILE] == file )
+        return ri_rem_ob(ob), 0;
+    else if ( pointerp(ritem[RITEM_FILE]) && pointerp(file) &&
+              sizeof(file & ritem[RITEM_FILE]) == sizeof(ritem[RITEM_FILE]) )
+        return ri_rem_ob(ob), 0;
+
+    return 1;
+}
+
+
+public void RemoveItem( mixed filename )
+{
+    mixed *items;
+    int i;
+
+    if ( !pointerp(items = QueryProp(P_ITEMS)) || !sizeof(items) )
+        return;
+
+    if ( pointerp(filename) )
+        for ( i = sizeof(filename); i--; )
+            filename[i] = (string)master()->_get_path( filename[i], "?" );
+    else
+        filename = (string)master()->_get_path( filename, "?" );
+  
+    SetProp( P_ITEMS, filter( items, #'ri_filter/*'*/, filename ) );
+}
+
+
+private mixed _do_refresh( mixed item )
+{
+    string file;
+    object ob;
+
+    if ( !pointerp(item) || item[RITEM_REFRESH] == REFRESH_NONE )
+        return item;
+
+    if ( pointerp(item[RITEM_FILE]) ) 
+        file = item[RITEM_FILE][random( sizeof(item[RITEM_FILE]) )];
+    else
+        file = item[RITEM_FILE];
+
+    switch( item[RITEM_REFRESH] ){
+    case REFRESH_MOVE_HOME:
+        if ( objectp(item[RITEM_OBJECT]) &&
+             environment(item[RITEM_OBJECT]) != ME ) {
+            item[RITEM_OBJECT]->move( ME, M_GO|M_NO_ATTACK );
+            break;
+        }
+        
+    // fall through
+    case REFRESH_DESTRUCT:
+        if ( objectp(item[RITEM_OBJECT]) ) 
+            break; // else FALL THROUGH
+        
+    case REFRESH_REMOVE:
+        if ( objectp(item[RITEM_OBJECT]) &&
+             environment(item[RITEM_OBJECT]) == ME )
+            break; // else FALL THROUGH
+        
+    default:
+        if ( sizeof(item) > RITEM_PROPS && item[RITEM_PROPS] == 1 ) {
+            ob = load_object(file);
+        }
+        else
+            ob = clone_object(file);
+
+        ob->move( ME, M_NOCHECK|M_NO_ATTACK );
+        break;
+    }
+    
+    if ( ob ){
+        item[RITEM_OBJECT] = ob;
+        
+        if ( sizeof(item) > RITEM_PROPS && mappingp(item[RITEM_PROPS]) )
+            walk_mapping( item[RITEM_PROPS], symbol_function( "SetProp", ob ) );
+    }
+    
+    return item;
+}
+
+
+// reset handling: check how the items should be refreshed.
+void reset() 
+{
+    mixed *items;
+
+    if ( !pointerp(items = QueryProp(P_ITEMS)) ){
+        SetProp( P_ITEMS, ({}) );
+        return;
+    }
+    
+    SetProp( P_ITEMS, map( items, #'_do_refresh/*'*/ ) - ({0}) );
+}
+
diff --git a/std/container/light.c b/std/container/light.c
new file mode 100644
index 0000000..435400f
--- /dev/null
+++ b/std/container/light.c
@@ -0,0 +1,149 @@
+// MorgenGrauen MUDlib
+//
+// container/light.c -- Lichtsystem fuer Container
+//
+// $Id: description.c 6986 2008-08-22 21:32:15Z Zesstra $
+
+inherit "/std/thing/light";
+
+#pragma strict_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <container.h>
+#include <properties.h>
+
+// BTW: _query_last_content_change() sollte bloss nie auch -1 fuer
+// uninitialisiert zurueckliefern...
+private nosave int last_light_calc = -1;
+
+protected void create()
+{
+  ::create();
+  // wieviel Licht schluckt der Container _muss_ > 0 sein!
+  SetProp(P_LIGHT_TRANSPARENCY, 2);
+}
+
+protected void create_super() {set_next_reset(-1);}
+
+
+static mixed _query_light_transparency()
+{
+   if (QueryProp(P_TRANSPARENT))
+     return Query(P_LIGHT_TRANSPARENCY,F_VALUE);
+   return 999;
+}
+
+public int add_light_sources(int * sources) {
+  float light = 0.0;
+  //printf("als(%O): %O\n",this_object(),sources);
+  // Alle Lichtlevel werden als Exponent von e aufgefasst und die Summe dieser
+  // Potenzen gebildet.
+  foreach(int l : sources) {
+     if (l > 0)
+       light += exp(l);
+     else if (l < 0)
+       light -= exp(abs(l));
+     // l==0 muss ignoriert werden
+  }
+  // anschliessend wird aus dieser Summe der natuerliche Logarithmus
+  // berechnet.
+  // auf diese Weise haben hoehere Lichtlevel ueberproportional viel Einfluss
+  // auf die Helligkeit im Raum, kleine Lichtlevel fallen aber nicht komplett
+  // raus.
+  if (light > 0) {
+      light = log(light);
+  }
+  else if (light < 0) {
+      light = -log(abs(light));
+  }
+
+  //printf("als(): %O\n",light);
+  // runden und als int zurueckgeben.
+  if (light >= 0)
+    return to_int(light+0.5);
+  //else
+  return to_int(light-0.5);
+}
+
+static int _query_total_light()
+{
+  int totallight;
+
+  if ( _query_last_content_change() == last_light_calc )
+    return Query(P_TOTAL_LIGHT);
+
+  // eigenes P_LIGHT und P_TOTAL_LIGHT der enthaltenen Objekte verrechnen
+  int intlight = add_light_sources(
+      all_inventory()->QueryProp(P_TOTAL_LIGHT) + ({QueryProp(P_LIGHT)}));
+  // P_INT_LIGHT (gerundet) abspeichern
+  Set(P_INT_LIGHT, intlight, F_VALUE);
+
+  // Licht nach aussen muss Containertransparenz beruecksichtigen.
+  if (intlight > 0) {
+    totallight = intlight - QueryProp(P_LIGHT_TRANSPARENCY);
+    if (totallight < 0)
+      totallight = 0;
+  }
+  else {
+    totallight = intlight + QueryProp(P_LIGHT_TRANSPARENCY);
+    if (totallight > 0)
+      totallight = 0;
+  }
+  //printf("_query_total_light(%O): %O\n",this_object(),totallight);
+
+  last_light_calc = _query_last_content_change();
+
+  // Runden und Konversion nach int nicht vergessen...
+  if (totallight > 0)
+    return SetProp(P_TOTAL_LIGHT, to_int(totallight + 0.5));
+  //else
+  return SetProp(P_TOTAL_LIGHT, to_int(totallight - 0.5));
+}
+
+static int _query_int_light()
+{
+   int intlight, envlight;
+
+   if ( _query_last_content_change() != last_light_calc )
+       _query_total_light();
+
+   // P_INT_LIGHT des environments kann sich natuerlich aendern _ohne_ das
+   // etwas an einem container geaendert wird. Daher Auswertung jedes mal
+   // neu aktualisieren.
+   if (!environment()
+       || !(envlight=(int)environment()->QueryProp(P_INT_LIGHT)))
+      return Query(P_INT_LIGHT, F_VALUE);
+   else {
+      intlight = Query(P_INT_LIGHT, F_VALUE);
+      int transparency = QueryProp(P_LIGHT_TRANSPARENCY);
+
+      // bei einem transparentem Container kann natuerlich das Licht des
+      // environments den Container auch ausleuchten....
+      // (Anmerkung: in dem Licht von draussen ist unser Lichtanteil
+      // natuerlich auch drin, gedaempft um die Transparenz (raus und rein).
+      // Ich vernachlaessige das.)
+      if (abs(envlight) > transparency) {
+        if (envlight > 0)
+            envlight -= transparency;
+        else
+            envlight += transparency;
+        // jetzt Licht von aussen und innen verrechnen
+        intlight = add_light_sources(({intlight, envlight}));
+      }
+      // else: nur P_INT_LIGHT, Licht des Environments keine Bedeutung
+   }
+   return intlight;
+}
+
+static int _set_light(int light)
+{
+  last_light_calc=-1;
+
+  return ::_set_light(light);
+}
+
diff --git a/std/container/moneyhandler.c b/std/container/moneyhandler.c
new file mode 100644
index 0000000..ae25453
--- /dev/null
+++ b/std/container/moneyhandler.c
@@ -0,0 +1,53 @@
+// MorgenGrauen MUDlib
+//
+// container/moneyhandler.c -- money handler for container
+//
+// $Id: moneyhandler.c 6738 2008-02-19 18:46:14Z Humni $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <moving.h>
+#include <money.h>
+
+public int AddMoney( int amount )
+{
+  object ob;
+  int ret;
+
+  if ( !amount )
+    return 1;
+    
+  ob = clone_object( GELD );
+  ob->SetProp( P_AMOUNT, amount );
+
+  ret=ob->move( this_object(), M_PUT|M_MOVE_ALL );
+  // Bei fehlerhaftem move ggf. wieder zerstoeren.
+  if ((ret != MOVE_OK)
+      && ob)
+  {
+    ob->remove(1);
+  }
+  return ret;
+}
+
+public int QueryMoney()
+{
+  object money;
+  int geld;
+
+  if ( money = present_clone(GELD, this_object()) )
+    geld = money->QueryProp(P_AMOUNT);
+
+  if ( money = present(GELDBOERSE_MIT_GELD, this_object()) )
+    geld += money->QueryProp(P_AMOUNT);
+
+  return geld;
+}
diff --git a/std/container/restrictions.c b/std/container/restrictions.c
new file mode 100644
index 0000000..3da5c9d
--- /dev/null
+++ b/std/container/restrictions.c
@@ -0,0 +1,480 @@
+// MorgenGrauen MUDlib
+//
+// container/restrictions.c -- container restrictions
+//
+// $Id: restrictions.c 9020 2015-01-10 21:49:41Z Zesstra $
+
+// This is a simple container to put objects in. It defines all functions
+// which are necessary to describe an object which can be filled with
+// other things.
+//
+// It will support restrictions for volume, weight etc.
+//
+// The following properties are defined:
+// P_MAX_WEIGHT - maximum weight which container can carry
+// P_TOTAL_WEIGHT - total weight, with contents.
+// P_WEIGHT - the weight of the container without contents
+//
+// Functions for manipulation of weight
+// MayAddWeight(weight) - Can <weight> be inserted?
+// AddWeight(weight) - Add an amount of <weight>
+//
+// IMPORTANT: unit equals 1 gram
+
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include "/sys/thing/properties.h"
+#include <properties.h>
+#include <defines.h>
+#include <thing/description.h>
+
+// local properties prototypes
+static int _query_total_weight();
+
+
+private nosave int LastWeightCalc, contents, LastObjectCount, objcount;
+private nosave int last_content_change;
+
+
+void create()
+{
+    Set( P_WEIGHT_PERCENT, 50 );
+    Set( P_TOTAL_WEIGHT, NOSETMETHOD, F_SET_METHOD );
+    Set( P_TOTAL_WEIGHT, PROTECTED, F_MODE );
+    Set( P_MAX_OBJECTS, 100 );
+    Set( P_TOTAL_OBJECTS, NOSETMETHOD, F_SET_METHOD );
+    Set( P_TOTAL_OBJECTS, PROTECTED, F_MODE );
+    LastWeightCalc = -1;
+    LastObjectCount = -1;
+}
+
+
+static int _query_last_content_change()
+{
+    return last_content_change;
+}
+
+
+// absichtlich public, da es von der simul_efun.c *direkt* aufgerufen
+// wird aus Performancegruenden!
+public varargs int _set_last_content_change( mixed wert )
+{
+    // Der uebergebene Wert ist unerheblich. Die Property
+    // P_LAST_CONTENT_CHANGE wird so oder so bei jedem SetProp()-
+    // Aufruf um eins erhoeht, um der 2s-"Unschaerfe" von time()
+    // aus dem Weg zu gehen.
+    return ++last_content_change;
+}
+
+
+int query_weight_contents()
+{
+    object *objs;
+    int w, w2, i;
+
+    if ( last_content_change == LastWeightCalc )
+        return contents;
+    
+    w = 0;
+    objs = all_inventory(this_object());
+    i = sizeof(objs);
+    
+    while ( i-- ){
+        if ( !(w2 = (int)objs[i]->QueryProp(P_TOTAL_WEIGHT)) )
+            w2 = (int)objs[i]->QueryProp(P_WEIGHT);
+        w += w2;
+    }
+    
+    LastWeightCalc = last_content_change;
+    return contents = w;
+}
+
+
+static int _query_total_objects()
+{
+  if ( last_content_change == LastObjectCount )
+      return objcount;
+
+  objcount = sizeof( filter_objects( all_inventory(), "short" ) );
+  LastObjectCount = last_content_change;
+  return objcount;
+}
+
+
+// diese Funktion sollte von Raeumen natuerlich ueberschrieben werden...
+int MayAddObject( object ob )
+{
+    if (ob) {
+        if ( !ob->short() )
+            return 1; // invis-Objekte duerfen immer
+        
+        if ( ob == ME || environment(ob) == ME)
+            return 1; // objekt ist schon drin
+    }
+    
+    return (QueryProp(P_TOTAL_OBJECTS) < QueryProp(P_MAX_OBJECTS));
+}
+
+
+#define ENV environment
+#define PO previous_object()
+
+int MayAddWeight( int w )
+{
+    int nw, aw;
+
+    // was nix wiegt, passt
+    if ( w <= 0 )
+        return 0;
+
+    nw = w; // Gewicht im neuen Container
+    aw = 0; // Gewicht fuer das env()
+
+    // Von Raum in Behaelter im Spieler: Container sieht volles Gewicht (nw=w),
+    // Check im env() (Spieler) mit reduziertem Gewicht.
+    if ( ENV() && PO && ENV(PO) != ENV() )
+        aw = QueryProp(P_WEIGHT_PERCENT) * w / 100 || 1;
+
+    if ( PO && ENV(PO) && ENV(ENV(PO)) ){
+        // Wenn das Objekt ein env() hochbewegt wird, wird das Gewicht im alten
+        // Container abgezogen. Weiterer Check im env() ist unnoetig (aw=0).
+        if ( ENV(ENV(PO)) == ME )
+            nw = w - (int)ENV(PO)->QueryProp(P_WEIGHT_PERCENT) * w / 100;
+        // Eine Ebene tiefer bewegen: Test im neuen Container mit unveraendertem
+        // Gewicht; Check im env() mit um Gewichtsreduktion verringertem Gewicht
+        else if ( present( ME, ENV(PO) ) )
+            aw = QueryProp(P_WEIGHT_PERCENT) * w / 100 - w;
+        // Auf derselben Ebene verschieben (von Paket in Beutel):
+        // Neuer Container sieht volles Gewicht (nw=w), Check im env() mit
+        // Differenz aus Gewicht in Container1 und in Container2.
+        else if ( ENV(ENV(ENV(PO))) && ENV() == ENV(ENV(PO)) )
+            aw = QueryProp(P_WEIGHT_PERCENT) * w / 100
+                - ((int)ENV(PO)->QueryProp(P_WEIGHT_PERCENT) * w / 100 || 1);
+    }
+
+    if ( query_weight_contents() + nw > QueryProp(P_MAX_WEIGHT) )
+        // Container kann Gewicht nicht mehr aufnehmen
+        return -1;
+    
+    if ( aw && ENV()->MayAddWeight(aw) < 0 )
+        // Umgebung des Containers kann Gewicht nicht mehr aufnehmen
+        return -2;
+    
+    return 0;
+}
+
+
+/* Redefine PreventInsert() to prevent inserting of special objects. If
+ * a value greater 0 is returned the object ob can't be inserted in the
+ * container.
+ */
+public int PreventInsert( object ob ) { return 0; }
+public int PreventLeave( object ob, mixed dest ) { return 0; }
+public int PreventInsertLiving( object ob ) { return 0; }
+public int PreventLeaveLiving( object ob, mixed dest ) { return 0; }
+public void NotifyInsert(object ob, object oldenv) { }
+
+// **** local property methods
+static int _query_total_weight() 
+{ 
+    return QueryProp(P_WEIGHT) +
+        (QueryProp(P_WEIGHT_PERCENT) * query_weight_contents() / 100); 
+}
+
+
+// Hilfsfunktion
+static int _behalten( object ob, string uid )
+{
+    return (string)ob->QueryProp(P_KEEP_ON_SELL) == uid;
+}
+
+
+/*
+ *
+ * get a list of all contained objects matching a complex description
+ *
+ */
+
+#define POS_INVERS   0x01  /* nur zur funktionsinternen Verwendung */
+#define POS_LETZTES  0x02  /* nur zur funktionsinternen Verwendung */
+
+object *present_objects( string complex_desc )
+{
+    int i;          // Zaehlervariable
+    int meth;       // 0x01 = invers?,  0x02 = letztes?
+    object ob;      // einzelnes temporaeres Objekt
+    object *obs;    // liste der ausgewaehlten Objekte
+    object *erg;    // zum sammeln bis es nachher zurueckgegeben wird...
+    string *strlst; // liste aller Gruppen die mit AND verknuepft sind
+    object haufen;
+
+    strlst = allocate(2);
+    
+    if ( sscanf( complex_desc, "%s ausser %s", strlst[0], strlst[1]) == 2 ){
+        erg = present_objects( strlst[0] );
+        obs = present_objects( strlst[1] );
+        return erg-obs;
+    }
+    
+   strlst = explode( complex_desc, " und " );
+   erg = ({});
+   
+   for ( i = sizeof(strlst); i--; )
+   {
+       complex_desc = strlst[i];
+       // auf letzte/letztes/letzten pruefen...
+       if ( complex_desc[0..5] == "letzte" ){
+           switch( complex_desc[6..6] ){
+           case " ":
+               meth |= POS_LETZTES;
+               complex_desc = complex_desc[7..];
+               break;
+           case "s":
+           case "n":
+           case "m":
+           case "r":
+               if ( complex_desc[7..7] != " " )
+                   break;
+               meth |= POS_LETZTES;
+               complex_desc = complex_desc[8..];
+               break;
+           default:
+           }
+       }
+
+       // auf verneinung pruefen
+       if ( complex_desc[0..5] == "nicht " ){
+           meth |= POS_INVERS;
+           complex_desc = complex_desc[6..];
+       }
+
+       obs=({});
+       // nun nach Main-Ids (Gruppen) suchen...
+       if ( meth & POS_LETZTES )
+       { // geht es nur um den letzten Gegenstand?
+           switch( complex_desc ){
+           case "waffe":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_WEAPON_TYPE );
+               break;
+               
+           case "ruestung":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_ARMOUR_TYPE );
+               break;
+          
+           case "kleidung":
+               obs = filter_objects( all_inventory(), "IsClothing");
+               break;
+               
+           case "verschiedenem":
+           case "verschiedenes":
+               obs = all_inventory();
+               obs -= filter_objects( obs, "QueryProp", P_WEAPON_TYPE );
+               obs -= filter_objects( obs, "QueryProp", P_ARMOUR_TYPE );
+               obs -= filter_objects( obs, "IsClothing");
+               obs -= filter( obs, #'living/*'*/ );
+               break;
+        
+           case "eigenes":
+           case "meins":
+               if (objectp(haufen=present("\nhaufen "+
+                     this_player()->name(WEM)))) {
+                  obs = all_inventory(haufen);
+               }
+               // kein break;, Fall-through!
+           case "behaltenem":
+           case "behaltenes":
+               obs += filter( all_inventory(), "_behalten", ME,
+                                   getuid(this_player() || previous_object()) );
+
+               obs += (QueryProp(P_ARMOURS) || ({}))
+                   + ({ QueryProp(P_WEAPON) }) - ({ 0 });
+                   break;
+                   
+           case "gegenstand":
+               obs = all_inventory() -
+                   filter( all_inventory(), #'living/*'*/ );
+               break;
+               
+           default:
+               obs = filter_objects( all_inventory(), "id", complex_desc );
+           }
+
+           // unsichtbare objekte entfernen
+           obs = filter_objects( obs, "short" );
+
+           // letzten Gegenstand raussuchen
+           obs = obs[0..0]; 
+       } // if (letzter Gegenstand)
+       else
+       { // ganze Gruppen und nicht nur das letzte Objekt
+           switch ( complex_desc )
+           {
+           case "allem":
+           case "alles":
+           case "jeden gegenstand":
+           case "jedem gegenstand":
+           case "gegenstaende":
+           case "alle gegenstaende":
+           case "allen gegenstaenden":
+               if ( meth & POS_INVERS )
+                   continue; // alles nicht = nichts :)
+               
+               obs = all_inventory() -
+                   filter( all_inventory(), #'living/*'*/ );
+               break;
+               
+           case "waffen":
+           case "jede waffe":
+           case "jeder waffe":
+           case "alle waffen":
+           case "allen waffen":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_WEAPON_TYPE );
+               break;
+               
+           case "ruestungen":
+           case "jede ruestung":
+           case "jeder ruestung":
+           case "alle ruestungen":
+           case "allen ruestungen":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_ARMOUR_TYPE );
+               break;
+ 
+           case "kleidung":
+           case "jede kleidung":
+           case "jeder kleidung":
+           case "alle kleidung":
+           case "allen kleidung":
+               obs = filter_objects( all_inventory(), "IsClothing");
+               break;
+              
+           case "gegenstand":
+               obs = filter_objects( all_inventory() -
+                                     filter( all_inventory(),
+                                                   #'living/*'*/ ),
+                                     "short" )[0..0];
+               break;
+               
+           case "verschiedenem":
+           case "verschiedenes":
+               obs = all_inventory();
+               obs -= filter_objects( obs, "QueryProp", P_WEAPON_TYPE );
+               obs -= filter_objects( obs, "QueryProp", P_ARMOUR_TYPE );
+               obs -= filter_objects( obs, "IsClothing");
+               obs -= filter( obs, #'living/*'*/ );
+               break;
+       
+           case "eigenes":
+           case "eigenem":
+           case "eigenen":
+           case "meins":
+           case "alles eigene":
+               if (objectp(haufen=present("\nhaufen "+
+                       this_player()->name(WEM)))) {
+                  obs = all_inventory(haufen);
+               }
+               // kein break;, Fall-through!
+           case "behaltenem":
+           case "behaltenen":
+           case "behaltenes":
+           case "alles behaltene":
+               obs += filter( all_inventory(), "_behalten", ME,
+                                   getuid(this_player() || previous_object()) );
+
+               obs += (QueryProp(P_ARMOURS) || ({}))
+                   + ({ QueryProp(P_WEAPON) }) - ({ 0 });
+                   break;
+                   
+           default:
+               if ( complex_desc[0..3] == "jede" ||
+                    complex_desc[0..4] == "alle ")
+               {
+                   if ( complex_desc[4..4] == " " )
+                   {
+                       obs = filter_objects( all_inventory(), "id",
+                                             complex_desc[5..] );
+                       break;
+                   }
+                   else
+                   {
+                       switch( complex_desc[4..5] )
+                       {
+                       case "m ":
+                       case "r ":
+                       case "n ":
+                       case "s ":
+                           obs = filter_objects( all_inventory(), "id",
+                                                 complex_desc[6..] );
+                           break;
+                           
+                       default:
+                           obs = 0;
+                       }
+                       if (obs)
+                           break;
+                   }
+               }
+
+               // Der Normalfall: einzelne ID...
+               ob = present( complex_desc, ME );
+               // Achtung: dieser Teil setzt das for() fort (continue) und
+               // umgeht dabei die Pruefung auf Sichtbarkeit nach dem Ende vom
+               // switch(). Aus diesem Grunde muss hier selber geprueft
+               // werden.
+               if ( meth & POS_INVERS )
+               {
+                   if ( ob && ob != ME )
+                       erg += (filter_objects( all_inventory(), "short" )
+                               - ({ ob }) );
+                   else
+                       erg += filter_objects( all_inventory(), "short" );
+               }
+               else if ( ob && ob != ME && !ob->QueryProp(P_INVIS) )
+                   erg += ({ ob });   //Normalfall: einzelne ID
+
+               continue;
+
+           }  // switch
+           // unsichtbare objekte entfernen
+           obs = filter_objects( obs, "short" ); 
+       } // else
+
+       if ( meth & POS_INVERS )
+           erg += ( filter_objects( all_inventory(), "short" ) - obs );
+       else
+           erg += obs;
+   } // for
+   return erg;
+}
+
+/*
+ * returns a list of all found objects inside itself
+ * may call same function in objects inside
+ *
+ * Funktion wird nicht mehr von put_and_get aufgerufen, stattdessen wird
+ * direkt present_objects benutzt!
+ */ 
+object *locate_objects( string complex_desc, int info ) {
+    string was, wo;
+
+    if ( sscanf( complex_desc, "%s in %s", was, wo ) == 2 ){
+      object *found_obs = ({});
+      foreach(object invob: present_objects(wo)) {
+        // || ({}) weil invob ein Objekt ohne locate_objects() sein koennte.
+        found_obs += (object *)invob->locate_objects( was, info) || ({});
+      }
+      return found_obs;
+    }
+    // kein "in" gefunden
+    return present_objects( complex_desc );
+}
+
diff --git a/std/corpse.c b/std/corpse.c
new file mode 100644
index 0000000..8e4d8ae
--- /dev/null
+++ b/std/corpse.c
@@ -0,0 +1,727 @@
+// MorgenGrauen MUDlib               
+//
+// corpse.c -- corpse standard object
+//
+// $Id: corpse.c 9391 2015-12-07 22:28:53Z Arathorn $
+
+// A corpse.
+// 
+// Ekeltexte by Boing
+//
+// This is a decaying corpse. It is created automatically
+// when a player or monster dies.
+//
+// Erweiterung Raschaua:
+// - Kopieren von P_CLASS
+// - Teilweises aufessen
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma pedantic
+
+//#define NEED_PROTOTYPES
+
+inherit "std/container";
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <living/life.h>
+#include "/secure/scoremaster.h"
+
+#define CORPSE_OBJ "/std/corpse.c"
+#define PILE_OBJ "/std/pile.c"
+
+nosave int _decay = 4;
+nosave string _name = "Unbekannt";
+nosave object moerder;
+private nosave int gespottet = 0;
+nosave int nahrung_gesamt = 1;
+nosave int nahrung_aktuell = 1;
+// Spielerleiche?
+private nosave status spielerleiche;
+
+void Identify( object ob );
+void do_decay();
+int QueryDecay();
+static int mampf( string str );
+static int spott(string str);
+void ChannelMessageJeer( mixed sender, string text, int flag );
+object _channel( object ob );
+void transform_into_pile();
+
+void create()
+{
+  if (object_name(ME) == __FILE__[0..<3]) {
+    set_next_reset(-1);
+  }
+  ::create();
+  if( clonep(this_object()) )
+  {
+    AddId(({ "leiche","\nleiche","rest","ueberrest"}));
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Leiche" );
+    SetProp( P_MATERIAL, ([ MAT_MISC_DEAD: 100 ]) );
+    SetProp( P_CORPSE_DECAY_TIME, 30 );
+    SetProp(P_NO_SCORE,1);
+    AddCmd(({"iss","verspeise"}),"mampf");
+    AddCmd("spotte","spott");
+    SetProp(P_ARTICLE, 1);
+  }
+  else
+    SetProp( P_ARTICLE, 0 );
+}
+
+/* Damit die Leiche nicht voll wird... */
+int MayAddWeight( int weight ) { return 0; }
+int MayAddObject( object ob ) { return 1; }
+
+
+/* Humni, 2004-06-16: Entstatict */
+string _query_leichen_name()
+{   return _name;   }
+
+/* Uebernehme den Namen von ob */
+void Identify( object ob )
+{
+    closure cl;
+    int i;
+    string info;
+
+    spielerleiche = query_once_interactive(ob);
+    cl=symbol_function("QueryProp", ob);
+    _name = (string) ob->name(WESSEN,0);
+    SetProp( P_SHORT, "Die Leiche "+ _name );
+    SetProp( P_LONG, "Du siehst die sterblichen Ueberreste "+ _name + ".\n" );
+    _decay = 4;
+    SetProp( P_ORIG_NAME, to_string(ob->name(RAW))      );
+    SetProp( P_PILE_NAME, to_string(ob->name(WEM))      );
+    AddId( "\nleiche "+ QueryProp(P_PILE_NAME)          );
+    SetProp( P_KILLER,        (object)   funcall(cl, P_KILLER)        );
+    SetProp( P_ATTRIBUTES,    (mapping)  funcall(cl, P_ATTRIBUTES)    );
+    SetProp( P_LAST_DAMTYPES, (string *) funcall(cl, P_LAST_DAMTYPES) );
+    SetProp( P_XP,     to_int(funcall(cl, P_XP))        );
+    SetProp( P_SIZE,   to_int(funcall(cl, P_SIZE))      );
+    SetProp( P_LEVEL,  to_int(funcall(cl, P_LEVEL))     );
+    SetProp( P_MAX_HP, to_int(funcall(cl, P_MAX_HP))    );
+    SetProp( P_RACE,   to_string(funcall(cl, P_RACE))   );
+    SetProp( P_CLASS,  (string *)(funcall(cl, P_CLASS)) );
+    SetProp( P_HANDS,  (mixed *) funcall(cl, P_HANDS)   );
+    SetProp( P_WEIGHT, (mixed *) funcall(cl, P_WEIGHT)  );
+    SetProp( P_ALIGN,  to_int(funcall(cl, P_ALIGN))     );
+    SetProp( P_ORIG_FILE_NAME, object_name(ob)          );
+    SetProp( P_HEAL,   to_int(funcall(cl, P_HEAL))      );
+    
+    // Saettigung der Leiche berechnen
+    // P_WEIGHT und P_SIZE werden verwendet, Mittel bilden
+    // Ein Saettigungspunkt fuer (konservative Rechnung):
+    //           5 kg
+    //          12 cm
+    // Beispiele:
+    //          Mensch, 1.80 m gross, 75 kg schwer:
+    //          180/12 + 75/5 = 15 + 15 = 30
+    //          Kuh, 1.50 m gross, 500 kg schwer:
+    //          150/12 + 500/5 = 12 + 100 = 112
+    //          Drache, 5 m gross, 1500 kg schwer:
+    //          500/12 + 1500/5 = 41 + 300 = 341
+    nahrung_gesamt = QueryProp(P_WEIGHT)/5000 + QueryProp(P_SIZE)/12;
+    if (nahrung_gesamt <= 0)
+      nahrung_gesamt = 1;
+    nahrung_aktuell = nahrung_gesamt;
+
+    if ( intp(i = funcall(cl, P_CORPSE_DECAY_TIME)) && i > 2 )
+        SetProp( P_CORPSE_DECAY_TIME, i );
+
+    call_out( "do_decay", QueryProp(P_CORPSE_DECAY_TIME) );
+
+    if ( !query_once_interactive(ob) )
+        moerder = (object) "/std/corpse"->_channel(ob);
+}
+
+public int IsPlayerCorpse() { return spielerleiche; }
+
+public string QueryHealInfo()
+{
+  string info = "Solltest Du auf die aberwitzige Idee verfallen, diese "
+    "Leiche zu essen, ";
+  switch( QueryProp(P_HEAL) )
+  {
+    case -__INT_MAX__..-100:
+      info += "wird Dir das erheblichen Schaden zufuegen.";
+      break;
+    case -99..-11:
+      info += "ist das ausgesprochen ungesund fuer Dich.";
+      break;
+    case -10..-1:
+      info += "wird Dir sicher ein wenig uebel werden.";
+      break;
+    case 0..5:
+      info += "wird Dir nichts schlimmes passieren.";
+      break;
+    case 6..__INT_MAX__:
+      info += "koenntest Du sogar ein wenig geheilt werden.";
+      break;
+  }
+  return info;
+}
+
+private string knabber_text()
+{
+  string txt;
+  switch (nahrung_aktuell * 4 / nahrung_gesamt) {
+  case 4:
+    // Leiche noch komplett
+    txt = "";
+    break;
+  case 3:
+    // noch maximal 99%
+    txt = " mit Knabberspuren uebersaete";
+    break;
+  case 2:
+    // noch maximal 74%
+    txt = " angefressene";
+    break;
+  case 1:
+    // noch maximal 49%
+    txt = " schon halb aufgefutterte";
+    break;
+  default:
+    // noch maximal 24%
+    txt = " total abgenagte";
+    break;
+  }
+  return txt;
+}
+
+static string _query_short()
+{
+  string txt;
+  switch(_decay) {
+  case 4:
+    txt = "Die"+knabber_text()+" Leiche ";
+    break;
+  case 3:
+    txt = "Die bereits ziemlich stinkende"+knabber_text()+" Leiche ";
+    break;
+  case 2:
+    txt = "Die schimmelnde und halbverweste"+knabber_text()+" Leiche ";
+    break;
+  case 1:
+    txt = "Die verfaulten"+(sizeof(knabber_text()) ?
+                            "n":"")+" Einzelteile ";
+    break;
+  }
+  txt += _name;
+  return txt;
+}
+
+void do_decay()
+{
+    _decay -= 1;
+    
+    if ( _decay > 0 ) 
+        {
+          if ( (_decay == 2) && (QueryProp( P_HEAL ) >= -4) )
+            SetProp( P_HEAL, -4 );
+            call_out( "do_decay", QueryProp(P_CORPSE_DECAY_TIME) );
+            return;
+        }
+   
+    transform_into_pile();
+    remove();
+}
+
+/* Haeh? Das move ins env macht der Master im prepare_destruct() doch eh? */
+/*
+varargs int remove()
+{
+    // units bekommen eine Chance zur Vereinigung...
+    filter_objects( all_inventory(ME), "move", environment(ME), M_SILENT );
+    return ::remove();
+}
+*/
+
+int QueryDecay() { return _decay; }
+
+static int * _query_nahrung()
+{   return ({nahrung_aktuell, nahrung_gesamt});   }
+
+private string mampf_heilung(int wieviel)
+{
+  int heal;
+  string msg;
+  heal = QueryProp(P_HEAL);
+  // Anteil an Gesamtheilung ausrechnen
+  heal = heal * wieviel / nahrung_gesamt;
+  if ( ( heal ) < 0 ) {
+    // 
+    this_player()->do_damage( random(-heal), this_object() );
+    msg = "Buah, diese Leiche war zweifellos nicht besonders gesund.\n";
+  } else {
+    this_player()->heal_self( random(heal) );
+    msg = "Hmmm, lecker!\n";
+  }
+  return msg;
+}
+
+static int mampf( string str )
+{
+    int gegessen;
+
+    _notify_fail("Was moechtest Du essen?\n");
+    
+    if ( !str || !id(str) )
+        return 0;
+
+    if (this_player()->QueryProp(P_GHOST))
+    {
+	_notify_fail("Das wuerde durch Dich hindurch fallen.\n");
+	return 0;
+    }
+
+    // Das folgende ist nicht ganz sauber, da die Staerke bei eat_food nicht
+    // unbedingt dem Saettigungswert entsprechen muss (wegen FOOD_VALUE).
+    gegessen = (int) this_player()->QueryProp(P_MAX_FOOD) -
+      (int) this_player()->QueryProp(P_FOOD);
+    if (gegessen <= 0) {
+      // Spieler ist proppenvoll, Meldung ausgeben
+      gegessen = 0;
+      write("Du bekommst von der Leiche nicht einen Bissen mehr runter.\n");
+    } else if (gegessen >= nahrung_aktuell) {
+      // spieler kann die gesamte Leiche essen, also entfernen.
+      this_player()->eat_food(nahrung_aktuell);
+      // Verdammt. eat_food() kann TP umgebracht haben und im Falle eines NPC
+      // ist der dann weg.
+      if (objectp(this_player())) {
+	write(mampf_heilung(nahrung_aktuell));
+	say( sprintf("%s wird von %s voll Hingebung verspeist.\n",
+                   capitalize(name(WER)),
+                   (string) this_player()->name(WEM)) );
+      }
+      transform_into_pile();
+      remove();
+    } else {
+      // Auch teilweise Verspeisung ist moeglich, nahrung_aktuell anpassen
+      this_player()->eat_food(gegessen);
+      if (objectp(this_player())) {
+	write(mampf_heilung(gegessen)+"Leider bist Du nicht in der Lage,"
+               " alles aufzuessen.\n");
+      say( sprintf("%s knabbert an %s herum.\n",
+                   (string) this_player()->name(),
+                   capitalize(name(WEM)) ) );
+      }
+      nahrung_aktuell -= gegessen;
+    }
+    return 1;
+}
+
+
+static int spott( string str )
+{
+    if ( !str )
+        return _notify_fail( "Syntax: spotte <text>\n" ), 0;
+    
+    if ( !objectp(moerder) )
+        return _notify_fail( "Dieses Opfer ist den Spott nicht wert.\n" ), 0;
+    
+    if ( PL != moerder )
+        return _notify_fail( "Du kannst nur Deine eigenen Opfer "
+                             +"verspotten!\n" ), 0;
+
+    if ( gespottet )
+        return _notify_fail( "Du kannst Dein Opfer nur einmal verspotten!\n" )
+            , 0;
+
+    if ( _decay < 4 )
+        return _notify_fail( "Da solltest Du schon etwas spontaner sein.\n" )
+            , 0;
+
+    str = (string) PL->_unparsed_args();
+    
+    switch ( str[0] )
+        {
+        case ':':
+            "/std/corpse"->ChannelMessageJeer( PL, str[1..], MSG_EMOTE );
+            break;
+          
+        case ';':
+            "/std/corpse"->ChannelMessageJeer( PL, str[1..], MSG_GEMOTE );
+            break;
+          
+        default:
+            "/std/corpse"->ChannelMessageJeer( PL, str, MSG_SAY );
+            break;
+        }
+  
+  gespottet = 1;
+  write( "Du verspottest Dein totes Opfer.\n" );
+  
+  return 1;
+}
+
+
+void ChannelMessageJeer( mixed sender, string text, int flag )
+{
+  if (member(inherit_list(previous_object()),CORPSE_OBJ)>-1)
+      CHMASTER->send( "Moerder", sender, text, flag );
+}
+
+
+// _channel() --
+#define KILL_MESSAGES \
+"Jetzt hat mich doch glatt %s erwischt :(", \
+"Wie soll das noch enden, %s?", \
+"Unglaublich, wie hat %s das geschafft?", \
+"RACHE! Bringt %s um!", \
+"%s ist staerker als ich dachte.", \
+"Wenn ich Dich erwische, %s!", \
+"%s hat mich ermordet!", \
+"Welche Schmach! %s hat mich besiegt.", \
+"Huetet Euch vor %s!", \
+"Warum hat mir niemand gegen %s geholfen?", \
+"%s, Du Monsterschaender!", \
+"Monster aller Regionen, vereinigt Euch gegen %s!", \
+"Danke, %s, Du hast mich von dieser schrecklichen, " \
+"sterblichen Huelle befreit.", \
+"%s? Ich bin dann jetzt weg.", \
+"Macht Euch keine Muehe, %s hat mich mal kurz zu " \
+"Gevatter Tod geschickt.", \
+"Oh nein, %s hat mich schon wieder kaltgemacht!", \
+"Sheriff! Verhafte %s!", \
+"Hilfe! Diebe! Moerder! Halsabschneider! %s!", \
+"Wieder mal an %s gestorben, find ich albern!", \
+"%s, probier das bei Anne!", \
+"Und wieder ein Drittel Erfahrung weg wegen %s...", \
+"Oh waer ich doch ein Spieler, dann duerfte mich "\
+"%s nicht mehr toeten!", \
+"Wenn Du das noch oefter machst, %s, dann spiele "\
+"ich hier nicht mehr!", \
+"%s, wieso redet der Kerl hier in Grossbuchstaben "\
+"zu mir? CAPS-LOCK kaputt?", \
+"%s ist schuld!",\
+"So schaff ich's wohl nie, Seher zu werden, %s :(",\
+"Musste das WIRKLICH sein, %s?",\
+"Wuenscht mir ne kurze Todessequenz, ich hab sie "\
+"%s zu verdanken...",\
+"Naechstes Mal bring mir lieber Post statt mich "\
+"zu ermorden, %s!",\
+"%s, wieso sagt der Typ: KOMM MIT MIR, STERBLICHER?",\
+"Ich glaub, jetzt kenne ich ALLE Todessequenzen, %s!",\
+"Was, %s hat mir WIE VIELE Lebenspunkte abgezogen???",\
+"Verdammt, %s wollte nicht sterben...",\
+"Tod den Monsterschaendern! Straft %s!",\
+"Naechstes Mal werde ICH siegen, %s!",\
+"Findest Du das fair, %s?",\
+"Das war ein gemeiner Tiefschlag, %s!",\
+"Wenn ich mal gross bin, zahl ich Dir das alles "\
+"heim, %s!",\
+"ALAAAAAARM! %s IST AUSGEBROCHEN!",\
+"Ein Koenigreich fuer den, der mir den Kopf von "\
+"%s bringt!",\
+"Suche Killer, zahle gut, Zielperson %s.",\
+"Das sag ich alles Boing, %s!",\
+"Das sag ich alles Rumata, %s!",\
+"Das sag ich alles Jof, %s!",\
+"Das sag ich alles dem Sheriff, %s!",\
+"Komm Du mir nur wieder, %s!",\
+"Ich fordere Revanche, %s!",\
+"Sheriff! Unternimm was gegen %s!",\
+"%s, wer hat DICH denn eingeladen?",\
+"NAECHSTES MAL NEHM ICH DICH AUCH MIT, %s!",\
+"Haetten wir das nicht ausdiskutieren koennen, %s?",\
+"Dich merk ich mir, %s!",\
+"Na prima, schon wieder dieser dunkle Typ! Und "\
+"alles nur wegen %s.",\
+"Hallo? Hat jemand meinen Koerper gesehen? Muesste "\
+"irgendwo bei %s liegen!",\
+"%s, waer jetzt nicht eigentlich Vorlesung?",\
+"Lern lieber, statt unschuldige Monster zu er"\
+"morden, %s!",\
+"Ohne dieses Lag haette mich %s nicht umgebracht!",\
+"In spaetestens einer Stunde komme ich wieder, %s!",\
+"Na warte %s, beim naechsten Mal trete ich Dir "\
+"kraeftig in den Gedan-Bereich!",\
+"SCHIEBUNG, der letzte Schlag von %s war nicht "\
+"regelgerecht!",\
+"Lieber eine H2O-Vergiftung, als von %s ermordet "\
+"zu werden!",\
+"Die Ehre werd ich %s nicht antun, und hier "\
+"herumposaunen, wer mich umgenietet hat!",\
+"Is mir doch egal %s, ich glaube an "\
+"die Wiedergeburt!",\
+"Aber vorsicht, %s isch kuuul maen!",\
+"Ok, %s, Du wirst von meiner Erwarteliste gestrichen!",\
+"Das naechste Mal sieht man unsere beiden Namen auf [Tod:], %s!",\
+"Magier zu mir, hier ist ein Bug: %s ist immer noch nicht tot!",\
+"Morgen frueh um halb sechs in der Arena, %s! Ich hab die Wahl der Waffen.",\
+"%s ist schlimmer als das Mensaessen. Absolut toedlich.",\
+"Mist, meine Vorsicht hat versagt! Und das bei %s!",\
+"%s, die Hydra ist mir lieber...",\
+"So, meine Ignoriereliste ist schon wieder um einen Namen laenger, %s."
+
+#define KILL_MESSAGES2 \
+"Feierabend! Danke, %s!",\
+"Toll! GENAU das hab ich jetzt gebraucht! Super, %s! :(",\
+"Schon wieder ein Tag versaut wegen %s!",\
+"Ihr sagt, ICH sei ein Monster? Ihr kennt %s nicht!!!",\
+"Sperrt %s mal in's Polargebiet! Gaebe ein gutes Untier ab...",\
+"Freiheit fuer Nicht-Spieler! Vernichtet %s!",\
+"Merlin, mach %s zum Magier!",\
+"%s ist NICHT zu meiner Beerdigung eingeladen!",\
+"Ich hab keine Angst vor Dir, %s!",\
+"%s ist doof und stinkt!",\
+"Moegest Du im Lag verrotten, %s!",\
+"%s wird gleich das Spiel verlassen, um meiner Rache zu entgehen!",\
+"Der Todestag von %s wird fuer mich ein Feiertag sein!",\
+"%s ist ein Metzelskript, ich hab's genau gesehen!", \
+"Waer ich Tanjian, haettest Du das nicht geschafft, %s!", \
+"Das naechste Mal komme ich als Kaempfer wieder, %s!", \
+"Ich lass' mich aufwerten und werde Zauberer, %s!", \
+"Gildenbalance! %s ist viel zu stark!", \
+"Jetzt ist mir das Kleingeld zum Tanken ausgegangen, %s...", \
+"%s hat ein Zap-Tool!", \
+"%s, Du <zensiert>!", \
+"%s, das ist doch Hullepanz. (c) Catweazle", \
+"Bald bin ich SuperSeher, %s!", \
+"Mit Dir 'piel ich nich' mehr, %s, Du hast in mein Foermssen depinkelt!", \
+"Verdammt, %s muss mir den Feenring geklaut haben...", \
+"Nie ist ein Kleriker in der Naehe, wenn man ihn braucht, %s...", \
+"Menno, %s, wo war hier noch mal die Heilstelle?", \
+"Hoffentlich werde ich naechstes Mal als Kuh wiedergeboren, %s...", \
+"Wenn Du sonst noch Probleme hast, %s, geh' zu Marvin und red' " \
+"mit ihm darueber.", \
+"Du, %s, Gewalt ist keine Loesung, lass uns darueber diskutieren.", \
+"Das naechste Mal zeig' ich Dir meine Mateteesammlung, %s.", \
+"%s eunt domum!", \
+"%s, ist es so kalt hier oder bin ich das?", \
+"Ich war schon wieder zur falschen Zeit am falschen Ort, %s...", \
+"Danke, %s. Im Namen des Mondes werde ich Dich bestrafen!", \
+"Das war mal wieder ein Schuss in den Ofen, %s...", \
+"Kannst Du das mit Deinem Gewissen vereinbaren, %s?", \
+"Und Kandri sprach: Mein ist die Rache, %s!", \
+"%s, Du haettest meine Leiche nicht auch noch schaenden muessen...", \
+"%s, ich kann so nicht arbeiten!", \
+"%s, ich geb' Dir mal 2 Muenzen, dann kannst Du Deine Sorgen " \
+"jemand anderem erzaehlen...", \
+"Manchmal verspeist Du den Baer, %s, manchmal verspeist " \
+"der Baer Dich...", \
+"Das naechste Mal betrittst _Du_ die Welt des Schmerzes, %s!", \
+"Wenn ich wiederkomme, %s, werde ich erst Dich toeten, dann " \
+"Deinen Partner, dann Deine Brueder und Schwestern und jeden " \
+"der Dich kennt!", \
+"Oh nein, %s! Schon wieder diese ewig lange Todessequenz mit der " \
+"Drachenschule!", \
+"%s, wo bekomme ich jetzt einen neuen Koerper her?", \
+"Lass Dir meine Leiche schmecken, %s.", \
+"Haeh?", \
+"Ja, aber wieso das denn? Och menno, %s!", \
+"So ein Mist, %s, ich hatte 'nen Disconnect...", \
+"*winkewinke*, %s!", \
+"Heute ist nicht alle Tage, ich komm' wieder, keine Frage.", \
+"Lass das, %s!", \
+"Niemand! Niemand nennt mich eine feige Sau, %s!", \
+"Da hab' ich jetzt wohl verloren, %s...", \
+"%s, ich kann nicht metzeln, ich kann nur sterben...", \
+"Hattest Du ueberhaupt einen Termin, %s?", \
+"Du warst gar nicht an der Reihe, %s!", \
+"Aetsch, %s, der Kill zaehlt nicht!", \
+"Jetzt noch mal mit Gefuehl, %s.", \
+"Tschuess erstmal, %s.", \
+"Ich weiss nicht ob Du es gemerkt hast, %s, aber " \
+"ich bin schon tot...", \
+"%s, ich haette nicht gedacht, dass ich noch mal sterbe " \
+"bevor Taramis angeschlossen wird...", \
+"Ich wollte sowieso grade gehen, %s.", \
+"Shit happens, %s.", \
+"Dazu faellt mir jetzt nix mehr ein, %s."
+
+#define KILL_MESSAGES3 \
+"Was heisst denn \"Dein Teddy knuddelt Dich ein letztes Mal\", %s?", \
+"Ich bin beeindruckt, %s.", \
+"Ok, Dein Gesicht merke ich mir, %s!", \
+"Kaum da, haut mich %s schon wieder tot... *grummel*", \
+"Ich werde Dich verfolgen, %s! Ich werde in Deine Traeume eindringen " \
+"und Dich in den Wahnsinn treiben! Du wirst diesen Tag noch verfluchen!", \
+"Ich geh ja schon, %s.", \
+"Auf den ersten Blick sieht %s gar nicht so gefaehrlich aus.", \
+"Von %s besiegt - wie peinlich.", \
+"Mist. Einmal nicht aufgepasst, schon wird man von %s ermordet.", \
+"Das war ein Glueckstreffer, %s!", \
+"Hah, das ist doch nur eine Fleischwunde! Gibst Du etwa schon auf, %s?", \
+"%s macht gerade eine destruktive Phase durch.", \
+"Zum Glueck ist das ja nur ein Spiel, %s. Sonst waere ich jetzt wirklich " \
+"boese.", \
+"Begrabe mich wenigstens anstaendig, %s.", \
+"Hm, die Flatrate brauch' ich dann wohl doch nicht, %s.", \
+"Na warte, %s! Zur Strafe werde ich gleich das Mud crashen!", \
+"Ich sollte nicht mehr soviel mudden. Dann muesste ich mich auch nicht mehr " \
+"ueber %s aergern.", \
+"Auf am Boden Liegende einzuschlagen! Schaem Dich, %s!", \
+"Einigen wir uns auf unentschieden, %s?", \
+"Ich sagte doch \"Ich ergebe mich!\", %s.", \
+"Aeh, %s, koennte das unter uns bleiben? Ich habe schliesslich einen " \
+"Ruf zu verlieren...", \
+"Du bist vielleicht staerker, %s, aber dafuer bin ich schoener.", \
+"Die Rechnung fuer die Behandlung meiner Minderwertigkeitskomplexe geht " \
+"an %s.", \
+"Ab und zu muss man %s ja gewinnen lassen, sonst gibt es wieder Traenen.", \
+"Bis eben war mir %s noch sympathisch.", \
+"Geht's Dir jetzt besser, %s?", \
+"Macht kaputt, was mich kaputt macht! Toetet %s!", \
+"Beim naechsten Mal mach' ich es Dir nicht mehr so einfach, %s!", \
+"Weisst Du, %s, ich habe nur verloren, weil Du... aeh, weil ich... " \
+"aeh... weil Du gewonnen hast.", \
+"Jo, immer feste druff, %s. Ist ja nur ein dummer NPC. *grummel*", \
+"Ich muss sagen, %s kann feste schlagen.", \
+"Also %s! Kommst hier rein, haust mich um und tust dann so, als sei nichts " \
+"gewesen...", \
+"In welcher Gilde konnte man nochmal \"befriede\" lernen, %s?", \
+"Tja, %s, aus diesem Kampf bist Du wohl nur als Vorletzter hervorgegangen" \
+"...", \
+"Vorsicht, %s greift immer von hinten an!", \
+"Was meint %s mit \"Sparringspartner\"?", \
+"Ich habe mir bei Dir nur deshalb keine Muehe gegeben, %s, weil Du eh nicht " \
+"stupst.", \
+"Huch, ich bin ja schon wieder auf -Moerder!", \
+"Ich spuck' Dir in's Auge und blende Dich, %s!", \
+"Huch, wo seid ihr alle hin?", \
+"Wer war das?", \
+"Moegest Du in interessanten Zeiten leben, %s.", \
+"Ich sehe was, was Du nicht siehst, %s, und es ist... schwarz.", \
+"Hey Maedels! Ratet, wen ich gerade getroffen habe!", \
+"%s?", \
+"Noch einmal sowas, %s, und ich lasse Deine Gilde abwerten. Ich kann das!", \
+"Was bedeutet 'Jemand beschwoert Dein Bild herauf.'? Warst Du das, %s?"
+
+private string *moerder_msgs = ({KILL_MESSAGES, KILL_MESSAGES2, KILL_MESSAGES3,
+"Sterben ist schoen, Sterben ist toll - Ach waer ich wie ein Kaempfer voll!",
+"Lauschige Nacht - ich geh mir die Sterne anschauen. Bis spaeter!",
+"Echt %s, das war 0815, keine Kreativitaet beim Toeten!",
+"%s, Du kannst ja gar ni... Verdammt!",
+"Kaempfer, Zauberer, Tanjian, alles Luschen! Ich werde Abenteurer, da ist "
+    "wenigstens Thrill!", 
+"Toeten, Toeten, Toeten. Ist das alles, was Du kannst, %s?",
+"Ach geh doch Bluemchen pfluecken %s!", 
+"Mein Tod war KEIN FP %s... HA!",
+"Lars gibt mir nen Whisky, wenn ich komme. Und Dir?",
+"Geh lernen, Depp!",
+"Auf der naechsten Mud-Party haut Dich mein Magier zurueck!",
+"Lass gut sein, tat eh nicht weh!", 
+"Ich lass Dich von Zook abhaengen, %s!",
+"Koennen wir naechstes Mal nicht lieber einen trinken gehen, %s?",
+"Verdammt, meine neue Gilde ist ja noch gar nicht angeschlossen!",
+"Yohoho und ne Buddel voll Rum!",
+"Beim Kartenspiel haettest Du keine Chance gehabt %s!",
+"%s, Du hast echt keine Ahnung von stilvollem Metzeln...",
+"Was muss ich noch tun fuer Deinen Aufstieg, %s? *seufz*",
+"EK-Poser!",
+"Leistung ist was anderes, %s!",
+"Lass mich leeeeeeb... Arrrrggggh!",
+"Hast Du sonst nix in der Drachenschule gelernt, %s?",
+"Und wer kuemmert sich jetzt um meine Haustiere, %s?",
+"Ich musste sterben, Du willst mich nur beerben. Ich gehe "
+    "mit Stil, Du findest nicht viel.",
+"Ich habe nun mehr Tode als Du, %s. Topp mich, wenn Du kannst!",
+"Ok, dann geh ich halt WOW spielen.",
+"Toll, freut sich der Naechste. Wieder ne Todesfolge, %s.",
+"Du gucktest so traurig, %s, ich machte Dir meinen Tod zum Geschenk. "
+    "Sollst ja auch mal 'n Erfolgserlebnis haben!",
+"Nun reiche ich den letzten Becher, trinke den Wein, erkenne Dein Sein.",
+"%s hat nicht einmal getroffen. Ich habe mich wortwoertlich totgelacht.",
+"Lass mich nachdenken, %s. Ich starb fuer nen EK?",
+"Ich kann nicht glauben, dass das gerade passiert ist!",
+"Du Suender. Du sollst _nicht_ toeten!",
+"%s, mein Testament steckt im rechten Schuh!",
+"Sterben und sterben lassen, mein Freund. Wir sehen uns wieder!",
+"Und ich sag noch, %s... Das ist KEINE Schwertscheide... Aber Du musst "
+  "ja alles selbst ausprobieren.",
+"Soll das jetzt alles gewesen sein, %s?",
+"In meinem Code ist ein Fehler und gleich gibt's 'nen Crash.",
+"Ach, auch egal, %s. Ist eh gleich Schichtwechsel.",
+"Jofverdammt... warst Du gut, %s!",
+"Duelle von Killern enden meist fuer einen toedlich und das wirst "
+  "Du s... Argh!",
+"Bist Du sicher, dass Du den EK bekommen hast, %s?",
+"Danke %s! Weisst Du, ich hatte mit Zook gewettet, wer mich "
+  "toetet. Und ich habe eine Wiedergeburt gewonnen!",
+"Nun bin ich sicher vor Dir, %s!",
+"Ich mache jetzt Urlaub und habe Zook gebeten, meinen Reset "
+  "um einen Monat zu verschieben. Gluecklich, %s?",
+});
+
+
+int _query_kma() { return sizeof(moerder_msgs); }
+
+
+object _channel( object ob )
+{
+  int m_FMM, m_HP, m_WC, m_AC, s_HP, s_WC, s_AC;
+  string msg;
+  object rueck;
+
+  if (member(inherit_list(previous_object()),CORPSE_OBJ)>-1)
+      {
+          string killer;
+          int i, x, y, z, nr;
+          closure m_q, s_q;
+    
+          while( previous_object(i) &&
+                 !query_once_interactive(previous_object(i)) )
+              i++;
+          
+          if( !previous_object(i) || IS_LEARNER(previous_object(i)) )
+              return rueck;
+
+          killer = (string) previous_object(i)->name();
+          
+          if ( lower_case(killer) != getuid(previous_object(i)) )
+              killer = capitalize(getuid(previous_object(i)));
+          
+          m_q = symbol_function( "QueryProp", ob ); // Monster
+          s_q = symbol_function( "QueryProp", previous_object(i) ); // Spieler
+          
+          if ( (m_FMM = (int) funcall( m_q, P_FORCE_MURDER_MSG )) >= 0 )
+              if ( (object_name(ob) == "/obj/shut") ||
+                   (m_FMM > 0) ||
+                   (nr = (random(100) >= 99) ? 1 : 0 ) || 
+                   (nr = ((x = (m_HP = (int) funcall( m_q, P_MAX_HP )) * 
+                           ((m_WC = (int) funcall( m_q, P_TOTAL_WC )) +
+                            (m_AC = (int) funcall( m_q, P_TOTAL_AC ))))
+                          > 200000) ? 2 : 0) ||
+                   (nr = (((y = m_HP * (m_WC + m_AC)) >
+                           (z = 5 * (s_HP = (int) funcall( s_q, P_MAX_HP )) *
+                            ((s_WC = (int) funcall( s_q, P_TOTAL_WC )) +
+                             (s_AC = (int) funcall( s_q, P_TOTAL_AC )))))
+                          ? 3 : 0)))
+                  {
+                      SetProp( P_NAME, "Geist "+(string) ob->name(WESSEN, 0) );
+
+                      if( !(msg = (string) ob->QueryProp(P_MURDER_MSG)) )
+                          msg = moerder_msgs[random(sizeof(moerder_msgs))]; 
+
+		      if ( stringp(msg) )
+			  msg = sprintf( msg, killer || "Moerder" );
+
+                      CHMASTER->send( "Moerder", this_object(), funcall(msg) );
+
+                      rueck = previous_object(i);
+                  }
+          
+          log_file( "moerder.log",
+                    sprintf( "MON(%O) COND(%d) NPC(%d), DIFF(%d,%d)\n",
+                             ob, nr, x, y, z) );
+      }
+
+  return rueck;
+}
+
+void transform_into_pile() {
+	if( environment()->QueryProp(P_PREVENT_PILE) ) return;
+	object* inv = all_inventory();
+	if( sizeof(inv)<2 ) return;
+	object p = clone_object(PILE_OBJ);
+	filter_objects( inv, "move", p, M_SILENT | M_NOCHECK );
+	p->move( environment(), M_SILENT | M_NOCHECK );
+}
diff --git a/std/def_workroom.c b/std/def_workroom.c
new file mode 100644
index 0000000..12034fa
--- /dev/null
+++ b/std/def_workroom.c
@@ -0,0 +1,46 @@
+// MorgenGrauen MUDlib
+//
+// def_workroom.c -- standard workroom for new wizards
+//
+// $Id: def_workroom.c 7423 2010-02-07 22:56:38Z Zesstra $
+
+#pragma strong_types,save_types,rtt_checks
+#pragma pedantic, range_check
+
+inherit "std/room";
+
+#include <properties.h>
+
+void create()
+{
+  string WIZNAME;
+
+  ::create();
+  WIZNAME = capitalize(getuid(this_object()));
+
+  SetProp( P_LIGHT, 1 );
+  SetProp( P_INT_SHORT, "Der Arbeitsraum von "+WIZNAME );
+  SetProp( P_INT_LONG,
+   "Dieses ist der Arbeitsraum von "+WIZNAME+". Es liegen vollgeschriebene\n"
+  +"Zettel auf dem Boden, die Waende sind mit Kreidezeichen zugekritzelt.\n"
+  +"Man merkt, dass dieser Raum dafuer da ist, dass "+WIZNAME+" fuer sich\n"
+  +"ungestoert arbeiten kann. Ein magisches Portal fuehrt in die Welt\n"
+  +"der Sterblichen.\n"
+	  );
+  SetProp(P_INDOORS,1);
+  AddDetail( ({ "portal", "portale" }), 
+	    "Das Portal fuehrt in die Abenteurer-gilde.\n" );
+  AddDetail( ({ "raum", "arbeitsraum" }),
+	     "Du befindest Dich mittendrin.\n" );
+  AddDetail( "zettel",
+	     "Die Zettel sind mit unleserlichen Zeichen vollgeschrieben.\n" );
+  AddDetail( ({ "boden", "fussboden" }),
+	     "Der Fussboden ist bedeckt mit vollgeschriebenen Zetteln.\n" );
+  AddDetail( ({ "wand", "waende" }),
+	     "An den Waenden befinden sich Kreidezeichen.\n" );
+  AddDetail( ({ "kreide", "kreidezeichen", "zeichen" }),
+	    "Die Zeichen ergeben nur fuer " + WIZNAME + " einen Sinn.\n" );
+  AddDetail( "portal",
+	     "Das Portal fuehrt in die Welt der Sterblichen.\n" );
+  AddExit("gilde","in die Gilde#/gilden/abenteurer");
+}
diff --git a/std/food.c b/std/food.c
new file mode 100644
index 0000000..7b75921
--- /dev/null
+++ b/std/food.c
@@ -0,0 +1,732 @@
+// MorgenGrauen MUDlib
+//
+// /std/food.c  - Standardobjekt fuer Lebensmittel
+//
+// $Id: food.c 8248 2012-11-23 21:28:04Z Zesstra $
+#pragma strict_types,save_types,rtt_checks
+#pragma no_clone
+#pragma pedantic,range_check
+
+inherit "/std/thing";
+
+#include <food.h>
+#include <living/life.h>
+#include <break_string.h>
+
+// Standard-IDs und -Adjektive muessen gesichert werden
+#define  ID_BACKUP       "std_food_id_backup"
+#define  ADJ_BACKUP      "std_food_adj_backup"
+
+// Zeitpunkt, an dem die Speise verdorben ist
+private nosave int time_gone_bad;
+
+// Zeitpunkt, an dem die Speise verderben darf
+// wird immer erst gesetzt, wenn ein Spieler hiermit in Beruehrung kommt
+private nosave int time_togo_bad;
+
+// Zeitpunkt, an dem die Speise zerstoert werden darf
+// wird immer erst gesetzt, wenn ein Spieler hiermit in Beruehrung kommt
+private nosave int time_to_remove;
+
+// Zeitpunkt, ab dem die Speise mit einem Spieler in Beruehrung kam
+// bei ewig haltbaren Speisen ist sie immer 0;
+private nosave int start_life = 0;
+
+// Dauer des ersten Resets
+private nosave int first_reset_length = 0;
+
+// Wertet Properties aus (Ersetzungen werden durchgefuehrt)
+protected mixed eval_property(string val,object consumer) {
+  mixed what = QueryProp(val);
+  if (stringp(what)) {
+    what=capitalize(replace_personal(what,({this_object(),consumer}),1));
+  }
+  return break_string(what,78,"",BS_LEAVE_MY_LFS);
+}
+
+// wird nur verwendet, wenn der gesetzte Reset ein Timerende darstellt,
+// um zu garantieren, dass der nicht abgestellt wird.
+// Zugegebenenermassen keine schoener Hack :-/
+private void my_set_next_reset(int zeit) {
+  set_next_reset(zeit);
+  // Damit der Reset auch wirklich ausgefuehrt wird ...
+  call_out(#'id,1);
+}
+
+// Ausgabe von Meldungen an Besitzer oder Raum
+protected void message(string prop) {
+  object env = environment(this_object());
+  if (objectp(env)) {
+    if (interactive(env)) {
+      tell_object(env,
+        break_string(eval_property(prop,env),78,"",BS_LEAVE_MY_LFS));
+    }
+    else if(!living(env)) {
+      tell_room(env,
+        break_string(eval_property(prop,0),78,"",BS_LEAVE_MY_LFS));
+    }
+  }
+}
+
+// Flag, ob die Speise verdorben ist
+public int is_bad() {
+  return time_gone_bad > 0;
+}
+
+// Anzahl der Sekunden, die das Lebensmittel bereits hinter sich hat,
+// seit es mit einem Spieler in Beruehrung kam
+public int get_current_lifetime() {
+  if (!start_life) return 0;
+  return time() - start_life;
+}
+
+// Berechnet den Wert der Speise korrekt
+int _query_value() {
+  int result = 0;
+  if (!is_bad()) {
+    result += to_int(QueryProp(P_PORTIONS)*Query(P_VALUE,F_VALUE));
+  }
+  if (mappingp(QueryProp(P_EMPTY_PROPS))) {
+    result += QueryProp(P_EMPTY_PROPS)[P_VALUE];
+  }
+  return result;
+}
+
+// Berechnet den Wert der Speise korrekt
+int _query_weight() {
+  int result = to_int(QueryProp(P_PORTIONS)*Query(P_WEIGHT,F_VALUE));
+  if (mappingp(QueryProp(P_EMPTY_PROPS))) {
+    result += QueryProp(P_EMPTY_PROPS)[P_WEIGHT];
+  }
+  return result;
+}
+
+// Dauer des ersten Resets wird wieder hinzugerechnet
+// damit wird bei Nichtsetzen der Prop der Standard geliefert
+int _query_std_food_lifetime() {
+  return Query(P_LIFETIME,F_VALUE)+(first_reset_length);
+}
+
+// Setter fuer die Lifetime, damit die Resettime immer mitkorrigiert wird
+// und Dauer des ersten Resets abgezogen werden, da die als Standard immer
+// dazukommen
+protected void set_std_food_lifetime(int val) {
+  if (val > 0) {
+    if (!time_togo_bad && !is_bad()) {
+      Set(P_LIFETIME,val-(first_reset_length),F_VALUE);
+      // Anzahl der Resets richtet sich nach dem Mittelwert
+      Set(P_RESET_LIFETIME,to_int((val-(first_reset_length))
+                           /(__RESET_TIME__*0.75))+1,F_VALUE);
+      set_next_reset(val);
+    }
+  }
+  else {
+    write("Da ist was schief gelaufen! Sprich mal mit einem Magier.\n");
+    raise_error("setting P_LIFETIME to invalid int value: "+
+      to_string(val)+"\n");
+  }
+}
+
+// Anzahl der Resets als Lebenszeit
+void _set_std_food_lifetime_reset(int value) {
+  if (value < 1) value = 1;
+
+  Set(P_RESET_LIFETIME,value,F_VALUE);
+  // per Zufall die Laenge der einzelnen Resets berechnen
+  int length = first_reset_length;
+  foreach(int i: value) {
+    length += to_int((__RESET_TIME__ + random(__RESET_TIME__)) / 2);
+  }
+  set_std_food_lifetime(length);
+}
+
+// Flag, ob Speise "essbar" ist
+public int is_eatable() {
+  return QueryProp(P_FOOD) > 0;
+}
+
+// Flag, ob Speise "trinkbar" ist
+public int is_drinkable() {
+  return QueryProp(P_FOOD) == 0 && QueryProp(P_DRINK) > 0;
+}
+
+// Flag, ob noch Portionen der Speise vorhanden sind
+public int is_not_empty() {
+  return QueryProp(P_PORTIONS) > 0;
+}
+
+// Macht eine Speise leer bzw. zerstoert sie.
+// Muss beim Ueberschreiben immer als letztes aufgerufen werden.
+// Diese Methode wird aufgerufen, wenn ein Behaelter leergefuttert wird
+// oder der Inhalt zerstoert werden soll
+// Rueckgabewert: Ergebnis von remove() bzw. 0, wenn Objekt nicht
+//                zerstoert werden muss
+// Beim Aufruf bitte IMMER das Ergebnis pruefen, damit keine Aufrufe an
+// geloeschten Objekten passieren! if (make_empty()) return ...;
+public int make_empty() {
+  mixed props = QueryProp(P_EMPTY_PROPS);
+  if (mappingp(props)) {
+    foreach(string key:m_indices(props)) {
+      if (key == P_IDS) {
+        RemoveId(QueryProp(P_IDS));
+        AddId(QueryProp(ID_BACKUP));
+        AddId(props[key]);
+      }
+      else if (key == P_ADJECTIVES) {
+        RemoveId(QueryProp(P_ADJECTIVES));
+        AddAdjective(QueryProp(ADJ_BACKUP));
+        AddAdjective(props[key]);
+      }
+      else {
+        SetProp(key,props[key]);
+      }
+    }
+
+    // Sicherheitshalber :-)
+    SetProp(P_PORTIONS,0);
+    return 0;
+  }
+  else {
+    return remove(1);
+  }
+}
+
+// wird aufgerufen, wenn die Speise schlecht war und der Removetimer
+// abgelaufen ist
+// Zerstoert die Speise oder den Inhalt eines Behaelters
+// Rueckgabewert: Ergebnis von remove() bzw. 0, wenn Objekt nicht
+//                zerstoert werden muss
+public int make_destroy() {
+  message(P_REMOVE_MSG);
+  set_next_reset(-1);
+  return make_empty(); // Leermachen oder zerstoeren
+}
+
+// Laesst die Speise schlecht werden (mit Meldungen)
+// muss beim Ueberschreiben immer mit aufgerufen werden
+// Rueckgabewert: Ergebnis von remove() bzw. 0, wenn Objekt nicht
+//                zerstoert werden muss
+// Beim Aufruf bitte IMMER das Ergebnis pruefen, damit keine Aufrufe an
+// geloeschten Objekten passieren! if (make_bad()) return ...;
+public int make_bad() {
+  time_gone_bad = time();
+  if (is_not_empty()) {
+    message(P_BAD_MSG);
+  }
+  if (QueryProp(P_DESTROY_BAD) == DESTROY_BAD && !QueryProp(P_EMPTY_PROPS)) {
+    return remove(1);
+  }
+  return 0;
+}
+
+// Startet den Timer zum Schlechtwerden der Speise
+public void start_lifetime() {
+  // Test, ob Lebensmittel schlecht werden darf
+  if (!QueryProp(P_NO_BAD) && !time_togo_bad) {
+    start_life = time();
+    int zeit = QueryProp(P_LIFETIME);
+     // Zeitpunkt des Schlechtwerdens
+    time_togo_bad = start_life + zeit;
+    // Zeitpunkt des Zerstoerens
+    time_to_remove = time_togo_bad + QueryProp(P_DESTROY_BAD);
+    my_set_next_reset(zeit);
+  }
+}
+
+// Reset wird in 2 Situationen aufgerufen:
+// 1. Timer zum schlecht werden ist abgelaufen, das Futter muss also
+//    schlecht werden und evtl. der Timer zum Zerstoeren gestartet werden
+// 2. Timer zum Zerstoeren ist abgelaufen, also wird Futter zerstoert.
+// Wird der Reset aus irgendeinem Grund zu falschen Zeiten aufgerufen,
+// wird der naechste Reset korrekt initialisiert.
+void reset() {
+  // wenn kein env, Abbruch und reset abschalten.
+  if (!environment()) {
+      set_next_reset(-1);
+      return;
+  }
+
+  // Test, ob Lebensmittel schlecht werden darf
+  if (!QueryProp(P_NO_BAD)) {
+    if (!is_bad()) {
+      // Test, ob Timer laeuft
+      if (time_togo_bad) {
+        // Pruefen, ob der Reset so in etwa zur richtigen Zeit aufgerufen wurde
+        // 10% Abweichung sind erlaubt
+        int diff = time() - time_togo_bad;
+        int std = to_int(QueryProp(P_LIFETIME)/10);
+        if (abs(diff) < std || diff > 0) {
+          if (make_bad()) return;
+          // Reset zum Zerstoeren vorbereiten
+          if (QueryProp(P_DESTROY_BAD) > 0) {
+            my_set_next_reset(time_to_remove - time());
+          }
+          else {
+            set_next_reset(-1);
+          }
+        }
+        else {
+          // Reset nochmal zum Ablauf der Frist
+          my_set_next_reset(abs(diff));
+        }
+      }
+    }
+    else if (time_to_remove && QueryProp(P_DESTROY_BAD) > 0) {
+      // wir sind im Reset nach dem Schlechtwerden
+      int diff = time() - time_to_remove;
+      int std = to_int((QueryProp(P_LIFETIME)+QueryProp(P_DESTROY_BAD))/10);
+      // Pruefen, ob der Reset so in etwa zur richtigen Zeit aufgerufen wurde
+      // 10% Abweichung sind erlaubt
+      if (abs(diff) < std || diff > 0) {
+        if (make_destroy()) return; // Leermachen oder zerstoeren
+      }
+      else {
+        // Reset nochmal zum Ablauf der Frist
+        my_set_next_reset(abs(diff));
+      }
+    }
+  } // if (P_NO_BAD)
+
+  ::reset();
+}
+
+private int safety_check() {
+  // Damit ein Spieler auch gaaaanz sicher nicht Speisen ausserhalb der 
+  // P_LIFETIME nutzen kann ...
+  if (time_togo_bad && !is_bad() && time() > time_togo_bad) {
+    if (make_bad()) return 0;
+    // Reset zum Zerstoeren vorbereiten
+    if (QueryProp(P_DESTROY_BAD) > 0) {
+      my_set_next_reset(time_to_remove - time());
+    }
+    else {
+      set_next_reset(-1);
+    }
+    return 0;
+  }
+  else if (time_to_remove && time() > time_to_remove
+      && QueryProp(P_DESTROY_BAD) > 0) {
+    make_destroy(); // Leermachen oder zerstoeren
+    return 0;
+  }
+  return 1;
+}
+
+// Pruefung zum Starten ausgelagert, da das im init und in
+// NotifyMove gebraucht wird
+private void check_start_timer(object oldenv) {
+  // Test, ob Lebensmittel schlecht werden kann
+  if (!QueryProp(P_NO_BAD) && !time_togo_bad) {
+    // Wenn wir irgendwie in einem Spieler stecken oder ein Spieler in der
+    // Umgebung ist oder das Futter mehrfach bewegt wurde
+    if (sizeof(filter(all_environment(),#'query_once_interactive))
+        || sizeof(filter(all_inventory(environment()),#'query_once_interactive))
+        || objectp(oldenv)) {
+      start_lifetime();
+    }
+  }
+  else {
+    // Damit ein Spieler auch gaaaanz sicher nicht Speisen ausserhalb
+    // der P_LIFETIME nutzen kann ...
+    safety_check();
+  }
+}
+
+// hier wird geprueft, ob das Futter dem Spieler zugaenglich ist.
+void init() {
+  ::init();
+  check_start_timer(environment());
+}
+
+// hier wird geprueft, ob das Futter dem Spieler zugaenglich ist.
+// das ist sicherer als nur im init()
+protected void NotifyMove(object dest, object oldenv, int method) {
+  ::NotifyMove(dest, oldenv, method);
+  check_start_timer(oldenv);
+}
+
+void create() {
+  // Sicherheitshalber,
+  // falls jemand die Blueprint mit Props initialisieren will
+  ::create();
+  Set(P_LIFETIME, #'set_std_food_lifetime, F_SET_METHOD);
+  Set(P_LIFETIME, PROTECTED, F_MODE_AS);
+
+  time_togo_bad = 0;
+  time_to_remove = 0;
+  time_gone_bad = 0;
+
+  // Standardlaufzeit 1 Reset
+  first_reset_length = to_int((__RESET_TIME__ + random(__RESET_TIME__)) / 2);
+  // Reset wird erstmal abgeschaltet, das wird im NotifyMove geprueft
+  set_next_reset(-1);
+
+  SetProp(P_SHORT,"Ein Lebensmittel");
+  SetProp(P_LONG,"Das ist ein Lebensmittel.\n");
+  SetProp(P_NAME,"Lebensmittel");
+  SetProp(P_MATERIAL, MAT_MISC_FOOD);
+  SetProp(P_GENDER,NEUTER);
+  SetProp(P_WEIGHT,50);
+  SetProp(P_VALUE,10);
+  SetProp(P_PORTIONS,1); // einmal Abbeissen
+  SetProp(P_DESTROY_BAD,DESTROY_BAD); // Zerstoeren beim Schlechtwerden
+  SetProp(P_DISTRIBUTION,HD_STANDARD); // Heilung per Standardverteilung (5/hb)
+
+  SetProp(P_CONSUME_MSG,"@WER2 konsumiert @WEN1.");
+  SetProp(P_EATER_MSG,"Du konsumierst @WEN1.");
+  SetProp(P_EMPTY_MSG,"@WER1 ist bereits leer.");
+  SetProp(P_NOFOOD_MSG,"@WEN1 kann man nicht essen!");
+  SetProp(P_NODRINK_MSG,"@WEN1 kann man nicht trinken!");
+  SetProp(P_BAD_MSG,"@WER1 verdirbt.");
+  SetProp(P_FOOD_FULL_MSG,"Du bist zu satt, das schaffst Du nicht mehr.");
+  SetProp(P_DRINK_FULL_MSG,"So viel kannst Du im Moment nicht trinken.");
+  SetProp(P_ALC_FULL_MSG,"Soviel Alkohol vertraegst Du nicht mehr.");
+  SetProp(P_ENV_MSG,"Vielleicht solltest Du @WEN1 vorher nehmen.");
+  SetProp(P_REMOVE_MSG,"@WER1 zerfaellt zu Staub.");
+
+  // Sichern der Standards
+  SetProp(ID_BACKUP,QueryProp(P_IDS));
+  SetProp(ADJ_BACKUP,QueryProp(P_ADJECTIVES));
+
+  AddId(({"lebensmittel","nahrung","\nfood"}));
+
+  AddCmd(({"iss","esse"}),"cmd_eat");
+  AddCmd(({"trink","trinke"}),"cmd_drink");
+}
+
+// Aendert Werte der Speise, wenn sie vergammelt ist und konsumiert wird
+protected void consume_bad(mapping entry_info) {
+  entry_info[P_HP] = 0;
+  entry_info[P_SP] = 0;
+  entry_info[H_EFFECTS] = ([P_POISON:1]);
+}
+
+// Stellt Mapping zum Konsumieren zusammen und konsumiert.
+// kann mit testonly=1 aufgerufen werden, um zu pruefen, ob konsumieren klappt
+// Gibt das Ergebnis von living->consume() zurueck:
+// >0 -> erfolgreich konsumiert
+// <0 -> nicht konsumiert (siehe Hilfe zu consume)
+protected varargs int try_consume(object consumer, int testonly) {
+  mapping entry_info = ([P_FOOD:QueryProp(P_FOOD),
+                        P_DRINK:QueryProp(P_DRINK),
+                           P_HP:QueryProp(P_HP),
+                           P_SP:QueryProp(P_SP),
+                       P_POISON:QueryProp(P_POISON),
+                      P_ALCOHOL:QueryProp(P_ALCOHOL),
+                 H_DISTRIBUTION:QueryProp(P_DISTRIBUTION)]);
+
+  if (is_bad()) consume_bad(entry_info);
+
+  int result = (int)consumer->consume(entry_info, testonly);
+  if (!result) {
+    tell_object(consumer,
+      "Da ist was schief gelaufen! Sprich mal mit einem Magier.\n");
+    raise_error("living->consume() mit falschen Parametern aufgerufen:\n"+
+      sprintf("%O\n",entry_info));
+  }
+
+  return result;
+}
+
+// Konsumieren war erfolgreich
+// Hier kann man beim Ueberschreiben noch zusaetzliche Sachen machen
+// P_PORTIONS ist bereits runtergezaehlt, Speise ist aber noch nicht leer!
+// make_empty() wird also eventuell noch danach aufgerufen
+// entspricht alter Func BeimEssen()
+protected void success_consume(object consumer) {
+  tell_room(environment(consumer),eval_property(P_CONSUME_MSG,consumer),({consumer}));
+  tell_object(consumer,eval_property(P_EATER_MSG,consumer));
+}
+
+// Konsumieren war nicht erfolgreich
+// Hier kann man beim Ueberschreiben noch zusaetzliche Sachen machen
+// in reason wird der Grund des Fehlschlagens uebergeben (siehe 'man consume')
+protected void failed_consume(object consumer, int reason) {
+  if (reason & HC_MAX_FOOD_REACHED)
+    tell_object(consumer,eval_property(P_FOOD_FULL_MSG,consumer));
+  else if (reason & HC_MAX_DRINK_REACHED)
+    tell_object(consumer,eval_property(P_DRINK_FULL_MSG,consumer));
+  else if (reason & HC_MAX_ALCOHOL_REACHED)
+    tell_object(consumer,eval_property(P_ALC_FULL_MSG,consumer));
+}
+
+// Konsumiert die Speise
+// Gibt das Ergebnis von living->consume() zurueck:
+// >0 -> erfolgreich konsumiert
+// <0 -> nicht konsumiert (siehe Hilfe zu consume)
+protected int consume(object consumer) {
+  if (!objectp(consumer) || !living(consumer)) {
+    raise_error("argument pl not a living:\n"+
+      sprintf("%O\n",consumer));
+  }
+  // damit abgelaufene Speisen auch wirklich abgelaufen sind
+  if (!safety_check()) return 0;
+
+  if (!is_not_empty()) {
+    notify_fail(eval_property(P_EMPTY_MSG,consumer));
+    return 0;
+  }
+  int result = try_consume(consumer);
+  if (result > 0) {
+    SetProp(P_PORTIONS,QueryProp(P_PORTIONS)-1);
+    success_consume(consumer);
+    if (!is_not_empty()) make_empty();
+  }
+  else if (result < 0) {
+    failed_consume(consumer,result);
+  }
+  return result;
+}
+
+// Futtern
+int cmd_eat(string str) {
+  notify_fail("WAS moechtest Du essen?\n");
+  if (!str || !id(str)) return 0;
+  object env = environment(this_object());
+  if (env != this_player() && QueryProp(P_ENV_MSG)) {
+    notify_fail(eval_property(P_ENV_MSG,env));
+    return 0;
+  }
+
+  if (!is_eatable() && !is_drinkable()) {
+    write("Mit diesem Futter stimmt was nicht!"
+      " Sprich mal mit einem Magier.\n");
+    raise_error("Food ohne Werte.\n");
+    return 0;
+  }
+
+  if (!is_eatable()) {
+    notify_fail(eval_property(P_NOFOOD_MSG,env));
+    return 0;
+  }
+
+  return consume(this_player());
+}
+
+// Trinken
+int cmd_drink(string str) {
+  notify_fail("WAS moechtest Du trinken?\n");
+  if (!str || !id(str)) return 0;
+  object env = environment(this_object());
+  if (env != this_player() && QueryProp(P_ENV_MSG)) {
+    notify_fail(eval_property(P_ENV_MSG,env));
+    return 0;
+  }
+
+  if (!is_eatable() && !is_drinkable()) {
+    write("Mit diesem Futter stimmt was nicht!"
+      " Sprich mal mit einem Magier.\n");
+    raise_error("Food ohne Werte.\n");
+    return 0;
+  }
+
+  if (!is_drinkable()) {
+    notify_fail(eval_property(P_NODRINK_MSG,env));
+    return 0;
+  }
+
+  return consume(this_player());
+}
+
+//============================================================================
+// Kompatibilitaetscode
+// um eventuell vergessenes Food oder Zugriff von aussen auf altes Food
+// mitzubekommen
+
+nosave deprecated int alc, alc_pp, food, food_pp, heal, p, soft, soft_pp, c;
+
+deprecated int eat_this( string str ) {
+  return cmd_eat(str);
+}
+
+deprecated int drink_this(string str) {
+  return cmd_drink(str);
+}
+
+deprecated void MakeDrink() {
+  SetProp(P_DRINK,1);
+  SetProp(P_FOOD,0);
+}
+
+deprecated mixed _query_old_is_food() {
+  return is_eatable();
+}
+
+deprecated void _set_old_is_food(mixed value) {
+  SetProp(P_DRINK,0);
+  SetProp(P_FOOD,value);
+}
+
+deprecated mixed _query_old_is_drink() {
+  return is_drinkable();
+}
+
+deprecated void _set_old_is_drink(mixed value) {
+  SetProp(P_DRINK,value);
+  SetProp(P_FOOD,0);
+}
+
+deprecated mixed _query_old_is_full() {
+  return is_not_empty();
+}
+
+deprecated void _set_old_is_full(mixed value) {
+  SetProp(P_PORTIONS,value);
+}
+
+deprecated mixed _query_old_alc() {
+  return QueryProp(P_ALCOHOL);
+}
+
+deprecated void _set_old_alc(mixed value) {
+  SetProp(P_ALCOHOL,value);
+}
+
+deprecated mixed _query_old_water() {
+  return QueryProp(P_DRINK);
+}
+
+deprecated void _set_old_water(mixed value) {
+  SetProp(P_DRINK,value);
+}
+
+deprecated mixed _query_old_food_size() {
+  return QueryProp(P_FOOD);
+}
+
+deprecated void _set_old_food_size(mixed value) {
+  SetProp(P_FOOD,value);
+}
+
+deprecated mixed _query_old_potion() {
+  return QueryProp(P_PORTIONS);
+}
+
+deprecated void _set_old_potion(mixed value) {
+  SetProp(P_PORTIONS,value);
+}
+
+deprecated mixed _query_old_heal_hp() {
+  return QueryProp(P_HP);
+}
+
+deprecated void _set_old_heal_hp(mixed value) {
+  SetProp(P_HP,value);
+}
+
+deprecated mixed _query_old_heal_sp() {
+  return QueryProp(P_SP);
+}
+
+deprecated void _set_old_heal_sp(mixed value) {
+  SetProp(P_SP,value);
+}
+
+deprecated mixed _query_old_bad() {
+  return is_bad();
+}
+
+deprecated void _set_old_bad(mixed value) {
+  return;
+}
+
+deprecated mixed _query_old_mess() {
+  return QueryProp(P_CONSUME_MSG);
+}
+
+deprecated void _set_old_mess(mixed value) {
+  SetProp(P_CONSUME_MSG,value);
+}
+
+deprecated mixed _query_old_eater() {
+  return QueryProp(P_EATER_MSG);
+}
+
+deprecated void _set_old_eater(mixed value) {
+  SetProp(P_EATER_MSG,value);
+}
+
+deprecated mixed _query_old_poison() {
+  return QueryProp(P_POISON);
+}
+
+deprecated void _set_old_poison(mixed value) {
+  SetProp(P_POISON,value);
+}
+
+// interne Methode
+private mixed get_old_empty(string prop) {
+  mixed props = QueryProp(P_EMPTY_PROPS);
+  if (mappingp(props)) return props[prop];
+  return 0;
+}
+// interne Methode
+private void set_old_empty(string prop, mixed value) {
+  mixed props = QueryProp(P_EMPTY_PROPS);
+  if (!mappingp(props)) props = ([prop:value]);
+  else props[prop] = value;
+  SetProp(P_EMPTY_PROPS,props);
+}
+
+deprecated mixed _query_old_empty_con() {
+  return get_old_empty(P_SHORT);
+}
+
+deprecated void _set_old_empty_con(mixed value) {
+  set_old_empty(P_SHORT,value);
+}
+
+deprecated mixed _query_old_empty_gender() {
+  return get_old_empty(P_GENDER);
+}
+
+deprecated void _set_old_empty_gender(mixed value) {
+  set_old_empty(P_GENDER,value);
+}
+
+deprecated mixed _query_old_empty_id() {
+  return get_old_empty(P_IDS);
+}
+
+deprecated void _set_old_empty_id(mixed value) {
+  set_old_empty(P_IDS,value);
+}
+
+deprecated mixed _query_old_empty_long() {
+  return get_old_empty(P_LONG);
+}
+
+deprecated void _set_old_empty_long(mixed value) {
+  set_old_empty(P_LONG,value);
+}
+
+deprecated mixed _query_old_no_con() {
+  return mappingp(QueryProp(P_EMPTY_PROPS));
+}
+
+deprecated void _set_old_no_con(mixed value) {
+  if (value) SetProp(P_EMPTY_PROPS,([]));
+  else SetProp(P_EMPTY_PROPS,0);
+}
+
+// Mupfel
+deprecated int Essbar() {
+  return 1;
+}
+
+deprecated mixed _query_food_info() {
+  return ([]);
+}
+
+deprecated void _set_food_info(mixed value) {
+  return;
+}
+
+// Troy
+deprecated void food_decay() {
+  return;
+}
+
+deprecated int eat_me(string str) {
+  return 0;
+}
diff --git a/std/gilde.c b/std/gilde.c
new file mode 100644
index 0000000..6fb84cf
--- /dev/null
+++ b/std/gilde.c
@@ -0,0 +1,380 @@
+// MorgenGrauen MUDlib
+//
+// gilde.c -- Standardgilde
+//
+// $Id: gilde.c 8388 2013-02-16 17:28:31Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/room";
+
+#include <properties.h>
+#include <defines.h>
+#include <rooms.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <new_skills.h>
+#include <exploration.h>
+#include <ansi.h>
+#include "/secure/questmaster.h"
+#include "/secure/lepmaster.h"
+#include "/secure/config.h"
+#include <events.h>
+
+#define kosten_0 \
+  ([0:                "Bis Du Dich das naechste Mal hier blicken laesst, solltest Du eine Menge Stufenpunkte sammeln. Sonst wird das nix!",\
+    "zauberer":       "Jetzt fang hier ja nicht an zu jammern. Du hast doch grade erst erhoeht. Fuer die naechste Stufe musst Du erstmal Leistung zeigen!",\
+    "kaempfer":       "Hat grade erst erhoeht und faengt schon an zu jammern wie ein altersschwacher Zauberer. Nimm gefaelligst Haltung an!",\
+    "klerus":         "Wer hat Dir denn das Gehirn gelaeutert? Zeig erstmal was von Deinem Koennen, bevor Du schon wieder erhoehen willst!",\
+    "chaos":          "So chaotisch kannst Du doch nicht sein zu glauben, dass Du schon wieder erhoehen kannst!",\
+    "karate":         "Ruh Deine Knochen erstmal ein bisschen aus. Bis zur naechsten Stufe werden sie noch genug leiden muessen!",\
+    "katzenkrieger":  "Da hilft kein Maunzen und kein Schnurren. Du hast grade erst erhoeht und dabei bleibt das!",\
+    "tanjian":        "Jetzt schrei nicht gleich nach Siamil. Du musst noch eine Menge leisten bis zur naechsten Stufe!",\
+    "bierschuettler": "Du hast das Bier auf Deinen letzten Erfolg noch nicht einmal ausgetrunken. Werd jetzt ja nicht groessenwahnsinnig!",\
+    "werwoelfe" :     "Und wenn Du noch so grimmig guckst und den Mond anheulst. Du hast gerade erst erhoeht. Nun musst Du erstmal wieder etwas tun!",\
+  ])
+#define kosten_20 \
+  ([0:                "Da liegt aber noch ein weiter Weg vor Dir. Aber Du schaffst es ganz sicher!",\
+    "zauberer":       "Du kannst zwar zaubern, aber nicht hexen. Fuer die naechste Stufe musst Du schon noch einiges tun!",\
+    "kaempfer":       "Oh weh, da hast Du aber noch ne Menge vor Dir. Beiss die Zaehne zusammen und los gehts!",\
+    "klerus":         "Du wirst bestimmt noch einige Heiltraenke brauchen bis zur naechsten Stufe!",\
+    "chaos":          "Hast Du versucht Dein Chaos zu beseitigen, oder was hast Du gemacht? Sehr weit gekommen biste jedenfalls noch nicht. Musst Dich schon ein bisschen anstrengen!",\
+    "karate":         "Ganz schoen anstrengend immer nur mit Hand und Fuss zu metzeln, oder? Aber es hat Dich schon etwas naeher an die naechste Stufe rangebracht!",\
+    "katzenkrieger":  "Bisschen weniger Fellpflege betreiben, und ein bisschen mehr Stufenpunkte sammeln. Du hast naemlich noch ein gutes Stueck vor Dir!",\
+    "tanjian":        "Du hast noch ne Menge zu tun bis zur naechsten Stufe. Sterb nicht so oft, aber sowas tun Tanjian ja eh nicht!",\
+    "bierschuettler": "Bier trinken alleine wird Dich nicht weiter bringen, obwohl Du schon ein Stueck weit gekommen bist!",\
+    "werwoelfe" :     "Staerke Deine Willenskraft. Du hast noch einiges vor Dir!",\
+  ])
+#define kosten_40 \
+  ([0:                "Also die Haelfte hast Du schon ungefaehr geschafft. Aber ruh Dich jetzt ja nicht darauf aus. Sieh mal zu, dass Du die zweite Haelfte zur naechsten Stufe auch noch schaffst. Auf einem Bein kann man schliesslich nicht lange stehn!",\
+    "zauberer":       "Teile Llystrathe mit Hilfe. Och schade scheint nicht zu funktionieren, dann musst Du eben selber noch ne Menge Stufenpunkte sammeln!",\
+    "kaempfer":       "Kaempfer schnell an die Waffen! Die Haelfte hast Du ungefaehr geschafft, aber es gibt noch viel zu tun!",\
+    "klerus":         "Beile Dich! Das Boese sitzt Dir im Nacken und Du hast noch ungefaehr die Haelfte vor Dir!",\
+    "chaos":          "Schnapp Dir Deinen Daemon und teil Dir die zweite Haelfte der Arbeit mit ihm!",\
+    "karate":         "Du scheinst eingesehen zu haben, dass hirnloses Draufhauen nix nutzt. Die Haelfte haste ungefaehr schon geschafft!",\
+    "katzenkrieger":  "So ungefaehr bis zur Haelfte hast Du Dich schon an die neue Stufe herangeschlichen!",\
+    "tanjian":        "Man kann den Wert zwischen den beiden Stufen durchaus als ausgewogen bezeichnen. Trotzdem hast Du noch ein Stueck des Weges vor Dir!",\
+    "bierschuettler": "War das Fass nu halb voll oder halb leer? Jedenfalls biste ungefaehr bei der Haelfte angekommen!",\
+    "werwoelfe" :     "Noch einige Gegner werden sich vor Dir fuerchten muessen, obwohl Du schon viel geschafft hast!",\
+  ])
+#define kosten_60 \
+  ([0:                "Also ein bisschen was musst Du noch tun, aber es sieht schon ziemlich gut aus!",\
+    "zauberer":       "Ein bisschen Wille und Magie und Du schaffst es wieder ein Stueck naeher an die neue Stufe heranzukommen!",\
+    "kaempfer":       "Staerke Deinen Kampfwillen. Die groessten Anstrengungen hast Du schon hinter Dir!",\
+    "klerus":         "So nah am Ziel wirst Du doch nicht aufgeben wollen. Hol Dir noch ein bisschen goettliche Unterstuetzung und weiter gehts!",\
+    "chaos":          "Chaotisch sein alleine hilft nicht. Ein bisschen was musst Du schon noch tun!",\
+    "karate":         "Die Handkanten geschaerft, den Gi fest zugebunden. Bald hast Du es geschafft!",\
+    "katzenkrieger":  "Auf Samtpfoten schleichst Du mit grossen Schritten ans Ziel heran. Aber ein bisschen fehlt noch!",\
+    "tanjian":        "Eine Quest hier, ein paar Monster da. Aber immer schoen ausgeglichen bleiben, dann klappts auch weiterhin!",\
+    "bierschuettler": "Noch ein paar kraeftige Schlucke und Du hast es fast geschafft!",\
+    "werwoelfe" :     "Verlass Dein Rudel und zieh hinaus ins Abenteuer. Dann wirst Du bald siegreich heimkehren!",\
+  ])
+#define kosten_80 \
+  ([0:                "Huch na sowas, Du hast die naechste Stufe ja schon fast erreicht. Na also! Der Rest ist ja wirklich nur noch Kleinkram!",\
+    "zauberer":       "Die paar fehlenden Puenktchen haste Dir doch rasch zusammengezaubert!",\
+    "kaempfer":       "Das fehlende bisschen machst Du als gnadenlos guter Kaempfer doch mit Links. Jetzt halt Dich aber mal ran. Oder willst Du Dich auf dem letzten Stueck noch von so einem luschigen Zauberer ueberrunden lassen?",\
+    "klerus":         "Bei Saphina, Kandri und Lembold, Du bist fast bei der naechsten Stufe angekommen!",\
+    "chaos":          "Boese waehrt am laengsten. Fast hast Du es geschafft!",\
+    "karate":         "So viele blaue Flecken wie Du an Armen und Beinen hast, kann die naechste Stufe nicht mehr weit sein!",\
+    "katzenkrieger":  "Pass bloss auf, dass Dich auf den letzten Zentimetern nicht noch ein Hund erwischt!",\
+    "tanjian":        "Siamil wird stolz auf Dich sein. Du hast Dich gegen Gut und Boese durchgesetzt und das Ziel fast erreicht!",\
+    "bierschuettler": "Lange musst Du nicht mehr warten. Kannst ruhig schon mal ein frisches Bier in Auftrag geben!",\
+    "werwoelfe" :     "Noch ein letztes Mal die fuerchterlichen Krallen schaerfen, dann hast Du Deinen neuen Level erreicht!",\
+  ])
+
+void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);
+      return;
+  }
+  room::create();
+
+  AddCmd("kosten","kosten");
+  AddCmd("liste","liste");
+  AddCmd("erhoehe","advance");
+  AddCmd(({"treff"}), "GotoMagierTreff");
+ }
+
+void init()
+{
+  int lvl;
+  room::init();
+
+  if (PL && query_once_interactive(PL)
+      && (lvl=PL->QueryProp(P_LEVEL)) <= 6
+      && LEPMASTER->QueryLevel(PL->QueryProp(P_LEP)) > lvl)
+  {
+    tell_object(PL,
+      "\nDu koenntest Deine Stufe mit \"erhoehe spieler\" hier in der Gilde "
+      "erhoehen.\n\n");
+  }
+}
+
+string zeige_reboot()
+{
+  string str;
+  int t,t2;
+
+  t=time()-last_reboot_time();
+  t2=t;
+  str="Seit dem letzten Shutdown sind "+t+" Sekunden vergangen.\n";
+  if (t<60) return str;
+  str+="Das sind ";
+  if (t>=86400)
+  {
+    str+=t/86400+" Tag";
+    if (t>=86400*2)
+      str+="e";
+    str+=", ";
+    t2=t2%86400;
+  }
+  if (t>=3600)
+  {
+    str+=t2/3600+" Stunde";
+    if (t2/3600!=1)
+      str+="n";
+    str+=", ";
+    t2=t2%3600;
+  }
+  if (t>=60)
+  {
+    str+=t2/60+" Minute";
+    if (t2/60!=1)
+      str+="n";
+    str+=" und ";
+    t2=t2%60;
+  }
+  str+=t2+" Sekunde";
+  if (t2!=1)
+    str+="n";
+  str+=".\n";
+  return str;
+}
+
+int seer_cond(int silent)
+{
+  int cond;
+
+  cond=LEPMASTER->QueryReadyForWiz(this_player());
+
+  if (!silent)
+    write(break_string(LEPMASTER->QueryReadyForWizText(this_player()),
+          78, 0, 1));
+
+  return cond;
+}
+
+varargs int kosten(string str)
+{
+  string tmp;
+
+  int lep = PL->QueryProp(P_LEP);
+  int lvl = PL->QueryProp(P_LEVEL);
+  int diff = LEPMASTER->QueryNextLevelLEP(lvl, lep);
+
+  switch ( diff ) {
+    case 101..__INT_MAX__:
+      // Falls mal LEPs abhandengekommen sind...
+      tmp = "Wie bist Du ueberhaupt an Deinen Level gekommen?\n"
+      "Sei froh, dass Du nicht wieder abgestuft wirst.";
+      break;
+
+    case 81..100:
+      tmp=kosten_0[PL->QueryProp(P_GUILD)] || kosten_0[0];
+      break;
+
+    case 61..80:
+      tmp=kosten_20[PL->QueryProp(P_GUILD)] || kosten_20[0];
+      break;
+
+    case 41..60:
+      tmp=kosten_40[PL->QueryProp(P_GUILD)] || kosten_40[0];
+      break;
+
+    case 21..40:
+      tmp=kosten_60[PL->QueryProp(P_GUILD)] || kosten_60[0];
+      break;
+
+    case 1..20:
+      tmp=kosten_80[PL->QueryProp(P_GUILD)] || kosten_80[0];
+      break;
+
+    default:
+      if ( lvl < 9 )
+        tmp = "Probier mal den Befehl 'erhoehe'.";
+      else
+        tmp = "Den Befehl 'erhoehe' kennst Du aber, ja?";
+      break;
+  }
+
+  write( break_string( tmp, 78, 0, BS_LEAVE_MY_LFS ) );
+
+  if (!IS_SEER(this_player()) 
+      && ( (str == "lang") ||
+        (this_player()->QueryProp(P_LEVEL) > 12 && str != "kurz"))) {
+    seer_cond(0);
+    write (break_string("\nMit 'kosten kurz' kannst Du die Angabe der "
+          "Seher-Anforderungen unterdruecken.", 78,0,1));
+  }
+
+  return 1;
+}
+
+// ermittelt Gildentitel zum Level <lev>.
+// Der letzte verfuegbare Gildentitel wird nur fuer Seher vergeben.
+// TODO: mit dem entsprechenden Code aus gilden_ob.c vereinigen?
+string get_new_title(int lev, object pl)
+{
+  mapping titles;
+
+  if (!pl) return 0;
+
+  if (lev<0) lev=0;
+
+  if (pl->QueryProp(P_GENDER) == MALE)
+    titles=(mapping)QueryProp(P_GUILD_MALE_TITLES);
+  else
+    titles=(mapping)QueryProp(P_GUILD_FEMALE_TITLES);
+
+  if (!mappingp(titles) || !sizeof(titles)) return 0;
+
+  int maxlevel = max(m_indices(titles));
+
+  // Level begrenzen. Max-Level fuer Seher.
+  if (lev >= maxlevel)
+    lev = IS_SEER(pl) ? maxlevel : maxlevel-1;
+
+  return titles[lev];
+}
+
+// versucht das Spielerlevel zu erhoehen. Macht nicht den ganzen Krams
+// drumrum, den advance() aus hysterischen Gruenden tut.
+int try_player_advance(object pl) {
+
+  if (PL->QueryProp(P_KILLS)>0)
+    return -1;
+
+  int level = pl->QueryProp( P_LEVEL );
+  if (level == -1) level = 0;
+
+  if (LEPMASTER->QueryNextLevelLEP(level, pl->QueryProp(P_LEP)) > 0)
+      return 0;
+  else
+      ++level;
+
+  pl->SetProp( P_LEVEL, level );
+
+  // Aufstiegs-Event ausloesen
+  EVENTD->TriggerEvent(EVT_LIB_ADVANCE, ([
+        E_OBJECT: PL, E_PLNAME: getuid(PL),
+        E_ENVIRONMENT: environment(PL),
+        E_GUILDNAME: PL->QueryProp(P_GUILD),
+        P_LEVEL: PL->QueryProp(P_LEVEL),
+        ]) );
+
+  // Falls die konkrete Gilde des Spielern irgedwas mit dem Titel in
+  // ABhaengigkeit des Spielerlevels tun will. Ausnahmsweise per call_other,
+  // die Funktion kommt eigentlich aus /std/gilden_ob.c.
+  string gname=(string)pl->QueryProp(P_GUILD);
+  (GUILD_DIR+"/"+gname)->adjust_title(pl);
+
+  return 1;
+}
+
+// "erhoehe"-Kommando.
+// erhoeht das Spielerlevel, falls moeglich. Setzt den passenden
+// Abenteurergildentitel.
+int advance(string arg)
+{
+
+  if (arg &&
+      arg != "stufe" && arg != "spielerstufe" && arg != "spieler"
+      && arg != "spielerlevel")
+    return 0;
+
+  int res = try_player_advance(PL);
+
+  if (!res)
+    return kosten("kurz");
+  else if (res < 0) {
+    notify_fail(break_string(
+        "Du hast einen Mitspieler umgebracht!\n"+
+        "In diesem Fall kannst Du Deine Stufe nicht erhoehen.\n"+
+        "Bitte wende Dich an den Sheriff (oder einen Erzmagier) und bring "
+        "das in Ordnung.\n",78,BS_LEAVE_MY_LFS));
+    say(break_string(PL->Name(WER) 
+          + " hat soeben auf schmerzliche Weise erfahren muessen, dass "
+          "es wirklich nicht foerderlich ist, Mitspieler umzubringen.\n",
+          78), PL);
+    return 0;
+  }
+
+  string name_of_player = PL->name(WER);
+  int level = PL->QueryProp(P_LEVEL);
+  say( name_of_player + " hat jetzt Stufe " + level + " erreicht.\n");
+
+  string title = PL->QueryProp(P_TITLE);
+
+  switch(random(3)) {
+    case 0:
+      write("Du bist jetzt " + name_of_player + " " + title +
+        " (Stufe " + level + ").\n");
+      break;
+
+    case 1:
+      write("Gut gemacht, " + name_of_player + " " + title +
+        " (Stufe " + level + ").\n");
+      break;
+
+    default:
+      write("Willkommen auf Deiner neuen Stufe.\n" +
+        "Du bist jetzt " + name_of_player+" "+ title +
+        " (Stufe " + level + ").\n");
+      break;
+  }
+
+  return 1;
+}
+
+// Spielerkommando fuer Abenteurerliste
+varargs int liste(mixed pl)
+{
+  string str; 
+  if (!this_player()) return 0;
+
+  if(!objectp(pl))
+    if(stringp(pl))
+      pl=find_player(pl)||find_netdead(pl);
+  if(!objectp(pl))
+    pl=PL;
+  if (pl != this_player()) {
+    write ("Du kannst Dir nur Deine eigenen Abenteuer ansehen.\n");
+    return 1;
+  }
+
+  str = QM->liste(pl);
+
+  this_player()->More( str, 0 );
+  return 1;
+}
+
+int GotoMagierTreff()
+{
+  if(IS_LEARNER(this_player()))
+  {
+    write("Ein Zauberspruch zieht vor Deinem geistigen Auge vorbei und Du\n"
+    "sprichst ihn nach.\n");
+    say(PL->name()+" murmelt einen geheimen Zauberspruch und schwebt langsam\n"
+    "zur Decke hinauf und verschwindet durch die Wand.\n");
+    write("Du schwebst langsam zur Decke hinauf und als ob diese nicht da\n"
+    "waere mitten hindurch in den Magiertreff.\n");
+    return (PL->move("/secure/merlin", M_TPORT | M_SILENT ) >= 0);
+  }
+  write("Du springst zur Decke hinauf und nix passiert.\n");
+  return 1;
+}
+
diff --git a/std/gilden_ob.c b/std/gilden_ob.c
new file mode 100644
index 0000000..6805acc
--- /dev/null
+++ b/std/gilden_ob.c
@@ -0,0 +1,462 @@
+// MorgenGrauen MUDlib
+//
+// gilden_ob.c -- Basisfunktionen einer Gilde
+//
+// $Id: gilden_ob.c 8433 2013-02-24 13:47:59Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma no_shadow
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/restriction_checker";
+#include <properties.h>
+#include <attributes.h>
+#include <new_skills.h>
+#include <defines.h>
+#include <player/fao.h>
+#include <wizlevels.h>
+
+void create() {
+    // P_GUILD_SKILLS sollte nicht direkt von aussen manipuliert werden...
+    SetProp(P_GUILD_SKILLS,([]));
+    Set(P_GUILD_SKILLS,PROTECTED,F_MODE_AS);
+}
+
+mapping _query_guild_skills() {
+    // eine Kopie zurueckliefern, um versehentliche Aenderungen des
+    // Originalmappings in der Gilde zu vermeiden.
+    // Darf nicht ohne weiteres entfernt werden, da es hier im File Code gibt,
+    // der sich auf die Kopie verlaesst.
+    return(deep_copy(Query(P_GUILD_SKILLS)));
+}
+
+string GuildName() {
+  //Gilden muessen Blueprints sein, so. ;-)
+  return object_name(this_object())[8..];
+}
+
+varargs int
+IsGuildMember(object pl) {
+  string plg;
+
+  if (!pl && !(pl=this_player()))
+    return 0;
+  if (!(plg=(string)pl->QueryProp(P_GUILD)))
+    return 0;
+  if (GuildName()!=plg) {
+    _notify_fail("Du gehoerst dieser Gilde nicht an!\n");
+    return 0;
+  }
+  return 1;
+}
+
+int check_cond(mixed cond) {
+  string res;
+
+  if (intp(cond)) {
+    _notify_fail("Dir fehlt noch die noetige Erfahrung. "+
+                "Komm spaeter wieder.\n");
+    return (this_player()->QueryProp(P_XP)>=cond);
+  }
+  if (mappingp(cond)) {
+    res=check_restrictions(this_player(),cond);
+    if (!stringp(res)) return 1;
+    _notify_fail(res);
+    return 0;
+  }
+  return 1;
+}
+
+int can_advance() {
+  int lv;
+  mapping lvs;
+
+  if (!(lv=(int)this_player()->QueryProp(P_GUILD_LEVEL))) return 1;
+  if (!(lvs=QueryProp(P_GUILD_LEVELS))) return 0;
+  return check_cond(lvs[lv+1]); // Bedingung fuer naechsten Level testen.
+}
+
+void adjust_title(object pl) {
+  int lv;
+  mixed ti;
+
+
+  if (!pl ||
+      !(lv=(int)pl->QueryProp(P_GUILD_LEVEL)))
+    return;
+  switch((int)pl->QueryProp(P_GENDER)) {
+  case MALE:
+    ti=QueryProp(P_GUILD_MALE_TITLES);
+    break;
+  case FEMALE:
+    ti=QueryProp(P_GUILD_FEMALE_TITLES);
+    break;
+  default:
+    return;
+  }
+  if (mappingp(ti))
+    ti=ti[lv];
+  if (stringp(ti))
+    pl->SetProp(P_GUILD_TITLE,ti);
+  
+  // Spielertitel nullen, damit der Gildentitel angezeigt wird.
+  if (!IS_SEER(pl) && !FAO_HAS_TITLE_GIFT(pl))
+      pl->SetProp(P_TITLE,0);
+}
+
+void do_advance() {
+  int lv;
+
+  lv=this_player()->QueryProp(P_GUILD_LEVEL)+1;
+  if (lv<1) lv=1;
+  this_player()->SetProp(P_GUILD_LEVEL,lv);
+  adjust_title(this_player());
+}
+
+int try_advance() {
+  if (can_advance()) {
+    if (IsGuildMember(this_player()))
+      do_advance();
+    return 1;
+  }
+  return 0;
+}
+
+int beitreten() {
+  string res;
+  int erg;
+
+  if (res=check_restrictions(this_player(),QueryProp(P_GUILD_RESTRICTIONS))) {
+    // Werden die Beitrittsbedingungen erfuellt?
+    printf("Du kannst dieser Gilde nicht beitreten.\nGrund: %s",res);
+    return -3;
+  }
+  if (erg=(int)GUILDMASTER->beitreten()) {
+    if (erg<0)
+      return erg;
+    if (!(this_player()->QueryProp(P_GUILD_LEVEL)))
+      try_advance(); // Level 1 wird sofort vergeben
+    return 1;
+  }
+  return 0;
+}
+
+varargs int austreten(int loss) {
+    return (int)GUILDMASTER->austreten(loss);
+}
+
+int bei_oder_aus_treten(string str) {
+  if (!str) return 0;
+  if (sizeof(regexp(({lower_case(str)}),
+                    "\\<aus\\>.*gilde\\>.*\\<aus\\>")))
+    return austreten();
+  if (sizeof(regexp(({lower_case(str)}),
+                     "(gilde\\>.*\\<bei\\>|\\<in\\>.*gilde\\>.*\\<ein\\>)")))
+    return beitreten();
+  return 0;
+}
+
+varargs int
+AddSkill(string sname, mapping ski) {
+  mapping skills;
+
+  if (!sname)
+    return 0;
+
+  // per Query() abfragen, hier ist keine Kopie noetig.
+  if (!mappingp(skills=Query(P_GUILD_SKILLS))) {
+    skills=([]);
+    SetProp(P_GUILD_SKILLS,skills);
+  }
+  
+  if (!mappingp(ski))
+      ski=([]);
+  else
+      //Zur Sicherheit Kopie erstellen, wer weiss, was der Eintragende noch
+      //mit seinem Mapping macht...
+      ski=deep_copy(ski);
+
+  if (!stringp(ski[SI_SKILLFUNC]))
+    // Wenn keine Funktion angegeben ist, Funktionsname=Skillname
+    ski[SI_SKILLFUNC]=sname;
+
+  // Gilden-Offsets addieren
+  ski=AddSkillMappings(QueryProp(P_GLOBAL_SKILLPROPS),ski);
+
+  // Skill setzen.
+  skills[sname]=ski;
+  //SetProp() unnoetig, da oben per Query() das Originalmapping erhalten
+  //wurde.
+  //SetProp(P_GUILD_SKILLS,skills);
+  return 1;
+}
+
+
+varargs int
+AddSpell(string verb, mapping ski) {
+  mapping skills;
+
+  if (!verb)
+      return 0;
+  if (!mappingp(ski))
+      ski=([]);
+
+  if (!stringp(ski[SI_SPELLBOOK]) &&
+      !stringp(ski[SI_SPELLBOOK]=QueryProp(P_GUILD_DEFAULT_SPELLBOOK)))
+    // Wenn kein Spellbook angegeben ist muss ein
+    // Default-Spellbook angegeben sein, sonst Fehler
+    return 0;
+  if (file_size(SPELLBOOK_DIR+ski[SI_SPELLBOOK]+".c")<0)
+    return 0; // Spellbook sollte auch existieren...
+
+  return AddSkill(lower_case(verb),ski);
+}
+
+mapping QuerySkill(string skill) {
+  mapping ski;
+
+  // Abfrage per Query(), da vielleicht nur ein Skill gewuenscht
+  // wird und daher die komplette Kopie des Spellmappings in der Query-Methode
+  // unnoetig ist.
+  if (!skill
+      || !(ski=Query(P_GUILD_SKILLS, F_VALUE))
+      || !(ski=ski[skill])) // Gildenspezifische Skilleigenschaften
+    return 0;
+  // hier aber dann natuerlich eine Kopie erstellen. ;-)
+  return(deep_copy(ski));
+}
+
+mapping QuerySpell(string spell) {
+  mapping ski,ski2;
+  string spellbook,sfunc;
+
+  // QuerySkill() hier und QuerySpell() im Spellbook liefern Kopien der
+  // Skillmappings zurueck, Kopieren hier daher unnoetig.
+  if (!spell
+      || !(ski=QuerySkill(spell))
+      || !(spellbook=ski[SI_SPELLBOOK]))
+    return 0;
+  if (!(sfunc=ski[SI_SKILLFUNC]))
+    sfunc=spell;
+  spellbook=SPELLBOOK_DIR+spellbook;
+  if (!(ski2=(mapping)(spellbook->QuerySpell(sfunc))))
+    return 0;
+  return AddSkillMappings(ski2,ski); // Reihenfolge wichtig!
+  // Die Gilde kann Spelleigenschaften neu definieren!
+}
+
+varargs int
+UseSpell(object caster, string spell, mapping sinfo) {
+  mapping ski;
+  string spellbook;
+
+  if (!caster
+      || !spell
+      || !(ski=QuerySkill(spell)) // Existiert dieser Spell in dieser Gilde?
+      || !(spellbook=ski[SI_SPELLBOOK])) // Spellbook muss bekannt sein
+    return 0;
+  if (sinfo)
+    ski+=sinfo;
+  spellbook=SPELLBOOK_DIR+spellbook;
+  // printf("%O %O %O %O\n",spellbook,caster,spell,ski);
+  return (int)spellbook->UseSpell(caster,spell,ski);
+}
+
+static int
+InitialSkillAbility(mapping ski, object pl) {
+  if (!ski || !pl) return 0;
+  return (300*GetOffset(SI_SKILLLEARN,ski,pl)+
+          (200*(int)pl->QueryAttribute(A_INT)*
+           GetFactor(SI_SKILLLEARN,ski,pl))/100);
+}
+
+int
+SkillListe(int what) {
+  int res;
+  // Querymethode erstellt Kopie (wichtig!)
+  mapping allskills=QueryProp(P_GUILD_SKILLS);
+  string *skills=({});
+  string *spells=({});
+
+  if (!mappingp(allskills) || !sizeof(allskills))
+    return 0;
+
+  foreach(string s, mapping sdata: &allskills) {
+    // Lernlevel ermitteln und speichern.
+    mapping tmp=QuerySpell(s);
+    // wenn nix gefunden, dann ist es ein Skill, sonst ein Spell.
+    if (tmp) {
+      spells += ({s});
+      // das Spellbook hat im Zweifel das SI_SKILLINFO
+      sdata[SI_SKILLINFO] = tmp[SI_SKILLINFO];
+    }
+    else {
+      // SI_SKILLINFO steht in diesem Fall schon in sdata...
+      tmp = QuerySkill(s);
+      skills += ({s});
+    }
+    // gucken, obs nen Lernlevel gibt und einfach in sdata schreiben
+    if ( (tmp=tmp[SI_SKILLRESTR_LEARN]) )
+      sdata["_restr_level"] = tmp[P_LEVEL];
+  }
+
+  // jetzt sortieren.
+  closure cl = function int (string a, string b)
+    { return allskills[a]["_restr_level"] > allskills[b]["_restr_level"]; };
+  if (what & 0x01)
+    spells = sort_array(spells, cl);
+  if (what & 0x02)
+    skills = sort_array(skills, cl);
+
+  // und ausgeben
+  cl = function void (string *list)
+    {
+      string tmp="";
+      int lvl;
+      foreach(string sp: list) {
+        lvl = allskills[sp]["_restr_level"];
+        if (lvl>0)
+          tmp += sprintf("%-20s %d\n",sp,lvl);
+        else
+          tmp += sprintf("%-20s\n",sp);
+        if (allskills[sp][SI_SKILLINFO])
+          tmp += break_string(allskills[sp][SI_SKILLINFO], 78,
+                              "    ");
+      }
+      tell_object(PL, tmp);
+    };
+
+  if ((what & 0x01) && sizeof(spells)) {
+    res = 1;
+    tell_object(PL,sprintf(
+        "Du kannst versuchen, folgende Zaubersprueche zu lernen:\n"
+        "%-20s %-3s\n","Zauberspruch","Spielerstufe"));
+    funcall(cl, spells);
+    tell_object(PL,break_string(
+       "Du kannst einen Zauberspruch mit dem Kommando \"lerne\", gefolgt "
+       "vom Zauberspruchnamen lernen.",78));
+  }
+  if ((what & 0x02) && sizeof(skills)) {
+    res = 1;
+    tell_object(PL,sprintf(
+        "Du kannst versuchen, folgende Faehigkeiten zu erwerben:\n"
+        "%-20s %-3s\n","Faehigkeit","Spielerstufe"));
+    funcall(cl, skills);
+  }
+
+  return res;
+}
+
+varargs int
+LearnSpell(string spell,object pl) {
+  mapping ski,restr;
+  string res;
+  mixed learn_initfunc;
+  int abil,diff;
+
+  // Wenn kein pl gesetzt ist, nehmen wir this_player(), das ist der
+  // Normalfall.
+  if (!pl)
+	  pl=this_player();
+  if (!IsGuildMember(pl)) {
+    _notify_fail("Du gehoerst dieser Gilde nicht an!\n");
+    return 0;
+  }
+  _notify_fail("Was moechtest Du lernen?\n");
+  if (!spell)
+    return SkillListe(0x01);
+  spell=lower_case(spell);
+  if (!(ski=QuerySpell(spell)))
+    return 0;
+  if (pl->QuerySkill(spell)) {
+    _notify_fail("Du kannst diesen Spruch doch schon!\n");
+    return 0;
+  }
+  if ((restr=ski[SI_SKILLRESTR_LEARN])
+      && (res=check_restrictions(pl,restr))) {
+    printf("Du kannst diesen Spruch noch nicht lernen.\nGrund: %s",res);
+    return 1;
+  }
+  abil=InitialSkillAbility(ski,pl);
+  if (abil<1) abil=1;
+  if (abil>7500) abil=7500;
+  write("Du lernst einen neuen Zauberspruch.\n");
+  if (!(diff=GetFValueO(SI_DIFFICULTY,ski,pl)))
+    diff=GetFValueO(SI_SPELLCOST,ski,pl);
+  pl->ModifySkill(spell,abil,diff);
+  return 1;
+}
+
+int
+LearnSkill(string skill) {
+  mapping ski,restr;
+  object pl;
+  string res;
+  mixed learn_initfunc;
+  int abil,diff;
+
+  if (!IsGuildMember(pl=this_player())) {
+    _notify_fail("Du gehoerst dieser Gilde nicht an!\n");
+    return 0;
+  }
+  _notify_fail("Was moechtest Du lernen?\n");
+  if (!skill)
+    return SkillListe(0x02);
+  skill=capitalize(skill);
+  if (!(ski=QuerySkill(skill)))
+    return 0;
+  if (pl->QuerySkill(skill)) {
+    _notify_fail("Du hast diese Faehigkeit doch schon!\n");
+    return 0;
+  }
+  if ((restr=ski[SI_SKILLRESTR_LEARN])
+      && (res=check_restrictions(pl,restr))) {
+    printf("Du kannst diese Faehigkeit noch nicht erwerben.\nGrund: %s",res);
+    return 1;
+  }
+  abil=InitialSkillAbility(ski,pl);
+  if (!abil) abil=1;
+  if (abil>MAX_ABILITY) abil=MAX_ABILITY;
+  if (abil<-MAX_ABILITY) abil=-MAX_ABILITY;
+  write("Du erwirbst eine neue Faehigkeit.\n");
+  diff=GetFValueO(SI_DIFFICULTY,ski,pl);
+  pl->ModifySkill(skill,abil,diff);
+  return 1;
+}
+
+int GuildRating(object pl)
+{
+  mapping ski;
+  string *sk;
+  int i, cnt, sum;
+  closure qsa;
+  
+  if (!IsGuildMember(pl)||!query_once_interactive(pl))
+    return 0;
+
+  if (!(ski = QueryProp(P_GUILD_SKILLS)) ||
+      !(qsa = symbol_function("QuerySkillAbility",pl)))
+    return 0;
+
+  sk = m_indices(ski);
+  cnt = sizeof(ski);
+  
+  for (i=cnt-1, sum=0; i>=0; i--)
+    sum += funcall(qsa, sk[i]);
+
+  sum = sum/cnt;
+  if (sum < 0)
+    sum = 0;
+  else if (sum > MAX_ABILITY)
+    sum = MAX_ABILITY;
+  
+  return (int)pl->SetProp(P_GUILD_RATING, sum);
+}
+
+// Wird von /std/player/quest.c aufgerufen, wenn Quest geloest.
+// (Jedes mal - nicht nur beim ersten mal.)
+// Ist zum selbst-ueberschreiben gedacht, sollte aber definiert sein.
+void NotifyGiveQuest(object pl, string key){  }
+
diff --git a/std/gilden_room.c b/std/gilden_room.c
new file mode 100644
index 0000000..763de59
--- /dev/null
+++ b/std/gilden_room.c
@@ -0,0 +1,33 @@
+// MorgenGrauen MUDlib
+//
+// gilden_room.c -- Gildengebaeude
+//
+// $Id: gilden_room.c 6380 2007-07-20 22:32:38Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+// #define NEED_PROTOTYPES
+inherit "/std/gilde";
+inherit "/std/gilden_ob";
+
+#include <properties.h>
+#include <attributes.h>
+#include <new_skills.h>
+
+void create() {
+  gilde::create();
+  gilden_ob::create();
+  AddCmd(({"tritt","trete"}),"bei_oder_aus_treten");
+  AddCmd(({"lern","lerne"}),"LearnSpell");
+}
+
+int advance(string arg) {
+  int r1,r2;
+
+  r1=gilde::advance(arg);
+  r2=gilden_ob::try_advance();
+  return (r1||r2);
+}
diff --git a/std/hook_consumer.c b/std/hook_consumer.c
new file mode 100644
index 0000000..1da03b3
--- /dev/null
+++ b/std/hook_consumer.c
@@ -0,0 +1,19 @@
+// MorgenGrauen MUDlib

+//

+// /std/hook_consumer.c  - used to contain some template for hook consumers,

+//                         not needed anymore.

+//

+

+#pragma strong_types

+#pragma save_types

+#pragma no_clone

+#pragma pedantic

+#pragma range_check

+

+#include <hook.h>

+

+deprecated mixed HookCallback(object hookSource, int hookid, mixed hookData)

+{

+  return ({H_NO_MOD,hookData});

+}

+

diff --git a/std/hook_provider.c b/std/hook_provider.c
new file mode 100644
index 0000000..00b8ca3
--- /dev/null
+++ b/std/hook_provider.c
@@ -0,0 +1,493 @@
+// MorgenGrauen MUDlib
+//
+// /std/hook_provider.c  - Hooksystem
+//
+// $Id: hook_provider.c 9453 2016-02-04 21:22:54Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#undef NEED_PROTOTYPES
+
+// get the object fur closure <cl> (where the code is!)
+#define GET_OBJECT(cl) get_type_info(cl,2)
+
+// Struct describing one hook consumer. <cl> will be called when the hook is
+// triggered. The lower <prio>, the earlier the consumer will be called.
+// <type> is a consumertype, e.g. surveyor, listener etc. (see hook.h)
+struct hook_entry_s {
+  closure cl;
+  int prio;
+  int endtime;
+  int type;
+};
+
+// Struct holding the consumers of one specific hook.
+// the value arrays are guarenteed to exist, but may be empty.
+struct hook_s {
+  struct hook_entry_s *surveyors;
+  struct hook_entry_s *hmods;
+  struct hook_entry_s *dmods;
+  struct hook_entry_s *listeners;
+};
+
+/* hook mapping
+   the list of all offered hooks in the following structure:
+   ([hookid: (<hook_s>), ...
+   )]
+*/
+private nosave mapping hookMapping=([]);
+
+protected void CleanHookMapping(int *hookids);
+
+// Debugging - ggf. ueberschreiben
+protected int h_dbg() {return 0;}
+
+void HookTestOffer(int id, int stat){
+  if(h_dbg()) {
+      offerHook(id,stat);
+  }
+}
+
+void HookTestTrigger(int id, mixed data){
+  if(h_dbg()) {
+      HookFlow(id,data);
+  }
+}
+
+// NOTE: if you have the closure, you can call the lfun, even if it is
+// private. Therefore access to this data should be restricted to
+// this_object().
+// These two functions should be used for debugging purposes.
+protected mapping HCopyHookMapping(){
+  return deep_copy(hookMapping);
+}
+
+protected mapping HCopyHookConsumers(int hookid){
+  if(member(hookMapping,hookid)) {
+      CleanHookMapping(({hookid}));
+      return deep_copy(hookMapping[hookid]);
+  }
+  return 0;
+}
+
+// Ggf. zum Ueberschreiben.
+int HConsumerTypeIsAllowed(int type, object consumer){
+  return 1;
+}
+
+int HPriorityIsAllowed(int prio, object consumer){
+  return 1;
+}
+
+// clean internal hook data structures of stale hook consumers.
+protected void CleanHookMapping(int *hookids){
+  // hooks enthaelt die aufzuraeumenden Hooks. Wenn kein Array -> alle Hooks
+  if (!pointerp(hookids))
+    hookids=m_indices(hookMapping);
+
+  foreach(int hookid : hookids) { // alle Hooks
+    struct hook_s hooks = hookMapping[hookid];
+    if (!structp(hooks))
+      continue;
+    // ueber alle Consumertypen laufen
+    foreach (string consumertype: H_CONSUMERNAMES)
+    {
+      // Yeah - compute struct lookup at runtime... ;-)
+      struct hook_entry_s *consumers = hooks->(consumertype);
+      // Hookeintraege / Consumer
+      foreach(struct hook_entry_s h : &consumers) {
+        // alle abgelaufenen Eintraege oder solche mit zerstoerten Objekten
+        // nullen
+        if (!h->cl || h->endtime < time() )
+            h = 0;
+      }
+      // 0 noch rauswerfen.
+      hooks->(consumertype) -= ({0});
+    }
+  }
+}
+
+protected void offerHook(int hookid, int offerstate)
+{
+  H_DMSG(sprintf("offerHook hookid %d offerstate %d\n",hookid,offerstate));
+  if (hookid>0)
+  {
+      if (offerstate) {
+          if (!member(hookMapping,hookid)) {
+              struct hook_s hook = (<hook_s>
+                  surveyors: ({}),
+                  hmods: ({}),
+                  dmods: ({}),
+                  listeners: ({}) );
+              hookMapping[hookid] = hook;
+          }
+      }
+      else {
+          if (member(hookMapping,hookid)) {
+            m_delete(hookMapping,hookid);
+          }
+      }
+  }
+  H_DMSG(sprintf("  result %O\n",hookMapping));
+}
+
+// hookConsumerInfo() liefert Array von hook_entry_s zurueck. D.h. bei Abfrage
+// von Objekten alle Closures dieses Objekts und jede davon erzeugt ein
+// Element hook_entry_s im Ergebnisarray. Bei Abfrage von Closures hat das
+// Array immer genau 1 oder kein Element.
+// WARNING: whoever has a hook_entry_s can change/delete the hook!
+//          NEVER return the original to an external caller!
+// NOTE: whoever has the cl from hook_entry_s can call it, even if the lfun
+//       is private (and this object the only one knowing it).
+protected mixed * hookConsumerInfo(int hookid, object|closure consumer)
+{
+  closure filter_cl;
+
+  if (!member(hookMapping,hookid))
+    return ({});
+
+  // Closure zum Filtern bestimmen - je nachdem, was gesucht wird.
+  if (closurep(consumer))
+  {
+    filter_cl = function int (struct hook_entry_s h)
+                { return h->cl == consumer
+                         && h->endtime >= time(); };
+  }
+  else if (objectp(consumer))
+  {
+    filter_cl = function int (struct hook_entry_s h)
+                { return GET_OBJECT(h->cl) == consumer
+                         && h->endtime >= time(); };
+  }
+  else
+  {
+    return ({});
+  }
+
+  struct hook_s hook = hookMapping[hookid];
+  struct hook_entry_s *result = ({});
+  foreach (string consumertype: H_CONSUMERNAMES )
+  {
+    result += filter(hook->(consumertype), filter_cl);
+  }
+  return result;
+}
+
+int HIsHookConsumer(int hookid, mixed consumer) {
+  return sizeof(hookConsumerInfo(hookid,consumer)) != 0;
+}
+
+int* HListHooks() {
+  return m_indices(hookMapping);
+}
+
+int HUnregisterFromHook(int hookid, mixed consumer) {
+
+  H_DMSG(sprintf("HUnregisterFromHook hookid %d consumer %O\n",hookid,consumer));
+  if (objectp(consumer))
+    consumer = symbol_function("HookCallback", consumer);
+  if (!closurep(consumer))
+    return 0;
+
+  struct hook_entry_s *info = hookConsumerInfo(hookid,consumer);
+
+  // it should never happen that a closure is registered more than once, i.e.
+  // the result contains more than one element.
+  if (sizeof(info)) {
+      struct hook_entry_s h = info[0];
+      h->cl = 0;
+      H_DMSG(sprintf("  result %O\n", hookMapping));
+      // the now invalid h will be cleaned up later.
+      return 1;
+  }
+  return 0;
+}
+
+// surveyors are asked for registration permittance
+protected int askSurveyorsForRegistrationAllowance(
+                  struct hook_entry_s *surveyors, object consumer,int hookid,
+                  int hookprio,int consumertype)
+{
+  H_DMSG(sprintf("askSurveyorsForRegistrationAllowance surveyors %O, "
+        "consumer %O, hookid %d, hookprio %d, consumertype %d\n",
+        surveyors,consumer,hookid,hookprio,consumertype));
+
+  foreach(struct hook_entry_s surveyor : surveyors) {
+    if (closurep(surveyor->cl) && surveyor->endtime >= time())
+    {
+      // surveyor hook gueltig.
+      object sob = GET_OBJECT(surveyor->cl);
+      if (!sob->HookRegistrationCallback(consumer, hookid,
+                        this_object(), hookprio, consumertype))
+        return 0;
+    }
+  }
+  return 1;
+}
+
+int HRegisterToHook(int hookid, mixed consumer, int hookprio,
+                       int consumertype, int timeInSeconds) {
+  int ret, regtime;
+
+  if (!closurep(consumer) && !objectp(consumer))
+    raise_error(sprintf("Wrong argument %.50O to HRegisterToHook(): consumer "
+          "must be closure or object.\n",consumer));
+
+  if (!member(hookMapping, hookid))
+    return -1;
+
+  if (objectp(consumer)) {
+    consumer = symbol_function("HookCallback", consumer);
+    if (!closurep(consumer))
+      return -2;
+  }
+
+  if (timeInSeconds > 0) {
+      regtime=time() + timeInSeconds;
+  }
+  else {
+      regtime=__INT_MAX__;
+  }
+
+  H_DMSG(sprintf("HRegisterToHook hookid %d consumer %O\n hookprio %d "
+        "consumertype %d\n",hookid,consumer,hookprio,consumertype));
+
+  CleanHookMapping(({hookid})); // entfernt ungueltige/abgelaufene Eintraege
+
+  // nur einmal pro closure registrieren!
+  if (HIsHookConsumer(hookid, consumer))
+    return -3;
+
+  // Consumertyp erlaubt?
+  if (H_CONSUMERCHECK(consumertype) == -1
+      || !HConsumerTypeIsAllowed(consumertype,GET_OBJECT(consumer)))
+    return -4;
+
+  // Prioritaet erlaubt?
+  if (H_HOOK_VALIDPRIO(hookprio) == -1
+      || !HPriorityIsAllowed(hookprio, GET_OBJECT(consumer)))
+    return -5;
+
+  struct hook_s hook = hookMapping[hookid];
+
+  // Und surveyors erlauben die Registierung?
+  if (!askSurveyorsForRegistrationAllowance(hook->surveyors,
+                    GET_OBJECT(consumer),hookid,
+                    hookprio,consumertype))
+    return -6;
+
+  string ctypename = H_CONSUMERNAMES[consumertype];
+
+  // get the consumer array
+  struct hook_entry_s *consumers = hook->(ctypename);
+
+  // assemble new hook consumer struct
+  struct hook_entry_s newconsumer = (<hook_entry_s>
+                      cl : consumer,
+                      prio : hookprio,
+                      endtime : regtime,
+                      type : consumertype );
+
+  // consumers enthaelt die Hookeintraege
+  if (sizeof(consumers) < MAX_HOOK_COUNTS[consumertype]) {
+    // max. Anzahl an Eintraegen fuer diesen Typ noch nicht
+    // erreicht, direkt anhaengen.
+    consumers += ({ newconsumer });
+    hook->(ctypename) = consumers;
+    ret=1;
+  }
+  else {
+    // gibt es einen Eintrag mit hoeherem Priowert (niedrigere
+    // Prioritaet), den man ersetzen koennte?
+    // Das Array ist sortiert, mit hoechsten Priowerten am
+    // Ende. Ersetzt werden soll der Eintrag mit dem hoechsten
+    // Zahlenwert, falls der neue Eintrag einen niedrigeren Wert
+    // hat, d.h. es muss nur er letzte Consumer im Array geprueft werden.
+    // Pruefung auf Closureexistenz, falls der Surveyor Objekte
+    // zerstoert (hat)...
+    struct hook_entry_s oh = consumers[<1];
+    if (!oh->cl || oh->prio > newconsumer->prio) {
+      // Found superseedable entry - replace it, but inform the object.
+      H_DMSG("Found superseedable entry\n");
+      consumers[<1] = newconsumer;
+      GET_OBJECT(oh->cl)->superseededHook(hookid, this_object());
+      ret = 1;
+      // nicht noetig, consumers wieder in sein hook_s zu haenngen wegen Array
+      // -> Referenz
+    }
+  }
+
+  // wenn ein Eintrag hinzugefuegt wurde, muss neu sortiert werden
+  if (ret) {
+    hook->(ctypename) = sort_array(consumers,
+        function int (struct hook_entry_s a, struct hook_entry_s b) {
+            return a->prio > b->prio; } );
+  }
+
+  H_DMSG(sprintf("  result %O\n",hookMapping));
+
+  // -7, wenn kein Eintrag mehr frei / zuviele Hooks
+  return (ret > 0 ? 1 : -7);
+}
+
+// Conveniences wrapper for simple listener hooks
+int HRegisterListener(int hookid, mixed consumer)
+{
+  return HRegisterToHook(hookid, consumer, H_HOOK_OTHERPRIO(2), H_LISTENER, 0);
+}
+
+// Cnveniences wrapper for simple modificator hooks
+int HRegisterModifier(int hookid, mixed consumer)
+{
+  return HRegisterToHook(hookid, consumer, H_HOOK_OTHERPRIO(2),
+                         H_HOOK_MODIFICATOR, 0);
+}
+
+// surveyors are asked for cancellation permittance
+protected int askSurveyorsForCancelAllowance(mixed surveyors,
+  object modifiyingOb,mixed data,int hookid,int prio,object hookOb){
+
+  foreach(struct hook_entry_s surveyor : surveyors) {
+    if (closurep(surveyor->cl) && surveyor->endtime >= time())
+    {
+      // surveyor hook gueltig.
+      object sob = GET_OBJECT(surveyor->cl);
+      if (!sob->HookCancelAllowanceCallback(modifiyingOb, hookid,
+                        hookOb, prio, data))
+        return 0;
+    }
+  }
+  return 1;
+}
+
+// surveyors are asked for data change permittance
+protected int askSurveyorsForModificationAllowance(mixed surveyors,
+  object modifiyingOb,mixed data,int hookid,int prio,object hookOb){
+
+  foreach(struct hook_entry_s surveyor : surveyors) {
+    if (closurep(surveyor->cl) && surveyor->endtime >= time())
+    {
+      // surveyor hook gueltig.
+      object sob = GET_OBJECT(surveyor->cl);
+      if (!sob->HookModificationAllowanceCallback(modifiyingOb,
+                        hookid, hookOb, prio, data))
+        return 0;
+    }
+  }
+  return 1;
+}
+
+protected mixed HookFlow(int hookid, mixed hookdata){
+  mixed tmp, ret;
+
+  ret=({H_NO_MOD,hookdata});
+
+  H_DMSG(sprintf("HookFlow hookid %d hookdata %O\n",hookid,hookdata));
+
+  if (!member(hookMapping, hookid))
+    return ret;
+
+  struct hook_s hook = hookMapping[hookid];
+
+  // notify surveyors
+  foreach(struct hook_entry_s h : hook->surveyors) {
+    if (closurep(h->cl) && h->endtime >= time())
+    {
+      // Hook gueltig
+      tmp = funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+      if(tmp[H_RETCODE]==H_CANCELLED) {
+        ret[H_RETCODE]=H_CANCELLED;
+        return ret;  // und weg...
+      }
+      else if(tmp[H_RETCODE]==H_ALTERED){
+        ret[H_RETCODE]=H_ALTERED;
+        ret[H_RETDATA]=tmp[H_RETDATA];
+      }
+    }
+    // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+    else if (find_call_out(#'CleanHookMapping) == -1) {
+      call_out(#'CleanHookMapping, 0, ({hookid}));
+    }
+  } // surveyors fertig
+
+  // notify hmods
+  foreach(struct hook_entry_s h : hook->hmods) {
+    if (closurep(h->cl) && h->endtime >= time())
+    {
+      // Hook gueltig
+      tmp = funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+      if(tmp[H_RETCODE]==H_CANCELLED) {
+        // ask allowance in surveyors
+        if(h->cl &&
+           askSurveyorsForCancelAllowance(hook->surveyors,
+              GET_OBJECT(h->cl), hookdata, hookid,
+              h->prio, this_object()))
+        {
+            ret[H_RETCODE] = H_CANCELLED;
+            return ret; // und raus...
+        }
+      }
+      else if(tmp[H_RETCODE]==H_ALTERED) {
+        // ask allowance in surveyors
+        if(h->cl &&
+           askSurveyorsForModificationAllowance(hook->surveyors,
+              GET_OBJECT(h->cl),hookdata, hookid,
+              h->prio,this_object()))
+        {
+            ret[H_RETCODE] = H_ALTERED;
+            ret[H_RETDATA] = tmp[H_RETDATA];
+        }
+      }
+    }
+    // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+    else if (find_call_out(#'CleanHookMapping) == -1) {
+      call_out(#'CleanHookMapping, 0, ({hookid}));
+    }
+  } // hmods fertig
+
+  // notify dmods
+  foreach(struct hook_entry_s h : hook->dmods) {
+    if (closurep(h->cl) && h->endtime >= time())
+    {
+      // Hook gueltig
+      tmp = funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+      if(tmp[H_RETCODE]==H_ALTERED) {
+        // ask allowance in surveyors
+        if (h->cl &&
+            askSurveyorsForModificationAllowance(hook->surveyors,
+              GET_OBJECT(h->cl),hookdata, hookid,
+              h->prio,this_object()))
+        {
+            ret[H_RETCODE] = H_ALTERED;
+            ret[H_RETDATA] = tmp[H_RETDATA];
+        }
+      }
+    }
+    // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+    else if (find_call_out(#'CleanHookMapping) == -1) {
+      call_out(#'CleanHookMapping, 0, ({hookid}));
+    }
+  } // dmods fertig
+
+  // notify listener
+  foreach(struct hook_entry_s h : hook->listeners) {
+    if (closurep(h->cl) && h->endtime >= time())
+    {
+      // Hook gueltig
+      funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+    }
+    // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+    else if (find_call_out(#'CleanHookMapping) == -1) {
+      call_out(#'CleanHookMapping, 0, ({hookid}));
+    }
+  } // listener fertig
+
+  return ret;
+}
+
diff --git a/std/hook_surveyor.c b/std/hook_surveyor.c
new file mode 100644
index 0000000..bafb193
--- /dev/null
+++ b/std/hook_surveyor.c
@@ -0,0 +1,41 @@
+// MorgenGrauen MUDlib
+//
+// /std/hook_surveyor.c  - Basisklasse fuer Surveyor-Hooks
+//
+
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#include <hook.h>
+
+// override as wished and needed
+
+int HookRegistrationCallback(object registringObject, int hookid,
+                        object hookSource, int registringObjectsPriority,
+                        int registringObjectsType)
+{
+  return 1;
+}
+
+int HookCancelAllowanceCallback(object cancellingObject, int hookid,
+                        object hookSource, int cancellingObjectsPriority,
+                        mixed hookData)
+{
+  return 1;
+}
+
+int HookModificationAllowanceCallback(object modifyingObject, int hookid,
+                        object hookSource, int modifyingObjectsPriority,
+                        mixed hookData)
+{
+  return 1;
+}
+
+mixed HookCallback(object hookSource, int hookid, mixed hookData)
+{
+  return ({H_NO_MOD,hookData});
+}
+
diff --git a/std/inpc.c b/std/inpc.c
new file mode 100644
index 0000000..63edd67
--- /dev/null
+++ b/std/inpc.c
@@ -0,0 +1,62 @@
+// MorgenGrauen MUDlib
+//
+// inpc.c -- Intelligenter NPC
+//
+// $Id: inpc.c 6571 2007-10-21 14:41:10Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/npc";
+inherit "/std/inpc/nobank";
+inherit "/std/inpc/select";
+inherit "/std/inpc/boozing";
+inherit "/std/inpc/items";
+inherit "/std/inpc/eval";
+
+#include <moving.h>
+#include <inpc.h>
+#define ME this_object()
+#define ENV environment()
+#define TP this_player()
+
+#pragma strong_types
+
+protected void create() {
+  npc::create();
+  items::create();
+  add_select_commands();
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset() {
+  npc::reset();
+  items::reset();
+}
+
+void init() {
+  npc::init();
+  if (TP && query_once_interactive(TP))
+    SetProp(P_INPC_LAST_PLAYER_CONTACT,time());
+}
+
+varargs int move(mixed dest, int meth, string dir, string tin, string tout) {
+  int res;
+  object env;
+
+  env=ENV;
+  if (!(meth&M_NOCHECK)
+      && !ENV
+      && QueryProp(P_INPC_WALK_MODE)
+      && !may_enter_room(dest))
+    return ME_CANT_TPORT_IN;
+  res=::move(dest,meth,dir,tin,tout);
+  if (env!=ENV)
+    SetProp(P_INPC_LAST_ENVIRONMENT,ENV);
+  return res;
+}
diff --git a/std/inpc/.readme b/std/inpc/.readme
new file mode 100644
index 0000000..06c1f14
--- /dev/null
+++ b/std/inpc/.readme
@@ -0,0 +1,11 @@
+
+INPC: Intelligenter NPC
+*** IN ENTWICKLUNG - BENUTZUNG AUF EIGENE GEFAHR ***
+
+nobank:   Moneyhandler+Behandlung von Bankzweities
+walking:  Laufmonster, automatische Routensuche
+boozing:  Optimale Saeuferei
+select:   Auswahl guter Objekte (beste Waffe/Ruestung...)
+eval.c:   Gegner einschaetzen
+items.c:  Waffen, Ruestungen einfach einfuegen, automatisch im Reset zuecken
+
diff --git a/std/inpc/boozing.c b/std/inpc/boozing.c
new file mode 100644
index 0000000..4f24f52
--- /dev/null
+++ b/std/inpc/boozing.c
@@ -0,0 +1,62 @@
+// MorgenGrauen MUDlib
+//
+// inpc/boozing.c -- Intelligentes Saufen
+//
+// $Id: boozing.c 8396 2013-02-18 21:56:37Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing.h>
+#include <living.h>
+#include <inpc.h>
+#include <container/moneyhandler.h>
+#include <properties.h>
+
+#define DRINK_IDS 1
+#define DRINK_COST 2
+#define DRINK_HEAL 3
+#define DRINK_ALC 4
+#define DRINK_SAT 5
+
+int Drink() {
+  mixed drinks;
+  object env;
+  int i,max,mon,hdiff;
+ 
+  if ((mon=QueryMoney())<=0
+      || !(env=environment())
+      || !pointerp(drinks=env->query_drink())
+      || (hdiff=QueryProp(P_MAX_HP)-QueryProp(P_HP))<=0)
+    return 0;
+  max=-1;
+  for (i=sizeof(drinks)-1;i>=0;i--) {
+    if (drinks[i][DRINK_COST]>mon
+        || ((drinks[i][DRINK_ALC]>0) &&
+	    ((drinks[i][DRINK_ALC]+QueryProp(P_ALCOHOL))
+	     > (100-QueryProp(P_I_HATE_ALCOHOL))))
+        || drinks[i][DRINK_SAT]+QueryProp(P_DRINK)>100
+        || drinks[i][DRINK_HEAL]<=0)
+      continue;
+    if (max<0
+        || (drinks[i][DRINK_HEAL]>drinks[max][DRINK_HEAL] &&
+            drinks[max][DRINK_HEAL]<hdiff)
+        || (drinks[i][DRINK_HEAL]>=hdiff &&
+            drinks[i][DRINK_COST]<drinks[max][DRINK_COST]))
+      max=i;
+  }
+  if (max<0) return 0;
+  command("bestell "+drinks[max][DRINK_IDS][0]);
+  return 1;
+}
+
+void DrinkLoop() {
+  while (remove_call_out("DrinkLoop")>=0);
+  if (!Drink())
+    return;
+  call_out("DrinkLoop",0);
+}
+
diff --git a/std/inpc/eval.c b/std/inpc/eval.c
new file mode 100644
index 0000000..f755576
--- /dev/null
+++ b/std/inpc/eval.c
@@ -0,0 +1,189 @@
+// MorgenGrauen MUDlib
+//
+// inpc/eval.c -- Einschaetzen von Gegnern
+//
+// $Id: eval.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <living.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <thing.h>
+#include <inpc.h>
+
+varargs void SetEvalFactor(mixed what, int fac, int off) {
+  mapping offs,facs;
+
+  if (!mappingp(facs=QueryProp(P_EVAL_FACTORS))) facs=([]);
+  if (!mappingp(offs=QueryProp(P_EVAL_OFFSETS))) offs=([]);
+  facs[what]=fac;
+  offs[what]=off;
+  SetProp(P_EVAL_FACTORS,facs);
+  SetProp(P_EVAL_OFFSETS,offs);
+}
+
+void SetDefaultEvalFactors() {
+  SetEvalFactor(P_HP,150,-60);
+  SetEvalFactor(P_TOTAL_WC,250,0);
+  SetEvalFactor(P_TOTAL_AC,1000,-50);
+  SetEvalFactor("abenteurer_pfeil",3,0);
+  SetEvalFactor("abenteurer_feuerball",8,0);
+}
+
+varargs int
+modify_eval(object ob, mixed what, mapping fac, mapping off, int val) {
+  if (!val) val=QueryProp(what);
+  val-=off[what];
+  if (val<0) val=0;
+  val=(val*fac[what])/10000;
+  if (val>200) val=200;
+  return val;
+}
+
+varargs int eval_enemy(object enemy, mapping fac, mapping off) {
+  int res;
+  string gilde;
+  
+  if (!objectp(enemy)) return 0;
+  if (!mappingp(fac)) fac=QueryProp(P_EVAL_FACTORS);
+  if (!mappingp(fac)) fac=([]);
+  if (!mappingp(off)) off=QueryProp(P_EVAL_OFFSETS);
+  if (!mappingp(off)) off=([]);
+
+  res=0;
+  res+=modify_eval(enemy,P_TOTAL_WC,fac,off);
+  res+=modify_eval(enemy,P_TOTAL_AC,fac,off);
+  res+=modify_eval(enemy,P_HP,fac,off);
+  if (stringp(gilde=enemy->QueryProp(P_GUILD)))
+    res+=call_other(this_object(),"eval_guild_"+gilde,enemy,fac,off,gilde);
+  if (res<0) res=0;
+  if (res>1000) res=1000;
+  return res;
+}
+
+varargs mixed *eval_enemies(mapping fac, mapping off, object *here) {
+  int i,sz;
+  mixed *res;
+
+  res=({});
+  if (!here)
+    here=PresentEnemies();
+  if (!pointerp(here) || !(sz=sizeof(here)))
+    return res;
+  if (!mappingp(fac)) fac=QueryProp(P_EVAL_FACTORS);
+  if (!mappingp(fac)) fac=([]);
+  if (!mappingp(off)) off=QueryProp(P_EVAL_OFFSETS);
+  if (!mappingp(off)) off=([]);
+
+  for (i=sz-1;i>=0;i--)
+    res+=({({here[i],eval_enemy(here[i],fac,off)})});
+  return res;
+}
+
+varargs int sum_eval_enemies(mapping fac, mapping off, object *here) {
+  int res,i;
+  mixed *evals,x,y;
+
+  res=0;
+  if (!pointerp(evals=eval_enemies(fac,off,here)))
+    return res;
+  for (i=sizeof(evals)-1;i>=0;i--)
+    if (pointerp(x=evals[i]) && sizeof(x)>=2 && intp(y=x[1]))
+      res+=y;
+  return res;
+}
+
+varargs object *minimize_prop_filt(object *here, mixed prop) {
+  object *obs,ob;
+  int i,mhp,hp,sz;
+  
+  obs=0;
+  if (!pointerp(here))
+    here=PresentEnemies();
+  if (!prop)
+    prop=P_HP;
+  for (i=sizeof(here)-1;i>=0;i--) {
+    if (objectp(ob=here[i])) {
+      hp=ob->QueryProp(prop);
+      if (!pointerp(obs) || hp<mhp) {
+	obs=({ob});
+	mhp=hp;
+      } else if (hp==mhp) {
+	obs+=({ob});
+      }
+    }
+  }
+  if (!pointerp(obs))
+    obs=({});
+  return obs;
+}
+
+varargs object *maximize_prop_filt(object *here, mixed prop) {
+  object *obs,ob;
+  int i,mwc,wc,sz;
+  
+  obs=0;
+  if (!pointerp(here))
+    here=PresentEnemies();
+  if (!prop)
+    prop=P_TOTAL_WC;
+  for (i=sizeof(here)-1;i>=0;i--) {
+    if (objectp(ob=here[i])) {
+      wc=ob->QueryProp(prop);
+      if (!pointerp(obs) || wc<mwc) {
+	obs=({ob});
+	mwc=wc;
+      } else if (wc==mwc) {
+	obs+=({ob});
+      }
+    }
+  }
+  if (!pointerp(obs))
+    obs=({});
+  return obs;
+}
+
+varargs object *player_filt(object *here) {
+  object *obs,ob;
+  int i;
+  
+  obs=({});
+  if (!pointerp(here))
+    here=PresentEnemies();
+  for (i=sizeof(here)-1;i>=0;i--)
+    if (objectp(ob=here[i]) && query_once_interactive(ob))
+      obs+=({ob});
+  return obs;
+}
+
+varargs object select_enemy_min_prop(object *here, mixed prop) {
+  object *obs;
+  int sz;
+
+  if (pointerp(obs=minimize_prop_filt(here,prop)) && (sz=sizeof(obs)))
+    return obs[random(sz)];
+  return 0;
+}
+
+varargs object select_enemy_max_prop(object *here, mixed prop) {
+  object *obs;
+  int sz;
+
+  if (pointerp(obs=maximize_prop_filt(here,prop)) && (sz=sizeof(obs)))
+    return obs[random(sz)];
+  return 0;
+}
+
+varargs object select_player_min_prop(object *here, mixed prop) {
+  return select_enemy_min_prop(player_filt(here),prop);
+}
+
+varargs object select_player_max_prop(object *here, mixed prop) {
+  return select_enemy_max_prop(player_filt(here),prop);
+}
diff --git a/std/inpc/items.c b/std/inpc/items.c
new file mode 100644
index 0000000..fafdd04
--- /dev/null
+++ b/std/inpc/items.c
@@ -0,0 +1,78 @@
+// MorgenGrauen MUDlib
+//
+// inpc/items.c -- Die richtige Ausruestung tragen
+//
+// $Id: items.c 6571 2007-10-21 14:41:10Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <thing.h>
+#include <inpc.h>
+#include <properties.h>
+#include <moving.h>
+
+protected void create() {
+  SetProp(P_ITEMS,({0}));
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void AddWeapon(string nm, string path) {
+  object ob;
+  mixed *it;
+  
+  if (!path || !nm) return;
+  if (ob=clone_object(path)) {
+    ob->move(this_object(),M_NOCHECK);
+    command("zuecke "+nm);
+  }
+  it=QueryProp(P_ITEMS);
+  if (!pointerp(it) || !sizeof(it)) it=({0});
+  it[0]=({ob,path,nm});
+  SetProp(P_ITEMS,it);
+}
+
+void AddArmour(string nm, string path) {
+  object ob;
+  mixed *it;
+  
+  if (!path || !nm) return;
+  if (ob=clone_object(path)) {
+    ob->move(this_object(),M_NOCHECK);
+    command("zieh "+nm+" an");
+  }
+  it=QueryProp(P_ITEMS);
+  if (!pointerp(it) || !sizeof(it)) it=({0});
+  it+=({ob,path,nm});
+  SetProp(P_ITEMS,it);
+}
+
+void reset() {
+  mixed *it,x;
+  int i;
+  object ob;
+  
+  if (!pointerp(it=QueryProp(P_ITEMS))) return;
+  for (i=sizeof(it)-1;i>=0;i--) {
+    x=it[i];
+    if (!pointerp(x) || sizeof(x)<3) continue;
+    if (!objectp(x[0])) {
+      if (ob=clone_object(x[1]))
+	ob->move(this_object(),M_NOCHECK);
+      x[0]=ob;
+    }
+    if (objectp(x[0])) {
+      if (i)
+	command("zieh "+x[2]+" an");
+      else
+	command("zuecke "+x[2]);
+    }
+  }
+}
diff --git a/std/inpc/nobank.c b/std/inpc/nobank.c
new file mode 100644
index 0000000..6f04aef
--- /dev/null
+++ b/std/inpc/nobank.c
@@ -0,0 +1,100 @@
+// MorgenGrauen MUDlib
+//
+// inpc/nobank.c -- Nieder mit Bankzweities!
+//
+// $Id: nobank.c 8966 2014-11-19 21:41:12Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/moneyhandler";
+
+#include <properties.h>
+#include <attributes.h>
+#include <money.h>
+#define ME this_object()
+
+int DeepQueryMoney(object ob) {
+  int res,i;
+  object *obs;
+  
+  if (!ob) return 0;
+  res=0;obs=deep_inventory(ob);
+  for (i=sizeof(obs)-1;i>=0;i--)
+    if ((ob=obs[i]) && load_name(ob)==GELD)
+      res+=ob->QueryProp(P_AMOUNT);
+  return res;
+}
+
+int is_bank(object ob) {
+  int *stats,geld;
+  
+  if (!ob || !query_once_interactive(ob)) return 0;
+  stats=({ob->QueryAttribute(A_STR),
+	  ob->QueryAttribute(A_INT),
+	  ob->QueryAttribute(A_DEX),
+	  ob->QueryAttribute(A_CON),
+	  0});
+  stats[4]=stats[1]+stats[2]+stats[3];
+  geld=DeepQueryMoney(ob);
+  
+  if (stats[0]>=10
+      && stats[4]<stats[0]
+      && geld>5000
+      && ob->QueryProp(P_XP)<300000
+      && ob->QueryProp(P_QP)<300)
+    return geld;
+  return 0;
+}
+
+int DeepTransferMoney(object pl, int amount) {
+  object *obs,ob;
+  int act,trans,i;
+  
+  if (!pl || amount<=0)
+    return 0;
+  if (pl->QueryMoney()>=amount) {
+    pl->AddMoney(-1*amount);
+    trans=amount;
+  } else {
+    obs=deep_inventory(pl);trans=0;
+    for (i=sizeof(obs)-1;i>=0;i--) {
+      if ((ob=obs[i]) && load_name(ob)==GELD) {
+	act=ob->QueryProp(P_AMOUNT);
+	if (act<=0) continue;
+	if (act>=amount) { // mehr Geld als benoetigt?
+	  ob->SetProp(P_AMOUNT,act-amount); // abziehen
+	  trans+=amount; 
+	  amount=0; // nichts mehr benoetigt
+	  break;    // also auch ende
+	}
+	amount-=act;
+	trans+=act;
+	ob->remove(); // abziehen was da ist
+      }
+    }
+  }
+  log_file("stolen_money",
+	   sprintf("%s %O: %O (%O)\n",ctime(time())[4..15],pl,trans,ME));
+  AddMoney(trans);
+  return trans;
+}
+
+varargs int TestAndTransfer(object ob, int trans, string msg) {
+  int geld,abzug;
+
+  if ((geld=is_bank(ob))<=0) return 0;
+  if (trans<0)
+    abzug=(geld*trans)/-100; // Prozentsatz
+  else
+    abzug=trans;             // fester Abzug
+  if (abzug>geld) abzug=geld;
+  if (abzug<=0) return 0;
+  abzug=DeepTransferMoney(ob,abzug);
+  if (abzug<=0) return 0;  
+  if (msg && ob)
+    tell_object(ob,sprintf(msg,abzug));
+  return abzug;
+}
diff --git a/std/inpc/select.c b/std/inpc/select.c
new file mode 100644
index 0000000..d73ebed
--- /dev/null
+++ b/std/inpc/select.c
@@ -0,0 +1,337 @@
+// MorgenGrauen MUDlib
+//
+// inpc/select.c -- Beste Ausruestung suchen
+//
+// $Id: select.c 6998 2008-08-24 17:17:46Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <thing.h>
+#include <living.h>
+#include <inpc.h>
+#include <properties.h>
+#include <combat.h>
+#include <language.h>
+
+#define ME this_object()
+
+mapping scan_objects(mixed src) {
+  // Ermittelt zu jedem Typ (Waffe, Ruestungstyp...) alle Objekte diesen Typs
+  // Gesucht wird: - Im Inventory, falls Objekt angegeben
+  //               - Im Array, falls Array angegeben
+  object *obs;
+  mapping res;
+  mixed x;
+  int i,cost,cost_limit;
+
+  if (mappingp(src))
+    return src;
+  res=([]);
+  if (objectp(src))
+    src=all_inventory(src);
+  if (!pointerp(src))
+    src=({src});
+  
+  cost_limit = get_eval_cost()/3;
+  foreach(object ob: src)
+  {
+    if ( (cost=get_eval_cost()) < cost_limit )
+    {
+      log_file("rochus/raeuber_eval",
+        ctime()+"select::scan_objects(). Rest "+to_string(cost)+
+        ", i="+to_string(i)+", Size "+to_string(sizeof(src))+".\n");
+      return res;
+      break;
+    }
+    if (!objectp(ob))
+      continue;
+    if (x=ob->QueryProp(P_ARMOUR_TYPE))
+      ;
+    else if (ob->QueryProp(P_WEAPON_TYPE))
+      x=OT_WEAPON;
+    else if (ob->QueryProp(P_COMBATCMDS))
+      x=OT_COMBAT_OBJECT;
+    else
+      x=OT_MISC;
+    if (!pointerp(obs=res[x]))
+      obs=({});
+    obs+=({ob});
+    res[x]=obs;
+  }
+  return res;
+}
+
+mixed *find_object_by_type(mixed from, mixed typ) {
+  // Findet all Objekte eines Typs, z.B. alle Waffen im Monster
+  // <from> kann auch ein Mapping sein, das schon die mit scan_objects
+  // erstellt Klassifikation enthaelt, damit diese nicht mehrfach
+  // erstellt werden muss.
+  mixed res;
+
+  if (!mappingp(from))
+    from=scan_objects(from);
+  if (!mappingp(from) ||
+      !pointerp(res=from[typ]))
+    return ({});
+  return res;
+}
+
+int eval_weapon(object ob) {
+  return ob->QueryProp(P_WC);
+}
+
+object find_best_weapon(mixed from) {
+  // Findet die beste Waffe
+  int i,wc,bwc,cost,cost_limit;
+  object *res,bob;
+
+  if (!pointerp(res=find_object_by_type(from, OT_WEAPON)))
+    return 0;
+  bwc=-1;bob=0;
+  
+  cost_limit = get_eval_cost()/3;
+  for (i=sizeof(res);i--;)
+  foreach(object ob: res)
+  {
+    if (!objectp(ob)) continue;
+    wc=eval_weapon(ob);
+    if (wc>bwc)
+    {
+      bob=ob;
+      bwc=wc;
+    }
+    
+    if ( (cost=get_eval_cost()) < cost_limit )
+    {
+      log_file("rochus/raeuber_eval",
+        "Break in select::find_best_weapon(). Rest-Ticks "+to_string(cost)+
+        ", i = "+to_string(i)+", Size "+to_string(sizeof(res))+".\n");
+      return bob; // zurueckgeben, was bisher drinsteht.
+      break;
+    } 
+  }
+  return bob;
+}
+
+varargs int wield_best_weapon(mixed from) {
+  // Zueckt die beste Waffe.
+  // Sollte mit command("zuecke_beste_waffe") aufgerufen werden,
+  // damit this_player() richtig gesetzt wird.
+  object ob,old;
+
+  // Einige NPC moegen keine Waffen. Zum Beispiel Karate-Gilden-NPC
+  // Durch Setzen von INPC_DONT_WIELD_WEAPONS kann man das Zuecken verhindern
+  if(QueryProp(INPC_DONT_WIELD_WEAPONS)) return 0;
+
+  if (!from)
+    from=ME;
+  
+  if (!objectp(ob=find_best_weapon(from)))
+    return 0;
+  if (objectp(old=QueryProp(P_WEAPON)) && old!=ob) {
+    old->RemoveId(INPC_BEST_WEAPON_ID);
+    old->DoUnwield();
+  }
+  if (!ob->id(INPC_BEST_WEAPON_ID))
+    ob->AddId(INPC_BEST_WEAPON_ID);
+
+  return ob->DoWield();
+}
+
+int eval_armour(object ob) {
+  return ob->QueryProp(P_AC);
+}
+
+object find_best_armour(mixed from, mixed typ) {
+  // Findet die beste Ruestung eines Typs
+  int i,ac,bac;
+  object *res,bob;
+  
+  if (!pointerp(res=find_object_by_type(from, typ)))
+    return 0;
+  bac=-1;
+  bob=0;
+  foreach(object ob: res)
+  {
+    if (!objectp(ob)) continue;
+    ac=eval_armour(ob);
+    if (ac>bac) 
+    {
+      bob=ob;
+      bac=ac;
+    }
+  }
+  return bob;
+}
+
+object *find_best_armours(mixed from) {
+  // Findet die besten Ruestungen, die gleichzeitig getragen werden koennen
+  mapping ol;
+  object *res,ob;
+  mixed *types;
+  int i;
+  
+  if (!mappingp(ol=scan_objects(from)))
+    return ({});
+  if (!pointerp(res=ol[AT_MISC]))
+    res=({});
+  types=m_indices(ol);
+  foreach(object typ: types)
+  { 
+    if (VALID_ARMOUR_CLASS[typ]) // anderer Armour-Typ ausser AT_MISC?
+    {
+      ob=find_best_armour(from,typ);
+      if (objectp(ob))
+	res+=({ob});
+    }
+  }
+  return res;
+}
+
+varargs int wear_best_armours(mixed from) {
+  // Die besten Ruestungen werden angezogen
+  // Sollte mit command("trage_beste_ruestungen") aufgerufen werden,
+  // damit this_player() richtig gesetzt wird.
+  object *na,*oa,*diff;
+  int i,cost,cost_limit;
+  
+  if (!from)
+    from=ME;
+  if (!pointerp(na=find_best_armours(from)))
+    return 0;
+  if (!pointerp(oa=QueryProp(P_ARMOURS)))
+    oa=({});
+  diff=oa-na;
+  cost_limit = get_eval_cost()/3;
+  foreach(object di: diff)
+  {
+    di->RemoveId(INPC_BEST_SHIELD_ID);
+    di->DoUnwear();
+    if ( (cost=get_eval_cost()) < cost_limit )
+    {
+      log_file("rochus/raeuber_eval",
+        ctime()+"Break 1 in select::wear_best_armours(). Rest "+
+	to_string(cost)+", i="+to_string(i)+", Size "+
+	to_string(sizeof(diff))+".\n");
+      return 1; // zurueckgeben, was bisher drinsteht.
+      break;
+    }
+  }
+  diff=na-oa;
+  cost_limit = get_eval_cost()/3;
+  foreach(object di: diff)
+  {
+    if ( di->QueryProp(P_ARMOUR_TYPE)==AT_SHIELD
+         && !di->id(INPC_BEST_SHIELD_ID))
+     di->AddId(INPC_BEST_SHIELD_ID);
+     di->do_wear("alles");
+     if ( (cost=get_eval_cost()) < cost_limit )
+     {
+       log_file("rochus/raeuber_eval",
+         ctime()+"Break 2 in select::wear_best_armours(). Rest "+
+	 to_string(cost)+", i="+to_string(i)+", Size "+
+	 to_string(sizeof(diff))+".\n");
+      return 1; // zurueckgeben, was bisher drinsteht.
+      break;
+    }
+  }
+  return 1;
+}
+
+int eval_combat_object(object ob, mapping vals, object enemy) {
+  return 0;
+}
+
+varargs string
+find_best_combat_command(mixed from, object enemy, mapping pref) {
+  // Sucht den guenstigsten Befehl zur Benutzung einer Sonderwaffe heraus
+  object *obs;
+  mixed *ind,y,vul;
+  mapping x;
+  string best;
+  int i,j,max,val,mhp;
+  
+  if (!from)
+    from=ME;
+  if (!enemy)
+    enemy=SelectEnemy();
+  if (!mappingp(pref)) // bevorzugte Eigenschaften
+    pref=([C_MIN:5,
+	   C_AVG:10,
+	   C_MAX:2,
+	   C_HEAL:10
+	   ]);
+  best=0;max=-1;
+  if (!pointerp(obs=find_object_by_type(from,OT_COMBAT_OBJECT)))
+    return best;
+  mhp=QueryProp(P_MAX_HP)-QueryProp(P_HP);
+  if (objectp(enemy))
+    vul=enemy->QueryProp(P_RESISTANCE_STRENGTHS);
+  if (mhp<0) mhp=0;
+  foreach(object ob: obs)
+  {
+    if (!objectp(ob)) 
+      continue;
+    if (!mappingp(x=ob->QueryProp(P_COMBATCMDS)))
+      continue;
+    ind=m_indices(x);
+    for (j=sizeof(ind)-1;j>=0;j--)
+    {
+      if (!stringp(ind[j])) continue;
+      y=x[ind[j]];
+      if (mappingp(y)) 
+      {
+	if (val=y[C_HEAL]) 
+	{
+	  if (val>mhp) val=mhp; // Nur MOEGLICHE Heilung beruecksichtigen
+	  val*=pref[C_HEAL];
+	} 
+	else 
+	{
+	  val=y[C_MIN]*pref[C_MIN]
+	    + y[C_AVG]*pref[C_AVG]
+	    + y[C_MAX]*pref[C_MAX];
+	  // auskommentiert, da eval_resistance() bisher nicht implementiert
+	  // ist. Zesstra, 06.08.2007
+	  //if (y[C_DTYPES] && vul) // Vulnerability des Gegners bedenken...
+	  //  val=(int)(((float)val)*(1.0+eval_resistance(y[C_DTYPES],vul)));
+	}
+      } 
+      else 
+      {
+	val=1;
+      }
+      val+=eval_combat_object(obs[i],y,enemy);
+      if (val<max) continue;
+      max=val;
+      if (mappingp(y) && y[C_HEAL]>0)
+        best=sprintf(ind[j],name(RAW)); // Heilung auf sich selbst
+      else if (objectp(enemy))
+        best=sprintf(ind[j],enemy->name(RAW)); // Schaden auf Gegner
+    }
+  }
+
+  return best;
+}
+
+varargs int use_best_combat_command(mixed enemy, mixed from, mapping pref) {
+  // Fuehrt moeglichst guten Kampfeinsatz mit einer Sonderwaffe aus
+  string str;
+    
+  if (stringp(enemy) && environment())
+    enemy=present(enemy,environment());
+  if (str=find_best_combat_command(from,enemy,pref))
+    return command(str);
+  return 0;
+}
+
+void add_select_commands() {
+  add_action("wield_best_weapon","zuecke_beste_waffe");
+  add_action("wear_best_armours","trage_beste_ruestungen");
+  add_action("use_best_combat_command","benutze_beste_sonderwaffe");
+}
diff --git a/std/inpc/walking.c b/std/inpc/walking.c
new file mode 100644
index 0000000..f24aae6
--- /dev/null
+++ b/std/inpc/walking.c
@@ -0,0 +1,130 @@
+// MorgenGrauen MUDlib
+//
+// inpc/walking.c -- Intelligent herumlaufen
+//
+// $Id: walking.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing.h>
+#undef NEED_PROTOTYPES
+
+#include <inpc.h>
+#include <properties.h>
+#include <moving.h>
+
+#define ME this_object()
+#define ENV environment()
+
+int _set_inpc_walk_delay(int del) {
+  if (del && del<5) del=5;
+  return Set(P_INPC_WALK_DELAYS,del);
+}
+
+mixed _query_inpc_home() {
+  mixed res;
+
+  res=Query(P_INPC_HOME);
+  if (!res) return "/room/void";
+  return res;
+}
+
+int area_check(string fn) {
+  mixed area;
+  string *words;
+  int i;
+
+  if (!(area=QueryProp(P_INPC_WALK_AREA)))
+    return 1; // Keine Beschraenkung im Gebiet?
+  if (mappingp(area)) {
+    if (area[fn])
+      return 1; // Explizit erlaubter Raum
+    words=old_explode(fn,"/");
+    for (i=sizeof(words)-2;i>=0;i--)
+      if (area[implode(words[0..i],"/")])
+	return 1; // Erlaubtes Gebiet
+    return 0; // Nicht erlaubtes Gebiet
+  }
+  if (pointerp(area)) {
+    for (i=sizeof(area)-1;i>=0;i--)
+      if (fn[0..(sizeof(area[i])-1)]==area[i])
+	return 1; // Erlaubtes Gebiet
+    return 0; // Nicht erlaubtes Gebiet
+  }
+  return 1;
+}
+
+int may_enter_room(mixed room) {
+  int flags;
+  string fn;
+  object ob;
+  
+  if (objectp(room)) {
+    fn=object_name(room);
+    ob=room;
+  } else if (stringp(room)) {
+    fn=room;
+    ob=find_object(room);
+  } else
+    return 1; // Dann sollte move schon nen Fehler machen
+  if (fn=="/room/void") // Void darf IMMER betreten werden
+    return 1;
+  flags=QueryProp(P_INPC_WALK_FLAGS);
+  if (!(flags & WF_MAY_LOAD)
+      && !objectp(ob))
+    return 0; // Darf nicht in nicht geladene Raeume folgen.
+  if (!(flags & WF_MAY_WALK_BACK)
+      && ob==QueryProp(P_INPC_LAST_ENVIRONMENT))
+    return 0; // Darf nicht in den vorherigen Raum
+  return area_check(fn);
+}
+
+int walk_random() {
+  string *ex,*ex2;
+  object env;
+  int i,r,flags;
+  
+  if (!objectp(env=ENV))
+    return 0;
+  ex=m_indices(ENV->QueryProp(P_EXITS));
+  flags=QueryProp(P_CAN_FLAGS);
+  if (flags & WF_MAY_USE_SPECIAL)
+    ex+=m_indices(ENV->QueryProp(P_SPECIAL_EXITS));
+  ex2=ex[0..];
+  while (i=sizeof(ex)) {
+    r=random(i);
+    command(ex[r]);
+    if (ENV!=env)
+      return 1;
+    ex-=({ex[r]});
+  }
+  if (!(flags & WF_MAY_WALK_BACK)) {
+    SetProp(P_INPC_LAST_ENVIRONMENT,0);//Dirty Hack, um Sackgassen zu verlassen
+    ex=ex2;
+    while (i=sizeof(ex)) {
+      r=random(i);
+      command(ex[r]);
+      if (ENV!=env)
+	return 1;
+      ex-=({ex[r]});
+    }
+  }
+  return move(QueryProp(P_INPC_HOME),M_TPORT);
+}
+
+int walk_route() {
+}
+
+int walk_to() {
+}
+
+int walk_follow() {
+}
+
+int walk_flee() {
+}
diff --git a/std/items/fishing/angel.c b/std/items/fishing/angel.c
new file mode 100644
index 0000000..1b5709f
--- /dev/null
+++ b/std/items/fishing/angel.c
@@ -0,0 +1,510 @@
+/*
+  Zusammenfassung Bedingungen:
+  - Angel auswerfen
+      - Haken muss dran sein
+      - Haken muss Koeder haben
+      - darf nur ein Haken drin sein, sonst nix, und genau ein Haken
+      - Angel im Inventar
+      - Angel darf nicht schon ausgeworfen sein
+      - passendes Gewaesser im Raum
+      - Move-Hook muss korrekt registrierbar sein
+  - Angel einholen
+      - Angel-Callout muss ueberhaupt erstmal laufen
+  - Waehrend des Angelns
+      - bewegen beendet das Angeln
+      - Ausloggen beendet das Angeln
+      - Ende beendet das Angeln
+      - nichts rausnehmen
+      - nichts reintun, ausser Fisch am Ende der Wartezeit
+      - Langbeschreibung zeigt "ausgeworfen", Koeder und gefangenen Fisch an
+  - Fisch fangen (call_out zuende)
+      - Gewaesser passt (immer noch)
+      - Fisch passt noch in die Angel rein
+  - Haken dranhaengen
+      - Haken im Inv
+      - Haken muss "echter" Angelhaken sein
+      - Angel im Inv
+      - Koeder am Haken
+      - noch kein anderer Koeder dran
+      - Angel ist nicht ausgeworfen
+  - generell
+      - nur Haken dranstecken, nix anderes reintun
+      - reintun nur mit Syntax "haenge" etc. (damit sind Fische aussen vor,
+        weil die Syntax auf Haken prueft)
+        (ggf. ist dann das PreventInsert() zu vereinfachen)
+ */
+
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow
+
+inherit "/std/container";
+inherit "/std/hook_consumer";
+
+#include <new_skills.h>
+#include <language.h>
+#include <properties.h>
+#include <moving.h>
+#include <hook.h>
+#include <wizlevels.h>
+#include <items/fishing/fishing.h>
+#include <items/fishing/aquarium.h>
+
+#define TP this_player()
+#define ME this_object()
+#define BS(x) break_string(x, 78)
+
+#define MIN_DELAY       80   // Mindestdelay bis Fisch beisst!
+#define STD_DELAY_PLUS  60   //  +/- 60 sekunden Tolerant
+#define MAX_FISH_BONUS  60   // max. Wartezeitverkuerzung durch P_FISH
+#define WRONGWATER      60   // 1-2 min, falls Angel nicht geeignet
+#define WRONGWORM       60   // 1-2 min, falls Koeder nicht geeignet
+
+#define KOEDER_LOST     10
+// Verlustwahrsch. fuer Koeder falls Fisch zu schwer
+
+private object room, current_user;
+private int active, actime;
+private mapping aquarium = STDFISHLIST;
+
+nomask varargs void StopFishing(string msg_me, string msg_room);
+int IsFishing();
+
+protected void create() {
+  ::create();
+  
+  SetProp(P_SHORT, "Eine Angel");
+  SetProp(P_LONG, "Eine nagelneue Angel.\n");
+  SetProp(P_NAME, "Angel");
+  AddId(({"angel", "rute", ANGEL_ID}));
+
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_MATERIAL,([MAT_MISC_WOOD:90,MAT_WOOL:10]));
+  SetProp(P_TRANSPARENT,1);
+  SetProp(P_WEIGHT,200);
+  SetProp(P_MAX_WEIGHT, 10000);
+  SetProp(P_WATER, W_USER);     //muss (mehrere) Flags gesetzt haben!
+  SetProp(P_SOURCE_PREPOSITION, "von");
+  SetProp(P_NR_HANDS, 2); 
+
+  Set(P_NODROP, function string () {
+    return (IsFishing()?"Du solltest die Angel nicht fallenlassen, solange "
+      "Du angelst.\n":0);
+  }, F_QUERY_METHOD);
+
+  AddCmd(({"angel","angle"}),"angel");
+  AddCmd("hole|hol&@ID&ein", "stopit", "Was willst Du (ein)holen?|"
+    "Willst Du @WEN2 einholen?");
+  AddCmd("haenge|haeng|befestige&@PRESENT&@ID", "move_in",
+    "Was willst Du woran @verben?|"
+    "Woran willst Du @WEN2 denn @verben?");
+
+  AddCmd("atell","tell_stat");
+  AddCmd("angeltest","qangel");
+}
+ 
+protected void create_super() {
+  set_next_reset(-1);
+}
+     
+static int stopit(string str) {
+  if( find_call_out("do_angel")==-1 )
+    tell_object(TP, "Du angelst nicht!\nTP, ");
+  else 
+    StopFishing();
+  return 1;
+}
+
+varargs string long(int mode) {
+  string descr = QueryProp(P_LONG);
+  if(!QueryProp(P_TRANSPARENT)) 
+    return descr;
+
+  if ( find_call_out("do_angel")!=-1 )
+    descr += capitalize(QueryPronoun(WER))  + " ist ausgeworfen.\n";
+
+  object *inv = all_inventory(ME);
+  string inv_descr = make_invlist(TP, inv);
+  if ( inv_descr != "" )
+    descr += "An "+QueryPronoun(WEM) + " haeng"+(sizeof(inv)>1?"en":"t")+
+    ":\n" + inv_descr;
+  
+  return descr;
+}
+
+/*int tell_stat()
+{
+  int n;
+  
+  if(!IS_WIZARD(TP))
+    return 0;
+  if(!active){ 
+    write("Du angelst nicht\n"); return 1; 
+  }
+  n=find_call_out("do_angel");
+  if(active)
+    write(
+     "----------------------------------------------------------------------\n"
+     +"Der Fisch beisst in "+n+" sec!\n"
+     +"Bestandswert des Raumes: "+room->QueryProp(P_FISH)
+     +" = "+(room->QueryProp(P_FISH)+100)+"%.\n"
+     +"Bestandswert des Koeders: "+koeder->QueryProp(P_FISH)
+     +" = "+(100+koeder->QueryProp(P_FISH))+"%.\n"
+     +"Gesamt:"+bestand+" = "+(bestand+100)+"%.\n");
+  if(!(QueryProp(P_WATER)&room->QueryProp(P_WATER)))
+    write("Die Angel passt nicht zum Gewaessertypen.\n");
+  else
+    write("Die Angel passt zum Gewaessertypen.\n");
+  if(!(room->QueryProp(P_WATER)&koeder->QueryProp(P_WATER)))
+    write("Der Koeder passt nicht zum Gewaessertypen.\n");
+  else
+    write("Der Koeder passt zum Gewaessertypen.\n");
+  write(" => Delay="+delay+".\n"
+    +"----------------------------------------------------------------------\n"
+);
+  return 1;
+}*/
+
+static int move_in(string str, mixed *param) {
+  object haken = param[0];
+  // param[1] sind wir selbst, daher ab hier ME verwendet.
+
+  if ( find_call_out("do_angel")!=-1 ) { // angelt noch nicht?
+    tell_object(TP, BS(Name(WER,1)+" ist bereits ausgeworfen, da haengst "
+      "Du jetzt besser nichts mehr dran, das vertreibt nur die Fische."));
+  }
+  else if ( !haken->id(HAKEN_ID) ) { // echter Angelhaken?
+    tell_object(TP, BS(haken->Name(WER,1)+" ist kein Angelhaken, "+
+      haken->QueryArticle(WEN, 1, 1)+"kannst Du nicht an "+name(WEN,1)+
+      " haengen."));
+  }
+  else if ( environment(haken) != TP ) { // Haken im Inv?
+    tell_object(TP, BS("Wie er da so auf dem Boden liegt, schaffst Du es "
+      "einfach nicht, "+haken->name(WEN,1)+" vernuenftig an "+name(WEM,1)+
+      " zu befestigen."));
+  }
+  else if ( environment(ME) != TP ) { // Angel im Inv?
+    tell_object(TP, BS("Es stellt sich viel fummeliger heraus als gedacht, "+
+      haken->name(WEN,1)+" an "+name(WEM,1)+" zu befestigen, solange "+
+      QueryPronoun(WER)+" auf dem Boden herumliegt."));
+  }
+  else if ( present(HAKEN_ID, ME) ) { // anderer Haken schon dran?
+    tell_object(TP, BS("An "+name(WEM,1)+" haengt bereits ein Angelhaken."));
+  }
+  else if ( !haken->QueryKoeder() ) { // Koeder am Haken?
+    tell_object(TP, BS("Du willst "+haken->name(WEN,1)+" gerade an "+
+      name(WEN,1)+" haengen, als Dir auffaellt, dass Du beinahe den Koeder "
+      "vergessen haettest."));
+  }
+  else { // dann darf der Haken rangehaengt werden.
+    if ( haken->move(ME, M_PUT) == MOVE_OK ) {
+      tell_object(TP, BS("Du haengst "+haken->name(WEN,1)+" an "+
+        name(WEN,1)+"."));
+      tell_room(environment(TP), BS(TP->Name(WER)+" haengt etwas metallisch "
+        "Glaenzendes an "+name(WEN)+", vermutlich einen Angelhaken."), 
+        ({TP}));
+    }
+    else {
+      tell_object(TP, BS(haken->Name(WER,1)+" laesst sich nicht an "+
+        name(WEM,1)+" befestigen."));
+    }
+  }
+  return 1;
+}
+
+static int qangel() {
+  if(IS_WIZARD(TP)) {
+    call_out("do_angel",1);
+    return 1;
+  }
+  return 0;
+}
+
+static int angel(string str) {
+  object haken = present(HAKEN_ID,ME);
+  if ( environment(ME) != TP ) {
+    tell_object(TP, BS("Dafuer musst Du die Angel in die Hand nehmen."));
+  }
+  else if ( find_call_out("do_angel")!=-1 ) {
+    tell_object(TP, "Das tust Du doch schon.\n");
+  }
+  else if(!objectp(haken)) {
+    tell_object(TP, "Wie soll das gehen? Du hast ja nichtmal einen Haken "
+      "an der Angel!\n");
+  }
+  else if(!haken->QueryKoeder()) {
+    tell_object(TP, break_string("Ohne Koeder am Haken wird sich kein "
+      "Fisch der Welt fuer Deine Angel interessieren.",78));
+  }
+  else if(present(FISCH_ID,ME)) {
+    tell_object(TP, "Nimm erst mal die Beute von der Angel, die noch daran "
+      "zappelt!\n");
+  }
+  else if ( !TP->UseHands(ME, QueryProp(P_NR_HANDS)) ) { // freie Haende?
+    tell_object(TP, BS("Du musst schon beide Haende frei haben, um die "
+      "Angel sicher festhalten zu koennen."));
+  }
+  else {
+    // Raum festhalten, in dem geangelt wird.
+    room = environment(TP);
+    // Gewaessertyp abfragen
+    active = room->QueryProp(P_WATER);
+    // Kein Wasser vorhanden, oder nicht-befischbarer Sondertyp?
+    if( (!active) || (active & W_OTHER) ) {
+      tell_object(TP, "Du kannst hier nicht angeln.\n");
+      TP->FreeHands(ME);
+    }
+    // totes Gewaesser erwischt
+    else if ( active & W_DEAD ) {
+      write("In dem Gewaesser hier gibt es keine Fische.\n");
+      TP->FreeHands(ME);
+    }
+    // Jetzt aber: es gibt Fisch, Baby. ;-)
+    else {
+      int delay = MIN_DELAY; // 80 Sekunden.
+      // Fischbestand ermitteln aus Raum- und Koederparametern
+      int bonus = room->QueryProp(P_FISH) + haken->QueryProp(P_FISH);
+      // Ist kein Bonus, sondern ein Malus rausgekommen?
+      if ( bonus < 0 ) {
+        // Dann begrenzen auf 5 Min.
+        if ( bonus < -300 )
+          bonus = -300;
+        // Wartezeit erhoehen.
+        delay += random(-bonus);
+      }
+      else {
+        // Bonus deckeln auf Max-Wert (60 Sekunden)
+        if ( bonus > MAX_FISH_BONUS ) {
+          bonus = MAX_FISH_BONUS;
+        }
+        // Delay reduzieren (minimal 20 Sekunden)
+        delay -= random(bonus);
+      }
+  
+      // passt das Gewaesser zur Angel/zum Koeder ?
+      if( !(QueryProp(P_WATER) & room->QueryProp(P_WATER)) )
+        delay += WRONGWATER + random(WRONGWATER);
+      if( !(room->QueryProp(P_WATER) & haken->QueryProp(P_WATER)) &&
+          room->QueryProp(P_WATER) != W_USER )
+        delay += WRONGWORM + random(WRONGWORM);
+
+      int hook = TP->HRegisterToHook(H_HOOK_MOVE, ME, H_HOOK_OTHERPRIO(1),
+                                     H_LISTENER, 0);
+      if ( hook != 1 ) {
+        active = 0;
+        room = 0;
+        TP->FreeHands(ME);
+        tell_object(TP, BS(
+          "Du schleuderst die Angel mit Schwung in Richtung Wasser, aber der "
+          "Haken verfaengt sich in Deinen Sachen und piekst Dir in den "
+          "Allerwertesten. Das versuchst Du besser noch einmal anders."));
+        tell_room(environment(TP), BS(
+          TP->Name()+" schleudert eine Angel in Richtung Wasser, ist dabei "
+          "aber so ungeschickt, dass sich der Haken verfaengt."), ({TP}));
+      }
+      else {
+        // Alle Bedingungen erfuellt: 
+        // - Angel in der Hand.
+        // - Gewaesser stimmt.
+        // - Haken mit Koeder ist dran.
+        // - alte Beute wurde abgenommen.
+        // - Move-Hook gesetzt, um Abbruch ausloesen zu koennen.
+        actime = time();
+        current_user = TP;
+        tell_object(TP, "Du wirfst die Angel aus.\n");
+        tell_room(environment(TP), TP->Name()+" wirft eine Angel aus.\n", 
+          ({TP}));
+        call_out("do_angel", delay);
+      }
+    }
+  }
+  return 1;
+}
+
+private object GetFish(object room) {
+  int wtype = room->QueryProp(P_WATER);
+  string *fische;
+  
+  // Wenn GetAquarium() bei W_USER nichts zurueckliefert, geht der Angler
+  // leer aus. Es kann kein Fisch aus der Std-Liste genommen werden, weil
+  // der Gewaessertyp ja per Definition benutzerdefiniert ist.
+  if ( wtype == W_USER )
+    fische = room->GetAquarium(ME)||({});
+  else 
+    fische = aquarium[wtype];
+
+  string beute = fische[random(sizeof(fische))];
+
+  if ( !beute )
+    return 0;
+  // GetAquarium() liefert volle Pfade, STDFISHLIST nur Dateinamen relativ
+  // zu FISH()
+  else if ( wtype == W_USER )
+    return clone_object(beute);
+  else
+    return clone_object(FISH(beute));
+}
+
+static void do_angel() {
+  object haken = present(HAKEN_ID, ME);
+  object room  = environment(TP);
+  object fisch;
+
+  string msg_self, msg_other;
+  if ( member(TP->QueryProp(P_HANDS_USED_BY), ME)==-1 ) {
+    tell_object(TP, BS("Waehrend Du vertraeumt vor Dich hingeangelt hast, "
+      "hat sich Dein Griff an der Angel wohl gelockert, so dass der "
+      "Fisch, der gerade anbeissen wollte, sie Dir beinahe entrissen "
+      "haette."));
+    tell_room(environment(TP), BS(TP->Name(WER)+" hat sich gerade "
+      "ziemlich erschreckt, als "+TP->QueryPronoun(WEM)+" beinahe "+
+      TP->QPP(ME, WEN, SINGULAR)+" "+Name(RAW)+" entrissen worden waere."),
+      ({TP}));
+    if ( !random(3) && haken->KoederGefressen() )
+      tell_object(TP, BS("Der Koeder ist jedenfalls futsch."));
+    return;
+  }
+
+  if ( !objectp(haken) ) {
+    msg_self = "Huch, anscheinend hat sich in einem unbeobachteten "
+      "Moment der Angelhaken von der Angelschnur geloest. War der Knoten "
+      "doch nicht gut genug gesichert?";
+    msg_other = TP->Name()+" zieht unglaeubig eine leere Angelschnur aus "
+      "dem Wasser.";
+  }
+  else if ( !objectp(fisch=GetFish(room)) || 
+            active != room->QueryProp(P_WATER) )
+  {
+    msg_self = "Anscheinend gibt es hier doch keine Fische. Du gibst "
+      "auf und holst "+name(WEN,1)+" wieder ein.";
+    // Leaken von Fischobjekten verhindern.
+    if (objectp(fisch))
+      fisch->remove();
+  }
+  else if ( fisch->move(ME) != MOVE_OK ) {
+    msg_self = fisch->Name(WER)+" biss an, war aber zu schwer fuer "
+      "Dich (oder Deine Angel). "+capitalize(fisch->QueryPronoun(WER))+
+      " entgleitet Dir und plumpst zurueck ins Wasser!";
+    
+    if( !(fisch->QueryProp(P_FISH) & F_NOTHUNGRY) ) {
+      haken->KoederGefressen();
+      if(!random(KOEDER_LOST)) {
+        msg_self += " Dein Haken ist dabei leider abgerissen!";
+        tell_room(environment(TP), BS(Name(WER,1)+" von "+TP->Name(WEM)+
+          " wackelte sehr stark."), ({TP}));
+        haken->remove();
+      }
+    }
+    fisch->remove(1);
+  }
+  else {
+    haken->KoederGefressen();
+  }
+  StopFishing(msg_self, msg_other);
+}
+
+varargs int remove(int silent) {
+  if ( find_call_out("do_angel")!=-1 )
+    StopFishing(Name(WER,1)+" loest sich ploetzlich im Nichts auf.");
+  return ::remove(silent);
+}
+
+// Jemand macht "ende" waehrend er angelt?
+protected void NotifyMove(object dest, object oldenv, int method) {
+  if ( find_call_out("do_angel")!=-1 && oldenv == current_user && 
+       dest->IsRoom() ) {
+    StopFishing("Du holst die Angel ein und legst sie beiseite.");
+  }
+  return ::NotifyMove(dest, oldenv, method);
+}
+
+// Spieler loggt waehrend des Angelns aus? Dann angeln einstellen.
+void BecomesNetDead(object pl) {
+  if ( find_call_out("do_angel")!=-1 && pl == current_user ) {
+    StopFishing("Angeln ist so eine beruhigende Freizeitbeschaeftigung! "
+      "Bevor Du mit der Angel in der Hand einschlaeft, holst Du sie "
+      "lieber schnell ein.", pl->Name()+" holt schnell noch die Angel ein, "
+      "bevor "+pl->QueryPronoun(WER)+" einschlaeft.");
+  }
+}
+
+int PreventInsert(object ob) {
+  // Funktion wird aus einer Spielereingabe heraus gerufen
+  // Ich nehme hier nicht TP, weil der call_out("do_angel") die
+  // gefangenen Fische per move() in die Angel bewegt: Bei call_out()s wird
+  // TP durch die Kette weitergereicht, so dass dann hier keine 
+  // Unterscheidung zwischen Spielereingabe und call_out() moeglich waere.
+  SetProp(P_NOINSERT_MSG, 0);
+  if ( stringp(query_verb()) &&
+       member(({"haenge","haeng","befestige"}), query_verb()) == -1 ) {
+    SetProp(P_NOINSERT_MSG, BS(Name(WER,1)+" ist nur dafuer geeignet, "
+      "Angelhaken daranzuhaengen."));
+    return 1;
+  }
+  if( ob->id(FISCH_ID) ) {
+    write("Etwas zappelt an Deiner Angel.\n");
+  }
+  return ::PreventInsert(ob);
+}
+
+// Wenn geangelt wird, nimmt hier niemand was raus.
+public int PreventLeave(object ob, mixed dest) {
+  SetProp(P_NOLEAVE_MSG, 0);
+  if ( find_call_out("do_angel")!=-1 ) {
+    if ( objectp(TP) && ob->id(HAKEN_ID) )
+      SetProp(P_NOLEAVE_MSG, BS("Der Haken ist gerade unter Wasser. Es "
+        "waere kontraproduktiv, ihn ausgerechnet jetzt abzunehmen."));
+    return 1;
+  }
+  return ::PreventLeave(ob, dest);
+}
+
+// Beendet das Angeln, indem Aktiv-Flag geloescht, Hook deregistriert
+// und call_out() geloescht wird. Zusaetzlich werden die als Argument ueber-
+// gebenen Meldungen ausgegeben, oder passende generische verwendet.
+nomask varargs void StopFishing(string msg_me, string msg_room) {
+  active = 0;
+  while(remove_call_out("do_angel")!=-1);
+  object env = environment(ME);
+  if ( objectp(env) && env == current_user ) {
+    env->FreeHands(ME);
+    current_user = 0;
+    env->HUnregisterFromHook(H_HOOK_MOVE, ME);
+    tell_object(env, BS(msg_me||"Du holst Deine Angel wieder ein."));
+    tell_room(environment(env), BS(msg_room||
+      env->Name(WER)+" holt "+env->QueryPossPronoun(ME,WEN,SINGULAR)+" "+
+      name(RAW)+" ein."), ({env}));
+  }
+}
+
+// Diese Methode wird in jedem Hook-Konsumenten eines Hook-Providers
+// aufgerufen, solange die Verarbeitung nicht vorher abgebrochen wurde.
+// Dann jedoch wurde auch die Aktion (Bewegung) nicht ausgefuehrt, d.h.
+// solange hier kein Event ankommt, wurde keine Bewegung ausgefuehrt und 
+// das Angeln kann weiterlaufen.
+mixed HookCallback(object hookSource, int hookid, mixed hookData) {
+  if ( hookid == H_HOOK_MOVE && hookSource == current_user ) {
+    StopFishing("Deine Angelschnur ist zu kurz, um damit rumlaufen zu "
+      "koennen. Du holst die Angel wieder ein.");
+  }
+  return ({H_NO_MOD, hookData});
+}
+
+// Wird gerufen, wenn der Konsument von einem anderen mit hoeherer Prioritaet
+// verdraengt wurde.
+void superseededHook(int hookid, object hookSource) {
+  if ( hookid == H_HOOK_MOVE && hookSource == current_user ) {
+    StopFishing("Irgendetwas ist gerade passiert, das alle Fische "
+      "vertrieben hat. Du siehst sie unter der Wasseroberflaeche davon"
+      "flitzen. Ernuechtert holst Du Deine Angel wieder ein.");
+  }
+}
+
+// Angelzustand abfragen.
+int IsFishing() {
+  return (find_call_out("do_angel")!=-1);
+}
+
+// Gewaessertyp abfragen, in dem gerade geangelt wird.
+int query_active() {
+  return active;
+}
diff --git a/std/items/fishing/fish.c b/std/items/fishing/fish.c
new file mode 100644
index 0000000..4209b35
--- /dev/null
+++ b/std/items/fishing/fish.c
@@ -0,0 +1,222 @@
+//Revision 1.1: QueryQuality() added, sollte aber noch verbessert werden!
+/*
+Letzte Aenderung: Vanion, 26.05.02
+                  - Die Fische haben jetzt wieder einen Wert. Der Preis wird 
+                    von einem Master kontrolliert. Je mehr Fisch ein Spieler
+                    angelt, desto billiger wird der Fisch.
+                  - Gross-Kleinschreibung beim essen korrigiert.
+*/
+
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow
+
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+#include <defines.h>
+#include <items/fishing/fishing.h>
+
+#define TP this_player()
+#define BS(x) break_string(x, 78)
+
+private string eatmessage;
+string corpseobject = "/items/fishing/graeten";
+static int price_modifier;
+
+#define DECAYSTART   600  // beginnt nach 10 min zu faulen
+#define DECAYSTEP    120  // alle 2 Minuten wird er schlechter
+#define MAXQUALITY     4  // nach 4 Decays ist er giftig
+#define KILLFACTOR     5  // reduce_hit_points(STEPS*KILLFACTOR)
+#define HEALFACTOR    32  // 800 g = 25 LP
+#define MAXHEAL       40  // Kein Fisch bringt mehr als 40 LP
+#define MINHEAL        0  // oder weniger als 0
+#define FOODHEALFACTOR 3  // LP=LP + 1/3 des Gewichts
+
+void SetEatMessage(string str);
+void SetCorpseObject(string str);
+int QueryQuality();
+
+protected void create() {
+  ::create();
+  // Reset kurz nach Erreichen des max. Gammelzustands, dann zerstoeren.
+  set_next_reset(DECAYSTART+(MAXQUALITY+1)*DECAYSTEP);
+  AddId(({FISCH_ID ,"fisch"}));
+  SetProp(P_NAME, "Fisch" );
+  SetProp(P_GENDER , MALE);
+  SetProp(P_SHORT, "Ein Fisch" );
+  SetProp(P_LONG, "Ein stinknormaler Fisch.\n");
+  SetProp(P_MATERIAL, MAT_MISC_FOOD);
+  SetProp(P_NOBUY,1);
+  SetProp(P_WEIGHT, 500);
+  AddCmd("iss&@ID", "eatit", "Was willst Du essen?");
+
+  // Vanion, 26.05.02: Der Preis wird erst nach dem create() geholt, 
+  // da das Gewicht dann schon gesetzt werden muss.
+  call_out("GetPriceModifier",0);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Wert des Fisches ist abhaengig vom price_modifier und von P_VALUE
+static int _query_value() {
+  int value;
+
+  // Minimalpreis ist immer 1 Muenze. Damit man ihn loswird.
+  if(!price_modifier) 
+    return 1;
+
+  value = Query(P_VALUE, F_VALUE);
+
+  if(!value)
+    return 0; 
+
+  return (value*price_modifier)/100 ;
+}
+
+/*Eingebaut von Vanion, 26.05.02
+  Die Funktion holt den price_modifier aus dem Fish-Master.
+  Hierbei wird ein Wert zwischen 0 und 100 zurueckgegeben, der 
+  den Prozentsatz des Wertes von P_VALUE angibt. 50 bedeutet, dass
+  der Fisch 50% seines Maximalwertes hat. */
+
+#define FISHMASTER "p/daemon/fishmaster"
+
+void GetPriceModifier() {
+  if (!this_player()) return;
+
+  // Jetzt wird der Preis fuer den Fisch bestimmt.
+  price_modifier = FISHMASTER->PriceOfFish(TP, ME);
+  return;
+}
+
+void SetEatMessage(string str) {
+  eatmessage=str;
+}
+
+void SetCorpseObject(string str) {
+  corpseobject=str;
+}
+
+int _set_weight(int gramm) {
+  int tmp = gramm/2 + random(gramm/2); // "nat. Schwankungen" nachbilden
+  Set(P_WEIGHT, tmp, F_VALUE);
+  SetProp(P_VALUE, tmp/4); // Im normalen Laden nur 1/4 Gewicht in Muenzen
+  return tmp;
+}
+
+void init() {
+  ::init();
+  if( QueryProp(P_FISH)&F_REPLACE ) {
+    if(query_once_interactive(environment(ME))) {
+      call_out("ReplaceFish",0);
+    }
+  }
+  return;
+}
+ 
+varargs string long(int mode) {
+  string pron = capitalize(QueryPronoun(WER));
+  string txt = QueryProp(P_LONG);
+ 
+  switch(QueryQuality()) {
+    case 4:  txt += pron+" ist fangfrisch.\n";            break;
+    case 3:  txt += pron+" ist noch recht frisch.\n";     break;
+    case 2:  txt += pron+" ist schon etwas runzlig.\n";   break;
+    case 1:  txt += pron+" sieht schon recht alt aus.\n"; break;
+    case 0:  txt += pron+" ist nicht mehr geniessbar!\n"; break;
+    case -1: txt += pron+" ist vergammelt.\n";            break;
+    default:
+      txt += (pron+" stinkt widerlich und ist bereits voller Maden!\n"+pron+
+        " zerfaellt noch waehrend Du "+QueryPronoun(WEN)+" betrachtest.\n");
+      remove();
+      break;
+  }
+  return(txt);
+}
+
+int _query_fish_age() {
+  return (time()-QueryProp(P_CLONE_TIME));
+}
+
+static int eatit(string str) {
+  string msg_other = TP->Name()+" isst "+name(WEN)+".";
+
+  if ( !eatmessage )
+    eatmessage = "Du isst "+name(WEN,1)+" auf.";
+
+  if ( stringp(corpseobject) ) {
+    object muell=clone_object(corpseobject);
+    if ( muell->move(TP, M_GET) != MOVE_OK ) {
+      muell->move(environment(TP), M_GET);
+    }
+  }
+  
+  // Heilung berechnen.
+  // Heilwert runterskalieren mit Qualitaet, wenn das Mindestalter erreicht
+  // ist, aber nur, wenn der Fisch auch vergammeln kann.
+  int healing = QueryProp(P_WEIGHT)/HEALFACTOR;
+  int age = QueryProp(P_FISH_AGE);
+  if ( age > DECAYSTART && !(QueryProp(P_FISH)&F_NOROTTEN)) {
+    healing = healing * (QueryQuality()*25)/100;
+  }
+  if ( healing > MAXHEAL )
+    healing = MAXHEAL;
+  else if ( healing < MINHEAL )
+    healing = MINHEAL;
+
+  if( age > DECAYSTART + MAXQUALITY*DECAYSTEP) {
+    tell_object(TP, BS(eatmessage + " Dir wird speiuebel, der Fisch war "
+      "zu alt!"));
+    tell_room(environment(TP), BS(msg_other + TP->QueryPronoun(WER)+
+      " verdreht angeekelt die Augen."), ({TP}));
+    TP->reduce_hit_points( ((age-DECAYSTART)/DECAYSTEP)*KILLFACTOR );
+    remove();
+  }
+  else {
+    if ( TP->eat_food(healing*FOODHEALFACTOR) ) {
+      tell_object(TP, BS(eatmessage));
+      tell_room(environment(TP), BS(msg_other), ({TP}));
+      if ( !(QueryProp(P_FISH)&F_NOHEAL) )
+        TP->buffer_hp(healing,5);
+      remove();
+    }
+  }
+  return 1;
+}
+
+// Qualitaet des Fisches ermitteln. Rueckgabewert: 4, 3, 2, 1, 0, -1 ...
+int QueryQuality() {
+  // Alter in Sekunden ueber der Grenze, ab der der Fisch anfaengt zu gammeln
+  int age = QueryProp(P_FISH_AGE)-DECAYSTART;
+  // Wenn die Grenze noch nicht erreicht ist, oder der Fisch generell nicht
+  // gammelt (F_NOROTTEN), dann ist das Alter 0 und die Qualitaet max.
+  if ( age<0 || QueryProp(P_FISH) & F_NOROTTEN ) 
+    age = 0;
+
+  // Qualitaet in DECAYSTEP-Stufen reduzieren (60 s pro Gammelstufe)
+  return MAXQUALITY-(age/DECAYSTEP);
+}
+
+void reset() {
+  if (clonep(ME)) {
+    string msg = BS(Name()+" ist vergammelt und zerfaellt.") ;
+    if ( interactive(environment()) ) {
+      tell_object(environment(), msg);
+    }
+    else if ( environment()->IsRoom() ) {
+      tell_room(environment(), msg);
+    }
+    if ( stringp(corpseobject) ) {
+      object muell=clone_object(corpseobject);
+      if ( muell->move(environment(), M_GET) != MOVE_OK ) {
+        muell->move(environment(environment()), M_GET);
+      }
+    }
+    call_out("remove",0);
+    return; 
+  }
+  return ::reset();
+}
diff --git a/std/items/fishing/haken.c b/std/items/fishing/haken.c
new file mode 100644
index 0000000..66b91f4
--- /dev/null
+++ b/std/items/fishing/haken.c
@@ -0,0 +1,157 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_shadow, no_clone
+
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+#include <items/fishing/fishing.h>
+#include <items/flasche.h>
+#include <unit.h>
+
+#define TP this_player()
+#define ME this_object()
+
+int QueryKoeder();
+
+protected void create() {
+  ::create();
+
+  AddId(({HAKEN_ID,"haken","angelhaken"}));
+  SetProp(P_NAME, "Haken");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_ARTICLE, 1);
+  SetProp(P_WEIGHT, 5);
+  SetProp(P_SHORT, "Ein Angelhaken");
+  SetProp(P_LONG, "Ein Angelhaken aus Metall.\n");
+  SetProp(P_LONG_EMPTY,"Vielleicht kannst Du etwas damit aufspiessen?\n");
+
+  // Lang- und Kurzbeschreibungen reagieren auf einen evtl. vorhandenen 
+  // Koeder, allerdings nur auf ein tatsaechlich existierendes Koeder-Objekt,
+  // nicht auf einen per ueberschriebenem QueryKoeder() vorgetaeuschten
+  // Koeder, wie es der Spezialhaken tut.
+  Set(P_SHORT, function string () {
+    object koeder = present(WURM_ID, ME);
+    return Query(P_SHORT,F_VALUE)+
+      (objectp(koeder)?" mit "+koeder->name(WEM):"");
+  }, F_QUERY_METHOD);
+  Set(P_LONG, function string () { 
+    object koeder = present(WURM_ID, ME);
+    return Query(P_LONG,F_VALUE) + (objectp(koeder)?
+      koeder->Name(WER)+" haengt daran.\n":QueryProp(P_LONG_EMPTY));
+  }, F_QUERY_METHOD);
+
+  // P_FISH und P_WATER liefern die Daten eines evtl. vorhandenen Koeders
+  // Wenn kein Koeder dranhaengt, werden P_WATER und P_FISH des Hakens
+  // zurueckgegeben, falls es sich um einen Fake-Koeder handelt wie beim
+  // Spezialhaken; in diesem Fall hat der Haken selbst die Properties 
+  // gesetzt.
+  Set(P_FISH, function int () {
+    object koeder = present(WURM_ID, ME);
+    return (objectp(koeder)?koeder->QueryProp(P_FISH):Query(P_FISH));
+  }, F_QUERY_METHOD);
+  Set(P_WATER, function int () {
+    object koeder = present(WURM_ID, ME);
+    return (objectp(koeder)?koeder->QueryProp(P_WATER):Query(P_WATER));
+  }, F_QUERY_METHOD);
+
+  SetProp(P_MATERIAL,([MAT_STEEL:100]));
+
+  AddCmd(({"spiess","spiesse"}),"spiessauf");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+static int spiessauf(string str) {
+  string haken,wurmname,*substr;
+  object wurm;
+  int amount;
+
+  notify_fail("Was willst Du denn aufspiessen?\n");
+  if (!stringp(str) || !sizeof(str)) 
+    return 0;
+  
+  if( sscanf(str, "%s auf%s", wurmname, haken) != 2 )
+    return 0;
+
+  haken = trim(haken);
+
+  notify_fail("So etwas hast Du nicht bei Dir.\n");
+  if ( !objectp(wurm=present(wurmname,TP)) )
+    return 0;
+
+  notify_fail("Das kannst Du nicht aufspiessen.\n");
+  if( !wurm->id(WURM_ID) )
+    return 0;
+
+  notify_fail("Worauf willst Du "+wurm->name(WEN,1)+" denn spiessen?\n");
+  if ( haken!="" && !id(haken) )
+    return 0;
+
+  notify_fail(break_string("Dazu solltest Du "+name(WEN,1)+" erst einmal "
+    "bei Dir haben.",78));
+  if ( environment(ME) != TP )
+    return 0;
+
+  notify_fail("An dem Haken haengt schon ein Koeder.\n");
+  if ( QueryKoeder() )
+    return 0;
+
+  // Haken und Koeder sind im Inventar und fuehlen sich von der Syntax
+  // angesprochen.
+  if( wurm->IsUnit() ) {
+    // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
+    // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
+    // Bei der ersten Abfrage wuerde also das hier gesetzte U_REQ wieder
+    // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+    // werden...
+    wurm->QueryProp(U_REQ);
+    wurm->SetProp(U_REQ,1);
+  }
+  tell_object(TP, break_string(
+    "Du spiesst "+wurm->name(WEN,1)+" auf "+name(WEN,1)+".",78));
+  tell_room(environment(TP), break_string(
+    TP->Name(WER)+" spiesst "+wurm->name(WEN)+" auf "+name(WEN)+".",78),
+    ({TP}));
+  // M_GET, damit auch P_NODROP-Koeder aufgespiesst werden koennen.
+  if ( wurm->move(ME,M_GET) != MOVE_OK) {
+    tell_object(TP, break_string(
+      "Verdammt! Jetzt hast Du Dir fast in den Finger gestochen! "+
+      wurm->Name(WEN,1)+" hast Du dabei natuerlich verfehlt.", 78));
+  }
+  return 1;
+}
+
+int QueryKoeder(){ 
+  return objectp(present(WURM_ID, ME));
+}
+
+object QueryKoederObject() {
+  return present(WURM_ID,ME);
+}
+
+int MayAddObject(object ob) {
+  return (objectp(ob) && ob->id(WURM_ID) && sizeof(all_inventory(ME))<=1); 
+}
+
+int _query_total_weight() {
+  int tw;
+  foreach(int w: all_inventory(ME)+({ME}))
+    tw += w->QueryProp(P_WEIGHT);
+  return tw;
+}
+
+varargs int remove(int sil) {
+  all_inventory(ME)->remove(); // funktioniert auch mit leeren Arrays
+  return ::remove(sil);
+}
+
+int KoederGefressen() {
+  // Nicht QueryKoeder() pruefen, da das bei Spezialhaken immer 1 ist.
+  object koeder = present(WURM_ID, ME);
+  if ( objectp(koeder) )
+    return koeder->remove();
+  return -1;
+}
diff --git a/std/items/fishing/koeder.c b/std/items/fishing/koeder.c
new file mode 100644
index 0000000..c63317b
--- /dev/null
+++ b/std/items/fishing/koeder.c
@@ -0,0 +1,28 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow
+
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+#include <items/fishing/fishing.h>
+
+protected void create(){
+  ::create();
+  AddId(({WURM_ID,"koeder", "wurm","regenwurm"}));
+  SetProp(P_NAME, "Wurm");
+  SetProp(P_GENDER , MALE);
+  SetProp(P_ARTICLE, 1);
+  SetProp(P_FISH, 0);
+  SetProp(P_WATER, 0);
+  SetProp(P_SHORT, "Ein kleiner Wurm");
+  SetProp(P_LONG, "Ein kleiner Regenwurm.\n");
+  SetProp(P_MATERIAL, MAT_MISC_LIVING);
+  SetProp(P_VALUE, 1);
+  SetProp(P_WEIGHT, 5);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
diff --git a/std/items/flasche.c b/std/items/flasche.c
new file mode 100644
index 0000000..43735fe
--- /dev/null
+++ b/std/items/flasche.c
@@ -0,0 +1,333 @@
+/*OBJ*/
+/* 2013-Mai-23, Arathorn
+   Umfassend ueberarbeitet; setup(), init() und ReplaceWasser() entsorgt,
+   P_WEIGHT und P_LONG dynamisch gebaut, so dass das staendige Umsetzen
+   der Properties mittels setup() nach jedem Fuellen/Leeren entfaellt.
+ */
+
+/* Letzte Aenderung: 4.5.2004, Arathorn.
+   Zeile 149: cont_obj->remove() eingefuegt, damit das geclonte Objekt
+   nach dem Abfragen seines Namens wieder entfernt wird.
+   Update 6.6.2004, Arathorn. Habe um ein destruct() ergaenzt, weil das
+   trotz remove() hartnaeckig weitergebuggt hat. *fluch*
+ */
+
+/* Version 1.3 by Fraggle (17.1.1995) */
+
+ // PreventInsert: verhindert Fuellen wie std/container
+/* Version 1.2 by Fraggle (17.1.1995) */
+
+ // Flasche kann nun mittels environment(TP)->GetLiquid()
+ // etwas anderes als Wasser enthalten, sofern
+ // environment(TP)->QueryProp(P_WATER)==W_OTHER
+ // BUG: auch 1l Methan wiegt 1 Kg, aendere ich spaeter
+
+/* Version 1.1 by Fraggle (17.1.1995) */
+#pragma strong_types,rtt_checks
+#pragma no_clone
+
+inherit "/std/thing";
+#include <language.h>
+#include <properties.h>
+#include <items/flasche.h>
+#include <items/fishing/fishing.h>
+
+#define DEFAULT_LIQ "Wasser"
+#define TP this_player()
+
+private string co_filename;
+public string liquid_name=DEFAULT_LIQ;
+
+string dynamic_long();
+
+protected void create() {
+  ::create();
+
+  AddId(({"wasserbehaelter","behaelter","\nwater_source"}));
+  SetProp(P_NAME, "Wasserbehaelter");
+  SetProp(P_GENDER , MALE);
+  SetProp(P_ARTICLE, 1);
+  SetProp(P_SHORT, "Ein Standard-Wasserbehaelter");
+  SetProp(P_LONG,"none");
+  Set(P_LONG, #'dynamic_long, F_QUERY_METHOD);
+  SetProp(P_LONG_EMPTY,""); // Beschreibung fuer leeren Zustand
+  SetProp(P_LONG_FULL,"");  // Beschreibung fuer gefuellten Zusand
+  SetProp(P_LIQUID,1000);   // Fuellmenge = 1 Liter
+  SetProp(P_WATER,0);       // Flasche ist defaultmaessig leer!
+  SetProp(P_VALUE,10);
+
+  // P_WEIGHT auf Leergewicht der Flasche setzen, QueryMethode liefert
+  // die Summe aus Leergewicht+Fuellung zurueck (Dichte = 1).
+  SetProp(P_WEIGHT,20);
+  Set(P_WEIGHT, function int () {
+     return ( Query(P_WEIGHT,F_VALUE)+
+              (QueryProp(P_WATER)?QueryProp(P_LIQUID):0) );
+     }, F_QUERY_METHOD);
+
+  AddCmd(({"fuell","fuelle"}),"cmd_fuelle");
+  AddCmd(({"leere", "entleere"}),"cmd_leere");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Behaelter ist gefuellt, dann ist die Langbeschreibung entweder die 
+// P_LONG_FULL, falls angegben, oder die P_LONG + Beschreibung der Fuellung.
+// Genauso, wenn sie leer ist.
+string dynamic_long() {
+  string l=Query(P_LONG,F_VALUE);
+  if ( QueryProp(P_WATER) ) {
+    string lf = QueryProp(P_LONG_FULL);
+    if ( stringp(lf) && sizeof(lf) )
+      l=lf;
+    else
+      l+=capitalize(QueryPronoun(WER))+" enthaelt "+liquid_name+".\n";
+    // Falls die Flasche mit etwas anderem als Wasser gefuellt wird, die
+    // Langbeschreibung fuer "volle Flasche" (P_LONG_FULL) aber nur fuer
+    // Wasser ausgelegt ist, wird "Wasser" durch den Inhalt von liquid_name
+    // ersetzt.
+    if ( liquid_name != DEFAULT_LIQ )
+      l=regreplace(l, DEFAULT_LIQ, liquid_name, 1);
+  } else {
+    string le = QueryProp(P_LONG_EMPTY);
+    if ( stringp(le) && sizeof(le) )
+      l=le;
+    else
+      l+=capitalize(QueryPronoun(WER))+" ist leer.\n";
+  }
+  return l;
+}
+
+// Zum Ueberschreiben! PreventInsert(object ob){return 0;} z.B.
+// macht die Flasche gasdicht.
+// Oder man kann die Flasche verschliessbar machen.
+int PreventInsert(object obj)
+{
+  if(obj->id("gas")) { //default: NICHT Gasdicht!
+    write(obj->Name(WER,1)+" entweicht sofort wieder!\n");
+    return 1;
+  }
+  return 0;
+}
+
+// Transferiert den Inhalt der Flasche an <dest>
+protected int transfer_to(object dest)
+{
+  int water=QueryProp(P_WATER);
+  if (!water)
+  {
+    write(Name(WER,1) + " ist schon leer!\n");
+    return 0;   // War schon leer!
+  }
+  int contents=QueryProp(P_LIQUID);
+
+  if ( water&W_OTHER )
+  {
+    dest->PutLiquid(co_filename);
+  }
+  else
+  {
+    dest->AddWater(contents);
+  }
+  SetProp(P_WATER,0);
+  RemoveId(lower_case(liquid_name));
+  liquid_name=DEFAULT_LIQ;
+  return contents; //gib die ml an Umgebung ab :)
+}
+
+// Entleert die Flasche ins Environment der Flasche, allerdings nicht, wenn
+// dies ein Lebewesen ist, dann wird das Environment von dem genommen.
+// TODO: Eine Flasche in einem Paket leeren wurde dann in das paket entleeren.
+// Das waere an sich sogar richtig... Nur: gewollt? Alternative koennen wir
+// auch das aeusserste Environment nehmen, was nicht lebt.
+public int empty()
+{
+  if (environment())
+  {
+    // Environment des Benutzers finden.
+    object env = environment();
+    while (living(env))
+      env=environment(env);
+    return transfer_to(env);
+  }
+  return 0;
+}
+
+// Fuellt die Flasche aus <src>
+protected int fill_bottle(object src)
+{
+  int liquidtype = src->QueryProp(P_WATER);
+  if(liquidtype)
+  {
+    if(QueryProp(P_WATER)) {
+      write(Name(WER,1)+" ist bereits voll!\n");
+      return 1;
+    }
+    // Wasser von Umgebung abziehen!
+    // Man kann als Magier die Funktion AddWater(int n) dazu benuetzten,
+    // beispielsweise eine Pfuetze zu leeren, ...
+    src->AddWater(-QueryProp(P_LIQUID));
+    object cont_obj;
+    if(liquidtype&W_OTHER)
+    {
+      // Mittels GetLiquid() kann die Flasche mit was anderem als Wasser
+      // gefuellt werden.
+      co_filename=src->GetLiquid();
+      if (co_filename)
+      {
+        cont_obj=clone_object(co_filename);
+        if(PreventInsert(cont_obj))
+        {
+          // Hier passiert eigentlich das gleiche wie nach dem ifblock, aber
+          // auch noch Funktion beenden.
+          // TODO: Rueckgaenig machen von AddWater()?
+          // TODO: Die Meldung aus dem PreventInsert() muesste eigentlich
+          // _vorher_ noch mit einer Befuellmeldung begleitet werden.
+          cont_obj->remove(1);
+          if ( objectp(cont_obj) )
+            cont_obj->move("/room/muellraum",M_PUT);
+          cont_obj=0;
+          return 0;
+        }
+        else
+          liquid_name=cont_obj->name(WEN);
+        // In jedem Fall wird das Objekt wieder zerstoert - es wurde nur fuer
+        // das Ermitteln des liquid_name benutzt... Weia.
+        if ( cont_obj ) cont_obj->remove();
+        if ( cont_obj ) cont_obj->move("/room/muellraum",M_PUT);
+      }
+    }
+    SetProp(P_WATER,liquidtype);
+    AddId(lower_case(liquid_name));
+    //wie praktisch, 1 ml == 1 g :) // Aber nur fuer Wasser, du VOGEL! :-|
+    return 1;
+  }
+  else {
+    write("Du findest hier nichts, was Du in "+name(WEN,1)+
+      " fuellen koenntest!\n");
+    return 0;
+  }
+  return 0;
+}
+
+static int cmd_leere(string str)
+{
+  object dest;
+  notify_fail("Was willst Du denn (wo hinein) leeren?\n");
+  if (!str)
+    return 0;
+
+  string strbottle,strobj;
+  // leere flasche
+  if (id(str))
+  {
+    //NOOP
+  }
+  // leere flasche in xxx
+  else if (sscanf(str,"%s in %s",strbottle,strobj)==2)
+  {
+    if (!id(strbottle))
+      return 0;
+    dest = present(strobj, environment(this_player()))
+           || present(strobj, this_player());
+    if (!dest)
+      return 0;
+  }
+  else
+    return 0;
+  // Syntaxpruefung fertig.
+
+  if(!QueryProp(P_WATER))
+  {
+    write("Da ist kein "+liquid_name+" drin!\n");
+    return 1;
+  }
+
+  if (dest)
+  {
+    write(break_string("Du leerst "+name(WEN,1)+ " in "
+          + dest->name(WEN) + ".",78));
+    say(break_string(TP->name()+" leert "+name(WEN,0)
+          + " in " + dest->name(WEN) + ".",78),TP);
+    transfer_to(dest);
+    return 1;
+  }
+  write(break_string("Du leerst "+name(WEN,1)+".",78));
+  say(break_string(TP->name()+" leert "+name(WEN,0)+".",78),TP);
+  empty();
+  return 1;
+}
+
+public int cmd_fuelle(string str)
+{
+  string strbottle,strobj;
+
+  notify_fail("Was willst Du denn (womit) fuellen?\n");
+  if(!str)
+    return 0;
+
+  // fuelle flasche
+  if (id(str))
+  {
+    if (fill_bottle(environment(this_player())))
+    {
+      write(break_string("Du fuellst etwas "+liquid_name+" in "
+            +name(WEN,1)+".",78));
+      say(break_string(TP->Name(WER)+" fuellt etwas "
+            +liquid_name+" in "+name(WEN)+".",78), TP);
+    }
+    return 1;
+  }
+  // fuelle flasche aus xxx
+  // fuelle flasche mit xxx
+  // fuelle xxx in flasche
+  // fuelle flasche in xxx
+  // fuelle xxx aus flasche
+  // fuelle xxx mit flasche
+  else if (sscanf(str,"%s in %s",strobj,strbottle)==2
+           || sscanf(str,"%s mit %s",strbottle,strobj)==2
+           || sscanf(str,"%s aus %s",strbottle,strobj)==2)
+  {
+    object obj;
+    // Flasche befuellen?
+    if (id(strbottle)
+        && ( obj=present(strobj, environment(this_player())) 
+                 || present(strobj, this_player()) )
+       )
+    {
+      if (fill_bottle(obj))
+      {
+        write(break_string(
+              "Du fuellst etwas "+liquid_name+" aus " + obj->name(WEM,1) 
+              + " in "+name(WEN,1)+".",78));
+        say(break_string(TP->Name(WER)+" fuellt etwas "+liquid_name+ " aus " 
+              + obj->name(WEM,1) + " in "+name(WEN)+".",78), TP);
+      }
+      return 1;
+    }
+    // anderes Gefaess befuellen?
+    else if (id(strobj)
+        && ( obj=present(strbottle, environment(this_player())) 
+                 || present(strbottle, this_player()) )
+       )
+    {
+      if (transfer_to(obj))
+      {
+        write(break_string(
+              "Du fuellst etwas "+liquid_name+" aus " + name(WEM,1)
+              + " in "+obj->name(WEN,1)+".",78));
+        say(break_string(TP->Name(WER)+" fuellt etwas "+liquid_name+ " aus " 
+              + name(WEM,1) + " in "+obj->name(WEN)+".",78), TP);
+      }
+      return 1;
+    }
+  }
+  // Syntax passt nicht.
+  return 0;
+}
+
+int IsBottle() {
+   return 1;
+}
+
diff --git a/std/items/kraeuter/kraut.c b/std/items/kraeuter/kraut.c
new file mode 100644
index 0000000..2ce5045
--- /dev/null
+++ b/std/items/kraeuter/kraut.c
@@ -0,0 +1,195 @@
+// (c) September 2000 by Padreic (Padreic@mg.mud.de)
+
+#pragma strong_types, save_types, rtt_checks
+
+inherit "/std/thing";
+
+#include <properties.h>
+#include <items/kraeuter/kraeuter.h>
+
+#define PLANT_LIFETIME  (24*3600)
+#define FRESH_TIME      (6*3600)
+// Die plantID wird fuer ungueltig erschaffene Kraeuter auf -1 gesetzt.
+#define DRIED_PLANT     -1
+
+private int age=time();
+// enthaelt die Nummer des Krauts
+private int plantId;
+// enthaelt den Pfad des clonenden Objekts
+private string cloner;
+// Qualitaet des Krautes.
+private int quality=100;
+
+// File kann ein name sein, dessen Eigenschaften der VC konfigurieren sein
+// oder 0, wenn er selber ermitteln soll, was fuer ein File er gerade erzeugt
+// hat.
+// Sprich: real existierende Files auf der Platte muessen da ihren Namen
+// angeben, Standardfall ist aber, dass man 0 angibt und der VC weiss, was er
+// gerade erzeugt hat.
+void customizeMe(string file)
+{
+  if (stringp(file)) 
+    file=explode(file, "/")[<1];
+  KRAEUTERVC->CustomizeObject(file);
+}
+
+protected void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+ 
+  Set(P_QUALITY, function int () { return quality; }, F_QUERY_METHOD);
+  SetProp(P_WEIGHT, 120);
+  SetProp(P_VALUE, 70);
+  SetProp(P_MATERIAL, MAT_MISC_PLANT);
+}
+
+protected void create_super()
+{
+  set_next_reset(-1);
+}
+
+public string short()
+{
+  string str=QueryProp(P_SHORT);
+  if (!stringp(str)) 
+      return 0;
+  if (plantId==-1)
+      return str+" (unwirksam).\n";
+  else if (age==DRIED_PLANT)
+     return str+" (getrocknet).\n";
+  else if (age+FRESH_TIME+PLANT_LIFETIME<time())
+     return str+" (verfault).\n";
+  return str+".\n";
+}
+
+// Liefert einen Skalierungsfaktor zurueck, der mit dem aktuellen Wert von
+// P_QUALITY verrechnet wird. Rueckgabewerte:
+// Pflanze getrocknet oder nicht aelter als 6 h : 100
+// Pflanze aelter als 6, aber juenger als 30 h : 99...1
+// Pflanze aelter als 30 h: 0.
+// DryPlant() zerstoert das Kraut, wenn P_QUALITY unter 1 faellt.
+public int PlantQuality()
+{
+  int factor;
+  // schon getrocknet oder nicht aelter als 6 h? 
+  // Dann keine weitere Reduktion.
+  if ( age == DRIED_PLANT || age+FRESH_TIME > time()) 
+    factor = 100;
+  // >30 Stunden nach dem Pfluecken ist das Kraut verschimmelt.
+  else if ( age + FRESH_TIME + PLANT_LIFETIME < time() ) 
+    factor = 1;
+  // Zeit zwischen 6 und 30 Stunden nach dem Pfluecken in 99 gleichmaessige
+  // Abschnitte unterteilen. 24 h sind 86400 s, 86400/99 = 873.
+  else
+    factor=(time()-age-FRESH_TIME)/873;
+
+  return QueryProp(P_QUALITY)*factor/100;
+}
+
+// Wie lange (in Sekunden) ist das Kraut noch haltbar?
+public int TimeToLive()
+{
+  if ( age == DRIED_PLANT )
+    return __INT_MAX__;
+  return age-time()+PLANT_LIFETIME;
+}
+
+//TODO: vielleicht etwas zufall? Sonst Kraeuterqualitaet hieran ermittelbar.
+static int _query_value()
+{
+  int val = Query(P_VALUE,F_VALUE)*PlantQuality()/100;
+  if (plantId<=0 || val = 0)
+    return 0;
+  return val-val%10;
+}
+
+static string _query_nosell() 
+{
+  if (age != DRIED_PLANT)
+    return "Mit ungetrockneten Kraeutern handele ich nicht. Die verderben "
+      "immer so schnell im Lager, und dann werde ich sie nicht wieder los.";
+  return 0;
+}
+
+// Mit DryPlant() wird die Pflanze getrocknet. Als Argument wird der Prozent-
+// wert uebergeben, auf den die Qualitaet sinken soll. Als Ausgangswert
+// dieser Berechnung wird der Rueckgabewert von PlantQuality() verwendet.
+// Hintergrund: Die Qualitaet des Krauts sinkt im ungetrockneten Zustand
+// ueber einen Zeitraum von 24 h kontinuierlich von 100 auf 1 ab, sobald
+// es aelter als 6 Stunden ist. Danach ist es verschimmelt, was aber seiner
+// verbleibenden "Wirkung" keinen Abbruch tut.
+// Es wird die zum Zeitpunkt des Trocknungsvorganges gueltige Qualitaet
+// also sozusagen "eingefroren" und entsprechend dem Rueckgabewert von
+// PlantQuality() heruntergerechnet.
+
+// Diese Funktion kann natuerlich auch ueberschrieben werden, wenn bestimmte
+// Kraeuter erst durch trocknen in der Qualitaet steigen.
+
+// TODO: Ist das Argument "qual" dabei prozentual aufzufassen, oder 
+// soll nur ein noch zu bestimmender Festwert abgezogen werden?
+// Arathorn: Es bleibt jetzt erstmal prozentual.
+
+#define DRYING_ALLOWED ({PLANTMASTER, "/items/kraeuter/trockner"})
+
+public void DryPlant(int qual) 
+{
+  // Keine mehrfache Trocknung zulassen.
+  if ( age == DRIED_PLANT )
+    return;
+ 
+  // Nur bestimmte Objekte duerfen Trocknungen ausloesen.
+  if ( member(DRYING_ALLOWED, load_name(previous_object())) == -1 )
+    return;
+
+  // Qualitaet auf 100 deckeln.
+  if ( qual>100 )
+    qual = 100;
+  
+  // Qualitaet mittels PlantQuality() runterskalieren.
+  qual = PlantQuality()*qual/100;
+  
+  if ( qual < 1 ) {
+    if(objectp(this_player()))
+      tell_object(this_player(), 
+        Name(WER,1)+" zerfaellt in unzaehlige feine Kruemel.\n");
+    remove();
+    return;
+  }
+  // Kraut als getrocknet kennzeichnen.
+  age=DRIED_PLANT;
+  quality = qual;
+}
+
+/* Funktionen zum Initialisieren der Pflanze */
+// Der Kraeutermaster prueft den Clone auf Gueltigkeit. Eine einmal gesetzte
+// ID kann auf diesem Weg nicht mehr geaendert werden.
+nomask int SetPlantId(int new_id)
+{
+  if (plantId != 0)
+    return -1;
+  cloner=0;
+  age=time();
+  if (catch(cloner=call_other(PLANTMASTER, "CheckPlant", new_id)) || !cloner)
+    new_id = -1;
+  return (plantId=new_id);
+}
+
+nomask int QueryPlantId()
+{
+  return plantId;
+}
+
+nomask string QueryCloner()
+{
+  return cloner;
+}
+
+nomask int QueryDried() 
+{
+  return (age == DRIED_PLANT);
+}
diff --git a/std/items/kraeuter/trank.c b/std/items/kraeuter/trank.c
new file mode 100644
index 0000000..f3c0efe
--- /dev/null
+++ b/std/items/kraeuter/trank.c
@@ -0,0 +1,1147 @@
+//TODO:
+
+#pragma strong_types,rtt_checks
+
+inherit "/std/thing";
+
+#include <properties.h>
+#include <defines.h>
+#include <items/kraeuter/kraeuter.h>
+#include <items/kraeuter/trankattribute.h>
+#include <hook.h>
+#include <class.h>
+#include <new_skills.h>
+#include <wizlevels.h>
+
+#ifndef BS
+#  define BS(x)             break_string(x, 78)
+#endif
+
+#define allowed(x) (object_name(x)==PLANTMASTER)
+#define DRINK_POTION "lib_kraeutertrank_trinken"
+// for debug
+#define private public
+
+// Ablaufzeit des Tranks, ab dann keine Wirkung mehr
+private nosave int expiry;
+// Ablaufzeit der wirkungen des Trankes (0, wenn Trank nicht getrunken)
+private nosave int duration;
+// Trankattribute, vom Krautmaster schon skaliert, gekappt, beschraenkt auf
+// die jeweils max. positiven Effekte
+private nosave mapping data;
+// Klassen, die bei Schutz oder verstaerktem Schaden betroffen sind. Werte
+// ist der Schutz- oder Attackebonus/-malus.
+private nosave mapping att_classes;
+private nosave mapping prot_classes;
+// max. Summe von Giftstufen (P_POISON und P_LEVEL/10 bei CL_POISON).
+private nosave int prot_poison;
+// max. geheilte Summe von Krankheitslevel (P_LEVEL/5 in CL_DISEASE).
+private nosave int prot_disease;
+// nach Wirkung absteigend sortierte Liste der Effekte, wird beim ersten
+// Aufruf von DetermineStrongesEffect() befuellt.
+private nosave string* sorted_effects;
+
+mixed _query_data() {return data;}
+int _set_data(mixed d) {data=d; expiry = __INT_MAX__; return data!=0;}
+private string effect2colour();
+
+protected void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_NAME, "Glasflasche");
+  SetProp(P_NAME_ADJ, ({"klein"}));
+  SetProp(P_VALUE, 10);
+  SetProp(P_WEIGHT, 100);
+  SetProp(P_KILL_NAME, "Ein Kraeutertrank");
+  SetProp(P_KILL_MSG, "%s hat sich wohl beim Anruehren vertan.");
+  AddId(({"glasflasche", "flasche"}));
+  AddAdjective(({"klein", "kleine"}));
+  AddCmd("trink|trinke&@ID", "cmd_trinken",
+    "Was willst Du trinken?");
+}
+
+protected void create_super()
+{
+  set_next_reset(-1);
+}
+
+static string _query_short()
+{
+  if (!clonep(ME))
+    return "Eine kleine Glasflasche";
+  return Name(WEN, 0)+(data==0 ? "" : " (gefuellt)");
+}
+
+static string _query_long()
+{
+  if (data==0)
+    return break_string(
+      "Eine kleine leere Glasflasche, die mit einem Korken verschlossen "
+      "ist.\n"
+      "Flaschen wie diese werden ueblicherweise verwendet, um darin "
+      "magische Traenke abzufuellen.", 78, 0, BS_LEAVE_MY_LFS);
+
+  return break_string(
+    "Eine kleine Glasflasche, die mit einer "+effect2colour()+"en "
+    "Fluessigkeit gefuellt ist.\n"
+    "Sie ist mit einem Korken verschlossen, um den Inhalt der "
+    "Flasche zu schuetzen.",78, 0, BS_LEAVE_MY_LFS);
+}
+
+private string num2desc(int bumms)
+{
+  switch(abs(bumms))
+  {
+    case 0..499:
+      return "ein wenig";
+    case 500..999:
+      return "so einiges";
+    case 1000..1499:
+      return "erheblich";
+    case 1500..2000:
+      return "unglaublich";
+  }
+  return "ungenehmigt viel"; // kommt hoffentlich nicht vor.
+}
+
+varargs private string DetermineStrongestEffect(int pos)
+{
+  // globale Werteliste befuellen, wenn da noch nichts drinsteht.
+  if ( !pointerp(sorted_effects) ) {
+    sorted_effects = sort_array(m_indices(data) & T_KRAUT_EFFECTS,
+      function int (string a, string b) {
+        return (abs(data[a])<=abs(data[b]));
+      });
+  }
+
+  // Zur Indizierung des Arrays muss <pos> bei Null starten, es wird
+  // aber mit der Bedeutung einer Ordinalzahl (erste, zweite, dritte, ...)
+  // uebergeben. Daher um 1 reduzieren.
+  --pos;
+
+  string ret;
+
+  // Im Array muss mindestens ein Eintrag stehen, sonst gibt's gar keinen
+  // Effekt.
+  if ( sizeof(sorted_effects) )
+  {
+    // Wenn der angefragte Index ausserhalb der Arraygrenzen liegt, wird
+    // angenommen, dass der erste bzw. letzte Eintrag gesucht waren.
+    if ( pos < 0 )
+      ret = sorted_effects[0];
+    else if ( pos >= sizeof(sorted_effects) )
+      ret = sorted_effects[<1];
+    else
+      ret = sorted_effects[pos];
+  }
+  return ret;
+}
+
+// Liefert zu dem maximal wirksamen Effekt die Farbe des Trankes zurueck.
+private string effect2colour()
+{
+  // Ist die Haltbarkeit schon abgelaufen, wird der Trank farblos.
+  if ( time() > expiry )
+    return "farblos";
+
+  // Namen des staerksten Effekts holen.
+  string effect = DetermineStrongestEffect(1);
+  mapping colours = ([
+    T_CARRY:              "trueb braun",
+    T_DAMAGE_ANIMALS:     "blutrot",
+    T_DAMAGE_MAGIC:       "oktarinfarben",
+    T_DAMAGE_UNDEAD:      "anthrazitfarben",
+    T_FLEE_TPORT:         "schwefelgelb",
+    T_FROG:               "schlammgruen",
+    T_HEAL_DISEASE:       "perlmuttfarben",
+    T_HEAL_POISON:        "gruen",
+    T_HEAL_SP:            "blau",
+    T_HEAL_LP:            "scharlachrot",
+    T_PROTECTION_ANIMALS: "metallisch grau",
+    T_PROTECTION_MAGIC:   "violett",
+    T_PROTECTION_UNDEAD:  "strahlend weiss",
+    T_SA_SPEED:           "orangefarben",
+    T_SA_SPELL_PENETRATION: "stahlblau",
+    T_SA_DURATION:        "pinkfarben",
+  ]);
+  string ret = colours[effect];
+  return stringp(ret) ? ret : "farblos";
+}
+
+// Wird gerufen, wenn die Wirkung des Trankes ablaufen soll.
+private void terminate_effects()
+{
+  tell_object(environment(),
+      "Die letzten Wirkungen des Kraeutertrankes klingen ab.\n");
+  remove(1);
+}
+
+// Von den Hooks H_HOOK_ATTACK_MOD und H_HOOK_DEFEND gerufen, erhoeht oder
+// verringert den Schaden gegen Lebenwesen bestimmter Klassen (Keys in
+// <classes>). Der Malus/Bonus steht als Wert zum jeweiligen Key in dem
+// Mapping.
+mixed hcallback(object hookSource, int hookid, mixed hookData)
+{
+    if (hookSource != environment())
+        return ({H_NO_MOD,hookData});
+    switch(hookid)
+    {
+      case H_HOOK_ATTACK_MOD:
+        foreach(string class, int modval : att_classes)
+        {
+          if (hookData[SI_ENEMY]->is_class_member(class))
+          {
+            // Yeah. Treffer. Schaden erhoehen oder verringern... ;)
+            hookData[SI_SKILLDAMAGE] += modval;
+            // Ende. keine weiteren Klassen pruefen.
+            return ({H_ALTERED, hookData});
+          }
+        }
+        break;
+      case H_HOOK_DEFEND:
+        // hookData: ({dam,dam_type,spell,enemy})
+        foreach(string class, int modval : prot_classes)
+        {
+          if (hookData[3]->is_class_member(class))
+          {
+            // Yeah. Treffer. Schaden erhoehen oder verringern... ;)
+            hookData[0] += modval;
+            // Ende. keine weiteren Klassen pruefen.
+            return ({H_ALTERED, hookData});
+          }
+        }
+        break;
+      case H_HOOK_INSERT:
+        // Wenn die Giftschutzkapazitaet noch ausreicht, wird das reinkommende
+        // Objekt zerstoert (und die Kapazitaet reduziert).
+        // hookData: neues object
+        if (prot_poison > 0
+            && hookData->is_class_member(CL_POISON))
+        {
+          // kostet ein Zehntel des Levels, aber min. 1.
+          int p=hookData->QueryProp(P_LEVEL) / 10 + 1;
+          if (p < prot_poison)
+          {
+            hookData->remove(1);
+            prot_poison-=p;
+            return ({H_CANCELLED, hookData});
+          }
+        }
+        // Wenn die Krankheitsschutzkapazitaet noch ausreicht, wird das reinkommende
+        // Objekt zerstoert (und die Kapazitaet reduziert).
+        // hookData: neues object
+        if (prot_disease > 0
+            && hookData->is_class_member(CL_DISEASE))
+        {
+          // kostet ein Fuenftel des Levels, aber min. 1.
+          int lvl = hookData->QueryProp(P_LEVEL) / 5 + 1;
+          if (lvl < prot_disease)
+          {
+            hookData->remove(1);
+            prot_disease-=lvl;
+            return ({H_CANCELLED, hookData});
+          }
+        }
+        break;
+      case H_HOOK_POISON:
+        // hookData: poisonval
+        // Alle Giftlevel werden reduziert auf 0 und von prot_poison
+        // abgezogen. Wenn das 0 ist, endet der Giftschutz.
+        if (prot_poison>0)
+        {
+          if (hookData < prot_poison)
+          {
+            prot_poison-=hookData;
+            hookData = 0;
+          }
+          else
+          {
+            hookData -= prot_poison;
+            prot_poison=0;
+          }
+          return ({H_ALTERED, hookData});
+        }
+        break;
+    }
+    return ({H_NO_MOD, hookData});
+}
+
+private int reg_hook(int hook, int hooktype)
+{
+  // Wenn schon registriert, zaehlt das auch als Erfolg.
+  if (environment()->HIsHookConsumer(hook, #'hcallback))
+    return 1;
+  int res = environment()->HRegisterToHook(hook, #'hcallback,
+                 H_HOOK_OTHERPRIO(0), hooktype, duration);
+  if (res <= 0)
+  {
+    // wenn andere Fehler als -7 (zuviele Hooks registriert) vorkommen:
+    // Fehlermeldung ausgeben
+    if (res != -7)
+      tell_object(environment(),break_string(sprintf(
+          "Technischer Hinweis, den Du an einen Magier weitergeben "
+          "solltest: Beim Registrieren des Hooks %d gab es Fehler: %d\n",
+          hook, res),78));
+    return 0;
+  }
+  return 1;
+}
+
+// effekt: Wirkungswert des Tranks (muss negativ sein)
+// type: 1 fuer Gift, 0 fuer Krankheit
+private void ticktack(int effekt, int type)
+{
+  // Schaden tickt alle 5 Sekunden
+  int delay = 5;
+  // Der halbe Betrag des negativen Effekts wird als Schaden am
+  // Spieler verursacht.
+  // Berechnung: (Schaden pro Sekunde) * (Delay in Sekunden)
+  // in float rechnen ist hier sinnvoll, inkl. aufrunden (durch die +0.5)
+//  int dam = to_int(((-0.5*effekt)/duration)*delay + 0.5);
+  int dam = to_int(0.5*abs(effekt)/data[T_EFFECT_DURATION]*delay + 0.5);
+
+  if (type)
+    tell_object(environment(),
+      break_string("Gift pulsiert brennend durch Deine Adern.",78));
+  else
+    tell_object(environment(),
+      break_string("Du fuehlst Dich schwach und elend, eine Erkrankung "
+      "zehrt an Deinen Kraeften.",78));
+
+  environment()->do_damage(dam, this_object());
+  call_out(#'ticktack, delay, effekt, type);
+}
+
+private int act_attr_heal_poison(int effekt)
+{
+  int erfolgreich;
+  tell_object(environment(), break_string(
+     "Du fuehlst, wie der Trank wie Feuer durch Deinen Koerper schiesst "
+     "und kruemmst Dich vor Schmerzen. Doch Momente spaeter laesst die "
+     "Tortur nach.",78));
+
+  // max. 40 Giftlevel heilen...
+  prot_poison = effekt / 50;
+
+  if (prot_poison < 0)
+  {
+    tell_object(environment(), BS(
+      "Bah! Der Trank schmeckt widerlich bitter. Wenn der mal nicht "
+      "giftig war."));
+    call_out(#'ticktack, 5, effekt, 1); // 1 => Gift
+    return 1;
+  }
+  
+  // ab jetzt nur noch positive Wirkungen.
+
+  // P_POISON zuerst.
+  int poison = environment()->QueryProp(P_POISON);
+  if (poison)
+  {
+    if (poison <= prot_poison)
+    {
+      prot_poison -= poison;
+      environment()->SetProp(P_POISON,0);
+      if (!environment()->QueryProp(P_POISON))
+        ++erfolgreich;
+    }
+    else
+    {
+      poison -= prot_poison;
+      prot_poison=0;
+      environment()->SetProp(P_POISON, poison);
+      // Wenn erfolgreich, direkt Meldung und raus.
+      if (environment()->QueryProp(P_POISON) == poison)
+      {
+        tell_object(environment(), break_string(
+           "Ueberrascht stellst Du fest, dass Du Dich "
+           "besser fuehlst - der Trank hat Deine Vergiftung offenbar "
+           "gelindert.",78));
+        return 1;
+      }
+    }
+  }
+
+  // wenn Trank immer noch positiv (also noch WIrkung uebrig)
+  if (prot_poison > 0)
+  {
+    // Als naechstes Objekte suchen.
+    object *ob = filter_objects(all_inventory(environment()),
+                                "is_class_member", CL_POISON);
+    foreach(object o: ob)
+    {
+      // Entgiften kostet ein Zehntel des Levels, aber min. 1.
+      poison = o->QueryProp(P_LEVEL);
+      if (poison <= prot_poison*10)
+      {
+        prot_poison -= poison/10 + 1;
+        o->SetProp(P_LEVEL, 0);
+        if (o->remove())
+          ++erfolgreich;
+      }
+      else
+      {
+        poison -= prot_poison * 10;
+        prot_poison = 0;
+        o->SetProp(P_LEVEL, poison);
+        if (o->QueryProp(P_LEVEL) == poison)
+          ++erfolgreich;
+      }
+      if (prot_poison <= 0)
+        break;
+    }
+  }
+
+  if (erfolgreich)
+  {
+    tell_object(environment(), break_string(
+      "Ueberrascht stellst Du fest, dass Du Dich viel besser fuehlst - der "
+      "Trank wirkt offenbar gegen Vergiftungen.",78));
+  }
+  else
+  {
+    tell_object(environment(), break_string(
+      "Eine Ahnung sagt Dir, dass der Trank irgendeine positive "
+      "Wirkung hat."));
+  }
+
+  // ggf. an die Hooks registrieren, wenn noch Schutzwirkung uebrig ist.
+  if (prot_poison > 0)
+  {
+    // Rueckgabewerte von HRegisterToHook speichern...
+    int *res = ({ reg_hook(H_HOOK_POISON, H_DATA_MODIFICATOR) });
+    res += ({ reg_hook(H_HOOK_INSERT, H_HOOK_MODIFICATOR) });
+    // Wenn alle versuchten Registrierungen erfolgreich waren...? Ansonsten
+    // andere Meldung... Ich bin noch nicht gluecklich, das hier so explizit
+    // auszugeben, aber ich weiss gerade sonst nicht, wie man drauf kommen
+    // soll, dass es ein Problem gibt.
+    if (sizeof(res) == sizeof(res & ({1})))
+      tell_object(environment(),
+         "Vielleicht haelt diese Wirkung ja sogar noch etwas an?\n");
+    else
+    {
+      // zumindest ein erfolg?
+      if (member(res, 1) > -1)
+        tell_object(environment(),
+            "Vielleicht haelt ein Teil dieser Wirkung ja sogar noch etwas an?\n");
+    }
+  }
+  return 1;
+}
+
+private int act_attr_heal_disease(int effekt)
+{
+  int erfolgreich;
+
+  // max. 40 Krankheitslevel heilen...
+  prot_disease = effekt / 50;
+
+  if (prot_disease > 0)
+  {
+     tell_object(environment(), break_string(
+       "Du fuehlst, wie der Trank in Deinem Bauch eine wohlige Waerme "
+       "verbreitet und laechelst unwillkuerlich.",78));
+   
+    // Objekte suchen.
+    object *ob = filter_objects(all_inventory(environment()),
+                                "is_class_member", CL_DISEASE);
+    foreach(object o: ob)
+    {
+      // Heilen kostet ein Fuenftel des Levels, aber min. 1.
+      int disease = o->QueryProp(P_LEVEL);
+      if (disease <= prot_disease*5)
+      {
+        prot_disease -= disease/5 + 1;
+        o->SetProp(P_LEVEL, 0);
+        if (o->remove())
+          ++erfolgreich;
+      }
+      else
+      {
+        disease -= prot_disease * 5;
+        prot_disease = 0;
+        o->SetProp(P_LEVEL, disease);
+        if (o->QueryProp(P_LEVEL) == disease)
+          ++erfolgreich;
+      }
+      if (prot_disease <= 0)
+        break;
+    }
+  }
+  else
+  {
+    tell_object(environment(), BS(
+      "Der Trank schmeckt eklig faulig. Dein Magen rebelliert umgehend. "
+      "Du kannst Deinen Brechreiz gerade noch unterdruecken, fuehlst "
+      "Dich aber krank."));
+    call_out(#'ticktack, 5, effekt, 0); // 0 => Krankheit
+    return 1;
+  }
+
+  if (erfolgreich)
+  {
+    tell_object(environment(), break_string(
+      "Entspannt durchatmend stellst Du fest, dass Du Dich viel besser fuehlst - der "
+      "Trank wirkt offenbar gegen Krankheiten.",78));
+  }
+  else
+  {
+    tell_object(environment(), break_string(
+      "Eine Ahnung sagt Dir, dass der Trank irgendeine positive "
+      "Wirkung hat."));
+  }
+
+  // ggf. an die Hooks registrieren.
+  if (prot_disease > 0)
+  {
+    // Registrierung erfolgreich...? Ansonsten andere Meldung... Ich bin
+    // noch nicht gluecklich, das hier so explizit auszugeben, aber ich
+    // weiss gerade sonst nicht, wie man drauf kommen soll, dass es ein
+    // Problem gibt.
+    if (reg_hook(H_HOOK_INSERT, H_HOOK_MODIFICATOR)==1)
+      tell_object(environment(),
+         "Vielleicht haelt diese Wirkung ja sogar noch etwas an?\n");
+
+  }
+  return 1;
+}
+
+private string num2desc_fight(int bumms)
+{
+  switch(abs(bumms))
+  {
+    case 0..499:
+      return "ein wenig";
+    case 500..999:
+      return "spuerbar";
+    case 1000..1499:
+      return "deutlich";
+    case 1500..2000:
+      return "erheblich";
+  }
+  return "ungenehmigt viel"; // kommt hoffentlich nicht vor.
+}
+
+// AN: Tiere sind: CL_ANIMAL, CL_FISH, CL_FROG, CL_INSECT, CL_MAMMAL,
+// CL_MAMMAL_LAND, CL_MAMMAL_WATER, CL_REPTILE, CL_ARACHNID, CL_BIRD
+private int act_attr_dam_animals(int effekt)
+{
+  if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1)
+  {
+    if (!mappingp(att_classes)) att_classes=m_allocate(1);
+    att_classes[CL_ANIMAL] = effekt/20;
+    tell_object(environment(), break_string(
+        "Du spuerst in Dir ein seltsames Verlangen aufsteigen, auf die Jagd "
+        "zu gehen - als wuerde Artemis persoenlich Deine Angriffe "
+        + num2desc_fight(effekt)
+        + " verbessern.",78));
+    return 1;
+  }
+  return 0;
+}
+
+// AN: Magische Wesen sollen sein: CL_ELEMENTAL, CL_ILLUSION, CL_SHADOW
+// CL_DRAGON, CL_DEMON, CL_SHAPECHANGER, CL_HARPY
+private int act_attr_dam_magical(int effekt)
+{
+  if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1)
+  {
+    if (!mappingp(att_classes)) att_classes=m_allocate(4);
+    att_classes[CL_DRAGON] = att_classes[CL_ELEMENTAL]
+                            = att_classes[CL_SHADOW]
+                            = att_classes[CL_ILLUSION]
+                            = effekt/20;
+    tell_object(environment(), break_string(
+        "Merkwuerdig. Du hast gerade das Gefuehl, als fiele Dir der "
+        "Kampf gegen von Hekate beschenkte Wesen "
+        + num2desc_fight(effekt)
+        + " leichter.",78));
+    return 1;
+  }
+  return 0;
+}
+
+// AN: Untote sollen sein: CL_SKELETON, CL_GHOUL, CL_GHOST, CL_VAMPIRE
+// CL_ZOMBIE, CL_UNDEAD
+// Bloed ist nur, dass CL_UNDEAD alle anderen enthaelt bis auf CL_GHOST.
+// Also lassen wir die Unterscheidung und schrittweise Freischaltung
+// erst einmal sein.
+private int act_attr_dam_undead(int effekt)
+{
+  if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1)
+  {
+    // Zombies, Skelette, Ghule...
+    if (!mappingp(att_classes)) att_classes=m_allocate(1);
+    att_classes[CL_UNDEAD] =  effekt/20;
+    tell_object(environment(), break_string(
+        "Auf einmal hast Du den Eindruck, dass die Kreaturen des "
+        "Hades Deinen Angriffen "
+        + num2desc_fight(effekt)
+        + " weniger entgegen zu setzen haben.",78));
+    return 1;
+  }
+  return 0;
+}
+
+private int act_attr_prot_animals(int effekt)
+{
+  if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1)
+  {
+    if (!mappingp(prot_classes)) prot_classes=m_allocate(1);
+    prot_classes[CL_ANIMAL] = effekt/20;
+    tell_object(environment(), break_string(
+        "Du hast das Gefuehl, dass Artemis ihre schuetzende Hand "
+        + num2desc_fight(effekt)
+        + " ueber Dich haelt.",78));
+    return 1;
+  }
+  return 0;
+}
+
+private int act_attr_prot_magical(int effekt)
+{
+  if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1)
+  {
+    if (!mappingp(prot_classes)) prot_classes=m_allocate(4);
+    prot_classes[CL_DRAGON] = prot_classes[CL_ELEMENTAL]
+                            = prot_classes[CL_SHADOW]
+                            = prot_classes[CL_ILLUSION]
+                            = effekt/20;
+    tell_object(environment(), break_string(
+        "Du hast das Gefuehl, dass von Hekate beschenkte Wesenheiten Dir "
+        +num2desc_fight(effekt)
+        + " weniger anhaben koennen.",78));
+    return 1;
+  }
+  return 0;
+}
+
+private int act_attr_prot_undead(int effekt)
+{
+  if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1)
+  {
+    // Zombies, Skelette, Ghule...
+    if (!mappingp(prot_classes)) prot_classes=m_allocate(1);
+    prot_classes[CL_UNDEAD] =  effekt/20;
+    tell_object(environment(), break_string(
+        "Du bist ploetzlich zuversichtlich, Angriffen der Kreaturen "
+        "des Hades "
+        + num2desc_fight(effekt)
+        + " besser widerstehen zu koennen.",78));
+    return 1;
+  }
+  return 0;
+}
+
+
+private int act_attr_tragen(int effekt)
+{
+  if ( IS_LEARNER(environment()) )
+    tell_object(environment(), sprintf("effekt: %d\n",effekt));
+  SetProp(P_WEIGHT, QueryProp(P_WEIGHT) - effekt*4);
+  tell_object(environment(),
+      BS("Du fuehlst Dich ploetzlich "+num2desc(effekt)
+        + (effekt>0 ? " entlastet" : " belastet") + "."));
+  return 1;
+}
+
+// Berechnet eine max. Verzoegerung der Wirkung abhaengig von der Wirkung und
+// einer Wirkschwelle. Der rel. Abstand von der Wirkschwelle (relativ zum max.
+// moeglichen Abstand) wird hierbei genutzt. Ausserdem ist die max.
+// Verzoegerung natuerlich die Wirkungsdauer des Trankes.
+// <duration> muss im Trank schon gesetzt sein.
+private int calc_max_delay(int effekt, int wirkschwelle)
+{
+  int abstand = abs(effekt - wirkschwelle);
+  if (!duration) duration = time()+600;
+  if ( IS_LEARNER(environment()) )
+    printf("calc_max_delay: %d\n",((duration-time()) * abstand) /
+      (2000-abs(wirkschwelle)));
+  return ((duration-time()) * abstand) / (2000-abs(wirkschwelle));
+}
+
+//TODO: die Zeitverzoegerung ist nen netter Effekt, aber zeitvergzoegerte
+//Tports sind oefter keine gute Idee -> noch pruefen
+//TODO: Die Zeitverzoegerung bei NO_TPORT_OUT auch nochmal pruefen
+private int act_attr_flee_tport(int effekt)
+{
+  // effekt > 0 teleportiert sofort zu halbwegs sicheren Orten
+  // -750 <= effekt < 0 teleportiert auch sofort zu nicht so dollen Orten
+  // -2000 <= effekt < -750 teleportiert spaeter zu bloeden Orten
+  if (effekt < -750 && effekt >= -2000)
+  {
+    // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
+    // effekt rufen, der eine instantane Reaktion ausloest.
+    // effekt - 2000 ist ein Hack, damit nicht nochmal verzoegert wird.
+    call_out(#'act_attr_flee_tport,
+        random(calc_max_delay(effekt, -750))+1, effekt - 2000);
+    tell_object(environment(),
+        "Deine Umgebung fuehlt sich nach und nach unwirklicher an.\n");
+    return 1;
+  }
+
+  // Effekte instantan ausloesen.
+
+  // wenn hier kein P_NO_TPORT erlaubt ist, mal gucken, ob wir spaeter noch
+  // zeit haben.
+  if (environment(environment())->QueryProp(P_NO_TPORT)==NO_TPORT_OUT)
+  {
+    tell_object(environment(),
+        BS("Irgendetwas haelt Dich an diesem Ort fest."));
+    int delay = duration - time();
+    // wenn noch genug Restzeit, nochmal versuchen, sonst nix machen.
+    if (delay>10)
+    {
+      // AN/TODO: Kann das nicht auch ziemlich lang sein?
+      call_out(#'act_attr_flee_tport, random(delay), effekt);
+    }
+    return 0;
+  }
+
+  // den Hack von oben rueckgaengig machen, wir brauchen den originalen
+  // Effektwert zurueck.
+  if (effekt < -2000)
+    effekt += 2000;
+
+  string* dests;
+  if ( effekt > 0 )
+  {
+    switch(effekt)
+    {
+      case 0..499:
+        dests = ({"/d/inseln/zesstra/vulkanweg/room/r1",
+                  "/d/fernwest/li/xian/lab2/grab2",
+                  "/d/ebene/miril/schloss/heide11",
+                  "/d/polar/alle/room/weg4_15",
+                  "/d/dschungel/arathorn/tempel/room/t4-5",
+                  "/d/anfaenger/arathorn/minitut/room/huette_"+
+                    environment()->query_real_name(),
+                 });
+        break;
+      case 500..749:
+        dests = ({"/d/ebene/bertram/ebene/wasser8",
+                  "/d/ebene/room/gebuesch2_3",
+                  "/d/polar/alle/room/eiswueste/eiswueste[4,6]",
+                 });
+        if (environment()->QueryProp(P_REAL_RACE)!="Dunkelelf")
+          dests += ({"/d/unterwelt/padreic/kneipe/kneipe"});
+        break;
+      case 750..999:
+        dests = ({"/d/gebirge/silvana/cronoertal/room/tf4",
+                  "/d/inseln/schiffe/floss",
+                  "/d/polar/humni/hexen/room/leuchtkammer",
+                  "/d/polar/gabylon/temong/rooms/anlegestelle",
+                 });
+        break;
+      case 1000..1249:
+        dests = ({"/d/fernwest/shinobi/konfu_quest/room/insel4",
+                  "/d/fernwest/ulo/mura/tokoro/haus4",
+                  "/d/ebene/room/Halle/shalle14",
+                 });
+        break;
+      case 1250..1499:
+        dests = ({"/d/gebirge/silvana/cronoertal/room/tf4",
+                  "/gilden/zauberer",
+                  "/d/gebirge/georg/room/w11"
+                 });
+        break;
+      case 1500..1749:
+        dests = ({"/gilden/bierschuettler",
+                  "/gilden/kaempfer",
+                  "/d/wald/gundur/hobbitdorf/schrein",
+                  "/d/vland/morgoth/room/city/rathalle",
+                 });
+        break;
+      default:
+        dests = ({environment()->QueryProp(P_START_HOME)||
+                    environment()->QueryDefaultHome(),
+                  environment()->QueryDefaultHome(),
+                  "/d/ebene/room/PortVain/po_haf2",
+                  "/d/gebirge/room/he3x3",
+                  "/d/ebene/room/huette",
+                 });
+        break;
+    }
+  }
+  else if ( effekt < 0 )
+  {
+    switch(effekt)
+    {
+      case -499..0:
+        dests = ({"/d/polar/bendeigid/rooms/neskaya/neskay12",
+                  "/players/ketos/gl/room/gl1x1",
+                  "/d/inseln/zesstra/vulkanweg/room/r10",
+                  "/d/vland/morgoth/room/kata/ktanke",
+                  "/d/ebene/zardoz/burg/feuerrau",
+                 });
+        break;
+      case -999..-500:
+        dests = ({"/d/ebene/room/Hoehle/hg6",
+                  "/d/gebirge/silvana/warok/room/l3/wa_3x7",
+                  "/d/vland/morgoth/room/kata/xkat03",
+                  "/d/vland/morgoth/room/kata/kata5",
+                  "/d/wald/yoru/ort/dravi/weg5",
+                  "/d/wald/atamur/room/e83",
+                 });
+        break;
+      case -1499..-1000:
+        dests = ({"/d/polar/bendeigid/rooms/pass/pass_e1",
+                  "/d/ebene/room/gebuesch",
+                  "/d/gebirge/silvana/warok/room/l1/wa_1x6",
+                  "/d/vland/morgoth/room/kata/gkat17",
+                  "/d/wald/atamur/room/e12",
+                 });
+        if (environment()->QueryProp(P_REAL_RACE)=="Dunkelelf")
+          dests += ({"/d/unterwelt/padreic/kneipe/kneipe"});
+        break;
+      case -1749..-1500:
+        dests = ({"/d/dschungel/wurzel/t2",
+                  "/d/vland/morgoth/room/kata/ukat26",
+                  "/d/vland/morgoth/room/kata/h12",
+                  "/d/gebirge/boing/sm/l5/m5x2",
+                 });
+        if (environment()->QueryProp(P_GUILD)=="chaos")
+          dests+=({"/gilden/klerus"});
+        break;
+      default:
+        dests = ({"/d/ebene/rochus/room/sp10",
+                  "/d/ebene/rochus/quest_3player/room/schacht10",
+                 });
+        if ( IS_SEER(environment()) )
+          dests += ({"/d/wald/paracelsus/froom/sch_6e",
+                     "/d/wald/paracelsus/froom/sch_9x",
+                     "/d/wald/paracelsus/froom/sch2_6d",
+                    });
+        break;
+    }
+  }
+  tell_object(environment(),
+      BS("Eine Kraft zerrt an Dir, die Welt verschwimmt..."));
+  if (environment()->move(dests[random(sizeof(dests))],M_TPORT) == MOVE_OK)
+    tell_object(environment(),
+        "Einen Moment spaeter bist Du ganz woanders.\n");
+  else
+    tell_object(environment(),
+        "Aber sonst passiert nichts.\n");
+  return 1;
+}
+
+private int act_attr_change_dimension(int effekt)
+{
+  // nur effekt >= 1000 aendern die Dimension instantan. ;-)
+  if (effekt > 0 && effekt < 1000)
+  {
+    // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
+    // effekt rufen, der eine instantane Reaktion ausloest.
+    call_out(#'act_attr_change_dimension,
+        random(calc_max_delay(effekt,1000))+1, 1000);
+    tell_object(environment(),BS("Um Dich herum wird alles "
+        "langsam grauer.\n"));
+    return 1;
+  }
+  // nur -600 < effekt < 0 aendern die Dimension instantan ;-)
+  if (effekt < -600)
+  {
+    // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
+    // effekt rufen, der eine instantane Reaktion ausloest.
+    call_out(#'act_attr_change_dimension,
+        random(calc_max_delay(effekt, -600))+1, -601);
+    tell_object(environment(),BS("Um Dich herum wird alles "
+        "langsam grauer.\n"));
+    return 1;
+  }
+  // Effekte instantan ausloesen.
+  // wenn hier kein Para-Trans erlaubt ist, mal gucken, ob wir spaeter noch
+  // zeit haben.
+  if (environment(environment())->QueryProp(P_NO_PARA_TRANS))
+  {
+    int delay = duration - time();
+    // wenn noch genug Restzeit, nochmal versuchen, sonst nix machen.
+    if (delay>10)
+    {
+      call_out(#'act_attr_change_dimension, random(delay), effekt);
+      tell_object(environment(), BS("Die Welt um Dich wird ein "
+            "wenig grauer."));
+    }
+    return 0;
+  }
+  if ( effekt > 0 )
+  {
+    if ( environment()->QueryProp(P_PARA) > 0 )
+    {
+      environment()->SetProp(P_PARA, 0);
+      tell_object(environment(), BS("Fuer einen kurzen Moment siehst Du nur "
+          "ein graues Wabern um Dich herum, bevor die Welt wieder "
+          "normal aussieht.\n"));
+    }
+    else
+    {
+      tell_object(environment(), BS("Fuer einen kurzen Moment sieht alles um "
+            "Dich herum grau aus."));
+    }
+  }
+  else if ( effekt < 0 )
+  {
+    if ( !environment()->QueryProp(P_PARA) )
+    {
+      environment()->SetProp(P_PARA, 1);
+       tell_object(environment(), BS("Fuer einen kurzen Moment siehst Du nur "
+          "ein graues Wabern um Dich herum, bevor die Welt wieder "
+          "normal aussieht. Aber es bleibt ein ungutes Gefuehl.\n"));
+    }
+    else {
+      tell_object(environment(), BS("Fuer einen kurzen Moment sieht alles um "
+            "Dich herum grau aus."));
+    }
+  }
+  return 1;
+}
+
+private int act_attr_defrog(int effekt)
+{
+  // nur effekt > 1000 entfroscht instantan. ;-)
+  if (effekt > 0 && effekt < 1000)
+  {
+    // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
+    // effekt rufen, der eine instantane Reaktion ausloest.
+    call_out(#'act_attr_defrog, random(calc_max_delay(effekt,1000))+1, 1000);
+    tell_object(environment(),BS(
+        "Du hoerst ploetzlich lautes Gequake, was langsam immer leiser "
+        "wird.\n"));
+    return 1;
+  }
+  // nur -500 < effekt < 0 froscht instantan ;-)
+  if (effekt < -500)
+  {
+    // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
+    // effekt rufen, der eine instantane Reaktion ausloest.
+    call_out(#'act_attr_defrog, random(calc_max_delay(effekt, -500))+1, -501);
+    tell_object(environment(),BS(
+        "Du hoerst ploetzlich ganz leisess Gequake, was langsam immer lauter "
+        "wird.\n"));
+    return 1;
+  }
+
+  // Effekte instantan ausloesen.
+  if ( effekt > 0 )
+  {
+    if ( environment()->QueryProp(P_FROG) )
+    {
+      environment()->SetProp(P_FROG,0);
+      tell_object(PL, "Du fuehlst Dich deutlich weniger gruen.\n");
+    }
+    else
+    {
+      tell_object(environment(), break_string("Die Welt um Dich herum verliert "
+        "ploetzlich alle gruenen Farbtoene, die bald darauf allmaehlich "
+        "zurueckkehren.",78));
+    }
+  }
+  else if ( effekt < 0 ) {
+    if ( !environment()->QueryProp(P_FROG) ) {
+      environment()->SetProp(P_FROG, 1);
+      tell_object(environment(), "Quak!\n");
+    }
+    else {
+      tell_object(environment(), break_string("Deine Sicht verschwimmt, alles wird "
+        "intensiv gruen. Merkwuerdig. Zum Glueck ist das schnell wieder "
+        "vorbei.",78));
+    }
+  }
+  return 1;
+}
+
+private int act_attr_heal_lp(int effekt)
+{
+  if (effekt > 0)
+  {
+    tell_object(environment(),
+        BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
+          + " besser."));
+    environment()->restore_hit_points(effekt/10);
+  }
+  else
+  {
+    tell_object(environment(),
+        BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
+          + " schlechter."));
+    environment()->do_damage(-effekt/10);
+  }
+  return 1;
+}
+
+private int act_attr_heal_sp(int effekt)
+{
+  if (effekt > 0)
+  {
+    tell_object(environment(),
+        BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
+          + " konzentrierter."));
+    environment()->restore_spell_points(effekt/10);
+  }
+  else
+  {
+    tell_object(environment(),
+        BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
+          + " unkonzentrierter."));
+    environment()->reduce_spell_points(-effekt/10);
+  }
+  return 1;
+}
+
+
+private int modify_sa(string sa, int effekt)
+{
+  string msg;
+  switch (sa)
+  {
+    case SA_SPEED:
+      msg = "Du wirst gerade " + num2desc(effekt)
+            +(effekt>0 ? " schneller." : " langsamer.");
+      effekt = (effekt * 30) / 2000;
+      break;
+    case SA_DURATION:
+      msg = "Du wirst gerade " + num2desc(effekt)
+            +(effekt>0 ? " ausdauernder."
+                       : " weniger ausdauernd.");
+      effekt = (effekt * 25) / 2000;
+      break;
+    case SA_SPELL_PENETRATION:
+      msg = "Deine geistige Durchsetzungskraft hat sich gerade "
+            + num2desc(effekt)
+            +(effekt>0 ? " verbessert." : " verschlechtert.");
+      effekt = (effekt * 30) / 2000;
+      break;
+  }
+  if (environment()->ModifySkillAttribute(sa, effekt, duration-time()) ==
+        SA_MOD_OK)
+  {
+    tell_object(environment(),BS(msg));
+    return 1;
+  }
+  return 0;
+}
+
+private int act_attr_change_sa_speed(int effekt)
+{
+  return modify_sa(SA_SPEED, effekt);
+}
+private int act_attr_change_sa_duration(int effekt)
+{
+  return modify_sa(SA_DURATION, effekt);
+}
+private int act_attr_change_sa_spell_penetration(int effekt)
+{
+  return modify_sa(SA_SPELL_PENETRATION, effekt);
+}
+
+
+// Spieler MUSS das environment() sein!
+private void effekt()
+{
+  // Als erstes die Wirkungsdauer verwursten, einige Effekte brauchen
+  // <duration>.
+  // Wann laufen die Effekte denn spaetenstens ab?
+  duration = time() + data[T_EFFECT_DURATION];
+  call_out(#'terminate_effects, data[T_EFFECT_DURATION]);
+
+  // nur echte wirkungen beruecksichtigen, keine "Metadaten"
+  mapping effects = data & T_KRAUT_EFFECTS;
+  // fuer Spieler wird der Paratrans nicht stattfinden.
+  if (!IS_SEER(environment()))
+    m_delete(data, T_CHANGE_DIMENSION);
+
+  // Waehrend der Wirkung ist dieses Objekt schonmal unsichtbar.
+  SetProp(P_INVIS, 1);
+  // Gewicht nullen, bevor die Wirkungen aktiviert werden, da die
+  // Wirkung von T_CARRY ansonsten wieder deaktiviert wuerde. 
+  SetProp(P_WEIGHT, 0);
+  // neue, leere Flasche ins Inventar des Spielers bewegen.
+  clone_object(TRANKITEM)->move(environment(),M_NOCHECK|M_SILENT);
+
+  // auftrennen in positive und negative Effekte. Keys mit Wert 0 werden
+  // ignoriert.
+  mapping peff = filter(effects, function int (string k, int val)
+                        {return val > 0;});
+  mapping neff = filter(effects, function int (string k, int val)
+                        {return val < 0;});
+  // erst die positiven, dann die negativen Wirkungen aktivieren
+  // fuer jede Wirkung wird eine lfun act_<trankattribut>() gerufen, z.B.
+  // act_attr_tragen() (-> act_T_CARRY() )
+  mapping notactivated =
+          filter(peff, function int (string k, int val)
+              {return !funcall(symbol_function("act_"+k,this_object()), val);})
+         +
+          filter(neff, function int (string k, int val)
+              {return !funcall(symbol_function("act_"+k,this_object()), val);});
+  // Meldungen ausgeben ueber nicht aktivierte Wirkungen?
+  // TODO
+}
+
+static int cmd_trinken(string str, mixed *param)
+{
+  if (environment(this_object()) != PL)
+  {
+    write("Aus auf dem Boden liegende Flaschen trinkt es sich so "
+       "schlecht!\n");
+  }
+  else if (data==0)
+  {
+    write("Die Flasche ist leer, Du muesstest erst etwas hineinfuellen.\n");
+  }
+  else if (time()>expiry) 
+  {
+    write(BS("Irgendwie passiert nichts. Hattest Du vielleicht doch nur "
+      "klares Wasser abgefuellt?"));
+    data=0;
+  }
+  else {
+    // Die Sperrzeit muss hier separat berechnet werden, weil eine
+    // Anpassung der Wirkungsdauer im Krautmaster dazu fuehren wuerde,
+    // dass es keine instantan wirkenden Traenke mehr gaebe.
+    int blocktime = max(60+random(60), data[T_EFFECT_DURATION]);
+    if(environment()->check_and_update_timed_key(blocktime, DRINK_POTION)==-1)
+    {
+      write(BS("Du oeffnest die Flasche und trinkst ihren Inhalt aus."));
+      say(BS(PL->Name(WER, 0)+" oeffnet eine Flasche und trinkt sie in einem "
+        "Schluck aus."));
+      effekt();
+      // TODO: reicht das hier aus, oder muss das noch an anderen Stellen 
+      // eingebaut werden?
+      RemoveId(({"trank","kraeutertrank"}));
+    }
+    else {
+      tell_object(environment(), BS(
+        "Der letzte Trank wirkt noch ein wenig nach. Du kannst Deinem "
+        "Magen nicht so bald schon den naechsten zumuten."));
+    }
+  }
+  return 1;
+}
+
+public nomask int Fill(object *plants)
+{
+  if (!pointerp(plants)) return -1;
+  if (data) return -2;  // schon voll
+  data = PLANTMASTER->make_potion(plants);
+  AddId(({"trank","kraeutertrank"}));
+  if (mappingp(data))
+  {
+    // Pflanzen zerstoert der Master, wenns geklappt hat.
+    expiry=time()+data[T_EXPIRE];
+    SetProp(P_WEIGHT, 300);
+    return 1;
+  }
+  return -3;
+}
+
+void NotifyPlayerDeath(object vic, object killer, int exp)
+{
+  call_out("remove",1);
+}
diff --git a/std/kraeuterstore.c b/std/kraeuterstore.c
new file mode 100644
index 0000000..996a1d6
--- /dev/null
+++ b/std/kraeuterstore.c
@@ -0,0 +1,35 @@
+// (c) 2003 by Padreic (padreic@mg.mud.de)
+// Stdstore fuer Kraeuterlaeden!
+
+inherit "/std/store";
+
+#include <properties.h>
+#include <bank.h>
+
+void create()
+{
+   if (object_name(this_object()) == __FILE__[0..<3])
+   {
+     set_next_reset(-1);
+     return;
+   }
+   ::create();
+   SetProp(P_MIN_STOCK, 50);
+   // in jedem Reset verschwinden nur 2% aller Objekte
+   SetProp(P_STORE_CONSUME, 2);
+}
+
+protected void create_super()
+{
+  set_next_reset(-1);
+}
+
+void reset()
+{
+   ::reset();
+   // damit die Kraeuter im Laden nicht schimmeln, ggf. trocknen...
+   object *obs = filter( all_inventory(), function int (object ob) {
+                   return ob->TimeToLive()<3600;} );
+   obs->DryPlant(95);
+}
+
diff --git a/std/lightsource.c b/std/lightsource.c
new file mode 100644
index 0000000..e4b2245
--- /dev/null
+++ b/std/lightsource.c
@@ -0,0 +1,313 @@
+// MorgenGrauen MUDlib
+//
+// lightsource.c -- standard light source
+//
+// $Id: lightsource.c 7806 2011-07-11 20:28:17Z Zesstra $
+
+// std/lightsource.c
+//
+// Original von Miril April '92
+// Neufassung von Sir Dezember '92 ( unter Boings Aegide :)
+// Version : 1.0
+//
+// Bugfixes von Sir Juni '93
+// Version : 1.2
+//
+// Wichtige Funktionen, die man in create() aufrufen sollte :
+//
+// Setzen der Brennstoffmenge :  
+// SetProp( P_FUEL, int )       int gibt die Brennzeit in Sekunden an
+//
+// Setzen der Beschreibung wenn Objekt angezuendet( default : "brennend" ) :
+// SetProp( P_LIGHTDESC, string )
+//
+// Legt fest, ob ein Leuchtobjekt nach seinem Abbrennen destructed wird
+// SetProp( P_DO_DESTRUCT, int )
+//
+// Legt fest, wie hell die Fackel leuchtet.
+// SetProp( P_LIGHT, int )
+//
+// Ansonsten sind die Standardfuktionen wie z.B. SetProp( P_GENDER, MALE ) 
+// aufzurufen
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "std/thing" ;
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+
+#define TRUE  1
+#define FALSE 0
+#define bool  int
+
+#define DFLT_FUEL       20 // 20 Sekunden
+#define DFLT_LIGHTDESC  "brennend"
+#define DFLT_LIGHT      2
+
+// So ergibt sich die Moeglichkeit sie wieder auzufuellen
+#define CALL_OUT_TIME   100
+
+#undef DEBUG
+
+// TODO: Private waer langfristig nett...
+nosave int  fuel, max_fuel;
+nosave bool lighted ;
+nosave int  call_time ;
+
+void create()
+{
+  thing::create() ;
+  
+  SetProp( P_NAME, "Lichtquelle" ) ;
+  SetProp( P_SHORT, "Eine Lichtquelle" ) ;
+  SetProp( P_LONG, "Du siehst nichts besonderes.\n" ) ;
+  SetProp( P_GENDER, FEMALE ) ;
+  SetProp( P_FUEL, DFLT_FUEL ) ;
+  SetProp( P_LIGHTDESC, DFLT_LIGHTDESC ) ;
+  SetProp( P_LIGHT, DFLT_LIGHT );
+  SetProp( P_DO_DESTRUCT, TRUE ) ;
+  SetProp( P_VALUE, 5 ) ;
+  
+  AddId( ({ "lichtquelle", "\nlichtquelle" }) ) ;
+  AddCmd( ({ "zuende", "zuend" }), "light" );
+  AddCmd( ({ "loesche", "loesch" }), "extinguish" );
+}
+
+void test_remove()
+{  if (QueryProp(P_DO_DESTRUCT)) remove();  }
+
+/*
+ * Lichtquelle anzuenden
+ */
+bool light(string str)
+{
+  string tmp ;
+  object env;
+
+  _notify_fail( "Zuende was an?\n" ) ;
+
+  if( (!str) || (!sscanf( str, "%s an", tmp )) || (!id( tmp )) )
+    return FALSE ;
+  
+  if( environment() != PL ) // Player hat es nicht
+  {
+    _notify_fail( "Erstmal musst Du " + name( WEN, 0 ) + " haben.\n" ) ;
+    return FALSE ;
+  }
+  
+  if( lighted )
+  {
+    _notify_fail( CAP( name( WER, 1 ) ) + " brennt schon.\n" ) ;
+    return FALSE ;
+  }
+
+  if( fuel <= 0 )
+  {
+    _notify_fail( CAP( name( WER, 1 ) ) + " ist abgebrannt.\n" ) ;
+    test_remove() ;
+    return FALSE ;
+  }
+
+  lighted = TRUE ;
+  env=this_object();
+  while (objectp(env=environment(env)))
+      // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+      // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+      // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+      // da um jedes bisschen Rechenzeit geht.
+      // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+      //
+      // Tiamak
+      env->_set_last_content_change();
+  call_time = (fuel < CALL_OUT_TIME)? fuel : CALL_OUT_TIME ;
+  call_out( "out_of_fuel", call_time ) ;
+  if( (int)PL->QueryProp(P_PLAYER_LIGHT) == 1 )
+    write( "Du kannst wieder etwas sehen.\n" ) ;
+  else write( "Ok.\n" ) ;
+  say((string)PL->Name(WER)
+      + " zuendet " + name( WEN, 0 ) + " an.\n" ) ;
+
+  return TRUE ;
+}
+
+bool extinguish(string str)
+{
+  int ti;
+  object env;
+
+  _notify_fail( "Welche Lichtquelle moechtest Du loeschen?\n" ) ;
+
+  if( (!str) || (!id( str )) )
+    return FALSE ;
+  
+  if( !lighted )
+  {
+    _notify_fail( CAP( name( WER, 1 ) ) + " brennt gar nicht.\n" ) ;
+    return FALSE ;
+  }
+
+  if( environment() != PL )
+  {
+    _notify_fail( "Erstmal musst Du " + name( WEN, 0 ) + " haben.\n" ) ;
+    return FALSE ;
+  }
+
+  if( ( ti = remove_call_out( "out_of_fuel" ) ) == -1 )
+    ti = 0 ;
+
+  fuel -= (call_time - ti) ;
+  lighted = FALSE ;
+  env=this_object();
+  while (objectp(env=environment(env)))
+      // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+      // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+      // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+      // da um jedes bisschen Rechenzeit geht.
+      // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+      //
+      // Tiamak
+      env->_set_last_content_change();
+  if ( this_player()->QueryProp(P_PLAYER_LIGHT) == 0 )
+  {
+    write( "Es wird dunkel.\n" ) ;
+    say((string)PL->Name(WER) 
+	+ " macht das Licht aus.\n" ) ;
+  }
+  else
+  {
+    write( "Ok.\n" ) ;
+    say((string)PL->Name(WER)
+	+ " loescht " + name( WEN, 0 ) + " aus.\n" ) ;
+  }
+
+  if( fuel <= 0 ) test_remove() ;
+  return TRUE ;
+}
+
+bool unlight()
+{
+  int ti;
+  object env;
+
+  if( !lighted )
+    return FALSE ;
+
+  if( ( ti = remove_call_out( "out_of_fuel" ) ) == -1 )
+    ti = 0 ;
+
+  fuel -= (call_time - ti) ;
+  lighted = FALSE ;
+  env=this_object();
+  while (objectp(env=environment(env)))
+      // Kommentar siehe oben ;^)
+      env->_set_last_content_change();
+  if( fuel <= 0 ) test_remove() ;
+  return TRUE ;
+}
+
+void out_of_fuel()
+{
+  int i;
+  object *inv, env;
+  
+  fuel -= call_time ;
+
+  if (fuel>0) {
+    call_out( "out_of_fuel", call_time ) ;
+    return ;
+  }
+  lighted=FALSE;
+  env=this_object();
+  while (objectp(env=environment(env)))
+      // Immer noch nicht wirklich sauber. Aber Begruendung siehe oben.
+      env->_set_last_content_change();
+
+  if (environment())
+  {
+    if ( living(environment()) && environment(environment()) )
+    {
+      inv=(users() & all_inventory(environment(environment())))-({ environment() });
+      for (i=sizeof(inv)-1; i>=0; i--) 
+        if (inv[i]->QueryProp(P_PLAYER_LIGHT)<=0)
+          tell_object(inv[i], "Es wird dunkel als " + environment()->name(WESSEN) +
+            " " + QueryProp(P_NAME) + " erlischt.\n" ) ;
+        else tell_object(inv[i], CAP( name( WER, 0 ) ) + " erlischt.\n" ) ;
+      if (environment()->QueryProp(P_PLAYER_LIGHT)<=0)
+        tell_object(environment(), 
+          CAP( name( WER, 1 ) ) + " erlischt und es wird dunkel.\n" ) ;
+      else tell_object(environment(), CAP( name( WER, 1 ) ) + " erlischt.\n" ) ;    
+    }
+    else
+    {
+      inv=(users() & all_inventory(environment()));
+      for (i=sizeof(inv)-1; i>=0; i--) 
+        if (inv[i]->QueryProp(P_PLAYER_LIGHT)<=0)
+          tell_object(inv[i], "Es wird dunkel als " + name(WER,1)
+          + " erlischt.\n" ) ;
+        else tell_object(inv[i], CAP( name( WER, 0 ) ) + " erlischt.\n" ) ;
+    }
+  }  
+  test_remove() ;
+}
+
+// Brennstoff nachfuellen
+void AddFuel(int f)
+{  fuel += f ;  }
+
+// Property - Funktionen
+
+static void _set_lighted(bool l)
+{  lighted = l ;  }
+
+bool _query_lighted()
+{  return lighted ;  }
+
+static void _set_fuel(int f)
+{
+  if (f>max_fuel) max_fuel=f;
+  fuel = f ;
+}
+
+static int _query_fuel()
+{
+  int tmp;
+
+  if ((tmp=find_call_out("out_of_fuel"))>=0)
+    return fuel-call_time+tmp;
+  else return fuel;
+}
+
+static string _query_lightdesc()
+{
+  string *tmp;
+  int n,i;
+  
+  tmp=Query(P_LIGHTDESC);
+  if (!pointerp(tmp)) return (string)tmp;
+  n=sizeof(tmp);
+  i=n*_query_fuel()/max_fuel;
+  if (i>=n) i=n-1;
+  return tmp[i];
+}
+
+static int _query_light()
+{   return (lighted ? Query(P_LIGHT) : 0);  }
+
+/*
+ * short() ueberladen
+ */
+string short()
+{
+  string s;
+  string desc;
+
+  if(!(s=QueryProp(P_SHORT)))
+    return 0;
+  return s + ( (lighted)? (" ("+QueryProp( P_LIGHTDESC ) +").\n") : ".\n" );
+}
diff --git a/std/living/attributes.c b/std/living/attributes.c
new file mode 100644
index 0000000..90027a4
--- /dev/null
+++ b/std/living/attributes.c
@@ -0,0 +1,671 @@
+// MorgenGrauen MUDlib
+//
+// living/attributes.c -- attributes for living objects
+//
+// $Id: attributes.c 8375 2013-02-12 21:52:58Z Zesstra $
+
+#include <sys_debug.h>
+
+// attribute handling
+//
+//   filter_ldfied: str, dex, con, int
+//
+// In den Attributen und Abilites werden Rassenspzifische und erworbene
+// Faehigkeiten (mit autoload-Eigenschaft) abgepseichert.
+//
+// Funktionen:
+//   SetAttribute( attr, val ) (Erzeuge und) setze das Attribut auf val.
+//   QueryAttribute( attr )    Gebe den Wert des Attributes zurueck.
+//
+//   Wenn ein Objekt eine Funktion _filterattr_<name> beitzt, wird beim Setzen
+//   des Attributes <name>, der vorgeschlagene Wert uebergeben und der von
+//   dieser Funktion zurueckgegebene gesetzt. (fuer ueberpruefungszwecke)
+//   Gleiches gilt fuer _filterabil_<name>.
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <attributes.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+
+mapping attributes; // Dies sind die mit ZTs veraenderbaren Attribute
+mapping attributes_modifier; // Modifier sollen gespeichert werden
+nosave mixed* attributes_timed_mods; //P_TIMED_ATTR_MOD
+nosave mapping attributes_offsets; // Offsets NICHT speichern!
+nosave mapping used_attributes_offsets; // Zur Beschleunigung der Berechnung
+nosave object* all_modifiers; // objekte mit P_X/M_ATTR_MOD, P_X/M_HEALTH_MOD
+nosave object* invalid_modifiers; // objekte welche das limit ueberschreiten
+nosave int cumulative_mod; // bilanz der P_X/M_ATTR_MOD
+nosave int hp_off;
+nosave int sp_off;
+
+
+nomask public int SetTimedAttrModifier(string key, mapping modifier, 
+    int outdated, object dependent, mixed notify) {
+  if(!key || key=="" || !modifier || (outdated>0 && outdated<time()) || 
+      (notify && !stringp(notify) && !objectp(notify)) ) {
+    return TATTR_INVALID_ARGS;
+  }
+  
+  
+  if(member(attributes_timed_mods[TATTR_ENTRIES],key)) {
+    // change entry
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_MOD]=modifier;
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_OUTDATED]=outdated;
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_DEPENDENT]=dependent;
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_NOTIFY]=notify;
+  }
+  else {
+    // add entry
+    attributes_timed_mods[TATTR_ENTRIES]+=([key:modifier;outdated;dependent;notify]);
+  }
+  
+  // add outdate
+  if(outdated>0 && member(attributes_timed_mods[TATTR_OUTDATE],outdated)==-1)
+  {
+    attributes_timed_mods[TATTR_OUTDATE]+=({outdated});
+    attributes_timed_mods[TATTR_OUTDATE]=
+      sort_array(attributes_timed_mods[TATTR_OUTDATE],#'<);
+  }
+  
+  // add dependent
+  if(objectp(dependent))
+  {
+  	if(member(attributes_timed_mods[TATTR_DEPENDENTS],key))
+  	{
+	  attributes_timed_mods[TATTR_DEPENDENTS][key]=dependent;
+  	}
+  	else
+  	{
+          attributes_timed_mods[TATTR_DEPENDENTS]+=([key:dependent]);
+  	}
+  }
+  else
+  {
+  	// remove previously set dependent
+  	if(member(attributes_timed_mods[TATTR_DEPENDENTS],key))
+  	{
+	  attributes_timed_mods[TATTR_DEPENDENTS]-=([key]);
+  	}
+  }
+  
+  UpdateAttributes();
+
+  return TATTR_OK;
+}
+
+nomask public mapping QueryTimedAttrModifier(string key)
+{
+  int outdated;
+  object dependent;
+  mixed notify;
+  mapping mod;
+  
+  if(!key || key=="")
+  {
+    return ([]);
+  }
+
+  if(!member(attributes_timed_mods[TATTR_ENTRIES],key))
+  { 
+    return ([]);
+  }
+
+  mod=deep_copy(attributes_timed_mods[TATTR_ENTRIES][key,TATTR_MOD]);
+  outdated=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_OUTDATED];
+  dependent=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_DEPENDENT];
+  notify=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_NOTIFY];
+  
+  return ([key:mod;outdated;dependent;notify ]);
+}
+
+nomask public int DeleteTimedAttrModifier(string key)
+{
+  if(!key || key=="")
+  {
+    return TATTR_INVALID_ARGS;
+  }
+
+  if(!member(attributes_timed_mods[TATTR_ENTRIES],key))
+  { 
+    return TATTR_NO_SUCH_MODIFIER;
+  }
+  
+  attributes_timed_mods[TATTR_DEPENDENTS]-=([key]);
+  attributes_timed_mods[TATTR_ENTRIES]-=([key]);
+  UpdateAttributes();
+  
+  return TATTR_OK;
+}
+
+nomask protected void attribute_hb()
+{
+  int now,i,k,update,outdated;
+  string* keys;
+  mapping tonotify;
+	
+  // initialize
+  now=time();
+  tonotify=([]);
+
+  keys=m_indices(attributes_timed_mods[TATTR_ENTRIES]);
+  
+  // delete outdated 
+  for(i=sizeof(attributes_timed_mods[TATTR_OUTDATE])-1;i>=0;i--)
+  {
+  	outdated=attributes_timed_mods[TATTR_OUTDATE][i];
+  	if(outdated>now)
+  	{
+  	  break;
+  	}
+  	
+  	for(k=sizeof(keys)-1;k>=0;k--)
+  	{
+  	  if(attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_OUTDATED]==outdated)
+  	  {
+  	    // bei fehlendem notifier wurde das zum verhaengnis
+  	    // dank an gloinson
+  	    /*
+  	    if(objectp(attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_NOTIFY]))
+  	    {
+  	    */
+  	    tonotify+=([keys[k]:attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_NOTIFY]]);
+  	    //}
+  	    
+	    attributes_timed_mods[TATTR_DEPENDENTS]-=([keys[k]]);
+ 	    attributes_timed_mods[TATTR_ENTRIES]-=([keys[k]]);
+  	    keys-=({keys[k]});
+  	  }
+  	}
+  	
+  	attributes_timed_mods[TATTR_OUTDATE]-=({outdated});
+  }
+  
+  // delete depending
+  keys=m_indices(attributes_timed_mods[TATTR_DEPENDENTS]);
+  for(i=sizeof(keys)-1;i>=0;i--)
+  {
+  	if(!objectp(attributes_timed_mods[TATTR_DEPENDENTS][keys[i]]))
+  	{
+  	    // siehe oben
+  	    /*
+  	    if(objectp(attributes_timed_mods[TATTR_ENTRIES][keys[i],TATTR_NOTIFY]))
+  	    {
+  	    */
+  	    tonotify+=([keys[i]:attributes_timed_mods[TATTR_ENTRIES][keys[i],TATTR_NOTIFY]]);
+  	    //}
+
+	    attributes_timed_mods[TATTR_DEPENDENTS]-=([keys[i]]);
+ 	    attributes_timed_mods[TATTR_ENTRIES]-=([keys[i]]);
+  	    keys-=({keys[i]});
+  	}
+  }
+  
+  
+  // update
+  if(sizeof(tonotify))
+  {
+    UpdateAttributes();
+    call_out(#'notifyExpiredModifiers,0,tonotify);
+  }
+}
+
+nomask protected void notifyExpiredModifiers(mapping nots)
+{
+  int i;
+  string* keys;
+  while(remove_call_out(#'notifyExpiredModifiers)!=-1);
+  
+  if(!nots)
+  {
+    return;
+  }
+  
+  keys=m_indices(nots);
+  for(i=sizeof(nots)-1;i>=0;i--)
+  {
+  	if(nots[keys[i]] && 
+	    ( objectp(nots[keys[i]])||stringp(nots[keys[i]]) ) )
+  	{
+  	   call_other(nots[keys[i]],"NotifyTimedAttrModExpired",keys[i]);
+  	}
+  }
+}
+
+
+// invalide modifier benachrichtigen
+nomask protected void notifyInvalidModifiers() {
+
+  while(remove_call_out(#'notifyInvalidModifiers)!=-1);
+  
+  if(!invalid_modifiers) {
+    invalid_modifiers=({});
+  }
+  
+  call_other(invalid_modifiers,"NotifyXMAttrModLimitViolation");
+
+}
+
+
+// welche Modifier / modif. Objekte wirken nun?
+protected nomask void calculate_valid_modifiers() {
+  closure qp;
+  mapping res;	
+  string key;	
+  int wert;
+  // Unterscheidung Bonus <-> Malus, weil der Malus voll eingehen soll	
+  int hp_malus, sp_malus;
+ 
+  used_attributes_offsets=([]);
+  cumulative_mod=0;
+  hp_off=sp_off=0;
+  invalid_modifiers=({});
+
+  // rassenspezifische boni P_ATTRIBUTES_OFFSETS  
+  if ( mappingp(attributes_offsets) )
+    used_attributes_offsets+=attributes_offsets;
+
+  if (!pointerp(all_modifiers) || !sizeof(all_modifiers)) {
+    // in diesem Fall koennen wir hier direkt mit dieser Funktion Schluss
+    // machen. ;-)
+    return;
+  }
+  else
+    all_modifiers-=({0}); //zerstoerte Objekte rauswerfen.
+    
+  // einmal ueber alle modifizierenden Objekt iterieren und aufaddieren
+  foreach(object ob: all_modifiers) {
+    qp = symbol_function("QueryProp",ob);
+ 
+    if (!objectp(ob) || environment(ob)!=this_object()) {
+      all_modifiers-=({ob});
+      continue;
+    }
+ 		
+    // ext. Attribut-Modifier
+    if ( mappingp(res=funcall(qp,P_X_ATTR_MOD)) ) {		
+      foreach(key, wert: res) {
+        cumulative_mod+=wert;
+      }
+    }
+
+    // magic Attribut-Modifier
+    if ( mappingp(res=funcall(qp,P_M_ATTR_MOD))
+        && ( funcall(qp,P_WORN)
+            || funcall(qp,P_WIELDED) ) ) {		
+      foreach(key, wert: res) {
+        cumulative_mod+=wert;
+      }
+    }
+    // Magic Health-Modifier 		
+    if ( mappingp(res=funcall(qp,P_M_HEALTH_MOD))
+        && ( funcall(qp,P_WORN)
+            || funcall(qp,P_WIELDED) ) ) {
+      if (res[P_HP] < 0)
+        hp_malus += res[P_HP];
+      else
+        hp_off+=res[P_HP];
+
+      if (res[P_SP] < 0)
+        sp_malus += res[P_SP];
+      else
+        sp_off+=res[P_SP];
+      }
+	      
+    // external Health Modifier
+    if ( mappingp(res=funcall(qp,P_X_HEALTH_MOD)) ) {
+      if (res[P_HP] < 0)
+        hp_malus += res[P_HP];
+      else
+        hp_off+=res[P_HP];
+
+      if (res[P_SP] < 0)
+        sp_malus += res[P_SP];
+      else
+        sp_off+=res[P_SP];	    
+    }	
+  
+  } // Ende 1. foreach()
+  
+ 
+  // und nochmal, soviele Objekt wieder rausschmeissen, bis das Limit wieder
+  // unterschritten wird. (Verbesserungsvorschlaege erwuenscht.) :-(
+  foreach(object ob: all_modifiers) {
+
+    qp = symbol_function("QueryProp",ob);
+ 
+    if ( mappingp(res=funcall(qp,P_X_ATTR_MOD)) ) {
+      if(cumulative_mod>CUMULATIVE_ATTR_LIMIT) { 
+        invalid_modifiers+=({ob});	  
+	foreach(key, wert: res) {
+          cumulative_mod-=wert;
+        }
+      }
+      else {
+        add_offsets(res);
+      }
+    }
+
+    if ( mappingp(res=funcall(qp,P_M_ATTR_MOD))
+        && ( funcall(qp,P_WORN)
+            || funcall(qp,P_WIELDED) ) ) {
+      if(cumulative_mod>CUMULATIVE_ATTR_LIMIT) {     
+        if(member(invalid_modifiers,ob)==-1) {
+          invalid_modifiers+=({ob});
+        }			
+	foreach(key, wert: res) {
+          cumulative_mod-=wert;
+        }
+      }
+      else {
+        add_offsets(res);
+      }
+    }
+  }
+
+  // HEALTH_MOD werden durch eine Formel 'entschaerft', damit man nur schwer
+  // das Maximum von 150 erreichen kann. (Formel von Humni, beschlossen auf 3.
+  // EM-Treffen)
+  // Mali gehen aber voll ein
+  hp_off = (int)(150 - (150*150.0/(150 + hp_off))) + hp_malus;
+  sp_off = (int)(150 - (150*150.0/(150 + sp_off))) + sp_malus;
+
+  /* alte Version
+    hp_off += hp_malus;
+    sp_off += sp_malus;
+  */
+ 
+  // notify invalid modifiers
+  if(sizeof(invalid_modifiers)>0) {
+    call_out(#'notifyInvalidModifiers,0);
+  }
+}
+
+// abmelden eines modifiers 
+nomask public void deregister_modifier(object modifier)
+{ 
+  if (!pointerp(all_modifiers)) {
+    return;
+  }
+
+  // valid object?
+  if (!objectp(modifier) || member(all_modifiers,modifier)==-1) {
+    return;
+  }
+  
+  all_modifiers-=({modifier});
+  if (invalid_modifiers) {
+    invalid_modifiers-=({modifier});
+  }
+}
+
+// anmelden eines modifiers 
+nomask public void register_modifier(object modifier) { 
+  closure qp;
+  
+  if (!pointerp(all_modifiers)) {
+    all_modifiers=({});
+  }
+
+  // valid object?
+  if (!objectp(modifier) || environment(modifier)!=this_object() || 
+      member(all_modifiers,modifier)!=-1) {
+    return;
+  }
+  
+  qp = symbol_function("QueryProp",modifier);
+  // modifier after all? Die P_M_* muessen getragen/gezueckt sein.
+  if(mappingp(funcall(qp,P_X_ATTR_MOD)) ||
+     mappingp(funcall(qp,P_X_HEALTH_MOD)) ||
+    ((mappingp(funcall(qp,P_M_ATTR_MOD)) ||
+       mappingp(funcall(qp,P_M_HEALTH_MOD))) &&
+      (funcall(qp,P_WORN) || funcall(qp,P_WIELDED)) ) ) {
+    all_modifiers+=({modifier});
+  }
+}
+
+protected void add_offsets(mapping arr) {
+  mixed *ind;
+  int i;
+  
+  if ( !mappingp(arr) )
+    return;
+
+  foreach(string key, int wert: arr) {
+    used_attributes_offsets[key]+=wert;
+  }
+}
+
+public void UpdateAttributes() {
+  mixed   *ind;
+  int     i;
+  
+  // alle gueltigen Modifier ermitteln aus Objekten im Inventar.
+  calculate_valid_modifiers();
+
+  // persistente modifier drauf (frosch zb)
+  // aus P_ATTRIBUTES_MODIFIERS
+  if ( mappingp(attributes_modifier) ) {
+    foreach(mixed key, mapping wert: attributes_modifier) {
+      add_offsets(wert); // Modifier addieren...		
+    }
+  }
+  // timed modifier drauf aus P_TIMED_ATTR_MOD
+  ind=m_indices(attributes_timed_mods[TATTR_ENTRIES]);
+  for ( i=sizeof(ind)-1 ; i>=0 ; i-- ) {
+    // Modifier addieren...
+    add_offsets(attributes_timed_mods[TATTR_ENTRIES][ind[i],0]);
+  }
+  // Bei Monstern werden die HP/SP ueblicherweise selbst gesetzt
+  if ( !query_once_interactive(this_object()))
+    return;
+
+  SetProp(P_MAX_HP, QueryAttribute(A_CON)*8+42+hp_off);
+  SetProp(P_MAX_SP, QueryAttribute(A_INT)*8+42+sp_off);
+  
+  if(QueryProp(P_HP)>QueryProp(P_MAX_HP)) {
+    SetProp(P_HP,QueryProp(P_MAX_HP));
+  }
+  
+  if(QueryProp(P_SP)>QueryProp(P_MAX_SP)) {
+    SetProp(P_SP,QueryProp(P_MAX_SP));
+  }
+
+  GMCP_Char( ([ A_INT: QueryAttribute(A_INT),
+                A_DEX: QueryAttribute(A_DEX),
+                A_STR: QueryAttribute(A_STR),
+                A_CON: QueryAttribute(A_CON) ]) );
+}
+
+
+protected void create() {
+  hp_off=sp_off=0;
+  used_attributes_offsets=([]);
+  all_modifiers=({});
+  invalid_modifiers=({});
+  cumulative_mod=0;
+  
+  Set(P_ATTRIBUTES_MODIFIER, attributes_modifier=([])); // nicht geschuetzt
+  Set(P_ATTRIBUTES_OFFSETS, attributes_offsets=([]));
+  Set(P_ATTRIBUTES_OFFSETS, PROTECTED, F_MODE);
+  Set(P_ATTRIBUTES, attributes=([]));
+  Set(P_ATTRIBUTES, PROTECTED, F_MODE);
+
+  Set(P_TIMED_ATTR_MOD, attributes_timed_mods=({ ({}),([]),([]) }));
+  Set(P_TIMED_ATTR_MOD, NOSETMETHOD|SECURED, F_MODE_AS);
+}
+
+static mixed _query_timed_attr_mod() {
+  mixed ret;
+  return Set(P_TIMED_ATTR_MOD,	    
+      ({attributes_timed_mods[0],
+	deep_copy(attributes_timed_mods[1]),
+	deep_copy(attributes_timed_mods[2])}));
+}
+
+static mapping _set_attributes(mapping arr) 
+{
+  Set(P_ATTRIBUTES, attributes=arr);
+  UpdateAttributes();
+  return arr;
+}
+
+static mapping _query_attributes() 
+{
+  return deep_copy(Set(P_ATTRIBUTES, attributes)); 
+}
+
+static mapping _set_attributes_offsets(mapping arr) 
+{
+  Set(P_ATTRIBUTES_OFFSETS, attributes_offsets=arr);
+  UpdateAttributes();
+  return attributes_offsets;
+}
+
+static mapping _query_attributes_offsets() 
+{ 
+  return deep_copy(Set(P_ATTRIBUTES_OFFSETS, attributes_offsets));
+}
+
+static mixed _set_attributes_modifier(mixed arr) 
+{ string fn;
+  mixed pre;
+  mapping map_ldfied;
+  
+  if ( pointerp(arr) && (sizeof(arr)>=2) )
+  {
+    pre=arr[0];
+    map_ldfied=arr[1];
+  }
+  else 
+  {
+    pre=previous_object();
+    map_ldfied=arr;
+  }
+
+  if ( objectp(pre) )
+    fn=old_explode(object_name(pre),"#")[0];
+  else
+    fn=pre;
+
+  if ( !stringp(fn) )
+    return 0;
+
+  // wenn Modifier kein mapping oder ein leeres Mapping: loeschen
+  if ( !mappingp(map_ldfied) || !sizeof(map_ldfied))
+    m_delete(attributes_modifier,fn);
+  else
+    attributes_modifier[fn]=map_ldfied;
+
+  Set(P_ATTRIBUTES_MODIFIER, attributes_modifier);
+  UpdateAttributes();
+  return attributes_modifier[fn];
+}
+
+static mapping _query_attributes_modifier() 
+{
+  return deep_copy(attributes_modifier);  
+}
+
+public int SetAttr(string attr, int val) 
+{ closure filter_ldfied;
+
+  if ( filter_ldfied = symbol_function("_filterattr_"+attr, this_object()) ) 
+    val = funcall(filter_ldfied, val );
+
+  attributes[attr] = val;
+  UpdateAttributes();
+  return val;
+}
+
+public int SetAttribute(string attr, int val) 
+{
+  return SetAttr(attr, val-used_attributes_offsets[attr]);
+}
+
+// Diese Funktion sollte zum Abfragen verwendet werden:
+public int QueryAttribute(string attr) 
+{ int re;
+
+  re=attributes[attr]+used_attributes_offsets[attr];
+
+  if ( query_once_interactive(this_object()) && (re>30) )
+    re=30;
+
+  return re;
+}
+
+public int QueryRealAttribute(string attr) 
+{
+  return attributes[attr];
+}
+
+public int SetRealAttribute(string attr, int val) 
+{
+  return SetAttr(attr, val);
+}
+
+public int QueryAttributeOffset(string attr) 
+{
+  return used_attributes_offsets[attr];
+}
+
+public status TestLimitViolation(mapping check)
+{
+  int k,test;
+  mixed* ind;
+    
+  if(!check || !mappingp(check))
+  {
+    return 0;
+  }
+
+  test=0;
+  ind=m_indices(check);
+  for( k=sizeof(ind)-1 ; k>=0 ; k-- )
+  {
+    test+=check[ind[k]];
+  }
+  
+  test+=cumulative_mod;
+  if(test>CUMULATIVE_ATTR_LIMIT)
+  {
+    return 1;
+  }
+  
+  return 0;
+}
+
+/*
+ *------------------------------------------------------------
+ * attributes compatibility functions
+ */
+
+protected int _filterattr_str(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
+
+protected int _filterattr_dex(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
+
+protected int _filterattr_int(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
+
+protected int _filterattr_con(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
diff --git a/std/living/clothing.c b/std/living/clothing.c
new file mode 100644
index 0000000..56aff44
--- /dev/null
+++ b/std/living/clothing.c
@@ -0,0 +1,96 @@
+// MorgenGrauen MUDlib
+//
+// living/clothing.c -- Modul fuer Bekleidungsfragen. ;-)
+//
+// $Id: armour.c,v 3.8 2003/08/25 09:36:04 Rikus Exp $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+#include <living/clothing.h>
+#include <living/combat.h>
+#include <armour.h>
+
+protected void create() {
+  SetProp(P_CLOTHING, ({}));
+  Set(P_CLOTHING, PROTECTED, F_MODE_AS);
+}
+
+public object *FilterClothing(closure filterfun, varargs mixed* extra) {
+  if (!closurep(filterfun))
+    return ({});
+  return apply(#'filter, QueryProp(P_CLOTHING), filterfun, extra);
+}
+
+public object *FilterArmours(closure filterfun, varargs mixed* extra) {
+  if (!closurep(filterfun))
+    return ({});
+  return apply(#'filter, QueryProp(P_ARMOURS)-({0}), filterfun, extra);
+}
+
+public int WearClothing(object ob) {
+  object *clothing = QueryProp(P_CLOTHING);
+  if (member(clothing, ob) != -1)
+    return 0;
+  clothing += ({ob});
+  SetProp(P_CLOTHING, clothing);
+  return 1;
+}
+
+public int WearArmour(object ob) {
+  if (!VALID_ARMOUR_TYPE(ob->QueryProp(P_ARMOUR_TYPE)))
+    return 0;
+
+  object *armours = QueryProp(P_ARMOURS);
+  if (member(armours, ob) != -1)
+    return 0;
+
+  armours += ({ob});
+  SetProp(P_ARMOURS, armours);
+  return 1;
+}
+
+public int Wear(object ob) {
+  // reihenfolge ist wichtig! Ruestung sind _auch_ Kleidung, aber Kleidung
+  // keine Ruestung.
+  if (ob->IsArmour())
+    return WearArmour(ob);
+  else if (ob->IsClothing())
+    return WearClothing(ob);
+  return 0;
+}
+
+public int UnwearClothing(object ob) {
+object *clothing = QueryProp(P_CLOTHING);
+  if (member(clothing, ob) == -1)
+    return 0;
+  clothing -= ({ob});
+  SetProp(P_CLOTHING, clothing);
+  return 1;
+}
+
+public int UnwearArmour(object ob) {
+  object *armours = QueryProp(P_ARMOURS);
+  if (member(armours, ob) == -1)
+    return 0;
+
+  armours -= ({ob});
+  SetProp(P_ARMOURS, armours);
+  return 1;
+}
+
+public int Unwear(object ob) {
+  // reihenfolge ist wichtig! Ruestung sind _auch_ Kleidung, aber Kleidung
+  // keine Ruestung.
+  if (ob->IsArmour())
+    return UnwearArmour(ob);
+  else if (ob->IsClothing())
+    return UnwearClothing(ob);
+  return 0;
+}
+
diff --git a/std/living/combat.c b/std/living/combat.c
new file mode 100644
index 0000000..880f3f6
--- /dev/null
+++ b/std/living/combat.c
@@ -0,0 +1,2311 @@
+// MorgenGrauen MUDlib
+//
+// living/combat.c -- Basis-Kampfmodul
+//
+// $Id: combat.c 9568 2016-06-05 18:53:10Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/skill_utils";
+inherit "/std/living/inventory";
+inherit "/std/living/team";
+
+#include <sys_debug.h>
+#include <debug_message.h>
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#include <living/skills.h>
+#include <thing/properties.h>
+#include <player/comm.h>
+#include <living/skill_attributes.h>
+#include <combat.h>
+#include <living.h>
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <attributes.h>
+#include <new_skills.h>
+
+#include <defines.h>
+
+#include <sensitive.h>
+
+#define HUNTTIME 300 //300 HBs sind 10 Minuten
+#define RNAME(x) capitalize(getuid(x))
+
+// 'private'-Prototypes
+private string _kill_alias( string str );
+
+// globale Variablen
+nosave mapping enemies;
+private nosave string magic_attack;
+private nosave int attack_busy;
+nosave int no_more_attacks;
+private nosave int remaining_heart_beats;
+private nosave int att2_time;
+private nosave string last_attack_msg;
+private nosave object *missing_attacks;
+private nosave mapping peace_tries;
+// Cache fuer QueryArmourByType()
+private nosave mapping QABTCache;
+
+protected void create()
+{
+  Set(P_WIMPY, SAVE, F_MODE_AS);
+  Set(P_TOTAL_AC, PROTECTED, F_MODE_AS);
+  Set(P_HANDS, SAVE, F_MODE_AS);
+  Set(P_SHOW_ATTACK_MSG,SAVE,F_MODE_AS);
+  Set(P_RESISTANCE, ({}));
+  Set(P_VULNERABILITY, ({}));
+  Set(P_GUILD_PREPAREBLOCK, SAVE, F_MODE_AS);
+  // Kein Setzen von P_ARMOURS von aussen per Set(). (Per SetProp() geht es
+  // durch die Setmethode).
+  Set(P_ARMOURS,PROTECTED,F_MODE_AS);
+  SetProp(P_ARMOURS, ({}));
+  attack_busy=100;
+  att2_time=0;
+  enemies=([]);
+  peace_tries=([]);
+  team::create(); 
+  offerHook(H_HOOK_DEFEND,1);
+  offerHook(H_HOOK_ATTACK,1);
+  offerHook(H_HOOK_ATTACK_MOD,1);
+}
+
+#undef DEBUG
+#define DEBUG(x) if (find_object("zesstra")) \
+  tell_object(find_player("zesstra"),x)
+
+public void UpdateResistanceStrengths() { 
+  mapping resmods, strmap;
+
+  if ( !mappingp(resmods=Query(P_RESISTANCE_MODIFIER)) )
+    return;
+
+  //erstmal die alte Aufsummation loeschen
+  m_delete(resmods,"me");
+
+  //wenn jetzt leer: Abbruch, keine Res-Modifier da.
+  if (!sizeof(resmods))
+      return(Set(P_RESISTANCE_MODIFIER,0));
+
+  strmap = ([]);
+
+  // ueber alle gesetzten ResModifier gehen
+  foreach(string mod, mapping resmap, object ob: resmods) {
+    if ( !mappingp(resmap) || !sizeof(resmap)
+        || !objectp(ob) ) {
+      m_delete(resmods, mod);
+      continue; // Resi ungueltig, weg damit.
+    }
+    // jetzt noch ueber die Submappings laufen, die die Resis der Objekte
+    // beinhalten.
+    foreach(string reskey, float resi: resmap) {
+        strmap[reskey] = ((strmap[reskey]+1.0)*(resi+1.0))-1.0;
+    }
+  }
+
+  if ( !sizeof(strmap) )
+    Set(P_RESISTANCE_MODIFIER, 0);
+  else
+    Set(P_RESISTANCE_MODIFIER, resmods+([ "me" : strmap; 0 ]) );
+}
+
+public varargs int AddResistanceModifier(mapping mod, string add)
+{ string  key;
+  mapping res;
+
+  if ( !mappingp(mod) || !sizeof(mod) || !previous_object() )
+    return 0;
+
+  key = explode(object_name(previous_object()),"#")[0];
+
+  if ( add )
+    key += ("#"+add);
+
+  res = Query(P_RESISTANCE_MODIFIER);
+  mod = deep_copy(mod);
+
+  if ( !mappingp(res) )
+    res = ([ key : mod; previous_object() ]);
+  else
+    res += ([ key : mod; previous_object() ]);
+
+  Set(P_RESISTANCE_MODIFIER, res);
+  UpdateResistanceStrengths();
+
+  return 1;
+}
+
+public varargs void RemoveResistanceModifier(string add)
+{ string  key;
+  mapping res;
+
+  if ( !previous_object() )
+    return;
+
+  key = explode(object_name(previous_object()),"#")[0];
+
+  if ( add )
+    key += ("#"+add);
+
+  if ( !mappingp(res = Query(P_RESISTANCE_MODIFIER)) )
+    return;
+
+  m_delete(res, key);
+  Set(P_RESISTANCE_MODIFIER, res);
+  UpdateResistanceStrengths();
+}
+
+// veraltete Prop, aus Kompatibilitaetsgruenden noch vorhanden.
+static mixed _set_resistance(mixed arg)
+{ int     i;
+  mapping resimap;
+  mixed   old;
+
+  if ( !pointerp(arg) )
+    arg=({arg});
+
+  if ( !mappingp(resimap=Query(P_RESISTANCE_STRENGTHS)) )
+    resimap=([]);
+
+  if ( pointerp(old=QueryProp(P_RESISTANCE)) )
+    for ( i=sizeof(old)-1 ; i>=0 ; i-- )
+      resimap[old[i]]=(1.0+((float)resimap[old[i]]))*2.0-1.0;
+
+  for ( i=sizeof(arg)-1 ; i>=0 ; i-- )
+    resimap[arg[i]]=(1.0+((float)resimap[arg[i]]))*0.5-1.0;
+
+  SetProp(P_RESISTANCE_STRENGTHS,resimap);
+
+  return Set(P_RESISTANCE,arg);
+}
+
+// veraltete Prop, aus Kompatibilitaetsgruenden noch vorhanden.
+static mixed _set_vulnerability(mixed arg)
+{ int     i;
+  mapping resimap;
+  mixed   old;
+
+  if ( !pointerp(arg) )
+    arg=({arg});
+
+  if ( !mappingp(resimap=Query(P_RESISTANCE_STRENGTHS)) )
+    resimap=([]);
+
+  if ( pointerp(old=QueryProp(P_VULNERABILITY)) )
+    for ( i=sizeof(old)-1 ; i>=0 ; i-- )
+      resimap[old[i]]=(1.0+((float)resimap[old[i]]))*0.5-1.0;
+
+  for ( i=sizeof(arg)-1 ; i>=0 ; i-- )
+    resimap[arg[i]]=(1.0+((float)resimap[arg[i]]))*2.0-1.0;
+
+  SetProp(P_RESISTANCE_STRENGTHS,resimap);
+
+  return Set(P_VULNERABILITY,arg);
+}
+
+
+/** kill - Kampf starten.
+ * Fuegt ob der Feindesliste hinzu.
+ */
+public int Kill(object ob)
+{ int   res, arena;
+  int|string no_attack;
+
+  if ( !objectp(ob) )
+    return 0;
+
+  if ( ob->QueryProp(P_GHOST) )
+  {
+    tell_object(ME,ob->Name(WER)+" ist doch schon tot!\n");
+    return -1;
+  }
+
+  if ( no_attack = ob->QueryProp(P_NO_ATTACK) )
+  {
+    if ( stringp(no_attack) )
+      tell_object(ME, no_attack);
+    else
+      tell_object(ME, ob->Name(WER,1)+" laesst sich nicht angreifen!\n");
+
+    return -2;
+  }
+
+  if ( QueryProp(P_NO_ATTACK) )
+    return -3;
+
+  res=InsertEnemy(ob);
+  if (res)
+    tell_object(ME, "Ok.\n");
+  else if (IsEnemy(ob))
+    tell_object(ME, "Jajaja, machst Du doch schon!\n");
+  //else //kein gueltiger Feind, ob wurde nicht eingetragen.
+
+  if ( !res )
+    return -4;  // nicht aendern ohne Kill in player/combat.c
+
+  return 1;
+}
+
+public int InsertSingleEnemy(object ob)
+{
+    if ( !living(ob) )
+        return 0;
+
+    // Wie lange verfolgt dieses Living? Wenn Prop nicht gesetzt oder 
+    // ungueltig, d.h. kein int oder <= 0, dann wird die Defaultzeit genommen.
+    int hunttime = (int)QueryProp(P_HUNTTIME);
+    hunttime = (intp(hunttime) && hunttime > 0) ? 
+                 hunttime / __HEART_BEAT_INTERVAL__ : HUNTTIME;
+
+    // Auch wenn ein Objekt schon als Gegner eingetragen ist, trotzdem die
+    // HUNTTIME erneuern. Das "return 0;" muss aber bleiben, da der Gegner
+    // ja nicht neu ist.
+    //
+    // 09.12.2000, Tiamak
+    if ( member( enemies, ob ) ) {
+        enemies[ob,ENEMY_HUNTTIME] = hunttime;
+        return 0;
+    }
+
+    if ( (ob==ME) || QueryProp(P_NO_ATTACK)
+         || !objectp(ob) || (ob->QueryProp(P_NO_ATTACK))
+         || (ob->QueryProp(P_GHOST)) )
+        return 0;
+
+    object team;
+    if ( ( query_once_interactive(ME) || query_once_interactive(ob) )
+         && objectp(team=Query(P_TEAM)) && (team==(ob->Query(P_TEAM))) )
+        return 0;
+
+    set_heart_beat(1);
+
+    last_attack_msg=0;
+
+    enemies[ob,ENEMY_HUNTTIME] = hunttime;
+    Set(P_LAST_COMBAT_TIME,time());
+
+    return 1;
+}
+
+public int InsertEnemy(object ob)
+{ int res;
+
+  if ( res=InsertSingleEnemy(ob) )
+    InsertEnemyTeam(ob);
+
+  return res;
+}
+
+public object QueryPreferedEnemy()
+{ int    sz,r;
+  <int|object>*  pref;
+  object enemy;
+
+  enemy=0;
+  if ( pointerp(pref=QueryProp(P_PREFERED_ENEMY)) && ((sz=sizeof(pref))>1)
+      && intp(r=pref[0]) && (random(100)<r) )
+  {
+    enemy=pref[1+random(sz-1)];
+
+    if ( !objectp(enemy) )
+    {
+      pref-=({enemy});
+
+      if ( sizeof(pref)<2 )
+        pref=0;
+
+      SetProp(P_PREFERED_ENEMY,pref);
+
+      return 0;
+    }
+
+    if ( !IsEnemy(enemy) )
+      return 0;
+  }
+
+  return enemy;
+}
+
+public object *PresentEnemies() {
+
+  object *here=({});
+  object *netdead=({});
+  mixed no_attack;
+  foreach(object pl: enemies) {
+    if (no_attack = (mixed)pl->QueryProp(P_NO_ATTACK)) {
+      m_delete(enemies,pl);
+      // Und auch im Gegner austragen, sonst haut der weiter zu und dieses
+      // Living wehrt sich nicht. (Zesstra, 26.11.2006)
+      if (stringp(no_attack)) {
+        tell_object(ME, no_attack);
+        pl->StopHuntFor(ME, 1);
+      }
+      else pl->StopHuntFor(ME, 0);
+    }
+    else if ( environment()==environment(pl) )
+    {
+      if ( interactive(pl) || !query_once_interactive(pl) )
+        here+=({pl});
+      else
+        netdead+=({pl});
+    }
+  }
+
+  if ( !sizeof(here) ) // Netztote sind nur Feinde, falls keine anderen da sind
+    return netdead;
+
+  return here;
+}
+
+public varargs object SelectEnemy(object *here)
+{ object enemy;
+
+  if ( !pointerp(here) )
+    here=PresentEnemies();
+
+  if ( !sizeof(here) )
+    return 0;
+
+  if ( !objectp(enemy=QueryPreferedEnemy())
+      || (environment(enemy)!=environment()) )
+    enemy=here[random(sizeof(here))];
+
+  return enemy;
+}
+
+protected void heart_beat() {
+  int hbs; 
+  // leider rufen viele Leute einfach das geerbte heart_beat() obwohl sie
+  // schon tot sind und damit das Objekt zerstoert ist.
+  if (!living(this_object()))
+      return;
+
+  // Paralyse pruefen, ggf. reduzieren
+  int dis_attack = QueryProp(P_DISABLE_ATTACK);
+  if ( intp(dis_attack) && (dis_attack > 0) )
+  {
+      SetProp(P_DISABLE_ATTACK, --dis_attack);
+  }
+
+  // Attacken ermitteln: SA_SPEED + Rest aus dem letzten HB
+  hbs = remaining_heart_beats + QuerySkillAttribute(SA_SPEED);
+  if ( hbs <= 0 )
+    hbs = 100; 
+
+  // P_ATTACK_BUSY bestimmen
+  if ( attack_busy > 99) 
+   attack_busy = 100 + hbs;
+  else
+    attack_busy += 100 + hbs;
+  // mehr fuer Seher...
+  if ( IS_SEER(ME) )
+    attack_busy+=(100+QueryProp(P_LEVEL)); 
+  // max. 500, d.h. 5 Attacken
+  if ( attack_busy>500 )
+    attack_busy=500;
+
+  // unganzzahligen Rest fuer naechsten HB speichern
+  remaining_heart_beats = hbs % 100;
+  hbs /= 100; // ganze Attacken. ;-)
+
+  if ( hbs > 10 ) // nicht mehr als 10 Attacken.
+    hbs = 10; 
+
+  // Falls jemand seit dem letzten HB in diesem Living von aussen Attack()
+  // gerufen hat (nicht ExtraAttack()), wird jetzt was abgezogen.
+  hbs -= no_more_attacks;
+  no_more_attacks = 0;
+
+  // Wie lange verfolgt dieser NPC? Wenn Prop nicht gesetzt oder ungueltig,
+  // d.h. kein int oder <= 0, dann wird die Defaultzeit genommen.
+  int hunttime = (int)QueryProp(P_HUNTTIME);
+  hunttime = (intp(hunttime) && hunttime > 0) ? 
+              hunttime / __HEART_BEAT_INTERVAL__ : HUNTTIME;
+  // erstmal die Hunttimes der Gegner aktualisieren und nebenbei die
+  // anwesenden Feinde merken. Ausserdem ggf. Kampf abbrechen, wenn
+  // P_NO_ATTACK gesetzt ist.
+  // eigentlich ist diese ganze Schleife fast das gleiche wie
+  // PresentEnemies(), aber leider muessen ja die Hunttimes aktulisiert werde,
+  // daher kann man nicht direkt PresentEnemies() nehmen.
+  // TODO: Diese Schleife und PresentEnmies() irgendwie vereinigen.
+  object *netdead=({});
+  object *here=({});
+  object enemy;
+  mixed no_attack;
+  foreach(enemy, int htime: &enemies) { // Keys in enemy
+    // ggf. Meldungen ausgeben und Gegner austragen und dieses Living beim
+    // Gegner austragen.
+    if (no_attack=enemy->QueryProp(P_NO_ATTACK)) {
+      m_delete(enemies,enemy);
+        if ( stringp(no_attack) ) {
+            write( no_attack );
+            enemy->StopHuntFor( ME, 1 );
+        }
+        else
+            enemy->StopHuntFor( ME, 0 );
+    }
+    else if ( environment()==environment(enemy) ) {
+      // Gegner anwesend, als Feind merken...
+      if ( interactive(enemy) || !query_once_interactive(enemy) )
+        here+=({enemy});
+      else
+        netdead+=({enemy});
+      // ... und Hunttime erneuern.
+      htime = hunttime;
+    }
+    // Feind nicht anwesend. Timer dekrementieren und Feind austragen, wenn
+    // Jagdzeit abgelaufen.
+    else if ( (--htime) <= 0 )
+      StopHuntFor(enemy);
+  }
+  // Netztote sind nur Feinde, wenn sonst keine da sind.
+  if ( !sizeof(here) )
+    here=netdead;
+
+  // schonmal abfragen, ob ein Spell vorbereitet wird.
+  mixed pspell=QueryProp(P_PREPARED_SPELL);
+
+  //Da hbs 0 und no_more_attacks durch einen manuellen Aufruf von Attack() im
+  //Spieler >0 sein kann, kann jetzt hbs <0 sein. (s.o.)
+  if (hbs > 0 && sizeof(here)) {
+    foreach(int i: hbs) {
+      // Feind in Nahkampfreichweite finden
+      if ( objectp(enemy=SelectNearEnemy(here)) ) {
+        // Flucht erwuenscht?
+        if ( QueryProp(P_WIMPY) > QueryProp(P_HP) ) {
+          Flee();
+          // Flucht gelungen?
+          if ( enemy && (environment(enemy)!=environment()) ) {
+            here = 0; // naechste Runde neue Feinde suchen
+            enemy = 0;
+            continue; // kein Kampf, Runde ueberspringen
+          }
+        }
+      }
+      else {
+        // keine Feinde gefunden... Abbrechen.
+        break;
+      }
+      // Paralyse gesetzt? -> Abbrechen. Dieses Pruefung muss hier passieren,
+      // damit ggf. die automatische Flucht des Lebewesens durchgefuehrt wird,
+      // auch wenn es paralysiert ist.
+      if (dis_attack > 0) break;
+
+      // Kampf durch Spell-Prepare blockiert?
+      // Keine genaue Abfrage, wird spaeter noch genau geprueft.
+      if ( pspell && QueryProp(P_GUILD_PREPAREBLOCK) )
+        break; // keine Angriffe in diesem HB.
+      // wenn Feind da: hit'em hard.
+      if ( objectp(enemy) )
+        Attack(enemy);
+      // ggf. hat Attack() no_more_attacks gesetzt. Zuruecksetzen
+      no_more_attacks = 0;
+      // naechste Kampfrunde neue Feinde suchen
+      here = 0;
+    }  // foreach
+  }
+
+  no_more_attacks=0;
+
+  // Ist ein Spell in Vorbereitung und evtl. jetzt fertig vorbereitet?
+  if ( pointerp(pspell)
+      && (sizeof(pspell)>=3) && intp(pspell[0]) && stringp(pspell[1]) ) {
+    if ( time()>=pspell[0] ) // Kann der Spruch jetzt ausgefuehrt werden?
+    {
+      UseSpell(pspell[2],pspell[1]); // Dann los
+      SetProp(P_PREPARED_SPELL,0);
+    }
+  }
+  else if ( pspell ) // Unbrauchbarer Wert, loeschen
+    SetProp(P_PREPARED_SPELL,0);
+
+} // Ende heart_beat
+
+// wird von NPC gerufen, die Heartbeats nachholen und dabei die Hunttimes um
+// die Anzahl verpasster Heartbeats reduzieren muessen.
+// Wird von Spielerobjekten gerufen, wenn sie nach 'schlafe ein' aufwachen, um
+// die notwendige Anzahl an HB hier abzuziehen und ggf. die Feinde zu expiren,
+// da Spieler ja netztot keinen Heartbeat haben.
+protected void update_hunt_times(int beats) {
+  if (!mappingp(enemies)) return;
+  foreach(object en, int htime: &enemies) { // Mapping-Keys in en
+    htime -= beats;
+    if ( htime <= 0 )
+      StopHuntFor(en);
+  }
+}
+
+public int IsEnemy(object wer)
+{
+  return (member(enemies,wer));
+}
+
+public void StopHuntText(object arg)
+{
+  tell_object(arg,
+    Name(WER,1)+" "+(QueryProp(P_PLURAL)?"jagen ":"jagt ")+
+               (arg->QueryProp(P_PLURAL)?"Euch":"Dich")+" nicht mehr.\n");
+  tell_object(ME,(QueryProp(P_PLURAL)?"Ihr jagt ":"Du jagst ")+
+                 arg->name(WEN,1)+" nicht mehr.\n");
+}
+
+public varargs int StopHuntFor(object arg, int silent)
+{
+  if ( !objectp(arg) || !IsEnemy(arg) )
+    return 0;
+
+  if (!silent)
+    StopHuntText(arg);
+
+  m_delete(enemies,arg);
+  last_attack_msg=0;
+
+  return 1;
+}
+
+// Begruessungsschlag nur einmal pro HB
+public void Attack2(object enemy)
+{
+  if ( att2_time > time() )
+    return;
+
+  att2_time=time() + __HEART_BEAT_INTERVAL__;
+  Attack(enemy);
+}
+
+// Fuer evtl. Attack-Aenderungen, ohne dass man gleich das ganze
+// Attack ueberlagern muss:
+protected void InternalModifyAttack(mapping ainfo)
+{
+  return; // Vorerst!
+/*
+  int   fac;
+  mixed res;
+
+  fac=100;
+
+  if (CannotSee(1))
+    fac -= 50;
+
+  // Weitere Mali?
+
+  if (fac<100)
+    ainfo[SI_SKILLDAMAGE] = ainfo[SI_SKILLDAMAGE]*fac/100;
+  // Fertig
+*/
+}
+
+// Ausfuehren Eines Angriffs auch wenn es bereits einen Angriff
+// in dieser Runde gegeben hat. Dieser Angriff wird auch nicht
+// gezaehlt. Die Nutzung dieser Funktion ist nur fuer Spezialfaelle
+// gedacht und immer BALANCEPFLICHTIG!
+varargs public void ExtraAttack(object enemy, int ignore_previous)
+{
+  int saved_no_more_attacks;
+
+  // Erstschlagsinfo speichern.
+  saved_no_more_attacks = no_more_attacks;
+
+  // Bei Bedarf bisher schon durchgefuehrte Erstschlaege ignorieren
+  if (ignore_previous) no_more_attacks=0;
+
+  // Normalen Angriff durchfuehren
+  Attack (enemy);
+
+  // Gespeicherten Wert zuruecksetzen
+  no_more_attacks = saved_no_more_attacks;
+
+  return;
+}
+
+public void Attack(object enemy)
+{
+  closure cl;
+  mixed   res;
+  mapping ainfo;
+  mapping edefendinfo; // erweiterte Defend-Infos
+  mixed hookData;
+  mixed hookRes;
+
+  if ( no_more_attacks || QueryProp(P_GHOST) || 
+      !objectp(enemy) || !objectp(this_object()) || 
+      (QueryProp(P_DISABLE_ATTACK) > 0) || enemy->QueryProp(P_NO_ATTACK) || 
+      (query_once_interactive(this_object()) && !interactive(this_object())) )
+    return;
+
+  edefendinfo=([]);
+
+  Set(P_LAST_COMBAT_TIME,time());
+
+  // inkrementieren. Diese Variable wird im HB nach den Angriffen genullt.
+  // Diese Variable wird im naechsten Heartbeat von der Anzahl an verfuegbaren
+  // Angriffen des Livings abgezogen. Dies beruecksichtigt also zwischen den
+  // HBs (z.B. extern) gerufene Attacks().
+  no_more_attacks++;
+
+  // Wird das Attack durch einen temporaeren Attack-Hook ersetzt?
+  if ( res=QueryProp(P_TMP_ATTACK_HOOK) )
+  {
+    if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0])
+        && objectp(res[1]) && stringp(res[2]) )
+    {
+      if ( !(res=call_other(res[1],res[2],enemy)) )
+        return;
+    }
+    else
+      SetProp(P_TMP_ATTACK_HOOK,0);
+  }
+
+  // trigger attack hook
+  hookData=({enemy});
+  hookRes=HookFlow(H_HOOK_ATTACK,hookData);
+  if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+    if(hookRes[H_RETCODE]==H_CANCELLED){
+      return;
+    }
+  }
+
+  ainfo = ([ SI_ENEMY : enemy,
+             SI_SPELL : 0,
+           ]);
+
+  if ( objectp(ainfo[P_WEAPON]=QueryProp(P_WEAPON)) )
+  {
+    ainfo[P_WEAPON]->TakeFlaw(enemy);
+
+    // Abfrage fuer den Fall, dass Waffe durch das TakeFlaw() zerstoert
+    // wurde. Dann wird das Attack abgebrochen.
+    if ( !objectp(ainfo[P_WEAPON]) )
+      return;
+
+    cl=symbol_function("name",ainfo[P_WEAPON]);
+    ainfo[SI_SKILLDAMAGE_MSG] = (" mit "+(string)funcall(cl,WEM,0));
+    ainfo[SI_SKILLDAMAGE_MSG2] = (" mit "+(string)funcall(cl,WEM,1));
+
+    ainfo[SI_SKILLDAMAGE] = (int)ainfo[P_WEAPON]->QueryDamage(enemy);
+
+    cl=symbol_function("QueryProp",ainfo[P_WEAPON]);
+    ainfo[SI_SKILLDAMAGE_TYPE] = funcall(cl,P_DAM_TYPE);
+    ainfo[P_WEAPON_TYPE] = (string)funcall(cl,P_WEAPON_TYPE);
+    ainfo[P_NR_HANDS] = (int)funcall(cl,P_NR_HANDS);
+    ainfo[P_WC] = (int)funcall(cl,P_WC);
+
+    // Zweihaendige Waffe?
+    if ( ainfo[P_NR_HANDS]==2
+        && mappingp(res=UseSkill(SK_TWOHANDED,deep_copy(ainfo)))
+        && member(res,SI_SKILLDAMAGE) )
+    {
+      // Nur den neuen Schadenswert uebernehmen.
+      ainfo[SI_SKILLDAMAGE]=((int)(res[SI_SKILLDAMAGE]));
+    }
+  }
+  else // Keine Waffe gezueckt
+  {
+    /* Check if there is a magical attack */
+    if ( mappingp(res=UseSkill(SK_MAGIC_ATTACK,([SI_ENEMY:enemy]))) )
+    {
+      ainfo[SI_SKILLDAMAGE]=(int)res[SI_SKILLDAMAGE];
+      ainfo[SI_SKILLDAMAGE_TYPE]=res[SI_SKILLDAMAGE_TYPE];
+
+      if ( stringp(res[SI_SKILLDAMAGE_MSG]) )
+        ainfo[SI_SKILLDAMAGE_MSG] = " mit "+(string)res[SI_SKILLDAMAGE_MSG];
+      else
+        ainfo[SI_SKILLDAMAGE_MSG] = " mit magischen Faehigkeiten";
+
+      if ( stringp(res[SI_SKILLDAMAGE_MSG2]) )
+        ainfo[SI_SKILLDAMAGE_MSG2] = " mit "+(string)res[SI_SKILLDAMAGE_MSG2];
+      else
+        ainfo[SI_SKILLDAMAGE_MSG2] = (string)ainfo[SI_SKILLDAMAGE_MSG];
+
+      if ( !(ainfo[P_WEAPON_TYPE]=res[P_WEAPON_TYPE]) )
+        ainfo[P_WEAPON_TYPE]=WT_MAGIC;
+
+      if ( member(res,SI_SPELL) )
+        ainfo[SI_SPELL]=res[SI_SPELL];
+    }
+    else
+    {
+      /* Ohne (freie) Haende wird auch nicht angegriffen */
+      if ( interactive(this_object())
+          && (QueryProp(P_USED_HANDS) >= QueryProp(P_MAX_HANDS)) )
+        return ;
+
+      if ( !pointerp(res=QueryProp(P_HANDS)) || (sizeof(res)<2) )
+        return;
+
+      ainfo[SI_SKILLDAMAGE] = (( 2*random(((int)res[1])+1)
+                              + 10*(QueryAttribute(A_STR)) )/3);
+      ainfo[SI_SKILLDAMAGE_TYPE] = res[2];
+      ainfo[SI_SKILLDAMAGE_MSG] = ainfo[SI_SKILLDAMAGE_MSG2]
+                                = ((string)res[0]);
+      ainfo[P_WEAPON_TYPE] = WT_HANDS;
+      ainfo[P_WC] = (int)res[1];
+    }
+  }  // besondere Faehigkeiten mit diesem Waffentyp?
+  if ( mappingp(res=UseSkill(FIGHT(ainfo[P_WEAPON_TYPE]),
+                             deep_copy(ainfo))) )
+    SkillResTransfer(res,ainfo);
+
+  // besondere allgemeine Kampffaehigkeiten?
+  if ( mappingp(res=UseSkill(SK_FIGHT,deep_copy(ainfo))) )
+    SkillResTransfer(res,ainfo);
+
+  // Veraenderungen durch einen Attack-Modifier?
+  if ( (res=QueryProp(P_TMP_ATTACK_MOD)) )
+  {
+    if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0])
+        && (time()<res[0]) && objectp(res[1]) && stringp(res[2]) )
+    {
+      if ( !(res=call_other(res[1],res[2],deep_copy(ainfo)))
+          || !mappingp(res) )
+        return;
+      else
+        SkillResTransfer(res,ainfo);
+    }
+    else
+      SetProp(P_TMP_ATTACK_MOD,0);
+  }
+
+  // trigger attack mod hook
+  hookData=deep_copy(ainfo);
+  hookRes=HookFlow(H_HOOK_ATTACK_MOD,hookData);
+  if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+    if(hookRes[H_RETCODE]==H_CANCELLED){
+      return;
+    }
+    else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] &&
+             mappingp(hookRes[H_RETDATA])){
+        SkillResTransfer(hookRes[H_RETDATA],ainfo);
+    }
+  }
+
+  // Interne Modifikationen der Angriffswerte
+  InternalModifyAttack(ainfo);
+
+  if ( !objectp(enemy) )
+    return;
+
+  // hier mal bewusst nicht auf P_PLURAL zuerst testen. in 90% der Faelle
+  // wird eh keine Angriffsmeldung mehr ausgegeben, also pruefen wir das
+  // lieber zuerst. Plural testen zieht schon genug Rechenzeit :/
+  // Nicht meine Idee! Nicht meine Idee! Nachtwind 7.7.2001
+  if ( ainfo[SI_SKILLDAMAGE_MSG2]!=last_attack_msg )
+  {
+    last_attack_msg = ainfo[SI_SKILLDAMAGE_MSG2];
+    if (QueryProp(P_PLURAL))
+    {
+      tell_object( ME, "  Ihr greift " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+      say("  "+(Name(WER,1))+" greifen "+(enemy->name(WEN,1))+
+          ainfo[SI_SKILLDAMAGE_MSG]+" an.\n", enemy );
+      tell_object( enemy, "  "+(Name(WER,1))+" greifen "+
+                  (enemy->QueryProp(P_PLURAL)?"Euch":"Dich")+
+                   ainfo[SI_SKILLDAMAGE_MSG2]+" an.\n" );
+    }
+    else
+    {
+      tell_object( ME, "  Du greifst " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+      say("  "+(Name(WER,2))+" greift "+(enemy->name(WEN,1))+
+          ainfo[SI_SKILLDAMAGE_MSG]+" an.\n", enemy );
+      tell_object( enemy, "  "+(Name(WER,1))+" greift "+
+                  (enemy->QueryProp(P_PLURAL)?"Euch":"Dich")+
+                   ainfo[SI_SKILLDAMAGE_MSG2]+" an.\n" );    }
+  }
+  else if ( Query(P_SHOW_ATTACK_MSG) )
+    if (QueryProp(P_PLURAL))
+      tell_object( ME, "  Ihr greift " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+    else
+      tell_object( ME, "  Du greifst " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+
+  // ainfo in defendinfo merken
+  edefendinfo[ ORIGINAL_AINFO]= deep_copy(ainfo);
+  edefendinfo[ ORIGINAL_DAM]= ainfo[SI_SKILLDAMAGE];
+  edefendinfo[ ORIGINAL_DAMTYPE]= ainfo[SI_SKILLDAMAGE_TYPE];
+  edefendinfo[ CURRENT_DAM]= ainfo[SI_SKILLDAMAGE];
+  edefendinfo[ CURRENT_DAMTYPE]= ainfo[SI_SKILLDAMAGE_TYPE];
+
+  // ainfo[SI_SPELL] auf ein mapping normieren
+  if ( intp(ainfo[SI_SPELL]) )
+  {
+    ainfo[SI_SPELL] = ([ SP_PHYSICAL_ATTACK : !ainfo[SI_SPELL],
+               SP_SHOW_DAMAGE     : !ainfo[SI_SPELL],
+               SP_REDUCE_ARMOUR   : ([ ])  ]);
+  }
+
+  // defendinfo anhaengen, falls ainfo[SI_SPELL] ein mapping ist
+  if( mappingp(ainfo[SI_SPELL]))
+  {
+    ainfo[SI_SPELL][EINFO_DEFEND]=edefendinfo;
+  }
+
+
+  enemy->Defend(ainfo[SI_SKILLDAMAGE], ainfo[SI_SKILLDAMAGE_TYPE],
+                ainfo[SI_SPELL],       this_object());
+
+
+  //edefendinfo=([]);
+
+  /* Done attacking */
+}
+
+public void AddDefender(object friend)
+{ object *defs;
+
+  if ( !objectp(friend) )
+    return;
+
+  if ( !pointerp(defs=QueryProp(P_DEFENDERS)) )
+    defs=({});
+
+  if ( member(defs,friend)>=0 )
+    return;
+
+  defs+=({friend});
+  SetProp(P_DEFENDERS,defs);
+}
+
+public void RemoveDefender(object friend)
+{ object *defs;
+
+  if ( !objectp(friend) )
+    return;
+
+  if ( !pointerp(defs=QueryProp(P_DEFENDERS)) )
+    defs=({});
+
+  if ( member(defs,friend)==-1 )
+    return;
+
+  defs -= ({friend});
+  SetProp(P_DEFENDERS,defs);
+}
+
+public void InformDefend(object enemy)
+{
+  UseSkill(SK_INFORM_DEFEND,([ SI_ENEMY  : enemy,
+                               SI_FRIEND : previous_object() ]));
+  // Oh, oh - ich hoffe mal, dass InformDefend wirklich NUR aus Defend
+  // eines befreundeten livings aufgerufen wird... (Silvana)
+  // This is only experimental... ;)
+}
+
+public <int|string*|mapping>* DefendOther(int dam, string|string* dam_type,
+                                          int|mapping spell, object enemy)
+{
+  <int|string*|mapping>* res;
+
+  if ( (res=UseSkill(SK_DEFEND_OTHER,([ SI_SKILLDAMAGE : dam,
+                                        SI_SKILLDAMAGE_TYPE : dam_type,
+                                        SI_SPELL : spell,
+                                        SI_FRIEND : previous_object(),
+                                        SI_ENEMY : enemy ])))
+      && pointerp(res) )
+    return res;
+
+  return 0;
+}
+
+public void CheckWimpyAndFlee()
+{
+  if ( (QueryProp(P_WIMPY)>QueryProp(P_HP)) && !TeamFlee()
+      && find_call_out("Flee")<0 )
+    call_out(#'Flee,0,environment());
+}
+
+protected string mess(string msg,object me,object enemy)
+{ closure mname, ename;
+  string  *parts,x;
+  int     i;
+
+  mname = symbol_function("name", me);
+  ename = symbol_function("name", enemy);
+
+  parts=regexplode(msg,"@WE[A-Z]*[12]");
+  for ( i=sizeof(parts)-2 ; i>=1 ; i-=2 )
+  {
+    switch(parts[i])
+    {
+      case "@WER1":    parts[i]=funcall(mname,WER,1);    break;
+      case "@WESSEN1": parts[i]=funcall(mname,WESSEN,1); break;
+      case "@WEM1":    parts[i]=funcall(mname,WEM,1);    break;
+      case "@WEN1":    parts[i]=funcall(mname,WEN,1);    break;
+      case "@WER2":    parts[i]=funcall(ename,WER,1);    break;
+      case "@WESSEN2": parts[i]=funcall(ename,WESSEN,1); break;
+      case "@WEM2":    parts[i]=funcall(ename,WEM,1);    break;
+      case "@WEN2":    parts[i]=funcall(ename,WEN,1);    break;
+      default: ;
+    }
+  }
+
+  return break_string(capitalize(implode(parts,"")),78,"  ",1);
+}
+
+// Fuer evtl. Defend-Aenderungen, ohne dass man gleich das ganze
+// Attack ueberlagern muss:
+protected void InternalModifyDefend(int dam, string* dt, mapping spell, object enemy)
+{
+  return;
+}
+
+public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy)
+{
+  int     i,k;
+  mixed   res,res2;
+  object  *armours,tmp;
+  mixed hookData;
+  mixed hookRes;
+  
+  //  string  what, how;
+  string enname, myname;
+
+  // this_player(), wenn kein enemy bekannt...
+  enemy ||= this_player();
+  // Testen, ob dieses Lebewesen ueberhaupt angegriffen werden darf
+  if ( !this_object() || !enemy || QueryProp(P_NO_ATTACK)
+      || ( query_once_interactive(enemy) && ! interactive(enemy) ) )
+    return 0;
+
+  if ( intp(spell) )
+    spell = ([ SP_PHYSICAL_ATTACK : !spell,
+               SP_SHOW_DAMAGE     : !spell,
+               SP_REDUCE_ARMOUR   : ([ ])  ]);
+  else if ( !mappingp(spell) ) // Illegaler spell-Parameter
+    return 0;
+
+  // testen ob eine erweiterte defendinfo vorhanden ist
+  if(!member(spell,EINFO_DEFEND))
+  {
+          //spell+=([EINFO_DEFEND:([])]); // ggf hinzufuegen
+        // use a temporary mapping to avoid recursive
+        // val[x][y] = deep_copy(val);
+        mapping tmpdefend = ([
+                ORIGINAL_AINFO:deep_copy(spell),
+                ORIGINAL_DAM:dam,
+                  ORIGINAL_DAMTYPE:dam_type,
+        ]);
+          spell[EINFO_DEFEND]=tmpdefend;
+  }
+
+  // Schadenstyp ueberpruefen
+  if ( !pointerp(dam_type) )
+    dam_type = ({ dam_type });
+
+  spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Testen, ob der Angreifer schon als Feind registriert worden ist.
+  // Wenn nein, registrieren.
+  if ( !IsEnemy(enemy) && !spell[SP_NO_ENEMY] )
+  {
+    spell[EINFO_DEFEND][ENEMY_INSERTED]=1;
+    InsertEnemy(enemy);
+  }
+
+  // RFR-Taktik abfangen
+  if ( !QueryProp(P_ENABLE_IN_ATTACK_OUT) )
+  {
+    i=time()-(enemy->QueryProp(P_LAST_MOVE));
+    // Gegner hat sich bewegt, man selbst nicht
+    if ( (i<3) && (time()-QueryProp(P_LAST_MOVE)>=5) )
+    {
+      // Bei Erster Kampfrunde wenige Schaden
+      dam/=(4-i);
+      spell[EINFO_DEFEND][RFR_REDUCE]=dam;
+      spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+    }
+  }
+
+  // Man kann Verteidiger haben. Diese kommen als erste zum Zuge
+  if ( res=QueryProp(P_DEFENDERS) )
+  { object *defs,*defs_here;
+
+    defs=({});
+    defs_here=({});
+    if ( !pointerp(res) )
+      res=({res});
+    // erst alle anwesenden finden.
+    foreach(object defender: res) {
+      if ( objectp(defender) && (member(defs,defender)<0) )
+      {
+        defs+=({defender});
+        //  Verteidiger muessen im gleichen Raum oder im Living selber
+        //  enthalten sein.
+        if ( environment(defender) == environment()
+            || environment(defender) == ME)
+        {
+          call_other(defender,"InformDefend",enemy);
+          if (defender)
+            defs_here += ({ defender });
+        }
+      }
+    }
+    //Anwesende Verteidiger eintragen.
+    spell[EINFO_DEFEND][PRESENT_DEFENDERS]=defs_here;
+
+    // P_DEFENDERS auch gleich aktualisieren
+    if ( sizeof(defs)<1 )
+      defs=0;
+    SetProp(P_DEFENDERS,defs);
+
+    if ( spell[SP_PHYSICAL_ATTACK] ) {
+      // Bei physischen Angriffen nur Verteidiger aus Reihe 1
+      // nehmen (z.B. fuer Rueckendeckung)
+      foreach(object defender: defs_here) {
+        if ( (defender->PresentPosition())>1 ) {
+          defs_here-=({defender});
+        }
+      }
+    }
+ 
+    if ( (i=sizeof(defs_here)) )
+    {
+      mixed edefendtmp=({defs_here[random(i)],0,0,0}); 
+      res=call_other(edefendtmp[DEF_DEFENDER],"DefendOther",
+                     dam,dam_type,spell,enemy);
+      if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0])
+          && pointerp(res[1]))
+      {
+        // Helfer koennen den Schaden oder Schadenstyp aendern,
+        // z.B. Umwandlung von Feuer nach Eis oder so...
+        dam=res[0];
+        edefendtmp[DEF_DAM]=dam;
+        dam_type=res[1];
+        edefendtmp[DEF_DAMTYPE]=dam_type;
+
+        if ( mappingp(res[2]) )
+        {
+          spell=res[2];
+          // teuer, aber geht nicht anders (Rekursion vermeiden)
+          edefendtmp[DEF_SPELL]=deep_copy(res[2]);
+        }
+        spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+        spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+      }
+      spell[EINFO_DEFEND][DEFENDING_DEFENDER]=edefendtmp;
+    }
+  } // Ende Defender-Verarbeitung
+
+
+  // Ueber einen P_TMP_DEFEND_HOOK werden z.B. Schutzzauber gehandhabt
+  spell[EINFO_DEFEND][DEFEND_HOOK]=DI_NOHOOK;
+  if ( res=QueryProp(P_TMP_DEFEND_HOOK) )
+  {
+    if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0])
+        && objectp(res[1]) && stringp(res[2]) )
+    {
+      if ( !(res=call_other(res[1],res[2],dam,dam_type,spell,enemy)) )
+      {
+        // Ein P_TMP_DEFEND_HOOK kann den Schaden vollstaendig abfangen,
+        // das Defend wird dann hier abgebrochen *SICK* und es wird nur
+        // noch getestet, ob man in die Flucht geschlagen wurde
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT;
+        CheckWimpyAndFlee();
+        return 0;
+      }
+      else
+      {
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK;
+        if ( pointerp(res) && (sizeof(res)>=3)
+            && intp(res[0] && pointerp(res[1])) )
+        {
+          mixed edefendtmp=({0,0,0});
+          // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und
+          // -art sowie die Spell-Infos veraendern
+          dam=res[0];
+          edefendtmp[HOOK_DAM]=dam;
+          dam_type=res[1];
+          edefendtmp[HOOK_DAMTYPE]=dam_type;
+
+          if ( mappingp(res[2]) )
+          {
+            spell=res[2];
+            // Waeh. Teuer. Aber geht nicht anders.
+            edefendtmp[HOOK_SPELL]=deep_copy(spell);
+          }
+          spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp;
+          spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+          spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+        }
+      }
+    }
+    else
+      SetProp(P_TMP_DEFEND_HOOK,0);
+  } // P_TMP_DEFEND_HOOK
+
+  // trigger defend hook
+  hookData=({dam,dam_type,spell,enemy});
+  hookRes=HookFlow(H_HOOK_DEFEND,hookData);
+  if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+    if(hookRes[H_RETCODE]==H_CANCELLED){
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT;
+        CheckWimpyAndFlee();
+        return 0;
+    }
+    else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA]){
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK;
+        if ( pointerp(hookRes[H_RETDATA]) && (sizeof(hookRes[H_RETDATA])>=3)
+            && intp(hookRes[H_RETDATA][0] && pointerp(hookRes[H_RETDATA][1])) )
+        {
+          mixed edefendtmp=({0,0,0});
+          // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und
+          // -art sowie die Spell-Infos veraendern
+          dam=hookRes[H_RETDATA][0];
+          edefendtmp[HOOK_DAM]=dam;
+          dam_type=hookRes[H_RETDATA][1];
+          edefendtmp[HOOK_DAMTYPE]=dam_type;
+
+          if ( mappingp(hookRes[H_RETDATA][2]) )
+          {
+            spell=hookRes[H_RETDATA][2];
+            // Teuer, teuer... :-(
+            edefendtmp[HOOK_SPELL]=deep_copy(spell);
+          }
+          spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp;
+          spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+          spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+        }
+    }
+  } // Ende Hook-Behandlung
+
+  if ( !member(spell,SP_REDUCE_ARMOUR) || !mappingp(spell[SP_REDUCE_ARMOUR]) )
+        spell[SP_REDUCE_ARMOUR] = ([]);
+
+  // Es gibt auch Parierwaffen,
+  if ( objectp(tmp=QueryProp(P_PARRY_WEAPON)) )
+  {
+    res2=tmp->QueryDefend(dam_type, spell, enemy);
+
+    // Reduzierbare Wirksamkeit der Parierwaffe?
+    if ( member(spell[SP_REDUCE_ARMOUR],P_PARRY_WEAPON)
+        && intp(res=spell[SP_REDUCE_ARMOUR][P_PARRY_WEAPON]) && (res>=0) )
+    {
+      res2=(res2*res)/100;
+    }
+
+    dam-=res2;
+    spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+  }
+
+  // Jetzt kommen die Ruestungen des Lebewesens ins Spiel (wenn es denn
+  // welche traegt)
+
+  armours=QueryProp(P_ARMOURS)-({0});
+  if ( (i=sizeof(armours))>0 ) { 
+    string aty;
+
+    tmp=armours[random(i)];
+
+    if ( objectp(tmp) )
+      //Uebergabe des Mappings eh als Pointer/Referenz, daher billig
+      tmp->TakeFlaw(dam_type,spell[EINFO_DEFEND]);
+
+    // pro Ruestung ein Key an Platz reservieren
+    spell[EINFO_DEFEND][DEFEND_ARMOURS]=m_allocate(i,2);
+    foreach(object armour : armours) {
+      if ( objectp(armour) ) {
+        aty=(string)armour->QueryProp(P_ARMOUR_TYPE);
+
+        if ( member(spell[SP_REDUCE_ARMOUR],aty)
+            && intp(res2=spell[SP_REDUCE_ARMOUR][aty]) && (res2>=0) )
+          dam -= (res2*armour->QueryDefend(dam_type, spell, enemy))/100;
+        else
+          dam -= (int)(armour->QueryDefend(dam_type, spell, enemy));
+        // Schaden NACH DefendFunc vermerken. 
+        // Schutzwirkung VOR DefendFunc (DEF_ARMOUR_PROT) vermerkt
+        // das QueryDefend() selber.
+        spell[EINFO_DEFEND][DEFEND_ARMOURS][armour,DEF_ARMOUR_DAM]=dam;
+        // akt. Schaden vermerken und Schutz der aktuellen Ruestung (vor
+        // DefendFunc) wieder auf 0 setzen fuer den naechsten Durchlauf.
+        spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+        spell[EINFO_DEFEND][DEFEND_CUR_ARMOUR_PROT]=0;
+      }
+    }
+  }
+
+  // Manche Gilden haben einen Verteidigungsskill. Der kommt jetzt zum
+  // tragen. Der Skill kann die Schadenshoehe und -art beeinflussen.
+  spell[EINFO_DEFEND]+=([DEFEND_GUILD:({})]);
+  if ( mappingp(res=UseSkill(SK_MAGIC_DEFENSE,
+                            ([ SI_ENEMY            : enemy,
+                               SI_SKILLDAMAGE      : dam,
+                               SI_SKILLDAMAGE_TYPE : dam_type,
+                               SI_SPELL            : spell     ]))) )
+  {
+    dam=(int)res[SI_SKILLDAMAGE];
+
+    if ( pointerp(res[SI_SKILLDAMAGE_TYPE]) )
+        dam_type=res[SI_SKILLDAMAGE_TYPE];
+
+    spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+    spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+    spell[EINFO_DEFEND][DEFEND_GUILD]=({dam,dam_type});
+  }
+
+  // Evtl. interne Modifikationen
+  InternalModifyDefend(&dam, &dam_type, &spell, &enemy);
+
+  spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Testen, ob irgendwas im Inventory des Lebewesen auf den Angriff
+  // "reagiert"
+  CheckSensitiveAttack(dam,dam_type,spell,enemy);
+
+  if ( !objectp(enemy) )
+    return 0;
+
+  // Angriffszeit im Gegner setzen
+  enemy->SetProp(P_LAST_COMBAT_TIME,time());
+
+  // Die Resistenzen des Lebewesens (natuerliche und durch Ruestungen
+  // gesetzte) beruecksichtigen
+  dam = to_int(CheckResistance(dam_type)*dam);
+  spell[EINFO_DEFEND][DEFEND_RESI]=dam;
+
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Bei physikalischen Angriffen wird die natuerliche Panzerung und die
+  // Geschicklichkeit des Lebewesens beruecksichtigt
+  object stat = find_object("/d/erzmagier/zesstra/pacstat"); // TODO: remove
+  if ( spell[SP_PHYSICAL_ATTACK] )
+  {
+    // Minimum ist auch hier 1.
+    int body = QueryProp(P_BODY)+QueryAttribute(A_DEX);
+    res2 = (body/4 + random(body*3/4 + 1)) || 1;
+    if (stat)
+      stat->bodystat(body, res2, random(body)+1);
+
+    // Reduzierbare Wirksamkeit des Bodies?
+    if ( member(spell[SP_REDUCE_ARMOUR], P_BODY)
+        && intp(res=spell[SP_REDUCE_ARMOUR][P_BODY]) && (res>=0) )
+      res2=(res2*res)/100;
+
+    dam-=res2;
+  }
+  spell[EINFO_DEFEND][DEFEND_BODY]=dam;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Ist ueberhaupt noch etwas vom Schaden uebrig geblieben?
+  if ( dam<0 )
+    dam = 0;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // fuer die Statistik
+  // TODO: entfernen nach Test-Uptime
+  if (stat)
+    stat->damagestat(spell[EINFO_DEFEND]);
+
+  // Die Anzahl der abzuziehenden Lebenspunkte ist der durch 10 geteilte
+  // Schadenswert
+  dam = dam / 10;
+  spell[EINFO_DEFEND][DEFEND_LOSTLP]=dam;
+
+  // evtl. hat entweder der Aufrufer oder das Lebewesen selber eigene
+  // Schadensmeldungen definiert. Die vom Aufrufer haben Prioritaet.
+  mixed dam_msg = spell[SP_SHOW_DAMAGE];
+  // Wenn != 0 (d.h. Treffermeldungen grundsaetzlich erwuenscht), aber kein
+  // Array, hier im Living fragen.
+  if (dam_msg && !pointerp(dam_msg))
+    dam_msg = QueryProp(P_DAMAGE_MSG);
+
+  // In den meisten Faellen soll auch eine Schadensmeldung ausgegeben
+  // werden, die die Hoehe des Schadens (grob) anzeigt
+  if (spell[SP_SHOW_DAMAGE] && !pointerp(dam_msg)) {
+    myname=name(WEN);
+    enname=enemy->Name(WER);
+    if (enemy->QueryProp(P_PLURAL)) {
+      switch (dam) {
+      case 0:
+        tell_object(enemy,sprintf("  Ihr verfehlt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s verfehlen Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+           sprintf("  %s verfehlen %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 1:
+        tell_object(enemy,sprintf("  Ihr kitzelt %s am Bauch.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s kitzeln Dich am Bauch.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kitzeln %s am Bauch.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 2..3:
+        tell_object(enemy,sprintf("  Ihr kratzt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s kratzen Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kratzen %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 4..5:
+        tell_object(enemy,sprintf("  Ihr trefft %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s treffen Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s treffen %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 6..10:
+        tell_object(enemy,sprintf("  Ihr trefft %s hart.\n",myname));
+        tell_object(this_object(),sprintf("  %s treffen Dich hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s treffen %s hart.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 11..20:
+        tell_object(enemy,sprintf("  Ihr trefft %s sehr hart.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s treffen Dich sehr hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s treffen %s sehr hart.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 21..30:
+        tell_object(enemy,
+          sprintf("  Ihr schlagt %s mit dem Krachen brechender Knochen.\n",
+                  myname));
+        tell_object(this_object(),
+          sprintf("  %s schlagen Dich mit dem Krachen brechender Knochen.\n",
+                  enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlagen %s mit dem Krachen brechender Knochen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 31..50:
+        tell_object(enemy,
+          sprintf("  Ihr zerschmettert %s in kleine Stueckchen.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s zerschmettern Dich in kleine Stueckchen.\n",
+                                          enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerschmettern %s in kleine Stueckchen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 51..75:
+        tell_object(enemy,sprintf("  Ihr schlagt %s zu Brei.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s schlagen Dich zu Brei.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlagen %s zu Brei.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 76..100:
+        tell_object(enemy,sprintf("  Ihr pulverisiert %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s pulverisieren Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s pulverisieren %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 101..150:
+        tell_object(enemy,sprintf("  Ihr zerstaeubt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s zerstaeuben Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerstaeuben %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 151..200:
+        tell_object(enemy,sprintf("  Ihr atomisiert %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s atomisieren Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s atomisieren %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      default:
+        tell_object(enemy,sprintf("  Ihr vernichtet %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s vernichten Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s vernichten %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      }
+
+    }
+    else {
+      switch (dam) {
+      case 0:
+        tell_object(enemy,sprintf("  Du verfehlst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s verfehlt Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s verfehlt %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 1:
+        tell_object(enemy,sprintf("  Du kitzelst %s am Bauch.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s kitzelt Dich am Bauch.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kitzelt %s am Bauch.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 2..3:
+        tell_object(enemy,sprintf("  Du kratzt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s kratzt Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kratzt %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 4..5:
+        tell_object(enemy,sprintf("  Du triffst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s trifft Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s trifft %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 6..10:
+        tell_object(enemy,sprintf("  Du triffst %s hart.\n",myname));
+        tell_object(this_object(),sprintf("  %s trifft Dich hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s trifft %s hart.\n",enname,myname),
+            ({ enemy, this_object() }));
+        break;
+      case 11..20:
+        tell_object(enemy,sprintf("  Du triffst %s sehr hart.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s trifft Dich sehr hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s trifft %s sehr hart.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 21..30:
+        tell_object(enemy,
+          sprintf("  Du schlaegst %s mit dem Krachen brechender Knochen.\n",
+                  myname));
+        tell_object(this_object(),
+          sprintf("  %s schlaegt Dich mit dem Krachen brechender Knochen.\n",
+                  enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlaegt %s mit dem Krachen brechender Knochen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 31..50:
+        tell_object(enemy,
+          sprintf("  Du zerschmetterst %s in kleine Stueckchen.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s zerschmettert Dich in kleine Stueckchen.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerschmettert %s in kleine Stueckchen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 51..75:
+        tell_object(enemy,sprintf("  Du schlaegst %s zu Brei.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s schlaegt Dich zu Brei.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlaegt %s zu Brei.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 76..100:
+        tell_object(enemy,sprintf("  Du pulverisierst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s pulverisiert Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s pulverisiert %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 101..150:
+        tell_object(enemy,sprintf("  Du zerstaeubst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s zerstaeubt Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerstaeubt %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 151..200:
+        tell_object(enemy,sprintf("  Du atomisierst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s atomisiert Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s atomisiert %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      default:
+        tell_object(enemy,sprintf("  Du vernichtest %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s vernichtet Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s vernichtet %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      }
+    }
+  }
+
+  // Man kann auch selbst-definierte Schadensmeldungen ausgeben lassen
+  else if( spell[SP_SHOW_DAMAGE] && pointerp(dam_msg) )
+  {
+    for( i=sizeof(dam_msg) ; --i >= 0 ; )
+    {
+      if ( dam>dam_msg[i][0] )
+      {
+        tell_object(ME,mess(dam_msg[i][1],ME,enemy));
+        tell_object(enemy,mess(dam_msg[i][2],ME,enemy));
+        say(mess(dam_msg[i][3],ME,enemy), enemy);
+        break;
+      }
+    }
+  }
+  // else (!spell[SP_SHOW_DAMAGE]) keine Schadensmeldung.
+
+  // Informationen ueber den letzten Angriff festhalten
+  Set(P_LAST_DAMTYPES, dam_type);
+  Set(P_LAST_DAMTIME,  time());
+  Set(P_LAST_DAMAGE,   dam);
+
+  // Bei Angriffen mit SP_NO_ENEMY-Flag kann man nicht sterben ...
+  if ( spell[SP_NO_ENEMY] )
+    reduce_hit_points(dam);
+  // ... bei allen anderen natuerlich schon
+  else
+    do_damage(dam,enemy);
+
+  // evtl. ist dies Objekt hier tot...
+  if (!objectp(ME)) return dam;
+
+  // Testen, ob man in die Fucht geschlagen wird
+  CheckWimpyAndFlee();
+
+  // Verursachten Schaden (in LP) zurueckgeben
+  return dam;
+}
+
+public float CheckResistance(string *dam_type) {
+    //funktion kriegt die schadensarten uebergeben, schaut sich dieses
+    //sowie P_RESISTENCE_STRENGTH und P_RESITENCE_MODFIFIER an und berechnet
+    //einen Faktor, mit dem die urspruengliche Schadensmenge multipliziert
+    //wird, um den Schaden nach Beruecksichtigung der Resis/Anfaelligkeiten zu
+    //erhalten. Rueckgabewert normalerweise (s.u.) >=0.
+  mapping rstren, mod;
+  float   faktor,n;
+  int     i;
+
+  mod = Query(P_RESISTANCE_MODIFIER);
+  if ( mappingp(mod) )
+    mod = mod["me"];
+
+  if ( !mappingp(rstren=Query(P_RESISTANCE_STRENGTHS)) )
+  {
+    if (!mappingp(mod))
+      return 1.0;
+    else
+      rstren = ([]);
+  }
+
+  if ( !mappingp(mod) )
+    mod = ([]);
+
+  if ( (i=sizeof(dam_type))<1 )
+    return 1.0;
+
+  n=to_float(i);
+  faktor=0.0;
+
+  //dies hier tut nicht mehr das gewuenschte, wenn in P_RESISTENCE_STRENGTHS
+  //Faktoren <-1.0 angegeben werden. Rueckgabewerte <0 sind dann moeglich und
+  //leider werden die Schadensarten ohne Resis nicht mehr richtig verwurstet. :-/
+  foreach(string dt: dam_type) {
+    faktor = faktor + (1.0 + to_float(rstren[dt]))
+           * (1.0 + to_float(mod[dt]))-1.0;
+  }
+  return 1.0+(faktor/n);
+}
+
+public varargs mapping StopHuntingMode(int silent)
+{ mapping save_enemy;
+  int     i;
+
+  save_enemy=enemies;
+  if ( !silent )
+    walk_mapping(enemies, #'StopHuntText); //#');
+
+  enemies=m_allocate(0,1);
+  last_attack_msg=0;
+
+  return save_enemy;
+}
+
+public <object*|int*>* QueryEnemies()
+{
+  return ({m_indices(enemies),m_values(enemies)});
+}
+
+public mapping GetEnemies()
+{
+  return enemies;
+}
+
+public mapping SetEnemies(<object*|int*>* myenemies)
+{
+  enemies=mkmapping(myenemies[0],myenemies[1]);
+  return enemies;
+}
+
+private string _kill_alias( string str )
+{
+    return "\\" + str;
+}
+
+public varargs void Flee( object oldenv, int force )
+{ mixed   *exits, exit, dex;
+  mapping tmp;
+  int     i;
+  object  env;
+
+  if ( !environment() )
+    return;
+
+  // mit 'force' kann man die Checks umgehen, damit der Spieler auf jeden
+  // Fall fluechtet ...
+  if ( !force && ( oldenv && (oldenv != environment()) ||
+                   query_once_interactive(ME) && (!EnemyPresent() ||
+                   (QueryProp(P_HP) >= QueryProp(P_WIMPY))) ) )
+    return;
+
+  // ... aber Magier fluechten zu lassen ist nicht ganz so einfach ;-)
+  if ( query_once_interactive(this_object()) && IS_LEARNING(this_object()) )
+    return;
+
+  // Geister brauchen nicht um ihr Leben zu fuerchten
+  if ( QueryProp(P_GHOST) )
+    return;
+
+  tell_object( ME, "Die Angst ist staerker als Du ... "+
+                   "Du willst nur noch weg hier.\n");
+
+  if ( TeamFlee() ) // Erfolgreiche Flucht in hintere Kampfreihe?
+    return;
+
+  env = environment();
+  tmp = environment()->QueryProp(P_EXITS);
+  exits = m_indices(tmp);
+  tmp = environment()->QueryProp(P_SPECIAL_EXITS);
+  exits += m_indices(tmp);
+
+  if ( query_once_interactive(ME) )
+        exits = map( exits, #'_kill_alias/*'*/ );
+
+  // die Fluchtrichtung von Magiern wird aus Sicherheitsgruenden
+  // nicht ausgewertet
+  if ( interactive(ME) && IS_SEER(ME) && !IS_LEARNER(ME)
+      && (dex=QueryProp(P_WIMPY_DIRECTION)) )
+  {
+    i = 60 + 4 * (QueryProp(P_LEVEL) - 30) / 3;
+    exits += ({dex}); // bevorzugte Fluchtrichtung mindestens einmal
+  }
+
+  if ( !sizeof(exits) )
+  {
+    tell_object( ME, "Du versuchst zu fliehen, schaffst es aber nicht.\n" );
+
+    return;
+  }
+
+  while ( sizeof(exits) && (environment()==env) )
+  {
+    if ( dex                         // Vorzugsweise Fluchtrichtung?
+        && (member(exits,dex) >= 0)  // moeglich?
+        && (random(100) <= i))       // und Wahrscheinlichkeit gross genug?
+      exit = dex;
+    else
+      exit = exits[random(sizeof(exits))];
+
+    catch(command(exit);publish);
+    exits -= ({exit});
+  }
+
+  if ( environment()==env )
+    tell_object( ME, "Dein Fluchtversuch ist gescheitert.\n" );
+}
+
+public object EnemyPresent()
+{
+  foreach(object en: enemies) {
+    if (environment()==environment(en))
+      return en;
+  }
+  return 0;
+}
+
+public object InFight()
+{
+  return EnemyPresent();
+}
+
+public varargs int StopHuntID(string str, int silent) { 
+
+  if ( !stringp(str) )
+    return 0;
+
+  int j;
+  foreach(object en: enemies) {
+    if (en->id(str)) {
+      StopHuntFor(en,silent);
+      j++;
+    }
+  }
+
+  return j;
+}
+
+public int SpellDefend(object caster, mapping sinfo)
+{ int    re;
+  mixed  res;
+  string *ind;
+
+  re = UseSkill(SK_SPELL_DEFEND,([ SI_SKILLARG : sinfo ,
+                                   SI_ENEMY    : caster ]) );
+
+  if ( (res=QueryProp(P_MAGIC_RESISTANCE_OFFSET)) && mappingp(res)
+      && pointerp(sinfo[SI_MAGIC_TYPE]))
+  {
+    ind = m_indices(res) & sinfo[SI_MAGIC_TYPE];
+
+    if (pointerp(ind) && sizeof(ind) ) {
+      foreach(string index : ind)
+        re+=res[index];
+    }
+  }
+  else if(res && intp(res))
+    re+=res;
+
+  if ( (re>3333) && query_once_interactive(this_object()) )
+    re=3333; /* Maximal 33% Abwehrchance bei Spielern */
+  return re;
+}
+
+// **** this is property-like
+
+static int _set_attack_busy(mixed val)
+{
+  if ( ((to_int(val))>5) && previous_object(1)
+      && query_once_interactive(previous_object(1)) )
+    log_file("ATTACKBUSY",sprintf("%s %d Taeter: %O Opfer: %O\n",
+             dtime(time()),to_int(val),previous_object(1),this_object()));
+
+  attack_busy-=to_int(val*100.0);
+
+  if ( attack_busy<-2000 )
+    attack_busy=-2000;
+
+  return attack_busy;
+}
+
+static int _query_attack_busy()
+{
+  if (IS_LEARNING(ME))
+    return 0;
+
+  return (attack_busy<100);
+}
+
+// **** local property methods
+static int _set_wimpy(int i)
+{
+  if ( !intp(i) || (i>QueryProp(P_MAX_HP)) || (i<0) )
+    return 0;
+
+  // ggf. Statusreport ausgeben
+  if (interactive(ME))
+    status_report(DO_REPORT_WIMPY, i);
+
+  return Set(P_WIMPY, i);
+}
+
+static string _set_wimpy_dir(string s) {
+  // ggf. Statusreport ausgeben
+  if (interactive(ME))
+    status_report(DO_REPORT_WIMPY_DIR, s);
+  return Set(P_WIMPY_DIRECTION, s, F_VALUE);
+}
+
+static mixed _set_hands(mixed h)
+{ 
+  if ( sizeof(h)==2 )
+    h += ({ ({DT_BLUDGEON}) });
+  if (!pointerp(h[2]))
+    h[2] = ({h[2]});
+  return Set(P_HANDS, h, F_VALUE);
+}
+
+//TODO normalisieren/korrigieren in updates_after_restore().
+static mixed _query_hands()
+{
+  mixed *hands = Query(P_HANDS);
+  if ( !hands )
+    return Set(P_HANDS, ({ " mit blossen Haenden", 30, ({DT_BLUDGEON})}));
+  else if ( sizeof(hands)<3 )
+    return Set(P_HANDS, ({hands[0], hands[1], ({DT_BLUDGEON})}));
+  else if ( !pointerp(hands[2]) )
+    return Set(P_HANDS, ({hands[0], hands[1], ({ hands[2] })}));
+
+  return Query(P_HANDS);
+}
+
+static int _query_total_wc()
+{ mixed res;
+  int   totwc;
+
+  if ( objectp(res=QueryProp(P_WEAPON)) )
+    totwc = ((int)(res->QueryProp(P_WC)));
+  else if ( pointerp(res=QueryProp(P_HANDS)) && sizeof(res)>1
+           && intp(res[1]) )
+    totwc=((int)res[1]);
+  else
+    totwc=30;
+
+  totwc = ((2*totwc)+(10*QueryAttribute(A_STR)))/3;
+
+  return Set(P_TOTAL_WC, totwc, F_VALUE);
+}
+
+static int _query_total_ac() {
+
+  int totac = 0;
+  object *armours = QueryProp(P_ARMOURS);
+  object parry = QueryProp(P_PARRY_WEAPON);
+
+  if ( member(armours,0)>=0 ) {
+    armours -= ({ 0 }); 
+  }
+
+  foreach(object armour: armours)
+    totac += ((int)armour->QueryProp(P_AC));
+
+  if ( objectp(parry) )
+    totac += parry->QueryProp(P_AC);
+
+  totac += (QueryProp(P_BODY)+QueryAttribute(A_DEX));
+
+  return Set(P_TOTAL_AC, totac, F_VALUE);
+}
+
+static mapping _query_resistance_strengths() {
+
+  UpdateResistanceStrengths();
+
+  mapping rstren = copy(Query(P_RESISTANCE_STRENGTHS, F_VALUE));
+  mapping mod = Query(P_RESISTANCE_MODIFIER, F_VALUE);
+
+  if ( !mappingp(rstren) )
+    rstren = ([]);
+
+  if ( !mappingp(mod) || !mappingp(mod=mod["me"]) || !sizeof(mod) )
+    return rstren;
+
+  foreach(string modkey, float modvalue : mod)
+    rstren[modkey] = ((1.0+rstren[modkey])*(1.0+modvalue))-1.0;
+
+  return rstren;
+}
+
+static int _set_disable_attack(int val)
+{
+  if (val<-100000)
+           {
+                  log_file("PARALYSE_TOO_LOW",
+                          sprintf("Wert zu klein: %s, Wert: %d, "
+                                  "Aufrufer: %O, Opfer: %O",
+                                  ctime(time()),
+                                          val,previous_object(1),
+                                          this_object()));
+           }
+  if ( val>30 )
+    val=30;
+  if ( (val>=20) && previous_object(1)!=ME && query_once_interactive(ME) )
+    log_file("PARALYSE",sprintf("%s %d Taeter: %O Opfer: %O\n",
+                                ctime(time())[4..15],
+                                val,previous_object(1),this_object()));
+ 
+  if (time()<QueryProp(P_NEXT_DISABLE_ATTACK))
+  {
+    // gueltige Zeitsperre existiert.
+    // Erhoehen von P_DISABLE_ATTACK geht dann nicht. (Ja, auch nicht erhoehen
+    // eines negativen P_DISABLE_ATTACK)
+    if (val >= QueryProp(P_DISABLE_ATTACK))
+      return DISABLE_TOO_EARLY;
+    // val ist kleiner als aktuelles P_DISABLE_ATTACK - das ist erlaubt, ABER es
+    // darf die bestehende Zeitsperre nicht verringern, daher wird sie nicht
+    // geaendert.
+    return Set(P_DISABLE_ATTACK,val,F_VALUE);
+  }
+  // es existiert keine gueltige Zeitsperre - dann wird sie nun gesetzt.
+  // (Sollte val < 0 sein, wird eine Zeitsperre in der Vergangenheit gesetzt,
+  // die schon abgelaufen ist. Das ist ueberfluessig, schadet aber auch
+  // nicht.)
+  SetProp(P_NEXT_DISABLE_ATTACK,time()+val*2*__HEART_BEAT_INTERVAL__);
+  return Set(P_DISABLE_ATTACK,val);
+}
+
+// Neue Verwaltung der Haende:
+
+// P_HANDS_USED_BY enhaelt ein Array mit allen Objekten, die Haende
+// belegen, jedes kommt so oft vor wie Haende belegt werden.
+
+static mixed *_query_hands_used_by()
+{
+  return ((Query(P_HANDS_USED_BY, F_VALUE) || ({}) ) - ({0}));
+}
+
+static int _query_used_hands()
+{
+  return sizeof(QueryProp(P_HANDS_USED_BY));
+}
+
+static int _query_free_hands()
+{
+  return (QueryProp(P_MAX_HANDS)-QueryProp(P_USED_HANDS));
+}
+
+public varargs int UseHands(object ob, int num)
+{ mixed *h;
+
+  if ( !ob && !(ob=previous_object(1)) )
+    return 0;
+
+  if ( (num<=0) && ((num=ob->QueryProp(P_HANDS))<=0) )
+    return 0;
+
+  h=QueryProp(P_HANDS_USED_BY)-({ob});
+
+  if ( (sizeof(h)+num)>QueryProp(P_MAX_HANDS) )
+    return 0;
+
+  foreach(int i: num)
+    h+=({ob});
+
+  SetProp(P_HANDS_USED_BY,h);
+
+  return 1;
+}
+
+public varargs int FreeHands(object ob)
+{
+  if ( !ob && !(ob=previous_object(1)) )
+    return 0;
+
+  SetProp(P_HANDS_USED_BY,QueryProp(P_HANDS_USED_BY)-({ob}));
+
+  return 1;
+}
+
+// Kompatiblitaetsfunktionen:
+
+static int _set_used_hands(int new_num)
+{ int    old_num, dif;
+  object ob;
+
+  old_num=QueryProp(P_USED_HANDS);
+
+  if ( !objectp(ob=previous_object(1)) )
+    return old_num;
+
+  // Meldung ins Debuglog schreiben. Aufrufer sollte gefixt werden.
+  debug_message(sprintf("P_USED_HANDS in %O wird gesetzt durch %O\n",
+        this_object(), ob), DMSG_LOGFILE|DMSG_STAMP);
+
+  if ( !(dif=new_num-old_num) )
+    return new_num;
+
+  if ( dif>0 )
+    UseHands(ob,dif);
+  else
+    FreeHands(ob);
+
+  return QueryProp(P_USED_HANDS);
+}
+
+// Funktionen fuer Begruessungsschlag / Nackenschlag:
+
+public int CheckEnemy(object ob)
+{
+  return (living(ob) && IsEnemy(ob));
+}
+
+public varargs void ExecuteMissingAttacks(object *remove_attackers)
+{
+  if ( !pointerp(missing_attacks) )
+    missing_attacks=({});
+
+  if ( pointerp(remove_attackers) )
+    missing_attacks-=remove_attackers;
+
+  foreach(object ob : missing_attacks) {
+    if ( objectp(ob) && (environment(ob)==environment()) )
+      ob->Attack2(ME);
+  }
+  missing_attacks=({});
+}
+
+public void InitAttack()
+{ object ob,next;
+  closure cb;
+
+  if ( !living(ME) )
+    return;
+
+  ExecuteMissingAttacks();
+  //EMA kann das Living zerstoeren oder toeten...
+  if (!living(ME) || QueryProp(P_GHOST)) return;
+
+  if ( objectp(ob=IsTeamMove()) )
+    cb=symbol_function("InitAttack_Callback",ob);
+  else
+    cb=0;
+
+  for ( ob=first_inventory(environment()) ; objectp(ob) ; ob=next)
+  {
+    next=next_inventory(ob);
+
+    if ( !living(ob) )
+      continue;
+
+    if (ob->IsEnemy(ME))
+    {
+      // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
+      // aktualisiert und b) werden Teammitglieder von mir bei diesem
+      // InsertEnemy() ggf. erfasst.
+      ob->InsertEnemy(ME);
+
+      if ( closurep(cb) && funcall(cb,ob) ) // Wird ganzes Team gemoved?
+        missing_attacks += ({ ob }); // Dann erstmal warten bis alle da sind
+      else
+        ob->Attack2(ME);
+
+    }
+    else if ( IsEnemy(ob) )
+    {
+      // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
+      // aktualisiert und b) werden Teammitglieder von ob bei diesem 
+      // InsertEnemy() ggf. erfasst.
+      InsertEnemy(ob);
+      Attack2(ob);
+    }
+    //Attack2 kann dieses Objekt zerstoeren oder toeten. Wenn ja: abbruch
+    if ( !living(ME) || QueryProp(P_GHOST)) break;
+  }
+}
+
+public void ExitAttack()
+{
+  if ( !living(ME) )
+    return;
+
+  // Noch nachzuholende Begruessungsschlaege:
+  ExecuteMissingAttacks();
+}
+
+public object|object*|mapping QueryArmourByType(string type)
+{
+  // Rueckgabewert:
+  // DIE Ruestung vom Typ <type>, falls <type> nicht AT_MISC,
+  // Array aller AT_MISC-Ruestungen falls <type> AT_MISC (auch leer),
+  // Mapping mit allen oben genannten Infos, falls <type> Null
+
+  object *armours;
+  string typ2;
+
+  // Wenn Cache vorhanden, dann Cache liefern.
+  if (mappingp(QABTCache)) {
+    if (type == AT_MISC)
+      return QABTCache[AT_MISC] - ({0});
+    else if (type)
+      return QABTCache[type];
+    else
+      return copy(QABTCache);
+  }
+
+  if ( !pointerp(armours=QueryProp(P_ARMOURS)) )
+    armours=({});
+
+  // Cache erzeugen
+  QABTCache = ([ AT_MISC: ({}) ]);
+  foreach(object ob: armours - ({0}) ) {
+    if ( !stringp(typ2=ob->QueryProp(P_ARMOUR_TYPE)) )
+      continue;
+    if ( typ2==AT_MISC )
+      QABTCache[AT_MISC] += ({ob});
+    else
+      QABTCache[typ2] = ob;
+  }
+  // Und gewuenschtes Datum liefern.
+  if (type)
+    return QABTCache[type];
+  else
+    return copy(QABTCache);
+}
+
+//TODO: langfristig waers besser, wenn hier nicht jeder per SetProp() drauf
+//los schreiben wuerde.
+static object *_set_armours(object *armours) {
+  if (pointerp(armours)) {
+    // Cache wegwerfen
+    QABTCache = 0;
+    // armours ggf. unifizieren. Ausserdem Kopie reinschreiben.
+    return Set(P_ARMOURS, m_indices(mkmapping(armours)), F_VALUE);
+  }
+  return QueryProp(P_ARMOURS);
+}
+
+/** Reduziert die Befriedezaehler pro Reset im Durchschnitt um 2.5.
+  Berechnet ganzzahlige durchschnittliche Resets seit dem letzten Expire und
+  erhoeht die letzte Expirezeit um Resets*__RESET_TIME__ (Standard Intervall
+  fuer Reset ist momentan 3600s, im Durchschnitt kommen dann 2700 zwischen 2
+  Resets bei raus). So wird der unganzzahlige Rest beim naechsten Aufruf
+  beruecksichtigt.
+  Diese Variante des Expires wurde gewaehlt, um zu vermeiden, combat.c einen
+  reset() zu geben und in jedem Reset in jedem Lebewesen ein Expire machen zu
+  muessen, auch wenn nur in sehr wenigen Lebewesen was gemacht werden muss.
+  @param[in,out] ph Mapping aus P_PEACE_HISTORY. Wird direkt aktualisiert.
+  @attention Muss ein gueltiges P_PEACE_HISTORY uebergeben bekommen, anderem
+  Datentyp inkl. 0 wird die Funktion buggen.
+  */
+private void _decay_peace_history(mixed ph) {
+  // Ganzzahlige resets seit letztem Expire ermitteln. (Durchschnitt)
+  int resets = (time() - ph[0]) / (__RESET_TIME__*75/100);
+  // auf Zeitstempel draufrechnen, damit der unganzzahlige Rest nicht
+  // verlorengeht, der wird beim naechsten Expire dann beruecksichtigt.
+  ph[0] += resets * (__RESET_TIME__ * 75 / 100);
+  // pro Reset werden im Durchschnitt 2.5 Versuche abgezogen. (Hier werden
+  // beim Expire mal 2 und mal 3 Versuche pro Reset gerechnet. Aber falls hier
+  // viele Resets vergangen sind, ist es vermutlich eh egal, weil die Counter
+  // auf 0 fallen.)
+  int expire = resets * (random(2) + 2);
+  // ueber alle Gilden
+  mapping tmp=ph[1];
+  foreach(string key, int count: &tmp ) {
+    count-=expire;
+    if (count < 0) count = 0;
+  }
+}
+
+/** Pacify() dient zur Bestimmung, ob sich ein Lebewesen gerade 
+ * befrieden lassen will.
+ * Berechnet eine Wahrscheinlichkeit nach unten stehender Formel, welche die
+ * Intelligenz dieses Lebenwesens, die Intelligenz des Casters und die
+ * bisherige Anzahl erfolgreicher Befriedungsversuche dieser Gilde eingeht.
+ * Anschliessend wird aus der Wahrscheinlichkeit und random() bestimmt, ob
+ * dieser Versuch erfolgreich ist.
+Formel: w = (INT_CASTER + 10 - ANZ*4) / (INT_ME + 10)
+INT_CASTER: Caster-Intelligenz, INT_ME: Intelligenz dieses Livings
+ANZ: Anzahl erfolgreicher Befriedungsversuche
+
+Annahme: INT_CASTER === 22, alle Wahrscheinlichkeiten * 100
+
+INT_ME   Erfolgswahrscheinlichkeiten je nach Anzahl erfolgreicher Versuche
+              1       2       3       4       5       6       7       8
+      0     280     240     200     160     120      80      40       0
+      2  233,33     200  166,67  133,33     100   66,67   33,33       0
+      4     200  171,43  142,86  114,29   85,71   57,14   28,57       0
+      6     175     150     125     100      75      50      25       0
+      8  155,56  133,33  111,11   88,89   66,67   44,44   22,22       0
+     10     140     120     100      80      60      40      20       0
+     12  127,27  109,09   90,91   72,73   54,55   36,36   18,18       0
+     14  116,67     100   83,33   66,67      50   33,33   16,67       0
+     16  107,69   92,31   76,92   61,54   46,15   30,77   15,38       0
+     18     100   85,71   71,43   57,14   42,86   28,57   14,29       0
+     20   93,33      80   66,67   53,33      40   26,67   13,33       0
+     22    87,5      75    62,5      50    37,5      25    12,5       0
+     24   82,35   70,59   58,82   47,06   35,29   23,53   11,76       0
+     26   77,78   66,67   55,56   44,44   33,33   22,22   11,11       0
+     28   73,68   63,16   52,63   42,11   31,58   21,05   10,53       0
+     30      70      60      50      40      30      20      10       0
+     32   66,67   57,14   47,62    38,1   28,57   19,05    9,52       0
+     34   63,64   54,55   45,45   36,36   27,27   18,18    9,09       0
+     35   62,22   53,33   44,44   35,56   26,67   17,78    8,89       0
+     36   60,87   52,17   43,48   34,78   26,09   17,39     8,7       0
+     38   58,33      50   41,67   33,33      25   16,67    8,33       0
+     40      56      48      40      32      24      16       8       0
+     42   53,85   46,15   38,46   30,77   23,08   15,38    7,69       0
+     44   51,85   44,44   37,04   29,63   22,22   14,81    7,41       0
+     46      50   42,86   35,71   28,57   21,43   14,29    7,14       0
+     48   48,28   41,38   34,48   27,59   20,69   13,79     6,9       0
+     50   46,67      40   33,33   26,67      20   13,33    6,67       0
+     52   45,16   38,71   32,26   25,81   19,35    12,9    6,45       0
+     54   43,75    37,5   31,25      25   18,75    12,5    6,25       0
+     56   42,42   36,36    30,3   24,24   18,18   12,12    6,06       0
+     58   41,18   35,29   29,41   23,53   17,65   11,76    5,88       0
+     60      40   34,29   28,57   22,86   17,14   11,43    5,71       0
+     62   38,89   33,33   27,78   22,22   16,67   11,11    5,56       0
+     64   37,84   32,43   27,03   21,62   16,22   10,81    5,41       0
+     66   36,84   31,58   26,32   21,05   15,79   10,53    5,26       0
+     68    35,9   30,77   25,64   20,51   15,38   10,26    5,13       0
+     70      35      30      25      20      15      10       5       0
+     72   34,15   29,27   24,39   19,51   14,63    9,76    4,88       0
+     74   33,33   28,57   23,81   19,05   14,29    9,52    4,76       0
+     76   32,56   27,91   23,26    18,6   13,95     9,3    4,65       0
+     78   31,82   27,27   22,73   18,18   13,64    9,09    4,55       0
+     80   31,11   26,67   22,22   17,78   13,33    8,89    4,44       0
+     82   30,43   26,09   21,74   17,39   13,04     8,7    4,35       0
+     84   29,79   25,53   21,28   17,02   12,77    8,51    4,26       0
+     86   29,17      25   20,83   16,67    12,5    8,33    4,17       0
+     88   28,57   24,49   20,41   16,33   12,24    8,16    4,08       0
+     90      28      24      20      16      12       8       4       0
+     92   27,45   23,53   19,61   15,69   11,76    7,84    3,92       0
+     94   26,92   23,08   19,23   15,38   11,54    7,69    3,85       0
+     96   26,42   22,64   18,87   15,09   11,32    7,55    3,77       0
+     98   25,93   22,22   18,52   14,81   11,11    7,41     3,7       0
+    100   25,45   21,82   18,18   14,55   10,91    7,27    3,64       0
+ * @return 1, falls Befrieden erlaubt ist, 0 sonst.
+ * @param[in] caster Derjenige, der den Spruch ausfuehrt.
+ * @attention Wenn acify() 1 zurueckliefert, zaehlt dies als
+ * erfolgreicher Befriedungsversuch und in diesem Lebewesen wurde
+ * StopHuntingMode(1) aufgerufen.
+*/
+public int Pacify(object caster) {
+  
+  // wenn das Viech keine Gegner hat, dann witzlos. ;-) Ohne Caster gehts auch
+  // direkt raus.
+  if (!mappingp(enemies) || !sizeof(enemies)
+      || !objectp(caster)) {
+    return 0;
+  }
+
+  // Wenn P_ACCEPT_PEACE gesetzt ist, altes Verhalten wiederspiegeln
+  // -> der NPC ist einfach immer befriedbar. Gleiches gilt fuer den Caster
+  // selber, der wird sich ja nicht gegen das eigene Befriede wehren. Und auch
+  // im team wehrt man sich nicht gegen das Befriede eines Teamkollegen
+  if (QueryProp(P_ACCEPT_PEACE)==1 || caster==ME
+      || member(TeamMembers(), caster) > -1) {
+    StopHuntingMode(1); // Caster/Gilde sollte eigene Meldung ausgeben
+    return 1;
+  }
+
+  string gilde = caster->QueryProp(P_GUILD) || "ANY";
+
+  // ggf. P_PEACE_HISTORY initialisieren
+  mixed ph = (mixed)QueryProp(P_PEACE_HISTORY);
+  if (!pointerp(ph))
+    SetProp(P_PEACE_HISTORY, ph=({time(), ([]) }) );
+  
+  // ggf. die Zaehler reduzieren.
+  if ( ph[0] + __RESET_TIME__ * 75/100 < time()) {
+    _decay_peace_history(&ph);
+  }
+  
+  float w = (caster->QueryAttribute(A_INT) + 10 - ph[1][gilde] * 4.0) / 
+             (QueryAttribute(A_INT) + 10);
+  // auf [0,1] begrenzen.
+  if (w<0) w=0.0;
+  else if (w>1) w=1.0;
+  // w * n ist eine Zahl zwischen 0 und n, wenn w * n > random(n)+1,
+  // darf befriedet werden. Da das Random fuer grosse Zahlen
+  // besser verteilt ist, nehm ich n = __INT_MAX__ und vernachlaessige
+  // ausserdem die +1 beim random().
+  if (ceil(w * __INT_MAX__) > random(__INT_MAX__) ) {
+    ph[1][gilde]++;
+    StopHuntingMode(1);
+    return 1;
+  }
+  // ein SetProp(P_PEACE_HISTORY) kann entfallen, da das Mapping direkt
+  // geaendert wird. Sollte die Prop allerdings mal ne Querymethode haben,
+  // welche eine Kopie davon liefert, muss das hier geaendert oder die
+  // Prop per Query() abgefragt werden.
+  return 0;
+}
+
diff --git a/std/living/comm.c b/std/living/comm.c
new file mode 100644
index 0000000..6001ffd
--- /dev/null
+++ b/std/living/comm.c
@@ -0,0 +1,133 @@
+// MorgenGrauen MUDlib
+//
+// living/comm.c -- communiction module for livings
+//
+// $Id$
+
+#pragma strong_types,save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#include <defines.h>
+#include <living/comm.h>
+
+void create_super()
+{
+  set_next_reset(-1);
+}
+
+protected string comm_guess_action() {
+  string cmd;
+  string action = query_verb();
+  // Die Aktionen sind intern in der Regel nach den haeufigsten Kommandoverben
+  // dieser Aktion benannt. Bei einigen Aktionen sind mehrere Kommandoverben
+  // ueblich, die sollen hier noch abgehandelt werden.
+  switch(action) {
+    case "nehme":
+      // MA_TAKE == nimm
+      action = MA_TAKE;
+      break;
+
+    case "norden":
+    case "nordosten":
+    case "osten":
+    case "suedosten":
+    case "sueden":
+    case "suedwesten":
+    case "westen":
+    case "nordwesten":
+    case "oben":
+    case "unten":
+    case "betrete":
+    case "verlasse":
+    case "teleport":
+    case "teleportiere":
+      action = MA_MOVE;
+      break;
+
+    case "unt":
+      action = MA_LOOK;
+      break;
+
+    case "wirf":
+      if (strstr(query_command(), " weg") > -1)
+        action = MA_PUT;
+      break;
+
+    case "stecke":
+      cmd = query_command();
+      if (strstr(cmd, " weg") > -1)
+        action = MA_UNWIELD;
+      else if (strstr(cmd," in ") > -1)
+        action = MA_PUT;
+      break;
+
+    case "ziehe":
+      cmd = query_command();
+      if (strstr(cmd, " an") > -1)
+        action = MA_WEAR;
+      else if (strstr(cmd, " aus") > -1)
+        action = MA_UNWEAR;
+      break;
+
+    case "esse":
+    case "friss":
+      action = MA_EAT;
+      break;
+
+    case "saufe":
+      action = MA_DRINK;
+      break;
+
+    case "hoere":
+      //MA_LISTEN == lausche
+      action = MA_LISTEN;
+      break;
+    case "lese":
+      action = MA_READ;
+      break;
+
+    case ":":
+    case ";":
+      action = MA_EMOTE;
+      break;
+
+    case "zerbreche":
+    case "zerstoere":
+    case "verbrenne":
+    case "entsorge":
+      action = MA_REMOVE;
+      break;
+  }
+  return action;
+}
+
+protected int comm_guess_message_type(string action, mixed origin) {
+  // everything not mentioned in the switch becomes MT_LOOK.
+  switch(action) {
+    case MA_FIGHT:
+      // Kampf kann man meisten sowohl sehen als auch hoeren.
+      return MT_LOOK | MT_LISTEN;
+    case MA_LISTEN:
+    case MA_SAY:
+      return MT_LISTEN;
+    case MA_FEEL:
+      return MT_FEEL;
+    case MA_SMELL:
+      return MT_SMELL;
+    case MA_CHANNEL:
+      return MT_COMM | MT_FAR;
+    case MA_EMOTE:
+      if (objectp(origin)
+          && environment(origin) == environment())
+        return MT_COMM;
+      else
+        return MT_COMM | MT_FAR;
+    case MA_SHOUT:
+      return MT_LISTEN | MT_FAR;
+  }
+  // die meisten Aktionen sind zumindest sichtbar...
+  return MT_LOOK;
+}
+
diff --git a/std/living/description.c b/std/living/description.c
new file mode 100644
index 0000000..6353ca9
--- /dev/null
+++ b/std/living/description.c
@@ -0,0 +1,332 @@
+// MorgenGrauen MUDlib
+//
+// living/description.c -- description for living objects
+//
+// $Id: description.c 9395 2015-12-08 23:04:38Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/description";
+
+#define NEED_PROTOTYPES
+
+#include <living/skills.h>
+#include <living/clothing.h>
+#include <thing/properties.h>
+#include <wizlevels.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <class.h>
+#include <sys_debug.h>
+
+public string _query_internal_extralook() {
+  mixed xl;
+  int zeit;
+  string res, look="";
+
+  xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE);
+  if (!mappingp(xl))
+    return(0);
+
+  foreach(string key, mapping xld: xl) {
+    if (intp(zeit=xld["xlduration"])) {
+      //hat offenbar nen Ablaufdatum
+      if ( (zeit > 0 && zeit < time()) ||
+           (zeit < 0 && abs(zeit) < object_time(ME)) ) {
+        // Zeit abgelaufen oder
+        // negative "Ablaufzeit" und das Objekt ist neuer als die
+        // Eintragzeit, also loeschen und weiter (ja, das geht. ;-) und xld
+        // hat das Eintragsmapping ja noch, weitere Benutzung also moeglich.)
+        m_delete(xl,key);
+        // ggf. Meldung ausgeben
+        if (interactive(ME)) {
+          if (sizeof(xld["xlende"])) {
+            tell_object(ME,xld["xlende"]);
+          }
+          //kein einfacher String, aber Objekt+Funktion gegeben?
+          else if (sizeof(xld["xlendefun"]) && sizeof(xld["xlobjectname"]) &&
+            (!catch(res=call_other(xld["xlobjectname"],xld["xlendefun"],ME)
+                    ;publish))) {
+              if (stringp(res) && sizeof(res))
+                tell_object(ME,res);
+            }
+        }
+        continue;
+      }
+    }
+    // Der Eintrag ist offenbar noch gueltig, Meldung anhaengen, bzw. via
+    // Funktionsaufruf beschaffen.
+    if (sizeof(xld["xllook"]))
+      look+=xld["xllook"];
+    else if (sizeof(xld["xlfun"]) && sizeof(xld["xlobjectname"])) {
+      closure cl;
+      if (catch(cl=symbol_function(xld["xlfun"],xld["xlobjectname"]);publish)
+          || !cl) {
+          // wenn Fehler beim Laden/Closure erstellen, dann Eintrag loeschen
+          // -> Annahme, dass dieser Fehler permanent sein wird, z.B. Eintrag
+          // von Clonen
+          m_delete(xl,key);
+          continue;
+      }
+      else if (!catch(res=funcall(cl, ME); publish)) {
+        if (!stringp(res) || !sizeof(res)) {
+          // keinen String oder leeren String gekriegt -> ueberspringen.
+          continue;
+        }
+        else
+          look+=res;
+      }
+    }
+  }
+  // fertig. Wenn look nicht leer ist, zurueckgeben, sonst 0.
+  if (sizeof(look))
+    return(look);
+  else
+    return(0);
+}
+
+public varargs int AddExtraLook(string look, int duration, string key, 
+                                string lookende, object ob) {
+  mapping xl;
+  string oname;
+  if (!stringp(key) || !sizeof(key)) {
+    // Automatisch erzeugen, wenn moeglich
+    if (!objectp(previous_object()) || 
+        !stringp(key=object_name(previous_object())) || !sizeof(key))
+      return(-1);
+  }
+
+  if (!stringp(look) || !sizeof(look))
+    return(-2);
+  if (!intp(duration))
+    return(-3);
+
+  xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE); // dran denken: liefert referenz zurueck
+  if (!mappingp(xl)) {
+    Set(P_INTERNAL_EXTRA_LOOK, xl=([]) );
+  }
+
+  // kein Automatisches Ueberschreiben.
+  if (member(xl,key))
+    return(-4);
+
+  // neg. Werte: "bis Ende/reboot", abs(duration) == Eintragzeit
+  // 0: "fuer ewig", >0: Zeitdauer in Sekunden
+  if (duration > 0)
+    duration+=time();  // hoffentlich gibt es reichtzeitig 64bit-Ints
+  else if (duration < 0)
+    duration=negate(time());
+  // 0 bleibt, wie es ist.
+
+  if (objectp(ob)) {
+    // Funktionsname und Objektname (als Name, damit es auch noch geht, wenn
+    // das Objekt entladen wurde, Crash/reboot war etc.) speichern
+    // Clone werden auch gespeichert, aber es wird direkt ein harter Fehler
+    // ausgeloest, wenn ein permanenter Xlook fuer einen Clone registriert
+    // werden soll: das kann nicht gehen.
+    if (!duration && clonep(ob))
+        raise_error(sprintf(
+           "AddExtraLook(): Fehlerhaftes Argument <duration>: %d, "
+           "permanente Extralooks durch Clone (%s) nicht registrierbar.\n",
+           duration, object_name(ob)));
+
+    xl[key]=(["xlobjectname":object_name(ob),
+              "xlfun": look,
+             ]);
+    // ggf. Name der Funktion speichern, die bei Ablauf aufgerufen wird.
+    if (stringp(lookende) && sizeof(lookende))
+        xl[key]["xlendefun"]=lookende;
+  }
+  else {
+    // Einfacher Eintrag, nur den bearbeiteten String merken. ;-)
+    xl[key]=(["xllook": break_string(replace_personal(look,({ME}),1),78,
+                                     "",BS_LEAVE_MY_LFS),
+             ]);
+    // ggf. Meldung speichern, die bei Ablauf ausgegeben werden soll.
+    if (stringp(lookende) && sizeof(lookende)) {
+      xl[key]["xlende"]=break_string(replace_personal(lookende,({ME}),1),78,
+                                     "",BS_LEAVE_MY_LFS);
+    }
+  }
+  // Endezeit vermerken.
+  if (duration != 0)
+    xl[key]["xlduration"]=duration;
+
+  // Kein Set noetig, weil Query das Mapping ja als Referenz lieferte.
+  return(1);
+}
+
+public int RemoveExtraLook(string key) {
+  mapping xl;
+  if (!stringp(key) || !sizeof(key)) {
+    // Automatisch erzeugen, wenn moeglich
+    if (!objectp(previous_object()) ||
+        !stringp(key=object_name(previous_object())) || !sizeof(key))
+      return(-1);
+  }
+  xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE); // dran denken: liefert referenz zurueck
+  if (!mappingp(xl))
+    return (-2);
+  if (!member(xl,key))
+    return(-2);
+
+  m_delete(xl,key);
+  // Kein Set noetig, weil Query das Mapping ja als Referenz lieferte.
+  return(1);
+}
+
+void create()
+{ 
+  ::create();
+  Set(P_GENDER, SAVE, F_MODE);
+  // Extralook-Property speichern und vor manueller Aenderung schuetzen
+  // EMs duerfen, die wissen hoffentlich, was sie tun.
+  Set(P_INTERNAL_EXTRA_LOOK, SAVE|PROTECTED, F_MODE_AS);
+  SetProp(P_CLOTHING,({}));
+  AddId("Living");
+}
+
+string condition()
+{
+  int hpnt, max_hpnt, perc;
+
+  hpnt        = QueryProp( P_HP );
+  max_hpnt    = QueryProp( P_MAX_HP );
+
+  if(max_hpnt>0 && hpnt>0)
+    perc=100*hpnt/max_hpnt;
+ 
+  switch(perc) {
+    case 0..9:
+        return capitalize(QueryPronoun(WER))+" steht auf der Schwelle des Todes.\n";
+    case 10..19:
+        return capitalize(QueryPronoun(WER))+" braucht dringend einen Arzt.\n";
+    case 20..29:
+        return capitalize(QueryPronoun(WER))+" ist in keiner guten Verfassung.\n";
+    case 30..39:
+        return capitalize(QueryPronoun(WER))+" wankt bereits bedenklich.\n";
+    case 40..49:
+        return capitalize(QueryPronoun(WER))+" macht einen mitgenommenen Eindruck.\n";
+    case 50..59:
+        return capitalize(QueryPronoun(WER))+" sieht nicht mehr taufrisch aus.\n";
+    case 60..69:
+        return capitalize(QueryPronoun(WER))+" ist leicht angeschlagen.\n";
+    case 70..79:
+        return capitalize(QueryPronoun(WER))+" fuehlte sich heute schon besser.\n";
+    case 80..89:
+        return capitalize(QueryPronoun(WER))+" ist schon etwas geschwaecht.\n";
+  }
+  //fall-through
+  return capitalize(QueryPronoun(WER))+" ist absolut fit.\n";
+}
+
+varargs string long() {
+  string str, cap_pronoun;
+  string descr, invl,tmp,exl;
+  int hpnt, max_hpnt;
+  mixed filter_ldfied;
+  object ob;
+
+  str = process_string( QueryProp(P_LONG) );
+  if(!stringp(str)) str = "";
+
+  str += condition();
+
+  // Extralook
+  if(stringp(tmp = QueryProp(P_EXTRA_LOOK)))
+    str += tmp;
+  if (stringp(tmp = QueryProp(P_INTERNAL_EXTRA_LOOK)))
+    str += tmp;
+  for(ob = first_inventory(ME); ob; ob = next_inventory(ob))
+    if(exl = ob->QueryProp(P_EXTRA_LOOK)) 
+      str += exl;
+    else if(exl = ob->extra_look()) 
+      str += exl; // TO BE REMOVED
+
+  
+  if(filter_ldfied = QueryProp(P_TRANSPARENT))
+  {
+    invl = make_invlist(PL, all_inventory(ME));
+    if(invl != "")
+      str += capitalize(QueryPronoun(WER))+" traegt bei sich:\n" + invl;
+  }
+  return str;
+}
+
+varargs string name(int casus, int demonst)
+{ 
+  if( QueryProp( P_INVIS ) )
+  {
+    if( casus == RAW ) return "Jemand";
+    return ({"Jemand","Jemands","Jemandem","Jemanden"})[casus];
+  }
+  if (QueryProp(P_FROG) && casus != RAW )
+  {
+    string s=QueryArticle(casus, 0, 1)+"Frosch";
+    if (casus==WESSEN) s += "es";
+    return s;
+  }
+  return ::name( casus, demonst );
+}
+
+static int _query_gender()
+{
+  if (QueryProp(P_FROG)) return 1;
+  return Query(P_GENDER);
+}
+
+// NPC sollen aus Kompatibilitaetsgruenden auch eine "echte" Rasse haben.
+// Default ist hier die Rasse, man kann aber hiermit auch einen NPC faken,
+// der sich tarnt, indem man P_REAL_RACE per Hand setzt.
+static string _query_real_race()
+{
+  return Query(P_REAL_RACE,F_VALUE)||QueryProp(P_RACE);
+}
+
+static mixed _set_name(mixed nm )
+{
+  string lvnam;
+  lvnam = nm;
+  if(pointerp(nm)) lvnam = nm[0];
+  set_living_name(lower_case(lvnam));
+  return Set(P_NAME, nm);
+}
+
+int _query_container()
+{
+  return 0;
+}
+
+int is_class_member(mixed str) {
+  // Keine Klasse, keine Mitgliedschaft ...
+  if (!str || (!stringp(str) && !pointerp(str)) || str=="") 
+      return 0;
+
+  if (::is_class_member(str))
+    return 1;
+
+  if (stringp(str))
+    str = ({str});
+
+  // Rassen werden als implizite Klassen aufgefasst.
+  // TODO: Pruefen, ob das unbedingt hart-kodiert sein muss.
+  string race = QueryProp(P_RACE);
+  if ( stringp(race) && member( str, lower_case(race) ) > -1 )
+    return 1;
+  else
+    return 0;
+}
+
+mapping _query_material() {
+  mixed res;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  return ([MAT_MISC_LIVING:100]);
+}
+
diff --git a/std/living/helpers.c b/std/living/helpers.c
new file mode 100644
index 0000000..3d292c0
--- /dev/null
+++ b/std/living/helpers.c
@@ -0,0 +1,149 @@
+// MorgenGrauen MUDlib
+//
+// living/helpers.c -- (Query)Methoden fuer Hilfsobjekte, z.B. zum Tauchen
+//
+// $Id: moneyhandler.h,v 3.1 1997/02/12 13:29:09 Wargon Exp %
+
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <living/helpers.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+public int RegisterHelperObject(object helper, int type, 
+                                string|closure callback) 
+{
+  // cb: closure auf die Callback-Funktion in previous_object()
+  closure cb;
+  // helpers: Mapping aller eingetragenen Helfer-Objekte
+  mapping helpers;
+
+  // Kein positiver Integerwert als Helfertyp uebergeben?
+  if ( !intp(type) || type < 1 )
+     raise_error(sprintf( "Wrong argument 1 to RegisterHelperObject(). "
+        "Expected positive <int>, got %O.\n", type));
+  // Kein Objekt vorhanden, an dem die Callback-Funktion gerufen werden soll?
+  if ( !objectp(helper) )
+    return HELPER_NO_CALLBACK_OBJECT;
+
+  // Funktionsname zum Zweck des Callbacks uebergeben?
+  if ( stringp(callback) ) {
+    // Dann Closure davon erstellen.
+    cb = symbol_function(callback, helper);
+    // Wenn das nicht klappt (zB weil die Funktion private ist), dann
+    // Fehler werfen und abbrechen.
+    if ( !closurep(cb) )
+      raise_error(sprintf("Error in RegisterHelperObject(): Unable to call "
+        "function %s in object %O.\n", callback, helper));
+  }
+  // Wenn schon eine Closure uebergeben wurde, dann diese direkt speichern.
+  else if ( closurep(callback) ) {
+    cb = callback;
+  }
+  // Weder Funktionsname, noch Closure, dann Fehler werfen und abbrechen.
+  else
+    raise_error(sprintf("Wrong argument 2 to RegisterHelperObject(). "
+      "Expected <string/closure>, got %O.\n",callback));
+
+  // Property auslesen und zwischenspeichern
+  helpers = QueryProp(P_HELPER_OBJECTS);
+  // Wenn die Prop leer ist, hier initialisieren
+  if ( !helpers ) {
+    helpers = ([type:({})]);
+  }
+  // Wenn der Typ noch nicht existiert, hier nachtragen.
+  else if ( !pointerp(helpers[type]) ) {
+    helpers[type] = ({});
+  }
+
+  // Closure eintragen, wenn noch nicht vorhanden
+  if ( member(helpers[type], cb)==-1 ) {
+    helpers[type] = helpers[type]+({cb});
+    SetProp(P_HELPER_OBJECTS, helpers);
+    return HELPER_SUCCESS;
+  }
+  else
+    return HELPER_ALREADY_LISTED;
+}
+
+public int UnregisterHelperObject(object helper, int type) {
+  if ( !intp(type) || type < 1 )
+     raise_error(sprintf( "Wrong argument 2 to UnregisterHelperObject(). "
+        "Expected positive <int>, got %O.\n", type));
+  if ( !objectp(helper) )
+    return HELPER_NO_CALLBACK_OBJECT;
+
+  mapping helpers = Query(P_HELPER_OBJECTS, F_VALUE);
+
+  if ( mappingp(helpers) ) {
+    foreach(closure cl: helpers[type]) {
+      if ( get_type_info(cl,2) == helper ) {
+        helpers[type] = helpers[type]-({cl});
+        return HELPER_SUCCESS;
+      }
+    }
+  }
+  return HELPER_NOTHING_TO_UNREGISTER;
+}
+
+// Querymethode fuer P_AQUATIC_HELPERS
+public mapping _query_lib_p_aquatic_helpers() {
+  mapping ret = ([]);
+  // eingetragene Callback-Closures auslesen
+  closure *helpers = 
+    ( Query(P_HELPER_OBJECTS, F_VALUE) || ([]) )[HELPER_TYPE_AQUATIC];
+  // Es sind gar keine Werte eingetragen? Dann gleich rausspringen.
+  if ( !pointerp(helpers) )
+    return ret;
+
+  // Nullelement substrahieren
+  helpers -= ({0});
+
+  if ( sizeof(helpers) ) {
+    // Mapping erstellen: Keys sind die Objekte, deren Closures eingetragen
+    // sind. Values sind die Rueckgabewerte der Closures,
+    // die dabei das Spielerobjekt und das abfragende Objekt uebergeben
+    // bekommen.
+    object *keys = map(helpers, #'get_type_info, 2);
+    int *vals = map(helpers, #'funcall, this_object(), previous_object(1));
+    ret = mkmapping(keys,vals);
+  }
+  return ret;
+}
+
+// Querymethode fuer P_AERIAL_HELPERS
+public mapping _query_lib_p_aerial_helpers() {
+  mapping ret = ([]);
+  // eingetragene Callback-Closures auslesen
+  closure *helpers = 
+    ( Query(P_HELPER_OBJECTS, F_VALUE) || ([]) )[HELPER_TYPE_AERIAL];
+
+  // Es sind gar keine Werte eingetragen? Dann gleich rausspringen.
+  if ( !pointerp(helpers) )
+    return ret;
+  
+  // Nullelement substrahieren
+  helpers -= ({0});
+
+  if ( sizeof(helpers) ) {
+    // Mapping erstellen: Keys sind die Objekte, deren Closures eingetragen
+    // sind. Values sind die Rueckgabewerte der Closures,
+    // die dabei das Spielerobjekt und das abfragende Objekt uebergeben
+    // bekommen.
+    object *keys = map(helpers,#'get_type_info, 2);
+    int *vals = map(helpers, #'funcall, this_object(), previous_object(1));
+    ret = mkmapping(keys,vals);
+  }
+  return ret;
+}
+
+// Querymethode fuer P_HELPER_OBJECTS
+public mapping _query_lib_p_helper_objects() {
+  return deep_copy(Query(P_HELPER_OBJECTS,F_VALUE));
+}
+
diff --git a/std/living/inventory.c b/std/living/inventory.c
new file mode 100644
index 0000000..e3027e4
--- /dev/null
+++ b/std/living/inventory.c
@@ -0,0 +1,66 @@
+// MorgenGrauen MUDlib
+//
+// container/inventory.c -- Umgang mit besonderen Objekten im Inventory
+//
+// $Id: inventory.c 6554 2007-10-17 22:45:53Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/inventory";
+
+#define NEED_PROTOTYPES
+
+#include <properties.h>
+#include <sensitive.h>
+#include <attributes.h>
+
+#define ME this_object()
+
+public void RemoveSensitiveObject(object ob) {
+  ::RemoveSensitiveObject(ob);
+  RemoveSensitiveObjectFromList(ob,SENSITIVE_ATTACK);
+  if (objectp(ob) && (ob->QueryProp(P_X_ATTR_MOD)   ||
+                      ob->QueryProp(P_X_HEALTH_MOD) ))
+  {
+    deregister_modifier(ob);
+    // Erst wenn das Objekt den Spieler verlassen konnte, die Attribute
+    // neu berechnen.
+    if (find_call_out("UpdateAttributes")==-1)
+      call_out("UpdateAttributes",0); 
+  }
+}
+
+public void InsertSensitiveObject(object ob, mixed arg) {
+  ::InsertSensitiveObject(ob,arg);
+  if (objectp(ob) && (ob->QueryProp(P_X_ATTR_MOD)   ||
+                      ob->QueryProp(P_X_HEALTH_MOD) ))
+  {
+    register_modifier(ob);
+    UpdateAttributes();
+  }
+}
+
+public void CheckSensitiveAttack(int dam, mixed dam_type, mixed spell, 
+                                 object enemy) {
+  mixed a,x;
+  int i;
+  
+  if (!pointerp(a=QueryProp(P_SENSITIVE_ATTACK)))
+    return;
+  if (!pointerp(dam_type))
+    dam_type=({dam_type});
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (pointerp(x=a[i]) &&
+	dam>x[SENS_THRESHOLD] &&
+	member(dam_type,x[SENS_KEY])>=0 &&
+	objectp(x[SENS_OBJECT]) &&
+	environment(x[SENS_OBJECT])==ME &&
+	closurep(x[SENS_CLOSURE]))
+      funcall(x[SENS_CLOSURE],
+	      enemy,x[SENS_KEY],dam,
+	      spell,x[SENS_OPT]);
+}
+
diff --git a/std/living/life.c b/std/living/life.c
new file mode 100644
index 0000000..f3a72f5
--- /dev/null
+++ b/std/living/life.c
@@ -0,0 +1,1572 @@
+// MorgenGrauen MUDlib
+//
+// living/life.c -- life variables
+//
+// $Id: life.c 9426 2016-01-03 10:02:57Z Zesstra $
+
+// living object life variables
+//
+//  P_ALIGN         -- alignment value
+//  P_NPC           -- if living is an NPC
+//  P_HP            -- HitPoints
+//  P_SP            -- SpellPoints
+//  P_ALCOHOL       -- value of intoxication
+//  P_DRINK         -- value of soakness
+//  P_FOOD          -- value of stuffness
+//  P_XP            -- experience
+//  P_POISON        -- level of poison
+//  P_CORPSE        -- corpse-object
+//  P_DEAF          -- if living is deaf
+#pragma strong_types,save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#include <living/skills.h>
+#include <thing/properties.h>
+#include <living/life.h>
+#include <living/moving.h>
+#include <living/combat.h>
+#include <living/attributes.h>
+#include <thing/description.h>
+#include <thing/language.h>
+#undef NEED_PROTOTYPES
+#include <health.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <scoremaster.h>
+#include <defuel.h>
+#include <properties.h>
+#include <events.h>
+#include <wizlevels.h>
+
+#define ALCOHOL_VALUE(n) n
+
+#include <debug_info.h> //voruebergehend
+
+
+// 'private'-Prototypen
+private void DistributeExp(object enemy, int exp_to_give);
+
+// Variablen
+nosave int delay_alcohol; /* time until next alcohol effect */
+nosave int delay_drink;   /* time until next drink effect */
+nosave int delay_food;    /* time until next food effect */
+nosave int delay_heal;    /* time until next heal effect */
+nosave int delay_sp;      /* time until next sp regeneration */
+nosave int delay_poison;  /* time until next poison effect */
+nosave int drop_poison;
+nosave mapping enemy_damage;
+nosave mapping hp_buffer;
+nosave mapping sp_buffer;
+nosave int remove_me;
+int nextdefueltimefood;
+int nextdefueltimedrink;
+
+
+protected void create()
+{
+  Set(P_GHOST, SAVE, F_MODE);
+  Set(P_FROG, SAVE, F_MODE);
+  Set(P_ALIGN, SAVE, F_MODE);
+  Set(P_HP, SAVE, F_MODE);
+  Set(P_SP, SAVE, F_MODE);
+  Set(P_XP, SAVE, F_MODE);
+  Set( P_LAST_XP, ({ "", 0 }) );
+  Set( P_LAST_XP, PROTECTED, F_MODE_AS );
+
+  Set(P_ALCOHOL, SAVE, F_MODE);
+  Set(P_DRINK, SAVE, F_MODE);
+  Set(P_FOOD, SAVE, F_MODE);
+  Set(P_POISON, SAVE, F_MODE);
+  Set(P_DEAF, SAVE, F_MODE);
+
+  SetProp(P_FOOD_DELAY, FOOD_DELAY);
+  SetProp(P_DRINK_DELAY, DRINK_DELAY);
+  SetProp(P_ALCOHOL_DELAY, ALCOHOL_DELAY);
+  SetProp(P_HP_DELAY,HEAL_DELAY);
+  SetProp(P_SP_DELAY,HEAL_DELAY);
+  SetProp(P_POISON_DELAY,POISON_DELAY);
+  // default fuer alle Lebewesen (NPC + Spieler):
+  SetProp(P_MAX_POISON, 10);
+  SetProp(P_CORPSE, "/std/corpse");
+
+  nextdefueltimefood=time()+QueryProp(P_DEFUEL_TIME_FOOD);
+  nextdefueltimedrink=time()+QueryProp(P_DEFUEL_TIME_DRINK);
+
+  enemy_damage=([:2 ]);
+  hp_buffer=([]);
+  sp_buffer=([]);
+
+  SetProp(P_DEFUEL_LIMIT_FOOD,1);
+  SetProp(P_DEFUEL_LIMIT_DRINK,1);
+  SetProp(P_DEFUEL_TIME_FOOD,1);
+  SetProp(P_DEFUEL_TIME_DRINK,1);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,1);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,1);
+
+  offerHook(H_HOOK_DIE,1);
+
+  offerHook(H_HOOK_FOOD,1);
+  offerHook(H_HOOK_DRINK,1);
+  offerHook(H_HOOK_ALCOHOL,1);
+  offerHook(H_HOOK_POISON,1);
+  offerHook(H_HOOK_CONSUME,1);
+}
+
+// Wenn der letzte Kampf lang her ist und das Lebewesen wieder vollgeheilt
+// ist, wird P_ENEMY_DAMAGE zurueckgesetzt.
+protected void ResetEnemyDamage() {
+  if (time() > QueryProp(P_LAST_COMBAT_TIME) + __RESET_TIME__ * 4
+      && QueryProp(P_HP) == QueryProp(P_MAX_HP))
+    enemy_damage=([:2 ]);
+}
+
+private void DistributeExp(object enemy, int exp_to_give) {
+  int total_damage, tmp, ex;
+  mapping present_enemies;
+
+  if ( exp_to_give<=0 )
+    return;
+
+  mapping endmg=deep_copy(enemy_damage);
+
+  // Mitglieder im Team des Killers bekommen:
+  //
+  //                  Gesamtanteil des Teams
+  // Eigenen Anteil + ----------------------
+  //                  Anzahl  Teammitglieder
+  // ---------------------------------------
+  //                2
+  //
+  object *inv = enemy->TeamMembers();
+  if ( pointerp(inv) )
+  {
+    present_enemies=m_allocate(sizeof(inv), 1);
+    foreach(object ob: inv)
+    {
+      if ( objectp(ob) && (environment(ob)==environment()) )
+      {
+        tmp=endmg[object_name(ob)];
+        total_damage+=tmp;
+        present_enemies[ob] = tmp/2;
+        m_delete(endmg,object_name(ob)); //s.u.
+      }
+    }
+    int mitglieder = sizeof(present_enemies);
+    if ( mitglieder )
+    {
+      tmp=total_damage/(2*mitglieder);
+      foreach(object ob, int punkte: &present_enemies)
+        punkte += tmp;
+    }
+  }
+  else {
+    // ohne Team wird trotzdem ein Mapping gebraucht. Da Groessenveraenderung
+    // rel. teuer sind, kann einfach mal fuer 3 Eintraege Platz reservieren.
+    present_enemies=m_allocate(3, 1);
+  }
+  // Und noch die Lebewesen im Raum ohne Team.
+  foreach(object ob: all_inventory(environment()))
+  {
+    if ( tmp=endmg[object_name(ob)] )
+    {
+      total_damage += tmp;
+      present_enemies[ob] = tmp;
+      m_delete(endmg,object_name(ob)); // Nur einmal pro Leben Punkte :)
+    }
+  }
+  if ( !total_damage )
+  {
+    enemy->AddExp(exp_to_give);
+  }
+  else
+  {
+    foreach(object ob, int damage: present_enemies)
+    {
+      if ( !objectp(ob) )
+        continue;
+      if ( query_once_interactive(ob) && ( !interactive(ob)
+              || (query_idle(ob)>600) ) )
+        continue;
+      //exp_to_give*present_enemies[i][1]/total_damage gibt bei viel Schaden
+      //einen numerical overflow. Daher muessen wir hier wohl doch
+      //zwischenzeitlich mit floats rechnen, auch wenn das 0-1 XP Verlust
+      //durch float->int-Konversion gibt. (ceil() lohnt sich IMHO nicht.)
+      ex = (int)(exp_to_give*((float)damage/(float)total_damage));
+      ob->AddExp(ex);
+    }
+  }
+}
+
+/*
+ * This function is called from other players when they want to make
+ * damage to us. But it will only be called indirectly.
+ * We return how much damage we received, which will
+ * change the attackers score. This routine is probably called from
+ * heart_beat() from another player.
+ * Compare this function to reduce_hit_points(dam).
+ */
+public int do_damage(int dam, object enemy)
+{ int hit_point,al,al2;
+
+  if ( extern_call()
+      && objectp(enemy)
+      && living(enemy)
+      && !QueryProp(P_ENABLE_IN_ATTACK_OUT))
+  {
+    al=time()-enemy->QueryProp(P_LAST_MOVE);
+    if (al<3)      // Erste Kampfrunde nach Betreten des Raumes?
+      dam/=(4-al); // Gegen Rein-Feuerball-Raus-Taktik
+  }
+
+  if ( QueryProp(P_GHOST) || QueryProp(P_NO_ATTACK) || (dam<=0)
+      || ( objectp(enemy)
+          && ( enemy->QueryProp(P_GHOST)
+              || enemy->QueryProp(P_NO_ATTACK) ) ) )
+    return 0;
+
+  hit_point = QueryProp(P_HP)-dam;
+
+  if ( QueryProp(P_XP) && objectp(enemy) )
+  {
+    if ( !QueryProp(P_NO_XP) )
+      enemy->AddExp(dam*(int)QueryProp(P_TOTAL_WC)/10);
+  }
+
+  if (living(enemy)) {
+      string enname = object_name(enemy);
+      // Hmpf. Blueprints sind doof. Die Chance ist zwar gering, aber koennte
+      // sein, dass ein Unique-NPC mit zwei verschiedenen Spielern am gleichen
+      // NPC metzelt.
+      // TODO: MHmm. wie gross ist das Risiko wirklich?
+      //if (!clonep(enemy))
+      //    enname = enname + "_" + to_string(object_time(enemy));
+      // nur wenn gegner NPC ist und noch nicht drinsteht: Daten aus
+      // P_HELPER_NPC auswerten
+      if (!member(enemy_damage,enemy) && !query_once_interactive(enemy)) {
+          mixed helper = enemy->QueryProp(P_HELPER_NPC);
+          if (pointerp(helper) && objectp(helper[0]))
+              enemy_damage[enname,1] = helper[0];
+      }
+      enemy_damage[enname,0]+=dam;
+  }
+
+  SetProp(P_HP, hit_point);
+
+  if ( hit_point<0 )
+  {
+    //TODO: Warum nicht das ganze Zeug ins die() verlegen?
+    if ( enemy )
+    {
+      enemy->StopHuntFor(ME,1);
+      if ( !QueryProp(P_NO_XP) )
+        DistributeExp(enemy,QueryProp(P_XP)/100);
+      if ( !query_once_interactive(ME) )
+        log_file ("NPC_XP", sprintf(
+	    "[%s] %s, XP: %d, HP*WC: %d, Killer: %s\n",
+	    dtime(time()), object_name(ME), (QueryProp(P_XP)/100),
+                  QueryProp(P_TOTAL_WC)*QueryProp(P_MAX_HP)/10,
+                  enemy->name()||"NoName" ));
+      al = QueryProp(P_ALIGN)/50 + enemy->QueryProp(P_ALIGN)/200;
+      if (al>20)
+        al=20;
+      else if(al<-20)
+        al=-20;
+      enemy->SetProp(P_ALIGN,enemy->QueryProp(P_ALIGN)-al);
+    }
+    SetProp(P_KILLER, enemy);
+    
+    die();
+  }
+  return dam;
+}
+
+
+private void _transfer( object *obs, string|object dest, int flag )
+{   int i;
+
+    i = sizeof(obs);
+
+    // Eine Schleife ist zwar langsamer als filter() o.ae., aber
+    // selbst mit einer noch so schnellen Loesung kann leider nicht
+    // ausgeschlossen werden, dass irgendwo ein too-long-eval-Bug dazwischen
+    // kommt. Dazu sind die Kaempfe mit Gilden-NPCs etc. einfach zu teuer ...
+    // Pruefung auf zerstoerte Objekte, da einige sich evtl. im NotifyPlayerDeath() 
+    // zerstoeren.
+   while ( i && get_eval_cost() > 300000 )
+        if ( objectp(obs[--i]) && !obs[i]->QueryProp(P_NEVERDROP) )
+        // Jetzt wird's noch etwas teurer mit catch() - aber manche Sachen
+        // duerfen einfach nicht buggen
+            catch( obs[i]->move( dest, flag );publish );
+
+    if ( i > 0 )
+        // Zuviel Rechenzeit verbraten, es muessen noch Objekte bewegt werden
+        call_out( #'_transfer, 0, obs[0..i-1], dest, flag );
+    else {
+        if ( remove_me )
+            remove();
+    }
+}
+
+
+public varargs void transfer_all_to( string|object dest, int isnpc ) {
+    int flags;
+    object *obs;
+
+    if ( !objectp(ME) )
+        return;
+
+    // Das Flag "isnpc" ist fuer NPCs gedacht. Deren Ausruestung darf nicht
+    // mit M_NOCHECK bewegt werden, da Spieler das bei Nicht-Standard-Leichen
+    // sonst u.U. ausnutzen koennten.
+    if ( isnpc )
+        flags = M_SILENT;
+    else
+        flags = M_SILENT|M_NOCHECK;
+
+    obs = all_inventory(ME) || ({});
+
+    // unnoetig, weil _transfer() auch auf P_NEVERDROP prueft. Zesstra
+    //obs -= filter_objects( obs, "QueryProp", P_NEVERDROP );
+
+    _transfer( obs, dest, flags );
+}
+
+
+protected varargs void create_kill_log_entry(string killer, object enemy) {
+  int level,lost_exp;
+
+  if ( (level=QueryProp(P_LEVEL))<20 || !IS_SEER(ME) )
+    lost_exp = QueryProp(P_XP)/3;
+  else
+    lost_exp = QueryProp(P_XP)/(level-17);
+  
+  log_file("KILLS",sprintf("%s %s (%d,%d) %s\n", strftime("%e %b %H:%M"),
+               capitalize(REAL_UID(ME)), level, lost_exp/1000, killer)); 
+}
+
+// Liefert im Tod (nach dem toetenden do_damage()) das Spielerobjekt, was den
+// Tod wohl zu verantworten hat, falls es ermittelt werden kann. Es werden vor
+// allem registrierte Helfer-NPC und einige Sonderobjekte beruecksichtigt.
+protected object get_killing_player()
+{
+  object killer=QueryProp(P_KILLER);
+  // koennte sein, wenn ausserhalb des Todes gerufen oder eine Vergiftung uns
+  // umgebracht hat.
+  if (!objectp(killer))
+    return 0;
+
+  while (killer && !query_once_interactive(killer))
+    killer = killer->QueryUser();
+
+  return killer;
+}
+
+protected object GiveKillScore(object pl, int npcnum)
+{
+  // Stufenpunkt fuer den Kill vergeben.
+  // Falls der Killer den Punkt schon hat, wird
+  // zufaellig ein Mitglied seines Teams ausgewaehlt
+  // und diesem der Punkt gegeben.
+  object *obs,ob;
+  mixed *fr;
+  int i,j,sz;
+
+  if ( pointerp(obs=pl->TeamMembers()) && (member(obs,pl)>=0) )
+  {
+    if ( !pointerp(fr=pl->PresentTeamRows())
+        || !sizeof(fr)
+        || !pointerp(fr=fr[0])) // Erste Reihe des Teams
+      fr=({});
+    fr-=({pl,0});
+    obs-=({pl,0});
+    obs-=fr;
+    i=sz=sizeof(obs); // restliche Teammitglieder in zufaelliger Reihenfolge:
+    for ( --i ; i>=0 ; i-- )
+    {
+      j=random(sz);
+      ob=obs[j];
+      obs[j]=obs[0];
+      obs[0]=ob;
+    }
+    i=sz=sizeof(fr);  // Erste Reihe in zufaelliger Reihenfolge:
+    for ( --i ; i>=0 ; i-- )
+    {
+      j=random(sz);
+      ob=fr[j];
+      fr[j]=fr[0];
+      fr[0]=ob;
+    }
+
+    obs+=fr;     // Erste Reihe wird vor Rest getestet
+    obs+=({pl}); // Killer wird als erstes getestet
+  }
+  else
+  {
+    obs=({pl});
+  }
+  for ( i=sizeof(obs)-1 ; i>=0 ; i-- )
+    if ( objectp(ob=obs[i] )
+        && interactive(ob)     // Nur netztot dabei stehen gilt nicht :)
+        && query_idle(ob)<600  // gegen Leute die sich nur mitschleppen lassen
+        && environment(ob)==environment(pl) // Nur anwesende Teammitglieder
+        && !IS_LEARNER(ob)
+//        && !ob->QueryProp(P_TESTPLAYER)
+        && !(SCOREMASTER->HasKill(ob,ME)) )
+      return SCOREMASTER->GiveKill(ob,npcnum),ob;
+
+  return SCOREMASTER->GiveKill(pl,npcnum),pl;
+}
+
+// zum ueberschreiben in Spielern
+public int death_suffering() {
+  return 0; // NPC haben keine Todesfolgen
+}
+
+// kein 2. Leben fuer Nicht-Spieler. ;-)
+varargs protected int second_life( object corpse ) {
+    return 0;
+}
+
+public varargs void die( int poisondeath, int extern )
+{   object corpse;
+    string die_msg, tmp;
+    mixed res;
+    mixed hookData;
+    mixed hookRes;
+
+    if ( !objectp(this_object()) || QueryProp(P_GHOST) )
+        return; // Ghosts dont die ...
+
+    // direkt von extern aufgerufen und nicht ueber heart_beat() oder
+    // do_damage() hierher gelangt?
+    if (extern_call() && previous_object() != this_object()) {
+      extern=1;
+      SetProp(P_KILLER, previous_object());
+    }
+
+    if ( res = QueryProp(P_TMP_DIE_HOOK) ){
+        if ( pointerp(res) && sizeof(res)>=3
+            && intp(res[0]) && time()<res[0]
+            && objectp(res[1]) && stringp(res[2]) )
+        {
+            if ( res = call_other( res[1], res[2], poisondeath ) ) {
+              SetProp(P_KILLER,0);
+              return;
+            }
+        }
+        else
+            SetProp(P_TMP_DIE_HOOK,0);
+    }
+
+    // trigger die hook
+    hookData=poisondeath;
+    hookRes=HookFlow(H_HOOK_DIE,hookData);
+    if (pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+      if(hookRes[H_RETCODE]==H_CANCELLED) {
+        SetProp(P_KILLER,0);
+        return;
+      }
+      else if (hookRes[H_RETCODE]==H_ALTERED)
+          poisondeath = hookRes[H_RETDATA];
+    }
+
+    if ( IS_LEARNING(ME) && query_once_interactive(ME) ){
+       tell_object( ME, "Sei froh dass Du unsterblich bist, sonst waere es "
+                        "eben Dein Ende gewesen.\n");
+       SetProp(P_KILLER,0);
+       return;
+    }
+
+    // Gegner befrieden.
+    map_objects( QueryEnemies()[0], "StopHuntFor", ME, 1 );
+    StopHuntingMode(1);
+
+    // Falls die() direkt aufgerufen wurde und dies ein Spieler ist, muss das
+    // die() noch Eintraege in /log/KILLS via create_kill_log_entry bzw. in
+    // /log/KILLER erstellen.
+    if ( query_once_interactive(ME) && extern )
+    {
+      object killer = QueryProp(P_KILLER) 
+                     || previous_object() || this_interactive() || this_player();
+      if ( killer && !query_once_interactive(killer) )
+      {
+          tmp = explode( object_name(killer), "#")[0] + " (direkt !)";
+
+          create_kill_log_entry( tmp + " (" + REAL_UID(killer) + ")", killer );
+      }
+      else if ( killer && !QueryProp(P_TESTPLAYER) && !IS_LEARNER(ME) )
+      {
+          log_file( "KILLER", sprintf( "%s %s (%d/%d) toetete %s (%d/%d)\n",
+                                       ctime(time()),
+                                       capitalize(getuid(killer)),
+                                       query_wiz_level(killer),
+                                       killer->QueryProp(P_LEVEL),
+                                       capitalize(getuid(ME)),
+                                       query_wiz_level(ME),
+                                       QueryProp(P_LEVEL) ) );
+
+          killer->SetProp( P_KILLS, -1 );
+      }
+    }
+
+  // Bei NPC EKs vergeben und ggf. in der Gilde des Killers und im Raum
+  // NPC_Killed_By() rufen.
+  if ( !query_once_interactive(ME) )
+  {
+    object killer = ((object) QueryProp(P_KILLER)) || previous_object() ||
+      this_interactive() || this_player();
+
+    if ( killer && query_once_interactive(killer) )
+    {
+      if (stringp(res=killer->QueryProp(P_GUILD))
+          && objectp(res=find_object("/gilden/"+res)))
+        res->NPC_Killed_By(killer);
+
+      if (environment())
+          environment()->NPC_Killed_By(killer);
+
+      res = QueryProp(P_XP);
+      res = (res < SCORE_LOW_MARK) ? 0 : ((res > SCORE_HIGH_MARK) ? 2 : 1);
+      if ( !QueryProp(P_NO_SCORE) && !IS_LEARNER(killer) &&
+           // !killer->QueryProp(P_TESTPLAYER) &&
+           pointerp( res = SCOREMASTER->QueryNPC(res)) )
+        GiveKillScore( killer, res[0] );
+    }
+  }
+
+  if( !(die_msg = QueryProp(P_DIE_MSG)) )
+    if (QueryProp(P_PLURAL))
+      die_msg = " fallen tot zu Boden.\n";
+    else
+      die_msg = " faellt tot zu Boden.\n";
+
+  if ( poisondeath )
+  {
+      Set( P_LAST_DAMTYPES, ({ DT_POISON }) );
+      Set( P_LAST_DAMTIME, time() );
+      Set( P_LAST_DAMAGE, 1 );
+      die_msg = " wird von Gift hinweggerafft und kippt um.\n";
+  }
+
+  say( capitalize(name(WER,1)) + die_msg );
+
+  // Wenn keine Leiche, dann Kram ins Env legen.
+  if ( QueryProp(P_NOCORPSE) || !(tmp = QueryProp(P_CORPSE))
+      || catch(corpse = clone_object(tmp);publish) 
+      || !objectp(corpse) )
+  {
+      // Magier oder Testspieler behalten ihre Ausruestung.
+      // Sonst kaemen u.U. Spieler an Magiertools etc. heran
+      if ( !(IS_LEARNER(ME) || (tmp = Query(P_TESTPLAYER)) &&
+             (!stringp(tmp) || IS_LEARNER( lower_case(tmp) ))) )
+          transfer_all_to( environment(), 0 );
+      else
+          // Aber sie ziehen sich aus.
+          filter_objects(QueryProp(P_ARMOURS),"DoUnwear",M_NOCHECK,0);
+  }
+  else
+  // sonst in die Leiche legen.
+  {
+      corpse->Identify(ME);
+      corpse->move( environment(), M_NOCHECK|M_SILENT );
+      // Magier oder Testspieler behalten ihre Ausruestung.
+      // Sonst kaemen u.U. Spieler an Magiertools etc. heran
+      if ( !(IS_LEARNER(ME) || (tmp = Query(P_TESTPLAYER)) &&
+             (!stringp(tmp) || IS_LEARNER( lower_case(tmp) ))) )
+          transfer_all_to( corpse, !query_once_interactive(ME) );
+      else
+          // Aber sie ziehen sich aus.
+          filter_objects(QueryProp(P_ARMOURS),"DoUnwear",M_NOCHECK,0);
+  }
+
+  if ( query_once_interactive(ME) ) {
+      Set( P_DEADS, Query(P_DEADS) + 1 );
+      // Spieler-Tod-event ausloesen
+      EVENTD->TriggerEvent(EVT_LIB_PLAYER_DEATH, ([
+      E_OBJECT: ME, E_PLNAME: getuid(ME),
+      E_ENVIRONMENT: environment(), E_TIME: time(),
+      P_KILLER: QueryProp(P_KILLER),
+      P_LAST_DAMAGE: QueryProp(P_LAST_DAMAGE),
+      P_LAST_DAMTYPES: copy(QueryProp(P_LAST_DAMTYPES)),
+      E_EXTERNAL_DEATH: extern,
+      E_POISON_DEATH: poisondeath, 
+      E_CORPSE: (objectp(corpse)?corpse:0) ]) );
+  }
+  else {
+      // NPC-Todes-Event ausloesen. Div. Mappings/Arrays werden nicht kopiert,
+      // weil der NPC ja jetzt eh zerstoert wird.
+      mapping data = ([
+            E_OBNAME: object_name(ME),
+            E_ENVIRONMENT: environment(), E_TIME: time(),
+            P_NAME: QueryProp(P_NAME),
+            P_KILLER: QueryProp(P_KILLER),
+            P_ENEMY_DAMAGE: QueryProp(P_ENEMY_DAMAGE),
+            P_LAST_DAMAGE: QueryProp(P_LAST_DAMAGE),
+            P_LAST_DAMTYPES: QueryProp(P_LAST_DAMTYPES),
+            E_EXTERNAL_DEATH: extern,
+            E_POISON_DEATH: poisondeath,
+            E_CORPSE: (objectp(corpse)?corpse:0),
+            P_XP: QueryProp(P_XP),
+            P_ATTRIBUTES: QueryProp(P_ATTRIBUTES),
+            P_MAX_HP: QueryProp(P_MAX_HP),
+            P_HANDS: QueryProp(P_HANDS),
+            P_ALIGN: QueryProp(P_ALIGN),
+            P_RACE: QueryProp(P_RACE),
+            P_CLASS: QueryProp(P_CLASS),
+            ]);
+      EVENTD->TriggerEvent(EVT_LIB_NPC_DEATH(""), data);
+      EVENTD->TriggerEvent(
+          EVT_LIB_NPC_DEATH(load_name(ME)), data);
+  }
+
+  // transfer_all_to() ist evtl. (wenn zuviele Objekte bewegt werden mussten)
+  // noch nicht ganz fertig und wird per call_out() den Rest erledigen.
+  // Sollte die Leiche dann nicht mehr existieren, verbleiben die restlichen
+  // Objekte im Spieler.
+  // Es bleiben aber auf jeden Fall noch rund 300k Eval-Ticks ueber, damit
+  // kein Spieler dank "evalcost too high" ungeschoren davon kommt.
+  if ( !(second_life(corpse)) )
+  {
+      Set( P_GHOST, 1 ); // Fuer korrekte Ausgabe auf Teamkanal.
+
+      if ( find_call_out(#'_transfer) == -1 )
+          // Falls kein call_out() mehr laeuft, sofort destructen ...
+          remove();
+      else
+          // ... ansonsten vormerken
+          remove_me = 1;
+  }
+}
+
+public void heal_self(int h)
+{
+  if ( h<=0 )
+    return;
+  SetProp(P_HP, QueryProp(P_HP)+h);
+  SetProp(P_SP, QueryProp(P_SP)+h);
+}
+
+
+//--------------------------------------------------------------------------
+//
+//   int defuel_food( /* void */ )
+//
+//   Enttankt den Spieler um einen gewissen Essens-Wert.
+//   Sollte nur von Toiletten aufgerufen werden.
+//
+//--------------------------------------------------------------------------
+public int defuel_food()
+{
+  int food;
+
+  food=QueryProp(P_FOOD);
+
+// wenn spieler kein food hat: return 0
+  if ( !food )
+   return NO_DEFUEL;
+
+// wenn spieler unter enttank-grenze: return -1
+  if ( food < QueryProp(P_DEFUEL_LIMIT_FOOD) )
+   return DEFUEL_TOO_LOW;
+
+// wenn letztes enttanken nicht lange genug zurueckliegt: return -2
+  if ( time() < nextdefueltimefood )
+   return DEFUEL_TOO_SOON;
+
+  food=to_int(((food*QueryProp(P_DEFUEL_AMOUNT_FOOD))/2));
+  food+=random(food);
+
+// sicherheitshalber
+  if ( food > QueryProp(P_FOOD) )
+   food=QueryProp(P_FOOD);
+
+  SetProp(P_FOOD,(QueryProp(P_FOOD)-food));
+
+  nextdefueltimefood=time()+QueryProp(P_DEFUEL_TIME_FOOD);
+
+  return food;
+}
+
+
+//--------------------------------------------------------------------------
+//
+//   int defuel_drink( /* void */ )
+//
+//   Enttankt den Spieler um einen gewissen Fluessigkeits-Wert.
+//   Gleichzeitig wird eine gewisse Menge Alkohol reduziert.
+//   Sollte nur von Toiletten aufgerufen werden.
+//
+//--------------------------------------------------------------------------
+public int defuel_drink()
+{
+  int alc, drink;
+
+  drink=QueryProp(P_DRINK);
+
+// wenn spieler kein drink hat: return 0
+  if ( !drink )
+   return NO_DEFUEL;
+
+// wenn spieler unter enttank-grenze: return -1
+  if ( drink < QueryProp(P_DEFUEL_LIMIT_DRINK) )
+   return DEFUEL_TOO_LOW;
+
+// wenn letztes enttanken nicht lange genug zurueckliegt: return -2
+  if ( time() < nextdefueltimedrink )
+   return DEFUEL_TOO_SOON;
+    
+  drink=to_int(((drink*QueryProp(P_DEFUEL_AMOUNT_DRINK))/2));
+  drink+=random(drink);
+
+// sicherheitshalber
+  if ( drink > QueryProp(P_DRINK) )
+   drink=QueryProp(P_DRINK);
+
+  SetProp(P_DRINK,(QueryProp(P_DRINK)-drink));
+
+// jedes fluessige Enttanken macht auch etwas nuechterner :^)
+// bei sehr kleinen Mengen enttankt man keinen Alkohol
+// ansonsten in Abhaengigkeit von enttankter Menge, P_ALCOHOL und P_WEIGHT
+
+  if ( drink > 9 && QueryProp(P_ALCOHOL) > 0 )
+   {
+    alc=(to_int(exp(log(1.1)*(drink)))*
+         to_int(exp(log(0.67)*(QueryProp(P_ALCOHOL)))))/
+         (QueryProp(P_MAX_DRINK)*QueryProp(P_MAX_ALCOHOL))*
+         (to_int(QueryProp(P_WEIGHT)/1000));
+
+     SetProp(P_ALCOHOL,QueryProp(P_ALCOHOL)-(alc+random(alc)));
+   }
+
+  nextdefueltimedrink=time()+QueryProp(P_DEFUEL_TIME_DRINK);
+ 
+  return drink;
+}
+
+
+public void reduce_spell_points(int h)
+{
+  SetProp(P_SP, QueryProp(P_SP)-h);
+}
+
+public void restore_spell_points(int h)
+{
+  SetProp(P_SP, QueryProp(P_SP)+h);
+}
+
+/* Reduce hitpoints. Log who is doing it. */
+public int reduce_hit_points(int dam)
+{ object o;
+  int i;
+
+#ifdef LOG_REDUCE_HP
+  if (this_player()!=ME)
+  {
+    log_file("REDUCE_HP", name()+" by ");
+    if(!this_player()) log_file("REDUCE_HP","?\n");
+    else {
+      log_file("REDUCE_HP",this_player()->name());
+      o=previous_object();
+      if (o)
+        log_file("REDUCE_HP", " " + object_name(o) + ", " +
+                 o->name(WER,0) + " (" + creator(o) + ")\n");
+      else
+        log_file("REDUCE_HP", " ??\n");
+    }
+  }
+#endif
+  if ((i=QueryProp(P_HP)) <= dam)
+    return SetProp(P_HP,1);
+  return SetProp(P_HP, i - dam);
+}
+
+public int restore_hit_points(int heal)
+{
+  return reduce_hit_points(-heal);
+}
+
+public varargs int drink_alcohol(int strength,int testonly, string mytext)
+{ int alc,add,res;
+
+  add=ALCOHOL_VALUE(strength);
+  res=UseSkill(SK_BOOZE,([
+      SI_SKILLARG : add,
+      SI_TESTFLAG : 1])); // Kann der Spieler gut saufen?
+  if (intp(res) && res>0) add=res;
+  alc=QueryProp(P_ALCOHOL)+add;
+  if ((alc >= QueryProp(P_MAX_ALCOHOL)) && !IS_LEARNING(this_object())){
+    if(!testonly)
+      tell_object(ME,mytext||"So ein Pech, Du hast alles verschuettet.\n");
+    return 0;
+  }
+  if(testonly)return 1;
+  UseSkill(SK_BOOZE,([ SI_SKILLARG : ALCOHOL_VALUE(strength) ]));
+  if(alc < 0) alc = 0;
+  if(!alc) tell_object(ME, "Du bist stocknuechtern.\n");
+  SetProp(P_ALCOHOL, alc);
+  return 1;
+}
+
+public varargs int drink_soft(int strength, int testonly, string mytext)
+{ int soaked;
+
+  soaked = QueryProp(P_DRINK);
+  if((soaked + strength > QueryProp(P_MAX_DRINK)) &&
+     !IS_LEARNING(this_object())){
+    if(!testonly)
+     tell_object(ME, mytext||
+       "Nee, so viel kannst Du momentan echt nicht trinken.\n" );
+    return 0;
+  }
+  if(testonly)return 1;
+  if((soaked += DRINK_VALUE(strength)) < 0) soaked = 0;
+  if(!soaked) tell_object(ME, "Dir klebt die Zunge am Gaumen.\n");
+  SetProp(P_DRINK, soaked);
+  return 1;
+}
+
+public varargs int eat_food(int strength, int testonly, string mytext)
+{ int stuffed;
+
+  stuffed = QueryProp(P_FOOD);
+  if ((stuffed + strength > QueryProp(P_MAX_FOOD)) &&
+      !IS_LEARNING(this_object()))
+  {
+    if(!testonly)
+        tell_object(ME,
+            mytext || "Das ist viel zu viel fuer Dich! Wie waers mit etwas "
+            "leichterem?\n");
+    return 0;
+  }
+  if(testonly)return 1;
+  stuffed += FOOD_VALUE(strength);
+  if(stuffed < 0) stuffed = 0;
+  if(!stuffed) tell_object(ME, "Was rumpelt denn da in Deinem Bauch?\n");
+  SetProp(P_FOOD, stuffed);
+  return 1;
+}
+
+public int buffer_hp(int val,int rate)
+{
+  int dif;
+
+  if(val<=0 || rate<=0)return 0;
+  if(val < rate)rate = val;
+  if(rate>20) rate=20;
+
+  /* Check for BufferOverflow */
+  if((dif=(hp_buffer[0]+val)-QueryProp(P_MAX_HP)) > 0)val-=dif;
+  if(val<=0)return 0;
+
+  hp_buffer[0] += val;
+  hp_buffer[1+rate] += val;
+  if(rate > hp_buffer[1])hp_buffer[1] = rate;
+
+  return hp_buffer[0];
+}
+
+public int buffer_sp(int val,int rate)
+{
+  int dif;
+
+  if(val<=0 || rate<=0)return 0;
+  if(val < rate)rate = val;
+  if(rate>20) rate=20;
+
+  /* Check for BufferOverflow */
+  if((dif=(sp_buffer[0]+val)-QueryProp(P_MAX_SP)) > 0)val-=dif;
+  if(val<=0)return 0;
+
+  sp_buffer[0] += val;
+  sp_buffer[1+rate] += val;
+  if(rate > sp_buffer[1])sp_buffer[1] = rate;
+
+  return sp_buffer[0];
+}
+
+protected void update_buffers()
+{ int i, rate, max;
+
+  rate=0;
+  max=0;
+  for(i=1;i<=20;i++){
+    if(member(hp_buffer, i+1))
+      if(hp_buffer[i+1]<=0)
+        hp_buffer = m_delete(hp_buffer,i+1);
+      else{
+        max+=hp_buffer[i+1];
+        rate=i;
+      }
+  }
+
+  hp_buffer[0]=max;
+  hp_buffer[1]=rate;
+  rate=0;
+  max=0;
+  for(i=1;i<=20;i++){
+    if(member(sp_buffer, i+1))
+      if(sp_buffer[i+1]<=0)
+        sp_buffer = m_delete(sp_buffer,i+1);
+      else{
+        max+=sp_buffer[i+1];
+        rate=i;
+      }
+  }
+  sp_buffer[0]=max;
+  sp_buffer[1]=rate;
+}
+
+public int check_timed_key(string key) {
+
+  // keine 0 als key (Typ wird per RTTC geprueft)
+  if (!key)
+    return 0;
+  mapping tmap=Query(P_TIMING_MAP, F_VALUE);
+  if (!mappingp(tmap)) {
+    tmap=([]);
+    Set(P_TIMING_MAP, tmap, F_VALUE);
+  }
+  // Wenn key noch nicht abgelaufen, Ablaufzeitpunkt zurueckgeben.
+  // Sonst 0 (key frei)
+  return (time() < tmap[key]) && tmap[key];
+}
+
+public int check_and_update_timed_key(int duration,string key) {
+
+  // wenn key noch gesperrt, die zeit der naechsten Verfuegbarkeit
+  // zurueckgeben.
+  int res = check_timed_key(key);
+  if (res) {
+    return res;
+  }
+
+  // duration <= 0 ist unsinnig. Aber key ist nicht mehr gesperrt, d.h. time()
+  // ist ein sinnvoller Rueckgabewert.
+  if (duration <= 0)
+    return time();
+ 
+  mapping tmap = Query(P_TIMING_MAP,F_VALUE);
+  tmap[key]=time()+duration;
+  
+  // speichern per SetProp() unnoetig, da man das Mapping direkt aendert,
+  // keine Kopie.
+  //SetProp(P_TIMING_MAP, tmap);
+  
+  return -1; // Erfolg.
+}
+
+protected void expire_timing_map() {
+  
+  mapping tmap = Query(P_TIMING_MAP, F_VALUE);
+  if (!mappingp(tmap) || !sizeof(tmap))
+    return;
+  foreach(string key, int endtime: tmap) {
+    if (endtime < time())
+      m_delete(tmap, key);
+  }
+  // speichern per SetProp() unnoetig, da man das Mapping direkt aendert,
+  // keine Kopie.
+}
+
+protected void heart_beat()
+{
+    if ( !this_object() )
+        return;
+
+    attribute_hb();
+
+    // Als Geist leidet man nicht unter so weltlichen Dingen wie
+    // Alkohol, Gift&Co ...
+    if ( QueryProp(P_GHOST) )
+        return;
+
+    int hpoison = QueryProp(P_POISON);
+    int rlock = QueryProp(P_NO_REGENERATION);
+    int hp = QueryProp(P_HP);
+    int sp = QueryProp(P_SP);
+    int alc;
+
+    // Wenn Alkohol getrunken: Alkoholauswirkungen?
+    if ( (alc = QueryProp(P_ALCOHOL)) && !random(40) ){
+        int n;
+        string gilde;
+        object ob;
+
+        n = random( 5 * (alc - 1)/QueryProp(P_MAX_ALCOHOL) );
+
+        switch (n){
+        case ALC_EFFECT_HICK:
+            say( capitalize(name( WER, 1 )) + " sagt: <Hick>!\n" );
+            write( "<Hick>! Oh, Tschuldigung.\n" );
+            break;
+            
+        case ALC_EFFECT_STUMBLE:
+            say( capitalize(name( WER, 1 )) + " stolpert ueber " +
+                 QueryPossPronoun( FEMALE, WEN ) + " Fuesse.\n" );
+            write( "Du stolperst.\n" );
+            break;
+            
+        case ALC_EFFECT_LOOKDRUNK:
+            say( capitalize(name( WER, 1 )) + " sieht betrunken aus.\n" );
+            write( "Du fuehlst Dich benommen.\n" );
+            break;
+            
+        case ALC_EFFECT_RUELPS:
+            say( capitalize(name( WER, 1 )) + " ruelpst.\n" );
+            write( "Du ruelpst.\n" );
+            break;
+        }
+       
+        // Gilde und Environment informieren ueber Alkoholauswirkung.
+        if ( stringp(gilde = QueryProp(P_GUILD))
+             && objectp(ob = find_object( "/gilden/" + gilde )) )
+            ob->InformAlcoholEffect( ME, n, ALC_EFFECT_AREA_GUILD );
+        
+        if ( environment() )
+            environment()->InformAlcoholEffect( ME, n, ALC_EFFECT_AREA_ENV );
+    }
+    
+    // Alkohol abbauen und etwas extra heilen, falls erlaubt.
+    if ( alc && (--delay_alcohol < 0)
+         && !(rlock & NO_REG_ALCOHOL) ){
+
+        SetProp( P_ALCOHOL, alc - 1 );
+        
+        if ( !hpoison ){
+            hp++;
+            sp++;
+        }
+        
+        delay_alcohol = QueryProp(P_ALCOHOL_DELAY);
+    }
+
+    // P_DRINK reduzieren, falls erlaubt.
+    if ( (--delay_drink < 0) && !(rlock & NO_REG_DRINK) ){
+        delay_drink = QueryProp(P_DRINK_DELAY);
+        SetProp( P_DRINK, QueryProp(P_DRINK) - 1 );
+    }
+
+    // P_FOOD reduzieren, falls erlaubt.
+    if ( (--delay_food < 0) && !(rlock & NO_REG_FOOD) ){
+        delay_food = QueryProp(P_FOOD_DELAY);
+        SetProp( P_FOOD, QueryProp(P_FOOD) - 1 );
+    }
+
+    // Regeneration aus dem HP-Puffer
+    // Hierbei wird zwar nur geheilt, wenn das erlaubt ist, aber der Puffer
+    // muss trotzdem abgearbeitet/reduziert werden, da Delfen sonst eine
+    // mobile Tanke kriegen. (Keine Heilung im Hellen, Puffer bleibt sonst
+    // konstant und kann bei Bedarf ueber dunkelmachende Items abgerufen
+    // werden.)
+    int val;
+    if (hp_buffer[0]) {
+        int rate = hp_buffer[1];
+        val = hp_buffer[rate + 1];
+    
+        if ( val > rate )
+            val = rate;
+        hp_buffer[0] -= val;
+        hp_buffer[rate + 1] -= val;
+        if ( hp_buffer[rate + 1] <= 0 )
+            update_buffers();
+    }
+    // Jetzt Regeneration aus dem Puffer durchfuehren, aber nur wenn erlaubt.
+    if ( val && !(rlock & NO_REG_BUFFER_HP) )
+        hp += val; 
+    // normales Heilen, falls keine Regeneration aus dem Puffer erfolgte und
+    // es erlaubt ist.
+    else if ( (--delay_heal < 0) && !(rlock & NO_REG_HP) ){
+        delay_heal = QueryProp(P_HP_DELAY);
+        if ( !hpoison )
+            hp++;
+    }
+
+    // Gleiches Spiel jetzt fuer den SP-Puffer (s.o.)
+    val=0;
+    if ( sp_buffer[0] ) {
+        int rate = sp_buffer[1];
+        val = sp_buffer[rate + 1];
+        
+        if ( val > rate )
+            val = rate;
+        
+        sp_buffer[0] -= val;
+        sp_buffer[rate + 1] -= val;
+ 
+        if ( sp_buffer[rate + 1] <= 0 )
+            update_buffers();
+    }
+    // Regeneration erlaubt?
+    if ( val && !(rlock & NO_REG_BUFFER_SP) )
+         sp += val;
+    // Wenn nicht, normales Hochideln versuchen.
+    else if ( (--delay_sp < 0) && !(rlock & NO_REG_SP) ){
+        delay_sp = QueryProp(P_SP_DELAY);
+        if ( !hpoison )
+            sp++;
+    }
+
+    if ( hpoison && (interactive(ME) || !query_once_interactive(ME)) ){
+        // Vanion, 26.10.03
+        // Wenn _set_poison() per SET_METHOD ueberschrieben wird, kann
+        // nicht sichergestellt werden, dass poison immer groesser 0 ist
+        // Daher muss hier ein Test rein, so teuer das auch ist :(
+        if (--hpoison < 0)
+          hpoison=0;
+        
+        if ( --delay_poison < 0 ){
+            delay_poison = QueryProp(P_POISON_DELAY)
+                + random(POISON_MERCY_DELAY);
+            hp -= hpoison;
+
+            if ( hp < 0 ){
+                tell_object( ME, "Oh weh - das Gift war zuviel fuer Dich!\n"
+                             + "Du stirbst.\n" );
+                
+                if ( query_once_interactive(ME) ){
+                    create_kill_log_entry( "Vergiftung", 0 );
+                    
+                    // Beim Gifttod gibt es keinen Killer. Aber auf diese Art
+                    // erkennt der Todesraum die Ursache korrekt und gibt die
+                    // richtige Meldung aus.
+                    SetProp( P_KILLER, "gift" );
+                }
+                
+                die(1);
+                return;
+            }
+            
+            if ( (hpoison < 3 || !query_once_interactive(ME) )
+                 && --drop_poison < 0)
+            {
+                // Giftlevel eins reduzieren. hpoison wurde oben schon
+                // reduziert, d.h. einfach hpoison in P_POISON schreiben.
+                // dabei wird dann auch ggf. drop_poison richtig gesetzt.
+                SetProp( P_POISON, hpoison );
+                if ( !hpoison )
+                    tell_object( ME, "Du scheinst die Vergiftung "
+                                     "ueberwunden zu haben.\n" );  
+            }
+        }
+        
+        if ( hpoison && !random(15) )
+            switch ( hp*100/QueryProp(P_MAX_HP) ){
+            case 71..100 :
+                write( "Du fuehlst Dich nicht gut.\n" );
+                say( capitalize(name(WER)) +
+                     " sieht etwas benommen aus.\n" );
+                break;
+                
+            case 46..70 :
+                write( "Dir ist schwindlig und Dein Magen revoltiert.\n" );
+                say( capitalize(name(WER)) + " taumelt ein wenig.\n" );
+                break;
+                
+            case 26..45 :
+                write( "Dir ist heiss. Du fuehlst Dich schwach. Kopfweh "
+                       "hast Du auch.\n" );
+                say( capitalize(name(WER)) + " glueht direkt und scheint "
+                     "grosse Schwierigkeiten zu haben.\n" );
+                break;
+                
+            case 11..25 :
+                write( "Du fuehlst Dich beschissen. Alles tut weh, und Du "
+                       "siehst nur noch unscharf.\n" );
+                say( capitalize(name(WER)) + " taumelt und stoehnt und "
+                     "kann gerade noch vermeiden, hinzufallen.\n" );
+                break;
+                
+            case 0..10 :
+                write( break_string( "Du siehst fast nichts mehr und kannst "
+                                     "Dich nur noch unter groessten Schmerzen "
+                                     "bewegen. Aber bald tut nichts mehr weh"
+                                     "...", 78 ) );
+                say( break_string( capitalize(name(WER)) + " glueht wie "
+                                   "im Fieber, kann sich kaum noch ruehren "
+                                   "und hat ein schmerzverzerrtes Gesicht.\n",
+                                   78 ) );
+                break;
+            }
+    }
+
+    SetProp( P_HP, hp );
+    SetProp( P_SP, sp );
+}
+
+public int AddExp( int e )
+{
+  int experience;
+  string fn;
+  mixed last;
+
+  experience = QueryProp(P_XP);
+
+  if ( QueryProp(P_KILLS) > 1 && e > 0 )
+      return experience;
+
+  fn = implode( explode( object_name( environment() || this_object() ),
+                               "/" )[0..<2], "/" );
+
+  if ( pointerp(last = Query(P_LAST_XP)) && sizeof(last) == 2 && last[0] == fn )
+      Set( P_LAST_XP, ({ fn, last[1]+e }) );
+  else
+      Set( P_LAST_XP, ({ fn, e }) );
+
+  if ( (experience += e) < 0 )
+      experience = 0;
+
+  return SetProp( P_XP, experience );
+}
+
+static <string|int>* _set_last_xp( <int|string>* last )
+{
+    if ( !pointerp(last) || sizeof(last) != 2 || !stringp(last[0]) ||
+         !intp(last[1]) )
+        return Query(P_LAST_XP);
+    else
+        return Set( P_LAST_XP, last );
+}
+
+
+static int _set_align(int a)
+{
+  if (a<-1000) a = -1000;
+  if (a>1000) a = 1000;
+  return Set(P_ALIGN, a);
+}
+
+
+static int _set_hp( int hp )
+{
+    if ( QueryProp(P_GHOST) )
+        return QueryProp(P_HP);
+
+    if ( hp < 0 )
+        return Set( P_HP, 0 );
+
+    if ( hp > QueryProp(P_MAX_HP) )
+        return Set( P_HP, QueryProp(P_MAX_HP), F_VALUE );
+
+    return Set( P_HP, hp, F_VALUE );
+}
+
+static int _set_sp( int sp )
+{
+    //einige Leute schreiben floats in die P_HP. :-(
+    if (!intp(sp)) {
+	sp=to_int(sp);
+	//ja, es ist teuer. Aber ich will wissen, wers ist. Kann vor
+	//naechstem Reboot wieder raus.
+	log_file("ILLEGAL_TYPE.log",sprintf(
+	      "Versuch, einen nicht-int in P_SP in %O zu schreiben: \n%O\n",
+	      this_object(),
+	      debug_info(DINFO_TRACE,DIT_STR_CURRENT)));
+    }
+
+    if ( QueryProp(P_GHOST) )
+        QueryProp(P_SP);
+
+    if ( sp < 0 )
+        return Set( P_SP, 0 );
+
+    if ( sp > QueryProp(P_MAX_SP) )
+        return Set( P_SP, QueryProp(P_MAX_SP), F_VALUE );
+
+    return Set( P_SP, sp, F_VALUE );
+}
+
+static int _set_alcohol(int n)
+{
+  if(!intp(n))
+      raise_error(sprintf(
+        "_set_alcohol(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_ALCOHOL, F_VALUE);
+
+  // nur Änderungen und Werte >=0 werden gesetzt...
+  n = n < 0 ? 0 : n;
+  int old = Query(P_ALCOHOL, F_VALUE);
+  if ( old == n)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_ALCOHOL, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_alcohol(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  return Set(P_ALCOHOL, n, F_VALUE);
+}
+
+static int _set_drink(int n)
+{
+  if(!intp(n))
+      raise_error(sprintf(
+        "_set_drink(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_DRINK, F_VALUE);
+
+  // nur Änderungen und Werte >=0 werden gesetzt...
+  n = n < 0 ? 0 : n;
+  int old = Query(P_DRINK, F_VALUE);
+  if ( old == n)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_DRINK, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_drink(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  return Set(P_DRINK, n, F_VALUE);
+}
+
+static int _set_food(int n)
+{
+  if(!intp(n))
+      raise_error(sprintf(
+        "_set_food(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_FOOD, F_VALUE);
+
+  // nur Änderungen und Werte >=0 werden gesetzt...
+  n = n < 0 ? 0 : n;
+  int old = Query(P_FOOD, F_VALUE);
+  if ( old == n)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_FOOD, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_food(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  return Set(P_FOOD, n, F_VALUE);
+}
+
+static int _set_poison(int n)
+{
+    if(!intp(n))
+      raise_error(sprintf(
+        "_set_poison(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_POISON, F_VALUE);
+
+  int mp = QueryProp(P_MAX_POISON);
+  n = (n<0 ? 0 : (n>mp ? mp : n));
+
+  // nur >=0 zulassen.
+  n = n < 0 ? 0 : n;
+
+  int old = Query(P_POISON, F_VALUE);
+  if ( old == 0 && n == 0)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_POISON, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_poison(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  // Fuer die Selbstheilung.
+  switch(n) {
+  case 1:
+    drop_poison = 40+random(16);
+    break;
+  case 2:
+    drop_poison = 25+random(8);
+    break;
+  case 3:
+    drop_poison = 18+random(4);
+    break;
+  default:
+    // nur relevant fuer NPC, da Spieler bei >3 Gift nicht mehr regegenieren.
+    drop_poison = 22 - 2*n + random(43 - 3*n);
+    break;
+  }
+
+  // fuer Setzen der Prop von aussen ein Log schreiben
+  if (previous_object(1) != ME)
+    log_file("POISON", sprintf("%s - %s: %d von %O (%s)\n",
+           dtime(time())[5..],
+           (query_once_interactive(this_object()) ?
+             capitalize(geteuid(this_object())) :
+             capitalize(name(WER))),
+           n,
+           (previous_object(2) ? previous_object(2) : previous_object(1)),
+           (this_player() ? capitalize(geteuid(this_player())) : "???")));
+
+  return Set(P_POISON, n, F_VALUE);
+}
+
+static int _set_xp(int xp) { return Set(P_XP, xp < 0 ? 0 : xp, F_VALUE); }
+
+static mixed _set_die_hook(mixed hook)
+{
+  if(hook && query_once_interactive(this_object()))
+    log_file("DIE_HOOK",
+             sprintf("%s : DIE_HOOK gesetzt von %O in %O (%s)\n",
+                     dtime(time())[5..],
+                     (previous_object(2) ? previous_object(2):previous_object(1)),
+                     this_object(),getuid(this_object())));
+  return Set(P_TMP_DIE_HOOK,hook, F_VALUE);
+}
+
+static mapping _query_enemy_damage()
+{
+  return copy(enemy_damage);
+}
+
+// nur ne Kopie liefern, sonst kann das jeder von aussen aendern.
+static mapping _query_timing_map() {
+  return copy(Query(P_TIMING_MAP));
+}
+
+/****************************************************************************
+ * Consume-Funktion, um zentral durch konsumierbare Dinge ausgeloeste
+ * Aenderungen des Gesundheitszustandes herbeizufuehren.
+ ***************************************************************************/
+
+/* Konsumiert etwas
+ *
+ * Rueckgabewert
+ *      1       erfolgreich konsumiert
+ *      0       fehlende oder falsche Parameter
+ *     <0       Bedingung fuer konsumieren nicht erfuellt, Bitset aus:
+ *      1       Kann nichts mehr essen
+ *      2       Kann nichts mehr trinken
+ *      4       Kann nichts mehr saufen
+ *      8       Abgebrochen durch Hook H_HOOK_CONSUME
+ */
+public varargs int consume(mapping cinfo, int testonly)
+{
+  int retval = 0;
+  // nur was tun, wenn auch Infos reinkommen
+  if (mappingp(cinfo) && sizeof(cinfo)) {
+    // Hooks aufrufen, sie aendern ggf. noch was in cinfo.
+    mixed *hret = HookFlow(H_HOOK_CONSUME, ({cinfo, testonly}) );
+    switch(hret[H_RETCODE])
+    {
+        case H_CANCELLED:
+          return -HC_HOOK_CANCELLATION;
+        case H_ALTERED:
+          // testonly kann nicht geaendert werden.
+          cinfo = hret[H_RETDATA][0];
+    }
+    // Legacy-Mappings (flache) neben strukturierten Mappings zulassen
+    // flache Kopien erzeugen (TODO?: und fuer Teilmappings nicht relevante
+    // Eintraege loeschen)
+    mapping conditions;
+    if (mappingp(cinfo[H_CONDITIONS])) {
+      conditions = copy(cinfo[H_CONDITIONS]);
+    } else {
+      conditions = copy(cinfo);
+    }
+    mapping effects;
+    if (mappingp(cinfo[H_EFFECTS])) {
+      effects = filter(cinfo[H_EFFECTS], (: member(H_ALLOWED_EFFECTS, $1) > -1 :));
+    } else {
+      effects = filter(cinfo, (: member(H_ALLOWED_EFFECTS, $1) > -1 :));
+    }
+
+    // Bedingungen pruefen
+    if (mappingp(conditions) && sizeof(conditions)) {
+      // Bedingungen fuer Konsum auswerten
+      if (conditions[P_FOOD] && !eat_food(conditions[P_FOOD], 1))
+        retval |= HC_MAX_FOOD_REACHED;
+      else if (conditions[P_DRINK] && !drink_soft(conditions[P_DRINK], 1))
+        retval |= HC_MAX_DRINK_REACHED;
+      else if (conditions[P_ALCOHOL] && !drink_alcohol(conditions[P_ALCOHOL], 1))
+        retval |= HC_MAX_ALCOHOL_REACHED;
+      // retval negativ machen, damit Fehler leicht erkennbar ist
+      retval = -retval;
+    }
+    // Bedingungen wurden abgearbeitet, jetzt die Heilung durchfuehren
+    if (!retval) {
+      if (!testonly) {
+        // Bedingungen erfuellen, wenn alles passt und kein Test
+        if (conditions[P_ALCOHOL])
+          drink_alcohol(conditions[P_ALCOHOL]);
+        if (conditions[P_DRINK])
+          drink_soft(conditions[P_DRINK]);
+        if (conditions[P_FOOD])
+          eat_food(conditions[P_FOOD]);
+        // Und jetzt die Wirkungen
+        if (effects[P_POISON])
+          SetProp(P_POISON, QueryProp(P_POISON) + effects[P_POISON]);
+        // Und nun wirklich heilen
+        switch (cinfo[H_DISTRIBUTION]) {
+        case HD_INSTANT:
+          map(effects, (: SetProp($1, QueryProp($1) + $2) :));
+          break;
+        case 1..50:
+          buffer_hp(effects[P_HP], cinfo[H_DISTRIBUTION]);
+          buffer_sp(effects[P_SP], cinfo[H_DISTRIBUTION]);
+          break;
+        default:
+          buffer_hp(effects[P_HP], HD_STANDARD);
+          buffer_sp(effects[P_SP], HD_STANDARD);
+          break;
+        }
+      }
+      retval = 1;
+    }
+  }
+  return retval;
+}
diff --git a/std/living/light.c b/std/living/light.c
new file mode 100644
index 0000000..5b12e1f
--- /dev/null
+++ b/std/living/light.c
@@ -0,0 +1,55 @@
+// MorgenGrauen MUDlib
+//
+// living/description.c -- description for living objects
+//
+// $Id: description.c 7340 2009-11-19 21:44:51Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <living/description.h>
+#include <living/skills.h>
+#undef NEED_PROTOTYPES
+
+#include <player/viewcmd.h>
+#include <new_skills.h>
+#include <container.h>
+#include <player/base.h>
+#include <wizlevels.h>
+
+inherit "/std/container/light";
+
+protected void create() {
+  ::create();
+  SetProp(P_LIGHT_TRANSPARENCY, 0);
+}
+
+static int _query_player_light()
+{
+  if (environment())
+    return environment()->QueryProp(P_INT_LIGHT) + QueryProp(P_LIGHT_MODIFIER);
+}
+
+varargs int CannotSee(int silent)
+{
+   string is_blind;
+   if (is_blind = QueryProp(P_BLIND)) {
+      if (!silent) {
+         if (stringp(is_blind))
+            tell_object(this_object(), is_blind);
+         else tell_object(this_object(), "Du bist blind!\n");
+      }
+      return 2;
+   }
+   if (UseSkill(SK_NIGHTVISION)<=0 &&
+       environment() && QueryProp(P_PLAYER_LIGHT)<=0 &&
+       (!IS_LEARNER(this_object()) || !Query(P_WANTS_TO_LEARN)))
+   {
+       if (!silent) tell_object(this_object(), "Es ist zu dunkel!\n");
+       return 1;
+   }
+   return 0;
+}
diff --git a/std/living/moneyhandler.c b/std/living/moneyhandler.c
new file mode 100644
index 0000000..7a22226
--- /dev/null
+++ b/std/living/moneyhandler.c
@@ -0,0 +1,26 @@
+// MorgenGrauen MUDlib
+//
+// living/moneyhandler.c -- money handler for livings
+//
+// $Id: moneyhandler.c 6738 2008-02-19 18:46:14Z Humni $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/moneyhandler";
+
+// Funktionen sollen nur das Programm ersetzen, natuerlich nur in der
+// Blueprint _dieses_ Objektes, nicht in anderen. ;-) BTW: Normalerweise
+// sollte niemand hierdrin create() rufen, der das Ding hier erbt.
+protected void create_super() {
+  if (object_name(this_object()) == __FILE__[..<3])
+    replace_program();
+}
+
+// wird nicht von erbenden Objekten gerufen. (Wozu auch.)
+protected void create() {
+  create_super();
+}
+
diff --git a/std/living/moving.c b/std/living/moving.c
new file mode 100644
index 0000000..c6149ff
--- /dev/null
+++ b/std/living/moving.c
@@ -0,0 +1,457 @@
+// MorgenGrauen MUDlib
+//
+// living/moving.c -- moving of living objects
+//
+// $Id: moving.c 9448 2016-01-22 17:52:28Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/moving";
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#include <living/moving.h>
+#include <living/skills.h>
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <moving.h>
+#include <new_skills.h>
+#include <living.h>
+
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+
+protected void create()
+{
+    if (object_name(this_object()) == __FILE__[0..<3])
+    {
+      return;
+    }
+    offerHook(H_HOOK_MOVE,1);
+}
+
+public void AddPursuer(object ob)
+{
+  mixed *pur;
+
+  if (!objectp(ob))
+    return;
+
+  if (!pointerp(pur=Query(P_PURSUERS)))
+    pur=({0,({})});
+  else if (member(pur[1],ob)!=-1)
+    return;
+  
+  SetProp(P_PURSUERS,({ pur[0], pur[1]+({ob})-({0}) }));
+  ob->_SetPursued(ME);
+}
+
+public void RemovePursuer(object ob)
+{
+  mixed *pur;
+
+  if (pointerp(pur=Query(P_PURSUERS,F_VALUE)) 
+      && member(pur[1],ob)!=-1)
+  {
+    pur[1]-=({ob,0});
+    if (ob)
+      ob->_RemovePursued(ME);
+    if (!pur[0]&&!sizeof(pur[1]))
+      pur=0;
+    SetProp(P_PURSUERS,pur);
+  }
+}
+
+public void _SetPursued(object ob)
+{
+  mixed *pur;
+
+  if (!pointerp(pur=Query(P_PURSUERS)))
+    pur=({0,({})});
+  else
+    if (objectp(pur[0]))
+      pur[0]->RemovePursuer(ME);
+  pur[0]=ob;
+  pur[1]-=({0});
+  Set(P_PURSUERS,pur);
+}
+
+public void _RemovePursued(object ob)
+{
+  mixed *pur;
+
+  if (!pointerp(pur=Query(P_PURSUERS)) || pur[0]!=ob)
+    return;
+  pur[0]=0;
+  pur[1]-=({0});
+  if (!sizeof(pur[1]))
+    pur=0;
+  Set(P_PURSUERS,pur);
+}
+
+
+private void kampfende( object en ) {
+  if (!objectp(en)) return;
+  tell_object( ME, capitalize(en->name()) +
+      " ist jetzt hinter Dir her.\n" );
+  tell_object( en, "Du verfolgst jetzt " + name(WEN) + ".\n" );      
+  en->InsertSingleEnemy(ME);
+}
+
+private int _is_learner(object pl) {
+  return IS_LEARNER(pl); 
+}
+
+
+// a) Pruefungen, ob das move erlaubt ist.
+// b) zum Ueberschreiben
+protected int PreventMove(object dest, object oldenv, int method) {
+
+  // M_NOCHECK? -> Bewegung eh erlaubt (und Rueckgabewert wuerde ignoriert),
+  // aber PreventInsert/PreventLeave() rufen und ignorieren.
+  if ((method&M_NOCHECK)) {
+      // erst PreventLeaveLiving() rufen...
+      if(environment())        
+          environment()->PreventLeaveLiving(this_object(), dest);
+      // dann PreventInsertLiving() im Ziel-Env.
+      dest->PreventInsertLiving(this_object());
+      // und raus...
+      return(0);
+  }
+
+  // bei Lebewesen muss die Bewegungsmethode M_GO und M_TPORT sein. Dies ist
+  // gleichzeigt die Restriktion gegen das Nehmen von Lebewesen, da dort
+  // M_GET/M_GIVE/M_PUT etc. verwendet wuerde. Bei M_GO und M_TPORT findet
+  // keine Pruefung statt, ob das Objekt ins Ziel 'reinpasst' (Gewicht, Anzahl
+  // Objekte usw.).
+  // Ich finde es etwas merkwuerdig gebaut (Zesstra).
+  if ( !(method & (M_GO | M_TPORT)) )
+      return ME_PLAYER;
+  
+  // alte und neue Umgebung auf NO_TPORT pruefen.
+  if ( (method & M_TPORT) ) {
+    if ( environment() &&
+        (environment()->QueryProp(P_NO_TPORT) & (NO_TPORT_OUT|NO_TPORT)) )
+          return ME_CANT_TPORT_OUT;
+    else if ( dest->QueryProp(P_NO_TPORT) & (NO_TPORT_IN|NO_TPORT) )
+          return ME_CANT_TPORT_IN;
+  }
+
+  // erst PreventLeaveLiving() testen...
+  if( environment() && environment()->PreventLeaveLiving(this_object(), dest))
+      return ME_CANT_LEAVE_ENV;
+  // dann PreventInsertLiving() im Ziel-Env
+  if (dest->PreventInsertLiving(this_object())) 
+      return ME_CANT_BE_INSERTED;
+
+  return 0;
+}
+
+// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
+protected void NotifyMove(object dest, object oldenv, int method) {
+  mixed res;
+  object enem;
+
+  // Begruessungsschlag fuer die Gegener
+  if ( !(method & M_NO_ATTACK) )
+      InitAttack();
+
+  if (!objectp(ME)) return;
+
+  // Verfolger nachholen.
+  if ( pointerp(res = Query(P_PURSUERS)) && sizeof(res[1]) ) {
+      while ( remove_call_out( "TakeFollowers" ) >= 0 );
+
+      call_out( "TakeFollowers", 0 );
+  }
+
+  // und noch das Team nachholen.
+  if ( oldenv != dest
+      && objectp(ME)
+      && QueryProp(P_TEAM_AUTOFOLLOW)
+      && objectp( enem = IsTeamLeader() ) )
+      enem->StartFollow(oldenv); // Teamverfolgung
+
+}
+
+varargs public int move( object|string dest, int method, string direction,
+                         string textout, string textin )
+{
+    int para, nightvis, invis, tmp;
+    object oldenv, *inv;
+    string fn,vc;
+    mixed res;
+    mixed hookData, hookRes;
+
+    if (!objectp(dest) && !stringp(dest))
+      raise_error(sprintf("Wrong argument 1 to move(). 'dest' must be a "
+            "string or object! Argument was: %.100O\n",
+            dest));
+
+    // altes Env erstmal merken.
+    oldenv = environment();
+    
+    //erstmal den richtigen Zielraum suchen, bevor irgendwelche Checks gemacht
+    //werden...
+    // Ist der Spieler in einer Parallelwelt?
+    if ( (para = QueryProp(P_PARA)) && intp(para) ) {
+        fn = objectp(dest) ? object_name(dest) : dest;
+
+        // Falls der Zielraum nicht schon explizit in der Parallelwelt ist,
+        // neuen Zielraum suchen. Aber nur, wenn fn kein # enthaelt (also kein
+        // Clone ist), sonst wuerde eine Bewegung nach raum#42^para versucht,
+        // was dann buggt. ;-) Problem wird offenbar, wenn ein Para-Lebewesen
+        // im create() eines VC-Raums in Para in den Raum bewegt wird, da
+        // dieser dann noch nicht vom Driver umbenannt wurde und raum#42
+        // heisst.
+        if ( !sizeof(regexp( ({ fn }), "\\^[1-9][0-9]*$" )) &&
+            strrstr(fn,"#")==-1 )
+        {
+            fn += "^" + para;
+
+          // Der Parallelwelt-Raum muss existieren und fuer Spieler
+          // freigegeben sein, damit er zum neuen Ziel wird. Ansonsten
+          // duerfen nur NPCs, Testspieler und Magier herein.
+          if ( (find_object(fn) 
+                || ((file_size(fn+".c")>0 ||
+                    (file_size(vc=implode(explode(fn,"/")[0..<2],"/")+
+                          "/virtual_compiler.c")>0 &&
+                    !catch(tmp=(int)call_other(vc,"QueryValidObject",fn);
+                           publish) && tmp>0)) &&
+                    !catch(load_object(fn);publish) )) &&
+                  (!interactive(ME) || !fn->QueryProp(P_NO_PLAYERS) || 
+                  (method & M_NOCHECK) || IS_LEARNER(ME) ||
+                  (stringp(res = QueryProp(P_TESTPLAYER)) &&
+                   IS_LEARNER( lower_case(res) ))) )
+          {
+              dest = fn;
+          }
+          else
+          {
+              // Wir bleiben in der Normalwelt.
+              para = 0;
+          }
+        }
+    }
+
+    // jetzt erstmal Hooks abpruefen, da sie ggf. die Daten aendern.
+    // alten P_TMP_MOVE_HOOK pruefen.
+    if ( res = QueryProp(P_TMP_MOVE_HOOK) ){
+        if ( pointerp(res) && sizeof(res) >= 3
+             && intp(res[0]) && time()<res[0]
+             && objectp(res[1]) && stringp(res[2]) ){
+            if ( res = call_other( res[1], res[2], dest, method, direction,
+                                   textout, textin ) ){
+                if ( pointerp(res) && sizeof(res) == 5 ){
+                    dest = res[0];
+                    method = res[1];
+                    direction = res[2];
+                    textout = res[3];
+                    textin = res[4];
+                }
+                else if ( intp(res) && res == -1 )
+                    return ME_CANT_LEAVE_ENV;
+            }
+        } else
+            SetProp( P_TMP_MOVE_HOOK, 0 );
+    }
+    // move hook nach neuem Hooksystem triggern.
+    hookData=({dest,method,direction,textout,textin});
+    hookRes=HookFlow(H_HOOK_MOVE,hookData);
+    if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA) {
+      if(hookRes[H_RETCODE]==H_CANCELLED) {
+        return ME_CANT_LEAVE_ENV;
+      }
+    else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] &&
+          pointerp(hookRes[H_RETDATA]) && sizeof(hookRes[H_RETDATA])>=5 ){
+      dest = hookRes[H_RETDATA][0];
+      method = hookRes[H_RETDATA][1];
+      direction = hookRes[H_RETDATA][2];
+      textout = hookRes[H_RETDATA][3];
+      textin = hookRes[H_RETDATA][4];
+      }
+    }
+
+    // dest auf Object normieren
+    if (stringp(dest)) dest=load_object(dest);
+
+    // jetzt Checks durchfuehren, ob das Move durchgefuehrt werden darf.
+    if (tmp=PreventMove(dest, oldenv, method)) {
+      // auf gueltigen Fehler pruefen, wer weiss, was Magier da evtl.
+      // versehentlich zurueckgeben.
+      if (VALID_MOVE_ERROR(tmp))
+        return(tmp);
+      else
+        return(ME_DONT_WANT_TO_BE_MOVED);
+    }
+  
+    if ( invis = QueryProp(P_INVIS) )
+        method |= M_SILENT;
+
+    if ( objectp(oldenv) ) {
+        if ( !(method & M_SILENT) ) {
+            string *mout;
+            if ( !textout ){
+                if ( method & M_TPORT )
+                    textout = (string) QueryProp(P_MMSGOUT) ||
+                        (string) QueryProp(P_MSGOUT);
+                else 
+                    textout = (mout = explode( (string)
+                                                QueryProp(P_MSGOUT) || "",
+                                                      "#" ))[0]
+                         || (string)QueryProp(P_MMSGOUT);
+            }
+
+            if ( !sizeof(direction) )
+                direction = 0;
+
+            inv = all_inventory(environment()) - ({ this_object() });
+            inv = filter( inv, #'living/*'*/ );
+            inv -= filter_objects( inv, "CannotSee", 1 );
+
+            filter( inv, #'tell_object/*'*/,
+                          Name( WER, 2 ) + " " + textout +
+                          (direction ? " " + direction : "") +
+                          (sizeof(mout) > 1 ? mout[1] : "") + ".\n" );
+        }
+        // Magier sehen auch Bewegungen, die M_SILENT sind
+        else if ( interactive(ME) ){
+            inv = (all_inventory(environment()) & users())
+                - ({ this_object() });
+            inv = filter( inv, #'_is_learner/*'*/ );
+
+            if ( invis )
+                fn = "(" + capitalize(getuid(ME)) + ") verschwindet "
+                    "unsichtbar.\n";
+            else
+                fn = capitalize(getuid(ME)) + " verschwindet ganz leise.\n";
+            
+            filter( inv, #'tell_object/*'*/, fn );
+        }
+        
+        // Nackenschlag beim Fluechten:
+        if ( !(method & M_NO_ATTACK) && objectp(ME) )
+            ExitAttack();
+        //falls nach ExitAttack() das Living nicht mehr existiert, muss das
+        //move() auch nicht mehr fortgesetzt werden. Weiter unten gibt es auch
+        //min. eine Stelle, die nicht prueft und ggf. buggt. Daher erfolgt
+        //hier ggf. ein Abbruch. 15.11.06 Zesstra
+        if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
+
+        // Nackenschlag kann ME in den Todesraum bewegt haben...
+        if ( oldenv == environment() ) {
+            // Fuer alle anwesenden gegner kampfende() aufrufen
+            filter((QueryEnemies()[0] & all_inventory(oldenv))-({0}),
+                #'kampfende);
+            // Bugs im exit() sind ohne catch() einfach mist.
+            catch(environment()->exit(ME, dest);publish);
+        }
+    }
+
+    // irgendwas kann das Objekt zerstoert haben, z.B. env->exit().
+    if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
+
+    if ( oldenv != environment() )
+        // Der Nackenschlag oder exit() koennen einen schon bewegt haben.
+        // Und wenn es in den Todesraum ist. ;^)
+        return MOVE_OK;
+    
+    SetProp( P_PREPARED_SPELL, 0 ); // Spruchvorbereitung abgebrochen
+    SetProp( P_LAST_MOVE, time() ); // Zeitpunkt der letzten Bewegung
+    
+    move_object(ME, dest);
+    if (!objectp(ME))
+      return(ME_WAS_DESTRUCTED);
+
+    dest = environment();
+    
+    nightvis = UseSkill(SK_NIGHTVISION);
+    // Meldungen an nicht-Blinde ausgeben, falls keine 'stille' Bewegung
+    if ( !(method & M_SILENT) ) {
+      if ( !textin ) {        
+        if ( method & M_TPORT )
+              textin = (string) QueryProp(P_MMSGIN);
+        else
+              textin = (string) QueryProp(P_MSGIN);
+      }
+            
+      inv = all_inventory(environment()) - ({ this_object() });
+      inv = filter( inv, #'living/*'*/ );
+            inv -= filter_objects( inv, "CannotSee", 1 );
+            filter( inv, #'tell_object/*'*/,
+                          capitalize(name( WER, 0 )) + " " + textin + ".\n" );
+    }
+    // sonst: Magier sehen auch M_SILENT-Bewegungen, hier also nur an Magier
+    // ausgeben, alle anderen sehen eh nix.
+    else if ( interactive(ME) ) {  
+      inv = (all_inventory(environment()) & users()) - ({this_object()});        
+      inv = filter( inv, #'_is_learner/*'*/ );        
+      if ( invis )
+        fn = "(" + capitalize(getuid(ME)) + ") taucht unsichtbar auf.\n";
+      else
+        fn = capitalize(getuid(ME)) + " schleicht leise herein.\n";        
+      filter( inv, #'tell_object, fn );    
+    }
+
+    // "Objekt" ueber das Move informieren.
+    NotifyMove(dest, oldenv, method);
+
+    // InitAttack() in NotifyMove() kann das Objekt zerstoert haben.
+    if (!objectp(ME))
+      return(ME_WAS_DESTRUCTED);
+
+    //scheint wohl geklappt zu haben.
+    return MOVE_OK;
+}
+
+public void TakeFollowers()
+{
+  mixed *f,env;
+  int meth,i,r;
+
+  f=Query(P_PURSUERS);
+  if (!pointerp(f))
+    return;
+  env=environment();
+  if(object_name(env) == "/room/netztot") return;
+  foreach(object follower: f[1]-({0}) ) {
+    // die pruefung auf objectp ist nicht verrueckt, es kann theo. sein, dass
+    // Verfolger im PreventFollow() oder in ihrem move/init andere Verfolger
+    // zerstoeren.
+    if (objectp(follower) && environment(follower)!=env) {
+      //meth=M_NOCHECK;
+      meth=M_GO;
+      if (follower->Query(P_FOLLOW_SILENT))
+          meth|=M_SILENT|M_NO_SHOW;
+      catch(r=follower->PreventFollow(env);publish);
+      if (!r)
+          follower->move(env,meth);
+      else if (r==2)
+          RemovePursuer(follower);
+    }
+  }
+}
+
+varargs public int remove()
+{ object team;
+
+  if (environment())
+  {
+    if ( objectp(team=Query(P_TEAM)) )
+      catch(team->RemoveMember(ME);publish);
+
+    environment()->NotifyRemove(ME);
+  }
+  destruct(ME);
+  return 1;
+}
+
diff --git a/std/living/put_and_get.c b/std/living/put_and_get.c
new file mode 100644
index 0000000..4e1a97f
--- /dev/null
+++ b/std/living/put_and_get.c
@@ -0,0 +1,1243 @@
+// MorgenGrauen MUDlib
+//
+// living/put_and_get.c -- taking and putting things
+//
+// $Id: put_and_get.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+/*
+  Grundlegend neu strukturiert von Amynthor im April-Juni 2007
+
+Die eigentlichen Funktionen:
+
+  private string put_or_get(object o, object dest)
+    Bewegt ein einzelnes Objekt mit automatisch bestimmter Method. Gibt im
+    Erfolgsfall 0 zurueck, sonst die auszugebende Fehlermeldung.
+
+  varargs int drop(object o, mixed msg)
+  varargs int put(object o, object dest, mixed msg)
+  varargs int pick(object o, mixed msg)
+  varargs int give(object o, object dest, mixed msg)
+  varargs int show(object o, object dest, mixed msg)
+    Der Spieler nimmt/legt/gibt/zeigt/laesst ein Objekt fallen, wobei die
+    entsprechenden oder optional abweichende (im Format von P_XXX_MSG) oder
+    gar keine (msg == 1) Meldungen ausgegeben werden. Es liegt in der
+    Verantwortung des Rufenden, sinnvolle Werte zu uebergeben; insbesondere
+    wird nicht geprueft, ob sich die Objekte in der Reichweite des Spielers
+    befinden. Gibt 1 zurueck, wenn das Objekt bewegt wurde, sonst 0.
+
+Hilfsfunktionen:
+
+  private object *__find_objects(string *tokens, object env, int is_source)
+  object *find_objects(string what, object env, int is_source)
+    Sucht im Raum und im Spieler (oder alternativ in der angegebenen Umgebung)
+    nach den in tokens/what bezeichneten Objekten. is_source bestimmt die
+    erwartete grammatische Form (0 fuer "topf auf herd" und 1 fuer "topf von
+    herd", siehe Manpage).
+
+  varargs int drop_objects(string str, mixed msg)
+  varargs int put_objects(string str, int casus, string verb, mixed msg)
+  varargs int pick_objects(string str, mixed msg, int flag)
+  varargs int give_objects(string str, mixed msg)
+  varargs int show_objects(string str, mixed msg)
+    Ein Befehl wie "wirf waffen weg" resultiert in einem Aufruf von
+    drop_objects("waffen"). Diese Funktionen sind hauptsaechlich fuer die
+    Behandlung der jeweiligen Kommandos vorgesehen, koennen jedoch auch fuer
+    eigene Befehle verwendet werden. put_objects() erwartet ausserdem den
+    Kasus ("Du kannst nichts an DER Pinwand befestigen.") und das verwendete
+    Verb in der Gundform ("befestigen"). Das Flag fuer pick_objects() gibt
+    an, ob das Objekt auch einfach herumliegen darf ("nimm ...") oder nicht
+    ("hole ..."). Gibt bei Erfolg 1, sonst 0 zurueck.
+
+  object *moved_objects()
+  object moved_where()
+    Gibt die eben fallengelassenen/gesteckten/... Objekte zurueck und wohin
+    sie gesteckt/wem sie gegeben/gezeigt wurden. Fuer den Fall, dass man
+    anschliessend noch etwas mit ihnen machen moechte.
+
+Die einzelnen Kommandos:
+  static int fallenlassen(string str)
+  static int werfen(string str)
+  static int legen(string str)
+  static int stecken(string str)
+  static int holen(string str)
+  static int nehmen(string str)
+  static int geben(string str)
+    Minimale Wrapper fuer XXX_objects(), entfernen "fallen", "weg" bzw. "ab"
+    aus den Argumenten und setzen entsprechende Standard-Fehlermeldungen.
+    
+  protected void add_put_and_get_commands()
+    Registriert obige Funktionen per add_action().
+
+Aus reinen Kompatibilitaetsgruenden weiterhin enthalten:
+
+  object* find_obs(string str, int meth)
+  int pick_obj(object ob)
+  int drop_obj(object ob)
+  int put_obj(object ob, object where)
+  int give_obj(object ob, object where)
+    siehe Manpages
+
+*/
+
+/*
+  21. Okt 1998 komplette neu programmierung von put_and_get.c (Padreic)
+- die Gruppenauswahlen alles, waffen und ruestungen sind jetzt immer moeglich
+  die Gruppen sind sehr leicht erweiterbar und man sollte sich nicht scheuen
+  davon gebrauch zu machen...
+- mit "in mir" und "im raum" kann man den abzusuchenden Raum selbst eingrenzen
+- mit "alle|jede|jeden|jedes <id>" kann man auch ganze objektgruppen auswaehlen
+*/
+
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <language.h>
+#include <thing/description.h>
+#include <thing/properties.h>
+#include <moving.h>
+#include <container.h>
+#undef NEED_PROTOTYPES
+
+#include <defines.h>
+#include <properties.h>
+#include <wizlevels.h>
+
+#define TME(str) tell_object(this_object(), \
+    break_string(str, 78, 0, BS_LEAVE_MY_LFS))
+#define TOB(ob,str) tell_object(ob, break_string(str, 78, 0, BS_LEAVE_MY_LFS))
+#define SAY(str) tell_room(environment(), \
+    break_string(str, 78, 0, BS_LEAVE_MY_LFS), ({this_object()}))
+#define SAY2(obs, str) tell_room(environment(), \
+    break_string(str, 78, 0, BS_LEAVE_MY_LFS), ({this_object()}) + obs)
+#define NF(str) _notify_fail(break_string(str, 78, 0, BS_LEAVE_MY_LFS))
+
+private nosave closure cl;
+private nosave string wen0, wen1, wer0;
+private nosave object *last_moved_objects;
+private nosave object last_moved_where;
+
+
+/*********************** Die eigentlichen Funktionen ************************/
+
+private string put_or_get(object o, object dest)
+
+/* Bewegt ein einzelnes Objekt <o> in das Zielobjekt <dest>. Verwendet dazu
+ * je nach Umstaenden (Ziel ist der Spieler, die Umgebung, ein Lebewesen) den
+ * entsprechenden Wert fuer <method>. Gibt im Erfolgsfall 0 zurueck, erstellt
+ * sonst die auszugebende Fehlermeldung und gibt diese zurueck.
+ */
+
+{
+    int method, ret;
+    string str;
+
+    //if (living(o))
+    //    raise_error(sprintf("Lebendes Argument fuer put_or_get: %O\n", o));
+
+    if (dest == this_object())          /* pick */
+        method = M_GET;
+    else if (dest == environment())     /* drop */
+        method = M_PUT;
+    else if (living(dest))              /* give */
+        method = M_GIVE;
+    else {                              /* put */
+        method = M_PUT | M_GET;
+        if (first_inventory(o))
+            return o->Name(WER, 1) + " ist nicht leer!";
+    }
+
+    if ((ret = o->move(dest, method)) > 0)
+        return 0;
+
+    switch (ret) {
+        case ME_TOO_HEAVY:
+            if (dest == this_object())
+                if (QueryProp(P_GHOST))
+                    return "Als Geist kannst Du nichts mitnehmen.";
+                else
+                    return "Du kannst " + wen1 + " nicht mehr tragen.";
+
+            if (stringp(str = dest->QueryProp(P_TOO_HEAVY_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (living(dest)) {
+                if (dest->QueryProp(P_GHOST))
+                    return "Als Geist kann " + dest->name(WER, 1) +
+                           " nichts mitnehmen.";
+                else
+                    return dest->Name(WER, 1) + " kann " +
+                           wen0 + " nicht mehr tragen.";
+            }
+
+            if (dest == environment())
+                return (stringp(str = dest->Name(WER, 1)) && sizeof(str) ?
+                        str : "Der Raum") + " wuerde dann zu schwer werden.";
+
+            return capitalize(wer0 + " passt in " + dest->name(WEN, 1) +
+                              " nicht mehr rein.");
+
+        case ME_CANT_BE_DROPPED:
+            if (o && stringp(str = o->QueryProp(P_NODROP)))
+                return str;
+
+            if (dest == environment())
+                return "Du kannst " + wen1 + " nicht wegwerfen!";
+
+            if (living(dest))
+                return "Du kannst " + wen1 + " nicht weggeben.";
+
+            return "So wirst Du " + wen1 + " nicht los...";
+
+        case ME_CANT_BE_TAKEN:
+            if (o && stringp(str = o->QueryProp(P_NOGET)))
+                return str;
+
+            if (stringp(str = environment(o)->QueryProp(P_NOLEAVE_MSG)))
+                return capitalize(
+                    replace_personal(str, ({o, environment(o)}), 1));
+
+            //if (dest != environment())
+            //    return "Du kannst " + wen1 + " nicht einmal nehmen.";
+
+            return "Du kannst " + wen1 + " nicht nehmen.";
+
+        case ME_CANT_BE_INSERTED:
+            if (stringp(str = dest->QueryProp(P_NOINSERT_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (dest == environment())
+                return "Das darfst Du hier nicht ablegen.";
+
+            if (dest == this_object())
+                return "Du kannst " + wen1 + " nicht nehmen.";
+
+            if (living(dest))
+                return "Das kannst Du " + dest->name(WEM, 1) + " nicht geben.";
+
+            return capitalize(wen0 + " kannst Du dort nicht hineinstecken.");
+
+        case ME_CANT_LEAVE_ENV:
+            // ME_CANT_LEAVE_ENV kann nur auftreten, wenn o ein Environment
+            // hat, deshalb kein Check dadrauf
+            if (stringp(str = environment(o)->QueryProp(P_NOLEAVE_MSG)))
+                return capitalize(
+                    replace_personal(str, ({o, environment(o)}), 1));
+
+            if (environment(o) != this_object())
+                return "Du kannst " + wen1 + " nicht nehmen.";
+
+            if (dest == environment())
+                return "Du kannst " + wen1 + " nicht wegwerfen!";
+
+            if (living(dest))
+                return "Du kannst " + wen1 + " nicht weggeben.";
+
+            return "So wirst Du " + wen1 + " nicht los...";
+
+        case ME_TOO_HEAVY_FOR_ENV:
+            if (stringp(str = dest->QueryProp(P_ENV_TOO_HEAVY_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (environment(dest) == this_object())
+                return dest->Name(WER, 1) +
+                       " wuerde Dir dann zu schwer werden.";
+
+            return (stringp(str = environment(dest)->Name(WER, 1))
+                        && sizeof(str) ? str : "Der Raum") +
+                    " wuerde dann zu schwer werden.";
+
+        case TOO_MANY_OBJECTS:
+            if (stringp(str = dest->QueryProp(P_TOO_MANY_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (dest == this_object())
+                return "Soviele Gegenstaende kannst Du unmoeglich tragen!";
+
+            if (dest == environment())
+                return "Dafuer ist hier nicht mehr genug Platz.";
+
+            if (living(dest))
+                return dest->Name(WER, 1) + " kann "  + wen0 +
+                       " nicht mehr tragen.";
+
+            return "Dafuer ist nicht mehr genug Platz in " +
+                   dest->name(WEM, 1) + ".";
+
+        default:
+            if (dest == this_object())
+                return "Du kannst " + wen1 + " nicht nehmen.";
+
+            if (dest == environment())
+                return "Du kannst " + wen1 + " nicht wegwerfen!";
+
+            if (living(dest))
+                return "Du kannst " + wen1 + " nicht weggeben.";
+
+            return capitalize(wen0 + " kannst Du dort nicht hineinstecken.");
+    }
+    return 0; // NOT REACHED
+}
+
+
+/* varargs int drop(object o, mixed msg)
+ * varargs int put(object o, object dest, mixed msg)
+ * varargs int pick(object o, mixed msg)
+ * varargs int give(object o, object dest, mixed msg)
+ * varargs int show(object o, object dest, mixed msg)
+ *
+ * Der Spieler nimmt/legt/gibt/zeigt/laesst ein Objekt fallen, wobei die
+ * entsprechenden oder optional abweichende (im Format von P_XXX_MSG) oder
+ * gar keine (msg == 1) Meldungen ausgegeben werden. Es liegt in der
+ * Verantwortung des Rufenden, sinnvolle Werte zu uebergeben; insbesondere
+ * wird nicht geprueft, ob sich die Objekte in der Reichweite des Spielers
+ * befinden. Gibt 1 zurueck, wenn das Objekt bewegt wurde, sonst 0.
+ */
+
+varargs int drop(object o, mixed msg)
+{
+    string str;
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = funcall(cl, WEN, 0);
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = 0;
+
+    if (!msg)
+        msg = o->QueryProp(P_DROP_MSG);
+
+    if (str = put_or_get(o, environment())) {
+        TME(str);
+        return 0;
+    }
+
+    if (!msg) {
+        TME("Du laesst " + wen1 + " fallen.");
+        SAY(Name(WER,1) + " laesst " + wen0 + " fallen.");
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          // Wenn es zwei Strings gibt, geht die 2. ans Environment
+          case 2:
+            SAY(replace_personal(msg[1], ({this_object(), o||wen0}), 1));
+          case 1:
+            TME(replace_personal(msg[0], ({this_object(), o||wen1}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_DROP_MSG: %O\n", o||wen1));
+        }
+
+    return 1;
+}
+
+varargs int put(object o, object dest, mixed msg)
+{
+    string str;
+
+    // Falls das jemand von aussen ruft und Schrott uebergibt...
+    //if (living(dest))
+    //    raise_error(sprintf("Lebendes Ziel fuer put(): %O\n", dest));
+    if (dest == environment())
+        raise_error("Ziel fuer put() ist Umgebung des Spielers\n");
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = funcall(cl, WEN, 0);
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = funcall(cl, WER, 0);
+
+    if (!msg)
+        msg = o->QueryProp(P_PUT_MSG);
+
+    if (str = put_or_get(o, dest)) {
+        TME(str);
+        return 0;
+    }
+
+  
+    if (!msg) {
+        TME("Du steckst " + wen1 + " in " + dest->name(WEN, 1) + ".");
+        if (environment())
+	  SAY(Name(WER, 1) + " steckt " + wen0 +
+	      " in " + dest->name(WEN, 0) + ".");
+    }
+    else if (pointerp(msg)) {
+        switch (sizeof(msg)) {
+          case 2:
+            if (environment())
+	      SAY(replace_personal(msg[1], ({this_object(), o||wen0, dest}), 1));
+          case 1:
+            TME(replace_personal(msg[0], ({this_object(), o||wen1, dest}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_PUT_MSG: %O\n",o||wen1));
+        }
+    }
+
+    return 1;
+}
+
+varargs int pick(object o, mixed msg)
+{
+    string str;
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = 0;
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = 0;
+
+    if (!msg)
+        msg = o->QueryProp(P_PICK_MSG);
+
+    if (str = put_or_get(o, this_object())) {
+        TME(str);
+        return 0;
+    }
+
+    if (!msg) {
+        TME("Du nimmst " + wen1 + ".");
+        SAY(Name(WER, 1) + " nimmt " + wen1 + ".");
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          case 2:
+            SAY(replace_personal(msg[1], ({this_object(), o||wen1}), 1));
+          case 1:
+            TME(replace_personal(msg[0], ({this_object(), o||wen1}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_PICK_MSG: %O\n", o||wen1));
+        }
+
+    return 1;
+}
+
+varargs int give(object o, object dest, mixed msg)
+{
+    string zname, gname;
+    string str;
+
+    // Falls das jemand von aussen ruft und Schrott uebergibt...
+    if (!living(dest))
+        raise_error(sprintf("Totes Ziel fuer give(): %O\n", dest));
+
+    zname = dest->name(WEM, 1);
+    gname = Name(WER, 1);
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = funcall(cl, WEN, 0);
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = 0;
+
+    if (!msg)
+        msg = o->QueryProp(P_GIVE_MSG);
+
+    if (str = put_or_get(o, dest)) {
+        TME(str);
+        return 0;
+    }
+
+    if (!msg) {
+        TME("Du gibst " + zname + " " + wen1 + ".");
+        TOB(dest, gname + " gibt Dir " + wen0 + ".");
+        SAY2(({dest}), gname + " gibt " + zname + " " + wen0 + ".");
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          case 3:
+            TOB(dest, replace_personal(
+                msg[2], ({this_object(), o||wen0, dest||zname}), 1));
+          case 2:
+            SAY2(({dest, this_object()}), replace_personal(
+                 msg[1], ({this_object(), o||wen0, dest||zname}), 1));
+          case 1:
+            TME(replace_personal(
+                msg[0], ({this_object(), o||wen1, dest||zname}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_GIVE_MSG: %O\n", o||wen1));
+        }
+
+    if (!query_once_interactive(dest))
+        dest->give_notify(o);
+
+    return 1;
+}
+
+varargs int show(object o, object whom, mixed msg)
+{
+    string zname, gname;
+    string wen0, wen2, long;
+
+    zname = whom ? whom->name(WEM, 1) : "allen";
+    gname = Name(WER, 1);
+
+    if (!msg)
+        msg = o->QueryProp(P_SHOW_MSG);
+
+    if (environment(o) == this_object() ||
+        environment(environment(o)) == this_object()) {
+        wen0 = o->name(WEN, 0);
+
+        /* Der Akkusativ muss mit dem unbestimmten Artikel gebildet werden,
+         * damit eventuelle Adjektive die richtige Endung besitzen.
+         * (ein kleines Schwert -> kleines Schwert -> Dein kleines Schwert)
+         */
+
+        if (o->QueryProp(P_ARTICLE) && member(wen0, ' ') >= 0) {
+            int obgender = o->QueryProp(P_GENDER);
+            int obnum = o->QueryProp(P_AMOUNT) > 1 ? PLURAL : SINGULAR;
+
+            // Wichtig: P_AMOUNT ist 0 fuer Objekte, die nicht von unit erben.
+            // Da unit.c kein P_AMOUNT==0 zulaesst, nehmen wir diesen Fall als
+            // singular an. *Rumata
+
+            wen2 = wen0[member(wen0, ' ')..];
+            wen0 = QueryPossPronoun(o, WEN, obnum) + wen2;
+
+            if (obnum == PLURAL || obgender == FEMALE)
+                wen2 = "Deine" + wen2;
+            else if (obgender == MALE)
+                wen2 = "Deinen" + wen2;
+            else
+                wen2 = "Dein" + wen2;
+        } else
+            wen2 = wen0;
+    } else
+        wen2 = wen0 = o->name(WEN, 1);
+
+    // vorher speichern, falls das Objekt im catch_tell() zerstoert wird
+    long = o->long(4);
+
+    if (!msg) {
+        TME("Du zeigst " + zname + " " + wen2 + ".");
+        if (!whom)
+            SAY(gname + " zeigt Dir " + wen0 + ".");
+        else {
+            TOB(whom, gname + " zeigt Dir " + wen0 + ".");
+            SAY2(({whom}), gname + " zeigt " + zname + " " + wen0 + ".");
+        }
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          case 3:
+            if (whom)
+                TOB(whom, replace_personal(
+                    msg[2], ({this_object(), o||wen0, whom||zname}), 1));
+            else
+                SAY(replace_personal(
+                    msg[2], ({this_object(), o||wen0, whom||zname}), 1));
+          case 2:
+            if (whom)
+                SAY2(({whom, this_object()}), replace_personal(
+                     msg[1], ({this_object(), o||wen0, whom||zname}), 1));
+          case 1:
+            TME(replace_personal(
+                msg[0], ({this_object(), o||wen2, whom||zname}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_SHOW_MSG: %O\n", o||wen0));
+        }
+
+    if (!whom)
+        SAY(long);
+    else {
+        TOB(whom, long);
+        if (!query_once_interactive(whom))
+            whom->show_notify(o);
+    }
+
+    return 1;
+}
+
+
+/***************************** Hilfsfunktionen *****************************/
+
+/* private object *__find_objects(string *tokens, object env, int is_source);
+ * object *find_objects(string what, object env, int is_source);
+ *
+ * Sucht im Raum und im Spieler (oder alternativ in der angegebenen Umgebung)
+ * nach den in tokens/what bezeichneten Objekten. is_source bestimmt die
+ * erwartete grammatische Form (0 fuer "topf auf herd" und 1 fuer "topf von
+ * herd", siehe Manpage).
+ */
+ 
+private object *__find_objects(string *tokens, object env, int is_source)
+{
+    object ob, *obs;
+
+    // is_source == 0: Objekt soll nicht bewegt werden ("topf auf herd")
+    //              1: Objekt soll bewegt werden ("topf von herd")
+    //              2: intern
+
+    if (!env && sizeof(tokens) > 1 && tokens[<1] == "hier") {
+        tokens = tokens[..<2];
+        env = environment();
+    }
+    else if (!env && sizeof(tokens) > 2 && tokens[<2] == "in")
+    {
+        if (tokens[<1] == "mir" ||
+            tokens[<1] == "dir") {
+            tokens = tokens[..<3];
+            env = this_object();
+        }
+        else if (tokens[<1] == "raum") {
+            tokens = tokens[..<3];
+            env = environment();
+        }
+    }
+
+    for (int i = sizeof(tokens)-1; i > 1; i--) {
+        if (env)
+            ob = present(implode(tokens[i..], " "), env);
+        else
+            ob = present(implode(tokens[i..], " "), environment()) ||
+                 present(implode(tokens[i..], " "), this_object());
+
+        if (!ob)
+            continue;
+
+        if (living(ob)) {
+            NF("Aber " + ob->name(WER, 1) + " lebt doch!");
+            continue;
+        }
+
+        if (ob->QueryProp(P_CNT_STATUS)) {
+            NF("Aber " + ob->name(WER, 1) + " ist doch geschlossen!");
+            continue;
+        }
+
+        if (is_source != 0 &&
+            tokens[i-1] == ob->QueryProp(P_SOURCE_PREPOSITION))
+            return ob->present_objects(implode(tokens[..i-2], " "));
+
+        if (tokens[i-1] == ob->QueryProp(P_PREPOSITION))
+            return __find_objects(tokens[..i-2], ob, is_source ? 2 : 0);
+
+        NF("Du kannst nichts " + tokens[i-1] + " " +
+           ob->name(WEM, 1) + " nehmen.");
+    }
+
+    if (is_source == 2)
+        return ({});
+
+    if (env)
+        return env->present_objects(implode(tokens, " "));
+
+    if (environment() &&
+        sizeof(obs = environment()->present_objects(implode(tokens, " "))))
+        return obs;
+
+    return present_objects(implode(tokens, " "));
+}
+
+object *find_objects(string what, object env, int is_source)
+{
+  if (!stringp(what) || !sizeof(what))
+    return ({});
+  return __find_objects(explode(what, " "), env, is_source);
+}
+
+
+/* varargs int drop_objects(string str, mixed msg);
+ * varargs int put_objects(string str, int casus, string verb, mixed msg);
+ * varargs int pick_objects(string str, int flag, mixed msg);
+ * varargs int give_objects(string str, mixed msg);
+ * varargs int show_objects(string str, mixed msg);
+ *
+ * Ein Befehl wie "wirf waffen weg" resultiert in einem Aufruf von
+ * drop_objects("waffen"). Diese Funktionen sind hauptsaechlich fuer die
+ * Behandlung der jeweiligen Kommandos vorgesehen, koennen jedoch auch fuer
+ * eigene Befehle verwendet werden. put_objects() erwartet ausserdem den
+ * Kasus ("Du kannst nichts an DER Pinwand befestigen.") und das verwendete
+ * Verb in der Gundform ("befestigen"). Das Flag fuer pick_objects() gibt
+ * an, ob das Objekt auch einfach herumliegen darf ("nimm ...") oder nicht
+ * ("hole ..."). Gibt bei Erfolg 1, sonst 0 zurueck.
+ */
+
+varargs int drop_objects(string str, mixed msg)
+{
+    object *obs;
+
+    if (!sizeof(obs = find_objects(str, this_object(), 1)))
+        return 0;
+
+    foreach (object o: obs) {
+        if (objectp(o))
+            drop(o, msg);
+
+        if (get_eval_cost() < 100000) {
+            TME("Den Rest behaeltst Du erst mal.");
+            last_moved_objects = obs[..member(obs, o)];
+            last_moved_where = 0;
+            return 1;
+        }
+    }
+
+    last_moved_objects = obs;
+    last_moved_where = 0;
+    return 1;
+}
+
+varargs int put_objects(string str, int casus, string verb, mixed msg)
+{
+    object *obs, dest, *no_move;
+    
+    if (!stringp(str) || !sizeof(str)) return 0;
+
+    string *tokens = explode(str, " ");
+    int allow_room = 1;
+    int allow_me = 1;
+
+    if (sizeof(tokens) > 1 && tokens[<1] == "hier") {
+        tokens = tokens[..<2];
+        allow_me = 0;
+    } else if (sizeof(tokens) > 2 && tokens[<2] == "in")
+        if (tokens[<1] == "mir" ||
+            tokens[<1] == "dir") {
+            tokens = tokens[..<3];
+            allow_room = 0;
+        } else if (tokens[<1] == "raum") {
+            tokens = tokens[..<3];
+            allow_me = 0;
+        }
+
+    for (int i = sizeof(tokens)-1; i > 1; i--) {
+        if (!(dest = allow_room && present(implode(tokens[i..], " "),
+                                           environment())) &&
+            !(dest = allow_me && present(implode(tokens[i..], " "),
+                                         this_object())))
+            continue;
+
+        if (living(dest)) {
+            NF("Aber " + dest->name(WER, 1) + " lebt doch!");
+            continue;
+        }
+/*
+        if (verb == "legen" && !dest->QueryProp(P_TRAY)) {
+            NF("Du kannst nichts auf " + dest->name(WEN, 1) + " legen.");
+            continue;
+        }
+*/
+        if (verb == "stecken" && !dest->QueryProp(P_CONTAINER)) {
+            NF("Du kannst in " + dest->name(WEN, 1) + " nichts reinstecken.");
+            continue;
+        }
+
+        if (dest->QueryProp(P_CNT_STATUS)) {
+            NF("Aber " + dest->name(WER, 1) + " ist doch geschlossen!");
+            continue;
+        }
+
+        if (tokens[i-1] != dest->QueryProp(P_DEST_PREPOSITION)) {
+            NF("Du kannst nichts " + tokens[i-1] + " " +
+               dest->name(casus, 1) + " " + verb + ".");
+            continue;
+        }
+
+        if (!sizeof(obs = __find_objects(tokens[..i-2], 0, 1) - ({ dest }))) {
+            NF("WAS moechtest Du " + tokens[i-1] + " " +
+               dest->name(casus, 1) + " " + verb + "?");
+            return 0;
+        }
+
+        if (sizeof(no_move = obs & all_inventory(dest))) {
+            TME(capitalize(CountUp(map_objects(no_move, "name", WER, 1))) +
+                (sizeof(no_move) == 1 ? " ist" : " sind") +
+                " doch bereits in " + dest->name(WEM,1) + ".");
+            if (!sizeof(obs -= no_move))
+                return 0;
+        }
+
+        foreach (object o: obs) {
+            if (objectp(o))
+                put(o, dest, msg);
+
+            if (get_eval_cost() < 100000) {
+                TME("Den Rest laesst Du erst mal, wo er ist.");
+                last_moved_objects = obs[..member(obs, o)];
+                last_moved_where = dest;
+                return 1;
+            }
+        }
+
+        last_moved_objects = obs;
+        last_moved_where = dest;
+        return 1;
+    }
+    
+    return 0;
+}
+
+varargs int pick_objects(string str, int flag, mixed msg)
+{
+    object *obs;
+
+    if (((int)QueryProp(P_MAX_HANDS)) < 1){
+        NF("Ohne Haende kannst Du nichts nehmen.");
+        return 0;
+    }
+
+    if (!sizeof(obs = find_objects(str, 0, 1) - all_inventory()
+	  - (flag ? all_inventory(environment()) : ({}))))
+        return 0;
+
+    foreach (object o: obs) {
+        if (objectp(o))
+            pick(o, msg);
+
+        if (get_eval_cost() < 100000) {
+            TME("Den Rest laesst Du erst mal liegen.");
+            last_moved_objects = obs[..member(obs, o)];
+            last_moved_where = 0;
+            return 1;
+        }
+    }
+
+    last_moved_objects = obs;
+    last_moved_where = 0;
+    return 1;
+}
+
+varargs int give_objects(string str, mixed msg)
+{
+    object *obs, dest;
+    
+    if (!stringp(str) || !sizeof(str)) return 0;
+
+    string *tokens = explode(str, " ");
+
+    if (((int)QueryProp(P_MAX_HANDS)) < 1){
+        NF("Ohne Haende kannst Du nichts weggeben.");
+        return 0;
+    }
+
+    for (int i = 0; i < sizeof(tokens)-1; i++) {
+        if (!(dest = present(implode(tokens[..i], " "), environment())))
+            continue;
+
+        if (!living(dest)) {
+            NF("Aber " + dest->name(WER, 1) + " lebt doch gar nicht!");
+            dest = 0;
+            continue;
+        }
+
+        if (!sizeof(obs = __find_objects(tokens[i+1..], 0, 1))) {
+            NF("WAS moechtest Du " + dest->name(WEM, 1)+" geben?");
+            dest = 0;
+        } else
+            break;
+    }
+
+    if (!dest) {
+        int pos;
+
+        if ((pos = strrstr(str, " an ")) >= 0) {
+            dest = present(str[pos+4..], environment());
+            // zu gebende Objekte in Env + Living suchen
+            obs = find_objects(str[..pos-1], 0, 1);
+        }
+    }
+
+    if (!dest || !living(dest) || !sizeof(obs))
+        return 0;
+
+    foreach (object o: obs) {
+        if (objectp(o))
+            give(o, dest, msg);
+
+        if (get_eval_cost() < 100000) {
+            TME("Den Rest behaeltst Du erst mal.");
+            last_moved_objects = obs[..member(obs, o)];
+            last_moved_where = dest;
+            return 1;
+        }
+    }
+
+    last_moved_objects = obs;
+    last_moved_where = dest;
+    return 1;
+}
+
+varargs int show_objects(string str, mixed msg)
+{
+    object *obs, whom;
+    
+    if (!stringp(str) || !sizeof(str))
+      return 0;
+
+    string *tokens = explode(str, " ");
+
+    for (int i = 0; i < sizeof(tokens)-1; i++) {
+        if (whom = present(implode(tokens[..i], " "), environment())) {
+            if (!living(whom)) {
+                NF("Aber " + whom->name(WER, 1) + " lebt doch gar nicht!");
+                continue;
+            }
+        } else {
+            if (i != 0 || tokens[0] != "allen")
+                continue;
+
+            if (!sizeof(filter(all_inventory(environment()) -
+                    ({ this_object() }), #'living))) {
+                NF("Hier ist niemand, dem Du etwas zeigen koenntest!");
+                continue;
+            }
+        }
+
+        if (whom == this_object()) {
+            NF("Dazu solltest Du dann besser 'schau' benutzen!\n");
+            continue;
+        }
+
+        if (!sizeof(obs = __find_objects(tokens[i+1..], this_object(), 0)) &&
+            !sizeof(obs = __find_objects(tokens[i+1..], 0, 0)
+                            - ({ this_object(), whom }))) {
+            NF("WAS moechtest Du " + (whom ? whom->name(WEM, 1) : "allen") +
+               " zeigen?");
+            continue;
+        }
+
+        foreach (object o: obs) {
+            if (objectp(o))
+                show(o, whom, msg);
+
+            if (get_eval_cost() < 100000) {
+                TME("Das reicht erst mal.");
+                last_moved_objects = obs[..member(obs, o)];
+                last_moved_where = whom;
+                return 1;
+            }
+        }
+
+        last_moved_objects = obs;
+        last_moved_where = whom;
+        return 1;
+    }
+
+    return 0;
+}
+
+object *moved_objects(void)
+{
+    return last_moved_objects;
+}
+
+object moved_where(void)
+{
+    return last_moved_where;
+}
+
+
+/************************* Die einzelnen Kommandos **************************/
+
+/* static int fallenlassen(string str)
+ * static int werfen(string str)
+ * static int legen(string str)
+ * static int stecken(string str)
+ * static int holen(string str)
+ * static int nehmen(string str)
+ * static int geben(string str)
+ *   Minimale Wrapper fuer XXX_objects(), entfernen "fallen", "weg" bzw. "ab"
+ *   aus den Argumenten und setzen entsprechende Standard-Fehlermeldungen.
+ *   
+ * protected void add_put_and_get_commands()
+ *   Registriert obige Funktionen per add_action().
+ */
+
+static int fallenlassen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts fallenlassen.\n");
+        return 0;
+    }
+
+    if (!str || str[<7..] != " fallen") {
+        _notify_fail("Lass etwas FALLEN, oder was meinst Du?\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du fallenlassen?\n");
+    return drop_objects(str[0..<8]);
+}
+
+static int werfen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts wegwerfen.\n");
+        return 0;
+    }
+
+    if (!str || str[<4..] != " weg") {
+        _notify_fail("Wirf etwas WEG, oder was meinst Du?\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du loswerden?\n");
+    return drop_objects(str[0..<5]);
+}
+
+static int legen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts weglegen.\n");
+        return 0;
+    }
+
+    if (!str) {
+        _notify_fail("Lege etwas AB, oder was meinst Du?\n");
+        return 0;
+    }
+
+    if (str[<3..] == " ab") {
+        _notify_fail("WAS moechtest Du ablegen?\n");
+        return drop_objects(str[0..<4]);
+    }
+
+    if (str[<4..] == " weg") {
+        _notify_fail("WAS moechtest Du weglegen?\n");
+        return drop_objects(str[0..<5]);
+    }
+
+    _notify_fail("WAS moechtest Du WOHIN legen?\n");
+    return put_objects(str, WEN, "legen");
+}
+
+static int stecken(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Das kannst Du in Deinem immateriellen Zustand nicht.\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du WOHIN stecken?\n");
+    return put_objects(str, WEN, "stecken");
+}
+
+static int holen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts nehmen.\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du aus WAS holen?\n");
+    return pick_objects(str, 1);
+}
+
+static int nehmen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts nehmen.\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du nehmen?\n");
+    return pick_objects(str, 0);
+}
+
+static int geben(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts weggeben.\n");
+        return 0;
+    }
+
+    _notify_fail("WEM moechtest Du WAS geben?\n");
+    return give_objects(str);
+}
+
+static int zeigen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du niemandem etwas zeigen.\n");
+        return 0;
+    }
+
+    _notify_fail("WEM moechtest Du WAS zeigen?\n");
+    return show_objects(str);
+}
+
+protected void add_put_and_get_commands(void)
+{
+    add_action("fallenlassen", "lass");
+    add_action("fallenlassen", "lasse");
+    add_action("werfen",       "wirf");
+    add_action("werfen",       "werf");
+    add_action("werfen",       "werfe");
+    add_action("legen",        "leg");
+    add_action("legen",        "lege");
+    add_action("stecken",      "steck");
+    add_action("stecken",      "stecke");
+    add_action("holen",        "hol");
+    add_action("holen",        "hole");
+    add_action("nehmen",       "nimm");
+    add_action("nehmen",       "nehm");
+    add_action("nehmen",       "nehme");
+    add_action("geben",        "gebe");
+    add_action("geben",        "gib");
+    add_action("zeigen",       "zeig");
+    add_action("zeigen",       "zeige");
+}
+
+
+/********** Aus reinen Kompatibilitaetsgruenden weiterhin enthalten *********/
+
+object* find_obs(string str, int meth)
+// gibt ein array zurueck mit allen Objekten die mit str angesprochen werden
+{
+   object inv;
+   if (!str) return 0;
+   if (str[<7..]==" in mir") {
+     inv=ME;
+     str=str[0..<8];
+   }
+   else if (str[<8..]==" in raum") {
+     if (meth & PUT_GET_DROP) { // man kann nichts aus dem Raum wegwerfen
+       _notify_fail("Du kannst nichts wegwerfen, das Du gar nicht hast.\n");
+       return 0;
+     }
+     inv=environment();
+     str=str[0..<9];
+   }
+   else if (meth & PUT_GET_DROP) inv=ME; // Raum bei drop uninteressant
+   // else kein besonderes inv ausgewaehlt also inv=0
+   if (!sizeof(str))
+     return 0; // hier passt die bereits gesetzte _notify_fail
+   else {
+     object *obs;
+     string con;
+     if (sscanf(str, "%s aus %s", str, con)==2 ||
+         sscanf(str, "%s in %s", str, con)==2 ||
+         sscanf(str, "%s von %s", str, con)==2 ||
+         sscanf(str, "%s vom %s", str, con)==2) {
+       if (!inv) {
+         if (!environment() || !(inv=present(con, environment())))
+            inv=present(con, ME); // sowohl im env als auch im inv suchen
+       }
+       else inv=present(con, inv); // nur in ausgewaehltem inv suchen
+       if (inv==ME) inv=0;
+       if (!inv || !(inv->short())) {
+         _notify_fail(break_string("Du hast hier aber kein '"+capitalize(con)
+                                 +"'.",78));
+         return 0;
+       }
+       if (living(inv)) {
+         _notify_fail(break_string("Aber "+inv->name(WER,1)+" lebt doch!",78));
+         return 0;
+       }
+       // wieso man aus Objekten die von std/tray abgeleitet werden etwas
+       // nehmen koennen soll, versteh ich zwar nicht so ganz...
+       if (!(inv->QueryProp(P_CONTAINER)) && !(inv->QueryProp(P_TRAY))) {
+         _notify_fail(break_string("Du kannst nichts aus "+inv->name(WEM,1)
+                                 +" nehmen.",78));
+         return 0;
+       }
+       if (inv->QueryProp(P_CNT_STATUS)) { // Container ist geschlossen
+         _notify_fail(break_string("Aber "+inv->name(WER,1)
+                                 +" ist doch geschlossen.", 78));
+         return 0;
+       }
+     }
+     else if (inv==ME && (meth & PUT_GET_TAKE)) { // nichts aus sich nehmen
+       _notify_fail("Du kannst nichts nehmen, "
+                    "was Du schon bei Dir traegst.\n");
+       return 0;
+     }
+     if (!inv && (meth & PUT_GET_TAKE))
+       inv=environment(); // nichts nehmen was man schon hat
+
+     if (!inv) {
+       if (environment()) {
+         obs=(environment()->present_objects(str)||({}));
+         if (!sizeof(obs)) obs+=(ME->present_objects(str)||({}));
+       }
+       else obs=(ME->present_objects(str) || ({}));
+     }
+     else obs=(inv->present_objects(str) || ({}));
+     return obs-({ ME });
+   }
+   return(0);
+}
+
+int pick_obj(object ob)
+{
+  object env;
+
+  if (!ob || ob == this_object() || environment(ob) == this_object()) return 0;
+  if ((env=environment(ob)) != environment()) {
+    if (!env->QueryProp(P_CONTAINER) && !env->QueryProp(P_TRAY)) {
+      TME("Du kannst nichts aus " + env->name(WEM,1) + " nehmen.");
+      return 1;
+    }
+    else if (env->QueryProp(P_CNT_STATUS)) {  // Container ist geschlossen
+      TME("Aber " + env->name(WER, 1) + " ist doch geschlossen.");
+      return 1;
+    }
+  }
+  if (ob->IsUnit() && ob->QueryProp(P_AMOUNT)<0) {
+    TME("Du kannst nicht mehr nehmen als da ist.");
+    return 1;
+  }
+  pick(ob);
+  return 1;
+}
+
+int drop_obj(object ob)
+{
+  if (!ob || ob==this_object() || environment(ob)!=this_object()) return 0;
+  drop(ob);
+  return 1;
+}
+
+int put_obj(object ob, object where)
+{
+  object env;
+
+  if (ob == this_object() || ob == where || environment(ob) == where) return 0;
+  env=environment(ob);
+  if (!where->QueryProp(P_CONTAINER)) {
+    TME("Du kannst in " + where->name(WEN,1) + " nix reinstecken.");
+    return 1;
+  }
+  if (where->QueryProp(P_CNT_STATUS)) {  // Container ist geschlossen
+    TME("Aber " + where->name(WER, 1) + " ist doch geschlossen.");
+    return 1;
+  }
+  if (env!=environment(this_object()) && env!=this_object()) {
+    _notify_fail("Da kommst du so nicht ran.\n");
+    return 0;
+  }
+  put(ob, where);
+  return 1;
+}
+
+int give_obj(object ob, object where)
+{
+  object env;
+
+  if (environment(ob)!=this_object()) {
+    TME("Das solltest Du erstmal nehmen.");
+    return 1;
+  }
+  if (!ob || ob == this_object() || ob == where ||
+      environment(where)!=environment())
+    return 0;
+  if (environment(ob) == where) {
+    _notify_fail("Das Ziel ist in dem zu gebenden Object enthalten!\n");
+    return 0;
+  }
+  if (environment(ob)!=this_object()) {
+    TME("Das hast Du nicht.");
+    return 1;
+  }
+  give(ob, where);
+  return 1;
+}
diff --git a/std/living/skill_attributes.c b/std/living/skill_attributes.c
new file mode 100644
index 0000000..ad90aa0
--- /dev/null
+++ b/std/living/skill_attributes.c
@@ -0,0 +1,379 @@
+// MorgenGrauen MUDlib
+//
+// living/skills_attributes.c - Verwaltung der Skillattribute von Lebewesen
+//
+// $Id: skills.c 6673 2008-01-05 20:57:43Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/util/executer";
+
+#define NEED_PROTOTYPES
+#include <living/skill_attributes.h>
+#include <player/life.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <properties.h>
+#include <defines.h>
+
+//#define ZDEBUG(x) if (find_player("zesstra")) tell_object(find_player("zesstra"),x)
+#define ZDEBUG(x)
+//#define SASETLOG(x) log_file("SASET.LOG", x, 250000)
+
+//#define __DEBUG__
+
+// Variable fuer die Skill-Attribute, Datenstruktur:
+/*  ([ SA_ATTR: ({Summe_Stat_Modifier, Zeitpunkt, AnzahlModifier, });
+              ([ ob1:value;duration,
+                 ob2:value;duration, ...]);  // stat. Modifier
+              ([ ob1:closure;duration,
+                 ob2:closure;duration, ...])     // dyn. Modifier
+             ,
+     SA_ATTR2: ({...}); ([]); ([]),
+   ]) */
+private nosave mapping skillattrs;
+
+protected void create() {
+  Set(P_SKILL_ATTRIBUTES, SECURED, F_MODE_AS);
+}
+
+// von aussen Prop setzen ist nicht...
+mapping _set_skill_attr(mapping sa) {
+  return deep_copy(skillattrs);
+}
+// und auch beim Abfragen nur kopien liefern. ;-)
+mapping _query_skill_attr() {
+  return deep_copy(skillattrs);
+
+//TODO: Evtl. ext. Setzen von P_SKILL_ATTRIBUTE_OFFSETS mitloggen?
+}
+
+private void UpdateSACache(string *attrs) {
+#ifdef __DEBUG__
+    if (getuid(this_object()) == "zesstra")
+    ZDEBUG(sprintf("%O\n",skillattrs));
+#endif
+  if (!mappingp(skillattrs)) return;
+  if (!pointerp(attrs) || sizeof(attrs))
+    attrs = m_indices(skillattrs); // alle
+  // sonst schnittmenge aus existierenden und den uebergebenen.
+  attrs = m_indices(skillattrs) & attrs;
+
+  // und jetzt ueber alle gewuenschten SAs drueber
+  foreach(string attr : attrs) {
+    int *cache = skillattrs[attr, SAM_CACHE];
+    mapping stat = skillattrs[attr, SAM_STATIC];
+    mapping dyn = skillattrs[attr, SAM_DYNAMIC];
+    int sum = 0;
+    int timeout = __INT_MAX__;
+ 
+    // ueber stat. Mods iterieren und Aufsummieren, kleinsten Timeout
+    // ermitteln.
+    foreach(object ob, int value, int duration: stat) {
+      // gueltige Mods aufaddieren, abgelaufene rauswerfen
+      if (duration >= time()) {
+        sum += value;
+        if (duration < timeout)
+          timeout = duration;
+      }
+      else
+        m_delete(stat, ob); // ja, geht im foreach ;-)
+    }
+    // Ablaufzeiten der dyn. Mods pruefen, waere hier zwar nicht unbedingt
+    // noetig sondern koennte man ausschliesslich im QuerySkillAttribute()
+    // machen, aber dann waere die Ermittlung der Summe der Mods schwieriger.
+    foreach(object ob, closure value, int duration: dyn) {
+      if (duration < time())
+        m_delete(dyn, ob); // ungueltig, weg damit.
+    }
+    // gesamtzahl Mods?
+    cache[SAM_COUNT] = sizeof(stat) + sizeof(dyn);
+    if (!cache[SAM_COUNT]) {
+      // keine mods da, Submapping fuer dieses SA komplett loeschen.
+      m_delete(skillattrs, attr);
+      continue;
+    }
+    // sonst die anderen Cache-Werte setzen.
+    cache[SAM_SUM] = sum;
+    cache[SAM_CACHE_TIMEOUT] = timeout;
+  }
+  // wenn alle Mods geloescht wurden.
+  if (!sizeof(skillattrs)) skillattrs=0;
+#ifdef __DEBUG__
+  if (getuid(this_object()) == "zesstra")
+    ZDEBUG(sprintf("%O\n",skillattrs));
+#endif
+}
+
+private int InternalModifySkillAttribute(object caster, string atrname,
+                                mixed value, int duration) {
+  int zeit = utime()[1];
+  int ticks = get_eval_cost();
+
+  // nur existierende SAs...
+  if (!stringp(atrname)
+      || member(VALID_SKILL_ATTRIBUTES, atrname) == -1)
+    return SA_MOD_INVALID_ATTR;
+
+  if (!objectp(caster)) return SA_MOD_INVALID_OBJECT;
+
+  if (!mappingp(skillattrs)) skillattrs=m_allocate(1,3);
+  
+  if (!member(skillattrs, atrname)) {
+    skillattrs[atrname,SAM_CACHE] = ({0, 0, 0});
+    // die meisten Mods sind statisch, daher auf Verdacht hier fuer einen
+    // Eintrag Platz reservieren, aber nicht fuer den dyn. Teil.
+    skillattrs[atrname,SAM_STATIC] = m_allocate(1,2);
+    skillattrs[atrname,SAM_DYNAMIC] = m_allocate(0,2);
+  }
+  // pruefen, ob Maximalzahl an Eintraegen drin ist
+  else if (skillattrs[atrname,SAM_CACHE][SAM_COUNT] >= SAM_MAX_MODS) {
+    // letzte Chance: destructete Objekte drin?
+    skillattrs[atrname,SAM_CACHE][SAM_COUNT] = 
+      sizeof(skillattrs[atrname,SAM_STATIC])
+    +sizeof(skillattrs[atrname,SAM_DYNAMIC]);
+    // es kann sein, dass noch abgelaufene Objekte drinstehen,
+    // aber die Pruefung ist mir gerade zu teuer. TODO
+    // nochmal gucken (rest vom cache wird ggf. unten geprueft.)
+    if (skillattrs[atrname,SAM_CACHE][SAM_COUNT] >= SAM_MAX_MODS)
+      return SA_TOO_MANY_MODS; // dann nicht.
+  }
+
+  // Dauer darf nur ein int sein.
+  if (!intp(duration))
+    raise_error(sprintf("Wrong argument 3 to ModifySkillAttribute: "
+    "expected 'int', got %.10O\n", duration));
+  // Zeitstempel ermitteln
+  duration += time();
+
+  // statischer oder dyn. Modifier?
+  if (intp(value)) {
+    // Mod darf nicht zu gross oder zu klein sein. TODO: Grenzen?
+    if (value < -1000)
+      return SA_MOD_TOO_SMALL;
+    else if (value > 1000)
+      return SA_MOD_TOO_BIG;
+    else if (!value)
+      return SA_MOD_INVALID_VALUE; 
+    // jedes Objekt darf nur einen mod haben. Wenn dieses schon einen dyn.
+    // hat, muss der geloescht werden (stat. werden ja eh ersetzt).
+    if (member(skillattrs[atrname,SAM_DYNAMIC], caster))
+      m_delete(skillattrs[atrname,SAM_DYNAMIC], caster);
+    // sonst eintragen
+    skillattrs[atrname, SAM_STATIC] += ([caster: value; duration]);
+  }
+  else if (closurep(value)) {
+    // nur ein Mod pro Objekt, s.o.
+    if (member(skillattrs[atrname,SAM_STATIC], caster))
+      m_delete(skillattrs[atrname,SAM_STATIC], caster);
+    // direkt ohne weitere Pruefung eintragen
+    skillattrs[atrname, SAM_DYNAMIC] += ([caster: value; duration]);
+  }
+  else
+    raise_error(sprintf("Wrong argument 2 to ModifySkillAttribute(): "
+    "expected 'int' or 'closure', got %.10O\n",value));
+
+#ifdef SASETLOG
+  if (query_once_interactive(this_object()))
+    SASETLOG(sprintf("%s: %O, %s, %O, %O\n", 
+        strftime("%y%m%d-%H%M%S"), this_object(), atrname, caster, value));
+#endif
+#ifdef SASTATD
+  object daemon;
+  if (query_once_interactive(ME)
+      && objectp(daemon=find_object(SASTATD)))
+    daemon->LogModifier(caster, atrname, value, duration);
+#endif
+  // noch den Cache fuer dieses SA neu berechnen
+  // TODO: Cache nur invalidieren, damit er erst bei der naechsten Abfrage
+  // aktualisiert wird. Spart Zeit, wenn bis dahin mehrere Mods
+  // entfernt/addiert werden. Dafuer ist die gecache Anzahl an Mods
+  // inkonsistent.
+  UpdateSACache( ({atrname}) );
+
+  ZDEBUG(sprintf("MSA: %O, Zeit: %d, Ticks: %d\n",
+	this_object(),
+	utime()[1]-zeit, ticks-get_eval_cost()));
+
+  return SA_MOD_OK;
+}
+
+public int ModifySkillAttribute(string atrname, mixed value, 
+                                    int duration) {
+  return InternalModifySkillAttribute(
+      (extern_call()?previous_object():ME), atrname, value, duration);
+}
+
+public int RemoveSkillAttributeModifier(object caster, string attrname) {
+  if (!stringp(attrname) || !mappingp(skillattrs) || !objectp(caster)
+      || !member(skillattrs, attrname))
+    return SA_MOD_NOT_FOUND;
+  // TODO: Berechtigung pruefen. ;-)
+
+  if (member(skillattrs[attrname, SAM_STATIC], caster)) {
+    m_delete(skillattrs[attrname, SAM_STATIC], caster);
+  }
+  else if (member(skillattrs[attrname, SAM_DYNAMIC], caster)) {
+    m_delete(skillattrs[attrname, SAM_DYNAMIC], caster);
+  }
+  else
+    return SA_MOD_NOT_FOUND;
+  
+  // TODO: Cache nur invalidieren, damit er erst bei der naechsten Abfrage
+  // aktualisiert wird. Spart Zeit, wenn bis dahin mehrere Mods
+  // entfernt/addiert werden. Dafuer ist die gecache Anzahl an Mods
+  // inkonsistent.
+  UpdateSACache( ({attrname}) );
+
+  return SA_MOD_REMOVED;
+}
+
+public int QuerySkillAttribute(string atrname)
+{
+  mixed offsets, attr;
+  int modsumme, qual, ret, cval;
+
+  if (!stringp(atrname)) // VALID_SKILL_ATTRIBUTES beruecksichtigen?
+    return 100;
+
+  // wenn nicht SA_QUALITY gefragt ist, erstmal jenes ermitteln, weil es den
+  // ersten Modifier auf alle anderen SAs darstellt. Sonst den Startwert fuer
+  // SA_QUALITY (100) modifiziert durch evtl. Todesfolgen ermitteln.
+  if ( atrname != SA_QUALITY )
+    qual = QuerySkillAttribute(SA_QUALITY);
+  else
+    // bei SA_QUALITY gehen die Todesfolgen ein
+    qual = to_int(100 * pow(0.9, death_suffering()/10.0));
+
+  // Die Offsets sind sozusagen der Basiswert der SAs. Als erstes verwursten,
+  // sofern vorhanden, nen int drinsteht und der offset != 0 ist.
+  if ( mappingp(offsets = Query(P_SKILL_ATTRIBUTE_OFFSETS))
+       && intp(attr=offsets[atrname]) 
+       && attr)
+    ret = attr;
+  else
+    ret = 100;
+
+  // wenn keine Mods gesetzt sind, wars das jetzt. ;-)
+  if ( !mappingp(skillattrs)
+       || !member(skillattrs, atrname) )
+    return (ret*qual)/100;
+
+  // wenn Cache der stat. Mods abgelaufen oder offenbar Objekte zerstoert
+  // wurden, muss der Cache neu berechnet werden.
+  if ( skillattrs[atrname,SAM_CACHE][SAM_CACHE_TIMEOUT] < time()
+      || sizeof(skillattrs[atrname,SAM_STATIC])
+         +sizeof(skillattrs[atrname,SAM_DYNAMIC]) !=
+           skillattrs[atrname,SAM_CACHE][SAM_COUNT] )
+  {
+    UpdateSACache( ({atrname}) );
+    // UpdateSACache() loescht uU das SA-Mapping oder Eintraege daraus.
+    if ( !mappingp(skillattrs)
+         || !member(skillattrs, atrname) )
+    return (ret*qual)/100;
+  }
+  // Summe der statischen Mods.
+  modsumme = skillattrs[atrname,SAM_CACHE][SAM_SUM];
+
+  // TODO! Evtl. andere Daten als ME an die Funktion uebergeben
+  // TODO! bereits nach Addition des Funktionsrueckgabewertes pruefen, ob der
+  //       Wertebereich des SA-Modifiers ueberschritten ist, oder freien
+  //       Wertebereich erlauben und erst am Ende deckeln (aktuelle Variante)
+  // Dynamische Modifier auswerten
+  foreach( object ob, closure cl, int duration:
+           skillattrs[atrname,SAM_DYNAMIC] )
+  {
+    if ( duration > time()               // Noch nicht abgelaufen und
+         && intp(cval=funcall(cl, ME)) ) // Funktion liefert int zurueck
+      modsumme += cval;
+    else {
+      m_delete(skillattrs[atrname,SAM_DYNAMIC], ob);
+      skillattrs[atrname,SAM_CACHE][SAM_COUNT]--;
+    }
+  }
+  ret = ((ret+modsumme)*qual)/100;
+  if ( ret < 10 )
+    ret = 10;
+  else if ( ret > 1000 )
+    ret = 1000;
+
+  return ret;
+}
+
+public varargs mapping QuerySkillAttributeModifier(object caster, 
+                           string *attrnames) {
+  
+  // auf abgelaufene Modifikatoren pruefen
+  if (!pointerp(attrnames))
+    UpdateSACache( ({}) );
+  else
+    UpdateSACache(attrnames);
+
+  if (!mappingp(skillattrs))
+    return ([]);
+  if (!pointerp(attrnames) || !sizeof(attrnames))
+    attrnames = m_indices(skillattrs); // alle durchsuchen
+  else // schnittmenge der gew. und vorhandenen bilden
+    attrnames = m_indices(skillattrs) & attrnames;
+  
+  mapping res=m_allocate(sizeof(attrnames), 1);
+
+  foreach(string atr: attrnames) {
+    res[atr] = m_allocate(5, 2); // mal fuer 5 Werte Platz reservieren
+    if (!objectp(caster)) {
+      // wenn kein bestimmter caster angefragt ist, alle mods liefern
+      foreach(object c, int value, int dur: skillattrs[atr, SAM_STATIC]) {
+	res[atr] += ([c: value; dur]);
+      }
+      foreach(object c, closure value, int dur: skillattrs[atr, SAM_DYNAMIC]) {
+	res[atr] += ([c: value; dur]);
+      }
+    }
+    else {
+      // sonst nur den Mod von caster
+      if (member(skillattrs[atr, SAM_STATIC], caster)) {
+	res[atr] += ([caster: 
+			 skillattrs[atr, SAM_STATIC][caster, SAM_VALUE];
+	                 skillattrs[atr, SAM_STATIC][caster, SAM_DURATION]
+		    ]);
+      }
+      else if (member(skillattrs[atr, SAM_DYNAMIC], caster)) {
+	res[atr] += ([caster:
+			 skillattrs[atr, SAM_DYNAMIC][caster, SAM_VALUE];
+	                 skillattrs[atr, SAM_DYNAMIC][caster, SAM_DURATION]
+		    ]);
+      }
+    }
+  }
+  return res;
+}
+
+// Kompatibilitaetsfunktion mit altem Interface. Ist nur ein Wrapper, der
+// value umrechnet und 'alte' Rueckgabewerte liefert.
+
+public varargs int ModifySkillAttributeOld(object caster, string atrname,
+                          int value, int duration, mixed fun) {
+  int res;
+  // Caller ermitteln
+  if (extern_call()) caster=previous_object();
+  else caster=ME;
+
+  // Closures koennen via ModifySkillAttributeOld() nicht mehr gesetzt werden,
+  // da deren Rueckgabewert nicht sinnvoll umgerechnet werden koennen. (Man
+  // weiss nicht, ob es eine neue oder alte Closure ist.)
+  if (pointerp(fun) || closurep(fun))
+      raise_error(sprintf("Closures for SA modifiers can't be set by "
+      "ModifySkillAttributeOld()! Use ModifySkillAttribute()!\n"));
+  
+  res = InternalModifySkillAttribute(caster, atrname, value-100, duration);
+  // die alte funktion hatte nur 0 fuer ungueltigen Wert und < 0 fuer zu
+  // kleines Level als Rueckgabewert. Zu kleines Level gibt nicht mehr, also
+  // bleibt nur 0 als Sammel-Fehlercode uebrig. *seufz*
+  if (res < 0) return 0;
+  return res;
+}
+
diff --git a/std/living/skill_utils.c b/std/living/skill_utils.c
new file mode 100644
index 0000000..24caf75
--- /dev/null
+++ b/std/living/skill_utils.c
@@ -0,0 +1,35 @@
+// MorgenGrauen MUDlib
+//
+// living/skill_utils -- some helper functions for manipulating skill data 
+//                       needed in more than one program.
+//
+// $Id: skill_utils.c 6738 2008-02-19 18:46:14Z Humni $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <new_skills.h>
+
+protected void SkillResTransfer(mapping from_M, mapping to_M)
+{
+  if ( !mappingp(from_M) || !mappingp(to_M) )
+    return;
+
+  if ( member(from_M,SI_SKILLDAMAGE) )
+    to_M[SI_SKILLDAMAGE] = to_int(from_M[SI_SKILLDAMAGE]);
+
+  if ( member(from_M,SI_SKILLDAMAGE_MSG) )
+    to_M[SI_SKILLDAMAGE_MSG] = to_string(from_M[SI_SKILLDAMAGE_MSG]);
+
+  if ( member(from_M,SI_SKILLDAMAGE_MSG2) )
+    to_M[SI_SKILLDAMAGE_MSG2] = to_string(from_M[SI_SKILLDAMAGE_MSG2]);
+
+  if ( member(from_M,SI_SKILLDAMAGE_TYPE) )
+    to_M[SI_SKILLDAMAGE_TYPE] = from_M[SI_SKILLDAMAGE_TYPE];
+
+  if ( member(from_M,SI_SPELL) )
+    to_M[SI_SPELL] = from_M[SI_SPELL];
+}
+
diff --git a/std/living/skills.c b/std/living/skills.c
new file mode 100644
index 0000000..93c9dcc
--- /dev/null
+++ b/std/living/skills.c
@@ -0,0 +1,595 @@
+// MorgenGrauen MUDlib
+//
+// living/skills.c -- Gilden-, Skill- und Spellfunktionen fuer Lebewesen
+//
+// $Id: skills.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/std_skills";
+
+#define NEED_PROTOTYPES
+#include <living/skills.h>
+#include <living/skill_attributes.h>
+#include <player/life.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <new_skills.h>
+#include <wizlevels.h>
+
+// speichert die Spell-Fatigues (global, Spruchgruppen, Einzelsprueche)
+private mapping spell_fatigues = ([]);
+
+// Prototypen
+private void expire_spell_fatigues();
+
+protected void create()
+{
+  // mainly necessary for players, but there may be some NPC with savefiles.
+  // Additionally, it simplifies expiration of old keys to have it here.
+  call_out(#'expire_spell_fatigues, 4);
+}
+
+
+// Diese - hier scheinbar sinnlose - Funktion wird von /std/player/skills.c dann 
+// ueberladen.
+public int is_deactivated_skill(string sname, string gilde)
+{
+	return 0;
+}
+
+
+
+static string _query_visible_guild()
+{ string res;
+
+  if ( stringp(res=Query(P_VISIBLE_GUILD)) )
+    return res;
+
+  return QueryProp(P_GUILD);
+}
+
+static string _query_visible_subguild_title()
+{ string res;
+
+  if ( stringp(res=Query(P_VISIBLE_SUBGUILD_TITLE)) )
+    return res;
+
+  return QueryProp(P_SUBGUILD_TITLE);
+}
+
+static mixed _query_guild_prepareblock()
+{ mapping res;
+  string  gilde;
+
+  if ( !stringp(gilde=QueryProp(P_GUILD)) )
+    return 0;
+  if ( !mappingp(res=Query(P_GUILD_PREPAREBLOCK)) 
+      || !member(res,gilde) )
+    return 0;
+  return res[gilde];
+}
+
+static mixed _set_guild_prepareblock(mixed arg)
+{ mapping res;
+  string  gilde;
+
+  if ( !stringp(gilde=QueryProp(P_GUILD)) )
+    return 0;
+  if ( !mappingp(res=Query(P_GUILD_PREPAREBLOCK)) )
+    res=([]);
+
+  res[gilde]=arg;
+  Set(P_GUILD_PREPAREBLOCK,res);
+
+  return arg;
+}
+
+
+private nosave int valid_setskills_override;
+// Man sollte eigentlich ja nicht Parameter als globale Variablen
+// uebergeben, aber hier ging es nicht anders
+nomask private int valid_setskills(string gilde)
+{ string fn;
+
+  if ( !query_once_interactive(this_object()) )
+    return 1; // Monster duerfen sich selber Skills setzen :)
+
+  if ( QueryProp(P_TESTPLAYER) || IS_WIZARD(this_object()) )
+      return 1; // Testspieler und Magier sind schutzlose Opfer ;-)
+
+  if ( previous_object() )
+  {
+    if ( previous_object()==this_object()
+        && this_interactive()==this_object() )
+      return 1;
+
+    fn=object_name(previous_object());
+    if ( fn[0..7]=="/gilden/"
+        || fn[0..11]=="/spellbooks/"
+        || fn[0..7]=="/secure/"
+        || fn[0..11]=="/p/zauberer/" )
+      return 1; // Die sollten problemlos aendern duerfen
+
+    if ( file_size("/gilden/access_rights")>0
+       && call_other("/gilden/access_rights",
+                    "access_rights",
+                    getuid(previous_object()),
+                    gilde+".c"))
+      return 1; // Setzendes Objekt kommt vom Gildenprogrammierer
+
+    if ( file_size("/gilden/"+gilde+".c")>0
+        && call_other("/gilden/"+gilde,
+                      "valid_setskills",
+                        explode(fn,"#")[0]) )
+      return 1; // Die Gilde selber kann Ausnahmen zulassen
+  }
+
+  if (valid_setskills_override)
+  {
+    valid_setskills_override=0;
+    return 1; // Fuers Setzen der Closure
+  }
+
+  if ( this_interactive() )
+  {
+    if ( IS_ARCH(this_interactive()) )
+      return 1; // Erzmagier duerfen immer aendern
+
+    if ( call_other("/gilden/access_rights",
+                    "access_rights",
+                    getuid(this_interactive()),
+                    gilde+".c"))
+      return 1;  // Der Gildenprogrammierer selber auch
+  }
+
+  // Fuer die Waffenskills, die sollen sich selbst auch setzen duerfen
+  if (!this_interactive() && this_object()==previous_object())
+	  return 1;
+  
+  
+  log_file("SETSKILLS",sprintf("*****\n%s PO:%O->TO:%O TI:%O\n GUILD:%s VERB_ARGS:'%s'\n",
+            ctime(time())[4..15],
+            previous_object(),
+            this_object(),
+            this_interactive(),
+            gilde,
+            ( this_interactive() ? query_verb() + " " +
+                this_interactive()->_unparsed_args() : "") ));
+
+  return 0;
+}
+
+// Nur interne Verwendung, value wird nicht weiter prueft, muss ok sein.
+// Es wird keine Kopie von value gemacht, wenn es ins Mapping geschrieben
+// wird!
+private mapping internal_set_newskills(mapping value, string gilde) {
+  mapping skills;
+
+  // in der richtigen Gilde setzen.
+  if ( !gilde && !(gilde=QueryProp(P_GUILD)) )
+    gilde="ANY";
+
+  // Query(), hier ist eine Kopie nicht sinnvoll.
+  if ( !mappingp(skills=Query(P_NEWSKILLS,F_VALUE)) ) {
+    skills=([]);
+    Set(P_NEWSKILLS, skills, F_VALUE);
+  }
+
+  // Falls dies hier mal ausgewertet werden sollte, nicht vergessen, dass
+  // einige Funktion hier im File die Prop evtl. via
+  // internal_query_newskills() abrufen und direkt aendern...
+  valid_setskills(gilde); // Sicherheitsueberpruefung
+  
+  // Skills setzen. Set() unnoetig, weil wir das von Query() gelieferte
+  // Mapping aendern und das ja via Referenz bekommen haben.
+  skills[gilde]=value;
+  //Set(P_NEWSKILLS,skills);
+
+  return(value);
+}
+
+// nur zur internen Verwendung, es wird keine Kopie des Skillmappings gemacht!
+private mapping internal_query_newskills(string gilde) {
+  mapping skills;
+
+  // richtige Gilde abfragen.
+  if ( !gilde && !(gilde=QueryProp(P_GUILD)) )
+    gilde="ANY";
+
+  skills=Query(P_NEWSKILLS);
+
+  if (!mappingp(skills) || !mappingp(skills=skills[gilde]) )
+    return ([]);
+
+  return(skills);
+}
+
+// Eigentlich sollte man den _query-Funktionen keine Parameter geben...
+static varargs mapping _query_newskills(string gilde) {
+
+  // sonst Kopie des spellmappings liefern! Kostet zwar, aber verhindert
+  // einige andere Bugs und versehentliche Aenderungen an den Skills!
+  return(deep_copy(internal_query_newskills(gilde)));
+}
+
+// Eigentlich sollte man den _set-Funktionen keine weiteren Parameter geben
+static varargs mapping _set_newskills(mapping value, string gilde) {
+
+  // value auf Mappings normalisieren, ggf. Kopieren
+  if ( !mappingp(value) )
+      value=([]);
+  else
+      //zur Sicherheit, wer weiss, was der setzende noch damit macht...
+      value=deep_copy(value);
+
+  // und setzen...
+  internal_set_newskills(value, gilde);
+
+  // und noch ne Kopie von dem Liefern, was wir gesetzt haben (keine Referenz,
+  // sonst koennte der Aufrufende ja noch im Nachhinein aendern).
+  return(_query_newskills(gilde));
+}
+
+private mapping InternalQuerySkill(string sname, string gilde) {
+  mixed skill, skills;
+  // In is_any wird gespeichert, ob es ein gildenunabhaengier Skill ist,
+  // fuer die is_deactivate_skill-Abfrage.
+  int is_any;
+
+  // Skills komplett abfragen, keine spez. Gilde
+  if (!mappingp(skills=Query(P_NEWSKILLS,F_VALUE)))
+      return 0;
+
+  if (stringp(gilde) && sizeof(gilde)) {
+    //bestimmte Gilde angegeben, gut, dort gucken.
+    if (mappingp(skills[gilde]))
+      skill=skills[gilde][sname]; 
+  }
+  else {
+    gilde=QueryProp(P_GUILD); //reale Gilde holen
+    if (gilde && mappingp(skills[gilde]) && 
+	(skill=skills[gilde][sname])) {
+      // gibt es den Spell in der Gilde des Spielers?
+      // dann hier nix machen...
+    }
+    else if (mappingp(skills["ANY"])) {
+     // Zum Schluss: Gibt es den Skill vielleicht Gildenunabhaengig?
+      skill=skills["ANY"][sname];
+      // wenn man hier reinkommt, dann spaeter mit is_deactivated_skill() 
+      // pruefen!
+      is_any=1;
+    }
+  }
+
+  // wenn kein Skill gefunden, mit 0 direkt raus
+  if (!skill) return 0;
+
+  // Bei gildenunabhaengigen auch im Skillmapping vermerken
+  if ( is_any ) {	
+      skill+=([SI_GUILD:"ANY"]);	
+      // Ist er vielleicht in der Gilde des Spielers deaktiviert? 		
+      // Dies kann nur der Fall sein, wenn es kein Gildenskill ist.		
+      if (is_deactivated_skill(sname,gilde)) {		    
+	  return 0;		
+      }
+  }
+
+  return(skill);
+}
+
+public varargs mapping QuerySkill(string sname, string gilde) {
+ 
+    if (!stringp(sname) || !sizeof(sname))
+	return 0;
+
+    //Kopie zurueckliefern
+    return(deep_copy(InternalQuerySkill(sname,gilde)));
+}
+
+#define SMUL(x,y) ((x<0 && y<0)?(-1*x*y):(x*y))
+public varargs int QuerySkillAbility(string sname, string gilde)
+{ mapping skill;
+  string skill2;
+
+  if ( !(skill=InternalQuerySkill(sname, gilde)) )
+    return 0;
+
+  int val=skill[SI_SKILLABILITY];
+
+  if (skill2=skill[SI_INHERIT])
+  {
+    int val2;
+    val2=QuerySkillAbility(skill2);
+    val=(val*MAX_ABILITY+SMUL(val,val2))/(2*MAX_ABILITY);
+  }
+
+  return val;
+}
+
+protected varargs mixed LimitAbility(mapping sinfo, int diff)
+{ mixed abil;
+  int max,old,d2;
+
+  abil=sinfo[SI_SKILLABILITY];
+
+  if ( !intp(abil) )
+    return sinfo;
+  old=abil;
+
+  // Beim Spieler eingetragene Schwierigkeit gilt vor angegebener.
+  if ( (d2=sinfo[SI_DIFFICULTY]) )
+    diff=d2;
+
+  // diff <-100 soll nicht hemmen und macht keinen Sinn
+  diff=(diff<(-100))?(-100):diff;
+  
+  max=MAX_ABILITY-(diff+100)*(35-QueryProp(P_LEVEL));
+
+// diff|lvl 1:|   3:|	7:| 10:| 13:| 16:| 19:| 22:| 25:| 28:| 31:| 34:|
+// ----+------+-----+-----+----+----+----+----+----+----+----+----+----+
+//  -50|   83%|  84%|  86%| 87%| 89%| 90%| 92%| 93%| 95%| 96%| 98%| 99%|
+//  -10|   69%|  72%|  74%| 77%| 80%| 82%| 85%| 88%| 91%| 93%| 96%| 99%|
+//    0|   66%|  69%|  72%| 75%| 78%| 81%| 84%| 87%| 90%| 93%| 96%| 99%|
+//   10|   62%|  65%|  69%| 72%| 75%| 79%| 82%| 85%| 89%| 92%| 95%| 98%|
+//   20|   59%|  62%|  66%| 70%| 73%| 77%| 80%| 84%| 88%| 91%| 95%| 98%|
+//   30|   55%|  59%|  63%| 67%| 71%| 75%| 79%| 83%| 87%| 90%| 94%| 98%|
+//   40|   52%|  56%|  60%| 65%| 69%| 73%| 77%| 81%| 86%| 90%| 94%| 98%|
+//   50|   49%|  53%|  58%| 62%| 67%| 71%| 76%| 80%| 85%| 89%| 94%| 98%|
+//  100|   32%|  38%|  44%| 50%| 56%| 62%| 68%| 74%| 80%| 86%| 92%| 98%|
+//  150|   15%|  22%|  30%| 37%| 45%| 52%| 60%| 67%| 75%| 82%| 90%| 97%|
+//  200|   -2%|   7%|  16%| 25%| 34%| 43%| 52%| 61%| 70%| 79%| 88%| 97%|
+//  250|  -19%|  -8%|	2%| 12%| 23%| 33%| 44%| 54%| 65%| 75%| 86%| 96%|
+//  300|  -36%| -24%| -12%|  0%| 12%| 24%| 36%| 48%| 60%| 72%| 84%| 96%|
+//  400|  -70%| -55%| -40%|-25%|-10%|  5%| 20%| 35%| 50%| 65%| 80%| 95%|
+//  500| -104%| -86%| -68%|-50%|-32%|-14%|  4%| 22%| 40%| 58%| 76%| 94%|
+//  600| -138%|-117%| -96%|-75%|-54%|-33%|-12%|  9%| 30%| 51%| 72%| 93%|
+
+  if ( abil>max )
+    abil=max;
+  if ( abil>MAX_ABILITY
+    ) abil=MAX_ABILITY;
+  else if ( abil<-MAX_ABILITY )
+    abil=-MAX_ABILITY;
+
+  if ( old && !abil )
+    abil=1;
+  // Faehigkeiten sollen nicht durch die Begrenzung verschwinden
+
+  sinfo[SI_SKILLABILITY]=abil;
+
+  return sinfo;
+}
+
+public varargs void ModifySkill(string sname, mixed val, int diff, string gilde)
+{ 
+  mapping skills;
+  mixed skill;
+
+  if ( !stringp(sname) || !sizeof(sname) )
+    return;
+
+  // internal_query_newskills() macht keine Kopie
+  skills=internal_query_newskills(gilde);
+
+  // Skill ermitteln, wenn nicht existiert, wird er angelegt.
+  if (!skill=skills[sname]) {
+      skill=([]); 
+  }
+  
+  // Zur Sicherheit mal das Mapping kopieren, wer weiss, was der	
+  // Aufrufende dieser Funktion selber spaeter damit noch macht.
+  // ist ok, wenn val kein Mapping ist, dann macht deep_copy nix.
+  val=deep_copy(val);
+
+  // Skill und val vereinigen
+  if ( mappingp(val) )
+    skill+=val;
+  else if (intp(val))
+    skill[SI_SKILLABILITY]=val;
+  else
+    raise_error(sprintf("Bad arg 2 to ModifySkill(): expected 'int', "
+          "got %.10O.\n",val));
+
+  // Lernen entsprechend SI_DIFFICULTY begrenzen.
+  if(diff && !member(skill,SI_DIFFICULTY))
+    skill[SI_DIFFICULTY]=diff;
+  skill=LimitAbility(skill,diff || skill[SI_DIFFICULTY]);
+  
+  // schliesslich im Skillmapping vermerken. Im Normalfall ist der Skill jetzt
+  // schon geaendert, nicht erst nach dem internal_set_newskills().
+  skills[sname]=skill;
+
+  // explizites Abspeichern fast ueberfluessig, weil wir oben eine Referenz
+  // auf das Skillmapping gekriegt haben...
+  // Aber es koennte sein, dass dies der erste Skill fuer diese Gilde ist,
+  // dann ist es noetig. Zum anderen wird internal_set_newskills() nochmal
+  // geloggt.
+  internal_set_newskills(skills,gilde);
+}
+
+public varargs void LearnSkill(string sname, int add, int diff)
+{ mapping skill;
+  string skill2,gilde;
+  int val;
+
+  // Spieler sollen nur lernen, wenn sie interactive sind. Das soll
+  // natuerlich nur fuer Spieler gelten.
+  if (query_once_interactive(this_object()) && !interactive())
+	  return;
+
+  if ( add>MAX_SKILLEARN )
+    add=MAX_SKILLEARN;
+  else if ( add<1 )
+    add=1;
+
+  // Skillmapping ermitteln (hier kommt keine Kopie zurueck)
+  skill=InternalQuerySkill(sname, 0);
+  // wenn kein Skill, dann Abbruch
+  if (!skill) return;
+
+  val=skill[SI_SKILLABILITY];
+  gilde=skill[SI_GUILD];
+ 
+  val+=add;
+
+  ModifySkill(sname,val,diff,gilde);
+  if ( skill2=skill[SI_INHERIT] )
+    LearnSkill(skill2,add/3,diff);
+}
+
+public varargs int UseSpell(string str, string spell)
+{ string gilde,sbook;
+  mapping sinfo;
+  closure cl;
+  
+  if ( !spell && !(spell=query_verb()) )
+    return 0;
+
+  spell=lower_case(spell);
+
+  // QuerySkill() liefert eine Kopie des Skillmappings.
+  // wenn skill unbekannt oder Ability <= 0, ist der Spell nicht nutzbar.
+  if ( !(sinfo=QuerySkill(spell,0))
+        || sinfo[SI_SKILLABILITY] <= 0 )
+    return 0;
+
+  sinfo[SI_SKILLARG]=str; // Argument eintragen
+
+  if ( !closurep(cl=sinfo[SI_CLOSURE]) )
+  {
+    // Wenn ein Spellbook angegeben ist wird der Spell direkt ausgefuehrt
+    if ( stringp(sbook=sinfo[SI_SPELLBOOK]) )
+      cl=symbol_function("UseSpell",SPELLBOOK_DIR+sbook);
+
+    // Wenn der Spieler in einer Gilde ist, so weiss diese, in welchem
+    // Spellbook der Spell zu finden ist...
+    else if ( (gilde=QueryProp(P_GUILD)) && 
+      ( find_object(GUILD_DIR+gilde) || file_size(GUILD_DIR+gilde+".c")>-1))
+      cl=symbol_function("UseSpell",GUILD_DIR+gilde);
+    else
+      cl=function int () {return 0;};
+
+    sinfo[SI_CLOSURE]=cl;
+    valid_setskills_override=1;
+    ModifySkill(spell,([SI_CLOSURE:cl]),0,sinfo[SI_GUILD]);
+    valid_setskills_override=0;
+  }
+  return funcall(cl,this_object(),spell,sinfo);
+}
+
+public varargs mixed UseSkill(string skill, mapping args)
+{ mapping sinfo;
+  string gilde, func,skill2;
+  mixed res;
+  closure cl;
+  
+  if ( !skill ||
+       QueryProp(P_GHOST))
+    return 0;
+
+  skill=capitalize(skill);
+  // QuerySkill() liefert eine Kopie des Skillmappings
+  if ( !(sinfo=QuerySkill(skill,0)) )
+    return 0;
+
+  if (args)
+    sinfo+=args;
+
+  if ( !closurep(cl=sinfo[SI_CLOSURE]) )
+  {
+    if ( !(func=sinfo[SI_SKILLFUNC])    // Keine Funktion angegeben?
+        || !(gilde=QueryProp(P_GUILD))) // Keine Gilde angegeben?
+    {
+      // Dann Standard-Funktion nehmen, wenn es die nicht gibt, den
+      // Ability-Wert zurueckliefern.
+      if (!closurep(cl = symbol_function("StdSkill_"+skill,this_object())))
+        cl=function int (object ob, string sname)
+             {return QuerySkillAbility(sname);} ;
+    }
+    else
+    {
+      // Sonst diese Funktion im Gildenobjekt aufrufen
+      cl=symbol_function(func,GUILD_DIR+gilde);
+    }
+
+    sinfo[SI_CLOSURE]=cl;
+    valid_setskills_override=1;
+    ModifySkill(skill,([SI_CLOSURE:cl]),0,sinfo[SI_GUILD]);
+    valid_setskills_override=0;
+  }
+
+  res=funcall(cl,this_object(),skill,sinfo);
+  if ( (skill2=sinfo[SI_INHERIT]) && mappingp(res) )
+    res=UseSkill(skill2,res); // Fuer Skills, die von anderen abhaengen
+
+  return res;
+}
+
+// ************** Spellfatigues ***************
+
+/*  Prueft die Spellfatigue fuer Spruch(gruppe) <key>.
+ *  <key> darf 0 sein (globale Spruchsperre).
+ *  Liefert 0, wenn keine Sperre und die Ablaufzeit, wenn eine Sperre noch
+ *  gueltig. ist.
+ */
+public varargs int CheckSpellFatigue(string key) {
+  // key==0 is the (default) global spellfatigue.
+  if (spell_fatigues[key] > time())
+    return spell_fatigues[key]; // Ablaufzeit zurueckgeben.
+
+  return 0; // ok, keine Sperre.
+}
+
+/** Speichert eine Spellfatigue von <duration> Sekunden fuer <key>.
+ * <key> darf 0 sein und bezeichnet das globale Spellfatigue.
+ * Rueckgabewert: Ablaufzeit der gesetzten Sperre
+                  -1, wenn noch eine nicht-abgelaufene Sperre auf dem <key> lag.
+                  0, wenn duration 0 ist.
+ */
+public varargs int SetSpellFatigue(int duration, string key) {
+  // aktuelle Sperre abgelaufen?
+  if (CheckSpellFatigue(key))
+    return -1; // alte Sperre noch aktiv.
+
+  duration += time();
+  // 0 is OK for <key>, it is the key for global spell fatigues
+  spell_fatigues[key] = duration;
+  return duration;
+}
+
+/*  Prueft die Spellfatigue fuer Spruch(gruppe) <key>.
+ *  <key> darf fuer diese Funktion 0 (globale Spruchsperre) sein, aber man
+ *  darf das Argument nicht weglassen, damit nicht ein verpeilter Magier
+ *  versehentlich die globale Spruchsperre nullt.
+ */
+public void DeleteSpellFatigue(string key) {
+  // key==0 is the (default) global spellfatigue.
+  m_delete(spell_fatigues, key);
+}
+
+/** Loescht abgelaufene Keys aus dem spell_fatigue mapping.
+ */
+private void expire_spell_fatigues() {
+  foreach(string key, int endtime: spell_fatigues) {
+    if (endtime <= time())
+      m_delete(spell_fatigues, key);
+  }
+}
+
+/** Setmethode fuer P_NEXT_SPELL_TIME.
+  */
+static int _set_next_spell(int fatigue) {
+  return SetSpellFatigue(fatigue - time());
+}
+/** Querymethode fuer P_NEXT_SPELL_TIME.
+  */
+static int _query_next_spell() {
+  return CheckSpellFatigue();
+}
+
diff --git a/std/living/std_skills.c b/std/living/std_skills.c
new file mode 100644
index 0000000..d132efc
--- /dev/null
+++ b/std/living/std_skills.c
@@ -0,0 +1,104 @@
+// MorgenGrauen MUDlib
+//
+// living/std_skills.c -- Standardfaehigkeiten
+//
+// $Id: std_skills.c 6673 2008-01-05 20:57:43Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <living/skills.h>
+#include <living/skill_attributes.h>
+#include <thing/properties.h>
+#include <attributes.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <new_skills.h>
+
+#define SIG(x) (x?(x>0?1:-1):0)
+
+protected int StdSkill_Nightvision(object me, string sname, mixed sinfo) {
+  int abil,light,llt,dt,res;
+
+  if (!sinfo || !environment()) return 0;
+  if (intp(sinfo)) sinfo=([SI_SKILLABILITY:sinfo]);
+  if (!mappingp(sinfo)) return 0;
+  if ((light=QueryProp(P_PLAYER_LIGHT))>0) {
+    ModifySkill(sname,([SI_LASTLIGHT:time()]));
+    return light;
+  }
+  abil=sinfo[SI_SKILLABILITY];
+  if (!(llt=sinfo[SI_LASTLIGHT])) {
+    ModifySkill(sname,([SI_LASTLIGHT:time()]));
+    dt=0;
+  } else {
+    dt=time()-llt;
+    if (dt<0) dt=0;
+    if (dt>86400) dt=86400;
+  }
+
+  res=(abil*dt)/(20*MAX_ABILITY)+light;
+  if (res<=0) {
+    res--; // Wert muss !=0 sein
+    if (res<-MAX_ABILITY) res=-MAX_ABILITY;
+  } else {
+    if (res>MAX_ABILITY) res=MAX_ABILITY;
+  }
+  return res;
+}
+
+protected mapping StdSkill_Bihand(object me, string sname, mapping sinfo) {
+  int abil,val;
+
+  // printf("Bihand: %O\n",sinfo);
+  if (!sinfo) return 0;
+  abil=sinfo[SI_SKILLABILITY];
+  val=(abil*(QueryAttribute(A_STR)+33))/MAX_ABILITY;
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+  // + max. 53
+  return sinfo;
+}
+
+protected mapping StdSkill_Fight_sword(object me, string sname, mapping sinfo) {
+  int abil,asig,val;
+
+  abil=sinfo[SI_SKILLABILITY];asig=SIG(abil);
+  val=(abil*(QueryAttribute(A_STR) +
+             QueryAttribute(A_DEX)*asig +
+             33))/MAX_ABILITY;
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+  // + max. 73
+  return sinfo;
+}
+
+protected mapping StdSkill_Fight_hands(object me, string sname, mapping sinfo) {
+  int abil,asig,val;
+
+  if (!sinfo) return 0;
+  abil=sinfo[SI_SKILLABILITY];asig=SIG(abil);
+  val=(abil*(QueryAttribute(A_STR) +
+             QueryAttribute(A_DEX)*3*asig +
+             100))/MAX_ABILITY;
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+  // + max. 180
+  return sinfo;
+}
+
+protected int StdSkill_Booze(object me, string sname, mapping sinfo) {
+  int abil,val;
+
+  val=0;
+  if (!sinfo || (val=sinfo[SI_SKILLARG])<=0)
+    return val;
+  abil=sinfo[SI_SKILLABILITY];
+  val-=(val*abil)/(MAX_ABILITY+2500); // Bis zu 80% Abzug bei Alkoholikern.
+  if (val<=0) val=1;
+  return val;
+}
+
diff --git a/std/living/team.c b/std/living/team.c
new file mode 100644
index 0000000..2b31db9
--- /dev/null
+++ b/std/living/team.c
@@ -0,0 +1,650 @@
+// MorgenGrauen MUDlib
+//
+// living/team.c
+//
+// $Id: team.c 9138 2015-02-03 21:46:56Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <properties.h>
+#include <thing/properties.h>
+#include <living/combat.h>
+#include <combat.h>
+#include <living/team.h>
+#include <wizlevels.h>
+#include <hook.h>
+
+#define ME this_object()
+#define TP this_player()
+#define PO previous_object()
+#define ENV environment()
+
+private nosave string team_attack_cmd;
+private nosave mapping team_follow_todo;
+private nosave int team_autofollow;
+private nosave object teammove;
+
+void create() {
+  Set(P_TEAM_ATTACK_CMD,-1,F_SET_METHOD);
+  Set(P_TEAM_ATTACK_CMD,PROTECTED,F_MODE_AS);
+  Set(P_TEAM_AUTOFOLLOW,-1,F_SET_METHOD);
+  Set(P_TEAM_AUTOFOLLOW,PROTECTED,F_MODE_AS);
+  teammove=0;
+  offerHook(H_HOOK_TEAMROWCHANGE, 1);
+}
+
+void add_team_commands() {
+  add_action("teamcmd","gruppe");
+  add_action("teamcmd","g");
+  add_action("teamcmd","team");
+}
+
+string _query_team_attack_cmd() {
+  return Set(P_TEAM_ATTACK_CMD,team_attack_cmd);
+}
+
+int _query_team_autofollow() {
+  return Set(P_TEAM_AUTOFOLLOW,team_autofollow);
+}
+
+private int team_help() {
+  // Syntax-Kompatiblitaet (Avalon) ist ganz nett :-)
+  write("\
+(Befehle des Teamleiters sind mit * gekennzeichnet\n\
+\n\
+* team angriff\n\
+  team angriffsbefehl <befehl>\n\
+* team aufnahme <name>\n\
+  team autof[olge] <ein/aus>\n\
+* team autoi[nfo] <ein/aus> [+[lp]] [+[kp]] [sofort]\n\
+* team entlasse <name>\n\
+  team farben lp_rot lp_gelb kp_rot kp_gelb\n\
+  team flucht[reihe] <reihe>\n\
+  team folge <name>\n\
+* team formation <min[-max]> [<min[-max]> ...]\n\
+  team hilfe|?\n\
+  team [info] [sortiert|alphabetisch]\n\
+  team [kampf]reihe <reihe>\n\
+* team leiter[in] <name>\n\
+  team liste\n\
+* team name <gruppenname>\n\
+  team orte [alle]\n\
+  team ruf[e]\n\
+  team uebersicht\n\
+  team verlasse\n");
+  return 1;
+}
+
+object IsTeamLeader() {
+  object team;
+
+  if (!objectp(team=Query(P_TEAM))
+      || team!=Query(P_TEAM_LEADER)
+      || team->Leader()!=ME)
+    return 0;
+  return team;
+}
+
+object *TeamMembers() {
+  object team;
+
+  if (!objectp(team=Query(P_TEAM)))
+    return ({ME});
+  return team->Members();
+}
+
+string TeamPrefix() {
+  object team;
+
+  if (!objectp(team=Query(P_TEAM)))
+    return "";
+  return "["+team->Name()+"] ";
+}
+
+
+private int team_aufnahmewunsch(string arg) {
+  object pl;
+
+  if ((!objectp(pl=find_player(arg)) && !objectp(pl=present(arg,ENV)))
+      || pl->QueryProp(P_INVIS) || environment(pl)!=ENV)
+    return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
+  if (!living(pl))
+    return notify_fail(pl->Name(WER)+" ist etwas zu inaktiv.\n"),0;
+  if (pl==ME)
+    return notify_fail("Du bist eine Person zu wenig fuer ein Team.\n"),0;
+  SetProp(P_TEAM_NEWMEMBER,pl);
+  if (pl->IsTeamLeader()) {
+    write("Du bittest "+pl->name(WEN)+" um Aufnahme ins Team.\n");
+    tell_object(pl,TP->Name(WER)+" bittet Dich um Aufnahme ins Team.\n");
+  } else {
+    write("Du bittest "+pl->name(WEN)+" um Gruendung eines Teams.\n");
+    tell_object(pl,TP->Name(WER)+" bittet Dich um Gruendung eines Teams.\n");
+  }
+  return 1;
+}
+
+private int team_aufnahme(string arg) {
+  object pl,team;
+  int res;
+
+  if ((!objectp(pl=find_player(arg)) && !objectp(pl=present(arg,ENV)))
+      || pl->QueryProp(P_INVIS) || environment(pl)!=ENV)
+    return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
+  if (pl->QueryProp(P_TEAM_NEWMEMBER)!=ME)
+    return notify_fail(pl->Name(WER)+" hat Dich nicht um Aufnahme gebeten.\n"),
+      0;
+  if (pl==ME)
+    return notify_fail("Du bist eine Person zu wenig fuer ein Team.\n"),0;
+  if (!objectp(team=QueryProp(P_TEAM)))
+    team=clone_object(TEAM_OBJECT);
+  res=team->AddMember(pl);
+  if (!sizeof(team->Members()))
+    team->remove();
+  return res;
+}
+
+object IsTeamMove() {
+  if (!objectp(teammove) || (teammove!=Query(P_TEAM)))
+    teammove=0;
+  return teammove;
+}
+
+static void DoTeamAttack(object env, object callbackto) {
+  if (env==ENV && stringp(team_attack_cmd) && !IS_LEARNER(ME)
+      && (interactive(ME) || !query_once_interactive(ME))
+      && objectp(callbackto) && callbackto==Query(P_TEAM)) {
+    teammove=callbackto;
+    command(team_attack_cmd);
+  }
+  if (objectp(callbackto))
+    callbackto->TeamAttackExecuted_Callback(teammove?1:0);
+  teammove=0;
+}
+
+int CallTeamAttack(object env) {
+  if (stringp(team_attack_cmd)
+      && find_call_out("DoTeamAttack")<0
+      && PO
+      && PO==Query(P_TEAM))
+    return call_out("DoTeamAttack",0,env,PO),1;
+  return 0;
+}
+
+static int DoTeamFollow() {
+  string cmd;
+
+  if (!team_autofollow
+      || (!interactive(ME) && query_once_interactive(ME))
+      || IS_LEARNER(ME)
+      || !mappingp(team_follow_todo))
+    return 0;
+  if (!stringp(cmd=team_follow_todo[ENV]))
+    return team_follow_todo=0;
+
+  do {
+    m_delete(team_follow_todo,ENV);
+    tell_object(ME,sprintf("Du folgst Deinem Team mit \"%s\".\n",cmd));
+    command(cmd);
+  } while (get_eval_cost()>900000 && random(1000)>20 && objectp(ME)
+           && stringp(cmd=team_follow_todo[ENV]));
+
+  // Ist Spieler in Umgebung gelandet, fuer die noch ein
+  // Befehl auszufuehren ist?
+  if (!objectp(ME) || !stringp(team_follow_todo[ENV]))
+    return team_follow_todo=0;
+  while  (remove_call_out("DoTeamFollow")!=-1) ;
+  call_out("DoTeamFollow",0);
+  return 0;
+}
+
+int CallTeamFollow(object env, string cmd) {
+  if (!team_autofollow
+      || PO!=Query(P_TEAM)
+      || !PO
+      || !objectp(env)
+      || !stringp(cmd))
+    return 0;
+  if (!mappingp(team_follow_todo))
+    team_follow_todo=([]);
+  if (ENV!=env && !team_follow_todo[ENV])
+    return 0;
+  team_follow_todo[env]=cmd;
+  if (find_call_out("DoTeamFollow")<0)
+    call_out("DoTeamFollow",0);
+  return 1;
+}
+
+int ClearTeamFollow() {
+  if (PO!=Query(P_TEAM) || !PO)
+    return 0;
+  team_follow_todo=([]);
+  return 1;
+}
+
+mixed *PresentTeamRows() {
+  object team;
+  mixed *res;
+  int i;
+
+  if (!objectp(team=Query(P_TEAM))) {
+    res=EMPTY_TEAMARRAY;
+    res[0]=({ME});
+    return res;
+  }
+  res=team->PresentRows(ENV);
+  for (i=0;i<MAX_TEAMROWS;i++)
+    if (member(res[i],ME)>=0)
+      return res;
+  res[0]+=({ME});
+  return res;
+}
+
+varargs mixed *PresentEnemyRows(object *here) {
+  mixed *res,*rows;
+  mapping added_teams;
+  int i,j;
+  object ob,team;
+
+  added_teams=([Query(P_TEAM):1]); // Nicht auf eigenes Team hauen
+  res=EMPTY_TEAMARRAY;
+  if (!pointerp(here))
+    here=PresentEnemies();
+  for (i=sizeof(here)-1;i>=0;i--) {
+    if (!objectp(ob=here[i]))
+      continue;
+    if (!objectp(team=ob->QueryProp(P_TEAM))) {
+      res[0]+=({ob});
+      continue;
+    }
+    if (added_teams[team])
+      continue;
+    added_teams[team]=1;
+    rows=team->PresentRows(ENV);
+    for (j=0;j<MAX_TEAMROWS;j++)
+      res[j]+=rows[j];
+  }
+  return res;
+}
+
+varargs object SelectNearEnemy(object *here, int forcefrom) {
+  object ob,en,team;
+  mixed *rows;
+  int *prob,prot,i,r,sz,upsz,sum;
+
+  if (!pointerp(here))
+    here=PresentEnemies();
+  if (!objectp(ob=SelectEnemy(here)))
+    return 0;
+  en=ob->QueryProp(P_TEAM);          // Feindliches Team
+  if (objectp(team=Query(P_TEAM))) { // Eigenes Team
+    if (en==team) // Feind im eigenen Team, kein ANDERES Mitglied waehlen.
+      return ob;  // Aber auch ausserhalb Reihe 1 draufhauen
+    rows=team->PresentRows(ENV);
+    if (member(rows[0],ME)<0) // Stehe ich in der ersten Reihe?
+      return 0; // Falls nein ist auch kein Gegner nahe.
+  }
+  if (!objectp(en))
+    return ob; // Ist nicht in einem Team, also drauf.
+  rows=en->PresentRows(environment(ob));
+  prob=({1,0,0,0,0});
+  prot=sum=0;
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    if (prot>0) prot--;                // Schutzkegel nimmt ab.
+    if (!sz=sizeof(rows[i])) continue; // Gegner in dieser Reihe
+    upsz=sz-prot;if (upsz<0) continue; // Anzahl ungeschuetzter Gegner
+    prob[i]+=(upsz+sum);               // Wahrscheinlichkeit += ungeschuetzt
+    sum=prob[i];                       // Summe bisheriger Wahrscheinlichkeiten
+    if (sz>prot) prot=sz;              // Neuer Schutzkegel
+  }
+  r=random(sum);
+  for (i=0;i<MAX_TEAMROWS;i++)
+    if (r<prob[i])
+      break;
+  if (i>=MAX_TEAMROWS)
+    i=0;
+  if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
+    return en;
+  if (i && objectp(en=SelectEnemy(forcefrom?(here&rows[0]):rows[0])))
+    return en;
+  return ob;
+}
+
+varargs object SelectFarEnemy(object *here, int min, int max, int forcefrom) {
+  mixed *rows;
+  int *prob,i,r,sum;
+  object en;
+
+  if (max<0 || min>=MAX_TEAMROWS || max<min)
+    return 0;
+  if (min<0) min=0;
+  if (max>=MAX_TEAMROWS) max=MAX_TEAMROWS-1;
+  if (!pointerp(here))
+    here=PresentEnemies();
+  rows=PresentEnemyRows(here);
+  prob=({0,0,0,0,0});
+  sum=0;
+  for (i=min;i<=max;i++)
+    sum=prob[i]=sum+sizeof(rows[i])+max-i;
+
+  r=random(sum);
+  for (i=min;i<=max;i++)
+    if (r<prob[i])
+      break;
+  if (i>max)
+    i=min;
+  if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
+    return en;
+  for (i=min;i<=max;i++)
+    if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
+      return en;
+  return 0;
+}
+
+mixed _query_friend() {
+  mixed res;
+
+  if (res=Query(P_FRIEND))
+    return res;
+  if (objectp(res=Query(P_TEAM_ASSOC_MEMBERS))
+      && query_once_interactive(res))
+    return res;
+  return 0;
+}
+
+int DeAssocMember(object npc) {
+  mixed obs;
+  object team;
+
+  if (extern_call() && PO!=npc &&
+      member(({"gilden","spellbooks"}),
+             explode(object_name(PO),"/")[1])<0)
+    return 0;
+  obs=QueryProp(P_TEAM_ASSOC_MEMBERS);
+  if (!pointerp(obs))
+    return 0;
+  obs-=({npc,0});
+  SetProp(P_TEAM_ASSOC_MEMBERS,obs);
+  if (objectp(team=QueryProp(P_TEAM)))
+    team->RemoveAssocMember(ME,npc);
+  return 1;
+}
+
+int AssocMember(object npc) {
+  mixed obs;
+  object team;
+
+  if (extern_call() && PO!=npc &&
+      member(({"gilden","spellbooks"}),
+             explode(object_name(PO),"/")[1])<0)
+    return 0;
+  if (!objectp(npc)
+      || npc->QueryProp(P_TEAM_ASSOC_MEMBERS)
+      || IsEnemy(npc)
+      || npc==ME
+      || query_once_interactive(npc))
+    return 0;
+  obs=QueryProp(P_TEAM_ASSOC_MEMBERS);
+  if (objectp(obs))
+    return 0;
+  if (!pointerp(obs))
+    obs=({});
+  obs=(obs-({npc,0}))+({npc});
+  SetProp(P_TEAM_ASSOC_MEMBERS,obs);
+  npc->SetProp(P_TEAM_ASSOC_MEMBERS,ME);
+  if (objectp(team=QueryProp(P_TEAM)))
+    team->AddAssocMember(ME,npc);
+  return 1;
+}
+
+varargs void InsertEnemyTeam(mixed ens, int rek) {
+  object *obs,ob,eteam,team;
+  int i;
+
+  team=Query(P_TEAM);
+  // Alle Teammitglieder des Gegners sind Feind:
+  if (objectp(ens)) {
+    if (objectp(eteam=ens->QueryProp(P_TEAM))) {
+      if (eteam==team) // feindliches Team = eigenes Team?
+        return;        // also nicht alle Teammitglieder gegeneinander hetzen
+      ens=eteam->Members();
+    } else {
+      ens=({ens});
+    }
+  }
+  if (!pointerp(ens))
+    return;
+  ens-=({ME});
+
+  // Interactives sollen keine Interactives durch Team angreifen:
+  if (query_once_interactive(ME)) {
+    for (i=sizeof(ens)-1;i>=0;i--)
+      if (objectp(ob=ens[i]) && environment(ob)==environment()
+          && !query_once_interactive(ob))
+        InsertSingleEnemy(ob);
+  } else {
+    for (i=sizeof(ens)-1;i>=0;i--)
+      if (objectp(ob=ens[i]) && environment(ob)==environment())
+        InsertSingleEnemy(ob);
+  }
+
+  // Alle anderen Teammitglieder Informieren:
+  if (rek || !objectp(team) || !pointerp(obs=team->Members()))
+    return;
+  obs-=({ME});
+  obs-=ens;
+  for (i=sizeof(obs)-1;i>=0;i--)
+    if (objectp(ob=obs[i]))
+      ob->InsertEnemyTeam(ens,1);
+}
+
+int TeamFlee() {
+  object team;
+
+  if (Query(P_TEAM_WIMPY_ROW)<2 || !objectp(team=Query(P_TEAM)))
+    return 0;
+  if (!team->FleeToRow(ME))
+     return 0;
+  if (Query(P_TEAM_LEADER)==team) {
+    if (team_autofollow)
+      tell_object(ME,"Du versuchst zu fliehen, "+
+                  "Dein Team folgt Dir nicht mehr.\n");
+    team_autofollow=0;
+  }
+  return 1;
+}
+
+varargs mapping PresentTeamPositions(mixed pres_rows) {
+  mapping res;
+  int i,j;
+  object *obs,ob;
+
+  res=([]);
+  if (!pointerp(pres_rows))
+    pres_rows=PresentTeamRows();
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    obs=pres_rows[i];
+    for (j=sizeof(obs)-1;j>=0;j--)
+      if (objectp(ob=obs[j]) && !res[ob])
+        res[ob]=i+1;
+  }
+  return res;
+}
+
+varargs int PresentPosition(mixed pmap) {
+  object team;
+  int i;
+
+  if (!objectp(team=Query(P_TEAM)))
+    return 1;
+  if (mappingp(pmap))
+    return pmap[ME];
+  if (!pointerp(pmap))
+    pmap=team->PresentRows(ENV);
+  for (i=1;i<MAX_TEAMROWS;i++)
+    if (member(pmap[i],ME)>=0)
+      return i+1;
+  return 1;
+}
+
+#define FILLSTRING "                                        "
+varargs private string center_string(string str, int w) {
+  return (FILLSTRING[0..((w-sizeof(str))/2-1)]+str+FILLSTRING)[0..(w-1)];
+}
+
+private int ShowTeamRows() {
+  int i,j,sz;
+  mixed *pres_rows;
+  object *obs,ob;
+  string str;
+
+  pres_rows=PresentEnemyRows();
+  for (sz=MAX_TEAMROWS-1;sz>=0;sz--)
+    if (sizeof(pres_rows[sz]))
+      break;
+  for (i=sz;i>=0;i--) {
+    obs=pres_rows[i];str="";
+    for (j=sizeof(obs)-1;j>=0;j--)
+      if (objectp(ob=obs[j])) {
+        if (str!="") str+=" / ";
+        str+=ob->Name(RAW);
+      }
+    printf("%d. %s\n",i+1,center_string(str,75));
+  }
+  if (sz>=0)
+    write("   ---------------------------------------------------------------------------\n");
+  pres_rows=PresentTeamRows();
+  for (sz=MAX_TEAMROWS-1;sz>0;sz--)
+    if (sizeof(pres_rows[sz]))
+      break;
+  for (i=0;i<=sz;i++) {
+    obs=pres_rows[i];str="";
+    for (j=sizeof(obs)-1;j>=0;j--)
+      if (objectp(ob=obs[j])) {
+        if (str!="") str+=" / ";
+        str+=ob->Name(RAW);
+      }
+    printf("%d. %s\n",i+1,center_string(str,75));
+  }
+  return 1;
+}
+
+varargs int team_list(string arg) {
+  object *tobs,*obs,tob,ob,ld;
+  string *nms,*tnms,str;
+  int i,j;
+
+  if (!pointerp(tobs=TEAM_MASTER->ListTeamObjects())) return 0;
+  if (arg!="alle") arg=0;
+  tnms=({});
+  for (i=sizeof(tobs)-1;i>=0;i--) {
+    if (!objectp(tob=tobs[i])
+        || !objectp(ld=tob->Leader())
+        || (!query_once_interactive(ld) && !arg)
+        || !pointerp(obs=tob->Members()))
+      continue;
+    nms=({});
+    for (j=sizeof(obs)-1;j>=0;j--) {
+      if (!objectp(ob=obs[j])
+          || (!query_once_interactive(ob) &&!arg))
+        continue;
+      if (!stringp(str=ob->Name(WER))) str="?";
+      if (ob==ld) str+="(*)";
+      nms+=({str});
+      nms=sort_array(nms,#'>);
+    }
+    if (!stringp(str=tob->Name())) str="Team ?";
+    str+=": ";
+    tnms+=({break_string(implode(nms,", "),78,str)});
+    tnms=sort_array(tnms,#'<);
+  }
+  if (sizeof(tnms))
+    tell_object(ME, sprintf("%@s\n", tnms));
+  else
+    tell_object(ME, "Keine Teams gefunden.\n"); 
+  
+  return 1;
+}
+
+varargs int teamcmd(string arg) {
+  string *words,narg;
+  object team;
+
+  if (!arg)
+    arg="";
+  if (!stringp(narg=TP->_unparsed_args()))
+    narg = arg;
+  if (!sizeof(words=explode(arg," ")))
+    return 0;
+ 
+  if (sizeof(words) > 1) {
+      arg=implode(words[1..]," ");
+      narg = implode(explode(narg, " ")[1..], " ");
+  }
+  else
+      arg = narg = "";
+
+  switch(words[0]) { // Befehle die keine Mitgliedschaft erfordern:
+  case "aufnahme":
+    return team_aufnahme(arg);
+  case "folge":
+    return team_aufnahmewunsch(arg);
+  case "?":
+  case "hilfe":
+    return team_help();
+  case "liste":
+    return team_list(arg);  
+  case "uebersicht":
+    return ShowTeamRows();
+  default:;
+  }
+
+  if (!objectp(team=QueryProp(P_TEAM)))
+    return notify_fail("Du bist in keinem Team.\n"),0;
+
+  switch(words[0]) {
+  case "angriffsbefehl":
+    if (narg=="") narg=0;
+    team_attack_cmd=narg;
+    if (stringp(narg))
+      write("Du beginnst den Kampf mit \""+narg+"\"\n");
+    else
+      write("Du hast den Teamangriffsbefehl deaktiviert.\n");
+    break; // NICHT return!
+  case "autofolge":
+  case "autof":
+    if (arg=="ein" || arg=="an") {
+      team_autofollow=1;
+      if (IsTeamLeader())
+        write("Dein Team folgt Dir.\n");
+      else
+        write("Du folgst jetzt dem Teamleiter.\n");
+    } else {
+      team_autofollow=0;
+      if (IsTeamLeader())
+        write("Dein Team folgt Dir nicht mehr.\n");
+      else
+        write("Du folgst jetzt nicht mehr dem Teamleiter.\n");
+    }
+    break; // NICHT return!
+  default: ;
+  }
+  return team->TeamCmd(words[0],narg); // Befehle die Mitgliedschaft erfordern:
+}
+
+varargs void InformRowChange(int from, int to, object caster) {
+
+  if (caster) return; // Fuer den Fall, dass Gildenobjekt==ME ist
+  if (PO!=Query(P_TEAM)) return;
+#if __BOOT_TIME__ < 1281904437
+  mixed gilde = QueryProp(P_GUILD);
+  if (!stringp(gilde)) return;
+  if (!objectp(gilde=find_object("/gilden/"+gilde))) return;
+  gilde->InformRowChange(from,to,ME);
+#endif
+  HookFlow(H_HOOK_TEAMROWCHANGE, ({from,to}) );
+}
diff --git a/std/mailcabin.c b/std/mailcabin.c
new file mode 100644
index 0000000..1a94e85
--- /dev/null
+++ b/std/mailcabin.c
@@ -0,0 +1,12 @@
+#pragma strict_types,save_types
+
+inherit "/p/service/loco/std/mailcabin";
+
+void create() {
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);
+      return;
+  }
+  ::create();
+}
+
diff --git a/std/more.c b/std/more.c
new file mode 100644
index 0000000..8518981
--- /dev/null
+++ b/std/more.c
@@ -0,0 +1,143 @@
+// MorgenGrauen MUDlib
+//
+// more.c -- more files
+//
+// $Id: more.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/util/pager";
+
+#include <pager.h>
+
+#ifdef GERMAN
+# define MSG_WRAPPED	" VOM ANFANG"
+# define MSG_NOT_FOUND	" NICHTS GEFUNDEN"
+# define MSG_NO_REGX	" FEHLENDER REG. AUSDRUCK"
+# define MSG_ILLEGAL	" ILLEGALE ZEILENZAHL"
+# define MSG_HELP       "\
+Hilfe fuer more:\n\
+\n\
+b,B        -- Eine Seite zurueck.\n\
+u,U        -- Eine halbe Seite zurueck.\n\
+f,F        -- Eine Seite vorwaerts.\n\
+d,D        -- Eine halbe Seite vorwaerts.\n\
+<Zeile>    -- Springe zu Zeile <Zeile>\n\
+q,x        -- Verlassen von more.\n\
+/<RegExp>  -- Nach dem regulaeren Ausdruck <RegExp> suchen.\n"
+#else
+# define MSG_WRAPPED	" WRAPPED"
+# define MSG_NOT_FOUND	" NOTHING FOUND"
+# define MSG_NO_REGX	" NO PREV REGULAR EXPR"
+# define MSG_ILLEGAL	" ILLEGAL LINE NUMBER"
+# define MSG_HELP       "\
+Help for more:\n\
+\n\
+b,B        -- One page back.\n\
+u,U        -- Half a page back.\n\
+f,F        -- One page forward.\n\
+d,D        -- Half a page forward.\n\
+<line>     -- Jump to line number <line>\n\
+q,x        -- Quit or eXit more.\n\
+/<regexp>  -- Search for the regular expression <regexp>.\n"
+#endif
+
+private nosave string cprompt = "";
+
+string prompt(mixed pinfo, string add)
+{
+  int line,max; max = 1;
+  if(pointerp(pinfo))
+  {
+     if(pinfo[CURL] + pinfo[PAGE] >= pinfo[MAXL]) line = pinfo[MAXL];
+     else line = pinfo[CURL] + pinfo[PAGE];
+     max = pinfo[MAXL];
+  }
+  if (pinfo[FLAG] & E_ABS)
+    return sprintf("%s (%d/%d)%s %s",
+		   ::prompt(pinfo, ""),
+		   line-1, pinfo[MAXL],
+		   cprompt, stringp(add)?add:"");
+  else
+    return sprintf("%s(%d%%)%s %s",
+		   ::prompt(pinfo, ""),
+		   line*100/max, 
+		   cprompt, stringp(add)?add:"");
+}
+
+int search(mixed pinfo)
+{
+  int l, lines;
+  mixed tmp;
+  l = pinfo[CURL];
+  while((tmp = fread(pinfo, l, pinfo[PAGE])) &&
+        !sizeof(regexp(old_explode(tmp, "\n"), pinfo[REGX])))
+    l += pinfo[PAGE];
+  if(!tmp)
+  {
+    cprompt += MSG_WRAPPED;
+    l = 1;
+    while(l < pinfo[CURL] &&
+          (tmp = fread(pinfo, l, pinfo[PAGE])) &&
+          !sizeof(regexp(old_explode(tmp, "\n"), pinfo[REGX])))
+      l += pinfo[PAGE];
+    if(l >= pinfo[CURL]) return 0;
+  }
+  return l;
+}
+
+varargs int eval_command(mixed in, mixed pinfo)
+{
+  cprompt = "";
+  if(stringp(in))
+    switch(in)
+    {
+    case "?":
+      write(MSG_HELP+"\n");
+      return 0; 
+    case "b":
+    case "B":
+      pinfo[CURL] -= pinfo[PAGE];
+      break;
+    case "u":
+    case "U":
+      pinfo[CURL] -= pinfo[PAGE] / 2;
+      break;
+    case "f":
+    case "F":
+      pinfo[CURL] += pinfo[PAGE];
+      break;
+    case "d":
+    case "D":
+      pinfo[CURL] += pinfo[PAGE] / 2;
+      break;
+    default:
+    {
+      int l;
+      if(l = to_int(in))
+      {
+        if(l > pinfo[MAXL] || l < 1) return (cprompt = MSG_ILLEGAL, 0);
+        pinfo[CURL] = l;
+        break;
+      }
+      if(sizeof(in) && in[0] == '/')
+      {
+	if(sizeof(in) == 1)
+	{
+	  if(!pinfo[REGX]) return (cprompt = MSG_NO_REGX, 0);
+	}
+	else pinfo[REGX] = in[1..];
+	if(l = search(pinfo)) pinfo[CURL] = l;
+	else return (cprompt = MSG_NOT_FOUND, 0);
+      }
+      else return ::eval_command(in, pinfo);    
+      break;
+    }
+    }
+  else return ::eval_command(in, pinfo);
+  return ::eval_command(-1, pinfo);
+}   
diff --git a/std/newsclient.c b/std/newsclient.c
new file mode 100644
index 0000000..b047e41
--- /dev/null
+++ b/std/newsclient.c
@@ -0,0 +1,244 @@
+// MorgenGrauen MUDlib
+//
+// newsclient.c -- Nachrichtenbretter
+//
+// $Id: newsclient.c 8349 2013-02-04 21:15:28Z Zesstra $
+
+/* 1992 JOF */
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/thing";
+inherit "/std/more";
+
+#include <config.h>
+#include <properties.h>
+#include <news.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <defines.h>
+#include <input_to.h>
+
+string GROUP;
+string writer;
+string usemsg;
+
+void SetInUseMessage(string str)
+{
+  usemsg=str;
+}
+
+string SetGroup(string str)
+{
+  return GROUP=str;
+}
+
+protected void create()
+{
+  thing::create();
+  writer=="";
+
+}
+
+void thinginit() /* If someone doesnt want our add_action he may call this */
+{
+  thing::init();
+}
+
+varargs int remove(int silent)
+{
+  return thing::remove();
+}
+
+void init()
+{
+  thing::init();
+  add_action("schreib","schreib",1);
+  add_action("lies","lies");
+  add_action("loesche","loesch",1);
+}
+
+string ladj(string str, int weite)
+{
+  return((str+"                                            ")[0..weite-1]);
+}
+
+string QueryLong()
+{
+  mixed *messages;
+  string desc;
+  int i;
+
+  desc=QueryProp(P_LONG);
+  desc+="\n";
+  messages=NEWSSERVER->GetNotes(GROUP);
+  if (!pointerp(messages) || !sizeof(messages)) 
+    return desc+"Zur Zeit befinden sich keine Nachrichten am Brett.\n";
+  if (sizeof(messages)==1)
+    desc+="Zur Zeit befindet sich eine Nachricht am Brett:\n\n";
+  else
+    desc+="Zur Zeit befinden sich "+sizeof(messages)+" Nachrichten am Brett:\n\n";
+  for (i=0;i<sizeof(messages);i++)
+  {
+    if (i<9) desc+=" ";
+    desc+=""+(i+1)+". ";
+    desc+=(ladj(messages[i][M_TITLE],45)+" ("+
+	   ladj(messages[i][M_WRITER]+",",13)+
+	   dtime(messages[i][M_TIME])[4..24] + ")\n");
+  }
+  return desc;
+}
+
+varargs string long(int mode)
+{
+  return QueryLong();
+}
+
+mixed *message;
+
+int schreib(string str)
+{
+  int err;
+
+  if (!this_interactive() || !this_interactive()->query_real_name()) return 0;
+  if (writer)
+  {
+    write("Das geht im Moment nicht, da "+capitalize(writer)+" gerade an diesem Brett schreibt.\n");
+    if (usemsg) write(usemsg+"\n");
+    return 1;
+  }
+  _notify_fail("Du musst einen Titel fuer die Notiz angeben !\n");
+  if (!str) return 0;
+  switch (err=NEWSSERVER->AskAllowedWrite(GROUP))
+  {
+    case  1: break;
+	     case -1: write("Du darfst keine Notes an dieses Brett heften !\n"); return 1;
+	     case -3: write("Das Brett ist leider voll !\n"); return 1;
+	     default: write("Interner Fehler "+err+", Erzmagier verstaendigen !\n"); return 1;
+	   }
+  writer=this_interactive()->query_real_name();
+  message=allocate(6);
+  message[M_BOARD]=GROUP;
+  message[M_TITLE]=str;
+  message[M_MESSAGE]="";
+  write("Ok, beende Deine Notiz mit '**' oder brich sie mit '~q' ab !\n");
+  if (IS_WIZARD(this_player()))
+    write("Als Magier"+(this_player()->QueryProp(P_GENDER)==FEMALE ? "in" : "")+
+	  " kannst Du per '~r<filename>' ein File in die Notiz einlesen.\n");
+  input_to("get_note_line", INPUT_PROMPT, ":");
+  return 1;
+}
+
+int get_note_line(string str)
+{
+  int err;
+  if (str=="**" || str=="~.")
+  {
+    writer=0;
+    switch (err=(NEWSSERVER->WriteNote(message)))
+    {
+      case 1:  write("Ok, Nachricht aufgehaengt.\n");
+	       say(message[M_WRITER]+" hat eine Nachricht ans Brett gehaengt.\n");
+	       return 1;
+	       case -1: write("Du darfst leider keine Nachrichten hier aufhaengen.\n");
+	       return 1;
+	       case -3: write("Das Brett ist leider voll !\n");
+	       return 1;
+	       default: write("Interner Fehler "+err+" aufgetreten ! Bitte Erzmagier verstaendigen.\n");
+	       return 1;
+	     }
+  }
+  if (str=="~q")
+  {
+    writer=0;
+    write("Notiz nicht angeheftet.\n");
+    return 1;
+  }
+  if (str[0..1]=="~r" && IS_WIZARD(this_player())) {
+    str=str[2..<1];
+    if (str[0]==' ') str=str[1..<1];
+    if (!str || catch(err=file_size(str);publish) || err<0) {
+      write("File nicht gefunden.\n");
+      input_to("get_note_line", INPUT_PROMPT, ":");
+      return 1;
+    }
+    str=read_file(str);
+    if (!str){
+      write("Zu gross!\n");
+      input_to("get_note_line", INPUT_PROMPT, ":");
+      return 1;
+    }
+    write("Ok.\n");
+  }
+  if (message[M_MESSAGE] && message[M_MESSAGE]!="") message[M_MESSAGE]+="\n";
+  message[M_MESSAGE]+=str;
+  input_to("get_note_line", INPUT_PROMPT, ":");
+  return 1;
+}
+
+int lies(string str)
+{
+  int num;
+  mixed *messages;
+
+  if (!str || str=="" || sscanf(str,"%d",num)!=1 || num<=0)
+  {
+    _notify_fail("WELCHE Nachricht willst Du lesen ?\n");
+    return 0;
+  }
+  num--;
+  messages=(NEWSSERVER->GetNotes(GROUP));
+  if (sizeof(messages)<=num)
+  {
+    _notify_fail("So viele Nachrichten sind da nicht !\n");
+    return 0;
+  }
+  this_player()->More(messages[num][M_TITLE]+" ("+messages[num][M_WRITER]+", "+
+      dtime(messages[num][M_TIME])[5..26] + "):\n" + 
+       messages[num][M_MESSAGE]);
+  return 1;
+}
+
+int loesche(string str)
+{
+  int num;
+
+  if (!str || str=="" || sscanf(str,"%d",num)!=1 || num<=0)
+  {
+    _notify_fail("WELCHE Nachricht willst Du loeschen ?\n");
+    return 0;
+  }
+  num--;
+  switch (NEWSSERVER->RemoveNote(GROUP, num)){
+    case 1: write("Ok.\n");
+	    say(this_player()->name()+" entfernt eine Nachricht vom Brett.\n");
+	    return 1;
+	    case -1: write("Das darfst Du nicht.\n");
+	    say(this_player()->name()+" versucht, eine Nachricht vom Brett zu reissen.\n");
+	    return 1;
+	    case -3: write("So viele Nachrichten sind da nicht !\n");
+	    return 1;
+	    default: write("Interner Fehler. Bitte Erzmagier verstaendigen !\n");
+	    return 1;
+	  }
+  return 0;
+}
+
+void Crumble()
+{
+  if (environment() && living(environment()))
+    return;
+  say(capitalize(name(WER))+" zerfaellt zu Staub, der sich sofort verteilt.\n");
+  write(capitalize(name(WER))+" zerfaellt zu Staub, der sich sofort verteilt.\n");
+  remove();
+}
+
+varargs int move(object target,mixed method)
+{
+  if (objectp(target)&&!living(target)&&environment()&&living(environment()))
+    call_out("Crumble",30+random(30));
+  return ::move(target, method);
+}
diff --git a/std/npc.c b/std/npc.c
new file mode 100644
index 0000000..f612f1e
--- /dev/null
+++ b/std/npc.c
@@ -0,0 +1,175 @@
+// MorgenGrauen MUDlib
+//
+// npc.c -- a generic npc (non player character)
+//
+// Testversion mit support fuer AddItem
+// soll spaeter in npc.c aufgehen
+//
+// $Id: npc.c 9478 2016-02-19 21:18:35Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/properties";
+inherit "/std/hook_provider";
+inherit "/std/living/description";
+inherit "/std/living/light";
+inherit "/std/living/life";
+inherit "/std/living/attributes";
+inherit "/std/npc/moving";
+inherit "/std/living/skill_attributes";
+inherit "/std/living/skills";
+inherit "/std/living/clothing";
+inherit "/std/npc/combat";
+inherit "/std/npc/chat";
+inherit "/std/npc/comm";
+inherit "/std/container/restrictions";
+inherit "/std/thing/commands";
+inherit "/std/thing/language";
+inherit "/std/npc/info";
+inherit "/std/npc/put_and_get";
+inherit "/std/npc/items";
+inherit "/std/thing/envchk";
+inherit "/std/living/helpers";
+
+#include <config.h>
+#include <properties.h>
+#include <ansi.h>
+#include <wizlevels.h>
+#include <living.h>
+#include <language.h>
+#include <attributes.h>
+#include <defines.h>
+#include <health.h>
+#include <npc.h>
+#include <moving.h>
+#include <guard.h>
+
+static int _set_level(int arg);
+
+protected void create()
+{ 
+  seteuid(getuid());
+  properties::create();
+  restrictions::create();
+  commands::create();
+  light::create();
+  description::create();
+  attributes::create();
+  clothing::create();
+  life::create();
+  enable_commands();
+  comm::create();
+  combat::create();
+  info::create();
+  put_and_get::add_put_and_get_commands();
+  add_team_commands();
+  items::create();
+  envchk::create();
+  moving::create();
+
+  add_action("UseSpell","",1);
+
+  SetProp(P_LIGHT_MODIFIER, 1);
+  SetProp(P_WEIGHT_PERCENT, 100);
+  SetProp(P_NAME, "Niemand");
+  SetProp(P_MSGIN, "kommt an");
+  SetProp(P_MSGOUT, "geht");
+  SetProp(P_MMSGIN, "erscheint in einer Rauchwolke");
+  SetProp(P_MMSGOUT, "verschwindet mit Knall und Schwefelduft");
+  SetProp(P_GENDER, NEUTER );
+  SetProp(P_WEIGHT, 75000);
+  SetProp(P_MAX_WEIGHT, 50000);
+  SetProp(P_RACE, "Npc");
+  SetProp(P_MAX_HP,100);
+  SetProp(P_MAX_SP,100);
+  SetProp(P_HP,50);
+  SetProp(P_SP,50);
+  SetProp(P_MAX_ALCOHOL,100);
+  SetProp(P_MAX_DRINK,100);
+  SetProp(P_MAX_FOOD,100);
+  SetProp(P_DRINK, 0);
+  SetProp(P_FOOD, 0);
+  SetProp(P_ALCOHOL, 0);
+  SetProp(P_HANDS, ({ "", 30 }) );
+  SetProp(P_MAX_HANDS, 2);
+  SetProp(P_NPC, 1);
+  SetProp(P_GUARD,100);
+  SetProp(P_NO_TPORT,NO_TPORT_IN);
+
+  set_heart_beat(1);
+  heartbeat=1;
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset(){
+  items::reset();
+  combat::reset();
+}
+
+static int _set_level(int arg)
+{
+  Set(P_LEVEL, arg);
+  SetAttr(A_CON, arg);
+  SetAttr(A_DEX, arg);
+  SetAttr(A_INT, arg);
+  SetAttr(A_STR, arg);
+  return(arg); //Rueckgabewert noetig!
+}
+
+varargs void create_default_npc(int level, int maxhp)
+{ 
+  if(level < 1) return;
+  SetProp(P_LEVEL, level);
+  if (maxhp<=0) maxhp = 42 + level*8;
+  SetProp(P_MAX_HP, maxhp);
+  SetProp(P_MAX_SP, maxhp);
+  SetProp(P_HANDS, ({" mit blossen Haenden", level*10 }) );
+  SetProp(P_BODY, (level*20)/3);
+  SetProp(P_XP, level * maxhp * 50);
+} 
+
+protected void heart_beat()
+{
+  if( environment() ) life::heart_beat();
+  if (!this_object()) return; // Evtl. an Gift gestorben...
+  combat::heart_beat();
+  chat::heart_beat();
+}
+
+void give_notify(object ob)
+{
+  put_and_get::give_notify(ob);
+}
+
+// Force the monster to do a command.
+int command_me(string cmd) { return command(cmd); }
+
+void init()
+{
+  combat::init();
+  info::init();
+  commands::init();
+//  description::init();
+}
+
+// items initialisieren?
+protected void NotifyMove(object dest, object oldenv, int method) {
+    
+  ::NotifyMove(dest, oldenv, method);
+  // gestorben?
+  if (!objectp(this_object())) return;
+
+  if ( Query(NPC_NEEDS_ITEM_INIT, F_VALUE))
+      _clone_items();
+}
+
+string _query_race()
+{
+   if (stringp(Query(P_RACE))) return capitalize(Query(P_RACE));
+}
diff --git a/std/npc/chat.c b/std/npc/chat.c
new file mode 100644
index 0000000..7f4f89b
--- /dev/null
+++ b/std/npc/chat.c
@@ -0,0 +1,66 @@
+// MorgenGrauen MUDlib
+//
+// npc/chat.c -- Labernde NPCs
+//
+// $Id: chat.c 6801 2008-03-21 23:34:46Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <properties.h>
+#define NEED_PROTOTYPES
+#include <living/combat.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#define ME this_object()
+
+/*
+ * Some simple chat variables
+ */
+
+/*
+ * heart_beat is called so the monster may chat.
+ */
+void SetChats(int chance, mixed strs) {
+  if (!pointerp(strs))
+    return;
+  SetProp(P_CHAT_CHANCE,chance);
+  SetProp(P_CHATS,strs);
+}
+
+void SetAttackChats(int chance, mixed strs) {
+  if (!pointerp(strs))
+    return;
+  SetProp(P_ACHAT_CHANCE,chance);
+  SetProp(P_ACHATS,strs);
+}
+
+void DoAttackChat() {
+  string* c;
+  if (!ME || !environment(ME))
+    return;
+  if (QueryProp(P_DISABLE_ATTACK)>0)return ;
+  if (random(100) < QueryProp(P_ACHAT_CHANCE))
+    if ((c = QueryProp(P_ACHATS)) && sizeof(c)) 
+      tell_room(environment(ME),
+		process_string(c[random(sizeof(c))]));
+}
+
+void DoChat() {
+  string *c;
+  if (!ME || !environment(ME))
+    return;
+  if (random(100) < QueryProp(P_CHAT_CHANCE))
+    if ((c = QueryProp(P_CHATS)) && sizeof(c)) 
+      tell_room(environment(ME),
+		process_string(c[random(sizeof(c))]));
+}
+
+protected void heart_beat()
+{
+  if( InFight() ) DoAttackChat();
+  else            DoChat();
+}
diff --git a/std/npc/combat.c b/std/npc/combat.c
new file mode 100644
index 0000000..747714b
--- /dev/null
+++ b/std/npc/combat.c
@@ -0,0 +1,407 @@
+// MorgenGrauen MUDlib
+//
+// npc/combat.c -- NPC-spezifische Kampffunktionen
+//
+// $Id: combat.c 9488 2016-02-19 21:24:04Z Arathorn $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "std/living/combat";
+
+#include <combat.h>
+#include <language.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+
+#define NEED_PROTOTYPES 1
+#include <living/life.h>
+#undef NEED_PROTOTYPES
+
+#define HB_CHECK 7
+#define ME this_object()
+#define STATMASTER "/p/service/rochus/guildstat/master"
+
+nosave int heartbeat, beatcount;
+
+private void catch_up_hbs();
+
+protected void create() {
+  ::create();
+  beatcount=1;
+  heartbeat=1;
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// aktuelles Lebewesen, fuer das dieser NPC gerade taetig ist. Default:
+// Spieler, bei dem er als Helfer-NPC registriert ist.
+public object QueryUser()
+{
+  mixed helperdata = QueryProp(P_HELPER_NPC);
+  if (pointerp(helperdata) && objectp(helperdata[0]))
+    return helperdata[0];
+  return 0;
+}
+
+// ggf. Feinde expiren. Soll das Problem verringern, dass Spieler nach Tagen
+// erst die Meldung kriegen, dass der NPC sie nicht mehr jagt, wenn der HB
+// reaktivert wird.
+void reset() {
+  // ggf. die abgeschalteten HBs nachholen.
+  if (!heartbeat)
+    catch_up_hbs();
+  // ggf. P_ENEMY_DAMAGE zuruecksetzen
+  ResetEnemyDamage();
+}
+
+static void _set_max_hp(int i) {
+  Set(P_MAX_HP,i);
+  SetProp(P_HP,i);
+}
+
+static void _set_max_sp(int i) {
+  Set(P_MAX_SP,i);
+  SetProp(P_SP,i);
+}
+
+
+// Check-Funktion fuer die P_NO_ATTACK-QueryMethod
+static mixed _check_immortality()
+{
+    int t;
+
+    if ( !(t = Query("time_to_mortality")) || time() > t ){
+        // Zeit ist abgelaufen - wieder angreifbar machen
+        Set( P_NO_ATTACK, 0, F_QUERY_METHOD );
+        heartbeat = 1;
+        beatcount = 1;
+        set_heart_beat(1);
+
+        return 0;
+    }
+
+    // der NPC ist noch unangreifbar
+    return break_string( capitalize(name( WER, 1 )) + " versteckt sich hinter "
+                         "einem Fehler im Raum-Zeit-Gefuege und entgeht so "
+                         "voruebergehend allen Angriffen.", 78 );
+}
+
+
+// wenn der HeartBeat buggt, wird diese Funktion vom Master aufgerufen
+public void make_immortal()
+{
+    // fuer 5 Minuten unangreifbar machen
+    Set( P_NO_ATTACK, #'_check_immortality, F_QUERY_METHOD );
+
+    Set( "time_to_mortality", time() + 300, F_VALUE );
+
+    // damit die Spieler keinen Vorteil durch den Bug haben, heilen
+    heal_self(10000);
+
+    // da nun der Heartbeat abgeschaltet ist und normalerweise erst
+    // reaktiviert wird, sobald jemand nach 5min P_NO_ATTACK abfragt, muss man
+    // aber auf Viecher achten, die immer nen Heartbeat haben wollen. In dem
+    // fall per call_out selber die Prop abfragen.
+    if (QueryProp(P_HB))
+      call_out(#'QueryProp, 301, P_NO_ATTACK);
+}
+
+
+// Damit NPCs gegeneinander weiterkaempfen, auch wenn kein Spieler
+// in der Naehe ist:
+static int _query_hb()
+{
+    // TODO: return InFight() || Query(P_HB, F_VALUE), sobald InFight()
+    // geaendert.
+    return (InFight() || Query(P_HB,F_VALUE)) ? 1 : 0;
+}
+
+
+#define SPELL_TOTALRATE 0
+#define SPELL_DAMAGE 1
+#define SPELL_TEXT_FOR_ENEMY 2
+#define SPELL_TEXT_FOR_OTHERS 3
+#define SPELL_DAMTYPE 4
+#define SPELL_FUNC 5
+#define SPELL_ARG 6
+
+varargs int AddSpell(int rate, int damage, string TextForEnemy,
+                     string TextForOthers, string|string* dam_type, string func,
+                     int|mapping spellarg) {
+  mixed *spells;
+  int total_rates;
+
+  if (rate<0 || damage<=0 || !stringp(TextForEnemy) ||
+      !stringp(TextForOthers))
+     return 0;
+
+  if (stringp(dam_type))
+     dam_type = ({dam_type});
+  else if (!pointerp(dam_type))
+      dam_type = ({DT_MAGIC});
+  
+  // Tatsaechlich ist es immer ein nicht-physischer Angriff, wenn spellarg ein
+  // int ist, weil wenn spellarg==0 ist der Default nicht-physischer Angriff
+  // und bei spellarg!=0 auch. Nur mit einem mapping kann man einen phys.
+  // Angriff erzeugen.
+  if (intp(spellarg))
+    spellarg = ([SP_PHYSICAL_ATTACK: 0]);
+
+  // Falls vorhanden, alte Syntax auf die von replace_personal() anpassen,
+  // die im heart_beat() beim Ausgeben der Meldung verwendet wird.
+  if ( strstr(TextForOthers, "@", 0) != -1 )
+  {
+    // Zeichen nach @WER & Co in runde Klammern einschliessen, damit es als
+    // Sub-Pattern im Ausgabestring wiederholt werden kann. Ansonsten wuerde
+    // es mit ersetzt.
+    TextForOthers = regreplace(TextForOthers, "@WER([^1-9])", "@WER1\\1", 1);
+    TextForOthers = regreplace(TextForOthers, "@WESSEN([^1-9])",
+                               "@WESSEN1\\1", 1);
+    TextForOthers = regreplace(TextForOthers, "@WEM([^1-9])", "@WEM1\\1", 1);
+    TextForOthers = regreplace(TextForOthers, "@WEN([^1-9])", "@WEN1\\1", 1);
+  }
+  total_rates=Query("npc:total_rates")+rate;
+  spells=Query(P_SPELLS);
+  if (!pointerp(spells))
+    spells=({});
+  spells+=({({total_rates, damage, TextForEnemy, TextForOthers,
+              dam_type, func, spellarg})});
+  Set(P_SPELLS,spells);
+  Set("npc:total_rates",total_rates);
+  return 1;
+}
+
+int AutoAttack(object ob) {
+  mixed m;
+
+  if (!query_once_interactive(ob))
+    return 0;
+  if (mappingp(m=QueryProp(P_AGGRESSIVE))) {
+    mixed *ind,x,z;
+    float f;
+    int i,n;
+
+    ind=m_indices(m)-({0});n=0;f=0.0;
+    for (i=sizeof(ind)-1;i>=0;i--) {
+      x=ind[i];
+      if ((z=m[x][ob->QueryProp(x)]) || (z=m[x][0])) {
+  f=f+(float)z;
+  n++;
+      }
+    }
+    if (n)
+      m=f/((float)n);
+    else
+      m=m[0];
+  }
+  if (((int)(100*(m+ob->QueryProp(P_AGGRESSIVE))))<=random(100))
+    return 0;
+  if  (IS_LEARNER(ob)
+       && (ob->QueryProp(P_INVIS)
+     || ob->QueryProp(P_WANTS_TO_LEARN)))
+    return 0;
+  return 1;
+}
+
+void SpellAttack(object enemy) {
+}
+
+#if 0
+TJ(string s) {
+  object o;
+  if (o=find_player("jof"))
+    tell_object(o,sprintf("%O: %s\n",this_object(),s));
+}
+#else
+#define TJ(x)
+#endif
+
+protected void heart_beat() {
+  int r,i;
+  mixed env,*spells, sinfo;
+  object enemy;
+
+  if ( --beatcount < 0 )
+      beatcount = 0;
+  
+  if (!beatcount && !Query(P_HB)) {
+    if (!environment()) {
+      set_heart_beat(0);
+      heartbeat = 0;
+      if( clonep(this_object()) ) remove();
+      return;
+    }
+    if (!QueryProp(P_POISON)) {
+      // Spieler anwesend?
+      env = filter(all_inventory(environment()), #'query_once_interactive);
+      if (!sizeof(env)) {
+	// Nein, HBs abschalten.
+	set_heart_beat(0);
+	heartbeat=0;
+	TJ("OFF\n");
+	beatcount=HB_CHECK;
+	Set("npc:beat_off_num",absolute_hb_count());
+	return;
+      }
+    }
+  }
+  ::heart_beat();
+  if (!ME)
+      return;
+  enemy=SelectEnemy();
+  if (QueryProp(P_AGGRESSIVE)
+      && (!enemy || environment()!=environment(enemy))
+      && !beatcount) {
+    beatcount=HB_CHECK;
+    env=filter(all_inventory(environment()),#'AutoAttack);
+    if (!sizeof(env))
+       return;
+    i=random(sizeof(env));
+    Kill(env[i]);
+  }
+  else if (!beatcount)
+    beatcount=HB_CHECK;
+  if (!objectp(enemy) ||QueryProp(P_DISABLE_ATTACK)>0)
+    return;
+  SpellAttack(enemy);
+
+  if (!pointerp(spells=Query(P_SPELLS))
+      || !sizeof(spells)
+      || !objectp(enemy=SelectEnemy())
+      || environment(enemy)!=environment()
+      || (QueryProp(P_DISABLE_ATTACK)>0)
+      || random(100)>Query(P_SPELLRATE))
+    return;
+  r=random(Query("npc:total_rates"));
+  for (i=sizeof(spells)-1;(i>0 && spells[i-1][SPELL_TOTALRATE]>r);i--)
+    ;
+  string akt_spell_mess = spells[i][SPELL_TEXT_FOR_ENEMY];
+  if ( sizeof(akt_spell_mess) )
+    tell_object(enemy, break_string(akt_spell_mess, 78));
+  akt_spell_mess = spells[i][SPELL_TEXT_FOR_OTHERS];
+  // Nur, wenn ueberhaupt eine Meldung gesetzt wurde, muss diese verarbeitet
+  // werden.
+  if (stringp(akt_spell_mess) && sizeof(akt_spell_mess))
+  {
+    akt_spell_mess = replace_personal(akt_spell_mess, ({enemy}), 1);
+    say(break_string(akt_spell_mess, 78),({enemy, this_object()}));
+  }
+  sinfo = deep_copy(spells[i][SPELL_ARG]);
+  if(!mappingp(sinfo))
+    sinfo=([ SI_MAGIC_TYPE :({ MT_ANGRIFF }) ]);
+  else if(!sinfo[SI_MAGIC_TYPE])
+    sinfo[ SI_MAGIC_TYPE]=({ MT_ANGRIFF });
+  if(!sinfo[SP_PHYSICAL_ATTACK] &&
+     (enemy->SpellDefend(this_object(),sinfo) >
+      random(MAX_ABILITY+QueryProp(P_LEVEL)*50))){
+    tell_object(enemy,"Du wehrst den Spruch ab.\n");
+    say(enemy->Name(WER,1)+" wehrt den Spruch ab.\n",
+        ({ enemy, this_object()}));
+    return ;
+  }
+  int damage = random(spells[i][SPELL_DAMAGE])+1;
+  enemy->Defend(damage, spells[i][SPELL_DAMTYPE],
+                spells[i][SPELL_ARG],
+                this_object());
+
+  // Falls der Gegner (oder wir) im Defend stirbt, hier abbrechen
+  if ( !objectp(ME) || !objectp(enemy) 
+      || enemy->QueryProp(P_GHOST) ) return;
+  
+  if (spells[i][SPELL_FUNC] && stringp(spells[i][SPELL_FUNC]))
+    catch(call_other(this_object(),
+         spells[i][SPELL_FUNC],
+         enemy,
+         damage,
+         spells[i][SPELL_DAMTYPE]);publish);
+}
+
+// Heartbeats nachholen.
+private void catch_up_hbs() {
+  // gibt es HBs zum nachholen?
+  int beat_off_num = Query("npc:beat_off_num");
+  if (!beat_off_num)
+    return; // nein.
+  // wieviele HBs nachholen?
+  beat_off_num = absolute_hb_count() - beat_off_num;
+  
+  if (beat_off_num>0) {
+    // Nicht ausgefuehrtes HEILEN nachholen
+    int rlock=QueryProp(P_NO_REGENERATION);
+    int hp=QueryProp(P_HP);
+    int sp=QueryProp(P_SP);
+    int alc=QueryProp(P_ALCOHOL);
+    if (!(rlock & NO_REG_HP)) {
+      hp+=beat_off_num/HEAL_DELAY+alc/ALCOHOL_DELAY;
+      SetProp(P_HP,hp);
+    }
+    if (!(rlock & NO_REG_SP)) {
+      sp+=beat_off_num/HEAL_DELAY+alc/ALCOHOL_DELAY;
+      SetProp(P_SP,sp);
+    }
+    alc-=beat_off_num/ALCOHOL_DELAY;
+    if ( alc < 0 )
+      alc = 0;
+    SetProp(P_ALCOHOL,alc);
+    int da = QueryProp(P_DISABLE_ATTACK);
+    // Paralysen abbauen
+    if ( da > 0 ) {
+      da -= beat_off_num;
+      if ( da < 0 )
+	da = 0;
+      SetProp( P_DISABLE_ATTACK, da );
+    }
+    // Hunttimes aktualisieren, Feinde expiren
+    update_hunt_times(beat_off_num);
+    if (!heartbeat)
+      // HBs immer noch abgeschaltet, naechstes Mal HBs seit jetzt nachholen.
+      Set("npc:beat_off_num",absolute_hb_count());
+    else
+      // HB laeuft wieder, nix mehr nachholen, bis zur naechsten Abschaltung.
+      Set("npc:beat_off_num",0);
+  }
+}
+
+void init() {
+
+  // ggf. Heartbeats nachholen und wieder einschalten.
+  if (!heartbeat) {
+    set_heart_beat(1);
+    heartbeat=1;
+    catch_up_hbs();
+  }
+
+  if (AutoAttack(this_player()))
+    Kill(this_player());
+}
+
+private nosave closure mod_att_stat;
+
+int Defend(int dam, mixed dam_type, mixed spell, object enemy) {
+  if (objectp(enemy=(enemy||this_player()))
+      && query_once_interactive(enemy)
+      && !IS_LEARNER(enemy)) {
+    if (!objectp(get_type_info(mod_att_stat,2))) {
+      object ma;
+      if (!objectp(ma=find_object(STATMASTER)))
+        return ::Defend(dam,dam_type,spell,enemy);
+      // Keine Statistik wenn Master nicht geladen ist.
+      mod_att_stat=symbol_function("ModifyAttackStat",ma);
+    }
+    funcall(mod_att_stat,
+            enemy->QueryProp(P_GUILD),
+            enemy->QueryProp(P_GUILD_LEVEL),
+            dam,
+            dam_type,
+            spell);
+  }
+
+  return ::Defend(dam,dam_type,spell,enemy);
+}
diff --git a/std/npc/comm.c b/std/npc/comm.c
new file mode 100644
index 0000000..892d8fa
--- /dev/null
+++ b/std/npc/comm.c
@@ -0,0 +1,57 @@
+// MorgenGrauen MUDlib
+//
+// npc/comm.c -- Basiskommunikation fuer NPCs
+//
+// $Id: comm.c 9358 2015-10-22 18:35:04Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/comm";
+
+#include <language.h>
+#include <living/comm.h>
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+
+
+void create() {
+  add_action( "sage", "sag", 1 );
+  add_action( "echo", "echo" );
+  add_action( "emote", "emote" );
+}
+
+int echo( string str ) {
+  say( str + "\n" );
+  return 1;
+}
+
+int sage( string str ) {
+  say( break_string(str, 78, capitalize(name(WER,2))+" sagt: "));
+  return 1;
+}
+
+int emote( string str ) {
+  say( capitalize(name(WER,2))+" "+str+"\n" );
+  return 1;
+}
+
+// zum ueberschreiben - DEPRECATED! USE ReceiveMsg()!
+public void catch_msg(mixed *arr, object obj) {}
+public void catch_tell(string str) {}
+
+// by default, the msg is delivered to catch_tell() for compatibility reasons
+// and otherwise ignored.
+public varargs int ReceiveMsg(string msg, int msg_typ, string msg_action,
+                              string msg_prefix, object origin)
+{
+  // compatibility...
+  if (msg_typ & MSG_DONT_WRAP)
+    catch_tell(sprintf("%s%s", msg_prefix||"", msg));
+  else
+    catch_tell(sprintf("%s%s\n", msg_prefix||"", msg));
+  return MSG_DELIVERED;
+}
+
diff --git a/std/npc/info.c b/std/npc/info.c
new file mode 100644
index 0000000..d66ee26
--- /dev/null
+++ b/std/npc/info.c
@@ -0,0 +1,345 @@
+// MorgenGrauen MUDlib
+//
+// npc/info.c -- Behandeln von Fragen an den NPC
+//
+// $Id: info.c 9522 2016-03-01 19:20:10Z Arathorn $
+
+/* Letzte Aenderungen von Wim 8.1.99
+ *
+ * AddInfo( schluessel, antwort [, indent [, [silent [, casebased] ] ] )
+ *  Wenn ein Spieler dieses Monster nach "schluessel" fragt, so gib die
+ *  Programmierte Antwort aus.
+ *  Erweiterung von Wim: ist silent gesetzt, so erfolgt eine "persoenliche"
+ *    Antwort. d.h. umstehende Personen bekommen bei silent==1 keinen, bei
+ *    stringp(silent), den String ausgegeben, dabei kann er auch die Schluessel-
+ *    Worte @WER @WESSEN @WEM @WEN enthalten.
+ *  - Ergaenzt um @CAP_WER... fuer zwangs-capitalisierte Namen an Satzanfaengen.
+ *    ist bei fragenden NPCs und PCs mit Tarnkappe wichtig! (Silvana)
+ *  - Auch in der Antwort des fragenden wird nun ersetzt (Vanion)
+ *  Enthaelt casedbased einen Funktionsnamen oder verweist auf eine closure, so
+ *    wird die Beantwortung der Frage von dem return-Wert abhaengig gemacht.
+ *    Bei 0 wird die Frage normal beantwortet, bei 1 erfolgt die Ausgabe des
+ *    unter DEFAULT_NOINFO gespeicherten Textes.
+ *    Wird ein String zurueckgegeben, so wird er unter Beachtung von ident an
+ *    Stelle der urspruenglichen Information ausgegeben.
+ *
+ * RemoveInfo( schluessel )
+ *  Das Monster antwortet nicht mehr auf diesen Schluessel.
+ *
+ * SetProp( P_DEFAULT_INFO, antwort [, indent ] )
+ *  Setze die Antwort, die das Monster auf unverstaendliche Fragen geben
+ *  soll. (Diese Funktion ist obsolet! Benutze stattdessen
+ *  AddInfo( "\ndefault info", antwort [, indent ] );
+ *
+ * GetInfo( [schluessel] )
+ *  Wenn Schluessel gesetzt ist, so wird die dazugehoerige Info,
+ *  ansonsten werden alle Infos zurueckgegeben.
+ *
+ * Die Antworten sollten wie emote - kommandos aussehen.
+ * Der optionale Indent wird zum Umbrechen von langen Infos benutzt.
+ * (Typischerweise sollte indent="sagt: " sein.)
+ *
+ * In den Infos darf mit process_string gearbeitet werden. Das Ergebnis von
+ * process_string wird dann mit umgebrochen!
+ *
+ *---------------------------------------------------------------------------
+ */
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+#include <thing/properties.h>
+#include <npc.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <config.h>
+#include <exploration.h>
+
+// TODO: langfristig waer hier private schoen.
+nosave mapping infos;
+
+protected void create()
+{
+    // Initialisierung nur wenn noetig, damit beim virtuellen Erben von
+    // /std/npc in npc1 und npc2 dann in npc3 beim npc1::create();
+    // npc2:create(); im zweiten create() die Infos nicht
+    // ueberschrieben/geloescht werden.
+    if (!mappingp(infos)) {
+        infos = ([
+            DEFAULT_INFO:"schaut Dich fragend an.\n";0;
+            "schaut @WEN fragend an.\n";0,
+            DEFAULT_NOINFO:"moechte Dir nicht antworten.\n";0;
+            "verweigert @WEM die Antwort.\n";1
+            ]);
+    }
+}
+
+
+void init() {
+  add_action( "frage", "frag", 1 );
+}
+
+
+static void smart_npc_log(string str)
+{
+  string creat, creat_det;
+
+  if (!stringp(creat=QueryProp(P_LOG_INFO))) {
+    creat = MASTER->creator_file(this_object());
+    if (creat == ROOTID)
+      creat = "ROOT";
+    else if( creat==BACKBONEID )
+      creat="STD";
+    creat_det="report/"+explode(creat, ".")[<1]+"_INFO.rep";
+    creat="report/"+explode(creat, ".")[<1]+".rep";
+  }
+  log_file(creat,
+           sprintf("INFO von %s [%s] (%s):\n%s\n",
+                   getuid(this_interactive()),
+                   explode(object_name(this_object()),"#")[0],
+                   strftime("%d. %b %Y"),
+                   str));
+  if (stringp(creat_det) && sizeof(creat_det))
+    log_file(creat_det,
+             sprintf("INFO von %s [%s] (%s):\n%s\n",
+                     getuid(this_interactive()),
+                     explode(object_name(this_object()),"#")[0],
+                     strftime("%d. %b %Y"),
+                     str));
+}
+
+public int frage(string str) {
+  string myname, text;
+
+  str=(extern_call()?this_player()->_unparsed_args():str);
+  if( !str || sscanf( str, "%s nach %s", myname, text ) != 2 ) {
+    _notify_fail( "WEN willst Du nach WAS fragen?\n" );
+    return 0;
+  }
+
+  if( !id( lower_case(myname) )
+    || QueryProp(P_INVIS) ) {
+    _notify_fail( "So jemanden findest Du hier nicht.\n" );
+    return 0;
+  }
+  say( capitalize(this_player()->name(WER))+" fragt " +
+    name(WEN,2)+" nach "+capitalize(text)+".\n",
+      this_player() );
+
+  text = lower_case(text);
+  GiveEP(EP_INFO, text);
+
+  return do_frage( text );
+}
+
+static string infoDefaultReplace(string pstring, object pl)
+{
+  pstring=" "+pstring;
+  if (strstr(pstring,"@WER",0) >-1 )
+    pstring= regreplace(pstring,"@WER",pl->name(WER,1),1);
+  if (strstr(pstring,"@WESSEN",0) >-1 )
+    pstring= regreplace(pstring,"@WESSEN",pl->name(WESSEN,1),1);
+  if (strstr(pstring,"@WEM",0) >-1 )
+    pstring= regreplace(pstring,"@WEM",pl->name(WEM,1),1);
+  if (strstr(pstring,"@WEN",0) >-1 )
+    pstring= regreplace(pstring,"@WEN",pl->name(WEN,1),1);
+  if (strstr(pstring,"@CAP_WER",0) >-1 )
+    pstring= regreplace(pstring,"@CAP_WER",pl->Name(WER,1),1);
+  if (strstr(pstring,"@CAP_WESSEN",0) >-1 )
+    pstring= regreplace(pstring,"@CAP_WESSEN",pl->Name(WESSEN,1),1);
+  if (strstr(pstring,"@CAP_WEM",0) >-1 )
+    pstring= regreplace(pstring,"@CAP_WEM",pl->Name(WEM,1),1);
+  if (strstr(pstring,"@CAP_WEN",0) >-1 )
+    pstring= regreplace(pstring,"@CAP_WEN",pl->Name(WEN,1),1);
+
+  return pstring[1..];
+}
+
+static mixed *GetInfoArr(string str)
+{  
+   return ({ infos[str, 0], infos[str, 1], infos[str,2], infos[str, 3] });
+}
+
+public int do_frage(string text)
+{
+  string indent,answer;
+  mixed silent, preinfo, noanswer;
+  mixed *info;
+
+  if (stringp(preinfo = QueryProp(P_PRE_INFO)))
+  {
+     // Die message action wird auf "frage" fixiert, damit dies immer das
+     // gleiche ist, egal, mit welchem Verb man in diese Funktion kommt.
+     this_player()->ReceiveMsg(preinfo, MT_LISTEN, "frage",
+                               Name(WER,2)+" ",this_object());
+     send_room(environment(this_object()),
+               "ist nicht gewillt, "+this_player()->Name(WEM,2)
+               +" zu antworten.",
+               MT_LISTEN, "frage",
+               Name(WER,2)+" ",
+               ({this_player()}) );
+     return 1;
+  }
+  else
+  {
+    if (intp(preinfo) && preinfo > 0)
+      return 1;
+  }
+
+  info=GetInfoArr(text);
+  if (!info[0])
+  {
+    if( this_interactive() && QueryProp(P_LOG_INFO) )
+      smart_npc_log(text);
+    text = DEFAULT_INFO;
+    info=GetInfoArr(text);
+  }
+
+  if (closurep(info[0]) ) {
+    answer=funcall(info[0]);
+    if( !answer || answer=="") return 1;
+  } else {
+    answer=process_string(info[0]);
+  }
+
+  if (closurep(info[3]) )
+   {
+    noanswer=funcall(info[3]);
+    if ( intp(noanswer) && noanswer > 0)
+     {
+       text = DEFAULT_NOINFO;
+       info = GetInfoArr(text);
+       if (closurep(info[0]) ) {
+         answer=funcall(info[0]);
+         if( !answer || answer=="") return 1;
+       } else {
+         answer=process_string(info[0]);
+       }
+     }
+    else if ( stringp(noanswer) )
+      answer = noanswer;
+   }
+
+  silent=info[2];
+
+  // Replacements gehen auch in der Antwort des NPC. Das gibt den Antworten 
+  // eine persoenliche Note, und so teuer is das auch nicht :)
+  answer = infoDefaultReplace(answer, this_player());
+
+  if( indent=info[1] )
+  {
+    if (stringp(silent) || (intp(silent) && silent > 0) )
+    {  // Persoenliche Antwort mit indent
+       this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
+                                 "frage",
+                                 Name(WER,2)+" "+indent,
+                                 this_object());
+       if (stringp(silent))
+       {
+          silent=infoDefaultReplace(silent, this_player());
+          send_room(environment(), silent, MT_LISTEN, "frage",
+                    Name(WER,2)+" ", ({this_player()}));
+       }
+    }
+    else // "normale Antwort" mit Indent
+    {
+      send_room(environment(), answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
+                "frage",
+                Name(WER,2)+" "+indent, ({this_object()}));
+    }
+  }
+  else
+  {
+    if (stringp(silent) || (intp(silent) && silent > 0) )
+    {  // Persoenliche Antwort ohne indent
+       this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_DONT_WRAP,
+                                 "frage",
+                                 Name(WER,2)+" ",
+                                 this_object());
+       if (stringp(silent))
+       {
+          silent=infoDefaultReplace(silent, this_player());
+          send_room(environment(), silent, MT_LISTEN, "frage",
+                    Name(WER,2)+" ", ({this_player()}) );
+       }
+    }
+    else // "normale Antwort" ohne Indent
+      send_room(environment(), answer, MT_LISTEN|MSG_DONT_WRAP,
+                "frage", Name(WER,2)+" ");
+  }
+  return 1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Setzen von Infos
+ *---------------------------------------------------------------------------
+ */
+
+public varargs void AddInfo(mixed key, mixed info, string indent, 
+                            mixed silent, mixed casebased ) {
+
+  if (stringp(casebased))
+    casebased=symbol_function(casebased,this_object());
+
+  if( pointerp( key ) ) {
+    int i;
+    for ( i=sizeof( key )-1; i>=0; i-- )
+      infos += ([ key[i]: info; indent; silent; casebased ]);
+  }
+  else
+    infos += ([ key: info; indent; silent; casebased ]);
+}
+
+public varargs void AddSpecialInfo(mixed keys, string functionname, 
+                      string indent, mixed silent, mixed casebased )
+{
+  int i;
+  closure cl;
+
+  if(!(cl=symbol_function(functionname,this_object()))) return;
+  return AddInfo(keys,cl,indent,silent,casebased);
+}
+
+
+public void RemoveInfo( string key )
+{
+  m_delete(infos,key);
+}
+
+static varargs void _set_default_info( mixed info )
+{
+  if (pointerp(info))
+    apply(#'AddInfo/*'*/,DEFAULT_INFO,info);
+  else
+    AddInfo(DEFAULT_INFO,info);
+}
+
+public varargs mixed GetInfo(string str)
+{
+  if (!str) return deep_copy(infos);
+  return infos[str];
+}
+
+
+static mapping _query_npc_info()
+{
+    return deep_copy(infos);
+}
+
+
+static mapping _set_npc_info( mapping map_ldfied )
+{
+    if ( !mappingp(map_ldfied) )
+        return 0;
+
+    return infos = map_ldfied;
+}
+
diff --git a/std/npc/items.c b/std/npc/items.c
new file mode 100644
index 0000000..f0260cf
--- /dev/null
+++ b/std/npc/items.c
@@ -0,0 +1,240 @@
+// MorgenGrauen MUDlib
+//
+// /std/npc/items.c -- Item-Verwaltung fuer NPCs
+//
+// $Id: items.c 8146 2012-10-30 18:18:14Z Zesstra $
+//
+// (c) by Padreic (Padreic@mg.mud.de)
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <living/combat.h>
+#undef NEED_PROTOTYPES
+#include <npc.h>
+#include <properties.h>
+#include <combat.h>
+#include <config.h>
+#include <moving.h>
+#include <sys_debug.h>
+
+protected void create()
+{
+  // dieses flag ist nur bis zum ersten move gesetzt. Nimmt danach also auch
+   Set(NPC_NEEDS_ITEM_INIT, 1);  // keinen Speicher mehr in Anspruch.
+   // Set(P_ITEMS, ({})); // keine initialisierung spart Speicher  
+}
+
+protected void create_super() {
+      set_next_reset(-1);
+}
+
+protected object* maybe_own_stuff() {
+  object* obs = all_inventory(environment());
+  object haufen = present("\nhaufen "+name(WEM),environment());
+  if( objectp(haufen) ) obs += all_inventory(haufen);
+  object leiche = present("\nleiche "+name(WEM),environment());
+  if( objectp(leiche) ) obs += all_inventory(leiche);
+  return obs;
+}
+
+// clont alle Objekte die geclont werden muessen...
+protected void _clone_items()
+{
+  int i, j, mode, refresh;
+  object ob, *inv1;
+  mixed  props, *items;
+  string file, *inv2;
+
+  if (!pointerp(items=QueryProp(P_ITEMS))) return;
+  if (environment()) { // Liste mit filenamen (inv2) erstellen.
+    inv1 = maybe_own_stuff();
+    inv2 = map(inv1, #'load_name);
+  }
+  else inv2=({}); // inv1 wird gar nicht mehr benoetigt
+
+  for (i=sizeof(items)-1; i>=0; i--) {
+    ob = items[i][RITEM_OBJECT];
+    file = items[i][RITEM_FILE];
+    mode = items[i][RITEM_REFRESH];
+    refresh = mode & 0x0000ffff;
+    props = items[i][RITEM_PROPS];
+    if (props && intp(props)) { // unique Gegenstand?
+      ob=find_object(file);
+      if (ob) {
+        // kann ob aufgenommen werden? Wenn das Objekt in inv1 drin ist, ist es
+        // im Env oder in einer Leiche oder einem Haufen.
+        if (member(inv1,ob) > -1) {
+          if (environment()) {
+            if (environment(ob) == environment())
+              tell_room(environment(),
+                  capitalize(name(WER))+" hebt "+ob->name(WEN)+" auf.\n");
+            else
+              tell_room(environment(),break_string(
+                  capitalize(name(WER))+" nimmt "+ob->name(WEN)+ " aus "
+                  +environment(ob)->name(WEM) + ".",78));
+          }
+          ob->remove();  // zerstoeren und neuladen.
+          if (ob) destruct(ob);
+          if (catch(ob=load_object(file);publish))             
+              raise_error(sprintf(
+                "_clone_items(): %O does not exist or is not loadable\n", file)); 
+          ob->move(this_object(), M_NOCHECK);
+          items[i][RITEM_OBJECT]=ob;
+        }
+        else switch( refresh ) {
+          case REFRESH_DESTRUCT:
+            if (environment(ob)) {
+              ob=0; // nicht zuecken, tragen, initialisieren...
+              break;
+            }
+          case REFRESH_ALWAYS:
+          case REFRESH_REMOVE:                
+            ob->remove();
+            if (ob) destruct(ob);
+            if (catch(ob=load_object(file);publish)) 
+                raise_error(sprintf("_clone_items(): "
+                      "%O does not exist or is not loadable\n", file));
+            ob->move(this_object(), M_NOCHECK);
+            items[i][RITEM_OBJECT]=ob;
+            break;
+          case REFRESH_NONE:
+          default: ob=0; // nicht zuecken, tragen, initialisieren...
+        }
+      }
+      else {
+        if (catch(ob=load_object(file);publish)) raise_error(sprintf(
+          "_clone_items(): %O does not exist or is not loadable\n", file));
+        if (objectp(ob)) ob->move(this_object(), M_NOCHECK);
+        items[i][RITEM_OBJECT]=ob;
+      }
+    } // if (props ...) // also nicht unique
+    else {
+      switch( refresh ) {
+        case REFRESH_DESTRUCT: // erfuellt auch REFRESH_REMOVE Bedingung...
+          if (ob) {
+            ob=0; // nicht zuecken, tragen, initialisieren...
+            break;
+          }
+        case REFRESH_REMOVE:
+          if (ob) {
+            if(present(ob, this_object())) {
+              ob=0; // nicht zuecken, tragen, initialisieren...
+              break;
+            }
+            else {
+              if ((member(inv2, file))==-1) {  
+                ob=0;
+              }
+            }
+          }
+        case REFRESH_NONE: // wird entfernt nach dem ersten clonen!!!
+        case REFRESH_ALWAYS:
+          // schauen ob der Gegenstand im environment liegt.
+          if ((j=member(inv2, file))!=-1) {
+            ob=inv1[j]; // inv1 kann leer sein, aber dann ist inv2 auch leer
+            inv1[j]=0; // wichtig falls mehrere gleiche AddItems
+            inv2[j]=0; // = 0 setzen um Array kopieren zu vermeiden
+            if (environment()) {
+              if (environment() == environment(ob))
+                tell_room(environment(),
+                    capitalize(name(WER))+" hebt "+ob->name(WEN)+" auf.\n");
+              else
+                tell_room(environment(),break_string(
+                    capitalize(name(WER))+" nimmt "+ob->name(WEN)+" aus "
+                    +environment(ob)->name(WEM) + ".",78));
+            }
+          }
+          if (ob) {
+            ob->remove();
+            if (ob) destruct(ob);
+          }
+          if (catch(ob=clone_object(file);publish)) raise_error(sprintf(
+            "_clone_items(): %O does not exist or is not loadable\n", file));
+          ob->move(this_object(), M_NOCHECK);
+          switch( refresh ) {
+          case REFRESH_NONE:
+            items[i] = 0; // Speicher freimachen
+            break;
+          case REFRESH_ALWAYS:
+            items[i][RITEM_OBJECT] = 0; // Objekt "vergessen"
+            break;
+          default:
+            items[i][RITEM_OBJECT] = ob;
+          }
+          break;
+        default: ob=0; // nicht zuecken, tragen, initialisieren...
+      }
+    } // if (props ...) else 
+    if (ob) {
+      if (mappingp(props)) walk_mapping(props, symbol_function("SetProp", ob));
+      // Eigentlich will man hier sowas wie command("zuecke schwert")
+      // machen, aber das handling der id's kann nicht sicherstellen,
+      // dass der NPC die richtige Waffe erwischt, deshalb machen wir
+      // das tragen hier von Hand.
+      if (mode & CLONE_WEAR) {
+        if( mode & CLONE_NO_CHECK ) {
+          object *armours;
+          ob->DoUnwear(); // evtl. dem Vorgaenger abnehmen.
+          UseHands(ob, ob->QueryProp(P_NR_HANDS));
+          armours=QueryProp(P_ARMOURS)+({ ob });
+          SetProp(P_ARMOURS, armours);
+          SetProp(P_TOTAL_AC, QueryProp(P_TOTAL_AC)+ob->QueryProp(P_AC));
+          ob->SetProp(P_WORN, this_object());
+        } else {
+          command( "trage \n"+object_name(ob) );
+        }
+      }
+      if (mode & CLONE_WIELD) {
+        if( mode & CLONE_NO_CHECK ) {
+          ob->DoUnwield(); // evtl. dem Vorgaenger abnehmen. 
+          UseHands(ob, ob->QueryProp(P_NR_HANDS));
+          SetProp(P_WEAPON, ob);
+          SetProp(P_TOTAL_WC, ob->QueryProp(P_WC));
+          ob->SetProp(P_WIELDED, this_object());
+        } else {
+          command( "zuecke \n"+object_name(ob) );
+        }
+      }
+    } // if (ob) 
+  } // for i
+  items-=({ 0 }); // REFRESH_NONEs von nicht unique Objekten 
+  if (!sizeof(items)) items=0; // Speicher sparen...
+  SetProp(P_ITEMS, items);
+  Set(NPC_NEEDS_ITEM_INIT, 0);
+}
+
+public varargs object AddItem(mixed filename, int refresh, mixed props)
+{
+  // Aus Array ein Element auswaehlen
+  if (pointerp(filename)) 
+    filename = filename[random(sizeof(filename))];
+ 
+  // Kein String? -> Fehler
+  if (!stringp(filename)){
+    raise_error("AddItem: filename or array of filenames expected.\n"); 
+  } else {
+    // Pfad normieren und eventuell vorhandenes ".c" entfernen
+    filename = MASTER->_get_path(
+                  filename[<2..]==".c"?filename=filename[0..<3]
+                                        : filename,"?"); 
+    // Property setzen
+    SetProp(P_ITEMS, (QueryProp(P_ITEMS)||({}))+
+                     ({ ({ 0, filename, refresh, props }) }));
+
+  if (environment()) _clone_items();
+  }
+  return 0;
+}
+
+void reset()
+// fuer REFRESH_ Objekte...
+{
+  _clone_items();
+}
+
diff --git a/std/npc/moving.c b/std/npc/moving.c
new file mode 100644
index 0000000..f06e0da
--- /dev/null
+++ b/std/npc/moving.c
@@ -0,0 +1,39 @@
+// MorgenGrauen MUDlib
+//
+// living/moving.c -- moving of living objects
+//
+// $Id$
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/moving";
+
+#include <hook.h>
+#define NEED_PROTOTYPES
+#include <moving.h>
+
+//<int|<string|closure>* >* GuardExit(object room, int hookid,
+//                                    <string|closure>* hdata)
+//{
+//}
+
+protected int _reg_exit_hook(object dest, object oldenv)
+{
+  closure cl = symbol_function("GuardExit", this_object());
+  if (cl)
+  {
+    if (oldenv)
+      oldenv->HUnregisterFromHook(H_HOOK_EXIT_USE, cl);
+    return dest->HRegisterModifier(H_HOOK_EXIT_USE, cl);
+  }
+  return 0;
+}
+// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+  _reg_exit_hook(dest, oldenv);
+  return ::NotifyMove(dest, oldenv, method);
+}
diff --git a/std/npc/put_and_get.c b/std/npc/put_and_get.c
new file mode 100644
index 0000000..8b72cf4
--- /dev/null
+++ b/std/npc/put_and_get.c
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// npc/put_and_get.c.c -- Geben und nehmen fuer NPCs
+//
+// $Id: put_and_get.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "std/living/put_and_get";
+#include <moving.h>
+#include <properties.h>
+
+void give_notify( object obj )
+{
+   mixed* msg;
+   if (pointerp(msg=QueryProp(P_REJECT))) {
+      switch(msg[0]) {
+         case REJECT_GIVE:
+            say(msg[1]);
+            give_obj( obj, this_player() );
+            break;
+         case REJECT_LIGHT_MODIFIER:
+            if (obj->QueryProp(P_LIGHT_MODIFIER) ||
+                obj->QueryProp(P_LIGHT)) break;
+         case REJECT_DROP:
+            say(msg[1]);
+            drop_obj( obj );
+            break;
+         case REJECT_KEEP:
+         default: say(msg[1]); /* keep it */
+    }
+  }
+}
diff --git a/std/npc/sequencer.c b/std/npc/sequencer.c
new file mode 100644
index 0000000..50c85e3
--- /dev/null
+++ b/std/npc/sequencer.c
@@ -0,0 +1,125 @@
+// MorgenGrauen MUDlib
+//
+// npc/sequencer.c -- Scripte und Trigger fuer NPCs :)
+//
+// $Id: sequencer.c 7526 2010-04-02 00:01:26Z Arathorn $
+
+// seq2.c
+// Version 2.0 des Sequencers von Don Rumata 28.06.93
+// 
+// Sinn:
+//   Bereitstellung von Funktionen, ddamit ein npc mehrere Befehle
+//   bei Eintritt eines bestimmten Ereignisses nacheinander
+//   automatisch durchfuehrt.
+//
+// Ereignisse:
+//   TellEvent: Es wird etwas in dem Raum, in dem der npc sich
+//              befindet, gesagt.
+//   GiveEvent: Es wird dem npc etwas gegeben.
+//
+// Programm:
+//   Ein Programm ist eine Liste von Befehlen.
+//   Jeder Befehl ist eine Liste, bestehend aus einem Kommando
+//   und einer Zahl.
+//   Das Kommendo wird aehnlich der Befehle, die ein Spieler ein-
+//   gibt ausgefuehrt.
+//   Vorsicht: NPCs koennen nur einen Teil der Befehle, die ein
+//   Spieler kann, dafuer aber immer 'echo' und 'emote'.
+//   Die Zahl gibt die Anzahl der Sekunden an, in der der naechste
+//   Befehl ausgefuehrt wird.
+//
+// Funktionen:
+//   RegisterTell( funktion, programm )
+//     Wenn dem npc etwas gesagt wird, so wird die gesagte Meldung
+//     an die Funktion uebergeben. Gibt die Funktionen nicht 0
+//     zurueck, wird das Programm gestartet.
+//   RegisterGive( funktion, programm )
+//     Wird dem npc etwas gegeben, so wird das Objekt an die
+//     Funktion uebergeben. Gibt die Funktion nicht 0 zurueck, so
+//     wird das Programm gestartet.
+//   Load( programm )
+//     Starte das angegebene Programm.
+//
+//   give_notify() gibt eine 1 zurueck, wenn das Objekt akzeptiert
+//     wurde. (Es muss - falls gewuenscht - dann von Hand zuruech-
+//     gegeben werden. (give_obj(ob,this_player())) in der Funk.)
+//   mittels add_action() kann man im create() ds NPCs eigene
+//     Verben fuer den NPC einfuehren.
+//
+// Es kann immer nur eine Funktion (egal ob Tell oder Give) angemeldet
+// sein. Es kann immer nur ein Programm gleichzeitig laufen.
+//
+// Ideen und Bugreports an Rumata
+
+string givefun, tellfun;
+mixed *program;
+int isRunning;
+
+// zur sicherheit nicht von aussen aufrufbar !!!!
+static string RegisterGive( string fun, mixed prog )
+{
+  if( isRunning ) return 0;
+  program = prog;
+  tellfun = 0;
+  return givefun = fun;
+}
+
+// zur sicherheit nicht von aussen aufrufbar !!!!
+static string RegisterTell( string fun, mixed prog )
+{
+  if( isRunning ) return 0;
+  program = prog;
+  givefun = 0;
+  return tellfun = fun;
+}
+
+// zur sicherheit nicht von aussen aufrufbar !!!!
+static void Start()
+{
+  if( isRunning || !objectp(this_object())) return;
+  isRunning = 1;
+  call_out( "nextStep", 0, 0 );
+}
+
+static void Load( mixed prog )
+{
+  if( isRunning ) return;
+  program = prog;
+  Start();
+}
+
+public void catch_tell( string str )
+{
+  if( isRunning || previous_object()==this_object()) return;
+  if( stringp(tellfun) && call_other( this_object(), tellfun, str ) )
+    Start();
+}
+
+int give_notify( object ob )
+{
+  if( isRunning ) return 0;
+  if( stringp(givefun) && call_other( this_object(), givefun, ob ) )
+  {
+    Start();
+    return 1;
+  }
+  return 0;
+}
+
+static void nextStep( int line )
+{
+  if( !isRunning ) return;
+  if( line >= sizeof( program ) )
+  {
+    isRunning = 0;
+    return;
+  }
+  command( process_string( program[line][0] ) );
+  if( !isRunning || !objectp(this_object()) ) return;
+  call_out( "nextStep", program[line][1], line+1 );
+}
+
+mixed QueryProg()
+{
+  return program[0..];
+} 
diff --git a/std/pile.c b/std/pile.c
new file mode 100644
index 0000000..dcfe5ef
--- /dev/null
+++ b/std/pile.c
@@ -0,0 +1,124 @@
+// Ein transparenter Container, in dem die Gegenstaende landen,
+// die ein Spieler bei seinem Tod verliert.
+// Sinn dieses Objektes ist es, das invenotry eines Raumes klein
+// zu halten und die kosten im init() eines Spielers zu senken.
+
+#pragma strict_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+
+inherit "std/container";
+
+// Spielerhaufen sollen sich etwas anders als NPC-haufen verhalten.
+private nosave status spielerhaufen;
+
+#include <properties.h>
+#include <moving.h>
+
+void create() {
+	string von;
+	if (object_name(this_object()) == __FILE__[0..<3]) {
+	  set_next_reset(-1);
+	  return;
+	}   
+	if( !clonep(this_object()) ) return;
+	::create();
+	von = (string)previous_object()->QueryProp(P_PILE_NAME);
+	SetProp( P_PILE_NAME, "\nhaufen "+von );
+	spielerhaufen = (int)previous_object()->IsPlayerCorpse();
+
+	AddId( ({"haufen","krempel","kluengel","gegenstaende"}) );
+	AddId( QueryProp(P_PILE_NAME) );
+	SetProp( P_NAME, "Haufen Krempel" );
+	SetProp( P_GENDER, MALE );
+	SetProp( P_INFO, "Diese Gegenstaende gehoerten einmal " + von + ".\n" );
+	SetProp( P_LONG, break_string("Du betrachtest einen Haufen, der aus einer von Leichenresten zusammengebackenen Menge diverser Gegenstaende besteht. Zum Glueck ist von der Leiche nicht mehr genug uebrig, um Ekelgefuehle zu wecken. Ganz im Gegenteil: Dir juckt es ein wenig in den Fingern, Dich an diesem Haufen zu bedienen. Aber man kann sich nie ganz sicher sein, ob nicht doch ein Fluch auf den Dingen lastet. Vielleicht hat der verschiedene Besitzer sie auch jemandem vererbt? Andererseits, wenn gerade niemand guckt ...\n" ) );
+	AddDetail( ({"fetzen","leiche","leichenteile"}), "Mit blossen Augen kann man nicht mehr viel erkennen, aber einige Fetzen deuten darauf hin, dass die Sachen von "+von+" stammen.\n" );
+	AddDetail( ({"ekel","ekelgefuehle"}), "Es ist nicht mehr genug da, um welche zu wecken.\n" );
+	AddDetail( "menge", "Viele kleine Fetzen, die an den Gegenstaenden kleben.\n" );
+	AddDetail( "fluch", "Dem Anschein nach lastet auf den Sachen kein Fluch, aber wer weiss ...?\n" );
+	call_out( "merge_pile", 1 );
+}
+
+mixed _query_short() {
+	int sz = sizeof(all_inventory());
+	if( sz==0 ) {
+		call_out("remove",0,1); // fallback, wenn inhalt auf unerwartete weise verschwindet
+		return 0;
+	} else if( sz<10 ) {
+		return "Ein kleiner Haufen Krempel";
+	} else if( sz<30 ) {
+		return "Ein Haufen Krempel";
+	} else {
+		return "Ein grosser Haufen Krempel";
+	}
+  return 0;
+}
+
+// Prueft, ob wir in einen Spieler gemovt werden sollen.
+// Liefert passendes Moveresult, ansonsten 0.
+protected int PreventMove(object dest, object oldenv, int method) {
+    if( !objectp(dest) ) return 0;
+    if( method & M_NOCHECK ) return 0;
+    if( query_once_interactive(dest) ) {
+      return ME_NOT_ALLOWED;
+    // default meldung in put_and_get:pick
+    // IDEE: im spieler per hook eine andere meldung unterbringen
+    }
+    return ::PreventMove(dest, oldenv, method);
+}
+
+// Sorgt dafuer, dass im NPC sich der Haufen selbst zerstoert.
+protected void NotifyMove(object dest, object oldenv, int method) {
+    if( query_once_interactive(dest)) {
+	log_file( "PILE.log", dtime(time()) + ": pile in " 
+	    + dest->query_real_name() + " gelandet.\n" );
+    }
+    if( living(dest) ) {
+	filter_objects( all_inventory(), "move", dest, M_SILENT | M_NOCHECK );
+	// NPCs erwarten nicht, dass dinge im move verschwinden, deshalb
+	// abwarten
+	call_out( "remove", 0, 1 );
+    }
+    return ::NotifyMove(dest, oldenv, method);
+}
+
+// wenn nur noch ein Objekt im Haufen ist, soll dieser zerstoert werden.
+public void NotifyLeave( object ob, object dest ) {
+	if( sizeof(all_inventory())>1 ) return;
+	call_out( "remove", 0, 1 ); // verzoegern, um prepare_destruct gnaedig zu stimmen
+}
+
+// dito fuers loeschen
+public void NotifyRemove( object ob ) {
+    NotifyLeave( ob, 0);
+}
+
+// Haufen, die von zweimal dem selben NPC kommen, werden
+// zusammengelegt
+void merge_pile() {
+	object* other = filter_objects(
+		all_inventory(environment()) - ({ this_object() }),
+		"id",
+		QueryProp( P_PILE_NAME )
+	);
+	if( !sizeof(other) ) return;
+	filter_objects( all_inventory(), "move", other[0], M_SILENT | M_NOCHECK );
+	remove();
+}
+
+void reset() {
+  // wenn es ein NPC-Haufen ist, werden Gegenstaende, die oefter als 3mal
+  // vorkommen, zerstoert. Schraenkt die Objektflut an Stellen ein, wo NPC
+  // ihren Kram nicht mit AddItem clonen.
+  if (!spielerhaufen)
+    remove_multiple(3);
+  ::reset();
+  // wenn nur noch unsichtbare items im Haufen: aufloesen
+  if (!sizeof(filter(all_inventory(), function int (object o)
+          { return !o->short();} )))
+    remove(1);
+
+}
+
diff --git a/std/player/base.c b/std/player/base.c
new file mode 100644
index 0000000..9c9a6e1
--- /dev/null
+++ b/std/player/base.c
@@ -0,0 +1,4355 @@
+// MorgenGrauen MUDlib
+//
+// player/base.c -- the basic player object
+//
+// $Id: base.c 9467 2016-02-19 19:48:24Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <sys_debug.h>
+#include <regexp.h>
+#include <input_to.h>
+#include <logging.h>
+#include <werliste.h>
+#include <time.h>
+#include <errord.h>
+#include <wizlevels.h>
+#include <money.h>
+
+inherit "/std/hook_provider";
+inherit "/std/player/restrictions";
+inherit "/std/living/attributes";
+inherit "/std/living/put_and_get";
+inherit "/std/living/clothing";
+inherit "/std/thing/properties";
+inherit "/std/player/util";
+inherit "/std/thing/language";
+inherit "/std/player/travel";
+inherit "/std/player/combat";
+inherit "/std/player/description";
+inherit "/std/player/moving";
+inherit "/std/player/life";
+inherit "/std/player/comm";
+inherit "/std/player/viewcmd";
+inherit "/std/player/moneyhandler";
+inherit "/std/player/command";
+inherit "/std/living/skill_attributes";
+inherit "/std/living/light";
+inherit "/std/player/skills";
+inherit "/std/player/quests";
+inherit "/std/player/potion";
+inherit "/std/player/soul";
+inherit "/std/more";
+inherit "/std/user_filter";
+inherit "/secure/telnetneg";
+inherit "/std/player/guide";
+inherit "/std/player/reputation";
+inherit "/std/player/protocols/gmcp";
+inherit "/std/living/helpers";
+
+#define NEED_PROTOTYPES
+#include <player/skills.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+#include <player.h>
+#include <properties.h>
+#include <udp.h>
+#include <config.h>
+#include <ansi.h>
+#include <wizlevels.h>
+#include <living.h>
+#include <attributes.h>
+#include <language.h>
+#include <moving.h>
+#include <defines.h>
+#include <terminal.h>
+#include <new_skills.h>
+#include <pager.h>
+#include <combat.h>
+#include "/secure/questmaster.h"
+#include "/secure/lepmaster.h"
+#include <events.h>
+
+#undef NAME /* DEFINED BY UDP.H; BAD NAME CLASH :( */
+#define NAME(who) capitalize(getuid(who))
+
+mapping autoload;           /* autoload-mapping */
+int hc_play;
+
+private nosave mapping autoload_rest;
+private nosave string  *autoload_error;
+private nosave string realip; 
+
+private nosave string passw;        /* temporarily for password change */
+private nosave string passwold;     /* temporarily for password change */
+
+// HB-Zaehler. Wenn 0 erreicht wird, wird  ein Telnet TM Paket als Keep-Alive
+// an den Client gesendet und der Counter wieder auf hochgesetzt. 
+// Wenn == 0, ist das Keep-Alive abgeschaltet.
+private int telnet_tm_counter;
+
+nosave string default_home; /* Where to move us if we dont have a home set */
+
+nosave int ndead_lasttime;
+nosave mixed ndead_location;
+nosave string ndead_l_filename;
+nosave int ndead_currently;
+nosave int ndead_next_check;
+nosave object *hb_obs;
+
+private nosave string default_pray_room;
+
+nosave mixed env_ndead_info;
+
+static int _set_invis(int a);
+static string _set_tty(string str);
+static mixed _set_fraternitasdonoarchmagorum(mixed arg);
+
+
+static string al_to_title(int a);
+
+static void ndead_revive();
+
+static int wegmeldung(string player);
+
+int quit();
+void save_me(mixed value_items);
+varargs int remove(mixed arg);
+mixed RaceDefault(string arg);
+
+/** Setzt Defaultwerte vor dem Laden des Savefiles.
+    Nur die Werte bleiben spaeter uebrig, die NICHT aus dem Savefile geladen
+    werden und diese hier ersetzen.
+    Ausserdem kann man hier nicht mit den Werten aus den Savefiles arbeiten.
+    Hierzu muss man updates_after_restore() verwenden.
+*/
+protected void create()
+{
+  if(QueryProp(P_LEVEL)) 
+  {
+    return; // darf nur EINMAL gemacht werden
+  }
+  call_out("checkConsistency", 0);
+
+  ndead_next_check=NETDEAD_CHECK_TIME;
+  ndead_lasttime=0;
+  ndead_location=0;
+  ndead_l_filename=0;
+  ndead_currently=0;
+  ndead_next_check=0;
+  hc_play=0;
+  
+  command::create();
+  properties::create();
+  description::create();
+  light::create();
+  attributes::create();
+  clothing::create();
+  combat::create();
+  life::create();
+  comm::create();
+  viewcmd::create();
+  quests::create();
+  restrictions::create();
+  moving::create();
+  travel::create();
+  skills::create();
+   
+  SetProp(P_LEVEL, -1);
+  Set(P_LEVEL, SAVE|SECURED, F_MODE_AS);
+  Set(P_GHOST, SAVE, F_MODE_AS);
+  SetProp(P_SCREENSIZE, -1);
+  Set(P_SCREENSIZE, SAVE,F_MODE_AS);
+  Set(P_MORE_FLAGS, SAVE, F_MODE_AS);
+  SetProp(P_WEIGHT_PERCENT,100);
+  SetProp(P_LONG, 0);
+  SetProp(P_TITLE, "der hoffnungsvolle Anfaenger");
+  SetProp(P_ALIGN, 0);
+  SetProp(P_GENDER, NEUTER);
+  Set(P_GENDER, SAVE, F_MODE_AS);
+  SetProp(P_TTY, "vt100");
+  Set(P_TTY, SAVE, F_MODE_AS);
+  SetProp(P_WEIGHT, 75000);
+  SetProp(P_MAX_HP,50);
+  SetProp(P_MAX_SP,50);
+  SetProp(P_MAX_FOOD,100);
+  SetProp(P_MAX_DRINK,100);
+  SetProp(P_MAX_ALCOHOL,100);
+  Set( P_WIMPY, 20, F_VALUE);
+
+  SetProp(P_HANDS, ({" mit blossen Haenden", 30}));
+  Set(P_HANDS, SAVE, F_MODE_AS);
+  SetProp(P_MAX_HANDS, 2);
+
+  Set(P_MARRIED, SAVE, F_MODE_AS);
+  Set(P_EXTRA_LOOK, SAVE, F_MODE_AS);
+  Set(P_SHOW_EXITS, SAVE, F_MODE_AS);
+  Set(P_SHOW_EXITS, 1);
+  Set(P_WANTS_TO_LEARN, SAVE, F_MODE_AS);
+  Set(P_CAN_FLAGS, SAVE, F_MODE_AS);
+  Set(P_TESTPLAYER, SAVE|PROTECTED, F_MODE_AS);
+  Set(P_ALLOWED_SHADOW, SAVE|SECURED, F_MODE_AS);
+  Set(P_SECOND, SAVE, F_MODE_AS);
+  Set(P_INVIS, SAVE, F_MODE_AS);
+  Set(P_READ_NEWS, SAVE, F_MODE_AS);
+  Set(P_START_HOME, SAVE, F_MODE_AS);
+  Set(P_PRAY_ROOM, SAVE, F_MODE_AS);
+  Set(P_MAILADDR, SAVE, F_MODE_AS);
+  Set(P_HOMEPAGE, SAVE, F_MODE_AS);
+  Set(P_ICQ, SAVE, F_MODE_AS);
+  Set(P_MESSENGER, SAVE, F_MODE_AS);
+  Set(P_LOCATION, SAVE, F_MODE_AS);
+
+  Set(P_NO_ASCII_ART, SAVE, F_MODE_AS);
+
+  Set(P_VISUALBELL, SAVE, F_MODE_AS);
+  Set(P_CARRIED_VALUE, SAVE, F_MODE_AS);
+
+  Set(P_PROMPT, "> ");
+  Set(P_PROMPT, SAVE, F_MODE_AS);
+  Set(P_CALLED_FROM_IP, SAVE, F_MODE_AS);
+  Set(P_INFORMME,SAVE|PROTECTED,F_MODE_AS);
+  Set(P_WAITFOR,SAVE|PROTECTED,F_MODE_AS);
+  Set(P_WAITFOR_REASON,SAVE|PROTECTED,F_MODE_AS);
+  Set(P_DAILY_PLAYTIME,SAVE|PROTECTED,F_MODE_AS);
+  Set(P_NETDEAD_ENV, PROTECTED|NOSETMETHOD, F_MODE_AS);
+  
+  autoload = ([]);
+  autoload_rest = ([]);
+  autoload_error = ({});
+  SetProp(P_ARTICLE,0);
+  Set(P_GUILD,SAVE,F_MODE_AS);
+  Set(P_GUILD_TITLE,SAVE,F_MODE_AS);
+  Set(P_GUILD_LEVEL,SAVE,F_MODE_AS);
+  Set(P_GUILD_RATING,SAVE,F_MODE_AS);
+  Set(P_NEWSKILLS,SAVE,F_MODE_AS);
+  Set(P_NEEDED_QP,REQ_QP);
+  Set(P_DEADS,0);
+  Set(P_DEADS, NOSETMETHOD,F_SET_METHOD);
+  Set(P_DEADS,SAVE|PROTECTED|SECURED,F_MODE_AS);
+  Set(P_LAST_LOGIN,-1);
+  Set(P_LAST_LOGIN, NOSETMETHOD,F_SET_METHOD);
+  Set(P_LAST_LOGIN,SAVE|PROTECTED|SECURED,F_MODE_AS);
+  Set(P_LAST_LOGOUT,-1);
+  Set(P_LAST_LOGOUT, NOSETMETHOD,F_SET_METHOD);
+  Set(P_LAST_LOGOUT,SAVE|PROTECTED|SECURED,F_MODE_AS);
+  Set(P_CLOCKMSG,SAVE,F_MODE_AS);
+  Set(P_TIMEZONE,SAVE,F_MODE_AS);
+  Set(P_SHOWEMAIL,SAVE,F_MODE_AS);
+  Set(P_LAST_QUIT,SAVE|PROTECTED|SECURED,F_MODE_AS);
+
+  Set(P_CMSG, 0, F_MODE); // to clean out the old clone messages
+  Set(P_DMSG, 0, F_MODE);
+  Set(P_CLONE_MSG, SAVE, F_MODE);
+  SetProp(P_CLONE_MSG, "zaubert etwas hervor");
+  Set(P_DESTRUCT_MSG, SAVE, F_MODE);
+  SetProp(P_DESTRUCT_MSG, "verschwindet einfach");
+
+  Set(P_FAO, SAVE|SECURED, F_MODE_AS);
+  Set(P_FAO_PORTALS, SAVE, F_MODE_AS);
+
+  SetProp(P_NEWBIE_GUIDE,0);
+  Set(P_NEWBIE_GUIDE,SAVE,F_MODE_AS);
+
+  //TODO: Remove - Property ist not needed any more.
+  Set(P_TELNET_KEEP_ALIVE, PROTECTED|SAVE, F_MODE_AD);
+  SetProp(P_TELNET_KEEP_ALIVE, 0);
+
+  AddId("Interactive");
+
+  realip="";
+}
+
+// ACHTUNG: Falls hier mal sonst noch weitere Resets geerbt werden, muss das
+// hier ordentlich definiert werden!
+void reset() { 
+  comm::reset();
+  // momentan kann der Reset jetzt abgeschaltet werden, da er nur die
+  // TM-History loescht und ggf. von Netdead() und Disconnect() reaktiviert
+  // wird.
+  set_next_reset(-1);
+}
+
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+  moving::NotifyMove(dest,oldenv,method);
+  // ggf. Daten ueber neues Env per GMCP senden.
+  if (dest != oldenv)
+    GMCP_Room();
+}
+
+string Forschung()
+{
+  return LEPMASTER->QueryForschung();
+}
+
+/** Setzt Defaultwerte fuer Rassen - wird von den Shells ueberschrieben.
+*/
+mixed RaceDefault(string arg)
+{
+  if (!arg)
+    return 0;
+  switch(arg)
+  {
+    case P_HANDS :
+      return ({" mit blossen Haenden",30,DT_BLUDGEON});
+    case P_BODY :
+      return 0;
+  }
+  return 0;
+}
+
+/** Prueft Spielerobjekt auf Konsistenz.
+  Ueberprueft das Spielerobjekt nach kompletter Initialisierung (im 
+  callout) auf korrekte Werte einiger Props.
+*/
+void checkConsistency()
+{ mixed h;
+  int   m;
+
+  if (pointerp(h=RaceDefault(P_HANDS)) && sizeof(h)>1)
+    m=(int)h[1];
+  else
+    m=30;
+  if((h=Query(P_HANDS))[1] > m && !IS_LEARNER(this_object())) {
+    log_file("inconsistent", sprintf(
+      "[%s] %O: HANDS: %d\n", dtime(time()), this_player(), h[1]));
+    h[1] = m;
+    Set(P_HANDS,h);
+  }
+
+  if (Query(P_BODY)!=(m=RaceDefault(P_BODY)))
+     Set(P_BODY,m);
+
+  if (!Query(P_SECOND_MARK,F_MODE))
+    Set(P_SECOND_MARK,SAVE|PROTECTED,F_MODE_AS);
+  if (!Query(P_TTY_SHOW,F_MODE)&SAVE)
+  {
+    Set(P_TTY_SHOW,0,F_VALUE);
+    Set(P_TTY_SHOW,SAVE,F_MODE_AS);
+  }
+  if (Query(P_TTY_COLS,F_MODE)&SAVE)
+  {
+    Set(P_TTY_COLS,SAVE,F_MODE_AD);
+    Set(P_TTY_ROWS,SAVE,F_MODE_AD);
+    Set(P_TTY_TYPE,SAVE,F_MODE_AD);
+  }
+}
+
+/** Bittet das Spielerobjekt, sich zu zerstoeren.
+  \param[in] silent Flag, ob ohne Textausgaben zerstoert werden soll
+  \return Erfolg der Selbstzerstoerung
+*/
+varargs int remove(int silent)
+{
+  return moving::remove(silent);
+}
+
+/** Schaltet in allen Objekten im Inv HBs aus.
+  Schaltet im Inv in allen Objekten (rekursiv, deep_inventory)
+  die Heartbeats aus. Falls obs uebergeben wird, werden diese Objekte
+  statt des Inventars benutzt.
+  Speichert die Objekte, die einen HB hatten, in der glob. Var. hb_obs.
+  @param[in] obs mixed - Objekte, in denen der HB abgeschaltet werden soll. 
+    Container werden rekursiv behandelt.
+  @attention Achtung! Niemals zweimal hintereinander rufen, ohne 
+    zwischendurch restart_heart_beats() gerufen zu haben!
+  @sa restart_heart_beats
+*/
+varargs static void stop_heart_beats(mixed obs)
+{
+  int i;
+
+  if (!obs)
+  {
+    hb_obs=({});
+    obs=deep_inventory(ME);
+  } 
+  foreach(mixed ob: obs) {
+    if (pointerp(ob))
+        stop_heart_beats(ob);
+    else if (set_object_heart_beat(ob,0))  
+        hb_obs+=({ob});
+  }
+}
+
+/** Schaltet HBs in Objekten im Inv wieder ein.
+  Schaltet in allen Objekten in hb_obs den Heartbeat ein.
+    In hb_obs (glob. Var.) stehen alle Objekte, die beim letzten Aufruf von
+    stop_heart_beats() einen HB hatten.
+  @sa stop_heart_beats
+*/
+static void restart_heart_beats()
+{
+  int i;
+
+  if (pointerp(hb_obs))
+  {
+    foreach(object ob: hb_obs)
+      set_object_heart_beat(ob,1);
+    hb_obs=0;
+  }
+}
+
+/** Prueft auf abgelaufene Spielzeit.
+  Prueft in Spielerobjekten, ob die taegliche Maximalspielzeit
+    abgelaufen ist.
+    Wird im heart_beat() gerufen.
+  @return int - Flag, ob Spielzeit abgelaufen und Logout erfolgen soll
+*/
+static int CheckDailyPlaytime() {
+  int *spieldauer,d;
+
+  if (!pointerp(spieldauer=Query(P_DAILY_PLAYTIME)))
+    return 0;
+  // 0:Minuten pro Tag, 1:Nr. des letzen Tages, 2:Nr. des angefangenen Tages,
+  // 3:letzte Zeitpruefung, 4:verbleibende Zeit
+  d=time()/86400;
+  if (spieldauer[1]<=d) { // Ende der zeitbeschraenkten Tage?
+    Set(P_DAILY_PLAYTIME,0);
+    return 0;
+  } else if (spieldauer[2]!=d) { // Neuer Tag?
+    spieldauer[4]=spieldauer[0];
+    spieldauer[2]=d;
+  } else {
+    spieldauer[4]-=(time()-spieldauer[3]);
+  }
+  spieldauer[3]=time();  // Letzte Zeitpruefung
+  Set(P_DAILY_PLAYTIME,spieldauer);
+  if (spieldauer[4]<0) { // Keine Zeit mehr uebrig fuer heute
+    if (!interactive(ME))
+      return 1;
+    write("Du hast lange genug gemuddet fuer heute.\n");
+    say(Name(WER)+" hat fuer heute genug gemuddet.\n");
+    remove_interactive(ME);
+    return 1;
+  }
+  return 0;
+}
+
+/** Gibt Erwartemeldung mit Grund aus.
+  @param[in] who   string - Wer ist hereingekommen?
+  @param[in] invis int - Ist der Spieler Invis?
+*/
+static void Show_WaitFor_Reason(string who, int invis)
+{
+  mixed list;
+  string reason,name;
+
+  if (invis) name="("+who+")";
+    else name=who;
+  if ((mappingp(list=QueryProp(P_WAITFOR_REASON))) && (reason=list[who]))
+    tell_object(ME,sprintf("\nDu erwartest %s wegen:\n%s\n",name,reason));
+  else
+    tell_object(ME,sprintf("Du erwartest %s aus keinem bestimmten Grund.\n",
+      name));
+}
+
+/** Gibt Liste der Erwarteten Spieler an this_player() aus.
+*/
+static void ListAwaited() //Anwesende Erwartete auflisten
+{
+    string *list;
+    mixed mlist;
+    object ob;
+    int mag;
+
+    mag=IS_LEARNER(ME);
+
+    list=({});
+    foreach(string erwartet : QueryProp(P_WAITFOR)) {
+        if (objectp(ob=find_player(lower_case(erwartet)))) {
+            if (ob->QueryProp(P_INVIS)) {
+                if (mag) list+=({ sprintf("(%s)",erwartet) });
+            }
+            else list+=({erwartet});
+        }
+    }
+    if (sizeof(list))
+        printf("Anwesende Erwartete: %s.\n",
+            CountUp(sort_array(list,#'>)));
+
+    if ((mappingp(mlist=QueryProp(P_WAITFOR_REASON))) && (sizeof(mlist)))
+        {
+          foreach(string erwartet : mlist) {
+                if (!(ob=find_player(lower_case(erwartet))) ||
+                    (!mag && ob->QueryProp(P_INVIS)));
+                else Show_WaitFor_Reason(erwartet,ob->QueryProp(P_INVIS));
+          }
+        }
+}
+/** Teilt den Gilden und anderen Spielern mit, wer reingekommen ist.
+  Ausserdem wird ggf. PlayerQuit() im Environment gerufen.
+  \param[in] rein int - wahr, wenn der Spieler einloggt.
+*/
+protected void call_notify_player_change(int rein)
+{
+  string wer = getuid(ME);
+  // erst die Gilde informieren
+  string gilde = QueryProp(P_GUILD);
+  if (stringp(gilde) && (find_object("/gilden/"+gilde)
+        || file_size("/gilden/"+gilde+".c")>0))
+    catch(("/gilden/"+gilde)->notify_player_change(ME, rein); publish);
+
+  // dann die anderen Spieler
+  int mag = IS_LEARNER(ME);
+  int invis = QueryProp(P_INVIS);
+  object *u = users() - ({ME}); // sich selber nicht melden 
+  if (mag && invis) {   // Invismagier nur Magiern melden
+    u = filter(u, function int (object o)
+        { return query_wiz_level(o) >= LEARNER_LVL; }
+        );
+  }
+  u->notify_player_change(capitalize(wer),rein,invis);
+
+  // und beim Ausloggen noch das Env informieren.
+  if (!rein) {
+    if(environment()) catch(environment()->PlayerQuit(ME);publish);
+  }
+}
+
+/** Ruft im uebergebenen Objekt ein init() auf, sofern notwendig.
+  Ruft in ob ein init() auf, falls das Objekt nach dem
+    letzten Ausloggen geschaffen wurde.
+  \param[in] ob object - Objekt, in dem init() gerufen wird.
+  \param[in] logout int - Letzter Logout
+  \return 1, falls init() gerufen wurde. 0 sonst.
+*/
+static int call_init( object ob, int logout )
+{
+    if ( objectp(ob) && object_time(ob) > logout )
+        return catch(ob->init(); publish), 1;
+    return(0);
+}
+
+/** Holt seit dem letzten Ausloggen ausgefallene Inits nach.
+  Ruft in den uebergebenen Objekten call_init(), was einen init()
+  ausloest, falls das Objekt seit dem letzten Ausloggen erstellt wurde.
+  \param[in] logout Zeitpunkt des letzten Logouts
+  \param[in] obs Array von Objekten
+  \sa call_init()
+*/
+static void inits_nachholen( int logout, object *obs )
+{
+    filter( obs, "call_init", ME, logout );
+}
+
+/** Belebt einen Netztoten wieder.
+  Wird im Login gerufen, wenn der Spieler netztot war. Aequivalent zu
+  start_player()
+  @param[in] silent Wenn Flag gesetzt, werden keine Meldung an den Raum
+  ausgegeben.
+  @param[in] ip Textuelle Repraesentation der IP-Adresse, von der der Spieler
+  kommt.
+  @see start_player()
+*/
+varargs void Reconnect( int silent )
+{
+    int num;
+    string called_from_ip;
+    object *inv;
+
+    if ( query_once_interactive(ME) )
+    {
+        // perform the telnet negotiations. (all that are available)
+        "*"::startup_telnet_negs();
+        Set( P_LAST_LOGIN, time() );
+    }
+
+    enable_commands();
+    set_living_name( getuid() );
+    _remove_netdead();
+    set_heart_beat(1);
+    // Hunttimes aktualisieren und ggf. Feinde vergessen.
+    update_hunt_times((time()-QueryProp(P_LAST_LOGOUT)) /__HEART_BEAT_INTERVAL__);
+    // Heartbeats in Objekten im Inv reaktiveren.
+    restart_heart_beats();
+    // life.c will ggf. was aufraeumen
+    life::reconnect();
+
+    log_file( "REENTER", sprintf( "%-11s %s, %-15s (%s).\n",
+                                  capitalize(getuid(ME)), ctime(time())[4..15],
+                                  query_ip_number(ME)||"Unknown",
+                                  query_ip_name(ME)||"Unknown" ),
+              200000 );
+
+    if ( ndead_currently )
+        ndead_revive();
+
+    if ( !silent && interactive(ME) )
+        call_notify_player_change(1);
+
+    command::reconnect();
+    if ( query_once_interactive(ME) )
+        modify_prompt();
+
+    // Login-event ausloesen
+    EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
+          E_OBJECT: ME,
+          E_PLNAME: getuid(ME),
+          E_ENVIRONMENT: environment() ]) );
+
+    catch( num = "secure/mailer"->FingerMail(geteuid());publish );
+
+    if ( num )
+        write( "Du hast " + num + " neue" + (num == 1 ? "n Brief" : " Briefe")
+               + " im Postamt liegen.\n" );
+
+    if ( QueryProp(P_AWAY) )
+        write( break_string( "Du bist als abwesend gekennzeichnet: " +
+                             QueryProp(P_AWAY) + ".", 78 ) );
+
+    catch( RegisterChannels(); publish );
+
+    if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
+         query_ip_number(ME) != called_from_ip ) {
+        string tmp;
+
+        if ( stringp(tmp = query_ip_name(called_from_ip)) &&
+             tmp != called_from_ip )
+            tmp = " [" + tmp + "]";
+        else
+            tmp = "";
+
+        write( "Das letzte Mal kamst Du von " + called_from_ip + tmp + ".\n" );
+    }
+
+    Set( P_CALLED_FROM_IP, query_ip_number(ME) );
+
+    // falls Gegenstaende mit 'upd -ar' upgedated wurden, muessen die
+    // "verloren gegangenen" init()'s nachgeholt werden
+    if ( query_once_interactive(ME) && !IS_LEARNER(ME) )
+        call_out( "inits_nachholen", 0, Query(P_LAST_LOGOUT),
+                  all_inventory(ME) );
+
+    // noch nicht geclonte Autoloader "nach"clonen
+    while ( remove_call_out("load_auto_objects") != -1 )
+        /* do nothing */;
+
+    if ( sizeof(autoload_rest) )
+        call_out( "load_auto_objects", 0, autoload_rest );
+
+    if (ndead_location) {
+      catch( ndead_location->BecomesNetAlive(ME);publish );
+      inv = all_inventory(ndead_location);
+      ndead_location = 0;
+    }
+    else
+      inv = ({});
+
+    inv += deep_inventory(ME);
+
+    //ZZ foreach statt call_other(), damit nen bug in BNA nicht die anderen
+    //BNA verhindert.
+    foreach(object ob: inv) {
+        //es ist nicht auszuschliessen, dass Items durch BecomesNetAlive()
+        //eines anderen zerstoert werden.
+        if (objectp(ob))
+          catch( call_other(ob, "BecomesNetAlive", ME);publish );
+    }
+
+    // Erst an dieser Stelle, weil der Spieler u.U. durch ein BecomesNetAlive()
+    // noch bewegt wurde.
+    if ( !silent && environment() && object_name(environment()) != NETDEAD_ROOM )
+    {
+        if(query_hc_play()<=1)
+          tell_room(environment(),QueryProp(P_NAME) + " weilt wieder unter den Lebenden.\n",({ME}) );
+        else 
+          tell_room(environment(),QueryProp(P_NAME) + " weilt wieder unter den Verstorbenen.\n",({ME}) );
+    }
+
+    NewbieIntroMsg();
+
+    if ( query_once_interactive(ME) )
+        ListAwaited();
+}
+
+/** Loggt einen Spieler aus und macht ihn netztot.
+  Bewegt einen Spieler in den Netztotenraum, deaktiviert Heartbeats im
+  Inventar, ruft BecomesNetDead(), loest Erwartemeldungen aus, triggert
+  Ausloggevent.
+*/
+void NetDead()
+{
+  object *inv;
+  int num;
+
+  catch(RemoveChannels();publish);
+
+  if(query_hc_play()>1)
+    say("Ploetzlich weicht alle spirituelle Energie aus "+QueryProp(P_NAME)+".\n");
+  else
+    say("Ploetzlich weicht alles Leben aus "+QueryProp(P_NAME)+".\n");
+
+  _set_netdead();
+  remove_call_out("quit");
+  remove_living_name();
+  // Wird zwar im save_me() gemacht, aber die Zeitunterschiede beim
+  // fingern direkt nach dem Ausloggen fuehren immer wieder zu Verwirrungen
+  if(query_once_interactive(ME) && !QueryProp(P_INVIS))
+      Set(P_LAST_LOGOUT,time());
+  if (ME)
+    ndead_location = environment();
+
+  if (query_once_interactive(ME))
+    call_notify_player_change(0);
+
+  // Logout-event ausloesen
+  EVENTD->TriggerEvent(EVT_LIB_LOGOUT, ([
+          E_OBJECT: ME,
+          E_PLNAME: getuid(ME),
+          E_ENVIRONMENT: environment() ]) );
+
+  set_next_reset(900);
+  /* Bei Nicht-Magier-Shells wird comm::reset() aufgerufen, das prueft, ob
+     der Spieler immer noch netztot ist, und falls ja, die tmhist loescht.
+     Die Methode wird von /std/shells/magier.c ueberschrieben, netztote
+     Magier (die eigentlich schon anderweitig beseitigt worden sein sollten)
+     werden remove()d und destruct()ed. --Amynthor 05.05.2008 */
+
+  if (environment()) {
+    catch(environment()->BecomesNetDead(ME);publish);
+    inv = deep_inventory(ME)+all_inventory(environment());
+  }
+  else inv=deep_inventory(ME);
+  foreach(object ob: inv) {
+      if (objectp(ob)) //man weiss nie was BND() macht...
+          catch( call_other(ob, "BecomesNetDead", ME);publish );
+  }
+}
+
+
+/** Sendet ggf. Telnet Timing Marks als Keep-Alive Pakete an den Client.
+  * Wird in heart_beat() gerufen.
+  * @return 1, falls der Spieler Keep-Alive Paket wuenscht, sonst 0. 
+  * @see heart_beat()
+*/
+protected int CheckTelnetKeepAlive() {
+  if (telnet_tm_counter > 0) {
+    // Spieler hat offenbar ein Keep-Alive konfiguriert ...
+    if (!(--telnet_tm_counter)) {
+      // und das Intervall ist gerade abgelaufen.
+      // Prop offenbar gesetzt, FEature ist wirklich gewuenscht,
+      // Telnet Timing Mark senden 
+      send_telnet_timing_mark();
+      // alle 120 HBs (240s, 4min).
+      // sollte eigentlich 240 / __HEART_BEAT_INTERVAL__ sein. Aber spart
+      // eine Operation im HB. ;-)
+      telnet_tm_counter = 120;
+    }
+    return 1; // Keep-Alive ist eingeschaltet
+  }
+  return 0;
+}
+
+static void ndead_move_me();
+
+/** Heartbeat des Spielerobjektes.
+  Prueft taegliche Spielzeit, speichert regelmaessig den Spieler,
+  bewegt Netztote in den Netztodenraum, ruft die HBs aus living/combat und
+  player/life.
+*/
+protected void heart_beat() {
+  if (!ME)
+    return;
+  if (ndead_currently)
+  {
+    if (interactive(ME))
+    {
+      ndead_revive();
+      ndead_location=0;
+    }
+    else return;
+  }
+  else
+    if (!(ndead_next_check--))
+    {
+      ndead_next_check=NETDEAD_CHECK_TIME;
+      if (!interactive(ME))
+        if (ndead_lasttime)
+        {
+          save_me(1);
+          if (IS_LEARNER(ME))
+          {
+            quit();
+            if (ME)
+              remove();
+            if (ME)
+              destruct(ME);
+            return;
+          }
+          ndead_move_me();
+          // Zumindest bei Gaesten ist das Objekt jetzt zerstoert
+          if (!objectp(this_object()))
+            return;
+        }
+        else
+          ndead_lasttime=1;
+    }
+  if (ME && ndead_lasttime && interactive(ME))
+    ndead_lasttime=0;
+  if (CheckDailyPlaytime())
+    return;
+
+  CheckTelnetKeepAlive();
+
+  life::heart_beat();
+  combat::heart_beat();
+  skills::heart_beat();
+}
+
+/** ID-Funktion fuer Spielerobjekte.
+ * id() fuer Spieler. Besondere Behandlung fuer Froesche und Geister,
+ * sowie Invis-Status.
+ * Gibt immer 0 zurueck, wenn P_INVIS && lvl < P_LEVEL
+ * @param[in] str angefragter ID-String
+ * @param[in] lvl Level des Anfragenden
+ * @return 1, falls str auf den Spieler zutrifft, 0 sonst.
+ */
+varargs int id(string str, int lvl)
+{
+  if (::id(str))
+    return 1;
+  if (Query(P_INVIS) && lvl < QueryProp(P_LEVEL))
+    return 0;
+  if (QueryProp(P_GHOST)&& str == "geist von "+ lower_case(QueryProp(P_NAME)))
+    return 1;
+  if (QueryProp(P_FROG) && str == "frosch") return 1;
+  return(0);
+}
+
+/** Setzt Spielerhomepage (Spielerkommando).
+  * @param[in] str Spielereingabe
+  * @return 1 bei Erfolg, 0 sonst.
+  */
+static int set_homepage(string str)
+{
+  mixed tmp;
+  if (!(str=_unparsed_args())) {
+    if (!QueryProp(P_HOMEPAGE))
+      write("Du hast keine URL-Adresse gesetzt!\n");
+    else
+      write("Deine offizielle URL-Adresse lautet: " + QueryProp(P_HOMEPAGE)
+            +"\n");
+    return 1;
+  }
+  write("Deine offizielle URL-Adresse wurde geaendert.\n");
+  if (str=="keine") 
+    SetProp(P_HOMEPAGE, 0);
+  else {
+    tmp = filter(regexplode(str, "[<][^>]*[>]"),
+                       lambda(({'e}), ({#'!=, ({#'[, 'e, 0}), '<'})));
+    write("Sie lautet jetzt: "+(str = implode(tmp, ""))+"\n");
+    SetProp(P_HOMEPAGE, str);
+  }
+  return 1;
+}
+
+/** Setzt Spieler-Wohnort (Spielerkommando).
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int set_location( string str )
+{
+    mixed ort;
+
+    if ( str == "0" || str == "loeschen" ){
+        Set( P_LOCATION, 0 );
+        write( "Du loescht Deine Ortsangabe.\n" );
+        return 1;
+    }
+
+    if ( stringp(str = _unparsed_args()) && str != "" ){
+        Set( P_LOCATION, capitalize(str) );
+        printf( "Du aenderst Deine Ortsangabe auf \"%s\".\n",
+                Query(P_LOCATION) );
+    }
+    else if ( stringp(ort = Query(P_LOCATION)) )
+        printf( "Deine Ortsangabe lautet \"%s\".\n", Query(P_LOCATION) );
+    else{
+        Set( P_LOCATION, 0, F_VALUE );
+        write( "Du hast keine Ortsangabe gesetzt.\n" );
+    }
+
+    return 1;
+}
+
+/** Setzt ICQ-UIN des Spielers (Spielerkommando).
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int set_icq(string str) {
+  int num;
+
+  if (!str || str=="") {
+    if (!num=QueryProp(P_ICQ))
+      write("Du hast keine ICQ-Nummer gesetzt!\n");
+    else
+      printf("Deine ICQ-Nummer lautet: %d\n",num);
+    return 1;
+  }
+  if (sscanf(str,"%d",num)!=1 || !num) {
+    write("Deine ICQ-Nummer wurde geloescht.\n");
+    SetProp(P_ICQ, 0);
+  } else {
+    write("Deine ICQ-Nummer wurde geaendert.\n");
+    printf("Sie lautet jetzt: %d\n",num);
+    SetProp(P_ICQ, num);
+  }
+  return 1;
+}
+
+/** Setzt Instant Messanger vom Spieler (Spielerkommando).
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int set_messenger(string str) {
+  int num;
+  string s;
+
+  if (!str || str=="") {
+    if (!s=QueryProp(P_MESSENGER))
+      if (!num=QueryProp(P_ICQ)) 
+        write("Du hast keine Messenger-ID gesetzt.\n");
+      else 
+        printf("Du hast keine Messenger-ID gesetzt, aber eine ICQ-Nummer: %d\n", num);
+    else
+      printf("Deine Messenger-ID lautet: %s\n", s);
+    return 1;
+  }
+  if (str=="loeschen" || str=="keine") {
+    write("Deine Messenger-ID wurde geloescht.\n");
+    SetProp(P_MESSENGER, 0);
+  } else {
+    s = _unparsed_args();
+    printf("Deine Messenger-ID lautet nun: %s\n", s);
+    SetProp(P_MESSENGER, s);
+  }
+  return 1;
+}
+
+
+// Prueft, ob der String vermutlich eine eMail-Adresse ist.
+// dies ist nicht narrensicher, wird aber die meisten eMail-Adressen zulassen
+// und viel Schrott ablehnen.
+private string check_email(string str) {
+  if (!stringp(str)) return 0;
+  return regmatch(lower_case(str),
+      "[a-z0-9._%+-]+@(?:[a-z0-9-]+\.)+[a-z]{2,4}",RE_PCRE);
+}
+
+/** Setzt Email-Adresse des Spielers (Spielerkommando).
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int set_email(string str)
+{
+  if (!(str=_unparsed_args())) {
+    write("Deine offizielle Email-Adresse lautet: " + QueryProp(P_MAILADDR)
+          +"\n");
+    return 1;
+  }
+  str = check_email(str);
+  if (!str) {
+    notify_fail("Deine Eingabe scheint keine gueltige EMail-Adresse "
+        "zu sein.\n");
+    return 0;
+  }
+  write("Deine EMail-Adresse wurde geaendert zu:\n"
+      +str+"\n");
+  SetProp(P_MAILADDR, str);
+  return 1;
+}
+
+/** Spielerkommando 'selbstloeschung'.
+  * Gibt Meldung aus und fragt nach einer Bestaetigung.
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int self_delete()
+{
+  write(
+    "     B I S T  D U  D I R  W I R K L I C H  S I C H E R ????????????\n"+
+    "Wenn Du Dich selbstloeschen willst, ist Dein Charakter UNWIDERRUFLICH\n"+
+    "verloren. Es gibt KEINE Moeglichkeit ihn wiederzuerschaffen. Solltest\n"+
+    "Du nur zeitweilig vom "MUDNAME" wegbleiben wollen, so benutze bitte\n"+
+    "den Befehl 'spielpause'.\n"+
+    "Fallst Du Dich immer noch selbstloeschen willst, gib Dein Password ein."+
+    "\n\n");
+  input_to("self_delete2",INPUT_PROMPT|INPUT_NOECHO, "Bitte das Password angeben: ");
+  return 1;
+}
+
+/** Spielerkommando 'selbstloeschung'.
+  * Empfaengt Bestaetigung des Spielers und ruft Loeschfunktion in 
+  * /secure/master auf.
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+int self_delete2(string str)
+{
+  int ret;
+  ret=(int)"secure/master"->delete_player(str, getuid(PL));
+  if (!ret)
+  {
+    write("Das hat nicht hingehauen (Gott sei Dank ....)\n");
+    return 1;
+  }
+  if (QueryProp(P_GUILD)&&file_size(GUILD_DIR+QueryProp(P_GUILD)+".c")>-1)
+    catch(call_other(GUILD_DIR+QueryProp(P_GUILD), "austreten");publish);
+
+  if (QueryProp(P_DEADS) < 5) {
+    write("Adios! Man sieht sich.\n");
+    say(name(WER,1)+" hat sich gerade selbst zerstoert.\n");
+  }
+  else {
+    write(
+      "\nTod kommt auf seinem weissen Pferd angeritten.\n"
+     +"Er steigt ab, baut sich drohend vor Dir auf und mustert Dich schadenfroh.\n"
+     +"\nTod sagt: ENDLICH! NUN KANN DIR AUCH LARS NICHT MEHR HELFEN!\n"
+     +"\nTod holt weit mit seiner Sense aus. Mit grossem Schwung laesst er sie auf\n"
+     +"Dich zusausen und dann...\n");
+    say(name(WER,1)+" schied gerade endgueltig von uns.\n");
+  }
+
+  // Event ausloesen. ;-)
+  EVENTD->TriggerEvent(EVT_LIB_PLAYER_DELETION, ([
+        E_PLNAME: getuid(ME),
+        E_ENVIRONMENT: environment(),
+        E_GUILDNAME: QueryProp(P_GUILD) ]) );
+
+  remove(1);
+  return 1;
+}
+
+/** Setzt neue taegliche Spieldauer (Spielerkommando).
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int spieldauer(string str) {
+  int min,day;
+  string foo;
+
+  notify_fail("  spieldauer <x> minuten fuer %d tage\noder\n"+
+              "  spieldauer <x> stunden fuer %d tage\n");
+  if (!str)
+    return 0;
+  if (sscanf(str,"%d stunde%s fuer %d tag%s",min,foo,day,foo)==4)
+    min*=60;
+  else if (sscanf(str,"%d minute%s fuer %d tag%s",min,foo,day,foo)!=4)
+    return 0;
+  if (min<5)
+    min=5;
+  if (min>=1440)
+    return notify_fail("Witzbold.\n"),0;
+
+  Set(P_DAILY_PLAYTIME,
+      ({min*60,time()/86400+day,time()/86400,time(),min*60}));
+  // 0:Minuten pro Tag, 1:Nr. des letzen Tages, 2:Nr. des angefangenen Tages,
+  // 3:letzte Zeitpruefung, 4:verbleibende Zeit
+  printf("Du darfst die naechsten %d Tag(e) nur noch\n"+
+   "%d Minuten am Tag mudden.\n",day,min);
+  return 1;
+}
+
+/** Interpretiert Angabe des Spielers fuer Spielpause.
+  * \param[in] a Zeitangabe fuer Spielpause.
+  * \return Zeitpunkt, Ende der Spielpause.
+  */
+private int InterpretTime(string a){
+  // akzeptiert folgende Formate:
+  //   dd.mm.jj     (Rueckgabe: 0:00 des entsprechenden Tages)
+
+  int *ts = allocate(9);
+  int i,j,k,nrargs;
+
+  if ((nrargs=sscanf(a,"%d.%d.%d",i,j,k))==3 ||
+      (nrargs=sscanf(a,"%d.%d.",i,j))==2) {
+    // wenn kein jahr angegeben ist, das aktuelle nehmen.
+    if (nrargs == 2)
+       ts[TM_YEAR] = localtime()[TM_YEAR];
+    else {
+       // Zwei-Ziffern-Angabe des Jahres...
+       if (k<100)
+           k += 2000;
+       ts[TM_YEAR] = k;
+    }
+    ts[TM_MDAY] = i;
+    ts[TM_MON] = j - 1;
+
+    int zeit = mktime(ts);
+
+    // negative und vergangene Zeiten pruefen.
+    if (zeit <= time()) {
+      write("Dieser Zeitpunkt liegt in der Vergangenheit.\n");
+      return 0;
+    }
+    return zeit;
+  }
+  return 0;
+}
+
+/** Setzt neue Spielpause (Spielerkommando).
+  * Fragt vorher nach Bestaetigung durch den Spieler.
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  * \sa spielpause2()
+  */
+static int spielpause(string str)
+{
+  int days,endezeit;
+  string foo;
+
+  notify_fail("spielpause <x> tage          oder\n"+
+              "spielpause bis tt.mm[.jj]\n");
+  if (!str) return 0;
+  if(sscanf(_unparsed_args(),"bis %s",foo)==1) {
+    endezeit = InterpretTime(foo);
+    if (endezeit == 0) 
+        return 0;
+    days = ((endezeit - time()) / 86400) + 1;
+  }
+  else if(sscanf(str, "%d tag%s", days, foo) == 2) {
+    if (days < 0)
+        days = -1;
+    else
+        endezeit = (time()/86400) * 86400 + days * 86400;
+  }
+  else return 0;
+
+  if (days > 0)
+    write(strftime("Du wirst Dich erst wieder am %d.%m.%Y einloggen koennen!\n",
+          endezeit));
+  else if (days < 0)
+    write( "Du wirst Dich auf unbestimmte Zeit nicht mehr einloggen koennen.\n"
+          +"Wenn Du wieder spielen willst, musst Du Dich an einen Gott oder\n"
+          +"Erzmagier wenden (mit einem Gast oder Mail von aussen).\n" );
+  else {
+    write( "Die Spielpause ist aufgehoben.\n" );
+    master()->TBanishName(getuid(this_object()), 0);
+    return 1;
+  }
+  write( "Wenn Du das wirklich willst, gib jetzt 'ja' ein.\n" );
+  input_to( "spielpause2", INPUT_PROMPT, "]", days);
+  return 1;
+}
+
+/** Setzt neue taegliche Spieldauer, wird ueber spielpause() gerufen.
+  * \param[in] str Spielereingabe, Bestaetigung
+  * \param[in] days Dauer der Spielpause (in Tagen).
+  * \sa spielpause()
+  */
+static void spielpause2(string str, int days)
+{
+  if (str && (str == "ja" || str == "Ja" || str == "JA")) {
+    master()->TBanishName(getuid(this_object()), days);
+    write(
+      "Ok, die Spielpause wird mit dem naechsten Ausloggen wirksam.\n"
+     +"Solltest Du es Dir bis dahin noch einmal ueberlegt haben, so kannst\n"
+     +"Du den Vorgang mit 'spielpause 0 tage' wieder rueckgaengig machen.\n" );
+    return;
+  }
+  write("Vorgang wurde abgebrochen.\n" );
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+  * Fragt nach altem Passwort und ruft change_password2().
+  * \return 1 bei Erfolg, 0 sonst.
+  * \sa change_password2(), change_password3(), change_password4()
+  */
+static int change_password() {
+  string verb;
+  verb=query_verb();
+  if (verb!="passwd"&&verb!="password"&&verb!="passwort")
+    return 0;
+  input_to("change_password2",INPUT_NOECHO|INPUT_PROMPT,
+      "Bitte das ALTE Passwort angeben: ");
+  return 1;
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+  * Prueft altes Passwort, fragt nach neuem Passwort und ruft 
+  *   change_password3().
+  * \param[in] str Spielereingabe des alten Passwortes
+  * \return 1 bei Erfolg, 0 sonst.
+  * \sa change_password(), change_password3(), change_password4()
+  */
+static int change_password2(string str) {
+  write("\n");
+  if (!str)
+    str="";
+  if (MASTER->update_password(str,str) == 0) {
+    write("Falsches Passwort!\n");
+    return 1;
+  }
+  passwold = str;
+  input_to("change_password3",INPUT_NOECHO|INPUT_PROMPT,
+      "Bitte das NEUE Passwort eingeben: ");
+  return 1;
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+  * Prueft neues Passwort, fragt nach Bestaetigung des neues
+  * Passwortes und ruft change_password4().
+  * \param[in] str Spielereingabe des neuen Passwortes
+  * \return 1 bei Erfolg, 0 sonst.
+  * \sa change_password(), change_password2(), change_password4()
+  */
+static int change_password3( string str )
+{
+    write( "\n" );
+
+    if ( !str || str == "" ){
+        write( "Abgebrochen !\n" );
+        passwold = passw = 0;
+        return 1;
+    }
+
+    if ( passwold == str ){
+        write( "Das war Dein altes Passwort.\n" );
+        input_to( "change_password3", INPUT_NOECHO|INPUT_PROMPT,
+            "Bitte das NEUE Passwort eingeben (zum Abbruch Return druecken): ");
+        return 1;
+    }
+
+    if ( !MASTER->good_password( str, getuid(ME) ) ){
+        input_to( "change_password3", INPUT_NOECHO|INPUT_PROMPT,
+            "Bitte das NEUE Passwort eingeben: ");
+        return 1;
+    }
+
+    passw = str;
+    input_to( "change_password4", INPUT_NOECHO|INPUT_PROMPT,
+        "Bitte nochmal: ");
+    return 1;
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+  * Prueft neues Passwort und setzt neues Passwort.
+  * \param[in] str Spielereingabe des neuen Passwortes
+  * \return 1 bei Erfolg, 0 sonst.
+  * \sa change_password(), change_password2(), change_password3()
+  */
+static int change_password4( string str )
+{
+    write( "\n" );
+
+    if ( !str || str != passw ){
+        write( "Das war verschieden! Passwort NICHT geaendert.\n" );
+        passwold = passw = 0;
+        return 1;
+    }
+
+    if ( MASTER->update_password( passwold, passw ) )
+        write( "Passwort geaendert.\n" );
+    else
+        write( "Hat nicht geklappt!\n" );
+
+    passwold = passw = 0;
+    return 1;
+}
+
+
+/*
+ *-----------------------------------------------------------------
+ * Rueckmeldungen von Spielern an Magier
+ *-----------------------------------------------------------------
+ */
+static int fehlerhilfe(string str) {
+  write("Welche Art von Fehler moechtest Du denn melden?\n"
+      "Fehlfunktionen    ->  bug\n"
+      "Ideen/Anregungen  ->  idee\n"
+      "Tippfehler/Typos  ->  typo\n"
+      "fehlende Details  ->  detail\n");
+
+  return 1;
+}
+
+/** Setzt eine Fehlermeldung an Magier ab (Spielerkommando).
+  * Fragt nach der Fehlermeldung und liest sie via bug2() ein, fall der
+  * Spieler kein Argument angeben hat.
+  * \param[in] str optionale Spielereingabe der Fehlerbeschreibung
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see bug2(string)
+  */
+static int bug(string str) {
+  if (!(str=_unparsed_args())) {
+    write( "Wie sieht der Fehler denn aus?\n" );
+    input_to("bug2", INPUT_PROMPT, "]");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("BUGS",str);
+  return 1;
+}
+
+/** Setzt eine Fehlermeldung an Magier ab (Spielerkommando).
+  * Lies Fehlerbeschreibung ein und speichert sie ab.
+  * \param[in] str Spielereingabe der Fehlerbeschreibung.
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see bug(string)
+  */
+static int bug2(string str) {
+  if (!str || str == "") {
+    write("Bug abgebrochen...\n");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("BUGS",str);
+  return 1;
+}
+
+/** Setzt eine Typomeldung an Magier ab (Spielerkommando).
+  * Fragt nach der Typomeldung und liest sie via typo2() ein, fall der
+  * Spieler kein Argument angeben hat.
+  * \param[in] str optionale Spielereingabe der Typobeschreibung
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see typo2(string)
+  */
+static int typo(string str) {
+  if (!(str=_unparsed_args())) {
+    write( "Wo ist denn der Tippfehler?\n" );
+    input_to("typo2", INPUT_PROMPT, "]");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("TYPO",str);
+  return 1;
+}
+
+/** Setzt eine Fehlermeldung an Magier ab (Spielerkommando).
+  * Liest die Typobeschreibung ein und speichert sie.
+  * \param[in] str Spielereingabe der Typobeschreibung
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see typo(string)
+  */
+static int typo2(string str) {
+  if (!str || str == "") {
+    write("Typo abgebrochen...\n");
+    return 1;
+  }
+  smart_log("TYPO",str);
+  write("Vielen Dank fuer die Hilfe.\n");
+  return 1;
+}
+
+/** Setzt eine Idee an Magier ab (Spielerkommando).
+  * Fragt nach der Idee und liest sie via idee2() ein, falls der
+  * Spieler kein Argument angeben hat.
+  * \param[in] str optionale Spielereingabe der Idee
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see idea2(string)
+  */
+static int idea(string str) {
+  if (!(str=_unparsed_args())) {
+    write( "Was fuer eine Idee hast Du denn?\n" );
+    input_to("idea2",INPUT_PROMPT, "]");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("IDEA",str);
+  return 1;
+}
+
+/** Setzt eine Idee an Magier ab (Spielerkommando).
+  * Liest die Idee ein und speichert sie.
+  * \param[in] str Spielereingabe der Idee
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see idea(string)
+  */
+static int idea2(string str) {
+  if (!str || str == "") {
+    write("Idee abgebrochen...\n");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("IDEA",str);
+  return 1;
+}
+
+/** Setzt ein fehlendes Detail an Magier ab (Spielerkommando).
+  * Fragt nach dem Detail und liest es via idee2() ein, falls der
+  * Spieler kein Argument angeben hat.
+  * \param[in] str optionale Spielereingabe des fehlenden Details
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see md2(string)
+  */
+static int md(string str) {
+  if (!(str=_unparsed_args())) {
+    write( "Fuer welches Detail fehlt denn die Beschreibung?\n" );
+    input_to("md2",INPUT_PROMPT, "]");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("DETAILS",str);
+  return 1;
+}
+
+/** Setzt ein fehlendes Detail an Magier ab (Spielerkommando).
+  * Liest das Detail ein und speichert es.
+  * \param[in] str Spielereingabe des fehlenden Details.
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see md(string)
+  */
+static int md2(string str) {
+  if (!str || str == "") {
+    write("Details abgebrochen...\n");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("DETAILS",str);
+  return 1;
+}
+
+/** Loggt eine Spielermeldung an Magier.
+  * Loggt die Spielermeldung in das passende File unter /log/report/ oder im
+  * vom Magier gewuenschten File. Hierbei werden Fehler, Ideen, MDs und Typos
+  * in getrennte Files sortiert.
+  * \param[in] str Spielermeldung
+  * \param[in] myname Art der Spielermeldung (DETAILS, BUG, TYPO, MD)
+  * @see md(string), idea(string), bug(string), typo(string)
+  */
+void smart_log(string myname, string str)
+{
+  string obnam;
+  object obj;
+
+  string *tmp = explode(str, ":");
+  if (sizeof(tmp) > 1) {
+    obnam = lower_case(trim(tmp[0]));
+    obj = present(obnam, environment()) || present(obnam);
+    if (!obj) {
+      obj = environment(this_object());
+    }
+    else // nur hier Teil vor dem : wegschneiden
+      str = trim(implode(tmp[1..],":"));
+  }
+  else {
+    obj = QueryProp(P_REFERENCE_OBJECT);
+    if (!obj || !present(obj))
+      obj = environment(this_interactive());
+  }
+
+  mapping err = ([ F_PROG: "unbekannt",
+           F_LINE: 0,
+           F_MSG: str,
+           F_OBJ: obj
+         ]);
+
+  string desc="etwas unbekanntes";
+  switch(myname) {
+    case "BUGS":
+      desc="einen Fehler";
+      err[F_TYPE]=T_REPORTED_ERR;
+      break;
+    case "DETAILS":
+      desc="ein fehlendes Detail";
+      err[F_TYPE]=T_REPORTED_MD;
+      break;
+    case "IDEA":
+      desc="eine Idee";
+      err[F_TYPE]=T_REPORTED_IDEA;
+      break;
+    case "TYPO":
+      desc="einen Typo";
+      err[F_TYPE]=T_REPORTED_TYPO;
+      break;
+  }
+
+  // Eintragung in die Fehler-DB
+  string hashkey = (string)ERRORD->LogReportedError(err);
+
+  // ggf. will das Objekte mit noch irgendwas anfangen.
+  obj->SmartLog(0, myname, str, strftime("%d. %b %Y"));
+
+  tell_object(this_object(), break_string( sprintf(
+    "Du hast an %s erfolgreich %s abgesetzt.\n"
+    "Die ID der abgesetzten Meldung lautet: %s\n",
+    (obj->IsRoom() ? "diesem Raum" : obj->name(WEM,1)),desc,
+    hashkey||"N/A"),78,BS_LEAVE_MY_LFS));
+}
+
+/** Speichert den Spieler und loggt ihn aus (Spielerkommando 'ende').
+  * Der Spieler wird vollstaendig ausgeloggt, d.h. das Spielerobjekt
+  * zerstoert.
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see disconnect()
+  */
+int quit()
+{
+  int arg;
+  SetProp(P_LAST_QUIT,time());
+  catch(RemoveChannels();publish);
+  if(!QueryGuest())
+  {
+    save_me(0);
+    tell_object(ME,"Speichere "+QueryProp(P_NAME)+".\n");
+  }
+
+  if (interactive(ME))
+    call_notify_player_change(0);
+
+  remove_living_name();
+  // EVT_LIB_LOGOUT wird in remove() getriggert.
+  if(catch(remove();publish)) destruct(ME);
+  return 1;
+}
+
+/** Wrapper im quit() herum, verhindert 'ende', falls Spieler kaempft.
+  * \return 0 oder Rueckgabewert von quit()
+  * @see quit()
+  */
+static int new_quit() {
+  notify_fail("Du bist in Gedanken noch bei Deinem letzten Kampf.\n"+
+              "Warte noch etwas bevor Du das Spiel verlaesst,\n"+
+              "damit Du so nicht in RL weitermachst...\n");
+  if (time()-Query(P_LAST_COMBAT_TIME)<120 && !IS_LEARNING(ME))
+    return 0;
+  return quit();
+}
+
+/** Gibt die Infos ueber den Char an den Spieler aus (Spielerkommando 'info').
+  * \param[in] arg Wenn arg=="short", wird eine Kurzuebersicht ausgegeben.
+  * \return 1
+  * @see short_score()
+  */
+static int score(string arg) {
+  string tmp, gender;
+  int i,sz,val;
+  mixed ind;
+  object *enem1, *enem2, *inv;
+
+  if (QueryProp(P_GHOST)) {
+    write("Im ewigen Leben gibt es keine Punkte.\n");
+    return 1;
+  }
+
+  int plev = LEPMASTER->QueryLevel();
+ 
+  switch(tmp = QueryProp(P_GENDER)) {
+  case MALE: gender = "maennlich"; break;
+  case FEMALE: gender = "weiblich"; break;
+  case NEUTER: gender = "neutral"; break;
+  default: gender = lower_case(tmp);
+  }
+
+  ind = m_indices(QueryProp(P_ATTRIBUTES));
+  tmp = "";
+  foreach(string index: ind) {
+    string aname;
+    switch (index) {
+      case "int": aname = "Intelligenz"; break;
+      case "con": aname = "Ausdauer"; break;
+      case "dex": aname = "Geschicklichkeit"; break;
+      case "str": aname = "Kraft"; break;
+      default:
+        if(stringp(index)) aname = capitalize(index);
+        else aname = "Unbekannt";
+    }
+    aname = sprintf("%-18'.'s %2.2d", aname+" ", QueryRealAttribute(index));
+    if((val = QueryAttributeOffset(index)))
+      aname += sprintf(" (%s%d)", (val>=0?"+":""), val);
+    tmp += aname + "\n";
+  }
+
+  printf("- %-'-'68s\n",
+         TeamPrefix()+capitalize(implode(explode(short()||"","\n"),""))+" ");
+  if(arg!="short") {
+    printf("Rasse ............ %-' '18s Abenteuer ........ %d %s\n",
+           QueryProp(P_RACE), QueryProp(P_QP),
+           (val = QM->QueryTotalQP()) == QueryProp(P_QP) ? "" : "("+val+")");
+    printf("Geschlecht ....... %-' '18s Groesse .......... %d cm\n",
+           gender, QueryProp(P_SIZE));
+    printf("Stufe ............ %-3.3d %-' '14s Gewicht .......... %d kg\n",
+           QueryProp(P_LEVEL), (QueryProp(P_LEVEL) < plev ? "("+plev+")" : ""),
+           QueryProp(P_WEIGHT) / 1000);
+    printf("Gilde ............ %-' '18s Gildenstufe ...... %d\n",
+         capitalize(QueryProp(P_GUILD)), QueryProp(P_GUILD_LEVEL));
+  }
+  printf("Erfahrung ........ %-' '18s Charakter ........ %-s\n\n",
+         QueryProp(P_XP)+ " Punkte", al_to_title(QueryProp(P_ALIGN)));
+  printf("%#-76.2s\n\n", tmp);
+  printf("Gesundheit ....... %-3.3d %-' '14s Gift ............. %s\n",
+         QueryProp(P_HP),
+         (QueryProp(P_HP) == (val = QueryProp(P_MAX_HP)) ? "" : "("+val+")"),
+         ((val = QueryProp(P_POISON)) ?
+          (val < 4 ? "leicht" : "gefaehrlich") : "gesund"));
+  printf("Konzentration .... %-3.3d %-' '14s Vorsicht ......... %s\n",
+         QueryProp(P_SP),
+         (QueryProp(P_SP) == (val = QueryProp(P_MAX_SP)) ? "" : "("+val+")"),
+         ((ind = QueryProp(P_WIMPY)) ? ""+ind : "mutig"));
+  printf("Todesfolgen....... %-' '18s %s\n",
+         ((val = death_suffering()) ? ""+((val+9)/10) : "kein Malus"),
+         (QueryProp(P_WIMPY) && ind=QueryProp(P_WIMPY_DIRECTION))
+         ? sprintf("Fluchtrichtung ... %O", ind) : "");
+  printf("%s",
+         (time()-Query(P_LAST_COMBAT_TIME)<120 && !IS_LEARNING(ME)) ?
+         "Spiel verlassen .. nicht moeglich\n" : ""
+         );
+
+  if(arg!="short") {
+    write(break_string(Forschung(), 70));
+    if(ind=QueryProp(P_AWAY))
+      printf("Du bist nicht ansprechbar: %O\n",ind);
+  }
+
+  if(sizeof(enem1=((mixed)QueryEnemies())[0])) {
+    enem2=({});
+    inv=all_inventory(environment(ME));
+    foreach(object en: enem1) {
+      if (member(inv,en)==-1) // Ist unser Feind und ist nicht hier
+        enem2+=({en});
+    }
+    if(sizeof(enem2))
+    {
+      write(break_string(
+            "Du verfolgst " + CountUp(map_objects(enem2, "name", WEN))+".",
+            78));
+    }
+  }
+  if(arg!="short") show_age();
+  printf("%-'-'70s\n", "");
+  return 1;
+}
+
+/** Gibt eine kuerzere Info ueber den Char aus (Spielerkommando punkte|score).
+    Ruft score("short").
+  * \param[in] arg UNUSED
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see score(string), very_short_score()
+  */
+static int short_score(string arg) {
+  return score("short");
+}
+
+/** Gibt eine Miniinfo ueber LP / KP aus (Spielerkommando: kurzinfo)
+  * \return 1
+  * @see score(string), short_score(string)
+  */
+static int very_short_score(string arg) {
+  int    lp,mlp,xlp,kp,mkp,xkp;
+  string bar;
+
+  lp=QueryProp(P_HP); mlp=QueryProp(P_MAX_HP);
+  kp=QueryProp(P_SP); mkp=QueryProp(P_MAX_SP);
+  if (mlp)
+    xlp=(lp*40/mlp);
+  if (mkp)
+    xkp=(kp*40/mkp);
+  bar="  .    .    .    .    .    .    .    .  ";
+  if (QueryProp(P_NO_ASCII_ART) || arg == "-k")
+    printf("Gesundheit: %3.3d (%3.3d), Konzentration: %3.3d (%3.3d)\n",
+           lp, mlp, kp, mkp);
+  else
+    printf("Gesundheit:    0 |%'#'40.40s| %3.3d%s\n"+
+           "Konzentration: 0 |%'#'40.40s| %3.3d%s\n",
+           (xlp<0?bar:bar[xlp..]),lp,(lp==mlp?"":sprintf(" (%d)",mlp)),
+           (xkp<0?bar:bar[xkp..]),kp,(kp==mkp?"":sprintf(" (%d)",mkp))
+           );
+  return 1;
+}
+
+/** Gibt eine Manpage/Hilfeseite an den Spieler aus.
+    Beruecksichtigt hierbei die Synonymliste aus dir/.synonym, um die richtige
+    Manpage auszugeben.
+  * \param[in] dir Verzeichnis der gewuenschten Manpage
+  * \param[in] page Name der gewuenschten Manpage
+  * \return String der gewuenschten Manpage
+  */
+static string getmanpage(string dir, string page)
+{
+  string text, *syn;
+  int i;
+
+  if (dir[<1] != '/')
+    dir += "/";
+
+  if ((text=read_file(dir+page)) && sizeof(text))
+    return text;
+
+  if (text = read_file(dir+".synonym")) {
+    syn = regexplode(text, "([ \t][ \t]*|\n)");
+    if ((i=member(syn, page))!=-1)
+      return read_file(dir+syn[i+2]);
+  }
+  return 0;
+}
+
+/** Gibt eine Hilfeseite an den Spieler aus (Spielerkommando hilfe|man).
+  * Die Hilfeseite wird in div. Verzeichnissen gesucht (je nach Gilde,
+  * Magier-/Spielerstatus).
+  * \param[in] str Name der gewuenschten Hilfeseite.
+  * \return 1
+  */
+static int help(string str) {
+  string verb, rest, text, gilde;
+  mixed found;
+
+  found=0;
+  text = "";
+  if (str) {
+    str = implode( explode(str, ".." ), "");
+
+    if ( sscanf( str, "gilde %s %s", gilde, rest)==2)
+      str=rest;
+    else
+      gilde=QueryProp(P_GUILD);
+    if (!gilde) gilde="abenteurer";
+
+    if ( sscanf( str, "%s %s",verb,rest )==2 ) str = verb;
+
+    if ((IS_LEARNER(PL)) ) {
+      if (rest = getmanpage("/doc/wiz/",str)) {
+        found = 1;
+        text += rest;
+      }
+      else if (rest = getmanpage("/doc/mcmd/", str)) {
+        found = 1;
+        text += rest;
+      }
+    }
+
+    if ((IS_SEER(PL)) /*&& !found*/ ) {
+      if (rest = getmanpage("/doc/scmd/",str)) {
+        if (found)
+          text += "\n--------------------\n";
+        found = 1;
+        text += rest;
+      }
+    }
+
+    if (rest = getmanpage("/doc/g."+gilde+"/",str)) {
+      if (found)
+        text += "\n--------------------\n";
+      found = 1;
+      text += rest;
+    } else {
+      if (rest = getmanpage("/doc/help/",str)) {
+        if (found)
+          text += "\n--------------------\n";
+        found = 1;
+        text += rest;
+      }
+      else if (rest = getmanpage("/doc/pcmd/",str)) {
+        if (found)
+          text += "\n--------------------\n";
+        found = 1;
+        text += rest;
+      }
+      else if (rest = getmanpage("/doc/REGELN/",str)) {
+        if (found)
+          text += "\n--------------------\n";
+        found = 1;
+        text += rest;
+      }
+    }
+
+    if (!found)
+      text = "Dazu ist keine Hilfe verfuegbar.\n";
+
+    More(text,0);
+    return 1;
+  }
+  if (IS_LEARNER(PL))
+    text = read_file("/doc/hilfe.magier");
+  else if (IS_SEER(PL))
+    text = read_file("/doc/hilfe.seher");
+
+  More(text + read_file("/doc/hilfe.spieler"), 0);
+  return 1;
+}
+
+/** Ermittelt angebene Optionen fuer das Spielerkommando 'wer'.
+  * \param[in] str vom Spieler spezifizierter String von Filteroptionen: -k,
+  * -v, -a, -s (+ Langformen).
+  * \return Array von Spieleroptionen als veroderte Int-Flags und Rest der
+  * Spielereingabe ohne die Optionen.
+  */
+static mixed filter_who_options(string str)
+{
+  string* opt, *ans;
+  int i,len,res;
+
+  opt = explode(str," "); len=sizeof(opt);
+  ans = ({});
+  res = 0;
+  for(i=0;i<len;i++)
+    switch(opt[i]){
+      case "-k":
+      case "-kurz":
+      res |= WHO_SHORT; break;
+      case "-v":
+      case "-vertikal":
+      res |= WHO_VERTICAL; break;
+      case "-alphabetisch":
+      case "-a":
+      case "-alpha":
+      res |= WHO_ALPHA; break;
+      case "-s":
+      case "-spieler":
+      res |= WHO_PLAYER_VIEW; break;
+      default:
+      return ({ res, implode(opt[i..]," ") });
+    }
+  return ({ res, 0 });
+
+}
+
+/** Spielerkommando 'wer', fragt /obj/werliste ab.
+  * \param[in] str Spielereingabe mit Optionen fuer wer.
+  * \return 1
+  */
+static int who(string str) {
+  int i,shrt;
+  string ret;
+  mixed ans;
+
+  if ((str=_unparsed_args())&&str[0..0]!="-") {
+    ans = filter_who_options(str);
+    shrt = ans[0];
+    str = ans[1];
+    if (!shrt) {
+      if (ret=INETD->_send_udp(str,
+                              ([ REQUEST: "who", SENDER: getuid(ME) ]), 1 ))
+        write(ret);
+      else
+        write("Anfrage abgeschickt.\n");
+      return 1;
+    }
+  }
+  if (str) i=(member(str,'o')>0); else i=0;
+  if (sizeof(str)>1 && str[0] == '-') str = str[1..1];
+  More(implode( "/obj/werliste"->QueryWhoListe(
+    IS_LEARNER(ME) && QueryProp(P_WANTS_TO_LEARN),shrt,0,str,i),"\n"),0);
+  return 1;
+}
+
+/** Spielerkommando 'kwer', fragt /obj/werliste ab.
+  * \param[in] str Spielereingabe mit Optionen fuer wer.
+  * \return 1
+  */
+static int kwho(string str)
+{
+  int shrt;
+  mixed res;
+
+  if(str) {
+    res = filter_who_options(str);
+    shrt = res[0];
+    str = res[1];
+  }
+  More(implode( "/obj/werliste"->QueryWhoListe(
+      IS_LEARNER(ME) && QueryProp(P_WANTS_TO_LEARN), shrt|WHO_SHORT ,0,str),
+    "\n")+"\n\n",0);
+  return 1;
+}
+
+/** Spielerkommando 'kkwer', gibt eine einfache Liste der Anwesenden aus.
+    Filtert unsichtbare Spieler aus, falls SPielerobjekt kein Magier ist.
+  * \param[in] str Spielereingabe mit Optionen fuer wer.
+  * \return 1
+  */
+static varargs int kkwho(string str) {
+  object *obs;
+  string *namen;
+
+  obs=filter_users(str);
+  namen=({});
+  if (IS_LEARNER(this_player())) {
+    foreach(object ob: obs) {
+      if (environment(ob))
+        namen+=({capitalize(geteuid(ob))});
+    }
+  } 
+  else {
+    foreach(object ob: obs) {
+      if (!ob->QueryProp(P_INVIS) && environment(ob))
+        namen+=({capitalize(geteuid(ob))});
+    }
+  }
+  if (sizeof(namen))
+    write(break_string(CountUp(sort_array(namen,#'>),", ", ", ")+".",75));
+  else
+    write("Keine passenden Spieler gefunden.\n");
+  
+  return 1;
+}
+
+/** Spielerkommando 'toete'.
+  * Prueft auf Geist, Gast, toten HC-Spieler, Waffe/freie Hand. Versucht einen
+  * Gegner zu finden und ruft dann Kill(string).
+  * \param[in] str Spielereingabe
+  * \return 1 oder 0, falls kein potentieller Gegner bei 'toete alle' gefunden
+  * wird.
+  */
+static int kill(string str) {
+  object eob,wob;
+
+  if (QueryProp(P_GHOST))
+  {
+    write("Das kannst Du in Deinem immateriellen Zustand nicht.\n");
+    return 1;
+  }
+  
+  if(hc_play>1)
+  {
+    write("DAS HAST DU HINTER DIR.\n");
+    return 1;
+  }
+  
+  if (QueryGuest())
+  {
+    write("Du bist doch nur Gast hier.\n");
+    return 1;
+  }
+  if (!str || str == "") {
+    write("WEN willst Du toeten?\n");
+    return 1;
+  }
+  if( !QueryProp(P_WEAPON) && QueryProp(P_FREE_HANDS)==0 ) {
+    write(
+      "Dazu solltest Du eine Waffe gezueckt oder eine Hand frei haben.\n");
+    return 1;
+  }
+  str=lower_case(str);
+  if (str=="alle") {
+    object livs;
+    livs=filter(all_inventory(environment(PL)),
+        function int (object ob) {
+            if (living(ob) && !query_once_interactive(ob)
+                  && !ob->QueryProp(P_INVIS)
+                  && !ob->QueryProp(P_NO_GLOBAL_ATTACK)
+                  && !ob->QueryProp(P_FRIEND))
+            {
+                Kill(ob);
+                return 1;
+            }
+            return 0;
+        } );
+    // wenn Gegner gefunden, raus, ansonsten kommt die Fehlermeldung unten.
+    if (sizeof(livs)) return 1;
+  }
+  else {
+    int i=1;
+    while(objectp(eob = present(str,i++,environment(PL)))) {
+      if (living(eob) && !eob->QueryProp(P_INVIS))
+        break;
+      else
+        eob=0;
+    }
+  }
+  if (!objectp(eob)) {
+    // per write und return 1 ist hier mal ok, weil dies Kommando im Spieler
+    // eh zuletzt in der Kommandokette ausgefuehrt wird und per return 0 eh
+    // kein anderes mehr zum Zug kommt.
+    write("Du siehst hier kein derartiges Wesen!\n");
+    return 1;
+  }
+  else if (eob == PL) {
+    write("Selbstmord ist keine Loesung!\n");
+    return 1;
+  }
+
+  /* Kill him */
+  Kill(eob);
+  return 1;
+}
+
+/** Spielerkommando 'stop'.
+  * Loescht die Gegnerliste, sofern man nicht InFight() ist.
+  * \param[in] str Spielereingabe, wird ignoriert.
+  * \return 1
+  */
+static int stop( string str )
+{
+    if ( InFight() ){
+        write( "Das geht nicht mitten im Kampf.\n" );
+        return 1;
+    }
+
+    if ( !str ){
+        StopHuntingMode();
+        write( "Ok.\n" );
+        return 1;
+    }
+
+    if ( !StopHuntID(str) )
+        write( "So jemanden verfolgst Du nicht!\n" );
+
+    return 1;
+}
+
+/** Spielerkommando fuers emoten ':'.
+  * \param[in] str Spielereingabe
+  * \param[in] genitiv Genetivflag
+  * \return 1 oder 0, falls Spieler nicht emoten kann (kein CAN_EMOTE Flag in
+  * P_CAN_FLAGS).
+  */
+int emote(string str,int genitiv)
+{
+  string *commands,message,verb;
+  object living;
+  int i,size;
+
+  if (!(Query(P_CAN_FLAGS)&CAN_EMOTE)) return 0;
+  if (query_verb()[0]==';') genitiv=1;
+  if (query_verb()[0]==':'||query_verb()[0]==';')
+    verb=query_verb()[1..]+" ";
+  else
+    verb="";
+  str=this_player()->_unparsed_args();
+  commands=explode(verb+(str||""),"#");
+  message=break_string((IS_SEER(ME) ? "" : ">")
+                       +capitalize(genitiv ? name(WESSEN) :
+                                   name())
+                       +" "+commands[0],78);
+  size=sizeof(commands);
+  if(size>=3)
+  {
+    living=find_living(lower_case(commands[1]));
+    if(!living || environment(living)!=environment() ||
+       (living->QueryProp(P_INVIS)) && !IS_LEARNER(ME))
+    {
+      write(capitalize(commands[1])+" sehe ich hier nicht!\n");
+      return 1;
+    }
+    if(living!=this_object())
+      tell_object(living,break_string((IS_SEER(this_player()) ? "" : ">")
+                                    +capitalize(genitiv ?
+                                                this_player()->name(WESSEN) :
+                                                this_player()->name())
+                                    +" "+commands[2],78));
+  }
+  if(size>=4)
+    write(break_string(commands[3],78));
+  else
+    write(message);
+  tell_room(environment(),message,({this_object(),living}));
+  return 1;
+}
+
+/** Spielerkommando fuers remoten 'r:'.
+  * \param[in] str Spielereingabe
+  * \param[in] flag Genetivflag
+  * \return 1 oder 0, falls Spieler nicht emoten kann (kein CAN_REMOTE Flag in
+  * P_CAN_FLAGS).
+  */
+static int remote(string str, int flag)
+{
+  int m;
+  string tmp, dest;
+  string *exstr;
+  object destpl;
+
+  if ( !(Query(P_CAN_FLAGS) & CAN_REMOTE) )
+      return 0;
+
+  if ( !(str=_unparsed_args()) ||
+       sizeof( (exstr=explode(str," ")) - ({""}) ) <= 1 ){
+      write("Was willst Du zu wem `emoten`?\n");
+      return 1;
+  }
+
+  dest = lower_case(exstr[0]);
+
+  if( !(destpl=find_player( dest ) ) ||
+      (destpl->QueryProp(P_INVIS) && !IS_LEARNER(ME)) ){
+    write("Einen solchen Spieler gibt es derzeit nicht.\n");
+    return 1;
+  }
+
+  tmp = implode( exstr[1..], " " );
+
+  tmp = regreplace( tmp, "(^|[^#])#($|[^#])", "\\1aus der Ferne\\2", 1 );
+  tmp = regreplace( tmp, "(^|[^\\^])\\^($|[^\\^])", "\\1in der Ferne\\2", 1 );
+  tmp = regreplace( tmp, "##", "#", 1 );
+  tmp = regreplace( tmp, "\\^\\^", "^", 1 );
+
+  if ( strstr( tmp, "aus der Ferne" ) == -1
+       && strstr( tmp, "in der Ferne" ) == -1 )
+      tmp += " aus der Ferne";
+
+  if ( QueryProp(P_INVIS) && IS_LEARNER(destpl) ){
+      str = "(" + capitalize(getuid(ME));
+      if ( flag )
+          str += member( "sxz", str[<1] ) == -1 ? "s" : "'";
+      str += ")";
+  }
+  else
+      str = (flag ? capitalize(name(WESSEN)) : capitalize(name(WER)));
+
+  str += " " + tmp + (member( ".?!", tmp[<1] ) == -1 ? "." : "") + "\n";
+
+  m = destpl->ReceiveMsg(str, MT_COMM|MT_FAR, MA_EMOTE,0, ME);
+  switch(m)
+  {
+    case MSG_DELIVERED:
+      _recv(destpl, capitalize(destpl->name()) + "->" + str, MSGFLAG_REMOTE);
+      break;
+    case MSG_BUFFERED:
+      write( capitalize(destpl->name(WER) + " ist gerade beschaeftigt.\n") );
+      break;
+    case MSG_IGNORED:
+      write( capitalize(destpl->name(WER) + " ignoriert Dich.\n") );
+      break;
+    case MSG_VERB_IGN:
+    case MSG_MUD_IGN:
+      write( capitalize(destpl->name(WER) + " ignoriert Deine Meldung.\n") );
+      break;
+    default:
+      write( capitalize(destpl->name(WER) + " kann Dich gerade nicht "
+            "wahrnehmen.\n") );
+  }
+  return 1;
+}
+
+/** Spielerkommando fuers emoten im Genitiv ';'.
+  * Ruft emote(string,int) auf.
+  */
+static int gemote(string str)
+{
+  return emote(str, 1);
+}
+
+/** Spielerkommando fuers remoten im Genitiv 'r;'.
+  * Ruft remote(string, int) auf.
+  */
+static int gremote(string str)
+{
+  return remote(str, 1);
+}
+
+static void load_auto_objects(mapping map_ldfied);
+
+private void InitPlayer();
+private void InitPlayer2();
+private void InitPlayer3();
+
+/** Gibt eine Zufallszahl um P_AVERAGE_SIZE herum zurueck.
+  * \return Zufaellige Groesse.
+  */
+private int RandomSize()
+{
+  return (100+random(13)-random(13)+random(13)-random(13))*
+    (QueryProp(P_AVERAGE_SIZE)||170)/100;
+}
+
+/** Setzt bestimmte Props im Spieler, falls diese nicht gesetzt sind oder
+  * loescht obsolete Props. Repariert bestimmte Datenstrukturen im Spieler.
+  * Wird von start_player() nach Laden des Savefiles gerufen.
+  * Momentan wird z.B. die Groesse gesetzt, falls sie bisher 0 ist und die
+  * Skills des Spielers initialisiert bzw. repariert oder auf die aktuellste
+  * Version des Skillsystems
+  * Ruft ggf. InitSkills() und FixSkills().
+  * @param[in] newflag Gibt an, ob es ein neuerstellter Spieler ist.
+  * @sa start_player(), InitSkills(), FixSkills()
+  */
+private void updates_after_restore(int newflag) {
+ 
+  // Seher duerfen die Fluchtrichtung uebermitteln lassen.
+  // Eigentlich koennte es Merlin machen. Dummerweise gibt es ja auch alte
+  // Seher und dann kann es gleiche fuer alle hier gemacht werden. (Ob der
+  // Code jemals rauskann?)
+  //TODO: Irgendwann alle Seher korrigieren und Code nach Merlin schieben...
+  if (IS_SEER(ME))
+    SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS) | CAN_REPORT_WIMPY_DIR);
+
+  // ggf. Invis-Eigenschaft aus dem Loginobjekt abrufen (Invislogin), koennte
+  // ja anders als aus Savefile sein. Gesetztes P_INVIS aus diesem aber
+  // beibehalten.
+  if (IS_LEARNER(ME) && !QueryProp(P_INVIS)
+//      && load_name(previous_object()) == "/secure/login"
+      )
+  {
+    SetProp(P_INVIS, previous_object()->query_invis());
+    if (QueryProp(P_INVIS))
+      tell_object(ME, "DU BIST UNSICHTBAR!\n" );
+  }
+  "*"::updates_after_restore(newflag);
+
+  attributes::UpdateAttributes();
+
+  int size=Query(P_SIZE);
+  if (!size) size=RandomSize();
+  while(size==QueryProp(P_AVERAGE_SIZE))
+    size=RandomSize();
+  Set(P_SIZE,size);
+
+  // Prop wird nicht mehr genutzt. TODO: irgendwann entfernen.
+  Set(P_SECOND_LIST, SAVE, F_MODE_AD);
+  Set(P_SECOND_LIST, 0, F_VALUE);
+}
+
+
+/** Setzt den HC-Modus.
+  */
+varargs nomask void set_hc_play(string str,int val)
+{
+   string str1;
+
+    str1 = explode( object_name(previous_object()), "#" )[0];
+    
+    if ( str1 != "/secure/login" &&
+         previous_object()!=this_object() &&
+         extern_call() &&
+         (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
+          capitalize(geteuid(ME)) != str ||
+          geteuid(ME) != geteuid(previous_object())) ){
+        write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
+        return;
+    }
+    
+    hc_play=val;
+}
+
+/** gibt den HC-Modus zurueck.
+  */
+nomask int query_hc_play()
+{
+  return hc_play;
+}
+
+/** Initialisiert und aktiviert das Spielerobjekt.
+  * Kann nur von /secure/login oder /secure/master gerufen werden.
+  * Startet Telnet Negotiation, laedt Savefile, setzt einige Props. 
+  * Ruft updates_after_restore(), um div. Daten zu aktualisieren/reparieren.
+  * Ruft create() aus /std/player/potion.c.
+  * Bei neuem Spieler wird der entsprechende Event ausgeloest die Attribute
+  * auf die Startwerte gesetzt.
+  * Prueft Zweitiemarkierungen.
+  * Ruft InitPlayer().
+  * @param[in] str Name des Charakters, der geladen werden soll.
+  * @param[in] ip textuelle Repraesentation der IP-Adresse des Spielers.
+  * @return 1 bei Erfolg, 0 sonst.
+  * @todo Div. Reparaturen/Updates nach updates_after_restore() auslagern.
+  */
+varargs nomask int start_player( string str, string ip )
+{
+    mixed second;
+    int newflag;  /* could player be restored? */
+    string str1;
+
+    call_out( "disconnect", 600 );
+
+    str1 = explode( object_name(previous_object()), "#" )[0];
+
+    if ( str1 != "/secure/login" &&
+         str1 != "/secure/master" &&
+         (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
+          capitalize(geteuid(ME)) != str ||
+          geteuid(ME) != geteuid(previous_object())) ){
+        write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
+        destruct(ME);
+        return 0;
+    }
+
+    /* try to restore player. If it doesn't exist, set the new flag */
+    newflag = !restore_object( "/" + SAVEPATH + lower_case(str)[0..0] + "/"
+                               +lower_case(str) );
+
+    updates_after_restore(newflag);
+
+   if ( query_once_interactive(ME) )
+    {
+      // Telnet-Negotiations durchfuehren, aber nur die grundlegenden aus
+      // telnetneg. Alle anderen sollten erst spaeter, nach vollstaendiger
+      // Initialisierung gemacht werden.
+        telnetneg::startup_telnet_negs();
+        modify_prompt();
+        Set( P_LAST_LOGIN, time() );
+    }
+
+    Set( P_WANTS_TO_LEARN, 1 ); // 1 sollte der default sein !!!
+    Set( P_WANTS_TO_LEARN, PROTECTED, F_MODE_AS );
+    // Eingefuegt 18.11.99, kann nach einem Jahr wieder raus:
+    Set( P_TESTPLAYER, PROTECTED, F_MODE_AS );
+
+    if ( IS_LEARNER(ME) )
+        SetProp( P_CAN_FLAGS, QueryProp(P_CAN_FLAGS)|CAN_REMOTE );
+
+    Set( P_NAME, str );
+    Set( P_NAME, SECURED, F_MODE_AS );
+
+    if ( !QueryProp(P_NEEDED_QP) )
+        SetProp( P_NEEDED_QP, REQ_QP );
+
+    Set( P_NEEDED_QP, NOSETMETHOD, F_SET_METHOD );
+    Set( P_NEEDED_QP, SAVE|SECURED, F_MODE_AS );
+
+    /* autosave the player after 500 heartbeats */
+    time_to_save = age + 500;
+    potion::create(); /* DO IT HERE AFTER THE RESTORE !! */
+
+    AddId( getuid() );
+    SetProp( P_AC, 0 );
+    SetProp( P_WEAPON, 0 );
+
+    /* Set some things which wont be set when all is OK */
+    SetProp( P_MAX_HP, (QueryAttribute(A_CON) * 8 + 42 ));
+    SetProp( P_MAX_SP, (QueryAttribute(A_INT) * 8 + 42 ));
+
+    catch( bb = "/secure/bbmaster"->query_bb() );
+
+    /* If this is a new character, we call the adventurers guild to get
+     * our first title !
+     */
+    if ( newflag ) {
+        if ( QueryGuest())
+            SetProp( P_TITLE, "ueberkommt das "MUDNAME" ..." );
+
+        Set( P_LEVEL, -1 );
+        SetProp( P_ATTRIBUTES, ([ A_STR:1, A_CON:1, A_INT:1, A_DEX:1 ]) );
+        SetProp( P_HP, QueryProp(P_MAX_HP) );
+
+        // Event ausloesen
+        EVENTD->TriggerEvent(EVT_LIB_PLAYER_CREATION, ([
+              E_OBJECT: ME,
+              E_PLNAME: getuid(ME) ]) );
+    }
+    
+    InitPlayer();
+
+    // Padreic 01.02.1999
+    if ( !IS_LEARNER(ME) && second = QueryProp(P_SECOND) ) {
+        if ( stringp(second) && lower_case(second)[0..3] == "von " ) {
+            second = lower_case(second[4..]);
+            SetProp( P_SECOND, second );
+        }
+
+        if ( !stringp(second ) ||
+             file_size( "/save/" + second[0..0] + "/" + second + ".o" ) <= 0 ){
+            if ( stringp(second) &&
+                 file_size( "/save/" + lower_case(second[0..0]) + "/" +
+                            lower_case(second) + ".o" ) >0 ){
+                SetProp( P_SECOND, lower_case(second) );
+                log_file( "WRONG_SECOND",
+                          sprintf( "%s: %s: P_SECOND = %O -> Automatisch "
+                                   "korrigiert,\n",
+                                   dtime(time()), object_name(), second ) );
+            }
+            else {
+                tell_object( ME,
+                             "*\n*\n* Deine Zweitiemarkierung ist ungueltig, "
+                             "bitte aendere diese und sprich im\n* Zweifel "
+                             "bitte einen Erzmagier an.\n*\n*\n" );
+
+                log_file( "WRONG_SECOND",
+                          sprintf( "%s: %s: P_SECOND = %O\n",
+                                   dtime(time()), object_name(), second ) );
+                // ein bisschen deutlicher auffordern.. Padreic 08.04.1999
+                move( "/d/gebirge/room/zwafflad", M_GO );
+            }
+        }
+    }
+    return(0);
+}
+
+/** Letzte Phase der Spielerinitialisierung beim Laden des Charakters.
+  * Ruft enable_commands(), aktiviert den Heartbeat und aktiviert die
+  * Kommandos aus den geerbten command.c, put_and_get.c, team.c, soul.c,
+  * guide.c, setzt den Living Name.
+  * Registriert UseSpell() als Catchall-Kommando.
+  * Laesst den Spieler ggf. einen Level per /std/gilde aufsteigen, ruft
+  * call_notify_player_change(), loest Login-Event aus.
+  * Gibt Willkommenstexte, News und neue Mails aus.
+  * Findet den Startraum und bewegt den Spieler dorthin.
+  * Ruft FinalSetup() (aus den Rassenshells).
+  * Begrenzt Geldmenge im Spieler (wegen Bankzweities) nach Tragkraft und
+  * erstattet Geld bei Reboot.
+  * Ruft set_is_wizard().
+  * Startet Clonen der Autoloader.
+  * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
+  */
+private void InitPlayer4()
+{
+    int num, str, ski;
+    string err, called_from_ip;
+    mixed start_place;
+    object mon;
+
+    enable_commands();
+    set_heart_beat(1);
+    command::initialize();
+    add_put_and_get_commands();
+    add_team_commands();
+    add_soul_commands();
+    add_guide_commands();
+    add_action( "UseSpell", "", 1 );
+    set_living_name( getuid() );
+    while ( remove_call_out("disconnect") != -1 )
+        ;
+
+    if ( QueryProp(P_LEVEL) == -1 )
+    {
+        catch( "/std/gilde"->try_player_advance(this_object()) ;publish );
+    }
+
+    if ( interactive(ME) )
+        call_notify_player_change(1);
+
+    if ( interactive(this_object()) ) {
+        cat( "/etc/NEWS" );
+
+        NewbieIntroMsg();
+
+        if ( QueryProp(P_INVIS) && !IS_WIZARD(ME) )
+            SetProp( P_INVIS, 0 );
+
+        catch( num = "secure/mailer"->FingerMail(getuid());publish );
+
+        if ( num )
+            write( "Du hast " + num + " neue" +
+                   (num == 1 ? "n Brief" : " Briefe")+" im Postamt liegen.\n" );
+
+        catch( RegisterChannels();publish );
+
+        if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
+             query_ip_number(ME) != called_from_ip ){
+            string tmp;
+
+            if ( stringp(tmp = query_ip_name(called_from_ip)) &&
+                 tmp != called_from_ip )
+                tmp = " [" + tmp + "]";
+            else
+                tmp = "";
+
+            write( "Das letzte Mal kamst Du von " + called_from_ip
+                   + tmp + ".\n" );
+        }
+
+        Set( P_CALLED_FROM_IP, query_ip_number(ME) );
+    }
+
+    if ( !stringp(default_home) || default_home == "" )
+        default_home = "/gilden/abenteurer";
+
+    if ( IS_SEER(ME) && !IS_LEARNER(ME) )
+        catch( start_place = HAUSVERWALTER->FindeHaus(getuid(ME));publish );
+    // wenn der Spieler noch ganz frisch ist und noch wenig  Stufenpunkte
+    // gekriegt hat und das Tutorial noch nicht gemacht hat, startet er im
+    // Tutorial.
+    else if (QueryProp(P_LEP) <= 120
+             && QM->HasMiniQuest(this_object(),
+               "/d/anfaenger/ennox/tutorial/npcs/verkaeufer") != 1)
+        start_place = "/room/welcome/"+ getuid(this_object());
+    else
+        start_place = 0;
+
+    if ( !start_place )
+        start_place = QueryProp(P_START_HOME);
+
+    if( objectp(start_place) || (stringp(start_place) && start_place != "" ) ){
+        if ((err = catch(move( start_place, M_GO|M_SILENT|M_NO_SHOW );publish)) 
+            || !environment() )
+            err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
+    }
+    else
+        err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
+
+    if ( err )
+        catch(move( "/gilden/abenteurer", M_GO|M_SILENT|M_NO_SHOW );publish);
+
+    // Die Shell muss FinalSetup() nicht implementieren. Daher Callother
+    catch( ME->FinalSetup();publish );
+
+    // Login-event ausloesen
+    EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
+          E_OBJECT: ME,
+          E_PLNAME: getuid(ME),
+          E_ENVIRONMENT: environment() ]) );
+
+    // erst jetzt GMCP freigeben und zu verhandeln.
+    gmcp::startup_telnet_negs();
+
+    // Schonmal sichern, falls ein Bug (Evalcost...) dazwischen kommen sollte.
+    autoload_rest = autoload;
+
+    // Um Geld-Xties mit illegal viel Geld zu vermeiden, kommt ein Check:
+    if ( !IS_LEARNER(ME) && !Query(P_TESTPLAYER) &&
+         mon = present( "\ngeld", ME ) ){
+        // maximale Kraft, die der Spieler haette haben koennen
+        str = QueryAttribute(A_STR) + 4;
+        if ( Query(P_FROG) )
+            str += 30;
+        if ( str < 1 )
+            str = QueryRealAttribute(A_STR) + 4;
+        if ( str > 30 )
+            str = 30;
+
+        // Trageskill beachten
+        ski = to_int(UseSkill( SK_CARRY, ([SI_SKILLARG : str ]) ));
+        if ( !intp(ski) )
+            ski = 0;
+
+        // Wieviel konnte der Spieler insgesamt maximal tragen?
+        num = 9200 + str * 800 + ski;
+        if ( num < 3000 )
+            num = 3000;
+
+        // Verdoppeln fuer einen guten Container und nochmal 20% draufschlagen
+        // zur Sicherheit. Das Ganze danach *4, um die maximale Anzahl Muenzen
+        // zu erhalten.
+        num = (int) (num * 8.8);
+
+        // Geld, das zuviel ist, auf den Maximalwert kuerzen.
+        // Zur Sicherheit wird mitgeloggt. Falls ein Spieler sich (zu recht)
+        // beschwert, kann man immer noch wieder die Summe korrigieren ;-)
+        if ( (str = mon->QueryProp(P_AMOUNT)) > num ){
+            mon->SetProp( P_AMOUNT, num );
+            log_file( "ZUVIEL_GELD", sprintf( "%s: %s hatte %d Muenzen bei "
+                                              "sich. Korrigiert auf %d.\n ",
+                                              ctime(time())[4..],
+                                              capitalize(getuid(ME)),
+                                              str, num ) );
+        }
+    }
+    int entschaedigung = QueryProp(P_CARRIED_VALUE);
+    if ( entschaedigung > 0 )
+    {
+        write( "Du findest " + entschaedigung +
+               " Muenzen, die Du beim letzten Mal verloren hast.\n" );
+
+        if ( MayAddWeight( entschaedigung / 4 ) ){
+            write( "Weil Du nicht mehr soviel tragen kannst, spendest Du den "+
+                   "Rest der Zentralbank.\n" );
+
+            num = (QueryProp(P_MAX_WEIGHT) - query_weight_contents()) * 4;
+            entschaedigung = (num < 0) ? 0 : num;
+        }
+
+        AddMoney( entschaedigung );
+        SetProp(P_CARRIED_VALUE,0);
+    }
+
+    if ( !QueryProp(P_INVIS) )
+        say( capitalize(name(WER)) + " betritt diese Welt.\n" );
+    else
+        write( "DU BIST UNSICHTBAR!\n\n" );
+#if __EFUN_DEFINED__(set_is_wizard)
+    if ( IS_WIZARD(getuid(ME)) )
+        set_is_wizard( ME, 1 );
+    else
+        set_is_wizard( ME, 0 );
+#endif
+    if ( query_once_interactive(ME) )
+        ListAwaited();
+
+    // Autoloader werden ganz zum Schluss geclont, da das bis zum bitteren
+    // (Evalcost-)Ende geschieht und danach u.U. keine Rechenzeit fuer
+    // andere Aktionen mehr ueber ist
+    load_auto_objects( autoload );
+}
+
+/** Setzt die Spielerinitialisierung nach start_player() fort.
+  * Prueft den Wert der vom Spieler getragenen Sachen (fuer Entschaedigung
+  * nach Reboot).
+  * Setzt div. Properties des "Spielerkoerpers", die eigentlich gespeichert
+  * werden, nach einem Reboot zurueck.
+  * Fragt ggf. nach eMail-Adresse und uebergibt per input_to an
+  * InitPlayer2().
+  * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
+*/
+private void InitPlayer()
+{
+    string mailaddr;
+
+    // wenn es einen Crash gab, sollen Spieler nicht noch extra bestraft werden
+    if ( file_time( "/save/" + getuid()[0..0] + "/" + getuid() + ".o" )
+         < last_reboot_time() ){
+        SetProp( P_FOOD, 0 );
+        SetProp( P_DRINK, 0 );
+        SetProp( P_ALCOHOL, 0 );
+        SetProp( P_BLIND, 0 );
+        SetProp( P_DEAF, 0 );
+        SetProp( P_POISON, 0 );
+        SetProp( P_GHOST, 0 );
+        SetProp( P_FROG, 0 );
+        SetProp( P_HP, QueryProp(P_MAX_HP) );
+        SetProp( P_SP, QueryProp(P_MAX_SP) );
+    }
+
+    if ( QueryGuest() )
+        Set( P_MAILADDR, "none" );
+    else if ( !(mailaddr = Query(P_MAILADDR)) || mailaddr == "" ) {
+        write(break_string(
+          "Eine gueltige EMail-Adresse erleichtert es erheblich, Dir "
+          "ein neues Passwort setzen zu lassen, falls Du einmal Dein "
+          "Passwort vergisst.",78)); 
+        input_to( "getmailaddr",INPUT_PROMPT,
+            "Gib bitte Deine EMail-Adresse an: " );
+        return;
+    }
+    InitPlayer2();
+}
+
+/** liest eMail-Adresse vom Spieler ein und speichert sie.
+  * Uebergibt anschliessend an InitPlayer2()
+  * @param[in] maddr Spielereingabe der Emailadresse.
+  * @sa InitPlayer2().
+  */
+static void getmailaddr( string maddr )
+{
+    maddr = check_email(maddr);
+
+    if ( !stringp(maddr)) {
+      write("Deine Eingabe scheint keine gueltige EMail-Adresse gewesen "
+          "zu sein.\n");
+      input_to( "getmailaddr", INPUT_PROMPT,
+          "Gib bitte Deine EMail-Adresse an: " );
+      return;
+    }
+    Set( P_MAILADDR, maddr );
+    InitPlayer2();
+}
+
+
+/** Prueft Geschlecht des Spielers und fragt ggf. beim Spieler nach.
+  * Uebergibt an InitPlayer3() oder get_gender().
+  * @sa InitPlayer3(), get_gender().
+  */
+private void InitPlayer2()
+{
+    if( member(({ MALE, FEMALE }), QueryProp(P_GENDER) ) == -1 ) {
+        input_to( "getgender", INPUT_PROMPT,
+            "Bist Du maennlich oder weiblich: ");
+        return;
+    }
+
+    InitPlayer3();
+}
+
+/** Liest Spielerantwort auf die Frage nach dem Geschlecht des Chars ein.
+  * Wird gerufen von input_to().
+  * Uebergibt an InitPlayer3().
+  * @sa InitPlayer3().
+  */
+static void getgender( string gender_string )
+{
+    gender_string = lower_case( gender_string );
+
+    if ( sizeof(gender_string)==1 && gender_string[0] == 'm' ){
+        write( "Willkommen, mein Herr!\n" );
+        SetProp( P_GENDER, MALE );
+    }
+    else if ( sizeof(gender_string)==1 && gender_string[0] == 'w' ){    
+        write( "Willkommen, gnae' Frau!\n" );
+        SetProp( P_GENDER, FEMALE );    
+    }    
+    else {
+        write( "Wie? Was? Verstehe ich nicht!\n" );    
+        input_to( "getgender", INPUT_PROMPT,
+            "Bist Du maennlich oder weiblich? (tippe m oder w): ");    
+        return;
+    }
+
+    InitPlayer3();
+}
+
+
+/** Prueft Terminaltyp des Spielers und fragt ggf. beim Spieler nach.
+  * Uebergibt an InitPlayer4() oder gettty().
+  * @sa InitPlayer4(), gettty().
+  */
+private void InitPlayer3()
+{
+    if ( !QueryProp(P_TTY) || QueryProp(P_TTY) == "none" )
+    {
+        write( "Waehle einen Terminaltyp (kann spaeter mit <stty> geaendert "
+               "werden)\n");
+        input_to( "gettty", INPUT_PROMPT, "vt100, ansi, dumb (Standard: dumb): " );
+        return;
+    }
+    InitPlayer4();
+}
+
+/** Liest Spielerantwort auf die Frage nach dem Terminaltyp ein.
+  * Wird gerufen von input_to().
+  * Uebergibt an InitPlayer4().
+  * @sa InitPlayer4().
+  */
+static void gettty( string ttystr )
+{
+    if ( !ttystr || ttystr == "" )
+        ttystr = "dumb";
+
+    ttystr = lower_case(ttystr);
+
+    if ( ttystr == "vt100" ){
+        write( "Dies sollte " + ANSI_BOLD + "fett" + ANSI_NORMAL + " sein.\n" );
+        SetProp( P_TTY, ttystr );
+    }
+    else
+        if ( ttystr == "ansi" ){
+            write( "Dies sollte " + ANSI_RED + "rot" + ANSI_NORMAL +
+                   " sein.\n" );
+            SetProp( P_TTY, "ansi" );
+        }
+        else if ( ttystr == "dumb" ){
+            write( "Ohje, oede! Besorg Dir ein besseres Terminal!\n" );
+            SetProp( P_TTY, "dumb" );
+        }
+        else {
+            write( "Dieser Terminaltyp wird nicht unterstuetzt. Nimm bitte "
+                   "einen aus:\nvt100, ansi or dumb (Standard ist dumb).\n" );
+            input_to( "gettty", INPUT_PROMPT,
+                "vt100, ansi or dumb (Standard ist dumb): ");
+            return;
+        }
+
+    InitPlayer4();
+}
+
+
+/** Liefert die UID des Charakters zurueck, also den Charakternamen.
+  * @return UID des Objekts (Charaktername).
+  */
+nomask string query_real_name() {
+  /* ACHTUNG !! DIES LFUN DARF NICHT ENTFERNT WERDEN !!! */
+  /* Sie wird vom Gamedriver (zB bei F_ED) aufgerufen !! */
+  /* Ich bin da zwar nicht so ueberzeugt von, dass der Driver die heutzutage
+   * noch ruft, aber die halbe Mudlib ruft sie. ;-) (Zesstra, 27.4.08)
+   */
+  return getuid();
+}
+
+/*
+ * the wizard command review: show player moving messages
+ */
+int review() {
+  string *msg;
+  write(short());
+  write("Deine Bewegungen werden wie folgt gemeldet:\n"+
+        "mout:  "+name(WER)+" "+(msg=explode(QueryProp(P_MSGOUT),"#"))[0]
+       +" <Richtung>"+(sizeof(msg)>1 ? msg[1] : "")+".\n"+
+        "min:   "+name(WER)+" "+QueryProp(P_MSGIN)+".\n"+
+        "mmout: "+name(WER)+" "+QueryProp(P_MMSGOUT)+".\n"+
+        "mmin:  "+name(WER)+" "+QueryProp(P_MMSGIN)+".\n"+
+        (IS_LEARNER(ME) ?
+         "cmsg:  "+name(WER)+" "+QueryProp(P_CLONE_MSG)+".\n"+
+         "dmsg:  <Irgendetwas> "+QueryProp(P_DESTRUCT_MSG)+".\n"
+         : "")+
+        "Wenn Du jemanden angreifst, sieht das so aus:\n"+
+        name(WER)+" greift Dich"+QueryProp(P_HANDS)[0]+" an.\n");
+  return 1;
+}
+
+/*
+ * set player moving messages
+ */
+
+static int setmin(string str)
+{
+  SetProp(P_MSGIN, _unparsed_args()||"kommt an");
+  write("Ok.\n");
+  return 1;
+}
+
+static int setmout(string str)
+{
+  string *msg;
+
+  if(sizeof(msg=explode((_unparsed_args()||"geht"),"#"))>2)
+  {
+    write("Du darfst nur einmal '#' fuer die Richtung angeben.\n");
+    return 1;
+  }
+  if(sizeof(msg)>1)
+  {
+    if (msg[0]!="" && msg[0][<1]==' ') msg[0]=msg[0][0..<2];
+    SetProp(P_MSGOUT, msg[0]+"#"+msg[1]);
+  }
+  else
+    SetProp(P_MSGOUT, _unparsed_args()||"geht");
+  write("Ok.\n");
+  return 1;
+}
+
+static int setmmin(string str)
+{
+  SetProp(P_MMSGIN, _unparsed_args()||"erscheint");
+  write("Ok.\n");
+  return 1;
+}
+
+static int setmmout(string str)
+{
+  SetProp(P_MMSGOUT, _unparsed_args()||"verschwindet");
+  write("Ok.\n");
+  return 1;
+}
+
+static int setcmsg(string str)
+{
+  SetProp(P_CLONE_MSG, _unparsed_args()||"zaubert etwas aus "
+          + QueryPossPronoun(MALE,WEM) + " Aermel hervor");
+  write("Ok.\n");
+  return 1;
+}
+
+static int setdmsg(string str)
+{
+  SetProp(P_DESTRUCT_MSG, _unparsed_args()||"wird von " + name(WER,1)
+          + " zerstaeubt");
+  write("Ok.\n");
+  return 1;
+}
+
+static int set_title(string str)
+{
+  string bonus;
+  
+  if(!IS_SEER(this_object()) && !FAO_HAS_TITLE_GIFT(this_object()) )
+  {
+    return 0;
+  }
+  
+  bonus = "";
+  if (!(str=_unparsed_args()))
+    str = "";
+  else if( str[0..2]=="\\b," || str[0..2]=="\\b'" )
+  {
+    bonus = "\b"; // ein backspace fuer ein (hoch)komma ist ok! :-)
+    str = str[2..];
+  }
+  if(str=="0")  // damit der Gildentitel zum Vorschein kommen kann
+    SetProp(P_TITLE, 0);
+  else
+    SetProp(P_TITLE, bonus+str);
+  write("Ok.\n");
+  return 1;
+}
+
+static int extra_input(string str, string look)
+{
+  if (str=="**")
+  {
+    if (look=="")
+      SetProp(P_EXTRA_LOOK,0);
+    else
+      SetProp(P_EXTRA_LOOK,look);
+    write("Ok.\n");
+    return 1;
+  }
+  input_to("extra_input",INPUT_PROMPT, "]" ,look+str+"\n");
+  return 1;
+}
+
+static int extralook(mixed str)
+{
+  if( str=="?" )
+  {
+    write( "Dein Extralook ist : "+QueryProp(P_EXTRA_LOOK) + "\n");
+    return 1;
+  }
+  write("Bitte gib Deinen Extra-Look ein. Beenden mit **:\n");
+  input_to("extra_input",INPUT_PROMPT,"]","");
+  return 1;
+}
+
+static void calculate_value()
+{
+  int i, carried_value, value;
+
+  carried_value=0;
+  foreach(object ob: deep_inventory(ME)) {
+    if (!ob->QueryProp(P_AUTOLOADOBJ))
+      carried_value+=((value=(int)ob->QueryProp(P_VALUE)) > 1000 ? 1000 : value);
+  }
+  SetProp(P_CARRIED_VALUE, carried_value);
+}
+
+/* Called by command 'save' */
+int save_character() {
+  save_me(1);
+  write("Ok.\n");
+  return 1;
+}
+
+void save_me(mixed value_items)
+{
+  // Gaeste werden nicht gespeichert
+  if( getuid()[0..3]=="gast" )
+    return;
+ 
+  // Autoloader identifizieren und speichern
+  autoload=([]);
+  foreach(object ob: deep_inventory(ME)) {
+    int val = ob->QueryProp( P_AUTOLOADOBJ );
+    if (val && clonep(ob))
+    {
+      string obname=load_name(ob);
+      if (obname == GELD)
+        autoload[obname] += val;
+      else
+        autoload += ([obname:val]);
+    }
+  }
+  // An noch nicht geclonte Autoloader denken!
+  autoload += autoload_rest;
+  
+  // Im Bedarfsfall Wert des Inventory bestimmen
+  if (value_items)
+    calculate_value();
+  else
+    SetProp(P_CARRIED_VALUE, 0);
+
+  // Logout-Zeit speichern
+  if(query_once_interactive(ME) && !QueryProp(P_INVIS))
+    Set(P_LAST_LOGOUT,time());
+
+  // Funktion zur Bestimmung des Gildenrating aufrufen
+  string gilde=GUILD_DIR+QueryProp(P_GUILD);
+  if (find_object(gilde) || file_size(gilde+".c")>-1)
+    catch(call_other(gilde,"GuildRating",this_object());publish);
+
+  // Speichern des Spielers
+  save_object("/"+SAVEPATH+getuid()[0..0]+"/" + getuid());
+}
+
+static varargs void log_autoload( string file, string reason, mixed data, string error )
+{
+  if (member(autoload_error,file)!=-1) return;
+  log_file(SHELLLOG("NO_AUTO_FILE"),sprintf("%s: %s: %s\nreason: cannot %s file\ndata: %O\n%s\n",
+           ctime()[4..15],capitalize(getuid()),file,reason,data,
+           (error?"Fehlermeldung: "+error+"\n":"")));
+  autoload_error+=({file});
+}
+
+// tics, die fuer autoloader reichen sollten:
+#define SAFE_FOR_AUTOLOADER __MAX_EVAL_COST__/4
+
+private void load_auto_object( string file, mixed data )
+{
+    object ob;
+    string error;
+   
+    if( get_eval_cost() < SAFE_FOR_AUTOLOADER ) return;
+    m_delete( autoload_rest, file );
+    autoload_error-=({file});
+
+    if ( file == "/obj/money" )
+      file = "/items/money";
+    if ( file == "/obj/seercard" )
+      file = "/items/seercard";
+    
+    ob = find_object(file);
+    
+    if (!ob)
+    {
+        if (file_size(file+".c")<0&&
+           file_size(implode(explode(file,"/")[0..<2],"/")+
+                     "/virtual_compiler.c")<0)
+        {
+           log_autoload(file,"find",data,0);
+           return;
+        }
+        if (error = catch(load_object(file); publish))
+        {
+           log_autoload(file,"load",data,error);
+           return;
+        }
+    }
+    if ( error = catch(ob = clone_object(file); publish) )
+    {
+        log_autoload(file,"clone",data, error);
+        return;
+    }
+
+    if ( error = catch(ob->SetProp( P_AUTOLOADOBJ, data ); publish) )
+    {
+        log_autoload(file,"SetProp",data, error);
+        ob->remove(1);
+        if (ob) destruct(ob);
+        return;
+    }
+
+    if ( error = catch(ob->move( ME, M_NOCHECK );publish) ) {
+        log_autoload(file,"move",data, error);
+        ob->remove(1);
+        if(ob) destruct(ob);
+        return;
+    }
+}
+
+static void load_auto_objects( mapping map_ldfied )
+{
+    if ( (!mappingp(map_ldfied) || !sizeof(map_ldfied)) && !sizeof(autoload_rest) )
+        return;
+
+    // Mit Netztoten Spielern rechnen manche Autoloader nicht. Also
+    // das Clonen unterbrechen und in Reconnect() wieder anwerfen.
+    if ( !interactive() )
+        return;
+
+    // Kleiner Hack: autoload_rest ist eine globale Variable, die beim
+    // Clonen der einzelnen Autoloader direkt veraendert wird.
+    // So lange das Mapping noch Eintraege hat, muessen wir noch fehlende
+    // Autoloader clonen.
+    if ( !sizeof(autoload_rest) )
+        autoload_rest = map_ldfied;
+
+    // Schon hier einen call_out() zum "Nach"clonen von noch nicht geclonten
+    // Autoloadern starten, da spaeter u.U. keine Rechenzeit mehr dafuer da ist.
+    while ( remove_call_out("load_auto_objects") != -1 )
+        /* do nothing */;
+    
+    // Mit Parameter '0' aufrufen, da das globale Mapping benutzt wird.
+    // Verzoegerung 0 in rekursiven Callouts ist bloed, also 1s Delay
+    call_out( "load_auto_objects", 2, 0 );
+    
+    // Mit catch() gegen die Evalcost-Falle!
+    // Mit Absicht das walk_mapping() aus der "alten" Version erhalten und
+    // nicht durch eine (einfachere) Schleife inkl. get_eval_cost() ersetzt,
+    // da eine Schleife gegenueber der walk_mapping()-Loesung den Aufbau
+    // der previous_object()-Kette veraendern wuerde; darauf testen aber
+    // manche Objekte.
+    catch( walk_mapping( autoload_rest, #'load_auto_object/*'*/ ) );
+}
+
+/*
+ * al_to_title: Make the numeric alignment value into a string
+ */
+static string al_to_title(int a)
+{
+  if (a >= KILL_NEUTRAL_ALIGNMENT * 100)
+    return "heilig";
+  if (a > KILL_NEUTRAL_ALIGNMENT * 20)
+    return "gut";
+  if (a > KILL_NEUTRAL_ALIGNMENT * 4)
+    return "nett";
+  if (a > - KILL_NEUTRAL_ALIGNMENT * 4)
+    return "neutral";
+  if (a > - KILL_NEUTRAL_ALIGNMENT * 20)
+    return "frech";
+  if (a > - KILL_NEUTRAL_ALIGNMENT * 100)
+    return "boese";
+  return "satanisch";
+}
+
+static int toggle_whimpy_dir(string str) {
+  SetProp(P_WIMPY_DIRECTION,str=_unparsed_args()||str);
+  if (str)
+    printf("Ok, Fluchtrichtung %O.\n",str);
+  else
+    printf("Ok, bevorzugte Fluchtrichtung deaktiviert.\n");
+  return 1;
+}
+
+static int toggle_whimpy(string str)
+{
+  int i;
+
+  if(!str || str=="" || (sscanf(str,"%d",i)<0))
+  {
+    write("vorsicht <hp>, 0<=hp<"+QueryProp(P_MAX_HP)+"\n");
+    return 1;
+  }
+  if(i>=QueryProp(P_MAX_HP) || i<0)
+  {
+    write("Der Wert ist nicht erlaubt.\n");
+    return 1;
+  }
+  if(!i) write("Prinz Eisenherz-Modus.\n");
+  else write("Vorsicht-Modus ("+i+")\n");
+  SetProp(P_WIMPY,i);
+  return 1;
+}
+
+/** Bestimmt, ob das Spielerobjekt beschattet werden darf.
+  * Beschatten ist nur fuer Objekte erlaubt, die in /std/player/shadows
+  * abgelegt sind.
+  * Ausnahme: Testspieler mit gesetztem P_ALLOWED_SHADOW
+  * @param[in] obj Objekt, was beschatten moechte.
+  * @return 0, wenn Beschatten erlaubt, 1 sonst.
+  */
+varargs nomask int query_prevent_shadow(object obj)
+{
+  string what, allowed_shadow;
+  int    dummy;
+
+//  if ( Query(P_TESTPLAYER) )
+//      return 0;
+  
+  if (obj){
+    what=object_name(obj);
+    if (what && what != "" &&
+        sscanf(what,"/std/player/shadows/%s#%d",what,dummy)==2)
+      return 0;
+    
+    // Einem Testspieler kann man P_ALLOWED_SHADOW auf einen zu testenden
+    // Shadow setzen.
+    if ( Query(P_TESTPLAYER) && 
+         stringp(what) && 
+         stringp(allowed_shadow=Query(P_ALLOWED_SHADOW)) &&
+         strstr(what, allowed_shadow)==0)
+            return 0;
+  }
+  return 1;
+}
+
+static int uhrzeit()
+{
+  write(dtime(time()+QueryProp(P_TIMEZONE)*3600)+".\n");
+  return 1;
+}
+
+protected string SetDefaultHome(string str)
+{
+  return default_home=str;
+}
+
+string QueryDefaultHome()
+{
+  return default_home;
+}
+
+protected string SetDefaultPrayRoom(string str)
+{
+  if(hc_play>1)
+  {
+    default_pray_room="/room/nirvana";
+  }
+  else
+    default_pray_room=str;
+  
+  return default_pray_room;
+}
+
+string QueryPrayRoom()
+{
+  if(hc_play>1)
+  {
+    return "/room/nirvana";
+  } 
+  string room = QueryProp(P_PRAY_ROOM);
+  if (stringp(room))
+    return room;
+  else if (default_pray_room)
+    return default_pray_room;
+  // hoffentlich ist das wenigstens gesetzt. 
+  return default_home;
+}
+
+void _restart_beat()
+{
+  tell_object(ME,
+      "Der GameDriver teilt Dir mit: Dein Herzschlag hat wieder eingesetzt.\n");
+  set_heart_beat(1);
+}
+
+static int weg(string str)
+{
+  if (!(str=_unparsed_args()))
+  {
+    printf("Du bist nicht%s als abwesend gekennzeichnet.\n",
+           QueryProp(P_AWAY) ? " mehr" : "");
+    SetProp(P_AWAY, 0);
+    return 1;
+  }
+  write("Du bist jetzt als abwesend gekennzeichnet.\n");
+  SetProp(P_AWAY, str);
+  return 1;
+}
+
+/* Ein Befehl zum anschauen der Wegmeldung anderer Spieler */
+static int wegmeldung(string player)
+{
+
+  object player_ob;
+  string weg;
+
+  if ( !player || player=="" || 
+       player==lowerstring(this_player()->QueryProp(P_NAME)))
+  {
+    weg=this_player()->QueryProp(P_AWAY);
+    write ("Du bist "+(weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
+    if (weg)
+      write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
+    return 1;  
+  }
+
+  // Welcher Spieler ist gemeint?
+  player_ob=find_player(player);
+
+  // Spieler nicht da oder Invis und Anfrager is kein Magier
+  if (!player_ob || 
+      (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player()))) 
+  {
+    write(capitalize(player)+" ist gerade nicht im Spiel.\n");
+    return 1;
+  }
+
+  weg=player_ob->QueryProp(P_AWAY);
+
+        // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
+  write (player_ob->QueryProp(P_NAME)+" ist "+
+         (weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
+
+  if (weg)
+    write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
+
+  return 1;
+}
+
+static string timediff(int time)
+{
+  string ret;
+
+  ret="";
+  if(time>=86400) {
+    ret+=time/86400+"d ";
+    time%=86400;
+  }
+  if(time<36000) ret+="0";
+  ret+=time/3600+":";
+  time%=3600;
+  if(time<600) ret+="0";
+  ret+=time/60+":";
+  time%=60;
+  if(time<10) ret+="0";
+  ret+=time+"";
+  return ret;
+}
+
+
+/* Ein Befehl zum anschauen der Idlezeit anderer Spieler */
+static int idlezeit(string player)
+{
+
+  object player_ob;
+  int idle;
+
+  if ( !player || player=="" || 
+       player==lowerstring(this_player()->QueryProp(P_NAME)))
+  {
+    write ("Du bist selber natuerlich gerade nicht idle.\n");
+    return 1;  
+  }
+
+  // Welcher Spieler ist gemeint?
+  player_ob=find_player(player);
+
+  // Spieler nicht da oder Invis und Anfrager is kein Magier
+  if (!player_ob || 
+      (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player()))) 
+  {
+    write(capitalize(player)+" ist gerade nicht im Spiel.\n");
+    return 1;
+  }
+
+  idle=query_idle(player_ob);
+
+        // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
+  write (player_ob->QueryProp(P_NAME)+" ist "+
+         (idle>=60?timediff(idle):"nicht")+" passiv.\n");
+
+  return 1;
+}
+
+
+/** Belebt einen netztoten Spieler wieder.
+  * Reaktiviert Heartbeats, bewegt den Spieler zurueck an den Ort, der eim
+  * Einschlafen zum Aufwachen ermittelt wurde (im einfachsten Fall der Raum,
+  * wo man eingeschlafen ist).
+  */
+static void ndead_revive()
+{
+    string fname;
+    int ret;
+
+    set_heart_beat(1);
+    ndead_next_check = NETDEAD_CHECK_TIME;
+    ndead_currently = 0;
+    ndead_lasttime = 0;
+
+    if ( !objectp(ndead_location) && 
+         stringp(ndead_l_filename) && sizeof(ndead_l_filename)) {
+        
+        if ( member( ndead_l_filename, '#' ) == -1 ){
+            catch(load_object( ndead_l_filename); publish);
+            ndead_location = find_object(ndead_l_filename);
+        }
+        else {
+            ndead_location = find_object(ndead_l_filename);
+            if ( !ndead_location && env_ndead_info ){
+                fname = explode( ndead_l_filename, "#" )[0];
+                catch(ndead_location = 
+                    (load_object(fname)->SetProp(P_NETDEAD_INFO, env_ndead_info));
+                    publish);
+                if ( !objectp(ndead_location) ){
+                    catch(load_object( ndead_location);publish);
+                    ndead_location = find_object(ndead_location);
+                }
+            }
+        }
+    }
+
+    if ( !objectp(ndead_location)
+        || catch(ret = move( ndead_location, M_GO|M_SILENT );publish) 
+        || ret != 1 ) {
+        move( "gilden/abenteurer", M_GO|M_SILENT );
+        ndead_location = environment();
+    }
+
+    //  ndead_location=0;
+    ndead_l_filename = 0;
+    env_ndead_info = 0;
+}
+
+/** Bewegt einen netztoten Spieler in den Netztotenraum
+  * Gerufen von heartbeat().
+  * Zerstoert Gaeste, verlaesst ggf. das Team, ermittelt, ob der Spieler beim
+  * Aufwachen in das alte Environment bewegt werden soll oder in einen anderen
+  * Raum, hierzu wird im Environment P_NETDEAD_INFO abgefragt.
+  * Deaktiviert die Kommandos per disable_commands().
+  */
+static void ndead_move_me() {
+  object team;
+  mixed amem;
+
+  set_heart_beat(0);
+  stop_heart_beats();
+  if (QueryGuest()) {
+    quit();
+    if (ME)
+      remove();
+    if (ME)
+      destruct(ME);
+    return;
+  }
+  ndead_next_check=NETDEAD_CHECK_TIME;
+  ndead_currently=1;
+  ndead_lasttime=0;
+  ndead_location=environment();
+  if (objectp(ndead_location))
+    ndead_l_filename=object_name(ndead_location);
+  
+  if (objectp(environment())
+      && sizeof(explode(object_name(environment()),"#")) > 1)
+    env_ndead_info=environment()->QueryProp(P_NETDEAD_INFO);
+  else
+    env_ndead_info=0;
+  
+  if ( objectp(team=Query(P_TEAM)) )
+      // Der Test auf assoziierte Teammitglieder (== FolgeNPCs)
+      // verhindert, dass Spieler nach "schlafe ein" aus dem
+      // Team ausgetragen werden. -- 29.01.2002 Tiamak
+      //      && !objectp(amem=Query(P_TEAM_ASSOC_MEMBERS))
+      //      && !(pointerp(amem) && sizeof(amem)))
+    team->RemoveMember(ME);
+  
+  disable_commands();
+  move(NETDEAD_ROOM,M_GO|M_NO_ATTACK|M_NOCHECK,"ins Reich der Netztoten");
+}
+
+/** Ist dieser Character ein Gast?
+  * @return 1, wenn Gast, 0 sonst.
+  */
+int QueryGuest()
+{
+  string dummy;
+  return sscanf(getuid(),"gast%d",dummy);
+}
+
+/** Spielerkommando 'schlafe ein'.
+  * Ruft remove_interactive() bei Spielern, bei Magiern wird quit() gerufen,
+  * um das Magierobjekt zu zerstoeren.
+  * @sa quit()
+  */
+int disconnect(string str)
+{ 
+  string verb;
+  string desc = break_string(
+      "\"schlafe ein\" beendet Deine Verbindung mit "MUDNAME". Du behaeltst "
+     "Deine Sachen.\nFalls "MUDNAME" jedoch abstuerzt oder neu gestartet "
+     "wird, waehrend Du weg bist, verlierst Du die meisten allerdings "
+     "(genauso, als wenn Du Deine Verbindung mit \"ende\" beendet haettest). "
+     "In diesem Fall bekommst Du dann eine kleine Entschaedigung."
+     ,78,0,BS_LEAVE_MY_LFS);
+
+  verb=query_verb();
+  if (!verb)
+    verb="AUTOCALL";
+  if (verb[0..5]=="schlaf" && str!="ein")
+  {
+    notify_fail(desc);
+    return 0;
+  }
+  if (IS_LEARNER(this_object()))
+    return quit();
+  
+  tell_object(this_object(), desc);
+
+  if (clonep(environment()) && !environment()->QueryProp(P_NETDEAD_INFO))
+    tell_object(this_object(),break_string(
+        "\nACHTUNG: Wenn Du hier laengere Zeit schlaefst, "
+        "kommst Du vermutlich nicht an diesen Ort zurueck!",78));
+
+  say(capitalize(name(WER))+" hat gerade die Verbindung zu "MUDNAME" gekappt.\n");
+  remove_interactive(ME);
+  call_out(#'clear_tell_history,4);
+  return 1;
+}
+
+static int finger (string str)
+{
+  string ret;
+  mixed xname;
+
+  if (!str || str==""
+      || sizeof(explode(str," ")-({"-n","-p","-s","-v","-a"}))>1)
+  {
+    write("finger <spielername> oder finger <spielername@mudname>\n"+
+      "Bitte nur den reinen Spielernamen verwenden, keine Namensvorsaetze oder Titel\n");
+    return 1;
+  }
+  xname=explode(str,"@");
+  if(sizeof(xname)==2)
+  {
+    if (xname[0]=="-n " || xname[0]=="-p " || xname[0]=="-s ")  {
+      write("finger <spielername>@<mudname> - der Spielername fehlt.\n");
+      return 1;
+    }
+    if (ret=INETD->_send_udp(xname[1],([
+                   REQUEST: "finger",
+                   SENDER: getuid(ME),
+                   DATA: (explode(xname[0]," ")-({"-n","-p","-s"}))[0]
+                   ]), 1))
+            write(ret);
+        else
+      write("Anfrage abgeschickt.\n");
+    return 1;
+  }
+  "/p/daemon/finger"->finger_single(str,1);
+  return 1;
+}
+
+string lalign(string str, int wid)
+{
+  return (str+"                                                   "+
+      "                                                   ")[0..wid-1];
+}
+
+#define MUDS_BAR "\
+-------------------------------------------------------------------------------"
+
+private void format(mixed mud, mixed hosts, string output)
+{
+  output += lalign(hosts[mud][HOST_NAME], 20) + "  " +
+        (hosts[mud][HOST_STATUS] ?
+           hosts[mud][HOST_STATUS] > 0 ?
+             "UP       " + ctime(hosts[mud][HOST_STATUS])[4..15] :
+             "DOWN     " + ctime(-hosts[mud][HOST_STATUS])[4..15]
+         : "UNKNOWN  Never accessed.") + "\n";
+}
+
+static int muds() {
+  mapping hosts;
+  int i;
+  mixed muds, output;
+
+  output = lalign("Mudname", 20) + "  Status   Last access";
+  output += "\n" + MUDS_BAR[0..sizeof(output)] + "\n";
+  muds = sort_array(m_indices(hosts = INETD->query("hosts")),#'>);
+  map(muds, #'format, hosts, &output);
+  More(output);
+  return 1;
+}
+
+// **** local property methods
+static int _set_level(int i)
+{
+  if (!intp(i)) return -1;
+  if (i<1) return -1;
+  Set(P_LEVEL, i);
+  GMCP_Char( ([P_LEVEL: i]) );
+  return i;
+}
+
+static int _set_invis(int a)
+{
+  return Set(P_INVIS, intp(a) ? a : !Query(P_INVIS));
+}
+
+/* sets the terminal type */
+/* note: support vt100 (b/w), ansi (color), dumb (none) */
+static string _set_tty(string str) {
+  if(str != "dumb" && str != "vt100" && str != "ansi")
+    return Query(P_TTY);
+  return Set(P_TTY, str);
+}
+
+static int stty(string str)
+{
+  if(str!="dumb"&&str!="vt100"&&str!="ansi"&&str!="reset")
+  {
+    write("Kommando: stty dumb|vt100|ansi oder reset\n");
+  }
+  if(str == "reset") {
+      printf("Dieser Text sollte lesbar sein!\n");
+      return 1;
+  }
+
+  write("TTY steht jetzt auf "+SetProp(P_TTY,str)+".\n");
+  if(str == "ansi" || str == "vt100") {
+      printf("Terminal Test:\n");
+      printf("VT100: fett unterstrichen "+
+             "blinkend invers\n");
+      if(str == "ansi") {
+          printf("ANSI Farben und VT100 Attribute:\n");
+          foreach(int fg: 30 .. 37) {
+              foreach(int bg: 40 .. 47) {
+                  printf("[%d;%dm@", fg, bg);
+                  printf("[%d;%dm@", fg, bg);
+                  printf("[%d;%dm@", fg, bg);
+                  printf("[%d;%dm@", fg, bg);
+              }
+              printf("\n");
+          }
+          printf("Sollte dieser Text hier nicht richtig lesbar\nsein,"+
+                 "benutze das Kommando stty reset!\n");
+      }
+
+  }
+  return 1;
+}
+
+int set_ascii_art(string str)
+{
+  if (str!="ein"&&str!="aus")
+  {
+     printf("Du moechtest 'Grafik' "+(QueryProp(P_NO_ASCII_ART)?"NICHT ":"")+
+                     "sehen.\n");
+  }
+
+  if (str=="ein") {
+    SetProp(P_NO_ASCII_ART, 0);
+    printf("Zukuenftig moechtest Du 'Grafik' sehen.\n");
+  }
+
+  if (str=="aus") {
+    SetProp(P_NO_ASCII_ART, 1);
+    printf("Zukuenftig moechtest Du KEINE 'Grafik' mehr sehen.\n");
+  }
+
+
+  return 1;
+}
+
+int _set_shell_version(int arg)
+{
+  if (!intp(arg))
+    return -1;
+  Set(P_SHELL_VERSION,({QueryProp(P_RACE),arg}));
+  return 1;
+}
+
+int _query_shell_version()
+{   mixed sv;
+
+    if (!(sv=Query(P_SHELL_VERSION)) || !pointerp(sv) || sizeof(sv)!=2 ||
+        sv[0]!=QueryProp(P_RACE) || !intp(sv[1]))
+      return 0;
+    return sv[1];
+}
+
+// XxXxXxXxXx
+
+int more(string str)
+{
+  if(!str)
+  {
+    notify_fail("Usage: more <file>\n");
+    return 0;
+  }
+  if (file_size(str) <= 0) {
+    notify_fail(str+": No such file\n");
+    return 0;
+  }
+  More(str, 1);
+  return 1;
+}
+
+static int set_visualbell(string str)
+{
+  if(!str)
+  {
+    write("Derzeitige Einstellung fuer Tonausgabe: "+
+         (QueryProp(P_VISUALBELL)?"AUS":"EIN")+".\n");
+    return 1;
+  }
+  if (str=="ein")
+  {
+    if(!QueryProp(P_VISUALBELL))
+      write("Die Tonausgabe stand schon auf EIN.\n");
+    else
+      {
+  SetProp(P_VISUALBELL,0);
+        write("OK, Tonausgabe auf EIN gestellt.\n");
+      }
+  }
+  else
+    if (str=="aus")
+    {
+      if(QueryProp(P_VISUALBELL))
+        write("Die Tonausgabe stand schon auf AUS.\n");
+      else
+        {
+          SetProp(P_VISUALBELL,1);
+    write("OK, Tonausgabe auf AUS gestellt.\n");
+  }
+    }
+    else
+      write("Syntax: ton [ein|aus]\n");
+  return 1;
+}
+
+static int set_screensize(string str)
+{
+  int size;
+
+  if (str && (str[0..2] == "abs" || str[0..2]=="rel")) {
+    size = QueryProp(P_MORE_FLAGS);
+    if (str[0..2] == "abs") {
+      size |= E_ABS;
+      write("Es wird beim Prompt die Zeilenzahl des Textes angegeben.\n");
+    }
+    else {
+      size &= ~E_ABS;
+      write("Es wird beim Prompt der prozentuale Anteil des Textes angegeben.\n");
+    }
+    SetProp(P_MORE_FLAGS, size);
+    return 1;
+  }
+
+  if ( str && (str=="auto" || sscanf( str, "auto %d", size )) ){
+      if ( size > 0 ){
+          write("Ungueltiger Wert! "
+                "In Verbindung mit 'auto' sind nur negative Werte erlaubt.\n");
+          return 1;
+      }
+
+      SetProp( P_SCREENSIZE, size-1 );
+
+      write("Ok, Deine Zeilenzahl wird nun automatisch ermittelt (derzeit "+
+            QueryProp(P_SCREENSIZE)+").\n"+
+            break_string("Bitte beachte, dass dies nur einwandfrei "
+                         "funktioniert, wenn Dein Client Telnetnegotiations "
+                         "unterstuetzt (siehe auch \"hilfe telnegs\").") );
+    return 1;
+  }
+
+  if ( !str || str=="" || !sscanf( str, "%d", size ) || size < 0 || size > 100){
+      write(break_string(
+            sprintf("Mit dem Befehl 'zeilen <groesse>' kannst Du einstellen, "
+            "wieviele Zeilen bei mehrseitigen Texten auf einmal ausgegeben "
+            "werden. Die angegebene Groesse muss zwischen 0 und 100 liegen. "
+            "Bei Groesse 0 wird einfach alles ausgegeben (ohne Pause). Mit "
+            "der Einstellung 'auto' wird die Groesse automatisch ueber "
+            "die Telnetnegotiations ermittelt (siehe auch 'hilfe telnegs'). "
+            "Um nach einer Seite Text noch etwas Platz zu haben, kann man z.B. "
+            "'zeilen auto -3' einstellen.\n"
+            "Die Voreinstellung ist 20 Zeilen.\n"
+            "Mit 'zeilen abs[olut]' und 'zeilen rel[ativ]' kannst Du fest"
+            "legen, ob im Prompt bei langen Texten die aktuelle Zeilennummer "
+            "oder eine prozentuale Angabe ausgegeben wird.\n"
+            "Deine aktuelle Einstellung ist %d%s Zeilen (%s).",
+            QueryProp(P_SCREENSIZE),
+            Query(P_SCREENSIZE) < 0 ? " 'automatische'" : "",
+            QueryProp(P_MORE_FLAGS) & E_ABS ? "absolut" : "relativ"),78,0,1));
+    return 1;
+  }
+
+  SetProp( P_SCREENSIZE, size );
+
+  printf( "Okay, Deine Zeilenzahl steht nun auf %d.\n", size );
+  return 1;
+}
+
+static int _query_screensize()
+{
+    int sz,rows;
+
+    if ( (sz=Query(P_SCREENSIZE)) >= 0 )
+        return sz;
+
+    if ( !rows=QueryProp(P_TTY_ROWS) )
+        return 0;
+
+    return (rows+=sz) >= 5 ? rows : 5;
+}
+
+static int presay(string str)
+{
+  if (!str=_unparsed_args())
+    write("Dein Presay ist jetzt geloescht.\n");
+  else
+    printf("Dein Presay lautet jetzt: \"%s\".\n",str=capitalize(str));
+  SetProp(P_PRESAY,str);
+  return 1;
+}
+
+static int sethands(string str)
+{
+  mixed *hands;
+
+  if (!(str=_unparsed_args()))
+  {
+    write("sethands <message>\n");
+    return 1;
+  }
+  if (str=="0")
+      hands=RaceDefault(P_HANDS);
+  if (!hands || !pointerp(hands))
+      hands=Query(P_HANDS);
+  hands[0]=" "+str;
+  Set(P_HANDS,hands);
+  write("Ok.\n");
+  return 1;
+}
+
+static int inform(string str)
+{
+  switch (str) {
+    case "on":
+    case "ein":
+    case "an":
+      if (Query(P_INFORMME))
+    write("Das hattest Du schon so eingestellt.\n");
+      else
+      {
+    write("Kuenftig wirst Du informiert, wenn jemand das "MUDNAME" verlaesst/betritt.\n");
+    Set(P_INFORMME,1);
+      }
+      return 1;
+    case "aus":
+    case "off":
+      if (!Query(P_INFORMME))
+    write("Das hattest Du schon so eingestellt.\n");
+      else
+      {
+    write("Ok.\n");
+    Set(P_INFORMME,0);
+      }
+      return 1;
+    case 0:
+      write("Inform-Mode ist "+(Query(P_INFORMME)?"an":"aus")+"geschaltet.\n");
+      return 1;
+    }
+  write("inform an oder inform aus, bitte.\n");
+  return 1;
+}
+
+void delayed_write(mixed *what)
+{
+  if (!pointerp(what)||!sizeof(what)||!pointerp(what[0]))
+    return;
+  tell_object(ME,what[0][0]);
+  if (sizeof(what)>1&&sizeof(what[0])>1)
+    call_out("delayed_write",what[0][1],what[1..]);
+}
+
+void notify_player_change(string who, int rein, int invis)
+{
+  string *list,name;
+  mixed mlist;
+
+  if (invis) name="("+who+")";
+    else name=who;
+
+  if (Query(P_INFORMME))
+  {
+      if (rein)
+        tell_object(ME,name+" ist gerade ins "MUDNAME" gekommen.\n");
+      else
+        tell_object(ME,name+" hat gerade das "MUDNAME" verlassen.\n");
+  }
+
+  if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
+
+  if(pointerp(list=Query(P_WAITFOR)) && sizeof(list) && member(list,who)!=-1)
+  {
+    if (!QueryProp(P_VISUALBELL))
+        name+=sprintf("%c",7); // Char fuer Pieps an den String anhaengen.
+    // Moechte der Spieler keine ASCII-Grafik sehen, wird diese Meldung ohne
+    // Leerzeichen formatiert, so dass sie von Screenreadern vorgelesen wird.
+    // Anderenfalls wuerde sie einzeln buchstabiert.
+    if ( QueryProp(P_NO_ASCII_ART) )
+    {
+      delayed_write( ({ ({ sprintf("%s IST JETZT %sDA !!!\n", 
+                           name, (rein?"":"NICHT MEHR ")) }) }) );
+    }
+    else 
+    {
+      delayed_write( ({ ({ sprintf("%s   I S T   J E T Z T   %sD A !!!\n",
+                           name, (rein?"":"N I C H T   M E H R   ")) }) }) );
+    }
+  }
+
+  if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
+     (mappingp(mlist)) && (mlist[who]))
+        Show_WaitFor_Reason(who,invis);
+}
+
+static int erwarte(string str)
+{
+  string *list,*str1;
+  mixed mlist;
+  int i;
+
+  if (!mappingp(mlist=QueryProp(P_WAITFOR_REASON)))
+     mlist=([]);
+  if (!pointerp(list=Query(P_WAITFOR)))
+     list=({});
+
+  if (!str || str=="-u")
+  {
+     if(Query(P_WAITFOR_FLAGS)&0x01)
+       write("Du hast 'erwarte' temporaer deaktiviert.\n");
+     write("Du erwartest jetzt");
+     if (!sizeof(list))
+        write(" niemanden mehr.\n");
+     else
+     {
+        write(":\n");
+        if (!str) list=sort_array(list,#'>);
+        More(break_string(CountUp(list),78));
+     }
+     return 1;
+  }
+  if(str=="aus"){
+    Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)|0x01);
+    write("Erwarte ist jetzt deaktiviert.\n");
+    return 1;
+  }
+  if(str=="an" || str=="ein"){
+    Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)&0xFE);
+    write("Erwarte ist jetzt aktiv.\n");
+    return 1;
+  }
+
+  str1=explode(_unparsed_args()||""," ");
+  if (sizeof(str1)==1)
+  {
+     if (str1[0]!="wegen")
+     {
+        str=capitalize(lower_case(str));
+        if (member(list,str)!=-1)
+        {
+           SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,str));
+           list-=({str});
+           write(str+" aus der Liste entfernt.\n");
+        } else
+        {
+           if (sizeof(list)>1000)
+           {
+             write("Du erwartest schon genuegend!\n");
+             return 1;
+           }
+           list+=({str});
+           write(str+" an die Liste angehaengt.\n");
+        }
+        Set(P_WAITFOR,list);
+     }
+     else
+     {
+        if (sizeof(mlist) && sizeof(list=m_indices(mlist)))
+        {
+           write("Du erwartest aus einem bestimmten Grund:\n");
+           write(break_string(CountUp(sort_array(list,#'>))+".",78));
+        }
+        else write("Du erwartest niemanden aus einem bestimmten Grund.\n");
+     }
+     return 1;
+  }
+  notify_fail("Falsche Syntax, siehe 'hilfe erwarte'!\n");
+  if (str1[1]!="wegen") return 0;
+  if (sizeof(str1)==2)
+     Show_WaitFor_Reason(capitalize(lower_case(str1[0])),0);
+  else {
+     string s=capitalize(lower_case(str1[0]));
+     if (sizeof(str1)==3 && (str1[2]=="nichts" || str1[2]=="loeschen"))
+        if (!mlist[s])
+           write("Du hast "+s+" aus keinem bestimmten Grund erwartet!\n");
+        else
+        {
+           SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,s));
+           write("Du erwartest "+s+" aus keinem bestimmten Grund mehr!\n");
+        }
+     else
+     {
+        if (IS_ARCH(ME)) i=80; else if (IS_LEARNER(ME)) i=40;
+        else if (IS_SEER(ME)) i=20; else i=10;
+        if (!mlist[s] && sizeof(mlist)>=i)
+           write("Sorry, aber Du erwartest schon genuegend Leute!\n");
+        else
+        {
+           SetProp(P_WAITFOR_REASON,mlist+([s:implode(str1[2..]," ")]));
+           Show_WaitFor_Reason(s,0);
+        }
+     }
+  }
+  return 1;
+}
+
+static int uhrmeldung(string str)
+{
+  if (!(str=_unparsed_args()))
+  {
+    str=QueryProp(P_CLOCKMSG);
+    if (!str)
+    {
+      write("Du hast die Standard-Uhrmeldung.\n");
+      return 1;
+    }
+        if( !stringp(str) ) str = sprintf("%O\n",str);
+    printf("Deine Uhrmeldung ist:\n%s\n",str[0..<2]);
+    return 1;
+  }
+  if (str=="0")
+  {
+    SetProp(P_CLOCKMSG,0);
+    write("Ok, Du hast jetzt wieder die Standard-Meldung.\n");
+    return 1;
+  }
+  if (sizeof(explode(str,"%d"))>2)
+  {
+    write("Fehler, es darf nur ein %d in der Meldung vorkommen.\n");
+    return 1;
+  }
+  /* Mehrere %-Parameter verursachen das Abschalten der Uhr zur vollen Stunde.
+   */
+  if (sizeof(explode(str,"%"))>2)
+  {
+    write("Fehler: Zuviele %-Parameter in der Meldung.\n");
+    return 1;
+  }
+  /* Nur ein %-Parameter, aber der falsche: nicht sinnvoll. */
+  else
+  {
+    int i = strstr(str,"%",0);
+    if ( i>-1 && ( i==sizeof(str)-1 || str[i+1]!='d'))
+    {
+      write("Fehler: Falscher %-Parameter in der Meldung.\n");
+      return 1;
+    }
+  }
+  str+="\n";
+  SetProp(P_CLOCKMSG,str);
+  write("Ok.\n");
+  return 1;
+}
+
+static int zeitzone(string str)
+{
+  int zt;
+  if(!str || str==""){
+    if(!(zt=QueryProp(P_TIMEZONE)))
+      write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
+            "eingestellt.\n");
+    else if(zt>0)
+      printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
+             "eingestellt.\n",zt);
+    else
+      printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
+             "Berlin eingestellt.\n",-zt);
+    return 1;
+  }
+  if(sscanf(str,"utc %d",zt)==1)  zt=(zt-1)%24;
+  else zt=to_int(str)%24;
+
+  SetProp(P_TIMEZONE,zt);
+
+  if(!zt)
+    write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
+          "eingestellt.\n");
+  else if(zt>0)
+    printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
+           "eingestellt.\n",zt);
+  else
+    printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
+           "Berlin eingestellt.\n",-zt);
+  return 1;
+}
+
+static int emailanzeige(string str){
+  notify_fail("Syntax: emailanzeige [alle|freunde|niemand]\n");
+  if(!str || str==""){
+    if(!(str=QueryProp(P_SHOWEMAIL)))str="Niemandem";
+    else if(str=="alle")str="allen";
+    else if(str=="freunde")str="Deinen Freunden";
+    else if(str=="niemand")str="niemandem";
+    else{
+      SetProp(P_SHOWEMAIL,0);
+      str="Niemandem";
+    }
+    write("Deine Email wird "+str+" angezeigt.\n");
+    return 1;
+  }
+  else if(member(({"alle","freunde","niemand"}),str)==-1)return 0;
+
+  SetProp(P_SHOWEMAIL,str);
+
+  if(str=="alle")str="allen";
+  else if(str=="freunde")str="Deinen Freunden";
+  else str="niemandem";
+  write("Deine Email wird "+str+" angezeigt.\n");
+  return 1;
+}
+
+static int zaubertraenke()
+{
+  More("/room/orakel"->TipListe());
+  return 1;
+}
+
+varargs static int angriffsmeldung(string arg) {
+  if (arg=="ein" || arg=="an")
+    SetProp(P_SHOW_ATTACK_MSG,1);
+  else if (arg=="aus")
+    SetProp(P_SHOW_ATTACK_MSG,0);
+  if (QueryProp(P_SHOW_ATTACK_MSG))
+    write("Du siehst saemtliche Angriffsmeldungen von Dir.\n");
+  else
+    write("Du siehst nur neue Angriffsmeldungen von Dir.\n");
+  return 1;
+}
+
+static mixed _query_localcmds()
+{
+  return ({({"zeilen","set_screensize",0,0}),
+           ({"email","set_email",0,0}),
+           ({"url","set_homepage",0,0}),
+           ({"icq","set_icq",0,0}),
+           ({"messenger", "set_messenger", 0, 0}), 
+           ({"ort","set_location",0,0}),
+           ({"punkte","short_score",0,0}),
+           ({"score","short_score",0,0}),
+           ({"info","score",0,0}),
+           ({"kurzinfo","very_short_score",0,0}),
+           ({"quit","new_quit",0,0}),
+           ({"ende","new_quit",0,0}),
+           ({"disconnect","disconnect",0,0}),
+           ({"schlaf","disconnect",1,0}),
+           ({"speichern","save_character",0,0}),
+           ({"save","save_character",0,0}),
+           ({"toete","kill",0,0}),
+           ({"angriffsmeldung","angriffsmeldung",0,0}),
+           ({"passw","change_password",1,0}),
+           ({"hilfe","help",1,0}),
+           ({"selbstloeschung","self_delete",0,0}),
+           ({"spielpause","spielpause",0,0}),
+           ({"spieldauer","spieldauer",0,0}),
+           ({"idee","idea",0,0}),
+           ({"typo","typo",0,0}),
+           ({"bug","bug",0,0}),
+           ({"fehler","fehlerhilfe",0,0}),
+           ({"md","md",0,0}),
+           ({"detail","md",0,0}),
+           ({"vorsicht","toggle_whimpy",0,0}),
+           ({"stop","stop",0,0}),
+           ({"kwho","kwho",0,0}),
+           ({"kwer","kwho",0,0}),
+           ({"kkwer","kkwho",0,0}),
+           ({"kkwho","kkwho",0,0}),
+           ({"who","who",0,0}),
+           ({"wer","who",0,0}),
+           ({"zeit","uhrzeit",0,0}),
+           ({"uhrzeit","uhrzeit",0,0}),
+           ({"weg","weg",0,0}),
+           ({"wegmeldung", "wegmeldung", 0, 0}),
+           ({"idlezeit", "idlezeit", 0, 0}),
+           ({"finger","finger",0,0}),
+           ({"muds","muds",0,0}),
+           ({"emote","emote",0,0}),
+           ({":","emote",1,0}),
+           ({";","emote",1,0}),
+           ({"remote","remote",0,SEER_LVL}),
+           ({"r:","remote",1,0}),
+           ({"r;","gremote",1,0}),
+           ({"titel","set_title",0,0}),
+           ({"review","review",0,SEER_LVL}),
+           ({"setmin","setmin",0,SEER_LVL}),
+           ({"setmout","setmout",0,SEER_LVL}),
+           ({"setmmin","setmmin",0,SEER_LVL}),
+           ({"setmmout","setmmout",0,SEER_LVL}),
+           ({"sethands","sethands",0,SEER_LVL}),
+           ({"presay","presay",0,SEER_LVL}),
+           ({"extralook","extralook",0,SEER_LVL}),
+           ({"fluchtrichtung","toggle_whimpy_dir",0,SEER_LVL}),
+           ({"inform","inform",0,0}),
+           ({"erwarte","erwarte",0,0}),
+           ({"stty","stty",0,0}),
+           ({"grafik", "set_ascii_art", 0, 0}), 
+           ({"uhrmeldung","uhrmeldung",0,0}),
+           ({"zeitzone","zeitzone",0,0}),
+           ({"behalte","behalte",0,0}),
+           ({"zweitiemarkierung","zweitiemarkierung",0,0}),
+           ({"emailanzeige","emailanzeige",0,0}),
+           ({"topliste","topliste",0,0}),
+           ({"ton","set_visualbell",0,0}),
+           ({"telnegs","show_telnegs",0,0}),
+           ({"spotte", "spotte", 0, 0}),
+           ({"reise","reise",0,0}),
+           ({"zaubertraenke","zaubertraenke",0,0}),
+           ({"telnet","telnet_cmd",0,0}),
+     })+
+     command::_query_localcmds()+
+     viewcmd::_query_localcmds()+
+     comm::_query_localcmds()+
+     skills::_query_localcmds()+
+     description::_query_localcmds();
+}
+
+static int _check_keep(object ob)
+{
+  return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
+}
+
+static mixed _set_testplayer(mixed arg) {
+  mixed res;
+  object setob;
+
+  setob=this_player();
+  if (!objectp(setob) || !query_once_interactive(setob))
+    setob=this_interactive();
+  if (!objectp(setob))
+    setob=previous_object();
+  if (setob && !IS_DEPUTY(setob)) {
+    arg=geteuid(setob);
+    if (!arg || arg=="NOBODY")
+      arg=getuid(setob);
+    arg=capitalize(arg);
+  }
+  res=Set(P_TESTPLAYER,arg);
+  Set(P_TESTPLAYER,PROTECTED,F_MODE_AS);
+  return res;
+}
+
+int zweitiemarkierung(string arg)
+{
+  if (!QueryProp(P_SECOND))
+    return _notify_fail("Aber Du bist doch gar kein Zweiti.\n"),0;
+  notify_fail("Syntax: zweitiemarkierung [unsichtbar|sichtbar|name]\n");
+  if (!arg)
+    return 0;
+  switch (arg)
+  {
+    case "unsichtbar" :
+      SetProp(P_SECOND_MARK,-1);
+      write("Jetzt sieht kein Spieler mehr, dass Du ein Zweiti bist.\n");
+      return 1;
+    case "sichtbar" :
+      SetProp(P_SECOND_MARK,0);
+      write("Jetzt sieht kein Spieler mehr, wessen Zweiti Du bist.\n");
+      return 1;
+    case "name" :
+      SetProp(P_SECOND_MARK,1);
+      write("Jetzt koennen alle sehen, wessen Zweiti Du bist.\n");
+      return 1;
+  }
+  return 0;
+}
+
+int topliste(string arg)
+{
+    if (!arg)
+    {
+        printf("Du hast Dich fuer die Topliste %s.\n",
+            (QueryProp(P_NO_TOPLIST) ? "gesperrt" : "freigegeben"));
+        return 1;
+    }
+    else if (member(({"j","ja","n","nein"}),arg)==-1)
+        return _notify_fail("Syntax: topliste [ja|nein]\n"),0;
+    if (arg[0]=='j')
+    {
+        SetProp(P_NO_TOPLIST,0);
+        write("Du kannst jetzt (theoretisch) in der Topliste auftauchen.\n");
+    }
+    else
+    {
+        SetProp(P_NO_TOPLIST,1);
+        write("Du wirst jetzt nicht in der Topliste auftauchen.\n");
+    }
+    Set(P_NO_TOPLIST,SAVE|PROTECTED,F_MODE_AS);
+    return 1;
+}
+
+int show_telnegs(string arg)
+{
+    if (!arg)
+    {
+        write("Du bekommst Aenderungen Deiner Fenstergroesse "+
+              (QueryProp(P_TTY_SHOW)?"":"nicht ")+"angezeigt.\n");
+        return 1;
+    }
+    if (member(({"ein","an","aus"}),arg)==-1)
+    {
+        write("Syntax: telnegs [ein|aus]\n");
+        return 1;
+    }
+    if (arg=="ein" || arg=="an")
+    {
+        write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"":"nun ")+
+              "Aenderungen Deiner Fenstergroesse angezeigt.\n");
+        Set(P_TTY_SHOW,1);
+        return 1;
+    }
+    write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"nun ":"")+
+          "Aenderungen Deiner Fenstergroesse nicht "+
+          (QueryProp(P_TTY_SHOW)?"mehr ":"")+"angezeigt.\n");
+    Set(P_TTY_SHOW,0);
+    return 1;
+}
+
+private int set_keep_alive(string str) {
+  if (str == "ein") {
+    telnet_tm_counter = 240 / __HEART_BEAT_INTERVAL__;
+    tell_object(this_object(), break_string(
+        "An Deinen Client werden jetzt alle 4 Minuten unsichtbare Daten "
+        "geschickt, um zu verhindern, dass Deine Verbindung zum "MUDNAME
+        " beendet wird.", 78));
+  }
+  else if (str == "aus") {
+    telnet_tm_counter = 0;
+    tell_object(this_object(),break_string(
+        "Du hast das Senden von unsichtbaren Daten (Keep-Alive-Pakete) an "
+        "Deinen Client ausgeschaltet.",78));
+  }
+  else {
+    if (!telnet_tm_counter)
+      tell_object(this_object(), break_string(
+        "An Deinen Client werden keine Keep-Alive-Pakete geschickt.",78));
+    else
+      tell_object(this_object(), break_string(
+        "An Deinen Client werden alle 4 Minuten " 
+        "unsichtbare Daten geschickt, damit Deine Verbindung "
+        "zum "MUDNAME" nicht beendet wird.",78));
+  }
+  return 1;
+}
+
+private int print_telnet_rttime() {
+  int rtt = QueryProp(P_TELNET_RTTIME);
+  if (rtt>0)
+    tell_object(ME, break_string(
+      "Die letzte gemessene 'round-trip' Zeit vom MG zu Deinem Client "
+      "und zurueck betrug " + rtt + " us.",78));
+  else
+    tell_object(ME, break_string(
+      "Bislang wurde die 'round-trip' Zeit vom MG zu Deinem Client "
+      "noch nicht gemessen oder Dein Client unterstuetzt dieses "
+      "nicht.",78));
+  return 1;
+}
+
+int telnet_cmd(string str) {
+  if (!str) return 0;
+  string *args = explode(str, " ");
+  string newargs;
+  if (sizeof(args) > 1)
+    newargs = implode(args[1..], " ");
+  else
+    newargs = "";
+
+  switch(args[0])
+  {
+    case "keepalive":
+      return set_keep_alive(newargs);
+    case "rttime":
+      return print_telnet_rttime();
+  }
+  return 0;
+}
+
+int spotte( string str )
+{
+    _notify_fail( "Hier ist nichts, was Du verspotten koenntest!\n" );
+    return 0;
+}
+
+int behalte(string str)
+{
+  object ob,*obs;
+  string s;
+
+  if (str)
+  {
+    if (str=="alles") {
+      filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, getuid());
+      write("Ok!\n");
+      return 1;
+    }
+    if (str=="nichts") {
+      filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, 0);
+      write("Ok!\n");
+      return 1;
+    }
+    if (!sizeof(obs=find_obs(str,PUT_GET_NONE)))
+    {
+      _notify_fail("Aber sowas hast Du nicht dabei!\n");
+      return 0;
+    }
+    else ob=obs[0];
+
+    if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
+        ob->SetProp(P_KEEP_ON_SELL,0);
+    else
+        ob->SetProp(P_KEEP_ON_SELL,geteuid(ME));
+
+    // erneut abfragen, da sich der Wert nicht geaendert haben muss
+    if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
+        write(break_string(sprintf("Ok, Du wirst %s jetzt bei 'verkaufe alles' "
+                                   "behalten.\n",ob->name(WEN)),78));
+    else
+        write(break_string(sprintf("Ok, Du wirst %s beim naechsten 'verkaufe "
+                                   "alles' mitverkaufen!\n",ob->name(WEN)),78));
+
+    return 1;
+  }
+  s=make_invlist(ME,filter(all_inventory(ME),#'_check_keep)); //'));
+  More(s);
+  return 1;
+}
+
+static int _query_lep()
+{
+  int val;
+  val = LEPMASTER->QueryLEP();
+  Set( P_LEP, val );
+  return val;
+}
+
+static mixed _set_fraternitasdonoarchmagorum(mixed arg)
+{
+  if (!intp(arg)) return -1;
+
+  if ((!previous_object(1)||object_name(previous_object(1))!=FAO_MASTER) && 
+      (!this_interactive() || !IS_ARCH(this_interactive())))
+    return -1;
+
+  if (!intp(arg)) return -1;
+
+  log_file("fao/P_FAO",sprintf("%s - %s P_FAO gesetzt auf %O\n",
+        dtime(time()),query_real_name(),arg) );
+  return Set(P_FAO,arg);
+}
+
+nomask void set_realip(string str)
+{
+  if(previous_object() && strstr(object_name(previous_object()),"/secure")==0)
+  {
+    realip=str;
+  }
+}
+
+nomask string query_realip()
+{
+  return realip ? realip : "";
+}
+
+mixed _query_netdead_env() {
+        return ndead_location || ndead_l_filename;
+}
diff --git a/std/player/channel.c b/std/player/channel.c
new file mode 100644
index 0000000..9f8512a
--- /dev/null
+++ b/std/player/channel.c
@@ -0,0 +1,579 @@
+// MorgenGrauen MUDlib
+//
+// channel.c -- channel client
+//
+// $Id: channel.c 9404 2015-12-13 00:21:44Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <util.h>
+#include <thing/properties.h>
+#include <living/comm.h>
+#include <player.h>
+#include <player/comm.h>
+#include <daemon.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+#include <wizlevels.h>
+#include <defines.h>
+#include <properties.h>
+#include <sys_debug.h>
+#include <regexp.h>
+
+#define P_SWAP_CHANNELS  "swap_channels"
+#define P_CHANNEL_SHORT  "short_channels"
+
+#define CHANNELCMDS      "[#@%$&()<>a-zA-Z0-9\\-]"
+
+#define DEFAULT_CHANNELS ({"Abenteuer", "Anfaenger","Grats","Tod", "ZT"})
+#define DEFAULT_SHORTCUTS \
+([                     \
+ "b":"Abenteuer",      \
+ "a":"Allgemein",      \
+ "B":"Beileid",        \
+ "q":"D-chat",         \
+ "G":"Grats",          \
+ "M":"Moerder",        \
+ "h":"Seher",          \
+ "T":"Tod",            \
+])
+
+#define WIZARD_SHORTCUTS \
+([                     \
+ "P":"D-code",         \
+ "D":"Debug",          \
+ "O":"Intercode",      \
+ "I":"Intermud",       \
+ "m":"Magier",         \
+])
+
+
+private nosave mapping shortcut;
+private nosave int c_status;
+
+void create()
+{
+  Set(P_CHANNELS, SAVE, F_MODE);
+  Set(P_CHANNELS, DEFAULT_CHANNELS);
+  Set(P_SWAP_CHANNELS, SAVE, F_MODE);
+  Set(P_STD_CHANNEL, "Allgemein");
+  Set(P_STD_CHANNEL, SAVE, F_MODE);
+  Set(P_CHANNEL_SHORT, SAVE, F_MODE);
+  Set(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
+      + (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
+}
+
+static mixed _query_localcmds()
+{
+  return ({({"-","ChannelParser", 1, 0}),
+           ({"ebene", "ChannelAdmin", 0, 0}),
+           ({"ebenen", "ChannelAdmin", 1, 0}),
+         });
+}
+
+mixed RegisterChannels()
+{
+  mixed err;
+  if(extern_call() &&
+     previous_object() != find_object(CHMASTER)) return;
+  c_status = 0;
+  shortcut = QueryProp(P_CHANNEL_SHORT);
+  SetProp(P_CHANNELS, map(QueryProp(P_CHANNELS) || ({}),
+	#'lower_case));
+  err = filter(QueryProp(P_CHANNELS),
+         symbol_function("join", CHMASTER),
+         this_object());
+  if(QueryProp(P_LEVEL) < 5) return err;
+  while(sizeof(err)) {
+    CHMASTER->new(err[0], this_object());
+    err[0..0] = ({});
+  }
+  return err;
+}
+
+mixed RemoveChannels()
+{
+  closure cl;
+  mixed err=({});
+  if(extern_call() &&
+     previous_object() != find_object(CHMASTER)) return;
+  if(!c_status) c_status = 1;
+  else return ({});
+  cl=symbol_function("leave", CHMASTER);
+  if (closurep(cl)) {
+      err = filter(QueryProp(P_CHANNELS), cl, this_object());
+      SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - err);
+  }
+  return err;
+}
+
+varargs private string getName(mixed x, int fall) {
+  
+  mixed o = closurep(x) ? query_closure_object(x) : x;
+  if(stringp(o) && sizeof(o) && (x = find_object(o))) 
+    o = x;
+  
+  // Objekte
+  if (objectp(o)) {
+    // Magier sehen unsichtbare nicht nur als "Jemand"
+    if (o->QueryProp(P_INVIS) && IS_LEARNING(this_object()))
+      return "("+capitalize(getuid(o))+")";
+    // Froesche mit Namen versorgen.
+    if (o->QueryProp(P_FROG))
+      return "Frosch "+capitalize(getuid(o));
+    // Default (Unsichtbare als "Jemand" (s. Name()))
+    return o->Name(fall, 2)||"<Unbekannt>";
+  }
+  // Strings
+  else if (stringp(o) && sizeof(o)) {
+    if (o[0] == '/') {
+      // unsichtbare Objekte...
+      int p = strstr(o, "$");
+      if (p != -1) {
+	// Magier im Magiermodus kriegen den Realnamen, andere nicht.
+	if (IS_LEARNING(this_object()))
+	  return o[1..p-1];
+	else
+	  return o[p+1..];
+      }
+      else
+	// doch nicht unsichtbar
+	return (fall == WESSEN ? o+"s" : o);
+    }
+    else
+      // nicht unsichtbar
+      return (fall == WESSEN ? o+"s" : o);
+  }
+  // Fall-through
+  return "<Unbekannt>";
+}
+
+// <nonint> unterdrueckt die AUsgabe an den Spieler und liefert den Text
+// zurueck. Wird nur fuer die Ebenenhistory benutzt. 
+string ChannelMessage(mixed* msg, int nonint)
+{
+  string channel_message;
+  string channel=msg[0];
+  object sender=msg[1];
+  string message=msg[2];
+  int msg_type = msg[3];
+
+  if ( previous_object() != find_object(CHMASTER) &&
+       previous_object() != ME )
+      return 0;
+
+  string sender_name = getName(sender, msg_type == MSG_GEMOTE ? WESSEN : WER);
+  int prepend_indent_flag=QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0;
+  switch(msg_type) {
+  case MSG_EMPTY:
+    channel_message= message+"\n";
+    break;
+  case MSG_GEMOTE:
+  case MSG_EMOTE:
+    channel_message = break_string(sender_name + " "+ message+"]",
+                   78, sprintf("[%s:", channel),
+                   BS_INDENT_ONCE|prepend_indent_flag);
+    break;
+  case MSG_SAY:
+  default:
+    string presay=sprintf("[%s:%s] ", channel, sender_name);
+    channel_message = break_string(message, max(78,sizeof(presay)+10),
+                   presay, prepend_indent_flag);
+    break;
+  }
+  if(nonint)
+    return channel_message;
+
+  // Wenn GMCP sich um Uebertragung der Nachricht kuemmert, wird ReceiveMsg()
+  // nicht mehr aufgerufen.
+  if (GMCP_Channel(channel_message, channel, sender_name) != 1)
+  {
+    // Der Ebenenname muss in Kleinbuchstaben uebergeben werden, damit die
+    // Ignorierepruefung funktioniert. Die ignorierestrings sind naemlich alle
+    // kleingeschrieben.
+    ReceiveMsg(channel_message, 
+               MT_COMM|MT_FAR|MSG_DONT_STORE|MSG_DONT_WRAP,
+               MA_CHANNEL"." + lower_case(channel), 0, sender);
+  }
+  return 0;
+}
+
+private void createList(string n, mixed a, mixed m, mixed l)
+{
+  int pos; string sh, *mem;
+  if((pos = member(map(m_values(shortcut), #'lower_case/*'*/), n)) != -1)
+    sh = m_indices(shortcut)[pos];
+  else sh = "";
+  mem=map(a[I_MEMBER],#'getName/*'*/, WER);
+  mem-=({"<MasteR>"});
+  l += ({ sprintf("%-12.12'.'s %c[%-1.1s] %|12.12' 's (%-|3' 'd) %-42.42s\n",
+                  a[I_NAME], (member(m, n) != -1 ? '*' : ' '), sh,
+                  a[I_MASTER] ?
+                  getName(a[I_MASTER]) : getName(a[I_ACCESS]),
+                  sizeof(mem),
+            (closurep(a[I_INFO]) && objectp(query_closure_object(a[I_INFO]))) ?
+                  funcall(a[I_INFO]) || "- Keine Beschreibung -" :
+                  (stringp(a[I_INFO]) ? a[I_INFO] : "- Keine Beschreibung -")
+        ) });
+}
+
+private mixed getChannel(string ch)
+{
+  mixed ff;
+  if(!sizeof(ch)) ch = QueryProp(P_STD_CHANNEL);
+  if(shortcut && shortcut[ch]) ch = shortcut[ch];
+  return CHMASTER->find(ch, this_object());
+}
+
+#ifndef DEBUG
+#define DEBUG(x)        if (funcall(symbol_function('find_player),"zesstra"))\
+          tell_object(funcall(symbol_function('find_player),"zesstra"),\
+	              "MDBG: "+x+"\n")
+#endif
+int ChannelParser(string args)
+{
+  mixed ch, cmd, tmp;
+  int pos, type, err;
+  string txt;
+  cmd = query_verb();
+  args = _unparsed_args();
+  notify_fail("Benutzung: -<Ebene>[ ]['|:|;]<Text>\n"
+              "           -<Ebene>[+|-|?|!|*]\n"
+              "           -?\n");
+  if(!cmd && !args) return 0;
+  if(!args) args = "";
+  cmd = cmd[1..];
+  if(sizeof(cmd = regexplode(cmd,
+                             "^" CHANNELCMDS "*"
+                             "([+-]|\\!|\\?|\\*)*")) > 1)
+  {
+    //z.B. cmd= ({"","allgemein",":testet"})
+    if(sizeof(cmd[1]) > 1 &&
+       strstr("+-?!*", cmd[1][<1..<1]) > -1)
+      tmp = cmd[1][0..<2];
+    else
+      tmp = cmd[1];
+    if(cmd[1] != "?" && cmd[1] != "!")
+      if(pointerp(ch = getChannel(tmp)))
+      {
+        notify_fail("Diese Angabe war nicht eindeutig! "
+                    "Folgende Ebenen passen:\n"
+                    +implode(ch, ", ")+"\n");
+        return 0;
+      }
+      else if(!ch) return (notify_fail("Die Ebene '"+tmp
+                                       +"' gibt es nicht!\n"), 0);
+    //DEBUG(sprintf("ChanCmd: %O\n",cmd));
+    if (sizeof(cmd[1])) {
+      switch(cmd[1][<1]) {
+    case '+':
+      switch(CHMASTER->join(ch, this_object()))
+      {
+      case E_ACCESS_DENIED:
+        notify_fail("Du darfst an die Ebene '"+ch+"' nicht heran.\n");
+        return 0;
+      case E_ALREADY_JOINED:
+        notify_fail("Du hast diese Ebene schon betreten!\n");
+        return 0;
+      default: break;
+      }
+      write("Du betrittst die Ebene '"+ch+"'.\n");
+      if(member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
+        SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
+      return 1;
+    case '-':
+      switch(CHMASTER->leave(ch, this_object()))
+      {
+      case E_ACCESS_DENIED:
+        write("Du kannst die Ebene '"+ch+"' nicht verlassen.\n");
+        break;
+      case E_NOT_MEMBER:
+        write("Wie willst Du eine Ebene verlassen, welche Du nicht "
+              "betreten hast?\n");
+        break;
+      default:
+        write("Du verlaesst die Ebene '"+ch+"'.\n");
+        SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - ({ lower_case(ch), ch }));
+        break;
+      }
+      return 1;
+    case '!':
+    case '?':
+    {
+      mapping l;
+      if(mappingp(l = CHMASTER->list(this_object())))
+        if(stringp(ch) && sizeof(ch) && pointerp(l[ch = lower_case(ch)]))
+        {
+          int c; object o; string n; string *m;
+          m=sort_array(map(l[ch][I_MEMBER],#'getName/*'*/, WER),#'>/*'*/);
+          m-=({"<MasteR>"});
+          write(l[ch][I_NAME]+", "+funcall(l[ch][I_INFO])+".\n");
+          write("Du siehst "+((c = sizeof(m)) > 0
+                              ? (c == 1 ? "ein Gesicht" : c+" Gesichter")
+                              : "niemanden")+" auf der Ebene '"
+                                +l[ch][I_NAME]+"':\n");
+          write(break_string(implode(m,", "), 78));
+          write((l[ch][I_MASTER] ?
+                 getName(l[ch][I_MASTER]) : getName(l[ch][I_ACCESS], WER))
+                +" hat das Sagen auf dieser Ebene.\n");
+        }
+        else
+        {
+          mixed list; list = ({});
+          if(cmd[1][<1] == '!')
+            l -= mkmapping(m_indices(l) - QueryProp(P_CHANNELS));
+          walk_mapping(l, #'createList/*'*/, QueryProp(P_CHANNELS), &list);
+          list = sort_array(list, #'>/*'*/);
+          txt = sprintf("%-12.12' 's  [A] %|12' 's (%-3' 's) %-42.42s\n",
+                        "Name", "Eigner", "Sp", "Beschreibung")
+              + "-------------------------------------------------------"
+              + "-----------------------\n"
+              + implode(list, "");
+          More(txt);
+        }
+      return 1;
+    }
+    case '*':
+    {
+      mixed hist; int amount;
+      if(!pointerp(hist = CHMASTER->history(ch, this_object())) 
+		      || !sizeof(hist))
+      {
+        write("Es ist keine Geschichte fuer '"+ch+"' verfuegbar.\n");
+        return 1;
+      }
+      
+      //(Zesstra) cmd hat offenbar immer 3 Elemente...
+      //bei -all* ({"","all*",""})
+      //bei -all*10 ({"","all*,"10"})
+      //also ist bei -all* amount immer == 0 und es funktioniert eher zufaellig.
+      /*if(sizeof(cmd) > 2) 
+        amount = to_int(cmd[2]);
+      else 
+        amount=sizeof(hist);*/
+      amount=to_int(cmd[2]);
+      if (amount <= 0 || amount >= sizeof(hist))
+        amount=sizeof(hist);
+
+      txt = "Folgendes ist auf '"+ch+"' passiert:\n"
+          + implode(map(hist[<amount..], #'ChannelMessage/*'*/, 1), "");
+      More(txt);
+      return 1;
+    }
+    default:
+      break;
+    }
+    }
+  }
+  if(sizeof(cmd = implode(cmd[2..], "")))
+     args = cmd + (sizeof(args) ? " " : "") + args;
+
+  // KOntrollchars ausfiltern.
+  args = regreplace(args,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
+  if(!sizeof(args)) return 0;
+
+  //Wenn cmd leer ist: MSG_SAY
+  if (!sizeof(cmd)) type=MSG_SAY;
+  else {
+    switch(cmd[0])
+    {
+    case ':' :
+      type = MSG_EMOTE;
+      args = args[1..];
+      break;
+    case ';' :
+      type = MSG_GEMOTE;
+      args = args[1..];
+      break;
+    case '\'':
+      args = args[1..];
+    default  : type = MSG_SAY; break;
+    }
+  }
+  if(!ch || !sizeof(ch)) ch = QueryProp(P_STD_CHANNEL);
+  if((err = CHMASTER->send(ch, this_object(), args, type)) < 0)
+    if(!(err = CHMASTER->join(ch, this_object())))
+    {
+      if(member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
+        SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
+      err = CHMASTER->send(ch, this_object(), args, type);
+    }
+
+  switch(err)
+  {
+  case E_ACCESS_DENIED:
+    notify_fail("Auf der Ebene '"+ch+"' darfst Du nichts sagen.\n");
+    return 0;
+  case E_NOT_MEMBER:
+    notify_fail("Du hast die Ebene '"+ch+"' nicht betreten!\n");
+    return 0;
+  }
+  return 1;
+}
+
+int ChannelAdmin(string args)
+{
+  string n, descr, sh, cn;
+  mixed pa, tmp;
+  args = _unparsed_args();
+  notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
+              "           ebene <Abkuerzung>=\n"
+              "           ebene abkuerzungen [standard]\n"
+              "           ebene standard <Ebene>\n"
+              "           ebene an|ein|aus\n"
+              +(QueryProp(P_LEVEL) >= 5 ?
+                "           ebene neu <Name> <Bezeichnung>\n"
+    "           ebene beschreibung <Name> <Beschreibung>\n" : "")
+        +(IS_ARCH(this_object()) ?
+          "           ebene kill <Name>\n"
+	  "           ebene clear <Name>\n": ""));
+  if(!args || !sizeof(args)) return 0;
+  if(sscanf(args, "kill %s", n) && IS_ARCH(this_object()))
+  {
+    if(!(cn = CHMASTER->find(n, this_object()))) cn = n;
+    switch(CHMASTER->remove(cn, this_object()))
+    {
+    case E_ACCESS_DENIED:
+     notify_fail("Die Ebene '"+cn+"' lies sich nicht entfernen!\n");
+     return 0;
+    }
+    write("Du entfernst die Ebene '"+cn+"'.\n");
+    return 1;
+  }
+  if(sscanf(args, "clear %s", n) && IS_ARCH(this_object()))
+  {
+    if(!(cn = CHMASTER->find(n, this_object()))) cn = n;
+    switch(CHMASTER->clear_history(cn, this_object()))
+    {
+    case E_ACCESS_DENIED:
+     notify_fail("Der Verlauf zur Ebene '"+cn+"' lies sich nicht entfernen!\n");
+     return 0;
+    }
+    write("Du entfernst den Verlauf zur Ebene '"+cn+"'.\n");
+    return 1;
+  }
+  if(sscanf(args, "neu %s %s", n, descr) == 2)
+  {
+    mixed x;
+    if(QueryProp(P_LEVEL) < 5)
+      return (notify_fail("Neue Ebenen zu erstellen ist dir verwehrt.\n"), 0);
+    if(!sizeof(regexp(({ n }), "^" CHANNELCMDS CHANNELCMDS "*")))
+      return (notify_fail("Der Name '"+n+"' ist nicht konform!\n"), 0);
+    if (sizeof(n) > 20 )
+      return(notify_fail("Der Name '"+n+"' ist zu lang.\n"), 0);
+    switch(x = CHMASTER->new(n, this_object(), descr))
+    {
+    case E_ACCESS_DENIED:
+      notify_fail("Diese Ebene darfst du nicht erschaffen!\n"); break;
+    default:
+      write("Du erschaffst die Ebene '"+n+"'.\n");
+      SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ lower_case(n) }));
+      return 1;
+    }
+  }
+  if(sscanf(args, "beschreibung %s %s", n, descr) == 2)
+  {
+    mixed ch;
+    cn = CHMASTER->find(n, this_object());
+    if(!cn || pointerp(cn))
+      return (notify_fail("Die Ebene '"+n+"' existiert nicht oder die Angabe "
+        "war nicht eindeutig.\n"), 0);
+    ch = CHMASTER->list(this_object());
+    if(ch[lower_case(cn)][I_MASTER] != this_object())
+      return (notify_fail("Du bist nicht berechtigt die Beschreibung der Ebene"
+        " '"+cn+"' zu aendern.\n"), 0);
+    ch[lower_case(cn)][I_INFO] = descr;
+    write("Die Ebene '"+cn+"' hat ab sofort die Beschreibung:\n"+descr+"\n");
+    return 1;
+  }
+  if(sscanf(args, "%s=%s", sh, n) == 2 && sizeof(n))
+  {
+    mapping sc;
+    if(pointerp(tmp = CHMASTER->find(n, this_object())) || !tmp)
+      return (notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
+                          +(pointerp(tmp) ? implode(tmp, ", ") + "\n" :
+                            "Ebene '"+n+"' nicht gefunden!\n")), 0);
+    sc = QueryProp(P_CHANNEL_SHORT);
+    if(!sc) sc = ([]);
+    sc[sh] = tmp;
+    SetProp(P_CHANNEL_SHORT, sc);
+    shortcut = QueryProp(P_CHANNEL_SHORT);
+    write("'"+sh+"' wird jetzt als Abkuerzung fuer '"+tmp+"' anerkannt.\n");
+    return 1;
+  }
+  if(sscanf(args, "%s=", sh))
+  {
+    SetProp(P_CHANNEL_SHORT, m_copy_delete(QueryProp(P_CHANNEL_SHORT) || ([]), sh));
+    shortcut = QueryProp(P_CHANNEL_SHORT);
+    write("Du loeschst die Abkuerzung '"+sh+"'.\n");
+    return 1;
+  }
+  if(args == "an" || args == "ein")
+  {
+    mixed excl;
+    if(pointerp(QueryProp(P_SWAP_CHANNELS)))
+      SetProp(P_CHANNELS, QueryProp(P_SWAP_CHANNELS));
+    else
+      SetProp(P_CHANNELS, m_indices(CHMASTER->list(this_object())));
+    excl = RegisterChannels();
+    write("Du schaltest folgende Ebenen ein:\n"
+          +break_string(implode(QueryProp(P_CHANNELS) - excl, ", "), 78));
+    SetProp(P_SWAP_CHANNELS, 0);
+    return 1;
+  }
+  if(args == "aus")
+  {
+    SetProp(P_SWAP_CHANNELS, QueryProp(P_CHANNELS));
+    RemoveChannels();
+    SetProp(P_CHANNELS, ({}));
+    write("Du stellst die Ebenen ab.\n");
+    return 1;
+  }
+  pa = old_explode(args, " ");
+  if(!strstr("abkuerzungen", pa[0]))
+  {
+    string txt; txt = "";
+    if(sizeof(pa) > 1 && !strstr("standard", pa[1]))
+    {
+      write("Die Standard Abkuerzungen werden gesetzt.\n");
+      SetProp(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
+              + (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
+    }
+    walk_mapping(QueryProp(P_CHANNEL_SHORT),
+                 lambda(({'i/*'*/, 'c, 'r}),
+                        ({#'+=, 'r/*'*/,
+                          ({#'sprintf/*'*/, "%5.5s = %s\n", 'i, 'c})})),
+                 &txt);
+    txt = sprintf("Folgende Abkuerzungen sind definiert:\n%-78#s\n",
+                  implode(sort_array(old_explode(txt, "\n"), #'>/*'*/), "\n"));
+    More(txt);
+    return 1;
+  }
+  if(!strstr("standard", pa[0]))
+    if(sizeof(pa) < 2)
+      return (notify_fail("Benutzung: ebene standard <Ebene>\n"
+                          +(QueryProp(P_STD_CHANNEL)
+                            ? "Momentan ist '"+QueryProp(P_STD_CHANNEL)
+                            +"' eingestellt.\n"
+                            : "Es ist keine Standardebene eingestellt.\n")),0);
+    else
+      if(pointerp(tmp = CHMASTER->find(pa[1], this_object())))
+        return (notify_fail("Das war keine eindeutige Angabe! "
+                            "Folgende Ebenen passen:\n"
+                            +break_string(implode(tmp, ", "), 78)), 0);
+      else
+        if(!tmp) return (notify_fail("Ebene '"+pa[1]+"' nicht gefunden!\n"),0);
+        else
+        {
+          write("'"+tmp+"' ist jetzt die Standardebene.\n");
+          SetProp(P_STD_CHANNEL, tmp);
+          return 1;
+        }
+  return(0);
+}
+
diff --git a/std/player/combat.c b/std/player/combat.c
new file mode 100644
index 0000000..abb5a1b
--- /dev/null
+++ b/std/player/combat.c
@@ -0,0 +1,217 @@
+// MorgenGrauen MUDlib
+//
+// player/combat.c -- combat statistics
+//
+// $Id: combat.c 9008 2015-01-06 17:20:17Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/combat";
+inherit "/std/player/pklog";
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <combat.h>
+#include <new_skills.h>
+
+#define ME this_object()
+#define STATMASTER "/p/service/rochus/guildstat/master"
+
+private nosave closure mod_def_stat;
+private nosave string *plAttacked = ({});
+
+protected void create() {
+  combat::create();
+  // P_NO_ATTACK ist nicht fuer Spieler gedacht. Ausnahme: Spieler ist
+  // Geist, dann setzt das Spielerobjekt aber selber.
+  Set(P_NO_ATTACK, SECURED|NOSETMETHOD, F_MODE_AS);
+  
+  Set(P_HELPER_NPC, PROTECTED, F_MODE);
+  SetProp(P_HELPER_NPC, ([]) );
+}
+
+// Maske fuer alle moeglichen Klassen von Helfer-NPC
+#define CLASS_MASK 0x1fffffff
+/** registriert den NPC als Helfer von diesem Spieler.
+  @param[in] npc object Helfer-NPC
+  @param[in] flags int Bitfeld von Flags
+  @return int 1, falls der Helfer-NPC registriert wurde und noch nicht
+  registriert war.
+  @attention Nutzt aus, dass QueryProp(P_HELPER_NPC) _keine_ Kopie des
+  Mappings in der Prop liefert.
+  */
+public int RegisterHelperNPC(object npc, int flags) {
+  if (!objectp(npc))
+    raise_error(sprintf( "Wrong argument 1 in RegisterHelperNPC(). "
+          "Expected <object>, got %.100O\n", npc));
+  if (!intp(flags) || flags < 1)
+     raise_error(sprintf( "Wrong argument 2 in RegisterHelperNPC(). "
+          "Expected positive <int>, got %O\n", flags));
+  
+  mapping helpers = QueryProp(P_HELPER_NPC);
+  
+  // schon registrierte sind witzlos.
+  if (member(helpers, npc))
+    return 0;
+  
+  // auf exklusive Helfer pruefen.
+  foreach(object helper, int fl: helpers) {
+    // flags identisch? Dann Klasse und Exklusivitaet identisch
+    if (fl == flags)
+      return 0;
+    // oder einer von beiden exklusiv und beide in der gleichen Klasse?
+    else if ( ((fl & EXCLUSIVE_HELPER) || (flags & EXCLUSIVE_HELPER))
+              && ((fl & CLASS_MASK) == (flags & CLASS_MASK)) )
+      return 0;
+  }
+  // scheint wohl ok zu sein. Registrieren und Prop im NPC setzen.
+  helpers += ([ npc: flags ]);
+  npc->SetProp(P_HELPER_NPC, ({ this_object(), flags }) );
+  // momentan unnoetig, da helpers keine Kopie ist.
+  // SetProp(P_HELPER_NPC, helpers);
+
+  return 1;
+}
+#undef CLASS_MASK
+
+/** de-registriert den NPC als Helfer von diesem Spieler.
+  @param[in] npc object Helfer-NPC
+  @return int 1, falls der Helfer-NPC registriert war und entfernt wurde.
+  @attention Nutzt aus, dass QueryProp(P_HELPER_NPC) _keine_ Kopie des
+  Mappings in der Prop liefert.
+ */
+public int UnregisterHelperNPC(object npc) {
+  if (!objectp(npc))
+    raise_error(sprintf("Wrong argument in UnregisterHelpernNPC(). "
+          "Expected <object>, got %.100O\n", npc));
+
+  mapping helpers = QueryProp(P_HELPER_NPC);
+  if (member(helpers, npc)) {
+    m_delete(helpers, npc);
+    // momentan unnoetig, da helpers keine Kopie ist.
+    // SetProp(P_HELPER_NPC, helpers);
+    npc->SetProp(P_HELPER_NPC, 0);
+    return 1;
+  }
+  return 0;
+}
+
+/** Feind eintragen.
+  * Traegt ob als Feind ein. Dies allerdings nur, wenn ob kein Spieler ist
+  * oder beide Spieler (dieses Objekt und ob) in der Schattenwelt sind oder
+  * beide Spieler Testspieler sind.
+   @param[in] ob potentieller Feind.
+   @return int 1, falls ob als _neuer_ Feind eingetragen wurde.
+   */
+public int InsertEnemy(object ob) {
+  // wenn ob ein Spieler ist und nicht sowohl ich als auch ob in der
+  // Schattenwelt sind, wird ob nicht als Feind eingetragen.
+  if (query_once_interactive(ob)
+      && (strstr(object_name(environment(ob)),"/d/schattenwelt/")!=0
+          || strstr(object_name(environment(ME)),"/d/schattenwelt/")!=0)
+      && (!QueryProp(P_TESTPLAYER) || !ob->QueryProp(P_TESTPLAYER))
+     )
+  {
+    return 0;
+  }
+  return ::InsertEnemy(ob);
+}
+
+/** Hat dieser Spieler den Spieler pl angegriffen?.
+  @param[in] pl object zu pruefender Spieler
+  @return int 1, falls dieser Spieler pl angegriffen hat.
+  @attention Nebeneffekt: bereinigt den internen Speicher von UIDs von
+  zerstoerten Spielern und solchen, die keine Feinde mehr sind.
+  */
+public int QueryPlAttacked(object pl) {
+  object ob;
+
+  if ( !objectp(pl) )
+    return 0;
+
+  foreach(string plname: plAttacked) {
+    if ( !( ob=(find_player(plname)||find_netdead(plname)) )
+        || ( !IsEnemy(ob) && !(ob->IsEnemy(ME)) ) )
+      plAttacked -= ({plname}); // ja, das geht. ;-)
+  }
+  return (member( plAttacked, getuid(pl) ) >= 0);
+}
+
+/** kill - Kampf starten.
+ * Fuegt ob der Feindesliste hinzu.
+ */
+public int Kill(object ob) {
+
+  if (!objectp(ob)) return 0;
+
+  // dies dient nur dazu, das plAttacked mapping zu bereinigen.
+  // TODO: besser machen. ;-)
+  if ( query_once_interactive(ob) && !IsEnemy(ob)) {
+    QueryPlAttacked(ME);
+    ob->QueryPlAttacked(ob); // aktualisieren ...
+  }
+
+  int res = combat::Kill(ob);
+
+  // falls ob nen Spieler ist, pruefen, ob es ein Spieler-Spieler-Angriff
+  // ist.
+  // Dabei ggf. loggen und Magier verstaendigen.
+  if (query_once_interactive(ob) && CheckPlayerAttack(ME, ob, 0))
+  {
+    if (res == -4) // feind wurde nicht eingetragen
+      tell_object(ME, "Ein goettlicher Befehl hindert Dich am Kampf.\n");
+    else
+      plAttacked += ({ getuid(ob) });
+  }
+
+  return res;
+}
+
+public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy) {
+  int delta_hp,res;
+
+  if (query_once_interactive(ME)
+      && !IS_LEARNER(ME)
+      && !objectp(get_type_info(mod_def_stat,2))) {
+    object ma;
+    if (!objectp(ma=find_object(STATMASTER)))
+      return ::Defend(dam,dam_type,spell,enemy);
+    // Statistik nur aufrufen falls Master geladen
+    mod_def_stat=symbol_function("ModifyDefendStat",ma);
+  }
+
+  if (closurep(mod_def_stat))
+    delta_hp=QueryProp(P_HP);
+
+  res=::Defend(dam,dam_type,spell,enemy);
+
+  if (closurep(mod_def_stat)) {
+    delta_hp-=QueryProp(P_HP);
+    if (delta_hp<0)
+      delta_hp=0;
+    funcall(mod_def_stat,
+            QueryProp(P_GUILD),
+            QueryProp(P_GUILD_LEVEL),
+            dam-10*delta_hp,
+            dam_type,
+            spell);
+  }
+
+  return res;
+}
+
+// Spieler koennen als Geist nicht kämpfen
+// TODO: pruefen, ob das Setzen und Loeschen der Prop in set_ghost() nicht
+// auch ausreichen wuerde. In dem Fall muesste man aber P_NO_ATTACK auch
+// speichern, da P_GHOST gespeichert wird...
+static mixed _query_no_attack()
+{
+    if ( QueryProp(P_GHOST) )
+        return 1;
+
+    return Query(P_NO_ATTACK);
+}
diff --git a/std/player/comm.c b/std/player/comm.c
new file mode 100644
index 0000000..f91f1de
--- /dev/null
+++ b/std/player/comm.c
@@ -0,0 +1,1922 @@
+// MorgenGrauen MUDlib
+//
+// player/comm.c-- basic player communiction commands
+//
+// $Id: comm.c 9576 2016-06-18 15:00:01Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+//#pragma range_check
+
+inherit "/std/living/comm";
+inherit "/std/player/channel";
+inherit "/std/player/comm_structs";
+
+#include <input_to.h>
+
+#define NEED_PROTOTYPES
+#include <player/quest.h>
+#include <player/gmcp.h>
+#include <living/description.h>
+#undef NEED_PROTOTYPES
+
+#include <sys_debug.h>
+
+#include <thing/properties.h>
+#include <player/comm.h>
+#include <player/base.h>
+
+#include <properties.h>
+#include <config.h>
+#include <ansi.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <udp.h>
+#include <defines.h>
+#include <daemon.h>
+#include <strings.h>
+#include <regexp.h>
+#include <interactive_info.h>
+
+#define TELLHIST_DISABLED   0
+#define TELLHIST_NO_MESSAGE 1
+#define TELLHIST_ENABLED    2
+#define TELLHIST_LONGLIFE   3
+
+#define ECHO_COST 50
+#define ERWIDER_PARAM ","
+
+#define ZDEBUG(x)  if (find_player("zesstra"))\
+              efun::tell_object(find_player("zesstra"),"CommDBG: "+x+"\n")
+
+private int tell_history_enabled = TELLHIST_NO_MESSAGE;
+private nosave mapping tell_history=([]);
+private nosave string *commreceivers = ({});
+private nosave string last_comm_partner;
+private nosave int last_beep_time;
+
+// Statusreporte aktiviert? Binaere Flags (s. set_report())
+private int stat_reports;
+// interner Cache fuer die LP/KP/Gift-Werte fuer die Statusreport-Ausgaben
+// Eintraege (in dieser Reihenfolge): P_HP, P_SP, Giftstatus
+// Initialisierung erfolgt beim ersten Report nach Login
+private nosave mixed *report_cache;
+
+// Puffer fuer Kobold.
+private nosave struct msg_buffer_s kobold = (<msg_buffer_s>
+                                             buf: allocate(32),
+                                             index: -1,);
+#define MAX_KOBOLD_LIMIT 256
+
+varargs string name(int casus, int demonst);
+
+//local property prototypes
+static int _query_intermud();
+public int RemoveIgnore(string ign);
+public int AddIgnore(string ign);
+
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+                              string msg_prefix, object origin);
+
+// erzeugt sortierte Liste an Kommunikationspartnern
+private string *sorted_commpartners(int reversed);
+
+private nosave string *buffer = ({});
+
+void create()
+{
+  ::create();
+  Set(P_EARMUFFS, 0);
+  Set(P_EARMUFFS, SAVE, F_MODE);
+  Set(P_EARMUFFS, SECURED, F_MODE);
+  Set(P_INTERMUD, SAVE, F_MODE);
+  Set(P_IGNORE, ([]), F_VALUE);
+  Set(P_IGNORE, SAVE, F_MODE);
+  Set(P_BUFFER, SAVE, F_MODE);
+  Set(P_MESSAGE_PREPEND, SAVE, F_MODE_AS);
+  Set(P_MESSAGE_BEEP, SAVE, F_MODE_AS);
+}
+
+void create_super()
+{
+  set_next_reset(-1);
+}
+
+// uebermittelt eine MT_NOTIFICATION an this_object(), welche nicht ignoriert
+// werden kann und auch nicht gespeichert wird.
+protected void _notify(string msg, string action) {
+  ReceiveMsg(msg,
+             MT_NOTIFICATION|MSG_DONT_BUFFER|MSG_DONT_IGNORE|MSG_DONT_STORE,
+             action, 0, this_object());
+}
+
+protected void updates_after_restore(int newflag) {
+  // Altes Ignoriere loeschen...
+  mixed ign = Query(P_IGNORE,F_VALUE);
+  if (!mappingp(ign))
+  {
+    if (pointerp(ign))
+      _notify(break_string(
+        "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
+        "weil es eine Aktualisierung der Ignorierefunktion gab, "
+        "bei der eine Konversion der Daten leider nicht "
+        "moeglich war.",78), 0);
+
+    Set(P_IGNORE, ([]), F_VALUE);
+  }
+}
+
+static int set_report(string str) {
+  int canflags = QueryProp(P_CAN_FLAGS);
+
+  if(!str)
+  {
+    if (stat_reports) {
+    string *res=({});
+    if (stat_reports & DO_REPORT_HP)
+      res+=({"Lebenspunkte"});
+    if (stat_reports & DO_REPORT_SP)
+      res+=({"Konzentrationspunkte"});
+    if (stat_reports & DO_REPORT_POISON)
+      res+=({"Vergiftungen"});
+    if (stat_reports & DO_REPORT_WIMPY)
+      res+=({"Vorsicht"});
+
+    tell_object(ME,break_string(
+        "Dir werden jetzt Veraenderungen Deiner "
+        +CountUp(res) + " berichtet.",78));
+    }
+    else
+      tell_object(ME,
+        "Alle Statusreports sind ausgeschaltet.\n");
+
+    return 1;
+  }
+  else if (str == "aus") {
+    if (stat_reports & DO_REPORT_HP || stat_reports & DO_REPORT_WIMPY) {
+      string s="";
+      if (stat_reports & DO_REPORT_HP) {
+        str="ebenfalls ";
+        tell_object(ME, "Der Report wurde ausgeschaltet.\n");
+      }
+      if ( stat_reports & DO_REPORT_WIMPY ) {
+        tell_object(ME, "Der Vorsicht-Report wurde "+s+
+          "ausgeschaltet.\n");
+      }
+      stat_reports=0;
+    }
+    else {
+      tell_object(ME, "Der Report ist bereits ausgeschaltet.\n");
+    }
+    return 1;
+  }
+  else if (str == "ein") {
+    if ( stat_reports & DO_REPORT_HP ) {
+      tell_object(ME, "Der Report ist bereits eingeschaltet.\n");
+      return 1;
+    }
+    tell_object(ME, "Der Report wurde eingeschaltet.\n");
+    stat_reports |= DO_REPORT_HP;
+    if (!(canflags & CAN_REPORT_SP)) {
+      if (QueryQuest("Hilf den Gnarfen")==1) {
+        SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_SP);
+        stat_reports |= DO_REPORT_SP;
+      }
+      else {
+        tell_object(ME, break_string(
+          "Fuer den Statusreport Deiner Konzentration musst Du jedoch "
+          "zunaechst die Quest \"Hilf den Gnarfen\" bestehen.",78));
+      }
+    }
+    else {
+      stat_reports |= DO_REPORT_SP;
+    }
+    if (!(canflags & CAN_REPORT_POISON)) {
+      if (QueryQuest("Katzenjammer")==1) {
+        SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_POISON);
+        stat_reports |= DO_REPORT_POISON;
+      }
+      else {
+        tell_object(ME, break_string(
+          "Fuer den Statusreport Deiner Vergiftung musst Du jedoch "
+          "zunaechst die Quest \"Katzenjammer\" bestehen.",78));
+      }
+    }
+    else {
+      stat_reports |= DO_REPORT_POISON;
+    }
+    // Cache loeschen, damit beim naechsten Report-Event alle Daten neu
+    // eingetragen werden muessen. Muss beim Einschalten des Reports
+    // passieren, weil auch in der inaktiven Zeit weiterhin Aenderungen in
+    // status_report() eingehen, so dass der Cache zwar erst einmal leer ist,
+    // aber beim Wiedereinschalten nicht mehr ungueltig waere und somit
+    // veraltete Daten an den Spieler ausgegeben werden. Im unguenstigsten
+    // Fall wuerde das sogar dazu fuehren, dass die veralteten Daten lange
+    // Zeit nicht aktualisiert werden, wenn z.B. P_HP == P_MAX_HP, so dass
+    // kein P_HP-Event mehr eingeht.
+    report_cache=0;
+  }
+  else if (str == "vorsicht") {
+    if (!(canflags & CAN_REPORT_WIMPY)) {
+      if (QueryQuest("Schrat kann nicht einschlafen")==1) {
+        SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_WIMPY);
+        tell_object(ME, "Der Vorsicht-Report wurde eingeschaltet.\n");
+        stat_reports |= DO_REPORT_WIMPY;
+      }
+      else {
+        tell_object(ME, break_string(
+          "Fuer den Statusreport Deiner Vorsicht musst Du "
+          "zunaechst die Quest \"Schrat kann nicht einschlafen\" "
+          "bestehen.",78));
+      }
+    }
+    else
+    {
+      stat_reports |= DO_REPORT_WIMPY;
+    }
+    // fuer Seher auch Bericht der Fluchtrichtung einschalten.
+    if ((stat_reports & DO_REPORT_WIMPY)
+        && !(stat_reports & DO_REPORT_WIMPY_DIR)
+        && ((canflags & CAN_REPORT_WIMPY) || IS_SEER(ME)))
+    {
+      stat_reports |= DO_REPORT_WIMPY_DIR;        
+    }
+  }
+  // sendet einmalig genau jetzt den konfigurierten report. Kann zum testen
+  // (von Triggern) oder beim Login benutzt werden, wenn man einen initialen
+  // Datenbestand erhalten will.
+  else if (str=="senden")
+  {
+    // Es wird Ausgabe von LP und Vorsicht getriggert, das sendet beide
+    // Zeilen.
+    status_report(DO_REPORT_HP, QueryProp(P_HP));
+    status_report(DO_REPORT_WIMPY, QueryProp(P_WIMPY));
+    return 1;
+  }
+  else 
+    return 0;
+  // nur aktuellen Zustand berichten
+  set_report(0);
+  return 1;
+}
+
+private string get_poison_desc(int p) {
+  string ret;
+  if ( intp(p) ) {
+    switch(p) {
+      case 0:    ret="keins";       break;
+      case 1..3: ret="leicht";      break;
+      case 4..8: ret="gefaehrlich"; break;
+      default:   ret="sehr ernst";  break;
+    }
+    return ret;
+  }
+  else return "(nicht verfuegbar)";
+}
+
+// sprintf()-Formatstrings fuer die Reportausgabe.
+#define REPORTLINE "LP: %3d, KP: %3s, Gift: %s.\n"
+#define REPORTLINE_WIMPY "Vorsicht: %d, Fluchtrichtung: %s.\n"
+// Defines zur Adressierung der Cache-Eintraege
+#define REP_HP     0
+#define REP_SP     1
+#define REP_POISON 2
+
+protected void status_report(int type, mixed val) {
+  // Wenn der Spieler GMCP hat und das sich um die Information kuemmert,
+  // erfolgt keine textuelle Ausgabe mehr. Daher return, wenn GMCP_Char()
+  // erfolg vermeldet hat.
+  int flags = QueryProp(P_CAN_FLAGS);
+  switch (type) {
+    case DO_REPORT_HP:
+      if (GMCP_Char( ([ P_HP: val ]) ) ) return;
+      break;
+    case DO_REPORT_SP:
+      if (!(flags & CAN_REPORT_SP)) return;
+      if (GMCP_Char( ([ P_SP: val ]) ) ) return;
+      break;
+    case DO_REPORT_POISON:
+       if (!(flags & CAN_REPORT_POISON)) return;
+       if (GMCP_Char( ([ P_POISON: val ]) ) ) return;
+      break;
+    case DO_REPORT_WIMPY:
+      if (!(flags & CAN_REPORT_WIMPY)) return;
+      if (GMCP_Char( ([ P_WIMPY: val ]) ) ) return;
+      break;
+    case DO_REPORT_WIMPY_DIR:
+      if (!(flags & CAN_REPORT_WIMPY_DIR)) return;
+      if (GMCP_Char( ([ P_WIMPY_DIRECTION: val ]) ) ) return;
+      break;
+  }
+
+  // konventionelle textuelle Ausgabe des Reports ab hier.
+  if (!(type & stat_reports))
+    return;
+
+  if ( !report_cache ) {
+    report_cache = ({
+      QueryProp(P_HP),
+      (stat_reports&DO_REPORT_SP) ? to_string(QueryProp(P_SP)) : "###",
+      (stat_reports&DO_REPORT_POISON) ?
+          get_poison_desc(QueryProp(P_POISON)) : "(nicht verfuegbar)"
+    });
+  }
+
+  switch(type) {
+      // LP berichten: Cache aktualisieren und Meldung ausgeben.
+      case DO_REPORT_HP:
+        report_cache[REP_HP]=val;
+        tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
+          report_cache[REP_SP], report_cache[REP_POISON]));
+        break;
+      // KP berichten: Wenn der Spieler den Report freigeschaltet hat,
+      // wird bei Aenderungen gemeldet. Wenn nicht, aendert sich nur der
+      // Cache-Eintrag. So wird verhindert, dass ein Spieler ueber KP-
+      // Veraenderungen auch dann informiert wuerde, wenn er den KP-Report
+      // gar nicht benutzen koennte.
+      case DO_REPORT_SP:
+        report_cache[REP_SP]=to_string(val);
+        tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
+          report_cache[REP_SP], report_cache[REP_POISON]));
+        break;
+      // Giftstatus berichten: Wenn der Giftreport freigeschaltet ist,
+      // Cache aktualisieren und berichten. Wenn nicht, aendert sich nur
+      // der Cache-Eintrag. Erlaeuterung hierzu s.o. beim KP-Report.
+      case DO_REPORT_POISON:
+        report_cache[REP_POISON] = get_poison_desc(val);
+        tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
+          report_cache[REP_SP], report_cache[REP_POISON]));
+        break;
+      // Vorsicht-Report: kann ohne weitere Abfragen ausgegeben werden, da
+      // alle noetigen Checks schon zu Beginn dieser Funktion erledigt wurden.
+      // Lediglich der Inhalt der Meldung muss abhaengig vom Seherstatus
+      // konfiguriert werden.
+      case DO_REPORT_WIMPY:
+        string res;
+        if (IS_SEER(ME)) {
+          // QueryProp() aus Kostengruenden im if(), damit die Aufruf-
+          // Haeufigkeit zumindest ein wenig reduziert wird.
+          string dir = QueryProp(P_WIMPY_DIRECTION)||"keine";
+          res = sprintf(REPORTLINE_WIMPY, val, dir);
+        }
+        else
+          res = sprintf(REPORTLINE_WIMPY, val, "(nicht verfuegbar)");
+        tell_object(ME, res);
+        break;
+      // Fluchtrichtungs-Report: wird nur bei Sehern ausgegeben, damit
+      // nicht auch Spieler eine VS-/FR-Meldung bekommen, wenn z.B. eine
+      // externe Manipulation der Fluchtrichtung stattfindet, sie aber den
+      // Report mangels Seherstatus gar nicht freigeschaltet haben.
+      case DO_REPORT_WIMPY_DIR:
+        if (IS_SEER(ME)) {
+          if (!val) val = "keine";
+          tell_object(ME,sprintf(REPORTLINE_WIMPY, QueryProp(P_WIMPY), val));
+        }
+        break;
+  }
+}
+
+#undef REPORTLINE
+#undef REPORTLINE_WIMPY
+#undef REP_HP
+#undef REP_SP
+#undef REP_POISON
+
+private string permutate(string msg)
+{
+  // Kontrollzeichen rausfiltern. *seufz*
+  msg = regreplace(msg,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
+  object ob=QueryProp(P_PERM_STRING);
+  if (!objectp(ob))
+    return msg;
+
+  return (string)ob->permutate_string(msg)||"";
+}
+
+// neue nachricht an den Kobold anhaengen
+// Rueckgabewerte: MSG_BUFFER_FULL oder MSG_BUFFERED
+private int add_to_kobold(string msg, int msg_type, string msg_action,
+                          string msg_prefix, object origin)
+{
+  // Nachricht soll im Kobold gespeichert werden.
+  // Kobold speichert Rohdaten und gibt spaeter das ganze auch wieder via
+  // ReceiveMsg() aus - dabei wird MSG_DONT_BUFFER | MSG_DONT_STORE gesetz,
+  // damit keine erneute Speicher in Kobold oder Komm-History erfolgt.
+
+  // wenn der Puffer zu klein ist, Groesse verdoppeln, wenn noch unterhalb
+  // des Limits.
+  if (kobold->index >= sizeof(kobold->buf)-1) {
+    if (sizeof(kobold->buf) < MAX_KOBOLD_LIMIT)
+      kobold->buf += allocate(sizeof(kobold->buf));
+    else
+      return MSG_BUFFER_FULL;
+  }
+  kobold->index = kobold->index +1;
+  // neue Nachricht an den Puffer anhaengen.
+  string sendername = query_once_interactive(origin) ?
+                      origin->query_real_name() :
+                      origin->name(WER) || "<Unbekannt>";
+  kobold->buf[kobold->index] = (<msg_s> msg: msg,
+      type : msg_type, action : msg_action, prefix : msg_prefix,
+      sendername : sendername);
+  return MSG_BUFFERED;
+}
+
+private void _flush_cache(int verbose) {
+  // nur mit genug Evalticks ausgeben.
+  if (get_eval_cost() < 100000) return;
+  if (kobold->index >= 0)
+  {
+    ReceiveMsg("Ein kleiner Kobold teilt Dir folgendes mit:",
+               MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
+               0, 0, this_object());
+    int prepend = QueryProp(P_MESSAGE_PREPEND);
+    foreach(int i: 0 .. kobold->index) // '0 ..' ist wichtig!
+    {
+      struct msg_s msg = kobold->buf[i];
+      // dies ist dient der Fehlerabsicherung, falls es nen Fehler (z.B. TLE)
+      // in der Schleife unten gab: dann ist index nicht auf -1 gesetzt
+      // worden, aber einige Nachrichten sind schon geloescht.
+      if (!structp(msg)) continue;
+      // Ausgabe via efun::tell_object(), weil die Arbeit von ReceiveMsg()
+      // schon getan wurde. Allerdings muessen wir uns noch um den UMbruch
+      // kuemmern.
+      if ((msg->type) & MSG_DONT_WRAP)
+        msg->msg = (msg->prefix ? msg->prefix : "") + msg->msg;
+      else
+      {
+        int bsflags = msg->type & MSG_ALL_BS_FLAGS;
+        if (prepend)
+          bsflags |= BS_PREPEND_INDENT;
+        msg->msg = break_string(msg->msg, 78, msg->prefix, bsflags);
+      }
+      efun::tell_object(this_object(), msg->msg);
+      kobold->buf[i]=0;
+    }
+    kobold->index=-1;
+  }
+  else if (verbose)
+  {
+    ReceiveMsg("Der kleine Kobold hat leider nichts Neues fuer Dich.",
+               MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
+               0, 0, this_object());
+  }
+}
+
+varargs int cmd_kobold(string arg)
+{
+  switch(arg)
+  {
+    case "ein":
+      SetProp(P_BUFFER, 1);
+      printf("Der Kobold merkt sich jetzt alles!\n"); break;
+    case "aus":
+      SetProp(P_BUFFER, 0);
+      printf("Der Kobold wird Dich nicht stoeren!\n"); break;
+    default: if(arg) printf("Der Kobold sagt: kobold ein oder kobold aus\n");
+  }
+  _flush_cache(1);
+  return 1;
+}
+
+public int TestIgnoreSimple(string *arg)
+{   string *ignore;
+
+    if (!pointerp(arg) || !mappingp(ignore=Query(P_IGNORE,F_VALUE)))
+        return 0;
+    foreach(string s: arg)
+    {
+      if (member(ignore,s))
+        return 1;
+    }
+    return 0;
+}
+
+//TODO: deprecated - entfernen, wenn Message() entfernt wird.
+private int check_ignore(mixed ignore, string verb, string name)
+{
+  if (ignore == verb)
+    return 1;
+  ignore = explode(ignore, ".");
+  return ((sizeof(ignore) > 1) &&
+     (name == ignore[0] && member(ignore[1..], verb) != -1));
+}
+
+private int comm_beep() {
+  if (QueryProp(P_VISUALBELL)) return 0; // kein ton
+  int beep_interval=(int)QueryProp(P_MESSAGE_BEEP);
+  if (!beep_interval || ((time()-last_beep_time) < beep_interval)) return 0;
+  last_beep_time=time();
+  return 1;
+}
+
+private varargs void add_to_tell_history( string uid, int sent, int recv,
+                                 string message, string indent, int flags )
+{
+  /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
+     Als Wert ist eine Strukur vom Typ chat_s eingetragen.
+     Strukturen chat_s und stored_msg_s sind in /std/player/comm_structs.c
+     definiert.
+     TODO fuer spaeter, gerade keine Zeit fuer:
+     Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
+     jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
+     weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
+     Element ist aeltestes Gespraech).
+     */
+
+  //TODO: Entfernen, wenn das nicht mehr passiert.
+  if (!stringp(uid))
+  {
+    ReceiveMsg(sprintf(
+      "\nadd_to_tell_history(): got bad uid argument %O."
+      "sent: %d, recv: %d, flags: %d, msg: %s", 
+      uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);              
+  }
+  
+  // letzten Gespraechspartner fuer erwidere.
+  if (!(flags & MSGFLAG_REMOTE))
+    last_comm_partner = uid;
+
+  // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
+  // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
+  // Anfang.
+  if (sent) {
+    if (!sizeof(commreceivers))
+      commreceivers = ({uid});
+    else if (commreceivers[0] != uid) {
+      // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
+      // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
+      // entfernen.
+      // TODO: Effizienter gestalten.
+      commreceivers = ({uid}) + (commreceivers-({uid}));
+      // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
+      // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
+      // commreceivers drin sind.)
+      if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
+        commreceivers = commreceivers[0..MAX_SAVED_CHATS];
+    }
+  }
+
+  if (!tell_history_enabled)
+    return;
+
+  if (!indent && message[<1] == 10)
+      message = message[..<2];
+
+  struct chat_s chat;
+  // Gespraechspartner unbekannt?
+  if (!member(tell_history, uid)) {
+    // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
+    // dazu kommt.
+    if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
+      string deluid;
+      int zeit = __INT_MAX__;
+      foreach(string tuid, chat : tell_history) {
+        // aeltestes Gespraech suchen
+        if (zeit > chat->time_last_msg) {
+          deluid = tuid;
+          zeit = chat->time_last_msg;
+        }
+      }
+      // aeltestes Gespraech raus.
+      m_delete(tell_history, deluid);
+      if (member(commreceivers,deluid)>-1)
+        commreceivers-=({deluid});
+    }
+    // neues Gespraech anlegen
+    chat = (<chat_s> uid: uid, time_first_msg: time(), 
+               time_last_msg: time(),
+         sentcount: sent, recvcount: recv,
+         msgbuf: 0, ptr: 0 );
+    tell_history[uid] = chat;
+  }
+  else {
+    // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
+    chat = tell_history[uid];
+    chat->time_last_msg = time();
+    chat->sentcount += sent;
+    chat->recvcount += recv;
+  }
+
+  if (tell_history_enabled < TELLHIST_ENABLED)
+    return;
+
+  // ggf. Array fuer Messages anlegen
+  if (!pointerp(chat->msgbuf))
+    chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
+
+  // Message-Struktur ermitteln oder neu anlegen
+  struct stored_msg_s msg;
+  if (!structp(chat->msgbuf[chat->ptr])) {
+    // neue Struct ins Array schreiben
+    chat->msgbuf[chat->ptr] = msg = (<stored_msg_s>);
+  }
+  else {
+    // alte Struct ueberschreiben
+    msg = chat->msgbuf[chat->ptr];
+  }
+  // Index auf naechste Messagestruktur ermitteln
+  chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
+  // Message speichern
+  msg->msg = message;
+  msg->prefix = indent;
+  msg->timestamp = time();
+}
+
+protected void clear_tell_history()
+{
+  /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
+     sofern der Spieler nichts abweichendes eingestellt hat. */
+
+#ifdef TELLHIST_LONGLIFE
+  if (tell_history_enabled == TELLHIST_LONGLIFE)
+    return;
+#endif
+
+  foreach (string uid, struct chat_s chat: tell_history)
+    if (pointerp(chat->msgbuf)) {
+      chat->msgbuf = 0;
+      chat->ptr = 0;
+    }
+}
+
+protected void reset(void)
+{
+  /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
+     Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
+     wie bei einem "schlafe ein" die Mitteilungen geloescht. */
+
+  if (!interactive())
+    clear_tell_history();
+
+}
+
+// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
+// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
+// werden soll.
+private void _send(object ob, string msg, int msg_type,
+                   string msg_action, string msg_prefix)
+{
+  int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
+  switch(res) {
+    case MSG_DELIVERED:
+      break;  // nix machen
+    case MSG_BUFFERED:
+      ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden."
+          "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
+          "genommen. Er wird sie spaeter weiterleiten!",
+          MT_NOTIFICATION, msg_action, 0, this_object());
+      break;
+    case MSG_IGNORED:
+    case MSG_VERB_IGN:
+    case MSG_MUD_IGN:
+      ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
+           MT_NOTIFICATION, msg_action, 0, this_object());
+      break;
+    case MSG_SENSE_BLOCK:
+      ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
+          MT_NOTIFICATION, msg_action, 0, this_object());
+      break;
+    case MSG_BUFFER_FULL:
+      ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden."
+          "Die Mitteilung ging verloren, denn der Kobold kann sich "
+          "nichts mehr merken!", MT_NOTIFICATION, msg_action, 
+          0, this_object());
+      break;
+    default:
+      ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
+          "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
+      break;
+  }
+}
+
+// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
+// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
+// die Nachricht ggf. in den Kommhistory erfasst wird.
+// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
+protected varargs int _recv(object ob, string message, int flag, string indent)
+{
+  write(break_string(message, 78, indent,
+        QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
+  if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
+      query_once_interactive(ob))
+  {
+    if (flag & MSGFLAG_WHISPER)
+      add_to_tell_history(getuid(ob), 1, 0,
+        "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
+        flag);
+    else
+      add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
+  }
+  return 1;
+}
+
+// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
+// Fehlerbehandlung) ist es jedoch auch mal ein String.
+varargs int Message(string msg, int flag, string indent,
+                    string cname, mixed sender)
+{
+  object ti;
+  string verb, reply, *ignore, tin;
+  int em, te;
+  mixed deaf;
+
+  // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
+  // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
+  // damit 'ignoriere name.debug' funktioniert.
+  if( flag == MSGFLAG_CHANNEL ){
+      if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
+            || msg[1..9]=="Warnungen"))
+      {
+        // Missbrauch der Variable 'ignore' als Zwischenspeicher
+        ignore = regexplode( msg, ":| |\\]" );
+        verb = lower_case(ignore[0][1..]);
+        tin = lower_case(ignore[2]);
+      }
+      else
+      {
+        if(cname)
+          verb=lower_case(cname);
+        else
+          verb=query_verb();
+        if( ti = this_interactive() )
+        {
+          tin = getuid(this_interactive());
+        }
+        else
+        {
+          //falls doch kein Objekt...
+          if (objectp(sender))
+            tin=lower_case(sender->name(RAW)||"<Unbekannt>");
+        }
+      }
+  }
+  else {
+    if( ti = this_interactive() )
+      tin = getuid(this_interactive());
+    verb = query_verb();
+  }
+
+  te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
+
+  // fuer "erwidere"
+  if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
+    if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
+      if (flag & MSGFLAG_WHISPER)
+        add_to_tell_history(getuid(ti), 0, 1,
+          capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
+            (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
+            ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
+          " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
+      else
+        add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
+    }
+  }
+  // Hoert der Spieler nicht?
+  em = (ti &&
+        (te || flag & MSGFLAG_SHOUT) &&
+        (QueryProp(P_EARMUFFS) &&
+          (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
+  ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
+
+  // Werden der Sender oder das Verb ignoriert?
+  if(!ti && tin && flag == MSGFLAG_CHANNEL)
+  {
+     if((member(ignore, tin) != -1))
+     {
+       return MESSAGE_IGNORE_YOU;
+     }
+     if(verb &&  sizeof(filter(ignore, #'check_ignore, verb, tin)) )
+     {
+       return MESSAGE_IGNORE_YOU;
+     }
+  }
+  if (ti && (member(ignore, getuid(ti)) != -1)) {
+    if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
+      efun::tell_object(ti, capitalize(name())+
+                      " hoert gar nicht zu, was Du sagst.\n");
+    return MESSAGE_IGNORE_YOU;
+  }
+  if(tin && verb &&
+     sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
+  {
+    if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
+       verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
+      efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
+    return MESSAGE_IGNORE_VERB;
+  }
+  if (flag & MSGFLAG_RTELL) {
+    int at;
+
+    verb = lower_case(old_explode(msg, " ")[0][1..]);
+    at = member(verb, '@');
+    /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
+    add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
+
+    if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
+      return MESSAGE_IGNORE_YOU;
+    else if (at > 0 && member(ignore, verb[at..]) >= 0)
+      return MESSAGE_IGNORE_MUD;
+  }
+
+  // Taubheit/Oropax
+  te |= (flag & MSGFLAG_SAY);
+
+  if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
+    deaf = QueryProp(P_DEAF);
+    if (te)
+      reply = stringp(deaf) ?
+        capitalize(sprintf(deaf, name())) :
+        capitalize(name())+" ist momentan leider taub.\n";
+  }
+  else if (em)
+    reply = capitalize(name())+" hat Oropax in den Ohren.\n";
+
+  msg = break_string(msg, 78, indent,
+    (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
+
+  if(QueryProp(P_BUFFER) &&
+     (deaf ||
+      query_editing(this_object()) ||
+      query_input_pending(this_object())))
+  {
+    deaf = MESSAGE_DEAF;
+    if(flag & MSGFLAG_CACHE)
+    {
+      if(!stringp(reply))
+        reply = name()+" moechte gerade nicht gestoert werden.\n";
+
+      msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
+
+      int res =  add_to_kobold(msg, 0, 0, 0,
+                               objectp(sender) ? sender : ME);
+      if(res == MSG_BUFFERED)
+      {
+
+        reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
+                 "genommen.\nEr wird sie spaeter weiterleiten!";
+        deaf = MESSAGE_CACHE;
+      }
+      else {
+        reply += "Die Mitteilung ging verloren, denn "+
+                 "der Kobold kann sich nichts mehr merken!";
+        deaf = MESSAGE_CACHE_FULL;
+      }
+      if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
+        efun::tell_object(ti, reply+"\n");
+    }
+    return deaf;
+  }
+  else if((deaf || em) &&
+          ( (flag & MSGFLAG_RTELL) ||
+            (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
+    if (te && ti)
+      efun::tell_object(ti, reply);
+    return MESSAGE_DEAF;
+  }
+
+  _flush_cache(0);
+  if(te && QueryProp(P_AWAY))
+    msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
+
+  if (flag & (MSGFLAG_SAY | MSGFLAG_TELL) && comm_beep()) {
+    msg=MESSAGE_BEEP+msg;
+  }
+  efun::tell_object(ME, msg);
+  return MESSAGE_OK;
+}
+
+static int ignoriere(string str)
+{
+  str = _unparsed_args(1);
+  mapping ignore=Query(P_IGNORE, F_VALUE);
+
+  if (!str)
+  {
+    string* ignarr = m_indices(ignore);
+    if (!sizeof(ignarr))
+        tell_object(ME, "Du ignorierst niemanden.\n");
+      else
+        ReceiveMsg("Du ignorierst:\n"
+                   + break_string(CountUp(map(sort_array(ignarr, #'> ),
+                                          #'capitalize ) 
+                                         ) + ".",78),
+                   MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
+                   0,0,this_object());
+      return 1;
+  }
+  // trim spaces from args and convert to lower case.
+  str = lower_case(trim(str, TRIM_BOTH));
+
+  if (member(ignore, str))
+  {
+    RemoveIgnore(str);
+    tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
+  }
+  else if (sizeof(ignore)>100)
+  {
+   tell_object(ME, "Du ignorierst schon genuegend!\n");
+  }
+  else if (AddIgnore(str) == 1)
+  {
+    tell_object(ME,
+        sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
+  }
+  else
+  {
+    tell_object(ME,
+        sprintf("'%s' kannst Du nicht ignorieren.\n",str));
+  }
+  return 1;
+}
+
+
+static int _msg_beep(string str) {
+  int beep_interval;
+  notify_fail("Syntax: klingelton <1 bis 3600 Sekunden> oder klingelton aus\n");
+  if (stringp(str)) {
+    if (str=="aus")
+      SetProp(P_MESSAGE_BEEP,0);
+    else if ((beep_interval=to_int(str)) > 0 && beep_interval<=3600)
+      SetProp(P_MESSAGE_BEEP,beep_interval);
+    else return 0;
+  }
+
+  beep_interval=(int)QueryProp(P_MESSAGE_BEEP);
+  _notify("Ton bei Mitteilungen: "+
+        (beep_interval ? "aller "+beep_interval+" Sekunden." : "aus."),
+        query_verb());
+  return 1;
+}
+
+static int _msg_prepend(string str) {
+  int beep_interval;
+  notify_fail("Syntax: senderwiederholung ein/aus\n");
+  if (stringp(str)) {
+    if (str=="aus")  
+      SetProp(P_MESSAGE_PREPEND,1);
+    else if (str=="ein")  
+      SetProp(P_MESSAGE_PREPEND,0);
+    else return 0;
+  }
+
+  _notify("Senderwiederholung bei Mitteilungen: "+ 
+          ((int)QueryProp(P_MESSAGE_PREPEND) ?  "aus" : "ein")+".",
+          query_verb());
+
+  return 1;
+}
+
+static int _communicate(mixed str, int silent)
+{
+  string  verb;
+  string  myname;
+  string  msg;
+
+  if (!str || extern_call()) str=_unparsed_args()||"";
+  /* str=_unparsed_args()||""; */
+  verb = query_verb();
+  if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
+  if (str==""||str==" "||!str)
+  {
+    _notify("Was willst Du sagen?",MA_SAY);
+    return 1;
+  }
+  msg=permutate(str);
+
+  myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
+     !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
+    "":QueryProp(P_PRESAY))+name())||"";
+
+  // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
+  // ist, keine MT_COMM)
+  send_room(environment(), msg, MT_LISTEN, MA_SAY,
+            capitalize(myname)+" sagt: ", ({this_object()}) );
+
+  if(!silent)
+  {
+    ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
+               MA_SAY, "Du sagst: ", ME);
+  }
+  return 1;
+}
+
+static int _shout_to_all(mixed str)
+{
+  string pre, myname, realname, wizards_msg, players_msg;
+  string wizard_prefix, player_prefix;
+  int chars;
+
+  if (!(str=_unparsed_args()))
+  {
+    _notify("Was willst Du rufen?",MA_SHOUT);
+    return 1;
+  }
+  chars=sizeof(str)/2;
+  if (chars<4) chars=4;
+  pre = (!IS_LEARNER(ME) ||
+   QueryProp(P_INVIS) ||
+   !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
+  realname = capitalize((pre + capitalize(getuid()))||"");
+  myname = capitalize(pre + name()||"");
+  if (QueryProp(P_INVIS))
+    realname = "("+realname+")";
+
+  wizards_msg = permutate(str);
+  wizard_prefix = myname+" ruft: ";
+
+  if(QueryProp(P_FROG)) {
+    players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
+    player_prefix = myname+" quakt: ";
+  }
+  else {
+    players_msg = wizards_msg;
+    player_prefix = wizard_prefix;
+  }
+
+  if(!IS_LEARNER(this_player()))
+  {
+    if(QueryProp(P_GHOST)) {
+        _notify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
+                MA_SHOUT);
+        return 1;
+    }
+    if (QueryProp(P_SP) <(chars+20))
+    {
+      _notify("Du musst erst wieder magische Kraefte sammeln.",
+              MA_SHOUT);
+      _notify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
+              "Ebenen').", MA_SHOUT);
+      return 1;
+    }
+    SetProp(P_SP, QueryProp(P_SP) - chars - 20);
+  }
+
+  ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
+             "rufe", "Du rufst: ", ME);
+
+  foreach ( object ob : users()-({this_object()}) )
+    if ( IS_LEARNER(ob) )
+      ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR, MA_SHOUT, wizard_prefix,
+                    this_object());
+    else
+      ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR, MA_SHOUT, player_prefix,
+                    this_object());
+
+  return 1;
+}
+
+varargs int _tell(string who, mixed msg)
+{
+  object    ob;
+  string    away,myname,ret;
+  mixed     ignore,it;
+  string    *xname;
+  int       i,visflag;
+
+  if (extern_call() && this_interactive()!=ME) return 1;
+  if (!who || !msg) {
+    _notify("Was willst Du mitteilen?",MA_TELL);
+    return 1;
+  }
+
+  if(who == ERWIDER_PARAM)
+  {
+    if (!last_comm_partner)
+    {
+      _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
+          "Du was erwidern\nkoenntest.\n");
+      return 0;
+    }
+    who=last_comm_partner;
+  }
+
+  // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
+  if (who == ".")
+   who = ".1";
+
+  if ( sscanf(who, ".%d", i) == 1 ) {
+    if(i > 0 && i <= sizeof(commreceivers))
+      who = commreceivers[i-1];
+    else {
+      _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
+      return 0;
+    }
+  }
+
+  xname = explode(who, "@");
+
+  if (sizeof(xname) == 2) 
+  {
+    if ( QueryProp(P_QP) )
+    {
+      if (ret=(string)INETD->_send_udp(xname[1],
+                                    ([ REQUEST:   "tell",
+                                       RECIPIENT: xname[0],
+                                       SENDER:    getuid(ME),
+                                       DATA:     msg ]), 1))
+      {
+        _notify(ret, MA_TELL);
+      }
+      else
+      {
+        write("Nachricht abgeschickt.\n");
+        add_to_tell_history(who, 1, 0, msg,
+          "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_TELL, 1);
+      }
+    }
+    else
+      write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
+        "Muds etwas mitteilen zu koennen.\n");
+    return 1;
+  }
+
+  if (!ob=find_player(it = lower_case(who)))
+  {
+    it = match_living(it, 0);
+    if (!stringp(it))
+      switch(it) {
+      case -1:
+        _notify("Das war nicht eindeutig!",MA_TELL);
+        return 1;
+      case -2:
+        _notify("Kein solcher Spieler!",MA_TELL);
+        return 1;
+      }
+    ob = find_player(it) || find_living(it);
+    if(!ob) {
+      _notify("Kein solcher Spieler!",MA_TELL);
+      return 1;
+    }
+  }
+
+  if(QueryProp(P_INVIS)){
+    if(!IS_LEARNER(ob))
+      myname = name();
+    else
+      myname="("+
+             ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
+             capitalize(getuid()) + ")";
+  }
+  else
+    myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
+              QueryProp(P_PRESAY):"") + name();
+  if (myname && sizeof(myname)) myname=capitalize(myname);
+  // erstmal an Empfaenger senden
+  _send(ob, permutate(msg), MT_COMM|MT_FAR, MA_TELL,
+        myname + " teilt Dir mit: ");
+
+  // dann evtl. noch an Absender ausgeben...
+  if (visflag = !ob->QueryProp(P_INVIS) || IS_LEARNER(this_player()))
+    _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(it) + " mit: ");
+  // oder irgendwas anderes an den Absender ausgeben...
+  if (!visflag && interactive(ob))
+      _notify("Kein solcher Spieler!",MA_TELL);
+  else if (away = (string)ob->QueryProp(P_AWAY))
+      ReceiveMsg( break_string( away, 78, capitalize(it)
+                           + " ist gerade nicht da: ", BS_INDENT_ONCE ),
+          MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
+          MA_TELL, 0, this_object());
+  else if (interactive(ob) && (i=query_idle(ob))>=600)
+  { //ab 10 Mins
+      if (i<3600)
+        away=time2string("%m %M",i);
+      else
+        away=time2string("%h %H und %m %M",i);
+
+      _notify(sprintf("%s ist seit %s voellig untaetig.",
+              capitalize(it),away),
+              MA_TELL);
+    }
+
+  return 1;
+}
+
+static int _teile(string str)
+{
+  string who, message;
+  if (!(str=_unparsed_args())) return 0;
+  if (sscanf(str, "%s mit %s", who, message) == 2)
+    return _tell(who, message,1);
+  return 0;
+}
+static int _teile_mit_alias(string str)
+{
+  str = _unparsed_args(), TRIM_LEFT;
+  if (!str) return 0;
+  str = trim(str, TRIM_LEFT);
+  // Ziel muss min. 2 Buchstaben haben (.<nr>)
+  if (sizeof(str) < 4) return 0;
+  int pos = strstr(str, " ");
+  if (pos >= 2)
+    return _tell(str[..pos-1], str[pos+1..]);
+  return 0;
+}
+
+static int _erzaehle(string str)
+{
+  string who, message;
+
+  if (!(str=_unparsed_args())) return 0;
+  if (sscanf(str, "%s %s", who, message) == 2)
+    return _tell(who, message,1);
+  return 0;
+}
+
+static int _whisper(string str)
+{
+  object    ob;
+  string    who;
+  string    msg;
+  string    myname;
+
+  if (!(str=_unparsed_args()) ||
+       (sscanf(str, "%s zu %s", who, msg) != 2 &&
+        sscanf(str, "%s %s", who, msg) !=2 )) {
+    _notify("Was willst Du wem zufluestern?",MA_SAY);
+    return 1;
+  }
+  if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
+    _notify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
+    return 1;
+  }
+
+  myname = capitalize((((IS_LEARNER(ME) &&
+       !QueryProp(P_INVIS) &&
+       (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
+      QueryProp(P_PRESAY) : "") + name()) || "");
+
+  _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
+        MSG_SAY, myname + " fluestert Dir zu: ");
+  send_room(environment(),
+            myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
+            MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
+
+  _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
+
+
+  return 1;
+}
+
+static int _remote_whisper(string str)
+{
+  /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
+     Nachricht nicht in der tell_history verewigt. */
+
+  object    ob;
+  string    who, it;
+  string    msg;
+  string    myname;
+
+  if (!(str=_unparsed_args()) ||
+       (sscanf(str, "%s zu %s", who, msg) != 2 &&
+        sscanf(str, "%s %s", who, msg) !=2 )) {
+    _notify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
+    return 1;
+  }
+
+  if (!ob=find_player(it = lower_case(who)))
+  {
+    it = match_living(it, 0);
+    if (!stringp(it))
+      switch(it){
+      case -1:
+        _notify("Das war nicht eindeutig!",MA_EMOTE);
+        return 1;
+      case -2:
+        _notify("Kein solcher Spieler!",MA_EMOTE);
+        return 1;
+      }
+    ob = find_player(it);
+    if(!ob) ob = find_living(it);
+    if(!ob){
+      _notify("Kein solcher Spieler!",MA_EMOTE);
+      return 1;
+    }
+  }
+  if (environment(ob) == environment()) {
+    _notify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
+    return 1;
+  }
+
+  myname = capitalize((((IS_LEARNER(ME) &&
+       !QueryProp(P_INVIS) &&
+       (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
+      QueryProp(P_PRESAY) : "") + name()) || "");
+
+  // An Empfaenger senden.
+  _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE, MA_EMOTE,
+         myname + " fluestert Dir aus der Ferne zu: ");
+
+  // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
+  if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
+    _notify("Kein solcher Spieler!",MA_EMOTE);
+    return 1;
+  }
+  // sonst eigene Meldung via _recv() ausgeben.
+  else 
+    _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
+        "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
+
+  return 1;
+}
+
+static int _converse(string arg)
+{
+  _notify("Mit '**' wird das Gespraech beendet.",MA_SAY);
+  if (stringp(arg) && strstr(arg, "-s") == 0)
+    input_to("_converse_more", INPUT_PROMPT, "]", 1);
+  else
+    input_to("_converse_more", INPUT_PROMPT, "]", 0);
+  return 1;
+}
+
+static int _converse_more(mixed str, int silent)
+{
+  if (str == "**") {
+    _notify("Ok.",MA_SAY);
+    return 0;
+  }
+
+  if(str != "")
+    _communicate(str, silent);
+
+  input_to("_converse_more", INPUT_PROMPT, "]", silent);
+  return 1;
+}
+
+private int is_learner(object o) { return IS_LEARNER(o); }
+
+static int _shout_to_wizards(mixed str)
+{
+  int     i, j;
+  string    myname;
+  object   *u;
+
+  str = _unparsed_args();
+  if (!str||!sizeof(str)) {
+    _notify("Was willst Du den Magiern zurufen?",MA_SHOUT);
+    return 1;
+  }
+  // Kontrollzeichen rausfiltern.
+  str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
+  myname = capitalize(getuid(this_object()));
+  if (!IS_LEARNER(this_object()))
+    _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
+
+  // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
+  filter(users(), #'is_learner)->ReceiveMsg(str,
+      MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE,
+      MA_SHOUT, myname+" an alle Magier: ", this_object());
+
+  return 1;
+}
+
+static int _echo(string str) {
+  if (!IS_SEER(ME) || (!IS_LEARNER(ME)
+        && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
+    return 0;
+
+  if (!(str=_unparsed_args())) {
+    _notify("Was moechtest Du 'echoen'?", 0);
+    return 1;
+  }
+
+  if (!IS_LEARNER(this_interactive()))
+  {
+    if (QueryProp(P_GHOST))
+    {
+      _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
+      return 0;
+    }
+    if (QueryProp(P_SP)<ECHO_COST)
+    {
+      _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
+      return 0;
+    }
+    SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
+    str=">\b"+str;
+    log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
+           str));
+  }
+  // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
+  // Kommhistory noch im Kobold landen.
+  send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
+            MA_UNKNOWN, 0, 0);
+  return 1;
+}
+
+// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
+// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
+// und gefedert.
+static string *_set_ignore(mixed arg)
+{
+  raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
+      "Benutze AddIgnore/RemoveIgnore()!\n");
+}
+// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
+// nix dran, dass alle TestIgnore() & Co benutzen sollen.
+static string *_query_ignore() {
+  mixed ign=Query(P_IGNORE, F_VALUE);
+  if (mappingp(ign))
+    return m_indices(ign);
+  return ({});
+}
+
+public int AddIgnore(string ign) {
+  // Einige strings sind nicht erlaubt, z.B. konsekutive .
+  if (!sizeof(ign)
+      || regmatch(ign,"[.]{2,}",RE_PCRE)
+      || regmatch(ign," ",RE_PCRE)
+      || sizeof(explode(ign,"."))>3)
+    return 0;
+
+  mapping ignores=Query(P_IGNORE, F_VALUE);
+  ignores[ign]=time();
+  // kein Set() noetig.
+  return 1;
+}
+
+public int RemoveIgnore(string ign)
+{
+  mapping ignores=Query(P_IGNORE,F_VALUE);
+  m_delete(ignores,ign);
+  // Kein Set() noetig
+  return 1;
+}
+
+static int _query_intermud()
+{
+  mixed tmp;
+  return member(pointerp(tmp=Query(P_CHANNELS))?tmp:({}), "Intermud") > -1;
+}
+
+
+int erwidere(string str)
+{
+  str=_unparsed_args();
+  if (!str) return 0;
+  return _tell(ERWIDER_PARAM, str ,1);
+}
+
+static int tmhist(string str)
+{
+
+  if (str == "aus") {
+    tell_history_enabled = TELLHIST_DISABLED;
+    write("Ok, es wird nichts mehr gespeichert.\n");
+    if (sizeof(tell_history)) {
+      tell_history = ([]);
+      commreceivers = ({});
+      write("Deine Mitteilungsgeschichte wurde geloescht.\n");
+    }
+    return 1;
+  }
+
+  if (str == "namen") {
+    int flag;
+    tell_history_enabled = TELLHIST_NO_MESSAGE;
+    write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
+    foreach (string uid, struct chat_s chat: tell_history)
+      if (pointerp(chat->msgbuf)) {
+        chat->msgbuf = 0;
+        chat->ptr = 0;
+        flag = 1;
+      }
+    if (flag)
+      write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
+    return 1;
+  }
+
+  if (str == "ein" || str == "an") {
+    tell_history_enabled = TELLHIST_ENABLED;
+    write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
+    return 1;
+  }
+
+#ifdef TELLHIST_LONGLIFE
+  if (str == "langlebig") {
+    tell_history_enabled = TELLHIST_LONGLIFE;
+    write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
+          "Ende/Crash/\nReboot gespeichert.\n");
+    return 1;
+  }
+#endif
+
+  if (str == "status") {
+    switch (tell_history_enabled) {
+      case TELLHIST_DISABLED:
+        write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
+        break;
+      case TELLHIST_NO_MESSAGE:
+        write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
+        break;
+      case TELLHIST_ENABLED:
+        write("Deine Mitteilungen werden gespeichert.\n");
+        break;
+#ifdef TELLHIST_LONGLIFE
+      case TELLHIST_LONGLIFE:
+        write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
+              "Crash/Reboot\ngespeichert.\n");
+        break;
+#endif
+    }
+    return 1;
+  }
+
+  if (tell_history_enabled == TELLHIST_DISABLED) {
+    _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
+    return 0;
+  }
+
+  if (!sizeof(tell_history)) {
+    _notify_fail("Du hast noch keinem etwas mitgeteilt "
+                 "und noch keine Mitteilungen erhalten.\n");
+    return 0;
+  }
+
+  if (str && sizeof(str)) {
+
+    if (tell_history_enabled < TELLHIST_ENABLED) {
+      _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
+      return 0;
+    }
+
+    string uid;
+    if (member(tell_history, str)) {
+      // Name gewuenscht, da der String in der History vorkommt.
+      uid = str;
+    }
+    else {
+      // evtl. ne Zahl angegeben.
+      int i;
+      string *partners = sorted_commpartners(0);
+      if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
+        uid = partners[i];
+      else {
+        notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
+        return 0;
+      }
+    }
+
+    mixed *data = tell_history[uid]->msgbuf;
+    if (!data) {
+      _notify_fail(
+        "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
+      return 0;
+    }
+
+    int ptr = tell_history[uid]->ptr;
+
+    More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
+                              data[0..ptr-1],
+         function string (struct stored_msg_s msg) {
+             if (!structp(msg)) return "";
+               return break_string( msg->msg + " <"
+                 + strftime("%H:%M:%S",msg->timestamp) + ">", 78,
+                 msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
+         } ) ) );
+    return 1;
+  }
+
+  string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
+  int i;
+  foreach (string uid : sorted_commpartners(0) ) {
+    int j;
+    struct chat_s chat = tell_history[uid];
+    history += sprintf("%2d.%-4s %s  %-11s  %d gesendet/%d empfangen\n", ++i,
+      ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
+      strftime("%a, %e.%m.%y",chat->time_last_msg),
+      capitalize(chat->uid), chat->sentcount, chat->recvcount);
+  }
+
+  More(history);
+
+  return 1;
+}
+
+static mixed _query_localcmds()
+{
+  return ({
+    ({"kobold", "cmd_kobold",0,0}),
+     ({"sag","_communicate",0,0}),
+     ({"sage","_communicate",0,0}),
+     ({"'","_communicate",1,0}),
+     ({"mruf","_shout_to_wizards",0,0}),
+     ({"mrufe","_shout_to_wizards",0,0}),
+     ({"ruf","_shout_to_all",0,0}),
+     ({"rufe","_shout_to_all",0,0}),
+     ({"erzaehl","_erzaehle",0,0}),
+     ({"erzaehle","_erzaehle",0,0}),
+     ({"teil","_teile",0,0}),
+     ({"teile","_teile",0,0}),
+     ({"tm","_teile_mit_alias",0,0}),
+     ({"fluester","_whisper",0,0}),
+     ({"fluestere","_whisper",0,0}),
+     ({"rfluester","_remote_whisper",0,0}),
+     ({"rfluestere","_remote_whisper",0,0}),
+     ({"gespraech","_converse",0,0}),
+     ({"echo","_echo",0,0}),
+     ({"ignorier","ignoriere",0,0}),
+     ({"ignoriere","ignoriere",0,0}),
+     ({"tmhist","tmhist",0,0}),
+     ({"erwider","erwidere",0,0}),
+     ({"erwidere","erwidere",0,0}),
+     ({"klingelton","_msg_beep",0,0}),
+     ({"senderwiederholung","_msg_prepend",0,0}),
+     ({"report","set_report",0,0}),
+    })+channel::_query_localcmds();
+}
+
+private string *sorted_commpartners(int reversed) {
+  return sort_array(m_indices(tell_history),
+      function int (string uid1, string uid2) {
+          if (reversed)
+            return tell_history[uid1]->time_last_msg >
+                   tell_history[uid2]->time_last_msg;
+          else
+            return tell_history[uid1]->time_last_msg <=
+                   tell_history[uid2]->time_last_msg;
+      } );
+}
+
+// Eigentlich nur in Magierobjekten gerufen. Gehoert aber thematisch hier
+// irgendwie hin.
+static void modify_prompt() {
+    string text = Query(P_PROMPT, F_VALUE);
+
+    if ( !stringp(text) || !sizeof(text) )
+        text = "> ";
+    else {
+        string path = Query(P_CURRENTDIR, F_VALUE);
+        if (stringp(path) && sizeof(path))
+          text = regreplace(text,"\\w",path,0); // Pfad einsetzen
+    }
+    configure_interactive(this_object(), IC_PROMPT, text);
+}
+
+// Prueft auf Ingoriereeintraege.
+// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
+#ifdef __LPC_UNIONS__
+public int TestIgnore(string|string* srcnames)
+#else
+public int TestIgnore(mixed srcnames)
+#endif
+{
+  mapping ign = Query(P_IGNORE, F_VALUE);
+  if (stringp(srcnames))
+    srcnames = ({srcnames});
+
+  foreach(string srcname: srcnames)
+  {
+    // einfachster Fall, exakter Match
+    if (member(ign, srcname))
+      return MSG_IGNORED;
+    // ansonsten muss aufgetrennt werden.
+    if (strstr(srcname,".") > -1)
+    {
+      string *srcparts=explode(srcname,".");
+      switch(sizeof(srcparts))
+      {
+        // case 0 und 1 kann nicht passieren.
+        case 3:
+          // zu pruefen: [sender].aktion.qualifizierer.
+          // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
+          // oben schon geprueft. im Spieler geprueft werden muss noch:
+          // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
+          if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
+              || member(ign, "."+srcparts[1])      // .aktion
+              || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
+              || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
+             )
+          {
+            return MSG_IGNORED;
+          }
+          break;
+        case 2:
+          // zu pruefen: spieler.aktion
+          // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
+          // oben schon geprueft. Im Spieler zu pruefen ist noch:
+          // spieler und .aktion
+          if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
+              || member(ign, "."+srcparts[1]))
+          {
+            return MSG_IGNORED;
+          }
+          break;
+        default: // mehr als 3 Teile...
+          raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
+                "is supported. Got: %s\n",srcname));
+          break;
+      }
+    }
+  }
+  // Default: nicht ignorieren.
+  return 0;
+}
+
+#ifdef __LPC_UNIONS__
+public int TestIgnoreExt(string|string* srcnames)
+#else
+public int TestIgnoreExt(mixed srcnames)
+#endif
+{
+  return TestIgnore(srcnames);
+}
+
+// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
+// Typen. 
+// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
+private int check_ignores(string msg, int msg_type, string msg_action,
+                            string msg_prefix, object origin)
+{
+  // Einige Dinge lassen sich nicht ignorieren.
+  if (msg_type & (MT_NEWS|MT_NOTIFICATION))
+    return 0;
+  // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
+  // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
+  // eine ignorierbare msg_action geben.
+  else if (stringp(msg_action) && origin && origin != ME)
+  {
+    string srcname =
+      (query_once_interactive(origin) ? origin->query_real_name()
+                                      : origin->name(WER) || "");
+    mapping ign = Query(P_IGNORE, F_VALUE);
+
+    if (member(ign, srcname))
+      return MSG_IGNORED;
+    // vielleicht wird irgendwas a la name.aktion ignoriert?
+    // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
+    // Ebenenmeldungen)
+    if (member(ign, srcname+"."+msg_action))
+      return MSG_VERB_IGN;
+    // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
+    // das reichlich sinnfrei ist.
+    if (member(ign, "."+msg_action))
+      return MSG_VERB_IGN;
+    // Spieler auf Ebenen ignoriert?
+    // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
+    if (strstr(msg_action, MA_CHANNEL) == 0)
+    {
+      // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
+      if (member(ign, srcname + "."MA_CHANNEL))
+        return MSG_IGNORED;
+      // spieler.ebenen.ebenenname ist oben schon abgedeckt.
+      // .ebenen halte ich fuer sinnfrei, nicht geprueft.
+    }
+    // Spieler aus anderem mud? *seufz*
+    if (strstr(srcname,"@") > -1)
+    {
+      string *srcparts = explode(srcname,"@");
+      if (sizeof(srcparts)==2)
+      {
+        // spieler@?
+        if (member(ign, srcparts[0]+"@"))
+          return MSG_IGNORED;
+        // oder Mud per @mud?
+        if (member(ign, "@" + srcparts[1]))
+          return MSG_MUD_IGN;
+        // BTW: spieler@mud wurde schon ganz oben erfasst.
+      }
+    }
+  }
+  // Default: nicht ignorieren.
+  return 0;
+}
+
+// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
+// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
+// BLindheit/Taubheit).
+// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
+// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
+// Sinn gar nix zu tun hat.
+// Rueckgabe: 0 oder MSG_SENSE_BLOCK
+private int check_senses(string msg, int msg_type, string msg_action,
+                              string msg_prefix, object origin)
+{
+  int senses = msg_type & (MT_LOOK|MT_LISTEN);
+  // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
+  // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
+  // unterdrueckt werden.
+  if (!senses)
+    return 0;
+
+  if ((senses & MT_LOOK) && CannotSee(1))
+    senses &= ~MT_LOOK;  // Sinn loeschen
+
+  if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
+    senses &= ~MT_LISTEN;
+
+  // wenn kein Sinn mehr ueber, wird die Nachricht nicht wahrgenommen.
+  if (!senses)
+    return MSG_SENSE_BLOCK;
+
+  return 0;
+}
+
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+                              string msg_prefix, object origin)
+{
+  if (!msg) return MSG_FAILED;
+
+  // Flags und Typen spalten
+  int flags = msg_type & MSG_ALL_FLAGS;
+  int type = msg_type & ~flags;
+
+  // ggf. defaults ermitteln
+  origin ||= previous_object();
+  msg_action ||= comm_guess_action();
+  type ||= comm_guess_message_type(msg_action, origin);
+
+  // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
+  if (msg_type & MT_DEBUG)
+  {
+    if (!QueryProp(P_WIZ_DEBUG)
+        || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
+    return MSG_FAILED;
+  }
+
+  // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
+  // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
+  // Weiterbearbeitung.
+  // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
+  if (!(flags & MSG_DONT_IGNORE))
+  {
+    // Sinne pruefen. (nur typen uebergeben, keine Flags)
+    int res=check_senses(msg, type, msg_action, msg_prefix, origin);
+    if (res) return res;
+
+    // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
+    res=check_ignores(msg, type, msg_action, msg_prefix, origin);
+    if (res) return res;
+  }
+
+  // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
+  if ((type & MT_COMM))
+  {
+    // erstmal in der Komm-History ablegen, wenn gewuenscht.
+    if ((!(flags & MSG_DONT_STORE)))
+    {
+      string uid;
+      if (query_once_interactive(origin))
+        uid = origin->query_real_name();
+      else
+        uid = origin->name(WER) || "<unbekannt>";
+      add_to_tell_history(uid, 0, 1, msg, msg_prefix, 0);
+    }
+
+    // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
+    // Ebenenmeldungen. (Die haben ggf. schon.)
+    if (stringp(msg_action) && QueryProp(P_AWAY)
+        && strstr(msg_action, MA_CHANNEL) != 0)
+    {
+      // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
+      if (msg[<1] == '\n')
+        msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
+      else
+        msg = msg + " [" + strftime("%H:%M",time()) + "]";
+    }
+    // Kobold erlaubt und gewuenscht? Kobold ist fuer die
+    // direkte Kommunikation mittels MT_COMM vorgesehen.
+    // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
+    // ignorieren.
+    // die if-Konstruktion ist so, weil ich das _flush_cache() im else
+    // brauche.
+    if (query_editing(this_object()) || query_input_pending(this_object())
+        || QueryProp(P_EARMUFFS))
+    {
+      if (!(flags & MSG_DONT_BUFFER)
+            && QueryProp(P_BUFFER))
+      {
+        // Nachricht soll im Kobold gespeichert werden.
+        return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
+      }
+    }
+    else
+    {
+      // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
+      // entleeren.
+      _flush_cache(0);
+    }
+
+    // ggf. Piepston anhaengen. NACH Koboldablage, die sollen erstmal keinen
+    // Pieps kriegen.
+    if (comm_beep())
+      msg=msg + MESSAGE_BEEP;
+  }
+
+  // Ausgabenachricht bauen und an den Spieler senden.
+  if (flags & MSG_DONT_WRAP)
+    msg = (msg_prefix ? msg_prefix : "") + msg;
+  else
+  {
+    int bsflags = flags & MSG_ALL_BS_FLAGS;
+    if (QueryProp(P_MESSAGE_PREPEND))
+      bsflags |= BS_PREPEND_INDENT;
+    msg = break_string(msg, 78, msg_prefix, bsflags);
+  }
+  efun::tell_object(ME, msg);
+
+  return MSG_DELIVERED;
+}
diff --git a/std/player/comm_structs.c b/std/player/comm_structs.c
new file mode 100644
index 0000000..5f268eb
--- /dev/null
+++ b/std/player/comm_structs.c
@@ -0,0 +1,41 @@
+// MorgenGrauen MUDlib
+//
+// player/comm.c-- basic player communiction commands
+//
+// $Id: comm.c 6918 2008-08-07 21:13:16Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+struct msg_s {
+  string msg;       // Inhalt der Nachricht
+  int type;         // Messagetyp fuer ReceiveMsg
+  string action;    // Messageaction fuer ReceiveMsg
+  string prefix;    // Einrueckung der Nachricht bei Darstellung (msg_prefix)
+  string sendername;// Ursprung der Nachricht
+};
+
+struct stored_msg_s (msg_s) {
+  int timestamp;    // Zeitstempel der Nachricht
+};
+
+struct msg_buffer_s {
+  //struct msg_s *buf;
+  mixed *buf;
+  int index;
+};
+
+struct chat_s {
+  string uid;           // UID des Gespraechspartners
+  int time_first_msg;   // Zeit der ersten Nachricht
+  int time_last_msg;    // Zeit der letzen Nachricht
+  int sentcount;        // Anzahl gesendeter Nachrichten
+  int recvcount;        // Anzahl empfangener Nachrichten
+  mixed msgbuf;         // Array von msg_s (Art Ringpuffer)
+  int ptr;              // Pointer auf die naechste zu ueberschreibende msg_s
+                        // in msgbuf
+};
+
diff --git a/std/player/command.c b/std/player/command.c
new file mode 100644
index 0000000..230445d
--- /dev/null
+++ b/std/player/command.c
@@ -0,0 +1,1074 @@
+// MorgenGrauen MUDlib
+//
+// player/commands.c -- alias, history and player command handling
+//
+// $Id: command.c 9576 2016-06-18 15:00:01Z Zesstra $
+#pragma strong_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player/command.h>
+#include <player/comm.h>
+#include <thing/properties.h>
+#include <living/moving.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <language.h>
+#include <new_skills.h>
+#include <config.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <logging.h>
+#include <strings.h>
+
+#define CBLOG(x)    log_file(SHELLLOG("DISABLECOMMANDS"),x,200000)
+
+#define HIST_SIZE 40
+#define EPMASTER "/secure/explorationmaster"
+
+private mapping aliases;
+private string *commands;
+private int hist_size, show_processing, histmin;
+private string default_notify_fail;
+private nosave string *history, *unparsed_args, unmodified;
+private nosave int hist_now;
+private nosave object last_command_env;
+private nosave int cmds_per_time, last_chg, max_commands, *cmd_types;
+// Datenstruktur: ({Setzer, Ablaufzeit, String/Closure})
+private nosave mixed disablecommands;
+private nosave object* syntaxdb;
+
+nomask void __set_bb(int flag);
+
+static varargs int __auswerten(string str, string intern);
+varargs int SoulComm(string str, string _verb);
+varargs mixed More(string str, int fflag, string returnto);
+static int _starts_with(string str, string start);
+static void reallocate_histbuf();
+
+private void AddHistory(string str)
+{
+  if (!stringp(str) || str=="" || str[0]=='&' || str[0]=='^' ||
+      str=="hist")
+    return;
+  if (!hist_size) return;
+  if (!pointerp(history) || sizeof(history)!=hist_size)
+    reallocate_histbuf();
+  if (sizeof(str)>=histmin && history[(hist_size+hist_now-1)%hist_size]!=str)
+    history[(hist_now++)%hist_size]=str;
+}
+
+static void create()
+{
+  last_chg=0;
+  histmin=hist_now=0;
+  Set(P_LOCALCMDS,({}));
+  Set(P_LOCALCMDS,PROTECTED,F_MODE_AS);
+  Set("_syntaxdb", SECURED|SAVE, F_MODE_AS);
+
+  show_processing=1;
+  unparsed_args=({0,0,0});
+  hist_size=HIST_SIZE;
+}
+
+static int replacedisplay(string str)
+{
+  if (!str || str=="" || !sscanf(str,"%d",show_processing))
+    printf("Unzulaessige Eingabe!\n%s 0|1|2\n",query_verb());
+    printf("Ersetzungsanzeige auf Level %d.\nLevel 0: Nichts anzeigen\n"+
+     "Level 1: Nur History-Ersetzungen anzeigen\n"+
+     "Level 2: History- und Alias-Ersetzungen anzeigen\n",show_processing);
+  if (show_processing>2&&!IS_WIZARD(ME)) show_processing=2;
+  return 1;
+}
+
+static int histmin(string str)
+{
+  int len;
+
+  if (!str||!sscanf(str,"%d",len)||len<0)
+  {
+    write("Benutzung: histmin ZAHL\nLegt die Mindestlaenge fest, die eine \
+Befehlszeile haben muss, um in den\nHistory-Puffer zu gelangen. Derzeit \
+eingestellt auf "+(string)histmin+" Zeichen.\n");
+    return 1;
+  }
+  histmin=len;
+  write("Mindestlaenge auf "+(string)len+" eingestellt.\n");
+  return 1;
+}
+
+static void reallocate_histbuf()
+{
+  int i;
+
+  history=allocate(hist_size);
+  hist_now=0;
+  for (i=0;i<hist_size;i++)
+    if (!stringp(history[i]))
+      history[i]="\n\n";
+}
+
+static int histlen(string str)
+{
+  int d;
+  if (!str||!sscanf(str,"%d",d)||d<0||d>40)
+  {
+    write("Benutzung: histlen ZAHL\nZAHL muss zwischen 0 und 40 liegen.\n");
+    printf("Deine History-Buffer-Laenge liegt bei %d Befehlen.\n",hist_size);
+    return 1;
+  }
+  hist_size=d;
+  printf("Deine History-Buffer-Laenge liegt jetzt bei %d Befehlen.\n",
+   hist_size);
+  reallocate_histbuf();
+  return 1;
+}
+
+static void initialize()
+{
+  if (!pointerp(history)||sizeof(history)!=hist_size)
+    reallocate_histbuf();
+  add_action("__auswerten","",1);
+    max_commands = EPMASTER->QueryCommands();
+    cmd_types = EPMASTER->QueryCmdTypes() || ({});
+
+    if ( !mappingp(aliases) )
+        aliases = ([]);
+
+    if ( !pointerp(commands) )
+        commands = ({});
+
+    if (QueryProp("_syntaxdb"))
+        syntaxdb = ({find_object("/secure/syntaxdb")});
+/*    else if (QueryProp(P_TESTPLAYER))
+    {
+      SetProp("_syntaxdb", 1);
+      call_out(#'_notify, 2,
+          "\nDa Du als Testspieler markiert bist, wurde bei Dir "
+          "die Syntaxsammlung eingeschaltet. Du kannst dies "
+          "wieder ausschalten. (hilfe syntaxsammlung) "
+          "Es waere schoen, wenn Du es beim Testen von "
+          "Gebieten einschaltest.", 0);
+    }*/
+}
+
+static mixed _set_default_notify_fail(string s)
+{
+  if (stringp(s)&&s!="")
+  {
+    if (s[<1]!='\n') s+="\n";
+    return default_notify_fail=s;
+  }
+  else if (!s||s=="")
+    return (default_notify_fail=0);
+}
+
+static mixed _query_default_notify_fail()
+{
+  return default_notify_fail;
+}
+
+static int set_errormessage(string s)
+{
+  if (!(s=_unparsed_args()))
+  {
+    _set_default_notify_fail(0);
+    write("Standard-Fehlermeldung auf \"Wie bitte?\" gesetzt.\n");
+  } else
+  {
+    write(break_string(sprintf("Standard-Fehlermeldung auf %s gesetzt.\n",
+             s),78));
+    _set_default_notify_fail(s);
+  }
+  return 1;
+}
+
+void reconnect()
+{
+  if (!mappingp(aliases)) aliases=([]);
+
+    if ( !pointerp(commands) )
+        commands = ({});
+
+    max_commands = EPMASTER->QueryCommands();
+    cmd_types = EPMASTER->QueryCmdTypes() || ({});
+}
+
+static int show_hist()
+{
+  int i;
+  string comm;
+
+  tell_object( ME, "Die History-Liste enthaelt folgende Kommandos:\n" );
+
+  for( i = 0; i < hist_size; i++ )
+      if ((comm=history[(hist_now+i)% hist_size])!= "\n\n")
+          tell_object( ME, " &"+(hist_now+i-hist_size)+"/-"+ (hist_size-i-1)
+      +"\t= "+comm+"\n");
+  return 1;
+}
+
+static string present_alias(mixed *ali)
+{
+  int j,k;
+  string s,s2;
+
+  for (s="",j=sizeof(ali)-1;j>=0;j--)
+    if (intp(ali[j]))
+      if ((k=ali[j])<0)
+  s="$"+(k==-1?"":(string)-k)+"*"+s;
+      else
+  s="$"+(string)k+s;
+    else
+      {
+         s2=implode(explode(ali[j],"\\"),"\\\\");
+         s=implode(explode(s2,"$"),"\\$")+s;
+      }
+  return s;
+}
+
+#define ALIFORMAT ({" %s\t= %s", "alias %s %s"})[display_as_aliascommand]
+// Ich weiss, den Variablennamen im define zu haben ist unfein, aber das
+// macht es im Code dann angenehm uebersichtlich.  -HrT
+
+static int query_aliases(int display_as_aliascommand)
+{
+  int i;
+  string *a,*ali;
+
+  if(i=sizeof(ali=sort_array(m_indices(aliases),#'<))) //')))
+  {
+    for(a=({}),i--; i>=0; i--)
+        a+=({sprintf(ALIFORMAT, ali[i], present_alias( aliases[ali[i]] ) ) });
+    More("Du hast folgende Aliase definiert:\n"+implode(a,"\n"));
+  }
+  else
+    write("Du hast keine Aliase definiert.\n");
+  return 1;
+}
+
+static int
+_starts_with(string str, string start)
+{
+  return (sizeof(start)>sizeof(str) ? 0
+    : str[0..sizeof(start)-1]==start);
+}
+
+static int alias(string str)
+{
+  string command;
+  string *tmp,um,*hits;
+  int num, l, pos, cont;
+  int display_as_aliascommand;
+
+  if (unmodified&&unmodified!="")
+    um=implode(old_explode(unmodified," ")[1..]," ");
+  if (um=="") um=0;
+  if(!(str=um||_unparsed_args()) || str=="*") return query_aliases(0);
+
+    if (str=="-a" || strstr(str, "-a ")==0 )  {
+    str=str[2..];
+    if (str && str!="" && str[0]==' ') str=str[1..];
+    if (!str || str=="" || str=="*") return query_aliases(1);
+    display_as_aliascommand=1;
+  }
+
+  if ((pos=member(str,' '))<0) // 1 Arg only
+  {
+    if ((tmp=aliases[str]))
+      printf(ALIFORMAT+"\n",str,present_alias(tmp));
+    else
+      if (str[<1]=='*')
+      {
+        str=str[0..<2];
+        hits=filter(m_indices(aliases), #'_starts_with, str);
+        if (!sizeof(hits))
+        {
+          printf("Du hast kein Alias, das mit \"%s\" anfaengt.\n", str);
+          return 1;
+        }
+        hits=sort_array(hits, #'>);
+        for (l=sizeof(hits); l--;)
+          hits[l]=sprintf(ALIFORMAT, hits[l], present_alias(aliases[hits[l]]));
+        More("Folgende Aliase beginnen mit \""+str+"\":\n"+implode(hits,"\n"));
+      }
+    else
+      printf("Du hast kein Alias \"%s\" definiert.\n",str);
+    return 1;
+  }
+  if (!pos)
+  {
+    write("Fehler: Blanc am Alias-Anfang\n");
+    return 1;
+  }
+  if ((command=str[0..pos-1])=="unalias")
+  {
+    write
+      ("Es nicht moeglich, den Befehl unalias zu ueberladen (waer dumm :))\n");
+    return 1;
+  }
+  if ((command=str[0..pos-1])=="*")
+  {
+    write
+      ("Es nicht moeglich, den Befehl \"*\" zu ueberladen.\n");
+    return 1;
+  }
+
+  str=str[pos+1..],tmp=({});
+  while (l=sizeof(str)) {
+    pos=0,cont=1;
+    while (cont) {
+      if (pos<l) {
+        if(str[pos]=='\\') {
+          str=str[0..pos-1]+str[pos+1..];
+          l--;
+        } else {
+          if (str[pos]=='&' || str[pos]=='$') {
+            cont=0;
+            if (pos>0) {
+              tmp+=({str[0..pos-1]});
+            }
+            if (pos==l-1) {
+              printf("Fehler: %c am Zeilenende\n",str[pos]);
+              return 1;
+            }
+            if ((num=str[++pos])=='*') {
+              num=1;
+              pos--;
+            } else {
+              num-='0';
+            }
+            if (num<0 || num>9) {
+              printf("Fehler: Nach %c muss Ziffer oder * folgen\n",
+               str[pos-1]);
+              return 1;
+            }
+            if ((str=str[pos+1..])!=""&&str[0]=='*') {
+              str=str[1..];
+              num=-num;
+            }
+            tmp+=({num});
+          }
+        }
+        pos++;
+      } else {
+        cont=0;
+        if (str!="") tmp+=({str});
+        str="";
+      }
+    }
+  }
+  if ((!aliases[command]) && (sizeof(aliases)>2000))
+    printf("Du hast schon genuegend Aliase definiert!\n");
+  else
+  {
+    aliases[command]=tmp;
+    printf("Neues Alias: %s\t= %s\n",command,present_alias(tmp));
+  }
+  return 1;
+}
+
+static int unalias(string str) {
+  int i;
+  string *als,um;
+
+  if (unmodified&&unmodified!="")
+    um=implode(old_explode(unmodified," ")[1..]," ");
+  if (um=="") um=0;
+  if ( !(str=um || _unparsed_args())) return 0;
+  if (str == "*.*" || str == "*") {
+    write(break_string(
+      "Versuchs mal mit 'unalias .*', wenn Du wirklich alle Alias entfernen "
+      "willst.",78));
+    return 1;
+  }
+  if (!member(aliases,str)) {
+    als=regexp(m_indices(aliases),("^"+str+"$"));
+    if (!(i=sizeof(als))) {
+      write("So ein Alias hast Du nicht definiert.\n");
+      return 1;
+    }
+    for (--i;i>=0;i--)
+      m_delete(aliases,als[i]);
+    write(break_string(("Du entfernst folgende Aliase: "+
+      implode(als," ")+".\n"),75));
+    return 1;
+  }
+  m_delete(aliases,str);
+  write("Du entfernst das Alias \""+str+"\".\n");
+  return 1;
+}
+
+varargs string _unparsed_args(int level)
+{
+  return unparsed_args[level];
+}
+
+#define ARTIKEL ({"das","der","die","des","dem","den","ein","eine","einer",\
+                  "eines"})
+
+#define TRENNER ({"in","aus","ueber","auf","unter","mit","durch","fuer",\
+                  "von","vom","im","aufs","ein","weg","zurueck"})
+
+static string _single_spaces(string str)
+{
+  return regreplace(str, "  *", " ", 1);
+}
+
+static mixed _return_args(string str)
+{
+  string *t,*t2,verb,s2;
+  int i,l,j,l2;
+
+  t=explode(trim(str,TRIM_BOTH)," ");
+  verb=t[0];
+  t = t[1..];
+  if (!sizeof(t))
+  {
+    unparsed_args[0]=unparsed_args[1]=unparsed_args[2]=0;
+    return str=verb;
+  }
+  else
+    str = unparsed_args[0] = implode(t, " ");
+
+  str=unparsed_args[1]=lower_case(_single_spaces(str));
+  t=regexplode(str,"\\<im\\>|\\<ins\\>");
+  for (i=1;i<sizeof(t);i+=2) t[i]="in";
+  t=regexplode(implode(t,""),"[\\,\\!\\:][\\,\\!\\:]*");
+  l=sizeof(t);
+  for(i=1;i<l;i+=2) t[i]="";
+  t=old_explode(implode(t,"")," ")-({""});
+  for (i=sizeof(t)-2;i>=0;i--)
+  {
+    if (member(ARTIKEL,t[i])>=0)
+      t=t[0..i-1]+t[i+1..];
+  }
+  unparsed_args[2]=implode(t," ");
+  t=regexplode((str=implode(t," ")),"[0-9][0-9]*\\.");
+  if ((l=sizeof(t))>2)
+  {
+    i=1;
+    while (i<l-1)
+    {
+      t[i]=" "+t[i][0..<2]+" ";
+      if ((l2=sizeof(t2=old_explode(t[i+1]," ")))<2)
+  t[i+1]+=t[i];
+      else
+      {
+  for (j=1;j<l2;j++)
+  {
+    if (member(TRENNER,t2[j])>=0)
+    {
+      t2[j-1]+=t[i];
+      l2=0;
+    }
+  }
+  if (!l2)
+    t[i+1]=implode(t2," ");
+  else
+    t[i+1]+=t[i];
+      }
+      t[i]="";
+      i+=2;
+    }
+    str=_single_spaces(verb+" "+implode(t," "));
+    if (str[<1]==' ') str=str[0..<2];
+  } else str=verb+(str==""?"":" "+str);
+  if (show_processing>2)
+    printf("-> {%s}\n",str);
+  return str;
+}
+
+static void decay_average()
+{
+  if (absolute_hb_count()-last_chg>14)
+  {
+    last_chg=absolute_hb_count()-last_chg;
+    if (last_chg>3000)
+      last_chg=absolute_hb_count(),cmds_per_time=0;
+    else
+    {
+      while (last_chg>14)
+  cmds_per_time=cmds_per_time*9/10, last_chg-=15;
+      last_chg=absolute_hb_count()-last_chg;
+    }
+  }
+}
+
+private void DelayPreparedSpells() {
+  mixed ps;
+
+  if (pointerp(ps=QueryProp(P_PREPARED_SPELL))
+      && sizeof(ps)>=1 && intp(ps[0])) {
+    ps[0]++;
+    SetProp(P_PREPARED_SPELL,ps);
+    write("Die Ausfuehrung Deines vorbereiteten Spruches wird verzoegert.\n");
+  } else if (ps) {
+    SetProp(P_PREPARED_SPELL,0);
+  }
+}
+
+static mixed bb;
+#ifndef BBMASTER
+#define BBMASTER "/secure/bbmaster"
+#endif
+
+/** Interpretiert Aliase und History-Kommandos
+  Eigentlich muesste hier noch die Umwandlung der Sonderzeichen
+  verschiedener Zeichensaetze mit convert_charset gemacht werden,
+  aber noch gibt es keine Moeglichkeit, den vom Spieler genutzten
+  Zeichensatz zu identifizieren.
+  \param[in] str string - Kommando des Spielers
+  \return interpretiertes Alias bzw. korrektes Kommando aus der History
+*/
+private string parsecommand(string str)
+{
+  if (str[0]=='\\')
+  {
+    // Kommando soll nicht interpretiert werden
+    return str[1..];
+  }
+  else if (str[0]=='&')
+  {
+    // Kommando aus der History
+    string cmd = str[1..];
+    int cmd_size = sizeof(cmd);
+    int cmd_found = 0;
+    if (cmd_size)
+    {
+      // Test ob &<text> etwas findet
+      for (int i=0;i<hist_size-1 && !cmd_found;i++)
+      {
+        int idx = (hist_size-i+hist_now-1)%hist_size;
+        if (history[idx][0..cmd_size-1]==cmd)
+        {
+          str = history[idx];
+          cmd_found = 1;
+        }
+        if (cmd_found)
+        {
+          if (show_processing)
+            printf("[%s]\n",str);
+        }
+      }
+    }
+    if (!cmd_found)
+    {
+      // Test, ob &<nr> klappt
+      int nummer;
+      if (str=="&&")
+        str = "&-0";
+      if (sscanf(str,"&%d",nummer))
+      {
+        if (nummer<0 || (!nummer && str[1]=='-'))
+        {
+          if (nummer<-(hist_size-1))
+            nummer=-1;
+          else
+            nummer=(hist_now+nummer-1+hist_size)%hist_size;
+        }
+        else
+        {
+          if (nummer>hist_now || hist_now-nummer>hist_size)
+            nummer=-1;
+          else
+            nummer=nummer%hist_size;
+        }
+        if (nummer<0 
+            || ( (cmd=history[nummer]) =="\n\n") )
+          notify_fail("Der Befehl ist nicht in der History!\n");
+        else
+        {
+          str = cmd;
+          if (show_processing)
+            printf("[%s]\n",str);
+        }
+      }
+    }
+  }
+  switch (str)
+  {
+    case "n": return "norden";
+    case "s": return "sueden";
+    case "w": return "westen";
+    case "o": return "osten";
+    case "nw": return "nordwesten";
+    case "sw": return "suedwesten";
+    case "so": return "suedosten";
+    case "no": return "nordosten";
+    case "ob": return "oben";
+    case "u": return "unten";
+  }
+  // Test auf Alias
+  string output = "";
+  string* input = explode(str," ");
+  int input_size = sizeof(input);
+  mixed alias = aliases[input[0]];
+  if (!alias)
+    return str;
+  foreach (mixed a:alias)
+  {
+    if (!intp(a))
+      output += a;
+    else
+    {
+      if (a >= 0)
+      {
+        if (input_size > a)
+          output += input[a];
+      }
+      else
+      {
+        a = -a;
+        if (input_size > a)
+          output += implode(input[a..]," ");
+      }
+    }
+  }
+  output = _single_spaces(output);
+  str = trim(output,TRIM_RIGHT);
+  if (show_processing>1)
+    printf("[%s]\n",str);
+  return str;
+}
+
+/** Behandelt alle Sonderfaelle der Eingabe des Spielers
+  Alle Befehle des Spielers, die nicht durch Objekte behandelt
+  werden sollen, werden hier erkannt und ausgefuehrt.
+  Dazu gehoert auch die Interpretation von Aliases und History-
+  befehlen.
+  \param[in] str string: Kommando des Spielers
+  \return auszufuehrendes Kommando
+    oder 0 fuer ein nicht interpretierbares Kommando
+    oder 1 fuer ein bereits durchgefuehrtes Kommando
+*/
+mixed modify_command(string str)
+{
+
+  if (extern_call() && previous_object() &&
+      (previous_object()!=this_object() || process_call()) )
+  {
+    return 0;
+  }
+
+  // Leerzeichen an den Enden abschneiden.
+  str = trim(str, TRIM_BOTH);
+
+  if (bb)
+    BBMASTER->BBWrite(trim(str,TRIM_RIGHT,"\n"), 0);
+
+  decay_average();
+  cmds_per_time+=10000;
+
+  unparsed_args[0]=unparsed_args[1]=unparsed_args[2]=unmodified=""; 
+
+  if (!sizeof(str)) return "";
+
+  // Kommando wird geparst
+  unmodified=parsecommand(str);
+
+  // Environment schonmal merken.
+  last_command_env=environment();
+
+  if (unmodified == "")
+      return "";
+  // Kommando in History merken, auch wenn es im Kommandoblock abgebrochen
+  // wird.
+  AddHistory(unmodified);
+
+  // pruefen, ob Kommandoblock gesetzt ist.
+  // (Fuer Magier mit mschau ein wird das ignoriert.)
+  // BTW: Es wird absichtlich nicht das Ergebnis der Closure zurueckgegeben,
+  // sonst wuerde man beliebigen Objekten nicht nur das Abbrechen, sondern
+  // auch das Aendern von Kommandos ermoeglichen.
+  if (disablecommands && !IS_LEARNING(ME) )
+  {
+    if (disablecommands[B_TIME] >= time()
+      && objectp(disablecommands[B_OBJECT]))
+    {
+      // disablecommands valid
+      // hart-kodierte Ausnameliste pruefen
+      if ( member(({"mrufe","mschau","bug","idee","typo","detail"}),
+            explode(str," ")[0]) == -1)
+      {
+        if (closurep(disablecommands[B_VALUE]))
+        {
+          if (funcall(disablecommands[B_VALUE],_return_args(unmodified)))
+          {
+            // Non-zero Closure-Ergebnis, Abbruch. Die gerufene Funktion ist
+            // fuer eine geeignete Meldung an den Spieler verantwortlich.
+            return 1;
+          }
+        }
+        // wenn Text, dann auch pruefen, ob das Kommandoverb in den Ausnahmen
+        // steht. (query_verb() geht leider hier noch nicht.)
+        else if (stringp(disablecommands[B_VALUE])
+                 && member(disablecommands[B_EXCEPTIONS],
+                           explode(str," ")[0]) == -1)
+        {
+          // meldung ausgeben...
+          tell_object(PL, disablecommands[B_VALUE]);
+          // und Ende...
+          return 1;
+        }
+      }
+    }
+    else disablecommands=0;
+  }
+
+  // Verfolger direkt ins Env reinholen.
+  if (remove_call_out("TakeFollowers")>=0)
+    catch(TakeFollowers();publish);
+
+  DelayPreparedSpells();
+
+  // Historyeintrag korrigieren
+  if (unmodified[0]=='^')
+  {
+    string *oldnew,pre,post;
+    if (sizeof(oldnew=explode(unmodified,"^"))>2)
+    {
+      int hist_idx = (hist_now-1)%hist_size;
+      sscanf(history[hist_idx],"%s"+oldnew[1]+"%s", pre, post);
+      unmodified = pre+oldnew[2]+post;
+      if (show_processing)
+        write("["+unmodified+"]\n");
+      // korrigiertes Kommando natuerlich auch in die History.
+      AddHistory(unmodified);
+    }
+  }
+
+  if( bb )
+    BBMASTER->BBWrite(" -> " + unmodified, 1);
+
+  if (show_processing>1)
+    printf("[%s]\n",unmodified);
+
+  mixed ret = _return_args(unmodified);
+
+  // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
+  // dorthin melden.
+  if (syntaxdb)
+  {
+    if (!objectp(syntaxdb[0]))
+      syntaxdb[0] = find_object("/secure/syntaxdb");
+    if (syntaxdb[0])
+      catch(syntaxdb[0]->start_cmd(unmodified);nolog);
+  }
+  return ret;
+}
+
+static int do_list(string str)
+{
+  string *cmdlist;
+  int i;
+
+  if (!QueryProp(P_WANTS_TO_LEARN))
+    return 0;
+  cmdlist=old_explode(_unparsed_args()||"",";")-({ "" });
+  for (i=0;i<sizeof(cmdlist);i++)
+  {
+    cmdlist[i]=implode(old_explode(cmdlist[i]," ")-({}), " ");
+    if (show_processing)
+      write("["+cmdlist[i]+"]\n");
+    command(cmdlist[i]);
+  }
+  return 1;
+}
+
+//falls die aliasliste kaputt ist ...
+
+int unalias_all()
+{
+  if (IS_ELDER(this_interactive())) aliases=([]);
+  return 1;
+}
+
+object _query_last_command_env()
+{
+  return last_command_env;
+}
+
+int _query_show_alias_processing()
+{
+  return show_processing;
+}
+
+int _query_histmin()
+{
+  return histmin;
+}
+
+varargs void AddAction(mixed fun, mixed cmd, int flag, int lvl)
+{
+  int i;
+  mixed *cmds;
+
+  log_file( "ARCH/ADD_ACTION", sprintf(
+	"%s:\n  TO: %O TP: %O PO: %O\n   fun: %O cmd: %O flag: %O lvl: %O",
+        dtime(time()), this_object(), this_player(), previous_object(),
+	fun, cmd, flag, lvl));
+
+  if (!(cmds=Query(P_LOCALCMDS))) cmds=({});
+
+  if (!pointerp(cmd)) cmd=({cmd});
+
+  for (i = sizeof(cmd)-1; i>=0; i--)
+    cmds += ({({ cmd[i] , fun, flag, lvl})});
+
+  Set(P_LOCALCMDS, cmds);
+}
+
+static int auswerten(mixed cmd, string str)
+{
+  if (closurep(cmd))
+    return funcall(cmd,str);
+  if (stringp(cmd))
+    return call_other(this_object(),cmd,str);
+  return 0;
+}
+
+static varargs int __auswerten(string str, string intern)
+{
+  string verb;
+  mixed *cmd, cmds;
+  int i,ret,lvl,l,vl;
+
+  if (!intern)
+    verb=query_verb();
+  else
+    verb=intern;
+  lvl=query_wiz_level(ME);
+  vl=sizeof(verb);
+  cmds=QueryProp(P_LOCALCMDS);
+
+  for(i=sizeof(cmds)-1;i>=0;i--)
+  {
+    cmd=cmds[i],l=sizeof(cmd[0]);
+    if (cmd[0]==verb[0..l-1] && cmd[3]<=lvl && (cmd[2]||vl==l) &&
+  (ret=auswerten(cmd[1],str)))
+      return ret;
+  }
+  // An dieser Stelle gibt es hier und vermutlich nirgendwo anders etwas, was
+  // dieses Kommando als gueltig betrachtet. Wir informieren ggf. die
+  // Syntax-DB. (Achtung: wenn jemand ne add_action() im Spielerobjekt
+  // einbaut, die vor dieser eingetragen wird, ist die Annahme ggf. falsch.)
+  // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
+  // dorthin melden.
+  if (syntaxdb)
+  {
+    if (!objectp(syntaxdb[0]))
+      syntaxdb[0] = find_object("/secure/syntaxdb");
+    if (syntaxdb[0])
+      catch(syntaxdb[0]->cmd_unsuccessful();nolog);
+  }
+
+  return 0;
+}
+
+public void syntax_log_ep(int type)
+{
+  // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
+  // dorthin melden.
+  if (syntaxdb && syntaxdb[0])
+  {
+      catch(syntaxdb[0]->LogEP(type);nolog);
+  }
+}
+
+static mixed _query_localcmds()
+{
+  mixed *l;
+
+  l=Query(P_LOCALCMDS);
+  if (!pointerp(l))
+    l=({});
+  return ({
+    ({"ali","alias",0,0}),
+    ({"alias","alias",0,0}),
+    ({"unali","unalias",1,0}),
+    ({"histmin","histmin",0,0}),
+    ({"histlen","histlen",0,0}),
+    ({"hist","show_hist",0,0}),
+    ({"history","show_hist",0,0}),
+    ({"do","do_list",0,LEARNER_LVL}),
+    ({"ersetzungsanzeige","replacedisplay",0,0}),
+    ({"syntaxsammlung","collect_cmds",0,0}),
+    ({"fehlermeldung","set_errormessage",0,SEER_LVL}),
+  })+l;
+}
+
+static int collect_cmds(string cmd)
+{
+  if (!stringp(cmd))
+  {
+    _notify("Mit diesem Befehl kannst Du mithelfen, Syntaxen im MG zu "
+            "verbessern. Wenn Du einverstanden bist, speichern wir "
+            "anonym (d.h. ohne Deinen Charnamen), welche Deiner Befehle "
+            "erfolgreich und nicht erfolgreich waren. Uebliche "
+            "Kommunikationsbefehle werden dabei nicht gespeichert.",
+            0);
+    _notify("Mit 'syntaxsammlung ja' kannst Du die Speicherung einschalten, "
+            "mit 'syntaxsammlung nein' kannst Du sie ausschalten.",0);
+    _notify("Deine Befehle werden zur Zeit"
+            + (QueryProp("_syntaxdb") ? " " : " NICHT ")
+            + "gespeichert.", 0);
+  }
+  else if (cmd == "ja")
+  {
+    SetProp("_syntaxdb", 1);
+    _notify("Ab jetzt werden Deine Befehle gespeichert. Vielen Dank!", 0);
+  }
+  else
+  {
+    SetProp("_syntaxdb", 0);
+    _notify("Ab jetzt werden Deine Befehle NICHT gespeichert.", 0);
+  }
+  return 1;
+}
+
+int _query_command_average()
+{
+  decay_average();
+  return cmds_per_time;
+}
+
+nomask void __set_bb(int flag)  {
+  if( previous_object()!=find_object(BBMASTER) || process_call() )
+    return;
+  bb=flag;
+}
+
+
+nomask public void countCmds( int type, string key )
+{
+    string tmp;
+
+    if ( this_player() != this_interactive()
+         || this_interactive() != this_object()
+         || member( cmd_types, type ) < 0 )
+        return;
+
+    tmp = sprintf( "%d\n%s", type, key );
+
+    commands -= ({ tmp });
+    commands += ({ tmp });
+    commands = commands[0..max_commands-1];
+}
+
+
+nomask public string *getCmds()
+{
+    string *tmp;
+
+    if ( previous_object() != find_object(BBMASTER) )
+        return ({});
+
+    tmp = commands;
+    commands = ({});
+
+    return tmp;
+}
+
+/*
+ * Force the monster to do a command. The force_us() function isn't
+ * always good, because it checks the level of the caller, and this function
+ * can be called by a room.
+ */
+int command_me(string cmd)
+{
+  if (IS_LEARNER(ME))
+  {
+    if (!this_interactive() || !previous_object())
+      return 0;
+    if( geteuid(ME)!=geteuid(this_interactive())
+        || geteuid(ME)!=geteuid(previous_object()) )
+    {
+      if( query_wiz_level(ME)<query_wiz_level(previous_object()))
+        tell_object(ME,previous_object()->name()+" zwingt Dich zu: "
+                    + cmd + ".\n");
+      else
+      {
+        tell_object(ME,previous_object()->name()
+                    + " versucht, Dich zu " + cmd + " zu zwingen.\n" );
+        return 0;
+      }
+    }
+  }
+  return command(cmd);
+}
+
+
+static mixed _query_p_lib_disablecommands() {
+    // abgelaufen oder Objekt zerstoert? Weg damit.
+    if (pointerp(disablecommands)
+        && (disablecommands[B_TIME] < time()
+          || !objectp(disablecommands[B_OBJECT])) )
+        return(disablecommands = 0);
+
+    // sonst Kopie zurueck (copy(0) geht)
+    return(copy(disablecommands));
+}
+
+static mixed _set_p_lib_disablecommands(mixed data) {
+
+  // setzendes Objekt ermitteln, da diese Funktion nur per SetProp() gerufen
+  // werden sollte (!), ist das PO(1);
+  object origin = previous_object(1);
+  // wenn nicht existent, direkt abbruch
+  if (!objectp(origin))
+    return _query_p_lib_disablecommands();
+
+  // Prop loeschen? Explizit loeschen darf jeder, allerdings nicht direkt
+  // ungeprueft ueberschreiben.
+  if (!data) {
+      return (disablecommands = 0 );
+  }
+  // mal direkt buggen bei falschen Datentyp, damits auffaellt.
+  if (!pointerp(data) || sizeof(data) < 2 || !intp(data[0])
+      || (!stringp(data[1]) && !closurep(data[1]))
+      || (sizeof(data) >= 3 && !pointerp(data[2])) )
+      raise_error(sprintf(
+            "Wrong data type for P_DISABLE_COMMANDS. Expected Array with "
+            "2 or 3 elements (int, string|closure, [string*]), got %.25O\n",
+            data));
+
+  // Wenn abgelaufen oder gleiches Objekt wie letztes Mal: eintragen.
+  if (!disablecommands || (disablecommands[B_TIME] < time()
+        || !objectp(disablecommands[B_OBJECT]) 
+        || disablecommands[B_OBJECT] == origin) ) {
+      // Loggen nur, wenn eine Closure eingetragen wird. Reduziert den
+      // Logscroll und Strings haben deutlich weniger Missbrauchspotential.
+      if (closurep(data[1])) {
+        CBLOG(sprintf("[%s] CB gesetzt von %O, gueltig bis %s, Daten: %O\n",
+        strftime("%Y%m%d-%H:%M:%S"),origin,
+        strftime("%Y%m%d-%H:%M:%S",data[0]),
+        (stringp(data[1]) ? regreplace(data[1],"\n","\\n",0)
+                          : data[1])));
+      }
+      if (sizeof(data)+1 <= B_EXCEPTIONS)
+        disablecommands = ({ origin, data[0], data[1], ({}) });
+      else
+        disablecommands = ({ origin, data[0], data[1], data[2] });
+      return(copy(disablecommands));
+  }
+
+  return(_query_p_lib_disablecommands());
+}
+
+static mixed _set__syntaxdb(mixed v)
+{
+  Set("_syntaxdb", v, F_VALUE);
+  if (v)
+      syntaxdb = ({find_object("/secure/syntaxdb")});
+  else
+      syntaxdb = 0;
+  return QueryProp("_syntaxdb");
+}
+
diff --git a/std/player/description.c b/std/player/description.c
new file mode 100644
index 0000000..4a0663f
--- /dev/null
+++ b/std/player/description.c
@@ -0,0 +1,292 @@
+// MorgenGrauen MUDlib
+//
+// player/description.c -- player description handling
+//
+// $Id: description.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/description";
+
+#include <thing/language.h>
+#include <player/description.h>
+#include <living/clothing.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <combat.h>
+#include <sys_debug.h>
+
+#define NEED_PROTOTYPES
+#include <player/command.h>
+
+protected void create()
+{
+  ::create();
+  Set(P_NAME, NOSETMETHOD, F_SET_METHOD);
+  Set(P_PRESAY, SAVE, F_MODE_AS);
+  Set(P_TITLE, SAVE, F_MODE_AS);
+  Set(P_EXTRA_LOOK, SAVE, F_MODE_AS);
+  Set(P_GENDER, SAVE, F_MODE_AS);
+  Set(P_SIZE, SAVE, F_MODE_AS);
+  Set(P_INFO, NOSETMETHOD, F_SET_METHOD);
+  // Avatar-URIs speichern. Ausserdem hat an denen keiner rumzufummeln.
+  Set(P_AVATAR_URI, SAVE|SECURED, F_MODE_AS);
+}
+
+string _query_info()
+{
+  string info;
+  info = Query(P_INFO);
+  if(!info)info=="";
+  info = (string)query_wiz_level(this_object());
+  if(IS_GOD(this_object()))
+    return info+" [Gott]\n";
+  if(IS_ARCH(this_object()))
+    return info+" [Erzmagier]\n";
+  if(IS_ELDER(this_object()))
+    return info+" [Weiser]\n";
+  if(IS_LORD(this_object()))
+    return info+" [Regionschef]\n";
+  if(IS_SPECIAL(this_object()))
+    return info+" [Special]\n";
+  if(IS_DOMAINMEMBER(this_object()))
+    return info+" [Regionsmitarbeiter]\n";
+  if(IS_WIZARD(this_object()))
+    return info+" [Magier]\n";
+  if(IS_LEARNER(this_object()))
+    return info+" [Magieranwaerter]\n";
+  info = QueryProp(P_LEVEL);
+  if(IS_SEER(this_object()))
+    return info+" [Seher]\n";
+  return info+" [Spieler]\n";
+}
+
+int _query_size() {
+  if (QueryProp(P_FROG))
+    return 20;
+  return Query(P_SIZE);
+}
+
+// short() -- get the short description of a player
+string short()
+{
+  string answer;
+  string title;
+
+  if(QueryProp(P_INVIS))
+    if(interactive(previous_object()) && IS_LEARNING(previous_object()))
+      return "("+QueryProp(P_NAME)+") \n";
+    else
+      return 0;
+
+  if(QueryProp(P_GHOST))
+  {
+    if (QueryProp(P_FROG))
+      return "Der Geist eines kleinen Frosches namens "+QueryProp(P_NAME)+
+       ".\n";
+    else
+      return "Der Geist von "+QueryProp(P_NAME)+".\n";
+  }
+
+  if (QueryProp(P_FROG))
+    return "Ein kleiner Frosch namens "+QueryProp(P_NAME)+".\n";
+
+  answer = QueryProp(P_PRESAY) + QueryProp(P_NAME);
+  if (QueryProp(P_ARTICLE)) answer=QueryArticle(0,0)+answer;
+  if((title=QueryProp(P_TITLE)) && title != "") answer += " " + title;
+  if(!interactive(ME)) answer += " (netztot)";
+  return answer+".\n";
+}
+
+private string andlist(object *arr) {
+  string *tmp;
+  if(!pointerp(arr)) return "";
+  if(sizeof(tmp = map_objects(arr, "name", WEN)))
+    return(CountUp(tmp));
+  return "";
+}
+
+// gibt fuer nicht-Invis-Objekte den Namen zurueck
+private string get_vis_name(object ob) {
+  if (ob->QueryProp(P_INVIS))
+    return 0;
+  return (ob->name(WEN));
+}
+
+varargs string long()
+{
+  string exl, descr, tmp, size_descr;
+  object ob;
+  mixed trans, w, a, r;
+  int per;
+  string fill, fill2;
+  /* fuer groessenvergleich */
+  string comparesize, pers1, pers2;
+  int relation;
+
+  per=1000*QueryProp(P_SIZE)/QueryProp(P_AVERAGE_SIZE);
+  switch(per)
+  {
+    case 0..800: size_descr="ziemlich winzig"; break;
+    case 801..850: size_descr="eher winzig"; break;
+    case 851..900: size_descr="recht klein"; break;
+    case 901..950: size_descr="eher klein"; break;
+    case 951..1050: size_descr="mittelgross"; break;
+    case 1051..1100: size_descr="relativ gross"; break;
+    case 1101..1150: size_descr="ziemlich gross"; break;
+    case 1151..1200: size_descr="sehr gross"; break;
+    default: size_descr="riesig"; break;
+  }
+#define RassenString() ((QueryProp(P_FROG) ? "en Frosch" :\
+       (!QueryProp(P_GENDER)?" ":QueryProp(P_GENDER)==2?"e ":"en ")+\
+       (pointerp(QueryProp(P_RACESTRING))?\
+        QueryProp(P_RACESTRING)[WEN]:QueryProp(P_RACE))))
+  fill2=fill=0;
+  if (QueryProp(P_AVERAGE_SIZE)<170)
+  {
+    if (per<950)
+      fill="selbst fuer ein"+RassenString()+" ";
+    else
+      if (per>1050)
+  fill2=", wenn auch nur fuer ein"+RassenString();
+  }
+  else
+  {
+    if (QueryProp(P_AVERAGE_SIZE)>170)
+      if (per>1050)
+  fill="sogar fuer ein"+RassenString()+" ";
+      else
+  if (per<950)
+    fill2=", wenn auch nur fuer ein"+RassenString();
+  }
+  if (!fill&&!fill2) fill="fuer ein"+RassenString()+" ";
+  descr = "Das ist "+name(WER,1)+". "+capitalize(QueryPronoun())+" ist "+
+    (fill||"")+size_descr+(fill2||"")+".\n";
+
+  if(this_player()) {
+    /* groessenvergleich_anfang (NEU) */
+    pers1 = QueryPronoun(WER);
+    pers2 = QueryPronoun(WEM);
+ 
+    // || falls TP keine Groesse gesetzt hat... Warum auch immer...
+    relation = (QueryProp(P_SIZE)*100) / 
+                  (this_player()->QueryProp(P_SIZE) || 1);
+    switch (relation) 
+     {
+     case   0 ..  25 : comparesize = "Damit gibt "+pers1+" einen guten"
+                                     " Fusschemel fuer Dich ab"; 
+                                     break;
+     case  26 ..  50 : comparesize = "Damit reicht "+pers1+" Dir nicht mal bis"
+                                     " zur Huefte"; 
+                                     break;
+     case  51 ..  75 : comparesize = "Damit kannst Du "+pers2+" locker auf den"
+                                     " Kopf spucken"; 
+                                     break;
+     case  76 ..  90 : comparesize = "Damit ist "+pers1+" einen Kopf kleiner"
+                                     " als Du"; 
+                                     break;
+     case  91 .. 110 : comparesize = "Damit hat "+pers1+" etwa Deine Groesse";
+                                     break;  
+     case 111 .. 120 : comparesize = "Damit ist "+pers1+" einen Kopf groesser" 
+                                     " als Du";
+                                     break;
+     case 121 .. 150 : comparesize = "Damit holst Du Dir einen steifen Nacken,"
+                                     " wenn Du "+pers2+" in die Augen siehst";
+                                     break;
+     case 151 .. 200 : comparesize = "Damit versperrt "+pers1+" Dir absolut"
+		                     " die Sicht"; 
+                                     break;
+     case 201 .. 300 : comparesize = "Damit waere jeder Schlag von Dir ein" 
+                                     " Tiefschlag";
+                                     break;
+     default         : comparesize = "Damit kannst Du "+pers2+" bequem zwischen"
+                                     " den Beinen durchlaufen";
+                                     break;
+     }
+  
+     descr+=comparesize+".\n";
+     /* groessenvergleich_ende (NEU) */
+   }
+
+  if(QueryProp(P_GHOST)) return descr;
+
+  trans = QueryProp(P_TRANSPARENT); SetProp(P_TRANSPARENT, 0);
+  descr += ::long(); SetProp(P_TRANSPARENT, trans);
+
+  // Waffen, Ruestungen/Kleidung und Sonstiges ermitteln
+  w = ({QueryProp(P_WEAPON), QueryProp(P_PARRY_WEAPON)}) - ({0});
+  a = QueryProp(P_ARMOURS) + QueryProp(P_CLOTHING) - ({0});
+  r = all_inventory(ME) - w - a; //Rest logischerweise
+
+  // rest noch nach sichbarkeit von ausserhalb des SPielers filtern.
+  r=map(r,function string (object ob) {
+	  if(sizeof(all_inventory(ob)) || ob->QueryProp(P_LIGHT) || 
+	     ob->QueryProp(P_LIGHTED) || ob->QueryProp(P_SHOW_INV))
+	    return(get_vis_name(ob));
+          return(0); } ) - ({0});
+
+  // Invis-Objekte ausfiltern und Namen ermitteln, anschliessend sind in w, a
+  // und r je ein Array von Strings
+  w = map(w, #'get_vis_name) - ({0});
+  a = map(a, #'get_vis_name) - ({0});
+
+  return descr +
+         (QueryProp(P_TRANSPARENT) ?
+          break_string(capitalize(name(WER, 1))+" hat "
+                      +(sizeof(a)?CountUp(a):"keine Ruestung")+" an"
+                      +(sizeof(r)?", ":" und ")
+                      +(sizeof(w)?CountUp(w):"keine Waffe")+" gezueckt"
+          +(sizeof(r)?" und traegt "+CountUp(r):"")+".", 78): "");
+}
+
+
+// **** local property methods
+static mixed _query_presay()
+{
+  string presay;
+  if((presay = Query(P_PRESAY)) && (presay != "")) return presay + " ";
+  return "";
+}
+
+static string _query_name()
+{
+  return capitalize(Query(P_NAME) || "NoName");
+}
+// ****
+
+// Local commands
+static mixed _query_localcmds() {
+  return
+    ({
+      ({"avatar","set_avataruri",0,0}),
+    });
+}
+
+int set_avataruri(string arg) {
+  arg = _unparsed_args(0);
+  if (!arg || !sizeof(arg)) {
+    string uri = QueryProp(P_AVATAR_URI);
+    if (stringp(uri))
+      tell_object(ME,
+          "Aktuelle Avatar-URI: " + uri + "\n");
+    else
+      tell_object(ME, "Du hast keine Avatar-URI eingestellt.\n");
+  }
+  else if (arg == "keine") {
+      SetProp(P_AVATAR_URI, 0);
+      tell_object(ME, "Deine Avatar-URI wurde geloescht.\n");
+  }
+  else if (sizeof(arg) > 512)
+      tell_object(ME, "Deine neue Avatar-URI ist zu lang!\n");
+  else {
+      SetProp(P_AVATAR_URI, arg);
+      tell_object(ME,
+          "Aktuelle Avatar-URI: " + arg + "\n");
+  }
+  return 1;
+}
+
diff --git a/std/player/exploration.c b/std/player/exploration.c
new file mode 100644
index 0000000..8a1976e
--- /dev/null
+++ b/std/player/exploration.c
@@ -0,0 +1,125 @@
+// MorgenGrauen MUDlib
+//
+// player/exploration.c -- exploration point management
+//
+// $Id: exploration.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player/life.h>
+#include <player/base.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <exploration.h>
+#include <scoremaster.h>
+#include <properties.h>
+#include <new_skills.h>
+
+private string given_scores;
+
+private nosave mixed epnum;
+
+void create() {
+  Set(P_LEP, SECURED|SAVE, F_MODE_AS);
+
+  given_scores = "";
+}
+
+string Forschung()
+{
+  return EPMASTER->QueryForschung();
+}
+
+static string _query_given_scores()
+{
+  return given_scores;
+}
+
+// Hier kommen Funktionen fuer die Levelpunkte
+
+#define XP_FAC ([1:10,2:40,3:150,4:600,5:2250,6:9000,7:35000,8:140000,9:500000])
+
+//#define DEBUG(x,y) printf(x,y)
+#define DEBUG(x,y)
+
+int AddScore(int contributor)
+{
+  mixed info;
+  object po;
+  int drin;
+  
+  if (!pointerp(info = SCOREMASTER->QueryNPCbyNumber(contributor)))
+    return -1;
+
+  if ((po = previous_object()) && (object_name(po) == SCOREMASTER))
+    po = previous_object(1);
+
+  if (!po || old_explode(object_name(po),"#")[0] != info[SCORE_KEY])
+    return -2;
+
+  if (!stringp(given_scores))
+    given_scores = " ";
+
+  if (catch(drin = test_bit(given_scores, contributor);publish))
+    return -3;
+
+  if (!drin) {
+    given_scores = set_bit(given_scores, contributor);
+    Set(P_LEP, Query(P_LEP) + info[SCORE_SCORE]);
+    force_save();
+    return info[SCORE_SCORE];
+  }
+  return 0;
+}
+
+int TestScore(int contributor)
+{
+  int ret;
+  
+  if (!previous_object() || (object_name(previous_object()) != SCOREMASTER))
+    return 0;
+
+  catch(ret = test_bit(given_scores, contributor);publish);
+
+  return ret;
+}
+
+int SetScoreBit(int contributor)
+{
+  int drin;
+  
+  if (!previous_object() || (object_name(previous_object()) != SCOREMASTER))
+    return -1;
+
+  if (catch(drin = test_bit(given_scores, contributor);publish))
+    return -2;
+
+  if (drin) return -3;
+
+  given_scores = set_bit(given_scores, contributor);
+  force_save();
+  return 1;
+}
+
+int ClearScoreBit(int contributor)
+{
+  int drin;
+  
+  if (!previous_object() || (object_name(previous_object()) != SCOREMASTER))
+    return -1;
+
+  if (catch(drin = test_bit(given_scores, contributor);publish))
+    return -2;
+
+  if (!drin) return -3;
+
+  given_scores = clear_bit(given_scores, contributor);
+  force_save();
+  return 1;
+}
+
diff --git a/std/player/guide.c b/std/player/guide.c
new file mode 100644
index 0000000..fb08b43
--- /dev/null
+++ b/std/player/guide.c
@@ -0,0 +1,182 @@
+// MorgenGrauen MUDlib
+// 
+// player/guide.c -- newbie guide handling 
+//
+// $Id: guide.c 7391 2010-01-25 22:52:51Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <config.h>
+#define NEED_PROTOTYPES
+#include <player/user_filter.h> // fuer is_active_guide()
+#include <properties.h>
+#include <defines.h>
+#include <thing/properties.h>
+
+/* Funktion, die die Guide-Kommandos aktiviert */
+void add_guide_commands()
+{
+	add_action("CiceroneCmd","cicerone");
+	// Sollte eigentlich an derselben Stelle moeglich sein.
+}
+
+/* Gibt die Meldung beim Aendern aus*/
+protected int NewbieChangeMsg() {
+    int cic=QueryProp(P_NEWBIE_GUIDE);
+    // begrenzen auf 1 Tag, falls jemand da Schrott reingschrieben hat.
+    if (cic > 86400) {
+	cic=86400;
+	SetProp(P_NEWBIE_GUIDE,cic);
+    }
+    if (cic<=0) {
+	write("Du bist jetzt kein Cicerone mehr.\n");
+    }
+    else if (cic < 60) {
+	write("Du bist jetzt ein Cicerone.\n");
+    }
+    else {
+	write(break_string(
+	    "Du bist jetzt ein Cicerone, allerdings nur in den Zeiten, "
+	    "in denen Du weniger als " + cic/60
+	    + ((cic/60)<2 ? " Minute ":" Minuten ")
+	    + "idle bist.\n",78));
+    }
+    return 1;
+}
+
+/* Gibt die Statusmeldung aus */
+protected int NewbieStatusMsg() {
+  int cic=QueryProp(P_NEWBIE_GUIDE);
+  // begrenzen auf 1 Tag, falls jemand da Schrott reingschrieben hat.
+  if (cic > 86400) {
+      cic=86400;
+      SetProp(P_NEWBIE_GUIDE,cic);
+  }
+
+  if (cic <= 0)
+    write ("Du bist kein Cicerone.\n");
+  else if (cic < 60)
+    write ("Du stehst Neuspielern als Cicerone zur Verfuegung.\n");
+  else {
+    write(break_string(
+	    "Du stehst Neuspielern als Cicerone zur Verfuegung, allerdings "
+	    "nur in den Zeiten, in denen Du weniger als " + cic/60
+	    + ((cic/60)<2 ? " Minute ":" Minuten ")
+	    + "idle bist.\n",78));
+  }
+  return 1;
+}
+
+/* Fuehrt das eigentliche Kommando aus*/
+
+int CiceroneCmd(string str)
+{
+	if (QueryProp(P_LEVEL)<20)
+	{
+		write(break_string("Du solltest erst noch ein wenig "
+					"Erfahrung sammeln, bevor Du Dich "
+				       "als Cicerone zur Verfuegung stellst.",78));
+		return 1;
+	}
+	// Idlezeit uebergeben?
+	int idle=to_int(str);
+	// max. einen Tag (1440 min) zulassen.
+	if (idle < 0) idle=0;
+	else if (idle > 1440) idle=1440;
+
+	if (!str) {
+	    return NewbieStatusMsg();
+	}
+	else if (str=="status") {
+	    return NewbieStatusMsg();
+	}
+	// "ein" schaltet einfach generell ein, hierbei steht 1 in der Prop
+	// fuer "permanent ein".
+	else if (str=="ein") {
+	    SetProp(P_NEWBIE_GUIDE,1);
+	    return NewbieChangeMsg();
+	}
+	// "aus" oder "0" deaktiviert.
+	else if (str=="aus") {
+	    SetProp(P_NEWBIE_GUIDE,0);
+	    return NewbieChangeMsg();
+	}
+	// wenn Zahl uebergeben ist, die groesser 0 und kleiner 1440 ist
+	// (s.o.), wird es als Anzahl an Idle-Minuten aufgefasst, ab der man
+	// ausgeblendet werden will.
+	else if (idle) {
+	    SetProp(P_NEWBIE_GUIDE, idle*60); // als Sekunden speichern.
+	    return NewbieChangeMsg();
+	}
+	write(break_string(
+	      "cicerone ein  - Du bist Cicerone\n"
+	      "cicerone aus  - Du bist kein Cicerone\n"
+	      "cicerone      - Status anzeigen\n"
+	      +break_string(
+		"Du bist Cicerone, aber wenn Du laenger als <zahl> Minuten "
+		"idle bist, wirst Du automatisch ausgeblendet, bis Du wieder "
+		"endidelt bist.",
+		76,"cicerone zahl - ",BS_INDENT_ONCE),
+	      78,"Syntaxhilfe:",BS_PREPEND_INDENT|BS_LEAVE_MY_LFS));
+
+	return 1;
+}
+
+protected string IstSindMsg(string* namen)
+{
+	if (sizeof(namen)==1)
+		return "ist davon "+namen[0];
+	else
+		return "sind davon "+CountUp(namen);
+}
+
+void NewbieIntroMsg()
+{
+	object* cicerones;
+	string* namen;
+	string restext;
+
+	// Nur bis Level 5 wird etwas ausgegeben.
+	if (QueryProp(P_LEVEL)>5) return;
+	
+	// is_active_guide() ist in /std/user_filter.c, welches vom
+	// Spielerobjekt geerbt wird und damit zur Verfuegung steht.
+	cicerones=filter(users(),#'is_active_guide);
+	// uid verwenden, da sonst kleine Spieler einen getarnten 
+	// "Riesen" oder aehnliches anstprechen.
+	namen=map(cicerones,function string (object o) 
+	    { return(capitalize(geteuid(o))); } );
+
+	if (namen && sizeof(namen)>0)
+	{
+		restext="\nEs gibt einige nette Spieler, die bereit sind, Dich "
+				"auf Deinen ersten Schritten im "MUDNAME
+				" zu begleiten. \n\nDerzeit "
+				+IstSindMsg(namen)+" eingeloggt. Du kannst "
+				"einen oder eine von Ihnen ansprechen, "
+				"indem Du z.B. einfach \n"
+				"  'teile "+
+				lower_case(namen[random(sizeof(namen))])+
+				" mit Hallo ich bin neu hier, kannst Du "
+				"mir bitte helfen?'\n"
+				"eintippst. Nur keine Scheu, diese Spieler "
+				"haben sich freiwillig dazu bereiterklaert!\n"
+				"\nDu kannst Dir diese Spieler jederzeit "
+				"mit 'kwer cicerones' anzeigen lassen.\n\n";
+		write(break_string(restext,78,"*  ",BS_LEAVE_MY_LFS));
+	}
+	else
+	{
+		// Weia, kein Newbie-Guide da. Lieber erstmal nix tun,
+		// bis uns was besseres einfaellt.
+	}
+	return;
+}
+
+
+
+
diff --git a/std/player/invmaster/gfx/Amulett_female b/std/player/invmaster/gfx/Amulett_female
new file mode 100644
index 0000000..5085026
--- /dev/null
+++ b/std/player/invmaster/gfx/Amulett_female
@@ -0,0 +1,5 @@
+6
+35
+5
+\_/
+?&
diff --git a/std/player/invmaster/gfx/Amulett_male b/std/player/invmaster/gfx/Amulett_male
new file mode 100644
index 0000000..224e09c
--- /dev/null
+++ b/std/player/invmaster/gfx/Amulett_male
@@ -0,0 +1,5 @@
+6
+36
+5
+\_/
+?&
diff --git a/std/player/invmaster/gfx/Beschriftung b/std/player/invmaster/gfx/Beschriftung
new file mode 100644
index 0000000..afddc5e
--- /dev/null
+++ b/std/player/invmaster/gfx/Beschriftung
@@ -0,0 +1,24 @@
+7
+0
+0
+??????????????????????????????????????????____________________<Helm>
+?<Waffe>______________
+??????????????????????\?????????????????????_____________<Koecher>
+
+??????????????????????????????????????????????_____________<Amulett>
+???????????????????????????????????????????????______
+?????????????????????????????????????????????????????\
+??????????????????????????????????????????????????????<Panzer>
+
+?<Ring>_____________/
+?????????????????????/??????????????????????????___________<Schild>
+?<Handschuhe>_______/??????????/
+??????????????????????????????/
+?<Guertel>___________________/
+
+?????????????????????????????????????????????\____________<Umhang>
+
+???????????????????????????????????????????_________<Hose>
+
+
+?????????<Schuhe>_____________
diff --git a/std/player/invmaster/gfx/Guertel_female b/std/player/invmaster/gfx/Guertel_female
new file mode 100644
index 0000000..83ed884
--- /dev/null
+++ b/std/player/invmaster/gfx/Guertel_female
@@ -0,0 +1,4 @@
+5
+33
+10
+`==O=='
diff --git a/std/player/invmaster/gfx/Guertel_male b/std/player/invmaster/gfx/Guertel_male
new file mode 100644
index 0000000..25c14a4
--- /dev/null
+++ b/std/player/invmaster/gfx/Guertel_male
@@ -0,0 +1,4 @@
+5
+33
+10
+`===O==='
diff --git a/std/player/invmaster/gfx/Handschuh_female b/std/player/invmaster/gfx/Handschuh_female
new file mode 100644
index 0000000..8e3f19f
--- /dev/null
+++ b/std/player/invmaster/gfx/Handschuh_female
@@ -0,0 +1,10 @@
+4
+21
+7
+?_,-_
+(__,I
+
+??????????????????????|7
+??????????????????????/|
+?????????????????????`)/
+??????????????????????'
diff --git a/std/player/invmaster/gfx/Handschuh_male b/std/player/invmaster/gfx/Handschuh_male
new file mode 100644
index 0000000..7b81dd1
--- /dev/null
+++ b/std/player/invmaster/gfx/Handschuh_male
@@ -0,0 +1,10 @@
+4
+22
+7
+,--__
+(__,I
+???????????????????????_?
+??????????????????????| |
+??????????????????????/ |
+??????????????????????`))
+?????????????????????? ' 
diff --git a/std/player/invmaster/gfx/Helm_female b/std/player/invmaster/gfx/Helm_female
new file mode 100644
index 0000000..63ba647
--- /dev/null
+++ b/std/player/invmaster/gfx/Helm_female
@@ -0,0 +1,6 @@
+1
+34
+0
+,-o-.
+====I
+???\_\
diff --git a/std/player/invmaster/gfx/Helm_male b/std/player/invmaster/gfx/Helm_male
new file mode 100644
index 0000000..2c188a3
--- /dev/null
+++ b/std/player/invmaster/gfx/Helm_male
@@ -0,0 +1,6 @@
+1
+35
+0
+,-o-.
+====I
+???\_\
diff --git a/std/player/invmaster/gfx/Hosen_female b/std/player/invmaster/gfx/Hosen_female
new file mode 100644
index 0000000..a9cfdd6
--- /dev/null
+++ b/std/player/invmaster/gfx/Hosen_female
@@ -0,0 +1,13 @@
+6
+32
+10
+/`-----'\
+|/     \|
+|   Y   |
+|   |   |
+?|  |  |
+?(  |  )
+?|  |  |
+?|  |  |
+?|  |  |
+?L_/ \_J
diff --git a/std/player/invmaster/gfx/Hosen_male b/std/player/invmaster/gfx/Hosen_male
new file mode 100644
index 0000000..e8e6390
--- /dev/null
+++ b/std/player/invmaster/gfx/Hosen_male
@@ -0,0 +1,13 @@
+6
+33
+10
+/`-----'\
+|/     \|
+|   Y   |
+|   |   |
+|   |   |
+<.  |  .>
+|   |   |
+|   |   |
+|   |   |
+?L_/ \_J
diff --git a/std/player/invmaster/gfx/Koecher_female b/std/player/invmaster/gfx/Koecher_female
new file mode 100644
index 0000000..de0b1cf
--- /dev/null
+++ b/std/player/invmaster/gfx/Koecher_female
@@ -0,0 +1,5 @@
+7
+40
+3
+"""
+//
diff --git a/std/player/invmaster/gfx/Koecher_male b/std/player/invmaster/gfx/Koecher_male
new file mode 100644
index 0000000..de0b1cf
--- /dev/null
+++ b/std/player/invmaster/gfx/Koecher_male
@@ -0,0 +1,5 @@
+7
+40
+3
+"""
+//
diff --git a/std/player/invmaster/gfx/Ring_female b/std/player/invmaster/gfx/Ring_female
new file mode 100644
index 0000000..00f8b00
--- /dev/null
+++ b/std/player/invmaster/gfx/Ring_female
@@ -0,0 +1,4 @@
+6
+21
+8
+c
diff --git a/std/player/invmaster/gfx/Ring_male b/std/player/invmaster/gfx/Ring_male
new file mode 100644
index 0000000..03620a8
--- /dev/null
+++ b/std/player/invmaster/gfx/Ring_male
@@ -0,0 +1,4 @@
+6
+22
+8
+c
diff --git a/std/player/invmaster/gfx/Ruestung_female b/std/player/invmaster/gfx/Ruestung_female
new file mode 100644
index 0000000..8849f35
--- /dev/null
+++ b/std/player/invmaster/gfx/Ruestung_female
@@ -0,0 +1,9 @@
+1
+26
+4
+??????___???___
+?????/(  \_/  )\
+????/ |\_____/| \
+___/ /?\_/_\_/?\ \
+|__\'???)\_/(???\/|
+???????/_____\???||
diff --git a/std/player/invmaster/gfx/Ruestung_male b/std/player/invmaster/gfx/Ruestung_male
new file mode 100644
index 0000000..e59b67c
--- /dev/null
+++ b/std/player/invmaster/gfx/Ruestung_male
@@ -0,0 +1,9 @@
+1
+25
+4
+?????_^____???____^_?
+????(  \   \_/   /  )
+????/\ |\___ ___/| /\
+?__/  /?\_     _/?\_ >
+I___\>???)\___/(???| |
+????????/_______\??|_|
diff --git a/std/player/invmaster/gfx/Schild_female b/std/player/invmaster/gfx/Schild_female
new file mode 100644
index 0000000..fc49eea
--- /dev/null
+++ b/std/player/invmaster/gfx/Schild_female
@@ -0,0 +1,10 @@
+7
+43
+7
+??|\
+??||
+??||
+==||)
+??||
+??||
+??|/
diff --git a/std/player/invmaster/gfx/Schild_male b/std/player/invmaster/gfx/Schild_male
new file mode 100644
index 0000000..55b329f
--- /dev/null
+++ b/std/player/invmaster/gfx/Schild_male
@@ -0,0 +1,10 @@
+7
+45
+7
+??|\
+??||
+??||
+==||)
+??||
+??||
+??|/
diff --git a/std/player/invmaster/gfx/Schuhe_female b/std/player/invmaster/gfx/Schuhe_female
new file mode 100644
index 0000000..dbe958d
--- /dev/null
+++ b/std/player/invmaster/gfx/Schuhe_female
@@ -0,0 +1,7 @@
+5
+32
+18
+??_???_
+?|_|?|_|
+?/ J?L \
+(_/???\_)
diff --git a/std/player/invmaster/gfx/Schuhe_male b/std/player/invmaster/gfx/Schuhe_male
new file mode 100644
index 0000000..2b33014
--- /dev/null
+++ b/std/player/invmaster/gfx/Schuhe_male
@@ -0,0 +1,7 @@
+5
+32
+18
+???_???_
+??|_|?|_|
+?// J?L \\
+(__/???\__)
diff --git a/std/player/invmaster/gfx/Umhang_female b/std/player/invmaster/gfx/Umhang_female
new file mode 100644
index 0000000..2d885ba
--- /dev/null
+++ b/std/player/invmaster/gfx/Umhang_female
@@ -0,0 +1,17 @@
+2
+31
+4
+??_?????_
+??V?????V
+
+
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+?\???????/
diff --git a/std/player/invmaster/gfx/Umhang_male b/std/player/invmaster/gfx/Umhang_male
new file mode 100644
index 0000000..0025dbe
--- /dev/null
+++ b/std/player/invmaster/gfx/Umhang_male
@@ -0,0 +1,17 @@
+4
+31
+4
+???_?????_
+???V?????V
+
+
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+?\_???????_/
diff --git a/std/player/invmaster/gfx/axe b/std/player/invmaster/gfx/axe
new file mode 100644
index 0000000..9927f48
--- /dev/null
+++ b/std/player/invmaster/gfx/axe
@@ -0,0 +1,13 @@
+7
+19
+1
+?__????__
+// \()/ \\
+||  ||  ||
+\\_/||\_//
+????||
+????||
+
+
+????||
+????\/
diff --git a/std/player/invmaster/gfx/base_female b/std/player/invmaster/gfx/base_female
new file mode 100644
index 0000000..9eaec1a
--- /dev/null
+++ b/std/player/invmaster/gfx/base_female
@@ -0,0 +1,25 @@
+3
+0
+0
+                                                                              
+                                   __                                         
+                                  |  \\                                       
+                                  |_ /|                                       
+                                ___| ||__                                     
+                               /  `   '  \                                    
+                              / |\_/ \_/| \                                   
+                       .-___,/ / \     / \ \                                  
+                       `--..__'   )   (   \ |                                 
+                                 /  .  \   ||                                 
+                                |  \ /  |  /|                                 
+                                |   Y   |  |'                                 
+                                 \  |  /                                      
+                                 |  |  |                                      
+                                 |  |  |                                      
+                                 (  |  )                                      
+                                 | | | |                                      
+                                 | ) ( |                                      
+                                 | ) ( |                                      
+                                 | | | |                                      
+                                 / J L \                                      
+                                '''   ```                                     
diff --git a/std/player/invmaster/gfx/base_male b/std/player/invmaster/gfx/base_male
new file mode 100644
index 0000000..b836653
--- /dev/null
+++ b/std/player/invmaster/gfx/base_male
@@ -0,0 +1,25 @@
+3
+0
+0
+                                                                              
+                                    ___                                       
+                                   |  \\                                      
+                                   |_ /|                                      
+                                ____| |____                                   
+                              /^   ` | '   ^\                                 
+                             / ,|\__/ \__/|.,\                                
+                      ,--___| '/ \  ___  / \_`\                               
+                      |_..___.'   )/_|_\(   | |                               
+                                  /\_|_/\   \ |                               
+                                 | .\ /. |   ||                               
+                                 l|  Y  |l  / |                               
+                                 ||  |  ||   \|                               
+                                 |` '|` '|                                    
+                                  |  |  |                                     
+                                  (  |  )                                     
+                                  | | | |                                     
+                                  |  |  |                                     
+                                  |  |  |                                     
+                                  | | | |                                     
+                                 / J. .L \                                    
+                                '''     ```                                   
diff --git a/std/player/invmaster/gfx/club b/std/player/invmaster/gfx/club
new file mode 100644
index 0000000..5566ece
--- /dev/null
+++ b/std/player/invmaster/gfx/club
@@ -0,0 +1,13 @@
+7
+21
+0
+??__
+_/__\_
+_|__|_
+_|__|_
+?\??/
+??||
+??||
+
+
+??()
diff --git a/std/player/invmaster/gfx/fernwaffe b/std/player/invmaster/gfx/fernwaffe
new file mode 100644
index 0000000..30897e8
--- /dev/null
+++ b/std/player/invmaster/gfx/fernwaffe
@@ -0,0 +1,19 @@
+7
+23
+0
+???)
+??/.
+?/?.
+|??.
+|??.
+|??.
+I??.
+
+
+I??.
+|??.
+|??.
+|??.
+?\?.
+??\.
+???)
diff --git a/std/player/invmaster/gfx/knife b/std/player/invmaster/gfx/knife
new file mode 100644
index 0000000..f6a4ecd
--- /dev/null
+++ b/std/player/invmaster/gfx/knife
@@ -0,0 +1,9 @@
+7
+23
+4
+?,
+(|
+(|
+--
+
+?U
diff --git a/std/player/invmaster/gfx/magic b/std/player/invmaster/gfx/magic
new file mode 100644
index 0000000..06c594b
--- /dev/null
+++ b/std/player/invmaster/gfx/magic
@@ -0,0 +1,10 @@
+7
+20
+0
+,
+?.?'??.
+?.\|/?,
+-?-+-?-
+,?/|\?.
+.?,.???.
+???\/
diff --git a/std/player/invmaster/gfx/misc b/std/player/invmaster/gfx/misc
new file mode 100644
index 0000000..74b5281
--- /dev/null
+++ b/std/player/invmaster/gfx/misc
@@ -0,0 +1,11 @@
+7
+23
+4
+/\
+||
+||
+
+
+||
+||
+`'
diff --git a/std/player/invmaster/gfx/spear b/std/player/invmaster/gfx/spear
new file mode 100644
index 0000000..4ae9d6c
--- /dev/null
+++ b/std/player/invmaster/gfx/spear
@@ -0,0 +1,25 @@
+7
+23
+0
+/\
+!|
+\/
+||
+||
+||
+||
+
+
+||
+||
+||
+||
+||
+||
+||
+||
+||
+||
+||
+||
+`'
diff --git a/std/player/invmaster/gfx/staff b/std/player/invmaster/gfx/staff
new file mode 100644
index 0000000..58446d0
--- /dev/null
+++ b/std/player/invmaster/gfx/staff
@@ -0,0 +1,22 @@
+7
+23
+0
+,.
+||
+||
+||
+||
+||
+||
+
+
+||
+||
+||
+||
+||
+||
+||
+||
+||
+`'
diff --git a/std/player/invmaster/gfx/sword b/std/player/invmaster/gfx/sword
new file mode 100644
index 0000000..e0ff2dc
--- /dev/null
+++ b/std/player/invmaster/gfx/sword
@@ -0,0 +1,13 @@
+7
+22
+0
+??,
+?||
+?||
+?||
+?||
+?||
+_||_
+
+
+?`'
diff --git a/std/player/invmaster/gfx/whip b/std/player/invmaster/gfx/whip
new file mode 100644
index 0000000..00db843
--- /dev/null
+++ b/std/player/invmaster/gfx/whip
@@ -0,0 +1,21 @@
+7
+16
+2
+?? ,_ 
+? /  \ 
+ / ?? \
+ | ??? |
+ | ??? ^
+ |
+? \
+?? | ??#
+?? / 
+? / 
+? | 
+? / 
+ / 
+ | 
+ | 
+ | 
+ \ 
+? ` 
diff --git a/std/player/invmaster/invmaster.c b/std/player/invmaster/invmaster.c
new file mode 100644
index 0000000..06ccda0
--- /dev/null
+++ b/std/player/invmaster/invmaster.c
@@ -0,0 +1,464 @@
+// invmaster.c by Nachtwind@MG V1.1 (5.3.2001)
+// A small master that provides a graphical display of the player´s
+// equipment. 
+#pragma strong_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <input_to.h>
+#include <properties.h>
+#include <ansi.h>
+#include <combat.h>
+#include <language.h>
+#include "invmaster.h"
+
+
+mapping data;
+closure abbreviate;
+
+
+// i'm aware this can be determined with m_indices(VALID_ARMOUR_TYPE),
+// but the position in the arrays is important for the drawing order.
+// first item in the array is drawn last
+static string *armour_types = 
+({AT_BELT,
+  AT_SHIELD,
+  AT_HELMET,
+  AT_BOOT,
+  AT_TROUSERS,
+  AT_AMULET,
+  AT_RING,
+  AT_GLOVE,
+  AT_QUIVER,
+  AT_CLOAK,
+  AT_ARMOUR});
+
+static mapping colors = 
+([0:ANSI_BLACK,
+  1:ANSI_RED, 
+  2:ANSI_GREEN,
+  3:ANSI_YELLOW,
+  4:ANSI_BLUE,
+  5:ANSI_PURPLE,
+  6:ANSI_CYAN,
+  7:ANSI_WHITE,
+  8:""]);
+  
+static mapping bgcolors = 
+([0:ANSI_BG_BLACK,
+  1:ANSI_BG_RED, 
+  2:ANSI_BG_GREEN,
+  3:ANSI_BG_YELLOW,
+  4:ANSI_BG_BLUE,
+  5:ANSI_BG_PURPLE,
+  6:ANSI_BG_CYAN,
+  7:ANSI_BG_WHITE,
+  8:""]);
+  
+
+
+static string Mapping2ColoredText(mapping pic, object player);
+static string Mapping2PlainText(mapping pic);
+static void AddDescription(mapping pic, string type, object item);
+static string ComposeDesc(object item);
+static void ConfigureColors(string text);
+
+void ShowInv(object player, string arg);
+
+// ok, let´s just read in the graphics...
+// really takes some time (~250 eval ticks) but is only done
+// once in an uptime
+void create()
+{
+  mapping pic;
+  string *files, *lines, text;
+  int i,j,k, indentx,indenty, color;
+
+  data=([]);
+  
+  DB("Trying to fire up master, path is '"+INVPATH+"'...");
+  files=get_dir(INVPATH+"gfx/*")-({".", ".."});
+  DB(sprintf("Files found in 'gfx/': \n%O", files));
+  for (i=sizeof(files)-1;i>=0;i--)
+  {
+    DB("Reading '"+files[i]+"' ...");
+    text=read_file(INVPATH+"gfx/"+files[i]);
+    if (!stringp(text))
+    {
+      DB("Failed to read file.");
+      continue;
+    }
+    lines=explode(text, "\n");
+    if (sizeof(lines) < 4)
+    {
+      DB("File corrupt.");
+      continue;
+    }
+    indentx=to_int(lines[1]);
+    indenty=to_int(lines[2]);
+    color=to_int(lines[0]);
+    pic=([]);
+    for (j=sizeof(lines)-1;j>2;j--)
+    {
+      for (k=sizeof(lines[j])-1;k>=0;k--)
+      {
+        if (lines[j][k..k]!="?")
+          pic+=([(j-3+indenty)*80+k+indentx:lines[j][k..k];color]);
+      }
+    }
+    data+=([files[i]:pic]);
+    DB("File successfully read.");
+  }
+  DB(sprintf("Types covered:\n%O\n", m_indices(data)));
+
+  // create closure only once to save time
+  // needed by ComposeDesc()
+  // the closure ist not as complicated as it seems ;)
+  // it just checks every word of the name, if it does not begin
+  // with a capital letter, it is abbreviated
+  // this happens only if the name length exceeds 20 chars...
+  abbreviate=lambda(({'x}), 
+      ({#'?, ({#'>, ({#'member, quote(({"der", "des"})), 'x}), 0}),
+        "d.",
+        ({#'?, ({#'>, ({#'sizeof, 'x}), 3}), 
+          ({#'?, ({#',, ({#'=, 'a, ({#'allocate, 1}) }),
+                        ({#'=, ({#'[, 'a, 0}), 'x }), 
+                        ({#'sizeof, ({#'regexp, 'a, "^[a-z].*"}) }) 
+                 }), 
+            ({#'+, ({#'[..], 'x, 0, 1}), "."}),
+            'x
+          }), 
+          'x
+        })
+      }));
+}
+
+// function that tries to guess a good item name and use abbrevations
+// where possible
+static string ComposeDesc(object item)
+{
+  int i;
+  string text, *buff;
+  
+  text=regreplace(item->QueryProp(P_SHORT)
+                ||item->QueryProp(P_NAME)
+                ||"<?>",
+                "^(Ein Paar|Ein|Eine|Der|Die|Das) ","",0);
+                
+// try to shorten the name with the closure
+  if (sizeof(text) > 20)
+    return implode(map(explode(text, " "), abbreviate), " ");
+  else      
+    return text;
+}
+
+// converts a mapping with characters and color info into a
+// text. data in the mapping is stored in a one-dimensional 
+// order with the position as key (ypos is pos/80, xpos pos%80)
+// this setup has a huge advantage: combining several
+// graphics just takes a "+" operator, the rest is handled
+// by the game driver. freakin' fast, much better than doing an
+// iteration over one or more array in lpc.
+static string Mapping2ColoredText(mapping pic, object player)
+{
+  string text;
+  mapping configmap;
+  int i,j,color;
+  
+  configmap=default_config+(player->QueryProp(P_INVMASTER_CONFIG)||([]));
+
+  text="";
+  color=0;
+  for (i=0;i<22;i++)
+  {
+    text+=bgcolors[configmap[8]];
+    for (j=0;j<78;j++)
+    { 
+      if (pic[i*80+j,1]!=color)
+      {
+        color=pic[i*80+j,1];
+        text+=colors[configmap[color]];
+      }
+      text+=pic[i*80+j];
+    }
+    text+=ANSI_NORMAL+"\n";
+    color=0;
+  }
+  return text;
+}
+
+static string Mapping2PlainText(mapping pic)
+{
+  string text;
+  int i,j;
+  
+  text="";
+
+  for (i=0;i<22;i++)
+  {
+    for (j=0;j<78;j++)
+      text+=pic[i*80+j];
+    text+="\n";
+  }
+  return text;
+}
+static void AddDescription(mapping pic, string type, object item)
+{
+  int indentx, indenty, i;
+  string text;
+
+  switch(type)
+  {
+   case AT_HELMET:
+      indentx=47;
+      indenty=0;
+      text=sprintf("%-30s",ComposeDesc(item)[0..30]);break;
+    case AT_QUIVER:
+      indentx=49;
+      indenty=2;
+      text=sprintf("%-28s",ComposeDesc(item)[0..28]);break;     
+    case AT_AMULET:
+      indentx=49;
+      indenty=4;     
+      text=sprintf("%-27s",ComposeDesc(item)[0..28]);break;     
+    case AT_ARMOUR:
+      indentx=53;
+      indenty=7;
+      text=sprintf("%-24s",ComposeDesc(item)[0..25]);break;          
+    case AT_SHIELD:
+      indentx=54;
+      indenty=10;   
+      text=sprintf("%-20s",ComposeDesc(item)[0..24]);break;
+    case AT_CLOAK:
+      indentx=53;
+      indenty=15;
+      text=sprintf("%-20s",ComposeDesc(item)[0..25]);break;
+    case AT_TROUSERS:
+      indentx=49;
+      indenty=17;
+      text=sprintf("%-20s",ComposeDesc(item)[0..20]);break;     
+    case AT_RING:
+      indentx=0;
+      indenty=9;
+      text=sprintf("%14s",ComposeDesc(item)[0..17]);break;
+    case AT_GLOVE:
+      indentx=0;
+      indenty=11;
+      text=sprintf("%14s",ComposeDesc(item)[0..17]);break;
+    case AT_BELT:
+      indentx=1;
+      indenty=13;
+      text=sprintf("%14s",ComposeDesc(item)[0..18]);break;     
+    case AT_BOOT:
+      indentx=1;
+      indenty=20;
+      text=sprintf("%18s",ComposeDesc(item)[0..18]);break;
+    case "Waffe":
+      indentx=1;
+      indenty=1;
+      text=sprintf("%18s",ComposeDesc(item)[0..25]);
+      if (item->QueryProp(P_NR_HANDS) > 1 &&
+          this_player() &&
+          !(this_player()->QueryArmorByType(AT_SHIELD)))
+        AddDescription(pic, AT_SHIELD, item);break;
+    default: return;
+  }
+  for (i=0;i<sizeof(text);i++)
+    pic+=([(80*indenty+indentx+i):text[i..i];2]);
+}
+
+varargs static void ConfigureColors(string text)
+{ 
+  mapping config, display;
+  string *strs;
+  
+  if (!objectp(this_player())) return;
+  
+  if (this_player()->InFight())
+  {
+    write(break_string(
+      "Im Kampf? Na Du hast Nerven, das lassen wir doch mal lieber! "
+      "Probier es danach nochmal...", 78));
+    return;
+  }
+  
+  if (stringp(text)) text=lower_case(text);
+  
+  if (text=="ok")
+  {
+    write("Farbkonfiguration beendet.\n");
+    return;
+  }
+
+  //"ansi_config", def in invmaster.h
+  config=this_player()->QueryProp(P_INVMASTER_CONFIG)||([]);
+  display=default_config+config;
+  
+  if (!text || text=="")
+  {
+    write( 
+    "*** Farbkonfiguration fuer den Ausruestungsbefehl ***\n\n"
+    " Farbe:                     wird dargestellt mit:\n"
+    "------------------          --------------------\n"
+    " Hintergrund                 "+COLORNAMES[display[8]]+"\n"
+    " Schwarz                     "+COLORNAMES[display[0]]+"\n"
+    " Rot                         "+COLORNAMES[display[1]]+"\n"
+    " Gruen                       "+COLORNAMES[display[2]]+"\n"
+    " Gelb                        "+COLORNAMES[display[3]]+"\n"
+    " Blau                        "+COLORNAMES[display[4]]+"\n"
+    " Magenta                     "+COLORNAMES[display[5]]+"\n"
+    " Tuerkis                     "+COLORNAMES[display[6]]+"\n"
+    " Weiss                       "+COLORNAMES[display[7]]+"\n\n"
+    "Farbe aendern mit '<farbe> <gewuenschte farbe>'.\n"
+    "Beispiel: 'gelb rot'.\n"
+    "Alles, was standardmaessig gelb waere, wuerde dann mit der ANSI-Farbe \n"
+    "Rot dargestellt.\n"
+    "Der Hintergrund kann zusaetzlich die Farbe 'keine' haben, bei der der \n"
+    "Hintergrund eben ueberhaupt nicht gefaerbt wird.\n"
+    "Beispiel: 'hintergrund keine'. Schaltet die Hintergrundfarbe aus.\n\n"
+    "Beenden mit 'ok'. \n"
+    "Wiederholung der Farbliste mit <Return>.\n"
+    "Farbliste auf Standard zuruecksetzen mit 'reset'.\n");
+  }
+  else
+  if (text=="reset")
+  {
+    this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AD);    
+    this_player()->SetProp(P_INVMASTER_CONFIG, 0);
+    write("Farben zurueckgesetzt!\n");    
+  }
+  else
+  {
+    if ( sizeof(strs=explode(text, " ")-({""})) !=2 
+      || !member((COLORCODES-(["keine"])), strs[0])
+      || !member((COLORCODES-(["hintergrund"])), strs[1])
+      || ((strs[0]!="hintergrund") && (strs[1]=="keine")) ) 
+    {
+      write("Falsche Eingabe.\n"
+            "Format: <farbe|hintergrund> <zugewiesene Farbe>\n"
+            "Abbrechen mit 'ok'.\n");
+    }
+    else
+    {
+      if (COLORCODES[strs[1]]==default_config[COLORCODES[strs[0]]])
+        config-=([COLORCODES[strs[0]]]);
+      else
+        config+=([COLORCODES[strs[0]]:COLORCODES[strs[1]]]);
+      if (!sizeof(config))
+      {
+        this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AD);    
+        this_player()->SetProp(P_INVMASTER_CONFIG, 0);
+      }
+      else
+      {
+        this_player()->SetProp(P_INVMASTER_CONFIG, deep_copy(config));
+        this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AS);        
+      }
+      write("Ok, Farbe gewaehlt!\n");
+    }
+  }
+  input_to("ConfigureColors", INPUT_PROMPT, "\nEingabe: ");
+}
+
+
+string* armour_order=({ 
+    AT_HELMET, AT_AMULET, AT_QUIVER, AT_ARMOUR, AT_CLOAK,
+  AT_GLOVE, AT_RING, AT_BELT,
+  AT_TROUSERS, AT_BOOT, AT_SHIELD, AT_MISC}); 
+
+mapping weapon_names=([
+		       WT_SPEAR : "Speer",
+		       WT_SWORD : "Schwert",
+		       WT_STAFF : "Kampfstab",
+		       WT_WHIP  : "Peitsche",
+		       WT_CLUB  : "Keule",
+		       WT_KNIFE : "Messer",
+		       WT_MISC  : "Irgendwas",
+		       WT_MAGIC : "Artefakt",
+		       WT_AXE   : "Axt",
+	       WT_RANGED_WEAPON : "Fernwaffe"
+		       ]);
+
+string SimpleInv(object player) {
+  object* armours=player->QueryProp(P_ARMOURS);
+  int count=sizeof(armour_order);
+  string* list=allocate(count);
+  string result="Ausruestung\n";
+  int i;
+
+  foreach(object ob: armours) {
+    if (!objectp(ob)) continue;
+      int idx = member(armour_order, ob->QueryProp(P_ARMOUR_TYPE));	      
+      if (idx>=0)
+        list[idx]=ob->QueryProp(P_SHORT);
+  }
+
+  // AT_MISC (letztes Element in list und armour_order) weglassen.
+  for (i=0;i<count-1;i++) {
+    result+=sprintf("%-20s %-57s\n",armour_order[i],list[i] || "");
+  }
+
+  object ob=ob=player->QueryProp(P_WEAPON);
+  if (objectp(ob)) {
+    result+=sprintf("%-20s %-57s\n",
+		    (ob->QueryProp(P_NR_HANDS)==1 ? "Einhand-":"Zweihand-")
+		    +weapon_names[ob->QueryProp(P_WEAPON_TYPE)],
+		    ob->QueryProp(P_SHORT));
+  } else result+="Keine Waffe\n";
+
+  return result;
+}
+// the main function called by the player object.
+// determines gender, then adds armor/weapon graphics
+// dynamically. still very fast due to the use of the "+" operator,
+// see above.
+void ShowInv(object player, string arg)
+{
+  string gender, type;
+  mapping pic;
+  int i;
+  object item;
+
+  if (!objectp(player)||!interactive(player)) return;
+
+  // split args.
+  string *args;
+  if (stringp(arg))
+    args = explode(lower_case(arg), " ") - ({" "});
+  else
+    args = ({});
+
+  if (member(args, "farben") > -1) {
+      ConfigureColors();
+      return;
+  }
+
+  if (member(args, "-k") > -1 || player->QueryProp(P_NO_ASCII_ART)) {
+    tell_object(player, SimpleInv(player));
+    return;
+  }
+
+  gender=player->QueryProp(P_GENDER)==FEMALE?"_female":"_male";
+  pic=deep_copy(data["base"+gender]);
+  pic+=data["Beschriftung"];
+  for (i=sizeof(armour_types)-1;i>=0;i--)
+    if (objectp(item=player->QueryArmourByType(armour_types[i])))
+    {
+      pic+=data[armour_types[i]+gender];
+      AddDescription(pic, armour_types[i], item);
+    }
+  if (item=player->QueryProp(P_WEAPON))
+  {
+    pic+=data[(VALID_WEAPON_TYPE(type=item->QueryProp(P_WEAPON_TYPE)))?
+      type:WT_MISC];
+    AddDescription(pic, "Waffe", item);
+  }      
+  if (player->QueryProp(P_TTY)!="ansi")
+    player->More(Mapping2PlainText(pic));
+  else
+    player->More(Mapping2ColoredText(pic, player));
+  DB(geteuid(player)+" eval cost: "+(1000000-get_eval_cost())+" ticks.\n");
+}
+
diff --git a/std/player/invmaster/invmaster.h b/std/player/invmaster/invmaster.h
new file mode 100644
index 0000000..8821b63
--- /dev/null
+++ b/std/player/invmaster/invmaster.h
@@ -0,0 +1,21 @@
+#define INVPATH "/std/player/invmaster/"
+#define DB(x)
+/*
+#define DB(x) if (find_player("rikus")) \
+                   tell_object(find_player("rikus"), \
+                     break_string(x, 78, "INVMASTER: ", 1));
+*/
+#define P_INVMASTER_CONFIG "invmaster_config"
+#define COLORNAMES ({"Schwarz","Rot","Gruen","Gelb",\
+                     "Blau","Magenta","Tuerkis","Weiss","Keine"})
+#define COLORCODES (["schwarz":0,\
+                     "rot":1,\
+                     "gruen":2,\
+                     "gelb":3,\
+                     "blau":4,\
+                     "magenta":5,\
+                     "tuerkis":6,\
+                     "weiss":7,\
+                     "keine":8,\
+                     "hintergrund":8])
+#define default_config ([0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:0])
diff --git a/std/player/life.c b/std/player/life.c
new file mode 100644
index 0000000..73f1ff7
--- /dev/null
+++ b/std/player/life.c
@@ -0,0 +1,841 @@
+// MorgenGrauen MUDlib
+//
+// player/life.c -- player life handling
+//
+// $Id: life.c 9397 2015-12-11 20:29:26Z Zesstra $
+
+// Defines some things which are different than in living.c
+// One example is the heart_beat().
+
+// Properties
+//  P_AGE           -- Age
+
+#pragma strong_types, save_types, rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/life";
+
+#include <rtlimits.h>
+#include <debug_info.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <player/base.h>
+#include <player/life.h>
+#include <moving.h>
+#include <player/comm.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <language.h>
+#include <properties.h>
+#include <hook.h>
+
+#include <living/life.h>
+#include <player/pklog.h>
+#include <player/combat.h>
+#include <health.h>
+#include <living/combat.h>
+#include <attributes.h>
+#include <defuel.h>
+#include <new_skills.h>
+
+// Die Folgen eines Todes wirken 4 Stunden lang nach
+#define SUFFER_TIME 7200
+#define P_DEATH_INFO "death_info"
+
+int age;                  /* Number of heart beats of this character. */
+
+private int suffer_time;
+static int time_to_save;  /* when to next save player */
+private nosave int die_in_progress; // semaphore fuer die()-Aufrufe.
+
+static int _set_playerkills( int val );
+
+protected void create()
+{
+    ::create();
+    Set(P_AGE, -1, F_SET_METHOD);
+    Set(P_AGE, PROTECTED, F_MODE);
+    Set( P_KILLS, SAVE|SECURED, F_MODE_AS );
+    Set( P_GHOST, SAVE, F_MODE_AS );
+    Set( P_TIMING_MAP, SAVE|SECURED, F_MODE_AS );
+    Set( P_LAST_DEATH_TIME, SAVE|SECURED, F_MODE_AS );
+    Set( P_DEATH_INFO, SAVE|SECURED, F_MODE_AS );
+    Set( P_DEFUEL_LIMIT_FOOD,PROTECTED,F_MODE_AS);
+    Set( P_DEFUEL_LIMIT_DRINK,PROTECTED,F_MODE_AS);
+    Set( P_DEFUEL_TIME_FOOD,PROTECTED,F_MODE_AS);
+    Set( P_DEFUEL_TIME_DRINK,PROTECTED,F_MODE_AS);
+    Set( P_DEFUEL_AMOUNT_FOOD,PROTECTED,F_MODE_AS);
+    Set( P_DEFUEL_AMOUNT_DRINK,PROTECTED,F_MODE_AS);
+    offerHook(H_HOOK_HP,1);
+    offerHook(H_HOOK_SP,1);
+    // P_TIMING_MAP aufraeumen, aber zeitverzoegert, weil jetzt die Daten noch
+    // nicht aus dem Savefile geladen wurden.
+    call_out(#'expire_timing_map, 4);
+}
+
+// called from base.c in Reconnect()
+protected void reconnect() {
+  expire_timing_map();
+}
+
+static int _set_playerkills( int val )
+{
+    string tmp;
+    int playerkills;
+
+    // ist der Setzer in einer Arena/Schattenwelt. Dann nicht. (Ja, Bug ist,
+    // dass EMs aus Schattenwelt/Arena heraus auch das PK-Flag nicht
+    // zuruecksetzen koennen.)
+    if ( previous_object(1) && environment(previous_object(1)) &&
+         (tmp = object_name(environment(previous_object(1)))) &&
+         CheckArenaFight(previous_object(1)) )
+        return 0;
+
+    tmp = sprintf( "%O: %s %O %s",
+                   previous_object(1) || this_interactive() || this_player(),
+                   getuid(ME), val, dtime(time()) );
+
+    playerkills = Query(P_KILLS);
+
+    if( intp(val) && val >= 0 )
+        if( previous_object(1) && IS_ARCH(getuid(previous_object(1))) &&
+            this_interactive() && IS_ARCH(this_interactive()) )
+            playerkills = val;
+        else
+            tmp += " ILLEGAL!";
+    else
+        playerkills++;
+
+    if ( !previous_object(1) || !query_once_interactive(previous_object(1)) ||
+         IS_LEARNER(previous_object(1)) )
+        log_file( "SET_KILLS", tmp + "\n" );
+
+    return Set( P_KILLS, playerkills );
+}
+
+
+public int death_suffering()
+{
+    if ( suffer_time <= 0 )
+    return suffer_time = 0;
+
+    return 1 + (10 * suffer_time) / SUFFER_TIME;
+}
+
+
+protected void heart_beat()
+{
+    mapping di, mods;
+
+    ++age; // erstmal altern ;-)
+
+    ::heart_beat();
+
+    if ( age > time_to_save ){
+        save_me(1);
+        time_to_save = age + 500;
+    }
+
+    // als geist hat man mit den meisten weltlichen Dingen nicht allzuviel zu
+    // tun.
+    if ( QueryProp(P_GHOST) )
+        return;
+
+    if ( suffer_time > 0 )
+        suffer_time--;
+
+    // Todesfolgen nur alle 20 Sekunden (10 HB) checken.
+    // Das ist immer noch oft genug und spart Rechenzeit.
+    if ( (age % 10) || !mappingp(di = QueryProp(P_DEATH_INFO)) )
+        return;
+
+    mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"];
+    if (!mappingp(mods)) return;
+
+    if ( mods[A_STR] && --di[A_STR] <= 0) {
+        // einen Attributspunkt regenerieren
+        if ( mods[A_STR] < -1 ) {
+            mods[A_STR]++;
+            di[A_STR] = (110 + 5 * (di[A_STR, 1] + mods[A_STR])) / 10;
+        }
+        else {
+            m_delete( mods, A_STR );
+            m_delete( di, A_STR );
+        }
+    }
+
+    if ( mods[A_CON] && --di[A_CON] <= 0) {
+        // einen Attributspunkt regenerieren
+        if ( mods[A_CON] < -1 ){
+            mods[A_CON]++;
+            di[A_CON] = (110 + 5 * (di[A_CON, 1] + mods[A_CON])) / 10;
+        }
+        else {
+            m_delete( mods, A_CON );
+            m_delete( di, A_CON );
+        }
+    }
+
+    if ( mods[A_DEX] && --di[A_DEX] <= 0) {
+        // einen Attributspunkt regenerieren
+        if ( mods[A_DEX] < -1 ){
+            mods[A_DEX]++;
+            di[A_DEX] = (110 + 5 * (di[A_DEX, 1] + mods[A_DEX])) / 10;
+        }
+        else {
+            m_delete( mods, A_DEX );
+            m_delete( di, A_DEX );
+        }
+    }
+
+    if ( mods[A_INT] && --di[A_INT] <= 0) {
+        // einen Attributspunkt regenerieren
+        if ( mods[A_INT] < -1 ){
+            mods[A_INT]++;
+            di[A_INT] = (110 + 5 * (di[A_INT, 1] + mods[A_INT])) / 10;
+        }
+        else {
+            m_delete( mods, A_INT );
+            m_delete( di, A_INT );
+        }
+    }
+
+    if ( sizeof(di) && sizeof(mods))
+        SetProp( P_DEATH_INFO, di );
+    else
+        SetProp( P_DEATH_INFO, 0 );
+
+    SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+}
+
+
+public void force_save() {
+    time_to_save = 0;
+}
+
+
+nomask public int do_damage( int dam, object enemy )
+{
+    int hit_point;
+
+    if( QueryProp(P_GHOST) || dam <= 0 )
+        return 0;
+
+    hit_point = QueryProp(P_HP);
+
+    if ( query_once_interactive(ME) && dam >= hit_point && IS_LEARNING(ME) ){
+        tell_object( ME, "Deine magischen Kraefte verhindern Deinen Tod.\n" );
+        return 0;
+    }
+
+    if ( !objectp(enemy) )
+        enemy = previous_object() || this_interactive() || this_player();
+
+    hit_point -= dam;
+
+    if( hit_point < 0 ){
+        if ( !interactive(ME) )
+            // Netztote sterben nicht
+            hit_point = 10;
+        else {
+            if ( objectp(enemy) && interactive( enemy ) && enemy != ME &&
+                 !QueryProp(P_TESTPLAYER) && !IS_WIZARD(ME) &&
+                 !CheckArenaFight(ME) ) {
+                if ( QueryPlAttacked(enemy) )
+                    hit_point = 1;
+                else {
+                    hit_point = 0;
+                    enemy->SetProp( P_KILLS, -1 );
+                }
+
+                log_file( "KILLER",
+                          sprintf( "%s %s(%d/%d) toetete %s(%d/%d)%s\n",
+                                   ctime(time()),
+                                   getuid(enemy), query_wiz_level(enemy),
+                                   (int) enemy->QueryProp(P_LEVEL), getuid(ME),
+                                   query_wiz_level(ME), QueryProp(P_LEVEL),
+                                   (hit_point ? " NOTWEHR=>KEIN PK" : "") ) );
+            }
+            else { 
+                string killername;
+                if (objectp(enemy))
+                    killername=sprintf("%s (%s)",
+                        BLUE_NAME(enemy), REAL_UID(enemy));
+                else
+                    killername="??? (???)"; 
+
+                if ( !QueryProp(P_TESTPLAYER) )
+                    create_kill_log_entry(killername, enemy );
+            }
+
+            if ( enemy )
+                enemy->StopHuntFor( ME, 1 );
+
+            map_objects( QueryEnemies()[0], "StopHuntFor", ME, 1 );
+            StopHuntingMode(1);
+
+            Set( P_KILLER, enemy );
+            die();
+        }
+    }
+
+    SetProp( P_HP, hit_point );
+    return dam;
+}
+
+
+// Loescht im sterbenden Spieler die 'koerperabhaengigen' Properties
+private void reset_my_properties()
+{
+  // Loeschen der Properties
+  if ( QueryProp(P_POISON) )
+  {
+     // Don't die twice 'cause of the same poison
+     Set( P_POISON, 0, F_SET_METHOD );
+     Set( P_POISON, 0, F_QUERY_METHOD );
+     SetProp( P_POISON, 0 );
+  }
+
+  Set( P_FROG, 0, F_QUERY_METHOD ); 
+  Set( P_FROG, 0, F_SET_METHOD );
+  SetProp( P_FROG, 0 ); // Damit die Attribute auch stimmen.
+  Set( P_ALCOHOL, 0, F_QUERY_METHOD );
+  Set( P_ALCOHOL, 0, F_SET_METHOD );
+  SetProp(P_ALCOHOL, 0 );
+  Set( P_DRINK, 0, F_QUERY_METHOD );
+  Set( P_DRINK, 0, F_SET_METHOD );
+  SetProp(P_DRINK, 0 );
+  Set( P_FOOD, 0, F_QUERY_METHOD );
+  Set( P_FOOD, 0, F_SET_METHOD );
+  SetProp(P_FOOD, 0 );
+  Set( P_BLIND, 0, F_QUERY_METHOD );
+  Set( P_BLIND, 0, F_SET_METHOD );
+  SetProp(P_BLIND, 0 );
+  Set( P_DEAF, 0, F_QUERY_METHOD );
+  Set( P_DEAF, 0, F_SET_METHOD );
+  SetProp(P_DEAF, 0 );
+  Set( P_MAX_HANDS, 0, F_QUERY_METHOD );
+  Set( P_MAX_HANDS, 0, F_SET_METHOD );
+  SetProp( P_MAX_HANDS, 2 );
+  Set( P_HANDS_USED_BY, 0, F_QUERY_METHOD );
+  Set( P_HANDS_USED_BY, 0, F_SET_METHOD );
+  SetProp( P_HANDS_USED_BY, ({}) );
+  Set( P_PARA, 0 );
+  Set( P_NO_REGENERATION, 0, F_QUERY_METHOD );
+  Set( P_NO_REGENERATION, 0, F_SET_METHOD );
+  SetProp(P_NO_REGENERATION, 0 );
+  Set( P_TMP_MOVE_HOOK, 0, F_QUERY_METHOD );
+  Set( P_TMP_MOVE_HOOK, 0, F_SET_METHOD );
+  SetProp(P_TMP_MOVE_HOOK, 0 );
+  Set( P_LAST_DEATH_TIME , time() );
+  // damit der Teddy o.ae. mitbekommt, dass man jetzt tot ist ]:->
+  SetProp( P_HP, 0 );
+  SetProp( P_SP, 0 );
+}
+
+varargs protected int second_life( object corpse )
+{
+    int lost_exp, level;
+    // Es gibt Funktionen, die sollte man nicht per Hand aufrufen duerfen ;-)
+    if ( extern_call() && previous_object() != ME )
+        return 0;
+
+    if ( query_once_interactive(ME) && IS_LEARNING(ME) ){
+        tell_object( ME, "Sei froh, dass Du unsterblich bist, sonst waere "
+                     "es eben zu Ende gewesen.\n" );
+        return 1;
+    }
+
+    if ( !IS_SEER(ME) || (level = QueryProp(P_LEVEL)) < 20 )
+        lost_exp = QueryProp(P_XP) / 3;
+    else
+        lost_exp = QueryProp(P_XP) / (level - 17);
+
+    AddExp(-lost_exp);
+
+
+    // Todesfolgen setzen....
+    //SetProp( P_DEATH_INFO, 1);
+    if ( !IS_LEARNING(ME) && !QueryProp(P_TESTPLAYER) ) {
+
+        mapping attr = QueryProp(P_ATTRIBUTES);
+        mapping mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
+
+        // Attribute auf 75% vom aktuellen Wert senken
+        mods[A_STR] = -attr[A_STR] + (3 * (attr[A_STR] + mods[A_STR]) / 4);
+        mods[A_CON] = -attr[A_CON] + (3 * (attr[A_CON] + mods[A_CON]) / 4);
+        mods[A_DEX] = -attr[A_DEX] + (3 * (attr[A_DEX] + mods[A_DEX]) / 4);
+        mods[A_INT] = -attr[A_INT] + (3 * (attr[A_INT] + mods[A_INT]) / 4);
+
+        SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+
+        int offs = 220;  // 220 heart_beats == 7min20
+        // Die 220 HB sind fix, dazu kommen noch 5 HB pro realem 
+        // Attributspunkt. Geteilt wird das ganze noch durch 10, weil im HB
+        // nur alle 10 HBs die TF gecheckt werden. Da wird dann alle 10 HB ein
+        // Punkt abgezogen und wenn 0 erreicht ist, wird das Attribut um eins
+        // regeneriert.
+        SetProp( P_DEATH_INFO, ([ 
+            A_STR: (offs + 5 * (attr[A_STR] + mods[A_STR]))/10; attr[A_STR],
+            A_CON: (offs + 5 * (attr[A_CON] + mods[A_CON]))/10; attr[A_CON],
+            A_DEX: (offs + 5 * (attr[A_DEX] + mods[A_DEX]))/10; attr[A_DEX],
+            A_INT: (offs + 5 * (attr[A_INT] + mods[A_INT]))/10; attr[A_INT]
+                  ]) );
+
+        // die suffer_time wird via death_suffering() von
+        // QuerySkillAttribute() abgefragt und geht dann als Malus in
+        // SA_QUALITY mit ein.
+        if ( suffer_time <= 2*SUFFER_TIME )
+            suffer_time += SUFFER_TIME - 1;
+        else
+            suffer_time = 3 * SUFFER_TIME - 1;
+    }
+
+    // Die verschiedenen NotifyPlayerDeath-Funktionen koennen u.U. schlecht
+    // programmiert sein und zuviel Rechenzeit ziehen. Deshalb werden sie mit
+    // einem Limits von 150k bzw. 80k aufgerufen. Ausserdem werden sie nur
+    // gerufen, solange noch min. 25k Ticks da sind.
+    int *limits=query_limits();
+    limits[LIMIT_EVAL] = 150000;
+    limits[LIMIT_COST] = LIMIT_DEFAULT;
+
+    mixed killer = QueryProp(P_KILLER);
+    mixed gi = QueryProp(P_GUILD);
+    if (stringp(gi))
+        gi = find_object("/gilden/"+gi);
+    // jedes Objekt nur einmal, aber nicht via m_indices(mkmapping)) wegen
+    // Verlust der Reihenfolge.
+    object *items = ({killer});
+    if (environment() != killer)
+        items += ({environment()});
+    if (gi != killer && gi != environment())
+        items += ({gi});
+    foreach(object item: items) {
+        if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
+            break;
+        // falls ein NPD() implizit andere Objekt zerstoert hat.
+        if (objectp(item)) {
+            catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
+                  ME, killer, lost_exp);publish);
+        }
+    }
+    // jetzt den Rest.
+    limits[LIMIT_EVAL] = 80000;
+    foreach(object item: (environment() ? all_inventory(environment()) : ({}))
+                        + deep_inventory(ME)
+                        + (objectp(corpse) ? deep_inventory(corpse) : ({}))
+                        - items ) {
+        // wenn nicht mehr genug Ticks, haben die restlichen Pech gehabt.
+        if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
+            break;
+        // NPD() koennen andere Objekt zerstoeren.
+        if (objectp(item)) {
+            catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
+                                    ME, killer, lost_exp);publish);
+        }
+    }
+
+    // Properties zuruecksetzen, sollte nach dem NotifyPlayerDeath()
+    // passieren, weil Objekte sich darin evtl. erstmal noch aufraeumen und
+    // props manipulieren.
+    reset_my_properties();
+    UpdateAttributes(); // Beim Tod werden Dinge entfernt, Attribute pruefen
+    
+    // Auch Bewegung erst nach den NPD(), da diese den Spieler bewegen
+    // koennten, was eine Bewegung aus dem Todesraum waere, wenn die Bewegung
+    // vor dem NPD() stattfaende.
+    SetProp( P_GHOST, 1 ); // nach reset_my_properties() !
+    clone_object( "room/death/death_mark" )->move( ME, M_NOCHECK ); 
+
+    return 1;
+}
+
+
+public int AddHpHook( object ob )
+{
+    object *hooks;
+
+    if ( !objectp(ob) )
+        return 0;
+
+    if ( !pointerp(hooks = Query(P_HP_HOOKS)) ){
+        Set( P_HP_HOOKS, ({ ob }) );
+        return 1;
+    }
+
+    if ( member( hooks, ob ) >= 0 )
+        return 0;
+
+    Set( P_HP_HOOKS, (hooks - ({0})) + ({ ob }) );
+    return 1;
+}
+
+
+public int RemoveHpHook( object ob )
+{
+    object *hooks;
+
+    if ( !pointerp(hooks = Query(P_HP_HOOKS)) )
+        return 0;
+
+    Set( P_HP_HOOKS, hooks - ({ ob, 0 }) );
+    return 1;
+}
+
+
+static int _query_age() {
+    return Set(P_AGE, age, F_VALUE);
+}
+
+static int _set_hp( int hp )
+{
+    object *hooks;
+    int ret, i, old;
+
+    if ( (old = Query(P_HP, F_VALUE)) == hp )
+        return old;
+
+    ret = life::_set_hp(hp);
+
+    if ( ret == old )
+        return ret;
+
+    // Call old hooks in all objects... destructed objects will be ignored.
+    if (pointerp(hooks = Query(P_HP_HOOKS)))
+      call_other(hooks, "NotifyHpChange");
+
+    // Call new hooks.
+    HookFlow(H_HOOK_HP,ret);
+
+    // Report-ausgabe
+    status_report(DO_REPORT_HP, ret);
+
+    return ret;
+}
+
+
+static int _set_sp( int sp )
+{
+    object *hooks;
+    int ret, i, old;
+
+    if ( (old = Query(P_SP,F_VALUE)) == sp )
+        return old;
+
+    ret = life::_set_sp(sp);
+
+    if ( ret == old )
+        return ret;
+
+    // Call old hooks in all objects... destructed objects will be ignored.
+    if (pointerp(hooks = Query(P_HP_HOOKS)))
+      call_other(hooks, "NotifyHpChange");
+
+    // Call new hooks.
+    HookFlow(H_HOOK_SP,ret);
+
+    // Report-ausgabe
+    status_report(DO_REPORT_SP, ret);
+
+    return ret;
+}
+
+static int _set_poison(int n)
+{
+  int old = Query(P_POISON, F_VALUE);
+  if (old == n )
+      return old;
+  n = life::_set_poison(n);
+  if ( n == old )
+      return n;
+  // ggf. Statusreport ausgeben
+  if (interactive(ME))
+    status_report(DO_REPORT_POISON, n);
+  return n;
+}
+
+static int _set_max_poison(int n)
+{
+  if (n >= 0)
+  {
+    Set(P_MAX_POISON, n, F_VALUE);
+    int maxp=QueryProp(P_MAX_POISON); // koennte ne Querymethode drauf sein...
+    if (QueryProp(P_POISON) > maxp)
+      SetProp(P_POISON, maxp);
+  }
+  GMCP_Char( ([P_MAX_POISON: n]) );
+  return n;
+}
+
+static int _set_max_hp( int hp )
+{
+  if (hp >= 0)
+  {
+    Set(P_MAX_HP, hp, F_VALUE);
+    int maxhp=QueryProp(P_MAX_HP); // koennte ne Querymethode drauf sein...
+    if (QueryProp(P_HP) > maxhp)
+      SetProp(P_HP, maxhp);
+  }
+  GMCP_Char( ([P_MAX_HP: hp]) );
+  return hp;
+}
+
+static int _set_max_sp( int sp )
+{
+  if (sp >= 0)
+  {
+    Set(P_MAX_SP, sp, F_VALUE);
+    int maxsp=QueryProp(P_MAX_SP); // koennte ne Querymethode drauf sein...
+    if (QueryProp(P_SP) > maxsp)
+      SetProp(P_SP, maxsp);
+  }
+  GMCP_Char( ([P_MAX_SP: sp]) );
+  return sp;
+}
+
+static int _set_ghost( int g ) {
+    object team;
+
+    if(!g && query_hc_play()>1)
+    {
+      write("HAHAHA, DU BIST AUF EWIG MEIN.\n");
+      return Query(P_GHOST);
+    }
+
+    g = Set( P_GHOST, g );
+
+    if ( g && objectp(team = Query(P_TEAM)) )
+        team->RemoveMember(ME);
+
+    return g;
+}
+
+
+public int undie()
+{
+    mixed x, di;
+    mapping attr, mods;
+
+    if ( !this_interactive() || !previous_object() )
+        return 0;
+
+    if ( !IS_ARCH(this_interactive()) || !IS_ARCH(getuid(previous_object())) ||
+         process_call() )
+        log_file( "UNDIE", sprintf( "%s %O -> %O\n", dtime(time())[5..16],
+                                    this_interactive(), ME ) );
+
+    if ( x = Query(P_DEADS) )
+        x--;
+
+    Set( P_DEADS, x );
+
+    x = QueryProp(P_XP);
+
+    if ( (di = QueryProp(P_LEVEL)) < 20 || !IS_SEER(ME) )
+        x = (int)(x * 1.5);
+    else
+        // Umweg ueber float, weil bei hohen XP+Level sonst 32Bit nicht
+        // mehr ausreichen -> negative XP
+        x = (int) ( x * ((float) (di - 17) / (di - 18)) );
+
+    Set( P_XP, x );
+
+    attr = QueryProp(P_ATTRIBUTES) || ([]);
+    mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
+
+    if ( mappingp(di = QueryProp(P_DEATH_INFO)) ){
+        // Beim naechsten heart_beat checken
+        // Zesstra: Wieso eigentlich? Die Modifier werden doch direkt hier
+        // geloescht. So expired man auch einen Teil von nicht-undie-ten Toden
+        // vorzeitig...? Mal auskommentiert. 29.10.2007
+        //di[A_STR] = 1;
+        //di[A_DEX] = 1;
+        //di[A_INT] = 1;
+        //di[A_CON] = 1;
+    }
+    else
+      di = ([]);
+    
+    mods[A_STR] = ((4 * (attr[A_STR] + mods[A_STR])) / 3) - attr[A_STR];
+    mods[A_DEX] = ((4 * (attr[A_DEX] + mods[A_DEX])) / 3) - attr[A_DEX];
+    mods[A_INT] = ((4 * (attr[A_INT] + mods[A_INT])) / 3) - attr[A_INT];
+    mods[A_CON] = ((4 * (attr[A_CON] + mods[A_CON])) / 3) - attr[A_CON];
+
+    if ( mods[A_STR] >= 0 ) {
+        m_delete( mods, A_STR );
+        m_delete( di, A_STR);
+    }
+    if ( mods[A_DEX] >= 0 ) {
+        m_delete( mods, A_DEX );
+        m_delete( di, A_DEX);
+    }
+    if ( mods[A_INT] >= 0 ) {
+        m_delete( mods, A_INT );
+        m_delete( di, A_INT);
+    }
+    if ( mods[A_CON] >= 0 ) {
+        m_delete( mods, A_CON );
+        m_delete( di, A_CON);
+    }
+
+    SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+    if (sizeof(di))
+      SetProp( P_DEATH_INFO, di );
+    else
+      SetProp( P_DEATH_INFO, 0);
+
+    suffer_time -= ((SUFFER_TIME)-1);
+
+    if ( suffer_time < 0 )
+        suffer_time = 0;
+
+    Set( P_GHOST, 0 );
+    return 1;
+}
+
+
+varargs public void die( int poisondeath, int extern)
+{
+    // laeuft schon ein die()? Fehler ausloesen, Ursache rekursiver die() soll
+    // gefunden werden. DINFO_EVAL_NUMBER wird in jedem Top-Level Call im
+    // driver erhoeht, d.h. gleiche Zahl signalisiert ein rekursives die().
+
+    if (die_in_progress == debug_info(DINFO_EVAL_NUMBER))
+    {
+      // TODO: ist das die_in_progress aus dem letzten Backend-Cycle?
+      raise_error(sprintf(
+            "die() in %O gerufen, aber die() laeuft bereits!\n",
+            this_object()));
+    }
+    die_in_progress = debug_info(DINFO_EVAL_NUMBER);
+    
+    // Fuer HC-Player ists jetzt gelaufen...
+    if(query_hc_play()==1)
+    {
+      set_hc_play(capitalize(geteuid(ME)),time());
+      SetDefaultHome("/room/nirvana");
+      SetDefaultPrayRoom("/room/nirvana");
+      SetProp(P_START_HOME,"/room/nirvana");
+      log_file("HCDEAD",dtime(time())+" "+capitalize(geteuid(ME))
+          +" geht in das Nirvana ein!\n");
+    }
+
+    // Wenn das die() direkt von aussen gerufen wurde, muss P_KILLER hier
+    // gespeichert werden.
+    if (extern_call())
+        SetProp(P_KILLER, previous_object());
+
+    // Sichern der zu loeschenden Properties. Diese Props werden im Verlauf des
+    // Todes zurueckgesetzt. Einige Magier wollen diese Daten aber spaeter
+    // noch haben und fragen teilweise P_LAST_DEATH_PROPS im
+    // NotifyPlayerDeath() ab. Daher wird der Kram jetzt hier schonmal
+    // gesichert.
+    // BTW: Props mit Mappings/Arrays sollten kopiert werden.
+    SetProp(P_LAST_DEATH_PROPS, ([
+      P_POISON          : QueryProp(P_POISON),
+      P_FROG            : QueryProp(P_FROG),
+      P_ALCOHOL         : QueryProp(P_ALCOHOL),
+      P_DRINK           : QueryProp(P_DRINK),
+      P_FOOD            : QueryProp(P_FOOD),
+      P_BLIND           : QueryProp(P_BLIND),
+      P_DEAF            : QueryProp(P_DEAF),
+      P_MAX_HANDS       : QueryProp(P_MAX_HANDS),
+      P_PARA            : QueryProp(P_PARA),
+      P_NO_REGENERATION : QueryProp(P_NO_REGENERATION),
+      P_HP              : QueryProp(P_HP),
+      P_SP              : QueryProp(P_SP),
+      P_LAST_DEATH_TIME : QueryProp(P_LAST_DEATH_TIME )
+    ]) );
+
+    // call the inherited die() with 10 Mio Ticks which will be accounted as 1
+    // Tick... ;-)
+    int *limits = query_limits();
+    limits[LIMIT_EVAL] == 10000000;
+    limits[LIMIT_COST] == LIMIT_UNLIMITED;
+    limited(#'::die, limits, poisondeath, (extern_call() ? 1 : 0)); 
+
+    // nach dem Tod sollte man auch keine LP mehr haben.
+    SetProp(P_HP, 0);
+
+    // naechster Tod kann kommen. Dekrementierung, da 0 ein gueltiger Wert
+    // fuer DINFO_EVAL_NUMBER waere. abs(), um nicht  -__INT_MAX__ zu
+    // dekrementieren.
+    die_in_progress = abs(die_in_progress) - 1;
+}
+
+int defuel_food()
+{
+    int ret;
+    object prev;
+    
+    ret=::defuel_food();
+    prev=previous_object();
+    if(!prev || !objectp(prev))
+    {
+        prev=this_object();
+    }
+    
+    if(ret<=0)
+    {
+            call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,1);
+    }
+    else
+    {
+            call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,1);
+    }
+    return ret;
+}
+
+int defuel_drink()
+{
+    int ret;
+    object prev;
+    
+    ret=::defuel_drink();
+    prev=previous_object();
+    if(!prev || !objectp(prev))
+    {
+        prev=this_object();
+    }
+    
+    if(ret<=0)
+    {
+            call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,0);
+    }
+    else
+    {
+            call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,0);
+    }
+    return ret;
+}
+
+public void show_age()
+{ int i,j;
+
+    write("Alter:\t");
+    i = QueryProp(P_AGE);
+    if ((j=i/43200))
+    {
+      write(j + " Tag"+(j==1?" ":"e "));
+      i %= 43200;
+    }
+    if ((j=i/1800))
+    {
+      write(j + " Stunde"+(j==1?" ":"n "));
+      i %= 1800;
+    }
+    if ((j=i/30))
+    {
+      write(j + " Minute"+(j==1?" ":"n "));
+      i %= 30;
+    }
+    write(i*2 + " Sekunden.\n");
+}
+
diff --git a/std/player/moneyhandler.c b/std/player/moneyhandler.c
new file mode 100644
index 0000000..86a17db
--- /dev/null
+++ b/std/player/moneyhandler.c
@@ -0,0 +1,56 @@
+// MorgenGrauen MUDlib
+//
+// player/moneyhandler.c -- money handler for players
+// Nur noch aus Kompatibilitaetsgruenden vorhanden
+//
+// $Id: moneyhandler.c 9051 2015-01-11 20:28:00Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/moneyhandler";
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+#include <container/moneyhandler.h>
+#include <wizlevels.h>
+#include <properties.h>
+#include <money.h>
+#include <moving.h>
+
+public int AddMoney( int amount )
+{
+  object ob;
+
+  if ( !amount )
+    return 1;
+
+  int ret = moneyhandler::AddMoney(amount);
+
+  // ggf. noch loggen
+  if ( ret == MOVE_OK
+       && objectp(ob = find_object("/p/daemon/moneylog"))
+       && amount > 0
+       // dieses muss leider drinbleiben, weil viele nicht-Spieler dieses
+       // erben
+       && query_once_interactive(this_object())
+       && !IS_WIZARD(this_object())
+       && !Query(P_TESTPLAYER) )
+    ob->AddMoney( previous_object(), amount );
+
+  return ret;
+}
+
+public int QueryMoney()
+{
+  object money = present(SEHERKARTEID_AKTIV, this_object());
+  // zusaetzlich zu den anderen Geldquellen auch noch die Seherkarte pruefen.
+  if (money)
+    return moneyhandler::QueryMoney() + money->QueryProp(P_AMOUNT);
+
+  return moneyhandler::QueryMoney();
+}
+
diff --git a/std/player/moving.c b/std/player/moving.c
new file mode 100644
index 0000000..ab62f59
--- /dev/null
+++ b/std/player/moving.c
@@ -0,0 +1,182 @@
+// MorgenGrauen MUDlib
+//
+// player/moving.c -- player moving
+//
+// $Id: moving.c 9434 2016-01-12 12:34:05Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "std/living/moving";
+
+#define NEED_PROTOTYPES
+#include <player/base.h>
+#include <living/description.h>
+#include <player/viewcmd.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <events.h>
+#include <pathd.h>
+
+private nosave string *connections;
+
+public void create()
+{
+    SetProp( P_MSGIN,  "kommt an" );
+    SetProp( P_MSGOUT, "geht" );
+    SetProp( P_MMSGIN, "tritt aus einer Schwefelwolke" );
+    SetProp( P_MMSGOUT, "verschwindet mit Knall und Schwefelduft" );
+    Set( P_MSGIN,   SAVE, F_MODE );
+    Set( P_MSGOUT,  SAVE, F_MODE );
+    Set( P_MMSGIN,  SAVE, F_MODE );
+    Set( P_MMSGOUT, SAVE, F_MODE );
+    connections = ({});
+
+    ::create();
+}
+
+
+static mixed _to_remove( object ob )
+{
+    return ob->QueryProp(P_AUTOLOADOBJ) || ob->QueryProp(P_NODROP);
+}
+
+
+// autoload and nodrop object may not fall into the environment
+static varargs int remove( int silent )
+{
+    object* dest_obj = filter( deep_inventory(ME) - ({0}), "_to_remove" );
+    filter_objects( dest_obj, "remove" );
+    filter( dest_obj - ({0}), #'destruct );
+
+    if ( !QueryProp(P_INVIS) && !silent )
+        catch( say( name(WER, 1) + " verlaesst diese Welt.\n", ME );publish );
+    
+    if ( ME && !silent )
+        tell_object( ME, "Bis bald!\n" );
+
+    // Im Falle des Resets ist previous_object() == ME, aber
+    // previous_object(0) == 0. Ausserdem ist der Caller-Stack leer. Also
+    // schauen, ob es ein PO gibt, was nicht gleich dem Objekt selber ist, TI
+    // pruefen und 
+    if ( this_interactive() != ME
+        && objectp(previous_object()) && previous_object() != ME
+        && object_name(previous_object())[0..7] != "/secure/"
+        && member(object_name(ME), ':') > -1 )
+      log_file( "PLAYERDEST",
+                sprintf( "%s: %O vernichtet von PO %O, TI %O, TP %O\n",
+                         dtime(time()), ME, previous_object(),
+                         this_interactive(), this_player() ) );
+
+    // Logout-event ausloesen
+    EVENTD->TriggerEvent(EVT_LIB_LOGOUT, ([
+	    E_OBJECT: ME,
+	    E_PLNAME: getuid(ME),
+	    E_ENVIRONMENT: environment() ]) );
+
+    return ::remove();
+}
+
+public string NotifyDestruct(object caller) {
+  
+  if (previous_object() != master()
+      || object_name(this_object()) == __FILE__[..<3])
+    return 0;
+
+  // Das Zerstoeren von Spielern wird ggf. geloggt.
+  if ( objectp(caller) && caller != this_object()
+       && getuid(caller) != ROOTID ) {
+      log_file( "PLAYERDEST",
+		sprintf( "%s: %O VERNICHTET von PO %O, TI %O, TP %O\n",
+			 dtime(time()), this_object(), caller,
+			 this_interactive(), this_player() ) );
+  }
+  // erstmal nix weiter tun, destruct gestatten.
+  return 0;
+}
+
+protected int PreventMove(object dest, object oldenv, int method) {
+  string hcroom;
+  mixed res;
+
+  // gestorbene HC-Spieler duerfen nicht mehr aus dem Nirvana, nicht umgehbar
+  // durch M_NOCHECK
+  if ( interactive(ME) && (query_hc_play()>1) ) {
+    if (objectp(dest))
+      hcroom=object_name(dest);
+    if (sizeof(hcroom)<7 || hcroom[0..5]!="/room/") { 
+      return ME_CANT_BE_INSERTED;
+    }
+  }
+
+  // alle anderen Pruefungen sind mit M_NOCHECK egal.
+  if ( (method & M_NOCHECK) )
+    return(::PreventMove(dest,oldenv,method));
+
+  // Spieler duerfen in Raeume mit gesetztem P_NO_PLAYERS nicht hinein
+  if ( dest->QueryProp(P_NO_PLAYERS) && interactive(ME) &&
+	!(method & M_NOCHECK) &&
+        !IS_LEARNER(ME) && (!stringp(res = QueryProp(P_TESTPLAYER))
+                             || !IS_LEARNER( lower_case(res) )) ){
+      tell_object( ME, "Da darfst Du als Spieler nicht hin.\n" );
+      return ME_NOT_ALLOWED;
+  }
+
+  return ::PreventMove(dest,oldenv,method);
+}
+
+// Fuck. Ausnahmsweise wegen VC brauch ich nen anderes BLUE_NAME
+#define OLD_BLUE_NAME(ob) (explode(object_name(ob),"#")[0])
+// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
+protected void NotifyMove(object dest, object oldenv, int method) {
+
+  // erstmal ggf. Rauminhalt an Spieler ausgeben.
+  if ( interactive(ME) && !(method & M_NO_SHOW) ) {
+      if (!CannotSee(1))
+          tell_object( ME, "" + env_descr(1) );
+      else if ( QueryProp(P_BRIEF) < 2 )
+          tell_object( ME, "Finsternis.\n" );
+  }
+
+  //dann geerbten Kram machen
+  ::NotifyMove(dest,oldenv,method);
+
+  // Schlussendlich noch fuer den PathD bewegung protokollieren.
+  // (dest nicht geprueft, da ein Spieler nicht ausserhalb jedes Env bewegt
+  // werden kann)
+  if ( interactive() && environment() && query_verb() && objectp(oldenv)) {
+      connections += ({ ({ OLD_BLUE_NAME(oldenv), OLD_BLUE_NAME(dest),
+                        query_verb() + " " + (_unparsed_args(2) || ""),
+                        method, dest->QueryProp(P_PARA) }) });
+
+      if ( sizeof(connections) > 50
+           && find_call_out("flush_connections") == -1 )
+            call_out( "flush_connections", 0, connections );
+  }
+}
+
+public void flush_connections() {
+    catch(PATHD->add_paths(connections);publish);
+    connections = ({});
+}
+
+/*** Query-Methoden fuer Froesche... ;^) ***/
+static string _query_msgin()
+{
+    return QueryProp(P_FROG) ? "huepft herein" : Query(P_MSGIN);
+}
+
+
+static string _query_msgout()
+{
+    return QueryProp(P_FROG) ? "huepft" : Query(P_MSGOUT);
+}
diff --git a/std/player/objects.c b/std/player/objects.c
new file mode 100644
index 0000000..9263ea3
--- /dev/null
+++ b/std/player/objects.c
@@ -0,0 +1,243 @@
+// MorgenGrauen MUDlib
+//
+// player/objects.c -- object handling for player
+//
+// $Id: objects.c 8675 2014-02-18 20:39:54Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include "/sys/player/filesys.h"
+
+#include <config.h>
+#include <player.h>
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <thing/moving.h>
+
+static int update_object(string str) {
+  object ob;
+  string upd_file;
+  if (!(str=_unparsed_args())) {
+    notify_fail("Usage: Update <object_path>\n"); return 0;
+  }
+  upd_file = find_file(str,".c");
+  if (!upd_file) upd_file=find_file(str);
+  if (!upd_file) {
+    notify_fail(str+": No such file.\n"); return 0;
+  }
+  ob = find_object(upd_file);
+  if (!ob) {
+    notify_fail(upd_file+": No such object.\n"); return 0;
+  }
+  destruct(ob);
+  write(upd_file + ": will be reloaded at next reference.\n");
+  return 1;
+}
+
+/*
+ * "soft" means that the object is given the chance to self-destruct, thus
+ * allowing it to do necessary cleanup like subtracting from the carried
+ * weight of the environment(). We call remove() in the object to be
+ * destructed.
+ */
+static int soft_update_object(string str) 
+{
+  object ob;
+  string upd_file;
+  if (!(str=_unparsed_args())) {
+    notify_fail("Usage: update <object_path>\n"); return 0;
+  }
+  upd_file = find_file(str,".c");
+  if (!upd_file) upd_file=find_file(str);
+  if (!upd_file) {
+    notify_fail(str+": No such file.\n"); return 0;
+  }
+  ob = find_object(upd_file);
+  if (!ob) {
+    notify_fail(upd_file+": No such object.\n"); return 0;
+  }
+  if (ob->remove() == 0) {
+    notify_fail(upd_file+": doesn't want to be destructed!\n"); return 0;
+  }
+  write(upd_file + ": will be reloaded at next reference.\n");
+  return 1;
+}
+
+int clone(string str) 
+{
+  object ob;
+  string clone_file;
+
+  if (!(str=_unparsed_args())){
+    notify_fail("Usage: clone <object_path>\n"); return 0;
+  }
+  clone_file = find_file(str,".c");
+  if (!clone_file) clone_file=find_file(str);
+  if (!clone_file) {
+    notify_fail(str+": No such file.\n"); return 0;
+  }
+  if (!(ob = clone_object(clone_file)))
+    return notify_fail(str+": Failed to load.\n"), 0;
+
+  /* Some objects destruct themselves rather fast */
+  if (!objectp(ob))
+    return notify_fail(str+": Destructed whilst created.\n"), 0;
+  
+  /* try to move the object to my environment */
+  if ((ob->move(this_object(),M_GET)>0) || 
+      (ob->move(environment(),M_NOCHECK)>0))
+  {
+    if (!objectp(ob))
+      return notify_fail(str+": Destructed whilst created.\n"), 0;
+    write("Cloned "+object_name(ob)+".\n");
+    say(this_player()->name(WER,1) + " "
+		+ this_player()->QueryProp(P_CLONE_MSG)+".\n");
+    return 1;
+  }
+  say(this_player()->name(WER,1)+" malt wilde Zeichen in die Luft und "
+      +"murmelt vor sich hin, aber nichts passiert...\n");
+  destruct(ob);
+  write(clone_file+": failed to move\n");
+  return 1;
+}
+
+/*
+ * "soft" means that the object is given the chance to self-destruct, thus
+ * allowing it to do necessary cleanup like subtracting from the carried
+ * weight of the environment(). We call remove() in the object to be
+ * destructed.
+ */
+static int soft_destruct_object(string str)
+{ 
+  object ob;
+  object *obs;
+  string strWER,strWEN;
+
+  if (!(str=_unparsed_args())){
+    notify_fail("Usage: destruct <objectname>\n"); return 0;
+  }
+  strWER = lower_case(str);
+  obs = this_player()->find_obs(strWER,PUT_GET_NONE);
+  if (!obs || !sizeof(obs)) {
+    notify_fail("Kein \"" + str + "\" gefunden.\n");
+    return 0;
+  }
+  ob=obs[0];
+  strWER=ob->name(WER);
+  strWEN=ob->name(WEN);
+  if (!strWER)
+    strWER="jemand";
+  if (!strWEN)
+    strWEN="jemanden";
+
+  if (ob->remove() == 0) {
+    notify_fail(strWER+" will nicht 'destructed' werden!\n");
+    say(this_player()->name(WER,1)+" versucht vergeblich, "+strWEN+
+        " zu atomisieren.\n");
+    return 0;
+  }
+  say(capitalize(strWER)+" "+this_player()->QueryProp(P_DESTRUCT_MSG)+".\n");
+  write(capitalize(strWER)+" wird von dir zerstaeubt.\n");
+  return 1;
+}
+
+static int destruct_object(string str)
+{ 
+  object ob;
+  object *obs;
+  string strWER,strWEN;
+
+  if (!(str=_unparsed_args())) {
+    notify_fail("Usage: Destruct <objectname>\n"); return 0;
+  }
+  strWER = lower_case(str);
+  obs = this_player()->find_obs(strWER,PUT_GET_NONE);
+  if (!obs || !sizeof(obs)) {
+    notify_fail("Kein \"" + str + "\" gefunden.\n"); return 0;
+  }
+  ob=obs[0];
+  strWER=ob->name(WER);
+  strWEN=ob->name(WEN);
+
+  say(capitalize(strWER)+" "+this_player()->QueryProp(P_DESTRUCT_MSG)+".\n");
+  destruct( ob );
+  write(capitalize(strWER)+" wird von dir zerstaeubt.\n");
+  return 1;
+}
+
+static int load(string str)
+{ 
+  object env;
+  string file;
+  string err;
+
+  if (!(str=_unparsed_args())) {
+    notify_fail("Usage: load <object_path>\n"); return 0;
+  }
+  file = find_file(str,".c");
+  if (!file) file=find_file(str);
+  if (!file) {
+    notify_fail(str+": No such file.\n"); return 0;
+  }
+  if ( err = catch(load_object(file);publish) )
+    printf("Cannot load %O, err = %O\n",file,err);
+  else write(file+"\n");
+  return 1;
+}
+
+static int exec_playerob(string name)
+{
+  object ob, *inv;
+  int i;
+
+  if (!IS_LORD(this_object())) return 0;
+  if (this_player() != this_interactive()) return 0;
+  if (this_player() != this_object()) return 0;
+  if (!(name=_unparsed_args())) return 0;
+  name="secure/master"->_get_path(name,getuid(this_object()));
+  if (catch(load_object(name);publish) ) 
+  {
+    write("BUG in "+name+"\n");
+    return 1;
+  }
+  ob=clone_object(name);
+  if (!ob) return 0;
+  if (getuid(ob) != getuid(this_object()))
+  {
+    write("UID conflict.\n");
+    destruct(ob);
+    return 1;
+  }
+  log_file("EXEC", getuid(this_object())+" "+name+" "+dtime(time()));
+  disable_commands();
+  exec(ob,this_object());
+  if (interactive(this_object()) || !interactive(ob))
+  {
+    enable_commands();
+    write("Fehler in exec\n");
+    return 1;
+  }
+  inv=all_inventory(this_object());
+  ob->start_player(capitalize(getuid(this_object())));
+  remove();
+  return 1;
+}
+
+static mixed _query_localcmds()
+{
+  return ({
+           ({"clone","clone",0,WIZARD_LVL}),
+	   ({"destruct","soft_destruct_object",0,LEARNER_LVL}),
+	   ({"Destruct","destruct_object",0,LEARNER_LVL}),
+	   ({"load","load",0,WIZARD_LVL}),
+	   ({"update","soft_update_object",0,LEARNER_LVL}),
+	   ({"Update","update_object",0,LEARNER_LVL}),
+	   ({"exec","exec_playerob",0,LEARNER_LVL})
+	 });
+}
diff --git a/std/player/pklog.c b/std/player/pklog.c
new file mode 100644
index 0000000..4613ebe
--- /dev/null
+++ b/std/player/pklog.c
@@ -0,0 +1,108 @@
+// MorgenGrauen MUDlib
+/** \file /std/player/pklog.c
+* Funktion zur Detektion und Loggen von Angriffen von Spielern auf Spieler.
+* \author Zesstra
+* \date 12.08.2008
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+#include <defines.h>
+#include <commands.h>
+#include <wizlevels.h>
+#include <player/base.h>
+
+#define RNAME(x) capitalize(getuid(x))
+
+/** Ist victim in einer Arena oder Schattenwelt?
+  */
+nomask int CheckArenaFight(object victim) {
+  return (object_name(environment(victim))[0..14]=="/d/schattenwelt");
+}
+
+nomask protected int CheckPlayerAttack(object attacker, object victim,
+                                       string angriffsmsg)
+{
+  // falls mal jemand ne Closure auf diese Funktion in die Finger bekommt und
+  // protected umgeht.
+  if (extern_call())
+      raise_error(sprintf("Extern Call to CheckPlayerAttack in %O!\n",ME));
+
+  // nur Spieler sind interessant.
+  if ( query_once_interactive(attacker) && query_once_interactive(victim)
+      && !( IS_LEARNER(attacker) && IS_LEARNER(victim) ) ) {
+    string filemessage, wizshout;
+    int arena;
+
+    // Arena- oder Schattenweltkampf?
+    arena=CheckArenaFight(attacker);
+
+    wizshout = sprintf("\n**** %s greift %s an. (%s) ***\n",
+        RNAME(attacker), RNAME(victim), object_name(ME));
+    filemessage = sprintf("\n[%s] %s greift %s an. (%s) %s %s\n",
+        strftime("%d%m%y-%H:%M:%S",time()), RNAME(attacker),
+        RNAME(victim), object_name(this_object()),
+        (arena ? "(ARENA)" : ""),
+        (victim->QueryProp(P_TESTPLAYER) ? "(Testspieler)" : ""));
+
+    // Angriffsmsg vom Aufrufer anhaengen.
+    if (stringp(angriffsmsg) && sizeof(angriffsmsg)) {
+      wizshout += angriffsmsg;
+      filemessage += angriffsmsg;
+    }
+    // ggf. echten TI anhaengen oder warnen, falls keiner existiert.
+    if ( this_interactive() != attacker ) {
+        if ( this_interactive() ) {
+          wizshout += "ACHTUNG: TI = " + getuid(this_interactive())
+            +"\n";
+          filemessage += "ACHTUNG: TI = "+getuid(this_interactive())
+            +"\n";
+        }
+        else {
+            filemessage += " ACHTUNG: Kein TI vorhanden!\n";
+            wizshout += " ACHTUNG: Kein TI vorhanden!\n";
+        }
+    }
+    // caller_stack() mitloggen (aber nicht Magier vollscrollen).
+    filemessage += "Callerstack: " + CountUp(map(caller_stack(1),
+          function string (object po) {return to_string(po);}),
+        ", ", ", ") + "\n";
+    // Commandstack anhaengen
+    mixed cstack = command_stack();
+    filemessage += "Commandstack: " + CountUp(map(cstack,
+          function string (mixed arr) {
+              return sprintf("({Original-TP: %O, TP: %O, Kommando: %s})",
+                arr[CMD_ORIGIN],arr[CMD_PLAYER],arr[CMD_TEXT] || "");
+          },", ",", ")) + "\n";
+    // fuer Magier originaeren Befehl anhaengen:
+    if (sizeof(cstack))
+      wizshout += sprintf("Kommando: %s\n",
+          cstack[0][CMD_TEXT] || "<unbekannt>");
+
+    wizshout += "\n";
+
+    // erstmal loggen
+    if ( arena )
+        log_file("ATTACKS_ARENA", filemessage);
+    else 
+        log_file("ATTACKS", filemessage);
+
+    // dann Magiern bescheidgeben
+    if ( !arena && !(victim->QueryProp(P_TESTPLAYER)) ) {
+      foreach(object wiz: users()) {
+        if ( IS_LORD(wiz) || IS_DEPUTY(wiz) )
+          tell_object(wiz, wizshout);
+      }
+    }
+    return 1; // Spieler-Spielerkampf
+  }
+  return 0; // kein Spieler-Spielerkampf
+}
+
diff --git a/std/player/potion.c b/std/player/potion.c
new file mode 100644
index 0000000..23d8caa
--- /dev/null
+++ b/std/player/potion.c
@@ -0,0 +1,252 @@
+// MorgenGrauen MUDlib
+//
+// player/potion.c -- potion handling for player
+//
+// $Id: potion.c 9280 2015-08-15 22:20:36Z Arathorn $
+//
+
+#pragma strong_types,save_types
+
+#include <input_to.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <player/potion.h>
+#include <attributes.h>
+#include <living/life.h>
+#include <player/base.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+
+#define POTIONMASTER "/secure/potionmaster"
+
+static mixed *list;
+
+mixed *potionrooms;
+mixed *known_potionrooms;
+
+static mixed _query_potionrooms();
+static mixed _query_known_potionrooms();
+
+protected void create()
+{
+  if (!potionrooms) potionrooms = POTIONMASTER->InitialList();
+  Set(P_POTIONROOMS, NOSETMETHOD, F_SET_METHOD);  // no tampering by methods
+  Set(P_POTIONROOMS, SECURED, F_MODE_AS);         // no tampering with list
+
+  if (!known_potionrooms) known_potionrooms = ({});
+  Set(P_KNOWN_POTIONROOMS, NOSETMETHOD, F_SET_METHOD);
+  Set(P_KNOWN_POTIONROOMS, SECURED, F_MODE_AS);
+}
+
+static int ReportPotion(string s, int pnum);
+static int SelectWhich(int pnum);
+static int ask_question(int pnum);
+static int get_answer(string erg, int pnum);
+static int raise(string what, int pnum);
+int AddKnownPotion(int nr);
+int RemoveKnownPotion(int nr);
+
+protected void updates_after_restore(int newflag)
+{
+  // P_VISITED_POTIONROOMS ist lang veraltet und unbenutzt, aber bis zum
+  // 21.1.2015 sogar in neuen Spielern gespeichert worden.
+  // Aehnlich fuer P_BONUS_POTIONS. Weg damit.
+  Set(P_VISITED_POTIONROOMS, SAVE|PROTECTED, F_MODE_AD);
+  Set(P_BONUS_POTIONS, SAVE|PROTECTED, F_MODE_AD);
+}
+
+varargs int FindPotion(string s)
+{
+  object po = previous_object();
+  int pnum = POTIONMASTER->HasPotion(po);
+  int flag = 1;
+
+  if ( QueryProp(P_TRANK_FINDEN) && IS_WIZARD(ME) )
+  {
+    return ReportPotion(s, pnum);
+  }
+
+  if ( QueryProp(P_KILLS) )
+    return 0;
+
+  if ( !potionrooms || !sizeof(potionrooms) ||
+       !(POTIONMASTER->InList(po, potionrooms, known_potionrooms)) )
+    flag=0;
+
+  if ( pnum < 0 || !flag )
+    return 0;
+
+  if ( query_input_pending(ME) || query_editing(ME) )
+  {
+    tell_object(ME,
+      "Jetzt haettest Du fast einen Zaubertrank gefunden. Du solltest den\n"
+      "Editor/das More verlassen und es dann noch einmal versuchen!\n");
+    return 1;
+  }
+
+  // Hier der Ausbau der ZTs fuer Geister, wobei das natuerlich in der
+  // Geisterschlossquest immer noch gehen sollte.
+  object env = environment(ME);
+  string fn = old_explode(object_name(env), "#")[0];
+  if ( QueryProp(P_GHOST) && fn[0..24] != "/d/wald/bertram/gschloss/" )
+  {
+    tell_object(ME,"Als Geist einen Zaubertrank? Hier nicht!\n");
+    return 1;
+  }
+  log_file("ARCH/POTIONS", sprintf("%s  %s in %s\n", dtime(time()),
+    capitalize(getuid()), object_name(po)));
+
+  return ReportPotion(s, pnum);
+}
+
+static int ReportPotion(string s, int pnum)
+{
+  if (stringp(s) && sizeof(s))
+    tell_object(ME, s);
+  else
+    tell_object(ME, "Du findest einen Zaubertrank, den Du sofort trinkst.\n");
+
+  SelectWhich(pnum);
+
+  return 1;
+}
+
+static int SelectWhich(int pnum)
+{
+  list=({"Intelligenz","Kraft","Geschicklichkeit","Ausdauer"});
+  if (QueryRealAttribute(A_INT)>=20) list-=({"Intelligenz"});
+  if (QueryRealAttribute(A_STR)>=20) list-=({"Kraft"});
+  if (QueryRealAttribute(A_DEX)>=20) list-=({"Geschicklichkeit"});
+  if (QueryRealAttribute(A_CON)>=20) list-=({"Ausdauer"});
+  if (!sizeof(list)) {
+    heal_self(10000000);
+    tell_object(ME, "Der Trank hat Dich geheilt.\n");
+    log_file("ARCH/POTIONS",
+       sprintf("  %s: Heiltrank (noch %d)\n",
+         capitalize(getuid()), sizeof(potionrooms)-1));
+
+    potionrooms -= ({pnum});
+    known_potionrooms -= ({pnum});
+    save_me(1);
+    return 1;
+  }
+  if ( sizeof(list)==1 )
+    return raise(list[0], pnum);
+  ask_question(pnum);
+  return 1;
+}
+
+static int ask_question(int pnum)
+{
+  int i;
+
+  tell_object(ME, "Deine Attribute sehen so aus:\n\n"
+    +sprintf("Intelligenz     : %2d (%+d)\n",
+         QueryRealAttribute(A_INT),QueryAttributeOffset(A_INT))
+    +sprintf("Kraft           : %2d (%+d)\n",
+         QueryRealAttribute(A_STR),QueryAttributeOffset(A_STR))
+    +sprintf("Geschicklichkeit: %2d (%+d)\n",
+         QueryRealAttribute(A_DEX),QueryAttributeOffset(A_DEX))
+    +sprintf("Ausdauer        : %2d (%+d)\n",
+         QueryRealAttribute(A_CON),QueryAttributeOffset(A_CON))
+    );
+
+  tell_object(ME,
+    "\nWas moechtest Du erhoehen? Du hast folgende Moeglichkeiten:\n");
+  for (i=0; i<sizeof(list); i++)
+    tell_object(ME, sprintf("%d) %s\n", i+1, list[i]));
+
+  input_to("get_answer", INPUT_PROMPT,
+    sprintf("\nBitte gib jetzt eine Zahl (1-%d) an: ", i), pnum);
+
+  return 1;
+}
+
+static int get_answer(string erg, int pnum)
+{
+  int num = to_int(erg);
+
+  if ( num > 0 && num <= sizeof(list) )
+    return raise(list[num-1], pnum);
+
+  tell_object(ME, "Deine Wahl war ungueltig. Bitte versuch's nochmal!\n\n");
+  return ask_question(pnum);
+}
+
+static int raise(string what, int pnum) {
+  string attr;
+
+  switch (what)
+  {
+    case "Geschicklichkeit": attr=A_DEX; break;
+    case "Intelligenz":      attr=A_INT; break;
+    case "Kraft":            attr=A_STR; break;
+    case "Ausdauer":         attr=A_CON; break;
+    default:                 return 0;
+  }
+
+  int yet=QueryRealAttribute(attr);
+  SetRealAttribute(attr, yet+1);
+  tell_object(ME,
+    sprintf("Deine %s hat sich von %d auf %d erhoeht.\n", what, yet, yet+1));
+  log_file("ARCH/POTIONS",
+     sprintf("  %s: %s %d->%d (noch %d)\n",
+       capitalize(getuid()), capitalize(attr),
+       yet, yet+1, sizeof(potionrooms)-1));
+
+  // Wenn die Property gesetzt ist, wird nicht versucht, den gefundenen Trank
+  // aus der ZT-Liste des Magiers auszutragen. Der Nebeneffekt, dass
+  // existierende, angeschlossene ZTs auch nicht mehr aus der ZT-Liste von
+  // testenden Magiern ausgetragen werden, wird dabei in Kauf genommen.
+  if ( !QueryProp(P_TRANK_FINDEN) )
+  {
+    potionrooms       -= ({ pnum });
+    known_potionrooms -= ({ pnum });
+    save_me(1);
+  }
+  return 1;
+}
+
+static mixed _query_potionrooms()
+{
+  return copy(Set(P_POTIONROOMS, potionrooms));
+}
+
+static mixed _query_known_potionrooms()
+{
+  return copy(Set(P_KNOWN_POTIONROOMS, known_potionrooms));
+}
+
+int AddKnownPotion(int nr)
+{
+  if (!previous_object() ||
+      object_name(previous_object()) != "/room/orakel")
+    return -1; // Keine Berechtigung
+
+  if (member(known_potionrooms, nr) != -1)
+    return -2; // Nummer bereits eingetragen
+  else
+  {
+    known_potionrooms += ({ nr });
+    return 1;
+  }
+}
+
+int RemoveKnownPotion(int nr)
+{
+  if (!previous_object() ||
+      object_name(previous_object()) != "/room/orakel")
+    return -1; // Keine Berechtigung
+
+  if (member(known_potionrooms, nr) == -1)
+    return -2; // Nummer nicht eingetragen
+  else
+  {
+    known_potionrooms -= ({ nr });
+    return 1;
+  }
+}
diff --git a/std/player/protocols/gmcp.c b/std/player/protocols/gmcp.c
new file mode 100644
index 0000000..b951daa
--- /dev/null
+++ b/std/player/protocols/gmcp.c
@@ -0,0 +1,785 @@
+// MorgenGrauen MUDlib
+//
+// gmcp.c -- Verwaltung von GMCP im Spielerobjekt
+//
+// $Id$
+
+#pragma strong_types,save_types
+#pragma range_check
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+
+#include <regexp.h>
+#include <telnet.h>
+
+#define NEED_PROTOTYPES
+#include <player/base.h>
+#include <thing/properties.h>
+#include <living/attributes.h>
+#include <player/gmcp.h>
+#include <thing/description.h>
+#include <living/description.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <new_skills.h>
+#include <rooms.h>
+#include <tls.h>
+
+inherit "/secure/telnetneg-structs.c";
+
+struct gmcp_mod_s {
+  string id;        // Name des GMCP-Moduls (z.B. "MG.Char")
+  int version;      // Version des aktivierten moduls
+  closure sendcl;   // Handler zum Senden (lfun-closure)
+  closure recvcl;   // Handler zum Empfangen (lfunc-closure)
+  mixed data;       // optional data of the module
+};
+
+nosave mapping gmcpdata;
+/* Struktur:
+   Jedes Modul hat einen Schluessel im Toplevel, worunter ggf. seine Daten
+   abgelegt sind. Die Daten sind eine struct gmcp_mod_s. Der Schluessel ist
+   der Modulname OHNE Version.
+   */
+#define NEED_PROTOTYPES
+#include "/secure/telnetneg.h"
+#undef NEED_PROTOTYPES
+
+//#define __GMCP_DEBUG__ 1
+// Low priority debug messages
+#define GMCP_DEBUG(pre,msg,prio) if (interactive(this_object()) \
+                            && gmcpdata) \
+                          GMCP_debug(pre,msg,prio);
+// higher priority error messages
+#define GMCPERROR(msg) if (interactive(this_object()) \
+                       && gmcpdata) \
+                     GMCP_debug("ERROR",msg,10);
+
+
+// **************** API nach Aussen folgt ab hier ********************
+
+// Wird vom Spielerobjekt gerufen, wenn sich Daten am Charakter veraendert
+// haben, die gesendet werden sollten.
+// Dies ist eigentlich nur ein Wrapper, der die Daten an den Handler eines
+// Moduls weitergibt, welches vom Client aktiviert wurde. Hierzu kommen zur
+// Zeit 2 in Frage: MG.Char (bevorzugt) und Char (minimaler Support).
+/*protected*/ int GMCP_Char(mapping data) {
+
+  if (!mappingp(gmcpdata)) return 0;
+
+  // Als erstes schauen, ob der Client MG.Char aktiviert hat.
+  struct gmcp_mod_s mod = gmcpdata["MG.char"];
+  if (structp(mod) && closurep(mod->sendcl))
+  {
+    funcall(mod->sendcl, data);
+    return 1;
+  }
+  // Dann noch das Modul char pruefen. Das ist aber ziemlich eingeschraenkt
+  // und es gibt hoffentlich nicht viele Clients, die es benutzen.
+  // (Aardwolf-Modul)
+  mod = gmcpdata["char"];
+  if (structp(mod) && closurep(mod->sendcl))
+  {
+    funcall(mod->sendcl, data);
+    return 1;
+  }
+  // Dann noch das Modul Char pruefen. Das ist aber ziemlich eingeschraenkt
+  // und es gibt hoffentlich nicht viele Clients, die es benutzen.
+  // (IRE-Modul)
+  mod = gmcpdata["Char"];
+  if (structp(mod) && closurep(mod->sendcl))
+  {
+    funcall(mod->sendcl, data);
+    return 1;
+  }
+  return 0;
+}
+
+/*protected*/ int GMCP_Channel(string msg, string channel, string sender) {
+  if (!mappingp(gmcpdata)) return 0;
+  // comm.channel Modul aktiv?
+  struct gmcp_mod_s mod = gmcpdata["comm.channel"];
+  if (structp(mod) && closurep(mod->sendcl))
+  {
+    funcall(mod->sendcl, (["chan":channel, "player": sender,
+                           "msg": msg]) );
+    return 1;
+  }
+  return 0;
+}
+
+/*protected*/ int GMCP_Room() {
+  if (!mappingp(gmcpdata)) return 0;
+  // MG.room Modul aktiv?
+  struct gmcp_mod_s mod = gmcpdata["MG.room"];
+  if (structp(mod) && closurep(mod->sendcl))
+  {
+    funcall(mod->sendcl, 0);
+    return 1;
+  }
+  return 0;
+}
+
+// **************** Ab hier folgen eher die Lowlevel-Dinge ***********
+private void GMCP_debug(string pre, string msg, int prio) {
+  struct gmcp_mod_s mod = gmcpdata["Core"];
+  if (mod && (mod->data)["Debug"] >= prio)
+    tell_object(this_object(), sprintf("GMCP %s: %s\n",pre,msg));
+}
+
+private void GMCP_send(string cmd, mixed data)
+{
+  GMCP_DEBUG("GMCP_send",sprintf("%s %O",cmd,data), 30);
+  send_telnet_neg_str(sprintf("%c%c%s %s", SB, TELOPT_GMCP,
+                              cmd, json_serialize(data)), 1);
+}
+
+private void GMCP_unregister_module(string mod)
+{
+  int version;
+  // Wenn nicht "mod version" Schema, ignorieren
+  if (sscanf(mod, "%s %d", mod, version) != 2)
+      return;
+
+  if (mod=="Core") // darf nicht abgeschaltet werden.
+      return;
+
+  m_delete(gmcpdata, mod);
+}
+
+private void GMCP_register_module(string modname)
+{
+  int version;
+  GMCP_DEBUG("register_module(): trying ... ",modname, 20);
+  // Wenn nicht "mod version" Schema, ignorieren
+  if (sscanf(modname, "%s %d", modname, version) != 2)
+      return;
+
+//  GMCP_DEBUG("register_module()",modname + " v" + version);
+
+  // Modul (ggf. mit anderer Version) bereits aktiv?
+  struct gmcp_mod_s mod = gmcpdata[modname];
+  if (structp(mod)) {
+    // wenn gleicher Name und Version, wird nix gemacht, bei anderer Version
+    // wird ein neuer Handler eingetragen und die Daten geloescht.
+    // Wenn nicht-existierende Modul/Version-Kombi angefordert wird, ist das
+    // Modul hinterher aus.
+    if (mod->id == modname && mod->version == version)
+      return;
+    else
+      m_delete(gmcpdata,modname);
+  }
+
+  // Das GMCP-Modul ist nur verfuegbar, wenn es zu der Kombination aus mod und
+  // version einen Handler zum Senden gibt...
+  // Der Handler ist: GMCP_<mod>_v<version>_send, aber in <mod> werden alle
+  // "." durch "_" ersetzt.
+  string replacedname = regreplace(modname, "\\.", "_", RE_GLOBAL);
+  closure sendcl = symbol_function(sprintf("GMCPmod_%s_v%d_send",
+                      replacedname, version),
+                   this_object());
+  if (!sendcl)
+    return;
+  // Diese Closure darf 0 sein. Dann findet keine Behandlung von vom Client
+  // gesendeten Kommandos statt. Was fuer die meisten Module auch in Ordnung
+  // ist, da sie dem Client keine Kommandos anbieten.
+  closure recvcl = symbol_function(sprintf("GMCPmod_%s_v%d_recv",
+                      replacedname, version),
+                   this_object());
+
+  GMCP_DEBUG("register_module()",modname+" erfolgreich registriert.",10);
+
+  mod = (<gmcp_mod_s> id: modname, version : version,
+         sendcl : sendcl, recvcl: recvcl, data: ([]) );
+  gmcpdata[modname] = mod;
+
+  // Zum schluss noch den Senden-handler mal rufen, damit der mal alle
+  // verfuegbaren daten sendet.
+  funcall(mod->sendcl, 0);
+}
+
+// Handler fuer das Core Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_Core_v1_recv(string cmd, mixed args)
+{
+  struct gmcp_mod_s mod = gmcpdata["Core"];
+  mapping data = mod->data;
+
+/*  if (!mod)
+  {
+    GMCPERROR("Command %s for disabled module ignored.");
+    return;
+  }
+  */
+  GMCP_DEBUG("GMCPmod_Core_v1: ", cmd, 20);
+
+  switch (cmd)
+  {
+    case "Core.Hello":
+      if (mappingp(args))
+        data["Hello"] = (["client": args["client"],
+                          "version": args["version"] ]);
+      break;
+    case "Core.Supports.Set":
+      if (pointerp(args))
+      {
+        // Alte Module abschalten/loeschen
+        foreach(string m : data["Supports"])
+          GMCP_unregister_module(m);
+        data["Supports"] = args;
+        // Versuchen, die neuen Module zu registrieren
+        foreach(string m : args)
+          GMCP_register_module(m);
+      }
+      else
+          GMCP_DEBUG("GMCPmod_Core_v1: ",
+              "Data for Core.Supports.Set is no array", 5);
+      break;
+    case "Core.Supports.Add":
+      if (!pointerp(data["Supports"]))
+        data["Supports"] = ({});
+      if (pointerp(args))
+      {
+        foreach(string m: args)
+          GMCP_register_module(m);
+        data["Supports"] += args;
+      }
+      break;
+    case "Core.Supports.Remove":
+      if (!pointerp(data["Supports"]))
+        break;
+      if (pointerp(args))
+      {
+        foreach(string m: args)
+          GMCP_unregister_module(m);
+        data["Supports"] -= args;
+      }
+      break;
+    case "Core.Supports.KeepAlive":
+      break;  // this is ignored by us.
+    case "Core.Ping":
+      if (intp(args))
+        data["Ping"] = args;
+      // send a ping back
+      GMCP_send("Core.Ping",0);
+      break;
+    case "Core.Debug":
+      if (intp(args) && args >= 0)
+        data["Debug"] = args;
+      break;
+    default:
+      GMCPERROR(sprintf("Unknown GMCP Core cmd %s with args %O",
+            cmd, args));
+      break;
+  }
+}
+
+// Handler fuer das Core Modul von GMCP
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_Core_v1_send(mapping data)
+{
+  // Zur Zeit nix, spaeter mal Core.Goodbye.
+}
+
+
+// Handler fuer das MG.Char Modul
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_char_v1_send(mapping data)
+{
+  mapping squeue = m_allocate(5,0);
+  struct gmcp_mod_s mod = gmcpdata["MG.char"];
+  // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+  // leider immer alle senden, nicht nur die geaenderten.
+  if (!mappingp(data))
+  {
+    // Alle verfuegbaren Informationen senden...
+    mod->data = m_allocate(6);
+    m_add(mod->data, "MG.char.base",
+              ([P_NAME: Name(WER),
+                P_GUILD: QueryProp(P_GUILD),
+                P_PRESAY: QueryProp(P_PRESAY),  // TODO
+                P_TITLE: QueryProp(P_TITLE),
+                "wizlevel": query_wiz_level(this_object()),
+                P_RACE: QueryProp(P_RACE)]) );  // TODO
+    m_add(mod->data,"MG.char.vitals", 
+              ([P_HP: QueryProp(P_HP),
+                P_SP: QueryProp(P_SP),
+                P_POISON: QueryProp(P_POISON) ]) );
+    m_add(mod->data,"MG.char.maxvitals",
+              ([P_MAX_HP: QueryProp(P_MAX_HP),
+                P_MAX_SP: QueryProp(P_MAX_SP),
+                P_MAX_POISON: QueryProp(P_MAX_POISON) ]) );
+    m_add(mod->data,"MG.char.attributes",
+              ([ A_STR: QueryAttribute(A_STR),
+                 A_INT: QueryAttribute(A_INT),
+                 A_DEX: QueryAttribute(A_DEX),
+                 A_CON: QueryAttribute(A_CON) ]) );
+    m_add(mod->data,"MG.char.info",
+              ([P_LEVEL: QueryProp(P_LEVEL),
+                P_GUILD_LEVEL: QueryProp(P_GUILD_LEVEL),
+                P_GUILD_TITLE: QueryProp(P_GUILD_TITLE) ]) );
+    m_add(mod->data,"MG.char.wimpy",
+              ([P_WIMPY: QueryProp(P_WIMPY),
+                P_WIMPY_DIRECTION: QueryProp(P_WIMPY_DIRECTION) ]) );
+    m_add(squeue,"MG.char.base");
+    m_add(squeue,"MG.char.vitals");
+    m_add(squeue,"MG.char.maxvitals");
+    m_add(squeue,"MG.char.attributes");
+    m_add(squeue,"MG.char.info");
+    m_add(squeue,"MG.char.wimpy");
+    // dies wird direkt gesendet, weil es nicht gespeichert werden muss. (wird
+    // nur beim Start des Moduls gesendet).
+    GMCP_send("MG.char.infoVars", ([
+          P_LEVEL: "Spielerstufe", P_GUILD_LEVEL: "Gildenstufe",
+          P_GUILD_TITLE: "Gildentitel" ]) );
+  }
+  else
+  {
+    // nur die in data enthaltenen senden.
+    // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+    // muss... *seufz*
+    foreach(string key, mixed val : data)
+    {
+      switch(key)
+      {
+        case P_HP:
+        case P_SP:
+        case P_POISON:
+          (mod->data)["MG.char.vitals"] += ([key: val]);
+          m_add(squeue,"MG.char.vitals");
+          break;
+        case P_MAX_HP:
+        case P_MAX_SP:
+        case P_MAX_POISON:
+          (mod->data)["MG.char.maxvitals"] += ([key: val]);
+          m_add(squeue,"MG.char.maxvitals");
+          break;
+        case P_NAME:
+          (mod->data)["MG.char.base"] += ([key: Name(WER)]);
+          m_add(squeue,"MG.char.base");
+          break;
+        case P_RACE:
+        case P_PRESAY:
+        case P_TITLE:
+        case P_GUILD:
+          (mod->data)["MG.char.base"] += ([key: val]);
+          m_add(squeue,"MG.char.base");
+          break;
+        case A_DEX:
+        case A_STR:
+        case A_CON:
+        case A_INT:
+          (mod->data)["MG.char.attributes"] += ([key: val]);
+          m_add(squeue,"MG.char.attributes");
+          break;
+        case P_LEVEL:
+        case P_GUILD_LEVEL:
+        case P_GUILD_TITLE:
+          (mod->data)["MG.char.info"] += ([key: val]);
+          m_add(squeue,"MG.char.info");
+          break;
+        case P_WIMPY:
+        case P_WIMPY_DIRECTION:
+          (mod->data)["MG.char.wimpy"] += ([key: val]);
+          m_add(squeue,"MG.char.wimpy");
+          break;
+      }
+    }
+  }
+  GMCP_DEBUG("GMCPmod_MG_char_v1_send()",
+      sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+  // Jetzt die squeue senden...
+  foreach(string key : squeue)
+  {
+    GMCP_send(key, (mod->data)[key]);
+  }
+}
+
+// Handler fuer das MG.Char Modul
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_char_v1_recv(string cmd, mixed args)
+{
+  // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+  GMCP_DEBUG("GMCPmod_MG_Char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+/*
+// Handler fuer das MG.Room Modul von GMCP
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_MG_Room_v1_send(mapping data)
+{
+}
+
+// Handler fuer das Room Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_Room_v1_recv(string cmd, mixed args)
+{
+  // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+  GMCP_DEBUG("GMCPmod_MG_Room_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+*/
+
+// Recv Handler fuer das comm.channel Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_comm_channel_v1_recv(string cmd, mixed args)
+{
+  GMCP_DEBUG("GMCPmod_comm_channel_v1_recv",
+      "Client-Kommando ignoriert: "+cmd,20);
+}
+
+// Send Handler fuer das comm.channel Modul von GMCP
+protected void GMCPmod_comm_channel_v1_send(mapping data)
+{
+  // Ganz simpel: einfach raussenden...
+  // Core uebergibt beim Einschalten 0 als data. Dieses modul muss aber beim
+  // Eisnchalten nix machen. Also nur ignorieren.
+  if (mappingp(data))
+    GMCP_send("comm.channel", data);
+}
+
+// Recv Handler fuer das MG.room Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_room_v1_recv(string cmd, mixed args)
+{
+  GMCP_DEBUG("GMCPmod_MG_room_v1_recv",
+      "Client-Kommando ignoriert: "+cmd,20);
+}
+
+// Send Handler fuer das comm.channel Modul von GMCP
+protected void GMCPmod_MG_room_v1_send(mapping data)
+{
+  // Bekommt immer 0 als <data> uebergeben und sucht sich die Daten aus dem
+  // Raum zusammen.
+
+  // Baeh. Warum wird das denn ohne Env gerufen. :-(
+  if (!environment())
+    return;
+
+  // Blind gibt es auch per GMCP nix.
+  if (CannotSee(1))
+    return;
+
+  int restr = environment()->QueryProp(P_MAP_RESTRICTIONS);
+
+  if (restr & MR_NOINFO)
+    return; // gar keine info senden.
+
+  // Anmerkung: int_short() waere cool. Dummerweise uebertraegt das auch
+  // sichtbare Ausgange und Objekte. Insofern: geht nicht.
+  data = ([
+      P_SHORT: process_string(environment()->QueryProp(P_INT_SHORT)||"")+".",
+      "domain": environment()->QueryProp(P_DOMAIN) || "unbekannt",
+      ]);
+
+  // sichtbare Ausgaenge ausgeben
+  mixed hide = environment()->QueryProp(P_HIDE_EXITS);
+  if (hide && !pointerp(hide))
+      data["exits"] = ({});   // alle verstecken
+  else
+  {
+      // Query() verwenden, damit sowohl normale als auch Special Exits
+      // kommen... Die Summe von beiden wuerde auch gehen, aber dann hat man
+      // zwei unnoetige Filter in den Querymethoden. Hngl.
+      mapping exits = environment()->Query(P_EXITS, F_VALUE) || ([]);
+      if (pointerp(hide))
+        data["exits"] = m_indices(exits) - hide;
+      else
+        data["exits"] = m_indices(exits);
+  }
+
+  if (restr & MR_NOUID)
+    data["id"] = "";
+  else
+    data["id"] = hash(TLS_HASH_MD5, object_name(environment()));
+
+  GMCP_send("MG.room.info", data);
+}
+
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_char_v1_send(mapping data)
+{
+  mapping squeue = m_allocate(4,0);
+  struct gmcp_mod_s mod = gmcpdata["char"];
+  // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+  // leider immer alle senden, nicht nur die geaenderten.
+  if (!mappingp(data))
+  {
+    // Alle verfuegbaren Informationen senden...
+    mod->data = m_allocate(4);
+    m_add(mod->data, "char.base", (["name": query_real_name(),
+                                 "race": QueryProp(P_RACE)]) );
+    m_add(mod->data,"char.vitals", (["hp": QueryProp(P_HP),
+                                  "mana": QueryProp(P_SP)]) );
+    m_add(mod->data,"char.stats", ([ "str": QueryAttribute(A_STR),
+                               "int": QueryAttribute(A_INT),
+                               "dex": QueryAttribute(A_DEX),
+                               "con": QueryAttribute(A_CON) ]) );
+    m_add(mod->data,"char.status", (["level": QueryProp(P_LEVEL) ]) );
+    m_add(squeue,"char.base");
+    m_add(squeue,"char.vitals");
+    m_add(squeue,"char.stats");
+    m_add(squeue,"char.status");
+  }
+  else
+  {
+    // nur die in data enthaltenen senden.
+    // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+    // muss... *seufz*
+    foreach(string key, mixed val : data)
+    {
+      switch(key)
+      {
+        case P_HP:
+          (mod->data)["char.vitals"] += (["hp": val]);
+          m_add(squeue,"char.vitals");
+          break;
+        case P_SP:
+          (mod->data)["char.vitals"] += (["mana": val]);
+          m_add(squeue,"char.vitals");
+          break;
+        case P_NAME:
+        case P_RACE:
+          (mod->data)["char.base"] += ([key: val]);
+          m_add(squeue,"char.base");
+          break;
+        case A_DEX:
+        case A_STR:
+        case A_CON:
+        case A_INT:
+          (mod->data)["char.stats"] += ([key: val]);
+          m_add(squeue,"char.stats");
+          break;
+        case P_LEVEL:
+          (mod->data)["char.status"] += ([key: val]);
+          m_add(squeue,"char.status");
+          break;
+      }
+    }
+  }
+  GMCP_DEBUG("GMCPmod_char_v1_send()",
+      sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+  // Jetzt die squeue senden...
+  foreach(string key : squeue)
+  {
+    GMCP_send(key, (mod->data)[key]);
+  }
+}
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_char_v1_recv(string cmd, mixed data)
+{
+  // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+  GMCP_DEBUG("GMCPmod_char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+
+// Handler fuer das "Char" Modul von GMCP (Modul von IRE)
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_Char_v1_send(mapping data)
+{
+  mapping squeue = m_allocate(4,0);
+  struct gmcp_mod_s mod = gmcpdata["Char"];
+  // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+  // leider immer alle senden, nicht nur die geaenderten.
+  if (!mappingp(data))
+  {
+    // Alle verfuegbaren Informationen senden...
+    mod->data = m_allocate(4);
+    m_add(mod->data,"Char.Vitals", (["hp": QueryProp(P_HP),
+                                     "mp": QueryProp(P_SP),
+                                     "maxhp": QueryProp(P_MAX_HP),
+                                     "maxmp": QueryProp(P_MAX_SP) ]) );
+    m_add(mod->data,"Char.Status", (["level": QueryProp(P_LEVEL),
+                                     "guild": QueryProp(P_GUILD) ]) );
+    m_add(squeue,"Char.Vitals");
+    m_add(squeue,"Char.Status");
+    // dies wird direkt gesendet, weil es nicht gespeichert werden muss. (wird
+    // nur beim Start des Moduls gesendet).
+    GMCP_send("Char.StatusVars", ([
+          "level": "Spielerstufe", "guild": "Gilde" ]) );
+  }
+  else
+  {
+    // nur die in data enthaltenen senden.
+    // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+    // muss... *seufz*
+    foreach(string key, mixed val : data)
+    {
+      switch(key)
+      {
+        case P_HP:
+          (mod->data)["Char.Vitals"] += (["hp": val]);
+          m_add(squeue,"Char.Vitals");
+          break;
+        case P_SP:
+          (mod->data)["Char.Vitals"] += (["mp": val]);
+          m_add(squeue,"Char.Vitals");
+          break;
+        case P_MAX_HP:
+          (mod->data)["Char.Vitals"] += (["maxhp": val]);
+          m_add(squeue,"Char.Vitals");
+          break;
+        case P_MAX_SP:
+          (mod->data)["Char.Vitals"] += (["maxmp": val]);
+          m_add(squeue,"Char.Vitals");
+          break;
+        case P_LEVEL:
+        case P_GUILD:
+          (mod->data)["Char.Status"] += ([key: val]);
+          m_add(squeue,"Char.Status");
+          break;
+      }
+    }
+  }
+  GMCP_DEBUG("GMCPmod_Char_v1_send()",
+      sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+  // Jetzt die squeue senden...
+  foreach(string key : squeue)
+  {
+    GMCP_send(key, (mod->data)[key]);
+  }
+}
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_Char_v1_recv(string cmd, mixed args)
+{
+  // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+  GMCP_DEBUG("GMCPmod_Char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+
+// Handler, der von telnetneg.c gerufen wird.
+private void _std_re_handler_gmcp(struct telopt_s opt, int action,
+                                  int *optargs)
+{
+  switch(action)
+  {
+    case LOCALON:
+      // super!
+      GMCP_DEBUG("recv:", "LOCALON",10);
+      gmcpdata = ([]);
+      opt->data = gmcpdata;   // daten auch dort ablegen.
+      // Coremodule in der Version 1 registrieren (es gibt nur eine).
+      GMCP_register_module("Core 1");
+#ifdef __GMCP_DEBUG__
+      GMCPmod_Core_v1_recv("Core.Debug",30);
+#endif
+      break;
+    case LOCALOFF:
+      // alles abschalten und Daten loeschen
+      GMCP_DEBUG("recv:", "LOCALOFF",10);
+      opt->data = 0;
+      gmcpdata = 0;
+      break;
+    case REMOTEON:
+    case REMOTEOFF:
+      // Huch. Auf Clientseite ist GMCP eigentlich nie an. Ignorieren...
+      GMCP_DEBUG("recv:", "Huh? REMOTE state changed?",50);
+      break;
+    case SB:
+      // Der eigentlich interessante Fall... GMCP-Kommandos
+      if (!mappingp(gmcpdata)) return; // GMCP wohl nicht eingeschaltet...
+      string cmd;
+      mixed args;
+      string payload=to_string(optargs);
+      GMCP_DEBUG("recv", payload,10);
+      if (sscanf(payload,"%s %s", cmd, args) != 2) {
+        // ist vermutlich ein Kommando ohne daten (oder Muell)
+        cmd = payload;
+        //args = 0;
+      }
+      else
+      {
+        string err=catch(args = json_parse(args);nolog);
+        if (err)
+        {
+          printf("\nFehler beim Parsen einer GMCP-Nachricht: %s. "
+                 "Nachricht war: '%s'\n"
+                 "Befehl: '%s', Argument: '%s'\n\n",err,payload,cmd,args||"");
+          return;
+        }
+      }
+      GMCP_DEBUG("recv", sprintf("Command: %s, Data: %O", cmd, args),20);
+
+      string *cmdparts = explode(cmd, ".");
+      struct gmcp_mod_s mod;
+      string modname;
+      // versuch, ein Modul fuer das Kommando zu finden. Anfangen mit der
+      // Annahme, dass bis zum letzten Punkt der Modulname geht und dann
+      // in jedem case einen Punkt kuerzer werdend.
+      switch(sizeof(cmdparts))
+      {
+        case 4:
+          modname = implode(cmdparts[0..2],".");
+          GMCP_DEBUG("trying modname... ", modname, 20 );
+          if (member(gmcpdata, modname)) {
+            mod = gmcpdata[modname];
+            funcall(mod->recvcl, cmd, args);
+            break;
+          }
+          // Fall-through!
+        case 3:
+          modname = implode(cmdparts[0..1],".");
+          GMCP_DEBUG("trying modname... ", modname, 20);
+          if (member(gmcpdata, modname)) {
+            mod = gmcpdata[modname];
+            funcall(mod->recvcl, cmd, args);
+            break;
+          }
+          // Fall-through!
+        case 2:
+          modname = implode(cmdparts[0..0],".");
+          GMCP_DEBUG("trying modname... ", modname, 20);
+          if (member(gmcpdata, modname)) {
+            mod = gmcpdata[modname];
+            funcall(mod->recvcl, cmd, args);
+            break;
+          }
+          // Hier ists jetzt nen Fehler.
+          GMCPERROR(sprintf("Unknown GMCP module for cmd %s",cmd));
+          break;
+        default:
+          // zuviele oder zuwenig . ;-)
+          GMCPERROR(sprintf("Illegal GMCP cmd %s with args %O",
+                cmd, args));
+          break;
+      }
+      // sbdata brauchen wir eigentlich nicht mehr.
+      opt->re_wishes->sbdata = 0;
+      break;
+  } // switch (action)
+}
+
+// wird von base.c nach Konnektierung gerufen.
+// Darf aber erst gerufen werden, wenn das Spielerobjekt fertig initialisiert
+// und eingelesen ist.
+protected void startup_telnet_negs()
+{
+  // evtl. war es ein reconnect, dann steht in gmcp noch alter kram drin. Der
+  // muss weg, koennte ja auch sein, dass der Client (jetzt) kein GMCP
+  // mehr
+  // will.
+  gmcpdata = 0;
+
+  // Hack besonderer Sorte: GMCP soll lokal eingeschaltet sein. Auf
+  // Clientseiten ist es laut Protokoll nicht vorgesehen, daher duerfen
+  // (sollten?) wir kein DO an den Client senden. Wir brauchen aber einen
+  // remote handler, um die Wuensche vom Client zu verarbeiten. Daher erstmal
+  // nur den local handler binden (und gleichzeitig negotiation anstossen) und
+  // dann direkt danach den remote handler auch binden (ohne erneute
+  // negotiation zu starten). Achja und wir nehmen die gleiche Funktion als
+  // Handler fuer remote und lokal.
+  bind_telneg_handler(TELOPT_GMCP, 0, #'_std_re_handler_gmcp, 1);
+  bind_telneg_handler(TELOPT_GMCP, #'_std_re_handler_gmcp,
+                                   #'_std_re_handler_gmcp, 0);
+}
+
diff --git a/std/player/quests.c b/std/player/quests.c
new file mode 100644
index 0000000..aae566d
--- /dev/null
+++ b/std/player/quests.c
@@ -0,0 +1,244 @@
+// MorgenGrauen MUDlib
+//
+// player/quests.c -- quest handler
+//
+// $Id: quests.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+// Dieses Modul enhaelt die Quest-spezifischen Teile der Playerobjekte.
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player/life.h>
+#include <player/quest.h>
+#include <thing/properties.h>
+#include <player/base.h>
+#include <living/life.h>
+#undef NEED_PROTOTYPES
+
+#include "/secure/questmaster.h"
+#include <wizlevels.h>
+#include <daemon.h>
+#include <language.h>
+#include <mail.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <events.h>
+
+mixed quests;
+
+int QueryQuest(string questname);
+// local properties prototype
+static mixed _query_quests();
+static int   _query_questpoints();
+
+protected void create() {
+  Set(P_QUESTS, NOSETMETHOD, F_SET_METHOD);
+  Set(P_QUESTS, quests = ([]), F_VALUE);
+  Set(P_QUESTS, SECURED, F_MODE);
+  Set(P_QP, SAVE, F_MODE);
+  Set(P_QP, SECURED, F_MODE);
+}
+
+varargs int GiveQuest(string questname, string message) {
+  mixed *quest = QM->QueryQuest(questname);
+
+  // Questname ungueltig
+  if (!quest||!pointerp(quest)||quest==({}))
+    return GQ_KEY_INVALID;
+  // Unbefugter Zugriff auf deaktivierte Quest
+  if (!quest[6]&&!IS_ARCH(this_interactive()))
+    return GQ_IS_INACTIVE;
+  // Unbefugter Zugriff
+  if (member(quest[2], load_name(previous_object()))==-1 &&
+      !IS_ARCH(this_interactive()))
+    return GQ_ILLEGAL_OBJ;
+
+  // Gilde wird in jedem Fall informiert.
+  string guild=GUILD_DIR+QueryProp(P_GUILD);
+  if (find_object(guild) || file_size(guild+".c")>-1)
+    catch( call_other(guild, "NotifyGiveQuest", ME, questname);publish );
+
+  // Quest bereits gesetzt
+  if (QueryQuest(questname))
+    return GQ_ALREADY_SET;
+  AddExp(quest[1]);
+  quests += ([ questname : quest[0]; time() ]);
+  force_save();
+  // Event ausloesen
+  EVENTD->TriggerEvent(EVT_LIB_QUEST_SOLVED,([
+             E_OBJECT: ME,
+             E_PLNAME: getuid(ME),
+             E_ENVIRONMENT: environment(),
+             E_QUESTNAME: questname,
+             E_QP_GRANTED: quest[0] ]) );
+
+  if (message && message!="") {
+    if (message!="__silent__") {
+      message=implode(explode(message,"@@name@@"),
+          capitalize(query_real_name()));
+    }
+    else {
+      message="";
+    }
+  }
+  else
+    message=capitalize(query_real_name())
+      +" hat gerade ein Abenteuer bestanden: "+ questname+"\n";
+  if(message!="")
+    catch(QM->Channel(message);publish);
+  catch(QM->SendMail(questname, quest, ME);publish);
+  return OK;
+}
+
+int DeleteQuest(string questname) {
+  // Quest ist nicht gesetzt
+  if(!QueryQuest(questname))
+    return DQ_NOT_SET;
+
+  mixed *quest = QM->QueryQuest(questname);
+  // Questname ungueltig
+  if (!quest||!pointerp(quest)||quest==({}))
+    return DQ_KEY_INVALID;
+  // Unbefugter Zugriff
+  if (!IS_ARCH(this_interactive()))
+    return DQ_ILLEGAL_OBJ;
+  AddExp(-quest[1]);
+  m_delete(quests, questname);
+  force_save();
+  return OK;
+}
+
+int QueryQuest(string questname) {
+  int dummy;
+
+  // Gaeste haben keine Quests.
+  if( sscanf( getuid(), "gast%d", dummy ) == 1 )
+    return QQ_GUEST;
+  // Uebergebener Parameter "questname" ungueltig oder leer?
+  if(!questname || !stringp(questname) || questname == "")
+    return QQ_KEY_INVALID;
+  // Questname ist tatsaechlich in der Questliste enthalten? Alles klar!
+  if ( member(quests, questname) )
+    return OK;
+  // Ansonsten war der Name wohl ungueltig.
+  return QQ_KEY_INVALID;
+}
+
+int ModifyQuestTime(string qname, int when) {
+  if ( process_call() )
+    return -1;
+
+  // Nur EM+ oder der Tagebuchmaster duerfen die Werte aendern.
+  if ( !IS_ARCH(this_interactive()) &&
+    load_name(previous_object())!="/d/wald/leusel/quest/objs/tagebuch-master")
+    return -1;
+
+  // Questliste ist unerwartet kein Mapping.
+  if ( !mappingp(quests) )
+    return -2;
+
+  // Kein Questname angegeben, oder Spieler hat diese Quest ueberhaupt nicht
+  // geloest.
+  if ( !stringp(qname) || !member(quests, qname) )
+    return -3;
+
+  // Der Tagebuchmaster setzt Eintraege ggf. auf 0, wenn er keine Daten
+  // findet, und EM+ wollen Eintraege auf -1 setzen koennen, um das Einlesen
+  // der Daten noch einmal zu ermoeglichen, d.h. diese Werte sind zusaetzlich
+  // zu gueltigen Zeitwerten erlaubt.
+  if ( !intp(when) || when < -1 || when > time() )
+    return -4;
+
+  // Neuen Wert eintragen.
+  quests[qname,1] = when;
+  return 1;
+}
+
+int QueryQuestTime(string qname) {
+  return quests[qname,1];
+}
+
+// Konvertiert Datenstruktur von quests in ein Mapping mit folgendem Aufbau:
+// quests = ([ "questname" : Abenteuerpunkte; Zeit_des_Questabschlusses, ])
+protected void updates_after_restore(mixed newflag) {
+  // Ganz frischer Spieler? Dann keine Behandlung erforderlich.
+  if ( newflag )
+    return;
+  // Wenn die Questliste noch kein Mapping ist, Konvertierung anstossen.
+  if ( !mappingp(quests) ) {
+    // Wenn noch keine Quests eingetragen sind, Leermapping eintragen.
+    if ( !sizeof(quests) ) {
+      quests = ([:2]);
+      return;
+    }
+    // Vorsichtshalber Leereintraege rausziehen.
+    quests -= ({({})});
+    // Liste der Questzeiten aus dem Spieler auslesen. Wenn nicht vorhanden,
+    // Array mit -1-Elementen passender Laenge erzeugen.
+    // -1 an allen Stellen eintragen, wo bisher keine Daten vorliegen.
+    // Diese werden dann vom Questtagebuch durch die Daten ersetzt.
+    int *qtimes = QueryProp("questtime")||allocate(sizeof(quests), -1);
+    // Falls die Questliste laenger ist als die Zeitenliste, wird letztere
+    // um die fehlende Laenge in Form von -1-eintraegen ergaenzt, unter der
+    // Annahme, dass die fehlenden Eintraege bisher lediglich nicht
+    // eingelesen wurden. Im umgekehrten Fall werden alle Zeiten verworfen,
+    // da dieser Fall eintritt, wenn einem Spieler eine Quest ausgetragen
+    // wurde und die Listen in der Vergangenheit nicht synchron gehalten
+    // wurden.
+    if ( sizeof(qtimes) < sizeof(quests) ) {
+      qtimes += allocate(sizeof(quests)-sizeof(qtimes), -1);
+    }
+    if ( sizeof(qtimes) > sizeof(quests) ) {
+      qtimes = allocate(sizeof(quests), -1);
+    }
+    // Questdaten und Questzeiten zusammenpferchen. Ergibt folg. Mapping:
+    // temp = ([ ({Questname1, QP1}) : Questzeit, ... ])
+    mapping temp = mkmapping(quests, qtimes);
+    quests = m_allocate(sizeof(quests),2);
+    foreach(mixed qdata, int qtime : temp) {
+      quests += ([ qdata[0] : qdata[1]; qtime ]);
+    }
+    if (QueryProp("questtime")) {
+      SetProp("questtime",0);
+      Set("questtime", SAVE, F_MODE_AD);
+    }
+  }
+}
+
+// **** local property methods
+static int _query_questpoints() {
+  int qp;
+
+  if ( !mappingp(quests) || !sizeof(quests) ) {
+    return 0;
+  }
+
+  closure qqp = symbol_function("QueryQuestPoints", QM);
+  // Die aktuell gueltigen Abenteuerpunkte aus dem Questmaster holen und
+  // die Questliste damit aktualisieren. qp wird als Referenz mit uebergeben
+  // damit das Additionsergebnis auch nach dem Durchlauf ausserhalb der
+  // Closure verfuegbar ist.
+  // Falls Abenteuerpunkte < 0 existieren, wird die entsprechende Quest
+  // aus der Liste ausgetragen.
+  walk_mapping(quests,
+    function void (string qname, int qpoints, int qtime, int sum)
+    {
+      qpoints = funcall(qqp, qname);
+      if (qpoints<0)
+        m_delete(quests,qname);
+      else
+        sum += qpoints;
+    }, &qp);
+
+  Set(P_QP, qp);
+  return qp;
+}
+
+static mixed _query_quests() {
+  return copy(quests);
+}
diff --git a/std/player/reputation.c b/std/player/reputation.c
new file mode 100644
index 0000000..9fb3d17
--- /dev/null
+++ b/std/player/reputation.c
@@ -0,0 +1,73 @@
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <reputation.h>
+
+private mapping reputations = ([ ]);
+
+/*
+ * Argumente:
+ * repid = Reputation-ID im Repmaster
+ * value = Wert um den die Reputation geaendert werden soll,
+ *         positiv oder negativ
+ * silent = Keine Std-Meldung ausgeben
+ *
+ * Return:
+ * REP_RET_WRONGARGS = Falsche Argumente
+ * REP_RET_INACTIVE = Rep inaktiv (kann derzeit nicht geaendert werden)
+ * REP_RET_INVALIDUID = Unzulaessie UID
+ * REP_RET_ALREADYMAX = Rep bereits maximum / minimum
+ * REP_RET_INVALIDREP = Reputation nicht vorhanden
+ *
+ * REP_RET_SUCCESS = Reputation wurde veraendert
+ * REP_RET_SUCCESSCUT = Reputation wurde auf Min / Max veraendert
+ */
+public varargs int ChangeReputation(string repid, int value, 
+                                          int silent) {
+  string uid, changemsg; int newval; mapping rep;
+  
+  if(!intp(value) || !value || !stringp(repid) || !sizeof(repid)) 
+    return REP_RET_WRONGARGS;
+  if(!mappingp(rep = REPMASTER->GetReputationData(repid)))
+    return REP_RET_INVALIDREP;
+  if(!(rep["flags"] & REP_FLAG_ACTIVE))
+    return REP_RET_INACTIVE;
+  if(REPMASTER->CheckValidUid(repid, previous_object()) < 1)
+    return REP_RET_INVALIDUID;
+  if(reputations[repid] >= REP_MAXIMUM || reputations[repid] <= REP_MINIMUM) 
+    return REP_RET_ALREADYMAX;
+
+  if(reputations[repid] + value > REP_MAXIMUM) 
+    newval = reputations[repid] + value - REP_MAXIMUM;
+  else if(reputations[repid] - value < REP_MINIMUM)
+    newval = reputations[repid] + value + REP_MINIMUM;
+
+  if(!silent &&
+     stringp(changemsg = REPMASTER->GetDefaultChangeMsg(repid, 
+       newval || value)))
+    tell_object(this_object(), changemsg);
+
+  reputations[repid] += newval || value;
+
+  return newval ? REP_RET_SUCCESSCUT : REP_RET_SUCCESS;
+}
+
+/*
+ * Argumente:
+ * repid = Reputation-ID im Repmaster
+ *
+ * Return:
+ * 0 = Reputation noch nicht veraendert / enthalten 
+ * !0 = Reputationswert
+ */
+public int GetReputation(string repid) { return reputations[repid]; }
+
+/*
+ * Return:
+ * Mappingkopie aller gespeicherten Reputationswert
+ */
+public mapping GetReputations() { return copy(reputations); }
diff --git a/std/player/restrictions.c b/std/player/restrictions.c
new file mode 100644
index 0000000..d71e14c
--- /dev/null
+++ b/std/player/restrictions.c
@@ -0,0 +1,130 @@
+// MorgenGrauen MUDlib
+//
+// player/restrictions.c -- container aspect of players
+//
+// $Id: restrictions.c 9020 2015-01-10 21:49:41Z Zesstra $
+
+// This is a simple container to put objects in. It defines all functions
+// which are necessary to describe an object which can be filled with
+// other things.
+//
+// It will support restrictions for volume, weight etc.
+//
+// The following properties are defined:
+// P_MAX_WEIGHT - maximum weight which container can carry
+// P_WEIGHT_CONTENTS - current contents
+// P_WEIGHT - builtin property: read->total weight, write->own weight
+//
+// Functions for manipulation of weight
+// MayAddWeight(weight) - Can <weight> be inserted?
+// AddWeight(weight) - Add an amount of <weight>
+//
+// IMPORTANT: unit should be interpreted as grams (g).
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/restrictions";
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <hook.h>
+#include <living/skills.h>
+#include <attributes.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <wizlevels.h>
+#include <container.h>
+#include <defines.h>
+#include <new_skills.h>
+
+//Liste von Objekten, in denen InsertNotify() gerufen wird, wenn etwas in den
+//Spieler bewegt wurde.
+nosave object *InsertHooks=({});
+
+// local properties prototypes
+static int _query_max_weight();
+static mixed _set_frog(mixed arg);
+
+void create()
+{
+  ::create();
+
+  Set(P_MAX_WEIGHT, NOSETMETHOD, F_SET_METHOD);
+  Set(P_MAX_WEIGHT, SECURED, F_MODE);
+  offerHook(H_HOOK_INSERT, 1);
+}
+
+// **** local property methods
+static int _query_max_weight() {
+  int str,val;
+  mixed ski;
+  
+  if (QueryProp(P_GHOST) && !IS_WIZARD(ME))
+    return 0;
+  str=QueryAttribute(A_STR);
+  ski = UseSkill(SK_CARRY, ([SI_SKILLARG : str ]));
+
+  if (!intp(ski))
+    ski = 0;
+  
+  if (str<0) {
+    val=9200+str*160+(int)ski;
+    if (val<3000) val=3000;
+    return val;
+  }
+  val = 9200+str*800+(int)ski;
+  if (val<3000)
+    val = 3000;
+  return val;
+}
+
+static mixed _set_frog(mixed arg) {
+  mixed res;
+  
+  res=Set(P_FROG,arg);
+  if (res)
+    SetProp(P_ATTRIBUTES_MODIFIER,({"#frosch",([A_STR:-30])}));
+  else
+    SetProp(P_ATTRIBUTES_MODIFIER,({"#frosch",0 }));
+  return res;
+}
+
+public void NotifyInsert(object ob, object oldenv)
+{
+  ::NotifyInsert(ob, oldenv);
+  // Alle Listener im neuen Hooksystem vom InsertHook informieren
+  HookFlow(H_HOOK_INSERT, ob);
+  // Alle Listener im alten InsertHook informieren
+  if (sizeof(InsertHooks))
+  {
+    foreach(object h: &InsertHooks)
+    {
+      if (h && environment(h) == ME)
+        h->InsertNotify(ob);
+      else
+        h=0;
+    }
+    InsertHooks-=({0}); // genullte Elemente loeschen
+  }
+}
+
+void AddInsertHook(object ob)
+{
+  if (member(InsertHooks,ob)!=-1 || environment(ob)!=this_object())
+    return;
+  InsertHooks+=({ob});
+}
+
+void RemoveInsertHook(object ob)
+{
+  InsertHooks-=({ob});
+}
+
+object *QueryInsertHooks()
+{
+  return InsertHooks;
+}
+
diff --git a/std/player/shadows/ark_hunger_shadow.c b/std/player/shadows/ark_hunger_shadow.c
new file mode 100644
index 0000000..42d6dff
--- /dev/null
+++ b/std/player/shadows/ark_hunger_shadow.c
@@ -0,0 +1,54 @@
+/* Der Shadow fuer den Hungerfluch */
+/* /d/ebene/ark/wolf/obj/hunger.c  */
+/* Shadow: /std/player/shadows/ark_hunger_shadow.c */
+/* Original: /d/ebene/ark/wolf/obj/hunger_obj.c */
+
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <properties.h>
+
+#define BS break_string
+
+object spieler;
+
+void Setzen(object sp)
+{
+ if (!objectp(sp) || !interactive(sp)) return destruct(this_object());
+ spieler=sp;
+ if (!shadow(sp,1)) destruct(this_object());
+}
+
+int _query_food() { return 0; }
+
+int _query_max_food() { return 0; }
+
+int eat_food(int strength, int testonly)
+{
+ if (strength==0) return 1;
+ if (strength>0)
+ {
+  strength=0;
+  if (spieler)
+  {
+    tell_object(spieler, BS("Du hast zwar einen tierischen Hunger, doch irgendwas sagt Dir, dass Du das, was "+
+                            "Du gerade essen willst, nicht mehr in Deinen Magen bekommst. Das ist ziemlich "+
+                            "gefaehrlich, hoffentlich verhungerst Du nicht !", 78));
+    return 0;
+  }
+ }
+ else
+ {
+  if (spieler)
+  {
+   tell_object(spieler, BS("Dein Hunger vertieft sich so nur, Du leidest Hoellenqualen.", 78));
+   spieler->reduce_hit_points(strength);
+  }
+  return 0;
+ }
+}
+
+void Loeschen() {
+    unshadow();
+    destruct(this_object());
+}
diff --git a/std/player/shadows/block_shadow.c b/std/player/shadows/block_shadow.c
new file mode 100644
index 0000000..f7a4333
--- /dev/null
+++ b/std/player/shadows/block_shadow.c
@@ -0,0 +1,47 @@
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+
+private nosave object pl;
+
+void create()
+{
+  if( IS_BLUE(ME) ) return;
+  shadow( PL, 1);
+  pl = PL;
+}
+
+int
+AddExp(int ep)
+{
+  object block;
+  int diff, lim;
+
+  block = present("\n block", pl);
+  lim = 30 + random(10);
+
+  if ( ep > lim &&				      // Mindestwert
+       previous_object() &&
+       ( previous_object() == pl ||		      // zB. GiveQuest()
+	 ( living(previous_object()) && 	      // Oder NPCs
+	  !query_once_interactive(previous_object())
+	 )
+       )
+     )
+  {
+    diff = block->Gutschreiben(ep-lim);
+    return pl->AddExp(lim+diff);
+  }
+  return pl->AddExp(ep);
+}
+
+void
+SeherHatGenug()
+{
+  unshadow();
+  destruct(this_object());
+}
+
diff --git a/std/player/shadows/morph_shadow.c b/std/player/shadows/morph_shadow.c
new file mode 100644
index 0000000..afc97c4
--- /dev/null
+++ b/std/player/shadows/morph_shadow.c
@@ -0,0 +1,598 @@
+/* -*- lpc -*- */
+//--------------------------------------------------------------------------
+//
+//   morph.c
+//
+//   (c) Troy (troy@mg.mud.de)
+//   Kopieren, Veraendern oder Weitergabe: na klar, immer zu, je schlimmer 
+//   um so besser
+//
+//   Objekt erstellt: 14.08.01, Troy
+//
+//   Dieser shadow implementiert generische Verwandlungen. Im Gegensatz oder
+//   in Ergänzung zum Tarnhelm sind diese nicht auf die Beschreibung
+//   beschränkt, sondern schlagen sich auch in anderen Properties nieder.
+//
+//--------------------------------------------------------------------------
+
+#include <moving.h>
+#include <properties.h>
+#include <wizlevels.h>
+
+//--------------------------------------------------------------------------
+
+#pragma strong_types,save_types
+
+//--------------------------------------------------------------------------
+
+varargs int remove( int silent );
+
+//--------------------------------------------------------------------------
+//
+//   Property-Einstellungen je nach Rasse
+//
+//--------------------------------------------------------------------------
+
+private mapping morph_properties;
+
+private object pl; // der schattierte Spieler
+
+//--------------------------------------------------------------------------
+//
+//   start_shadow( Spieler, Properties )
+//
+//   Startet das Shadowing von Spieler. Properties ist ein Mapping mit
+//   den zu ändernden Properties. Dort nicht vorhandene Properties werden
+//   zum Spieler durchgereicht. Es werden dort entweder einzelne Werte
+//   erwartet (Beispiel: ([ P_GENDER: MALE ])), die dann für alle Rassen
+//   gelten, oder closures, die dann ausgeführt werden unter Übergabe der
+//   Spielerrasse als Parameter oder aber Mappings mit den Rassennamen
+//   (Beispiel: ([ P_GENDER: ([ "Mensch": NEUTER, "Elf": FEMALE,
+//   "Zwerg": MALE, ... ]) ])). Ist eine Rasse in dem Rassenmapping nicht
+//   vorhanden, so wird das Property zum Spieler durchgereicht. Speziell
+//   behandelt werden P_IDS (siehe _query_ids()) und P_NAME (siehe
+//   _query_name()). 
+//
+//--------------------------------------------------------------------------
+int start_shadow( object _pl, mapping preset )
+{
+  if ( !clonep( this_object() ) )
+    return 1;
+  if ( !_pl || !query_once_interactive( _pl ) )
+    return remove();
+  pl = _pl;
+  morph_properties = deep_copy( preset );
+  shadow( pl, 1 );
+  return 1;
+}
+
+//--------------------------------------------------------------------------
+//
+//   stop_shadow()
+//
+//   Beendet das Shadowing und zerstört dieses Objekt
+//
+//--------------------------------------------------------------------------
+int stop_shadow( /* void */ )
+{
+  if ( !clonep( this_object() ) )
+    return 0;
+  unshadow();
+  return remove();
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_property( Property )
+//
+//   Generische Property-Maskierung. liefert aus morph_properties den zur
+//   Rasse des Trägers passenden Eintrag.
+//
+//--------------------------------------------------------------------------
+nomask static mixed _query_property( string prop )
+{
+  string race;
+
+  // Rasse holen.
+  if ( IS_LEARNER(pl) )
+    race = "Magier";
+  else // _query_race() ist notwendig, um closures zu umgehen.
+    race = pl->_query_race();
+
+  if ( member( morph_properties, prop ) == 0 )
+    return pl->Query( prop );
+  if ( closurep( morph_properties[ prop ] ) )
+    return funcall( morph_properties[ prop ], race );
+  if ( mappingp( morph_properties[ prop ] ) )
+  {
+    if ( member( morph_properties[ prop ], race ) == 0 )
+      return pl->Query( prop );
+    if ( closurep( morph_properties[ prop ][ race ] ) )
+      return funcall( morph_properties[ prop ][ race ] );
+
+    return morph_properties[ prop ][ race ];
+  }
+  return morph_properties[ prop ];
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_article()
+//
+//   Property-Maskierung für P_ARTICLE
+//
+//--------------------------------------------------------------------------
+int _query_article( /* void */ )
+{
+  return (int)_query_property( P_ARTICLE );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_average_size()
+//
+//   Property-Maskierung für P_AVERAGE_SIZE
+//
+//--------------------------------------------------------------------------
+int _query_average_size( /* void */ )
+{
+  return (int)_query_property( P_AVERAGE_SIZE );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_average_weight()
+//
+//   Property-Maskierung für P_AVERAGE_WEIGHT
+//
+//--------------------------------------------------------------------------
+int _query_average_weight( /* void */ )
+{
+  return (int)_query_property( P_AVERAGE_WEIGHT );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_body()
+//
+//   Property-Maskierung für P_BODY
+//
+//--------------------------------------------------------------------------
+int _query_body( /* void */ )
+{
+  return (int)_query_property( P_BODY );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_gender()
+//
+//   Property-Maskierung für P_GENDER
+//
+//--------------------------------------------------------------------------
+int _query_gender( /* void */ )
+{
+  return (int)_query_property( P_GENDER );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_hands()
+//
+//   Property-Maskierung für P_HANDS
+//
+//--------------------------------------------------------------------------
+mixed _query_hands( /* void */ )
+{
+  return _query_property( P_HANDS );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_ids()
+//
+//   Property-Maskierung für P_IDS - Nicht-Standard, da je nach Ursprungs-
+//   geschlecht des Spielers zusätzliche ids fällig werden. Ablauf: Es gibt
+//   5 Schritte, bei deren jeweiligem Versagen die ids des Spielers durch-
+//   gereicht werden:
+//   1. P_IDS ist im property-mapping vorhanden
+//   2. a) es ist ein string oder ein array: es wird an die ids des Spielers
+//         angehängt und zurückgegeben.
+//      b) es ist eine closure. Diese wird ausgeführt unter Übergabe der
+//         Spieler-ids, der Rasse des Spielers und der Geschlechter
+//         (dieses Objekts und des Spielers). Der Ergebniswert der closure
+//         wird direkt zurückgegeben.
+//      c) es ist ein mapping. Hier nehmen wir nun das übliche Rassennamen-
+//         mapping an -> 3.)
+//   3. Für die Rasse des Spielers wird ein Eintrag gesucht
+//      a) er ist ein string oder ein array: er wird an die ids des Spielers
+//         angehängt und zurückgegeben.
+//      b) er ist eine closure. Diese wird ausgeführt unter Übergabe der
+//         Spieler-ids, der Rasse des Spielers und der Geschlechter
+//         (dieses Objekts und des Spielers). Der Ergebniswert der closure
+//         wird direkt zurückgegeben.
+//      c) er ist ein mapping. Es wird angenommen, dass je Geschlecht DIESES
+//         Objekts ein Eintrag vorhanden ist. -> 4.)
+//   4. Für das Geschlecht dieses Objekts wird ein Eintrag gesucht
+//      a) er ist ein string oder ein array: er wird an die ids des Spielers
+//         angehängt und zurückgegeben.
+//      b) er ist eine closure. Diese wird ausgeführt unter Übergabe der
+//         Spieler-ids, der Rasse des Spielers und der Geschlechter
+//         (dieses Objekts und des Spielers). Der Ergebniswert der closure
+//         wird direkt zurückgegeben.
+//      c) er ist ein mapping. Es wird angenommen, dass je Geschlecht DES
+//         Spielers ein Eintrag vorhanden ist. -> 5.)
+//   5. Für das Geschlecht des Spielers wird ein Eintrag gesucht
+//      a) er ist ein string oder ein array: er wird an die ids des Spielers
+//         angehängt und zurückgegeben.
+//      b) er ist eine closure. Diese wird ausgeführt unter Übergabe der
+//         Spieler-ids, der Rasse des Spielers und der Geschlechter
+//         (dieses Objekts und des Spielers). Der Ergebniswert der closure
+//         wird direkt zurückgegeben.
+//
+//--------------------------------------------------------------------------
+mixed _query_ids( /* void */ )
+{
+  string race;
+  mixed ids;
+  int gender, sgender;
+
+  // Test 1.
+  ids = pl->Query( P_IDS );
+  if ( member( morph_properties, P_IDS ) == 0 )
+    return ids;
+
+  // Rasse holen.
+  if ( IS_LEARNER(pl) )
+    race = "Magier";
+  else // _query_race() ist notwendig, um closures zu umgehen.
+    race = pl->_query_race();
+
+  // Geschlechter holen
+  gender = _query_gender();
+  sgender = pl->Query( P_GENDER );
+
+  // Test 2.
+  // string? Dann einfach den normalen ids dazu, genauso mit array
+  if ( stringp( morph_properties[ P_IDS ] ) )
+    return ids + ({ morph_properties[ P_IDS ] });
+  if ( pointerp( morph_properties[ P_IDS ] ) )
+    return ids + morph_properties[ P_IDS ];
+  if ( closurep( morph_properties[ P_IDS ] ) )
+    return funcall( morph_properties[ P_IDS ], ids, race, gender, sgender );
+  // falls kein mapping, dann raus
+  if ( !mappingp( morph_properties[ P_IDS ] ) )
+    return ids;
+
+  // Test 3.
+  if ( member( morph_properties[ P_IDS ], race ) == 0 )
+    return ids;
+  if ( stringp( morph_properties[ P_IDS ][ race ] ) )
+    return ids + ({ morph_properties[ P_IDS ][ race ] });
+  if ( pointerp( morph_properties[ P_IDS ][ race ] ) )
+    return ids + morph_properties[ P_IDS ][ race ];
+  if ( closurep( morph_properties[ P_IDS ][ race ] ) )
+    return funcall( morph_properties[ P_IDS ][ race ], ids, race, gender, sgender );
+  // falls kein mapping, dann raus
+  if ( !mappingp( morph_properties[ P_IDS ][ race ] ) )
+    return ids;
+
+  // Test 4.
+  if ( member( morph_properties[ P_IDS ][ race ], gender ) == 0 )
+    return ids;
+  if ( stringp( morph_properties[ P_IDS ][ race ][ gender ] ) )
+    return ids + ({ morph_properties[ P_IDS ][ race ][ gender ] });
+  if ( pointerp( morph_properties[ P_IDS ][ race ][ gender ] ) )
+    return ids + morph_properties[ P_IDS ][ race ][ gender ];
+  if ( closurep( morph_properties[ P_IDS ][ race ][ gender ] ) )
+    return funcall( morph_properties[ P_IDS ][ race ][ gender ],
+		    ids, race, gender, sgender );
+  // falls kein mapping, dann raus
+  if ( !mappingp( morph_properties[ P_IDS ][ race ][ gender ] ) )
+    return ids;
+
+  // Test 5.
+  if ( member( morph_properties[ P_IDS ][ race ][ gender ], sgender ) == 0 )
+    return ids;
+  if ( stringp( morph_properties[ P_IDS ][ race ][ gender ][ sgender ] ) )
+    return ids + ({ morph_properties[ P_IDS ][ race ][ gender ][ sgender ] });
+  if ( pointerp( morph_properties[ P_IDS ][ race ][ gender ][ sgender ] ) )
+    return ids + morph_properties[ P_IDS ][ race ][ gender ][ sgender ];
+  if ( closurep( morph_properties[ P_IDS ][ race ][ gender ][ sgender ] ) )
+    return funcall( morph_properties[ P_IDS ][ race ][ gender ][ sgender ],
+		    ids, race, gender, sgender );
+
+  return ids;
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_is_morphed()
+//
+//   Property-Methode für "is_morphed"
+//
+//--------------------------------------------------------------------------
+int _query_is_morphed( /* void */ )
+{
+  return 1;
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_max_hands()
+//
+//   Property-Maskierung für P_MAX_HANDS
+//
+//--------------------------------------------------------------------------
+int _query_max_hands( /* void */ )
+{
+  return (int)_query_property( P_MAX_HANDS );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_mmsgin()
+//
+//   Property-Maskierung für P_MMSGIN
+//
+//--------------------------------------------------------------------------
+string _query_mmsgin( /* void */ )
+{
+  return (string)(_query_property( P_MMSGIN ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_mmsgout()
+//
+//   Property-Maskierung für P_MMSGOUT
+//
+//--------------------------------------------------------------------------
+string _query_mmsgout( /* void */ )
+{
+  return (string)(_query_property( P_MMSGOUT ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_msgin()
+//
+//   Property-Maskierung für P_MSGIN
+//
+//--------------------------------------------------------------------------
+string _query_msgin( /* void */ )
+{
+  return (string)(_query_property( P_MSGIN ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_msgout()
+//
+//   Property-Maskierung für P_MSGOUT
+//
+//--------------------------------------------------------------------------
+string _query_msgout( /* void */ )
+{
+  return (string)(_query_property( P_MSGOUT ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_name()
+//
+//   Property-Methode für P_NAME. Leider ist die player-shell so grottig,
+//   dass überall angenommen wird, QueryProp(P_NAME) liefere einen String :-|
+//   Vollständiges Property daher unter _query_name_full().
+//
+//--------------------------------------------------------------------------
+string _query_name( /* void */ )
+{
+  mixed prop;
+  prop = _query_property( P_NAME );
+  if ( stringp( prop ) )
+    return sprintf( prop, pl->Query( P_NAME ) );
+  if ( pointerp( prop ) )
+    return map( prop,
+		      lambda( ({ 'el, 's }),
+			      ({#'sprintf, 'el, 's}) ),
+		      pl->Query( P_NAME ) )[ WER ];
+  return pl->Query( P_NAME );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_name_full()
+//
+//   Property-Methode für "name_full".
+//
+//--------------------------------------------------------------------------
+mixed _query_name_full( /* void */ )
+{
+  mixed prop;
+  prop = _query_property( P_NAME );
+  if ( stringp( prop ) )
+    return sprintf( prop, pl->Query( P_NAME ) );
+  if ( pointerp( prop ) )
+    return map( prop,
+		      lambda( ({ 'el, 's }),
+			      ({#'sprintf, 'el, 's}) ),
+		      pl->Query( P_NAME ) );
+  return pl->Query( P_NAME );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_presay()
+//
+//   Property-Maskierung für P_PRESAY
+//
+//--------------------------------------------------------------------------
+string _query_presay( /* void */ )
+{
+  return (string)(_query_property( P_PRESAY ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_race()
+//
+//   Property-Maskierung für P_RACE
+//
+//--------------------------------------------------------------------------
+string _query_race( /* void */ )
+{
+  return (string)(_query_property( P_RACE ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_racestring()
+//
+//   Property-Maskierung für P_RACESTRING
+//
+//--------------------------------------------------------------------------
+string* _query_racestring( /* void */ )
+{
+  return (string*)_query_property( P_RACESTRING );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_size()
+//
+//   Property-Maskierung für P_SIZE
+//
+//--------------------------------------------------------------------------
+int _query_size( /* void */ )
+{
+  return (int)_query_property( P_SIZE );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_title()
+//
+//   Property-Maskierung für P_TITLE
+//
+//--------------------------------------------------------------------------
+string _query_title( /* void */ )
+{
+  return (string)(_query_property( P_TITLE ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_weight()
+//
+//   Property-Maskierung für P_WEIGHT
+//
+//--------------------------------------------------------------------------
+int _query_weight( /* void */ )
+{
+  return (int)(_query_property( P_WEIGHT ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   id( Text, Level)
+//
+//   Die Identifizierung spinnt mit P_NAME-Arrays
+//
+//--------------------------------------------------------------------------
+varargs int id( string str, int lvl )
+{
+  string plname;
+
+  if ( pl->QueryProp( P_GHOST ) )
+    if ( str == "geist" )
+      return 1;
+    else if ( ( sscanf( str, "geist von %s", plname ) == 1 ) &&
+	      pl->id( plname ) )
+      return 1;
+    else if ( ( sscanf( str, "geist %s", plname ) == 1 ) &&
+	      pl->id( plname ) )
+      return 1;
+
+  return pl->id( str, lvl );
+}
+
+//--------------------------------------------------------------------------
+//
+//   long()
+//
+//   Die Langbeschreibung im Spieler hadert mit dem Geschlecht NEUTER...
+//
+//--------------------------------------------------------------------------
+varargs string long( /* void */ )
+{
+  string slong;
+  slong = pl->long();
+
+  if ( _query_gender() == NEUTER )
+  {
+    string *along;
+    int i;
+
+    // alle Er-s und er-s suchen...
+    along = regexplode( slong, "\\<[Ee]r\\>" );
+    // ... und das r durch ein s ersetzen.
+    for ( i = 1 ; i < sizeof( along ) ; i += 2 )
+      along[ i ][ 1 ] = 's';
+    slong = implode( along, "" );
+  }
+
+  return slong;
+}
+
+//--------------------------------------------------------------------------
+//
+//   short()
+//
+//   Die Kurzbeschreibung im Spieler hat ein Problem mit P_NAME-Arrays
+//
+//--------------------------------------------------------------------------
+string short( /* void */ )
+{
+  mixed names;
+  string answer;
+  string title;
+
+  if ( pl->QueryProp( P_INVIS ) )
+    if ( interactive( previous_object() ) &&
+	 IS_LEARNING( previous_object() ) )
+      return "(" + pl->Query( P_NAME ) + ") \n";
+    else
+      return (string)0;
+
+  names = _query_name_full();
+  if ( stringp( names ) )
+    names = ({ names, names, names, names });
+
+  if ( pl->QueryProp( P_GHOST ) )
+    answer = "Der Geist " + pl->QueryArticle( WESSEN, 0 ) + names[ WESSEN ];
+  else
+    answer = pl->QueryArticle( WER, 0 ) + names[ WER ];
+  if ( ( title = pl->QueryProp( P_TITLE ) ) &&
+       ( title != "" ) )
+     answer += " " + title;
+  if ( !interactive( pl ) )
+     answer += " (netztot)";
+  return capitalize( answer ) + ".\n";
+}
+
+//--------------------------------------------------------------------------
+//
+//   remove( Schnauze )
+//
+//   aufräumen
+//
+//--------------------------------------------------------------------------
+varargs int remove( int silent )
+{
+  destruct( this_object() );
+  return 1;
+}
+
+//--------------------------------------------------------------------------
+//   -- ENDE --
diff --git a/std/player/shadows/pony.c b/std/player/shadows/pony.c
new file mode 100644
index 0000000..8d3de8d
--- /dev/null
+++ b/std/player/shadows/pony.c
@@ -0,0 +1,59 @@
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+
+// 
+// Da der Shadow sich nicht im Player befindet, wird ein
+// Zusatzobjekt benoetigt.
+// 7.10.97 - Die Hook sollte in den Verzeichnissen der Benniequest
+//           zu finden sein. - Rumata
+
+#define HOOK "d/wald/kassandra/bennie/obj/pony_hook"
+
+static object pl;
+
+void create()
+{
+ if( IS_BLUE(ME) ) return;
+  shadow( PL, 1);
+ pl=PL;
+ //tell_object(this_player(),"Du besteigst das Pony samt Schatten.\n");
+  clone_object( HOOK ); // Steuerung des shadows ueber die hook
+}
+
+string _query_title()
+{ 
+ if( pl->QueryProp(P_GENDER) == FEMALE )
+     return "die Ponyreiterin";
+ return "der Ponyreiter";
+}
+
+string _query_msgin() { return "reitet herein"; }
+string _query_msgout() { return "reitet"; }
+
+varargs int move(mixed dest, int method, string dir, string textout,string textin)
+{
+ if( !(method & M_NOCHECK) && dest->QueryProp(P_INDOORS) )
+ {
+  write( "Das Pony weigert sich, dorthin zu traben.\n" );
+  return 1;
+ }
+ return pl->move( dest, method, dir, textout, textin );
+}
+
+int _inventory(string str)
+{
+ if( !environment() || set_light(0)>0 )
+  write( "Du sitzt auf einem Pony.\n" );
+ return pl->_inventory(str);
+}
+
+void absteigen() {
+  unshadow();
+  destruct(ME);
+}
+
+int QueryReiter() { return 1; }
diff --git a/std/player/shadows/sizetest.c b/std/player/shadows/sizetest.c
new file mode 100644
index 0000000..1268911
--- /dev/null
+++ b/std/player/shadows/sizetest.c
@@ -0,0 +1,57 @@
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+
+#define SPIELER "nefis"
+#define DEBUG(x)  if (find_player("zesstra"))\
+            tell_object(find_player("zesstra"),\
+		                      "Nefis P_SIZE: "+x+"\n")
+#define LOG(x) log_file("zesstra/P_SIZE.log", x)
+
+private nosave object pl;
+
+void create()
+{
+  if (!clonep() ) return;
+  pl = find_player(SPIELER) || find_netdead(SPIELER);
+  if (!objectp(pl)) {
+      DEBUG("Playerobjekt nicht gefunden.");
+      destruct(ME);
+      return;
+  }
+  shadow( pl, 1);
+  DEBUG(sprintf("%O shadowed by: %O\n", pl, shadow(pl, 0)));
+}
+
+int _set_size(int sz) {
+  LOG(sprintf("[%s] Aenderung von P_SIZE in %O auf %O,\nCallerstack: %O\n\n",
+	strftime("%c",time()), pl, sz, caller_stack()));
+  return pl->Set(P_SIZE, sz, F_VALUE);
+}
+
+public varargs mixed Set( string name, mixed Value, int Type, int extern ) {
+  if (name == P_SIZE) {
+  LOG(sprintf("[%s] Aenderung von P_SIZE in %O auf %O (Typ: %O) [Set()!]"
+	",\nCallerstack: %O\n\n",
+	        strftime("%c",time()), pl, Value, Type, caller_stack()));
+  }
+  return pl->Set(name, Value, Type, extern);
+}
+
+int stop() {
+  unshadow();
+  destruct(ME);
+  return 1;
+}
+
+void reset() {
+  if (!objectp(pl)) 
+    stop();
+}
+
+int remove() {
+  stop();
+  return 1;
+}
+
diff --git a/std/player/shadows/tarnhelm_shadow.c b/std/player/shadows/tarnhelm_shadow.c
new file mode 100644
index 0000000..63ea744
--- /dev/null
+++ b/std/player/shadows/tarnhelm_shadow.c
@@ -0,0 +1,140 @@
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <properties.h>
+
+#define WEG() destruct(this_object())
+
+object player;
+int gender;
+string desc;
+
+void _tarn_turn_on(object pl,string txt,int gen)
+{
+  if (!objectp(pl)||!interactive(pl)) return WEG();
+  if (!stringp(txt)||txt=="") return WEG();
+  if (gen<0||gen>2) return WEG();
+  player=pl;
+  desc=capitalize(txt);
+  gender=gen;
+  shadow(pl,1);
+}
+
+int special_verb()
+{
+  string verb;
+  
+  verb=query_verb();
+  if (!verb||verb=="") return 0;
+  if (verb[0]=='-') return 1;
+  if (verb=="ruf"||verb=="rufe"||verb=="teile"||verb=="teil"||verb=="mruf"||
+      verb=="mrufe"||verb=="erzaehl"||verb=="erzaehle") return 1;
+  return(0); // non-void funktion, Zesstra
+}
+
+int _query_gender()
+{
+  return gender;
+}
+
+string _query_name()
+{
+  if (!special_verb())
+    return capitalize(desc);
+  return player->Query(P_NAME);
+}
+
+string _query_short()
+{
+  return capitalize(player->name());
+}
+
+string _query_long()
+{
+  string str;
+
+  str=player->name();
+  return capitalize(str)+" ist "+str+" ist "+str+".\n";
+}
+
+string* _query_ids()
+{
+  return player->Query("ids")+({lower_case(desc)});
+}
+
+string _query_race()
+{
+  return desc;
+}
+
+void _tarn_turn_off()
+{
+  unshadow();
+  destruct(this_object());
+}
+
+int _query_article()
+{
+  if (!special_verb())
+    return 1;
+  return(0); // non-void funktion, Zesstra
+}
+
+string _query_presay()
+{
+  return "";
+}
+
+string _query_title()
+{
+  return "";
+}
+
+void Defend(int dam,mixed dam_type,mixed spell,object enemy)
+{
+  object o;
+
+  if (!query_once_interactive(previous_object()))
+    player->Defend(dam, dam_type, spell, enemy);
+  else
+    previous_object()->StopHuntFor(player);
+  if ((o=present("\ntarnhelm",player)))
+        o->DoUnwear();
+  if (this_object()) destruct(this_object());
+}
+
+int Kill(object ob)
+{
+  object o;
+
+  if ((o=present("\ntarnhelm",player)))
+      o->DoUnwear();
+  if (this_object()) destruct(this_object());
+  return(0); // non-void funktion, Zesstra
+}
+
+int InsertEnemy(object ob)
+{
+  object o;
+
+  if (!query_once_interactive(ob))
+    player->InsertEnemy(ob);
+  else
+    ob->StopHuntFor(player);
+  if ((o=present("\ntarnhelm",player)))
+      o->DoUnwear();
+  if (this_object()) destruct(this_object());
+  return 0;
+}
+
+string short()
+{
+  if (old_explode(object_name(previous_object()),"#")[0]=="/obj/werliste")
+    return capitalize(geteuid(player)+" verkleidet als "+player->short());
+  return player->short();
+}
+
+string QueryDisguise()
+{
+  return desc; 
+}
diff --git a/std/player/shadows/zaubersh.c b/std/player/shadows/zaubersh.c
new file mode 100644
index 0000000..a19cecc
--- /dev/null
+++ b/std/player/shadows/zaubersh.c
@@ -0,0 +1,220 @@
+#pragma strong_types,save_types
+
+#include <properties.h>
+
+object caster;
+string*ids,name;
+int gender,article,plural;
+string lgdesc;
+string min,mout,mmin,mmout;
+mixed hands;
+
+void Initialize(object _caster,
+                string*_ids,
+                string _name,
+                int _gender,
+                int _article,
+                int _plural)
+{ if(!objectp(_caster)    ||
+     !interactive(_caster)||
+     !stringp(_name)      ||
+     _name==""            ||
+     _gender<0            ||
+     _gender>2)
+  { destruct(this_object());
+    return;
+  }
+  caster=_caster;
+  ids=_ids;
+  name=_name;
+  gender=_gender;
+  article=_article;
+  plural=_plural;
+  lgdesc=0;
+  shadow(caster,1);
+}
+
+void SetLongDesc(string txt)
+{ if(!stringp(txt)||txt=="")
+    return;
+  lgdesc=txt;
+}
+
+string* _query_ids()
+{ return caster->Query(P_IDS)+ids;
+}
+
+// nicht alle Verben mit veraendertem Aussehen
+int special_verb()
+{ string verb;
+  verb=query_verb();
+  if(!stringp(verb)||verb=="")
+    return 0;
+  if(verb[0]=='-')
+    return 1;
+  if(member(({"ruf","rufe","mruf","mrufe",
+              "teil","teile","erzaehl","erzaehle"}),verb)!=-1)
+    return 1;
+  return 0;
+}
+
+string _query_name()
+{ if(!special_verb())
+    return name;
+  return capitalize(caster->Query(P_NAME));
+}
+
+string _query_short()
+{ if(!special_verb())
+    return caster->Name();
+  return caster->Query(P_SHORT);
+}
+
+string _query_long()
+{ if(!special_verb())
+  { string str;
+    if(lgdesc)
+      return lgdesc;
+    str=caster->name();
+    return break_string(capitalize(str)+" ist "+str+" ist "+str+".",78);
+  }
+  return caster->Query(P_LONG);
+}
+
+int _query_gender()
+{ if(!special_verb())
+    return gender;
+  return caster->Query(P_GENDER);
+}
+
+int _query_article()
+{ if(!special_verb())
+    return article;
+  return caster->Query(P_ARTICLE);
+}
+
+int _query_plural()
+{ if(!special_verb())
+    return plural;
+  return caster->Query(P_PLURAL);
+}
+
+string _query_race()
+{ if(!special_verb())
+    return name;
+  return caster->Query(P_RACE);
+}
+
+varargs int remove(int silent) {
+  unshadow();
+  destruct(this_object());
+  return 1;
+}
+
+void stop_shadow()
+{ 
+  remove();
+}
+
+string _query_presay()
+{ return"";
+}
+
+string _query_title()
+{ return"";
+}
+
+string _set_msgin(string val)
+{ return min=val;
+}
+
+string _query_msgin()
+{ return min;
+}
+
+string _set_msgout(string val)
+{ return mout=val;
+}
+
+string _query_msgout()
+{ return mout;
+}
+
+string _set_mmsgin(string val)
+{ return mmin=val;
+}
+
+string _query_mmsgin()
+{ return mmin;
+}
+
+string _set_mmsgout(string val)
+{ return mmout=val;
+}
+
+string _query_mmsgout()
+{ return mmout;
+}
+
+mixed _set_hands(mixed val)
+{ return hands=val;
+}
+
+mixed _query_hands()
+{ return hands;
+}
+
+varargs int Defend(int dam,mixed dam_type,mixed spell,object enemy)
+{ object ob;
+  if(!enemy ||  // Silvana 26.1.2002
+     (!query_once_interactive(previous_object())&&
+      !query_once_interactive(enemy)))
+    return caster->Defend(dam,dam_type,spell,enemy);
+  else
+  { enemy->StopHuntFor(caster);
+    caster->StopHuntFor(enemy);
+  }
+  if(objectp(ob=present("zauberer\nshadow",caster)))
+    ob->remove();
+  if(this_object())
+    remove();
+  return 0;
+}
+
+int Kill(object enemy)
+{ object ob;
+  if(!query_once_interactive(enemy))
+    return caster->Kill(enemy);
+  if(objectp(ob=present("zauberer\nshadow",caster)))
+    ob->remove();
+  if(this_object())
+    remove();
+  return 0;
+}
+
+int InsertEnemy(object enemy)
+{ object ob;
+  if(!query_once_interactive(enemy))
+    return caster->InsertEnemy(enemy);
+  else {
+    enemy->StopHuntFor(caster);
+    caster->StopHuntFor(enemy);
+  }
+  if(objectp(ob=present("zauberer\nshadow",caster)))
+    ob->remove();
+  if(this_object())
+    remove();
+  return 0;
+}
+
+string short()
+{ if(load_name(previous_object()) == "/obj/werliste")
+    return capitalize(geteuid(caster))+" verkleidet als "+caster->short();
+  return caster->short();
+}
+
+varargs string long()
+{ if(lgdesc)
+    return lgdesc;
+  return caster->long();
+}
diff --git a/std/player/skills.c b/std/player/skills.c
new file mode 100644
index 0000000..503da97
--- /dev/null
+++ b/std/player/skills.c
@@ -0,0 +1,621 @@
+// MorgenGrauen MUDlib
+//
+// player/skills.c -- Spielerskills
+//
+// $Id: skills.c 8809 2014-05-08 19:52:48Z Zesstra $
+
+//
+// 2003-01-20: Nun Zooks Baustelle
+//
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/skills";
+
+#include <combat.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <break_string.h>
+#include <wizlevels.h>
+
+#define NEED_PROTOTYPES
+#include <player/base.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+// Dieses Mapping speichert die deaktivierten Skills der einzelnen Gilden
+// Diese werden in den Gilden ueber P_GUILD_DEACTIVATED_SKILL gesetzt.
+nosave mapping deactivated_skills = ([]);
+
+// Flag fuer den Kompatibilitaetsmodus des Kampfs (Emulation von
+// 2s-Alarmzeit). Wenn != 0 speichert das Flag gleichzeitig die Zeit des
+// letzten Heartbeats. Auf diese Zeit wird der Startpunkt eines Spellfatigues
+// ggf. zurueckdatiert. (max. eine Sekunde)
+int spell_fatigue_compat_mode;
+
+// Ein create() fuer das Mapping
+
+protected void create()
+{
+  mapping act;
+
+  ::create();
+
+  // Wir holen die Gilden aus dem Gildenmaster 
+  foreach(string guild:
+      (string *)call_other(GUILDMASTER,"QueryProp",P_VALID_GUILDS))
+  {
+    if(catch(act=call_other("/gilden/"+guild,"QueryProp",
+        P_GUILD_DEACTIVATE_SKILLS); publish ))
+        log_file("WEAPON_SKILLS", sprintf ("%s: Gilde nicht ladbar: "
+              +"TP: %O, TI: %O, PO: %O, Gilde: %s\n", dtime(time()),
+              this_player(), this_interactive(), previous_object(), guild));
+    else if (act) // wenn act, ins Mapping aufnehmen.
+        deactivated_skills+=([guild:act]);
+  }
+  // keine echte Prop mehr, Save-Modus unnoetig.
+  Set(P_NEXT_SPELL_TIME,SAVE,F_MODE_AD);
+
+  Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
+}
+
+// Das Mapping kann man auch abfragen
+
+public mapping GetDeactivatedSkills()
+{
+    return copy(deactivated_skills);
+}
+
+// Funktion, die sagt ob ein ANY-Skill deaktiviert ist.
+public int is_deactivated_skill(string sname,string guild)
+{
+        if (deactivated_skills[guild])        
+                return deactivated_skills[guild][sname];
+        return 0;
+}
+
+
+// Funktion fuer die Waffenskills
+// traegt die allg. Waffenskills ein. Wird ggf. von FixSkills() gerufen.
+// (Das Eintragen bedeutet nicht, dass die aktiv sind! Aber bei Gildenwechsel
+// werden sie nicht eingetragen).
+private void set_weapon_skills() {
+
+  if (QuerySkillAbility(FIGHT(WT_SWORD))<=0)                
+      ModifySkill(FIGHT(WT_SWORD),([SI_SKILLABILITY:0]),150,"ANY");    
+  if (QuerySkillAbility(FIGHT(WT_AXE))<=0)                
+      ModifySkill(FIGHT(WT_AXE),([SI_SKILLABILITY:0]),150,"ANY");          
+  if (QuerySkillAbility(FIGHT(WT_SPEAR))<=0)        
+      ModifySkill(FIGHT(WT_SPEAR),([SI_SKILLABILITY:0]),150,"ANY");          
+  if (QuerySkillAbility(FIGHT(WT_WHIP))<=0)
+      ModifySkill(FIGHT(WT_WHIP),([SI_SKILLABILITY:0]),150,"ANY");
+  if (QuerySkillAbility(FIGHT(WT_KNIFE))<=0)                
+      ModifySkill(FIGHT(WT_KNIFE),([SI_SKILLABILITY:0]),150,"ANY");          
+  if (QuerySkillAbility(FIGHT(WT_CLUB))<=0)                
+      ModifySkill(FIGHT(WT_CLUB),([SI_SKILLABILITY:0]),150,"ANY");          
+  if (QuerySkillAbility(FIGHT(WT_STAFF))<=0)
+      ModifySkill(FIGHT(WT_STAFF),([SI_SKILLABILITY:0]),150,"ANY");
+} 
+
+// initialisiert die Skills fuer Spieler (momentan: allg. Waffenskills setzen
+// und P_SKILLS_VERSION)
+protected void InitSkills() {
+  mapping ski;
+  // schonmal initialisiert?
+  if (mappingp(ski=Query(P_NEWSKILLS, F_VALUE)) && sizeof(ski))
+    return;
+
+  // allg. Waffenskills aktivieren
+  set_weapon_skills();
+
+  // Version setzen
+  SetProp(P_SKILLSVERSION, CURRENT_SKILL_VERSION);
+  Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
+}
+
+// Updated Skills aus Version 0 und 1 heraus.
+private void FixSkillV1(string skillname, mixed sinfo) {
+  // alte Skills auf mappings normieren
+  if (intp(sinfo)) {
+    sinfo = ([SI_SKILLABILITY: sinfo ]);
+  }
+  // Eine Reihe von Daten werden geloescht, da die Daten aus der
+  // Gilde/Spellbook frisch kommen sollten und sie nicht spieler-individuell
+  // sind: SI_CLOSURE (wird onthefly korrekt neu erzeugt), SI_SKILLARG,
+  // SI_NUMBER_ENEMIES, SI_NUMBER_FRIENDS, SI_DISTANCE, SI_WIDTH, SI_DEPTH,
+  // SI_TESTFLAG, SI_ENEMY, SI_FRIEND.
+  // Ausserdem sind alle SP_* im toplevel falsch, die muessen a) in SI_SPELL
+  // und sollten b) nicht im Spieler gespeichert werden, sondern von
+  // Gilden/Spellbook jeweils frisch kommen.
+  // all dieses Zeug landete in alten Spielern im Savefile.
+  if (mappingp(sinfo))
+    sinfo -= ([SI_CLOSURE, SI_SKILLARG, SI_NUMBER_ENEMIES, SI_NUMBER_FRIENDS,
+             SI_DISTANCE, SI_WIDTH, SI_DEPTH, SI_TESTFLAG, SI_ENEMY,
+             SI_FRIEND, SP_NAME, SP_SHOW_DAMAGE, SP_REDUCE_ARMOUR,
+             SP_PHYSICAL_ATTACK, SP_RECURSIVE, SP_NO_ENEMY,
+             SP_NO_ACTIVE_DEFENSE, SP_GLOBAL_ATTACK ]);
+  else
+  {
+    tell_object(this_object(),sprintf(
+      "\n**** ACHTUNG - FEHLER ***\n" 
+      "Deine Skills enthalten einen defekten Skill %O:\n"
+      "Bitte lass dies von einem Erzmagier ueberpruefen.\n",
+      skillname));
+  }
+}
+
+// Updatet und repariert ggf. Skillmappings in Spielern
+protected void FixSkills() {
+
+  // nur bei genug rechenzeit loslegen
+  if (get_eval_cost() < 750000) {
+    call_out(#'FixSkills, 1);
+    return;
+  }
+  // wenn gar keine Skills da (?): InitSkills() rufen.
+  mapping allskills = Query(P_NEWSKILLS, F_VALUE);
+  if (!mappingp(allskills) || !sizeof(allskills)) {
+      InitSkills();
+      return;
+  }
+
+  // Die Fallthroughs in diesem switch sind voll Absicht!
+  switch(QueryProp(P_SKILLSVERSION)) {
+    // bei Version 0 und 1 das gleiche tun
+    case 0: // von 0 auf 1
+    case 1: // von 1 auf 2
+      foreach(string gilde, mapping skills: allskills) {
+        if (!stringp(gilde)) {
+          // sollte nicht vorkommen - tat aber... *seufz*
+          m_delete(skills, gilde);
+          continue;
+        }
+        walk_mapping(skills, #'FixSkillV1);
+      }
+      // allg. Waffenskills aktivieren, einige alte Spieler haben die noch
+      // nicht.
+      set_weapon_skills();
+      // Speicherflag fuer die Versionsprop muss noch gesetzt werden.
+      Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
+      // Version ist jetzt 2.
+      SetProp(P_SKILLSVERSION, 2);
+      // Fall-through
+   case 2:
+      // gibt es noch nicht, nichts machen.
+      //SetProp(P_SKILLSVERSION, 3);
+      // Fall-through, ausser es sind zuwenig Ticks da!
+      if (get_eval_cost() < 750000)
+        break; 
+  }
+  // Falls noch nicht auf der aktuellen Version angekommen, neuer callout
+  if (QueryProp(P_SKILLSVERSION) < CURRENT_SKILL_VERSION)
+      call_out(#'FixSkills, 2);
+}
+
+protected void updates_after_restore(int newflag) {
+  //Allgemeine Waffenskills aktivieren, wenn noetig
+  // Wird nun von InitSkills bzw. FixSkills uebernommen, falls noetig.
+  if (newflag) {
+    InitSkills();
+  }
+  else if (QueryProp(P_SKILLSVERSION) < CURRENT_SKILL_VERSION) {
+    // Falls noetig, Skills fixen/updaten. *grummel*
+    FixSkills();
+  }
+  // Prop gibt es nicht mehr. SAVE-Status loeschen. 
+  Set(P_GUILD_PREVENTS_RACESKILL,SAVE,F_MODE_AD);
+}
+
+// Standardisierte Nahkampf-Funktion fuer alle Nahkampf-Waffenarten
+protected mapping ShortRangeSkill(object me, string sname, mapping sinfo) 
+{ 
+  int val, w;
+  object enemy;
+  
+  if (!mappingp(sinfo) || !objectp(sinfo[P_WEAPON]))
+    return 0;
+
+  w = ([WT_KNIFE : 8,
+        WT_SWORD : 5,
+        WT_AXE   : 4,
+        WT_SPEAR : 6,
+        WT_CLUB  : 1,
+        WT_WHIP  : 9,
+        WT_STAFF : 7])[sinfo[P_WEAPON]->QueryProp(P_WEAPON_TYPE)];
+      
+
+  val = sinfo[SI_SKILLABILITY]*(sinfo[P_WEAPON]->QueryProp(P_WC)*
+                                (w*QueryAttribute(A_DEX)+
+                                 (10-w)*QueryAttribute(A_STR))/700)
+        /MAX_ABILITY;
+
+  if (val > 85) {
+    log_file("WEAPON_SKILLS", sprintf("%s: Zu hoher Schaden von: "
+    +"TO: %O, TI: %O, PO: %O, val: %d, A_DEX: %d, A_STR: %d, "
+                                   +"P_WEAPON: %O, P_WC: %d\n", dtime(time()),
+                                   this_object(), this_interactive(), 
+                                   previous_object(), val, 
+                                   QueryAttribute(A_DEX),
+                                   QueryAttribute(A_STR), sinfo[P_WEAPON],
+                                   sinfo[P_WEAPON]->QueryProp(P_WC)));
+    val = 85;
+  }
+
+  /*
+    Der zusätzliche Schaden der allgemeinen Waffenskills berechnet
+    sich wie folgt: 
+
+    sinfo[SI_SKILLABILITY)* (P_WC * ( X ) / 800) / MAX_ABILITY
+
+    Dabei beruecksichtigt X je nach Waffentyp in unterschiedlicher
+    Gewichtung die Werte fuer Geschicklichkeit und Staerke. 
+
+     X == 
+
+       Messer   : 8*A_DEX + 2*A_STR
+       Schwert  : 5*A_DEX + 5*A_STR
+       Axt      : 4*A_DEX + 6*A_STR 
+       Speer    : 6*A_DEX + 4*A_STR
+       Keule    : 1*A_DEX + 9*A_STR
+       Peitsche : 9*A_DEX + 1*A_STR
+  */
+
+  sinfo[SI_SKILLDAMAGE]+=val;
+
+
+  /* Lernen: Wird immer schwieriger, nur bei jedem 20. Schlag im Schnitt,
+   * und nur dann, wenn der Gegner auch XP gibt. */
+  if (random(MAX_ABILITY+1)>sinfo[SI_SKILLABILITY] && !random(10))
+  {
+         enemy=sinfo[SI_ENEMY];
+         if (objectp(enemy) && (enemy->QueryProp(P_XP)>0))
+         {
+           object ausbilder;
+        //         log_file("humni/log_wurm","Haut: %s und zwar %s, mit xp %d\n",geteuid(this_object()),to_string(enemy),enemy->QueryProp(P_XP));
+            LearnSkill(sname, random(5), 150);
+        // Gibt es einen Ausbilder?
+        if (QueryProp(P_WEAPON_TEACHER) && 
+                        ausbilder=find_player(QueryProp(P_WEAPON_TEACHER)))
+          {
+            // Ist der Ausbilder anwesend?
+            if (present(ausbilder,environment()))
+              {
+              // Ausbilder und Azubi muessen dieselbe Waffe haben.
+              //string wt_aus,wt_azu;
+              object waf_aus,waf_azu;
+
+              waf_azu=QueryProp(P_WEAPON);
+              waf_aus=call_other(ausbilder,"QueryProp",P_WEAPON);
+
+              //wt_azu=call_other(waf_azu,"QueryProp",P_WEAPON_TYPE);
+              //wt_aus=call_other(waf_aus,"QueryProp",P_WEAPON_TYPE);
+              //if (wt_azu==wt_aus)
+              if (objectp(waf_aus) && objectp(waf_azu) &&
+                  (string)waf_aus->QueryProp(P_WEAPON_TYPE)
+                     == (string)waf_azu->QueryProp(P_WEAPON_TYPE)) 
+                {
+                // Bonus von bis zu 5 Punkten
+                //log_file("humni/log_azubi",
+                  // sprintf("Azubi %O und Ausbilder %O : Waffentypen %s und %s, gelernt\n",this_object(),
+                    //   ausbilder, wt_azu, wt_aus));
+                LearnSkill(sname,random(6),150);
+                }
+          }
+        }
+    }
+  }
+
+  /* 
+     Die Schwierigkeit liegt bei 150, so dass 
+     ein Lvl. 1 Spieler maximal 15% Skill 
+     usw...
+     lernen kann. (Genaue Tabelle in /std/living/skills bei LimitAbility)
+  */         
+
+  return sinfo;
+}
+
+
+// Standardisierte Fernkampf-Funktion fuer alle Fernkampf-Waffenarten
+
+// *** noch deaktiviert ***
+
+protected mapping LongRangeSkill(object me, string sname, mapping sinfo, int dam) 
+{ int abil,val;
+
+  if (!mappingp(sinfo) || !dam || !objectp(sinfo[P_WEAPON]) ||
+      (sinfo[P_WEAPON]->QueryProp(P_SHOOTING_WC))<5)
+    return 0;
+
+  abil=sinfo[SI_SKILLABILITY]+sinfo[OFFSET(SI_SKILLABILITY)]; 
+  val=dam*abil/MAX_ABILITY;
+  val=val/2+random(val/2+1);
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+
+  if (random(MAX_ABILITY+1)>sinfo[SI_SKILLABILITY] && random(50)==42)
+    LearnSkill(sname, 1, 150);
+
+  return sinfo;
+}
+
+
+
+// Die einzelnen Waffenskills rufen dann nur die Standard-Funktion auf.
+
+protected mapping StdSkill_Fight_axe(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_club(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_knife(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_spear(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_sword(object me, string sname, mapping sinfo) 
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_whip(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_staff(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+
+
+
+// Die Fernwaffenskills sind Munitionsabhaengig
+
+// *** noch deaktiviert ***
+
+protected mapping StdSkill_Shoot_arrow(object me, string sname, mapping sinfo)
+{
+  return LongRangeSkill(me, sname, sinfo, 40);
+}
+
+protected mapping StdSkill_Shoot_bolt(object me, string sname, mapping sinfo)
+{ 
+  return LongRangeSkill(me, sname, sinfo, 40);
+}
+
+protected mapping StdSkill_Shoot_dart(object me, string sname, mapping sinfo)
+{
+  return LongRangeSkill(me, sname, sinfo, 20);
+}
+
+protected mapping StdSkill_Shoot_stone(object me, string sname, mapping sinfo)
+{
+  return LongRangeSkill(me, sname, sinfo, 40);
+}
+
+protected mixed _query_localcmds() {
+    return ({ ({"spruchermuedung","enable_spell_fatigue_compat",0,0}) });
+}
+
+
+
+// *** Kompatibilitaetsmodus fuer Spellfatigues (Emulation 2s-Alarmzeit) ***
+
+/** Speichert eine Spellfatigue von <duration> Sekunden fuer <key>.
+ * Ist hier nur fuer den Spellfatigue-Compat-mode.
+ * <key> darf 0 sein und bezeichnet das globale Spellfatigue.
+ * Rueckgabewert: Ablaufzeit der gesetzten Sperre
+                  -1, wenn noch eine nicht-abgelaufene Sperre auf dem <key> lag.
+                  0, wenn duration 0 ist.
+ */
+public varargs int SetSpellFatigue(int duration, string key) {
+
+  // 0 sollte nie eine Sperre bewirken, auch nicht im compat mode.
+  if (!duration) return 0;
+
+  if (spell_fatigue_compat_mode) {
+    // Spell-Fatigues auf HBs synchronisieren (2s-Alarmzeit-Emulation).
+    // Aufrunden auf ganzzahlige Vielfache von __HEART_BEAT_INTERVAL__
+    if (duration % __HEART_BEAT_INTERVAL__)
+        ++duration;
+
+    // Startpunkt des Delay soll Beginn der aktuellen Kampfrunde (HB-Zyklus)
+    // sein, ggf. um max. eine Sekunde zurueckdatieren.
+    // (spell_fatigue_compat_mode hat die Zeit des letzten HB-Aufrufs)
+    // Falls durch irgendein Problem (z.B. sehr hohe Last), der letzte HB
+    // laenger als 1s zurueckliegt, funktioniert das natuerlich nicht, aber
+    // bei fatigue+=spell_fatigue_compat_mode kann der Spieler zuviel Zeit
+    // einsparen.
+    if (time() > spell_fatigue_compat_mode)
+        --duration;  //1s zurueckdatieren
+  }
+
+  return ::SetSpellFatigue(duration, key);
+}
+
+/** Befehlsfunktion fuer Spieler um den Spellfatigue-Kompatibilitaetsmodus
+ * umzuschalten.
+ */
+public int enable_spell_fatigue_compat(string cmd) {
+  if (QueryProp(P_LAST_COMBAT_TIME) + 600 > time()) {
+    write(break_string(
+      "Im Kampf oder kurz nach einem Kampf kannst Du nicht zwischen "
+      "alter und neuer Spruchermuedung umschalten.\n"
+      "Momentan benutzt Du die "
+      + (spell_fatigue_compat_mode ? "alte (ungenauere)" : "neue (normale)")
+      + " Spruchermuedung.",78,0,BS_LEAVE_MY_LFS));
+    return 1;
+  }
+
+  if (cmd=="alt") {
+    spell_fatigue_compat_mode=time();
+    write(break_string(
+      "Alte Spruchermuedung wurde eingeschaltet. Alle Ermuedungspausen "
+      "zwischen Spruechen werden auf Vielfache von 2s aufgerundet und "
+      "beginnen in der Regel am Anfang Deiner Kampfrunde."));
+    return 1;
+  }
+  else if (cmd=="neu" || cmd=="normal") {
+    spell_fatigue_compat_mode=0;
+    write(break_string(
+      "Normale Spruchermuedung wurde eingeschaltet. Alle Ermuedungspausen "
+      "zwischen Spruechen werden sekundengenau berechnet."));
+    return 1;
+  }
+
+  notify_fail(break_string(
+      "Moechtest Du die alte oder die neue Spruchermuedung?\n"
+      "Momentan benutzt Du die "
+      + (spell_fatigue_compat_mode ? "alte (ungenauere)" : "neue (normale)")
+      + " Spruchermuedung.",78,0,BS_LEAVE_MY_LFS));
+  return 0;
+}
+
+/** Speichert die Zeit des letztes Heartbeats.
+ */
+protected void heart_beat() {
+  if (spell_fatigue_compat_mode)
+    spell_fatigue_compat_mode = time();
+}
+
+static int _set_guild_level(int num)
+{ string gilde;
+  mapping levels;
+
+  if ( !(gilde=QueryProp(P_GUILD)) )
+    return 0;
+
+  if ( !mappingp(levels=Query(P_GUILD_LEVEL)) )
+    levels=([]);
+
+  levels[gilde]=num;
+  Set(P_GUILD_LEVEL,levels);
+  GMCP_Char( ([P_GUILD_LEVEL: num]) );
+
+  return num;
+}
+
+static int _query_guild_level()
+{ string  gilde;
+  mapping levels;
+
+  if ( !(gilde=QueryProp(P_GUILD)) )
+    return 0;
+
+  if ( !mappingp(levels=Query(P_GUILD_LEVEL)) )
+      return 0;
+
+  return levels[gilde];
+}
+
+static string _set_guild_title(string t)
+{ string gilde;
+  mapping titles;
+
+  if ( !(gilde=QueryProp(P_GUILD)) )
+    return 0;
+
+  if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
+    titles=([]);
+
+  titles[gilde]=t;
+  Set(P_GUILD_TITLE,titles);
+  GMCP_Char( ([P_GUILD_TITLE: t]) );
+  return t;
+}
+
+static string _query_guild_title()
+{ string gilde,t;
+  object g;
+  mapping titles;
+
+  if ( !(gilde=QueryProp(P_GUILD)) )
+    return 0;
+
+  if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
+    titles=([]);
+
+  t=titles[gilde];
+  if ( !t && query_once_interactive(this_object())
+      && objectp(g=find_object("/gilden/"+gilde)) )
+  {
+    g->adjust_title(this_object());
+    SetProp(P_TITLE,0);
+
+    if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
+      return 0;
+
+    t=titles[gilde];
+  }
+
+  return t;
+}
+
+
+static string _set_guild(string gildenname)
+{ object pre;
+
+  if (!objectp(pre=previous_object()))
+    return 0;
+
+  if ( pre!=this_object() // Das Lebewesen selber darf die Gilde setzen,
+      && object_name(pre)!=GUILDMASTER  // der Gildenmaster auch
+      && (!this_player()
+          || this_player() != this_interactive()
+          || !IS_ARCH(this_player())
+         )
+      )
+    return 0;
+
+  Set(P_GUILD,gildenname);
+  GMCP_Char( ([P_GUILD: gildenname]) );
+  return gildenname;
+}
+
+static string _query_guild()
+{ string res;
+
+  if ( !(res=Query(P_GUILD)) && query_once_interactive(this_object()) )
+  {
+    // Spieler, die keiner Gilde angehoeren, gehoeren zur Abenteurergilde
+    if ( !(res=QueryProp(P_DEFAULT_GUILD)) )
+      return DEFAULT_GUILD;
+    else
+      Set(P_GUILD,res);
+    return res;
+  }
+
+  return res;
+}
+
+static string _query_title()
+{ string ti;
+
+  if ( stringp(ti=Query(P_TITLE)) )
+    return ti;
+
+  return QueryProp(P_GUILD_TITLE);
+}
+
+static string _set_title(string t)
+{
+  Set(P_TITLE, t, F_VALUE);
+  GMCP_Char( ([P_TITLE: t]) );
+  return t;
+}
+
diff --git a/std/player/soul.c b/std/player/soul.c
new file mode 100644
index 0000000..438f2c6
--- /dev/null
+++ b/std/player/soul.c
@@ -0,0 +1,3218 @@
+// MorgenGrauen MUDlib
+//
+// player/soul.c -- Die Seele des Spielers
+//
+// $Id: soul.c 9527 2016-03-12 11:37:54Z Arathorn $
+
+// Letzte Aenderung vom 08.09.95  Wargon
+
+// Set TabStop to 2 characters
+
+/* Version 1.41 MG, September 95
+   - Bei "frage" und "antworte" Test auf P_PERM_STRING fuer Sprachflueche.
+   - bugfix bei "schweige" (nahm keine Adverbien an)
+   */
+
+/* Version 1.4 MG, August 95
+   - Hilfefunktion eingebaut, siehe auch soulhelp.c
+   - einige kleinere Aenderungen erst jetzt durch eine neue Version gewuerdigt
+   - neue Verben, wie eigentlich fast immer :>
+   - typos und bugs gefixed (und neue eingebaut...)
+   - Funktion zur Abfrage von Adverbien von aussen eingebaut (Hallo, Anthea :>)
+   - so schlimm kann das doch nicht sein, einen TabStop von 2 zu nehmen, fuer
+     dieses eine file, alles andere zerlegt meine Formatierung immer so :<
+   - koenntet ihr bitte mal oben das "Letzte Aenderung" aendern, wenn ihr
+     irgendwo was aendert?
+   */
+
+/* Version 1.35 MG, Dezember 94
+   - Verben
+   - Aenderung des Parsings der quoted adverbs
+   - rknuddel ignorieren wird vom normalen ignore uebernommen
+   */
+
+/* Version 1.32 MG, Juli 94
+   - einige Verben
+   - ignorieren des rknuddel
+   */
+
+/* Version 1.31 MG, Mai 94
+   - einige Verben
+   */
+
+/* Version 1.3 MG, Mai 94
+   - quoted adverbs GEHEN jetzt
+   - ParseRest / ParseRemote neu geschrieben
+   */
+
+/* Version 1.21 MG, April 94
+   - quoted adverbs
+   */
+
+/* Danke an Angus fuer den Text von "liebe" */
+
+/* Version 1.2 MG, Januar 94
+   - Umstellung der Feelings von vielen kleinen Funktionen auf eine grosse,
+     damit der Funktionsoverhead wegfaellt.
+   - neue Ausgabe der Adverbien, mit more und nur noch eines pro Zeile
+   - mal wieder neue Verben.
+   - und das neue Standardadverb "jofi" :)
+   */
+
+/* Version 1.1 MG, November 93
+   Aenderungen:
+   - Ich habe "alle(n)" eingebaut. Die Verwaltung steht, man kann jetzt
+     Verben die Moeglichkeit "alle" geben (Bsp. "wink alle").
+   - inzwischen wieder einige Verben mehr, aber muss ich das noch
+     erwaehnen?
+   - (HASS) Rumata hat mein schoenes System fuer die Ausgabestrings
+     verkompliziert. Statt &a steht da jetzt z.B. @@adverb@@, was in
+     einer weiteren unnoetigen Funktion resultierte. Naja.
+   Highlander ryn Tahar
+   */
+
+/*
+   Ha! Ich nehme jetzt die erste ganze Versionsnummer fuer mich in Anspruch :)
+   So, heute ist's geschafft (Ich auch...). Ich bin fertig - ich mach nur
+   noch neue Verben rein, wenn Ideen kommen.
+   Gegeben an Dienstag, dem 22. Juni 1993 im heimatlichen Horst.
+   Highlander ryn Tahar.
+   P.S.: Kopiere sich das Ding, wer will - unter folgenden Bedingungen:
+   - Den Goettern von MorgenGrauen und
+   - Highlander@TAPPMud     Bescheid sagen und ausserdem
+   *seufz* nehmt Highlander hier in MorgenGrauen!! -HrT
+   Ha! Ihr koennt wieder TAPPMud nehmen :>  Aber sagt's ruhig in MG. -HrT
+   - entweder den ganzen Schwall hier drinlassen oder mich mit einem
+   neuen Text erwaehnen.
+   Das Ganze unter der Voraussetzung, dass ueberhaupt mal jemand eine deutsche
+   Seele braucht und sie nicht selber schreiben will :-) (ersparts euch lieber)
+   Highlander ryn Tahar.
+
+   **Arbeit:
+   Einbau von Adverbien, andere Reaktionen bei Geistern als "Wie bitte?",
+   einige neue Verben, einige alte Verben rausgeschmissen.
+   Weil es Probleme mit dem autoloading der playeradverbs gab, wurde
+   ausserdem die soul vom Objekt zum inheritfile fuer die playershell.
+
+   **Ideen  zum Weitermachen:
+   - (verb) alle, z.B. tritt alle   -- Moeglichkeit eingebaut
+   - Geisterverben mit Adverb
+
+   Version 1.0 fuer MorgenGrauen    Highlander Mai/Juni 93
+   */
+
+
+/* Hier ist sie nun, die DEUTSCHE version der soul,
+   viel Spass damit.
+   Poietix  Mai 1992
+   Vers.: 0.4 fuer JRMud
+   P.S. bitte meckert nicht dran rum , sondern verbessert und
+   erweitert sie.
+
+   Olpp November 1992
+   Vers.: 0.5 fuer MorgenGrauen
+
+   He, Olpp, schreibt man nicht die neueste Aenderung UEBER die alten?
+   *grins*, Highlander
+   */
+#pragma strong_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define SOULHELP "/std/player/soulhelp"
+#define LF "\n"
+#define NOT_SELF 1
+#define NOT_DEAD 1
+
+#define QPP QueryPossPronoun
+#define RETURN return _notify_fail
+#define Return return 0||_notify_fail  // netter Trick, muss ich mir merken -HrT
+#define GHOSTCHECK(sel,oth,vic) if (ghost()) {  write(sel); say(oth,who||ME);\
+                                  if (vic) who->Message(vic); return 1;  }
+#define HELPCHECK(x) if (str && (str=="-h" || str=="-?" || str=="/h" \
+         || str=="/?" || str=="hilfe"))\
+                       { More(SOULHELP->Help(x)); return 1; }
+
+// "schau an" als nicht-Untersuchung. Klappt aber anscheinend nicht, weil
+// ich nicht gegen den GD ankomme. Also besser auskommentiert lassen.
+#ifdef SCHAU_AN
+#undef SCHAU_AN
+#endif
+
+// Anpiepsen mit Text. Im Moment erlaubt, bei Missfallen auskommentieren und
+// in der Hilfe auskommentieren.
+#ifdef WECKE
+#undef WECKE
+#endif
+
+#define WECKE
+
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+#include <thing/properties.h>
+#include <player.h>
+#include <player/comm.h>
+#include <language.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+
+#include <defines.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <class.h>
+
+static object who, ofoo;
+static int for_all, flag, ifoo;
+mapping plr_adverbs;
+static string out_sel, out_vic, out_oth, adverb, sfoo;
+
+private void ParseAdverb(string *words);
+private string convert_string(string str);
+varargs mixed More(string str, int fflag, string returnto);
+string MatchAdverb(string str);
+
+mapping
+QueryStdAdverbs()  {
+  return ([
+    "unve" : "unverschaemt",
+    "gutg" : "gutgelaunt",
+    "gutm" : "gutmuetig",
+    "froh" : "froh",
+    "glue" : "gluecklich",
+    "wuet" : "wuetend",
+    "frec" : "frech",
+    "daem" : "daemonisch",
+    "boes" : "boese",
+    "ungl" : "ungluecklich",
+    "lang" : "langsam",
+    "schn" : "schnell",
+    "jamm" : "jammernd",
+    "freu" : "freundlich",
+    "shue" : "schuechtern",
+    "amue" : "amuesiert",
+    "aerg" : "aergerlich",
+    "aner" : "anerkennend",
+    "erst" : "erstaunt",
+    "bitt" : "bitter",
+    "brei" : "breit",
+    "vors" : "vorsichtig",
+    "char" : "charmant",
+    "kalt" : "kalt",
+    "verf" : "verfuehrerisch",
+    "zufr" : "zufrieden",
+    "tief" : "tief",
+    "verz" : "verzweifelt",
+    "drec" : "dreckig",
+    "vert" : "vertraeumt",
+    "uebe" : "ueberzeugt",
+    "frus" : "frustriert",
+    "stra" : "strahlend",
+    "hoff" : "hoffnungsvoll",
+    "unge" : "ungeduldig",
+    "unsi" : "unsinnigerweise",
+    "unsc" : "unschuldig",
+    "unwi" : "unwissend",
+    "iron" : "ironisch",
+    "wiss" : "wissend",
+    "gema" : "gemaechlich",
+    "sehn" : "sehnsuechtig",
+    "laut" : "laut",
+    "lieb" : "liebevoll",
+    "froe" : "froehlich",
+    "dank" : "dankbar",
+    "natu" : "natuerlich",
+    "gedu" : "geduldig",
+    "perf" : "perfekt",
+    "vers" : "verspielt",
+    "hoef" : "hoeflich",
+    "stol" : "stolz",
+    "frag" : "fragend",
+    "rupp" : "ruppig",
+    "trau" : "traurig",
+    "vera" : "veraechtlich",
+    "scha" : "schamlos",
+    "erns" : "ernst",
+    "schu" : "schuechtern",
+    "zaer" : "zaertlich",
+    "sanf" : "sanft",
+    "entg" : "entgeistert",
+    "heim" : "heimtueckisch",
+    "gela" : "gelangweilt",
+    "wild" : "wild",
+    "jofi" : "wie Jof, wenn er mal nicht idlet",
+  ]);
+}
+
+mapping
+QueryAdverbs() {
+  if (extern_call())
+    return deep_copy(plr_adverbs);
+  return plr_adverbs;
+}
+
+string
+MatchAdverb(string a)  {
+  ParseAdverb(explode(a," "));
+  return adverb;
+}
+
+// Verwaltungsroutinen
+
+static void
+add_soul_commands()  {
+  if (!plr_adverbs)
+    plr_adverbs=([]);
+  add_action("SoulComm", "", 1);
+}
+
+static int
+verben_liste()  {
+  More(SOULHELP->Help());
+  return 1;
+}
+
+#define ghost() QueryProp(P_GHOST)
+#define frog() QueryProp(P_FROG)
+#define capname() capitalize(name())
+#define gname() (ghost()?(frog()?"Der Geist eines Frosches"\
+			        :"Der Geist von "+capname())\
+		        :capname())
+
+varargs private void
+ParseRest(string arg, mixed extra)  {
+  string wer,wie,*words,quotea;
+  int num,bis;
+  who = for_all = adverb = 0;
+  if (!arg) return;
+  if (extra)
+    if (!pointerp(extra)) {
+      if (sscanf(arg, extra+" %s", wie)==1)
+      arg=wie;
+    }
+    else
+      for (bis=sizeof(extra),num=0; num<bis; num++)
+        if (sscanf(arg, extra[num]+" %s", wie)==1)
+          arg=wie;
+
+  if ((bis=strstr(arg, "/"))>=0)
+    quotea=arg[bis..],arg=arg[0..bis-1];
+  quotea=quotea||"",arg=arg||"";
+
+  words=explode(implode(explode(arg, ","), " und"), " ");
+  if (!sizeof(words)) return;
+  if (sizeof(words) && (words[0]=="alle" || words[0]=="allen"))
+    for_all=1,wer=words[0],words=words[1..];
+  if (!for_all)  {     /* noch kein Opfer */
+    wer=match_living(lower_case(words[0]));
+    if (stringp(wer)) who=present(wer, environment(ME));
+    if (!who) who=present(words[0], environment(ME));
+    if (who && who->QueryProp(P_INVIS)) who=0;
+    }
+  if (who && sizeof(words))
+    words=words[1..];  /* Opfer gefunden - wenn's eines gibt */
+  words+=explode(quotea, " ");
+  words-=({""});
+  if (sizeof(words)) ParseAdverb(words);
+}
+
+private int
+ParseRemote(string arg)  {
+  string wer,*words; 
+
+  adverb = 0; // Adverb vom letzten Mal keinesfalls wiederverwenden. ;-)
+
+  if (!stringp(arg) || !sizeof(arg)) return 0;
+  
+  words=explode(arg," ");
+
+  mixed liv = match_living(lower_case(words[0]));
+  if (stringp(liv))
+      who=find_player(liv);
+  
+  if (who) {
+    // Ziel ist ein Spieler.
+    if (!who->QueryProp(P_INVIS) || IS_WIZARD(ME))
+    {
+      // Spieler ist nicht Invis oder ich bin Magier.
+      string nam = (query_once_interactive(ME) ? getuid() : 
+	             lower_case(name(RAW)));
+      if (query_verb()[0..5]=="rknudd" &&
+	  who->TestIgnore(nam+".rknuddel") )
+      {
+        // ich oder das Kommando werde ignoriert.
+        write(who->Name(WER)+" ignoriert Deinen Knuddelversuch.\n");
+        return 1;
+      }
+    }
+    else
+      // Spieler ist invis und ich bin kein Magier.
+      who = 0;
+  }
+  // kein eingeloggter und sichtbarer Spieler. Vielleicht ein NPC? (BTW: kein
+  // else if, weil im if fuer Spieler oben who genullt werden kann und dann
+  // nochmal nach nem NPC gesucht werden soll.)
+  if (!who) {
+    wer = match_living(lower_case(words[0]));
+    if(stringp(wer)) 
+      who=present(wer,environment(ME));
+    if (!who) who=present(words[0], environment(ME));
+    if (who && who->QueryProp(P_INVIS)) who=0;
+  }
+
+  if (!who || sizeof(words)==1) return 0;
+  words=words[1..];
+  ParseAdverb(words);
+  return(0);
+}
+
+/**
+ Gibt den passenden Adverb-Text zu einem key zurueck
+ \param s Danach wird in der Adverbien-Liste gesucht
+ \param fuzzy 
+ \return Der gefundene Adverbientext oder 0
+ */
+varargs string GetPlayerAdverb( string s, int fuzzy ) {
+  int i, j; 
+  string *search_pattern,
+         *search_result, 
+          result;
+
+  // Erstmal gucken, ob der String direkt gefunden werden kann
+  // das geht am schnellsten
+  result = QueryStdAdverbs()[s] || plr_adverbs[s];
+
+  // Wenn noch kein Ergebnis gefunden, und man unscharf suchen will
+  if ( fuzzy && !result) {
+
+    // Suchmuster fuer das Intersect erzeugen
+    search_pattern=({s});
+
+    j = sizeof(s)-1;
+    for ( i=2;  i < j ;i++) {
+      search_pattern += ({s[0..<i]});
+    }
+
+    // Intersect zwischen allen bekannten Abkuerzungen und Search-Pattern
+    // erzeugen. Dieses wird dann gleichzeitig nach Laenge sortiert
+    // genauester Treffer = String mit groesster Laenge
+    search_result = sort_array(
+      (m_indices(QueryStdAdverbs()) | m_indices(plr_adverbs))&search_pattern, 
+         #'>);
+
+    // Adverb zum genauesten Treffer zurueckgeben
+    if (sizeof(search_result)) 
+      result = QueryStdAdverbs()[search_result[0]] || 
+               plr_adverbs[search_result[0]];
+  }
+
+  return result;
+}
+
+/**
+  Parst die Adverbienparameter fuer Verben und speichert die
+  passende Textausgabe in der globalen Variable "adverb"
+  \param words Array mit den zu parsenden Adverbien-Strings
+*/
+private void
+ParseAdverb(string *words)  {
+  int num,andsign,bis;
+  string qadv,*adv,cnt;
+
+  adv=({});
+  qadv=0;
+
+  bis=sizeof(words);
+  // Sonderfall Gequotetes Adverb (alles nach dem Quote) speichern und aus 
+  // Words rausschneiden.
+  for (num=0; num<bis; num++)
+    if (words[num][0..0]=="/")  {
+      words[num]=words[num][1..];
+      qadv=implode(words[num..], " ");
+      words=words[0..num-1];
+      break;
+    }
+
+  // Es kann sein, dass vor dem Quote noch eine und steht. Das wird jetzt auch 
+  // noch entfernt, muss aber spaeter wieder eingefuegt werden.
+  if (sizeof(words) && words[<1]=="und")  {
+    words=words[0..<2];
+    andsign=1;
+  }
+
+  // Weitersuchen?
+  if (bis=sizeof(words))
+    for (num=0; num<bis; num+=2)
+       adv+=({GetPlayerAdverb(words[num], 1)});
+  cnt=CountUp(adv-({0}));
+
+  // Ausgabe zusammenbauen
+  if (andsign)
+    adverb=CountUp((sizeof(adv) ? adv : ({}))+(qadv ? ({qadv}) : ({})));
+  else if (sizeof(cnt) && sizeof(qadv))
+    adverb = cnt + " " + qadv;
+  else if (sizeof(qadv))
+    adverb = qadv;
+  else if (sizeof(cnt))
+    adverb = cnt; 
+  if (adverb=="") adverb=0;
+}
+
+private mixed MixedOut(int casus)  {
+  object *envs,*vics;
+  string *names,out,aufz;
+  int count,msg;
+
+  for_all=0;
+  vics=({});
+  names=({});
+  envs=all_inventory(environment())-({this_player()});
+  if (!(count=sizeof(envs)))
+    RETURN("Nichts und niemand da. Schau Dich naechstes Mal besser um.\n");
+  for ( ; count--; )
+    if (living(envs[count]) && !envs[count]->QueryProp(P_INVIS))  {
+      vics+=({envs[count]});
+      names+=({envs[count]->name(casus)});
+    }
+  if (!sizeof(vics))
+    RETURN("Keiner da. Schau Dich naechstes Mal besser um.\n");
+  aufz=CountUp(names);
+  for (count=sizeof(vics); count--;)
+  {
+    out=implode(explode(out_vic, "@@alle@@"),aufz);
+        out = regreplace( out, "\\<"+vics[count]->name(casus)+"\\>",
+                          capitalize(vics[count]->QueryDu(casus)), 0 );
+
+    msg=vics[count]->ReceiveMsg(convert_string(out),MT_COMM,MA_EMOTE,
+                                0,this_object());
+    switch(msg)
+    {
+      case MSG_DELIVERED:
+      case MSG_BUFFERED:
+        break;
+      case MSG_IGNORED:
+      case MSG_VERB_IGN:
+      case MSG_MUD_IGN:
+        write(vics[count]->Name()+" ignoriert Dich oder diesen Befehl.\n");
+        break;
+      default:
+        write(vics[count]->Name()+" konnte Dich gerade nicht lesen.\n");
+    }
+  }
+  write(break_string(convert_string(implode(explode(out_sel,"@@alle@@"),aufz)
+    +LF), 78));
+  return 1;
+}
+
+varargs private int
+CheckLife(int no_self,int no_dead, string no_self_text, string no_dead_text)  {
+  if (who && living(who) && who!=this_player()) return 0;
+  if (no_self && who && who==this_player())  {
+    if (no_self_text)
+      write(no_self_text+LF);
+    else
+      write("Mach das mit anderen, nicht mit Dir selber.\n");
+    return 1;
+  }
+  if (who && !living(who) && no_dead)  {
+    if (no_dead_text)
+      write(no_dead_text+LF);
+    else
+      write("Das darfst Du nur mit Lebewesen.\n");
+    return 2;
+  }
+  if (!who)  {
+    write("Schau Dich erst mal um - das angegebene Objekt ist nicht da.\n");
+    return 3;
+  }
+  return(0); //non-void. Fall-through, alles OK.
+}
+
+private string
+convert_string(string str)  {
+  /* Ich bin unschuldig, ich hatte das viel einfacher und schoener :)
+     Rumata wollte das so ;)  -HrT                                     */
+  str = implode( explode( str, "@@name@@" ), capname() );
+  str = implode( explode( str, "@@gname@@" ), gname() );
+  str = implode( explode( str, "@@wer@@" ),
+    (who?capitalize(who->name(WER,2)||""):"" ));
+  str = implode( explode( str, "@@ wen@@" ),
+    (who?" "+who->name(WEN,2):""));
+  str = implode( explode( str, "@@ wem@@" ),
+    (who?" "+who->name(WEM,2):""));
+  str = implode( explode( str, "@@wen@@" ),
+    (who?who->name(WEN,2):""));
+  str = implode( explode( str, "@@wem@@" ),
+    (who?who->name(WEM,2):""));
+  str = implode( explode( str, "@@wessen@@" ),
+    (who?who->name(WESSEN,2):""));
+  str = implode( explode( str, "@@adverb@@" ),
+    (adverb?" "+adverb:"") );
+  return str;
+}
+
+private int
+FeelIt()  {
+  int msg, flg;
+
+  flg = MSGFLAG_SOUL;
+  if (query_verb() && (query_verb()[0..3]=="frag" || query_verb()[0..3]=="antw"))
+    flg |= MSGFLAG_SAY;
+  if (query_verb() && (query_verb()[0..5]=="rknudd" || query_verb()=="rwink"))
+    flg |= MSGFLAG_REMOTE;
+
+  // NPC haben keine TM-Hist (comm.c). Leider erben aber div. Magier die Soul
+  // (trotzdem sie in /std/player/ liegt) in ihren NPC... *fluch*
+  if (query_once_interactive(ME))
+    _recv(who, break_string(convert_string(out_sel),78), flg);
+  else
+    tell_object(ME, break_string(convert_string(out_sel),78));
+
+  if (out_vic && who)  {
+    if (query_once_interactive(who))  {
+      msg=who->Message( break_string(convert_string( out_vic ),78), flg);
+      if (msg==-1)
+        write(who->name()+" ignoriert Dich oder diesen Befehl.\n");
+    } else
+      tell_object(who,break_string(convert_string( out_vic ),78));
+  }
+  if (out_oth)
+  say( break_string(convert_string( out_oth ),78), ({who,this_player()}) );
+  out_sel=out_vic=out_oth=0;
+  return 1;
+}
+
+/**
+  Die Funktion stellt einen Hilfetext zur Verfuegung und listet die 
+  definierten Adverbien auf.
+  \param mine 0 = alle Adverbien, 
+              1=nur selbst definierte Adverbien 
+              2=nur die vom System bereitgestellten
+ */
+private int zeige_adverbs(int mine)  {
+  mapping adverb_list;
+  string out,s;
+
+  // Parameter auswerten
+  switch (mine){
+    case 1:
+      adverb_list=plr_adverbs;
+      out = "Du hast folgende Adverbien definiert:\n";
+      break;
+    case 2:
+      adverb_list=QueryStdAdverbs();
+      out = "Systemseitig sind folgende Adverbien definiert:\n";
+      break;
+    default:
+      adverb_list=QueryStdAdverbs()+plr_adverbs;
+      out = "Folgende Adverbien stehen Dir zur Verfuegung:\n";
+  }
+
+  out +="  Abk.    Adverb\n  ======  ======\n";
+
+  if ( sizeof(adverb_list) == 0) 
+    out += " keine.\n";
+  else
+    // Ueber alle Elemente der indizies der Adverbienliste gehen
+   foreach ( s : sort_array(m_indices(adverb_list), #'> ) ) {
+      out += break_string(adverb_list[s],78,
+                          sprintf("  %-6s  ",s),BS_INDENT_ONCE);
+    }
+
+  More(out+"\nWie diese Adverbien benutzt werden ist in <hilfe adverb> "
+    "beschrieben.\n");
+
+  return 1;
+}
+
+varargs static int
+SoulComm(string str, string _verb)  {
+  int t_g,t_n,flag;
+  string str1,str2,str3,*taenze,vb;
+  out_sel=out_vic=out_oth=who=0;
+//  if (this_interactive()!=ME) return 0;
+  if (interactive(ME)) str=_unparsed_args(); // NPCs haben das nicht :(
+  if (str=="") str=0;
+  vb=_verb||query_verb();
+  if (sizeof(vb)>1 && vb[<1]=='e' && vb!="noe") vb=vb[0..<2];
+  sfoo = 0;
+  switch (vb)  {
+    /**************** Aechzen ***************/
+    case "aechz":
+    HELPCHECK("aechz");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Aechze wie?\n");
+    out_sel="Du aechzt@@adverb@@.";
+    out_oth="@@gname@@ aechzt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Anschmiegen ***************/
+    case "schmieg":
+    HELPCHECK("schmieg");
+    ParseRest(str);
+    if (!who)
+      Return("An wen willst Du Dich anschmiegen?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Das geht doch nicht.",
+      "Nein, das macht keinen Spass. Lebt ja nicht mal."))
+        return 1;
+    out_sel="Du schmiegst Dich@@adverb@@ an@@ wen@@ an.";
+    out_vic="@@gname@@ schmiegt sich@@adverb@@ an Dich.";
+    out_oth="@@gname@@ schmiegt sich@@adverb@@ an@@ wen@@ an.";
+    return FeelIt();
+
+    /**************** Antworten ***************/
+    case "antwort":
+    HELPCHECK("antwort");
+    if (!str)
+      Return("Antworte [WEM] WAS?\n");
+    ParseRest(str);
+    if (!who)
+      str1=capitalize(str);
+    else
+      if (sscanf(str,"%s %s",str1,str1)!=2)
+        Return("Antworte was?\n");
+      else
+        str1=capitalize(str1);
+    out_sel="Du antwortest@@ wem@@: "+str1;
+    /* Sprachflueche beruecksichtigen -Wargon, 8. 9. 95 */
+    if (QueryProp(P_PERM_STRING))
+      str1 = call_other(QueryProp(P_PERM_STRING),"permutate_string",str1)||"";
+    if (who) out_vic="@@gname@@ antwortet Dir: "+str1;
+    out_oth="@@gname@@ antwortet@@ wem@@: "+str1;
+    return FeelIt();
+
+    /**************** Applaudieren ***************/
+    case "applaudier":
+    HELPCHECK("applaudier");
+    GHOSTCHECK("Deine Haende fahren durcheinander durch - war wohl nix.\n",
+      gname()+" will applaudieren, aber "+QPP(FEMALE,WER,PLURAL)
+        +" Haende sausen\ndurcheinander durch.\n", 0);
+    if (!str)  {
+      out_sel="Du applaudierst von ganzem Herzen.";
+      out_oth="@@name@@ gibt eine Runde Applaus.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du applaudierst @@alle@@@@adverb@@.";
+        out_vic="@@name@@ applaudiert @@alle@@@@adverb@@.";
+        return MixedOut(WEM);
+      }
+      if (!who && !adverb)
+        Return("Applaudiere wem oder wie oder so aehnlich.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Nein, das gehoert sich nicht.",
+        "Sachen wird hier nicht applaudiert, OK?"))
+          return 1;
+      out_sel="Du applaudierst@@ wem@@@@adverb@@.";
+      if (who) out_vic="@@name@@ applaudiert Dir@@adverb@@.";
+      out_oth="@@name@@ applaudiert@@ wem@@@@adverb@@.";
+    }
+    return FeelIt();
+
+    /**************** Argln ***************/
+    case "argl":
+    HELPCHECK("argl");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Argle wie?\n");
+    out_sel="Du arglst"+(adverb ? "@@adverb@@." : " ein wenig vor Dich hin.");
+    out_oth="@@gname@@ arglt"
+      +(adverb ? "@@adverb@@." : " ein wenig vor sich hin.");
+    return FeelIt();
+
+    /**************** Aufatmen ***************/
+    case "atm":
+    HELPCHECK("atm");
+    if (!str || sscanf(str,"%sauf",str1)!=1)
+      Return("Atme wie auf?\n");
+    ParseRest(str1);
+    out_sel="Du atmest"+(adverb ? "@@adverb@@" : " erleichtert")+" auf.";
+    out_oth="@@gname@@ atmet"+(adverb ? "@@adverb@@" : " erleichtert")+" auf.";
+    return FeelIt();
+
+    /**************** Begruessen ***************/
+    case "hallo":
+    case "hi":
+    case "begruess":
+    HELPCHECK("begruess");
+    ParseRemote(str);
+    if (!who)
+      Return("Wen willst Du begruessen?\n");
+    if (present(who, environment()))  {
+      out_sel="Du heisst @@wen@@@@adverb@@ willkommen.";
+      out_vic="@@gname@@ heisst Dich@@adverb@@ willkommen.";
+      out_oth="@@gname@@ heisst @@wen@@@@adverb@@ willkommen.";
+    }
+    else  {
+      out_sel="Du heisst @@wen@@@@adverb@@ aus der Ferne willkommen.";
+      out_vic="@@gname@@ heisst Dich@@adverb@@ aus der Ferne willkommen.";
+    }
+    return FeelIt();
+
+    /**************** Betasten ***************/
+    case "betast":
+    HELPCHECK("betast");
+    ParseRest(str);
+    if (!who)
+      Return("Begrabsche wen?\n");
+    out_sel="Du grabbelst@@adverb@@ an "+who->name(WEM)+" herum.";
+    out_vic="@@gname@@ grabbelt@@adverb@@ an Dir herum.";
+    out_oth="@@gname@@ grabbelt@@adverb@@ an "+who->name(WEM)+" herum.";
+    return FeelIt();
+
+    /**************** Bewundern ***************/
+    case "bewunder":
+    HELPCHECK("bewunder");
+    ParseRest(str);
+    if (!who)
+      Return("Bewundere wen?\n");
+    out_sel="Du bewunderst @@wen@@@@adverb@@.";
+    out_vic="@@gname@@ bewundert Dich@@adverb@@.";
+    out_oth="@@gname@@ bewundert @@wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Bibbern ***************/
+    case "bibber":
+    HELPCHECK("bibber");
+    if (ghost())
+      Return("Als Geist fuehlst Du keine Kaelte.\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Bibbere wie?\n");
+    out_sel="Du bibberst@@adverb@@ vor Kaelte.";
+    out_oth="@@name@@ bibbert@@adverb@@ vor Kaelte.";
+    return FeelIt();
+
+    /**************** Bohre Nase ***************/
+    case "bohr":
+    HELPCHECK("bohr");
+    ParseRest(str, ({"nase","in der nase","in nase"}));
+    if (str && str!="nase" && str!="in nase" && str!="in der nase" && !adverb)
+      Return("Bohre wie Nase?\n");
+    out_sel="Du bohrst@@adverb@@ in Deiner Nase.";
+    out_oth="@@gname@@ bohrt@@adverb@@ in der Nase.     Igitt! :)";
+    return FeelIt();
+
+    /**************** Brummeln ***************/
+    case "brummel":
+    HELPCHECK("brummel");
+    ParseRest(str);
+    out_sel="Du brummelst"
+      +(adverb ? "@@adverb@@." : (str ? " kaum verstaendlich: "+str+"." : "."));
+    out_oth="@@gname@@ brummelt"
+      +(adverb ? "@@adverb@@." : (str ? " kaum verstaendlich: "+str+"." : "."));
+    return FeelIt();
+
+    /**************** cls ***************/
+    case "cls":
+    HELPCHECK("cls");
+    write("");
+    return 1;
+
+    /**************** Daeumchendrehen ***************/
+    case "dreh":
+    HELPCHECK("dreh");
+    if (!str)
+      Return("Drehe was?\n");
+    if(strstr(str,"daeumchen")<0 && strstr(str,"daumen")<0)
+      Return("Drehe was?\n");
+    ParseRest(str,({"daeumchen","daumen"}));
+    out_sel="Du drehst@@adverb@@ Daeumchen.";
+    out_oth="@@gname@@ dreht@@adverb@@ Daeumchen.";
+    return FeelIt();
+
+    /**************** Danken ***************/
+    case "dank":
+    HELPCHECK("dank");
+    ParseRest(str);
+    if (!who)
+      Return("Bei wem willst Du Dich bedanken?\n");
+    if (CheckLife(NOT_SELF, NOT_DEAD,
+      "Leidest Du jetzt schon an Persoenlickeitsspaltung? Ne, ne...",
+      "Keine Reaktion. Ist wohl befriedigender, sich bei Lebewesen zu "
+        +"bedanken."))
+        return 1;
+    out_sel="Du bedankst Dich@@adverb@@ bei@@ wem@@.";
+    out_vic="@@gname@@ bedankt sich@@adverb@@ bei Dir.";
+    out_oth="@@gname@@ bedankt sich@@adverb@@ bei@@ wem@@.";
+    return FeelIt();
+
+    /**************** Denken ***************/
+    case "denk":
+    HELPCHECK("denk");
+    if (ghost())
+      Return("Womit willst Du denn denken? Du hast keine grauen Zellen...\n");
+//    ParseRest(str);
+    str2=old_explode(str||""," ")[0];
+    if (str
+    && (!adverb||((QueryStdAdverbs()[str2]||plr_adverbs[str2]))!=adverb))  {
+      out_sel="Du denkst   . o O ("+str+")";
+      out_oth="@@name@@ denkt   . o O ("+str+")";
+      out_vic="@@name@@ denkt   . o O ("+str+")";
+    }
+    else  {
+      out_sel="Du faengst@@adverb@@ an zu denken.\nKleine "
+        +"Rauchwoelkchen steigen auf...";
+      out_oth="@@name@@ faengt@@adverb@@ an zu denken.\nKleine "
+        +"Rauchwoelkchen steigen auf...";
+    }
+    return FeelIt();
+
+    /**************** Deuten ***************/
+    case "deut":
+    HELPCHECK("deut");
+    ParseRest(str,"auf");
+    if (for_all)  {
+      out_sel="Du deutest@@adverb@@ auf @@alle@@.";
+      out_vic="@@gname@@ deutet@@adverb@@ auf @@alle@@.";
+      return MixedOut(WEN);
+    }
+    if (!who)
+      Return("Auf wen oder was willst Du deuten?\n");
+    out_sel="Du deutest@@adverb@@ auf"
+      +(who==this_object()?" Dich." : "@@ wen@@.");
+    if (who != this_object()) out_vic="@@gname@@ deutet@@adverb@@ auf Dich.";
+    out_oth="@@gname@@ deutet@@adverb@@ auf"
+      +(who==this_object() ? " sich selber.": "@@ wen@@.");
+    return FeelIt();
+
+    /**************** Druecken ***************/
+    case "drueck":
+    HELPCHECK("drueck");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du drueckst @@alle@@"+(adverb ? "@@adverb@@" : " zaertlich")
+        +" an Dich.";
+      out_vic="@@gname@@ drueckt @@alle@@"+
+        (adverb ? "@@adverb@@" : " zaertlich")+" an sich.";
+      return MixedOut(WEN);
+    }
+    if (!who)
+      Return("Wen willst Du denn druecken?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Das macht doch keinen Spass.",
+      "Ich druecke nur jemanden, nicht etwas."))
+        return 1;
+    GHOSTCHECK("Du willst "+who->name(WEN)+" an Dich druecken - nur hast Du "
+        +"schon\nwieder nicht daran gedacht, dass so was als Geist nicht "
+        +"geht.\n",
+      gname()+" will "+who->name(WEN)+" an sich druecken - hat aber\n"
+        +"mal wieder nicht an die Nachteile des Geisterdaseins gedacht.\n",
+      gname()+" will Dich an sich druecken - hat aber mal wieder\n"
+        +"nicht an die Nachteile des Geisterdaseins gedacht.\n");
+    out_sel="Du drueckst @@wen@@"+(adverb ? "@@adverb@@" : " zaertlich")
+      +" an Dich.";
+    out_vic="@@name@@ drueckt Dich"+(adverb ? "@@adverb@@" : " zaertlich")
+      +" an sich.";
+    out_oth="@@name@@ drueckt @@wen@@"+(adverb ? "@@adverb@@" : " zaertlich")
+      +" an sich.";
+    return FeelIt();
+
+    /**************** Entschuldige ***************/
+    case "entschuldig":
+    HELPCHECK("entschuldig");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Entschuldige Dich wie oder bei wem?\n");
+    out_sel="Du bittest"+(who ? " @@wen@@" : "")
+      +"@@adverb@@ um Entschuldigung.";
+    if (who) out_vic="@@gname@@ bittet Dich@@adverb@@ um Entschuldigung.";
+    out_oth="@@gname@@ bittet"+(who ? " @@wen@@" : "")
+      +"@@adverb@@ um Entschuldigung.";
+    return FeelIt();
+
+    /**************** Erbleichen ***************/
+    case "erbleich":
+    HELPCHECK("erbleich");
+    GHOSTCHECK("Ich weiss zwar nicht, wie Du das schaffst, aber Du wirst "
+        +"noch bleicher.\n",
+      break_string("Wie unwahrscheinlich das auch ist, aber "+gname()
+        +" schafft es tatsaechlich, noch bleicher zu werden.",78), 0 );
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Wie willst Du erbleichen?\n");
+    out_sel="Du erbleichst@@adverb@@.";
+    out_oth="@@name@@ erbleicht@@adverb@@.";
+    return FeelIt();
+
+    /**************** Erroeten ***************/
+    case "erroet":
+    HELPCHECK("erroet");
+    GHOSTCHECK("Du schaffst es nur bis zu einem blassen Rosa, aber immerhin.\n",
+      "Die Wangen des Geistes von "+capname()+" werden leicht rosa.\n", 0);
+    ParseRest(str);
+    if (!adverb && str)
+      Return("Erroete wie?\n");
+    out_sel="Deine Wangen gluehen@@adverb@@.";
+    out_oth="@@name@@ erroetet@@adverb@@.";
+    return FeelIt();
+
+    /**************** Erschrecken ***************/
+    case "erschreck":
+    case "erschrick":
+    if (!ghost())
+      Return("Du bist zu harmlos, Geist muesste man sein...\n");
+    HELPCHECK("erschreck");
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du denn erschrecken?\n");
+    out_sel="Mit einem lauten BUH! erschreckst Du @@wen@@"
+      +(adverb ? "@@adverb@@." : " fuerchterlich.");
+    out_vic="BUH! Du zuckst vor Schreck zusammen. Muss dieser Geist von "
+      +"@@gname@@ Dich auch@@adverb@@ erschrecken.";
+    out_oth="BUH! @@gname@@ erschreckt @@wen@@"
+      +(adverb ? "@@adverb@@." : " fuerchterlich.");
+    return FeelIt();
+
+    /**************** Flippen ***************/
+    case "flipp":
+    HELPCHECK("flipp");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Ausflippen wollen wir also, so so. Und wie, wenn ich "
+        +"fragen darf?\n");
+    out_sel="Du flippst"+(adverb ? "@@adverb@@ aus." : " total aus.");
+    out_oth="@@gname@@ flippt"+(adverb ? "@@adverb@@ aus." : " total aus.");
+    return FeelIt();
+
+    /**************** Fluchen ***************/
+    case "fluch":
+    HELPCHECK("fluch");
+    GHOSTCHECK("Du faengst mangels Resonanzkoerper leise an zu fluchen.\n",
+      gname()+" faengt leise an zu fluchen. Laut kann er nicht,\n"
+        +"mangels Luft und Resonanzkoerper.\n", 0);
+    if (!str)  {
+      out_sel="Du fluchst lautstark.";
+      out_oth="@@name@@ faengt an, fuerchterlich zu fluchen.";
+    }
+    else  {
+      ParseRest(str);
+      if (!adverb)
+        Return("Wie willst Du fluchen?\n");
+      out_sel="Du fluchst@@adverb@@.";
+      out_oth="@@name@@ flucht auf einmal@@adverb@@.";
+    }
+    return FeelIt();
+
+    /**************** Fragen ***************/
+    case "frag":
+    HELPCHECK("frag");
+    if (!str)
+      Return("Frage wen was?\n");
+    ParseRest(str);
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du faengst zu gruebeln an...",
+      "Frage jemand, der lebt."))
+        return 1;
+    if (who)
+      sscanf(str,"%s %s", str1,str1);
+    else
+      str1=str;
+    if (!str1)
+      Return("Frage "+who->name(WEN)+" WAS?\n");
+    str1=capitalize(str1);
+    if (str1[<1] != '?')
+      str1 += "?";
+    out_sel="Du fragst@@ wen@@: "+str1;
+    /* Sprachfluch beruecksichtigen -Wargon, 8. 9. 95 */
+    if (objectp(QueryProp(P_PERM_STRING)))
+      str1 = call_other(QueryProp(P_PERM_STRING), "permutate_string", str1)||"";
+    if (who) out_vic=(ghost() ? "Der Geist von " : /* IS_LEARNER(ME) ?
+      QueryProp(P_PRESAY)||"" : */ "")+capname()+" fragt Dich: "+str1;
+    out_oth=(ghost() ? "Der Geist von " : /* IS_LEARNER(ME) ?
+      QueryProp(P_PRESAY)||"" : */ "")+capname()+" fragt@@ wen@@: "+str1;
+    return FeelIt();
+
+    /**************** Freuen ***************/
+    case "freu":
+    HELPCHECK("freu");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Freue Dich wie?\n");
+    out_sel="Du "+(who ? "grinst @@wen@@ an und " : "")
+      +"freust Dich@@adverb@@.";
+    if (who) out_vic="@@gname@@ grinst Dich an und freut sich@@adverb@@.";
+    out_oth="@@gname@@ "+(who ? "grinst @@wen@@ an und " : "")
+      +"freut sich@@adverb@@.";
+    return FeelIt();
+
+    /**************** Furzen ***************/
+    case "furz":
+    HELPCHECK("furz");
+    GHOSTCHECK("Du laesst einen fahren - aber er riecht nach gar nix.\n",
+      gname()+" laesst einen fahren. Man riecht aber nix.\n", 0);
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Furze wie?\n");
+    out_sel="Du furzt"+(adverb ? "@@adverb@@." : " hemmungslos.");
+    out_oth="@@name@@ laesst@@adverb@@ einen Stinkefurz fahren.";
+    ofoo=clone_object("/items/furz");
+    ofoo->set_furzer(this_player());
+    ofoo->move(environment(this_player()));
+    return FeelIt();
+// DEBUG Furz testen!
+
+    /**************** Gaehnen ***************/
+    case "gaehn":
+    HELPCHECK("gaehn");
+    if (ghost())
+      Return("Als Geist wirst Du nicht muede - also nicht gaehnen.\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Wie willst Du gaehnen?\n");
+    if (!adverb)
+      out_sel="Kannst Du aber Dein(en) Mund/Maul/Schnabel weit aufreissen!";
+    else
+      out_sel="Du gaehnst@@adverb@@.";
+    out_oth="@@gname@@ gaehnt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Glucksen ***************/
+    case "glucks":
+    HELPCHECK("glucks");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Gluckse wie?\n");
+    out_sel="Du gluckst"+(adverb ? "@@adverb@@." : " wie ein Huhn.");
+    out_oth="@@gname@@ gluckst"+(adverb ? "@@adverb@@." : " wie ein Huhn.");
+    return FeelIt();
+
+    /**************** Gratulieren ***************/
+    case "gratulier":
+    case "beglueckwuensch":
+    HELPCHECK("gratulier");
+    ParseRest(str);
+    if (!who)
+      Return("Wem willst Du gratulieren?\n");
+    if (CheckLife(NOT_SELF, NOT_DEAD,
+      "Na, meinst Du nicht, dass Eigenlob stinkt?",
+      "Soll ich dem Ding vielleicht zum Totsein gratulieren? Nee nee."))
+        return 1;
+    out_sel="Du gratulierst @@wem@@@@adverb@@.";
+    out_vic="@@gname@@ gratuliert Dir@@adverb@@.";
+    out_oth="@@gname@@ gratuliert @@wem@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Grinsen ***************/
+    case "grins":
+    HELPCHECK("grins");
+    GHOSTCHECK("Als Du grinst, siehst Du regelrecht, wie die anderen eine "
+        +"Gaensehaut bekommen.\n",
+      "Du bekommst eine Gaensehaut, als der Geist von "+capname()
+        +" zu grinsen anfaengt.\n", 0);
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du grinst @@alle@@@@adverb@@ an.";
+      out_vic="@@name@@ grinst @@alle@@@@adverb@@ an.";
+      return MixedOut(WEN);
+    }
+    if (!who && !adverb && str)
+      Return("Grinsen - schoen und gut. Aber wen oder wie (an)grinsen?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du grinst Dir was, aber so, dass es kein anderer sieht.",
+      "Nicht mal einen Spiegel darf man hier angrinsen, nur Lebewesen!"))
+        return 1;
+    out_sel="Du grinst@@ wen@@@@adverb@@"+(who ? " an" : "")+".";
+    if (who) out_vic="@@name@@ grinst Dich@@adverb@@ an.";
+    out_oth="@@name@@ grinst@@ wen@@@@adverb@@"+(who ? " an" : "")+".";
+    return FeelIt();
+
+    /**************** Gruebeln ***************/
+    case "gruebel":
+    case "gruebl":
+    HELPCHECK("gruebel");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Grueble wie?\n");
+    out_sel="Du gruebelst@@adverb@@ eine Weile vor Dich hin.";
+    out_oth="@@gname@@ gruebelt@@adverb@@ eine Weile vor sich hin.";
+    return FeelIt();
+
+    /**************** Grummeln ***************/
+    case "grummel":
+    case "grumml":
+    HELPCHECK("grummel");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Grummle wie?\n");
+    out_sel="Du grummelst@@adverb@@.";
+    out_oth="@@gname@@ grummelt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Grunzen ***************/
+    case "grunz":
+    HELPCHECK("grunz");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Grunze wie?\n");
+    out_sel="Du grunzt@@adverb@@.";
+    out_oth="@@gname@@ grunzt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Gucken ***************/
+    case "guck":
+    HELPCHECK("guck");
+    ParseRest(str);
+    if (!adverb)
+      Return("Gucke wie aus der Waesche?\n");
+    out_sel="Du guckst@@adverb@@ aus der Waesche.";
+    out_oth="@@gname@@ guckt@@adverb@@ aus der Waesche.";
+    return FeelIt();
+
+    /**************** Jammern ***************/
+    case "jammer":
+    HELPCHECK("jammer");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Wie willst Du jammern?\n");
+    out_sel="Du jammerst@@adverb@@.";
+    out_oth="@@gname@@ jammert@@adverb@@.";
+    return FeelIt();
+
+    /**************** Haetscheln ***************/
+    case "haetschel":
+    case "haetschl":
+    HELPCHECK("haetschel");
+    GHOSTCHECK("Du ueberlegst es Dir anders - mit Deinen durchlaessigen "
+        +"Haenden...",
+      gname()+" will anscheinend jemand haetscheln, ueberlegt\n"
+        +"es sich nach einem kurzen Blick auf seine Haende anders.\n", 0);
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du haetschelst @@alle@@@@adverb@@.";
+      out_vic="@@name@@ haetschelt @@alle@@@@adverb@@.";
+      return MixedOut(WEN);
+    }
+    if (!str || !who)
+      Return("Wen willst Du haetscheln?\n");
+    if (who && CheckLife(NOT_SELF, NOT_DEAD,
+      "Das sieht viel zu albern aus - Du laesst es bleiben.",
+      "Ist da ueberhaupt was zu haetscheln? Nein, da lebt doch nix."))
+        return 1;
+    out_sel="Du haetschelst@@ wen@@@@adverb@@.";
+    out_vic="@@name@@ haetschelt Dich@@adverb@@.";
+    out_oth="@@name@@ haetschelt@@ wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Hicksen ***************/
+    case "hicks":
+    HELPCHECK("hicks");
+    GHOSTCHECK("Hoppla! Dieser Hickser zieht Dich ganz schoen zusammen!\n",
+      gname()+" hat anscheinend Schluckauf.\n"
+        +"Und was fuer einen! Fuer einen Moment zieht es "+QueryPronoun(WEN)
+        +" ziemlich zusammen.\n", 0);
+    if (!str)  {
+      out_sel="Hicks!";
+      out_oth="@@name@@ muss hicksen. Wahrscheinlich zu viel Alkohol...";
+    }
+    else  {
+      ParseRest(str);
+      if (!adverb)
+        Return("Hickse wie?\n");
+      out_sel="Du hickst@@adverb@@.";
+      out_oth="@@name@@ hickst@@adverb@@.";
+    }
+    return FeelIt();
+
+    /**************** Huepfen ***************/
+    case "huepf":
+    HELPCHECK("huepf");
+    GHOSTCHECK("Du schwebst durch die Gegend.\n",
+      gname()+" schwebt durch die Gegend.\n", 0);
+    if (!str)  {
+      out_sel="B O I N G !! Du huepfst in der Gegend herum.";
+      out_oth="@@name@@ huepft in der Gegend herum.";
+    }
+    else  {
+      ParseRest(str);
+      if (!who && !adverb)
+        Return("Huepfe wie oder um wen oder wie oder was oder haeh?\n");
+      out_sel="Du huepfst@@adverb@@"+(who ? " um@@ wen@@" : "")+" herum.";
+      if (who) out_vic="@@name@@ huepft@@adverb@@ um Dich herum.";
+      out_oth="@@name@@ huepft@@adverb@@"+(who ? " um@@ wen@@" : "")+" herum.";
+    }
+    return FeelIt();
+
+    /**************** Husten ***************/
+    case "hust":
+    HELPCHECK("hust");
+    GHOSTCHECK("Du verstreust ein paar Geisterbazillen im Raum.\n",
+      gname()+" macht ufff, ufff und verteilt ein paar Geister-\n"
+        +"bazillen im Raum.\n", 0);
+    if (!str)  {
+      out_sel="Hust! Keuch! Halt dir doch wenigstens die Hand vor den Mund!";
+      out_oth="@@name@@ hustet sich fast die Seele aus dem Leib.";
+    }
+    else  {
+      ParseRest(str);
+      if (!who && !adverb)
+        Return("Wenn Du schon was hinter huste tippst, dann bitte was "
+          +"vernuenftiges!\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Dir selber koennen nur andere was husten.",
+        "Bitte huste nur Lebewesen was."))
+          return 1;
+      out_sel="Du hustest@@ wem@@@@adverb@@"+(who? " was" : "")+".";
+      if (who) out_vic="@@name@@ hustet Dir@@adverb@@was.";
+      out_oth="@@name@@ hustet@@ wem@@@@adverb@@"+(who? " was" : "")+".";
+    }
+    return FeelIt();
+
+    /**************** Jubeln ***************/
+    case "jubel":
+    case "jubl":
+    HELPCHECK("jubel");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Juble wie? Oder wem zu?\n");
+    out_sel="Du jubelst@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+    if (who) out_vic="@@gname@@ jubelt Dir@@adverb@@ zu.";
+    out_oth="@@gname@@ jubelt@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+    return FeelIt();
+
+    /**************** Keuchen ***************/
+    case "keuch":
+    HELPCHECK("keuch");
+    if (ghost())
+      Return("Als Geist strengt Dich nix an - also wird auch nicht "
+        +"gekeucht.\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Keuche wie?\n");
+    out_sel="Du keuchst"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+    out_oth="@@name@@ keucht"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+    return FeelIt();
+
+    /**************** Kichern ***************/
+    case "kicher":
+    HELPCHECK("kicher");
+    if (!str)  {
+      out_sel="Du kicherst. (Wie albern von Dir)";
+      out_oth="@@gname@@ gibt ein albernes Kichern von sich.";
+    }
+    else  {
+      ParseRest(str);
+      if (!who && !adverb)
+        Return("Das haut so nicht hin, gib vernuenftige Parameter.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "In diesem Fall nimm bitte nur kicher.",
+        "Musst schon etwas Lebendes angeben, nichts Totes."))
+          return 1;
+      out_sel="Du kicherst@@adverb@@"+(who ? " hinter "+who->name(WESSEN)+
+        " Ruecken." : ".");
+      if (who)  out_vic="Jemand kichert@@adverb@@ hinter deinem Ruecken.";
+      out_oth="@@gname@@ kichert@@adverb@@"+(who ? " hinter "+who->name(WESSEN)
+        +" Ruecken." : ".");
+    }
+    return FeelIt();
+
+    /**************** Kitzeln ***************/
+    case "kitzel":
+    case "kitzl":
+    HELPCHECK("kitzel");
+    GHOSTCHECK("Mit Deinen immateriellen Fingern schaffst Du das nicht.\n",
+      gname()+" muss gerade feststellen, dass man mit\n"
+        +"immateriellen Fingern nicht kitzeln kann.\n", 0);
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du kitzeln?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du bist doch kein Masochist! Du laesst es bleiben.",
+      "Dinge sind so selten kitzlig. Lass es bleiben."))
+        return 1;
+    if (member(({"highlander","hobo"}), who->query_real_name())>-1)
+      switch (who->query_real_name())  {
+        case "highlander": str1="unter"; str2="Federn"; break;
+        case "hobo"      : str1="an";    str2="Kinn"; break;
+      }
+    else if (who->QueryProp(P_RACE))
+      switch (lower_case(who->QueryProp(P_RACE)))  {
+        case "drache" : str1="unter";str2="Schuppen";
+          t_g=FEMALE; t_n=PLURAL; break;
+        case "greif"  : str1="unter";str2="Federn";
+          t_g=FEMALE; t_n=PLURAL; break;
+        default       : str1="an"; str2="Kinn"; t_g=NEUTER; t_n=SINGULAR;
+      }
+    else  {
+      str1="an"; str2="Kinn"; t_g=NEUTER; t_n=SINGULAR;
+    }
+    if (getuid(who)=="trest" || getuid(who)=="woelkchen")  {
+      str1="an"; str2="Fuessen"; t_g=MALE; t_n=PLURAL;
+    }
+    out_sel="Du kitzelst@@ wen@@@@adverb@@ "+str1+" "+who->QPP(t_g,WEM,t_n)
+      +" "+str2+".\n@@wer@@ versucht, sich zu beherrschen, muss aber "
+      +"doch lachen.";
+    out_vic="@@name@@ kitzelt Dich@@adverb@@ "+str1+" Deine"
+      +(t_n ? "n" : (t_g==FEMALE ? "r" : "m"))+" "+str2
+      +".\nDu versuchst, Dich zu beherrschen, musst aber doch lachen.";
+    out_oth="@@name@@ kitzelt@@ wen@@@@adverb@@ "+str1+" "
+      +who->QPP(t_g,WEM,t_n)+" "+str2
+      +".\n@@wer@@ versucht, sich zu beherrschen, muss aber doch lachen.";
+    return FeelIt();
+
+    /**************** Klatschen ***************/
+    case "klatsch":
+    HELPCHECK("klatsch");
+    GHOSTCHECK("Deine Haende sausen durcheinander durch.\n",
+      gname()+" will in die Haende klatschen - aber sie\n"
+        +"sausen durcheinander durch.\n", 0);
+    ParseRest(str);
+    if (!adverb && str)
+      Return("Klatsche wie?\n");
+    out_sel="Du klatschst@@adverb@@ in die Haende.";
+    out_oth="@@name@@ klatscht@@adverb@@ in die Haende.";
+    return FeelIt();
+
+    /**************** Klopfen ***************/
+    case "klopf":
+    HELPCHECK("klopf");
+    if (!str||sscanf(str,"%s auf schulter",sfoo)!=1)
+      if (!str||sscanf(str,"%s auf die schulter",sfoo)!=1)
+        Return("Klopfe wie wem wieso was?\n");
+    if (ghost())
+      Return("Das geht leider nicht mit durchlaessigen Haenden.\n");
+    str=sfoo;
+    if (str=="") str=0;
+    ParseRest(str);
+    if (!who)
+      Return("Klopfe wem auf die Schulter?\n");
+    out_sel="Du klopfst @@wem@@@@adverb@@ auf die Schulter.";
+    out_vic="@@name@@ klopft Dir@@adverb@@ auf die Schulter.";
+    out_oth="@@name@@ klopft @@wem@@@@adverb@@ auf die Schulter.";
+    return FeelIt();
+
+    /**************** Knabbern ***************/
+    case "knabber":
+    HELPCHECK("knabber");
+    if (ghost())
+      Return("Sorry, aber dafuer fehlt Dir im Moment der noetige "
+        +"\"Biss\"...\n");
+    ParseRest(str);
+    if (!who)
+      Return("Knabbere wen an?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Du kommst nicht an dein eigenes Ohr ran...",
+      "Noe, noe, das schmeckt bestimmt nicht gut."))
+        return 1;
+    out_sel="Du knabberst@@adverb@@ an "+who->name(WESSEN)+" Ohr.";
+    out_vic="@@name@@ knabbert@@adverb@@ an Deinem Ohr.";
+    out_oth="@@name@@ knabbert@@adverb@@ an "+who->name(WESSEN)+" Ohr.";
+    return FeelIt();
+
+    /**************** Knicksen ***************/
+    case "knicks":
+    HELPCHECK("knicks");
+    GHOSTCHECK("Du knickst in der Mitte ab, kriegst Dich aber schnell wieder "
+        +"zusammen.\n",
+      gname()+" knick(s)t in der Mitte ab, kriegt sich aber\n"
+        +"zum Glueck schnell wieder zusammen.\n", 0);
+    if (!str) {
+      out_sel="Du machst einen anmutigen Knicks.";
+      out_oth="@@name@@ macht einen anmutigen Knicks.";
+    }
+    else  {
+      ParseRest(str,"vor");
+      if (for_all)  {
+        out_sel="Du knickst@@adverb@@ vor @@alle@@.";
+        out_vic="@@name@@ knickst@@adverb@@ vor @@alle@@.";
+        return MixedOut(WEM);
+      }
+      if (!who && !adverb)
+        Return("Knickse irgendwie oder vor jemandem.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Wie willst Du das denn schaffen?",
+        "Vor Sachen wird hier nicht geknickst!"))
+          return 1;
+      out_sel="Du knickst@@adverb@@"+(who ? " vor" : "")+"@@ wem@@.";
+      if (who ) out_vic="@@name@@ knickst@@adverb@@ vor Dir.";
+      out_oth="@@name@@ knickst@@adverb@@"+(who ? " vor" : "")+"@@ wem@@.";
+    }
+    return FeelIt();
+
+    /**************** Knirschen ***************/
+    case "knirsch":
+    HELPCHECK("knirsch");
+    if (ghost())
+      Return("Du kannst mit nichts knirschen, so als Geist. Versuche doch "
+        +"dafuer\nmal, zu rasseln...\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Knirsche wie?\n");
+    switch (QueryProp(P_RACE))  {
+      case "greif"  : str1="dem Schnabel."; break;
+      case "sandtiger" : str1="den Fangzaehnen."; break;
+      case "drache" : str1="den Fangzaehnen."; break;
+      default       : str1="den Zaehnen.";
+    }
+    out_sel="Du knirschst@@adverb@@ mit "+str1;
+    out_oth="@@name@@ knirscht@@adverb@@ mit "+str1;
+    return FeelIt();
+
+    /**************** Knuddeln ***************/
+    case "knuddel":
+    case "knuddl":
+    HELPCHECK("knuddel");
+    if (ghost())
+      Return("Sorry, nicht als Geist.\n");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du knuddelst @@alle@@@@adverb@@.";
+      out_vic="@@name@@ knuddelt @@alle@@@@adverb@@.";
+      return MixedOut(WEN);
+    }
+    if (!who)
+      Return("Knuddle wen?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Das bringt doch nix, lass es halt.",
+      "Du kannst soviel ich weiss ausser Lebewesen nur Teddys knuddeln."))
+        return 1;
+    out_sel="Du knuddelst@@ wen@@@@adverb@@.";
+    out_vic="@@name@@ knuddelt Dich@@adverb@@.";
+    out_oth="@@name@@ knuddelt@@ wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Knurren ***************/
+    case "knurr":
+    HELPCHECK("knurr");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du knurrst @@alle@@@@adverb@@ an.";
+      out_vic="@@gname@@ knurrt @@alle@@@@adverb@@ an.";
+      return MixedOut(WEN);
+    }
+    if (str && !who && !adverb)
+      Return("Wen anknurren oder wie knurren?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du knurrst in Dich hinein.",
+      "Reagiert nicht. Solltest wohl besser Lebwesen anknurren."))
+        return 1;
+    out_sel="Du knurrst@@ wen@@@@adverb@@"+(who ? " an." : ".");
+    if (who) out_vic="@@gname@@ knurrt Dich@@adverb@@ an.";
+    out_oth="@@gname@@ knurrt@@ wen@@@@adverb@@"+(who ? " an." : ".");
+    return FeelIt();
+
+    /****************  Knutschen ***************/
+    case "knutsch":
+    HELPCHECK("knutsch");
+    if (ghost())
+      Return("Das kannst Du als Geist leider nicht. Irgendwie fehlt Dir "
+        +"dazu das Herz.\n");
+    ParseRest(str);
+    if (!who)
+      Return("Knutsche wen ab?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Das geht nicht.",
+      "Igitt! Lieber nicht!"))
+        return 1;
+    out_sel="Du gibst @@wem@@@@adverb@@ einen RICHTIGEN Kuss.";
+    out_vic="@@name@@ gibt Dir@@adverb@@ einen tiefen und hingebungsvollen "
+      +"Kuss.\nDu schwebst im 7. Himmel.";
+    out_oth="@@name@@ gibt @@wem@@@@adverb@@ einen tiefen und "
+      +"hingebungsvollen Kuss.";
+    return FeelIt();
+
+    /**************** Kotzen ***************/
+    case "kotz":
+    HELPCHECK("kotz");
+    if (ghost())
+      Return("Ne, das ist eins von den Sachen, die als Geist nicht gehen.\n");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Kotze wie? Kotze auf wen?\n");
+    if (who && CheckLife(NOT_SELF,0,
+      "Igitt, nein danke."))
+        return 1;
+    if(!str) {
+      out_sel="Du kotzt ueber deine Schuhe.";
+      out_oth="@@name@@ verdreht die Augen und kotzt.";
+    }
+    else  {
+      out_sel="Du kotzt@@adverb@@"+(who ? " auf @@wen@@." : ".");
+      if (who) out_vic="@@name@@ kotzt@@adverb@@ auf Dich.";
+      out_oth="@@name@@ kotzt@@adverb@@"+(who ? " auf @@wen@@." : ".");
+    }
+    return FeelIt();
+
+    /**************** Kratzen ***************/
+    case "kratz":
+    HELPCHECK("kratz");
+    ParseRest(str);
+    if (who && (who!=this_player()))
+      Return("Das mach mal schoen nur mit Dir selber.\n");
+    if (str && !adverb)
+      Return("Wie willst Du Dich kratzen?\n");
+    out_sel="Du kratzt dich@@adverb@@ am Kopp.";
+    out_oth="@@gname@@ kratzt sich@@adverb@@ am Kopp.";
+    return FeelIt();
+
+    /**************** Krieche ***************/
+    case "kriech":
+    HELPCHECK("kriech");
+    ParseRest(str);
+    out_sel="Du kriechst"+(who ? " vor @@wem@@" : "")+"@@adverb@@ im Staub.";
+    if (who) out_vic="@@gname@@ kriecht@@adverb@@ vor Dir im Staub.";
+    out_oth="@@gname@@ kriecht"+(who ? " vor @@wem@@" : "")
+      +"@@adverb@@ im Staub.";
+    return FeelIt();
+
+    /**************** Kuessen ***************/
+    case "kuess":
+    HELPCHECK("kuess");
+    if (ghost())
+      Return("Als Geist kannst Du leider niemanden kuessen.\n");
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du kuessen?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Da hast Du aber Schwierigkeiten... Du gibst es schliesslich auf.",
+      "Nix. Absolut nix. Kuesse lieber Lebewesen - die reagieren\n"
+        +"wenigstens (und sei es, dass sie Dich fressen...)."))
+        return 1;
+    out_sel="Du kuesst@@ wen@@@@adverb@@.";
+    out_vic="@@name@@ kuesst Dich@@adverb@@.";
+    out_oth="@@name@@ kuesst@@ wen@@@@adverb@@.";
+    FeelIt();
+    if (who->QueryProp(P_FROG)&&QueryProp(P_LEVEL)>who->QueryProp(P_LEVEL)) {
+      tell_room(environment(this_player()),"PLOPP!\n");
+      write("Huch! Du wirst auf einmal so gruen und klein und kriegst auf\n"
+        +"einmal furchtbar Hunger auf Fliegen und so...\n");
+      who->Message("Auf einmal wird die Welt um Dich wieder so klein, wie sie\n"
+        +" frueher mal war - und vor Dir sitzt ein kleiner gruener Frosch.\n");
+      say(who->name(WER)+" steht auf einmal da und schaut dumm aus der "
+        +"Waesche. Dafuer fehlt\njetzt seltsamerweise "+capname()
+        +". Die Gesamtzahl an kleinen gruenen\nFroeschen im Raum hat sich "
+        +"jedoch nicht geaendert...\n",({who,this_player()}));
+      who->SetProp(P_FROG,0);
+      SetProp(P_FROG,1);
+    }
+    return 1;
+
+    /**************** Kuscheln ***************/
+    case "kuschel":
+    case "kuschl":
+    HELPCHECK("kuschel");
+    GHOSTCHECK("Dazu bist Du als Geist viel zu kalt und gar "
+        +"schroecklich anzusehen.\n",
+      gname()+" scheint Anwandlungen zu haben, sich an jemand "
+        +"ankuscheln zu wollen.\nEntsetzt springen alle zurueck, weil "
+        +"dazu ist er doch zu kalt und schroecklich\nanzusehen.\n", 0);
+    ParseRest(str);
+    if (!who)
+      Return("An wen willst Du Dich ankuscheln?\n");
+    out_sel="Du kuschelst Dich@@adverb@@ an @@wen@@ an.";
+    out_vic="@@name@@ kuschelt sich@@adverb@@ an Dich an.";
+    out_oth="@@name@@ kuschelt sich@@adverb@@ an @@wen@@ an.";
+    return FeelIt();
+
+    /**************** Lachen ***************/
+    case "lach":
+    HELPCHECK("lach");
+    GHOSTCHECK("Du lachst mit hohler Stimme. Den Umstehenden (sind doch welche"
+        +" da, oder?)\nlaeuft es kalt den Ruecken runter.\n",
+      gname()+" lacht mit hohler Stimme.\nDir laeuft es eiskalt den Ruecken"
+        +" runter.\n", 0);
+    if (!str)  {
+      out_sel="Du brichst vor Lachen zusammen.";
+      out_oth="@@name@@ bricht vor Lachen zusammen.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du lachst@@adverb@@ ueber @@alle@@.";
+        out_vic="@@name@@ lacht@@adverb@@ ueber @@alle@@.";
+        return MixedOut(WEN);
+      }
+      if (!who && !adverb)
+        Return("Lache wie, lache ueber wen?\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Lach Dich doch nicht selber aus - das machen schon andere...",
+        "Gelacht wird nur ueber Lebewesen (die koennen sich drueber aergern)."))
+          return 1;
+      out_sel="Du lachst@@adverb@@"+(who?" ueber@@ wen@@":"")+".";
+      if (who) out_vic="@@name@@ lacht@@adverb@@ ueber Dich.";
+      out_oth="@@name@@ lacht@@adverb@@"+(who?" ueber@@ wen@@":"")+".";
+    }
+    return FeelIt();
+
+    /**************** Laecheln ***************/
+    case "laechel":
+    case "laechl":
+    HELPCHECK("laechel");
+    if (ghost()) {
+      write("Du laechelst innerlich.\n");
+      return 1;
+    }
+    if(!str) {
+      out_sel="Du laechelst.";
+      out_oth="@@name@@ laechelt.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du laechelst @@alle@@@@adverb@@ an.";
+        out_vic="@@name@@ laechelt @@alle@@@@adverb@@ an.";
+        return MixedOut(WEN);
+      }
+      if (!who && !adverb && str)
+        Return("Wie oder wen?\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Musst schon jemand anders anlaecheln.",
+        "Bitte ein Lebewesen anlaecheln."))
+          return 1;
+      out_sel="Du laechelst@@ wen@@@@adverb@@"+(who ? " an." : ".");
+      if (who) out_vic="@@name@@ laechelt Dich@@adverb@@ an.";
+      out_oth="@@name@@ laechelt@@ wen@@@@adverb@@"+(who ? " an." : ".");
+    }
+    return FeelIt();
+
+    /**************** Liebe ***************/
+    /* These lines appear Courtesy of Angus@MorgenGrauen. So long, and thanks */
+    /* for all the fish, errr, text, Angus :) */
+    case "lieb":
+    HELPCHECK("lieb");
+    if (ghost())
+      Return("Auf diese Freuden musst Du als Geist leider verzichten.\n");
+    ParseRest(str);
+    if (!who)
+      Return("Wen hast Du lieb?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Ja, ich weiss, Du magst Dich, aber das musst Du nicht zur Schau"
+        +"stellen.",
+      "Du entwickelst seltsame Neigungen, finde ich."))
+        return 1;
+    str1=(who->QueryProp(P_GENDER)==FEMALE ? "ihr" : "ihm");
+    /* old version:
+      out_sel="Du fluesterst @@wem@@@@adverb@@ liebevolle Worte ins Ohr.";
+      out_vic=gname()+" fluestert Dir@@adverb@@ liebevolle Worte ins Ohr.";
+      out_oth=gname()+" fluestert@@adverb@@ sanfte Worte zu @@wem@@.";
+    */
+    out_sel="Du schliesst die Augen, schmiegst Dich eng an @@wen@@ und gibst"
+      +LF+str1+" einen zaertlichen und leidenschaftlichen Kuss.\n"
+      +"Um Dich herum versinkt die Welt und Du glaubst, auf Wolken zu "
+      +"schweben.";
+    out_vic="@@name@@ drueckt Dich zaertlich an sich und gibt Dir\n"
+      +"einen zaertlichen und leidenschaftlichen Kuss. Du schliesst die\n"
+      +"Augen und traeumst ein wenig......Du schwebst auf Wolken direkt\n"
+      +"in den siebten Himmel.";
+    out_oth="Du schaust dezent weg, als sich @@name@@ und "+who->name()
+      +" verliebt in die Arme\nsinken.";
+    return FeelIt();
+
+    /**************** Loben ***************/
+    case "lob":
+    HELPCHECK("lob");
+    if (!str)
+      Return("Wen oder was willst Du loben?\n");
+    ParseRest(str);
+    if (who==ME)  {
+      ofoo=clone_object("/items/furz");
+      ofoo->set_furzer(this_player());
+      ofoo->set_eigenlob();
+      ofoo->move(environment(this_player()));
+      //DEBUG Furz zum Eigenlob patchen :>
+      out_sel="Du lobst Dich selber@@adverb@@. Die Folgen kennst Du ja...";
+      out_oth="@@gname@@ lobt sich selber@@adverb@@, mit den bekannten Folgen.";
+    } else if (who) {
+      out_sel="Du lobst @@wen@@@@adverb@@.";
+      out_vic="@@gname@@ lobt Dich@@adverb@@.";
+      out_oth="@@gname@@ lobt @@wen@@@@adverb@@.";
+    } else  {
+      out_sel="Du lobst "+str+".";
+      out_oth="@@gname@@ lobt "+str+".";
+    }
+    return FeelIt();
+
+    /**************** Moppern ***************/
+    case "mopper":
+    HELPCHECK("mopper");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Moppere wie?\n");
+    out_sel="Du mopperst@@adverb@@.";
+    out_oth="@@gname@@ moppert@@adverb@@.";
+    return FeelIt();
+
+    /**************** Mustern ***************/
+    case "muster":
+    HELPCHECK("muster");
+    ParseRest(str);
+    if (!who)
+      Return("Mustere wen?\n");
+    out_sel="Du musterst @@wen@@@@adverb@@.";
+    out_vic="@@gname@@ mustert Dich@@adverb@@.";
+    out_oth="@@gname@@ mustert @@wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Nicken ***************/
+    case "ja":
+    case "nick":
+    HELPCHECK("nick");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du nickst @@alle@@@@adverb@@ zu.";
+      out_vic="@@gname@@ nickt @@alle@@@@adverb@@ zu.";
+      return MixedOut(WEM);
+    }
+    if (str && !who && !adverb)
+      Return("Nicke wie oder wem zu oder wem wie zu?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du willst Dir selber zunicken? Lieber nicht, das sieht so albern aus.",
+      "Hm. Nix passiert. Von Lebewesen bekommt man meistens mehr Feedback."))
+        return 1;
+    out_sel="Du nickst@@ wem@@@@adverb@@"
+      +(who ? " zu." : (adverb ? "." : " zustimmend."));
+    if (who) out_vic="@@gname@@ nickt Dir@@adverb@@ zu.";
+    out_oth="@@gname@@ nickt@@ wem@@@@adverb@@"
+      +(who ? " zu." : (adverb ? "." : " zustimmend."));
+    return FeelIt();
+
+    /**************** Niesen ***************/
+    case "nies":
+    HELPCHECK("nies");
+    if (ghost())
+      Return("Du hast keine Nase mehr, in der es Dich jucken koennte...\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Niese wie?\n");
+    out_sel="Haaaaaa-tschi! Gesundheit!"+(adverb ? " Du niest@@adverb@@." : "");
+    out_oth="Haaaaaa-tschi! @@name@@ niest@@adverb@@.";
+    return FeelIt();
+
+    /**************** Ohrfeigen ***************/
+    case "gib":
+    HELPCHECK("gib");
+    if (!str)
+      Return("Gib wem was?\n");
+    if (sscanf( str,"%s ohrfeige",str1)==0)
+      return 0;
+    ParseRest(str, ({"ohrfeige", "eine ohrfeige"}));
+    if (for_all)  {
+      out_sel="Du verpasst @@alle@@@@adverb@@ eine Ohrfeige.";
+      out_vic="@@name@@ verpasst @@alle@@@@adverb@@ eine Ohrfeige.";
+      return MixedOut(WEM);
+    }
+    if (!who)
+      Return("Gib wem eine Ohrfeige?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Dazu sind Dir Deine Backen doch zu schade.",
+      "Du wirst doch nichts schlagen, was sich nicht wehren kann?"))
+        return 1;
+    GHOSTCHECK("Deine Hand saust mit voller Wucht durch dein Opfer durch!",
+      gname()+" will "+who->name(WEM)+" eine Ohrfeige geben - aber die Hand\n"
+        +"saust mit voller Wucht durch das Opfer durch!", 0);
+    out_sel="Du verpasst @@wem@@@@adverb@@ eine schallende Ohrfeige.";
+    out_vic="@@name@@ verpasst Dir@@adverb@@ eine Watsche, dass Dir Hoeren "
+      +"und Sehen vergeht.";
+    out_oth="@@name@@ verpasst @@wem@@@@adverb@@ eine schallende Ohrfeige.";
+    return FeelIt();
+
+    /**************** Pfeifen ***************/
+    case "pfeif":
+    HELPCHECK("pfeif");
+    GHOSTCHECK("Es kommt leider nur (nicht mal heisse) Luft, aber kein "
+        +"Pfiff.\n",
+      gname()+" spitzt den Mund und pustet angestrengt. Nichts passiert.\n", 0);
+    ParseRest(str, "nach");
+    if (str && !who && !adverb)
+      Return("Pfeife wie? Pfeife wem nach? Haeh?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Was willst Du denn damit ausdruecken? Das gibt fuer mich keinen Sinn.",
+      "Ich habe keine Lust dazu."))
+        return 1;
+    out_sel="Du pfeifst@@ wen@@@@adverb@@"
+      +(who ? " nach." : (adverb ? "." : " anerkennend."));
+    if (who) out_vic="@@name@@ pfeift Dir@@adverb@@ nach.";
+    out_oth="@@name@@ pfeift@@ wen@@@@adverb@@"
+      +(who ? " nach." : (adverb ? "." :" anerkennend."));
+    return FeelIt();
+
+    /**************** Philosophieren ***************/
+    case "philosophier":
+    HELPCHECK("philosophier");
+    ParseRest(str);
+    out_sel="Du philosophierst"+(adverb ? "@@adverb@@." :
+      (str ? " ueber "+str+"." : "."));
+    out_oth="@@gname@@ philosophiert"+(adverb ? "@@adverb@@." :
+      (str ? " ueber "+str+"." : "."));
+    return FeelIt();
+
+    /**************** (Nase) Putzen ***************/
+    case "putz":
+    HELPCHECK("putz");
+    if (ghost())
+      Return("Nix da zum Putzen, so nebuloes, wie Du bist.\n");
+    ParseRest(str, ({"nase", "die nase"}));
+    if (str && str!="nase" && !adverb)
+      Return("Putze Deine Nase wie?\n");
+    out_sel="Du putzt Deine Nase@@adverb@@.";
+    out_oth="@@name@@ putzt@@adverb@@ "+QPP(FEMALE,WEN)+" Nase.";
+    return FeelIt();
+
+    /**************** Quaken ***************/
+    case "quak":
+    HELPCHECK("quak");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Quake wie?\n");
+    sfoo="";
+    flag=QueryProp(P_FROG)&&!ghost();
+    for (t_g=0; t_g<=random(flag ? 4 : 2); t_g++)  {
+      sfoo+=(flag ? " Qu" : " kw");
+      for (t_n=0; t_n<=random(flag ? 10 : 5); t_n++)
+        sfoo+="aA"[random(1)..random(1)];
+      sfoo+="k";
+    }
+    if (!flag)
+      sfoo=lower_case(sfoo);
+    else
+      sfoo+="!";
+    out_sel="Du quakst"+(adverb ? "@@adverb@@" : (flag ? " aus voller Kehle"
+      : " in etwa wie ein Frosch"))+":"+sfoo;
+    out_oth="@@gname@@ quakt"+(adverb ? "@@adverb@@" : (flag ? " aus voller Kehle"
+      : " in etwa wie ein Frosch"))+":"+sfoo;
+    return FeelIt();
+
+    /**************** Quietschen ***************/
+    case "quietsch":
+    case "quiek":
+    HELPCHECK("quiek");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Quietsche wie?\n");
+    out_sel="Du quietschst"+(adverb ? "@@adverb@@." : " vergnuegt.");
+    out_oth="@@gname@@ quietscht"+(adverb ? "@@adverb@@." : " vergnuegt.");
+    return FeelIt();
+
+    /**************** Raeuspern ***************/
+    case "raeusper":
+    HELPCHECK("raeusper");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Hm? Wie meinen?\n");
+    out_sel="Du raeusperst Dich@@adverb@@.";
+    out_oth="@@gname@@ raeuspert sich@@adverb@@.";
+    return FeelIt();
+
+    /**************** Reiben ***************/
+    case "reib":
+    HELPCHECK("reib");
+    if (ghost())
+      Return("Du hast nichts zum Reiben, aber auch gar nichts.\n");
+    if (str && (sscanf(str,"%s die Augen",sfoo)==1 || sscanf(str,"%s Augen",sfoo)==1))
+      str=sfoo;
+    else if (str && (sscanf(str,"%s die Haende",sfoo)==1
+    ||sscanf(str,"%s Haende",sfoo)==1))  {
+      flag=2;
+      str=sfoo;
+    }
+    if (str=="") str=0;
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Reibe wie die "+(flag==2 ? "Haende" : "Augen")+"?\n");
+    out_sel="Du reibst Dir"+(adverb ? "@@adverb@@"
+      : (flag==2 ? " vergnuegt" : " muede"))+" die "
+      +(flag==2 ? "Haende." : "Augen.");
+    out_oth="@@name@@ reibt sich"+(adverb ? "@@adverb@@"
+      : (flag==2 ? " vergnuegt" : " muede"))+" die "
+      +(flag==2 ? "Haende." : "Augen.");
+    return FeelIt();
+
+    /**************** Rotfln ***************/
+    case "rotfl":
+    HELPCHECK("rotfl");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Rotfl wie?\n");
+    out_sel="Du rotflst@@adverb@@.";
+    out_oth="@@gname@@ rotflt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Ruelpsen ***************/
+    case "ruelps":
+    HELPCHECK("ruelps");
+    GHOSTCHECK("Ein leichter Windhauch entfaehrt Deinem Mund, mehr nicht.\n",
+      "Dem Mund des Geistes von "+capname()
+        +" entfaehrt ein leichtes Lueftchen.\n", 0);
+    if (!str)  {
+      out_sel="BOOOOEEERRRRPP!  Entschuldige dich gefaelligst!";
+      out_oth="@@name@@ ruelpst unanstaendig.";
+    }
+    else  {
+      ParseRest(str);
+      if (!adverb)  {
+        write("Ruelpse wie (schlimm genug, dass Du Dich nicht beherrschen "
+          +"kannst!)?\n");
+        return 1;
+      }
+      out_sel="Du ruelpst@@adverb@@. Schaem Dich!";
+      out_oth="@@name@@ ruelpst@@adverb@@ und wird nicht mal rot dabei.";
+    }
+    return FeelIt();
+  }
+
+  switch (vb)  {
+    /**************** Runzeln ***************/
+    case "runzel":
+    case "runzl":
+    HELPCHECK("runzel");
+    if (ghost())
+      Return("DAS geht als Geist nun wirklich nicht.\n");
+    ParseRest(str,"stirn");
+    if (str && !adverb)
+      Return("Runzle die Stirn wie?\n");
+    out_sel="Du runzelst@@adverb@@ die Stirn.";
+    out_oth="@@name@@ runzelt@@adverb@@ die Stirn.";
+    return FeelIt();
+
+    /**************** Sabbere ***************/
+    case "sabber":
+    HELPCHECK("sabber");
+    sfoo=ghost() ? "schleim" : "sabber";
+    ParseRest(str);
+    if (str && !adverb && !who)
+      Return("Sabber wie oder wen an?\n");
+    out_sel="Du "+sfoo+"st@@ wen@@@@adverb@@ "
+      +(who ? "an." : "auf den Boden.");
+    if (who) out_vic="@@gname@@ "+sfoo+"t Dich@@adverb@@ an.";
+    out_oth="@@gname@@ "+sfoo+"t@@ wen@@@@adverb@@ "
+      +(who ? "an." : "auf den Boden.");
+    return FeelIt();
+
+    /**************** Schaemen ***************/
+    case "schaem":
+    HELPCHECK("schaem");
+    ParseRest(str);
+    if (str && !adverb && lower_case(str)!="dich")
+      Return("Schaeme Dich wie?\n");
+    out_sel="Du schaemst Dich@@adverb@@.";
+    out_oth="@@gname@@ schaemt sich@@adverb@@.";
+    return FeelIt();
+
+#ifdef SCHAU_AN
+    /**************** Schau an ***************/
+    case "schau":
+    HELPCHECK("schau");
+    if (!str || old_explode(str, " ")[sizeof(old_explode(str, " "))]!="an")
+      return 0;
+    ParseRest(str, "an");
+    if (!who)
+      Return("Schau wen an?\n");
+    out_sel="Du schaust @@wen@@@@adverb@@ an.";
+    out_vic="@@gname@@ schaut Dich@@adverb@@ an.";
+    out_oth="@@gname@@ schaut @@wen@@@@adverb@@ an.";
+    return FeelIt();
+#endif
+
+    /**************** Schluchzen ***************/
+    case "schluchz":
+    HELPCHECK("schluchz");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schluchze wie?\n");
+    out_sel="Du schluchzt"+(adverb ? "@@adverb@@." : " herzzerreissend.");
+    out_oth="@@gname@@ schluchzt"
+      +(adverb ? "@@adverb@@." : " herzzerreissend.");
+    return FeelIt();
+
+    /**************** Schmollen ***************/
+    case "schmoll":
+    HELPCHECK("schmoll");
+    GHOSTCHECK("Du schwebst beleidigt in die Ecke.\n",
+      gname()+" schwebt beleidigt in die Ecke und schmollt.\n", 0);
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schmolle wie?\n");
+    out_sel="Du schmollst@@adverb@@.";
+    out_oth="@@name@@ geht in die Ecke und schmollt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Schmunzeln ***************/
+    case "schmunzel":
+    case "schmunzl":
+    HELPCHECK("schmunzel");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schmunzle wie?\n");
+    out_sel="Du schmunzelst@@adverb@@.";
+    out_oth="@@gname@@ schmunzelt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Schnalzen ***************/
+    case "schnalz":
+    HELPCHECK("schnalz");
+    ParseRest(str, ({"zunge","mit zunge", "mit der zunge"}));
+    out_sel="Du schnalzt@@adverb@@ mit der Zunge.";
+    out_oth="@@gname@@ schnalzt@@adverb@@ mit der Zunge.";
+    return FeelIt();
+
+    /**************** Schnauben ***************/
+    case "schnaub":
+    HELPCHECK("schnaub");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schnaube wie?\n");
+    out_sel="Du schnaubst"+(adverb ? "@@adverb@@." : " entruestet.");
+    out_oth="@@gname@@ schnaubt"+(adverb ? "@@adverb@@." : " entruestet.");
+    return FeelIt();
+
+    /**************** Schnaufen ***************/
+    case "schnauf":
+    HELPCHECK("schnauf");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schnaufe wie?\n");
+    out_sel="Du schnaufst"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+    out_oth="@@gname@@ schnauft"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+    return FeelIt();
+
+    /**************** Schnippen ***************/
+    case "schnipp":
+    case "schnipps":
+    HELPCHECK("schnipp");
+    GHOSTCHECK("Du schaffst es nicht, weil die Finger durcheinander durch "
+        +"gehen.\n",
+      gname()+" versagt beim Schnippen - die Finger\ngehen durcheinander "
+        +"durch.\n", 0);
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schnippe wie?\n");
+    out_sel="Du schnippst@@adverb@@ mit deinen Fingern.";
+    out_oth="@@name@@ schnippt@@adverb@@ mit den Fingern.";
+    return FeelIt();
+
+    /**************** Schnarchen ***************/
+    case "schnarch":
+    HELPCHECK("schnarch");
+    if (ghost())
+      Return("Ich glaube, da fehlen Dir irgendwie die physischen "
+        +"Voraussetzungen dazu.\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schnarche wie?\n");
+    out_sel=(str ? "Zzzzzzzzzzz..." : "Du schnarchst@@adverb@@.");
+    out_oth="@@name@@ schnarcht "+(str ? "@@adverb@@." : "laut.");
+    return FeelIt();
+
+    /**************** Schniefen ***************/
+    case "snief":
+    case "schnief":
+    HELPCHECK("schnief");
+    GHOSTCHECK("Du schniefst ganz leise.\n",
+      gname()+" schnieft ganz leise.\n", 0);
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schniefe wie?\n");
+    out_sel="Du schniefst@@adverb@@.";
+    out_oth="@@name@@ schnieft@@adverb@@.";
+    return FeelIt();
+
+    /**************** Schnurren ***************/
+    case "schnurr":
+    HELPCHECK("schnurr");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Wie willst Du schnurren?\n");
+    out_sel="MMMMIIIIIAAAAAAUUUUUUUU! Du schnurrst"
+      +(adverb ? "@@adverb@@." : " zufrieden.");
+    out_oth="@@gname@@ schnurrt"+(adverb ? "@@adverb@@." : " zufrieden.");
+    return FeelIt();
+
+    /**************** Schreien ***************/
+    case "schrei":
+    HELPCHECK("schrei");
+    GHOSTCHECK("AAAAIIIIIIIIIIIEEEEEEEEEEEEEEEEEEEEEEEEEE! Ja, nur Geister "
+        +"koennen so schreien!\n",
+      gname()+" schreit - das Blut gefriert fast in deinen Ader!\n", 0);
+    if (!str)  {
+      out_sel="AUUUAAAHHHHHH!!!!";
+      out_oth="@@name@@ schreit laut!";
+    }
+    else  {
+      ParseRest(str);
+      if (!who && !adverb)
+        Return("Schreien - wie denn? Oder wen denn anschreien?\n");
+      out_sel="Du schreist@@ wen@@@@adverb@@"+(who ? " an" : "")+".";
+      if (who) out_vic="@@name@@ schreit Dich@@adverb@@ an.";
+      out_oth="@@name@@ schreit@@ wen@@@@adverb@@"+(who? " an" : "")+".";
+    }
+    return FeelIt();
+
+    /**************** Schuetteln ***************/
+    case "schuettel":
+    case "schuettl":
+    HELPCHECK("schuettel");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du schuettelst @@alle@@@@adverb@@ die Haende.";
+      out_vic="@@gname@@ schuettelt @@alle@@@@adverb@@ die Haende.";
+      return MixedOut(WEM);
+    }
+    if (str && !who && !adverb)
+      Return("Schuettle wie? Schuettle wem die Hand?\n");
+    if(!who) {
+      out_sel="Du schuettelst Dich@@adverb@@.";
+      out_oth="@@gname@@ schuettelt sich@@adverb@@.";
+    }
+    else  {
+      if (CheckLife(0,NOT_DEAD,
+        "", "Noe, das mach ich nur mit Lebewesen."))
+          return 1;
+      if (who == this_player())  {
+        out_sel="Du hebst"+(adverb ? "@@adverb@@" : " triumphierend")
+          +" Deine Haende ueber den Kopf und schuettelst sie.";
+        out_oth="@@gname@@ hebt"+(adverb ? "@@adverb@@" : " triumphierend")
+          +" die Haende ueber den Kopf\nund schuettelt sie.";
+      }
+      else  {
+        out_sel="Du schuettelst@@ wem@@@@adverb@@ die Haende.";
+        if (ghost()) out_sel+="\nNaja, Du versuchst es wenigstens - "
+          +"immer diese durchlaessigen Haende...";
+        out_vic="@@gname@@ schuettelt Dir@@adverb@@ die Haende.";
+        if (ghost()) out_vic+="\nNaja, Du versuchst es wenigstens - "
+          +"immer diese durchlaessigen Haende...";
+        out_oth="@@gname@@ schuettelt@@ wem@@@@adverb@@ die Haende.";
+        if (ghost()) out_oth+="\nNaja, Du versuchst es wenigstens - "
+          +"immer diese durchlaessigen Haende...";
+      }
+    }
+    return FeelIt();
+
+    /**************** Schweigen ***************/
+    case "schweig":
+    HELPCHECK("schweig");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schweige wie?\n");
+    out_sel="Du schweigst@@adverb@@.";
+    out_oth="@@gname@@ schweigt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Seufzen ***************/
+    case "seufz":
+    HELPCHECK("seufz");
+    GHOSTCHECK("Du seufzt geisterhaft.\n",
+      gname()+" seufzt geisterhaft. Naja, wie denn sonst?\n", 0);
+    ParseRest(str);
+    if (!adverb && str)
+      Return("Seufze wie?\n");
+    out_sel="Du seufzst@@adverb@@.";
+    out_oth="@@name@@ seufzt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Singen ***************/
+    case "sing":
+    HELPCHECK("sing");
+    if (!str) {
+      out_sel="Oh sole mio!";
+      out_oth="@@gname@@ singt irgendwas italienisches.";
+    }
+    else  {
+      ParseRest(str);
+      out_sel="Du singst@@adverb@@"+(adverb ? "." : " '"+capitalize(str)+"'.");
+      out_oth="@@gname@@ singt@@adverb@@"+(adverb ? "." : " '"
+        +capitalize(str)+"'.");
+    }
+    return FeelIt();
+
+    /**************** Sniffen ***************/
+    case "sniff":
+    HELPCHECK("sniff");
+    ParseRest(str);
+    if (str && !adverb && !who)
+      Return("Sniffe wie?\n");
+    out_sel="Du sniffst"+(who ? " @@wen@@" : "")
+      +(adverb ? "@@adverb@@" : " traurig")+(who ? " an." : ".");
+    if (who) out_vic="@@gname@@ snifft Dich"
+      +(adverb ? "@@adverb@@" : " traurig")+" an.";
+    out_oth="@@gname@@ snifft"+(who ? " @@wen@@" : "")
+      +(adverb ? "@@adverb@@" : " traurig")+(who ? " an." : ".");
+    return FeelIt();
+
+    /**************** Spucken ***************/
+    case "spuck":
+    HELPCHECK("spuck");
+    GHOSTCHECK("Du bringst nicht genug Spucke zusammen.\n",
+      gname()+" stellt gerade fest, dass man ohne Spucke nicht\n"
+        +"spucken kann.\n", 0);
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Spucke wen wie an?\n");
+    if (who && CheckLife(NOT_SELF,0,
+      "Hast Du Dich so schlecht benommen? Lass es lieber bleiben."))
+        return 1;
+    out_sel="Du spuckst@@ wen@@@@adverb@@ "+(who ? "an." : "auf den Boden.");
+    if (who) out_vic="@@name@@ spuckt Dich@@adverb@@ an.";
+    out_oth="@@name@@ spuckt@@ wen@@@@adverb@@ "
+      +(who ? "an." : "auf den Boden.");
+    return FeelIt();
+
+    /**************** Stampfen ***************/
+    case "stampf":
+    HELPCHECK("stampf");
+    ParseRest(str, "auf");
+    out_sel="Du stampfst@@adverb@@ mit dem Fuss auf.";
+    out_oth="@@gname@@ stampft@@adverb@@ mit dem Fuss auf.";
+    return FeelIt();
+
+    /**************** Starren ***************/
+    case "starr":
+    HELPCHECK("starr");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Starre wie bzw. wen an?\n");
+    if (who && CheckLife(NOT_SELF,0,
+      "Wie willst Du in Deine eigenen Augen starren? "
+        +"(Spiegel gelten nicht...)"))
+        return 1;
+    out_sel="Du starrst"+(!str ? " ins Leere." : (who ? "@@ wen@@" : "")
+      +(adverb ? "@@adverb@@" : " vertraeumt")+(who ? " an." : "."));
+    if (who) out_vic="@@gname@@ starrt"+(adverb ? "@@adverb@@" : " tief")
+      +" in Deine Augen.";
+    out_oth="@@gname@@ starrt"+(!str ? " ins Leere." : (who ? "@@ wen@@" : "")
+      +(adverb ? "@@adverb@@" : " vertraeumt")+(who ? " an." : "."));
+    return FeelIt();
+
+    /**************** Staunen ***************/
+    case "staun":
+    HELPCHECK("staun");
+    if (!str)  {
+      out_sel="Du bist erstaunt.";
+      out_oth="@@gname@@ ist erstaunt.";
+    }
+    else  {
+      ParseRest(str, "ueber");
+      if (!who && !adverb)
+        Return("Bla bla. Wenn Du nach staune noch was tippst, sollte "
+          +"das ein\nLebewesen sein.\n");
+      if (who == this_player())  {
+        out_sel="Du staunst@@adverb@@ ueber Dich selber.";
+        out_oth="@@gname@@ staunt@@adverb@@ ueber sich selber.";
+      }
+      else  {
+        out_sel="Du staunst@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+        if (who) out_vic="@@gname@@ staunt@@adverb@@ ueber Dich.";
+        out_oth="@@gname@@ staunt@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+      }
+    }
+    return FeelIt();
+
+    /**************** Stieren ***************/
+    case "stier":
+    HELPCHECK("stier");
+    GHOSTCHECK("Du stierst mit hohlem Blick in die Gegend.\n",
+      gname()+" stiert mit hohlem Blick in die Gegend.\n", 0);
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Stiere wie oder wen an?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du kannst Dich nicht selber anstieren.",
+      "Bitte nur Lebewesen anstieren."))
+        return 1;
+    out_sel="Du stierst@@ wen@@@@adverb@@"
+      +(who ? " an." : " in der Gegend herum.");
+    if (who) out_vic="@@gname@@ stiert Dich@@adverb@@ an.";
+    out_oth="@@gname@@ stiert@@ wen@@@@adverb@@"
+      +(who ? " an." : " in der Gegend herum.");
+    return FeelIt();
+
+    /**************** Stimme zu ***************/
+    case "stimm":
+    HELPCHECK("stimm");
+    ParseRest(str, "zu");
+    if (str && !who && !adverb)
+      Return("Stimme wem zu?\n");
+    out_sel="Du stimmst@@ wem@@@@adverb@@ zu.";
+    if (who) out_vic="@@gname@@ stimmt Dir@@adverb@@ zu.";
+    out_oth="@@gname@@ stimmt@@ wem@@@@adverb@@ zu.";
+    return FeelIt();
+
+    /**************** Stoehnen ***************/
+    case "stoehn":
+    HELPCHECK("stoehn");
+    GHOSTCHECK("Du stoehnst schauderlich.\n",
+      gname()+" stoehnt schauderlich. Zum Glueck\nhast Du ziemlich "
+        +"gute Nerven.\n", 0);
+    ParseRest(str);
+    if (!adverb && str)
+      Return("Wie willst Du stoehnen?\n");
+    out_sel="Du stoehnst@@adverb@@.";
+    out_oth="@@name@@ stoehnt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Stossen ***************/
+    case "stoss":
+    HELPCHECK("stoss");
+    ParseRest(str);
+    if (!who)
+      Return("Stosse wen?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Was soll der Unsinn? Lass das!",
+      "Das gibt nur bei Lebewesen Sinn."))
+        return 1;
+    GHOSTCHECK("Dein Ellenbogen versinkt in "+who->name(WEM)+".\n",
+      gname()+" will "+who->name(WEM)+" in die Rippen stossen, aber "
+        +QPP(MALE,WER,PLURAL)+"\nEllenbogen verteilen keinen Stoss, "
+        +"sondern versinken.\n",
+      gname()+" will Dich in die Rippen stossen, aber "+QPP(MALE,WER,PLURAL)
+        +" Ellenbogen versinken.\n");
+    out_sel="Du stoesst@@ wen@@@@adverb@@ in die Rippen.";
+    out_vic="@@name@@ stoesst Dir@@adverb@@ in die Rippen.";
+    out_oth="@@name@@ stoesst@@ wen@@@@adverb@@ in die Rippen.";
+    return FeelIt();
+
+    /**************** Streicheln ***************/
+    case "streichel":
+    case "streichl":
+    HELPCHECK("streichel");
+    ParseRest(str);
+    if (!who)
+      Return("Streichle wen?\n");
+    if (for_all)  {
+      out_sel="Du streichelst @@alle@@@@adverb@@.";
+      out_vic="@@gname@@ streichelt @@alle@@@@adverb@@.";
+      return MixedOut(WEN);
+    }
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Lass Dich von anderen streicheln.",
+      "Ich streichle nur Lebewesen."))
+        return 1;
+    GHOSTCHECK("Du willst "+who->name(WEN,2)+" streicheln, aber Deine "
+      +"Haende koennen\nnichts beruehren.\n",
+    gname()+" will "+who->name(WEN,2)+" streicheln, aber diese\n"
+      +"Geisterhaende koennen halt nix beruehren...\n",
+    gname()+" will Dich streicheln, scheitert aber wie so oft an\n"
+      +"diesen dummen durchlaessigen Geisterhaenden.\n");
+    out_sel="Du streichelst @@wen@@@@adverb@@.";
+    out_vic="@@name@@ streichelt Dich@@adverb@@.";
+    out_oth="@@name@@ streichelt @@wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Stupsen ***************/
+    case "stups":
+    HELPCHECK("stups");
+    if (ghost())
+      Return("Das geht nicht ohne Ellenbogen,..\n");
+    ParseRest(str);
+    if (!who)
+      Return("Stupse wen an?\n");
+    out_sel="Du stupst @@wen@@@@adverb@@ an.";
+    out_vic="@@name@@ stupst Dich@@adverb@@ an.";
+    out_oth="@@name@@ stupst @@wen@@@@adverb@@ an.";
+    return FeelIt();
+
+    /**************** Stutzen ***************/
+    case "stutz":
+    HELPCHECK("stutz");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Stutze wie?\n");
+    out_sel="Du stutzt@@adverb@@.";
+    out_oth="@@gname@@ stutzt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Taetscheln ***************/
+    case "taetschel":
+    case "taetschl":
+    HELPCHECK("taetschel");
+    ParseRest(str);
+    if (!who)
+      Return("Taetschle wen?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Das sieht zu doof aus, das mache ich nicht.",
+      "Ich taetschle nur Lebewesen."))
+        return 1;
+     GHOSTCHECK("Du willst "+who->name(WEN)+" taetscheln - aber Deine "
+        +"Haende gehen\nglatt durch den Kopf durch.\n",
+      gname()+" will "+who->name(WEN)+" den Kopf taetscheln, aber "
+        +"die Geister-\nhaende gehen glatt durch den Kopf durch.\n",
+      gname()+" will Deinen Kopf taetscheln, aber diese Geisterhaende "
+        +"gehen\nglatt durch Deinen Kopf durch - Du hast ein seltsames "
+        +"Gefuehl dabei.\n");
+    out_sel="Du taetschelst @@wem@@@@adverb@@ den Kopf.";
+    out_vic="@@name@@ taetschelt Dir@@adverb@@ den Kopf.";
+    out_oth="@@name@@ taetschelt @@wem@@@@adverb@@ den Kopf.";
+    return FeelIt();
+
+    /**************** Tanzen ***************/
+    case "tanz":
+    HELPCHECK("tanz");
+    GHOSTCHECK("Du tanzt den klassischen GeisterTanz (tm).\n",
+      gname()+" tanzt den klassischen GeisterTanz (tm).\n", 0);
+    if (!str) {
+      out_sel="Kommst Du Dir nicht irgendwie bloed vor? Du tanzt "
+        +"den Ententanz.";
+      out_oth="@@name@@ tanzt den Ententanz.";
+    }
+    else  {
+      taenze=({"Walzer","Polka","Rumba","Tango","Cha cha cha","Foxtrott",
+        "Mambo","Salsa","Slowfox","Breakdance","Pogo","Merengue",
+        "Rock'n'Roll","Ballett","Regentanz","Hexentanz"});
+      ParseRest(str,"mit");
+      if (!who)
+        Return("Mit wem willst Du tanzen?\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Mit Dir selber kannst Du nicht tanzen.",
+        "Keine Reaktion - will wahrscheinlich nicht tanzen."))
+          return 1;
+      ifoo=random(sizeof(taenze));
+      out_sel="Du tanzt@@adverb@@ mit @@wem@@ eine Runde "+taenze[ifoo]+".";
+      out_vic="@@name@@ reisst Dich an sich und tanzt@@adverb@@ eine Runde "
+        +taenze[ifoo]+" mit Dir.";
+      out_oth="@@name@@ schnappt sich @@wen@@ und die beiden tanzen"
+        +"@@adverb@@ eine Runde "+taenze[ifoo]+".";
+    }
+    return FeelIt();
+
+    /**************** Traeumen ***************/
+    case "traeum":
+    HELPCHECK("traeum");
+    if (!str)
+      Return("Traeume wovon oder von wem?\n");
+    ParseRest(str);
+    out_sel="Du traeumst"+(who ? "@@adverb@@ von @@wem@@."
+      : (adverb ? "@@adverb@@." : " "+str+"."));
+    if (who) out_vic="@@gname@@ traeumt@@adverb@@ von Dir.";
+    out_oth="@@gname@@ traeumt"+(who ? "@@adverb@@ von @@wem@@."
+      : (adverb ? "@@adverb@@." : " "+str+"."));
+    return FeelIt();
+
+    /**************** Treten (tritt) ***************/
+    case "tritt":
+    case "tret":
+    HELPCHECK("tritt");
+    if (!str)  {
+      GHOSTCHECK("Dein Fuss faehrt durch die beruehmte langvergessene "
+          +"unsichtbare Schildkroete\nhindurch.\n",
+        gname()+" will die beruehmte langvergessene unsichtbare\n"
+          +"Schildkroete treten, aber "+QPP(MALE,WER)
+          +" Fuss faehrt durch sie hindurch.\n", 0);
+      out_sel="Du trittst die beruehmte langvergessene unsichtbare "
+        +"Schildkroete.";
+      out_oth="@@gname@@ tritt die beruehmte langvergessene unsichtbare "
+        +"Schildkroete.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du trittst @@alle@@@@adverb@@. Solltest Du nicht langsam "
+          +"an Flucht denken?";
+        out_vic="@@name@@ tritt @@alle@@@@adverb@@. Traut sich ganz "
+          +"schoen was!";
+        return MixedOut(WEN);
+      }
+      if (!who && !adverb)
+        Return("Wenn Du schon was nach tritt tippst, dann sag mir, wen "
+          +"oder wie ich das soll.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Du schaffst es nicht, Dir selber in den Hintern zu treten.",
+        "Tote Sachen tritt man nicht auch noch!"))
+          return 1;
+      if (who)  {
+        out_sel="Du trittst@@ wen@@@@adverb@@.";
+        if (who) out_vic="@@gname@@ tritt Dich@@adverb@@.";
+        out_oth="@@gname@@ tritt@@ wen@@@@adverb@@.";
+      }
+      else  {
+        out_sel="Du trittst die beruehmte langvergessene unsichtbare "
+          +"Schildkroete@@adverb@@.";
+        out_oth="@@gname@@ tritt die beruehmte langvergessene unsichtbare "
+          +"Schildkroete\n@@adverb@@.";
+      }
+    }
+    return FeelIt();
+
+    /**************** Troesten ***************/
+    case "troest":
+    HELPCHECK("troest");
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du troesten?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Trost musst Du bei jemand anders suchen.",
+      "Das Teil musst Du nicht troesten, das fuehlt nix."))
+        return 1;
+    out_sel="Du troestest@@ wen@@@@adverb@@.";
+    out_vic="@@gname@@ troestet Dich@@adverb@@.";
+    out_oth="@@gname@@ troestet@@ wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Umarmen ***************/
+    case "umarm":
+    HELPCHECK("umarm");
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du umarmen?\n");
+    if (who && CheckLife(0,NOT_DEAD,0,"Bitte umarme nur Lebewesen."))
+      return 1;
+    if (ghost() && CheckLife(NOT_SELF,0,
+      "Du kannst Dich als Geist nicht selber waermen."))
+        return 1;
+    str1=who->QueryProp(P_NAME);
+    if(pointerp(str1))str1=(string)str1[0]; // Rumata
+    str2=who->QueryPronoun(WEN);
+    GHOSTCHECK("Du willst "+str1+" umarmen, aber Deine Arme gehen durch "
+        +str2+" durch.\n",
+      gname()+" will "+str1+" umarmen, aber "+QPP(MALE,WER,PLURAL)
+        +" Arme gehen\ndurch "+str2+" hindurch.\n",
+      gname()+" will Dich umarmen, aber "+QPP(MALE,WER,PLURAL)
+        +" Arme gehen durch Dich hindurch.\n");
+    if (for_all)  {
+      out_sel="Du umarmst @@alle@@@@adverb@@.";
+      out_vic="@@name@@ umarmt @@alle@@@@adverb@@.";
+      return MixedOut(WEN);
+    }
+    if (who==this_player())  {
+      out_sel="Du legst Deine Arme um Dich und waermst Dich "
+        +"ein bisschen selber.";
+      out_oth="@@name@@ legt "+QPP(MALE,WER,PLURAL)
+        +" Arme um sich und waermt sich ein bisschen selber.";
+    }
+    else  {
+      out_sel="Du umarmst@@ wen@@@@adverb@@.";
+      out_vic="@@name@@ umarmt Dich@@adverb@@.";
+      out_oth="@@name@@ umarmt@@ wen@@@@adverb@@.";
+    }
+    return FeelIt();
+
+    /**************** Verfluchen ***************/
+    case "verfluch":
+    HELPCHECK("verfluch");
+    if (!str)
+      Return("Wen oder was willst Du denn verfluchen?\n");
+    ParseRest(str);
+    if (!who)  {
+      out_sel="Du verfluchst "+str+".";
+      out_oth="@@gname@@ verflucht "+str+".";
+    }
+    else  {
+      if (who==this_player())
+        Return("Sich selber verflucht man besser nicht...\n");
+      if (!adverb)  {
+        flag=sscanf(str, "%s %s", str1,str2);
+        out_sel="Du verfluchst@@ wen@@"+(flag==2 ? " "+str2 : "")+".";
+        out_vic="@@gname@@ verflucht Dich"+(flag==2?" "+str2 : "")+".";
+        out_oth="@@gname@@ verflucht@@ wen@@"+(flag==2 ? " "+str2 : "")+".";
+      }
+      else  {
+        out_sel="Du verfluchst@@ wen@@@@adverb@@.";
+        out_vic="@@gname@@ verflucht Dich@@adverb@@.";
+        out_oth="@@gname@@ verflucht@@ wen@@@@adverb@@.";
+      }
+    }
+    return FeelIt();
+
+    /**************** Verneigen / Verbeugen ***************/
+    case "verneig":
+    case "verbeug":
+    HELPCHECK("verneig");
+    GHOSTCHECK("Du verneigst Dich ein bisschen heftig - Dein Kopf taucht "
+        +"kurz in den Boden.\n",
+      gname()+" verneigt sich. Ein bisschen heftig - "+QPP(MALE,WER)
+        +" Kopf\ntaucht kurz in den Boden ein.\n", 0);
+    if ((!str) || (str == "dich")) {
+      out_sel="Du verneigst Dich vor den Anwesenden.";
+      out_oth="@@name@@ verneigt sich anmutig.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du verneigst Dich@@adverb@@ vor @@alle@@.";
+        out_vic="@@name@@ verneigt sich@@adverb@@ vor @@alle@@.";
+        return MixedOut(WEM);
+      }
+      if (!who && !adverb)
+        Return("Verneige dich irgendwie oder vor jemandem.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Wie willst Du das denn schaffen?",
+        "Vor Sachen wird hier nicht verneigt, klar?\n"))
+          return 1;
+      out_sel="Du verneigst Dich@@adverb@@"+(who ? " vor" : "")+"@@ wem@@.";
+      if (who ) out_vic="@@name@@ verneigt sich@@adverb@@ vor Dir.";
+      out_oth="@@name@@ verneigt sich@@adverb@@"+(who ? " vor" : "")
+        +"@@ wem@@.";
+    }
+    return FeelIt();
+
+    /**************** Verneinen ***************/
+    case "nein":
+    case "noe":
+    HELPCHECK("nein");
+    GHOSTCHECK("Du schuettelst Deinen Kopf so heftig, dass er kurz "
+        +"davonschwebt.\n",
+      gname()+" schuettelt heftig den Kopf.\nSo heftig, dass dieser "
+        +"kurz davonschwebt und wieder eingefangen werden muss.\n", 0);
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schuettle wie den Kopf?\n");
+    out_sel="Du schuettelst@@adverb@@ den Kopf.";
+    out_oth="@@name@@ schuettelt@@adverb@@ den Kopf.";
+    return FeelIt();
+
+    /**************** Wackeln ***************/
+    case "wackel":
+    case "wackl":
+    HELPCHECK("wackel");
+    if (ghost())
+      Return("Da gibt es nichts mehr, womit Du wackeln kannst.\n");
+    if (str)
+      if (strstr(str, "mit ")==0)
+        sscanf(str, "mit %s", sfoo);
+      else if (strstr(str,"mit ")>0)  {
+        sscanf(str, "%s mit %s", sfoo, sfoo);
+        flag=1;
+      }
+    if (sfoo=="") sfoo=0;
+    ParseRest(str, (sfoo ? (flag ? " mit " : "mit ")+sfoo : 0));
+    if (str && !adverb && !sfoo)
+      Return("Wackle wie oder womit?\n");
+    out_sel="Du wackelst@@adverb@@ mit "+(sfoo ? sfoo+"." : "dem Hintern.");
+    out_oth="@@name@@ wackelt@@adverb@@ mit "
+      +(sfoo ? sfoo+"." : QPP(MALE,WEM)+" Hintern.");
+    return FeelIt();
+
+    /**************** Waelzen ***************/
+    case "waelz":
+    HELPCHECK("waelz");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Waelze Dich wie auf dem Boden?\n");
+    out_sel="Du waelzt Dich"+(adverb ? "@@adverb@@" : " vor Lachen")
+      +" auf dem Boden.";
+    out_oth="@@gname@@ waelzt sich"+(adverb ? "@@adverb@@" : " vor Lachen")
+      +(ghost() ? " im" : " auf dem")+" Boden.";
+    return FeelIt();
+
+    /**************** Warten ***************/
+    case "wart":
+    HELPCHECK("wart");
+    ParseRest(str);
+    if (!str)  {
+      out_sel="Du tippst mit dem Fuss auf den Boden.";
+      out_oth="@@gname@@ tippt mit dem Fuss auf den Boden.";
+    } else if (!who && adverb)  {
+      out_sel="Du wartest@@adverb@@.";
+      out_oth="@@gname@@ wartet@@adverb@@.";
+    } else  {
+      out_sel="Du wartest@@adverb@@ auf "+(who ? "@@wen@@." : str+".");
+      if (who) out_vic="@@gname@@ wartet@@adverb@@ auf Dich.";
+      out_oth="@@gname@@ wartet@@adverb@@ auf "+(who ? "@@wen@@." : str+".");
+    }
+    return FeelIt();
+
+#ifdef WECKE
+    /**************** Wecken ***************/
+    case "weck":
+    HELPCHECK("weck");
+    if (ParseRemote(str))
+      return 1;
+//    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du wecken?\n");
+    if (sscanf(str, "%s %s", sfoo, sfoo)==2)
+      flag=1;
+    out_sel="Dein Wecker klingelt bei @@wem@@@@adverb@@"
+      +(adverb ? "." : (flag ? ": "+sfoo : "."));
+    out_vic=" "+name(WESSEN)+" Wecker klingelt bei Dir@@adverb@@"
+      +(adverb ? "." : (flag ? ": "+sfoo : "."));
+    out_oth="@@gname@@ wirft "+QPP(MALE, WEN)
+      +" Wecker@@adverb@@ nach @@wem@@.";
+          if (!who->QueryProp(P_VISUALBELL))
+         out_vic[0]=7; // chr(7)==BEL
+      else out_vic=out_vic[1..];
+    return FeelIt();
+#endif
+
+    /**************** Weinen ***************/
+    case "wein":
+    HELPCHECK("wein");
+    GHOSTCHECK("Es reicht leider nur fuer ein paar winzige Nebelwoelkchen, "
+        +"nicht fuer Traenen.\n",
+      gname()+" verzieht das Gesicht und ein paar winzige Nebel-\n"
+        +"woelkchen entfernen sich von seinen \"Augen\".\n", 0);
+    if (!str)  {
+      out_sel="Waaaaah! Du weinst bitterlich.";
+      out_oth="@@name@@ bricht in Traenen aus und weint bitterlich.";
+    }
+    else  {
+      ParseRest(str);
+      if (!who && !adverb)
+        Return("Weine Dich irgendwie bei irgendwem aus, aber nicht so.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Bei sich selber kann man sich so schlecht ausweinen.",
+        "Bei wem willst Du Dich ausweinen???"))
+          return 1;
+      if (who)  {
+        out_sel="Du weinst Dich@@adverb@@ bei@@ wem@@ aus.";
+        out_vic="@@name@@ weint sich@@adverb@@ bei Dir aus.";
+        out_oth="@@name@@ weint sich@@adverb@@ bei@@ wem@@ aus.";
+      }
+      else  {
+        out_sel="Du brichst in Traenen aus und weinst@@adverb@@.";
+        out_oth="@@name@@ bricht in Traenen aus und weint@@adverb@@.";
+      }
+    }
+    return FeelIt();
+
+    /**************** Winken ***************/
+    case "wink":
+    HELPCHECK("wink");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du winkst @@alle@@@@adverb@@ zu.";
+      out_vic="@@name@@ winkt @@alle@@@@adverb@@ zu.";
+      return MixedOut(WEM);
+    }
+    if (!who && !adverb && str)
+      Return("Vielleicht solltest Du auch sagen, wem oder wie Du "
+        +"(zu)winken willst.\n");
+    if (who && CheckLife(NOT_SELF, NOT_DEAD,
+      "Wink Dir nicht selber zu.",
+      "Du musst schon einem Lebewesen zuwinken."))
+        return 1;
+    out_sel="Du winkst@@ wem@@@@adverb@@"+(who ? " zu" : "")+".";
+    if (who) out_vic="@@gname@@ winkt Dir@@adverb@@ zu.";
+    out_oth="@@gname@@ winkt@@ wem@@@@adverb@@"+(who ? " zu" : "")+".";
+    return FeelIt();
+
+    /**************** Wuergen ***************/
+    case "wuerg":
+    HELPCHECK("wuerg");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Wuerge wen oder wie?\n");
+    if (!who)  {
+      out_sel="Du faengst@@adverb@@ an zu wuergen.";
+      out_oth="@@gname@@ faengt@@adverb@@ an zu wuergen.";
+    } else if (CheckLife(NOT_SELF, NOT_DEAD,
+          "Du wuergst ein bischen an Dir rum. Dir wird schnell langweilig.",
+	  "Wuerg lieber ein Lebewesen.")) {
+      return 1;
+    } else {
+      out_sel="Du springst @@wen@@ an und faengst an, "+who->QueryPronoun(WEN)
+        +"@@adverb@@ zu wuergen.";
+      out_vic="@@gname@@ springt Dich auf einmal an und wuergt Dich@@adverb@@.";
+      out_oth="@@gname@@ springt auf einmal @@wen@@ an und wuergt "
+        +who->QueryPronoun(WEN)+"@@adverb@@.";
+    }
+    return FeelIt();
+
+    /**************** Wundern ***************/
+    case "wunder":
+    HELPCHECK("wunder");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Wie oder ueber wen willst Du Dich wundern?\n");
+    out_sel="Du wunderst Dich@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+    if (who) out_vic="@@gname@@ wundert sich@@adverb@@ ueber Dich.";
+    out_oth="@@gname@@ wundert sich@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+    return FeelIt();
+
+    /**************** Wuscheln ***************/
+    case "wuschel":
+    case "wuschl":
+    HELPCHECK("wuschel");
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du denn wuscheln?\n");
+    if (CheckLife(0,NOT_DEAD,
+      "", "Hmm, sehr tot. Ne, lieber nicht."))
+        return 1;
+    if (who->QueryProp(P_FROG))  {
+      write("Du verwuschelst...  aeh... hm. Ein Frosch hat wohl nix "
+        +"zum Wuscheln.\n");
+      return 1;
+    };
+    GHOSTCHECK("Du willst "+who->name(WEN)+" wuscheln - aber Deine "
+        +"Haende gehen\nglatt durch den Kopf durch.\n",
+      gname()+" will "+who->name(WEN)+" den Kopf wuscheln, aber "
+        +"die Geister-\nhaende gehen glatt durch den Kopf durch.\n",
+      gname()+" will Dich wuscheln, aber diese Geisterhaende "
+        +"gehen\nglatt durch Deinen Kopf durch - Du hast ein seltsames "
+        +"Gefuehl dabei.\n");
+    if (member(({"highlander","boing","mieze","freund"}), who->query_real_name())>-1)
+      switch (who->query_real_name())  {
+        case "highlander": str1="Federn"; break;
+        case "freund"    :
+        case "mieze"     :
+        case "boing"     : str1="Fell"; break;
+      }
+    else if (who->is_class_member(({CL_DRAGON, CL_FISH, CL_REPTILE})))
+      str1="Schuppen";
+    else if (who->is_class_member(({CL_BIRD, "elster","greif"})))
+      str1="Federn";
+    else if (who->is_class_member(({CL_MAMMAL_LAND,CL_FELINE,"tiger",
+                                    "steinbeisser","knuddeleisbaer"})))
+      str1="Fell";
+    else str1="Haare";
+    out_sel="Du verwuschelst@@adverb@@ @@wessen@@ "+str1+".";
+    out_vic="@@name@@ verwuschelt@@adverb@@ Dein"
+      +(str1=="Fell" ? " " : "e ")+str1+".";
+    out_oth="@@name@@ verwuschelt@@adverb@@ @@wessen@@ "+str1+".";
+    return FeelIt();
+
+    /**************** Zitieren ***************/
+    case "zitier":
+    HELPCHECK("zitier");
+    ParseRest(str);
+    if (!str)
+      Return("Zitiere was oder wen womit?\n");
+    sfoo=implode(explode(str, " ")[1..], " ");
+    if (sfoo=="") sfoo=0;
+    if (who)  {
+      out_sel="Du zitierst @@wen@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+      out_vic="@@gname@@ zitiert Dich"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+      out_oth="@@gname@@ zitiert @@wen@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+    }
+    else  {
+      sfoo=explode(str, "/")[0];
+      out_sel="Du zitierst@@adverb@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+      out_oth="@@gname@@ zitiert@@adverb@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+    }
+    return FeelIt();
+
+    /**************** Zittern ***************/
+    case "zitter":
+    HELPCHECK("zitter");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Zittere wie?\n");
+    out_sel="Du zitterst"+(adverb ? "@@adverb@@." : " vor Angst.");
+    out_oth="@@gname@@ zittert"+(adverb ? "@@adverb@@." : " vor Angst.");
+    return FeelIt();
+
+    /**************** Schulterzucken ***************/
+    case "zuck" :
+    HELPCHECK("zuck");
+          if (str)
+      if (sscanf(str,"%s mit den schultern",sfoo))
+        str=sfoo;
+    else if (sscanf(str,"%s den schultern",sfoo))
+      str=sfoo;
+    else
+      if (sscanf(str,"%s schultern",sfoo))
+        str=sfoo;
+    if (str=="") str=0;
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Zucke wie mit den Schultern?\n");
+    out_sel="Du zuckst@@adverb@@ mit den Schultern.";
+    out_oth="@@gname@@ zuckt"+(adverb ? "@@adverb@@" : " ratlos")
+            +" mit den Schultern.";
+    return FeelIt();
+
+    /**************** Zwinkern ***************/
+    case "zwinker":
+    HELPCHECK("zwinker");
+    if (ghost())
+      Return("Vergiss es - das ist als Geist viel zu unauffaellig, als dass\n"
+        +"es andere Leute sehen wuerden.\n");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Zwinkere wie? Zwinkere wem zu?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du kannst Dir nicht selber zuzwinkern.",
+      "Wieso reagiert das Ding da nicht auf Dein Zwinkern? Ist es etwa tot?"))
+        return 1;
+    out_sel="Du zwinkerst@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+    if (who) out_vic="@@name@@ zwinkert Dir@@adverb@@ zu.";
+    out_oth="@@name@@ zwinkert@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+    return FeelIt();
+
+    /**************** Zunge rausstrecken ***************/
+    case "streck":
+    HELPCHECK("streck");
+    GHOSTCHECK("Sorry, Du hast keine Zunge zum Rausstrecken.\n","",0);
+    if (!str)
+      Return("Strecke was wie wem wo wann wieso?\n");
+    str=lower_case(str);
+    if (sscanf(str, "%s zunge raus", str1)!=1 &&
+    sscanf(str, "%s die zunge raus", str1)!=1)
+      Return("Strecke was wie wem wo wann wieso?\n");
+    ParseRest(str1);
+    if (for_all)  {
+      out_sel="Du streckst @@alle@@@@adverb@@ die Zunge raus.";
+      out_vic="@@name@@ streckt @@alle@@@@adverb@@ die Zunge raus.";
+      return MixedOut(WEM);
+    }
+    out_sel="Du streckst@@ wem@@@@adverb@@ die Zunge raus.";
+    if (who) out_vic="@@name@@ streckt Dir@@adverb@@ die Zunge raus.";
+    out_oth="@@name@@ streckt@@ wem@@@@adverb@@ die Zunge raus.";
+    return FeelIt();
+
+    // Spezialsachen - Geisterverben und Magierverben
+
+    /**************** Rasseln ***************/
+    case "rassel":
+    case "rassl":
+    if (!ghost())
+      Return("Das ist nicht Dein Fachgebiet - Du bist doch kein Geist!\n");
+    HELPCHECK("rassel");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Rassel wie?\n");
+    out_sel="Du rasselst"+(adverb ? "@@adverb@@" : " fuerchterlich")
+      +" mit einer rostigen Rasselkette,\n"
+      +"die Du auf einmal fuer einen Moment in der Hand haeltst.";
+    out_oth="@@gname@@ holt auf einmal eine rostige Rasselkette aus\n"
+      +"dem Nichts und faengt an,"+(adverb ? "@@adverb@@" : " fuerchterlich")
+      +" damit zu rasseln.\n"
+      +"Danach ist die Kette auf einmal wieder verschwunden.";
+    return FeelIt();
+
+    /**************** Heulen ***************/
+    case "heul":
+    if (!ghost())
+      Return("Lass das mal den Fachleuten (also den Geistern).\n");
+    HELPCHECK("heul");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Heule wie?\n");
+    out_sel="Du heulst"+(adverb ? "@@adverb@@." : " schauerlich.");
+    out_oth="@@gname@@ heult"+(adverb ? "@@adverb@@." : " schauerlich.");
+    return FeelIt();
+
+    /**************** Treten (tretet) ***************/
+    case "kick":
+    if (!IS_WIZARD(this_player()))
+      return 0;
+    HELPCHECK("kick");
+    if (!str)  {
+      GHOSTCHECK("Dein Fuss faehrt durch die beruehmte langvergessene "
+          +"unsichtbare Schildkroete\nhindurch.\n",
+        gname()+" will die beruehmte langvergessene unsichtbare\n"
+          +"Schildkroete treten, aber "+QPP(MALE,WER)
+          +" Fuss faehrt durch sie hindurch.\n", 0);
+      out_sel="Du tretest die beruehmte langvergessene unsichtbare "
+        +"Schildkroete.";
+      out_oth="@@name@@ tretet die beruehmte langvergessene unsichtbare "
+        +"Schildkroete.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du tretest @@alle@@@@adverb@@.";
+        out_vic="@@name@@ tretet @@alle@@@@adverb@@.";
+        return MixedOut(WEN);
+      }
+      if (!who && !adverb)
+        Return("Wenn Du schon was nach kick tippst, dann sag mir wen "
+          +"oder wie ichdas soll.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Du schaffst es nicht, Dir selber in den Hintern zu treten.",
+        "Tote Sachen tritt man nicht auch noch!"))
+          return 1;
+      if (who)  {
+        out_sel="Du tretest@@ wen@@@@adverb@@.";
+        if (who) out_vic="@@gname@@ tretet Dich@@adverb@@.";
+        out_oth="@@gname@@ tretet@@ wen@@@@adverb@@.";
+      }
+      else  {
+        out_sel="Du tretest die beruehmte langvergessene unsichtbare "
+          +"Schildkroete@@adverb@@.";
+        out_oth="@@gname@@ tretet die beruehmte langvergessene unsichtbare "
+          +"Schildkroete\n@@adverb@@.";
+      }
+    }
+    return FeelIt();
+
+    /************* Nassspritzen ***************/
+    case "splash":
+    if (!IS_WIZARD(this_player()) &&
+    !(IS_SEER(this_player()) && present("SEHER\nspritzpistole",this_player())))
+      return 0;
+    HELPCHECK("splash");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du ziehst Deine Wasserpistole und spritzt @@alle@@@@adverb@@ "
+        +"patschnass.";
+      out_vic="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt\n"
+        +"@@alle@@@@adverb@@ patschnass.";
+      return MixedOut(WEN);
+    }
+    if (!who)
+      Return("Wen willst Du denn nassmachen?\n");
+    if (who == this_player())  {
+      out_sel="Sag mal, kommst Du Dir nicht ein bisschen doof vor?\n"
+        +"Du ziehst Deine Wasserpistole und spritzt Dich@@adverb@@ selber patschnass.";
+      out_oth="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt "
+        +"sich@@adverb@@ aus unerfindlichen Gruenden selbst patschnass.";
+    }
+    else  {
+      out_sel="Du ziehst Deine Wasserpistole und spritzt @@wen@@@@adverb@@ "
+        +"patschnass.";
+      out_vic="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt "
+        +"Dich@@adverb@@ patschnass.";
+      out_oth="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt "
+        +"@@wen@@@@adverb@@ patschnass.";
+    }
+    return FeelIt();
+
+    /**************** Anflammen ***************/
+    case "flam":
+    if (!IS_WIZARD(this_player()) &&
+    !(IS_SEER(this_player()) && present("SEHER\nflammenwerfer",this_player())))
+      return 0;
+    HELPCHECK("flame");
+    if (ghost())
+      Return("Du hast leider grade Deinen Flammenwerfer nicht dabei.\n");
+    ParseRest(str);
+    ifoo=!random(7);
+    if (for_all)  {
+      out_sel="Du holst aus Deinen tiefsten Taschen (oder was weiss denn "
+        +"ich woher) Deinen\nMorgenGrauen handgearbeiteten Mini-Flammenwerfer "
+        +"(tm), richtest ihn aus und...\n"
+        +(ifoo ? "schaust leicht frustriert auf das Streichholz, in das "
+          +"er sich verwandelt hat."
+        : "feuerst@@adverb@@ einen riesigen Feuerball auf @@alle@@ ab.\n"
+          +"Es riecht auf einmal so verbrannt hier...");
+      out_vic="@@name@@ holt auf einmal irgendwoher einen MorgenGrauen "
+        +"handgearbeiteten\nMini-Flammenwerfer (tm), richtet ihn aus und...\n"
+        +(ifoo ? "schaut ziemlich frustriert auf das Streichholz, in das "
+          +"sich das Ding verwandelt hat."
+        : "feuert@@adverb@@ einen riesigen Feuerball auf @@alle@@ ab.\n"
+          +"Dir wird so warm um's Herz...");
+      return MixedOut(WEN);
+    }
+    if (!who)
+      Return("Wen willst Du denn ankokeln?\n");
+    out_sel="Du holst aus Deinen tiefsten Taschen (oder was weiss denn "
+      +"ich woher) Deinen\nMorgenGrauen handgearbeiteten Mini-Flammenwerfer "
+      +"(tm), richtest ihn aus und...\n"
+      +(ifoo ? "schaust leicht frustriert auf das Streichholz, in das er "
+        +"sich verwandelt hat."
+      : "feuerst@@adverb@@ einen riesigen Feuerball auf @@wen@@ ab.\n"
+        +"Es riecht auf einmal so verbrannt hier...");
+    out_vic="@@name@@ holt auf einmal irgendwoher einen MorgenGrauen "
+      +"handgearbeiteten\nMini-Flammenwerfer (tm), richtet ihn auf Dich "
+      +"aus und...\n"
+      +(ifoo ? "schaut ziemlich frustriert auf das Streichholz, in das "
+        +"sich das Ding\nverwandelt hat."
+      : "feuert@@adverb@@ einen riesigen Feuerball auf Dich ab.\n"
+        +"Dir wird so warm ums Herz...");
+    out_oth="@@name@@ holt auf einmal irgendwoher einen MorgenGrauen "
+      +"handgearbeiteten\nMini-Flammenwerfer (tm), richtet ihn "
+      +"auf@@ wen@@ aus und...\n"
+      +(ifoo ? "schaut ziemlich frustriert auf das Streichholz, in das "
+        +"sich das Ding\nverwandelt hat."
+      : "feuert@@adverb@@ einen riesigen Feuerball auf@@ wen@@ ab.\nEs "
+        +"riecht auf einmal irgendwie verbrannt hier ...");
+    return FeelIt();
+
+    // Special 2: remote verbs
+
+    /**************** Remote knuddeln ***************/
+    case "rknuddel":
+    case "rknuddl":
+    HELPCHECK("rknuddel");
+    if (ParseRemote(str))
+      return 1;
+    if (!who)
+      Return("Knuddle wen?\n");
+    if (CheckLife(NOT_SELF,0,
+      "Das bringt doch nix, lass es halt.",
+      0))
+        return 1;
+    if (present(who, environment()))
+      Return("Wenn jemand neben Dir steht, nimm knuddel.\n");
+    out_sel="Du knuddelst @@wen@@@@adverb@@ aus der Ferne.";
+    out_vic="@@gname@@ knuddelt Dich@@adverb@@ aus der Ferne.";
+    return FeelIt();
+
+    /**************** Remote winken ***************/
+    case "rwink":
+    HELPCHECK("rwink");
+    if (ParseRemote(str))
+      return 1;
+    if (!who)
+      Return("Winke wem zu?\n");
+    if (CheckLife(NOT_SELF,0,
+      "Sehr witzig. Pah.", 0))
+        return 1;
+    if (present(who, environment()))
+      Return("Wenn jemand neben Dir steht, nimm wink.\n");
+    out_sel="Du winkst @@wem@@@@adverb@@ aus der Ferne zu.";
+    out_vic="@@gname@@ winkt Dir@@adverb@@ aus der Ferne zu.";
+    return FeelIt();
+
+    /**************** Verbenliste ***************/
+    case "verb":
+    case "verben":
+    HELPCHECK("verb");
+    More(SOULHELP->Help());
+    return 1;
+
+    /**************** Adverbienverwaltung ***************/
+    case "adverb":
+    case "adverben":
+    case "adverbien": {   /* Das ist die richtige Form, aber wer weiss das? */
+    string f1,f2;
+    HELPCHECK("adverb");
+    if (!str || str=="#" || str=="$")
+      return zeige_adverbs((str=="#" ? 1 : (str=="$" ? 2 : 0)));
+    if (sscanf(str, "%s %s", f1,f2)==2)  {
+      f1 = lower_case(f1); // kleingeschrieben speichern, spart Umwandlung
+      if (f1=="")
+        Return("Hm, da muss wohl ein Leerzeichen zu viel gewesen sein. Bitte "
+          +"nochmal,\naber ohne zuviele Leerzeichen.\n");
+      if (f1=="?")  {
+        f2 = lower_case(f2);
+        string match;
+        if ((match=QueryStdAdverbs()[f2] || plr_adverbs[f2]))
+          write("Die Abkuerzung "+f2+" gehoert zu dem Adverb:\n"+match+LF);
+        else
+          write("Diese Abkuerzung ist bisher nicht definiert.\n");
+        return 1;
+      }
+      if (QueryStdAdverbs()[f1])
+        Return("Die Standardabkuerzungen koennen nicht neu definiert "
+          +"werden.\n");
+      if (sizeof(plr_adverbs)>=100) 
+      {
+        write("Mehr als 100 eigene Adverbien kannst Du nicht definieren.\n");
+        return 1;
+      }
+      if (plr_adverbs[f1])  {
+        plr_adverbs[f1]=f2;
+        write("OK, Adverb mit der Abkuerzung \""+f1+"\" auf \""+f2
+          +"\" gesetzt.\n");
+      }
+      else  {
+        if (sizeof(f1) > 6)
+          Return("Die Abkuerzung ist zu lang, bitte nicht mehr als "
+            +"6 Zeichen.\n");
+        plr_adverbs[f1]=f2;
+        write("OK, neues Adverb \""+f2+"\" mit der Abkuerzung \""+f1+"\".\n");
+      }
+    }
+    else  {
+      str = lower_case(str);
+      if (QueryStdAdverbs()[str])
+        Return("Die Standardadverben koennen nicht geloescht werden.\n");
+      else if (!plr_adverbs[str])
+        Return("Du hast kein Adverb mit dieser Abkuerzung.\n"
+          +"Syntax: adverb, um die Adverbien anzuzeigen,\n"
+          +"        adverb #, um nur Deine Adverbien anzuzeigen,\n"
+          +"        adverb $, um nur die Standardadverbien anzuzeigen,\n"
+          +"        adverb ? <Abkuerzung>, um nachzusehen, ob <Abkuerzung> "
+            +"definiert ist,\n"
+          +"        adverb <Abkuerzung> <Adverb>, um der <Abkuerzung> das "
+            +"<Adverb>\n"
+          +"               zuzuordnen,\n"
+          +"        adverb <Abkuerzung>, um das Adverb mit der <Abkuerzung> "
+            +"zu loeschen,\n");
+      else  {
+        write("OK, Adverb \""+plr_adverbs[str]+"\" geloescht.\n");
+        plr_adverbs=m_copy_delete(plr_adverbs, str);
+      }
+    }
+    return 1; 
+    }
+  }
+  return(0);  //fallthrough
+}
+
diff --git a/std/player/soulhelp.c b/std/player/soulhelp.c
new file mode 100644
index 0000000..27a48f9
--- /dev/null
+++ b/std/player/soulhelp.c
@@ -0,0 +1,317 @@
+// MorgenGrauen MUDlib
+//
+// player/soulhelp.c -- Hilfe zu den Soulkommandos
+//
+// $Id: soulhelp.c 7423 2010-02-07 22:56:38Z Zesstra $
+
+#pragma strong_types,save_types
+
+#include <wizlevels.h>
+#ifdef WECKE
+#undef WECKE
+#endif
+#define WECKE
+
+static string *wizcmds, *plrcmds, *ghostcmds;
+static mapping help;
+
+private string HelpVerb(string v);
+private string* SortIt(string *arr);
+
+
+/**
+  Initialisierung
+*/
+void create()  {
+	plrcmds=({
+		"zuck", "schmieg", "antworte", "applaudiere",
+		"betaste", "cls", "drehe (daeumchen)", "danke",
+		"druecke", "erroete", "flippe", "frage",
+		"furze", "gaehne", "gluckse", "grinse",
+		"guck", "haetschel", "hickse", "huepfe",
+		"huste", "keuche", "kichere", "klatsche",
+		"knabbere", "knickse", "knirsche", "knurre",
+		"knutsche", "kotze", "kuesse", "lache",
+		"laechle", "liebe", "nicke", "niese",
+		"gib", "pfeife", "ruelpse", "runzle",
+		"schmolle", "schmunzle", "schnippe", "schnarche",
+		"schnurre", "schreie", "schuettle", "seufze",
+		"singe", "sniefe/schniefe", "spucke", "starre",
+		"staune", "stiere", "stoehne", "stosse",
+		"streichle", "tanze", "tritt", "troeste",
+		"umarme", "verneige", "wackle", "waelze",
+		"weine", "winke", "zwinkere", "verben",
+		"aechze", "erbleiche", "fluche", "verfluche",
+		"kitzle", "nein", "deute", // "zeige",
+		"denke [text]", "knuddle", "taetschel", "wuschel",
+		"strecke ... [die] zunge raus", "kratz",
+		"grummel", "jubel / juble ... [zu]", "wuerg",
+		"gratuliere / beglueckwuensche", "raeusper",
+		"argl", "rotfl", "grunz", "kuschel", "atme ... auf",
+		"freue", "sniff", "grueble", "bohre ... [in der nase]",
+		"putze [nase]", "bibbere", "quietsche/quieke", "schluchze",
+		"schnaufe", "schnaube", "philosophiere", "sabbere",
+		"stimme [...] zu", "krieche", "mustere", "schaeme",
+		"schnalze ... [zunge]", "stampfe ... [auf]", "zitiere", "lobe",
+		"quake", "reibe ... [die] Augen|Haende", "stutze", "schweige",
+		"klopfe", "wundere", "stupse", "brummel", "entschuldige",
+		"mopper", "zeige", "traeume", "begruesse","jammer",
+	});
+#ifdef WECKE
+	plrcmds+=({"wecke"});
+#endif
+	wizcmds=({
+		"kick", "splash", "flame",
+	});
+	ghostcmds=({
+		"rassel/rassle", "heule", "erschrecke",
+	});
+	// Aufbau des help-mappings:
+	// key    : Verb, wie es in soul.c in CHECK_HELP geschrieben ist
+	// entry 1: &n = [<Name>]   &a = [<Adverb>]    &t=[<Text>}
+	//          ! danach heisst noetige Angabe, also ohne []
+	// entry 2: &g = Verhaelt sich bei Geistern anders
+	//          &a = Man kann "alle" als Ziel angeben
+	//          &d = Defaultadverb; muss am Ende angegeben werden
+	help=([
+		"zuck" : "&a [[mit [den]] schultern]"; "Gibt man nicht mindestens "
+			+"\"schultern\" an, so zuckt man zusammen. Das Defaultadverb gilt "
+			+"nur fuer das Schulterzucken.&dratlos",
+		"schmieg" : "&n &a"; "",
+		"antwort" : "&n &t!"; "",
+		"applaudier" : "&n &a"; "&g&a",
+		"begruess" : "&n! &a"; "",
+		"betast" : "&n! &a"; "",
+		"bibber" : "&a"; "",
+		"bohr" : "&a [[in [der]] nase]"; "",
+		"brummel" : "&a | &t"; "",
+		"cls" : ""; "Loescht den Bildschirm auf vt100-kompatiblen Terminals.",
+		"dreh" : "&a [daeumchen | daumen]"; "",
+		"dank" : "&n! &a"; "",
+		"drueck" : "&n! &a"; "&g&a&dzaertlich",
+		"entschuldig" : "&n &a"; "",
+		"erroet" : "&a"; "&g",
+		"erschreck" : "&n! &a"; "&g&dfuerchterlich",
+		"flipp" : "&a"; "&dtotal",
+		"frag" : "&n![|]&t!"; "",
+		"freu" : "&n &a"; "",
+		"furz" : "&a"; "&gHinterlaesst eine duftige Erinnerung im Raum.",
+		"gaehn" : "&a"; "&g",
+		"glucks" : "&a"; "&dwie ein Huhn",
+		"gratulier" : "&n! &a"; "",
+		"grins" : "&n &a"; "&g&a",
+		"gruebel" : "&a"; "",
+		"grummel" : "&a"; "",
+		"guck" : "&a"; "",
+		"haetschel" : "&n! &a"; "&g&a",
+		"hicks" : "&a"; "&g",
+		"huepf" : "&n &a"; "&g",
+		"hust" : "&n &a"; "&g",
+		"jubel" : "&n &a"; "",
+		"jammer" : "&a"; "",
+		"keuch" : "&a"; "&g&dvor Anstrengung",
+		"kicher" : "&n &a"; "",
+		"klatsch" : "&a"; "&g",
+		"klopf" : "&n! &a"; "",
+		"knabber" : "&n! &a"; "&g",
+		"knicks" : "&n &a"; "&g&a",
+		"knirsch" : "&a"; "&g",
+		"knurr" : "&n &a"; "&a",
+		"knutsch" : "&n! &a"; "&g",
+		"kotz" : "&n &a"; "&g",
+		"kriech" : "&n &a"; "",
+		"kuess" : "&n! &a"; "&gKann ausserdem bei bestimmten Gelegenheiten "
+			+"gewisse Nebeneffekte haben.",
+		"lach" : "&n &a"; "&g&a",
+		"laechel" : "&n &a"; "&g&a",
+		"lieb" : "&n! &a"; "",
+		"lob" : "{&n! &a} | &t!"; "Kann einen Nebeneffekt haben.",
+		"mopper" : "&a"; "",
+		"muster" : "&n! &a"; "",
+		"nick" : "&n &a"; "&a&dzustimmend",
+		"nies" : "&a"; "&g",
+		"gib" : "&n! &a [[eine] ohrfeige]"; "&g&a",
+		"pfeif" : "&n &a [nach]"; "&g&danerkennend",
+		"philosophier" : "&a | &t"; "",
+		"putz" : "&a [[die] nase]"; "",
+		"quak" : "&a"; "Geht als Frosch wesentlich besser.",
+		"quiek" : "&a"; "&dvergnuegt",
+		"raeusper" : "&a"; "",
+		"reib" : "&a [[die] Augen] | [[die] Haende]"; "Ohne weitere Angabe reibt "
+			+"man sich die Augen;&dmuede bzw. vergnuegt",
+		"ruelps" : "&a"; "&g",
+		"runzel" : "&a"; "&g",
+		"sabber" : "&n &a"; "",
+		"schaem" : "&a"; "",
+		"schluchz" : "&a"; "&dherzzerreissend",
+		"schmoll" : "&a"; "&g",
+		"schmunzel" : "&a"; "",
+		"schnalz" : "&a [[mit [der]] zunge]"; "",
+		"schnaub" : "&a"; "&dentruestet",
+		"schnauf" : "&a"; "&dvor Anstrengung",
+		"schnipp" : "&a"; "&g",
+		"schnarch" : "&a"; "&g&dlaut",
+		"schnief" : "&a"; "&g",
+		"schnurr" : "&a"; "&dzufrieden",
+		"schrei" : "&n &a"; "&g",
+		"schuettel" : "&n &a"; "&g&aHat einen Unterschied, je nachdem, ob ein Name"
+			+" angegeben wurde oder nicht.",
+		"schweig" : "&a"; "",
+		"seufz" : "&a"; "&g",
+		"sing" : "&a | &t"; "",
+		"sniff" : "&n &a"; "&dtraurig",
+		"spuck" : "&n &a"; "&g",
+		"stampf" : "&a"; "",
+		"starr" : "&n &a"; "Standardadverb gilt hier nur bei Angabe eines Namens."
+			+"&dvertraeumt",
+		"staun" : "&n &a"; "",
+		"stier" : "&n &a"; "&g",
+		"stimm" : "&n &a"; "",
+		"stoehn" : "&a"; "&g",
+		"stoss" : "&n! &a"; "&g",
+		"streichel" : "&n! &a"; "&g&a",
+		"stups" : "&n! &a"; "",
+		"stutz" : "&a"; "",
+		"tanz" : "&n &a"; "&g",
+		"traeum" : "{&n &a} | &t"; "Es koennen Probleme auftreten, wenn ein Adverb"
+			+" in freiem Text erkannt wird. Text wird genau so an \"traeum(s)t\" "
+			+"angehaengt, wie er angegeben wurde.",
+		"tritt" : "&n &a"; "&g&a",
+		"troest" : "&n! &a"; "",
+		"umarm" : "&n! &a"; "&g&a",
+		"verneig" : "&n &a"; "&g&a",
+		"wackel" : "&a [mit &t!]"; "&gMan kann mit allem moeglichem wackeln.",
+		"waelz" : "&a"; "&dvor Lachen",
+		"wart" : "&n &a"; "Man kann auf alles moegliche warten (warte alles "
+			+"moegliche)",
+		"weck" : "&n {&a | &t}"; "Piepst <Name> an und sendet ihm ggf. den Text.",
+		"wein" : "&n &a"; "&g",
+		"wink" : "&n &a"; "&a",
+		"wuerg" : "&n &a"; "",
+		"wunder" : "&n &a"; "",
+		"zeig" : "<Objekt-ID>"; "&aZeigt das Objekt (Langbeschreibung).",
+		"zitier" : "&n &a"; "Hier uebernimmt das Adverb ggf. die Stelle des "
+			+"zitierten, wenn dieser nicht anwesend ist (zitiere Ja /Jof)",
+		"zitter" : "&a"; "",
+		"zwinker" : "&n &a"; "&g",
+		"aechz" : "&a"; "",
+		"argl" : "&a"; "",
+		"atm" : "&a"; "&derleichtert",
+		"bewunder" : "&n! &a"; "",
+		"erbleich" : "&a"; "&g",
+		"fluch" : "&a"; "&g",
+		"grunz" : "&a"; "",
+		"kuschel" : "&n! &a"; "&g",
+		"rotfl" : "&a"; "",
+		"verfluch" : "{&n &a} | &t!"; "",
+		"kitzel" : "&n! &a"; "&g",
+		"nein" : "&a"; "&g",
+		"deut" : "&n! &a"; "&a",
+		"denk" : "&a | &t"; "&g",
+		"knuddel" : "&n! &a"; "&g&a",
+		"kratz" : "&a"; "",
+		"streck" : "&n &a [[die] zunge raus"; "&g",
+		"taetschel" : "&n! &a"; "&g",
+		"wuschel" : "&n! &a"; "",
+		"rassel" : "&a"; "&g&dfuerchterlich",
+		"heul" : "&a"; "&g&dschauerlich",
+		"kick" : "&n &a"; "&g&a",
+		"splash" : "&n! &a"; "&a",
+		"flame" : "&n! &a"; "&g&a",
+		"rknuddel" : "&n! &a"; "Nur fuer Ferngebrauch gedacht.",
+		"rwink" : "&n! &a"; "Nur fuer Ferngebrauch gedacht.",
+		"verb" : ""; "Listet alle derzeit moeglichen Verben auf.",
+		"adverb" : "[# | $ | {? <Abkuerzung>} | <Abkuerzung> [<Adverb>]]";
+			"Genauere Hilfe mit \"adverb ?\"",
+	]);
+}
+
+/**
+  Gibt die Hilfe zu einem Verb oder die Verbenuebersicht zurueck
+  \param verb   "string"  Ein Verb dessen Hilfe angezeigt werden soll.
+                0 oder "" Ueberblicksseite
+  \return 	String der an den Spieler ausgegeben werden kann.
+*/
+string Help(string verb)  {
+	string out;
+	if (verb && verb!="")
+		return HelpVerb(verb);
+	out="";
+	out+=break_string("Standardverben:\n"+implode(SortIt(plrcmds), ", ")
+		+".", 78, 0, 1);
+	if (IS_WIZARD(this_player()))
+		out+="\n"+break_string("Magierverben:\n"+implode(SortIt(wizcmds), ", ")
+			+".", 78, 0, 1);
+	if (this_player()->ghost())
+		out+="\n"+break_string("Geisterverben:\n"+implode(SortIt(ghostcmds), ", ")
+			+".", 78, 0, 1);
+	out+=break_string("\nAdverbien koennen entweder in der Abkuerzung (bei "
+		+"bereits definierten Adverbien) oder mit \"/<Text>\" angegeben werden. "
+		+"Bei der zweiten Methode wird <Text> so, wie er angegeben wurde, an "
+		+"Stelle des Adverbs eingesetzt.", 78, 0, 1);
+	out+="\n"+break_string("Eine kurze Hilfe zu einem Verb bekommst du mit einem der "
+                +"Befehle \"<Verb> -?\", \"<Verb> /?\", \"<Verb> -h\", \"<Verb> /h\" oder "
+                +"\"<Verb> hilfe\". (Da war jemand wirklich fleissig).", 78);
+	return out+"\n"+break_string("Einige Befehle reagieren auch auf leicht "
+		+"unterschiedliche Schreibweise. Diese Befehlsliste kann sich mit Deinem "
+		+"Zustand aendern.", 78);
+}
+
+/**
+  Sortiert ein array of string alphabetisch. 
+  \param arr Das zu sortierende Array
+  \return    Sortierte Kopie des Arrays
+*/
+private string*
+SortIt(string *arr)  {
+  return sort_array(arr, 
+           function int (string x, string y){ 
+             return lower_case(x) > lower_case(y) ;
+           } 
+         );
+}
+
+#define NO_MORE_E ({"cls","flipp","gruebel","grummel","guck","haetschel",\
+  "jubel","laechel","gib","runzel","schmunzel","schuettel","sniff",\
+  "streichel","tritt", "wackel","argl","rotfl","kuschel","kitzel","knuddel",\
+  "taetschel","wuschel","rassel","kick","splash","flame","verb","adverb",\
+  "nein","brummel","mopper",})
+
+/**
+  Gibt die Hilfe zu einem Verb 
+  \param v     Ein Verb dessen Hilfe angezeigt werden soll.
+  \return 	Hilfe zum Verb in Form eines Strings
+*/
+private string
+HelpVerb(string v)  {
+	string h,t;
+	if (member(m_indices(help),v)<0)
+		return "Dazu ist keine Hilfe vorhanden.\n";
+	h="Syntax:\n* "+v;
+	if (member(NO_MORE_E, v)<0)
+		h+="e";
+	h+=" ";
+	t=help[v,0];
+	t=implode(explode(t,"&t!"), "<Text>");
+	t=implode(explode(t,"&n!"), "<Name>");
+	t=implode(explode(t,"&t"), "[<Text>]");
+	t=implode(explode(t,"&n"), "[<Name>]");
+	t=implode(explode(t,"&a"), "[<Adverb>]");
+	h+=t;
+	t=help[v,1];
+	if (t!="")  {
+		h+="\nBemerkungen:";
+		if ((explode(t,"&"))[0]!="")
+			t="\n"+break_string((explode(t,"&"))[0], 76, "* ", 1)[0..<2]
+				+"&"+implode((explode(t,"&"))[1..<1],"&");
+		t=implode(explode(t,"&g"), "\n* Verhaelt sich bei Geistern anders "
+			+"als sonst.");
+		t=implode(explode(t,"&a"), "\n* Bei diesem Verb geht alle(n) als "
+			+"Zielangabe.");
+		if (strstr(t,"&d")>=0)  {
+			t=implode(explode(t,"&d"), "\n* Standardadverb ist ");
+			t+=".";
+		}
+	}
+	return break_string(h+t, 78, 2, 1);
+}
diff --git a/std/player/travel.c b/std/player/travel.c
new file mode 100644
index 0000000..3e07c0a
--- /dev/null
+++ b/std/player/travel.c
@@ -0,0 +1,506 @@
+
+/* 'reise' handling
+ *
+ * Ueberarbeitete und 
+ * erweiterte Version: Tilly@MorgenGrauen, 10.01.02
+ * Basierend auf     : base.c@SilberLand,
+ *                     Revision 3.55, Woody@SilberLand, 11.05.99
+ */   
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player.h>
+#include <living/moving.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <config.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <living.h>
+#include <attributes.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <combat.h>
+#include <transport.h>
+
+void create(){}
+
+private string _traveller(string *s)
+{
+  switch (sizeof(s))
+  {
+    case 1 : return s[0];
+    case 2 : return s[0]+" oder "+s[1];
+  }
+  return efun::implode(s[0..<2],", ")+" oder "+s[<1];
+}
+
+static int reise(string str)
+{
+  mixed  t, ship, dstr;
+  string mit, nach, s1, s2;
+  int    i;
+
+  _notify_fail("Syntax: reise mit <transportmittel> nach <zielort>\n\n"
+               "Weitere Syntaxen moeglich, bitte 'hilfe reise' lesen.\n");
+
+  t = QueryProp(P_TRAVEL_INFO);
+  
+  if (!pointerp(t) || (sizeof(t) < 4) || !objectp(t[0]) || !objectp(t[1]))
+  {
+    SetProp(P_TRAVEL_INFO, t = 0);
+  }
+/* * * * 
+ * REISE
+ * * * */
+  if (!str)
+  {
+    if (!t)
+    {
+      write("Du hast keine Reiseroute festgelegt.\n");
+    }
+    else if (t[0] == environment())
+    {
+      if (environment()->id("Transporter"))
+      {
+        write(sprintf("Du reist mit %s%s.\n",environment()->name(WEM,1), 
+                                             t[2]?" "+t[3]:""));
+      }
+      else
+      {
+        write(sprintf("Du wirst mit %s%s reisen.\n",t[1]->name(WEM,1), 
+                                                   t[2]?" "+t[3]:""));
+      }
+    }
+    else
+    {
+       write(sprintf("Deine letzte Route (mit %s%s) ist hier nicht wirksam.\n",
+                     t[1]->name(WEM,1),
+                     t[2]?" "+t[3]:""));
+    }
+    return 1;
+  }
+
+  str = lower_case( _unparsed_args() );
+/* * * * * * * 
+ * REISE ROUTE
+ * * * * * * */
+  if (str == "route")
+  {
+    string *harbours;
+    
+    if (environment()->id("Transporter"))
+    {
+      if (environment()->QueryProp(P_NO_TRAVELING))
+      {
+        write(break_string(
+         capitalize(environment()->name(WER,1))+" hat gar keine "
+         "Reiseroute. Wo Dich das wohl hinfuehrt?",78));
+        return 1;
+      }
+
+      harbours = environment()->QueryHarbours(1);
+      
+      if (!sizeof(harbours) || !stringp(harbours[0]))
+      {
+        write("Die Reiseroute "+environment()->name(WESSEN,1)+" ist "
+             +"leider nicht bekannt.\n");
+      }
+      else
+      {
+        write(break_string(capitalize(efun::implode(harbours," - "))+".",78,
+         "Reiseroute "+environment()->name(WESSEN,1)+": ",BS_INDENT_ONCE));
+      }
+      return 1;
+    }
+    if (environment()->QueryProp(P_NO_TRAVELING))
+    {
+      write(break_string("Hier kannst Du leider keine Reiseroute waehlen. "
+                         "Was nicht heisst, dass hier kein Transportmittel "
+                         "verkehrt.",78));
+      return 1;
+    }
+    if (!pointerp(ship = TRAVELD->HasTransporter(environment())))
+    {
+      _notify_fail("Hier verkehrt kein Transportmittel.\n");
+      return 0;
+    }
+
+    write("Hier verkehren folgende Transportmittel \n"
+          "--------------------------------------- \n");
+         
+    for (i = 0; i < sizeof(ship); i++)
+    {    
+      if (!ship[i]->Query(P_SHORT)) continue;
+      
+      harbours = ship[i]->QueryHarbours(1);
+
+      if (sizeof(harbours) && stringp(harbours[0]))
+      {
+        write(break_string(efun::implode(harbours," - ")+".",78,
+                           ship[i]->Query(P_SHORT)+": ",BS_INDENT_ONCE));
+      }
+      else
+      {
+        write(ship[i]->Query(P_SHORT)+": Route unbekannt.\n");
+      }
+    }
+    return 1;
+  }
+/* * * * * * * 
+ * REISE AUS
+ * REISE NICHT
+ * * * * * * */
+  if (member((["aus","nicht"]),str))
+  {
+    if (!t)
+    {
+      write("Du hattest keine Reiseroute eingestellt.\n");
+    }
+    else
+    {
+      write("Du loeschst Deine Reiseroute.\n");
+    }
+    SetProp(P_TRAVEL_INFO, 0);
+    return 1;
+  }
+
+  str = regreplace(str,"\\<(zu|zum|zur|ins|ans)\\>","nach",0);
+/* * * * * * * * *
+ * REISE MIT NACH
+ * REISE NACH MIT
+ * * * * * * * * */
+  if ((sscanf(str, "mit %s nach %s", mit, nach) == 2) ||
+      (sscanf(str, "nach %s mit %s", nach, mit) == 2))
+  {
+    _notify_fail("Hier kannst Du leider keine Reiseroute waehlen.\n");
+
+    if (environment()->QueryProp(P_NO_TRAVELING))
+    {
+      return 0;
+    }
+    if (!sizeof(nach))
+    {
+      _notify_fail("Syntax: reise mit <transportmittel> nach <zielort>\n"
+                   "        reise nach <zielort> mit <transportmittel>\n");
+      return 0;
+    }
+    if (environment()->id("Transporter"))
+    {
+      if (environment()->id(mit))
+      {
+        command("reise nach "+nach);
+        return 1;
+      }
+      else
+      {
+        _notify_fail("Beende erstmal Deine aktuelle Reise mit "+
+                      environment()->name(WEM,1)+".\n");
+        return 0;
+      }
+    }
+    if (!pointerp(ship = TRAVELD->HasTransporter(environment(), mit)))
+    {
+      _notify_fail("So ein Transportmittel verkehrt hier nicht.\n");
+      return 0;
+    }
+    for (i = sizeof(ship) -1 ; i >= 0; i--)
+      if (!ship[i]->HasRoute(nach))
+      {
+        ship[i] = 0;
+      }
+
+    ship -= ({0});
+
+    if (pointerp(t) && objectp(t[1]) && (member(ship,t[1]) != -1))
+    {
+      ship = ({ t[1] });
+    }
+    if (sizeof(ship) > 1)
+    {
+      if (object_name(environment()) == ship[0]->HasRoute(nach)[0])
+      {
+        _notify_fail("Aber da bist Du doch bereits.\n");
+        return 0;
+      }
+     write("Dorthin kannst Du mit "+CountUp(map_objects(ship,"name",WEM))
+           +"reisen.\n");
+ 
+
+      dstr = filter( filter_objects(ship,"short"), lambda( ({'x}),
+             ({ #'==, ({#'environment, 'x}), environment() }) ) );
+
+      if (sizeof(dstr))
+      {
+        ship = dstr[0];
+      }
+      else
+      {
+        ship = ship[0];
+      }
+
+      dstr = ship->HasRoute(nach);
+
+      write(sprintf("Du entscheidest Dich fuer %s und reist %s.\n",
+            ship->name(WEN,1),dstr[1]));
+    }
+    else if (sizeof(ship) < 1)
+    {
+      _notify_fail("Nach '"+capitalize(nach)+"' kann Dich das angegebene "
+                  +"Transportmittel leider nicht bringen.\n");
+      return 0;
+    }
+    else
+    {
+      ship = ship[0];
+      dstr = ship->HasRoute(nach);
+
+      if (object_name(environment()) == dstr[0])
+      {
+        _notify_fail("Aber da bist Du doch bereits.\n");
+        return 0;
+      }
+      if (t && stringp(t[2]))
+      {
+        if (t[2] == dstr[0])
+        {
+          _notify_fail("Aber das tust Du doch bereits.\n");
+          return 0;
+        }
+      }
+      write(sprintf("Ok, Du reist nun mit %s %s.\n",
+                     ship->name(WEM,1),dstr[1]));
+    }
+    if (environment(ship)==environment() && ship->short())
+    {
+      ship->Enter(this_object());
+    }
+    SetProp(P_TRAVEL_INFO, ({ environment(), ship, dstr[0], dstr[1] }) );
+    return 1;
+  }
+/* * * * * * *
+ * REISE NACH
+ * * * * * * */
+  if (sscanf(str,"nach %s",nach))
+  {
+    _notify_fail("Hier kannst Du leider keine Reiseroute waehlen.\n");
+
+    if (environment()->QueryProp(P_NO_TRAVELING))
+    {
+      return 0;
+    }
+    if (environment()->id("Transporter"))
+    {
+      if (!dstr = environment()->HasRoute(nach))
+      {
+        _notify_fail("Dorthin kann Dich "+environment()->name(WER,1)+
+                     " leider nicht bringen.\n");
+        return 0;
+      }
+      if (t && stringp(t[2]))
+      {
+        if (t[2] == dstr[0])
+        {
+          _notify_fail("Aber das tust Du doch bereits.\n");
+          return 0;
+        }
+      }
+      write(sprintf("Ok, Du reist jetzt mit %s %s.\n",
+                    environment()->name(WEM,1),dstr[1]));
+      
+      if (IS_WIZARD(this_object()))
+      {
+        write("Als Magier nimmst Du natuerlich die Abkuerzung.\n");
+        move(dstr[0],M_NOCHECK);
+        return 1;
+      }
+      SetProp(P_TRAVEL_INFO,({ environment(), 
+                               environment(), 
+                               dstr[0], 
+                               dstr[1] }) );
+
+      if (object_name(environment(ship = environment())) == dstr[0] && 
+          ship->short())
+      {
+        environment()->Leave(this_object());
+
+        if (environment() != ship)
+        {
+          SetProp(P_TRAVEL_INFO, 0);
+        }
+      }
+      return 1;
+    }
+    if (!pointerp(ship = TRAVELD->HasTransporter(environment())))
+    {
+      _notify_fail("Von hier aus kannst Du nicht reisen.\n");
+      return 0;
+    }
+    for (i = sizeof(ship) - 1; i >= 0; i--)
+      if (!ship[i]->HasRoute(nach))
+      {
+        ship[i] = 0;
+      }
+
+    ship -= ({ 0 });
+ 
+    if (pointerp(t) && objectp(t[1]) && (member(ship,t[1]) != -1))
+    {
+      ship = ({ t[1] });
+    }
+    if (sizeof(ship) > 1)
+    {
+      if (object_name(environment()) == ship[0]->HasRoute(nach)[0])
+      {
+        _notify_fail("Aber da bist Du doch bereits.\n");
+        return 0;
+      }
+
+      write(break_string("Dahin kannst Du mit "
+           +_traveller(map_objects(ship, "name", WEM))+" gelangen.",78));
+
+      dstr = filter(filter_objects(ship,"short"),lambda( ({'x}),
+            ({ #'==, ({#'environment, 'x}), environment() }) ) );
+
+      if (sizeof(dstr))
+      {
+        ship = dstr[0];
+      }
+      else
+      {
+        ship = ship[0];
+      }
+
+      dstr = ship->HasRoute(nach);
+
+      write(sprintf("Du waehlst %s und reist %s.\n",ship->name(WEN,1),
+                                                    dstr[1]));
+    }
+    else if (sizeof(ship) < 1)
+    {
+      _notify_fail("Nach '"+capitalize(nach)+"' kann Dich leider keines der "
+                  +"hier verkehrenden Transportmittel bringen.\n");
+      return 0;
+    }
+    else
+    {
+      ship = ship[0];
+      dstr = ship->HasRoute(nach);
+
+      if (object_name(environment()) == dstr[0])
+      {
+        _notify_fail("Aber da bist Du ja bereits.\n");
+        return 0;
+      }
+      else if (t && stringp(t[2]))
+      {
+        if (t[2] == dstr[0])
+        {
+          _notify_fail("Aber das tust Du doch bereits.\n");
+          return 0;
+        }
+      }
+      write(sprintf("Ok, Du reist nun mit %s %s.\n",ship->name(WEM),
+                                                    dstr[1]));
+    }
+    if (IS_WIZARD(this_object()))
+    {
+      write("Als Magier nimmst Du natuerlich die Abkuerzung.\n");
+      move(dstr[0],M_NOCHECK);
+      return 1;
+    }
+    if (environment(ship)==environment() && ship->short())
+    {
+      ship->Enter(this_object());
+    }
+    SetProp(P_TRAVEL_INFO, ({ environment(), ship, dstr[0], dstr[1] }) );
+    return 1;
+  }
+/* * * * * *
+ * REISE MIT
+ * * * * * */
+  if (sscanf(str, "mit %s", mit))
+  {
+    _notify_fail("Hier kannst Du leider keine Reiseroute waehlen.\n");
+
+    if (environment()->QueryProp(P_NO_TRAVELING))
+    {
+      return 0;
+    }
+    if (environment()->id("Transporter"))
+    {
+      if (environment()->id(mit))
+      {
+        _notify_fail("Aber das tust Du doch bereits.\n");
+        return 0;
+      }
+      else
+      {
+        _notify_fail("Beende erstmal Deine aktuelle Reise mit "+
+                      environment()->name(WEM,1)+".\n");
+        return 0;
+      }
+    }
+    if (t && objectp(t[1]) && t[1]->id(mit) && t[0] == environment())
+    {
+      _notify_fail("Aber das tust Du doch bereits.\n");
+      return 0;
+    }
+    if (!pointerp(ship = TRAVELD->HasTransporter(environment(),mit)))
+    {
+      _notify_fail("So ein Transportmittel verkehrt hier nicht.\n");
+      return 0;
+    }
+    if (sizeof(ship) > 1)
+    {
+      write("'"+capitalize(mit)+"' koennte "
+               +_traveller(map_objects(ship,"name",WER))+" sein.\n");
+
+      dstr = filter(filter_objects(ship,"short"),lambda( ({'x}),
+            ({ #'==, ({#'environment, 'x}), environment() }) ) );
+
+      if (sizeof(dstr))
+      {
+        ship = dstr[0];
+      }
+      else
+      {
+        ship = ship[0];
+      }
+      write(sprintf("Du waehlst %s.\n", ship->name(WEN,1)));
+    }
+    else if (sizeof(ship) < 1)
+    {
+      notify_fail("So ein Transportmittel verkehrt hier nicht.\n");
+      return 0;
+    }
+    else
+    {
+      ship = ship[0];
+      write(sprintf("Du reist nun mit %s.\n",ship->name(WEM,1)));
+    }
+    if (environment(ship)==environment() && ship->short())
+    {
+      ship->Enter(this_object());
+    }
+    if (pointerp(t) && stringp(t[2]) && stringp(t[3]) &&
+        member(ship->QueryHarbours(),t[2]) != -1)
+    {
+      write("Du behaeltst Dein bisheriges Reiseziel ("+t[3]+") bei.\n");
+      SetProp(P_TRAVEL_INFO, ({ environment(), ship, t[2], t[3] }) );
+    }
+    else
+    {
+      SetProp(P_TRAVEL_INFO, ({ environment(), ship, 0, 0 }) );
+    }
+    return 1;
+  }
+  return 0;
+}
+
diff --git a/std/player/util.c b/std/player/util.c
new file mode 100644
index 0000000..a6c8fa9
--- /dev/null
+++ b/std/player/util.c
@@ -0,0 +1,111 @@
+// MorgenGrauen MUDlib
+//
+// player/util. -- Utilities
+//
+// $Id: util.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include "/sys/player/util.h"
+#include "/sys/thing/properties.h"
+
+public void ShowPropList(string *props) 
+{
+  int i,j;
+
+  j=sizeof(props);
+
+  for ( i=0; i<j ; i++) 
+  {
+    write("*"+props[i]+": ");
+    PrettyDump(QueryProp(props[i]));
+    write("\n");
+  }
+}
+
+static void PrettyDump(mixed x) 
+{
+  if (pointerp(x)) 
+  {
+    DumpArray(x);
+  }
+  else if (mappingp(x))
+  {
+    DumpMapping(x);
+  }
+  else if (objectp(x)) 
+  {
+    write ("OBJ("+object_name(x)+")");
+  }
+  else if (stringp(x))
+  {
+    write("\""+x+"\"");
+  }
+  else
+  {
+    write (x);
+  }
+}
+
+static void DumpArray(mixed *x) 
+{
+  int i,j;
+
+  write ("({ ");
+  if ( (j=sizeof(x))>0 )
+  {
+    for ( i=0 ; i<(j-1) ; i++) 
+    {
+      PrettyDump(x[i]);
+      write(", ");
+    }
+    PrettyDump(x[i]);
+    write(" ");
+  }
+  write ("})");
+}
+
+static void DumpMapping(mapping x)
+{
+  int   i, c, s;
+  mixed *ind;
+
+  write("([ ");
+
+  if ( (c=sizeof(ind=m_indices(x)))<1 )
+  {
+    write(" ])");
+    return;
+  }
+
+  s=get_type_info(x,1);
+
+  DumpKeyValPair(x, ind[0], s);
+  for ( i=1 ; i<c ; i++ )
+  {
+    write(", ");
+    DumpKeyValPair(x, ind[i], s);
+  }
+  write(" ])");
+}
+
+// Lacht nicht ueber den Namen!!! -Boing
+// Nein, ueber den Namen lache ich nicht ... -Paracelsus
+static void DumpKeyValPair(mapping x, mixed key, int size)
+{ int j, vc;
+
+  PrettyDump(key);
+  write(" : ");
+  PrettyDump(x[key,0]);
+
+  for ( j=1; j<size; j++)
+  {
+    write("; ");
+    PrettyDump(x[key, j]);
+  }
+}
diff --git a/std/player/viewcmd.c b/std/player/viewcmd.c
new file mode 100644
index 0000000..5d7e618
--- /dev/null
+++ b/std/player/viewcmd.c
@@ -0,0 +1,754 @@
+// MorgenGrauen MUDlib
+//
+// player/viewcmd.c -- player view command handling
+//
+// $Id: viewcmd.c 9548 2016-04-17 19:28:22Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include "/sys/thing/properties.h"
+#include "/sys/living/put_and_get.h"
+#include "/sys/living/description.h"
+
+#include <container.h>
+#include <player.h>
+#include <properties.h>
+#include <rooms.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <moving.h>
+#include <new_skills.h>
+#include <ansi.h>
+
+#include <sys_debug.h>
+
+varargs mixed More(string str, int fflag, string returnto);
+
+void create()
+{
+  Set(P_BRIEF, SAVE, F_MODE);
+  Set(P_BLIND, SAVE, F_MODE);
+}
+
+int _toggle_brief()
+{
+  int brief;
+
+  if (query_verb()=="kurz")
+    brief=1;
+  else if (query_verb()=="ultrakurz")
+    brief=2;
+  else brief=0;
+  SetProp(P_BRIEF, brief);
+  write("Du bist nun im \""+
+        (brief?(brief==1?"Kurz":"Ultrakurz"):"Lang")+"\"modus.\n");
+  return 1;
+}
+
+private int sortinv(mixed a, mixed b) { return a[0] > b[0]; }
+
+private string collectinv(mixed obj)
+{
+  if(obj[0]=="") return 0;
+  return (obj[2] ? " " : "")
+       + obj[0]
+       + (obj[1] > 1 ? " ("+obj[1]+")" : "");
+}
+
+#define I_AUTOLOAD      1
+#define I_KEEP          4
+#define I_FORMATTED     16
+#define I_ARMOUR        64
+#define I_SORT          256
+#define I_WEAPON        1024
+#define I_FORCE_SORT    4096
+#define I_NO_TABLE      16384
+
+private string getflags(string arg, int flags)
+{
+  int no, i;
+  if(sizeof(arg) < 2) return 0;
+  no = (arg[0] == '-');
+
+  for(i = 1; i < sizeof(arg); i++)
+  {
+    switch(arg[i])
+    {
+    case 'a': flags |= I_AUTOLOAD << no; break;
+    case 'b': flags |= I_KEEP << no; break;
+    case 'f': flags |= I_FORMATTED << no; break;
+    case 'r': flags |= I_ARMOUR << no; break;
+    case 's': flags |= I_SORT << no; break;
+    case 'w': flags |= I_WEAPON << no; break;
+    case 'v': flags |= (I_ARMOUR | I_WEAPON) << !no; break;
+    case '1': flags |= I_NO_TABLE; break;
+        // Die Option macht nur Aerger und kommentiert ist sie eh nicht.
+        // Wer das dringend braucht, soll Wargons Schiebepuzzle benutzen.
+        //
+        // Tiamak, 15.10.2000
+        //    case 'S': flags |= I_FORCE_SORT << no; break;
+    default : return arg[i..i]; // wird ausgegeben an Spieler als unbekannt.
+    }
+  }
+  return 0;
+}
+
+static int _check_keep(object ob)
+{
+  return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
+}
+
+int _inventory(string str)
+{
+  mixed *args, output;
+  int ansi, i, flags, minv;
+  mixed inventory, weapons, armours, misc;
+  string format;
+
+  if(CannotSee()) return 1;
+
+  if((str = _unparsed_args()) && str!="")
+  {
+    string error;
+    error = "Benutzung: i[nventar] [-/+1abfrsvw]\n";
+    args = regexp(regexplode(str, "[-+][1abfrswv][1abfrswv]*"),
+                  "[-+][1abfrswv][1abfrswv]*");
+    if(!sizeof(args)) return (_notify_fail(error), 0);
+    if(sizeof(args = map(args, #'getflags/*'*/, &flags) - ({ 0 })))
+    {
+      printf("%s: Unbekanntes Argument.\n"+error, implode(args, ", "));
+      return 1;
+    }
+  }
+  // Fuer Spieler gehen nur sichtbare Objekte in den Algorithmus
+  if (IS_LEARNING(ME))  
+    inventory = all_inventory(ME);
+  else
+    inventory = filter_objects(all_inventory(ME), "short");
+  
+  ansi = member(({"vt100", "ansi"}), QueryProp(P_TTY)) != -1;
+  minv = 1 | (flags & (I_FORMATTED | (I_FORMATTED << 1)) ? 2 : 0);
+  format = (flags & I_NO_TABLE) ? "=" : "#";
+
+//  if(flags & (I_FORCE_SORT | I_FORCE_SORT << 1))
+//  {
+//    closure sf;
+//    sf = flags & I_FORCE_SORT ? #'> : #'<;
+//    s = sort_array(s, lambda(({'a, 'b}),
+//                             ({#'funcall, sf,
+//                                   ({#'||,({#'call_other,'a,"short"}),""}),
+//                                   ({#'||,({#'call_other,'b,"short"}),""})})));
+//    map_objects(s, "move", this_object());
+//    s = all_inventory(ME);
+//  }
+
+  if (flags & I_AUTOLOAD)
+    inventory = filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
+  else if (flags & (I_AUTOLOAD << 1))
+    inventory -= filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
+
+  if(flags & I_KEEP)
+    inventory = filter(inventory,#'_check_keep);
+  else if(flags & (I_KEEP << 1))
+    inventory -= filter(inventory,#'_check_keep);
+
+  armours = filter_objects(inventory, "QueryProp", P_ARMOUR_TYPE);
+  // Kleidung dazu addieren, vorher die erkannten Ruestungen abziehen, die
+  // muessen nicht nochmal durchiteriert werden.
+  armours += filter_objects(inventory-armours, "IsClothing");
+  // Ruestungen werden hier nicht abgezogen, weil es Kram gibt, welche sowohl
+  // Ruestung als auch Waffe ist.
+  weapons = filter_objects(inventory, "QueryProp", P_WEAPON_TYPE);
+  misc = inventory - weapons - armours; // rest ;-)
+
+  if(flags & I_WEAPON)
+  {
+    inventory = weapons; misc = ({});
+    if(!(flags & (I_ARMOUR))) armours = ({});
+  }
+  if(flags & I_ARMOUR)
+  {
+     inventory = armours; misc = ({});
+     if(!(flags & I_WEAPON)) weapons = ({});
+  }
+  if(flags & (I_WEAPON << 1)) { weapons = ({}); inventory = armours + misc; }
+  if(flags & (I_ARMOUR << 1)) { armours = ({}); inventory = weapons + misc; }
+
+  output = "";
+  if(flags & (I_FORMATTED | (I_FORMATTED << 1)))
+  {
+    inventory = make_invlist(this_player(), inventory, minv);
+    if(flags & (I_SORT | (I_SORT << 1)))
+    inventory = sort_array(inventory, #'sortinv/*'*/);
+    output += sprintf("%"+format+"-78s\n",
+                      implode(map(inventory,#'collectinv/*'*/),"\n"));
+  }
+  else
+  {
+    if(weapons && sizeof(weapons))
+    {
+      weapons = make_invlist(this_player(), weapons, minv);
+      if(flags & (I_SORT | (I_SORT << 1)))
+        weapons = sort_array(weapons, #'sortinv/*'*/);
+      output += (ansi?ANSI_BOLD:"") + "Waffen:" + (ansi?ANSI_NORMAL:"")+"\n"
+              + sprintf("%"+format+"-78s\n",
+                        implode(map(weapons, #'collectinv/*'*/), "\n"));
+    }
+    if(armours && sizeof(armours))
+    {
+      armours = make_invlist(this_player(), armours, minv);
+      if(flags & (I_SORT | (I_SORT << 1)))
+        armours = sort_array(armours, #'sortinv/*'*/);
+      output += (ansi?ANSI_BOLD:"") 
+              + "Kleidung & Ruestungen:" + (ansi?ANSI_NORMAL:"")+"\n"
+              + sprintf("%"+format+"-78s\n",
+                        implode(map(armours, #'collectinv/*'*/), "\n"));
+    }
+    if(misc && sizeof(misc))
+    {
+      misc = make_invlist(this_player(), misc, minv);
+      if(flags & (I_SORT | (I_SORT << 1)))
+        misc = sort_array(misc, #'sortinv/*'*/);
+      output += (ansi?ANSI_BOLD:"") + "Verschiedenes:" + (ansi?ANSI_NORMAL:"")+"\n"
+              + sprintf("%"+format+"-78s\n",
+                        implode(map(misc, #'collectinv/*'*/), "\n"));
+    }
+  }
+
+  // Spielerwunsch: 'inventar' sollte doch das Bezugsobjekt auf den Spieler
+  // aendern.
+  SetProp(P_REFERENCE_OBJECT, this_object());
+
+  if (output=="") 
+    output += (ansi?ANSI_BOLD:"")+"Die Liste ist leer."+(ansi?ANSI_NORMAL:"");
+  More(output);
+  return 1;
+}
+
+private nosave int exa_cnt;
+private nosave int exa_time;
+private nosave string *exa;
+
+varargs int _examine(string str, int mode)
+{
+  object base, *objs, env;
+  string what, detail, parent, out, error;
+  int i, size, done;
+
+  if(CannotSee()) return 1;
+
+  _notify_fail("Was willst Du denn untersuchen?\n");
+  if (!str) return 0;
+
+  if (member(({"boden","decke","wand","waende"}),old_explode(str," ")[0]) == -1) {
+    exa_cnt -= (time() - exa_time)/2;
+    exa_time = time();
+    exa_cnt++;
+    if (!exa)
+      exa = ({ str });
+    else
+      exa += ({ str });
+    if (exa_cnt > 10) {
+      log_file("ARCH/LOOK", 
+          sprintf("%s: %s in %s\n%@O\n",dtime(time()),getuid(this_object()), 
+            environment() ? object_name(environment()) : "???",exa), 150000);
+      exa_cnt = 0;
+      exa = ({});
+    }
+    else if (exa_cnt < 0) {
+      exa_cnt = 0;
+      exa = ({});
+    }
+  }
+  // do we look at an object in our environment ?
+  if (sscanf(str,"%s in raum", what) || sscanf(str,"%s im raum", what))
+      base = environment();
+  // is the object inside of me (inventory)
+  else if (sscanf(str,"%s in mir", what) || sscanf(str,"%s in dir", what))
+      base = this_object();
+  else {
+      what = str;
+      // get the last object we looked at
+      base = QueryProp(P_REFERENCE_OBJECT);
+      
+      // if a reference object exists, test for its existance in the room
+      // or in our inventory
+      if (objectp(base))
+      {
+       if (base == environment() || base==this_object())
+        {
+          // Umgebung oder Spieler selber sind als Bezugsobjekt immer in
+          // Ordnung, nichts machen.
+        }
+        // Das Referenzobjekt darf nicht unsichtbar sein.
+        else if (!base->short())
+          base = 0;
+        else if(member(deep_inventory(environment()), base) != -1)
+        {
+          foreach(env : all_environment(base)) {
+            // Ist eine Umgebung Living oder intransparenter Container oder
+            // unsichtbar?
+            if (living(env) || !env->QueryProp(P_TRANSPARENT)
+                || !env->short())
+            {
+              // in dem Fall ist ende, aber wenn das gefundene Env nicht
+              // dieses Living selber oder sein Env ist, wird das
+              // Referenzobjekt zusaetzlich genullt.
+              if (env != this_object() && env != environment())
+                base = 0;
+              break;
+            }
+          }
+        }
+        else
+          base = 0; // nicht im Raum oder Inventory
+      }
+  }
+
+  // scan input if we want a specific object to look at
+  if(sscanf(what, "%s an %s", detail, parent) == 2 ||
+     sscanf(what, "%s am %s", detail, parent) == 2 ||
+     sscanf(what, "%s in %s", detail, parent) == 2 ||
+     sscanf(what, "%s im %s", detail, parent) == 2)
+  {
+    // if an ref object exists get its inventory. (Oben wurde sichergestellt,
+    // dass das Referenzobjekt einsehbar ist)
+    if(base)
+      objs = base->locate_objects(parent, 1) || ({});
+    else {
+      // else get our inv and env
+      objs = environment()->locate_objects(parent, 1)
+           + locate_objects(parent, 1);
+    }
+    objs = filter_objects(objs, "short"); // nur sichtbare...
+    if(sizeof(objs) > 1)
+      return (notify_fail("Es gibt mehr als eine(n) "+capitalize(parent)+".\n"), 0);
+    else
+    {
+      if (sizeof(objs))
+        base = objs[0];
+      else
+        return (notify_fail("Hier ist kein(e) "+capitalize(parent)+".\n"), 0);
+    }
+    objs = 0;
+  }
+  else detail = what;
+
+  int base_was_env = 1;
+  do {
+    // if a base exists get its inventory, else get our inv and env
+    if (base)
+    {
+      if  (base == this_object() || base == environment() ||
+          (base->QueryProp(P_TRANSPARENT) && !living(base)))
+      {
+        // ich kann in base reingucken...
+        objs = base->locate_objects(detail, 1) || ({});
+      }
+      else
+      {
+        // Referenzobjekt da, aber nicht reinguckbar. base aber nicht nullen,
+        // denn es ist ja noch gueltig fuer Detailsuchen an base...
+        objs = ({});
+      }
+    }
+    else
+    {
+      objs = environment()->locate_objects(detail, 1)
+           + locate_objects(detail, 1);
+      base = environment();
+    }
+    objs = filter_objects(objs, "short"); // nur sichtbare...
+
+    if(!sizeof(objs))
+    {
+      // wenn keine Objekte gefunden wurden, wird nach Details gesucht...
+      if((out = base->GetDetail(detail, QueryProp(P_REAL_RACE),SENSE_VIEW)) ||
+         (out = base->GetDoorDesc(detail)))
+      {
+        SetProp(P_REFERENCE_OBJECT, base);
+        return (write(out), 1);
+      }
+      else
+      {
+        // wenn auch keine Details gefunden, dann schauen, ob Ende ist
+        // (base==env) oder wir evtl. noch im env suchen koennen.
+        if (base == environment())
+        {
+          if (base_was_env) {
+            // in diesem Fall war das Env das Bezugsobjekt - daher wegwerfen.
+            SetProp(P_REFERENCE_OBJECT, 0);
+            _notify_fail("Sowas siehst Du da nicht!\n");
+          }
+          else {
+            _notify_fail("Sowas siehst Du auch da nicht!\n");
+            // in diesem Fall war nicht das Env das Bezugsobjekt - es soll
+            // behalten und nicht geloescht werden.
+          }
+          return 0;
+        }
+        else {
+          base_was_env=0;
+          write(break_string("Du findest an "+base->name(WEM)
+                +" kein \"" + capitalize(detail) + "\"."
+                 " Dein Blick wendet sich der Umgebung zu.",78));
+          base = 0;
+        }
+      }
+    }
+    else  // Objekte gefunden!
+      done = 1;
+  } while(!done);
+
+  // Es muss min. ein (sichtbares) Objekt geben, sonst waere man hier nicht
+  // hingekommen.
+  object ob = objs[0];
+  SetProp(P_REFERENCE_OBJECT, ob);
+  tell_object(ME, ob->long(mode));
+  return 1;
+}
+
+varargs int _sense_exa(string str)
+{
+  object base, *objs, env;
+  string what, detail, parent, out, error;
+  int sense;
+
+  if(member(({"riech","rieche","schnupper","schnuppere"}),query_verb())!=-1)
+  {
+    _notify_fail("Du kannst nichts Besonderes riechen.\n");
+    sense = SENSE_SMELL;
+  }
+  else if(member(({"lausche","lausch","hoer","hoere"}),query_verb())!=-1)
+  {
+    if(QueryProp(P_DEAF))
+      return notify_fail("Du bist taub!\n"), 0;
+
+    _notify_fail("Du kannst nichts Besonderes hoeren.\n");
+    sense = SENSE_SOUND;
+  }
+  else if(member(({"taste","beruehre","beruehr"}),query_verb())!=-1)
+  {
+    sense = SENSE_TOUCH;
+    // ein "ab" ganz am Ende von str wird abgeschnitten, es soll sowohl "taste
+    // x ab" als auch "taste x" funktionieren.
+    if (str) {
+      _notify_fail("Sowas kannst Du hier nicht ertasten!\n");
+      string *tmp = explode(str," ");
+      if (sizeof(tmp) > 1 && tmp[<1] == "ab")
+        str = implode(tmp[0..<2], " ");
+    }
+    else
+      _notify_fail("Was willst Du denn abtasten?\n");
+  }
+  else if (member(({"lies","lese","les"}), query_verb()) > -1)
+  {
+    _notify_fail("Was willst Du lesen?\n");
+    if ( !str ) // Kein SENSE_DEFAULT zulassen.
+      return 0;
+    if (this_object()->CannotSee()) {
+      notify_fail("Du kannst nichts sehen!\n");
+      return 0;
+    }
+    sense = SENSE_READ;
+  }
+
+  if (!str) {
+    if(!detail =
+        environment()->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),sense))
+      return 0;
+    write(detail);
+    return 1;
+  }
+  else if(sscanf(str,"an %s",what)==1)
+    str=what;
+
+  // do we look at an object in our environment ?
+  if (sscanf(str,"%s in raum", what) || sscanf(str,"%s im raum", what))
+      base = environment();
+  // is the object inside of me (inventory)
+  else if (sscanf(str,"%s in mir", what) || sscanf(str,"%s in dir", what))
+      base = this_object();
+  else {
+      what = str;
+      // get the last object we looked at
+      base = QueryProp(P_REFERENCE_OBJECT);
+      
+      // if a reference object exists, test for its existance in the room
+      // or in our inventory
+      if (objectp(base))
+      {
+        if (base == environment() || base==this_object())
+        {
+          // Umgebung oder Spieler selber sind als Bezugsobjekt immer in
+          // Ordnung, nichts machen.
+        }
+        // Das Referenzobjekt darf nicht unsichtbar sein.
+        else if (!base->short())
+          base = 0;
+        else if(member(deep_inventory(environment()), base) != -1)
+        {
+          foreach(env : all_environment(base)) {
+            // Ist eine Umgebung Living oder intransparenter Container oder
+            // unsichtbar?
+            if (living(env) || !env->QueryProp(P_TRANSPARENT)
+                || !env->short())
+            {
+              // in dem Fall ist ende, aber wenn das gefundene Env nicht
+              // dieses Living selber oder sein Env ist, wird das
+              // Referenzobjekt zusaetzlich genullt.
+              if (env != this_object() && env != environment())
+                base = 0;
+              break;
+            }
+          }
+        }
+        else
+          base = 0; // nicht im Raum oder Inventory
+      }
+  }
+
+  // scan input if we want a specific object to look at
+  if(sscanf(what, "%s an %s", detail, parent) == 2 ||
+     sscanf(what, "%s am %s", detail, parent) == 2 ||
+     sscanf(what, "%s in %s", detail, parent) == 2 ||
+     sscanf(what, "%s im %s", detail, parent) == 2)
+  {
+    // if an ref object exists get its inventory. (Oben wurde sichergestellt,
+    // dass das Referenzobjekt einsehbar ist)
+
+    if(base)
+      objs = base->locate_objects(parent, 1) || ({});
+    else
+    {
+      // else get our inv and env
+      objs = environment()->locate_objects(parent, 1)
+           + locate_objects(parent, 1);
+    }
+    objs = filter_objects(objs, "short"); // nur sichtbare...
+    if(sizeof(objs) > 1)
+      return (notify_fail("Es gibt mehr als eine(n) "+capitalize(parent)+".\n"), 0);
+    else
+    {
+      if(sizeof(objs))
+          base = objs[0];
+      else
+          return (notify_fail("Hier ist kein(e) "+capitalize(parent)+".\n"), 0);
+    }
+    objs = 0;
+  }
+  else detail = what;
+
+  // wie auch immer haben wir jetzt ein Bezugsobjekt.
+  int maxtries=3;
+  do {
+    int base_was_env=1;
+    // als ersten werden in Frage kommende Objekte gesucht. Wenn base
+    // existiert (idR nur im ersten Durchlauf), wird dort gesucht, sonst in
+    // Env und Inv.
+    if (base)
+    {
+      if  (base == this_object() || base == environment() ||
+          (base->QueryProp(P_TRANSPARENT) && !living(base)))
+      {
+        // ich kann in base reingucken...
+        objs = base->locate_objects(detail, 1) || ({});
+      }
+      else
+      {
+        // Referenzobjekt da, aber nicht reinguckbar. base aber nicht nullen,
+        // denn es ist ja noch gueltig fuer Detailsuchen an base...
+        objs = ({});
+      }
+    }
+    else
+    {
+      objs = environment()->locate_objects(detail, 1)
+           + locate_objects(detail, 1);
+      base = environment();
+    }
+    objs = filter_objects(objs, "short"); // nur sichtbare...
+
+    if (sizeof(objs))
+    {
+      // Objekte gefunden, mal schauen, ob die taugen (d.h. fuer den jew. Sinn
+      // Infos haben. Wenn nicht, muessen wir weitersuchen.
+      // Aber erstmal die Objekte durchlaufen.
+      foreach(object ob: objs)
+      {
+        if (sense == SENSE_READ)
+        {
+          // Extrawurst: P_READ_MSG auch noch abfragen.
+          out = ob->QueryProp(P_READ_MSG);
+          if (!stringp(out))
+            out = ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),SENSE_READ);
+        }
+        else 
+          out=ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),sense);
+        if (stringp(out))
+        {
+          SetProp(P_REFERENCE_OBJECT, ob);
+          tell_object(ME, out);
+          return 1;
+        }
+      }
+    }
+
+    // Keine Objekte gefunden, die in Frage kommen. Nach Details suchen.
+    if(out = base->GetDetail(detail, QueryProp(P_REAL_RACE),sense))
+    {
+      SetProp(P_REFERENCE_OBJECT, base);
+      return (write(out), 1);
+    }
+    else
+    {
+      // Auch keine Details gefunden... Wenn wir uns noch das Env angucken
+      // koennen (weil base != env), dann machen wir das, ansonsten ist
+      // jetzt hier leider Ende...
+      if(base == environment())
+      {
+        if (base_was_env)
+          SetProp(P_REFERENCE_OBJECT, 0);
+        return 0;
+      }
+      else
+      {
+        // nochmal im naechsten Schleifendurchlauf ohne base probieren.
+        base = 0;
+        base_was_env = 0; 
+      }
+    }
+  } while(--maxtries);
+
+  // nach dieser Schleife sollte man nie ankommen...
+  raise_error(sprintf("_sense_exa(): zuviele Versuche, etwas zu finden."));
+
+  return 0;
+}
+
+varargs int look_into(string str,int mode)
+{
+  object *found_obs;
+
+  if( CannotSee() ) return 1;
+  _notify_fail("Wo willst Du denn reinschauen ?\n");
+  found_obs=find_obs(str,PUT_GET_NONE);
+  if (!found_obs)
+  {
+    if (environment() &&
+        (environment()->GetDetail(str,QueryProp(P_REAL_RACE))||
+         environment()->GetDoorDesc(str)))
+      _notify_fail("Da kannst Du so nicht reinsehen.\n");
+    return 0;
+  }
+  return _examine(str, mode);
+}
+
+/* Gebe die Umgebung des aktiven Spielers zurueck, lasse dabei  */
+/* rekursiv geschachtelte Raeume zu.                            */
+/* Wenn allow_short 0 ist, so wird immer die long-descr benutzt */
+varargs string env_descr(int allow_short,int flags, int force_short )
+{
+  object env;
+  int brief;
+
+  env = environment(ME);
+
+  if(!env)
+    return "Du schwebst im Nichts ... Du siehst nichts, rein gar nichts ...\n";
+
+  if (!force_short && (!allow_short || !QueryProp(P_BRIEF)))
+      return env->int_long(ME,ME,flags);
+
+  if (!flags && ((brief=QueryProp(P_BRIEF))>=2))
+      return "";
+
+  return env->int_short(ME,ME);
+}
+
+int _look(string str)
+{
+  string s;
+  int flag;
+
+  if(CannotSee()) return 1;
+
+  if(!str)
+  {
+    SetProp(P_REFERENCE_OBJECT, 0);
+    write( env_descr() );
+    return 1;
+  }
+  if(str=="-f" || str=="genau")
+  {
+    SetProp(P_REFERENCE_OBJECT, 0);
+    write( env_descr(0,2) );
+    return 1;
+  }
+  if(str=="-k" || str=="kurz")
+  {
+    SetProp(P_REFERENCE_OBJECT, 0);
+    write( env_descr(1,2,1) );
+    return 1;
+  }
+  if(str[0..2]=="-f "){
+    flag=2;
+    str=str[3..];
+  }
+  else if(str[0..5]=="genau "){
+    flag=2;
+    str=str[6..];
+  }
+  else flag = 0;
+  if (sscanf(str,"%s an",s)) str=s;
+  if (sscanf(str,"%s in mir",s)||sscanf(str,"%s in dir",s)) return _examine(str,flag);
+  if (sscanf(str,"in %s",s)) return look_into(s,flag);
+  return _examine(str,flag);
+}
+
+int _equipment(string arg)
+{
+  if (CannotSee()) return 1;
+  call_other("/std/player/invmaster/invmaster", "ShowInv", ME, arg);
+  return 1;
+}
+
+static mixed _query_localcmds()
+{
+  return
+    ({({"ausruestung", "_equipment",0,0}),
+      ({"i","_inventory",0,0}),
+      ({"inv","_inventory",0,0}),
+      ({"inventur","_inventory",0,0}),
+      ({"schau","_look",0,0}),
+      ({"schaue","_look",0,0}),
+      ({"unt","_examine",0,0}),
+      ({"untersuch","_examine",0,0}),
+      ({"betracht","_examine",0,0}),
+      ({"untersuche","_examine",0,0}),
+      ({"betrachte","_examine",0,0}),
+      ({"betr","_examine",0,0}),
+      ({"lausche","_sense_exa",0,0}),
+      ({"lausch","_sense_exa",0,0}),
+      ({"hoer","_sense_exa",0,0}),
+      ({"hoere","_sense_exa",0,0}),
+      ({"lies","_sense_exa",0,0}),
+      ({"lese","_sense_exa",0,0}),
+      ({"les","_sense_exa",0,0}),
+      ({"schnupper","_sense_exa",0,0}),
+      ({"schnuppere","_sense_exa",0,0}),
+      ({"riech","_sense_exa",0,0}),
+      ({"rieche","_sense_exa",0,0}),
+      ({"taste","_sense_exa",0,0}),
+      ({"beruehre","_sense_exa",0,0}),
+      ({"beruehr","_sense_exa",0,0}),
+      ({"kurz","_toggle_brief",0,0}),
+      ({"lang","_toggle_brief",0,0}),
+      ({"ultrakurz","_toggle_brief",0,0}) 
+    });
+}
diff --git a/std/post.c b/std/post.c
new file mode 100644
index 0000000..d0dbbb1
--- /dev/null
+++ b/std/post.c
@@ -0,0 +1,42 @@
+/* Neue std/post
+   (C) 1993 by Loco
+*/
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/room";
+
+#include "/mail/post.h"
+#include <properties.h>
+#include <rooms.h>
+
+void create() {
+  int i;
+  (::create());
+  SetProp(P_INT_SHORT,"Postamt");
+  SetProp(P_INT_LONG,"\
+Dies ist ein Testpostamt.\n\
+Von hier aus kannst Du Briefe an Deine Mitspieler schicken und Briefe von\n\
+ihnen lesen. Wenn Du das willst, tippe 'post' oder 'mail',\n\
+bzw. 'mail <spieler>'.\n");
+  SetProp( P_LIGHT, 1 );
+  AddCmd("post","do_mail");
+  AddCmd("mail","do_mail");
+  SetProp(P_INDOORS,1);
+    for (i=0;i<NRCABINS;++i) AddItem(MAILCABIN,REFRESH_REMOVE);
+    AddItem(COUNTER,REFRESH_REMOVE);
+}
+
+int do_mail(string str) {
+  object mailer;
+  if (this_interactive()!=this_player()) return 0;
+  mailer=clone_object(MAILER);
+  mailer->SetOfficeName(short());
+  mailer->do_mail(str);
+  return 1;
+}
+
+
diff --git a/std/preload_file b/std/preload_file
new file mode 100644
index 0000000..5717fca
--- /dev/null
+++ b/std/preload_file
@@ -0,0 +1,36 @@
+/secure/errord
+/secure/materialdb
+/p/daemon/eventd
+/p/daemon/channeld
+/room/void
+/std/shells/magier
+/p/daemon/routingd
+/p/daemon/sastatd
+/std/shells/elf
+/std/shells/human
+/std/shells/dwarf
+/std/shells/hobbit
+/std/shells/feline
+/secure/login
+/secure/mailer
+/secure/combat
+/obj/uhr
+/obj/mliste
+/secure/scoremaster
+/secure/explorationmaster
+/secure/questmaster
+/secure/lepmaster
+/secure/potionmaster
+/items/money
+/obj/tools/xtool
+/obj/tools/lupe
+/secure/udp_mail
+/secure/inetd
+/secure/awmaster
+/p/daemon/traveld
+/p/daemon/mand
+/p/daemon/svn2news
+/p/daemon/moneylog
+/p/daemon/uptime_master
+/players/tiamak/workroom
+/secure/zweities
diff --git a/std/pub.c b/std/pub.c
new file mode 100644
index 0000000..53b019a
--- /dev/null
+++ b/std/pub.c
@@ -0,0 +1,75 @@
+// MorgenGrauen MUDlib
+//
+// pub.c -- Alles, was eine Kneipe braucht.
+//
+// $Id: pub.c 6571 2007-10-21 14:41:10Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/room";
+inherit "/std/room/pub";
+
+#include <properties.h>
+
+protected void create()
+{
+  room::create();
+  pub::create();
+  SetProp(P_LIGHT,1);
+  SetProp(P_INDOORS,1);
+  SetProp(P_INT_LONG,
+   "Der Magier, der diesen Pub erschuf, war leider zu faul, eine "+
+   "Beschreibung\ndafuer herzustellen.\n");
+  SetProp(P_INT_SHORT,"generic pub");
+  AddItem("/obj/topliste",REFRESH_REMOVE);
+}
+
+protected void create_super() {
+      set_next_reset(-1);
+}
+
+void reset()
+{
+   room::reset();
+   pub::reset();
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/std/ranged_weapon.c b/std/ranged_weapon.c
new file mode 100644
index 0000000..5578483
--- /dev/null
+++ b/std/ranged_weapon.c
@@ -0,0 +1,275 @@
+// MorgenGrauen MUDlib
+//
+// ranged_weapon.c -- Standardobjekt fuer Fernkampfwaffen
+//
+// $Id: ranged_weapon.c 8820 2014-05-14 19:31:46Z Zesstra $
+
+// Gebraucht wird Munition mit einer MUN_* id. Dieses Objekt sollte ausserdem
+// P_DAM_TYPE setzen sowie P_SHOOTING_WC und ggf. P_HIT_FUNC. In der Regel
+// sollten diese Objekte units sein, da groessere Anzahlen davon gebraucht
+// werden.
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/living/skill_utils";
+inherit "/std/weapon";
+
+#include <ranged_weapon.h>
+
+#include <properties.h>
+#include <defines.h>
+#include <combat.h>
+#include <new_skills.h>
+#include <unit.h>
+
+static int last_shoot;
+
+protected void create()
+{
+    ::create();
+
+    SetProp(P_WEAPON_TYPE, WT_RANGED_WEAPON);
+    SetProp(P_STRETCH_TIME, 1);
+    SetProp(P_AMMUNITION, MUN_ARROW);
+    SetProp(P_SHOOTING_WC, 70);
+    SetProp(P_RANGE, 50);
+    SetProp(P_INFO, "Syntax: schiesse WAS auf WEN?\n");
+    // fuer den Fall, das man den Bogen als Knueppel nutzt...
+    SetProp(P_WC, 30);
+    SetProp(P_QUALITY, 100);
+
+    AddCmd( ({"schiess", "schiesse"}), "cmd_shoot");
+}
+      
+static int shoot_dam(mapping shoot)
+// ausgegliedert, damit man diese Funktion ggf. ueberschreiben kann.
+{   mixed   res;
+    object  hitfunc;
+    closure usk;
+
+    // diesselbe Formel wie bei Nahkampfwaffen, nur das hier DEX anstatt STR
+    // benutzt wird, und die WC der Munition noch addiert wird
+    shoot[SI_SKILLDAMAGE] = random(1+( 2*(shoot[P_WC]+shoot[P_SHOOTING_WC])
+                                     + 10*PL->QueryAttribute(A_DEX) )/3);
+    // HitFunc der Munition addieren
+    if ( objectp(hitfunc=(shoot[P_AMMUNITION]->QueryProp(P_HIT_FUNC))) )
+      shoot[SI_SKILLDAMAGE] += (hitfunc->HitFunc(shoot[SI_ENEMY]));
+
+    usk=symbol_function("UseSkill",PL);
+
+    // Schuss-Skill der Gilde fuer diesen Munitionstyp anwenden
+    if ( mappingp(res=funcall(usk,SHOOT(shoot[P_WEAPON_TYPE]),
+                              deep_copy(shoot))) )
+      SkillResTransfer(res, &shoot);
+
+    // Allgemeinen Schuss-Skill der Gilde anwenden
+    if ( mappingp(res=funcall(usk,SK_SHOOT,deep_copy(shoot))) )
+      SkillResTransfer(res, &shoot);
+
+    return shoot[SI_SKILLDAMAGE];
+}
+
+static string FindRangedTarget(string str, mapping shoot)
+{   string wen;
+    mixed  area;
+    int    psr;
+
+    if ( sscanf(str, "%s auf %s", str, wen)!=2 )
+    {
+        shoot[SI_ENEMY]=PL->SelectFarEnemy(PL->PresentEnemies(),1,4);
+        str = shoot[P_WEAPON_TYPE];
+    }
+    else if ( objectp(shoot[SI_ENEMY]=present(wen, environment(PL))) )
+    {
+        return str;
+    }
+    else if ( ( (area=(environment(PL)->QueryProp(P_TARGET_AREA)))
+               || (area=environment(environment(PL))) )
+             && ( objectp(area)
+                 || ( stringp(area)
+                     && objectp(area=find_object(area)) ) )
+             && (psr=environment(PL)->QueryProp(P_SHOOTING_AREA)) )
+    {
+        shoot[SI_ENEMY]=present(wen, area);
+        if ( shoot[SI_ENEMY] && (psr>QueryProp(P_RANGE)) )
+        {
+            write(break_string((shoot[SI_ENEMY]->Name(WER))+
+                " ist leider ausserhalb der Reichweite "+name(WESSEN, 1)+".",
+               78));
+            return 0;
+         }        
+    }
+
+    if ( !shoot[SI_ENEMY] )
+    {
+        write("Es ist kein Feind vorhanden auf den Du schiessen koenntest.\n");
+        return 0;
+    }
+
+    return str;
+}
+
+static int cmd_shoot(string str)
+{   int    dam;
+    string no_at;
+    object quiver;
+    mapping shoot;
+
+    if ( PL->QueryProp(P_GHOST) )
+    {
+        write("Deine immateriellen Finger gleiten durch die Waffe hindurch.\n");
+        return 1;
+    }
+   
+    if ( !QueryProp(P_NOGET) )
+    {
+        // Katapulte oder aehnliches die von Magiern aufgestellt werden
+        // muessen natuerlich nicht gezueckt werden :).
+        if ( !QueryProp(P_WIELDED) )
+        {
+            notify_fail(break_string("Du solltest "+name(WEN, 1)+
+                                     " dazu schon zuecken.", 78));
+            return 0;
+        }
+
+        if ( (PL->InFight()) && ((PL->PresentPosition())<2) )
+        {
+            write(break_string(
+                "Du solltest Dich erst etwas weiter zurueckziehen, um mit "+
+                name(WEM, 1)+" schiessen zu koennen!", 78));
+            return 1;
+        }
+    }
+
+    if ( ( (QueryProp(P_EQUIP_TIME)+QueryProp(P_STRETCH_TIME)*2) > time() )
+        || ( (last_shoot+QueryProp(P_STRETCH_TIME)) > absolute_hb_count() ) )
+    {
+        write(break_string("Du kannst mit "+name(WEM, 1)+
+                           " noch nicht wieder schiessen.", 78));
+        return 1;
+    }
+
+    if ( (PL->QueryProp(P_DISABLE_ATTACK))>0 )
+    {
+        write(break_string("Solange Du noch gelaehmt bist, kannst Du "+
+                           name(WEN, 1)+" nicht benutzen!", 78));
+        return 1;
+    }
+
+    if ( PL->QueryProp(P_ATTACK_BUSY) )
+    {
+        write("Nicht so hektisch! So schnell bist Du nicht.\n");
+        return 1;
+    }
+
+    // P_ATTACK_BUSY natuerlich auch setzen...
+    PL->SetProp(P_ATTACK_BUSY,1);
+
+    // Info-Mapping erzeugen
+    shoot = ([ P_WEAPON       : ME,
+               P_WEAPON_TYPE  : QueryProp(P_AMMUNITION),
+               P_STRETCH_TIME : QueryProp(P_STRETCH_TIME),
+               P_WC           : QueryProp(P_SHOOTING_WC),
+               SI_SPELL       : 0
+            ]);
+
+    if ( !stringp(str) || str=="" )
+      str=shoot[P_WEAPON_TYPE];
+    else if (str[0..3]=="mit ")
+      str=str[4..];
+    else if (str[0..3]=="auf ")
+      str=shoot[P_WEAPON_TYPE]+" "+str;
+
+    if ( !(str=FindRangedTarget(str,shoot)) )
+      return 1;
+
+    if ( shoot[SI_ENEMY]->QueryProp(P_GHOST) )
+    {
+        write(break_string("Aber "+(shoot[SI_ENEMY]->name(WER, 1))+
+                           " ist doch ein Geist!", 78));
+        return 1;
+    }
+    else if ( no_at=(shoot[SI_ENEMY]->QueryProp(P_NO_ATTACK)) )
+    {
+        if ( stringp(no_at) )
+          write(no_at);
+        else
+          write(break_string("Du kannst "+(shoot[SI_ENEMY]->name(WEN, 1))+
+                             " nicht angreifen.", 78));
+        return 1;
+    }
+    else if ( shoot[SI_ENEMY]==PL )
+    {
+        write("Du kannst doch nicht auf Dich selbst schiessen!\n");
+        return 1;
+    }
+
+    if ( !(shoot[P_AMMUNITION]=present(str, PL))
+        && ( !objectp(quiver=PL->QueryArmourByType(AT_QUIVER))
+            || !(shoot[P_AMMUNITION]=present(str, quiver)) ) )
+    {
+        if ( str==shoot[P_WEAPON_TYPE] )
+            write(break_string("Du hast keine passende Munition bei Dir, um "+
+                               "mit "+name(WEM, 1)+" schiessen zu koennen.",
+                               78));
+        else
+          write(break_string("Du hast kein '"+str+"' bei Dir.", 78));
+        return 1;
+    }
+
+    if ( !shoot[P_AMMUNITION]->id(shoot[P_WEAPON_TYPE]) )
+    {
+        write(break_string((shoot[P_AMMUNITION]->Name(WER))+" kannst Du mit "+
+                           name(WEM, 1)+" nicht verschiessen.", 78));
+        return 1;
+    }
+
+    shoot[P_AMMUNITION]->SetProp(U_REQ, 1);
+
+    shoot[SI_SKILLDAMAGE_MSG]  = shoot[SI_SKILLDAMAGE_MSG2]
+                               = shoot[P_AMMUNITION]->name(WEN);
+    shoot[SI_SKILLDAMAGE_TYPE] = ( shoot[P_AMMUNITION]->QueryProp(P_DAM_TYPE)
+                                  || ({DT_PIERCE}) );
+
+    shoot[P_SHOOTING_WC]  = shoot[P_AMMUNITION]->QueryProp(P_SHOOTING_WC);
+
+    dam=shoot_dam(shoot);
+
+    // Textausgabe
+    if (shoot[SI_ENEMY])
+    {
+      tell_object(PL,break_string(
+          "Du schiesst "+shoot[SI_SKILLDAMAGE_MSG]+" auf "+
+          shoot[SI_ENEMY]->name(WEN, 1)+".", 78) );
+      tell_room(environment(PL), break_string(
+          PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+" auf "+
+          shoot[SI_ENEMY]->name(WEN, 1)+".", 78),
+          ({ shoot[SI_ENEMY], PL }) );
+      if ( environment(PL)!=environment(shoot[SI_ENEMY]) )
+        tell_room(environment(shoot[SI_ENEMY]),break_string(
+            PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+" auf "+
+            shoot[SI_ENEMY]->name(WEN, 1)+".", 78),
+            ({ shoot[SI_ENEMY] }) );
+      tell_object(shoot[SI_ENEMY], break_string(
+          PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+
+          " auf Dich ab.",78) );
+
+      shoot[SI_ENEMY]->Defend(dam,shoot[SI_SKILLDAMAGE_TYPE],shoot[SI_SPELL],PL);
+    }
+    // Munition verbrauchen
+    if ( shoot[P_AMMUNITION]->IsUnit() )
+      shoot[P_AMMUNITION]->AddAmount(-1);
+    else
+      shoot[P_AMMUNITION]->remove();
+
+    // es wird an dieser stelle absolute_hb_count anstelle von time() benutzt
+    // um dem Spieler wirklich die vollen 2 sek Zeit zu geben um zu reagieren
+    // ohne das er dadurch einen Schaden hat (wie er es z.b. bei time() haette)
+    last_shoot=absolute_hb_count();
+
+    return 1;
+}
+
diff --git a/std/restriction_checker.c b/std/restriction_checker.c
new file mode 100644
index 0000000..f2d4248
--- /dev/null
+++ b/std/restriction_checker.c
@@ -0,0 +1,323 @@
+// MorgenGrauen MUDlib
+//
+// restriction_checker.c -- Beschraenkungen in Gilden, Spells und Skills
+//
+// $Id: restriction_checker.c 8754 2014-04-26 13:12:58Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+//#pragma range_check
+#pragma no_shadow
+
+inherit "/std/util/executer";
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <properties.h>
+#include <new_skills.h>
+#include <wizlevels.h>
+#include <questmaster.h>
+
+#define DEBUG(x) if (find_player("zesstra")) tell_object(find_player("zesstra"),x)
+
+// erweitert um das <pl> argument und die extra-Argument, die bereits in <fun>
+// angegeben wurden. (*grummel*)
+protected mixed execute_anything(mixed fun, object pl, varargs mixed args)
+{
+  //DEBUG(sprintf("ex_any(): Fun: %O, Ob: %O, args: %O\n",fun,pl,args));
+  // wenn in fun schon extraargumente stehen, dann pl einfuegen und args
+  // dranhaengen.
+  if (pointerp(fun) && sizeof(fun) > 2)
+  {
+    // args kann ({}) sein, wenn keine Extraargumente uebergeben wurden, macht
+    // aber nix.
+    args = ({pl}) + fun[2..] + args;
+    fun = fun[0..1];
+    return ::execute_anything(fun, args...);
+  }
+  // s.o.
+  return ::execute_anything(fun, pl, args...);
+}
+
+mapping race_modifier(object pl, mapping map_ldfied) {
+  mapping rmap,help;
+
+  if (mappingp(map_ldfied) && member(map_ldfied,SM_RACE) && objectp(pl) &&
+      mappingp(rmap=map_ldfied[SM_RACE]))
+//    if (mappingp(help=rmap[pl->_query_real_race()]))
+    if (mappingp(help=rmap[pl->QueryProp(P_REAL_RACE)]))
+        return map_ldfied+help;
+    else if(member(rmap,"*") && mappingp(help=rmap["*"]))
+        return map_ldfied+help;
+  return map_ldfied;
+}
+
+string
+check_restrictions(object pl, mapping restr) {
+  /* Folgende Einschraenkungen koennen geprueft werden:
+   * - Mindestwerte bei allen Attributen und Properties Level, QP, XP
+   * - bestimmte Rassen koennen ausgeschlossen werden bzw. erlaubt werden
+   *   (SR_EXCLUDE_RACE ist ein Array von ausgeschlossenen Rassen,
+   *    SR_INCLUDE_RACE eines mit eingeschlossenen)
+   * - SR_RACE kann ein Mapping sein, dass Rassen ein eigenes
+   *   Restriction-mapping zuordnet. "*" steht fuer alle anderen Rassen.
+   */
+  closure cl,cl2;
+  string race, guild;
+
+  if (!mappingp(restr) || !sizeof(restr)) return 0; // Keine Einschraenkungen
+  if (!objectp(pl)) return "";
+  if (pl->QueryDisguise())
+    return "Zieh erst mal den Tarnhelm aus.\n";
+
+  restr=race_modifier(&pl,&restr);
+
+  cl=symbol_function("QueryProp",pl);
+  cl2=symbol_function("QueryAttribute",pl);
+
+  foreach(string to_check, mixed condition: restr)
+  {
+    switch(to_check)
+    {
+      case P_LEVEL:
+        if (funcall(cl,P_LEVEL) < condition)
+          return "Deine Stufe reicht dafuer nicht aus.\n";
+        break;
+      case P_GUILD_LEVEL:
+        if (funcall(cl,P_GUILD_LEVEL) < condition)
+          return "Deine Gildenstufe reicht dafuer nicht aus.\n";
+        break;
+      case SR_SEER: // das macht nun wahrlich nur bei interactives sinn!!!
+        if (condition && query_once_interactive(pl) && !IS_SEER(pl))
+          return "Du musst dazu erst Seher werden.\n";
+        break;
+      case P_XP:
+        if (funcall(cl,P_XP) < condition)
+          return "Du hast nicht genuegend Erfahrung dafuer.\n";
+        break;
+      case P_QP:
+        if (funcall(cl,P_QP) < condition)
+          return "Du hast nicht genuegend Aufgaben geloest.\n";
+        break;
+      case P_ALCOHOL:
+        if (funcall(cl,P_ALCOHOL) >= condition)
+          return "Du bist zu besoffen.\n";
+        break;
+      case P_DRINK:
+        if (funcall(cl,P_DRINK) >= condition)
+          return "Du hast zuviel getrunken.\n";
+        break;
+      case P_FOOD:
+        if (funcall(cl,P_FOOD) >= condition)
+          return "Du hast zuviel gegessen.\n";
+        break;
+      case P_DEAF:
+        if (funcall(cl,P_DEAF))
+          return "Du kannst nichts hoeren.\n";
+        break;
+      case P_BLIND:
+        if (funcall(cl,P_BLIND))
+          return "Du kannst nichts sehen.\n";
+        break;
+      case P_FROG:
+        if (funcall(cl,P_FROG))
+          return "Als Frosch kannst Du das leider nicht.\n";
+        break;
+      case A_INT:
+        if (funcall(cl2,A_INT) < condition)
+          return "Deine Intelligenz reicht dafuer nicht aus.\n";
+        break;
+      case A_DEX:
+        if (funcall(cl2,A_DEX) < condition)
+          return "Deine Geschicklichkeit reicht dafuer nicht aus.\n";
+        break;
+      case A_CON:
+        if (funcall(cl2,A_CON) < condition)
+          return "Deine Ausdauer reicht dafuer nicht aus.\n";
+        break;
+      case A_STR:
+        if (funcall(cl2,A_STR) < condition)
+          return "Deine Staerke reicht dafuer nicht aus.\n";
+        break;
+      case SR_BAD:
+        if (funcall(cl,P_ALIGN) < condition)
+          return "Du bist zu boese.\n";
+        break;
+      case SR_GOOD:
+        if (funcall(cl,P_ALIGN) > condition)
+          return "Du bist nicht boese genug.\n";
+        break;
+      case SR_MIN_SIZE:
+        if (funcall(cl,P_SIZE) < condition)
+          return "Du bist dafuer zu klein.\n";
+        break;
+      case SR_MAX_SIZE:
+        if (funcall(cl,P_SIZE) > condition)
+          return "Du bist dafuer zu gross.\n";
+        break;
+      case SR_FREE_HANDS:
+        if (condition > (funcall(cl,P_MAX_HANDS)-funcall(cl,P_USED_HANDS)))
+          return "Du hast nicht genug freie Haende dafuer.\n";
+        break;
+      case SR_EXCLUDE_RACE:
+        if (IS_LEARNER(pl))
+          race=funcall(cl,P_RACE);
+        else
+          race=funcall(cl,P_REAL_RACE); //race=pl->_query_real_race();
+        if ( pointerp(condition) && member(condition,race)>=0 )
+          return ("Als "+race+" kannst Du das nicht.\n");
+        break;
+     case SR_INCLUDE_RACE:
+        if (IS_LEARNER(pl))
+          race=funcall(cl,P_RACE);
+        else
+          race=funcall(cl,P_REAL_RACE); //race=pl->_query_real_race();
+        if (pointerp(condition) && member(condition,race)<0)
+          return ("Als "+race+" kannst Du das nicht.\n");
+        break;
+     case SR_EXCLUDE_GUILD:
+        guild=funcall(cl,P_GUILD);
+        if (pointerp(condition) && member(condition,guild)>=0)
+          return ("Mit Deiner Gildenzugehoerigkeit kannst Du das nicht.\n");
+        break;
+      case SR_INCLUDE_GUILD:
+        guild=funcall(cl,P_GUILD);
+        if (pointerp(condition) && member(condition,guild)<0)
+          return ("Mit Deiner Gildenzugehoerigkeit kannst Du das nicht.\n");
+        break;
+     case SR_FUN:
+        string res=execute_anything(condition, pl);
+        if (stringp(res))
+          return res;
+        break;
+     case SR_PROP:
+        foreach ( string propname, mixed value, string msg: condition ) {
+          if ( funcall(cl, propname) != value )
+            return ( stringp(msg) && sizeof(msg) ? msg :
+              "Koennte es sein, dass Du vergessen hast, etwas zu "
+              "erledigen?\n");
+        }
+        break;
+     case SR_QUEST:
+        if (pointerp(condition))
+        {
+          // geloeste Quests aus der Liste der Bedingungen entfernen
+          condition -= filter(condition, "QueryQuest", pl);
+          if ( sizeof(condition) )
+            return ("Du musst zuerst das Abenteuer '"+condition[0]+
+              "' loesen.\n");
+        }
+        break;
+     case SR_MINIQUEST:
+        if (pointerp(condition))
+        {
+          closure unsolved_MQs = function int (mixed mq_num)
+            { return !(QM)->HasMiniQuest(pl, mq_num); };
+          if ( sizeof(filter(condition, unsolved_MQs)) )
+            return "Du hast eine benoetigte Miniquest noch nicht geloest.\n";
+            // Der Name der MQ wird absichtlich nicht explizit ausgegeben.
+        }
+        break;
+    } // switch
+  } // foreach
+  return 0;
+}
+
+varargs mixed
+GetData(string dname, mapping map_ldfied, object pl) {
+  mixed dat,res;
+
+  if (!dname || !mappingp(map_ldfied)) return 0;
+  if (closurep(dat=map_ldfied[dname]) && (res=funcall(dat,pl,map_ldfied)))
+        return res;
+  return dat;
+
+}
+
+varargs int
+GetValue(string vname, mapping map_ldfied, object pl) {
+  mixed dat,res;
+
+  // printf("GetValue(%O): %O\n",vname,map_ldfied);
+  if (!vname || !map_ldfied) return 0;
+  if ((dat=map_ldfied[vname]) && (res=execute_anything(dat,pl,map_ldfied)))
+        return res;
+  // printf("Value: %O\n",dat);
+  return intp(dat) ? dat : 0;
+}
+
+varargs int
+GetFactor(string fname, mapping map_ldfied, object pl) {
+  mixed res;
+
+  // printf("GetFactor(%O):\n",fname);
+  if (!fname  || !(res=GetValue(FACTOR(fname),map_ldfied,pl)))
+        return 100;
+  if (res<10) res=10;
+  else if (res>1000) res=1000;
+  // printf("Factor: %O\n",res);
+  return res;
+}
+
+varargs int
+GetOffset(string oname, mapping map_ldfied, object pl) {
+  mixed res;
+
+  // printf("GetOffset(%O):\n",oname);
+  if (!oname  || !(res=GetValue(OFFSET(oname),map_ldfied,pl)))
+        return 0;
+  if (res<-10000) res=-10000;
+  else if (res>10000) res=10000;
+  // printf("Offset: %O\n",res);
+  return res;
+}
+
+varargs int
+GetFValue(string vname, mapping map_ldfied, object pl) {
+  return (GetValue(vname,map_ldfied,pl)*GetFactor(vname,map_ldfied,pl))/100;
+}
+
+varargs int
+GetValueO(string vname, mapping map_ldfied, object pl) {
+  return GetValue(vname,map_ldfied,pl)+GetOffset(vname,map_ldfied,pl);
+}
+
+varargs int
+GetFValueO(string vname, mapping map_ldfied, object pl) {
+  return ((GetValue(vname,map_ldfied,pl)*GetFactor(vname,map_ldfied,pl))/100
+                  + GetOffset(vname,map_ldfied,pl));
+}
+
+varargs int
+GetRandFValueO(string vname, mapping map_ldfied, object pl) {
+  return (random(GetValue(vname,map_ldfied,pl)*GetFactor(vname,map_ldfied,pl))/100
+          + GetOffset(vname,map_ldfied,pl));
+}
+
+mapping AddSkillMappings(mapping oldmap, mapping newmap) {
+  mapping res,t1,t2;
+  mixed *inx,ind;
+  int i;
+
+  if (!mappingp(oldmap)) return newmap;
+  if (!mappingp(newmap)) return oldmap;
+  inx=({SI_SKILLRESTR_LEARN,SI_SKILLRESTR_USE,SM_RACE});
+  res=oldmap+newmap;
+  for (i=sizeof(inx);i--;) {
+        ind=inx[i];
+        t1=oldmap[ind];
+        t2=newmap[ind];
+        if (t1) {
+          if (t2)
+                res[ind]=t1+t2;
+          else
+                res[ind]=t1;
+        } else {
+          if (t2)
+                res[ind]=t2;
+        }
+  }
+  return res;
+}
+
diff --git a/std/room.c b/std/room.c
new file mode 100644
index 0000000..29ab503
--- /dev/null
+++ b/std/room.c
@@ -0,0 +1,239 @@
+// MorgenGrauen MUDlib
+//
+// room.c -- room base object
+//
+// $Id: room.c 9475 2016-02-19 21:16:17Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/properties";
+inherit "/std/thing/language";
+inherit "/std/hook_provider";
+inherit "/std/room/light";
+inherit "/std/container/inventory";
+inherit "/std/room/moving";
+inherit "/std/room/restrictions";
+inherit "/std/room/description";
+inherit "/std/room/exits";
+inherit "/std/room/commands";
+inherit "/std/room/items";
+inherit "/std/room/doors";
+
+#include <thing/properties.h>
+#include <config.h>
+#include <properties.h>
+#include <rooms.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <defines.h>
+#include <doorroom.h>
+#include <functionlist.h>
+
+void reset()
+{
+  items::reset();
+  exits::reset();
+  doors::reset();
+}
+
+static int
+_query_noget()
+{
+  return 1;
+}
+
+//#define DB(x) if (find_player("jof")) tell_object(find_player("jof"),x); else write(x)
+//#undef DB
+//#define DB(x)
+void maybe_replace_program()
+{
+  string *list, first;
+  object first_ob;
+
+// Debugkram auskommentiert, Zesstra, 04.11.06
+//  DB("MAYBE_REPLACE_PROGRAM\n");
+//  DB(sprintf("FILENAME: %O\n",object_name(this_object())));
+//  DB(sprintf("FIRST_OB: %O\n",inherit_list(this_object())));
+//  first_ob=find_object(first=(list=inherit_list(this_object()))[1]);
+//  DB(sprintf("%O ?= %O\n",sizeof(list),1+sizeof(inherit_list(first_ob))));
+//  DB(sprintf("%O ?= sizeof(%O)\n",1,list=functionlist(this_object(),RETURN_FUNCTION_NAME|NAME_INHERITED)));
+  if (object_name(this_object())=="/std/room" ||
+      !(first_ob=find_object(first=(list=inherit_list(this_object()))[1])) ||
+      (sizeof(list)!=1+sizeof(inherit_list(first_ob))) ||
+      (1!=sizeof(list=functionlist(this_object(),
+           RETURN_FUNCTION_NAME|NAME_INHERITED))) ||
+      list[0]!="create")
+    return;
+//  DB("REPLACING\n");
+  replace_program(first);
+}
+
+protected void create()
+{
+  maybe_replace_program();
+  /* Set effective userid to userid */
+  /* so that we may clone other things */
+  seteuid(getuid(this_object()));
+  properties::create();
+  restrictions::create();
+  commands::create();
+  light::create();
+  description::create();
+  exits::create();
+  items::create();
+  doors::create();
+
+  SetProp(P_NAME,0);
+  SetProp(P_NAME_ADJ,({}));
+  Set(P_SHORT,0);
+  Set(P_LONG,0);
+  Set(P_TRANSPARENT,0);
+  Set(P_ADJECTIVES,({}));
+  Set(P_IDS,({}));
+  Set(P_WEIGHT,PROTECTED,F_MODE);
+  Set(P_TOTAL_WEIGHT,PROTECTED,F_MODE);
+  Set(" clean counter ",2);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+private int
+check_clean_count()
+{
+  int cc;
+
+  cc=Query(" clean counter ");
+  if (--cc<=0)
+    return 1;
+  Set(" clean counter ",cc);
+  return 0;
+}
+
+int
+clean_up(int arg)
+{
+  mixed itema;
+
+  if(arg>1) return 1; // better not ;)
+
+  if (Query(" never clean ")) return 0;
+
+  // if there are any item we have produced ourselfes check them
+  if(pointerp(itema = QueryProp(P_ITEMS)))
+  {
+    mixed names;
+    int i;
+    i = sizeof(names = all_inventory(this_object()));
+    while(i--)
+    {
+      if (query_once_interactive(names[i]))
+      {
+	Set(" clean counter ",2);
+	return 1;
+      }
+      if(objectp(names[i])) names[i] = explode(object_name(names[i]),"#")[0];
+    }
+
+    for(i = sizeof(itema)-1; i >= 0; i--)
+    {
+      // Semantik:
+      // 1. Wenn RITEM_OBJECT ein array ist, kann evtl ge'clean'ed werden.
+      // 2. Wenn es ein REFRESH_NONE Item ist und es entweder nicht mehr 
+      //    existiert oder nicht in diesem Raum ist, kein clean_up (es wuerde
+      //    beim neuladen des Raumes ja wieder erzeugt;
+      //    falls es aber hier ist, wird es mitvernichtet, dann ists ok)
+      // 3. Wenn es ein REFRESH_DESTRUCT ist und noch existiert, aber nicht
+      //    hier ist, KEIN clean_up.
+      if (!pointerp(itema[i][RITEM_OBJECT]) 
+	  && ((itema[i][RITEM_REFRESH] == REFRESH_NONE 
+	      && (!itema[i][RITEM_OBJECT] 
+		|| environment(itema[i][RITEM_OBJECT])!=this_object())) 
+	    || (itema[i][RITEM_REFRESH] == REFRESH_DESTRUCT 
+	      && itema[i][RITEM_OBJECT] 
+	      && environment(itema[i][RITEM_OBJECT]) != this_object())))
+	  return 1;
+      names -= (pointerp(itema[i][RITEM_FILE]) ?
+	  itema[i][RITEM_FILE] : ({ itema[i][RITEM_FILE] }));
+    }
+    // if there are objects left in the room do not clean up but try again later
+    if(sizeof(names) && !check_clean_count()) return 1;
+  }
+  else
+    // is there any object lying around?
+    if(first_inventory(this_object()) && !check_clean_count()) return 2;
+
+  // do clean_up
+  //log_file("clean_up_log",sprintf(
+  //	"%s:%s: %O\n",ctime(time())[11..18],__HOST_NAME__,this_object()));
+  
+  remove();
+  // wenn der Raum sich im remove() nicht zerstoert, hat er dafuer vermutlich
+  // nen Grund. Evtl. klappts ja naechstes Mal.
+
+  return(1);
+}
+
+/* Instead of printing the exits with the long description, we implement */
+/* the command "exits" to show them. */
+int
+show_exits() {
+  mixed ex;
+  if( this_player()->CannotSee() ) return 1;
+  if ((ex=QueryProp(P_HIDE_EXITS)) && intp(ex)) return 1;
+  if (ex = GetExits(this_player())) write(ex);
+  return 1;
+}
+
+int
+toggle_exits(string str)
+{
+  int ex;
+
+  /* Nur das aktuelle Environment des Spielers ist zustaendig fuer die
+     Auflistung der Ausgaenge. Anderenfalls wird das Kommando von Raeumen
+     im Raum (z.B. Transportern) abgefangen und es werden dessen Ausgaenge
+     aufgelistet.
+     Sprich: keine Auflistung von Aussen. */
+  if (environment(this_player()) != this_object()) return 0;
+  if (!str) return show_exits();
+  if (str!="auto") return 0;
+  ex = this_player()->QueryProp(P_SHOW_EXITS);
+  this_player()->SetProp(P_SHOW_EXITS, !ex);
+  if (ex) write("Ausgaenge werden nicht mehr automatisch angezeigt.\n");
+  else write("Ausgaenge werden automatisch angezeigt.\n");
+  return 1;
+}
+
+void
+init()
+{
+  Set(" clean counter ",2);
+
+  exits::init();
+  commands::init();
+  description::init();
+  doors::init();
+
+  add_action("toggle_exits", "exits");
+  add_action("toggle_exits", "ausgang");
+  add_action("toggle_exits", "ausgaenge");
+  add_action("toggle_exits", "aus");
+}
+
+int _query_para(){
+  int re;
+  if(re=Query(P_PARA))return re;
+  if(sizeof(regexp(({object_name(this_object())}),".*\\^0$")))
+      return -1;
+  return to_int(
+    regreplace(object_name(this_object()),".*\\^\([1-9][0-9]*\)$","\\1",1));
+}
+
+//dies ist ein Raum, war gewuenscht von mehreren Leuten.
+status IsRoom() {return(1);}
diff --git a/std/room/commands.c b/std/room/commands.c
new file mode 100644
index 0000000..5aa75c3
--- /dev/null
+++ b/std/room/commands.c
@@ -0,0 +1,37 @@
+// MorgenGrauen MUDlib
+//
+// room/commands.c -- room commands handling
+//
+// $Id: commands.c 8201 2012-11-07 17:55:12Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/thing/commands";
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+
+void init() 
+{
+  ::init();
+
+  add_action("imposs", "such");
+  add_action("imposs", "suche");
+}
+
+/* Fuer etwas bessere Fehlermeldungen als 'Wie bitte?' bei einigen */
+/* allgemeinen Kommandos.					   */
+int imposs()
+{
+  _notify_fail("Du suchst, findest aber nichts.\n");
+  return 0;
+}
diff --git a/std/room/description.c b/std/room/description.c
new file mode 100644
index 0000000..60d3b28
--- /dev/null
+++ b/std/room/description.c
@@ -0,0 +1,222 @@
+// MorgenGrauen MUDlib
+//
+// room/description.c -- room description handling
+//
+// $Id: description.c 9468 2016-02-19 21:07:04Z Gloinson $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/container/description";
+
+#define NEED_PROTOTYPES
+
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <doorroom.h>
+
+void create()
+{
+  ::create();
+  SetProp(P_NAME, "Raum");
+  SetProp(P_INT_SHORT,"<namenloser Raum>");
+  SetProp(P_INT_LONG,0);
+  SetProp(P_ROOM_MSG, ({}) );
+  SetProp(P_FUNC_MSG, 0);
+  SetProp(P_MSG_PROB, 30);
+  AddId(({"raum", "hier"}));
+}
+
+void init()
+{
+  // Wenn P_ROOM_MSG gesetzt oder P_FUNC_MSG und kein Callout laeuft,
+  // Callout starten.
+  mixed roommsg = QueryProp(P_ROOM_MSG);
+  if( ( (roommsg && sizeof(roommsg)) ||
+        QueryProp(P_FUNC_MSG) ) &&
+      (find_call_out("WriteRoomMessage")==-1))
+    call_out("WriteRoomMessage", random(QueryProp(P_MSG_PROB)));
+}
+
+varargs void AddRoomMessage(string *mesg, int prob, mixed func)
+{
+  if (mesg && !pointerp(mesg))
+    raise_error(sprintf(
+      "AddRoomMessage(): wrong argument type, expected Array or 0, "
+      "got %.20O",mesg));
+
+   SetProp(P_ROOM_MSG, mesg);
+
+  if (prob>0)
+    SetProp(P_MSG_PROB, prob);
+
+  if (func)
+    SetProp(P_FUNC_MSG, func);
+}
+
+static void WriteRoomMessage()
+{
+  int tim, msgid;
+  string *room_msg,func;
+  mixed *func_msg;
+
+  room_msg = (string *)QueryProp(P_ROOM_MSG);
+  func_msg = QueryProp(P_FUNC_MSG);
+  if ((!room_msg || !sizeof(room_msg)) && !func_msg)
+    return;
+
+  if (room_msg&&sizeof(room_msg))
+  {
+    msgid = random(sizeof(room_msg));
+    // Defaultwerte sind fuer Altcode schwierig
+    send_room(this_object(), room_msg[msgid],
+              MT_LOOK|MT_LISTEN|MT_FEEL|MT_SMELL|
+              MSG_DONT_STORE|MSG_DONT_BUFFER|MSG_DONT_WRAP);
+  }
+
+  if (func_msg)
+  {
+    if (stringp(func_msg))
+      func=(string)func_msg;
+    else
+      func=func_msg[random(sizeof(func_msg))];
+    if (func && function_exists(func))
+      call_other (this_object(), func, msgid);
+  }
+
+  while (remove_call_out("WriteRoomMessage")!=-1);
+  tim=QueryProp(P_MSG_PROB);
+  if(this_object() && sizeof(filter(
+       deep_inventory(this_object()), #'interactive))) //')))
+    call_out("WriteRoomMessage", (tim<15 ? 15 : tim));
+}
+
+varargs string int_long(mixed viewer,mixed viewpoint,int flags)
+{
+  string descr, inv_descr;
+
+  flags &= 3;
+  if( IS_LEARNER(viewer) && viewer->QueryProp( P_WANTS_TO_LEARN ) )
+    descr = "[" + object_name(ME) + "]\n";
+  else
+    descr = "";
+
+  descr += process_string(QueryProp(P_INT_LONG)||"");
+  
+  // ggf. Tueren hinzufuegen.
+  if (QueryProp(P_DOOR_INFOS)) {
+    string tmp=((string)call_other(DOOR_MASTER,"look_doors"));
+    if (stringp(tmp) && sizeof(tmp))
+        descr += tmp;
+  }
+  
+  // ggf. Ausgaenge hinzufuegen.
+  if ( viewer->QueryProp(P_SHOW_EXITS) && (!QueryProp(P_HIDE_EXITS) 
+	|| pointerp(QueryProp(P_HIDE_EXITS))) )
+    descr += GetExits(viewer) || "";
+
+  // Viewpoint (Objekt oder Objektarray) sind nicht sichtbar
+  inv_descr = (string) make_invlist(viewer, all_inventory(ME) 
+		  - (pointerp(viewpoint)?viewpoint:({viewpoint})) ,flags);
+
+  if ( inv_descr != "" )
+    descr += inv_descr;
+
+  if(environment() && (inv_descr=QueryProp(P_TRANSPARENT)))
+  {
+    if(stringp(inv_descr)) descr += inv_descr;
+    else                   descr += "Ausserhalb siehst Du:\n";
+            
+    descr += environment()->int_short(viewer,ME);
+  }
+                  
+  return descr;
+}
+
+string int_short(mixed viewer,mixed viewpoint)
+{
+  string descr, inv_descr;
+
+  descr = process_string( QueryProp(P_INT_SHORT)||"");
+  if( IS_LEARNER(viewer) && viewer->QueryProp( P_WANTS_TO_LEARN ) )
+    descr += " [" + object_name(ME) + "].\n";
+  else
+    descr += ".\n";
+
+  if ( ( viewer->QueryProp(P_SHOW_EXITS)
+         || ( environment(viewer) == ME && !viewer->QueryProp(P_BRIEF) ) )
+       && (!QueryProp(P_HIDE_EXITS) || pointerp(QueryProp(P_HIDE_EXITS))) )
+    descr += GetExits(viewer) || "";
+  
+  // Viewpoint (Objekt oder Objektarray) sind nicht sichtbar
+  inv_descr = (string) make_invlist( viewer, all_inventory(ME) 
+		  - (pointerp(viewpoint)?viewpoint:({viewpoint})) );
+
+  if ( inv_descr != "" )
+    descr += inv_descr;
+
+  return descr;
+}
+
+/** Roommessages abschalten, wenn keine Interactives mehr da sind.
+  */
+// TODO: Irgendwann das varargs loswerden, wenn in der restlichen Mudlib
+// TODO::exit() 'richtig' ueberschrieben wird.
+varargs void exit(object liv, object dest) {
+  // fall erbende Objekte das liv nicht uebergeben. Pruefung nur auf
+  // previous_object(). Wenn Magier da noch irgendwelche Spielchen mit
+  // call_other() & Co treiben, haben wir Pech gehabt, macht aber nicht viel,
+  // weil die Raummeldungen dann im naechsten callout abgeschaltet werden.
+  if (!living(liv=previous_object())) return;
+
+  object *interactives = filter(all_inventory(), #'interactive);
+  // liv wurde noch nicht bewegt, ggf. beruecksichtigen.
+  if ( !sizeof(interactives) ||
+      (interactive(liv) && sizeof(interactives) < 2) )
+    while (remove_call_out("WriteRoomMessage")!=-1);
+}
+
+static string _query_int_long() {return Query(P_INT_LONG, F_VALUE);}
+
+
+// Querymethode fuer P_DOMAIN - gibt die Region an, in der Raum liegt, sofern
+// er unter /d/ liegt...
+static string _query_lib_p_domain()
+{
+  string fn = object_name();
+  if (strstr(fn, "/d/") == 0)
+  {
+    return capitalize(explode(fn, "/")[2]);
+  }
+
+  return "unbekannt";
+}
+
+<string|string*>* _set_harbour_name( <string|string*>* desc) 
+{
+  if ( sizeof(desc)!=2 )
+  {
+    raise_error(sprintf("Unacceptable data in P_HARBOUR, sizeof() was %d, "
+      "expected 2.", sizeof(desc)));
+  }
+  else if ( !stringp(desc[0]) )
+  {
+    raise_error("Wrong data type in P_HARBOUR[0]: expected 'string'.");
+  }
+  else if ( pointerp(desc[1]) && sizeof(desc[1])<1 )
+  {
+    raise_error("Insufficient data in P_HARBOUR[1]: expected 'string*', "
+      "got '({})'.");
+  }
+  else if ( stringp(desc[1]) )
+  {
+    desc[1] = ({desc[1]});
+  }
+  return Set(P_HARBOUR, desc, F_VALUE);
+}
+
diff --git a/std/room/doors.c b/std/room/doors.c
new file mode 100644
index 0000000..e05f009
--- /dev/null
+++ b/std/room/doors.c
@@ -0,0 +1,162 @@
+// MorgenGrauen MUDlib
+//
+// room/doors.c -- new doors, managed by doormaster
+//
+// $Id: doors.c 9134 2015-02-02 19:26:03Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+#include <config.h>
+#include <properties.h>
+#include <defines.h>
+#include <language.h>
+#include <doorroom.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <room/exits.h>
+
+protected void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+  SetProp(P_DOOR_INFOS,0);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+varargs int NewDoor(string|string* cmds, string dest, string|string* ids,
+                    mapping|<int|string|string*>* props)
+{
+/*
+  cmds: Befehl(e), um durch die Tuer zu gehen (String oder Array von Strings)
+  dest: Zielraum
+  ids:  Id(s) der Tuer, default "tuer" (String, Array von Strings oder 0)
+  props: besondere Eigenschaften der Tuer (optional)
+  Array mit Paaren Nummer der Eigenschaft, Inhalt
+  definierte Properties sind:
+  D_FLAGS:  Flags wie bei Sir's Tueren
+  default: DOOR_CLOSED | DOOR_RESET_CL
+  Bei Schluesseln wird getestet, ob der String, den
+  QueryDoorKey zurueckliefert, gleich
+  "raumname1:raumname2" ist, wobei raumname1,2 die
+  kompletten Filenamen der beiden Raeume in sortierter
+  Reihenfolge sind.
+  D_LONG:   lange Beschreibung der Tuer
+  default: "Eine Tuer.\n"
+  D_SHORT:  kurze Beschreibung der Tuer, wird an die Raumbeschreibung
+  angefuegt, wobei %s durch geoeffnet bzw. geschlossen
+  ersetzt wird.
+  default: "Eine %se Tuer. "
+  D_NAME:   Name, der beim Oeffnen/Schliessen und bei Fehlermeldungen
+  angezeigt wird.
+  default: "Tuer"
+  D_GENDER: default: FEMALE
+  D_FUNC:   Funktion, die im Raum aufgerufen werden soll, wenn die
+  Tuer erfolgreich durchschritten wird.
+  default: 0
+  D_MSGS:   Falls String: ausgegebene Richtung fuer move
+  Falls Array: ({direction, textout, textin}) fuer move
+  default: 0
+
+  Beispiel:
+  NewDoor("norden","/players/rochus/room/test2","portal",
+  ({D_NAME,"Portal",
+  D_GENDER,NEUTER,
+  D_SHORT,"Im Norden siehst Du ein %ses Portal. ",
+  D_LONG,"Das Portal ist einfach nur gigantisch.\n"
+  }));
+
+*/
+
+  if (!cmds || !dest) return 0;
+  return call_other(DOOR_MASTER,"NewDoor",cmds,dest,ids,props);
+}
+
+void init()
+{
+  mixed *info;
+  string *cmds;
+  int i,j;
+
+  if (!pointerp(info=(mixed *)QueryProp(P_DOOR_INFOS))) return;
+  add_action("oeffnen","oeffne");
+  add_action("schliessen","schliesse");
+  add_action("schliessen","schliess");
+  for (i=sizeof(info)-1;i>=0;i--) {
+    cmds=(string *)(info[i][D_CMDS]);
+    for (j=sizeof(cmds)-1;j>=0;j--)
+      add_action("go_door",cmds[j]);
+    // Befehle IMMER anfuegen, gechecked wird sowieso erst beim Durchgehen.
+  }
+}
+
+void reset()
+{
+  if (QueryProp(P_DOOR_INFOS))
+    call_other(DOOR_MASTER,"reset_doors");
+}
+
+int oeffnen (string str)
+{
+  if (!str || !QueryProp(P_DOOR_INFOS))
+    return 0;
+  return (int) call_other(DOOR_MASTER,"oeffnen",str);
+}
+
+int schliessen (string str)
+{
+  if (!str || !QueryProp(P_DOOR_INFOS))
+    return 0;
+  return (int) call_other(DOOR_MASTER,"schliessen",str);
+}
+
+varargs int
+go_door (string str)
+{
+  if (!QueryProp(P_DOOR_INFOS))
+    return 0;
+  if (call_other(DOOR_MASTER,"go_door",query_verb()))
+    return 1;
+  return 0;
+}
+
+int set_doors(string *cmds,int open)
+{
+  int j;
+  
+  if (!previous_object())
+    return 0;
+  if (object_name(previous_object())!=DOOR_MASTER)
+    return 0;
+  // Andere sollen nicht rumpfuschen.
+  if (!this_player()) return 0;
+  if (environment(this_player())!=this_object())
+    return 0;
+  // Ist sowieso keiner da...
+  if (!pointerp(cmds))
+    return 0;
+  if (open)
+    AddSpecialExit(cmds,"go_door");
+  else
+    RemoveSpecialExit(cmds);
+  for (j=sizeof(cmds)-1;j>=0;j--)
+    add_action("go_door",cmds[j]);
+  return 1;
+}
+
+/* Fuer Tueren, die flexible Langbeschreibungen haben, wird ein 
+ * SpecialDetail eingefuehrt.
+ */
+
+string special_detail_doors(string key){
+  return DOOR_MASTER->special_detail_doors(key);
+}
diff --git a/std/room/exits.c b/std/room/exits.c
new file mode 100644
index 0000000..dc724b8
--- /dev/null
+++ b/std/room/exits.c
@@ -0,0 +1,334 @@
+// MorgenGrauen MUDlib
+//
+// room/exits.c -- room exits handling 
+//
+// $Id: exits.c 9497 2016-02-21 14:20:03Z Zesstra $
+
+/*
+ * Exits of the room (obvious ones, doors, and special ones)
+ * we define the following function for easy reference:
+ * GetExits() - return a string containing an "Obvious Exits" Statement
+ *
+ * The exits are implemented as properties P_EXITS
+ * They are stored locally (_set_xx, _query_xx)
+ * as mapping to speed up the routines in this module.
+ *
+ */
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <moving.h>
+#include <room/exits.h>
+#include <hook.h>
+#include <exploration.h>
+#undef NEED_PROTOTYPES
+
+#include <sys_debug.h>
+#include <config.h>
+#include <properties.h>
+#include <defines.h>
+#include <daemon.h>
+#include <doorroom.h>
+#include <routingd.h>
+
+#define NUMBERS ({ "zwei", "drei", "vier", "fuenf", "sechs", "sieben", "acht" })
+
+
+// Hilfsfunktion, die bei kaputten Exits eine Notrettung betreibt, aber
+// trotzdem auf Debug eine Meldung macht.
+static mapping rescueExit()
+{
+  catch(raise_error(sprintf(
+	  "room/exits.c: Forgotten ::create()? "
+	  "P_EXITS in %O is 0!\n", this_object()));publish); 
+
+  return ([:2]);
+}
+
+
+static mapping _set_exits( mapping map_ldfied ) 
+{
+    if( mappingp(map_ldfied) )
+        return Set( P_EXITS, map_ldfied );
+    return 0;
+}
+
+
+static mapping _query_exits() 
+{
+    if( (!previous_object() || object_name(previous_object()) != DOOR_MASTER)
+        && QueryProp(P_DOOR_INFOS) )
+        call_other( DOOR_MASTER, "init_doors" );
+
+    mapping exits = Query(P_EXITS) || rescueExit();
+
+    return filter(exits, function int (string key, mixed val)
+        {return stringp(val[0]);} );
+}
+
+
+static int _set_special_exits( mapping map_ldfied )
+{
+    return -1;
+}
+
+
+static mapping _query_special_exits() 
+{
+    mapping exits = Query(P_EXITS) || rescueExit();
+
+    return filter(exits, function int (string key, mixed val)
+        {return closurep(val[0]);} );
+}
+
+
+void reset()
+{}
+
+
+protected void create()
+{
+  offerHook(H_HOOK_EXIT_USE, 1);
+  SetProp( P_EXITS, ([:2]) );
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+protected void _AddExit(string|string* cmd, string|closure room,
+                        string message)
+{
+  mapping exita;
+
+  exita = Query(P_EXITS) || rescueExit();
+
+  if ( !closurep(room) )
+  {
+    object router;
+
+    room = _MakePath(room);
+
+    if ( !clonep(this_object()) && objectp(router = find_object(ROUTER)) )
+    {
+      router->RegisterExit( object_name(this_object()), cmd, room );
+    }
+  }
+
+  if( stringp(cmd) )
+  {
+    exita += ([ cmd : room; message ]);
+  }
+  else
+  {
+    foreach(string c : cmd)
+    {
+      if (stringp(c))
+        exita += ([ c : room; message ]);
+    }
+  }
+
+  Set( P_EXITS, exita );
+}
+
+void AddExit(string|string* cmd, closure|string dest)
+{
+  string msg;
+  if ( stringp(dest) )
+  {
+    int s;
+    if( (s = member(dest, '#')) != -1 )
+    {
+      msg  = dest[0..s-1];
+      dest = dest[s+1..];
+    }
+  }
+  _AddExit(cmd, dest, msg);
+}
+
+void RemoveExit(string|string* cmd )
+{
+    mapping exita;
+
+    if ( !cmd ) {
+        SetProp(P_EXITS, ([:2]) );
+        return;
+    }
+
+    if ( stringp(cmd) )
+        cmd = ({ cmd });
+
+    exita = Query(P_EXITS, F_VALUE) || rescueExit();
+    foreach(string c : cmd)
+      m_delete( exita, c );
+
+    Set( P_EXITS, exita, F_VALUE );
+}
+
+
+void AddSpecialExit(string|string* cmd, string|closure functionname )
+{
+
+    if ( stringp(functionname) )
+        functionname = symbol_function( functionname, this_object() );
+
+    if ( !closurep(functionname) )
+    {
+        catch(raise_error(sprintf( "method %O doesn't exist\n",
+                          functionname)); publish);
+        return;
+    }
+
+    AddExit( cmd, functionname );
+}
+
+
+void RemoveSpecialExit(string|string* cmd)
+{
+    RemoveExit( cmd );
+}
+
+
+varargs string GetExits( object viewer ) 
+{
+    string *indices, *hidden;
+    string exits;
+
+    if ( QueryProp(P_DOOR_INFOS) )
+        call_other( DOOR_MASTER, "init_doors" );
+
+    indices = m_indices( Query(P_EXITS) || rescueExit() );
+    
+    if ( pointerp(hidden = QueryProp(P_HIDE_EXITS)) )
+        indices -= hidden;
+
+    int n=sizeof(indices);
+    switch (n) {
+    case 0:
+        return "Es gibt keine sichtbaren Ausgaenge.\n";
+        
+    case 1:
+        return "Es gibt einen sichtbaren Ausgang: " + indices[0] + ".\n";
+        
+    case 2: case 3: case 4: case 5: case 6: case 7: case 8:
+        exits = "Es gibt " + NUMBERS[n-2] + " sichtbare Ausgaenge: ";
+        break;
+        
+    default:
+        exits = "Es gibt viele sichtbare Ausgaenge: ";
+    }
+    exits += CountUp(indices);
+    return break_string( exits+".", 78 );
+}
+
+
+// Richtungsbefehle nur interpretieren, wenn der Spieler *im* Raum steht und
+// nicht davor (Transporter etc.)/o
+void init()
+{
+    if ( environment(this_player()) == this_object() )
+        add_action( "_normalfunction", "", 1 );
+}
+
+
+/* not only normal exits are handled here */
+
+int _normalfunction()
+{
+  int ret;
+  mapping exits = Query(P_EXITS, F_VALUE) || ([:3]);
+  if (!member(exits,query_verb()))
+    return 0;
+
+  string verb = query_verb();
+  string destroom = exits[query_verb(),0];
+  string message = exits[query_verb(),1];
+
+  mixed hres = HookFlow(H_HOOK_EXIT_USE, ({verb, destroom, message}));
+  if(hres && pointerp(hres) && sizeof(hres)>H_RETDATA)
+  {
+    if(hres[H_RETCODE]==H_CANCELLED)
+    {
+      return 1;
+    }
+    else if(hres[H_RETCODE]==H_ALTERED
+            && pointerp(hres[H_RETDATA])
+            && sizeof(hres[H_RETDATA]) >= 3)
+    {
+      <string|closure>* hdata = hres[H_RETDATA];
+      if (!stringp(hdata[0])
+          || (!stringp(hdata[1]) && !closurep(hdata[1]))
+          || (hdata[2] && !stringp(hdata[2])) )
+        raise_error(sprintf("Invalide Daten aus H_HOOK_EXIT_USE: %.150O\n",
+                            hdata));
+      verb = hdata[0];
+      destroom = hdata[1];
+      message = hdata[2];
+    }
+  }
+
+  if( closurep(destroom) )
+  {
+    ret = funcall( destroom, verb );
+
+    if(ret==MOVE_OK)
+    {
+      GiveEP( EP_EXIT, verb );
+    }
+
+    return ret;
+  }
+
+  if (!stringp(message))
+  {
+    if( member( ({ "sueden", "suedwesten", "westen","nordwesten", "norden",
+        "nordosten", "osten","suedosten" }), verb ) != -1 )
+    {
+      message = "nach " + capitalize(verb);
+    }
+    else if ( member( ({ "oben", "unten" }), verb ) != -1 )
+    {
+      message = "nach " + verb;
+    }
+    else
+    {
+      message = verb;
+    }
+  }
+
+  ret = this_player()->move( destroom, M_GO, message );
+
+  if (ret==MOVE_OK)
+  {
+    GiveEP( EP_EXIT, verb );
+  }
+
+  return ret;
+}
+
+static string _MakePath( string str )
+{
+  string *comp;
+
+  comp = explode( object_name(this_object()), "/" ) - ({""});
+  
+   switch( str[0] ){
+   case '.':
+       str = "/" + implode( comp[0..<2], "/" ) + "/" + str;
+       break;
+       
+   case '~':
+       str = "/" + comp[0] + "/" + (comp[0] == "d" ? comp[1] + "/" : "")
+           +REAL_UID(this_object()) + str[1..];
+       break;
+   }
+   
+   return MASTER->_get_path( str, getuid(this_object()) );
+}
+
diff --git a/std/room/items.c b/std/room/items.c
new file mode 100644
index 0000000..1d4005a
--- /dev/null
+++ b/std/room/items.c
@@ -0,0 +1,42 @@
+// MorgenGrauen MUDlib
+//
+// room/items.c -- creating extra items in room
+//
+// $Id: items.c 9538 2016-03-20 23:46:41Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "std/container/items";
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset() 
+{
+  ::reset();
+
+  object *inh = all_inventory(this_object());
+  if ( !pointerp(inh) || sizeof(inh) < 10 )
+    return;
+  // nur wenn keine Spieler anwesend sind.
+  if ( !sizeof(inh & users()) )
+    remove_multiple(3);
+}
+
+// Per Default nur an alle Items im Inventar weiterleiten.
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+                              string msg_prefix, object origin)
+{
+  int *res = all_inventory()->ReceiveMsg(msg, msg_type, msg_action,
+                                         msg_prefix,
+                                         origin || previous_object());
+  if (sizeof(res))
+    return min(res);
+  return 0;
+}
+
diff --git a/std/room/kraeuter.c b/std/room/kraeuter.c
new file mode 100644
index 0000000..24f5193
--- /dev/null
+++ b/std/room/kraeuter.c
@@ -0,0 +1,240 @@
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/language.h>
+#include <thing/description.h>
+#include <thing/commands.h>
+#undef NEED_PROTOTYPES
+#include <moving.h>
+#include <items/kraeuter/kraeuter.h>
+#include <defines.h>
+#include <living/combat.h> // Fuer P_FREE_HANDS
+
+// Standardwert von 2 h fuer vollstaendige Regeneration des Krautes. 
+// Einfaches Nachwachsen ist typischerweise die Haelfte davon.
+#define STD_WACHSTUM  7200
+#define BS(x)         break_string(x, 78)
+
+// Struktur (6 Eintraege pro Kraut):
+// ([ filename : ({ Zeit_bis_nachgewachsen, Zeit_bis_komplett_regeneriert,
+//                  Kraut-IDs, Kraut-Adjektive, Kraut->Name(WER,1),
+//                  ID_des_Bewacher-NPCs }) ])
+mapping plantMap = ([]);
+
+// kann benutzt werden um schnell und einfach eine Pflanze in einem Raum
+// hinzuzufuegen. Beispiel: AddPlant(BAERENKLAU);
+// Diese Funktion erzeugt automatisch ein AddCmd() fuer das Pfluecken und
+// (falls noch nicht vorhanden) Details fuer die Pflanze.
+// Rueckgabewerte:
+// 1 -> Erfolg; -1 -> filename ungueltig
+varargs int AddPlant(string filename, string|string* npcId)
+{
+  mixed arr;
+
+  // Dateiname nicht uebergeben? Dann tun wir erstmal gar nix.
+  if (!stringp(filename)) 
+    return -1;
+  object ob=load_object(filename);
+   
+  // Wenn wir zu dem Kraut schon Daten haben (erkennbar an >2 Eintraegen),
+  // werfen wir einen Fehler, damit das beim Laden des Raumes schon
+  // erkannt wird.
+  if (pointerp(arr=plantMap[filename]) && sizeof(arr)>2)
+    raise_error("AddPlant(): "+filename+" already exists.\n");
+
+  // IDs und Adjektive parsen und den Datensatz zusammenstellen
+  string *ids = ob->Query(P_IDS, F_VALUE)-({ "Ding" });
+  string *adj = ob->Query(P_ADJECTIVES, F_VALUE);
+  
+  if (!pointerp(arr) || sizeof(arr)<2) 
+    arr = ({0,0});
+  if ( !npcId )
+    npcId = ({});
+  else if (stringp(npcId))
+    npcId = ({npcId});
+  plantMap[filename]=({arr[0], arr[1], ids, adj, ob->Name(WER, 1), npcId });
+   
+  // Details erzeugen aus Adjektiven und IDs
+  adj = ob->QueryProp(P_NAME_ADJ);
+  
+  // aktuelles Geschlecht zwischenspeichern, wird spaeter wiederhergestellt
+  int gender = Query(P_GENDER, F_VALUE);
+  Set(P_GENDER, ob->Query(P_GENDER, F_VALUE), F_VALUE);
+  
+  // erzeugt fuer jede moegliche Kombination aus Adjektiv im Akkusativ
+  // und ID des Krautes ein Detail.
+  adj = map(adj, #'DeclAdj, WEN, 0);
+
+  string *det=({});
+  foreach(string _id : ids) {
+    foreach(string _adj : adj) {
+      det += ({ _adj + _id });
+    }
+  }
+
+  det += ids;
+  // keine existierenden Details ueberschreiben
+  det -= m_indices(Query(P_DETAILS, F_VALUE) || ([]));
+  if (sizeof(det)) 
+    AddDetail(det, ob->Query(PLANT_ROOMDETAIL, F_VALUE));
+  
+  // Eine Befehlsfunktion brauchen wir natuerlich auch.
+  AddCmd(({"pflueck", "pfluecke", "ernte"}), "_pfluecken");
+  
+  return 1;
+}
+
+// Wenn jemand per Hand das Plantdetail hinzufuegen moechte...
+// z.B. bei Verwendung von GetPlant() anstelle von AddPlant()
+void AddPlantDetail(string filename)
+{
+  // Pfad in Objektpointer wandeln
+  object ob=load_object(filename);
+   
+  // Details erzeugen
+  string *det = ({});
+  string *ids = ob->Query(P_IDS, F_VALUE)-({ "Ding" });
+  string *adj = ob->QueryProp(P_NAME_ADJ);
+  // aktuelles Geschlecht zwischenspeichern, wird spaeter wiederhergestellt
+  int gender=Query(P_GENDER, F_VALUE);
+  Set(P_GENDER, ob->Query(P_GENDER, F_VALUE));
+  // erzeugt fuer jede moegliche Kombination aus Adjektiv im Akkusativ
+  // und ID des Krautes ein Detail.
+  adj = map(adj, #'DeclAdj, WEN, 0);
+  foreach(string _id : ids) {
+    foreach(string _adj : adj) {
+      det += ({ _adj + _id });
+    }
+  }
+  AddDetail(det+ids, ob->Query(PLANT_ROOMDETAIL, F_VALUE));
+  // Geschlecht zuruecksetzen
+  Set(P_GENDER, gender, F_VALUE);
+}
+
+// Prueft, ob die Pflanze zu "filename" in diesem Raum schon nachgewachsen
+// ist.
+protected int CheckPlant(string filename)
+{
+  mixed arr=plantMap[filename];
+  if (!pointerp(arr) || sizeof(arr)<2) {
+    arr=plantMap[filename]=({ 0, 0 });
+  }
+  // Solange die Zeit arr[0] noch nicht erreicht ist, ist das Kraut nicht 
+  // nachgewachsen, dann gibt es gar nix.
+  return (time()>arr[0]);
+}
+
+// Moechte man AddPlant() nicht benutzen, weil man die Pflanze nicht einfach
+// pfluecken, sondern vielleicht abschneiden, oder ausgraben soll, so kann
+// man sich mittels GetPlant(filename) das Objekt erzeugen lassen. Gibt
+// GetPlant() 0 zurueck, ist die Pflanze noch nicht wieder weit genug
+// nachgewachsen.
+object GetPlant(string filename)
+{
+  int *arr=plantMap[filename];
+  if (!pointerp(arr) || sizeof(arr)<2)
+  {
+     arr=plantMap[filename]=({ 0, 0 });
+  }
+  // arr[0] enthaelt den Zeitpunkt, wann das Kraut nachgewachsen ist,
+  int nachgewachsen = arr[0];
+  // arr[1] denjenigen, wann es vollstaendig regeneriert ist.
+  int regeneriert = arr[1];
+
+  // Vor dem Nachgewachsen-Zeitpunkt kann man gar nix ernten.
+  if (time()<nachgewachsen) return 0; // noch nicht nachgewachsen
+
+  // Restzeit bis zur vollstaendigen Regeneration.
+  regeneriert-=time();
+  
+  // Wenn vollstaendig regeneriert, ist STD_WACHSTUM die neue Zeit bis zur
+  // Regeneration. Wenn noch nicht vollstaendig regenriert, Restzeit
+  // verdoppeln und STD_WACHSTUM nochmal drauf addieren.
+  regeneriert = (regeneriert<=0 ? STD_WACHSTUM 
+                                : (regeneriert*2)+STD_WACHSTUM);
+  // nachgewachsen ist die halbe Regenerationszeit
+  arr[0]=nachgewachsen=time()+(regeneriert/2);
+  // Zeit voelliger Regeneration
+  arr[1]=regeneriert+=time();
+  
+  return clone_object(filename);
+}
+
+static int _pfluecken(string str)
+{
+  int res;
+
+  if (!mappingp(plantMap)) return 0;
+  _notify_fail("WAS moechtest Du pfluecken?\n");
+
+  // IDs und Adjektive zwischenspeichern
+  mixed ids = Query(P_IDS, F_VALUE);
+  mixed adj = Query(P_ADJECTIVES, F_VALUE);
+
+  foreach(string key, mixed krautinfo : plantMap) 
+  {
+    if ( sizeof(krautinfo) != 6 )
+      continue;
+ 
+    // IDs und Adjektive des Krautes kopieren 
+    Set(P_IDS, krautinfo[2], F_VALUE);
+    Set(P_ADJECTIVES, krautinfo[3], F_VALUE);
+
+    // Syntaxpruefung wird dann mit id() gemacht.
+    if (id(str)) 
+    {
+      object ob;
+      object bewacher;
+      res=1;
+
+      // Liste der eingetragenen Bewacher-IDs durchlaufen und pruefen, ob
+      // mindestens einer davon anwesend ist.
+      foreach( string npcId : krautinfo[5] )
+      {
+        bewacher = present(npcId, ME);
+        if (objectp(bewacher))
+          break;
+      }
+      
+      if ( !PL->QueryProp(P_FREE_HANDS) ) 
+      {
+        tell_object(PL, BS("Du hast keine Hand frei, um etwas pfluecken "
+          "zu koennen."));
+      }
+      // Ist der Bewacher anwesend? Dann kann man das Kraut nicht pfluecken.
+      else if ( objectp(bewacher) )
+      {
+        tell_object(PL, BS(bewacher->Name(WER, 2)+" laesst Dich "
+          "leider nicht nah genug heran. Irgendwie musst Du Dich wohl "
+          "zunaechst um "+bewacher->QueryPronoun(WEN)+" kuemmern."));
+      }
+      // Wenn GetPlant() ein Objekt liefert, kann was gepflueckt werden.
+      else if ( objectp(ob=GetPlant(key)) ) 
+      {
+        if ( ob->move(PL, M_GET) == MOVE_OK )
+        {
+          write(BS("Vorsichtig pflueckst Du "+ob->name(WEN, 1)+
+            " und nimmst "+ob->QueryPronoun(WEN)+" an Dich."));
+        }
+        else 
+        {
+          write(BS("Vorsichtig pflueckst Du "+ob->name(WEN, 1)+", kannst "+
+            ob->QueryPronoun(WEN)+" aber nicht nehmen."));
+          ob->move(environment(PL), M_GET);
+        }
+      }
+      // Wenn alles nicht, dann ist das Kraut noch nicht wieder da.
+      else 
+      {
+        write(BS(krautinfo[4]+" ist noch nicht reif genug "
+          +"und muss erst noch etwas wachsen."));
+        break;
+      }
+    }
+  }
+   // IDs und Adjektive zuruecksetzen.
+  Set(P_IDS, ids, F_VALUE);
+  Set(P_ADJECTIVES, adj, F_VALUE);
+
+  return res;
+}
+
diff --git a/std/room/kraeuterladen.c b/std/room/kraeuterladen.c
new file mode 100644
index 0000000..cf99793
--- /dev/null
+++ b/std/room/kraeuterladen.c
@@ -0,0 +1,167 @@
+// (c) 2003 by Padreic (padreic@mg.mud.de)
+// 
+// Es kann bestimmte Laeden zum Handeln von Kraeutern geben.
+// Zunaechst einmal gibt es einen in der Dunkelelfengilde.
+// Hier koennen Kraeuter ge- und verkauft werden.
+// Grundsaetzlich kann es beliebig viele kraeuterkundige
+// Haendler geben, eine kurze Absprache waere jedoch von Vorteil.
+
+// Der Laden erweitert die room/shop Funktionen.
+// Zur Verwendung muss dieser noch mit einem std/room kombiniert
+// werden. Dies erlaubt die Verwendung von eigenen Standardraeumen oder
+// Kombination mit Kneipen.
+
+inherit "/std/room/shop";
+
+#define NEED_PROTOTYPES
+
+#include <items/kraeuter/kraeuter.h>
+#include <properties.h>
+
+// Flag, das im reset() das Speichern ausloest, wenn es gesetzt ist.
+static  int need_save;  
+static  int basisvalue=400; // gibt den _Durchschnittswert_ eines Krauts an
+static  int maxvalue=10000; // bestimmt den max. Wert aller Kraeuter
+
+// Enthaelt fuer jede PlantID einen Zaehler, der angibt, wieviel von dem
+// jeweiligen Kraut verkauft wurde.
+// private int *count=({});
+
+// Summe ueber alle Eintraege in count
+// private int sum;
+
+protected void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+
+  SetProp(P_BUY_ONLY_PLANTS,1);
+  /*
+  seteuid(getuid()); // wird zum speichern benötigt
+  int si=sizeof(count);
+  // fuer jede PlantID einen Eintrag anlegen, d.h. wenn in count noch
+  // nicht genug vorhanden sind, werden fehlende Eintraege ergaenzt.
+  if (si<PLANTCOUNT) 
+    count=count+allocate(PLANTCOUNT-si);
+  */
+}
+
+protected void create_super()
+{
+  set_next_reset(-1);
+}
+
+static string sell_obj_only_plants(object ob, int short)
+{
+   if (!IS_PLANT(ob))
+      return "Tut mir leid, damit kann ich nichts anfangen.";
+   // es werden nur Kraeuter angekauft, die legal geclont wurden,
+   // d.h. im entsprechenden Master ordentlich eingetragen wurden!
+   if (ob->QueryPlantId()<=0)
+      return ob->Name(WER, 2)+" ist illegal auf die Welt gekommen. Ich darf "
+            +ob->QueryPronoun(WEN)+" leider nicht annehmen.";
+   return ::sell_obj(ob, short);
+}
+
+static string sell_obj(object ob, int short)
+{
+   // es werden nur Kraeuter angekauft, die legal geclont wurden,
+   // d.h. im entsprechenden Master ordentlich eingetragen wurden!
+  if (IS_PLANT(ob) && ob->QueryPlantId()<=0)
+    return "Hm, "+ob->Name(WER, 2)+" stammt aber aus einer sehr dubiosen "
+      "Quelle. Ich kann "+ob->QueryPronoun(WEN)+" leider nicht annehmen.";
+  if (QueryProp(P_BUY_ONLY_PLANTS))
+    return sell_obj_only_plants(ob, short);
+  return ::sell_obj(ob,short);
+}
+
+/*
+void reset()
+{
+  ::reset();
+  // Daten sind nicht sooo wichtig, als das bei jeder Aenderung
+  // gesavet werden muesste. Daher nun im reset speichern.
+  if (need_save) {
+    // basisvalue/PLANTCOUNT: Durchschnittswert eines einzelnen Krauts
+    // sum + PLANTCOUNT: Gesamtzahl verkaufter plus Zahl existierender
+    //                   Kraeuter
+    // Wenn also der Wert all dieser Kraeuter > 10k ist, wird jeder
+    // Einzelzaehler auf 90% gestutzt, damit die Werte nicht ins Uferlose
+    // steigen.
+    if (((sum+PLANTCOUNT)*basisvalue/PLANTCOUNT)>maxvalue) {
+      int i, newsum;
+      for (i=sizeof(count)-1; i>=0; i--) {
+        count[i] = count[i]*9/10;
+        newsum+=count[i];
+      }
+      sum=newsum;
+    }
+    need_save=0;
+  }
+}
+
+// Aktualisiert den Datenbestand beim Kaufen oder Verkaufen eines Objekts
+void UpdateCounter(object ob, int num)
+{
+   int id=ob->QueryPlantId();
+   if (id>0 && is_plant(ob)) {
+      // Kauf oder Verkauf von Kraeutern, veraendert den Wert der
+      // Kraeuter
+      // Zaehler in der Liste hochzaehlen
+      count[id]+=num;
+      if (count[id]<0) count[id]=0; // darf aber ansich nich passieren
+      // Gesamtsumme aktualisieren
+      sum+=num;
+      need_save=1;
+   }
+   ::UpdateCounter(ob, num);
+}
+
+// Die Preise die hier im Labor fuer Kraeuter gezahlt und verlangt
+// werden, sind nicht fix sondern haengen von Angebot und Nachfrage ab.
+// Hier weiss man ueber den wahren Wert der einzelnen Kraeuter bescheid.
+static int realValue(object ob, object player)
+{
+   // Preise fuer normale Gueter ganz normal...
+   if (!is_plant(ob))
+       return ob->QueryProp(P_VALUE);
+
+   // jede Kraeuterkategorie bekommt den gleichen Wert zugewiesen.   
+   // val entspricht dem aktuellen "Durchschnittswert" eines Krautes
+   int val=(sum+PLANTCOUNT)*basisvalue/PLANTCOUNT;
+
+   // aber dieser Wert verteilt sich auf unterschiedlich viele Kraeuter
+   // (AN: Dieser Kommentar erschliesst sich mir nicht.)
+   int id=ob->QueryPlantId();
+   if (id<=0) return 0; // illegal geclont -> wertlos
+   // ggf. die Zaehlerliste um die fehlenden Eintraege erweitern.
+   if ((id-1)>sizeof(count)) 
+     count=count+allocate(id-1-sizeof(count));
+   
+   // "mittleren Wert" des abgefragten Krautes errechnen (Division durch
+   // dessen umgesetzte Anzahl)
+   val=val/(count[id]+1);
+   
+   // Wert runden auf naechstniedrigeren glatt durch 10 teilbaren Wert.
+   return val-val%10; 
+}
+
+// gibt den Preis zurück, der zum Ankauf des Objektes verwendet werden soll
+static varargs int QueryValue(object ob, int value, object player)
+{
+   return ::QueryValue(ob, realValue(ob, player), player);
+}
+
+// gibt den Preis an, zu dem das Objekt verkauft werden soll.
+static varargs int QueryBuyValue(mixed ob, object player)
+{
+   if (objectp(ob))
+      return (realValue(ob, player)*QueryBuyFact(player) + 50)/100;
+   // fixed Objekte...
+   return ::QueryBuyValue(ob, player);
+}
+*/
diff --git a/std/room/light.c b/std/room/light.c
new file mode 100644
index 0000000..a372047
--- /dev/null
+++ b/std/room/light.c
@@ -0,0 +1,61 @@
+// MorgenGrauen MUDlib
+//
+// container/light.c -- Lichtsystemkomponenten fuer Raeume
+//
+// $Id: description.c 6986 2008-08-22 21:32:15Z Zesstra $
+
+inherit "/std/container/light";
+
+#pragma strict_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/description.h>
+#include <room/description.h>
+#include <container.h>
+
+protected void create()
+{
+  ::create();
+  SetProp(P_LIGHT_ABSORPTION, 1);
+}
+
+protected void create_super() {set_next_reset(-1);}
+
+/*
+   // das wird selten benutzt und ist zur zeit funktionsunfaehig, da die
+   // interne Speicherung der Prop sich geaendert hat.
+static int _set_int_light(int *light)
+{
+   int tmp;
+
+   // zur Sicherheit
+   if (!pointerp(light)) return -1;
+   if (light[0]>QueryProp(P_LIGHT)) {
+      // Licht verlaeuft sich in einem grossen Raum, daher Modifier abfragen...
+      tmp=light[0]-QueryProp(P_LIGHT_ABSORPTION);
+      // wenn sich das Vorzeichen geaendert hat, auf 0 setzen.
+      light[0]=((tmp^light[0]) & 0x80000000 ? 0 : tmp);
+   }
+   if (light[1]<QueryProp(P_LIGHT) && light[1]<0) {
+      // Licht verlaeuft sich in einem grossen Raum, daher Modifier abfragen...
+      tmp=light[1]+QueryProp(P_LIGHT_ABSORPTION);
+      // wenn sich das Vorzeichen geaendert hat, auf 0 setzen.
+      light[1]=((tmp^light[1]) & 0x80000000 ? 0 : tmp);
+   }
+   light[2]=light[0]+light[1];
+   Set(P_INT_LIGHT, light, F_VALUE);
+   // diese Prop setzen kaum Leute (offiziell gehts ja auch gar nicht. Keiner
+   // davon erwartet nen Rueckgabewert. Daher wird hier 0 zurueckgeben, statt
+   // des aufwaendig berechneten QueryProp(P_INT_LIGHT).
+   // Achja. Der Rueckgabewert vom Set() waere ein int*, was nicht geht, weil
+   // diese Funktion nur int zurueckgeben darf.
+   return 0;
+}
+*/
diff --git a/std/room/moving.c b/std/room/moving.c
new file mode 100644
index 0000000..0f7c2b1
--- /dev/null
+++ b/std/room/moving.c
@@ -0,0 +1,17 @@
+// MorgenGrauen MUDlib
+//
+// room/moving.c -- Entfernen von Raeumen
+//
+// $Id: moving.c 8041 2012-03-19 18:38:21Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+public varargs int remove(int silent)
+{
+  destruct( this_object() );
+  return 1;
+}
diff --git a/std/room/para.c b/std/room/para.c
new file mode 100644
index 0000000..4666782
--- /dev/null
+++ b/std/room/para.c
@@ -0,0 +1,18 @@
+// This may look like C code, but it is really -*- C++ -*-
+
+// MorgenGrauen MUDlib
+//
+// room/para.c -- Betreten einer Parallelwelt
+//
+// $Id: para.c 7510 2010-03-25 23:37:19Z Zesstra $
+
+// Veraltet - wird nicht mehr benoetigt.
+// Die Zuordnung der 'richtigen' Welt wird jetzt direkt von move() uebernommen.
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+deprecated int paramove() { return 0; }
diff --git a/std/room/pub.c b/std/room/pub.c
new file mode 100644
index 0000000..c15e33e
--- /dev/null
+++ b/std/room/pub.c
@@ -0,0 +1,989 @@
+//
+// pub.c -- Alles, was eine Kneipe braucht.
+//
+// $Id: pub.c 8778 2014-04-30 23:04:06Z Zesstra $
+// spendiere ueberarbeitet, 22.05.2007 - Miril
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#include <thing/properties.h>
+
+#include <defines.h>
+#include <rooms.h>
+#include <properties.h>
+#include <routingd.h>
+#include <bank.h>
+#include <exploration.h>
+#include <wizlevels.h>
+#include <pub.h>
+
+// Alle nicht-privaten werden in erbenen Objekten verwendet.
+private nosave int     max_list;
+private nosave int     refresh_count;
+private nosave int     sum;
+private nosave mapping refresh_list;
+nosave mapping id_list;
+nosave mapping menu_list;
+nosave object  *waiting;
+
+#define PM_RATE_PUBMASTER  "rate"
+#define PM_DELAY_PUBMASTER "delay"
+
+protected void create()
+{ object router;
+
+  SetProp( P_ROOM_TYPE, QueryProp(P_ROOM_TYPE) | RT_PUB );
+
+  SetProp( P_PUB_NOT_ON_MENU,
+    "Tut mir leid, das fuehren wir nicht! Wir sind ein anstaendiges "+
+    "Lokal!\n" );
+  SetProp( P_PUB_UNAVAILABLE,
+    "Davon ist leider nichts mehr da.\n" );
+  SetProp(P_PUB_NO_MONEY,
+    "Das kostet %d Goldstuecke, und Du hast nicht so viel!\n" );
+  SetProp(P_PUB_NO_KEEPER,
+    "Es ist niemand anwesend, der Dich bedienen koennte.\n");
+
+  AddCmd( "menue","menue" );
+  AddCmd( ({"kauf","kaufe","bestell","bestelle"}),"bestelle" );
+  AddCmd( ({"spendier","spendiere"}),"spendiere" );
+  AddCmd( "pubinit","pubinit" );
+
+  max_list=0;
+  refresh_count=0;
+  waiting = ({ });
+  id_list=([]);
+  menu_list=([]);
+  refresh_list=([]);
+
+  if ( !clonep(ME) && objectp(router=find_object(ROUTER)) )
+    router->RegisterTarget(TARGET_PUB,object_name(ME));
+
+  call_out("add_std_drinks",1);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+/* Zur Syntax:
+ *
+ * menuetext - Der Text steht im Menue
+ *
+ * ids       - Array der Namen, mit denen bestellt werden kann
+ *
+ * minfo     - Mapping mit Eintraegen fuer:
+ *                 P_HP (HP-Heilung),
+ *                 P_SP (SP-Heilung),
+ *                 P_FOOD (Saettigung),
+ *                 P_DRINK (Fluessigkeit)
+ *                 P_ALCOHOL (Alkoholisierung)
+ *                 P_VALUE (Preis)
+ *             Die Eintraege werden ueber eval_anything ausgewertet
+ *             (siehe da)
+ *
+ * rate      - Heilrate (auch ueber eavl_anything) in Punkte / heart_beat()
+ *
+ * msg       - Meldung beim Essen.
+ *             a) closure (wird mit funcall(msg,zahler,empfaenger)
+ *                ausgewertet)
+ *             b) string (wie closure: call_other(this_object...))
+ *             c) array mit 2 strings: 1) fuer Essenden, 2) fuer andere
+ *                siehe auch mess()
+ *
+ * refresh   - Mapping mit den moeglichen Eintraegen:
+ *                 PR_USER : <Kontingent> ; <Update> (pro Spieler)
+ *                 PR_ALL  : <Kontingent> ; <Update> (fuer alle)
+ *             Es wird zunaechst geprueft, ob noch etwas fuer den
+ *             (zahlenden!) Spieler selbst vorhanden ist wenn nein wird
+ *             geschaut, ob das Kontingent fuer alle schon erschoepft ist.
+ *             Sind beide Kontingente erschoepft, kann der Spieler das
+ *             Bestellte nicht bekommen.
+ *             Die Kontingente wird alle <Update> reset()s "aufgefrischt".
+ *             <Kontingent> wird ueber eval_anything() ausgewertet.
+ *             Alternativ kann man einen Int-Wert angeben. Dieser wird wie
+ *             ([ PR_ALL : <wert> ; 1 ]) behandelt.
+ *
+ * delay     - Zahl der Sekunden, um die verzoegert die Heilung eintritt
+ *             z.B. weil das Essen erst zubereitet werden muss.
+ *             Ebenfalls ueber eval_anything()
+ *
+ * d_msg     - Meldung beim bestellen, falls Delay. Wie msg.
+ */
+varargs string AddToMenu(string menuetext, mixed ids, mapping minfo,
+                         mixed rate, mixed msg, mixed refresh,
+                         mixed delay, mixed d_msg)
+{ string ident;
+  int i;
+
+  if ( !stringp(menuetext) || !ids || !mappingp(minfo) )
+    return 0;
+
+  if ( stringp(ids) )
+    ids = ({ ids });
+  else if ( !pointerp(ids) )
+    return 0;
+
+  ident = sprintf("menuentry%d",max_list);
+  max_list++;
+
+  /* Fuer schnelles Nachschlagen ein eigenes Mapping fuer Ids */
+  for( i=sizeof(ids)-1;i>=0;i-- )
+    id_list += ([ ids[i] : ident ]);
+
+  if ( intp(refresh) && (refresh>0) )
+    refresh = ([ PR_ALL : refresh; 1 ]);
+  else if ( !mappingp(refresh) )
+    refresh = 0;
+
+  menu_list += ([ ident : menuetext; minfo; rate; msg; refresh;
+                   delay; d_msg; ids ]);
+  return ident;
+}
+
+// Diese Methode ist nur noch aus Kompatibilitaetsgruenden vorhanden und darf
+// nicht mehr verwendet werden!!!
+void AddFood(string nameOfFood, mixed ids, int price, int heal,
+             mixed myFunction)
+{
+  if ( !nameOfFood || !ids || !price)
+    return; /* Food not healing is ok ! */
+
+  AddToMenu( nameOfFood,ids,
+             ([ P_VALUE : price, P_FOOD : heal, P_HP : heal, P_SP : heal ]),
+             ((heal>5)?5:heal), myFunction, 0,0,0);
+}
+
+// Diese Methode ist nur noch aus Kompatibilitaetsgruenden vorhanden und darf
+// nicht mehr verwendet werden!!!
+void AddDrink(string nameOfDrink, mixed ids, int price, int heal,
+              int strength, int soak, mixed myFunction)
+{
+  if ( !nameOfDrink || !ids || !price )
+    return;
+
+  heal=heal/2;
+  /* Kleine Korrektur, damit man in alten Pubs ueberhaupt trinken kann */
+  AddToMenu(nameOfDrink,ids,
+             ([ P_VALUE : price, P_DRINK : soak, P_ALCOHOL : strength,
+                P_HP : heal, P_SP : heal ]),
+             ((heal>5)?5:heal), myFunction,0,0,0);
+}
+
+int RemoveFromMenu(mixed ids) {
+  int ret;
+
+  if (stringp(ids))
+    ids = ({ids});
+
+  if (pointerp(ids)) {
+    foreach (string id: ids) {
+      // look if the id has a matching ident
+      string ident = id_list[id];
+      if (stringp(ident)) {
+        // remove this ident-entry from the id-list ...
+        m_delete(id_list, id);
+        // ... and remove all others now too (so it won't bug later, if
+        //     the wizard calling this method forgot an id)
+        foreach (string listid: m_indices(id_list))
+          if (id_list[listid] == ident)
+            m_delete(id_list, listid);
+
+        // now remove the ident from the menu_list
+        if (member(menu_list, ident)) {
+          ret++;
+          m_delete(menu_list, ident);
+
+          // decrease the ident-counter, if this entry was the last one
+          int oldnum;
+          if (sscanf(ident, "menuentry%d", oldnum) == 1 &&
+              oldnum == (max_list-1))
+            max_list--;
+        }
+      }
+    }
+  }
+  // return removed entries
+  return ret;
+}
+
+/* Zum Auswerten der Eintraege fuer Preis, Rate, HP...
+ * a) integer-Wert    -> wird direkt uebernommen
+ * b) mapping         -> Wird als RaceModifiere verstanden. Es wird der
+ *                       Eintrag gewaehlt, der der Rasse des Spielers
+ *                       entspricht, falls vorhanden, ansonsten der Eintrag
+ *                       fuer 0.
+ * c) Array           -> In erstem Element (muss Objekt oder dessen Filename
+ *                       sein) wird die Funktion mit dem Namen im 2.Element
+ *                       aufgerufen.
+ * d) String          -> Die genannte Funktion wird in der Kneipe selbst
+ *                       aufgerufen.
+ * e) Closure         -> Die Closure wird ausgewertet.
+ * Alle Funktionsaufrufe bekommen den essenden Spieler (bei Price und Delay
+ * den bestellenden Spieler) als Argument uebergeben. Die Auswertung erfolgt
+ * in der angegebenen Reihenfolge. Am Ende muss ein Intergerwert herauskommen
+ */
+int eval_anything(mixed what, object pl)
+{ mixed re;
+
+  if (intp(what))
+    return what;
+
+  if (mappingp(what) && pl)
+  {
+    re = what[pl->QueryProp(P_RACE)]||what[0];
+  }
+
+  if (re)
+    what=re;
+
+  if ( pointerp(what) && (sizeof(what)>=2)
+      && ( objectp(what[0]) || stringp(what[0]) )
+      && stringp(what[1]) )
+    what = call_other(what[0],what[1],pl);
+
+  if ( stringp(what) && function_exists(what,ME) )
+    what = call_other(ME,what,pl);
+
+  if ( closurep(what) )
+    what = funcall(what,pl);
+
+  if ( intp(what) )
+    return what;
+
+  return 0;
+}
+
+/* Diese Funktion ueberprueft, ob das Kontingent eines Menueeintrags
+ * fuer einen Spieler erschoepft ist.
+ */
+string CheckAvailability(string ident, object zahler)
+{ string uid;
+
+  if ( !stringp(ident) || !member(menu_list,ident) || !objectp(zahler) )
+    return 0;
+  if ( !mappingp(menu_list[ident,PM_REFRESH]) )
+    return PR_NONE;
+
+  if ( !member(refresh_list,ident) )
+    refresh_list += ([ ident : ([ ]) ]);
+
+  if ( query_once_interactive(zahler) )
+    uid=getuid(zahler);
+  else
+    uid=object_name(zahler);
+
+  if ( member(menu_list[ident,PM_REFRESH],PR_USER) )
+  {
+    if ( !member(refresh_list[ident],uid) )
+    {
+      refresh_list[ident] += ([ uid : 0 ; refresh_count ]);
+    }
+
+    /* Kontingent des Zahlenden pruefen */
+    if ( refresh_list[ident][uid,PRV_AMOUNT] <
+          eval_anything(menu_list[ident,PM_REFRESH][PR_USER,PRV_AMOUNT],
+                        zahler) )
+      return uid;
+  }
+
+  if ( member(menu_list[ident,PM_REFRESH],PR_ALL) )
+  {
+    if ( !member(refresh_list[ident],PR_DEFAULT) )
+    {
+      refresh_list[ident] += ([ PR_DEFAULT : 0 ; refresh_count ]);
+    }
+
+    /* Kontingent der Allgemeinheit pruefen */
+    if ( refresh_list[ident][PR_DEFAULT,PRV_AMOUNT] <
+          eval_anything(menu_list[ident,PM_REFRESH][PR_ALL,PRV_AMOUNT],
+                        zahler) )
+      return PR_DEFAULT;
+  }
+
+  return 0;
+}
+
+/* Diese Funktion reduziert das Kontingent des Lebewesens uid beim
+ * Menueeintrag ident um 1
+ */
+void DecreaseAvailability(string ident, string uid)
+{
+  if ( !stringp(ident) || !stringp(uid) )
+    return;
+  refresh_list[ident][uid,PRV_AMOUNT]++;
+}
+
+/* Diese Funktion sorgt dafuer, dass die Kontingente an limitierten
+ * Sachen in regelmaessigen Abstaenden wiederhergestellt werden.
+ */
+static void UpdateAvailability()
+{ int    i1,i2;
+  string *ind1,*ind2,chk;
+
+  /* Keine Refresh-Eintraege, kein Update */
+  if ( !mappingp(refresh_list)
+      || (i1=sizeof(ind1=m_indices(refresh_list)))<1 )
+    return;
+
+  /* Es muss jeder Menueeintrag, der in der refresh_list steht,
+   * durchgegangen werden.
+   */
+  for ( --i1 ; i1>=0 ; i1-- )
+  {
+    if ( !mappingp(refresh_list[ind1[i1]])
+        || (i2=sizeof(ind2=m_indices(refresh_list[ind1[i1]])))<1 )
+      continue;
+
+    /* Fuer jeden Menueeintrag muss jeder Spielereintrag durchgegangen
+     * werden, der in dem entspr. mapping steht.
+     */
+    for ( --i2 ; i2>=0 ; i2-- ) // Alle Spieler
+    {
+      if ( ind2[i2]==PR_DEFAULT )
+        chk = PR_ALL;
+      else
+        chk = PR_USER;
+
+      if ( ( refresh_list[ind1[i1]][ind2[i2],PRV_REFRESH]
+            + menu_list[ind1[i1],PM_REFRESH][chk,PRV_REFRESH] )
+          <= refresh_count )
+      {
+        refresh_list[ind1[i1]][ind2[i2],PRV_AMOUNT]=0;
+        refresh_list[ind1[i1]][ind2[i2],PRV_REFRESH]=refresh_count;
+      }
+    }
+  }
+}
+
+mixed DBG(mixed o) {
+  if(find_player("rumata"))
+    tell_object(
+        find_player("rumata"),
+        sprintf("DBG: %O\n", o)
+    );
+  return 0;
+}
+
+/* Erweitert um die Moeglichkeit, Speise- oder Getraenke-Karte zu sehen. */
+string menue_text(string str)
+{ int i,sdr,sfo;
+  string ident,res;
+  string *fo=({}),*dr=({});
+
+  if ( !max_list )
+    return "Hier scheint es derzeit nichts zu geben.\n";
+
+  if ( !stringp(str) || str=="" )
+    str="alles";
+
+  /* Fuers Menue entscheiden ob Drink oder Food */
+  foreach(string id, string menuetext, mapping minfo: menu_list)
+  {
+    if (eval_anything(minfo[P_FOOD],this_player()))
+      fo+=({ id });
+    else
+      dr+=({ id });
+  }
+
+  sdr = sizeof(dr);
+  sfo = sizeof(fo);
+
+  if ( member(({"alle","alles"}),str)!=-1)
+  {
+    if ( !sfo )
+      str="drinks";
+    else if ( !sdr )
+      str="speise";
+    else
+    {
+      /* Gemischte Karte */
+      res = "Getraenke                    Preis alc | "+
+            "Speisen                          Preis\n"+
+            "---------------------------------------+-"+
+            "--------------------------------------\n";
+
+      for ( i=0 ; ( i<sdr || i<sfo ) ; i++ )
+      {
+        if ( i<sdr )
+          res += sprintf("%-29.29s%5.5d  %c  | ",
+                   menu_list[dr[i],PM_TEXT],
+                   eval_anything(menu_list[dr[i],PM_INFO][P_VALUE],PL),
+                   (eval_anything(menu_list[dr[i],PM_INFO][P_ALCOHOL],PL)>0) ? 'J' : 'N');
+        else
+          res += "                                       | ";
+
+        if ( i<sfo )
+          res += sprintf("%-33.33s%5.5d",
+                   menu_list[fo[i],PM_TEXT],
+                   eval_anything(menu_list[fo[i],PM_INFO][P_VALUE],PL));
+
+        res += "\n";
+      }
+
+      return res;
+    }
+  }
+
+  /* Reine Getraenkekarte */
+  if ( member(({"getraenke","drinks","getraenk","trinken"}),str)!=-1 )
+  {
+    if ( !sdr )
+      return "Hier gibt es leider nichts zu trinken.\n";
+
+    res = "Getraenke                    Preis alc | "+
+          "Getraenke                    Preis alc\n"+
+          "---------------------------------------+-"+
+          "--------------------------------------\n";
+
+    for ( i=0 ; i<sdr ; i++ )
+    {
+      ident = dr[i];
+
+      if ( !eval_anything(menu_list[ident,PM_INFO][P_FOOD], PL) )
+        res += sprintf("%-29.29s%5.5d  %c%s",
+                 menu_list[ident,PM_TEXT],
+                 eval_anything(menu_list[ident,PM_INFO][P_VALUE],PL),
+                 (eval_anything(menu_list[ident,PM_INFO][P_ALCOHOL],PL)>0) ? 'J' : 'N',
+                 ((i%2)?"\n":"  | "));
+    }
+
+    if ( res[<1..<1]!="\n" )
+      res += "\n";
+
+    return res;
+  }
+
+  /* Reine Speisekarte */
+  if ( member(({"speise","speisen","essen"}),str)!=-1 )
+  {
+    if ( !sfo )
+      return "Hier gibt es leider nichts zu essen.\n";
+
+    res = "Speisen                          Preis | "+
+          "Speisen                          Preis\n"+
+          "---------------------------------------+-"+
+          "--------------------------------------\n";
+
+    for ( i=0 ; i<sfo ; i++ )
+    {
+      ident = fo[i];
+      if (eval_anything(menu_list[ident,PM_INFO][P_FOOD],PL) )
+        res += sprintf("%-33.33s%5.5d%s",
+                 menu_list[ident,PM_TEXT],
+                 eval_anything(menu_list[ident,PM_INFO][P_VALUE],PL),
+                 ((i%2)?"\n":" | "));
+    }
+
+    if ( res[<1..<1]!="\n" )
+      res += "\n";
+
+    return res;
+  }
+
+  return 0;
+}
+
+int menue(string str)
+{ string txt;
+
+  _notify_fail("Welchen Teil des Menues moechtest Du sehen?\n");
+  if ( !stringp(txt=menue_text(str)) || sizeof(txt)<1 )
+    return 0;
+  write(txt);
+  return 1;
+}
+
+/* Diese Funktion kann/soll bei Bedarf ueberladen werden, um simultane
+ * Aenderungen des Mappings zu ermoeglichen (zu Beispiel wie es in guten
+ * Tagen groesser Portionen gib, Hobbits per se mehr kriegen, ...
+ */
+mapping adjust_info(string ident, mapping minfo, object zahler,
+                    object empfaenger)
+{
+  return 0;
+}
+
+/* Hier hats jede Menge neue Platzhalter */
+string mess(string str,object pl)
+{
+  string dummy1, dummy2;
+
+  if ( !pl )
+    pl=PL;
+
+  if ( !stringp(str) || str=="" )
+    return str;
+
+  str=implode(explode(str,"&&"),pl->name(WER,2));
+  str=implode(explode(str,"&1&"),pl->name(WER,2));
+  str=implode(explode(str,"&2&"),pl->name(WESSEN,2));
+  str=implode(explode(str,"&3&"),pl->name(WEM,2));
+  str=implode(explode(str,"&4&"),pl->name(WEN,2));
+  str=implode(explode(str,"&1#"),capitalize(pl->name(WER,2)));
+  str=implode(explode(str,"&2#"),capitalize(pl->name(WESSEN,2)));
+  str=implode(explode(str,"&3#"),capitalize(pl->name(WEM,2)));
+  str=implode(explode(str,"&4#"),capitalize(pl->name(WEN,2)));
+  str=implode(explode(str,"&!"),pl->QueryPronoun(WER));
+  str=implode(explode(str,"&5&"),pl->QueryPronoun(WER));
+  str=implode(explode(str,"&6&"),pl->QueryPronoun(WESSEN));
+  str=implode(explode(str,"&7&"),pl->QueryPronoun(WEM));
+  str=implode(explode(str,"&8&"),pl->QueryPronoun(WEN));
+  str=implode(explode(str,"&5#"),capitalize(pl->QueryPronoun(WER)));
+  str=implode(explode(str,"&6#"),capitalize(pl->QueryPronoun(WESSEN)));
+  str=implode(explode(str,"&7#"),capitalize(pl->QueryPronoun(WEM)));
+  str=implode(explode(str,"&8#"),capitalize(pl->QueryPronoun(WEN)));
+
+  return break_string(capitalize(str),78,"", BS_LEAVE_MY_LFS);
+}
+
+protected void _copy_menulist_values(mapping entryinfo, string ident) {
+  /* Kopieren aller Werte ins minfo-Mapping, um Problemen bei Loeschung
+     aus dem Weg zu gehen. Slow and dirty */
+  entryinfo[PM_TEXT]      = deep_copy(menu_list[ident, PM_TEXT]);
+  // PM_INFO is already flat in entryinfo
+  entryinfo[PM_RATE_PUBMASTER]
+                          = deep_copy(menu_list[ident, PM_RATE]);
+  entryinfo[PM_SERVE_MSG] = deep_copy(menu_list[ident, PM_SERVE_MSG]);
+  entryinfo[PM_REFRESH]   = deep_copy(menu_list[ident, PM_REFRESH]);
+  // PM_DELAY is already evaluated in entryinfo
+  entryinfo[PM_DELAY_MSG] = deep_copy(menu_list[ident, PM_DELAY_MSG]);
+  entryinfo[PM_IDS]       = deep_copy(menu_list[ident, PM_IDS]);
+}
+
+int do_deliver(string ident, object zahler, object empfaenger,
+               mapping entryinfo) {
+  waiting -= ({ empfaenger,0 });
+
+  /* Empfaenger muss natuerlich noch da sein */
+  if ( !objectp(empfaenger) || !present(empfaenger) )
+    return 0;
+
+  /* Zahler wird nur wegen der Abwaertskompatibilitaet gebraucht */
+  if ( !objectp(zahler) )
+    zahler = empfaenger;
+
+  // alte Pubs, die do_deliver irgendwie selbst aufrufen, sollten
+  // mit der Zeit korrigiert werden
+  if(!mappingp(entryinfo))
+    raise_error("Pub ruft do_deliver() ohne sinnvolle Argumente auf.\n");
+  if(!member(entryinfo, PM_RATE_PUBMASTER)) {
+    if(!member(menu_list, ident))
+      raise_error("Pub ruft do_deliver() mit geloeschtem Getraenk und "
+                  "teilweisen Argumenten auf!\n");
+
+    _copy_menulist_values(entryinfo, ident);
+    call_out(#'raise_error, 1,
+             "Pub ruft do_deliver() nur mit teilweisen Argumenten auf.\n");
+  }
+
+  entryinfo[PM_RATE_PUBMASTER] = eval_anything(entryinfo[PM_RATE_PUBMASTER],
+                                               empfaenger);
+  entryinfo[P_HP]              = eval_anything(entryinfo[P_HP], empfaenger);
+  entryinfo[P_SP]              = eval_anything(entryinfo[P_SP], empfaenger);
+
+  /* Ueberpruefen, ob Heilmoeglichkeit legal */
+  if ( query_once_interactive(empfaenger)
+      && ((PUBMASTER->RegisterItem(entryinfo[PM_TEXT], entryinfo))<1) ) {
+     tell_object(empfaenger,
+       "Mit diesem Getraenk/Gericht scheint etwas nicht in Ordnung "+
+       "zu sein.\nVerstaendige bitte den Magier, der fuer diesen "+
+       "Raum verantwortlich ist.\n");
+     return -4;
+  }
+
+  if ( QueryProp(P_NPC_FASTHEAL) && !query_once_interactive(empfaenger) ) {
+    entryinfo[H_DISTRIBUTION] = HD_INSTANT;
+  }
+  else {
+    entryinfo[H_DISTRIBUTION] = entryinfo[PM_RATE_PUBMASTER];
+  }
+  empfaenger->consume(entryinfo);
+
+  /* Meldung ausgeben */
+  /* Hinweis: Da die ausfuehrenden Funktionen auch ident und minfo
+   * uebergeben bekommen, kann man hier auch ueber adjust_info oder
+   * an anderer Stelle zusaetzliche Informationen uebergeben...
+   */
+  if (closurep(entryinfo[PM_SERVE_MSG]) )
+    funcall(entryinfo[PM_SERVE_MSG], zahler, empfaenger, ident, entryinfo);
+  else if (stringp(entryinfo[PM_SERVE_MSG]) &&
+           function_exists(entryinfo[PM_SERVE_MSG],ME))
+    call_other(ME, entryinfo[PM_SERVE_MSG], zahler, empfaenger, ident,
+                   entryinfo);
+  else if (pointerp(entryinfo[PM_SERVE_MSG]) &&
+           sizeof(entryinfo[PM_SERVE_MSG])>=2)  {
+    if (stringp(entryinfo[PM_SERVE_MSG][0]) &&
+        sizeof(entryinfo[PM_SERVE_MSG][0]))
+      tell_object(empfaenger,
+        mess(entryinfo[PM_SERVE_MSG][0]+"\n", empfaenger));
+    if (stringp(entryinfo[PM_SERVE_MSG][1]) &&
+        sizeof(entryinfo[PM_SERVE_MSG][1]))
+      tell_room(environment(empfaenger)||ME,
+        mess(entryinfo[PM_SERVE_MSG][1]+"\n",empfaenger),
+        ({empfaenger}) );
+  }
+
+  return 1;
+}
+
+/* Testet, ob genug Geld zur Verfuegung steht.
+ * Falls die Bonitaet anderen Beschraenkungen unterliegt, als
+ * dass der Zahler genug Geld dabei hat, muss diese Methode
+ * ueberschrieben werden.
+ * Rueckgabewerte:
+ * -2 : Out of Money
+ *  0 : Alles OK.
+ * Rueckgabewerte != 0 fuehren zu einem Abbruch der Bestellung
+ */
+int CheckSolvency(string ident, object zahler, object empfaenger,
+                   mapping entryinfo)
+{
+  if ( (zahler->QueryMoney())<entryinfo[P_VALUE] )
+  {
+    string res;
+    if ( !stringp(res=QueryProp(P_PUB_NO_MONEY)) )
+      res = "Das kostet %d Goldstuecke, und Du hast nicht so viel!\n";
+    tell_object(zahler,sprintf(res, entryinfo[P_VALUE]));
+    return -2;
+  }
+  return 0;
+}
+
+/* Fuehrt die Bezahlung durch.
+ * Falls die Bezahlung anders erfolgt, als durch Abzug des Geldes vom Zahler,
+ * muss diese Methode ueberschrieben werden
+ * Rueckgabewerte:
+ * Anzahl der Muenzen, die im Pub landen und bei Reset in die Zentralbank
+ * eingezahlt werden
+ */
+int DoPay(string ident, object zahler, object empfaenger, mapping entryinfo)
+{
+  zahler->AddMoney(-entryinfo[P_VALUE]);
+  return entryinfo[P_VALUE];
+}
+
+/* Rueckgabewerte:
+ * -6 : Nicht vorraetig
+ * -5 : Wirt nicht anwesend
+ * -4 : Illegales Getraenk/Gericht. Ausgabe verweigert.
+ *      Nur bei sofortiger Lieferung...
+ * -3 : Empfaenger bereits voll
+ * -2 : Out of Money
+ * -1 : spendieren ignoriert
+ *  0 : Empfaenger ausgeflogen (sollte eigentlich nicht passieren)
+ *  1 : Alles OK.
+ */
+int consume_something(string ident, object zahler, object empfaenger) {
+  if ( !objectp(zahler) )
+    zahler=PL;
+
+  if ( !objectp(empfaenger) )
+    empfaenger=PL;
+
+  /* Die Abfrage auf anwesenden Wirt erfolgt NUR an dieser Stelle, damit */
+  /* kein Spieler darunter leiden muss, wenn jemand anderes zwischen     */
+  /* Bestellung und Lieferung den Wirt meuchelt.                         */
+  if ( stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME))
+  {
+    string res = QueryProp(P_PUB_NO_KEEPER);
+    if ( !stringp(res) ) {
+      res = "Es ist niemand anwesend, der Dich bedienen koennte.\n";
+    }
+    tell_object(zahler,res);
+    return -5;
+  }
+
+  /* Spendiert und ignoriert? */
+  if ( zahler!=empfaenger )
+  {
+    mixed res = ({"spendiere"});
+    if ( eval_anything(menu_list[ident,PM_INFO][P_DRINK],empfaenger) )
+      res += ({"spendiere.getraenke"});
+    if ( eval_anything(menu_list[ident,PM_INFO][P_FOOD],empfaenger) )
+      res += ({"spendiere.essen"});
+    if ( eval_anything(menu_list[ident,PM_INFO][P_ALCOHOL],empfaenger) )
+      res += ({"spendiere.alkohol"});
+    if ( empfaenger->TestIgnoreSimple(res) )
+    {
+      tell_object(zahler,
+        empfaenger->Name(WER)+" will das nicht.\n");
+      return -1;
+    }
+  }
+
+  /* Hier kann das Info-Mapping erst mal als ganzes angepasst werden. */
+  mapping xinfo;
+  mapping entryinfo = deep_copy(menu_list[ident, PM_INFO]);
+  if ( (xinfo=adjust_info(ident,entryinfo,zahler,empfaenger)) &&
+       mappingp(xinfo) )
+    entryinfo += xinfo;
+
+  /* Genug Geld vorhanden? */
+  entryinfo[P_VALUE] = eval_anything(entryinfo[P_VALUE], zahler);
+  {
+    int res = CheckSolvency(ident, zahler, empfaenger, entryinfo);
+    if (res != 0) return res;
+  }
+
+  string avb;
+  if ( !stringp(avb=CheckAvailability(ident, zahler)) )
+  {
+    string res = QueryProp(P_PUB_UNAVAILABLE);
+    if ( !stringp(res) )
+      res = "Davon ist leider nichts mehr da.\n";
+    tell_object(zahler,res);
+    return -6;
+  }
+
+  /* Textausgabe beim spendieren */
+  if ( empfaenger!=zahler)
+  {
+    tell_room(environment(empfaenger)||ME,
+      zahler->Name(WER)+" spendiert "+empfaenger->name(WEM)+" etwas.\n",
+      ({zahler, empfaenger}) );
+    tell_object(empfaenger,
+      zahler->Name(WER)+" spendiert Dir etwas.\n");
+    tell_object(zahler,
+      "Du spendierst "+empfaenger->name(WEM)+" etwas.\n");
+  }
+
+  /* Testen, ob mans noch essen / trinken kann */
+  /* Die int-Werte werden in minfo uebernommen fuer die Auswertung */
+  /* im Pubmaster. */
+  entryinfo[P_FOOD]    = eval_anything(entryinfo[P_FOOD],   empfaenger);
+  entryinfo[P_ALCOHOL] = eval_anything(entryinfo[P_ALCOHOL],empfaenger);
+  entryinfo[P_DRINK]   = eval_anything(entryinfo[P_DRINK],  empfaenger);
+
+  int result = empfaenger->consume(entryinfo, 1);
+  if (result < 0) {
+    if (result & HC_MAX_FOOD_REACHED)
+      tell_object(empfaenger,
+                  "Du bist zu satt, das schaffst Du nicht mehr.\n");
+    else if (result & HC_MAX_DRINK_REACHED)
+      tell_object(empfaenger,
+                  "So viel kannst Du im Moment nicht trinken.\n");
+    else if (result & HC_MAX_ALCOHOL_REACHED)
+      tell_object(empfaenger,
+                  "Soviel Alkohol vertraegst Du nicht mehr.\n");
+    return -3;
+  }
+
+  /* Gezahlt wird auch sofort */
+  sum += DoPay(ident, zahler, empfaenger, entryinfo);
+
+  /* FPs gibts auch sofort */
+  if (zahler == empfaenger)
+    GiveEP(EP_PUB,menu_list[ident,PM_IDS][0]);
+
+  /* Falls die Anzahl des Bestellten beschraenkt ist, muss diese natuerlich
+   * angepasst werden.
+   */
+  if ( avb!=PR_NONE )
+    DecreaseAvailability(ident,avb);
+
+  /* Gibt es eine Zeitverzoegerung zwischen Bestellen und Servieren? */
+  entryinfo[PM_DELAY_PUBMASTER] = eval_anything(menu_list[ident, PM_DELAY], zahler);
+
+  // alle fuer einen Drink notwendigen Werte kopieren
+  _copy_menulist_values(entryinfo, ident);
+
+  if (entryinfo[PM_DELAY_PUBMASTER]<=0)
+    return do_deliver(ident,zahler,empfaenger,entryinfo);
+
+  /* Bestell-Meldung ausgeben */
+  if (closurep(entryinfo[PM_DELAY_MSG]))
+    funcall(entryinfo[PM_DELAY_MSG], zahler, empfaenger,ident, entryinfo);
+  else if (stringp(entryinfo[PM_DELAY_MSG]) &&
+           function_exists(entryinfo[PM_DELAY_MSG],ME))
+    call_other(ME, entryinfo[PM_DELAY_MSG], zahler, empfaenger, ident,
+               entryinfo);
+  else if (pointerp(entryinfo[PM_DELAY_MSG]) &&
+           sizeof(entryinfo[PM_DELAY_MSG])>=2) {
+    if (stringp(entryinfo[PM_DELAY_MSG][0]) &&
+       sizeof(entryinfo[PM_DELAY_MSG][0]))
+      tell_object(empfaenger,
+        mess(entryinfo[PM_DELAY_MSG][0]+"\n",empfaenger));
+    if (stringp(entryinfo[PM_DELAY_MSG][1]) &&
+        sizeof(entryinfo[PM_DELAY_MSG][1]))
+      tell_room(environment(empfaenger)||ME,
+        mess(entryinfo[PM_DELAY_MSG][1]+"\n",empfaenger),
+        ({empfaenger}) );
+  }
+
+  waiting += ({ empfaenger });
+  call_out("do_deliver", entryinfo[PM_DELAY_PUBMASTER],
+           ident, zahler, empfaenger, entryinfo);
+
+  return 1;
+}
+
+/* Rueckgabewere: 0: Nicht im Menue gefunde, 1 sonst */
+int search_what(string str,object zahler,object empfaenger)
+{ string ident;
+
+  if ( member(waiting,empfaenger)!=-1 )
+  {
+    if ( PL==empfaenger )
+      write("Du wartest doch noch auf etwas!\n");
+    else
+      write(empfaenger->Name(WER,2)+" wartet noch auf etwas.\n");
+    return 1;
+  }
+
+  str = lower_case(str);
+  if ( ident=id_list[str] )
+  {
+    consume_something(ident,zahler,empfaenger);
+    return 1;
+  }
+
+  return 0;
+}
+
+// Neue Version von Mesi:
+int spendiere(string str)
+{
+   _notify_fail("spendiere <spieler> <drink>\n");
+
+   if ( !stringp(str) || str=="" )
+     return 0;
+
+   string who,what;
+   int whoidx;
+
+   if (sscanf(str,"%s %d %s",who,whoidx,what)!=3
+    && sscanf(str,"%s %s",who,what)!=2)
+      return 0;
+  object target=present(who, whoidx);
+  if(!target && this_player()) target=present(who, whoidx, this_player());
+   if ( !target || !living(target) )
+   {
+     write("Das Lebewesen ist nicht hier...\n");
+     return 1;
+   }
+
+   notify_fail((string)QueryProp(P_PUB_NOT_ON_MENU)||"So etwas gibt es hier nicht!\n");
+
+   return search_what(what,PL,target);
+}
+
+int bestelle(string str)
+{
+  notify_fail((string)QueryProp(P_PUB_NOT_ON_MENU));
+
+  if ( !stringp(str) )
+    return 0;
+
+  return search_what(str,PL,PL);
+}
+
+int pubinit()
+{ string  *liste,ident,fn;
+  int     si,erg,max;
+  mapping minfo,xinfo;
+
+  if ( !PL || !IS_WIZARD(PL) )
+    return 0;
+
+  si=sizeof(liste=sort_array(m_indices(menu_list),#'<));
+  if ( si<1 )
+    return notify_fail("Keine Gerichte/Getraenke vorhanden.\n"),0;
+
+  fn=old_explode(object_name(ME),"#")[0];
+  printf("\n%'_'|30s %3s %3s %3s %5s %2s %2s %3s %3s %4s %3s\n",
+    "ITEM","ALC","DRI","FOO","VALUE","RT","DL","_HP","_SP","TEST","MAX");
+  for ( --si ; si>=0 ; si-- )
+  {
+    ident=liste[si];
+    minfo=deep_copy(menu_list[ident,PM_INFO]);
+
+    if ( (xinfo=adjust_info(ident,minfo,PL,PL)) && mappingp(xinfo) )
+      minfo+=xinfo;
+
+    minfo[P_VALUE]   = eval_anything(minfo[P_VALUE],            PL);
+    minfo[P_FOOD]    = eval_anything(minfo[P_FOOD],             PL);
+    minfo[P_ALCOHOL] = eval_anything(minfo[P_ALCOHOL],          PL);
+    minfo[P_DRINK]   = eval_anything(minfo[P_DRINK],            PL);
+    minfo[PM_DELAY_PUBMASTER]
+                     = eval_anything(menu_list[ident,PM_DELAY], PL);
+    minfo[PM_RATE_PUBMASTER]
+                     = eval_anything(menu_list[ident,PM_RATE],  PL);
+    minfo[P_HP]      = eval_anything(minfo[P_HP],               PL);
+    minfo[P_SP]      = eval_anything(minfo[P_SP],               PL);
+    erg=PUBMASTER->RegisterItem(menu_list[ident,0],minfo);
+    max=PUBMASTER->CalcMax(minfo,fn);
+
+    printf("%-'..'30.30s %3d %3d %3d %5d %2d %2d %3d %3d %|4s %3d\n",
+      menu_list[ident,PM_TEXT],
+      minfo[P_ALCOHOL], minfo[P_DRINK], minfo[P_FOOD],
+      minfo[P_VALUE],
+      minfo[PM_RATE_PUBMASTER],
+      minfo[PM_DELAY_PUBMASTER],
+      minfo[P_HP], minfo[P_SP],
+      ( erg ? "OK" : "FAIL" ),max);
+  }
+  write("Done.\n");
+  return 1;
+}
+
+void reset()
+{
+  if ( sum>0 )
+    ZENTRALBANK->PayIn(sum);
+  sum=0;
+  refresh_count++;
+  UpdateAvailability();
+}
+
+void add_gluehwein()
+{
+  if ( ctime(time())[4..6]=="Dec" )
+    AddToMenu( "Gluehwein",({"gluehwein"}),([
+      P_VALUE   : 80,
+      P_DRINK   :  5,
+      P_ALCOHOL : 20,
+      P_HP      : 15,
+      P_SP      : 15 ]),2,({
+      ("Du trinkst ein Glas Gluehwein, an dem Du Dir beinahe die Zunge "+
+       "verbrennst.\n"),
+      ("&& bestellt ein Glas Gluehwein und verbrennt sich beim\n"+
+       "Trinken beinahe die Zunge.\n") }), 0, 0, 0);
+}
+
+void add_std_drinks()
+{
+  if ( QueryProp(P_NO_STD_DRINK) )
+    return ;
+  add_gluehwein();
+}
+
+mapping query_menulist()
+{
+  return deep_copy(menu_list);
+}
+
+string *query_drinks()
+{
+  string *dr=({});
+  foreach( string ident, string menuetext, mapping minfo: menu_list) {
+    if (eval_anything(minfo[P_DRINK], 0))
+      dr += ({ ident });
+  }
+  return dr;
+}
+
+string *query_food()
+{
+  string *fo=({});
+  foreach( string ident, string menuetext, mapping minfo: menu_list) {
+    if (eval_anything(minfo[P_FOOD], 0))
+      fo += ({ ident });
+  }
+  return fo;
+}
diff --git a/std/room/restrictions.c b/std/room/restrictions.c
new file mode 100644
index 0000000..aa50c9e
--- /dev/null
+++ b/std/room/restrictions.c
@@ -0,0 +1,61 @@
+// MorgenGrauen MUDlib
+//
+// room/restrictions.c -- weight property handling for rooms
+//
+// $Id: restrictions.c 9020 2015-01-10 21:49:41Z Zesstra $
+
+inherit "std/container/restrictions";
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+
+static int _set_weight(int w);
+static int _query_weight();
+static int _set_total_weight(int w);
+static int _query_total_weight();
+
+void create()
+{
+  Set(P_WEIGHT,PROTECTED,F_MODE);
+  Set(P_TOTAL_WEIGHT,PROTECTED,F_MODE);
+}
+
+int MayAddWeight(int w)
+{
+  return 0;
+}
+
+int MayAddObject(object ob)
+{  return 1;  }
+
+int PreventInsert(object ob)
+{
+  return 0;
+}
+
+static int _set_weight(int w)
+{
+  return 0;
+}
+
+static int _query_weight()
+{
+  return 0;
+}
+
+static int _set_total_weight(int w)
+{
+  return 0;
+}
+
+static int _query_total_weight()
+{
+  return 0;
+}
diff --git a/std/room/shop.c b/std/room/shop.c
new file mode 100644
index 0000000..c429127
--- /dev/null
+++ b/std/room/shop.c
@@ -0,0 +1,1110 @@
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/trading_price";
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#include <thing/description.h>
+#undef NEED_PROTOTYPES
+
+#include <defines.h>
+#include <properties.h>
+#include <rooms.h>
+#include <language.h>
+#include <moving.h>
+#include <routingd.h>
+#include <bank.h>
+#include <combat.h>
+#include <input_to.h>
+#include <unit.h>
+#include <money.h>
+
+// TODO: pruefen, um die Variablen private sein koennen.
+
+// allgemein benoetige Variablen
+nosave string storage; // Filename des Stores in dem die Objekte lagern...
+nosave mapping ob_anz; // wie oft ist ein Objekt im Laden vorhanden?
+
+// Jetzt Variablen fuer staendig verfuegbare Objekte:
+nosave string *fixed_obj; // Liste der staendig verfuegbaren Objekte
+nosave mapping fixed_value; // Preise bei Sonderangeboten
+nosave mapping fixed_ids;   // Ids
+
+varargs void AddFixedObject(string str, int val, string|string* ids)
+{
+  int i;
+
+  // Achtung, bei solchen Objekten muss die Blueprint initialisiert werden!
+  if (!str) return;
+  if (!val) val=str->QueryProp(P_VALUE);
+  if (!ids)
+  {
+    if (str->QueryProp("u_ids")) // units haben keine P_IDS
+      ids=str->QueryProp("u_ids")[0];
+    else
+      ids=str->QueryProp(P_IDS);
+  }
+  if (!pointerp(ids))
+  {
+    if (stringp(ids))
+      ids=({ids});
+    else
+      ids=({});
+  }
+
+  fixed_obj += ({str});
+  fixed_value[str] = val;
+  // Alle IDs entfernen, die Sonderzeichen enthalten. Die koennte ein Spieler
+  // bei "kaufe" ohnehin nicht eingeben.
+  ids -= regexp(ids, "[\b\n\r\t]");
+  foreach(string id : ids)
+  {
+    // Nur IDs aufnehmen, die keine Grossbuchstaben enthalten, da Spieler
+    // diese ebenfalls nicht eingeben koennte.
+    if ( lowerstring(id) == id )
+      fixed_ids[id]=str;
+  }
+}
+
+void RemoveFixedObject(string filename)
+{
+  if( !stringp(filename) || !sizeof(fixed_obj))
+    return;
+  if( member(fixed_obj, filename)==-1 )
+    return;
+
+  fixed_obj -= ({ filename });
+  m_delete(fixed_value, filename);
+
+  foreach(string id, string file : fixed_ids)
+  {
+    if ( file == filename )
+      m_delete(fixed_ids, id);
+  }
+}
+
+static string SetStorageRoom(string str)
+{
+  if (str && stringp(str)) return storage=str;
+  return 0;
+}
+
+string QueryStorageRoom()
+{   return storage;   }
+
+protected void create()
+{
+  object router;
+  
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+
+  trading_price::create();
+
+  SetProp( P_NAME, "Haendler" );
+  SetProp( P_GENDER, MALE );
+  SetProp( P_ROOM_TYPE, QueryProp(P_ROOM_TYPE) | RT_SHOP );
+
+  AddCmd("zeige","list");
+  AddCmd(({"kauf","kaufe","bestell","bestelle"}),"buy");
+  AddCmd(({"verkauf","verkaufe","verk"}),"sell");
+  AddCmd(({"versetz","versetze"}),"force_sell");
+  AddCmd(({"schaetz","schaetze"}),"evaluate");
+  AddCmd(({"untersuche","unt"}), "show_obj");
+
+  SetTradingData(50000,300,3);
+
+  ob_anz=([]);
+  fixed_obj=({});fixed_value=([]);fixed_ids=([]);
+
+  AddFixedObject(BOERSE, 80,({ "boerse","geldboerse"}));
+
+  if (!clonep(ME) && objectp(router=find_object(ROUTER)))
+    router->RegisterTarget(TARGET_SHOP,object_name(ME));
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Legacy-Version von GetShopItems() fuer die erbenden Laeden, die auf
+// die Funktion in dieser Form angewiesen sind.
+static mixed *GetList()
+{
+  object store = load_object(storage);
+  store->_register_shop(ME);
+
+  mixed *output=({});
+  if (!objectp(store))
+    return output;
+
+  mixed tmp = map(fixed_obj, #'load_object)+all_inventory(store);
+  mapping tmp2 = ([]);
+  mixed str;
+  string comp;
+  int i;
+  int s=1;
+
+  for (i=sizeof(tmp)-1 ; i>=0 ; i--)
+  {
+    str = ({ ({ sprintf("%-25.25s%7.7d",
+                        (tmp[i]->short()||"???")[0..<3],
+                        QueryBuyValue(tmp[i], PL)),
+                tmp[i] }) });
+    comp=str[0][0][0..25];
+    if (!tmp2[comp])
+    {
+      tmp2[comp] = s++;
+      output += str;
+    }
+    else output[tmp2[comp]-1][0] = str[0][0];
+  }
+  return output;
+}
+
+// Legacy-Version von PrintList() fuer die erbenden Laeden, die auf
+// die Funktion in dieser Form angewiesen sind.
+static int DoList(string query_fun)
+{
+  mixed* output=GetList();
+  int si = sizeof(output);
+  if (!si)
+  {
+    write("Im Moment sind wir leider VOELLIG ausverkauft!\n");
+    return 1;
+  }
+  string out="";
+  int i;
+  int indent;
+  for (i=0; i<si; i++)
+  {
+    if (call_other(ME, query_fun, output[i][1]))
+    {
+      indent = !indent;
+      out += sprintf("%3d. %s", i+1, output[i][0]);
+      if (!indent)
+        out += "\n";
+      else out += " | ";
+    }
+  }
+  if (indent)
+    out+="\n";
+  PL->More(out);
+  return 1;
+}
+
+// Liefert eine Liste der Objekte zurueck, die gerade im Storage liegen,
+// pro Blueprint jeweils eins.
+protected object* GetShopItems()
+{
+  object store = load_object(storage);
+  store->_register_shop(ME);
+  object* output = ({});
+  object* all_items = all_inventory(store);
+
+  // Wir brauchen eine Liste, die von jeder Blueprint nur einen Clone
+  // enthaelt. Daher werden die Ladenamen der Objekte als Keys im Mapping
+  // <items> verwendet und jeweils der aktuelle Clone als Value zugeordnet.
+  mapping items = m_allocate(sizeof(all_items));
+  foreach(object ob: all_items)
+  {
+    items[load_name(ob)] = ob;
+  }
+  // Die Fixed Objects werden ans Ende angehaengt, damit in dem Fall, dass
+  // ein Clone eines solchen Objektes im Lager liegt, dieser zuerst verkauft
+  // wird und nicht immer wieder ein neuer erstellt wird.
+  return m_values(items) + map(fixed_obj, #'load_object);
+}
+
+#define LIST_LONG  1
+#define LIST_SHORT 0
+
+// Kuemmert sich um die Listenausgabe fuer den Befehl "zeige"
+varargs protected int PrintList(string filter_fun, int liststyle)
+{
+  // Alle Items im Lager holen. Wenn keine vorhanden, tut uns das leid.
+  object *items_in_store = GetShopItems();
+  if ( !sizeof(items_in_store) ) {
+    write("Im Moment sind wir leider VOELLIG ausverkauft!\n");
+    return 1;
+  }
+
+  // Das Listenformat ist von der Spielereingabe abhaengig. Wurde "lang"
+  // angefordert, geben wir einspaltig aus mit groesserer Spaltenbreite.
+  // Die Spaltenbreite wird dabei von dem Item mit dem laengsten Namen im
+  // gesamten Shop-Inventar bestimmt, damit nicht bei jeder Teilliste
+  // (Waffen, Ruestungen, Verschiedenes) unterschiedliche Breiten rauskommen.
+  //
+  // Der erste Parameter enthaelt die Katalognummer des Items, der zweite
+  // die Kurzbeschreibung, der dritte den Preis.
+  string listformat = "%3d. %-25.25s %6.6d";
+  if ( liststyle == LIST_LONG )
+  {
+    string* names = sort_array(
+                      items_in_store->short(),
+                      function int (string s1, string s2) {
+                        return (sizeof(s1) < sizeof(s2));
+                      });
+    // Wenn einspaltig ausgegeben wird, soll die Liste nicht beliebig breit
+    // werden. Daher wird die Short auf 65 Zeichen limitiert.
+    int len = 65;
+    if ( sizeof(names) )
+      len = min(len, sizeof(names[0]));
+    listformat = "%3d. %-"+len+"."+len+"s %6.6d";
+  }
+
+  string out="";
+  // Variablen, die innerhalb der Schleife benoetigt werden.
+  string kurz;
+  int indent, preis;
+  object item;
+  // Ueber die Liste laufen. <i> wird benoetigt, um die ausgegebene Liste
+  // konsistent numerieren zu koennen, damit kaufe <nr> funktioniert.
+  foreach(int i : sizeof(items_in_store))
+  {
+    item = items_in_store[i];
+    if ( call_other(ME, filter_fun, item) )
+    {
+      // Kurzbeschreibung und Preis ermitteln. Items ohne Short werden
+      // als "?" angezeigt.
+      kurz = (item->short() || "???")[0..<3];
+      preis = QueryBuyValue(item, PL);
+      // Beschreibung des Items anfuegen.
+      out += sprintf(listformat, i+1, kurz, preis);
+      indent = !indent;
+      // Wenn indent gesetzt ist, handelt es sich um das linke Item in der
+      // zweispaltigen Liste, dann fuegen wir einen Spaltentrenner an,
+      // ansonsten ist es das rechte, dann brechen wir um.
+      // Gilt natuerlich nur fuer kurze Listen.
+      out += ((indent && liststyle==LIST_SHORT)? " | " : "\n");
+    }
+  }
+  // Wenn die Liste eine ungerade Zahl Items enthaelt, ist in der letzten
+  // Zeile links ein Item aufgefuehrt, daher muss danach umbrochen werden.
+  // Gilt natuerlich nur fuer kurze Listen
+  if (indent && liststyle==LIST_SHORT)
+    out+="\n";
+
+  // Vor den Listen wird eine Info-Zeile ausgegeben, um gefilterte Listen
+  // kenntlich zu machen. Wird nach der Filterung des Inventars erzeugt,
+  // um eine leere Meldung ausgeben zu koennen, wenn nach Filterung nichts
+  // mehr uebrigbleibt.
+  string was;
+  switch(filter_fun)
+  {
+    case "IsArmour": was = "Ruestungen"; break;
+    case "IsWeapon": was = "Waffen"; break;
+    case "NoWeaponNoArmour":
+         was = (out==""?"sonstigen Waren":"sonstige Waren"); break;
+    default: was = "Waren"; break;
+  }
+  // <out> ist ein Leerstring, wenn keine Waren da sind, die dem Filterkri-
+  // terium entsprechen. Dann gibt's eine entsprechende Meldung.
+  if ( out == "" )
+    out = sprintf("Leider sind momentan keine %s im Angebot.\n", was);
+  else
+    out = sprintf("Folgende %s kannst Du hier kaufen:\n",was) + out;
+
+  PL->More(out);
+  return 1;
+}
+
+// Hilfsfunktionen zum Filtern des Ladeninventars fuer den "zeige"-Befehl
+static int AlwaysTrue(object ob)
+{   return 1;   }
+
+static string IsWeapon(object ob)
+{  return ob->QueryProp(P_WEAPON_TYPE);  }
+
+static string IsArmour(object ob)
+{  return ob->QueryProp(P_ARMOUR_TYPE);  }
+
+static int NoWeaponNoArmour(object ob)
+{ return (!ob->QueryProp(P_WEAPON_TYPE) && !ob->QueryProp(P_ARMOUR_TYPE)); }
+
+
+// Diese Funktion ist oeffentlich, falls Magier abfragen wollen, ob ein laden
+// ein Objekt zerstoeren wuerde. Aber: Benutzung auf eigenes Risiko! Es wird
+// nicht garantiert, dass diese Funktion bzw. ihr Interface sich nicht
+// aendert.
+public int CheckForDestruct(object ob) // Pruefen, ob zerstoert werden soll
+{
+  string str;
+  /*
+   * P_NOBUY - Objekte auf jeden Fall zerstoeren
+   */
+  if(ob->QueryProp(P_NOBUY)) return 1;
+  /*
+   * Beschaedigte Objekte werden ebenfalls zerstoert
+   */
+  if(ob->QueryProp(P_DAMAGED)) return 1;
+  /*
+   * Ruestungen wenn sie a) eine DefendFunc definiert haben oder
+   *                     b) ueber der in KEEP_ARMOUR_CLASS definierten AC
+   *                        liegen (siehe /sys/combat.h)
+   */
+  if(str = IsArmour(ob))
+  {
+    if(objectp(ob->QueryProp(P_DEFEND_FUNC))) return 1;
+    if(ob->QueryProp(P_AC) >= KEEP_ARMOUR_CLASS[str]) return 1;
+    return 0;
+  }
+  /*
+   * Waffen wenn sie a) 1-haendig sind und eine WC > 120 besitzen oder
+   *                 b) 2-haendig sind und eine WC > 150 besitzen oder aber
+   *                 c) eine HitFunc definiert haben
+   */
+  if(str = IsWeapon(ob))
+  {
+    if(ob->QueryProp(P_NR_HANDS) > 1 && ob->QueryProp(P_WC) > 150) return 1;
+    if(ob->QueryProp(P_NR_HANDS) < 2 && ob->QueryProp(P_WC) > 120) return 1;
+    if(objectp(ob->QueryProp(P_HIT_FUNC))) return 1;
+    return 0;
+  }
+  return 0;
+}
+
+static int list(string str)
+{
+  _notify_fail(
+    "Bitte 'zeige', 'zeige waffen', 'zeige ruestungen' oder\n"
+    "'zeige verschiedenes' eingeben. Wenn Du das Schluesselwort 'lang'\n"
+    "oder '-1' anhaengst, wird die Liste einspaltig ausgegeben.\n");
+
+  if (!stringp(str) || !sizeof(str) )
+    return PrintList("AlwaysTrue");
+  if ( str == "lang" || str == "-1" )
+    return PrintList("AlwaysTrue", LIST_LONG);
+
+  string *params = explode(str," ");
+  if (sizeof(params[0])<3)
+    return 0;
+
+  int liststyle = LIST_SHORT;
+  if ( sizeof(params)>1 && params[1] == "lang" )
+    liststyle = LIST_LONG;
+
+  str=params[0][0..2];
+  if (str=="waf")
+    return PrintList("IsWeapon", liststyle);
+  if (str=="ver")
+    return PrintList("NoWeaponNoArmour", liststyle);
+  if (str=="rue")
+    return PrintList("IsArmour", liststyle);
+  return 0;
+}
+/*
+static varargs int QueryBuyValue(mixed ob, object client)
+{
+  if (objectp(ob))
+    return trading_price::QueryBuyValue(ob, client);
+  return (fixed_value[ob]*QueryBuyFact(client))/100;
+}
+*/
+
+static varargs int QueryBuyValue(object ob, object client)
+{
+  int fprice = fixed_value[load_name(ob)];
+
+  return (fprice>0) ? 
+         fprice*QueryBuyFact(client)/100 : 
+         trading_price::QueryBuyValue(ob, client);
+}
+
+static void UpdateCounter(object ob, int num)
+{
+  string tmp;
+
+  if (!num || !objectp(ob)) return;
+  tmp=BLUE_NAME(ob);
+  if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/")
+    tmp=ob->short()+tmp;
+  ob_anz[tmp] += num;
+  if (ob_anz[tmp] <= 0)
+    m_delete(ob_anz,tmp);
+}
+
+protected object FindInStore(string|int x)
+{
+  object* list = GetShopItems();
+  if ( intp(x) && x>0 && x<=sizeof(list) ) {
+    return list[x-1];
+  }
+  if (stringp(x))
+  {
+    if ( fixed_ids[x] )
+      return load_object(fixed_ids[x]);
+    list = filter_objects(list, "id", x);
+    if ( sizeof(list) )
+      return list[0];
+    // Wenn nix im Store gefunden (das schliesst eigentlicht schon die BPs der
+    // fixen Objekte ein, aber nicht, wenn diese nicht konfiguriert sind. D.h.
+    // diese Pruefung ist fuer nicht-konfigurierte BPs), Liste der
+    // FixedObjects pruefen unde so die eventuell manuell in
+    // AddFixedObject() angegebene Liste von IDs beruecksichtigen.
+    else if ( fixed_ids[x] )
+      return load_object(fixed_ids[x]);
+  }
+  return 0;
+}
+
+static string buy_obj(mixed ob, int short)
+{ return 0; }
+
+private void really_buy(int val, object pl, object ob, int u_req)
+{
+  // Staendig verfuegbare Objekte (fixed_obj) sind daran erkennbar, dass sie
+  // nicht im Lager liegen. Daher hier einen Clone erstellen, der dann 
+  // stattdessen rausgegeben wird.
+  if ( !present(ob, find_object(storage)) )
+    ob = clone_object(ob);
+
+  // In Unitobjekten U_REQ (wieder) setzen (wegen input_to (bei dem sich das
+  // Kommandoverb aendert und deswegen U_REQ geloescht wird), und wegen
+  // Kaufens von Fixed-Objekt-Unitobjekten (bei diesen muss U_REQ _nach_ dem
+  // Clonen im Clone gesetzt werden, nicht automagisch in der BP durch den
+  // Aufruf von id() weiter vorher).
+  if (u_req>0)
+  {
+    // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
+    // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
+    // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
+    // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+    // werden...
+      ob->QueryProp(U_REQ);
+      ob->SetProp(U_REQ, u_req);
+  }
+
+  pl->AddMoney(-val);
+  _add_money(val);
+
+  if (ob->move(pl,M_GET) != MOVE_OK) // Kann der Spieler das Objekt tragen?
+  {
+    write(break_string("Du kannst "+ob->name(WEN,1)+" nicht mehr tragen. "
+     "Ich lege "+ob->QueryPronoun(WEN)+" hier auf den Boden.",78,
+     Name(WER)+" sagt: "));
+    ob->move(ME,M_NOCHECK);                         // Nein :-)
+  }
+  else
+  {
+    // Falls das Objekt sich vereinigt hat, muss jetzt wieder U_REQ
+    // restauriert werden.
+    ob->SetProp(U_REQ, u_req);
+    write(break_string("Du kaufst "+ob->name(WEN,1)+".", 78));
+  }
+
+  say(break_string(PL->Name(WER)+" kauft "+ob->name(WEN)+".",78), ({PL}));
+  UpdateCounter(ob,-1);
+}
+
+static void ask_buy(string str, int val, object pl, object ob, int u_req)
+{
+  _notify_fail(break_string("Gut, Du kannst es Dir ja jederzeit "
+   "noch anders ueberlegen.",78,Name(WER)+" sagt: "));
+
+  if(!str || !stringp(str) || str == "nein" || str == "n")
+  {
+    return;
+  }
+  if(str != "ja" && str != "j")
+  {
+    return;
+  }
+  really_buy(val, pl, ob, u_req);
+}
+
+static int buy(string str)
+{
+  int i, val, par, dex;
+  mixed ob, res;
+  string dummy;
+
+  if (!str) {
+    write("Was willst Du kaufen?\n");
+    return 1;
+  }
+
+  if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
+    write("Es ist niemand da, der Dich bedienen koennte.\n");
+    return 1;
+  }
+
+  _notify_fail(break_string("Das kann ich in meinem Lager nicht finden.",78,
+   Name(WER)+" sagt: "));
+
+  // Um auch Teile von Unit-Stacks kaufen zu koennen, z.B. "kaufe 5 phiolen",
+  // darf hier zusaetzlich <dummy> nur ein Leerstring sein, sonst verzweigt
+  // die Syntaxpruefung hierhin und es wird das 5. Item der Liste gekauft.
+  if (sscanf(str,"%d%s",i,dummy)>0 && i>0 && !sizeof(dummy)) {
+    ob=FindInStore(i);
+  }
+  else ob=FindInStore(str);
+
+  if (!ob) return 0;
+
+  if (str = buy_obj(ob,0)) 
+  {
+    write(break_string(str,78,Name(WER)+" sagt: "));
+    return 1;
+  }
+
+  val = QueryBuyValue(ob,PL);
+
+  if (PL->QueryMoney() < val)
+  {
+    write(break_string(capitalize(ob->QueryPronoun(WER))+" wuerde "+val+
+     " Muenzen kosten, und Du hast nur "+PL->QueryMoney()+".",78,
+     Name(WER)+" bedauert: "));
+    return 1;
+  }
+
+  // Anzahl der angeforderten Einheiten vor dem Bewegen zwischenspeichern.
+  // Weil dabei im Fall von Units eine Vereinigung mit bereits im Inventar
+  // befindlichen Einheiten stattfindet, muss das ggf. nach Bewegen
+  // zurueckgesetzt werden.
+  int u_req = ob->QueryProp(U_REQ);
+
+  if ((res = ob->QueryProp(P_RESTRICTIONS)) && mappingp(res) &&
+      (res = call_other("/std/restriction_checker",
+                        "check_restrictions",PL,res)) &&
+      stringp(res))
+  {
+    _notify_fail(break_string("Du koenntest "+ob->name(WEN,2)+" nicht "
+     "verwenden. Grund: "+res+"Moechtest Du "+ob->QueryPronoun(WEN)+
+     " dennoch kaufen?",78,Name(WER)+" sagt: "));
+
+    input_to("ask_buy",INPUT_PROMPT, "(ja/nein) ", val,PL,ob,u_req);
+    return 0;
+  }
+
+  par = (int)ob->QueryProp(P_PARRY);
+  dex = (int)PL->QueryAttribute(A_DEX);
+
+  if ((((par < PARRY_ONLY) && ((dex + 8) * 10) < ob->QueryProp(P_WC)) ||
+       ((par > PARRY_NOT)  && ((dex + 5) *  2) < ob->QueryProp(P_AC))) &&
+      VALID_WEAPON_TYPE(ob))
+  {
+    _notify_fail(break_string("Du koenntest "+ob->name(WEN,2)+" nicht "
+     "zuecken, da Deine Geschicklichkeit dafuer nicht ausreicht. Moechtest "
+     "Du "+ob->QueryPronoun(WEN)+" dennoch kaufen?",78,
+     Name(WER)+" sagt: "));
+
+    input_to("ask_buy",INPUT_PROMPT, "(ja/nein) ", val,PL,ob,u_req);
+    return 0;
+  }
+
+  really_buy(val, PL, ob, u_req);
+
+  return 1;
+}
+
+private void give_money(int value)
+// Geld gutschreiben...
+{
+  if (!value) return;
+  write(break_string(Name(WER, 1)+" zahlt Dir "+value+" Goldstueck"
+                    +(value==1?".":"e."), 78));
+  if ((PL->AddMoney(value))<=0) {
+     object mon;
+
+     write("Du kannst das Geld nicht mehr tragen!\n");
+     mon=clone_object(GELD);
+     mon->SetProp(P_AMOUNT,value);
+     mon->move(ME,M_MOVE_ALL|M_NOCHECK);
+  }
+}
+
+static int make_to_money(object ob, int value)
+// Interne Funktion, die ob versucht in das Lager zu ueberfuehren und das
+// Geld das dabei fuer den Spieler abfaellt zurueckliefert.
+{
+  string str;
+  int ret;
+
+  if (!objectp(ob) || environment(ob)==find_object(storage)) {
+    write(break_string(Name(WER, 1)+" wundert sich ueber Dich.", 78));
+    return 0;
+  }
+  if (value>QueryProp(P_CURRENT_MONEY)) {
+    write(break_string("Ich hab das Geld leider nicht mehr.", 78,
+                       Name(WER, 1)+" sagt: "));
+    return 0;
+  }
+  // U_REQ merken, falls sich Objekte vereinigen. Sonst stimmt nicht nur der
+  // Name, sondern es werden ggf. auch die falsche Anzahl zerstoert.
+  //TOOO: Oder Units entsorgen und als Feature deklarieren?
+  int req = ob->QueryProp(U_REQ);
+  if (CheckForDestruct(ob) > 0)  // soll ob zerstoert werden?
+  {
+    ret = ob->move(storage,M_PUT|M_GET);
+    // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
+    // Einheiten restauriert werden.
+    // Problem: falls das verkaufte Objekt Units hat, beschaedigt ist und sich
+    // vereinigt hat, sind jetzt leider alle Einheiten im Lager beschaedigt.
+    // Das ist unschoen - aber mir jetzt zuviel AUfwand, das korrekt zu bauen,
+    // weil es nur sehr selten vorkommt. (Hint: separater Muellraum)
+    ob->SetProp(U_REQ, req);
+    if (ret > 0) // Sonst werden auch Sachen zerstoert, die man nicht
+    {                 // weglegen kann
+      say(break_string(PL->Name()+" verkauft "+ob->name(WEN)+".", 78));
+      if(ob->QueryProp(P_DAMAGED))  // Andere Meldung bei beschaedigten
+      {                             // Objekten ...
+        write(break_string(Name(WER,1)+" findet zwar keinen Gefallen an "
+         +ob->name(WEM,1)+", nimmt "+ob->QueryPronoun(WEN)+" Dir zuliebe "
+         "aber trotzdem.",78));
+      }
+      else
+      {
+        write(break_string(Name(WER, 1)+" findet Gefallen an "
+           +ob->name(WEM, 1) + " und legt "+ob->QueryPronoun(WEN)
+           +" zu "+(QueryProp(P_GENDER)==FEMALE?"ihren":"seinen")
+           +" Privatsachen.", 78));
+      }
+      /* Er zahlt Dir "+value+" Muenze"+(value==1?"":"n")+" dafuer.", 78)); */
+      _add_money(-value);
+      _add_money(value*QueryProp(P_SHOP_PERCENT_LEFT)/100); // Wegen Zerstoerung des Objektes
+      UpdateCounter(ob,1);
+      ob->remove(1);
+      return value;
+    }
+    else if (ret == ME_CANT_BE_DROPPED) {
+      if ((str=ob->QueryProp(P_NODROP)) && stringp(str)) {
+        write(str);
+        return 0;
+      }
+      write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+      return 0;
+    }
+    else
+      write(break_string(ob->Name(WER)+" interessiert mich nicht.", 78,
+               Name(WER, 1)+" sagt: "));
+  }
+  else if ((ret=(ob->move(storage,M_PUT|M_GET)))>0)
+  {
+    // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
+    // Einheiten restauriert werden.
+    ob->SetProp(U_REQ, req);
+    say(break_string(PL->Name(WER)+" verkauft "+ob->name(WEN)+".", 78));
+    _add_money(-value);
+    UpdateCounter(ob,1);
+    return value;
+  }
+  else if (ret == ME_CANT_BE_DROPPED) {
+    if ((str=ob->QueryProp(P_NODROP)) && stringp(str))
+       write(str);
+    else write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+  }
+  else write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+  return 0;
+}
+
+static void ask_sell(string str, object ob, int val, int u_req)
+// Wenn ein einzelnen Stueck unter Wert verkauft werden soll, wird nachgefragt
+// u_req ist bei Unitobjekten die Anzahl an zu verkaufenden Einheiten. Bei
+// normalen Objekten ist u_req 0.
+{
+  str=lower_case(str||"");
+  if (str=="ja"||str=="j")
+  {
+     // In Unitobjekten U_REQ (wieder) setzen.
+     if (u_req>0)
+     {
+       // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
+       // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
+       // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
+       // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+       // werden...
+         ob->QueryProp(U_REQ);
+         ob->SetProp(U_REQ, u_req);
+     }
+     give_money(make_to_money(ob,val));
+  }
+  else
+     write(break_string("Ok, dann behalts!", 78,
+             Name(WER, 1)+" sagt: "));
+}
+
+static string sell_obj(object ob, int short)
+// Ist der Haendler bereit ob zu kaufen? wenn nein, Rueckgabe einer Meldung,
+// die der Haendler sagen soll.
+{  mixed nosell;
+
+   if (BLUE_NAME(ob)==GELD)
+      return "Das waere ja wohl Unsinn, oder ...?";
+   if (nosell=ob->QueryProp(P_NOSELL))
+   {
+     if (stringp(nosell))
+       return nosell;
+     return ("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!");
+   }
+   if (ob->QueryProp(P_CURSED))
+     return ob->Name(WER,1)
+         +" ist mir irgendwie ungeheuer! Das kannst Du nicht verkaufen!";
+   // man sollte keine COntainer mit Inhalt verkaufen koennen, ggf. kauft sie
+   // dann ein anderer Spieler.
+   if (first_inventory(ob))
+   {
+     return ob->Name(WER, 1) + " ist nicht leer!";
+   }
+   return 0;
+}
+
+static varargs int sell(string str, int f)
+{
+  int i, val, oval, tmp;
+  object *obs;
+
+  if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER),ME)) {
+     write("Es ist niemand da, der Dich bedienen koennte.\n");
+     return 1;
+  }
+  
+  if (!str) {
+     notify_fail("Was moechtest Du denn verkaufen?\n");
+     return 0;
+  }
+  
+  /* Ergebnis von find_obs() sollte unifiziert werden, damit ein mehrfach
+     gefundenes Objekt nicht mehrfach versucht wird zu verkaufen.
+     Beispiel: Objekt hat P_NOBUY gesetzt und mehrere IDs gesetzt. Wenn
+     ein Spieler es mit "verkaufe ID1 und ID2" versucht zu verkaufen,
+     wuerde das einen Bug ausloesen. Derselbe Bug entsteht, wenn man mit
+     "verkaufe ID1 und ID1" verkauft. */
+  obs = PL->find_obs(str, PUT_GET_DROP);
+  /* Erst im Inventar schauen, dann im Environment. find_obs() ohne 2.
+     Parameter macht das standardmaessig andersherum.
+     TODO: Aenderung ueberpruefen, sobald das neue put_and_get.c 
+     eingebaut wurde. */
+  if ( !sizeof(obs) )
+    obs = PL->find_obs(str, PUT_GET_TAKE) || ({});
+  obs = m_indices(mkmapping(obs));
+  if (!i=sizeof(obs)) {
+     notify_fail("Was moechtest Du denn verkaufen?\n");
+     return 0;
+  }
+  call_other(storage, "_register_shop", ME);
+  if (i==1) {
+     if (str=sell_obj(obs[0], 0)) {
+        write(break_string(str, 78, Name(WER, 1)+" sagt: "));
+        return 1;
+     }
+     if ((oval=obs[0]->QueryProp(P_VALUE))<=0) {
+        write(break_string(obs[0]->Name(WER)
+              +(obs[0]->QueryProp(P_PLURAL) ? " haben" : " hat")
+              +" keinen materiellen Wert, tut mir leid.", 78,
+              Name(WER, 1)+" sagt: "));
+        return 1;
+     }
+     val=QuerySellValue(obs[0], PL);
+     if (!val) {
+        write(break_string("Ich bin absolut pleite. Tut mir aufrichtig leid.",
+              78, Name(WER, 1)+" sagt: "));
+        return 1;
+     }
+     if (val==oval || f) {
+        give_money(make_to_money(obs[0], val));
+        return 1;
+     }
+     if (str=obs[0]->QueryProp(P_NODROP)) {
+        if (stringp(str))
+           write(str);
+        else write(break_string("Du kannst "+obs[0]->name(WEN,1)
+                               +" nicht verkaufen!", 78));
+        return 1;
+     }
+
+     if (obs[0]->QueryProp(P_DAMAGED))  // Bei beschaedigten Objekten gibt
+     {                                  // es auch hier eine andere Meldung
+       write(break_string("Da "+obs[0]->name(WER)+" beschaedigt "
+        +(obs[0]->QueryProp(P_PLURAL)?"sind":"ist")+", kann ich Dir "
+        "nur "+val+" Muenze"+(val == 1?"":"n")+" dafuer bieten. Und "
+        "damit mache ich noch Verlust! Nimmst Du mein Angebot an? "
+        "(ja/nein)",78,Name(WER,1)+" sagt: "));
+     }
+     else                              // Default
+     {
+       write(break_string(Name(WER, 1)+" sagt: "
+          +"Nach der aktuellen Marktlage kann ich Dir dafuer nur "
+          +val+" Muenze"+(val==1?"":"n")+" bieten, obwohl "
+          +obs[0]->name(WER)+" eigentlich "+oval+" Muenze"
+          +(oval==1?"":"n")+" wert waere. Willst Du "
+          +(QueryProp(P_PLURAL) ? "sie" : "es")
+          +" mir dafuer ueberlassen?", 78));
+     }
+     // in ask_sell() gibt es das query_verb() nicht mehr, weswegen U_REQ in
+     // Unitobjekten zurueckgesetzt wird. Damit geht die info verloren,
+     // wieviele Objekte der Spieler angegeben hat. Diese muss gerettet und
+     // via ask_sell() in make_to_money() ankommen. In normalen Objekten ist
+     // U_REQ 0.
+     input_to("ask_sell",INPUT_PROMPT, "(ja/nein) ",obs[0], val,
+              (obs[0])->QueryProp(U_REQ) );
+     return 1;
+  }
+  for (--i; i>=0 && get_eval_cost()>50000; i--) {
+     if (oval=obs[i]->QueryProp(P_VALUE)) {
+        if (obs[i]->QueryProp(P_KEEP_ON_SELL)==getuid(PL)
+            || obs[i]->QueryProp(P_WORN) || obs[i]->QueryProp(P_WIELDED))
+           write(break_string(obs[i]->Name(WER)+": Du behaeltst "
+                +obs[i]->name(WEN)+".", 78));
+        else if (str=sell_obj(obs[i], 1))
+           write(break_string(obs[i]->Name(WER)+": "+str, 78));
+        else {
+           tmp=QuerySellValue(obs[i], PL);
+           if (!tmp) {
+              write(break_string(
+                    "Ich bin absolut pleite. Tut mir aufrichtig leid.", 78,
+                    Name(WER, 1)+" sagt: "));
+              break;
+           }
+           else if (!f && tmp*10<oval)
+              write(break_string(obs[i]->Name(WER)+": "+Name(WER, 1)
+                    +" bietet Dir aber nur "+tmp+" Goldstueck"
+                    +(tmp>1 ? "e" : "")+" dafuer.", 78));
+           else {
+              str=(obs[i]->Name(WER));
+              if (tmp=make_to_money(obs[i], tmp)) {
+                 write(break_string(str+": "+Name(WER, 1)
+                      +" gibt Dir dafuer "+tmp+" Goldstueck"
+                      +(tmp==1?".":"e."), 78));
+                 val+=tmp;
+              }
+           }
+        }
+     }
+  }
+  if (!val)
+     write(break_string("Hmmm, Du hast aber nicht besonders viel zu bieten...",
+                        78, Name(WER)+" sagt: "));
+  else give_money(val);
+  return 1;
+}
+
+static int force_sell(string str)
+{  return sell(str, 1);  }
+
+static int evaluate(string str)
+{
+  object ob;
+  int val,rval;
+
+  if (!str) return 0;
+  if(stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
+    write("Es ist niemand da, der Dich bedienen koennte.\n");
+    return 1;
+  }
+
+  ob=present(str, ME);
+  if (!ob) ob=deep_present(str,PL);
+  if (!ob) {
+    write("Hm? "+capitalize(str)+"? Wovon redest Du?\n");
+    return 1;
+  }
+  if (living(ob)) {
+    _notify_fail("Nanu, seit wann werden hier Lebewesen verkauft?\n");
+    return 0;
+  }
+  if (str=sell_obj(ob, 0)) {
+    write(break_string(str, 78, Name(WER)+" sagt: "));
+    return 1;
+  }
+  rval=ob->QueryProp(P_VALUE);
+  if (rval) {
+    val=QuerySellValue(ob, PL);
+    if (rval==val) {
+      tell_object(this_player(),break_string(
+         "Naja, ich denke, " +val+ " Muenze"
+         + (val==1 ? "" : "n")
+         + " waere"+(ob->QueryProp(P_AMOUNT)>1?"n ":" ")
+         + (ob->QueryPronoun(WER))+" schon wert.\n",78));
+    }
+    else if (val) {
+        tell_object(this_player(),break_string(
+          "Oh, nach der aktuellen Marktlage kann ich nur "+val+" Muenze"+
+          (val==1?"":"n")+" bezahlen, obwohl "
+          + (QueryProp(P_PLURAL) ? "sie" : "es")
+          + " eigentlich "+rval
+          + " Muenze"+(rval==1?"":"n")+" wert ist.\n",78));
+    }
+    else write("Ich bin vollkommen pleite. Tut mir leid.\n");
+  }
+  else write("Das ist voellig wertlos.\n");
+  return 1;
+}
+
+static int show_obj(string str)
+{
+  int i;
+  string was;
+  mixed ob;
+
+  if (!str) return 0;
+  if (sscanf(str,"%s im laden",was)>0 || sscanf(str,"%s in laden",was)>0) {
+    _notify_fail("Das kann ich im Lager nicht finden.\n");
+    ob=FindInStore(was);
+  } else if (sscanf(str,"%d",i)>0 && i>0) {
+    _notify_fail("Das kann ich im Lager nicht finden.\n");
+    ob=FindInStore(i);
+  }
+  if (!ob) return 0;
+  write(ob->Name(WER)+":\n"+ob->long()+capitalize(ob->QueryPronoun())
+       +" kostet "+QueryBuyValue(ob,PL)+" Muenzen.\n");
+  return 1;
+}
+
+// benutzt von trading_price::QueryValue(object, int, object)
+static int ObjectCount(object ob)
+{
+  string tmp;
+
+  if (!objectp(ob)) return 0;
+  tmp = BLUE_NAME(ob);
+  if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/") tmp=ob->short()+tmp;
+  return ob_anz[tmp];
+}
+
+// benutzt von trading_price::QuerySellValue(object, object)
+static varargs int QueryValue(object ob, int value, object client)
+{
+  int new_value, mymoney;
+
+  if (!objectp(ob)) return 0;
+  if (Query("laden::compat")) {
+    new_value=(value>1000?1000:value);
+    mymoney = QueryProp(P_CURRENT_MONEY);
+    if (new_value>mymoney)
+      return (mymoney>0?mymoney:0);
+    else return new_value;
+  }
+  return ::QueryValue(ob, value, client);
+}
+
+void reset()
+{
+  mixed *keys;
+  int i;
+
+  trading_price::reset();
+
+  if (!mappingp(ob_anz))
+    return;
+  keys=m_indices(ob_anz);
+  for (i=sizeof(keys)-1;i>=0;i--) {
+    ob_anz[keys[i]]=ob_anz[keys[i]]*7/8;
+    if (!ob_anz[keys[i]])
+       m_delete(ob_anz,keys[i]);
+  }
+}
+
+varargs int CheckFindRestrictions(object ob, mixed restr, closure qp) {
+  return 0;
+}
+
+int EvalWeapon(object ob, closure qp) {
+  int wc,val;
+
+  wc=funcall(qp,P_WC);
+  val=funcall(qp,P_EFFECTIVE_WC);
+  if (val>wc) wc=val;
+  return wc;
+}
+
+varargs object FindBestWeapon(mixed type, int maxmon, int maxw, int hands,
+                              int bestwc, mixed restr) {
+  object bestob,ob;
+  string otype;
+  int wc,bestval,val,w,bestw;
+  closure qp;
+
+  if (!stringp(storage) || !objectp(ob=find_object(storage))) return 0;
+  if (!maxmon) maxmon=100000;
+  if (!maxw) maxw=75000;
+  if (!hands) hands=2;
+  if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
+  if (type && !pointerp(type) && !mappingp(type)) type=({type});
+
+  for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
+    qp=symbol_function("QueryProp",ob);
+    if (!otype=funcall(qp,P_WEAPON_TYPE)) continue;
+    if (type && member(type,otype)<0) continue;
+    wc=EvalWeapon(ob,qp);
+    if (wc<bestwc) continue;
+    if (funcall(qp,P_NR_HANDS)>hands) continue;
+    w=funcall(qp,P_WEIGHT);
+    if (w>maxw) continue;
+    val=funcall(qp,P_VALUE);
+    if (val>maxmon) continue;
+    if (bestob && wc<=bestwc) {
+      if (val>bestval) continue;
+      else if (val==bestval && w>bestw) continue;
+    }
+    if (val>bestval && bestob && wc<=bestwc) continue;
+    if (CheckFindRestrictions(ob,restr,qp)) continue;
+    bestob=ob;
+    bestwc=wc;
+    bestval=val;
+    bestw=w;
+  }
+  return bestob;
+}
+
+int EvalArmour(object ob,closure qp) {
+  int ac,val;
+
+  ac=funcall(qp,P_AC);
+  val=funcall(qp,P_EFFECTIVE_AC);
+  if (val>ac) ac=val;
+  return ac;
+}
+
+varargs mapping FindBestArmoursT(mixed type, int maxmon, int maxw,
+                                 mapping bestac, mixed restr) {
+  object ob;
+  string otype;
+  int ac,val,sum,w,wsum;
+  mapping bestob,bestval,bestw;
+  closure qp;
+
+  if (!stringp(storage) || !objectp(ob=find_object(storage))) return ([]);
+  if (!maxmon) maxmon=100000;
+  if (!maxw) maxw=75000;
+  if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
+  if (type && !pointerp(type) && !mappingp(type)) type=({type});
+  if (!mappingp(bestac)) bestac=([]);
+  bestob=([]);bestval=([]);bestw=([]);
+
+  for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
+    qp=symbol_function("QueryProp",ob);
+    if (!otype=funcall(qp,P_ARMOUR_TYPE)) continue;
+    if (type && member(type,otype)<0) continue;
+    ac=EvalArmour(ob,qp);
+    if (ac<bestac[otype]) continue;
+    w=funcall(qp,P_WEIGHT);
+    if (wsum-bestw[otype]+w>maxw) continue;
+    val=funcall(qp,P_VALUE);
+    if (sum-bestval[otype]+val>maxmon) continue;
+    if (bestob[otype] && ac<=bestac[otype]) {
+      if (val>bestval[otype]) continue;
+      else if (val==bestval[otype] && w>bestw[otype]) continue;
+    }
+    if (CheckFindRestrictions(ob,restr,qp)) continue;
+    sum=sum-bestval[otype]+val;
+    wsum=wsum-bestw[otype]+w;
+    bestob[otype]=ob;
+    bestac[otype]=ac;
+    bestval[otype]=val;
+    bestw[otype]=w;
+  }
+  return bestob;
+}
+
+varargs object *FindBestArmours(mixed type, int maxmon, int maxw,
+                                                mapping bestac, mixed restr) {
+  return m_values(FindBestArmoursT(type,maxmon,maxw,bestac,restr));
+}
diff --git a/std/secure_thing.c b/std/secure_thing.c
new file mode 100644
index 0000000..7219529
--- /dev/null
+++ b/std/secure_thing.c
@@ -0,0 +1,34 @@
+/*
+ * thing.c fuer Objekte, die sicherheitsrelevant sind.
+ * Sollte von Dingen aus /secure oder Magiertools auf jeden
+ * Fall statt /std/thing inherited werden.
+ *
+ * Der Unterschied zu /std/thing besteht darin, potentiell unsichere
+ * Auswertungen wie process_string() (@@fun@@) zu verhindern - ansonsten
+ * koennte jemand Magiern Code unterschieben.
+ *
+ */
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/thing";
+#include <properties.h>
+
+// int secure_level() // ist nun in simul_efun
+
+varargs string long(int mode)
+{
+  return funcall(QueryProp(P_LONG));
+}
+
+string short()
+{
+  string sh;
+  if( sh=QueryProp(P_SHORT) )
+    return funcall(sh)+".\n";
+}
+
diff --git a/std/shells/baum.c b/std/shells/baum.c
new file mode 100644
index 0000000..522f3fe
--- /dev/null
+++ b/std/shells/baum.c
@@ -0,0 +1,113 @@
+// Autor: Rumata@gmx.de
+// ... sozusagen meine private rasse ...
+//
+// MorgenGrauen MUDlib
+//
+// shells/magier.c -- magier shell
+//
+// $Id: baum.c 8675 2014-02-18 20:39:54Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "/std/shells/magier";
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <moving.h>
+#include <attributes.h>
+#include <combat.h>
+#include <defines.h>
+#include <ansi.h>
+#include <udp.h>
+#include <new_skills.h>
+
+static int _wurzel;
+
+void create()
+{
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  ::create();
+	_wurzel = -1;
+}
+
+static int wurzel_an( string arg ) {
+	string num;
+	int count;
+
+	if( this_object() != this_interactive() ) return 0;
+	notify_fail( "WAS willst Du schlagen?\n" );
+	if( !arg || sscanf( arg, "wurzel%s", num ) == 0 ) return 0;
+
+	if( sscanf( num, " %d", count ) == 0 ) {
+		notify_fail( "schage wurzel <zahl>\n" );
+		return 0;
+	}
+
+	_wurzel = count;
+	say( Name(WER) + " schlaegt hier Wurzeln.\n" );
+	write( "Du schlaegst nun Wurzeln.\n" );
+	return 1;
+}
+
+static int wurzel_aus( string arg ) {
+	
+	if( this_object() != this_interactive() ) return 0;
+	notify_fail( "WAS willst du loesen?\n" );
+	if( !arg ) return 0;
+	if( member( ({"wurzel","wurzeln"}),	arg ) == -1 ) return 0;
+	
+	_wurzel = -1;
+	say( Name(WER) + " loest " + QueryPronoun(FEMALE,WEN,PLURAL)
+			 + " Wurzeln aus dem Boden.\n" );
+	write( "Du loest Deine Wurzeln aus dem Boden.\n" );
+	return 1;
+}
+
+int wurzel_info( string arg ) {
+	if( this_object() != this_interactive() ) return 0;
+	switch( _wurzel ) {
+	case -1:
+		write( "Deine Wurzeln sind lose.\n" );
+		break;
+	case 0:
+		write( "Deine Wurzeln sitzen fest.\n" );
+		break;
+	case 1:
+		write( "Noch eine Bewegung und Du sitzt fest.\n" );
+		break;
+	default:
+		printf( "Noch %d Bewegungen, und Du sitzt fest.\n", _wurzel );
+	}
+	return 1;
+}
+
+static mixed _query_localcmds() {
+	return ::_query_localcmds() + ({
+		({ "schlag"    , "wurzel_an"  ,1,ARCH_LVL }),
+		({ "loes"      , "wurzel_aus" ,1,ARCH_LVL }),
+		({ "wurzelinfo", "wurzel_info",0,ARCH_LVL })
+	});
+}
+
+varargs int move( mixed dest, int method, string dir,
+									string textout, string textin )
+{
+	if( _wurzel == 0 ) {
+		return ME_TOO_HEAVY;
+	}
+	if( _wurzel > 0 ) { _wurzel--; }
+	return ::move( dest, method, dir, textout, textin );
+}
+
+static int new_quit() {
+	_wurzel = -1;
+	return ::new_quit();
+}
diff --git a/std/shells/darkelf.c b/std/shells/darkelf.c
new file mode 100644
index 0000000..45e37fb
--- /dev/null
+++ b/std/shells/darkelf.c
@@ -0,0 +1,270 @@
+// MorgenGrauen MUDlib
+//
+// shells/darkelf.c -- Darkelf Shell
+//
+// $Id: darkelf.c 8675 2014-02-18 20:39:54Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "/std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <defines.h>
+#include <combat.h>
+#include <defuel.h>
+#include <errord.h>
+
+
+protected void create()
+{
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+    set_next_reset(-1);
+    return;
+  }
+  base::create();
+  SetDefaultHome("/gilden/dunkelelfen");
+  SetDefaultPrayRoom("/d/unterwelt/cadra/room/town/templemain");
+  SetProp(P_AVERAGE_SIZE,175);
+  SetProp(P_AVERAGE_WEIGHT,70000);
+  SetProp(P_ALIGN, -500);
+  SetProp(P_SKILL_ATTRIBUTE_OFFSETS,([SA_ENEMY_SAVE:110]));
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_INT:4,A_DEX:2]));
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_UNHOLY: 100,
+                                 MATGROUP_MAGIC:   70,
+                                 MATGROUP_DEAD:    50,
+                                 MATGROUP_METAL:   30]) );
+  SetProp(P_RESISTANCE_STRENGTHS,
+            ([ DT_HOLY :    0.25,
+               DT_UNHOLY : -0.15,
+               DT_TERROR : -0.05 ]));
+  SetProp(P_MAX_FOOD,80);
+  SetProp(P_MAX_DRINK,150);
+  SetProp(P_MAX_ALCOHOL,70);
+
+  SetProp(P_SP_DELAY, HEAL_DELAY-2); // dafuer regeneriert man im freien
+  SetProp(P_HP_DELAY, HEAL_DELAY-1); // wirklich _nichts_
+  SetProp(P_FOOD_DELAY,FOOD_DELAY+1);
+  SetProp(P_DRINK_DELAY,DRINK_DELAY-2);
+  SetProp(P_ALCOHOL_DELAY,ALCOHOL_DELAY+1);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+        ([ MT_ILLUSION    : 200,
+           MT_ANGRIFF     : 500,
+	   MT_BEHERRSCHUNG: 500,
+	   MT_PSYCHO      : 800 ]));
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)) {
+    SetProp(P_SIZE,150+random(50));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,60000+random(20001));
+    if(QueryProp(P_GENDER)==FEMALE)
+      SetProp(P_WEIGHT,QueryProp(P_WEIGHT)-5000);
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+  SetProp(P_DEFUEL_LIMIT_FOOD,40);
+  SetProp(P_DEFUEL_LIMIT_DRINK,30);
+  SetProp(P_DEFUEL_TIME_FOOD,400);
+  SetProp(P_DEFUEL_TIME_DRINK,250);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.45);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.4);
+}
+
+static void FinalSetup()
+{
+   if (!QuerySkill(SK_NIGHTVISION))
+      ModifySkill(SK_NIGHTVISION, 5000, 0, "ANY");
+}
+
+string _query_race()
+// nicht static, da sie manchmal auch so aufgerufen wird...
+{
+  return "Dunkelelf";
+}
+
+string _query_real_race()
+{
+  return "Dunkelelf";
+}
+
+static string _query_racedescr()
+{
+	  return
+"\
+Das Volk der Dunkelelfen lebt in einer grossen Hoehlenstadt gut versteckt\n\
+hinter einem Wasserfall. Ueber kaum ein anderes Volk gibt es soviele\n\
+Vorurteile wie ueber die Dunkelelfen, und so werden sie von allen misstrauisch\n\
+beaeugt oder sogar bekaempft. In diesem Kampf, insbesondere gegen die Elfen,\n\
+sind sie voellig auf sich allein gestellt, und so hat sich eine mehr oder\n\
+minder autarke Gesellschaft entwickelt. Die Dunkelelfen haben eine eigene\n\
+Kultur und eine eigene Goettin, der sie huldigen. Wie auch die Elfen\n\
+verfuegen sie ueber ausserordenlich grosse magische Faehigkeiten, auch wenn\n\
+sie sich mehr auf die schwarze Seite der Magie spezialisiert haben.\n";
+}
+
+// int QueryAllowSelect() { return 0; }
+// Aktiviert am 28.07.03, Ark.
+int QueryAllowSelect() { return 1; }
+
+string *_query_racestring()
+{
+  if (QueryProp(P_GENDER)==2)
+    return ({"Dunkelelfe","Dunkelelfe","Dunkelelfe","Dunkelelfe"});
+  return ({"Dunkelelf","Dunkelelfen","Dunkelelf","Dunkelelf"});
+}
+
+static string _query_default_guild()
+{
+  return "dunkelelfen";
+}
+
+static int sun_in_room(object room)
+{
+  if (!room) return 0;
+  closure qp=symbol_function("QueryProp", room);
+  int lt=funcall(qp, P_LIGHT_TYPE);
+  // (lt & LT_SUN) ist hier zunaechst _testweise_ drin. Die Rasse wurde
+  // anders genehmigt. Sollte das im MG ueberhand nehmen und jeder Keller
+  // nun sonnendurchflutet sein, dann wird das wieder ausgebaut!
+  // 27.06.04 Padreic
+  return ( (funcall(qp, P_INT_LIGHT)>0) &&
+          ((lt & LT_SUN) || ((lt==LT_MISC) && !funcall(qp, P_INDOORS))));
+}
+
+protected void heart_beat()
+{
+  ::heart_beat();
+  if (sun_in_room(environment()) &&
+      QueryProp("Dunkelelfen:Outdoor")<random(100) && !QueryProp(P_GHOST)) {
+     int hp;
+     hp=QueryProp(P_HP)-1;
+     SetProp(P_HP, hp);
+     if (hp<0) {
+       tell_object(ME,
+         "Das war wohl zuviel fuer Dich. Das naechste mal solltest Du Dich "
+        +"wohl besser\ngegen die Sonne schuetzen.\n");
+       SetProp(P_KILL_NAME,"Zuviel Sonne");
+       do_damage(999,ME);
+       SetProp(P_KILL_NAME,0);
+     }
+     else tell_object(ME, /* die Info musste irgendwie in eine Zeile */
+           "Die Sonne scheint gnadenlos auf Dein Haupt und schwaecht Dich.\n");
+  }
+}
+
+static int _query_no_regeneration()
+{
+  if (sun_in_room(environment()))
+     return NO_REG;
+  return Query(P_NO_REGENERATION);
+}
+
+int StdSkill_Nightvision(object me, string sname, mixed sinfo)
+{
+  int last, light;
+
+  if (!sinfo || !environment()) return 0;
+  if (intp(sinfo)) sinfo=([SI_SKILLABILITY:sinfo]);
+  if (!mappingp(sinfo)) return 0;
+  light=(QueryProp(P_PLAYER_LIGHT)<=0 ? -1 : 1);
+  if (last=sinfo[SI_USR]) { // letztes Lichtlevel != 0
+     if (light==last) {
+       if (sinfo[SI_LASTLIGHT]<=time())
+          return sinfo[SI_SKILLABILITY]+1;
+       return -1;
+     }
+     else {
+       last=( MAX_ABILITY - sinfo[SI_SKILLABILITY] );
+       last=(last/1000) + (last%1000 > random(1001) ? 1 : 0);
+       if (light<0) last/=2; // an Dunkelkeit schneller gewoehnen...
+       if(!this_interactive() || this_interactive()==this_object())
+       {
+	       ModifySkill(sname, ([SI_USR: light, SI_LASTLIGHT: time()+last]),
+        	           0, sinfo[SI_GUILD]);
+       }
+       if (last<=0)
+          return sinfo[SI_SKILLABILITY]+1;
+       return -1;
+     }
+  }
+  else { // Startwert...
+    if(!this_interactive() || this_interactive()==this_object())
+    {
+    	ModifySkill(sname, ([SI_USR: light, SI_LASTLIGHT: 0]), 0, sinfo[SI_GUILD]);
+    }
+    return sinfo[SI_SKILLABILITY]+1;
+  }
+  return 0;
+}
+
+varargs int CannotSee(int silent)
+{
+  string is_blind;
+
+  if ( is_blind = QueryProp( P_BLIND ) ) {
+     if (!silent) {
+       if (stringp(is_blind)) write(is_blind);
+       else write("Du bist blind!\n");
+     }
+     return 1;
+  }
+  if (environment() && (!IS_LEARNER(ME) || !Query(P_WANTS_TO_LEARN))) {
+     if (QueryProp(P_GHOST)) {
+        if (StdSkill_Nightvision(ME, SK_NIGHTVISION, QuerySkill(SK_NIGHTVISION))>0)
+            return 0;
+     }
+     else if (UseSkill(SK_NIGHTVISION)>0) return 0;
+     if (!silent) {
+        if (QueryProp(P_PLAYER_LIGHT)<=0)
+          write("Du kannst nichts sehen, da sich Deine Augen noch nicht an die Dunkelheit\n"
+               +"gewoehnt haben!\n");
+        else write("Du bist von dem hellen Licht total geblendet und Du musst Dich erst langsam\n"
+                  +"daran gewoehnen.\n");
+     }
+     return 1;
+  }
+  return 0;
+}
+
+
+/*
+ * 2003-11-05, Zook:
+ *
+ *   Temporaere Funktion, die den Delfen es ermoeglicht, einen
+ *   moeglicherweise falschen Raum (Indoor/Outdoor) zu melden.
+ *
+ */
+
+static int _indoorbug(string key)
+{
+  if (!stringp(key))
+    key= "";
+
+  ERRORD->LogReportedError(
+      ([ F_PROG: "unbekannt",
+         F_LINE: 0,
+         F_MSG: "Sonnenfehler: " + key,
+         F_OBJ: environment(this_object())
+      ])
+      );
+
+  write("Du hast einen fehlerhaften Innen-/Aussen-/Sonnenlichtraum gemeldet.\n");
+
+  return 1;
+}
+
+static mixed _query_localcmds()
+{
+  return ({ ({"sonnenfehler", "_indoorbug", 0, 0 }) })
+            + base::_query_localcmds();
+}
+
diff --git a/std/shells/dwarf.c b/std/shells/dwarf.c
new file mode 100644
index 0000000..7a696dc
--- /dev/null
+++ b/std/shells/dwarf.c
@@ -0,0 +1,156 @@
+// MorgenGrauen MUDlib
+//
+// shells/dwarf.c -- Dwarven Shell
+//
+// $Id: dwarf.c,v 3.14 2004/12/13 12:54:31 Zook Exp $
+
+#pragma strong_types,save_types
+
+inherit "std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <combat.h>
+#include <defuel.h>
+
+
+void create(){
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  mixed res;
+
+  base::create();
+  SetDefaultHome("/d/gebirge/room/zkapelle");
+  SetDefaultPrayRoom("/d/gebirge/room/zkapelle");
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_STR:2,A_DEX:1,A_CON:3]));
+  SetProp(P_SKILL_ATTRIBUTE_OFFSETS,([SA_DURATION:110]));
+  SetProp(P_AVERAGE_SIZE,120);
+  SetProp(P_AVERAGE_WEIGHT,75000);
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_STONE:30,
+                                 MATGROUP_METAL:30, 
+                                 MATGROUP_PRECIOUS_METAL: 40, 
+                                 MAT_GOLD:100]));
+  SetProp(P_RESISTANCE_STRENGTHS,
+	  ([ DT_FIRE : -0.2,
+	   DT_WATER : 0.4 ]));
+  SetProp(P_MAX_ALCOHOL,200);
+  SetProp(P_MAX_FOOD,160);
+
+  SetProp(P_SP_DELAY,HEAL_DELAY+1);
+  SetProp(P_POISON_DELAY,POISON_DELAY+1);
+  SetProp(P_FOOD_DELAY,FOOD_DELAY-1);
+  SetProp(P_ALCOHOL_DELAY,ALCOHOL_DELAY-1);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+          ([ MT_ANGRIFF : 200,
+	   MT_ILLUSION : -500,
+           MT_BEHERRSCHUNG : 1000,
+	   MT_VERWANDLUNG : 500 ]));
+
+  if(!IS_SEER(this_object())){
+    SetProp(P_MSGIN,"stapft herein");
+    SetProp(P_MSGOUT,"stapft");
+  }
+
+  if(!(res=QueryProp(P_HANDS)) || !pointerp(res) || (sizeof(res)<3))
+    res=({" mit blossen Haenden",35,({DT_BLUDGEON}) });
+  res[1]=35;
+  SetProp(P_HANDS,res);
+  SetProp(P_BODY,10);
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)){
+    SetProp(P_SIZE,110+random(21));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,65000+random(20001));
+    if(QueryProp(P_GENDER)==FEMALE)
+      SetProp(P_WEIGHT,QueryProp(P_WEIGHT)-5000);
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+
+  SetProp(P_DEFUEL_LIMIT_FOOD,70);
+  SetProp(P_DEFUEL_LIMIT_DRINK,50);
+  SetProp(P_DEFUEL_TIME_FOOD,535);
+  SetProp(P_DEFUEL_TIME_DRINK,500);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.8);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.6);
+
+}
+
+string _query_race()
+{
+  return "Zwerg";
+}
+
+string _query_real_race()
+{
+  return "Zwerg";
+}
+
+string _query_racedescr()
+{
+  return
+  "Zwerge sind kleine aber kraeftige Gebirgsbewohner, nicht sehr gespraechig,\n"
+    +"leicht erzuernt, aber eine schlagkraeftige Unterstuetzung fuer ihre Freunde."
+    +"\n"
+    +"Ihr Mut und ihre Standfestigkeit ist weit und breit beruehmt, auch ihr\n"
+    +"Geschick im Umgang mit Zwergenwaffen verleiht ihnen zusaetzliche Kraft.\n"
+    +"Leider sind Zwerge nicht allzu schlau, sie verlassen sich lieber auf\n"
+    +"ihre Kraft als auf ihr Gehirn.\n";
+}
+
+int QueryAllowSelect() { return 1; }
+
+string *_query_racestring()
+{
+  if (QueryProp(P_GENDER)==2)
+    return ({"Zwergin","Zwergin","Zwergin","Zwergin"});
+  return ({"Zwerg","Zwerges","Zwerg","Zwerg"});
+}
+int _query_hp_delay(){
+  int re;
+  re = Query(P_HP_DELAY);
+  if (environment() && environment()->QueryProp(P_INDOORS))
+    re--;
+  else
+    re++;
+  return re;
+}
+int _query_sp_delay(){
+  int re;
+  re = Query(P_SP_DELAY);
+  if (environment() && environment()->QueryProp(P_INDOORS))
+    re--;
+  else
+    re++;
+  return re;
+}
+
+string _query_default_guild(){
+  return "abenteurer";
+}
+
+mixed RaceDefault(string arg)
+{
+  if (!arg)
+    return 0;
+  switch(arg)
+  {
+    case P_HANDS :
+      return ({" mit blossen Haenden",35,({DT_BLUDGEON}) });
+    case P_BODY :
+      return 10;
+  }
+  return 0;
+}
diff --git a/std/shells/elf.c b/std/shells/elf.c
new file mode 100644
index 0000000..dec4118
--- /dev/null
+++ b/std/shells/elf.c
@@ -0,0 +1,154 @@
+// MorgenGrauen MUDlib
+//
+// shells/elf.c -- Elven Shell
+//
+// $Id: elf.c 8928 2014-09-08 16:18:41Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <combat.h>
+#include <defuel.h>
+
+
+
+void create()
+{
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  base::create();
+  SetDefaultHome("/d/wald/room/es_mitte");
+  SetDefaultPrayRoom("/d/wald/room/es_mitte");
+  SetProp(P_AVERAGE_SIZE,195);
+  SetProp(P_AVERAGE_WEIGHT,70000);
+  SetProp(P_SKILL_ATTRIBUTE_OFFSETS,([SA_ENEMY_SAVE:110]));
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_INT:3,A_DEX:2,A_CON:1]));
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_WOOD:30, 
+                                 MATGROUP_HERBAL:30, 
+                                 MATGROUP_LIVING:20]));
+
+  SetProp(P_MAX_FOOD,80);
+  SetProp(P_MAX_DRINK,150);
+  SetProp(P_MAX_ALCOHOL,70);
+
+  SetProp(P_SP_DELAY,HEAL_DELAY-1);
+  SetProp(P_FOOD_DELAY,FOOD_DELAY+1);
+  SetProp(P_DRINK_DELAY,DRINK_DELAY-2);
+  SetProp(P_ALCOHOL_DELAY,ALCOHOL_DELAY+1);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+          ([ MT_ILLUSION : 800,
+           MT_ANGRIFF : 200,
+	   MT_VERWANDLUNG : 400,
+	   MT_PSYCHO : 500 ]));
+
+  // Elfen kriegen die Ebene der Wipfellaeufer per default. (Zu diesem
+  // Zeitpunkt stehen in P_CHANNELS nur die default channel drin, wird dann
+  // ueber das Einlesen des Savefiles ggf. ueberschrieben.)
+  SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({"wipfellaeufer"}));
+
+  if(!IS_SEER(this_object())){
+    SetProp(P_MSGIN,"wandelt herein");
+    SetProp(P_MSGOUT,"wandelt");
+  }
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)){
+    SetProp(P_SIZE,185+random(21));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,60000+random(20001));
+    if(QueryProp(P_GENDER)==FEMALE)
+      SetProp(P_WEIGHT,QueryProp(P_WEIGHT)-5000);
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+  SetProp(P_DEFUEL_LIMIT_FOOD,40);
+  SetProp(P_DEFUEL_LIMIT_DRINK,20);
+  SetProp(P_DEFUEL_TIME_FOOD,400);
+  SetProp(P_DEFUEL_TIME_DRINK,200);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.4);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.35);
+
+}
+
+string _query_race()
+{
+  return "Elf";
+}
+
+string _query_real_race()
+{
+  return "Elf";
+}
+
+string _query_racedescr()
+{
+  return 
+"\
+Als Elfen bezeichnet man in der Regel jene hageren Hinterwaeldler, deren\n\
+demonstratives Naturgehabe in der Regel nur durch ihre Liebe zu kitschigen\n\
+Gedichten und ausschweifendem Geschlechtsleben in den Schatten gestellt wird.\n\
+Einen Elf kann man im allgemeinen nicht nur an aeusseren Missbildungen\n\
+(spitze Ohren, spindelduerre Gestalt, blonde Haare) erkennen, sondern auch\n\
+an seiner aufdringlichen Art, ueber jeden und alles hemmungslos ins Gruene\n\
+loszuphilosophieren.\n";
+
+}
+
+int QueryAllowSelect() { return 1; }
+
+string *_query_racestring()
+{
+  if (QueryProp(P_GENDER)==2)
+    return ({"Elfe","Elfe","Elfe","Elfe"});
+  return ({"Elf","Elfen","Elf","Elf"});
+}
+int _query_hp_delay(){
+  int re;
+  re = Query(P_HP_DELAY);
+  if (environment() && environment()->QueryProp(P_INDOORS))
+    re++;
+  else
+    re--;
+  return re;
+}
+int _query_sp_delay(){
+  int re;
+  re = Query(P_SP_DELAY);
+  if (environment() && environment()->QueryProp(P_INDOORS))
+    re++;
+  else
+    re--;
+  return re;
+}
+
+string _query_default_guild(){
+  return "wipfellaeufer";
+}
+
+static void FinalSetup()
+{
+  if(QueryProp(P_GUILD) != "chaos")
+    SetProp(P_RESISTANCE_STRENGTHS,
+            ([ DT_MAGIC : -0.2,
+               DT_HOLY : 0.1,
+               DT_UNHOLY : 0.3 ]));
+  else
+    SetProp(P_RESISTANCE_STRENGTHS,
+            ([ DT_MAGIC : -0.2,
+               DT_UNHOLY : 0.1,
+               DT_HOLY : 0.3 ]));
+}
diff --git a/std/shells/feline.c b/std/shells/feline.c
new file mode 100644
index 0000000..f590fab
--- /dev/null
+++ b/std/shells/feline.c
@@ -0,0 +1,259 @@
+// MorgenGrauen MUDlib
+//
+// shells/feline.c -- Feline Shell
+//
+// $Id: feline.c 8487 2013-05-21 19:15:52Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "/std/player/base";
+
+#include <attributes.h>
+#include <combat.h>
+#include <health.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <defuel.h>
+
+
+void create()
+{   
+    if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+    }
+
+    int   i,g;
+    mixed h;
+
+    base::create();
+
+// Startraum/Kapelle setzen
+    SetDefaultHome("/d/dschungel/paracelsus/room/fkapelle");
+    SetDefaultPrayRoom("/d/dschungel/paracelsus/room/fkapelle");
+
+// Besondere rassenspezifische Properties (bei denen es nichts macht, wenn
+// sie nach jedem Einloggen neu gesetzt werden):
+
+    SetProp(P_ATTRIBUTES_OFFSETS, // Summe 4 statt 6 wg. SA_SPEED
+    ([
+        A_STR :  1,
+        A_INT :  2,
+        A_DEX :  2,
+        A_CON : -1 
+    ]) );
+    SetProp(P_AVERAGE_SIZE,200);
+    SetProp(P_AVERAGE_WEIGHT,85000);
+    SetProp(P_BODY,15);
+    SetProp(P_SKILL_ATTRIBUTE_OFFSETS,([SA_SPEED:120]));
+    SetProp(P_MATERIAL_KNOWLEDGE,
+    ([
+        MATGROUP_WOOD    :  60,
+        MATGROUP_JEWEL   : 100,
+        MATGROUP_EATABLE :  30
+    ]) );
+
+    SetProp(P_MAGIC_RESISTANCE_OFFSET,
+    ([
+        MT_ANGRIFF     :  200,
+        MT_ILLUSION    : -500,
+        MT_VERWANDLUNG :  500,
+        MT_PSYCHO      : 1000 
+    ]) );
+
+    SetProp(P_RESISTANCE_STRENGTHS,
+    ([
+        DT_WATER : -0.1,
+        DT_ACID  :  0.1,
+        DT_COLD  :  0.1 
+    ]) );
+
+// Lebenspunkte werden langsamer als normal regeneriert
+    SetProp(P_HP_DELAY,HEAL_DELAY+1);
+
+// Magiepunkte werden schneller als normal regeneriert
+    SetProp(P_SP_DELAY,HEAL_DELAY-1);
+
+// Gift wirkt etwas langsamer als normal
+    SetProp(P_POISON_DELAY,POISON_DELAY+1);
+
+// Getraenke werden etwas schneller als normal abgebaut
+    SetProp(P_DRINK_DELAY,DRINK_DELAY-1);
+
+// Essen wird etwas schneller als normal abgebaut ...
+    SetProp(P_FOOD_DELAY,FOOD_DELAY-1);
+    SetProp(P_MAX_FOOD,140);
+
+// Es gibt einige Sachen, die sollen nur beim ersten Einloggen gesetzt werden.
+// Andere muessen nachtraeglich - aber nur einmal - gemacht werden, weil sich
+// etwas geaendert hat.
+    switch( QueryProp(P_SHELL_VERSION) )
+    {
+        case 0 :
+
+            g=QueryProp(P_GENDER);
+
+            if ( !(i=QueryProp(P_SIZE)) || (i<(g==FEMALE?165:170)) 
+                || (i>(g==FEMALE?225:230)) )
+            {
+                SetProp(P_SIZE, (g==FEMALE?195:200)
+                    + random(16) - random(16) + random(16) - random(16) );
+            }
+
+            if( !(i=QueryProp(P_WEIGHT)) || (i<(g==FEMALE?70000:77000)) 
+               || (i>(g==FEMALE?88000:95000)) || (i==75000) )
+            {
+                SetProp(P_WEIGHT, (g==FEMALE?70000:77000)
+                    + random(4501) + random(4501) + random(4501) + random(4501) );
+            }
+
+
+            SetProp(P_MATERIAL,([
+                MAT_MISC_LIVING : 90,
+                MAT_PELT        :  8,
+                MAT_HORN        :  2
+            ]) );
+
+            if ( !IS_SEER(this_object()) )
+            {
+                SetProp(P_MSGIN,"schleicht herein");
+                SetProp(P_MSGOUT,"schleicht");
+                SetProp(P_MMSGIN,"erscheint mit einem grellen Blitz");
+                SetProp(P_MMSGOUT,"verschwindet mit einem grellen Blitz");
+                SetProp(P_HANDS,({" mit scharfen Krallen",40, ({DT_RIP}) }));
+            }
+            else
+            {
+                if ( !pointerp(h=QueryProp(P_HANDS)) || (sizeof(h)<1) )
+                    h = ({" mit scharfen Krallen",40, ({DT_RIP}) });
+                else
+                    h = ({h[0],40, ({DT_RIP}) });
+                SetProp(P_HANDS, h);
+            }
+
+            if ( !pointerp(h=QueryProp(P_CHANNELS)) )
+                SetProp(P_CHANNELS,({"katzenkrieger"}));
+            else if ( member(h,"katzenkrieger")==-1 )
+                SetProp(P_CHANNELS, h + ({"katzenkrieger"}) );
+
+            Set(P_SIZE,SAVE,F_MODE_AS);
+            Set(P_MATERIAL,SAVE,F_MODE_AS);
+            Set(P_WEIGHT,SAVE,F_MODE_AS);
+
+        default :
+
+            SetProp(P_SHELL_VERSION,1);
+    }
+  SetProp(P_DEFUEL_LIMIT_FOOD,70);
+  SetProp(P_DEFUEL_LIMIT_DRINK,40);
+  SetProp(P_DEFUEL_TIME_FOOD,400);
+  SetProp(P_DEFUEL_TIME_DRINK,300);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.55);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.4);
+
+}
+
+// Diese Rasse kann derzeit gewaehlt werden:
+int QueryAllowSelect() { return 1; }
+
+// Rassenbezeichnung
+string _query_race()
+{
+    return "Feline";
+}
+
+string _query_real_race()
+{
+    return "Feline";
+}
+
+// Die Rassenbeschreibung, die man beim ersten Einloggen abrufen kann.
+string _query_racedescr()
+{
+    return break_string(
+        "Felinen sind aufrecht gehende Katzenwesen.\n"+
+        "Ihre Heimat ist der Dschungel. Kaum jemand duerfte sich dort "+
+        "besser zurechtfinden als sie. Bedingt durch diese Umgebung "+
+        "haben sie im Laufe der Zeit eine Vorliebe fuer elegante Hoelzer "+
+        "und funkelnde Edelsteine entwickelt. Sie sind zwar nicht so "+
+        "'raffgierig' wie Zwerge, aber dennoch sollte man besser nicht "+
+        "versuchen, einem Felinen einen Edelstein wegzunehmen. Sie "+
+        "benutzen die Edelsteine sehr gerne, um sich damit zu schmuecken. "+
+        "Felinen betreiben sogar einen regelrechten Koerperkult, "+
+        "insbesondere wenn es darum geht, das Fell oder die Krallen zu "+
+        "faerben. Edelsteine kommen da als Accessoires gerade recht.\n"+
+        "Auch im Kampf gegen einen Felinen sollte man sehr vorsichtig "+
+        "sein, da Felinen ihre geringe Ausdauer durch eine hohe "+
+        "Geschwindigkeit sowie Geschick und Intelligenz wettmachen. "+
+        "Auch die Spitzen Krallen sind da nicht zu verachten und so "+
+        "mancher Gegner musste schon als Ersatz fuer einen Kratzbaum "+
+        "herhalten.",78,0,1);
+}
+
+// Geschlechtsabhaengiges Rassenbezeichnungs-Array
+string *_query_racestring()
+{
+    if (QueryProp(P_GENDER)==2)
+        return ({"Felinin","Felinin","Felinin","Felinin"});
+    return ({"Feline","Felinen","Felinen","Felinen"});
+}
+
+// Regeneration der Lebenspunkte ist von der Umgebung abhaengig
+// Im Wald und Dschungel geht es schneller, in der Wueste und im
+// Polargebiet dagegen langsamer.
+int _query_hp_delay()
+{
+    int re;
+    string fn;
+
+    re = Query(P_HP_DELAY);
+    if (environment() && !(environment()->QueryProp(P_INDOORS)) &&
+        fn=object_name(environment()))
+    {
+        if (fn[0..12]=="/d/dschungel/" || fn[0..7]=="/d/wald/")
+            re--;
+        else if (fn[0..9]=="/d/wueste/" || fn[0..8]=="/d/polar/")
+            re++;
+    }
+    return re;
+}
+
+// Regeneration der Magiepunkte ist von der Umgebung abhaengig
+// Im Wald und Dschungel geht es schneller, in der Wueste und im
+// Polargebiet dagegen langsamer.
+int _query_sp_delay()
+{
+    int re;
+    string fn;
+
+    re = Query(P_SP_DELAY);
+    if (environment() && !(environment()->QueryProp(P_INDOORS)) &&
+        fn=object_name(environment()))
+    {
+        if (fn[0..12]=="/d/dschungel/" || fn[0..7]=="/d/wald/")
+            re--;
+        else if (fn[0..9]=="/d/wueste/" || fn[0..8]=="/d/polar/")
+            re++;
+    }
+    return re;
+}
+
+string _query_default_guild(){
+  return "katzenkrieger";
+}
+
+mixed RaceDefault(string arg)
+{
+    if (!arg)
+      return 0;
+    switch(arg)
+    {
+        case P_HANDS :
+            return ({" mit scharfen Krallen",40, ({DT_RIP}) });
+        case P_BODY :
+            return 15;
+    }
+    return base::RaceDefault(arg);
+}
diff --git a/std/shells/goblin.c b/std/shells/goblin.c
new file mode 100644
index 0000000..ed694b3
--- /dev/null
+++ b/std/shells/goblin.c
@@ -0,0 +1,213 @@
+/*
+ * Goblin-Rassenshell
+ * [/std/shells/goblin.c]
+ * (c) 2007 nibel@mg.mud.de
+ *
+ * Werte von Ark abgesegnet am 11.12.2007
+ */
+
+#pragma strong_types,save_types
+
+#include <attributes.h>
+#include <health.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <defuel.h>
+#include <moving.h>
+
+inherit "/std/player/base.c";
+
+static varargs int GoblinCmdWaaagh(string arg);
+
+public void create() {
+  mixed res;
+  base::create();
+
+  SetDefaultHome("/d/wald/kessa/waaagh/room/starthut/hut[" + 
+    getuid(this_object()) +"]");
+  SetDefaultPrayRoom("/d/wald/nibel/lichtung/room/lichtung_45");
+  
+  SetProp(P_AVERAGE_SIZE, 80);
+  SetProp(P_AVERAGE_WEIGHT, 32000);
+  SetProp(P_MATERIAL_KNOWLEDGE, ([MATGROUP_EATABLE:20, MATGROUP_DRUG:40,
+    MATGROUP_PRECIOUS_METAL:25, MATGROUP_JEWEL:25]));
+
+  SetProp(P_BODY, 15);
+  SetProp(P_ATTRIBUTES_OFFSETS, ([A_STR:0, A_INT:1, A_DEX:2, A_CON:2]));
+  SetProp(P_RESISTANCE_STRENGTHS, ([DT_FIRE:-0.15, DT_LIGHTNING:-0.15,
+    DT_SOUND:0.1, DT_HOLY:0.1, DT_AIR:0.1, DT_ACID:0.1]));
+  SetProp(P_MAGIC_RESISTANCE_OFFSET, ([MT_ANGRIFF:600, MT_ILLUSION:500,
+    MT_VERWANDLUNG:-300, MT_HELLSICHT:-750, MT_BEHERRSCHUNG:250]));
+  SetProp(P_SKILL_ATTRIBUTE_OFFSETS, ([SA_ENEMY_SAVE:103, SA_DAMAGE:107]));
+
+  SetProp(P_SP_DELAY, HEAL_DELAY + 2);
+  SetProp(P_HP_DELAY, HEAL_DELAY - 1);
+  SetProp(P_ALCOHOL_DELAY, ALCOHOL_DELAY - 1);
+
+  switch(QueryProp(P_SHELL_VERSION)) {
+    case 0:
+      if(!QueryProp(P_SIZE)) // Maennlein und Weiblein sind gleich "gross"
+        SetProp(P_SIZE, 75 + random(11));
+      if(QueryProp(P_WEIGHT) == 75000) // Dito Gewicht
+        SetProp(P_WEIGHT, (QueryProp(P_SIZE) * 390) +
+          random(QueryProp(P_SIZE) * 10));
+      SetProp(P_MATERIAL,([MAT_MISC_LIVING:100]));
+          
+      Set(P_SIZE, SAVE, F_MODE_AS);
+      Set(P_WEIGHT, SAVE, F_MODE_AS);
+      Set(P_MATERIAL, SAVE, F_MODE_AS);
+
+      if(!pointerp(res = QueryProp(P_HANDS)) || sizeof(res) < 3)
+        res = ({" mit kleinen Faeustchen", 30, ({ DT_BLUDGEON })});
+      SetProp(P_HANDS, res);
+
+      if(!IS_SEER(this_object())) {
+        SetProp(P_MSGIN, "flitzt herein");
+        SetProp(P_MSGOUT, "flitzt");
+      }
+      SetProp(P_SHELL_VERSION, 1);
+    case 1:
+      // /std/player/base setzt ja schon P_WEIGHT...
+      if(QueryProp(P_WEIGHT) == 75000) {
+        SetProp(P_WEIGHT, (QueryProp(P_SIZE) * 390) +
+          random(QueryProp(P_SIZE) * 10));
+      }
+      SetProp(P_SHELL_VERSION, 2);
+    default: break;
+  }
+  
+  SetProp(P_MAX_FOOD, 110);
+  SetProp(P_MAX_DRINK, 80);
+  SetProp(P_MAX_ALCOHOL, 125);
+  SetProp(P_DEFUEL_LIMIT_FOOD, 60);
+  SetProp(P_DEFUEL_LIMIT_DRINK, 60);
+  SetProp(P_DEFUEL_TIME_FOOD, 400);
+  SetProp(P_DEFUEL_TIME_DRINK, 360);
+  SetProp(P_DEFUEL_AMOUNT_FOOD, 0.5);
+  SetProp(P_DEFUEL_AMOUNT_DRINK, 0.4);
+}
+
+static void FinalSetup() {
+  object o;
+  if(QueryProp(P_LEVEL) > 5 || present("\nibel:waldlichtungskarte",
+    this_object())) return;
+  if(!catch(o = clone_object("/d/wald/nibel/lichtung/obj/karte")))
+    o->move(this_object(), M_NOCHECK);
+}
+
+public int QueryAllowSelect() { return 1; }
+
+public string _query_race() { return "Goblin"; }
+public string _query_real_race() { return "Goblin"; }
+
+public string _query_racedescr() {
+  return break_string("Goblins sind winzige, gruenhaeutige Wesen, sogar "
+    "noch kleiner als Hobbits. An ihren zu dick geratenen Koepfchen "
+    "befinden sich lange, selten reglose, Ohren und eine grosse, krumme "
+    "Nase. Ihre kleine Statur sollte jedoch nicht taeuschen, denn ihre "
+    "fehlende Kraft machen sie mit Geschwindigkeit, Praezision und nicht "
+    "zuletzt ihrer unbestrittenen Ruchlosigkeit alleweil wett. Obwohl "
+    "fuer sie Pluendern, lautes Herumbruellen und die gemeinsten Streiche "
+    "spielen zum Alltag gehoert, wuerde sie niemand als boesartig "
+    "bezeichnen. Denn Goblins sind vieles, aber sicherlich nicht die "
+    "intelligentesten Kreaturen. Durch ihren zaehen Willen und die dicke, "
+    "lederne Haut sind sie aussergewoehnlich widerstandsfaehig, und, "
+    "sofern funkelnde Beute winkt, fuer jedes Abenteuer zu haben.", 78);
+}
+
+public string *_query_racestring() {
+  if(QueryProp(P_GENDER) == FEMALE)
+    return ({"Goblinfrau", "Goblinfrau", "Goblinfrau", "Goblinfrau"});
+  return ({"Goblin", "Goblins", "Goblin", "Goblin"});
+}
+
+public string _query_default_guild() {return "abenteurer";}
+
+public string _query_visible_guild() {
+  switch(lower_case(QueryProp(P_GUILD))) {
+    case "abenteurer": return "abentoira";
+    case "wipfellaeufer": return "wiffelloifa";
+    case "chaos": return "kaos";
+    case "zauberer": return "zaubara";
+    case "bierschuettler": return "biaschuettla";
+    case "katzenkrieger": return "kaznkriega";
+	  case "tanjian": return "tanschan";
+	  case "klerus": return "klerikae";	  	
+	  case "dunkelelfen": return "dunklelfn";
+	  case "kaempfer": return "kaempfa";
+	  case "karate": return "karatae";
+	  case "werwoelfe": return "weawoelf";
+	  case "magus": return "magia";
+    case "urukhai": return "urugai";
+  }
+  return QueryProp(P_GUILD);
+}
+
+public mixed RaceDefault(string arg) {
+  if(!arg) return 0;
+  switch(arg) {
+    case P_HANDS:
+      return ({" mit kleinen Faeustchen", 30, ({  DT_BLUDGEON })});
+    case P_BODY:
+      return 15;
+  }
+  return base::RaceDefault(arg);
+}
+
+static mixed _query_localcmds() {
+  return ({({"waaagh", "GoblinCmdWaaagh", 0, 0})}) +
+    base::_query_localcmds();
+}
+
+// "knuddel alle" ist deutlich teurer also who cares :-)
+static varargs int GoblinCmdWaaagh(string arg) {
+  object *obs;
+  string s, w;
+  
+  if(!objectp(environment())) return 0;
+  obs = filter(all_inventory(environment()) - ({this_object()}), #'living);
+  obs = obs - filter_objects(obs, "QueryProp", P_INVIS);
+  // levelabhaengige Anzahl aaaaaaa's
+  w = "W"+ sprintf("%'a'"+ (QueryProp(P_LEVEL) / 10 + 3) +"s", "aaa") +"gh!";
+
+  foreach(object o : obs)
+  {
+    string str=(break_string(Name(WER) +" ballt die Faeustchen und "
+      "kreischt laut: "+ w +"\n"
+      + capitalize(o->QueryDu(WER)) + " zuckst erschrocken zusammen.",
+      78, 0, BS_LEAVE_MY_LFS));
+
+    int res=o->ReceiveMsg(str,MT_LISTEN,MA_EMOTE,0,this_object());
+    if (res<0)
+    {
+      obs-=({o}); // unten nicht mehr mit anzeigen.
+      if (res==MSG_SENSE_BLOCK)
+        ReceiveMsg(o->Name(WER) +" kann Dich nicht hoeren.",
+                   MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
+                   MA_EMOTE,0,this_object());
+      else
+        ReceiveMsg(o->Name(WER) +" ignoriert Dich oder diesen Befehl.",
+                   MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
+                   MA_EMOTE,0,this_object());
+    }
+  }
+  int anzahl=sizeof(obs);
+  if(!anzahl)
+  {
+    ReceiveMsg("Du ballst die Faeustchen und kreischst laut: "
+               + w, MT_NOTIFICATION|MSG_DONT_IGNORE,MA_EMOTE,0,this_object());
+  }
+  else
+  {
+    s = CountUp(map_objects(obs, "name", WER));
+    ReceiveMsg(break_string("Du ballst die Faeustchen und kreischst laut: "
+               + w +"\n"+ capitalize(s) +" zuck"
+               +(anzahl > 1 ? "en" : "t") +" erschrocken zusammen.",
+               78, 0, BS_LEAVE_MY_LFS),
+               MT_NOTIFICATION|MSG_DONT_STORE|MSG_DONT_IGNORE,
+               MA_EMOTE,0,this_object());
+  }
+  return 1;
+}
+
diff --git a/std/shells/hobbit.c b/std/shells/hobbit.c
new file mode 100644
index 0000000..b626b06
--- /dev/null
+++ b/std/shells/hobbit.c
@@ -0,0 +1,139 @@
+// MorgenGrauen MUDlib
+//
+// shells/hobbit.c -- Hobbit Shell
+//
+//   9.April 1995  V1.0 Gundur
+//
+//   15.Juni prayroom und defHome auf Hobbitdorf gesetzt von Gundur
+//
+// $Id: hobbit.c 8920 2014-09-02 20:18:38Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <combat.h>
+#include <moving.h>
+#include <defuel.h>
+
+
+
+void create(){
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  mixed res;
+
+  base::create();
+  SetDefaultHome("/d/wald/gundur/hobbitdorf/schrein");
+  SetDefaultPrayRoom("/d/wald/gundur/hobbitdorf/schrein");
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_DEX:4,A_CON:2]));
+  SetProp(P_AVERAGE_SIZE, 105);
+  SetProp(P_AVERAGE_WEIGHT, 60000);
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_EATABLE:30, 
+                                 MATGROUP_DRUG:30, 
+                                 MATGROUP_POISONOUS:10]));
+  SetProp(P_RESISTANCE_STRENGTHS,
+	  ([ DT_TERROR : -0.1,
+	   DT_MAGIC : -0.1,
+	   DT_SOUND : 0.2,
+	   DT_LIGHTNING : 0.1,
+	   DT_POISON : 0.1 ]));
+  SetProp(P_MAX_FOOD,250);
+  SetProp(P_MAX_DRINK,100);
+  SetProp(P_MAX_ALCOHOL,150);
+
+  SetProp(P_SP_DELAY,HEAL_DELAY+1);
+  SetProp(P_POISON_DELAY,POISON_DELAY-1);
+  SetProp(P_FOOD_DELAY,FOOD_DELAY-2);
+  SetProp(P_ALCOHOL_DELAY,ALCOHOL_DELAY+1);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+          ([ MT_ANGRIFF : 500,
+	   MT_HELLSICHT : -500,
+	   MT_PSYCHO : -500 ]));
+
+  if(!(res=QueryProp(P_HANDS)) || !pointerp(res) || (sizeof(res)<3))
+    res=({" mit pelzigen Haenden",25,({DT_BLUDGEON})});
+  res[1]=25;
+  SetProp(P_HANDS,res);
+  SetProp(P_BODY,15);
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)){
+    SetProp(P_SIZE,95+random(21));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,50000+random(20001));
+    if(QueryProp(P_GENDER)==FEMALE)
+      SetProp(P_WEIGHT,QueryProp(P_WEIGHT)-5000);
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+  SetProp(P_DEFUEL_LIMIT_FOOD,140);
+  SetProp(P_DEFUEL_LIMIT_DRINK,50);
+  SetProp(P_DEFUEL_TIME_FOOD,850);
+  SetProp(P_DEFUEL_TIME_DRINK,450);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.8);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.6);
+
+}
+
+string _query_race()
+{
+  return "Hobbit";
+}
+
+string _query_real_race()
+{
+  return "Hobbit";
+}
+
+string _query_racedescr()
+{
+  return "Hobbits sind kleine Wesen, die am ehesten den Menschen aehneln.\n"+
+    "Sie zeichnen sich trotz Ihrer Groesse durch ihren Mut und Standfestigkeit "+
+	"aus.\nObwohl sie viel lieber zuhause vorm warmen Kamin sitzen, sind sie "+
+	"immer\nfuer ein Abenteuer zu haben.\n";
+}
+
+int QueryAllowSelect() { return 1; }
+
+string *_query_racestring(){
+  if (QueryProp(P_GENDER) == FEMALE)
+    return ({"Hobbitfrau","Hobbitfrau","Hobbitfrau","Hobbitfrau"});
+  return ({"Hobbit","Hobbits","Hobbit","Hobbit"});
+}
+
+string _query_default_guild(){
+  return "abenteurer";
+}
+
+void FinalSetup() {
+  if(!present("pfeifchen",this_object()))
+    clone_object("/items/pfeifchen")->move(this_object(),M_NOCHECK);
+}
+
+mixed RaceDefault(string arg)
+{
+  if (!arg)
+    return 0;
+  switch(arg)
+  {
+    case P_HANDS :
+      return ({" mit pelzigen Haenden",25,({DT_BLUDGEON}) });
+    case P_BODY :
+      return 15;
+  }
+  return base::RaceDefault(arg);
+}
diff --git a/std/shells/human.c b/std/shells/human.c
new file mode 100644
index 0000000..c18df2d
--- /dev/null
+++ b/std/shells/human.c
@@ -0,0 +1,113 @@
+// MorgenGrauen MUDlib
+//
+// shells/human.c -- Human Shell
+//
+// $Id: human.c 9022 2015-01-10 21:50:50Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "/std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <defuel.h>
+
+
+
+void create() {
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  base::create();
+  SetDefaultHome("/gilden/abenteurer");
+  SetDefaultPrayRoom("/d/ebene/room/PortVain/pray_room");
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_INT:1,A_STR:1,A_CON:1,A_DEX:3]));
+  SetProp(P_AVERAGE_SIZE,170);
+  SetProp(P_AVERAGE_WEIGHT,75000);
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_WOOD:20, 
+                                 MATGROUP_METAL:20, 
+                                 MATGROUP_ELEMENTAL:20, 
+                                 MATGROUP_CLOTH:20]));
+
+  SetProp(P_MAX_FOOD,120);
+  SetProp(P_MAX_DRINK,120);
+  SetProp(P_MAX_ALCOHOL,120);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+          ([ MT_ANGRIFF : 500,
+	   MT_ILLUSION : 700,
+           MT_BEHERRSCHUNG : 500,
+	   MT_HELLSICHT : 1000,
+	   MT_VERWANDLUNG : -500,
+	   MT_PSYCHO : -500 ]));
+
+  // Zukuenftig 0, nicht mehr -5. Ark, 04.01.08.
+  SetProp(P_BODY,0);
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)){
+    SetProp(P_SIZE,160+random(21));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,65000+random(20001));
+    if(QueryProp(P_GENDER)==FEMALE)
+      SetProp(P_WEIGHT,QueryProp(P_WEIGHT)-5000);
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+  SetProp(P_DEFUEL_LIMIT_FOOD,60);
+  SetProp(P_DEFUEL_LIMIT_DRINK,50);
+  SetProp(P_DEFUEL_TIME_FOOD,500);
+  SetProp(P_DEFUEL_TIME_DRINK,245);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.75);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.5);
+
+}
+
+string _query_race()
+{
+  return "Mensch";
+}
+
+string _query_real_race()
+{
+  return "Mensch";
+}
+
+string *_query_racestring()
+{
+  if (QueryProp(P_GENDER)==2)
+    return ({"Menschenfrau","Menschenfrau","Menschenfrau","Menschenfrau"});
+  return ({"Mensch","Menschen","Mensch","Menschen"});
+}
+
+string _query_racedescr()
+{
+  return "Die Staerke des Menschen ist seine Vielseitigkeit.\n"+
+    "Der Mensch kann zwar nichts besonders gut - dafuer aber eigentlich alles.\n";
+}
+
+int QueryAllowSelect() { return 1; }
+
+void FinalSetup()
+{
+  // Im MG gibt fuer kleine Spieler eine Karte von Port Vain. Die gibt es
+  // woanders meist nicht.
+#if MUDNAME == "MorgenGrauen"
+  if (QueryProp(P_LEVEL)<=3 && !present("portvainkarte",this_object()))
+    clone_object("/d/ebene/obj/pv")->move(this_object(),M_NOCHECK);
+#endif
+}
+
+string _query_default_guild(){
+  return "abenteurer";
+}
diff --git a/std/shells/magier.c b/std/shells/magier.c
new file mode 100644
index 0000000..dc2a150
--- /dev/null
+++ b/std/shells/magier.c
@@ -0,0 +1,326 @@
+// MorgenGrauen MUDlib
+//
+// shells/magier.c -- magier shell
+//
+// $Id: magier.c 9231 2015-05-27 21:53:32Z Zesstra $
+
+//
+// Magiershell Basisfile
+//
+// Ueberarbeitung abgeschlossen am 18.12.2002
+//
+// Dank an Zwirch@PK, Rikus@MG, Zoran@PK, Vanion@MG
+// und viele andere, die ich vergessen habe.
+//
+// Fragen und Bughinweise an Mandragon@MG oder einen
+// Erzmagier Deiner Wahl.
+//
+// Zur Shell gehoeren ausser dieser Datei:
+// admin.c:      Administrative Befehle
+// comm.c:       Kommunikationsbefehle
+// fileedit.c:   Befehle zum Veraendern von Dateien
+// fileview.c:   Befehle zum Lesen von Dateien
+// magier_ext.c: Generelle Magierbefehle
+// moving.c:     Bewegungsbefehle
+// objects.c:    Erzeugen und zerstoeren von Objekten
+// parsing.c     Auswertung von Pfadangaben und Wildcards
+// players.c:    Befehle zur Beeinflussung von Spielern
+// todo.c:       Implementation der Todoliste
+// upd.c:        Der Befehl upd
+// magier.h      Generelle Header-Datei
+//
+
+#pragma strict_types,save_types
+
+inherit "/std/player/base";
+inherit "/std/shells/magier/magier_ext";
+
+#include <wizlevels.h>
+#include <moving.h>
+#include <properties.h>
+#include <new_skills.h>
+#include <config.h>
+
+protected void create()
+{
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);
+      return;
+  }
+
+  base::create();
+
+  Set(P_RACE, SAVE, F_MODE);
+  Set(P_ZAP_MSG, SAVE, F_MODE);
+  Set(P_TRANK_FINDEN, SAVE, F_MODE);
+  Set(P_HANDS, SAVE, F_MODE);
+  Set(P_RACESTRING, SAVE, F_MODE);
+  SetDefaultHome("/gilden/abenteurer");
+  SetProp(P_ENEMY_DEATH_SEQUENCE,
+    ([17:"Der Tod schuettelt verstaendnislos den Kopf.\n\n",
+      18:"Der Tod sagt: WIESO MUSSTEST DU DICH AUCH UNBEDINGT "
+      "MIT EINEM MAGIER ANLEGEN?\n\n"]));
+  SetProp(P_ATTRIBUTES_OFFSETS,([]));
+  SetProp(P_AVERAGE_SIZE,185);
+  if(!QueryProp(P_DEFAULT_GUILD)) SetProp(P_DEFAULT_GUILD,"abenteurer");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+
+protected void heart_beat()
+{
+  mixed *en;
+
+  if (!QueryProp(P_WANTS_TO_LEARN)||((en=QueryEnemies())&&sizeof(en[0])))
+    base::heart_beat();
+  else if (!CheckTelnetKeepAlive()) {
+    // Wenn der Magier kein Telnet Keep-Alive wuenscht, kann der HB ganz
+    // abgeschaltet werden. Sonst muss er aber weiterlaufen, damit
+    // CheckTelnetKeepAlive() regelmaessig gerufen wird.
+    set_heart_beat(0);
+  }
+}
+
+
+public varargs int remove(int silent)
+{
+  string workroom;
+
+  if (IS_WIZARD(this_object()))
+    workroom = "/players/"+getuid()+"/workroom";
+  else
+    workroom = "/secure/merlin";
+  if( !environment() || object_name(environment()) != workroom )
+    catch(move(workroom, M_GO, "nach Hause"));
+  return base::remove(silent);
+}
+
+public string NotifyDestruct(object caller) {
+
+  if (previous_object() != master()
+      || object_name(this_object()) == __FILE__[..<3])
+    return 0;
+  
+  // Nicht-EMs sollen keine EMs zerstoeren koennen, woraufhin auch evtl.
+  // EM-Tools rumliegen koennten.
+  if ( IS_ARCH(this_object()) && caller != this_object() 
+      && getuid(caller) != ROOTID
+      && (process_call() || !ARCH_SECURITY) )
+    return "Das Zerstoeren von EMs ist ein Fehler. ;-)\n";
+
+  return ::NotifyDestruct(caller);
+}
+
+void reset()
+{
+  if (!interactive(this_object()))
+  {
+    quit();
+    if (this_object())
+      remove();
+    if (this_object())
+        destruct(this_object());
+    return;
+  }
+}
+
+//                ####################
+//################# Query-Funktionen ##################
+//                ####################
+
+varargs int id (string str) {
+  if (QueryProp(P_INVIS) &&
+      (!this_interactive() ||!IS_LEARNER(this_interactive())))
+    return 0;
+  return ::id(str);
+}
+
+
+static string *_query_racestring()
+{
+  if (pointerp(Query(P_RACESTRING)))
+    return Query(P_RACESTRING);
+  else
+    return
+    ({QueryProp(P_RACE),QueryProp(P_RACE),
+      QueryProp(P_RACE),QueryProp(P_RACE)});
+}
+
+
+static string _query_default_guild()
+{
+    return (Query(P_DEFAULT_GUILD)||"abenteurer");
+}
+
+
+static string _query_racedescr()
+{
+  return "Magier koennen einfach alles. Aber manche Magier koennen mehr.\n";
+}
+
+
+static string _query_race()
+{
+  if (previous_object() && previous_object()->query_login_object())
+    return 0;
+
+  return Query(P_RACE) ? Query(P_RACE) : Set(P_RACE, "Magier");
+}
+
+
+static mixed _query_localcmds()
+{
+  return
+    base::_query_localcmds()
+    +magier_ext::_query_localcmds();
+}
+
+
+static void upd_my_age()
+{
+  age=_age+absolute_hb_count()-_hbstop;
+  _age=age;
+  _hbstop=absolute_hb_count();
+  return;
+}
+
+
+static int _query_age()
+{
+  upd_my_age();
+  return age;
+}
+
+static int _set_earmuffs(int level)
+{
+  int maxl=1+query_wiz_level(this_object());
+  maxl = max(maxl,99);
+  return Set(P_EARMUFFS,min(maxl,level));
+}
+
+
+//                   ############################
+//#################### Interne Shell-Funktionen ####################
+//                   ############################
+
+int MayAddWeight(int w) { return 0;}
+int MayAddObject(object ob) { return 1; }
+
+
+static void initialize()
+{
+  magier_ext::initialize();
+  return;
+}
+
+
+static void FinalSetup()
+{
+  SetProp(P_CURRENTDIR,"/players/"+getuid());
+  initialize();
+  if (IS_LEARNER(this_player())) cat("/etc/WIZNEWS");
+  _age=age;
+  _hbstop=absolute_hb_count();
+  return;
+}
+
+
+void save_me(int i)
+{
+  upd_my_age();
+  base::save_me(i);
+  return;
+}
+
+
+varargs void Reconnect(int silent,string my_ip)
+{
+  base::Reconnect(silent,my_ip);
+  magier_ext::reconnect();
+  return;
+}
+
+
+void notify_player_change(string who, int rein, int invis)
+{
+  string *list,name;
+  mixed mlist;
+  int vis_change;
+
+  if (invis) name="("+who+")";
+    else name=who;
+
+  if(query_verb() && (query_verb()=="vis" || query_verb()=="invis"))
+    vis_change=1;
+
+  if (Query(P_INFORMME) && !vis_change)
+  {
+    if (rein) 
+      tell_object(this_object(),
+                  sprintf("%s ist gerade ins "MUDNAME" gekommen.\n",name));
+    else
+      tell_object(this_object(),
+                  sprintf("%s hat gerade das "MUDNAME" verlassen.\n",name));
+  }
+
+  if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
+
+  if (pointerp(list=Query(P_WAITFOR))&&sizeof(list))
+    if (member(list,who)!=-1)
+      delayed_write(
+         ({
+           ({sprintf("%s%s   I S T   J E T Z T   %s !!!\n",
+                     (QueryProp(P_VISUALBELL) ? "" : sprintf("%c",7)),
+                     name,
+                     (vis_change?
+                       (rein?"S I C H T B A R":"U N S I C H T B A R"):
+                       (rein?"D A":"N I C H T   M E H R   D A"))),
+           0})
+         }));
+
+  if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
+     (mappingp(mlist)) && (mlist[who]))
+        Show_WaitFor_Reason(who,invis);
+  return;
+}
+
+mixed modify_command(string str) {
+  if (previous_object() &&
+      (previous_object()!=this_object() || process_call()) )
+  {
+    if (IS_ARCH(this_object()))
+      tell_object(this_object(),
+        sprintf("Illegal modify_command(%s) from %O\n",
+        str, previous_object()));
+    return 0;
+  }
+  //////////////////////////////////////////////////////////////////////
+  // Magier-Escape-Kommandos werden behandelt 
+  if (str=="\\ESCAPE" && IS_LORD(this_object()))
+  {
+    __set_environment(this_object(),"/room/void");
+    environment()->init();
+    printf("You escaped.\n");
+    return "";
+  }
+  if (str[0..2]=="\\\\\\" && IS_LORD(this_object()))
+  {
+    str = _return_args(str);
+    string* input = explode(str[3..]," ");
+    string verb = input[0];
+    if (verb && verb!="")
+    {
+      string cmd = implode(input[1..]," ");
+      if (!__auswerten(cmd,verb))
+        SoulComm(cmd,verb);
+    }
+    return 1;
+  }
+  //////////////////////////////////////////////////////////////////////
+
+  return ::modify_command(str);
+}
+
diff --git a/std/shells/magier/admin.c b/std/shells/magier/admin.c
new file mode 100644
index 0000000..a41d1b7
--- /dev/null
+++ b/std/shells/magier/admin.c
@@ -0,0 +1,428 @@
+// MorgenGrauen MUDlib
+//
+// admin.c
+//
+// $Id: admin.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <udp.h>
+#include <wizlevels.h>
+#include <input_to.h>
+
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <player.h>
+
+inherit "/std/util/cidr";
+
+mixed _query_localcmds()
+{
+  return ({({"udpq","_udpq",0,LEARNER_LVL}),
+           ({"shutdown","shut_down_game",0,ARCH_LVL}),
+           ({"addmaster","_addmaster",0,GOD_LVL}),
+           ({"removemaster","_removemaster",0,GOD_LVL}),
+           ({"addguildmaster", "_addguildmaster", 0, GOD_LVL}),
+           ({"removeguildmaster", "_removeguildmaster", 0, GOD_LVL}),
+           ({"suender","sinners",0,WIZARD_LVL}),
+           ({"banish","banish", 0, WIZARD_LVL}),
+           ({"mbanish","mbanish", 0, WIZARD_LVL}),
+           ({"tbanish","tbanish", 0, WIZARD_LVL}),
+           ({"sbanish","sbanish", 0, WIZARD_LVL})});
+}
+
+static int _udpq(string str)
+{
+  string ret, mud, type;
+
+  if (!(str=_unparsed_args()) || str=="" || sscanf(str,"%s %s",mud,type)<2)
+  {
+    write("Syntax: udpq mud type\n");
+    return 1;
+  }
+  if (member(({"commands","email","hosts","inetd","list","mud_port","time",
+         "version"}),type)==-1)
+    write("TYPEs: commands, email, hosts, inetd, list, mud_port, time, version\n");
+  if (ret=(string)INETD->_send_udp(mud,([SENDER:getuid(), REQUEST:QUERY, DATA:type]),1))
+    write(ret);
+  else
+    write("Anfrage abgeschickt.\n");
+  return 1;
+}
+
+static int shut_down_game(string str)
+{
+  if (!IS_ARCH(this_object()) || this_object()!=this_interactive())
+    return 0;
+  _notify_fail("Du musst einen Grund dafuer angeben.\n");
+  if (!str) return 0;
+  write( "Direkter shutdown mit Grund \""+str+"\"?\n" );
+  input_to("shut_down_game_2",INPUT_PROMPT, "(ja/nein) :", str);
+  return 1;
+}
+
+static int shut_down_game_2(string arg,string str)
+{
+  if (!IS_ARCH(this_object()) || this_object()!=this_interactive())
+    return 0;
+  if( arg!="ja" ) {
+    write( "Shutdown abgebrochen.\n" );
+  } else {
+    shutdown(str);
+  }
+  return 1;
+}
+
+
+static int _addmaster(string str)
+{
+  string master, domain;
+
+  if (!GOD_SECURITY)
+  {
+    write("Das darfst Du nicht!\n");
+    return 1;
+  }
+  _notify_fail("Syntax: addmaster <user> <domain>\n");
+  if (!str) return 0;
+  if (sscanf(str,"%s %s",master,domain)!=2) return 0;
+  if (!master || !domain) return 0;
+  if (!"/secure/master"->add_domain_master(master,domain))
+    write("Hat nicht funktioniert.\n");
+  else
+    write("Ok.\n");
+  return 1;
+}
+
+static int _removemaster(string str)
+{
+  string master, domain;
+
+  if (!GOD_SECURITY)
+  {
+    write("Das darfst Du nicht!\n");
+    return 1;
+  }
+  _notify_fail("Syntax: removemaster <user> <domain>\n");
+  if (!str) return 0;
+  if (sscanf(str,"%s %s",master,domain)!=2) return 0;
+  if (!master || !domain) return 0;
+  if (!"/secure/master"->remove_domain_master(master,domain))
+    write("Hat nicht funktioniert.\n");
+  else
+    write("Ok.\n");
+  return 1;
+}
+
+static int _addguildmaster(string str)
+{
+  string master, guild;
+  
+  if (!GOD_SECURITY)
+  {
+    write ("Das darfst Du nicht!\n");
+    return 1;
+  }
+
+  _notify_fail("Synatx: addguildmaster <user> <guild>\n");
+  if (!str) return 0;
+  if (sscanf(str, "%s %s", master, guild)!=2) return 0;
+  if (!master || !guild) return 0;
+  if (!"/secure/master"->add_guild_master(master,guild))
+    write("Hat nicht funktioniert.\n");
+  else
+    write ("Ok.\n");
+  return 1;
+}
+
+static int _removeguildmaster(string str)
+{
+  string master, guild;
+  
+  if (!GOD_SECURITY)
+  {
+    write ("Das darfst Du nicht!\n");
+    return 1;
+  }
+  _notify_fail("Syntax: removeguildmaster <user> <guild>\n");
+  if (!str) return 0;
+  if (sscanf(str, "%s %s", master, guild)!=2) return 0;
+  if (!master || !guild) return 0;
+  if (!"/secure/master"->remove_guild_master(master,guild))
+    write("Hat nicht funktioniert.\n");
+  else
+    write("Ok.\n");
+  return 1;
+}
+
+static int sinners(string arg)
+{   string *parts;
+    int    i;
+
+    if ( !IS_DEPUTY(this_object()) )
+      return 0;
+
+    arg=_unparsed_args()||arg;
+
+    notify_fail(
+        "Syntax: suender ?              => Liste aller Eingetragenen\n"+
+        "        suender <name>         => Eintraege lesen\n"+
+        "        suender +<name> <text> => Eintrag hinzufuegen\n"+
+        "        suender -<name> <nr>   => Eintrag loeschen\n"+
+        "        suender !              => Alle Eintraege dumpen\n"+
+        "        suender *              => Alle Eintraege anzeigen\n");
+
+    if ( !stringp(arg) || (sizeof(arg)<1) )
+      return 0;
+
+    if ( arg=="?" )
+    {
+        write(call_other("/secure/sinmaster","ListSinners"));
+        return 1;
+    }
+    if ( arg=="!" )
+    {
+        write(call_other("/secure/sinmaster","Dump"));
+        return 1;
+    }
+    if ( arg=="*" )
+    {
+        More((string)call_other("/secure/sinmaster","Dump",1));
+        return 1;
+    }
+
+    if ( (i=sizeof(parts=explode(arg," ")))<1 )
+      return 0;
+
+    if ( parts[0][0..0]=="-" )
+    {
+      if ( i<2 )
+        return 0;
+      write(call_other("/secure/sinmaster","RemoveSin",
+            lowerstring(parts[0][1..]),
+            to_int(parts[1])));
+    }
+    else if ( parts[0][0..0]=="+" )
+    {
+      if ( i<2 )
+        return 0;
+      write(call_other("/secure/sinmaster","AddSin",
+            lowerstring(parts[0][1..]),
+            implode(parts[1..]," ")));
+    }
+    else
+    {
+      if ( i>1 )
+        return 0;
+      write(call_other("/secure/sinmaster","ListSins",
+            lowerstring(parts[0])));
+    }
+    return 1;
+}
+
+static int banish(string str)
+{
+  string grund, name;
+  int force;
+
+  if ( !LORD_SECURITY && !IS_DEPUTY(secure_euid()) )
+      return 0;
+
+  if ( !str || !stringp(str) || !sizeof(str) ) {
+      write("Syntax: banish [-f] <name> [<grund>]\n");
+      return 1;
+  }
+
+  str = _unparsed_args();
+
+  if ( explode(str, " ")[0] == "-f" ){
+      str = implode( explode(str, " ")[1..], " " );
+      force = 1;
+  }
+
+  if ( sscanf( str, "%s %s", name, grund ) != 2 )
+      name=str;
+
+  if ( !name || !sizeof(name) ){
+    write("Syntax: banish [-f] <name> [<grund>]\n");
+    return 1;
+  }
+
+  name=lower_case(name);
+  "/secure/master"->BanishName( name, grund, force );
+  return 1;
+}
+
+static int mbanish(string str)
+{
+    string grund, name, *namen, txt, *dummy;
+    mapping list;
+    int i;
+
+    if ( !IS_DEPUTY(secure_euid()) )
+        return 0;
+
+    _notify_fail( "Syntax: mbanish <name> [<grund>]\n" );
+
+    if ( !str || !stringp(str) || !sizeof(str) ){
+        if ( !mappingp(list = (mapping)"/secure/merlin"->MBanishList()) ||
+             !(i = sizeof(list)) ){
+            write( "Momentan ist kein Spieler auf der mbanish-Liste.\n" );
+            return 1;
+        }
+
+        txt = "      Name     | gebanisht von |                    Grund\n" +
+            "=============================================================" +
+            "==================\n";
+
+        namen = sort_array( m_indices(list), #'</*'*/ );
+
+        for ( ; i--; ){
+            dummy = explode( break_string( list[namen[i],0] ||
+                                                 "-- keine Begruendung --", 45 ),
+                                   "\n" ) - ({""});
+
+            txt += sprintf( "  %-11s  |  %-11s  |  %s\n",
+                            capitalize( namen[i] ),
+                            capitalize( list[namen[i],1] || "" ),
+                            capitalize( implode( dummy, "\n               |"
+                                                 "               |  " ) ) );
+        }
+
+        More(txt);
+
+        return 1;
+    }
+
+    if ( sscanf( str, "%s %s", name, grund ) !=2 )
+        name = str;
+
+    if ( !name || !sizeof(name) )
+        return 0;
+
+    name = lower_case(name);
+
+    if ( !grund || !stringp(grund) || lower_case(grund) != "loeschen" ){
+        "/secure/merlin"->MBanishInsert( name, grund, this_interactive() );
+        write( "Du setzt "+capitalize(name)+" auf die MBanish-Liste.\n" );
+    }
+    else{
+        if ( !ARCH_SECURITY ){
+            write( "Das duerfen nur Erzmagier.\n" );
+            return 1;
+        }
+        "/secure/merlin"->MBanishDelete( name );
+        write( "Du loescht "+capitalize(name)+" von der MBanish-Liste.\n" );
+    }
+
+    return 1;
+}
+
+
+static int tbanish( string str )
+{
+  string name;
+  int days;
+
+  if ( !IS_DEPUTY(secure_euid()) )
+      return 0;
+
+  _notify_fail("Syntax: tbanish <name> <tage>\n");
+
+  if ( !str || !stringp(str) || !sizeof(str) )
+    return 0;
+
+  if ( sscanf(str,"%s %d",name,days) != 2 )
+      return 0;
+
+  if ( !name || !sizeof(name) )
+    return 0;
+
+  name = lower_case(name);
+
+  if ( !"/secure/master"->TBanishName( name, days ) )
+      return 1;
+
+  if ( !days )
+      write( "Okay, keine Spielpause fuer "+capitalize(name)+" mehr.\n" );
+  else
+      write( "Du verpasst "+capitalize(name)+" eine Spielpause fuer "+
+             (days>0 ? days+" Tage" : "laaaange Zeit")+".\n" );
+  return 1;
+}
+
+static int sbanish( string str )
+{
+    string ip;
+    int days;
+    mapping sites;
+
+    // Mindestens L26 fuer diesen Befehl
+    if ( secure_level() <= DOMAINMEMBER_LVL )
+        return 0;
+
+    if ( !str || !stringp(str) || !sizeof(str) ){
+        if ( !sizeof(sites = (mapping)MASTER->SiteBanish( 0, 0 )) ){
+            write( "Es sind zur Zeit keine Adressen gesperrt!\n" );
+            return 1;
+        }
+
+        ip = "      Adresse      |  gesperrt bis                 |  gesperrt "
+            + "durch\n========================================================"
+            + "==============\n";
+
+        int *keys = sort_array( m_indices(sites), #'</*'*/ );
+
+        foreach(int key : keys) {
+            ip += sprintf( "  %:15-s  |  %:27-s  |  %-s\n",
+                           IPv4_int2addr(key),
+                           sites[key] > 0 ? dtime(sites[key]) :
+                           "St. Nimmerleinstag", 
+                           capitalize(sites[key, 1]) );
+        }
+        write( ip + "\n" );
+        return 1;
+    }
+
+    _notify_fail("Syntax: sbanish <numerische ip> <tage>\n");
+
+    if ( sscanf( this_player()->_unparsed_args(), "%s %d", ip, days ) != 2 )
+        return 0;
+
+    if ( !ip || !sizeof(ip) )
+        return 0;
+
+//    _notify_fail( "Ungueltiges Adress-Format!\n" );
+
+    if ( !days ){
+        int res=(int)MASTER->SiteBanish(ip, 0);
+        if ( res == 1 )
+             printf( "Die Adresse '%s' ist jetzt nicht mehr gesperrt.\n",
+                     ip );
+        else if ( res == 0 )
+             printf( "Die Adresse '%s' war gar nicht gesperrt!\n",
+                     ip );
+        else
+            printf( "Du darfst nur eigene Sperrungen wieder aufheben!\n" );
+    }
+    else {
+        int res;
+        if ( days != 1 && !IS_DEPUTY(secure_euid()) )
+            write( "Du darfst Adressen nur fuer einen Tag sperren!\n" );
+        else if ( (res = (int)MASTER->SiteBanish(ip, days)) == 1 )
+             printf( "Die Adresse '%s' ist jetzt fuer %s gesperrt.\n",
+                     ip, (days > 1 ? sprintf( "%d Tage", days ) :
+                      (days > 0 ? "einen Tag" : "immer")) );
+        else if ( res == -1 )
+            write( "Du darfst " + (LORD_SECURITY ? "255 IP-Adressen"
+                                   : "nur einzelne IP-Adressen") + " sperren!\n" );
+        else if ( res == -2 )
+            write( "Du hast schon genug Adressen gesperrt!\n" );
+    }
+
+    return 1;
+}
+
diff --git a/std/shells/magier/comm.c b/std/shells/magier/comm.c
new file mode 100644
index 0000000..0cbd266
--- /dev/null
+++ b/std/shells/magier/comm.c
@@ -0,0 +1,149 @@
+// MorgenGrauen MUDlib
+//
+// comm.c
+//
+// $Id: comm.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <magier.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <player.h>
+#include <properties.h>
+
+
+//                           ###########
+//############################ ECHOALL ################################
+//                           ###########
+
+static int _echoall(string str)
+{
+  if (!(str=_unparsed_args()))
+    return USAGE("echoall <text>\n");
+  str=break_string(str);
+  shout(str);
+  printf(str);
+  return 1;
+}
+
+//                           ##########
+//############################ ECHOTO #################################
+//                           ##########
+
+static int _echoto(string str)
+{
+  object ob;
+  string who;
+  string msg;
+
+  if (!(str=_unparsed_args())||sscanf(str, "%s %s", who, msg) != 2)
+    return USAGE("echoto <spieler> <text>\n");
+  ob = find_player(lower_case(who));
+  if (!ob)
+  {
+    printf("echoto: Es ist kein Spieler '%s' eingeloggt.\n",who);
+    return 1;
+  }
+  msg=break_string(msg,78);
+  tell_object(ob,msg);
+  printf("%s->%s",ob->Name(WEN),msg);
+  return 1;
+}
+
+//                           #########
+//############################ MECHO ##################################
+//                           #########
+
+static int _mecho(string str)  {
+  object *who;
+  int i;
+
+  if (!sizeof(str=_unparsed_args()))
+    return USAGE("mecho <text>\n");
+  who=users();
+  i=sizeof(who);
+  while(i--)if (IS_LEARNER(who[i]))
+      tell_object(who[i], break_string(str,78));
+  return 1;
+}
+
+//                            ########
+//############################# PING ##################################
+//                            ########
+
+static int _ping(string str)
+{
+  object pl;
+
+  if (!sizeof(str))
+    return USAGE("ping <spielername>\n");
+  if (!(pl=find_player(lower_case(str))))
+    return
+      _notify_fail(sprintf("ping: Spieler %s nicht gefunden.\n",
+                           capitalize(str))),0;
+  tell_object(pl,sprintf("%s pingt Dich an.\a\n",capitalize(getuid())));
+  return printf("PINGGGGGGG! DAS sollte %s auf Dich aufmerksam "
+                "gemacht haben.\n",capitalize(getuid(pl))),1;
+}
+
+//                           ##########
+//############################ OROPAX #################################
+//                           ##########
+
+int _oropax(string cmdline)
+{
+  int level,old,new;
+
+  cmdline=_unparsed_args();
+  old=QueryProp(P_EARMUFFS);
+  if (!sizeof(cmdline)||cmdline=="0")
+  {
+    if (old)
+    {
+      printf("Du nimmst das Oropax aus Deinen Ohren und hoerst "
+             "wieder allen anderen zu.\n");
+      SetProp(P_EARMUFFS, 0);
+    }
+    else
+      printf("Du hast doch gar kein Oropax in den Ohren.\n");
+    return 1;
+  }
+  if (sscanf(cmdline, "%d", level) == 0 || level < 1)
+    return USAGE("oropax [<magierlevel>]\n");
+  if ((new=SetProp(P_EARMUFFS, level))==level)
+  {
+    if (new==old)
+      return printf("Du hattest Deine Oropaxmenge schon auf Magierlevel "
+                    "%d angepasst.\n",level),1;
+    if (new>old)
+      return printf("Du stopfst Dir soviel Oropax in die Ohren, dass "
+                    "Du nur noch %s ab\nLevel %d hoerst.\n",
+                    level>=LEARNER_LVL?"Magier":"Seher",level),1;
+    return printf("Du nimmst soviel Oropax aus den Ohren, dass Du ab "
+                  "sofort wieder %s ab\nLevel %d verstehst.\n",
+                  level>=LEARNER_LVL?"Magier":"Seher",level),1;
+  }
+  return printf("Du stopfst und stopfst, bis Dir das Oropax wieder zur "
+                "Nase herauskommt.\nLeider musst Du damit Magier ab Level "
+                "%d weiterhin hoeren.\n",new),1;
+}
+
+//                         ###################
+//########################## INITIALISIERUNG #############################
+//                         ###################
+
+static mixed _query_localcmds()
+{
+  return
+    ({({"oropax","_oropax",0,LEARNER_LVL}),
+      ({"echoto","_echoto",0,LEARNER_LVL}),
+      ({"echoall","_echoall",0,LEARNER_LVL}),
+      ({"mecho","_mecho",0,LEARNER_LVL}),
+      ({"ping","_ping",0,LEARNER_LVL})});
+}
diff --git a/std/shells/magier/fileedit.c b/std/shells/magier/fileedit.c
new file mode 100644
index 0000000..f4df0a5
--- /dev/null
+++ b/std/shells/magier/fileedit.c
@@ -0,0 +1,718 @@
+// MorgenGrauen MUDlib
+//
+//fileedit.c
+//
+// $Id: fileedit.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <input_to.h>
+
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <player.h>
+#include <files.h>
+
+//                        ###################
+//######################### INITIALISIERUNG #############################
+//                        ###################
+
+mixed _query_localcmds()
+{
+  return ({ ({"cp","_cp",0,WIZARD_LVL}),
+            ({"mv","_cp",0,WIZARD_LVL}),
+            ({"rm","_rm",0,WIZARD_LVL}),
+            ({"rmdir","_rmdir",0,WIZARD_LVL}),
+            ({"mkdir","_mkdir",0,WIZARD_LVL}),
+            ({"ed","_ed",0,WIZARD_LVL})});
+}
+
+//                               ######
+//################################ ED ###################################
+//                               ######
+
+
+//
+// _ed_file: Mehrere Files hintereinander editieren
+//
+
+private nosave string *_ed_cache;
+
+private void _ed_file()
+{
+  if (!sizeof(_ed_cache)) return;
+  printf("ed: Naechste Datei: %s\n",_ed_cache[0]);
+  ed(_ed_cache[0],"_ed_file");
+  _ed_cache=_ed_cache[1..];
+  return;
+}
+
+#if __VERSION__ < "3.2.9"
+private mixed _ed_size_filter(mixed *arg)
+{
+  if (arg[FILESIZE]>=-1) return arg[FULLNAME];
+  printf("%s ist ein Verzeichnis.\n",arg[FULLNAME]);
+  return 0;
+}
+#endif
+
+//
+// _more: Dateien anzeigen
+// cmdline: Kommandozeile
+//
+
+static int _ed(string cmdline)
+{
+  mixed *args,*args2;
+  int flags,i,arg_size;
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,"",1);
+  if (flags==-1||!(arg_size=sizeof(args)))
+    return USAGE("ed <datei> [<datei2>..]");
+  while(arg_size--)
+  {
+    if (sizeof(args2=file_list(args[arg_size..arg_size],MODE_ED,0,"/")))
+      args[arg_size..arg_size]=args2;
+    else
+      args[arg_size]=({ "" , -1, 0 , (string)
+              call_other(master(),"_get_path",args[arg_size],
+                  getuid())});
+  }
+#if __VERSION__ < "3.2.9"
+  args=map(args,#'_ed_size_filter)-({0});
+#else
+  args=map(args,(:
+          if ($1[FILESIZE]>=-1) return $1[FULLNAME];
+          printf("%s ist ein Verzeichnis.\n",$1[FULLNAME]);
+          return 0; :))-({0});
+#endif
+  if (flags==-1||!sizeof(args)) return USAGE("ed <datei> [<datei2>..]");
+  _ed_cache=args;
+  _ed_file();
+  return 1;
+}
+
+
+//                             ###########
+//############################## CP & MV ################################
+//                             ###########
+
+static void _cp_ask_copy(mixed *filedata,int dummy, int flags);
+static void _cp_ask_overwrite(mixed *filedata, int mode, int flags);
+static void _mv_ask_overwrite(mixed *filedata, int mode, int flags);
+
+static mixed cp_file(mixed filedata,int move,int flags, mixed *do_delete)
+{
+  string source,dest;
+  source=(string)filedata[FULLNAME];
+  dest=(string)filedata[DESTNAME];
+  if (source==dest) return ERROR(SAME_FILE,source,RET_FAIL);
+  if (!MAY_READ(source)) return ERROR(NO_READ,source,RET_JUMP);
+  if (!MAY_WRITE(dest)) return ERROR(NO_WRITE,dest,RET_JUMP);
+  if (filedata[FILESIZE]==-1) return ERROR(DOESNT_EXIST,source,RET_JUMP);
+  if (filedata[FILESIZE]==-2) // Quelle ist Verzeichnis
+  {
+    switch(file_size(dest))
+    {
+      case -1:
+        if (move)
+        {
+          if (rename(source,dest)) return ERROR(NO_CREATE_DIR,dest,RET_JUMP);
+          if (flags&CP_V) printf(FILE_MOVED,source);
+          return RET_JUMP;
+        }
+        if (!mkdir(dest)) return ERROR(NO_CREATE_DIR,dest,RET_JUMP);
+        if (flags&CP_V) printf(DIR_CREATED,dest);
+      case -2:
+        if (!move) return RET_OK;
+        if (filedata[SUBDIRSIZE]>0) return RET_DELETE;
+        if (!rmdir(source)) return ERROR(NO_DELETE,source,RET_FAIL);
+        if (flags&MV_V) printf("mv: %s: Quellverzeichnis wurde "
+                               "geloescht.\n",source);
+        return RET_OK;    
+      default:  return ERROR(NO_DIRS,dest,RET_JUMP);
+    }
+  }
+  switch(file_size(dest))
+  {
+    case -2: return ERROR(DEST_IS_DIR,dest,RET_FAIL);
+    default:
+      if (flags&CP_F)
+      {
+        if (!rm(dest)) return ERROR(DEST_NO_DELETE,dest,RET_FAIL);
+        if (flags&CP_V) printf(FILE_DELETED,dest);
+      }
+      else
+      {
+        if (move) return #'_mv_ask_overwrite;
+        else return #'_cp_ask_overwrite;
+      }
+    case -1:
+      if (move)
+      {
+        if (rename(source,dest)) return ERROR(NO_MOVE,source,RET_FAIL);
+        if (flags&CP_V) printf(FILE_MOVED,source);
+        return RET_OK;
+      }
+      if (copy_file(source,dest)) return ERROR(NO_COPY,source,RET_FAIL);
+      if (flags&CP_V) printf(FILE_COPIED,source);
+      return RET_OK;
+  }
+  return 0; // not-reached
+}
+
+static void _cp_ask_overwrite2(string input, mixed *filedata,
+                               int interactive,int flags,int move)
+{
+  if (!sizeof(input)) input=" ";
+  input=lower_case(input);
+  switch(input[0])
+  {
+    case 'q':
+      printf("%s abgebrochen!\n",move?"Bewegen":"Kopieren");
+      return;
+    case 'a':
+      flags|=CP_F;
+      if (!(flags&CP_I))
+      {
+        asynchron(filedata,#'cp_file,move,flags,0);
+        return;
+      }
+    case 'y':
+    case 'j':
+      if (!rm(filedata[0][DESTNAME]))
+        printf(DEST_NO_DELETE "Uebergehe Datei...\n",filedata[0][DESTNAME]);
+      else
+      {
+        if (flags&CP_V) printf(FILE_DELETED,filedata[0][DESTNAME]);
+        if (move)
+        {
+          if (rename(filedata[0][FULLNAME],filedata[0][DESTNAME]))
+            printf(NO_MOVE "Uebergehe Datei...\n",filedata[0][FULLNAME]);
+        }
+        else
+        {
+          if (copy_file(filedata[0][FULLNAME],filedata[0][DESTNAME]))
+            printf(NO_COPY "Uebergehe Datei...\n",filedata[0][FULLNAME]);
+        }
+      }
+    case 'n':
+      if (flags&CP_I)
+        _cp_ask_copy(filedata[1+filedata[0][SUBDIRSIZE]..],move,flags);
+      else
+        asynchron(filedata[1+filedata[0][SUBDIRSIZE]..],
+                                     #'cp_file,move,flags,0);
+      return;
+    default:
+      printf("Kommando nicht verstanden.\n");
+      _cp_ask_overwrite(filedata,interactive,flags);
+      return;
+  }
+
+}
+
+static void _cp_ask_overwrite(mixed *filedata, int interactive, int flags)
+{
+  printf("Die Datei '%s' existiert schon.\n",
+         filedata[0][DESTNAME]);
+  input_to("_cp_ask_overwrite2",INPUT_PROMPT,"Ueberschreiben? (j,n,a,q): ",
+      filedata,interactive,flags,0);
+  return;
+}
+
+static void _mv_ask_overwrite(mixed *filedata, int interactive, int flags)
+{
+  printf("Die Datei '%s' existiert schon.",
+         filedata[0][DESTNAME]);
+  input_to("_cp_ask_overwrite2",INPUT_PROMPT,"Ueberschreiben? (j,n,a,q): ",
+      filedata,interactive,flags,1);
+  return;
+}
+
+static void _cp_ask_copy2(string input,mixed *filedata,int mode,
+                           int flags,int move)
+{
+  if (!sizeof(input)) input=" ";
+  input=lower_case(input);
+  switch(input[0])
+  {
+    case 'y':
+    case 'j':
+      if (mode==1)
+      {
+        if (!(flags&CP_F))
+        {
+          if (move) _mv_ask_overwrite(filedata,1,flags);
+          else      _cp_ask_overwrite(filedata,1,flags);
+          return;
+        }
+        if (!rm(filedata[0][DESTNAME]))
+        {
+          printf(DEST_NO_DELETE "Uebergehe Datei...\n",
+                 filedata[0][DESTNAME]);
+          _cp_ask_copy(filedata[1..],move,flags);
+          return;
+        }
+        if (flags&CP_V) printf(FILE_DELETED,filedata[0][DESTNAME]);
+      }
+      if (mode<2)
+      {
+        if (move) rename(filedata[0][FULLNAME],filedata[0][DESTNAME]);
+        else copy_file(filedata[0][FULLNAME],filedata[0][DESTNAME]);
+        _cp_ask_copy(filedata[1..],move,flags);
+        return;
+      }
+      if (mode==2)
+      {
+        if (move)
+        {
+          if (rename(filedata[0][FULLNAME],filedata[0][DESTNAME]))
+            printf(NO_MOVE "Uebergehe Verzeichnis...\n",
+                   filedata[0][FULLNAME]);
+          _cp_ask_copy(filedata[1+filedata[0][SUBDIRSIZE]..],move,flags);
+          return;
+        }
+        if (mkdir(filedata[0][DESTNAME]))
+        {
+          _cp_ask_copy(filedata[1..],0,flags);
+          return;
+        }
+        printf(NO_CREATE_DIR "Uebergehe Verzeichnis...\n",
+               filedata[0][DESTNAME]);
+      }
+    case 'n':
+      _cp_ask_copy(filedata[(1+filedata[0][SUBDIRSIZE])..],0,flags);
+      return;
+    case 'q':
+      printf("Kopieren abgebrochen!\n");
+      return;
+    case 'a':
+      flags&=~CP_I;
+      asynchron(filedata,#'cp_file,move,flags,0);
+      return;
+    default:
+      printf("Kommando nicht verstanden.\n");
+      _cp_ask_copy(filedata,0,flags);
+      return;
+  }
+}
+
+static void _cp_ask_copy(mixed *filedata,int move, int flags)
+{
+  mixed data;
+  string dest,source;
+  int delete_subs,jump;
+
+  if(!sizeof(filedata))
+  {
+    printf("%s: abgeschlossen.\n",move?"mv":"cp");
+    return; 
+  }
+  dest=filedata[0][DESTNAME];
+  source=filedata[0][FULLNAME];
+  switch(0)   // break wirkt damit wie ein goto end_of_switch
+  {
+    default:
+    case 0: // Sinnlos, aber er compiliert sonst nicht :(
+      jump=filedata[0][SUBDIRSIZE];
+      if (source==dest)
+      {
+        printf(SAME_FILE,source);
+        break;
+      }
+      if (!MAY_READ(source))
+      {
+        printf(NO_READ,source);
+        break;
+      }
+      if (!MAY_WRITE(dest))
+      {
+        printf(NO_WRITE,dest);
+        jump=0;
+        break;
+      }
+      if (filedata[0][FILESIZE]==-1)
+      {
+        printf(DOESNT_EXIST,source);
+        break;
+      }
+      if (filedata[0][FILESIZE]==-2) // Quelle ist Verzeichnis
+      {
+        if (file_size(dest)>-1)
+        {
+          printf(NO_DIRS,dest);
+          break;
+        }
+        if (file_size(dest)==-2)
+        {
+          jump=0;
+          break;
+        }
+        printf("Verzeichnis '%s' %s?\n",source,
+               move?"bewegen":"kopieren");
+        input_to("_cp_ask_copy2",INPUT_PROMPT, "(j,n,a,q) ",
+            filedata,2,flags,move);
+        return;
+      }
+      if (file_size(dest)==-2)
+      {
+        printf(DEST_IS_DIR,dest);
+        break;
+      }
+      printf("'%s' %s?\n",source,move?"bewegen":"kopieren");
+      input_to("_cp_ask_copy2",INPUT_PROMPT, "(j,n,a,q) ",
+          filedata,(file_size(dest)!=-1),flags,move);
+      return;
+  }
+  _cp_ask_copy(filedata[1+jump..],move,flags);
+  return;
+}
+
+
+static int _cp(string cmdline)
+{
+  mixed *args;
+  int flags;
+  string mask;
+  string dest,*dest2;
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,CP_OPTS,0);
+  if (flags==-1||!sizeof(args))
+    return USAGE(query_verb()+" [-" CP_OPTS
+                 "] <datei/verz> [<datei2/verz2> ... ] <ziel> [<maske>]");
+  if (flags&CP_M)
+  {
+    mask=args[<1];
+    args=args[0..<2];
+  }
+  if (!dest=to_filename(args[<1]))
+     return USAGE(query_verb()+" [-" CP_OPTS
+          "] <datei/verz> [<datei2/verz2> ... ] <ziel> [<maske>]");
+  if (file_size(dest)==-1)
+  {
+    dest2=explode(dest,"/");
+    if (file_size(implode(dest2[0..<2],"/"))==-2)
+    {
+      if (dest2[<1]=="*")
+        dest=implode(dest2[0..<2],"/");
+      else
+        if (member(dest2[<1],'*')>-1||
+            member(dest2[<1],'?')>-1)
+         return notify_fail(
+          sprintf("%s: Keine * und ? im Zielnamen erlaubt.\n",query_verb())),0;
+    }
+    else
+      return notify_fail(
+        sprintf("%s: Der angegebene Zielpfad existiert nicht.\n",
+                query_verb())),0;
+  }
+  args=args[0..<2];
+  if (file_size(dest)!=-2&&sizeof(args)>1)
+    return notify_fail(
+        sprintf("%s: Bei mehreren Quellen muss das Ziel ein Verzeichnis "
+                "sein.\n",query_verb())),0;
+  if (!sizeof(args=map(args,#'to_filename)-({0})))
+    return USAGE(query_verb()+" [-" CP_OPTS
+          "] <datei/verz> [<datei2/verz2> ... ] <ziel> [<maske>]");
+  // DEBUG("DEST: " + dest + " : FLAGS: " + flags);
+  args=file_list(args,MODE_CP,(flags&CP_R),dest+"/",mask);
+  if (!sizeof(args))
+    return notify_fail(sprintf("%s: Keine passenden Dateien gefunden.\n",
+                               query_verb())),0;
+
+  if (sizeof(args)>1&&(args[0][FILESIZE]>=0)&&file_size(dest)!=-2)
+      return notify_fail(
+        sprintf("%s: Bei mehreren Quellen muss das Ziel ein Verzeichnis "
+                "sein.\n",query_verb())),0;
+  if (sizeof(args)==1&&file_size(dest)!=-2)
+    args[0][DESTNAME]=dest;
+  if (!(flags&CP_I))
+  {
+    asynchron(args,#'cp_file,(query_verb()=="mv"),flags,0);
+    return 1;
+  }
+  if (query_verb()=="cp")
+    _cp_ask_copy(args,0,flags);
+  else
+    _cp_ask_copy(args,1,flags);
+  return 1;
+}
+
+//                              #########
+//############################### RMDIR #################################
+//                              #########
+
+
+//
+// _rmdir: Verzeichnis loeschen
+// cmdline: Kommandozeilenargumente
+//
+
+
+#if __VERSION__ < "3.2.9"
+
+private int _dir_filter(mixed arg)
+{
+  return (arg[FILESIZE]==-2);
+}
+
+#endif
+
+static int _rmdir(string cmdline)
+{
+  string dest,tmp;
+  int flags;
+  mixed *args;
+  
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,RMDIR_OPTS,1);
+  if (flags==-1||!sizeof(args))
+    return USAGE("rmdir [-" RMDIR_OPTS "] <Verzeichnis>");
+  if (sizeof(args)>1)
+    return
+    notify_fail("Mit 'rmdir' kann nur jeweils EIN Verzeichnis geloescht "
+         "werden.\nDer Befehl 'rm' bietet erweiterte Moeglichkeiten.\n"),0;
+  dest=args[0];
+  if (dest!="/")
+  {
+    args=file_list(({dest}),MODE_RMDIR,0,"/");
+#if __VERSION__ < "3.2.9"
+    args=filter(args,#'_dir_filter);
+#else
+    args=filter(args,(: ($1[FILESIZE]==-2) :));
+#endif
+    if (!sizeof(args))
+      return notify_fail(
+        sprintf("rmdir: %s: Kein solches Verzeichnis gefunden.\n",dest)),0;
+    if (sizeof(args)>1)
+      return notify_fail(
+                sprintf("rmdir: %s: Maske ist nicht eindeutig.\n",dest)),0;
+    dest=args[0][FULLNAME];
+    if (!MAY_WRITE(dest)) return ERROR(NO_WRITE,dest,1);
+    if (!rmdir(dest))
+    {
+      if (sizeof((get_dir(dest+"/*")||({}))-({".",".."})))
+        printf("rmdir: %s: Verzeichnis ist nicht leer.\n",dest);
+    }
+    else
+    {
+      if (flags&&RMDIR_V) printf(FILE_DELETED,dest);
+    }
+    return 1;
+  }
+  return ERROR(NO_DELETE,dest,1);
+}
+
+//                              #########
+//############################### MKDIR #################################
+//                              #########
+
+
+//
+// _mkdir: Verzeichnis erstellen
+// cmdline: Kommandozeilenargumente
+//
+
+static int _mkdir(string cmdline)
+{
+  string dest,tmp;
+  int flags,i;
+  string *args;
+  
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,MKDIR_OPTS,1);
+  if (flags==-1) return 0;
+  if (!sizeof(args))
+    return USAGE("mkdir [-" MKDIR_OPTS "] <Verzeichnis>");
+  if (sizeof(args)>1)
+    return notify_fail("Mit 'mkdir' kann nur jeweils EIN Verzeichnis "
+                       "erstellt werden.\n"),0;
+  dest=args[0];
+  
+  if ((i=file_size(implode((args=explode(dest,"/"))[0..<2],"/")))==FSIZE_DIR)
+  {
+    if (!mkdir(dest)) return ERROR(NO_CREATE_DIR,dest,1);
+    if (flags&MKDIR_V) printf(DIR_CREATED,dest,1);
+    printf("mkdir: abgeschlossen.\n");
+    return 1;
+  }
+  
+  if (i==FSIZE_NOFILE)
+  {
+    if (flags&MKDIR_R)
+    {
+      if (mkdirp(dest) != 1)
+        return ERROR(NO_CREATE_DIR,dest,1);
+      if (flags&MKDIR_V)
+        printf(DIR_CREATED,implode(args[0..i],"/"));
+      printf("mkdir: abgeschlossen.\n");
+      return 1;
+    }
+    return ERROR(DOESNT_EXIST,implode(args[0..<2],"/"),1);
+  }
+  return ERROR(ALREADY_EXISTS,dest,1);
+}
+
+//                               ######
+//################################ RM ###################################
+//                               ######
+
+private void _rm_ask_delete(mixed *filedata, int flags);
+
+static void _rm_ask_delete2(string input,mixed *filedata,int flags)
+{
+  int i;
+  if (!sizeof(input)) input=" ";
+  input=lower_case(input);
+  switch(input[0])
+  {
+    case 'q':
+      printf("Loeschen abgebrochen!\n");
+      return;
+    case 'y':
+    case 'j':
+      if (filedata[0][FILESIZE]==-2)
+      {
+        if (i=filedata[0][SUBDIRSIZE]) // Dir-Eintrag nach hinten schieben
+        {
+          mixed temp;
+          int j;
+          temp=filedata[0];
+          temp[SUBDIRSIZE]=0;
+          for(j=0;j<i;j++) filedata[j]=filedata[j+1];
+          filedata[j]=temp;
+          _rm_ask_delete(filedata,flags);
+          return;
+        }
+        if (!rmdir(filedata[0][FULLNAME]))
+          printf(NO_DELETE,filedata[0][FULLNAME]);
+        else if (flags&RM_V) printf(FILE_DELETED,filedata[0][FULLNAME]);
+      }
+      else // Datei existiert
+      {
+        if (!rm(filedata[0][FULLNAME]))
+          printf(DEST_NO_DELETE "Uebergehe Datei...\n",
+                 filedata[0][FULLNAME]);
+        else if (flags&RM_V) printf(FILE_DELETED,filedata[0][FULLNAME]);
+        
+      }
+    case 'n':
+      _rm_ask_delete(filedata[1+filedata[0][SUBDIRSIZE]..],flags);
+      return;
+    default:
+      printf("Kommando nicht verstanden.\n");
+      _rm_ask_delete(filedata,flags);
+      return;
+  }
+  return;
+}
+
+private void _rm_ask_delete(mixed *filedata, int flags)
+{
+  int i;
+  mixed temp;
+  if (!sizeof(filedata))
+  {
+    printf("rm: abgeschlossen.\n");
+    return;
+  }
+  switch(filedata[0][FILESIZE])
+  {
+    case -1:
+      if (flags&RM_V) printf(DOESNT_EXIST,filedata[0][FULLNAME]);
+      _rm_ask_delete(filedata[1..],flags);
+      return;
+    case -2:
+      if (i=filedata[0][SUBDIRSIZE])
+        printf("Ins Verzeichnis '%s' hinabsteigen?\n",
+          filedata[0][FULLNAME]);
+      else
+        printf("Verzeichnis '%s' loeschen?\n",
+               filedata[0][FULLNAME]);
+      input_to("_rm_ask_delete2",INPUT_PROMPT, "(j,n,q) ",
+          filedata,flags);
+      return;
+    default:
+      printf("'%s' loeschen? (j,n,q)\n",
+         filedata[0][FULLNAME]);
+      input_to("_rm_ask_delete2",INPUT_PROMPT, "(j,n,q) ",
+          filedata,flags);
+      return;
+  }
+}
+
+
+private void rm_file(mixed filedata, mixed notused, int flags)
+{
+  string dest;
+  dest=filedata[FULLNAME];
+  if (!MAY_WRITE(dest))
+  {
+    printf(NO_WRITE,dest);
+    return;
+  }
+  switch(filedata[FILESIZE])
+  {
+    case -1:
+      if (flags&RM_V) printf(DOESNT_EXIST,dest);
+      return;
+    case -2:
+      if (!rmdir(dest)) printf(DEST_NO_DELETE,dest);
+      else
+      {
+        if (flags&RM_V) printf(FILE_DELETED,dest);
+      }
+      return;
+    default:
+      if (!rm(dest)) printf(DEST_NO_DELETE,dest);
+      else
+      {
+        if (flags&RM_V) printf(FILE_DELETED,dest);
+      }
+      return;
+  }
+}
+
+static int _rm(string cmdline)
+{
+  mixed *args,*args2;
+  int flags,i;
+  string mask;
+  
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,RM_OPTS,0);
+  if (flags==-1||!sizeof(args))
+    return USAGE("rm [-" RM_OPTS
+                 "] <datei/verz> [<datei2/verz2> ... ] [<maske>]");
+  if (flags&RM_M)
+  {
+    mask=args[<1];
+    args=args[0..<2];
+  }
+  args=map(args,#'to_filename)-({0});
+  args=file_list(args,MODE_RM,(flags&RM_R),"/",mask);
+  if (!(i=sizeof(args)))
+    return printf("Keine passende Datei gefunden.\n"),1;
+  if (!(flags&RM_I))
+  {
+    if (i>1) // Umdrehen
+    {
+      mixed temp;
+      i>>=1;
+      while(i)
+      {
+        temp=args[<(i--)];
+        args[<(i+1)]=args[i];
+        args[i]=temp;
+      }
+    }
+    asynchron(args,#'rm_file,args,flags,0);
+    return 1;
+  }
+  _rm_ask_delete(args,flags);
+  return 1;
+}
diff --git a/std/shells/magier/fileview.c b/std/shells/magier/fileview.c
new file mode 100644
index 0000000..731de5a
--- /dev/null
+++ b/std/shells/magier/fileview.c
@@ -0,0 +1,728 @@
+// MorgenGrauen MUDlib
+//
+// fileview.c
+//
+// $Id: fileview.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <ansi.h>
+#include <player/base.h>
+#include <wizlevels.h>
+#include <shells.h>
+#include <daemon/mand.h>
+#include <udp.h>
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <thing/properties.h>
+#include <player.h>
+
+private nosave mapping colorstrings;
+private nosave mapping oldman_result;
+
+//                        ###################
+//######################### INITIALISIERUNG #############################
+//                        ###################
+
+mixed _query_localcmds()
+{
+  return ({({"ls","_ls",0,LEARNER_LVL}),
+           ({"more","_more",0,LEARNER_LVL}),
+           ({"cat","_cat",0,LEARNER_LVL}),
+           ({"head","_cat",0,LEARNER_LVL}),
+           ({"tail","_cat",0,LEARNER_LVL}),
+           ({"man","_man",0,LEARNER_LVL}),
+           ({"rman","_rman",0,LEARNER_LVL}),
+           ({"showprops","_showprops",0,WIZARD_LVL}),
+           ({"grep","_grep",0,LEARNER_LVL})});
+}
+
+
+//                               ######
+//################################ LS ###################################
+//                               ######
+
+//
+// _ls: Der ls-Befehl: Verzeichnisse und Dateien anzeigen
+// cmdline: Kommandozeile
+//
+
+//
+// Funktionen zum Sortieren und fuer filter_ldfied/map_ldfied
+//
+
+
+private string _get_color(string color)
+{
+  return COLORS[lower_case(color)];
+}
+
+
+private void SetColorstrings()
+{
+  string tmp,term;
+  mapping vars;
+  vars=QueryProp(P_VARIABLES);
+  colorstrings=m_allocate(0,2);
+  switch(term=QueryProp(P_TTY))
+  {
+    case "vt100":
+    case "ansi":
+      if(stringp(vars["LS_DIR"]))
+        tmp=implode(map(explode(vars["LS_DIR"],"+"),#'_get_color),
+                    "");
+      else
+        tmp = (term == "ansi") ? ANSI_BLUE+ANSI_BOLD: ANSI_BOLD;
+      colorstrings[DIR] = tmp+"%s"+(term == "vt100"?"/":"")+ANSI_NORMAL;
+      if(term == "vt100") colorstrings[DIR, 1] = 1;
+      if(stringp(vars["LS_OBJ"]))
+        tmp=implode(map(explode(vars["LS_OBJ"],"+"),#'_get_color),
+                    "");
+      else
+        tmp = (term == "ansi") ? ANSI_RED : ANSI_INVERS;
+      colorstrings[OBJ] = tmp+"%s"+ANSI_NORMAL;
+      if(stringp(vars["LS_VC"]))
+        tmp=implode(map(explode(vars["LS_VC"],"+"),#'_get_color),
+                    "");
+      else
+        tmp = (term == "ansi") ? ANSI_PURPLE : ANSI_INVERS;
+      colorstrings[VC] = tmp+"%s"+ANSI_NORMAL;
+      break;
+    case "dumb":
+      colorstrings[DIR] = "%s/"; colorstrings[DIR, 1] = 1;
+      colorstrings[OBJ] = "%s*"; colorstrings[OBJ, 1] = 1;
+      colorstrings[VC]  = "%s*"; colorstrings[VC , 1] = 1;
+      break;
+    default:
+      colorstrings[DIR] = "%s";
+      colorstrings[OBJ] = "%s";
+      colorstrings[VC]  = "%s";
+  }
+  return;
+}
+
+
+private string _ls_output_short(mixed filedata,
+                                int maxlen,int counter,int maxcount)
+{
+  string tmp;
+  tmp=filedata[BASENAME];
+  maxlen-=sizeof(tmp);
+  switch(filedata[FILESIZE])
+  {
+    case -2: tmp=sprintf(colorstrings[DIR],tmp);
+             maxlen-=colorstrings[DIR,1]; break;
+    case -1: tmp=sprintf(colorstrings[VC],tmp);
+             maxlen-=colorstrings[VC,1]; break;
+    default: if (find_object(filedata[FULLNAME]))
+             {
+               maxlen-=colorstrings[OBJ,1];
+               tmp=sprintf(colorstrings[OBJ],tmp); break;
+             }
+  }
+  if (!maxcount) return tmp+"\n";
+  return sprintf("%-*s%s",(maxlen+sizeof(tmp)),tmp,
+	((counter++)==maxcount?(counter=0,"\n"):"  "));
+}
+
+private int _ls_maxlen(mixed filedata,int flags,int maxlen)
+{
+  string base;
+  int size;
+  base=filedata[BASENAME];
+  if (!(flags&LS_A)&&(base[0]=='.')) return 0;
+#if __VERSION__ < "3.2.9"
+  if (sizeof(base)>maxlen) maxlen=sizeof(base);
+#else
+  maxlen=max(maxlen,sizeof(base));
+#endif
+  return 1;
+}
+
+private string _ls_output_long(mixed filedata, int flags,closure valid_read,
+                               closure valid_write,closure creator_file)
+{
+  string *tmp,full,base,path,date,creator,group;
+  int size,dir,ftime;
+  object ob;
+  
+  base=filedata[BASENAME];
+  if (!(sizeof(base))||((!(flags&LS_A))&&(base[0]=='.')))
+    return 0;
+  size=filedata[FILESIZE];
+  path=filedata[PATHNAME];
+  tmp=(string *)call_other(master(),"full_path_array",
+                 filedata[FULLNAME],getuid());
+  full=sprintf("/%s",implode(tmp,"/"));
+  dir=(size==-2);
+  ob=find_object(full);
+  ftime=filedata[FILEDATE];
+  date=dtime(ftime);
+  date=sprintf("%s %s %s",date[9..11],date[5..6],
+               ((time()-ftime)<31536000?date[19..23]:(" "+date[13..16])));
+  creator="";
+  group="";
+  if (flags&LS_U)
+  {
+    creator=(string)call_other(master(),"creator_file",full);
+    switch(creator)
+    {
+      case ROOTID: creator="root"; break;
+      case BACKBONEID: creator="daemon"; break;
+      default: if(!creator) creator="none"; break;
+    }
+  }
+  if (flags&LS_G)
+  {
+    if (sizeof(tmp))
+    {
+      switch(tmp[0])
+      {
+      case WIZARDDIR: group="magier"; break;
+      case "news": group="news"; break;
+      case "mail": group="mail"; break;
+      case "open": group="public"; break;
+      case "p": group="project"; break;
+      case DOMAINDIR: if (sizeof(tmp)>1) { group=tmp[1]; break; }
+      default: group="mud"; break;
+      }
+    }
+    else group="mud";
+  }
+  if (dir) base=sprintf(colorstrings[DIR],base);
+  else
+  {
+    if (ob)
+    {
+      if (size==-1)
+        base=sprintf(colorstrings[VC],base);
+      else
+        base=sprintf(colorstrings[OBJ],base);
+    }
+  }
+  return sprintf(("%c%c%c%c %3d"+((flags&LS_U)?" %-24.24s":"%-0.1s")+
+                 ((flags&LS_G)?" %-8.8s":"%0.1s")+" %8s %s %s\n"),
+                 (dir?'d':'-'),
+                 (!funcall(valid_read,full,getuid(),
+                           "read_file",this_object())?'-':'r'),
+                 (!funcall(valid_write,full,getuid(),
+                           "write_file",this_object())?'-':'w'),
+                 (ob?'x':'-'),
+                 (dir?(sizeof((get_dir(full+"/*")||({}))-({".",".."}))):0),
+                 creator,group,(dir?"-":size==-1?"<vc>":to_string(size)),
+                                    date,base);
+}
+
+
+static int _ls(string cmdline)
+{
+  int flags,cmp,i,arg_size,size;
+  int maxlen,counter,maxcount;
+  string *args,output;
+  mixed *tmp;
+  closure output_fun,v_read,v_write,creator,sort_fun;
+  
+  cmdline=_unparsed_args()||"";
+  args=parseargs(cmdline,&flags,LS_OPTS,1);
+  if (flags==-1)
+    return USAGE("ls [-" LS_OPTS "] [<Verzeichnis>] [<Verzeichnis2> ..]");
+  SetColorstrings();
+  output="";
+  if (!sizeof(args)) args=({ QueryProp(P_CURRENTDIR) });
+  if (!sizeof(args=file_list(args,MODE_LSA,0,"/")))
+    return notify_fail("ls: Keine passenden Verzeichnisse gefunden.\n"), 0;
+// Files sortieren die erste
+  if (flags&LS_T) cmp=FILEDATE;
+  else if (flags&LS_S) cmp=FILESIZE;
+  else cmp=BASENAME; // =0 :-)
+  sort_fun=lambda(({ 'a,'b }),({
+    ((!cmp&&!(flags&LS_R))||(cmp&&(flags&LS_R))?#'>:#'<),
+    ({#'[,'a,cmp}),({#'[,'b,cmp})}));
+  args=sort_array(args,sort_fun);
+// Ausgabeformat bestimmen
+  if (flags&LS_L)
+  {
+    v_read=VALID_READ_CL;
+    v_write=VALID_WRITE_CL;
+    creator=CREATOR_CL;
+  }
+  arg_size=sizeof(args);
+  if (arg_size>1||(arg_size&&args[0][FILESIZE]>=0))
+  {
+    if (flags&LS_L)
+      tmp=map(args,#'_ls_output_long,flags,v_read,
+                    v_write,creator)-({0});
+    else
+    {
+      counter=0;maxlen=0;
+      tmp=filter(args,#'_ls_maxlen,flags,&maxlen);
+      if (maxlen>76) maxlen=76;
+      maxcount=(78/(maxlen+2))-1;
+      tmp=map(tmp,#'_ls_output_short,maxlen,&counter,maxcount);
+    }
+    output=sprintf("\n%d Dateien/Unterverzeichnisse:\n%s\n",
+                   sizeof(tmp),implode(tmp,""));
+  }
+  for(i=0;i<arg_size;i++)
+  {
+    tmp=({});
+    size=args[i][FILESIZE];
+    if (size==-2)
+    {
+      tmp=file_list(({args[i][FULLNAME]+"/*"}),MODE_LSB,0,"/");
+      tmp=sort_array(tmp,sort_fun);
+      if (flags&LS_L)
+        tmp=map(tmp,#'_ls_output_long,flags,v_read,
+                      v_write,creator)-({0});
+      else
+      {
+        counter=0;maxlen=0;
+        tmp=filter(tmp,#'_ls_maxlen,flags,&maxlen);
+        maxcount=(78/(maxlen+2));
+        if (maxcount) maxcount--;
+        tmp=map(tmp,#'_ls_output_short,maxlen,&counter,maxcount);
+      }
+      if (sizeof(tmp))
+      {
+        output+=sprintf("\n%s: Verzeichnis, %d Dateien/Unterverzeichnisse:\n",
+                      args[i][FULLNAME],sizeof(tmp));
+        output+=(implode(tmp,"")+((string)(tmp[<1][<1..<1])=="\n"?"":"\n"));
+      }
+      else
+      {
+        output+=sprintf("\n%s: Leeres Verzeichnis.\n",args[i][FULLNAME]);
+      }
+    }
+  }
+  More(output);
+  return 1;
+}
+
+//                              ########
+//############################### MORE ###################################
+//                              ########
+//
+// _more_file: Mehrere Files hintereinander ausgeben
+//
+
+private void _more_file(string *arg)
+{
+  if (!sizeof(arg)) return;
+  printf("more: Naechste Datei: %s\n",arg[0]);
+  More(arg[0],1,#'_more_file,({ arg[1..]}));
+  return;
+}
+
+
+private mixed _size_filter(mixed *arg)
+{
+  if (arg[FILESIZE]>0) return arg[FULLNAME];
+  if (arg[FILESIZE]==0)
+  {
+    printf("%s: %s: Leere Datei.\n",query_verb()||"more",arg[FULLNAME]);
+    return 0;
+  }
+  if (arg[FILESIZE]==-2)
+    printf("%s: %s ist ein Verzeichnis.\n",query_verb()||"more",arg[FULLNAME]);
+  else
+    printf("%s: %s: Datei existiert nicht.\n", query_verb()||"more",
+           arg[FULLNAME]);
+  return 0;
+}
+
+
+//
+// _more: Dateien anzeigen
+// cmdline: Kommandozeile
+//
+
+static int _more(string cmdline)
+{
+  mixed *args;
+  int flags;
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,"",1);
+  if (flags==-1||!sizeof(args)) return USAGE("more <datei> [<datei2>..]");
+  args=file_list(args,MODE_MORE,0,"/");
+  if (!sizeof(args))
+    return printf("more: %s: Keine passende Datei gefunden.\n",cmdline),1;
+  args=map(args,#'_size_filter)-({0});
+  if (sizeof(args)) _more_file(args);
+  return 1;
+}
+
+//                         ###################
+//########################## HEAD, TAIL, CAT #############################
+//                         ###################
+
+static int _cat(string cmdline)
+{
+  mixed *args;
+  int flags;
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,"",1);
+  if(flags==-1||!sizeof(args))
+    return USAGE(query_verb()+" <dateiname> [<datei2>..]");
+  args=file_list(args,MODE_CAT,0,"/");
+  if (!sizeof(args))
+    return printf("%s: %s: Keine passende Datei gefunden.\n",query_verb(),
+                cmdline),1;
+  args=map(args,#'_size_filter)-({0});
+  if (!sizeof(args)) return 1;
+  while(sizeof(args))
+  {
+    switch(query_verb())
+    {
+      case "cat":
+        if (!cat(args[0]))
+          printf("cat: %s konnte nicht angezeigt werden.\n",args[0]);
+        break;
+      case "head":
+        if (!cat(args[0],0,10))
+          printf("head: %s konnte nicht angezeigt werden.\n",args[0]);
+        break;
+      case "tail": tail(args[0]); break;
+    }
+    args=args[1..];
+  }
+  return 1;
+}
+
+//                              #######
+//############################### MAN ###################################
+//                              #######
+
+static int _man(string cmdline)
+{
+  mixed *args;
+  int i, flags;
+  string *tmp, *tmp2;
+
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,MAN_OPTS,0);
+  
+  if (flags==-1 ||
+      (sizeof(args)!=1 &&
+       (sizeof(args)<2 || sizeof(args[1])>1 || !(i=to_int(args[1])))))
+    return USAGE("man [-" MAN_OPTS "] <hilfeseite>");
+  tmp=explode(args[0],"/");
+
+  if (oldman_result && sizeof(tmp)==1 && sizeof(args)==1 &&
+      sizeof(tmp[0])==1 && (i=to_int(tmp[0])) && member(oldman_result,i)) {
+   tmp=({oldman_result[i,0],oldman_result[i,1]});
+   i=0;
+  }
+  else if (!(flags&(MAN_M|MAN_R))&&sizeof(tmp)>1)
+  {
+    if (file_size(MAND_DOCDIR+args[0])>=0)
+      tmp=({tmp[<1],args[0]});
+    else
+      tmp=({});
+  }
+  else
+  {
+    if (flags&MAN_R)
+    {
+      flags&=(~MAN_M);
+      if (catch(regexp(({""}),args[0]))||
+          !regexp(({""}),args[0]))
+        return printf("man: Ungueltiger Ausdruck in Maske.\n"),1;
+    }
+    tmp=(string *)call_other(MAND,"locate",args[0],flags&(MAN_M|MAN_R));
+  }
+
+  oldman_result=(mapping)0;
+  if(i && sizeof(tmp)>2 && sizeof(tmp)>=(i<<1))
+    tmp=tmp[((i<<1)-2)..((i<<1)-1)];
+  switch(sizeof(tmp))
+  {
+    case 0:
+      printf("Keine Hilfeseite gefunden fuer '%s'.\n",args[0]);
+      break;
+    case 2:
+       /*
+         if (flags&MAN_I)
+         {
+         // BRANCH TO MANUALD
+         return 1;
+         }
+       */
+      printf("Folgende Hilfeseite wurde gefunden: %s\n",tmp[1]);
+      More(MAND_DOCDIR+tmp[1],1);
+      return 1;
+    default:
+      i=sizeof(tmp)>>1;
+      tmp2=allocate(i);
+      oldman_result=m_allocate(i,2);
+      while(i)
+      {
+        tmp2[(i-1)]=sprintf("%d: ",i)+tmp[(i<<1)-2];
+	oldman_result[i,0]=tmp[(i<<1)-2];
+	oldman_result[i,1]=tmp[(i<<1)-1];
+        i--;
+      }
+      printf("Es wurden folgende potentiell passenden Seiten gefunden:\n"
+             "%'-'78.78s\n%s%'-'78.78s\n","",
+             break_string(implode(tmp2," "),78),"");
+      break;
+  }
+  return 1;
+}
+
+//                              ########
+//############################### RMAN ##################################
+//                              ########
+
+static int _rman(string str)
+{
+  int flags;
+  string *args;
+  
+  str=_unparsed_args();
+  args=parseargs(str,&flags,"",0);
+  if (flags==-1||sizeof(args)!=2)
+    return USAGE("rman <hilfeseite> <mudname>");
+  write("man: " +
+        (string)call_other(UDP_CMD_DIR+"man","send_request",args[1],args[0]));
+  return 1;
+}
+
+
+//                            #############
+//############################# SHOWPROPS ###############################
+//                            #############
+
+//
+// _showprops: Zeigt Properties zu einem Objekt an
+//
+
+static int _showprops(string str)
+{
+   int i;
+   string *list, ausgabe;
+
+   if (str) {
+      if (str[0]!='/') str="/"+str;
+      if (str[0..4]!="/std/")
+      {
+         printf("showprops: Es koennen nur Properties von Objekten "
+                "aus /std/ angezeigt werden.\n");
+         return 1;
+      }
+      if (str[<1]=='.') str+="c";
+      if (str[<2..<1]!=".c") str+=".c";
+      if (file_size(str)<0)
+      {
+        printf("showprops: %s: Es gibt kein Objekt diesen Namens.\n",str[0..<3]);
+        return 1;
+      }
+      if (catch(call_other(str[0..<3], "???")))
+      {
+        printf("showprops: %s: Datei konnte nicht geladen werden.\n",str);
+        return 1;
+      }
+   }
+   if (!str || !find_object(str)) {
+      notify_fail("Welche Properties moechtest Du sehen?"
+                  " Bsp.: <showprops /std/npc>\n");
+      return 0;
+   }
+   list=inherit_list(find_object(str));
+#if __VERSION__ < "3.2.9"
+   list=map(list,lambda(({'x}),({#'extract,'x,4,-2})));
+   list+=map(list,lambda(({'x}),({#'[<,({#'old_explode,'x,"/"}),1})));
+   list=map(m_indices(mkmapping(list)),lambda(({'x}),({#'+,({#'+,"/sys/",'x}),"h"})));
+   list=filter(list,lambda(({'x}),({#'>,({#'file_size,'x}),0})) );
+#else
+   list=map(list,(: return $1[5..<2]+"h"; :));
+   list+=map(list,(: return explode($1,"/")[<1]; :));
+   list=map(m_indices(mkmapping(list)),(: return "/sys/"+$1; :));
+   list=filter(list,(: return file_size($1)>0; :));
+#endif
+   list=sort_array(list, #'<);
+   ausgabe="";
+   for (i=sizeof(list);i--;)
+   {
+#if __VERSION__ < "3.2.9"
+     str=implode(filter(old_explode(read_file(list[i]), "\n"),
+        lambda( ({ 'x }), ({#'==, ({#'extract, 'x, 0, 9}), "#define P_"}) ))
+                 , "\n");
+#else
+     str=implode(filter(explode(read_file(list[i]),"\n"),
+                              (: return $1[0..9]=="#define P_";:)),"\n");
+#endif
+     if (str!="") ausgabe+=sprintf("%s\n%s\n\n", list[i], str);
+   }
+   if (ausgabe!="")
+     More(ausgabe);
+   else
+     printf("Keine Property-Definitionen zu diesem Objekt gefunden!\n");
+   return 1;
+}
+
+//                              ########
+//############################### GREP ###################################
+//                              ########
+
+#if __VERSION__ < "3.2.9"
+
+private int _grep_filter(string filename)
+{
+  return (call_other("valid_write",filename,getuid(this_object()),
+                      "write_file",this_object())!=0);
+}
+
+#endif
+
+//
+// grep_file: Datei greppen
+// rexpr: Regular Expression
+// flags: Flags
+//
+
+private int grep_file(mixed filedata, string rexpr, int flags)
+{
+  string fullname,data,carry,*lines,*lines_orig,*tmp,*result;
+  int ptr,count,i,nol,match,index;
+  fullname=filedata[FULLNAME];
+  if ((flags&GREP_F)&&fullname=="/players/"+getuid()+"/grep.out")
+  {	 
+	write_file("/players/"+getuid()+"/grep.out",
+                   "Uebergehe grep.out ...\n");
+	return RET_FAIL;
+  }
+  switch(filedata[FILESIZE])
+  {
+    case -2: return RET_FAIL;
+    case -1: return ERROR(DOESNT_EXIST,fullname,RET_FAIL);
+    case 0:  return RET_FAIL;
+    default: break;
+  }
+  if (!MAY_READ(fullname)) return ERROR(NO_READ,fullname,RET_FAIL);
+  carry=""; result=({});
+  if (flags&GREP_I)
+    rexpr=lower_case(rexpr);
+  do
+  {
+    data=read_bytes(fullname,ptr,MAXLEN)||"";
+    ptr+=MAXLEN;
+    lines=explode(carry+data,"\n");
+    switch(sizeof(lines))
+    {
+      case 0: continue;
+      case 1:
+        carry="";
+        break;
+      default:
+        carry=lines[<1];
+        lines=lines[0..<2];
+        break;
+    }
+    lines_orig=lines;
+    if (flags&GREP_I)
+      lines=map(lines,#'lower_case);
+    nol=sizeof(lines);
+    for (i=0;i<nol;i++)
+    {
+      match=sizeof(regexp(lines[i..i],rexpr));
+      if (flags&GREP_V) match=!match;
+      if (match)
+      {
+        if (!(flags&GREP_C))
+        {
+          if (flags&GREP_N)
+            result+=({ sprintf("%4d %s",index+i+1,lines_orig[i])});
+          else
+            result+=lines_orig[i..i];
+        }
+        count++;
+      }
+    }
+    index+=nol;
+  }
+  while(sizeof(data)==MAXLEN);
+  if (carry!="")
+  {
+    if (flags&GREP_I) carry=lower_case(carry);
+    match=sizeof(regexp(({ carry }),rexpr));
+    if (flags&GREP_V) match=!match;
+    if (match)
+    {
+      if(!(flags&GREP_C))
+      {
+        if(flags&GREP_N)
+           result+=({ sprintf("%4d %s",index+1,carry) });
+        else
+           result+=({carry});
+      }
+      count++;
+    }
+  }
+  carry="";
+  if (count)
+  {
+    if (flags&GREP_L) carry=sprintf("%s\n",fullname);
+    else if (flags&GREP_H) data="";
+    else data=sprintf(" %s:",fullname);
+    if (flags&GREP_C) carry=sprintf("%s %d passende Zeile%s.\n",data,count,
+                             (count==1?"":"n"));
+    else
+      carry=(data+"\n"+implode(result,"\n")+"\n");
+  }
+  if (flags&GREP_F)
+    return write_file("/players/"+getuid()+"/grep.out",carry);
+  write(carry);
+  return RET_OK;
+}
+
+static int _grep(string cmdline)
+{
+  string rexpr,mask;
+  mixed *args;
+  int flags;
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,GREP_OPTS,0);
+  if ((flags==-1)||!sizeof(args))
+    return USAGE("grep [-" GREP_OPTS
+          "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
+  rexpr=args[0];
+  if (catch(regexp(({""}),rexpr))||!regexp(({""}),rexpr))
+    return printf("grep: Ungueltiger Suchausdruck: %s\n",rexpr) ,1;
+  args=args[1..];
+  if (flags&GREP_M)
+  {
+    mask=args[<1];
+    args=args[0..<2];
+  }
+  if (!sizeof(args))
+      return USAGE("grep [-" GREP_OPTS
+          "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
+  args=map(args,#'to_filename)-({0});
+  /*
+#if __VERSION__ < "3.2.9"
+  args=filter(args,#'_grep_filter);
+#else
+  args=filter(args,(: return (valid_write($1,
+            getuid(this_object()),"write_file",this_object())!=0):));
+#endif
+  */
+  args=file_list(args,MODE_GREP,(flags&GREP_R?1:0),"/",mask);
+  if (!sizeof(args))
+    return printf("Keine passenden Dateien gefunden.\n"),1;
+  if (flags&GREP_I) rexpr=lower_case(rexpr);
+  if (flags&GREP_F)
+  {
+    if (file_size("/players/"+getuid()+"/grep.out")==-2||
+	  !MAY_WRITE("/players/"+getuid()+"/grep.out"))
+      return printf("grep: Datei /players/%s/grep.out kann nicht "
+		      "geschrieben werden.\n",getuid()),1;
+    else
+      write_file("/players/"+getuid()+"/grep.out",
+                 "Ausgabe von \"grep " + _unparsed_args() + "\":\n"); 
+  }
+  asynchron(args,#'grep_file,rexpr,flags,0);
+  return 1;
+}
diff --git a/std/shells/magier/magier_ext.c b/std/shells/magier/magier_ext.c
new file mode 100644
index 0000000..a5d30b2
--- /dev/null
+++ b/std/shells/magier/magier_ext.c
@@ -0,0 +1,733 @@
+// $Id: magier_ext.c 9555 2016-05-03 20:42:46Z Zesstra $
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <logging.h>
+#include <magier.h>
+#include <snooping.h>
+#include <driver_info.h>
+#define NEED_PROTOTYPES
+#include <player/telnetneg.h>
+#include <player/base.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <files.h>
+#include <events.h>
+#include <player/comm.h>
+
+inherit "/std/shells/magier/parsing";
+inherit "/std/shells/magier/upd";
+inherit "/std/shells/magier/fileview";
+inherit "/std/shells/magier/objects";
+inherit "/std/shells/magier/fileedit";
+//inherit "/std/shells/magier/todo";
+inherit "/std/shells/magier/players";
+inherit "/std/shells/magier/admin";
+inherit "/std/shells/magier/moving";
+inherit "/std/shells/magier/comm";
+
+
+//                              #######
+//############################### SET #################################
+//                              #######
+
+
+//
+// _set(): Shell-Befehl 'set'
+// args:   Kommandozeilenargumente
+//
+
+static int _set(mixed args)
+{
+  int pos;
+  string var;
+  mixed val;
+  mapping vars;
+
+  if(!args=_unparsed_args())
+  {
+    if(!sizeof(vars=QueryProp(P_VARIABLES))) 
+      printf("Du hast noch keine Variablen definiert!\n");
+    else
+    {
+      printf("Du hast die folgenden Variablen definiert:\n");
+      walk_mapping(vars,((: printf(" %-20s = %s\n",$1,
+                                 pointerp($2)?implode($2,":"):$2) :)));
+
+    }
+    return 1;
+  }
+  pos = member(args,' ');
+  if(pos == -1)
+    if(sizeof(args))
+      {
+        m_delete(QueryProp(P_VARIABLES), args);
+        printf("Variable %s wurde geloescht.\n",args);
+        return 1;
+      }
+    else return USAGE("set <variable> <wert>");
+  var = args[0..pos-1];
+  val = (member(args[pos+1..],':')>-1?
+         explode(args[pos+1..], ":") :
+         args[pos+1..]);
+  vars=QueryProp(P_VARIABLES);
+  vars+= ([var : val]);
+  printf("Variable gesetzt: %s = %s\n",var,args[pos+1..]);
+  return 1;
+}
+
+//                            ############
+//############################# CD + PWD ###################################
+//                            ############
+
+static int _pwd()
+{
+  printf("Aktuelles Verzeichnis: %s\n",QueryProp(P_CURRENTDIR));
+  return 1;
+}
+
+static int _cd2(string cmdline)
+{
+  string dest,tmp;
+  int flags;
+  mixed *args;
+  
+  args=parseargs(cmdline,&flags,CD_OPTS,1);
+  if (flags==-1) return 0;
+  if(flags&CD_B)
+  {
+    dest=QueryProp(P_LASTDIR);
+  }
+  if (sizeof(args)>1)
+    return notify_fail("Man kann sich (leider) nur in einem "
+                       "Verzeichnis gleichzeitig befinden.\n"),0;
+  if(!dest)
+  {
+    if (!sizeof(args))
+    {
+      if (IS_WIZARD(this_object()))
+        dest="/players/"+getuid(this_object());
+      else
+        dest="/doc";
+    }
+    else
+    {
+      dest=args[0];
+    }
+  }
+  
+  if (dest!="/")
+  {
+    args=file_list(({dest}),MODE_CD,0,"/");
+    args = filter(args, function int (mixed arr)
+        { return arr[FILESIZE] == FSIZE_DIR; } );
+
+    if (!sizeof(args))
+      return notify_fail("cd: "+dest+
+                       ": Kein solches Verzeichnis gefunden.\n"),0;
+    if (sizeof(args)>1)
+      return notify_fail("cd: "+dest+": Maske ist nicht eindeutig.\n"),0;
+    dest=args[0][FULLNAME];
+  }
+  if (!IS_WIZARD(this_object())&&dest[0..3]!="/doc") dest="/doc";
+  SetProp(P_LASTDIR,QueryProp(P_CURRENTDIR));
+  printf("Aktuelles Verzeichnis ist jetzt: %s\n",SetProp(P_CURRENTDIR,dest));
+  tmp="";
+  if (((flags&CD_L)||
+       ((!m_contains(&tmp,QueryProp(P_VARIABLES),"CD_SHORT")||
+         tmp!="1")&&(!(flags&CD_S)))))
+    tell_object(this_object(),
+                read_file(dest+(dest[<1]=='/'?".readme":"/.readme"))||"");
+  return 1;
+}
+
+static int _cd(string cmdline)
+{
+  return _cd2(_unparsed_args());
+}
+
+static string _set_currentdir(string path)
+{
+  Set(P_CURRENTDIR, path);
+  this_object()->modify_prompt();  // Prompt mit neuem Pfad setzen, telnetneg
+  return path;
+}
+
+private string _query_currentdir()
+{
+  string c;
+  if(!c=Query(P_CURRENTDIR))
+    return Set(P_CURRENTDIR, "/players/"+getuid(this_object()),F_VALUE);
+  return c;
+}
+
+//                           ##########
+//############################ PROMPT #################################
+//                           ##########
+
+//
+// _prompt_subst: Mudnamen, Usernamen, Zeilenvorschub in Eingabeauf-
+//                forderung einbauen
+// str:           Uebergebener Kommandozeilenabschnitt
+//
+
+private string _prompt_subst(string str)
+{
+  switch(str)
+  {
+    case "\\h": return sizeof(MUDNAME)?MUDNAME:"Mud ohne Namen";
+    case "\\u": return capitalize(getuid(this_object()));
+    case "\\n": return "\n";
+    case "\\t": return "\t";
+    case "\\w": return "\w";
+  }
+  return str;
+}
+
+
+//
+// _prompt: Eingabeaufforderung setzen
+// arg:     Kommandozeile
+//
+
+static int _prompt(string args)
+{
+  string *pargs;
+
+  args=(extern_call()?_unparsed_args():args);
+  if (!sizeof(args)) args="> ";
+  if (args[0]=='"') args=args[1..<2];     //");
+  if(!sizeof(pargs = regexplode(args, "\\\\[thuwn]")))
+    return USAGE("prompt \"<Eingabeaufforderungsdefinition>\"");
+  pargs=map(pargs,#'_prompt_subst);
+
+  SetProp(P_PROMPT,implode(pargs,""));
+  return 1;
+}
+
+static string _set_prompt(string prompt) {
+  Set(P_PROMPT, prompt, F_VALUE);
+  this_object()->modify_prompt(); // neuen Prompt setzen (telnetneg.c)
+  return prompt;
+}
+
+
+//                           ##############
+//############################ SHOWPRESAY ###############################
+//                           ##############
+
+static int _showpresay(string cmdline)
+{
+  int presay;
+  presay=QueryProp(P_CAN_FLAGS)&CAN_PRESAY;
+  cmdline=_unparsed_args(0);
+  if (!sizeof(cmdline))
+  {
+    printf("Dein Presay wird im Moment %sangezeigt.\n"
+           "%sschalten mit \'showpresay %s\'.\n",
+           presay?"":"nicht ",presay?"Aus":"Ein",
+           presay?"aus":"ein");
+    return 1;
+  }
+  if (cmdline=="ein"||cmdline=="an")
+  {
+    printf("Dein Presay wird jetzt angezeigt.\n");
+    SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS)|CAN_PRESAY);
+    return 1;
+  }
+  if (cmdline=="aus")
+  {
+    printf("Dein Presay wird jetzt nicht mehr angezeigt.\n");
+    SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS)&~CAN_PRESAY);
+    return 1;
+  }
+  return USAGE("showpresay [ein|an|aus]");
+}
+
+//                              ########
+//############################### EXEC ###############################
+//                              ########
+
+static int _exec(string filename)
+{
+  object ob;
+  if (!IS_LORD(this_object())) return 0;
+  if (this_player()!=this_interactive()) return 0;
+  if (this_player()!=this_object()) return 0;
+  if (!(filename=_unparsed_args())) return USAGE("exec <objektname>");
+  filename=(string)"secure/master"->_get_path(filename,getuid());
+  if (file_size(filename)<0&&(!to_filename(filename+".c"))||
+      file_size(to_filename(filename+".c"))<0)
+  {
+    printf("exec: %s: Datei nicht vorhanden oder ein Verzeichnis.\n",filename);
+    return 1;
+  }
+  if (catch(call_other(filename,"????"))) 
+  {
+    printf("exec: Fehler beim Laden von %s.\n",filename);
+    return 1;
+  }
+  if (!(ob=clone_object(filename))) return 0;
+  if (getuid(ob) != getuid(this_object()))
+  {
+    printf("exec: UID-Konflikt: %s <-> %s.\n",getuid(ob),
+           getuid(this_object()));
+    destruct(ob);
+    return 1;
+  }
+  log_file(SHELLLOG("EXEC"),
+         sprintf("%12.12s %40.40s %25.25s\n",
+                 capitalize(getuid(this_object())),filename,dtime(time())[4..]));
+  disable_commands();
+  exec(ob,this_object());
+  if (interactive(this_object())||!interactive(ob))
+  {
+    enable_commands();
+    printf("Fehler bei EXEC: Uebertragung der Shell "
+           "nicht moeglich.\n");
+    return 1;
+  }
+  ob->start_player(capitalize(getuid(this_object())));
+  remove();
+  return 1;
+}
+//                            ############
+//############################# SNOOPING ###############################
+//                            ############
+
+private nosave string snoop_allowed;
+private nosave object snoopee;
+
+nomask int QueryAllowSnoop(object who)
+{
+  return (getuid(who) == snoop_allowed);
+}
+
+static int _sallow(string str)
+{
+  object ob;
+
+  if (!sizeof(str))
+  {
+    if (!snoop_allowed) return USAGE("sallow [<name>]\n");
+    str=snoop_allowed;
+    snoop_allowed=0;
+    printf("Du entziehst %s die Erlaubnis zum Snoopen.\n",capitalize(str));
+    ob=query_snoop(this_player());
+    if (!ob||(getuid(ob)!=str)) return 1;
+    tell_object(ob,break_string(sprintf("%s entzieht Dir die "
+            "Erlaubnis zum snoopen und zwingt Dich so dazu, mit "
+            "dem Snoopen aufzuhoeren.\n",capitalize(getuid()))));
+    snoop(ob);
+    return 1;
+  }
+  if (snoop_allowed) _sallow(""); // Erstmal abschalten
+  ob=find_player(lower_case(str));
+  str=capitalize(str);
+  if (!ob) return
+      printf("sallow: Spieler %s konnte nicht gefunden werden.\n",str),1;
+  if (query_wiz_grp(ob)>query_wiz_grp(this_player()))
+    return printf("sallow: %s hat einen hoeheren Rang als Du und kann "
+                  "Dich daher ohnehin snoopen.\n",str),1;
+  snoop_allowed=getuid(ob);
+  return printf("sallow: Du erlaubst %s, Dich zu snoopen.\n",str),1;
+}
+
+static int _snoop(string cmdline)
+{
+  object ob;
+  int flags;
+  string *args;
+
+  if (!sizeof(cmdline=_unparsed_args())||
+      sizeof(args=parseargs(cmdline,&flags,SNOOP_OPTS,0))!=1||flags==-1)
+  {
+    if (!snoop(this_object())) return USAGE("snoop [-" SNOOP_OPTS
+                                            "] [<spieler>]\n");
+    if (snoopee)
+      printf("Du snoopst %s jetzt nicht mehr.\n",capitalize(getuid(snoopee)));
+    else
+    {
+      printf("Du hoerst auf zu snoopen.\n");
+    // evtl. irgendetwas loggen ... sollte eigentlich nicht passieren.
+    }
+    snoopee=(object)0;
+    return 1;
+  }
+  SetProp(P_SNOOPFLAGS,flags); // FUNKTIONIERT NUR, WENN magier.h und
+                               // snooping.h abgeglichen sind
+  if (!(ob = find_player(args[0])))
+    return printf("snoop: Konnte keinen Spieler '%s' finden.\n",
+                  capitalize(args[0])),1;
+  if (!snoop(this_player(),ob))
+    return printf("snoop: Der Versuch, %s zu snoopen, ist "
+                  "fehlgeschlagen.\n%s",capitalize(args[0]),
+                  ((~flags)&SNOOP_F)?"Eventuell funktioniert es mit dem "
+                  "Flag '-f'.\n":""),1;
+  snoopee=ob;
+  return printf("Du snoopst jetzt %s.\n",capitalize(getuid(ob))),1;
+}
+
+
+//                           ##########
+//############################ MSCHAU #################################
+//                           ##########
+
+static int _mschau(string str)
+{
+  if (this_interactive()!=this_object()) return 0;
+  if (str=="ja"||str=="an"||str=="ein")
+  {
+    if ( QueryProp(P_WANTS_TO_LEARN) )
+      printf("Du hast den Magier-Modus doch schon eingeschaltet!\n");
+    else
+    {
+      printf("Du hast jetzt den Magier-Modus eingeschaltet.\n" );
+      SetProp(P_WANTS_TO_LEARN, 1);
+    }
+    return 1;
+  }
+  if (str=="nein"||str=="aus")
+  {
+    if (QueryProp(P_WANTS_TO_LEARN))
+    {
+      printf( "Du schaltest den Magier-Modus aus.\n");
+      set_heart_beat(1);
+      SetProp(P_WANTS_TO_LEARN,0);
+    }
+    else
+      printf("Du hast den Magier-Modus doch schon ausgeschaltet.\n");
+    return 1;
+  }
+  if (str=="+debug")
+  {
+    printf("Du nimmst jetzt Debugausgaben wahr.\n");
+    SetProp(P_WIZ_DEBUG,1);
+    return 1;
+  }
+  if (str=="-debug")
+  {
+    printf("Du nimmst jetzt keine Debugausgaben mehr wahr.\n");
+    SetProp(P_WIZ_DEBUG,0);
+    return 1;
+  }
+  return USAGE("mschau [an|ein|ja|aus|nein|+debug|-debug]\n");
+}
+
+//                           ###########
+//############################ PROTECT ################################
+//                           ###########
+
+static int _protect(string str)
+{
+ 
+  if (this_object()!=this_interactive()) return 0;
+  if (!sizeof(str))
+    return USAGE("protect <propertyname>\n");
+  Set(str, PROTECTED, F_MODE);
+  return printf("Deine Property %s ist jetzt %sgeschuetzt.\n",
+                str,(Query(str,F_MODE) & PROTECTED?"":"nicht mehr ")),1;
+}
+
+
+//                         ###############
+//########################## VIS + INVIS ##############################
+//                         ###############
+
+static int _hbstop;
+static int _age;
+
+static int _invis(string inform)
+{
+
+  if (QueryProp(P_INVIS))
+    return printf("Du bist doch schon unsichtbar!\n"),1;
+  tell_room(environment(),sprintf("%s %s.\n",capitalize(Name()),
+            QueryProp(P_MMSGOUT)),({ this_object() }));
+  if (inform=="e") {
+    // Logout-event ausloesen
+    EVENTD->TriggerEvent(EVT_LIB_LOGOUT, ([
+            E_OBJECT: this_object(),
+            E_PLNAME: getuid(this_object()),
+            E_ENVIRONMENT: environment() ]) );
+
+    call_notify_player_change(0);
+  }
+
+  SetProp(P_INVIS, QueryProp(P_AGE));
+  printf("Du bist jetzt unsichtbar.\n");
+  return 1;
+}
+
+static int _vis(string inform)
+{
+  if (!QueryProp(P_INVIS))
+    return printf("Du bist doch schon sichtbar.\n"),1;
+   tell_room(environment(),sprintf("%s %s.\n",capitalize(Name()),
+            QueryProp(P_MMSGIN)),({ this_object() }));
+  SetProp(P_INVIS, 0);
+  if (inform=="e") {
+    // Login-event ausloesen
+    EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
+          E_OBJECT: this_object(),
+          E_PLNAME: getuid(this_object()),
+          E_ENVIRONMENT: environment() ]) );
+
+    call_notify_player_change(1);
+  }
+  printf("Du bist jetzt sichtbar.\n");
+  return 1;
+}
+
+//                          ############
+//########################### LOCALCMD ###############################
+//                          ############
+
+
+static int _localcmd()
+{
+  int size,more;
+  string *verbs,result;
+
+  // Per Umweg ueber Mapping doppelte Werte einstampfen
+  size=sizeof(verbs=m_indices(mkmapping(query_actions(this_object()))));
+  verbs-=({""});
+  more=(size>sizeof(verbs));
+  if (!sizeof(verbs))
+  {
+    if (more)
+      printf("Die vorhandenen Befehle koennen nicht angezeigt werden, "
+             "da sie per 'add_action(\"funktion\",\"\",1)' definiert "
+             "wurden.\n");
+    else
+      printf("Dir stehen keine Befehle zur Verfuegung ... eigenartig!\n");
+    return 1;
+  }
+  verbs=sort_array(verbs,#'>);
+  result=break_string(sprintf("\'%s\'",implode(verbs,"' '")),78);
+  return printf("\n%'-'78.78s\nDie fuer Dich aktuell verfuegbaren "
+                "Befehle sind:\n\n%s\n%s%'-'78.78s\n","",result,
+                more?"Zudem wurden Befehle per 'add_action("
+                "\"funktion\",\"\",1)' definiert.\n":"",""),1;
+}
+
+//                           ###########
+//############################ TRAENKE ################################
+//                           ###########
+
+static int _traenke(string str)
+{
+  if(SetProp(P_TRANK_FINDEN, !QueryProp(P_TRANK_FINDEN)))
+    write("Du kannst jetzt Zaubertraenke finden.\n");
+  else
+    write("Du findest jetzt keine Zaubertraenke mehr.\n");
+  return 1;
+}
+
+static int norm_rusage() {
+	mixed* r;
+  r = rusage();
+  return r[0]/100 + r[1]/100;
+}
+
+//                           #############
+//############################ ASYNCHRON #################################
+//                           #############
+
+//
+// asynchron(): Asynchrone Abarbeitung eines Arrays
+// array:     Zu bearbeitendes Array
+// cmd:       Auszufuehrende Closure
+// data:      Extraargumente
+// flags:     Zusatzdaten (normalerweise Flags)
+// c:         schon asynchron?
+//
+
+static varargs void asynchron(mixed* array, closure cmd, mixed data, mixed flags,int c)
+{
+  int i, j, k;
+  mixed ret_val;
+  string cmd_string;
+
+  k = norm_rusage()+5;
+  j = sizeof(array);
+  i=0;
+  
+  switch (cmd_string=explode(sprintf("%O",cmd),"->")[1])
+  {
+    case "_make":     cmd_string=(data&UPD_LOAD?"load":"upd");break;
+    case "cp_file":   cmd_string =(data?"mv":"cp"); break;
+    case "grep_file": cmd_string = "grep"; break;
+    case "rm_file":   cmd_string = "rm"; break;
+    default: break;
+  }
+  
+  while(i < j && get_eval_cost() > 200000 && norm_rusage()<k)
+    // Sowohl Too-Long-Eval als auch Lag verhindern
+  {
+    ret_val=apply(cmd,array[i],data, flags);
+    if (closurep(ret_val))
+    {
+      if(c) tell_object(this_object(),
+               sprintf("%s: Verlasse Asynchronen Modus.\n",cmd_string));
+      funcall(ret_val,array[i..],data,flags);
+      return;
+    }
+    if (ret_val==RET_DELETE)
+    {
+      array[i+array[i][SUBDIRSIZE]+1..i+array[i][SUBDIRSIZE]]=
+        ({ (array[i][0..5]+({0})) });
+    }
+    if (ret_val==RET_JUMP) i+=array[i][SUBDIRSIZE];
+    i++;
+  }
+  if(sizeof(array = array[i..])) 
+  {  
+    if(!c) tell_object(this_object(),
+              sprintf("%s: Asynchroner Modus aktiviert!\n", cmd_string));
+    call_out(#'asynchron, 1, array, cmd, data,flags, 1);
+  }
+  else
+  {
+    if(c) tell_object(this_object(),
+                      sprintf("%s: abgeschlossen.\n",cmd_string));
+      else if (query_verb()) tell_object(this_object(),
+            sprintf("%s: abgeschlossen.\n",query_verb()));
+  }
+  return;                                                    
+}
+
+//
+// ######################## Driver Status Infos ##########################
+//
+static int _driver_malloc(string str)
+{
+    if(!sizeof(str))
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC));
+        return 1;
+    }
+    if(str == "extstats")
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC_EXTENDED));
+        return 1;
+    }
+    return 0;
+}
+
+static int _driver_dumpallobj(string str)
+{
+    write("Dumping to /OBJ_DUMP ... ");
+    efun::dump_driver_info(DDI_OBJECTS);
+    efun::dump_driver_info(DDI_OBJECTS_DESTRUCTED);
+    write("done\n");
+    return 1;
+}
+
+static int _driver_opcdump(string str)
+{
+    efun::dump_driver_info(DDI_OPCODES);
+    return 1;
+}
+
+static int _driver_status(string str)
+{
+    int opt;
+
+    switch(str || "")
+    {
+        case "":
+            opt = DI_STATUS_TEXT_MEMORY;
+            break;
+
+        case "tables":
+        case " tables":
+            opt = DI_STATUS_TEXT_TABLES;
+            break;
+
+        case "swap":
+        case " swap":
+            opt = DI_STATUS_TEXT_SWAP;
+            break;
+
+        case "malloc":
+        case " malloc":
+            opt = DI_STATUS_TEXT_MALLOC;
+            break;
+
+        case "malloc extstats":
+        case " malloc extstats":
+            opt = DI_STATUS_TEXT_MALLOC_EXTENDED;
+            break;
+
+        default:
+            return 0;
+    }
+    write(efun::driver_info(opt));
+    return 1;
+}
+
+//                         ###################
+//########################## INITIALISIERUNG #############################
+//                         ###################
+
+//
+// _query_localcmds: Welche Kommandos werden in dieser Datei implementiert?
+//
+
+static mixed _query_localcmds()
+{
+  return ({
+    ({"set","_set",0,LEARNER_LVL}),
+    ({"pwd","_pwd",0,LEARNER_LVL}),
+    ({"cd","_cd",0,LEARNER_LVL}),
+    ({"prompt","_prompt",0,LEARNER_LVL}),
+    ({"showpresay","_showpresay",0,LEARNER_LVL}),
+    ({"exec","_exec",0,ARCH_LVL}),
+    ({"sallow","_sallow",0,LEARNER_LVL}),
+    ({"snoop","_snoop",0,WIZARD_LVL}),
+    ({"mschau","_mschau",0,LEARNER_LVL}),
+    ({"protect","_protect",0,WIZARD_LVL}),
+    ({"invis","_invis",0,LEARNER_LVL}),
+    ({"vis","_vis",0,LEARNER_LVL}),
+    ({"localcmd","_localcmd",0,LEARNER_LVL}),
+    ({"traenke","_traenke",0,DOMAINMEMBER_LVL}),
+    ({"malloc","_driver_malloc",0,LEARNER_LVL}),
+    ({"dumpallobj","_driver_dumpallobj",0,ARCH_LVL}),
+    ({"opcdump","_driver_opcdump",0,ARCH_LVL}),
+    ({"status","_driver_status",0,LEARNER_LVL}),
+  })
+    +fileview::_query_localcmds()
+    +upd::_query_localcmds()
+    +objects::_query_localcmds()
+    +fileedit::_query_localcmds()
+//    +todo::_query_localcmds()
+    +players::_query_localcmds()
+    +admin::_query_localcmds()
+    +moving::_query_localcmds()
+    +comm::_query_localcmds();
+}
+
+//
+// initialize: Initialisierung der Shell
+//
+
+static void initialize()
+{
+  Set(P_PROMPT, SAVE, F_MODE_AS);
+  Set(P_VARIABLES,SAVE,F_MODE_AS);
+  Set("filesys",SAVE,F_MODE_AD);   // P_FILESYS ist obsolet
+  Set("short_cwd",SAVE,F_MODE_AD); // P_SHORT_CWD auch
+  if(!mappingp(QueryProp(P_VARIABLES)))
+    SetProp(P_VARIABLES, ([]));
+  _prompt(QueryProp(P_PROMPT));
+//  todo::initialize();
+  _cd2("");
+  return;
+}
+
+static void reconnect() { _cd(QueryProp(P_CURRENTDIR)); }
diff --git a/std/shells/magier/moving.c b/std/shells/magier/moving.c
new file mode 100644
index 0000000..3095a3f
--- /dev/null
+++ b/std/shells/magier/moving.c
@@ -0,0 +1,232 @@
+// MorgenGrauen MUDlib
+//
+// moving.c
+//
+// $Id: moving.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <thing/properties.h>
+#include <living/moving.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+#include <wizlevels.h>
+#include <moving.h>
+#include <properties.h>
+
+private mixed verfolger()
+{
+  mixed *pur;
+
+  if (!pointerp(pur=QueryProp(P_PURSUERS))) return 0;
+  else return pur[0];
+}
+
+//                         #########
+//########################## IN+AT ############################
+//                         #########
+
+static int _move_base(object target, object old_room, string cmd)
+{
+  if (environment()!=target)
+    if (IS_ARCH(this_object()))
+    {
+      __set_environment(this_object(),target);
+      printf("%s: Bewegung hat nicht geklappt. Versuche es mit "
+             "set_environment... %s.\n.",query_verb(),
+             environment()==target?"hat geklappt":"erfolglos");
+    }
+  else
+    printf("%s: Bewegung hat nicht geklappt.\n",query_verb());
+  if (environment()!=target) return 1;
+  command(cmd);
+  if (old_room) move_object(old_room);
+  else
+    return printf("%s: Ursprungsraum wurde zerstoert.\n",query_verb()),1;  
+  if (environment()!=old_room)
+  {
+    if (IS_ARCH(this_object()))
+    {
+      __set_environment(this_object(),old_room);
+      printf("%s: Zurueckbewegen hat nicht geklappt. Versuche es mit "
+             "set_environment ... %s.\n",query_verb(),
+             environment()==old_room?"hat geklappt":"erfolglos");
+    }
+    else
+      printf("at: Zurueckbewegen hat nicht geklappt.\n");
+  }
+  return 1;
+}
+
+static int _in_room(string str)
+{
+  string room;int size;
+  object old_room;
+  string cmd,err;
+
+  if (!sizeof(str=_unparsed_args()) ||
+      !sizeof(str=regreplace(str,"^ *","",1)) ||
+      sscanf(str, "%s %s", room, cmd) != 2)
+    return USAGE("in <raum> <befehl>\n");
+  old_room = environment();
+  room=(string)call_other(master(),"_get_path",room,getuid());
+  if (err=catch(move_object(room)))
+  {
+    if (catch(size=file_size(room+".c"))||size<1)
+      printf("%s: %s.c: Datei nicht vorhanden.\n",query_verb(),room);
+    else
+      printf("%s: Bewegung nach %s hat nicht funktioniert: %s\n",
+             query_verb(),room,err);
+    return 1;
+  }
+  return _move_base(find_object(room),old_room,cmd);
+}
+
+static int _at_player(string dest)
+{
+  object ob,old_room;
+  mixed tmp;
+  string cmd;
+
+  if (!sizeof(dest=_unparsed_args()) ||
+      !sizeof(dest=regreplace(dest,"^ *","",1)) ||
+      sscanf(dest, "%s %s", dest, cmd) != 2)
+    return USAGE("at <lebewesen> <befehl>\n");
+  if (!(ob=find_living(dest)))
+  {
+    tmp=match_living(dest,1);
+    if (stringp(tmp)) ob = find_living(tmp);
+  }
+  if (!ob||!(ob=environment(ob)))
+    return _notify_fail(sprintf("at: Lebewesen \'%s\' nicht gefunden.\n",
+                                dest)),0;
+  old_room=environment();
+  move_object(ob);
+  return _move_base(ob,old_room,cmd);
+}
+
+//                           ########
+//############################ GOTO ############################
+//                           ########
+
+static object find_living_nr(string str)
+{ string name,check;
+  int nr;
+  object*livings;
+  if(sscanf(str,"%s %d%s",name,nr,check)<2||sizeof(check))
+    return find_living(str);
+  if(!sizeof(livings=filter((find_livings(name)||({})),#'environment))
+     ||nr<1||sizeof(livings)<nr)
+    return 0;
+  return livings[nr-1];
+}
+
+static int _goto(string dest){
+  mixed target;
+  string target2,err;
+
+  if (!sizeof(dest=_unparsed_args()))
+    return USAGE("goto [lebewesen|filename]\n");
+  if (!((target=find_living_nr(dest)) && (target=environment(target))))
+  {
+     target2=target=(mixed)call_other(master(),"_get_path",dest,getuid());
+     if (!find_object(target))
+     {
+       if (target2[<1]=='.') target2+="c";
+       if (target2[<2..<1]!=".c") target2+=".c";
+       notify_fail(sprintf("goto: Datei %O nicht vorhanden.\n",target));
+       if (!(file_size(target2)>-1||
+           file_size(implode(explode(target,"/")[0..<2],"/")+
+               "/virtual_compiler.c")>-1)||(err=catch(call_other(target,"?"))))
+       {
+         if (err)
+              notify_fail(sprintf("goto: Fehler beim Teleport nach %O:\n%s\n",
+                      dest,implode(explode(err,"\n")," ")));
+         target=match_living(dest,1);
+         if (!(stringp(target)&&(target=find_living(target))&&
+               (target=environment(target))))
+           return 0;
+       }
+     }
+  }
+  if (verfolger()) _verfolge("");
+  if (move(target,M_TPORT|M_NOCHECK)<0)
+    printf("Bewegung fehlgeschlagen!\n");
+  return 1;
+}
+
+//                           ########
+//############################ HOME ############################
+//                           ########
+
+static int _home()
+{
+  string dest;
+  if (verfolger()) _verfolge("");
+  dest="/players/" + getuid() + "/workroom";
+  if (file_size(dest+".c")<1||catch(call_other(dest,"???")))
+  {
+    printf("Fehler beim Laden Deines Workrooms.\n"
+           "Gehe zum Magiertreff.\n");
+    dest="/secure/merlin";
+  }
+  
+  if (move(dest,M_TPORT|M_NOCHECK)<0)
+    printf("Bewegung fehlgeschlagen!\n");
+  return 1;
+}
+
+//                        ###############
+//######################### +MAGIERNAME ##########################
+//                        ###############
+
+static int _go_wiz_home(string str)
+{
+  _notify_fail("Syntax: '+magiername'\n");
+  if(sizeof(query_verb())>1) str=query_verb()[1..];
+  if(!sizeof(str)) return 0;
+  if(query_verb()[0]!='+') return 0;
+  str=(old_explode(str," ")-({0}))[0];
+  if(!sizeof(str)) return 0;
+  str=lower_case(str);
+  if (str=="merlin")
+  {
+    move("/secure/merlin",M_TPORT|M_NOCHECK);
+    return 1;
+  }
+  if ((!call_other(master(),"get_userinfo",str))||
+      !IS_LEARNER(str))
+  {
+    printf("Es gibt keinen Magier namens %s.\n",
+           capitalize(str));
+    return 1;
+  }
+  if (file_size("/players/"+str+"/workroom.c")<1)
+  {
+    printf("%s hat keinen Workroom.\n",capitalize(str));
+    return 1;
+  }
+  if (catch(call_other("/players/"+str+"/workroom","???")))
+  {
+    printf("Der Workroom von %s hat Fehler.\n",capitalize(str));
+    return 1;
+  }
+  move("/players/"+str+"/workroom",M_TPORT|M_NOCHECK);
+  return 1;
+}
+
+
+static mixed _query_localcmds()
+{
+  return
+    ({({"goto","_goto",0,LEARNER_LVL}),
+      ({"in","_in_room",0,LEARNER_LVL}),
+      ({"at","_at_player",0,LEARNER_LVL}),
+      ({"home","_home",0,WIZARD_LVL}),
+      ({"+","_go_wiz_home",1,LEARNER_LVL})});
+}
diff --git a/std/shells/magier/objects.c b/std/shells/magier/objects.c
new file mode 100644
index 0000000..ed74f56
--- /dev/null
+++ b/std/shells/magier/objects.c
@@ -0,0 +1,168 @@
+// $Id: objects.c 8848 2014-06-11 22:05:04Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <moving.h>
+#define NEED_PROTOTYPES
+#include <thing/language.h>
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <living/put_and_get.h>
+#include <player.h>
+#include <magier.h>
+
+//                        ###################
+//######################### INITIALISIERUNG #############################
+//                        ###################
+
+
+static mixed _query_localcmds()
+{
+  return ({({"clone","_clone",0,WIZARD_LVL}),
+           ({"setcmsg","_setcmsg",0,WIZARD_LVL}),
+           ({"setdmsg","_setdmsg",0,WIZARD_LVL}),
+           ({"destruct","_destruct",0,WIZARD_LVL}),
+          });
+}
+
+//                              #########
+//############################### CLONE ##############################
+//                              #########
+
+static int _clone(string cmdline)
+{
+  mixed *tmp;
+  int flags;
+  object ob;
+  string *args,*args2,err;
+  
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,CLONE_OPTS,1);
+  if (flags==-1||sizeof(args)!=1)
+    return USAGE("clone [-" CLONE_OPTS "] <objektname>");
+  if (flags&CLONE_F)
+    cmdline=args[0];
+  else
+  {
+    // etwas umstaendlich, aber so kann man auch Dateien clonen,
+    // wenn man keine Leserechte hat. Man muss aber im Verzeichnis
+    // lesen koennen
+    args2=explode(args[0],"/");
+    if (args2[<1][<1]=='.') args2[<1]+="c";
+    else if (args2[<1][<2..<1]!=".c") args2[<1]+=".c";
+    tmp=file_list(({implode(args2[0..<2],"/")+"/*"}),MODE_CLONE,0,"",
+                  args2[<1]);
+    if (!sizeof(tmp)||tmp[0][FILESIZE]<0)
+      return printf("clone: %s: Datei existiert nicht.\n",args[0]),1;
+    cmdline=tmp[0][FULLNAME];
+  }
+
+  if (err=catch(ob=clone_object(cmdline))||!ob)
+    return printf("clone: %s: Objekt konnte nicht erzeugt werden.\n"
+                  "Grund: %O",
+                  args[0],err||"unbekannt"),1;
+  if (!objectp(ob))
+    return printf("clone: %s: Objekt beim Erzeugen zerstoert.\n",
+                  args[0]),1;
+  if ((ob->move(this_object(),M_GET)>0) || 
+      (!objectp(ob)||ob->move(environment(),M_NOCHECK)>0)||!objectp(ob))
+  {
+    if (!objectp(ob))
+      return printf("clone: %s: Objekt beim Erzeugen zerstoert.\n",
+                  args[0]),1;
+    printf("Clone: %s erzeugt.\n",object_name(ob));
+    tell_room(environment(this_object()),
+              sprintf("%s %s.\n",Name(WER,1),QueryProp(P_CLONE_MSG)),
+                      ({ this_object()}));
+    return 1;
+  }
+  tell_room(environment(this_object()),
+            sprintf("%s malt wilde Zeichen in die Luft und "
+                    "murmelt vor sich hin, aber nichts "
+                    "passiert...\n",Name(WER,1)),
+            ({ this_object()}));
+  destruct(ob);
+  printf("Clone: %s: Objekt konnte nicht bewegt werden.",args[0]);
+  return 1;
+}
+
+
+//                            ############
+//############################# DESTRUCT ##############################
+//                            ############
+
+//
+// _destruct: Objekte zerstoeren
+//
+
+static int _destruct(string cmdline)
+{
+  int flags;
+  mixed *args;
+  object ob;
+
+  if (!sizeof(cmdline=_unparsed_args()))
+    return USAGE(query_verb()+" <objektname>");
+  args=find_obs(lower_case(cmdline),PUT_GET_NONE);
+  if (!args||!sizeof(args))
+  {
+    if (!(ob=find_object(cmdline)))
+    {
+      notify_fail(query_verb()+": Objekt \"" +cmdline+
+                  "\" nicht gefunden.\n");
+      return 0;
+    }
+  }
+  else
+    ob=args[0];
+  cmdline=capitalize(to_string(ob->name(WER)));
+  flags=(int)ob->QueryProp(P_PLURAL); // Missbrauch von flags :o)
+  if (query_verb()=="destruct")
+  {
+    if (!ob->remove())
+    {
+      notify_fail(cmdline+" will nicht zerstoert werden!\n");
+      return 0;
+    }
+  }
+  else destruct(ob);
+  if (!ob)
+  {
+    if (!QueryProp(P_INVIS))
+    {
+      tell_room(environment(this_object()),
+                sprintf("%s %s.\n",cmdline,QueryProp(P_DESTRUCT_MSG)),
+                ({ this_object() }));
+    }
+    printf("%s w%s von Dir zerstaeubt.\n",cmdline,(flags?"erden":"ird"));
+  }
+  else
+    printf("%s kann nicht zerstoert werden.\n",cmdline);
+  return 1;
+}
+
+//                        ####################
+//######################### SetCMsg, SetDMsg ############################
+//                        ####################
+
+static int _setcmsg(string str)
+{
+  printf("Beim Clonen von Objekten sehen die Anderen nun:\n"
+         "<Dein Name> %s.\n",
+         (SetProp(P_CLONE_MSG, _unparsed_args()||"zaubert etwas aus "
+          + QueryPossPronoun(MALE,WEM) + " Aermel hervor")));
+  return 1;
+}
+
+static int _setdmsg(string str)
+{
+  printf("Beim Zerstoeren von Objekten sehen die Anderen nun:\n"
+         "<Objekt> %s.\n",
+         SetProp(P_DESTRUCT_MSG, _unparsed_args()||"wird von " + name(WER,1)
+          + " zerstaeubt"));
+  return 1;
+}
diff --git a/std/shells/magier/parsing.c b/std/shells/magier/parsing.c
new file mode 100644
index 0000000..555332d
--- /dev/null
+++ b/std/shells/magier/parsing.c
@@ -0,0 +1,370 @@
+// $Id: parsing.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <files.h>
+#include <wizlevels.h>
+#include <logging.h>
+#include <regexp.h>
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <thing/properties.h>
+#include <player.h>
+
+//
+// glob2regexp: Argument von glob in Regexp umwandeln
+// str: Argument (als Referenz)
+//
+
+static string glob2regexp(string str)
+{
+  str=regreplace(str,"([\\.\\^\\$\\[\\]\\(\\)])","\\\\\\1",RE_TRADITIONAL|1);
+  str=regreplace(str,"\\*",".*",RE_TRADITIONAL|1);
+  return sprintf("^%s$",regreplace(str,"?",".",RE_TRADITIONAL|1));
+}
+
+//
+// to_filename: Argument in Dateinamen umwandeln
+// str:  Argument
+// Rueckgabe: Dateiname
+//
+
+static mixed to_filename(string str)
+{
+  string *tmp,p,newfn;
+  int i;
+// Testen ob .. in einem Filenamenabschnitt, falls Version <3.2.5
+  tmp=explode(str,"/");
+// Testen auf Pfadvariable
+  if (sizeof(tmp[0]) && tmp[0][0]=='$' 
+      && m_contains(&p,QueryProp(P_VARIABLES),tmp[0][1..]))
+    tmp[0]=p;
+// Pfad absolut machen (Hat danach noch Wildcards drinnen) oder auch nicht
+  return master()->make_path_absolute(implode(tmp,"/"));
+}
+
+
+//
+// _parseargs(): Kommandozeilenabschnitt parsen
+// str:         Kommandozeilenabschnitt
+// line:        Array von geparsten Kommandozeilenabschnitten (Referenz)
+// flags:       Als Referenz uebergebener Flag-Wert
+// opts:        Erlaubte Flags
+// build_fn:    Filenamen aendern
+//
+
+private void _parseargs(string str, string *line,int flags,string opts,
+                     int build_fn )
+{
+
+// Strings in "" erhalten
+  if(str[0] == "\""[0])  
+  { 
+    line += ({ str[1..<2] });
+    return; 
+  }  
+//  Flags parsen
+  if(str[0] == '-')  
+  { 
+    int i,j;
+    i=sizeof(str);
+    while(i--)
+      if (str[i]!='-')
+      {
+        if((j = member(opts, str[i])) != -1)
+          flags |= (1 << j);
+        else
+        {
+          flags=-1;
+          printf("Das Flag '-%c' wird von dem Befehl '%s' nicht "
+                 "unterstuetzt.\n",str[i],query_verb()||"");
+        }
+      }
+    return;
+  }
+  if (build_fn)
+  {
+    if (str=(string)to_filename(str)) line+=({ str });
+  } 
+  else
+    line+= ({ str });
+}
+
+
+//
+// parseargs() - zerlegt Kommandozeile in ein Array:
+// cmdline:     Kommandozeile
+// flags:       Als Referenz uebergebener leerer Integerwert. Enthaelt danach
+//              die Flags
+// opts:        String mit erlaubten Optionen
+// build_fn:    Filenamen umbauen?
+// Rueckgabe: Array der Kommandozeilenargumente (ausser Flags)
+//
+
+static string *parseargs(string cmdline,int flags, string opts,int build_fn)
+{
+  int i;
+  string *line;
+  line=({});
+  if (!sizeof(cmdline)) return ({});
+  map(regexplode(cmdline,"[\"][^\"]*[\"]| ", RE_TRADITIONAL)-({" ", ""}),
+            #'_parseargs, &line, &flags,opts, build_fn);
+  return line - ({""});
+}
+
+//
+// _vc_map: VC-Objektnamen zu Arrays umwandeln ({ name, -1, program_time() })
+//
+
+private int *_vc_map(object ob,mixed *list)
+{
+  list+=({ explode(object_name(ob),"/")[<1],-1,program_time(ob) });
+  return 0;
+}
+
+
+//
+// _get_files: Filedaten beim Rekursiven kopieren erstellen
+// dirname:    Bearbeitetes Verzeichnis
+// mask:       Maske, der Dateinamen genuegen muessen
+// mode:       Welche Daten werden benoetigt? (siehe magier.h)
+// dest:       In welches Verzeichnis soll kopiert werden?
+// Rueckgabe:  Alist mit den Dateiinformationen
+//
+
+private varargs mixed *_get_files(string dirname,string mask,int mode,string dest)
+{
+  mixed *files,*tmp,*data;
+  string fullname,base;
+
+  //DEBUG("_GF: DIRNAME " + dirname);
+  data=get_dir(dirname+"*",7);
+  if(!sizeof(data)) return ({});
+  files=({});
+ 
+  while(sizeof(data))
+  {
+    tmp=({});
+    base=data[BASENAME];
+    fullname=dirname+base;
+    if (base!="."&&base!=".."&&(!(mode==MODE_GREP&&base=="RCS"))&&
+        ((data[FILESIZE]==-2&&
+       sizeof(tmp=_get_files(fullname+"/",mask,mode,
+      dest+base+"/"))&&mode!=MODE_RM)||!mask||sizeof(regexp(({ base }),mask, RE_TRADITIONAL))))
+    {
+      //DEBUG("_GF: ADDING FILE " + fullname);
+      files+= ({ data[0..2]+({ fullname,dirname,dest+base,
+                               sizeof(tmp) }) });
+    }
+    if (sizeof(files)+sizeof(tmp)>MAX_ARRAY_SIZE)
+       raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
+    files+=tmp;
+    data=data[3..];
+  }
+  
+  if(sizeof(files)>300&&!IS_ARCH(this_object()))
+    // Tod allen Laggern :o)
+    raise_error("Zu viele Files (>300)!! Abgebrochen!\n");
+  return files;
+}
+ 
+
+//
+// _get_matching: Rekursive Funktion zum ermitteln der Files, wenn mit Maske
+//                gearbeitet wird (z.B. cp -m /bla/*/* /ziel *.c)
+// pathmask: Array, das die einzelnen Elemente der Maske (Arg1) enthaelt
+//                   (per efun:old_explode(arg1,"/"))
+// depth:     Aktuelles Element von pathmask
+// path:      implode(pathmask[0..depth],"/");
+// mode:      Welche Daten werden benoetigt? (siehe magier.h)
+// flags:     Welche Flags wurden gesetzt?
+// dest:      Zielverzeichnis (beim kopieren/moven)
+// filemask:  Maske, der die Files genuegen muessen
+// Rueckgabe: Alist mit den Dateiinformationen
+//
+
+private mixed *_get_matching(string *pathmask, int depth, string path, 
+                    int mode, int recursive, string dest,string filemask)
+{
+  mixed *data,*tmp,*files;
+  string base,full;
+
+  //DEBUG("_GM: PM: " + pathmask[depth]);
+  //DEBUG("_GM: FM: " + filemask);
+  data=get_dir(path+pathmask[depth++],GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES)||({});
+  if (!sizeof(data)) return ({});
+  files=({});
+  while(sizeof(data))
+  {
+    if ((base=data[BASENAME])=="."||base=="..")
+    {
+      data=data[3..];
+      continue;
+    }
+    full=path+base;
+    //DEBUG("_GM: FULL: " + full);
+    if ((data[FILESIZE]==-2)&&(sizeof(pathmask)>depth)&&
+        (!(mode==MODE_GREP&&base=="RCS")))
+    {
+      //DEBUG("DESCEND INTO " + full);
+      tmp=_get_matching(pathmask,depth,full+"/",mode,recursive,
+                        (recursive?dest+base+"/":dest),filemask);
+    }
+    else tmp=({});
+    //DEBUG("DEPTH: " + depth + " : " + sizeof(pathmask));
+    if((!filemask&&(depth==sizeof(pathmask)))||
+        (filemask&&(depth+2>sizeof(pathmask))&&
+        sizeof(regexp(({ base }),filemask,RE_TRADITIONAL)))||
+       ((mode==MODE_CP||mode==MODE_MV||(filemask&&
+        (mode==MODE_RM)&&sizeof(regexp(({ base}),filemask,RE_TRADITIONAL))))&&
+        sizeof(tmp)))
+    {
+      //DEBUG("ADDING: " + base+ " : "+ full );
+      files+=({ data[0..2]+({ full, path, dest+base,sizeof(tmp)}) });
+    }
+    if (sizeof(files)+sizeof(tmp)>MAX_ARRAY_SIZE)
+       raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
+    files+=tmp;
+    data=data[3..];
+  }
+  if(sizeof(files)>300&&!IS_ARCH(this_object()))
+    // Tod allen Laggern!
+    raise_error("Zu viele Files (>300)!! Abgebrochen!\n");
+  return files;
+}
+
+
+//
+// get_files: Basisroutine zum Ermitteln der zu bearbeitenden Dateien
+// filename:  Pfadmaske, Verzeichnisname, Dateiname, der bearbeitet werden
+//            soll
+// mode:      Welche Daten werden benoetigt? (siehe magier.h)
+// recursive: Auch Unterverzeichnisse bearbeiten?
+// dest:      Wenn kopiert werden soll: wohin?
+// filemask:  Maske, der die Dateinamen genuegen muessen
+// Rueckgabe: Alist mit den Dateiinformationen
+//
+
+static varargs mixed *get_files(string filename, int mode, int recursive, string dest, string filemask)
+{ 
+  string full,path,base,*patharray,*vrooms,dest2;
+  object vcompiler;
+  mixed *files,*data;
+
+  // DEBUG("GF: " + filename);
+  // DEBUG("REC: " + recursive + " MODE: " + mode);
+  // if (dest[<1..<1]!="/") DEBUG("DEST: " + dest);
+  if (filename=="/")
+    {
+      switch (mode)
+      {
+        case MODE_LSA: return ({({ "", -2, 0,"","","",0 })});
+        default: if (!recursive) return ({});
+                 break;
+      }
+    }
+  patharray=explode(filename,"/");
+  if(!sizeof(data=get_dir(filename,7)||({}))&&
+     (mode==MODE_UPD||mode==MODE_MORE||mode==MODE_ED))
+    data=get_dir(filename+".c",7)||({});
+  if ((mode==MODE_LSA||mode==MODE_LSB)&&
+      (vcompiler = find_object(implode(patharray[0..<2],"/")+"/virtual_compiler")) &&
+      pointerp(vrooms=(mixed *)vcompiler->QueryObjects()))
+    map(vrooms,#'_vc_map,&data);
+  files=({});
+  if (sizeof(data)) // passende files
+  {
+    mixed *subfiles;
+    subfiles=({});
+    path=implode(patharray[0..<2],"/")+"/";
+    while (sizeof(data))
+    {
+      subfiles=({});
+      base=data[BASENAME];
+      if (mode==MODE_LSB||(base!="."&&base!=".."))
+      {
+        //DEBUG("PATH: " + path+" BASE: " + base + " MODE: " + mode);
+        full=path+base;
+        dest2=((dest=="/"||file_size(dest[0..<2])==-2)?
+               (dest+base):(dest=="/"?"/":dest[0..<2]));
+        //DEBUG("DEST: " + dest);
+        if (recursive&&data[FILESIZE]==-2) // Verzeichnis, Rekursiv
+          subfiles=_get_files(full+"/",filemask,mode,dest2+"/");
+        if (!(filemask&&!sizeof(subfiles)&&!sizeof(regexp(({ base }),filemask,RE_TRADITIONAL))))
+        {
+          if (!filemask||mode!=MODE_RM)
+            files+=({ data[0..2]+({ full, path, dest2,sizeof(subfiles)}) });
+          if (sizeof(files)+sizeof(subfiles)>MAX_ARRAY_SIZE)
+            raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
+          files+=subfiles;
+        }
+      }
+      data=data[3..];
+    }
+    return files;
+  }
+// File existiert nicht -> Wildcard oder tatsaechlich nicht existent
+// Behandeln je nach mode
+  switch(mode)
+  {
+    case MODE_CP:
+    case MODE_MV:
+    case MODE_CD:
+    case MODE_LSA:
+      files=_get_matching(patharray+(filemask?({ "*" }):({})),1,"/",mode,
+                           recursive,dest,filemask);
+      break;
+    default: break; 
+  }
+  return files;
+}
+
+
+//
+// file_list(): Liste der Fileinformationen
+// files:     Array der Filenamen MIT Wildcards
+// mode:      Welche Daten werden benoetigt
+// recursive: Rekursiv listen?
+// dest:      Zielverzeichnis
+// mask:      Maske (regexp, wenn definiert)
+// Rueckgabe: Liste der betroffenen Files
+//
+
+static varargs mixed *file_list(string *files, int mode, int recursive, string dest, string mask)
+{
+  string *list,err,*result;
+  int i,j;
+  list=({});
+  if (mask) mask=glob2regexp(mask);
+  j=sizeof(files);
+  for(i=0;i<j;i++)
+  {
+    // Abschliessenden / von Pfadnamen abschneiden, weil in diesem Fall 
+    // die Inhalte der ersten Unterverzeichnisebene mit ausgegeben
+    // wurden. Ursache hierfuer ist ein Fix an full_path_array() im 
+    // Masterobjekt, der dazu fuehrte, dass get_dir() den abschliessenden /
+    // des uebergebenen Pfades jetzt korrekt behandelt. \o/
+    if ( sizeof(files[i]) > 1 && files[i][<1] == '/' )
+    {
+      files[i] = files[i][0..<2];
+      if ( !sizeof(files[i]) )
+        continue;
+    }
+    if (err=catch(list+=get_files(files[i],mode,recursive,dest,mask)))
+    {
+      printf("Fehler aufgetreten: %s\n",err);
+      log_file(SHELLLOG("FILE_LIST"),
+               sprintf("%s fuehrte folgendes Kommando aus: (Zeit: %s)\n"
+                       "  >>%s %s<<\n"
+                       "  Folgender Fehler trat dabei auf:\n"
+                       "  %s\n\n",
+                       capitalize(getuid())||"<Unbekannt>",dtime(time()),
+                       query_verb()||"*",_unparsed_args()||"*",err||"*"));
+      return ({});
+    }
+  }
+   return list;
+}
diff --git a/std/shells/magier/players.c b/std/shells/magier/players.c
new file mode 100644
index 0000000..c563d9d
--- /dev/null
+++ b/std/shells/magier/players.c
@@ -0,0 +1,529 @@
+// MorgenGrauen MUDlib
+//
+// players.c
+//
+// $Id: players.c 9551 2016-04-20 22:54:58Z Arathorn $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <ansi.h>
+#include <logging.h>
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <living/comm.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <moving.h>
+
+static mixed _query_localcmds()
+{
+  return ({({"zap","_zap",0,WIZARD_LVL}),
+           ({"verfolge","_verfolge",0,LEARNER_LVL}),
+           ({"trans","_trans",0,LEARNER_LVL}),
+           ({"peace","_frieden",0,LEARNER_LVL}),
+           ({"frieden","_frieden",0,LEARNER_LVL}),
+           ({"pwho","_pwho",0,WIZARD_LVL}),
+           ({"zwinge","_zwinge",0,WIZARD_LVL}),
+           ({"heal","_heile",0,WIZARD_LVL}),
+           ({"heil","_heile",1,WIZARD_LVL}),
+           ({"people","_people",0,LEARNER_LVL}),
+           ({"spieler","_spieler",0,WIZARD_LVL})});
+}
+
+//                               #######
+//################################ ZAP ##################################
+//                               #######
+
+private string _zap_message(string str, object obj)
+{
+  str=regreplace(str,"@@wer@@",(string)obj->name(WER,2),1);
+  str=regreplace(str,"@@wessen@@",(string)obj->name(WESSEN,2),1);
+  str=regreplace(str,"@@wem@@",(string)obj->name(WEM,2),1);
+  str=regreplace(str,"@@wen@@",(string)obj->name(WEN,2),1);
+  str=regreplace(str,"@@ich@@",name(WER,2),1);
+  return capitalize(str);
+}
+
+static int _zap(string str)
+{
+    object opfer;
+    string *message, dummy;
+    int spieler;
+
+    if (!str) return USAGE("zap <name>");
+    if (sscanf( str, "spieler %s", dummy ))
+    {
+      str = dummy;
+      spieler = 1;
+    }
+    if (opfer=present(str,environment()))
+    {
+      if ( !living(opfer) )
+      {
+        printf("%s ist doch gar kein Lebewesen!\n",capitalize(str) );
+        return 1;
+      }
+      if (query_once_interactive(opfer)&&!spieler )
+      {
+        printf( "Spieler kannst Du nur mit der Syntax 'zap spieler <name>' "
+                "toeten!\n" );
+        return 1;
+      }
+      else
+        if ( !query_once_interactive(opfer) && spieler )
+        {
+          printf( "Aber %s ist doch gar kein Spieler!\n",capitalize(str));
+          return 1;
+        }
+
+      message = QueryProp(P_ZAP_MSG);
+
+      if ( !pointerp(message) || sizeof(message) != 3 ){
+          tell_room(environment(),sprintf("%s beschwoert einen Blitz "
+                "vom Himmel.\n",capitalize(getuid())),({ this_object() }));
+          printf("Du toetest %s.\n",opfer->name( WEN,2));
+      }
+      else
+      {
+        printf(_zap_message(message[0],opfer));
+        tell_room(environment(),_zap_message(message[1],opfer),
+             ({this_player(),opfer}));
+        tell_object(opfer,_zap_message(message[2],opfer));
+      }
+
+      opfer->die();
+      return 1;
+  }
+  else{
+      printf("Sowas siehst Du hier nicht.\n");
+      return 1;
+  }
+}
+
+
+//                             ############
+//############################## VERFOLGE ################################
+//                             ############
+
+static int _verfolge(string str)
+{
+  // Wenn nichts eingegeben wurde, wird ver Verfolgungsmodus beendet, sofern
+  // er zuvor eingeschaltet war. Ansonsten wird eine Fehlermeldung 
+  // ausgegeben.
+  if (!sizeof(str))
+  {
+    mixed *pur = Query(P_PURSUERS);
+    if ( pointerp(pur) && sizeof(pur) && objectp(pur[0]) )
+    {
+      pur[0]->RemovePursuer(this_object());
+      ReceiveMsg("Verfolgungsmodus abgeschaltet.", MT_NOTIFICATION);
+    }
+    else
+    {
+      ReceiveMsg("Du verfolgst doch ueberhaupt niemanden.", MT_NOTIFICATION);
+    }
+    return 1;
+  }
+  str=lower_case(str);
+  
+  // match_living() erlaubt die Pruefung, ob die Angabe eindeutig war.
+  int|string lv = match_living(str);
+  if ( intp(lv) )
+  {
+    if ( lv == -2 )
+      ReceiveMsg("Kein solches Wesen gefunden.", MT_NOTIFICATION);
+    else
+      ReceiveMsg(sprintf("verfolge: '%s' ist nicht eindeutig.\n", str),
+        MT_NOTIFICATION);
+    return 1;
+  }
+  
+  // Spieler zuerst auswaehlen, danach im Raum anwesende Lebewesen.
+  object ziel = find_player(lv) || present(lv, environment(this_player()));
+  
+  // Wurde kein Lebewesen gefunden, wird das erste Element aus der Liste der
+  // Lebewesen dieses Namens gewaehlt, falls vorhanden. Nur Lebewesen mit
+  // Environment kommen in Frage, denn sonst gibt es keinen Raum, in den der
+  // neue Verfolger bewegt werden koennte.
+  if ( !objectp(ziel) ) {
+    object* eligible_livings = filter(find_livings(lv), #'environment);
+    if (sizeof(eligible_livings))
+      ziel = eligible_livings[0];
+  }
+  
+  // Endlich etwas gefunden? Dann Verfolger eintragen und zum Ziel bewegen.
+  if ( objectp(ziel) )
+  {
+    if ( ziel == this_player() ) 
+    {
+      ReceiveMsg("Du kannst Dich nicht selbst verfolgen.", MT_NOTIFICATION);
+    }
+    else
+    {
+      ReceiveMsg(sprintf(
+        "Du verfolgst jetzt %s. [%s]", ziel->name(WEN), object_name(ziel)),
+        MT_NOTIFICATION, MA_MOVE);
+      ziel->AddPursuer(this_object());
+      ziel->TakeFollowers();
+    }
+  }
+  else
+  {
+    ReceiveMsg("Kein Wesen mit dem Namen '"+lv+"' gefunden, oder nur "
+      "solche ohne Environment.", MT_NOTIFICATION, 0, "verfolge: ");
+  }
+  return 1;
+}
+
+
+//                              #########
+//############################### TRANS #################################
+//                              #########
+
+static int _trans(string str)
+{
+  object living;
+
+  if (!sizeof(str))
+    return _notify_fail("Syntax: trans <spielername>\n"),0;
+  str=match_living(str,0);
+  if (intp(str))
+    switch (str)
+    {
+      case -1: write("Das war nicht eindeutig.\n"); return 1;
+      case -2: write("So ein Wesen gibt es nicht.\n"); return 1;
+    }
+  if(living=find_living(str))
+  {
+    if (living->move(object_name(environment()),
+                     M_TPORT|M_NOCHECK)<=0)
+    {
+      printf("Teleportieren von %s fehlgeschlagen.\n",living->Name(WEM));
+      if (IS_LEARNER(living))
+        tell_object(living,sprintf("%s wollte Dich teleportieren, "
+             "hat aber versagt!\n",capitalize(getuid())));
+      return 1;
+    }
+    tell_object(living,sprintf(
+    "Ein seltsames Gefuehl ueberkommt Dich ...\n"
+    "Du verlierst die Orientierung ...\n"
+    +(QueryProp(P_INVIS)?"":"%s holt Dich zu sich.\n"),
+    capitalize(getuid())));
+    printf("%s wurde herbeizitiert.\n",living->Name(WER));
+    return 1;
+  }
+  printf("Das Lebewesen '%s' konnte nicht gefunden werden.\n",
+         capitalize(str));
+  return 1;
+}
+
+//                             ###########
+//############################## FRIEDEN #################################
+//                             ###########
+
+static int _frieden(string sname)
+{
+  object *enem,obj;
+  int i;
+  string him;
+  
+  if (!sname)
+  {
+    enem=all_inventory(environment());
+    map_objects(enem,"StopHuntingMode");
+    tell_room(environment(),sprintf("%s stiftet Frieden.\n",capitalize(getuid())),
+              ({ this_object()}));
+    printf("Du stiftest Frieden.\n");
+    return 1;
+  }
+  else
+  {
+    if (!obj=find_living(sname))
+      return printf("Kein solches Lebewesen im Spiel.\n"),1;
+    him=(string)obj->name(WEM);
+    i=sizeof(enem=(object *)(((mixed *)obj->StopHuntingMode())[0]));
+    // Mistdriver ... object** waere richtig gewesen ... *seufz*
+    while(i--)
+    {
+      enem[i]->StopHuntFor(obj);
+      tell_object(obj,sprintf("%s beendet Deinen Kampf mit %s.\n",
+                              capitalize(getuid()),enem[i]->Name(WEM)));
+      tell_object(enem[0],sprintf("%s beendet Deinen Kampf mit %s.\n",
+                                  capitalize(getuid()),him));
+    }
+  }
+  printf("%s und alle Gegner wurden befriedet.\n",obj->Name(WER));
+  return 1;
+}
+
+//                              ########
+//############################### PWHO ##################################
+//                              ########
+
+#if __VERSION__ < "3.2.9"
+private int _pwho_learner_test(object ob)
+{
+  return !IS_LEARNER(ob);
+}
+#endif
+
+static int _pwho()
+{
+  mixed* spieler, res, *hands;
+  int i;
+#if __VERSION__ < "3.2.9"
+  spieler = filter(users(),#'_pwho_learner_test);
+#else
+  spieler = filter(users(),(: return !IS_LEARNER($1); :));
+#endif
+  spieler = sort_array(spieler, function int (object a, object b)
+      { return a->QueryProp(P_LEVEL) > b->QueryProp(P_LEVEL); } );
+  
+  res = "Lvl Name         Erfahrung   QP  Int Str Dex Con WC   "
+    "AC   HANDS HP  (max)\n"
+    "--------------------------------------------------------------"
+    "-----------------\n";
+  for (i=sizeof(spieler)-1; i>=0; i--)
+    res += sprintf("%3d %-12s %9d %5d %3d %3d %3d %3d %4d %4d  %5d "
+                   "%4d (%4d)\n",
+     spieler[i]->QueryProp(P_LEVEL),
+     capitalize(getuid(spieler[i])),
+     spieler[i]->QueryProp(P_XP),
+     spieler[i]->QueryProp(P_QP),
+     spieler[i]->QueryAttribute(A_INT),
+     spieler[i]->QueryAttribute(A_STR),
+     spieler[i]->QueryAttribute(A_DEX),
+     spieler[i]->QueryAttribute(A_CON),
+     spieler[i]->QueryProp(P_TOTAL_WC),
+     spieler[i]->QueryProp(P_TOTAL_AC),
+     (sizeof(hands=((int *)spieler[i]->QueryProp(P_HANDS)))?hands[1]:0),
+     spieler[i]->QueryProp(P_HP),
+     spieler[i]->QueryProp(P_MAX_HP));
+  More(res);
+  return 1;
+}
+
+//                             ##########
+//############################## ZWINGE #################################
+//                             ##########
+
+static int _zwinge(string str)
+{
+  object living;
+  string what, rest;
+  string living_name;
+
+  str = _unparsed_args();
+  if(!str|| sscanf( str, "%s %s", living_name, what ) != 2 )
+    return _notify_fail("Zwinge WEN zu WAS?\n"),0;
+  if( sscanf( what, "zu %s", rest ) == 1 ) what = rest;
+  if (!(living = find_living(living_name)))
+    return printf ("Ein Lebewesen namens '%s' konnte nicht gefunden werden!\n",
+                   capitalize(living_name)),1;
+  if (living->command_me(what))
+  {
+    printf("Du zwingst %s zu \"%s\".\n",capitalize(living_name),what);
+    if (!IS_ARCH(this_object())&&getuid()!=(string)living->QueryProp(P_TESTPLAYER))
+      log_file(SHELLLOG("ZWINGE"),
+               sprintf("%s zwingt %s (%s) zu %s [%s]\n",
+                       capitalize(getuid()),living->Name(),capitalize(getuid(living)),
+                       what,dtime(time())));
+  }
+  else
+    write("Hat leider nicht geklappt!\n");
+  return 1;
+}
+
+//                              #########
+//############################### HEILE #################################
+//                              #########
+
+static int _heile(string name)
+{
+  object ob;
+  int lpv, mpv;
+
+  if (!name) return USAGE("heile <name>");
+  name = lower_case(name);
+  if ((!(ob = present(name,environment())))
+      ||!living(ob))
+    ob = find_living(name);
+  if (!ob)
+  {
+    printf("'%s' ist momentan nicht da.\n",capitalize(name));
+    return 1;
+  }
+  lpv = (int)ob->QueryProp(P_HP);
+  mpv = (int)ob->QueryProp(P_SP);
+  ob->heal_self(1000000);
+  if (!IS_LEARNER(ob) && (!ob->QueryProp(P_TESTPLAYER)||
+          (((string)ob->QueryProp(P_TESTPLAYER))[<5..<1]=="Gilde")))
+    log_file(SHELLLOG("HEAL"),
+       sprintf("%s heilt %s (%s) %s (LP: %d -> %d, MP: %d -> %d)\n",
+         capitalize(geteuid(this_player())),
+         call_other(ob,"name"), capitalize(geteuid(ob)),
+         dtime(time()), lpv, (int)ob->QueryProp(P_HP),
+               mpv,(int)ob->QueryProp(P_SP)));
+  tell_object(ob, QueryProp(P_NAME) + " heilt Dich.\n");
+  printf("Du heilst %s.\n",capitalize(name));
+  return 1;
+}
+
+//                             ##########
+//############################## PEOPLE #################################
+//                             ##########
+
+private string _people_filename(object obj)
+{
+  string str;
+  str=object_name(environment(obj));
+  if (!str) return 0;
+  if (str[0..2] == "/d/") return sprintf("+%s",str[3..<1]);
+  if (str[0..8] == "/players/") return sprintf("~%s",str[9..<1]);
+  return str;
+}
+
+static int _people()
+{
+  mixed *list, res;
+  int i,j, a;
+  string a_age,a_ipnum,a_name,a_level,a_idle,a_room,a_end, a_title;
+
+  switch(QueryProp("tty"))
+  {
+    case "vt100":
+      a_ipnum = ""; a_name = ANSI_BOLD;
+      a_level = ANSI_NORMAL; a_idle = ANSI_BLINK;
+      a_room = ANSI_NORMAL; a_end = ANSI_NORMAL;
+      a_title = ANSI_INVERS; a_age = ANSI_NORMAL;
+      break;
+    case "ansi":
+      a_ipnum = ANSI_BLUE; a_name = ANSI_BOLD;
+      a_level = ANSI_RED; a_idle = ANSI_BLACK+ANSI_BOLD;
+      a_room = ANSI_BOLD+ANSI_BLUE; a_end = ANSI_NORMAL;
+      a_title = ANSI_INVERS; a_age = ANSI_PURPLE;
+      break;
+    default:
+      a_title = a_ipnum = a_name = a_level = a_idle = a_room = a_end = "";
+      a_age = "";
+  }
+  list = sort_array(users(), function int (object a, object b) {
+      return query_ip_number(a)>query_ip_number(b);} ); 
+
+  j=sizeof(list);
+  a=0;res="";
+  for(i=0; i<sizeof(list); i++) {
+    string name_; 
+    name_ = capitalize(list[i]->query_real_name()||"<logon>");
+    res += sprintf( "%s%-15s%s %s%-13s%s %s%3d%s %s %s %s%s%s%s %s%s\n",
+                    a_ipnum, query_ip_number(list[i]),a_end,a_name,
+                    (list[i]->QueryProp(P_INVIS)?"("+name_+")":name_),
+                    a_end,a_level, MASTER->get_wiz_level(getuid(list[i])),
+                    a_end,a_age,
+                    time2string("%4x %0X",((int)list[i]->QueryProp(P_AGE))*2),
+                    query_idle(list[i])>=300?(a++,(a_idle+"I")):" ",
+                    a_end,
+                    query_editing(list[i])?a_idle+"E"+a_end:" ",
+                    query_input_pending(list[i])?a_idle+"M"+a_end:" ",
+                    environment(list[i])?a_room+_people_filename(list[i]):"",
+                    a_end);
+  }
+  if (a)
+    res = sprintf("%s%d Spieler anwesend (%d aktiv). %s%s\n",a_title,j,
+                  (j-a),query_load_average(),a_end)+res;
+  else
+    res = sprintf("%s%d Spieler anwesend. %s%s\n",a_title,j,
+                  query_load_average(),a_end)+res;
+  More(res);
+
+  return 1;
+}
+
+
+//                             ###########
+//############################## SPIELER #################################
+//                             ###########
+
+private string _spieler_time2string(int time)
+{
+  string ret;
+
+  ret="";
+  if (time>=86400)
+  {
+    ret+=time/86400+"d ";
+    time%=86400;
+  }
+  if(time<36000) ret+="0";
+  ret+=time/3600+":";
+  time%=3600;
+  if(time<600) ret+="0";
+  ret+=time/60+":";
+  time%=60;
+  if(time<10) ret+="0";
+  ret+=time+"";
+  return ret;
+}
+
+
+static int _spieler(string arg)
+{
+  string dummy,ip;
+  object *spieler,pl;
+  int i;
+  
+  arg=_unparsed_args();
+  if(!sizeof(arg) || sscanf(arg,"aus ip %s",dummy)!=1)
+    return USAGE("spieler aus ip [von <spieler>|<ip>]");
+  arg=dummy;
+  if (sscanf(arg,"von %s",dummy)==1)
+  {
+    dummy=lower_case(dummy);
+    if (!(pl=find_player(dummy)))
+      return notify_fail(sprintf("Spieler '%s' konnte nicht gefunden "
+                                 "werden.\n",capitalize(dummy))),0;
+    ip=query_ip_number(pl);
+  }
+  else ip=arg;
+  ip=implode((explode(ip,".")-({""})+({"*","*","*","*"}))[0..3],".");
+if (catch(
+  spieler=filter(users(),
+    (: return sizeof(regexp(({query_ip_number($1)}),$2)); :),"^"+glob2regexp(ip)+"$")
+
+                                      ))
+  return printf("In der IP duerfen nur Zahlen(0-255), Punkte (.) und "
+                "Sterne (*) vorkommen.\n"),1;
+  if (!sizeof(spieler))
+    return printf("Es konnte kein Spieler mit der IP '%s' gefunden "
+                  "werden.\n",ip),1;
+  arg=sprintf("\nFolgende Spieler haben die IP %s:\n"
+       "================================================================"
+              "===========\n"
+       "Name:       Zweitie von:      Eingeloggt:                  "
+              "Idle seit:\n"
+       "----------------------------------------------------------------"
+              "-----------\n",ip);
+  i=sizeof(spieler);
+  while(i--)
+  {
+    arg+=sprintf("%-11s %-17s %26s  %-15s\n",
+                 capitalize(getuid(spieler[i])),
+                 ((dummy=(string)spieler[i]->QueryProp(P_SECOND))?
+                  (sizeof((mixed *)call_other(master(),
+                                              "get_userinfo",dummy))?
+                   capitalize(dummy):"*ungueltig*"):""),
+                 dtime(spieler[i]->QueryProp(P_LAST_LOGIN)),
+                 _spieler_time2string(query_idle(spieler[i])));
+  }
+  arg+="==============================================================="
+    "============\n\n";
+  More(arg);
+  return 1;
+}
diff --git a/std/shells/magier/todo.c b/std/shells/magier/todo.c
new file mode 100644
index 0000000..f4120e7
--- /dev/null
+++ b/std/shells/magier/todo.c
@@ -0,0 +1,222 @@
+// $Id: todo.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <defines.h>
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <player.h>
+
+#define SAVEFILENAME sprintf("/players/%s/.todoliste",getuid())
+
+private nosave status todo_initialized;
+private nosave mixed *todo_data;
+mixed *todo_data_public;
+static mixed _query_localcmds()
+{
+  return ({
+    ({"todo","_todo",0,WIZARD_LVL})});
+}
+
+private void todo_save()
+{
+
+  int i,j;
+  string a,b;
+  a=SAVEFILENAME+".o";
+  rm(a);
+  if (j=sizeof(todo_data))
+  {
+#if !__EFUN_DEFINED__(save_value)   
+    write_file(a,"#0:0\ntodo_data_public ({");
+    for (i=0;i<j;i++)
+    {
+      b=regreplace(todo_data[i][1],"\"","\\\"",1);
+      b=regreplace(b,"\n","\\n",1);
+      write_file(a,sprintf("({%d,\"%s\",}),",
+                           todo_data[i][0],b));
+    }
+    write_file(a,"})\n");
+#else
+    todo_data_public=todo_data;
+    write_file(a,save_value(todo_data_public));
+    todo_data_public=0;
+#endif
+  }
+  return;
+
+}
+
+static void initialize()
+{
+  if(!IS_WIZARD(this_object())) return;
+  if (!restore_object(SAVEFILENAME)) todo_data=({});
+  else
+    {
+      todo_data=todo_data_public;
+      todo_data_public=0;
+    }
+  return;
+}
+
+static void _todo_neu2(string input,string carry)
+{
+  if (input=="q"||(input=="."&&!sizeof(carry)))
+  {
+    printf("Abbruch!");
+    return;
+  }
+  if (input!=".")
+  {
+    printf("]");
+    input_to("_todo_neu2",0,carry+input+"\n");
+    return;
+  }
+  todo_data+=({ ({ time(), carry }) });
+  todo_save();
+  printf("Eintrag angelegt. Laufende Nummer ist %d.\n",sizeof(todo_data));
+  return;
+}
+
+private int _todo_neu(string cmdline)
+{
+  if (cmdline!="neu") return 0;
+  printf("Was musst Du noch machen? (Abbruch mit 'q', "
+         "Ende mit '.')\n]");
+  input_to("_todo_neu2",0,"");
+  return 1;
+}
+
+private int _todo_loeschen(string cmdline)
+{
+  int nr;
+  if (!sscanf(cmdline,"loeschen %d",nr))
+    return notify_fail("Welchen Eintrag moechtest Du loeschen?\n"),0;
+  if (!sizeof(todo_data))
+    return notify_fail("Deine Todo-Liste hat ja ueberhaupt keinen "
+                       "Eintrag!\n"),0;
+  if (nr>sizeof(todo_data))
+  {
+    printf("Deine Todo-Liste hat nur %d Eintra%s.\n",sizeof(todo_data),
+           (sizeof(todo_data)==1?"g":"ege"));
+    return 1;
+  }
+  todo_data[nr-1]=0;
+  todo_data-=({0});
+  todo_save();
+  printf("Eintrag Nummer %d wurde geloescht. Da hast Du ja ordentlich "
+         "was geschafft...\noder geschickt delegiert ;-)\n",nr);
+  return 1;
+}
+
+private int _todo_anzeigen(string cmdline)
+{
+  int nr1,nr2;
+  string output;
+  if (!sizeof(todo_data))
+    return notify_fail("Deine Todo-Liste hat keine Eintraege.\n"),0;
+  switch(sscanf(cmdline,"anzeigen %d bis %d",nr1,nr2)) 
+  {
+  case 0: nr1=1; nr2=sizeof(todo_data); break;
+  case 1: nr2=nr1; break;
+  case 2: break;
+  }
+  if (nr1<0) nr1=sizeof(todo_data)+nr1+1;
+  if (nr2<0) nr2=sizeof(todo_data)+nr2+1;
+  if (nr1<1||nr2<1||nr1>sizeof(todo_data)||nr2>sizeof(todo_data))
+    return notify_fail(sprintf("Deine Todo-Liste hat (gluecklicherweise) "
+                               "nur %d Eintra%s.\n",sizeof(todo_data),
+                               (sizeof(todo_data)>1?"ege":"g"))),0;
+  output="\n-----------------------------------------------------------------------------\n";
+  if (nr1>nr2) // Rueckwaerts
+  {
+    while(nr1>=nr2)
+      output+=sprintf("Eintrag Nr. %d von %s:\n%s\n\n",nr1--,
+           dtime(todo_data[nr1][0]),break_string(todo_data[nr1][1],78,5,
+                                       BS_LEAVE_MY_LFS));
+  }
+  else
+  {
+    nr1--;
+    while (nr1<nr2)
+      output+=sprintf("\nEintrag Nr. %d von %s:\n%s\n",(nr1+1),
+           dtime(todo_data[nr1][0]),break_string(todo_data[nr1++][1],78,5,
+                                       BS_LEAVE_MY_LFS));
+  }
+  More(output+"-----------------------------------------------------------------------------\n");
+  return 1;
+}
+
+private int _todo_verschieben(string cmdline)
+{
+  int from;
+  mixed to,dummy;
+  if (!(sscanf(cmdline,"verschieben %d nach %d",from,to)==2||
+        sscanf(cmdline,"verschieben %d nach %s",from,to)==2))
+    return 0;
+  from--;
+  if (stringp(to))
+  {
+    switch(to)
+    {
+      case "oben": to=from-1; break;
+      case "unten": to=from+1; break;
+      default: return 0;
+    }
+  }
+  else to--;
+  if (to==from) return notify_fail("Da ist der Eintrag ja schon!\n"),0;
+  if (from<0||from>=sizeof(todo_data)) return notify_fail(
+        "Wie willst Du einen nicht existenten Eintrag verschieben?\n"),0;
+  if (to<0||to>=sizeof(todo_data)) return notify_fail(
+        "Dahin kannst Du den Eintrag nicht verschieben.\n"),0;
+  dummy=todo_data[from];
+  if (to>from)
+    while (from++<to) todo_data[from-1]=todo_data[from];
+  else
+    while(from-->to) todo_data[from+1]=todo_data[from];
+  todo_data[to]=dummy;
+  printf("Eintrag wurde verschoben.\n");
+  return 1;
+}
+
+private int _todo_hilfe()
+{
+  printf(
+  "-------------------------------------------------------------------------------\n\n"
+  " Die Todo-Liste laesst sich mit den folgenden Befehlen steuern:\n\n"
+  " todo neu                                - Legt einen neuen Eintrag an.\n"
+  " todo loeschen <nummer>                  - Loescht einen Eintrag.\n"
+  " todo anzeigen [<nummer> [bis <nummer>]] - Zeigt die Todo-Liste an.\n"
+  " todo verschieben <nummer> nach <ziel>   - Verschiebt einen Eintrag.\n\n"
+  " <ziel> kann eine Zahl oder die Worte 'oben' oder 'unten' sein.\n"
+  "-------------------------------------------------------------------------------\n");
+  return 1;
+}
+
+
+static int _todo(string cmdline)
+{
+  string *args;
+  
+  cmdline=_unparsed_args(1);
+  notify_fail("Falsche Syntax. 'todo hilfe' zeigt eine Hilfe an.\n");
+  if (sizeof(cmdline))
+  {
+    args=explode(cmdline," ");
+    switch(args[0])
+    {
+      case "neu":         return _todo_neu(cmdline);
+      case "loeschen":    return _todo_loeschen(cmdline);
+      case "anzeigen":    return _todo_anzeigen(cmdline);
+      case "verschieben": return _todo_verschieben(cmdline);
+      case "hilfe":       return _todo_hilfe();
+     default:            return 0;
+    }
+  }
+  return 0;
+}
diff --git a/std/shells/magier/upd.c b/std/shells/magier/upd.c
new file mode 100644
index 0000000..f09ea9a
--- /dev/null
+++ b/std/shells/magier/upd.c
@@ -0,0 +1,563 @@
+// MorgenGrauen MUDlib
+//
+// upd.c
+//
+// $Id: upd.c 8850 2014-06-13 21:34:44Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+#include <debug_info.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <properties.h>
+#include <logging.h>
+#include <thing/properties.h>
+
+varargs static int _make(string file, int flags, int recursive);
+
+static mixed _query_localcmds()
+{
+  return ({({"upd","_upd",0,LEARNER_LVL}),
+           ({"load","_load",0,LEARNER_LVL})});
+}
+
+//
+// _save(): Spieler in Rettungsraum retten
+// obj:      Spielerobjekt(?)
+// inv_save: Rettungsraumname 
+// Rueckgabe: 0 wenn kein Spielerobjekt
+//            Spielerobjekt, falls doch
+//
+
+static mixed _save( object obj, object inv_saver )
+{ 
+    if ( query_once_interactive(obj) )
+      { 
+        obj->move( inv_saver, NO_CHECK );
+        return obj;
+      }
+    return 0;
+}
+
+
+//
+// _reload(): Objekt laden
+// file:  Filename. Muss in der Form /xx/xx/xx.c vorliegen
+// clone:  > 0 -> Es soll geclont werden, enthaelt Objektnummer des
+//                Vorgaengerobjektes
+// flags: Kommandozeilenoptionen
+// err:   Leerstring als Referenz uebergeben. Enthaelt nach dem
+//        Aufruf vom _reload() die Fehlermeldungen als String.
+// Rueckgabe: Das neu erzeugte Objekt bzw. das schon vorhandene Objekt
+//            bzw. 0, wenn Fehler auftrat.
+//
+
+private object _reload(string file, int clone, int flags, string err)
+{
+  object obj;
+  
+  if (!obj=find_object(file[0..<3]+(clone?("#"+clone):"")))
+  {
+    int pos,pos2;
+    string bt;
+
+    if(file_size(file)<0)
+    {
+      if (file_size(file)==-1)
+        err = sprintf("upd: %s: Datei existiert nicht.\n", file);
+      else // directory
+        err = sprintf("upd: %s: Verzeichnisse koennen nicht geladen "
+                      "werden.\n",file);
+      return obj; // 0
+    }
+    pos = max(file_size(__DEBUG_LOG__),0);
+
+    if ((err = (clone?catch(obj = clone_object(file)):
+                catch(load_object(file)) )) && (flags & UPD_B))        
+    {     
+      if (( pos2=file_size(__DEBUG_LOG__)) > pos )
+        err+=sprintf("\nBacktrace:\n%s",
+                     read_bytes(__DEBUG_LOG__,pos, pos2-pos ));
+      else
+        err+=sprintf("\nKEIN BACKTRACE VERFUEGBAR!\n");
+    }
+    if (!err&&!obj&&(!obj = find_object(file[0..<3])))
+      err += sprintf( "upd: %s: Blueprint nach dem Laden zerstoert.\n",file );
+  }
+  else
+    err=sprintf("upd: Objekt existiert schon: %O\n",obj);
+  return obj;
+}
+
+
+//
+// _update(): File updaten -> Blueprint destructen
+// file:  Filename
+// dummy: simulieren? (1->wird nicht aktualisiert)
+// flags: Kommandozeilenoptionen
+// Rueckgabe: -1: Keine Vollzugriff
+//             0: Objekt ist nicht geladen
+//             1: Operation wird durchgefuehrt
+//
+
+private varargs int _update(string file, int dummy, int flags)
+{
+  object obj;
+  string err;
+  if (!dummy && !objectp(obj = find_object(file))) return 0;
+  if (!IS_ARCH(this_object()))
+  {
+    // Schreibrechte nur pruefen, wenn echt aktualisiert werden soll.
+    if(!dummy && !MAY_WRITE(file))
+      return (printf("upd: %s: Keine Schreibrechte!\n",file), -1);
+    if(!MAY_READ(file)) 
+      return (printf("upd: %s: Keine Leserechte!\n", file), -1);
+  }
+  if (dummy) return 1;
+  
+  if ( flags & UPD_D )
+  {
+    object *inv;
+    if (sizeof(inv = deep_inventory(obj)))
+    {
+      printf("upd: %s: Entferne Objekte im Inventar\n", file );
+      if (!(flags&UPD_H))
+      {
+        if(err=catch(filter_objects( inv, "remove", 1 )))
+          printf("upd: %s: Fehlgeschlagen. Grund:\n%s\n",
+                 file,err);
+      }
+      if (sizeof(inv = deep_inventory(obj)))
+        filter(inv, function void (object ob)
+            {destruct(ob);});
+    }
+  }
+  if (!(flags&UPD_H))
+  {
+    if(err = catch(obj->remove()))
+      printf("upd: %s: remove() fehlgeschlagen. Aufruf von " +
+             "destruct().\n",file);
+  }
+  if(objectp(obj)) destruct(obj);
+  return 1;
+}
+
+
+//
+// _instance_upd(): Alle Objekte nach Clones des Objekts durchsuchen
+// file:  Filename des Objektes
+// flags: Kommandozeilenargumente
+// obj:   Aktuelles Objekt
+// instances: Zahl der gefundenen Instanzen
+//
+
+private void _instance_upd(string file, int flags, mixed obj, int instances,
+                           int firstcall)
+{
+ int i;
+ if (firstcall)
+    printf("upd: %s: %s Instanzen.\n",file,flags&UPD_A?"Aktualisiere":"Suche");
+  
+  while (get_eval_cost()>220000 && i < sizeof(obj))
+  {
+    if (!objectp(obj[i]))
+      instances--;
+    else
+    {
+      if (flags&UPD_F&&!(flags&UPD_S))
+        printf( "upd: %O gefunden in %s\n", obj[i],
+                environment(obj[i])?object_name(environment(obj[i])) 
+                : "keiner Umgebung" );
+      if (flags&UPD_A) _make(object_name(obj[i]), flags & ~(UPD_A|UPD_F),1 );
+    }
+    i++;
+  }
+  if (i < sizeof(obj)) 
+    call_out( #'_instance_upd/*'*/,2,file,flags,obj[i..],instances,0);
+  else
+    printf( "upd: %s: %d Instanzen %s\n", file, instances,
+            (flags & UPD_A) ? "aktualisiert" : "gefunden" );
+  return;
+}
+  
+  
+//
+// _do_make(): Alle geerbten Objekte bearbeiten (fuer -m/-v)
+// file:  Name des Files
+// clone: 0, wenn blueprint, ansonsten Clonenummer
+// flags: Kommandozeilenparameter
+// dep:   geschachteltes Array mit Meldungen (wg. Rekursion)
+// ready: Array der schon bearbeiteten Objekte (wg. Rekursion)
+// Rueckgabe: Array der Objektnamen, die bearbeitet wurden
+//            (Array in Array in ...)
+// 
+
+varargs private int _do_make( string file,int clone,int flags,mixed dep,
+                              string *ready )
+{
+  object obj;
+  string err;
+  string *ilist;
+  mixed downdeps;
+  int ret;
+  
+  if (!pointerp(ready)) ready = ({});
+  ready += ({ file });
+  
+  if ( !(obj = _reload(file,clone,flags,&err)))
+  {
+    dep += ({ err });
+    return 0;
+  }
+  
+  ilist = inherit_list(obj)-ready;
+  
+  downdeps = ({});
+  
+  while (sizeof(ilist))
+  {
+    ret = _do_make( ilist[0],0,flags, &downdeps, &ready )||ret;
+    ilist[0..0] = ({});
+    ilist -= ready;
+  }
+  
+  if ( ret||file_time(file)>program_time(obj)||(flags &UPD_I))
+    if ( _make( file, flags & ~(UPD_M|UPD_I) ,1) < 0 ) 
+      dep = ({ "{" + explode(file,"/")[<1] + "}", downdeps });
+    else{
+      dep = ({ "[" + explode(file,"/")[<1] + "]", downdeps });
+      ret = 1;
+    }
+  else if (flags&UPD_V) dep += ({ explode(file,"/")[<1], downdeps });
+  return ret;
+}
+
+
+//
+// _make_dep(): Ausgabe des Ererbungsbaumes
+// Objekte im Array dep
+// prefix enthaelt Zeilenanfang (fuer Rekursion)
+// Rueckgabe: String mit dem Vererbungsbaum des Objektes
+//
+
+private string _make_dep( mixed dep, string prefix )
+{
+  string ret;
+  int i, size;
+  
+  ret="";
+  size=sizeof(dep);
+  for (i=0; i<size;i++)
+    if (pointerp(dep[i]))
+      ret += _make_dep(dep[i],prefix + (i < (size-1) ? "| ":" "));
+    else 
+      ret += prefix + "+-" + dep[i] + "\n";
+  return ret;
+}
+
+
+//
+// _illegal_closure(): ist closure in arg an objekt gebunden?
+// arg: closure(-array/mapping)
+// Rueckgabe: 0 wenn alles okay
+//            1 wenn closure geloescht werden muss
+//
+
+private int _illegal_closure( mixed arg )
+{
+  int i, j;
+  string *indices;
+  
+  if ( closurep(arg) && !objectp(query_closure_object(arg)) )
+    return 1;
+  
+  if ( pointerp(arg) ){
+    for ( i = sizeof(arg); i--; )
+      if ( _illegal_closure(arg[i]) )
+        return 1;
+  }
+  else if ( mappingp(arg) ){
+    indices = m_indices(arg);
+    for ( i = sizeof(indices); i--; )
+      for ( j = get_type_info( arg, 1 ); j--; )
+        if ( _illegal_closure(arg[indices[i], j]) )
+          return 1;
+  }
+  return 0;
+}
+
+//
+// _make(): Update file
+// file:  Filename
+// flags: Kommandozeilenargumente
+//
+
+varargs static int _make(string file, int flags,int recursive)
+{
+  string msg, err, blue;
+  int inst;
+  object obj, inv_saver;
+  mixed tmp;
+  
+  msg = "";
+
+  if (!file) return printf( "upd: Kein Filename uebergeben!\n" ), RET_FAIL;
+    
+// Filename in Blue, Objektname in blue, Instanznummer in inst
+
+  if (sscanf(file,"%s#%d",blue,inst)==2) blue += ".c";
+  else blue = file + (file[<2..]==".c" ? "" : ".c");
+
+// Alle Instanzen durchsuchen im Falle von -a oder -f 
+
+  if ((flags & UPD_LOAD)&&find_object(file))
+    return printf("load: %s: Objekt ist schon geladen.\n",file),RET_OK;
+  
+  if ( flags & (UPD_F|UPD_A))
+  {
+    if (inst) return printf( "upd: %s: Eine Instanz kann keine " +
+                             "Clones haben.\n",file ), RET_FAIL;
+    if ((tmp=_update(file, 1,flags))==-1) 
+      return printf( "upd: %s: Kein Vollzugriff auf die " +
+                     "Datei erlaubt.\n",file), RET_FAIL;
+    if (tmp==0) return RET_FAIL;
+
+   tmp=clones(blue[0..<3],2);
+    if (sizeof(tmp))
+      call_out( #'_instance_upd/*'*/, 0, file,flags,tmp,sizeof(tmp),1);
+    else
+      printf( "upd: %s: Keine Clones vorhanden!\n", blue[0..<3]);
+
+    if ( (flags & UPD_F) && !(flags &(UPD_R|UPD_L|UPD_LOAD))) return RET_OK; 
+    // Nichts laden -> Auch kein Backup
+  }
+
+// Backupraum festlegen
+   
+  if( blue==INV_SAVE ) {
+                printf("upd: Achtung: Raum zum Zwischenspeichern soll geladen werden?!\n");
+        } 
+  if ( !(inv_saver=load_object(INV_SAVE)) )
+  {
+    printf("upd: %s: Raum zum Zwischenspeichern des " +
+           "Rauminhalts ist nicht ladbar.\n" +
+           "         %s\n",file,INV_SAVE);
+    return RET_FAIL;
+  }
+
+// Wenn das Objekt existiert bzw. Deep aktualisiert werden soll
+
+  if ( (!(flags&UPD_LOAD))&&
+       ((obj = find_object(file)) || (flags & (UPD_M|UPD_I))))
+  {
+    object *inv, env, *pl_inv;
+    mapping pro;
+    int i;
+    mixed configdata;
+    int restore_config;
+
+    // Wenn Objekt existiert, dann Inhalt und ggf. Daten aus Configure() oder
+    // Props sichern:
+    if (obj)
+    {
+      // im Fall UPD_C erfolgt _kein_ Auslesen und _keine_ Restauration
+      // mittels Configure()
+      if ( ! (flags & UPD_C ) )
+      {
+        catch(restore_config=(mixed)call_resolved(&configdata,obj,
+                                                  "Configure",0);
+              publish);
+        // Wenn UPD_CONF gesetzt wird, _muss_ das Objekt ein oeffentliches
+        // Configure() definieren, sonst erfolgt Abbruch.
+        if ((flags & UPD_CONF) && !restore_config)
+        {
+          printf("upd: %s: hat kein Configure(), Zerstoerung abgebrochen.\n",file);
+          return RET_FAIL;
+        }
+      }
+      if (!(flags&UPD_D)&&(flags&(UPD_L|UPD_R)))
+      {
+        if (i=sizeof(inv=(all_inventory(obj)-({0}))))
+        {
+          mixed items;
+          // Herausbekommen, ob hier Items existieren, die per AddItem 
+          // erzeugt werden. Die duerfen nicht gesichert werden.
+          items=(mixed)obj->QueryProp(P_ITEMS); // mixed, da array of arrays
+          if (pointerp(items)&&sizeof(items))
+          {
+            items=transpose_array(items)[0];
+            while (i--)
+              if (member(items, inv[i])==-1)
+                    inv[i]->move(inv_saver,NO_CHECK);
+          }
+          else // In diesem Objekt sind keine Items gesetzt.
+            while (i--) inv[i]->move(inv_saver,NO_CHECK);
+        }
+      }
+      else
+      {
+        inv=map( deep_inventory(obj), #'_save/*'*/, inv_saver )-({0});
+      }
+      env = environment(obj);
+      if ( flags & UPD_C )
+      {
+        pro = (mapping)(obj->QueryProperties());
+      }
+    }
+    else inv = ({});
+
+// Ererbte Objekte durchsuchen.
+    if ( flags & (UPD_M|UPD_I) )
+    {
+      mixed dep;
+      dep = ({});
+      _do_make( blue, inst, flags & ~(UPD_M|UPD_L|UPD_R|UPD_F|UPD_A), &dep );
+      printf( _make_dep( dep, "" ) + "\n" );
+    }
+   
+// Tatsaechlichen Update durchfuehren
+   
+    if ( _update(file, 0, flags)< 0) return RET_FAIL;
+    msg += (inst ? "zerstoert" : "aktualisiert");
+    
+// Neu laden ??
+    if ( flags & (UPD_R|UPD_L) )
+    {
+      if ( obj = _reload( blue,inst,flags, &err ) )
+        msg += ", " + (inst ? "neu geclont" : "neu geladen");
+     
+// Neu geladen: Properties wiederherstellen, Closures filtern 
+      if (!err)
+      {
+        if (!obj) obj = find_object(file);
+        // Wenn gewuenscht, Props zurueckschreiben (hat Prioritaet vor
+        // Configure(), weil explizit vom Magier angefordert).
+        if ( pro && (flags & UPD_C) )
+        {
+          string *names;
+          
+          names = m_indices(pro);
+          
+          // Closures in (mittlerweile) zerstoerten Objekten
+          // rausfiltern, damit die (hoffentlich korrekten) Closures
+          // im neu geclonten Objekt erhalten bleiben
+          for ( i = sizeof(names); i--; )
+            if ( _illegal_closure(pro[names[i], F_VALUE]) ||
+                 _illegal_closure(pro[names[i], F_QUERY_METHOD]) ||
+                 _illegal_closure(pro[names[i], F_SET_METHOD]) )
+              m_delete( pro, names[i] );
+          
+          obj->SetProperties(pro);
+          msg += ", Properties gesetzt";
+        }
+        // Wenn kein UPD_C, wird ggf. das Ergebnis von Configure() wieder
+        // uebergeben.
+        else if (restore_config)
+        {
+          int conf_res;
+          if (!catch(conf_res=(int)obj->Configure(configdata); publish)
+              && conf_res == 1)
+          {
+            msg += ", (re-)konfiguriert";
+          }
+          else
+          {
+            msg += ", (Re-)Konfiguration fehlgeschlagen";
+            if (flags & UPD_CONF)
+              printf("upd: Daten von %s konnten nicht rekonfiguriert werden: "
+                           "%O\n", file, configdata);
+          }
+        }
+        if (env)
+        {
+          if ( obj->move( env, NO_CHECK ) <= 0 )
+            printf( "upd: /%O konnte nicht in /%O zurueckbewegt werden\n",
+                    obj, env );
+          else
+            msg += ", bewegt";
+        }
+        if (i=sizeof(inv))
+        {
+          while(i--) if (inv[i]) inv[i]->move(obj, NO_CHECK );
+          msg += ", Inhalt zurueckbewegt";
+        }
+      }
+      else 
+        return printf( "upd: %s: %s", file, err ), RET_FAIL;
+    }
+  }
+  else 
+    if ( !_update(file, 0, flags) && (flags & (UPD_L|UPD_LOAD)) )
+      if ( !_reload( blue, inst, flags, &err ) )
+        return printf( "%s: %s: %s", (flags&UPD_LOAD?"load":"upd"),file, err ),
+          RET_FAIL;
+      else
+        msg += "geladen";
+  
+  if ( sizeof(msg)&&!(flags&UPD_S&&recursive) )
+    printf("%s: %s: %s.\n",(flags&UPD_LOAD?"load":"upd"),file,msg);
+  return RET_OK;
+}
+
+//
+// _upd: Objekte laden, zerstoeren und aktualisieren
+//
+
+static int _upd(string cmdline)
+{
+  int flags;
+  mixed *args;
+
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,UPD_OPTS,1);
+  if(flags==-1||!sizeof(args))
+    return USAGE("upd [-"+UPD_OPTS+"] <datei> [<datei> ..]");
+  if ((flags & UPD_C) && (flags & UPD_CONF))
+  {
+    printf("upd: -c und -C gleichzeitig werden nicht unterstuetzt.\n");
+    return 1;
+  }
+  args=file_list(args,MODE_UPD,0,"/");
+  if(!sizeof(args)) return printf("upd: Keine passende Datei gefunden!\n"),1;
+
+  args=map(args,(: $1[FULLNAME] :))-({0});
+
+  if(!sizeof(args))
+  {
+    printf("upd: Verzeichnisse koennen nicht aktualisiert werden!\n");
+    return 1;
+  }
+  asynchron(args,#'_make,flags,0,0);
+  return 1;
+}
+
+//
+// _load: Objekte laden
+//
+
+static int _load(string cmdline)
+{
+  int flags;
+  mixed *args;
+
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,"",1);
+  if(flags==-1||!sizeof(args))
+    return USAGE("load <datei> [<datei> ..]");
+  args=file_list(args,MODE_UPD,0,"/");
+  if(!sizeof(args)) return printf("load: Keine passende Datei gefunden!\n"),1;
+  args=map(args,(: (($1[FILESIZE]!=-2||find_object($1[FULLNAME]))?
+                          $1[FULLNAME]:0) :))-({0});
+
+  if(!sizeof(args))
+    return printf("load: Verzeichnisse koennen nicht geladen werden!\n"),1;
+  asynchron(args,#'_make,UPD_LOAD,0,0);
+  return 1;
+}
diff --git a/std/shells/orc.c b/std/shells/orc.c
new file mode 100644
index 0000000..95c73f6
--- /dev/null
+++ b/std/shells/orc.c
@@ -0,0 +1,203 @@
+#pragma strong_types,save_types
+
+inherit "std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <combat.h>
+#include <defines.h>
+#include <defuel.h>
+
+/*
+ * Orks:
+ * Orks sind eigentlich boese und blutruenstig, was auch oft genug zum 
+ * Vorschein tritt :) Wenn ein Ork zu heftig forscht, quengelt er rum 
+ * und weigert sich, bis er nicht wieder ein bisschen Blut verspritzt
+ * hat,
+*/ 
+#define F_MAX 500
+#define F_DEG 3 
+#define NO_EXAMINE ({ \
+    "Du knurrst: Ich will Blut, keine Bluemchen.", \
+    "Du grummelst: Bin ich ein Forscher, oder was?" \
+    })
+
+static int f_cnt, f_deg;
+
+int 
+QueryFCnt() {
+  return f_cnt;
+}
+
+int 
+SetFCnt(int fc) {
+  if(fc > -1 && fc < F_MAX)
+    f_cnt = fc;
+  return f_cnt;
+}
+  
+int
+QueryFDeg() {
+  return f_deg;
+}
+
+void 
+create() {
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  mixed res;
+
+  base::create();
+
+  f_cnt=0;
+  f_deg=F_DEG;
+  
+  SetDefaultHome("/d/vland/morgoth/room/city/rathalle");
+  SetDefaultPrayRoom("/d/vland/morgoth/room/city/c0606");
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_STR:3,A_INT:-1,A_CON:2]));
+  /* Kleine aeh grosse Muskelpakete */
+  SetProp(P_SKILL_ATTRIBUTE_OFFSETS,([SA_DAMAGE:110]));
+  SetProp(P_AVERAGE_SIZE,195);
+  SetProp(P_AVERAGE_WEIGHT,125000); // ziemlich schwer, viele Muskeln
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_DEAD:60,
+                                 MATGROUP_BIO:40, 
+                                 MATGROUP_ELEMENTAL: 20, 
+                                 MAT_BLOOD:100]));
+  SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({"Uruk-Hai"}));
+  SetProp(P_RESISTANCE_STRENGTHS,
+	  ([ 
+     DT_FIRE : -0.2,
+     DT_HOLY :0.3,
+     DT_UNHOLY : -0.2,
+	   DT_ACID : 0.2 ]));
+
+  SetProp(P_MAX_FOOD,110);
+  SetProp(P_MAX_DRINK,110);
+  SetProp(P_MAX_ALCOHOL,150);
+  SetProp(P_DEFUEL_LIMIT_FOOD,50);
+  SetProp(P_DEFUEL_LIMIT_DRINK,70);
+  SetProp(P_DEFUEL_TIME_FOOD,300);
+  SetProp(P_DEFUEL_TIME_DRINK,400);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.4);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.35);
+
+  /* SP regenerieren sie nich ganz so schnell, dafuer sind sie
+   * ein bischen schneller bei Gift und Futter */
+  SetProp(P_SP_DELAY,HEAL_DELAY+1);
+  SetProp(P_POISON_DELAY,POISON_DELAY-1);
+  SetProp(P_FOOD_DELAY,FOOD_DELAY-1);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+          ([ MT_ANGRIFF : 500,
+        	   MT_ILLUSION : -250,
+             MT_BEHERRSCHUNG : -250,
+        	   MT_VERWANDLUNG : 500 ]));
+
+
+  if(!(res=QueryProp(P_HANDS)) || !pointerp(res) || (sizeof(res)<3))
+    res=({" mit starken Haenden",35,({DT_BLUDGEON, DT_RIP}) });
+  SetProp(P_HANDS,res);
+  /* Orks haben dicke Haut */
+  SetProp(P_BODY,20);
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)){
+    SetProp(P_SIZE,180+random(31));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,100000+random(25001)+random(25001));
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+}
+
+string 
+_query_race() {
+  return "Ork";
+}
+
+string 
+_query_real_race() {
+  return "Ork";
+}
+
+string 
+_query_racedescr() {
+  return break_string("Ein Ork. Die brutale Macht des Boesen. Ein "
+      "erbitterter Kaempfer, ohne Furcht vor dem Tod. So stellst Du "
+      "Dir einen Ork vor.\nDiese Orks sehen vielleicht ausserlich so "
+      "aus: Eine dicke und dunkle, lederartige Haut, die sicher "
+      "einiges an Schlaegen abhaelt. Lange Eckzaehne, die sicher boese "
+      "Wunden reissen koennen.\nDu spuerst, dass diese Orks hier ein "
+      "wenig anders sind. Ihren Drang nach dem Blut ihrer Feinde scheinen "
+      "sie recht gut unter Kontrolle zu haben, die meiste Zeit zumindest. "
+      "Sie sind in der Lage, friedlich zwischen den anderen Rassen zu "
+      "wandeln. Dennoch sind vor allem die Uruk-Hai Orks als Gegner nicht "
+      "zu unterschaetzen, sollte es doch einmal zu einem Kampf kommen.",
+      78,0,BS_LEAVE_MY_LFS);
+}
+
+int 
+QueryAllowSelect() { 
+  return 1; 
+}
+
+string 
+*_query_racestring() {
+  if (QueryProp(P_GENDER)==FEMALE)
+    return ({"Orkin","Orkin","Orkin","Orkin"});
+  return ({"Ork","Orkes","Ork","Ork"});
+}
+
+string 
+_query_default_guild(){
+  return "urukhai";
+}
+
+mixed 
+RaceDefault(string arg) {
+  if (!arg)
+    return 0;
+  switch(arg) {
+    case P_HANDS :
+      return ({" mit starken Haenden",35,({DT_BLUDGEON,DT_RIP}) });
+    case P_BODY :
+      return 25;
+  }
+  return base::RaceDefault(arg);
+}
+
+protected void heart_beat() {
+  ::heart_beat();
+
+  if(f_cnt > 0 && !--f_deg) {
+    f_cnt--;
+    f_deg=F_DEG;
+  }
+}
+
+void
+Attack(object enemy) {
+  if(f_cnt > 0)
+    f_cnt--;
+  return ::Attack(enemy);
+}
+
+varargs int 
+_examine(string str, int mode) {
+
+  if(++f_cnt > F_MAX) {
+    tell_object(this_object(),break_string(NO_EXAMINE[random(sizeof(NO_EXAMINE))],78));
+    return 1;
+  }
+  return ::_examine(str,mode);
+}
diff --git a/std/shells/testie.c b/std/shells/testie.c
new file mode 100644
index 0000000..18c42af
--- /dev/null
+++ b/std/shells/testie.c
@@ -0,0 +1,6 @@
+#pragma strong_types,save_types
+
+inherit "/std/shells/human";
+
+int QueryAllowSelect() { return 0; }
+
diff --git a/std/shop.c b/std/shop.c
new file mode 100644
index 0000000..d4cf690
--- /dev/null
+++ b/std/shop.c
@@ -0,0 +1,38 @@
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/room";
+inherit "/std/room/shop";
+
+#include <properties.h>
+
+protected void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+  room::create();
+  shop::create();
+
+  SetProp(P_LIGHT, 1);
+  SetProp(P_INDOORS,1);
+  SetProp(P_INT_SHORT, "StdLaden");
+  SetProp(P_INT_LONG,
+   "Der Magier, der diesen Laden erschuf, fand keine Beschreibung dafuer.\n");
+  AddItem("/obj/entsorg",REFRESH_REMOVE);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset()
+{
+  room::reset();
+  shop::reset();
+}
diff --git a/std/spellbook.c b/std/spellbook.c
new file mode 100644
index 0000000..24f824f
--- /dev/null
+++ b/std/spellbook.c
@@ -0,0 +1,785 @@
+// MorgenGrauen MUDlib
+//
+// spellbook.c -- Grundlegende Funktionen fuer Zaubersprueche
+//
+// $Id: spellbook.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma no_shadow
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+//#define NEED_PROTOTYPES
+inherit "/std/thing";
+inherit "/std/restriction_checker";
+inherit "/std/player/pklog";
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <new_skills.h>
+#include <spellbook.h>
+#define ME this_object()
+
+void create() {
+  seteuid(getuid());
+  // diese Prop sollte von aussen nicht direkt geaendert werden koennen.
+  Set(P_SB_SPELLS,([]),F_VALUE);
+  Set(P_SB_SPELLS,PROTECTED,F_MODE_AS);
+
+  ::create();
+}
+
+mapping _query_sb_spells() {
+    // Kopie liefern, da sonst versehentlich schnell eine Aenderung des
+    // originalen Mappings hier passieren kann.
+    return(deep_copy(Query(P_SB_SPELLS, F_VALUE)));
+}
+
+mapping QuerySpell(string spell) {
+  mapping spi;
+
+  //Query-Methode umgehen, damit nur ein Spellmapping kopiert werden muss und
+  //nicht das ganzen Mapping mit allen Spells.
+  if (!spell
+      || !(spi=Query(P_SB_SPELLS))
+      || !(spi=spi[spell]))
+    return 0;
+  return deep_copy(spi);
+}
+
+varargs int
+AddSpell(string verb, int kosten, mixed ski) {
+  int level;
+  mapping spells;
+
+  if (!verb || kosten<0)
+    return 0;
+  
+  verb=lower_case(verb);
+  level=(intp(ski))?ski:0;
+
+  if (!mappingp(ski)) ski=([]);
+  // Zur Sicherheit eine Kopie machen, wer weiss, was da uebergeben wird, bzw.
+  // was der Setzende mit ski hinterher noch macht.
+  else ski=deep_copy(ski);
+
+  ski=AddSkillMappings(QueryProp(P_GLOBAL_SKILLPROPS),ski);
+  ski+=([SI_SPELLCOST:kosten]);
+  // SI_SPELL-Submapping hinzufuegen, wenn nicht da.
+  if (!member(ski,SI_SPELL))
+      ski+=([SI_SPELL:([]) ]);
+  // ski[SI_SPELL][SP_NAME] ergaenzen, ggf. aus ski verschieben
+  if (!stringp(ski[SI_SPELL][SP_NAME]) && stringp(ski[SP_NAME])) {
+      ski[SI_SPELL][SP_NAME] = ski[SP_NAME];
+      m_delete(ski, SP_NAME);
+  }
+  else if (!stringp(ski[SI_SPELL][SP_NAME])) {
+      ski[SI_SPELL][SP_NAME] = verb;
+  }
+  if (stringp(ski[SP_NAME])) {
+      m_delete(ski, SP_NAME);
+  }
+
+  if (level)
+    ski=AddSkillMappings(ski,([SI_SKILLRESTR_LEARN:([P_LEVEL:level])]));
+  
+  // Abfrage per Query(), um das Mapping als Referenz, nicht als Kopie zu
+  // bekommen, das spart etwas Zeit.
+  if (!(spells=Query(P_SB_SPELLS,F_VALUE))) {
+    spells=([]);
+    SetProp(P_SB_SPELLS,spells);
+  }
+  // Spell setzen...
+  spells[verb]=ski;
+  // Set() nicht noetig, da Query() an spell das Originalmapping geliefert hat
+  // und dieses bereits jetzt geaendert wurde. ;-)
+  //Set(P_SB_SPELLS,spells+([verb:ski]),F_VALUE);
+
+  // hat wohl geklappt...
+  return(1);
+}
+
+int
+TryAttackSpell(object victim, int damage, mixed dtypes,
+               mixed is_spell, object caster, mapping sinfo) {
+  mixed no_attack;
+  int nomag;
+
+  if (no_attack = (mixed)victim->QueryProp(P_NO_ATTACK)) {
+    if (stringp(no_attack))
+      write(no_attack);
+    else
+      write(victim->Name(WER,1)+" laesst sich nicht angreifen!\n");
+    return 0;
+  }
+  if (victim->QueryProp(P_GHOST)) {
+    write(victim->Name(WER,1)+" ist ein Geist!\n");
+    return 0;
+  }
+
+  damage=(damage*(int)caster->QuerySkillAttribute(SA_DAMAGE))/100;
+
+  // ggf. Loggen von PK-Versuchen und ggf. ausserdem Angriff abbrechen.
+  // BTW: Aufruf von InsertEnemy() gibt 0 zurueck, wenn der Gegner nicht
+  // eingetragen wurde. Allerdings tut es das auch, wenn der Gegner schon
+  // Feind war. Daher muss noch geprueft werden, ob nach dem InsertEnemy() die
+  // beiden Spieler Feinde sind. Wenn nicht, wird der Spell auch abgebrochen
+  // und eine Meldung ausgegeben.
+  if (query_once_interactive(caster) && query_once_interactive(victim)
+      && CheckPlayerAttack(caster, victim, 
+        sprintf("Spellbook: %O, Spell: %O\n",
+                object_name(this_object()),
+                ((mappingp(sinfo) && is_spell) ? sinfo[SP_NAME]  : "")))
+      && !caster->IsEnemy(victim)
+      && !caster->InsertEnemy(victim)
+      )
+  {
+    tell_object(ME, "Ein goettlicher Einfluss schuetzt Deinen Gegner.\n");
+    return 0;
+  }
+
+  nomag=(int)victim->QueryProp(P_NOMAGIC);
+  if ((sinfo[SI_NOMAGIC] < nomag) &&
+      nomag*100 > random(100)*(int)caster->QuerySkillAttribute(SA_ENEMY_SAVE)) {
+    printf("%s wehrt Deinen Zauber ab.\n", capitalize((string)victim->name(WER, 1)));
+    return 0;
+  }
+  else {
+    return (int)victim->Defend(damage, dtypes, is_spell, caster);
+  }
+}
+
+varargs int
+TryDefaultAttackSpell(object victim, object caster, mapping sinfo, 
+                      mixed si_spell) {
+  object team;
+  int row;
+  
+  if (!si_spell) si_spell=sinfo[SI_SPELL];
+  // Wenn der Spieler in einem Team ist, die Teamreihen-Boni
+  // beruecksichtigen. Wenn nicht, eben nicht.
+  if (!team=((object)caster->QueryProp(P_TEAM)))
+  return TryAttackSpell(victim,
+                        GetRandFValueO(SI_SKILLDAMAGE,sinfo,caster),
+                        GetData(SI_SKILLDAMAGE_TYPE,sinfo,caster),
+                        si_spell,
+                        caster,
+                        sinfo);
+  else
+  {
+	  row=(int)caster->PresentPosition();
+	  return TryAttackSpell(victim,
+		  GetRandFValueO(SI_SKILLDAMAGE,sinfo,caster)+
+		  // Nur wenn SI_SKILLDAMAGE_BY_ROW ein mapping ist, 
+		  // randomisiert addieren
+		  (mappingp(sinfo[SI_SKILLDAMAGE_BY_ROW])?
+		   random(sinfo[SI_SKILLDAMAGE_BY_ROW][row]):0)+
+		  // Nur wenn das OFFSET davon ein mapping ist,
+		  // nicht randomisiert addieren.
+		  (mappingp(sinfo[OFFSET(SI_SKILLDAMAGE_BY_ROW)])?
+		   sinfo[OFFSET(SI_SKILLDAMAGE_BY_ROW)][row] : 0),
+		   GetData(SI_SKILLDAMAGE_TYPE,sinfo,caster),
+		   si_spell,caster,sinfo);
+   }
+}
+
+#define SMUL(x,y) ((x<0 && y<0)?(-1*x*y):(x*y))
+int
+SpellSuccess(object caster, mapping sinfo) {
+  int cval,abil,res;
+
+  abil=sinfo[SI_SKILLABILITY];
+  cval=(int)caster->UseSkill(SK_CASTING);
+  res=abil + (SMUL(abil,cval)) / MAX_ABILITY - random(MAX_ABILITY);
+  if (cval && res>MAX_ABILITY) // besonders gut gelungen?
+    caster->LearnSkill(SK_CASTING,1+(res-MAX_ABILITY)/2000);
+  return res;
+}
+
+int
+CanTrySpell(object caster, mapping sinfo) {
+  mapping rmap;
+  string res;
+
+  if (caster->QueryProp(P_GHOST)) {
+    write("Als Geist kannst Du nicht zaubern.\n");
+    return 0;
+  }
+  if ((rmap=sinfo[SI_SKILLRESTR_USE])
+      && (res=check_restrictions(caster,rmap))) {
+    write(res);
+    return 0;
+  }
+  return 1;
+}
+
+void
+Learn(object caster, string spell, mapping sinfo) {
+  int val,diff,attval;
+  mapping attr;
+ 
+  // gar nicht lernen?
+  if (sinfo[SI_NO_LEARN])
+    return;
+
+ // Attribut sollte int sein, wenn nicht anders angegeben
+ if (mappingp(sinfo[SI_LEARN_ATTRIBUTE]))
+ {
+	attr=sinfo[SI_LEARN_ATTRIBUTE];
+	// Direkt in einem durch berechnen.
+	// Ich gehe davon aus, dass wir nie nie nie
+	// ein weiteres Attribut ins MG einfuegen.
+	// Wir berechnen den Mittelwert
+	attval=((int)caster->QueryAttribute(A_INT)*attr[A_INT]+
+		(int)caster->QueryAttribute(A_DEX)*attr[A_DEX]+
+		(int)caster->QueryAttribute(A_STR)*attr[A_STR]+
+		(int)caster->QueryAttribute(A_CON)*attr[A_CON])
+		/(attr[A_CON]+attr[A_INT]+attr[A_STR]+attr[A_DEX]);
+		
+	 
+ } else {
+	 attval=(int)caster->QueryAttribute(A_INT);
+ }
+
+  
+  val=((attval/2)*GetFValue(SI_SKILLLEARN,sinfo,caster)+
+       GetOffset(SI_SKILLLEARN,sinfo,caster));
+  if (!(diff=GetFValueO(SI_DIFFICULTY,sinfo,caster)))
+    diff=GetFValueO(SI_SPELLCOST,sinfo,caster);
+  caster->LearnSkill(spell,val,diff);
+}
+
+void
+Erfolg(object caster, string spell, mapping sinfo) {
+  object env;
+  if(env=environment(caster))
+     env->SpellInform(caster,spell,sinfo);
+}
+
+void
+Misserfolg(object caster, string spell, mapping sinfo) {
+  write("Der Zauberspruch ist missglueckt.\n");
+  write("Du lernst aus Deinem Fehler.\n");
+  Learn(caster,spell,sinfo);
+}
+
+string
+SelectSpell(string spell, mapping sinfo) {
+  if (sinfo && sinfo[SI_SKILLFUNC])
+    return sinfo[SI_SKILLFUNC];
+  return spell;
+}
+
+varargs void
+prepare_spell(object caster, string spell, mapping sinfo) {
+  string txt;
+
+  if (!caster || !spell) return ;
+  if (!mappingp(sinfo) || !stringp(txt=sinfo[SI_PREPARE_MSG]))
+    txt="Du bereitest Dich auf den Spruch ``%s'' vor.\n";
+  tell_object(caster,sprintf(txt,spell));
+}
+
+varargs int
+UseSpell(object caster, string spell, mapping sinfo) {
+  mapping ski,tmp;
+  string spellbook,sname,fname,txt;
+  int res,fat,cost;
+  mixed ps;
+
+  if (!caster || !spell)
+    return 0;
+  // Spell kann in der Gilde anderen Namen haben
+  sname=SelectSpell(spell,sinfo);
+  if (!(ski=QuerySpell(sname))) // Existiert dieser Spell?
+    return 0;
+  // Gildeneigenschaften sollen Spelleigenschaften ueberschreiben koennen
+  ski=AddSkillMappings(ski,sinfo);
+  // Fuer verschiedene Rassen unterschiedliche Eigenschaften
+  ski=race_modifier(caster,ski);
+
+  // printf("Spellinfo: %O\n",ski);
+
+  if (!CanTrySpell(caster, ski))
+    return 1;
+
+  if (caster->QueryProp(P_SP) < (cost=GetFValueO(SI_SPELLCOST,ski,caster))) {
+    if(txt=ski[SI_SP_LOW_MSG])write(txt);
+    else write("Du hast zu wenig Zauberpunkte fuer diesen Spruch.\n");
+    return 1;
+  }
+  // printf("cost: %d\n",cost);
+
+  if (mappingp(ski[SI_X_SPELLFATIGUE])) {
+    // fuer jeden Key die Spellfatigue pruefen, wenn das Mapping hinterher
+    // nicht leer ist, ist man zu erschoepft.
+    tmp = filter(ski[SI_X_SPELLFATIGUE],
+        function int (string key, int val)
+        { return (int)caster->CheckSpellFatigue(key); } );
+    if (sizeof(tmp)) {
+        write(ski[SI_TIME_MSG] ||
+            "Du bist noch zu erschoepft von Deinem letzten Spruch.\n");
+      return 1;
+    }
+  }
+  else {
+    if (caster->CheckSpellFatigue()) {
+        write(ski[SI_TIME_MSG] ||
+            "Du bist noch zu erschoepft von Deinem letzten Spruch.\n");
+      return 1;
+    }
+  }
+
+  if (!(ski[SI_NO_ATTACK_BUSY]&NO_ATTACK_BUSY_QUERY) &&
+      caster->QueryProp(P_ATTACK_BUSY)) {
+    if (txt=ski[SI_ATTACK_BUSY_MSG]) write(txt);
+    else write("Du bist schon zu sehr beschaeftigt.\n");
+    return 1;
+  }
+
+  // Spruchvorbereitung
+  if (pointerp(ps=(mixed)caster->QueryProp(P_PREPARED_SPELL)) // Ausstehender Spruch
+      && sizeof(ps)>=3 && intp(ps[0] && stringp(ps[1]))) {
+    if (ps[1]==spell) { // Dieser Spruch wird noch vorbereitet
+      if (time()<ps[0]) {
+        if (!stringp(txt=ski[SI_PREPARE_BUSY_MSG]))
+          txt="Du bist noch mit der Spruchvorbereitung beschaeftigt.\n";
+        write(txt);
+        return 1;
+      }
+    }
+    else { // Andere Sprueche brechen die Vorbereitung ab
+      if (!mappingp(tmp=QuerySpell(ps[1])) || // richtige Meldung holen...
+          !stringp(txt=tmp[SI_PREPARE_ABORT_MSG]))
+        txt="Du brichst die Spruchvorbereitung fuer `%s' ab.\n";
+      printf(txt,ps[1]);
+      if (fat=GetValue(SI_PREPARE_TIME,ski,caster)) {
+        // Spruch braucht vorbereitungszeit
+        caster->SetProp(P_PREPARED_SPELL,({time()+fat,spell,ski[SI_SKILLARG]}));
+        prepare_spell(caster,spell,ski);
+        return 1;
+      }
+    }
+  }
+  else {
+    if (fat=GetValue(SI_PREPARE_TIME,ski,caster)) {
+      // Spruch braucht vorbereitungszeit
+      caster->SetProp(P_PREPARED_SPELL,({time()+fat,spell,ski[SI_SKILLARG]}));
+      prepare_spell(caster,spell,ski);
+      return 1;
+    }
+  }
+  if (ps)
+    caster->SetProp(P_PREPARED_SPELL,0);
+  
+  // Funktion kann anderen Namen haben als Spell
+  if (!(fname=sinfo[SI_SKILLFUNC]))
+    fname=sname;
+
+  if((ski[SI_NOMAGIC] < environment(caster)->QueryProp(P_NOMAGIC)) &&
+      random(100) < environment(caster)->QueryProp(P_NOMAGIC)) {
+    if (txt=ski[SI_NOMAGIC_MSG])
+      write(txt);
+    else
+      write("Dein Zauberspruch verpufft im Nichts.\n");
+    res=ABGEWEHRT;
+  }
+  else {
+    // Spruch ausfuehren.
+    res=(int)call_other(this_object(),fname,caster,ski);
+  }
+  // TODO: Wenn die ausgefuehrte Spellfunktion eine 0 zurueckgibt, sollen jetzt
+  // noch notify_fails zum Zuge kommen koennen. Daher in diesem Fall auch 0
+  // zurueckgeben.
+  if (!res || !caster)
+    return 1;
+
+  if (!(ski[SI_NO_ATTACK_BUSY]&NO_ATTACK_BUSY_QUERY))
+  	{
+	if (!ski[SI_ATTACK_BUSY_AMOUNT])  
+    		caster->SetProp(P_ATTACK_BUSY,1);
+	else
+		caster->SetProp(P_ATTACK_BUSY,ski[SI_ATTACK_BUSY_AMOUNT]);
+  	}
+
+  caster->restore_spell_points(-1*cost);
+
+  if (mappingp(ski[SI_X_SPELLFATIGUE])) {
+    // fuer jeden Key die Spellfatigue setzen. Keys mit Dauer 0 loesen keine
+    // Spellfatigue aus.
+    filter(ski[SI_X_SPELLFATIGUE],
+        function int (string key, int val)
+        { return (int)caster->SetSpellFatigue(val, key); } );
+  }
+  else {
+    if ((fat=GetFValueO(SI_SPELLFATIGUE,ski,caster))<0)
+      fat=1;
+    caster->SetSpellFatigue(fat);
+  }
+
+
+  if (res==ERFOLG)
+    Erfolg(caster,spell,ski);
+  else
+  if (res==MISSERFOLG)
+    Misserfolg(caster,spell,ski);
+  return 1;
+}
+
+object *
+FindGroup(object pl, int who) {
+  object *res,ob,env,team;
+  int p1,p2;
+  closure qp;
+
+  res=({});
+  if (!pl || !(env=environment(pl))) return res;
+  p1 = query_once_interactive(pl) ? 1 : -1;
+  team=(object)pl->QueryProp(P_TEAM);
+  for (ob=first_inventory(env);ob;ob=next_inventory(ob)) {
+    if (!living(ob)) continue;
+    qp=symbol_function("QueryProp",ob);
+    if (pl->IsEnemy(ob)) // Feinde sind immer Gegner
+      p2=-1*p1;
+    else if (objectp(team) && funcall(qp,P_TEAM)==team)
+      p2=p1; // Teammitglieder sind immer auf Seite des Spielers
+    else
+      p2 = (query_once_interactive(ob)||funcall(qp,P_FRIEND)) ? 1 : -1;
+    if (p2>0 && !interactive(ob) && query_once_interactive(ob))
+      continue; // keine Netztoten.
+    if (funcall(qp,P_GHOST))
+      continue;
+    if ( who<0 && (funcall(qp,P_NO_ATTACK) || funcall(qp,P_NO_GLOBAL_ATTACK)) )
+      continue;
+    if (IS_LEARNING(ob) &&
+        (funcall(qp,P_INVIS) || (who<0 && !pl->IsEnemy(ob))))
+      continue;
+    if (p1*p2*who >=0)
+      res+=({ob});
+  }
+  return res;
+}
+
+object *
+FindGroupN(object pl, int who, int n) {
+  if (!pl) return ({});
+  n=(n*(int)pl->QuerySkillAttribute(SA_EXTENSION))/100;
+  if (n<1) n=1;
+  return FindGroup(pl,who)[0..(n-1)];
+}
+
+object *
+FindGroupP(object pl, int who, int pr) {
+  object *res,*nres;
+  int i;
+
+  nres=({});
+  if (!pl) return nres;
+  pr=(pr*(int)pl->QuerySkillAttribute(SA_EXTENSION))/100;
+  if (pr<0) return nres;
+  res=FindGroup(pl,who);
+  for (i=sizeof(res)-1;i>=0;i--)
+    if (pr>=random(100))
+      nres+=({res[i]});
+  return nres;
+}
+
+// Findet feindliche und freundliche GRUPPEN im angegebenen Bereich.
+varargs mixed
+FindDistantGroups(object pl, int dist, int dy, int dx) {
+  mapping pos;
+  object ob,enteam,myteam;
+  int p1,min,max,i;
+  mixed *b_rows,x;
+  closure is_enemy, qp;
+
+  if (!objectp(pl) || !environment(pl))
+    return ({({}),({})});
+  if (!dy) dy=100;
+  if (!dx) dx=MAX_TEAM_ROWLEN*100;
+  x=(int)pl->QuerySkillAttribute(SA_EXTENSION);
+  dx=(dx*x)/100;dy=(dy*x)/100;
+  dist=(dist*(int)pl->QuerySkillAttribute(SA_RANGE))/100;
+  min=dist-dy/2;
+  max=dist+dy/2;
+
+  pos=([]);
+  p1=query_once_interactive(pl) ? 1 : -1;
+  is_enemy=symbol_function("IsEnemy",pl); // zur Beschleunigung
+  myteam=(object)pl->QueryProp(P_TEAM);
+  for (ob=first_inventory(environment(pl));ob;ob=next_inventory(ob)) {
+    if (!living(ob)) continue;
+    qp=symbol_function("QueryProp",ob); // zur Beschleunigung
+
+    // Zuerst mal die Position feststellen:
+    if (!objectp(enteam=funcall(qp,P_TEAM)))
+      pos[ob]=1;
+    else if (!pos[ob] && mappingp(x=(mapping)ob->PresentTeamPositions()))
+      pos+=x;
+    // PresentTeamPositions wird nur einmal pro Team ausgerechnet, weil
+    // anschliessend jedes anwesende Teammitglied pos[ob]!=0 hat.
+
+    pos[ob]=(2*pos[ob])-1;
+    // Reihen sollen Abstand 2 haben, auch Reihen 1 und -1 (nach Umrechnung)
+
+    // Feindliche Reihen an Y-Achse spiegeln, also gegenueber hinstellen:
+    if (funcall(is_enemy,ob))
+      pos[ob]*=-1; // Ist auf jeden Fall Feind
+    else if (objectp(myteam) && myteam==enteam)
+        ;            // Teammitglieder sind immer auf eigener Seite
+    else
+      pos[ob]*=(p1*((int)(query_once_interactive(ob)||
+                          funcall(qp,P_FRIEND))?1:-1));
+
+    // Den Spieler auf keinen Fall entfernen
+    if (ob==pl)
+      continue;
+    // Netztote, Geister  und unsichtbare Magier nicht beruecksichtigen,
+    // nicht (global) angreifbare Monster und nicht-feindliche Magier
+    // von feindlicher Seite entfernen.
+    if ((!interactive(ob) && query_once_interactive(ob)) // Netztote raus
+        || funcall(qp,P_GHOST)
+        || (IS_LEARNING(ob) && funcall(qp,P_INVIS))   // Bin nicht da :-)
+        || (pos[ob]<0 && (funcall(qp,P_NO_GLOBAL_ATTACK) // Nicht angreifen
+                          || funcall(qp,P_NO_ATTACK)
+                          || (IS_LEARNING(ob) && !funcall(is_enemy,ob)))))
+      m_delete(pos,ob);
+  }
+
+  // Reihen aufstellen
+  // Kampfreihe: |   5     4     3     2     1 ||  1     2     3     4     5|
+  // Arrays:     |0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19|
+  //             |<----------- Freunde ------->||<--------- Feinde -------->|
+  b_rows=EMPTY_TEAMARRAY+EMPTY_TEAMARRAY+EMPTY_TEAMARRAY+EMPTY_TEAMARRAY;
+  x=m_indices(pos);
+  for (i=sizeof(x)-1;i>=0;i--) {
+    p1=2*MAX_TEAMROWS-pos[ob=x[i]];
+    pos[ob]=p1;
+    if (p1>=0 && p1<4*MAX_TEAMROWS) {
+      if (random(100)<50)             // Damit gut gemischt wird...
+        b_rows[p1]=b_rows[p1]+({ob}); // zufaellig hinten anfuegen
+      else
+        b_rows[p1]=({ob})+b_rows[p1]; // oder vorne anfuegen.
+    }
+  }
+
+  // Umrechnung von Min, Max auf Reihen
+  // ... -2: -124..-75, -1:-74..-25, 0:-24..24, 1:25..74 2:75..124 ...
+  // Das muss man machen, wenn man keine vernuenftige Rundungsfunktion hat:
+  if (min<0) min=-1*((25-min)/50); else min=(25+min)/50;
+  if (max<0) max=-1*((25-max)/50); else max=(25+max)/50;
+  // Relativ zur Position des Spielers verschieben:
+  p1=pos[pl];min+=p1;max+=p1;
+  // Umrechnung von Breite auf Anzahl:
+  dx=((dx+50)/100)-1;if (dx<0) dx=0;
+
+  x=({({}),({})});
+  for (i=0;i<2*MAX_TEAMROWS;i++)
+    if (i>=min && i<=max)
+      x[1]+=b_rows[i][0..dx]; // Freunde
+  for (i=2*MAX_TEAMROWS+1;i<4*MAX_TEAMROWS;i++)
+    if (i>=min && i<=max)
+      x[0]+=b_rows[i][0..dx]; // Feinde
+
+  return x;
+}
+
+// Findet feindliche oder freundliche Gruppe (oder Summe beider) im
+// angegebenen Bereich.
+varargs object *
+FindDistantGroup(object pl, int who, int dist, int dy, int dx) {
+  mixed *x;
+
+  x=FindDistantGroups(pl,dist,dy,dx);
+  if (who<0)
+    return x[0];
+  else if (who>0)
+    return x[1];
+  return x[0]+x[1];
+}
+
+
+static varargs object
+find_victim(string wen, object pl) {
+  object victim;
+
+  if (!pl) return 0;
+  if (!sizeof(wen)) {
+    if (victim = (object)pl->SelectEnemy())
+      return victim;
+    else
+      return 0;
+  }
+  if (victim = present(wen, environment(pl)))
+    return victim;
+  else if (victim = present(wen, pl))
+    return victim;
+  else
+    return 0;
+}
+varargs object
+FindVictim(string wen, object pl, string msg) {
+  object vic;
+
+  if (!(vic=find_victim(wen,pl)) && msg)
+    write(msg);
+  return vic;
+}
+varargs object
+FindLivingVictim(string wen, object pl, string msg) {
+  object vic;
+
+  if (!(vic=FindVictim(wen,pl,msg)))
+    return 0;
+  if (!living(vic) || vic->QueryProp(P_GHOST)) {
+    printf("%s lebt doch nicht!\n", capitalize((string)vic->name()));
+    return 0;
+  }
+  return vic;
+}
+
+private varargs object
+DoFindEnemyVictim(string wen, object pl, string msg,
+                  mixed func, int min, int max) {
+  object vic;
+  mixed no_attack;
+  int row;
+
+  if (!(vic=FindLivingVictim(wen,pl,msg))) {
+    if ((stringp(wen) && wen!="") || !objectp(pl))
+      return 0;
+    if (pointerp(func)) { // Soll einer DIESER Gegner genommen werden?
+      if (!(vic=(object)pl->SelectEnemy(func))) // Dann daraus auswaehlen
+        return 0;
+    } else {
+      if (!stringp(func))
+        func="SelectEnemy";
+      if (!(vic=(object)call_other(pl,func,0,min,max)))
+        return 0;
+    }
+    func=0; // kein zweites Mal pruefen.
+  }
+  if (no_attack = (mixed)vic->QueryProp(P_NO_ATTACK)) {
+    if (stringp(no_attack))
+      write(no_attack);
+    else
+      write(vic->Name(WER,1)+" laesst sich nicht angreifen.\n");
+    return 0;
+  }
+  if (vic==pl) {
+    write("Du koenntest Dir dabei wehtun.\n");
+    return 0;
+  }
+  if (stringp(func)) {
+    switch(func) {
+    case "SelectNearEnemy":
+      if (pl->PresentPosition()>1) {
+        write("Du stehst nicht in der ersten Kampfreihe.\n");
+        return 0;
+      }
+      if (vic->PresentPosition()>1) {
+        write(vic->Name(WER,1)+" ist in einer hinteren Kampfreihe.\n");
+        return 0;
+      }
+      break;
+    case "SelectFarEnemy":
+      if (row=(int)vic->PresentPosition())
+        row--;
+      if (row>=min && row<=max)
+        break;
+      if (row<min)
+        write(vic->Name(WER,1)+" ist zu nahe.\n");
+      else if (row>max)
+        write(vic->Name(WER,1)+" ist zu weit weg.\n");
+      else
+        write(vic->Name(WER,1)+" ist unerreichbar.\n");
+      return 0;
+    default:;
+    }
+  } else if (pointerp(func)) {
+    if (member(func,vic)<0) {
+      write(vic->Name(WER,1)+" ist unerreichbar.\n");
+      return 0;
+    }
+  }
+
+  if (!pl->IsEnemy(vic)) // War es bisher kein Feind?
+    pl->Kill(vic);       // Dann ist es jetzt einer.
+  return vic;
+}
+
+varargs object
+FindEnemyVictim(string wen, object pl, string msg) {
+  return DoFindEnemyVictim(wen,pl,msg,"SelectEnemy");
+}
+
+// Wie FindEnemyVictim, aber nur im Nahkampf erreichbare Feinde werden
+// gefunden.
+varargs object
+FindNearEnemyVictim(string wen, object pl, string msg) {
+  return DoFindEnemyVictim(wen,pl,msg,"SelectNearEnemy");
+}
+
+// Wie FindEnemyVictim, aber nur Feinde im Bereich der angegebenen Reihen
+// (min,max) werden gefunden.
+varargs object
+FindFarEnemyVictim(string wen, object pl, string msg,
+                   int min, int max) {
+  return DoFindEnemyVictim(wen,pl,msg,"SelectFarEnemy",min,max);
+}
+
+// Wie FindEnemyVictim, findet aber nur Feinde in
+// FindDistantGroup(GEGNER,entfernung,abweichung)
+varargs object
+FindDistantEnemyVictim(string wen, object pl, string msg,
+                       int dist, int dy) {
+  return DoFindEnemyVictim(wen,pl,msg,
+                           FindDistantGroup(pl,-1,dist,dy,10000));
+}
+
+varargs int
+TryGlobalAttackSpell(object caster, mapping sinfo, int suc,
+                     int damage, mixed dt, mixed is_spell,
+                     int dist, int depth, int width) {
+  int i;
+  mixed x,coldam;
+  object *obs,ob;
+
+  if (!suc) suc=random(sinfo[SI_SKILLABILITY]);
+  if (!dt) dt=GetData(SI_SKILLDAMAGE_TYPE,sinfo,caster);
+  if (!is_spell) is_spell=GetData(SI_SPELL,sinfo,caster);
+  if (!dist) dist=GetRandFValueO(SI_DISTANCE,sinfo,caster);
+  if (!depth) depth=GetRandFValueO(SI_DEPTH,sinfo,caster);
+  if (!width) width=GetRandFValueO(SI_WIDTH,sinfo,caster);
+
+  if (!depth && width) depth=width;
+  if (!width && depth) width=depth;
+  if (!mappingp(is_spell)) is_spell=([]);
+  is_spell[SP_GLOBAL_ATTACK]=1;
+
+  x=FindDistantGroups(caster,dist,depth,width);
+  sinfo[SI_NUMBER_ENEMIES]=sizeof(x[0]);
+  sinfo[SI_NUMBER_FRIENDS]=sizeof(x[1]);
+
+  obs=x[0];
+  for (i=sizeof(obs)-1;i>=0;i--)
+    if (objectp(ob=obs[i]) && suc>=ob->SpellDefend(caster,sinfo))
+      TryAttackSpell(ob,(damage?random(damage):
+                         GetRandFValueO(SI_SKILLDAMAGE,sinfo,caster)),
+                     dt,is_spell,caster,sinfo);
+
+  if (!intp(coldam=sinfo[SI_COLLATERAL_DAMAGE]) || !coldam)
+    return 1;
+  obs=x[1];
+  for (i=sizeof(obs)-1;i>=0;i--)
+    if (objectp(ob=obs[i]) && suc>=ob->SpellDefend(caster,sinfo))
+      ob->reduce_hit_points(((damage?random(damage):
+                              GetRandFValueO(SI_SKILLDAMAGE,sinfo,caster))
+                             *coldam)/10);
+  // 10 statt 100 ist Absicht, weil reduce_hit_points schon um Faktor
+  // 10 staerker wirkt als Defend.
+
+  return 1;
+}
diff --git a/std/store.c b/std/store.c
new file mode 100644
index 0000000..3511105
--- /dev/null
+++ b/std/store.c
@@ -0,0 +1,166 @@
+// MorgenGrauen MUDlib
+//
+// store.c -- Das Lager eines Ladens
+//
+// $Id: store.c 7100 2009-02-01 09:24:43Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define ANZ 3
+#include <properties.h>
+#include <bank.h>
+#include <defines.h>
+
+inherit "/std/thing/properties";
+inherit "/std/room/items";
+
+private nosave object *all;
+private nosave mapping all2;
+private nosave object shop;
+private nosave int sum;
+private nosave int store_percent_left;
+
+static int _set_store_percent_left()
+{
+  return store_percent_left=ZENTRALBANK->_query_store_percent_left();
+}
+
+public void _register_shop(object ob)
+{
+  shop=ob;
+}
+
+protected void create()
+{
+  seteuid(getuid());
+  properties::create();
+  items::create();
+  _set_store_percent_left();
+  SetProp(P_MIN_STOCK, 20); // immer eine Reserve fuer unsere Anfaenger...
+}
+
+protected void create_super() {
+      set_next_reset(-1);
+}
+
+public int MayAddObject(object ob)
+{   return 1;   }
+
+protected varargs void remove_multiple(int limit, mixed fun) {}
+
+static int _query_store_consume()
+// da 0 = 20 ist, ist kein default initialisieren im create noetig!!!
+{
+  int consum;
+  consum=100-Query(P_STORE_CONSUME);
+  if (consum<0 || consum >=100) // consum = 100, wenn P_STORE_CONSUM = 0
+    return 70;
+  return consum;
+}
+
+static int _set_store_consume(int consum)     /* 1 <= consum <= 100 */
+{
+  if (consum<1 || consum>100) return -1;
+  return (100-(int)Set(P_STORE_CONSUME, consum));
+}
+
+static int _query_min_stock()
+{
+  int stock;
+  stock=Query(P_MIN_STOCK);
+  if (stock<0) return 0;
+  return stock;
+}
+
+private void update_money() {
+  if (sum) {
+    if (!shop)
+      ZENTRALBANK->PayIn(sum);
+    else
+      shop->_add_money(sum);
+  }
+  sum=0;
+}
+
+protected void RemoveObjectFromStore(object ob) {
+  // Alle Funktionen die ausserhalb aufgerufen werden, werden "gecatcht"
+  catch(sum+=ob->QueryProp(P_VALUE)*store_percent_left/100; publish);
+  catch(ob->remove(); publish);
+  if (ob) destruct(ob);  // Objekt auf jeden Fall zerstoeren
+}
+
+protected void aufraeumen() {
+  int i, size;
+  object ob;
+  string element;
+
+  if (!pointerp(all)) return;
+  if (!mappingp(all2)) all2=([]);
+  size=sizeof(all);
+  for (i=(size<=50 ? 0 : size-50); i<size; i++) {
+    if (!objectp(ob=all[i])) continue;
+    if (object_name(ob)[0..2]=="/d/" || object_name(ob)[0..8]=="/players/")
+      element=BLUE_NAME(ob);
+    else
+      catch(element=ob->short()+BLUE_NAME(ob); publish);
+    if (all2[element]++>ANZ)
+      RemoveObjectFromStore(ob);
+  }
+  if (size<=50) {
+    all=0;  // Speicher freigeben...
+    all2=0;
+    update_money();
+  } else {
+    all=all[0..size-51];
+    call_out(#'aufraeumen,2);
+  }
+}
+
+void reset() {
+  int i, to, stock;
+  mixed *itemlist;
+  
+  items::reset();
+  _set_store_percent_left();
+
+  if (!(all=all_inventory()) || !sizeof(all)) {
+    all=0; // Speicher freigeben
+    return;
+  }
+  if (sizeof(itemlist=QueryProp(P_ITEMS))) {
+    itemlist=filter(itemlist, #'[, 0);
+    all-=itemlist;
+    if (!sizeof(all)) {
+      all=0; // Speicher freigeben
+      return;
+    }
+  }
+
+  i=sizeof(all)-1;
+  to=i*QueryProp(P_STORE_CONSUME)/100;
+  if ( to < (stock=QueryProp(P_MIN_STOCK)) )
+    to=stock;
+  else
+    stock=0;
+
+  // Hinterer Teil des Inventories wird zerstoert, also alle aelteren
+  // und somit vermutlich selten gekaufte Objekte
+  for (;i>=to;i--)
+    RemoveObjectFromStore(all[i]);
+  all2=([]);
+  call_out(#'aufraeumen,random(10));
+  update_money();
+}
+
+//dieser Raum wird sich nie per clean_up() zerstoeren, daher geben wir
+//0 zurueck, damit der Driver clean_up() nicht mehr ruft.
+int clean_up(int refcount) { return 0; }
+
+static int _query_current_money()
+{
+  return sum;
+}
+
diff --git a/std/thing.c b/std/thing.c
new file mode 100644
index 0000000..908b6c1
--- /dev/null
+++ b/std/thing.c
@@ -0,0 +1,45 @@
+// MorgenGrauen MUDlib
+//
+// thing.c -- standard object
+//
+// $Id: thing.c 7804 2011-07-10 20:37:52Z Zesstra $                      
+
+#pragma strict_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+//#pragma no_clone
+
+inherit "/std/thing/properties";
+inherit "/std/thing/light";
+inherit "/std/thing/description";
+inherit "/std/thing/moving";
+inherit "/std/thing/language";
+inherit "/std/thing/commands";
+inherit "/std/thing/restrictions";
+inherit "/std/thing/envchk";
+
+protected void create()
+{
+  seteuid(getuid());
+  properties::create();
+  light::create();
+  commands::create();
+  description::create();
+  restrictions::create();
+  envchk::create();
+  AddId("Ding");
+
+  return;
+}
+
+// wird gerufen, wenn implizit per inherit geladen. In diesem Fall wird kein
+// Reset benoetigt, weil das Objekt nicht konfiguriert ist und nur das
+// Programm wichtig ist.
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Damit man in ALLEN Standardobjekten ::reset aufrufen kann.
+void reset() {}
+
diff --git a/std/thing/commands.c b/std/thing/commands.c
new file mode 100644
index 0000000..f745b67
--- /dev/null
+++ b/std/thing/commands.c
@@ -0,0 +1,532 @@
+// MorgenGrauen MUDlib
+//
+// thing/commands.c -- thing description
+//
+// $Id: commands.c 9514 2016-02-23 20:33:09Z Gloinson $
+//
+// Aus Regenbogen MUDLib
+// aus Gueldenland/Wunderland MUDlib (Morgengrauen MUDlib)
+//
+// P_COMMANDS data-structure:
+//
+// AddCmd(verb,fun1,1);
+// AddCmd(verb+syn1a|syn1b&syn2a|syn2b|syn2c,fun2,
+//	   error1_notify|error2_notify^error2_write);
+// -->
+// ([verb:({fun1,fun2});					// funs
+//	  ({1,({error1_notify, error2_write^error2_say, 1})});  // flags
+//        ({0,({({syn1a,syn1b}),({syn2a,syn2b,syn2c})})});	// rules
+//        0])							// IDs
+//
+// Rules: ({<Regelsatz fuer fun1>, ({<1. Synonymgruppe>,
+//				     <2. Synonymgruppe, ...}), ...})
+// Flags: ({<Flag fuer fun1>, ({<Fehlermeldung 1. Synonymgruppe>, ... ,
+//				[, Index fuer write anstatt notify_fail]}),
+//	    ... })
+// IDs:   0 oder ({<ID fuer fun1>}) oder ({0, <ID fuer fun2>}) ...
+//
+// IDEA: save no 0s in rules/flags if possible (as in IDs)
+//       (adressing especially old-style-AddCmd-MUDs)
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <moving.h>
+#include <thing/language.h>
+#include <exploration.h>
+
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+#include <thing/commands.h>
+#undef NEED_PROTOTYPES
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(x) printf("Object %O tmpstr=%s\n", explode(object_name(this_object()),"#")[1], x);
+
+private nosave mapping added_cmds;
+
+protected void create()
+{
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}     
+
+varargs void AddCmd(mixed cmd, mixed func, mixed flag, mixed cmdid) {
+ int i,j;
+ closure cl;
+ mixed *rule;
+
+ // potentielle AddCmd mit Regel?
+ if(stringp(cmd)) {
+  // eine Regel? - aufsplitten
+  if((i=member(cmd,'&'))>0) {
+   // ... in Array mit Verknuepfungselementen
+   rule=explode(cmd[(i+1)..],"&");
+   j=sizeof(rule);
+   // ... in Array mit Arrays mit Alternativelementen:
+   // "p|q&r|s" -> ({ ({"p","q"}), ({"r","s"}} })
+   while(j--)
+    rule[j]=explode(rule[j],"|");
+
+   // Regeln von Kommandoverben abschneiden
+   cmd=cmd[0..(i-1)];
+  }
+  // Kommandoverben extrahieren
+  cmd=explode(cmd,"|");
+
+  // Satz von Regeln existiert: Aufsplitten von Fehlermeldungen
+  if(rule)
+   if(stringp(flag)) {
+    mixed *fail;
+    // in einfaches Array mit jeweiligen Fehlermeldungen
+    fail=explode(flag,"|");
+    j=0;
+    i=sizeof(fail);
+    while(j<i) {
+     // write - Fehlermeldung entdeckt - Position ggf. eintragen
+     if(member(fail[j],'^')>=0 && !intp(fail[<1]))
+      fail+=({j});
+     if(member(fail[j],'@')>=0) {
+      int s;
+      flag=regexplode(fail[j], "@WE[A-SU]*[0-9]");
+      s=sizeof(flag);
+      while((s-=2)>0) {
+       int tmpint;
+       tmpint=flag[s][<1]-'1';
+       if(tmpint<0 || tmpint>j)
+        raise_error(sprintf(
+         "AddCmd: error-message %d contains out-of-bounds @WExx-rule.\n",j+1));
+      }
+     }
+     j++;
+    }
+    // "Was?|Wie das?" -> ({"Was?","Wie das?"})
+    // "Was?|Wie das?^|Womit das?|Worauf das?^@WER1 macht was." ->
+    //  ({"Was?",
+    //    "Wie das?^Womit das?",
+    //	  "Worauf das?^@WER1 macht was.",1})
+    flag=sizeof(fail);
+    if(flag && flag<sizeof(rule))
+     raise_error(
+      "AddCmd: number of error-messages does not match number of rules.\n");
+    flag=fail; // ueberschreiben mit den parsefreundlichen Format
+  } else if(flag)
+   raise_error("AddCmd: rules exist but flags are not an error-string.\n");
+ } // end if(stringp(cmd)) ... kein Regelstring vorhanden
+
+ // kein Kommandoarray gewesen noch erzeugt?
+ if(!pointerp(cmd))
+   raise_error("AddCmd: missing string/pointer-parameter for command.\n");
+
+ // Closure aus einem String erzeugen, wenn moeglich und sicher
+ // (function_exists() filtert unnoetigerweise auch reine "static" funs,
+ //  die genaue Pruefung ueber functionlist() kostet jedoch zuviel)
+ if(stringp(func) &&
+    (!extern_call() || function_exists(func,this_object())) &&
+    closurep(cl=symbol_function(func,this_object())))
+  func=cl;
+
+ // jedes einzelne Verb mit seinen Regeln und Funktionen eintragen
+ i=sizeof(cmd);
+ if(!added_cmds) added_cmds=m_allocate(i,4);
+ while(i--) {
+  string str;
+  str=cmd[i];
+  if(!func)
+   if(extern_call()) func=previous_object();
+   else func=this_object();
+  if(!member(added_cmds,str))
+   added_cmds+=([str:allocate(0);allocate(0);allocate(0);0]);
+  // existierendes Verb ergaenzen
+  added_cmds[str,0]+=({func});
+  added_cmds[str,1]+=({flag});
+  added_cmds[str,2]+=({rule});
+  // ggf. id in das ID-Mapping eintragen
+  if(cmdid) {
+   mixed *tmp;
+   j=sizeof((string*)added_cmds[str,0]);
+   tmp=added_cmds[str,3]||allocate(j);
+   if(sizeof(tmp)<j) tmp+=allocate(j-sizeof(tmp));
+   tmp[<1]=cmdid;
+   added_cmds[str,3]=tmp;
+  }
+ }
+}
+
+// Auswertung fuer ein Verb loeschen
+varargs int RemoveCmd(mixed cmd, int del_norule, mixed onlyid) {
+ int ret;
+
+ // Falls Magier das RemoveCmd falsch nutzen (z.B. analog zu AddCmd)
+ // wird das Regelsystem verwirrt. Da das del_norule nur int sein darf,
+ // gibt es hier eine gute Chance den Fehler abwaertskompatibel zu ent-
+ // decken. Damit Spieler den Fehler nicht mitbekommen, wird hier auf
+ // ein raise_error verzichtet, und statt dessen in ein Logfile ge-
+ // schrieben.
+ if (!intp(del_norule))
+ {
+   log_file("REMOVE_CMD",
+     sprintf("\n-- %s --\nIllegal RemoveCommand() in Object [%O]:\n %O\n",
+       dtime(time()), this_object(), cmd));
+   del_norule=0;
+   onlyid=0;
+ }
+ 
+ if(!added_cmds || (!cmd && !del_norule && !onlyid))
+  added_cmds=(mapping)0; 
+ else {
+  int i, j;
+  mixed *rule, *flag, *fun, *delrule, *ids;
+
+  if(stringp(cmd)) {
+   // Regeln entdeckt - Zerlegen (wie AddCmd)
+   if((i=member(cmd,'&'))>0) {
+    delrule=explode(cmd[(i+1)..],"&");
+    j=sizeof(delrule);
+    while(j--)
+     delrule[j]=explode(delrule[j],"|");
+    cmd=cmd[0..(i-1)];
+   }
+   cmd=explode(cmd,"|");
+  } else if(del_norule || onlyid) cmd=m_indices(added_cmds);
+
+  if(!pointerp(cmd))
+   raise_error("RemoveCmd: missing string/pointer-parameter.\n");
+  i=sizeof(cmd);
+
+  while(i--) {
+   // keine Regeln da und Regeln loeschen erlaubt: alles loeschen
+   if(!delrule && !del_norule && !onlyid) m_delete(added_cmds,cmd[i]);
+   else if(m_contains(&fun, &flag, &rule, &ids, added_cmds, cmd[i])) {
+    j=sizeof(fun);
+    while(j--) {
+     int k;
+     // DBG(rule[j]);
+    	// Regeln nicht löschen und Regel?
+     if(!(del_norule && pointerp(rule[j])) &&
+        // nur bestimmte ID löschen und ID passt nicht?
+        !(onlyid && (!pointerp(ids) || sizeof(ids)<=j || ids[j]!=onlyid)) &&
+        // Löschregel existiert und passt nicht auf Regel?
+        !(delrule && (k=sizeof(rule[j]))!=sizeof(delrule))) {
+      // partielles Testen einer Löschregel ...
+      if(delrule) {
+       while(k--)
+        if(!sizeof(rule[j][k]&delrule[k])) break;
+       if(k>=0) continue;
+      }
+      // alles korrekt: Löschen!
+      // (Arraybereich durch leeres Array loeschen)
+      flag[j..j]  = allocate(0);
+      fun[j..j]   = allocate(0);
+      rule[j..j]  = allocate(0);
+      if(ids) {
+       ids[j..j] = allocate(0);
+       if(!sizeof(ids-allocate(1))) ids=(mixed*)0;
+      }
+      ret++;
+     }
+    } // end while(j--) {
+   }
+   // Funktions/Regelliste update oder ggf. Kommando voellig loeschen
+   if(sizeof(rule)) {
+    added_cmds[cmd[i],0]=fun;
+    added_cmds[cmd[i],1]=flag;
+    added_cmds[cmd[i],2]=rule;
+    added_cmds[cmd[i],3]=ids;
+   } else m_delete(added_cmds,cmd[i]);
+  }
+  if(!sizeof(added_cmds)) added_cmds=(mapping)0;
+ }
+ return ret;
+}
+
+// Ausfuehren samt geparstem Inputstring und getriggerten Parserergebnissen
+static int _execute(mixed fun, string str, mixed *parsed) {
+ if(closurep(fun))
+  return ((int)funcall(fun,str,&parsed));
+ if(stringp(fun))
+  return ((int)call_other(this_object(),fun,str,&parsed));
+ return 0;
+}
+
+#define CHECK_PRESENT     1
+#define CHECK_ID          2
+#define CHECK_PUTGETNONE  4
+#define CHECK_PUTGETDROP  8
+#define CHECK_PUTGETTAKE  16
+#define CHECK_PUTGET      (CHECK_PUTGETNONE|CHECK_PUTGETDROP|CHECK_PUTGETTAKE)
+
+// Wert fuer Fehlschlag, Fallback-Wert der benutzten while-- - Schleifen
+#define NOMATCHFOUND      -1
+
+// Regeln fuer ein (nun unwichtiges) Verb triggern
+static int _process_command(string str, string *noparsestr,
+			     mixed fun, mixed flag, mixed rule) {
+ mixed *parsed, *objmatches;
+
+ // eine Regel ... auswerten ...
+ if(pointerp(rule)) {
+  int nrul;
+  parsed=objmatches=allocate(0);
+  int lastmatchpos=NOMATCHFOUND;
+
+  // Abgleichen der gesplitteten Eingabe mit Regeln:
+  // vorwaerts durch die Synonymgruppen
+  int rs=sizeof(rule);
+  while(nrul<rs) {
+   int matchpos;
+   string *synonym;
+   mixed matchstr;
+
+   matchpos=NOMATCHFOUND;
+   matchstr=0;
+
+   // Synonyme extrahieren
+   int nrsynonyms=sizeof(synonym=rule[nrul]);
+
+   // egal wie durch Synonyme bis Match - Abgleich mit Eingabe
+   while(nrsynonyms--) {
+    int tmppos = member(noparsestr,synonym[nrsynonyms]);
+    // ist Synonym im Eingabestring und kommt spaeter als vorheriges Synonym?
+    if(tmppos>=0 && tmppos>lastmatchpos) {
+     // Erfolg: merken der Position im Eingabestring und den matchenden String
+     matchpos=tmppos;
+     matchstr=noparsestr[tmppos];
+     break;
+    }
+   }
+
+   // kein Match durch Synonyme? Pruefe die @-Spezialvariablen.
+   if(matchpos == NOMATCHFOUND) {
+    int check_present;
+    // ist Abpruefen von ID/PRESENT in der Synonymgruppe verlangt
+    // bei present()/find_obs gleich Voraussetzung gueltiger TP mitprufen
+    if(member(synonym,"@ID")>=0) check_present=CHECK_ID;
+    if(this_player()) {
+     if(member(synonym,"@PRESENT")>=0) check_present|=CHECK_PRESENT;
+     else if(member(synonym,"@PUT_GET_NONE")>=0)
+		check_present|=CHECK_PUTGETNONE;
+     else if(member(synonym,"@PUT_GET_TAKE")>=0)
+		check_present|=CHECK_PUTGETDROP;
+     else if(member(synonym,"@PUT_GET_DROP")>=0)
+		check_present|=CHECK_PUTGETTAKE;
+    }
+
+    if(check_present) {
+     // wir fangen hinter dem letzten Match an
+     int q_start=lastmatchpos+1;
+     int r_end=sizeof(noparsestr)-1;
+
+     int range;
+     while((range=r_end-q_start)>=0) {
+      mixed tmpobj;
+
+      // wie weit wollen wir zurückgehen?
+      if(range)
+        if(!(check_present&CHECK_PUTGET))
+          range=range>2?2:range; // 3 Fragmente fuer @ID/@PRESENT (Adverb/Nr)
+        else if(range>4)
+          range=4;               // 5 Fragmente fuer @PUT_XXX 
+
+      // und jetzt die Substrings pruefen
+      while(range>=0 && !matchstr) {
+       string tmpstr;
+
+       // zu pruefenden String aus den Teilen zusammensetzen
+       if(range) tmpstr=implode(noparsestr[q_start..(q_start+range)]," ");
+       else tmpstr=noparsestr[q_start];
+
+       //DBG(tmpstr);
+       if(check_present&CHECK_PRESENT &&			// PRESENT ?
+          ((tmpobj=present(tmpstr,this_player())) ||
+           (tmpobj=present(tmpstr,environment(this_player())))))
+        matchstr=tmpobj;
+       else if(check_present&CHECK_ID && id(tmpstr))	// ID ?
+        matchstr=this_object();
+       else if((check_present&CHECK_PUTGET) &&	// PUT_GET_??? ?
+               (tmpobj=(object*)
+                  this_player()->find_obs(tmpstr,
+                      ([CHECK_PUTGETNONE:PUT_GET_NONE,
+                        CHECK_PUTGETDROP:PUT_GET_TAKE,
+                        CHECK_PUTGETTAKE:PUT_GET_DROP])
+                                        [CHECK_PUTGET&check_present])) &&
+               sizeof(tmpobj)) {
+        if(sizeof(tmpobj)==1) matchstr=tmpobj[0];
+        else {	// Arrays werden zwischengespeichert ...
+         objmatches+=({sizeof(parsed),tmpobj});
+         matchstr=tmpstr;
+        }
+       } else { // dieser Substring hat nicht gematcht
+        // ab weniger als 3 Teilen ist das Nichtmatching eines Substrings mit
+        // beendendem Numeral Kriterium fuer Abbruch ("objekt 2" soll matchen)
+        string numeralcheck;
+        if(range && range<=2 &&
+           sizeof(numeralcheck=noparsestr[q_start+range]) &&
+           to_string(to_int(numeralcheck))==numeralcheck)
+          break;
+
+        // Substringlaenge verkuerzen und weiter
+        range--;
+       }
+      }
+
+      // Match gefunden!
+      if(matchstr) {
+       matchpos=range+q_start;
+       // DBG(matchpos);
+       break;
+      }
+      q_start++;
+     } // end while
+    }
+   }
+
+   // Fehlermeldung fuer diese fehlgeschlagene Synonymgruppe setzen
+   if(matchpos == NOMATCHFOUND) {
+    // Fehlermeldungen und ein Eintrag an der Fehlerstelle?
+    if(pointerp(flag) && sizeof(flag)>nrul) {
+
+     matchstr=flag[nrul];
+
+     if(stringp(matchstr) && sizeof(matchstr)) {
+      if(member(matchstr,'@')>=0) {
+       matchstr=replace_personal(&matchstr,({this_player()})+parsed,1);
+       string stamm=((query_verb()[<1]^'e')?query_verb():query_verb()[0..<2]);
+       matchstr=regreplace(matchstr,"@VERB",capitalize(stamm),1);
+       matchstr=regreplace(matchstr,"@verb",stamm,1);
+      }
+
+      // ist Fehlermeldung ein WRITE?
+      // dann return 1 !
+      if(intp(flag[<1]) && flag[<1]<=nrul) {
+       if(member(matchstr,'^')>=0) {
+        matchstr=explode(matchstr,"^");
+        write(capitalize(break_string(matchstr[0],78,0,1)));
+        if(sizeof(matchstr[1]))
+         say(capitalize(break_string(matchstr[1],78,0,1)),({this_player()}) );
+       } else write(capitalize(break_string(matchstr,78,0,1)));
+       return 1;
+      } else notify_fail(capitalize(break_string(matchstr,78,0,1)));
+     }
+    }
+    return 0;
+   }
+
+   // Updaten der Hilfsvariablen
+   parsed+=({matchstr});
+   lastmatchpos=matchpos;
+   nrul++;
+  } // end while(nrul<rs) ... Erfolg ... ab zum naechsten Regelteil
+ }
+
+ // Arrays der @-Objectmatches in Parameter fuer Methode resubstituieren
+ int objsize;
+ if((objsize=sizeof(objmatches)))
+  while((objsize-=2)>=0)
+   parsed[objmatches[objsize]]=objmatches[objsize+1];
+
+ // erfolgreich Methode gefunden/eine Regel bis zum Ende durchgeparst:
+ return(_execute(&fun,&str,&parsed));
+}
+
+// Auswertung - add_action-Fun 
+public int _cl(string str) {
+ int nindex;
+ string *keys;
+ mixed *flag;
+ // Verb existiert, Kommandos auch, Eintrag fuer Verb gefunden
+ if(mappingp(added_cmds) && query_verb()) {
+  mixed *fun, *rule, *ids;
+
+  // ist das Verb ein Key im Kommandomapping?
+  if(m_contains(&fun, &flag, &rule, &ids, added_cmds, query_verb())) {
+   string *noparsestr;
+   nindex=sizeof(fun);
+   noparsestr=explode((str||"")," ")-({""});
+   // dann matche ungeparsten Input gegen etwaige Regeln
+   // -- nicht aendern: neue Kommandos sollen alte "ueberschreiben" koennen
+   while(nindex--)
+    if(_process_command(&str, noparsestr,
+		       fun[nindex], flag[nindex], rule[nindex]))
+    {
+      GiveEP(EP_CMD, query_verb());
+      return 1;
+    }
+  }
+
+  // keine Regel passte, unscharfe Auswertung auf alle
+  // AddCmdverben ausdehnen
+  keys=m_indices(added_cmds);
+  nindex=sizeof(keys);
+  while(nindex--)
+   if(!strstr(query_verb(),keys[nindex]) &&
+      member((flag=added_cmds[keys[nindex],1]),1)>=0) {
+    int i,ret;
+    i=sizeof(flag);
+    // Reihenfolge nicht aendern !
+    while(i--)
+      if(flag[i]==1) {
+        mixed *exec = added_cmds[keys[nindex],0];
+        if(!pointerp(exec) || sizeof(exec)<=i)
+          catch(raise_error(
+            "AddCmd-Auswertung: CatchAll-Kommando \""+keys[nindex]+"\""
+            " wurde waehrend der Ausfuehrung veraendert oder geloescht. "
+            "Klartext: Das ist schlecht. Sucht ein RemoveCmd? ");
+            publish);
+        else if(_execute(exec[i],str,0)) {
+          GiveEP(EP_CMD, query_verb());
+          return 1;
+        }
+      }
+   }
+ }
+ return 0;
+}
+
+void init() {
+ add_action("_cl","",1);
+}
+
+
+static void _check_copy_commands(string ind, mixed *fun,
+				  mixed *flag, mixed *rule) {
+ if(pointerp(fun)) added_cmds[ind,0]=fun+allocate(0);
+ else added_cmds[ind,0]=({fun});
+ if(pointerp(flag)) added_cmds[ind,1]=flag+allocate(0);
+ else if(flag) added_cmds[ind,1]=({flag});
+ else added_cmds[ind,1]=allocate(sizeof(added_cmds[ind]));
+ if(pointerp(rule)) added_cmds[ind,2]=rule+allocate(0);
+ else added_cmds[ind,2]=allocate(sizeof(added_cmds[ind]));
+
+ if(sizeof(added_cmds[ind])!=sizeof(added_cmds[ind,1]) ||
+    sizeof(added_cmds[ind])!=sizeof(added_cmds[ind,2])) {
+  added_cmds=(mapping)0;
+  raise_error("SetProp(P_COMMANDS): corrupt commands-mapping.\n");
+ }
+}
+
+static mapping _set_commands(mapping commands) {
+ if(!commands) added_cmds=(mapping)0;
+ else if(mappingp(commands)) {
+  added_cmds=m_allocate(sizeof(commands),4);
+  walk_mapping(commands,#'_check_copy_commands);
+ }
+ if (mappingp(added_cmds))
+   return(deep_copy(added_cmds));
+ else
+   return (mapping)0;
+}
+
+static mapping _query_commands() {
+ if (mappingp(added_cmds))
+   return(deep_copy(added_cmds));
+ else
+   return (mapping)0;
+}
+
diff --git a/std/thing/description.c b/std/thing/description.c
new file mode 100644
index 0000000..47bfb0e
--- /dev/null
+++ b/std/thing/description.c
@@ -0,0 +1,851 @@
+// MorgenGrauen MUDlib
+//
+// thing/description.c -- description handling for standard objects
+//
+// $Id: description.c 9561 2016-05-25 19:33:22Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <tls.h>
+#include <thing/description.h>
+#include <thing/material.h>
+#include <thing/lighttypes.h>
+#include <exploration.h>                  // wegen EPMASTER
+#include <class.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/language.h>
+
+#undef NEED_PROTOTYPES
+#include <properties.h>
+
+// Variable um den FP abzuspeichern 
+private nosave mixed *explore;
+
+// Prototypen
+public string short();
+public varargs string long(int mode);
+
+//                       #####################
+//######################## System-Funktionen ############################
+//                       #####################
+
+// Objekt erzeugen
+protected void create()
+{
+  string poid, tpid;
+  object tp;
+
+  SetProp( P_NAME, "Ding" );
+  SetProp( P_SHORT, "Nichts besonderes" );
+  SetProp( P_LONG, 0 );
+
+  Set( P_ADJECTIVES, ({}) );
+  Set( P_NAME_ADJ, ({}) );
+  Set( P_IDS, ({}) );
+  Set( P_CLASS, ({}) );
+
+  Set( P_READ_DETAILS, ([]), F_VALUE);
+  Set( P_READ_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS);
+
+  Set( P_DETAILS, ([]), F_VALUE);
+  Set( P_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+  Set( P_SMELLS, ([]), F_VALUE);
+  Set( P_SMELLS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+  Set( P_SOUNDS, ([]), F_VALUE);
+  Set( P_SOUNDS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+  Set( P_TOUCH_DETAILS, ([]), F_VALUE);
+  Set( P_TOUCH_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+  // Aenderungen an dieser Prop sind tabu.
+  Set( P_CLONE_TIME, NOSETMETHOD|SECURED, F_MODE_AS );
+
+  // Id des Cloners und des Besitzers kommen nach P_CLONER
+  if (objectp( tp=this_interactive()||this_player() ))
+  {
+    tpid=geteuid(tp);
+    if (!(tpid=geteuid(tp))) tpid=getuid(tp);
+  }
+  else
+    tpid="UNKNOWN";
+
+  if (previous_object())
+  {
+    if (!(poid = geteuid(previous_object())))
+      poid = getuid(previous_object());
+  }
+  else
+    poid="UNKNOWN";
+
+  Set( P_CLONER, (poid != tpid ? poid+":"+tpid: tpid) );
+  Set( P_CLONER, NOSETMETHOD|SECURED, F_MODE_AS );
+
+  // Gibt es FPs ?
+  explore = (mixed *)EPMASTER->QueryExplore();
+
+  return;
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+//                        ##################
+//######################### Forscherpunkte ##############################
+//                        ##################
+
+// FP vergeben
+static void GiveEP( int type, string key )
+{
+  //Abbruch, wenn vergebendes Objekt schon zerstoert ist. ACHTUNG: Auch
+  //diese Abfrage wuerde kein FP vergeben werden, wenn sich das Objekt im
+  //vergebenden Kommando zerstoert, da der Driver call_other() von zerstoerten
+  //Objekten ignoriert!
+  if (!objectp(this_object())) return;
+  if (this_player()) this_player()->countCmds( type, key );
+  
+  if (explore&&!extern_call()&&
+      (explore[0] == type) && (member(explore[1], key) >= 0) )
+    EPMASTER->GiveExplorationPoint(key);
+  return;
+}
+
+// Manche Objekte koennen mit rename_object einen neuen Filenamen bekommen.
+// Danach sollte der EPMASTER neu nach den Details befragt werden.
+void __reload_explore()
+{
+  explore = (mixed *)EPMASTER->QueryExplore();
+  return;
+}
+
+//                         #################
+//########################## ID-Management ##############################
+//                         #################
+
+// Gibt eine ID zurueck, die den Ladenamen, die aktuelle Kurzbeschreibung und
+// die aktuelle Langbeschreibung beruecksichtigt. Diese ID ist ein Hashwert.
+public string description_id() {
+  return hash(TLS_HASH_MD5, load_name() + short() + long());
+}
+
+/* Ids muessen uebergeben werden, da unit machmal mit plural-Ids, */
+/* also anderen als den normalen arbeiten muss. */
+int match_item( string str, string *ids )
+{
+  string *obj,*ads;
+  int len, i;
+  
+  // Parameter falsch? Passt nicht ...
+  if(!str)           return 0;
+  if(!pointerp(ids)) return 0;
+  if(!sizeof(ids))   return 0;
+  
+  // Ist schon so dabei? Na Klasse :-)
+  if(member(ids,str)>-1) return 1;
+  
+  // Keine Adjektive vorhanden ... passt nicht.
+  if (!(ads=QueryProp(P_ADJECTIVES))) return 0;
+  if (!sizeof(ads))                   return 0;
+  
+  // Nur ein Wort? Dann passt es nicht
+  obj=explode(str," ");
+  if (!(len=sizeof(obj)-1)) return 0;
+  
+  // Adjektive stehen am Anfang. Sobald es nicht mehr passt,
+  // muss es das Objektiv sein.
+  while(i<len&&member(ads,obj[i])>-1) i++;
+
+  return (member(ids,implode(obj[i..len]," "))>-1);
+}
+
+// Wird vom Gamedriver aufgerufen (present)
+// Hat dieser Gegenstand die ID str?
+// lvl wird ignoriert
+varargs int id( string str, int lvl ) 
+{ 
+  string str2, tmp;
+  int count;	
+  string|string* ids;
+
+  // Kein Argument? Dann passt es nicht ...
+  if (!stringp(str)) return 0;
+
+  // Keine IDs? Auch nicht gut ...
+  if (!pointerp(ids=QueryProp(P_IDS))) return 0;
+  if (!sizeof(ids)) return 0;
+
+  ids += ({ ("\n" + object_name()),
+            ("\n" + explode(object_name(),"#")[0]) });
+
+  // Id passt? Alles klar :-)
+  if (match_item( str, ids )) return 1;
+
+  // Die id hat eine Zahl drin. Wenn Zahl die Rohid passt,
+  // dann gucken, ob man selber das nte Element ist.
+  if (sscanf( str, "%s %d%s", str2, count, tmp)<2) return 0;
+  if (count<1) return 0;
+  if (sizeof(tmp)) return 0;
+  if (!match_item( str2, ids )) return 0;
+  if (!environment()) return 0;
+  return present(str2,count,environment())==this_object();
+}
+
+// Gleich eine ganze Liste von ids testen
+int match_ids(string *list)
+{
+  string *ids;
+
+  // Ungueltige Parameter? Weg hier ...  
+  if (!pointerp(list)) return 0;
+  if (!pointerp(ids=QueryProp(P_IDS))) return 0;
+
+  ids += ({ ("\n" + object_name()),
+            ("\n" + explode(object_name(),"#")[0]) });
+
+  return sizeof( list & ids );
+}
+
+// ID hinzufuegen
+void AddId( string|string* str )
+{
+  if (stringp(str)) str = ({ str });
+  if (pointerp(str))
+    // Doppelte eliminieren
+    Set( P_IDS, Query(P_IDS, F_VALUE)-str+str, F_VALUE);
+  return;
+}
+
+// ID entfernen
+void RemoveId(string|string* str)
+{
+  if (stringp(str)) str = ({ str });
+  if (pointerp(str))
+    Set(P_IDS,Query(P_IDS, F_VALUE)-str, F_VALUE);
+  return;
+}
+
+// Alle Ids auf einmal setzen
+static string* _set_ids( string* ids )
+{
+    Set( P_IDS,({}));
+    AddId(ids);
+    return Query(P_IDS, F_VALUE);
+}
+
+// Adjektiv hinzufuegen
+void AddAdjective(string|string* str)
+{
+  if (stringp(str)) str = ({ str });
+  if (pointerp(str))
+    // Doppelte eliminieren
+      Set( P_ADJECTIVES, Query(P_ADJECTIVES, F_VALUE)-str+str, F_VALUE );
+  return;
+}
+
+// Adjektiv entfernen
+void RemoveAdjective(string|string* str)
+{
+  if (stringp(str)) str = ({ str });
+  if (pointerp(str))
+    Set( P_ADJECTIVES, Query(P_ADJECTIVES, F_VALUE) - str, F_VALUE );
+  return;
+}
+
+// Alle Adjektive auf einmal setzen
+static string* _set_adjectives(string* adjectives)
+{
+  Set( P_ADJECTIVES,({}), F_VALUE);
+  AddAdjective(adjectives);
+  return Query(P_ADJECTIVES, F_VALUE);
+}
+
+
+//                         ################
+//########################## Namensgebung ###############################
+//                         ################
+
+// Im Fall von mehreren Adjektiven muessen diese mit komma
+// zusamengebaut werden, dazu muss ich das leerzeichen aber erstmal
+// abschneiden und so weiter ...
+private string depointer_adj( string* adj, int casus, int demon ) {
+  string msg;
+  int start;
+  string res,a;
+	adj = map( adj, #'DeclAdj, casus, demon );
+  start = 1;
+  res = "";
+  foreach( a: adj ) {
+    res += (start ? "" : ", ") + a[0..<2];
+    start = 0;
+  }
+  return res + " ";
+}
+
+// Wie lautet der Name des Objekts?
+varargs string name(int casus,int demon)
+{
+  mixed sh, adj;
+  int art, plural;
+
+  art = QueryProp(P_ARTICLE);
+
+  // RAW: direkt zurueckgeben ohne Verarbeitung
+  if (casus == RAW )
+  {
+    if(pointerp(QueryProp(P_NAME)))
+      return QueryProp(P_NAME)[WER];
+    return QueryProp(P_NAME);
+  }
+
+  // Unsichtbar: Etwas
+  if (QueryProp(P_INVIS))
+    return ({ "etwas", "von etwas", "etwas", "etwas" })[casus];
+
+  // Kein Name? Schade ...
+  if (!(sh=QueryProp(P_NAME)) ||
+      (stringp(sh) && !sizeof(sh))) return 0;
+
+  // P_NAME pruefen.
+  if (pointerp(sh) && sizeof(sh) != 4)
+      raise_error(sprintf("Ungueltige Arraygroesse in P_NAME: %d\n",
+            sizeof(sh)));
+
+  // Plural .. Namen verwursten
+  if (plural = QueryProp(P_PLURAL))
+  {
+    // Selber Artikel suchen ist nicht ...
+    if (demon==2||!art) demon = 0;
+
+    // falls P_NAME ein Array mit Faellen enthaelt, den richtigen
+    // extrahieren...
+    if (pointerp(sh)) {
+        sh = sh[casus];
+    }
+    else {
+        // sonst versuchen, zu deklinieren.
+        int last = sh[<1];
+        if (casus == WEM&&last!='s'&&last!='n') sh = sh + "n";
+    }
+
+    // Sind Adjektive vorhanden?
+    if ( pointerp(adj = QueryProp(P_NAME_ADJ)) && sizeof(adj))
+        adj = depointer_adj(adj,casus,demon);
+    if (!stringp(adj)) adj = "";
+
+    return sprintf("%s%s%s%s",QueryArticle(casus,demon,0),adj,
+                   (plural < 2 ? "":(plural < 8 ?
+                    ({ "zwei ", "drei ", "vier ", "fuenf ", "sechs ",
+                       "sieben " })[plural-2] : to_string(plural)+" ")),sh);
+  }
+
+  // Name ist Pointer: Einfach den richtigen auswaehlen
+  if (pointerp(sh))
+    sh = sh[casus];
+
+  // Ansonsten doch wieder verwursten ...
+  else if (stringp(sh))
+  {
+    int last = sh[<1];
+
+    switch(casus)
+    {
+      case WEM:
+      case WEN:
+        if ( art && last=='e'&&QueryProp(P_GENDER) == MALE)
+          sh = (string)sh + "n";
+        break;
+
+      case WESSEN:
+        if( !art )
+        {
+          switch(last)
+          {
+            case 'x':
+            case 's':
+            case 'z':
+              sh = (string)sh + "'";
+              break;
+
+          default:
+            sh = (string)sh + "s";
+          }
+        } 
+        else
+        {
+          switch(last)
+          {
+            default:
+              if (QueryProp(P_GENDER)!=FEMALE)
+                sh=(string)sh+"s";
+              break;
+            case 'e':
+              if (QueryProp(P_GENDER)==MALE)
+                sh=(string)sh+"n";
+            case 'x':
+            case 's':
+            case 'z':
+              break;
+          } /* switch (last) */
+        } /* if( !art ) else */
+    } /* switch( casus ) */
+  } /* pointerp(sh) */
+
+  // RAW? Dann mal zurueck
+  if (demon == RAW) return (string)sh;
+
+  // Selber Artikel suchen ...
+  if (demon==2)
+  {
+    if (art)
+      demon = SuggestArticle();
+    else
+      demon=0; // Kein Artikel: egal (SuggestArticle ist zeitaufwendig)
+  }
+
+  if (pointerp(adj = QueryProp(P_NAME_ADJ)) && sizeof(adj))
+    adj = depointer_adj(adj,casus,demon);
+
+  if (!stringp(adj))  adj = "";
+
+  return QueryArticle( casus, demon )+adj+sh;
+}
+
+// Grossgeschriebenen Namen zurueckgeben
+varargs string Name( int casus, int demon )
+{
+    return capitalize(name( casus, demon )||"");
+}
+
+// Langbeschreibung anzeigen
+public varargs string long(int mode)
+{
+    return process_string( QueryProp(P_LONG) );
+}
+
+// Kurzbeschreibung anzeigen, falls nicht unsichtbar
+public string short()
+{
+  string sh;
+
+  // Unsichtbar? Dann gibts nichts zu sehen ...
+  if (QueryProp(P_INVIS)||!(sh=QueryProp(P_SHORT)))
+    return 0;
+  
+  return process_string(sh)+".\n";
+}
+
+// Namens-Adjektive setzen
+static string* _set_name_adj(string|string* adjectives)
+{
+  if (!adjectives)
+      adjectives=({});
+  // In Array umwandeln
+  else if ( !pointerp(adjectives)) 
+      adjectives = ({ to_string(adjectives) });
+  return Set( P_NAME_ADJ, adjectives );
+}
+
+//                   ############################
+//#################### Details, Gerueche, Laerm #########################
+//                   ############################
+
+// Low-level Funktion zum Ergaenzen von Details, wird von den div. Add...()
+// gerufen, die das richtige Mapping uebergeben.
+// Aendert das Mapping <details> direkt.
+private void _add_details(string|string* keys,
+                          string|string*|mapping|closure descr,
+                          mapping details )
+{
+  if (stringp(keys))
+    details[keys]=descr;
+  else if (pointerp(keys))
+  {
+    foreach(string key : keys)
+      details[lower_case(key)]=descr;
+  }
+  else
+    raise_error("Wrong type to argument 1, expected string|string*.\n");
+}
+
+// Low-level Funktion zum Entfernen von Details, wird von den div. Remove...()
+// gerufen, die das richtige Mapping uebergeben.
+// Aendert das Mapping <details> direkt.
+private void _remove_details(string|string* keys, mapping details )
+{
+  if (stringp(keys))
+    details -= ([keys]);
+  else if (pointerp(keys))
+    details -= mkmapping(keys);
+  else
+    raise_error("Wrong type to argument 1, expected string|string*.\n");
+}
+
+// Detail(s) hinzufuegen
+void AddDetail(string|string* keys, string|string*|mapping|closure descr)
+{
+    int i;
+    mapping details;
+
+    details = Query(P_DETAILS, F_VALUE);
+
+    // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    return _add_details(keys, descr, details);
+}
+
+// Detail(s) entfernen
+varargs void RemoveDetail(string|string* keys )
+{
+  // Alle loeschen geht direkt ...
+  if (!keys )
+    Set(P_DETAILS, ([]), F_VALUE);
+  else
+    // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    _remove_details(keys, Query(P_DETAILS, F_VALUE));
+}
+
+// SpecialDetail hinzufuegen
+void AddSpecialDetail(string|string* keys, string functionname )
+{
+    closure cl;
+
+    // Absichern! Sonst koennte jeder interne Funktionen aufrufen
+    if (extern_call() &&
+        (geteuid(previous_object()) != geteuid() || process_call()) &&
+        !(object_name(previous_object()) == "/obj/doormaster" &&
+          functionname == "special_detail_doors") )
+      raise_error( "Illegal use of AddSpecialDetail!\n" );
+
+    // Closure generieren
+    if ( !stringp(functionname)||
+         !(cl = symbol_function( functionname, this_object())) )
+      return;
+
+    // Detail hinzufuegen
+    AddDetail( keys, cl );
+    return;
+}
+
+// SpecialDetail(s) entfernen
+void RemoveSpecialDetail(string|string* keys )
+{
+  // RemoveSpecialDetail(0) wuerde sonst ALLE Details (auch die
+  // 'normalen') loeschen
+  if (pointerp(keys)||stringp(keys))
+    RemoveDetail(keys);
+  return;
+}
+
+// Lesbares Detail einfuegen
+void AddReadDetail(string|string* keys,
+                   string|string*|mapping|closure descr )
+{
+  // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+  return _add_details(keys, descr, Query(P_READ_DETAILS, F_VALUE));
+}
+
+// Lesbare(s) Detail(s) entfernen
+varargs void RemoveReadDetail(string|string* keys )
+{
+  // Alle loeschen geht direkt ...
+  if (!keys )
+    Set(P_READ_DETAILS, ([]), F_VALUE);
+  else
+    // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    _remove_details(keys, Query(P_READ_DETAILS, F_VALUE));
+}
+
+// Geraeusch(e) dazufuegen
+void AddSounds(string|string* keys,
+               string|string*|mapping|closure descr )
+{
+  // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+  return _add_details(keys, descr, Query(P_SOUNDS, F_VALUE));
+}
+
+// Geraeusch(e) entfernen
+varargs void RemoveSounds(string|string* keys )
+{
+  // Alle loeschen geht direkt ...
+  if (!keys )
+    Set(P_SOUNDS, ([]), F_VALUE);
+  else
+    // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    _remove_details(keys, Query(P_SOUNDS, F_VALUE));
+}
+
+// Geru(e)ch(e) hinzufuegen
+void AddSmells(string|string* keys,
+               string|string*|mapping|closure descr )
+{
+  // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+  return _add_details(keys, descr, Query(P_SMELLS, F_VALUE));
+}
+
+// Geru(e)ch(e) entfernen
+varargs void RemoveSmells(string|string* keys )
+{
+  // Alle loeschen geht direkt ...
+  if (!keys )
+    Set(P_SMELLS, ([]), F_VALUE);
+  else
+    // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    _remove_details(keys, Query(P_SMELLS, F_VALUE));
+}
+
+// Tastbare(s) Detail(s) hinzufuegen
+void AddTouchDetail(string|string* keys,
+                    string|string*|mapping|closure descr )
+{
+  // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+  return _add_details(keys, descr, Query(P_TOUCH_DETAILS, F_VALUE));
+}
+
+// Tastbare(s) Detail(s) entfernen
+varargs void RemoveTouchDetails(string|string* keys )
+{
+  // Alle loeschen geht direkt ...
+  if (!keys )
+    Set(P_TOUCH_DETAILS, ([]), F_VALUE);
+  else
+    // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    _remove_details(keys, Query(P_TOUCH_DETAILS, F_VALUE));
+}
+
+// Detailinfos fuer Detail key, Spieler hat die Rasse race
+// und benutzt seinen Sinn sense
+varargs string GetDetail(string key, string race, int sense)
+{
+  string|string*|mapping|closure detail;
+  
+  if (stringp(race)) race = lower_case(race);
+  
+  switch(sense)
+  {
+    case SENSE_SMELL: detail=Query(P_SMELLS, F_VALUE)[key];
+                      sense=EP_SMELL; break;
+    case SENSE_SOUND: detail=Query(P_SOUNDS, F_VALUE)[key];
+                      sense=EP_SOUND; break;
+    case SENSE_TOUCH: detail=Query(P_TOUCH_DETAILS, F_VALUE)[key];
+                      sense=EP_TOUCH; break;
+    case SENSE_READ:  detail=Query(P_READ_DETAILS, F_VALUE)[key];
+                      sense=EP_RDET;
+                      break;
+
+    default:          detail=Query(P_DETAILS, F_VALUE)[key];
+                      sense=EP_DETAIL; break;
+  }
+
+  if (!stringp(detail))
+  {
+    if (closurep(detail))
+      detail = (string)funcall(detail,key);
+    else if (mappingp(detail))
+      detail = (string)(detail[race]||detail[0]);
+    else if (pointerp(detail))
+      detail = (string)(detail[random(sizeof(detail))]);
+  }
+
+  // FP vergeben (so vorhanden ;-) )
+  if (detail) GiveEP(sense,key);
+
+  return detail;
+}
+
+// TODO: OBSOLET (Libgrep notwendig)
+void read( string str ) {
+  raise_error("Diese Funktion existiert nicht mehr.\n");
+}
+
+
+//                      ######################
+//####################### Zugriffsfunktionen ############################
+//                      ######################
+
+// Dienen dazu, die direkte Manipulation der Props von aussen zu erschweren.
+
+// Filter, um Specialdetails zu eliminieren
+// erstellt ausserdem ne Kopie vom Mapping. (Wichtig!)
+// Wird vor allem benoetigt, um P_DETAILS in P_DETAILS und 
+// P_SPECIAL_DETAILS zu treffen.
+private int _closures(string x, mapping details, int yes ) 
+{ 
+    return yes ? closurep(details[x]) : !closurep(details[x]); 
+}
+
+static mapping _query_details()
+{
+  return filter_indices(Query(P_DETAILS, F_VALUE), #'_closures,
+      Query(P_DETAILS, F_VALUE),0);
+}
+
+static mapping _query_special_details()
+{
+  return filter_indices(Query(P_DETAILS, F_VALUE),#'_closures,
+      Query(P_DETAILS, F_VALUE),1);
+}
+
+static mapping _query_read_details() {
+  return deep_copy(Query(P_READ_DETAILS, F_VALUE));
+}
+
+static mapping _query_sound_details() {
+  return deep_copy(Query(P_SOUNDS, F_VALUE));
+}
+
+static mapping _query_smell_details() {
+  return deep_copy(Query(P_SMELLS, F_VALUE));
+}
+
+
+//                    ##########################
+//##################### Klassen-Mitgliedschaft ##########################
+//                    ##########################
+
+// Klasse hinzufuegen
+public void AddClass(string|string* str)
+{
+  if (stringp(str)) 
+      str = ({ str });
+  // Aliase aufloesen und implizite Klassen addieren.
+  str = (string*)CLASSDB->AddImplicitClasses(str);
+  // Summe mit alten Klassen bilden und Doppelte eliminieren
+  str = str + Query(P_CLASS, F_VALUE);
+  Set( P_CLASS, m_indices(mkmapping(str)), F_VALUE);
+
+  return;
+}
+
+// Klasse entfernen
+void RemoveClass(string|string* str)
+{
+ if (stringp(str)) 
+      str = ({ str });
+
+  // Aliase aufloesen und implizite Klassen addieren.
+  str = (string*)CLASSDB->AddImplicitClasses(str);
+
+  // Und alle - inklusive impliziter Klassen - entfernen
+  // TODO: Pruefen, ob dies die richtige Entscheidung ist.
+  Set( P_CLASS, Query(P_CLASS, F_VALUE)-str, F_VALUE);
+
+  return;
+}
+
+// Ist das Objekt Mitglied der Klasse str?
+int is_class_member(string|string* str)
+{
+  // Keine Klasse, keine Mitgliedschaft ...
+  if (!str || str=="") 
+      return 0;
+
+  // Es sollte schon ein Array sein
+  if (stringp(str)) 
+      str = ({ str });
+
+  // Klassen und Ids ins Array
+  string *classes=QueryProp(P_CLASS);
+  if (!pointerp(classes))
+    return 0;
+
+  // .. und testen
+  foreach(string class : str)
+    if (member(classes,class) > -1 ) return 1;
+
+  return 0;
+}
+
+// Klasse direkt setzen abfangen
+static string* _set_class(string* classes )
+{
+  Set( P_CLASS, ({}), F_VALUE );
+  AddClass(classes);
+  return QueryProp(P_CLASS);
+}
+
+//                       #####################
+//######################## Material-Handling ############################
+//                       #####################
+
+// Material setzen
+static mapping _set_material(mapping|string|string* mat )
+{
+  mapping mats = ([]);
+  
+  if (mappingp(mat)) 
+  {
+    if( !sizeof(mat) || !widthof(mat) )
+      raise_error(sprintf("P_MATERIAL: expected mapping with at least one "
+        "key and one value, got %.50O\n",mat));
+    else 
+      mats = mat;
+  }
+  else if (stringp(mat))
+    mats[mat]=100;
+  else
+  {
+    int sz = sizeof(mat);
+    // Kommt dann vor, wenn <mat> 0 oder ({}) ist.
+    if ( !sz )
+      raise_error(sprintf("P_MATERIAL: expected string or non-empty "
+        "mapping|string*, got %.50O.\n", mat));
+    mats = mkmapping(mat, allocate(sz, 100/sz));
+  } 
+  return Set( P_MATERIAL, mats, F_VALUE );
+}
+
+// Woraus besteht das Objekt?
+static mapping _query_material()
+{
+  mixed res;
+  
+  if ( !mappingp(res = Query(P_MATERIAL, F_VALUE)) )
+    return ([MAT_MISC:100]);
+  
+  return res;
+}
+
+// Anteil von mat am Objekt?
+int QueryMaterial( string mat )
+{
+  mapping mats;
+  
+  if ( !mappingp(mats = QueryProp(P_MATERIAL)) )
+    return 0;
+  
+  return mats[mat];
+}
+
+// Anteil der Gruppe am Objekt
+int QueryMaterialGroup( string matgroup )
+{
+  return (int)call_other( MATERIALDB, "MaterialGroup",
+                          QueryProp(P_MATERIAL), matgroup );
+}
+
+
+string MaterialList( int casus, mixed idinf )
+{
+  return (string)call_other( MATERIALDB, "ConvMaterialList",
+                             QueryProp(P_MATERIAL), casus, idinf );
+}
+
+static int _set_size(int sz) {
+//Groesse muss > 0 sein, alles andere ist unsinnig! (0 und neg. Groessen
+//haben keine phys. Relevanz und machen u.U. Probleme mit Objekten, die
+//Schaden in Abhaengigkeit der Groesse machen)
+  if (sz>0)
+    Set(P_SIZE,sz,F_VALUE);
+  return(Query(P_SIZE,F_VALUE));
+}
+
+// P_CLONE_TIME
+static int _query_clone_time() { return object_time(); }
+
diff --git a/std/thing/envchk.c b/std/thing/envchk.c
new file mode 100644
index 0000000..f6ce13b
--- /dev/null
+++ b/std/thing/envchk.c
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// envchk.c -- verhindern, dass objekte ohne env herumfliegen
+//
+// $Id: thing.c 6283 2007-05-09 21:30:33Z Zesstra $                      
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <moving.h>
+#define NEED_PROTOTYPES
+#include <thing/moving.h>
+
+protected void check_for_environment(string cloner)
+{
+  // Clones, die innerhalb von 10 Sekunden kein Environment haben,
+  // sollen auf -debug scrollen.
+  if ( clonep() && !environment() ) {
+    // mal in den Muellraum bewegen, damit diese Objekte zwar nicht zerstoert
+    // werden, aber zumindest hinterher noch einfach auffindbar sind. (Und
+    // entweder per hand oder automatisch aufgeraeumt werden koennen.)
+    move("/room/muellraum",M_NOCHECK|M_SILENT);
+    if ( !stringp(cloner) || !sizeof(cloner) )
+      cloner = "<Unbekannt>";
+    raise_error("Objekt hat kein Environment. Cloner: ["+cloner+"] ");
+  }
+}
+
+void create()
+{
+	if( clonep() ) 
+    call_out(#'check_for_environment, 3, object_name(previous_object()));
+}
diff --git a/std/thing/language.c b/std/thing/language.c
new file mode 100644
index 0000000..4a3d6f9
--- /dev/null
+++ b/std/thing/language.c
@@ -0,0 +1,199 @@
+// MorgenGrauen MUDlib
+//
+// thing/language.c -- language handling object
+//
+// $Id: language.c 8359 2013-02-09 11:43:39Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <thing/language.h>
+#include <thing/description.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+
+// Kein create()
+void create()
+{
+  return;
+}
+
+//Inversion, damit Defaultwert von 0 einen Artikel ausgibt
+static int _query_article() { return !Query(P_ARTICLE); }
+
+static int _set_article(int art) { return !Set(P_ARTICLE,!art); }
+
+// Bestimmten Artikel bestimmen
+private string query_c_article(int casus)
+{
+  if (QueryProp(P_PLURAL))
+    return ({ "die ", "der ", "den ", "die "})[casus];
+
+  return ({ ({ "das ", "des ", "dem ", "das " }),
+            ({ "der ", "des ", "dem ", "den " }),
+            ({ "die ", "der ", "der ", "die " }) })
+    [(int)QueryProp(P_GENDER)][casus];
+}
+
+// Ende fuer Artikel und Adjektive
+private varargs string query_g_suffix(int gen, int casus, int anzahl)
+{
+  return ({ ({ ({ "","e"}), ({"es","er"}), ({"em","en"}), ({  "","e"}) }),
+	    ({ ({ "","e"}), ({"es","er"}), ({"em","en"}), ({"en","e"}) }),
+	    ({ ({"e","e"}), ({"er","er"}), ({"er","en"}), ({ "e","e"}) }) })
+    [gen][casus][anzahl];
+}
+
+// Artikel vorschlagen: gibt es noch mehr Objekte im inv?
+varargs int SuggestArticle(string id)
+{
+  object ob,*obs;
+
+  // Raum oder Master: Bestimmt.
+  if (!environment()) return 1;
+
+  // Keine id? Dann raus
+  if (!id) return 1;
+
+  // Objekt mit gleichem Namen im env? Dann unbestimmt
+  for ( ob=first_inventory(environment()) ; ob ; ob=next_inventory(ob) )
+    if ( ob!=this_object()&& id==(string)ob->QueryProp(P_NAME) )
+      return 0;
+
+  // sonst bestimmt
+  return 1;
+}
+
+// Artikel bestimmen
+varargs string QueryArticle(int casus, int dem, int force)
+{
+  // Kein Artikel
+  if (!force &&!(QueryProp(P_ARTICLE))) return "";
+
+  // Artikelart aussuchen
+  if ( dem==2 ) dem = SuggestArticle(QueryProp(P_NAME));
+
+  // Bestimmter Artikel
+  if (dem) return query_c_article(casus);
+
+  // Unbestimmter Artikel
+  if (QueryProp(P_PLURAL)) return "";
+  
+  return sprintf("ein%s ",query_g_suffix((int)QueryProp(P_GENDER),casus));
+}
+
+// Besitzanzeiger fuer Objekt bestimmen
+varargs string QueryOwn(int casus)
+{
+  return sprintf("Dein%s",query_g_suffix(QueryProp(P_GENDER),casus));
+}
+
+// Possessivpronomen bestimmen
+varargs string QueryPossPronoun(mixed what, int casus, int number)
+{
+  int gen2;
+
+  // Geschlecht ermitteln
+  gen2 = (objectp(what)?(int)what->QueryProp(P_GENDER):(int)what);
+
+  // Plural ist schoen einfach
+  if (QueryProp(P_PLURAL)) return "ihr"+query_g_suffix(gen2, casus, number);
+
+  return (((((int)QueryProp( P_GENDER ))==FEMALE )? "ihr":"sein")+
+          query_g_suffix(gen2%3, casus, number));
+}
+
+// Pronomen bestimmen nach KNG
+varargs string QueryPronoun(int casus)
+{
+  int gender;
+
+  // Plural ist immer einfach ...
+  if (QueryProp(P_PLURAL)) 
+  {
+     if (casus==WEM) return "ihnen";
+     return "sie";
+  }
+
+  switch(QueryProp(P_GENDER))
+  {
+    case FEMALE:
+      if (casus==WESSEN) return "ihrer";
+      if (casus==WEM) return "ihr";
+      return "sie";
+
+    case MALE:
+      if (casus==WER) return "er";
+      if (casus==WESSEN) return "seiner";
+      if (casus==WEM) return "ihm";
+      return "ihn";
+  }    
+  if (casus==WESSEN) return "seiner";
+  if (casus==WEM) return "ihm";
+  return "es"; //fall-through
+}
+
+// Anrede fuer Spieler bestimmen
+varargs string QueryDu(int casus,int gender,int zahl)
+{
+  return
+    ({ ({({    "du",   "ihr"}),({    "du",   "ihr"}),({    "du",   "ihr"})}),
+       ({({"deiner",  "euer"}),({"deiner",  "euer"}),({"deiner",  "euer"})}),
+       ({({   "dir",  "euch"}),({   "dir",  "euch"}),({   "dir",  "euch"})}),
+       ({({  "dich",  "euch"}),({  "dich",  "euch"}),({  "dich",  "euch"})})
+     })[casus][gender][zahl];
+}
+
+// Welches Geschlecht hat das Objekt als String?
+string QueryGenderString()
+{
+  switch( (int)QueryProp( P_GENDER ))
+  {
+    case MALE:   return "maennlich";
+    case FEMALE: return "weiblich";
+  }
+  return("saechlich"); //fall-through
+}
+
+// Artikel durchdeklinieren nach Kasus, Numerus, Genus und Art
+// des Artikels (demon==bestimmt)
+varargs string DeclAdj(mixed adj, int casus, int demon)
+{
+	// Unregelmaessige Adjektive
+	if( pointerp(adj) ) return adj[casus]+" ";
+
+  // Falscher Typ? Und Tschuess ...
+  if (!stringp(adj)) return "";
+
+  // Plural ist einfach
+  if (QueryProp(P_PLURAL))
+  {
+    // Bestimmt
+    if (demon) return adj+"en ";
+
+    // Unbestimmt
+    return adj + ({ "e ", "er ", "en ", "e " })[casus];
+  }
+  
+  if ( demon )
+    return adj + ({ ({ "e " , "en ", "en ", "e "  }),
+		    ({ "e " , "en ", "en ", "en " }),
+		    ({ "e " , "en ", "en ", "e "  }) })
+      [(int)QueryProp( P_GENDER )][casus];
+  else
+    return adj + ({ ({ "es ", "en ", "en ", "es " }),
+		    ({ "er ", "en ", "en ", "en " }),
+		    ({ "e " , "en ", "en ", "e "  }) })
+      [(int)QueryProp( P_GENDER )][casus];
+}
+
+// P_GENDER setzen. Es sind nur 0,1 und 2 (MALE,FEMALE,NEUTER) erlaubt
+static int _set_gender(int i)
+{
+  if (i>-1&&i<3) return Set(P_GENDER,i);
+  return Query(P_GENDER);
+}
diff --git a/std/thing/light.c b/std/thing/light.c
new file mode 100644
index 0000000..36411b4
--- /dev/null
+++ b/std/thing/light.c
@@ -0,0 +1,62 @@
+// MorgenGrauen MUDlib
+//
+// thing/description.c -- Lichtsystemkomponenten fuer Standardobjekte
+//
+// $Id: description.c 7635 2010-08-19 21:21:30Z Zesstra $
+
+#pragma strict_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/language.h>
+#include <hook.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/lighttypes.h>
+#include <properties.h>
+
+//                       #####################
+//######################## System-Funktionen ############################
+//                       #####################
+
+// Objekt erzeugen
+protected void create()
+{
+  Set( P_LIGHT, 0 );
+  Set( P_LIGHT_TYPE, LT_MISC);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}     
+
+// Lichtsy ... sys ...
+static int _query_total_light() { return QueryProp(P_LIGHT); }
+
+static int _set_light( int light )
+{
+    object env = this_object();
+
+    // TODO: Temporaer Lichtlevel in geeignete Wertefenster zwingen.
+    if (light > 100)
+      light = 100;
+    else if (light < -100)
+      light = -100;
+   
+    while ( objectp(env = environment(env)) )
+        // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+        // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+        // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+        // da um jedes bisschen Rechenzeit geht.
+        // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+        //
+        // Tiamak
+        env->_set_last_content_change();
+    
+    return Set( P_LIGHT, light, F_VALUE);
+}
+
diff --git a/std/thing/moving.c b/std/thing/moving.c
new file mode 100644
index 0000000..780ac41
--- /dev/null
+++ b/std/thing/moving.c
@@ -0,0 +1,201 @@
+// MorgenGrauen MUDlib
+//
+// thing/moving.c -- object moving
+//
+// $Id: moving.c 8892 2014-08-04 19:48:28Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+
+// Das Objekt bewegen.
+// Rueckgabe 1 ist Erfolg, <=0 ist Fehler
+
+// a) P_NODROP/P_NOGET-Behandlung.
+// b) zum Ueberschreiben
+protected int PreventMove(object dest, object oldenv, int method) {
+  int tmp;
+  
+  // M_NOCHECK? -> Bewegung eh erlaubt (und Rueckgabewert wuerde ignoriert)
+  if ((method&M_NOCHECK)) {
+    // Bei M_NOCHECK zwar Prevent* aufrufen, aber das Resultat ignorieren
+    if (oldenv) oldenv->PreventLeave(this_object(),dest);
+    dest->PreventInsert(this_object());
+
+    return 0; // das wars, rest ist egal.
+  }
+
+  // P_NODROP verhindert weggeben
+  if ((method & (M_PUT|M_GIVE)) && QueryProp(P_NODROP))
+      return ME_CANT_BE_DROPPED;
+
+  // P_NOGET verhindert nehmen
+  if ((method & (M_GET|M_GIVE)) && QueryProp(P_NOGET))
+      return ME_CANT_BE_TAKEN;
+
+  // Gewicht ermitteln
+  if ( !(tmp = (int)QueryProp(P_TOTAL_WEIGHT)) )
+      tmp = (int)QueryProp(P_WEIGHT);
+        
+  // Ist das Objekt nicht zu schwer?
+  if ( (tmp = (int)dest->MayAddWeight(tmp)) < 0) {
+      if ( tmp == -2 ) return ME_TOO_HEAVY_FOR_ENV;
+      return ME_TOO_HEAVY;
+  }
+
+  // Ist das Zielobjekt schon voll?
+  if ( !dest->MayAddObject(this_object()) )
+      return TOO_MANY_OBJECTS;
+
+  // Darf hinausbewegt werden?
+  if ( oldenv && oldenv->PreventLeave(this_object(), dest) )
+      return ME_CANT_LEAVE_ENV;
+
+  // Darf hineinbewegt werden?
+  if ( dest->PreventInsert(this_object()) )
+      return ME_CANT_BE_INSERTED;
+
+  return(0);
+}
+
+// zum Ueberschreiben...
+protected void NotifyMove(object dest, object oldenv, int method) {
+}
+
+varargs int move( object|string dest, int method )
+{
+  object oldenv;
+  int tmp;
+  string fn,vc;
+  mixed sens;
+
+  if (!objectp(dest) && !stringp(dest))
+      raise_error(sprintf("Wrong argument 1 to move(). 'dest' must be a "
+            "string or object! Argument was: %.100O\n",
+            dest));
+
+  // Jetzige Umgebung merken
+  oldenv = environment();
+
+  // Bewegung in Para-Welt-Raeume?
+  // tmp ist die Ziel-Parallelweltnummer
+  if (!environment()||!living(environment())||
+      !intp(tmp =(int)environment()->Query(P_PARA)))
+    tmp=0;
+    
+  // Wenn das Objekt von einem in der Parawelt befindlichen Spieler
+  // oder NPC bewegt wird, sollte es auch wieder in der Parawelt landen.
+  // Um Rechenzeit zu sparen, wird angenommen, dass bei Bewegungen in
+  // das Inv oder Env des Spielers 'dest' als Objekt uebergeben wird,
+  // wohingegen bei Bewegungen in Nachbarraeume (die eigentlich nur
+  // interessant sind) 'dest' als Filename angegeben wird.
+  if (tmp&&!objectp(dest)&&!environment(dest)) {
+      // Falls der Zielraum nicht schon explizit in der Parallelwelt ist,
+      // neuen Zielraum suchen. Aber nur, wenn das Ziel kein Clone ist. Sonst
+      // buggt, wenn man versucht, nach raum#42^para zu bewegen.
+      if (!IS_PARA(dest) && strrstr(dest,"#")==-1) {
+        fn=dest+"^"+tmp;
+
+        // Der Parawelt-Raum wird nur zum Ziel, wenn er a) existiert
+        // und b) auch von Spielern betreten werden darf. Letzteres
+        // Kriterium kann nur mit im Objekt gesetzter Property
+        // P_TESTPLAYER umgangen werden.
+          if ( (find_object(fn) || ((file_size(fn+".c")>0||
+                  (file_size(vc=implode(explode(fn,"/")[0..<2],"/")+
+                  "/virtual_compiler.c")>0 &&
+                  !catch(tmp=(int)call_other(vc,"QueryValidObject",fn);
+                    publish) && tmp>0)) &&
+                  !catch(load_object( fn );publish))) &&
+              (!fn->QueryProp(P_NO_PLAYERS) || QueryProp(P_TESTPLAYER)) )
+           dest = fn;
+      }
+  }
+    
+  // dest auf Objekt normieren.
+  if (stringp(dest))
+      dest = load_object(dest);
+    
+  // testen, ob das Objekt bewegt werden will
+  if (tmp=PreventMove(dest, oldenv, method)) {
+      // auf gueltigen Fehler pruefen, wer weiss, was Magier da evtl.
+      // versehentliche zurueckgeben.
+      if (VALID_MOVE_ERROR(tmp))
+        return(tmp);
+      else
+        return(ME_DONT_WANT_TO_BE_MOVED);
+  }
+
+  // Sensitive Objekte muessen entfernt werden
+  sens = QueryProp(P_SENSITIVE);
+
+  if (sens && environment())
+  {
+    environment()->RemoveSensitiveObject( this_object() );
+    if (!objectp(ME))
+      return ME_WAS_DESTRUCTED;
+  }
+  // Bewegen
+  move_object(ME, dest);
+
+  //falls (sich) das objekt im init() zerstoert (wurde). (Die u. stehenden
+  //Funktionsaufrufe werden dann vom Driver eh groesstenteils ignoriert.)
+  if (!objectp(this_object())) return(ME_WAS_DESTRUCTED);
+
+  // Objekt informieren. ;-)
+  NotifyMove(environment(), oldenv, method);
+
+  // Alte Umgebung informieren
+  if (oldenv) oldenv->NotifyLeave(this_object(), dest);
+
+  // Wenn das Objekt eine Umgebung hat, selbige informieren
+  if (environment()) {
+    if (sens)
+    {
+      environment()->InsertSensitiveObject(this_object(),sens);
+      if (!objectp(ME)) return ME_WAS_DESTRUCTED;
+    }
+    environment()->NotifyInsert(this_object(), oldenv);
+  }
+  //wurde das Objekt vielleicht noch zerstoert?
+  if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
+  
+  //scheint wohl alles ok zu sein.
+  return MOVE_OK;
+}
+
+// Das Objekt zerstoeren
+varargs int remove(int silent)
+{ 
+    if (environment() ) {
+        if(QueryProp(P_SENSITIVE))
+                environment()->RemoveSensitiveObject(this_object());
+        environment()->NotifyRemove(this_object());
+    }
+    if (objectp(this_object()))
+        destruct(this_object());
+    return 1;
+}
+
+public string NotifyDestruct(object caller) {
+  // Lichtsystem mit der aenderung versorgen. :-/
+  foreach(object env : all_environment() || ({})) {
+      // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+      // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+      // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+      // da um jedes bisschen Rechenzeit geht.
+      // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+      //
+      // Tiamak
+      env->_set_last_content_change();
+  }
+  return 0;
+}
+
diff --git a/std/thing/properties.c b/std/thing/properties.c
new file mode 100644
index 0000000..06592bf
--- /dev/null
+++ b/std/thing/properties.c
@@ -0,0 +1,326 @@
+// MorgenGrauen MUDlib
+//
+// thing/properties.c -- most general class (property handling)
+//
+// $Id: properties.c 6951 2008-08-09 23:08:31Z Zesstra $
+
+// Properties.c -- Propertyverwaltung
+// (c) 1993 Hate@MorgenGrauen, Mateese@NightFall
+//          Idea and Code      Flames and Destructions
+// -> *grin* thats the point actually :) 
+//
+// Ueberarbeitet von Jof       am 12.06.1994
+// Ueberarbeitet von Mandragon am 11.05.2003
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include "/sys/thing/properties.h"
+#include "/secure/wizlevels.h"
+
+
+// the mapping where the actual properties are stored. Direct initialization.
+private nosave mapping *prop = ({ ([]), ([]), ([]), ([]) });
+
+// the mapping that is used for saving
+private mapping properties;
+
+// security-flag
+private nosave int closure_call;
+
+// Initialisierung der Props. Kann leider momentan nicht private sein, weil
+// Padreic son komisches Objekt hat, was die Funktion hier ruft.
+// TODO: irgendwann mal private machen.
+// TODO: Da props jetzt einfach bei der Deklaration initlisiert wird,
+// eruebrigt sich diese Funktion eigentlich. Bis auf Padreics Objekt...
+protected void InitializeProperties() {
+  prop = ({ ([]), ([]), ([]), ([]) });
+  return;
+}
+
+// Props nur dann initialisieren, wenn sie es noch nicht sind
+protected void create() {
+  // Blueprints in /std benoetigenkeinen Reset ....
+  if (object_name()=="/std/thing/properties")
+    set_next_reset(-1);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Welche externen Objekte duerfen zugreifen?
+nomask private int allowed()
+{
+    if ( (previous_object() && IS_ARCH(getuid(previous_object())) &&
+          this_interactive() && IS_ARCH(this_interactive())) ||
+         (previous_object() && getuid(previous_object()) == ROOTID &&
+          geteuid(previous_object()) == ROOTID) )
+        return 1;
+    return 0;
+}
+
+
+// Set() -- provides direct access to a property, no filters
+public varargs mixed Set( string name, mixed Value, int Type, int extern )
+{
+
+  if (!objectp(this_object()))
+    return 0;
+
+  // Properties, die SECURED oder PROTECTED sind, duerfen nur vom Objekt
+  // selber, EM+ oder ROOT veraendert werden
+  if ((prop[F_MODE][name]&(PROTECTED|SECURED))&&
+      (closure_call||extern||extern_call()) &&
+       previous_object() != this_object() && !allowed())
+    return -1;
+  
+  // Das SECURED-Flag darf bei Properties nicht mehr geloescht werden
+  if ((prop[F_MODE][name]&SECURED)&&
+      (Type==F_MODE||Type==F_MODE_AD)&&(Value & SECURED))
+    return -2;
+  
+  // Setzen duerfen das SECURED-Flag nur das Objekt selber, EM+ oder ROOT
+  if ((Type==F_MODE||Type==F_MODE_AS)&&(Value&SECURED)&&
+      (closure_call ||extern || extern_call()) &&
+       previous_object() != this_object() && !allowed() )
+    return -3;
+
+  switch(Type)
+  {
+    // Je nach Modus Flags veraendern
+    case F_MODE_AS:  prop[F_MODE][name]|= Value;
+                     return prop[F_MODE][name];
+    case F_MODE_AD:  prop[F_MODE][name]&= ~Value;
+                     if (!prop[F_MODE][name]) prop[F_MODE]-=([name]);
+                     return prop[F_MODE][name];
+    case F_MODE:     prop[F_MODE][name]^= Value;
+                     if (!prop[F_MODE][name]) prop[F_MODE]-=([name]);
+                     return prop[F_MODE][name];
+    case F_SET_METHOD:
+      // -1 als Setz-Methode: Nosetmethod setzen
+      if (Value == -1)
+      {
+        prop[F_SET_METHOD]-=([name]);
+        prop[F_MODE][name] |= NOSETMETHOD;
+        return 0;
+      }
+      // Kein break!
+    case F_QUERY_METHOD:
+      // Ungebundene Lambda_Closure? Binden!
+      if (closurep(Value)&&!query_closure_object(Value))
+      {
+        if (extern_call() &&
+             (getuid(previous_object()) != getuid()||
+              geteuid(previous_object()) != geteuid()))
+          return prop[Type][name];
+        
+        Value = bind_lambda( Value,this_object());
+      }
+      // Kein break!
+    default:
+      if (!Value) prop[Type]-=([name]);
+      else prop[Type][name] = Value;
+  }
+
+  // Gesamtwert zurueckgeben
+  return prop[Type][name];
+}
+
+
+// Direktes Auslesen der Property ohne Filter ...
+public varargs mixed Query( string name, int Type )
+{
+    if (pointerp(prop)) return prop[Type][name];
+    return 0;
+}
+
+// Property setzen unter Verwendung evtl. vorhandener Zugriffsfunktionen
+public mixed SetProp( string name, mixed Value )
+{
+  closure func;
+  mixed result;
+   
+  // nur fuer heute
+  if (!objectp(this_object()))
+    return 0;
+
+  // NOSETMETHOD: Darf nicht gesetzt werden
+  if (prop[F_MODE][name] & NOSETMETHOD ) return -1;
+
+  // Set-Method abfragen, so vorhanden
+  if (func=prop[F_SET_METHOD][name])
+  {
+    int flag;
+
+    // Wert als Set-Method? gleich zurueckgeben
+    if (!closurep(func)) return func;
+
+    // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene
+    // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das
+    // Objekt existiert auch noch (sonst waere func == 0).
+
+    // closure_call setzen, falls noch nicht gesetzt
+    if ((flag=closure_call<time()))
+      closure_call = time()+59;
+
+    // Dann mal die Closure aufrufen. Bei Fehler selbige loeschen
+    if (catch(result=funcall(func, Value, name);publish))
+    {
+      prop[F_SET_METHOD]-=([name]);
+    }
+      
+    // Wenn closure_call gesetzt wurde, wieder loeschen
+    if (flag) closure_call = 0;
+
+    // Und zurueckgeben
+    return result; 
+  }
+
+  // _set_*-Methode vorhanden? falls ja, aufrufen.i
+  // TODO: Closurecache einfuehren und Funktionaufruf nur noch machen, wenn es
+  // die _set_* auch gibt?
+  if (call_resolved(&result,this_object(),"_set_"+name,Value ))
+        return result;
+
+  // Letzte Moeglichkeit: Muss eine 'normale' Property sein
+  return Set( name, Value, F_VALUE, extern_call() );
+}
+
+
+// Property auslesen unter Verwendung evtl. vorhandener Zugriffsfunktionen
+public mixed QueryProp( string name )
+{
+  closure func;
+  mixed result;
+ 
+  // nur fuer heute
+  if (!objectp(this_object()))
+    return;
+
+  // Query-Methode vorhanden?
+  if ( func = prop[F_QUERY_METHOD][name] )
+  {
+    int flag;
+
+    // Wert als Query-Method? Gleich zurueckgeben ...
+    if (!closurep(func)) return func;
+ 
+    // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene
+    // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das
+    // Objekt existiert auch noch (sonst waere func == 0).
+   
+    // closure_call setzen, falls noch nicht gesetzt
+    if ((flag=closure_call<time()))
+      closure_call = time()+59;
+    
+    // Dann Mal die Closure aufrufen. Bei Fehler selbige loeschen
+    if (catch(result=funcall(func);publish))
+    {
+      prop[F_QUERY_METHOD]-=([name]);
+    }
+    // Wenn closure_call gesetzt wurde, wieder loeschen    
+    if (flag) closure_call = 0;
+    
+    // Und zurueckgeben
+    return result;
+  }
+  
+  // _query_*-Methode vorhanden? falls ja, aufrufen.
+  // TODO: Closurecache und nur rufen, wenn es _query_* auch gibt?
+  if (call_resolved(&result,this_object(),"_query_"+name))
+    return result;
+  
+  // Hilft alles nichts. Es ist eine 'normale' Property ...
+  return prop[F_VALUE][name];
+}
+
+
+// Das gesamte Property-Mapping auf einen Schlag setzen
+public void SetProperties( mapping props )
+{
+  string *names;
+  int i, j, same_object;
+ 
+  // Kein Mapping? Schlecht ...
+  if(!mappingp(props)) return;
+
+  // Setzen wir selber?
+  same_object = (!closure_call &&
+                 (!extern_call()||previous_object()==this_object()||
+                  allowed()));
+  names = m_indices(props);
+  
+  // Das SECURED-Flag darf nur durch das Objekt selber gesetzt werden:
+  // Alle SECURED-Flags aus props loeschen
+  if (!same_object)
+  {
+    j=sizeof(names);
+    while(j--) props[names[j], F_MODE] &= ~SECURED;
+  }
+
+  j=sizeof(names);
+  while(j--)
+  {
+    // Properties, die schon SECURED oder PROTECTED sind, duerfen
+    // nur vom Objekt selber manipuliert werden
+    if (same_object||!(prop[F_MODE][names[j]] & (PROTECTED|SECURED)) )
+    {
+      i=4;
+      while(i--)
+      {
+        if(props[names[j],i])
+          prop[i][names[j]] = props[names[j], i];
+        else
+          prop[i]-=([names[j]]);
+      }
+    }
+  }
+  return;
+}
+
+
+// Ein Mapping mit allen Properties zurueckgeben
+public mapping QueryProperties()
+{
+  mapping props;
+  int i, j;
+  string *names;
+
+  props = m_allocate( 0, 4 );
+  
+  if (pointerp(prop))
+  {
+    i=4;
+    while(i--)
+    {
+      names = m_indices(prop[i]);
+      j=sizeof(names);
+      while(j--) props[names[j], i] = prop[i][names[j]];
+    }
+  }
+  return props;
+}
+
+// Die Properties als urspruengliches Array zurueckgeben
+public mixed *__query_properties()
+{
+  if ( pointerp(prop) )
+    return(deep_copy(prop));
+  else
+    return ({ ([]),([]),([]),([]) });
+}
+
+
+// mapping Properties setzen zum Speichern (per save_object())
+// Aufruf nur aus simul_efun heraus
+public void  _set_save_data(mixed data) { properties = data; }
+
+// mapping Properties zum Restoren zurueckgeben
+public mixed _get_save_data()           { return properties; }
+
diff --git a/std/thing/restrictions.c b/std/thing/restrictions.c
new file mode 100644
index 0000000..e8d0f83
--- /dev/null
+++ b/std/thing/restrictions.c
@@ -0,0 +1,142 @@
+// MorgenGrauen MUDlib
+//
+// thing/restrictions.c -- Gewicht u.ae. eines Objekts
+//
+// $Id: restrictions.c 6809 2008-03-29 21:54:04Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <defines.h>
+#include <properties.h>
+
+#define NEED_PROTOTYPES 
+#include <thing/properties.h>
+
+// Gewicht ist 1kg, wenn nicht anders bestimmt
+protected void create() 
+{  
+  SetProp(P_WEIGHT, 1000);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}     
+
+// P_TOTAL_WEIGHT bei Nicht-Containern auf P_WEIGHT umleiten
+static int _set_weight(int weight) {
+  return SetProp(P_TOTAL_WEIGHT,
+	  Set(P_WEIGHT, weight, F_VALUE) );
+}
+
+// P_X_ATTR_MOD aendern (Attributaenderungen durch Ausruestung)
+static mapping _set_extern_attributes_modifier(mapping xmod) 
+{
+  mapping res;
+
+  // wenn Muell oder ein leeres Mapping uebergeben wurde, wird die prop
+  // geloescht und ggf. das Objekt im Living abgemeldet.
+  if (!mappingp(xmod)) xmod=([]);
+  
+  if (!sizeof(xmod)) {
+ 
+    res=Set(P_X_ATTR_MOD, xmod);
+  
+    // wenn Prop geloescht wird, kann man das Item auch aus der Liste der
+    // Statmodifizierer des Lebewesens austragen.
+    if (living(environment())) {  
+      environment()->deregister_modifier(ME);
+      environment()->UpdateAttributes();
+    }
+  }
+  else {
+    // ok, Prop wird auf nen interessanten Wert gesetzt. ;-)
+
+    // Damit Insert/RemoveSensitiveObject beim Move aufgerufen wird,
+    // muss P_SENSITIVE gesetzt sein
+    if (!QueryProp(P_SENSITIVE)) SetProp(P_SENSITIVE,({}));
+  
+    if (living(environment()) && 
+        (!mappingp(res=QueryProp(P_X_ATTR_MOD)) || !sizeof(res))) {
+      // Wenn dieses Objekt in einem Living ist und bisher die prop nicht
+      // gesetzt war, muss sich das Objekt noch im Living als Statmodifizierer
+      // registrieren.
+      res=Set(P_X_ATTR_MOD, xmod);
+      environment()->register_modifier(ME);
+      environment()->UpdateAttributes(ME);
+    }
+    else if (living(environment())){
+      // sonst reicht ein einfaches UpdateAttributes() um das Lebewesen zu
+      // informieren.
+      res=Set(P_X_ATTR_MOD, xmod);
+      environment()->UpdateAttributes();
+    }
+    else {
+      // wenn kein lebendes Env, reicht einfaches Setzen.
+      res=Set(P_X_ATTR_MOD,xmod);
+    }
+  }
+
+  return res;
+}
+
+// P_X_HEALTH_MOD aendern (LP/KP-Aenderung durch Ausruestung)
+static mapping _set_extern_health_modifier(mapping xmod) {
+  mapping res;
+
+  // wenn Muell oder ein leeres Mapping uebergeben wurde, wird die prop
+  // geloescht und ggf. das Objekt im Living abgemeldet.
+  if (!mappingp(xmod)) xmod=([]);
+  
+  if (!sizeof(xmod)) {
+ 
+    res=Set(P_X_HEALTH_MOD, xmod);
+  
+    // wenn Prop geloescht wird, kann man das Item auch aus der Liste der
+    // Statmodifizierer des Lebewesens austragen.
+    if (living(environment())) {  
+      environment()->deregister_modifier(ME);
+      environment()->UpdateAttributes();
+    }
+  }
+  else {
+    // ok, Prop wird auf nen interessanten Wert gesetzt. ;-)
+
+    // Damit Insert/RemoveSensitiveObject beim Move aufgerufen wird,
+    // muss P_SENSITIVE gesetzt sein
+    if (!QueryProp(P_SENSITIVE)) SetProp(P_SENSITIVE,({}));
+  
+    if (living(environment()) && 
+        (!mappingp(res=QueryProp(P_X_HEALTH_MOD)) || !sizeof(res))) {
+      // Wenn dieses Objekt in einem Living ist und bisher die prop nicht
+      // gesetzt war, muss sich das Objekt noch im Living als Statmodifizierer
+      // registrieren.
+      res=Set(P_X_HEALTH_MOD, xmod);
+      environment()->register_modifier(ME);
+      environment()->UpdateAttributes(ME);
+    }
+    else if (living(environment())){
+      // sonst reicht ein einfaches UpdateAttributes() um das Lebewesen zu
+      // informieren.
+      res=Set(P_X_HEALTH_MOD, xmod);
+      environment()->UpdateAttributes();
+    }
+    else {
+      // wenn kein lebendes Env, reicht einfaches Setzen.
+      res=Set(P_X_HEALTH_MOD,xmod);
+    }
+  }
+
+  return res;
+
+}
+
+// P_X/M_ATTR_MOD werden nicht beruecksichtigt, da CUMULATIVE_ATTR_LIMIT
+// ueberschritten ist
+void NotifyXMAttrModLimitViolation()
+{
+}
+  
diff --git a/std/thing/sockets.c b/std/thing/sockets.c
new file mode 100644
index 0000000..64d9899
--- /dev/null
+++ b/std/thing/sockets.c
@@ -0,0 +1,254 @@
+// MorgenGrauen MUDlib
+/** @file
+* Implementation fuer Sockelitems.
+* Langbeschreibung... TODO
+* @author Zesstra + Arathorn
+* @date xx.05.2008
+* @version $Id$
+*/
+
+/* Changelog:
+*/
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+#define NEED_PROTOTYPES
+#include <thing/sockets.h>
+#undef NEED_PROTOTYPES
+#include <defines.h>
+#include <lpctypes.h>
+
+#ifndef DEBUG
+/** Outputs debug message to Maintainer, if Maintainer is logged in. 
+*/
+#define DEBUG(x) __debug_msg__(x)
+#endif
+
+
+private void __debug_msg__(string x) {
+  if (find_player("zesstra"))
+      tell_object(find_player("zesstra"),"SDBG: "+x+"\n");
+}
+
+/** Setzt \a item in einen passenden Sockel ein.
+  Wird vom Handwerker gerufen. Sofern die Pruefung mittels TestSocketItem()
+  erfolgreich ist, werden die Props dieses Objekt durch die im Item
+  gespeicherten in P_SOCKET_PROPS ergaenzt, ggf. P_RESTRICTION geaendert und
+  Name der Blueprint, Name und P_SOCKET_PROPS in P_SOCKETS gespeichert, um
+  spaeter nachvollziehen zu koennen, wie dieses Objekt bereits modifiziert
+  wurde.
+  @attention Am Ende der Funktion wird \a item zerstoert und darf nicht mehr
+  benutzt werden!
+  @param[in] item Objekt, welches in einen passenden Socket eingesetzt werden
+  soll.
+  @return 1, falls Einsetzen erfolgreich, <0 wenn nicht.
+  @sa TestSocketItem(object)
+ */
+public int MountSocketItem(object item) {
+  if (!objectp(item)) return SOCKET_NO_OBJECT;
+  if ((int res=TestSocketItem(item)) != SOCKET_OK)
+    return res;
+  mapping idata=(mapping)item->QueryProp(P_SOCKET_PROPS);
+  // TODO: Spezialbehandlung fuer Props, bei denen das Objekt erhalten bleiben
+  // muss. (z.B. P_DEFEND_FUNC).
+
+  // zu modifizierende Props addieren
+  foreach(string propname, mixed pval: idata) {
+    SetProp(propname, AddToEntity(QueryProp(propname), pval) );
+  }
+  // Restrictions hinzufuegen.
+  SetProp(P_RESTRICTIONS, 
+      AddToEntity(QueryProp(P_RESTRICTIONS), 
+	item->QueryProp(P_SOCKET_RESTR_USE)) );
+
+  // Daten ueber dieses Socketitem abspeichern.
+  mixed sockets=QueryProp(P_SOCKETS)[item->QueryProp(P_SOCKET_TYPE)];
+  // freier Sockel muss existieren (->TestSocketItem())
+  int index=member(sockets, SOCKET_FREE);
+  sockets[index] = idata + (["BLUE_NAME": load_name(item),
+                           "DESCRIPTION": item->name(WER) ]);
+
+  // ggf. Beschreibung aktualisieren?
+  // ggf. Sonderbehandlung fuer Props, bei denen das Objekt noch gebraucht
+  // wird (z.B. P_DEFEND_INFO)
+
+  // Prop schuetzen, sobald Daten drin stehen.
+  Set(P_SOCKETS, PROTECTED|NOSETMETHOD, F_MODE_AS);
+
+  // item entsorgen
+  if (!item->remove(1))
+    raise_error(sprintf("MountSocketItem() in %O: %O hat Ausfuehrung von "
+	  "remove() verweigert.\n", ME, item));
+  return SOCKET_OK;
+}
+
+/** Prueft, ob \a item in einen Sockel eingesetzt werden kann.
+  Prueft, ob es fuer das Item einen passenden Sockel gibt und ob 
+  es die formalen Anforderungen erfuellt, dort eingesetzt zu werden. 
+  Kann vom Handwerker vor dem echten Einsetzen gerufen werden.
+  Wird auch von MountSocketItem(object) gerufen.
+  @param[in] item Objekt, welches auf Eignung zum Einsetzen in einen Sockel
+  getestet werden soll.
+  @return 1, falls ein passender und freier Sockel existiert, 0 sonst.
+  @sa MountSocket(object)
+*/
+public int TestSocketItem(object item) {
+
+  if (!objectp(item)) return SOCKET_NO_OBJECT;
+  
+  // ist es ein Sockelitem und hat es einen gueltigen Typ?
+  mapping idata = (mapping)item->QueryProp(P_SOCKET_PROPS);
+  if (!mappingp(idata) || !sizeof(idata))
+    return SOCKET_NO_DATA;
+  string styp=(string)item->QueryProp(P_SOCKET_TYPE);
+  if (!stringp(styp)
+      || member(VALID_SOCKET_TYPES, styp) == -1)
+    return SOCKET_INVALID_TYPE;
+
+  // Hat dieses Item ueberhaupt Sockel? Und wenn ja, haben wir nen freien
+  // Socke fuer den betreffenden Typ?
+  mapping mysockets = QueryProp(P_SOCKETS);
+  if (!mappingp(mysockets) || !sizeof(mysockets)) 
+    return SOCKET_NO_SOCKETS;
+  if (!member(mysockets, styp)
+      || !member(mysockets[styp], SOCKET_FREE) == -1 )
+    return SOCKET_NONE_AVAILABLE;
+
+  // Handwerker pruefen.
+  // TODO: Soll die Fehlermeldung komplett vom Aufrufer erledigt werden oder
+  // soll es einen Heinweis geben, warum der Handwerker nicht geeignet ist?
+  object craftsman = previous_object();
+  mapping restr = (mapping)item->QueryProp(P_SOCKET_RESTR_MOUNT);
+  if (!objectp(craftsman)
+      || (mappingp(restr)
+	&& "/std/restriction_checker"->check_restrictions(craftsman,
+	  restr)))
+    return SOCKET_NO_EXPERTISE;
+
+  // da P_RESTRICTION nur beim Anziehen/Zuecken geprueft wird, darf man ein
+  // Item nicht in getragenem Zustand modifizieren.
+  if (objectp((object)item->QueryProp(P_WIELDED))
+      || objectp((object)item->QueryProp(P_WORN)))
+    return SOCKET_ITEM_INUSE;
+
+  return SOCKET_OK;
+}
+
+/** Liefert Infos ueber die Sockets (Typ, was drin ist, etc.\ ).
+  Primaer fuer Gilden gedacht.
+*/
+public mixed GetSocketInfo() {
+}
+
+/* **************************** private ************************* */
+
+/** Addiert zwei Variablen, sofern sie kompatibel sind.
+  Lassen sich die beiden Datentypen nicht sinnvoll addieren, erfolgt keine
+  Addition und \a old wird zurueck geliefert (z.B. bei Objekten, Closures).
+  Hierbei erfolgt z.B. bei Mappings eine wertweise Addition, keine blosse
+  Ersetzung der Keys wie bei der normalen Mapping-Addition.
+  @param[in,out] old Datum, zu dem addiert werden soll. Wird bei Mappings
+  geaendert.
+  @param[in] new Datum, welches addierten soll.
+  @return Summe von old und new, falls die Datentypen kompatibel sind. Falls
+  nicht, kann old zurueckgegeben werden. Der Datentyp von 
+  @attention \b Rekursiv! Kann teuer werden.\n
+    Kann (modifizierte) \a old oder \a new oder ganz neues Datum 
+    zurueckliefern, d.h. der zurueckgelieferte Datentyp muss nicht dem
+    Datentyp von old entsprechen. Ebenso erzeugt z.B. die Addition zweier
+    Arrays ein neues Array.
+    Wenn die korrespondierenden Werte nicht den gleichen Datentyp haben,
+    findet u.U. keine Addition statt oder es wird eine Datentyp-Umwandlung
+    durchgefuehrt, z.B. Int + Float == Float.\n
+  */
+private mixed AddToEntity(mixed old, mixed new) {
+
+  // einfachste Faelle:
+  if (!old)
+    return new;
+  else if (!new)
+    return old;
+
+  // Typ bestimmen
+  int oldtype = typeof(old);
+  int newtype = typeof(new);
+  // Variablen gleichen Typs sind einfach.
+  if (oldtype == newtype) {
+    switch (oldtype) {
+      // einige Typen werden stumpf addiert.
+      case T_NUMBER:
+      case T_STRING:
+      case T_FLOAT:
+      case T_POINTER: // TODO: anderes Verhalten?
+	return old+new;
+      // Mappings werden wertweise addiert.
+      case T_MAPPING:
+	// nur wenn die Breite des 2. Summanden <= der des 1. Summanden ist,
+	// lassen sich Mappings hiermit addieren.
+	if (widthof(new) > widthof(old))
+	  return old;
+	// new auf old addieren. Keys werden nicht ersetzt, sondern nach
+	// Moeglichkeit die werte unter den keys jeweils addiert.
+	// map() nur zum Uebergeben aller Keys+Value. AddToMapping aendert
+	// direkt das uebergebene Mapping old.
+	map(new, #'AddToMapping, old); 
+	// alles hier nicht aufgefuehrte kann nicht addiert werden.
+      default: return old;
+    }
+  }
+  // Ints und Floats sind auch noch gut addierbar.
+  else if ((oldtype == T_FLOAT && newtype == T_NUMBER) ||
+           (oldtype == T_NUMBER && newtype == T_FLOAT) )
+    return old + new;
+  // Arrays lassen sich auch gut verwursten (new kann kein Array sein).
+  // Umgekehrter Fall waere auch meoglich, aber der Datentyp von old waere
+  // sehr deutlich unterschiedlich vom urspruenglichen.
+  else if (oldtype == T_POINTER)
+    return old+({new});
+  // Strings und Zeichenliterale (Ints) sind Ansichtssache.
+  else if (oldtype == T_STRING && newtype == T_NUMBER)
+    return sprintf("%s%c",old,new);
+  else if (oldtype == T_NUMBER && newtype == T_STRING)
+    return sprintf("%c%s",old,new);
+
+  // Fall-through
+  return old;
+}
+
+/** Addiert einen Schluessel und seine Werte zu einem Mapping, ggf.\ auf die
+ * bestehenden Werte des Mappings \a old.
+ * Der Key und seine Werte ersetzen bestehende Werte in \a old nicht, wie es
+ * die normale Mapping-Addition des Driver macht. Stattdessen wird versucht,
+ * die neuen Werte auf die entsprechenden alten Werte zu addieren. Falls die
+ * Datentypen zweier Werte inkompatibel sind, erfolgt keine Addition und der
+ * alte Werte hat Bestand.
+  @param[in] key (mixed) Wert, der in das Mapping addiert wird.
+  @param[in] values (mixed)
+  @param[in,out] old Mapping, in das addiert wird.
+  @attention Die Breites des 2. Summanden darf \b nicht groesser sein als die
+  Breite des 1, Summanden! \n
+    Die korrespondieren Werte sollten den gleichen Datentyp haben, sonst
+    findet u.U. keine Addition statt oder es wird eine Datentyp-Umwandlung
+    durchgefuehrt, z.B. Int + Float == Float.\n
+  */
+private void AddToMapping(mixed key, mixed values, mapping old) {
+  if (!mappingp(old)) return;
+  if (!pointerp(values)) values = ({values});
+
+  // wenn der Key noch nicht existiert, ists einfach.
+  if (!member(old, key))
+    m_add(old, key, values...); // flatten operator ... cool. ;-)
+  else {
+    // sonst muessen wir teure handarbeit machen. Insb. weil die Values einen
+    // beliebigen Datentyp haben koennen, u.a. Mappings, weswegen wir wieder
+    // rekursiv AddToEntity() rufen muessen.
+    for(int i=sizeof(values) ; i-- ; ) {
+      old[key,i] = AddToEntity(old[key,i], values[i]);
+    }
+  }
+}
+
diff --git a/std/thing/util.c b/std/thing/util.c
new file mode 100644
index 0000000..d434642
--- /dev/null
+++ b/std/thing/util.c
@@ -0,0 +1,111 @@
+// MorgenGrauen MUDlib
+//
+// thing/util.c -- Utilities
+//
+// $Id: util.c 6366 2007-07-15 21:06:24Z Zesstra $
+
+#define NEED_PROTOTYPES
+#include "/sys/thing/util.h"
+#include "/sys/thing/properties.h"
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+public void ShowPropList(string *props) 
+{
+  int i,j;
+
+  j=sizeof(props);
+
+  for ( i=0; i<j ; i++) 
+  {
+    write("*"+props[i]+": ");
+    PrettyDump(QueryProp(props[i]));
+    write("\n");
+  }
+}
+
+static void PrettyDump(mixed x) 
+{
+  if (pointerp(x)) 
+  {
+    DumpArray(x);
+  }
+  else if (mappingp(x))
+  {
+    DumpMapping(x);
+  }
+  else if (objectp(x)) 
+  {
+    write ("OBJ("+object_name(x)+")");
+  }
+  else if (stringp(x))
+  {
+    write("\""+x+"\"");
+  }
+  else
+  {
+    write (x);
+  }
+}
+
+static void DumpArray(mixed *x) 
+{
+  int i,j;
+
+  write ("({ ");
+  if ( (j=sizeof(x))>0 )
+  {
+    for ( i=0 ; i<(j-1) ; i++) 
+    {
+      PrettyDump(x[i]);
+      write(", ");
+    }
+    PrettyDump(x[i]);
+    write(" ");
+  }
+  write ("})");
+}
+
+static void DumpMapping(mapping x)
+{
+  int   i, c, s;
+  mixed *ind;
+
+  write("([ ");
+
+  if ( (c=sizeof(ind=m_indices(x)))<1 )
+  {
+    write(" ])");
+    return;
+  }
+
+  s=get_type_info(x,1);
+
+  DumpKeyValPair(x, ind[0], s);
+  for ( i=1 ; i<c ; i++ )
+  {
+    write(", ");
+    DumpKeyValPair(x, ind[i], s);
+  }
+  write(" ])");
+}
+
+// Lacht nicht ueber den Namen!!! -Boing
+// Nein, ueber den Namen lache ich nicht ... -Paracelsus
+static void DumpKeyValPair(mapping x, mixed key, int size)
+{ int j, vc;
+
+  PrettyDump(key);
+  write(" : ");
+  PrettyDump(x[key,0]);
+
+  for ( j=1; j<size; j++)
+  {
+    write("; ");
+    PrettyDump(x[key, j]);
+  }
+}
diff --git a/std/trading_price.c b/std/trading_price.c
new file mode 100644
index 0000000..4bfc96c
--- /dev/null
+++ b/std/trading_price.c
@@ -0,0 +1,174 @@
+// MorgenGrauen MUDlib
+//
+// trading_price.c -- Preisverhalten von Laeden und Haendlern
+//
+// $Id: trading_price.c,v 3.3 2002/06/09 19:51:40 Tilly Exp $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <bank.h>
+
+#undef NEED_PROTOTYPES
+#include <properties.h>
+
+// TODO: langfristig zu private aendern?
+nosave int buyfact, shop_percent_left, mymoney, wantto, last;
+private nosave int wantthresh, maxnum;
+
+varargs int SetTradingData(int money, int factor, int maxobs)
+{
+  if (extern_call() && previous_object() != this_object())
+    log_file("ZENTRALBANK",sprintf("INITIALIZE: %O got (%d,%d,%d) from %O (%O)\n",
+				   this_object(), money,factor,maxobs,
+				   previous_object(),this_player()));
+
+  if (money < 1000)
+    money = 1000;
+
+  last=wantto=mymoney=money;
+  wantthresh=wantto/2;
+  if (wantthresh < 5000)
+    wantthresh = 5000;
+
+  if (factor < 100)
+    buyfact = 300;
+  else
+    buyfact = factor;
+  
+  if (maxobs < 1)
+    maxnum=3;
+  else
+    maxnum=maxobs;
+  
+  return mymoney;
+}
+
+void SetBuyFact(int i)
+{
+  buyfact=i*100;
+}
+
+varargs int QueryBuyFact(object client)
+{
+  return buyfact;
+}
+
+int InflateSellValue(int value, int cnt)
+{
+  cnt-=maxnum;
+  if (cnt<0) cnt=0;
+  return value-(value*cnt)/(cnt+25);
+}
+
+int TruncateSellValue(int value)
+{
+  if (value > 500) {
+    value-=500;
+    value = ((1000*value)/(value+1000))+500;
+  }
+  return value;
+}
+
+int ObjectCount(object ob)
+{
+  return objectp(ob); // 0 oder 1
+}
+
+varargs int QueryValue(object ob, int value, object client)
+{
+  value=TruncateSellValue(InflateSellValue(value,ObjectCount(ob)));
+  if (3*value>mymoney)
+  {
+    log_file("LADEN",sprintf("Out of money: %O for %O (%s)\n",this_object(),
+			     this_player(),dtime(time())[5..]));
+    value=mymoney/3;
+  }
+  if (value<0) value=0;
+  
+  /*   Preisverfall von beschaedigtenObjekten gleich hier festlegen  */
+
+  if(value && ob->QueryProp(P_DAMAGED))  
+  {                                      
+    if(ob->QueryProp(P_WC) || ob->QueryProp(P_AC))
+    {
+      value = (6 * (value / 10));
+    }
+    else
+    {
+      value = (4 * (value / 10));
+    }
+  }
+                                          
+  return value;
+}
+
+varargs int QuerySellValue(object ob, object client)
+{
+  return QueryValue(ob,ob->QueryProp(P_VALUE),client);
+}
+
+varargs int QueryBuyValue(mixed ob, object client)
+{
+  // Mit Runden.
+  return (ob->QueryProp(P_VALUE)*QueryBuyFact(client) + 50)/100;
+}
+
+protected void create()
+{
+  SetProp(P_SHOP_PERCENT_LEFT, 0);
+  SetTradingData(50000,300,3);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset()
+{
+  SetProp(P_SHOP_PERCENT_LEFT, 0);
+
+  if (last/3>mymoney)
+    wantto=wantto*110/100;
+  else
+    if (last/2<mymoney)
+      wantto=wantto*85/100;
+  if (wantto<wantthresh) wantto=wantthresh+500;
+  if (wantto>1500000) wantto=1500000;
+  if (wantto<mymoney)
+  {
+    ZENTRALBANK->PayIn(mymoney-wantto);
+    mymoney=wantto;
+  } else
+    mymoney+=ZENTRALBANK->WithDraw(wantto-mymoney);
+  last=mymoney;
+}
+
+void _add_money(int i) {
+  if (extern_call() && previous_object() != this_object())
+    log_file("ZENTRALBANK",sprintf("%s: TRANSFER: %O got %d from %O\n",
+	strftime("%H:%M"), this_object(), i, previous_object()));
+  mymoney+=i;
+}
+
+int _set_shop_percent_left(int dummy) {
+  return shop_percent_left=ZENTRALBANK->_query_shop_percent_left();
+}
+
+int _query_shop_percent_left() {
+  return shop_percent_left;
+}
+
+int _query_current_money() {
+  return mymoney;
+}
+
+int _query_wantto() {
+  return wantto;
+}
+
+
diff --git a/std/transport.c b/std/transport.c
new file mode 100644
index 0000000..fffe1e0
--- /dev/null
+++ b/std/transport.c
@@ -0,0 +1,521 @@
+// MorgenGrauen MUDlib
+//
+// transport.c -- Basisklasse fuer Schiffe und aehnliche Transporter
+//
+// $Id: transport.c 9400 2015-12-11 21:56:14Z Zesstra $
+#pragma strong_types
+//#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/moving";
+inherit "/std/room";
+
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+#include <language.h>
+#include <transport.h>
+#include <regexp.h>
+
+/* transport.c
+ * 
+ * Ueberarbeitete und 
+ * erweiterte Version : Tilly@MorgenGrauen, 10.01.02
+ * Basierend auf      : transport.c@SilberLand (Woody@SilberLand), 05.12.99
+ * Basierend auf      : Hates und Rumatas generisches Transport Objekt
+ *                      MorgenGrauen 15.02.93
+ */
+     
+/*
+ ********************* Variablen *********************
+ */
+
+// TODO: langfristig waer ja private schoen...
+//
+// Datenstruktur von 'route' (bei HP_ROOM)
+// 0 ({string ID,      : HP_ROOM
+// 1   string room,    : Dateiname Zielraum
+// 2   int stay,       : Dauer Haltezeit
+// 3   int next,       : Dauer naechste Fahrtzeit
+// 4   string code,    : Haltestellenname fuer QueryArrived
+// 5   mixed dest,     : Haltestellen-IDs fuer HasRoute (reise nach)
+// 6   mixed deststr }): unbenutzt.
+//
+// Datenstruktur von 'route' (bei HP_MSG, HP_FUN)
+// 0 ({string ID,      : HP_MSG
+// 1   string message, : Meldung    oder    string fun : Funktionsname
+// 2   int next})      : Dauer bis zum naechsten Ereignis
+nosave mixed *route;    /* Liste der Haltepunkte. */
+nosave int rpos;        /* Momentane Position in obiger Liste. */
+nosave string roomCode; /* Code des aktuellen Raumes (oder 0). */
+
+/*
+ ********** Management der builtin-properties **********
+ */
+
+string _query_short()
+{ 
+  if (roomCode) return Query(P_SHORT); 
+  return 0; 
+}
+
+mixed _query_transparent()
+{ 
+  if (roomCode) return Query(P_TRANSPARENT);
+  return 0; 
+}
+
+mixed *_set_route(mixed *r) { return route = r; }
+mixed *_query_route()       { return route; }
+
+/*
+ **************** Zugriffsfunktionen ***************
+ */
+
+void Halt()
+{
+  while (remove_call_out( "changeHp" )>-1);
+  while (remove_call_out( "disconnect" )>-1);
+}
+
+// Aktualisiert/Setzt die Route im TravelD, wenn erlaubt (d.h. kein
+// P_NO_TRAVELING)
+private void ReportRoute()
+{
+  if(!QueryProp(P_NO_TRAVELING))
+  {
+    mixed tmp = filter(route, function int (mixed arr)
+        {
+          return arr[0] == HP_ROOM;
+        } );
+    string *route = map(tmp, function string (mixed arr)
+        { return arr[1]; }
+        );
+    TRAVELD->AddRoute(object_name(this_object()),route);
+  }
+}
+
+varargs void Start(int pos)
+{
+  Halt();
+  rpos = (pos >= sizeof(route))?-1:pos-1;
+  call_out("changeHp",0);
+  // Tell TRAVELD our current route
+  ReportRoute();
+}
+
+void SetTravelCmds()
+{
+  if (pointerp(QueryProp(P_LEAVECMDS)))
+     AddCmd(QueryProp(P_LEAVECMDS),"GoOutside");
+  if (pointerp(QueryProp(P_ENTERCMDS)))
+    AddCmd(QueryProp(P_ENTERCMDS),"GoInside");
+  if (pointerp(QueryProp(P_TRAVEL_CMDS)))
+    AddCmd(QueryProp(P_TRAVEL_CMDS),"GoInAndOutside");
+  return;
+}
+
+mixed HasRoute(mixed dest)
+{
+  int i,s,z;
+  string str;
+  object ob;
+  mixed harb;
+  
+  s = sizeof(route);
+
+  for (i = rpos;i <= rpos+s-1;i++)
+  {
+    if (route[i%s][0] == HP_ROOM)
+    {
+      if (member(route[i%s][5],dest) != -1 &&
+          objectp(ob=load_object(route[i%s][1])) &&
+          pointerp(harb=ob->QueryProp(P_HARBOUR)) &&
+          sizeof(harb))
+      {
+        return ({ route[i%s][1], harb[0] });
+      }
+    }
+  }
+  return 0;
+}
+
+public varargs void AddRoute(string room, int stay, int next, 
+    string harbour_desc, string|string* dest_ids, string deststr)
+{
+  // Daten aus dem Zielanleger abfragen.
+  <string|string*>* harbour = room->QueryProp(P_HARBOUR)||({});
+  string* harbour_ids = ({});
+
+  // IDs des Zielanlegers fuer Syntaxpruefung 
+  if ( sizeof(harbour)==2 )
+  {
+    if ( pointerp(harbour[1]) )
+      harbour_ids = harbour[1];
+    else
+      harbour_ids = ({harbour[1]});
+  }
+  
+  // <dest_ids> in ein Array umwandeln, ist dann ggf. leer
+  if ( !dest_ids )
+  {
+    dest_ids = ({});
+  }
+  if ( stringp(dest_ids) )
+  {
+    dest_ids = ({dest_ids});
+  }
+
+  // explizit angegebene IDs stehen jetzt in <dest_ids>, die IDs des 
+  // Zielhafens aus P_HARBOUR werden addiert.
+  dest_ids += harbour_ids;
+
+  // Ist <dest> immer noch leer, versuchen wir, aus <harbour_desc> ein paar
+  // Stichwoerter zu erzeugen, die man als Zielangabe in der Syntax 
+  // "reise nach <ziel>" verwenden kann.
+  if ( !sizeof(dest_ids) )
+  {
+    // Grossgeschriebene Begriffe in <harbour_desc> in <dest> eintragen. Dazu:
+    // 1) <code> erstmal so zerschneiden, dass alle ueblichen Satzzeichen
+    // rausfliegen (es gibt Transporter, die sowas in <harbour_desc> 
+    // uebergeben).
+    dest_ids = regexplode(harbour_desc, "[(),.;:&\+_ ]", 
+                          RE_OMIT_DELIM|RE_GLOBAL);
+    // 2a) So filtern, dass nur grossgeschriebene Woerter uebrig bleiben,
+    //     von 1) uebriggebliebene Leerstrings gleich mit wegwerfen.
+    // 2b) Ergebnis kleinschreiben, damit die Syntaxpruefung damit arbeiten
+    //     kann.
+    dest_ids = map( filter(dest_ids, function int (string key) { 
+                  return (key!="" && key[0]>='A' && key[0]<='Z');
+               }), #'lower_case);
+  }
+  // Sollte <dest> jetzt immer noch leer sein, wurde an allen drei Stellen
+  // nichts oder nur Muell uebergeben.
+  if ( !sizeof(dest_ids) )
+  {
+    raise_error("Transporterfehlfunktion in AddRoute(): Identifikations"
+      "matrix unzureichend definiert. Transporter unbenutzbar fuer "
+      "Spieler. Bitte mindestens eine Ziel-ID via P_HARBOUR oder als "
+      "Argument to AddRoute().");
+  }
+  route += ({ ({ HP_ROOM, room, stay, next, harbour_desc, dest_ids, 
+                 deststr }) });
+}
+
+varargs void AddMsg(string msg, int next) 
+{
+  route += ({ ({ HP_MSG, msg, next }) }); 
+}
+
+void AddFun(string fun, int next) { route += ({ ({ HP_FUN, fun, next }) }); }
+
+string QueryArrived() { return roomCode; }
+
+mixed* QueryPosition()
+{
+  return ({ route[rpos][1],route[(rpos+1)<sizeof(route)?(rpos+1):0][1] });
+}
+
+object* QueryPassengers()
+{
+  return filter(all_inventory(),#'query_once_interactive); 
+}
+
+varargs string *QueryHarbours(int textflag)
+{
+  string *ret = ({});
+
+  foreach( mixed* entry : route )
+  {
+    if ( entry[0] == HP_ROOM )
+    {
+      if ( textflag )
+      {
+        string *hp_ids = entry[1]->QueryProp(P_HARBOUR)[1];
+        if (pointerp(hp_ids) && sizeof(hp_ids))
+        {
+          string *h = map( explode(hp_ids[0]," "), #'capitalize);
+          ret += ({ implode(h, " ") });
+        }
+      }
+      else
+      {
+        ret += ({ entry[1] });
+      }
+    }
+  }
+  return ret;
+}
+
+// beim zerstoeren sollte auch die route und der Transporter aus dem traveld
+// abgemeldet werden.
+public varargs int remove(int silent)
+{
+  TRAVELD->RemoveTransporter(this_object());
+  return ::remove(silent);
+}
+
+void RemoveRoute()
+{
+  Halt();
+  route = ({ });
+  rpos  =   0;
+  TRAVELD->RemoveTransporter(this_object());
+}
+
+varargs int Enter(object who)
+{
+  string *emsg;
+  mixed efail;
+
+  if (!objectp(who)) who = this_player();
+  if (environment(who) == this_object())
+  {
+    tell_object(who,"Da bist Du doch bereits, schon vergessen?\n");
+    return 1;
+  }
+  if (!QueryArrived()) return 0;
+  if (QueryProp(P_MAX_PASSENGERS) && 
+     (sizeof(QueryPassengers()) >= QueryProp(P_MAX_PASSENGERS)))
+  {
+    if (pointerp(efail=QueryProp(P_ENTERFAIL)))
+    {
+      if (sizeof(efail) == 2)
+        tell_room(this_object(),who->Name(WER,2)+" "+process_string(efail[1])+
+                                                   ".\n",({who}));      
+      tell_object(who,process_string(efail[0])+".\n");
+    }
+    else if (stringp(efail))
+       tell_object(who,process_string(efail)+".\n");
+    else if (closurep(efail)) funcall(efail);
+    return 1;
+  }
+  
+  tell_object(who,"Du betrittst "+name(WEN,1)+".\n");
+  if (pointerp(emsg=QueryProp(P_ENTERMSG)) && sizeof(emsg) == 2)
+     return who->move(this_object(),M_GO,"",process_string(emsg[0]),
+                                             process_string(emsg[1]));
+  return who->move(this_object(),M_GO,
+        name(WEN,1),"betritt","kommt herein");
+}
+
+varargs int Leave(object who)
+{
+  string *lmsg;
+  mixed lfail;
+
+  if (!objectp(who)) who = this_player();
+  if (environment(who) != this_object())
+  {
+    if (QueryArrived())
+    {
+      tell_object(who,"Dafuer muesstest Du erstmal dort sein.\n");
+      return 1;
+    }
+    return 0;
+  }
+  if (!QueryArrived())
+  { 
+    if (lfail=QueryProp(P_LEAVEFAIL))
+    {
+      if (pointerp(lfail) && sizeof(lfail))
+      {
+        if (sizeof(lfail) == 2)
+           tell_room(this_object(),who->Name(WER,2)+" "+process_string(
+               lfail[1])+".\n",({who}));
+        tell_object(who,process_string(lfail[0])+".\n");
+      }
+      else if (stringp(lfail))
+        tell_object(who,process_string(lfail)+".\n");
+      else if (closurep(lfail)) funcall(lfail);
+      return 1;
+    }
+    tell_object(who,"Fehler beim Verlassen des Transporters.\n"
+                    "Bitte zustaendigen Magier verstaendigen.\n");
+    return 1;
+  }
+
+  if (who->QueryProp(P_TRAVEL_INFO)) who->SetProp(P_TRAVEL_INFO,0);
+  tell_object(who,"Du verlaesst "+name(WEN,1)+".\n");
+  if (pointerp(lmsg=QueryProp(P_LEAVEMSG)) && sizeof(lmsg) == 2)
+      return who->move(environment(),M_GO,"",process_string(lmsg[0]),
+                                             process_string(lmsg[1]));
+  return who->move(environment(),M_GO,
+           name(WEN,1),"verlaesst","kommt herein");
+}
+
+/*
+ ****************** Internal Functions ******************
+ */
+
+static int GoInside(string str)
+{
+  _notify_fail("Was moechtest Du denn genau?\n");
+  if (stringp(str) && id(str)) {
+      Enter();
+      return 1;
+  }
+  return 0;
+}
+
+static int GoOutside(string str)
+{
+  _notify_fail("Was moechtest Du denn genau?\n");
+  if (stringp(str) && id(str)) {
+      Leave();
+      return 1;
+  }
+  return 0;
+}
+
+static int GoInAndOutside(string str)
+{
+  string to;
+
+  _notify_fail("Was moechtest Du denn genau?\n");
+  if (!sizeof(str)) return 0;  
+  if ((sscanf(str,"auf %s",to) == 1 || sscanf(str,"in %s",to) == 1) && id(to))
+    return Enter(),1;
+  if ((sscanf(str,"von %s",to) == 1 || sscanf(str,"aus %s",to) == 1) && id(to))
+    return Leave(),1;
+  return 0;
+}
+
+protected void create()
+{
+  ::create();
+
+  route = ({});
+
+  SetProp(P_LEAVEFAIL,"Das ist momentan viel zu gefaehrlich");
+  SetProp(P_ENTERFAIL,"Dort ist kein Platz mehr fuer Dich");
+  SetProp(P_TRANSPARENT,1);
+
+  AddId("Transporter");
+  
+  call_out("SetTravelCmds",1);
+}
+
+static varargs void disconnect(int change, int change_time)
+{
+  object room;
+  mixed *departmsg;
+
+  departmsg = QueryProp(P_DEPARTMSG);
+
+  if ((room = environment()) && pointerp(departmsg))
+  {
+    tell_room(this_object(),process_string(departmsg[0]));
+    tell_room(room,process_string(departmsg[1]));
+  }
+
+  roomCode = 0;
+
+  if (change) call_out("changeHp",change_time);
+}
+
+static varargs void connect(string room, string code)
+{
+  mixed *arrivemsg, *t;
+  object *trav, ob;
+  string *trs, *msgs;
+  int i;
+
+  if (roomCode) disconnect();
+  
+  roomCode = code?code:"";
+
+  if (catch(move(room,M_SILENT|M_NOCHECK);publish))
+  {
+    roomCode = 0;
+    return;
+  }
+
+  arrivemsg = QueryProp(P_ARRIVEMSG);
+
+  if (pointerp(arrivemsg))
+  {
+    tell_room(this_object(),process_string(arrivemsg[0]));
+    tell_room(room,process_string(arrivemsg[1]));
+  }
+
+  trav = filter(all_inventory(this_object()),#'living);
+
+  i = sizeof(trav);
+  while(i--)
+  {
+    if (pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
+        t[0]==this_object()&&t[2]==room)
+    { 
+      if (trav[i]->InFight())
+        tell_object(trav[i],break_string("Du solltest Deinen Kampf "
+                "schnell beenden,denn eigentlich wolltest Du hier "
+                "aussteigen.",78));
+      else
+        Leave(trav[i]);
+      if (environment(trav[i])!=this_object())
+        trav[i]->SetProp(P_TRAVEL_INFO,0);
+    }
+  }
+  trav = filter(all_inventory(find_object(room))-trav,#'living);
+  i=sizeof(trav);
+  while(i--)
+  {
+    if (objectp(trav[i]) && pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
+        t[0] == environment(trav[i]) && t[1] == this_object())
+    {
+      if ( trav[i]->InFight() )
+        tell_object(trav[i],
+           break_string("Du solltest Deinen Kampf schnell beenden, denn "
+                        "eigentlich wolltest Du mit "+name(WEM,1)+
+                        " reisen.",78));
+      else
+        Enter(trav[i]);
+      if (environment(trav[i]) == this_object()) 
+      {
+        t[0] = this_object();
+        trav[i]->SetProp(P_TRAVEL_INFO,t);
+      }
+    }
+  }
+}
+
+// this object never performs any clean-up, the driver should not call it
+// again.
+int clean_up(int arg) { return 0; }
+
+void changeHp()
+{
+  if (++rpos == sizeof(route))
+  {
+    rpos = 0;
+    //TRAVELD die aktuelle Route uebermitteln
+    ReportRoute();
+  }
+  if (route[rpos][0] == HP_MSG)
+  {
+    call_out("changeHp",route[rpos][2]);
+    tell_room(this_object(),route[rpos][1]);
+  }
+  else if (route[rpos][0] == HP_FUN)
+  {
+    call_out("changeHp",route[rpos][2]);
+    call_other(this_object(),route[rpos][1]);
+  }
+  else 
+  {
+    call_out("disconnect",route[rpos][2],1,route[rpos][3]);
+    connect(route[rpos][1],route[rpos][4]);
+  }
+}
+
+void __restart(string funname)
+{
+  if (!funname || funname == "" || (funname != "changeHp" && 
+                                    funname != "disconnect"))
+    return;
+  while(remove_call_out(funname) != -1);
+  call_out(funname,funname == "changeHp"?15:5);
+}
diff --git a/std/tray.c b/std/tray.c
new file mode 100644
index 0000000..df47955
--- /dev/null
+++ b/std/tray.c
@@ -0,0 +1,60 @@
+// MorgenGrauen MUDlib               
+//
+// tray.c -- container standard object
+//
+// $Id: tray.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+// The most general object class. It defines the really basic functions.
+//
+// This object should understand the properties short, long, info, read_msg,
+// value, weight.
+//
+// weight is given in grams, 1 kilogram (kg) = 1000 grams (g) = 1 old weight
+// unit
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/properties";
+inherit "/std/thing/moving";
+inherit "/std/thing/commands";
+inherit "/std/thing/language";
+inherit "/std/container/light";
+inherit "/std/container/restrictions";
+inherit "/std/tray/description";
+inherit "/std/thing/envchk";
+
+#include <properties.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+protected void create()
+{
+  properties::create();
+  commands::create();
+  light::create();
+  description::create();
+  restrictions::create();
+  envchk::create();
+  SetProp(P_TRAY,1);
+  SetProp(P_PREPOSITION, "auf");
+  SetProp(P_SOURCE_PREPOSITION, "von");
+  SetProp(P_DEST_PREPOSITION, "auf");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+/*
+void init() 
+{
+  commands::init();
+  description::init();
+}
+*/
+
+void reset()
+{}
diff --git a/std/tray/description.c b/std/tray/description.c
new file mode 100644
index 0000000..9079712
--- /dev/null
+++ b/std/tray/description.c
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// tray/description.c -- standard description for containers
+//
+// $Id: description.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "std/container/description";
+
+//#define NEED_PROTOTYPES
+
+#include <container.h>
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <language.h>
+
+void create()
+{ 
+  ::create();
+  AddId("tray");
+}
+
+varargs string long(int mode) {
+  string descr, inv_descr;
+
+  descr = process_string(QueryProp(P_LONG));
+  inv_descr = make_invlist(PL, all_inventory(ME), mode );
+  if ( inv_descr != "" )
+    descr += "Auf " + QueryPronoun(WEM) + " liegt:\n" + inv_descr;
+  return descr;
+}
diff --git a/std/unit.c b/std/unit.c
new file mode 100644
index 0000000..ae715f2
--- /dev/null
+++ b/std/unit.c
@@ -0,0 +1,771 @@
+// MorgenGrauen MUDlib
+//
+// unit.c -- Basisklasse fuer Mengenobjekte
+//           (neue Version von Padreic)
+//
+// $Id: unit.c 9077 2015-01-16 23:26:00Z Zesstra $
+#pragma strong_types,save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing";
+
+#define NEED_PROTOTYPES
+#include <unit.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <thing/properties.h>
+#include <defines.h>
+#include <language.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <debug_info.h>
+
+// zum debuggen und extra public
+public string debugmode;
+public string __set_debug(string recv) {return debugmode=recv;}
+#include <living/comm.h>
+#define ZDEBUG(x) if (stringp(debugmode) && find_player(debugmode)) \
+  find_player(debugmode)->ReceiveMsg(x,MT_DEBUG,0,object_name()+": ",ME)
+//#define ZDEBUG(x)
+
+private void _call_DoDecay(object *klone);
+
+private nosave string lastverb;
+private nosave int lastevalnumber;
+
+protected void create()
+{
+  ::create();
+  SetProp(U_IDS,({({}),({})}));
+  SetProp(P_GENDER, 1);
+  SetProp(P_AMOUNT, 1);
+  // Props sollten nicht direkt manipuliert werden, nur ueber Set-Methoden
+  Set(P_UNIT_DECAY_INTERVAL, PROTECTED, F_MODE_AS);
+  Set(P_UNIT_DECAY_QUOTA, PROTECTED, F_MODE_AS);
+} 
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+//// Query und Set Funktionen fuer die Propertys
+
+static void check_leave()
+{  if (Query(P_AMOUNT,F_VALUE)<1) remove(1);  }
+
+static int _query_invis()
+{
+  if (QueryProp(P_AMOUNT) < 1)
+    return 1;
+  return Query(P_INVIS, F_VALUE);
+}
+
+static int _set_amount(int am)
+{
+  if (am<1)
+    call_out("check_leave",1);
+  SetProp(U_REQ, am);
+  return Set(P_AMOUNT,am, F_VALUE);
+}
+
+static int _set_u_req(int ureq)
+{
+  lastverb = query_verb();
+  lastevalnumber = debug_info(DINFO_EVAL_NUMBER);
+  return Set(U_REQ, ureq, F_VALUE);
+}
+
+static int _query_u_req()
+{
+  // Zwei Dinge benutzten, um zu entscheiden, ob U_REQ noch gueltig ist oder
+  // besser auf P_AMOUNT zurueckgesetzt werden sollte: die DINFO_EVAL_NUMBER
+  // und das Kommandoverb. Wenn eines von beiden sich geaendert hat, wird
+  // U_REQ als ungueltig betrachtet. (Und dies bleibt ein uebler Hack.)
+  if (lastevalnumber != debug_info(DINFO_EVAL_NUMBER)
+      || lastverb != query_verb())
+  {
+    return SetProp(U_REQ, Query(P_AMOUNT, F_VALUE));
+  }
+  return Query(U_REQ, F_VALUE);
+}
+
+// Gesamtwert der gerade mit U_REQ ausgewaehlten unitobjekte
+static int _query_value()
+{
+  mixed cpu;
+  cpu=Query(U_CPU);
+  if (intp(cpu)) return QueryProp(U_REQ) * cpu;
+  if (!pointerp(cpu)) return 0;
+  return (QueryProp(U_REQ) * cpu[0])/cpu[1];
+}
+
+static int _set_value(int num) {
+// Setzt den Wert fuer die derzeitige Anzahl
+  SetCoinsPerUnits(num, QueryProp(U_REQ));
+  return QueryProp(P_VALUE);
+}
+
+static int _query_weight()
+{
+  mixed gpu, req;
+  if ((req=QueryProp(U_REQ))<1) return 0;
+  gpu=Query(U_GPU);
+  if (intp(gpu)) return req * gpu;
+  if (!pointerp(gpu)) return 0;
+  return MAX(1,(req*gpu[0])/gpu[1]);
+}
+
+static int _set_weight(int num) {
+// Setzt das Gewicht fuer die derzeitige Anzahl
+    SetGramsPerUnits(num, QueryProp(U_REQ));
+    return QueryProp(P_WEIGHT);
+}
+
+static int _query_total_weight()
+{
+  mixed gpu, amount;
+  if ((amount=Query(P_AMOUNT))<1) return 0;
+  gpu=Query(U_GPU);
+  if (intp(gpu))
+    return amount*(int)gpu;
+  if (!pointerp(gpu)) return 0;
+  return MAX(1,(amount*gpu[0])/gpu[1]);
+}
+
+static int _query_plural()
+{
+  int i;
+  i=QueryProp(U_REQ); // wichtig _nicht_ QueryProp
+  return (i<=1 ? 0 : i);
+}
+
+// gibt string | string* zurueck.
+static string|string* _query_name()
+{
+  if (QueryProp(U_REQ)==1)
+    return "%s%s"+((string *)Query(P_NAME))[0];
+  return "%d %s"+((string *)Query(P_NAME))[1];
+}
+
+// gibt string | string* zurueck.
+static string|string*  _set_name(mixed names)
+{
+  if(!names)
+    return Set(P_NAME,({"",""}));
+  if(stringp(names))
+    return Set(P_NAME,({names,names}));
+  if(pointerp(names))
+    switch(sizeof(names)) {
+      case 0:  return Set(P_NAME,({"",""}));
+      case 1:  if(!stringp(names[0]))
+                 names[0]="";
+               return Set(P_NAME,({names[0],names[0]}));
+      default: if(!stringp(names[0]))
+                 names[0]="";
+               if(!stringp(names[1]))
+                 names[1]="";
+               return Set(P_NAME,names[0..1]);
+    }
+  return QueryProp(P_NAME);
+}
+
+// die Funktionen
+
+void AddSingularId(mixed str)
+{
+  string *ids;
+  if (!pointerp(str))
+    str=({str});
+  ids=Query(U_IDS);
+  Set(U_IDS,({ids[0]+str,ids[1]}));
+}
+
+void RemoveSingularId(mixed str)
+{ 
+  if(stringp(str))
+    str=({str});
+  if(pointerp(str))
+    Set(U_IDS,({Query(U_IDS)[0]-str,Query(U_IDS)[1]}));
+}
+ 
+void AddPluralId(mixed str)
+{
+  string *ids;
+  
+  if (!pointerp(str))
+    str=({str});
+  ids=Query(U_IDS);
+  Set(U_IDS,({ids[0],ids[1]+str}));
+}
+
+void RemovePluralId(mixed str)
+{ 
+  if(stringp(str))
+    str=({str});
+  if(pointerp(str))
+    Set(U_IDS,({Query(U_IDS)[0],Query(U_IDS)[1]-str}));
+}
+
+void SetCoinsPerUnits(int coins,int units)
+{
+  if (coins<0||units<=0) return;
+  if (units==1)
+    Set(U_CPU, coins);
+  else Set(U_CPU,({coins,units}));
+}
+
+void SetGramsPerUnits(int grams, int units)
+{
+  if (grams<0||units<=0) return;
+  if (units==1)
+    Set(U_GPU, grams);
+  else Set(U_GPU,({grams,units}));
+}
+
+int *QueryCoinsPerUnits()
+{
+  mixed cpu;
+  cpu=Query(U_CPU);
+  if (intp(cpu)) return ({ cpu, 1 });
+  return cpu;
+}
+
+int *QueryGramsPerUnits()
+{
+  mixed gpu;
+  gpu=Query(U_GPU);
+  if (intp(gpu)) return ({ gpu, 1 });
+  return gpu;
+}
+
+void AddAmount(int am) {
+  SetProp(P_AMOUNT, am+Query(P_AMOUNT));
+}
+
+int IsUnit() {
+  return 1;
+}
+
+int IsEqual(object ob)
+// haben ob und dieses Objekte die gleiche Blueprint?
+{
+  if (ob==this_object()) return 0; // das ist ZU gleich :)
+  return (BLUE_NAME(ME)==BLUE_NAME(ob));
+}
+
+// ueberschreiben von Standardmethoden
+
+varargs string name(int fall, int demo)
+{
+  int req;
+  mixed n_adj;
+  string adj;
+
+  if ((req=QueryProp(U_REQ))<1) return 0;
+  if (fall!=RAW && 
+      pointerp(n_adj=QueryProp(P_NAME_ADJ)) && sizeof(n_adj) )
+    adj = implode(map(n_adj, #'DeclAdj, fall, demo && req==1), "");
+  else
+    adj = "";
+
+  if (req==1)
+    return sprintf(QueryProp(P_NAME),
+                   (fall==RAW || !QueryProp(P_ARTICLE) 
+                    ? "" : QueryArticle(fall,demo,1)), adj) /* +korrektur */;
+
+  if (fall!=WEM)
+    return sprintf(QueryProp(P_NAME), req, adj);
+  else {
+     int last;
+     last=Query(P_NAME)[1][<1];
+     return sprintf(QueryProp(P_NAME), req, adj)
+                  +(last!='s' && last!='n' ? "n" : "");
+  }
+}
+
+varargs string QueryPronoun(int casus)
+{
+  if (QueryProp(U_REQ)==1)
+    return ::QueryPronoun(casus);
+  switch(casus) {
+    case WESSEN: return "ihrer";
+    case WEM:    return "ihnen";
+    //default:     return "sie";
+  }
+  return("sie"); //fall-through
+}
+
+string short()
+{
+  if (QueryProp(U_REQ)<1 || QueryProp(P_INVIS)) return 0;
+  return capitalize(name(WER,0))+".\n";
+  /*
+  if (req>1) return sprintf(QueryProp(P_NAME), req)+".\n";
+  return capitalize(sprintf(QueryProp(P_NAME), QueryArticle(WER,0,1)))+".\n";
+  */
+}
+
+varargs string long()
+{
+  return (QueryProp(P_LONG) ? process_string(QueryProp(P_LONG)) : "")+
+    sprintf("Du siehst %s.\n",name(WEN));
+}
+
+varargs int id(string str,int lvl)
+{
+
+  string s1,s2,*ids;
+  int i;
+
+  if (!str) return 0;
+  
+  // Wenn kein pos. Amount, hat dieses Objekt keine IDs mehr, damit es nicht
+  // mehr ansprechbar ist.
+  int amount=QueryProp(P_AMOUNT);
+  if (amount < 1)
+    return 0;
+
+  if (::id(str)) {
+    SetProp(U_REQ, Query(P_AMOUNT,F_VALUE));
+    return 1;
+  }
+
+ ids=Query(U_IDS);
+  if (!ids)
+    return 0;
+ 
+  //die casts auf 'mixed' sind absicht. Sonst geht der Driver offenbar davon
+  //aus, dass ids[1] ein String ist. Es ist aber aber ein Array von Strings
+  //und genau das will auch match_item() haben. ;-) Zesstra
+  if (match_item(str,(mixed)ids[1] )) {
+    SetProp(U_REQ, amount);
+    return 1;
+  }
+  if (match_item(str,(mixed)ids[0] )) {
+    SetProp(U_REQ,1);
+    return 1;
+  }
+  if (sscanf(str,"%s %s",s1,s2) && s1[0..3]=="alle" && 
+    match_item(s2,(mixed)ids[1] )) {
+    SetProp(U_REQ, amount);
+    return 1;
+  }
+  if (sscanf(str,"%d %s",i,s1)==2 && i==1 && match_item(s1,(mixed)ids[0] )) {
+    SetProp(U_REQ,1);
+    return 1;
+  }
+  if (sscanf(str,"%d %s",i,s1)==2 && match_item(s1,(mixed)ids[1] ) && 
+      i<=amount && i>0) {
+    SetProp(U_REQ,i);
+    return 1;
+  }
+  return 0;
+}
+
+// Status fuer move... *seufz*
+int _orig_amount, _move_requested;
+
+varargs int move(object|string dest,int method)
+{
+  _orig_amount = QueryProp(P_AMOUNT);
+  // Wenn M_MOVE_ALL gesetzt ist, wird IMMER ALLES bewegt.
+  if (method & M_MOVE_ALL)
+    _move_requested = _orig_amount;
+  else
+    _move_requested = QueryProp(U_REQ);
+
+  ZDEBUG(sprintf("move from %O to %O: amount %d, req %d\n",environment(),dest,_orig_amount,_move_requested));
+  // Wenn nicht alles bewegt werden soll, wird die Menge dieses Objektes
+  // erstmal reduziert und bewegt. Erst nach der Bewegung wird der Rest dann
+  // im alten Environment neu erzeugt.
+  if ( _orig_amount > _move_requested )
+  {
+     // wenn in einem alten Paket das Gewicht noch nicht neu berechnet
+     // wurde muss dies geschehn solange die Muenzen im Paket noch das
+     // volle Gewicht haben...
+     if ( environment() )
+         environment()->query_weight_contents();
+     // ab jetzt nur auf <_move_requested> Einheiten arbeiten
+     Set( P_AMOUNT, _move_requested, F_VALUE);
+     ZDEBUG(sprintf("move: amount set to %d\n",_move_requested));
+  }
+
+  int res = ::move( dest, method );
+
+  if ( res != MOVE_OK )
+  {
+      // Fehlgeschlagene Bewegung ist doof.
+      // ggf. verfruehte Aenderung (P_AMOUNT oben auf <U_REQ> Einheiten
+      // reduziert) rueckgaengig machen
+      Set( P_AMOUNT, _orig_amount, F_VALUE );
+      ZDEBUG(sprintf("move: not OK, amount restored to %d\n",_orig_amount));
+      return res;
+  }
+
+  // TODO: eigentlich sollte es nicht passieren, dass die Menge jetzt negagtiv
+  // ist. Aber bei Geld kann es durch vereinigen mit Geldboerse/Geldkarten
+  // passieren und das ist kein Fehler. Koennte man evtl. mal differenziert
+  // pruefen.
+  int finalamount= QueryProp(P_AMOUNT);
+  /*if ( finalamount < 1 )
+  {
+    ZDEBUG(sprintf("move: zerstoerung, amount %d\n",finalamount));
+    remove(1);
+    return ME_WAS_DESTRUCTED;
+  }
+  */
+  ZDEBUG(sprintf("move: OK, amount %d\n",finalamount));
+  return res;
+}
+
+
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+  // Erst einen evtl. nicht zu bewegenden Rest im alten Environment wieder erzeugen.
+  if (oldenv && _orig_amount > _move_requested)
+  {
+      if ( oldenv == dest )
+      {
+          // Objekt wurde nur an den Anfang des inventory bewegt, sonst nichts.
+          // ggf. verfruehte Aenderung (oben auf <req> Einheiten reduziert)
+          // rueckgaengig machen
+          ZDEBUG(sprintf("move: same env, amount restored to %d\n",_orig_amount));
+          Set( P_AMOUNT, _orig_amount, F_VALUE );
+      }
+      else
+      {
+        // verbleibende Einheiten in einem neuen Objekte wieder zurueck ins
+        // alte Environment zurueck bewgen
+          object temp;
+          temp = clone_object(BLUE_NAME(ME));
+          temp->SetProp( P_AMOUNT, _orig_amount-_move_requested );
+          temp->move( oldenv, M_NOCHECK );
+          ZDEBUG(sprintf("move: Restmenge im alten Env erzeugt, amount %d\n",_orig_amount-_move_requested));
+      }
+  }
+
+  // Und jetzt ggf. mit den anderen Units gleichen Typs im neuen Environment
+  // vereinigen.
+  foreach(object item: all_inventory(environment()))
+  {
+      if ( IS_EQUAL(item) )
+      {
+        // Es ist ein Feature, dass auch mit Objekten mit negativen Amount
+        // vereinigt wird.
+        ZDEBUG(sprintf("move: vereinige mit %O (%d)\n",
+               item,item->QueryProp(P_AMOUNT)));
+        int mergedamount = QueryProp(P_AMOUNT) + item->QueryProp(P_AMOUNT);
+        item->Set( P_AMOUNT, 0, F_VALUE);
+        item->remove(1);
+
+        SetProp( P_AMOUNT, mergedamount);
+        // U_REQ ist jetzt auch zurueckgesetzt.
+
+        ZDEBUG(sprintf("move: nach vereinigung, amount %d\n",mergedamount));
+      }
+  }
+
+  // wenn man in frisch geclonten Units die noch kein environment haben
+  // per Hand U_REQ auf einen Wert != P_AMOUNT setzt, so gehen die
+  // restlichen Einheiten  verloren (es gibt kein Environment, in das man die
+  // Resteinheiten zurueck bewegen koennte).
+
+  // Und jetzt mal Decaykrams machen...
+
+  // wenn NO_DECAY_UNTIL_MOVE an ist und dieses ein Move ist, was von einem
+  // Env in ein anderes Env geht, loeschen. Nicht geloescht wird
+  // hingegen, wenn das move() in das allererste Env erfolgt (nach dem Clonen)
+  if ( (QueryProp(P_UNIT_DECAY_FLAGS) & NO_DECAY_UNTIL_MOVE)
+      && objectp(dest) && objectp(oldenv) && dest != oldenv)
+    SetProp(P_UNIT_DECAY_FLAGS, 
+        QueryProp(P_UNIT_DECAY_FLAGS) & ~NO_DECAY_UNTIL_MOVE );
+  ::NotifyMove(dest, oldenv, method);
+}
+
+
+void reset() {
+  if (!clonep(ME)) {
+    // Blueprint ist Master fuer zerfallende Unitobjekte
+    // das Decay sollte besser nicht durch manuellen Aufruf in der BP
+    // ausgeloest werden, daher Pruefung hier (PO == ME, falls der Reset vom
+    // Driver kommt).
+    if (previous_object() && previous_object() != ME)
+      return; 
+    int zeit = QueryProp(P_UNIT_DECAY_INTERVAL);
+    if (zeit > 0)
+    {
+      set_next_reset(zeit);
+      // Das Callout muss auf jeden Fall gemacht werden, wenn ein Decay
+      // Intervall gesetzt ist, damit die Blueprint auch den naechsten Reset
+      // kriegt, auch wenn es jetzt keine Clone gibt. Wenn es kein Quota gibt,
+      // kann der Callout wegfallen, beim Setzen eines Quota wird ja eine
+      // Funktion an diesem Objekt gerufen.
+      if (QueryProp(P_UNIT_DECAY_QUOTA) > 0)
+        call_out(#'_call_DoDecay, 1, 0);
+    }
+  }
+  else {
+    // Clones
+    if (Query(P_AMOUNT,F_VALUE)<1) remove(1);
+    else ::reset();
+  }
+}
+
+varargs int remove(int silent)
+{
+  int amount = QueryProp(P_AMOUNT);
+  int req = QueryProp(U_REQ);
+  if (amount > req)
+  {
+    ZDEBUG(sprintf("remove: reduziere amount %d um %d -> %d\n",
+           amount, req, amount-req ));
+    SetProp(P_AMOUNT, amount - req);
+    return 1;
+  }
+  ZDEBUG(sprintf("remove: restlos weg.\n"));
+  return ::remove(silent);
+}
+
+// sollte nicht von aussen gerufen werden.
+private void _call_DoDecay(object *klone) {
+  ZDEBUG(sprintf("call_DoDecay() in %O\n",ME));
+  // Clone oder kein Decay konfiguriert?
+  if (clonep()
+      || QueryProp(P_UNIT_DECAY_FLAGS) & NO_DECAY)
+    return;
+
+  if (!pointerp(klone))
+    klone = clones(2);
+  // Klone da?
+  int i=sizeof(klone);
+  if (!i)
+    return;
+
+  // in allen Klonen DoDecay rufen
+  while(get_eval_cost() > __MAX_EVAL_COST__/2 && i--) {
+    if (objectp(klone[i]))
+      catch(call_other(klone[i],"DoDecay", 0);publish);
+  }
+  // das i ist schon abgearbeitet.
+  i--;
+  // noch was uebrig?
+  if (i >= 0)
+    call_out(#'_call_DoDecay, 4, klone[0..i]);
+}
+
+// zerstoert quota Prozent der vorhandenen Units, liefert die Restmenge
+// zurueck. Ruft ggf. DoDecayMessage()
+public int DoDecay(int silent) {
+  // nur fuer Clones
+  if (!clonep())
+    return 0;
+  ZDEBUG(sprintf("DoDecay() in %O\n",ME));
+
+  // wenn das Objekt NO_DECAY oder NO_DECAY_UNTIL_MOVE in den Flags
+  // zerfaellt nix.
+  int flags = QueryProp(P_UNIT_DECAY_FLAGS);
+  if ((flags & NO_DECAY) || (flags & NO_DECAY_UNTIL_MOVE) )
+    return QueryProp(P_AMOUNT);
+
+  int quota = QueryProp(P_UNIT_DECAY_QUOTA);
+
+  // Zugewinn ist nicht. Ausserdem kann nicht mehr zerfallen, als da ist...
+  // Also ohne gueltiges Quota raus. Nur bei abs. Zerfall sind Zahlen > 10000
+  // erlaubt, bei relativen Zerfall waere das sinnlos.
+  if (quota <= 0 || 
+      (quota > 10000 && !(flags & ABSOLUTE_DECAY) ) ) 
+    return QueryProp(P_AMOUNT);
+  
+  int amount = QueryProp(P_AMOUNT);
+  int minimum = QueryProp(P_UNIT_DECAY_MIN);
+  // Zerfall nur, wenn man mehr als das Minimum hat.
+  if (amount > minimum) {
+    int zerfall;
+    if (flags & ABSOLUTE_DECAY) {
+      // Decay Quota soll einfach als abs. Zahl aufgefasst werden:
+      zerfall = quota;
+    }
+    else if (flags & INACCURATE_DECAY) {
+      // Rundungsfehler ignorieren und dafuer immer min. eine Einheit
+      // zerfallen lassein
+      zerfall = (amount * quota) / 10000;
+      if (zerfall <= 0) zerfall = 1;
+    }
+    else {
+      // Standardfall:
+      // nicht-ganzzahligen Rest als Wahrscheinlichkeit fuer den Zerfall einer
+      // Einheit auffassen
+      float tmp = amount * quota / 10000.0;
+      zerfall = to_int(tmp);
+      tmp -= zerfall; // Rest in tmp
+      // tmp*n ist eine Zahl zwischen 0 und n, wenn tmp*n > random(n)+1,
+      // dann zerfaellt eine Einheit extra. Da das Random fuer grosse Zahlen
+      // besser verteilt ist, nehm ich n = __INT_MAX__
+      if (ceil(tmp * __INT_MAX__) > random(__INT_MAX__) + 1)
+        zerfall++;
+    }
+
+    // nicht unter Minimum zerfallen
+    if (amount - zerfall < minimum)
+      zerfall = amount - minimum;
+
+    // erst ggf. Meldung ausgeben.
+    if (!silent && zerfall > 0)
+      DoDecayMessage(amount, zerfall);
+    // dann runtersetzen.
+    SetProp(P_AMOUNT, amount - zerfall);
+    ZDEBUG(sprintf("%O decayed by %d\n",ME, zerfall));
+    return amount-zerfall;
+  }
+  else if (amount < 0)
+    return 0; // neg. Anzahl gilt als "0 uebrig".
+  
+  // sonst ists wohl zwischen 0 und minimum, nix aendert sich.
+  return amount;
+}
+
+// gibt die Zerfallsmeldung aus. Wenn man mit der Standardmeldung nicht zufrieden
+// ist (duerfte der Normalfall sein), sollte man diese Funktion
+// ueberschreiben. Bekommt bisherige und jetzt gerade zerfallene Menge
+// uebergeben. Wichtig: zu diesem Zeitpunkt hat sich an der Unit noch nix (!)
+// geaendert!
+protected void DoDecayMessage(int oldamount, int zerfall) {
+  string msg;
+  if (oldamount == zerfall) {
+    if (living(environment())) {
+      tell_object(environment(), break_string(sprintf(
+          "Auf einmal %s%s zu Staub!",
+          (oldamount>1?"zerfallen Deine ":"zerfaellt D"), name(WER)),78));
+    }
+    // das tell_room() muss auf jeden fall sein, weil es "lebende" Raeume
+    // gibt. Liegt der KRam in einem solchen wuerden die Livings in diesem Raum
+    // sonst keine meldung kriegen.
+    tell_room(environment(), break_string(sprintf(
+          "Auf einmal %s %s zu Staub!",
+          (oldamount>1?"zerfallen":"zerfaellt"), name(WER)),78),
+        ({environment()}));
+  }
+  else {
+     if (living(environment())) {
+      tell_object(environment(), break_string(sprintf(
+          "Auf einmal %s %d Deiner %s zu Staub!",
+          (zerfall>1?"zerfallen":"zerfaellt"),
+          zerfall, Query(P_NAME)[1]),78));
+    }
+    // das tell_room() muss auf jeden fall sein, weil es "lebende" Raeume
+    // gibt. Liegt der KRam in einem solchen wuerden die Livings in diesem Raum
+    // sonst keine meldung kriegen.
+    tell_room(environment(), break_string(sprintf(
+          "Auf einmal %s %d %s zu Staub!",
+          (zerfall>1?"zerfallen":"zerfaellt"),
+          zerfall, Query(P_NAME)[1]),78), ({environment()}) ); 
+  }
+}
+
+static int _query_unit_decay_interval() {
+  if (clonep()) {
+    // fuer clones den Wert aus der BP liefern, der Wert in Clones ist eh
+    // uninteressant.
+    if (objectp(blueprint()))
+      return blueprint()->QueryProp(P_UNIT_DECAY_INTERVAL);
+    else
+      return load_object(load_name())->QueryProp(P_UNIT_DECAY_INTERVAL);
+  }
+  return Query(P_UNIT_DECAY_INTERVAL);
+}
+
+static int _set_unit_decay_interval(int zeit) {
+  // fuer Clones ist die Prop sinnlos und kann nicht gesetzt werden.
+  if (clonep()) {
+    if (objectp(blueprint()))
+      return blueprint()->QueryProp(P_UNIT_DECAY_INTERVAL);
+    else
+      return load_object(load_name())->QueryProp(P_UNIT_DECAY_INTERVAL);
+  }
+  // ueber diese Prop liesse sich der Reset relativ beliebig veraendern,
+  // verhindern, etc. Daher darf sie nur von diesem Objekt gesetzt werden, von
+  // Weisen+ oder eines Objektes, das die gleiche UID hat wie dieses hier oder
+  // des Programmieres dieses Objekts oder eines Magiers, der berechtigt fuer
+  // die jeweilige UID ist. Letztere beiden werden ueber den Ruf von
+  // QueryUIDsForWizard() ermittelt.
+  // erstes fremdes Objekt finden und pruefen.
+  foreach(object po: caller_stack(1)) {
+    if (po != ME) {
+      // fremden Aufrufer gefunden
+      if (getuid(po) != getuid(ME) 
+          && member(master()->QueryUIDsForWizard(getuid(po)),getuid(ME)) == -1
+          && (!this_interactive() || !IS_ELDER(this_interactive()) ) )
+        // unberechtigt, Abbruch
+        return QueryProp(P_UNIT_DECAY_INTERVAL);
+      else
+        // nur das erste ext. Objekt in der Callkette wird geprueft, raus.
+        break;
+    }
+  }
+  set_next_reset(zeit);
+  // falls dies aus dem create() gesetzt wird, muss zeitverzoegert eine
+  // Funktion von aussen am Objekt gerufen werden, da nach einem create() das
+  // Objekt als resettet gilt und sonst keinen Reset kriegt.
+  call_out("_query_unit_decay_interval",1);
+  return Set(P_UNIT_DECAY_INTERVAL, zeit, F_VALUE);
+}
+
+static int _query_unit_decay_quota() {
+  if (clonep()) {
+    // hat dieses Objekt einen eigenen wert gespeichert?
+    int quota=Query(P_UNIT_DECAY_QUOTA, F_VALUE);
+    if (quota)
+      return quota;
+    // sonst den Wert aus der Blueprint
+    if (objectp(blueprint()))
+      return blueprint()->QueryProp(P_UNIT_DECAY_QUOTA);
+    else
+      return load_object(load_name())->QueryProp(P_UNIT_DECAY_QUOTA);
+  }
+  return Query(P_UNIT_DECAY_QUOTA, F_VALUE);
+}
+
+static int _set_unit_decay_quota(int quota) { 
+  // momentan nur quota zwischen 0 und 10000
+  if (quota >= 0 && quota <=10000)
+    return Set(P_UNIT_DECAY_QUOTA, quota, F_VALUE);
+  else
+    return Query(P_UNIT_DECAY_QUOTA, F_VALUE);
+}
+
+static int _query_unit_decay_min() {
+  if (clonep()) {
+    // hat dieses Objekt einen eigenen wert gespeichert?
+    int minimum=Query(P_UNIT_DECAY_MIN, F_VALUE);
+    if (minimum)
+      return minimum;
+    // sonst den Wert aus der Blueprint
+    if (objectp(blueprint()))
+      return blueprint()->QueryProp(P_UNIT_DECAY_MIN);
+    else
+      return load_object(load_name())->QueryProp(P_UNIT_DECAY_MIN);
+  }
+  return Query(P_UNIT_DECAY_MIN, F_VALUE);
+}
+
+static int _query_unit_decay_flags() {
+  if (clonep()) {
+    // hat dieses Objekt einen eigenen wert gespeichert?
+    int flags=Query(P_UNIT_DECAY_FLAGS, F_VALUE);
+    if (flags)
+      return flags;
+    // sonst den Wert aus der Blueprint
+    if (objectp(blueprint()))
+      return blueprint()->QueryProp(P_UNIT_DECAY_FLAGS);
+    else
+      return load_object(load_name())->QueryProp(P_UNIT_DECAY_FLAGS);
+  }
+  // Die BP liefert den Wert auf jeden Fall ohne NO_DECAY_UNTIL_MOVE zurueck,
+  // weil dieses Flag im Falle der BP nicht zurueckgesetzt wird und bei Clients 
+  // ohne eigene Flags der Zerfall stoppen wuerde.
+  return Query(P_UNIT_DECAY_FLAGS, F_VALUE) & ~NO_DECAY_UNTIL_MOVE;
+}
+
diff --git a/std/user_filter.c b/std/user_filter.c
new file mode 100644
index 0000000..e4190c8
--- /dev/null
+++ b/std/user_filter.c
@@ -0,0 +1,435 @@
+// MorgenGrauen MUDlib
+//
+// user_filter.c -- Hilfsmodul fuer die wer-Liste
+//
+// $Id: user_filter.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#include <properties.h>
+#include <wizlevels.h>
+#include <language.h>
+
+#define ME this_object()
+
+static int is_in ( object ob, mixed x )
+{
+  return (strstr( lower_case(country(ob)), x ) != -1 ||
+          strstr( lower_case(ob->Query(P_LOCATION) || ""), x ) != -1);
+}
+
+static int is_at( object ob, mixed x )
+{
+  return environment(ob) == x;
+}
+
+static int is_wiz_level_lt( object ob, mixed x )
+{
+  return query_wiz_level(ob) < x;
+}
+
+static int is_wiz_level_ge( object ob, mixed x )
+{
+  return query_wiz_level(ob) >= x;
+}
+
+static int is_prop_set( object ob, mixed x )
+{
+  return (mixed)ob->QueryProp(x) != 0;
+}
+
+static int is_second( object ob, mixed x )
+{
+  return ob->QueryProp(P_SECOND) && ob->QueryProp(P_SECOND_MARK) > x;
+}
+
+static int is_mage_second( object ob )
+{
+  return ob->QueryProp(P_SECOND) && IS_LEARNER(ob->QueryProp(P_SECOND));
+}
+
+static int is_in_gilde( object ob, mixed x )
+{
+  string str;
+
+  // nur Magier koennen eine andere Gilde per P_VISIBLE_GUILD in werlisten
+  // etc. vortaeuschen.
+  if (IS_LEARNER(ob)) {
+    // Querymethode von P_VISIBLE_GUILD fragt ggf. auch P_GUILD ab.
+    if ( !stringp(str = (string)ob->QueryProp(P_VISIBLE_GUILD)) )
+      return 0;
+  }
+  else {
+      str = (string)ob->QueryProp(P_GUILD);
+  }
+  if (!stringp(str)) return 0; // login hat z.B. keine Gilde.
+  return strstr(lower_case(str),x) == 0;
+}
+
+static int is_in_team( object ob, mixed x )
+{
+  object team;
+  
+  if ( !objectp(team = (object)ob->QueryProp(P_TEAM)) )
+    return x == "";
+  
+  return lower_case((string)team->Name()) == x;
+}
+
+static int is_name_in( object ob, mixed x )
+{
+  return member( x, capitalize(geteuid(ob)) ) >= 0;
+}
+
+static int is_in_region( object ob, mixed x )
+{
+  return environment(ob) &&
+    (object_name(environment(ob))[0..(sizeof(x)-1)] == x);
+}
+
+static int is_region_member( object ob, mixed x )
+{
+  return (int)master()->domain_member( geteuid(ob), x );
+}
+
+static int is_region_master( object ob, mixed x )
+{
+  return (int)master()->domain_master( geteuid(ob), x );
+}
+
+static int is_guild_master(object ob, mixed x)
+{
+  return (int)master()->guild_master(geteuid(ob), x);
+}
+
+static int is_gender( object ob, mixed x )
+{
+  return ob->QueryProp(P_GENDER) == x;
+}
+
+static int is_race( object ob, mixed x )
+{
+  return ob->QueryProp(P_RACE) == x;
+}
+
+static int is_unmarried( object ob )
+{
+  return ob->QueryProp(P_MARRIED) == 0;
+}
+
+static int is_idle( object ob, mixed x )
+{
+  return query_idle(ob) >= x;
+}
+
+static int is_hc( object ob)
+{
+  return ob->query_hc_play()>0;
+}
+
+static int is_ghost( object ob)
+{
+  return (int)ob->QueryProp(P_GHOST);
+}
+
+static int uses_ssl(object ob)
+{
+  if(!this_interactive() || !IS_LEARNER(this_interactive()))
+    return 0;
+#if __EFUN_DEFINED__(tls_query_connection_info)
+  return tls_query_connection_info(ob) != 0;
+#else
+  return 0;
+#endif
+}
+
+protected int is_active_guide(object ob) {
+    int cic=(int)ob->QueryProp(P_NEWBIE_GUIDE);
+    if (!intp(cic) || cic <= 0)
+      return 0;
+    else if (cic < 60)
+      return 1; // kleiner 60s in der Prop -> immer aktiv.
+    else if (cic <= 86400) {
+      // wenn laenger idle als cic -> inaktiv
+      if (query_idle(ob) > cic)
+	return 0;
+      return 1;
+    }
+    // ungueltiger Wertebereich -> inaktiv.
+    return 0;
+}
+
+object *filter_users( string str )
+{
+  object *res, *orig, *zwi;
+  string *words;
+  int i, sz, f, l,t;
+  mixed x;
+  
+  orig = users();
+  
+  if (!str)
+    return orig;
+  
+  res = ({});
+  words = old_explode( lower_case(str), " " );
+  sz = sizeof(words);
+  
+  for ( i = 0; i < sz; i++ ){
+    zwi = ({});
+    
+    switch (words[i]) {
+    case "ausser":
+    case "ohne":
+    case "nicht":
+      f=-1;
+      if (!i) res=orig;
+      continue;
+      
+    case "oder":
+      f=0;
+      continue;
+      
+    case "und":
+      f=1;
+      continue;
+      
+    case "alle":
+      zwi = orig;
+      break;
+
+    case "in":
+    case "aus":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_in", ME, words[i] );
+      break;
+      
+    case "region":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_in_region", ME, "/d/" + words[i] );
+      break;
+      
+    case "mitarbeiter":
+    case "regionsmitarbeiter":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_region_member", ME, words[i] );
+      break;
+      
+    case "regionsmagier":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_region_master", ME, words[i] );
+      break;
+      
+    case "gildenmagier":
+      if ( ++i >= sz )
+	break;
+      zwi = filter (orig, "is_guild_master", ME, words[i] );
+      break;
+      
+    case "gilde":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_in_gilde", ME, words[i] );
+      break;
+      
+    case "team":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_in_team", ME,
+                          lower_case("team " + words[i]) );
+      break;
+      
+    case "bei":
+      if ( ++i >= sz )
+        break;
+      if ( !objectp(x = find_player(words[i]) ) &&
+           !objectp(x = find_living(words[i])) )
+        break;
+      if ( x->QueryProp(P_INVIS) )
+        break;
+      zwi = filter( orig, "is_at", ME, environment(x) );
+      break;
+
+    case "goetter":
+      zwi = filter( orig, "is_wiz_level_ge", ME, GOD_LVL );
+      break;
+      
+    case "erzmagier":
+    case "erzi":
+    case "erzis":
+      zwi = filter( orig, "is_wiz_level_ge", ME, ARCH_LVL );
+      break;
+      
+    case "magier":
+      zwi = filter( orig, "is_wiz_level_ge", ME, LEARNER_LVL );
+      break;
+      
+    case "spieler":
+      zwi = filter( orig, "is_wiz_level_lt", ME, SEER_LVL );
+      break;
+      
+    case "seher":
+      zwi = filter( filter( orig, "is_wiz_level_lt", ME,
+                                        LEARNER_LVL),
+                          "is_wiz_level_ge", ME, SEER_LVL );
+      break;
+
+    case "maennlich":
+      zwi = filter( orig, "is_gender", ME, MALE );
+      break;
+      
+    case "weiblich":
+      zwi = filter( orig, "is_gender", ME, FEMALE );
+      break;
+      
+    case "ledig":
+      zwi = filter( orig, "is_unmarried" );
+      break;
+
+    case "ssl":
+    case "stunnel":
+      zwi = filter(orig,"uses_ssl");
+      break;
+      
+    case "hardcore":
+      zwi = filter( orig, "is_hc" );
+      break;
+      
+    case "geist":
+    case "tot":
+      zwi = filter( orig, "is_ghost" );
+      break;
+      
+    case "mensch":
+      zwi = filter( orig, "is_race", ME, "Mensch" );
+      break;
+      
+    case "zwerg":
+      zwi = filter( orig, "is_race", ME, "Zwerg" );
+      break;
+      
+    case "elf":
+      zwi = filter( orig, "is_race", ME, "Elf" );
+      break;
+      
+    case "feline" :
+      zwi = filter( orig, "is_race", ME, "Feline" );
+      break;
+      
+    case "hobbit":
+      zwi = filter( orig, "is_race", ME, "Hobbit" );
+      break;
+      
+    case "dunkelelf":
+      zwi = filter( orig, "is_race", ME, "Dunkelelf" );
+      break;
+
+    case "goblin":
+      zwi = filter( orig, "is_race", ME, "Goblin" );
+      break;
+
+    case "ork":
+      zwi = filter( orig, "is_race", ME, "Ork" );
+      break;
+
+    case "frosch":
+      zwi = filter( orig, "is_prop_set", ME, P_FROG );
+      break;
+
+    case "weg":
+      zwi = filter( orig, "is_prop_set", ME, P_AWAY );
+      break;
+
+    case "idle":
+      zwi = filter( orig, "is_idle", ME, 120 );
+      break;
+
+    case "idlezeit":
+      if ( ++i >= sz )
+        break;
+      sscanf(words[i],"%d",t);
+      zwi = filter( orig, "is_idle", ME, 60*t );
+      break;
+
+    case "cicerone":
+    case "cicerones":
+      // is_active_guide() ist in /std/player/guide.c
+      zwi = filter( orig, #'is_active_guide);
+      break;
+
+    case "testie":
+    case "testies":
+    case "testspieler":
+      zwi = filter( orig, "is_prop_set", ME, P_TESTPLAYER );
+      break;
+      
+    case "zweitie":
+    case "zweities":
+    case "zweitspieler":
+        if (IS_LEARNER(this_player()))
+          zwi = filter( orig, "is_second", ME, -2 );
+        else
+          zwi = filter( orig, "is_second", ME, -1 );
+      break;
+
+    case "magierzweitie":
+    case "magierzweities":
+    case "magierzweitspieler":
+        if (IS_ARCH(this_player()))
+          zwi = filter( orig, "is_mage_second");
+      break;
+
+    case "erwartete":
+      if ( i < (sz-1) && words[i+1] == "wegen" ) {
+        i++;
+
+        if ( !mappingp(x = (mixed)this_player()->QueryProp(P_WAITFOR_REASON)) ||
+             !pointerp(x = m_indices(x)) )
+          break;
+        
+        zwi = filter( orig, "is_name_in", ME, x );
+        break;
+      }
+      
+      if ( !pointerp(x = (mixed)this_player()->QueryProp(P_WAITFOR)) )
+        break;
+      zwi = filter( orig, "is_name_in", ME, x );
+      break;
+
+    case "icq":
+      zwi = filter( orig, "is_prop_set", ME, P_ICQ );
+      break;
+
+    case "url":
+    case "www":
+      zwi = filter( orig, "is_prop_set", ME, P_HOMEPAGE );
+      break;
+          
+    default:
+      continue;
+    }
+    
+    switch (f) {
+    case -1:
+      res -= zwi;
+      break;
+      
+    case 1:
+      res &= zwi;
+      break;
+
+    default:
+      res += zwi;
+    }
+  }
+  
+  return res;
+}
+
diff --git a/std/util/cidr.c b/std/util/cidr.c
new file mode 100644
index 0000000..85a00e5
--- /dev/null
+++ b/std/util/cidr.c
@@ -0,0 +1,26 @@
+// MorgenGrauen MUDlib
+/** \file /std/util/cidr.c
+* Funktionen zur Interpretation und Umrechnung von IP-Adressen in CIDR-Notation.
+* \author Zesstra
+* \date 05.02.2010
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+// Never do this at home. ;-)
+// Der Hintergrund ist, dass der Master moeglichst wenig Code von ausserhalb
+// /secure/ erben/inkludieren sollte, ich aber nur an einer Stelle Aenderungen
+// vornehmen moechte...
+#define private public
+#include "/secure/master/cidr.c"
+#undef private
+
+//string IPv4_test(string addr) {return (IPv4_int2addr(IPv4_addr2int(addr)));}
+
diff --git a/std/util/ex.c b/std/util/ex.c
new file mode 100644
index 0000000..eae9e27
--- /dev/null
+++ b/std/util/ex.c
@@ -0,0 +1,293 @@
+// MorgenGrauen MUDlib
+//
+// util/ex.c -- ex compatible editor
+//
+// $Id: ex.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/util/input";
+
+private nosave mapping ctrl = ([]);
+private nosave mixed buf;
+
+#include <sys_debug.h>
+#undef ARGS
+#include <wizlevels.h>
+#include <player.h>
+
+#define MODE	0
+#  define CMD		0
+#  define INP		1
+#define TEXT	1
+#define LINE	2
+#define FUNC	3
+#define ARGS	4
+#define FLAG	5
+#  define NUM		1
+#define CHG	6
+
+#define YANK	"@YANK"
+
+int evaluate(string in);
+
+varargs int error(string msg, string arg)
+{
+  if(stringp(arg)) write(arg+": "+msg+"\n");
+  else write(msg+"\n");
+}
+
+string substitute(string s, string regex, string n, string extra)
+{
+  mixed del; int i;
+  if((i = sizeof(del = regexplode(s, regex))) < 2) return s;
+  if(member(extra, 'g') == -1) i = 1;
+  else i -= 2;
+  while(i>0) {
+    del[i] = n;
+    i -= 2;
+  }
+  return implode(del, "");
+}
+
+// saveText() -- save edited text
+//
+int saveText()
+{
+  string text;
+  text = implode(ctrl[buf][TEXT][1..], "\n")+"\n";
+  error(sprintf("%O, %d Zeilen, %d Zeichen", 
+	       buf ? buf : "", sizeof(ctrl[buf][TEXT])-1,
+	       sizeof(text)));
+  if(ctrl[buf][FUNC]) apply(ctrl[buf][FUNC], ctrl[buf][ARGS], text);
+  ctrl = m_copy_delete(ctrl, buf);
+  ctrl = m_copy_delete(ctrl, buf+"!");
+  buf = 0;
+  return 1;
+}
+
+// cmdPrompt() -- create command prompt
+//
+string cmdPrompt(int mode, mixed text, int line, int maxl)
+{
+  return ":";
+}
+
+// inputPrompt() -- create input prompt
+//
+string inputPrompt()
+{
+  if(ctrl[buf][FLAG] & NUM) return sprintf("[%02d] ", ctrl[buf][LINE]);
+  return "";
+}
+
+string AddNum(string s)
+{
+  string r;
+  r = inputPrompt()+s;
+  ctrl[buf][LINE]++;
+  return r;
+}
+
+void showLines(int from, int to)
+{
+  write(implode(map(ctrl[buf][TEXT][from..to], #'AddNum), "\n")+"\n");
+  ctrl[buf][LINE] = from;
+}
+
+// commandMode() -- handle commands
+//
+int commandMode(string in)
+{
+  int from, to, swap;
+  string cmd;
+
+  if(!in) return 0;
+  if(in[0..1] == "se") {
+    ctrl[buf][FLAG] ^= NUM;
+    return 0;
+  }
+    
+  if(sscanf(in, "%d,%d%s", from, to, cmd) != 3)
+    if(sscanf(in, "%d%s", from, cmd) != 2)
+      if(!stringp(in)) return error("Kein Editorkommando", in);
+      else { cmd = in; from = to = ctrl[buf][LINE]; }
+    else to = from;
+
+  if(from < 0) from = sizeof(ctrl[buf][TEXT])-1+from;
+  if(to < 1) to = sizeof(ctrl[buf][TEXT])-1+to;
+  if(to && to < from) return error("Erste Adresse groesser als die Zweite");
+  if(from) ctrl[buf][LINE] = from;
+  if(from > sizeof(ctrl[buf][TEXT])-1 ||
+     to > sizeof(ctrl[buf][TEXT])-1) 
+    return error("Nicht so viele Zeilen im Speicher");
+  if(!to) to = from;
+  switch(cmd[0])
+  {
+  case 'h':
+    write("ex: Kommandohilfe\n\n"
+	 "Alle Kommandos funktionieren nach folgendem Prinzip:\n"
+	 "Adresse Kommando Argumente\n"
+	 "Adressen werden gebildet durch: ZeileVon,ZeileBis\n"
+	 "(ZeileBis kann weggelassen werden, samt Komma)\n"
+	 "Kommandos:\n"
+	 "  q,x -- Editor verlassen\n"
+	 "  r   -- Datei einfuegen\n"
+	 "         r<datei>\n"
+	 "  a   -- Text hinter der aktuellen Zeile einfuegen\n"
+	 "  i   -- Text vor der aktuellen Zeile einfuegen\n"
+	 "  d   -- Zeilen loeschen\n"
+	 "  y   -- Zeilen zwischenspeichern\n"
+	 "  pu  -- Zwischengespeicherte Zeilen einfuegen\n"
+	 "  s   -- Substitution von Text in Zeilen\n"
+	 "         s/AlterText/NeuerText/\n"
+          "  p,l -- Zeilen anzeigen\n"
+	 "  z   -- Eine Seite anzeigen\n");
+    break;
+  case 'q':
+    if(ctrl[buf][CHG])
+      if(!(sizeof(cmd) > 1 && cmd[1]=='!'))
+        return error("Datei wurde geaendert! Abbrechen mit q!");
+      else ctrl[buf] = ctrl[buf+"!"];
+  case 'x':
+    if(saveText()) return 1;
+  case 'r':
+    if(!IS_WIZARD(this_player()))
+      return error("Kommando gesperrt", cmd[0..0]);
+    if(file_size(cmd[1..]) < 0) 
+      return error("Datei nicht gefunden", "\""+cmd[1..]+"\"");
+    ctrl[buf][TEXT] = ctrl[buf][TEXT][0..from] 
+		  + explode(read_file(cmd[1..]), "\n")
+	           + ctrl[buf][TEXT][(from == to ? to+1 : to)..];
+    ctrl[buf][CHG] = 1;
+    break;
+  case 'a':
+    ctrl[buf][MODE] = INP;
+    write("Texteingabe: (. beendet zum Kommandomodus, ** beendet ganz)\n");
+    input(#'inputPrompt, 0, #'evaluate); 
+    ctrl[buf][CHG] = 1;
+    return 1;
+  case 'i':
+    ctrl[buf][MODE] = INP;
+    ctrl[buf][LINE]--;
+    write("Texteingabe: (. beendet zum Kommandomodus, ** beendet ganz)\n");
+    input(#'inputPrompt, 0, #'evaluate);
+    ctrl[buf][CHG] = 1;
+    return 1;
+  case 'd':
+    ctrl[buf][TEXT][from..to] = ({});
+    ctrl[buf][CHG] = 1;
+    break;
+  case 'y':
+    ctrl[YANK] = ctrl[buf][TEXT][from..to];
+    if(to - from) error(to-from+1+" Zeilen gespeichert");
+    break;
+  case 's':
+  {
+    mixed reg, new, extra, scmd;
+    if(sizeof(scmd = old_explode(cmd[1..], cmd[1..1])) < 2)
+      return error("Substitution fehlgeschlagen");
+    reg = scmd[0]; new = scmd[1];
+    if(sizeof(scmd) > 2) extra = scmd[2];
+    else extra = "";
+    ctrl[buf][TEXT][from..to] = map(ctrl[buf][TEXT][from..to], 
+				      #'substitute, 
+                                          reg, new, extra);
+    showLines(from, to);
+    ctrl[buf][CHG] = 1;
+    break;
+  }
+  case 'z':
+    showLines(ctrl[buf][LINE], 
+	     ctrl[buf][LINE]+this_player()->QueryProp(P_SCREENSIZE));
+    ctrl[buf][LINE] += this_player()->QueryProp(P_SCREENSIZE);
+    break;
+  case 'p':
+    if(sizeof(cmd) > 1 && cmd[1] == 'u')
+    {
+      if(!pointerp(ctrl[YANK])) return error("Keine Zeilen im Speicher");
+      if(from >= ctrl[buf][LINE]) ctrl[buf][TEXT] += ctrl[YANK];
+      else
+        ctrl[buf][TEXT][0..from] + ctrl[YANK] + ctrl[buf][TEXT][from+1..];
+      ctrl[buf][LINE] += sizeof(ctrl[YANK]);
+      ctrl[YANK] = 0;
+      showLines(ctrl[buf][LINE], ctrl[buf][LINE]);
+      break;
+    }
+  case 'l':
+  default:
+    if(!from) 
+    {
+      error("Kein Editorkommando", sprintf("%c", cmd[0]));
+      return 0;
+    }
+    showLines(from, to);
+  }
+  input(#'cmdPrompt, 0, #'evaluate);
+  return 1;
+}
+
+// inputMode() -- handle input mode commands
+//
+int inputMode(string in)
+{
+  switch(in)
+  {
+  case ".": 
+    ctrl[buf][MODE] = CMD;
+    ctrl[buf][LINE]--;
+    input(#'cmdPrompt, 0, #'evaluate);
+    break;
+  case "**":
+    return saveText();
+  default:
+    if(!in) /* do nothing now */;
+    if(ctrl[buf][LINE] < sizeof(ctrl[buf][TEXT])-1)
+      ctrl[buf][TEXT] = ctrl[buf][TEXT][0..ctrl[buf][LINE]-1] + ({ in }) 
+	        + ctrl[buf][TEXT][ctrl[buf][LINE]..];
+    else ctrl[buf][TEXT] += ({ in });
+    ctrl[buf][LINE]++;
+    input(#'inputPrompt, 0, #'evaluate);
+    break;
+  }
+  return 1;
+}
+
+// evaluate() -- evaluate input (in) in context (ctrl[buf])
+//
+int evaluate(string in)
+{
+  switch(ctrl[buf][MODE])
+  {
+    case INP: return inputMode(in);
+    case CMD: return commandMode(in);
+    default : return 0;
+  }
+}
+
+void StartEX(string in, mixed c)
+{
+  if(!in || in == "j" || in == "J") ctrl[buf] = c;
+  else if(in && (in != "n"  || in != "N"))
+    return 0;
+  ctrl[buf+"!"] = ctrl[buf];
+  input(#'cmdPrompt, 0, #'evaluate);
+}
+
+varargs int ex(mixed text, closure func, mixed fargs, string buffer)
+{
+  int c, l;
+  mixed ct;
+  if(!text) text = "";
+  c = sizeof(text);
+  l = sizeof(text = explode(text, "\n")) - 1;
+  ct = ({ CMD, text, 0, func, fargs, 0, 0});
+  if(!ctrl[buffer]) StartEX(0, ct);
+  else input(sprintf("Es existiert bereits Text! Verwerfen? [j]"),
+	    0, #'StartEX, ct);
+  return 1;
+}
diff --git a/std/util/executer.c b/std/util/executer.c
new file mode 100644
index 0000000..4e0f707
--- /dev/null
+++ b/std/util/executer.c
@@ -0,0 +1,39 @@
+// MorgenGrauen MUDlib
+//
+// utils/executer.c - Helfer zum Ausfuehren vom Kram
+//
+// $Id: skills.c 6673 2008-01-05 20:57:43Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+protected mixed execute_anything(mixed fun, varargs mixed args)
+{
+  if ( closurep(fun) && objectp(query_closure_object(fun)) )
+    return apply(fun, args);
+
+  if (stringp(fun))
+    return call_other(this_object(), fun, args...);
+
+  if ( pointerp(fun) && sizeof(fun)==2 )
+  {
+    object ob;
+    if (sizeof(fun)>2)
+      raise_error(sprintf("execute_anything(): first argument may only "
+                         "have 2 elements if array.\n"));
+
+    if ( stringp(fun[0]) )
+      ob=find_object(fun[0]);
+    else
+      ob=fun[0];
+
+    if ( !objectp(ob) || !stringp(fun[1]) )
+      return 0;
+
+    return call_other(ob, fun[1], args...);
+  }
+  return 0;
+}
+
diff --git a/std/util/input.c b/std/util/input.c
new file mode 100644
index 0000000..e65bc84
--- /dev/null
+++ b/std/util/input.c
@@ -0,0 +1,31 @@
+// MorgenGrauen MUDlib
+//
+// util/input.c -- generic input handling
+//
+// $Id: input.c 8349 2013-02-04 21:15:28Z Zesstra $
+
+#include <input_to.h>
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+varargs void input(mixed prompt, mixed pargs, mixed ctrl, mixed ctrlargs)
+{
+  mixed prompttext;
+  if(closurep(prompt))
+    prompttext = apply(prompt, pointerp(pargs) ? pargs : ({}));
+  else prompttext = prompt;
+  if (!stringp(prompttext)) prompttext="";
+  input_to("done", INPUT_PROMPT, prompttext, prompt, pargs, ctrl, ctrlargs);
+}
+
+void done(string in, mixed prompt, mixed pargs, mixed ctrl, mixed ctrlargs)
+{
+  if(closurep(ctrl) &&
+     apply(ctrl, ({ in }) + (pointerp(ctrlargs) ? ctrlargs : ({})))) 
+    return;
+  input(prompt, pargs, ctrl, ctrlargs);
+}
diff --git a/std/util/pager.c b/std/util/pager.c
new file mode 100644
index 0000000..9d71b85
--- /dev/null
+++ b/std/util/pager.c
@@ -0,0 +1,142 @@
+// MorgenGrauen MUDlib
+//
+// util/pager.c -- generic pager
+//
+// $Id: pager.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/util/input";
+
+#include <pager.h>
+#include <sys_debug.h>
+#include <wizlevels.h>
+
+string prompt(mixed pinfo, string add)
+{
+  return sprintf(MSG_PROMPT+"%s", stringp(add)?add:"");
+}
+
+string fread(mixed pinfo, int begin, int c)
+{
+  if(begin > pinfo[MAXL] || begin < 1) return 0;
+  if(pinfo[FILE]) return read_file(pinfo[TEXT], begin, c);
+  else
+  {
+    int start, end, l, x;
+
+    if(member(pinfo[JUNK], begin)) 
+    {
+      start = pinfo[JUNK][begin];
+      if(!(end = pinfo[JUNK][begin+c]))
+        end = sizeof(pinfo[TEXT]);
+      else end--;
+      return pinfo[TEXT][start..end];
+    }
+    return 0;
+  }
+}
+
+mixed call(closure ctrl, mixed ctrlargs)
+{
+  if (!ctrl||!closurep(ctrl)) return 0;
+  if(!pointerp(ctrlargs)) return funcall(ctrl);
+  return apply(ctrl, ctrlargs);
+}
+
+ 
+varargs int eval_command(mixed in, mixed pinfo)
+{
+  string cprompt, tmp;
+  cprompt = "";
+  if(in == "q" || in == "x")
+  {
+    if(closurep(pinfo[CTRL])) apply(pinfo[CTRL], pinfo[CARG]);
+    return 1;
+  }
+  else
+    if(!in || (stringp(in) && !sizeof(in))) pinfo[CURL] += pinfo[PAGE];
+    else if(in != -1) return 0;
+
+  if(pinfo[CURL] >= pinfo[MAXL]) 
+    return (call(pinfo[CTRL], pinfo[CARG]), 1); 
+  if(pinfo[CURL] <= 0) pinfo[CURL] = 1;
+  if(pinfo[CURL] == 1) cprompt = MSG_TOP;
+  if(pinfo[CURL] + pinfo[PAGE] >= pinfo[MAXL]) cprompt = MSG_BOTTOM;
+  if(!tmp = fread(pinfo, pinfo[CURL], pinfo[PAGE]))
+    return 0; /* (write(MSG_OOPS+"\n"), 0); */
+  if (query_once_interactive(this_object()))
+     tell_object(this_object(), tmp);
+  else write(tmp);
+  if(!(pinfo[FLAG] & E_PROMPT) &&
+     (pinfo[CURL] + pinfo[PAGE] >= pinfo[MAXL] || !pinfo[PAGE]))
+    return (call(pinfo[CTRL], pinfo[CARG]), 1);
+  if(!(pinfo[FLAG] & E_CAT) &&
+     (pinfo[MAXL] > pinfo[PAGE] || (pinfo[FLAG] & E_PROMPT)))
+    input(#'prompt, ({ pinfo, cprompt }), #'eval_command, ({ pinfo }));
+  return 1;
+}
+
+varargs public void More(string txt, int file,
+			 mixed ctrl, mixed ctrlargs,
+			 int flags)
+{
+  int j;
+  mixed tmp, pinfo;
+  if(!txt) return call(ctrl, ctrlargs);
+  //         TEXT, FILE, CURL, MAXL, REGX, PAGE, CTRL, CARG
+  pinfo = ({ txt, file, 1, 1, 20, 0, ctrl, ctrlargs, flags });
+
+  if (pinfo[FILE])
+  { 
+     if (file_size(pinfo[TEXT])<50000)
+     {
+       pinfo[TEXT]=read_file(pinfo[TEXT]);
+       if (!pinfo[TEXT])
+       { 
+         call(ctrl, ctrlargs);
+         return;
+       }  
+       pinfo[FILE]=0;
+     }
+     else if (file_size(pinfo[TEXT])>1048576)
+     {
+	tell_object(this_player()||this_object(),break_string(sprintf(
+               "Die Datei '%s' kann nicht angezeigt werden, da " 
+               "sie groesser als 1 MB ist. %s",pinfo[TEXT],
+               IS_LEARNER(this_player()||this_object())?"":"Bitte verstaendige "
+               "diesbezueglich umgehend einen Magier."),78));
+	call(ctrl,ctrlargs);
+        return;
+     }
+  }
+
+  if(!pinfo[FILE] && pinfo[TEXT][<1..<1] != "\n") pinfo[TEXT] += "\n";
+  pinfo += ({ ([1:0]) });
+  if(!pinfo[FILE]) 
+    while((j = strstr(pinfo[TEXT], "\n", j+1)) != -1)
+  	pinfo[JUNK][++pinfo[MAXL]] = j+1;
+  else
+    while(tmp = read_file(pinfo[TEXT], pinfo[MAXL], MAX_LINE_READ))
+      pinfo[MAXL] += sizeof(explode(tmp, "\n"))+1;
+  pinfo[PAGE] = PAGELENGTH;
+  if(!pinfo[PAGE]) pinfo[FLAG] |= E_CAT;
+  if ((this_interactive() && (j=(int)this_interactive()->QueryProp(P_MORE_FLAGS))) ||
+      (this_player() && interactive(this_player()) &&
+       (j=(int)this_player()->QueryProp(P_MORE_FLAGS))))
+    pinfo[FLAG] |= j;
+  
+  pinfo[CURL] = 1;
+  eval_command(-1, pinfo);
+  return;
+}
+
+
+
+
+
+
diff --git a/std/util/pl_iterator.c b/std/util/pl_iterator.c
new file mode 100644
index 0000000..555a75e
--- /dev/null
+++ b/std/util/pl_iterator.c
@@ -0,0 +1,53 @@
+// MorgenGrauen MUDlib
+/** \file /file.c
+* Kurzbeschreibung.
+* Langbeschreibung...
+* \author <Autor>
+* \date <date>
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow
+#pragma pedantic, range_check
+#pragma warn_deprecated
+
+private void check_all_player(mapping allplayer, closure check_cl,
+                             closure finish_cl, int res,
+                             varargs mixed extra)
+{
+  // offenbar fertig mit allen Spielern, Aufrufer informieren.
+  if (!sizeof(allplayer))
+  {
+    funcall(finish_cl, extra...);
+    return;
+  }
+
+  string dir=m_indices(allplayer)[0];
+  string *pls=allplayer[dir];
+  foreach(string pl: &pls)
+  {
+    if (get_eval_cost() < res)
+      break; // spaeter weitermachen
+    catch(funcall(check_cl, pl, extra...) ; publish);
+    pl = 0;
+  }
+  pls-=({0});
+  allplayer[dir] = pls;
+
+  if (!sizeof(allplayer[dir]))
+    m_delete(allplayer,dir);
+
+  call_out(#'check_all_player,2, allplayer, check_cl, finish_cl, res, extra...);
+}
+
+protected int start_player_check(closure check_cl, closure finish_cl, int res,
+                             varargs mixed extra)
+{
+  res ||= 1250000;
+  mapping allplayer=(mapping)master()->get_all_players();
+  call_out(#'check_all_player,2, allplayer, check_cl, finish_cl, res, extra...);
+  return 1;
+}
+
diff --git a/std/util/rand-glfsr.c b/std/util/rand-glfsr.c
new file mode 100644
index 0000000..8bd624c
--- /dev/null
+++ b/std/util/rand-glfsr.c
@@ -0,0 +1,155 @@
+/* Dieses File implementiert einen Pseudo-Zufallszahlengenerator mit einem
+ * Linear Feedback Shift Register in der Galois-Variante.
+ * Dieses PRNG ist schlechter und viel ineffizienter als der im Driver
+ * eingebaute.
+ * Die einzige sinnvolle Anwendung ist, wenn man aus irgendeinem Grund das
+ * seed selber waehlen muss, damit man die Sequenz von Pseudozufall immer
+ * wieder reproduzieren kann.
+ *
+ * Das Seed von der Blueprint wird vermutlich staendig veraendert (d.h.
+ * verlasst euch nicht drauf, dass es konstant bleibt), wollt ihr eine
+ * 'private' Instanz mit eurem Seed, clont das Objekt (aber verliert den Clone
+ * nicht).
+ *
+ * Es wird standardmaessig ein Polynom fuer eine Periodenlaenge von 2^32 - 1
+ * verwendet.
+ * 
+ * Im Netz finden sich Infos ueber LFSRs und Tabellen fuer maximum-length
+ * feedback polynomials (M-Sequence Feedback Taps), falls jemand braucht.
+ *
+*/
+#pragma strong_types,rtt_checks,pedantic
+
+#include <tls.h>
+
+// Default-Polynom ist das:
+// x^32 + x^31 + x^28 + x^27 + x^24 + x^23 + x^20 + x^19 + x^16 + x^15 + x^12
+//      + x^11 + x^8 + x^7 + x^5 + x^3 + 1
+// Taps: 32, 31, 28, 27, 24, 23, 20, 19, 16, 15, 12, 11, 8, 7, 5, 3
+// Binary: 110011001100110011001100110101000
+#define DEFAULTP 0x1999999a8
+// Andere gebraeuchliche:
+// x^32 + x^30 + x^26 + x^25 + 1,
+// Taps 32, 30, 26, 25
+// Binary: 101000110000000000000000000000000
+//#define DEFAULTP 0x146000000
+// x^16 + x^14 + x^13 + x^11 + 1
+// Taps 16, 14, 13, 11
+// Binary: 1011010000000000
+//#define DEFAULTP 0xB400
+
+private int polynom = DEFAULTP;
+private int state = 2553647223;
+//private int period;
+
+public varargs void init(int seed, int newp)
+{
+  if (!seed)
+    raise_error("Illegal seed 0\n");
+  // Es darf nur ein 32 bit breiter Seed verwendet werden. Die oberen 32 bit
+  // werden mit den unteren 32 bit XOR-rt (also, einmal die oberen 32 shiften
+  // und einmal die oberen 32 wegmaskieren.
+  seed = ((seed>>32) & 0xffffffff) ^ (seed & 0xffffffff);
+  //printf("Seed: %064b - 0x%x\n",seed,seed);
+  
+  if (!seed)
+    raise_error("Illegal reduced seed: 0\n");
+
+  state = seed;
+  polynom = newp || DEFAULTP;
+}
+
+public void InitWithUUID(string uuid)
+{
+  string md5hash = hash(TLS_HASH_MD5, uuid);
+  int seed;
+  // 8 Bytes aus dem Hash ermitteln
+  foreach(int b: 16)
+  {
+    // Jeweils zwei Zeichen herausschneiden und als Hexadezimalzahl
+    // interpretieren, was jeweils ein byte (8 bit) gibt. 
+    int tmp = to_int("0x"+md5hash[b*2..b*2+1]);
+    // diese werden dann in eine 64 bit breite Zahl zusammengefasst. Das
+    // gerade ermittelte Byte wird nach links geshiftet und mit dem, was da
+    // ggf. schon steht, XORred. Ist der int voll, faengt man wegen Modulo 64
+    // wieder rechts an.
+    //printf("S: %064b - %08b\n",seed,tmp);
+    seed = seed ^ (tmp << ((b*8)%64));
+    //printf("S: %064b - 0x%x\n",seed, seed);
+  }
+  //printf("Seed: 0x%x - %b\n",seed,seed);
+  init(seed);
+}
+
+public int nextbit()
+{
+  // Get LSB (i.e., the output bit).
+  int lsb = state & 1;
+  // Shift register by one bit
+  state >>= 1;
+  // Apply a toggle mask, which flips all the tap bits, _if_ the output bit is
+  // 1. The mask has 1 at bits corresponding to taps, 0 elsewhere. In other
+  // words, the polynom from above.
+  if (lsb)
+      state ^= polynom;
+  // debug check ;-)
+  if (!state)
+    raise_error("State must not be zero, but it is.\n");
+  //++period; 
+  //printf("State: %032b (Period: %d)\n",state,period);
+  return lsb;
+}
+
+public int nextbits(int count)
+{
+  int result;
+  if (count>64)
+    raise_error(sprintf("Count is %d, but must be <= 64.\n",count));
+  foreach(int i: count)
+    result = (result << 1) | nextbit();
+  return result;
+}
+
+public int nextbyte()
+{
+  return nextbits(8);
+}
+
+public int random(int n)
+{
+  int rnd = nextbits(32);
+  //generates a random number on [0,1)-real-interval 
+  float tmp = rnd * (1.0/4294967296.0);
+  // Skalieren auf [0,n)
+  return to_int(tmp * n);
+}
+
+// Just skip some bits and discard them.
+public void skipbits(int count)
+{
+  foreach(int i: count)
+    nextbit();
+}
+
+
+public void dumpstream(string file, int bytes)
+{
+  int *stream = allocate(bytes);
+  foreach(int i: bytes)
+  {
+    stream[i] = nextbits(8);
+  }
+  write_file(file, sprintf("%@c",stream));
+}
+
+public int Configure(int data)
+{
+  if (!data)
+    return state;
+  
+  if (!intp(data))
+    return 0;
+  state = data;
+  return 1;
+}
+
diff --git a/std/util/ringbuffer.c b/std/util/ringbuffer.c
new file mode 100644
index 0000000..19338e1
--- /dev/null
+++ b/std/util/ringbuffer.c
@@ -0,0 +1,210 @@
+// MorgenGrauen MUDlib
+/** \file /std/util/ringbuffer.c
+* Implmentiert einen Ringbuffer in LPC.
+* Ab und an hat man den Fall, dass man Ereignisse/Meldungen o.ae. hat, von
+* denen man immer genau die letzten x gespeichert und abrufbar vorhalten
+* moechte. Dieses File stellt eine Datenstruktur und dazugehoerige
+* Verwaltungsfunktionen zu Verfuegung, mit denen dies effizient moeglich ist.
+* Insbesondere umgehen diese das staendige Slicing von Arrays, mit denen
+* haeufig Ringpuffer gebaut werden. Hierbei wird das Schreiben in den Puffer
+* billiger, das Abrufen der Daten allerdings teurer. Wird der Puffer haeufiger
+* abgefragt als beschrieben, ist dies keine effiziente Loesung.
+* 
+* \author Zesstra
+* \date 22.05.2008
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_inherit
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+#include <util/ringbuffer.h>
+
+/** Standardgroesse fuer Ringbuffer.
+  */
+#define MAXCOUNT 30
+
+
+/** Struktur, die einen Ringpuffer speichert.
+  Der Puffer selber ist im wesentlichen ein Array. Hinzu kommt noch ein
+  Zaehler, der den naechsten zu ueberschreibenden Wert indiziert sowie einen
+  Modus, der die Reihenfolge beim Abrufen beshreibt:
+  MODE_FIFO: First-in-First-out (default)
+  MODE_LIFO: Last-in-First-out
+  */
+struct std_ringbuffer {
+    mixed rbuffer;
+    int mode;
+    int next;
+};
+
+/** Erzeugt einen neuen Ringbuffer und liefert ihn zurueck.
+  Die Funktion erzeugt einen neuen, leeren Ringbuffer der Groesse \a size
+  und mit Ausgabemodus \a newmode und liefert die entsprechende
+  Ringbuffer-Struktur zurueck.
+  \attention Die zurueckgelieferte Datenstruktur bitte nicht per Hand
+  manipulieren.
+  \param[in] size Groesse des zu erzeugenden Ringbuffers. Default: 30
+  \param[in] newmode Ausgabemodus des Ringbuffers: \a MODE_FIFO oder
+  \a MODE_LIFO. Default: MODE_FIFO
+  \return Der erzeugte Ringbuffer (struct vom Typ std_ringbuffer).
+  \sa ResizeRingBuffer, RingBufferPut, RingBufferGet
+  */
+protected struct std_ringbuffer CreateRingBuffer(int size, int newmode) {
+  
+  struct std_ringbuffer buffer = (<std_ringbuffer>
+       rbuffer: allocate(size||MAXCOUNT, 0), 
+       mode: newmode || MODE_FIFO, 
+       next: 0 );
+
+  // Bei LIFO von oben anfangen.
+  if (newmode==MODE_LIFO)
+    buffer->next = sizeof(buffer->rbuffer);
+  
+  return buffer;
+}
+
+/** Schreibt einen neuen Wert in den Ringbuffer.
+  Diese Funktion schreibt einen neuen Wert in den Ringbuffer \a buffer und
+  loescht dabei ggf. den aeltesten Wert im Puffer.
+  \param[in,out] buffer zu aendernder Ringbuffer
+  \param[in] val hinzuzufuegender Wert
+  \sa CreateRingBuffer, ResizeRingBuffer, RingBufferGet
+  */
+protected void RingBufferPut(struct std_ringbuffer buffer, mixed val) {
+  int next = buffer->next;
+  // je nach Ausgabemodus von oben nach unten oder von unten nach oben die
+  // Werte reinschreiben.
+  switch(buffer->mode) {
+    case MODE_LIFO:
+      // next ist hier eigentlich ein 'last'. Dekrementieren und dann zum
+      // Indizieren benutzen.
+      buffer->rbuffer[--next] = val;
+      // wenn man gerade in Element 0 geschrieben hat, ist naechstes Element
+      // sizeof()-1, d.h. sizeof() als next vermerken.
+      next ||= sizeof(buffer->rbuffer);
+      break;
+    default:
+      // Wert schreiben und next inkrementieren.
+      buffer->rbuffer[next++] = val;
+      // wird sizeof() erreicht, ist das naechste Element 0. Etwas schneller
+      // waere ein Modulo mit der Buffergroesse direkt bei der Indizierung
+      // oben, aber dann kann es u.U. durch einen numerischen Overflow buggen.
+      if (next==sizeof(buffer->rbuffer))
+	next = 0;
+  }
+  buffer->next = next;
+}
+
+/** Liefert ein Array mit allen Werten des Ringbuffers \a buffer.
+  Das zurueckgelieferte Array enthaelt alle Daten des Ringbuffers in der
+  beim Erstellen des Puffers gewuenschten Reihenfolge (s. MODE_FIFO,
+  MODE_LIFO).
+  Ist der Puffer noch nicht vollstaendig gefuellt, enthalten die bisher noch
+  nie beschriebenen Elemente 0.
+  \param[in] buffer Ringpuffer, dessen Daten ausgeben werden sollen.
+  \return Array mit Daten des Puffers.
+  \sa CreateRingBuffer, RingBufferPut, ResizeRingBuffer
+  */
+protected mixed RingBufferGet(struct std_ringbuffer buffer) {
+  int size = sizeof(buffer->rbuffer);
+  int next = buffer->next;
+  mixed rbuffer = buffer->rbuffer;
+
+  switch(buffer->mode) {
+    case MODE_LIFO:
+      // der hintere Teil enthaelt die neueren Daten. Die kommen zuerst.
+      // Wenn next == sizeof(), dann kann das interne Array einfach kopiert
+      // werden.
+      if (next == size)
+	return copy(rbuffer);
+      else
+	return rbuffer[next .. size-1] + rbuffer[0 .. next-1];
+    default:
+      // der vordere Teil enthaelt die neueren Daten. Also zuerst den hinteren
+      // ausgeben, dann den vorderen.
+      // Wenn next 0, kann das interne Array einfach kopiert werden. Sonst
+      // muss es zusammengesetzt werden.
+      if (next)
+	return rbuffer[next .. size-1] + rbuffer[0 .. next-1];
+      else
+	return copy(rbuffer);
+  }
+  return 0;
+}
+
+/** Erzeugt einen neuen Ringbuffer der Groesse \a size und dem gleichen Modus
+ * wie \a buf, der dieselben Daten enthaelt wie \a buf.
+ * Diese Funktion erzeugt einen neuen Ringbuffer und kopiert alle Daten aus
+ * dem alten Puffer in den neuen um.
+ \param[in] buf alter Ringbuffer
+ \param[in] size gewuenschte neue Groesse
+ \return Liefert neuen Ringbuffer mit gewuenschten Groesse.
+ \sa CreateRingBuffer, RingBufferPut, RingBufferGet
+ \attention Wird der Ringbuffer verkleinert, kommt es zum Verlust der
+ aeltesten Daten, die nicht mehr in den neuen hineinpassen.
+ \todo Effizientere Implementation.
+ */
+protected struct std_ringbuffer ResizeRingBuffer(struct std_ringbuffer buf, 
+                                                 int size) {
+  // neuen Ringbuffer anlegen.
+  struct std_ringbuffer nbuf = CreateRingBuffer(size, buf->mode);
+  // und alle daten aus dem alten wieder reinstopfen.
+  // ja, das geht effizienter, hab ich gerade aber keinen Bock drauf. Die
+  // Funktion wird vermutlich eh sehr selten gerufen.
+  foreach(mixed val: RingBufferGet(buf))
+    RingBufferPut(nbuf, val);
+  // neuen Buffer zurueckgeben. Der alte bleibt unveraendert!
+  return nbuf;
+}
+
+/* halbfertige Implementation mit Arrays.
+#define RBUFFER   0
+#define MODE      1
+#define COUNTER   2
+
+protected mixed CreateRingBuffer(int size, int mode) {
+  mixed buffer = ({ allocate(size||MAXCOUNT, 0), 
+                    mode || MODE_FIFO, 
+	            0 });
+  if (mode==MODE_LIFO)
+    buffer[COUNTER] = sizeof(buffer[RBUFFER]);
+  return buffer;
+}
+
+protected void RingBufferPut(mixed buffer, mixed val) {
+  int counter = buffer[COUNTER];
+  switch(mode) {
+    case MODE_LIFO:
+      rbuffer[--counter] = val;
+      counter ||= sizeof(rbuffer);
+      break;
+    default:
+      rbuffer[(counter++)%sizeof(rbuffer)] = val;
+  }
+  buffer[COUNTER]=counter;
+}
+
+protected mixed RingBufferGet(mixed buffer) {
+  int size=sizeof(rbuffer);
+
+  switch(mode) {
+    case MODE_LIFO:
+      return rbuffer[counter..size] + rbuffer[0..counter-1];
+    default:
+      if (counter%size)
+	return rbuffer[(counter%size) .. size-1] 
+	       + rbuffer[0..(counter%size)-1];
+      else
+	return copy(rbuffer);
+  }
+}
+
+protected mixed test() {return rbuffer;}
+*/
diff --git a/std/util/testedit.c b/std/util/testedit.c
new file mode 100644
index 0000000..f31f6ed
--- /dev/null
+++ b/std/util/testedit.c
@@ -0,0 +1,25 @@
+// MorgenGrauen MUDlib
+//
+// util/testedit.c -- Tragbarer Editor
+//
+// $Id: testedit.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/thing";
+inherit "/std/util/ex";
+
+#include <properties.h>
+
+void create()
+{
+  ::create();
+  AddId("ex");
+  SetProp(P_NAME, "Editor");
+  SetProp(P_SHORT, "Ein Testeditor");
+  SetProp(P_LONG, "Testeditor: Kommando: ex\n");
+  AddCmd("ex", "ex");
+}
diff --git a/std/virtual/v_compiler.c b/std/virtual/v_compiler.c
new file mode 100644
index 0000000..80eb370
--- /dev/null
+++ b/std/virtual/v_compiler.c
@@ -0,0 +1,165 @@
+// MorgenGrauen MUDlib
+//
+// virtual/v_compiler.c -- a general virtual compiler object
+//
+// $Id: v_compiler.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+// principle:
+//  - inherit this object into your own 'virtual_compiler.c'
+//  - customize Validate() and CustomizeObject() for you own sake
+//  
+//  * Validate() checks if a room filename given as argument (without path)
+//    is valid and returns this filename with stripped '.c'!!
+//  * CustomizeObject() uses the previous_object()->Function() strategy to
+//    customize the standard object (for example to set a description)
+//
+// Properties: P_STD_OBJECT, P_COMPILER_PATH
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/properties";
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <defines.h>
+#include <v_compiler.h>
+#include <exploration.h>
+#include <sys_debug.h>
+#include <living/description.h> //fuer P_PARA
+
+// Der VC braucht das 'alte' object_name()-basierte BLUE_NAME, da sonst das
+// Konfigurieren der von einem VC-Objekt geclonten Clones via
+// CustomizeObject() nicht funktioniert (load_name() ermittelt den Namen des
+// VC-Standardobjektes)
+#ifdef BLUE_NAME
+#undef BLUE_NAME
+#endif
+#define BLUE_NAME(ob) (explode(object_name(ob),"#")[0])
+
+private nosave string last_loaded_file;
+private nosave mapping objects;
+
+void create()
+{
+  ::create();
+  seteuid(getuid());
+  SetProp(P_STD_OBJECT, "/std/room");
+  SetProp(P_COMPILER_PATH, sprintf("/%s/",
+        implode(old_explode(object_name(this_object()), "/")[0..<2], "/")));
+  SetProp(P_PARA, ({}) ); // keine Para-VC-Objekte
+  objects = ([]);
+}
+
+// von den erbenen VCs zu ueberschreiben...
+// TODO: aus Standardobjekt entfernen, weil durch P_PARA und QueryValidObject
+// obsolet. 
+int NoParaObjects() { return 0; }
+
+string Validate(string file)
+{
+  if(!file) return 0;
+  if(file[<2..] == ".c") file = file[0..<3];
+  EPMASTER->PrepareVCQuery(file);
+  return file;
+}
+
+// Die Funktion bekommt einen Objektnamen uebergeben und muss entscheiden, ob
+// dieser VC dafuer zustaendig ist, das Objekt zu generieren. Jeder Wert != 0
+// zaehlt als 'zustaendig'. Es ist eine Art generalisiertem Validate(). Fuer
+// maximale Nuetzlichkeit muss diese Funktion von den erbenden VCs
+// ueberschrieben werden.
+public int QueryValidObject(string oname) {
+    string fname, path, *pelem;
+    int para;
+    mixed ppara;
+
+    //erstmal Validate fragen
+    pelem=explode(oname,"/");
+    fname=pelem[<1];
+    if (!fname=Validate(fname))
+        return(0); //nicht zustaendig
+    // nicht im richtigen Pfad?
+    path=sprintf("%s/",implode(pelem[0..<2],"/"));
+    if (path!=QueryProp(P_COMPILER_PATH))
+        return(0);
+    // Para-Objekt?
+    if (sscanf(fname,"%s^%d",fname,para) > 1) {
+        if (NoParaObjects())
+            return(0); //direkt zurueck, keine Para-Objekte
+        // bestimmte Para-Dimensionen explizit erlaubt? (Wenn P_PARA nicht
+        // gesetzt ist, sind alle erlaubt!)
+        if (ppara=QueryProp(P_PARA)) {
+            if (pointerp(ppara) && member(ppara,para)!=-1)
+                return(1);
+            else if (intp(para) && ppara==para)
+                return(1);
+            // P_PARA gesetzt, aber gewuenschtes Para nicht enthalten...
+            else return(0);
+        }
+    }
+    return(1); //fall-through, offenbar zustaendig.
+}
+
+mixed CustomizeObject()
+{
+  string file;
+
+  if(!clonep(previous_object()))
+    return Validate(explode(BLUE_NAME(previous_object()), "/")[<1]);
+  if(stringp(last_loaded_file)) file = last_loaded_file;
+  else file = Validate(explode(BLUE_NAME(previous_object()), "/")[<1]);
+  if(!file) return 0;
+  last_loaded_file = 0;
+  return file;
+}
+
+// add a new object to the object list if it compiles
+private mixed AddObject(string file)
+{
+  object ob;
+  string err;
+
+  // clean up the object list
+  objects = filter_indices(objects, function int (string f) {
+      return (objectp(objects[f])); } );
+
+  last_loaded_file = file;
+  // register new object
+  if(ob = clone_object(QueryProp(P_STD_OBJECT)))
+    objects[file] = ob;
+  return ob;
+}
+
+// try to create an object for the wanted file
+mixed compile_object(string file)
+{
+  // validate if the file name is a correct one
+  if(file = Validate(file))
+    return AddObject(file);
+  return 0;
+}  
+
+// return all cloned virtual objects
+mixed QueryObjects()
+{
+  return m_values(objects)-({0});
+}
+
+// clean up rooms that have not been destructed yet
+int remove() {
+
+  if(!mappingp(objects)) return 0;
+
+  //for(ob = QueryObjects(); sizeof(ob); ob = ob[1..])
+  foreach(object ob: QueryObjects()) {
+      ob->remove();
+      if(objectp(ob)) destruct(ob);
+  }
+  return 1;
+}
+
diff --git a/std/weapon.c b/std/weapon.c
new file mode 100644
index 0000000..7c1f97c
--- /dev/null
+++ b/std/weapon.c
@@ -0,0 +1,54 @@
+// MorgenGrauen MUDlib
+//
+// weapon.c -- weapon standard object
+//
+// $Id: weapon.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+//#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/properties";
+inherit "/std/thing/language";
+inherit "/std/thing/commands";
+inherit "/std/thing/restrictions";
+inherit "/std/thing/light";
+inherit "/std/weapon/moving";
+inherit "/std/weapon/combat";
+inherit "/std/weapon/description";
+inherit "/std/thing/envchk";
+
+//#define NEED_PROTOTYPES
+
+#define <thing/properties.h>
+
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+
+void create()
+{
+  seteuid(getuid());
+  properties::create();
+  commands::create();
+  light::create();
+  restrictions::create();
+  combat::create();
+  description::create();
+  envchk::create();
+  AddId(({"Ding","waffe"}));
+}
+
+/*
+void init()
+{
+  commands::init();
+  description::init();
+}
+*/
+
+void reset()  // Man kann in ALLEN Standardobjekten ::reset aufrufen!
+{ }
+
diff --git a/std/weapon/combat.c b/std/weapon/combat.c
new file mode 100644
index 0000000..cb10ff5
--- /dev/null
+++ b/std/weapon/combat.c
@@ -0,0 +1,819 @@
+// MorgenGrauen MUDlib
+//
+// weapon/combat.c -- combat part of the weapon standard object
+//
+// $Id: combat.c 9425 2016-01-02 18:19:40Z Zesstra $
+
+#include <sys_debug.h>
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/commands.h>
+#include <thing/description.h>
+#include <properties.h>
+#include <language.h>
+#include <combat.h>
+#include <attributes.h>
+#include <defines.h>
+#include <moving.h>
+#include <new_skills.h>
+
+// Globale Variablen
+               int     ftime, flaw;
+private nosave int     logged;
+private nosave closure defend_cl, hit_cl;
+
+void create() 
+{
+    // Variablen initialisieren
+    logged=ftime=flaw=0;
+    defend_cl=0;
+    hit_cl=0;
+
+    // Einige Grundwerte setzen
+    SetProp(P_WEAPON_TYPE, WT_CLUB);
+    SetProp(P_DAM_TYPE, ({DT_BLUDGEON}));
+    SetProp(P_NR_HANDS, 2);
+    SetProp(P_PARRY,PARRY_NOT);
+    SetProp(P_AC,0);
+    Set(P_RESTRICTIONS,([]),F_VALUE);
+    Set(P_LAST_USE,time(),F_VALUE);
+
+    // Einige Properties sollten nicht von aussen gesetzt werden koennen
+    Set(P_PARRY,   PROTECTED, F_MODE_AS);
+    Set(P_WIELDED, PROTECTED, F_MODE_AS);
+    Set(P_LAST_USE,PROTECTED, F_MODE_AS);
+    
+    // Diese kann von aussen gesetzt werden (noch), aber bitte nur ueber die
+    // hier definierte Setmethode.
+    Set(P_DAMAGED, PROTECTED, F_MODE_AS);
+
+    // Eine Waffe benoetigt Kommandos, mit denen man sie zuecken 
+    // und wegstecken kann
+    AddCmd( ({"zueck","zuecke","zieh","ziehe"}), "wield" );
+    AddCmd( ({"steck","stecke"}), "unwield" );
+}
+
+// aktuelles Lebewesen, was diese Waffe zur Zeit gezueckt hat.
+public object QueryUser()
+{
+  return QueryProp(P_WIELDED);
+}
+
+/*
+ * Ausgabe einer Meldung beim Zuecken geht nur an Spieler, nicht an NPC.
+ * Die Umgebung bekommt natuerlich immer eine Meldung.
+ */
+void doWieldMessage()
+{
+  string *str, s1;
+  
+  if(QueryProp(P_WIELD_MSG))  // Ist eine WieldMsg gesetzt?
+  {
+    if(closurep(QueryProp(P_WIELD_MSG)))  // Sogar als eigene Fkt.?
+    {
+      str = funcall(QueryProp(P_WIELD_MSG),this_player());
+
+      if(interactive(this_player()))
+      {
+        this_player()->ReceiveMsg(str[0],
+            MT_NOTIFICATION|MSG_BS_LEAVE_LFS, MA_WIELD, 0,
+            this_player());
+      }
+    
+      if ( objectp(environment()) && objectp(environment(environment())) )
+          send_room(environment(environment()),
+              str[1],
+              MT_LOOK|MSG_BS_LEAVE_LFS,
+              MA_WIELD, 0, ({this_player()}), environment());
+      return;
+    }
+    else if(interactive(this_player()))
+    {
+      s1 = replace_personal(sprintf(QueryProp(P_WIELD_MSG)[0],"@WEN2"),
+		      ({this_player(),this_object()}), 1);
+
+      this_player()->ReceiveMsg(s1,
+          MT_NOTIFICATION|MSG_BS_LEAVE_LFS, MA_WIELD, 0,
+          this_player()); 
+    }
+
+    // Abwaertskompatibel: Das erste %s wird zu WEN1, das zweite zu WEN2
+    s1 = replace_personal(sprintf(QueryProp(P_WIELD_MSG)[1],"@WEN1","@WEN2"),
+		    ({this_player(), this_object()}), 1);
+        
+    if ( objectp(environment()) && objectp(environment(environment())) )
+        send_room(environment(environment()),
+              s1,
+              MT_LOOK|MSG_BS_LEAVE_LFS,
+              MA_WIELD, 0, ({this_player()}), environment());
+    return;
+  }
+  /*
+   * Keine WieldMsg gesetzt, Ausgabe der Default-Meldungen.
+   */
+  else if(interactive(this_player()))
+  {
+    this_player()->ReceiveMsg(
+        "Du zueckst "+name(WEN,1)+".",
+        MT_NOTIFICATION, MA_WIELD, 0, this_player());
+  }
+  
+  //s.o. ersetzt durch tell_room()
+  //say(break_string(this_player()->Name(WER)+" zueckt "
+  // +name(WEN,0)+".",78),({ this_player() }));
+  if ( objectp(environment()) && objectp(environment(environment())) )
+      send_room(environment(environment()),    
+          this_player()->Name(WER)+" zueckt "+name(WEN,0)+".",
+          MT_LOOK,
+          MA_WIELD, 0, ({this_player()}), environment());
+}
+
+/*
+ * Ausgabe einer Meldung beim Wegstecken geht nur an Spieler, nicht an NPC.
+ * Die Umgebung bekommt immer eine Meldung.
+ */
+void doUnwieldMessage(object wielded_by)
+{
+  string *str,s1;
+
+  if(!objectp(wielded_by))  // Hoops! Gar nicht gezueckt? Abbruch!
+  {
+    return;
+  }
+
+  if(QueryProp(P_UNWIELD_MSG))  // Hier gibt es eine UnwieldMsg.
+  {
+    if(closurep(QueryProp(P_UNWIELD_MSG)))  // Sogar als Closure, wow :-)
+    {
+      str = funcall(QueryProp(P_UNWIELD_MSG),wielded_by);
+
+      if(interactive(wielded_by))
+      { 
+        wielded_by->ReceiveMsg(str[0],
+          MT_NOTIFICATION|MSG_BS_LEAVE_LFS, MA_UNWIELD, 0, wielded_by);
+      }
+      if ( objectp(environment()) && objectp(environment(environment())) )
+          send_room(environment(environment()),
+              str[1],
+              MT_LOOK|MSG_BS_LEAVE_LFS,
+              MA_UNWIELD, 0, ({wielded_by}), environment());
+      return;
+    }
+  
+    else if(interactive(wielded_by))
+    {
+      s1 = replace_personal(sprintf(QueryProp(P_UNWIELD_MSG)[0],"@WEN2"),
+		      ({this_player(),this_object()}), 1); 
+      wielded_by->ReceiveMsg(s1,
+          MT_NOTIFICATION|MSG_BS_LEAVE_LFS, MA_UNWIELD, 0, wielded_by);
+    }
+
+    s1 = replace_personal(sprintf(QueryProp(P_UNWIELD_MSG)[1],"@WEN1","@WEN2"),
+		    ({wielded_by, this_object()}), 1);
+    
+    if ( objectp(environment()) && objectp(environment(environment())) )
+          send_room(environment(environment()),
+              s1,
+              MT_LOOK|MSG_BS_LEAVE_LFS,
+              MA_UNWIELD, 0, ({wielded_by}), environment());
+    return;
+  }
+  /*
+   * Keine UnwieldMsg, also Default-Meldungen ausgeben.
+   */
+  else if(interactive(wielded_by))
+  {
+    wielded_by->ReceiveMsg(
+        "Du steckst "+name(WEN,1)+" zurueck.",
+        MT_NOTIFICATION, MA_UNWIELD, 0, wielded_by);
+  }
+  if ( objectp(environment()) && objectp(environment(environment())) )
+      send_room(environment(environment()),
+              wielded_by->Name(WER) +" steckt "+name(WEN,0)+" zurueck.",
+              MT_LOOK,
+              MA_UNWIELD, 0, ({wielded_by}), environment()); 
+}
+
+// Diese Funktion wird aufgerufen, wenn die Waffe wirklich gezueckt wird
+protected void InformWield(object pl, int silent)
+{
+    return;
+}
+     
+// Diese Funktion wird aufgerufen, wenn die Waffe wirklich weggesteckt
+// wird
+protected void InformUnwield(object pl, int silent) 
+{
+    return;
+}
+
+
+// wield_me soll knallen. 
+varargs int wield_me(int silent) 
+{
+  raise_error("wield_me() ist veraltet. Bitte nutze DoWield()\n");
+  return 1;
+}
+
+// Die Funktion, die das eigentliche Zuecken durchfuehrt.
+varargs int DoWield(int silent) 
+{   int     dex, parry;
+    closure cl;
+    mixed   res;
+  
+    // Man kann nur Waffen zuecken, die man auch im Inventory hat.
+    if (environment()!=PL) 
+    {
+        _notify_fail( break_string(
+            "Du musst "+name(WEN,1)+" schon erst nehmen!",78) );
+        return 0;
+    }
+
+    // Eine gezueckte Waffe kann man natuerlich nicht nochmal zuecken
+    if (QueryProp(P_WIELDED)) 
+    {
+        notify_fail("Das hast Du schon gemacht!\n");
+        return 0;
+    }
+
+    // Waffen, die ein oder mehrere Attribut veraendern und gegen
+    // das gesetzte Limit verstossen, haben keine Wirkung bezueglich der
+    // Attribute.
+    if ( mappingp(res=QueryProp(P_M_ATTR_MOD)) && PL->TestLimitViolation(res) )
+    {
+        write(break_string(
+            "Irgendetwas an Deiner Ausruestung verhindert, dass Du Dich mit "+
+            name(WEM,1)+" so richtig wohl fuehlst.",78));
+    }
+
+    // Ueber P_RESTRICTIONS kann man einige Restriktionen definieren, ohne
+    // gleich auf eine WieldFunc zurueckgreifen zu muessen.
+    // Die Auswertung erfolgt ueber den RestrictionChecker
+    if ( (res=QueryProp(P_RESTRICTIONS)) && mappingp(res) &&
+         (res=(string)call_other("/std/restriction_checker","check_restrictions",
+             PL,res)) && stringp(res) ) 
+    {
+        notify_fail(res);
+        return 0;
+    }
+
+    parry=(int)QueryProp(P_PARRY);
+    dex=(int)PL->QueryAttribute(A_DEX);
+
+    // Testen, ob der Spieler die noetige Geschicklichkeit besitzt, um
+    // mit dieser (Parier)Waffe umgehen zu koennen
+    if ( ((parry<PARRY_ONLY) && ((dex+8)*10)<QueryProp(P_WC)) || 
+         ((parry>PARRY_NOT)  && ((dex+5)*2 )<QueryProp(P_AC)) )
+    {
+        notify_fail(
+            "Du bist nicht geschickt genug, um mit dieser Waffe umzugehen.\n");
+        return 0;
+    }
+
+    // Eine Gezueckte Waffe muss natuerlich erst mal weggesteckt werden.
+    if ( (parry<PARRY_ONLY) && objectp(res=(object)PL->QueryProp(P_WEAPON)) )
+    {
+        if ( (res->DoUnwield(silent)) && !((object)PL->QueryProp(P_WEAPON)) )
+        {
+            // Wenn die alte Waffe weggesteckt werden konnte, nochmal
+            // versuchen zu zuecken
+            return DoWield(silent);
+        }
+        else 
+        {
+            // Sonst Meldung ausgeben
+            notify_fail("Das geht nicht, solange Du noch eine andere "+
+                 "Waffe gezueckt hast.\n");
+            return 0;
+        }
+    }
+    // Das gleiche gilt natuerlich fuer Parierwaffen
+    if ( (parry>PARRY_NOT) && objectp(res=(object)PL->QueryProp(P_PARRY_WEAPON)) )
+    {
+        if ( (res->DoUnwield(silent)) && !(PL->QueryProp(P_PARRY_WEAPON)) )
+        {
+            // Wenn die alte Parierwaffe weggesteckt werden konnte, nochmal
+            // versuchen zu zuecken
+            return DoWield(silent);
+        }
+        else 
+        {
+            // Sonst Meldung ausgeben
+            notify_fail("Das geht nicht, solange Du noch eine andere "+
+                 "Paierwaffe gezueckt hast.\n");
+            return 0;
+        }
+    }
+
+    // Ist eine WieldFunc gesetzt, wird diese aufgerufen.
+    if (objectp(res=QueryProp(P_WIELD_FUNC)) 
+	&& !(res->WieldFunc(ME,silent,environment())))
+    {
+        // Eine Meldung sollte schon von der WieldFunc ausgegeben werden.
+        return 1;
+    }
+
+    // Die zulaessigen Hoechstwerte duerfen natuerlich nicht
+    // ueberschritten werden.
+    if ( (parry<PARRY_ONLY) && (QueryProp(P_WC)>MAX_WEAPON_CLASS) )
+    {
+        notify_fail(
+            "Ungueltige Waffenklasse, bitte Erzmagier verstaendigen.\n");
+        return 0;
+    }
+    if ( (parry>PARRY_NOT) && (QueryProp(P_AC)>MAX_PARRY_CLASS) )
+    {
+        notify_fail(
+            "Ungueltige Parierklasse, bitte Erzmagier verstaendigen.\n");
+        return 0;
+    }
+
+    // Testen, ob der Zuecker genug Haende frei hat.  
+    if (!(PL->UseHands(ME,QueryProp(P_NR_HANDS))))
+    {
+        notify_fail("Du hast keine Hand mehr frei.\n");
+        return 0;
+    }
+
+    // Ok, jetzt ist alles klar, die (Parier)Waffe wird gezueckt
+    SetProp(P_WIELDED, PL);
+    SetProp(P_EQUIP_TIME,time());
+
+    cl=symbol_function("SetProp",PL);
+
+    if (parry<PARRY_ONLY)
+    {
+        // Dieses Objekt als Waffe setzen
+        funcall(cl,P_WEAPON, ME );
+    }
+    if (parry>PARRY_NOT)
+    {
+        // Dieses Objekt als Parierwaffe setzen
+        funcall(cl,P_PARRY_WEAPON, ME );
+    }
+
+    // Waffen koennen Attribute aendern/blockieren. Also muessen diese
+    // nach dem Zuecken aktualisiert werden
+    PL->register_modifier(ME);
+    PL->UpdateAttributes();
+
+    // P_TOTAL_AC/P_TOTAL_WC im Spieler aktualisieren. Da dort Attribute
+    // eingehen, kann das erst hier gemacht werden.
+    if (parry<PARRY_ONLY)
+    {
+        PL->QueryProp(P_TOTAL_WC);
+    }
+    if (parry>PARRY_NOT)
+    {
+        PL->QueryProp(P_TOTAL_AC);
+    }
+
+    // Zueck-Meldung ausgeben, wenn das silent-Flag nicht gesetzt ist
+    if (!silent)
+    {
+        doWieldMessage();
+    }
+
+    // Alle Waffen werden im awmaster registriert, sobald sie von einem
+    // Spieler gezueckt werden
+    if (!logged && query_once_interactive(PL)) 
+    {
+        call_other("/secure/awmaster","RegisterWeapon",ME);
+        logged=1;
+    }
+
+    // Inform-Funktion aufrufen
+    InformWield(PL,silent);
+
+    // Fertig mit dem Zuecken
+    return 1;
+}
+
+// Die Funktion, die das eigentliche Wegstecken durchfuehrt.
+varargs int DoUnwield(int silent) 
+{   closure cl;
+    int     parry;
+    mixed   res;
+    object  wielded_by;
+
+    // Waffen, die nicht gezueckt sind, kann man natuerlich nicht
+    // wegstecken
+    if (!objectp(wielded_by=QueryProp(P_WIELDED)))
+    {
+        return 0;
+    }
+
+    // Ist eine UnwieldFunc gesetzt, wird diese aufgerufen
+    if ( objectp(res=QueryProp(P_UNWIELD_FUNC)) &&
+         !(res->UnwieldFunc(ME,silent,wielded_by)) ) 
+    {
+        // Eine Meldung muss die UnwieldFunc schon selbst ausgeben.
+        return 1;
+    }
+
+    // Eine verfluchte Waffe kann man natuerlich nicht wegstecken - aber
+    // auch da gibts Ausnahmefaelle, wie z.B. den Tod eines Spielers
+    if ((res=QueryProp(P_CURSED)) && !(silent&M_NOCHECK)) 
+    {
+        if (stringp(res))
+        {
+            // Stand in P_CURSED ein String? Dann diesen ausgeben
+            tell_object(wielded_by, 
+                (res[<1]=='\n' ? res : break_string(res,78)));
+        }
+        else
+        {
+            // Sonst eine Standard-Meldung ausgeben
+            tell_object(wielded_by, break_string(
+                "Du kannst "+name(WEN)+" nicht wegstecken.",78));
+        }
+        return 1;
+    }
+
+    // Ok, jetzt ist alles klar, die (Parier)Waffe wird weggesteckt
+    parry=QueryProp(P_PARRY);
+    cl=symbol_function("SetProp",wielded_by);
+
+    if (parry<PARRY_ONLY)
+    {
+        // Eintrag als Waffe im Spieler loeschen
+        funcall(cl,P_WEAPON, 0);
+    }
+    if (parry>PARRY_NOT)
+    {
+        // Eintrag als Parierwaffe im Spieler loeschen
+        funcall(cl,P_PARRY_WEAPON, 0);
+    }
+
+    // Im Spieler die Zeit setzen, zu der er zuletzt eine Waffe weggesteckt
+    // hat.
+    funcall(cl,P_UNWIELD_TIME,time());
+
+    // Meldung ausgeben, wenn silent-Flag nicht gesetzt ist
+    if (!silent) 
+    {
+        doUnwieldMessage(wielded_by);
+    } 
+
+    // Die Haende, die bisher von der Waffe benutzt wurden, freigeben
+    wielded_by->FreeHands(ME);
+    SetProp(P_WIELDED, 0);
+
+    // Waffen koennen Attribute aendern/blockieren. Also muessen diese
+    // nach dem Wegstecken aktualisiert werden
+    wielded_by->deregister_modifier(ME);
+    wielded_by->UpdateAttributes();
+
+    // P_TOTAL_AC/P_TOTAL_WC im Spieler aktualisieren. Da dort Attribute
+    // eingehen, kann das erst hier gemacht werden.
+    if (parry<PARRY_ONLY)
+    {
+        wielded_by->QueryProp(P_TOTAL_WC);
+    }
+    if (parry>PARRY_NOT)
+    {
+        wielded_by->QueryProp(P_TOTAL_AC);
+    }
+
+    // Inform-Funktion aufrufen
+    InformUnwield(wielded_by, silent);
+
+    // Fertig mit dem Wegstecken
+    return 1;
+}
+
+// Die Funktion, die das "zuecken"-Kommando auswertet
+varargs int wield(string str, int silent) 
+{
+    if ( !stringp(str) ||
+         (query_verb()[0..3]=="zieh" && sscanf(str,"%s hervor",str)!=1) )
+    {
+        return 0;
+    }
+
+    if (!id(str)) 
+    {
+        _notify_fail("Du hast sowas nicht.\n");
+        return 0;
+    }
+
+    return DoWield(silent);
+}
+
+// Die Funktion, die das "wegstecken"-Kommando auswertet
+int unwield(string str) 
+{   int    parry;
+    string dummy;
+
+    // Erstmal die Eingabe auswerten. Ist dies wirklich ein Kommando
+    // zum Wegstecken?
+    if ( !stringp(str) || (sscanf(str,"%s weg",     dummy)!=1  && 
+                           sscanf(str,"%s ein",     dummy)!=1  &&
+                           sscanf(str,"%s zurueck", dummy)!=1 ) )
+    {
+        return 0;
+    }
+
+    str = dummy;
+    parry=QueryProp(P_PARRY);
+
+    // Ist wirklich diese Waffe gemeint?
+    if ( !stringp(str) || !id(str) ||
+         ((parry<PARRY_ONLY)&&((object)PL->QueryProp(P_WEAPON)!=ME)) ||
+         ((parry>PARRY_NOT)&&((object)PL->QueryProp(P_PARRY_WEAPON)!=ME)) )
+    {
+        return 0;
+    }
+
+    // Man kann nur Waffen wegstecken, die man auch bei sich hat
+    if (environment() != PL) 
+    {
+        _notify_fail("Diese Waffe gehoert Dir nicht!\n");
+        return 0;
+    }
+
+    // Und natuerlich geht das auch nur, wenn die Waffe gezueckt ist.
+    if (!QueryProp(P_WIELDED)) 
+    {
+        _notify_fail("Diese Waffe hast Du gar nicht gezueckt ...\n");
+        return 0;
+    }
+
+    return DoUnwield();
+}
+
+// Die Funktion, die den Schaden berechnet, den die Waffe verursacht
+int QueryDamage(object enemy)
+{   int    dam;
+    mixed  hit_func;
+    object wielder;
+
+    // Nur gezueckte Waffen machen Schaden
+    if (!objectp(wielder=QueryProp(P_WIELDED)))
+        return 0;
+
+    // Den Basis-Schaden berechnen. Die Staerke des Benutzers wird
+    // hier beruecksichtigt.
+    dam = (2*QueryProp(P_WC)+10*((int)wielder->QueryAttribute(A_STR)))/3;
+
+    // Wie gut man getroffen hat, wird ueber ein random() simuliert
+    dam = random(1+dam);
+
+    // Ist eine HitFunc gesetzt, dann wird diese ausgewertet. Der 
+    // Rueckgabe-Wert wird zum Schaden addiert
+    if (!hit_cl || !get_type_info(hit_cl,2))
+    {
+        if (objectp(hit_func=QueryProp(P_HIT_FUNC)))
+        {
+            hit_cl=symbol_function("HitFunc",hit_func);
+        }
+    }
+    if (hit_cl && get_type_info(hit_cl,2))
+    {
+        dam += funcall(hit_cl,enemy);
+    }
+
+    // Zeitpunkt der letzten Benutzung ausgeben
+    SetProp(P_LAST_USE,time());
+
+    // Berechneten Schaden zurueckgeben
+    return dam;
+}
+
+// Die Funktion, die bei Parierwaffen den Schutzwert berechnet.
+int QueryDefend(string* dam_type, mixed spell, object enemy) 
+{   int     prot;
+    mixed   def_func;
+    object  pl;
+
+    prot = 0;
+
+    // Ruestungen schuetzen nur gegen physikalischen Schaden
+    if (!spell || (mappingp(spell) && spell[SP_PHYSICAL_ATTACK]))
+    {
+        if (sizeof(filter(dam_type,PHYSICAL_DAMAGE_TYPES)))
+        {
+            prot = random(1+QueryProp(P_AC));
+        }
+    }
+
+    // Ist eine DefendFunc gesetzt, wird diese ausgewertet
+    if (!defend_cl || !get_type_info(defend_cl,2))
+    {
+        if (objectp(def_func=QueryProp(P_DEFEND_FUNC))) 
+        {
+            defend_cl=symbol_function("DefendFunc",def_func);
+        }
+    }
+    //Bei Netztoten keine (zurueckschlagende) DefendFunc
+    if (defend_cl && get_type_info(defend_cl,2) &&
+        objectp(pl=QueryProp(P_WIELDED)) && (!query_once_interactive(pl) ||
+        interactive(pl)) )
+    {
+        // Der Rueckgabewert der DefendFunc wird zum Schutz addiert
+        prot += funcall(defend_cl, dam_type, spell, enemy);
+    }
+
+    // Zeitpunkt der letzten Benutzung ausgeben
+    SetProp(P_LAST_USE,time());
+
+    // Berechneten Schutz zurueckgeben
+    return prot;
+}
+
+// Die Anzahl der von einer Waffe benoetigten Haende darf natuerlich nicht
+// kleiner als 1 sein.
+int _set_nr_hands(int arg)
+{
+    if (!intp(arg) || (arg<1) )
+        return Query(P_NR_HANDS, F_VALUE);
+    return Set(P_NR_HANDS, arg, F_VALUE);
+}
+
+// Der Schadenstyp einer Waffe darf zwar als string angegeben werden, wird
+// intern aber immer als array gespeichert
+mixed _set_dam_type(mixed arg)
+{
+    if (pointerp(arg))
+    {
+        return Set(P_DAM_TYPE, arg, F_VALUE);
+    }
+    else if (stringp(arg))
+    {
+        return Set(P_DAM_TYPE, ({ arg }), F_VALUE);
+    }
+    return Query(P_DAM_TYPE, F_VALUE);
+}
+
+// Objekte, die die Beschaedigung einer Waffe durch direktes Setzen von
+// P_DAMAGED durchfuehren, werden im awmaster geloggt
+mixed _set_item_damaged(mixed arg)
+{
+    if (arg && !intp(arg))
+    {
+        return Query(P_DAMAGED, F_VALUE);
+    }
+
+    if (previous_object(1))
+      call_other("/secure/awmaster","RegisterDamager",
+            previous_object(1),QueryProp(P_DAMAGED),arg);
+    
+    return Set(P_DAMAGED,arg,F_VALUE);
+}
+
+// Wird etwas an P_HIT_FUNC geaendert, muss die zugehoerige closure
+// erstmal geloescht werden.
+mixed _set_hit_func(mixed arg)
+{
+    hit_cl=0;
+    return Set(P_HIT_FUNC, arg, F_VALUE);
+}
+
+// Wird etwas an P_DEFEND_FUNC geaendert, muss die zugehoerige closure
+// erstmal geloescht werden.
+mixed _set_defend_func(mixed arg)
+{
+    defend_cl=0;
+    return Set(P_DEFEND_FUNC, arg, F_VALUE);
+}
+
+// Die maximale Waffenklasse einer Waffe berechnet sich natuerlich aus
+// der aktuellen Waffenklasse und der Beschaedigung. Eine Ausnahme bilden
+// hier Waffen, deren effektive Waffenklasse groesser ist als diese Summe
+int  _query_max_wc() 
+{   int a,b;
+
+    a=QueryProp(P_WC)+QueryProp(P_DAMAGED);
+    b=QueryProp(P_EFFECTIVE_WC);
+    if (b>a)
+        return b;
+    return a;
+}
+
+// Will man eine Waffe beschaedigen oder reparieren, so macht man das
+// am besten ueber die Funktion Damage(argument). Positive Argumente
+// bedeuten eine Beschaedigung, negative eine Reparatur. Der Rueckgabewert
+// ist die wirklich durchgefuehrte Aenderung des Beschaedigungswertes
+int Damage(int new_dam)
+{   int    wc,old_dam;
+    object w;
+
+    // Uebergebenes Argument pruefen
+    if (!new_dam || !intp(new_dam))
+    {
+        return 0;
+    }
+
+    // Bei Waffen, die nicht ausschliesslich zur Parade eingesetzt werden,
+    // geht die Beschaedigung auf die Kampfkraft, sprich: P_WC
+    if (QueryProp(P_PARRY)<PARRY_ONLY)
+    {
+        if ((wc=QueryProp(P_WC))<=MIN_WEAPON_CLASS && new_dam>0)
+        {
+            // Sonst wuerde Beschaedigung zur Reparatur fuehren
+            return 0;
+        }
+
+        // Min-WC und Max-WC beachten
+        if ((wc-new_dam) < MIN_WEAPON_CLASS)
+        {
+            new_dam = wc-MIN_WEAPON_CLASS;
+        }
+        else if ((wc-new_dam) > MAX_WEAPON_CLASS)
+        {
+            new_dam = wc-MAX_WEAPON_CLASS;
+        }
+
+        // Nie mehr als 100% reparieren
+        if (((old_dam=QueryProp(P_DAMAGED))<-new_dam) && new_dam<0)
+        {
+            new_dam=-old_dam;
+        }
+
+        // Aenderungen an der Waffenklasse und der dem Beschaedigungswert
+        // durchfuehren
+        SetProp(P_WC,(wc-new_dam));
+        // Ausnahmeweise Set, um die loggende Setmethode zu umgehen.
+	// TODO: SetProp, sobald direktes Beschaedigen raus ist.
+	Set(P_DAMAGED, old_dam+new_dam, F_VALUE);
+
+        // P_TOTAL_WC im Traeger updaten, so vorhanden
+        if (objectp(w=QueryProp(P_WIELDED)))
+            w->QueryProp(P_TOTAL_WC);
+
+        // Rueckgabewert: Durchgefuehrte Aenderung an P_DAMAGE
+        return new_dam;
+    }
+
+    // Bei reinen Parierwaffen geht die Beschaedigung auf die
+    // Schutzwirkung, sprich: P_AC
+
+    if ((wc=QueryProp(P_AC))<=0 && new_dam>0)
+    {
+        // Sonst wuerde Beschaedigung zur Reparatur fuehren
+        return 0;
+    }
+    
+    // Min-AC=0 und Max-AC beachten
+    if ((wc-new_dam) < MIN_PARRY_CLASS)
+    {
+        new_dam = wc-MIN_PARRY_CLASS;
+    }
+    else if ((wc-new_dam) > MAX_PARRY_CLASS)
+    {
+        new_dam = wc-MAX_PARRY_CLASS;
+    }
+ 
+    // Nie mehr als 100% reparieren
+    if (((old_dam=QueryProp(P_DAMAGED))<-new_dam) && new_dam<0)
+    {
+        new_dam=-old_dam;
+    }
+
+    // Aenderungen an der Ruestungsklasse und dem Beschaedigungswert
+    // durchfuehren
+    SetProp(P_AC,wc-new_dam);
+    // Ausnahmeweise Set, um die loggende Setmethode zu umgehen.
+    // TODO: SetProp, sobald direktes Beschaedigen raus ist.
+    Set(P_DAMAGED,old_dam+new_dam, F_VALUE);
+
+    // P_TOTAL_AC im Traeger updaten, so vorhanden
+    if (objectp(w=QueryProp(P_WIELDED)))
+        w->QueryProp(P_TOTAL_AC);
+    
+    // Rueckgabewert: Durchgefuehrte Aenderung an P_DAMAGE
+    return new_dam;
+}
+
+// Wird die Waffe einer Belastung ausgesetzt (z.B. wenn man damit
+// zuschlaegt), dann wird TakeFlaw() aufgerufen.
+varargs void TakeFlaw(object enemy)
+{   int c;
+
+    // Flaw-Wert erhoehen
+    flaw++;
+
+    // Ist der Waffe eine Qualitaet gesetzt worden, so kann es zu einer
+    // allmaehlichen Beschaedigung der Waffe kommen
+    if ((c=QueryProp(P_QUALITY)) && !(flaw%c))
+        Damage(1);
+
+    // Zeitpunkt des ersten Aufrufes festhalten
+    if (!ftime)
+        ftime=time();
+}
+
+// Die Flaw-Daten koennen natuerlich auch abgerufen werden
+mixed *QueryFlaw()
+{
+    return({flaw,ftime,dtime(ftime)});
+}
+
diff --git a/std/weapon/description.c b/std/weapon/description.c
new file mode 100644
index 0000000..aad2938
--- /dev/null
+++ b/std/weapon/description.c
@@ -0,0 +1,108 @@
+// MorgenGrauen MUDlib
+//
+// weapon/description.c -- weapon description handling
+//
+// $Id: description.c 6475 2007-09-19 20:56:40Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <properties.h>
+#include <combat.h>
+#include <thing/material.h>
+
+inherit "/std/thing/description";
+
+void create()
+{
+  ::create();
+  SetProp(P_DAM_DESC,DFLT_DAM_DESC);
+}
+
+string dam_descr()
+{   string re;
+    mixed desc;
+    int max,dam,pos;
+
+    if (!QueryProp(P_NAME) || !QueryProp(P_DAMAGED) || !QueryProp(P_SHORT) ||
+        !(desc=QueryProp(P_DAM_DESC)) || (!stringp(desc) && !pointerp(desc)))
+        return "";
+    re = capitalize(name(WER,2))+" ";
+    max = QueryProp(P_WC)+(dam=QueryProp(P_DAMAGED));
+    // Bei reinen Parierwaffen den AC als max nehmen!
+    if (QueryProp(P_PARRY)==PARRY_ONLY)
+    {
+	    max=QueryProp(P_AC)+dam;
+    }
+    if (stringp(desc))
+        return (dam>(max/2))?(re+desc+".\n"):"";
+    pos = (sizeof(desc)*dam/max);
+    // Sonst koennen Parierwaffen, die Schrott sind, buggen
+    if (pos==sizeof(desc)) pos--;
+    if (stringp(desc[pos]))
+        return (re+desc[pos]+".\n");
+    return "";
+}
+
+string short()
+{   string s;
+
+    if (!(s=QueryProp(P_SHORT)))
+        return 0;
+    return s + (QueryProp(P_WIELDED)?" (gezueckt).\n":".\n");
+}
+
+varargs string long()
+{   
+    return (process_string(QueryProp(P_LONG)||"") + (dam_descr()||""));
+}
+
+mixed _query_size() {
+  mixed res, wt;
+  if (intp(res=Query(P_SIZE)) && (res>0))
+    return res;
+  wt=QueryProp(P_WEAPON_TYPE);
+  switch (wt) {
+    case WT_SWORD  : return 100;  // default: Langschwert
+    case WT_AXE    : return  80;
+    case WT_CLUB   : return  80;
+    case WT_SPEAR  : return 180;
+    case WT_KNIFE  : return  20;
+    case WT_WHIP   : return 200;
+    case WT_STAFF  : return 150;
+  }
+  return 10;  // alles andere
+}
+
+mapping _query_material() {
+  mixed res,wt;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  wt=QueryProp(P_WEAPON_TYPE);
+  switch(wt) {
+    case WT_SWORD:
+    return ([MAT_MISC_METAL:100]);
+    case WT_KNIFE:
+    return ([MAT_MISC_METAL:80,MAT_MISC_WOOD:20]);
+    case WT_AXE:
+    return ([MAT_MISC_METAL:50,MAT_MISC_WOOD:50]);
+    case WT_SPEAR:
+    return ([MAT_MISC_METAL:20,MAT_MISC_WOOD:80]);
+    case WT_STAFF:
+    case WT_CLUB:
+    return ([MAT_MISC_WOOD:100]);
+  }
+  return ([MAT_MISC_METAL:100]);
+}
+
+// P_BALANCED_WEAPON und P_TECHNIQUE sind mangels umgesetztem Konzept durch
+// EM-Beschluss fuer obsolet erklaert worden. Zesstra. 26.06.2007
+
diff --git a/std/weapon/moving.c b/std/weapon/moving.c
new file mode 100644
index 0000000..ce81770
--- /dev/null
+++ b/std/weapon/moving.c
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// weapon/moving.c -- Bewegen/Zerstoeren von Waffen
+//
+// $Id: moving.c 6312 2007-05-20 22:40:51Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/moving";
+
+//#define NEED_PROTOTYPES 1
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+
+varargs int DoUnwield(int silent);
+
+varargs int move(mixed dest, int method)
+{
+  DoUnwield( method & (M_SILENT|M_NOCHECK));
+  if ((method & M_NOCHECK) || (!QueryProp(P_WIELDED)))
+    return ::move(dest, method );
+}
+
+varargs int remove(int silent )
+{
+  DoUnwield( silent );
+  if (!QueryProp(P_WIELDED))
+    return ::remove( silent );
+}
diff --git a/sys/ansi.h b/sys/ansi.h
new file mode 100644
index 0000000..4e4d244
--- /dev/null
+++ b/sys/ansi.h
@@ -0,0 +1,35 @@
+// MorgenGrauen MUDlib
+//
+// ansi.h -- Sequenzen fuer ANSI- und VT100-Terminals
+//
+// $Id: ansi.h,v 3.3 2000/02/12 16:18:42 Padreic Exp $
+
+#ifndef _ANSI_
+#define _ANSI_
+
+#define ANSI_BOLD ""
+#define ANSI_UNDERL ""
+#define ANSI_BLINK ""
+#define ANSI_INVERS ""
+
+#define ANSI_BLACK ""
+#define ANSI_RED ""
+#define ANSI_GREEN ""
+#define ANSI_YELLOW ""
+#define ANSI_BLUE ""
+#define ANSI_PURPLE ""
+#define ANSI_CYAN ""
+#define ANSI_WHITE ""
+
+#define ANSI_BG_BLACK ""
+#define ANSI_BG_RED ""
+#define ANSI_BG_GREEN ""
+#define ANSI_BG_YELLOW ""
+#define ANSI_BG_BLUE ""
+#define ANSI_BG_PURPLE ""
+#define ANSI_BG_CYAN ""
+#define ANSI_BG_WHITE ""
+
+#define ANSI_NORMAL ""
+
+#endif
diff --git a/sys/armour.h b/sys/armour.h
new file mode 100644
index 0000000..b33e43e
--- /dev/null
+++ b/sys/armour.h
@@ -0,0 +1,41 @@
+// MorgenGrauen MUDlib
+//
+// armour.h -- armour header
+//
+// $Id: armour.h 9092 2015-01-19 23:57:50Z Zesstra $
+ 
+#ifndef __ARMOUR_H__
+#define __ARMOUR_H__
+
+#include <clothing.h>
+#include <combat.h>
+
+// properties
+#ifndef P_NR_HANDS
+#define P_NR_HANDS      "nr_hands"     // benoetigte Haende zum tragen (Schild)
+#endif
+
+#define P_AC            "ac"           // Ruestungsklasse (armour class)
+
+#define P_ARMOUR_TYPE   "armour_type"  // Art der Ruestung (Helm, Ring usw.)
+
+#define P_DEFEND_FUNC   "defend_func"  // Objekt das eine DefendFunc definiert
+
+#endif // __ARMOUR_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __ARMOUR_H_PROTO__
+#define __ARMOUR_H_PROTO__
+
+// prototypes
+int QueryDefend (string|string* dam_type, int|mapping spell, object enemy);
+
+// TO BE REMOVED
+
+void SetDefendFunc(object ob);
+object QueryDefendFunc();
+
+#endif // __ARMOUR_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/attributes.h b/sys/attributes.h
new file mode 100644
index 0000000..c00b3dc
--- /dev/null
+++ b/sys/attributes.h
@@ -0,0 +1,12 @@
+// MorgenGrauen MUDlib
+//
+// attributes.h -- living attributes
+//
+// $Id: attributes.h,v 3.1 1997/02/12 13:04:59 Wargon Exp $
+
+#ifndef __ATTRIBUTES_H__
+#define __ATTRIBUTES_H__
+
+#include "/sys/living/attributes.h"
+
+#endif
diff --git a/sys/bank.h b/sys/bank.h
new file mode 100644
index 0000000..6c6306e
--- /dev/null
+++ b/sys/bank.h
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// bank.h -- Definitionen fuer die Zentralbank bzw. Laeden
+//
+// $Id: bank.h 6749 2008-03-12 19:45:27Z Zesstra $
+
+#ifndef __BANK_H__
+#define __BANK_H__
+
+#define ZENTRALBANK "/p/daemon/zentralbank"
+
+// Diese Properties lassen sich nur abfragen!
+#define P_SHOP_PERCENT_LEFT "shop_percent_left"    // Aktuelle Prozente in
+#define P_STORE_PERCENT_LEFT "store_percent_left"  // Laden und Speicher
+#define P_CURRENT_MONEY "current_money"       // Vermoegen des Ladens
+
+#define P_STORE_CONSUME  "store_consume"    /* default 20%, immer mind. 1% */
+#define P_MIN_STOCK      "min_stock"    /* mindest Vorrat an Gegenstaenden */
+
+#endif // __BANK_H__
+
+// Konstanten fuer die Implementation
+#ifdef NEED_PROTOTYPES
+#ifndef __BANK_H_PROTOTYPES__
+#define __BANK_H_PROTOTYPES__
+
+#define BANK_DEFAULT_PERCENT 20 // So viele % der Einzahlung gehen aufs Konto
+#define SHOP_PERCENT_LEFT 30 // So viele % kriegt der Laden fuer Objekte
+			     // gutgeschrieben, die er zerstoert
+
+#define STORE_PERCENT_LEFT 25 // Dito fuer den Store
+
+#endif // __BANK_H_PROTOTYPES__
+
+#endif // NEED_PROTOTYPES
+
diff --git a/sys/break_string.h b/sys/break_string.h
new file mode 100644
index 0000000..ff4f21a
--- /dev/null
+++ b/sys/break_string.h
@@ -0,0 +1,21 @@
+// MorgenGrauen MUDlib
+//
+// break_string.h - sym. Konstanten fuer break_string()
+//
+// $Id: defines.h 6415 2007-08-10 19:24:04Z Zesstra $
+
+#ifndef _BREAK_STRING_H_
+#define _BREAK_STRING_H_
+
+#define BS_LEAVE_MY_LFS 	1
+#define BS_SINGLE_SPACE         2
+#define BS_BLOCK		4
+#define BS_NO_PARINDENT		8
+#define BS_INDENT_ONCE         16
+#define BS_PREPEND_INDENT      32
+// Falls hier Flags ergaenzt werden, sollten sie auch in living/comm.h fuer
+// ReceiveMsg() eingetragen werden. ReceiveMsg() kann per se auch nur 10
+// BS-Flags verarbeiten, das sollte beim Ergaenzen beachtet werden.
+
+#endif /* _BREAK_STRING_H_ */
+
diff --git a/sys/class.h b/sys/class.h
new file mode 100644
index 0000000..edfc389
--- /dev/null
+++ b/sys/class.h
@@ -0,0 +1,89 @@
+// MorgenGrauen MUDlib
+//
+// class.h -- Definitionen fuer Klassen von Objekten und Lebewesen.
+//            Siehe AddClass()/is_class_member() in /std/thing/description.c
+//
+// $Id: class.h 9552 2016-04-24 09:53:13Z Arathorn $
+
+#ifndef __CLASS_H__
+#define __CLASS_H__
+
+#define CLASSDB "/p/daemon/classdb"
+
+/* -------------------- Klassen fuer "Lebewesen" -------------------- */
+
+#define CL_ANIMAL "tier"
+#define CL_ARACHNID "spinne"  // und Spinnenartige
+#define CL_BIRD "vogel"
+#define CL_DARKELF "dunkelelf"
+#define CL_DEMON "daemon"
+#define CL_DRAGON "drache"
+#define CL_DWARF "zwerg"
+#define CL_ELF "elf"
+#define CL_ELEMENTAL "elementarwesen"
+#define CL_FELINE "feline"
+#define CL_FISH "fisch"
+#define CL_FROG "frosch"
+#define CL_FURIE "furie"
+#define CL_HARPY "harpyie"
+#define CL_GHOST "geist"
+#define CL_GHOUL  "ghoul"
+#define CL_GIANT "riese"
+#define CL_GNOME "gnom"
+#define CL_GOBLIN "goblin"
+#define CL_HOBBIT "hobbit"
+#define CL_HOBGOBLIN "hobgoblin"
+#define CL_HUMAN "mensch"
+#define CL_ILLUSION "illusion" // Bsp. beschworene Illusionen
+#define CL_INSECT "insekt"
+#define CL_LIVING "lebewesen"
+#define CL_MAMMAL "saeugetier"
+#define CL_MAMMAL_LAND "landsaeuger"
+#define CL_MAMMAL_WATER "wassersaeuger" // Damit man Delphinen nicht das Haar wuschelt
+#define CL_ORC "ork"
+#define CL_PLANT "pflanze"
+#define CL_REPTILE "reptil"
+#define CL_SHADOW "schattenwesen"
+#define CL_SKELETON "skelett"
+#define CL_SLIME "schleimwesen" // Fuer Einzeller und aehnliches
+#define CL_SNAKE "schlange"
+#define CL_TROLL "troll"
+#define CL_UNDEAD "untoter"
+#define CL_VAMPIRE "_cl_vampir"
+#define CL_ZOMBIE "zombie"
+#define CL_SHAPECHANGER "gestaltwandler" // Bsp. Werwoelfe etc.
+#define CL_GOLEM "golem"
+#define CL_CONSTRUCT "konstrukt"
+
+/* ------------------ Klassen nach Bewegungsmethoden ----------------- */
+
+#define CL_WALKING  "gehend"
+#define CL_CRAWLING "kriechend"
+#define CL_SWIMMING "schwimmend"
+#define CL_FLYING   "fliegend"
+
+/* -------------------- Klassen fuer Gegenstaende -------------------- */
+
+#define CL_EXPLOSIVE "sprengstoff"
+#define CL_DISEASE "krankheit"
+#define CL_CURSE "fluch"
+#define CL_INORGANIC "anorganisch"
+#define CL_POISON "gift"
+#define CL_POISONOUS "giftiges"
+#define CL_AMMUNITION "munition"
+// Es kann auch fuer Gegenstaende CL_ILLUSION verwendet werden.
+
+#define CL_ALL ({CL_ANIMAL, CL_ARACHNID, CL_BIRD, CL_DEMON, CL_DRAGON, \
+                CL_DWARF, CL_ELF, CL_ELEMENTAL, CL_FELINE, CL_FISH, CL_FROG, \
+                CL_FURIE, CL_HARPY, CL_VAMPIRE,\
+                CL_GHOST, CL_GHOUL, CL_GIANT, CL_GNOME, CL_GOBLIN, CL_HOBBIT, \
+                CL_HOBGOBLIN, CL_HUMAN, CL_INORGANIC, CL_INSECT, CL_LIVING, \
+                CL_MAMMAL, CL_MAMMAL_LAND, CL_MAMMAL_WATER, CL_ORC, CL_PLANT, \
+                CL_REPTILE, CL_SHADOW, CL_SKELETON, CL_SLIME, CL_SNAKE, \
+                CL_TROLL, CL_UNDEAD, CL_ZOMBIE, CL_SHAPECHANGER, CL_WALKING, \
+                CL_CRAWLING, CL_SWIMMING, CL_FLYING, CL_EXPLOSIVE, \
+                CL_DISEASE, CL_CURSE, CL_POISON, CL_POISONOUS, \
+                CL_AMMUNITION, CL_ILLUSION, CL_GOLEM, CL_CONSTRUCT })
+
+#endif
+
diff --git a/sys/clothing.h b/sys/clothing.h
new file mode 100644
index 0000000..ad61841
--- /dev/null
+++ b/sys/clothing.h
@@ -0,0 +1,49 @@
+// MorgenGrauen MUDlib
+//
+// clothing.h -- clothing header
+//
+// $Id: clothing.h 6065 2006-09-29 23:22:52Z Zesstra $
+ 
+#ifndef __CLOTHING_H__
+#define __CLOTHING_H__
+
+// properties
+#define P_WORN          "worn"         // Objekt mit dem Traeger der Ruestung
+
+#define P_WEAR_FUNC     "wear_func"    // Objekt das eine WearFunc definiert
+#define P_REMOVE_FUNC   "remove_func"  // Objekt das eine RemoveFunc definiert
+
+#define P_WEAR_MSG      "wear_msg"     // Meldungen beim Anziehen
+#define P_UNWEAR_MSG    "unwear_msg"   // Meldungen beim Ausziehen
+
+#define P_LAST_WEAR_ACTION "last_wear_action" // Letzte Aktion des Spielers
+                                              // An- und ausziehen betreff
+
+// Die Wear-Actions im einzelnen
+
+#define WA_WEAR            0
+#define WA_UNWEAR          1
+
+#endif // __CLOTHING_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __CLOTHING_H_PROTO__
+#define __CLOTHING_H_PROTO__
+
+// prototypes
+// functions that will be called if one issues a wear command (or to manually
+// wear this armour
+varargs int do_wear(string str, int silent);
+
+// function that will be called if one issues an unwear command or to manually
+// unwear the armour
+varargs int do_unwear(string str, int silent);
+// function is the rest of do_unwear() -- msgs etc
+varargs int DoUnwear(int silent, int all);
+
+int ziehe(string str);
+
+#endif // __CLOTHING_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/combat.h b/sys/combat.h
new file mode 100644
index 0000000..d00cbd3
--- /dev/null
+++ b/sys/combat.h
@@ -0,0 +1,360 @@
+// MorgenGrauen MUDlib
+//
+// combat.h -- Schadenstypen, Waffen- und Ruestungsklassen und ein
+//             paar Properties fuer das Kampfsystem
+//
+// $Id: combat.h 7178 2009-03-09 20:42:49Z Zesstra $
+
+#ifndef _COMBAT_H_
+#define _COMBAT_H_
+
+#include <weapon.h>
+#include <armour.h>
+#include <ranged_weapon.h>
+
+/* -------------------- Properties -------------------- */
+
+/* ---------- Kampf allgemein ---------- */
+
+#define P_ENABLE_IN_ATTACK_OUT "enable_in_attack_out"
+#define P_NO_GLOBAL_ATTACK     "no_global_attack"
+#define P_FRIEND               "friend"
+#define P_LAST_COMBAT_TIME     "last_combat_time"
+
+/* ---------- Befrieden ---------------- */
+
+#define P_ACCEPT_PEACE         "accept_peace"
+
+/* ---------- Waffen und Ruestungen ---------- */
+
+#define P_LAST_USE             "last_object_use"
+#define P_DAMAGED              "item_damaged"
+#define P_EFFECTIVE_WC         "effective_wc"
+#define P_EFFECTIVE_AC         "effective_ac"
+#define P_QUALITY              "quality"
+#define P_RESTRICTIONS         "restrictions"
+#define P_EQUIP_TIME           "equip_time"
+
+/* ---------- Artillerie ------------*/
+
+#define P_IS_ARTILLERY         "artillery"
+
+/* ---------- Fernkampfwaffen ---------- */
+
+#define P_SHOOTING_WC          "shooting_wc"
+#define P_AMMUNITION           "munition"
+#define P_STRETCH_TIME         "stretch_time"
+#define P_RANGE                "range"
+#define P_SHOOTING_AREA        "shooting_area"
+#define P_TARGET_AREA          "target_area"
+
+/* ----- Defines fuer Array-Positionen ----- */
+
+#define RW_SIZE   5
+#define RW_ENEMY  0
+#define RW_AMMU   1
+#define RW_WRITE  2
+#define RW_VICTIM 3
+#define RW_SAY    4
+
+/* -------------------- Waffentypen -------------------- */
+
+#define WT_AMMU          "ammunition"
+#define WT_AXE           "axe"
+#define WT_CLUB          "club"
+#define WT_HANDS         "hands"
+#define WT_KNIFE         "knife"
+#define WT_MAGIC         "magic"
+#define WT_RANGED_WEAPON "fernwaffe"
+#define WT_SPEAR         "spear"
+#define WT_STAFF         "staff"
+#define WT_SWORD         "sword"
+#define WT_WHIP          "whip"
+
+// Fuer alles, was sich unter den o.g. Typen nicht einordnen laesst
+#define WT_MISC          "misc"
+
+/* ---------- Liste aller erlaubten Waffentypen ---------- */
+
+#define VALID_WEAPON_TYPE(t) (member(({\
+    WT_SWORD, WT_AXE,  WT_CLUB, WT_SPEAR, WT_KNIFE, WT_AMMU, WT_MAGIC,\
+    WT_STAFF, WT_WHIP,  WT_MISC, WT_RANGED_WEAPON }), t ) != -1)
+
+/* ---------- Maximal erlaubte Waffenstaerke ---------- */
+
+#define MAX_WEAPON_CLASS 200
+
+#define MAX_TWOHANDED_WC MAX_WEAPON_CLASS
+/* Die Einhaltung der Einhaender-Grenze wird bisher nicht garantiert */
+#define MAX_ONEHANDED_WC 150
+
+/* ---------- Mindest-Waffenstaerke, die eine Waffe
+              bei einer Beschaedigung behaelt       ---------- */
+
+#define MIN_WEAPON_CLASS 30
+
+/* ---------- Maximal erlaubte Parierwaffenstaerke ---------- */
+
+#define MAX_PARRY_CLASS 40
+
+/* ---------- Auch fuer Paruerwaffen gibt 
+              es eine Minimalstaerke      ---------- */
+
+#define MIN_PARRY_CLASS 0
+
+/* ---------- Dito fuer Ruestungen ---------- */
+
+#define MIN_ARMOUR_CLASS 0
+
+/* -------------------- Munition fuer Fernkampfwaffen -------------------- */
+
+// als x bitte _immer_ den Namen der Munitionsart im Plural angeben,
+// dieses ermoeglicht ein auswerten via MUN_NAME() z.b. in gilden
+
+#define MUN_MISC(x) "Munition:"+x
+
+// ueber dieses define und bitte _nur_ ueber dieses define koennen
+// Gilden oder aehnliches den Namen einer Munitionsart ermitteln. Dieses
+// ist wichtig wenn naehere Infos ueber eine Waffe ausgegeben werden sollen.
+// Bsp.  "Ein Bogen wiegt 1500 Gramm und erlaubt es Dir Pfeile mit einer
+//        Reichweite von 50 m zu verschiessen."
+// sprintf("%s wiegt %d Gramm und erlaubt es Dir %s mit einer Reichweite "
+//        +"von %d m zu verschiessen.", ob->Name(WER), ob->QueryProp(P_WEIGHT),
+//        MUN_NAME(ob->QueryProp(P_AMMUNITION)), ob->QueryProp(P_RANGE))
+
+#define MUN_NAME(x) x[9..]
+
+#define MUN_ARROW   MUN_MISC("Pfeile")
+#define MUN_STONE   MUN_MISC("Steine")
+#define MUN_BOLT    MUN_MISC("Bolzen")
+#define MUN_DART    MUN_MISC("kleine Blasrohrpfeile")
+
+// Fuer die hier definierten Munitionstypen gibt es auch einen Schuss-Skill
+
+#define VALID_SHOOT_SKILL ([\
+  MUN_ARROW : "arrow",\
+  MUN_STONE : "stone",\
+  MUN_BOLT  : "bolt",\
+  MUN_DART  : "dart" ])
+
+/* -------------------- Schadensarten -------------------- */
+
+/* ---------- physikalisch --------- */
+
+// Schlagschaden
+#define DT_BLUDGEON "bludgeon"
+
+#define DT_EXPLOSION "explosion"
+
+// Stichschaden
+#define DT_PIERCE "pierce"
+
+// reissender Schaden/Krallen
+#define DT_RIP "rip"
+
+// Schnittschaden
+#define DT_SLASH "slash"
+
+// Quetschungsschaden
+#define DT_SQUEEZE "squeeze"
+
+// peitschender Schaden
+#define DT_WHIP "whip"
+
+/* ---------- Mapping aller physikalischen Schadensarten ---------- */
+
+#define PHYSICAL_DAMAGE_TYPES ([\
+    DT_BLUDGEON  : 1,\
+    DT_EXPLOSION : 1,\
+    DT_PIERCE    : 1,\
+    DT_RIP       : 1,\
+    DT_SLASH     : 1,\
+    DT_SQUEEZE   : 1,\
+    DT_WHIP      : 1 ])
+
+/* ----------- magisch ---------- */
+
+// Saeure
+#define DT_ACID "acid"
+
+// Luft(mangel)
+#define DT_AIR "air"
+
+// Kaelte oder Eis
+#define DT_COLD "cold"
+
+// Feuer
+#define DT_FIRE "fire"
+
+// heiliger Schaden
+#define DT_HOLY "holy"
+
+// Blitz oder Elektrizitaet
+#define DT_LIGHTNING "lightning"
+
+// (allg.) Magie
+#define DT_MAGIC "magic"
+
+// Gift
+#define DT_POISON "poison"
+
+// Laerm bzw. Schall
+#define DT_SOUND "sound"
+
+// Angst
+#define DT_TERROR "terror"
+
+// satanischer Schaden
+#define DT_UNHOLY "unholy"
+
+// Wasser
+#define DT_WATER "water"
+
+/* ---------- Mapping aller magischen Schadensarten ---------- */
+
+#define MAGICAL_DAMAGE_TYPES ([\
+    DT_ACID      : 1,\
+    DT_AIR       : 1,\
+    DT_COLD      : 1,\
+    DT_FIRE      : 1,\
+    DT_HOLY      : 1,\
+    DT_LIGHTNING : 1,\
+    DT_MAGIC     : 1,\
+    DT_POISON    : 1,\
+    DT_SOUND     : 1,\
+    DT_TERROR    : 1,\
+    DT_UNHOLY    : 1,\
+    DT_WATER     : 1 ])
+
+
+/* ---------- Liste aller Schadensarten ---------- */
+
+#define ALL_DAMAGE_TYPES   ({ DT_BLUDGEON, DT_SLASH, DT_PIERCE, DT_MAGIC, \
+                              DT_FIRE, DT_COLD, DT_ACID, DT_WATER, \
+                              DT_LIGHTNING, DT_AIR, DT_POISON, DT_EXPLOSION, \
+                              DT_SQUEEZE, DT_SOUND, DT_RIP, DT_WHIP, \
+                              DT_HOLY, DT_UNHOLY, DT_TERROR })
+
+/* Ist x ein gueltiger Schadenstyp der Mudlib? */
+#define VALID_DAMAGE_TYPE(x) (member(ALL_DAMAGE_TYPES, x) > -1)
+
+/* -------------------- Ruestungstypen -------------------- */
+
+#define AT_AMULET   "Amulett"
+#define AT_ARMOUR   "Ruestung"
+#define AT_BELT     "Guertel"
+#define AT_BOOT     "Schuhe"
+#define AT_CLOAK    "Umhang"
+#define AT_GLOVE    "Handschuh"
+#define AT_HELMET   "Helm"
+#define AT_QUIVER   "Koecher"
+#define AT_RING     "Ring"
+#define AT_SHIELD   "Schild"
+#define AT_TROUSERS "Hosen"
+
+// Fuer alles, was sich unter den o.g. Typen nicht einordnen laesst
+#define AT_MISC     "Misc"
+
+// Ruestungen, die nicht angezogen werden koennen/duerfen
+#define AT_ILLEGAL  "ILLEGAL"
+
+/* ---------- Liste aller erlaubten Ruestungstypen ---------- */
+
+#define VALID_ARMOUR_TYPE(t) (member(({\
+    AT_ARMOUR, AT_HELMET, AT_RING, AT_GLOVE, AT_CLOAK, AT_BOOT,\
+    AT_TROUSERS, AT_SHIELD, AT_AMULET, AT_MISC, AT_BELT,\
+    AT_QUIVER}), t ) != -1)
+
+/* ---------- Mapping mit den maximal erlaubten 
+              Ruestungswerten der einzelnen Ruestungstypen ---------- */
+
+#define VALID_ARMOUR_CLASS ([ \
+    AT_AMULET   :  2 ,\
+    AT_ARMOUR   : 50 ,\
+    AT_BELT     :  2 ,\
+    AT_BOOT     :  6 ,\
+    AT_CLOAK    : 10 ,\
+    AT_GLOVE    :  5 ,\
+    AT_HELMET   : 15 ,\
+    AT_QUIVER   :  0 ,\
+    AT_RING     :  2 ,\
+    AT_SHIELD   : 40 ,\
+    AT_TROUSERS : 15 ,\
+    AT_MISC     :  0 ])
+
+/* ---------- Mapping mit den Ruestungswerten, ab denen
+              die Ruestung im Laden einbehalten werden  ---------- */
+
+#define KEEP_ARMOUR_CLASS ([ \
+    AT_AMULET   :  2 ,\
+    AT_ARMOUR   : 35 ,\
+    AT_BELT     :  2 ,\
+    AT_BOOT     :  5 ,\
+    AT_CLOAK    :  7 ,\
+    AT_GLOVE    :  5 ,\
+    AT_HELMET   : 12 ,\
+    AT_QUIVER   :  1 ,\
+    AT_RING     :  2 ,\
+    AT_SHIELD   : 26 ,\
+    AT_TROUSERS : 12 ,\
+    AT_MISC     :  1 ])
+
+/* -------------------- Defines fuer Kampfkommandos -------------------- */
+
+#define C_AVG "avg"
+#define C_MAX "max"
+#define C_MIN "min"
+#define C_DTYPES "dtypes"
+#define C_HEAL "heal"
+
+
+/* -------------------- Defines fuer erweiterte Defendinformationen -- */
+#define EINFO_DEFEND		"einfo_defend"
+#define ORIGINAL_DAM		"original_dam"
+#define ORIGINAL_DAMTYPE	"original_damtype"
+#define CURRENT_DAM		"current_dam"
+#define CURRENT_DAMTYPE		"current_damtype"
+#define ORIGINAL_AINFO  	"original_ainfo"
+#define ENEMY_INSERTED		"enemy_inserted"
+#define RFR_REDUCE		"rfr_reduce"
+#define PRESENT_DEFENDERS  	"present_defenders"
+#define DEFENDING_DEFENDER	"defending_defender"
+#define DEFEND_HOOK		"defend_hook"
+#define DEFEND_ARMOURS		"defend_armours"
+#define DEFEND_GUILD		"defend_guild"
+#define DEFEND_RESI		"defend_resi"
+#define DEFEND_BODY		"defend_body"
+#define DEFEND_LOSTLP		"defend_lostlp"
+#define DEFEND_CUR_ARMOUR_PROT	"defend_cur_armour_prot"
+
+#define DEF_DEFENDER		0
+#define DEF_DAM			1
+#define DEF_DAMTYPE		2
+#define DEF_SPELL		3
+
+#define HOOK_DAM		0
+#define HOOK_DAMTYPE		1
+#define HOOK_SPELL		2
+#define DI_NOHOOK		0
+#define DI_HOOK			1
+#define DI_HOOKINTERRUPT	2
+
+#define DEF_ARMOUR_DAM		0
+#define DEF_ARMOUR_PROT		1
+
+#define GUILD_DAM		0
+#define GUILD_DAMTYPE		1
+
+#endif
+
+/* -------------------- Prototypen-Deklarationen -------------------- */
+
+#ifndef __COMBAT_H_PROTOTYPES__
+#define __COMBAT_H_PROTOTYPES__
+
+#ifdef NEED_PROTOTYPES
+
+#endif // NEED_PROTOTYPES
+
+#endif // __COMBAT_H_PROTOTYPES__
+
diff --git a/sys/comm.h b/sys/comm.h
new file mode 100644
index 0000000..16aeb33
--- /dev/null
+++ b/sys/comm.h
@@ -0,0 +1,15 @@
+#ifndef LPC_COMM_H_
+#define LPC_COMM_H_
+
+/* return values for net_connect() */
+
+#define NC_SUCCESS         0  /* Success! */
+#define NC_EUNKNOWNHOST    1  /* host address could not be resolved */
+#define NC_ENOSOCKET       2  /* socket could not be created */
+#define NC_ENOBIND         3  /* socket could not be bound */
+#define NC_ENOCONNECT      4  /* socket could not be connected */
+#define NC_ECONNREFUSED    5  /* target address not listening */
+#define NC_EMCONN          6  /* too many pending connections */
+#define NC_ENORESSOURCES   7  /* insufficient system ressources */
+
+#endif /* LPC_COMM_H_ */
diff --git a/sys/commands.h b/sys/commands.h
new file mode 100644
index 0000000..3982372
--- /dev/null
+++ b/sys/commands.h
@@ -0,0 +1,50 @@
+#ifndef LPC_COMMANDS_H_
+#define LPC_COMMANDS_H_
+
+/* Flags accepted by add_action(fun, verb, flag).
+ * (Below, VERB is what the player enters).
+ * Negative flag values allow VERB to be just the first -<flag>
+ * characters of <verb> (abbreviated verb).
+ */
+
+#define AA_VERB     0  /* VERB must be <verb> */
+#define AA_SHORT    1  /* VERB must start with <verb>,
+                        * args do not include extra characters */
+#define AA_NOSPACE  2  /* VERB must start with <verb>,
+                        * args and verb do include extra characters */
+#define AA_IMM_ARGS 3  /* VERB must start with <verb>,
+                        * args do include extra characters */
+
+/* Bitflags accepted by  query_actions(object ob, int mask)
+ */
+
+#define QA_VERB         1
+#define QA_TYPE         2
+#define QA_SHORT_VERB   4
+#define QA_OBJECT       8
+#define QA_FUNCTION    16
+
+
+/* Indices in the arrays returned from command_stack()
+ */
+
+#define CMD_VERB       0
+#define CMD_TEXT       1
+#define CMD_ORIGIN     2
+#define CMD_PLAYER     3
+#define CMD_FAIL       4
+#define CMD_FAILOBJ    5
+
+#define CMD_SIZE       6
+
+
+/* Indices into the subarrays returned from match_command()
+ */
+#define CMDM_VERB     0
+#define CMDM_ARG      1
+#define CMDM_OBJECT   2
+#define CMDM_FUN      3
+
+#define CMDM_SIZE     4
+
+#endif /* LPC_COMMANDS_H_ */
diff --git a/sys/configuration.h b/sys/configuration.h
new file mode 100644
index 0000000..b2c0b88
--- /dev/null
+++ b/sys/configuration.h
@@ -0,0 +1,40 @@
+#ifndef LPC_CONFIGURATION_H_
+#define LPC_CONFIGURATION_H_
+
+/* Definition of argument values for the configure_*() efuns.
+ */
+
+/* Possible options for configure_interactive().
+ */
+#define IC_MAX_WRITE_BUFFER_SIZE         0
+#define IC_SOCKET_BUFFER_SIZE            1
+#define IC_COMBINE_CHARSET_AS_STRING     2
+#define IC_COMBINE_CHARSET_AS_ARRAY      3
+#define IC_CONNECTION_CHARSET_AS_STRING  4
+#define IC_CONNECTION_CHARSET_AS_ARRAY   5
+#define IC_QUOTE_IAC                     6
+#define IC_TELNET_ENABLED                7
+#define IC_MCCP                          8
+#define IC_PROMPT                        9
+#define IC_MAX_COMMANDS                 10
+#define IC_MODIFY_COMMAND               11
+
+/* Possible options for configure_object().
+ */
+#define OC_COMMANDS_ENABLED    0
+#define OC_HEART_BEAT          1
+
+/* Possible options for configure_driver().
+ */
+#define DC_MEMORY_LIMIT                  0
+#define DC_ENABLE_HEART_BEATS            1
+#define DC_LONG_EXEC_TIME                2
+#define DC_DATA_CLEAN_TIME               3
+#define DC_TLS_CERTIFICATE               4
+#define DC_TLS_DHE_PARAMETER             5
+#define DC_TLS_CIPHERLIST                6
+#define DC_EXTRA_WIZINFO_SIZE            7
+#define DC_DEFAULT_RUNTIME_LIMITS        8
+#define DC_SWAP_COMPACT_MODE             9
+
+#endif /* LPC_CONFIGURATION_H_ */
diff --git a/sys/container.h b/sys/container.h
new file mode 100644
index 0000000..9555610
--- /dev/null
+++ b/sys/container.h
@@ -0,0 +1,73 @@
+// MorgenGrauen MUDlib
+//
+// container.h -- Properties und Definitionen fuer Behaelter
+//
+// $Id: container.h 9020 2015-01-10 21:49:41Z Zesstra $
+
+#ifndef __CONTAINER_H__
+#define __CONTAINER_H__
+
+#include <thing/restrictions.h>
+
+// properties
+#define P_CONTENTS            "contents"            // Obsolet!!!
+
+#define P_CONTAINER           "container"          // Objekt ist ein Container
+#define P_CNT_STATUS          "cnt_status"         // Status des Containers
+#define P_TRANSPARENT         "transparent"        // kann man hineinschaun?
+#define P_LIGHT_TRANSPARENCY  "light_transparency" // Lichtdurchlaessigkeit
+
+#define P_PREPOSITION         "preposition"        // Sachen IN dem Container
+#define P_SOURCE_PREPOSITION  "source_preposition" // kann man AUS ihm nehmen
+#define P_DEST_PREPOSITION    "dest_preposition"   // ...wieder IN ihn stecken
+
+#define P_MAX_WEIGHT          "max_weight"         // max. Gewicht
+#define P_WEIGHT_PERCENT      "weight_percent"     // Gewichtsreduktion in %
+
+#define P_TOTAL_OBJECTS       "total_objects"      // # der non-invis Objekte
+#define P_MAX_OBJECTS         "max_objects"        // max. Objektanzahl
+
+#define P_INT_LIGHT           "int_light"
+
+#define P_ITEMS               "items"              // siehe AddItem()
+
+#define P_PREVENT_PILE        "prevent_pile"       // siehe /std/corpse
+#define P_PILE_NAME           "pile_name"          // Name des Spielers im Dativ
+
+// special defines
+#define CNT_STATUS_OPEN   0
+#define CNT_STATUS_CLOSED 1
+#define CNT_STATUS_LOCKED 2
+
+#define R_INSERT "insert"
+
+#define CNT_ARRAY       1
+#define CNT_COLLECT     2
+
+#endif // __CONTAINER_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __CONTAINER_H_PROTO__
+#define __CONTAINER_H_PROTO__
+
+// prototypes
+// from: container/description.c
+varargs mixed make_invlist(object viewer, mixed inv, int array);
+
+// from: container/restrictions.c
+int query_weight_contents();
+static int _query_last_content_change();
+
+int MayAddWeight(int w);
+int MayAddObject(object ob);
+public int PreventInsert(object ob);
+public int PreventLeave(object ob, mixed dest);
+object *present_objects(string complex_desc);
+object *locate_objects(string complex_desc,int info);
+
+// container/items.c
+public varargs object     AddItem( mixed filename, int refresh, mixed props);
+#endif // __CONTAINER_H_PROTO__
+
+#endif // NEED_PROTOTYPES
diff --git a/sys/container/moneyhandler.h b/sys/container/moneyhandler.h
new file mode 100644
index 0000000..62df3b7
--- /dev/null
+++ b/sys/container/moneyhandler.h
@@ -0,0 +1,20 @@
+// MorgenGrauen MUDlib
+//
+// container/moneyhandler.h -- Das -Geld-Interface
+//
+// $Id: moneyhandler.h,v 3.1 1997/02/12 13:29:09 Wargon Exp $
+
+#ifndef __CONTAINER_MONEYHANDLER_H__
+#define __CONTAINER_MONEYHANDLER_H__
+
+#endif
+
+#ifdef NEED_PROTOTYPES
+#ifndef __CONTAINER_MONEYHANDLER_H_PROTO__
+#define __CONTAINER_MONEYHANDLER_H_PROTO__
+
+int AddMoney(int amount);
+int QueryMoney();
+
+#endif
+#endif
diff --git a/sys/daemon.h b/sys/daemon.h
new file mode 100644
index 0000000..4479bca
--- /dev/null
+++ b/sys/daemon.h
@@ -0,0 +1,10 @@
+// MorgenGrauen MUDlib
+//
+// daemon.h -- Definitionen fuer Objekte in /p/daemon/
+//
+// $Id: daemon.h 7419 2010-02-07 21:04:40Z Zesstra $
+
+#include "/sys/daemon/channel.h"    // channel handling
+#include "/sys/daemon/object.h"     // persistent object handling
+#include "/sys/daemon/traveld.h"    // TRAVEL daemon
+#include "/sys/daemon/mand.h"       // MAN daemon
diff --git a/sys/daemon/channel.h b/sys/daemon/channel.h
new file mode 100644
index 0000000..9f512e0
--- /dev/null
+++ b/sys/daemon/channel.h
@@ -0,0 +1,6 @@
+// ch.h - channel daemon defines
+// --
+// $timestamp::$
+
+#include "/p/daemon/channel.h"
+
diff --git a/sys/daemon/mand.h b/sys/daemon/mand.h
new file mode 100644
index 0000000..309f8d5
--- /dev/null
+++ b/sys/daemon/mand.h
@@ -0,0 +1,22 @@
+// MorgenGrauen MUDlib
+//
+// mand.h
+//
+// $Id: mand.h 6698 2008-01-29 23:26:08Z Zesstra $
+
+
+#ifndef __MAND_H__
+#define __MAND_H__
+
+#define MAND_DOCDIR "/doc/"
+#define MAND_EXCLUDE ({ "3.0","KURS","beispiele","old","new", "infomails"})
+#define MAND "/p/daemon/mand"
+
+#endif
+
+#ifdef NEED_PROTOTYPES
+#ifndef __MAND_H_PROTOS__
+#define __MAND_H_PROTOS__
+
+#endif
+#endif
diff --git a/sys/daemon/object.h b/sys/daemon/object.h
new file mode 100644
index 0000000..1ec06ac
--- /dev/null
+++ b/sys/daemon/object.h
@@ -0,0 +1,41 @@
+// MorgenGrauen MUDlib
+//
+// OBJECT.H -- persistent object handling
+//
+// $Date: 2002/08/28 10:04:28 $
+// $Revision: 1.1 $
+/* $Log: object.h,v $
+ * Revision 1.1  2002/08/28 10:04:28  Rikus
+ * Initial revision
+ *
+ * Revision 1.1  1995/03/31  13:30:33  Hate
+ * Initial revision
+ *
+ * Revision 1.1  1994/03/20  17:07:28  Hate
+ * Initial revision
+ *
+ */
+ 
+#ifndef __OBJECT_H__
+#define __OBJECT_H__
+
+// defines
+#define OBJECTD       "/p/daemon/objectd"
+#define OBJECTD_SAVE  "/p/daemon/save/objectd"
+
+#endif // __OBJECT_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __OBJECT_H_PROTO__
+#define __OBJECT_H_PROTO__
+
+// prototypes
+mixed AddObject(string obj, string env);
+mixed RemoveObject(string obj, string env);
+varargs void QueryObject(mixed env);
+
+
+#endif // __OBJECT_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/daemon/traveld.h b/sys/daemon/traveld.h
new file mode 100644
index 0000000..c17a637
--- /dev/null
+++ b/sys/daemon/traveld.h
@@ -0,0 +1,22 @@
+#ifndef __TRAVELD_H__
+#define __TRAVELD_H__
+
+#define TRAVELD          "/p/daemon/traveld"
+#define TRAVELD_SAVEFILE "/p/daemon/save/traveld"
+
+#ifdef NEED_PROTOTYPES
+#ifndef __TRAVELD_H_PROTO__
+#define __TRAVELD_H_PROTO__
+
+public void AddStop(string ship, string harbour);
+public void RemoveTransporter(object ship);
+public varargs mixed HasTransporter(object harbour, string ship);
+public mixed RouteExists(object ship, string dest);
+public mapping QueryShips();
+public mapping QueryAllHarbours();
+
+#endif // __TRAVELD_H_PROTO__
+
+#endif // NEED_PROTOTYPES
+
+#endif // __TRAVELD_H__
diff --git a/sys/daemon/whois.h b/sys/daemon/whois.h
new file mode 100644
index 0000000..b62682c
--- /dev/null
+++ b/sys/daemon/whois.h
@@ -0,0 +1,21 @@
+#ifndef __DAEMON_WHOIS_H__
+#define __DAEMON_WHOIS_H__
+
+#define WHOIS         "/p/daemon/udp/whois"
+
+#define DESCR   0
+#define COUNTRY 1
+#define DATE    2
+#define NETNAME 3
+
+
+// prototypes
+#ifdef NEED_PROTOTYPES
+
+mixed whois(string query, int type);
+string country(string query);
+string city(string query);
+string locate(string query, string ip);
+
+#endif /* NEED_PROTOTYPES */
+#endif /* __DAEMON_WHOIS_H__ */
diff --git a/sys/death_room.h b/sys/death_room.h
new file mode 100644
index 0000000..045db1b
--- /dev/null
+++ b/sys/death_room.h
@@ -0,0 +1,9 @@
+
+#ifndef _DEATH_ROOM_H_
+#define _DEATH_ROOM_H_
+
+
+// x muss ein Spielerobjekt sein
+#define IS_IN_DEATH_ROOM(x) (environment(x)&&object_name(environment(x))==("/room/death/virtual/death_room_"+getuid(x)))
+
+#endif
diff --git a/sys/debug.h b/sys/debug.h
new file mode 100644
index 0000000..188043a
--- /dev/null
+++ b/sys/debug.h
@@ -0,0 +1,61 @@
+// MorgenGrauen MUDlib
+//
+// debug.h -- Debug-Optionen fuer die MUDlib
+//
+// $Id: debug.h,v 3.1 1997/02/12 13:04:59 Wargon Exp $
+
+#ifndef _DEBUG_
+#define _DEBUG_
+
+
+/*
+ * LPC debug options
+ */
+
+
+#define NODEBUG			/* all debugging modes disabled */
+
+#ifndef NODEBUG
+
+#define DEBUG_STD_CREATE        /* debug object creation in /std objects */
+/* #define DEBUG_PROPERTIES	/* some property debug functions */
+/* #define DEBUG_ID		/* echo the object id operation */
+#define DEBUG_PICK		/* object pickup */
+#define DEBUG_CMDS		/* command debugging for init */
+#define DEBUG_DOMAIN		/* debug domain files */
+#define DEBUG_DOOR		/* debug doors */
+#define WARN_UNDEFPROP		/* warnings if undef properties are set */
+#define DEBUG_EVENTS		/* debug event handler call */
+#define DEBUG_MOVE		/* debug in object moving */
+
+#define DEBUG_RES		/* debug restriction handling */
+#define DEBUG_RES_MAX		/* debug restriction |res_max| */
+#define DEBUG_RES_LIMIT		/* debug restriction |res_limit| */
+#define DEBUG_RES_PLAYER	/* restriction of number of something */
+/* #define DEBUG_FUNCTION	/* debug message functions */
+/* #define DEBUG_ARRAY		/* debug array and member comparison */
+#define DEBUG_PROPAGATION	/* debug background propagation mechanism */
+#define DEBUG_RES_GROUND	/* debug ground type restrictions */
+#define DEBUG_FEATURES	        /* debug feature mechanism */
+#define DEBUG_RES_FEATURES	/* debug feature mechanism */
+#define DEBUG_VIEW		/* debug players vision */
+#define DEBUG_PROPAGATION	/* debug background propagation mechanism */
+#define DEBUG_MSG		/* message debugging */
+#define DEBUG_SELECT		/* debug selection mechanism */
+#define DEBUG_RESOURCES		/* debug living resources */
+#define DEBUG_ISME		/* debug the is_me operation */
+#define DEBUG_FOLLOW		/* debug follow mode for walkers */
+#define DEBUG_RESOURCES2	/* debug living resources */
+#define ACCESS_DEBUG 1		/* debug of access control */
+#define DEBUG_SAVE		/* debug loading and saving of objects */
+#define DEBUG_BOARD		/* debug board system */
+#define DEBUG_ASEE		/* debug "to see" ability */
+#define DEBUG_SPELLS		/* debug spells */
+#define DEBUG_BATTLE		/* debug battle system */
+
+#endif
+
+
+
+#endif
+
diff --git a/sys/debug_info.h b/sys/debug_info.h
new file mode 100644
index 0000000..34fb7ee
--- /dev/null
+++ b/sys/debug_info.h
@@ -0,0 +1,218 @@
+#ifndef LPC_DEBUG_INFO_H_
+#define LPC_DEBUG_INFO_H_ 1
+
+#include "/sys/driver_info.h"
+
+/* Request values for efun debug_info().
+ */
+
+#define DINFO_OBJECT  0  /* Print information about an object */
+#define DINFO_MEMORY  1  /* Print memory usage of an object */
+#define DINFO_OBJLIST 2  /* Return an object from the global list */
+#define DINFO_MALLOC  3  /* Print the information from the 'malloc' cmd */
+#define DINFO_STATUS  4  /* Return the 'status' information */
+#define DINFO_DUMP    5  /* Dump some special information into files */
+#define DINFO_DATA    6  /* Return internal information */
+#define DINFO_TRACE   7  /* Return the current call trace */
+#define DINFO_EVAL_NUMBER 8 /* Return the current eval number */
+
+/* Sub-request values for debug_info(DINFO_DATA) */
+
+#define DID_STATUS  0  /* Return the 'status' and 'status tables' information */
+#define DID_SWAP    1  /* Return the 'status swap' information */
+#define DID_MEMORY  2  /* Return the 'status malloc' information */
+
+/* Sub-request values for debug_info(DINFO_TRACE) */
+
+#define DIT_CURRENT        0  /* Return the current call chain as an array */
+#define DIT_ERROR          1  /* Return the last error call chain as an array */
+#define DIT_UNCAUGHT_ERROR 2  /* Return the last uncaught error call chain */
+#define DIT_STR_CURRENT    3  /* Return the current call chain as a string */
+#define DIT_CURRENT_DEPTH  4  /* Return the current control stack depth */
+
+/* Indices into the array resulting from debug_info(DINFO_DATA, DID_STATUS)
+ */
+
+#define DID_ST_ACTIONS             0
+#define DID_ST_ACTIONS_SIZE        1
+#define DID_ST_SHADOWS             2
+#define DID_ST_SHADOWS_SIZE        3
+
+#define DID_ST_OBJECTS             4
+#define DID_ST_OBJECTS_SIZE        5
+#define DID_ST_OBJECTS_SWAPPED     6
+#define DID_ST_OBJECTS_SWAP_SIZE   7
+#define DID_ST_OBJECTS_LIST        8
+#define DID_ST_OBJECTS_NEWLY_DEST  9
+#define DID_ST_OBJECTS_DESTRUCTED 10
+#define DID_ST_OBJECTS_PROCESSED  11
+#define DID_ST_OBJECTS_AVG_PROC   12
+
+#define DID_ST_OTABLE             13
+#define DID_ST_OTABLE_SLOTS       14
+#define DID_ST_OTABLE_SIZE        15
+
+#define DID_ST_HBEAT_OBJS         16
+#define DID_ST_HBEAT_CALLS        17
+#define DID_ST_HBEAT_CALLS_TOTAL  18
+#define DID_ST_HBEAT_SLOTS        19
+#define DID_ST_HBEAT_SIZE         20
+#define DID_ST_HBEAT_PROCESSED    21
+#define DID_ST_HBEAT_AVG_PROC     22
+
+#define DID_ST_CALLOUTS           23
+#define DID_ST_CALLOUT_SIZE       24
+
+#define DID_ST_ARRAYS             25
+#define DID_ST_ARRAYS_SIZE        26
+
+#define DID_ST_MAPPINGS           27
+#define DID_ST_MAPPINGS_SIZE      28
+#define DID_ST_HYBRID_MAPPINGS    51
+#define DID_ST_HASH_MAPPINGS      54
+
+#define DID_ST_STRUCTS            29
+#define DID_ST_STRUCTS_SIZE       30
+#define DID_ST_STRUCT_TYPES       31
+#define DID_ST_STRUCT_TYPES_SIZE  32
+
+#define DID_ST_PROGS              33
+#define DID_ST_PROGS_SIZE         34
+
+#define DID_ST_PROGS_SWAPPED      35
+#define DID_ST_PROGS_SWAP_SIZE    36
+
+#define DID_ST_USER_RESERVE       37
+#define DID_ST_MASTER_RESERVE     38
+#define DID_ST_SYSTEM_RESERVE     39
+
+#define DID_ST_ADD_MESSAGE        40
+#define DID_ST_PACKETS            41
+#define DID_ST_PACKET_SIZE        42
+#define DID_ST_PACKETS_IN         43
+#define DID_ST_PACKET_SIZE_IN     44
+
+#define DID_ST_APPLY              45
+#define DID_ST_APPLY_HITS         46
+
+#define DID_ST_STRINGS            47
+#define DID_ST_STRING_SIZE        48
+#define DID_ST_STR_TABLE_SIZE     49
+#define DID_ST_STR_OVERHEAD       50
+#define DID_ST_UNTABLED           52
+#define DID_ST_UNTABLED_SIZE      53
+#define DID_ST_UNUSED54           54 /* UNUSED */
+#define DID_ST_UNUSED55           55 /* UNUSED */
+#define DID_ST_TABLED             56
+#define DID_ST_TABLED_SIZE        57
+#define DID_ST_STR_SEARCHES       58
+#define DID_ST_STR_SEARCHLEN      59
+#define DID_ST_STR_SEARCHES_BYVALUE   60
+#define DID_ST_STR_SEARCHLEN_BYVALUE  61
+#define DID_ST_STR_CHAINS         62
+#define DID_ST_STR_ADDED          63
+#define DID_ST_STR_DELETED        64
+#define DID_ST_STR_COLLISIONS     65
+#define DID_ST_STR_FOUND          66
+#define DID_ST_STR_FOUND_BYVALUE  67
+
+#define DID_ST_RX_CACHED          68
+#define DID_ST_RX_TABLE           69
+#define DID_ST_RX_TABLE_SIZE      70
+#define DID_ST_RX_REQUESTS        71
+#define DID_ST_RX_REQ_FOUND       72
+#define DID_ST_RX_REQ_COLL        73
+
+#define DID_ST_MB_FILE            74
+#define DID_ST_MB_SWAP            75
+
+#define DID_ST_BOOT_TIME          76
+
+#define DID_STATUS_MAX            77 /* Total number of entries */
+
+
+/* Indices into the array resulting from debug_info(DINFO_DATA, DID_SWAP)
+ */
+
+#define DID_SW_PROGS            0
+#define DID_SW_PROG_SIZE        1
+#define DID_SW_PROG_UNSWAPPED   2
+#define DID_SW_PROG_U_SIZE      3
+#define DID_SW_VARS             4
+#define DID_SW_VAR_SIZE         5
+#define DID_SW_FREE             6
+#define DID_SW_FREE_SIZE        7
+#define DID_SW_FILE_SIZE        8
+#define DID_SW_REUSED           9
+#define DID_SW_SEARCHES        10
+#define DID_SW_SEARCH_LEN      11
+#define DID_SW_F_SEARCHES      12
+#define DID_SW_F_SEARCH_LEN    13
+#define DID_SW_COMPACT         14
+#define DID_SW_RECYCLE_FREE    15
+
+#define DID_SWAP_MAX           16
+
+
+/* Indices into the array resulting from debug_info(DINFO_DATA, DID_MEMORY)
+ */
+
+#define DID_MEM_NAME            0
+#define DID_MEM_SBRK            1
+#define DID_MEM_SBRK_SIZE       2
+#define DID_MEM_LARGE           3
+#define DID_MEM_LARGE_SIZE      4
+#define DID_MEM_LFREE           5
+#define DID_MEM_LFREE_SIZE      6
+#define DID_MEM_LWASTED         7
+#define DID_MEM_LWASTED_SIZE    8
+#define DID_MEM_CHUNK           9
+#define DID_MEM_CHUNK_SIZE     10
+#define DID_MEM_SLAB            (DID_MEM_CHUNK)
+#define DID_MEM_SLAB_SIZE       (DID_MEM_CHUNK_SIZE)
+#define DID_MEM_SMALL          11
+#define DID_MEM_SMALL_SIZE     12
+#define DID_MEM_SFREE          13
+#define DID_MEM_SFREE_SIZE     14
+#define DID_MEM_SWASTED        15
+#define DID_MEM_SWASTED_SIZE   16
+#define DID_MEM_SMALL_OVERHEAD_SIZE  (DID_MEM_SWASTED_SIZE)
+#define DID_MEM_MINC_CALLS     17
+#define DID_MEM_MINC_SUCCESS   19
+#define DID_MEM_MINC_SIZE      19
+#define DID_MEM_PERM           20
+#define DID_MEM_PERM_SIZE      21
+#define DID_MEM_CLIB           22
+#define DID_MEM_CLIB_SIZE      23
+#define DID_MEM_OVERHEAD       24
+#define DID_MEM_ALLOCATED      25
+#define DID_MEM_USED           26
+#define DID_MEM_TOTAL_UNUSED   27
+#define DID_MEM_DEFRAG_CALLS             28
+#define DID_MEM_DEFRAG_CALLS_REQ         29
+#define DID_MEM_SLAB_FREE                 (DID_MEM_DEFRAG_CALLS)
+#define DID_MEM_SLAB_FREE_SIZE            (DID_MEM_DEFRAG_CALLS_REQ)
+#define DID_MEM_DEFRAG_REQ_SUCCESS       30
+#define DID_MEM_DEFRAG_BLOCKS_INSPECTED  31
+#define DID_MEM_DEFRAG_BLOCKS_MERGED     32
+#define DID_MEM_DEFRAG_BLOCKS_RESULT     33
+#define DID_MEM_AVL_NODES      34
+#define DID_MEM_EXT_STATISTICS 35
+
+#define DID_MEMORY_MAX         36
+
+/* Indices into the subarrays of DID_MEM_EXT_STATISTICS (if given) */
+
+#define DID_MEM_ES_MAX_ALLOC   0
+#define DID_MEM_ES_CUR_ALLOC   1
+#define DID_MEM_ES_MAX_FREE    2
+#define DID_MEM_ES_CUR_FREE    3
+#define DID_MEM_ES_AVG_XALLOC  4
+#define DID_MEM_ES_AVG_XFREE   5
+#define DID_MEM_ES_FULL_SLABS  6
+#define DID_MEM_ES_FREE_SLABS  7
+#define DID_MEM_ES_TOTAL_SLABS 8
+
+#define DID_MEM_ES_MAX  9
+
+#endif /* LPC_DEBUG_INFO_H_ */
diff --git a/sys/debug_message.h b/sys/debug_message.h
new file mode 100644
index 0000000..c5944d9
--- /dev/null
+++ b/sys/debug_message.h
@@ -0,0 +1,18 @@
+#ifndef LPC_DEBUG_MESSAGE_H_
+#define LPC_DEBUG_MESSAGE_H_ 1
+
+/* Definitions and macros for the debug_message() */
+
+#define DMSG_DEFAULT 0         /* log to stdout and .debug.log */
+#define DMSG_STDOUT  (1 << 0)  /* log to stdout */
+#define DMSG_STDERR  (1 << 1)  /* log to stderr */
+#define DMSG_LOGFILE (1 << 2)  /* log to .debug.log */
+
+#define DMSG_STAMP   (1 << 3)  /* Prepend the timestamp */
+
+/* Derived macros */
+
+#define DMSG_TARGET (DMSG_STDOUT|DMSG_STDERR|DMSG_LOGFILE)
+  /* Mask for all target bitflags */
+
+#endif /* LPC_DEBUG_MESSAGE_H_ */
diff --git a/sys/defines.h b/sys/defines.h
new file mode 100644
index 0000000..484b66f
--- /dev/null
+++ b/sys/defines.h
@@ -0,0 +1,63 @@
+// MorgenGrauen MUDlib
+//
+// defines.h -- Nuetzliche und oft gebrauchte Definitionen
+//
+// $Id: defines.h 7265 2009-08-21 18:13:22Z Zesstra $
+
+#ifndef _DEFINES_H_
+#define _DEFINES_H_
+
+// defines.h darf leider keine Standard-Includeverzeichnisse benutzen, weils
+// es vom Master selber benutzt wird.
+#include "/sys/break_string.h"
+
+#ifndef ME
+#define ME this_object()
+#endif
+
+#ifndef PL
+#define PL this_player()
+#endif
+
+#ifndef RPL
+#define RPL this_interactive()
+#endif
+
+#ifndef CAP
+#define CAP(str) capitalize(str)
+#endif
+
+#ifndef QPP
+#define QPP QueryPossPronoun
+#endif
+
+#define REAL_UID(x)   (explode(getuid(x),  ".")[<1])
+#define REAL_EUID(x)  (explode(geteuid(x), ".")[<1])
+
+// obsolet, nur aus Kompatibilitaetsgruenden noch vorhanden
+#ifndef IS_CLONE
+#define IS_CLONE(ob) (clonep(ob))
+#endif
+
+// obsolet, nur aus Kompatibilitaetsgruenden noch vorhanden
+#ifndef IS_BLUE
+#define IS_BLUE(ob) (!clonep(ob))
+#endif
+
+#ifndef BLUE_NAME
+#define BLUE_NAME(ob) load_name(ob)
+//#define BLUE_NAME(ob) (explode(object_name(ob),"#")[0])
+#endif
+
+#ifndef IS_PARA
+#define IS_PARA(ob) (sizeof(regexp(({to_string(ob)}),"\\^[1-9][0-9]*$")))
+
+#endif
+
+// obsolet, nur aus Kompatibilitaetsgruenden noch vorhanden
+#ifndef MAX
+#define MAX(a,b) max(a,b)
+#endif
+
+#endif /* _DEFINES_H_ */
+
diff --git a/sys/defuel.h b/sys/defuel.h
new file mode 100644
index 0000000..d5f2f0d
--- /dev/null
+++ b/sys/defuel.h
@@ -0,0 +1,39 @@
+// MorgenGrauen MUDlib
+//
+// defuel.h -- Header fuer defuel.c (Enttanken)
+//
+// $Id: defuel.h,v 1.3 2003/11/11 10:59:06 Muadib Exp $
+
+#ifndef __DEFUEL_H__
+#define __DEFUEL_H__
+
+//--------------------------------------------------------------------------
+//   Statistik
+#define FUELSTAT	"/d/erzmagier/muadib/fuelstat/fuelstat"   
+
+
+//--------------------------------------------------------------------------
+//   Limit - #defines
+//   Ab wieviel kann man ueberhaupt enttanken? (rassenabhaengig)
+#define   P_DEFUEL_LIMIT_FOOD       "defuel_limit_food"
+#define   P_DEFUEL_LIMIT_DRINK      "defuel_limit_drink"
+
+//--------------------------------------------------------------------------
+//   Time - #defines
+//   Wielange muss man zwischen dem Enttanken verdauen? (rassenabhaengig)
+#define   P_DEFUEL_TIME_FOOD        "defuel_time_food"
+#define   P_DEFUEL_TIME_DRINK       "defuel_time_drink"
+
+//--------------------------------------------------------------------------
+//   Amount - #defines
+//   Wieviel Prozent der aktuellen P_FOOD/P_DRINK Werte kann man enttanken?
+//   (rassenabhaenhig)
+#define   P_DEFUEL_AMOUNT_FOOD      "defuel_amount_food"
+#define   P_DEFUEL_AMOUNT_DRINK     "defuel_amount_drink"
+
+// Returnwerte der zu verwendeten Interfaces
+#define   NO_DEFUEL                  0  // leer
+#define   DEFUEL_TOO_LOW            -1  // nicht leer, aber zu wenig
+#define   DEFUEL_TOO_SOON           -2  // letztes entanken ist noch nicht 
+                                        // lange genug her
+#endif // __DEFUEL_H__
diff --git a/sys/doorroom.h b/sys/doorroom.h
new file mode 100644
index 0000000..8f63143
--- /dev/null
+++ b/sys/doorroom.h
@@ -0,0 +1,73 @@
+// MorgenGrauen MUDlib
+//
+// doorroom.h -- new doors, managed by doormaster
+//
+// $Id: doorroom.h 9128 2015-01-26 17:42:21Z Arathorn $
+
+#ifndef _DOORROOM_H_
+#define _DOORROOM_H_
+
+#define DOOR_MASTER "/obj/doormaster"
+#define P_DOOR_INFOS "door_infos"   // Informationen
+
+#define DOOR_OPEN     0x01 // Tuer initial offen
+#define DOOR_CLOSED   0x02 // Tuer initial zu
+#define DOOR_NEEDKEY  0x08 // Man braucht einen Schluessel zum Oeffnen
+#define DOOR_CLOSEKEY 0x10 // Man braucht einen Schluessel zum Schliessen
+#define DOOR_RESET_CL 0x20 // Tuer schliesst sich bei jedem reset()
+#define DOOR_RESET_OP 0x40 // Tuer oeffnet sich bei jedem reset()
+
+#define D_DEST 0
+#define D_CMDS 1
+#define D_IDS 2
+
+// properties
+// Ab hier die aenderbaren Eigenschaften der Tuer
+#define D_FLAGS 3
+#define D_LONG 4
+#define D_SHORT 5
+#define D_NAME 6
+#define D_GENDER 7
+#define D_FUNC 8
+// Funktion, die VOR dem Durchschreiten der Tuer aufgerufen wird (im Startraum)
+#define D_MSGS 9
+// Messages fuer Move
+#define D_FUNC2 10
+// Funktion, die NACH dem Durchschreiten der Tuer aufgerufen wird (im Zielraum)
+#define D_TESTFUNC 11
+// Funktion, die testet, ob die Tuer durchschritten werden darf (im Startraum)
+#define D_RESET_MSG 12
+// Meldung beim Tuer-Reset (tuer->name(WER,0)+D_RESET_MSG).
+#define D_OPEN_WITH_MOVE 13
+// Falls gesetzt wird die Tuer auch mit dem Bewegungsbefehl geoeffnet
+// und durchschritten, falls oeffnen erfolgreich
+
+#define D_MINPROPS 3
+#define D_MAXPROPS 13
+
+#define D_STATUS_LOCKED -2
+#define D_STATUS_CLOSED -1
+#define D_STATUS_OPEN    1
+
+#endif /* _DOORROOM_H_ */
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __DOORROOM_H_PROTO__
+#define __DOORROOM_H_PROTO__
+
+// prototypes
+mapping QueryAllDoors();
+varargs int NewDoor(string|string* cmds, string dest, string|string* ids,
+                    mapping|<int|string|string*>* props);
+void    init_doors ();
+string  look_doors ();
+void    reset_doors ();
+varargs int go_door (string str);
+//int     oeffnen (string str);
+//int     schliessen (string str);
+int     set_doors (string *cmds, int open);
+varargs string GetExits(object viewer); 
+
+#endif /* __DOORROOM_H_PROTO__ */
+#endif /* NEED_PROTOTYPES */
diff --git a/sys/driver_hook.h b/sys/driver_hook.h
new file mode 100644
index 0000000..0b97f80
--- /dev/null
+++ b/sys/driver_hook.h
@@ -0,0 +1,36 @@
+#ifndef LPC_DRIVER_HOOK_H_
+#define LPC_DRIVER_HOOK_H_ 1
+
+/* --- Driver Hooks ---
+ */
+
+#define H_MOVE_OBJECT0           0
+#define H_MOVE_OBJECT1           1
+#define H_LOAD_UIDS              2
+#define H_CLONE_UIDS             3
+#define H_CREATE_SUPER           4
+#define H_CREATE_OB              5
+#define H_CREATE_CLONE           6
+#define H_RESET                  7
+#define H_CLEAN_UP               8
+#define H_MODIFY_COMMAND         9
+#define H_NOTIFY_FAIL           10
+#define H_NO_IPC_SLOT           11
+#define H_INCLUDE_DIRS          12
+#define H_TELNET_NEG            13
+#define H_NOECHO                14
+#define H_ERQ_STOP              15
+#define H_MODIFY_COMMAND_FNAME  16
+#define H_COMMAND               17
+#define H_SEND_NOTIFY_FAIL      18
+#define H_AUTO_INCLUDE          19
+#define H_DEFAULT_METHOD        20
+#define H_DEFAULT_PROMPT        21
+#define H_PRINT_PROMPT          22
+#define H_REGEXP_PACKAGE        23
+#define H_MSG_DISCARDED         24
+
+#define NUM_DRIVER_HOOKS        25  /* Number of hooks */
+
+#endif /* LPC_DRIVER_HOOK_ */
+
diff --git a/sys/driver_info.h b/sys/driver_info.h
new file mode 100644
index 0000000..336975c
--- /dev/null
+++ b/sys/driver_info.h
@@ -0,0 +1,237 @@
+#ifndef LPC_DRIVER_INFO_H_
+#define LPC_DRIVER_INFO_H_
+
+/* Definition of argument values for driver_info()
+ */
+
+#include "configuration.h"
+
+/* Driver Environment */
+#define DI_BOOT_TIME                                          -1
+
+/* LPC Runtime status */
+#define DI_CURRENT_RUNTIME_LIMITS                            -10
+#define DI_EVAL_NUMBER                                       -11
+
+/* Network configuration */
+#define DI_MUD_PORTS                                         -20
+#define DI_UDP_PORT                                          -21
+
+/* Memory management */
+#define DI_MEMORY_RESERVE_USER                               -30
+#define DI_MEMORY_RESERVE_MASTER                             -31
+#define DI_MEMORY_RESERVE_SYSTEM                             -32
+
+/* Traces */
+#define DI_TRACE_CURRENT                                     -40
+#define DI_TRACE_CURRENT_DEPTH                               -41
+#define DI_TRACE_CURRENT_AS_STRING                           -42
+#define DI_TRACE_LAST_ERROR                                  -43
+#define DI_TRACE_LAST_ERROR_AS_STRING                        -44
+#define DI_TRACE_LAST_UNCAUGHT_ERROR                         -45
+#define DI_TRACE_LAST_UNCAUGHT_ERROR_AS_STRING               -46
+
+/* LPC Runtime statistics */
+#define DI_NUM_FUNCTION_NAME_CALLS                          -100
+#define DI_NUM_FUNCTION_NAME_CALL_HITS                      -101
+#define DI_NUM_FUNCTION_NAME_CALL_MISSES                    -102
+
+#define DI_NUM_OBJECTS_LAST_PROCESSED                       -103
+
+#define DI_NUM_HEARTBEAT_TOTAL_CYCLES                       -104
+#define DI_NUM_HEARTBEAT_ACTIVE_CYCLES                      -105
+#define DI_NUM_HEARTBEATS_LAST_PROCESSED                    -106
+
+#define DI_NUM_STRING_TABLE_STRINGS_ADDED                   -110
+#define DI_NUM_STRING_TABLE_STRINGS_REMOVED                 -111
+#define DI_NUM_STRING_TABLE_LOOKUPS_BY_VALUE                -112
+#define DI_NUM_STRING_TABLE_LOOKUPS_BY_INDEX                -113
+#define DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_VALUE           -114
+#define DI_NUM_STRING_TABLE_LOOKUP_STEPS_BY_INDEX           -115
+#define DI_NUM_STRING_TABLE_HITS_BY_VALUE                   -116
+#define DI_NUM_STRING_TABLE_HITS_BY_INDEX                   -117
+#define DI_NUM_STRING_TABLE_COLLISIONS                      -118
+
+#define DI_NUM_REGEX_LOOKUPS                                -120
+#define DI_NUM_REGEX_LOOKUP_HITS                            -121
+#define DI_NUM_REGEX_LOOKUP_MISSES                          -122
+#define DI_NUM_REGEX_LOOKUP_COLLISIONS                      -123
+
+/* Network statistics */
+#define DI_NUM_MESSAGES_OUT                                 -200
+#define DI_NUM_PACKETS_OUT                                  -201
+#define DI_NUM_PACKETS_IN                                   -202
+#define DI_SIZE_PACKETS_OUT                                 -203
+#define DI_SIZE_PACKETS_IN                                  -204
+
+/* Load */
+#define DI_LOAD_AVERAGE_COMMANDS                            -300
+#define DI_LOAD_AVERAGE_LINES                               -301
+#define DI_LOAD_AVERAGE_PROCESSED_OBJECTS                   -302
+#define DI_LOAD_AVERAGE_PROCESSED_OBJECTS_RELATIVE          -303
+#define DI_LOAD_AVERAGE_PROCESSED_HEARTBEATS_RELATIVE       -304
+
+/* Memory use statistics */
+#define DI_NUM_ACTIONS                                      -400
+#define DI_NUM_CALLOUTS                                     -401
+#define DI_NUM_HEARTBEATS                                   -402
+#define DI_NUM_SHADOWS                                      -403
+#define DI_NUM_OBJECTS                                      -404
+#define DI_NUM_OBJECTS_SWAPPED                              -405
+#define DI_NUM_OBJECTS_IN_LIST                              -406
+#define DI_NUM_OBJECTS_IN_TABLE                             -407
+#define DI_NUM_OBJECTS_DESTRUCTED                           -408
+#define DI_NUM_OBJECTS_NEWLY_DESTRUCTED                     -409
+#define DI_NUM_OBJECT_TABLE_SLOTS                           -410
+#define DI_NUM_PROGS                                        -411
+#define DI_NUM_PROGS_SWAPPED                                -412
+#define DI_NUM_PROGS_UNSWAPPED                              -413
+#define DI_NUM_ARRAYS                                       -414
+#define DI_NUM_MAPPINGS                                     -415
+#define DI_NUM_MAPPINGS_CLEAN                               -416
+#define DI_NUM_MAPPINGS_HASH                                -417
+#define DI_NUM_MAPPINGS_HYBRID                              -418
+#define DI_NUM_STRUCTS                                      -419
+#define DI_NUM_STRUCT_TYPES                                 -420
+#define DI_NUM_VIRTUAL_STRINGS                              -421
+#define DI_NUM_STRINGS                                      -422
+#define DI_NUM_STRINGS_TABLED                               -423
+#define DI_NUM_STRINGS_UNTABLED                             -424
+#define DI_NUM_STRING_TABLE_SLOTS                           -425
+#define DI_NUM_STRING_TABLE_SLOTS_USED                      -426
+#define DI_NUM_REGEX                                        -427
+#define DI_NUM_REGEX_TABLE_SLOTS                            -428
+
+#define DI_SIZE_ACTIONS                                     -450
+#define DI_SIZE_CALLOUTS                                    -451
+#define DI_SIZE_HEARTBEATS                                  -452
+#define DI_SIZE_SHADOWS                                     -453
+#define DI_SIZE_OBJECTS                                     -454
+#define DI_SIZE_OBJECTS_SWAPPED                             -455
+#define DI_SIZE_OBJECT_TABLE                                -456
+#define DI_SIZE_PROGS                                       -457
+#define DI_SIZE_PROGS_SWAPPED                               -458
+#define DI_SIZE_PROGS_UNSWAPPED                             -459
+#define DI_SIZE_ARRAYS                                      -460
+#define DI_SIZE_MAPPINGS                                    -461
+#define DI_SIZE_STRUCTS                                     -462
+#define DI_SIZE_STRUCT_TYPES                                -463
+#define DI_SIZE_STRINGS                                     -464
+#define DI_SIZE_STRINGS_TABLED                              -465
+#define DI_SIZE_STRINGS_UNTABLED                            -466
+#define DI_SIZE_STRING_TABLE                                -467
+#define DI_SIZE_STRING_OVERHEAD                             -468
+#define DI_SIZE_REGEX                                       -469
+#define DI_SIZE_BUFFER_FILE                                 -470
+#define DI_SIZE_BUFFER_SWAP                                 -471
+
+/* Memory swapper statistics */
+#define DI_NUM_SWAP_BLOCKS                                  -500
+#define DI_NUM_SWAP_BLOCKS_FREE                             -501
+#define DI_NUM_SWAP_BLOCKS_REUSE_LOOKUPS                    -502
+#define DI_NUM_SWAP_BLOCKS_REUSE_LOOKUP_STEPS               -503
+#define DI_NUM_SWAP_BLOCKS_FREE_LOOKUPS                     -505
+#define DI_NUM_SWAP_BLOCKS_FREE_LOOKUP_STEPS                -506
+#define DI_SIZE_SWAP_BLOCKS                                 -507
+#define DI_SIZE_SWAP_BLOCKS_FREE                            -508
+#define DI_SIZE_SWAP_BLOCKS_REUSED                          -509
+#define DI_SWAP_RECYCLE_PHASE                               -510
+
+/* Memory allocator statistics */
+#define DI_MEMORY_ALLOCATOR_NAME                            -600
+
+#define DI_NUM_SYS_ALLOCATED_BLOCKS                         -610
+#define DI_NUM_LARGE_BLOCKS_ALLOCATED                       -611
+#define DI_NUM_LARGE_BLOCKS_FREE                            -612
+#define DI_NUM_LARGE_BLOCKS_WASTE                           -613
+#define DI_NUM_SMALL_BLOCKS_ALLOCATED                       -614
+#define DI_NUM_SMALL_BLOCKS_FREE                            -615
+#define DI_NUM_SMALL_BLOCKS_WASTE                           -616
+#define DI_NUM_SMALL_BLOCK_CHUNKS                           -617
+#define DI_NUM_UNMANAGED_BLOCKS                             -618
+#define DI_NUM_FREE_BLOCKS_AVL_NODES                        -619
+
+#define DI_SIZE_SYS_ALLOCATED_BLOCKS                        -630
+#define DI_SIZE_LARGE_BLOCKS_ALLOCATED                      -631
+#define DI_SIZE_LARGE_BLOCKS_FREE                           -632
+#define DI_SIZE_LARGE_BLOCKS_WASTE                          -633
+#define DI_SIZE_LARGE_BLOCK_OVERHEAD                        -634
+#define DI_SIZE_SMALL_BLOCKS_ALLOCATED                      -635
+#define DI_SIZE_SMALL_BLOCKS_FREE                           -636
+#define DI_SIZE_SMALL_BLOCKS_WASTE                          -637
+#define DI_SIZE_SMALL_BLOCK_OVERHEAD                        -638
+#define DI_SIZE_SMALL_BLOCK_CHUNKS                          -639
+#define DI_SIZE_UNMANAGED_BLOCKS                            -640
+#define DI_SIZE_MEMORY_USED                                 -641
+#define DI_SIZE_MEMORY_UNUSED                               -642
+#define DI_SIZE_MEMORY_OVERHEAD                             -643
+
+#define DI_NUM_INCREMENT_SIZE_CALLS                         -650
+#define DI_NUM_INCREMENT_SIZE_CALL_SUCCESSES                -651
+#define DI_SIZE_INCREMENT_SIZE_CALL_DIFFS                   -652
+#define DI_NUM_REPLACEMENT_MALLOC_CALLS                     -653
+#define DI_SIZE_REPLACEMENT_MALLOC_CALLS                    -654
+#define DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_FULL            -655
+#define DI_NUM_MEMORY_DEFRAGMENTATION_CALLS_TARGETED        -656
+#define DI_NUM_MEMORY_DEFRAGMENTATION_CALL_TARGET_HITS      -657
+#define DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_INSPECTED      -658
+#define DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_MERGED         -659
+#define DI_NUM_MEMORY_DEFRAGMENTATION_BLOCKS_RESULTING      -660
+
+#define DI_MEMORY_EXTENDED_STATISTICS                       -670
+
+/* Status texts */
+#define DI_STATUS_TEXT_MEMORY                               -700
+#define DI_STATUS_TEXT_TABLES                               -701
+#define DI_STATUS_TEXT_SWAP                                 -702
+#define DI_STATUS_TEXT_MALLOC                               -703
+#define DI_STATUS_TEXT_MALLOC_EXTENDED                      -704
+
+/* Indices into the subarrays of DI_MEMORY_EXTENDED_STATISTICS (if given) */
+
+#define DIM_ES_MAX_ALLOC   0
+#define DIM_ES_CUR_ALLOC   1
+#define DIM_ES_MAX_FREE    2
+#define DIM_ES_CUR_FREE    3
+#define DIM_ES_AVG_XALLOC  4
+#define DIM_ES_AVG_XFREE   5
+#define DIM_ES_FULL_SLABS  6
+#define DIM_ES_FREE_SLABS  7
+#define DIM_ES_TOTAL_SLABS 8
+
+#define DIM_ES_MAX  9
+
+
+/* Definition of argument values for dump_driver_info()
+ */
+
+#define DDI_OBJECTS                     0
+#define DDI_OBJECTS_DESTRUCTED          1
+#define DDI_OPCODES                     2
+#define DDI_MEMORY                      3
+
+/* Indices into the subarrays resulting from driver_info(DI_TRACE_*)
+ */
+
+#define TRACE_TYPE    0
+#define TRACE_NAME    1
+#define TRACE_PROGRAM 2
+#define TRACE_OBJECT  3
+#define TRACE_LOC     4
+#ifdef __EVAL_COST_TRACE__
+#define TRACE_EVALCOST 5
+
+#define TRACE_MAX     6
+#else
+#define TRACE_MAX     5
+#endif
+
+/* Values for entry TRACE_TYPE */
+
+#define TRACE_TYPE_SYMBOL  0
+#define TRACE_TYPE_SEFUN   1
+#define TRACE_TYPE_EFUN    2
+#define TRACE_TYPE_LAMBDA  3
+#define TRACE_TYPE_LFUN    4
+
+#endif /* LPC_DRIVER_INFO_H_ */
diff --git a/sys/erq.h b/sys/erq.h
new file mode 100644
index 0000000..3d0edfd
--- /dev/null
+++ b/sys/erq.h
@@ -0,0 +1,59 @@
+/* external request demon interface definitions */
+
+#ifndef LPC__ERQ_H__
+#define LPC__ERQ_H__ 1
+
+/* servive request types */
+
+#define ERQ_RLOOKUP     0  /* Lookup ip -> name */
+#define ERQ_EXECUTE     1  /* Execute a program */
+#define ERQ_FORK        2  /* Fork a program */
+#define ERQ_AUTH        3  /* Connect to a remote authd */
+#define ERQ_SPAWN       4  /* Spawn a program */
+#define ERQ_SEND        5  /* Send data to a program or connection */
+#define ERQ_KILL        6  /* Kill a program or connection */
+#define ERQ_OPEN_UDP    7  /* Open a UDP socket */
+#define ERQ_OPEN_TCP    8  /* Open a TCP connection */
+#define ERQ_LISTEN      9  /* Open a TCP accept-socket */
+#define ERQ_ACCEPT     10  /* Accept a connection from a accept-socket */
+#define ERQ_LOOKUP     11  /* Lookup name -> ip */
+
+#ifdef __IPV6__
+#define ERQ_RLOOKUPV6  12  /* Lookup name/ip6 */
+#endif
+
+/* Additional service request type flags evaluated by efun send_erq().
+ * The ERQ itself won't get to see it.
+ */
+
+#define ERQ_CB_STRING  (1 << 31)  /* Callback closure takes a string arg */
+
+
+/* answers from ERQ_EXECUTE / ERQ_FORK */
+
+#define ERQ_OK		  0
+#define ERQ_SIGNALED	  1
+#define ERQ_E_NOTFOUND	  2  /* process not found by wait */
+#define ERQ_E_UNKNOWN	  3  /* unknown exit condition from wait() */
+#define ERQ_E_ARGLENGTH	  4
+#define ERQ_E_ARGFORMAT	  5
+#define ERQ_E_ARGNUMBER	  6
+#define ERQ_E_ILLEGAL	  7
+#define ERQ_E_PATHLEN	  8
+#define ERQ_E_FORKFAIL	  9
+#define ERQ_E_TICKET	 11
+#define ERQ_E_INCOMPLETE 12
+#define ERQ_E_WOULDBLOCK 13
+#define ERQ_E_PIPE	 14
+#define ERQ_STDOUT	 15  /* Normal data received */
+#define ERQ_STDERR	 16
+#define ERQ_EXITED	 17  /* Connection closed on EOF */
+#define ERQ_E_NSLOTS	 18
+
+/* reserved handles */
+
+#define ERQ_HANDLE_RLOOKUP (-1)
+#define ERQ_HANDLE_KEEP_HANDLE (-2)
+#define ERQ_HANDLE_RLOOKUPV6 (-3)
+
+#endif /* LPC__ERQ_H__ */
diff --git a/sys/events.h b/sys/events.h
new file mode 100644
index 0000000..a5f45f6
--- /dev/null
+++ b/sys/events.h
@@ -0,0 +1,64 @@
+// MorgenGrauen MUDlib
+//
+// /sys/events.h -- Standard-Event-ID und Prototypen der Mudlib
+//
+// $Id$
+
+#ifndef __EVENTS_H__
+#define __EVENTS_H__
+
+#define EVENTD "/p/daemon/eventd"
+
+// Standardlib
+#define EVT_LIB_LOGIN               "evt_lib_login"
+#define EVT_LIB_LOGOUT              "evt_lib_logout"
+#define EVT_LIB_PLAYER_DEATH        "evt_lib_player_death"
+#define EVT_LIB_PLAYER_DELETION     "evt_lib_player_deletion"
+#define EVT_LIB_PLAYER_CREATION	    "evt_lib_player_creation"
+#define EVT_LIB_NPC_DEATH(x)        ("evt_lib_npc_death_"+x)
+#define EVT_LIB_ADVANCE             "evt_lib_advance"
+#define EVT_LIB_QUEST_SOLVED        "evt_lib_quest_solved"
+#define EVT_LIB_MINIQUEST_SOLVED    "evt_lib_miniquest_solved"
+#define EVT_LIB_PLAYER_ATTR_CHANGE  "evt_lib_player_attr_change"
+#define EVT_LIB_CLOCK               "evt_lib_clock"
+#define EVT_LIB_DATECHANGE          "evt_lib_datechange"
+#define EVT_LIB_NEW_ERROR           "evt_lib_eventd_new_error"
+
+// Gilden
+#define EVT_GUILD_CHANGE        "evt_guild_change"
+#define EVT_GUILD_ADVANCE       "evt_guild_advance"
+
+
+// Konstanten fuer Event-Daten. s. Manpages der einzelnen Events, die
+// Bedeutungen dieser Keys koennen sich jeweils leicht unterscheiden!
+#define E_TIME            "evt_time"
+#define E_OBJECT          "evt_object"
+#define E_OBNAME          "evt_object_name"
+#define E_ENVIRONMENT     "evt_environment"
+#define E_PLNAME          "evt_player_name"
+#define E_EXTERNAL_DEATH  "evt_external_death"
+#define E_POISON_DEATH    "evt_poison_death"
+#define E_CORPSE          "evt_corpse"
+#define E_GUILDNAME       "evt_guild_name"
+#define E_LAST_GUILDNAME  "evt_last_guild_name"
+#define E_QUESTNAME       "evt_quest_name"
+#define E_QP_GRANTED      "evt_qp_granted"
+#define E_MINIQUESTNAME   "evt_miniquest_name"
+#define E_MQP_GRANTED     "evt_mqp_granted"
+
+#endif // __EVENTS_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __EVENTS_PROTOTYPES_H_
+#define __EVENTS_PROTOTYPES_H_
+        int CheckEventID(string eid);
+        int RegisterEvent(string eid, string fun, object ob);
+        int UnregisterEvent(string eid, object ob);
+varargs int TriggerEvent(string eid, mixed args);
+
+#endif // __EVENTS_PROTOTYPES_H_
+
+
+#endif // NEED_PROTOTYPES
+
diff --git a/sys/files.h b/sys/files.h
new file mode 100644
index 0000000..3d7e2c8
--- /dev/null
+++ b/sys/files.h
@@ -0,0 +1,29 @@
+#ifndef LPC_FILES_H_
+#define LPC_FILES_H_ 1
+
+/* Definitions and macros for the various file efuns */
+
+/* Special return values of file_size() and the size part of get_dir().
+ */
+
+#define FSIZE_NOFILE  -1  /* File doesn't exist or is not readable */
+#define FSIZE_DIR     -2  /* The file is actually a directory */
+
+
+/* Flag values for get_dir().
+ */
+
+#define GETDIR_EMPTY       (0)  /* return an empty array (not very useful) */
+#define GETDIR_NAMES     (0x01)  /* return the filenames */
+#define GETDIR_SIZES     (0x02)  /* return the file sizes */
+#define GETDIR_DATES     (0x04)  /* return the dates of last modification */
+/*                       (0x08) unused */
+#define GETDIR_ACCESS    (0x40)  /* return the dates of last access */
+#define GETDIR_MODES     (0x80)  /* return the file mode */
+
+#define GETDIR_PATH      (0x10)  /* with _NAMES: add the path to the filenames */
+#define GETDIR_UNSORTED  (0x20)  /* return the results unsorted */
+
+#define GETDIR_ALL       (0xDF)  /* return all */
+
+#endif /* LPC_FILES_H_ */
diff --git a/sys/fishing.h b/sys/fishing.h
new file mode 100644
index 0000000..be660eb
--- /dev/null
+++ b/sys/fishing.h
@@ -0,0 +1,6 @@
+// MorgenGrauen MUDlib
+//
+// $Id: fishing.h 8930 2014-09-08 22:00:32Z Arathorn $
+
+#include <items/fishing/fishing.h>
+
diff --git a/sys/food.h b/sys/food.h
new file mode 100644
index 0000000..085c6cb
--- /dev/null
+++ b/sys/food.h
@@ -0,0 +1,198 @@
+// MorgenGrauen MUDlib
+//
+// /sys/food.sys  - Defines und Prototypen fuer Lebensmittel
+//
+// $Id: food.h 7702 2010-10-26 21:22:37Z Zesstra $
+
+#ifndef _FOOD_H_
+#define _FOOD_H_
+
+#include <properties.h>
+
+// Fuellgrad pro Portion Essen (defined in life.h)
+// #define   P_FOOD
+
+// Fuellgrad pro Schluck des Getraenkes (defined in life.h)
+// #define   P_DRINK
+
+// Menge an Alkohol, die die Speise pro Portion hat (defined in life.h)
+// #define   P_ALCOHOL
+
+// Heilwirkung der Speise (defined in life.h)
+// #define   P_SP
+// #define   P_HP
+
+// Gewicht pro Portion (defined in restrictions.h)
+// in P_EMPTY_PROPS ist es das Gewicht des Behaelters
+// mit beiden Angaben wird das Gewicht immer korrekt berechnet
+// #define   P_WEIGHT
+
+// Wert pro Portion (defined in properties.h)
+// in P_EMPTY_PROPS ist es der Wert des Behaelters
+// mit beiden Angaben wird der Wert immer korrekt berechnet
+// #define   P_VALUE
+
+// Die Meldung, die die Umstehenden erhalten, wenn gegessen/getrunken wird.
+#define   P_CONSUME_MSG        "std_food_consume_msg"
+
+// Die Meldung, die derjenige kriegt, der das Ding isst/trinkt.
+#define   P_EATER_MSG          "std_food_eater_msg"
+
+// Message, wenn man die Speise beim Konsumieren gar nicht in der Hand hat
+// Wenn man die Prop loescht, kann die Speise immer vertilgt werden
+#define   P_ENV_MSG            "std_food_env_msg"
+
+// Die Meldung, wenn die Speise leer ist
+#define   P_EMPTY_MSG          "std_food_empty_msg"
+
+// Die Meldung, wenn Getraenke gegessen werden
+#define   P_NOFOOD_MSG         "std_food_nofood_msg"
+
+// Die Meldung, wenn Speisen getrunken werden
+#define   P_NODRINK_MSG        "std_food_nodrink_msg"
+
+// Die Meldung, wenn die Speise gerade verdirbt
+#define   P_BAD_MSG            "std_food_bad_msg"
+
+// Die Meldung, wenn nichts mehr gegessen werden kann
+#define   P_FOOD_FULL_MSG      "std_food_full_msg"
+// Die Meldung, wenn nichts mehr getrunken werden kann
+#define   P_DRINK_FULL_MSG     "std_food_drink_full_msg"
+// Die Meldung, wenn nichts mehr alkoholisches geht
+#define   P_ALC_FULL_MSG       "std_food_alc_full_msg"
+// Die Meldung, wenn eine verdorbene Speise zerstoert wird
+// wird nur verwendet, wenn P_DESTROY_BAD > 0 ist
+#define   P_REMOVE_MSG         "std_food_remove_message"
+
+// Mapping mit Properties fuer den leeren Behaelter.
+// Alle enthaltenen Properties werden gesetzt, wenn
+// keine Portionen mehr vorhanden sind, bereits in diesen Properties
+// eingetragene Werte werden ueberschrieben!
+// Wenn diese Property nicht gesetzt ist, wird die Speise zerstoert,
+// wenn alle Portionen aufgebraucht ist.
+// Achtung: es werden keine closures unterstuetzt!
+#define   P_EMPTY_PROPS        "std_food_empty_props"
+
+// Wieviele Portionen hat die Speise
+#define   P_PORTIONS           "std_food_portions"
+
+// Verteilung der Heilungsrate pro Portion (siehe H_DISTRIBUTION)
+#define   P_DISTRIBUTION       "std_food_distribution"
+
+// Anzahl der Sekunden, bis die Speise verdorben ist.
+// Es muss ein Wert groesser 0 eingetragen werden.
+// Wenn kein Wert gesetzt wird, verdirbt die Speise beim naechsten Reset
+// Der Timer startet immer erst, wenn ein Spieler die Speise das erste Mal
+// in die Hand bekommt
+#define   P_LIFETIME           "std_food_lifetime"
+// Anzahl der Resets, bis die Speise verdorben ist.
+// P_RESET_LIFETIME und P_LIFETIME sind komplementaer, das heisst, dass
+// beim Setzen der einen die andere automatisch berechnet wird. Man braucht
+// daher nicht beide zu setzen, bzw. sollte dies gar nicht erst tun.
+#define   P_RESET_LIFETIME     "std_food_lifetime_reset"
+
+// Flag, ob die Speise verdirbt
+// Standard: 0 -> Darf nur mit Balancegenehmigung geaendert werden!
+#define   P_NO_BAD             "std_food_no_bad"
+
+// Zeit in Sekunden, wann die Speise nach dem Schlechtwerden zerstoert wird.
+// wenn P_EMPTY_PROPS gesetzt ist, wird der Behaelter geleert.
+// Standard: -1 = sofort beim Schlechtwerden
+//            0 = wird nicht zerstoert
+//           >0 = Anzahl der Sekunden
+#define   P_DESTROY_BAD        "std_food_destroy_bad"
+#define   DESTROY_NEVER         0
+#define   DESTROY_BAD          -1
+
+//============================================================================
+// Kompatibilitaetscode
+#define   F_IS_FOOD            "old_is_food"
+#define   F_IS_DRINK           "old_is_drink"
+#define   F_IS_FULL            "old_is_full"
+#define   F_MESSAGE            "old_mess"
+#define   F_EATER              "old_eater"
+#define   F_EMPTY_CONTAINER    "old_empty_con"
+#define   F_EMPTY_GENDER       "old_empty_gender"
+#define   F_EMPTY_ID           "old_empty_id"
+#define   F_EMPTY_LONG         "old_empty_long"
+#define   F_ALC                "old_alc"
+#define   F_LIQUID             "old_water"
+#define   F_FOOD_SIZE          "old_food_size"
+#define   F_POTION             "old_potion"
+#define   F_HEAL_SP            "old_heal_sp"
+#define   F_HEAL_HP            "old_heal_hp"
+#define   F_NO_CONTAINER       "old_no_con"
+#define   F_BAD                "old_bad"
+#define   F_POISON             "old_poison"
+
+#endif // _FOOD_H_
+
+#ifdef NEED_PROTOTYPES
+#ifndef _FOOD_H_PROTOTYPES_
+#define _FOOD_H_PROTOTYPES_
+
+public int cmd_eat(string str);
+public int cmd_drink(string str);
+public int is_eatable();
+public int is_drinkable();
+public int is_not_empty();
+public int is_bad();
+
+// Anzahl der Sekunden, die das Lebensmittel bereits hinter sich hat,
+// seit es mit einem Spieler in Beruehrung kam
+// wenn 0 zurueckgegeben wird, ist der Timer zum Schlechtwerden noch nicht
+// gestartet worden
+public int get_current_lifetime();
+
+// Startet den Timer zum Verderben der Speise, falls keine Gruende dagegen
+// sprechen, wird von init() und NotifyMove aufgerufen, kann in speziellen
+// Situationen aber auch manuell aufgerufen werden (siehe man food)
+public void start_lifetime();
+
+// wird aufgerufen, wenn die Speise verdorben ist und die Zeit aus
+// P_DESTROY_BAD abgelaufen ist
+// return 1, falls Objekt zerstoert wird, sonst 0
+public int make_destroy();
+
+// wird aufgerufen, wenn die Speise gerade verdirbt
+// return 1, falls Objekt dabei zerstoert wird, sonst 0
+public int make_bad();
+
+// wird aufgerufen, sobald die letzte Portion gegessen/getrunken wurde
+// Falls P_EMPTY_PROPS 0 ist, wird das Objekt zerstoert
+// return 1, falls Objekt dabei zerstoert wird, sonst 0
+public int make_empty();
+
+// wird jedesmal aufgerufen, wenn eine verdorbene Speise konsumiert wird.
+// Als Argument wird das Mapping uebergeben, das consume() im Lebewesen
+// uebergeben werden soll. Dieses Mapping kann veraendert werden.
+// Standard: Die Heilwirkung wird entfernt und stattdessen wird das Lebewesen
+//           vergiftet.
+protected void consume_bad(mapping entry_info);
+
+// wird aus cmd_eat und cmd_drink aufgerufen und fuehrt das Konsumieren durch
+protected int consume();
+// wird aus consume() aufgerufen und fuehrt das Konsumieren durch
+// ist ausgelagert, damit man das testflag bei Bedarf nutzen kann
+protected varargs int try_consume(int testonly);
+
+// Futtern hat geklappt
+protected void success_consume();
+// Futtern hat nicht geklappt (leerer Behaelter ist hier kein Grund!)
+// Wird aus try_consume() gerufen.
+// reason entspricht dem Rueckgabewert von consume() im Lebewesen.
+protected void failed_consume(int reason);
+
+// Methode, die Meldungen zu Statusaenderungen verarbeitet und korrekt ausgibt.
+// Es wird der Name der auszugebenen Property uebergeben.
+// Es werden alle notwendigen Ersetzungen per replace_personal gemacht und
+// geprueft, ob dem Besitzer oder dem Raum die Meldung ausgegeben werden
+// muss.
+// Hierueber sollten nur Meldungen ausgegeben werden, die durch Aenderungen
+// an der Speise im Reset ausgeloest werden, also im reset selbst und in den
+// make_*-Methoden.
+protected void message(string prop);
+
+#endif // _FOOD_H_PROTOTYPES_
+#endif // NEED_PROTOTYPES
+
diff --git a/sys/functionlist.h b/sys/functionlist.h
new file mode 100644
index 0000000..61dcd64
--- /dev/null
+++ b/sys/functionlist.h
@@ -0,0 +1,62 @@
+#ifndef LPC_FUNCTIONLIST_H
+#define LPC_FUNCTIONLIST_H
+
+#ifndef __DRIVER_SOURCE__
+#include "lpctypes.h"
+
+#define NAME_INHERITED      0x80000000 /* Defined by inheritance         */
+#define TYPE_MOD_STATIC     0x40000000 /* Static function or variable    */
+#define TYPE_MOD_NO_MASK    0x20000000 /* The nomask => not redefineable */
+#define TYPE_MOD_PRIVATE    0x10000000 /* Can't be inherited             */
+#define TYPE_MOD_PUBLIC     0x08000000 /* Force inherit through private  */
+#define TYPE_MOD_VARARGS    0x04000000 /* Used for type checking         */
+#define TYPE_MOD_VIRTUAL    0x02000000  /* can be re- and cross- defined */
+#define TYPE_MOD_PROTECTED  0x01000000 /* cannot be called externally    */
+#define TYPE_MOD_XVARARGS   0x00800000  /* accepts optional arguments    */
+#define TYPE_MOD_NOSAVE     TYPE_MOD_STATIC  /* vars: can't be saved     */
+  /* Internally TYPE_MOD_NOSAVE is 0x00400000, but that never leaves
+   * the compiler.
+   */
+#define NAME_CROSS_DEFINED  0x00080000 /* function defined from other program */
+#define NAME_HIDDEN         0x00020000 /* Not visible for inheritance    */
+#define NAME_PROTOTYPE      0x00010000 /* Defined by a prototype only    */
+#define NAME_UNDEFINED      0x00008000 /* Not defined yet                */
+#define NAME_TYPES_LOST     0x00004000 /* inherited, no save_types       */
+
+#endif /* !__DRIVER_SOURCE__ */
+
+
+/* Return value flag types for functionlist() and variable_list() */
+
+#define RETURN_FUNCTION_NAME	0x01
+#define RETURN_FUNCTION_FLAGS	0x02
+#define RETURN_FUNCTION_TYPE	0x04
+
+
+/* Additional return value flag types for functionlist() */
+
+#define RETURN_FUNCTION_NUMARG	0x08
+#define RETURN_FUNCTION_ARGTYPE 0x10 /* not implemented */
+
+
+/* Additional return value flag types for variable_list() */
+
+#define RETURN_VARIABLE_VALUE	0x08
+
+
+/* Masks of the flag unions allowed for various efuns: */
+#define RETURN_FUNCTION_MASK    0x0f  /* functionlist() */
+#define RETURN_VARIABLE_MASK    0x0f  /* variable_list() */
+
+/* Return value flag types for function_exists() */
+
+#define FEXISTS_PROGNAME  (0)
+#define FEXISTS_FILENAME  (1)
+#define FEXISTS_LINENO    (2)
+#define FEXISTS_NUMARG    (3)
+#define FEXISTS_TYPE      (4)
+#define FEXISTS_FLAGS     (5)
+
+#define FEXISTS_ALL       (3)
+
+#endif /* LPC_FUNCTIONLIST_H */
diff --git a/sys/guard.h b/sys/guard.h
new file mode 100644
index 0000000..9cc477f
--- /dev/null
+++ b/sys/guard.h
@@ -0,0 +1,12 @@
+// MorgenGrauen MUDlib
+//
+// guard.h -- Definition fuer P_GUARD-Property von NPCs.
+//
+// $Id: guard.h 7701 2010-10-24 20:38:37Z Zesstra $
+
+#ifndef _GUARD_H_
+#define _GUARD_H_
+
+#define P_GUARD "guard"
+
+#endif
diff --git a/sys/header.h b/sys/header.h
new file mode 100644
index 0000000..ac7a107
--- /dev/null
+++ b/sys/header.h
@@ -0,0 +1,24 @@
+// MorgenGrauen MUDlib
+//
+// xxxx.h -- header
+//
+// $Id: header.h,v 3.1 1997/02/12 13:04:59 Wargon Exp $
+ 
+#ifndef __XXXX_H__
+#define __XXXX_H__
+
+// properties
+
+#endif // __XXXX_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __XXXX_H_PROTO__
+#define __XXXX_H_PROTO__
+
+// prototypes
+// no prototypes
+
+#endif // __XXXX_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/health.h b/sys/health.h
new file mode 100644
index 0000000..7457bcb
--- /dev/null
+++ b/sys/health.h
@@ -0,0 +1,50 @@
+// MorgenGrauen MUDlib
+//
+// health.h -- Heilstaerken und -geschwindigkeiten
+//
+// $Id: health.h 8368 2013-02-12 21:03:07Z Zesstra $
+
+#ifndef _HEALTH_
+#define _HEALTH_
+
+/*
+ * health.h
+ *
+ * A configuration file for all food and drink stuff
+ */
+
+/* These are tune macros to determine the actual effect of food, */
+/* drink and alcohol */
+#define ALOCHOL_VALUE(strength) strength
+#define DRINK_VALUE(strength) strength
+#define FOOD_VALUE(strength) strength
+
+/* How fast alcohol wears off and drink and food are reduced */
+#define ALCOHOL_DELAY 5 /* quite fast */
+#define DRINK_DELAY 7 /* quite medium */
+#define FOOD_DELAY 9 /* quite slow */
+#define HEAL_DELAY 4 /* quite fast */
+
+#define POISON_DELAY 4
+#define POISON_MERCY_DELAY 3
+
+#define ALC_EFFECT_HICK 0
+#define ALC_EFFECT_RUELPS 1
+#define ALC_EFFECT_LOOKDRUNK 2
+#define ALC_EFFECT_STUMBLE 3
+
+#define ALC_EFFECT_AREA_GUILD 0
+#define ALC_EFFECT_AREA_ENV 1
+
+/* Any regeneration prohibited ? */
+#define NO_REG_HP         0x01
+#define NO_REG_BUFFER_HP  0x02
+#define NO_REG_SP         0x04
+#define NO_REG_BUFFER_SP  0x08
+#define NO_REG_ALCOHOL    0x10
+#define NO_REG_DRINK      0x20
+#define NO_REG_FOOD       0x40
+#define NO_REG   (NO_REG_HP|NO_REG_BUFFER_HP|NO_REG_SP|NO_REG_BUFFER_SP| \
+                  NO_REG_ALCOHOL|NO_REG_DRINK|NO_REG_FOOD)
+                  
+#endif
diff --git a/sys/hook.h b/sys/hook.h
new file mode 100644
index 0000000..b1e655d
--- /dev/null
+++ b/sys/hook.h
@@ -0,0 +1,96 @@
+// MorgenGrauen MUDlib
+//
+// /sys/hooks.h  - Hooksystem
+//
+// $Id: hook.h 9475 2016-02-19 21:16:17Z Zesstra $
+
+#ifndef _HOOKS_H_
+#define _HOOKS_H_
+
+// global hooks
+#define H_HOOK_MOVE           1
+#define H_HOOK_DIE            2
+#define H_HOOK_DEFEND         3
+#define H_HOOK_ATTACK         4
+#define H_HOOK_HP             5
+#define H_HOOK_SP             6
+#define H_HOOK_ATTACK_MOD     7
+#define H_HOOK_ALCOHOL        8
+#define H_HOOK_FOOD           9
+#define H_HOOK_DRINK          10
+#define H_HOOK_POISON         11
+#define H_HOOK_CONSUME        12
+#define H_HOOK_TEAMROWCHANGE  13
+#define H_HOOK_INSERT         14
+#define H_HOOK_EXIT_USE       15
+
+// the possible consumer types
+#define H_HOOK_SURVEYOR       0
+#define H_HOOK_MODIFICATOR    1
+#define H_DATA_MODIFICATOR    2
+#define H_LISTENER            3
+#define H_CONSUMERCHECK(x)    ((intp(x)&& x>=0 && x<=H_LISTENER)?x:-1)
+#define H_CONSUMERNAMES       ({"surveyors","hmods","dmods","listeners"})
+
+// priorities
+#define H_HOOK_PRIORITYRANGE  3
+#define H_HOOK_LIBBASEPRIO    0
+#define H_HOOK_GUILDBASEPRIO  (H_HOOK_LIBBASEPRIO+H_HOOK_PRIORITYRANGE)
+#define H_HOOK_OTHERBASEPRIO  (H_HOOK_GUILDBASEPRIO+H_HOOK_PRIORITYRANGE)
+#define H_HOOK_PRIOCHECK(x)   ((intp(x) && x>=0 && x<H_HOOK_PRIORITYRANGE)?x:-1)
+#define H_HOOK_LIBPRIO(x)     ((H_HOOK_PRIOCHECK(x)!=-1)?(H_HOOK_LIBBASEPRIO+x):-1)
+#define H_HOOK_GUILDPRIO(x)   ((H_HOOK_PRIOCHECK(x)!=-1)?(H_HOOK_GUILDBASEPRIO+x):-1)
+#define H_HOOK_OTHERPRIO(x)   ((H_HOOK_PRIOCHECK(x)!=-1)?(H_HOOK_OTHERBASEPRIO+x):-1)
+#define H_HOOK_VALIDPRIO(x)   ((intp(x) && x>=0 && x<(H_HOOK_OTHERBASEPRIO+H_HOOK_PRIORITYRANGE))?x:-1)
+
+// maximum consumer per type
+#define MAX_SURVEYOR          1
+#define MAX_HOOK_MODIFICATOR  2
+#define MAX_DATA_MODIFICATOR  3
+#define MAX_LISTENER          5
+#define MAX_HOOK_COUNTS ({MAX_SURVEYOR, MAX_HOOK_MODIFICATOR, \
+                          MAX_DATA_MODIFICATOR, MAX_LISTENER, \
+                         })
+
+// data indices for return value of HookCallback() & Co.
+#define H_RETCODE             0
+#define H_RETDATA             1
+
+// return codes for HookFlow & Co.
+#define H_NO_MOD              0
+#define H_CANCELLED           1
+#define H_ALTERED             2
+
+// debugging
+#define H_DMSG(x)             (h_dbg() && (find_player("zesstra")) ? \
+    tell_object(find_player("zesstra"),x):0)
+
+#endif //_HOOKS_H_
+
+// prototypes 
+#ifdef NEED_PROTOTYPES
+// provider
+#ifndef __HOOK_PROVIDER_PROTO
+#define __HOOK_PROVIDER_PROTO
+
+// list of offered hooks
+int* HListHooks();
+
+// register to hook
+int HRegisterToHook(int hookid, mixed consumer, int hookprio,
+    int consumertype, int timeInSeconds);
+
+// unregister from hook
+int HUnregisterFromHook(int hookid, mixed consumer);
+
+// check wether object is a consumer for a given hook
+int HIsHookConsumer(int hookid, mixed consumer);
+
+// offer a hook or stop offering it
+protected void offerHook(int hookid, int offerstate);
+
+// trigger a hook
+protected mixed HookFlow(int hookid, mixed hookdata);
+
+#endif // __HOOK_PROVIDER_PROTO
+#endif // NEED_PROTOTYPES
diff --git a/sys/idn.h b/sys/idn.h
new file mode 100644
index 0000000..d8823b5
--- /dev/null
+++ b/sys/idn.h
@@ -0,0 +1,26 @@
+#ifndef LPC_IDN_H_
+#define LPC_IDN_H_ 1
+
+/* --- IDNA Constants ---
+ */
+
+/* idna_stringprep() profiles. */
+
+#define STRINGPREP_NAMEPREP 1
+#define STRINGPREP_SASLPREP 2
+#define STRINGPREP_PLAIN 3
+#define STRINGPREP_TRACE 4
+#define STRINGPREP_KERBEROS5 5
+#define STRINGPREP_XMPP_NODEPREP 6
+#define STRINGPREP_XMPP_RESOURCEPREP 7 
+#define STRINGPREP_ISCSI 8
+
+/* idna_stringprep() flags */
+
+#define STRINGPREP_NO_NFKC_FLAG        (1<<0)
+#define STRINGPREP_NO_BIDI_FLAG        (1<<1)
+#define STRINGPREP_NO_UNASSIGNED_FLAG  (1<<2)
+
+#define STRINGPREP_FLAG_MAX  (1<<2)
+
+#endif
diff --git a/sys/include_list.h b/sys/include_list.h
new file mode 100644
index 0000000..ad86f24
--- /dev/null
+++ b/sys/include_list.h
@@ -0,0 +1,9 @@
+#ifndef LPC_INCLUDE_LIST_H
+#define LPC_INCLUDE_LIST_H
+
+/* Return value flag types for include_list() */
+
+#define INCLIST_FLAT         0x00
+#define INCLIST_TREE         0x01
+
+#endif /* LPC_INCLUDE_LIST_H */
diff --git a/sys/inherit_list.h b/sys/inherit_list.h
new file mode 100644
index 0000000..626b1e9
--- /dev/null
+++ b/sys/inherit_list.h
@@ -0,0 +1,10 @@
+#ifndef LPC_INHERIT_LIST_H
+#define LPC_INHERIT_LIST_H
+
+/* Return value flag types for inherit_list() */
+
+#define INHLIST_FLAT         0x00
+#define INHLIST_TREE         0x01
+#define INHLIST_TAG_VIRTUAL  0x02
+
+#endif /* LPC_INHERIT_LIST_H */
diff --git a/sys/inpc.h b/sys/inpc.h
new file mode 100644
index 0000000..0ffced9
--- /dev/null
+++ b/sys/inpc.h
@@ -0,0 +1,15 @@
+// MorgenGrauen MUDlib
+//
+// inpc.h -- Headerfile fuer intelligente NPCs
+//
+// $Id: inpc.h,v 3.4 2003/08/06 19:06:11 Vanion Exp $
+
+#ifndef __INPC_H__
+#define __INPC_H__
+
+#include "/sys/inpc/select.h"
+#include "/sys/inpc/eval.h"
+#include "/sys/inpc/walking.h"
+#include "/sys/inpc/boozing.h"
+
+#endif
diff --git a/sys/inpc/boozing.h b/sys/inpc/boozing.h
new file mode 100644
index 0000000..94909eb
--- /dev/null
+++ b/sys/inpc/boozing.h
@@ -0,0 +1,22 @@
+// MorgenGrauen MUDlib
+//
+// inpc/boozing.h -- Intelligent Saufen
+//
+// $Id: boozing.h,v 1.2 1997/02/12 13:17:14 Wargon Exp $
+
+#ifndef __INPC_BOOZING_H__
+#define __INPC_BOOZING_H__
+
+#define P_I_HATE_ALCOHOL "i_hate_alcohol"
+
+#endif
+
+#ifdef NEED_PROTOTYPES
+#ifndef __INPC_BOOZING_PROTO__
+#define __INPC_BOOZING_PROTO__
+
+int Drink();
+void DrinkLoop();
+
+#endif
+#endif
diff --git a/sys/inpc/eval.h b/sys/inpc/eval.h
new file mode 100644
index 0000000..0a10a42
--- /dev/null
+++ b/sys/inpc/eval.h
@@ -0,0 +1,13 @@
+// MorgenGrauen MUDlib
+//
+// inpc/eval.h -- Definitionen zum Einschaetzen
+//
+// $Id: eval.h,v 3.1 1997/02/12 13:17:14 Wargon Exp $
+
+#ifndef __INPC_EVAL_H__
+#define __INPC_EVAL_H__
+
+#define P_EVAL_OFFSETS "inpc_eval_offsets"
+#define P_EVAL_FACTORS "inpc_eval_factors"
+
+#endif
diff --git a/sys/inpc/select.h b/sys/inpc/select.h
new file mode 100644
index 0000000..ef2dcf2
--- /dev/null
+++ b/sys/inpc/select.h
@@ -0,0 +1,40 @@
+// MorgenGrauen MUDlib
+//
+// inpc/select.h -- Definitionen zur Objektauswahl
+//
+// $Id: select.h,v 3.3 2003/08/06 18:40:05 Vanion Exp $
+
+#ifndef __INPC_SELECT_H__
+#define __INPC_SELECT_H__
+
+#define OT_WEAPON "weapon"
+#define OT_COMBAT_OBJECT "combat_object"
+#define OT_MISC "misc_objects"
+#define INPC_BEST_WEAPON_ID "inpc_best_weapon"
+#define INPC_BEST_SHIELD_ID "inpc_best_shield"
+
+// if npc does not like weapons, i.e. Karateorc
+#define INPC_DONT_WIELD_WEAPONS "inpc_dont_wield_weapons"
+
+#endif
+
+#ifdef NEED_PROTOTYPES
+#ifndef __INPC_SELECT_PROTO__
+#define __INPC_SELECT_PROTO__
+
+int eval_wepon(object ob);
+int eval_armour(object ob);
+int eval_combat_object(object ob, mapping vals, object enemy);
+
+object find_best_weapon(mixed from);
+object find_best_armour(mixed from, mixed typ);
+object *find_best_armours(mixed from);
+varargs string find_best_combat_command(mixed from, object enemy, mapping pref);
+
+varargs int wield_best_weapon(mixed from);
+varargs int wear_best_armours(mixed from);
+varargs int use_best_combat_command(mixed enemy, mixed from, mapping pref);
+
+
+#endif
+#endif
diff --git a/sys/inpc/walking.h b/sys/inpc/walking.h
new file mode 100644
index 0000000..6e143cf
--- /dev/null
+++ b/sys/inpc/walking.h
@@ -0,0 +1,43 @@
+// MorgenGrauen MUDlib
+//
+// walking.h -- Definitionen und Properties fuer laufende INPCs
+//
+// $Id: walking.h,v 3.1 1997/02/12 13:17:14 Wargon Exp $
+
+#ifndef __INPC_WALKING_H__
+#define __INPC_WALKING_H__
+
+// Properties and types
+#define P_INPC_LAST_PLAYER_CONTACT "inpc_last_player_contact"
+#define P_INPC_LAST_ENVIRONMENT "inpc_last_environment"
+#define P_INPC_WALK_MODE "inpc_walk_mode"
+#define P_INPC_WALK_DELAYS "inpc_walk_delay"
+#define P_INPC_WALK_FLAGS "inpc_walk_flags"
+#define P_INPC_WALK_AREA "inpc_walk_area"
+#define P_INPC_WALK_ROUTE "inpc_walk_route"
+#define P_INPC_HOME "inpc_home"
+
+#define WF_MAY_LOAD 0x01
+#define WF_MAY_FOLLOW 0x02
+#define WF_MAY_USE_SPECIAL 0x04
+#define WF_MAY_WALK_BACK 0x08
+
+#define WM_STOP 0
+#define WM_RANDOM 1
+#define WM_ROUTE 2
+#define WM_WALKTO 3
+#define WM_FOLLOW 4
+#define WM_FLEE 5
+
+#endif // __INPC_WALKING_H__
+
+#ifdef NEED_PROTOTYPES
+#ifndef __INPC_WALKING_H_PROTOTYPES__
+#define __INPC_WALKING_H_PROTOTYPES__
+
+// Prototypen
+int may_enter_room(mixed room);
+
+#endif // __INPC_WALKING_H_PROTOTYPES__
+#endif // NEED_PROTOTYPES
+
diff --git a/sys/input_to.h b/sys/input_to.h
new file mode 100644
index 0000000..f7f094f
--- /dev/null
+++ b/sys/input_to.h
@@ -0,0 +1,17 @@
+#ifndef LPC_INPUT_TO_H_
+#define LPC_INPUT_TO_H_
+
+/* Mode values recognized by the efun input_to() */
+
+#define INPUT_NOECHO         1  /* Don't echo the next line typed */
+#define INPUT_CHARMODE       2  /* Switch into/out of charmode */
+#define INPUT_PROMPT         4  /* Use a custom prompt */
+#define INPUT_NO_TELNET      8  /* Switch into/out of charmode on the driver
+                                 * side only.
+                                 */
+#define INPUT_APPEND        16  /* Append the input_to to the list of already
+                                 * pending input_to's.
+                                 */
+#define INPUT_IGNORE_BANG  128  /* Disallow the '!' escape */
+
+#endif /* LPC_INPUT_TO_H_ */
diff --git a/sys/interactive_info.h b/sys/interactive_info.h
new file mode 100644
index 0000000..920ac92
--- /dev/null
+++ b/sys/interactive_info.h
@@ -0,0 +1,29 @@
+#ifndef LPC_INTERACTIVE_INFO_H_
+#define LPC_INTERACTIVE_INFO_H_
+
+/* Definition of argument values for interactive_info()
+ */
+
+#include "configuration.h"
+
+/* Connection information */
+#define II_IP_NAME                      -1
+#define II_IP_NUMBER                    -2
+#define II_IP_PORT                      -3
+#define II_IP_ADDRESS                   -4
+#define II_MUD_PORT                     -5
+
+/* Telnet related information */
+#define II_MCCP_STATS                   -10
+
+/* Input handling */
+#define II_INPUT_PENDING                -20
+#define II_EDITING                      -21
+#define II_IDLE                         -22
+
+/* Output handling */
+#define II_SNOOP_NEXT                   -30
+#define II_SNOOP_PREV                   -31
+#define II_SNOOP_ALL                    -32
+
+#endif /* LPC_INTERACTIVE_INFO_H_ */
diff --git a/sys/items/fishing/aquarium.h b/sys/items/fishing/aquarium.h
new file mode 100644
index 0000000..0ac700d
--- /dev/null
+++ b/sys/items/fishing/aquarium.h
@@ -0,0 +1,15 @@
+#ifndef _AQUARIUM_
+#define _AQUARIUM_
+
+#define STDFISHLIST ([ \
+  W_BEACH:  ({"scholle","flunder","rochen","szunge","khai"}),\
+  W_HARBOR: ({"rochen","dorsch","szunge","hering","khai"}),\
+  W_OCEAN:  ({"thun","hai","kjau","schwert","seehase","seeteufel", \
+              "seewolf"}),\
+  W_RIVER:  ({"piranha","lachs","forelle","bsaib"}), \
+  W_POOL:   ({"stichling","gfisch","schlei","karpfen","gorfe"}), \
+  W_LAKE:   ({"ssaib","barsch","hecht","karpfen"}), \
+  W_ROCK:   ({"lachs","forelle","bsaib"}), \
+  W_STREAM: ({"stichling","bforelle","neun","bsaib"}) ]) \
+
+#endif
diff --git a/sys/items/fishing/fishing.h b/sys/items/fishing/fishing.h
new file mode 100644
index 0000000..38970fc
--- /dev/null
+++ b/sys/items/fishing/fishing.h
@@ -0,0 +1,72 @@
+// MorgenGrauen MUDlib
+//
+// fishing.h -- Definitionen fuer alles, was sich ums Angeln dreht
+//              Fragen, Vorschlaege etc. an Arathorn
+//
+// $Id: fishing.h,v 3.2 2002/12/17 10:01:43 Rikus Exp $
+
+#ifndef __FISHING_H__
+#define __FISHING_H__
+
+// Standardobjekte/-pfade
+#define ANGELBASE(x) ("/std/items/fishing/"+x)
+#define STD_ANGEL    ANGELBASE("angel")
+#define STD_FISCH    ANGELBASE("fish")
+#define STD_HAKEN    ANGELBASE("haken")
+#define STD_KOEDER   ANGELBASE("koeder")
+
+#define ANGELOBJ(x)  ("/items/fishing/"+x)
+
+// ******************
+// * Gewaessertypen *
+// ******************
+#define P_WATER  "water"
+
+// Gewaessertypen
+// Salzwasser
+#define W_BEACH     1    // Strand
+#define W_HARBOR    2    // Hafen
+#define W_OCEAN     4    // Ozean
+// Suesswasser
+#define W_RIVER     8    // Fluss
+#define W_POOL     16    // Teich
+#define W_LAKE     32    // See
+#define W_ROCK     64    // Bergbach
+#define W_STREAM   256   // Bach
+// Sonstige
+#define W_USER     128   // hier kann man ein eigenes Aquarium anhaengen!
+#define W_DEAD     512   // Lebloses Wasser/enthaelt keine Fische/man kann
+                         // aber die Stdflasche fuellen.
+#define W_OTHER   1024   // Flasche enthaelt Fluessigkeit!=Wasser
+
+// vordefinierte Kombinationen fuer Angeln:
+#define W_SHORT (W_HARBOR|W_RIVER|W_POOL|W_LAKE|W_ROCK|W_USER|W_OCEAN|W_STREAM)
+#define W_LONG  (W_BEACH|W_USER)
+#define W_SALT  (W_HARBOR|W_OCEAN|W_BEACH)               // Salzwasser
+#define W_SWEET (W_RIVER|W_POOL|W_LAKE|W_ROCK|W_STREAM)  // Suesswasser
+#define W_UNIVERSAL (W_SHORT|W_LONG)
+
+// **********
+// * Fische *
+// **********
+#define P_FISH     "fish"
+#define P_FISH_AGE "fish_age"
+
+// Flags fuer Fische
+#define F_NOROTTEN  1   // Fisch fault nicht.
+#define F_NOTHUNGRY 2   // isst Koeder nicht auf, falls zu schwer fuer Angel
+#define F_REPLACE   4   // Soll sich beim Entfernen von Angel verwandeln.
+#define F_NOHEAL    8   // Fisch heilt nicht bei Verzehr
+
+// Pfad zum "Aquarium", dort finden sich die Standardfische. Zuordnung
+// zu den Gewaessertypen siehe /sys/items/fishing/aquarium.h
+#define FISH(x) ANGELOBJ("aquarium/"+x)
+
+// IDs fuer Angel,Haken,Wurm,Fisch
+#define ANGEL_ID "\nf_angel"
+#define HAKEN_ID "\nf_koeder"
+#define WURM_ID  "\nf_worm"
+#define FISCH_ID "\nf_fish"
+
+#endif // __FISHING_H__
+
diff --git a/sys/items/flasche.h b/sys/items/flasche.h
new file mode 100644
index 0000000..de901ab
--- /dev/null
+++ b/sys/items/flasche.h
@@ -0,0 +1,23 @@
+// MorgenGrauen MUDlib
+//
+// flasche.h -- Definitionen fuer eine Standardflasche
+//
+// $Id: fishing.h,v 3.2 2002/12/17 10:01:43 Rikus Exp $
+
+#ifndef __FLASCHE_H__
+#define __FLASCHE_H__
+
+// *************
+// * Behaelter *
+// *************
+// Zur Erklaerung siehe am besten /d/ebene/fraggle/txt/flasche.txt
+// Fassungsvermoegen des Behaelters in ml
+#define P_LIQUID     "w_max_wasserfuellmenge"
+
+// Zusatz zu P_LONG fuer leeren Zustand des Stdhakens
+#define P_LONG_EMPTY "w_longdesc_empty"
+
+// Zusatz zu P_LONG fuer gefuellten Zustand der Stdflasche
+#define P_LONG_FULL  "w_longdesc_full"
+
+#endif // __FLASCHE_H__
diff --git a/sys/items/kraeuter/kraeuter.h b/sys/items/kraeuter/kraeuter.h
new file mode 100644
index 0000000..596606f
--- /dev/null
+++ b/sys/items/kraeuter/kraeuter.h
@@ -0,0 +1,40 @@
+// MorgenGrauen MUDlib
+//
+// kraeuter.h -- Definition fuer Kraeuter 
+//
+// $Id: fishing.h,v 3.2 2002/12/17 10:01:43 Rikus Exp $
+
+#ifndef __KRAEUTER_H__
+#define __KRAEUTER_H__
+
+#define KRAEUTERLISTE      "/sys/items/kraeuter/kraeuterliste.h"
+
+#define PLANTDIR           "/items/kraeuter/"
+
+#define SECURE(x) ("/secure/"+x)
+#define KRAEUTERVCSAVEFILE SECURE("ARCH/kraeuterVC")
+#define PLANTMASTER        (SECURE("krautmaster"))
+#define KRAEUTERVC         PLANTDIR"virtual_compiler"
+
+#define STDPLANT           "/std/items/kraeuter/kraut"
+#define PLANTITEM          PLANTDIR"kraut"
+#define STDTRANK           "/std/items/kraeuter/trank"
+#define TRANKITEM          PLANTDIR"trank"
+
+#define KESSELID           "_lib\nkraeuterkessel"
+
+#define INGREDIENT_ID           0
+#define INGREDIENT_DEMON        1
+#define INGREDIENT_GENDER       2
+#define INGREDIENT_NAME         3
+#define INGREDIENT_ADJ          4
+#define INGREDIENT_LONG         5
+#define INGREDIENT_ROOMDETAIL   6
+
+#define PLANT_ROOMDETAIL   "_lib_p_plantroomdetail"
+
+#define IS_PLANT(x) (member(inherit_list(x), \
+                     "/std/items/kraeuter/kraut.c")>=0)
+
+#endif // __KRAEUTER_H__
+
diff --git a/sys/items/kraeuter/kraeuterliste.h b/sys/items/kraeuter/kraeuterliste.h
new file mode 100644
index 0000000..159d53f
--- /dev/null
+++ b/sys/items/kraeuter/kraeuterliste.h
@@ -0,0 +1,256 @@
+// Automatisch generiertes File, nicht von Hand editieren!
+// Erzeugendes File: /secure/krautmaster
+
+#define PLANTCOUNT 249
+
+#define PLANT(x) "/items/kraeuter/"+x
+
+#define ZYPRESSENWOLFSMILCH            PLANT("zypressenwolfsmilch")
+#define ZWOELFFINGERKRAUT              PLANT("zwoelffingerkraut")
+#define ZWIRBELWURZ                    PLANT("zwirbelwurz")
+#define ZWERGWURZ                      PLANT("zwergwurz")
+#define ZWERGENHUT                     PLANT("zwergenhut")
+#define ZWACKELKRAUT                   PLANT("zwackelkraut")
+#define ZOOKZWIEBEL                    PLANT("zookzwiebel")
+#define ZICKZACK_KLEE                  PLANT("zickzack_klee")
+#define ZESSTRAZWIEBEL                 PLANT("zesstrazwiebel")
+#define ZAPPERLILIE                    PLANT("zapperlilie")
+#define WURZELSILIE                    PLANT("wurzelsilie")
+#define WURMKRAUT                      PLANT("wurmkraut")
+#define WUNDKLEE                       PLANT("wundklee")
+#define WUESTENROSE                    PLANT("wuestenrose")
+#define WUESTENENZIAN                  PLANT("wuestenenzian")
+#define WUERGERKRAUT                   PLANT("wuergerkraut")
+#define WOLPITROESTER                  PLANT("wolpitroester")
+#define WINDWURZ                       PLANT("windwurz")
+#define WINDNESSEL                     PLANT("windnessel")
+#define WILHELMSBURGER_SIEBENSTERN     PLANT("wilhelmsburger_siebenstern")
+#define WIESENSCHAUMKRAUT              PLANT("wiesenschaumkraut")
+#define WIESENRISPE                    PLANT("wiesenrispe")
+#define WIESENRETTICH                  PLANT("wiesenrettich")
+#define WIESENMELDE                    PLANT("wiesenmelde")
+#define WIESENLICHTNELKE               PLANT("wiesenlichtnelke")
+#define WIESENKUCKUCKSBLUME            PLANT("wiesenkuckucksblume")
+#define WIESENKERBEL                   PLANT("wiesenkerbel")
+#define WIESENGLOCKENBLUME             PLANT("wiesenglockenblume")
+#define WIESENBUTTERBLUME              PLANT("wiesenbutterblume")
+#define WERWOLFSMILCH_GRUEN            PLANT("werwolfsmilch_gruen")
+#define WEISSWURZ_VIELBLUETIG          PLANT("weisswurz_vielbluetig")
+#define WEISSWURZ_QUIRLBLAETTRIG       PLANT("weisswurz_quirlblaettrig")
+#define WEISSWURZ_GEMEIN               PLANT("weisswurz_gemein")
+#define WEISSKLEE                      PLANT("weissklee")
+#define WEIDENKLEE                     PLANT("weidenklee")
+#define WASSERWURZ                     PLANT("wasserwurz")
+#define WASSERRETTICH                  PLANT("wasserrettich")
+#define WASSERKLEE                     PLANT("wasserklee")
+#define WASSER_HAHNENFUSS_WEISS        PLANT("wasser_hahnenfuss_weiss")
+#define WARZENWOLFSMILCH               PLANT("warzenwolfsmilch")
+#define WALDSCHATTENRAUTENWICKE        PLANT("waldschattenrautenwicke")
+#define WALDRETTICH                    PLANT("waldrettich")
+#define WALDREBE_WEISS                 PLANT("waldrebe_weiss")
+#define WALDREBE_SCHWARZ               PLANT("waldrebe_schwarz")
+#define WALDREBE_ROT                   PLANT("waldrebe_rot")
+#define WALDREBE_GEMEIN                PLANT("waldrebe_gemein")
+#define WALDMOOS_LANGLEBIG             PLANT("waldmoos_langlebig")
+#define WALDMOOS                       PLANT("waldmoos")
+#define WALDLICHTNELKE                 PLANT("waldlichtnelke")
+#define WALDENZIAN                     PLANT("waldenzian")
+#define VOGELHANF                      PLANT("vogelhanf")
+#define VAINERPILZ                     PLANT("vainerpilz")
+#define TYPONIA                        PLANT("typonia")
+#define TROPENENZIAN                   PLANT("tropenenzian")
+#define TROLLTROESTER                  PLANT("trolltroester")
+#define TROLLSCHMAROTZER               PLANT("trollschmarotzer")
+#define TRAEUMERSCHLINGE               PLANT("traeumerschlinge")
+#define TORFMOORRAUTENWICKE_GLATT      PLANT("torfmoorrautenwicke_glatt")
+#define TORFMOORRAUTENWICKE_GEZAHNT    PLANT("torfmoorrautenwicke_gezahnt")
+#define TORFMOORRAUTENWICKE_GEFRANST   PLANT("torfmoorrautenwicke_gefranst")
+#define TEUFELSKRALLE_SCHWARZ          PLANT("teufelskralle_schwarz")
+#define TEUFELSKRALLE_ROT              PLANT("teufelskralle_rot")
+#define TEUFELSKRALLE_RISPIG           PLANT("teufelskralle_rispig")
+#define TEUFELSKRALLE_KOPFIG           PLANT("teufelskralle_kopfig")
+#define TEUFELSKRALLE_DREIKANTIG       PLANT("teufelskralle_dreikantig")
+#define TEUFELSKRALLE_BUSCHIG          PLANT("teufelskralle_buschig")
+#define TEUFELSKRALLE_AEHRIG           PLANT("teufelskralle_aehrig")
+#define TEUFELSABBISS_GEMEIN           PLANT("teufelsabbiss_gemein")
+#define SUMPFRETTICH                   PLANT("sumpfrettich")
+#define SUMPFREBE_SCHWARZ              PLANT("sumpfrebe_schwarz")
+#define SUMPFRAUTENWICKE               PLANT("sumpfrautenwicke")
+#define SUMPFLICHTNELKE                PLANT("sumpflichtnelke")
+#define SUESSKLEE                      PLANT("suessklee")
+#define STUPSKRAUT                     PLANT("stupskraut")
+#define STERNMIERE                     PLANT("sternmiere")
+#define STERNENFLECHTE                 PLANT("sternenflechte")
+#define STEINBEISSERKNOLLE             PLANT("steinbeisserknolle")
+#define SPITZWEGERICH                  PLANT("spitzwegerich")
+#define SONNENWENDWOLFSMILCH           PLANT("sonnenwendwolfsmilch")
+#define SONNENPILZ                     PLANT("sonnenpilz")
+#define SOMMERFINDLING                 PLANT("sommerfindling")
+#define SICHELNELKE                    PLANT("sichelnelke")
+#define SCHWARZWURZ_WILD               PLANT("schwarzwurz_wild")
+#define SCHWARZWURZ_KRIECHEND          PLANT("schwarzwurz_kriechend")
+#define SCHWARZWURZ_GEZACKT            PLANT("schwarzwurz_gezackt")
+#define SCHWARZWURZ_GEMEIN             PLANT("schwarzwurz_gemein")
+#define SCHWARZSCHATTENMOOS            PLANT("schwarzschattenmoos")
+#define SCHWALBENWURZ_ENZIAN           PLANT("schwalbenwurz_enzian")
+#define SCHUETTELKRAUT                 PLANT("schuettelkraut")
+#define SCHILFRAUTENWICKE              PLANT("schilfrautenwicke")
+#define SCHATTENKRAUT                  PLANT("schattenkraut")
+#define SCHANDKRAUT                    PLANT("schandkraut")
+#define SAUERKLEE                      PLANT("sauerklee")
+#define RIESENORKSCHORFLING_LILA_GEPUNKTET PLANT("riesenorkschorfling_lila_gepunktet")
+#define REGENWURZ                      PLANT("regenwurz")
+#define RAUTENWICKE_FEURIG             PLANT("rautenwicke_feurig")
+#define RAUCHKRESSE                    PLANT("rauchkresse")
+#define RATSHERRENKRESSE               PLANT("ratsherrenkresse")
+#define RASCHELKRAUT                   PLANT("raschelkraut")
+#define POTTERRAUTENWICKE_GLATT        PLANT("potterrautenwicke_glatt")
+#define POTTERRAUTENWICKE_GEFRANST     PLANT("potterrautenwicke_gefranst")
+#define POLARWOLFSMILCH                PLANT("polarwolfsmilch")
+#define POLARBIRKENGESELLE             PLANT("polarbirkengeselle")
+#define ORANGUTANHANF                  PLANT("orangutanhanf")
+#define NEBELRAUTENWICKE_GLATT         PLANT("nebelrautenwicke_glatt")
+#define NEBELRAUTENWICKE_GEZAHNT       PLANT("nebelrautenwicke_gezahnt")
+#define NEBELRAUTENWICKE_GEFRANST      PLANT("nebelrautenwicke_gefranst")
+#define NEBELMOOS_TRUEGERISCH          PLANT("nebelmoos_truegerisch")
+#define NEANDERTALER_KNOCHENMOOS       PLANT("neandertaler_knochenmoos")
+#define NACHTSCHATTEN_SCHWARZ          PLANT("nachtschatten_schwarz")
+#define MOULOKINER_SIEBENSTERN         PLANT("moulokiner_siebenstern")
+#define MORONER_SIEBENSTERN            PLANT("moroner_siebenstern")
+#define MOORRETTICH                    PLANT("moorrettich")
+#define MIRILSZWIEBEL                  PLANT("mirilszwiebel")
+#define MEERRETTICH                    PLANT("meerrettich")
+#define MALTEKRESSE                    PLANT("maltekresse")
+#define MAEUSEHANF                     PLANT("maeusehanf")
+#define LUNGEN_ENZIAN_DUNKELBLAU       PLANT("lungen_enzian_dunkelblau")
+#define LOEWENHANF                     PLANT("loewenhanf")
+#define LICHTNELKE_ROT                 PLANT("lichtnelke_rot")
+#define LEIMKRAUT_WEISS                PLANT("leimkraut_weiss")
+#define LEIMKRAUT_ROT                  PLANT("leimkraut_rot")
+#define LEIMKRAUT_NICKEND              PLANT("leimkraut_nickend")
+#define LEIMKRAUT_KLEBRIG              PLANT("leimkraut_klebrig")
+#define LEIMKRAUT_AUFGEBLASEN          PLANT("leimkraut_aufgeblasen")
+#define LEBERMOOS                      PLANT("lebermoos")
+#define LAUCHKRESSE                    PLANT("lauchkresse")
+#define LAUBMOOS                       PLANT("laubmoos")
+#define LANGFINGERKRAUT_AUFRECHT       PLANT("langfingerkraut_aufrecht")
+#define KUCKUCKSLICHTNELKE             PLANT("kuckuckslichtnelke")
+#define KRONENZWIEBEL                  PLANT("kronenzwiebel")
+#define KRIESELZWIEBEL                 PLANT("krieselzwiebel")
+#define KOBOLDSWURZ_GETUEPFELT         PLANT("koboldswurz_getuepfelt")
+#define KNURRPILZ                      PLANT("knurrpilz")
+#define KLEINBUGERICH                  PLANT("kleinbugerich")
+#define KLEEFARN                       PLANT("kleefarn")
+#define KLEE_WEISS                     PLANT("klee_weiss")
+#define KLEE_TROPFIG                   PLANT("klee_tropfig")
+#define KLEE_ROT                       PLANT("klee_rot")
+#define KLEE_PERSISCH                  PLANT("klee_persisch")
+#define KLEE_KRIECHEND                 PLANT("klee_kriechend")
+#define KLEE_KOPFIG                    PLANT("klee_kopfig")
+#define KLEE_DOLDIG                    PLANT("klee_doldig")
+#define KICHERPILZ                     PLANT("kicherpilz")
+#define KICHERKRESSE                   PLANT("kicherkresse")
+#define KATZENWURZ                     PLANT("katzenwurz")
+#define KATZENHANF                     PLANT("katzenhanf")
+#define JOFSWURZ                       PLANT("jofswurz")
+#define INKARNATKLEE                   PLANT("inkarnatklee")
+#define HYDRAZAHN                      PLANT("hydrazahn")
+#define HYDRABALSAMKRAUT_GROSS         PLANT("hydrabalsamkraut_gross")
+#define HUNGERKRAUT                    PLANT("hungerkraut")
+#define HUNDEHANF                      PLANT("hundehanf")
+#define HUMNIZWIEBEL                   PLANT("humnizwiebel")
+#define HUEPFERLISL                    PLANT("huepferlisl")
+#define HONIGKLEE                      PLANT("honigklee")
+#define HEXENTRAUM_QUIETSCHGRUEN       PLANT("hexentraum_quietschgruen")
+#define HEXENKRAUT_WILD                PLANT("hexenkraut_wild")
+#define HEXENKRAUT_VERBRANNT           PLANT("hexenkraut_verbrannt")
+#define HEXENKRAUT_KREUZBLAETTRIG      PLANT("hexenkraut_kreuzblaettrig")
+#define HEXENKRAUT_KLEIN               PLANT("hexenkraut_klein")
+#define HEXENKRAUT_GROSS               PLANT("hexenkraut_gross")
+#define HEXENKRAUT_GEMEIN              PLANT("hexenkraut_gemein")
+#define HEXENKRAUT_BRAUN               PLANT("hexenkraut_braun")
+#define HEXENKRAUT_13_FINGRIG          PLANT("hexenkraut_13_fingrig")
+#define HASENKLEE                      PLANT("hasenklee")
+#define HANGRAUTENWICKE_WEISS_GEPUNKTET PLANT("hangrautenwicke_weiss_gepunktet")
+#define HANGRAUTENWICKE                PLANT("hangrautenwicke")
+#define HAMMERKRAUT                    PLANT("hammerkraut")
+#define HALTEKRESSE                    PLANT("haltekresse")
+#define HAHNENFUSS_WOLLIG              PLANT("hahnenfuss_wollig")
+#define HAHNENFUSS_SPREIZEND           PLANT("hahnenfuss_spreizend")
+#define HAHNENFUSS_SCHARF              PLANT("hahnenfuss_scharf")
+#define HAHNENFUSS_KRIECHEND           PLANT("hahnenfuss_kriechend")
+#define HAHNENFUSS_KNOLLIG             PLANT("hahnenfuss_knollig")
+#define HAHNENFUSS_GELB                PLANT("hahnenfuss_gelb")
+#define HAHNENFUSS_GACKERND            PLANT("hahnenfuss_gackernd")
+#define HAHNENFUSS_FLUTEND             PLANT("hahnenfuss_flutend")
+#define HAHNENFUSS_EISENHUTBLAETTRIG   PLANT("hahnenfuss_eisenhutblaettrig")
+#define HAHNENFUSS_EFEUBLAETTRIG       PLANT("hahnenfuss_efeublaettrig")
+#define HAHNENFUSS_BRENNEND            PLANT("hahnenfuss_brennend")
+#define GOLDHAHNENFUSS                 PLANT("goldhahnenfuss")
+#define GLOCKENBLUME                   PLANT("glockenblume")
+#define GLETSCHER_HAHNENFUSS_ROT       PLANT("gletscher_hahnenfuss_rot")
+#define GIPFELRAUTENWICKE_WILD         PLANT("gipfelrautenwicke_wild")
+#define GESELLENKRESSE                 PLANT("gesellenkresse")
+#define GEBIRGSRETTICH                 PLANT("gebirgsrettich")
+#define GEBIRGSENZIAN                  PLANT("gebirgsenzian")
+#define GAENSEFINGERKRAUT              PLANT("gaensefingerkraut")
+#define GAENSEBLUEMCHEN                PLANT("gaensebluemchen")
+#define FUTTERKRESSE                   PLANT("futterkresse")
+#define FRUEHLINGSFINGERKRAUT          PLANT("fruehlingsfingerkraut")
+#define FRUEHLINGS_ENZIAN_POLSTERWUECHSIG PLANT("fruehlings_enzian_polsterwuechsig")
+#define FRUEHLINGS_ENZIAN_HIMMELBLAU   PLANT("fruehlings_enzian_himmelblau")
+#define FRUEHLINGS_ENZIAN_FUENFZIPFLIG PLANT("fruehlings_enzian_fuenfzipflig")
+#define FRUEHLINGS_ENZIAN              PLANT("fruehlings_enzian")
+#define FROSTREBE_ROT                  PLANT("frostrebe_rot")
+#define FROSTENZIAN                    PLANT("frostenzian")
+#define FRANSENENZIAN                  PLANT("fransenenzian")
+#define FINSTERMOOS                    PLANT("finstermoos")
+#define FINGERKRAUT_KRIECHEND          PLANT("fingerkraut_kriechend")
+#define FINGERKRAUT_GROSSBLUETIG       PLANT("fingerkraut_grossbluetig")
+#define FINGERKRAUT_AUFRECHT           PLANT("fingerkraut_aufrecht")
+#define FEUERTULPE                     PLANT("feuertulpe")
+#define FEUERENZIAN                    PLANT("feuerenzian")
+#define FELDKLEE                       PLANT("feldklee")
+#define FELD_ENZIAN_BLAUVIOLETT        PLANT("feld_enzian_blauviolett")
+#define ERDBEERFINGERKRAUT             PLANT("erdbeerfingerkraut")
+#define ENZIAN_STENGELLOS              PLANT("enzian_stengellos")
+#define ENZIAN_GELB                    PLANT("enzian_gelb")
+#define ELFENOHRWURZ                   PLANT("elfenohrwurz")
+#define ELEFANTENHANF                  PLANT("elefantenhanf")
+#define EISZWIEBEL                     PLANT("eiszwiebel")
+#define EISZAHN                        PLANT("eiszahn")
+#define EISWURZ                        PLANT("eiswurz")
+#define EISSTERN                       PLANT("eisstern")
+#define EISNESSEL                      PLANT("eisnessel")
+#define EISKRAUT                       PLANT("eiskraut")
+#define EISENZWIEBEL                   PLANT("eisenzwiebel")
+#define EISDORN                        PLANT("eisdorn")
+#define EISDISTEL                      PLANT("eisdistel")
+#define DUNKELPILZ                     PLANT("dunkelpilz")
+#define DUNKELMOOS                     PLANT("dunkelmoos")
+#define DUESTERZWIEBEL_FINSTER         PLANT("duesterzwiebel_finster")
+#define DREIFINGERKRAUT_GROSSBLUETIG   PLANT("dreifingerkraut_grossbluetig")
+#define DREIFINGERKRAUT_GEMEIN         PLANT("dreifingerkraut_gemein")
+#define DRACHENZINNENDORN_GESCHECKT    PLANT("drachenzinnendorn_gescheckt")
+#define DRACHENHORTER_SIEBENSTERN      PLANT("drachenhorter_siebenstern")
+#define DRACHENHANF                    PLANT("drachenhanf")
+#define BUSCHREBE_NIEDER               PLANT("buschrebe_nieder")
+#define BUSCHREBE_GROSS                PLANT("buschrebe_gross")
+#define BRUNNENKRESSE                  PLANT("brunnenkresse")
+#define BREITWEGERICH                  PLANT("breitwegerich")
+#define BOCKSHORNKLEE                  PLANT("bockshornklee")
+#define BLUTNELKE                      PLANT("blutnelke")
+#define BITTERKLEE                     PLANT("bitterklee")
+#define BERGRETTICH                    PLANT("bergrettich")
+#define BERGRAUTENWICKE_GEZAHNT        PLANT("bergrautenwicke_gezahnt")
+#define BERGRAUTENWICKE_GEMEIN         PLANT("bergrautenwicke_gemein")
+#define BERGMOOS                       PLANT("bergmoos")
+#define BERGKLEE                       PLANT("bergklee")
+#define BAERLAPPMOOS                   PLANT("baerlappmoos")
+#define BAERENKLAU                     PLANT("baerenklau")
+#define AUGENTROST_GEMEIN              PLANT("augentrost_gemein")
+#define ARKZWIEBEL                     PLANT("arkzwiebel")
+#define ARATHORNSILIKUM_WEIS           PLANT("arathornsilikum_weis")
+#define ACKERLICHTNELKE                PLANT("ackerlichtnelke")
+#define ACKERKLEE_GELB                 PLANT("ackerklee_gelb")
+#define ACKER_RETTICH                  PLANT("acker_rettich")
diff --git a/sys/items/kraeuter/trankattribute.h b/sys/items/kraeuter/trankattribute.h
new file mode 100644
index 0000000..484347c
--- /dev/null
+++ b/sys/items/kraeuter/trankattribute.h
@@ -0,0 +1,59 @@
+#ifndef __KRAEUTER_TRANKATTRIBUTE_H__
+#define __KRAEUTER_TRANKATTRIBUTE_H__
+
+#define T_CARRY                 "attr_tragen"
+#define T_DAMAGE_ANIMALS        "attr_dam_animals"
+#define T_DAMAGE_MAGIC          "attr_dam_magical"
+#define T_DAMAGE_UNDEAD         "attr_dam_undead"
+#define T_PROTECTION_ANIMALS    "attr_prot_animals"
+#define T_PROTECTION_MAGIC      "attr_prot_magical"
+#define T_PROTECTION_UNDEAD     "attr_prot_undead"
+#define T_FROG                  "attr_defrog"
+#define T_FLEE_TPORT            "attr_flee_tport"
+#define T_HEAL_LP               "attr_heal_lp"
+#define T_HEAL_SP               "attr_heal_sp"
+#define T_HEAL_POISON           "attr_heal_poison"
+#define T_HEAL_DISEASE          "attr_heal_disease"
+#define T_CHANGE_DIMENSION      "attr_change_dimension"
+#define T_SA_SPEED              "attr_change_sa_speed"
+#define T_SA_DURATION           "attr_change_sa_duration"
+#define T_SA_SPELL_PENETRATION  "attr_change_sa_spell_penetration"
+#define T_EFFECT_DURATION       "attr_effect_duration"
+#define T_SUPPORTER             "attr_supporter"
+#define T_BLOCKING              "attr_blocking"
+#define T_ABUNDANCE             "attr_abundance"
+#define T_EXPIRE                "best_before"
+
+#define T_KRAUT_EFFECTS ({ \
+    T_CARRY, T_DAMAGE_ANIMALS, T_DAMAGE_MAGIC, \
+    T_DAMAGE_UNDEAD, T_PROTECTION_ANIMALS, T_PROTECTION_MAGIC, \
+    T_PROTECTION_UNDEAD, T_FROG, T_FLEE_TPORT, T_HEAL_LP, T_HEAL_SP, \
+    T_HEAL_POISON, T_HEAL_DISEASE, T_CHANGE_DIMENSION, \
+    T_SA_SPEED, T_SA_DURATION, T_SA_SPELL_PENETRATION, })
+
+#define T_KRAUT_MAP \
+  (["car": T_CARRY, \
+    "da":  T_DAMAGE_ANIMALS, \
+    "dm":  T_DAMAGE_MAGIC, \
+    "du":  T_DAMAGE_UNDEAD, \
+    "dn":  T_EFFECT_DURATION, \
+    "flt": T_FLEE_TPORT, \
+    "fro": T_FROG, \
+    "hI":  T_HEAL_DISEASE, \
+    "hP":  T_HEAL_POISON, \
+    "hK":  T_HEAL_SP, \
+    "hL":  T_HEAL_LP, \
+    "pa":  T_PROTECTION_ANIMALS, \
+    "pm":  T_PROTECTION_MAGIC, \
+    "pu":  T_PROTECTION_UNDEAD, \
+    "ss":  T_SA_SPEED, \
+    "sp":  T_SA_SPELL_PENETRATION, \
+    "sd":  T_SA_DURATION ])
+
+// negative Effekte muesssen abs. groesser als dies sein, damit sie bei der Wirkung
+// beruecksichtigt werden.
+#define T_MINIMUM_THRESHOLD	150
+// Dies ist der max. Effekt. Alles, was hierueber gebt, wird gedeckelt.
+#define T_MAXIMUM_THRESHOLD	2000
+
+#endif // __KRAEUTER_TRANKATTRIBUTE_H__
diff --git a/sys/language.h b/sys/language.h
new file mode 100644
index 0000000..aad6a41
--- /dev/null
+++ b/sys/language.h
@@ -0,0 +1 @@
+#include <thing/language.h>
diff --git a/sys/lightsource.h b/sys/lightsource.h
new file mode 100644
index 0000000..0a0928b
--- /dev/null
+++ b/sys/lightsource.h
@@ -0,0 +1,15 @@
+// MorgenGrauen MUDlib
+//
+// lightsource.h -- lightsources header file
+//
+// $Id: lightsource.h,v 1.1 2000/02/09 15:52:48 Padreic Exp $
+ 
+#ifndef __LIGHTSOURCE_H__
+#define __LIGHTSOURCE_H__
+
+#define P_FUEL         "fuel"        // Brennzeit in Sekunden
+#define P_LIGHTDESC    "lightdesc"   // Beschreibung des Lichts
+#define P_DO_DESTRUCT  "do_destruct" // Soll sich die Lichtquelle aufloesen?
+#define P_LIGHTED      "lighted"     // Flag ob die Lichtquelle in Betrieb ist
+
+#endif // __LIGHTSOURCE_H__
diff --git a/sys/living.h b/sys/living.h
new file mode 100644
index 0000000..f1f5380
--- /dev/null
+++ b/sys/living.h
@@ -0,0 +1,31 @@
+// MorgenGrauen MUDlib
+//
+// living.h -- Header fuer Lebewesen
+//
+// $Id: living.h 6513 2007-10-06 15:55:27Z Zesstra $
+
+#include "/sys/living/life.h"
+#include "/sys/living/attributes.h"
+#include "/sys/living/put_and_get.h"
+#include "/sys/living/combat.h"
+#include "/sys/living/team.h"
+#include "/sys/living/skills.h"
+//#include "/sys/living/moving.h"
+
+#ifndef __LIVING_H__
+#define __LIVING_H__
+
+#define INTERVAL_BETWEEN_HEALING	10
+#define WEAPON_CLASS_OF_HANDS		(3)
+#define ARMOUR_CLASS_OF_BARE		0
+#define KILL_NEUTRAL_ALIGNMENT		10
+#define ADJ_ALIGNMENT(al)		((-al - KILL_NEUTRAL_ALIGNMENT)/4)
+#define MAX_LIST			20
+#define NAME_OF_GHOST			"some mist"
+
+/* Hands */
+#define HAND_SHORT 0
+#define HAND_WEAPON 1
+#define HAND_WC 2
+
+#endif // __LIVING_H__
diff --git a/sys/living/attributes.h b/sys/living/attributes.h
new file mode 100644
index 0000000..8f28299
--- /dev/null
+++ b/sys/living/attributes.h
@@ -0,0 +1,96 @@
+// MorgenGrauen MUDlib
+//
+// living/attributes.h -- living attributes
+//
+// $Id: attributes.h 6282 2007-05-09 20:55:17Z Zesstra $
+ 
+#ifndef __LIVING_ATTRIBUTES_H__
+#define __LIVING_ATTRIBUTES_H__
+
+// properties
+
+#define P_ATTRIBUTES              "attributes"
+#define P_ATTRIBUTES_OFFSETS      "attributes_offsets"
+#define P_ATTRIBUTES_MODIFIER     "attributes_modifier"
+#define P_X_ATTR_MOD              "extern_attributes_modifier"
+#define P_X_HEALTH_MOD            "extern_health_modifier"
+#define P_M_ATTR_MOD              "magic_attributes_modifier"
+#define P_M_HEALTH_MOD            "magic_health_modifier"
+#define P_ABILITIES               "abilities"
+#define P_TIMED_ATTR_MOD		  "timed_attr_mod"
+
+// special defines
+
+/* standard attributes */
+#define A_STR "str"
+#define A_INT "int"
+#define A_DEX "dex"
+#define A_CON "con"
+
+/* errorcodes for P_TIMED_ATTR_MOD */
+#define TATTR_OK				0
+#define TATTR_NO_SUCH_MODIFIER	1
+#define TATTR_INVALID_ARGS  	2
+
+/* indices used for P_TIMED_ATTR_MOD */
+#define TATTR_OUTDATE		0
+#define TATTR_DEPENDENTS	1
+#define TATTR_ENTRIES		2	
+#define TATTR_MOD 		0
+#define TATTR_OUTDATED 		1
+#define TATTR_DEPENDENT 	2
+#define TATTR_NOTIFY 		3
+
+/* maximum of cumulative attribute modifiers */
+#define CUMULATIVE_ATTR_LIMIT	4
+
+#endif // __LIVING_ATTRIBUTES_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __LIVING_ATTRIBUTES_H_PROTO__
+#define __LIVING_ATTRIBUTES_H_PROTO__
+
+// prototypes
+
+nomask public int SetTimedAttrModifier(string key, mapping modifier, int outdated, object dependent, mixed notify);  
+nomask public mapping QueryTimedAttrModifier(string key);
+nomask public int DeleteTimedAttrModifier(string key);
+
+public int SetAttr(string attr, int val);
+nomask public void register_modifier(object modifier);
+nomask public void deregister_modifier(object modifier);
+
+public int SetAttribute(string attr, int val);
+public int SetRealAttribute(string attr, int val);
+public int QueryAttribute(string attr);
+public int QueryRealAttribute(string attr);
+public int QueryAttributeOffset(string attr);
+
+public void UpdateAttributes();
+
+public status TestLimitViolation(mapping check);
+
+// Set- und Query-Methoden
+static mapping _set_attributes(mapping arr);
+static mapping _query_attributes();
+static mapping _set_attributes_offsets(mapping arr);
+static mapping _query_attributes_offsets();
+static mixed   _set_attributes_modifier(mixed arr);
+static mapping _query_attributes_modifier();
+static mixed _query_timed_attr_mod();
+
+// internal
+nomask protected void attribute_hb();
+protected void add_offsets(mapping arr);
+protected nomask void calculate_valid_modifiers();
+protected nomask void notifyInvalidModifiers();
+protected int _filterattr_str(int val);
+protected int _filterattr_dex(int val);
+protected int _filterattr_int(int val);
+protected int _filterattr_con(int val);
+nomask protected void notifyExpiredModifiers(mapping nots);
+
+#endif // __LIVING_ATTRIBUTES_H_PROTO__
+
+#endif // NEED_PROTOYPES
diff --git a/sys/living/clothing.h b/sys/living/clothing.h
new file mode 100644
index 0000000..d875c61
--- /dev/null
+++ b/sys/living/clothing.h
@@ -0,0 +1,12 @@
+// MorgenGrauen MUDlib
+//
+// living/clothing.h -- Konstanten und Pfade rund um Bekleidung von Lebewesen
+//
+// $Id: moneyhandler.h,v 3.1 1997/02/12 13:29:09 Wargon Exp %
+
+#ifndef __LIVING_CLOTHING_H__
+#define __LIVING_CLOTHING_H__
+
+#define P_CLOTHING                "std:clothing"
+
+#endif // __LIVING_CLOTHING_H__
diff --git a/sys/living/combat.h b/sys/living/combat.h
new file mode 100644
index 0000000..528f446
--- /dev/null
+++ b/sys/living/combat.h
@@ -0,0 +1,166 @@
+// MorgenGrauen MUDlib
+//
+// living/combat.h -- combat header
+//
+// $Id: combat.h 9008 2015-01-06 17:20:17Z Zesstra $
+
+#ifndef __LIVING_COMBAT_H__
+#define __LIVING_COMBAT_H__
+
+// properties
+#define P_DISABLE_ATTACK          "disable_attack"
+#define P_NEXT_DISABLE_ATTACK     "next_disable_attack"
+
+#define P_CORPSE                  "corpse"
+#define P_NOCORPSE                "nocorpse"
+
+// bitte P_ARMOURS auch in /secure/master.h aendern, falls das mal geaendert
+// wird.
+#define P_ARMOURS                 "armours"
+#define P_BODY                    "body"
+
+#define P_RESISTANCE_STRENGTHS    "resistance_strengths"
+#define P_RESISTANCE_MODIFIER     "rstr:mod"
+#define P_RESISTANCE              "resistance"
+#define P_VULNERABILITY           "vulnerability"
+#define P_TOTAL_AC 	          "total_ac"
+#define P_TOTAL_WC                "total_wc"
+
+#define P_HANDS                   "hands"
+#define P_MAX_HANDS               "max_hands"
+#define P_USED_HANDS              "used_hands"
+#define P_FREE_HANDS              "free_hands"
+#define P_HANDS_USED_BY           "hands_used_by"
+
+#define P_ATTACK_BUSY	          "attack_busy"
+#define P_NO_ATTACK               "no_attack"
+
+#define P_PREFERED_ENEMY          "pref_enemy"
+#define P_SHOW_ATTACK_MSG         "show_attack_msg"
+
+#define P_LAST_DAMAGE             "last_damage"
+#define P_LAST_DAMTYPES           "last_damtypes"
+#define P_LAST_DAMTIME            "last_damtime"
+
+#define P_DEATH_SPONSORED_BY      "responsible_wizard_for_death"
+#define P_KILLER                  "killer"
+#define P_MURDER_MSG              "murder_msg"
+#define P_FORCE_MURDER_MSG        "force_murder_msg"
+#define P_ENEMY_DEATH_SEQUENCE    "enemy_death_sequence"
+#define P_NEXT_DEATH_SEQUENCE     "p_lib_next_death_sequence"
+#define P_DEATH_MSG               "death_msg"
+
+#define P_HUNTTIME                "p_lib_hunttime"
+
+// Befriede: Gildenzaehler
+#define P_PEACE_HISTORY           "_peace_history"
+
+#define DISABLE_TOO_EARLY 	  -100111
+
+#define ENEMY_HUNTTIME		0
+
+// Helfer-NPC
+// a) NPC: welchem Spieler ist dieser NPC zugeordnet, d.h. fuer wen kaempft
+//         er? Array: ({ spielerobject, flags })
+// b) Spieler: welche NPC sind diesem Spieler zugeordnet. Mapping mit Objekten
+//             und Flags.
+#define P_HELPER_NPC              "std:helper_npc"
+// Flags:
+// 2 Klassen von Helfern momentan: Gilden-NPC und sonstige.
+#define GUILD_HELPER     0x1
+#define MISC_HELPER      0x2
+// Wenn gesetzt, ist der Helfer exklusiv und laesst keinen anderen in der
+// derselben Klasse zu.
+#define EXCLUSIVE_HELPER 0x40000000
+// Wenn gesetzt, ist der Helfer 'aktiv', d.h. nicht nur Schlagfaenger
+#define ACTIVE_HELPER    0x20000000
+
+// individuelle Schadensmeldungen eines Lebewesens
+#define P_DAMAGE_MSG     "std_p_dam_msg"
+
+#endif // __LIVING_COMBAT_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __LIVING_COMBAT_H_PROTO__
+#define __LIVING_COMBAT_H_PROTO__
+
+// prototypes
+public int Kill(object ob);
+public void Attack2(object enemy);
+public void Attack(object enemy);
+public void AddDefender(object friend);
+public void RemoveDefender(object friend);
+public void InformDefend(object enemy);
+public mixed DefendOther(int dam, string|string* dam_type, int|mapping spell, object enemy);
+public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy);
+public void CheckWimpyAndFlee();
+public varargs void Flee(object oldenv, int force);
+
+public varargs int StopHuntFor(object arg, int silent);
+public varargs mapping StopHuntingMode(int silent);
+
+public void UpdateResistanceStrengths();
+public varargs int AddResistanceModifier(mapping mod, string add);
+public varargs void RemoveResistanceModifier(string add);
+public float CheckResistance(string* dam_type);
+
+public int InsertSingleEnemy(object ob);
+public int InsertEnemy(object ob);
+public int IsEnemy(object wer);
+public object *PresentEnemies();
+public object QueryPreferedEnemy();
+public varargs object SelectEnemy(object *here);
+public <object*|int*>* QueryEnemies();
+public mapping GetEnemies();
+#if __VERSION_MAJOR__>=3 && __VERSION_MINOR__>=5 && (__VERSION_MICRO__>0 || (__VERSION_MICRO__==0 && __VERSION_PATCH__>=2))
+public mapping SetEnemies(<object*|int*>* myenemies);
+#else
+public mapping SetEnemies(mixed *myenemies);
+#endif
+public mixed EnemyPresent();
+public mixed InFight();
+public int CheckEnemy(object ob);
+
+public int SpellDefend(object caster, mapping sinfo);
+
+public object|object* QueryArmourByType(string type);
+
+public varargs int UseHands(object ob, int num);
+public varargs int FreeHands(object ob);
+
+public void InitAttack();
+public void ExitAttack();
+
+public int Pacify(object caster);
+
+public varargs int StopHuntID(string str, int silent);
+public void StopHuntText(object arg);
+public varargs void ExecuteMissingAttacks(object *remove_attackers);
+
+// Query- und Set-Methoden
+static int     _query_attack_busy();
+static int     _query_free_hands();
+static mixed   _query_hands();
+static mixed  *_query_hands_used_by();
+static mapping _query_resistance_strengths();
+static int     _query_total_ac();
+static int     _query_total_wc();
+static int     _query_used_hands();
+static int     _set_attack_busy(mixed val);
+static int     _set_disable_attack(int val);
+static mixed   _set_hands(mixed h);
+static mixed   _set_resistance(mixed arg);
+static int     _set_used_hands(int new_num);
+static mixed   _set_vulnerability(mixed arg);
+static int     _set_wimpy(int i);
+
+// Internal
+protected void SkillResTransfer(mapping from_M, mapping to_M);
+protected void InternalModifyAttack(mapping ainfo);
+protected void InternalModifyDefend(int dam, string* dt, mapping spell, object enemy);
+protected string mess(string msg,object me,object enemy);
+ 
+#endif // __LIVING_COMBAT_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/living/comm.h b/sys/living/comm.h
new file mode 100644
index 0000000..3950622
--- /dev/null
+++ b/sys/living/comm.h
@@ -0,0 +1,101 @@
+// MorgenGrauen MUDlib
+//
+// living/comm.h -- communication module properties for Livings
+//
+// $Id: comm.h,v 3.3 2000/02/09 15:56:25 Padreic Exp $
+
+#ifndef __LIVING_COMM_H__
+#define __LIVING_COMM_H__
+
+#include <break_string.h>
+
+// Flags fuer ReceiveMsg().
+// The BS-Flags MUST always be equal to the one defined for break_string!
+#define MSG_BS_LEAVE_LFS    BS_LEAVE_MY_LFS    //1
+#define MSG_BS_SINGLE_SPACE BS_SINGLE_SPACE    //2
+#define MSG_BS_BLOCK        BS_BLOCK           //4
+#define MSG_BS_NO_PARINDENT BS_NO_PARINDENT    //8
+#define MSG_BS_INDENT_ONCE  BS_INDENT_ONCE     //16
+#define MSG_BS_PREP_INDENT  BS_PREPEND_INDENT  //32
+// Flags < 1024 (2^10) reserviert fuer BS
+#define MSG_DONT_BUFFER  1024
+#define MSG_DONT_STORE   2048
+#define MSG_DONT_WRAP    4096
+#define MSG_DONT_IGNORE  8192
+// Flags < 1048576 (2^20) reserviert fuer Flags
+#define MSG_ALL_BS_FLAGS (BS_LEAVE_MY_LFS | BS_SINGLE_SPACE | BS_BLOCK\
+                          | BS_NO_PARINDENT | BS_INDENT_ONCE\
+                          |BS_PREPEND_INDENT)
+#define MSG_ALL_FLAGS (MSG_DONT_BUFFER | MSG_DONT_STORE | MSG_DONT_WRAP\
+                      | MSG_DONT_IGNORE | MSG_ALL_BS_FLAGS)
+
+// Nachrichtentypen, Werte ab 2^20
+#define MT_UNKNOWN    0
+#define MT_LOOK       1048576
+#define MT_LISTEN     2097152
+#define MT_FEEL       4194304
+#define MT_TASTE      8388608
+#define MT_SMELL      16777216
+#define MT_MAGIC      33554432
+#define MT_NOTIFICATION 67108864
+#define MT_COMM       134217728
+#define MT_FAR        268435456
+#define MT_DEBUG      536870912
+#define MT_NEWS       1073741824
+
+// Aktionen
+// Bei MA_UNKNOWN wird die Aktion aus query_verb() geraten. Wenn das nicht
+// existiert, bleibt die Aktion MA_UNKNOWN.
+#define MA_UNKNOWN    0
+#define MA_PUT        "wirf"
+#define MA_TAKE       "nimm"
+#define MA_MOVE_IN    "move_in"
+#define MA_MOVE_OUT   "move_out"
+#define MA_MOVE       "move"
+#define MA_FIGHT      "toete"
+#define MA_WIELD      "zuecke"
+#define MA_UNWIELD    "stecke"
+#define MA_WEAR       "trage"
+#define MA_UNWEAR     "ziehe"
+#define MA_EAT        "iss"
+#define MA_DRINK      "trinke"
+#define MA_SPELL      "zaubere"
+#define MA_LOOK       "untersuche"
+#define MA_LISTEN     "lausche"
+#define MA_FEEL       "taste"
+#define MA_SMELL      "rieche"
+#define MA_SENSE      "erspueren"
+#define MA_READ       "lies"
+#define MA_USE        "benutze"
+#define MA_SAY        "sage"
+#define MA_REMOVE     "remove"
+#define MA_CHANNEL    "ebenen"
+#define MA_EMOTE      "emote"
+#define MA_SHOUT      "rufe"
+#define MA_SHOUT_SEFUN "shout"
+#define MA_TELL       "teilemit"
+
+// Rueckgabewerte von ReceiveMsg()
+#define MSG_DELIVERED    1
+#define MSG_BUFFERED     2
+#define MSG_FAILED      -1
+#define MSG_IGNORED     -2
+#define MSG_VERB_IGN    -3
+#define MSG_MUD_IGN     -4
+#define MSG_SENSE_BLOCK -5
+#define MSG_BUFFER_FULL -6
+
+#endif // __LIVING_COMM_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __LIVING_COMM_H_PROTO__
+#define __LIVING_COMM_H_PROTO__
+
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+                              string msg_prefix, object origin);
+
+#endif // __LIVING_COMM_H_PROTO__
+
+#endif // NEED_PROTOYPES
+
diff --git a/sys/living/description.h b/sys/living/description.h
new file mode 100644
index 0000000..dc07fbf
--- /dev/null
+++ b/sys/living/description.h
@@ -0,0 +1,48 @@
+#ifndef __LIVING_DESCRIPTION_H__
+#define __LIVING_DESCRIPTION_H__
+
+// properties
+
+#define P_GUILD                   "guild"
+#define P_VISIBLE_GUILD           "visible_guild"
+
+#define P_EXTRA_LOOK              "extralook"
+#define P_INTERNAL_EXTRA_LOOK     "internal_extralook"
+
+#define P_PLAYER_LIGHT            "player_light"
+#define P_LIGHT_MODIFIER          "light_modifier"
+
+#define P_LEVEL                   "level"
+
+#define P_RACE                    "race"
+#define P_REAL_RACE               "real_race"
+
+// bitte auch in /secure/master.h aendern, falls das mal geaendert wird.
+#define P_WEAPON                  "weapon"
+
+#define P_PARA                    "para"
+#define P_SIZE                    "size"
+
+#define P_DIE_MSG                 "die_msg"
+
+#define P_WIMPY                   "wimpy"
+#define P_WIMPY_DIRECTION         "wimpy_dir"
+
+#define P_AVATAR_URI              "p_lib_avataruri"
+
+#endif // __LIVING_DESCRIPTION_H__
+
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __LIVING_DESCRIPTION_H_PROTO__
+#define __LIVING_DESCRIPTION_H_PROTO__
+
+varargs int         CannotSee(int silent);
+public varargs int  AddExtraLook(string look, int duration, string key, 
+                                 string lookende, object ob);
+public int          RemoveExtraLook(string key);
+
+#endif // __LIVING_DESCRIPTION_H_PROTO__
+
+#endif // NEED_PROTOYPES
diff --git a/sys/living/helpers.h b/sys/living/helpers.h
new file mode 100644
index 0000000..4e3793a
--- /dev/null
+++ b/sys/living/helpers.h
@@ -0,0 +1,42 @@
+// MorgenGrauen MUDlib
+//
+// living/helpers.h -- Konstanten und Methoden fuer Hilfsobjekte
+//
+// $Id: moneyhandler.h,v 3.1 1997/02/12 13:29:09 Wargon Exp %
+
+#ifndef __LIVING_HELPERS_H__
+#define __LIVING_HELPERS_H__
+
+// Properties
+
+#define P_HELPER_OBJECTS    "lib_p_helper_objects"
+
+#define P_AQUATIC_HELPERS   "lib_p_aquatic_helpers"
+#define P_AERIAL_HELPERS    "lib_p_aerial_helpers"
+
+// Defines fuer Helferobjekt-Typen und Rueckgabewerte
+
+#define HELPER_TYPE_AQUATIC 1
+#define HELPER_TYPE_AERIAL  2
+
+#define HELPER_NO_CALLBACK_OBJECT    -1
+#define HELPER_ALREADY_LISTED        -2
+#define HELPER_NOTHING_TO_UNREGISTER -3
+#define HELPER_SUCCESS                1
+
+#endif // __LIVING_HELPERS_H__
+
+// Prototypes
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __LIVING_HELPERS_H_PROTO__
+#define __LIVING_HELPERS_H_PROTO__
+
+int RegisterHelperObject(object helper, int type, mixed callback);
+int UnregisterHelperObject(object helper, int type);
+
+#endif // __LIVING_HELPERS_H_PROTO__
+
+#endif // NEED_PROTOYPES
+
diff --git a/sys/living/life.h b/sys/living/life.h
new file mode 100644
index 0000000..81ba3dd
--- /dev/null
+++ b/sys/living/life.h
@@ -0,0 +1,125 @@
+// MorgenGrauen MUDlib
+//
+// living/life.h -- living life header
+//
+// $Id: life.h 9048 2015-01-11 18:21:32Z Zesstra $
+
+#ifndef __LIVING_LIFE_H__
+#define __LIVING_LIFE_H__
+
+// properties
+
+#define P_AGE                     "age"
+#define P_ALIGN                   "align"
+
+#define P_DEADS                   "deads"
+#define P_GHOST                   "ghost"
+#define P_FROG                    "frog"
+
+#define P_FOOD                    "food"
+#define P_MAX_FOOD                "max_food"
+#define P_DRINK                   "drink"
+#define P_MAX_DRINK               "max_drink"
+#define P_ALCOHOL                 "alcohol"
+#define P_MAX_ALCOHOL             "max_alcohol"
+
+#define P_HP                      "hp"
+#define P_MAX_HP                  "max_hp"
+#define P_SP                      "sp"
+#define P_MAX_SP                  "max_sp"
+#define P_XP                      "xp"
+#define P_NO_XP                   "no_xp"
+#define P_LAST_XP                 "last_xp"
+
+#define P_FOOD_DELAY              "food_delay"
+#define P_DRINK_DELAY             "drink_delay"
+#define P_ALCOHOL_DELAY           "alcohol_delay"
+#define P_HP_DELAY                "hp_delay"
+#define P_SP_DELAY                "sp_delay"
+#define P_POISON_DELAY            "poison_delay"
+
+#define P_POISON                  "poison"
+#define P_MAX_POISON              "max_poison"
+
+#define P_NO_REGENERATION         "no_regeneration"
+
+#define P_ENEMY_DAMAGE            "enemy_damage"
+#define P_TIMING_MAP              "timing_map"
+#define P_LAST_DEATH_TIME         "last_death_time"
+#define P_LAST_DEATH_PROPS        "last_death_props"
+#define P_CORPSE_DECAY_TIME       "corpse_decay"
+
+// ----- Definitionen fuer Beeinflussung der Gesundheit durch consume -----
+
+// H_EFFECTS - Zu aendernde Properties fuer den Effekt
+#define H_EFFECTS "effects"
+// H_CONDITIONS - Zu pruefende Properties für den Effekt (Saettigung, ...)
+#define H_CONDITIONS "conditions"
+// H_DISTRIBUTION - Wie die Aenderung an HP/SP austeilen, sofort, zeitlich
+// versetzt, ...
+#define H_DISTRIBUTION "distribution"
+// H_INGREDIENTS - Bestandteile, die die Aenderung verursachen
+#define H_INGREDIENTS "ingredients"
+
+// Verteilungen,
+// 0    Sofortwirkung
+// 1-50 gleichverteilte Pufferung mit angegebener Rate fuer SP/HP
+// >= 51 spezielle Verteilungen (TODO)
+#define HD_INSTANT      0
+#define HD_STANDARD     5
+
+#define H_ALLOWED_CONDITIONS ({P_FOOD, P_DRINK, P_ALCOHOL})
+#define H_ALLOWED_EFFECTS    ({P_SP, P_HP, P_POISON})
+
+#define HC_MAX_FOOD_REACHED        1
+#define HC_MAX_DRINK_REACHED       2
+#define HC_MAX_ALCOHOL_REACHED     4
+#define HC_HOOK_CANCELLATION       8
+
+// -------------------------------------------------------------------------
+
+#endif // __LIVING_LIFE_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __LIVING_LIFE_H_PROTO__
+#define __LIVING_LIFE_H_PROTO__         
+
+// prototypes
+
+// ----- zentrale Funktion, um die Gesundheit des Lebewesens zu beeinflussen -----
+public varargs int consume(mapping cinfo, int testonly);
+// -------------------------------------------------------------------------
+
+public int do_damage(int dam, object enemy);
+public int reduce_hit_points(int dam);
+public int restore_hit_points(int heal);
+
+public int reduce_hit_point(int dam);  //Nur als Uebergansloesung (Rikus)
+
+public void restore_spell_points(int h);
+public void reduce_spell_points(int h);
+
+public void heal_self(int h);
+
+public varargs void die( int poisondeath, int extern );
+
+public varargs void transfer_all_to( string|object dest, int check );
+
+public varargs int drink_alcohol(int strength, int testonly, string mytext);
+public varargs int drink_soft(int strength, int testonly, string mytext);
+public varargs int eat_food(int strength, int testonly, string mytext);
+
+public int buffer_hp(int val,int rate);
+public int buffer_sp(int val,int rate);
+
+public void show_age();
+
+public int AddExp(int e);
+
+// internal
+protected void ResetEnemyDamage();
+
+#endif // __LIVING_LIFE_H_PROTO__
+
+#endif // NEED_PROTOYPES
diff --git a/sys/living/moving.h b/sys/living/moving.h
new file mode 100644
index 0000000..4914466
--- /dev/null
+++ b/sys/living/moving.h
@@ -0,0 +1,40 @@
+// MorgenGrauen MUDlib
+//
+// living/moving.h -- Props/Prototypen fuer living/moving.c
+//
+// $Id: moving.h 8892 2014-08-04 19:48:28Z Zesstra $
+
+#ifndef __LIVING_MOVING_H__
+#define __LIVING_MOVING_H__
+
+#define P_PURSUERS       "pursuers"
+
+#endif // __LIVING_MOVING_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __LIVING_MOVING_H_PROTO__
+#define __LIVING_MOVING_H_PROTO__
+
+public void AddPursuer(object ob);
+public void RemovePursuer(object ob);
+
+public void TakeFollowers();
+
+#ifdef __THING_MOVING_H_PROTO__
+// das darf nicht passieren, weil move in thing anders ist als hier. *seufz*
+inconsistent_move_protoyp_error
+#endif
+public varargs int move( object|string dest, int methods, string direction,
+                         string textout, string textin );
+public varargs int remove(int silent);
+
+// internal
+public void _SetPursued(object ob);
+public void _RemovePursued(object ob);
+
+
+#endif // __LIVING_MOVING_H_PROTO__
+
+#endif // NEED_PROTOTYPES
+
diff --git a/sys/living/put_and_get.h b/sys/living/put_and_get.h
new file mode 100644
index 0000000..802ad23
--- /dev/null
+++ b/sys/living/put_and_get.h
@@ -0,0 +1,37 @@
+// MorgenGrauen MUDlib
+//
+// living/put_and_get.h -- living put and get header
+//
+// $Id: put_and_get.h 6930 2008-08-09 09:20:16Z Zesstra $
+ 
+#ifndef __LIVING_PUT_AND_GET_H__
+#define __LIVING_PUT_AND_GET_H__
+
+// properties
+#define P_DROP_MSG        "drop_message" 
+#define P_PUT_MSG         "put_message"
+#define P_PICK_MSG        "pick_message"
+#define P_GIVE_MSG        "give_message"
+#define P_SHOW_MSG        "show_message"
+
+// If you don't want put_and_get send a message, set the property to
+#define NO_PNG_MSG       -1
+
+// For user defined drop messages look for man P_DROP_MSG
+
+#endif // __LIVING_PUT_AND_GET_H__
+
+// prototypes
+#ifdef NEED_PROTOTYPES
+
+#ifndef __LIVING_PUT_AND_GET_H_PROTO__
+#define __LIVING_PUT_AND_GET_H_PROTO__
+
+object *find_obs(string str, int meth);
+
+void add_put_and_get_commands();
+
+#endif // __LIVING_PUT_AND_GET_H_PROTO__
+
+#endif	// NEED_PROTOYPES
+
diff --git a/sys/living/skill_attributes.h b/sys/living/skill_attributes.h
new file mode 100644
index 0000000..939ca15
--- /dev/null
+++ b/sys/living/skill_attributes.h
@@ -0,0 +1,100 @@
+// MorgenGrauen MUDlib
+//
+// FileName.c -- Beschreibung
+//
+// $Id$
+
+#ifndef __SKILL_ATTRIBUTES_H__
+#define __SKILL_ATTRIBUTES_H__
+
+// ===== Properties fuer Skill-Attribute: =====
+
+#define P_SKILL_ATTRIBUTES          "skill_attr"
+#define P_SKILL_ATTRIBUTE_OFFSETS   "skill_attr_offsets"
+
+// ===== Skill Attribute: =====  
+// Allgemeine Qualitaet
+#define SA_QUALITY                  "sa_qual"
+  
+// Schaden
+#define SA_DAMAGE                   "sa_dam"
+  
+// Geschwindigkeit
+#define SA_SPEED                    "sa_speed"
+
+// Dauer
+#define SA_DURATION                 "sa_dur"
+
+// Ausdehnung
+#define SA_EXTENSION                "sa_ext"
+
+// Reichweite
+#define SA_RANGE                    "sa_range"
+
+// Je hoeher, desto groesser die Chance, dass nen vom Lebewesen gecasteter
+// Spell durch das gegnerische P_NOMAGIC durchschlaegt.
+// (SA_ENEMY_SAVE obsolet!)
+#define SA_ENEMY_SAVE               "sa_save"
+#define SA_SPELL_PENETRATION        "sa_save"
+
+// welche Skill-Attribute gibt es?
+#define VALID_SKILL_ATTRIBUTES ({ SA_QUALITY, SA_DAMAGE, SA_SPEED,\
+                                  SA_DURATION, SA_EXTENSION, SA_RANGE,\
+                                  SA_ENEMY_SAVE })
+
+// Indizes fuer die Verwaltung der Skill-Attribute
+#define SAM_CACHE              0
+#define SAM_STATIC             1
+#define SAM_DYNAMIC            2
+
+#define SAM_SUM                0
+#define SAM_CACHE_TIMEOUT      1
+#define SAM_COUNT              2
+
+#define SAM_DURATION           0
+#define SAM_VALUE              1
+
+
+// einige Konstanten
+#define SAM_MAX_MODS           5
+#define SAM_MAX_CACHE_TIMEOUT  60
+
+// rueckgabewerte der Verwaltungsfunktionen:
+#define SA_MOD_OK              1
+#define SA_MOD_REMOVED         1
+#define SA_TOO_MANY_MODS       -1
+#define SA_MOD_TOO_SMALL       -2
+#define SA_MOD_TOO_BIG         -3
+#define SA_MOD_INVALID_ATTR    -4
+#define SA_MOD_INVALID_OBJECT  -5
+#define SA_MOD_NOT_FOUND       -6
+#define SA_MOD_INVALID_VALUE   -7
+
+// Statistik?
+#define SASTATD "/p/daemon/sastatd"
+
+#endif // __SKILL_ATTRIBUTES_H__
+
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __SKILL_ATTRIBUTES_H_PROTO__
+#define __SKILL_ATTRIBUTES_H_PROTO__
+
+// prototypes
+
+public int    ModifySkillAttribute(string atrname, mixed value,
+                                    int duration);
+public int    RemoveSkillAttributeModifier(object caster, string attrname);
+public int    QuerySkillAttribute(string attr);
+public varargs mapping QuerySkillAttributeModifier(object caster,
+                           string *attr);
+
+// Kompatibilitaetsfunktion mit altem Interface.
+public int    ModifySkillAttributeOld(object caster, string atrname, int value,
+                          int duration, mixed fun);
+
+#endif // __SKILL_ATTRIBUTES_H_PROTO__
+
+#endif // NEED_PROTOYPES
+
diff --git a/sys/living/skills.h b/sys/living/skills.h
new file mode 100644
index 0000000..ef2572c
--- /dev/null
+++ b/sys/living/skills.h
@@ -0,0 +1,33 @@
+// MorgenGrauen MUDlib
+//
+// skills.h -- Defines and prototypes for /std/living/skills.c
+//
+// $Id: skills.h 8694 2014-02-20 23:06:50Z Zesstra $
+
+#ifndef _LIVING_SKILLS_H_
+#define _LIVING_SKILLS_H_
+
+#endif // _LIVING_SKILLS_H_
+
+#ifdef NEED_PROTOTYPES
+#ifndef _LIVING_SKILLS_H_PROTO_
+#define _LIVING_SKILLS_H_PROTO_
+public    varargs int     UseSpell(string str, string spell);
+
+public    varargs mapping QuerySkill(string sname, string gilde);
+public    varargs int     QuerySkillAbility(mixed sname, mixed argl);
+
+protected varargs mixed   LimitAbility(mixed sinfo, int diff);
+public    varargs mixed   UseSkill(string skill, mapping args);
+
+public    varargs void    LearnSkill(string sname, int add, int diff);
+public    varargs void    ModifySkill(string sname, mixed val, int diff, 
+                                      string gilde);
+public    varargs int     SetSpellFatigue(int duration, string key);
+public    varargs int     CheckSpellFatigue(string key);
+public            void    DeleteSpellFatigue(string key);
+
+#endif // _LIVING_SKILLS_H_PROTO_
+
+#endif // NEED_PROTOTYPES
+
diff --git a/sys/living/team.h b/sys/living/team.h
new file mode 100644
index 0000000..942e0c3
--- /dev/null
+++ b/sys/living/team.h
@@ -0,0 +1,50 @@
+// MorgenGrauen MUDlib
+//
+// living/team.h -- team header
+//
+// $Id: team.h 8449 2013-03-04 21:06:38Z Zesstra $
+
+#ifndef __LIVING_TEAM_H__
+#define __LIVING_TEAM_H__
+
+#define P_TEAM                    "team"
+#define P_TEAM_NEWMEMBER          "potential_team_member"
+#define P_TEAM_ATTACK_CMD         "team_attack_cmd"
+#define P_TEAM_AUTOFOLLOW         "team_autofollow"
+#define P_TEAM_WANTED_ROW         "team_wanted_row"
+#define P_TEAM_WIMPY_ROW          "team_wimpy_row"
+#define P_TEAM_LEADER             "team_leader"
+#define P_TEAM_ASSOC_MEMBERS      "team_assoc_members"
+#define P_TEAM_COLORS             "team_colors"
+
+#define TEAM_OBJECT "/obj/team"
+#define TEAM_MASTER "/p/daemon/teammaster"
+#define EMPTY_TEAMARRAY ({({}),({}),({}),({}),({})})
+#define MAX_TEAMROWS 5
+#define MAX_TEAM_ROWLEN 6
+#define MAX_TEAM_MEMBERS 12
+
+#endif // __LIVING_TEAM_H__
+
+#ifdef NEED_PROTOTYPES
+#ifndef __LIVING_TEAM_H_PROTO__
+#define __LIVING_TEAM_H_PROTO__
+
+string TeamPrefix();
+object IsTeamLeader();
+object IsTeamMove();
+object *TeamMembers();
+varargs int PresentPosition(mixed pmap);
+varargs mapping PresentTeamPositions(mixed pres_rows);
+mixed *PresentTeamRows();
+varargs mixed *PresentEnemyRows(object *here);
+varargs object SelectNearEnemy(object *here, int forcefrom);
+varargs object SelectFarEnemy(object *here, int min, int max, int forcefrom);
+varargs void InsertEnemyTeam(mixed ens, int rek);
+int AssocMember(object npc);
+int DeAssocMember(object npc);
+int TeamFlee();
+varargs void InformRowChange(int from, int to, object caster);
+ 
+#endif // __LIVING_TEAM_H_PROTO__
+#endif // // NEED_PROTOYPES
diff --git a/sys/logging.h b/sys/logging.h
new file mode 100644
index 0000000..ffd4a11
--- /dev/null
+++ b/sys/logging.h
@@ -0,0 +1,31 @@
+// MorgenGrauen MUDlib
+//
+// logging.h
+//
+// $Id: logging.h 7116 2009-02-06 21:49:42Z Zesstra $
+
+#ifndef __LOGGING_H__
+#define __LOGGING_H__
+
+#define SYSLOGPATH    "syslog/"
+#define SYSLOG(x)     (SYSLOGPATH+x)
+
+#define SHELLLOGPATH  SYSLOG("shell/")
+#define SHELLLOG(x)   (SHELLLOGPATH+x)
+
+#define DAEMONLOGPATH SYSLOG("daemon/")
+#define DAEMONLOG(x)  (DAEMONLOGPATH+x)
+
+#define INTERMUDLOGPATH SYSLOG("intermud/")
+#define INTERMUDLOG(x) (INTERMUDLOGPATH+x)
+
+#define GUILDLOGPATH  "gilden/"
+#define GUILDLOG(x)   (GUILDLOGPATH+x)
+
+#define REPFILEPATH   "report/"
+#define REPFILE(x)    (REPFILEPATH+x)
+
+#define ERRORLOGPATH  "error/"
+#define ERRORLOG(x)   (ERRORLOGPATH+x)
+
+#endif
diff --git a/sys/lpctypes.h b/sys/lpctypes.h
new file mode 100644
index 0000000..3187d6b
--- /dev/null
+++ b/sys/lpctypes.h
@@ -0,0 +1,64 @@
+#ifndef LPC_LPCTYPES_H
+#define LPC_LPCTYPES_H
+
+/* compile time types, from functionlist() */
+
+#define TYPE_UNKNOWN    0       /* This type must be casted */
+#define TYPE_NUMBER     1
+#define TYPE_STRING     2
+#define TYPE_VOID       3
+#define TYPE_OBJECT     4
+#define TYPE_MAPPING    5
+#define TYPE_FLOAT      6
+#define TYPE_ANY        7       /* Will match any type */
+#define TYPE_CLOSURE    8
+#define TYPE_SYMBOL     9
+#define TYPE_QUOTED_ARRAY 10
+#define TYPE_STRUCT     11
+
+#define TYPE_MOD_POINTER        0x0040  /* Pointer to a basic type        */
+
+/* runtime types, from typeof() */
+
+#define T_INVALID       0x0
+#define T_LVALUE        0x1
+#define T_NUMBER        0x2
+#define T_STRING        0x3
+#define T_POINTER       0x4
+#define T_OBJECT        0x5
+#define T_MAPPING       0x6
+#define T_FLOAT         0x7
+#define T_CLOSURE       0x8
+#define T_SYMBOL        0x9
+#define T_QUOTED_ARRAY  0xa
+#define T_STRUCT        0xb
+
+/* Closure types, stored as secondary type info */
+
+#ifndef __DRIVER_SOURCE__
+
+#define CLOSURE_LFUN            0  /* lfun in an object */
+  /* Code 1: currently unused, used to be CLOSURE_ALIEN_LFUN */
+#define CLOSURE_IDENTIFIER      2  /* variable in this object */
+#define CLOSURE_PRELIMINARY     3
+    /* Efun closure used in a static initialization */
+#define CLOSURE_BOUND_LAMBDA    4  /* Bound unbound-lambda closure */
+#define CLOSURE_LAMBDA          5  /* normal lambda closure */
+#define CLOSURE_UNBOUND_LAMBDA  6  /* unbound lambda closure. */
+
+#define CLOSURE_OPERATOR        (0xe800)
+#define CLOSURE_EFUN            (0xf000)
+#define CLOSURE_SIMUL_EFUN      (0xf800)
+
+#define CLOSURE_IS_LFUN(x)		(((x)&~1) == 0)
+#define CLOSURE_IS_IDENTIFIER(x)	((x) == CLOSURE_IDENTIFIER)
+#define CLOSURE_IS_BOUND_LAMBDA(x)	((x) == CLOSURE_BOUND_LAMBDA)
+#define CLOSURE_IS_LAMBDA(x)		((x) == CLOSURE_LAMBDA)
+#define CLOSURE_IS_UNBOUND_LAMBDA(x)	((x) == CLOSURE_UNBOUND_LAMBDA)
+#define CLOSURE_IS_SIMUL_EFUN(x) (((x) & 0xf800) == CLOSURE_SIMUL_EFUN)
+#define CLOSURE_IS_EFUN(x)	 (((x) & 0xf800) == CLOSURE_EFUN)
+#define CLOSURE_IS_OPERATOR(x)	 (((x) & 0xf800) == CLOSURE_OPERATOR)
+
+#endif /* __DRIVER_SOURCE__ */
+
+#endif /* LPC_LPCTYPES_H */
diff --git a/sys/magier.h b/sys/magier.h
new file mode 100644
index 0000000..33cd98f
--- /dev/null
+++ b/sys/magier.h
@@ -0,0 +1,225 @@
+// MorgenGrauen MUDlib
+//
+// magier.h -- Magiererweiterung der Shell
+//
+// $Id: magier.h 9553 2016-05-03 20:22:47Z Zesstra $
+
+#ifndef _MAGIER_H_
+#define _MAGIER_H_
+
+#define SHELLDEBUGGER "zesstra"
+
+#define MAX_ARRAY_SIZE   3000 // MUSS EIGENTLICH NACH /sys/config.h oder so
+
+#define ERROR(msg, arg, res)   (printf(msg, arg),res)
+
+#define NO_READ         "%s: Keine Leserechte!\n"
+#define NO_WRITE        "%s: Keine Schreibrechte!\n"
+#define DOESNT_EXIST    "%s: Datei existiert nicht!\n"
+#define ALREADY_EXISTS  "%s: Datei existiert schon.\n"
+#define NO_DIRS          "%s ist ein Verzeichnis.\n"
+#define SAME_FILE        "%s: Gleiches Quell- und Zielfile: uebergangen.\n"
+#define NO_CREATE_DIR   "%s: Verzeichnis konnte nicht erstellt werden.\n"
+#define DEST_IS_DIR     "%s: Zieldatei ist ein Verzeichnis.\n"
+#define DEST_NO_DELETE  "%s: Zieldatei konnte nicht geloescht werden.\n"
+#define NO_MOVE         "%s: Datei konnte nicht verschoben werden.\n"
+#define NO_COPY         "%s: Datei konnte nicht kopiert werden.\n"
+#define FILE_COPIED     "%s: kopiert.\n"
+#define DIR_CREATED     "%s: Verzeichnis erzeugt.\n"
+#define FILE_MOVED      "%s: verschoben.\n"
+#define FILE_DELETED    "%s wurde geloescht.\n"
+#define NO_DELETE       "%s konnte nicht geloescht werden.\n"
+
+#define MAY_READ(x)   call_other(__MASTER_OBJECT__,"valid_read",x,getuid(this_object()))
+#define MAY_WRITE(x)  call_other(__MASTER_OBJECT__,"valid_write",x,getuid(this_object()))
+#define MAY_DELETE(x) call_other(__MASTER_OBJECT__,"remove_file",x,getuid(this_object()))
+
+#define MAXLEN  50000
+
+#define P_VARIABLES "variables"
+
+#define MAN_OPTS   "mri"
+#define MAN_M            1           // Matchen des Begriffs
+#define MAN_R            2           // Regexp-Matchen
+#define MAN_I            4           // Interaktive Hilfe (deaktiviert)
+
+#define MKDIR_OPTS "vrp"
+#define MKDIR_V          1           // Verbose: Ausgeben, was gemacht wird
+#define MKDIR_R          2           // Rekursiv alle hoeheren
+                                     // Verzeichnisse erstellen
+#define MKDIR_P          2           // das gleiche wie MKDIR_R
+
+#define RMDIR_OPTS "v"
+#define RMDIR_V          1           // Verbose: Ausgeben, was gemacht wird
+
+#define RM_OPTS   "vrmi"
+#define RM_V            1            // Verbose: Ausgeben, was gemacht wird
+#define RM_R            2            // Verzeichnisse rekursiv loeschen
+#define RM_M            4            // DateiMASKE beachten
+#define RM_I            8            // Interaktiv loeschen (immer fragen)
+
+#define UPD_OPTS  "afilrmbvcdhsC"
+#define UPD_A            1           // Alle Instanzen bearbeiten
+#define UPD_F            2           // Alle Instanzen finden und anzeigen
+#define UPD_I            4           // Geerbte Klassen aktualisieren
+#define UPD_L            8           // zerstoeren und neu laden
+#define UPD_R           16           // neu laden, falls zerstoert
+#define UPD_M           32           // alle geerbten Klassen auf
+                                     // aktualitaet Testen und
+                                     // ggf. aktualisieren
+#define UPD_B           64           // Backtrace bei Fehlern
+#define UPD_V          128           // Erbschaftslisten mit ausgeben
+#define UPD_C          256           // Properties erhalten
+#define UPD_D          512           // Objektinhalt komplett destructen
+#define UPD_H         1024           // Hard-Destruct (kein remove())
+#define UPD_S         2048           // Silent
+#define UPD_CONF      4096           // Configure() erzwingen (d.h. kein Update,
+                                     // wenn Configure() nicht definiert ist)
+#define UPD_LOAD      8192           // laden, falls nicht geladen (nur
+                                     // intern fuer 'load)
+
+#define CP_OPTS         "vrfmi"
+#define CP_V            1            // Verbose: Ausgeben, was gemacht wird
+#define CP_R            2            // Rekursiv kopieren
+#define CP_F            4            // Force (nie fragen)
+#define CP_M            8            // DateiMASKE beachten
+#define CP_I           16            // Interaktiv kopieren (immer fragen)
+
+#define MV_OPTS         "vrfmi"
+#define MV_V            1            // Verbose: Ausgeben, was gemacht wird
+#define MV_R            2            // Immer auch Dateien im Dir moven
+#define MV_F            4            // Force (nie fragen)
+#define MV_M            8            // DateiMASKE beachten
+#define MV_I           16            // Interaktiv bewegen (immer fragen)
+
+#define CD_OPTS         "ls"
+#define CD_L            1            // .readme ausgeben
+#define CD_S            2            // .readme unterdruecken
+#define CD_B            4            // Zurueck zum letzten Verzeichnis
+
+#define LS_OPTS         "alrtsug"
+#define LS_A            1            // ALLE Files anzeigen
+#define LS_L            2            // Lange Ausgabe
+#define LS_R            4            // Rueckwaerts ausgeben
+#define LS_T            8            // Nach Zeit ordnen
+#define LS_S            16           // Nach Größe ordnen
+#define LS_U            32           // UID anzeigen
+#define LS_G            64           // Gruppe anzeigen
+
+#define CLONE_OPTS      "f"
+#define CLONE_F         1            // Auf jeden Fall versuchen zu clonen
+
+#define GREP_OPTS       "chilnvrmf"
+#define GREP_C          1            // Nur Zahl der Zeilen ausgeben
+#define GREP_H          2            // Dateinamen nicht mit ausgeben
+#define GREP_I          4            // Gross-/Kleinschreibung ignorieren
+#define GREP_L          8            // Dateinamen immer mit ausgeben
+#define GREP_N          16           // Zeilennummern mit ausgeben
+#define GREP_V          32           // Nur Zeilen matchen, die NICHT passen
+#define GREP_R          64           // Rekursiv suchen
+#define GREP_M          128          // Dateimaske angegeben
+#define GREP_F          256          // Ausgabe in Datei
+
+#define ACCESS_OPTS     "arlfgm"
+#define ACCESS_A        1            // Berechtigung hinzufuegen
+#define ACCESS_R        2            // Berechtigung entfernen
+#define ACCESS_L        4            // Berechtigungen anzeigen
+#define ACCESS_F        8            // Berechtigung fuer Dateien anzeigen
+#define ACCESS_G        16           // Das ganze fuer Gruppen
+#define ACCESS_M        32           // Das ganze fuer Gruppenleiter
+
+// Funktioniert nur, wenn SNOOP_L == SF_LOCKED und SNOOP_F == SF_FORCE
+// (Definitionen aus /sys/snooping.h)
+#define SNOOP_OPTS      "lf"
+#define SNOOP_L         1            // Niedriger Magier kann sich nicht
+                                     // dazwischenklinken
+#define SNOOP_F         2            // Snoope snoopenden Magier, wenn der
+                                     // Charakter schon gesnoopt wird
+
+#define NO_CHECK        M_GO|M_SILENT|M_NO_SHOW|M_NO_ATTACK|M_NOCHECK
+
+#define INV_SAVE        "/room/void.c"
+
+#define USAGE(str)      (_notify_fail(break_string(sprintf("Syntax: %s\n", str),77)), 0)
+
+#define BASENAME 0
+#define FILESIZE 1
+#define FILEDATE 2
+#define FULLNAME 3
+#define PATHNAME 4
+#define DESTNAME 5
+#define SUBDIRSIZE 6
+
+#define RET_OK 1
+#define RET_FAIL 0
+#define RET_JUMP -1
+#define RET_DELETE -2
+
+#define MODE_LSA   0
+#define MODE_LSB   1
+#define MODE_CP    2
+#define MODE_MV    3
+#define MODE_RM    4
+#define MODE_CD    5
+#define MODE_CAT   6
+#define MODE_GREP  7
+#define MODE_ED    8
+#define MODE_UPD   9
+#define MODE_MORE  10
+#define MODE_RMDIR 11
+#define MODE_CLONE 12
+
+#define COLORS ([ "none": ANSI_NORMAL, \
+                  "bold": ANSI_BOLD, \
+                  "blink": ANSI_BLINK, \
+                  "invers": ANSI_INVERS, \
+                  "black": ANSI_BLACK, \
+                  "red": ANSI_RED, \
+                  "green": ANSI_GREEN, \
+                  "yellow": ANSI_YELLOW, \
+                  "blue": ANSI_BLUE, \
+                  "purple": ANSI_PURPLE, \
+                  "cyan": ANSI_CYAN, \
+                  "white": ANSI_WHITE, \
+                  "bblack": ANSI_BG_BLACK, \
+                  "bred": ANSI_BG_RED, \
+                  "bgreen": ANSI_BG_GREEN, \
+                  "byellow": ANSI_BG_YELLOW, \
+                  "bblue": ANSI_BG_BLUE, \
+                  "bpurple": ANSI_BG_PURPLE, \
+                  "bcyan": ANSI_BG_CYAN, \
+                  "bwhite": ANSI_BG_WHITE, \
+                ])
+
+#define VALID_READ_CL  symbol_function("valid_read",__MASTER_OBJECT__)
+#define VALID_WRITE_CL symbol_function("valid_write",__MASTER_OBJECT__)
+#define CREATOR_CL  symbol_function("creator_file",__MASTER_OBJECT__)
+
+#define DIR "directory"
+#define OBJ "loaded"
+#define VC  "virtual"
+
+#ifdef DEBUG
+#undef DEBUG
+#endif
+#define DEBUG(x) if (find_player(SHELLDEBUGGER)) \
+                     tell_object(find_player(SHELLDEBUGGER),x+"\n")
+
+#endif // _MAGIER_H_
+
+
+#ifdef NEED_PROTOTYPES
+#ifndef _MAGIER_H_PROTOS_
+#define _MAGIER_H_PROTOS_
+static string *parseargs(string cmdline,int flags,string opts,int build_fn);
+static varargs void asynchron(mixed* array, closure cmd, mixed data, mixed flags, int c);
+static varargs mixed *file_list(string *files, int mode, int recursive, string dest, string mask);
+static mixed to_filename(string str);
+static int _verfolge(string str);
+static string glob2regexp(string str);
+
+varargs public void More(string txt, int file,mixed ctrl, mixed *ctrlargs, int flags);
+#endif // _MAGIER_H_PROTOS_
+#endif // NEED_PROTOTYPES
+
+
diff --git a/sys/mail.h b/sys/mail.h
new file mode 100644
index 0000000..9db1ccc
--- /dev/null
+++ b/sys/mail.h
@@ -0,0 +1,43 @@
+// MorgenGrauen MUDlib
+//
+// mail.h -- Definitionen fuer die Post
+//
+// $Id: mail.h,v 3.4 2000/01/20 15:13:25 Tiamak Exp $
+
+#ifndef _MAIL_
+#define _MAIL_
+/*
+ *------------------------------------------------------------
+ * Definitions of fields used in mail messages
+ *------------------------------------------------------------
+ *
+ *	A message is represented as an array with the following fields:
+ */
+#define MSG_FROM 0
+#define MSG_SENDER 1
+#define MSG_RECIPIENT 2
+#define MSG_CC 3
+#define MSG_BCC 4
+#define MSG_SUBJECT 5
+#define MSG_DATE 6
+#define MSG_ID 7
+#define MSG_BODY 8
+
+#define NO_USER_ALIASES    1
+#define NO_SYSTEM_ALIASES  2
+#define NO_CARBON_COPIES   4
+
+#include "/mail/post.h"
+
+/*
+ *	DeliverMail(mixed *message)
+ *	  Hand a mail message over to the mailer demon. The mailer
+ *	  demon extracts recipients from the recipient, cc and bcc
+ *	  fields and removes the bcc information. It then deposits
+ *	  the message to the mail files of all recipients. A valid
+ *	  message is shown above.
+ *
+ *	int FingerMail(string user)
+ *	  Gives the number of unread messages a user has.
+ */
+#endif
diff --git a/sys/money.h b/sys/money.h
new file mode 100644
index 0000000..f304481
--- /dev/null
+++ b/sys/money.h
@@ -0,0 +1,32 @@
+// MorgenGrauen MUDlib
+//
+// money.h -- Konstanten und Pfade rund ums Geld
+//
+// $Id: moneyhandler.h,v 3.1 1997/02/12 13:29:09 Wargon Exp $
+
+#ifndef __LIB_MONEY_H__
+#define __LIB_MONEY_H__
+
+#include <container/moneyhandler.h>
+#include <bank.h>
+
+#define GELD       "/items/money"
+#define BOERSE     "/items/boerse"
+#define SEHERKARTE "/items/seercard"
+
+#define GELDID       "\ngeld"
+#define BOERSEID     "\ngeldboerse"
+#define SEHERKARTEID "\ngeldkarte"
+// Id fuer eine Boerse mit Geld drin...
+#define GELDBOERSE_MIT_GELD "\ngeldboerse_mit_geld"
+// ID fuer aktive Geldkarte
+#define SEHERKARTEID_AKTIV "\ngeldkarte_aktiv"
+
+#endif
+
+#ifdef NEED_PROTOTYPES
+#ifndef __LIB_MONEY_H_PROTO__
+#define __LIB_MONEY_H_PROTO__
+
+#endif
+#endif
diff --git a/sys/moving.h b/sys/moving.h
new file mode 100644
index 0000000..9b73bcc
--- /dev/null
+++ b/sys/moving.h
@@ -0,0 +1,83 @@
+// MorgenGrauen MUDlib
+//
+// moving.h -- Methoden und Rueckgabewerte fuer/von move()
+//             Dieser Header sollte IMMER benutzt werden, wenn ein Objekt
+//             move() benutzt!
+//
+// $Id: moving.h 8801 2014-05-04 21:30:59Z Zesstra $
+
+#ifndef _MOVING_
+#define _MOVING_
+
+// leider nicht via <> moeglich, da dieses File benutzt wird, bevor der Master
+// den Include-Dir-Hook gesetzt hat.
+#include "/sys/living/moving.h"
+#include "/sys/player/moving.h"
+
+/*
+ * moving.h
+ *
+ * How objects are moving around and reasons why they cant do it.
+ */
+
+/* Methods to move objects */
+#define M_GO      1  /* we walk around */
+#define M_TPORT 128  /* We teleport to another place */
+#define M_NOCHECK 2  /* just plain move it without any checks */
+#define M_GET     4  /* obj geht von nonliving zu living */
+#define M_PUT     8  /* obj geht von living zu nonliving */
+#define M_GIVE   16  /* obj geht von living zu living */
+#define M_SILENT 32  /* obj soll ohne meldung ausgezogen werden */
+#define M_NO_ATTACK 512 /* Kein Begruessungsschlag */
+#define M_NO_SHOW 256 /* Zeige nicht die Room-Descr beim ankommen */
+
+/* Hier folgt noch was fuer die Unit */
+#define M_MOVE_ALL 32
+
+/* Errors when moving things */
+#define MOVE_OK                   1 // gar kein Fehler. ;-)
+/* Hey, I'm a player and cant be simply carried away */
+#define ME_PLAYER 	         -1
+/* Cant move into container because I'm too heavy */
+#define ME_TOO_HEAVY             -2
+#define ME_CANT_TPORT_IN         -3 /* Cant teleport there ! */
+#define ME_CANT_TPORT_OUT        -4 /* Cant teleport from here ! */
+#define ME_CANT_BE_DROPPED       -5 /* You cant drop the feel... this thing */
+#define ME_CANT_BE_TAKEN         -6 /* Nope, you cannot take me */
+#define ME_CANT_BE_INSERTED      -7 /* Container prevents me from inserting */
+#define ME_CANT_LEAVE_ENV        -8 /* Container prevents me from leaving */
+/* Can't move into container, its Env would get too heavy */
+#define ME_TOO_HEAVY_FOR_ENV     -9
+#define TOO_MANY_OBJECTS         -10 /* too many objects in new environment */
+#define ME_NOT_ALLOWED           -11 /* Sorry - off limits for players */
+// Object was destructed during move/in init()
+#define ME_WAS_DESTRUCTED        -12
+// object itself does not want to be moved for undefined reason
+#define ME_DONT_WANT_TO_BE_MOVED -13
+
+// check for valid move error - mainly for internal use!
+#define ALL_MOVE_ERRORS ({ME_PLAYER,ME_TOO_HEAVY,ME_CANT_TPORT_IN,\
+                          ME_CANT_TPORT_OUT,ME_CANT_BE_DROPPED,\
+                          ME_CANT_BE_TAKEN,ME_CANT_BE_INSERTED,\
+                          ME_CANT_LEAVE_ENV,ME_TOO_HEAVY_FOR_ENV,\
+                          TOO_MANY_OBJECTS,ME_NOT_ALLOWED,\
+                          ME_WAS_DESTRUCTED,ME_DONT_WANT_TO_BE_MOVED})
+#define VALID_MOVE_ERROR(x) (member(ALL_MOVE_ERRORS,x) != -1)
+
+#define NO_TPORT 1
+#define NO_TPORT_IN 2
+#define NO_TPORT_OUT 4
+
+/* Ablehnreaktionen von NPCs */
+#define REJECT_LIGHT_MODIFIER "reject_light_modifier"
+#define REJECT_DROP           "reject_drop"
+#define REJECT_GIVE           "reject_give"
+#define REJECT_KEEP           "reject_keep"
+
+/* Zugriffsmethoden fuer find_obs */
+
+#define PUT_GET_NONE 0x00
+#define PUT_GET_TAKE 0x01
+#define PUT_GET_DROP 0x02
+
+#endif
diff --git a/sys/new_skills.h b/sys/new_skills.h
new file mode 100644
index 0000000..6af5cbc
--- /dev/null
+++ b/sys/new_skills.h
@@ -0,0 +1,415 @@
+// MorgenGrauen MUDlib
+//
+// new_skills.h -- Alle noetigen Defintionen fuer Skills (Faehigkeiten),
+//                 Spells (Zaubersprueche) und Gilden.
+//
+// $Id: new_skills.h 8610 2014-01-06 20:59:13Z Zesstra $
+
+#ifndef _NEW_SKILLS_H_
+#define _NEW_SKILLS_H_
+
+#include <combat.h>
+#include <living/skill_attributes.h>
+
+// ===== Aktuelle Version der Skills ====
+#define CURRENT_SKILL_VERSION       2
+
+// ===== Allgemeine Defines: =====
+
+#define DEFAULT_GUILD               "abenteurer"
+#define GUILD_DIR                   "/gilden/"
+#define GUILD_SAVEFILE              "/etc/gildenmaster"
+#define GUILDMASTER                 "/secure/gildenmaster"
+#define SPELLBOOK_DIR               "/spellbooks/"
+
+#define MAX_ABILITY                 10000
+#define MAX_SKILLEARN               200
+
+// ===== Moegliche Resultate eine Spells: =====
+
+#define ERFOLG                      1
+#define MISSERFOLG                  -1
+#define ABGEWEHRT                   2
+
+// ===== Properties des Gilden-Masters: =====
+
+#define P_VALID_GUILDS              "valid_guilds"
+
+// ===== Properties der Gilde: ======
+
+#define P_GUILD_SKILLS              "guild_skills"
+#define P_GUILD_RESTRICTIONS        "guild_rest"
+//bitte dieses Define auch in /scure/master/userinfo.c aendern...
+#define P_GUILD_DEFAULT_SPELLBOOK   "guild_sb"
+#define P_GUILD_MALE_TITLES         "guild_male_titles"
+#define P_GUILD_FEMALE_TITLES       "guild_female_titles"
+#define P_GUILD_LEVELS              "guild_levels"
+#define P_GUILD_PREPAREBLOCK        "guild_prepareblock"
+#define P_GUILD_DEACTIVATE_SKILLS   "guild_deactivate_skills"
+
+// ===== Properties des Spellbooks: =====
+
+#define P_SB_SPELLS                 "sb_spells"
+
+// ===== Properties der Gilde UND des Spellbooks: =====
+#define P_GLOBAL_SKILLPROPS         "sm_global"
+
+// ===== Properties des Spielers: =====
+
+// #define P_GUILD                     "guild"
+#define P_DEFAULT_GUILD             "default_guild"
+#define P_DEFENDERS                 "defenders"
+#define P_GUILD_LEVEL               "guild_level"
+#define P_GUILD_RATING              "guild_rating"
+#define P_GUILD_TITLE               "guild_title"
+#define P_SUBGUILD_TITLE            "subguild_title"
+#define P_VISIBLE_SUBGUILD_TITLE    "visible_subguild_title"
+#define P_MAGIC_RESISTANCE_OFFSET   "mag_res_offset"
+#define P_NEWSKILLS                 "newskills"
+#define P_NEXT_SPELL_TIME           "next_spell"
+#define P_PREPARED_SPELL            "prepared_spell"
+#define P_TMP_ATTACK_HOOK           "attack_hook"
+#define P_TMP_ATTACK_MOD            "attack_mod"
+#define P_TMP_DEFEND_HOOK           "defend_hook"
+#define P_TMP_DIE_HOOK              "die_hook"
+#define P_TMP_MOVE_HOOK             "move_hook"
+#define P_WEAPON_TEACHER            "weapon_teacher"
+#define P_SKILLSVERSION             "p_lib_skillsversion"
+
+// ===== Standard-Skills: =====
+
+#define FIGHT(x)                    ("Fight_"+x)
+#define SHOOT(x)                    ("Shoot_"+x)
+#define SK_BOOZE                    "Booze"
+#define SK_CARRY                    "CarrySkill"
+#define SK_CASTING                  "Cast"
+#define SK_DEFEND_OTHER             "DefendOther"
+#define SK_FIGHT                    "Fight"
+#define SK_INFORM_DEFEND            "InformDefend"
+#define SK_MAGIC_ATTACK             "MagicAttack"
+#define SK_MAGIC_DEFENSE            "MagicDefense"
+#define SK_NIGHTVISION              "Nightvision"
+#define SK_SHOOT                    "Shoot"
+#define SK_SPELL_DEFEND             "SpellDefend"
+#define SK_SWORDFIGHTING            FIGHT(WT_SWORD)
+#define SK_TWOHANDED                "Bihand"
+#define SK_WEAPONLESS               FIGHT(WT_HANDS)
+
+// ===== Skill Infos: =====
+
+#define FACTOR(x)                   ("fac_"+x)
+#define OFFSET(x)                   ("off_"+x)
+
+#define SI_SKILLFUNC                "si_func"
+  // Funktion, die aufgerufen wird
+
+#define SI_CLOSURE                  "si_closure"
+  // Nur fuer den internen Gebrauch
+  // (Geschwindigkeitsoptimierung)
+
+#define SI_SPELLBOOK                "si_spellbook"
+  // Spellbook, in dem der Spell
+  // steht
+
+#define SI_SPELLCOST                "si_cost"
+  // Kosten des Spells
+
+#define SI_TIME_MSG                 "time_msg"
+  // Die Meldung wird anstelle von
+  // "Du bist noch zu erschoepft
+  //  von Deinem letzten Spruch."
+  // ausgegeben.
+
+#define SI_SP_LOW_MSG               "sp_low_msg"
+  // Die Meldung wird anstelle von
+  // "Du hast zu wenig Zauberpunkte
+  //  fuer diesen Spruch."
+  // ausgegeben.
+
+#define SI_PREPARE_MSG              "si_prepare_msg"
+#define SI_PREPARE_ABORT_MSG        "si_prepare_abort_msg"
+#define SI_PREPARE_BUSY_MSG         "si_prepare_busy_msg"
+  // Meldungen fuer Spruch-
+  // vorbereitung, Abbruch und
+  // schon beschaeftigt
+
+#define SI_NOMAGIC                  "si_nomagic"
+  // Prozentwert, mit dem P_NOMAGIC
+  // umgangen werden kann
+
+#define SI_NOMAGIC_MSG              "si_nomagic_msg"
+  // Meldung, die Anstelle von
+  // "Dein Zauberspruch verpufft
+  //  im Nichts" ausgegeben werden
+  // soll.
+
+#define SI_SPELLFATIGUE             "si_fatigue"
+  // Erschoepfung - Zeit, in der
+  // keine weiteren Spells
+  // aufgerufen werden koennen
+#define SI_X_SPELLFATIGUE           "std:si:xfatigue"
+
+#define SI_SKILLLEARN               "si_learn"
+  // Lerngeschwindigkeit in 0.01%
+  // pro A_INT/2
+
+#define SI_LEARN_ATTRIBUTE          "si_learn_attribute"
+  // Wenn man nicht will, dass man in Abhaengigkeit von
+  // INT lernt
+
+#define SI_NO_LEARN                 "si_no_learn"
+  // Wenn man (temporaer!) nicht will, dass dieser Skill gelernt wird.
+  // Muss von den Spellbooks beachtet werden.
+  // Sollte niemals im Spieler abgespeichert werden. Oder permanent in
+  // Gilde/Spellbook gesetzt sein. Sondern im Laufe einesr Nutzung in der jew.
+  // Kopie von sinfo gesetzt werden, die gerade genutzt wird.
+
+#define SI_SKILLABILITY             "si_abil"
+  // Faehigkeit, diesen Spruch zu
+  // benutzen
+
+#define SI_SKILLARG                 "si_arg"
+  // Argumente, die uebergeben
+  // wurden
+
+#define SI_SKILLRESTR_USE           "si_restr_u"
+  // Beschraenkungen beim Gebrauch
+
+#define SI_SKILLRESTR_LEARN         "si_restr_l"
+  // Beschraenkungen beim Lernen
+
+#define SI_SKILLINFO                "si_info"
+  // Kurzer Informationstext
+
+#define SI_SKILLINFO_LONG           "si_info_l"
+  // Langer Informationstext
+
+#define SI_SKILLDAMAGE              "si_damage"
+  // Schaden
+
+#define SI_SKILLDAMAGE_BY_ROW       "si_damage_by_row"
+  // Schaden durch Teamreihen
+
+#define SI_SKILLDAMAGE_TYPE         "si_damtyp"
+  // Art des Schadens
+
+#define SI_SKILLDAMAGE_MSG          "si_dammsg"
+  // Meldung die anstelle einer
+  // Waffe kommen soll
+
+#define SI_SKILLDAMAGE_MSG2         "si_dammsg2"
+  // dito fuer den Text fuer
+  // andere im Raum
+
+#define SI_SPELL                    "si_spell"
+  // Spell, mit dem angegriffen
+  // wurde
+
+#define SI_COLLATERAL_DAMAGE        "si_collateral_damage"
+  // Schadensanteil bei Flaechen-
+  // spells, den Freunde abbekommen,
+  // in Prozent
+
+#define SI_NUMBER_ENEMIES           "si_num_enemies"
+  // Anzahl Feinde bei
+  // Flaechenspruch
+
+#define SI_NUMBER_FRIENDS           "si_num_friends"
+  // Anzahl Freunde bei
+  // Flaechenspruch
+
+#define SI_DISTANCE                 "si_distance"
+  // Entfernung
+
+#define SI_WIDTH                    "si_width"
+  // Breite des Flaechenangriffs
+
+#define SI_DEPTH                    "si_depth"
+  // Tiefe des Flaechenangriffs
+
+#define SI_INHERIT                  "si_inherit"
+  // Skill, von dem etwas
+  // uebernommen werden soll
+
+#define SI_DIFFICULTY               "si_difficulty"
+  // Wert, der die Obergrenze der
+  // Faehigkeit abgrenzt
+
+#define SI_LASTLIGHT                "si_lastlight"
+  // Fuer Nachtsicht: Wann hat
+  // der Spieler zum letzten mal
+  // Licht gesehen.
+
+#define SI_SKILLHEAL                "si_heal"
+  // Heilung
+
+#define SI_USR                      "si_usr"
+  // selbst definierte Infos
+
+#define SI_TESTFLAG                 "si_testflag"
+  // Testflag
+
+#define SI_GUILD                    "si_guild"
+  // Gilde, falls Auswahl aus
+  // mehreren moeglich
+
+#define SI_ENEMY                    "si_enemy"
+  // Feind bei Kampfspruechen
+
+#define SI_FRIEND                   "si_friend"
+  // Der zu verteidigende Freund
+  // bei DefendOther und
+  // InformDefend
+
+#define SI_MAGIC_TYPE               "si_magic_type"
+  // Von was fuer einer Art ist
+  // die Magie (s.u.)
+
+#define SI_PREPARE_TIME             "si_prepare_time"
+  // Zeit die zur Vorbereitung
+  // benoetigt wird.
+
+#define SI_ATTACK_BUSY_MSG          "si_attack_busy_msg"
+  // Meldung, wenn der Spieler zu
+  // beschaeftigt ist
+
+#define SI_NO_ATTACK_BUSY           "si_no_attack_busy"
+  // Wenn der Spell nicht als
+  // Taetigkeit zaehlen/gezaehlt
+  // werden soll, kann man hier
+  // NO_ATTACK_BUSY[_SET|_QUERY]
+  // (s.u.) setzen
+
+#define SI_ATTACK_BUSY_AMOUNT       "si_attack_busy_amount"
+  // Der Spell hat ein gewisses
+  // AttackBusy, aber das weicht
+  // vom Standardwert 1 ab. Der
+  // Wert kann hier gesetzt
+  // werden.
+
+#define SI_LAST_USE                 "si_last_use"
+  // Letzte benutzung eines
+  // Skills (fuer Rassenskills)
+
+#define SI_LEARN_PROB               "si_learn_prob"
+  // Lernwahrscheinlichkeit
+  // (fuer Rassenskills)
+
+#define SI_SKILLDURATION            "si_duration"
+  // Wie soll wird der Skill anhalten?
+  // (fuer Spells mit einer Dauer)
+
+// ===== Parameter fuer Spell-Mapping: =====
+
+#define SP_NAME                     "sp_name"
+  // Name des Spells
+
+#define SP_SHOW_DAMAGE              "sp_showdamage"
+  // Treffermeldung soll gezeigt
+  // werden.
+
+#define SP_REDUCE_ARMOUR            "sp_reduce_ac"
+  // AC soll Typabhaengig
+  // reduziert werden.
+
+#define SP_PHYSICAL_ATTACK          "sp_physical"
+  // Koerperlicher Angriff
+
+#define SP_RECURSIVE                "sp_recursive"
+  // Rekursionen
+
+#define SP_NO_ENEMY                 "no_enemy"
+  // Ist kein richtiger Angriff
+
+#define SP_NO_ACTIVE_DEFENSE        "sp_no_active_defense"
+  // Es ist keine aktive Abwehr erwuenscht
+
+#define SP_GLOBAL_ATTACK            "sp_global_attack"
+  // Ist Flaechenangriff
+
+// ===== Skill Restrictions: =====
+
+#define SR_FUN                      "restr_fun"
+  // Funktion, die weitere
+  // Einschraenkungen prueft
+
+#define SR_EXCLUDE_RACE             "restr_race_ex"
+  // Ausgeschlossene Rassen
+
+#define SR_INCLUDE_RACE             "restr_race_in"
+  // Eingeschlossene Rassen
+
+#define SR_EXCLUDE_GUILD            "restr_guild_ex"
+  // Ausgeschlossene Gilden
+
+#define SR_INCLUDE_GUILD            "restr_guild_in"
+  // Eingeschlossene Gilden
+
+#define SR_GOOD                     "restr_good"
+  // Align <
+
+#define SR_BAD                      "restr_bad"
+  // Align >
+
+#define SR_FREE_HANDS               "restr_hands"
+  // Benoetigte freie Haende
+
+#define SR_SEER                     "restr_seer"
+  // Muss Seher sein
+
+#define SR_MIN_SIZE                 "min_size"
+  // Mindestgroesse
+
+#define SR_MAX_SIZE                 "max_size"
+  // Maximalgroesse
+
+#define SR_PROP                     "restr_properties"
+  // Werte beliebiger Properties
+
+#define SR_QUEST                    "restr_quests"
+  // Quest bestanden
+
+#define SR_MINIQUEST                "restr_miniquests"
+  // Miniquest geloest
+
+// ===== Skill Modifier: =====
+
+#define SM_RACE                     "modify_race"
+  // Rassenspezifische
+  // Besonderheiten
+
+// ===== Fuer das Attack-Busy-Handling: =====
+
+#define NO_ATTACK_BUSY_SET          0x001
+#define NO_ATTACK_BUSY_QUERY        0x002
+#define NO_ATTACK_BUSY              0x003
+
+// ===== Magie-Arten: =====
+
+#define MT_ANGRIFF                  "mt_angriff"
+#define MT_BEHERRSCHUNG             "mt_beherrschung"
+#define MT_BESCHWOERUNG             "mt_beschwoerung"
+#define MT_BEWEGUNG                 "mt_bewegung"
+#define MT_CREATION                 "mt_creation"
+#define MT_HEILUNG                  "mt_heilung"
+#define MT_HELLSICHT                "mt_hellsicht"
+#define MT_ILLUSION                 "mt_illusion"
+#define MT_PSYCHO                   "mt_psycho"
+#define MT_SAKRAL                   "mt_sakral"
+#define MT_SCHUTZ                   "mt_schutz"
+#define MT_VERWANDLUNG              "mt_verwandlung"
+
+#define MT_MISC                     "mt_misc"
+
+// ===== Defines fuer FindGroup() =====
+
+#define FG_ENEMIES                  -1
+#define FG_FRIENDS                  1
+#define FG_ALL                      0
+
+// ======== OBSOLETE Properties / Defines =============
+// Prop wird nicht mehr genutzt, ist aber noch fuer /std/player/base.c noetig,
+// um den SAVE-Status in den Spieler-Props zu loeschen.
+#define P_GUILD_PREVENTS_RACESKILL  "guild_prevents_raceskill"
+
+#endif
diff --git a/sys/news.h b/sys/news.h
new file mode 100644
index 0000000..38085a1
--- /dev/null
+++ b/sys/news.h
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// news.h -- Definitionen fuer die Zeitung
+//
+// $Id: news.h 8809 2014-05-08 19:52:48Z Zesstra $
+
+#define G_NAME 0
+#define G_OWNER 1
+#define G_SAVEFILE 2
+#define G_EXPIRE 3
+#define G_DELETERS 4 
+#define G_WRITERS 5
+#define G_READERS 6
+#define G_DLEVEL 7
+#define G_WLEVEL 8
+#define G_RLEVEL 9
+#define G_MAX_MSG 10
+#ifdef TESTING
+#define G_MAX_NUM 11
+#define G_MESSAGES 12
+#else
+#define G_MESSAGES 11
+#endif
+
+#define M_BOARD 0
+#define M_WRITER 1
+#define M_ID 2
+#define M_TIME 3
+#define M_TITLE 4
+#define M_MESSAGE 5
+
+static string user_euid();
+static void save_group_list();
+static void save_group(string grp,mixed group);
+static mixed load_group(string name);
+               
\ No newline at end of file
diff --git a/sys/notify_fail.h b/sys/notify_fail.h
new file mode 100644
index 0000000..97d1786
--- /dev/null
+++ b/sys/notify_fail.h
@@ -0,0 +1,20 @@
+// MorgenGrauen MUDlib
+//
+// notify_failh -- sym. Konstanten fuer notify_fail()
+//
+// $Id: defines.h 6415 2007-08-10 19:24:04Z Zesstra $
+
+#ifndef _NOTIFY_FAIL_H_
+#define _NOTIFY_FAIL_H_
+
+// Prioritaeten / 'Nice levels' von notify_fail()s:
+// Wie 'nice' bin ich? Je groessere Zahl/Priorität, desto wenig lieb. ;-)
+#define NF_NL_NONE   -1   //max 'niceness', wird von allem ueberschrieben
+#define NF_NL_OWN    100  //eigenes Objekt (soul verben) ueberschreiben kaum was
+#define NF_NL_THING  100000
+#define NF_NL_ROOM   1000000 // Raeume ueberschreiben sonstigen Krams
+#define NF_NL_LIVING 10000000 // Lebewesen ueberschreiben auch Raeume
+#define NF_NL_MAX    __INT_MAX__ //hoechste Prioritaet, ueberschreibt alles
+
+#endif /* _NOTIFY_FAIL_H_ */
+
diff --git a/sys/npc.h b/sys/npc.h
new file mode 100644
index 0000000..8565930
--- /dev/null
+++ b/sys/npc.h
@@ -0,0 +1,60 @@
+// MorgenGrauen MUDlib
+//
+// npc.h -- Definitonen fuer NPCs
+//
+// $Id: npc.h 6709 2008-02-02 22:14:08Z Zesstra $
+
+#ifndef _NPC_H_
+#define _NPC_H_
+
+#define DEFAULT_INFO "\ndefault info"
+#define DEFAULT_NOINFO "\ndefault noinfo"
+#define P_PRE_INFO           "npc_pre_info"
+#define P_NPC_INFO           "npc_info"
+
+#define P_NPC                "is_npc"
+
+#define P_HB                 "hb"
+#define P_INFO               "info"
+
+#define P_AGGRESSIVE         "aggressive"
+
+#define P_SPELLS             "spells"
+#define P_SPELLRATE          "spellrate"
+
+#define P_CHATS              "chats"
+#define P_CHAT_CHANCE        "chat_chance"
+#define P_ACHATS             "achats"
+#define P_ACHAT_CHANCE       "achat_chance"
+
+#define P_REJECT             "reject"
+
+#define P_DEFAULT_INFO       "default_info" // Standardantwort fuer Fragen
+#define P_LOG_INFO           "log_info"
+
+#define CLONE_NEW   0x10000000  /* VERALTET! */
+#define CLONE_WEAR  0x20000000  /* anziehen */
+#define CLONE_WIELD 0x40000000  /* zuecken */
+#define CLONE_NO_CHECK 0x08000000  /* zuecken oder anziehen ohne checks */
+
+#define NPC_NEEDS_ITEM_INIT "npc:need item init"
+
+#endif
+
+#ifdef NEED_PROTOTYPES
+#ifndef _NPC_H_PROTOTYPES_
+#define _NPC_H_PROTOTYPES_
+// aus npc/info.c
+public varargs void    AddInfo(mixed key, mixed info, string indent, 
+                               mixed silent, mixed casebased);
+public varargs void    AddSpecialInfo(mixed keys, string functionname, 
+                          string indent, mixed silent, mixed casebased );
+public int             frage(string str);
+public int             do_frage(string text);
+public void            RemoveInfo( string key );
+public varargs mixed   GetInfo(string str);
+// aus npc/items.c
+public varargs object  AddItem(mixed filename, int refresh, mixed props);
+#endif // _NPC_H_PROTOTYPES_
+#endif // NEED_PROTOTYPES
+
diff --git a/sys/object_info.h b/sys/object_info.h
new file mode 100644
index 0000000..2578747
--- /dev/null
+++ b/sys/object_info.h
@@ -0,0 +1,70 @@
+#ifndef LPC_OBJECT_INFO_H_
+#define LPC_OBJECT_INFO_H_
+
+/* Definition of argument values for object_info()
+ */
+
+#include "configuration.h"
+
+/* Object flags */
+#define OI_ONCE_INTERACTIVE             -1
+#define OI_RESET_STATE                  -2
+#define OI_WILL_CLEAN_UP                -3
+#define OI_LAMBDA_REFERENCED            -4
+#define OI_REPLACED                     -5
+
+/* Program flags */
+#define OI_NO_INHERIT                   -10
+#define OI_NO_CLONE                     -11
+#define OI_NO_SHADOW                    -12
+#define OI_SHARE_VARIABLES              -13
+
+/* Swapping */
+#define OI_SWAPPED                      -20
+#define OI_PROG_SWAPPED                 -21
+#define OI_VAR_SWAPPED                  -22
+#define OI_SWAP_NUM                     -23
+
+/* Timing */
+#define OI_NEXT_RESET_TIME              -30
+#define OI_NEXT_CLEANUP_TIME            -31
+#define OI_LAST_REF_TIME                -32
+
+/* Object list */
+#define OI_OBJECT_NEXT                  -40
+#define OI_OBJECT_PREV                  -41
+#define OI_OBJECT_POS                   -42
+
+/* Shadows */
+#define OI_SHADOW_NEXT                  -50
+#define OI_SHADOW_PREV                  -51
+#define OI_SHADOW_ALL                   -52
+
+/* Statistics about the object */
+#define OI_OBJECT_REFS                  -60
+#define OI_TICKS                        -61
+#define OI_GIGATICKS                    -62
+#define OI_DATA_SIZE                    -63
+#define OI_DATA_SIZE_TOTAL              -64
+
+/* Statistics about the program */
+#define OI_PROG_REFS                    -70
+
+#define OI_NUM_FUNCTIONS                -71
+#define OI_NUM_VARIABLES                -72
+#define OI_NUM_STRINGS                  -73
+#define OI_NUM_INHERITED                -74
+#define OI_NUM_INCLUDED                 -75
+
+#define OI_SIZE_FUNCTIONS               -76
+#define OI_SIZE_VARIABLES               -77
+#define OI_SIZE_STRINGS                 -78
+#define OI_SIZE_STRINGS_DATA            -79
+#define OI_SIZE_STRINGS_DATA_TOTAL      -80
+#define OI_SIZE_INHERITED               -81
+#define OI_SIZE_INCLUDED                -82
+
+#define OI_PROG_SIZE                    -83
+#define OI_PROG_SIZE_TOTAL              -84
+
+#endif /* LPC_OBJECT_INFO_H_ */
diff --git a/sys/objectinfo.h b/sys/objectinfo.h
new file mode 100644
index 0000000..de2c33b
--- /dev/null
+++ b/sys/objectinfo.h
@@ -0,0 +1,82 @@
+#ifndef LPC_OBJECTINFO_H_
+#define LPC_OBJECTINFO_H_
+
+/* Definition of argument values for object_info() and
+ * of the indices in the corresponding result arrays.
+ */
+
+/* Possible types of information requested from object_info()
+ */
+#define OINFO_BASIC     0
+#define OINFO_POSITION  1
+#define OINFO_MEMORY    2
+
+
+/* Indices in the array resulting from OINFO_BASIC
+ */
+
+#define OIB_HEART_BEAT          0
+#define OIB_IS_WIZARD           1
+#define OIB_ENABLE_COMMANDS     2
+#define OIB_CLONE               3
+#define OIB_DESTRUCTED          4
+#define OIB_SWAPPED             5
+#define OIB_ONCE_INTERACTIVE    6
+#define OIB_RESET_STATE         7
+#define OIB_WILL_CLEAN_UP       8
+#define OIB_LAMBDA_REFERENCED   9
+#define OIB_SHADOW             10
+#define OIB_REPLACED           11
+#define OIB_NEXT_RESET         12
+#define OIB_TIME_OF_REF        13
+#define OIB_REF                14
+#define OIB_GIGATICKS          15
+#define OIB_TICKS              16
+#define OIB_SWAP_NUM           17
+#define OIB_PROG_SWAPPED       18
+#define OIB_VAR_SWAPPED        19
+#define OIB_NAME               20
+#define OIB_LOAD_NAME          21
+#define OIB_NEXT_ALL           22
+#define OIB_PREV_ALL           23
+#define OIB_NEXT_CLEANUP       24
+
+#define OIB_MAX 25  /* Number of OIB_ result elements */
+
+
+/* Indices in the array resulting from OINFO_POSITION
+ */
+#define OIP_NEXT  0
+#define OIP_PREV  1
+#define OIP_POS   2
+
+#define OIP_MAX 3  /* Number of OIP_ result elements */
+
+
+/* Indices in the array resulting from OINFO_MEMORY
+ */
+#define OIM_REF                  0
+#define OIM_NAME                 1
+#define OIM_PROG_SIZE            2
+#define OIM_NUM_FUNCTIONS        3
+#define OIM_SIZE_FUNCTIONS       4
+#define OIM_NUM_VARIABLES        5
+#define OIM_SIZE_VARIABLES       6
+#define OIM_NUM_STRINGS          7
+#define OIM_SIZE_STRINGS         8
+#define OIM_SIZE_STRINGS_DATA    9
+#define OIM_SIZE_STRINGS_TOTAL  10
+#define OIM_NUM_INHERITED       11
+#define OIM_SIZE_INHERITED      12
+#define OIM_TOTAL_SIZE          13
+#define OIM_DATA_SIZE           14
+#define OIM_TOTAL_DATA_SIZE     15
+#define OIM_NO_INHERIT          16
+#define OIM_NO_CLONE            17
+#define OIM_NO_SHADOW           18
+#define OIM_NUM_INCLUDES        19
+#define OIM_SHARE_VARIABLES     20
+
+#define OIM_MAX 21  /* Number of OIM_ result elements */
+
+#endif /* LPC_OBJECTINFO_H_ */
diff --git a/sys/oldprops b/sys/oldprops
new file mode 100644
index 0000000..5d202a9
--- /dev/null
+++ b/sys/oldprops
@@ -0,0 +1,4 @@
+
+// werden alle beide in keinem .c File benutzt...
+P_MAX_WC
+P_CONTENT
diff --git a/sys/pager.h b/sys/pager.h
new file mode 100644
index 0000000..fef652a
--- /dev/null
+++ b/sys/pager.h
@@ -0,0 +1,63 @@
+// MorgenGrauen MUDlib
+//
+// pager.h -- generic pager header
+//
+// $Id: pager.h 6142 2007-01-31 20:34:39Z Zesstra $
+
+#ifndef __PAGER_H__
+#define __PAGER_H__
+
+// select language
+#define GERMAN
+//#define ENGLISH
+
+// select optimization
+#define O_SPEED			// speed optimization
+#define O_SPEED_UP		// additional speed up 
+				// (works only with O_SPEED)
+#define MAX_LINE_READ	625	// max. lines to be read by
+				// read_file() 50k/80c
+
+// the pager info structure
+#define TEXT    0		// the filename or string
+#define FILE    1		// 1 == file, 0 = string
+#define CURL    2		// current line
+#define MAXL    3		// last line
+#define PAGE    4		// page length
+#define REGX	5		// last regular expression
+#define CTRL	6		// function to give control
+#define CARG	7		// array of args to CTRL
+#define FLAG	8		// special flags
+# define E_PROMPT 1             //   prompt at end of file
+# define E_CAT    2             //   do not prompt for more
+# define E_ABS    4             //   show line numbers at prompt
+#ifdef O_SPEED
+# define JUNK		9	// junk index
+# define JUNKSIZE	10	// size of string junks
+#endif
+
+#ifdef GERMAN
+# define MSG_PROMPT	"--mehr--"
+# define MSG_TOP	"ANFANG "
+# define MSG_BOTTOM	"ENDE "
+# define MSG_OOPS	"Ohh, da lief etwas schief!"
+#else
+# define MSG_PROMPT	"--more--"
+# define MSG_TOP	"TOP "
+# define MSG_BOTTOM	"BOTTOM "
+# define MSG_OOPS	"Oops, something went wrong!"
+#endif
+
+#include <player/base.h>
+
+#define PAGELENGTH	(this_interactive() ? \
+			   this_interactive()->QueryProp(P_SCREENSIZE) : \
+			   (interactive(this_player()) ? \
+			     this_player()->QueryProp(P_SCREENSIZE) : 20))
+
+// old_explode() must create an array that implode()'s to the same string
+#define Explode(s,x)	(explode(s,x))
+
+#endif // __PAGER_H__
+
+
diff --git a/sys/pathd.h b/sys/pathd.h
new file mode 100644
index 0000000..c75fceb
--- /dev/null
+++ b/sys/pathd.h
@@ -0,0 +1,13 @@
+// MorgenGrauen MUDlib
+//
+// pathd.h - sym. Konstanten fuer den PATHD
+//
+// $Id: pathd.h 6415 2007-08-10 19:24:04Z Zesstra $
+
+#ifndef _PATHD_H_
+#define _PATHD_H_
+
+#define PATHD "/p/daemon/pathd"
+
+#endif /* _PATHD_H_ */
+
diff --git a/sys/pgsql.h b/sys/pgsql.h
new file mode 100644
index 0000000..fa7cdf6
--- /dev/null
+++ b/sys/pgsql.h
@@ -0,0 +1,23 @@
+#ifndef _PGSQL_H
+#define _PGSQL_H
+
+/* Definitions for the PostgreSQL efuns */
+
+#define PGRES_EMPTY_QUERY    0  /* Unimplemented */
+#define PGRES_COMMAND_OK     1
+#define PGRES_TUPLES_OK      2
+#define PGRES_COPY_OUT       3  /* Unimplemented */
+#define PGRES_COPY_IN        4  /* Unimplemented */
+#define PGRES_BAD_RESPONSE   5
+#define PGRES_NONFATAL_ERROR 6
+#define PGRES_FATAL_ERROR    7
+#define PGRES_NOTICE        99
+
+#define PGCONN_SUCCESS 100
+#define PGCONN_FAILED  101
+#define PGCONN_ABORTED 102
+
+#define PG_RESULT_ARRAY 0
+#define PG_RESULT_MAP   1
+
+#endif
diff --git a/sys/player.h b/sys/player.h
new file mode 100644
index 0000000..74b40d1
--- /dev/null
+++ b/sys/player.h
@@ -0,0 +1,58 @@
+// MorgenGrauen MUDlib
+//
+// player.h -- player object defines
+//
+// $Id: player.h 7386 2010-01-22 14:59:05Z Zesstra $
+
+#ifndef _PLAYER_H_
+#define _PLAYER_H_
+
+#ifdef NEED_PROTOTYPES
+#undef NEED_PROTOTYPES                                  // we need only the properties here!!
+#define PLAYER_TMP_NEED_PROTOTYPES
+#endif
+
+#include "/sys/player/base.h"
+#include "/sys/player/description.h"
+#include "/sys/player/moving.h"
+#include "/sys/player/filesys.h"
+#include "/sys/player/potion.h"
+#include "/sys/player/quest.h"
+#include "/sys/player/skills.h"
+#include "/sys/player/viewcmd.h"
+#include "/sys/player/comm.h"
+#include "/sys/player/fao.h"
+#include <player/user_filter.h>
+#include <daemon.h>
+
+#ifdef PLAYER_TMP_NEED_PROTOTYPES
+#undef PLAYER_TMP_NEED_PROTOTYPES
+#define NEED_PROTOTYPES
+#endif
+
+#define P_LAST_COMMAND_ENV      "last_command_env"
+#define P_HISTMIN               "histmin"
+#define P_SHOW_ALIAS_PROCESSING "show_alias_processing"
+#define P_DEFAULT_NOTIFY_FAIL   "default_notify_fail"
+#define P_NETDEAD_INFO          "netdead_info"
+#define P_NETDEAD_ENV           "netdead_env"
+#define P_IP_NAME               "ip_name"
+#define P_LAST_KILLER           "last_killer"
+#define P_ACTUAL_NOTIFY_FAIL    "actual_notify_fail"
+#define P_LEP                   "lep"
+#define P_NEWBIE_GUIDE          "newbie_guide"
+
+#define DeclareAutoload(name,param) \
+  mlfp_create(({P_AUTOLOAD,name}),param)
+
+#define UndeclareAutoload(name) \
+  mlfp_remove(({P_AUTOLOAD}),name)
+
+#define QueryAutoload(name) \
+  mlfp_query(({P_AUTOLOAD,name}))
+
+#ifdef NEED_PROTOTYPES
+varargs string _unparsed_args(int level);
+#endif
+
+#endif
diff --git a/sys/player/base.h b/sys/player/base.h
new file mode 100644
index 0000000..3f28cb4
--- /dev/null
+++ b/sys/player/base.h
@@ -0,0 +1,124 @@
+// MorgenGrauen MUDlib
+//
+// player/base.h -- player base header
+//
+// $Id: base.h 8487 2013-05-21 19:15:52Z Zesstra $
+
+#ifndef __PLAYER_BASE_H__
+#define __PLAYER_BASE_H__
+
+// da dieses File vom Master inkludiert wird und dann die Standard-Include-
+// verzeichnisse noch nicht gesetzt sind, darf dieses File auch nur die abs.
+// Namen verwenden. *seufz*
+#include "/sys/player/can.h"
+#include "/sys/player/telnetneg.h"
+
+#define HAUSVERWALTER   "/d/seher/haeuser/hausverwalter"
+
+// properties
+
+#define P_KILLS              "playerkills"
+
+#define P_ZAP_MSG            "zap_msg"
+#define P_AWAY               "away"
+#define P_NO_TOPLIST         "no_toplist"
+#define P_CALLED_FROM_IP     "called_from_ip"
+#define P_MARRIED            "married"
+#define P_SIBLINGS           "siblings"
+#define P_RACE_DESCRIPTION   "racedescr"
+#define P_RACESTRING         "racestring"
+
+#define P_WAITFOR            "waitfor"
+#define P_WAITFOR_REASON     "waitfor_reason"
+#define P_WAITFOR_FLAGS      "waitfor_flags"
+#define P_VISUALBELL         "visualbell"
+#define P_LOCALCMDS          "localcmds"
+#define P_CLOCKMSG           "clockmsg"
+#define P_TIMEZONE           "timezone"
+#define P_SHOWEMAIL          "showemail"
+
+#define P_LAST_LOGIN         "last_login"
+#define P_LAST_LOGOUT        "last_logout"
+#define P_DAILY_PLAYTIME     "daily_playtime"
+#define P_IGNORE             "ignore"
+#define P_SHOW_EXITS         "show_exits"
+#define P_WANTS_TO_LEARN     "wants_to_learn"
+#define P_AUTOLOADOBJ        "autoloadobj"
+#define P_AUTOLOAD           "autoload"
+#define P_MAILADDR           "mailaddr"
+#define P_HOMEPAGE           "homepage"
+#define P_ICQ                "icq"
+#define P_MESSENGER          "messenger"
+#define P_LOCATION           "location"
+
+#define P_FOLLOW_SILENT      "follow_silent"
+
+#ifndef P_INVIS
+#define P_INVIS              "invis"
+#endif
+
+#define P_SECOND             "second"
+#define P_SECOND_MARK        "second_mark"
+#define P_SECOND_LIST        "second_list"
+#define P_MUD_NEWBIE         "_lib_mud_newbie"  // not played a mud before?
+
+#define P_TESTPLAYER         "testplayer"
+#define P_ALLOWED_SHADOW     "allowed_shadow"
+
+#define P_START_HOME         "start_home"
+#define P_PRAY_ROOM          "_lib_p_prayroom"
+
+#define P_SHELL_VERSION      "shell_version"
+
+#define P_CMSG               "clonemsg"
+#define P_DMSG               "destmsg"
+#define P_CLONE_MSG          "clone_msg"
+#define P_DESTRUCT_MSG       "destruct_msg"
+
+#define P_CARRIED_VALUE      "carried"
+
+#define P_PROMPT             "prompt"
+
+#define P_SCREENSIZE         "screensize"
+#define P_MORE_FLAGS         "more_flags"
+
+#define P_NO_ASCII_ART       "no_ascii_art"
+
+#define P_LAST_QUIT          "last_quit"
+
+#define P_READ_NEWS          "read_news"
+
+#define P_NEEDED_QP          "needed_qp"
+
+//TODO: Remove - Property is not needed any more.
+#define P_TELNET_KEEP_ALIVE  "send_telnet_keep_alive"
+
+#endif // __PLAYER_BASE_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_BASE_H_PROTO__
+#define __PLAYER_BASE_H_PROTO__
+
+// prototypes
+
+void smart_log(string myname, string str);
+int QueryGuest();
+int invis(string inform);
+int vis(string inform);
+
+varargs static void stop_heart_beats(mixed obs);
+static void restart_heart_beats();
+
+nomask int query_hc_play();
+varargs nomask void set_hc_play(string str, int val);
+string SetDefaultHome(string str);
+protected string SetDefaultPrayRoom(string str);
+
+void save_me(mixed value_items);
+nomask mixed query_real_name();
+protected void call_notify_player_change(int rein);
+
+#endif // __PLAYER_BASE_H_PROTO__
+
+#endif// NEED_PROTOYPES
diff --git a/sys/player/can.h b/sys/player/can.h
new file mode 100644
index 0000000..dcce693
--- /dev/null
+++ b/sys/player/can.h
@@ -0,0 +1,34 @@
+// MorgenGrauen MUDlib
+//
+// player/can.h -- various abitlities
+//
+// $Id: can.h 7924 2011-09-26 10:02:26Z Zesstra $
+
+#ifndef __CAN_H__
+#define __CAN_H__
+
+// properties
+#define P_CAN_FLAGS          "can_flags"
+
+// bitmasks
+#define CAN_EMOTE  0x01
+#define CAN_ECHO   0x02
+#define CAN_REMOTE 0x04
+#define CAN_PRESAY 0x08
+#define CAN_REPORT_SP     0x10     // Statusupdates fuer KP
+#define CAN_REPORT_POISON 0x20     // Statusupdates fuer Gift
+#define CAN_REPORT_WIMPY  0x40     // Statusupdates fuer Vorsicht
+#define CAN_REPORT_WIMPY_DIR 0x80  // Statusupdates fuer Vorsichtrichtung
+
+#endif // __CAN_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __CAN_H_PROTO__
+#define __CAN_H_PROTO__
+
+// no prototypes
+
+#endif // __CAN_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/player/combat.h b/sys/player/combat.h
new file mode 100644
index 0000000..01ab2fa
--- /dev/null
+++ b/sys/player/combat.h
@@ -0,0 +1,16 @@
+// MorgenGrauen MUDlib
+//
+// player/combat.h -- prototypes for player specific issues of combat
+//
+// $Id: comm.h,v 3.3 2000/02/09 15:56:25 Padreic Exp $
+
+#ifndef __PLAYER_COMBAT_H__
+#define __PLAYER_COMBAT_H__
+
+public int QueryPlAttacked(object pl);
+
+public int RegisterHelperNPC(object npc, int flags);
+public int UnregisterHelperNPC(object npc);
+
+#endif // __PLAYER_COMBAT_H__
+
diff --git a/sys/player/comm.h b/sys/player/comm.h
new file mode 100644
index 0000000..803b319
--- /dev/null
+++ b/sys/player/comm.h
@@ -0,0 +1,85 @@
+// MorgenGrauen MUDlib
+//
+// player/comm.h -- communication module properties
+//
+// $Id: comm.h,v 3.3 2000/02/09 15:56:25 Padreic Exp $
+
+#ifndef __PLAYER_COMM_H__
+#define __PLAYER_COMM_H__
+
+// living/comm.h brauchen wir auch.
+#include <living/comm.h>
+
+// properties
+
+#define P_INTERMUD           "intermud" // Does she listen to it ?
+#define P_BUFFER             "buffer"   // ignore-, tell-buffer
+#define P_DEAF               "deaf"     // Taubheit
+#define P_PERM_STRING        "perm_string" // Fuer Sprachflueche
+#define P_MESSAGE_BEEP       "message_beep" // wenn gesetzt 
+                                            // Anzahl der Sekunden dazwischen
+#define P_MESSAGE_PREPEND  "message_prepend"
+// Will der Magier MT_DEBUG sehen?
+#define P_WIZ_DEBUG          "std_p_wizdebug"
+
+#define MESSAGE_BEEP "\007"
+
+// definitions for Message()
+
+#define MSGFLAG_CHANNEL 0x0001	// messages from channeld.c
+#define MSGFLAG_CHIST	0x0002
+#define MSGFLAG_SOUL	0x0004	// messages from soul.c
+#define MSGFLAG_REMOTE	0x0008	// remote emotes
+#define MSGFLAG_SHOUT	0x0010	// communication commands
+#define MSGFLAG_TELL	0x0020
+#define MSGFLAG_SAY	0x0040
+#define MSGFLAG_WHISPER 0x0080
+#define MSGFLAG_MECHO	0x0100
+#define MSGFLAG_RTELL	0x0200	// tell from other mud
+
+#define MAX_SAVED_MESSAGES  50
+#define MAX_SAVED_CHATS     20
+
+// Was geht in den Kobold?
+#define MSGFLAG_CACHE	(MSGFLAG_TELL | MSGFLAG_REMOTE | MSGFLAG_WHISPER | MSGFLAG_RTELL)
+
+#define MSGFLAG_DEAFCHK (MSGFLAG_SHOUT | MSGFLAG_TELL | MSGFLAG_SAY | MSGFLAG_WHISPER | MSGFLAG_CHANNEL | MSGFLAG_RTELL)
+
+// Rueckgabewerte von Message()
+#define MESSAGE_OK	     1	// alles klar
+#define MESSAGE_CACHE	     0	// Meldung steht im Kobold
+#define MESSAGE_IGNORE_YOU  -1	// Meldung/Sender wird ignoriert
+#define MESSAGE_IGNORE_VERB -2
+#define MESSAGE_IGNORE_MUD  -3
+#define MESSAGE_DEAF	    -4	// Empfaenger hoert nicht zu
+#define MESSAGE_CACHE_FULL  -5	// Kobold kann nichts mehr merken
+
+// Flags fuer Statusreports
+#define DO_REPORT_HP         0x1
+#define DO_REPORT_SP         0x2
+#define DO_REPORT_POISON     0x4
+#define DO_REPORT_WIMPY      0x8
+#define DO_REPORT_WIMPY_DIR  0x10
+
+#endif // __PLAYER_COMM_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_COMM_H_PROTO__
+#define __PLAYER_COMM_H_PROTO__
+
+// prototypes
+
+protected varargs int _recv(object ob, string message,
+                            int flag, string indent);
+
+varargs int Message(string msg, int flag, string indent,
+                    string cname, mixed sender);
+
+protected void status_report(int type, mixed val);
+protected void _notify(string msg, string action);
+
+#endif // __PLAYER_COMM_H_PROTO__
+
+#endif // NEED_PROTOYPES
+
diff --git a/sys/player/command.h b/sys/player/command.h
new file mode 100644
index 0000000..1a41b56
--- /dev/null
+++ b/sys/player/command.h
@@ -0,0 +1,39 @@
+// MorgenGrauen MUDlib
+//
+// player/command.h - Commands & Co
+//
+// $Id: attributes.h 6282 2007-05-09 20:55:17Z Zesstra $
+ 
+#ifndef __PLAYER_COMMAND_H__
+#define __PLAYER_COMMAND_H__
+
+// properties
+#define P_DISABLE_COMMANDS "p_lib_disablecommands"
+
+// Indizes fuers Array von commandblock
+#define B_OBJECT  0
+#define B_TIME    1
+#define B_VALUE   2
+#define B_EXCEPTIONS 3
+
+#endif // __PLAYER_COMMAND_H__
+
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_COMMAND_H_PROTO__
+#define __PLAYER_COMMAND_H_PROTO__
+
+// prototypes
+mixed modify_command(string str);
+int command_me(string cmd);
+varargs string _unparsed_args(int level);
+
+// Set- und Query-Methoden
+static mixed _set_p_lib_disablecommands(mixed data);
+static mixed _query_p_lib_disablecommands();
+
+#endif // __PLAYER_ATTRIBUTES_H_PROTO__
+
+#endif // NEED_PROTOYPES
+
diff --git a/sys/player/description.h b/sys/player/description.h
new file mode 100644
index 0000000..078e169
--- /dev/null
+++ b/sys/player/description.h
@@ -0,0 +1,34 @@
+// MorgenGrauen MUDlib
+//
+// player/description.h -- player description header
+//
+// $Id: description.h,v 3.6 2000/02/09 15:56:25 Padreic Exp $
+
+#include <living/description.h>
+ 
+#ifndef __PLAYER_DESCRIPTION_H__
+#define __PLAYER_DESCRIPTION_H__
+
+// properties
+
+#define P_PRESAY             "presay"
+#define P_TITLE              "title"
+#define P_AVERAGE_SIZE       "average_size"
+#define P_AVERAGE_WEIGHT     "average_weight"
+#define P_REFERENCE_OBJECT   "reference_object"
+
+#define P_DESCRIPTION        "description"
+
+#endif // __PLAYER_DESCRIPTION_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_DESCRIPTION_H_PROTO__
+#define __PLAYER_DESCRIPTION_H_PROTO__
+
+// prototypes
+// no prototypes
+
+#endif // __PLAYER_DESCRIPTION_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/player/fao.h b/sys/player/fao.h
new file mode 100644
index 0000000..3c016a4
--- /dev/null
+++ b/sys/player/fao.h
@@ -0,0 +1,47 @@
+// MorgenGrauen MUDlib
+//
+
+#ifndef __PLAYER_FAO_H__
+#define __PLAYER_FAO_H__
+
+#define FAO_MASTER      "/d/ebene/miril/fraternitas/secure/faomaster" // die zentrale stelle des fao
+
+// die Liste der verfügbaren Portale fuer das erste portalgeschenk
+#define FAO_PORTALS1_LIST ({2,3,4,5,6,7,8,9,10,13,14,15,16,19,20,21,25,29,33})
+// die Liste der verfügbaren Portale fuer das zweite portalgeschenk
+#define FAO_PORTALS2_LIST FAO_PORTALS1_LIST+({1,11,12,17,18,24,26,27,28,31,34,36,37,38})
+// die anzahl der zu waehlenden portale fuer das erste portalgeschenk
+#define FAO_PORTALS1_NUM  5
+// die anzahl der zu waehlenden portale fuer das zweite portalgeschenk
+#define FAO_PORTALS2_NUM  5
+
+// properties
+#define P_FAO           "fraternitasdonoarchmagorum"  // enthaelt die doni
+#define P_FAO_PORTALS   P_FAO+"PORTALS" // enthaelt die dem spieler uebergebenen portale
+
+// bitmasks
+#define FAO_TITLE   0x01
+#define FAO_PORTALS1    0x02
+#define FAO_PORTALS2  0x04
+#define FAO_SEER_PORTAL 0x08
+#define FAO_MEMBER 0x10
+
+// hilfreiche makros
+#define FAO_HAS_TITLE_GIFT(x)   (x!=0 && query_once_interactive(x) \
+    && ((int)x->QueryProp(P_FAO)&FAO_TITLE)!=0)
+#define FAO_HAS_PORTALS1_GIFT(x)  (x!=0 && query_once_interactive(x) \
+    && ((int)x->QueryProp(P_FAO)&FAO_PORTALS1)!=0)
+#define FAO_HAS_PORTALS2_GIFT(x)  (x!=0 && query_once_interactive(x) \
+    && ((int)x->QueryProp(P_FAO)&FAO_PORTALS2)!=0)
+#define FAO_HAS_PORTALS_GIFT(x)   (FAO_HAS_PORTALS1_GIFT(x) || FAO_HAS_PORTALS2_GIFT(x))
+#define FAO_MAY_USE_PORTAL(x,p)   (intp(p) && ((FAO_HAS_PORTALS1_GIFT(x) && \
+        member(FAO_PORTALS1_LIST,p)!=-1 )||(FAO_HAS_PORTALS2_GIFT(x) && \
+        member(FAO_PORTALS2_LIST,p)!=-1) ) && \
+    (pointerp((int*)x->QueryProp(P_FAO_PORTALS)) \
+     && member((int*)x->QueryProp(P_FAO_PORTALS),p)!=-1) )
+#define FAO_HAS_SEER_PORTAL_GIFT(x)     (x!=0 && query_once_interactive(x) \
+    && ((int)x->QueryProp(P_FAO)&FAO_SEER_PORTAL)!=0)
+#define FAO_IS_MEMBER(x) (x!=0 && query_once_interactive(x) \
+    && ((int)x->QueryProp(P_FAO)&FAO_MEMBER)!=0)
+#endif // __PLAYER_FAO_H__
+
diff --git a/sys/player/filesys.h b/sys/player/filesys.h
new file mode 100644
index 0000000..d2fcadb
--- /dev/null
+++ b/sys/player/filesys.h
@@ -0,0 +1,25 @@
+// MorgenGrauen MUDlib
+//
+// player/filesys.h -- file system services
+//
+// $Id: filesys.h,v 3.1 1997/02/12 13:29:09 Wargon Exp $
+ 
+#ifndef __PLAYER_FILESYS_H__
+#define __PLAYER_FILESYS_H__
+
+// properties
+
+#endif // __PLAYER_FILESYS_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_FILESYS_H_PROTO__
+#define __PLAYER_FILESYS_H_PROTO__      
+
+// prototypes
+
+varargs string find_file(string str, string suffix);
+
+#endif // __PLAYER_FILESYS_H_PROTO__
+
+#endif // NEED_PROTOYPES
diff --git a/sys/player/gmcp.h b/sys/player/gmcp.h
new file mode 100644
index 0000000..45e37ce
--- /dev/null
+++ b/sys/player/gmcp.h
@@ -0,0 +1,26 @@
+// MorgenGrauen MUDlib
+//
+// player/gmcp.h -- Stuff for GMCP handling
+//
+// $Id$
+
+#ifndef __GMCP_H__
+#define __GMCP_H__
+
+// properties
+
+#endif // __GMCP_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __GMCP_H_PROTO__
+#define __GMCP_H_PROTO__
+
+// prototypes
+/*protected*/ int GMCP_Char(mapping data);
+/*protected*/ int GMCP_Channel(string msg, string channel, string sender);
+
+#endif // __GMCP_H_PROTO__
+
+#endif  // NEED_PROTOYPES
+
diff --git a/sys/player/life.h b/sys/player/life.h
new file mode 100644
index 0000000..9ca9877
--- /dev/null
+++ b/sys/player/life.h
@@ -0,0 +1,25 @@
+// MorgenGrauen MUDlib
+//
+// player/life.h -- player life  header
+//
+// $Id: description.h,v 3.6 2000/02/09 15:56:25 Padreic Exp $
+ 
+#ifndef __PLAYER_LIFE_H__
+#define __PLAYER_LIFE_H__
+
+// properties
+
+#endif // __PLAYER_LIFE_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_LIFE_H_PROTO__
+#define __PLAYER_LIFE_H_PROTO__
+
+// prototypes
+public    void    force_save();
+protected int     death_suffering();
+
+#endif // __PLAYER_LIFE_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/player/moneyhandler.h b/sys/player/moneyhandler.h
new file mode 100644
index 0000000..2942b62
--- /dev/null
+++ b/sys/player/moneyhandler.h
@@ -0,0 +1,12 @@
+// MorgenGrauen MUDlib
+//
+// player/moneyhandler.h -- Wrapper fuer /sys/container/moneyhandler.h
+// Existiert aus Gruenden der Abwaertskompatibilitaet.
+//
+// $Id: moneyhandler.h 8396 2013-02-18 21:56:37Z Zesstra $
+
+#ifndef __PLAYER_MONEYHANDLER_H__
+#define __PLAYER_MONEYHANDLER_H__
+#include <container/moneyhandler.h>
+#endif
+
diff --git a/sys/player/moving.h b/sys/player/moving.h
new file mode 100644
index 0000000..442ab59
--- /dev/null
+++ b/sys/player/moving.h
@@ -0,0 +1,29 @@
+// MorgenGrauen MUDlib
+//
+// player/moving.h -- player moving header
+//
+// $Id: moving.h,v 3.2 2000/02/09 15:56:25 Padreic Exp $
+ 
+#ifndef __PLAYER_MOVING_H__
+#define __PLAYER_MOVING_H__
+
+// properties
+
+#define P_MSGIN              "msgin"
+#define P_MSGOUT             "msgout"
+#define P_MMSGIN             "mmsgin"
+#define P_MMSGOUT            "mmsgout"
+
+#endif // __PLAYER_MOVING_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_MOVING_H_PROTO__   
+#define __PLAYER_MOVING_H_PROTO__
+
+// prototypes
+// no prototypes
+
+#endif // __PLAYER_MOVING_H_PROTO__
+
+#endif // NEED_PROTOYPES
diff --git a/sys/player/pklog.h b/sys/player/pklog.h
new file mode 100644
index 0000000..5f5acba
--- /dev/null
+++ b/sys/player/pklog.h
@@ -0,0 +1,15 @@
+// MorgenGrauen MUDlib
+//
+// player/pklog.h -- module for logging player vs player attacks
+//
+// $Id: comm.h,v 3.3 2000/02/09 15:56:25 Padreic Exp $
+
+#ifndef __PLAYER_PKLOG_H__
+#define __PLAYER_PKLOG_H__
+
+nomask int CheckArenaFight(object victim);
+nomask protected int CheckPlayerAttack(object attacker, object victim, 
+                                       string angriffsmsg);
+
+#endif // __PLAYER_PKLOG_H__
+
diff --git a/sys/player/potion.h b/sys/player/potion.h
new file mode 100644
index 0000000..08b81b1
--- /dev/null
+++ b/sys/player/potion.h
@@ -0,0 +1,34 @@
+// MorgenGrauen MUDlib
+//
+// player/potion.h -- potion services
+//
+// $Id: potion.h 9117 2015-01-25 11:25:14Z Zesstra $
+
+#ifndef __PLAYER_POTION_H__
+#define __PLAYER_POTION_H__
+
+// properties
+
+#define P_POTIONROOMS         "potionrooms"
+#define P_KNOWN_POTIONROOMS   "known_potionrooms"
+#define P_TRANK_FINDEN        "trank_finden"
+
+// veraltete Props, lang unbenutzt.
+#define P_VISITED_POTIONROOMS "visited_potionrooms"
+#define P_BONUS_POTIONS       "bonus_potions"
+
+#endif // __PLAYER_POTION_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_POTION_H_PROTO__
+#define __PLAYER_POTION_H_PROTO__ 
+
+// prototypes
+
+varargs int FindPotion(string s);
+
+#endif // __PLAYER_POTION_H_PROTO__
+
+#endif // NEED_PROTOYPES
+
diff --git a/sys/player/quest.h b/sys/player/quest.h
new file mode 100644
index 0000000..a870fc7
--- /dev/null
+++ b/sys/player/quest.h
@@ -0,0 +1,29 @@
+// MorgenGrauen MUDlib
+//
+// player/quests.h -- player quest handling
+//
+// $Id: quest.h,v 3.2 2000/02/09 15:56:25 Padreic Exp $
+ 
+#ifndef __PLAYER_QUESTS_H__
+#define __PLAYER_QUESTS_H__
+
+// properties
+
+#define P_QUESTS             "quests"
+#define P_QP                 "questpoints"
+
+#endif // __PLAYER_QUESTS_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_QUESTS_H_PROTO__
+#define __PLAYER_QUESTS_H_PROTO__ 
+
+// prototypes
+
+varargs int GiveQuest(string questname, string message);
+int QueryQuest(string questname);
+
+#endif // __PLAYER_QUESTS_H_PROTO__
+
+#endif // NEED_PROTOYPES
diff --git a/sys/player/skills.h b/sys/player/skills.h
new file mode 100644
index 0000000..2843cad
--- /dev/null
+++ b/sys/player/skills.h
@@ -0,0 +1,25 @@
+// MorgenGrauen MUDlib
+//
+// skills.h -- Prototypen fuer Skillfunktion aus Spielern.
+//
+// $Id: skills.h 6559 2007-10-20 13:00:20Z Zesstra $
+
+#ifndef __PLAYER_SKILLS_H__
+#define __PLAYER_SKILLS_H__
+
+// properties
+
+#endif // __PLAYER_SKILLS_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_SKILLS_H_PROTO__
+#define __PLAYER_SKILLS_H_PROTO__
+                                 
+// prototypes
+protected void  set_weapon_skills();
+protected void  FixSkills();
+protected void  InitSkills();
+
+#endif // __PLAYER_SKILLS_H_PROTO__
+#endif // NEED_PROTOYPES
diff --git a/sys/player/telnetneg.h b/sys/player/telnetneg.h
new file mode 100644
index 0000000..415ed90
--- /dev/null
+++ b/sys/player/telnetneg.h
@@ -0,0 +1,19 @@
+// MorgenGrauen MUDlib
+//
+// telnetneg.h -- Definitionen und Prototypes fuer Telnet-Negotiations
+//
+// $Id: telnetneg.h 8348 2013-02-03 22:14:39Z Zesstra $
+
+#include "/secure/telnetneg.h"
+
+#ifndef _TELNETNEG_H_
+#define _TELNETNEG_H_
+
+#endif
+
+#ifdef NEED_PROTOTYPES
+#ifndef _TELNETNEG_H_PROTOS_
+#define _TELNETNEG_H_PROTOS_
+
+#endif
+#endif
diff --git a/sys/player/user_filter.h b/sys/player/user_filter.h
new file mode 100644
index 0000000..99e8036
--- /dev/null
+++ b/sys/player/user_filter.h
@@ -0,0 +1,27 @@
+// MorgenGrauen MUDlib
+//
+// player/user_filter.h -- user_filtermodule prototypes/properties
+//
+// $Id: comm.h,v 3.3 2000/02/09 15:56:25 Padreic Exp $
+
+#ifndef __PLAYER_USER_FILTER_H__
+#define __PLAYER_USER_FILTER_H__
+
+// properties
+
+#endif // __PLAYER_USER_FILTER_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_USER_FILTER_H_PROTO__
+#define __PLAYER_USER_FILTER_H_PROTO__
+
+// prototypes
+
+protected int is_active_guide(object ob);
+object *filter_users(string str);
+
+#endif // __PLAYER_USER_FILTER_H_PROTO__
+
+#endif // NEED_PROTOYPES
+
diff --git a/sys/player/util.h b/sys/player/util.h
new file mode 100644
index 0000000..537dc5f
--- /dev/null
+++ b/sys/player/util.h
@@ -0,0 +1,25 @@
+// MorgenGrauen MUDlib
+//
+// sys/player/util.h -- Headerfile fuer /std/player/util.c
+//
+// $Id: util.h,v 1.1 2004/04/09 21:02:41 Vanion Exp $
+
+#ifndef __THING_UTIL_H__
+#define __THING_UTIL_H__
+
+#endif // __THING_UTIL_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __THING_UTIL_H_PROTO__
+#define __THING_UTIL_H_PROTO__
+
+public void ShowPropList(string *props);
+static void PrettyDump(mixed x);
+static void DumpArray(mixed *x);
+static void DumpMapping(mapping x);
+static void DumpKeyValPair(mapping x, mixed key, int size);
+
+#endif // __THING_UTIL_H_PROTO__
+
+#endif // NEED_PROTOTYPES
\ No newline at end of file
diff --git a/sys/player/viewcmd.h b/sys/player/viewcmd.h
new file mode 100644
index 0000000..182ccf6
--- /dev/null
+++ b/sys/player/viewcmd.h
@@ -0,0 +1,27 @@
+// MorgenGrauen MUDlib
+//
+// player/viewcmd.h -- view command handling
+//
+// $Id: viewcmd.h 6408 2007-08-07 23:02:18Z Zesstra $
+
+#ifndef __PLAYER_VIEWCMD_H__
+#define __PLAYER_VIEWCMD_H__
+
+// properties
+
+#define P_BLIND              "blind"
+#define P_BRIEF              "brief"
+
+#endif // __PLAYER_VIEWCMD_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __PLAYER_VIEWCMD_H_PROTO__     
+#define __PLAYER_VIEWCMD_H_PROTO__
+
+// prototypes
+varargs string env_descr(int allow_short,int flags, int force_short );
+
+#endif // __PLAYER_VIEWCMD_H_PROTO__
+
+#endif // NEED_PROTOYPES
diff --git a/sys/properties.h b/sys/properties.h
new file mode 100644
index 0000000..c8bec39
--- /dev/null
+++ b/sys/properties.h
@@ -0,0 +1,50 @@
+// MorgenGrauen MUDlib
+//
+// properties.h -- properties header file
+//
+// $Id: properties.h 7169 2009-03-08 18:09:53Z Zesstra $
+ 
+#ifndef __PROPERTIES_H__
+#define __PROPERTIES_H__
+
+#ifdef NEED_PROTOTYPES
+#undef NEED_PROTOTYPES		// we need only the properties here!!
+#define TMP_NEED_PROTOTYPES
+#endif
+
+#include <snooping.h>
+#include <thing.h>
+#include <clothing.h>
+#include <armour.h>
+#include <container.h>
+#include <living.h>
+#include <npc.h>
+#include <player.h>
+#include <rooms.h>
+#include <shells.h>
+#include <weapon.h>
+#include <transport.h>
+#include <lightsource.h>
+#include <quest_items.h>
+#include <moving.h>
+
+#ifdef TMP_NEED_PROTOTYPES
+#undef TMP_NEED_PROTOTYPES
+#define NEED_PROTOTYPES
+#endif
+
+#define P_ORIG_NAME      "original_name"
+#define P_ORIG_FILE_NAME "original_file_name"
+
+#define P_CAP_NAME "cap_name"
+#define P_EARMUFFS "earmuffs"
+#define P_VALUE_PER_UNIT "value_per_unit"
+#define P_WEIGHT_PER_UNIT "weight_per_unit"
+#define P_FW_UNDERSTAND "fw_understand" /* kann die sprache aus fernwest */
+#define P_TRAY "tray" /* man kann was auf uns drauflegen */
+#define P_AMOUNT "amount"
+#define P_HP_HOOKS "hp_hooks"
+
+#define P_INFORMME "informme"
+
+#endif // __PROPERTIES_H__
diff --git a/sys/pub.h b/sys/pub.h
new file mode 100644
index 0000000..09fe9db
--- /dev/null
+++ b/sys/pub.h
@@ -0,0 +1,80 @@
+// MorgenGrauen MUDlib
+//
+// pub.h -- Headerfile for pubs and pubmaster
+//
+// $Id: pub.h 4494 2006-09-23 10:14:36Z root $
+
+#ifndef _PUB_H_
+#define _PUB_H_
+
+/* Der Pubmaster */
+#define PUBMASTER "/secure/pubmaster"
+
+/* Werte fuer die Kneipenformel:
+
+  Alc*Alcdelay+Drink*Drinkdelay+Food*Fooddelay         Value
+( --------------------------------------------  +  -------------
+          Alcdelay+Drinkdelay+Fooddelay            VALUEDIV+Rate
+
+         Rate                                    Delay
+   exp(--------)                           exp(--------)
+       RATEDIV1                                WAITDIV1
+-  ------------- ) * Factor * ( WAITOFFS + --------------- )
+     RATEDIV2                                 WAITDIV2
+
+*/
+
+#define PUB_SOAKMULT 18.0
+#define PUB_VALUEDIV 10.0
+#define PUB_RATEDIV1  3.5
+#define PUB_RATEDIV2  2.5
+#define PUB_WAITOFFS  0.9
+#define PUB_WAITDIV1 10.0
+#define PUB_WAITDIV2  7.5
+#define PUB_MAXDELAY 15.0
+
+/* Properties */
+
+// NPCs tanken hier auf die "schnelle" Art
+#define P_NPC_FASTHEAL    "npc_fastheal"
+
+// Keine Standard-Getraenke
+#define P_NO_STD_DRINK    "no_std_drink"
+
+// Meldung, wenn etwas nicht mehr vorhanden ist
+#define P_PUB_UNAVAILABLE "pub_unavailable"
+
+// Meldung, wenn etwas nicht im Menue steht
+#define P_PUB_NOT_ON_MENU "pub_not_on_menu"
+
+// Meldung, wenn Spieler nicht genug Geld hat
+#define P_PUB_NO_MONEY    "pub_no_money"
+
+// Meldung, wenn der Barkeeper nicht anwesend ist
+#define P_PUB_NO_KEEPER   "pub_no_keeper"
+
+/* Sonstige Defines */
+
+// Indices fuer das Menue-Mapping
+#define PM_TEXT      0
+#define PM_INFO      1
+#define PM_RATE      2
+#define PM_SERVE_MSG 3
+#define PM_REFRESH   4
+#define PM_DELAY     5
+#define PM_DELAY_MSG 6
+#define PM_IDS       7
+
+// Keys fuer den Refresh-Parameter
+#define PR_ALL       "All"
+#define PR_USER      "User"
+
+// Default-Defines fuer den Availability-Check
+#define PR_DEFAULT   "DEF"
+#define PR_NONE      "N/A"
+
+// Indices fuer den Refresh-Parameter
+#define PRV_AMOUNT   0
+#define PRV_REFRESH  1
+
+#endif // _PUB_H_
diff --git a/sys/quest_items.h b/sys/quest_items.h
new file mode 100644
index 0000000..76d96cf
--- /dev/null
+++ b/sys/quest_items.h
@@ -0,0 +1,18 @@
+/*
+ * QUEST_ITEMS.H -- identifies quest items
+ *
+ * $Log: quest_items.h,v $
+ * Revision 1.1  2003/12/30 12:41:14  Vanion
+ * Initial revision
+ *
+ * Revision 1.0  1997/01/20 15:33:47  mud
+ * Initial revision
+ *
+ */
+
+#ifndef ___QUEST_ITEMS_H__
+#define ___QUEST_ITEMS_H__
+
+#define P_QUEST_ITEM	"quest_item"
+
+#endif // __QUEST_ITEMS_H__
diff --git a/sys/ranged_weapon.h b/sys/ranged_weapon.h
new file mode 100644
index 0000000..50bcd9b
--- /dev/null
+++ b/sys/ranged_weapon.h
@@ -0,0 +1,26 @@
+// MorgenGrauen MUDlib
+//
+// ranged_weapon.h -- Headerfile fuer Fernkampfwaffen
+//
+// $Id: ranged_weapon.h 9016 2015-01-10 18:29:26Z Zesstra $
+
+#ifndef __RANGED_WEAPON_H__
+#define __RANGED_WEAPON_H__
+
+// Properties
+
+#endif // __RANGED_WEAPON_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __RANGED_WEAPON_H_PROTO__
+#define __RANGED_WEAPON_H_PROTO__
+
+// Prototypes
+static int shoot_dam(mapping shoot);
+static string FindRangedTarget(string str, mapping shoot);
+static int cmd_shoot(string str);
+
+#endif // __RANGED_WEAPON_H_PROTO__
+
+#endif // NEED_PROTOTYPES
diff --git a/sys/regexp.h b/sys/regexp.h
new file mode 100644
index 0000000..7c0f10c
--- /dev/null
+++ b/sys/regexp.h
@@ -0,0 +1,42 @@
+#ifndef LPC_REGEXP_H_
+#define LPC_REGEXP_H_ 1
+
+/* Definitions of regexp option values */
+
+/* Not really an RE option, but used in conjunction with some RE functions: */
+
+#define RE_GLOBAL          0x0001  /* Apply RE globally (if possible) */
+
+/* Options supported by the old regexp package: */
+
+#define RE_EXCOMPATIBLE    0x0002  /* RE is compatible with ex */
+
+/* Options supported by the PCRE regexp package: */
+
+#define RE_CASELESS        0x0004
+#define RE_MULTILINE       0x0008
+#define RE_DOTALL          0x0010
+#define RE_EXTENDED        0x0020
+#define RE_ANCHORED        0x0040
+#define RE_DOLLAR_ENDONLY  0x0080
+#define RE_NOTBOL          0x0100
+#define RE_NOTEOL          0x0200
+#define RE_UNGREEDY        0x0400
+#define RE_NOTEMPTY        0x0800
+
+/* Options specific for regmatch(): */
+
+#define RE_MATCH_SUBS      0x1000  /* Return matched subexpressions */
+
+/* Options specific for regexplode(): */
+
+#define RE_OMIT_DELIM      0x1000  /* Omit the delimiters */
+
+/* Regexp package selection options: */
+
+#define RE_TRADITIONAL     0x04000000
+#define RE_PCRE            0x02000000
+
+#define RE_PACKAGE_MASK    (RE_TRADITIONAL | RE_PCRE)
+
+#endif /* LPC_REGEXP_H_ */
diff --git a/sys/reputation.h b/sys/reputation.h
new file mode 100644
index 0000000..c46b2d8
--- /dev/null
+++ b/sys/reputation.h
@@ -0,0 +1,32 @@
+#ifndef _REPUTATION_H_
+#define _REPUTATION_H_
+
+#define REPMASTER        "/secure/repmaster"
+#define REP_SAVEFILE     "/secure/ARCH/repmaster"
+
+// Grenzwerte
+#define REP_MAXIMUM      10000
+#define REP_MINIMUM     -10000
+
+// Zwischenstufen
+#define REP_TRUSTED      7500
+#define REP_LIKED        5000
+#define REP_FAMILIAR     2500
+#define REP_NEUTRAL      0
+#define REP_UNFAMILIAR  -2500
+#define REP_DISLIKED    -5000
+#define REP_DISTRUSTED  -7500
+
+// Returncodes
+#define REP_RET_WRONGARGS  -1 // Falsche Argumente fuer ChangeRep()
+#define REP_RET_INVALIDUID -2 // Unzulaessige UID
+#define REP_RET_ALREADYMAX -3 // Reputation bereits Max / Min
+#define REP_RET_INACTIVE   -4 // Reputation auf inaktiv gesetzt
+#define REP_RET_INVALIDREP -5 // Reputation nicht vorhanden
+#define REP_RET_SUCCESS     1 // Reputation wurde veraendert
+#define REP_RET_SUCCESSCUT  2 // Reputation wurde auf Min / Max veraendert
+
+// Flags fuer die Eintraege im Master
+#define REP_FLAG_ACTIVE     1
+
+#endif
diff --git a/sys/room/description.h b/sys/room/description.h
new file mode 100644
index 0000000..c571341
--- /dev/null
+++ b/sys/room/description.h
@@ -0,0 +1,51 @@
+// MorgenGrauen MUDlib
+//
+// room/description.h -- standard room header
+//
+// $Id: description.h 9190 2015-04-02 14:29:36Z Arathorn $
+ 
+#ifndef __ROOM_DESCRIPTION_H__
+#define __ROOM_DESCRIPTION_H__
+
+// properties
+
+#define P_INT_SHORT          "int_short"
+#define P_INT_LONG           "int_long"
+
+#define P_ROOM_MSG           "room_msg"
+#define P_FUNC_MSG           "func_msg"
+#define P_MSG_PROB           "msg_prob"
+
+#define P_HAUS_ERLAUBT       "haus_erlaubt"
+
+#define P_NO_TPORT           "tport"
+#define P_TPORT_COST_IN      "tport_cost_in"
+#define P_TPORT_COST_OUT     "tport_cost_out"
+#define P_INDOORS            "indoors"
+
+#define P_NEVER_CLEAN        " never clean "
+#define P_ROOM_TYPE          "room_type"
+#define P_NO_PLAYERS         "no_players"
+
+#define P_NO_PARA_TRANS      "no_para_trans"
+#define P_KEEPER             "shop_keeper"    // Eigentuemer von Pub oder Shop
+#define P_BUY_ONLY_PLANTS    "lib_p_buy_only_plants"
+#define P_ORAKEL             "orakel"
+
+#define P_LIGHT_ABSORPTION   "light_absorption" // Licht das der Raum schluckt
+
+#define P_DOMAIN             "lib_p_domain"   // Domain, in der der Raum ist.
+
+#endif // __ROOM_DESCRIPTION_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __ROOM_DESCRIPTION_H_PROTO__
+#define __ROOM_DESCRIPTION_H_PROTO__
+
+// prototypes
+// no prototypes
+
+#endif // __ROOM_DESCRIPTION_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/room/exits.h b/sys/room/exits.h
new file mode 100644
index 0000000..d3f0896
--- /dev/null
+++ b/sys/room/exits.h
@@ -0,0 +1,42 @@
+// MorgenGrauen MUDlib
+//
+// room/exits.h -- exit specific defines
+//
+// $Id: exits.h 9477 2016-02-19 21:17:51Z Zesstra $
+
+#ifndef __ROOM_EXITS_H__
+#define __ROOM_EXITS_H__
+
+// Properties
+
+#define P_EXITS              "exits"
+#define P_SPECIAL_EXITS      "special_exits"
+#define P_HIDE_EXITS         "hide_exits"
+
+#define P_BLOCK_MSG          "block_msg"
+
+#endif // __ROOM_EXITS_H__
+
+// Prototypes
+#ifdef NEED_PROTOTYPES
+
+#ifndef __ROOM_EXITS_H_PROTO__
+#define __ROOM_EXITS_H_PROTO__
+
+static mapping _set_exits(mapping map_ldfied) ;
+static mapping _query_exits();
+static int _set_special_exits(mapping map_ldfied);
+static mapping _query_special_exits();
+static string _MakePath(string str);
+void AddExit(string|string* cmd, closure|string dest);
+protected void _AddExit(string|string* cmd, string|closure room,
+                        string message);
+void RemoveExit(string|string* cmd);
+void AddSpecialExit(string|string* cmd, string|closure functionname);
+void RemoveSpecialExit(string|string* cmd);
+varargs string GetExits( object viewer );
+int _normalfunction();
+
+#endif // __ROOM_EXITS_H_PROTO__
+
+#endif // NEED_PROTOTYPES
diff --git a/sys/rooms.h b/sys/rooms.h
new file mode 100644
index 0000000..2951036
--- /dev/null
+++ b/sys/rooms.h
@@ -0,0 +1,63 @@
+// MorgenGrauen MUDlib
+//
+// rooms.h -- standard room header
+//
+// $Id: rooms.h 8425 2013-02-23 18:49:09Z Zesstra $
+
+#ifndef __ROOM_H__
+#define __ROOM_H__
+
+#include <room/description.h>
+#include <room/exits.h>
+#include <doorroom.h>
+
+// properties
+// Flags fuer Einschraenkung der Daten fuer Mappingzwecke an den Client
+#define P_MAP_RESTRICTIONS  "lib_p_map_restrictions"
+
+// raise error message
+#define AddDoor(a,b,c,d,e,f) raise_error("Use NewDoor instead of AddDoor!\n")
+#define AddDoor2(a,b,c,d,e,f,g) raise_error("Use NewDoor instead of AddDoor2!\n")
+#define AddRoomCmd(a,b) raise_error("Use AddCmd instead of AddRoomCmd!\n")
+#define RemoveRoomCmd(a,b) raise_error("Use RemoveCmd instead of RemoveRoomCmd!\n")
+// defines
+
+/* entries in item array */
+#define RITEM_OBJECT  0
+#define RITEM_FILE    1
+#define RITEM_REFRESH 2
+#define RITEM_PROPS   3
+
+/* refresh for extra items */
+#define REFRESH_NONE      0
+#define REFRESH_DESTRUCT  1
+#define REFRESH_REMOVE    2
+#define REFRESH_ALWAYS    3
+#define REFRESH_MOVE_HOME 4
+
+/* values for teleport forbid property */
+#define TPORT_IN   1
+#define TPORT_OUT  2
+#define TPORT_BOTH (TPORT_IN | TPORT_OUT)
+
+/* values for different roomtypes */
+#define RT_SHOP    1
+#define RT_PUB     2
+
+// Werte fuer P_MAP_RESTRICTIONS
+#define MR_NOUID  0x1
+#define MR_NOINFO 0x2
+
+#endif // __ROOM_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __ROOM_H_PROTO__
+#define __ROOM_H_PROTO__
+
+// prototypes
+// no prototypes
+
+#endif // __ROOM_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/routingd.h b/sys/routingd.h
new file mode 100644
index 0000000..3730679
--- /dev/null
+++ b/sys/routingd.h
@@ -0,0 +1,15 @@
+// MorgenGrauen MUDlib
+//
+// routingd.h -- Definitionen fuer den RoutingDaemon
+//
+// $Id: routingd.h,v 3.1 1997/02/12 13:04:59 Wargon Exp $
+
+#ifndef _ROUTINGD_H_
+#define _ROUTINGD_H_
+
+#define ROUTER "/p/daemon/routingd"
+
+#define TARGET_PUB "##pub"
+#define TARGET_SHOP "##shop"
+
+#endif
diff --git a/sys/rtlimits.h b/sys/rtlimits.h
new file mode 100644
index 0000000..a136aa2
--- /dev/null
+++ b/sys/rtlimits.h
@@ -0,0 +1,39 @@
+#ifndef LPC_RTLIMITS_H_
+#define LPC_RTLIMITS_H_
+
+/* Runtime limit index/tag values */
+
+#define LIMIT_EVAL           (0)
+#define LIMIT_ARRAY          (1)
+#define LIMIT_MAPPING        (2)
+#define LIMIT_MAPPING_KEYS   LIMIT_MAPPING
+#define LIMIT_MAPPING_SIZE   (3)
+#define LIMIT_BYTE           (4)
+#define LIMIT_FILE           (5)
+#define LIMIT_CALLOUTS       (6)
+#define LIMIT_COST           (7)
+#define LIMIT_MEMORY         (8)
+
+#define LIMIT_MAX      (9)  /* Number of recognized limits */
+
+/* Special limit values */
+
+#define LIMIT_UNLIMITED    0   /* No limit */
+#define LIMIT_KEEP       (-1)  /* Keep the old limit setting */
+#define LIMIT_DEFAULT    (-2)  /* Use the default setting */
+
+/* memory limits */
+#define MALLOC_SOFT_LIMIT    1
+#define MALLOC_HARD_LIMIT    2
+
+/* low memory conditions */
+#define NO_MALLOC_LIMIT_EXCEEDED     0
+#define SOFT_MALLOC_LIMIT_EXCEEDED   MALLOC_SOFT_LIMIT
+#define HARD_MALLOC_LIMIT_EXCEEDED   MALLOC_HARD_LIMIT
+
+/* Flags for available reserves */
+#define USER_RESERVE_AVAILABLE     0x1
+#define MASTER_RESERVE_AVAILABLE   0x2
+#define SYSTEM_RESERVE_AVAILABLE   0x4
+
+#endif /* LPC_RTLIMITS_H_ */
diff --git a/sys/seil.h b/sys/seil.h
new file mode 100644
index 0000000..3d59830
--- /dev/null
+++ b/sys/seil.h
@@ -0,0 +1,8 @@
+#define P_TIED "tied"		// wird im gebundenn object gesetzt und behinaltet ein mapping
+#define P_TIE_USER "tie_user"	// hier steht der user drin, der die letzte 
+				// aktion mit dem seil gemacht hat
+				// Prop kann in tie() ausgelesen werden.
+#define P_TIE_VERB "tie_verb"	// wird fuer automatisches binden/loesen benoetigt.
+#define P_TIE_MESSAGE "tie_message" // Ausgabe beim festbinden des seiles aus einem object/raum heraus
+#define P_UNTIE_MESSAGE "untie_message" // MSG zum loesen des Seiles
+#define P_TIE_AUTO	"tie_auto" 
diff --git a/sys/sensitive.h b/sys/sensitive.h
new file mode 100644
index 0000000..fc51dc6
--- /dev/null
+++ b/sys/sensitive.h
@@ -0,0 +1,44 @@
+// MorgenGrauen MUDlib
+//
+// sensitive.h -- Definitionen fuer sensitive Objekte.
+//                Siehe auch /std/container/inventory.c und
+//                /std/living/inventory.c
+//
+// $Id: sensitive.h,v 3.1 1997/02/12 13:04:59 Wargon Exp $
+
+#ifndef __SENSITIVE_H__
+#define __SENSITIVE_H__
+
+#define P_SENSITIVE_INVENTORY "sensitive_inv"
+#define P_SENSITIVE_INVENTORY_TRIGGER "sensitive_inv_trigger"
+#define P_SENSITIVE_ATTACK "sensitive_attack"
+
+#define SENSITIVE_INVENTORY "inv"
+#define SENSITIVE_INVENTORY_TRIGGER "inv_trigger"
+#define SENSITIVE_ATTACK "attack"
+
+#define SENS_OBJECT 0
+#define SENS_CLOSURE 1
+#define SENS_KEY 2
+#define SENS_THRESHOLD 3
+#define SENS_OPT 4
+
+#define SENS_PROP_PREFIX "sensitive_"
+#define SENS_INSERT_PREFIX "insert_sensitive_"
+#define SENS_TRIGGER_PREFIX "trigger_sensitive_"
+
+#endif
+
+#ifdef NEED_PROTOTYPES
+#ifndef __SENSITIVE_H_PROTO__
+#define __SENSITIVE_H_PROTO__
+
+void RemoveSensitiveObjectFromList(object ob, string list);
+void RemoveSensitiveObject(object ob);
+varargs void InsertSensitiveObjectToList(object ob, string list, string key,
+					 int threshold, mixed *opt);
+void InsertSensitiveObject(object ob, mixed arg);
+void CheckSensitiveAttack(int dam, mixed dam_type, mixed spell, object enemy);
+
+#endif
+#endif
diff --git a/sys/sent.h b/sys/sent.h
new file mode 100644
index 0000000..179bef2
--- /dev/null
+++ b/sys/sent.h
@@ -0,0 +1,10 @@
+#ifndef LPC_SENT_H_
+#define LPC_SENT_H_
+
+#define SENT_PLAIN	0
+#define SENT_SHORT_VERB	1
+#define SENT_NO_SPACE	2
+#define SENT_NO_VERB	3
+#define SENT_MARKER	4
+
+#endif
diff --git a/sys/shells.h b/sys/shells.h
new file mode 100644
index 0000000..b792cca
--- /dev/null
+++ b/sys/shells.h
@@ -0,0 +1,27 @@
+// MorgenGrauen MUDlib
+//
+// shells.h -- shell header
+//
+// $Id: shells.h 9555 2016-05-03 20:42:46Z Zesstra $
+
+#ifndef __SHELLS_H__
+#define __SHELLS_H__
+
+// properties
+
+#define P_CURRENTDIR "currentdir"
+#define P_LASTDIR "p_lib_lastdir"
+
+#endif // __SHELLS_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __SHELLS_H_PROTO__
+#define __SHELLS_H_PROTO__
+
+// prototypes
+// no prototypes
+
+#endif // __SHELLS_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/signals.h b/sys/signals.h
new file mode 100644
index 0000000..be4412e
--- /dev/null
+++ b/sys/signals.h
@@ -0,0 +1,23 @@
+#ifndef LPC_SIGNALS_H_
+#define LPC_SIGNALS_H_ 1
+
+// Our defines for some POSIX signals the driver defers to the mudlib.
+// This abstraction is intentional.
+#define LPC_SIGHUP    1
+#define LPC_SIGINT    2
+#define LPC_SIGTERM   15
+#define LPC_SIGUSR1   16
+#define LPC_SIGUSR2   17
+
+// __STD__ is defined during the driver compilation (the driver includes this
+// file as well) but not in LPC programs. In LPC we may safely define the
+// signals name without the LPC_ prefix.
+#if !defined(__STDC__)
+#define SIGHUP       LPC_SIGHUP
+#define SIGINT       LPC_SIGINT
+#define SIGTERM      LPC_SIGTERM
+#define SIGUSR1      LPC_SIGUSR1
+#define SIGUSR2      LPC_SIGUSR2
+#endif // __STD__
+
+#endif /* LPC_SIGNALS_H_ */
diff --git a/sys/snooping.h b/sys/snooping.h
new file mode 100644
index 0000000..45edcf6
--- /dev/null
+++ b/sys/snooping.h
@@ -0,0 +1,15 @@
+// MorgenGrauen MUDlib
+//
+// snooping.h -- Definitionen rund ums Snoopen
+//
+// $Id: snooping.h,v 3.1 1997/02/12 13:04:59 Wargon Exp $
+
+#ifndef _SNOOPING_H_
+#define _SNOOPING_H_
+
+#define SF_LOCKED        1
+#define SF_FORCE         2
+
+#define P_SNOOPFLAGS     "snoopflags"
+
+#endif
diff --git a/sys/spellbook.h b/sys/spellbook.h
new file mode 100644
index 0000000..876301e
--- /dev/null
+++ b/sys/spellbook.h
@@ -0,0 +1,56 @@
+// MorgenGrauen MUDlib
+//
+// spellbook.h -- Funktions-Prototypen fuer das /std/spellbook.h
+//
+// $Id: spellbook.h 8822 2014-05-17 09:49:49Z Zesstra $
+
+#ifndef __SPELLBOOK_H__
+#define __SPELLBOOK_H__
+
+// no defines
+
+#endif // __SPELLBOOK_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __SPELLBOOK_H_PROTO__
+#define __SPELLBOOK_H_PROTO__
+
+varargs int      AddSpell(string verb, int kosten, mixed ski);
+        int      CanTrySpell(object caster, mapping sinfo);
+        int      SpellSuccess(object caster, mapping sinfo);
+        int      TryAttackSpell(object victim, int damage, mixed dtypes,
+                                mixed is_spell, object caster,
+                                mapping sinfo);
+varargs int      TryDefaultAttackSpell(object victim, object caster,
+                                       mapping sinfo, mixed str);
+varargs int      TryGlobalAttackSpell(object caster, mapping sinfo, int suc,
+                                      int damage, mixed dt, mixed is_spell,
+                                      int dist, int depth, int width);
+varargs int      UseSpell(object caster, string spell, mapping sinfo);
+        mapping  QuerySpell(string spell);
+varargs mixed    FindDistantGroups(object pl, int dist, int dy, int dx);
+varargs object   FindDistantEnemyVictim(string wen, object pl, string msg,
+                                        int dist, int dy);
+varargs object  *FindDistantGroup(object pl, int who, int dist, int dy,
+                                  int dx);
+varargs object   FindEnemyVictim(string wen, object pl, string msg);
+varargs object   FindFarEnemyVictim(string wen, object pl, string msg,
+                                    int min, int max);
+        object  *FindGroup(object pl, int who);
+        object  *FindGroupN(object pl, int who, int n);
+        object  *FindGroupP(object pl, int who, int pr);
+varargs object   FindLivingVictim(string wen, object pl, string msg);
+varargs object   FindNearEnemyVictim(string wen, object pl, string msg);
+static
+varargs object   find_victim(string wen, object pl);
+varargs object   FindVictim(string wen, object pl, string msg);
+        string   SelectSpell(string spell, mapping sinfo);
+        void     Erfolg(object caster, string spell, mapping sinfo);
+        void     Learn(object caster, string spell, mapping sinfo);
+        void     Misserfolg(object caster, string spell, mapping sinfo);
+varargs void     prepare_spell(object caster, string spell, mapping sinfo);
+
+#endif // __SPELLBOOK_H_PROTO__
+
+#endif // // NEED_PROTOYPES
diff --git a/sys/strings.h b/sys/strings.h
new file mode 100644
index 0000000..a78ae1e
--- /dev/null
+++ b/sys/strings.h
@@ -0,0 +1,10 @@
+#ifndef LPC_STRINGS_H_
+#define LPC_STRINGS_H_ 1
+
+/* 'where' argument values for trim() */
+
+#define TRIM_LEFT  0x01
+#define TRIM_RIGHT 0x02
+#define TRIM_BOTH  (TRIM_LEFT|TRIM_RIGHT)
+
+#endif /* LPC_STRINGS_H_ */
diff --git a/sys/struct_info.h b/sys/struct_info.h
new file mode 100644
index 0000000..dde00e6
--- /dev/null
+++ b/sys/struct_info.h
@@ -0,0 +1,38 @@
+#ifndef LPC_STRUCTINFO_H_
+#define LPC_STRUCTINFO_H_
+
+#ifndef __DRIVER_SOURCE__
+#include "lpctypes.h"
+#endif
+
+/* Definition of argument values for struct_info() and
+ * of the indices in the corresponding result arrays.
+ */
+
+/* Possible types of information requested from struct_info()
+ */
+#define SINFO_FLAT    0
+#define SINFO_NESTED  1
+
+
+/* Indices in the result array
+ */
+
+#define SI_NAME         0
+#define SI_PROG_NAME    1
+#define SI_PROG_ID      2
+#define SI_BASE         3
+#define SI_MEMBER       4
+
+#define SI_MAX 5  /* Min Number of SI_ result elements */
+
+/* Indices in the SI_MEMBER arrays
+ */
+
+#define SIM_NAME   0
+#define SIM_TYPE   1
+#define SIM_EXTRA  2
+
+#define SIM_MAX 3 /* Number of SIM_ elements */
+
+#endif /* LPC_STRUCTINFO_H_ */
diff --git a/sys/sys_debug.h b/sys/sys_debug.h
new file mode 100644
index 0000000..4b82764
--- /dev/null
+++ b/sys/sys_debug.h
@@ -0,0 +1,24 @@
+// MorgenGrauen MUDlib
+//
+// sys_debug.h -- Definitionen fuer Debugzwecke
+//
+// $Id: sys_debug.h 6081 2006-10-23 14:12:34Z Zesstra $
+
+#ifndef __SYS_DEBUG_H__
+#define __SYS_DEBUG_H__
+
+#define DEBUG(x) printf("DEBUG: OBJ(/%O) %O\n", this_object(), (x))
+
+#define DBG_ON(c) (traceprefix(object_name(this_object())[1..]), trace(c?c:239))
+#define DBG_OFF() (traceprefix(""), trace(0))
+
+#define LFUN    1   // debug lfunc calls
+#define CALL    2   // debug call_other calls
+#define RET     4   // debug return values
+#define ARGS    8   // show arguments when calling a function
+#define STACK   16  // show all stack machine code (DON'T USE!)
+#define HB      32  // show calls to heart_beat()
+#define APPLY   64  // debug calls to apply
+#define NAME    128 // show name of file when debugging (don't use)
+
+#endif
diff --git a/sys/telnet.h b/sys/telnet.h
new file mode 100644
index 0000000..94a2b14
--- /dev/null
+++ b/sys/telnet.h
@@ -0,0 +1,261 @@
+#ifndef TELNET_H__
+#define TELNET_H__ 1
+
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *        @(#)telnet.h        5.7 (Berkeley) 11/14/89
+ */
+
+/*
+ * Definitions for the TELNET protocol.
+ */
+#define  IAC     255      /* interpret as command: */
+#define  DONT    254      /* you are not to use option */
+#define  DO      253      /* please, you use option */
+#define  WONT    252      /* I won't use option */
+#define  WILL    251      /* I will use option */
+#define  SB      250      /* interpret as subnegotiation */
+#define  GA      249      /* you may reverse the line */
+#define  EL      248      /* erase the current line */
+#define  EC      247      /* erase the current character */
+#define  AYT     246      /* are you there */
+#define  AO      245      /* abort output--but let prog finish */
+#define  IP      244      /* interrupt process--permanently */
+#define  BREAK   243      /* break */
+#define  DM      242      /* data mark--for connect. cleaning */
+#define  NOP     241      /* nop */
+#define  SE      240      /* end sub negotiation */
+#define  EOR     239      /* end of record (transparent mode) */
+#define  ABORT   238      /* Abort process */
+#define  SUSP    237      /* Suspend process */
+#define  xEOF    236      /* End of file: EOF is already used... */
+
+#define  SYNCH   242      /* for telfunc calls */
+
+#ifdef __DRIVER_SOURCE__
+
+#ifdef TELCMDS
+char *telcmds[] = {
+        "EOF", "SUSP", "ABORT", "EOR",
+        "SE", "NOP", "DMARK", "BRK", "IP", "AO", "AYT", "EC",
+        "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC",
+};
+#define  TELCMD_FIRST   xEOF
+#define  TELCMD_LAST    IAC
+#define  TELCMD_OK(x)   ((x) <= TELCMD_LAST && (x) >= TELCMD_FIRST)
+#define  TELCMD(x)      telcmds[(x)-TELCMD_FIRST]
+#endif
+
+#endif /* __DRIVER_SOURCE__ */
+
+/* telnet options */
+#define  TELOPT_BINARY         0        /* 8-bit data path */
+#define  TELOPT_ECHO           1        /* echo */
+#define  TELOPT_RCP            2        /* prepare to reconnect */
+#define  TELOPT_SGA            3        /* suppress go ahead */
+#define  TELOPT_NAMS           4        /* approximate message size */
+#define  TELOPT_STATUS         5        /* give status */
+#define  TELOPT_TM             6        /* timing mark */
+#define  TELOPT_RCTE           7        /* remote controlled transmission and echo */
+#define  TELOPT_NAOL           8        /* negotiate about output line width */
+#define  TELOPT_NAOP           9        /* negotiate about output page size */
+#define  TELOPT_NAOCRD        10        /* negotiate about CR disposition */
+#define  TELOPT_NAOHTS        11        /* negotiate about horizontal tabstops */
+#define  TELOPT_NAOHTD        12        /* negotiate about horizontal tab disposition */
+#define  TELOPT_NAOFFD        13        /* negotiate about formfeed disposition */
+#define  TELOPT_NAOVTS        14        /* negotiate about vertical tab stops */
+#define  TELOPT_NAOVTD        15        /* negotiate about vertical tab disposition */
+#define  TELOPT_NAOLFD        16        /* negotiate about output LF disposition */
+#define  TELOPT_XASCII        17        /* extended ascic character set */
+#define  TELOPT_LOGOUT        18        /* force logout */
+#define  TELOPT_BM            19        /* byte macro */
+#define  TELOPT_DET           20        /* data entry terminal */
+#define  TELOPT_SUPDUP        21        /* supdup protocol */
+#define  TELOPT_SUPDUPOUTPUT  22        /* supdup output */
+#define  TELOPT_SNDLOC        23        /* send location */
+#define  TELOPT_TTYPE         24        /* terminal type */
+#define  TELOPT_EOR           25        /* end or record */
+#define  TELOPT_TUID          26        /* TACACS user identification */
+#define  TELOPT_OUTMRK        27        /* output marking */
+#define  TELOPT_TTYLOC        28        /* terminal location number */
+#define  TELOPT_3270REGIME    29        /* 3270 regime */
+#define  TELOPT_X3PAD         30        /* X.3 PAD */
+#define  TELOPT_NAWS          31        /* window size */
+#define  TELOPT_TSPEED        32        /* terminal speed */
+#define  TELOPT_LFLOW         33        /* remote flow control */
+#define  TELOPT_LINEMODE      34        /* Linemode option */
+#define  TELOPT_XDISPLOC      35        /* X Display Location */
+#define  TELOPT_ENVIRON	      36        /* Environment opt for Port ID */
+#define  TELOPT_AUTHENTICATION 37       /* authentication */
+#define  TELOPT_ENCRYPT       38        /* authentication */
+#define  TELOPT_NEWENV        39        /* Environment opt for Port ID */
+#define  TELOPT_STARTTLS      46        /* Transport Layer Security */
+#define  TELOPT_KERMIT        47        /* Telnet KERMIT */
+#define  TELOPT_SEND_URL      48        /* Send URL */
+#define  TELOPT_EXOPL        255        /* extended-options-list */
+
+/* Inofficial, mud specific telnet options */
+#define  TELOPT_MSSP          70        /* Mud Server Status Protocol */
+#define  TELOPT_COMPRESS      85        /* Mud Compression Protocol, v.1 */
+#define  TELOPT_COMPRESS2     86        /* Mud Compression Protocol, v.2 */
+#define  TELOPT_MSP           90        /* Mud Sound Protocol */
+#define  TELOPT_MXP           91        /* Mud Extension Protocol */
+#define  TELOPT_ZMP           93        /* Zenith Mud Protocol */
+#define  TELOPT_MUSHCLIENT   102        /* Mushclient/Aardwolf Protocol */
+#define  TELOPT_ATCP         200        /* Achaea Telnet Client Protocol */
+#define  TELOPT_GMCP         201        /* General Mud Communication Protocol*/
+
+#define  NTELOPTS            256        /* was: (1+TELOPT_NEWENV) */
+
+#ifdef __DRIVER_SOURCE__
+
+#ifdef TELOPTS
+char *telopts[NTELOPTS]
+ = { "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD"
+   , "NAME", "STATUS", "TIMING MARK", "RCTE"
+   , "NAOL", "NAOP", "NAOCRD", "NAOHTS"
+   , "NAOHTD", "NAOFFD", "NAOVTS", "NAOVTD"
+   , "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO"
+   , "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT", "SEND LOCATION"
+   , "TERMINAL TYPE", "END OF RECORD", "TACACS UID", "OUTPUT MARKING"
+   , "TTYLOC", "3270 REGIME", "X.3 PAD", "NAWS"
+   , "TSPEED", "LFLOW", "LINEMODE", "XDISPLOC"
+   , "ENVIRON", "AUTH", "ENCRYPT", "NEWENV"
+   , "TELOPT 40", "TELOPT 41", "TELOPT 42", "TELOPT 43"
+   , "TELOPT 44", "TELOPT 45", "STARTTLS", "KERMIT"
+   , "SEND_URL", "TELOPT 49", "TELOPT 50", "TELOPT 51"
+   , "TELOPT 52", "TELOPT 53", "TELOPT 54", "TELOPT 55"
+   , "TELOPT 56", "TELOPT 57", "TELOPT 58", "TELOPT 59"
+   , "TELOPT 60", "TELOPT 61", "TELOPT 62", "TELOPT 63"
+   , "TELOPT 64", "TELOPT 65", "TELOPT 66", "TELOPT 67"
+   , "TELOPT 68", "TELOPT 69", "MSSP", "TELOPT 71"
+   , "TELOPT 72", "TELOPT 73", "TELOPT 74", "TELOPT 75"
+   , "TELOPT 76", "TELOPT 77", "TELOPT 78", "TELOPT 79"
+   , "TELOPT 80", "TELOPT 81", "TELOPT 82", "TELOPT 83"
+   , "TELOPT 84", "MUD COMPRESS", "MUD COMPRESS2", "TELOPT 87"
+   , "TELOPT 88", "TELOPT 89", "MUD SOUND", "MUD EXTENSION"
+   , "TELOPT 92", "ZMP", "TELOPT 94", "TELOPT 95"
+   , "TELOPT 96", "TELOPT 97", "TELOPT 98", "TELOPT 99"
+   , "TELOPT 100", "TELOPT 101", "MUSHCLIENT", "TELOPT 103"
+   , "TELOPT 104", "TELOPT 105", "TELOPT 106", "TELOPT 107"
+   , "TELOPT 108", "TELOPT 109", "TELOPT 110", "TELOPT 111"
+   , "TELOPT 112", "TELOPT 113", "TELOPT 114", "TELOPT 115"
+   , "TELOPT 116", "TELOPT 117", "TELOPT 118", "TELOPT 119"
+   , "TELOPT 120", "TELOPT 121", "TELOPT 122", "TELOPT 123"
+   , "TELOPT 124", "TELOPT 125", "TELOPT 126", "TELOPT 127"
+   , "TELOPT 128", "TELOPT 129", "TELOPT 130", "TELOPT 131"
+   , "TELOPT 132", "TELOPT 133", "TELOPT 134", "TELOPT 135"
+   , "TELOPT 136", "TELOPT 137", "TELOPT 138", "TELOPT 139"
+   , "TELOPT 140", "TELOPT 141", "TELOPT 142", "TELOPT 143"
+   , "TELOPT 144", "TELOPT 145", "TELOPT 146", "TELOPT 147"
+   , "TELOPT 148", "TELOPT 149", "TELOPT 150", "TELOPT 151"
+   , "TELOPT 152", "TELOPT 153", "TELOPT 154", "TELOPT 155"
+   , "TELOPT 156", "TELOPT 157", "TELOPT 158", "TELOPT 159"
+   , "TELOPT 160", "TELOPT 161", "TELOPT 162", "TELOPT 163"
+   , "TELOPT 164", "TELOPT 165", "TELOPT 166", "TELOPT 167"
+   , "TELOPT 168", "TELOPT 169", "TELOPT 170", "TELOPT 171"
+   , "TELOPT 172", "TELOPT 173", "TELOPT 174", "TELOPT 175"
+   , "TELOPT 176", "TELOPT 177", "TELOPT 178", "TELOPT 179"
+   , "TELOPT 180", "TELOPT 181", "TELOPT 182", "TELOPT 183"
+   , "TELOPT 184", "TELOPT 185", "TELOPT 186", "TELOPT 187"
+   , "TELOPT 188", "TELOPT 189", "TELOPT 190", "TELOPT 191"
+   , "TELOPT 192", "TELOPT 193", "TELOPT 194", "TELOPT 195"
+   , "TELOPT 196", "TELOPT 197", "TELOPT 198", "TELOPT 199"
+   , "ATCP", "TELOPT 201", "TELOPT 202", "TELOPT 203"
+   , "TELOPT 204", "TELOPT 205", "TELOPT 206", "TELOPT 207"
+   , "TELOPT 208", "TELOPT 209", "TELOPT 210", "TELOPT 211"
+   , "TELOPT 212", "TELOPT 213", "TELOPT 214", "TELOPT 215"
+   , "TELOPT 216", "TELOPT 217", "TELOPT 218", "TELOPT 219"
+   , "TELOPT 220", "TELOPT 221", "TELOPT 222", "TELOPT 223"
+   , "TELOPT 224", "TELOPT 225", "TELOPT 226", "TELOPT 227"
+   , "TELOPT 228", "TELOPT 229", "TELOPT 230", "TELOPT 231"
+   , "TELOPT 232", "TELOPT 233", "TELOPT 234", "TELOPT 235"
+   , "TELOPT 236", "TELOPT 237", "TELOPT 238", "TELOPT 239"
+   , "TELOPT 240", "TELOPT 241", "TELOPT 242", "TELOPT 243"
+   , "TELOPT 244", "TELOPT 245", "TELOPT 246", "TELOPT 247"
+   , "TELOPT 248", "TELOPT 249", "TELOPT 250", "TELOPT 251"
+   , "TELOPT 252", "TELOPT 253", "TELOPT 254", "EXOPL"
+};
+#endif
+
+#endif /* __DRIVER_SOURCE__ */
+
+/* sub-option qualifiers */
+#define  TELQUAL_IS     0        /* option is... */
+#define  TELQUAL_SEND   1        /* send option */
+
+/*
+ * LINEMODE suboptions
+ */
+
+#define  LM_MODE            1
+#define  LM_FORWARDMASK     2
+#define  LM_SLC             3
+
+#define  MODE_EDIT       0x01
+#define  MODE_TRAPSIG    0x02
+#define  MODE_ACK        0x04
+
+#define  MODE_MASK       (MODE_EDIT|MODE_TRAPSIG|MODE_ACK)
+
+/* Not part of protocol, but needed to simplify things... */
+#define  MODE_FLOW       0x0100
+#define  MODE_ECHO       0x0200
+#define  MODE_INBIN      0x0400
+#define  MODE_OUTBIN     0x0800
+#define  MODE_FORCE      0x1000
+
+#define  SLC_SYNCH   1
+#define  SLC_BRK     2
+#define  SLC_IP      3
+#define  SLC_AO      4
+#define  SLC_AYT     5
+#define  SLC_EOR     6
+#define  SLC_ABORT   7
+#define  SLC_EOF     8
+#define  SLC_SUSP    9
+#define  SLC_EC     10
+#define  SLC_EL     11
+#define  SLC_EW     12
+#define  SLC_RP     13
+#define  SLC_LNEXT  14
+#define  SLC_XON    15
+#define  SLC_XOFF   16
+#define  SLC_FORW1  17
+#define  SLC_FORW2  18
+
+#define  NSLC       18
+
+#define  SLC_NAMES  "0", "SYNCH", "BRK", "IP", "AO", "AYT", "EOR", \
+                    "ABORT", "EOF", "SUSP", "EC", "EL", "EW", "RP", \
+                    "LNEXT", "XON", "XOFF", "FORW1", "FORW2"
+
+#define  SLC_NOSUPPORT     0
+#define  SLC_CANTCHANGE    1
+#define  SLC_VARIABLE      2
+#define  SLC_DEFAULT       3
+#define  SLC_LEVELBITS    0x03
+
+#define  SLC_FUNC          0
+#define  SLC_FLAGS         1
+#define  SLC_VALUE         2
+
+#define  SLC_ACK          0x80
+#define  SLC_FLUSHIN      0x40
+#define  SLC_FLUSHOUT     0x20
+
+#endif  /* TELNET_H__ */
diff --git a/sys/temp/std_headers.h b/sys/temp/std_headers.h
new file mode 100644
index 0000000..f96080e
--- /dev/null
+++ b/sys/temp/std_headers.h
@@ -0,0 +1,15 @@
+#ifndef _STD_HEADERS_
+#define _STD_HEADERS_
+
+//LANGUAGE
+void SetArticle( int fl );
+static int QueryAFlag();
+public varargs string QueryArticle( int casus, int dem, int force );
+public varargs int SuggestArticle( string myname );
+public varargs string QueryPossPronoun( int what, int casus, int number );
+public string QueryPronoun( int casus );
+public varargs string QueryDu(int casus,int gender,int zahl);
+public string QueryGenderString();
+public varargs string DeclAdj( string adj, int casus, int demon );
+
+#endif
diff --git a/sys/temp/util.h b/sys/temp/util.h
new file mode 100644
index 0000000..b3fd23d
--- /dev/null
+++ b/sys/temp/util.h
@@ -0,0 +1,25 @@
+// MorgenGrauen MUDlib
+//
+// sys/thing/util.h -- Headerfile fuer /std/thing/util.c
+//
+// $Id: util.h,v 1.1 2000/08/15 12:07:30 Paracelsus Exp $
+
+#ifndef __THING_UTIL_H__
+#define __THING_UTIL_H__
+
+#endif // __THING_UTIL_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __THING_UTIL_H_PROTO__
+#define __THING_UTIL_H_PROTO__
+
+public void ShowPropList(string *props);
+static void PrettyDump(mixed x);
+static void DumpArray(mixed *x);
+static void DumpMapping(mapping x);
+static void DumpKeyValPair(mapping x, mixed key, int size);
+
+#endif // __THING_UTIL_H_PROTO__
+
+#endif // NEED_PROTOTYPES
\ No newline at end of file
diff --git a/sys/terminal.h b/sys/terminal.h
new file mode 100644
index 0000000..903af6d
--- /dev/null
+++ b/sys/terminal.h
Binary files differ
diff --git a/sys/thing.h b/sys/thing.h
new file mode 100644
index 0000000..e478014
--- /dev/null
+++ b/sys/thing.h
@@ -0,0 +1,19 @@
+// MorgenGrauen MUDlib
+//
+// thing.h -- standard thing definitions
+//
+// $Id: thing.h,v 3.5 2004/05/20 19:00:12 Vanion Exp $
+
+#ifndef __THING_H__
+#define __THING_H__
+
+#include "/sys/thing/commands.h"
+#include "/sys/thing/description.h"
+#include "/sys/thing/language.h"
+#include "/sys/thing/properties.h"
+#include "/sys/thing/moving.h"
+#include "/sys/thing/restrictions.h"
+#include "/sys/thing/material.h"
+#include "/sys/thing/lighttypes.h"
+
+#endif
diff --git a/sys/thing/commands.h b/sys/thing/commands.h
new file mode 100644
index 0000000..2bb876d
--- /dev/null
+++ b/sys/thing/commands.h
@@ -0,0 +1,41 @@
+// MorgenGrauen MUDlib
+//
+// thing/commands.h -- command handling header
+//
+// $Id: commands.h 8707 2014-02-23 22:38:34Z Zesstra $
+
+#ifndef __THING_COMMANDS_H__
+#define __THING_COMMANDS_H__
+
+// properties
+
+#define P_COMMANDS           "commands"
+
+#endif // __THING_COMMANDS_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __THING_COMMANDS_H_PROTO__
+#define __THING_COMMANDS_H_PROTO__
+
+// prototypes
+
+// add commands to the command list of the object
+varargs void AddCmd(mixed cmd, mixed func, mixed flag, mixed cmdid);
+
+// remove a command
+varargs int RemoveCmd(mixed cmd, int del_norule, mixed onlyid);
+// Set- und Query-Methoden
+static mapping _set_commands(mapping commands);
+static mapping _query_commands();
+
+
+// internal
+static int _execute(mixed fun, string str, mixed *parsed);
+static int _process_command(string str, string *noparsestr,mixed fun, mixed flag, mixed rule);
+
+public int _cl(string str);
+
+#endif // __THING_COMMANDS_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/thing/description.h b/sys/thing/description.h
new file mode 100644
index 0000000..029f445
--- /dev/null
+++ b/sys/thing/description.h
@@ -0,0 +1,128 @@
+// MorgenGrauen MUDlib
+//
+// thing/description.h -- header
+//
+// $Id: description.h 9208 2015-05-10 20:54:06Z Zesstra $
+// MorgenGrauen MUDlib
+//
+// thing/description.h -- header
+//
+// $Id: description.h 9208 2015-05-10 20:54:06Z Zesstra $
+ 
+#ifndef __THING_DESCRIPTION_H__
+#define __THING_DESCRIPTION_H__
+
+// properties
+#define P_NAME               "name"       // name of the object
+#define P_NAME_ADJ           "name_adj"   // adjective of the name
+#define P_SHORT              "short"      // short desciption of the object
+#define P_LONG               "long"       // long description of the object
+#define P_IDS                "ids"        // identifier of the object
+#define P_ADJECTIVES         "adjectives" // adjectives of the object
+#define P_SHOW_INV           "show_inv"   // Show in inventory of player
+#define P_CLASS              "class"      // class, like undead or explosive
+
+#define P_KILL_MSG           "kill_msg"
+#define P_KILL_NAME          "kill_name"
+
+#define P_HEAL               "heal"
+#define P_COMBATCMDS         "combatcmds"
+
+#define P_VALUE              "value"
+#define P_NOBUY              "nobuy"
+#define P_NOSELL             "nosell"
+#define P_MAGIC              "magic"
+#define P_NOMAGIC            "nomagic"
+
+#define P_LOG_FILE           "log_file"   // bugs in anderes repfile umleiten
+
+#define P_LIGHT              "light"
+#define P_TOTAL_LIGHT        "total_light"
+
+#define P_CURSED             "cursed"
+#define P_KEEP_ON_SELL       "keep_on_sell"
+
+#define P_CLONER             "cloner"
+#define P_CLONE_TIME         "clone_time"
+
+/* read_msg ist auch fuer nicht fernwest-kundige lesbar */
+#define P_READ_MSG           "read_msg"
+#define P_FW_ALWAYS_READABLE "fw_always_readable"
+#define P_DETAILS            "details"
+#define P_SPECIAL_DETAILS    "special_details"
+#define P_READ_DETAILS       "read_details"
+#define P_SMELLS             "smell_details"
+#define P_SOUNDS             "sound_details"
+#define P_TOUCH_DETAILS      "p_lib_touch_details"
+#ifndef P_INVIS
+#define P_INVIS              "invis"
+#endif
+
+#define SENSE_DEFAULT "\ndefault"
+#define SENSE_VIEW  0
+#define SENSE_SMELL 1
+#define SENSE_SOUND 2
+#define SENSE_TOUCH 3
+#define SENSE_READ  4
+
+#endif // __THING_DESCRIPTION_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __THING_DESCRIPTION_H_PROTO__
+#define __THING_DESCRIPTION_H_PROTO__
+
+// prototypes
+// check if the objects can by identified by str
+varargs int id(string str, int lvl);
+
+// check IDs vs. string*
+int match_ids(string *list);
+
+// add an id to the current object
+void AddId(string|string* str);
+
+// remove an id from the object
+void RemoveId(string|string* str);
+
+// add an adjective to describe the object
+void AddAdjective(string|string* str);
+
+// remove an adjective from the object
+void RemoveAdjective(string|string* str);
+
+// get the name with casus and demonstrative
+varargs string name(int casus, int demon);
+varargs string Name(int casus, int demon);
+
+// check if object is member of a class
+int is_class_member(string|string* str);
+
+// add object to a class
+void AddToClass(string str);
+
+// add a detail to object
+public void AddDetail( string|string* keys, string|string*|mapping|closure descr );
+public void AddReadDetail( string|string* keys, string|string*|mapping|closure descr );
+public void AddSmells( string|string* keys, string|string*|mapping|closure descr );
+public void AddSounds( string|string* keys, string|string*|mapping|closure descr );
+public void AddTouchDetail( string|string* keys, string|string*|mapping|closure descr );
+
+// remove a detail from object
+public varargs void RemoveDetail( string|string* keys );
+public varargs void RemoveSmells( string|string* keys );
+public varargs void RemoveSounds( string|string* keys );
+public varargs void RemoveReadDetail( string|string* keys );
+public varargs void RemoveTouchDetail( string|string* keys );
+
+// add a special detail to object
+void AddSpecialDetail(string|string* keys, string functionname );
+
+// remove a special detail to object
+void RemoveSpecialDetail( string|string* keys );
+
+static void GiveEP(int type, string key);
+
+#endif // __THING_DESCRIPTION_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/thing/language.h b/sys/thing/language.h
new file mode 100644
index 0000000..51afd86
--- /dev/null
+++ b/sys/thing/language.h
@@ -0,0 +1,79 @@
+// MorgenGrauen MUDlib
+//
+// thing/language.h -- language handling
+//
+// $Id: language.h 6330 2007-05-26 17:53:21Z Zesstra $
+ 
+#ifndef __THING_LANGUAGE_H__
+#define __THING_LANGUAGE_H__
+
+// properties
+#define P_ARTICLE            "article"
+#define P_GENDER             "gender"
+#define P_PLURAL             "plural"
+
+// special defines
+
+#define WER 	0
+#define WESSEN 	1
+#define WEM 	2
+#define WEN 	3
+#define RAW     99   // name without article
+
+#define NEUTER 		0
+#define MALE 		1
+#define FEMALE 		2
+
+#define SINGULAR 	0
+#define PLURAL 		1
+
+#endif // __THING_LANGUAGE_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __THING_LANGUAGE_H_PROTO__
+#define __THING_LANGUAGE_H_PROTO__
+
+// prototypes
+
+// Artikel im passenden Fall sowie demonst. bzw undemonst. zurueck-
+// geben. force ueberschreibt das SetArticle-Flag.
+public varargs string QueryArticle(int casus, int dem, int force);
+
+// Empfehle einen Artikel fuer das Objekt, getestet wird, ob ausser
+// diesem Objekt sich ein anderes Objekt mit der id im selben
+// Raum befindet.
+public varargs int SuggestArticle(string id);
+
+// Gib ein Pronomen zurueck, welches ausdrueckt, das ein Objekt diesem
+// Objekt gehoert. Dabei ist what das Geschlecht des Objektes.
+public varargs string QueryPossPronoun(mixed what, int casus, int number);
+
+// Er/Sie/Es u.ae. zurueckgeben.
+public varargs string QueryPronoun(int casus);
+
+// Du im passenden Fall zurueckgeben.
+public varargs string QueryDu(int casus,int gender, int zahl);
+
+// Geschlecht in Worten zurueckgeben (maennlich, weiblich, saechlich)
+public string QueryGenderString();
+
+// Deklination eines Adjektivs
+public varargs string DeclAdj(mixed adj, int casus, int demon);
+
+// Query- und Set-Methoden
+static int _set_article(int fl);
+static int _query_article();
+static int _set_gender(int i);
+
+// Interna
+static int QueryAFlag();
+
+#endif // __THING_LANGUAGE_H_PROTO__
+
+#endif // NEED_PROTOYPES
+
+
+
+
+
diff --git a/sys/thing/lighttypes.h b/sys/thing/lighttypes.h
new file mode 100644
index 0000000..dae95d5
--- /dev/null
+++ b/sys/thing/lighttypes.h
@@ -0,0 +1,83 @@
+// MorgenGrauen MUDlib
+//
+// lighttypes.h -- Welche Typen von Licht gibt es im MorgenGrauen? 
+//
+// $Id: lighttypes.h,v 1.2 2004/06/12 14:00:48 Vanion Exp $
+ 
+#ifndef __THING_LIGHTTYPES_H__
+#define __THING_LIGHTTYPES_H__
+
+#define P_LIGHT_TYPE   "light_type"  // Welches Licht herrscht hier vor?
+
+// Mode-Parameter fuer CheckLightType
+#define LT_CHECK_ANY     0 
+#define LT_CHECK_ALL     1
+#define LT_CHECK_MATCH   2
+#define LT_CHECK_NONE    3
+
+// Basislichttypen
+#define LT_MISC          0  // Unbestimmt
+
+// Natuerliche Lichtquellen (Tag)
+#define LT_SUN           1  // Sonnenschein
+#define LT_DIFFUSE       2  // Indirektes Tageslicht
+
+// Natuerliche Lichtquellen (Nacht)
+#define LT_MOON          4  // Mondlicht
+#define LT_STARS         8  // Sternenlicht
+
+// Drei besonders oft vorkommende Typen offenen Lichts
+#define LT_CANDLE       16  // Kerzenlicht
+#define LT_TORCH        32  // Fackelschein
+#define LT_OPEN_FIRE    64  // Lagerfeuer etc.
+
+// Magische erzeugtes Leuchten, wird nicht naeher definiert.
+#define LT_MAGIC       128  // Magische Lichtquelle
+
+// Alles was so gluehen kann: Von Gluehbirne bis zu Phosphor
+#define LT_GLOWING     256  // Selbstleuchtende Lichtquellen
+
+// Ein Raum kann auch explizit auf Dunkel gesetzt werden.
+// Eine Mischung mit anderen Typen scheint aber wenig sinnvoll.
+#define LT_DARKNESS    512  // Dunkelheit
+
+// Gemischte Typen, sie dienen dazu, komfortabel Abfragen auf Lichttypen 
+// zu implementieren. Beim setzen sollte immer besser auf die genaueren 
+// Lichttypen zurueckgegriffen werden.
+
+// Tageslicht im allgemeinen
+#define LT_DAYLIGHT    (LT_SUN|LT_DIFFUSE)
+
+// Da es verschiedene Typen offenen Feuers gibt, wird hier
+// noch ein Abfragetyp definiert.
+#define LT_FIRE        (LT_CANDLE|LT_TORCH|LT_OPEN_FIRE)
+
+// Natuerliches Licht
+#define LT_NATURAL     (LT_DAYLIGHT|LT_STARS|LT_MOON)
+
+// Kuenstliches Licht
+#define LT_ARTIFICIAL   (LT_MAGIC|LT_FIRE|LT_GLOWING)
+
+#endif // _THING_LIGHTTYPES_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __THING_LIGHTTYPES_H_PROTO__
+#define __THING_LIGHTTYPES_H_PROTO__
+
+// Diese Doku wird noch entfernt, wenn die Manpage erstellt worden ist.
+// 
+// Eine Funktion, mit der das Abfagen eines oder mehrerer Lichttypen 
+// moeglich ist, ist im std/thing/description definiert. Beispiel: 
+// 
+// thing->CheckLightType(LT_DAYLIGHT) 
+//       gibt true wenn ein Tageslichttyp gesetzt ist.
+//
+// thing->CheckLightType(LT_SUN|LT_MOON) 
+//       gibt true, wenn einer der beiden Himmelskoerper leuchtet.
+
+varargs int CheckLightType(int lighttype, int mode);
+
+#endif // __THING_LIGHTTYPES_H_PROTO__
+
+#endif // NEED_PROTOTYPES
diff --git a/sys/thing/material.h b/sys/thing/material.h
new file mode 100644
index 0000000..927969a
--- /dev/null
+++ b/sys/thing/material.h
@@ -0,0 +1,32 @@
+// MorgenGrauen MUDlib
+//
+// thing/material.h -- material handling header
+//
+// $Id: material.h 6930 2008-08-09 09:20:16Z Zesstra $
+
+#ifndef __THING_MATERIAL_H__
+#define __THING_MATERIAL_H__
+
+#define P_MATERIAL              "material"
+#define MATERIALDB              "/secure/materialdb"
+#define P_MATERIAL_KNOWLEDGE    "material_knowledge"
+#define MATERIAL_SYMMETRIC_RECOGNIZABILITY "mat_sym_recoc"
+
+// Materialien nicht beim Laden durch MatDB includen
+// TODO: besser recoc-propertydefinitionen woanders rein und Materialien hier
+// immer includen
+#ifndef _SKIP_MATERIALS_
+#include <materials.h>
+#endif
+
+#ifdef NEED_PROTOTYPES
+#ifndef __THING_MATERIAL_H_PROTO__
+#define __THING_MATERIAL_H_PROTO__
+
+int QueryMaterial(string mat);
+int QueryMaterialGroup(string matgroup);
+string MaterialList(int casus, mixed idinf);
+
+#endif // __THING_MATERIAL_H_PROTO__
+#endif // NEED_PROTOTYPES
+#endif // __THING_MATERIAL_H__
diff --git a/sys/thing/moving.h b/sys/thing/moving.h
new file mode 100644
index 0000000..46e14a7
--- /dev/null
+++ b/sys/thing/moving.h
@@ -0,0 +1,43 @@
+// MorgenGrauen MUDlib
+//
+// thing/moving.h -- object moving
+//
+// $Id: moving.h 8892 2014-08-04 19:48:28Z Zesstra $
+ 
+#ifndef __THING_MOVING_H__
+#define __THING_MOVING_H__
+
+// properties
+#define P_NEVERDROP           "neverdrop"
+#define P_NODROP              "nodrop"    // can't be dropped
+#define P_NOGET               "noget"     // can't be taken
+
+#define P_NOINSERT_MSG        "noinsert_msg"      // Fehlermeldung bei ME_CANT_BE_INSERTED
+#define P_NOLEAVE_MSG         "noleave_msg"       // Fehlermeldung bei ME_CANT_LEAVE_ENV
+#define P_ENV_TOO_HEAVY_MSG   "env_too_heavy_msg" // Fehlermeldung bei ME_TOO_HEAVY_FOR_ENV
+#define P_TOO_MANY_MSG        "too_many_msg"      // Fehlermeldung bei ME_TOO_MANY_OBJECTS
+#define P_TOO_HEAVY_MSG       "too_heavy_msg"     // Fehlermeldung bei ME_TOO_HEAVY
+
+#define P_LAST_MOVE           "last_move"
+#define P_LAST_CONTENT_CHANGE "last_content_change"
+
+#define P_SENSITIVE     "sensitive" // should be registered as sensitive object
+
+
+#endif // __THING_MOVING_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __THING_MOVING_H_PROTO__
+#define __THING_MOVING_H_PROTO__
+
+// prototypes
+// move the object from current position to dest using the method
+public varargs int move(object|string dest, int method);
+
+// remove the object return 1 for successful destruction
+public varargs int remove(int silent);
+
+#endif // __THING_MOVING_H_PROTO__
+
+#endif //  // NEED_PROTOYPES
diff --git a/sys/thing/properties.h b/sys/thing/properties.h
new file mode 100644
index 0000000..1a59522
--- /dev/null
+++ b/sys/thing/properties.h
@@ -0,0 +1,63 @@
+// MorgenGrauen MUDlib
+//
+// thing/properties.h -- contains property definitions and prototypes
+//
+// $Id: properties.h,v 3.5 2003/08/07 15:09:33 Rikus Exp $
+
+#ifndef __THING_PROPERTIES_H__
+#define __THING_PROPERTIES_H__
+
+// special defines
+
+#define F_VALUE         0
+#define F_MODE          1
+#define F_SET_METHOD    2
+#define F_QUERY_METHOD  3
+#define F_MODE_AS       4
+#define F_MODE_AD       5
+
+#define SAVE            64
+#define PROTECTED       128  // only this_object() can change the values
+#define SECURED         256  // like PROTECTED, but never resetable
+#define NOSETMETHOD     512  // Keine Set-Methode => Nicht mofifizierbar
+#define SETMNOTFOUND	1024 // Keine Set-Methode gefunden
+#define QUERYMNOTFOUND	2048 // Keine Query-Methode gefunden
+#define QUERYCACHED	4096
+#define SETCACHED	8192
+
+#endif // __THING_PROPERTIES_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __THING_PROPERTIES_H_PROTO__
+#define __THING_PROPERTIES_H_PROTO__
+
+// prototypes
+
+// (E)UID-Methods
+//static mixed _query_uid();
+//static mixed _query_euid();
+
+// Property-Initialisation
+protected void InitializeProperties();
+
+// direct property access
+public varargs mixed Set(string name, mixed Value, int Type, int extern);
+public varargs mixed Query(string name, int Type);
+
+// filtered property access
+public mixed SetProp(string name, mixed Value);
+public mixed QueryProp(string name);
+
+// global property handling
+public void SetProperties(mapping props);
+public mapping QueryProperties();
+
+// misc/interna
+public mixed *__query_properties();
+public void _set_save_data(mixed data);
+public mixed _get_save_data();
+
+#endif // __THING_PROPERTIES_H_PROTO__
+
+#endif // NEED_PROTOTYPES
diff --git a/sys/thing/restrictions.h b/sys/thing/restrictions.h
new file mode 100644
index 0000000..ea14401
--- /dev/null
+++ b/sys/thing/restrictions.h
@@ -0,0 +1,27 @@
+// MorgenGrauen MUDlib
+//
+// thing/restrictions.h -- restrictions of the object (weight)
+//
+// $Id: restrictions.h,v 3.3 2000/08/15 12:29:20 Paracelsus Exp $
+ 
+#ifndef __THING_RESTRICTIONS_H__
+#define __THING_RESTRICTIONS_H__
+
+// properties
+#define P_WEIGHT             "weight"       // weight of an object
+#define P_TOTAL_WEIGHT       "total_weight" // weight with all objects inside
+
+#endif // __THING_RESTRICTIONS_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __THING_RESTRICTIONS_H_PROTO__
+#define __THING_RESTRICTIONS_H_PROTO__
+
+static void _set_weight(int weight);
+static mapping _set_extern_attributes_modifier(mapping xmod);
+static mapping _set_extern_health_modifier(mapping xmod);
+
+#endif // __THING_RESTRICTIONS_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/thing/sockets.h b/sys/thing/sockets.h
new file mode 100644
index 0000000..a5a9cbb
--- /dev/null
+++ b/sys/thing/sockets.h
@@ -0,0 +1,79 @@
+// MorgenGrauen MUDlib
+/** @file
+  Konstanten, Prototypen und Properties fuer sockelbare Items.
+* Langbeschreibung...
+* @author Zesstra + Arathorn
+* @date xx.05.2008
+* @version $Id$
+*/
+
+/* Changelog:
+*/
+
+#ifndef _SOCKETS_H_
+#define _SOCKETS_H_
+
+/** Speichert die Sockel und deren Inhalt, die ein Item hat.
+  ([ TYP1: ({ITEM1, ITEM2}),
+     TYP2: ({-1}) ]) \n
+     ITEM1 = ([ P_SOCKET_PROPS, 
+                BLUE_NAME: load_name(),
+                DESCRIPTION: name(WER) ])
+  @internal
+ */
+#define P_SOCKETS      "_sockets"
+
+/** Speichert ein Mapping von Prop-Modifikatoren.
+  Die in diesem Mapping enthaltenen Props werden beim Einsetzen im Item mit
+  dem Sockel um den angegebenen Wert modifiziert.
+  */
+#define P_SOCKET_PROPS "_socket_props"
+
+/** Sockeltyp, in den das Item eingesetzt werden kann.
+  Alle anderen Sockel sind inkompatibel.
+  */
+#define P_SOCKET_TYPE  "_socket_type"
+
+/** Einschraenkungen, denen der gesockelte Gegenstand nach dem Einsetzen
+    zusaetzlich unterliegt. 
+    Wird P_RESTRICTIONS hinzugefuegt. Mapping a la P_RESTRICTIONS.
+ */
+#define P_SOCKET_RESTR_USE    "_socket_restr_use"
+
+/** Einschraenkungen, denen das Lebewesen unterliegt, welches das Item in 
+  den Sockel einbauen soll/will.
+  Mapping a la P_RESTRICTIONS.
+  */
+#define P_SOCKET_RESTR_MOUNT  "_socket_restr_mount"
+
+// Sockeltypen
+// TODO: gueltige Sockeltypen definieren festlegen, welche Props sie aendern
+// duerfen.
+#define VALID_SOCKET_TYPES ({})
+
+// Rueckgabewerte
+#define SOCKET_OK                1
+#define SOCKET_NO_OBJECT        -1
+#define SOCKET_NO_DATA          -2
+#define SOCKET_INVALID_TYPE     -3
+#define SOCKET_NO_SOCKETS       -4
+#define SOCKET_NONE_AVAILABLE   -5
+#define SOCKET_NO_EXPERTISE     -6
+#define SOCKET_ITEM_INUSE       -7
+
+// sym. Konstante fuer freien Sockel
+#define SOCKET_FREE             -1
+
+#endif // _SOCKETS_H_
+
+#ifdef NEED_PROTOTYPES
+#ifndef _SOCKETS_H_PROTOTYPES_
+#define _SOCKETS_H_PROTOTYPES_
+// public Interface 
+public int   MountSocketItem(object item);
+public int   TestSocketItem(object item);
+public mixed GetSocketInfo();
+
+#endif // _SOCKETS_H_PROTOTYPES_
+#endif // NEED_PROTOTYPES
+
diff --git a/sys/thing/util.h b/sys/thing/util.h
new file mode 100644
index 0000000..053f499
--- /dev/null
+++ b/sys/thing/util.h
@@ -0,0 +1,25 @@
+// MorgenGrauen MUDlib
+//
+// sys/thing/util.h -- Headerfile fuer /std/thing/util.c
+//
+// $Id: util.h 4707 2006-09-23 10:18:15Z root $
+
+#ifndef __THING_UTIL_H__
+#define __THING_UTIL_H__
+
+#endif // __THING_UTIL_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __THING_UTIL_H_PROTO__
+#define __THING_UTIL_H_PROTO__
+
+public void ShowPropList(string *props);
+static void PrettyDump(mixed x);
+static void DumpArray(mixed *x);
+static void DumpMapping(mapping x);
+static void DumpKeyValPair(mapping x, mixed key, int size);
+
+#endif // __THING_UTIL_H_PROTO__
+
+#endif // NEED_PROTOTYPES
\ No newline at end of file
diff --git a/sys/time.h b/sys/time.h
new file mode 100644
index 0000000..9bea657
--- /dev/null
+++ b/sys/time.h
@@ -0,0 +1,18 @@
+#ifndef LPC_TIME_H_
+#define LPC_TIME_H_ 1
+
+/* Indices into the array returned from gmtime() and localtime(). */
+
+#define TM_SEC    0  /* Seconds (0..59) */
+#define TM_MIN    1  /* Minutes (0..59) */
+#define TM_HOUR   2  /* Hours (0..23) */
+#define TM_MDAY   3  /* Day of the month (1..31) */
+#define TM_MON    4  /* Month of the year (0..11) */
+#define TM_YEAR   5  /* Year (e.g.  2001) */
+#define TM_WDAY   6  /* Day of the week (Sunday = 0) */
+#define TM_YDAY   7  /* Day of the year (0..365) */
+#define TM_ISDST  8  /* TRUE: Daylight saving time */
+
+#define TM_MAX 9  /* Number of entries in the array */
+
+#endif /* LPC_TIME_H_ */
diff --git a/sys/tls.h b/sys/tls.h
new file mode 100644
index 0000000..39b50ec
--- /dev/null
+++ b/sys/tls.h
@@ -0,0 +1,86 @@
+#ifndef LPC_TLS_H
+#define LPC_TLS_H
+
+/* Field indices for the result of tls_query_connection_info() */
+
+#define TLS_CIPHER  0
+#define TLS_COMP    1
+#define TLS_KX      2
+#define TLS_MAC     3
+#define TLS_PROT    4
+
+#define TLS_INFO_MAX 5  /* Total number of result fields */
+
+/* Interpretation of the cipher information */
+
+#define TLS_CIPHER_TABLE ({ \
+"TLS_CIPHER_NONE", \
+"TLS_CIPHER_NULL", \
+"TLS_CIPHER_ARCFOUR_128", \
+"TLS_CIPHER_3DES_CBC", \
+"TLS_CIPHER_RIJNDAEL_128_CBC", \
+"TLS_CIPHER_RIJNDAEL_256_CBC", \
+"TLS_CIPHER_ARCFOUR_40" \
+})
+
+#define TLS_CIPHER_NAME(x) TLS_CIPHER_TABLE[(x)]
+
+/* Interpretation of the key-exchange information */
+
+#define TLS_KX_TABLE ({ \
+"TLS_KX_NONE", \
+"TLS_KX_RSA", \
+"TLS_KX_DHE_DSS", \
+"TLS_KX_DHE_RSA", \
+"TLS_KX_ANON_DH", \
+"TLS_KX_SRP", \
+"TLS_KX_RSA_EXPORT", \
+"TLS_KX_SRP_RSA", \
+"TLS_KX_SRP_DSS" \
+})
+
+#define TLS_KX_NAME(x) TLS_KX_TABLE[(x)]
+
+/* Interpretation of the MAC information */
+
+#define TLS_MAC_TABLE ({ \
+"TLS_MAC_NONE", \
+"TLS_MAC_NULL", \
+"TLS_MAC_MD5", \
+"TLS_MAC_SHA" \
+})
+
+#define TLS_MAC_NAME(x) TLS_MAC_TABLE[(x)]
+
+/* Interpretation of the compression information */
+
+#define TLS_COMP_TABLE ({ \
+"TLS_COMP_NONE", \
+"TLS_COMP_NULL", \
+"TLS_COMP_ZLIB", \
+"TLS_COMP_LZO" \
+})
+
+#define TLS_COMP_NAME(x) TLS_COMP_TABLE[(x)]
+
+/* Interpretation of the protocol information */
+
+#define TLS_PROT_TABLE ({ \
+"TLS_PROT_NONE", \
+"TLS_PROT_SSL3", \
+"TLS_PROT_TLS1" \
+})
+
+#define TLS_PROT_NAME(x) TLS_PROT_TABLE[(x)]
+
+/* Recognized hash() algorithms (not all may be supported at runtime) */
+
+#define TLS_HASH_SHA1      (1)
+#define TLS_HASH_SHA224    (2)
+#define TLS_HASH_SHA256    (3)
+#define TLS_HASH_SHA384    (4)
+#define TLS_HASH_SHA512    (5)
+#define TLS_HASH_MD5       (6)
+#define TLS_HASH_RIPEMD160 (7)
+
+#endif /* LPC_TLS_H */
diff --git a/sys/trace.h b/sys/trace.h
new file mode 100644
index 0000000..9c1c473
--- /dev/null
+++ b/sys/trace.h
@@ -0,0 +1,18 @@
+#ifndef LPC_TRACE_H_
+#define LPC_TRACE_H_
+
+/* Argument values for the trace() efun.
+ */
+
+#define TRACE_NOTHING       0  /* Stop tracing */
+
+#define TRACE_CALL          1  /* Trace all lfun calls */
+#define TRACE_CALL_OTHER    2  /* Trace inter-object calls */
+#define TRACE_RETURN        4  /* Trace function returns */
+#define TRACE_ARGS          8  /* Print function arguments and results */
+#define TRACE_EXEC         16  /* Trace all executed instructions */
+#define TRACE_HEART_BEAT   32  /* Trace heartbeat code */
+#define TRACE_APPLY        64  /* Trace (internal) applies */
+#define TRACE_OBJNAME     128  /* Print the object names */
+
+#endif /* LPC_TRACE_H_ */
diff --git a/sys/transport.h b/sys/transport.h
new file mode 100644
index 0000000..d9ac735
--- /dev/null
+++ b/sys/transport.h
@@ -0,0 +1,29 @@
+// MorgenGrauen MUDlib
+//
+// transport.h -- Definitionen fuer Schiffe und andere Transporter
+//
+// $Id: transport.h,v 3.3 2002/02/09 11:56:59 Tilly Exp $
+
+#ifndef _TRANSPORT_H_
+#define _TRANSPORT_H_
+
+#define P_ENTERMSG           "entermsg"
+#define P_LEAVEMSG           "leavemsg"
+#define P_LEAVEFAIL          "leavefail"
+#define P_ENTERFAIL          "enterfail"
+#define P_ARRIVEMSG          "arrivemsg"
+#define P_DEPARTMSG          "departmsg"
+#define P_ENTERCMDS          "entercmds"
+#define P_LEAVECMDS          "leavecmds"
+#define P_MAX_PASSENGERS     "maxpass"
+
+#define P_TRAVEL_INFO        "travel_info"
+#define P_NO_TRAVELING       "no_traveling"
+#define P_TRAVEL_CMDS        "travel_cmds"
+#define P_HARBOUR            "harbour_name"
+
+#define HP_MSG  "hpMsg"
+#define HP_ROOM "hpRoom"
+#define HP_FUN  "hpFun"
+
+#endif
diff --git a/sys/udp.h b/sys/udp.h
new file mode 100644
index 0000000..a2f74c0
--- /dev/null
+++ b/sys/udp.h
@@ -0,0 +1,98 @@
+// MorgenGrauen MUDlib
+//
+// udp.h -- Definitionen fuers udp
+//
+// $Id: udp.h 9503 2016-02-23 18:27:45Z Zesstra $
+
+#ifndef _UDP_H
+#define _UDP_H
+
+/* --- User Configuration. --- */
+
+#include "/secure/config.h"
+#include <driver_info.h>
+
+#define INETD		"/secure/inetd"
+#define UDP_CMD_DIR	"/secure/udp/"
+
+#if !defined(__TESTMUD__) && MUDNAME=="MorgenGrauen"
+#  define HOST_FILE	"/etc/INETD_HOSTS"
+#else
+#  define HOST_FILE "/etc/INETD_HOSTS.TESTMUD"
+#endif
+
+#define INETD_LOG_FILE	"INETD"
+
+#define REPLY_TIME_OUT	12
+#define RETRIES		2
+
+/* #define LOCAL_NAME	SECURITY->get_mud_name()	// CD */
+#if (__HOST_NAME__==MUDHOST)
+#define LOCAL_NAME  MUDNAME
+#else
+#define LOCAL_NAME  "MG-Test-"+__HOST_NAME__
+#endif
+
+/* #define LOCAL_UDP_PORT	SECURITY->do_debug("udp_port");  // CD */
+#define LOCAL_UDP_PORT	driver_info(DI_UDP_PORT)
+
+/* If you are running another intermud system concurrently and there is a
+ * clash with the udp.h filename, rename the old udp.h file as appropriate
+ * and include this line. */
+/* #include <cdudp.h>	// CD */
+
+/* Include these definitions for CD muds. */
+/* #define CDLIB */
+/* #define CD_UDP		"/d/Standard/obj/udp" */
+
+/* --- End of config. --- */
+
+#define INETD_DIAGNOSTICS
+
+#define INETD_VERSION	"0.7a"
+
+/* --- Standard header macros. --- */
+
+#define RECIPIENT	"RCPNT"
+#define REQUEST		"REQ"
+#define SENDER		"SND"
+/* The DATA field should be used to store the main body of any packet. */
+#define DATA		"DATA"
+
+/* These headers are reserved for system use only. */
+#define HOST		"HST"
+#define ID		"ID"
+#ifndef NAME
+#define NAME		"NAME"
+#endif
+#define PACKET		"PKT"
+#define UDP_PORT	"UDP"
+#define SYSTEM		"SYS"
+
+/* Reserved headers for diagnostics. */
+#define PACKET_LOSS	"PKT_LOSS"
+#define RESPONSE_TIME	"TIME"
+
+/* --- Standard REQUEST macros. --- */
+
+#define PING		"ping"
+#define QUERY		"query"
+#define REPLY		"reply"
+
+/* --- Standard SYSTEM macros. --- */
+
+#define FAILED		"F"
+#define REPEAT		"R"
+#define TIME_OUT	"TO"
+#define UNAVAILABLE	"U"
+
+/* --- Index macros for host arrays. --- */
+
+#define HOST_NAME	0
+#define HOST_IP		1
+#define HOST_UDP_PORT	2
+#define LOCAL_COMMANDS	3
+#define HOST_COMMANDS	4
+#define HOST_STATUS	5
+
+#endif
diff --git a/sys/udp_language.h b/sys/udp_language.h
new file mode 100644
index 0000000..c003023
--- /dev/null
+++ b/sys/udp_language.h
@@ -0,0 +1,64 @@
+// Konfigurationsdatei fuer die Sprachausgabe des INETD
+
+#ifndef __UDP_LANG_H__
+#define __UDP_LANG_H__
+
+// ***
+// *** inetd.c
+// ***
+
+#define INETD_INV_ACCESS_MSG "%s: Illegaler Zugriff auf die x-Funktion.\n"
+//                            time                         function
+
+// ***
+// *** channel.c
+// ***
+
+#define INETD_INV_CHAN_MSG "\n%s: %s@%s versucht, auf Kanal \'%s\' zu senden.\n"
+//                          time who mud                  channel
+#define INETD_NO_SUCH_CHAN "[Root@%s] Es gibt hier keinen Intermud-Kanal %s.\n"
+//                                mud                                  channel
+#define INETD_CHAN_LISTEN  "[%s@%s] Auf dem Kanal befinde%s sich: %s\n"
+//                           channel mud                plural    names
+#define INETD_CHAN_NO_LIST \
+                         "[%s@%s] Es befindet sich niemand auf diesem Kanal.\n"
+//                         channel mud
+#define INETD_CHAN_NO_HIST "[%s@%s] Es ist keine Geschichte verfuegbar.\n"
+//                          channel mud
+#define INETD_CHAN_HIST    "[%s@%s] History:\n"
+//                          channel mud
+
+// ***
+// *** locate.c
+// ***
+
+// ***
+// *** man.c
+// ***
+
+#define INETD_MANPAGE_FOUND "[ManD@%s] Folgende Seite wurde gefunden: %s\n"
+//                              mud                                 file
+#define INETD_NO_MANPAGE    "[ManD@%s] Keine Hilfeseite gefunden fuer '%s'.\n"
+//                              mud                                 page
+#define INETD_MANPAGES      "[ManD@%s] Die folgenden Seiten passen:\n" \
+                             "%'-'78.78s\n%s%'-'78.78s\n"
+//                                mud  "" pagelist ""
+#define INETD_MAN_REQUESTED "%s@%s: Abfrage abgeschickt.\n"
+//                             page  mud
+
+// ***
+// *** newhost.c
+// ***
+
+
+// ***
+// *** query.c
+#define INETD_QUERY_REQUESTED "%s@%s: Abfrage abgeschickt.\n"
+// ***
+
+
+// ***
+// *** reply.c
+// ***
+
+#endif // __UDP_LANG_H__
diff --git a/sys/udp_mail.h b/sys/udp_mail.h
new file mode 100644
index 0000000..fe76232
--- /dev/null
+++ b/sys/udp_mail.h
@@ -0,0 +1,52 @@
+// MorgenGrauen MUDlib
+//
+// udp_mail.h -- Post per udp
+//
+// $Id: udp_mail.h,v 3.1 1997/02/12 13:04:59 Wargon Exp $
+
+/*
+ * VERSION 1.0
+ * Include file for the UDP MAIL system (Author: Alvin@Sushi)
+ * Requires INETD V0.60 or higher (INETD Author: Nostradamus@Zebedee)
+ */
+
+/*
+ * Your local mail delivery system
+ * Needs to have the following functions :-
+ * void deliver_mail(string to,string from,string subjct,string mail_body)
+ * int query_recipient_ok(string name)
+ */
+#define LOCAL_MAILER			"/secure/mailer"
+
+/*
+ * The directory in which mail is kept if the initial attempt times out
+ * The mail will be recent every UDPM_RETRY_SEND minutes until
+ * UDPM_SEND_FAIL minutes have passed
+ * NOTE: UDPM_SPOOL_DIR must end with / and must be writable
+ */
+#define UDPM_SPOOL_DIR			"mail/spool/"
+
+/* The following two times are in MINUTES */
+#define UDPM_RETRY_SEND			30
+#define UDPM_SEND_FAIL			1440	 /* 24 Hours */
+
+/* PLEASE DO NOT ALTER THE FOLLOWING */
+
+#define UDPM_WRITER			"udpm_writer"
+#define UDPM_SUBJECT			"udpm_subject"
+#define UDPM_STATUS			"udpm_status"
+#define UDPM_SPOOL_NAME			"udpm_spool_name"
+
+#define UDPM_STATUS_TIME_OUT		0
+#define UDPM_STATUS_DELIVERED_OK	1
+#define UDPM_STATUS_UNKNOWN_PLAYER	2
+#define UDPM_STATUS_IN_SPOOL		3
+
+/* things to do with the spool directory */
+
+#define UDPMS_TIME			"udpm_spool_time"
+#define UDPMS_TO			"udpm_spool_to"
+#define UDPMS_DEST			"udpm_spool_dest"
+#define UDPMS_FROM			"udpm_spool_from"
+#define UDPMS_SUBJECT			"udpm_spool_subject"
+#define UDPMS_BODY			"udpm_spool_body"
diff --git a/sys/unit.h b/sys/unit.h
new file mode 100644
index 0000000..27a8a2c
--- /dev/null
+++ b/sys/unit.h
@@ -0,0 +1,86 @@
+// MorgenGrauen MUDlib
+//
+// unit.h -- Defines fuer Units
+//
+// $Id: unit.h 9017 2015-01-10 19:20:14Z Zesstra $
+
+#ifndef _UNIT_H_
+#define _UNIT_H_
+
+#define P_UNIT_DECAY_INTERVAL      "unit_decay_interval"
+#define P_UNIT_DECAY_QUOTA         "unit_decay_quota"
+#define P_UNIT_DECAY_FLAGS         "unit_decay_flags"
+#define P_UNIT_DECAY_MIN           "unit_decay_min"
+
+// Flags fuer P_UNIT_DECAY_FLAGS 
+// verhindert das Zerfallen einzelner Unit-Objekte.
+#define NO_DECAY             0x1
+// kein Decay bis zum ersten move() von einem Env in ein Env. (d.h. das erste
+// move() in ein Env (Clonen im create vom NPC) ist unberuecksichtigt.)
+#define NO_DECAY_UNTIL_MOVE  0x2
+// ungenaueren Zerfall benutzt, dabei aber immer min. eine Einheit zerfallen
+// lassen.
+#define INACCURATE_DECAY     0x4
+// kein prozentualer Zerfall, sondern P_UNIT_DECAY_QUOTA gibt eine abs. Menge
+// an Einheiten an.
+#define ABSOLUTE_DECAY       0x8
+
+#define U_GPU   "u_gpu"
+#define U_CPU   "u_cpu"
+#define U_IDS   "u_ids"
+#define U_REQ   "u_req"
+
+#define IS_EQUAL(x) (objectp(x) && (IsEqual(x) && x->IsEqual(ME)))
+
+#endif // _UNIT_H_
+
+#ifdef NEED_PROTOTYPES
+#ifndef _UNIT_H_PROTYPES_
+#define _UNIT_H_PROTYPES_
+// public functions
+varargs int    id(string str,int lvl);
+        int    IsEqual(object ob);
+        int    IsUnit();
+varargs int    move(object|string dest, int method);
+        int    *QueryCoinsPerUnits();
+        int    *QueryGramsPerUnits();
+varargs int remove(int silent);
+varargs string long();
+varargs string|<string>* name(int fall, int demo);
+varargs string QueryPronoun(int casus);
+        string short();
+        void   AddAmount(int am);
+        void   AddPluralId(mixed str);
+        void   AddSingularId(mixed str);
+        void   RemovePluralId(mixed str);
+        void   RemoveSingularId(mixed str);
+        void   reset();
+        void   SetCoinsPerUnits(int coins,int units);
+        void   SetGramsPerUnits(int grams,int units);
+public  int    DoDecay(int silent);
+
+// internal functions
+static    void    check_leave();
+protected void    DoDecayMessage(int oldamount, int zerfall);
+
+// Query- und Set-Methoden
+static  int    _query_plural();
+static  int    _query_total_weight();
+static  int    _query_u_req();
+static  int    _query_value();
+static  int    _query_weight();
+static  string|string* _query_name();
+static  int    _set_amount(int am);
+static  string|string* _set_name(mixed names);
+static  int    _set_weight(int num);
+static  int    _set_value(int num);
+static  int    _query_unit_decay_interval();
+static  int    _set_unit_decay_interval(int zeit);
+static  int    _query_unit_decay_quota();
+static  int    _set_unit_decay_quota(int quota);
+static  int    _query_unit_decay_min();
+static  int    _query_unit_decay_flags();
+
+#endif // _UNIT_H_PROTYPES_
+#endif // NEED_PROTOTYPES
+
diff --git a/sys/userinfo.h b/sys/userinfo.h
new file mode 100644
index 0000000..05548be
--- /dev/null
+++ b/sys/userinfo.h
@@ -0,0 +1,53 @@
+// MorgenGrauen MUDlib
+//
+// userinfo.h -- Daten im Spieler-Cache (siehe /secure/master/userinfo.c)
+//
+// $Id: userinfo.h 6207 2007-02-18 23:32:35Z Zesstra $
+
+#ifndef _USERINFO_
+#define _USERINFO_
+
+/*
+ * userinfo: what each field of /secure/PASSWD is
+ */
+
+/* password (encrypted) */
+#define USER_PASSWORD 0
+
+/* wizard level (or programming level) (0 for simple users) */
+#define USER_LEVEL 1
+
+/* domain (if any) master == *domainname */
+#define USER_DOMAIN 2
+
+/* player object to be loaded by /secure/login */
+#define USER_OBJECT 3
+
+/* time of first login */
+#define USER_CREATION_DATE 4
+
+/* Time userinfo has been touched last */
+#define USER_TOUCH 5
+
+/* exploration points of player */
+#define USER_EP 6
+
+/* npcs killed by player */
+#define USER_EK 7
+
+/* miniquests solved by player */
+#define USER_MQ 8
+
+/* guilds (if any) master == *guildname */
+#define USER_GUILD 9 
+
+/* tips for eks given to player */
+#define USER_EKTIPS 10
+
+/* tips for fps given to player */
+#define USER_FPTIPS 11
+
+/* UIDs fuer den ein Magier u.U. explizit verantwortlich ist */
+#define USER_UIDS_TO_TAKE_CARE 12
+
+#endif
diff --git a/sys/util.h b/sys/util.h
new file mode 100644
index 0000000..299f044
--- /dev/null
+++ b/sys/util.h
@@ -0,0 +1,15 @@
+// MorgenGrauen MUDlib
+//
+// util.h -- Header fuer /std/util/*.c
+//
+// $Id: util.h 6404 2007-08-06 20:31:22Z Zesstra $
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+varargs public void More(string txt, int file, mixed ctrl, 
+                         mixed ctrlargs, int flags);
+
+
+#endif // _UTIL_H
+
diff --git a/sys/util/ringbuffer.h b/sys/util/ringbuffer.h
new file mode 100644
index 0000000..150d004
--- /dev/null
+++ b/sys/util/ringbuffer.h
@@ -0,0 +1,28 @@
+// MorgenGrauen MUDlib
+//
+// ringbuffer.h -- Header fuer /std/util/ringbuffer.c
+//
+// $Id: util.h 6404 2007-08-06 20:31:22Z Zesstra $
+
+#ifndef _RINGBUFFER_H
+#define _RINGBUFFER_H
+
+#define MODE_FIFO 1
+#define MODE_LIFO 2
+
+#endif // _RINGBUFFER_H
+
+#ifndef _RINGBUFFER_H_IMPLEMENTATION
+#ifdef NEED_PROTOTYPES
+
+#define _RINGBUFFER_H_IMPLEMENTATION
+
+protected struct std_ringbuffer CreateRingBuffer(int size, int newmode);
+protected void RingBufferPut(struct std_ringbuffer buffer, mixed val);
+protected mixed RingBufferGet(struct std_ringbuffer buffer);
+protected struct std_ringbuffer ResizeRingBuffer(struct std_ringbuffer buf, 
+                                                 int size);
+
+#endif // NEED_PROTOTYPES
+#endif // _RINGBUFFER_H_IMPLEMENTATION
+
diff --git a/sys/v_compiler.h b/sys/v_compiler.h
new file mode 100644
index 0000000..1a6e9b9
--- /dev/null
+++ b/sys/v_compiler.h
@@ -0,0 +1,34 @@
+// MorgenGrauen MUDlib
+//
+// v_compiler.h -- a general virtual compiler object
+//
+// $Id: v_compiler.h 6081 2006-10-23 14:12:34Z Zesstra $
+
+#ifndef __V_COMPILER_H__
+#define __V_COMPILER_H__
+
+#define P_STD_OBJECT         "std_object"
+#define P_COMPILER_PATH      "compiler_path"
+
+#endif // __V_COMPILER_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __V_COMPILER_H_PROTO__
+#define __V_COMPILER_H_PROTO__
+
+// to validate if the file name is in our range
+string Validate(string file);
+
+// sets costumization data in the cloned object
+mixed CustomizeObject();
+
+// this functions is called when a file is not found and may be virtual
+mixed compile_object(string file);
+
+// to get all yet cloned (and not cleaned up) objects
+mixed QueryObjects();
+
+#endif // __V_COMPILER_H_PROTO__
+
+#endif //NEED_PROTOTYPES
diff --git a/sys/weapon.h b/sys/weapon.h
new file mode 100644
index 0000000..29eb3b3
--- /dev/null
+++ b/sys/weapon.h
@@ -0,0 +1,77 @@
+// MorgenGrauen MUDlib
+//
+// weapon.h -- Properties fuer Waffen (Schdenstypen siehe <combat.h>)
+//
+// $Id: weapon.h,v 3.8 2002/09/20 07:14:11 Tilly Exp $
+ 
+#include "/sys/weapon/description.h"
+
+#ifndef __WEAPON_H__
+#define __WEAPON_H__
+
+#include <combat.h>
+
+/* ---------- Properties ---------- */
+
+#ifndef P_NR_HANDS
+#define P_NR_HANDS      "nr_hands"     // benoetigte Haende zum zuecken
+#endif
+
+#define P_WC            "wc"           // Waffenklasse (weapon class)
+#define P_MAX_WC        "max_wc"       // obsolet, nicht mehr in Benutzung
+#define P_WEAPON_TYPE   "weapon_type"  // Waffentyp (Schwert, Keule usw.)
+#define P_DAM_TYPE      "dam_type"     // Schadensart der Waffe
+#define P_WIELDED       "wielded"      // Objekt mit dem Traeger der Waffe
+
+#define P_HIT_FUNC      "hit_func"     // Objekt das eine HitFunc definiert
+#define P_WIELD_FUNC    "wield_func"   // Objekt das eine WieldFunc definiert
+#define P_UNWIELD_FUNC  "unwield_func" // Objekt das eine UnwieldFunc definiert
+
+#define P_WIELD_MSG     "wield_msg"    // Meldungen beim Zuecken
+#define P_UNWIELD_MSG   "unwield_msg"  // Meldungen beim Wegstecken
+
+// Wann der Spieler das letzte Mal eine Waffe weggesteckt hat
+#define P_UNWIELD_TIME  "unwield_time"
+
+// Mit was fuer Techniken kann die Waffe eingesetzt werden?
+#define P_TECHNIQUE       "technique"
+
+// Ist die Waffe balanciert oder nicht?
+#define P_BALANCED_WEAPON "balanced_weapon"
+
+/* ---------- Zugehoerige Defines ---------- */
+
+// Techniken fuer P_TECHNIQUE
+#define TQ_THRASH         "Schlagtechnik"
+#define TQ_THRUST         "Stosstechnik"
+#define TQ_STROKE         "Streichtechnik"
+#define TQ_WHIP           "Peitschtechnik"
+
+// Defines fuer balanciert/unbalanciert
+#define WP_BALANCED       1
+#define WP_UNBALANCED     -1
+
+/* ---------- Parierwaffen ----------- */
+
+#define P_PARRY         "parry"        // definiert eine Parierwaffe
+#define P_PARRY_WEAPON  "parry_weapon" // Welche Parierwaffe wird benutzt?
+
+/* -------------------- Defines fuer Parierwaffen -------------------- */
+
+#define PARRY_NOT  0
+#define PARRY_TOO  1
+#define PARRY_ONLY 2
+
+#endif // __WEAPON_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __WEAPON_H_PROTO__
+#define __WEAPON_H_PROTO__
+
+// prototypes
+// no prototypes
+
+#endif // __WEAPON_H_PROTO__
+
+#endif	// NEED_PROTOYPES
diff --git a/sys/weapon/description.h b/sys/weapon/description.h
new file mode 100644
index 0000000..999e460
--- /dev/null
+++ b/sys/weapon/description.h
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// weapon/description.h -- Description for damaged Weapons
+//
+// $Id: description.h 6081 2006-10-23 14:12:34Z Zesstra $
+
+#ifndef __WEAPON_DESCRIPTION_H__
+#define __WEAPON_DESCRIPTION_H__
+
+// properties
+#define P_DAM_DESC           "dam_desc"
+
+// Default-Messages
+#define DFLT_DAM_DESC ({"ist in einem hervorragenden Zustand",\
+                        "ist in einem sehr guten Zustand",\
+                        "ist in einem guten Zustand",\
+                        "sieht noch ganz brauchbar aus",\
+                        "wirkt etwas abgenutzt",\
+                        "wirkt stark abgenutzt",\
+                        "ist gerade noch zu gebrauchen",\
+                        "wird bald schrottreif sein",\
+                        "ist nur noch Schrott" })
+
+#endif // __WEAPON_DESCRIPTION_H__
+
+#ifdef NEED_PROTOTYPES
+
+#ifndef __WEAPON_DESCRIPTION_H_PROTO__
+#define __WEAPON_DESCRIPTION_H_PROTO__
+
+// prototypes
+string dam_descr();
+
+#endif // __WEAPON_DESCRIPTION_H_PROTO__
+
+#endif // // NEED_PROTOYPES
diff --git a/sys/werliste.h b/sys/werliste.h
new file mode 100644
index 0000000..adf1810
--- /dev/null
+++ b/sys/werliste.h
@@ -0,0 +1,11 @@
+#ifndef ___WER_LISTE_H__
+#define ___WER_LISTE_H__
+
+#define WHO_SHORT     0x01
+#define WHO_VERTICAL  0x02
+#define WHO_ALPHA     0x04
+
+// Aus Spielersicht darstellen, auch wenn der Aufrufende Magier ist
+#define WHO_PLAYER_VIEW   0x08
+
+#endif // __WER_LISTE_H__
diff --git a/sys/wizlist.h b/sys/wizlist.h
new file mode 100644
index 0000000..629bac7
--- /dev/null
+++ b/sys/wizlist.h
@@ -0,0 +1,22 @@
+#ifndef LPC_WIZLIST_H_
+#define LPC_WIZLIST_H_ 1
+
+/* Indices returned by wizlist_info()
+ */
+
+#define WL_NAME           0  /* Wizard name */
+#define WL_COMMANDS       1  /* Number of commands executed */
+#define WL_COST           2  /* Weighted evalcost spent on this wizard */
+#define WL_GIGACOST       3  /* Weighted giga-evalcost spent on this wizard */
+#define WL_TOTAL_COST     4  /* Total evalcost spent on this wizard */
+#define WL_TOTAL_GIGACOST 5  /* Total giga-evalcost spent on this wizard */
+#define WL_HEART_BEATS    6  /* Heartbeats spent on this wizard */
+#define WL_CALL_OUT       7  /* unimplemented */
+#define WL_ARRAY_TOTAL    8  /* Arrays accounted for */
+#define WL_MAPPING_TOTAL  9  /* Mappings accounted for */
+#define WL_STRUCT_TOTAL  10  /* Struct elements accounted for */
+#define WL_EXTRA         11  /* Extra Wizinfo, if set */
+
+#define WL_SIZE  12  /* Number of entries */
+
+#endif /* LPC_WIZLIST_H_ */
diff --git a/sys/www.h b/sys/www.h
new file mode 100644
index 0000000..e83a659
--- /dev/null
+++ b/sys/www.h
@@ -0,0 +1,90 @@
+// MorgenGrauen MUDlib
+//
+// www.h -- World Wide Web Services
+//
+// $Id: www.h 7724 2011-01-05 22:32:26Z Zesstra $
+
+// how do you spell your mud name
+#ifndef MUDNAME
+# define MUDNAME        "MorgenGrauen"
+#endif
+
+// Pathnames to different services
+// INETD -- the inet daemon
+#ifndef INETD
+# define INETD          "/secure/inetd"
+#endif
+
+#define FINGER(n)               "/p/daemon/finger"->finger_single(n)
+#define WHO             "/obj/werliste"->QueryWhoListe(0,0,1)
+
+// UDPPATH -- wherethe udp programs are
+#define UDPPATH         "/secure/udp"
+
+// the WWW request scheduler
+#define WWW             (UDPPATH+"/www")
+#define TIMEOUT         5
+
+// wher to log all WWW UDP Requests
+#define WWWLOG "/WWW"                   // used in log_file()
+
+// define MUDHOST, WWW SERVER address and the ICON URL
+#ifndef MUDHOST
+# define MUDHOST                "MG.Mud.DE"
+#else
+# undef MUDHOST
+# define MUDHOST           "MG.Mud.DE"
+#endif
+#define MUDUSER         "mud"
+#define SERVER          "mg.mud.de"
+#define ICONS           "/icons"
+#define MUDWWW          "/cgi-bin/mudwww"
+
+// define the address of the MUD Host
+#define HOMEPAGE        "http://"+SERVER+"/"
+#define MUDMAIL         "mailto:"+MUDUSER+"@"+MUDHOST
+
+// Request Syntax tokens
+#define REQ     "REQ"           // TYPE of REQuest
+
+// The WWW Newsreader
+# define R_NEWS     "news"              // - we would like to read news
+#  define GROUP       "GROUP"   //   * which newsgroup
+#  define ARTICLE     "ARTICLE" //   * which article
+
+// The WWW MUD Walker
+# define R_WALK     "walk"              // - or maybe walk around a bit
+#  define ROOM        "ROOM"    //   * ROOM to enter
+#  define DETAIL      "DETAIL"  //   * DETAIL to examine
+
+// The WWW Finger Request
+# define R_FINGER   "finger"    // - finger someone
+#  define USER        "USER"    //   * USER to retrieve info from
+
+// The WWW Wholist Request
+# define R_WHO      "who"               // - get the wholist
+
+// The WWW Intermud Gateway
+# define R_INTERMUD "intermud"  // - intermud communication request
+#  define IMUD        "IMUD"    //   * The MUD to request info from
+#  define TYPE        "TYPE"    //   * The TYPE of Request
+#  define IDATA       "DATA"    //   * extra DATA to aid request
+#  define PAGE        "PAGE"    //   * Alphabetical pages of muds
+
+#define BACK    "BACK"          // PAGE to go BACK to
+
+#ifndef MUDNAME
+# define MUDNAME "TestMUD"
+#endif /* MUDNAME */
+
+// Property specific defines
+#define P_WWWINFO      "wwwinfo"
+
+// The error message to view in any case
+#define ERROR(str)      "<H1>Ein Fehler ist aufgetreten!</H1><HR>"+str
+
+// debugging (change name to your own!)
+#define DNAME   "hate"
+#define DEBUG(str) if(find_player(DNAME)) \
+                     tell_object(find_player(DNAME), sprintf("%O\n", str))
+//#undef DEBUG
diff --git a/sys/xml.h b/sys/xml.h
new file mode 100644
index 0000000..e630638
--- /dev/null
+++ b/sys/xml.h
@@ -0,0 +1,10 @@
+#ifndef XML_H__
+#define XML_H__ 1
+
+#define XML_TAG_NAME                    0 /* Name of the current tag */
+#define XML_TAG_ATTRIBUTES              1 /* Atttributes of the current tag */
+#define XML_TAG_CONTENTS                2 /* Contents of the current tag */
+
+#define XML_TAG_SIZE                    3
+
+#endif